From 0548cfa2ccf0dd2412e5063aee84ee412e415bf3 Mon Sep 17 00:00:00 2001 From: kel Date: Thu, 5 Sep 2024 12:08:54 +0200 Subject: [PATCH 01/15] fixed issue with model description using wrong type for values related to clocks --- .../intocps/maestro/fmi/fmi3/Fmi3ModelDescription.kt | 10 +++++----- .../intocps/maestro/fmi/fmi3/Fmi3TypeDefinitions.kt | 4 ++-- .../org/intocps/maestro/fmi/fmi3/Fmi3Variables.kt | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/fmi/src/main/kotlin/org/intocps/maestro/fmi/fmi3/Fmi3ModelDescription.kt b/fmi/src/main/kotlin/org/intocps/maestro/fmi/fmi3/Fmi3ModelDescription.kt index 07a0156a..893a2861 100644 --- a/fmi/src/main/kotlin/org/intocps/maestro/fmi/fmi3/Fmi3ModelDescription.kt +++ b/fmi/src/main/kotlin/org/intocps/maestro/fmi/fmi3/Fmi3ModelDescription.kt @@ -614,10 +614,10 @@ class Fmi3ModelDescription : ModelDescription { ?: typeDefinition?.canBeDeactivated){true->true;false,null->false}, node.attributes.getNamedItem("priority")?.nodeValue?.toUInt() ?: typeDefinition?.priority, if (interval == null) typeDefinition!!.interval else valueOf(interval), - node.attributes.getNamedItem("intervalDecimal")?.nodeValue?.toFloat() + node.attributes.getNamedItem("intervalDecimal")?.nodeValue?.toDouble() ?: typeDefinition?.intervalDecimal, - node.attributes.getNamedItem("shiftDecimal")?.nodeValue?.toFloat() ?: typeDefinition?.shiftDecimal - ?: (0).toFloat(), + node.attributes.getNamedItem("shiftDecimal")?.nodeValue?.toDouble() ?: typeDefinition?.shiftDecimal + ?: (0).toDouble(), node.attributes.getNamedItem("supportsFraction")?.nodeValue?.toBoolean() ?: typeDefinition?.supportsFraction ?: false, node.attributes.getNamedItem("resolution")?.nodeValue?.toULong() ?: typeDefinition?.resolution, @@ -665,8 +665,8 @@ class Fmi3ModelDescription : ModelDescription { node.attributes.getNamedItem("canBeDeactivated")?.nodeValue?.toBoolean(), node.attributes.getNamedItem("priority")?.nodeValue?.toUInt(), valueOf(node.attributes.getNamedItem("interval")!!.nodeValue), - node.attributes.getNamedItem("intervalDecimal")?.nodeValue?.toFloat(), - node.attributes.getNamedItem("shiftDecimal")?.nodeValue?.toFloat(), + node.attributes.getNamedItem("intervalDecimal")?.nodeValue?.toDouble(), + node.attributes.getNamedItem("shiftDecimal")?.nodeValue?.toDouble(), node.attributes.getNamedItem("supportsFraction")?.nodeValue?.toBoolean(), node.attributes.getNamedItem("resolution")?.nodeValue?.toULong(), node.attributes.getNamedItem("intervalCounter")?.nodeValue?.toULong(), diff --git a/fmi/src/main/kotlin/org/intocps/maestro/fmi/fmi3/Fmi3TypeDefinitions.kt b/fmi/src/main/kotlin/org/intocps/maestro/fmi/fmi3/Fmi3TypeDefinitions.kt index 122f4d5f..0bc2b0b7 100644 --- a/fmi/src/main/kotlin/org/intocps/maestro/fmi/fmi3/Fmi3TypeDefinitions.kt +++ b/fmi/src/main/kotlin/org/intocps/maestro/fmi/fmi3/Fmi3TypeDefinitions.kt @@ -66,8 +66,8 @@ data class ClockTypeDefinition( val canBeDeactivated: Boolean? = false, val priority: UInt?, val interval: Fmi3ClockInterval, - val intervalDecimal: Float?, - val shiftDecimal: Float? = (0).toFloat(), + val intervalDecimal: Double?, + val shiftDecimal: Double? = (0).toDouble(), val supportsFraction: Boolean? = false, val resolution: ULong?, val intervalCounter: ULong?, diff --git a/fmi/src/main/kotlin/org/intocps/maestro/fmi/fmi3/Fmi3Variables.kt b/fmi/src/main/kotlin/org/intocps/maestro/fmi/fmi3/Fmi3Variables.kt index 5a91d591..1a31dca7 100644 --- a/fmi/src/main/kotlin/org/intocps/maestro/fmi/fmi3/Fmi3Variables.kt +++ b/fmi/src/main/kotlin/org/intocps/maestro/fmi/fmi3/Fmi3Variables.kt @@ -284,8 +284,8 @@ class ClockVariable( val canBeDeactivated: Boolean? = false, val priority: UInt? = null, val interval: Fmi3ClockInterval, - val intervalDecimal: Float? = null, - val shiftDecimal: Float? = (0).toFloat(), + val intervalDecimal: Double? = null, + val shiftDecimal: Double? = (0).toDouble(), val supportsFraction: Boolean? = false, val resolution: ULong? = null, val intervalCounter: ULong? = null, From a8abcfdf0488ed6f0f00314f6b61273988452664 Mon Sep 17 00:00:00 2001 From: kel Date: Thu, 5 Sep 2024 12:31:31 +0200 Subject: [PATCH 02/15] added support for float --- .../intocps/maestro/ast/MableAstFactory.java | 9 ++ ast/src/main/resources/mabl.astv2 | 2 + .../framework/fmi2/api/FmiBuilder.java | 9 +- .../scoping/DynamicActiveBuilderScope.java | 15 +++ .../fmi2/api/mabl/scoping/IMablScope.java | 6 + .../fmi2/api/mabl/scoping/ScopeFmi2Api.java | 9 ++ .../api/mabl/values/FloatExpressionValue.java | 106 ++++++++++++++++++ .../mabl/variables/FloatVariableFmi2Api.java | 47 ++++++++ .../api/mabl/variables/VariableFmi2Api.java | 4 +- .../maestro/interpreter/Interpreter.java | 5 + .../external/ExternalReflectCallHelper.java | 2 +- .../parser/ParseTree2AstConverter.java | 2 + .../maestro/typechecker/TypeCheckVisitor.java | 19 +--- 13 files changed, 216 insertions(+), 19 deletions(-) create mode 100644 frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/values/FloatExpressionValue.java create mode 100644 frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/variables/FloatVariableFmi2Api.java diff --git a/ast/src/main/java/org/intocps/maestro/ast/MableAstFactory.java b/ast/src/main/java/org/intocps/maestro/ast/MableAstFactory.java index ee3df3ad..9fdab6d2 100644 --- a/ast/src/main/java/org/intocps/maestro/ast/MableAstFactory.java +++ b/ast/src/main/java/org/intocps/maestro/ast/MableAstFactory.java @@ -370,6 +370,11 @@ public static ARealLiteralExp newARealLiteralExp(Double value) { return exp; } + public static AFloatLiteralExp newAFloatLiteralExp(Float value) { + AFloatLiteralExp exp = new AFloatLiteralExp(); + exp.setValue(value); + return exp; + } public static AIntLiteralExp newAIntLiteralExp(Integer value) { AIntLiteralExp exp = new AIntLiteralExp(); exp.setValue(value); @@ -416,6 +421,10 @@ public static ARealNumericPrimitiveType newARealNumericPrimitiveType() { ARealNumericPrimitiveType type = new ARealNumericPrimitiveType(); return type; } + public static AFloatNumericPrimitiveType newAFloatNumericPrimitiveType() { + AFloatNumericPrimitiveType type = new AFloatNumericPrimitiveType(); + return type; + } public static ARealNumericPrimitiveType newRealType() { diff --git a/ast/src/main/resources/mabl.astv2 b/ast/src/main/resources/mabl.astv2 index ecb10d78..20412389 100644 --- a/ast/src/main/resources/mabl.astv2 +++ b/ast/src/main/resources/mabl.astv2 @@ -15,6 +15,7 @@ Tokens java_Boolean = 'java:java.lang.Boolean'; java_Integer = 'java:java.lang.Integer'; java_Double = 'java:java.lang.Double'; + java_Float = 'java:java.lang.Float'; java_Long = 'java:java.lang.Long'; lex_identifier='java:org.intocps.maestro.ast.LexIdentifier'; @@ -92,6 +93,7 @@ exp | {int} [value]:java_Integer | {uInt} [value]:java_Long | {real} [value]:java_Double + | {float} [value]:java_Float ; #binary {-> package='org.intocps.maestro.ast' diff --git a/frameworks/builder/src/main/java/org/intocps/maestro/framework/fmi2/api/FmiBuilder.java b/frameworks/builder/src/main/java/org/intocps/maestro/framework/fmi2/api/FmiBuilder.java index c666f586..51d05dba 100644 --- a/frameworks/builder/src/main/java/org/intocps/maestro/framework/fmi2/api/FmiBuilder.java +++ b/frameworks/builder/src/main/java/org/intocps/maestro/framework/fmi2/api/FmiBuilder.java @@ -234,7 +234,7 @@ interface Scope extends Scoping { * @return */ DoubleVariable store(double value); - + FloatVariable store(float value); StringVariable store(String value); BoolVariable store(boolean value); @@ -248,6 +248,7 @@ interface Scope extends Scoping { * @return */ DoubleVariable store(String name, double value); + FloatVariable store(String name, float value); StringVariable store(String name, String value); @@ -477,6 +478,10 @@ interface DoubleVariable extends Variable, Prov void set(Double value); } + interface FloatVariable extends Variable, ProvidesTypedReferenceExp, NumericTypedReferenceExp { + + void set(Float value); + } interface BoolVariable extends Variable, ProvidesTypedReferenceExp { Predicate toPredicate(); @@ -882,6 +887,8 @@ interface BooleanExpressionValue extends FmiBuilder.ExpressionValue { interface DoubleExpressionValue extends NumericExpressionValue { } + interface FloatExpressionValue extends NumericExpressionValue { + } interface IntExpressionValue extends NumericExpressionValue { } diff --git a/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/scoping/DynamicActiveBuilderScope.java b/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/scoping/DynamicActiveBuilderScope.java index b6f16cc9..39366a3c 100644 --- a/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/scoping/DynamicActiveBuilderScope.java +++ b/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/scoping/DynamicActiveBuilderScope.java @@ -136,6 +136,11 @@ public DoubleVariableFmi2Api store(double value) { return activeScope.store(value); } + @Override + public FloatVariableFmi2Api store(float value) { + return activeScope.store(value); + } + @Override public StringVariableFmi2Api store(String value) { return activeScope.store(value); @@ -153,6 +158,11 @@ public DoubleVariableFmi2Api store(String name, double value) { } + @Override + public FloatVariableFmi2Api store(String name, float value) { + return activeScope.store(name,value); + } + @Override public StringVariableFmi2Api store(String name, String value) { return activeScope.store(name, value); @@ -207,6 +217,11 @@ public DoubleVariableFmi2Api store(String namePrefix, DoubleVariableFmi2Api vari return activeScope.store(namePrefix, variable); } + @Override + public FloatVariableFmi2Api store(String namePrefix, FloatVariableFmi2Api variable) { + return activeScope.store(namePrefix,variable); + } + @Override public ArrayVariableFmi2Api storeInArray(String name, VariableFmi2Api[] variables) { return activeScope.storeInArray(name, variables); diff --git a/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/scoping/IMablScope.java b/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/scoping/IMablScope.java index c0dcec9d..f650e981 100644 --- a/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/scoping/IMablScope.java +++ b/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/scoping/IMablScope.java @@ -64,6 +64,8 @@ public interface IMablScope extends FmiBuilder.Scope { @Override DoubleVariableFmi2Api store(double value); + @Override + FloatVariableFmi2Api store(float value); @Override IntVariableFmi2Api store(int value); @@ -74,6 +76,9 @@ public interface IMablScope extends FmiBuilder.Scope { @Override DoubleVariableFmi2Api store(String name, double value); + @Override + FloatVariableFmi2Api store(String name, float value); + @Override BooleanVariableFmi2Api store(String name, boolean value); @@ -96,6 +101,7 @@ public interface IMablScope extends FmiBuilder.Scope { DoubleVariableFmi2Api store(String namePrefix, DoubleVariableFmi2Api variable); + FloatVariableFmi2Api store(String namePrefix, FloatVariableFmi2Api variable); ArrayVariableFmi2Api storeInArray(String name, VariableFmi2Api[] variables); diff --git a/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/scoping/ScopeFmi2Api.java b/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/scoping/ScopeFmi2Api.java index 28f56b3d..2edcc2ba 100644 --- a/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/scoping/ScopeFmi2Api.java +++ b/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/scoping/ScopeFmi2Api.java @@ -490,6 +490,15 @@ public DoubleVariableFmi2Api store(String namePrefix, DoubleVariableFmi2Api vari newAIdentifierExp(name)); } + @Override + public FloatVariableFmi2Api store(String namePrefix, FloatVariableFmi2Api variable) { + String name = getName(namePrefix); + PStm var = newVariable(name, newAFloatNumericPrimitiveType(), variable.getReferenceExp()); + add(var); + return new FloatVariableFmi2Api(var, this, builder.getDynamicScope(), newAIdentifierStateDesignator(newAIdentifier(name)), + newAIdentifierExp(name)); + } + @Override public ArrayVariableFmi2Api storeInArray(String namePrefix, VariableFmi2Api[] variables) { String name = getName(namePrefix); diff --git a/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/values/FloatExpressionValue.java b/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/values/FloatExpressionValue.java new file mode 100644 index 00000000..0549a546 --- /dev/null +++ b/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/values/FloatExpressionValue.java @@ -0,0 +1,106 @@ +package org.intocps.maestro.framework.fmi2.api.mabl.values; + +import org.intocps.maestro.ast.node.AFloatNumericPrimitiveType; +import org.intocps.maestro.ast.node.ARealNumericPrimitiveType; +import org.intocps.maestro.ast.node.PExp; +import org.intocps.maestro.ast.node.PType; +import org.intocps.maestro.framework.fmi2.api.FmiBuilder; +import org.intocps.maestro.framework.fmi2.api.mabl.NumericExpressionValueFmi2Api; +import org.intocps.maestro.framework.fmi2.api.mabl.PredicateFmi2Api; + +import static org.intocps.maestro.ast.MableAstFactory.*; + + +public class FloatExpressionValue extends NumericExpressionValueFmi2Api implements FmiBuilder.FloatExpressionValue { + + final PType type = new AFloatNumericPrimitiveType(); + final PExp exp; + + public FloatExpressionValue(float value) { + this.exp = newAFloatLiteralExp(value); + } + + public FloatExpressionValue(PExp exp) { + this.exp = exp; + } + + public static FloatExpressionValue of(float value) { + return new FloatExpressionValue(value); + } + + @Override + public PExp getExp() { + return this.exp.clone(); + } + + @Override + public PType getType() { + return this.type; + } + + @Override + public FloatExpressionValue subtraction(int v) { + return new FloatExpressionValue(newMinusExp(getExp(), newAIntLiteralExp(v))); + } + + @Override + public FloatExpressionValue addition(int v) { + return new FloatExpressionValue(newPlusExp(getExp(), newAIntLiteralExp(v))); + } + + @Override + public FloatExpressionValue divide(int v) { + return new FloatExpressionValue(newDivideExp(getExp(), newAIntLiteralExp(v))); + } + + @Override + public FloatExpressionValue multiply(int v) { + return new FloatExpressionValue(newMultiplyExp(getExp(), newAIntLiteralExp(v))); + } + + + @Override + public FloatExpressionValue subtraction(double v) { + return new FloatExpressionValue(newMinusExp(getExp(), newARealLiteralExp(v))); + } + + @Override + public FloatExpressionValue addition(double v) { + return new FloatExpressionValue(newPlusExp(getExp(), newARealLiteralExp(v))); + } + + @Override + public FloatExpressionValue divide(double v) { + return new FloatExpressionValue(newDivideExp(getExp(), newARealLiteralExp(v))); + } + + @Override + public FloatExpressionValue multiply(double v) { + return new FloatExpressionValue(newMultiplyExp(getExp(), newARealLiteralExp(v))); + } + + @Override + public FloatExpressionValue addition(FmiBuilder.NumericTypedReferenceExp v) { + return new FloatExpressionValue(newPlusExp(getExp(), v.getExp())); + } + + @Override + public FloatExpressionValue divide(FmiBuilder.NumericTypedReferenceExp v) { + return new FloatExpressionValue(newDivideExp(getExp(), v.getExp())); + } + + @Override + public FloatExpressionValue subtraction(FmiBuilder.NumericTypedReferenceExp v) { + return new FloatExpressionValue(newMinusExp(getExp(), v.getExp())); + } + + @Override + public FloatExpressionValue multiply(FmiBuilder.NumericTypedReferenceExp v) { + return new FloatExpressionValue(newMultiplyExp(getExp(), v.getExp())); + } + + @Override + public PredicateFmi2Api lessThan(FmiBuilder.NumericTypedReferenceExp endTimeVar) { + return new PredicateFmi2Api(newALessBinaryExp(getExp(), endTimeVar.getExp())); + } +} diff --git a/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/variables/FloatVariableFmi2Api.java b/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/variables/FloatVariableFmi2Api.java new file mode 100644 index 00000000..64f530a9 --- /dev/null +++ b/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/variables/FloatVariableFmi2Api.java @@ -0,0 +1,47 @@ +package org.intocps.maestro.framework.fmi2.api.mabl.variables; + +import org.intocps.maestro.ast.node.PExp; +import org.intocps.maestro.ast.node.PStateDesignator; +import org.intocps.maestro.ast.node.PStm; +import org.intocps.maestro.ast.node.PType; +import org.intocps.maestro.framework.fmi2.api.FmiBuilder; +import org.intocps.maestro.framework.fmi2.api.mabl.scoping.IMablScope; +import org.intocps.maestro.framework.fmi2.api.mabl.values.FloatExpressionValue; + +import static org.intocps.maestro.ast.MableAstFactory.newAFloatNumericPrimitiveType; + +public class FloatVariableFmi2Api extends VariableFmi2Api implements FmiBuilder.FloatVariable { + public FloatVariableFmi2Api(PStm declaration, IMablScope declaredScope, FmiBuilder.DynamicActiveScope dynamicScope, + PStateDesignator designator, PExp referenceExp) { + super(declaration, newAFloatNumericPrimitiveType(), declaredScope, dynamicScope, designator, referenceExp); + } + + + @Override + public void set(Float value) { + super.setValue(FloatExpressionValue.of(value)); + } + + + @Override + public void setValue(FmiBuilder.FloatExpressionValue value) { + super.setValue(value.getExp()); + } + + + @Override + public FloatExpressionValue toMath() { + return new FloatExpressionValue(this.getExp()); + } + + + @Override + public PType getType() { + return newAFloatNumericPrimitiveType(); + } + + @Override + public FloatVariableFmi2Api clone(PStm declaration, IMablScope declaredScope, PStateDesignator designator, PExp referenceExp) { + return new FloatVariableFmi2Api(declaration, declaredScope, dynamicScope, designator, referenceExp); + } +} diff --git a/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/variables/VariableFmi2Api.java b/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/variables/VariableFmi2Api.java index fac53805..2a337b47 100644 --- a/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/variables/VariableFmi2Api.java +++ b/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/variables/VariableFmi2Api.java @@ -75,11 +75,11 @@ public void setValue(FmiBuilder.Scope scope, FmiBuilder.Variable @Override public void setValue(FmiBuilder.Scope scope, V value) { - if (!(value instanceof DoubleExpressionValue)) { + if (!(value instanceof FmiBuilder.ProvidesTypedReferenceExp)) { throw new IllegalArgumentException(); } - scope.add(MableAstFactory.newAAssignmentStm(this.designator.clone(), ((DoubleExpressionValue) value).getExp())); + scope.add(MableAstFactory.newAAssignmentStm(this.designator.clone(), ((FmiBuilder.ProvidesTypedReferenceExp) value).getExp())); } @Override diff --git a/interpreter/src/main/java/org/intocps/maestro/interpreter/Interpreter.java b/interpreter/src/main/java/org/intocps/maestro/interpreter/Interpreter.java index cc6244ce..f316f3b5 100644 --- a/interpreter/src/main/java/org/intocps/maestro/interpreter/Interpreter.java +++ b/interpreter/src/main/java/org/intocps/maestro/interpreter/Interpreter.java @@ -548,6 +548,11 @@ public Value caseARealLiteralExp(ARealLiteralExp node, Context question) throws return NumericValue.valueOf(node.getValue()); } + @Override + public Value caseAFloatLiteralExp(AFloatLiteralExp node, Context question) throws AnalysisException { + return NumericValue.valueOf(node.getValue()); + } + @Override public Value caseABoolLiteralExp(ABoolLiteralExp node, Context question) throws AnalysisException { return new BooleanValue(node.getValue()); diff --git a/interpreter/src/main/java/org/intocps/maestro/interpreter/external/ExternalReflectCallHelper.java b/interpreter/src/main/java/org/intocps/maestro/interpreter/external/ExternalReflectCallHelper.java index 82c2b116..5915b27c 100644 --- a/interpreter/src/main/java/org/intocps/maestro/interpreter/external/ExternalReflectCallHelper.java +++ b/interpreter/src/main/java/org/intocps/maestro/interpreter/external/ExternalReflectCallHelper.java @@ -365,7 +365,7 @@ public Object map(Value v) { } case Float: { var n = ((NumericValue) v); - return Integer.valueOf(n.intValue()).floatValue(); + return n.floatValue();//Integer.valueOf(n.intValue()).floatValue(); } case Int: { var n = ((NumericValue) v); diff --git a/parser/src/main/java/org/intocps/maestro/parser/ParseTree2AstConverter.java b/parser/src/main/java/org/intocps/maestro/parser/ParseTree2AstConverter.java index 0ac4cdfe..33b20337 100644 --- a/parser/src/main/java/org/intocps/maestro/parser/ParseTree2AstConverter.java +++ b/parser/src/main/java/org/intocps/maestro/parser/ParseTree2AstConverter.java @@ -507,6 +507,8 @@ public INode visitLiteral(MablParser.LiteralContext ctx) { } else if (ctx.FLOAT_LITERAL() != null) { ARealLiteralExp literal = new ARealLiteralExp(); literal.setValue(Double.parseDouble(ctx.FLOAT_LITERAL().getText())); + AFloatLiteralExp literal = new AFloatLiteralExp(); + literal.setValue(Float.parseFloat(ctx.FLOAT_LITERAL().getText())); return literal; } else if (ctx.STRING_LITERAL() != null) { diff --git a/typechecker/src/main/java/org/intocps/maestro/typechecker/TypeCheckVisitor.java b/typechecker/src/main/java/org/intocps/maestro/typechecker/TypeCheckVisitor.java index 4f84d36f..85eb15a3 100644 --- a/typechecker/src/main/java/org/intocps/maestro/typechecker/TypeCheckVisitor.java +++ b/typechecker/src/main/java/org/intocps/maestro/typechecker/TypeCheckVisitor.java @@ -717,22 +717,11 @@ public PType caseAStringLiteralExp(AStringLiteralExp node, Context ctxt) throws @Override public PType caseARealLiteralExp(ARealLiteralExp node, Context ctxt) throws AnalysisException { + return store(node, detectType(node.getValue())); + } - // double value = node.getValue(); - // if (Math.round(value) == value) { - // if (value < 0) { - // return store(node, MableAstFactory.newIntType()); - // } else if (value == 0) { - // - // //nat - // return store(node, MableAstFactory.newIntType()); - // } else { - // //natone - // return store(node, MableAstFactory.newIntType()); - // } - // } else { - // return store(node, MableAstFactory.newRealType()); // Note, "1.234" is really "1234/1000" (a rat) - // } + @Override + public PType caseAFloatLiteralExp(AFloatLiteralExp node, Context ctxt) throws AnalysisException { return store(node, detectType(node.getValue())); } From 2d887eea29f632e9c6010661ff4166369a1270dd Mon Sep 17 00:00:00 2001 From: kel Date: Thu, 5 Sep 2024 15:01:53 +0200 Subject: [PATCH 03/15] added support for uint --- ast/src/main/resources/mabl.astv2 | 1 + .../framework/fmi2/api/FmiBuilder.java | 8 ++++ .../scoping/DynamicActiveBuilderScope.java | 21 ++++++++++ .../fmi2/api/mabl/scoping/IMablScope.java | 7 ++++ .../fmi2/api/mabl/scoping/ScopeFmi2Api.java | 41 ++++++++++++++++++- .../variables/InstanceVariableFmi3Api.java | 4 +- .../external/ExternalReflectCallHelper.java | 4 +- .../parser/ParseTree2AstConverter.java | 2 - .../org/intocps/maestro/typechecker/FMI3.mabl | 2 +- 9 files changed, 81 insertions(+), 9 deletions(-) diff --git a/ast/src/main/resources/mabl.astv2 b/ast/src/main/resources/mabl.astv2 index 20412389..cdad5450 100644 --- a/ast/src/main/resources/mabl.astv2 +++ b/ast/src/main/resources/mabl.astv2 @@ -93,6 +93,7 @@ exp | {int} [value]:java_Integer | {uInt} [value]:java_Long | {real} [value]:java_Double + | {long} [value]:java_Long | {float} [value]:java_Float ; diff --git a/frameworks/builder/src/main/java/org/intocps/maestro/framework/fmi2/api/FmiBuilder.java b/frameworks/builder/src/main/java/org/intocps/maestro/framework/fmi2/api/FmiBuilder.java index 51d05dba..5fe95acc 100644 --- a/frameworks/builder/src/main/java/org/intocps/maestro/framework/fmi2/api/FmiBuilder.java +++ b/frameworks/builder/src/main/java/org/intocps/maestro/framework/fmi2/api/FmiBuilder.java @@ -240,7 +240,9 @@ interface Scope extends Scoping { BoolVariable store(boolean value); IntVariable store(int value); + UIntVariable storeUInt(long value); + LongVariable store(long value); /** * Store a given value with a prefix name * @@ -255,10 +257,13 @@ interface Scope extends Scoping { BoolVariable store(String name, boolean value); IntVariable store(String name, int value); + UIntVariable storeUInt(String name, long value); + LongVariable store(String name, long value); ArrayVariable store(String name, CV[] value); ArrayVariable createArray(String name,Class type, IntVariable...sizes ) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException; + ArrayVariable createArray(String name,Class type, UIntVariable...sizes ) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException; /** * Store the given value and get a tag for it. Copy @@ -893,6 +898,9 @@ interface FloatExpressionValue extends NumericExpressionValue { interface IntExpressionValue extends NumericExpressionValue { } + interface LongExpressionValue extends NumericExpressionValue { + } + interface UIntExpressionValue extends IntExpressionValue { } diff --git a/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/scoping/DynamicActiveBuilderScope.java b/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/scoping/DynamicActiveBuilderScope.java index 39366a3c..3c9abd04 100644 --- a/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/scoping/DynamicActiveBuilderScope.java +++ b/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/scoping/DynamicActiveBuilderScope.java @@ -152,6 +152,17 @@ public IntVariableFmi2Api store(int value) { } + @Override + public UIntVariableFmi2Api storeUInt(long value) { + return activeScope.storeUInt(value); + + } + + @Override + public FmiBuilder.LongVariable store(long value) { + return activeScope.store(value); + } + @Override public DoubleVariableFmi2Api store(String name, double value) { return activeScope.store(name, value); @@ -179,6 +190,10 @@ public IntVariableFmi2Api store(String name, int value) { return activeScope.store(name, value); } + @Override + public UIntVariableFmi2Api storeUInt(String name, long value) { + return activeScope.storeUInt(name, value); + } /* @Override public , Var extends Fmi2Builder.Variable> Var store(String name, Var value) { return activeScope.store(name, value); @@ -202,6 +217,12 @@ public ArrayVariableFmi2Api createArray(String name, Class t return activeScope.createArray(name,type,sizes); } + @Override + public ArrayVariableFmi2Api createArray(String name, Class type, + FmiBuilder.UIntVariable... sizes) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException { + return activeScope.createArray(name,type,sizes); + } + @Override public FmiBuilder.Variable store(FmiBuilder.Value tag) { return activeScope.store(tag); diff --git a/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/scoping/IMablScope.java b/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/scoping/IMablScope.java index f650e981..e619fed2 100644 --- a/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/scoping/IMablScope.java +++ b/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/scoping/IMablScope.java @@ -70,6 +70,9 @@ public interface IMablScope extends FmiBuilder.Scope { @Override IntVariableFmi2Api store(int value); + @Override + UIntVariableFmi2Api storeUInt(long value); + @Override StringVariableFmi2Api store(String value); @@ -85,6 +88,9 @@ public interface IMablScope extends FmiBuilder.Scope { @Override IntVariableFmi2Api store(String name, int value); + @Override + UIntVariableFmi2Api storeUInt(String name, long value); + @Override StringVariableFmi2Api store(String name, String value); @@ -93,6 +99,7 @@ public interface IMablScope extends FmiBuilder.Scope { @Override ArrayVariableFmi2Api createArray(String name,Class type, FmiBuilder.IntVariable... sizes ) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException; + ArrayVariableFmi2Api createArray(String name,Class type, FmiBuilder.UIntVariable... sizes ) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException; @Override FmiBuilder.Variable store(FmiBuilder.Value tag); diff --git a/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/scoping/ScopeFmi2Api.java b/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/scoping/ScopeFmi2Api.java index 2edcc2ba..3bdab2c3 100644 --- a/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/scoping/ScopeFmi2Api.java +++ b/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/scoping/ScopeFmi2Api.java @@ -205,6 +205,17 @@ public IntVariableFmi2Api store(int value) { return store(() -> builder.getNameGenerator().getName(), value); } + @Override + public UIntVariableFmi2Api storeUInt(long value) { + return storeUInt(() -> builder.getNameGenerator().getName(), value); + } + + + @Override + public LongVariableFmi2Api store(long value) { + return store(() -> builder.getNameGenerator().getName(), value); + } + @Override public DoubleVariableFmi2Api store(String prefix, double value) { return store(() -> builder.getNameGenerator().getName(prefix), value); @@ -224,6 +235,10 @@ public BooleanVariableFmi2Api store(String name, boolean value) { public IntVariableFmi2Api store(String name, int value) { return store(() -> builder.getNameGenerator().getName(name), value); } + @Override + public UIntVariableFmi2Api storeUInt(String name, long value) { + return storeUInt(() -> builder.getNameGenerator().getName(name), value); + } @Override public ArrayVariableFmi2Api store(String name, V[] value) { @@ -236,6 +251,12 @@ public ArrayVariableFmi2Api createArray(String name, Class t return newArray(() -> builder.getNameGenerator().getName(name), type, sizes); } + @Override + public ArrayVariableFmi2Api createArray(String name, Class type, + FmiBuilder.UIntVariable... sizes) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException { + return newArray(() -> builder.getNameGenerator().getName(name), type, sizes); + } + public DoubleVariableFmi2Api store(Supplier nameProvider, double value) { String name = nameProvider.get(); @@ -264,6 +285,15 @@ public IntVariableFmi2Api store(Supplier nameProvider, int value) { newAIdentifierExp(name)); } + public UIntVariableFmi2Api storeUInt(Supplier nameProvider, long value) { + String name = nameProvider.get(); + AUIntLiteralExp initial = newAUIntLiteralExp(value); + PStm var = newVariable(name, newAIntNumericPrimitiveType(), initial); + add(var); + return new UIntVariableFmi2Api(var, this, builder.getDynamicScope(), newAIdentifierStateDesignator(newAIdentifier(name)), + newAIdentifierExp(name)); + } + public StringVariableFmi2Api store(Supplier nameProvider, String value) { String name = nameProvider.get(); AStringLiteralExp initial = newAStringLiteralExp(value); @@ -273,8 +303,15 @@ public StringVariableFmi2Api store(Supplier nameProvider, String value) newAIdentifierExp(name)); } - private ArrayVariableFmi2Api newArray(Supplier nameProvider, Class type, - FmiBuilder.IntVariable... sizes) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { + + private ArrayVariableFmi2Api newArray(Supplier nameProvider, Class type, + S... sizes) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { + for (FmiBuilder.Variable v : sizes) { + if (!(v instanceof FmiBuilder.IntVariable || v instanceof FmiBuilder.UIntVariable)) { + throw new IllegalArgumentException("only int and uint variables allowed"); + } + } + PType type_ = null; diff --git a/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/variables/InstanceVariableFmi3Api.java b/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/variables/InstanceVariableFmi3Api.java index df0df89b..e28ebcc1 100644 --- a/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/variables/InstanceVariableFmi3Api.java +++ b/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/variables/InstanceVariableFmi3Api.java @@ -363,13 +363,13 @@ public void enterStepMode(FmiBuilder.Scope scope) { } public void getEventIndicators(FmiBuilder.Scope scope, FmiBuilder.ArrayVariable> eventIndicators, - FmiBuilder.IntVariable nEventIndicators) { + FmiBuilder.UIntVariable nEventIndicators) { fmiCall(scope, "getEventIndicators", new ARefExp(eventIndicators.getExp().clone()), nEventIndicators.getExp().clone()); } - public void getNumberOfEventIndicators(FmiBuilder.Scope scope, FmiBuilder.IntVariable nEventIndicators) { + public void getNumberOfEventIndicators(FmiBuilder.Scope scope, FmiBuilder.UIntVariable nEventIndicators) { fmiCall(scope, "getNumberOfEventIndicators", new ARefExp(nEventIndicators.getExp().clone())); } diff --git a/interpreter/src/main/java/org/intocps/maestro/interpreter/external/ExternalReflectCallHelper.java b/interpreter/src/main/java/org/intocps/maestro/interpreter/external/ExternalReflectCallHelper.java index 5915b27c..0c36f6b3 100644 --- a/interpreter/src/main/java/org/intocps/maestro/interpreter/external/ExternalReflectCallHelper.java +++ b/interpreter/src/main/java/org/intocps/maestro/interpreter/external/ExternalReflectCallHelper.java @@ -373,11 +373,11 @@ public Object map(Value v) { } case Long: { var n = ((NumericValue) v); - return Integer.valueOf(n.intValue()).longValue(); + return n.realValue();// Integer.valueOf(n.intValue()).longValue(); } case Real: { var n = ((NumericValue) v); - return Integer.valueOf(n.intValue()).doubleValue(); + return n.doubleValue();//Integer.valueOf(n.intValue()).doubleValue(); } case Short: { var n = ((NumericValue) v); diff --git a/parser/src/main/java/org/intocps/maestro/parser/ParseTree2AstConverter.java b/parser/src/main/java/org/intocps/maestro/parser/ParseTree2AstConverter.java index 33b20337..31d49fd0 100644 --- a/parser/src/main/java/org/intocps/maestro/parser/ParseTree2AstConverter.java +++ b/parser/src/main/java/org/intocps/maestro/parser/ParseTree2AstConverter.java @@ -505,8 +505,6 @@ public INode visitLiteral(MablParser.LiteralContext ctx) { literal.setValue(Integer.parseInt(ctx.DECIMAL_LITERAL().getText())); return literal; } else if (ctx.FLOAT_LITERAL() != null) { - ARealLiteralExp literal = new ARealLiteralExp(); - literal.setValue(Double.parseDouble(ctx.FLOAT_LITERAL().getText())); AFloatLiteralExp literal = new AFloatLiteralExp(); literal.setValue(Float.parseFloat(ctx.FLOAT_LITERAL().getText())); return literal; diff --git a/typechecker/src/main/resources/org/intocps/maestro/typechecker/FMI3.mabl b/typechecker/src/main/resources/org/intocps/maestro/typechecker/FMI3.mabl index 339599ae..de41c33e 100644 --- a/typechecker/src/main/resources/org/intocps/maestro/typechecker/FMI3.mabl +++ b/typechecker/src/main/resources/org/intocps/maestro/typechecker/FMI3.mabl @@ -252,7 +252,7 @@ module FMI3Instance /* end::getDerivatives[] */ /* tag::getEventIndicators[] */ - int getEventIndicators(out uint[] eventIndicators, int nEventIndicators); + int getEventIndicators(out uint[] eventIndicators, uint nEventIndicators); /* end::getEventIndicators[] */ /* tag::getContinuousStates[] */ From 8827481e6f8be6930720057067ef0eb806ab82c3 Mon Sep 17 00:00:00 2001 From: kel Date: Thu, 5 Sep 2024 15:03:22 +0200 Subject: [PATCH 04/15] added missing part for float support --- .../scoping/DynamicActiveBuilderScope.java | 6 ++++ .../fmi2/api/mabl/scoping/ScopeFmi2Api.java | 29 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/scoping/DynamicActiveBuilderScope.java b/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/scoping/DynamicActiveBuilderScope.java index 3c9abd04..4a56701d 100644 --- a/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/scoping/DynamicActiveBuilderScope.java +++ b/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/scoping/DynamicActiveBuilderScope.java @@ -194,6 +194,12 @@ public IntVariableFmi2Api store(String name, int value) { public UIntVariableFmi2Api storeUInt(String name, long value) { return activeScope.storeUInt(name, value); } + + @Override + public FmiBuilder.LongVariable store(String name, long value) { + return activeScope.store(name,value); + } + /* @Override public , Var extends Fmi2Builder.Variable> Var store(String name, Var value) { return activeScope.store(name, value); diff --git a/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/scoping/ScopeFmi2Api.java b/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/scoping/ScopeFmi2Api.java index 3bdab2c3..3a2283a7 100644 --- a/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/scoping/ScopeFmi2Api.java +++ b/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/scoping/ScopeFmi2Api.java @@ -190,6 +190,11 @@ public DoubleVariableFmi2Api store(double value) { return store(() -> builder.getNameGenerator().getName(), value); } + @Override + public FloatVariableFmi2Api store(float value) { + return store(() -> builder.getNameGenerator().getName(), value); + } + @Override public StringVariableFmi2Api store(String value) { return store(() -> builder.getNameGenerator().getName(), value); @@ -221,6 +226,12 @@ public DoubleVariableFmi2Api store(String prefix, double value) { return store(() -> builder.getNameGenerator().getName(prefix), value); } + + @Override + public FloatVariableFmi2Api store(String prefix, float value) { + return store(() -> builder.getNameGenerator().getName(prefix), value); + } + @Override public StringVariableFmi2Api store(String prefix, String value) { return store(() -> builder.getNameGenerator().getName(prefix), value); @@ -267,6 +278,15 @@ public DoubleVariableFmi2Api store(Supplier nameProvider, double value) newAIdentifierExp(name)); } + public FloatVariableFmi2Api store(Supplier nameProvider, float value) { + String name = nameProvider.get(); + AFloatLiteralExp initial = newAFloatLiteralExp(value); + PStm var = newVariable(name, newAFloatNumericPrimitiveType(), initial); + add(var); + return new FloatVariableFmi2Api(var, this, builder.getDynamicScope(), newAIdentifierStateDesignator(newAIdentifier(name)), + newAIdentifierExp(name)); + } + public BooleanVariableFmi2Api store(Supplier nameProvider, boolean value) { String name = nameProvider.get(); ABoolLiteralExp initial = newABoolLiteralExp(value); @@ -294,6 +314,15 @@ public UIntVariableFmi2Api storeUInt(Supplier nameProvider, long value) newAIdentifierExp(name)); } + public LongVariableFmi2Api store(Supplier nameProvider, long value) { + String name = nameProvider.get(); + ALongLiteralExp initial = newALongLiteralExp(value); + PStm var = newVariable(name, newALongNumericPrimitiveType(), initial); + add(var); + return new LongVariableFmi2Api(var, this, builder.getDynamicScope(), newAIdentifierStateDesignator(newAIdentifier(name)), + newAIdentifierExp(name)); + } + public StringVariableFmi2Api store(Supplier nameProvider, String value) { String name = nameProvider.get(); AStringLiteralExp initial = newAStringLiteralExp(value); From 1d5f2d918d829209c32120ca40096af40d32aa63 Mon Sep 17 00:00:00 2001 From: kel Date: Thu, 5 Sep 2024 15:03:47 +0200 Subject: [PATCH 05/15] fixed missing deref for array creation --- .../main/java/org/intocps/maestro/interpreter/Interpreter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interpreter/src/main/java/org/intocps/maestro/interpreter/Interpreter.java b/interpreter/src/main/java/org/intocps/maestro/interpreter/Interpreter.java index f316f3b5..968d1ec5 100644 --- a/interpreter/src/main/java/org/intocps/maestro/interpreter/Interpreter.java +++ b/interpreter/src/main/java/org/intocps/maestro/interpreter/Interpreter.java @@ -202,7 +202,7 @@ public Value caseANotEqualBinaryExp(ANotEqualBinaryExp node, Context question) t public ArrayValue createArrayValue(List sizes, PType type, Context question) throws AnalysisException { List arrayValues = new ArrayList<>(); - for (int i = 0; i < ((NumericValue) sizes.get(0).apply(this, question)).intValue(); i++) { + for (int i = 0; i < ((NumericValue) sizes.get(0).apply(this, question).deref()).intValue(); i++) { if (sizes.size() > 1) { List nextSizes = sizes.subList(1, sizes.size()); // Call recursively From 84fe32bdb72377a0b0407d84b11e1828b092f587 Mon Sep 17 00:00:00 2001 From: kel Date: Thu, 5 Sep 2024 15:04:13 +0200 Subject: [PATCH 06/15] fixed wrong args for fmi3 setDebugLogging --- .../fmi2/api/mabl/variables/InstanceVariableFmi3Api.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/variables/InstanceVariableFmi3Api.java b/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/variables/InstanceVariableFmi3Api.java index e28ebcc1..90b746b4 100644 --- a/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/variables/InstanceVariableFmi3Api.java +++ b/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/variables/InstanceVariableFmi3Api.java @@ -268,7 +268,7 @@ public void setDebugLogging(List categories, boolean enableLogging) { ALocalVariableStm callStm = newALocalVariableStm(newAVariableDeclaration(statusIdentifier, newAIntNumericPrimitiveType(), newAExpInitializer( newACallExp(newAIdentifierExp(name), newAIdentifier(method), - Arrays.asList(newABoolLiteralExp(enableLogging), MableAstFactory.newAIntLiteralExp(categories.size()), + Arrays.asList(newABoolLiteralExp(enableLogging), MableAstFactory.newAIdentifierExp(arrayName)))))); scope.add(arrayContent, callStm); From 5bceaccb6bde7894e57ca946e54d3f809e4e265a Mon Sep 17 00:00:00 2001 From: kel Date: Thu, 12 Sep 2024 13:36:05 +0200 Subject: [PATCH 07/15] added new example with periodic clocks --- .../extensions/fmi3/Fmi3Interpreter.java | 29 +-- .../external/ExternalReflectCallHelper.java | 30 ++++ .../intocps/maestro/fmi3/BuilderFmi3Test.java | 166 +++++++++++------- .../test/resources/fmi3/periodic_clock.fmu | Bin 0 -> 64712 bytes .../test/resources/fmi3/sinewave_array.fmu | Bin 0 -> 23961 bytes 5 files changed, 154 insertions(+), 71 deletions(-) create mode 100644 maestro/src/test/resources/fmi3/periodic_clock.fmu create mode 100644 maestro/src/test/resources/fmi3/sinewave_array.fmu diff --git a/interpreter/src/main/java/org/intocps/maestro/interpreter/extensions/fmi3/Fmi3Interpreter.java b/interpreter/src/main/java/org/intocps/maestro/interpreter/extensions/fmi3/Fmi3Interpreter.java index 5e606862..5743b9d6 100644 --- a/interpreter/src/main/java/org/intocps/maestro/interpreter/extensions/fmi3/Fmi3Interpreter.java +++ b/interpreter/src/main/java/org/intocps/maestro/interpreter/extensions/fmi3/Fmi3Interpreter.java @@ -78,6 +78,10 @@ public class Fmi3Interpreter { static final ExternalReflectCallHelper.ArgMapping intArrayOutArgMapper = new ExternalReflectCallHelper.ArgMapping(TP.Int, 2, ExternalReflectCallHelper.ArgMapping.InOut.Output, null); + static final ExternalReflectCallHelper.ArgMapping boolOutArgMapper = new ExternalReflectCallHelper.ArgMapping(TP.Bool, 1, + ExternalReflectCallHelper.ArgMapping.InOut.Output, null); + static final ExternalReflectCallHelper.ArgMapping doubleOutArgMapper = new ExternalReflectCallHelper.ArgMapping(TP.Real, 1, + ExternalReflectCallHelper.ArgMapping.InOut.Output, null); final static Logger logger = LoggerFactory.getLogger(Interpreter.class); private final File workingDirectory; @@ -529,18 +533,19 @@ private static Value getFmuInstanceValue(BufferedOutputStream fmuLogOutputStream })); functions.put("updateDiscreteStates", new FunctionValue.ExternalFunctionValue(fcargs -> { -// int updateDiscreteStates(out bool[] discreteStatesNeedUpdate, out bool[] terminateSimulation, -// out bool[] nominalsOfContinuousStatesChanged, out bool[] valuesOfContinuousStatesChanged, out bool[] nextEventTimeDefined, -// out real[] nextEventTime); +// int updateDiscreteStates(out bool discreteStatesNeedUpdate, out bool terminateSimulation, +// out bool nominalsOfContinuousStatesChanged, out bool valuesOfContinuousStatesChanged, out bool nextEventTimeDefined, +// out real nextEventTime); checkArgLength(fcargs, 6); try { FmuResult res = instance.updateDiscreteStates(); - boolArrayOutArgMapper.mapOut(fcargs.get(0), new boolean[]{res.result.isDiscreteStatesNeedUpdate()}); - boolArrayOutArgMapper.mapOut(fcargs.get(1), new boolean[]{res.result.isTerminateSimulation()}); - boolArrayOutArgMapper.mapOut(fcargs.get(2), new boolean[]{res.result.isNominalsOfContinuousStatesChanged()}); - boolArrayOutArgMapper.mapOut(fcargs.get(3), new boolean[]{res.result.isValuesOfContinuousStatesChanged()}); - boolArrayOutArgMapper.mapOut(fcargs.get(4), new boolean[]{res.result.isNextEventTimeDefined()}); - doubleArrayOutArgMapper.mapOut(fcargs.get(5), new double[]{res.result.getNextEventTime()}); + + boolOutArgMapper.mapOut(fcargs.get(0), res.result.isDiscreteStatesNeedUpdate()); + boolOutArgMapper.mapOut(fcargs.get(1), res.result.isTerminateSimulation()); + boolOutArgMapper.mapOut(fcargs.get(2), res.result.isNominalsOfContinuousStatesChanged()); + boolOutArgMapper.mapOut(fcargs.get(3), res.result.isValuesOfContinuousStatesChanged()); + boolOutArgMapper.mapOut(fcargs.get(4), res.result.isNextEventTimeDefined()); + doubleOutArgMapper.mapOut(fcargs.get(5), res.result.getNextEventTime()); return status2IntValue(res.status); } catch (FmuInvocationException e) { throw new InterpreterException(e); @@ -691,7 +696,11 @@ private static Value getFmuInstanceValue(BufferedOutputStream fmuLogOutputStream checkArgLength(fcargs, 1); try { FmuResult res = instance.getNumberOfEventIndicators(); - intArrayOutArgMapper.mapOut(fcargs.get(0), res.result); + final ExternalReflectCallHelper.ArgMapping uintOutArgMapper = new ExternalReflectCallHelper.ArgMapping(TP.Long, 1, + ExternalReflectCallHelper.ArgMapping.InOut.Output, null); + uintOutArgMapper.mapOut(fcargs.get(0), res.result); + UpdatableValue v= (UpdatableValue) fcargs.get(0); + v.setValue(new UnsignedIntegerValue(((LongValue)v.deref()).getValue())); return status2IntValue(res.status); } catch (FmuInvocationException e) { throw new InterpreterException(e); diff --git a/interpreter/src/main/java/org/intocps/maestro/interpreter/external/ExternalReflectCallHelper.java b/interpreter/src/main/java/org/intocps/maestro/interpreter/external/ExternalReflectCallHelper.java index 0c36f6b3..cd6025b9 100644 --- a/interpreter/src/main/java/org/intocps/maestro/interpreter/external/ExternalReflectCallHelper.java +++ b/interpreter/src/main/java/org/intocps/maestro/interpreter/external/ExternalReflectCallHelper.java @@ -469,6 +469,36 @@ public void mapOut(Value original, Object value) { break; } ref.setValue(new ArrayValue<>(values)); + }else if(dimension==1) + { + Value mappedValue = null; + switch (type){ + case Bool: + mappedValue=new BooleanValue(value.getClass().isPrimitive()?(boolean)value:(Boolean)value); + break; + case Byte: + mappedValue=new ByteValue(value.getClass().isPrimitive()?(byte)value:(Byte)value); + break; + case Float: + mappedValue=new FloatValue(value.getClass().isPrimitive()?(float)value:(Float)value); + break; + case Int: + mappedValue=new IntegerValue(value.getClass().isPrimitive()?(int)value:(Integer)value); + break; + case Long: + mappedValue=new LongValue(value.getClass().isPrimitive()?(long)value:(Long)value); + break; + case Real: + mappedValue=new RealValue(value.getClass().isPrimitive()?(double)value:(Double)value); + break; + case Short: + mappedValue=new ShortValue(value.getClass().isPrimitive()?(short)value:(Short)value); + break; + case String: + mappedValue=new StringValue((String)value); + break; + } + ref.setValue(mappedValue); } } diff --git a/maestro/src/test/java/org/intocps/maestro/fmi3/BuilderFmi3Test.java b/maestro/src/test/java/org/intocps/maestro/fmi3/BuilderFmi3Test.java index 5cbe0154..acf4b22b 100644 --- a/maestro/src/test/java/org/intocps/maestro/fmi3/BuilderFmi3Test.java +++ b/maestro/src/test/java/org/intocps/maestro/fmi3/BuilderFmi3Test.java @@ -3,7 +3,6 @@ import org.antlr.v4.runtime.CharStreams; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; -import org.intocps.fmi.FmuInvocationException; import org.intocps.fmi.jnifmuapi.fmi3.Fmu3; import org.intocps.maestro.Mabl; import org.intocps.maestro.ast.display.PrettyPrinter; @@ -11,14 +10,12 @@ import org.intocps.maestro.core.Framework; import org.intocps.maestro.core.messages.ErrorReporter; import org.intocps.maestro.core.messages.IErrorReporter; -import org.intocps.maestro.fmi.fmi3.Fmi3Causality; -import org.intocps.maestro.fmi.fmi3.Fmi3ModelDescription; -import org.intocps.maestro.fmi.fmi3.Fmi3TypeEnum; +import org.intocps.maestro.fmi.fmi3.*; import org.intocps.maestro.framework.fmi2.api.FmiBuilder; +import org.intocps.maestro.framework.fmi2.api.mabl.LoggerFmi2Api; import org.intocps.maestro.framework.fmi2.api.mabl.MablApiBuilder; import org.intocps.maestro.framework.fmi2.api.mabl.PortFmi3Api; import org.intocps.maestro.framework.fmi2.api.mabl.scoping.DynamicActiveBuilderScope; -import org.intocps.maestro.framework.fmi2.api.mabl.scoping.IfMaBlScope; import org.intocps.maestro.framework.fmi2.api.mabl.values.BooleanExpressionValue; import org.intocps.maestro.framework.fmi2.api.mabl.values.IntExpressionValue; import org.intocps.maestro.framework.fmi2.api.mabl.variables.*; @@ -29,6 +26,8 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledOnOs; +import org.junit.jupiter.api.condition.OS; import java.io.File; import java.io.IOException; @@ -39,6 +38,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.function.Supplier; import java.util.stream.Collectors; public class BuilderFmi3Test { @@ -48,7 +48,7 @@ public static void before() throws IOException { } - public static InstanceVariableFmi3Api createInstance(MablApiBuilder builder, String name, URI uri) throws Exception { + public static InstanceVariableFmi3Api createInstance(MablApiBuilder builder, String name, URI uri, boolean eventModeUsed) throws Exception { Fmi3ModelDescription md = new Fmi3ModelDescription(new Fmu3(new File(uri)).getModelDescription()); @@ -56,7 +56,7 @@ public static InstanceVariableFmi3Api createInstance(MablApiBuilder builder, Str boolean visible = true; boolean loggingOn = true; - boolean eventModeUsed = true; + boolean earlyReturnAllowed = true; ArrayVariableFmi2Api requiredIntermediateVariables = builder.getDynamicScope().store("requiredIntermediateVariables", new Long[]{1L}); InstanceVariableFmi3Api instance = @@ -70,9 +70,9 @@ public void test() throws Exception { MablApiBuilder builder = new MablApiBuilder(); InstanceVariableFmi3Api fd = createInstance(builder, "fd", - new File("target/Fmi3ModuleReferenceFmusTest/cache/Feedthrough.fmu").getAbsoluteFile().toURI()); + new File("target/Fmi3ModuleReferenceFmusTest/cache/Feedthrough.fmu").getAbsoluteFile().toURI(), false); InstanceVariableFmi3Api sg = createInstance(builder, "sg", - new File("src/test/resources/fmi3/reference/siggen-feedthrough/SignalGenerator.fmu").getAbsoluteFile().toURI()); + new File("src/test/resources/fmi3/reference/siggen-feedthrough/SignalGenerator.fmu").getAbsoluteFile().toURI(), false); // fd.enterInitializationMode(false, 0.0, 0.0, true, 10.0); @@ -113,8 +113,9 @@ public void testClocks() throws Exception { DynamicActiveBuilderScope scope = builder.getDynamicScope(); InstanceVariableFmi3Api instance = createInstance(builder, "clocks", - new File("target/Fmi3ModuleReferenceFmusTest/cache/Clocks.fmu").getAbsoluteFile().toURI()); + new File("src/test/resources/fmi3/sinewave_array.fmu").getAbsoluteFile().toURI(), true); + //we are not in event mode as eventModeUsed was true // fd.enterInitializationMode(false, 0.0, 0.0, true, 10.0); // sg.enterInitializationMode(false, 0.0, 0.0, true, 10.0); @@ -129,7 +130,7 @@ public void testClocks() throws Exception { instance.enterEventMode(); - FmiBuilder.IntVariable nEventIndicators = builder.getDynamicScope().store(0); + FmiBuilder.UIntVariable nEventIndicators = builder.getDynamicScope().storeUInt(0); instance.getNumberOfEventIndicators(builder.getDynamicScope(), nEventIndicators); @@ -198,13 +199,17 @@ public void testClocks() throws Exception { } @Test + @EnabledOnOs(OS.LINUX) public void testSimulateClocks() throws Exception { MablApiBuilder builder = new MablApiBuilder(); DynamicActiveBuilderScope scope = builder.getDynamicScope(); + var log = builder.getLogger(); - InstanceVariableFmi3Api instance = createInstance(builder, "clocks", - new File("target/Fmi3ModuleReferenceFmusTest/cache/Clocks.fmu").getAbsoluteFile().toURI()); + InstanceVariableFmi3Api instance = createInstance(builder, "i", + new File("src/test/resources/fmi3/periodic_clock.fmu").getAbsoluteFile().toURI(), true); + //we are not in event mode as eventModeUsed was true + log.log(LoggerFmi2Api.Level.INFO, "Instantiated"); // fd.enterInitializationMode(false, 0.0, 0.0, true, 10.0); // sg.enterInitializationMode(false, 0.0, 0.0, true, 10.0); @@ -217,17 +222,22 @@ public void testSimulateClocks() throws Exception { sgOutputs.stream().map(PortFmi3Api::getName).forEach(System.out::println); - instance.enterEventMode(); +// log.log(LoggerFmi2Api.Level.INFO,"Enter event mode again"); +// instance.enterEventMode(); - FmiBuilder.IntVariable nEventIndicators = builder.getDynamicScope().store(0); - instance.getNumberOfEventIndicators(builder.getDynamicScope(), nEventIndicators); - - ArrayVariableFmi2Api eventIndicators = builder.getDynamicScope() - .createArray("eventIndicators", UIntVariableFmi2Api.class, nEventIndicators); - -// FmiBuilder.ArrayVariable eventIndicators=builder.getDynamicScope().store(builder.n).storeInArray(); - instance.getEventIndicators(scope, eventIndicators, nEventIndicators); +// FmiBuilder.UIntVariable nEventIndicators = builder.getDynamicScope().storeUInt(0L); +// +// log.log(LoggerFmi2Api.Level.INFO,"getNumberOfEventIndicators"); +// instance.getNumberOfEventIndicators(builder.getDynamicScope(), nEventIndicators); +// +// +// ArrayVariableFmi2Api eventIndicators = builder.getDynamicScope() +// .createArray("eventIndicators", UIntVariableFmi2Api.class, nEventIndicators); +// +//// FmiBuilder.ArrayVariable eventIndicators=builder.getDynamicScope().store(builder.n).storeInArray(); +// log.log(LoggerFmi2Api.Level.INFO,"getEventIndicators"); +// instance.getEventIndicators(scope, eventIndicators, nEventIndicators); List outClocks = clocks.stream().filter(p -> p.scalarVariable.getVariable().getCausality() == Fmi3Causality.Output) @@ -238,14 +248,16 @@ public void testSimulateClocks() throws Exception { vrs.setValue(new IntExpressionValue(i), new IntExpressionValue((int) outClocks.get(i).scalarVariable.getVariable().getValueReferenceAsLong())); } ArrayVariableFmi2Api triggeredClocks = scope.createArray("clock_get_vrs", BooleanVariableFmi2Api.class, nvr); + //this shows us the clock state of the output clocks + log.log(LoggerFmi2Api.Level.INFO, "getClock"); instance.getClock(vrs, nvr, triggeredClocks); List inClocks = clocks.stream().filter(p -> p.scalarVariable.getVariable().getCausality() == Fmi3Causality.Input) .collect(Collectors.toList()); FmiBuilder.IntVariable clock_set_nvr = scope.store("clock_in_nvr", inClocks.size()); - ArrayVariableFmi2Api clock_set_vrs = scope.createArray("clock_in_vrs", UIntVariableFmi2Api.class, nvr); - ArrayVariableFmi2Api clock_set_Clocks = scope.createArray("clock_in_vrs", BooleanVariableFmi2Api.class, nvr); + ArrayVariableFmi2Api clock_set_vrs = scope.createArray("clock_in_vrs", UIntVariableFmi2Api.class, clock_set_nvr); + ArrayVariableFmi2Api clock_set_Clocks = scope.createArray("clock_in_vrs_values", BooleanVariableFmi2Api.class, clock_set_nvr); for (int i = 0; i < inClocks.size(); i++) { clock_set_vrs.setValue(new IntExpressionValue(i), new IntExpressionValue((int) inClocks.get(i).scalarVariable.getVariable().getValueReferenceAsLong())); @@ -253,51 +265,81 @@ public void testSimulateClocks() throws Exception { } instance.setClock(clock_set_vrs, clock_set_nvr, clock_set_Clocks); + instance.setDebugLogging(instance.getModelDescription().getLogCategories().stream().map(lc -> lc.getName()).collect(Collectors.toList()), true); - FmiBuilder.DoubleVariable currentCommunicationPoint = scope.store("time", 0d); - FmiBuilder.DoubleVariable stepSize = scope.store("step", 0.1d); - Map.Entry, InstanceVariableFmi3Api.StepResult> stepRes = instance.step(scope, currentCommunicationPoint, - stepSize, new ABoolLiteralExp(false)); + //initialize + instance.enterInitializationMode(false, 0.0, 0.0, true, 10d); - IfMaBlScope eventHandlingScope = scope.enterIf( - stepRes.getValue().getEventHandlingNeeded().toPredicate()); + instance.exitInitializationMode(); - instance.enterEventMode(); + //prepare for event handling + BooleanVariableFmi2Api stopSimulation = scope.store(false); + + BooleanVariableFmi2Api discreteStatesNeedUpdate = scope.store("discreteStatesNeedUpdate", true); + BooleanVariableFmi2Api terminateSimulation = scope.store("terminateSimulation", false); + BooleanVariableFmi2Api nominalsOfContinuousStatesChanged = scope.store("nominalsOfContinuousStatesChanged", false); + BooleanVariableFmi2Api valuesOfContinuousStatesChanged = scope.store("valuesOfContinuousStatesChanged", false); + BooleanVariableFmi2Api nextEventTimeDefined = scope.store("nextEventTimeDefined", false); + DoubleVariableFmi2Api nextEventTime = scope.store("nextEventTime", 0d); + + Supplier updateDiscreteStates=()->{ + scope.enterWhile(discreteStatesNeedUpdate.toPredicate()); + instance.updateDiscreteStates(scope, discreteStatesNeedUpdate, terminateSimulation, nominalsOfContinuousStatesChanged, valuesOfContinuousStatesChanged, + nextEventTimeDefined, nextEventTime); -//handle events - BooleanVariableFmi2Api discreteStatesNeedUpdate = scope.store("discreteStatesNeedUpdate",false); - BooleanVariableFmi2Api terminateSimulation = scope.store("terminateSimulation",false); - BooleanVariableFmi2Api nominalsOfContinuousStatesChanged = scope.store("nominalsOfContinuousStatesChanged",false); - BooleanVariableFmi2Api valuesOfContinuousStatesChanged = scope.store("valuesOfContinuousStatesChanged",false); - BooleanVariableFmi2Api nextEventTimeDefined = scope.store("nextEventTimeDefined",false); - DoubleVariableFmi2Api nextEventTime = scope.store("nextEventTime",0d); - instance.updateDiscreteStates(scope, discreteStatesNeedUpdate, terminateSimulation, nominalsOfContinuousStatesChanged, valuesOfContinuousStatesChanged, - nextEventTimeDefined, nextEventTime); + scope.enterIf(terminateSimulation.toPredicate()); + stopSimulation.setValue(scope,BooleanExpressionValue.of(true)); + scope.add(new ABreakStm()); + scope.leave(); + scope.leave(); + return scope; + }; + + //handle initial events + updateDiscreteStates.get(); + + //switch to step mode instance.enterStepMode(); + List periodicConstInClocks = clocks.stream().filter(p -> p.scalarVariable.getVariable().getCausality() == Fmi3Causality.Input && p.scalarVariable.getVariable() instanceof ClockVariable && ((ClockVariable) p.scalarVariable.getVariable()).getInterval()== Fmi3ClockInterval.Constant) + .collect(Collectors.toList()); + List periodicConstInClocksInterval = clocks.stream().filter(p -> p.scalarVariable.getVariable().getCausality() == Fmi3Causality.Input && p.scalarVariable.getVariable() instanceof ClockVariable && ((ClockVariable) p.scalarVariable.getVariable()).getInterval()== Fmi3ClockInterval.Constant) + .map(p->((ClockVariable) p.scalarVariable.getVariable()).getIntervalDecimal()) .collect(Collectors.toList()); -// stepRes.getValue().getLastSuccessfulTime() + List constantPeriodicClocks = clocks.stream().filter(p -> p.scalarVariable.getVariable() + .getCausality() == Fmi3Causality.Input && p.scalarVariable.getVariable() instanceof ClockVariable && ((ClockVariable) p.scalarVariable.getVariable()).getInterval() == Fmi3ClockInterval.Constant) + .collect( + Collectors.toList()); -// instance.g -// for (PortFmi3Api o : sgOutputs) { -// sg.get(o); -// } + DoubleVariableFmi2Api currentCommunicationPoint = scope.store("time", 0d); + DoubleVariableFmi2Api endTime = scope.store(4d); -// System.out.println("Linked ports"); -// sg.getPorts().stream().filter(PortFmi3Api::isLinked).forEach(System.out::println); -// System.out.println("---Linked ports"); -// sg.getPort("Int8_output").linkTo(fd.getPort("Int8_input")); -// sg.getPort("UInt8_output").linkTo(fd.getPort("UInt8_input")); -// System.out.println("Linked ports"); -// sg.getPorts().stream().filter(PortFmi3Api::isLinked).forEach(System.out::println); -// System.out.println("---Linked ports"); -// sg.getAndShare(); -// fd.setLinked(); + //loop for co-simulation + scope.enterWhile(terminateSimulation.toPredicate().not().and(currentCommunicationPoint.toMath().lessThan(endTime))); -// fd.exitInitializationMode(); + //determine a step size + DoubleVariableFmi2Api stepSize = scope.store("stepSize", Collections.min(periodicConstInClocksInterval)); + + +// //step the instance + Map.Entry, InstanceVariableFmi3Api.StepResult> stepRes = instance.step(scope, currentCommunicationPoint, + stepSize, new ABoolLiteralExp(false)); + + currentCommunicationPoint.setValue(currentCommunicationPoint.toMath().addition(stepSize)); + + //handle events of required + scope.enterIf( + stepRes.getValue().getEventHandlingNeeded().toPredicate()); + instance.enterEventMode(); + updateDiscreteStates.get(); + + //exit to step mode + instance.enterStepMode(); + + //program done; ASimulationSpecificationCompilationUnit program = builder.build(); @@ -305,15 +347,15 @@ public void testSimulateClocks() throws Exception { System.out.println(PrettyPrinter.printLineNumbers(program)); - File workingDirectory=getWorkingDirectory(null,this.getClass()); + File workingDirectory = getWorkingDirectory(null, this.getClass()); File specFile = new File(workingDirectory, "spec.mabl"); - FileUtils.write(specFile,PrettyPrinter.print(program),StandardCharsets.UTF_8); + FileUtils.write(specFile, PrettyPrinter.print(program), StandardCharsets.UTF_8); IErrorReporter reporter = new ErrorReporter(); Mabl mabl = new Mabl(workingDirectory, workingDirectory); mabl.setReporter(reporter); // mabl.setVerbose(getMablVerbose()); -mabl.parse(Collections.singletonList(specFile)); + mabl.parse(Collections.singletonList(specFile)); mabl.expand(); var tcRes = mabl.typeCheck(); mabl.verify(Framework.FMI2); @@ -331,10 +373,12 @@ public void testSimulateClocks() throws Exception { Map types = tcRes.getValue(); new MableInterpreter(new DefaultExternalValueFactory(workingDirectory, name -> TypeChecker.findModule(types, name), - IOUtils.toInputStream(mabl.getRuntimeDataAsJsonString(), StandardCharsets.UTF_8))).execute(MablParserUtil.parse(CharStreams.fromString(PrettyPrinter.print(program)))); + IOUtils.toInputStream(mabl.getRuntimeDataAsJsonString(), StandardCharsets.UTF_8))).execute( + MablParserUtil.parse(CharStreams.fromString(PrettyPrinter.print(program)))); } + static File getWorkingDirectory(File base, Class cls) throws IOException { - String s = Paths.get("target", cls.getSimpleName()).toString() + File.separatorChar + (base==null?"":base.getAbsolutePath().substring( + String s = Paths.get("target", cls.getSimpleName()).toString() + File.separatorChar + (base == null ? "" : base.getAbsolutePath().substring( base.getAbsolutePath().replace(File.separatorChar, '/').indexOf("src/test/resources/") + ("src" + "/test" + "/resources/").length())); File workingDir = new File(s.replace('/', File.separatorChar)); diff --git a/maestro/src/test/resources/fmi3/periodic_clock.fmu b/maestro/src/test/resources/fmi3/periodic_clock.fmu new file mode 100644 index 0000000000000000000000000000000000000000..b71c3c94ab162aa4abe6ac8e75a99b0568be0638 GIT binary patch literal 64712 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 Tn}>aEJMgy;=#UCr1%dt#^${tM literal 0 HcmV?d00001 diff --git a/maestro/src/test/resources/fmi3/sinewave_array.fmu b/maestro/src/test/resources/fmi3/sinewave_array.fmu new file mode 100644 index 0000000000000000000000000000000000000000..0d91d66714ac196823f708777ffb1b243c05f752 GIT binary patch literal 23961 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 me+4}P`QNCHdg32T+vA@* Date: Mon, 16 Sep 2024 11:46:47 +0200 Subject: [PATCH 08/15] removed long support commit which should not have been committed here --- .../maestro/framework/fmi2/api/FmiBuilder.java | 2 -- .../mabl/scoping/DynamicActiveBuilderScope.java | 9 +-------- .../fmi2/api/mabl/scoping/ScopeFmi2Api.java | 14 ++------------ 3 files changed, 3 insertions(+), 22 deletions(-) diff --git a/frameworks/builder/src/main/java/org/intocps/maestro/framework/fmi2/api/FmiBuilder.java b/frameworks/builder/src/main/java/org/intocps/maestro/framework/fmi2/api/FmiBuilder.java index 5fe95acc..c641ffa7 100644 --- a/frameworks/builder/src/main/java/org/intocps/maestro/framework/fmi2/api/FmiBuilder.java +++ b/frameworks/builder/src/main/java/org/intocps/maestro/framework/fmi2/api/FmiBuilder.java @@ -242,7 +242,6 @@ interface Scope extends Scoping { IntVariable store(int value); UIntVariable storeUInt(long value); - LongVariable store(long value); /** * Store a given value with a prefix name * @@ -259,7 +258,6 @@ interface Scope extends Scoping { IntVariable store(String name, int value); UIntVariable storeUInt(String name, long value); - LongVariable store(String name, long value); ArrayVariable store(String name, CV[] value); ArrayVariable createArray(String name,Class type, IntVariable...sizes ) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException; diff --git a/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/scoping/DynamicActiveBuilderScope.java b/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/scoping/DynamicActiveBuilderScope.java index 4a56701d..45b0a60a 100644 --- a/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/scoping/DynamicActiveBuilderScope.java +++ b/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/scoping/DynamicActiveBuilderScope.java @@ -158,10 +158,6 @@ public UIntVariableFmi2Api storeUInt(long value) { } - @Override - public FmiBuilder.LongVariable store(long value) { - return activeScope.store(value); - } @Override public DoubleVariableFmi2Api store(String name, double value) { @@ -195,10 +191,7 @@ public UIntVariableFmi2Api storeUInt(String name, long value) { return activeScope.storeUInt(name, value); } - @Override - public FmiBuilder.LongVariable store(String name, long value) { - return activeScope.store(name,value); - } + /* @Override public , Var extends Fmi2Builder.Variable> Var store(String name, Var value) { diff --git a/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/scoping/ScopeFmi2Api.java b/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/scoping/ScopeFmi2Api.java index 3a2283a7..bd133776 100644 --- a/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/scoping/ScopeFmi2Api.java +++ b/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/scoping/ScopeFmi2Api.java @@ -216,10 +216,7 @@ public UIntVariableFmi2Api storeUInt(long value) { } - @Override - public LongVariableFmi2Api store(long value) { - return store(() -> builder.getNameGenerator().getName(), value); - } + @Override public DoubleVariableFmi2Api store(String prefix, double value) { @@ -314,14 +311,7 @@ public UIntVariableFmi2Api storeUInt(Supplier nameProvider, long value) newAIdentifierExp(name)); } - public LongVariableFmi2Api store(Supplier nameProvider, long value) { - String name = nameProvider.get(); - ALongLiteralExp initial = newALongLiteralExp(value); - PStm var = newVariable(name, newALongNumericPrimitiveType(), initial); - add(var); - return new LongVariableFmi2Api(var, this, builder.getDynamicScope(), newAIdentifierStateDesignator(newAIdentifier(name)), - newAIdentifierExp(name)); - } + public StringVariableFmi2Api store(Supplier nameProvider, String value) { String name = nameProvider.get(); From ad798d72d06ca803dde9f3f17db5e67a6fc7d60e Mon Sep 17 00:00:00 2001 From: kel Date: Mon, 16 Sep 2024 11:47:14 +0200 Subject: [PATCH 09/15] fixed issue with long generating real value --- .../maestro/interpreter/external/ExternalReflectCallHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interpreter/src/main/java/org/intocps/maestro/interpreter/external/ExternalReflectCallHelper.java b/interpreter/src/main/java/org/intocps/maestro/interpreter/external/ExternalReflectCallHelper.java index cd6025b9..a4aa89d7 100644 --- a/interpreter/src/main/java/org/intocps/maestro/interpreter/external/ExternalReflectCallHelper.java +++ b/interpreter/src/main/java/org/intocps/maestro/interpreter/external/ExternalReflectCallHelper.java @@ -373,7 +373,7 @@ public Object map(Value v) { } case Long: { var n = ((NumericValue) v); - return n.realValue();// Integer.valueOf(n.intValue()).longValue(); + return n.longValue();// Integer.valueOf(n.intValue()).longValue(); } case Real: { var n = ((NumericValue) v); From 4e1702b8a8f18c0bb9c7c38b9789b38944446672 Mon Sep 17 00:00:00 2001 From: kel Date: Mon, 16 Sep 2024 11:47:58 +0200 Subject: [PATCH 10/15] fixed issue in the interpreter where values assigned in decls were not converted to the defined type --- .../java/org/intocps/maestro/interpreter/Interpreter.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interpreter/src/main/java/org/intocps/maestro/interpreter/Interpreter.java b/interpreter/src/main/java/org/intocps/maestro/interpreter/Interpreter.java index 968d1ec5..77f3cdd8 100644 --- a/interpreter/src/main/java/org/intocps/maestro/interpreter/Interpreter.java +++ b/interpreter/src/main/java/org/intocps/maestro/interpreter/Interpreter.java @@ -282,17 +282,17 @@ public Value caseAVariableDeclaration(AVariableDeclaration node, Context questio Value initialValue = node.getInitializer() == null ? new UndefinedValue() : node.getInitializer().apply(this, question); - if (initialValue.isNumeric()) { + if (initialValue.deref().isNumeric()) { Class targetValueType = typeValueMappings.get(node.getType().getClass()); if (targetValueType != null) { //we need to upcast all values explicitly here - NumericValue upcasted = ((NumericValue) initialValue).upCast((Class) targetValueType); + NumericValue upcasted = ((NumericValue) initialValue.deref()).upCast((Class) targetValueType); if (upcasted == null) { throw new InterpreterException( - String.format("Initializer value could not be upcasted. In ", initialValue.toString()) + "initializer is " + + String.format("Initializer value could not be upcasted. In ", initialValue.deref().toString()) + "initializer is " + "specified: " + node.getName() + " in " + node); } initialValue = upcasted; From 43d112d3077567df6eedf63aa71ed7486834e32d Mon Sep 17 00:00:00 2001 From: kel Date: Mon, 16 Sep 2024 11:48:14 +0200 Subject: [PATCH 11/15] added support for float and test --- .../interpreter/values/FloatValue.java | 4 ++ .../org/intocps/maestro/FloatDeclTest.java | 69 +++++++++++++++++++ .../parser/ParseTree2AstConverter.java | 4 +- .../maestro/typechecker/TypeCheckVisitor.java | 7 +- 4 files changed, 81 insertions(+), 3 deletions(-) create mode 100644 maestro/src/test/java/org/intocps/maestro/FloatDeclTest.java diff --git a/interpreter/src/main/java/org/intocps/maestro/interpreter/values/FloatValue.java b/interpreter/src/main/java/org/intocps/maestro/interpreter/values/FloatValue.java index 1fdcde66..3094f6c7 100644 --- a/interpreter/src/main/java/org/intocps/maestro/interpreter/values/FloatValue.java +++ b/interpreter/src/main/java/org/intocps/maestro/interpreter/values/FloatValue.java @@ -68,6 +68,10 @@ public int compareTo(Value value) { FloatValue ro = (FloatValue) other; return (int) Math.round(Math.signum(this.value - ro.getValue())); + } if (other instanceof RealValue) { + RealValue ro = (RealValue) other; + return (int) Math.round(Math.signum(this.value - ro.getValue())); + } else if (other instanceof IntegerValue) { IntegerValue ro = (IntegerValue) other; return (int) Math.round(Math.signum(this.value - ro.getValue())); diff --git a/maestro/src/test/java/org/intocps/maestro/FloatDeclTest.java b/maestro/src/test/java/org/intocps/maestro/FloatDeclTest.java new file mode 100644 index 00000000..d962c16d --- /dev/null +++ b/maestro/src/test/java/org/intocps/maestro/FloatDeclTest.java @@ -0,0 +1,69 @@ +package org.intocps.maestro; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.intocps.maestro.ast.AVariableDeclaration; +import org.intocps.maestro.ast.LexIdentifier; +import org.intocps.maestro.ast.analysis.AnalysisException; +import org.intocps.maestro.ast.display.PrettyPrinter; +import org.intocps.maestro.ast.node.AExpInitializer; +import org.intocps.maestro.ast.node.AFloatNumericPrimitiveType; +import org.intocps.maestro.ast.node.AIdentifierExp; +import org.intocps.maestro.ast.node.ALocalVariableStm; +import org.intocps.maestro.framework.fmi2.api.mabl.BaseApiTest; +import org.intocps.maestro.framework.fmi2.api.mabl.MablApiBuilder; +import org.intocps.maestro.framework.fmi2.api.mabl.scoping.DynamicActiveBuilderScope; +import org.intocps.maestro.framework.fmi2.api.mabl.variables.BooleanVariableFmi2Api; +import org.intocps.maestro.framework.fmi2.api.mabl.variables.DoubleVariableFmi2Api; +import org.intocps.maestro.framework.fmi2.api.mabl.variables.FloatVariableFmi2Api; +import org.intocps.maestro.framework.fmi2.api.mabl.variables.VariableFmi2Api; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + + +public class FloatDeclTest extends BaseApiTest { + protected FloatDeclTest() { + super(Assertions::assertTrue, Assertions::assertFalse); + } + + @Test + public void test() throws AnalysisException, IOException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException { + MablApiBuilder.MablSettings settings = new MablApiBuilder.MablSettings(); + settings.fmiErrorHandlingEnabled = false; + MablApiBuilder builder = new MablApiBuilder(settings); + + MDebugAssert assertModule = MDebugAssert.create(builder); + + DynamicActiveBuilderScope dscope = builder.getDynamicScope(); + + DoubleVariableFmi2Api r = dscope.store(123.456); + FloatVariableFmi2Api f = dscope.store(123.456f); + + AExpInitializer aExpInitializer = new AExpInitializer(); + aExpInitializer.setExp(r.getReferenceExp().clone()); + AVariableDeclaration decl = new AVariableDeclaration(); + decl.setName(new LexIdentifier("fr", null)); + decl.setType(new AFloatNumericPrimitiveType()); + decl.setInitializer(aExpInitializer); + ALocalVariableStm stm = new ALocalVariableStm(); + stm + .setDeclaration(decl); + dscope.add(stm); + + + assertModule.assertEquals(f, new VariableFmi2Api<>(stm, decl.getType().clone(), dscope,dscope, null, new AIdentifierExp(new LexIdentifier("fr", null)))); + + + String spec = PrettyPrinter.print(builder.build()); + System.out.println(spec); + + + check(spec, ""); + + } +} diff --git a/parser/src/main/java/org/intocps/maestro/parser/ParseTree2AstConverter.java b/parser/src/main/java/org/intocps/maestro/parser/ParseTree2AstConverter.java index 31d49fd0..0ac4cdfe 100644 --- a/parser/src/main/java/org/intocps/maestro/parser/ParseTree2AstConverter.java +++ b/parser/src/main/java/org/intocps/maestro/parser/ParseTree2AstConverter.java @@ -505,8 +505,8 @@ public INode visitLiteral(MablParser.LiteralContext ctx) { literal.setValue(Integer.parseInt(ctx.DECIMAL_LITERAL().getText())); return literal; } else if (ctx.FLOAT_LITERAL() != null) { - AFloatLiteralExp literal = new AFloatLiteralExp(); - literal.setValue(Float.parseFloat(ctx.FLOAT_LITERAL().getText())); + ARealLiteralExp literal = new ARealLiteralExp(); + literal.setValue(Double.parseDouble(ctx.FLOAT_LITERAL().getText())); return literal; } else if (ctx.STRING_LITERAL() != null) { diff --git a/typechecker/src/main/java/org/intocps/maestro/typechecker/TypeCheckVisitor.java b/typechecker/src/main/java/org/intocps/maestro/typechecker/TypeCheckVisitor.java index 85eb15a3..f7d6ea17 100644 --- a/typechecker/src/main/java/org/intocps/maestro/typechecker/TypeCheckVisitor.java +++ b/typechecker/src/main/java/org/intocps/maestro/typechecker/TypeCheckVisitor.java @@ -14,6 +14,7 @@ import org.intocps.maestro.typechecker.context.ModulesContext; import java.util.*; +import java.util.function.BiFunction; import java.util.function.Function; import java.util.stream.Collectors; @@ -490,7 +491,11 @@ public PType caseAVariableDeclaration(AVariableDeclaration node, Context ctxt) t } else if (node.getInitializer() != null) { PType initType = node.getInitializer().apply(this, ctxt); - if (!typeComparator.compatible(type, initType)) { + + BiFunction isDoubleToFloatAssignment = (declType, assignType)-> + typeComparator.compatible(declType,new AFloatNumericPrimitiveType())&&typeComparator.compatible(assignType,new ARealNumericPrimitiveType()); + + if (!typeComparator.compatible(type, initType) && !isDoubleToFloatAssignment.apply(type,initType)) { errorReporter.report(0, type + " cannot be initialized with type: " + initType, node.getName().getSymbol()); } From f44269e8c2a9420690826ccc1cc625eb107a579e Mon Sep 17 00:00:00 2001 From: kel Date: Mon, 16 Sep 2024 14:21:05 +0200 Subject: [PATCH 12/15] updated upload artifact to v4 --- .github/workflows/maven.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index bdc7d1af..b66f451d 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -71,7 +71,7 @@ jobs: # run: mvn test -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn - name: upload test reports win - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 if: ${{ failure() && matrix.os == env.WINDOWS_VERSION}} with: @@ -83,7 +83,7 @@ jobs: retention-days: 1 - name: upload test reports ubuntu - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 if: ${{ failure() && matrix.os == 'ubuntu-22.04' }} with: name: surfire-ubuntu From a3e221c85ab4b055dd8feec8484ff6e4e772e7e7 Mon Sep 17 00:00:00 2001 From: kel Date: Mon, 16 Sep 2024 16:06:01 +0200 Subject: [PATCH 13/15] run only on linux --- .../test/java/org/intocps/maestro/fmi3/BuilderFmi3Test.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/maestro/src/test/java/org/intocps/maestro/fmi3/BuilderFmi3Test.java b/maestro/src/test/java/org/intocps/maestro/fmi3/BuilderFmi3Test.java index e2505c99..6191c505 100644 --- a/maestro/src/test/java/org/intocps/maestro/fmi3/BuilderFmi3Test.java +++ b/maestro/src/test/java/org/intocps/maestro/fmi3/BuilderFmi3Test.java @@ -41,6 +41,8 @@ import java.util.function.Supplier; import java.util.stream.Collectors; +import static org.junit.jupiter.api.condition.OS.LINUX; + public class BuilderFmi3Test { @BeforeAll public static void before() throws IOException { @@ -199,6 +201,7 @@ public void testClocks() throws Exception { } @Test + @EnabledOnOs({ LINUX }) public void testSimulateClocks() throws Exception { MablApiBuilder builder = new MablApiBuilder(); DynamicActiveBuilderScope scope = builder.getDynamicScope(); From d114279f758ec59d60c2e40f8f5a26078562716a Mon Sep 17 00:00:00 2001 From: Kenneth Lausdahl Date: Thu, 19 Sep 2024 13:59:48 +0200 Subject: [PATCH 14/15] fixed missing ability to copy all variables except arrays --- .../maestro/framework/fmi2/api/mabl/scoping/ScopeFmi2Api.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/scoping/ScopeFmi2Api.java b/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/scoping/ScopeFmi2Api.java index bd133776..7a5ae4f7 100644 --- a/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/scoping/ScopeFmi2Api.java +++ b/frameworks/fmi2api/src/main/java/org/intocps/maestro/framework/fmi2/api/mabl/scoping/ScopeFmi2Api.java @@ -654,8 +654,7 @@ public void addTransferAs(String... names) { @Override public Var copy(String name, Var variable) { - if (variable instanceof BooleanVariableFmi2Api || variable instanceof DoubleVariableFmi2Api || variable instanceof IntVariableFmi2Api || - variable instanceof StringVariableFmi2Api) { + if (!(variable instanceof ArrayVariableFmi2Api) && variable instanceof VariableFmi2Api) { String varName = builder.getNameGenerator().getName(name); PStm variableDeclaration = newVariable(varName, variable.getType(), variable.getReferenceExp().clone()); add(variableDeclaration); From 11f0c7ecbb43cc5564d67d155f05785b653739d9 Mon Sep 17 00:00:00 2001 From: Kenneth Lausdahl Date: Thu, 19 Sep 2024 14:00:08 +0200 Subject: [PATCH 15/15] added new test for bunching ball --- .../intocps/maestro/fmi3/BuilderFmi3Test.java | 201 ++++++++++++++---- 1 file changed, 163 insertions(+), 38 deletions(-) diff --git a/maestro/src/test/java/org/intocps/maestro/fmi3/BuilderFmi3Test.java b/maestro/src/test/java/org/intocps/maestro/fmi3/BuilderFmi3Test.java index 6191c505..9ad95cf1 100644 --- a/maestro/src/test/java/org/intocps/maestro/fmi3/BuilderFmi3Test.java +++ b/maestro/src/test/java/org/intocps/maestro/fmi3/BuilderFmi3Test.java @@ -12,11 +12,13 @@ import org.intocps.maestro.core.messages.IErrorReporter; import org.intocps.maestro.fmi.fmi3.*; import org.intocps.maestro.framework.fmi2.api.FmiBuilder; +import org.intocps.maestro.framework.fmi2.api.mabl.DataWriter; import org.intocps.maestro.framework.fmi2.api.mabl.LoggerFmi2Api; import org.intocps.maestro.framework.fmi2.api.mabl.MablApiBuilder; import org.intocps.maestro.framework.fmi2.api.mabl.PortFmi3Api; import org.intocps.maestro.framework.fmi2.api.mabl.scoping.DynamicActiveBuilderScope; import org.intocps.maestro.framework.fmi2.api.mabl.values.BooleanExpressionValue; +import org.intocps.maestro.framework.fmi2.api.mabl.values.DoubleExpressionValue; import org.intocps.maestro.framework.fmi2.api.mabl.values.IntExpressionValue; import org.intocps.maestro.framework.fmi2.api.mabl.variables.*; import org.intocps.maestro.interpreter.DefaultExternalValueFactory; @@ -27,7 +29,6 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledOnOs; -import org.junit.jupiter.api.condition.OS; import java.io.File; import java.io.IOException; @@ -40,6 +41,7 @@ import java.util.Map; import java.util.function.Supplier; import java.util.stream.Collectors; +import java.util.stream.Stream; import static org.junit.jupiter.api.condition.OS.LINUX; @@ -67,6 +69,155 @@ public static InstanceVariableFmi3Api createInstance(MablApiBuilder builder, Str return instance; } + @Test + public void bouncingBallTest() throws Exception { + MablApiBuilder builder = new MablApiBuilder(); + + InstanceVariableFmi3Api instance = createInstance(builder, "ball", + new File("target/Fmi3ModuleReferenceFmusTest/cache/BouncingBall.fmu").getAbsoluteFile().toURI(), true); + + + + + DynamicActiveBuilderScope scope = builder.getDynamicScope(); +// DoubleVariableFmi2Api stepSize = scope.store("stepSize", instance.getModelDescription().getDefaultExperiment().getStepSize()); +// stepSize.setValue(stepSize.toMath().multiply(10d)); + DoubleVariableFmi2Api stepSize = scope.store("stepSize",0.1*10d); + + + instance.setDebugLogging(instance.getModelDescription().getLogCategories().stream().map(lc -> lc.getName()).collect(Collectors.toList()), true); + + +// instance.set(instance.getPort("v_min"),DoubleExpressionValue.of(0.1d)); + //initialize + instance.enterInitializationMode(false, 0.0, 0.0, true,instance.getModelDescription().getDefaultExperiment().getStopTime() ); + + instance.set( instance.getPort("g"),DoubleExpressionValue.of(-9.81)); + instance.set( instance.getPort("e"),DoubleExpressionValue.of(0.7d)); + instance.exitInitializationMode(); + + DataWriter dw = builder.getDataWriter(); + DataWriter.DataWriterInstance csv = dw.createDataWriterInstance(); + List outputs = instance.getPorts().stream().filter(p -> p.scalarVariable.getVariable().getCausality() == Fmi3Causality.Output).collect( + Collectors.toList()); + instance.get(outputs.toArray(PortFmi3Api[]::new)); + csv.initialize( + outputs.stream() + .map(p -> new DataWriter.DataWriterInstance.LogEntry(p.getName(), () -> + + scope.copy(p.getName(),instance.get(p).values().iterator().next()).getReferenceExp().clone() + + + + )).collect( + Collectors.toList())); + + + //prepare for event handling + BooleanVariableFmi2Api stopSimulation = scope.store(false); + + BooleanVariableFmi2Api discreteStatesNeedUpdate = scope.store("discreteStatesNeedUpdate", true); + BooleanVariableFmi2Api terminateSimulation = scope.store("terminateSimulation", false); + BooleanVariableFmi2Api nominalsOfContinuousStatesChanged = scope.store("nominalsOfContinuousStatesChanged", false); + BooleanVariableFmi2Api valuesOfContinuousStatesChanged = scope.store("valuesOfContinuousStatesChanged", false); + BooleanVariableFmi2Api nextEventTimeDefined = scope.store("nextEventTimeDefined", false); + DoubleVariableFmi2Api nextEventTime = scope.store("nextEventTime", 0d); + + Supplier updateDiscreteStates = () -> { + + scope.enterWhile(discreteStatesNeedUpdate.toPredicate()); + instance.updateDiscreteStates(scope, discreteStatesNeedUpdate, terminateSimulation, nominalsOfContinuousStatesChanged, + valuesOfContinuousStatesChanged, + nextEventTimeDefined, nextEventTime); + + scope.enterIf(terminateSimulation.toPredicate()); + stopSimulation.setValue(scope, BooleanExpressionValue.of(true)); + scope.add(new ABreakStm()); + scope.leave(); + scope.leave(); + return scope; + + }; + + //handle initial events + updateDiscreteStates.get(); + + //switch to step mode + instance.enterStepMode(); + + + DoubleVariableFmi2Api currentCommunicationPoint = scope.store("time", 0d); + DoubleVariableFmi2Api endTime = scope.store(3d); + + csv.log(currentCommunicationPoint); + + //loop for co-simulation + scope.enterWhile(terminateSimulation.toPredicate().not().and(currentCommunicationPoint.toMath().lessThan(endTime))); + + //determine a step size +// DoubleVariableFmi2Api stepSize = scope.store("stepSize", Collections.min(periodicConstInClocksInterval)); + + +// //step the instance + Map.Entry, InstanceVariableFmi3Api.StepResult> stepRes = instance.step(scope, currentCommunicationPoint, + stepSize, new ABoolLiteralExp(false)); + + currentCommunicationPoint.setValue(currentCommunicationPoint.toMath().addition(stepSize)); + csv.log(currentCommunicationPoint); + + + //handle events of required + scope.enterIf( + stepRes.getValue().getEventHandlingNeeded().toPredicate()); + instance.enterEventMode(); + updateDiscreteStates.get(); + + //exit to step mode + instance.enterStepMode(); + + + ASimulationSpecificationCompilationUnit program = builder.build(); + +// String test = PrettyPrinter.print(program); + + checkAndRunProgram(program); + } + + private void checkAndRunProgram(ASimulationSpecificationCompilationUnit program) throws Exception { + System.out.println(PrettyPrinter.printLineNumbers(program)); + + File workingDirectory = new File(getWorkingDirectory(null, this.getClass()),Thread.currentThread().getStackTrace()[2].getMethodName()); + workingDirectory.mkdirs(); + File specFile = new File(workingDirectory, "spec.mabl"); + FileUtils.write(specFile, PrettyPrinter.print(program), StandardCharsets.UTF_8); + + IErrorReporter reporter = new ErrorReporter(); + Mabl mabl = new Mabl(workingDirectory, workingDirectory); + mabl.setReporter(reporter); +// mabl.setVerbose(getMablVerbose()); + mabl.parse(Collections.singletonList(specFile)); + mabl.expand(); + var tcRes = mabl.typeCheck(); + mabl.verify(Framework.FMI2); + + + + if (mabl.getReporter().getErrorCount() > 0) { + mabl.getReporter().printErrors(new PrintWriter(System.err, true)); + Assertions.fail(); + } + if (mabl.getReporter().getWarningCount() > 0) { + mabl.getReporter().printWarnings(new PrintWriter(System.out, true)); + } + + mabl.dump(workingDirectory); + Map types = tcRes.getValue(); + + new MableInterpreter(new DefaultExternalValueFactory(workingDirectory, name -> TypeChecker.findModule(types, name), + IOUtils.toInputStream(mabl.getRuntimeDataAsJsonString(), StandardCharsets.UTF_8))).execute( + MablParserUtil.parse(CharStreams.fromString(PrettyPrinter.print(program)))); + } + @Test public void test() throws Exception { MablApiBuilder builder = new MablApiBuilder(); @@ -201,7 +352,7 @@ public void testClocks() throws Exception { } @Test - @EnabledOnOs({ LINUX }) + @EnabledOnOs({LINUX}) public void testSimulateClocks() throws Exception { MablApiBuilder builder = new MablApiBuilder(); DynamicActiveBuilderScope scope = builder.getDynamicScope(); @@ -285,14 +436,15 @@ public void testSimulateClocks() throws Exception { BooleanVariableFmi2Api nextEventTimeDefined = scope.store("nextEventTimeDefined", false); DoubleVariableFmi2Api nextEventTime = scope.store("nextEventTime", 0d); - Supplier updateDiscreteStates=()->{ + Supplier updateDiscreteStates = () -> { scope.enterWhile(discreteStatesNeedUpdate.toPredicate()); - instance.updateDiscreteStates(scope, discreteStatesNeedUpdate, terminateSimulation, nominalsOfContinuousStatesChanged, valuesOfContinuousStatesChanged, + instance.updateDiscreteStates(scope, discreteStatesNeedUpdate, terminateSimulation, nominalsOfContinuousStatesChanged, + valuesOfContinuousStatesChanged, nextEventTimeDefined, nextEventTime); scope.enterIf(terminateSimulation.toPredicate()); - stopSimulation.setValue(scope,BooleanExpressionValue.of(true)); + stopSimulation.setValue(scope, BooleanExpressionValue.of(true)); scope.add(new ABreakStm()); scope.leave(); scope.leave(); @@ -306,10 +458,12 @@ public void testSimulateClocks() throws Exception { //switch to step mode instance.enterStepMode(); - List periodicConstInClocks = clocks.stream().filter(p -> p.scalarVariable.getVariable().getCausality() == Fmi3Causality.Input && p.scalarVariable.getVariable() instanceof ClockVariable && ((ClockVariable) p.scalarVariable.getVariable()).getInterval()== Fmi3ClockInterval.Constant) + List periodicConstInClocks = clocks.stream().filter(p -> p.scalarVariable.getVariable() + .getCausality() == Fmi3Causality.Input && p.scalarVariable.getVariable() instanceof ClockVariable && ((ClockVariable) p.scalarVariable.getVariable()).getInterval() == Fmi3ClockInterval.Constant) .collect(Collectors.toList()); - List periodicConstInClocksInterval = clocks.stream().filter(p -> p.scalarVariable.getVariable().getCausality() == Fmi3Causality.Input && p.scalarVariable.getVariable() instanceof ClockVariable && ((ClockVariable) p.scalarVariable.getVariable()).getInterval()== Fmi3ClockInterval.Constant) - .map(p->((ClockVariable) p.scalarVariable.getVariable()).getIntervalDecimal()) .collect(Collectors.toList()); + List periodicConstInClocksInterval = clocks.stream().filter(p -> p.scalarVariable.getVariable() + .getCausality() == Fmi3Causality.Input && p.scalarVariable.getVariable() instanceof ClockVariable && ((ClockVariable) p.scalarVariable.getVariable()).getInterval() == Fmi3ClockInterval.Constant) + .map(p -> ((ClockVariable) p.scalarVariable.getVariable()).getIntervalDecimal()).collect(Collectors.toList()); List constantPeriodicClocks = clocks.stream().filter(p -> p.scalarVariable.getVariable() .getCausality() == Fmi3Causality.Input && p.scalarVariable.getVariable() instanceof ClockVariable && ((ClockVariable) p.scalarVariable.getVariable()).getInterval() == Fmi3ClockInterval.Constant) @@ -347,36 +501,7 @@ public void testSimulateClocks() throws Exception { // String test = PrettyPrinter.print(program); - System.out.println(PrettyPrinter.printLineNumbers(program)); - - File workingDirectory = getWorkingDirectory(null, this.getClass()); - File specFile = new File(workingDirectory, "spec.mabl"); - FileUtils.write(specFile, PrettyPrinter.print(program), StandardCharsets.UTF_8); - - IErrorReporter reporter = new ErrorReporter(); - Mabl mabl = new Mabl(workingDirectory, workingDirectory); - mabl.setReporter(reporter); -// mabl.setVerbose(getMablVerbose()); - mabl.parse(Collections.singletonList(specFile)); - mabl.expand(); - var tcRes = mabl.typeCheck(); - mabl.verify(Framework.FMI2); - - - if (mabl.getReporter().getErrorCount() > 0) { - mabl.getReporter().printErrors(new PrintWriter(System.err, true)); - Assertions.fail(); - } - if (mabl.getReporter().getWarningCount() > 0) { - mabl.getReporter().printWarnings(new PrintWriter(System.out, true)); - } - - mabl.dump(workingDirectory); - Map types = tcRes.getValue(); - - new MableInterpreter(new DefaultExternalValueFactory(workingDirectory, name -> TypeChecker.findModule(types, name), - IOUtils.toInputStream(mabl.getRuntimeDataAsJsonString(), StandardCharsets.UTF_8))).execute( - MablParserUtil.parse(CharStreams.fromString(PrettyPrinter.print(program)))); + checkAndRunProgram(program); } static File getWorkingDirectory(File base, Class cls) throws IOException {