sysmlpy 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. sysmlpy/__init__.py +226 -0
  2. sysmlpy/__main__.py +75 -0
  3. sysmlpy/antlr/README.md +146 -0
  4. sysmlpy/antlr/SysMLv2Lexer.interp +695 -0
  5. sysmlpy/antlr/SysMLv2Lexer.py +1123 -0
  6. sysmlpy/antlr/SysMLv2Lexer.tokens +444 -0
  7. sysmlpy/antlr/SysMLv2Parser.interp +960 -0
  8. sysmlpy/antlr/SysMLv2Parser.py +40750 -0
  9. sysmlpy/antlr/SysMLv2Parser.tokens +444 -0
  10. sysmlpy/antlr/SysMLv2ParserListener.py +4485 -0
  11. sysmlpy/antlr/SysMLv2ParserVisitor.py +2498 -0
  12. sysmlpy/antlr/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.interp +695 -0
  13. sysmlpy/antlr/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.py +1123 -0
  14. sysmlpy/antlr/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.tokens +444 -0
  15. sysmlpy/antlr_parser.py +185 -0
  16. sysmlpy/antlr_visitor.py +10864 -0
  17. sysmlpy/definition.py +1115 -0
  18. sysmlpy/examples/attribute_values.py +161 -0
  19. sysmlpy/examples/part_attributes.py +139 -0
  20. sysmlpy/examples/tuples_sequences.py +231 -0
  21. sysmlpy/formatting.py +47 -0
  22. sysmlpy/grammar/antlr4/README.md +30 -0
  23. sysmlpy/grammar/antlr4/SysMLv2Lexer.g4 +931 -0
  24. sysmlpy/grammar/antlr4/SysMLv2Lexer.interp +695 -0
  25. sysmlpy/grammar/antlr4/SysMLv2Lexer.py +1123 -0
  26. sysmlpy/grammar/antlr4/SysMLv2Lexer.tokens +444 -0
  27. sysmlpy/grammar/antlr4/SysMLv2Parser.g4 +2432 -0
  28. sysmlpy/grammar/antlr4/SysMLv2Parser.interp +960 -0
  29. sysmlpy/grammar/antlr4/SysMLv2Parser.py +40750 -0
  30. sysmlpy/grammar/antlr4/SysMLv2Parser.tokens +444 -0
  31. sysmlpy/grammar/antlr4/SysMLv2ParserListener.py +4485 -0
  32. sysmlpy/grammar/antlr4/SysMLv2ParserVisitor.py +2498 -0
  33. sysmlpy/grammar/antlr4/desc.xml +7 -0
  34. sysmlpy/grammar/antlr4/examples/camera.sysml +14 -0
  35. sysmlpy/grammar/antlr4/examples/toaster-system.sysml +193 -0
  36. sysmlpy/grammar/antlr4/examples/vehicle-model.sysml +118 -0
  37. sysmlpy/grammar/antlr4/pom.xml +59 -0
  38. sysmlpy/grammar/classes.py +8610 -0
  39. sysmlpy/library/LICENSE +165 -0
  40. sysmlpy/library/README.md +52 -0
  41. sysmlpy/library/__init__.py +0 -0
  42. sysmlpy/library/domain/Analysis/AnalysisTooling.sysml +34 -0
  43. sysmlpy/library/domain/Analysis/SampledFunctions.sysml +119 -0
  44. sysmlpy/library/domain/Analysis/StateSpaceRepresentation.sysml +143 -0
  45. sysmlpy/library/domain/Analysis/TradeStudies.sysml +171 -0
  46. sysmlpy/library/domain/Cause and Effect/CausationConnections.sysml +83 -0
  47. sysmlpy/library/domain/Cause and Effect/CauseAndEffect.sysml +81 -0
  48. sysmlpy/library/domain/Geometry/ShapeItems.sysml +899 -0
  49. sysmlpy/library/domain/Geometry/SpatialItems.sysml +168 -0
  50. sysmlpy/library/domain/Metadata/ImageMetadata.sysml +78 -0
  51. sysmlpy/library/domain/Metadata/ModelingMetadata.sysml +143 -0
  52. sysmlpy/library/domain/Metadata/ParametersOfInterestMetadata.sysml +39 -0
  53. sysmlpy/library/domain/Metadata/RiskMetadata.sysml +100 -0
  54. sysmlpy/library/domain/Quantities and Units/ISQ.sysml +42 -0
  55. sysmlpy/library/domain/Quantities and Units/ISQAcoustics.sysml +439 -0
  56. sysmlpy/library/domain/Quantities and Units/ISQAtomicNuclear.sysml +2726 -0
  57. sysmlpy/library/domain/Quantities and Units/ISQBase.sysml +206 -0
  58. sysmlpy/library/domain/Quantities and Units/ISQCharacteristicNumbers.sysml +1991 -0
  59. sysmlpy/library/domain/Quantities and Units/ISQChemistryMolecular.sysml +1353 -0
  60. sysmlpy/library/domain/Quantities and Units/ISQCondensedMatter.sysml +1223 -0
  61. sysmlpy/library/domain/Quantities and Units/ISQElectromagnetism.sysml +2333 -0
  62. sysmlpy/library/domain/Quantities and Units/ISQInformation.sysml +958 -0
  63. sysmlpy/library/domain/Quantities and Units/ISQLight.sysml +1537 -0
  64. sysmlpy/library/domain/Quantities and Units/ISQMechanics.sysml +1564 -0
  65. sysmlpy/library/domain/Quantities and Units/ISQSpaceTime.sysml +1169 -0
  66. sysmlpy/library/domain/Quantities and Units/ISQThermodynamics.sysml +1256 -0
  67. sysmlpy/library/domain/Quantities and Units/MeasurementRefCalculations.sysml +30 -0
  68. sysmlpy/library/domain/Quantities and Units/MeasurementReferences.sysml +526 -0
  69. sysmlpy/library/domain/Quantities and Units/Quantities.sysml +107 -0
  70. sysmlpy/library/domain/Quantities and Units/QuantityCalculations.sysml +70 -0
  71. sysmlpy/library/domain/Quantities and Units/SI.sysml +378 -0
  72. sysmlpy/library/domain/Quantities and Units/SIPrefixes.sysml +48 -0
  73. sysmlpy/library/domain/Quantities and Units/TensorCalculations.sysml +50 -0
  74. sysmlpy/library/domain/Quantities and Units/Time.sysml +274 -0
  75. sysmlpy/library/domain/Quantities and Units/USCustomaryUnits.sysml +260 -0
  76. sysmlpy/library/domain/Quantities and Units/VectorCalculations.sysml +62 -0
  77. sysmlpy/library/domain/Requirement Derivation/DerivationConnections.sysml +63 -0
  78. sysmlpy/library/domain/Requirement Derivation/RequirementDerivation.sysml +39 -0
  79. sysmlpy/library/kernel/Base.kerml +95 -0
  80. sysmlpy/library/kernel/BaseFunctions.kerml +80 -0
  81. sysmlpy/library/kernel/BooleanFunctions.kerml +22 -0
  82. sysmlpy/library/kernel/Clocks.kerml +156 -0
  83. sysmlpy/library/kernel/CollectionFunctions.kerml +68 -0
  84. sysmlpy/library/kernel/Collections.kerml +147 -0
  85. sysmlpy/library/kernel/ComplexFunctions.kerml +47 -0
  86. sysmlpy/library/kernel/ControlFunctions.kerml +117 -0
  87. sysmlpy/library/kernel/ControlPerformances.kerml +135 -0
  88. sysmlpy/library/kernel/DataFunctions.kerml +43 -0
  89. sysmlpy/library/kernel/FeatureReferencingPerformances.kerml +190 -0
  90. sysmlpy/library/kernel/IntegerFunctions.kerml +43 -0
  91. sysmlpy/library/kernel/KerML.kerml +483 -0
  92. sysmlpy/library/kernel/Links.kerml +67 -0
  93. sysmlpy/library/kernel/Metaobjects.kerml +58 -0
  94. sysmlpy/library/kernel/NaturalFunctions.kerml +27 -0
  95. sysmlpy/library/kernel/NumericalFunctions.kerml +43 -0
  96. sysmlpy/library/kernel/Objects.kerml +212 -0
  97. sysmlpy/library/kernel/Observation.kerml +161 -0
  98. sysmlpy/library/kernel/OccurrenceFunctions.kerml +154 -0
  99. sysmlpy/library/kernel/Occurrences.kerml +992 -0
  100. sysmlpy/library/kernel/Performances.kerml +293 -0
  101. sysmlpy/library/kernel/RationalFunctions.kerml +49 -0
  102. sysmlpy/library/kernel/RealFunctions.kerml +56 -0
  103. sysmlpy/library/kernel/ScalarFunctions.kerml +33 -0
  104. sysmlpy/library/kernel/ScalarValues.kerml +23 -0
  105. sysmlpy/library/kernel/SequenceFunctions.kerml +111 -0
  106. sysmlpy/library/kernel/SpatialFrames.kerml +197 -0
  107. sysmlpy/library/kernel/StatePerformances.kerml +145 -0
  108. sysmlpy/library/kernel/StringFunctions.kerml +25 -0
  109. sysmlpy/library/kernel/Transfers.kerml +281 -0
  110. sysmlpy/library/kernel/TransitionPerformances.kerml +66 -0
  111. sysmlpy/library/kernel/TrigFunctions.kerml +35 -0
  112. sysmlpy/library/kernel/Triggers.kerml +188 -0
  113. sysmlpy/library/kernel/VectorFunctions.kerml +273 -0
  114. sysmlpy/library/kernel/VectorValues.kerml +64 -0
  115. sysmlpy/library/systems/Actions.sysml +552 -0
  116. sysmlpy/library/systems/Allocations.sysml +29 -0
  117. sysmlpy/library/systems/AnalysisCases.sysml +38 -0
  118. sysmlpy/library/systems/Attributes.sysml +25 -0
  119. sysmlpy/library/systems/Calculations.sysml +37 -0
  120. sysmlpy/library/systems/Cases.sysml +71 -0
  121. sysmlpy/library/systems/Connections.sysml +60 -0
  122. sysmlpy/library/systems/Constraints.sysml +44 -0
  123. sysmlpy/library/systems/Flows.sysml +126 -0
  124. sysmlpy/library/systems/Interfaces.sysml +89 -0
  125. sysmlpy/library/systems/Items.sysml +153 -0
  126. sysmlpy/library/systems/Metadata.sysml +32 -0
  127. sysmlpy/library/systems/Parts.sysml +81 -0
  128. sysmlpy/library/systems/Ports.sysml +54 -0
  129. sysmlpy/library/systems/Requirements.sysml +194 -0
  130. sysmlpy/library/systems/StandardViewDefinitions.sysml +123 -0
  131. sysmlpy/library/systems/States.sysml +103 -0
  132. sysmlpy/library/systems/SysML.sysml +539 -0
  133. sysmlpy/library/systems/UseCases.sysml +57 -0
  134. sysmlpy/library/systems/VerificationCases.sysml +103 -0
  135. sysmlpy/library/systems/Views.sysml +164 -0
  136. sysmlpy/navigate.py +272 -0
  137. sysmlpy/store.py +547 -0
  138. sysmlpy/usage.py +2978 -0
  139. sysmlpy-0.1.0.dist-info/METADATA +230 -0
  140. sysmlpy-0.1.0.dist-info/RECORD +143 -0
  141. sysmlpy-0.1.0.dist-info/WHEEL +4 -0
  142. sysmlpy-0.1.0.dist-info/entry_points.txt +3 -0
  143. sysmlpy-0.1.0.dist-info/licenses/LICENSE +21 -0
sysmlpy/usage.py ADDED
@@ -0,0 +1,2978 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Created on Fri Jun 30 23:23:31 2023
5
+
6
+ @author: christophercox
7
+ """
8
+
9
+
10
+ # import os
11
+
12
+ # os.chdir(os.path.join(os.path.dirname(os.path.abspath(__file__)), ".."))
13
+
14
+ import uuid as uuidlib
15
+
16
+ import pint
17
+
18
+ ureg = pint.UnitRegistry()
19
+
20
+ from sysmlpy.formatting import classtree
21
+ from sysmlpy.navigate import Searchable
22
+
23
+ from sysmlpy.grammar.classes import (
24
+ Identification,
25
+ DefinitionBody,
26
+ DefinitionBodyItem,
27
+ FeatureSpecializationPart,
28
+ SubclassificationPart,
29
+ )
30
+
31
+ from sysmlpy.grammar.classes import (
32
+ AttributeUsage,
33
+ AttributeDefinition,
34
+ ValuePart,
35
+ PartUsage,
36
+ PartDefinition,
37
+ ItemUsage,
38
+ ItemDefinition,
39
+ PortUsage,
40
+ PortDefinition,
41
+ DefaultReferenceUsage,
42
+ RefPrefix,
43
+ ActionUsage,
44
+ ActionDefinition,
45
+ RequirementDefinition,
46
+ UseCaseDefinition,
47
+ StateUsage,
48
+ StateDefinition,
49
+ ConstraintUsage,
50
+ ConstraintDefinition,
51
+ ConnectionUsage,
52
+ ConnectionDefinition,
53
+ FlowConnectionUsage,
54
+ FlowConnectionDefinition,
55
+ CalculationUsage,
56
+ CalculationDefinition,
57
+ EnumerationDefinition,
58
+ AllocationDefinition,
59
+ AllocationUsage,
60
+ MetadataDefinition,
61
+ MetadataUsage,
62
+ RenderingDefinition,
63
+ RenderingUsage,
64
+ IndividualDefinition,
65
+ IndividualUsageSimple,
66
+ FlowDefinition,
67
+ ViewDefinition,
68
+ ViewUsage,
69
+ ViewpointDefinition,
70
+ ViewpointUsage,
71
+ ConcernDefinition,
72
+ ConcernUsage,
73
+ CaseDefinition,
74
+ CaseUsage,
75
+ AnalysisCaseDefinition,
76
+ AnalysisCaseUsage,
77
+ VerificationCaseDefinition,
78
+ VerificationCaseUsage,
79
+ )
80
+
81
+
82
+ class Usage(Searchable):
83
+ """Base class for all SysML v2 usage and definition wrapper objects."""
84
+
85
+ #: SysML type keyword for this element (overridden by each subclass).
86
+ sysml_type = None
87
+
88
+ def __init__(self):
89
+ self.name = str(uuidlib.uuid4())
90
+ self.children = []
91
+ self.typedby = None
92
+ self._is_definition = None # overridden via the setter below
93
+ self.parent = None
94
+
95
+ @property
96
+ def is_definition(self):
97
+ """``True`` when this object wraps a *definition* (e.g. ``part def``)
98
+ rather than a usage (``part``).
99
+
100
+ Subclass ``__init__`` methods assign ``self.is_definition = True/False``
101
+ which is stored via the setter so we never mask existing instance
102
+ variable semantics.
103
+ """
104
+ if self._is_definition is not None:
105
+ return self._is_definition
106
+ # Fallback: infer from the grammar class name
107
+ return type(getattr(self, "grammar", None)).__name__.endswith("Definition")
108
+
109
+ @is_definition.setter
110
+ def is_definition(self, value):
111
+ self._is_definition = bool(value)
112
+
113
+ def _ensure_body(self, subgrammar="usage"):
114
+ # Add children
115
+ body = []
116
+ for abc in self.children:
117
+ body.append(
118
+ DefinitionBodyItem(
119
+ abc._get_definition(child="DefinitionBody")
120
+ ).get_definition()
121
+ )
122
+
123
+ if len(body) > 0:
124
+ target = getattr(self.grammar, subgrammar)
125
+ if hasattr(target, 'completion'):
126
+ target.completion.body.body = DefinitionBody(
127
+ {"name": "DefinitionBody", "ownedRelatedElement": body}
128
+ )
129
+ else:
130
+ target.body = DefinitionBody(
131
+ {"name": "DefinitionBody", "ownedRelatedElement": body}
132
+ )
133
+ return self
134
+
135
+ def usage_dump(self, child):
136
+ # This is a usage.
137
+
138
+ self._ensure_body("usage")
139
+
140
+ # Add packaging
141
+ package = {
142
+ "name": "StructureUsageElement",
143
+ "ownedRelatedElement": self.grammar.get_definition(),
144
+ }
145
+ package = {"name": "OccurrenceUsageElement", "ownedRelatedElement": package}
146
+
147
+ if child == "DefinitionBody":
148
+ package = {
149
+ "name": "OccurrenceUsageMember",
150
+ "prefix": None,
151
+ "ownedRelatedElement": [package],
152
+ }
153
+
154
+ package = {"name": "DefinitionBodyItem", "ownedRelationship": [package]}
155
+ elif "PackageBody":
156
+ package = {"name": "UsageElement", "ownedRelatedElement": package}
157
+ package = {
158
+ "name": "PackageMember",
159
+ "ownedRelatedElement": package,
160
+ "prefix": None,
161
+ }
162
+
163
+ return package
164
+
165
+ def definition_dump(self, child):
166
+ # This is a definition.
167
+
168
+ self._ensure_body("definition")
169
+
170
+ package = {
171
+ "name": "DefinitionElement",
172
+ "ownedRelatedElement": self.grammar.get_definition(),
173
+ }
174
+
175
+ if child == "DefinitionBody":
176
+ package = {
177
+ "name": "DefinitionMember",
178
+ "prefix": None,
179
+ "ownedRelatedElement": [package],
180
+ }
181
+
182
+ package = {"name": "DefinitionBodyItem", "ownedRelationship": [package]}
183
+
184
+ elif child == "PackageBody" or child == None:
185
+ # Add these packets to make this dump without parents
186
+
187
+ package = {
188
+ "name": "PackageMember",
189
+ "ownedRelatedElement": package,
190
+ "prefix": None,
191
+ }
192
+
193
+ return package
194
+
195
+ def _get_definition(self, child=None):
196
+ # Determine if this is a usage or definition based on grammar class name
197
+ grammar_cls_name = type(self.grammar).__name__
198
+ is_def = grammar_cls_name.endswith('Definition')
199
+
200
+ if is_def:
201
+ package = self.definition_dump(child)
202
+ else:
203
+ package = self.usage_dump(child)
204
+
205
+ if child is None:
206
+ package = {
207
+ "name": "PackageBodyElement",
208
+ "ownedRelationship": [package],
209
+ "prefix": None,
210
+ }
211
+
212
+ # Add the typed by definition to the package output
213
+ if self.typedby is not None:
214
+ if child is None:
215
+ package["ownedRelationship"].insert(
216
+ 0, self.typedby._get_definition(child="PackageBody")
217
+ )
218
+ elif child == "PackageBody":
219
+ package = [self.typedby._get_definition(child="PackageBody"), package]
220
+ else:
221
+ package["ownedRelationship"].insert(
222
+ 0, self.typedby._get_definition(child=child)["ownedRelationship"][0]
223
+ )
224
+
225
+ return package
226
+
227
+ def dump(self, child=None):
228
+ return classtree(self._get_definition(child)).dump()
229
+
230
+ def __repr__(self):
231
+ # Safely get name
232
+ try:
233
+ name = getattr(self, 'name', None)
234
+ if not name:
235
+ id_obj = getattr(self.grammar, 'usage', None)
236
+ if id_obj:
237
+ id_obj = getattr(id_obj.declaration, 'declaration', None)
238
+ if id_obj:
239
+ name = getattr(id_obj.identification, 'declaredName', None)
240
+ except (AttributeError, TypeError):
241
+ name = None
242
+
243
+ # Safely get shortname
244
+ try:
245
+ shortname = None
246
+ id_obj = getattr(self.grammar, 'usage', None)
247
+ if id_obj:
248
+ id_obj = getattr(id_obj.declaration, 'declaration', None)
249
+ if id_obj:
250
+ shortname = getattr(id_obj.identification, 'declaredShortName', None)
251
+ if shortname:
252
+ shortname = shortname.strip('<').strip('>')
253
+ except (AttributeError, TypeError):
254
+ shortname = None
255
+
256
+ is_def = hasattr(self.grammar, 'definition')
257
+ cls_name = self.__class__.__name__
258
+
259
+ if is_def:
260
+ if name and shortname:
261
+ return f"{cls_name}(definition=True, name={name!r}, shortname={shortname!r})"
262
+ elif name:
263
+ return f"{cls_name}(definition=True, name={name!r})"
264
+ else:
265
+ return f"{cls_name}(definition=True)"
266
+ else:
267
+ if name and shortname:
268
+ return f"{cls_name}(name={name!r}, shortname={shortname!r})"
269
+ elif name:
270
+ return f"{cls_name}(name={name!r})"
271
+ else:
272
+ return f"{cls_name}()"
273
+
274
+ def _set_name(self, name, short=False):
275
+ if hasattr(self.grammar, "usage"):
276
+ path = self.grammar.usage.declaration.declaration
277
+ elif hasattr(self.grammar, "definition"):
278
+ path = self.grammar.definition.declaration
279
+ else:
280
+ # Navigate recursively through .declaration chain to find identification
281
+ path = self.grammar.declaration
282
+ while hasattr(path, "declaration") and not hasattr(path, "identification"):
283
+ path = path.declaration
284
+
285
+ if path.identification is None:
286
+ path.identification = Identification()
287
+
288
+ if short:
289
+ path.identification.declaredShortName = "<" + name + ">"
290
+ else:
291
+ self.name = name
292
+ path.identification.declaredName = name
293
+
294
+ return self
295
+
296
+ def _get_name(self):
297
+ return self.grammar.usage.declaration.declaration.identification.declaredName
298
+
299
+ def _set_child(self, child):
300
+ self.children.append(child)
301
+ child.parent = self
302
+ return self
303
+
304
+ def _get_child(self, featurechain):
305
+ # 'x.y.z'
306
+ if isinstance(featurechain, str):
307
+ fc = featurechain.split(".")
308
+ else:
309
+ raise TypeError
310
+
311
+ if fc[0] == self.name:
312
+ # This first one must match self name, otherwise pass it all
313
+ featurechain = ".".join(fc[1:])
314
+
315
+ for child in self.children:
316
+ fcs = featurechain.split(".")
317
+ if child.name == fcs[0]:
318
+ if len(fcs) == 1:
319
+ return child
320
+ else:
321
+ return child._get_child(featurechain)
322
+
323
+ def _set_typed_by(self, typed):
324
+ # Only set if the pointed object is a definition
325
+ if "definition" in typed.grammar.__dict__:
326
+ self.typedby = typed
327
+ if "definition" in self.grammar.__dict__:
328
+ raise ValueError("A definition element cannot be defined.")
329
+ else:
330
+ if self.grammar.usage.declaration.declaration.specialization is None:
331
+ package = {
332
+ "name": "QualifiedName",
333
+ "names": [typed.name],
334
+ }
335
+ package = {
336
+ "name": "FeatureType",
337
+ "type": package,
338
+ "ownedRelatedElement": [],
339
+ }
340
+ package = {"name": "OwnedFeatureTyping", "type": package}
341
+ package = {"name": "FeatureTyping", "ownedRelationship": package}
342
+ package = {"name": "TypedBy", "ownedRelationship": [package]}
343
+ package = {
344
+ "name": "Typings",
345
+ "typedby": package,
346
+ "ownedRelationship": [],
347
+ }
348
+ package = {
349
+ "name": "FeatureSpecialization",
350
+ "ownedRelationship": package,
351
+ }
352
+ package = {
353
+ "name": "FeatureSpecializationPart",
354
+ "specialization": [package],
355
+ "multiplicity": None,
356
+ "specialization2": [],
357
+ "multiplicity2": None,
358
+ }
359
+ self.grammar.usage.declaration.declaration.specialization = (
360
+ FeatureSpecializationPart(package)
361
+ )
362
+ else:
363
+ raise ValueError("Typed by element was not a definition.")
364
+ return self
365
+
366
+ def _set_specializes(self, *parents):
367
+ """Set specialization (`:>`) for definitions.
368
+
369
+ In SysML v2: `part def Car :> Vehicle;`
370
+
371
+ Args:
372
+ *parents: One or more definition elements to specialize.
373
+ """
374
+ if "definition" not in self.grammar.__dict__:
375
+ raise ValueError(
376
+ "Only definition elements can use specializes. "
377
+ "Use _set_subsets() for usage elements."
378
+ )
379
+
380
+ names = [p.name for p in parents]
381
+ relationships = []
382
+ for name in names:
383
+ relationships.append({
384
+ "name": "OwnedSubclassification",
385
+ "superclassifier": {
386
+ "name": "QualifiedName",
387
+ "names": [name],
388
+ },
389
+ })
390
+
391
+ package = {
392
+ "name": "SubclassificationPart",
393
+ "ownedRelationship": relationships,
394
+ }
395
+ self.grammar.definition.declaration.subclassificationpart = (
396
+ SubclassificationPart(package)
397
+ )
398
+ return self
399
+
400
+ def _set_subsets(self, *parents):
401
+ """Set subsetting (`:>`) for usage elements.
402
+
403
+ In SysML v2: `part myEng :> eng;`
404
+
405
+ Args:
406
+ *parents: One or more elements to subset.
407
+ """
408
+ if "definition" in self.grammar.__dict__:
409
+ raise ValueError(
410
+ "Definition elements cannot use subsets. "
411
+ "Use _set_specializes() for definitions."
412
+ )
413
+
414
+ names = [p.name if hasattr(p, 'name') else str(p) for p in parents]
415
+ relationships = []
416
+ for name in names:
417
+ relationships.append({
418
+ "name": "OwnedSubsetting",
419
+ "subsettedFeature": {
420
+ "name": "QualifiedName",
421
+ "names": [name],
422
+ },
423
+ "ownedRelatedElement": [],
424
+ })
425
+
426
+ package = {
427
+ "name": "Subsettings",
428
+ "ownedRelationship": relationships,
429
+ }
430
+ package = {
431
+ "name": "FeatureSpecialization",
432
+ "ownedRelationship": package,
433
+ }
434
+ package = {
435
+ "name": "FeatureSpecializationPart",
436
+ "specialization": [package],
437
+ "multiplicity": None,
438
+ "specialization2": [],
439
+ "multiplicity2": None,
440
+ }
441
+ self.grammar.usage.declaration.declaration.specialization = (
442
+ FeatureSpecializationPart(package)
443
+ )
444
+ return self
445
+
446
+ def _set_redefines(self, parent):
447
+ """Set redefinition (`:>>`) for usage elements.
448
+
449
+ In SysML v2: `attribute :>> mass = 100;`
450
+
451
+ Args:
452
+ parent: The element being redefined.
453
+ """
454
+ if "definition" in self.grammar.__dict__:
455
+ raise ValueError("Definition elements cannot use redefines.")
456
+
457
+ name = parent.name if hasattr(parent, 'name') else str(parent)
458
+
459
+ package = {
460
+ "name": "OwnedRedefinition",
461
+ "redefinedFeature": {
462
+ "name": "QualifiedName",
463
+ "names": [name],
464
+ },
465
+ "ownedRelatedElement": [],
466
+ }
467
+ package = {
468
+ "name": "Redefinitions",
469
+ "ownedRelationship": [package],
470
+ }
471
+ package = {
472
+ "name": "FeatureSpecialization",
473
+ "ownedRelationship": package,
474
+ }
475
+ package = {
476
+ "name": "FeatureSpecializationPart",
477
+ "specialization": [package],
478
+ "multiplicity": None,
479
+ "specialization2": [],
480
+ "multiplicity2": None,
481
+ }
482
+ self.grammar.usage.declaration.declaration.specialization = (
483
+ FeatureSpecializationPart(package)
484
+ )
485
+ return self
486
+
487
+ def _get_grammar(self):
488
+ self._ensure_body()
489
+ return self.grammar
490
+
491
+ def load_from_grammar(self, grammar):
492
+ #!TODO Typed By
493
+ self.__init__()
494
+ self.grammar = grammar
495
+ children = []
496
+
497
+ # Check if this is a definition or usage
498
+ # Some definition types use 'definition' (PartDefinition, AttributeDefinition)
499
+ # Others use 'declaration' directly (RequirementDefinition, UseCaseDefinition)
500
+ # PartDefinition: has .definition which is a Definition object
501
+ # Usage: has .declaration directly
502
+
503
+ # First check if grammar has 'definition' (for definitions like PartDefinition)
504
+ defn = getattr(grammar, 'definition', None)
505
+ if defn:
506
+ # This type uses 'definition' (PartDefinition, etc.)
507
+ # Get name from definition.declaration.identification.declaredName
508
+ u_name = defn.declaration.identification.declaredName
509
+
510
+ # Get body from definition.body
511
+ body = defn.body if hasattr(defn, 'body') else None
512
+
513
+ # body is DefinitionBody with .children
514
+ if body and hasattr(body, 'children') and body.children:
515
+ children_list = []
516
+ for body_item in body.children:
517
+ if hasattr(body_item, 'children') and body_item.children:
518
+ member = body_item.children[0]
519
+ if hasattr(member, 'children') and member.children:
520
+ inner = member.children[0]
521
+ # Handle DefinitionElement wrapping
522
+ if inner.__class__.__name__ == 'DefinitionElement' and hasattr(inner, 'children') and inner.children:
523
+ inner = inner.children[0]
524
+ if hasattr(inner, 'children'):
525
+ children_list.append(inner)
526
+ else:
527
+ children_list.append(inner)
528
+ elif hasattr(body_item, 'ownedRelatedElement'):
529
+ children_list.append(body_item)
530
+ children = children_list
531
+ else:
532
+ children = []
533
+ elif hasattr(grammar, 'usage'):
534
+ # This is a usage (like PartUsage, ItemUsage)
535
+ # Get name from grammar.usage.declaration.declaration.identification.declaredName
536
+ usage = grammar.usage
537
+ if hasattr(usage, 'declaration') and hasattr(usage.declaration, 'declaration'):
538
+ decl = usage.declaration.declaration
539
+ if hasattr(decl, 'identification') and decl.identification:
540
+ u_name = decl.identification.declaredName
541
+ else:
542
+ u_name = None
543
+ else:
544
+ u_name = None
545
+
546
+ # Extract children from usage body: grammar.usage.completion.body.body.children
547
+ # This mirrors the PartDefinition path: grammar.definition.body.children
548
+ try:
549
+ body = usage.completion.body.body
550
+ if hasattr(body, 'children') and body.children:
551
+ children_list = []
552
+ for body_item in body.children:
553
+ if hasattr(body_item, 'children') and body_item.children:
554
+ member = body_item.children[0]
555
+ mc = member.children if isinstance(member.children, list) else [member.children]
556
+ for inner in mc:
557
+ if inner is None:
558
+ continue
559
+ # Handle DefinitionElement wrapping
560
+ if inner.__class__.__name__ == 'DefinitionElement' and hasattr(inner, 'children') and inner.children:
561
+ inner = inner.children[0]
562
+ children_list.append(inner)
563
+ children = children_list
564
+ else:
565
+ children = []
566
+ except AttributeError:
567
+ children = []
568
+ elif hasattr(grammar, 'declaration'):
569
+ # This type uses 'declaration' directly (RequirementDefinition, UseCaseDefinition, some usages)
570
+ decl = grammar.declaration
571
+ u_name = None
572
+
573
+ # Check nested declaration for Usage (UsageDeclaration -> FeatureDeclaration)
574
+ nested_decl = getattr(decl, 'declaration', None)
575
+ if nested_decl and hasattr(nested_decl, 'identification') and nested_decl.identification:
576
+ u_name = nested_decl.identification.declaredName
577
+ # Check direct identification (for RequirementDefinition, UseCaseDefinition)
578
+ elif hasattr(decl, 'identification') and decl.identification:
579
+ u_name = decl.identification.declaredName
580
+ children = []
581
+ else:
582
+ u_name = None
583
+ children = []
584
+
585
+ if u_name is not None:
586
+ self.name = u_name
587
+
588
+ for child in children:
589
+ # Handle different child types
590
+ if hasattr(child, 'definition') and hasattr(child, 'body'):
591
+ # It's a Definition (PartDefinition, ItemDefinition, etc.)
592
+ sc = child
593
+ elif hasattr(child, 'children'):
594
+ # It's a StructureUsageElement or similar
595
+ sc = child.children if hasattr(child, 'children') else child
596
+ else:
597
+ continue
598
+
599
+ # Process the child
600
+ class_name = sc.__class__.__name__ if not hasattr(sc, '__class__') else sc.__class__.__name__
601
+
602
+ if class_name == "PartDefinition":
603
+ c = Part(definition=True).load_from_grammar(sc)
604
+ c.parent = self
605
+ self.children.append(c)
606
+ elif class_name == "ItemDefinition":
607
+ c = Item(definition=True).load_from_grammar(sc)
608
+ c.parent = self
609
+ self.children.append(c)
610
+ elif class_name == "PartUsage":
611
+ c = Part().load_from_grammar(sc)
612
+ c.parent = self
613
+ self.children.append(c)
614
+ elif class_name == "ItemUsage":
615
+ c = Item().load_from_grammar(sc)
616
+ c.parent = self
617
+ self.children.append(c)
618
+ elif class_name == "AttributeUsage":
619
+ c = Attribute().load_from_grammar(sc)
620
+ c.parent = self
621
+ self.children.append(c)
622
+ elif class_name == "AttributeDefinition":
623
+ c = Attribute(definition=True).load_from_grammar(sc)
624
+ c.parent = self
625
+ self.children.append(c)
626
+ elif class_name == "StructureUsageElement":
627
+ if hasattr(sc, 'children'):
628
+ inner = sc.children
629
+ if inner.__class__.__name__ == "PartUsage":
630
+ c = Part().load_from_grammar(inner)
631
+ c.parent = self
632
+ self.children.append(c)
633
+ elif inner.__class__.__name__ == "ItemUsage":
634
+ c = Item().load_from_grammar(inner)
635
+ c.parent = self
636
+ self.children.append(c)
637
+ elif class_name == "Definition":
638
+ # Unwrap Definition to get the inner type
639
+ if hasattr(sc, 'body') and hasattr(sc.body, 'children') and sc.body.children:
640
+ for body_item in sc.body.children:
641
+ if hasattr(body_item, 'children') and body_item.children:
642
+ inner = body_item.children[0]
643
+ if hasattr(inner, 'children'):
644
+ inner = inner.children
645
+ if inner.__class__.__name__ == 'PartDefinition':
646
+ c = Part(definition=True).load_from_grammar(inner)
647
+ else:
648
+ c = Item(definition=True).load_from_grammar(inner)
649
+ c.parent = self
650
+ self.children.append(c)
651
+
652
+ return self
653
+
654
+ def add_directed_feature(self, direction, name=str(uuidlib.uuid4())):
655
+ self._set_child(DefaultReference()._set_name(name).set_direction(direction))
656
+ return self
657
+
658
+ # def modify_directed_feature(self, direction, name):
659
+ # child = self._get_child(name)
660
+ # if child is not None:
661
+ # pass
662
+ # else:
663
+ # raise AttributeError("Invalid Feature Name or Chain")
664
+
665
+
666
+ class Attribute(Usage):
667
+ sysml_type = 'attribute'
668
+ def __init__(self, definition=False, name=None):
669
+ Usage.__init__(self)
670
+
671
+ if definition:
672
+ self.grammar = AttributeDefinition()
673
+ else:
674
+ self.grammar = AttributeUsage()
675
+
676
+ if name is not None:
677
+ self._set_name(name)
678
+
679
+ def usage_dump(self, child):
680
+ # Override - base output
681
+
682
+ # Add children
683
+ body = []
684
+ for abc in self.children:
685
+ body.append(DefinitionBodyItem(abc.dump(child=True)).get_definition())
686
+ if len(body) > 0:
687
+ self.grammar.usage.completion.body.body = DefinitionBody(
688
+ {"name": "DefinitionBody", "ownedRelatedElement": body}
689
+ )
690
+
691
+ # Add packaging
692
+ package = {
693
+ "name": "NonOccurrenceUsageElement",
694
+ "ownedRelatedElement": self.grammar.get_definition(),
695
+ }
696
+
697
+ if child == "DefinitionBody":
698
+ package = {
699
+ "name": "NonOccurrenceUsageMember",
700
+ "prefix": None,
701
+ "ownedRelatedElement": [package],
702
+ }
703
+ package = {"name": "DefinitionBodyItem", "ownedRelationship": [package]}
704
+ else:
705
+ # Add these packets to make this dump without parents
706
+ package = {"name": "UsageElement", "ownedRelatedElement": package}
707
+ package = {
708
+ "name": "PackageMember",
709
+ "ownedRelatedElement": package,
710
+ "prefix": None,
711
+ }
712
+ return package
713
+
714
+ def set_value(self, value):
715
+ if not isinstance(value, pint.Quantity):
716
+ value = value * ureg.dimensionless
717
+ if isinstance(value, pint.Quantity):
718
+ # Only add units if not dimensionless
719
+ if not value.units.dimensionless:
720
+ package_units = {
721
+ "name": "QualifiedName",
722
+ "names": [str(value.units)],
723
+ }
724
+ package_units = {
725
+ "name": "FeatureReferenceMember",
726
+ "memberElement": package_units,
727
+ }
728
+ package_units = {
729
+ "name": "FeatureReferenceExpression",
730
+ "ownedRelationship": [package_units],
731
+ }
732
+ package_units = {
733
+ "name": "BaseExpression",
734
+ "ownedRelationship": package_units,
735
+ }
736
+ package_units = {
737
+ "name": "PrimaryExpression",
738
+ "operand": [],
739
+ "base": package_units,
740
+ "operator": [],
741
+ "ownedRelationship1": [],
742
+ "ownedRelationship2": [],
743
+ }
744
+ package_units = {
745
+ "name": "ExtentExpression",
746
+ "operator": "",
747
+ "ownedRelationship": [],
748
+ "primary": package_units,
749
+ }
750
+ package_units = {
751
+ "name": "UnaryExpression",
752
+ "operand": [],
753
+ "operator": None,
754
+ "extent": package_units,
755
+ }
756
+ package_units = {
757
+ "name": "ExponentiationExpression",
758
+ "operand": [],
759
+ "operator": [],
760
+ "unary": package_units,
761
+ }
762
+ package_units = {
763
+ "name": "MultiplicativeExpression",
764
+ "operation": [],
765
+ "exponential": package_units,
766
+ }
767
+ package_units = {
768
+ "name": "AdditiveExpression",
769
+ "operation": [],
770
+ "multiplicitive": package_units,
771
+ }
772
+ package_units = {
773
+ "name": "RangeExpression",
774
+ "operand": None,
775
+ "additive": package_units,
776
+ }
777
+ package_units = {
778
+ "name": "RelationalExpression",
779
+ "operation": [],
780
+ "range": package_units,
781
+ }
782
+ package_units = {
783
+ "name": "ClassificationExpression",
784
+ "operand": [],
785
+ "operator": None,
786
+ "ownedRelationship": [],
787
+ "relational": package_units,
788
+ }
789
+ package_units = {
790
+ "name": "EqualityExpression",
791
+ "operation": [],
792
+ "classification": package_units,
793
+ }
794
+ package_units = {
795
+ "name": "AndExpression",
796
+ "operation": [],
797
+ "equality": package_units,
798
+ }
799
+ package_units = {
800
+ "name": "XorExpression",
801
+ "operand": [],
802
+ "operator": [],
803
+ "and": package_units,
804
+ }
805
+ package_units = {
806
+ "name": "OrExpression",
807
+ "xor": package_units,
808
+ "operand": [],
809
+ "operator": [],
810
+ }
811
+ package_units = {
812
+ "name": "ImpliesExpression",
813
+ "operand": [],
814
+ "operator": [],
815
+ "or": package_units,
816
+ }
817
+ package_units = {
818
+ "name": "NullCoalescingExpression",
819
+ "implies": package_units,
820
+ "operator": [],
821
+ "operand": [],
822
+ }
823
+ package_units = {
824
+ "name": "ConditionalExpression",
825
+ "operator": None,
826
+ "operand": [package_units],
827
+ }
828
+ package_units = {"name": "OwnedExpression", "expression": package_units}
829
+ package_units = {
830
+ "name": "SequenceExpression",
831
+ "operation": [],
832
+ "ownedRelationship": package_units,
833
+ }
834
+ package_units = [package_units]
835
+ operator = ["["]
836
+ else:
837
+ package_units = []
838
+ operator = []
839
+
840
+ package = {
841
+ "name": "BaseExpression",
842
+ "ownedRelationship": {
843
+ "name": "LiteralInteger",
844
+ "value": str(value.magnitude),
845
+ },
846
+ }
847
+ package = {
848
+ "name": "PrimaryExpression",
849
+ "operand": package_units,
850
+ "base": package,
851
+ "operator": operator,
852
+ "ownedRelationship1": [],
853
+ "ownedRelationship2": [],
854
+ }
855
+ package = {
856
+ "name": "ExtentExpression",
857
+ "operator": "",
858
+ "ownedRelationship": [],
859
+ "primary": package,
860
+ }
861
+ package = {
862
+ "name": "UnaryExpression",
863
+ "operand": [],
864
+ "operator": None,
865
+ "extent": package,
866
+ }
867
+ package = {
868
+ "name": "ExponentiationExpression",
869
+ "operand": [],
870
+ "operator": [],
871
+ "unary": package,
872
+ }
873
+ package = {
874
+ "name": "MultiplicativeExpression",
875
+ "operation": [],
876
+ "exponential": package,
877
+ }
878
+ package = {
879
+ "name": "AdditiveExpression",
880
+ "operation": [],
881
+ "multiplicitive": package,
882
+ }
883
+ package = {
884
+ "name": "RangeExpression",
885
+ "operand": None,
886
+ "additive": package,
887
+ }
888
+ package = {
889
+ "name": "RelationalExpression",
890
+ "operation": [],
891
+ "range": package,
892
+ }
893
+ package = {
894
+ "name": "ClassificationExpression",
895
+ "operand": [],
896
+ "operator": None,
897
+ "ownedRelationship": [],
898
+ "relational": package,
899
+ }
900
+ package = {
901
+ "name": "EqualityExpression",
902
+ "operation": [],
903
+ "classification": package,
904
+ }
905
+ package = {
906
+ "name": "AndExpression",
907
+ "operation": [],
908
+ "equality": package,
909
+ }
910
+ package = {
911
+ "name": "XorExpression",
912
+ "operand": [],
913
+ "operator": [],
914
+ "and": package,
915
+ }
916
+ package = {
917
+ "name": "OrExpression",
918
+ "xor": package,
919
+ "operand": [],
920
+ "operator": [],
921
+ }
922
+ package = {
923
+ "name": "ImpliesExpression",
924
+ "operand": [],
925
+ "operator": [],
926
+ "or": package,
927
+ }
928
+ package = {
929
+ "name": "NullCoalescingExpression",
930
+ "implies": package,
931
+ "operator": [],
932
+ "operand": [],
933
+ }
934
+ package = {
935
+ "name": "ConditionalExpression",
936
+ "operator": None,
937
+ "operand": [package],
938
+ }
939
+ package = {"name": "OwnedExpression", "expression": package}
940
+ package = {
941
+ "name": "FeatureValue",
942
+ "isDefault": False,
943
+ "isEqual": False,
944
+ "isInitial": False,
945
+ "ownedRelatedElement": package,
946
+ }
947
+ package = {"name": "ValuePart", "ownedRelationship": [package]}
948
+ self.grammar.usage.completion.valuepart = ValuePart(package)
949
+ # value.unit
950
+
951
+ return self
952
+
953
+ def get_value(self):
954
+ valuepart = self.grammar.usage.completion.valuepart
955
+ if valuepart is None:
956
+ raise AttributeError("No valuepart found in grammar")
957
+
958
+ feature_value = valuepart.relationships[0]
959
+ primary = (
960
+ feature_value.element.expression.operands[0]
961
+ .implies.orexpression.xor.andexpression.equality.classification.relational.range.additive.left_hand.exponential.unary.extent.primary
962
+ )
963
+
964
+ base = primary.base.relationship
965
+ base_name = base.__class__.__name__
966
+
967
+ has_unit = hasattr(primary, 'operand') and primary.operand and len(primary.operand) > 0
968
+
969
+ if has_unit:
970
+ unit = primary.operand[0]
971
+ unit_name = None
972
+ if hasattr(unit, 'relationship'):
973
+ unit_expr = unit.relationship.expression
974
+ if hasattr(unit_expr, 'operands') and unit_expr.operands:
975
+ primary2 = unit_expr.operands[0]
976
+ if hasattr(primary2, 'implies'):
977
+ primary2 = primary2.implies.orexpression.xor.andexpression.equality.classification.relational.range.additive.left_hand.exponential.unary.extent.primary
978
+ if hasattr(primary2, 'base'):
979
+ unit_rel = primary2.base.relationship
980
+ if hasattr(unit_rel, 'children') and unit_rel.children:
981
+ unit_name = unit_rel.children[0].memberElement.dump()
982
+ elif hasattr(unit_rel, 'dump'):
983
+ unit_name = unit_rel.dump()
984
+ if unit_name:
985
+ if base_name == "LiteralInteger":
986
+ return int(base.dump()) * ureg(unit_name)
987
+ elif base_name == "LiteralReal":
988
+ return float(base.dump()) * ureg(unit_name)
989
+
990
+ base_dump = base.dump()
991
+
992
+ if base_name == "LiteralString":
993
+ return base_dump.strip('"')
994
+ elif base_name == "LiteralInteger":
995
+ return int(base_dump)
996
+ elif base_name == "LiteralReal":
997
+ return float(base_dump)
998
+ elif base_name in ("LiteralTrue", "LiteralFalse"):
999
+ return base_name == "LiteralTrue"
1000
+ elif base_name == "LiteralNull":
1001
+ return None
1002
+ elif base_name == "FeatureReferenceExpression":
1003
+ return base_dump
1004
+ else:
1005
+ return base_dump
1006
+
1007
+
1008
+ class Part(Usage):
1009
+ sysml_type = 'part'
1010
+ def __init__(self, definition=False, name=None, shortname=None):
1011
+ Usage.__init__(self)
1012
+ if definition:
1013
+ self.grammar = PartDefinition()
1014
+ else:
1015
+ self.grammar = PartUsage()
1016
+
1017
+ if name is not None:
1018
+ self._set_name(name)
1019
+ if shortname is not None:
1020
+ self._set_name(shortname, short=True)
1021
+
1022
+
1023
+ class Item(Usage):
1024
+ sysml_type = 'item'
1025
+ def __init__(self, definition=False, name=None, shortname=None):
1026
+ Usage.__init__(self)
1027
+ if definition:
1028
+ self.grammar = ItemDefinition()
1029
+ else:
1030
+ self.grammar = ItemUsage()
1031
+
1032
+ if name is not None:
1033
+ self._set_name(name)
1034
+ if shortname is not None:
1035
+ self._set_name(shortname, short=True)
1036
+
1037
+
1038
+ class Port(Usage):
1039
+ sysml_type = 'port'
1040
+ def __init__(self, definition=False, name=None, shortname=None, conjugated=False):
1041
+ Usage.__init__(self)
1042
+ if definition:
1043
+ self.grammar = PortDefinition()
1044
+ else:
1045
+ self.grammar = PortUsage()
1046
+
1047
+ self.is_definition = definition
1048
+ self.conjugated = conjugated
1049
+ self.port_attributes = [] # list of (name, type_name)
1050
+ self.port_in_items = [] # list of (name, type_name)
1051
+ self.port_out_items = [] # list of (name, type_name)
1052
+ self.port_inout_items = [] # list of (name, type_name)
1053
+
1054
+ if name is not None:
1055
+ self._set_name(name)
1056
+ if shortname is not None:
1057
+ self._set_name(shortname, short=True)
1058
+
1059
+ def add_attribute(self, name, type_name=None):
1060
+ """Add an attribute to the port definition.
1061
+
1062
+ Args:
1063
+ name: Attribute name
1064
+ type_name: Optional type
1065
+ """
1066
+ self.port_attributes.append((name, type_name))
1067
+ return self
1068
+
1069
+ def add_in_item(self, name, type_name=None):
1070
+ """Add an input item to the port definition.
1071
+
1072
+ Args:
1073
+ name: Item name
1074
+ type_name: Optional type
1075
+ """
1076
+ self.port_in_items.append((name, type_name))
1077
+ return self
1078
+
1079
+ def add_out_item(self, name, type_name=None):
1080
+ """Add an output item to the port definition.
1081
+
1082
+ Args:
1083
+ name: Item name
1084
+ type_name: Optional type
1085
+ """
1086
+ self.port_out_items.append((name, type_name))
1087
+ return self
1088
+
1089
+ def add_inout_item(self, name, type_name=None):
1090
+ """Add an inout item to the port definition.
1091
+
1092
+ Args:
1093
+ name: Item name
1094
+ type_name: Optional type
1095
+ """
1096
+ self.port_inout_items.append((name, type_name))
1097
+ return self
1098
+
1099
+ def dump(self, child=None):
1100
+ # Check if we have enhanced features
1101
+ has_port_features = (
1102
+ self.port_attributes or self.port_in_items
1103
+ or self.port_out_items or self.port_inout_items
1104
+ )
1105
+
1106
+ if not has_port_features:
1107
+ # Use standard grammar dump for basic ports
1108
+ return classtree(self._get_definition(child)).dump()
1109
+
1110
+ # Enhanced dump with in/out items
1111
+ keyword = "port def" if self.is_definition else "port"
1112
+ name_str = getattr(self, 'name', "") or ""
1113
+
1114
+ body_items = []
1115
+ for attr_name, attr_type in self.port_attributes:
1116
+ if attr_type:
1117
+ body_items.append(f"attribute {attr_name} : {attr_type}")
1118
+ else:
1119
+ body_items.append(f"attribute {attr_name}")
1120
+
1121
+ for item_name, item_type in self.port_out_items:
1122
+ if item_type:
1123
+ body_items.append(f"out item {item_name} : {item_type}")
1124
+ else:
1125
+ body_items.append(f"out item {item_name}")
1126
+
1127
+ for item_name, item_type in self.port_in_items:
1128
+ if item_type:
1129
+ body_items.append(f"in item {item_name} : {item_type}")
1130
+ else:
1131
+ body_items.append(f"in item {item_name}")
1132
+
1133
+ for item_name, item_type in self.port_inout_items:
1134
+ if item_type:
1135
+ body_items.append(f"inout item {item_name} : {item_type}")
1136
+ else:
1137
+ body_items.append(f"inout item {item_name}")
1138
+
1139
+ return f"{keyword} {name_str} {{\n " + ";\n ".join(body_items) + ";\n}"
1140
+
1141
+
1142
+ class Interface(Usage):
1143
+ sysml_type = 'interface'
1144
+ def __init__(self, definition=False, name=None, shortname=None):
1145
+ self.is_definition = definition
1146
+ self.name = name if name else str(uuidlib.uuid4())
1147
+ self.children = []
1148
+ self.typedby = None
1149
+ self.parent = None
1150
+ self.grammar = True
1151
+ self.iface_shortname = shortname
1152
+ self.ends = [] # list of (name, type_name, multiplicity, children)
1153
+ self.connections = [] # list of (from_path, to_path)
1154
+
1155
+ if definition:
1156
+ self.keyword = "interface def"
1157
+ else:
1158
+ self.keyword = "interface"
1159
+
1160
+ def add_end(self, name, type_name=None, multiplicity=None):
1161
+ """Add an end to the interface definition.
1162
+
1163
+ Args:
1164
+ name: End name
1165
+ type_name: Optional type (e.g., 'FuelOutPort')
1166
+ multiplicity: Optional multiplicity (e.g., '1', '1..*')
1167
+ """
1168
+ self.ends.append((name, type_name, multiplicity))
1169
+ return self
1170
+
1171
+ def add_connection(self, from_path, to_path):
1172
+ """Add a connection between ends.
1173
+
1174
+ Args:
1175
+ from_path: Source path (e.g., 'suppliedBy.hot')
1176
+ to_path: Target path (e.g., 'deliveredTo.hot')
1177
+ """
1178
+ self.connections.append((from_path, to_path))
1179
+ return self
1180
+
1181
+ def _set_typed_by(self, typed):
1182
+ """Set typing (`:`) for interface usage."""
1183
+ self._typed_by_name = typed.name if hasattr(typed, 'name') else str(typed)
1184
+ return self
1185
+
1186
+ def _set_specializes(self, *parents):
1187
+ """Set specialization (`:>`) for interface definitions."""
1188
+ self._specializes_names = [
1189
+ p.name if hasattr(p, 'name') else str(p) for p in parents
1190
+ ]
1191
+ return self
1192
+
1193
+ def dump(self):
1194
+ name_str = getattr(self, 'name', "") or ""
1195
+ keyword = getattr(self, 'keyword', 'interface')
1196
+
1197
+ # Build type/specialization suffix
1198
+ type_suffix = ""
1199
+ if hasattr(self, '_typed_by_name') and self._typed_by_name:
1200
+ type_suffix = f" : {self._typed_by_name}"
1201
+ elif hasattr(self, '_specializes_names') and self._specializes_names:
1202
+ type_suffix = " :> " + ", ".join(self._specializes_names)
1203
+
1204
+ body_items = []
1205
+
1206
+ for end_name, end_type, end_mult in self.ends:
1207
+ mult_str = f"[{end_mult}]" if end_mult else ""
1208
+ if end_type:
1209
+ body_items.append(f"end {end_name} : {end_type}{mult_str};")
1210
+ else:
1211
+ body_items.append(f"end {end_name}{mult_str};")
1212
+
1213
+ for from_path, to_path in self.connections:
1214
+ body_items.append(f"connect {from_path} to {to_path};")
1215
+
1216
+ if body_items:
1217
+ body = " {\n " + "\n ".join(body_items) + "\n}"
1218
+ return f"{keyword} {name_str}{type_suffix}{body}"
1219
+ else:
1220
+ return f"{keyword} {name_str}{type_suffix};"
1221
+
1222
+
1223
+ class Action(Usage):
1224
+ sysml_type = 'action'
1225
+ def __init__(self, definition=False, name=None, shortname=None, grammar=None):
1226
+ # If grammar is provided (from load_from_grammar), use it
1227
+ if grammar is not None:
1228
+ self.grammar = grammar
1229
+ else:
1230
+ # Initialize grammar properly (like Part does)
1231
+ if definition:
1232
+ self.grammar = ActionDefinition()
1233
+ self.grammar.declaration.identification.declaredName = name if name else None
1234
+ else:
1235
+ self.grammar = ActionUsage(None)
1236
+ if self.grammar.declaration and hasattr(self.grammar.declaration, 'declaration') and self.grammar.declaration.declaration:
1237
+ if hasattr(self.grammar.declaration.declaration, 'identification') and self.grammar.declaration.declaration.identification:
1238
+ self.grammar.declaration.declaration.identification.declaredName = name if name else None
1239
+
1240
+ self.is_definition = definition
1241
+ self.action_shortname = shortname
1242
+ self.name = name if name else str(uuidlib.uuid4())
1243
+ self.children = []
1244
+ self.typedby = None
1245
+ self.parent = None
1246
+ self.action_inputs = [] # List of (name, type_name)
1247
+ self.action_outputs = []
1248
+
1249
+ if definition:
1250
+ self.keyword = "action def"
1251
+ else:
1252
+ self.keyword = "action"
1253
+
1254
+ def add_input(self, name, type_name=None):
1255
+ """Add an input parameter (in) to the action.
1256
+
1257
+ Args:
1258
+ name: Name of the input
1259
+ type_name: Optional type (e.g., 'Scene')
1260
+ """
1261
+ self.action_inputs.append((name, type_name))
1262
+ return self
1263
+
1264
+ def add_output(self, name, type_name=None):
1265
+ """Add an output parameter (out) to the action.
1266
+
1267
+ Args:
1268
+ name: Name of the output
1269
+ type_name: Optional type (e.g., 'Image')
1270
+ """
1271
+ self.action_outputs.append((name, type_name))
1272
+ return self
1273
+
1274
+ def _get_definition(self, child=None):
1275
+ if self.is_definition:
1276
+ keyword = "action def"
1277
+ else:
1278
+ keyword = "action"
1279
+
1280
+ decl = {
1281
+ "name": "UsageDeclaration",
1282
+ "identification": {
1283
+ "name": "Identification",
1284
+ "declaredName": self.name if self.name else "",
1285
+ "declaredShortName": None
1286
+ }
1287
+ }
1288
+
1289
+ # Build action body with in/out parameters
1290
+ body_items = []
1291
+
1292
+ # Add inputs
1293
+ for inp_name, inp_type in self.action_inputs:
1294
+ if inp_type:
1295
+ item = f"in {inp_name} : {inp_type};"
1296
+ else:
1297
+ item = f"in {inp_name};"
1298
+ body_items.append(item)
1299
+
1300
+ # Add outputs
1301
+ for out_name, out_type in self.action_outputs:
1302
+ if out_type:
1303
+ item = f"out {out_name} : {out_type};"
1304
+ else:
1305
+ item = f"out {out_name};"
1306
+ body_items.append(item)
1307
+
1308
+ body = {"name": "ActionBody", "items": body_items}
1309
+
1310
+ if self.is_definition:
1311
+ grammar_type = "ActionDefinition"
1312
+ else:
1313
+ grammar_type = "ActionUsage"
1314
+
1315
+ return {
1316
+ "name": grammar_type,
1317
+ "declaration": decl,
1318
+ "body": body
1319
+ }
1320
+
1321
+ def load_from_grammar(self, grammar):
1322
+ # Set the grammar
1323
+ self.grammar = grammar
1324
+
1325
+ self.is_definition = False
1326
+ self.action_inputs = []
1327
+ self.action_outputs = []
1328
+ self._nested_actions = []
1329
+
1330
+ # Check for 'declaration' directly (for definitions like ActionDefinition, RequirementDefinition)
1331
+ decl = getattr(grammar, 'declaration', None)
1332
+ if decl and hasattr(decl, 'identification') and decl.identification:
1333
+ self.name = decl.identification.declaredName
1334
+ self.is_definition = True
1335
+ self.keyword = "action def"
1336
+ elif hasattr(grammar, 'declaration') and grammar.declaration:
1337
+ # Handle nested declaration structure (ActionUsageDeclaration -> UsageDeclaration -> FeatureDeclaration)
1338
+ decl = getattr(grammar.declaration, 'declaration', None)
1339
+ if decl:
1340
+ # Try nested: declaration.declaration.declaration.identification (for ANTLR)
1341
+ nested_decl = getattr(decl, 'declaration', None)
1342
+ if nested_decl and hasattr(nested_decl, 'identification') and nested_decl.identification:
1343
+ self.name = nested_decl.identification.declaredName
1344
+ self.is_definition = False
1345
+ self.keyword = "action"
1346
+ elif hasattr(decl, 'identification') and decl.identification:
1347
+ self.name = decl.identification.declaredName
1348
+ self.is_definition = False
1349
+ self.keyword = "action"
1350
+
1351
+ # Extract parameters and nested actions from body
1352
+ body = getattr(grammar, 'body', None)
1353
+ if body and hasattr(body, 'children'):
1354
+ for item in body.children:
1355
+ if hasattr(item, 'children'):
1356
+ for child in item.children:
1357
+ child_name = child.__class__.__name__
1358
+ # Extract in/out parameters from NonOccurrenceUsageMember -> DefaultReferenceUsage
1359
+ if child_name == "NonOccurrenceUsageMember":
1360
+ self._extract_parameter_from_non_occurrence(child)
1361
+ elif child_name == "BehaviorUsageMember":
1362
+ self._extract_behavior_usage(child)
1363
+ elif child_name == "ActionNodeMember":
1364
+ self._extract_nested_action(child)
1365
+
1366
+ return self
1367
+
1368
+ def _extract_parameter_from_non_occurrence(self, non_occ_member):
1369
+ """Extract in/out parameter from NonOccurrenceUsageMember -> NonOccurrenceUsageElement -> DefaultReferenceUsage."""
1370
+ if hasattr(non_occ_member, 'children'):
1371
+ children = non_occ_member.children
1372
+ if not isinstance(children, list):
1373
+ children = [children]
1374
+ for elem in children:
1375
+ # elem is NonOccurrenceUsageElement
1376
+ if hasattr(elem, 'children'):
1377
+ # elem.children is DefaultReferenceUsage (not a list)
1378
+ usage = elem.children
1379
+ if isinstance(usage, list):
1380
+ usage = usage[0] if usage else None
1381
+ if usage and hasattr(usage, 'declaration') and usage.declaration:
1382
+ # UsageDeclaration has a 'declaration' attribute which is FeatureDeclaration
1383
+ usage_decl = usage.declaration
1384
+ if hasattr(usage_decl, 'declaration') and usage_decl.declaration:
1385
+ feat_decl = usage_decl.declaration
1386
+ if hasattr(feat_decl, 'identification') and feat_decl.identification:
1387
+ name = feat_decl.identification.declaredName
1388
+ # Extract direction from prefix (RefPrefix has direction directly)
1389
+ direction = None
1390
+ if hasattr(usage, 'prefix') and usage.prefix:
1391
+ prefix = usage.prefix
1392
+ # RefPrefix has direction directly
1393
+ if hasattr(prefix, 'direction') and prefix.direction:
1394
+ dir_obj = prefix.direction
1395
+ if hasattr(dir_obj, 'isIn') and dir_obj.isIn:
1396
+ direction = 'in'
1397
+ elif hasattr(dir_obj, 'isOut') and dir_obj.isOut:
1398
+ direction = 'out'
1399
+ # OccurrenceUsagePrefix has nested prefix
1400
+ elif hasattr(prefix, 'prefix') and prefix.prefix:
1401
+ inner_prefix = prefix.prefix
1402
+ if hasattr(inner_prefix, 'direction') and inner_prefix.direction:
1403
+ dir_obj = inner_prefix.direction
1404
+ if hasattr(dir_obj, 'isIn') and dir_obj.isIn:
1405
+ direction = 'in'
1406
+ elif hasattr(dir_obj, 'isOut') and dir_obj.isOut:
1407
+ direction = 'out'
1408
+ # Extract type from specialization
1409
+ type_name = None
1410
+ if hasattr(feat_decl, 'specialization') and feat_decl.specialization:
1411
+ for spec in feat_decl.specialization.specializations:
1412
+ if hasattr(spec, 'relationship') and spec.relationship:
1413
+ rel = spec.relationship
1414
+ type_name = self._extract_type_from_typings(rel)
1415
+ if direction == 'in' and name:
1416
+ self.action_inputs.append((name, type_name))
1417
+ elif direction == 'out' and name:
1418
+ self.action_outputs.append((name, type_name))
1419
+
1420
+ def _extract_type_from_typings(self, rel):
1421
+ """Extract type name from a Typings relationship."""
1422
+ if not hasattr(rel, 'typing') or not rel.typing:
1423
+ return None
1424
+
1425
+ typing = rel.typing
1426
+
1427
+ # Direct type attribute (simple case)
1428
+ if hasattr(typing, 'type') and typing.type:
1429
+ return typing.type.dump()
1430
+
1431
+ # TypedBy case: typing.relationships -> FeatureTyping -> relationship -> OwnedFeatureTyping -> type
1432
+ if hasattr(typing, 'relationships') and typing.relationships:
1433
+ for ft in typing.relationships:
1434
+ if hasattr(ft, 'relationship') and ft.relationship:
1435
+ owned = ft.relationship
1436
+ if hasattr(owned, 'type') and owned.type:
1437
+ return owned.type.dump()
1438
+
1439
+ return None
1440
+
1441
+ def _extract_nested_action(self, action_node_member):
1442
+ """Extract nested action from ActionNodeMember."""
1443
+ if hasattr(action_node_member, 'children'):
1444
+ node = action_node_member.children
1445
+ if hasattr(node, 'children'):
1446
+ action_node = node.children
1447
+ if hasattr(action_node, 'declaration'):
1448
+ decl = action_node.declaration
1449
+ if hasattr(decl, 'identification') and decl.identification:
1450
+ name = decl.identification.declaredName
1451
+ if name:
1452
+ nested_action = Action(name=name)
1453
+ nested_action.load_from_grammar(action_node)
1454
+ self._nested_actions.append(nested_action)
1455
+ self.children.append(nested_action)
1456
+
1457
+ def _extract_behavior_usage(self, behavior_usage_member):
1458
+ """Extract behavior usage (nested action/state) from BehaviorUsageMember."""
1459
+ if hasattr(behavior_usage_member, 'children'):
1460
+ for child in behavior_usage_member.children:
1461
+ if hasattr(child, 'children'):
1462
+ # child is BehaviorUsageElement, its children contain the actual usage
1463
+ behavior_element = child.children
1464
+ if isinstance(behavior_element, list):
1465
+ usage_list = behavior_element
1466
+ else:
1467
+ usage_list = [behavior_element]
1468
+ for usage in usage_list:
1469
+ if not usage:
1470
+ continue
1471
+ # Check if BehaviorUsageElement has children with the actual usage
1472
+ if hasattr(usage, 'children') and usage.children:
1473
+ inner = usage.children
1474
+ if isinstance(inner, list):
1475
+ inner_list = inner
1476
+ else:
1477
+ inner_list = [inner]
1478
+ for actual_usage in inner_list:
1479
+ if not actual_usage:
1480
+ continue
1481
+ self._process_nested_usage(actual_usage)
1482
+ elif hasattr(usage, 'declaration'):
1483
+ self._process_nested_usage(usage)
1484
+
1485
+ def _process_nested_usage(self, usage):
1486
+ """Process a nested usage element (ActionUsage, StateUsage, etc.)."""
1487
+ usage_type = usage.__class__.__name__
1488
+
1489
+ # Get name from declaration
1490
+ name = None
1491
+ if hasattr(usage, 'declaration') and usage.declaration:
1492
+ decl = usage.declaration
1493
+ # Handle nested declaration structure
1494
+ while hasattr(decl, 'declaration') and not hasattr(decl, 'identification'):
1495
+ decl = decl.declaration
1496
+ if hasattr(decl, 'identification') and decl.identification:
1497
+ name = decl.identification.declaredName
1498
+
1499
+ if not name:
1500
+ return
1501
+
1502
+ if 'Action' in usage_type:
1503
+ nested_action = Action(name=name)
1504
+ nested_action.parent = self
1505
+ nested_action.load_from_grammar(usage)
1506
+ self._nested_actions.append(nested_action)
1507
+ self.children.append(nested_action)
1508
+
1509
+ def _get_definition(self, child=None):
1510
+ # Sync self.name to grammar before getting definition
1511
+ if hasattr(self.grammar, 'declaration') and hasattr(self.grammar.declaration, 'identification'):
1512
+ self.grammar.declaration.identification.declaredName = self.name
1513
+
1514
+ # Get the grammar's get_definition output
1515
+ if hasattr(self.grammar, 'get_definition'):
1516
+ grammar_def = self.grammar.get_definition()
1517
+ else:
1518
+ # Fallback - shouldn't happen now
1519
+ grammar_def = {"name": "ActionDefinition", "declaration": {}, "body": {}}
1520
+
1521
+ if self.is_definition:
1522
+ package = {
1523
+ "name": "DefinitionElement",
1524
+ "ownedRelatedElement": grammar_def,
1525
+ }
1526
+ else:
1527
+ package = {
1528
+ "name": "BehaviorUsageElement",
1529
+ "ownedRelationship": grammar_def,
1530
+ }
1531
+ package = {"name": "OccurrenceUsageElement", "ownedRelatedElement": package}
1532
+
1533
+ if child == "DefinitionBody":
1534
+ if self.is_definition:
1535
+ package = {
1536
+ "name": "DefinitionMember",
1537
+ "prefix": None,
1538
+ "ownedRelatedElement": [package],
1539
+ }
1540
+ else:
1541
+ package = {
1542
+ "name": "OccurrenceUsageMember",
1543
+ "prefix": None,
1544
+ "ownedRelatedElement": [package],
1545
+ }
1546
+ package = {"name": "DefinitionBodyItem", "ownedRelationship": [package]}
1547
+ elif child == "PackageBody" or child == None:
1548
+ if self.is_definition:
1549
+ package = {
1550
+ "name": "PackageMember",
1551
+ "ownedRelatedElement": package,
1552
+ "prefix": None,
1553
+ }
1554
+ else:
1555
+ package = {"name": "UsageElement", "ownedRelatedElement": package}
1556
+ package = {
1557
+ "name": "PackageMember",
1558
+ "ownedRelatedElement": package,
1559
+ "prefix": None,
1560
+ }
1561
+
1562
+ return package
1563
+
1564
+ def dump(self):
1565
+ name_str = getattr(self, 'name', "") or ""
1566
+ keyword = getattr(self, 'keyword', 'action')
1567
+
1568
+ # Build output with in/out parameters
1569
+ parts = [keyword, name_str]
1570
+
1571
+ # Add in/out parameters
1572
+ params = []
1573
+ for inp_name, inp_type in self.action_inputs:
1574
+ if inp_type:
1575
+ params.append(f"in {inp_name} : {inp_type}")
1576
+ else:
1577
+ params.append(f"in {inp_name}")
1578
+
1579
+ for out_name, out_type in self.action_outputs:
1580
+ if out_type:
1581
+ params.append(f"out {out_name} : {out_type}")
1582
+ else:
1583
+ params.append(f"out {out_name}")
1584
+
1585
+ # Build type/specialization suffix
1586
+ type_suffix = ""
1587
+ if hasattr(self, '_typed_by_name') and self._typed_by_name:
1588
+ type_suffix = f" : {self._typed_by_name}"
1589
+ elif hasattr(self, '_specializes_names') and self._specializes_names:
1590
+ type_suffix = " :> " + ", ".join(self._specializes_names)
1591
+
1592
+ if params:
1593
+ return f"{keyword} {name_str}{type_suffix} {{ " + "; ".join(params) + "; }"
1594
+ else:
1595
+ return f"{keyword} {name_str}{type_suffix};"
1596
+
1597
+ def _set_typed_by(self, typed):
1598
+ """Set typing (`:`) for action usage typed by action definition."""
1599
+ self._typed_by_name = typed.name if hasattr(typed, 'name') else str(typed)
1600
+ return self
1601
+
1602
+ def _set_specializes(self, *parents):
1603
+ """Set specialization (`:>`) for action definitions."""
1604
+ self._specializes_names = [
1605
+ p.name if hasattr(p, 'name') else str(p) for p in parents
1606
+ ]
1607
+ return self
1608
+
1609
+
1610
+ class UseCase(Usage):
1611
+ sysml_type = 'use_case'
1612
+ def __init__(self, definition=False, name=None, shortname=None):
1613
+ if definition:
1614
+ self.grammar = UseCaseDefinition()
1615
+ self.grammar.declaration.identification.declaredName = name if name else None
1616
+ else:
1617
+ self.grammar = True
1618
+
1619
+ self.is_definition = definition
1620
+ self.name = name if name else str(uuidlib.uuid4())
1621
+ self.children = []
1622
+ self.typedby = None
1623
+ self.parent = None
1624
+ self.subject = None # (name, type_name)
1625
+ self.actors = [] # list of (name, type_name)
1626
+ self.includes = [] # list of use case names
1627
+
1628
+ if definition:
1629
+ self.keyword = "use case def"
1630
+ else:
1631
+ self.keyword = "use case"
1632
+
1633
+ def _get_definition(self, child=None):
1634
+ # Sync name to grammar
1635
+ if hasattr(self.grammar, 'declaration') and hasattr(self.grammar.declaration, 'identification'):
1636
+ self.grammar.declaration.identification.declaredName = self.name
1637
+
1638
+ if hasattr(self.grammar, 'get_definition'):
1639
+ grammar_def = self.grammar.get_definition()
1640
+ else:
1641
+ grammar_def = {"name": "UseCaseDefinition", "declaration": {}, "body": {}}
1642
+
1643
+ package = {
1644
+ "name": "DefinitionElement",
1645
+ "ownedRelatedElement": grammar_def,
1646
+ }
1647
+
1648
+ if child == "DefinitionBody":
1649
+ package = {
1650
+ "name": "DefinitionMember",
1651
+ "prefix": None,
1652
+ "ownedRelatedElement": [package],
1653
+ }
1654
+ package = {"name": "DefinitionBodyItem", "ownedRelationship": [package]}
1655
+ elif child == "PackageBody" or child == None:
1656
+ package = {
1657
+ "name": "PackageMember",
1658
+ "ownedRelatedElement": package,
1659
+ "prefix": None,
1660
+ }
1661
+
1662
+ return package
1663
+
1664
+ def set_subject(self, name, type_name=None):
1665
+ """Set the subject of the use case.
1666
+
1667
+ Args:
1668
+ name: Name of the subject
1669
+ type_name: Optional type (e.g., 'Vehicle')
1670
+ """
1671
+ self.subject = (name, type_name)
1672
+ return self
1673
+
1674
+ def add_actor(self, name, type_name=None):
1675
+ """Add an actor to the use case.
1676
+
1677
+ Args:
1678
+ name: Name of the actor
1679
+ type_name: Optional type (e.g., 'Driver')
1680
+ """
1681
+ self.actors.append((name, type_name))
1682
+ return self
1683
+
1684
+ def add_include(self, use_case):
1685
+ """Add an included use case.
1686
+
1687
+ Args:
1688
+ use_case: UseCase object or name string to include
1689
+ """
1690
+ name = use_case.name if hasattr(use_case, 'name') else str(use_case)
1691
+ self.includes.append(name)
1692
+ return self
1693
+
1694
+ def _set_typed_by(self, typed):
1695
+ """Set typing (`:`) for use case usage."""
1696
+ self._typed_by_name = typed.name if hasattr(typed, 'name') else str(typed)
1697
+ return self
1698
+
1699
+ def _set_specializes(self, *parents):
1700
+ """Set specialization (`:>`) for use case definitions."""
1701
+ self._specializes_names = [
1702
+ p.name if hasattr(p, 'name') else str(p) for p in parents
1703
+ ]
1704
+ return self
1705
+
1706
+ def dump(self):
1707
+ name_str = getattr(self, 'name', "") or ""
1708
+ keyword = getattr(self, 'keyword', 'use case')
1709
+
1710
+ # Build type/specialization suffix
1711
+ type_suffix = ""
1712
+ if hasattr(self, '_typed_by_name') and self._typed_by_name:
1713
+ type_suffix = f" : {self._typed_by_name}"
1714
+ elif hasattr(self, '_specializes_names') and self._specializes_names:
1715
+ type_suffix = " :> " + ", ".join(self._specializes_names)
1716
+
1717
+ # Build body members
1718
+ body_items = []
1719
+
1720
+ if self.subject:
1721
+ subj_name, subj_type = self.subject
1722
+ if subj_type:
1723
+ body_items.append(f"subject {subj_name} : {subj_type};")
1724
+ else:
1725
+ body_items.append(f"subject {subj_name};")
1726
+
1727
+ for actor_name, actor_type in self.actors:
1728
+ if actor_type:
1729
+ body_items.append(f"actor {actor_name} : {actor_type};")
1730
+ else:
1731
+ body_items.append(f"actor {actor_name};")
1732
+
1733
+ for inc in self.includes:
1734
+ body_items.append(f"include use case {inc};")
1735
+
1736
+ if body_items:
1737
+ body = " {\n " + "\n ".join(body_items) + "\n}"
1738
+ return f"{keyword} {name_str}{type_suffix}{body}"
1739
+ else:
1740
+ return f"{keyword} {name_str}{type_suffix};"
1741
+
1742
+
1743
+ class Requirement(Usage):
1744
+ sysml_type = 'requirement'
1745
+ def __init__(self, definition=False, name=None, shortname=None):
1746
+ if definition:
1747
+ self.grammar = RequirementDefinition(None)
1748
+ if self.grammar.declaration is not None and hasattr(self.grammar.declaration, 'identification'):
1749
+ self.grammar.declaration.identification.declaredName = name if name else None
1750
+ else:
1751
+ self.grammar = True
1752
+
1753
+ self.is_definition = definition
1754
+ self.name = name if name else str(uuidlib.uuid4())
1755
+ self.children = []
1756
+ self.typedby = None
1757
+ self.parent = None
1758
+ self.req_shortname = shortname
1759
+ self.subject = None
1760
+ self.actors = []
1761
+ self.doc = None
1762
+ self.req_attributes = []
1763
+ self.req_constraints = []
1764
+ self.assume_constraints = []
1765
+
1766
+ if definition:
1767
+ self.keyword = "requirement def"
1768
+ else:
1769
+ self.keyword = "requirement"
1770
+
1771
+ def _get_definition(self, child=None):
1772
+ # Sync name to grammar
1773
+ if hasattr(self.grammar, 'declaration') and hasattr(self.grammar.declaration, 'identification'):
1774
+ self.grammar.declaration.identification.declaredName = self.name
1775
+
1776
+ if hasattr(self.grammar, 'get_definition'):
1777
+ grammar_def = self.grammar.get_definition()
1778
+ else:
1779
+ grammar_def = {"name": "RequirementDefinition", "declaration": {}, "body": {}}
1780
+
1781
+ package = {
1782
+ "name": "DefinitionElement",
1783
+ "ownedRelatedElement": grammar_def,
1784
+ }
1785
+
1786
+ if child == "DefinitionBody":
1787
+ package = {
1788
+ "name": "DefinitionMember",
1789
+ "prefix": None,
1790
+ "ownedRelatedElement": [package],
1791
+ }
1792
+ package = {"name": "DefinitionBodyItem", "ownedRelationship": [package]}
1793
+ elif child == "PackageBody" or child == None:
1794
+ package = {
1795
+ "name": "PackageMember",
1796
+ "ownedRelatedElement": package,
1797
+ "prefix": None,
1798
+ }
1799
+
1800
+ return package
1801
+
1802
+ def set_subject(self, name, type_name=None):
1803
+ """Set the subject of the requirement.
1804
+
1805
+ Args:
1806
+ name: Name of the subject
1807
+ type_name: Optional type (e.g., 'Vehicle')
1808
+ """
1809
+ self.subject = (name, type_name)
1810
+ return self
1811
+
1812
+ def add_actor(self, name, type_name=None):
1813
+ """Add an actor to the requirement.
1814
+
1815
+ Args:
1816
+ name: Name of the actor
1817
+ type_name: Optional type
1818
+ """
1819
+ self.actors.append((name, type_name))
1820
+ return self
1821
+
1822
+ def set_doc(self, text):
1823
+ """Set the documentation string.
1824
+
1825
+ Args:
1826
+ text: Documentation text
1827
+ """
1828
+ self.doc = text
1829
+ return self
1830
+
1831
+ def add_attribute(self, name, type_name=None):
1832
+ """Add an attribute to the requirement.
1833
+
1834
+ Args:
1835
+ name: Attribute name
1836
+ type_name: Optional type
1837
+ """
1838
+ self.req_attributes.append((name, type_name))
1839
+ return self
1840
+
1841
+ def add_constraint(self, expr):
1842
+ """Add a require constraint expression.
1843
+
1844
+ Args:
1845
+ expr: Constraint expression string (e.g., 'massActual <= massReqd')
1846
+ """
1847
+ self.req_constraints.append(expr)
1848
+ return self
1849
+
1850
+ def add_assume_constraint(self, expr):
1851
+ """Add an assume constraint expression.
1852
+
1853
+ Args:
1854
+ expr: Assume constraint expression string
1855
+ """
1856
+ self.assume_constraints.append(expr)
1857
+ return self
1858
+
1859
+ def _set_typed_by(self, typed):
1860
+ """Set typing (`:`) for requirement usage."""
1861
+ self._typed_by_name = typed.name if hasattr(typed, 'name') else str(typed)
1862
+ return self
1863
+
1864
+ def _set_specializes(self, *parents):
1865
+ """Set specialization (`:>`) for requirement definitions."""
1866
+ self._specializes_names = [
1867
+ p.name if hasattr(p, 'name') else str(p) for p in parents
1868
+ ]
1869
+ return self
1870
+
1871
+ def dump(self):
1872
+ name_str = getattr(self, 'name', "") or ""
1873
+ keyword = getattr(self, 'keyword', 'requirement')
1874
+
1875
+ # Add shortname
1876
+ shortname_str = ""
1877
+ if self.req_shortname:
1878
+ shortname_str = f" <{self.req_shortname}>"
1879
+
1880
+ # Build type/specialization suffix
1881
+ type_suffix = ""
1882
+ if hasattr(self, '_typed_by_name') and self._typed_by_name:
1883
+ type_suffix = f" : {self._typed_by_name}"
1884
+ elif hasattr(self, '_specializes_names') and self._specializes_names:
1885
+ type_suffix = " :> " + ", ".join(self._specializes_names)
1886
+
1887
+ # Build body members
1888
+ body_items = []
1889
+
1890
+ if self.doc:
1891
+ body_items.append(f"doc /* {self.doc} */")
1892
+
1893
+ if self.subject:
1894
+ subj_name, subj_type = self.subject
1895
+ if subj_type:
1896
+ body_items.append(f"subject {subj_name} : {subj_type};")
1897
+ else:
1898
+ body_items.append(f"subject {subj_name};")
1899
+
1900
+ for actor_name, actor_type in self.actors:
1901
+ if actor_type:
1902
+ body_items.append(f"actor {actor_name} : {actor_type};")
1903
+ else:
1904
+ body_items.append(f"actor {actor_name};")
1905
+
1906
+ for attr_name, attr_type in self.req_attributes:
1907
+ if attr_type:
1908
+ body_items.append(f"attribute {attr_name} : {attr_type};")
1909
+ else:
1910
+ body_items.append(f"attribute {attr_name};")
1911
+
1912
+ for expr in self.req_constraints:
1913
+ body_items.append(f"require constraint {{ {expr} }}")
1914
+
1915
+ for expr in self.assume_constraints:
1916
+ body_items.append(f"assume constraint {{ {expr} }}")
1917
+
1918
+ if body_items:
1919
+ body = " {\n " + "\n ".join(body_items) + "\n}"
1920
+ return f"{keyword}{shortname_str} {name_str}{type_suffix}{body}"
1921
+ else:
1922
+ return f"{keyword}{shortname_str} {name_str}{type_suffix};"
1923
+
1924
+
1925
+ class Message(Usage):
1926
+ sysml_type = 'message'
1927
+ def __init__(self, name=None, from_port=None, to_port=None, of_type=None):
1928
+ """Create a message.
1929
+
1930
+ Args:
1931
+ name: Optional message name
1932
+ from_port: Source port/part path (e.g., 'sensor')
1933
+ to_port: Target port/part path (e.g., 'controller')
1934
+ of_type: Optional item type (e.g., 'SensorData')
1935
+ """
1936
+ self.name = name if name else str(uuidlib.uuid4())
1937
+ self.children = []
1938
+ self.typedby = None
1939
+ self.parent = None
1940
+ self.grammar = True
1941
+ self.from_port = from_port
1942
+ self.to_port = to_port
1943
+ self.of_type = of_type
1944
+
1945
+ def set_from(self, from_port):
1946
+ """Set the source of the message.
1947
+
1948
+ Args:
1949
+ from_port: Source path string or element with .name
1950
+ """
1951
+ self.from_port = from_port.name if hasattr(from_port, 'name') else str(from_port)
1952
+ return self
1953
+
1954
+ def set_to(self, to_port):
1955
+ """Set the target of the message.
1956
+
1957
+ Args:
1958
+ to_port: Target path string or element with .name
1959
+ """
1960
+ self.to_port = to_port.name if hasattr(to_port, 'name') else str(to_port)
1961
+ return self
1962
+
1963
+ def set_of(self, of_type):
1964
+ """Set the item type of the message.
1965
+
1966
+ Args:
1967
+ of_type: Item type string or element with .name
1968
+ """
1969
+ self.of_type = of_type.name if hasattr(of_type, 'name') else str(of_type)
1970
+ return self
1971
+
1972
+ def dump(self):
1973
+ parts = ["message"]
1974
+
1975
+ # Name (if not a UUID)
1976
+ name_str = getattr(self, 'name', "") or ""
1977
+ try:
1978
+ import uuid as _uuid
1979
+ _uuid.UUID(name_str)
1980
+ # It's a UUID - anonymous message, skip name
1981
+ except ValueError:
1982
+ parts.append(name_str)
1983
+
1984
+ if self.of_type:
1985
+ parts.append(f"of {self.of_type}")
1986
+
1987
+ if self.from_port and self.to_port:
1988
+ parts.append(f"from {self.from_port} to {self.to_port}")
1989
+ elif self.to_port:
1990
+ parts.append(f"to {self.to_port}")
1991
+
1992
+ return " ".join(parts) + ";"
1993
+
1994
+
1995
+ class _BehaviorUsage(Usage):
1996
+ """Base class for behavior-style usages (state, action, etc.).
1997
+
1998
+ These get wrapped in BehaviorUsageElement rather than StructureUsageElement.
1999
+ """
2000
+ # Subclasses should set _is_definition_grammar based on grammar class
2001
+ _is_definition_grammar = False
2002
+
2003
+ def _get_definition(self, child=None):
2004
+ # Determine if this is a definition or usage based on grammar class name
2005
+ grammar_cls_name = type(self.grammar).__name__
2006
+ is_def = grammar_cls_name.endswith('Definition')
2007
+
2008
+ if is_def:
2009
+ package = self.behavior_definition_dump(child)
2010
+ else:
2011
+ package = self.usage_dump(child)
2012
+
2013
+ if child is None:
2014
+ package = {
2015
+ "name": "PackageBodyElement",
2016
+ "ownedRelationship": [package],
2017
+ "prefix": None,
2018
+ }
2019
+ return package
2020
+
2021
+ def usage_dump(self, child):
2022
+ # This is a behavior usage, not a structure usage.
2023
+ package = {
2024
+ "name": "BehaviorUsageElement",
2025
+ "ownedRelationship": self.grammar.get_definition(),
2026
+ }
2027
+ package = {"name": "OccurrenceUsageElement", "ownedRelatedElement": package}
2028
+
2029
+ if child == "DefinitionBody":
2030
+ package = {
2031
+ "name": "OccurrenceUsageMember",
2032
+ "prefix": None,
2033
+ "ownedRelatedElement": [package],
2034
+ }
2035
+ package = {"name": "DefinitionBodyItem", "ownedRelationship": [package]}
2036
+ else:
2037
+ package = {"name": "UsageElement", "ownedRelatedElement": package}
2038
+ package = {
2039
+ "name": "PackageMember",
2040
+ "ownedRelatedElement": package,
2041
+ "prefix": None,
2042
+ }
2043
+ return package
2044
+
2045
+ def behavior_definition_dump(self, child):
2046
+ package = {
2047
+ "name": "DefinitionElement",
2048
+ "ownedRelatedElement": self.grammar.get_definition(),
2049
+ }
2050
+
2051
+ if child == "DefinitionBody":
2052
+ package = {
2053
+ "name": "DefinitionMember",
2054
+ "prefix": None,
2055
+ "ownedRelatedElement": [package],
2056
+ }
2057
+ package = {"name": "DefinitionBodyItem", "ownedRelationship": [package]}
2058
+ else:
2059
+ package = {
2060
+ "name": "PackageMember",
2061
+ "ownedRelatedElement": package,
2062
+ "prefix": None,
2063
+ }
2064
+ return package
2065
+
2066
+
2067
+ class _NonOccurrenceUsage(Usage):
2068
+ """Base class for non-occurrence usages (attribute, calculation, constraint, etc.).
2069
+
2070
+ These get wrapped in NonOccurrenceUsageElement rather than StructureUsageElement.
2071
+ """
2072
+ def _get_definition(self, child=None):
2073
+ grammar_cls_name = type(self.grammar).__name__
2074
+ is_def = grammar_cls_name.endswith('Definition')
2075
+
2076
+ if is_def:
2077
+ package = self.nonocc_definition_dump(child)
2078
+ else:
2079
+ package = self.usage_dump(child)
2080
+
2081
+ if child is None:
2082
+ package = {
2083
+ "name": "PackageBodyElement",
2084
+ "ownedRelationship": [package],
2085
+ "prefix": None,
2086
+ }
2087
+ return package
2088
+
2089
+ def usage_dump(self, child):
2090
+ # Add packaging for non-occurrence usage
2091
+ package = {
2092
+ "name": "NonOccurrenceUsageElement",
2093
+ "ownedRelatedElement": self.grammar.get_definition(),
2094
+ }
2095
+
2096
+ if child == "DefinitionBody":
2097
+ package = {
2098
+ "name": "NonOccurrenceUsageMember",
2099
+ "prefix": None,
2100
+ "ownedRelatedElement": [package],
2101
+ }
2102
+ package = {"name": "DefinitionBodyItem", "ownedRelationship": [package]}
2103
+ else:
2104
+ package = {"name": "UsageElement", "ownedRelatedElement": package}
2105
+ package = {
2106
+ "name": "PackageMember",
2107
+ "ownedRelatedElement": package,
2108
+ "prefix": None,
2109
+ }
2110
+ return package
2111
+
2112
+ def nonocc_definition_dump(self, child):
2113
+ package = {
2114
+ "name": "DefinitionElement",
2115
+ "ownedRelatedElement": self.grammar.get_definition(),
2116
+ }
2117
+
2118
+ if child == "DefinitionBody":
2119
+ package = {
2120
+ "name": "DefinitionMember",
2121
+ "prefix": None,
2122
+ "ownedRelatedElement": [package],
2123
+ }
2124
+ package = {"name": "DefinitionBodyItem", "ownedRelationship": [package]}
2125
+ else:
2126
+ package = {
2127
+ "name": "PackageMember",
2128
+ "ownedRelatedElement": package,
2129
+ "prefix": None,
2130
+ }
2131
+ return package
2132
+
2133
+
2134
+ class Transition:
2135
+ """Represents a state machine transition.
2136
+
2137
+ Attributes:
2138
+ name: Transition name (if any)
2139
+ source: Source state name (for explicit transitions)
2140
+ trigger: Trigger event (accept parameter)
2141
+ guard: Guard condition expression
2142
+ target: Target state name
2143
+ effect: Effect action
2144
+ is_entry: Whether this is an entry transition
2145
+ parent: Parent State element
2146
+ """
2147
+ def __init__(self):
2148
+ self.name = None
2149
+ self.source = None
2150
+ self.trigger = None
2151
+ self.guard = None
2152
+ self.target = None
2153
+ self.effect = None
2154
+ self.is_entry = False
2155
+ self.grammar = None
2156
+ self.parent = None
2157
+
2158
+ def load_from_grammar(self, grammar, is_entry=False):
2159
+ """Load transition from grammar element.
2160
+
2161
+ Args:
2162
+ grammar: TargetTransitionUsage, EntryTransitionMember, TransitionUsageMember, or similar
2163
+ is_entry: Whether this is an entry transition
2164
+ """
2165
+ self.grammar = grammar
2166
+ self.is_entry = is_entry
2167
+
2168
+ if grammar.__class__.__name__ == 'EntryTransitionMember':
2169
+ self.is_entry = True
2170
+ self._load_from_entry_transition(grammar)
2171
+ elif grammar.__class__.__name__ == 'TargetTransitionUsageMember':
2172
+ self._load_from_target_transition(grammar)
2173
+ elif grammar.__class__.__name__ == 'TargetTransitionUsage':
2174
+ self._load_from_target_transition_usage(grammar)
2175
+ elif grammar.__class__.__name__ == 'TransitionUsageMember':
2176
+ self._load_from_transition_usage_member(grammar)
2177
+
2178
+ return self
2179
+
2180
+ def _load_from_transition_usage_member(self, member):
2181
+ """Load from TransitionUsageMember (explicit `transition name first X then Y;`)."""
2182
+ if hasattr(member, 'children') and member.children:
2183
+ usage = member.children
2184
+ if hasattr(usage, '__class__') and usage.__class__.__name__ == 'TransitionUsage':
2185
+ self._load_from_transition_usage(usage)
2186
+
2187
+ def _load_from_transition_usage(self, usage):
2188
+ """Load from TransitionUsage."""
2189
+ # Extract name from declaration
2190
+ if hasattr(usage, 'declaration') and usage.declaration:
2191
+ decl = usage.declaration
2192
+ while hasattr(decl, 'declaration') and not hasattr(decl, 'identification'):
2193
+ decl = decl.declaration
2194
+ if hasattr(decl, 'identification') and decl.identification:
2195
+ self.name = decl.identification.declaredName
2196
+
2197
+ # Extract source and target from ownedRelationship
2198
+ if hasattr(usage, 'children'):
2199
+ for child in usage.children:
2200
+ child_name = child.__class__.__name__
2201
+ if child_name == 'TransitionSourceMember':
2202
+ self._extract_source(child)
2203
+ elif child_name == 'TransitionSuccessionMember':
2204
+ self._extract_target(child)
2205
+ elif child_name == 'TriggerActionMember':
2206
+ self._extract_trigger(child)
2207
+ elif child_name == 'GuardExpressionMember':
2208
+ self._extract_guard(child)
2209
+ elif child_name == 'EffectBehaviorMember':
2210
+ self._extract_effect(child)
2211
+
2212
+ def _extract_source(self, source_member):
2213
+ """Extract source from TransitionSourceMember."""
2214
+ if hasattr(source_member, 'children') and source_member.children:
2215
+ qname = source_member.children
2216
+ if isinstance(qname, list):
2217
+ qname = qname[0]
2218
+ if hasattr(qname, 'names'):
2219
+ self.source = '.'.join(qname.names)
2220
+
2221
+ def _load_from_entry_transition(self, entry_member):
2222
+ """Load from EntryTransitionMember."""
2223
+ if hasattr(entry_member, 'children') and entry_member.children:
2224
+ child = entry_member.children
2225
+ if child.__class__.__name__ == 'GuardedTargetSuccession':
2226
+ self._load_from_guarded_succession(child)
2227
+ elif child.__class__.__name__ == 'TransitionSuccession':
2228
+ self._extract_target_from_succession(child)
2229
+
2230
+ def _load_from_target_transition(self, target_member):
2231
+ """Load from TargetTransitionUsageMember."""
2232
+ if hasattr(target_member, 'children') and target_member.children:
2233
+ usage = target_member.children
2234
+ if hasattr(usage, '__class__') and usage.__class__.__name__ == 'TargetTransitionUsage':
2235
+ self._load_from_target_transition_usage(usage)
2236
+
2237
+ def _load_from_target_transition_usage(self, usage):
2238
+ """Load from TargetTransitionUsage."""
2239
+ if hasattr(usage, 'children'):
2240
+ for child in usage.children:
2241
+ child_name = child.__class__.__name__
2242
+ if child_name == 'TriggerActionMember':
2243
+ self._extract_trigger(child)
2244
+ elif child_name == 'GuardExpressionMember':
2245
+ self._extract_guard(child)
2246
+ elif child_name == 'EffectBehaviorMember':
2247
+ self._extract_effect(child)
2248
+ elif child_name == 'TransitionSuccessionMember':
2249
+ self._extract_target(child)
2250
+
2251
+ def _load_from_guarded_succession(self, guarded):
2252
+ """Load from GuardedTargetSuccession."""
2253
+ if hasattr(guarded, 'children'):
2254
+ for child in guarded.children:
2255
+ if child.__class__.__name__ == 'GuardExpressionMember':
2256
+ self._extract_guard(child)
2257
+ elif child.__class__.__name__ == 'TransitionSuccessionMember':
2258
+ self._extract_target(child)
2259
+
2260
+ def _extract_trigger(self, trigger_member):
2261
+ """Extract trigger from TriggerActionMember."""
2262
+ if hasattr(trigger_member, 'children') and trigger_member.children:
2263
+ trigger = trigger_member.children
2264
+ if hasattr(trigger, 'children') and trigger.children:
2265
+ accept_param = trigger.children
2266
+ if hasattr(accept_param, 'children'):
2267
+ for param in accept_param.children:
2268
+ if param.__class__.__name__ == 'PayloadParameterMember':
2269
+ if hasattr(param, 'children') and param.children:
2270
+ payload = param.children
2271
+ # PayloadParameter can have .children (PayloadFeature) or .identification
2272
+ if hasattr(payload, 'children') and payload.children:
2273
+ feat = payload.children
2274
+ if hasattr(feat, 'identification') and feat.identification:
2275
+ self.trigger = feat.identification.declaredName
2276
+ elif hasattr(feat, 'dump'):
2277
+ self.trigger = feat.dump()
2278
+ elif hasattr(payload, 'identification') and payload.identification:
2279
+ self.trigger = payload.identification.declaredName
2280
+
2281
+ def _extract_guard(self, guard_member):
2282
+ """Extract guard from GuardExpressionMember."""
2283
+ if hasattr(guard_member, 'children') and guard_member.children:
2284
+ self.guard = guard_member.children.dump()
2285
+
2286
+ def _extract_effect(self, effect_member):
2287
+ """Extract effect from EffectBehaviorMember."""
2288
+ if hasattr(effect_member, 'children') and effect_member.children:
2289
+ effect = effect_member.children
2290
+ if hasattr(effect, 'declaration') and effect.declaration:
2291
+ self.effect = effect.declaration.dump()
2292
+
2293
+ def _extract_target(self, succession_member):
2294
+ """Extract target from TransitionSuccessionMember."""
2295
+ if hasattr(succession_member, 'children') and succession_member.children:
2296
+ succession = succession_member.children
2297
+ if hasattr(succession, 'children') and succession.children:
2298
+ self._extract_target_from_succession(succession)
2299
+
2300
+ def _extract_target_from_succession(self, succession):
2301
+ """Extract target from TransitionSuccession."""
2302
+ if hasattr(succession, 'children') and succession.children:
2303
+ connector = succession.children
2304
+ if hasattr(connector, 'children'):
2305
+ target_list = connector.children
2306
+ if isinstance(target_list, list) and target_list:
2307
+ target = target_list[0]
2308
+ if hasattr(target, 'dump'):
2309
+ self.target = target.dump()
2310
+ elif hasattr(target_list, 'dump'):
2311
+ self.target = target_list.dump()
2312
+
2313
+ def __repr__(self):
2314
+ parts = []
2315
+ if self.trigger:
2316
+ parts.append(f"trigger={self.trigger!r}")
2317
+ if self.guard:
2318
+ parts.append(f"guard={self.guard!r}")
2319
+ if self.target:
2320
+ parts.append(f"target={self.target!r}")
2321
+ if self.is_entry:
2322
+ parts.append("entry=True")
2323
+ return f"Transition({', '.join(parts)})"
2324
+
2325
+
2326
+ class State(_BehaviorUsage):
2327
+ sysml_type = 'state'
2328
+ """SysML v2 State Machine state usage/definition.
2329
+
2330
+ Usage:
2331
+ State() # state ;
2332
+ State(name='Idle') # state Idle;
2333
+ State(definition=True, name='Mode') # state def Mode;
2334
+ """
2335
+ def __init__(self, definition=False, name=None, shortname=None):
2336
+ Usage.__init__(self)
2337
+ if definition:
2338
+ self.grammar = StateDefinition()
2339
+ else:
2340
+ self.grammar = StateUsage()
2341
+
2342
+ self.transitions = []
2343
+ self.entry_actions = []
2344
+ self.exit_actions = []
2345
+ self.do_actions = []
2346
+
2347
+ if name is not None:
2348
+ self._set_name(name)
2349
+ if shortname is not None:
2350
+ self._set_name(shortname, short=True)
2351
+
2352
+ def load_from_grammar(self, grammar):
2353
+ """Load state from grammar, extracting nested states, transitions, and actions."""
2354
+ self.grammar = grammar
2355
+ self.children = []
2356
+ self.transitions = []
2357
+ self.entry_actions = []
2358
+ self.exit_actions = []
2359
+ self.do_actions = []
2360
+
2361
+ # Extract name
2362
+ if hasattr(grammar, 'declaration') and grammar.declaration:
2363
+ decl = grammar.declaration
2364
+ # Handle nested declaration structure
2365
+ while hasattr(decl, 'declaration') and not hasattr(decl, 'identification'):
2366
+ decl = decl.declaration
2367
+ if hasattr(decl, 'identification') and decl.identification:
2368
+ self.name = decl.identification.declaredName
2369
+
2370
+ # Extract nested states, transitions, and actions from body
2371
+ # StateDefinition: grammar.body -> StateDefBody -> children -> StateBodyPart -> children -> items
2372
+ # StateUsage: grammar.body -> StateUsageBody -> children -> StateDefBody -> children -> StateBodyPart -> children -> items
2373
+ body = getattr(grammar, 'body', None)
2374
+ if body is None:
2375
+ return self
2376
+
2377
+ # Handle StateUsageBody wrapper (has .children -> StateDefBody)
2378
+ body_type = body.__class__.__name__
2379
+ if body_type == 'StateUsageBody':
2380
+ body = body.children
2381
+ if body is None:
2382
+ return self
2383
+
2384
+ if not hasattr(body, 'children'):
2385
+ return self
2386
+
2387
+ state_body_part = body.children
2388
+ if state_body_part is None:
2389
+ return self
2390
+
2391
+ if hasattr(state_body_part, 'children'):
2392
+ items = state_body_part.children
2393
+ elif isinstance(state_body_part, list):
2394
+ items = state_body_part
2395
+ else:
2396
+ items = []
2397
+
2398
+ if not items:
2399
+ return self
2400
+
2401
+ for item in items:
2402
+ if not hasattr(item, 'children') or not item.children:
2403
+ continue
2404
+ # Each StateBodyItem can have multiple members (e.g., state + transition)
2405
+ for member in item.children:
2406
+ member_name = member.__class__.__name__
2407
+
2408
+ if member_name == 'BehaviorUsageMember':
2409
+ self._extract_state_from_behavior_member(member)
2410
+ elif member_name == 'TargetTransitionUsageMember':
2411
+ self._extract_transition(member)
2412
+ elif member_name == 'EntryTransitionMember':
2413
+ self._extract_entry_transition(member)
2414
+ elif member_name == 'TransitionUsageMember':
2415
+ self._extract_transition(member)
2416
+ elif member_name == 'EntryActionMember':
2417
+ self._extract_entry_action(member)
2418
+ elif member_name == 'ExitActionMember':
2419
+ self._extract_exit_action(member)
2420
+ elif member_name == 'DoActionMember':
2421
+ self._extract_do_action(member)
2422
+
2423
+ return self
2424
+
2425
+ def _extract_state_from_behavior_member(self, behavior_member):
2426
+ """Extract nested state from BehaviorUsageMember."""
2427
+ if hasattr(behavior_member, 'children'):
2428
+ for child in behavior_member.children:
2429
+ if hasattr(child, 'children'):
2430
+ # child is BehaviorUsageElement, its children is the actual usage (StateUsage, etc.)
2431
+ usage = child.children
2432
+ if not usage:
2433
+ continue
2434
+
2435
+ usage_type = usage.__class__.__name__
2436
+ if usage_type in ('StateUsage', 'StateDefinition'):
2437
+ is_def = usage_type == 'StateDefinition'
2438
+ nested_state = State(definition=is_def)
2439
+ nested_state.parent = self
2440
+ nested_state.load_from_grammar(usage)
2441
+ self.children.append(nested_state)
2442
+
2443
+ def _extract_transition(self, target_member):
2444
+ """Extract transition from TargetTransitionUsageMember."""
2445
+ transition = Transition()
2446
+ transition.parent = self
2447
+ transition.load_from_grammar(target_member)
2448
+ self.transitions.append(transition)
2449
+
2450
+ def _extract_entry_transition(self, entry_member):
2451
+ """Extract entry transition from EntryTransitionMember."""
2452
+ transition = Transition()
2453
+ transition.parent = self
2454
+ transition.load_from_grammar(entry_member, is_entry=True)
2455
+ self.transitions.append(transition)
2456
+
2457
+ def _extract_entry_action(self, entry_member):
2458
+ """Extract entry action from EntryActionMember."""
2459
+ if hasattr(entry_member, 'children') and entry_member.children:
2460
+ action = entry_member.children
2461
+ self.entry_actions.append(action)
2462
+
2463
+ def _extract_exit_action(self, exit_member):
2464
+ """Extract exit action from ExitActionMember."""
2465
+ if hasattr(exit_member, 'children') and exit_member.children:
2466
+ action = exit_member.children
2467
+ self.exit_actions.append(action)
2468
+
2469
+ def _extract_do_action(self, do_member):
2470
+ """Extract do action from DoActionMember."""
2471
+ if hasattr(do_member, 'children') and do_member.children:
2472
+ action = do_member.children
2473
+ self.do_actions.append(action)
2474
+
2475
+
2476
+ class Constraint(_NonOccurrenceUsage):
2477
+ sysml_type = 'constraint'
2478
+ """SysML v2 Constraint usage/definition.
2479
+
2480
+ Usage:
2481
+ Constraint() # constraint ;
2482
+ Constraint(name='PowerLimit') # constraint PowerLimit;
2483
+ Constraint(definition=True, name='Limit') # constraint def Limit;
2484
+ """
2485
+ def __init__(self, definition=False, name=None, shortname=None):
2486
+ Usage.__init__(self)
2487
+ if definition:
2488
+ self.grammar = ConstraintDefinition()
2489
+ else:
2490
+ self.grammar = ConstraintUsage()
2491
+
2492
+ if name is not None:
2493
+ self._set_name(name)
2494
+ if shortname is not None:
2495
+ self._set_name(shortname, short=True)
2496
+
2497
+
2498
+ class Connection(Usage):
2499
+ sysml_type = 'connection'
2500
+ """SysML v2 Connection usage/definition.
2501
+
2502
+ Usage:
2503
+ Connection() # connection ;
2504
+ Connection(name='wire') # connection wire;
2505
+ Connection(definition=True, name='DataLink') # connection def DataLink;
2506
+ """
2507
+ def __init__(self, definition=False, name=None, shortname=None):
2508
+ Usage.__init__(self)
2509
+ if definition:
2510
+ self.grammar = ConnectionDefinition()
2511
+ else:
2512
+ self.grammar = ConnectionUsage()
2513
+
2514
+ if name is not None:
2515
+ self._set_name(name)
2516
+ if shortname is not None:
2517
+ self._set_name(shortname, short=True)
2518
+
2519
+
2520
+ class Flow(Usage):
2521
+ sysml_type = 'flow'
2522
+ """SysML v2 Flow connection usage/definition.
2523
+
2524
+ Usage:
2525
+ Flow() # flow ;
2526
+ Flow(name='fuelFlow') # flow fuelFlow;
2527
+ Flow(definition=True, name='WaterFlow') # flow def WaterFlow;
2528
+ """
2529
+ def __init__(self, definition=False, name=None, shortname=None):
2530
+ Usage.__init__(self)
2531
+ if definition:
2532
+ self.grammar = FlowConnectionDefinition()
2533
+ else:
2534
+ self.grammar = FlowConnectionUsage()
2535
+
2536
+ if name is not None:
2537
+ self._set_name(name)
2538
+ if shortname is not None:
2539
+ self._set_name(shortname, short=True)
2540
+
2541
+
2542
+ class Calculation(_NonOccurrenceUsage):
2543
+ sysml_type = 'calculation'
2544
+ """SysML v2 Calculation usage/definition.
2545
+
2546
+ Usage:
2547
+ Calculation() # calc ;
2548
+ Calculation(name='computeArea') # calc computeArea;
2549
+ Calculation(definition=True, name='Distance') # calc def Distance;
2550
+ """
2551
+ def __init__(self, definition=False, name=None, shortname=None):
2552
+ Usage.__init__(self)
2553
+ if definition:
2554
+ self.grammar = CalculationDefinition()
2555
+ else:
2556
+ self.grammar = CalculationUsage()
2557
+
2558
+ if name is not None:
2559
+ self._set_name(name)
2560
+ if shortname is not None:
2561
+ self._set_name(shortname, short=True)
2562
+
2563
+
2564
+ class Enumeration(Usage):
2565
+ sysml_type = 'enumeration'
2566
+ """SysML v2 Enumeration definition.
2567
+
2568
+ Note: SysML v2 only has EnumerationDefinition, no EnumerationUsage.
2569
+
2570
+ Usage:
2571
+ Enumeration(name='Color') # enum def Color;
2572
+ """
2573
+ def __init__(self, definition=True, name=None, shortname=None):
2574
+ Usage.__init__(self)
2575
+ # EnumerationDefinition is the only form
2576
+ self.grammar = EnumerationDefinition()
2577
+
2578
+ if name is not None:
2579
+ self._set_name(name)
2580
+ if shortname is not None:
2581
+ self._set_name(shortname, short=True)
2582
+
2583
+
2584
+ class Allocation(Usage):
2585
+ sysml_type = 'allocation'
2586
+ """SysML v2 Allocation usage/definition.
2587
+
2588
+ Allocation represents mapping from one model element to another.
2589
+
2590
+ Usage:
2591
+ Allocation() # allocation ;
2592
+ Allocation(name='alloc1') # allocation alloc1;
2593
+ Allocation(definition=True, name='AllocSpec') # allocation def AllocSpec;
2594
+ """
2595
+ def __init__(self, definition=False, name=None, shortname=None):
2596
+ Usage.__init__(self)
2597
+ if definition:
2598
+ self.grammar = AllocationDefinition()
2599
+ else:
2600
+ self.grammar = AllocationUsage()
2601
+
2602
+ if name is not None:
2603
+ self._set_name(name)
2604
+ if shortname is not None:
2605
+ self._set_name(shortname, short=True)
2606
+
2607
+
2608
+ class Metadata(_NonOccurrenceUsage):
2609
+ sysml_type = 'metadata'
2610
+ """SysML v2 Metadata usage/definition.
2611
+
2612
+ Metadata attaches additional information to model elements.
2613
+
2614
+ Usage:
2615
+ Metadata() # metadata ;
2616
+ Metadata(name='tag1') # metadata tag1;
2617
+ Metadata(definition=True, name='AuthorMeta') # metadata def AuthorMeta;
2618
+ """
2619
+ def __init__(self, definition=False, name=None, shortname=None):
2620
+ Usage.__init__(self)
2621
+ if definition:
2622
+ self.grammar = MetadataDefinition()
2623
+ else:
2624
+ self.grammar = MetadataUsage()
2625
+
2626
+ if name is not None:
2627
+ self._set_name(name)
2628
+ if shortname is not None:
2629
+ self._set_name(shortname, short=True)
2630
+
2631
+
2632
+ class Rendering(Usage):
2633
+ sysml_type = 'rendering'
2634
+ """SysML v2 Rendering usage/definition.
2635
+
2636
+ Rendering specifies how views should be rendered.
2637
+
2638
+ Usage:
2639
+ Rendering() # rendering ;
2640
+ Rendering(name='myRender') # rendering myRender;
2641
+ Rendering(definition=True, name='DefRender') # rendering def DefRender;
2642
+ """
2643
+ def __init__(self, definition=False, name=None, shortname=None):
2644
+ Usage.__init__(self)
2645
+ if definition:
2646
+ self.grammar = RenderingDefinition()
2647
+ else:
2648
+ self.grammar = RenderingUsage()
2649
+
2650
+ if name is not None:
2651
+ self._set_name(name)
2652
+ if shortname is not None:
2653
+ self._set_name(shortname, short=True)
2654
+
2655
+
2656
+ class Individual(Usage):
2657
+ sysml_type = 'individual'
2658
+ """SysML v2 Individual usage/definition.
2659
+
2660
+ Individual represents a specific instance or occurrence.
2661
+
2662
+ Usage:
2663
+ Individual() # individual ;
2664
+ Individual(name='instance1') # individual instance1;
2665
+ Individual(definition=True, name='DefIndiv') # individual def DefIndiv;
2666
+ """
2667
+ def __init__(self, definition=False, name=None, shortname=None):
2668
+ Usage.__init__(self)
2669
+ if definition:
2670
+ self.grammar = IndividualDefinition()
2671
+ else:
2672
+ self.grammar = IndividualUsageSimple()
2673
+
2674
+ if name is not None:
2675
+ self._set_name(name)
2676
+ if shortname is not None:
2677
+ self._set_name(shortname, short=True)
2678
+
2679
+
2680
+ class FlowDef(Usage):
2681
+ sysml_type = 'flow'
2682
+ """SysML v2 FlowDefinition alternate form.
2683
+
2684
+ Note: This is the simpler 'flow def' form (distinct from FlowConnectionDefinition).
2685
+ Usage already provides Flow class for flowConnectionUsage/Definition.
2686
+
2687
+ Usage:
2688
+ FlowDef(name='DataStream') # flow def DataStream;
2689
+ """
2690
+ def __init__(self, name=None, shortname=None):
2691
+ Usage.__init__(self)
2692
+ self.grammar = FlowDefinition()
2693
+
2694
+ if name is not None:
2695
+ self._set_name(name)
2696
+ if shortname is not None:
2697
+ self._set_name(shortname, short=True)
2698
+
2699
+
2700
+ class View(Usage):
2701
+ sysml_type = 'view'
2702
+ """SysML v2 View usage/definition.
2703
+
2704
+ Views define how models are presented and filtered.
2705
+
2706
+ Usage:
2707
+ View() # view ;
2708
+ View(name='systemOverview') # view systemOverview;
2709
+ View(definition=True, name='SysView') # view def SysView;
2710
+ """
2711
+ def __init__(self, definition=False, name=None, shortname=None):
2712
+ Usage.__init__(self)
2713
+ if definition:
2714
+ self.grammar = ViewDefinition()
2715
+ else:
2716
+ self.grammar = ViewUsage()
2717
+
2718
+ if name is not None:
2719
+ self._set_name(name)
2720
+ if shortname is not None:
2721
+ self._set_name(shortname, short=True)
2722
+
2723
+
2724
+ class Viewpoint(_BehaviorUsage):
2725
+ sysml_type = 'viewpoint'
2726
+ """SysML v2 Viewpoint usage/definition.
2727
+
2728
+ Viewpoints specify viewing perspectives with stakeholder concerns.
2729
+
2730
+ Usage:
2731
+ Viewpoint() # viewpoint ;
2732
+ Viewpoint(name='stakeholderVP') # viewpoint stakeholderVP;
2733
+ Viewpoint(definition=True, name='VPDef') # viewpoint def VPDef;
2734
+ """
2735
+ def __init__(self, definition=False, name=None, shortname=None):
2736
+ Usage.__init__(self)
2737
+ if definition:
2738
+ self.grammar = ViewpointDefinition()
2739
+ else:
2740
+ self.grammar = ViewpointUsage()
2741
+
2742
+ if name is not None:
2743
+ self._set_name(name)
2744
+ if shortname is not None:
2745
+ self._set_name(shortname, short=True)
2746
+
2747
+
2748
+ class Concern(_BehaviorUsage):
2749
+ sysml_type = 'concern'
2750
+ """SysML v2 Concern usage/definition.
2751
+
2752
+ Concerns represent stakeholder concerns for viewpoints.
2753
+
2754
+ Usage:
2755
+ Concern() # concern ;
2756
+ Concern(name='security') # concern security;
2757
+ Concern(definition=True, name='Safety') # concern def Safety;
2758
+ """
2759
+ def __init__(self, definition=False, name=None, shortname=None):
2760
+ Usage.__init__(self)
2761
+ if definition:
2762
+ self.grammar = ConcernDefinition()
2763
+ else:
2764
+ self.grammar = ConcernUsage()
2765
+
2766
+ if name is not None:
2767
+ self._set_name(name)
2768
+ if shortname is not None:
2769
+ self._set_name(shortname, short=True)
2770
+
2771
+
2772
+ class Case(_BehaviorUsage):
2773
+ sysml_type = 'case'
2774
+ """SysML v2 Case usage/definition.
2775
+
2776
+ A case is a broad classifier for analysis, verification, and use cases.
2777
+
2778
+ Usage:
2779
+ Case() # case ;
2780
+ Case(name='scenario1') # case scenario1;
2781
+ Case(definition=True, name='CaseSpec') # case def CaseSpec;
2782
+ """
2783
+ def __init__(self, definition=False, name=None, shortname=None):
2784
+ Usage.__init__(self)
2785
+ if definition:
2786
+ self.grammar = CaseDefinition()
2787
+ else:
2788
+ self.grammar = CaseUsage()
2789
+
2790
+ if name is not None:
2791
+ self._set_name(name)
2792
+ if shortname is not None:
2793
+ self._set_name(shortname, short=True)
2794
+
2795
+
2796
+ class AnalysisCase(_BehaviorUsage):
2797
+ sysml_type = 'analysis'
2798
+ """SysML v2 AnalysisCase usage/definition.
2799
+
2800
+ Analysis cases represent analytical scenarios or studies.
2801
+
2802
+ Usage:
2803
+ AnalysisCase() # analysis ;
2804
+ AnalysisCase(name='thermal1') # analysis thermal1;
2805
+ AnalysisCase(definition=True, name='Thermal') # analysis def Thermal;
2806
+ """
2807
+ def __init__(self, definition=False, name=None, shortname=None):
2808
+ Usage.__init__(self)
2809
+ if definition:
2810
+ self.grammar = AnalysisCaseDefinition()
2811
+ else:
2812
+ self.grammar = AnalysisCaseUsage()
2813
+
2814
+ if name is not None:
2815
+ self._set_name(name)
2816
+ if shortname is not None:
2817
+ self._set_name(shortname, short=True)
2818
+
2819
+
2820
+ class VerificationCase(_BehaviorUsage):
2821
+ sysml_type = 'verification'
2822
+ """SysML v2 VerificationCase usage/definition.
2823
+
2824
+ Verification cases represent verification scenarios or tests.
2825
+
2826
+ Usage:
2827
+ VerificationCase() # verification ;
2828
+ VerificationCase(name='test1') # verification test1;
2829
+ VerificationCase(definition=True, name='Verify1') # verification case def Verify1;
2830
+ """
2831
+ def __init__(self, definition=False, name=None, shortname=None):
2832
+ Usage.__init__(self)
2833
+ if definition:
2834
+ self.grammar = VerificationCaseDefinition()
2835
+ else:
2836
+ self.grammar = VerificationCaseUsage()
2837
+
2838
+ if name is not None:
2839
+ self._set_name(name)
2840
+ if shortname is not None:
2841
+ self._set_name(shortname, short=True)
2842
+
2843
+
2844
+ class Reference(Usage):
2845
+ sysml_type = 'reference'
2846
+ def __init__(self, name=None, shortname=None, redefines=None):
2847
+ self.name = name if name else str(uuidlib.uuid4())
2848
+ self.children = []
2849
+ self.typedby = None
2850
+ self.grammar = None
2851
+ self.parent = None
2852
+ self.shortname = shortname
2853
+ self.redefines = redefines # for redefinition like ref :>> name :
2854
+ self.ref_type = None # type reference
2855
+
2856
+ def set_type(self, type_obj):
2857
+ """Set the type this reference points to.
2858
+
2859
+ Args:
2860
+ type_obj: Another element (Item, Part, etc.) to reference
2861
+ """
2862
+ self.ref_type = type_obj
2863
+ return self
2864
+
2865
+ def _set_typed_by(self, type_obj):
2866
+ """Alias for set_type for consistency with other classes."""
2867
+ return self.set_type(type_obj)
2868
+
2869
+ def dump(self):
2870
+ name_str = getattr(self, 'name', "") or ""
2871
+ type_str = ""
2872
+
2873
+ if self.ref_type:
2874
+ type_name = getattr(self.ref_type, 'name', str(self.ref_type))
2875
+ type_str = f" : {type_name}"
2876
+
2877
+ if self.redefines:
2878
+ return f"ref :>> {name_str}{type_str};"
2879
+
2880
+ return f"ref {name_str}{type_str};"
2881
+
2882
+
2883
+ class DefaultReference(Usage):
2884
+ def __init__(self):
2885
+ Usage.__init__(self)
2886
+ self.grammar = DefaultReferenceUsage()
2887
+
2888
+ def set_direction(self, direction):
2889
+ r = RefPrefix()
2890
+ if direction == "in":
2891
+ r.direction.isIn = True
2892
+ elif direction == "out":
2893
+ r.direction.isOut = True
2894
+ elif direction == "inout":
2895
+ r.direction.isInOut = True
2896
+ else:
2897
+ raise ValueError
2898
+ self.grammar.prefix = r
2899
+ return self
2900
+
2901
+ def usage_dump(self, child):
2902
+ # This is a usage.
2903
+
2904
+ self._ensure_body("definition")
2905
+
2906
+ # Add packaging
2907
+ package = {
2908
+ "name": "NonOccurrenceUsageElement",
2909
+ "ownedRelatedElement": self.grammar.get_definition(),
2910
+ }
2911
+
2912
+ if child == "DefinitionBody":
2913
+ package = {
2914
+ "name": "NonOccurrenceUsageMember",
2915
+ "prefix": None,
2916
+ "ownedRelatedElement": [package],
2917
+ }
2918
+
2919
+ package = {"name": "DefinitionBodyItem", "ownedRelationship": [package]}
2920
+ elif "PackageBody":
2921
+ package = {"name": "UsageElement", "ownedRelatedElement": package}
2922
+ package = {
2923
+ "name": "PackageMember",
2924
+ "ownedRelatedElement": package,
2925
+ "prefix": None,
2926
+ }
2927
+
2928
+ return package
2929
+
2930
+ def _get_definition(self, child=None):
2931
+ package = self.usage_dump(child)
2932
+
2933
+ if child is None:
2934
+ package = {
2935
+ "name": "PackageBodyElement",
2936
+ "ownedRelationship": [package],
2937
+ "prefix": None,
2938
+ }
2939
+
2940
+ # Add the typed by definition to the package output
2941
+ if self.typedby is not None:
2942
+ if child is None:
2943
+ package["ownedRelationship"].insert(
2944
+ 0, self.typedby._get_definition(child="PackageBody")
2945
+ )
2946
+ elif child == "PackageBody":
2947
+ package = [self.typedby._get_definition(child="PackageBody"), package]
2948
+ else:
2949
+ package["ownedRelationship"].insert(
2950
+ 0, self.typedby._get_definition(child=child)["ownedRelationship"][0]
2951
+ )
2952
+
2953
+ return package
2954
+
2955
+ # def dump(self, child=None):
2956
+ # package = self.usage_dump(child)
2957
+
2958
+ # if child is None:
2959
+ # package = {
2960
+ # "name": "PackageBodyElement",
2961
+ # "ownedRelationship": [package],
2962
+ # "prefix": None,
2963
+ # }
2964
+
2965
+ # # Add the typed by definition to the package output
2966
+ # if self.typedby is not None:
2967
+ # if child is None:
2968
+ # package["ownedRelationship"].insert(
2969
+ # 0, self.typedby.dump(child="PackageBody")
2970
+ # )
2971
+ # elif child == "PackageBody":
2972
+ # package = [self.typedby.dump(child="PackageBody"), package]
2973
+ # else:
2974
+ # package["ownedRelationship"].insert(
2975
+ # 0, self.typedby.dump(child=child)["ownedRelationship"][0]
2976
+ # )
2977
+
2978
+ # return package