diff --git a/emv2/org.osate.aadl2.errormodel.tests/models/issue2820/.gitignore b/emv2/org.osate.aadl2.errormodel.tests/models/issue2820/.gitignore
new file mode 100644
index 0000000000..afce51184c
--- /dev/null
+++ b/emv2/org.osate.aadl2.errormodel.tests/models/issue2820/.gitignore
@@ -0,0 +1,2 @@
+/.aadlbin-gen/
+/instances/
diff --git a/emv2/org.osate.aadl2.errormodel.tests/models/issue2820/.project b/emv2/org.osate.aadl2.errormodel.tests/models/issue2820/.project
new file mode 100644
index 0000000000..41d3e11aca
--- /dev/null
+++ b/emv2/org.osate.aadl2.errormodel.tests/models/issue2820/.project
@@ -0,0 +1,18 @@
+
+
+ issue2820
+
+
+
+
+
+ org.eclipse.xtext.ui.shared.xtextBuilder
+
+
+
+
+
+ org.osate.core.aadlnature
+ org.eclipse.xtext.ui.shared.xtextNature
+
+
diff --git a/emv2/org.osate.aadl2.errormodel.tests/models/issue2820/InvalidSums.aadl b/emv2/org.osate.aadl2.errormodel.tests/models/issue2820/InvalidSums.aadl
new file mode 100644
index 0000000000..ebd521145e
--- /dev/null
+++ b/emv2/org.osate.aadl2.errormodel.tests/models/issue2820/InvalidSums.aadl
@@ -0,0 +1,51 @@
+package InvalidSums
+public
+ with ProbabilityProperties, Issue2820;
+
+ system precise_excess
+ annex EMV2 {**
+ use behavior Issue2820::machine;
+ component error behavior
+ transitions
+ t: state1 -[event1]-> (state2 with 0.2, state3 with ProbabilityProperties::probability);
+ end component;
+ properties
+ ProbabilityProperties::probability => 0.800000000000000001 applies to t;
+ **};
+ end precise_excess;
+
+ system ordinary_shortfall
+ annex EMV2 {**
+ use behavior Issue2820::machine;
+ component error behavior
+ transitions
+ t: state1 -[event1]-> (state2 with 0.2, state3 with ProbabilityProperties::probability);
+ end component;
+ properties
+ ProbabilityProperties::probability => 0.7 applies to t;
+ **};
+ end ordinary_shortfall;
+
+ system exhausted_remainder
+ annex EMV2 {**
+ use behavior Issue2820::machine;
+ component error behavior
+ transitions
+ t: state1 -[event1]-> (state2 with 0.2, state3 with ProbabilityProperties::probability, same state with others);
+ end component;
+ properties
+ ProbabilityProperties::probability => 0.8 applies to t;
+ **};
+ end exhausted_remainder;
+
+ system literal_shortfall
+ annex EMV2 {**
+ use behavior Issue2820::machine;
+ component error behavior
+ transitions
+ t: state1 -[event1]-> (state2 with 0.2, state3 with 0.7);
+ end component;
+ **};
+ end literal_shortfall;
+
+end InvalidSums;
diff --git a/emv2/org.osate.aadl2.errormodel.tests/models/issue2820/Issue2820.aadl b/emv2/org.osate.aadl2.errormodel.tests/models/issue2820/Issue2820.aadl
new file mode 100644
index 0000000000..1a074be9c7
--- /dev/null
+++ b/emv2/org.osate.aadl2.errormodel.tests/models/issue2820/Issue2820.aadl
@@ -0,0 +1,75 @@
+package Issue2820
+public
+ with ProbabilityProperties;
+
+ system decimal_value
+ annex EMV2 {**
+ use behavior Issue2820::machine;
+ component error behavior
+ transitions
+ t: state1 -[event1]-> (state2 with 0.2, state3 with ProbabilityProperties::probability);
+ end component;
+ properties
+ ProbabilityProperties::probability => 0.8 applies to t;
+ **};
+ end decimal_value;
+
+ system precise_value
+ annex EMV2 {**
+ use behavior Issue2820::machine;
+ component error behavior
+ transitions
+ t: state1 -[event1]-> (state2 with 0.200000000000000001, state3 with ProbabilityProperties::probability);
+ end component;
+ properties
+ ProbabilityProperties::probability => 0.799999999999999999 applies to t;
+ **};
+ end precise_value;
+
+ system constant_value
+ annex EMV2 {**
+ use behavior Issue2820::machine;
+ component error behavior
+ transitions
+ t: state1 -[event1]-> (state2 with 0.2, state3 with ProbabilityProperties::probability);
+ end component;
+ properties
+ ProbabilityProperties::probability => ProbabilityProperties::complement applies to t;
+ **};
+ end constant_value;
+
+ system formatted_value
+ annex EMV2 {**
+ use behavior Issue2820::machine;
+ component error behavior
+ transitions
+ t: state1 -[event1]-> (state2 with 0.2, state3 with ProbabilityProperties::probability);
+ end component;
+ properties
+ ProbabilityProperties::probability => +8.0_0e-1 applies to t;
+ **};
+ end formatted_value;
+
+ system precise_remainder
+ annex EMV2 {**
+ use behavior Issue2820::machine;
+ component error behavior
+ transitions
+ t: state1 -[event1]-> (state2 with 0.2, state3 with ProbabilityProperties::probability, same state with others);
+ end component;
+ properties
+ ProbabilityProperties::probability => 0.799999999999999999 applies to t;
+ **};
+ end precise_remainder;
+
+ annex EMV2 {**
+ error behavior machine
+ events
+ event1: error event;
+ states
+ state1: initial state;
+ state2: state;
+ state3: state;
+ end behavior;
+ **};
+end Issue2820;
diff --git a/emv2/org.osate.aadl2.errormodel.tests/models/issue2820/ProbabilityProperties.aadl b/emv2/org.osate.aadl2.errormodel.tests/models/issue2820/ProbabilityProperties.aadl
new file mode 100644
index 0000000000..56d45b9c2b
--- /dev/null
+++ b/emv2/org.osate.aadl2.errormodel.tests/models/issue2820/ProbabilityProperties.aadl
@@ -0,0 +1,4 @@
+property set ProbabilityProperties is
+ probability: aadlreal applies to (all);
+ complement: constant aadlreal => +8.0e-1;
+end ProbabilityProperties;
diff --git a/emv2/org.osate.aadl2.errormodel.tests/src/org/osate/aadl2/errormodel/tests/issues/Issue2820Test.java b/emv2/org.osate.aadl2.errormodel.tests/src/org/osate/aadl2/errormodel/tests/issues/Issue2820Test.java
new file mode 100644
index 0000000000..30b3315bcd
--- /dev/null
+++ b/emv2/org.osate.aadl2.errormodel.tests/src/org/osate/aadl2/errormodel/tests/issues/Issue2820Test.java
@@ -0,0 +1,78 @@
+/**
+ * Copyright (c) 2004-2026 Carnegie Mellon University and others. (see Contributors file).
+ * All Rights Reserved.
+ *
+ * NO WARRANTY. ALL MATERIAL IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY
+ * KIND, EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE
+ * OR MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE MATERIAL. CARNEGIE MELLON UNIVERSITY DOES NOT
+ * MAKE ANY WARRANTY OF ANY KIND WITH RESPECT TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Created, in part, with funding and support from the United States Government. (see Acknowledgments file).
+ *
+ * This program includes and/or can make use of certain third party source code, object code, documentation and other
+ * files ("Third Party Software"). The Third Party Software that is used by this program is dependent upon your system
+ * configuration. By using this program, You agree to comply with any and all relevant Third Party Software terms and
+ * conditions contained in any such Third Party Software or separate license file distributed with such Third Party
+ * Software. The parties who own the Third Party Software ("Third Party Licensors") are intended third party benefici-
+ * aries to this license with respect to the terms applicable to their Third Party Software. Third Party Software li-
+ * censes only apply to the Third Party Software and not any other portion of this program or this program as a whole.
+ */
+package org.osate.aadl2.errormodel.tests.issues;
+
+import java.util.ArrayList;
+
+import org.eclipse.xtext.testing.InjectWith;
+import org.eclipse.xtext.testing.XtextRunner;
+import org.eclipse.xtext.testing.validation.ValidationTestHelper;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.osate.aadl2.AadlPackage;
+import org.osate.aadl2.DefaultAnnexSubclause;
+import org.osate.aadl2.errormodel.tests.ErrorModelInjectorProvider;
+import org.osate.testsupport.AssertHelper;
+import org.osate.testsupport.TestHelper;
+import org.osate.xtext.aadl2.errormodel.errorModel.ErrorModelSubclause;
+
+import com.google.inject.Inject;
+import com.itemis.xtext.testing.FluentIssueCollection;
+import com.itemis.xtext.testing.XtextTest;
+
+@RunWith(XtextRunner.class)
+@InjectWith(ErrorModelInjectorProvider.class)
+public class Issue2820Test extends XtextTest {
+ private static final String PROJECT_LOCATION = "org.osate.aadl2.errormodel.tests/models/issue2820/";
+ private static final String PROPERTIES = PROJECT_LOCATION + "ProbabilityProperties.aadl";
+
+ @Inject
+ private TestHelper testHelper;
+
+ @Inject
+ private ValidationTestHelper validationHelper;
+
+ @Test
+ public void preserveExactDecimalPropertyProbabilities() {
+ validationHelper.assertNoIssues(testHelper.parseFile(PROJECT_LOCATION + "Issue2820.aadl", PROPERTIES));
+ }
+
+ @Test
+ public void rejectIncorrectSumsWithoutRoundingThemToOne() throws Exception {
+ var result = issues = testHelper.testFile(PROJECT_LOCATION + "InvalidSums.aadl", PROPERTIES,
+ PROJECT_LOCATION + "Issue2820.aadl");
+ var expected = new FluentIssueCollection(result.getResource(), new ArrayList<>(), new ArrayList<>());
+ var pkg = (AadlPackage) result.getResource().getContents().getFirst();
+ for (var classifier : pkg.getPublicSection().getOwnedClassifiers()) {
+ var annex = (ErrorModelSubclause) ((DefaultAnnexSubclause) classifier.getOwnedAnnexSubclauses().getFirst())
+ .getParsedAnnexSubclause();
+ var message = classifier.getName().equals("exhausted_remainder")
+ ? "Sum of branch probabilities must be less than 1 due to 'others'"
+ : "Sum of branch probabilities must be 1";
+ AssertHelper.assertError(annex.getTransitions().getFirst(), result.getIssues(), expected, message);
+ }
+ expected.sizeIs(4);
+ assertConstraints(expected);
+ }
+}
diff --git a/emv2/org.osate.xtext.aadl2.errormodel/src/org/osate/xtext/aadl2/errormodel/validation/ErrorModelValidator.java b/emv2/org.osate.xtext.aadl2.errormodel/src/org/osate/xtext/aadl2/errormodel/validation/ErrorModelValidator.java
index 9230b778d3..ef4f304c3f 100644
--- a/emv2/org.osate.xtext.aadl2.errormodel/src/org/osate/xtext/aadl2/errormodel/validation/ErrorModelValidator.java
+++ b/emv2/org.osate.xtext.aadl2.errormodel/src/org/osate/xtext/aadl2/errormodel/validation/ErrorModelValidator.java
@@ -36,6 +36,7 @@
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.EcoreUtil2;
+import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
import org.eclipse.xtext.validation.Check;
import org.eclipse.xtext.validation.CheckType;
import org.osate.aadl2.Aadl2Package;
@@ -60,6 +61,7 @@
import org.osate.aadl2.Property;
import org.osate.aadl2.PropertyAssociation;
import org.osate.aadl2.PropertyType;
+import org.osate.aadl2.RealLiteral;
import org.osate.aadl2.Subcomponent;
import org.osate.aadl2.modelsupport.util.AadlUtil;
import org.osate.aadl2.util.Aadl2Util;
@@ -1104,8 +1106,7 @@ private void checkBranches(ErrorBehaviorTransition ebt) {
Classifier cl = EMV2Util.getAssociatedClassifier(ebt);
List pa = EMV2Properties.getProperty(sl.getQualifiedName(), cl, ebt, null);
for (EMV2PropertyAssociation emv2PropertyAssociation : pa) {
- prob = prob.add(new BigDecimal(EMV2Properties.getRealValue(emv2PropertyAssociation),
- MathContext.UNLIMITED));
+ prob = prob.add(getBranchPropertyValue(emv2PropertyAssociation));
}
}
}
@@ -1117,6 +1118,18 @@ private void checkBranches(ErrorBehaviorTransition ebt) {
}
}
+ private BigDecimal getBranchPropertyValue(EMV2PropertyAssociation association) {
+ if (EMV2Properties.getPropertyValue(association) instanceof RealLiteral literal) {
+ var nodes = NodeModelUtils.findNodesForFeature(literal, Aadl2Package.eINSTANCE.getRealLiteral_Value());
+ if (!nodes.isEmpty()) {
+ var text = NodeModelUtils.getTokenText(nodes.getFirst()).replaceAll("[\\s_]", "");
+ return new BigDecimal(text);
+ }
+ return BigDecimal.valueOf(literal.getValue());
+ }
+ return BigDecimal.ZERO;
+ }
+
private void checkTransitionTargetTypes(TransitionBranch ebt) {
if (ebt.isSteadyState()) {
return;