Skip to content

Commit cfc9a05

Browse files
Fix #301 (#358)
1 parent 915ad3a commit cfc9a05

12 files changed

Lines changed: 972 additions & 4 deletions

File tree

CLAUDE.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,16 @@ dotnet-coverage collect "dotnet test SysML2.NET.sln --no-build" -f xml -o covera
2929

3030
Test framework: **NUnit**. Test classes use `[TestFixture]` and `[Test]` attributes.
3131

32+
## Reading `DEVELOPMENT_STANDARDS.md` is MANDATORY
33+
34+
**`DEVELOPMENT_STANDARDS.md` at the repo root governs how code is written in this solution, and it applies to EVERY piece of code you write or modify — production, tests, generator, scripts.** It is the org-wide Starion Group engineering convention document: C# style, LINQ usage, test conventions, exception and validation boundaries, XML documentation, and repo hygiene.
35+
36+
Read it before writing code in a session where you have not already done so. Do not author code from memory of "typical C#" conventions, or by copying the shape of a neighbouring file — parts of this repo predate the document and do not comply, so imitating them reproduces the deviation.
37+
38+
**Precedence, per its own §0:** a configured `.editorconfig` entry, Roslyn/StyleCop analyzer, or linter rule always WINS over the document's prose where they cover the same concern. The written rules are the fallback for what tooling does not enforce. So before treating one of its rules as binding, check `.editorconfig`, `Directory.Build.props` / `Directory.Build.targets` and any `.globalconfig` — and never flag a diff as a violation of the document when it is actually compliant with the repo's own tool configuration.
39+
40+
Where this `CLAUDE.md` and `DEVELOPMENT_STANDARDS.md` overlap, this file is the project-specific override and wins; the standards document is explicit that project-specific overrides belong in the repo's own `CLAUDE.md`. `TESTING.md` remains the binding authority for NUnit fixtures specifically.
41+
3242
## Reading `TESTING.md` is MANDATORY
3343

3444
**Before you write or modify a single line in any `*.Tests/` project, you MUST `Read` `TESTING.md` at the repo root — in full, in the current session.** It is the authoritative, binding specification for every NUnit fixture in this solution.

DEVELOPMENT_STANDARDS.md

Lines changed: 695 additions & 0 deletions
Large diffs are not rendered by default.

SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.ElementProcessing.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,15 @@ internal void ProcessAssignmentElement(EncodedTextWriter writer, IClass umlClass
463463
{
464464
writer.WriteSafeString($"SharedTextualNotationBuilder.AppendName(stringBuilder, poco.{targetPropertyName});");
465465
}
466+
else if (assignmentElement.Value is NonTerminalElement { Name: "STRING_VALUE" })
467+
{
468+
// STRING_VALUE carries its own quotes — '"' ( STRING_CHARACTER |
469+
// ESCAPE_SEQUENCE )* '"' — and the model holds the DECODED string, so the
470+
// writer owns re-encoding it. See AppendStringValue. Keyed on the TERMINAL
471+
// rather than the property type: a String-typed test would also quote
472+
// declaredName and every other string the grammar writes bare.
473+
writer.WriteSafeString($"SharedTextualNotationBuilder.AppendStringValue(stringBuilder, poco.{targetPropertyName});");
474+
}
466475
else if (string.Equals(targetPropertyName, "Operator", StringComparison.Ordinal))
467476
{
468477
// Operator tokens need a trailing space before the next operand, matching
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package '14a-Language Extensions' {
2+
private import 'User Defined Extensions'::*;
3+
package 'User Defined Extensions' {
4+
enum def ClassificationLevel {
5+
enum uncl;
6+
enum conf;
7+
enum secret;
8+
}
9+
metadata def Classified {
10+
:>> annotatedElement : SysML::Systems::PartUsage;
11+
attribute classificationLevel: ClassificationLevel[1];
12+
}
13+
}
14+
part part_X {
15+
@ Classified {
16+
ref :>> classificationLevel = ClassificationLevel::conf;
17+
}
18+
}
19+
part part_Y {
20+
@ Classified {
21+
ref :>> classificationLevel = ClassificationLevel::conf;
22+
}
23+
}
24+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package '14b-Language-Extensions' {
2+
package LibraryModel {
3+
part def ECU;
4+
}
5+
package UserModel {
6+
package Definitions {
7+
private import LibraryModel::*;
8+
part def VehicleControlUnit :> ECU;
9+
part def EngineControlUnit :> ECU;
10+
part def Vehicle;
11+
part def Engine;
12+
part def CanBus;
13+
port def BusIF;
14+
}
15+
package Usages {
16+
private import Definitions::*;
17+
part vehicle1: Vehicle {
18+
part vehicleControlUnit: VehicleControlUnit {
19+
port busIF: ~BusIF;
20+
}
21+
connect vehicleControlUnit.busIF to canBus.vehicleControlIF;
22+
part canBus: CanBus {
23+
port vehicleControlIF: BusIF;
24+
port engineControlIF: BusIF;
25+
port sensorIF: BusIF;
26+
}
27+
connect engine.engineControlUnit.busIF to canBus.engineControlIF;
28+
part engine: Engine {
29+
part engineControlUnit: EngineControlUnit {
30+
port busIF: ~BusIF;
31+
}
32+
}
33+
}
34+
}
35+
}
36+
}
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
package '14c-Language-Extensions' {
2+
private import ScalarValues::*;
3+
library package FMEALibrary {
4+
abstract occurrence def Situation;
5+
abstract occurrence situations: Situation[*] nonunique;
6+
occurrence def Cause :> Situation {
7+
attribute occurs[0..1] : Real;
8+
}
9+
abstract occurrence causes: Cause[*] nonunique;
10+
occurrence def FailureMode :> Situation {
11+
attribute detected[0..1] : Real;
12+
}
13+
abstract occurrence failureModes: FailureMode[*] nonunique;
14+
occurrence def Effect :> Situation {
15+
attribute severity[0..1] : String;
16+
}
17+
abstract occurrence effects: Effect[*] nonunique;
18+
item def FMEAItem :> Situation {
19+
attribute RPN: Real[0..1];
20+
occurrence :>> causes;
21+
occurrence :>> failureModes;
22+
occurrence :>> effects;
23+
}
24+
abstract item fmeaItems: FMEAItem[*] nonunique;
25+
connection def Causation :> Occurrences::HappensBefore {
26+
end[*] ref cause: Situation;
27+
end[*] ref effect: Situation;
28+
}
29+
abstract connection causations: Causation[*] nonunique;
30+
requirement def FMEARequirement;
31+
abstract requirement fmeaRequirements: FMEARequirement[*] nonunique;
32+
requirement def RequirementWithSIL :> FMEARequirement {
33+
attribute sil: SIL;
34+
}
35+
enum def SIL {
36+
enum A;
37+
enum B;
38+
enum C;
39+
}
40+
connection def Violation {
41+
end[*] ref sit: Situation;
42+
end[*] ref req: FMEARequirement;
43+
}
44+
abstract connection violations: Violation[*] nonunique;
45+
abstract connection def ControllingMeasure {
46+
end[*] ref sit: Situation;
47+
end[*] ref req: FMEARequirement;
48+
}
49+
connection def Prevention :> ControllingMeasure;
50+
abstract connection preventions: Prevention[*] nonunique;
51+
connection def Mitigation :> ControllingMeasure;
52+
abstract connection mitigations: Mitigation[*] nonunique;
53+
}
54+
library package FMEAMetadata {
55+
private import Metaobjects::SemanticMetadata;
56+
private import FMEALibrary::*;
57+
enum def Status {
58+
enum Approved;
59+
enum NotApproved;
60+
}
61+
metadata def StatusHolder {
62+
ref status: Status;
63+
}
64+
metadata def <situation> SituationMetadata :> SemanticMetadata {
65+
:>> baseType default = situations meta SysML::Systems::Usage;
66+
}
67+
metadata def <cause> CauseMetadata :> situation {
68+
:>> baseType = causes meta SysML::Systems::Usage;
69+
}
70+
metadata def <failure> FailureModeMetadata :> situation {
71+
:>> baseType = failureModes meta SysML::Systems::Usage;
72+
}
73+
metadata def <effect> EffectMetadata :> situation {
74+
:>> baseType = effects meta SysML::Systems::Usage;
75+
}
76+
metadata def <fmea> FMEAItemMetadata :> situation {
77+
:> annotatedElement : SysML::Systems::ItemDefinition;
78+
:> annotatedElement : SysML::Systems::ItemUsage;
79+
:>> baseType = fmeaItems meta SysML::Systems::Usage;
80+
}
81+
metadata def <causation> CausationMetadata :> SemanticMetadata {
82+
:>> annotatedElement : SysML::Systems::ConnectionUsage;
83+
:>> baseType = causations meta SysML::Systems::Usage;
84+
}
85+
metadata def <fmeaspec> FMEARequirementMetadata :> SemanticMetadata {
86+
:>> annotatedElement : SysML::Systems::RequirementUsage;
87+
:>> baseType = fmeaRequirements meta SysML::Systems::Usage;
88+
}
89+
metadata def <violation> ViolationMetadata :> SemanticMetadata {
90+
:>> annotatedElement : SysML::Systems::ConnectionUsage;
91+
:>> baseType = violations meta SysML::Systems::Usage;
92+
}
93+
abstract metadata def ControllingMeasureMetadata :> SemanticMetadata {
94+
:>> annotatedElement : SysML::Systems::ConnectionUsage;
95+
}
96+
metadata def <prevention> PreventionMetadata :> ControllingMeasureMetadata {
97+
:>> baseType = preventions meta SysML::Systems::Usage;
98+
}
99+
metadata def <mitigation> MitigationMetadata :> ControllingMeasureMetadata {
100+
:>> baseType = mitigations meta SysML::Systems::Usage;
101+
}
102+
}
103+
package FMEAUserModel {
104+
private import FMEALibrary::*;
105+
private import FMEAMetadata::*;
106+
#fmeaspec requirement req1 {
107+
doc
108+
/* Meter designed according to ISO00124 */
109+
}
110+
#fmeaspec requirement req2 {
111+
doc
112+
/* Device working for 1 week without the need to replace batteries */
113+
}
114+
#fmeaspec requirement req3: RequirementWithSIL {
115+
@ StatusHolder {
116+
ref :>> status = Status::Approved;
117+
}
118+
doc
119+
/* Alarm when battery has sank */
120+
:>> sil = SIL::A;
121+
}
122+
#fmea item def 'Glucose FMEA Item' {
123+
#prevention connect 'battery depleted' to req1;
124+
#cause occurrence 'battery depleted' {
125+
:>> Cause::occurs = 0.005;
126+
}
127+
#causation connect 'battery depleted' to 'battery cannot be charged';
128+
#failure occurrence 'battery cannot be charged' {
129+
:>> FailureMode::detected = 0.013;
130+
}
131+
#causation connect 'battery cannot be charged' to 'glucose level undetected';
132+
#effect occurrence 'glucose level undetected';
133+
#causation connect 'glucose level undetected' to 'therapy delay';
134+
#effect occurrence 'therapy delay' {
135+
:>> Effect::severity = "High";
136+
}
137+
}
138+
#violation connect 'Glucose Meter in Use' to req2;
139+
#mitigation connect 'Glucose Meter in Use' to req3;
140+
#fmea item 'Glucose Meter in Use': 'Glucose FMEA Item' {
141+
part 'glucose meter' {
142+
event 'glucose level undetected'[*];
143+
part battery {
144+
event 'battery depleted'[*];
145+
event 'battery cannot be charged'[*];
146+
}
147+
part pump;
148+
part reservoir;
149+
}
150+
part patient {
151+
event 'therapy delay'[*];
152+
}
153+
}
154+
}
155+
}

SysML2.NET.Serializer.TextualNotation.Tests/Writers/TextualNotationValidationTestFixture.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,9 @@ public void OneTimeTearDown()
117117
[TestCase("13-Model Containment", "13b-Safety and Security Features Element Group-1.sysmlx")]
118118
[TestCase("13-Model Containment", "13b-Safety and Security Features Element Group-2.sysmlx")]
119119
[TestCase("13-Model Containment", "13b-Safety and Security Features Element Group.sysmlx")]
120+
[TestCase("14-Language Extensions", "14a-Language Extensions.sysmlx")]
121+
[TestCase("14-Language Extensions", "14b-Language Extensions.sysmlx")]
122+
[TestCase("14-Language Extensions", "14c-Language Extensions.sysmlx")]
120123
public async Task VerifyValidationTextualNotationXmi(string folderName, string fileName)
121124
{
122125
var loggerFactory = LoggerFactory.Create(builder =>

SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/CommentTextualNotationBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ public static void BuildComment(SysML2.NET.Core.POCO.Root.Annotations.IComment p
8888
if (!string.IsNullOrWhiteSpace(poco.Locale))
8989
{
9090
stringBuilder.Append("locale ");
91-
stringBuilder.Append(poco.Locale);
91+
SharedTextualNotationBuilder.AppendStringValue(stringBuilder, poco.Locale);
9292
stringBuilder.Append(' ');
9393
}
9494

SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/DocumentationTextualNotationBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public static void BuildDocumentation(SysML2.NET.Core.POCO.Root.Annotations.IDoc
4848
if (!string.IsNullOrWhiteSpace(poco.Locale))
4949
{
5050
stringBuilder.Append("locale ");
51-
stringBuilder.Append(poco.Locale);
51+
SharedTextualNotationBuilder.AppendStringValue(stringBuilder, poco.Locale);
5252
stringBuilder.Append(' ');
5353
}
5454

SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/LiteralStringTextualNotationBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public static partial class LiteralStringTextualNotationBuilder
4242
/// <param name="stringBuilder">The <see cref="IndentedStringBuilder" /> that accumulates the entire textual notation with indentation</param>
4343
public static void BuildLiteralString(SysML2.NET.Core.POCO.Kernel.Expressions.ILiteralString poco, TextualNotationWriterContext writerContext, IndentedStringBuilder stringBuilder)
4444
{
45-
stringBuilder.Append(poco.Value);
45+
SharedTextualNotationBuilder.AppendStringValue(stringBuilder, poco.Value);
4646

4747
}
4848
}

0 commit comments

Comments
 (0)