diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a79260f..b92a01e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,6 +12,15 @@ consumers, and its portability (any SysML v2 tool, or an executing engine). - Never reuse an OMG standard-library package name. - `.kerml` only for KerML-only content; everything else is `.sysml`. +### Rendering and view vocabulary + +Use a standard filtered view wherever one exists; a library name is only an +optional shorter form of it. The extension libraries are for what standard +SysML cannot express: mixed diagrams, results from runs, and layout. A +proposal for new rendering or view vocabulary must show that no standard view +(a `GeneralView`, `GridView`, `InterconnectionView` or other standard view +with a filter) expresses the same diagram. + ## Documentation - A top-level `doc` that says NON-NORMATIVE and what the library is for. diff --git a/README.md b/README.md index 128bdf8..6efb352 100644 --- a/README.md +++ b/README.md @@ -24,8 +24,10 @@ non-normative OpenSysML library; other tools resolve it when given the | MigrationMetadata | MigrationMetadata.sysml | What a migration from another modelling language had to make up because the source left it unstated. | Any SysML v2 tool | | OOSEM | OOSEM.sysml | Object-Oriented Systems Engineering Method artefacts, viewpoints and product views. | Any SysML v2 tool | | OpenSysMLMathFunctions | OpenSysMLMathFunctions.kerml | exp, ln, log, atan2, ceiling and the Integer quotient the Kernel Function Library omits. | Executing engine | +| OpenSysMLRenderings | OpenSysMLRenderings.sysml | Renderings and views for case and mixed diagrams. `CaseView`/`asCaseDiagram` is a shorter way to write a standard `GeneralView` filtered on case-family metaclasses; the standard route is added by [OpenSysML#886](https://github.com/Open-MBEE/OpenSysML/pull/886). `MixedView`/`asMixedDiagram` is extension-only. | Any SysML v2 tool | | RandomFunctions | RandomFunctions.kerml | Seeded random draws: uniform, uniformInteger, triangular, normal. | Executing engine | | Simulation | Simulation.sysml | How a behavior is meant to be run: run count, draw resolution, clock, Monte Carlo. | Executing engine | +| StateActivity | StateActivity.kerml | `isActive`: a derived Boolean that is true while a state usage is in its state machine's active configuration. | Executing engine | | StateMachines | StateMachines.sysml | Pseudostates (choice, junction, shallow/deep history) the grammar has no production for. | Executing engine | | StateSpaceIntegration | StateSpaceIntegration.sysml | Fixed-step (Euler/RK4) running of StateSpaceRepresentation dynamics. | Executing engine | | Stochastic | Stochastic.sysml | Probability metadata for branches; a tool unaware of it runs the branches as an unweighted choice. | Executing engine | diff --git a/engine-contract.json b/engine-contract.json index 372004f..783385b 100644 --- a/engine-contract.json +++ b/engine-contract.json @@ -841,6 +841,41 @@ "internal/translate/migrate/opaque.go" ] }, + { + "name": "OpenSysMLRenderings", + "kind": "package", + "consumers": [ + "internal/ir/view/view.go" + ] + }, + { + "name": "OpenSysMLRenderings::CaseView", + "kind": "element", + "consumers": [ + "internal/ir/view/view.go" + ] + }, + { + "name": "OpenSysMLRenderings::MixedView", + "kind": "element", + "consumers": [ + "internal/ir/view/view.go" + ] + }, + { + "name": "OpenSysMLRenderings::asCaseDiagram", + "kind": "element", + "consumers": [ + "internal/ir/view/view.go" + ] + }, + { + "name": "OpenSysMLRenderings::asMixedDiagram", + "kind": "element", + "consumers": [ + "internal/ir/view/view.go" + ] + }, { "name": "RandomFunctions", "kind": "package", @@ -927,6 +962,24 @@ "internal/exec/simresults/simresults.go" ] }, + { + "name": "StateActivity", + "kind": "package", + "consumers": [ + "internal/semantic/semantics/state_activity.go" + ] + }, + { + "name": "StateActivity::isActive", + "kind": "element", + "consumers": [ + "internal/semantic/semantics/state_activity.go", + "internal/exec/runtime/state_activity.go", + "internal/semantic/resolve/featured.go", + "internal/translate/codegen/compile_rec.go", + "internal/check/passes/nonstandard_notation.go" + ] + }, { "name": "StateMachines", "kind": "package", diff --git a/libraries/DocumentQueries.sysml b/libraries/DocumentQueries.sysml index 12be0f2..141bc57 100644 --- a/libraries/DocumentQueries.sysml +++ b/libraries/DocumentQueries.sysml @@ -358,6 +358,10 @@ library package DocumentQueries { * small square on the part's border named beside it, or every port, * labelled name : Type. Minimal when absent. */ attribute ports : String[0..1]; + /* overlay: "verdicts", for a requirement diagram: runs the verification + * cases verifying each requirement and colours and labels it by their + * verdicts. Purely structural when absent. */ + attribute overlay : String[0..1]; ref source : Element[0..1]; } diff --git a/libraries/OpenSysMLRenderings.sysml b/libraries/OpenSysMLRenderings.sysml new file mode 100644 index 0000000..cdce24f --- /dev/null +++ b/libraries/OpenSysMLRenderings.sysml @@ -0,0 +1,19 @@ +library package OpenSysMLRenderings { + doc /* + * NON-NORMATIVE rendering vocabulary for case and mixed diagrams. + * Models using it are valid SysML v2 and depend on this OpenSysML library. + */ + + private import Views::*; + + rendering asCaseDiagram : GraphicalRendering[1] :> renderings; + rendering asMixedDiagram : GraphicalRendering[1] :> renderings; + + view def CaseView { + render asCaseDiagram; + } + + view def MixedView { + render asMixedDiagram; + } +} diff --git a/libraries/StateActivity.kerml b/libraries/StateActivity.kerml new file mode 100644 index 0000000..92f5f1f --- /dev/null +++ b/libraries/StateActivity.kerml @@ -0,0 +1,33 @@ +library package StateActivity { + doc + /* + * NON-NORMATIVE. This package is an OpenSysML extension, not part of the OMG KerML + * or SysML standard library: it declares a readable Boolean that tells whether a + * state usage is in the active configuration of its state machine, which neither + * States.sysml (StateAction, stateActions, substates) nor StatePerformances.kerml + * declares. The vendored OMG files are unmodified. + * + * A model that reads it writes `import StateActivity::*;` and then names the feature + * on a state usage, `fill.isActive`. The import is what resolves the name: a feature + * declared `featured by` a type is read by OpenSysML as a feature of every usage + * conforming to that type reached by a feature chain written where the feature is + * visible, a reading no other tool makes. Without the import the member is unresolved, + * exactly as in a conforming tool, and `nonstandard-notation` reports the import. + */ + + private import ScalarValues::Boolean; + private import States::StateAction; + + derived feature isActive : Boolean[1] featured by StateAction { + doc + /* + * True exactly while the featuring state is in the active configuration of its + * state machine when the expression reading it is evaluated: a composite state + * while any of its substates is, a state of a parallel region together with the + * states of its sibling regions, and a state during its own entry and exit + * behaviors. False during a transition's effect, whose source has been exited and + * whose target not yet entered, and for a state of a machine that has not started, + * has ended or is not executing. Derived: no behavior writes it. + */ + } +} diff --git a/libraries/SysMLValidation.sysml b/libraries/SysMLValidation.sysml index 493eed0..56ce94d 100644 --- a/libraries/SysMLValidation.sysml +++ b/libraries/SysMLValidation.sysml @@ -11,6 +11,7 @@ standard library package SysMLValidation { */ private import ControlFunctions::*; + private import SequenceFunctions::*; /* KerML: Feature */ @@ -104,4 +105,253 @@ standard library package SysMLValidation { element.nestedUsage->reject { in u : SysML::Usage; u istype SysML::PortUsage } ->forAll { in u : SysML::Usage; not u.isComposite } } + + constraint def validateFlowEndIsEnd { + doc /* isEnd */ + in element : KerML::FlowEnd; + element.isEnd + } + + constraint def validateFlowEndNestedFeature { + doc /* ownedFeature->size() = 1 */ + in element : KerML::FlowEnd; + size(element.ownedFeature) == 1 + } + + constraint def validateMetadataFeatureMetaclass { + doc /* type->selectByKind(Metaclass).size() = 1 */ + in element : KerML::MetadataFeature; + size(element.type->select { in t : KerML::Type; t istype KerML::Metaclass }) == 1 + } + + constraint def validateBindingConnectorIsBinary { + doc /* relatedFeature->size() = 2 */ + in element : KerML::BindingConnector; + size(element.relatedFeature) == 2 + } + + constraint def validateConnectorRelatedFeatures { + doc /* not isAbstract implies relatedFeature->size() >= 2 */ + in element : KerML::Connector; + not element.isAbstract implies size(element.relatedFeature) >= 2 + } + + constraint def validateFeatureEndNoDirection { + doc /* isEnd implies direction = null */ + in element : KerML::Feature; + element.isEnd implies isEmpty(element.direction) + } + + constraint def validateTypeOwnedMultiplicity { + doc /* ownedMember->selectByKind(Multiplicity)->size() <= 1 */ + in element : KerML::Type; + size(element.ownedMember->select { in m : KerML::Element; m istype KerML::Multiplicity }) <= 1 + } + + constraint def validateConnectionDefinitionIsSufficient { + doc /* isSufficient */ + in element : SysML::ConnectionDefinition; + element.isSufficient + } + + constraint def validateDefinitionVariationIsAbstract { + doc /* isVariation implies isAbstract */ + in element : SysML::Definition; + element.isVariation implies element.isAbstract + } + + constraint def validateUsageVariationIsAbstract { + doc /* isVariation implies isAbstract */ + in element : SysML::Usage; + element.isVariation implies element.isAbstract + } + + constraint def validateForLoopActionUsageLoopVariable { + doc /* ownedFeature->notEmpty() and ownedFeature->at(1).oclIsKindOf(ReferenceUsage) */ + in element : SysML::ForLoopActionUsage; + notEmpty(element.ownedFeature) and (element.ownedFeature#(1) istype SysML::ReferenceUsage) + } + + constraint def validateControlNodeOwningType { + doc /* owningType <> null and (owningType.oclIsKindOf(ActionDefinition) or owningType.oclIsKindOf(ActionUsage)) */ + in element : SysML::ControlNode; + element.owningType->exists { in t : KerML::Type; + t istype SysML::ActionDefinition or t istype SysML::ActionUsage } + } + + constraint def validatePortUsageIsReference { + doc /* owningType = null or not owningType.oclIsKindOf(PortDefinition) and not owningType.oclIsKindOf(PortUsage) implies isReference */ + in element : SysML::PortUsage; + (isEmpty(element.owningType) or + not element.owningType->exists { in t : KerML::Type; + t istype SysML::PortDefinition or t istype SysML::PortUsage }) + implies element.isReference + } + + constraint def validateUsageIsReferential { + doc /* direction <> null or isEnd or featuringType->isEmpty() implies isReference */ + in element : SysML::Usage; + (notEmpty(element.direction) or element.isEnd or isEmpty(element.featuringType)) + implies element.isReference + } + + constraint def validateCaseDefinitionSubjectParameterPosition { + doc /* input->notEmpty() and input->first() = subjectParameter */ + in element : SysML::CaseDefinition; + notEmpty(element.input) and element.input#(1) == element.subjectParameter + } + + constraint def validateCaseUsageSubjectParameterPosition { + doc /* input->notEmpty() and input->first() = subjectParameter */ + in element : SysML::CaseUsage; + notEmpty(element.input) and element.input#(1) == element.subjectParameter + } + + constraint def validateRequirementDefinitionSubjectParameterPosition { + doc /* input->notEmpty() and input->first() = subjectParameter */ + in element : SysML::RequirementDefinition; + notEmpty(element.input) and element.input#(1) == element.subjectParameter + } + + constraint def validateRequirementUsageSubjectParameterPosition { + doc /* input->notEmpty() and input->first() = subjectParameter */ + in element : SysML::RequirementUsage; + notEmpty(element.input) and element.input#(1) == element.subjectParameter + } + + constraint def validateOccurrenceUsageIsPortion { + doc /* portionKind <> null implies isPortion */ + in element : SysML::OccurrenceUsage; + notEmpty(element.portionKind) implies element.isPortion + } + + constraint def validateOccurrenceUsagePortionKind { + doc /* portionKind <> null implies owningType <> null and (owningType.oclIsKindOf(OccurrenceDefinition) or owningType.oclIsKindOf(OccurrenceUsage)) */ + in element : SysML::OccurrenceUsage; + notEmpty(element.portionKind) implies + element.owningType->exists { in t : KerML::Type; + t istype SysML::OccurrenceDefinition or t istype SysML::OccurrenceUsage } + } + + constraint def validateOccurrenceUsageIndividualDefinition { + doc /* occurrenceDefinition->selectByKind(OccurrenceDefinition)->select(isIndividual).size() <= 1 */ + in element : SysML::OccurrenceUsage; + size(element.occurrenceDefinition + ->select { in d : KerML::Class; d istype SysML::OccurrenceDefinition } + ->select { in d : SysML::OccurrenceDefinition; d.isIndividual }) <= 1 + } + + constraint def validateOccurrenceUsageIndividualUsage { + doc /* isIndividual implies individualDefinition <> null */ + in element : SysML::OccurrenceUsage; + element.isIndividual implies notEmpty(element.individualDefinition) + } + + constraint def validatePartUsagePartDefinition { + doc /* partDefinition->notEmpty() */ + in element : SysML::PartUsage; + notEmpty(element.partDefinition) + } + + constraint def validateTypeUnioningTypesNotSelf { + doc /* unioningType->excludes(self) */ + in element : KerML::Type; + excludes(element.unioningType, element) + } + + constraint def validateTypeIntersectingTypesNotSelf { + doc /* intersectingType->excludes(self) */ + in element : KerML::Type; + excludes(element.intersectingType, element) + } + + constraint def validateTypeDifferencingTypesNotSelf { + doc /* differencingType->excludes(self) */ + in element : KerML::Type; + excludes(element.differencingType, element) + } + + constraint def validateAssociationEndTypes { + doc /* ownedEndFeature->forAll(type->size() = 1) */ + in element : KerML::Association; + element.ownedEndFeature->forAll { in f : KerML::Feature; size(f.type) == 1 } + } + + constraint def validateMetadataFeatureMetaclassNotAbstract { + doc /* not metaclass.isAbstract */ + in element : KerML::MetadataFeature; + element.metaclass->forAll { in c : KerML::Metaclass; not c.isAbstract } + } + + /* KerML: Specializations */ + + constraint def validateSpecializationSpecificNotConjugated { + doc /* not specific.isConjugated */ + in element : KerML::Specialization; + not element.specific.isConjugated + } + + constraint def validateBehaviorSpecialization { + doc /* ownedSpecialization.general->forAll(not oclIsKindOf(Structure)) */ + in element : KerML::Behavior; + element.ownedSpecialization->forAll { in s : KerML::Specialization; + not (s.general istype KerML::Structure) } + } + + constraint def validateStructureSpecialization { + doc /* ownedSpecialization.general->forAll(not oclIsKindOf(Behavior)) */ + in element : KerML::Structure; + element.ownedSpecialization->forAll { in s : KerML::Specialization; + not (s.general istype KerML::Behavior) } + } + + constraint def validateClassSpecialization { + doc /* ownedSpecialization.general->forAll(not oclIsKindOf(DataType)) and not oclIsKindOf(Association) implies ownedSpecialization.general->forAll(not oclIsKindOf(Association)) */ + in element : KerML::Class; + element.ownedSpecialization->forAll { in s : KerML::Specialization; + not (s.general istype KerML::DataType) } and + ((element istype KerML::Association) or + element.ownedSpecialization->forAll { in s : KerML::Specialization; + not (s.general istype KerML::Association) }) + } + + constraint def validateDataTypeSpecialization { + doc /* ownedSpecialization.general->forAll(not oclIsKindOf(Class) and not oclIsKindOf(Association)) */ + in element : KerML::DataType; + element.ownedSpecialization->forAll { in s : KerML::Specialization; + not (s.general istype KerML::Class) and not (s.general istype KerML::Association) } + } + + /* KerML: Subsetting and redefinition */ + + constraint def validateFeatureOwnedReferenceSubsetting { + doc /* ownedSubsetting->selectByKind(ReferenceSubsetting)->size() <= 1 */ + in element : KerML::Feature; + size(element.ownedSubsetting->select { in s : KerML::Subsetting; + s istype KerML::ReferenceSubsetting }) <= 1 + } + + constraint def validateFeatureOwnedCrossSubsetting { + doc /* ownedSubsetting->selectByKind(CrossSubsetting)->size() <= 1 */ + in element : KerML::Feature; + size(element.ownedSubsetting->select { in s : KerML::Subsetting; + s istype KerML::CrossSubsetting }) <= 1 + } + + /* SysML: variations */ + + constraint def validateUsageVariationSpecialization { + doc /* isVariation implies not ownedSpecialization.specific->exists( oclIsKindOf(Definition) and oclAsType(Definition).isVariation or oclIsKindOf(Usage) and oclAsType(Usage).isVariation) */ + in element : SysML::Usage; + element.isVariation implies not element.ownedSpecialization->exists { in s : KerML::Specialization; + (s.general istype SysML::Definition and (s.general as SysML::Definition).isVariation) or + (s.general istype SysML::Usage and (s.general as SysML::Usage).isVariation) } + } + + constraint def validateDefinitionVariationSpecialization { + doc /* isVariation implies not ownedSpecialization.specific->exists( oclIsKindOf(Definition) and oclAsType(Definition).isVariation) */ + in element : SysML::Definition; + element.isVariation implies not element.ownedSpecialization->exists { in s : KerML::Specialization; + s.general istype SysML::Definition and (s.general as SysML::Definition).isVariation } + } }