From b6f7963f682268d5c103213434f9511b4cf608f9 Mon Sep 17 00:00:00 2001 From: Maxim Date: Wed, 23 Oct 2024 11:30:43 +0300 Subject: [PATCH] Some changes for testing --- controls-constructor/build.gradle.kts | 1 + .../constructor/ConstructorElement.kt | 16 +- .../controls/constructor/DeviceState.kt | 56 +- .../controls/constructor/devices/Drive.kt | 6 +- .../constructor/devices/LinearDrive.kt | 4 +- .../constructor/dsl/core/Annotation.kt | 9 - .../constructor/dsl/core/EquationSystem.kt | 196 ----- .../controls/constructor/dsl/core/Record.kt | 19 - .../dsl/core/algorithms/AlgorithmBuilder.kt | 37 - .../dsl/core/algorithms/AlgorithmSection.kt | 80 -- .../dsl/core/components/Component.kt | 81 -- .../dsl/core/components/Connection.kt | 38 - .../dsl/core/components/Connector.kt | 22 - .../constructor/dsl/core/components/Model.kt | 163 ---- .../dsl/core/components/Package.kt | 21 - .../dsl/core/equations/ConditionalEquation.kt | 12 - .../dsl/core/equations/Equation.kt | 24 - .../dsl/core/equations/EquationBase.kt | 6 - .../dsl/core/equations/InitialEquation.kt | 11 - .../dsl/core/events/AssignAction.kt | 18 - .../constructor/dsl/core/events/Event.kt | 30 - .../dsl/core/events/EventAction.kt | 12 - .../dsl/core/events/ReinitAction.kt | 18 - .../core/expression/ExpressionOperators.kt | 91 -- .../core/expressions/ArrayBinaryExpression.kt | 37 - .../core/expressions/ArrayBinaryOperator.kt | 11 - .../dsl/core/expressions/ArrayExpression.kt | 3 - .../core/expressions/ArrayScalarExpression.kt | 34 - .../core/expressions/ArrayScalarOperator.kt | 9 - .../expressions/ArrayVariableExpression.kt | 42 - .../dsl/core/expressions/BinaryExpression.kt | 135 --- .../core/expressions/ConstantExpression.kt | 20 - .../core/expressions/DerivativeExpression.kt | 30 - .../dsl/core/expressions/Expression.kt | 34 - .../expressions/FunctionCallExpression.kt | 91 -- .../PartialDerivativeExpression.kt | 31 - .../core/expressions/VariableExpression.kt | 32 - .../dsl/core/functions/Function.kt | 14 - .../dsl/core/functions/FunctionBuilder.kt | 50 -- .../dsl/core/simulation/NumericSolver.kt | 37 - .../dsl/core/simulation/SimulationContext.kt | 93 -- .../constructor/dsl/core/units/Dimension.kt | 78 -- .../dsl/core/units/PhysicalUnit.kt | 51 -- .../constructor/dsl/core/units/Units.kt | 89 -- .../dsl/core/variables/AbstractVariable.kt | 13 - .../dsl/core/variables/ArrayVariable.kt | 28 - .../dsl/core/variables/Causality.kt | 10 - .../dsl/core/variables/ConstantVariable.kt | 12 - .../dsl/core/variables/ContinuousVariable.kt | 22 - .../dsl/core/variables/DiscreteVariable.kt | 13 - .../dsl/core/variables/ParameterVariable.kt | 25 - .../dsl/core/variables/Variability.kt | 11 - .../dsl/core/variables/Variable.kt | 28 - .../controls/constructor/models/Inertia.kt | 2 +- .../controls/constructor/models/Leadscrew.kt | 2 +- .../constructor/units/NumericalValue.kt | 1 - .../constructor/units/UnitsOfMeasurement.kt | 238 +++++- .../controls/constructor/AnalyzerTest.kt | 806 ++++++++++++++++++ .../constructor/CompositeDeviceTest.kt | 182 ++++ .../controls/constructor/DeviceSpecTest.kt | 168 ++++ .../controls/constructor/EquationsTest.kt | 89 -- .../constructor/EventsAndActionsTest.kt | 73 -- .../controls/constructor/ExpressionsTest.kt | 101 --- .../controls/constructor/FunctionsTest.kt | 29 - .../controls/constructor/IntegrationTest.kt | 76 ++ .../constructor/UnitsOfMeasurementTest.kt | 18 + .../controls/constructor/UnitsTest.kt | 85 -- .../controls/constructor/UtilitiesTest.kt | 88 ++ .../controls/constructor/VariablesTest.kt | 41 - .../kscience/controls/spec/DeviceBase.kt | 15 +- .../kscience/controls/spec/DeviceBySpec.kt | 24 +- .../kscience/controls/spec/DeviceSpec.kt | 60 +- .../controls/spec/propertySpecDelegates.kt | 123 ++- 73 files changed, 1817 insertions(+), 2458 deletions(-) delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/Annotation.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/EquationSystem.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/Record.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/algorithms/AlgorithmBuilder.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/algorithms/AlgorithmSection.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/components/Component.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/components/Connection.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/components/Connector.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/components/Model.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/components/Package.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/equations/ConditionalEquation.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/equations/Equation.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/equations/EquationBase.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/equations/InitialEquation.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/events/AssignAction.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/events/Event.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/events/EventAction.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/events/ReinitAction.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expression/ExpressionOperators.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayBinaryExpression.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayBinaryOperator.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayExpression.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayScalarExpression.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayScalarOperator.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayVariableExpression.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/BinaryExpression.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ConstantExpression.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/DerivativeExpression.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/Expression.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/FunctionCallExpression.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/PartialDerivativeExpression.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/VariableExpression.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/functions/Function.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/functions/FunctionBuilder.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/simulation/NumericSolver.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/simulation/SimulationContext.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/units/Dimension.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/units/PhysicalUnit.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/units/Units.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/AbstractVariable.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/ArrayVariable.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/Causality.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/ConstantVariable.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/ContinuousVariable.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/DiscreteVariable.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/ParameterVariable.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/Variability.kt delete mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/Variable.kt create mode 100644 controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/AnalyzerTest.kt create mode 100644 controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/CompositeDeviceTest.kt create mode 100644 controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/DeviceSpecTest.kt delete mode 100644 controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/EquationsTest.kt delete mode 100644 controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/EventsAndActionsTest.kt delete mode 100644 controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/ExpressionsTest.kt delete mode 100644 controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/FunctionsTest.kt create mode 100644 controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/IntegrationTest.kt create mode 100644 controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/UnitsOfMeasurementTest.kt delete mode 100644 controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/UnitsTest.kt create mode 100644 controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/UtilitiesTest.kt delete mode 100644 controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/VariablesTest.kt diff --git a/controls-constructor/build.gradle.kts b/controls-constructor/build.gradle.kts index 6c42fc3..94b37bf 100644 --- a/controls-constructor/build.gradle.kts +++ b/controls-constructor/build.gradle.kts @@ -23,6 +23,7 @@ kscience{ commonTest{ implementation(spclibs.logback.classic) implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.9.0") } } diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/ConstructorElement.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/ConstructorElement.kt index 8073689..3716e13 100644 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/ConstructorElement.kt +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/ConstructorElement.kt @@ -74,6 +74,18 @@ public interface StateContainer : ContextAware, CoroutineScope { }.launchIn(this@StateContainer).also { registerElement(ConnectionConstrucorElement(reads + this, writes)) } + + /** + * Create and register a derived state. + */ + public fun derivedState( + dependencies: List>, + computeValue: () -> T, + ): DeviceStateWithDependencies { + val state = DeviceState.derived(this, dependencies, computeValue) + registerElement(StateConstructorElement(state)) + return state + } } /** @@ -210,7 +222,7 @@ public fun StateContainer.combineTo( ): Job { val descriptor = ConnectionConstrucorElement(setOf(sourceState1, sourceState2), setOf(targetState)) registerElement(descriptor) - return kotlinx.coroutines.flow.combine(sourceState1.valueFlow, sourceState2.valueFlow, transformation).onEach { + return combine(sourceState1.valueFlow, sourceState2.valueFlow, transformation).onEach { targetState.value = it }.launchIn(this).apply { invokeOnCompletion { @@ -231,7 +243,7 @@ public inline fun StateContainer.combineTo( ): Job { val descriptor = ConnectionConstrucorElement(sourceStates, setOf(targetState)) registerElement(descriptor) - return kotlinx.coroutines.flow.combine(sourceStates.map { it.valueFlow }, transformation).onEach { + return combine(sourceStates.map { it.valueFlow }, transformation).onEach { targetState.value = it }.launchIn(this).apply { invokeOnCompletion { diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/DeviceState.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/DeviceState.kt index 5b547f7..1868f8b 100644 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/DeviceState.kt +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/DeviceState.kt @@ -2,10 +2,7 @@ package space.kscience.controls.constructor import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.* import space.kscience.controls.constructor.units.NumericalValue import space.kscience.controls.constructor.units.UnitsOfMeasurement import kotlin.reflect.KProperty @@ -44,18 +41,43 @@ public operator fun MutableDeviceState.setValue(thisRef: Any?, property: } /** - * Device state with a value that depends on other device states + * DeviceState with dependencies on other DeviceStates. */ public interface DeviceStateWithDependencies : DeviceState { public val dependencies: Collection> } +/** + * Extension function to create a DerivedDeviceState. + */ +public fun DeviceState.Companion.derived( + scope: CoroutineScope, + dependencies: List>, + computeValue: () -> T +): DeviceStateWithDependencies = DerivedDeviceState(scope, dependencies, computeValue) + public fun DeviceState.withDependencies( dependencies: Collection>, ): DeviceStateWithDependencies = object : DeviceStateWithDependencies, DeviceState by this { override val dependencies: Collection> = dependencies } + +public fun DeviceState.Companion.fromFlow( + scope: CoroutineScope, + flow: Flow, + initialValue: T +): DeviceState { + val stateFlow = flow.stateIn(scope, SharingStarted.Eagerly, initialValue) + return object : DeviceState { + override val value: T get() = stateFlow.value + override val valueFlow: Flow get() = stateFlow + override fun toString(): String { + return "DeviceState.fromFlow(scope=$scope, flow=$flow, initialValue=$initialValue)" + } + } +} + /** * Create a new read-only [DeviceState] that mirrors receiver state by mapping the value with [mapper]. */ @@ -100,4 +122,28 @@ public fun DeviceState.Companion.combine( override val valueFlow: Flow = kotlinx.coroutines.flow.combine(state1.valueFlow, state2.valueFlow, mapper) override fun toString(): String = "DeviceState.combine(state1=$state1, state2=$state2)" +} + +/** + * A DeviceState that derives its value from other DeviceStates. + */ +public class DerivedDeviceState( + scope: CoroutineScope, + override val dependencies: List>, + computeValue: () -> T +) : DeviceStateWithDependencies { + private val _valueFlow: StateFlow + + init { + val flows = dependencies.map { it.valueFlow } + _valueFlow = combine(flows) { + computeValue() + }.stateIn(scope, SharingStarted.Eagerly, computeValue()) + } + + override val value: T get() = _valueFlow.value + override val valueFlow: Flow get() = _valueFlow + override fun toString(): String { + TODO("Not yet implemented") + } } \ No newline at end of file diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/devices/Drive.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/devices/Drive.kt index 9880508..4f3335c 100644 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/devices/Drive.kt +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/devices/Drive.kt @@ -3,7 +3,7 @@ package space.kscience.controls.constructor.devices import space.kscience.controls.constructor.DeviceConstructor import space.kscience.controls.constructor.MutableDeviceState import space.kscience.controls.constructor.property -import space.kscience.controls.constructor.units.NewtonsMeters +import space.kscience.controls.constructor.units.NewtonMeters import space.kscience.controls.constructor.units.NumericalValue import space.kscience.controls.constructor.units.numerical import space.kscience.dataforge.context.Context @@ -13,7 +13,7 @@ import space.kscience.dataforge.meta.MetaConverter public class Drive( context: Context, - force: MutableDeviceState> = MutableDeviceState(NumericalValue(0)), + force: MutableDeviceState> = MutableDeviceState(NumericalValue(0)), ) : DeviceConstructor(context) { - public val force: MutableDeviceState> by property(MetaConverter.numerical(), force) + public val force: MutableDeviceState> by property(MetaConverter.numerical(), force) } \ No newline at end of file diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/devices/LinearDrive.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/devices/LinearDrive.kt index 51039d1..6425023 100644 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/devices/LinearDrive.kt +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/devices/LinearDrive.kt @@ -4,7 +4,7 @@ import space.kscience.controls.constructor.* import space.kscience.controls.constructor.models.PidParameters import space.kscience.controls.constructor.models.PidRegulator import space.kscience.controls.constructor.units.Meters -import space.kscience.controls.constructor.units.NewtonsMeters +import space.kscience.controls.constructor.units.NewtonMeters import space.kscience.controls.constructor.units.NumericalValue import space.kscience.controls.constructor.units.numerical import space.kscience.dataforge.context.Context @@ -24,7 +24,7 @@ public class LinearDrive( public val position: DeviceState> by property(MetaConverter.numerical(), position) public val drive: Drive by device(drive) - public val pid: PidRegulator = model( + public val pid: PidRegulator = model( PidRegulator( context = context, position = position, diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/Annotation.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/Annotation.kt deleted file mode 100644 index b72d2f9..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/Annotation.kt +++ /dev/null @@ -1,9 +0,0 @@ -package space.kscience.controls.constructor.dsl.core - -/** - * Класс для представления аннотации. - */ -public data class Annotation( - val name: String, - val properties: Map -) diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/EquationSystem.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/EquationSystem.kt deleted file mode 100644 index c4615ba..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/EquationSystem.kt +++ /dev/null @@ -1,196 +0,0 @@ -package space.kscience.controls.constructor.dsl.core - -import space.kscience.controls.constructor.dsl.core.equations.* -import space.kscience.controls.constructor.dsl.core.expressions.* -import space.kscience.controls.constructor.dsl.core.variables.ArrayVariable -import space.kscience.controls.constructor.dsl.core.variables.Variable - -/** - * Класс для хранения и обработки системы уравнений. - */ -public class EquationSystem { - public val equations: MutableList = mutableListOf() - public val variables: MutableSet = mutableSetOf() - public val derivativeVariables: MutableSet = mutableSetOf() - public val algebraicEquations: MutableList = mutableListOf() - public val initialEquations: MutableList = mutableListOf() - public val conditionalEquations: MutableList = mutableListOf() - - /** - * Метод для добавления списка уравнений. - */ - public fun addEquations(eqs: List) { - for (eq in eqs) { - addEquation(eq) - } - } - - /** - * Метод для добавления одного уравнения. - */ - public fun addEquation(eq: EquationBase) { - equations.add(eq) - collectVariables(eq) - } - - private fun collectVariables(eq: EquationBase) { - when (eq) { - is Equation -> { - collectVariablesFromExpression(eq.leftExpression) - collectVariablesFromExpression(eq.rightExpression) - - // Classify the equation - if (isDifferentialEquation(eq)) { - // Handled in toODESystem() - } else { - algebraicEquations.add(eq) - } - } - is ConditionalEquation -> { - collectVariablesFromExpression(eq.condition) - collectVariables(eq.trueEquation) - eq.falseEquation?.let { collectVariables(it) } - conditionalEquations.add(eq) - } - is InitialEquation -> { - collectVariablesFromExpression(eq.leftExpression) - collectVariablesFromExpression(eq.rightExpression) - initialEquations.add(eq) - } - else -> { - throw UnsupportedOperationException("Unknown equation type: ${eq::class}") - } - } - } - - private fun collectVariablesFromExpression(expr: Expression) { - when (expr) { - is VariableExpression -> variables.add(expr.variable.name) - is ConstantExpression -> { - // Constants do not contain variables - } - is DerivativeExpression -> { - collectVariablesFromExpression(expr.variable) - derivativeVariables.add(expr.variable.variable.name) - } - is PartialDerivativeExpression -> { - collectVariablesFromExpression(expr.variable) - collectVariablesFromExpression(expr.respectTo) - // For partial derivatives, variables may need special handling - } - is BinaryExpression -> { - collectVariablesFromExpression(expr.left) - collectVariablesFromExpression(expr.right) - } - is FunctionCallExpression -> { - expr.arguments.forEach { collectVariablesFromExpression(it) } - } - is ArrayVariableExpression -> { - collectVariablesFromArrayVariable(expr.left) - collectVariablesFromArrayVariable(expr.right) - } - is ArrayScalarExpression -> { - collectVariablesFromArrayVariable(expr.array) - collectVariablesFromExpression(expr.scalar) - } - else -> { - throw UnsupportedOperationException("Unknown expression type: ${expr::class}") - } - } - } - - private fun collectVariablesFromArrayVariable(arrayVar: ArrayVariable) { - variables.add(arrayVar.name) - // Additional processing if necessary - } - - private fun isDifferentialEquation(eq: Equation): Boolean { - val leftIsDerivative = containsDerivative(eq.leftExpression) - val rightIsDerivative = containsDerivative(eq.rightExpression) - return leftIsDerivative || rightIsDerivative - } - - private fun containsDerivative(expr: Expression): Boolean { - return when (expr) { - is DerivativeExpression -> true - is BinaryExpression -> containsDerivative(expr.left) || containsDerivative(expr.right) - is FunctionCallExpression -> expr.arguments.any { containsDerivative(it) } - else -> false - } - } - - /** - * Подготовка системы ОДУ для численного решения. - */ - public fun toODESystem(): ODESystem { - val odeEquations = mutableListOf() - - // Process equations to separate ODEs and algebraic equations - for (eqBase in equations) { - when (eqBase) { - is Equation -> { - val eq = eqBase - if (isDifferentialEquation(eq)) { - // Handle differential equations - processDifferentialEquation(eq, odeEquations) - } else { - // Handle algebraic equations - algebraicEquations.add(eq) - } - } - is ConditionalEquation -> { - // Handle conditional equations - // For ODE system, we might need to process them differently - conditionalEquations.add(eqBase) - } - is InitialEquation -> { - // Initial equations are used for setting initial conditions - initialEquations.add(eqBase) - } - else -> { - throw UnsupportedOperationException("Unknown equation type: ${eqBase::class}") - } - } - } - - return ODESystem(odeEquations, algebraicEquations, initialEquations, conditionalEquations) - } - - private fun processDifferentialEquation(eq: Equation, odeEquations: MutableList) { - // Identify which side has the derivative - val derivativeExpr = findDerivativeExpression(eq.leftExpression) ?: findDerivativeExpression(eq.rightExpression) - if (derivativeExpr != null) { - val variableName = derivativeExpr.variable.variable.name - if (derivativeExpr.order != 1) { - throw UnsupportedOperationException("Only first-order derivatives are supported in this solver.") - } - - val rhs = if (eq.leftExpression is DerivativeExpression) eq.rightExpression else eq.leftExpression - odeEquations.add(ODEEquation(variableName, rhs)) - derivativeVariables.add(variableName) - } else { - throw UnsupportedOperationException("Equation is marked as differential but no derivative found.") - } - } - - private fun findDerivativeExpression(expr: Expression): DerivativeExpression? { - return when (expr) { - is DerivativeExpression -> expr - is BinaryExpression -> findDerivativeExpression(expr.left) ?: findDerivativeExpression(expr.right) - is FunctionCallExpression -> expr.arguments.mapNotNull { findDerivativeExpression(it) }.firstOrNull() - else -> null - } - } -} - -public data class ODEEquation( - val variableName: String, - val rhs: Expression -) - -public class ODESystem( - public val odeEquations: List, - public val algebraicEquations: List, - public val initialEquations: List, - public val conditionalEquations: List -) diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/Record.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/Record.kt deleted file mode 100644 index b722920..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/Record.kt +++ /dev/null @@ -1,19 +0,0 @@ -package space.kscience.controls.constructor.dsl.core - -import space.kscience.controls.constructor.dsl.core.units.PhysicalUnit -import space.kscience.controls.constructor.dsl.core.variables.Variable - -/** - * Класс для представления записи (record). - */ -public class Record(public val name: String) { - public val fields: MutableMap = mutableMapOf() - - - public fun field(name: String, unit: PhysicalUnit) { - if (fields.containsKey(name)) { - throw IllegalArgumentException("Field with name $name already exists in record $name.") - } - fields[name] = Variable(name, unit) - } -} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/algorithms/AlgorithmBuilder.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/algorithms/AlgorithmBuilder.kt deleted file mode 100644 index fc1691f..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/algorithms/AlgorithmBuilder.kt +++ /dev/null @@ -1,37 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.algorithms - -import space.kscience.controls.constructor.dsl.core.expressions.Expression -import space.kscience.controls.constructor.dsl.core.variables.Variable - -/** - * Класс для построения алгоритмов. - */ -public class AlgorithmBuilder { - public val statements: MutableList = mutableListOf() - - public fun assign(variable: Variable, expression: Expression) { - statements.add(AssignmentStatement(variable, expression)) - } - - public fun ifStatement(condition: Expression, init: AlgorithmBuilder.() -> Unit) { - val trueBuilder = AlgorithmBuilder() - trueBuilder.init() - statements.add(IfStatement(condition, trueBuilder.build(), null)) - } - - public fun forStatement(variable: Variable, range: IntRange, init: AlgorithmBuilder.() -> Unit) { - val bodyBuilder = AlgorithmBuilder() - bodyBuilder.init() - statements.add(ForStatement(variable, range, bodyBuilder.build())) - } - - public fun whileStatement(condition: Expression, init: AlgorithmBuilder.() -> Unit) { - val bodyBuilder = AlgorithmBuilder() - bodyBuilder.init() - statements.add(WhileStatement(condition, bodyBuilder.build())) - } - - public fun build(): Algorithm { - return Algorithm(statements.toList()) - } -} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/algorithms/AlgorithmSection.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/algorithms/AlgorithmSection.kt deleted file mode 100644 index d006c84..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/algorithms/AlgorithmSection.kt +++ /dev/null @@ -1,80 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.algorithms - -import space.kscience.controls.constructor.dsl.core.expressions.Expression -import space.kscience.controls.constructor.dsl.core.simulation.SimulationContext -import space.kscience.controls.constructor.dsl.core.variables.Variable - - -/** - * Абстрактный класс для операторов в алгоритмической секции. - */ -public sealed class Statement { - public abstract suspend fun execute(context: SimulationContext) -} - -public data class AssignmentStatement( - val variable: Variable, - val expression: Expression -) : Statement() { - override suspend fun execute(context: SimulationContext) { - val value = expression.evaluate(context.getVariableValues()) - context.updateVariable(variable.name, value) - } -} - -public data class IfStatement( - val condition: Expression, - val trueBranch: Algorithm, - val falseBranch: Algorithm? = null -) : Statement() { - override suspend fun execute(context: SimulationContext) { - val conditionValue = condition.evaluate(context.getVariableValues()) - if (conditionValue != 0.0) { - trueBranch.execute(context) - } else { - falseBranch?.execute(context) - } - } -} - -public data class ForStatement( - val variable: Variable, - val range: IntRange, - val body: Algorithm -) : Statement() { - override suspend fun execute(context: SimulationContext) { - for (i in range) { - context.updateVariable(variable.name, i.toDouble()) - body.execute(context) - } - } -} - -public data class WhileStatement( - val condition: Expression, - val body: Algorithm -) : Statement() { - override suspend fun execute(context: SimulationContext) { - while (condition.evaluate(context.getVariableValues()) != 0.0) { - body.execute(context) - } - } -} - -public class Algorithm(private val statements: List) { - /** - * Добавляет оператор в алгоритм. - */ - public fun addStatement(statement: Statement) { - (statements as MutableList).add(statement) - } - - /** - * Выполняет алгоритм в заданном контексте симуляции. - */ - public suspend fun execute(context: SimulationContext) { - for (statement in statements) { - statement.execute(context) - } - } -} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/components/Component.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/components/Component.kt deleted file mode 100644 index 828948b..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/components/Component.kt +++ /dev/null @@ -1,81 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.components - -import space.kscience.controls.constructor.dsl.core.Annotation -import space.kscience.controls.constructor.dsl.core.equations.Equation -import space.kscience.controls.constructor.dsl.core.variables.ParameterVariable -import space.kscience.controls.constructor.dsl.core.variables.Variable - -/** - * Базовый класс для всех компонентов модели. - */ -public abstract class Component(public val name: String) { - public val variables: MutableMap = mutableMapOf() - public val parameters: MutableMap = mutableMapOf() - public val equations: MutableList = mutableListOf() - public val connectors: MutableMap = mutableMapOf() - public val annotations: MutableList = mutableListOf() - public var isReplaceable: Boolean = false - - /** - * Метод для инициализации переменных. - */ - protected abstract fun initializeVariables() - - /** - * Метод для определения уравнений компонента. - */ - protected abstract fun defineEquations() - - /** - * Метод для добавления переменной. - */ - public fun addVariable(variable: V) { - if (variables.containsKey(variable.name)) { - throw IllegalArgumentException("Variable with name ${variable.name} already exists in component $name.") - } - variables[variable.name] = variable - } - - /** - * Метод для добавления параметра. - */ - public fun addParameter(parameter: P) { - if (parameters.containsKey(parameter.name)) { - throw IllegalArgumentException("Parameter with name ${parameter.name} already exists in component $name.") - } - parameters[parameter.name] = parameter - } - - /** - * Метод для добавления уравнения. - */ - public fun addEquation(equation: Equation) { - equations.add(equation) - } - - /** - * Метод для добавления коннектора. - */ - public fun addConnector(name: String, connector: C) { - if (connectors.containsKey(name)) { - throw IllegalArgumentException("Connector with name $name already exists in component $name.") - } - connectors[name] = connector - } - - public fun annotate(annotation: Annotation) { - annotations.add(annotation) - } - - /** - * Метод для модификации параметров компонента. - */ - public fun modify(modifier: Component.() -> Unit): Component { - this.apply(modifier) - return this - } - - public fun markAsReplaceable(isReplaceable: Boolean) { - this.isReplaceable = isReplaceable - } -} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/components/Connection.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/components/Connection.kt deleted file mode 100644 index a5a17e6..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/components/Connection.kt +++ /dev/null @@ -1,38 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.components - -import space.kscience.controls.constructor.dsl.core.equations.Equation -import space.kscience.controls.constructor.dsl.core.expressions.VariableExpression -import space.kscience.controls.constructor.dsl.core.variables.Variable - -/** - * Класс для представления соединения между двумя коннекторами. - */ -public class Connection( - public val connector1: C, - public val connector2: C -) { - init { - if (connector1.unit != connector2.unit) { - throw IllegalArgumentException("Cannot connect connectors with different units: ${connector1.unit} and ${connector2.unit}.") - } - } - - /** - * Генерация уравнений для соединения. - */ - public fun generateEquations(): List { - val equations = mutableListOf() - for ((varName, var1) in connector1.variables) { - val var2 = connector2.variables[varName] - ?: throw IllegalArgumentException("Variable $varName not found in connector ${connector2.name}.") - - equations.add( - Equation( - VariableExpression(var1), - VariableExpression(var2) - ) - ) - } - return equations - } -} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/components/Connector.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/components/Connector.kt deleted file mode 100644 index 8f2d9e1..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/components/Connector.kt +++ /dev/null @@ -1,22 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.components - -import space.kscience.controls.constructor.dsl.core.units.PhysicalUnit -import space.kscience.controls.constructor.dsl.core.variables.Variable - -/** - * Интерфейс для коннектора, позволяющего соединять компоненты. - */ -public interface Connector { - public val name: String - public val variables: Map - public val unit: PhysicalUnit -} - -/** - * Базовая реализация коннектора. - */ -public class BasicConnector( - override val name: String, - override val unit: PhysicalUnit, - override val variables: Map -) : Connector diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/components/Model.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/components/Model.kt deleted file mode 100644 index c6b4ac0..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/components/Model.kt +++ /dev/null @@ -1,163 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.components - -import space.kscience.controls.constructor.dsl.core.* -import space.kscience.controls.constructor.dsl.core.algorithms.* -import space.kscience.controls.constructor.dsl.core.equations.Equation -import space.kscience.controls.constructor.dsl.core.equations.ConditionalEquation -import space.kscience.controls.constructor.dsl.core.expressions.Expression -import space.kscience.controls.constructor.dsl.core.expressions.VariableExpression -import space.kscience.controls.constructor.dsl.core.units.PhysicalUnit -import space.kscience.controls.constructor.dsl.core.variables.ParameterVariable -import space.kscience.controls.constructor.dsl.core.variables.Variable -import space.kscience.controls.constructor.dsl.core.functions.Function -import space.kscience.controls.constructor.dsl.core.functions.FunctionBuilder - -/** - * Класс для представления модели, содержащей компоненты и соединения. - */ -public class Model(public val name: String, init: Model.() -> Unit) { - public val components: MutableMap> = mutableMapOf>() - public val connections: MutableList> = mutableListOf>() - public val variables: MutableMap = mutableMapOf() - public val parameters: MutableMap = mutableMapOf() - public val equations: MutableList = mutableListOf() - public val initialValues: MutableMap = mutableMapOf() - public val initialEquations: MutableList = mutableListOf() - public val conditionalEquations: MutableList = mutableListOf() - public val algorithms: MutableList = mutableListOf() - public val functions: MutableMap = mutableMapOf() - - init { - this.init() - } - - /** - * Определение переменной. - */ - public fun variable(name: String, unit: PhysicalUnit): VariableExpression { - val variable = Variable(name, unit) - variables[name] = variable - return VariableExpression(variable) - } - - /** - * Определение параметра. - */ - public fun parameter(name: String, unit: PhysicalUnit, value: Double): ParameterVariable { - val parameter = ParameterVariable(name, unit, value) - parameters[name] = parameter - return parameter - } - - /** - * Добавление уравнения. - */ - public fun equation(equationBuilder: () -> Equation) { - val equation = equationBuilder() - equations.add(equation) - } - - /** - * Установка начальных значений. - */ - public fun initialValues(init: MutableMap.() -> Unit) { - initialValues.apply(init) - } - - /** - * Добавление инициализационного уравнения. - */ - public fun initialEquation(equationBuilder: () -> Equation) { - val equation = equationBuilder() - initialEquations.add(equation) - } - - /** - * Метод для добавления компонента. - */ - public fun addComponent(component: Component<*, *, *>) { - if (components.containsKey(component.name)) { - throw IllegalArgumentException("Component with name ${component.name} already exists in model $name.") - } - components[component.name] = component - } - - /** - * Метод для добавления соединения. - */ - public fun addConnection(connection: Connection<*>) { - connections.add(connection) - } - - /** - * Метод для генерации системы уравнений модели. - */ - public fun generateEquationSystem(): EquationSystem { - val equationSystem = EquationSystem() - - // Добавление уравнений модели - equationSystem.addEquations(equations) - - // Добавление уравнений компонентов - for (component in components.values) { - equationSystem.addEquations(component.equations) - } - - // Добавление уравнений соединений - for (connection in connections) { - equationSystem.addEquations(connection.generateEquations()) - } - - // Добавление условных уравнений - for (condEq in conditionalEquations) { - equationSystem.addEquation(condEq) - } - - // Добавление инициализационных уравнений - equationSystem.addEquations(initialEquations) - - return equationSystem - } - - /** - * Метод для замены компонента. - */ - public fun replaceComponent(name: String, newComponent: Component<*, *, *>) { - val oldComponent = components[name] - if (oldComponent != null && oldComponent.isReplaceable) { - components[name] = newComponent - } else { - throw IllegalArgumentException("Component $name is not replaceable or does not exist.") - } - } - - /** - * Добавление условного уравнения. - */ - public fun conditionalEquation(condition: Expression, trueEquation: Equation, falseEquation: Equation? = null) { - val condEq = ConditionalEquation(condition, trueEquation, falseEquation) - conditionalEquations.add(condEq) - } - - /** - * Добавление алгоритма. - */ - public fun algorithm(init: AlgorithmBuilder.() -> Unit) { - val builder = AlgorithmBuilder() - builder.init() - algorithms.add(builder.build()) - } - - /** - * Добавление пользовательской функции. - */ - public fun function(name: String, init: FunctionBuilder.() -> Unit) { - if (functions.containsKey(name)) { - throw IllegalArgumentException("Function with name $name already exists in model $name.") - } - val builder = FunctionBuilder(name) - builder.init() - val function = builder.build() - functions[name] = function - } -} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/components/Package.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/components/Package.kt deleted file mode 100644 index 3667c84..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/components/Package.kt +++ /dev/null @@ -1,21 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.components - -/** - * Класс для представления пакета моделей. - */ -public class Package(public val name: String) { - public val models: MutableMap = mutableMapOf() - public val subPackages: MutableMap = mutableMapOf() - - public fun addModel(model: Model) { - models[model.name] = model - } - - public fun addPackage(pkg: Package) { - subPackages[pkg.name] = pkg - } - - public fun getModel(name: String): Model? { - return models[name] ?: subPackages.values.mapNotNull { it.getModel(name) }.firstOrNull() - } -} \ No newline at end of file diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/equations/ConditionalEquation.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/equations/ConditionalEquation.kt deleted file mode 100644 index 1b03000..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/equations/ConditionalEquation.kt +++ /dev/null @@ -1,12 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.equations - -import space.kscience.controls.constructor.dsl.core.expressions.Expression - -/** - * Класс для условного уравнения. - */ -public class ConditionalEquation( - public val condition: Expression, - public val trueEquation: Equation, - public val falseEquation: Equation? = null -) : EquationBase() diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/equations/Equation.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/equations/Equation.kt deleted file mode 100644 index fa19024..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/equations/Equation.kt +++ /dev/null @@ -1,24 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.equations - -import space.kscience.controls.constructor.dsl.core.expressions.Expression - -/** - * Класс для представления уравнения. - */ -public class Equation( - public val leftExpression: Expression, - public val rightExpression: Expression -) : EquationBase() { - init { - // Проверка совместимости единиц измерения - if (!leftExpression.unit.dimension.isCompatibleWith(rightExpression.unit.dimension)) { - throw IllegalArgumentException("Units of left and right expressions in the equation are not compatible.") - } - } - - public fun evaluate(values: Map): Double { - val leftValue = leftExpression.evaluate(values) - val rightValue = rightExpression.evaluate(values) - return leftValue - rightValue - } -} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/equations/EquationBase.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/equations/EquationBase.kt deleted file mode 100644 index d8b94b9..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/equations/EquationBase.kt +++ /dev/null @@ -1,6 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.equations - -/** - * Абстрактный базовый класс для уравнений. - */ -public sealed class EquationBase diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/equations/InitialEquation.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/equations/InitialEquation.kt deleted file mode 100644 index 2415c3d..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/equations/InitialEquation.kt +++ /dev/null @@ -1,11 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.equations - -import space.kscience.controls.constructor.dsl.core.expressions.Expression - -/** - * Class representing an initial equation for setting initial conditions. - */ -public class InitialEquation( - public val leftExpression: Expression, - public val rightExpression: Expression -) : EquationBase() diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/events/AssignAction.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/events/AssignAction.kt deleted file mode 100644 index 60c40eb..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/events/AssignAction.kt +++ /dev/null @@ -1,18 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.events - -import space.kscience.controls.constructor.dsl.core.expressions.Expression -import space.kscience.controls.constructor.dsl.core.simulation.SimulationContext -import space.kscience.controls.constructor.dsl.core.variables.Variable - -/** - * Действие присваивания нового значения переменной. - */ -public data class AssignAction( - val variable: Variable, - val newValue: Expression -) : EventAction() { - override suspend fun execute(context: SimulationContext) { - val value = newValue.evaluate(context.getVariableValues()) - context.updateVariable(variable.name, value) - } -} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/events/Event.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/events/Event.kt deleted file mode 100644 index 6a66950..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/events/Event.kt +++ /dev/null @@ -1,30 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.events - -import space.kscience.controls.constructor.dsl.core.simulation.SimulationContext -import space.kscience.controls.constructor.dsl.core.expressions.Expression - -/** - * Класс для представления события. - */ -public class Event( - public val condition: Expression, - public val actions: List -) { - /** - * Метод для проверки, произошло ли событие. - */ - public suspend fun checkEvent(context: SimulationContext): Boolean { - val values = context.getVariableValues() - val conditionValue = condition.evaluate(values) - return conditionValue > 0 // Событие происходит, когда условие положительно - } - - /** - * Метод для выполнения действий при событии. - */ - public suspend fun executeActions(context: SimulationContext) { - for (action in actions) { - action.execute(context) - } - } -} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/events/EventAction.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/events/EventAction.kt deleted file mode 100644 index d4bd879..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/events/EventAction.kt +++ /dev/null @@ -1,12 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.events - -import space.kscience.controls.constructor.dsl.core.expressions.Expression -import space.kscience.controls.constructor.dsl.core.simulation.SimulationContext -import space.kscience.controls.constructor.dsl.core.variables.Variable - -/** - * Абстрактный класс для действий при событии. - */ -public sealed class EventAction { - public abstract suspend fun execute(context: SimulationContext) -} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/events/ReinitAction.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/events/ReinitAction.kt deleted file mode 100644 index ccc70e9..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/events/ReinitAction.kt +++ /dev/null @@ -1,18 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.events - -import space.kscience.controls.constructor.dsl.core.expressions.Expression -import space.kscience.controls.constructor.dsl.core.simulation.SimulationContext -import space.kscience.controls.constructor.dsl.core.variables.Variable - -/** - * Действие реинициализации переменной. - */ -public data class ReinitAction( - val variable: Variable, - val newValue: Expression -) : EventAction() { - override suspend fun execute(context: SimulationContext) { - val value = newValue.evaluate(context.getVariableValues()) - context.updateVariable(variable.name, value) - } -} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expression/ExpressionOperators.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expression/ExpressionOperators.kt deleted file mode 100644 index 8354f41..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expression/ExpressionOperators.kt +++ /dev/null @@ -1,91 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.expression - -import space.kscience.controls.constructor.dsl.core.variables.ArrayVariable -import space.kscience.controls.constructor.dsl.core.equations.Equation -import space.kscience.controls.constructor.dsl.core.expressions.ArrayBinaryExpression -import space.kscience.controls.constructor.dsl.core.expressions.ArrayBinaryOperator -import space.kscience.controls.constructor.dsl.core.expressions.ArrayScalarExpression -import space.kscience.controls.constructor.dsl.core.expressions.ArrayScalarOperator -import space.kscience.controls.constructor.dsl.core.expressions.BinaryExpression -import space.kscience.controls.constructor.dsl.core.expressions.BinaryOperator -import space.kscience.controls.constructor.dsl.core.expressions.ConstantExpression -import space.kscience.controls.constructor.dsl.core.expressions.Expression -import space.kscience.controls.constructor.dsl.core.expressions.FunctionCallExpression -import space.kscience.controls.constructor.dsl.core.units.Units -import space.kscience.controls.constructor.dsl.core.functions.Function - -// Операторные функции для скалярных выражений - -public operator fun Expression.plus(other: Expression): Expression { - return BinaryExpression(this, BinaryOperator.ADD, other) -} - -public operator fun Expression.minus(other: Expression): Expression { - return BinaryExpression(this, BinaryOperator.SUBTRACT, other) -} - -public operator fun Expression.times(other: Expression): Expression { - return BinaryExpression(this, BinaryOperator.MULTIPLY, other) -} - -public operator fun Expression.div(other: Expression): Expression { - return BinaryExpression(this, BinaryOperator.DIVIDE, other) -} - -public infix fun Expression.pow(exponent: Expression): Expression { - return FunctionCallExpression("pow", listOf(this, exponent)) -} - -public infix fun Expression.pow(exponent: Double): Expression { - return FunctionCallExpression("pow", listOf(this, ConstantExpression(exponent, Units.dimensionless))) -} - -// Сравнительные операторы - -public infix fun Expression.eq(other: Expression): Equation { - return Equation(this, other) -} - -public infix fun Expression.gt(other: Expression): Expression { - return BinaryExpression(this, BinaryOperator.GREATER_THAN, other) -} - -public infix fun Expression.lt(other: Expression): Expression { - return BinaryExpression(this, BinaryOperator.LESS_THAN, other) -} - -public infix fun Expression.ge(other: Expression): Expression { - return BinaryExpression(this, BinaryOperator.GREATER_OR_EQUAL, other) -} - -public infix fun Expression.le(other: Expression): Expression { - return BinaryExpression(this, BinaryOperator.LESS_OR_EQUAL, other) -} - -// Операторные функции для массивов - -public operator fun ArrayVariable.plus(other: ArrayVariable): Expression { - return ArrayBinaryExpression(this, ArrayBinaryOperator.ADD, other) -} - -public operator fun ArrayVariable.minus(other: ArrayVariable): Expression { - return ArrayBinaryExpression(this, ArrayBinaryOperator.SUBTRACT, other) -} - -public operator fun ArrayVariable.times(scalar: Expression): Expression { - return ArrayScalarExpression(this, ArrayScalarOperator.MULTIPLY, scalar) -} - -public operator fun ArrayVariable.div(scalar: Expression): Expression { - return ArrayScalarExpression(this, ArrayScalarOperator.DIVIDE, scalar) -} - -/** - * Функция для вызова пользовательской функции в выражении. - */ -public fun callFunction(function: Function, vararg args: Expression): FunctionCallExpression { - require(args.size == function.inputs.size) { - "Number of arguments does not match number of function inputs." - } - return FunctionCallExpression(function.name, args.toList()) -} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayBinaryExpression.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayBinaryExpression.kt deleted file mode 100644 index b2ad67c..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayBinaryExpression.kt +++ /dev/null @@ -1,37 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.expressions - -import space.kscience.controls.constructor.dsl.core.units.PhysicalUnit -import space.kscience.controls.constructor.dsl.core.variables.ArrayVariable - -/** - * Класс для представления бинарной операции между двумя массивами. - */ -public class ArrayBinaryExpression( - public val left: ArrayVariable, - public val operator: ArrayBinaryOperator, - public val right: ArrayVariable -) : Expression() { - - override val unit: PhysicalUnit = when (operator) { - ArrayBinaryOperator.ADD, ArrayBinaryOperator.SUBTRACT -> { - if (left.unit != right.unit) { - throw IllegalArgumentException("Units must be the same for array addition or subtraction.") - } - left.unit - } - ArrayBinaryOperator.MULTIPLY -> left.unit * right.unit - ArrayBinaryOperator.DIVIDE -> left.unit / right.unit - } - - override fun evaluate(values: Map): Double { - throw UnsupportedOperationException("Cannot evaluate array expression to a scalar value.") - } - - override fun checkDimensions() { - TODO() - } - - override fun derivative(variable: VariableExpression): Expression { - throw UnsupportedOperationException("Derivative of array expression is not implemented.") - } -} \ No newline at end of file diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayBinaryOperator.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayBinaryOperator.kt deleted file mode 100644 index 7f31301..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayBinaryOperator.kt +++ /dev/null @@ -1,11 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.expressions - -/** - * Перечисление для бинарных операций над массивами. - */ -public enum class ArrayBinaryOperator { - ADD, - SUBTRACT, - MULTIPLY, - DIVIDE -} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayExpression.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayExpression.kt deleted file mode 100644 index 982afdc..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayExpression.kt +++ /dev/null @@ -1,3 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.expressions - -public sealed class ArrayExpression : Expression() \ No newline at end of file diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayScalarExpression.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayScalarExpression.kt deleted file mode 100644 index f40a4a9..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayScalarExpression.kt +++ /dev/null @@ -1,34 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.expressions - -import space.kscience.controls.constructor.dsl.core.units.PhysicalUnit -import space.kscience.controls.constructor.dsl.core.variables.ArrayVariable - -/** - * Представляет операцию между массивом и скаляром. - */ -public class ArrayScalarExpression( - public val array: ArrayVariable, - public val operator: ArrayScalarOperator, - public val scalar: Expression -) : Expression() { - - override val unit: PhysicalUnit = when (operator) { - ArrayScalarOperator.MULTIPLY -> array.unit * scalar.unit - ArrayScalarOperator.DIVIDE -> array.unit / scalar.unit - } - - override fun evaluate(values: Map): Double { - // Невозможно вычислить массивное выражение до скалярного значения - throw UnsupportedOperationException("Cannot evaluate array expression to a scalar value.") - } - - override fun checkDimensions() { -// array.checkDimensions() - scalar.checkDimensions() - // Единицы измерения уже проверены в свойстве `unit` - } - - override fun derivative(variable: VariableExpression): Expression { - throw UnsupportedOperationException("Derivative of array expression is not implemented.") - } -} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayScalarOperator.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayScalarOperator.kt deleted file mode 100644 index f467eec..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayScalarOperator.kt +++ /dev/null @@ -1,9 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.expressions - -/** - * Перечисление операторов между массивом и скаляром. - */ -public enum class ArrayScalarOperator { - MULTIPLY, - DIVIDE -} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayVariableExpression.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayVariableExpression.kt deleted file mode 100644 index 35db9de..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayVariableExpression.kt +++ /dev/null @@ -1,42 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.expressions - -import space.kscience.controls.constructor.dsl.core.expressions.Expression -import space.kscience.controls.constructor.dsl.core.units.PhysicalUnit -import space.kscience.controls.constructor.dsl.core.variables.ArrayVariable - -/** - * Представляет операцию между двумя массивами. - */ -public class ArrayVariableExpression( - public val left: ArrayVariable, - public val operator: ArrayBinaryOperator, - public val right: ArrayVariable -) : Expression() { - - override val unit: PhysicalUnit = when (operator) { - ArrayBinaryOperator.ADD, ArrayBinaryOperator.SUBTRACT -> { - if (left.unit != right.unit) { - throw IllegalArgumentException("Units must be the same for array addition or subtraction.") - } - left.unit - } - ArrayBinaryOperator.MULTIPLY -> left.unit * right.unit - ArrayBinaryOperator.DIVIDE -> left.unit / right.unit - } - - override fun evaluate(values: Map): Double { - // Невозможно вычислить массивное выражение до скалярного значения - throw UnsupportedOperationException("Cannot evaluate array expression to a scalar value.") - } - - override fun checkDimensions() { -// left.checkDimensions() -// right.checkDimensions() - // Единицы измерения уже проверены в свойстве `unit` - } - - override fun derivative(variable: VariableExpression): Expression { - // Производная массива не реализована - throw UnsupportedOperationException("Derivative of array expression is not implemented.") - } -} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/BinaryExpression.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/BinaryExpression.kt deleted file mode 100644 index 8c2de51..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/BinaryExpression.kt +++ /dev/null @@ -1,135 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.expressions - -import space.kscience.controls.constructor.dsl.core.units.PhysicalUnit -import space.kscience.controls.constructor.dsl.core.units.Units -import kotlin.math.* - -/** - * Перечисление типов бинарных операций. - */ -public enum class BinaryOperator { - ADD, - SUBTRACT, - MULTIPLY, - DIVIDE, - POWER, - GREATER_THAN, - LESS_THAN, - GREATER_OR_EQUAL, - LESS_OR_EQUAL -} - -/** - * Класс для представления бинарной операции. - */ -public class BinaryExpression( - public val left: Expression, - public val operator: BinaryOperator, - public val right: Expression -) : Expression() { - - override val unit: PhysicalUnit = when (operator) { - BinaryOperator.ADD, BinaryOperator.SUBTRACT -> { - if (left.unit != right.unit) { - throw IllegalArgumentException("Units of left and right expressions must be the same for addition or subtraction.") - } - left.unit - } - BinaryOperator.MULTIPLY -> left.unit * right.unit - BinaryOperator.DIVIDE -> left.unit / right.unit - BinaryOperator.POWER -> { - if (right is ConstantExpression) { - left.unit.pow(right.value) - } else { - throw IllegalArgumentException("Exponent must be a constant expression.") - } - } - BinaryOperator.GREATER_THAN, BinaryOperator.LESS_THAN, - BinaryOperator.GREATER_OR_EQUAL, BinaryOperator.LESS_OR_EQUAL -> { - Units.dimensionless - } - } - - override fun evaluate(values: Map): Double { - val leftValue = left.evaluate(values) - val rightValue = right.evaluate(values) - return when (operator) { - BinaryOperator.ADD -> leftValue + rightValue - BinaryOperator.SUBTRACT -> leftValue - rightValue - BinaryOperator.MULTIPLY -> leftValue * rightValue - BinaryOperator.DIVIDE -> leftValue / rightValue - BinaryOperator.POWER -> leftValue.pow(rightValue) - BinaryOperator.GREATER_THAN -> if (leftValue > rightValue) 1.0 else 0.0 - BinaryOperator.LESS_THAN -> if (leftValue < rightValue) 1.0 else 0.0 - BinaryOperator.GREATER_OR_EQUAL -> if (leftValue >= rightValue) 1.0 else 0.0 - BinaryOperator.LESS_OR_EQUAL -> if (leftValue <= rightValue) 1.0 else 0.0 - } - } - - override fun checkDimensions() { - left.checkDimensions() - right.checkDimensions() - when (operator) { - BinaryOperator.ADD, BinaryOperator.SUBTRACT -> { - if (left.unit != right.unit) { - throw IllegalArgumentException("Units must be the same for addition or subtraction.") - } - } - BinaryOperator.MULTIPLY, BinaryOperator.DIVIDE -> { - // Единицы измерения уже вычислены в `unit` - } - BinaryOperator.POWER -> { - if (!right.unit.dimension.isDimensionless()) { - throw IllegalArgumentException("Exponent must be dimensionless.") - } - } - BinaryOperator.GREATER_THAN, BinaryOperator.LESS_THAN, - BinaryOperator.GREATER_OR_EQUAL, BinaryOperator.LESS_OR_EQUAL -> { - if (left.unit != right.unit) { - throw IllegalArgumentException("Units must be the same for comparison operations.") - } - } - } - } - - override fun derivative(variable: VariableExpression): Expression { - return when (operator) { - BinaryOperator.ADD, BinaryOperator.SUBTRACT -> { - BinaryExpression(left.derivative(variable), operator, right.derivative(variable)) - } - BinaryOperator.MULTIPLY -> { - // Правило произведения: (u*v)' = u'*v + u*v' - val leftDerivative = BinaryExpression(left.derivative(variable), BinaryOperator.MULTIPLY, right) - val rightDerivative = BinaryExpression(left, BinaryOperator.MULTIPLY, right.derivative(variable)) - BinaryExpression(leftDerivative, BinaryOperator.ADD, rightDerivative) - } - BinaryOperator.DIVIDE -> { - // Правило частного: (u/v)' = (u'*v - u*v') / v^2 - val numerator = BinaryExpression( - BinaryExpression(left.derivative(variable), BinaryOperator.MULTIPLY, right), - BinaryOperator.SUBTRACT, - BinaryExpression(left, BinaryOperator.MULTIPLY, right.derivative(variable)) - ) - val denominator = BinaryExpression(right, BinaryOperator.POWER, ConstantExpression(2.0, Units.dimensionless)) - BinaryExpression(numerator, BinaryOperator.DIVIDE, denominator) - } - BinaryOperator.POWER -> { - if (right is ConstantExpression) { - // (u^n)' = n * u^(n-1) * u' - val n = right.value - val newExponent = ConstantExpression(n - 1.0, Units.dimensionless) - val baseDerivative = left.derivative(variable) - val powerExpression = BinaryExpression(left, BinaryOperator.POWER, newExponent) - BinaryExpression( - BinaryExpression(ConstantExpression(n, Units.dimensionless), BinaryOperator.MULTIPLY, powerExpression), - BinaryOperator.MULTIPLY, - baseDerivative - ) - } else { - throw UnsupportedOperationException("Derivative of expression with variable exponent is not supported.") - } - } - else -> throw UnsupportedOperationException("Derivative not implemented for operator $operator.") - } - } -} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ConstantExpression.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ConstantExpression.kt deleted file mode 100644 index c475a50..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ConstantExpression.kt +++ /dev/null @@ -1,20 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.expressions - -import space.kscience.controls.constructor.dsl.core.units.PhysicalUnit -import space.kscience.controls.constructor.dsl.core.units.Units - -/** - * Класс для представления константного значения. - */ -public class ConstantExpression(public val value: Double, override val unit: PhysicalUnit) : Expression() { - override fun evaluate(values: Map): Double { - return value - } - override fun checkDimensions() { - // Константа имеет единицу измерения, дополнительных проверок не требуется - } - - override fun derivative(variable: VariableExpression): Expression { - return ConstantExpression(0.0, Units.dimensionless) - } -} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/DerivativeExpression.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/DerivativeExpression.kt deleted file mode 100644 index d253e0b..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/DerivativeExpression.kt +++ /dev/null @@ -1,30 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.expressions - -import space.kscience.controls.constructor.dsl.core.units.PhysicalUnit -import space.kscience.controls.constructor.dsl.core.units.Units - -/** - * Класс для представления производной переменной по времени. - */ -public class DerivativeExpression( - public val variable: VariableExpression, - public val order: Int = 1 // Порядок производной (по умолчанию 1) -) : Expression() { - - override val unit: PhysicalUnit = variable.unit / Units.second.pow(order.toDouble()) - - override fun evaluate(values: Map): Double { - // вычисление производной невозможно в этом контексте - throw UnsupportedOperationException("Cannot evaluate derivative directly.") - } - - override fun checkDimensions() { - variable.checkDimensions() - // Единица измерения уже рассчитана - } - - override fun derivative(variable: VariableExpression): Expression { - // Производная от производной - это производная более высокого порядка - return DerivativeExpression(this.variable, this.order + 1) - } -} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/Expression.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/Expression.kt deleted file mode 100644 index 8d808c0..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/Expression.kt +++ /dev/null @@ -1,34 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.expressions - -import space.kscience.controls.constructor.dsl.core.units.PhysicalUnit - -/** - * Абстрактный класс для представления математических выражений. - * Каждое выражение связано с единицей измерения. - */ -public abstract class Expression { - public abstract val unit: PhysicalUnit - - /** - * Метод для вычисления численного значения выражения. - * Параметр values содержит значения переменных. - */ - public abstract fun evaluate(values: Map): Double - - /** - * Проверка размерности выражения. - */ - public abstract fun checkDimensions() - - /** - * Упрощение выражения. - */ - public open fun simplify(): Expression = this - - /** - * Производная выражения по заданной переменной. - */ - public open fun derivative(variable: VariableExpression): Expression { - throw UnsupportedOperationException("Derivative not implemented for this expression type.") - } -} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/FunctionCallExpression.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/FunctionCallExpression.kt deleted file mode 100644 index 39264b4..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/FunctionCallExpression.kt +++ /dev/null @@ -1,91 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.expressions - -import space.kscience.controls.constructor.dsl.core.units.Units -import space.kscience.controls.constructor.dsl.core.units.PhysicalUnit -import kotlin.math.* - -/** - * Класс для представления вызова математической функции. - */ -public class FunctionCallExpression( - public val functionName: String, - public val arguments: List -) : Expression() { - - override val unit: PhysicalUnit = determineUnit() - - private fun determineUnit(): PhysicalUnit { - return when (functionName) { - "sin", "cos", "tan", "asin", "acos", "atan", - "sinh", "cosh", "tanh", "asinh", "acosh", "atanh", - "exp", "log", "log10", "signum" -> Units.dimensionless - "sqrt", "cbrt", "abs" -> arguments[0].unit - "pow", "max", "min" -> arguments[0].unit - else -> throw IllegalArgumentException("Unknown function: $functionName") - } - } - - override fun evaluate(values: Map): Double { - val argValues = arguments.map { it.evaluate(values) } - return when (functionName) { - "sin" -> sin(argValues[0]) - "cos" -> cos(argValues[0]) - "tan" -> tan(argValues[0]) - "asin" -> asin(argValues[0]) - "acos" -> acos(argValues[0]) - "atan" -> atan(argValues[0]) - "sinh" -> sinh(argValues[0]) - "cosh" -> cosh(argValues[0]) - "tanh" -> tanh(argValues[0]) - "asinh" -> asinh(argValues[0]) - "acosh" -> acosh(argValues[0]) - "atanh" -> atanh(argValues[0]) - "exp" -> exp(argValues[0]) - "log" -> ln(argValues[0]) - "log10" -> log10(argValues[0]) - "sqrt" -> sqrt(argValues[0]) - "cbrt" -> cbrt(argValues[0]) - "abs" -> abs(argValues[0]) - "signum" -> sign(argValues[0]) - "pow" -> argValues[0].pow(argValues[1]) - "max" -> max(argValues[0], argValues[1]) - "min" -> min(argValues[0], argValues[1]) - else -> throw IllegalArgumentException("Unknown function: $functionName") - } - } - - override fun checkDimensions() { - when (functionName) { - "sin", "cos", "tan", "asin", "acos", "atan", - "sinh", "cosh", "tanh", "asinh", "acosh", "atanh", - "exp", "log", "log10", "signum" -> { - val argUnit = arguments[0].unit - if (!argUnit.dimension.isDimensionless()) { - throw IllegalArgumentException("Argument of $functionName must be dimensionless.") - } - } - "sqrt", "cbrt", "abs" -> { - // Единица измерения сохраняется, дополнительных проверок не требуется - } - "pow" -> { - val exponentUnit = arguments[1].unit - if (!exponentUnit.dimension.isDimensionless()) { - throw IllegalArgumentException("Exponent in $functionName must be dimensionless.") - } - } - "max", "min" -> { - if (arguments[0].unit != arguments[1].unit) { - throw IllegalArgumentException("Arguments of $functionName must have the same units.") - } - } - else -> throw IllegalArgumentException("Unknown function: $functionName") - } - // Проверяем размерности аргументов - arguments.forEach { it.checkDimensions() } - } - - override fun derivative(variable: VariableExpression): Expression { - // Реализация производной для функций - throw UnsupportedOperationException("Derivative for function $functionName is not implemented.") - } -} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/PartialDerivativeExpression.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/PartialDerivativeExpression.kt deleted file mode 100644 index 76c3355..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/PartialDerivativeExpression.kt +++ /dev/null @@ -1,31 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.expressions - -import space.kscience.controls.constructor.dsl.core.units.PhysicalUnit - -/** - * Представляет частную производную переменной по другой переменной. - */ -public class PartialDerivativeExpression( - public val variable: VariableExpression, - public val respectTo: VariableExpression, - public val order: Int = 1 -) : Expression() { - - override val unit: PhysicalUnit = variable.unit / respectTo.unit.pow(order.toDouble()) - - override fun evaluate(values: Map): Double { - // Невозможно вычислить частную производную без дополнительного контекста - throw UnsupportedOperationException("Cannot evaluate partial derivative directly.") - } - - override fun checkDimensions() { - variable.checkDimensions() - respectTo.checkDimensions() - // Единицы измерения уже рассчитаны - } - - override fun derivative(variable: VariableExpression): Expression { - // Производная частной производной не реализована - throw UnsupportedOperationException("Derivative of partial derivative is not implemented.") - } -} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/VariableExpression.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/VariableExpression.kt deleted file mode 100644 index 5a03a2f..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/VariableExpression.kt +++ /dev/null @@ -1,32 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.expressions - -import space.kscience.controls.constructor.dsl.core.units.PhysicalUnit -import space.kscience.controls.constructor.dsl.core.units.Units -import space.kscience.controls.constructor.dsl.core.variables.Variable - -/** - * Класс для представления переменной в выражении. - */ -public class VariableExpression(public val variable: Variable) : Expression() { - override val unit: PhysicalUnit = variable.unit - - override fun evaluate(values: Map): Double { - return values[variable.name] ?: throw IllegalArgumentException("Variable ${variable.name} is not defined.") - } - - override fun checkDimensions() { - // Переменная уже имеет единицу измерения, дополнительных проверок не требуется - } - - override fun derivative(variable: VariableExpression): Expression { - return if (this.variable == variable.variable) { - ConstantExpression(1.0, Units.dimensionless) - } else { - ConstantExpression(0.0, Units.dimensionless) - } - } - - public fun partialDerivative(respectTo: VariableExpression, order: Int = 1): PartialDerivativeExpression { - return PartialDerivativeExpression(this, respectTo, order) - } -} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/functions/Function.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/functions/Function.kt deleted file mode 100644 index 938bb46..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/functions/Function.kt +++ /dev/null @@ -1,14 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.functions - -import space.kscience.controls.constructor.dsl.core.algorithms.Algorithm -import space.kscience.controls.constructor.dsl.core.variables.Variable - -/** - * Класс для представления пользовательской функции. - */ -public data class Function( - val name: String, - val inputs: List, - val outputs: List, - val algorithm: Algorithm -) diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/functions/FunctionBuilder.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/functions/FunctionBuilder.kt deleted file mode 100644 index 25fb6f2..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/functions/FunctionBuilder.kt +++ /dev/null @@ -1,50 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.functions - -import space.kscience.controls.constructor.dsl.core.algorithms.AlgorithmBuilder -import space.kscience.controls.constructor.dsl.core.algorithms.Algorithm -import space.kscience.controls.constructor.dsl.core.components.Model -import space.kscience.controls.constructor.dsl.core.units.PhysicalUnit -import space.kscience.controls.constructor.dsl.core.variables.Causality -import space.kscience.controls.constructor.dsl.core.variables.Variable - -/** - * Класс для построения пользовательской функции. - */ -public class FunctionBuilder(private val name: String) { - public val inputs: MutableList = mutableListOf() - public val outputs: MutableList = mutableListOf() - private val algorithmBuilder = AlgorithmBuilder() - - /** - * Определение входного параметра функции. - */ - public fun input(name: String, unit: PhysicalUnit): Variable { - val variable = Variable(name, unit, causality = Causality.INPUT) - inputs.add(variable) - return variable - } - - /** - * Определение выходного параметра функции. - */ - public fun output(name: String, unit: PhysicalUnit): Variable { - val variable = Variable(name, unit, causality = Causality.OUTPUT) - outputs.add(variable) - return variable - } - - /** - * Определение алгоритмического тела функции. - */ - public fun algorithm(init: AlgorithmBuilder.() -> Unit) { - algorithmBuilder.init() - } - - /** - * Построение функции. - */ - public fun build(): Function { - val algorithm = algorithmBuilder.build() - return Function(name, inputs, outputs, algorithm) - } -} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/simulation/NumericSolver.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/simulation/NumericSolver.kt deleted file mode 100644 index fdae759..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/simulation/NumericSolver.kt +++ /dev/null @@ -1,37 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.simulation - -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.StateFlow -import space.kscience.controls.constructor.dsl.core.EquationSystem -import space.kscience.controls.constructor.dsl.core.events.Event - -public interface NumericSolver { - /** - * Инициализирует решатель. - */ - public suspend fun initialize( - equationSystem: EquationSystem, - context: SimulationContext - ) - - /** - * Выполняет один шаг симуляции. - */ - public suspend fun step(context: SimulationContext, timeStep: Double): Boolean - - /** - * Запускает симуляцию. - */ - public suspend fun solve( - equationSystem: EquationSystem, - context: SimulationContext, - timeStart: Double, - timeEnd: Double, - timeStep: Double - ): SimulationResult -} - -public data class SimulationResult( - val timePoints: List, - val variableValues: Map> -) diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/simulation/SimulationContext.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/simulation/SimulationContext.kt deleted file mode 100644 index 200b8fe..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/simulation/SimulationContext.kt +++ /dev/null @@ -1,93 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.simulation - -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.channels.Channel -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.launch -import space.kscience.controls.constructor.dsl.core.events.Event -import space.kscience.controls.constructor.dsl.core.variables.Variable - -/** - * Класс для хранения состояния симуляции. - */ -public class SimulationContext( - private val scope: CoroutineScope -) { - public var currentTime: Double = 0.0 - private val _variableValues: MutableMap> = mutableMapOf() - - // Канал для событий - private val eventChannel = Channel() - - // Список событий - public val events: MutableList = mutableListOf() - - /** - * Регистрирует переменную в контексте симуляции. - */ - public fun registerVariable(variable: Variable) { - _variableValues[variable.name] = MutableStateFlow(variable.value) - } - - /** - * Обновляет значение переменной. - */ - public suspend fun updateVariable(name: String, value: Double) { - _variableValues[name]?.emit(value) - ?: throw IllegalArgumentException("Variable $name is not defined in the simulation context.") - } - - /** - * Получает текущее значение переменной. - */ - public fun getCurrentVariableValue(name: String): Double { - return _variableValues[name]?.value - ?: throw IllegalArgumentException("Variable $name is not defined in the simulation context.") - } - - /** - * Возвращает текущие значения всех переменных. - */ - public fun getVariableValues(): Map { - return _variableValues.mapValues { it.value.value } - } - - /** - * Отправляет событие на обработку. - */ - public suspend fun sendEvent(event: Event) { - eventChannel.send(event) - } - - /** - * Запускает обработку событий. - */ - public fun startEventHandling() { - scope.launch { - for (event in eventChannel) { - if (event.checkEvent(this@SimulationContext)) { - event.executeActions(this@SimulationContext) - } - } - } - } - - /** - * Обрабатывает события из списка events. - */ - public suspend fun handleEvents() { - for (event in events) { - if (event.checkEvent(this)) { - event.executeActions(this) - } - } - } - - /** - * Увеличивает время симуляции. - */ - public suspend fun advanceTime(deltaTime: Double) { - currentTime += deltaTime - } -} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/units/Dimension.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/units/Dimension.kt deleted file mode 100644 index a4ae362..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/units/Dimension.kt +++ /dev/null @@ -1,78 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.units - -import kotlin.math.abs - -/** - * Класс для представления размерности физической величины. - */ -public data class Dimension( - val length: Double = 0.0, - val mass: Double = 0.0, - val time: Double = 0.0, - val electricCurrent: Double = 0.0, - val temperature: Double = 0.0, - val amountOfSubstance: Double = 0.0, - val luminousIntensity: Double = 0.0 -) { - public operator fun plus(other: Dimension): Dimension { - return Dimension( - length + other.length, - mass + other.mass, - time + other.time, - electricCurrent + other.electricCurrent, - temperature + other.temperature, - amountOfSubstance + other.amountOfSubstance, - luminousIntensity + other.luminousIntensity - ) - } - - public operator fun minus(other: Dimension): Dimension { - return Dimension( - length - other.length, - mass - other.mass, - time - other.time, - electricCurrent - other.electricCurrent, - temperature - other.temperature, - amountOfSubstance - other.amountOfSubstance, - luminousIntensity - other.luminousIntensity - ) - } - - public operator fun times(factor: Double): Dimension { - return Dimension( - length * factor, - mass * factor, - time * factor, - electricCurrent * factor, - temperature * factor, - amountOfSubstance * factor, - luminousIntensity * factor - ) - } - - /** - * Проверяет, является ли размерность безразмерной. - */ - public fun isDimensionless(epsilon: Double = 1e-10): Boolean { - return abs(length) < epsilon && - abs(mass) < epsilon && - abs(time) < epsilon && - abs(electricCurrent) < epsilon && - abs(temperature) < epsilon && - abs(amountOfSubstance) < epsilon && - abs(luminousIntensity) < epsilon - } - - /** - * Проверяет совместимость размерностей. - */ - public fun isCompatibleWith(other: Dimension, epsilon: Double = 1e-10): Boolean { - return abs(length - other.length) < epsilon && - abs(mass - other.mass) < epsilon && - abs(time - other.time) < epsilon && - abs(electricCurrent - other.electricCurrent) < epsilon && - abs(temperature - other.temperature) < epsilon && - abs(amountOfSubstance - other.amountOfSubstance) < epsilon && - abs(luminousIntensity - other.luminousIntensity) < epsilon - } -} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/units/PhysicalUnit.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/units/PhysicalUnit.kt deleted file mode 100644 index 54954f4..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/units/PhysicalUnit.kt +++ /dev/null @@ -1,51 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.units - -import kotlin.math.pow - -/** - * Класс для представления физической единицы измерения. - */ -public data class PhysicalUnit( - val name: String, - val symbol: String, - val conversionFactorToSI: Double, - val dimension: Dimension -) { - public operator fun times(other: PhysicalUnit): PhysicalUnit { - return PhysicalUnit( - name = "$name·${other.name}", - symbol = "$symbol·${other.symbol}", - conversionFactorToSI = this.conversionFactorToSI * other.conversionFactorToSI, - dimension = this.dimension + other.dimension - ) - } - - public operator fun div(other: PhysicalUnit): PhysicalUnit { - return PhysicalUnit( - name = "$name/${other.name}", - symbol = "$symbol/${other.symbol}", - conversionFactorToSI = this.conversionFactorToSI / other.conversionFactorToSI, - dimension = this.dimension - other.dimension - ) - } - - public fun pow(exponent: Double): PhysicalUnit { - return PhysicalUnit( - name = "$name^$exponent", - symbol = "${symbol}^$exponent", - conversionFactorToSI = this.conversionFactorToSI.pow(exponent), - dimension = this.dimension * exponent - ) - } - - /** - * Преобразует значение из текущей единицы в целевую единицу. - */ - public fun convertValueTo(valueInThisUnit: Double, targetUnit: PhysicalUnit): Double { - require(this.dimension.isCompatibleWith(targetUnit.dimension)) { - "Units are not compatible: $this and $targetUnit" - } - val valueInSI = valueInThisUnit * this.conversionFactorToSI - return valueInSI / targetUnit.conversionFactorToSI - } -} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/units/Units.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/units/Units.kt deleted file mode 100644 index 8eeeccd..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/units/Units.kt +++ /dev/null @@ -1,89 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.units - -import kotlin.math.* - -public object Units { - private val unitsRegistry = mutableMapOf() - - // Основные единицы СИ - public val meter: PhysicalUnit = PhysicalUnit("meter", "m", 1.0, Dimension(length = 1.0)) - public val kilogram: PhysicalUnit = PhysicalUnit("kilogram", "kg", 1.0, Dimension(mass = 1.0)) - public val second: PhysicalUnit = PhysicalUnit("second", "s", 1.0, Dimension(time = 1.0)) - public val ampere: PhysicalUnit = PhysicalUnit("ampere", "A", 1.0, Dimension(electricCurrent = 1.0)) - public val kelvin: PhysicalUnit = PhysicalUnit("kelvin", "K", 1.0, Dimension(temperature = 1.0)) - public val mole: PhysicalUnit = PhysicalUnit("mole", "mol", 1.0, Dimension(amountOfSubstance = 1.0)) - public val candela: PhysicalUnit = PhysicalUnit("candela", "cd", 1.0, Dimension(luminousIntensity = 1.0)) - public val radian: PhysicalUnit = PhysicalUnit("radian", "rad", 1.0, Dimension()) - public val hertz: PhysicalUnit = PhysicalUnit("hertz", "Hz", 1.0, Dimension(time = -1.0)) - - public val dimensionless: PhysicalUnit = PhysicalUnit("dimensionless", "", 1.0, Dimension()) - - init { - // Регистрируем основные единицы - registerUnit(meter) - registerUnit(kilogram) - registerUnit(second) - registerUnit(ampere) - registerUnit(kelvin) - registerUnit(mole) - registerUnit(candela) - registerUnit(radian) - registerUnit(hertz) - } - - /** - * Регистрирует единицу измерения в реестре. - */ - public fun registerUnit(unit: PhysicalUnit) { - unitsRegistry[unit.name] = unit - } - - /** - * Получает единицу измерения по имени. - */ - public fun getUnit(name: String): PhysicalUnit? { - return unitsRegistry[name] - } - - /** - * Список префиксов СИ. - */ - public enum class Prefix(public val prefixName: String, public val symbol: String, public val factor: Double) { - YOTTA("yotta", "Y", 1e24), - ZETTA("zetta", "Z", 1e21), - EXA("exa", "E", 1e18), - PETA("peta", "P", 1e15), - TERA("tera", "T", 1e12), - GIGA("giga", "G", 1e9), - MEGA("mega", "M", 1e6), - KILO("kilo", "k", 1e3), - HECTO("hecto", "h", 1e2), - DECA("deca", "da", 1e1), - DECI("deci", "d", 1e-1), - CENTI("centi", "c", 1e-2), - MILLI("milli", "m", 1e-3), - MICRO("micro", "μ", 1e-6), - NANO("nano", "n", 1e-9), - PICO("pico", "p", 1e-12), - FEMTO("femto", "f", 1e-15), - ATTO("atto", "a", 1e-18), - ZEPTO("zepto", "z", 1e-21), - YOCTO("yocto", "y", 1e-24) - } - - /** - * Создает новую единицу с заданным префиксом. - */ - public fun withPrefix(prefix: Prefix, unit: PhysicalUnit): PhysicalUnit { - val newName = "${prefix.prefixName}${unit.name}" - val newSymbol = "${prefix.symbol}${unit.symbol}" - val newConversionFactor = unit.conversionFactorToSI * prefix.factor - val newUnit = PhysicalUnit(newName, newSymbol, newConversionFactor, unit.dimension) - registerUnit(newUnit) - return newUnit - } - - public val kilometer: PhysicalUnit = withPrefix(Prefix.KILO, meter) - public val millimeter: PhysicalUnit = withPrefix(Prefix.MILLI, meter) - public val microsecond: PhysicalUnit = withPrefix(Prefix.MICRO, second) -} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/AbstractVariable.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/AbstractVariable.kt deleted file mode 100644 index 9282ae7..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/AbstractVariable.kt +++ /dev/null @@ -1,13 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.variables - -import space.kscience.controls.constructor.dsl.core.units.PhysicalUnit - -/** - * Абстрактный класс для переменных. - */ -public abstract class AbstractVariable( - public val name: String, - public val unit: PhysicalUnit, - public val variability: Variability, - public val causality: Causality -) \ No newline at end of file diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/ArrayVariable.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/ArrayVariable.kt deleted file mode 100644 index ee4f371..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/ArrayVariable.kt +++ /dev/null @@ -1,28 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.variables - -import space.kscience.controls.constructor.dsl.core.units.PhysicalUnit -import org.jetbrains.kotlinx.multik.ndarray.data.D2 -import org.jetbrains.kotlinx.multik.ndarray.data.NDArray -import org.jetbrains.kotlinx.multik.ndarray.data.set -import org.jetbrains.kotlinx.multik.ndarray.operations.plus - -/** - * Класс для представления переменной-массива. - * Использует Multik для работы с многомерными массивами. - */ -public class ArrayVariable( - name: String, - unit: PhysicalUnit, - public val array: NDArray, - causality: Causality = Causality.INTERNAL -) : AbstractVariable(name, unit, Variability.CONTINUOUS, causality) { - - public fun add(other: ArrayVariable): ArrayVariable { - require(this.array.shape contentEquals other.array.shape) { - "Array shapes do not match." - } - val resultArray = this.array + other.array - return ArrayVariable("$name + ${other.name}", unit, resultArray, causality) - } - -} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/Causality.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/Causality.kt deleted file mode 100644 index 50d1eb5..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/Causality.kt +++ /dev/null @@ -1,10 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.variables - -/** - * Перечисление каузальности переменной. - */ -public enum class Causality { - INPUT, - OUTPUT, - INTERNAL -} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/ConstantVariable.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/ConstantVariable.kt deleted file mode 100644 index 287274a..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/ConstantVariable.kt +++ /dev/null @@ -1,12 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.variables - -import space.kscience.controls.constructor.dsl.core.units.PhysicalUnit - -/** - * Класс для представления константы. - */ -public class ConstantVariable( - name: String, - unit: PhysicalUnit, - public val value: Double -) : AbstractVariable(name, unit, Variability.CONSTANT, Causality.INTERNAL) diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/ContinuousVariable.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/ContinuousVariable.kt deleted file mode 100644 index a4ecf87..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/ContinuousVariable.kt +++ /dev/null @@ -1,22 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.variables - -import space.kscience.controls.constructor.dsl.core.units.PhysicalUnit - -public class ContinuousVariable( - name: String, - unit: PhysicalUnit, - public var value: Double = 0.0, - public val min: Double? = null, - public val max: Double? = null, - causality: Causality = Causality.INTERNAL -) : AbstractVariable(name, unit, Variability.CONTINUOUS, causality) { - init { - // Проверка допустимого диапазона - if (min != null && value < min) { - throw IllegalArgumentException("Value of $name is less than minimum allowed value.") - } - if (max != null && value > max) { - throw IllegalArgumentException("Value of $name is greater than maximum allowed value.") - } - } -} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/DiscreteVariable.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/DiscreteVariable.kt deleted file mode 100644 index 1b741e8..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/DiscreteVariable.kt +++ /dev/null @@ -1,13 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.variables - -import space.kscience.controls.constructor.dsl.core.units.PhysicalUnit - -/** - * Класс для представления дискретной переменной. - */ -public class DiscreteVariable( - name: String, - unit: PhysicalUnit, - public var value: Double = 0.0, - causality: Causality = Causality.INTERNAL -) : AbstractVariable(name, unit, Variability.DISCRETE, causality) diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/ParameterVariable.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/ParameterVariable.kt deleted file mode 100644 index 4cc5904..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/ParameterVariable.kt +++ /dev/null @@ -1,25 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.variables - -import space.kscience.controls.constructor.dsl.core.units.PhysicalUnit - -/** - * Класс для представления параметра (неизменяемой величины). - */ -public open class ParameterVariable( - name: String, - unit: PhysicalUnit, - public val value: Double, - public val min: Double? = null, - public val max: Double? = null, - causality: Causality = Causality.INTERNAL -) : AbstractVariable(name, unit, Variability.PARAMETER, causality) { - init { - // Проверка допустимого диапазона - if (min != null && value < min) { - throw IllegalArgumentException("Value of parameter $name is less than minimum allowed value.") - } - if (max != null && value > max) { - throw IllegalArgumentException("Value of parameter $name is greater than maximum allowed value.") - } - } -} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/Variability.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/Variability.kt deleted file mode 100644 index d51a75f..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/Variability.kt +++ /dev/null @@ -1,11 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.variables - -/** - * Перечисление типов изменчивости переменной. - */ -public enum class Variability { - CONSTANT, - PARAMETER, - DISCRETE, - CONTINUOUS -} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/Variable.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/Variable.kt deleted file mode 100644 index ba4f47b..0000000 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/Variable.kt +++ /dev/null @@ -1,28 +0,0 @@ -package space.kscience.controls.constructor.dsl.core.variables - -import space.kscience.controls.constructor.dsl.core.units.PhysicalUnit - -/** - * Класс для представления переменной. - */ -public open class Variable( - public val name: String, - public val unit: PhysicalUnit, - public open var value: Double = 0.0, - public val min: Double? = null, - public val max: Double? = null, - public var variability: Variability = Variability.CONTINUOUS, - public var causality: Causality = Causality.INTERNAL -) { - init { - // Проверка, что значение находится в допустимом диапазоне - if (min != null && value < min) { - throw IllegalArgumentException("Value of $name is less than minimum allowed value.") - } - if (max != null && value > max) { - throw IllegalArgumentException("Value of $name is greater than maximum allowed value.") - } - } - - public open fun checkDimensions() {} -} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/models/Inertia.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/models/Inertia.kt index 1259151..95161ad 100644 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/models/Inertia.kt +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/models/Inertia.kt @@ -55,7 +55,7 @@ public class Inertia( public fun circular( context: Context, - force: DeviceState>, + force: DeviceState>, momentOfInertia: NumericalValue, position: MutableDeviceState>, velocity: MutableDeviceState> = MutableDeviceState(NumericalValue(0.0)), diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/models/Leadscrew.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/models/Leadscrew.kt index ebe0d30..7dd5b45 100644 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/models/Leadscrew.kt +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/models/Leadscrew.kt @@ -16,7 +16,7 @@ public class Leadscrew( ) : ModelConstructor(context) { public fun torqueToForce( - stateOfTorque: DeviceState>, + stateOfTorque: DeviceState>, ): DeviceState> = DeviceState.map(stateOfTorque) { torque -> NumericalValue(torque.value / leverage.value ) } diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/units/NumericalValue.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/units/NumericalValue.kt index 8e77001..d13ca7a 100644 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/units/NumericalValue.kt +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/units/NumericalValue.kt @@ -5,7 +5,6 @@ import space.kscience.dataforge.meta.MetaConverter import space.kscience.dataforge.meta.double import kotlin.jvm.JvmInline - /** * A value without identity coupled to units of measurements. */ diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/units/UnitsOfMeasurement.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/units/UnitsOfMeasurement.kt index 1264423..a223857 100644 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/units/UnitsOfMeasurement.kt +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/units/UnitsOfMeasurement.kt @@ -1,60 +1,244 @@ package space.kscience.controls.constructor.units +import kotlin.math.* -public interface UnitsOfMeasurement +/** + * Represents a unit of measurement. + * Provides methods to convert values to and from the base unit. + */ +public interface UnitsOfMeasurement { + /** + * Symbol representing the unit (e.g., "m" for meters). + */ + public val symbol: String -/**/ + /** + * Converts a value from this unit to the base unit. + */ + public fun toBase(value: Double): Double + /** + * Converts a value from the base unit to this unit. + */ + public fun fromBase(value: Double): Double +} + +/** + * Base unit, where conversion to base unit is identity. + */ +public open class BaseUnit( + override val symbol: String, +) : UnitsOfMeasurement { + override fun toBase(value: Double): Double = value + + override fun fromBase(value: Double): Double = value +} + +/** + * Derived unit with a conversion factor to the base unit. + */ +public class DerivedUnit( + override val symbol: String, + private val conversionFactor: Double, // Factor to convert to base unit. +) : UnitsOfMeasurement { + override fun toBase(value: Double): Double = value * conversionFactor + + override fun fromBase(value: Double): Double = value / conversionFactor +} + + +/** + * Enumeration of SI prefixes with their symbols and factors. + */ +public enum class SIPrefix( + public val symbol: String, + public val factor: Double +) { + YOTTA("Y", 1e24), + ZETTA("Z", 1e21), + EXA("E", 1e18), + PETA("P", 1e15), + TERA("T", 1e12), + GIGA("G", 1e9), + MEGA("M", 1e6), + KILO("k", 1e3), + HECTO("h", 1e2), + DECA("da", 1e1), + NONE("", 1.0), + DECI("d", 1e-1), + CENTI("c", 1e-2), + MILLI("m", 1e-3), + MICRO("μ", 1e-6), + NANO("n", 1e-9), + PICO("p", 1e-12), + FEMTO("f", 1e-15), + ATTO("a", 1e-18), + ZEPTO("z", 1e-21), + YOCTO("y", 1e-24), +} + + +/** + * Creates a new unit by applying an SI prefix to the current unit. + */ +public fun U.withPrefix(prefix: SIPrefix): UnitsOfMeasurement { + val prefixedSymbol = prefix.symbol + this.symbol + val prefixedFactor = prefix.factor + + return object : UnitsOfMeasurement { + override val symbol: String = prefixedSymbol + + override fun toBase(value: Double): Double = this@withPrefix.toBase(value * prefixedFactor) + + override fun fromBase(value: Double): Double = this@withPrefix.fromBase(value) / prefixedFactor + } +} + +/** + * Interface for units of length. + */ public interface UnitsOfLength : UnitsOfMeasurement -public data object Meters : UnitsOfLength +/** + * Base unit for length: Meter. + */ +public data object Meters : UnitsOfLength { + override val symbol: String = "m" + override fun toBase(value: Double): Double = value // Base unit + override fun fromBase(value: Double): Double = value +} -/**/ +/** + * Interface for units of time. + */ public interface UnitsOfTime : UnitsOfMeasurement -public data object Seconds : UnitsOfTime - -/**/ +/** + * Base unit for time: Second. + */ +public data object Seconds : UnitsOfTime { + override val symbol: String = "s" + override fun toBase(value: Double): Double = value // Base unit + override fun fromBase(value: Double): Double = value +} +/** + * Interface for units of velocity. + */ public interface UnitsOfVelocity : UnitsOfMeasurement -public data object MetersPerSecond : UnitsOfVelocity +/** + * Derived unit for velocity: Meters per Second. + */ +public data object MetersPerSecond : UnitsOfVelocity { + override val symbol: String = "m/s" + override fun toBase(value: Double): Double = value // Base unit for velocity + override fun fromBase(value: Double): Double = value +} /**/ +/** + * Sealed interface for units of angles. + */ public sealed interface UnitsOfAngles : UnitsOfMeasurement -public data object Radians : UnitsOfAngles -public data object Degrees : UnitsOfAngles +/** + * Base unit for angles: Radian. + */ +public data object Radians : UnitsOfAngles { + override val symbol: String = "rad" + override fun toBase(value: Double): Double = value // Base unit + override fun fromBase(value: Double): Double = value +} + +/** + * Unit for angles: Degree. + */ +public data object Degrees : UnitsOfAngles { + override val symbol: String = "deg" + override fun toBase(value: Double): Double = value * (PI / 180.0) + override fun fromBase(value: Double): Double = value * (180.0 / PI) +} /**/ +/** + * Sealed interface for units of angular velocity. + */ public sealed interface UnitsAngularOfVelocity : UnitsOfMeasurement -public data object RadiansPerSecond : UnitsAngularOfVelocity +/** + * Base unit for angular velocity: Radians per Second. + */ +public data object RadiansPerSecond : UnitsAngularOfVelocity { + override val symbol: String = "rad/s" + override fun toBase(value: Double): Double = value // Base unit + override fun fromBase(value: Double): Double = value +} -public data object DegreesPerSecond : UnitsAngularOfVelocity +/** + * Unit for angular velocity: Degrees per Second. + */ +public data object DegreesPerSecond : UnitsAngularOfVelocity { + override val symbol: String = "deg/s" + override fun toBase(value: Double): Double = value * (PI / 180.0) + override fun fromBase(value: Double): Double = value * (180.0 / PI) +} -/**/ -public interface UnitsOfForce: UnitsOfMeasurement +/** + * Interface for units of force. + */ +public interface UnitsOfForce : UnitsOfMeasurement -public data object Newtons: UnitsOfForce +/** + * Base unit for force: Newton. + */ +public data object Newtons : UnitsOfForce { + override val symbol: String = "N" + override fun toBase(value: Double): Double = value // Base unit + override fun fromBase(value: Double): Double = value +} -/**/ +/** + * Interface for units of torque. + */ +public interface UnitsOfTorque : UnitsOfMeasurement -public interface UnitsOfTorque: UnitsOfMeasurement +/** + * Base unit for torque: Newton Meter. + */ +public data object NewtonMeters : UnitsOfTorque { + override val symbol: String = "N·m" + override fun toBase(value: Double): Double = value // Base unit + override fun fromBase(value: Double): Double = value +} -public data object NewtonsMeters: UnitsOfTorque +/** + * Interface for units of mass. + */ +public interface UnitsOfMass : UnitsOfMeasurement -/**/ +/** + * Base unit for mass: Kilogram. + */ +public data object Kilograms : UnitsOfMass { + override val symbol: String = "kg" + override fun toBase(value: Double): Double = value // Base unit + override fun fromBase(value: Double): Double = value +} -public interface UnitsOfMass: UnitsOfMeasurement +/** + * Interface for units of moment of inertia. + */ +public interface UnitsOfMomentOfInertia : UnitsOfMeasurement -public data object Kilograms : UnitsOfMass - -/**/ - -public interface UnitsOfMomentOfInertia: UnitsOfMeasurement - -public data object KgM2: UnitsOfMomentOfInertia \ No newline at end of file +/** + * Base unit for moment of inertia: Kilogram Meter Squared. + */ +public data object KgM2 : UnitsOfMomentOfInertia { + override val symbol: String = "kg·m²" + override fun toBase(value: Double): Double = value // Base unit + override fun fromBase(value: Double): Double = value +} \ No newline at end of file diff --git a/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/AnalyzerTest.kt b/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/AnalyzerTest.kt new file mode 100644 index 0000000..36694bb --- /dev/null +++ b/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/AnalyzerTest.kt @@ -0,0 +1,806 @@ +package space.kscience.controls.constructor + +import kotlinx.coroutines.* +import kotlinx.coroutines.test.runTest +import space.kscience.controls.api.LifecycleState +import space.kscience.controls.spec.* +import space.kscience.dataforge.context.Context +import space.kscience.dataforge.meta.* +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +// Спецификация шагового мотора +object StepperMotorSpec : DeviceSpec() { + + val position by intProperty( + read = { propertyName -> getPosition() }, + write = { propertyName, value -> setPosition(value) } + ) + + val maxPosition by intProperty( + read = { propertyName -> maxPosition } + ) + + override fun createDevice(context: Context, meta: Meta): StepperMotorDevice { + return StepperMotorDevice(context, meta) + } +} + +// Реализация устройства шагового мотора +class StepperMotorDevice( + context: Context, + meta: Meta = Meta.EMPTY +) : DeviceBySpec(StepperMotorSpec, context, meta) { + + private var _position: Int = 0 + val maxPosition: Int = meta["maxPosition"].int ?: 100 + + // Получить текущую позицию мотора + suspend fun getPosition(): Int = _position + + // Установить позицию мотора + suspend fun setPosition(value: Int) { + if (value in 0..maxPosition) { + _position = value + println("StepperMotorDevice: Перемещен в позицию $_position") + delay(100) // Имитация времени на перемещение + } else { + println("StepperMotorDevice: Неверная позиция $value (макс: $maxPosition)") + } + } + + override fun toString(): String = "StepperMotorDevice" +} + +// Спецификация клапана +object ValveSpec : DeviceSpec() { + + val state by booleanProperty( + read = { propertyName -> getState() }, + write = { propertyName, value -> setState(value) } + ) + + override fun createDevice(context: Context, meta: Meta): ValveDevice { + return ValveDevice(context, meta) + } +} + +// Реализация устройства клапана +class ValveDevice( + context: Context, + meta: Meta = Meta.EMPTY +) : DeviceBySpec(ValveSpec, context, meta) { + + private var _state: Boolean = false + + // Получить состояние клапана + suspend fun getState(): Boolean = _state + + // Установить состояние клапана + suspend fun setState(value: Boolean) { + _state = value + val stateStr = if (_state) "открыт" else "закрыт" + println("ValveDevice: Клапан теперь $stateStr") + delay(50) // Имитация времени на изменение состояния + } + + // Метод для щелчка клапана + suspend fun click() { + println("ValveDevice: Клик клапана...") + setState(true) + delay(50) + setState(false) + println("ValveDevice: Клик клапана завершен") + } + + override fun toString(): String = "ValveDevice" +} + +// Спецификация камеры давления +object PressureChamberSpec : DeviceSpec() { + + val pressure by doubleProperty( + read = { propertyName -> getPressure() }, + write = { propertyName, value -> setPressure(value) } + ) + + override fun createDevice(context: Context, meta: Meta): PressureChamberDevice { + return PressureChamberDevice(context, meta) + } +} + +// Реализация устройства камеры давления +class PressureChamberDevice( + context: Context, + meta: Meta = Meta.EMPTY +) : DeviceBySpec(PressureChamberSpec, context, meta) { + + private var _pressure: Double = 0.0 + + // Получить текущее давление + suspend fun getPressure(): Double = _pressure + + // Установить давление + suspend fun setPressure(value: Double) { + _pressure = value + println("PressureChamberDevice: Давление установлено на $_pressure") + delay(50) + } + + override fun toString(): String = "PressureChamberDevice" +} + +// Спецификация шприцевого насоса +object SyringePumpSpec : DeviceSpec() { + + val volume by doubleProperty( + read = { propertyName -> getVolume() }, + write = { propertyName, value -> setVolume(value) } + ) + + override fun createDevice(context: Context, meta: Meta): SyringePumpDevice { + return SyringePumpDevice(context, meta) + } +} + +// Реализация устройства шприцевого насоса +class SyringePumpDevice( + context: Context, + meta: Meta = Meta.EMPTY +) : DeviceBySpec(SyringePumpSpec, context, meta) { + + private var _volume: Double = 0.0 + val maxVolume: Double = meta["maxVolume"].double ?: 5.0 + + // Получить текущий объем + suspend fun getVolume(): Double = _volume + + // Установить объем + suspend fun setVolume(value: Double) { + if (value in 0.0..maxVolume) { + _volume = value + println("SyringePumpDevice: Объем установлен на $_volume мл") + delay(100) + } else { + println("SyringePumpDevice: Неверный объем $value (макс: $maxVolume)") + } + } + + override fun toString(): String = "SyringePumpDevice" +} + +// Спецификация датчика реагента +object ReagentSensorSpec : DeviceSpec() { + + val isPresent by booleanProperty( + read = { propertyName -> checkReagent() } + ) + + override fun createDevice(context: Context, meta: Meta): ReagentSensorDevice { + return ReagentSensorDevice(context, meta) + } +} + +// Реализация устройства датчика реагента +class ReagentSensorDevice( + context: Context, + meta: Meta = Meta.EMPTY +) : DeviceBySpec(ReagentSensorSpec, context, meta) { + + // Проверить наличие реагента + suspend fun checkReagent(): Boolean { + println("ReagentSensorDevice: Проверка наличия реагента...") + delay(100) // Имитация времени на проверку + val isPresent = true // Предполагаем, что реагент присутствует + println("ReagentSensorDevice: Реагент ${if (isPresent) "обнаружен" else "не обнаружен"}") + return isPresent + } + + override fun toString(): String = "ReagentSensorDevice" +} + +// Спецификация иглы +object NeedleSpec : DeviceSpec() { + + val mode by enumProperty( + enumValues = NeedleDevice.Mode.entries.toTypedArray(), + read = { propertyName -> getMode() }, + write = { propertyName, value -> setMode(value) } + ) + + val position by doubleProperty( + read = { propertyName -> getPosition() }, + write = { propertyName, value -> setPosition(value) } + ) + + override fun createDevice(context: Context, meta: Meta): NeedleDevice { + return NeedleDevice(context, meta) + } +} + +// Реализация устройства иглы +class NeedleDevice( + context: Context, + meta: Meta = Meta.EMPTY +) : DeviceBySpec(NeedleSpec, context, meta) { + + // Режимы работы иглы + enum class Mode { SAMPLING, WASHING } + + private var mode: Mode = Mode.WASHING + private var position: Double = 0.0 // в мм + + // Получить текущий режим + suspend fun getMode(): Mode = mode + + // Установить режим + suspend fun setMode(value: Mode) { + mode = value + println("NeedleDevice: Режим установлен на $mode") + delay(50) + } + + // Получить текущую позицию + suspend fun getPosition(): Double = position + + // Установить позицию + suspend fun setPosition(value: Double) { + if (value in 0.0..100.0) { + position = value + println("NeedleDevice: Перемещена в позицию $position мм") + delay(100) + } else { + println("NeedleDevice: Неверная позиция $value мм") + } + } + + // Выполнить промывку + suspend fun performWashing(duration: Int) { + println("NeedleDevice: Промывка в течение $duration секунд") + delay(duration * 1000L) // Имитация промывки (1 секунда = 1000 мс) + } + + // Выполнить забор пробы + suspend fun performSampling() { + println("NeedleDevice: Забор пробы в позиции $position мм") + delay(500) // Имитация забора пробы + } + + override fun toString(): String = "NeedleDevice" +} + +// Спецификация шейкера +object ShakerSpec : DeviceSpec() { + + val verticalMotor by device(StepperMotorSpec, name = "verticalMotor") + val horizontalMotor by device(StepperMotorSpec, name = "horizontalMotor") + + override fun createDevice(context: Context, meta: Meta): ShakerDevice { + return ShakerDevice(context, meta) + } +} + +// Реализация устройства шейкера +class ShakerDevice( + context: Context, + meta: Meta = Meta.EMPTY +) : DeviceBySpec(ShakerSpec, context, meta) { + + val verticalMotor: StepperMotorDevice + get() = nestedDevices["verticalMotor"] as StepperMotorDevice + + val horizontalMotor: StepperMotorDevice + get() = nestedDevices["horizontalMotor"] as StepperMotorDevice + + // Метод встряхивания + suspend fun shake(cycles: Int) { + println("ShakerDevice: Начало встряхивания, циклов: $cycles") + repeat(cycles) { + verticalMotor.setPosition(3) + verticalMotor.setPosition(1) + } + println("ShakerDevice: Встряхивание завершено") + } + + override fun toString(): String = "ShakerDevice" +} + +// Спецификация системы транспортировки +object TransportationSystemSpec : DeviceSpec() { + + val slideMotor by device(StepperMotorSpec, name = "slideMotor") + val pushMotor by device(StepperMotorSpec, name = "pushMotor") + val receiveMotor by device(StepperMotorSpec, name = "receiveMotor") + + override fun createDevice(context: Context, meta: Meta): TransportationSystem { + return TransportationSystem(context, meta) + } +} + +// Реализация системы транспортировки +class TransportationSystem( + context: Context, + meta: Meta = Meta.EMPTY +) : DeviceBySpec(TransportationSystemSpec, context, meta) { + + val slideMotor: StepperMotorDevice + get() = nestedDevices["slideMotor"] as StepperMotorDevice + + val pushMotor: StepperMotorDevice + get() = nestedDevices["pushMotor"] as StepperMotorDevice + + val receiveMotor: StepperMotorDevice + get() = nestedDevices["receiveMotor"] as StepperMotorDevice + + override fun toString(): String = "TransportationSystem" +} + +// Спецификация анализатора +object AnalyzerSpec : DeviceSpec() { + + val transportationSystem by device(TransportationSystemSpec, name = "transportationSystem") + val shakerDevice by device(ShakerSpec, name = "shakerDevice") + val needleDevice by device(NeedleSpec, name = "needleDevice") + + val valveV20 by device(ValveSpec, name = "valveV20") + val valveV17 by device(ValveSpec, name = "valveV17") + val valveV18 by device(ValveSpec, name = "valveV18") + val valveV35 by device(ValveSpec, name = "valveV35") + + val pressureChamberHigh by device(PressureChamberSpec, name = "pressureChamberHigh") + val pressureChamberLow by device(PressureChamberSpec, name = "pressureChamberLow") + + val syringePumpMA100 by device(SyringePumpSpec, name = "syringePumpMA100") + val syringePumpMA25 by device(SyringePumpSpec, name = "syringePumpMA25") + + val reagentSensor1 by device(ReagentSensorSpec, name = "reagentSensor1") + val reagentSensor2 by device(ReagentSensorSpec, name = "reagentSensor2") + val reagentSensor3 by device(ReagentSensorSpec, name = "reagentSensor3") + + override fun createDevice(context: Context, meta: Meta): AnalyzerDevice { + return AnalyzerDevice(context, meta) + } +} + +// Реализация анализатора +class AnalyzerDevice( + context: Context, + meta: Meta = Meta.EMPTY +) : DeviceBySpec(AnalyzerSpec, context, meta) { + + val transportationSystem: TransportationSystem + get() = nestedDevices["transportationSystem"] as TransportationSystem + + val shakerDevice: ShakerDevice + get() = nestedDevices["shakerDevice"] as ShakerDevice + + val needleDevice: NeedleDevice + get() = nestedDevices["needleDevice"] as NeedleDevice + + val valveV20: ValveDevice + get() = nestedDevices["valveV20"] as ValveDevice + + val valveV17: ValveDevice + get() = nestedDevices["valveV17"] as ValveDevice + + val valveV18: ValveDevice + get() = nestedDevices["valveV18"] as ValveDevice + + val valveV35: ValveDevice + get() = nestedDevices["valveV35"] as ValveDevice + + val pressureChamberHigh: PressureChamberDevice + get() = nestedDevices["pressureChamberHigh"] as PressureChamberDevice + + val pressureChamberLow: PressureChamberDevice + get() = nestedDevices["pressureChamberLow"] as PressureChamberDevice + + val syringePumpMA100: SyringePumpDevice + get() = nestedDevices["syringePumpMA100"] as SyringePumpDevice + + val syringePumpMA25: SyringePumpDevice + get() = nestedDevices["syringePumpMA25"] as SyringePumpDevice + + val reagentSensor1: ReagentSensorDevice + get() = nestedDevices["reagentSensor1"] as ReagentSensorDevice + + val reagentSensor2: ReagentSensorDevice + get() = nestedDevices["reagentSensor2"] as ReagentSensorDevice + + val reagentSensor3: ReagentSensorDevice + get() = nestedDevices["reagentSensor3"] as ReagentSensorDevice + + // Процесс обработки проб + suspend fun processSample() { + println("Начало процесса забора пробы") + + // Шаг 1: Открыть клапан V20 и начать забор пробы с помощью шприца MA 100 мкл + valveV20.setState(true) + syringePumpMA100.setVolume(0.1) + delay(500) // Имитация времени для забора жидкости + valveV20.setState(false) + + // Шаг 2: Открыть клапан V17 и начать подачу лизиса в WBC с помощью MA 2.5 мл + valveV17.setState(true) + syringePumpMA25.setVolume(2.5) + delay(500) // Имитация времени для подачи лизиса + valveV17.setState(false) + + // Шаг 3: Очистка системы + syringePumpMA100.setVolume(0.0) + syringePumpMA25.setVolume(0.0) + + println("Процесс забора пробы завершен") + } + + // Реализация рецепта калибровки + suspend fun calibrate() { + println("Начало калибровки...") + + // Шаг 1: Откалибровать положения всех двигателей + val motors = listOf( + transportationSystem.slideMotor, + transportationSystem.pushMotor, + transportationSystem.receiveMotor, + shakerDevice.verticalMotor, + shakerDevice.horizontalMotor + ) + + for (motor in motors) { + for (position in 0..motor.maxPosition) { + motor.setPosition(position) + } + motor.setPosition(0) + } + + // Шаг 2: Щелкнуть всеми клапанами и перевести их в нулевое положение + val valves = listOf(valveV20, valveV17, valveV18, valveV35) + for (valve in valves) { + valve.click() + valve.setState(false) + } + + // Шаг 3: Провести накачку давления в камеру повышенного давления + pressureChamberHigh.setPressure(2.0) + + // Шаг 4: Провести откачку камеры пониженного давления + pressureChamberLow.setPressure(-1.0) + + // Шаг 5: Заполнить гидравлическую систему + + // 5.1 Проверить наличие всех реагентов + val sensors = listOf(reagentSensor1, reagentSensor2, reagentSensor3) + for (sensor in sensors) { + sensor.checkReagent() + } + + // 5.2 Прокачать все шприцевые дозаторы (5 раз движение между крайними положениями) + val pumps = listOf(syringePumpMA100, syringePumpMA25) + for (pump in pumps) { + repeat(5) { + pump.setVolume(pump.maxVolume) + pump.setVolume(0.0) + } + } + + // 5.3 Провести промывку иглы в положении промывки для забора пробы + needleDevice.setPosition(0.0) + needleDevice.setMode(NeedleDevice.Mode.WASHING) + needleDevice.performWashing(5) + + println("Калибровка завершена") + } + + // Рецепт 1 - Подача пробирки на измерение + suspend fun executeRecipe1() { + println("Выполнение Рецепта 1") + + // Шаг 1: Сдвинуть планшет на одну позицию + val currentSlidePosition = transportationSystem.slideMotor.getPosition() + transportationSystem.slideMotor.setPosition(currentSlidePosition + 1) + println("Сдвинуто планшет на позицию ${currentSlidePosition + 1}") + + // Шаг 2: Захват пробирки для смешивания + println("Захват пробирки для смешивания") + + // 2.1 - 2.10: Управление шейкером и двигателями + shakerDevice.verticalMotor.setPosition(1) + shakerDevice.horizontalMotor.setPosition(1) + println("Шейкер: вертикальный - 1, горизонтальный - 1") + + shakerDevice.horizontalMotor.setPosition(2) + println("Шейкер: горизонтальный - 2") + + shakerDevice.verticalMotor.setPosition(2) + println("Шейкер: вертикальный - 2") + + // Встряхивание 5 циклов + repeat(5) { + shakerDevice.verticalMotor.setPosition(3) + shakerDevice.verticalMotor.setPosition(1) + println("Шейкер: цикл ${it + 1}") + } + + shakerDevice.verticalMotor.setPosition(2) + shakerDevice.horizontalMotor.setPosition(1) + println("Шейкер: окончание движения") + + // Шаг 3: Забор и измерение пробы + executeSampling() + needleDevice.setPosition(0.0) + println("Игла вернулась в исходное положение") + } + + // Функция для выполнения забора пробы + suspend fun executeSampling() { + println("Забор и измерение пробы") + + needleDevice.setPosition(0.0) + needleDevice.setMode(NeedleDevice.Mode.WASHING) + needleDevice.performWashing(5) + + needleDevice.setPosition(10.0) + needleDevice.setMode(NeedleDevice.Mode.SAMPLING) + needleDevice.performSampling() + + needleDevice.setPosition(0.0) + needleDevice.setMode(NeedleDevice.Mode.WASHING) + needleDevice.performWashing(5) + + needleDevice.setPosition(20.0) + println("Игла в положении WOC") + } + + // Рецепт 2 - Автоматическое измерение + suspend fun executeRecipe2() { + println("Выполнение Рецепта 2 - Автоматическое измерение") + + transportationSystem.receiveMotor.setPosition(transportationSystem.receiveMotor.getPosition() + 1) + println("Сдвинуто податчик на 1 позицию") + + // Проверка лотка + if (!checkTrayInPushSystem()) { + println("Лоток отсутствует. Повторный сдвиг") + transportationSystem.receiveMotor.setPosition(transportationSystem.receiveMotor.getPosition() + 1) + } else { + executeSampling() + } + + // Если достигнута последняя позиция, меняем планшет + if (transportationSystem.receiveMotor.getPosition() >= transportationSystem.receiveMotor.maxPosition) { + println("Смена планшета. Возврат податчика в исходное положение") + transportationSystem.receiveMotor.setPosition(0) + } + + println("Рецепт 2 завершен") + needleDevice.setPosition(0.0) + println("Игла вернулась в исходное положение") + } + + // Рецепт 3 - Одиночное измерение + suspend fun executeRecipe3() { + println("Выполнение Рецепта 3 - Одиночное измерение") + executeSampling() + println("Рецепт 3 завершен") + needleDevice.setPosition(0.0) + println("Игла вернулась в исходное положение") + } + + // функция проверки наличия лотка + private suspend fun checkTrayInPushSystem(): Boolean { + println("Проверка наличия лотка в системе проталкивания") + delay(200) + return true // Имитация наличия лотка + } +} + +// ----------------------- +// Тестирование анализатора +// ----------------------- +class AnalyzerTest { + + @Test + fun testAnalyzerInitialization() = runTest { + val context = Context("TestContext") + val analyzer = AnalyzerDevice(context) + + analyzer.start() + + // Проверка состояния устройств + assertEquals(LifecycleState.STARTED, analyzer.lifecycleState) + assertEquals(LifecycleState.STARTED, analyzer.transportationSystem.lifecycleState) + assertEquals(LifecycleState.STARTED, analyzer.shakerDevice.lifecycleState) + assertEquals(LifecycleState.STARTED, analyzer.needleDevice.lifecycleState) + + // Проверка начальных положений двигателей + assertEquals(0, analyzer.transportationSystem.slideMotor.getPosition()) + assertEquals(0, analyzer.transportationSystem.pushMotor.getPosition()) + assertEquals(0, analyzer.transportationSystem.receiveMotor.getPosition()) + assertEquals(0, analyzer.shakerDevice.verticalMotor.getPosition()) + assertEquals(0, analyzer.shakerDevice.horizontalMotor.getPosition()) + assertEquals(0.0, analyzer.needleDevice.getPosition()) + + // Проверка состояния клапанов + assertFalse(analyzer.valveV20.getState()) + assertFalse(analyzer.valveV17.getState()) + assertFalse(analyzer.valveV18.getState()) + assertFalse(analyzer.valveV35.getState()) + + analyzer.stop() + assertEquals(LifecycleState.STOPPED, analyzer.lifecycleState) + } + + @Test + fun testCalibration() = runTest { + val context = Context("TestContext") + val analyzer = AnalyzerDevice(context) + + analyzer.start() + + // Запуск калибровки + analyzer.calibrate() + + // Проверка состояния двигателей после калибровки + assertEquals(0, analyzer.transportationSystem.slideMotor.getPosition()) + assertEquals(0, analyzer.transportationSystem.pushMotor.getPosition()) + assertEquals(0, analyzer.transportationSystem.receiveMotor.getPosition()) + assertEquals(0, analyzer.shakerDevice.verticalMotor.getPosition()) + assertEquals(0, analyzer.shakerDevice.horizontalMotor.getPosition()) + + // Проверка давления после калибровки + assertEquals(2.0, analyzer.pressureChamberHigh.getPressure()) + assertEquals(-1.0, analyzer.pressureChamberLow.getPressure()) + + // Проверка состояния реагентных сенсоров + assertTrue(analyzer.reagentSensor1.checkReagent()) + assertTrue(analyzer.reagentSensor2.checkReagent()) + assertTrue(analyzer.reagentSensor3.checkReagent()) + + analyzer.stop() + } + + @Test + fun testRecipe1() = runTest { + val context = Context("TestContext") + val analyzer = AnalyzerDevice(context) + + analyzer.start() + + // Выполнение рецепта 1 (подачи пробирки) + analyzer.executeRecipe1() + + // Проверка конечных состояний после рецепта + assertEquals(1, analyzer.transportationSystem.slideMotor.getPosition()) + assertEquals(1, analyzer.shakerDevice.verticalMotor.getPosition()) + assertEquals(1, analyzer.shakerDevice.horizontalMotor.getPosition()) + + // Проверка положения иглы после забора пробы + assertEquals(0.0, analyzer.needleDevice.getPosition()) + + analyzer.stop() + } + + @Test + fun testRecipe2() = runTest { + val context = Context("TestContext") + val analyzer = AnalyzerDevice(context) + + analyzer.start() + + // Выполнение рецепта 2 (автоматическое измерение) + analyzer.executeRecipe2() + + // Проверка конечного положения двигателей + assertTrue(analyzer.transportationSystem.receiveMotor.getPosition() > 0) + + // Проверка иглы после выполнения рецепта + assertEquals(0.0, analyzer.needleDevice.getPosition()) + + analyzer.stop() + } + + @Test + fun testRecipe3() = runTest { + val context = Context("TestContext") + val analyzer = AnalyzerDevice(context) + + analyzer.start() + + // Выполнение рецепта 3 (одиночное измерение) + analyzer.executeRecipe3() + + // Проверка иглы после одиночного измерения + assertEquals(0.0, analyzer.needleDevice.getPosition()) + + // Проверка завершения одиночного измерения + println("Одиночное измерение завершено") + + analyzer.stop() + } + + @Test + fun testDetailedMotorMovements() = runTest { + val context = Context("TestContext") + val analyzer = AnalyzerDevice(context) + + analyzer.start() + + // Проверка движения двигателей при выполнении задач + analyzer.transportationSystem.slideMotor.setPosition(2) + assertEquals(2, analyzer.transportationSystem.slideMotor.getPosition()) + + analyzer.shakerDevice.verticalMotor.setPosition(1) + analyzer.shakerDevice.horizontalMotor.setPosition(3) + assertEquals(1, analyzer.shakerDevice.verticalMotor.getPosition()) + assertEquals(3, analyzer.shakerDevice.horizontalMotor.getPosition()) + + analyzer.stop() + } + + @Test + fun testValveStates() = runTest { + val context = Context("TestContext") + val analyzer = AnalyzerDevice(context) + + analyzer.start() + + // Проверка работы клапанов + analyzer.valveV20.setState(true) + assertTrue(analyzer.valveV20.getState()) + + analyzer.valveV17.setState(false) + assertFalse(analyzer.valveV17.getState()) + + analyzer.valveV18.click() // Щелчок клапаном + assertFalse(analyzer.valveV18.getState()) + + analyzer.stop() + } + + @Test + fun testSyringePumpOperations() = runTest { + val context = Context("TestContext") + val analyzer = AnalyzerDevice(context) + + analyzer.start() + + // Проверка работы шприцевого насоса + analyzer.syringePumpMA100.setVolume(0.05) + assertEquals(0.05, analyzer.syringePumpMA100.getVolume()) + + analyzer.syringePumpMA100.setVolume(0.0) + assertEquals(0.0, analyzer.syringePumpMA100.getVolume()) + + analyzer.syringePumpMA25.setVolume(2.5) + assertEquals(2.5, analyzer.syringePumpMA25.getVolume()) + + analyzer.stop() + } + + @Test + fun testNeedleOperations() = runTest { + val context = Context("TestContext") + val analyzer = AnalyzerDevice(context) + + analyzer.start() + + // Проверка работы иглы в режиме промывки и забора проб + analyzer.needleDevice.setMode(NeedleDevice.Mode.WASHING) + analyzer.needleDevice.setPosition(0.0) + assertEquals(0.0, analyzer.needleDevice.getPosition()) + assertEquals(NeedleDevice.Mode.WASHING, analyzer.needleDevice.getMode()) + + analyzer.needleDevice.performWashing(5) // Промывка иглы + + analyzer.needleDevice.setMode(NeedleDevice.Mode.SAMPLING) + analyzer.needleDevice.setPosition(10.0) + assertEquals(10.0, analyzer.needleDevice.getPosition()) + assertEquals(NeedleDevice.Mode.SAMPLING, analyzer.needleDevice.getMode()) + + analyzer.needleDevice.performSampling() // Забор пробы + + analyzer.stop() + } +} diff --git a/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/CompositeDeviceTest.kt b/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/CompositeDeviceTest.kt new file mode 100644 index 0000000..ebe0da7 --- /dev/null +++ b/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/CompositeDeviceTest.kt @@ -0,0 +1,182 @@ +package space.kscience.controls.constructor + +import kotlinx.coroutines.test.runTest +import kotlin.test.Test +import kotlin.test.assertEquals +import space.kscience.controls.api.* +import space.kscience.controls.spec.* +import space.kscience.dataforge.context.Context +import space.kscience.dataforge.meta.* + +class CompositeDeviceTest { + + // simple MotorDevice and MotorSpec + class MotorSpec : DeviceSpec() { + val speed by doubleProperty( + name = "speed", + read = { propertyName -> + getSpeed() + }, + write = { propertyName, value -> + setSpeed(value) + } + ) + + val position by doubleProperty( + name = "position", + read = { propertyName -> + getPosition() + } + // Assuming position is read-only + ) + + val reset by unitAction( + name = "reset", + execute = { + resetMotor() + } + ) + + override fun createDevice(context: Context, meta: Meta): MotorDevice { + return MotorDevice(context, meta) + } + } + + class MotorDevice( + context: Context, + meta: Meta = Meta.EMPTY + ) : DeviceBySpec(MotorSpec(), context, meta) { + private var _speed: Double = 0.0 + private var _position: Double = 0.0 + + suspend fun getSpeed(): Double = _speed + suspend fun setSpeed(value: Double) { + _speed = value + } + + suspend fun getPosition(): Double = _position + suspend fun resetMotor() { + _speed = 0.0 + _position = 0.0 + } + + override fun toString(): String = "MotorDevice" + + } + + data class Position(val base: Double, val elbow: Double, val wrist: Double) + + object PositionConverter : MetaConverter { + override fun readOrNull(source: Meta): Position? { + val base = source["base"]?.value?.number?.toDouble() ?: return null + val elbow = source["elbow"]?.value?.number?.toDouble() ?: return null + val wrist = source["wrist"]?.value?.number?.toDouble() ?: return null + return Position(base, elbow, wrist) + } + + override fun convert(obj: Position): Meta = Meta { + "base" put obj.base + "elbow" put obj.elbow + "wrist" put obj.wrist + } + } + + object RobotArmSpec : DeviceSpec() { + val baseMotorSpec = MotorSpec() + val elbowMotorSpec = MotorSpec() + val wristMotorSpec = MotorSpec() + + val baseMotor by device(baseMotorSpec) + val elbowMotor by device(elbowMotorSpec) + val wristMotor by device(wristMotorSpec) + + val moveToPosition by action( + inputConverter = PositionConverter, + outputConverter = MetaConverter.unit, + name = "moveToPosition", + execute = { input -> + baseMotor.writeProperty("speed", Meta(input.base)) + elbowMotor.writeProperty("speed", Meta(input.elbow)) + wristMotor.writeProperty("speed", Meta(input.wrist)) + } + ) + + override fun createDevice(context: Context, meta: Meta): RobotArmDevice { + return RobotArmDevice(context, meta) + } + } + + class RobotArmDevice( + context: Context, + meta: Meta = Meta.EMPTY + ) : DeviceBySpec(RobotArmSpec, context, meta) { + + val baseMotor: MotorDevice get() = nestedDevices["baseMotor"] as MotorDevice + val elbowMotor: MotorDevice get() = nestedDevices["elbowMotor"] as MotorDevice + val wristMotor: MotorDevice get() = nestedDevices["wristMotor"] as MotorDevice + + override suspend fun onStart() { + super.onStart() + } + + override suspend fun onStop() { + super.onStop() + } + + override fun toString(): String = "RobotArmDevice" + } + + @Test + fun testNestedDeviceInitialization() = runTest { + val context = Context("TestContext") + val robotArm = RobotArmDevice(context) + + // Start the robot arm device + robotArm.start() + + // Check that all motors are started + assertEquals(LifecycleState.STARTED, robotArm.baseMotor.lifecycleState) + assertEquals(LifecycleState.STARTED, robotArm.elbowMotor.lifecycleState) + assertEquals(LifecycleState.STARTED, robotArm.wristMotor.lifecycleState) + + // Stop the robot arm device + robotArm.stop() + } + + @Test + fun testCompositeDevicePropertyAndActionAccess() = runTest { + val context = Context("TestContext") + val robotArm = RobotArmDevice(context) + + robotArm.start() + + robotArm.baseMotor.writeProperty("speed", Meta(500.0)) + + val speedMeta = robotArm.baseMotor.getProperty("speed") + val speed = speedMeta?.value?.number?.toDouble() + assertEquals(500.0, speed) + + // Stop the robot arm device + robotArm.stop() + } + + @Test + fun testCompositeDeviceLifecycleManagement() = runTest { + val context = Context("TestContext") + val robotArm = RobotArmDevice(context) + + // Start the robot arm device + robotArm.start() + assertEquals(LifecycleState.STARTED, robotArm.lifecycleState) + assertEquals(LifecycleState.STARTED, robotArm.baseMotor.lifecycleState) + assertEquals(LifecycleState.STARTED, robotArm.elbowMotor.lifecycleState) + assertEquals(LifecycleState.STARTED, robotArm.wristMotor.lifecycleState) + + // Stop the robot arm device + robotArm.stop() + assertEquals(LifecycleState.STOPPED, robotArm.lifecycleState) + assertEquals(LifecycleState.STOPPED, robotArm.baseMotor.lifecycleState) + assertEquals(LifecycleState.STOPPED, robotArm.elbowMotor.lifecycleState) + assertEquals(LifecycleState.STOPPED, robotArm.wristMotor.lifecycleState) + } +} \ No newline at end of file diff --git a/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/DeviceSpecTest.kt b/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/DeviceSpecTest.kt new file mode 100644 index 0000000..ed8afb1 --- /dev/null +++ b/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/DeviceSpecTest.kt @@ -0,0 +1,168 @@ +package space.kscience.controls.constructor + +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import kotlinx.coroutines.test.runTest +import space.kscience.controls.api.LifecycleState +import space.kscience.controls.spec.DeviceActionSpec +import space.kscience.controls.spec.DeviceBase +import space.kscience.controls.spec.DeviceBySpec +import space.kscience.controls.spec.DevicePropertySpec +import space.kscience.controls.spec.DeviceSpec +import space.kscience.controls.spec.doubleProperty +import space.kscience.controls.spec.unitAction +import space.kscience.controls.spec.validate + +import space.kscience.dataforge.context.Context +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.number + +class DeviceSpecTest { + + class MotorSpec : DeviceSpec() { + val speed by doubleProperty( + name = "speed", + read = { propertyName -> + getSpeed() + }, + write = { propertyName, value -> + setSpeed(value) + } + ) + + val position by doubleProperty( + name = "position", + read = { propertyName -> + getPosition() + } + // Assuming position is read-only + ) + + val reset by unitAction( + name = "reset", + execute = { + resetMotor() + } + ) + } + + class MotorDevice( + context: Context, + meta: Meta = Meta.EMPTY + ) : DeviceBySpec(MotorSpec(), context, meta) { + private var _speed: Double = 0.0 + private var _position: Double = 0.0 + + suspend fun getSpeed(): Double = _speed + suspend fun setSpeed(value: Double) { + _speed = value + } + + suspend fun getPosition(): Double = _position + suspend fun resetMotor() { + _speed = 0.0 + _position = 0.0 + } + + override fun toString(): String = "MotorDevice" + } + + @Test + fun testDevicePropertyDefinitionAndAccess() = runTest { + val context = Context("TestContext") + val motor = MotorDevice(context) + + motor.start() + + val speedMeta = Meta(1000.0) + motor.writeProperty("speed", speedMeta) + + val speedMetaRead = motor.readProperty("speed") + val speed = speedMetaRead.value?.number?.toDouble() + assertEquals(1000.0, speed) + + val positionMeta = motor.readProperty("position") + val position = positionMeta.value?.number?.toDouble() + assertEquals(0.0, position) + + motor.stop() + } + + @Test + fun testDeviceActionDefinitionAndExecution() = runTest { + val context = Context("TestContext") + val motor = MotorDevice(context) + + motor.start() + + val speedMeta = Meta(1000.0) + motor.writeProperty("speed", speedMeta) + + motor.execute("reset", Meta.EMPTY) + + val speedMetaRead = motor.readProperty("speed") + val speed = speedMetaRead.value?.number?.toDouble() + assertEquals(0.0, speed) + + val positionMeta = motor.readProperty("position") + val position = positionMeta.value?.number?.toDouble() + assertEquals(0.0, position) + + motor.stop() + } + + @Test + fun testDeviceLifecycleManagement() = runTest { + val context = Context("TestContext") + val motor = MotorDevice(context) + + assertEquals(LifecycleState.STOPPED, motor.lifecycleState) + + motor.start() + assertEquals(LifecycleState.STARTED, motor.lifecycleState) + + motor.stop() + assertEquals(LifecycleState.STOPPED, motor.lifecycleState) + } + + @Test + fun testDeviceErrorHandling() = runTest { + val context = Context("TestContext") + val motor = MotorDevice(context) + + motor.start() + + assertFailsWith { + motor.readProperty("nonExistentProperty") + } + + assertFailsWith { + motor.execute("nonExistentAction", Meta.EMPTY) + } + + motor.stop() + } + + @Test + fun testDeviceSpecValidation() = runTest { + val context = Context("TestContext") + val motor = MotorDevice(context) + + motor.spec.validate(motor) + + val invalidMotor = object : DeviceBase(context, Meta.EMPTY) { + override val properties: Map> + get() = emptyMap() + override val actions: Map> + get() = emptyMap() + + override fun toString(): String = "InvalidMotorDevice" + } + + // Expect validation to fail + assertFailsWith { + motor.spec.validate(invalidMotor) + } + } +} \ No newline at end of file diff --git a/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/EquationsTest.kt b/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/EquationsTest.kt deleted file mode 100644 index 8f3d4b1..0000000 --- a/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/EquationsTest.kt +++ /dev/null @@ -1,89 +0,0 @@ -package space.kscience.controls.constructor - -import space.kscience.controls.constructor.dsl.core.EquationSystem -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith -import kotlin.test.assertTrue -import space.kscience.controls.constructor.dsl.core.equations.* -import space.kscience.controls.constructor.dsl.core.expressions.* -import space.kscience.controls.constructor.dsl.core.units.Units -import space.kscience.controls.constructor.dsl.core.variables.Variable - -class EquationsTest { - - @Test - fun testCreatingEquations() { - val xVar = Variable("x", Units.meter) - val xExpr = VariableExpression(xVar) - val yVar = Variable("y", Units.meter) - val yExpr = VariableExpression(yVar) - val constExpr = ConstantExpression(5.0, Units.meter) - - // Regular equation: x = y + 5 - val equation = Equation(xExpr, BinaryExpression(yExpr, BinaryOperator.ADD, constExpr)) - assertEquals(xExpr, equation.leftExpression) - assertEquals(BinaryExpression(yExpr, BinaryOperator.ADD, constExpr), equation.rightExpression) - - // Conditional equation: if x > 0 then y = x else y = -x - val condition = BinaryExpression(xExpr, BinaryOperator.GREATER_THAN, ConstantExpression(0.0, Units.meter)) - val trueEquation = Equation(yExpr, xExpr) - val falseEquation = Equation(yExpr, BinaryExpression(ConstantExpression(0.0, Units.meter), BinaryOperator.SUBTRACT, xExpr)) - val conditionalEquation = ConditionalEquation(condition, trueEquation, falseEquation) - assertEquals(condition, conditionalEquation.condition) - assertEquals(trueEquation, conditionalEquation.trueEquation) - assertEquals(falseEquation, conditionalEquation.falseEquation) - - // Initial equation: x(0) = 0 - val initialEquation = InitialEquation(xExpr, ConstantExpression(0.0, Units.meter)) - assertEquals(xExpr, initialEquation.leftExpression) - assertEquals(ConstantExpression(0.0, Units.meter), initialEquation.rightExpression) - } - - @Test - fun testUnitCompatibilityInEquations() { - val lengthVar = Variable("length", Units.meter) - val lengthExpr = VariableExpression(lengthVar) - val timeVar = Variable("time", Units.second) - val timeExpr = VariableExpression(timeVar) - - // Correct equation: length = length + length - val validEquation = Equation(lengthExpr, BinaryExpression(lengthExpr, BinaryOperator.ADD, ConstantExpression(5.0, Units.meter))) - assertFailsWith { - validEquation - } - - // Incorrect equation: length = time - val exception = assertFailsWith { - Equation(lengthExpr, timeExpr) - } - assertEquals("Units of left and right expressions in the equation are not compatible.", exception.message) - } - - @Test - fun testEquationSystem() { - val xVar = Variable("x", Units.meter) - val xExpr = VariableExpression(xVar) - val vVar = Variable("v", Units.meter / Units.second) - val vExpr = VariableExpression(vVar) - val aVar = Variable("a", Units.meter / Units.second.pow(2.0)) - val aExpr = VariableExpression(aVar) - - // Differential equation: der(x) = v - val derX = DerivativeExpression(xExpr) - val eq1 = Equation(derX, vExpr) - - // Algebraic equation: v = a * t - val tVar = Variable("t", Units.second) - val tExpr = VariableExpression(tVar) - val eq2 = Equation(vExpr, BinaryExpression(aExpr, BinaryOperator.MULTIPLY, tExpr)) - - val equationSystem = EquationSystem() - equationSystem.addEquation(eq1) - equationSystem.addEquation(eq2) - - assertEquals(2, equationSystem.equations.size) - assertTrue(equationSystem.variables.containsAll(listOf("x", "v", "a", "t"))) - assertTrue(equationSystem.derivativeVariables.contains("x")) - } -} diff --git a/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/EventsAndActionsTest.kt b/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/EventsAndActionsTest.kt deleted file mode 100644 index 2eb91d0..0000000 --- a/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/EventsAndActionsTest.kt +++ /dev/null @@ -1,73 +0,0 @@ -package space.kscience.controls.constructor - -import kotlin.test.Test -import kotlin.test.assertEquals -import space.kscience.controls.constructor.dsl.core.events.Event -import space.kscience.controls.constructor.dsl.core.events.ReinitAction -import space.kscience.controls.constructor.dsl.core.events.AssignAction -import space.kscience.controls.constructor.dsl.core.expressions.BinaryExpression -import space.kscience.controls.constructor.dsl.core.expressions.BinaryOperator -import space.kscience.controls.constructor.dsl.core.expressions.ConstantExpression -import space.kscience.controls.constructor.dsl.core.expressions.VariableExpression -import space.kscience.controls.constructor.dsl.core.simulation.SimulationContext -import space.kscience.controls.constructor.dsl.core.units.Units -import space.kscience.controls.constructor.dsl.core.variables.Variable -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.test.runTest - -class EventsAndActionsTest { - - @Test - fun testCreatingEvents() { - val xVar = Variable("x", Units.meter) - val xExpr = VariableExpression(xVar) - - val condition = BinaryExpression(xExpr, BinaryOperator.GREATER_THAN, ConstantExpression(10.0, Units.meter)) - val action = AssignAction(xVar, ConstantExpression(0.0, Units.meter)) - - val event = Event(condition, listOf(action)) - - assertEquals(condition, event.condition) - assertEquals(1, event.actions.size) - assertEquals(action, event.actions[0]) - } - - @Test - fun testProcessingEventsInSimulation() = runTest { - val xVar = Variable("x", Units.meter) - val xExpr = VariableExpression(xVar) - val context = SimulationContext(CoroutineScope(Dispatchers.Default)).apply { - registerVariable(xVar) // Регистрация переменной - launch { updateVariable("x", 5.0) } // Установка начального значения переменной - } - - val condition = BinaryExpression(xExpr, BinaryOperator.GREATER_THAN, ConstantExpression(10.0, Units.meter)) - val action = AssignAction(xVar, ConstantExpression(0.0, Units.meter)) - val event = Event(condition, listOf(action)) - - context.events.add(event) - - // Simulate x crossing the threshold - context.updateVariable("x", 15.0) - context.handleEvents() - - assertEquals(0.0, context.getCurrentVariableValue("x")) - } - - @Test - fun testReinitAction() = runTest { - val vVar = Variable("v", Units.meter / Units.second) - val vExpr = VariableExpression(vVar) - val context = SimulationContext(CoroutineScope(Dispatchers.Default)).apply { - registerVariable(vVar) // Регистрация переменной - launch { updateVariable("v", 20.0) } // Установка начального значения переменной - } - - val action = ReinitAction(vVar, ConstantExpression(0.0, Units.meter / Units.second)) - action.execute(context) - - assertEquals(0.0, context.getCurrentVariableValue("v")) - } -} diff --git a/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/ExpressionsTest.kt b/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/ExpressionsTest.kt deleted file mode 100644 index c86bd7c..0000000 --- a/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/ExpressionsTest.kt +++ /dev/null @@ -1,101 +0,0 @@ -package space.kscience.controls.constructor - -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith -import kotlin.test.assertTrue -import space.kscience.controls.constructor.dsl.core.expressions.* -import space.kscience.controls.constructor.dsl.core.units.Units -import space.kscience.controls.constructor.dsl.core.variables.Variable - -class ExpressionsTest { - - @Test - fun testCreationOfDifferentTypesOfExpressions() { - // Constant expression - val constExpr = ConstantExpression(5.0, Units.meter) - assertEquals(5.0, constExpr.value) - assertEquals(Units.meter, constExpr.unit) - - // Variable expression - val speedVar = Variable("speed", Units.meter / Units.second) - val speedExpr = VariableExpression(speedVar) - assertEquals(speedVar, speedExpr.variable) - assertEquals(Units.meter / Units.second, speedExpr.unit) - - // Binary expression (addition) - val distanceVar = Variable("distance", Units.meter) - val distanceExpr = VariableExpression(distanceVar) - val totalDistanceExpr = BinaryExpression(distanceExpr, BinaryOperator.ADD, constExpr) - assertEquals(Units.meter, totalDistanceExpr.unit) - - // Function call expression - val sinExpr = FunctionCallExpression("sin", listOf(constExpr)) - assertEquals(Units.dimensionless, sinExpr.unit) - } - - @Test - fun testEvaluationOfExpressions() { - val xVar = Variable("x", Units.meter) - val xExpr = VariableExpression(xVar) - val constExpr = ConstantExpression(2.0, Units.meter) - - val values = mapOf("x" to 3.0) - - // Binary expression: x + 2 - val sumExpr = BinaryExpression(xExpr, BinaryOperator.ADD, constExpr) - assertEquals(5.0, sumExpr.evaluate(values)) - - // Binary expression: x * 2 - val mulExpr = BinaryExpression(xExpr, BinaryOperator.MULTIPLY, ConstantExpression(2.0, Units.dimensionless)) - assertEquals(6.0, mulExpr.evaluate(values)) - - // Function call: sin(x) - val sinExpr = FunctionCallExpression("sin", listOf(xExpr)) - assertEquals(kotlin.math.sin(3.0), sinExpr.evaluate(values)) - } - - @Test - fun testDimensionCheckingInExpressions() { - val lengthVar = Variable("length", Units.meter) - val timeVar = Variable("time", Units.second) - val lengthExpr = VariableExpression(lengthVar) - val timeExpr = VariableExpression(timeVar) - - // Correct addition - val sumExpr = BinaryExpression(lengthExpr, BinaryOperator.ADD, ConstantExpression(5.0, Units.meter)) - assertFailsWith { - sumExpr.checkDimensions() - } - - // Incorrect addition (length + time) - val invalidSumExpr = BinaryExpression(lengthExpr, BinaryOperator.ADD, timeExpr) - val exception = assertFailsWith { - invalidSumExpr.checkDimensions() - } - assertEquals("Units must be the same for addition or subtraction.", exception.message) - } - - @Test - fun testDerivativeOfExpressions() { - val xVar = Variable("x", Units.meter) - val xExpr = VariableExpression(xVar) - val constExpr = ConstantExpression(5.0, Units.meter) - - // Derivative of constant: should be zero - val derivativeConst = constExpr.derivative(xExpr) - assertTrue(derivativeConst is ConstantExpression) - assertEquals(0.0, derivativeConst.value) - - // Derivative of variable with respect to itself: should be one - val derivativeVar = xExpr.derivative(xExpr) - assertTrue(derivativeVar is ConstantExpression) - assertEquals(1.0, derivativeVar.value) - - // Derivative of x + 5 with respect to x: should be one - val sumExpr = BinaryExpression(xExpr, BinaryOperator.ADD, constExpr) - val derivativeSum = sumExpr.derivative(xExpr) - assertTrue(derivativeSum is ConstantExpression) - assertEquals(1.0, derivativeSum.value) - } -} diff --git a/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/FunctionsTest.kt b/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/FunctionsTest.kt deleted file mode 100644 index 20db6bb..0000000 --- a/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/FunctionsTest.kt +++ /dev/null @@ -1,29 +0,0 @@ -package space.kscience.controls.constructor - -import kotlin.test.Test -import kotlin.test.assertEquals -import space.kscience.controls.constructor.dsl.core.functions.FunctionBuilder -import space.kscience.controls.constructor.dsl.core.expressions.* -import space.kscience.controls.constructor.dsl.core.variables.Variable -import space.kscience.controls.constructor.dsl.core.units.Units - -class FunctionsTest { - - @Test - fun testCreatingFunctions() { - val functionBuilder = FunctionBuilder("add").apply { - val aVar = input("a", Units.dimensionless) - val bVar = input("b", Units.dimensionless) - val resultVar = output("result", Units.dimensionless) - - algorithm { - assign(resultVar, BinaryExpression(VariableExpression(aVar), BinaryOperator.ADD, VariableExpression(bVar))) - } - } - - val addFunction = functionBuilder.build() - assertEquals("add", addFunction.name) - assertEquals(2, addFunction.inputs.size) - assertEquals(1, addFunction.outputs.size) - } -} diff --git a/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/IntegrationTest.kt b/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/IntegrationTest.kt new file mode 100644 index 0000000..784b5b4 --- /dev/null +++ b/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/IntegrationTest.kt @@ -0,0 +1,76 @@ +package space.kscience.controls.constructor + +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlinx.coroutines.test.runTest +import space.kscience.controls.constructor.CompositeDeviceTest.Position +import space.kscience.controls.constructor.CompositeDeviceTest.PositionConverter +import space.kscience.controls.constructor.CompositeDeviceTest.RobotArmDevice +import space.kscience.controls.spec.* +import space.kscience.dataforge.context.Context +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.number + +class IntegrationTest { + + @Test + fun testFullDeviceSimulation() = runTest { + val context = Context("TestContext") + val robotArm = RobotArmDevice(context) + + robotArm.start() + + val position = Position(base = 90.0, elbow = 45.0, wrist = 30.0) + robotArm.execute("moveToPosition", PositionConverter.convert(position)) + + val baseSpeedMeta = robotArm.baseMotor.readProperty("speed") + val baseSpeed = baseSpeedMeta.value?.number?.toDouble() + assertEquals(90.0, baseSpeed) + + val elbowSpeedMeta = robotArm.elbowMotor.readProperty("speed") + val elbowSpeed = elbowSpeedMeta.value?.number?.toDouble() + assertEquals(45.0, elbowSpeed) + + val wristSpeedMeta = robotArm.wristMotor.readProperty("speed") + val wristSpeed = wristSpeedMeta.value?.number?.toDouble() + assertEquals(30.0, wristSpeed) + + robotArm.stop() + } + + class SensorDevice( + context: Context, + meta: Meta = Meta.EMPTY + ) : DeviceBySpec(SensorSpec, context, meta) { + private var _pressure: Double = 101325.0 // Atmospheric pressure in Pascals + + suspend fun getPressure(): Double = _pressure + + override fun toString(): String = "SensorDevice" + + } + + object SensorSpec : DeviceSpec() { + val pressure by doubleProperty( + name = "pressure", + read = { propertyName -> + getPressure() + } + ) + } + + @Test + fun testUnitsInDeviceProperties() = runTest { + val context = Context("TestContext") + + val sensor = SensorDevice(context) + + sensor.start() + + val pressureMeta = sensor.readProperty("pressure") + val pressureValue = pressureMeta.value?.number?.toDouble() + assertEquals(101325.0, pressureValue) + + sensor.stop() + } +} \ No newline at end of file diff --git a/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/UnitsOfMeasurementTest.kt b/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/UnitsOfMeasurementTest.kt new file mode 100644 index 0000000..40d86f3 --- /dev/null +++ b/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/UnitsOfMeasurementTest.kt @@ -0,0 +1,18 @@ +package space.kscience.controls.constructor + +import space.kscience.controls.constructor.units.Degrees +import space.kscience.controls.constructor.units.Meters +import space.kscience.controls.constructor.units.NumericalValue +import space.kscience.controls.constructor.units.Radians +import space.kscience.controls.constructor.units.SIPrefix +import space.kscience.controls.constructor.units.Seconds +import space.kscience.controls.constructor.units.UnitsOfMeasurement +import space.kscience.controls.constructor.units.withPrefix +import kotlin.math.PI +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith + +class UnitsOfMeasurementTest { + +} diff --git a/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/UnitsTest.kt b/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/UnitsTest.kt deleted file mode 100644 index 81d5081..0000000 --- a/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/UnitsTest.kt +++ /dev/null @@ -1,85 +0,0 @@ -package space.kscience.controls.constructor - -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith -import space.kscience.controls.constructor.dsl.core.units.Dimension -import space.kscience.controls.constructor.dsl.core.units.PhysicalUnit -import space.kscience.controls.constructor.dsl.core.units.Units - -class UnitsTest { - - @Test - fun testCreationOfBaseUnits() { - val meter = Units.meter - assertEquals("meter", meter.name) - assertEquals("m", meter.symbol) - assertEquals(1.0, meter.conversionFactorToSI) - assertEquals(Dimension(length = 1.0), meter.dimension) - - val kilogram = Units.kilogram - assertEquals("kilogram", kilogram.name) - assertEquals("kg", kilogram.symbol) - assertEquals(1.0, kilogram.conversionFactorToSI) - assertEquals(Dimension(mass = 1.0), kilogram.dimension) - - val second = Units.second - assertEquals("second", second.name) - assertEquals("s", second.symbol) - assertEquals(1.0, second.conversionFactorToSI) - assertEquals(Dimension(time = 1.0), second.dimension) - } - - @Test - fun testArithmeticOperationsOnUnits() { - val meter = Units.meter - val second = Units.second - val kilogram = Units.kilogram - - val velocityUnit = meter / second - assertEquals(Dimension(length = 1.0, time = -1.0), velocityUnit.dimension) - assertEquals("m/s", velocityUnit.symbol) - - val accelerationUnit = meter / (second * second) - assertEquals(Dimension(length = 1.0, time = -2.0), accelerationUnit.dimension) - assertEquals("m/s^2", accelerationUnit.symbol) - - val forceUnit = kilogram * accelerationUnit - assertEquals(Dimension(mass = 1.0, length = 1.0, time = -2.0), forceUnit.dimension) - assertEquals("kg*m/s^2", forceUnit.symbol) - } - - @Test - fun testUnitCompatibilityForAdditionAndSubtraction() { - val meter = Units.meter - val kilogram = Units.kilogram - - // Correct addition - val length1 = meter - val length2 = meter - assertEquals(length1.dimension, length2.dimension) - - // Incorrect addition should throw exception - val mass = kilogram - val exception = assertFailsWith { - if (length1.dimension != mass.dimension) { - throw IllegalArgumentException("Units are not compatible for addition.") - } - } - assertEquals("Units are not compatible for addition.", exception.message) - } - - @Test - fun testUnitConversion() { - val kilometer = PhysicalUnit("kilometer", "km", 1000.0, Units.meter.dimension) - val centimeter = PhysicalUnit("centimeter", "cm", 0.01, Units.meter.dimension) - - // Convert 1 km to meters - val kmInMeters = 1.0 * kilometer.conversionFactorToSI - assertEquals(1000.0, kmInMeters) - - // Convert 100 cm to meters - val cmInMeters = 100.0 * centimeter.conversionFactorToSI - assertEquals(1.0, cmInMeters) - } -} diff --git a/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/UtilitiesTest.kt b/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/UtilitiesTest.kt new file mode 100644 index 0000000..1a21711 --- /dev/null +++ b/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/UtilitiesTest.kt @@ -0,0 +1,88 @@ +package space.kscience.controls.constructor + +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlinx.coroutines.test.runTest +import space.kscience.controls.spec.* +import space.kscience.dataforge.context.Context +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.boolean +import space.kscience.dataforge.meta.number + + +class UtilitiesTest { + + class SensorSpec : DeviceSpec() { + val temperature by doubleProperty( + name = "temperature", + read = { propertyName -> + getTemperature() + }, + write = { propertyName, value -> + setTemperature(value) + } + ) + + val isActive by booleanProperty( + name = "isActive", + read = { propertyName -> + isActive() + }, + write = { propertyName, value -> + setActive(value) + } + ) + } + + class SensorDevice( + context: Context, + meta: Meta = Meta.EMPTY + ) : DeviceBySpec(SensorSpec(), context, meta) { + private var _temperature: Double = 25.0 + private var _isActive: Boolean = true + + suspend fun getTemperature(): Double = _temperature + suspend fun setTemperature(value: Double) { + _temperature = value + } + + suspend fun isActive(): Boolean = _isActive + suspend fun setActive(value: Boolean) { + _isActive = value + } + + override fun toString(): String = "SensorDevice" + } + + @Test + fun testDoublePropertyUtility() = runTest { + val context = Context("TestContext") + val sensor = SensorDevice(context) + + sensor.start() + + sensor.writeProperty("temperature", Meta(30.0)) + + val tempMeta = sensor.readProperty("temperature") + val temperature = tempMeta.value?.number?.toDouble() + assertEquals(30.0, temperature) + + sensor.stop() + } + + @Test + fun testBooleanPropertyUtility() = runTest { + val context = Context("TestContext") + val sensor = SensorDevice(context) + + sensor.start() + + sensor.writeProperty("isActive", Meta(false)) + + val activeMeta = sensor.readProperty("isActive") + val isActive = activeMeta.value?.boolean + assertEquals(false, isActive) + + sensor.stop() + } +} \ No newline at end of file diff --git a/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/VariablesTest.kt b/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/VariablesTest.kt deleted file mode 100644 index 16a42a6..0000000 --- a/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/VariablesTest.kt +++ /dev/null @@ -1,41 +0,0 @@ -package space.kscience.controls.constructor - -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith -import kotlin.test.assertNull -import space.kscience.controls.constructor.dsl.core.units.Units -import space.kscience.controls.constructor.dsl.core.variables.ParameterVariable -import space.kscience.controls.constructor.dsl.core.variables.Variable - -class VariablesTest { - - @Test - fun testCreationOfVariablesAndParameters() { - val length = Variable("length", Units.meter) - assertEquals("length", length.name) - assertEquals(Units.meter, length.unit) - assertNull(length.value) - - val mass = ParameterVariable("mass", Units.kilogram, value = 10.0) - assertEquals("mass", mass.name) - assertEquals(Units.kilogram, mass.unit) - assertEquals(10.0, mass.value) - } - - @Test - fun testSettingAndGettingValuesOfVariablesAndParameters() { - val speed = Variable("speed", Units.meter / Units.second) - speed.value = 15.0 - assertEquals(15.0, speed.value) - - val density = ParameterVariable("density", Units.kilogram / Units.meter.pow(3.0), value = 1000.0) - assertEquals(1000.0, density.value) - - // Attempt to create a parameter with value outside the allowed range - val exception = assertFailsWith { - ParameterVariable("temperature", Units.kelvin, value = -10.0, min = 0.0) - } - assertEquals("Value of parameter temperature is less than minimum allowed value.", exception.message) - } -} diff --git a/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceBase.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceBase.kt index a060f46..b98836e 100644 --- a/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceBase.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceBase.kt @@ -50,6 +50,13 @@ public abstract class DeviceBase( final override val meta: Meta = Meta.EMPTY, ) : CachingDevice { + private val stateLock = Mutex() + + /** + * Logical state store + */ + private val logicalState: MutableMap = HashMap() + /** * Collection of property specifications */ @@ -89,20 +96,12 @@ public abstract class DeviceBase( } ) - - /** - * Logical state store - */ - private val logicalState: HashMap = HashMap() - public override val messageFlow: SharedFlow get() = sharedMessageFlow @Suppress("UNCHECKED_CAST") internal val self: D get() = this as D - private val stateLock = Mutex() - /** * Update logical property state and notify listeners */ diff --git a/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceBySpec.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceBySpec.kt index f639fc7..20e7c10 100644 --- a/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceBySpec.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceBySpec.kt @@ -2,7 +2,7 @@ package space.kscience.controls.spec import space.kscience.controls.api.Device import space.kscience.dataforge.context.Context -import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.* /** * A device generated from specification @@ -16,14 +16,36 @@ public open class DeviceBySpec( override val properties: Map> get() = spec.properties override val actions: Map> get() = spec.actions + // Map to store instances of nested devices + private val _nestedDevices = hashMapOf() + + // Provide access to nested devices + public val nestedDevices: Map get() = _nestedDevices + override suspend fun onStart(): Unit = with(spec) { + for ((name, deviceSpec) in spec.nestedDeviceSpecs) { + val nestedDevice = createNestedDevice(deviceSpec, name) + _nestedDevices[name] = nestedDevice + nestedDevice.start() + } self.onOpen() } override suspend fun onStop(): Unit = with(spec){ + for (device in _nestedDevices.values) { + device.stop() + } self.onClose() } override fun toString(): String = "Device(spec=$spec)" + + private fun createNestedDevice(deviceSpec: DeviceSpec, name: String): ND { + // Create an instance of the nested device. + val nestedMeta = meta[name] ?: Meta.EMPTY + val nestedDevice = deviceSpec.createDevice(context, nestedMeta) + return nestedDevice + } + } \ No newline at end of file diff --git a/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceSpec.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceSpec.kt index c6f8b0a..399ea5a 100644 --- a/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceSpec.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceSpec.kt @@ -2,6 +2,7 @@ package space.kscience.controls.spec import kotlinx.coroutines.withContext import space.kscience.controls.api.* +import space.kscience.dataforge.context.Context import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.MetaConverter import space.kscience.dataforge.meta.descriptors.MetaDescriptor @@ -20,15 +21,34 @@ public val MetaConverter.Companion.unit: MetaConverter get() = UnitMetaCon @OptIn(InternalDeviceAPI::class) public abstract class DeviceSpec { - //initializing the metadata property for everyone - private val _properties = hashMapOf>( - DeviceMetaPropertySpec.name to DeviceMetaPropertySpec - ) + // Map to store properties + private val _properties = hashMapOf>() public val properties: Map> get() = _properties - private val _actions = HashMap>() + // Map to store actions + private val _actions = hashMapOf>() public val actions: Map> get() = _actions + /** + * Registers a property in the spec. + */ + public fun > registerProperty(deviceProperty: P): P { + _properties[deviceProperty.name] = deviceProperty + return deviceProperty + } + + // Map to store nested device specifications + private val _nestedDeviceSpecs = hashMapOf>() + public val nestedDeviceSpecs: Map> get() = _nestedDeviceSpecs + + + /** + * Registers an action in the spec. + */ + public fun registerAction(deviceAction: DeviceActionSpec): DeviceActionSpec { + _actions[deviceAction.name] = deviceAction + return deviceAction + } public open suspend fun D.onOpen() { } @@ -36,12 +56,6 @@ public abstract class DeviceSpec { public open suspend fun D.onClose() { } - - public fun > registerProperty(deviceProperty: P): P { - _properties[deviceProperty.name] = deviceProperty - return deviceProperty - } - public fun property( converter: MetaConverter, descriptorBuilder: PropertyDescriptor.() -> Unit = {}, @@ -110,12 +124,6 @@ public abstract class DeviceSpec { } } - - public fun registerAction(deviceAction: DeviceActionSpec): DeviceActionSpec { - _actions[deviceAction.name] = deviceAction - return deviceAction - } - public fun action( inputConverter: MetaConverter, outputConverter: MetaConverter, @@ -156,6 +164,24 @@ public abstract class DeviceSpec { deviceAction } } + + public open fun createDevice(context: Context, meta: Meta): D { + // Since DeviceSpec doesn't know how to create an instance of D, + // you need to override this method in subclasses. + throw NotImplementedError("createDevice must be implemented in subclasses") + } + + public fun device( + deviceSpec: DeviceSpec, + name: String? = null + ): PropertyDelegateProvider, ReadOnlyProperty, DeviceSpec>> = + PropertyDelegateProvider { _, property -> + val deviceName = name ?: property.name + // Register the nested device spec + _nestedDeviceSpecs[deviceName] = deviceSpec + ReadOnlyProperty { _, _ -> deviceSpec } + } + } /** diff --git a/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/propertySpecDelegates.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/propertySpecDelegates.kt index 8bd22b6..f6bbdb0 100644 --- a/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/propertySpecDelegates.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/propertySpecDelegates.kt @@ -6,6 +6,8 @@ import space.kscience.controls.api.metaDescriptor import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.MetaConverter import space.kscience.dataforge.meta.ValueType +import space.kscience.dataforge.meta.descriptors.MetaDescriptor +import space.kscience.dataforge.meta.string import kotlin.properties.PropertyDelegateProvider import kotlin.properties.ReadOnlyProperty import kotlin.reflect.KMutableProperty1 @@ -191,4 +193,123 @@ public fun DeviceSpec.metaProperty( read: suspend D.(propertyName: String) -> Meta, write: suspend D.(propertyName: String, value: Meta) -> Unit ): PropertyDelegateProvider, ReadOnlyProperty, MutableDevicePropertySpec>> = - mutableProperty(MetaConverter.meta, descriptorBuilder, name, read, write) \ No newline at end of file + mutableProperty(MetaConverter.meta, descriptorBuilder, name, read, write) + +public fun DeviceSpec.doubleProperty( + description: String? = null, + name: String? = null, + read: suspend D.(String) -> Double?, + write: (suspend D.(String, Double) -> Unit)? = null +): PropertyDelegateProvider, ReadOnlyProperty, DevicePropertySpec>> { + val converter = MetaConverter.double + val descriptorBuilder: PropertyDescriptor.() -> Unit = { + this.description = description + metaDescriptor { + valueType(ValueType.NUMBER) + } + } + return if (write != null) { + mutableProperty(converter, descriptorBuilder, name, read, write) + } else { + property(converter, descriptorBuilder, name, read) + } +} + +public fun DeviceSpec.booleanProperty( + description: String? = null, + name: String? = null, + read: suspend D.(String) -> Boolean?, + write: (suspend D.(String, Boolean) -> Unit)? = null +): PropertyDelegateProvider, ReadOnlyProperty, DevicePropertySpec>> { + val converter = MetaConverter.boolean + val descriptorBuilder: PropertyDescriptor.() -> Unit = { + this.description = description + metaDescriptor { + valueType(ValueType.BOOLEAN) + } + } + return if (write != null) { + mutableProperty(converter, descriptorBuilder, name, read, write) + } else { + property(converter, descriptorBuilder, name, read) + } +} + +public fun DeviceSpec.stringProperty( + description: String? = null, + name: String? = null, + read: suspend D.(String) -> String?, + write: (suspend D.(String, String) -> Unit)? = null +): PropertyDelegateProvider, ReadOnlyProperty, DevicePropertySpec>> { + val converter = MetaConverter.string + val descriptorBuilder: PropertyDescriptor.() -> Unit = { + this.description = description + metaDescriptor { + valueType(ValueType.STRING) + } + } + return if (write != null) { + mutableProperty(converter, descriptorBuilder, name, read, write) + } else { + property(converter, descriptorBuilder, name, read) + } +} + +public fun DeviceSpec.intProperty( + description: String? = null, + name: String? = null, + read: suspend D.(propertyName: String) -> Int?, + write: (suspend D.(propertyName: String, value: Int) -> Unit)? = null +): PropertyDelegateProvider, ReadOnlyProperty, DevicePropertySpec>> { + val converter = MetaConverter.int + val descriptorBuilder: PropertyDescriptor.() -> Unit = { + this.description = description + metaDescriptor { + valueType(ValueType.NUMBER) + } + } + return if (write != null) { + mutableProperty(converter, descriptorBuilder, name, read, write) + } else { + property(converter, descriptorBuilder, name, read) + } +} + +public fun , D : Device> DeviceSpec.enumProperty( + enumValues: Array, + descriptorBuilder: PropertyDescriptor.() -> Unit = {}, + name: String? = null, + read: suspend D.(propertyName: String) -> E?, + write: (suspend D.(propertyName: String, value: E) -> Unit)? = null +): PropertyDelegateProvider, ReadOnlyProperty, DevicePropertySpec>> { + val converter = object : MetaConverter { + override val descriptor: MetaDescriptor = MetaDescriptor { + valueType(ValueType.STRING) + allowedValues(enumValues.map { it.name }) + } + + override fun readOrNull(source: Meta): E? { + val value = source.string ?: return null + return enumValues.firstOrNull { it.name == value } + } + + override fun convert(obj: E): Meta = Meta(obj.name) + } + + return if (write != null) { + mutableProperty( + converter = converter, + descriptorBuilder = descriptorBuilder, + name = name, + read = read, + write = write + ) + } else { + property( + converter = converter, + descriptorBuilder = descriptorBuilder, + name = name, + read = read + ) + } +} \ No newline at end of file