diff --git a/capellambse/extensions/reqif/_requirements.py b/capellambse/extensions/reqif/_requirements.py index 7efac7a4..61ea2667 100644 --- a/capellambse/extensions/reqif/_requirements.py +++ b/capellambse/extensions/reqif/_requirements.py @@ -202,8 +202,8 @@ def __str__(self) -> str: class EnumerationDataTypeDefinition(DataTypeDefinition): """An enumeration data type definition for requirement types.""" - values = m.DirectProxyAccessor( - EnumValue, aslist=m.ElementList, single_attr="long_name" + values = m.Containment["EnumValue"]( + "specifiedValues", (NS, "EnumValue"), single_attr="long_name" ) diff --git a/capellambse/metamodel/__init__.py b/capellambse/metamodel/__init__.py index e18c93ae..39462abc 100644 --- a/capellambse/metamodel/__init__.py +++ b/capellambse/metamodel/__init__.py @@ -31,7 +31,7 @@ ), "available_in_states", ) -m.ModelElement.property_value_packages = m.DirectProxyAccessor( +m.ModelElement.property_value_pkgs = m.DirectProxyAccessor( capellacore.PropertyValuePkg, aslist=m.ElementList, mapkey="name", diff --git a/capellambse/metamodel/activity.py b/capellambse/metamodel/activity.py index 57402342..e6a0b232 100644 --- a/capellambse/metamodel/activity.py +++ b/capellambse/metamodel/activity.py @@ -1,5 +1,245 @@ # SPDX-FileCopyrightText: Copyright DB InfraGO AG # SPDX-License-Identifier: Apache-2.0 +import enum + +import capellambse.model as m + +from . import behavior, modellingcore from . import namespaces as ns NS = ns.ACTIVITY + + +@m.stringy_enum +@enum.unique +class ObjectNodeKind(enum.Enum): + """The behavior type of the object node with respect to incoming data.""" + + UNSPECIFIED = "Unspecified" + """Used when incoming object node management policy is not specified.""" + NO_BUFFER = "NoBuffer" + """Discard incoming tokens if they are refused. + + When the "nobuffer" stereotype is applied to object nodes, tokens + arriving at the node are discarded if they are refused by outgoing + edges, or refused by actions for object nodes that are input pins. + """ + OVERWRITE = "Overwrite" + """Incoming tokens may overwrite existing ones. + + When the "overwrite" stereotype is applied to object nodes, a token + arriving at a full object node replaces the ones already there. A + full object node has as many tokens as allowed by its upper bound. + """ + + +@m.stringy_enum +@enum.unique +class ObjectNodeOrderingKind(enum.Enum): + """Indicates queuing order within a node.""" + + FIFO = "FIFO" + """First In First Out ordering.""" + LIFO = "LIFO" + """Last In First Out ordering.""" + ORDERED = "ordered" + """Indicates that object node tokens are ordered.""" + UNORDERED = "unordered" + """Indicates that object node tokens are unordered.""" + + +class AbstractActivity( + behavior.AbstractBehavior, modellingcore.TraceableElement, abstract=True +): + is_read_only = m.BoolPOD("isReadOnly") + is_single_execution = m.BoolPOD("isSingleExecution") + nodes = m.Containment["ActivityNode"]("ownedNodes", (NS, "ActivityNode")) + edges = m.Containment["ActivityEdge"]("ownedEdges", (NS, "ActivityEdge")) + groups = m.Containment["ActivityGroup"]( + "ownedGroups", (NS, "ActivityGroup") + ) + + +class ExceptionHandler(modellingcore.ModelElement, abstract=True): + protected_node = m.Single["ExecutableNode"]( + m.Association((NS, "ExecutableNode"), "protectedNodes") + ) + handler_body = m.Single["ExecutableNode"]( + m.Association((NS, "ExecutableNode"), "handlerBody") + ) + exception_input = m.Single["ObjectNode"]( + m.Association((NS, "ObjectNode"), "exceptionInput") + ) + exception_types = m.Association["modellingcore.AbstractType"]( + (ns.MODELLINGCORE, "AbstractType"), "exceptionTypes" + ) + + +class ActivityGroup(modellingcore.ModelElement, abstract=True): + super_group = m.Association["ActivityGroup"]( + (NS, "ActivityGroup"), "superGroup" + ) + sub_groups = m.Containment["ActivityGroup"]( + "subGroups", (NS, "ActivityGroup") + ) + nodes = m.Containment["ActivityNode"]("ownedNodes", (NS, "ActivityNode")) + edges = m.Containment["ActivityEdge"]("ownedEdges", (NS, "ActivityEdge")) + + +class InterruptibleActivityRegion(ActivityGroup, abstract=True): + interrupting_edges = m.Association["ActivityEdge"]( + (NS, "ActivityEdge"), "interruptingEdges" + ) + + +class ActivityEdge(modellingcore.AbstractRelationship, abstract=True): + rate_kind = m.EnumPOD("kindOfRate", modellingcore.RateKind) + rate = m.Containment["modellingcore.ValueSpecification"]( + "rate", (ns.MODELLINGCORE, "ValueSpecification") + ) + probability = m.Containment["modellingcore.ValueSpecification"]( + "probability", (ns.MODELLINGCORE, "ValueSpecification") + ) + target = m.Single["ActivityNode"]( + m.Association((NS, "ActivityNode"), "target") + ) + source = m.Single["ActivityNode"]( + m.Association((NS, "ActivityNode"), "source") + ) + guard = m.Containment["modellingcore.ValueSpecification"]( + "guard", (ns.MODELLINGCORE, "ValueSpecification") + ) + weight = m.Containment["modellingcore.ValueSpecification"]( + "weight", (ns.MODELLINGCORE, "ValueSpecification") + ) + interrupts = m.Association["InterruptibleActivityRegion"]( + (NS, "InterruptibleActivityRegion"), "interrupts" + ) + + +class ControlFlow(ActivityEdge, abstract=True): + """An edge that starts an activity node after the previous one finished.""" + + +class ObjectFlow(ActivityEdge, abstract=True): + """Models the flow of values to or from object nodes.""" + + is_multicast = m.BoolPOD("isMulticast") + is_multireceive = m.BoolPOD("isMultireceive") + transformation = m.Single["behavior.AbstractBehavior"]( + m.Association((ns.BEHAVIOR, "AbstractBehavior"), "transformation") + ) + selection = m.Single["behavior.AbstractBehavior"]( + m.Association((ns.BEHAVIOR, "AbstractBehavior"), "selection") + ) + + +class ActivityPartition( + ActivityGroup, modellingcore.AbstractNamedElement, abstract=True +): + is_dimension = m.BoolPOD("isDimension") + is_external = m.BoolPOD("isExternal") + represented_element = m.Single["modellingcore.AbstractType"]( + m.Association((ns.MODELLINGCORE, "AbstractType"), "representedElement") + ) + + +class ActivityExchange(modellingcore.AbstractInformationFlow, abstract=True): + pass + + +class ActivityNode(modellingcore.AbstractNamedElement, abstract=True): + pass + + +class ExecutableNode(ActivityNode, abstract=True): + handlers = m.Containment["ExceptionHandler"]( + "ownedHandlers", (NS, "ExceptionHandler") + ) + + +class AbstractAction( + ExecutableNode, modellingcore.AbstractNamedElement, abstract=True +): + local_precondition = m.Single["modellingcore.AbstractConstraint"]( + m.Containment( + "localPrecondition", (ns.MODELLINGCORE, "AbstractConstraint") + ) + ) + local_postcondition = m.Single["modellingcore.AbstractConstraint"]( + m.Containment( + "localPostcondition", (ns.MODELLINGCORE, "AbstractConstraint") + ) + ) + context = m.Association["modellingcore.AbstractType"]( + (ns.MODELLINGCORE, "AbstractType"), "context" + ) + inputs = m.Containment["InputPin"]("inputs", (NS, "InputPin")) + outputs = m.Containment["OutputPin"]("outputs", (NS, "OutputPin")) + + +class StructuredActivityNode(ActivityGroup, AbstractAction, abstract=True): + pass + + +class AcceptEventAction(AbstractAction, abstract=True): + is_unmarshall = m.BoolPOD("isUnmarshall") + result = m.Containment["OutputPin"]("result", (NS, "OutputPin")) + + +class InvocationAction(AbstractAction, abstract=True): + arguments = m.Containment["InputPin"]("arguments", (NS, "InputPin")) + + +class SendSignalAction(InvocationAction, abstract=True): + target = m.Single["InputPin"](m.Containment("target", (NS, "InputPin"))) + signal = m.Single["behavior.AbstractSignal"]( + m.Association((ns.BEHAVIOR, "AbstractSignal"), "signal") + ) + + +class CallAction(InvocationAction, abstract=True): + results = m.Containment["OutputPin"]("results", (NS, "OutputPin")) + + +class CallBehaviorAction(CallAction, abstract=True): + behavior = m.Single["behavior.AbstractBehavior"]( + m.Association((ns.BEHAVIOR, "AbstractBehavior"), "behavior") + ) + + +class ObjectNode( + ActivityNode, modellingcore.AbstractTypedElement, abstract=True +): + is_control_type = m.BoolPOD("isControlType") + node_kind = m.EnumPOD("kindOfNode", ObjectNodeKind) + ordering = m.EnumPOD("ordering", ObjectNodeOrderingKind) + upper_bound = m.Containment["modellingcore.ValueSpecification"]( + "upperBound", (ns.MODELLINGCORE, "ValueSpecification") + ) + in_state = m.Association["modellingcore.IState"]( + (ns.MODELLINGCORE, "IState"), "inState" + ) + selection = m.Single["behavior.AbstractBehavior"]( + m.Association((ns.BEHAVIOR, "AbstractBehavior"), "selection") + ) + + +class Pin(ObjectNode, abstract=True): + is_control = m.BoolPOD("isControl") + + +class InputPin(Pin, abstract=True): + input_evaluation_action = m.Single["AbstractAction"]( + m.Association((NS, "AbstractAction"), "inputEvaluationAction") + ) + + +class ValuePin(InputPin, abstract=True): + value = m.Single["modellingcore.ValueSpecification"]( + m.Containment("value", (ns.MODELLINGCORE, "ValueSpecification")) + ) + + +class OutputPin(Pin, abstract=True): + pass diff --git a/capellambse/metamodel/behavior.py b/capellambse/metamodel/behavior.py index d7ddadd6..73db2383 100644 --- a/capellambse/metamodel/behavior.py +++ b/capellambse/metamodel/behavior.py @@ -1,5 +1,74 @@ # SPDX-FileCopyrightText: Copyright DB InfraGO AG # SPDX-License-Identifier: Apache-2.0 +from __future__ import annotations + +import capellambse.model as m + +from . import modellingcore from . import namespaces as ns NS = ns.BEHAVIOR + + +class AbstractBehavior(modellingcore.AbstractNamedElement, abstract=True): + """Abstract base class for behaviors.""" + + is_control_operator = m.BoolPOD("isControlOperator") + parameter_sets = m.Containment["modellingcore.AbstractParameterSet"]( + "ownedParameterSet", (ns.MODELLINGCORE, "AbstractParameterSet") + ) + parameters = m.Containment["modellingcore.AbstractParameter"]( + "ownedParameter", (ns.MODELLINGCORE, "AbstractParameter") + ) + + +class AbstractSignal(modellingcore.AbstractType, abstract=True): + """Abstract base class for signals.""" + + +class AbstractEvent(modellingcore.AbstractType, abstract=True): + """Specification of an occurrence that may trigger effects.""" + + +class AbstractTimeEvent(AbstractEvent, abstract=True): + """A point in time. + + A time event specifies a point in time by an expression. The + expression might be absolute or might be relative to some other + point in time. + """ + + is_relative = m.BoolPOD("isRelative") + when = m.Single["TimeExpression"]( + m.Association((NS, "TimeExpression"), "when") + ) + + +class AbstractMessageEvent(AbstractEvent, abstract=True): + """The receipt by an object of either a call or a signal.""" + + +class AbstractSignalEvent(AbstractMessageEvent, abstract=True): + """The receipt of an asynchronous signal. + + A signal event may cause a response, such as a state machine + transition as specified in the classifier behavior of the classifier + that specified the receiver object, if the signal referenced by the + send request is mentioned in a reception owned or inherited by the + classifier that specified the receiver object. + """ + + signal = m.Single["AbstractSignal"]( + m.Association((NS, "AbstractSignal"), "signal") + ) + + +class TimeExpression(modellingcore.ValueSpecification, abstract=True): + """A specification of a point in time.""" + + observations = m.Association["modellingcore.AbstractNamedElement"]( + (ns.MODELLINGCORE, "AbstractNamedElement"), "observations" + ) + expressions = m.Association["modellingcore.ValueSpecification"]( + (ns.MODELLINGCORE, "ValueSpecification"), "expression" + ) diff --git a/capellambse/metamodel/capellacommon.py b/capellambse/metamodel/capellacommon.py index 1b0cc714..2cee80c1 100644 --- a/capellambse/metamodel/capellacommon.py +++ b/capellambse/metamodel/capellacommon.py @@ -4,168 +4,296 @@ from __future__ import annotations +import enum import typing as t +import warnings import capellambse.model as m -from . import capellacore, modellingcore +from . import behavior, capellacore, modellingcore from . import namespaces as ns NS = ns.CAPELLACOMMON -class AbstractStateRealization(m.ModelElement): ... +@m.stringy_enum +@enum.unique +class ChangeEventKind(enum.Enum): + WHEN = "WHEN" -class TransfoLink(m.ModelElement): ... +@m.stringy_enum +@enum.unique +class TimeEventKind(enum.Enum): + AT = "AT" + """Trigger at a specific time. + An absolute time trigger is specified with the keyword 'at' followed + by an expression that evaluates to a time value, such as 'Jan. 1, + 2000, Noon'. + """ + AFTER = "AFTER" + """Trigger after a relative time duration has passed. -class CapabilityRealizationInvolvement(m.ModelElement): ... + A relative time trigger is specified with the keyword 'after' + followed by an expression that evaluates to a time value, such as + 'after (5 seconds)'. + """ -class Region(m.ModelElement): - """A region inside a state machine or state/mode.""" +@m.stringy_enum +@enum.unique +class TransitionKind(enum.Enum): + INTERNAL = "internal" + LOCAL = "local" + EXTERNAL = "external" + + +class AbstractCapabilityPkg(capellacore.Structure, abstract=True): + pass + + +class GenericTrace(capellacore.Trace): + key_value_pairs = m.Containment["capellacore.KeyValue"]( + "keyValuePairs", (ns.CAPELLACORE, "KeyValue") + ) + + +class TransfoLink(GenericTrace): + pass - _xmltag = "ownedRegions" - states: m.Accessor - modes: m.Accessor - transitions: m.Accessor +class JustificationLink(GenericTrace): + pass -class AbstractStateMode(m.ModelElement): - """Common code for states and modes.""" +class CapabilityRealizationInvolvement(capellacore.Involvement): + pass + +class CapabilityRealizationInvolvedElement( + capellacore.InvolvedElement, abstract=True +): + pass + + +class StateMachine(capellacore.CapellaElement, behavior.AbstractBehavior): + regions = m.Containment["Region"]("ownedRegions", (NS, "Region")) + connection_points = m.Containment["Pseudostate"]( + "ownedConnectionPoints", (NS, "Pseudostate") + ) + + +class Region(capellacore.NamedElement): + _xmltag = "ownedRegions" + + states = m.Containment["AbstractState"]( + "ownedStates", (NS, "AbstractState") + ) + modes = m.DeprecatedAccessor["AbstractState"]("states") + transitions = m.Containment["StateTransition"]( + "ownedTransitions", (NS, "StateTransition") + ) + involved_states = m.Association["AbstractState"]( + (NS, "AbstractState"), "involvedStates" + ) + + +class AbstractState( + capellacore.NamedElement, modellingcore.IState, abstract=True +): _xmltag = "ownedStates" - regions = m.DirectProxyAccessor(Region, aslist=m.ElementList) + realized_states = m.Allocation["AbstractState"]( + "ownedAbstractStateRealizations", + (NS, "AbstractStateRealization"), + (NS, "AbstractState"), + attr="targetElement", + backattr="sourceElement", + ) + realizing_states = m.Backref["AbstractState"]( + (NS, "AbstractState"), "realized_states" + ) + incoming_transitions = m.Backref["StateTransition"]( + (NS, "StateTransition"), "target" + ) + outgoing_transitions = m.Backref["StateTransition"]( + (NS, "StateTransition"), "source" + ) + + +class State(AbstractState): + """A situation during which some invariant condition holds. + + A condition of a system or element, as defined by some of its + properties, which can enable system behaviors and/or structure to + occur. + + Note: The enabled behavior may include no actions, such as + associated with a wait state. Also, the condition that defines the + state may be dependent on one or more previous states. + """ + + regions = m.Containment["Region"]("ownedRegions", (NS, "Region")) + connection_points = m.Containment["Pseudostate"]( + "ownedConnectionPoints", (NS, "Pseudostate") + ) + entry = m.Association["behavior.AbstractEvent"]( + (ns.BEHAVIOR, "AbstractEvent"), "entry" + ) + entries = m.DeprecatedAccessor["behavior.AbstractEvent"]("entry") + do_activity = m.Association["behavior.AbstractEvent"]( + (ns.BEHAVIOR, "AbstractEvent"), "doActivity" + ) + exit = m.Association["behavior.AbstractEvent"]( + (ns.BEHAVIOR, "AbstractEvent"), "exit" + ) + exits = m.DeprecatedAccessor["behavior.AbstractEvent"]("exit") + state_invariant = m.Containment["modellingcore.AbstractConstraint"]( + "stateInvariant", (ns.MODELLINGCORE, "AbstractConstraint") + ) + + +class Mode(State): + """Characterizes an expected behavior at a point in time. + + A Mode characterizes an expected behaviour through the set of + functions or elements available at a point in time. + """ + + +class FinalState(State): + """Special state signifying that the enclosing region is completed. + + If the enclosing region is directly contained in a state machine and + all other regions in the state machine also are completed, then it + means that the entire state machine is completed. + """ + + +class StateTransition(capellacore.NamedElement, capellacore.Relationship): + """A directed relationship between a source and target vertex. + + It may be part of a compound transition, which takes the state + machine from one state configuration to another, representing the + complete response of the state machine to an occurrence of an event + of a particular type. + """ + _xmltag = "ownedTransitions" -class State(AbstractStateMode): - """A state.""" + kind = m.EnumPOD("kind", TransitionKind) + trigger_description = m.StringPOD("triggerDescription") + guard = m.Association["capellacore.Constraint"]( + (ns.CAPELLACORE, "Constraint"), "guard" + ) + source = m.Single["AbstractState"]( + m.Association((NS, "AbstractState"), "source") + ) + target = m.Single["AbstractState"]( + m.Association((NS, "AbstractState"), "target") + ) + destination = m.DeprecatedAccessor["AbstractState"]("target") + effect = m.Association["behavior.AbstractEvent"]( + (ns.BEHAVIOR, "AbstractEvent"), "effect" + ) + effects = m.DeprecatedAccessor["behavior.AbstractEvent"]("effect") + triggers = m.Association["behavior.AbstractEvent"]( + (ns.BEHAVIOR, "AbstractEvent"), "triggers" + ) + realized_transitions = m.Allocation["StateTransition"]( + "ownedStateTransitionRealizations", + (NS, "StateTransitionRealization"), + (NS, "StateTransition"), + attr="targetElement", + backattr="sourceElement", + ) - entries = m.Association(m.ModelElement, "entry") - do_activity = m.Association(m.ModelElement, "doActivity") - exits = m.Association(m.ModelElement, "exit") - incoming_transitions = m.Accessor - outgoing_transitions = m.Accessor +class Pseudostate(AbstractState, abstract=True): + pass - functions: m.Accessor +class InitialPseudoState(Pseudostate): + pass -class Mode(AbstractStateMode): - """A mode.""" +class JoinPseudoState(Pseudostate): + pass -class DeepHistoryPseudoState(AbstractStateMode): - """A deep history pseudo state.""" +class ForkPseudoState(Pseudostate): + pass -class FinalState(AbstractStateMode): - """A final state.""" +class ChoicePseudoState(Pseudostate): + pass -class ForkPseudoState(AbstractStateMode): - """A fork pseudo state.""" +class TerminatePseudoState(Pseudostate): + pass -class InitialPseudoState(AbstractStateMode): - """An initial pseudo state.""" +class AbstractStateRealization(capellacore.Allocation): + pass -class JoinPseudoState(AbstractStateMode): - """A join pseudo state.""" +class StateTransitionRealization(capellacore.Allocation): + pass -class ShallowHistoryPseudoState(AbstractStateMode): - """A shallow history pseudo state.""" +class ShallowHistoryPseudoState(Pseudostate): + pass -class TerminatePseudoState(AbstractStateMode): - """A terminate pseudo state.""" +class DeepHistoryPseudoState(Pseudostate): + pass -class StateMachine(m.ModelElement): - """A state machine.""" - _xmltag = "ownedStateMachines" +class EntryPointPseudoState(Pseudostate): + pass - regions = m.DirectProxyAccessor(Region, aslist=m.ElementList) +class ExitPointPseudoState(Pseudostate): + pass -class StateTransition(m.ModelElement): - r"""A transition between :class:`State`\ s or :class:`Mode`\ s.""" - _xmltag = "ownedTransitions" +class StateEventRealization(capellacore.Allocation): + pass + + +class StateEvent( + capellacore.NamedElement, behavior.AbstractEvent, abstract=True +): + expression = m.Association["capellacore.Constraint"]( + (ns.CAPELLACORE, "Constraint"), "expression" + ) + realized_events = m.Allocation["StateEvent"]( + "ownedStateEventRealizations", + (NS, "StateEventRealization"), + (NS, "StateEvent"), + attr="targetElement", + backattr="sourceElement", + ) + + +class ChangeEvent(StateEvent): + kind = m.EnumPOD("kind", ChangeEventKind) + + +class TimeEvent(StateEvent): + kind = m.EnumPOD("kind", TimeEventKind) + + +if not t.TYPE_CHECKING: + + def __getattr__(name): + if name == "AbstractStateMode": + warnings.warn( + "AbstractStateMode has been renamed to AbstractState", + DeprecationWarning, + stacklevel=2, + ) + return AbstractState - source = m.Single(m.Association(m.ModelElement, "source")) - target = m.Single(m.Association(m.ModelElement, "target")) - destination = m.DeprecatedAccessor[t.Any]("target") - triggers = m.Association(m.ModelElement, "triggers") - effects = m.Association(m.ModelElement, "effect") - guard = m.Single(m.Association(capellacore.Constraint, "guard")) - - -class GenericTrace(modellingcore.TraceableElement): - """A trace between two elements.""" - - @property - def name(self) -> str: # type: ignore[override] - """Return the name.""" - direction = "" - if self.target is not None: - direction = f" to {self.target.name} ({self.target.uuid})" - - return f"[{type(self).__name__}]{direction}" - - -AbstractStateMode.realized_states = m.Allocation( - None, # FIXME fill in tag - AbstractStateRealization, - attr="targetElement", -) -for cls in [ - State, - Mode, - DeepHistoryPseudoState, - FinalState, - ForkPseudoState, - InitialPseudoState, - JoinPseudoState, - ShallowHistoryPseudoState, - TerminatePseudoState, -]: - cls.realizing_states = m.Backref(cls, "realized_states") - -for cls in [ - State, - Mode, - DeepHistoryPseudoState, - FinalState, - ForkPseudoState, - JoinPseudoState, - ShallowHistoryPseudoState, - TerminatePseudoState, -]: - cls.incoming_transitions = m.Backref(StateTransition, "destination") - -for cls in [ - State, - Mode, - DeepHistoryPseudoState, - ForkPseudoState, - InitialPseudoState, - JoinPseudoState, - ShallowHistoryPseudoState, -]: - cls.outgoing_transitions = m.Backref(StateTransition, "source") - -Region.states = m.Containment(AbstractStateMode._xmltag) -Region.modes = m.DirectProxyAccessor(Mode, aslist=m.ElementList) -Region.transitions = m.DirectProxyAccessor( - StateTransition, aslist=m.ElementList -) -m.ModelElement.traces = m.DirectProxyAccessor( - GenericTrace, aslist=m.ElementList -) + raise AttributeError(name) diff --git a/capellambse/metamodel/capellacore.py b/capellambse/metamodel/capellacore.py index a1a6818d..9ee3045d 100644 --- a/capellambse/metamodel/capellacore.py +++ b/capellambse/metamodel/capellacore.py @@ -2,171 +2,366 @@ # SPDX-License-Identifier: Apache-2.0 from __future__ import annotations +import enum import typing as t import capellambse.model as m +from . import modellingcore from . import namespaces as ns +if t.TYPE_CHECKING: + from . import information # noqa: F401 + NS = ns.CAPELLACORE -class Constraint(m.ModelElement): - """A constraint.""" +@m.stringy_enum +@enum.unique +class VisibilityKind(enum.Enum): + """The possibilities regarding the visibility of a feature of a class.""" + + UNSET = "UNSET" + """Visibility is not specified.""" + PUBLIC = "PUBLIC" + """The feature offers public access.""" + PROTECTED = "PROTECTED" + """The feature offers visibility only to children of the class.""" + PRIVATE = "PRIVATE" + """The feature is only visible/accessible from the class itself.""" + PACKAGE = "PACKAGE" + """The feature is accessible from any element within the same package.""" + + +class CapellaElement( + modellingcore.TraceableElement, + modellingcore.PublishableElement, + abstract=True, +): + summary = m.StringPOD("summary") + """Summary of the element.""" + description = m.HTMLStringPOD("description") + """Description of the Capella element.""" + review = m.StringPOD("review") + """Review description on the Capella element.""" + + property_values = m.Containment["AbstractPropertyValue"]( + "ownedPropertyValues", + (NS, "AbstractPropertyValue"), + mapkey="name", + mapvalue="value", + ) + enumeration_property_types = m.Containment["EnumerationPropertyType"]( + "ownedEnumerationPropertyTypes", (NS, "EnumerationPropertyType") + ) + applied_property_values = m.Association["AbstractPropertyValue"]( + (NS, "AbstractPropertyValue"), "appliedPropertyValues" + ) + property_value_groups = m.Containment["PropertyValueGroup"]( + "ownedPropertyValueGroups", (NS, "PropertyValueGroup") + ) + applied_property_value_groups = m.Association["PropertyValueGroup"]( + (NS, "PropertyValueGroup"), "appliedPropertyValueGroups" + ) + status = m.Single["EnumerationPropertyLiteral"]( + m.Association((NS, "EnumerationPropertyLiteral"), "status") + ) + features = m.Association["EnumerationPropertyLiteral"]( + (NS, "EnumerationPropertyLiteral"), "features" + ) + + +class NamedElement( + modellingcore.AbstractNamedElement, CapellaElement, abstract=True +): + pass + + +class Relationship( + modellingcore.AbstractRelationship, CapellaElement, abstract=True +): + pass + + +class Namespace(NamedElement, abstract=True): + traces = m.Containment["Trace"]("ownedTraces", (NS, "Trace")) + naming_rules = m.Containment["NamingRule"]( + "namingRules", (NS, "NamingRule") + ) + + +class NamedRelationship(Relationship, NamedElement, abstract=True): + naming_rules = m.Containment["NamingRule"]( + "namingRules", (NS, "NamingRule") + ) + + +class Structure(Namespace, abstract=True): + property_value_pkgs = m.Containment["PropertyValuePkg"]( + "ownedPropertyValuePkgs", + (NS, "PropertyValuePkg"), + mapkey="name", + ) + property_value_packages = m.DeprecatedAccessor["PropertyValuePkg"]( + "property_value_pkgs" + ) + + +class ReuserStructure(Structure, abstract=True): + reuse_links = m.Association["ReuseLink"]((NS, "ReuseLink"), "reuseLinks") + owned_reuse_links = m.Containment["ReuseLink"]( + "ownedReuseLinks", (NS, "ReuseLink") + ) + + +class AbstractModellingStructure(ReuserStructure, abstract=True): + architectures = m.Containment["ModellingArchitecture"]( + "ownedArchitectures", (NS, "ModellingArchitecture") + ) + architecture_pkgs = m.Containment["ModellingArchitecturePkg"]( + "ownedArchitecturePkgs", (NS, "ModellingArchitecturePkg") + ) + + +class Type(modellingcore.AbstractType, Namespace, abstract=True): + """Represents a set of values. + + A typed element that has this type is constrained to represent + values within this set. + """ + + +class ModellingBlock(Type, abstract=True): + """A modular unit that describes the structure of a system or element. + + A class (or block) that cannot be directly instantiated. Contrast: + concrete class. + """ + + +class ModellingArchitecture(Structure, abstract=True): + """Supports the definition of the model structure at a design level.""" + + +class ModellingArchitecturePkg(Structure, abstract=True): + """A container for modelling architectures.""" + + +class TypedElement( + modellingcore.AbstractTypedElement, NamedElement, abstract=True +): + """An element that has a type.""" + + +class Trace(Relationship, modellingcore.AbstractTrace, abstract=True): + pass + + +class AbstractAnnotation(CapellaElement, abstract=True): + content = m.StringPOD("content") + + +class NamingRule(AbstractAnnotation): + target_type = m.StringPOD("targetType") + """Type to whose instances the naming rule has to be applied.""" + + +class Constraint(NamedElement, modellingcore.AbstractConstraint): + """A condition or restriction. + + The constraint is expressed in natural language text or in a machine + readable language for the purpose of declaring some of the semantics + of an element. + """ _xmltag = "ownedConstraints" - constrained_elements = m.Association(m.ModelElement, "constrainedElements") - specification = m.SpecificationAccessor() +class KeyValue(CapellaElement): + key = m.StringPOD("key") + value = m.StringPOD("value") + + +class ReuseLink(Relationship): + reused = m.Single["ReuseableStructure"]( + m.Association((NS, "ReuseableStructure"), "reused") + ) + reuser = m.Single["ReuserStructure"]( + m.Association((NS, "ReuserStructure"), "reuser") + ) + + +class ReuseableStructure(Structure, abstract=True): + """A structure intended to be reused across various architectures.""" + + reuse_links = m.Association["ReuseLink"]((NS, "ReuseLink"), "reuseLinks") + + +class GeneralizableElement(Type, abstract=True): + """A type than can be generalized.""" + + is_abstract = m.BoolPOD("abstract") + generalizations = m.Containment["Generalization"]( + "ownedGeneralizations", (NS, "Generalization") + ) + sub = m.Backref["GeneralizableElement"]( + (NS, "GeneralizableElement"), "super" + ) + super = m.Single["GeneralizableElement"]( + m.Allocation( + "ownedGeneralizations", + (NS, "Generalization"), + (NS, "GeneralizableElement"), + attr="super", + backattr="sub", + ) + ) + + +class Classifier(GeneralizableElement, abstract=True): + owned_features = m.Containment["Feature"]("ownedFeatures", (NS, "Feature")) -class Generalization(m.ModelElement): - """A Generalization.""" +class GeneralClass( + Classifier, modellingcore.FinalizableElement, abstract=True +): + visibility = m.EnumPOD("visibility", VisibilityKind) + nested_classes = m.Containment["GeneralClass"]( + "nestedGeneralClasses", (NS, "GeneralClass") + ) + +class Generalization(Relationship): _xmltag = "ownedGeneralizations" - super: m.Accessor + super = m.Single["GeneralizableElement"]( + m.Association((NS, "GeneralizableElement"), "super") + ) + sub = m.Single["GeneralizableElement"]( + m.Association((NS, "GeneralizableElement"), "sub") + ) -class EnumerationPropertyLiteral(m.ModelElement): - """A Literal for EnumerationPropertyType.""" +class Feature(NamedElement, abstract=True): + is_abstract = m.BoolPOD("isAbstract") + is_static = m.BoolPOD("isStatic") + visibility = m.EnumPOD("visibility", VisibilityKind) - _xmltag = "ownedLiterals" + +class AbstractExchangeItemPkg(Structure, abstract=True): + exchange_items = m.Containment["information.ExchangeItem"]( + "ownedExchangeItems", (ns.INFORMATION, "ExchangeItem") + ) -class EnumerationPropertyType(m.ModelElement): - """An EnumerationPropertyType.""" +class Allocation(Relationship, modellingcore.AbstractTrace, abstract=True): + pass - _xmltag = "ownedEnumerationPropertyTypes" - literals = m.DirectProxyAccessor( - EnumerationPropertyLiteral, aslist=m.ElementList +class Involvement(Relationship, abstract=True): + involved = m.Single["InvolvedElement"]( + m.Association((NS, "InvolvedElement"), "involved") ) + source = m.DeprecatedAccessor["m.ModelElement"]("parent") + target = m.DeprecatedAccessor["InvolvedElement"]("involved") + + @property + def name(self) -> str: # type: ignore[override] + """Return the name.""" + direction = "" + if self.involved is not None: + direction = f" to {self.involved.name} ({self.involved.uuid})" + + return f"[{self.__class__.__name__}]{direction}" + + +class InvolverElement(CapellaElement, abstract=True): + pass + +class InvolvedElement(CapellaElement, abstract=True): + pass -class PropertyValue(m.ModelElement): - """Abstract base class for PropertyValues.""" +class AbstractPropertyValue(NamedElement, abstract=True): _xmltag = "ownedPropertyValues" - enumerations = m.DirectProxyAccessor( - EnumerationPropertyType, aslist=m.ElementList + involved_elements = m.Association["CapellaElement"]( + (NS, "CapellaElement"), "involvedElements" ) + enumerations = m.DeprecatedAccessor["EnumerationPropertyType"]( + "ownedEnumerationPropertyTypes" + ) + + +class StringPropertyValue(AbstractPropertyValue): + """A string property value.""" + + value = m.StringPOD("value") + - value: t.ClassVar[m.BasePOD | m.Single] +class IntegerPropertyValue(AbstractPropertyValue): + """An integer property value.""" + + value = m.IntPOD("value") -class BooleanPropertyValue(PropertyValue): +class BooleanPropertyValue(AbstractPropertyValue): """A boolean property value.""" value = m.BoolPOD("value") -class FloatPropertyValue(PropertyValue): +class FloatPropertyValue(AbstractPropertyValue): """A floating point property value.""" value = m.FloatPOD("value") -class IntegerPropertyValue(PropertyValue): - """An integer property value.""" - - value = m.IntPOD("value") +class EnumerationPropertyValue(AbstractPropertyValue): + type = m.Single["EnumerationPropertyType"]( + m.Association((NS, "EnumerationPropertyType"), "type") + ) + value = m.Single["EnumerationPropertyLiteral"]( + m.Association((NS, "EnumerationPropertyLiteral"), "value") + ) -class StringPropertyValue(PropertyValue): - """A string property value.""" +class EnumerationPropertyType(NamedElement): + _xmltag = "ownedEnumerationPropertyTypes" - value = m.StringPOD("value") + literals = m.Containment["EnumerationPropertyLiteral"]( + "ownedLiterals", (NS, "EnumerationPropertyLiteral") + ) -class EnumerationPropertyValue(PropertyValue): - """An enumeration property value.""" +class EnumerationPropertyLiteral(NamedElement): + """A Literal for EnumerationPropertyType.""" - type = m.Single(m.Association(EnumerationPropertyType, "type")) - value = m.Single(m.Association(EnumerationPropertyLiteral, "value")) + _xmltag = "ownedLiterals" -class PropertyValueGroup(m.ModelElement): +class PropertyValueGroup(Namespace): """A group for PropertyValues.""" _xmltag = "ownedPropertyValueGroups" - values = m.DirectProxyAccessor( - m.ModelElement, - ( - StringPropertyValue, - BooleanPropertyValue, - IntegerPropertyValue, - FloatPropertyValue, - EnumerationPropertyValue, - ), - aslist=m.ElementList, - mapkey="name", - mapvalue="value", - ) + values = m.DeprecatedAccessor["AbstractPropertyValue"]("property_values") -class PropertyValuePkg(m.ModelElement): +class PropertyValuePkg(Structure): """A Package for PropertyValues.""" _xmltag = "ownedPropertyValuePkgs" - enumeration_property_types = m.DirectProxyAccessor( - EnumerationPropertyType, aslist=m.ElementList + packages = m.Alias["PropertyValuePkg"]( + "property_value_pkgs", dirhide=False ) - groups = m.Containment["PropertyValueGroup"]( - "ownedPropertyValueGroups", - PropertyValueGroup, - mapkey="name", - mapvalue="values", - ) - values = m.DirectProxyAccessor( - m.ModelElement, - ( - StringPropertyValue, - BooleanPropertyValue, - IntegerPropertyValue, - FloatPropertyValue, - EnumerationPropertyValue, - ), - aslist=m.ElementList, - mapkey="name", - mapvalue="value", + groups = m.Alias["PropertyValueGroup"]( + "property_value_groups", dirhide=False ) + values = m.Alias["AbstractPropertyValue"]("property_values", dirhide=False) -PropertyValuePkg.packages = m.DirectProxyAccessor( - PropertyValuePkg, aslist=m.ElementList -) -m.ModelElement.constraints = m.DirectProxyAccessor( - Constraint, aslist=m.ElementList -) - -m.ModelElement.property_values = m.DirectProxyAccessor( - m.ModelElement, - ( - BooleanPropertyValue, - EnumerationPropertyValue, - FloatPropertyValue, - IntegerPropertyValue, - StringPropertyValue, - ), - aslist=m.MixedElementList, - mapkey="name", - mapvalue="value", -) -m.ModelElement.property_value_groups = m.DirectProxyAccessor( - PropertyValueGroup, - aslist=m.ElementList, - mapkey="name", - mapvalue="values", -) - -m.ModelElement.applied_property_values = m.Association( - None, "appliedPropertyValues" -) -m.ModelElement.applied_property_value_groups = m.Association( - None, "appliedPropertyValueGroups" -) +class AbstractDependenciesPkg(Structure, abstract=True): + pass diff --git a/capellambse/metamodel/capellamodeller.py b/capellambse/metamodel/capellamodeller.py index 97f35fcf..c9e53584 100644 --- a/capellambse/metamodel/capellamodeller.py +++ b/capellambse/metamodel/capellamodeller.py @@ -2,16 +2,47 @@ # SPDX-License-Identifier: Apache-2.0 from __future__ import annotations +import typing as t + import capellambse.model as m -from . import la +from . import capellacore from . import namespaces as ns -from . import oa, pa, sa + +if t.TYPE_CHECKING: + from . import epbs, la, oa, pa, sa NS = ns.CAPELLAMODELLER -class SystemEngineering(m.ModelElement): +class Project(capellacore.Structure): + key_value_pairs = m.Containment["capellacore.KeyValue"]( + "keyValuePairs", (ns.CAPELLACORE, "KeyValue") + ) + folders = m.Containment["Folder"]("ownedFolders", (NS, "Folder")) + model_roots = m.Containment["ModelRoot"]( + "ownedModelRoots", (NS, "ModelRoot") + ) + + @property + def model_root(self) -> ModelRoot: + if self.model_roots: + return self.model_roots[0] + return self.model_roots.create("SystemEngineering") + + +class Folder(capellacore.Structure): + folders = m.Containment["Folder"]("ownedFolders", (NS, "Folder")) + model_roots = m.Containment["ModelRoot"]( + "ownedModelRoots", (NS, "ModelRoot") + ) + + +class ModelRoot(capellacore.CapellaElement, abstract=True): + """A system engineering element or a package of those.""" + + +class SystemEngineering(capellacore.AbstractModellingStructure, ModelRoot): """A system engineering element. System engineering is an interdisciplinary approach encompassing the entire @@ -31,10 +62,10 @@ class SystemEngineering(m.ModelElement): [source:MIL-STD 499B standard] """ - architectures = m.Containment("ownedArchitectures", m.ModelElement) - @property def oa(self) -> oa.OperationalAnalysis: + from . import oa + try: return next( i @@ -48,6 +79,8 @@ def oa(self) -> oa.OperationalAnalysis: @property def sa(self) -> sa.SystemAnalysis: + from . import sa + try: return next( i @@ -61,6 +94,8 @@ def sa(self) -> sa.SystemAnalysis: @property def la(self) -> la.LogicalArchitecture: + from . import la + try: return next( i @@ -74,6 +109,8 @@ def la(self) -> la.LogicalArchitecture: @property def pa(self) -> pa.PhysicalArchitecture: + from . import pa + try: return next( i @@ -85,15 +122,28 @@ def pa(self) -> pa.PhysicalArchitecture: f"PhysicalArchitecture not found on {self._short_repr_()}" ) from None + @property + def epbs(self) -> epbs.EPBSArchitecture: + from . import epbs -class Project(m.ModelElement): - model_roots = m.Containment("ownedModelRoots", SystemEngineering) + try: + return next( + i + for i in self.architectures + if isinstance(i, epbs.EPBSArchitecture) + ) + except StopIteration: + raise AttributeError( + f"EPBSArchitecture not found on {self._short_repr_()}" + ) from None - @property - def model_root(self) -> SystemEngineering: - if self.model_roots: - return self.model_roots[0] - return self.model_roots.create() + +class SystemEngineeringPkg(capellacore.Structure, ModelRoot): + """A package that contains system engineering elements.""" + + system_engineerings = m.Containment["SystemEngineering"]( + "ownedSystemEngineerings", (NS, "SystemEngineering") + ) class Library(Project): diff --git a/capellambse/metamodel/cs.py b/capellambse/metamodel/cs.py index 9ef6bff0..370e3d91 100644 --- a/capellambse/metamodel/cs.py +++ b/capellambse/metamodel/cs.py @@ -1,192 +1,600 @@ # SPDX-FileCopyrightText: Copyright DB InfraGO AG # SPDX-License-Identifier: Apache-2.0 -"""Implementation of objects and relations for Functional Analysis. +"""Implementation of objects and relations for Functional Analysis.""" -Composite Structure objects inheritance tree (taxonomy): +from __future__ import annotations -.. diagram:: [CDB] CompositeStructure [Taxonomy] - -Composite Structure object-relations map (ontology): - -.. diagram:: [CDB] CompositeStructure [Ontology] -""" +import typing as t import capellambse.model as m -from . import capellacommon, fa, information +from . import capellacore, fa, information, interaction, modellingcore from . import namespaces as ns -NS = ns.CS +if t.TYPE_CHECKING: + from . import capellacommon # noqa: F401 +NS = ns.CS -class Part(m.ModelElement): - """A representation of a physical component.""" - _xmltag = "ownedParts" +class BlockArchitecturePkg( + capellacore.ModellingArchitecturePkg, abstract=True +): + """Container package for BlockArchitecture elements.""" - type = m.Single(m.Association(m.ModelElement, "abstractType")) - deployed_parts: m.Accessor +class BlockArchitecture(fa.AbstractFunctionalArchitecture, abstract=True): + """Parent class for deriving specific architectures for each design phase. + Formerly known as BaseArchitectureLayer. + """ -class ExchangeItemAllocation(m.ModelElement): - """An allocation of an ExchangeItem to an Interface.""" + capability_pkg = m.Single["capellacommon.AbstractCapabilityPkg"]( + m.Containment( + "ownedAbstractCapabilityPkg", + (ns.CAPELLACOMMON, "AbstractCapabilityPkg"), + ) + ) + capability_package = m.DeprecatedAccessor[ + "capellacommon.AbstractCapabilityPkg" + ]("capability_pkg") + interface_pkg = m.Single["InterfacePkg"]( + m.Containment("ownedInterfacePkg", (NS, "InterfacePkg")) + ) + interface_package = m.DeprecatedAccessor["InterfacePkg"]("interface_pkg") + data_pkg = m.Single["information.DataPkg"]( + m.Containment("ownedDataPkg", (ns.INFORMATION, "DataPkg")) + ) + data_package = m.DeprecatedAccessor["information.DataPkg"]("data_pkg") - item = m.Single(m.Association(information.ExchangeItem, "allocatedItem")) + @property + def all_classes(self) -> m.ElementList[information.Class]: + return self._model.search((ns.INFORMATION, "Class"), below=self) + @property + def all_collections(self) -> m.ElementList[information.Collection]: + return self._model.search((ns.INFORMATION, "Collection"), below=self) -class Interface(m.ModelElement): - """An interface.""" + @property + def all_unions(self) -> m.ElementList[information.Union]: + return self._model.search((ns.INFORMATION, "Union"), below=self) - exchange_item_allocations = m.DirectProxyAccessor( - ExchangeItemAllocation, aslist=m.ElementList - ) + @property + def all_enumerations( + self, + ) -> m.ElementList[information.datatype.Enumeration]: + return self._model.search( + (ns.INFORMATION_DATATYPE, "Enumeration"), below=self + ) + @property + def all_complex_values( + self, + ) -> m.ElementList[information.datavalue.AbstractComplexValue]: + return self._model.search( + (ns.INFORMATION_DATATYPE, "AbstractComplexValue"), below=self + ) -class InterfacePkg(m.ModelElement): - """A package that can hold interfaces and exchange items.""" + @property + def all_interfaces(self) -> m.ElementList[Interface]: + return self._model.search((NS, "Interface"), below=self) - exchange_items = m.DirectProxyAccessor( - information.ExchangeItem, aslist=m.ElementList + @property + def all_capabilities( + self, + ) -> m.ElementList[interaction.AbstractCapability]: + return self._model.search( + (ns.INTERACTION, "AbstractCapability"), below=self + ) + + +class Block( + fa.AbstractFunctionalBlock, + # NOTE: In the upstream metamodel, ModellingBlock comes first, + # but this would result in an MRO conflict. + capellacore.ModellingBlock, + abstract=True, +): + """A modular unit that describes the structure of a system or element.""" + + capability_pkg = m.Single["capellacommon.AbstractCapabilityPkg"]( + m.Containment( + "ownedAbstractCapabilityPkg", + (ns.CAPELLACOMMON, "AbstractCapabilityPkg"), + ) + ) + interface_pkg = m.Single["InterfacePkg"]( + m.Containment("ownedInterfacePkg", (NS, "InterfacePkg")) + ) + interface_package = m.DeprecatedAccessor["InterfacePkg"]("interface_pkg") + data_pkg = m.Single["information.DataPkg"]( + m.Containment("ownedDataPkg", (ns.INFORMATION, "DataPkg")) + ) + data_package = m.DeprecatedAccessor["information.DataPkg"]("data_pkg") + state_machines = m.Containment["capellacommon.StateMachine"]( + "ownedStateMachines", (ns.CAPELLACOMMON, "StateMachine") ) - interfaces = m.DirectProxyAccessor(Interface, aslist=m.ElementList) - packages: m.Accessor +class ComponentArchitecture(BlockArchitecture, abstract=True): + """A specialized kind of BlockArchitecture. -class PhysicalPort(m.ModelElement): - """A physical port.""" + Serves as a parent class for the various architecture levels, from + System analysis down to EPBS architecture. + """ - _xmltag = "ownedFeatures" - owner = m.ParentAccessor() - links: m.Accessor +class InterfaceAllocator(capellacore.CapellaElement, abstract=True): + interface_allocations = m.Containment["InterfaceAllocation"]( + "ownedInterfaceAllocations", (NS, "InterfaceAllocation") + ) + allocated_interfaces = m.Allocation["Interface"]( + "ownedInterfaceAllocations", + (NS, "InterfaceAllocation"), + (NS, "Interface"), + attr="targetElement", + backattr="sourceElement", + ) + +class Component( + Block, + capellacore.Classifier, + InterfaceAllocator, + information.communication.CommunicationLinkExchanger, + abstract=True, +): + """An entity, with discrete structure within the system. -class PhysicalLink(PhysicalPort): - """A physical link.""" + Interacts with other Components of the system, thereby contributing + at its lowest level to the system properties and characteristics. + """ - ends = m.PhysicalLinkEndsAccessor( - PhysicalPort, "linkEnds", aslist=m.ElementList + is_actor = m.BoolPOD("actor") + """Indicates if the Component is an Actor.""" + is_human = m.BoolPOD("human") + """Indicates whether the Component is a Human.""" + owner = m.DeprecatedAccessor[m.ModelElement]("parent") + interface_uses = m.Containment["InterfaceUse"]( + "ownedInterfaceUses", (NS, "InterfaceUse") + ) + used_interfaces = m.Allocation["Interface"]( + "ownedInterfaceUses", + (NS, "InterfaceUse"), + (NS, "Interface"), + attr="usedInterface", + ) + interface_implementations = m.Containment["InterfaceImplementation"]( + "ownedInterfaceImplementations", (NS, "InterfaceImplementation") + ) + implemented_interfaces = m.Allocation["Interface"]( + "ownedInterfaceImplementations", + (NS, "InterfaceImplementation"), + (NS, "Interface"), + attr="implementedInterfaces", + ) + component_realizations = m.Containment["ComponentRealization"]( + "ownedComponentRealizations", (NS, "ComponentRealization") ) - exchanges = m.Allocation[fa.ComponentExchange]( - "ownedComponentExchangeAllocations", - fa.ComponentExchangeAllocation, + realized_components = m.Allocation["Component"]( + "ownedComponentRealizations", + (NS, "ComponentRealization"), + (NS, "Component"), attr="targetElement", backattr="sourceElement", ) + realizing_components = m.Backref["Component"]( + (NS, "Component"), "realized_components" + ) + ports = m.Filter["fa.ComponentPort"]( + "owned_features", (ns.FA, "ComponentPort") + ) + physical_ports = m.Filter["PhysicalPort"]( + "owned_features", (NS, "PhysicalPort") + ) + parts = m.Backref["Part"]((NS, "Part"), "type") + physical_paths = m.Containment["PhysicalPath"]( + "ownedPhysicalPath", (NS, "PhysicalPath") + ) + physical_links = m.Containment["PhysicalLink"]( + "ownedPhysicalLinks", (NS, "PhysicalLink") + ) + physical_link_categories = m.Containment["PhysicalLinkCategory"]( + "ownedPhysicalLinkCategories", (NS, "PhysicalLinkCategory") + ) + exchanges = m.DeprecatedAccessor["fa.ComponentExchange"]( + "component_exchanges" + ) + related_exchanges = m.Backref["fa.ComponentExchange"]( + (ns.FA, "ComponentExchange"), + "source", + "source.owner", + "target", + "target.owner", + ) - physical_paths: m.Accessor - source = m.IndexAccessor[PhysicalPort]("ends", 0) - target = m.IndexAccessor[PhysicalPort]("ends", 1) +class DeployableElement(capellacore.NamedElement, abstract=True): + """A physical model element intended to be deployed.""" -class PhysicalPath(m.ModelElement): - """A physical path.""" +class DeploymentTarget(capellacore.NamedElement, abstract=True): + """The physical target that will host a deployable element.""" - _xmltag = "ownedPhysicalPath" - involved_items = m.Allocation[m.ModelElement]( - None, # FIXME fill in tag - "org.polarsys.capella.core.data.cs:PhysicalPathInvolvement", - attr="involved", +class AbstractPathInvolvedElement(capellacore.InvolvedElement, abstract=True): + pass + + +class Part( + information.AbstractInstance, + modellingcore.InformationsExchanger, + DeployableElement, + DeploymentTarget, + AbstractPathInvolvedElement, +): + """A representation of a physical component. + + In SysML, a Part is an owned property of a Block. + """ + + _xmltag = "ownedParts" + + deployed_parts = m.Allocation["DeployableElement"]( + "ownedDeploymentLinks", + (NS, "AbstractDeploymentLink"), + (NS, "DeployableElement"), + attr="deployedElement", + backattr="location", ) - exchanges = m.Allocation[fa.ComponentExchange]( - None, # FIXME fill in tag - "org.polarsys.capella.core.data.fa:ComponentExchangeAllocation", - attr="targetElement", + owned_type = m.Single["modellingcore.AbstractType"]( + m.Containment("ownedAbstractType", (ns.MODELLINGCORE, "AbstractType")) ) - @property - def involved_links(self) -> m.ElementList[PhysicalLink]: - return self.involved_items.by_type("PhysicalLink") +class ArchitectureAllocation(capellacore.Allocation, abstract=True): + pass -class Component(m.ModelElement): - """A template class for components.""" - is_abstract = m.BoolPOD("abstract") - """Boolean flag for an abstract Component.""" - is_human = m.BoolPOD("human") - """Boolean flag for a human Component.""" - is_actor = m.BoolPOD("actor") - """Boolean flag for an actor Component.""" +class ComponentRealization(capellacore.Allocation): + """A realization that links to a component.""" - owned_features = m.Containment( - "ownedFeatures", m.ModelElement, aslist=m.ElementList + _xmltag = "ownedComponentRealizations" + + +class InterfacePkg( + information.communication.MessageReferencePkg, + capellacore.AbstractDependenciesPkg, + capellacore.AbstractExchangeItemPkg, +): + """A container for Interface elements.""" + + interfaces = m.Containment["Interface"]( + "ownedInterfaces", (NS, "Interface") + ) + packages = m.Containment["InterfacePkg"]( + "ownedInterfacePkgs", (NS, "InterfacePkg") ) - owner = m.ParentAccessor() - state_machines = m.DirectProxyAccessor( - capellacommon.StateMachine, aslist=m.ElementList + +class Interface(capellacore.GeneralClass, InterfaceAllocator): + """An interface. + + An interface is a kind of classifier that represents a declaration of a set + of coherent public features and obligations. An interface specifies a + contract; any instance of a classifier that realizes the interface must + fulfill that contract. + + Interfaces are defined by functional and physical characteristics that + exist at a common boundary with co-functioning items and allow systems, + equipment, software, and system data to be compatible. + + That design feature of one piece of equipment that affects a design feature + of another piece of equipment. An interface can extend beyond the physical + boundary between two items. (For example, the weight and center of gravity + of one item can affect the interfacing item; however, the center of gravity + is rarely located at the physical boundary. An electrical interface + generally extends to the first isolating element rather than terminating at + a series of connector pins.) + + Usage guideline + --------------- + In Capella, Interfaces are created to declare the nature of interactions + between the System and external actors. + """ + + mechanism = m.StringPOD("mechanism") + is_structural = m.BoolPOD("structural") + exchange_item_allocations = m.Containment["ExchangeItemAllocation"]( + "ownedExchangeItemAllocations", (NS, "ExchangeItemAllocation") ) - ports = m.DirectProxyAccessor(fa.ComponentPort, aslist=m.ElementList) - physical_ports = m.DirectProxyAccessor(PhysicalPort, aslist=m.ElementList) - parts = m.Backref(Part, "type") - physical_paths = m.DirectProxyAccessor(PhysicalPath, aslist=m.ElementList) - physical_links = m.DirectProxyAccessor(PhysicalLink, aslist=m.ElementList) - exchanges = m.DirectProxyAccessor( - fa.ComponentExchange, aslist=m.ElementList + allocated_exchange_items = m.Allocation["information.ExchangeItem"]( + "ownedExchangeItemAllocations", + (NS, "ExchangeItemAllocation"), + (ns.INFORMATION, "ExchangeItem"), + attr="allocatedItem", ) - related_exchanges = m.Backref( - fa.ComponentExchange, - "source.owner", - "target.owner", + +class InterfaceImplementation(capellacore.Relationship): + implemented_interface = m.Single["Interface"]( + m.Association((NS, "Interface"), "implementedInterface") ) - realized_components = m.Allocation["Component"]( - "ownedComponentRealizations", - "org.polarsys.capella.core.data.cs:ComponentRealization", + +class InterfaceUse(capellacore.Relationship): + used_interface = m.Single["Interface"]( + m.Association((NS, "Interface"), "usedInterface") + ) + + +class ProvidedInterfaceLink(capellacore.Relationship, abstract=True): + interface = m.Single["Interface"]( + m.Association((NS, "Interface"), "interface") + ) + + +class RequiredInterfaceLink(capellacore.Relationship, abstract=True): + interface = m.Single["Interface"]( + m.Association((NS, "Interface"), "interface") + ) + + +class InterfaceAllocation(capellacore.Allocation, abstract=True): + pass + + +class ExchangeItemAllocation( + capellacore.Relationship, + information.AbstractEventOperation, + modellingcore.FinalizableElement, +): + """An allocation of an ExchangeItem to an Interface.""" + + send_protocol = m.EnumPOD( + "sendProtocol", information.communication.CommunicationLinkProtocol + ) + receive_protocol = m.EnumPOD( + "receiveProtocol", information.communication.CommunicationLinkProtocol + ) + allocated_item = m.Single["information.ExchangeItem"]( + m.Association((ns.INFORMATION, "ExchangeItem"), "allocatedItem") + ) + item = m.DeprecatedAccessor["information.ExchangeItem"]("allocated_item") + + +class AbstractDeploymentLink(capellacore.Relationship, abstract=True): + deployed_element = m.Single["DeployableElement"]( + m.Association((NS, "DeployableElement"), "deployedElement") + ) + location = m.Single["DeploymentTarget"]( + m.Association((NS, "DeploymentTarget"), "location") + ) + + +class AbstractPhysicalArtifact(capellacore.CapellaElement, abstract=True): + pass + + +class AbstractPhysicalLinkEnd(capellacore.CapellaElement, abstract=True): + pass + + +class AbstractPhysicalPathLink(fa.ComponentExchangeAllocator, abstract=True): + pass + + +class PhysicalLink( + AbstractPhysicalPathLink, + AbstractPhysicalArtifact, + AbstractPathInvolvedElement, +): + ends = m.Association["AbstractPhysicalLinkEnd"]( + (NS, "AbstractPhysicalLinkEnd"), "linkEnds", fixed_length=2 + ) + functional_exchange_allocations = m.Containment[ + "fa.ComponentExchangeFunctionalExchangeAllocation" + ]( + "ownedComponentExchangeFunctionalExchangeAllocations", + (ns.FA, "ComponentExchangeFunctionalExchangeAllocation"), + ) + allocated_functional_exchanges = m.Allocation["fa.FunctionalExchange"]( + "ownedComponentExchangeFunctionalExchangeAllocations", + (ns.FA, "ComponentExchangeFunctionalExchangeAllocation"), + (ns.FA, "FunctionalExchange"), attr="targetElement", + backattr="sourceElement", + ) + physical_link_ends = m.Containment["PhysicalLinkEnd"]( + "ownedPhysicalLinkEnds", (NS, "PhysicalLinkEnd") ) - realizing_components = m.Backref["Component"]((), "realized_components") + physical_link_realizations = m.Containment["PhysicalLinkRealization"]( + "ownedPhysicalLinkRealizations", (NS, "PhysicalLinkRealization") + ) + realized_physical_links = m.Allocation["PhysicalLink"]( + "ownedPhysicalLinkRealizations", + (NS, "PhysicalLinkRealization"), + (NS, "PhysicalLink"), + attr="targetElement", + backattr="sourceElement", + ) + exchanges = m.DeprecatedAccessor["fa.FunctionalExchange"]( + "allocated_functional_exchanges" + ) + physical_paths = m.Backref["PhysicalPath"]( + (NS, "PhysicalPath"), "involved_items" + ) + + @property + def source(self) -> AbstractPhysicalLinkEnd: + return self.ends[0] + @source.setter + def source(self, end: PhysicalPort) -> None: + self.ends[0] = end -class ComponentRealization(m.ModelElement): - """A realization that links to a component.""" + @property + def target(self) -> AbstractPhysicalLinkEnd: + return self.ends[1] - _xmltag = "ownedComponentRealizations" + @target.setter + def target(self, end: PhysicalPort) -> None: + self.ends[1] = end + + +class PhysicalLinkCategory(capellacore.NamedElement): + links = m.Association["PhysicalLink"]((NS, "PhysicalLink"), "links") + + +class PhysicalLinkEnd(AbstractPhysicalLinkEnd): + port = m.Single["PhysicalPort"]( + m.Association((NS, "PhysicalPort"), "port") + ) + part = m.Single["Part"](m.Association((NS, "Part"), "part")) + + +class PhysicalLinkRealization(capellacore.Allocation): + pass + + +class PhysicalPath( + fa.ComponentExchangeAllocator, + AbstractPathInvolvedElement, + capellacore.InvolverElement, + capellacore.NamedElement, +): + """A physical path.""" + + _xmltag = "ownedPhysicalPath" + involved_links = m.Association["AbstractPhysicalPathLink"]( + (NS, "AbstractPhysicalPathLink"), "involvedLinks" + ) + physical_path_involvements = m.Containment["PhysicalPathInvolvement"]( + "ownedPhysicalPathInvolvements", (NS, "PhysicalPathInvolvement") + ) + involved_paths = m.Allocation["AbstractPathInvolvedElement"]( + "ownedPhysicalPathInvolvements", + (NS, "PhysicalPathInvolvement"), + (NS, "AbstractPathInvolvedElement"), + attr="involved", + ) + involved_items = m.DeprecatedAccessor["AbstractPathInvolvedElement"]( + "involved_paths" + ) + physical_path_realizations = m.Containment["PhysicalPathRealization"]( + "ownedPhysicalPathRealizations", (NS, "PhysicalPathRealization") + ) + realized_paths = m.Allocation["PhysicalPath"]( + "ownedPhysicalPathRealizations", + (NS, "PhysicalPathRealization"), + (NS, "PhysicalPath"), + attr="targetElement", + backattr="sourceElement", + ) + exchanges = m.DeprecatedAccessor["fa.ComponentExchange"]( + "allocated_exchanges" + ) -class ComponentArchitecture(m.ModelElement): - """Formerly known as BaseArchitectureLayer.""" - data_package = m.DirectProxyAccessor(information.DataPkg) - interface_package = m.DirectProxyAccessor(InterfacePkg) - component_exchange_categories = m.DirectProxyAccessor( - fa.ComponentExchangeCategory, aslist=m.ElementList +class PhysicalPathInvolvement(capellacore.Involvement): + next_involvements = m.Association["PhysicalPathInvolvement"]( + (NS, "PhysicalPathInvolvement"), "nextInvolvements" ) - all_classes = m.DeepProxyAccessor(information.Class, aslist=m.ElementList) - all_collections = m.DeepProxyAccessor( - information.Collection, aslist=m.ElementList + +class PhysicalPathReference(PhysicalPathInvolvement): + pass + + +class PhysicalPathRealization(capellacore.Allocation): + pass + + +class PhysicalPort( + information.Port, + AbstractPhysicalArtifact, + modellingcore.InformationsExchanger, + AbstractPhysicalLinkEnd, + information.Property, +): + """A port on a physical component.""" + + _xmltag = "ownedFeatures" + + owner = m.DeprecatedAccessor[m.ModelElement]("parent") + component_port_allocations = m.Containment["fa.ComponentPortAllocation"]( + "ownedComponentPortAllocations", + (ns.FA, "ComponentPortAllocation"), ) - all_unions = m.DeepProxyAccessor(information.Union, aslist=m.ElementList) - all_enumerations = m.DeepProxyAccessor( - information.datatype.Enumeration, aslist=m.ElementList + allocated_component_ports = m.Allocation["fa.ComponentPort"]( + "ownedComponentPortAllocations", + (ns.FA, "ComponentPortAllocation"), + (ns.FA, "ComponentPort"), + attr="targetElement", + backattr="sourceElement", ) - all_complex_values = m.DeepProxyAccessor( - information.datavalue.ComplexValue, aslist=m.ElementList + physical_port_realizations = m.Containment["PhysicalPortRealization"]( + "ownedPhysicalPortRealizations", (NS, "PhysicalPortRealization") ) - all_interfaces = m.DeepProxyAccessor(Interface, aslist=m.ElementList) + realized_ports = m.Allocation["PhysicalPort"]( + "ownedPhysicalPortRealizations", + (NS, "PhysicalPortRealization"), + (NS, "PhysicalPort"), + attr="targetElement", + backattr="sourceElement", + ) + links = m.Backref["PhysicalLink"]((NS, "PhysicalLink"), "ends") + + +class PhysicalPortRealization(capellacore.Allocation): + pass -InterfacePkg.packages = m.DirectProxyAccessor( - InterfacePkg, aslist=m.ElementList -) -Part.deployed_parts = m.Allocation( - "ownedDeploymentLinks", - "org.polarsys.capella.core.data.pa.deployment:PartDeploymentLink", - attr="deployedElement", - backattr="location", -) -PhysicalPort.links = m.Backref(PhysicalLink, "ends") -PhysicalLink.physical_paths = m.Backref(PhysicalPath, "involved_items") -fa.ComponentExchange.allocating_physical_link = m.Single( - m.Backref(PhysicalLink, "exchanges") -) -fa.ComponentExchange.allocating_physical_paths = m.Backref( - PhysicalPath, "exchanges" -) +class ComponentPkg(capellacore.Structure, abstract=True): + """A package containing parts.""" + + parts = m.Containment["Part"]("ownedParts", (NS, "Part")) + exchanges = m.Containment["fa.ComponentExchange"]( + "ownedComponentExchanges", (ns.FA, "ComponentExchange") + ) + exchange_categories = m.Containment["fa.ComponentExchangeCategory"]( + "ownedComponentExchangeCategories", + (ns.FA, "ComponentExchangeCategory"), + ) + functional_links = m.Containment["fa.ExchangeLink"]( + "ownedFunctionalLinks", (ns.FA, "ExchangeLink") + ) + functional_allocations = m.Containment["fa.ComponentFunctionalAllocation"]( + "ownedFunctionalAllocations", + (ns.FA, "ComponentFunctionalAllocation"), + ) + allocated_functions = m.Allocation["fa.AbstractFunction"]( + "ownedFunctionalAllocations", + (ns.FA, "ComponentFunctionalAllocation"), + (ns.FA, "AbstractFunction"), + attr="targetElement", + backattr="sourceElement", + ) + component_exchange_realizations = m.Containment[ + "fa.ComponentExchangeRealization" + ]( + "ownedComponentExchangeRealizations", + (ns.FA, "ComponentExchangeRealization"), + ) + realized_component_exchanges = m.Allocation["fa.ComponentExchange"]( + "ownedComponentExchangeRealizations", + (ns.FA, "ComponentExchangeRealization"), + (ns.FA, "ComponentExchange"), + attr="targetElement", + backattr="sourceElement", + ) + physical_links = m.Containment["PhysicalLink"]( + "ownedPhysicalLinks", (NS, "PhysicalLink") + ) + physical_link_categories = m.Containment["PhysicalLinkCategory"]( + "ownedPhysicalLinkCategories", (NS, "PhysicalLinkCategory") + ) + state_machines = m.Containment["capellacommon.StateMachine"]( + "ownedStateMachines", (ns.CAPELLACOMMON, "StateMachine") + ) diff --git a/capellambse/metamodel/epbs.py b/capellambse/metamodel/epbs.py index dd4770f5..cde0181c 100644 --- a/capellambse/metamodel/epbs.py +++ b/capellambse/metamodel/epbs.py @@ -1,5 +1,108 @@ # SPDX-FileCopyrightText: Copyright DB InfraGO AG # SPDX-License-Identifier: Apache-2.0 +"""Tools for the EPBS layer.""" + +from __future__ import annotations + +import enum +import typing as t + +import capellambse.model as m + +from . import capellacommon, capellacore, cs from . import namespaces as ns +if t.TYPE_CHECKING: + from . import la, pa # noqa: F401 + NS = ns.EPBS + + +@m.stringy_enum +@enum.unique +class ConfigurationItemKind(enum.Enum): + UNSET = "Unset" + COTSCI = "COTSCI" + """Commercial Off The Shelves Configuration Item.""" + CSCI = "CSCI" + """Computer Software Configuration Item.""" + HWCI = "HWCI" + """Hardware Configuration Item.""" + INTERFACE_CI = "InterfaceCI" + """Interface Configuration Item.""" + NDICI = "NDICI" + """Non Developmental Configuration Item.""" + PRIME_ITEM_CI = "PrimeItemCI" + """Prime Item Configuration Item.""" + SYSTEM_CI = "SystemCI" + """System Configuration Item.""" + + +class EPBSArchitecturePkg(cs.BlockArchitecturePkg): + architectures = m.Containment["EPBSArchitecture"]( + "ownedEPBSArchitectures", (NS, "EPBSArchitecture") + ) + + +class EPBSArchitecture(cs.ComponentArchitecture): + configuration_item_pkg = m.Single["ConfigurationItemPkg"]( + m.Containment( + "ownedConfigurationItemPkg", (NS, "ConfigurationItemPkg") + ) + ) + physical_architecture_realizations = m.Containment[ + "PhysicalArchitectureRealization" + ]( + "ownedPhysicalArchitectureRealizations", + (NS, "PhysicalArchitectureRealization"), + ) + realized_physical_architecture = m.Single( + m.Allocation["pa.PhysicalArchitecture"]( + "ownedPhysicalArchitectureRealizations", + (NS, "PhysicalArchitectureRealization"), + (ns.PA, "PhysicalArchitecture"), + attr="targetElement", + backattr="sourceElement", + ) + ) + + +class ConfigurationItemPkg(cs.ComponentPkg): + configuration_items = m.Containment["ConfigurationItem"]( + "ownedConfigurationItems", (NS, "ConfigurationItem") + ) + configuration_item_pkgs = m.Containment["ConfigurationItemPkg"]( + "ownedConfigurationItemPkgs", (NS, "ConfigurationItemPkg") + ) + + +class ConfigurationItem( + capellacommon.CapabilityRealizationInvolvedElement, + cs.Component, +): + identifier = m.StringPOD("itemIdentifier") + kind = m.EnumPOD("kind", ConfigurationItemKind) + configuration_items = m.Containment["ConfigurationItem"]( + "ownedConfigurationItems", (NS, "ConfigurationItem") + ) + configuration_item_pkgs = m.Containment["ConfigurationItemPkg"]( + "ownedConfigurationItemPkgs", (NS, "ConfigurationItemPkg") + ) + physical_artifact_realizations = m.Containment[ + "PhysicalArtifactRealization" + ]("ownedPhysicalArtifactRealizations", (NS, "PhysicalArtifactRealization")) + realized_physical_artifacts = m.Allocation["cs.AbstractPhysicalArtifact"]( + "ownedPhysicalArtifactRealizations", + (NS, "PhysicalArtifactRealization"), + (ns.CS, "AbstractPhysicalArtifact"), + attr="targetElement", + backattr="sourceElement", + ) + + +class PhysicalArchitectureRealization(cs.ArchitectureAllocation): + pass + + +class PhysicalArtifactRealization(capellacore.Allocation): + pass diff --git a/capellambse/metamodel/fa.py b/capellambse/metamodel/fa.py index c66f25d5..f90196ae 100644 --- a/capellambse/metamodel/fa.py +++ b/capellambse/metamodel/fa.py @@ -1,328 +1,729 @@ # SPDX-FileCopyrightText: Copyright DB InfraGO AG # SPDX-License-Identifier: Apache-2.0 -"""Implementation of objects and relations for Functional Analysis. +from __future__ import annotations -Functional Analysis objects inheritance tree (taxonomy): +import enum +import typing as t +import warnings -.. diagram:: [CDB] FunctionalAnalysis [Taxonomy] +import capellambse.model as m -Functional Analysis object-relations map (ontology): +from . import activity, behavior, capellacore, cs, information, modellingcore +from . import namespaces as ns -.. diagram:: [CDB] FunctionalAnalysis [Ontology] -""" +if t.TYPE_CHECKING: + from . import capellacommon, interaction # noqa: F401 -from __future__ import annotations +NS = ns.FA -import capellambse.model as m -from . import capellacommon, capellacore, information, interaction, modeltypes -from . import namespaces as ns +@m.stringy_enum +@enum.unique +class ComponentExchangeKind(enum.Enum): + """The kind of a ComponentExchange.""" + + UNSET = "UNSET" + """Communication kind is not set.""" + DELEGATION = "DELEGATION" + """Indicates that the connector is a delegation connector.""" + ASSEMBLY = "ASSEMBLY" + """Indicates that the connector is an assembly connector.""" + FLOW = "FLOW" + """Describes a flow communication.""" + + +@m.stringy_enum +@enum.unique +class ComponentPortKind(enum.Enum): + STANDARD = "STANDARD" + """Describes a standard port. + + A port is an interaction point between a Block or sub-Block and its + environment that supports Exchanges with other ports. + """ + FLOW = "FLOW" + """Describes a flow port. + + A flow port is an interaction point through which input and/or + output of items such as data, material, or energy may flow. + """ + + +@m.stringy_enum +@enum.unique +class ControlNodeKind(enum.Enum): + OR = "OR" + AND = "AND" + ITERATE = "ITERATE" + + +@m.stringy_enum +@enum.unique +class FunctionKind(enum.Enum): + """The kind of a Function.""" + + FUNCTION = "FUNCTION" + DUPLICATE = "DUPLICATE" + GATHER = "GATHER" + SELECT = "SELECT" + SPLIT = "SPLIT" + ROUTE = "ROUTE" + + +@m.stringy_enum +@enum.unique +class FunctionalChainKind(enum.Enum): + """The kind of a Functional Chain.""" + + SIMPLE = "SIMPLE" + COMPOSITE = "COMPOSITE" + FRAGMENT = "FRAGMENT" + + +@m.stringy_enum +@enum.unique +class OrientationPortKind(enum.Enum): + """Direction of component ports.""" + + UNSET = "UNSET" + """The port orientation is undefined.""" + IN = "IN" + """The port represents an input of the component it is used in.""" + OUT = "OUT" + """The port represents an output of the component it is used in.""" + INOUT = "INOUT" + """The port represents both an input and on output of the component.""" + + +class AbstractFunctionalArchitecture( + capellacore.ModellingArchitecture, abstract=True +): + function_pkg = m.Single["FunctionPkg"]( + m.Containment("ownedFunctionPkg", (NS, "FunctionPkg")) + ) + function_package = m.DeprecatedAccessor["FunctionPkg"]("function_pkg") + component_exchanges = m.Containment["ComponentExchange"]( + "ownedComponentExchanges", (NS, "ComponentExchange") + ) + component_exchange_categories = m.Containment["ComponentExchangeCategory"]( + "ownedComponentExchangeCategories", (NS, "ComponentExchangeCategory") + ) + functional_links = m.Containment["ExchangeLink"]( + "ownedFunctionalLinks", (NS, "ExchangeLink") + ) + functional_allocations = m.Containment["ComponentFunctionalAllocation"]( + "ownedFunctionalAllocations", (NS, "ComponentFunctionalAllocation") + ) + component_exchange_realizations = m.Containment[ + "ComponentExchangeRealization" + ]( + "ownedComponentExchangeRealizations", + (NS, "ComponentExchangeRealization"), + ) -NS = ns.FA + @property + def root_function(self) -> AbstractFunction: + """Returns the first function in the function_pkg.""" + pkg = self.function_pkg + assert pkg is not None + if not pkg.functions: + raise RuntimeError(f"Package {pkg._short_repr_()} is empty") + return pkg.functions[0] + @property + def all_functions(self) -> m.ElementList[AbstractFunction]: + return self._model.search((NS, "AbstractFunction"), below=self) -class ComponentExchangeAllocation(m.ModelElement): ... + @property + def all_functional_chains(self) -> m.ElementList[FunctionalChain]: + return self._model.search((NS, "FunctionalChain"), below=self) + @property + def all_function_exchanges(self) -> m.ElementList[FunctionalExchange]: + return self._model.search((NS, "FunctionalExchange"), below=self) -class ComponentExchangeFunctionalExchangeAllocation(m.ModelElement): ... + +class AbstractFunctionalBlock(capellacore.ModellingBlock, abstract=True): + functional_allocations = m.Containment["ComponentFunctionalAllocation"]( + "ownedFunctionalAllocation", (NS, "ComponentFunctionalAllocation") + ) + allocated_functions = m.Allocation["AbstractFunction"]( + "ownedFunctionalAllocation", + (NS, "ComponentFunctionalAllocation"), + (NS, "AbstractFunction"), + attr="targetElement", + backattr="sourceElement", + ) + component_exchanges = m.Containment["ComponentExchange"]( + "ownedComponentExchanges", (NS, "ComponentExchange") + ) + component_exchange_categories = m.Containment["ComponentExchangeCategory"]( + "ownedComponentExchangeCategories", (NS, "ComponentExchangeCategory") + ) + in_exchange_links = m.Association["ExchangeLink"]( + (NS, "ExchangeLink"), "inExchangeLinks" + ) + out_exchange_links = m.Association["ExchangeLink"]( + (NS, "ExchangeLink"), "outExchangeLinks" + ) + + +class FunctionPkg(capellacore.Structure, abstract=True): + functional_links = m.Containment["ExchangeLink"]( + "ownedFunctionalLinks", (NS, "ExchangeLink") + ) + exchanges = m.Containment["FunctionalExchangeSpecification"]( + "ownedExchanges", (NS, "FunctionalExchangeSpecification") + ) + exchange_specification_realizations = m.Containment[ + "ExchangeSpecificationRealization" + ]( + "ownedExchangeSpecificationRealizations", + (NS, "ExchangeSpecificationRealization"), + ) + realized_exchange_specifications = m.Allocation["ExchangeSpecification"]( + "ownedExchangeSpecificationRealizations", + (NS, "ExchangeSpecificationRealization"), + (NS, "ExchangeSpecification"), + attr="targetElement", + backattr="sourceElement", + ) + categories = m.Containment["ExchangeCategory"]( + "ownedCategories", (NS, "ExchangeCategory") + ) + function_specifications = m.Containment["FunctionSpecification"]( + "ownedFunctionSpecifications", (NS, "FunctionSpecification") + ) -class ComponentFunctionalAllocation(m.ModelElement): ... +class FunctionSpecification(capellacore.Namespace, activity.AbstractActivity): + in_exchange_links = m.Association["ExchangeLink"]( + (NS, "ExchangeLink"), "inExchangeLinks" + ) + out_exchange_links = m.Association["ExchangeLink"]( + (NS, "ExchangeLink"), "outExchangeLinks" + ) + ports = m.Containment["FunctionPort"]( + "ownedFunctionPorts", (NS, "FunctionPort") + ) -class ExchangeCategory(m.ModelElement): +class ExchangeCategory(capellacore.NamedElement): _xmltag = "ownedCategories" - exchanges: m.Association[FunctionalExchange] + exchanges = m.Association["FunctionalExchange"]( + (NS, "FunctionalExchange"), "exchanges" + ) -class ComponentExchangeCategory(m.ModelElement): - _xmltag = "ownedComponentExchangeCategories" +class ExchangeLink(capellacore.NamedRelationship): + exchange_containment_links = m.Association["ExchangeContainment"]( + (NS, "ExchangeContainment"), "exchangeContainmentLinks" + ) + exchange_containments = m.Containment["ExchangeContainment"]( + "ownedExchangeContainments", (NS, "ExchangeContainment") + ) + sources = m.Association["FunctionSpecification"]( + (NS, "FunctionSpecification"), "sources" + ) + destinations = m.Association["FunctionSpecification"]( + (NS, "FunctionSpecification"), "destinations" + ) - exchanges: m.Association[ComponentExchange] +class ExchangeContainment(capellacore.Relationship): + exchange = m.Single["ExchangeSpecification"]( + m.Association((NS, "ExchangeSpecification"), "exchange") + ) + link = m.Single["ExchangeLink"]( + m.Association((NS, "ExchangeLink"), "link") + ) -class ControlNode(m.ModelElement): - """A node with a specific control-kind.""" - _xmltag = "ownedSequenceNodes" +class ExchangeSpecification( + capellacore.NamedElement, activity.ActivityExchange, abstract=True +): + link = m.Association["ExchangeContainment"]( + (NS, "ExchangeContainment"), "link" + ) - kind = m.EnumPOD("kind", modeltypes.ControlNodeKind, writable=False) +class FunctionalExchangeSpecification(ExchangeSpecification): + pass -class FunctionRealization(m.ModelElement): - """A realization that links to a function.""" - _xmltag = "ownedFunctionRealizations" +class FunctionalChain( + capellacore.NamedElement, + capellacore.InvolverElement, + capellacore.InvolvedElement, +): + _xmltag = "ownedFunctionalChains" + kind = m.EnumPOD("kind", FunctionalChainKind) + functional_chain_involvements = m.Containment[ + "FunctionalChainInvolvement" + ]("ownedFunctionalChainInvolvements", (NS, "FunctionalChainInvolvement")) -class AbstractExchange(m.ModelElement): - """Common code for Exchanges.""" + @property + def involved_functions(self) -> m.ElementList[AbstractFunction]: + return self.functional_chain_involvements.by___class__( + FunctionalChainInvolvementFunction, + ).map("involved") - source = m.Single(m.Association(m.ModelElement, "source")) - target = m.Single(m.Association(m.ModelElement, "target")) + @property + def involved_links(self) -> m.ElementList[AbstractExchange]: # FIXME + return self.functional_chain_involvements.by___class__( + FunctionalChainInvolvementLink, + ).map("involved") + @property + def involved_chains(self) -> m.ElementList[FunctionalChain]: + return self.functional_chain_involvements.by___class__( + FunctionalChainReference, + ).map("involved") -class AbstractFunction(m.ModelElement): - """An AbstractFunction.""" + @property + def involved( + self, + ) -> m.ElementList[AbstractFunction | AbstractExchange]: # FIXME + return self.functional_chain_involvements.by___class__( + FunctionalChainInvolvementFunction, FunctionalChainInvolvementLink + ).map("involved") + + involving_chains = m.Backref["FunctionalChain"]( + (NS, "FunctionalChain"), "involved_chains" + ) - available_in_states = m.Association( - capellacommon.State, "availableInStates" + functional_chain_realizations = m.Containment[ + "FunctionalChainRealization" + ]("ownedFunctionalChainRealizations", (NS, "FunctionalChainRealization")) + realized_chains = m.Allocation["FunctionalChain"]( + "ownedFunctionalChainRealizations", + (NS, "FunctionalChainRealization"), + (NS, "FunctionalChain"), + attr="targetElement", + backattr="sourceElement", + ) + realizing_chains = m.Backref["FunctionalChain"]( + (NS, "FunctionalChain"), "realized_chains" + ) + available_in_states = m.Association["capellacommon.State"]( + (ns.CAPELLACOMMON, "State"), "availableInStates" + ) + precondition = m.Single["capellacore.Constraint"]( + m.Association((ns.CAPELLACORE, "Constraint"), "preCondition") ) - scenarios = m.Backref[interaction.Scenario]( - (), "related_functions", aslist=m.ElementList + postcondition = m.Single["capellacore.Constraint"]( + m.Association((ns.CAPELLACORE, "Constraint"), "postCondition") + ) + sequence_nodes = m.Containment["ControlNode"]( + "ownedSequenceNodes", (NS, "ControlNode") + ) + sequence_links = m.Containment["SequenceLink"]( + "ownedSequenceLinks", (NS, "SequenceLink") ) + control_nodes = m.DeprecatedAccessor["ControlNode"]("sequence_nodes") -class FunctionPort(m.ModelElement): - """A function port.""" - owner = m.ParentAccessor() - exchanges: m.Accessor - state_machines = m.DirectProxyAccessor( - capellacommon.StateMachine, aslist=m.ElementList +class AbstractFunctionalChainContainer( + capellacore.CapellaElement, abstract=True +): + functional_chains = m.Containment["FunctionalChain"]( + "ownedFunctionalChains", (NS, "FunctionalChain") ) -class FunctionInputPort(FunctionPort): +class FunctionalChainInvolvement(capellacore.Involvement, abstract=True): + _xmltag = "ownedFunctionalChainInvolvements" + + +class FunctionalChainReference(FunctionalChainInvolvement): + involved = m.Single["FunctionalChain"]( + m.Association((NS, "FunctionalChain"), None) + ) + + +class FunctionPort( + information.Port, + capellacore.TypedElement, + behavior.AbstractEvent, + abstract=True, +): + represented_component_port = m.Single["ComponentPort"]( + m.Association((NS, "ComponentPort"), "representedComponentPort") + ) + realized_ports = m.Allocation["FunctionPort"]( + None, None, (NS, "FunctionPort") + ) + allocated_ports = m.Allocation["FunctionPort"]( + None, None, (NS, "FunctionPort") + ) + owner = m.DeprecatedAccessor[m.ModelElement]("parent") + exchanges = m.Backref["FunctionalExchange"]( + (NS, "FunctionalExchange"), "source", "target" + ) + state_machines = m.Containment[ # TODO not in metamodel? + "capellacommon.StateMachine" + ]("", (ns.CAPELLACOMMON, "StateMachine")) + + +class FunctionInputPort(FunctionPort, activity.InputPin): """A function input port.""" _xmltag = "inputs" - exchange_items = m.Association( - information.ExchangeItem, "incomingExchangeItems" + exchange_items = m.Association["information.ExchangeItem"]( + (ns.INFORMATION, "ExchangeItem"), "incomingExchangeItems" ) -class FunctionOutputPort(FunctionPort): +class FunctionOutputPort(FunctionPort, activity.OutputPin): """A function output port.""" _xmltag = "outputs" - exchange_items = m.Association( - information.ExchangeItem, "outgoingExchangeItems" + exchange_items = m.Association["information.ExchangeItem"]( + (ns.INFORMATION, "ExchangeItem"), "outgoingExchangeItems" ) -class Function(AbstractFunction): - """Common Code for Function's.""" +class AbstractFunctionAllocation(capellacore.Allocation, abstract=True): + pass - _xmltag = "ownedFunctions" - kind = m.EnumPOD("kind", modeltypes.FunctionKind, default="FUNCTION") +class ComponentFunctionalAllocation(AbstractFunctionAllocation): + pass - is_leaf = property(lambda self: not self.functions) - inputs = m.DirectProxyAccessor(FunctionInputPort, aslist=m.ElementList) - outputs = m.DirectProxyAccessor(FunctionOutputPort, aslist=m.ElementList) +class FunctionalChainRealization(capellacore.Allocation): + pass - exchanges: m.Accessor[m.ElementList[FunctionalExchange]] - functions: m.Accessor - packages: m.Accessor - related_exchanges: m.Accessor[m.ElementList[FunctionalExchange]] - realized_functions = m.Allocation[AbstractFunction]( - "ownedFunctionRealizations", - FunctionRealization, - attr="targetElement", - ) - realizing_functions = m.Backref[AbstractFunction]((), "realized_functions") +class ExchangeSpecificationRealization(capellacore.Allocation, abstract=True): + pass + + +class FunctionalExchangeRealization(capellacore.Allocation): + pass + + +class FunctionRealization(AbstractFunctionAllocation): + """A realization that links to a function.""" + _xmltag = "ownedFunctionRealizations" -class FunctionalExchange(AbstractExchange): - """A functional exchange.""" +class FunctionalExchange( + capellacore.Relationship, + capellacore.InvolvedElement, + activity.ObjectFlow, + behavior.AbstractEvent, + information.AbstractEventOperation, + # NOTE: NamedElement is first in the upstream metamodel, + # but that would result in an MRO conflict with AbstractEventOperation, + # which inherits from NamedElement. + capellacore.NamedElement, +): # TODO source / target? _xmltag = "ownedFunctionalExchanges" - exchange_items = m.Association(information.ExchangeItem, "exchangedItems") + exchange_specifications = m.Association["FunctionalExchangeSpecification"]( + (NS, "FunctionalExchangeSpecification"), "exchangeSpecifications" + ) + exchange_items = m.Association["information.ExchangeItem"]( + (ns.INFORMATION, "ExchangeItem"), "exchangedItems" + ) + + involving_functional_chains = m.Backref["FunctionalChain"]( + (NS, "FunctionalChain"), "involved_links" + ) realized_functional_exchanges = m.Allocation["FunctionalExchange"]( "ownedFunctionalExchangeRealizations", - "org.polarsys.capella.core.data.fa:FunctionalExchangeRealization", + (NS, "FunctionalExchangeRealization"), + (NS, "FunctionalExchange"), attr="targetElement", backattr="sourceElement", ) - realizing_functional_exchanges: m.Accessor[ - m.ElementList[FunctionalExchange] - ] - categories = m.Backref(ExchangeCategory, "exchanges") + realizing_functional_exchanges = m.Backref["FunctionalExchange"]( + (NS, "FunctionalExchange"), "realized_functional_exchanges" + ) + owner = m.Single["ComponentExchange"]( + m.Backref((NS, "ComponentExchange"), "allocated_functional_exchanges") + ) + categories = m.Backref["ExchangeCategory"]( + (NS, "ExchangeCategory"), "exchanges" + ) - @property - def owner(self) -> ComponentExchange | None: - return self.allocating_component_exchange +class AbstractFunction( + capellacore.Namespace, + capellacore.InvolvedElement, + information.AbstractInstance, + AbstractFunctionalChainContainer, + activity.CallBehaviorAction, + behavior.AbstractEvent, + abstract=True, +): + """An abstract function.""" -class FunctionalChainInvolvement(interaction.AbstractInvolvement): - """Abstract class for FunctionalChainInvolvementLink/Function.""" + _xmltag = "ownedFunctions" - _xmltag = "ownedFunctionalChainInvolvements" + kind = m.EnumPOD("kind", FunctionKind) # type: ignore[assignment] + condition = m.StringPOD("condition") + functions = m.Containment["AbstractFunction"]( + "ownedFunctions", (NS, "AbstractFunction") + ) + realized_functions = m.Allocation["AbstractFunction"]( + "ownedFunctionRealizations", + (NS, "FunctionRealization"), + (NS, "AbstractFunction"), + attr="targetElement", + backattr="sourceElement", + ) + realizing_functions = m.Backref["AbstractFunction"]( + (NS, "AbstractFunction"), "realized_functions" + ) + exchanges = m.Containment["FunctionalExchange"]( + "ownedFunctionalExchanges", (NS, "FunctionalExchange") + ) + available_in_states = m.Association["capellacommon.State"]( + (ns.CAPELLACOMMON, "State"), "availableInStates" + ) + related_exchanges = m.Backref["FunctionalExchange"]( + (NS, "FunctionalExchange"), "source.owner", "target.owner" + ) + scenarios = m.Backref["interaction.Scenario"]( + (ns.INTERACTION, "Scenario"), "related_functions" + ) + + @property + def is_leaf(self) -> bool: + return not self.functions + + +class ComponentExchange( + behavior.AbstractEvent, + information.AbstractEventOperation, + # NOTE: NamedElement comes before ExchangeSpecification in the upstream + # metamodel, but that would result in an MRO conflict. + ExchangeSpecification, + capellacore.NamedElement, +): + _xmltag = "ownedComponentExchanges" -class FunctionalChainInvolvementLink(FunctionalChainInvolvement): - """An element linking a FunctionalChain to an Exchange.""" + kind = m.EnumPOD("kind", ComponentExchangeKind) + is_oriented = m.BoolPOD("oriented") - exchanged_items = m.Association(information.ExchangeItem, "exchangedItems") - exchange_context = m.Single( - m.Association(capellacore.Constraint, "exchangeContext") + functional_exchange_allocations = m.Containment[ + "ComponentExchangeFunctionalExchangeAllocation" + ]( + "ownedComponentExchangeFunctionalExchangeAllocations", + (NS, "ComponentExchangeFunctionalExchangeAllocation"), + ) + allocated_functional_exchanges = m.Allocation["FunctionalExchange"]( + "ownedComponentExchangeFunctionalExchangeAllocations", + (NS, "ComponentExchangeFunctionalExchangeAllocation"), + (NS, "FunctionalExchange"), + attr="targetElement", + backattr="sourceElement", + ) + component_exchange_realizations = m.Containment[ + "ComponentExchangeRealization" + ]( + "ownedComponentExchangeRealizations", + (NS, "ComponentExchangeRealization"), + ) + realized_component_exchanges = m.Allocation["ComponentExchange"]( + "ownedComponentExchangeRealizations", + (NS, "ComponentExchangeRealization"), + (NS, "ComponentExchange"), + attr="targetElement", + backattr="sourceElement", + ) + realizing_component_exchanges = m.Backref["ComponentExchange"]( + (NS, "ComponentExchange"), "realized_component_exchanges" + ) + ends = m.Containment["ComponentExchangeEnd"]( + "ownedComponentExchangeEnds", (NS, "ComponentExchangeEnd") + ) + categories = m.Backref["ComponentExchangeCategory"]( + (NS, "ComponentExchangeCategory"), "exchanges" ) + allocated_exchange_items = m.DeprecatedAccessor[ + "information.ExchangeItem" + ]("convoyed_informations") -class FunctionalChainInvolvementFunction(FunctionalChainInvolvement): - """An element linking a FunctionalChain to a Function.""" + allocating_physical_links = m.Backref["cs.PhysicalLink"]( # FIXME + (ns.CS, "PhysicalLink"), "exchanges" + ) + allocating_physical_paths = m.Backref["cs.PhysicalPath"]( # FIXME + (ns.CS, "PhysicalPath"), "exchanges" + ) + @property + def allocating_physical_link(self) -> cs.PhysicalLink | None: # FIXME + warnings.warn( + ( + "ComponentExchange.allocating_physical_link is deprecated," + " use allocating_physical_links instead, which allows multiple links" + ), + DeprecationWarning, + stacklevel=2, + ) + links = self.allocating_physical_links + return links[0] if links else None -class FunctionalChainReference(FunctionalChainInvolvement): - """An element linking two related functional chains together.""" + @property + def owner(self) -> cs.PhysicalLink | None: # FIXME + return self.allocating_physical_link + @property + def exchange_items( + self, + ) -> m.ElementList[information.ExchangeItem]: # FIXME + return ( + self.allocated_exchange_items + + self.allocated_functional_exchanges.map("exchange_items") + ) -class FunctionalChain(m.ModelElement): - """A functional chain.""" - _xmltag = "ownedFunctionalChains" +class ComponentExchangeAllocation(capellacore.Allocation): + pass - kind = m.EnumPOD("kind", modeltypes.FunctionalChainKind, default="SIMPLE") - precondition = m.Single( - m.Association(capellacore.Constraint, "preCondition") - ) - postcondition = m.Single( - m.Association(capellacore.Constraint, "postCondition") - ) - involvements = m.DirectProxyAccessor( - m.ModelElement, - ( - FunctionalChainInvolvementFunction, - FunctionalChainInvolvementLink, - FunctionalChainReference, - ), - aslist=m.ElementList, - ) - involved_functions = m.Allocation[AbstractFunction]( - "ownedFunctionalChainInvolvements", - FunctionalChainInvolvementFunction, - attr="involved", - ) - involved_links = m.Allocation[AbstractExchange]( - "ownedFunctionalChainInvolvements", - FunctionalChainInvolvementLink, - attr="involved", +class ComponentExchangeAllocator(capellacore.NamedElement, abstract=True): + component_exchange_allocations = m.Containment[ + "ComponentExchangeAllocation" + ]("ownedComponentExchangeAllocations", (NS, "ComponentExchangeAllocation")) + allocated_component_exchanges = m.Allocation["ComponentExchange"]( + "ownedComponentExchangeAllocations", + (NS, "ComponentExchangeAllocation"), + (NS, "ComponentExchange"), + attr="targetElement", + backattr="sourceElement", ) - involved_chains = m.Allocation["FunctionalChain"]( - "ownedFunctionalChainInvolvements", - "org.polarsys.capella.core.data.fa:FunctionalChainReference", - attr="involved", + + +class ComponentExchangeCategory(capellacore.NamedElement): + _xmltag = "ownedComponentExchangeCategories" + + exchanges = m.Association["ComponentExchange"]( + (NS, "ComponentExchange"), "exchanges" ) - involving_chains: m.Accessor[m.ElementList[FunctionalChain]] - realized_chains = m.Allocation["FunctionalChain"]( - "ownedFunctionalChainRealizations", - "org.polarsys.capella.core.data.fa:FunctionalChainRealization", - attr="targetElement", - backattr="sourceElement", + +class ComponentExchangeEnd( + modellingcore.InformationsExchanger, capellacore.CapellaElement +): + port = m.Single["information.Port"]( + m.Association((ns.INFORMATION, "Port"), "port") ) - realizing_chains: m.Accessor[m.ElementList[FunctionalChain]] + part = m.Single["cs.Part"](m.Association((ns.CS, "Part"), "part")) - control_nodes = m.DirectProxyAccessor(ControlNode, aslist=m.ElementList) - @property - def involved(self) -> m.ElementList[AbstractFunction]: - return self.involved_functions + self.involved_links +class ComponentExchangeFunctionalExchangeAllocation( + AbstractFunctionAllocation +): + pass + + +class ComponentExchangeRealization(ExchangeSpecificationRealization): + pass -class ComponentPort(m.ModelElement): +class ComponentPort( + information.Port, modellingcore.InformationsExchanger, information.Property +): """A component port.""" _xmltag = "ownedFeatures" - direction = m.EnumPOD("orientation", modeltypes.OrientationPortKind) - owner = m.ParentAccessor() - exchanges: m.Accessor - provided_interfaces = m.Association(m.ModelElement, "providedInterfaces") - required_interfaces = m.Association(m.ModelElement, "requiredInterfaces") + orientation = m.EnumPOD("orientation", OrientationPortKind) + direction = m.DeprecatedAccessor[t.Any]("orientation") + kind = m.EnumPOD("kind", ComponentPortKind) # type: ignore[assignment] + owner = m.DeprecatedAccessor[m.ModelElement]("parent") + exchanges = m.Backref["ComponentExchange"]( + (NS, "ComponentExchange"), "source", "target" + ) + realized_ports = m.Allocation["ComponentPort"]( + None, None, (NS, "ComponentPort") + ) + allocated_ports = m.Allocation["FunctionPort"]( + None, None, (NS, "FunctionPort") + ) + +class ComponentPortAllocation(capellacore.Allocation): + ends = m.Containment["ComponentPortAllocationEnd"]( + "ownedComponentPortAllocationEnds", (NS, "ComponentPortAllocationEnd") + ) -class ComponentExchange(AbstractExchange): - """A functional component exchange.""" - _xmltag = "ownedComponentExchanges" +class ComponentPortAllocationEnd(capellacore.CapellaElement): + port = m.Single["information.Port"]( + m.Association((ns.INFORMATION, "Port"), "port") + ) + part = m.Single["cs.Part"](m.Association((ns.CS, "Part"), "part")) - kind = m.EnumPOD("kind", modeltypes.ComponentExchangeKind, default="UNSET") - allocated_functional_exchanges = m.Allocation[FunctionalExchange]( - "ownedComponentExchangeFunctionalExchangeAllocations", - ComponentExchangeFunctionalExchangeAllocation, - attr="targetElement", +class ReferenceHierarchyContext(modellingcore.ModelElement, abstract=True): + source_reference_hierarchy = m.Association["FunctionalChainReference"]( + (NS, "FunctionalChainReference"), "sourceReferenceHierarchy" ) - allocated_exchange_items = m.Association( - information.ExchangeItem, "convoyedInformations" + target_reference_hierarchy = m.Association["FunctionalChainReference"]( + (NS, "FunctionalChainReference"), "targetReferenceHierarchy" ) - categories = m.Backref(ComponentExchangeCategory, "exchanges") - @property - def owner(self) -> cs.PhysicalLink | None: - return self.allocating_physical_link - @property - def exchange_items(self) -> m.ElementList[information.ExchangeItem]: - return ( - self.allocated_exchange_items - + self.allocated_functional_exchanges.map("exchange_items") - ) +class FunctionalChainInvolvementLink( + FunctionalChainInvolvement, ReferenceHierarchyContext +): + exchange_context = m.Association["capellacore.Constraint"]( + (ns.CAPELLACORE, "Constraint"), "exchangeContext" + ) + context = m.DeprecatedAccessor["capellacore.Constraint"]( + "exchange_context" + ) + exchanged_items = m.Association["information.ExchangeItem"]( + (ns.INFORMATION, "ExchangeItem"), "exchangedItems" + ) + source = m.Single["FunctionalChainInvolvementFunction"]( # type: ignore[assignment] + m.Association((NS, "FunctionalChainInvolvementFunction"), "source") + ) + target = m.Single["FunctionalChainInvolvementFunction"]( # type: ignore[assignment] + m.Association((NS, "FunctionalChainInvolvementFunction"), "target") + ) -for _port, _exchange in [ - (ComponentPort, ComponentExchange), - (FunctionInputPort, FunctionalExchange), - (FunctionOutputPort, FunctionalExchange), -]: - _port.exchanges = m.Backref(_exchange, "source", "target") -del _port, _exchange - -ComponentExchange.realized_component_exchanges = m.Allocation[ - ComponentExchange -]( - "ownedComponentExchangeRealizations", - "org.polarsys.capella.core.data.fa:ComponentExchangeRealization", - attr="targetElement", - backattr="sourceElement", -) -ComponentExchange.realizing_component_exchanges = m.Backref( - ComponentExchange, "realized_component_exchanges" -) -FunctionalExchange.allocating_component_exchange = m.Single( - m.Backref(ComponentExchange, "allocated_functional_exchanges") -) -FunctionalExchange.realizing_functional_exchanges = m.Backref( - FunctionalExchange, "realized_functional_exchanges" -) -FunctionalExchange.involving_functional_chains = m.Backref( - FunctionalChain, "involved_links" -) - -FunctionalChain.involving_chains = m.Backref( - FunctionalChain, "involved_chains" -) -FunctionalChain.realizing_chains = m.Backref( - FunctionalChain, "realized_chains" -) - -Function.exchanges = m.DirectProxyAccessor( - FunctionalExchange, aslist=m.ElementList -) -Function.related_exchanges = m.Backref( - FunctionalExchange, "source.owner", "target.owner" -) -information.ExchangeItem.exchanges = m.Backref( - (ComponentExchange, FunctionalExchange), - "exchange_items", - "allocated_exchange_items", -) -ExchangeCategory.exchanges = m.Association( - FunctionalExchange, "exchanges", aslist=m.ElementList -) -ComponentExchangeCategory.exchanges = m.Association( - ComponentExchange, "exchanges", aslist=m.ElementList -) - - -from . import cs +class SequenceLink(capellacore.CapellaElement, ReferenceHierarchyContext): + condition = m.Single["capellacore.Constraint"]( + m.Association((ns.CAPELLACORE, "Constraint"), "condition") + ) + links = m.Association["FunctionalChainInvolvementLink"]( + (NS, "FunctionalChainInvolvementLink"), "links" + ) + source = m.Single["SequenceLinkEnd"]( + m.Association((NS, "SequenceLinkEnd"), "source") + ) + target = m.Single["SequenceLinkEnd"]( + m.Association((NS, "SequenceLinkEnd"), "target") + ) + + +class SequenceLinkEnd(capellacore.CapellaElement, abstract=True): + pass + + +class FunctionalChainInvolvementFunction( + FunctionalChainInvolvement, SequenceLinkEnd +): + pass + + +class ControlNode(SequenceLinkEnd): + _xmltag = "ownedSequenceNodes" + + kind = m.EnumPOD("kind", ControlNodeKind) diff --git a/capellambse/metamodel/information/__init__.py b/capellambse/metamodel/information/__init__.py index 0e44569c..4e556b33 100644 --- a/capellambse/metamodel/information/__init__.py +++ b/capellambse/metamodel/information/__init__.py @@ -13,102 +13,254 @@ from __future__ import annotations +import enum import typing as t +import warnings import capellambse.model as m -from .. import capellacommon, capellacore, modellingcore, modeltypes +from .. import behavior, capellacore, fa, modellingcore from .. import namespaces as ns -from . import datatype, datavalue -NS = ns.INFORMATION - - -class Unit(m.ModelElement): - """Unit.""" - - _xmltag = "ownedUnits" - - -class Association(m.ModelElement): - """An Association.""" - - _xmltag = "ownedAssociations" - - members: m.Accessor[m.ElementList[Property]] - navigable_members: m.Accessor[m.ElementList[Property]] - - @property - def roles(self) -> m.ElementList[Property]: - assert isinstance(self.members, m.ElementList) - assert isinstance(self.navigable_members, m.ElementList) - roles = [i._element for i in self.members + self.navigable_members] - return m.ElementList(self._model, roles, Property) - - -class PortAllocation(modellingcore.TraceableElement): - """An exchange between a ComponentPort and FunctionalPort.""" +if t.TYPE_CHECKING: + from .. import capellacommon, cs # noqa: F401 - _xmltag = "ownedPortAllocations" +NS = ns.INFORMATION +NS_DV = ns.INFORMATION_DATAVALUE +NS_DT = ns.INFORMATION_DATATYPE +NS_COMM = ns.INFORMATION_COMMUNICATION + + +@m.stringy_enum +@enum.unique +class AggregationKind(enum.Enum): + """Defines the specific kind of a relationship, as per UML definitions.""" + + UNSET = "UNSET" + """Used when value is not defined by the user.""" + ASSOCIATION = "ASSOCIATION" + """A semantic relationship between typed instances. + + It has at least two ends represented by properties, each of which is + connected to the type of the end. More than one end of the + association may have the same type. + + Indicates that the property has no aggregation. + """ + AGGREGATION = "AGGREGATION" + """A semantic relationship between a part and a whole. + + The part has a lifecycle of its own, and is potentially shared among + several aggregators. + """ + COMPOSITION = "COMPOSITION" + """A semantic relationship between whole and its parts. + + The parts lifecycles are tied to that of the whole, and they are not + shared with any other aggregator. + """ + + +@m.stringy_enum +@enum.unique +class CollectionKind(enum.Enum): + """Defines the specific kind of a Collection structure.""" + + ARRAY = "ARRAY" + """The collection is to be considered an array of elements.""" + SEQUENCE = "SEQUENCE" + """The collection is to be considered as a sequence (list) of elements.""" + + +@m.stringy_enum +@enum.unique +class ElementKind(enum.Enum): + """The visibility options for features of a class.""" + + TYPE = "TYPE" + """The ExchangeItemElement is a type for its ExchangeItem.""" + MEMBER = "MEMBER" + """The ExchangeItemElement is a member for its ExchangeItem.""" + + +@m.stringy_enum +@enum.unique +class ExchangeMechanism(enum.Enum): + """Enumeration of the different exchange mechanisms.""" + + UNSET = "UNSET" + """The exchange mechanism is not defined.""" + FLOW = "FLOW" + """Continuous supply of data.""" + OPERATION = "OPERATION" + """Sporadic supply of data with returned data.""" + EVENT = "EVENT" + """Asynchronous information that is taken into account rapidly.""" + SHARED_DATA = "SHARED_DATA" + + +@m.stringy_enum +@enum.unique +class ParameterDirection(enum.Enum): + """The direction in which data is passed along through a parameter.""" + + IN = "IN" + """The parameter represents an input of the operation it is used in.""" + OUT = "OUT" + """The parameter represents an output of the operation it is used in.""" + INOUT = "INOUT" + """The parameter represents both an input and output of the operation.""" + RETURN = "RETURN" + """The parameter represents the return value of the operation.""" + EXCEPTION = "EXCEPTION" + """The parameter is like an exception.""" + UNSET = "UNSET" + """The CommunicationLink protocol is not yet set.""" + + +@m.stringy_enum +@enum.unique +class PassingMode(enum.Enum): + """The data passing mechanism for parameters of an operation.""" + + UNSET = "UNSET" + """The data passing mechanism is not precised.""" + BY_REF = "BY_REF" + """The data is being passed by reference to the operation.""" + BY_VALUE = "BY_VALUE" + """The data is being passed by value to the operation.""" + + +@m.stringy_enum +@enum.unique +class SynchronismKind(enum.Enum): + """The synchronicity of an operation invocation.""" + + UNSET = "UNSET" + SYNCHRONOUS = "SYNCHRONOUS" + ASYNCHRONOUS = "ASYNCHRONOUS" + + +@m.stringy_enum +@enum.unique +class UnionKind(enum.Enum): + UNION = "UNION" + VARIANT = "VARIANT" + + +class MultiplicityElement(capellacore.CapellaElement, abstract=True): + is_ordered = m.BoolPOD("ordered") + """Indicates if this element is ordered.""" + is_unique = m.BoolPOD("unique") + """Indicates if this element is unique.""" + is_min_inclusive = m.BoolPOD("minInclusive") + is_max_inclusive = m.BoolPOD("maxInclusive") + default_value = m.Single["datavalue.NumericValue"]( + m.Containment("ownedDefaultValue", (NS_DV, "NumericValue")) + ) + min_value = m.Single["datavalue.NumericValue"]( + m.Containment("ownedMinCard", (NS_DV, "NumericValue")) + ) + max_value = m.Single["datavalue.NumericValue"]( + m.Containment("ownedMinCard", (NS_DV, "NumericValue")) + ) + null_value = m.Single["datavalue.NumericValue"]( + m.Containment("ownedMinCard", (NS_DV, "NumericValue")) + ) + min_card = m.Single["datavalue.NumericValue"]( + m.Containment("ownedMinCard", (NS_DV, "NumericValue")) + ) + min_length = m.Single["datavalue.NumericValue"]( + m.Containment("ownedMinLength", (NS_DV, "NumericValue")) + ) + max_card = m.Single["datavalue.NumericValue"]( + m.Containment("ownedMaxCard", (NS_DV, "NumericValue")) + ) + max_length = m.Single["datavalue.NumericValue"]( + m.Containment("ownedMaxLength", (NS_DV, "NumericValue")) + ) -class Property(m.ModelElement): +class Property( + capellacore.Feature, + capellacore.TypedElement, + MultiplicityElement, + modellingcore.FinalizableElement, +): """A Property of a Class.""" _xmltag = "ownedFeatures" - is_ordered = m.BoolPOD("ordered") - """Indicates if property is ordered.""" - is_unique = m.BoolPOD("unique") - """Indicates if property is unique.""" - is_abstract = m.BoolPOD("isAbstract") + aggregation_kind = m.EnumPOD("aggregationKind", AggregationKind) + kind = m.DeprecatedAccessor["t.Any"]("aggregation_kind") + is_abstract = m.BoolPOD("isAbstract") # TODO not in metamodel? """Indicates if property is abstract.""" is_static = m.BoolPOD("isStatic") """Indicates if property is static.""" - is_part_of_key = m.BoolPOD("isPartOfKey") - """Indicates if property is part of key.""" is_derived = m.BoolPOD("isDerived") """Indicates if property is abstract.""" is_read_only = m.BoolPOD("isReadOnly") """Indicates if property is read-only.""" - visibility = m.EnumPOD( - "visibility", modeltypes.VisibilityKind, default="UNSET" + is_part_of_key = m.BoolPOD("isPartOfKey") + """Indicates if property is part of key.""" + association = m.Single["Association"]( + m.Backref((NS, "Association"), "roles") ) - kind = m.EnumPOD( - "aggregationKind", modeltypes.AggregationKind, default="UNSET" + + +class AbstractInstance(Property, abstract=True): + pass + + +class AssociationPkg(capellacore.Structure, abstract=True): + visibility = m.EnumPOD("visibility", capellacore.VisibilityKind) + associations = m.Containment["Association"]( + "ownedAssociations", (NS, "Association") ) - type = m.Single(m.Association(m.ModelElement, "abstractType")) - default_value = m.Single[t.Any](m.Containment("ownedDefaultValue")) - min_value = m.Single[t.Any](m.Containment("ownedMinValue")) - max_value = m.Single[t.Any](m.Containment("ownedMaxValue")) - null_value = m.Single[t.Any](m.Containment("ownedNullValue")) - min_card = m.Single[t.Any](m.Containment("ownedMinCard")) - max_card = m.Single[t.Any](m.Containment("ownedMaxCard")) - association = m.Single[t.Any](m.Backref(Association, "roles")) -class Class(m.ModelElement): - """A Class.""" +class Association(capellacore.NamedRelationship): + _xmltag = "ownedAssociations" + + members = m.Containment["Property"]("ownedMembers", (NS, "Property")) + navigable_members = m.Association["Property"]( + (NS, "Property"), "navigableMembers" + ) + @property + def roles(self) -> m.ElementList[Property]: + assert isinstance(self.members, m.ElementList) + assert isinstance(self.navigable_members, m.ElementList) + roles = [i._element for i in self.members + self.navigable_members] + return m.ElementList(self._model, roles, Property) + + +class Class(capellacore.GeneralClass): _xmltag = "ownedClasses" - sub: m.Accessor - super: m.Accessor[Class] - is_abstract = m.BoolPOD("abstract") - """Indicates if class is abstract.""" - is_final = m.BoolPOD("final") - """Indicates if class is final.""" is_primitive = m.BoolPOD("isPrimitive") """Indicates if class is primitive.""" - visibility = m.EnumPOD( - "visibility", modeltypes.VisibilityKind, default="UNSET" + key_parts = m.Association["KeyPart"]((NS, "KeyPart"), "keyParts") + state_machines = m.Containment["capellacommon.StateMachine"]( + "ownedStateMachines", (ns.CAPELLACOMMON, "StateMachine") ) - state_machines = m.DirectProxyAccessor( - capellacommon.StateMachine, aslist=m.ElementList + data_values = m.Containment["datavalue.DataValue"]( + "ownedDataValues", (NS_DV, "DataValue") ) - owned_properties = m.DirectProxyAccessor(Property, aslist=m.ElementList) - generalizations = m.DirectProxyAccessor( - capellacore.Generalization, aslist=m.ElementList + realizations = m.Containment["InformationRealization"]( + "ownedInformationRealizations", (NS, "InformationRealization") + ) + realized_classes = m.Allocation["Class"]( + "ownedInformationRealizations", + (NS, "InformationRealization"), + (NS, "Class"), + attr="targetElement", + backattr="sourceElement", + ) + realized_by = m.Backref["Class"]((NS, "Class"), "realized_classes") + + owned_properties = m.DirectProxyAccessor( # TODO not in metamodel? + Property, aslist=m.ElementList ) @property @@ -121,115 +273,306 @@ def properties(self) -> m.ElementList[Property]: ) -class InformationRealization(modellingcore.TraceableElement): - """A realization for a Class.""" +from . import datavalue as datavalue - _xmltag = "ownedInformationRealizations" +class Collection( + capellacore.Classifier, + MultiplicityElement, + datavalue.DataValueContainer, + modellingcore.FinalizableElement, +): + """A Collection.""" -class Union(Class): - """A Union.""" + _xmltag = "ownedCollections" - _xmltag = "ownedClasses" + is_primitive = m.BoolPOD("isPrimitive") + visibility = m.EnumPOD("visibility", capellacore.VisibilityKind) + kind = m.EnumPOD("kind", CollectionKind) + aggregation_kind = m.EnumPOD("aggregationKind", AggregationKind) + type = m.Association["capellacore.Type"]((ns.CAPELLACORE, "Type"), "type") + index = m.Association["datatype.DataType"]((NS_DT, "DataType"), "index") - kind = m.EnumPOD("kind", modeltypes.UnionKind, default="UNION") +class AbstractCollectionValue(datavalue.DataValue, abstract=True): + pass -class Collection(m.ModelElement): - """A Collection.""" - _xmltag = "ownedCollections" +class CollectionValue(AbstractCollectionValue): + elements = m.Containment["datavalue.DataValue"]( + "ownedElements", (NS_DV, "DataValue") + ) + default_element = m.Containment["datavalue.DataValue"]( + "ownedDefaultElement", (NS_DV, "DataValue") + ) + - kind = m.EnumPOD("kind", modeltypes.CollectionKind, default="ARRAY") +class CollectionValueReference(AbstractCollectionValue): + value = m.Association["AbstractCollectionValue"]( + (NS, "AbstractCollectionValue"), "referencedValue" + ) + property = m.Association["Property"]( + (NS, "Property"), "referencedProperty" + ) - sub: m.Accessor - super: m.Accessor[Collection] +from . import communication as communication -class DataPkg(m.ModelElement): + +class DataPkg( + capellacore.AbstractDependenciesPkg, + capellacore.AbstractExchangeItemPkg, + AssociationPkg, + datavalue.DataValueContainer, + communication.MessageReferencePkg, +): """A data package that can hold classes.""" _xmltag = "ownedDataPkgs" - owned_associations = m.DirectProxyAccessor( - Association, aslist=m.ElementList - ) - classes = m.DirectProxyAccessor(Class, aslist=m.ElementList) - unions = m.DirectProxyAccessor(Union, aslist=m.ElementList) - collections = m.DirectProxyAccessor(Collection, aslist=m.ElementList) - enumerations = m.DirectProxyAccessor( - datatype.Enumeration, aslist=m.ElementList - ) - datatypes = m.DirectProxyAccessor( - m.ModelElement, - ( - datatype.BooleanType, - datatype.Enumeration, - datatype.StringType, - datatype.NumericType, - datatype.PhysicalQuantity, - ), - aslist=m.MixedElementList, - ) - complex_values = m.DirectProxyAccessor( - datavalue.ComplexValue, aslist=m.ElementList - ) packages: m.Accessor + classes = m.Containment["Class"]("ownedClasses", (NS, "Class")) + key_parts = m.Containment["KeyPart"]("ownedKeyParts", (NS, "KeyPart")) + collections = m.Containment["Collection"]( + "ownedCollections", (NS, "Collection") + ) + units = m.Containment["Unit"]("ownedUnits", (NS, "Unit")) + data_types = m.Containment["datatype.DataType"]( + "ownedDataTypes", (NS_DT, "DataType") + ) + datatypes = m.DeprecatedAccessor["datatype.DataType"]("data_types") + signals = m.Containment["communication.Signal"]( + "ownedSignals", (NS_COMM, "Signal") + ) + messages = m.Containment["communication.Message"]( + "ownedMessages", (NS_COMM, "Message") + ) + exceptions = m.Containment["communication.Exception"]( + "ownedExceptions", (NS_COMM, "Exception") + ) + state_events = m.Containment["capellacommon.StateEvent"]( + "ownedStateEvents", (ns.CAPELLACOMMON, "StateEvent") + ) + owned_associations = m.DeprecatedAccessor["Association"]("associations") + + @property + def unions(self) -> m.ElementList[Union]: + warnings.warn( + ( + f"{type(self).__name__}.unions is deprecated," + " use '.classes.by___class__(Union)' instead" + ), + DeprecationWarning, + stacklevel=2, + ) + return self.classes.by___class__(Union) + @property + def enumeration(self) -> m.ElementList[datatype.Enumeration]: + warnings.warn( + ( + f"{type(self).__name__}.enumerations is deprecated," + " use '.data_types.by___class__(Enumeration)' instead" + ), + DeprecationWarning, + stacklevel=2, + ) + return self.data_types.by___class__(datatype.Enumeration) -class ExchangeItemElement(m.ModelElement): - """An ExchangeItemElement (proxy link).""" - _xmltag = "ownedElements" +class DomainElement(Class): + pass + + +class KeyPart(capellacore.Relationship): + property = m.Single["Property"]( + m.Association((NS, "Property"), "property") + ) + + +class AbstractEventOperation(capellacore.NamedElement, abstract=True): + pass + + +class Operation( + capellacore.Feature, + behavior.AbstractEvent, + AbstractEventOperation, + abstract=True, +): + parameters = m.Containment["Parameter"]( + "ownedParameters", (NS, "Parameter") + ) + operation_allocations = m.Containment["OperationAllocation"]( + "ownedOperationAllocation", (NS, "OperationAllocation") + ) + allocated_operations = m.Allocation["Operation"]( + "ownedOperationAllocation", + (NS, "OperationAllocation"), + (NS, "Operation"), + attr="targetElement", + backattr="sourceElement", + ) + allocating_operations = m.Backref["Operation"]( + (NS, "Operation"), "allocated_operations" + ) + exchange_item_realizations = m.Containment["ExchangeItemRealization"]( + "ownedExchangeItemRealizations", (NS, "ExchangeItemRealization") + ) + realized_exchange_items = m.Allocation["ExchangeItem"]( + "ownedExchangeItemRealizations", + (NS, "ExchangeItemRealization"), + (NS, "ExchangeItem"), + attr="targetElement", + backattr="sourceElement", + ) + + +class OperationAllocation(capellacore.Allocation): + pass + + +class Parameter( + capellacore.TypedElement, + MultiplicityElement, + modellingcore.AbstractParameter, +): + direction = m.EnumPOD("direction", ParameterDirection) + passing_mode = m.EnumPOD("passingMode", PassingMode) + + +class Service(Operation): + synchronism_kind = m.EnumPOD("synchronismKind", SynchronismKind) + thrown_exceptions = m.Association["communication.Exception"]( + (NS_COMM, "Exception"), "thrownExceptions" + ) + message_references = m.Association["communication.MessageReference"]( + (NS_COMM, "MessageReference"), "messageReferences" + ) + + +class Union(Class): + """A Union.""" + + _xmltag = "ownedClasses" + + kind = m.EnumPOD("kind", UnionKind) + discriminant = m.Association["UnionProperty"]( + (NS, "UnionProperty"), "discriminant" + ) + default_property = m.Association["UnionProperty"]( + (NS, "UnionProperty"), "defaultProperty" + ) - abstract_type = m.Single(m.Association(m.ModelElement, "abstractType")) - owner = m.ParentAccessor() - min_card = m.Single[t.Any](m.Containment("ownedMinCard")) - max_card = m.Single[t.Any](m.Containment("ownedMaxCard")) +class UnionProperty(Property): + qualifier = m.Association["datavalue.DataValue"]( + (NS_DV, "DataValue"), "qualifier" + ) -class ExchangeItem(m.ModelElement): - """An item that can be exchanged on an Exchange.""" +class Unit(capellacore.NamedElement): + _xmltag = "ownedUnits" + +class Port(capellacore.NamedElement, abstract=True): + protocols = m.Containment["capellacommon.StateMachine"]( + "ownedProtocols", (ns.CAPELLACOMMON, "StateMachine") + ) + provided_interfaces = m.Association["cs.Interface"]( + (ns.CS, "Interface"), "providedInterfaces" + ) + required_interfaces = m.Association["cs.Interface"]( + (ns.CS, "Interface"), "requiredInterfaces" + ) + port_realizations = m.Containment["PortRealization"]( + "ownedPortRealizations", (NS, "PortRealization") + ) + realized_ports = m.Allocation["Port"]( + "ownedPortRealizations", + (NS, "PortRealization"), + (NS, "Port"), + attr="targetElement", + backattr="sourceElement", + ) + port_allocations = m.Containment["PortAllocation"]( + "ownedPortAllocations", (NS, "PortAllocation") + ) + allocated_ports = m.Allocation["Port"]( + "ownedPortRealizations", + (NS, "PortRealization"), + (NS, "Port"), + attr="targetElement", + backattr="sourceElement", + ) + + +class PortRealization(capellacore.Allocation): + pass + + +class PortAllocation(capellacore.Allocation): + _xmltag = "ownedPortAllocations" + + +class ExchangeItem( + modellingcore.AbstractExchangeItem, + behavior.AbstractEvent, + behavior.AbstractSignal, + modellingcore.FinalizableElement, + capellacore.GeneralizableElement, +): _xmltag = "ownedExchangeItems" - type = m.EnumPOD( - "exchangeMechanism", modeltypes.ExchangeMechanism, default="UNSET" + exchange_mechanism = m.EnumPOD("exchangeMechanism", ExchangeMechanism) + type = m.DeprecatedAccessor["t.Any"]("exchange_mechanism") + elements = m.Containment["ExchangeItemElement"]( + "ownedElements", (NS, "ExchangeItemElement") + ) + information_realizations = m.Containment["InformationRealization"]( + "ownedInformationRealizations", (NS, "InformationRealization") + ) + instances = m.Containment["ExchangeItemInstance"]( + "ownedExchangeItemInstances", (NS, "ExchangeItemInstance") + ) + + @property + def exchanges( + self, + ) -> m.ElementList[fa.ComponentExchange | fa.FunctionalExchange]: + """Exchanges using this ExchangeItem.""" + CX = (ns.FA, "ComponentExchange") + FX = (ns.FA, "FunctionalExchange") + cxs = self._model.search(CX).by_allocated_exchange_items(self) + fxs = self._model.search(FX).by_exchanged_items(self) + return cxs + fxs + + +class ExchangeItemElement( + MultiplicityElement, capellacore.TypedElement, capellacore.NamedElement +): + _xmltag = "ownedElements" + + kind = m.EnumPOD("kind", ElementKind) + direction = m.EnumPOD("direction", ParameterDirection) + is_composite = m.BoolPOD("composite") + referenced_properties = m.Association["Property"]( + (NS, "Property"), "referencedProperties" ) - elements = m.DirectProxyAccessor(ExchangeItemElement, aslist=m.ElementList) - exchanges: m.Accessor[m.ElementList[m.ModelElement]] - instances: m.Containment + abstract_type = m.DeprecatedAccessor["modellingcore.AbstractType"]("type") + owner = m.DeprecatedAccessor["m.ModelElement"]("parent") + + +class ExchangeItemInstance(AbstractInstance): + pass + + +class InformationRealization(capellacore.Allocation): + _xmltag = "ownedInformationRealizations" -class ExchangeItemInstance(Property): +class ExchangeItemRealization(capellacore.Allocation): pass -capellacore.Generalization.super = m.Single(m.Association(None, "super")) -for cls in [Class, Union, datatype.Enumeration, Collection]: - cls.super = m.Single( - m.Allocation( - "ownedGeneralizations", - capellacore.Generalization, - attr="super", - backattr="sub", - ), - ) - cls.sub = m.Backref(cls, "super") - -DataPkg.packages = m.DirectProxyAccessor(DataPkg, aslist=m.ElementList) -Association.members = m.Containment("ownedMembers") -Association.navigable_members = m.Association(Property, "navigableMembers") -Class.realized_classes = m.Allocation( - "ownedInformationRealizations", - InformationRealization, - attr="targetElement", -) -Class.realizations = m.DirectProxyAccessor( - InformationRealization, aslist=m.ElementList -) -Class.realized_by = m.Backref(Class, "realized_classes") -ExchangeItem.instances = m.Containment( - "ownedExchangeItemInstances", ExchangeItemInstance -) +from . import datatype as datatype diff --git a/capellambse/metamodel/information/communication.py b/capellambse/metamodel/information/communication.py index 1d4419ba..a208d0e1 100644 --- a/capellambse/metamodel/information/communication.py +++ b/capellambse/metamodel/information/communication.py @@ -1,5 +1,124 @@ # SPDX-FileCopyrightText: Copyright DB InfraGO AG # SPDX-License-Identifier: Apache-2.0 +from __future__ import annotations + +import enum +import typing as t + +import capellambse.model as m + +from .. import behavior, capellacore from .. import namespaces as ns +from . import datavalue + +if t.TYPE_CHECKING: + from .. import capellacommon # noqa: F401 + from . import ExchangeItem # noqa: F401 NS = ns.INFORMATION_COMMUNICATION + + +@m.stringy_enum +@enum.unique +class CommunicationLinkKind(enum.Enum): + """Enumeration listing the various possibilities of communication links.""" + + UNSET = "UNSET" + """The CommunicationLink protocol is not yet set.""" + PRODUCE = "PRODUCE" + """The CommunicationLink describes a production of ExchangeItem.""" + CONSUME = "CONSUME" + """The CommunicationLink describes a comsumption of ExchangeItem.""" + SEND = "SEND" + """The CommunicationLink describes a sending of ExchangeItem.""" + RECEIVE = "RECEIVE" + """The CommunicationLink describes a reception of ExchangeItem.""" + CALL = "CALL" + """The CommunicationLink describes a call of ExchangeItem.""" + EXECUTE = "EXECUTE" + """The CommunicationLink describes an execution of ExchangeItem.""" + WRITE = "WRITE" + """The CommunicationLink describes a writing of ExchangeItem.""" + ACCESS = "ACCESS" + """The CommunicationLink describes an access to the ExchangeItem.""" + ACQUIRE = "ACQUIRE" + """The CommunicationLink describes an acquisition of ExchangeItem.""" + TRANSMIT = "TRANSMIT" + """The CommunicationLink describes a transmission of ExchangeItem.""" + + +@m.stringy_enum +@enum.unique +class CommunicationLinkProtocol(enum.Enum): + """The various possibilities for the protocol of the communication link.""" + + UNSET = "UNSET" + """The CommunicationLink protocol is not yet set.""" + UNICAST = "UNICAST" + """Describes sending an ExchangeItem using the unicast protocol.""" + MULTICAST = "MULTICAST" + """Describes sending an ExchangeItem using the multicast protocol.""" + BROADCAST = "BROADCAST" + """Describes sending an ExchangeItem using the broadcast protocol.""" + SYNCHRONOUS = "SYNCHRONOUS" + """Describes a call of the ExchangeItem using the synchronous protocol.""" + ASYNCHRONOUS = "ASYNCHRONOUS" + """Describes a call of the ExchangeItem using the asynchronous protocol.""" + READ = "READ" + """Describes access to the ExchangeItem by reading it.""" + ACCEPT = "ACCEPT" + """Describes access to the ExchangeItem by accepting it.""" + + +class CommunicationItem( + capellacore.Classifier, datavalue.DataValueContainer, abstract=True +): + visibility = m.EnumPOD("visibility", capellacore.VisibilityKind) + state_machines = m.Containment["capellacommon.StateMachine"]( + "ownedStateMachines", (ns.CAPELLACOMMON, "StateMachine") + ) + + +class Exception(CommunicationItem): + pass + + +class Message(CommunicationItem): + pass + + +class MessageReference(capellacore.Relationship): + message = m.Single["Message"](m.Association((NS, "Message"), "message")) + + +class MessageReferencePkg(capellacore.Structure, abstract=True): + message_references = m.Containment["MessageReference"]( + "ownedMessageReferences", (NS, "MessageReference") + ) + + +class Signal(CommunicationItem, behavior.AbstractSignal): + instances = m.Containment["SignalInstance"]( + "signalInstances", (NS, "SignalInstance") + ) + + +from . import AbstractInstance + + +class SignalInstance(AbstractInstance): + pass + + +class CommunicationLink(capellacore.CapellaElement): + kind = m.EnumPOD("kind", CommunicationLinkKind) + protocol = m.EnumPOD("protocol", CommunicationLinkProtocol) + exchange_item = m.Association["ExchangeItem"]( + (NS, "ExchangeItem"), "exchangeItem" + ) + + +class CommunicationLinkExchanger(m.ModelElement, abstract=True): + links = m.Containment["CommunicationLink"]( + "ownedCommunicationLinks", (NS, "CommunicationLink") + ) diff --git a/capellambse/metamodel/information/datatype.py b/capellambse/metamodel/information/datatype.py index 769a8e7e..2274c6f7 100644 --- a/capellambse/metamodel/information/datatype.py +++ b/capellambse/metamodel/information/datatype.py @@ -2,50 +2,85 @@ # SPDX-License-Identifier: Apache-2.0 from __future__ import annotations +import enum import typing as t import capellambse.model as m -from .. import modeltypes +from .. import capellacore, modellingcore from .. import namespaces as ns from . import datavalue +if t.TYPE_CHECKING: + from . import InformationRealization, Unit # noqa: F401 + NS = ns.INFORMATION_DATATYPE +NS_DV = ns.INFORMATION_DATAVALUE + + +@m.stringy_enum +@enum.unique +class NumericTypeKind(enum.Enum): + """The kind of this numeric data type.""" + INTEGER = "INTEGER" + FLOAT = "FLOAT" -class DataType(m.ModelElement): + +class DataType( + capellacore.GeneralizableElement, + datavalue.DataValueContainer, + modellingcore.FinalizableElement, + abstract=True, +): _xmltag = "ownedDataTypes" is_discrete = m.BoolPOD("discrete") """Whether or not this data type characterizes a discrete value.""" - min_inclusive = m.BoolPOD("minInclusive") - max_inclusive = m.BoolPOD("maxInclusive") + is_min_inclusive = m.BoolPOD("minInclusive") + is_max_inclusive = m.BoolPOD("maxInclusive") + min_inclusive = m.DeprecatedAccessor["t.Any"]("is_min_inclusive") + max_inclusive = m.DeprecatedAccessor["t.Any"]("is_max_inclusive") pattern = m.StringPOD("pattern") """Textual specification of a constraint associated to this data type.""" - visibility = m.EnumPOD( - "visibility", modeltypes.VisibilityKind, default="UNSET" + visibility = m.EnumPOD("visibility", capellacore.VisibilityKind) + information_realizations = m.Containment["InformationRealization"]( + "ownedInformationRealizations", + (ns.INFORMATION, "InformationRealization"), ) class BooleanType(DataType): - literals = m.DirectProxyAccessor( - datavalue.LiteralBooleanValue, - aslist=m.ElementList, - fixed_length=2, + literals = m.Containment["datavalue.LiteralBooleanValue"]( + "ownedLiterals", (NS_DV, "LiteralBooleanValue"), fixed_length=2 + ) + default_value = m.Single["datavalue.AbstractBooleanValue"]( + m.Containment("ownedDefaultValue", (NS_DV, "AbstractBooleanValue")) + ) + default = m.DeprecatedAccessor["datavalue.AbstractBooleanValue"]( + "default_value" ) - default = m.Single[t.Any](m.Containment("ownedDefaultValue")) class Enumeration(DataType): - """An Enumeration.""" - - domain_type = m.Single(m.Association(m.ModelElement, "domainType")) - owned_literals = m.DirectProxyAccessor( - datavalue.EnumerationLiteral, aslist=m.ElementList + owned_literals = m.Containment["datavalue.EnumerationLiteral"]( + "ownedLiterals", (NS_DV, "EnumerationLiteral") + ) + default_value = m.Single["datavalue.AbstractEnumerationValue"]( + m.Containment("ownedDefaultValue", (NS_DV, "AbstractEnumerationValue")) + ) + null_value = m.Single["datavalue.AbstractEnumerationValue"]( + m.Containment("ownedNullValue", (NS_DV, "AbstractEnumerationValue")) + ) + min_value = m.Single["datavalue.AbstractEnumerationValue"]( + m.Containment("ownedMinValue", (NS_DV, "AbstractEnumerationValue")) + ) + max_value = m.Single["datavalue.AbstractEnumerationValue"]( + m.Containment("ownedMaxValue", (NS_DV, "AbstractEnumerationValue")) + ) + domain_type = m.Single["DataType"]( + m.Association((NS, "DataType"), "domainType") ) - - sub: m.Accessor - super: m.Accessor[Enumeration] @property def literals(self) -> m.ElementList[datavalue.EnumerationLiteral]: @@ -58,19 +93,35 @@ def literals(self) -> m.ElementList[datavalue.EnumerationLiteral]: class StringType(DataType): - default_value = m.Single[t.Any](m.Containment("ownedDefaultValue")) - null_value = m.Single[t.Any](m.Containment("ownedNullValue")) - min_length = m.Single[t.Any](m.Containment("ownedMinLength")) - max_length = m.Single[t.Any](m.Containment("ownedMaxLength")) + default_value = m.Single["datavalue.AbstractStringValue"]( + m.Containment("ownedDefaultValue", (NS_DV, "AbstractStringValue")) + ) + null_value = m.Single["datavalue.AbstractStringValue"]( + m.Containment("ownedNullValue", (NS_DV, "AbstractStringValue")) + ) + min_length = m.Single["datavalue.NumericValue"]( + m.Containment("ownedMinLength", (NS_DV, "NumericValue")) + ) + max_length = m.Single["datavalue.NumericValue"]( + m.Containment("ownedMaxLength", (NS_DV, "NumericValue")) + ) class NumericType(DataType): - kind = m.EnumPOD("kind", modeltypes.NumericTypeKind, default="INTEGER") - default_value = m.Single[t.Any](m.Containment("ownedDefaultValue")) - null_value = m.Single[t.Any](m.Containment("ownedNullValue")) - min_value = m.Single[t.Any](m.Containment("ownedMinValue")) - max_value = m.Single[t.Any](m.Containment("ownedMaxValue")) + kind = m.EnumPOD("kind", NumericTypeKind) + default_value = m.Single["datavalue.NumericValue"]( + m.Containment("ownedDefaultValue", (NS_DV, "NumericValue")) + ) + null_value = m.Single["datavalue.NumericValue"]( + m.Containment("ownedNullValue", (NS_DV, "NumericValue")) + ) + min_value = m.Single["datavalue.NumericValue"]( + m.Containment("ownedMinValue", (NS_DV, "NumericValue")) + ) + max_value = m.Single["datavalue.NumericValue"]( + m.Containment("ownedMaxValue", (NS_DV, "NumericValue")) + ) class PhysicalQuantity(NumericType): - unit = m.Single[t.Any](m.Containment("ownedUnit")) + unit = m.Single["Unit"](m.Association((NS, "Unit"), "unit")) diff --git a/capellambse/metamodel/information/datavalue.py b/capellambse/metamodel/information/datavalue.py index 0cf716be..f4a78f89 100644 --- a/capellambse/metamodel/information/datavalue.py +++ b/capellambse/metamodel/information/datavalue.py @@ -1,69 +1,249 @@ # SPDX-FileCopyrightText: Copyright DB InfraGO AG # SPDX-License-Identifier: Apache-2.0 +from __future__ import annotations + +import enum import typing as t +import warnings import capellambse.model as m +from .. import capellacore, modellingcore from .. import namespaces as ns +if t.TYPE_CHECKING: + from . import Property # noqa: F401 + NS = ns.INFORMATION_DATAVALUE -class LiteralBooleanValue(m.ModelElement): - """A Literal Boolean Value.""" +@m.stringy_enum +@enum.unique +class BinaryOperator(enum.Enum): + """Specifies the kind of this binary operator.""" + + UNSET = "UNSET" + """The binary operator is not initialized.""" + ADD = "ADD" + """The binary operator refers to an addition.""" + MUL = "MUL" + """The binary operator refers to a multiplication.""" + SUB = "SUB" + """The binary operator refers to a substraction.""" + DIV = "DIV" + """The binary operator refers to a division.""" + POW = "POW" + """The binary operator refers to a power operation.""" + MIN = "MIN" + """The binary operator refers to a min operation.""" + MAX = "MAX" + """The binary operator refers to a max operation.""" + EQU = "EQU" + """The binary operator refers to an equal operation.""" + IOR = "IOR" + """The binary operator refers to a logical inclusive OR operation.""" + XOR = "XOR" + """The binary operator refers to a logical exclusive OR operation.""" + AND = "AND" + """The binary operator refers to a logical AND operation.""" + + +@m.stringy_enum +@enum.unique +class UnaryOperator(enum.Enum): + """Specifies the kind of this unary operator.""" + + UNSET = "UNSET" + """The unary operator is not initialized.""" + NOT = "NOT" + """The unary operator refers to a NOT operation.""" + POS = "POS" + """The unary operator refers to a position operation.""" + VAL = "VAL" + """The unary operator refers to a value operation.""" + SUC = "SUC" + """The unary operator refers to a successor operation.""" + PRE = "PRE" + """The unary operator refers to a predecessor operation.""" + + +class DataValue( + capellacore.NamedElement, modellingcore.ValueSpecification, abstract=True +): + is_abstract = m.BoolPOD("abstract") + + +class DataValueContainer(capellacore.Structure, abstract=True): + data_values = m.Containment["DataValue"]( + "ownedDataValues", (NS, "DataValue") + ) + + @property + def complex_values(self) -> m.ElementList[AbstractComplexValue]: + warnings.warn( + ( + f"{type(self).__name__}.complex_values is deprecated," + " use '.data_values.by___class__(AbstractComplexValue)' instead" + ), + DeprecationWarning, + stacklevel=2, + ) + return self.data_values.by___class__(AbstractComplexValue) + +class AbstractBooleanValue(DataValue, abstract=True): + pass + + +class LiteralBooleanValue(AbstractBooleanValue): _xmltag = "ownedLiterals" value = m.BoolPOD("value") -class LiteralValue(m.ModelElement): - is_abstract = m.BoolPOD("abstract") - """Indicates if property is abstract.""" - value = m.StringPOD("value") - type = m.Single(m.Association(m.ModelElement, "abstractType")) +class BooleanReference(AbstractBooleanValue): + value = m.Single["AbstractBooleanValue"]( + m.Association((NS, "AbstractBooleanValue"), "referencedValue") + ) + property = m.Single["Property"]( + m.Association((NS, "Property"), "referencedProperty") + ) + + +class AbstractEnumerationValue(DataValue, abstract=True): + pass + + +class EnumerationLiteral(AbstractEnumerationValue, eq="name"): + _xmltag = "ownedLiterals" + + value = m.Single["DataValue"]( + m.Containment("domainValue", (NS, "DataValue")) + ) + + owner = m.DeprecatedAccessor["m.ModelElement"]("parent") + + +class EnumerationReference(AbstractEnumerationValue): + value = m.Single["AbstractEnumerationValue"]( + m.Association(m.ModelElement, "referencedValue") + ) + property = m.Single["Property"]( + m.Association((NS, "Property"), "referencedProperty") + ) -class LiteralNumericValue(LiteralValue): +class AbstractStringValue(DataValue, abstract=True): + pass + + +class LiteralStringValue(AbstractStringValue): value = m.StringPOD("value") - unit = m.Single(m.Association(m.ModelElement, "unit")) -class LiteralStringValue(LiteralValue): - """A Literal String Value.""" +class StringReference(AbstractStringValue): + value = m.Single["AbstractStringValue"]( + m.Association((NS, "AbstractStringValue"), "referencedValue") + ) + property = m.Single["Property"]( + m.Association((NS, "Property"), "referencedProperty") + ) -class ValuePart(m.ModelElement): - """A Value Part of a Complex Value.""" +class NumericValue(DataValue, abstract=True): + unit = m.Single["Unit"](m.Association((NS, "Unit"), "unit")) - _xmltag = "ownedParts" - referenced_property = m.Single( - m.Association(m.ModelElement, "referencedProperty") +class LiteralNumericValue(NumericValue): + value = m.StringPOD("value") + + +class NumericReference(NumericValue): + value = m.Single["NumericValue"]( + m.Association((NS, "NumericValue"), "referencedValue") + ) + property = m.Single["Property"]( + m.Association((NS, "Property"), "referencedProperty") ) - value = m.Single[t.Any](m.Containment("ownedValue")) -class ComplexValue(m.ModelElement): - """A Complex Value.""" +class AbstractComplexValue(DataValue, abstract=True): + pass + +class ComplexValue(AbstractComplexValue): _xmltag = "ownedDataValues" - type = m.Single(m.Association(m.ModelElement, "abstractType")) - value_parts = m.DirectProxyAccessor(ValuePart, aslist=m.ElementList) + parts = m.Containment["ValuePart"]("ownedParts", (NS, "ValuePart")) + value_parts = m.DeprecatedAccessor["ValuePart"]("parts") -class EnumerationLiteral(m.ModelElement, eq="name"): - _xmltag = "ownedLiterals" +class ComplexValueReference(AbstractComplexValue): + value = m.Single["AbstractComplexValue"]( + m.Association((NS, "AbstractComplexValue"), "referencedValue") + ) + property = m.Single["Property"]( + m.Association((NS, "Property"), "referencedProperty") + ) + + +class ValuePart(capellacore.CapellaElement): + _xmltag = "ownedParts" + + referenced_property = m.Single["Property"]( + m.Association((NS, "Property"), "referencedProperty") + ) + value = m.Single["DataValue"]( + m.Containment("ownedValue", (NS, "DataValue")) + ) + + +class AbstractExpressionValue( + AbstractBooleanValue, + AbstractComplexValue, + AbstractEnumerationValue, + NumericValue, + AbstractStringValue, + abstract=True, +): + expression = m.StringPOD("expression") + unparsed_expression = m.StringPOD("unparsedExpression") + + +class BinaryExpression(AbstractExpressionValue): + operator = m.EnumPOD("operator", BinaryOperator) + left_operand = m.Containment["DataValue"]( + "ownedLeftOperand", (NS, "DataValue") + ) + right_operand = m.Containment["DataValue"]( + "ownedRightOperand", (NS, "DataValue") + ) + - value = m.Single[t.Any](m.Containment("domainValue")) +class UnaryExpression(AbstractExpressionValue): + operator = m.EnumPOD("operator", UnaryOperator) + operand = m.Containment["DataValue"]("ownedOperand", (NS, "DataValue")) - owner = m.ParentAccessor() +class OpaqueExpression( + capellacore.CapellaElement, modellingcore.ValueSpecification +): + # TODO reimplement specification stuff + bodies = m.StringPOD("bodies") + languages = m.StringPOD("languages") -class EnumerationReference(m.ModelElement): - type = m.Single(m.Association(m.ModelElement, "abstractType")) - value = m.Single(m.Association(m.ModelElement, "referencedValue")) +if t.TYPE_CHECKING: -from . import datatype as _dt # noqa: F401 + def __getattr__(name): + if name == "LiteralValue": + warnings.warn( + ( + "LiteralValue has been dissolved," + " use the DataValue ABC or a concrete subclass instead" + ), + DeprecationWarning, + stacklevel=2, + ) + return DataValue + raise AttributeError(name) diff --git a/capellambse/metamodel/interaction.py b/capellambse/metamodel/interaction.py index 7faf8d4b..26e5a220 100644 --- a/capellambse/metamodel/interaction.py +++ b/capellambse/metamodel/interaction.py @@ -2,202 +2,451 @@ # SPDX-License-Identifier: Apache-2.0 from __future__ import annotations +import enum import typing as t +import warnings import capellambse.model as m -from . import capellacore +from . import behavior, capellacore, fa from . import namespaces as ns +if t.TYPE_CHECKING: + from . import capellacommon, information, modellingcore # noqa: F401 + NS = ns.INTERACTION -class FunctionalChainAbstractCapabilityInvolvement(m.ModelElement): ... +@m.stringy_enum +@enum.unique +class InteractionOperatorKind(enum.Enum): + UNSET = "UNSET" + ALT = "ALT" + OPT = "OPT" + PAR = "PAR" + LOOP = "LOOP" + CRITICAL = "CRITICAL" + NEG = "NEG" + ASSERT = "ASSERT" + STRICT = "STRICT" + SEQ = "SEQ" + IGNORE = "IGNORE" + CONSIDER = "CONSIDER" + + +@m.stringy_enum +@enum.unique +class MessageKind(enum.Enum): + """Identifies the type of message. + + This concept is similar to UML ``MessageSort``. + """ + + UNSET = "UNSET" + """The message kind is not specified.""" + ASYNCHRONOUS_CALL = "ASYNCHRONOUS_CALL" + """The message was generated by an asynchronous call to an operation. + + Equivalent to UML ``MessageSort::asynchCall``. + """ + SYNCHRONOUS_CALL = "SYNCHRONOUS_CALL" + """The message was generated by a synchronous call to an operation. + + Equivalent to UML ``MessageSort::synchCall``. + """ + REPLY = "REPLY" + """The message is a reply message to an operation call. + + Equivalent to UML ``MessageSort::reply``. + """ + DELETE = "DELETE" + """The message designates the termination of another lifeline. + + Equivalent to UML ``MessageSort::deleteMessage``. + """ + CREATE = "CREATE" + """The message designates the creation of an instance role.""" + TIMER = "TIMER" + + +@m.stringy_enum +@enum.unique +class ScenarioKind(enum.Enum): + UNSET = "UNSET" + INTERFACE = "INTERFACE" + DATA_FLOW = "DATA_FLOW" + INTERACTION = "INTERACTION" + FUNCTIONAL = "FUNCTIONAL" + + +class SequenceMessage(capellacore.NamedElement): + """A sequence message.""" + kind = m.EnumPOD("kind", MessageKind) + exchange_context = m.Association["capellacore.Constraint"]( + (ns.CAPELLACORE, "Constraint"), "exchangeContext" + ) + sending_end = m.Association["MessageEnd"]((NS, "MessageEnd"), "sendingEnd") + receiving_end = m.Association["MessageEnd"]( + (NS, "MessageEnd"), "receivingEnd" + ) + source = m.DeprecatedAccessor["MessageEnd"]("sending_end") + target = m.DeprecatedAccessor["MessageEnd"]("receiving_end") + exchanged_items = m.Association["information.ExchangeItem"]( + (ns.INFORMATION, "ExchangeItem"), "exchangedItems" + ) + valuations = m.Containment["SequenceMessageValuation"]( + "ownedSequenceMessageValuations", (NS, "SequenceMessageValuation") + ) -class AbstractCapabilityRealization(m.ModelElement): ... +class Scenario(capellacore.Namespace, behavior.AbstractBehavior): + kind = m.EnumPOD("kind", ScenarioKind) + is_merged = m.BoolPOD("merged") -class Execution(m.ModelElement): - """An execution.""" + precondition = m.Single["capellacore.Constraint"]( + m.Association((ns.CAPELLACORE, "Constraint"), "preCondition") + ) + postcondition = m.Single["capellacore.Constraint"]( + m.Association((ns.CAPELLACORE, "Constraint"), "postCondition") + ) + instance_roles = m.Containment["InstanceRole"]( + "ownedInstanceRoles", (NS, "InstanceRole") + ) + messages = m.Containment["SequenceMessage"]( + "ownedMessages", (NS, "SequenceMessage") + ) + fragments = m.Containment["InteractionFragment"]( + "ownedInteractionFragments", (NS, "InteractionFragment") + ) + time_lapses = m.Containment["TimeLapse"]( + "ownedTimeLapses", (NS, "TimeLapse") + ) + events = m.Containment["Event"]("ownedEvents", (NS, "Event")) + formal_gates = m.Containment["Gate"]("ownedFormalGates", (NS, "Gate")) + realized_scenarios = m.Allocation["Scenario"]( + "ownedScenarioRealization", + (NS, "ScenarioRealization"), + (NS, "Scenario"), + attr="targetElement", + backattr="sourceElement", + ) + realizing_scenarios = m.Backref["Scenario"]( + (NS, "Scenario"), "realized_scenarios" + ) + constraint_durations = m.Containment["ConstraintDuration"]( + "ownedConstraintDurations", (NS, "ConstraintDuration") + ) - start = m.Single(m.Association(m.ModelElement, "start")) - finish = m.Single(m.Association(m.ModelElement, "finish")) + @property + def related_functions(self) -> m.ElementList[fa.AbstractFunction]: + return self.fragments.map("function") -class StateFragment(Execution): - """A state fragment.""" +class InteractionFragment(capellacore.NamedElement, abstract=True): + """Abstract super class of all interaction fragments in a Scenario.""" - function = m.Single( - m.Association(m.ModelElement, "relatedAbstractFunction") - ) + covered = m.Association[m.ModelElement](None, "coveredInstanceRoles") -class CombinedFragment(Execution): - """A combined fragment.""" +class AbstractEnd(InteractionFragment, abstract=True): + event = m.Single["Event"](m.Association((NS, "Event"), "event")) - operator = m.StringPOD("operator") - operands = m.Association(m.ModelElement, "referencedOperands") +class MessageEnd(AbstractEnd): + pass -class InstanceRole(m.ModelElement): - """An instance role.""" - instance = m.Single( - m.Association[m.ModelElement](None, "representedInstance") +class TimeLapse(capellacore.NamedElement, abstract=True): + start = m.Single["InteractionFragment"]( + m.Association((NS, "InteractionFragment"), "start") + ) + finish = m.Single["InteractionFragment"]( + m.Association((NS, "InteractionFragment"), "finish") ) -class SequenceMessage(m.ModelElement): - """A sequence message.""" +class Execution(TimeLapse): + pass + + +class ExecutionEnd(AbstractEnd): + pass - source = m.Single(m.Association(m.ModelElement, "sendingEnd")) - target = m.Single(m.Association(m.ModelElement, "receivingEnd")) +class Event(capellacore.NamedElement, behavior.AbstractEvent, abstract=True): + pass -class Event(m.ModelElement): - """Abstract super class of all events in a Scenario.""" +class CreationEvent(Event): + pass -class EventOperation(Event): - """Abstract super class for events about operations.""" - operation = m.Single(m.Association(m.ModelElement, "operation")) +class DestructionEvent(Event): + pass class ExecutionEvent(Event): - """An execution event.""" + pass + + +class InstanceRole(capellacore.NamedElement): + instance = m.Single["information.AbstractInstance"]( + m.Association( + (ns.INFORMATION, "AbstractInstance"), "representedInstance" + ) + ) + + +class EventReceiptOperation(Event): + operation = m.Association["information.AbstractEventOperation"]( + (ns.INFORMATION, "AbstractEventOperation"), "operation" + ) + + +class EventSentOperation(Event): + operation = m.Association["information.AbstractEventOperation"]( + (ns.INFORMATION, "AbstractEventOperation"), "operation" + ) + +class MergeLink(capellacore.Trace): + pass -class EventSentOperation(EventOperation): - """An event-sent operation.""" +class RefinementLink(capellacore.Trace): + pass -class EventReceiptOperation(EventOperation): - """An event-receipt operation.""" +class AbstractCapabilityRealization(capellacore.Allocation): + pass -class Scenario(m.ModelElement): - """A scenario that holds instance roles.""" - instance_roles = m.DirectProxyAccessor[m.ModelElement]( - InstanceRole, aslist=m.ElementList +class AbstractCapability( + capellacore.Structure, + capellacore.InvolverElement, + fa.AbstractFunctionalChainContainer, + abstract=True, +): + precondition = m.Single["capellacore.Constraint"]( + m.Association((ns.CAPELLACORE, "Constraint"), "preCondition") ) - messages = m.DirectProxyAccessor(SequenceMessage, aslist=m.ElementList) - events = m.Containment[t.Any]("ownedEvents") - fragments = m.Containment[t.Any]("ownedInteractionFragments") - time_lapses = m.Containment[t.Any]("ownedTimeLapses") - postcondition = m.Single( - m.Association(capellacore.Constraint, "postCondition") + postcondition = m.Single["capellacore.Constraint"]( + m.Association((ns.CAPELLACORE, "Constraint"), "postCondition") ) - precondition = m.Single( - m.Association(capellacore.Constraint, "preCondition") + scenarios = m.Containment["Scenario"]("ownedScenarios", (NS, "Scenario")) + extends = m.Containment["AbstractCapabilityExtend"]( + "extends", (NS, "AbstractCapabilityExtend") ) - realized_scenarios = m.Allocation["Scenario"]( - "ownedScenarioRealization", - "org.polarsys.capella.core.data.interaction:ScenarioRealization", + extended_by = m.Backref["AbstractCapabilityExtend"]( + (NS, "AbstractCapabilityExtend"), "target" + ) + extension_points = m.Containment["AbstractCapabilityExtensionPoint"]( + "abstractCapabilityExtensionPoints", + (NS, "AbstractCapabilityExtensionPoint"), + ) + generalizations = m.Containment["AbstractCapabilityGeneralization"]( + "superGeneralizations", (NS, "AbstractCapabilityGeneralization") + ) + generalizes = m.Allocation["AbstractCapability"]( + "superGeneralizations", + (NS, "AbstractCapabilityGeneralization"), + (NS, "AbstractCapability"), + attr="super", + ) + generalized_by = m.Backref["AbstractCapability"]( + (NS, "AbstractCapability"), "generalizes" + ) + includes = m.Containment["AbstractCapabilityInclude"]( + "includes", (NS, "AbstractCapabilityInclude") + ) + included_by = m.Backref["AbstractCapabilityInclude"]( + (NS, "AbstractCapabilityInclude"), "included" + ) + involved_functions = m.Allocation["fa.AbstractFunction"]( + "ownedAbstractFunctionAbstractCapabilityInvolvements", + (NS, "AbstractFunctionAbstractCapabilityInvolvement"), + (ns.FA, "AbstractFunction"), + attr="involved", + ) + involved_chains = m.Allocation["fa.FunctionalChain"]( + "ownedFunctionalChainAbstractCapabilityInvolvements", + (NS, "FunctionalChainAbstractCapabilityInvolvement"), + (ns.FA, "AbstractFunction"), + attr="involved", + ) + available_in_states = m.Association["capellacommon.State"]( + (ns.CAPELLACOMMON, "State"), "availableInStates" + ) + states = m.DeprecatedAccessor["capellacommon.State"]("available_in_states") + realized_capabilities = m.Allocation["AbstractCapability"]( + "ownedAbstractCapabilityRealizations", + (NS, "AbstractCapabilityRealization"), + (NS, "AbstractCapability"), attr="targetElement", - backattr="sourceElement", ) - realizing_scenarios: m.Backref[Scenario] - - @property - def related_functions(self) -> m.ElementList[fa.AbstractFunction]: - return self.fragments.map("function") + realizing_capabilities = m.Backref["AbstractCapability"]( + (NS, "AbstractCapability"), "realized_capabilities" + ) -class InteractionFragment(m.ModelElement): - """Abstract super class of all interaction fragments in a Scenario.""" +class AbstractCapabilityExtend(capellacore.Relationship): + _xmltag = "extends" - covered = m.Association[m.ModelElement](None, "coveredInstanceRoles") + extended = m.Single["AbstractCapability"]( + m.Association((NS, "AbstractCapability"), "extended") + ) + extension_location = m.Single["AbstractCapabilityExtensionPoint"]( + m.Association( + (NS, "AbstractCapabilityExtensionPoint"), "extensionLocation" + ) + ) + source = m.DeprecatedAccessor["AbstractCapabilityExtensionPoint"]( + "extension_location" + ) + target = m.DeprecatedAccessor["AbstractCapability"]("extended") -class ExecutionEnd(InteractionFragment): - """An end for an execution.""" +class AbstractCapabilityExtensionPoint(capellacore.NamedRelationship): + extend_links = m.Association["AbstractCapabilityExtend"]( + (NS, "AbstractCapabilityExtend"), "extendLinks" + ) - event = m.Single(m.Association[Event](None, "event")) +class AbstractCapabilityGeneralization(capellacore.Relationship): + _xmltag = "superGeneralizations" -class FragmentEnd(InteractionFragment): - """An end for a fragment.""" + source = m.DeprecatedAccessor["m.ModelElement"]("parent") + super = m.Single["AbstractCapability"]( + m.Association((NS, "AbstractCapability"), "super") + ) + target = m.DeprecatedAccessor["AbstractCapability"]("super") -class InteractionOperand(InteractionFragment): - """An interaction-operand.""" +class AbstractCapabilityInclude(capellacore.Relationship): + _xmltag = "includes" - guard = m.Single(m.Association(capellacore.Constraint, "guard")) + included = m.Single["AbstractCapability"]( + m.Association((NS, "AbstractCapability"), "included") + ) + source = m.DeprecatedAccessor["m.ModelElement"]("parent") + target = m.DeprecatedAccessor["AbstractCapability"]("included") class InteractionState(InteractionFragment): - """An interaction-state.""" + state = m.Single["capellacommon.AbstractState"]( + m.Association( + (ns.CAPELLACOMMON, "AbstractState"), "relatedAbstractState" + ) + ) + function = m.Single["fa.AbstractFunction"]( + m.Association((ns.FA, "AbstractFunction"), "relatedAbstractFunction") + ) + + +class AbstractFragment(TimeLapse, abstract=True): + gates = m.Containment["Gate"]("ownedGates", (NS, "Gate")) - state = m.Single(m.Association(m.ModelElement, "relatedAbstractState")) - function = m.Single( - m.Association(m.ModelElement, "relatedAbstractFunction") + +class InteractionUse(AbstractFragment): + referenced_scenario = m.Single["Scenario"]( + m.Association((NS, "Scenario"), "referencedScenario") ) -class MessageEnd(InteractionFragment): - """A message-end.""" +class CombinedFragment(AbstractFragment): + operator = m.EnumPOD("operator", InteractionOperatorKind) + operands = m.Association["InteractionOperand"]( + (NS, "InteractionOperand"), "referencedOperands" + ) - event = m.Single(m.Association[Event](None, "event")) +class Gate(MessageEnd): + pass -class Exchange(m.ModelElement): - """An abstract Exchange.""" - source = m.ParentAccessor() +class InteractionOperand(InteractionFragment): + fragments = m.Association["InteractionFragment"]( + (NS, "InteractionFragment"), "referencedInteractionFragments" + ) + guard = m.Single["capellacore.Constraint"]( + m.Association((ns.CAPELLACORE, "Constraint"), "guard") + ) -class AbstractCapabilityExtend(Exchange): - """An AbstractCapabilityExtend.""" +class FragmentEnd(InteractionFragment): + pass - _xmltag = "extends" - source = m.ParentAccessor() - target = m.Single(m.Association(m.ModelElement, "extended")) +class FunctionalChainAbstractCapabilityInvolvement(capellacore.Involvement): + pass -class AbstractCapabilityInclude(Exchange): - """An AbstractCapabilityInclude.""" +class AbstractFunctionAbstractCapabilityInvolvement(capellacore.Involvement): + pass - _xmltag = "includes" - source = m.ParentAccessor() - target = m.Single(m.Association(m.ModelElement, "included")) +class ScenarioRealization(capellacore.Allocation): + pass -class AbstractCapabilityGeneralization(Exchange): - """An AbstractCapabilityGeneralization.""" +class StateFragment(TimeLapse): + state = m.Single["capellacommon.AbstractState"]( + m.Association( + (ns.CAPELLACOMMON, "AbstractState"), "relatedAbstractState" + ) + ) + function = m.Single["fa.AbstractFunction"]( + m.Association((ns.FA, "AbstractFunction"), "relatedAbstractFunction") + ) - _xmltag = "superGeneralizations" - source = m.ParentAccessor() - target = m.Single(m.Association(m.ModelElement, "super")) +class ArmTimerEvent(Event): + pass -class AbstractInvolvement(m.ModelElement): - """An abstract Involvement.""" +class CancelTimerEvent(Event): + pass - source = m.ParentAccessor() - target = m.Alias[t.Any]("involved") - involved = m.Single(m.Association(m.ModelElement, "involved")) - @property - def name(self) -> str: # type: ignore[override] - """Return the name.""" - direction = "" - if self.involved is not None: - direction = f" to {self.involved.name} ({self.involved.uuid})" +class ConstraintDuration(capellacore.NamedElement): + duration = m.StringPOD("duration") + start = m.Single["InteractionFragment"]( + m.Association((NS, "InteractionFragment"), "start") + ) + finish = m.Single["InteractionFragment"]( + m.Association((NS, "InteractionFragment"), "finish") + ) - return f"[{self.__class__.__name__}]{direction}" +class SequenceMessageValuation(capellacore.CapellaElement): + element = m.Single["information.ExchangeItemElement"]( + m.Association( + (ns.INFORMATION, "ExchangeItemElement"), "exchangeItemElement" + ) + ) + value = m.Single["modellingcore.ValueSpecification"]( + m.Association((ns.MODELLINGCORE, "ValueSpecification"), "value") + ) -class AbstractFunctionAbstractCapabilityInvolvement(AbstractInvolvement): - """An abstract CapabilityInvolvement linking to SystemFunctions.""" +if not t.TYPE_CHECKING: -Scenario.realizing_scenarios = m.Backref(Scenario, "realized_scenarios") + def __getattr__(name): + if name == "Exchange": + warnings.warn( + "The Exchange class has been removed, use capellacore.Relationship instead", + DeprecationWarning, + stacklevel=2, + ) + return capellacore.Relationship + if name == "AbstractInvolvement": + warnings.warn( + "AbstractInvolvement has been moved to capellacore.Involvement", + DeprecationWarning, + stacklevel=2, + ) + return capellacore.Involvement -from . import fa + raise AttributeError(name) diff --git a/capellambse/metamodel/la.py b/capellambse/metamodel/la.py index 367cac89..e005150a 100644 --- a/capellambse/metamodel/la.py +++ b/capellambse/metamodel/la.py @@ -7,218 +7,188 @@ from __future__ import annotations -from capellambse import model as m +import typing as t -from . import capellacommon, capellacore, cs, fa, interaction +import capellambse.model as m + +from . import capellacommon, cs, fa, interaction from . import namespaces as ns from . import sa -NS = ns.LA +if t.TYPE_CHECKING: + from . import pa # noqa: F401 +NS = ns.LA -class LogicalFunction(fa.Function): - """A logical function on the Logical Architecture layer.""" - realized_system_functions = m.TypecastAccessor( - sa.SystemFunction, "realized_functions" +class LogicalArchitecturePkg(cs.BlockArchitecturePkg): + architectures = m.Containment["LogicalArchitecture"]( + "ownedLogicalArchitectures", (NS, "LogicalArchitecture") ) - owner: m.Single[LogicalComponent] -class LogicalFunctionPkg(m.ModelElement): - """A logical function package.""" +class LogicalArchitecture(cs.ComponentArchitecture): + component_pkg = m.Single["LogicalComponentPkg"]( + m.Containment("ownedLogicalComponentPkg", (NS, "LogicalComponentPkg")) + ) + component_package = m.DeprecatedAccessor["LogicalComponentPkg"]( + "component_pkg" + ) + realized_system_analysis = m.Allocation["sa.SystemAnalysis"]( + "ownedSystemAnalysisRealizations", + (NS, "SystemAnalysisRealization"), + (ns.SA, "SystemAnalysis"), + attr="targetElement", + backattr="sourceElement", + ) - _xmltag = "ownedFunctionPkg" + @property + def root_component(self) -> LogicalComponent: + assert self.component_pkg is not None + return self.component_pkg.components.by_is_actor(False, single=True) + + @property + def all_components(self) -> m.ElementList[LogicalComponent]: + return self._model.search((NS, "LogicalComponent"), below=self) + + @property + def all_actors(self) -> m.ElementList[LogicalComponent]: + return self._model.search(LogicalComponent).by_is_actor(True) + + @property + def all_actor_exchanges(self) -> m.ElementList[fa.ComponentExchange]: + return self._model.search( + (ns.FA, "ComponentExchange"), below=self + ).filter( + lambda e: ( + (e.source is not None and e.source.is_actor) + or (e.target is not None and e.target.is_actor) + ) + ) - functions = m.Containment("ownedLogicalFunctions", LogicalFunction) + @property + def all_component_exchanges(self) -> m.ElementList[fa.ComponentExchange]: + return self._model.search((ns.FA, "ComponentExchange"), below=self) - packages: m.Accessor - categories = m.DirectProxyAccessor( - fa.ExchangeCategory, aslist=m.ElementList + diagrams = m.DiagramAccessor( + "Logical Architecture", cacheattr="_MelodyModel__diagram_cache" ) -class LogicalComponent(cs.Component): - """A logical component on the Logical Architecture layer.""" +class LogicalFunction(fa.AbstractFunction): + functions = m.Containment["LogicalFunction"]( + "ownedFunctions", (NS, "LogicalFunction") + ) + packages = m.Containment["LogicalFunctionPkg"]( + "ownedLogicalFunctionPkgs", (NS, "LogicalFunctionPkg") + ) + realized_system_functions = m.DeprecatedAccessor["sa.SystemFunction"]( + "realized_functions" + ) + owner = m.Single["LogicalComponent"]( + m.Backref((NS, "LogicalComponent"), "allocated_functions") + ) + involved_in = m.Backref["CapabilityRealization"]( + (NS, "CapabilityRealization"), "involved_functions" + ) + realizing_physical_functions = m.Backref["pa.PhysicalFunction"]( + (ns.PA, "PhysicalFunction"), "realized_logical_functions" + ) + - _xmltag = "ownedLogicalComponents" +class LogicalFunctionPkg(fa.FunctionPkg): + _xmltag = "ownedFunctionPkg" - allocated_functions = m.Allocation[LogicalFunction]( - "ownedFunctionalAllocation", - fa.ComponentFunctionalAllocation, - attr="targetElement", - backattr="sourceElement", + functions = m.Containment["LogicalFunction"]( + "ownedLogicalFunctions", (NS, "LogicalFunction") ) - realized_system_components = m.TypecastAccessor( - sa.SystemComponent, - "realized_components", + packages = m.Containment["LogicalFunctionPkg"]( + "ownedLogicalFunctionPkgs", (NS, "LogicalFunctionPkg") ) - components: m.Accessor +class LogicalComponent( + cs.Component, capellacommon.CapabilityRealizationInvolvedElement +): + _xmltag = "ownedLogicalComponents" + + components = m.Containment["LogicalComponent"]( + "ownedLogicalComponents", (NS, "LogicalComponent") + ) + architectures = m.Containment["LogicalArchitecture"]( + "ownedLogicalArchitectures", (NS, "LogicalArchitecture") + ) + packages = m.Containment["LogicalComponentPkg"]( + "ownedLogicalComponentPkgs", (NS, "LogicalComponentPkg") + ) + realized_system_components = m.DeprecatedAccessor["sa.SystemComponent"]( + "realized_components" + ) + realizing_physical_components = m.Backref["pa.PhysicalComponent"]( + (ns.PA, "PhysicalComponent"), "realized_logical_components" + ) -class LogicalComponentPkg(m.ModelElement): - """A logical component package.""" +class LogicalComponentPkg(cs.ComponentPkg): _xmltag = "ownedLogicalComponentPkg" - components = m.DirectProxyAccessor(LogicalComponent, aslist=m.ElementList) - state_machines = m.DirectProxyAccessor( - capellacommon.StateMachine, aslist=m.ElementList - ) - exchanges = m.DirectProxyAccessor( - fa.ComponentExchange, aslist=m.ElementList + components = m.Containment["LogicalComponent"]( + "ownedLogicalComponents", (NS, "LogicalComponent") ) - - packages: m.Accessor - exchange_categories = m.DirectProxyAccessor( - fa.ComponentExchangeCategory, aslist=m.ElementList + packages = m.Containment["LogicalComponentPkg"]( + "ownedLogicalComponentPkgs", (NS, "LogicalComponentPkg") ) -class CapabilityRealization(m.ModelElement): - """A capability.""" - +class CapabilityRealization(interaction.AbstractCapability): _xmltag = "ownedCapabilityRealizations" owned_chains = m.DirectProxyAccessor( fa.FunctionalChain, aslist=m.ElementList ) - involved_functions = m.Allocation[LogicalFunction]( + involved_functions = m.Allocation[LogicalFunction]( # TODO "ownedAbstractFunctionAbstractCapabilityInvolvements", interaction.AbstractFunctionAbstractCapabilityInvolvement, attr="involved", ) - involved_chains = m.Allocation[fa.FunctionalChain]( + involved_chains = m.Allocation[fa.FunctionalChain]( # TODO "ownedFunctionalChainAbstractCapabilityInvolvements", interaction.FunctionalChainAbstractCapabilityInvolvement, attr="involved", ) - involved_components = m.Allocation[LogicalComponent]( + involved_elements = m.Allocation[ + "capellacommon.CapabilityRealizationInvolvedElement" + ]( "ownedCapabilityRealizationInvolvements", - capellacommon.CapabilityRealizationInvolvement, + (ns.CAPELLACOMMON, "CapabilityRealizationInvolvement"), + (ns.CAPELLACOMMON, "CapabilityRealizationInvolvedElement"), attr="involved", ) - realized_capabilities = m.Allocation[sa.Capability]( + realized_capabilities = m.Allocation[sa.Capability]( # TODO "ownedAbstractCapabilityRealizations", interaction.AbstractCapabilityRealization, attr="targetElement", ) - postcondition = m.Single( - m.Association(capellacore.Constraint, "postCondition") - ) - precondition = m.Single( - m.Association(capellacore.Constraint, "preCondition") - ) - scenarios = m.DirectProxyAccessor( - interaction.Scenario, aslist=m.ElementList - ) - states = m.Association(capellacommon.State, "availableInStates") - - packages: m.Accessor + packages: m.Accessor # TODO -class CapabilityRealizationPkg(m.ModelElement): +class CapabilityRealizationPkg(capellacommon.AbstractCapabilityPkg): """A capability package that can hold capabilities.""" _xmltag = "ownedAbstractCapabilityPkg" - capabilities = m.DirectProxyAccessor( - CapabilityRealization, aslist=m.ElementList - ) - - packages: m.Accessor - - -class LogicalArchitecture(cs.ComponentArchitecture): - """Provides access to the LogicalArchitecture layer of the model.""" - - root_component = m.AttributeMatcherAccessor( - LogicalComponent, - attributes={"is_actor": False}, - rootelem=LogicalComponentPkg, - ) - root_function = m.DirectProxyAccessor( - LogicalFunction, rootelem=LogicalFunctionPkg - ) - - function_package = m.DirectProxyAccessor(LogicalFunctionPkg) - component_package = m.DirectProxyAccessor(LogicalComponentPkg) - capability_package = m.DirectProxyAccessor(CapabilityRealizationPkg) - - all_functions = m.DeepProxyAccessor( - LogicalFunction, - aslist=m.ElementList, - rootelem=LogicalFunctionPkg, - ) - all_capabilities = m.DeepProxyAccessor( - CapabilityRealization, aslist=m.ElementList + capabilities = m.Containment["CapabilityRealization"]( + "ownedCapabilityRealizations", (NS, "CapabilityRealization") ) - all_components = ( - m.DeepProxyAccessor( # maybe this should exclude .is_actor - LogicalComponent, aslist=m.ElementList - ) - ) - all_actors = property( - lambda self: self._model.search(LogicalComponent).by_is_actor(True) - ) - all_functional_chains = property( - lambda self: self._model.search(fa.FunctionalChain, below=self) + packages = m.Containment["CapabilityRealizationPkg"]( + "ownedCapabilityRealizationPkgs", (NS, "CapabilityRealizationPkg") ) - actor_exchanges = m.DirectProxyAccessor( - fa.ComponentExchange, - aslist=m.ElementList, - rootelem=LogicalComponentPkg, - ) - component_exchanges = m.DeepProxyAccessor( - fa.ComponentExchange, - aslist=m.ElementList, - rootelem=[LogicalComponentPkg, LogicalComponent], - ) - - all_function_exchanges = m.DeepProxyAccessor( - fa.FunctionalExchange, - aslist=m.ElementList, - rootelem=[LogicalFunctionPkg, LogicalFunction], - ) - all_component_exchanges = m.DeepProxyAccessor( - fa.ComponentExchange, aslist=m.ElementList - ) - diagrams = m.DiagramAccessor( - "Logical Architecture", cacheattr="_MelodyModel__diagram_cache" - ) +class SystemAnalysisRealization(cs.ArchitectureAllocation): + pass -sa.Capability.realizing_capabilities = m.Backref( - CapabilityRealization, "realized_capabilities" -) -sa.SystemComponent.realizing_logical_components = m.Backref( - LogicalComponent, "realized_components" -) -sa.SystemFunction.realizing_logical_functions = m.Backref( - LogicalFunction, "realized_system_functions" -) -LogicalFunction.owner = m.Single( - m.Backref(LogicalComponent, "allocated_functions") -) -LogicalFunction.packages = m.DirectProxyAccessor( - LogicalFunctionPkg, aslist=m.ElementList -) -LogicalFunction.involved_in = m.Backref( - CapabilityRealization, "involved_functions" -) -LogicalComponent.components = m.DirectProxyAccessor( - LogicalComponent, aslist=m.ElementList -) -LogicalComponentPkg.packages = m.DirectProxyAccessor( - LogicalComponentPkg, aslist=m.ElementList -) -LogicalFunction.functions = m.DirectProxyAccessor( - LogicalFunction, aslist=m.ElementList -) -LogicalFunctionPkg.packages = m.DirectProxyAccessor( - LogicalFunctionPkg, aslist=m.ElementList -) +class ContextInterfaceRealization(cs.InterfaceAllocation): + pass diff --git a/capellambse/metamodel/libraries.py b/capellambse/metamodel/libraries.py index 9c44bd83..ac46a1ec 100644 --- a/capellambse/metamodel/libraries.py +++ b/capellambse/metamodel/libraries.py @@ -1,5 +1,13 @@ # SPDX-FileCopyrightText: Copyright DB InfraGO AG # SPDX-License-Identifier: Apache-2.0 +import enum + from . import namespaces as ns NS = ns.LIBRARIES + + +@enum.unique +class AccessPolicy(enum.Enum): + READ_ONLY = "readOnly" + READ_AND_WRITE = "readAndWrite" diff --git a/capellambse/metamodel/modellingcore.py b/capellambse/metamodel/modellingcore.py index 8d2aae10..514dbf06 100644 --- a/capellambse/metamodel/modellingcore.py +++ b/capellambse/metamodel/modellingcore.py @@ -2,6 +2,8 @@ # SPDX-License-Identifier: Apache-2.0 from __future__ import annotations +import enum + import capellambse.model as m from . import namespaces as ns @@ -9,11 +11,201 @@ NS = ns.MODELLINGCORE +@m.stringy_enum +@enum.unique +class ParameterEffectKind(enum.Enum): + """A behavior's effect on values passed in or out of its parameters.""" + + READ = "read" + """The parameter value is only being read upon behavior execution.""" + UPDATE = "update" + """The parameter value is being updated upon behavior execution.""" + CREATE = "create" + """The parameter value is being created upon behavior execution.""" + DELETE = "delete" + """The parameter value is being deleted upon behavior execution.""" + + +@m.stringy_enum +@enum.unique +class RateKind(enum.Enum): + """The possible caracterizations for the rate of a streaming parameter.""" + + UNSPECIFIED = "Unspecified" + """The rate kind is not specified.""" + CONTINUOUS = "Continuous" + """The rate characterizes a continuous flow.""" + DISCRETE = "Discrete" + """The rate characterizes a discrete flow.""" + + ModelElement = m._obj.ModelElement +m.ModelElement.constraints = m.Containment( + "ownedConstraints", (ns.CAPELLACORE, "Constraint") +) +m.ModelElement.property_values = m.Containment( + "ownedPropertyValues", + (ns.CAPELLACORE, "AbstractPropertyValue"), + mapkey="name", + mapvalue="value", +) +m.ModelElement.property_value_groups = m.Containment( + "ownedPropertyValueGroups", + (ns.CAPELLACORE, "PropertyValueGroup"), + mapkey="name", + mapvalue="values", +) +m.ModelElement.applied_property_values = m.Association( + (ns.CAPELLACORE, "AbstractPropertyValue"), "appliedPropertyValues" +) +m.ModelElement.applied_property_value_groups = m.Association( + (ns.CAPELLACORE, "PropertyValueGroup"), "appliedPropertyValueGroups" +) + + +class AbstractRelationship(ModelElement, abstract=True): + realized_flow = m.Association["AbstractInformationFlow"]( + (NS, "AbstractInformationFlow"), "realizedFlow" + ) + + +class AbstractNamedElement(ModelElement, abstract=True): + """An element that may have a name. + + The name is used for identification of the named element within the + namespace in which it is defined. A named element also has a + qualified name that allows it to be unambiguously identified within + a hierarchy of nested namespaces. + """ + + name = m.StringPOD("name") + + +class InformationsExchanger(ModelElement, abstract=True): + """An element that may exchange information with other elements.""" + + +class TraceableElement(ModelElement, abstract=True): + """An element that may be traced to other elements.""" + + +class FinalizableElement(ModelElement, abstract=True): + is_final = m.BoolPOD("final") + + +class PublishableElement(ModelElement, abstract=True): + is_visible_in_doc = m.BoolPOD("visibleInDoc") + is_visible_in_lm = m.BoolPOD("visibleInLM") + + +class AbstractType(AbstractNamedElement, abstract=True): + """Base abstract class supporting the definition of data types.""" + + +class AbstractTypedElement(AbstractNamedElement, abstract=True): + """A (named) model element to which a specific type is associated.""" + + type = m.Single["AbstractType"]( + m.Association((NS, "AbstractType"), "abstractType") + ) + + +class AbstractTrace(TraceableElement, abstract=True): + target = m.Single["TraceableElement"]( + m.Association((NS, "TraceableElement"), "targetElement") + ) + source = m.Single["TraceableElement"]( + m.Association((NS, "TraceableElement"), "sourceElement") + ) + + +class AbstractConstraint(ModelElement, abstract=True): + """A constraint that applies to a given set of model elements.""" + + constrained_elements = m.Association["ModelElement"]( + (NS, "ModelElement"), "constrainedElements" + ) + specification = m.Single["ValueSpecification"]( + m.Containment("ownedSpecification", (NS, "ValueSpecification")) + ) + """A condition that must evaluate to true to satisfy the constraint.""" + + +class ValueSpecification(AbstractTypedElement, abstract=True): + """The specification of a set of instances. + + The set includes both objects and data values, and may be empty. + """ + + +class AbstractParameter(AbstractTypedElement, abstract=True): + """Specification of an argument to a behavioral feature. + + Parameters are used to pass information into or out of an invocation + of a behavioral feature. + """ + + is_exception = m.BoolPOD("isException") + is_stream = m.BoolPOD("isStream") + is_optional = m.BoolPOD("isOptional") + kind_of_rate = m.EnumPOD("kindOfRate", RateKind) + effect = m.EnumPOD("effect", ParameterEffectKind) + rate = m.Single["ValueSpecification"]( + m.Containment("rate", (NS, "ValueSpecification")) + ) + probability = m.Single["ValueSpecification"]( + m.Containment("probability", (NS, "ValueSpecification")) + ) + parameter_set = m.Single["AbstractParameterSet"]( + m.Association((NS, "AbstractParameterSet"), "parameterSet") + ) + + +class AbstractParameterSet(AbstractNamedElement, abstract=True): + """An alternative set of inputs or outputs that a behavior may use.""" + + conditions = m.Containment["AbstractConstraint"]( + "ownedConditions", (NS, "AbstractConstraint") + ) + probability = m.Single["ValueSpecification"]( + m.Containment("probability", (NS, "ValueSpecification")) + ) + parameters = m.Single["AbstractParameter"]( + m.Association((NS, "AbstractParameter"), "parameters") + ) + + +class AbstractInformationFlow( + AbstractNamedElement, AbstractRelationship, abstract=True +): + realizations = m.Association["AbstractRelationship"]( + (NS, "AbstractRelationship"), "realizations" + ) + convoyed_informations = m.Association["AbstractExchangeItem"]( + (NS, "AbstractExchangeItem"), "convoyedInformations" + ) + source = m.Single["InformationsExchanger"]( + m.Association((NS, "InformationsExchanger"), "source") + ) + target = m.Single["InformationsExchanger"]( + m.Association((NS, "InformationsExchanger"), "target") + ) + + +class AbstractExchangeItem(AbstractType, abstract=True): + """Set of exchanged element exchanged between ports.""" + +class IState(AbstractNamedElement, abstract=True): + """A vertex is an abstraction of a node in a state machine graph. -class TraceableElement(m.ModelElement): - """A template for traceable ModelObjects.""" + In general, it can be the source or destination of any number of + transitions. + """ - source = m.Single(m.Association(m.ModelElement, attr="sourceElement")) - target = m.Single(m.Association(m.ModelElement, attr="targetElement")) + referenced_states = m.Association["IState"]( + (NS, "IState"), "referencedStates" + ) + exploited_states = m.Association["IState"]( + (NS, "IState"), "exploitedStates" + ) diff --git a/capellambse/metamodel/modeltypes.py b/capellambse/metamodel/modeltypes.py index 91c475fc..89d5f5f6 100644 --- a/capellambse/metamodel/modeltypes.py +++ b/capellambse/metamodel/modeltypes.py @@ -2,571 +2,53 @@ # SPDX-License-Identifier: Apache-2.0 """Enumeration types used by the MelodyModel.""" -import enum as _enum -import typing as t - - -class _StringyEnumMixin: - """Mixin for enums that makes members compare equal to their key name. - - :meta public: - """ - - name: t.Any - _name_: t.Any - - def __eq__(self, other: object) -> bool: - if isinstance(other, type(self)): - return self is other - if isinstance(other, str): - return self.name == other - return NotImplemented - - def __str__(self) -> str: - return str(self.name) - - def __hash__(self): - return hash(self._name_) - - -@_enum.unique -class AccessPolicy(_StringyEnumMixin, _enum.Enum): - READ_ONLY = "readOnly" - READ_AND_WRITE = "readAndWrite" - - -@_enum.unique -class AggregationKind(_StringyEnumMixin, _enum.Enum): - """Defines the specific kind of a relationship, as per UML definitions.""" - - UNSET = "UNSET" - """Used when value is not defined by the user.""" - ASSOCIATION = "ASSOCIATION" - """A semantic relationship between typed instances. - - It has at least two ends represented by properties, each of which is - connected to the type of the end. More than one end of the - association may have the same type. - - Indicates that the property has no aggregation. - """ - AGGREGATION = "AGGREGATION" - """A semantic relationship between a part and a whole. - - The part has a lifecycle of its own, and is potentially shared among - several aggregators. - """ - COMPOSITION = "COMPOSITION" - """A semantic relationship between whole and its parts. - - The parts lifecycles are tied to that of the whole, and they are not - shared with any other aggregator. - """ - - -@_enum.unique -class BinaryOperator(_StringyEnumMixin, _enum.Enum): - """Specifies the kind of this binary operator.""" - - UNSET = "UNSET" - """The binary operator is not initialized.""" - ADD = "ADD" - """The binary operator refers to an addition.""" - MUL = "MUL" - """The binary operator refers to a multiplication.""" - SUB = "SUB" - """The binary operator refers to a substraction.""" - DIV = "DIV" - """The binary operator refers to a division.""" - POW = "POW" - """The binary operator refers to a power operation.""" - MIN = "MIN" - """The binary operator refers to a min operation.""" - MAX = "MAX" - """The binary operator refers to a max operation.""" - EQU = "EQU" - """The binary operator refers to an equal operation.""" - IOR = "IOR" - """The binary operator refers to a logical inclusive OR operation.""" - XOR = "XOR" - """The binary operator refers to a logical exclusive OR operation.""" - AND = "AND" - """The binary operator refers to a logical AND operation.""" - - -@_enum.unique -class CatalogElementKind(_StringyEnumMixin, _enum.Enum): - REC = "REC" - RPL = "RPL" - REC_RPL = "REC_RPL" - GROUPING = "GROUPING" - - -@_enum.unique -class ChangeEventKind(_StringyEnumMixin, _enum.Enum): - WHEN = "WHEN" - - -@_enum.unique -class CollectionKind(_StringyEnumMixin, _enum.Enum): - """Defines the specific kind of a Collection structure.""" - - ARRAY = "ARRAY" - """The collection is to be considered an array of elements.""" - SEQUENCE = "SEQUENCE" - """The collection is to be considered as a sequence (list) of elements.""" - - -@_enum.unique -class CommunicationLinkKind(_StringyEnumMixin, _enum.Enum): - """Enumeration listing the various possibilities of communication links.""" - - UNSET = "UNSET" - """The CommunicationLink protocol is not yet set.""" - PRODUCE = "PRODUCE" - """The CommunicationLink describes a production of ExchangeItem.""" - CONSUME = "CONSUME" - """The CommunicationLink describes a comsumption of ExchangeItem.""" - SEND = "SEND" - """The CommunicationLink describes a sending of ExchangeItem.""" - RECEIVE = "RECEIVE" - """The CommunicationLink describes a reception of ExchangeItem.""" - CALL = "CALL" - """The CommunicationLink describes a call of ExchangeItem.""" - EXECUTE = "EXECUTE" - """The CommunicationLink describes an execution of ExchangeItem.""" - WRITE = "WRITE" - """The CommunicationLink describes a writing of ExchangeItem.""" - ACCESS = "ACCESS" - """The CommunicationLink describes an access to the ExchangeItem.""" - ACQUIRE = "ACQUIRE" - """The CommunicationLink describes an acquisition of ExchangeItem.""" - TRANSMIT = "TRANSMIT" - """The CommunicationLink describes a transmission of ExchangeItem.""" - - -@_enum.unique -class CommunicationLinkProtocol(_StringyEnumMixin, _enum.Enum): - """The various possibilities for the protocol of the communication link.""" - - UNSET = "UNSET" - """The CommunicationLink protocol is not yet set.""" - UNICAST = "UNICAST" - """Describes sending an ExchangeItem using the unicast protocol.""" - MULTICAST = "MULTICAST" - """Describes sending an ExchangeItem using the multicast protocol.""" - BROADCAST = "BROADCAST" - """Describes sending an ExchangeItem using the broadcast protocol.""" - SYNCHRONOUS = "SYNCHRONOUS" - """Describes a call of the ExchangeItem using the synchronous protocol.""" - ASYNCHRONOUS = "ASYNCHRONOUS" - """Describes a call of the ExchangeItem using the asynchronous protocol.""" - READ = "READ" - """Describes access to the ExchangeItem by reading it.""" - ACCEPT = "ACCEPT" - """Describes access to the ExchangeItem by accepting it.""" - - -@_enum.unique -class ComponentExchangeKind(_StringyEnumMixin, _enum.Enum): - """The kind of a ComponentExchange.""" - - UNSET = "UNSET" - """Communication kind is not set.""" - DELEGATION = "DELEGATION" - """Indicates that the connector is a delegation connector.""" - ASSEMBLY = "ASSEMBLY" - """Indicates that the connector is an assembly connector.""" - FLOW = "FLOW" - """Describes a flow communication.""" - - -@_enum.unique -class ComponentPortKind(_StringyEnumMixin, _enum.Enum): - STANDARD = "STANDARD" - """Describes a standard port. - - A port is an interaction point between a Block or sub-Block and its - environment that supports Exchanges with other ports. - """ - FLOW = "FLOW" - """Describes a flow port. - - A flow port is an interaction point through which input and/or - output of items such as data, material, or energy may flow. - """ - - -@_enum.unique -class ConfigurationItemKind(_StringyEnumMixin, _enum.Enum): - UNSET = "Unset" - COTSCI = "COTSCI" - """Commercial Off The Shelves Configuration Item.""" - CSCI = "CSCI" - """Computer Software Configuration Item.""" - HWCI = "HWCI" - """Hardware Configuration Item.""" - INTERFACE_CI = "InterfaceCI" - """Interface Configuration Item.""" - NDICI = "NDICI" - """Non Developmental Configuration Item.""" - PRIME_ITEM_CI = "PrimeItemCI" - """Prime Item Configuration Item.""" - SYSTEM_CI = "SystemCI" - """System Configuration Item.""" - - -@_enum.unique -class ControlNodeKind(_StringyEnumMixin, _enum.Enum): - OR = "OR" - AND = "AND" - ITERATE = "ITERATE" - - -@_enum.unique -class ElementKind(_StringyEnumMixin, _enum.Enum): - """The visibility options for features of a class.""" - - TYPE = "TYPE" - """The ExchangeItemElement is a type for its ExchangeItem.""" - MEMBER = "MEMBER" - """The ExchangeItemElement is a member for its ExchangeItem.""" - - -@_enum.unique -class ExchangeMechanism(_StringyEnumMixin, _enum.Enum): - """Enumeration of the different exchange mechanisms.""" - - UNSET = "UNSET" - """The exchange mechanism is not defined.""" - FLOW = "FLOW" - """Continuous supply of data.""" - OPERATION = "OPERATION" - """Sporadic supply of data with returned data.""" - EVENT = "EVENT" - """Asynchronous information that is taken into account rapidly.""" - SHARED_DATA = "SHARED_DATA" - - -@_enum.unique -class FunctionalChainKind(_StringyEnumMixin, _enum.Enum): - """The kind of a Functional Chain.""" - - SIMPLE = "SIMPLE" - COMPOSITE = "COMPOSITE" - FRAGMENT = "FRAGMENT" - - -@_enum.unique -class FunctionKind(_StringyEnumMixin, _enum.Enum): - """The kind of a Function.""" - - FUNCTION = "FUNCTION" - DUPLICATE = "DUPLICATE" - GATHER = "GATHER" - SELECT = "SELECT" - SPLIT = "SPLIT" - ROUTE = "ROUTE" - - -@_enum.unique -class InteractionOperatorKind(_StringyEnumMixin, _enum.Enum): - UNSET = "UNSET" - ALT = "ALT" - OPT = "OPT" - PAR = "PAR" - LOOP = "LOOP" - CRITICAL = "CRITICAL" - NEG = "NEG" - ASSERT = "ASSERT" - STRICT = "STRICT" - SEQ = "SEQ" - IGNORE = "IGNORE" - CONSIDER = "CONSIDER" - - -@_enum.unique -class MessageKind(_StringyEnumMixin, _enum.Enum): - """Identifies the type of message. - - This concept is similar to UML ``MessageSort``. - """ - - UNSET = "UNSET" - """The message kind is not specified.""" - ASYNCHRONOUS_CALL = "ASYNCHRONOUS_CALL" - """The message was generated by an asynchronous call to an operation. - - Equivalent to UML ``MessageSort::asynchCall``. - """ - SYNCHRONOUS_CALL = "SYNCHRONOUS_CALL" - """The message was generated by a synchronous call to an operation. - - Equivalent to UML ``MessageSort::synchCall``. - """ - REPLY = "REPLY" - """The message is a reply message to an operation call. - - Equivalent to UML ``MessageSort::reply``. - """ - DELETE = "DELETE" - """The message designates the termination of another lifeline. - - Equivalent to UML ``MessageSort::deleteMessage``. - """ - CREATE = "CREATE" - """The message designates the creation of an instance role.""" - TIMER = "TIMER" - - -@_enum.unique -class NumericTypeKind(_StringyEnumMixin, _enum.Enum): - """The kind of this numeric data type.""" - - INTEGER = "INTEGER" - FLOAT = "FLOAT" - - -@_enum.unique -class ObjectNodeKind(_StringyEnumMixin, _enum.Enum): - """The behavior type of the object node with respect to incoming data.""" - - UNSPECIFIED = "Unspecified" - """Used when incoming object node management policy is not specified.""" - NO_BUFFER = "NoBuffer" - """Discard incoming tokens if they are refused. - - When the "nobuffer" stereotype is applied to object nodes, tokens - arriving at the node are discarded if they are refused by outgoing - edges, or refused by actions for object nodes that are input pins. - """ - OVERWRITE = "Overwrite" - """Incoming tokens may overwrite existing ones. - - When the "overwrite" stereotype is applied to object nodes, a token - arriving at a full object node replaces the ones already there. A - full object node has as many tokens as allowed by its upper bound. - """ - - -@_enum.unique -class ObjectNodeOrderingKind(_StringyEnumMixin, _enum.Enum): - """Indicates queuing order within a node.""" - - FIFO = "FIFO" - """First In First Out ordering.""" - LIFO = "LIFO" - """Last In First Out ordering.""" - ORDERED = "ordered" - """Indicates that object node tokens are ordered.""" - UNORDERED = "unordered" - """Indicates that object node tokens are unordered.""" - - -@_enum.unique -class OrientationPortKind(_StringyEnumMixin, _enum.Enum): - """Direction of component ports.""" - - UNSET = "UNSET" - """The port orientation is undefined.""" - IN = "IN" - """The port represents an input of the component it is used in.""" - OUT = "OUT" - """The port represents an output of the component it is used in.""" - INOUT = "INOUT" - """The port represents both an input and on output of the component.""" - - -@_enum.unique -class ParameterDirection(_StringyEnumMixin, _enum.Enum): - """The direction in which data is passed along through a parameter.""" - - IN = "IN" - """The parameter represents an input of the operation it is used in.""" - OUT = "OUT" - """The parameter represents an output of the operation it is used in.""" - INOUT = "INOUT" - """The parameter represents both an input and output of the operation.""" - RETURN = "RETURN" - """The parameter represents the return value of the operation.""" - EXCEPTION = "EXCEPTION" - """The parameter is like an exception.""" - UNSET = "UNSET" - """The CommunicationLink protocol is not yet set.""" - - -@_enum.unique -class ParameterEffectKind(_StringyEnumMixin, _enum.Enum): - """A behavior's effect on values passed in or out of its parameters.""" - - CREATE = "create" - """The parameter value is being created upon behavior execution.""" - READ = "read" - """The parameter value is only being read upon behavior execution.""" - UPDATE = "update" - """The parameter value is being updated upon behavior execution.""" - DELETE = "delete" - """The parameter value is being deleted upon behavior execution.""" - - -@_enum.unique -class PassingMode(_StringyEnumMixin, _enum.Enum): - """The data passing mechanism for parameters of an operation.""" - - UNSET = "UNSET" - """The data passing mechanism is not precised.""" - BY_REF = "BY_REF" - """The data is being passed by reference to the operation.""" - BY_VALUE = "BY_VALUE" - """The data is being passed by value to the operation.""" - - -@_enum.unique -class PhysicalComponentKind(_StringyEnumMixin, _enum.Enum): - """Categories of physical components. - - Allows to categorize a physical component, with respect to real life - physical entities. - """ - - UNSET = "UNSET" - """The physical component kind is not specified.""" - HARDWARE = "HARDWARE" - """The physical component is a hardware resource.""" - HARDWARE_COMPUTER = "HARDWARE_COMPUTER" - """The physical component is a computing resource.""" - SOFTWARE = "SOFTWARE" - """The physical component is a software entity.""" - SOFTWARE_DEPLOYMENT_UNIT = "SOFTWARE_DEPLOYMENT_UNIT" - """The physical component is a software deployment unit.""" - SOFTWARE_EXECUTION_UNIT = "SOFTWARE_EXECUTION_UNIT" - """The physical component is a software execution unit.""" - SOFTWARE_APPLICATION = "SOFTWARE_APPLICATION" - """The physical component is a software application.""" - FIRMWARE = "FIRMWARE" - """The physical component is a firmware part.""" - PERSON = "PERSON" - """The physical component is a person.""" - FACILITIES = "FACILITIES" - """The physical component refers to Facilities.""" - DATA = "DATA" - """The physical component represents a set of data.""" - MATERIALS = "MATERIALS" - """The physical component represents a bunch of materials.""" - SERVICES = "SERVICES" - """The physical component represents a set of services.""" - PROCESSES = "PROCESSES" - """The physical component represents a set of processes.""" - - -@_enum.unique -class PhysicalComponentNature(_StringyEnumMixin, _enum.Enum): - """The nature of a physical component.""" - - UNSET = "UNSET" - """The physical component nature is not specified.""" - BEHAVIOR = "BEHAVIOR" - """The physical component nature is behavioral. - - This typically means a piece of software. - """ - NODE = "NODE" - """The physical component is a host for behavioral components. - - This typically means a computing resource. - """ - - -@_enum.unique -class RateKind(_StringyEnumMixin, _enum.Enum): - """The possible caracterizations for the rate of a streaming parameter.""" - - UNSPECIFIED = "Unspecified" - """The rate kind is not specified.""" - CONTINUOUS = "Continuous" - """The rate characterizes a continuous flow.""" - DISCRETE = "Discrete" - """The rate characterizes a discrete flow.""" - - -@_enum.unique -class ScenarioKind(_StringyEnumMixin, _enum.Enum): - UNSET = "UNSET" - INTERFACE = "INTERFACE" - DATA_FLOW = "DATA_FLOW" - INTERACTION = "INTERACTION" - FUNCTIONAL = "FUNCTIONAL" - - -@_enum.unique -class SynchronismKind(_StringyEnumMixin, _enum.Enum): - """The synchronicity of an operation invocation.""" - - UNSET = "UNSET" - SYNCHRONOUS = "SYNCHRONOUS" - ASYNCHRONOUS = "ASYNCHRONOUS" - - -@_enum.unique -class TimeEventKind(_StringyEnumMixin, _enum.Enum): - AT = "AT" - """Trigger at a specific time. - - An absolute time trigger is specified with the keyword 'at' followed - by an expression that evaluates to a time value, such as 'Jan. 1, - 2000, Noon'. - """ - AFTER = "AFTER" - """Trigger after a relative time duration has passed. - - A relative time trigger is specified with the keyword 'after' - followed by an expression that evaluates to a time value, such as - 'after (5 seconds)'. - """ - - -@_enum.unique -class TransitionKind(_StringyEnumMixin, _enum.Enum): - INTERNAL = "internal" - LOCAL = "local" - EXTERNAL = "external" - - -@_enum.unique -class UnaryOperator(_StringyEnumMixin, _enum.Enum): - """The operator of a unary expression.""" - - UNSET = "UNSET" - """The unary operator is not initialized.""" - NOT = "NOT" - """The unary operator refers to a NOT operation.""" - POS = "POS" - """The unary operator refers to a position operation.""" - VAL = "VAL" - """The unary operator refers to a value operation.""" - SUC = "SUC" - """The unary operator refers to a successor operation.""" - PRE = "PRE" - """The unary operator refers to a predecessor operation.""" - - -@_enum.unique -class UnionKind(_StringyEnumMixin, _enum.Enum): - UNION = "UNION" - VARIANT = "VARIANT" - - -@_enum.unique -class VisibilityKind(_StringyEnumMixin, _enum.Enum): - """The possibilities regarding the visibility of a feature of a class.""" - - UNSET = "UNSET" - """Visibility is not specified.""" - PUBLIC = "PUBLIC" - """The feature offers public access.""" - PROTECTED = "PROTECTED" - """The feature offers visibility only to children of the class.""" - PRIVATE = "PRIVATE" - """The feature is only visible/accessible from the class itself.""" - PACKAGE = "PACKAGE" - """The feature is accessible from any element within the same package.""" +import warnings + +warnings.warn( + ( + "The 'modeltypes' module is deprecated," + " use the enums from their respective source module instead" + ), + DeprecationWarning, + stacklevel=2, +) + +from .activity import ObjectNodeKind as ObjectNodeKind +from .activity import ObjectNodeOrderingKind as ObjectNodeOrderingKind +from .capellacommon import ChangeEventKind as ChangeEventKind +from .capellacommon import TimeEventKind as TimeEventKind +from .capellacommon import TransitionKind as TransitionKind +from .capellacore import VisibilityKind as VisibilityKind +from .epbs import ConfigurationItemKind as ConfigurationItemKind +from .fa import ComponentExchangeKind as ComponentExchangeKind +from .fa import ComponentPortKind as ComponentPortKind +from .fa import ControlNodeKind as ControlNodeKind +from .fa import FunctionalChainKind as FunctionalChainKind +from .fa import FunctionKind as FunctionKind +from .fa import OrientationPortKind as OrientationPortKind +from .information import AggregationKind as AggregationKind +from .information import CollectionKind as CollectionKind +from .information import ElementKind as ElementKind +from .information import ExchangeMechanism as ExchangeMechanism +from .information import ParameterDirection as ParameterDirection +from .information import PassingMode as PassingMode +from .information import SynchronismKind as SynchronismKind +from .information import UnionKind as UnionKind +from .information.communication import ( + CommunicationLinkKind as CommunicationLinkKind, +) +from .information.communication import ( + CommunicationLinkProtocol as CommunicationLinkProtocol, +) +from .information.datatype import NumericTypeKind as NumericTypeKind +from .information.datavalue import BinaryOperator as BinaryOperator +from .information.datavalue import UnaryOperator as UnaryOperator +from .interaction import InteractionOperatorKind as InteractionOperatorKind +from .interaction import MessageKind as MessageKind +from .interaction import ScenarioKind as ScenarioKind +from .libraries import AccessPolicy as AccessPolicy +from .modellingcore import ParameterEffectKind as ParameterEffectKind +from .modellingcore import RateKind as RateKind +from .pa import PhysicalComponentKind as PhysicalComponentKind +from .pa import PhysicalComponentNature as PhysicalComponentNature +from .re import CatalogElementKind as CatalogElementKind diff --git a/capellambse/metamodel/namespaces.py b/capellambse/metamodel/namespaces.py index 788070cf..63291e3f 100644 --- a/capellambse/metamodel/namespaces.py +++ b/capellambse/metamodel/namespaces.py @@ -113,9 +113,21 @@ m.CORE_VIEWPOINT, "7.0.0", ) +RE = m.Namespace( + "http://www.polarsys.org/capella/common/re/{VERSION}", + "re", + m.CORE_VIEWPOINT, + "7.0.0", +) SA = m.Namespace( "http://www.polarsys.org/capella/core/ctx/{VERSION}", "org.polarsys.capella.core.data.ctx", m.CORE_VIEWPOINT, "7.0.0", ) +SHARED_MODEL = m.Namespace( + "http://www.polarsys.org/capella/core/sharedmodel/{VERSION}", + "org.polarsys.capella.core.data.sharedmodel", + m.CORE_VIEWPOINT, + "7.0.0", +) diff --git a/capellambse/metamodel/oa.py b/capellambse/metamodel/oa.py index 90af5967..d5518823 100644 --- a/capellambse/metamodel/oa.py +++ b/capellambse/metamodel/oa.py @@ -1,261 +1,422 @@ # SPDX-FileCopyrightText: Copyright DB InfraGO AG # SPDX-License-Identifier: Apache-2.0 -"""Tools for the Operational Analysis layer. - -.. diagram:: [CDB] OA ORM -""" +"""Tools for the Operational Analysis layer.""" from __future__ import annotations -from capellambse import model as m +import typing as t +import warnings + +import capellambse.model as m -from . import capellacommon, capellacore, cs, fa, information, interaction +from . import ( + activity, + capellacommon, + capellacore, + cs, + fa, + information, + interaction, + modellingcore, +) from . import namespaces as ns +if t.TYPE_CHECKING: + from . import sa # noqa: F401 + NS = ns.OA -class OperationalActivity(fa.AbstractFunction): - """An operational activity.""" +class OperationalAnalysis(cs.BlockArchitecture): + """Provides access to the OperationalAnalysis layer of the model.""" - _xmltag = "ownedOperationalActivities" + role_pkg = m.Containment["RolePkg"]("ownedRolePkg", (NS, "RolePkg")) + entity_pkg = m.Single["EntityPkg"]( + m.Containment("ownedEntityPkg", (NS, "EntityPkg")) + ) + entity_package = m.DeprecatedAccessor["EntityPkg"]("entity_pkg") + concept_pkg = m.Containment["ConceptPkg"]( + "ownedConceptPkg", (NS, "ConceptPkg") + ) - exchanges = m.DirectProxyAccessor( - fa.FunctionalExchange, aslist=m.ElementList + activity_pkg = m.Alias["OperationalActivityPkg"]("function_pkg") + activity_package = m.DeprecatedAccessor["OperationalActivityPkg"]( + "activity_pkg" ) + capability_package = m.DeprecatedAccessor("capability_pkg") + + @property + def all_activities(self) -> m.ElementList[OperationalActivity]: + return self._model.search((NS, "OperationalActivity"), below=self) - inputs = m.Backref(fa.FunctionalExchange, "target") - outputs = m.Backref(fa.FunctionalExchange, "source") + @property + def all_processes(self) -> m.ElementList[OperationalProcess]: + return self._model.search((NS, "OperationalProcess"), below=self) - owner: m.Single[Entity] + @property + def all_actors(self) -> m.ElementList[Entity]: + return self._model.search((NS, "Entity")).by_is_actor(True) @property - def related_exchanges(self) -> m.ElementList[fa.FunctionalExchange]: - seen: set[str] = set() - exchanges = [] - for fex in self.inputs + self.outputs: - if fex.uuid not in seen: - exchanges.append(fex._element) - seen.add(fex.uuid) - return self.inputs._newlist(exchanges) + def all_entities(self) -> m.ElementList[Entity]: + return self._model.search((NS, "Entity"), below=self) + @property + def all_activity_exchanges(self) -> m.ElementList[fa.FunctionalExchange]: + return self._model.search((ns.FA, "FunctionalExchange"), below=self) -class OperationalProcess(fa.FunctionalChain): - """An operational process.""" + @property + def all_entity_exchanges(self) -> m.ElementList[CommunicationMean]: + return self._model.search((NS, "CommunicationMean"), below=self) + @property + def all_operational_processes(self) -> m.ElementList[OperationalProcess]: + return self._model.search(OperationalProcess, below=self) -class EntityOperationalCapabilityInvolvement(interaction.AbstractInvolvement): - """An EntityOperationalCapabilityInvolvement.""" + @property + def root_activity(self) -> OperationalActivity: + pkg = self.function_pkg + if pkg is None: + raise m.BrokenModelError( + "OperationalAnalysis has no root ActivityPkg" + ) + candidates = pkg.functions + if len(candidates) < 1: + raise m.BrokenModelError( + "ActivityPkg does not contain any Activities" + ) + if len(candidates) > 1: + raise m.BrokenModelError( + "More than one Activity in the root ActivityPkg" + ) + return candidates[0] + @property + def root_entity(self) -> Entity: + pkg = self.entity_pkg + if pkg is None: + raise m.BrokenModelError( + "OperationalAnalysis has no root EntityPkg" + ) + candidates = pkg.entities + if len(candidates) < 1: + raise m.BrokenModelError("Root EntityPkg is empty") + if len(candidates) > 1: + raise m.BrokenModelError( + "More than one Entity in the root EntityPkg" + ) + return candidates[0] -class OperationalCapability(m.ModelElement): - """A capability in the OperationalAnalysis layer.""" + diagrams = m.DiagramAccessor( + "Operational Analysis", cacheattr="_MelodyModel__diagram_cache" + ) + + +class OperationalScenario(capellacore.NamedElement, abstract=True): + context = m.StringPOD("context") + objective = m.StringPOD("objective") + + +class OperationalActivityPkg(fa.FunctionPkg): + _xmltag = "ownedFunctionPkg" + + activities = m.Containment["OperationalActivity"]( + "ownedOperationalActivities", (NS, "OperationalActivity") + ) + packages = m.Containment["OperationalActivityPkg"]( + "ownedOperationalActivityPkgs", (NS, "OperationalActivityPkg") + ) + owner = m.Single["Entity"](m.Backref((NS, "Entity"), "activities")) # TODO - _xmltag = "ownedOperationalCapabilities" - extends = m.DirectProxyAccessor( - interaction.AbstractCapabilityExtend, aslist=m.ElementList +class OperationalActivity(fa.AbstractFunction): + _xmltag = "ownedOperationalActivities" + + packages = m.Containment["OperationalActivityPkg"]( + "ownedOperationalActivityPkgs", (NS, "OperationalActivityPkg") ) - extended_by = m.Backref(interaction.AbstractCapabilityExtend, "target") - includes = m.DirectProxyAccessor( - interaction.AbstractCapabilityInclude, aslist=m.ElementList + activities = m.Alias["OperationalActivity"]("functions") + inputs = m.Backref["fa.FunctionalExchange"]( # type: ignore[assignment] + (ns.FA, "FunctionalExchange"), "target" ) - included_by = m.Backref(interaction.AbstractCapabilityInclude, "target") - generalizes = m.DirectProxyAccessor( - interaction.AbstractCapabilityGeneralization, aslist=m.ElementList + outputs = m.Backref["fa.FunctionalExchange"]( # type: ignore[assignment] + (ns.FA, "FunctionalExchange"), "source" ) - generalized_by = m.DirectProxyAccessor( - interaction.AbstractCapabilityGeneralization, - "target", - aslist=m.ElementList, + realizing_system_functions = m.Backref["sa.SystemFunction"]( + (ns.SA, "SystemFunction"), "realized_operational_activities" ) - involved_activities = m.Allocation[OperationalActivity]( - "ownedAbstractFunctionAbstractCapabilityInvolvements", - interaction.AbstractFunctionAbstractCapabilityInvolvement, - attr="involved", + + owner = m.Single["Entity"](m.Backref((NS, "Entity"), "activities")) + + related_exchanges = m.Backref[fa.FunctionalExchange]( + (ns.FA, "FunctionalExchange"), "source", "target" ) - involved_entities = m.Allocation[m.ModelElement]( - "ownedEntityOperationalCapabilityInvolvements", - EntityOperationalCapabilityInvolvement, - attr="involved", + + +class OperationalProcess(fa.FunctionalChain): + pass + + +class Swimlane(capellacore.NamedElement, activity.ActivityPartition): + pass + + +class OperationalCapabilityPkg(capellacommon.AbstractCapabilityPkg): + _xmltag = "ownedAbstractCapabilityPkg" + + capabilities = m.Containment["OperationalCapability"]( + "ownedOperationalCapabilities", (NS, "OperationalCapability") ) - entity_involvements = m.DirectProxyAccessor( - EntityOperationalCapabilityInvolvement, aslist=m.ElementList + packages = m.Containment["OperationalCapabilityPkg"]( + "ownedOperationalCapabilityPkgs", (NS, "OperationalCapabilityPkg") ) - involved_processes = m.Allocation[OperationalProcess]( - "ownedFunctionalChainAbstractCapabilityInvolvements", - interaction.FunctionalChainAbstractCapabilityInvolvement, - attr="involved", + capability_configurations = m.Containment["CapabilityConfiguration"]( + "ownedCapabilityConfigurations", (NS, "CapabilityConfiguration") ) - owned_processes = m.DirectProxyAccessor( - OperationalProcess, aslist=m.ElementList + concept_compliances = m.Containment["ConceptCompliance"]( + "ownedConceptCompliances", (NS, "ConceptCompliance") ) + complies_with_concepts = m.Allocation["Concept"]( + "ownedConceptCompliances", + (NS, "ConceptCompliance"), + (NS, "Concept"), + attr="complyWithConcept", + backattr="compliantCapability", + ) + + +class OperationalCapability( + interaction.AbstractCapability, capellacore.Namespace +): + """A capability in the OperationalAnalysis layer.""" + + _xmltag = "ownedOperationalCapabilities" - postcondition = m.Single( - m.Association(capellacore.Constraint, "postCondition") + compliances = m.Association["ConceptCompliance"]( + (NS, "ConceptCompliance"), "compliances" ) - precondition = m.Single( - m.Association(capellacore.Constraint, "preCondition") + configurations = m.Association["CapabilityConfiguration"]( + (NS, "CapabilityConfiguration"), "configurations" ) - scenarios = m.DirectProxyAccessor( - interaction.Scenario, aslist=m.ElementList + entity_involvements = m.Containment[ + "EntityOperationalCapabilityInvolvement" + ]( + "ownedEntityOperationalCapabilityInvolvements", + (NS, "EntityOperationalCapabilityInvolvement"), + ) + involved_entities = m.Allocation["Entity"]( + "ownedEntityOperationalCapabilityInvolvements", + (NS, "EntityOperationalCapabilityInvolvement"), + (NS, "Entity"), + attr="involved", ) - states = m.Association(capellacommon.State, "availableInStates") + involved_activities = m.Alias["OperationalActivity"]("involved_functions") + involved_processes = m.Alias["OperationalProcess"]("involved_chains") + owned_processes = m.Alias["OperationalProcess"]("functional_chains") + packages: m.Accessor # TODO - packages: m.Accessor +class ActivityAllocation(capellacore.Allocation): + pass -class OperationalCapabilityPkg(m.ModelElement): - """A package that holds operational capabilities.""" - _xmltag = "ownedAbstractCapabilityPkg" +class RolePkg(capellacore.Structure): + packages = m.Containment["RolePkg"]("ownedRolePkgs", (NS, "RolePkg")) + roles = m.Containment["Role"]("ownedRoles", (NS, "Role")) + - capabilities = m.DirectProxyAccessor( - OperationalCapability, aslist=m.ElementList +class Role(information.AbstractInstance): + assembly_usages = m.Containment["RoleAssemblyUsage"]( + "ownedRoleAssemblyUsages", (NS, "RoleAssemblyUsage") + ) + activity_allocations = m.Containment["ActivityAllocation"]( + "ownedActivityAllocations", (NS, "ActivityAllocation") ) - packages: m.Accessor +class RoleAssemblyUsage(capellacore.NamedElement): + child = m.Association["Role"]((NS, "Role"), "child") -class AbstractEntity(cs.Component): - """Common code for Entities.""" - activities = m.Allocation[OperationalActivity]( - "ownedFunctionalAllocation", - fa.ComponentFunctionalAllocation, - attr="targetElement", - backattr="sourceElement", +class RoleAllocation(capellacore.Allocation): + pass + + +class EntityPkg(cs.ComponentPkg): + _xmltag = "ownedEntityPkg" + + entities = m.Containment["Entity"]("ownedEntities", (NS, "Entity")) + packages = m.Containment["EntityPkg"]("ownedEntityPkgs", (NS, "EntityPkg")) + locations = m.Containment["Location"]("ownedLocations", (NS, "Location")) + communication_means = m.Alias["CommunicationMean"]("exchanges") + + +class AbstractConceptItem(cs.Component, abstract=True): + composing_links = m.Association["ItemInConcept"]( + (NS, "ItemInConcept"), "composingLinks" ) - capabilities = m.Backref(OperationalCapability, "involved_entities") -class Entity(AbstractEntity): +class Entity( + AbstractConceptItem, + modellingcore.InformationsExchanger, + capellacore.InvolvedElement, +): """An Entity in the OperationalAnalysis layer.""" _xmltag = "ownedEntities" - entities: m.Accessor + organisational_unit_memberships = m.Association[ + "OrganisationalUnitComposition" + ]((NS, "OrganisationalUnitComposition"), "organisationalUnitMemberships") + actual_location = m.Association["Location"]( + (NS, "Location"), "actualLocation" + ) + entities = m.Containment["Entity"]("ownedEntities", (NS, "Entity")) + communication_means = m.Containment["CommunicationMean"]( + "ownedCommunicationMeans", (NS, "CommunicationMean") + ) + exchanges = m.Alias["m.ElementList[CommunicationMean]"]( + "communication_means" + ) + activities = m.Allocation[OperationalActivity]( + "ownedFunctionalAllocation", + fa.ComponentFunctionalAllocation, + attr="targetElement", + backattr="sourceElement", + ) + capabilities = m.Backref(OperationalCapability, "involved_entities") + related_exchanges = m.Backref["CommunicationMean"]( + (NS, "CommunicationMean"), "source", "target" + ) + realizing_system_components = m.Backref["sa.SystemComponent"]( + (ns.SA, "SystemComponent"), "realized_operational_entities" + ) @property def inputs(self) -> m.ElementList[CommunicationMean]: - return self._model.search(CommunicationMean).by_target(self) + return self._model.search((NS, "CommunicationMean")).by_target(self) @property def outputs(self) -> m.ElementList[CommunicationMean]: - return self._model.search(CommunicationMean).by_source(self) - + return self._model.search((NS, "CommunicationMean")).by_source(self) -class OperationalActivityPkg(m.ModelElement): - """A package that holds operational entities.""" - _xmltag = "ownedFunctionPkg" - - activities = m.DirectProxyAccessor( - OperationalActivity, aslist=m.ElementList +class ConceptPkg(capellacore.Structure): + packages = m.Containment["ConceptPkg"]( + "ownedConceptPkgs", (NS, "ConceptPkg") ) + concepts = m.Containment["Concept"]("ownedConcepts", (NS, "Concept")) - packages: m.Accessor +class Concept(capellacore.NamedElement): + compliances = m.Association["ConceptCompliance"]( + (NS, "ConceptCompliance"), "compliances" + ) + composite_links = m.Containment["ItemInConcept"]( + "compositeLinks", (NS, "ItemInConcept") + ) -class CommunicationMean(fa.AbstractExchange): - """An operational entity exchange.""" - - _xmltag = "ownedComponentExchanges" - allocated_interactions = m.Allocation[fa.FunctionalExchange]( - None, # FIXME fill in tag - fa.ComponentExchangeFunctionalExchangeAllocation, - attr="targetElement", +class ConceptCompliance(capellacore.Relationship): + comply_with_concept = m.Single["Concept"]( + m.Association((NS, "Concept"), "complyWithConcept") ) - allocated_exchange_items = m.Association( - information.ExchangeItem, - "convoyedInformations", + compliant_capability = m.Single["OperationalCapability"]( + m.Association((NS, "OperationalCapability"), "compliantCapability") ) - exchange_items = fa.ComponentExchange.exchange_items +class ItemInConcept(capellacore.NamedElement): + concept = m.Single["Concept"](m.Association((NS, "Concept"), "concept")) + item = m.Single["AbstractConceptItem"]( + m.Association((NS, "AbstractConceptItem"), "item") + ) -class EntityPkg(m.ModelElement): - """A package that holds operational entities.""" - - _xmltag = "ownedEntityPkg" - entities = m.DirectProxyAccessor(Entity, aslist=m.ElementList) - state_machines = m.DirectProxyAccessor( - capellacommon.StateMachine, aslist=m.ElementList +class CommunityOfInterest(capellacore.NamedElement): + community_of_interest_compositions = m.Containment[ + "CommunityOfInterestComposition" + ]( + "communityOfInterestCompositions", + (NS, "CommunityOfInterestComposition"), ) - packages: m.Accessor - exchanges = m.DirectProxyAccessor(CommunicationMean, aslist=m.ElementList) +class CommunityOfInterestComposition(capellacore.NamedElement): + community_of_interest = m.Association["CommunityOfInterest"]( + (NS, "CommunityOfInterest"), "communityOfInterest" + ) + interested_organisational_unit = m.Association["OrganisationalUnit"]( + (NS, "OrganisationalUnit"), "interestedOrganisationUnit" + ) -class OperationalAnalysis(cs.ComponentArchitecture): - """Provides access to the OperationalAnalysis layer of the model.""" - root_entity = m.DirectProxyAccessor(Entity, rootelem=EntityPkg) - root_activity = m.DirectProxyAccessor( - OperationalActivity, rootelem=OperationalActivityPkg - ) +class OrganisationalUnit(capellacore.NamedElement): + organisational_unit_compositions = m.Containment[ + "OrganisationalUnitComposition" + ]("organisationalUnitCompositions", (NS, "OrganisationalUnitComposition")) + community_of_interest_memberships = m.Association[ + "CommunityOfInterestComposition" + ]((NS, "CommunityOfInterestComposition"), "communityOfInterestMemberships") - activity_package = m.DirectProxyAccessor(OperationalActivityPkg) - capability_package = m.DirectProxyAccessor(OperationalCapabilityPkg) - entity_package = m.DirectProxyAccessor(EntityPkg) - all_activities = m.DeepProxyAccessor( - OperationalActivity, - aslist=m.ElementList, - ) - all_processes = m.DeepProxyAccessor( - OperationalProcess, - aslist=m.ElementList, +class OrganisationalUnitComposition(capellacore.NamedElement): + organisational_unit = m.Association["OrganisationalUnit"]( + (NS, "OrganisationalUnit"), "organisationalUnit" ) - all_capabilities = m.DeepProxyAccessor( - OperationalCapability, - aslist=m.ElementList, - ) - all_actors = property( - lambda self: self._model.search(Entity).by_is_actor(True) - ) - all_entities = m.DeepProxyAccessor( - Entity, - aslist=m.ElementList, + participating_entity = m.Association["Entity"]( + (NS, "Entity"), "participatingEntity" ) - all_activity_exchanges = m.DeepProxyAccessor( - fa.FunctionalExchange, - aslist=m.ElementList, - rootelem=[OperationalActivityPkg, OperationalActivity], + +class Location(AbstractConceptItem): + location_description = m.StringPOD("locationDescription") + located_entities = m.Association["Entity"]( + (NS, "Entity"), "locatedEntities" ) - all_entity_exchanges = m.DeepProxyAccessor( - CommunicationMean, - aslist=m.ElementList, + + +class CapabilityConfiguration(AbstractConceptItem): + configured_capability = m.Association["OperationalCapability"]( + (NS, "OperationalCapability"), "configuredCapability" ) - all_operational_processes = property( - lambda self: self._model.search(OperationalProcess, below=self) + + +# NOTE: CommunicationMean should directly inherit from NamedRelationship, +# however this would result in an MRO conflict that cannot be resolved. +# Therefore we only inherit from ComponentExchange, copy the only missing +# definition (naming_rules), and register it as virtual subclass. +class CommunicationMean(fa.ComponentExchange): + """An operational entity exchange.""" + + _xmltag = "ownedComponentExchanges" + + # Taken from NamedRelationship, see note above + naming_rules = m.Containment["capellacore.NamingRule"]( + "namingRules", (ns.CAPELLACORE, "NamingRule") ) - diagrams = m.DiagramAccessor( - "Operational Analysis", cacheattr="_MelodyModel__diagram_cache" + allocated_interactions = m.Alias["fa.FunctionalExchange"]( + "allocated_functional_exchanges" ) -OperationalActivity.packages = m.DirectProxyAccessor( - OperationalActivityPkg, aslist=m.ElementList -) -OperationalActivity.owner = m.Single(m.Backref(Entity, "activities")) -Entity.exchanges = m.DirectProxyAccessor( - CommunicationMean, # type: ignore[arg-type] # FIXME - aslist=m.ElementList, -) -Entity.related_exchanges = m.Backref(CommunicationMean, "source", "target") # type: ignore[arg-type] # FIXME -OperationalActivity.activities = m.DirectProxyAccessor( - OperationalActivity, aslist=m.ElementList -) -OperationalActivityPkg.packages = m.DirectProxyAccessor( - OperationalActivityPkg, aslist=m.ElementList -) -OperationalCapabilityPkg.packages = m.DirectProxyAccessor( - OperationalCapabilityPkg, aslist=m.ElementList -) -Entity.entities = m.DirectProxyAccessor(Entity, aslist=m.ElementList) -EntityPkg.packages = m.DirectProxyAccessor(EntityPkg, aslist=m.ElementList) +capellacore.NamedRelationship.register(CommunicationMean) + + +class EntityOperationalCapabilityInvolvement(capellacore.Involvement): + pass + + +if not t.TYPE_CHECKING: + + def __getattr__(name): + if name == "AbstractEntity": + warnings.warn( + "AbstractEntity has been merged into Entity", + DeprecationWarning, + stacklevel=2, + ) + return Entity + raise AttributeError(name) diff --git a/capellambse/metamodel/pa.py b/capellambse/metamodel/pa.py index 45e1c62e..ddfc728f 100644 --- a/capellambse/metamodel/pa.py +++ b/capellambse/metamodel/pa.py @@ -1,189 +1,242 @@ # SPDX-FileCopyrightText: Copyright DB InfraGO AG # SPDX-License-Identifier: Apache-2.0 -"""Tools for the Physical Architecture layer. - -.. diagram:: [CDB] Physical Architecture [Ontology] -""" +"""Tools for the Physical Architecture layer.""" from __future__ import annotations +import enum +import typing as t + import capellambse.model as m -from . import capellacommon, cs, fa, la, modeltypes +from . import capellacommon, cs, fa, information from . import namespaces as ns -NS = ns.PA +if t.TYPE_CHECKING: + from . import la # noqa: F401 +NS = ns.PA -class PhysicalFunction(fa.Function): - """A physical function on the Physical Architecture layer.""" - owner: m.Single[PhysicalComponent] - realized_logical_functions = m.TypecastAccessor( - la.LogicalFunction, "realized_functions" +@m.stringy_enum +@enum.unique +class PhysicalComponentKind(enum.Enum): + """Categories of physical components. + + Allows to categorize a physical component, with respect to real life + physical entities. + """ + + UNSET = "UNSET" + """The physical component kind is not specified.""" + HARDWARE = "HARDWARE" + """The physical component is a hardware resource.""" + HARDWARE_COMPUTER = "HARDWARE_COMPUTER" + """The physical component is a computing resource.""" + SOFTWARE = "SOFTWARE" + """The physical component is a software entity.""" + SOFTWARE_DEPLOYMENT_UNIT = "SOFTWARE_DEPLOYMENT_UNIT" + """The physical component is a software deployment unit.""" + SOFTWARE_EXECUTION_UNIT = "SOFTWARE_EXECUTION_UNIT" + """The physical component is a software execution unit.""" + SOFTWARE_APPLICATION = "SOFTWARE_APPLICATION" + """The physical component is a software application.""" + FIRMWARE = "FIRMWARE" + """The physical component is a firmware part.""" + PERSON = "PERSON" + """The physical component is a person.""" + FACILITIES = "FACILITIES" + """The physical component refers to Facilities.""" + DATA = "DATA" + """The physical component represents a set of data.""" + MATERIALS = "MATERIALS" + """The physical component represents a bunch of materials.""" + SERVICES = "SERVICES" + """The physical component represents a set of services.""" + PROCESSES = "PROCESSES" + """The physical component represents a set of processes.""" + + +@m.stringy_enum +@enum.unique +class PhysicalComponentNature(enum.Enum): + """The nature of a physical component.""" + + UNSET = "UNSET" + """The physical component nature is not specified.""" + BEHAVIOR = "BEHAVIOR" + """The physical component nature is behavioral. + + This typically means a piece of software. + """ + NODE = "NODE" + """The physical component is a host for behavioral components. + + This typically means a computing resource. + """ + + +class PhysicalArchitecturePkg(cs.BlockArchitecturePkg): + packages = m.Containment["PhysicalArchitecturePkg"]( + "ownedPhysicalArchitecturePkgs", (NS, "PhysicalArchitecturePkg") + ) + architectures = m.Containment["PhysicalArchitecture"]( + "ownedPhysicalArchitectures", (NS, "PhysicalArchitecture") ) -class PhysicalFunctionPkg(m.ModelElement): - """A logical component package.""" +class PhysicalArchitecture(cs.ComponentArchitecture): + """Provides access to the Physical Architecture layer of the model.""" - _xmltag = "ownedFunctionPkg" + component_pkg = m.Containment["PhysicalComponentPkg"]( + "ownedPhysicalComponentPkg", (NS, "PhysicalComponentPkg") + ) + component_package = m.DeprecatedAccessor["PhysicalComponentPkg"]( + "component_pkg" + ) + deployments = m.Containment["cs.AbstractDeploymentLink"]( + "ownedDeployments", (ns.CS, "AbstractDeploymentLink") + ) + realized_logical_architectures = m.Allocation["la.LogicalArchitecture"]( + "ownedLogicalArchitectureRealizations", + (NS, "LogicalArchitectureRealization"), + (ns.LA, "LogicalArchitecture"), + attr="sourceElement", + backattr="targetElement", + ) - functions = m.Containment("ownedPhysicalFunctions", PhysicalFunction) + @property + def root_component(self) -> PhysicalComponent: + return self.component_pkg.by_is_actor(False, single=True) - packages: m.Accessor - categories = m.DirectProxyAccessor( - fa.ExchangeCategory, aslist=m.ElementList - ) + @property + def all_functions(self) -> m.ElementList[PhysicalFunction]: + return self._model.search((NS, "PhysicalFunction"), below=self) + @property + def all_components(self) -> m.ElementList[PhysicalComponent]: + return self._model.search((NS, "PhysicalComponent"), below=self) -class PhysicalComponent(cs.Component): - """A physical component on the Physical Architecture layer.""" + @property + def all_actors(self) -> m.ElementList[PhysicalComponent]: + return self._model.search((NS, "PhysicalComponent")).by_is_actor(True) - _xmltag = "ownedPhysicalComponents" + @property + def all_function_exchanges(self) -> m.ElementList[fa.FunctionalExchange]: + return self._model.search((ns.FA, "FunctionalExchange"), below=self) - nature = m.EnumPOD( - "nature", modeltypes.PhysicalComponentNature, default="UNSET" - ) - kind = m.EnumPOD("kind", modeltypes.PhysicalComponentKind, default="UNSET") + @property + def all_physical_paths(self) -> m.ElementList[cs.PhysicalPath]: + return self._model.search((ns.CS, "PhysicalPath"), below=self) - allocated_functions = m.Allocation[PhysicalFunction]( - "ownedFunctionalAllocation", - fa.ComponentFunctionalAllocation, - attr="targetElement", - backattr="sourceElement", - ) - realized_logical_components = m.TypecastAccessor( - la.LogicalComponent, - "realized_components", - ) + @property + def all_component_exchanges(self) -> m.ElementList[fa.ComponentExchange]: + return self._model.search((ns.FA, "ComponentExchange"), below=self) - owned_components: m.Accessor - deploying_components: m.Accessor + @property + def all_physical_exchanges(self) -> m.ElementList[fa.FunctionalExchange]: + return self._model.search((ns.FA, "FunctionalExchange"), below=self) @property - def deployed_components( - self, - ) -> m.ElementList[PhysicalComponent]: - items = [ - cmp.type._element - for part in self.parts - for cmp in part.deployed_parts - ] - return m.ElementList(self._model, items, PhysicalComponent) + def all_physical_links(self) -> m.ElementList[cs.PhysicalLink]: + return self._model.search((ns.CS, "PhysicalLink"), below=self) @property - def components(self) -> m.ElementList[PhysicalComponent]: - return self.deployed_components + self.owned_components + def all_functional_chains(self) -> m.ElementList[fa.FunctionalChain]: + return self._model.search((ns.FA, "FunctionalChain"), below=self) + diagrams = m.DiagramAccessor( + "Physical Architecture", cacheattr="_MelodyModel__diagram_cache" + ) -class PhysicalComponentPkg(m.ModelElement): - """A logical component package.""" - _xmltag = "ownedPhysicalComponentPkg" +class PhysicalFunction(fa.AbstractFunction): + """A physical function on the Physical Architecture layer.""" - components = m.DirectProxyAccessor(PhysicalComponent, aslist=m.ElementList) - exchanges = m.DirectProxyAccessor( - fa.ComponentExchange, aslist=m.ElementList + owner = m.Single["PhysicalComponent"]( + m.Backref((NS, "PhysicalComponent"), "allocated_functions") ) - state_machines = m.DirectProxyAccessor( - capellacommon.StateMachine, aslist=m.ElementList + realized_logical_functions = m.DeprecatedAccessor["la.LogicalFunction"]( + "realized_functions" ) - - packages: m.Accessor - exchange_categories = m.DirectProxyAccessor( - fa.ComponentExchangeCategory, aslist=m.ElementList + functions = m.Containment["PhysicalFunction"]( + "ownedPhysicalComponents", (NS, "PhysicalComponent") + ) + packages = m.Containment["PhysicalFunctionPkg"]( + "ownedPhysicalFunctionPkgs", (NS, "PhysicalFunctionPkg") ) -class PhysicalArchitecture(cs.ComponentArchitecture): - """Provides access to the Physical Architecture layer of the model.""" +class PhysicalFunctionPkg(fa.FunctionPkg): + """A logical component package.""" - root_component = m.AttributeMatcherAccessor( - PhysicalComponent, - attributes={"is_actor": False}, - rootelem=PhysicalComponentPkg, + _xmltag = "ownedFunctionPkg" + + functions = m.Containment["PhysicalFunction"]( + "ownedPhysicalFunctions", (NS, "PhysicalFunction") ) - root_function = m.DirectProxyAccessor( - PhysicalFunction, rootelem=PhysicalFunctionPkg + packages = m.Containment["PhysicalFunctionPkg"]( + "ownedPhysicalFunctionPkgs", (NS, "PhysicalFunctionPkg") ) - function_package = m.DirectProxyAccessor(PhysicalFunctionPkg) - component_package = m.DirectProxyAccessor(PhysicalComponentPkg) - capability_package = m.DirectProxyAccessor(la.CapabilityRealizationPkg) - all_functions = m.DeepProxyAccessor( - PhysicalFunction, - aslist=m.ElementList, - rootelem=PhysicalFunctionPkg, +class PhysicalComponent( + cs.AbstractPhysicalArtifact, + cs.Component, + capellacommon.CapabilityRealizationInvolvedElement, + cs.DeployableElement, + cs.DeploymentTarget, +): + _xmltag = "ownedPhysicalComponents" + + kind = m.EnumPOD("kind", PhysicalComponentKind) + nature = m.EnumPOD("nature", PhysicalComponentNature) + deployment_links = m.Containment["cs.AbstractDeploymentLink"]( + "ownedDeploymentLinks", (ns.CS, "AbstractDeploymentLink") ) - all_capabilities = m.DeepProxyAccessor( - la.CapabilityRealization, aslist=m.ElementList + owned_components = m.Containment["PhysicalComponent"]( + "ownedPhysicalComponents", (NS, "PhysicalComponent") ) - all_components = m.DeepProxyAccessor( - PhysicalComponent, aslist=m.ElementList + component_pkgs = m.Containment["PhysicalComponentPkg"]( + "ownedPhysicalComponentPkgs", (NS, "PhysicalComponentPkg") ) - all_actors = property( - lambda self: self._model.search(PhysicalComponent).by_is_actor(True) + deploying_components = m.Backref["PhysicalComponent"]( + (NS, "PhysicalComponent"), "deployed_components" ) - all_function_exchanges = m.DeepProxyAccessor( - fa.FunctionalExchange, - aslist=m.ElementList, - rootelem=[PhysicalFunctionPkg, PhysicalFunction], - ) - all_physical_paths = m.DeepProxyAccessor( - cs.PhysicalPath, - aslist=m.ElementList, - rootelem=PhysicalComponentPkg, - ) - all_component_exchanges = m.DeepProxyAccessor( - fa.ComponentExchange, - aslist=m.ElementList, - rootelem=PhysicalComponentPkg, + realized_logical_components = m.DeprecatedAccessor["la.LogicalComponent"]( + "realized_components" ) + # TODO allocated_functions override + + @property + def deployed_components( + self, + ) -> m.ElementList[PhysicalComponent]: + items = [ + cmp.type._element + for part in self.parts + for cmp in part.deployed_parts + ] + return m.ElementList(self._model, items, PhysicalComponent) - all_physical_exchanges = m.DeepProxyAccessor( - fa.FunctionalExchange, - aslist=m.ElementList, - rootelem=[PhysicalFunctionPkg, PhysicalFunction], + @property + def components(self) -> m.ElementList[PhysicalComponent]: + return self.deployed_components + self.owned_components + + +class PhysicalComponentPkg(cs.ComponentPkg, information.AssociationPkg): + _xmltag = "ownedPhysicalComponentPkg" + + components = m.Containment["PhysicalComponent"]( + "ownedPhysicalComponents", (NS, "PhysicalComponent") ) - all_physical_links = m.DeepProxyAccessor( - cs.PhysicalLink, aslist=m.ElementList + packages = m.Containment["PhysicalComponentPkg"]( + "ownedPhysicalComponentPkgs", (NS, "PhysicalComponentPkg") ) - all_functional_chains = property( - lambda self: self._model.search(fa.FunctionalChain, below=self) + key_parts = m.Containment["information.KeyPart"]( + "ownedKeyParts", (ns.INFORMATION, "KeyPart") ) - - diagrams = m.DiagramAccessor( - "Physical Architecture", cacheattr="_MelodyModel__diagram_cache" + deployments = m.Containment["cs.AbstractDeploymentLink"]( + "ownedDeployments", (ns.CS, "AbstractDeploymentLink") ) - - -la.LogicalComponent.realizing_physical_components = m.Backref( - PhysicalComponent, "realized_logical_components" -) -la.LogicalFunction.realizing_physical_functions = m.Backref( - PhysicalFunction, "realized_logical_functions" -) -PhysicalComponent.deploying_components = m.Backref( - PhysicalComponent, "deployed_components" -) -PhysicalFunction.owner = m.Single( - m.Backref(PhysicalComponent, "allocated_functions") -) -PhysicalFunction.packages = m.DirectProxyAccessor( - PhysicalFunctionPkg, aslist=m.ElementList -) -PhysicalComponent.owned_components = m.DirectProxyAccessor( - PhysicalComponent, aslist=m.ElementList -) -PhysicalComponentPkg.packages = m.DirectProxyAccessor( - PhysicalComponentPkg, aslist=m.ElementList -) -PhysicalFunction.functions = m.DirectProxyAccessor( - PhysicalFunction, aslist=m.ElementList -) -PhysicalFunctionPkg.packages = m.DirectProxyAccessor( - PhysicalFunctionPkg, aslist=m.ElementList -) diff --git a/capellambse/metamodel/re.py b/capellambse/metamodel/re.py new file mode 100644 index 00000000..ad6a9f18 --- /dev/null +++ b/capellambse/metamodel/re.py @@ -0,0 +1,101 @@ +# SPDX-FileCopyrightText: Copyright DB InfraGO AG +# SPDX-License-Identifier: Apache-2.0 +from __future__ import annotations + +import enum + +import capellambse.model as m + +from . import namespaces as ns + +NS = ns.RE + + +@m.stringy_enum +@enum.unique +class CatalogElementKind(enum.Enum): + REC = "REC" + RPL = "RPL" + REC_RPL = "REC_RPL" + GROUPING = "GROUPING" + + +# NOTE: This should actually inherit from eMDE.ecore#ExtensibleElement +class ReAbstractElement(m.ModelElement, abstract=True): + id = m.StringPOD("id") + + +class ReNamedElement(ReAbstractElement, abstract=True): + name = m.StringPOD("name") + + +class ReDescriptionElement(ReNamedElement, abstract=True): + description = m.StringPOD("description") # type: ignore[assignment] + + +class ReElementContainer(m.ModelElement, abstract=True): + elements = m.Containment["CatalogElement"]( + "ownedElements", (NS, "CatalogElement") + ) + + +class CatalogElementPkg(ReNamedElement, ReElementContainer): + element_packages = m.Containment["CatalogElementPkg"]( + "ownedElementPkgs", (NS, "CatalogElementPkg") + ) + + +# NOTE: RecCatalog should also inherit from eMDE.ecore#ElementExtension +class RecCatalog(CatalogElementPkg): + compliancy_definition_pkg = m.Containment["CompliancyDefinitionPkg"]( + "ownedCompliancyDefinitionPkg", (NS, "CompliancyDefinitionPkg") + ) + + +# NOTE: GroupingElementPkg should also inherit from eMDE.ecore#ElementExtension +class GroupingElementPkg(CatalogElementPkg): + pass + + +class CatalogElementLink(ReAbstractElement): + source = m.Association["CatalogElement"]((NS, "CatalogElement"), "source") + # NOTE: `target` should link to EObject + target = m.Association["m.ModelElement"]( + (ns.MODELLINGCORE, "ModelElement"), "target" + ) + origin = m.Association["CatalogElementLink"]( + (NS, "CatalogElementLink"), "origin" + ) + unsynchronized_features = m.StringPOD("unsynchronizedFeatures") + is_suffixed = m.BoolPOD("suffixed") + + +class CatalogElement(ReDescriptionElement, ReElementContainer): + kind = m.EnumPOD("kind", CatalogElementKind) + author = m.StringPOD("author") + environment = m.StringPOD("environment") + suffix = m.StringPOD("suffix") + purpose = m.StringPOD("purpose") + is_read_only = m.BoolPOD("readOnly") + version = m.StringPOD("version") + tags = m.StringPOD("tags") + origin = m.Association["CatalogElement"]((NS, "CatalogElement"), "origin") + current_compliancy = m.Association["CompliancyDefinition"]( + (NS, "CompliancyDefinition"), "currentCompliancy" + ) + default_replica_compliancy = m.Association["CompliancyDefinition"]( + (NS, "CompliancyDefinition"), "defaultReplicaCompliancy" + ) + links = m.Containment["CatalogElementLink"]( + "ownedLinks", (NS, "CatalogElementLink") + ) + + +class CompliancyDefinitionPkg(ReNamedElement): + definitions = m.Containment["CompliancyDefinition"]( + "ownedDefinitions", (NS, "CompliancyDefinition") + ) + + +class CompliancyDefinition(ReDescriptionElement): + pass diff --git a/capellambse/metamodel/sa.py b/capellambse/metamodel/sa.py index be1fe114..509ea213 100644 --- a/capellambse/metamodel/sa.py +++ b/capellambse/metamodel/sa.py @@ -4,154 +4,199 @@ This is normally the place to declare data used in the model for e.g. functions, actors etc. which is best presented in a glossary document. - -.. diagram:: [CDB] SA ORM """ +from __future__ import annotations + +import typing as t + import capellambse.model as m from . import capellacommon, capellacore, cs, fa, interaction from . import namespaces as ns -from . import oa + +if t.TYPE_CHECKING: + from . import la, oa # noqa: F401 NS = ns.SA -class SystemFunction(fa.Function): - """A system function.""" +class SystemAnalysis(cs.ComponentArchitecture): + """Provides access to the SystemAnalysis layer of the model.""" - realized_operational_activities = m.TypecastAccessor( - oa.OperationalActivity, "realized_functions" + component_pkg = m.Containment["SystemComponentPkg"]( + "ownedSystemComponentPkg", (NS, "SystemComponentPkg") + ) + component_package = m.DeprecatedAccessor["SystemComponentPkg"]( + "component_pkg" ) + mission_pkg = m.Containment["MissionPkg"]( + "ownedMissionPkg", (NS, "MissionPkg") + ) + mission_package = m.DeprecatedAccessor["MissionPkg"]("mission_pkg") - owner: m.Accessor + realized_operational_analysis = m.Allocation["oa.OperationalAnalysis"]( + "ownedOperationalAnalysisRealizations", + (NS, "OperationalAnalysisRealization"), + (ns.OA, "OperationalAnalysis"), + attr="targetElement", + backattr="sourceElement", + ) + @property + def root_component(self) -> SystemComponent: + return self.component_pkg.by_is_actor(False, single=True) -class SystemFunctionPkg(m.ModelElement): - """A function package that can hold functions.""" + @property + def all_components(self) -> m.ElementList[SystemComponent]: + return self._model.search((NS, "SystemComponent"), below=self) - _xmltag = "ownedFunctionPkg" + @property + def all_actors(self) -> m.ElementList[SystemComponent]: + return self.all_components.by_is_actor(True) - functions = m.Containment("ownedSystemFunctions", SystemFunction) - packages: m.Accessor - categories = m.DirectProxyAccessor( - fa.ExchangeCategory, aslist=m.ElementList - ) + @property + def all_missions(self) -> m.ElementList[Mission]: + return self._model.search((NS, ", aslist=m.ElementList)"), below=self) + @property + def all_actor_exchanges(self) -> m.ElementList[fa.ComponentExchange]: + return self._model.search( + (ns.FA, "ComponentExchange"), below=self + ).filter( + lambda e: ( + (e.source is not None and e.source.is_actor) + or (e.target is not None and e.target.is_actor) + ) + ) -class SystemComponent(cs.Component): - """A system component.""" + @property + def all_capability_exploitations( + self, + ) -> m.ElementList[CapabilityExploitation]: + return self._model.search((NS, "CapabilityExploitation"), below=self) - _xmltag = "ownedSystemComponents" + @property + def all_component_exchanges(self) -> m.ElementList[fa.ComponentExchange]: + return self._model.search((ns.FA, "ComponentExchange"), below=self) - allocated_functions = m.Allocation[SystemFunction]( - "ownedFunctionalAllocation", - fa.ComponentFunctionalAllocation, - attr="targetElement", - backattr="sourceElement", + diagrams = m.DiagramAccessor( + "System Analysis", cacheattr="_MelodyModel__diagram_cache" + ) + + +class SystemFunction(fa.AbstractFunction): + packages = m.Containment["SystemFunctionPkg"]( + "ownedSystemFunctionPkgs", (NS, "SystemFunctionPkg") ) - realized_entities = m.TypecastAccessor( - oa.Entity, - "realized_components", + realized_operational_activities = m.Alias["oa.OperationalActivity"]( + "realized_functions" ) - realized_operational_entities = m.TypecastAccessor( - oa.Entity, - "realized_components", + owner = m.Single["SystemComponent"]( + m.Backref((NS, "SystemComponent"), "allocated_functions") + ) + realizing_logical_functions = m.Backref["la.LogicalFunction"]( + (ns.LA, "LogicalFunction"), "realized_system_functions" + ) + involved_in = m.Backref["Capability"]( + (NS, "Capability"), "involved_functions" ) -class SystemComponentPkg(m.ModelElement): - """A system component package.""" +class SystemFunctionPkg(fa.FunctionPkg): + """A function package that can hold functions.""" - _xmltag = "ownedSystemComponentPkg" + _xmltag = "ownedFunctionPkg" - components = m.DirectProxyAccessor(SystemComponent, aslist=m.ElementList) - state_machines = m.DirectProxyAccessor( - capellacommon.StateMachine, aslist=m.ElementList + functions = m.Containment["SystemFunction"]( + "ownedSystemFunctions", (NS, "SystemFunction") ) + packages = m.Containment["SystemFunctionPkg"]( + "ownedSystemFunctionPkgs", (NS, "SystemFunctionPkg") + ) + - packages: m.Accessor - exchange_categories = m.DirectProxyAccessor( - fa.ComponentExchangeCategory, aslist=m.ElementList +class SystemCommunicationHook(capellacore.NamedElement): + communication = m.Association["SystemCommunication"]( + (NS, "SystemCommunication"), "communication" ) + type = m.Association["cs.Component"]((ns.CS, "Component"), "type") -class CapabilityInvolvement(interaction.AbstractInvolvement): - """A CapabilityInvolvement.""" +class SystemCommunication(capellacore.Relationship): + ends = m.Containment["SystemCommunicationHook"]( + "ends", (NS, "SystemCommunicationHook") + ) -class Capability(m.ModelElement): - """A capability.""" +class CapabilityInvolvement(capellacore.Involvement): + pass - _xmltag = "ownedCapabilities" - extends = m.DirectProxyAccessor( - interaction.AbstractCapabilityExtend, aslist=m.ElementList +class MissionInvolvement(capellacore.Involvement): + _xmltag = "ownedMissionInvolvements" + + +class Mission(capellacore.NamedElement, capellacore.InvolverElement): + """A mission.""" + + _xmltag = "ownedMissions" + + involvements = m.Containment["MissionInvolvement"]( + "ownedMissionInvolvements", (NS, "MissionInvolvement") ) - extended_by = m.Backref(interaction.AbstractCapabilityExtend, "target") - includes = m.DirectProxyAccessor( - interaction.AbstractCapabilityInclude, aslist=m.ElementList + incoming_involvements = m.Backref(MissionInvolvement, "target") + capability_exploitations = m.Containment["CapabilityExploitation"]( + "ownedCapabilityExploitations", (NS, "CapabilityExploitation") ) - included_by = m.Backref(interaction.AbstractCapabilityInclude, "target") - generalizes = m.DirectProxyAccessor( - interaction.AbstractCapabilityGeneralization, aslist=m.ElementList + exploits = m.Allocation["Capability"]( + "ownedCapabilityExploitations", + (NS, "CapabilityExploitation"), + (NS, "Capability"), + attr="capability", ) - generalized_by = m.Backref( - interaction.AbstractCapabilityGeneralization, "target" + + +class MissionPkg(capellacore.Structure): + """A system mission package that can hold missions.""" + + _xmltag = "ownedMissionPkg" + + packages = m.Containment["MissionPkg"]( + "ownedMissionPkgs", (NS, "MissionPkg") ) - owned_chains = m.DirectProxyAccessor( - fa.FunctionalChain, aslist=m.ElementList + missions = m.Containment["Mission"]("ownedMissions", (NS, "Mission")) + + +class Capability(interaction.AbstractCapability): + _xmltag = "ownedCapabilities" + + owned_chains = m.DeprecatedAccessor["fa.FunctionalChain"]( + "functional_chains" ) - involved_functions = m.Allocation[SystemFunction]( - "ownedAbstractFunctionAbstractCapabilityInvolvements", - interaction.AbstractFunctionAbstractCapabilityInvolvement, - attr="involved", + involvements = m.Containment["CapabilityInvolvement"]( + "ownedCapabilityInvolvements", (NS, "CapabilityInvolvement") ) - involved_chains = m.Allocation[fa.FunctionalChain]( - "ownedFunctionalChainAbstractCapabilityInvolvements", - interaction.FunctionalChainAbstractCapabilityInvolvement, - attr="involved", + component_involvements = m.DeprecatedAccessor["CapabilityInvolvement"]( + "involvements" ) - involved_components = m.Allocation[SystemComponent]( + involved_components = m.Allocation["SystemComponent"]( "ownedCapabilityInvolvements", - CapabilityInvolvement, + (NS, "CapabilityInvolvement"), + (NS, "SystemComponent"), attr="involved", ) - component_involvements = m.DirectProxyAccessor( - CapabilityInvolvement, aslist=m.ElementList + incoming_exploitations = m.Backref["CapabilityExploitation"]( + (NS, "CapabilityExploitation"), "capability" ) - realized_capabilities = m.Allocation[oa.OperationalCapability]( - None, # FIXME fill in tag - interaction.AbstractCapabilityRealization, - attr="targetElement", - ) - - postcondition = m.Single( - m.Association(capellacore.Constraint, "postCondition") - ) - precondition = m.Single( - m.Association(capellacore.Constraint, "preCondition") - ) - scenarios = m.DirectProxyAccessor( - interaction.Scenario, aslist=m.ElementList - ) - states = m.Association(capellacommon.State, "availableInStates") - - packages: m.Accessor -class MissionInvolvement(interaction.AbstractInvolvement): - """A MissionInvolvement.""" - - _xmltag = "ownedMissionInvolvements" - - -class CapabilityExploitation(m.ModelElement): - """A CapabilityExploitation.""" - +class CapabilityExploitation(capellacore.Relationship): _xmltag = "ownedCapabilityExploitations" - capability = m.Single(m.Association(Capability, "capability")) + capability = m.Single["Capability"]( + m.Association((NS, "Capability"), "capability") + ) @property def name(self) -> str: # type: ignore[override] @@ -163,129 +208,53 @@ def name(self) -> str: # type: ignore[override] return f"[{self.__class__.__name__}]{direction}" -class Mission(m.ModelElement): - """A mission.""" - - _xmltag = "ownedMissions" +class CapabilityPkg(capellacommon.AbstractCapabilityPkg): + _xmltag = "ownedAbstractCapabilityPkg" - involvements = m.DirectProxyAccessor( - MissionInvolvement, aslist=m.ElementList + capabilities = m.Containment["Capability"]( + "ownedCapabilities", (NS, "Capability") ) - incoming_involvements = m.Backref(MissionInvolvement, "target") - exploits = m.Allocation[Capability]( - None, # FIXME fill in tag - CapabilityExploitation, - attr="capability", - ) - exploitations = m.DirectProxyAccessor( - CapabilityExploitation, aslist=m.ElementList + packages = m.Containment["CapabilityPkg"]( + "ownedCapabilityPkgs", (NS, "CapabilityPkg") ) -class MissionPkg(m.ModelElement): - """A system mission package that can hold missions.""" +class OperationalAnalysisRealization(cs.ArchitectureAllocation): + pass - _xmltag = "ownedMissionPkg" - - missions = m.DirectProxyAccessor(Mission, aslist=m.ElementList) - packages: m.Accessor - - -class CapabilityPkg(m.ModelElement): - """A capability package that can hold capabilities.""" - - _xmltag = "ownedAbstractCapabilityPkg" - - capabilities = m.DirectProxyAccessor(Capability, aslist=m.ElementList) - packages: m.Accessor - - -class SystemAnalysis(cs.ComponentArchitecture): - """Provides access to the SystemAnalysis layer of the model.""" +class SystemComponentPkg(cs.ComponentPkg): + _xmltag = "ownedSystemComponentPkg" - root_component = m.AttributeMatcherAccessor( - SystemComponent, - attributes={"is_actor": False}, - rootelem=SystemComponentPkg, + components = m.Containment["SystemComponent"]( + "ownedSystemComponents", (NS, "SystemComponent") ) - root_function = m.DirectProxyAccessor( - SystemFunction, rootelem=SystemFunctionPkg + packages = m.Containment["SystemComponentPkg"]( + "ownedSystemComponentPkgs", (NS, "SystemComponentPkg") ) - function_package = m.DirectProxyAccessor(SystemFunctionPkg) - capability_package = m.DirectProxyAccessor(CapabilityPkg) - component_package = m.DirectProxyAccessor(SystemComponentPkg) - mission_package = m.DirectProxyAccessor(MissionPkg) - all_functions = m.DeepProxyAccessor(SystemFunction, aslist=m.ElementList) - all_capabilities = m.DeepProxyAccessor(Capability, aslist=m.ElementList) - all_components = m.DeepProxyAccessor(SystemComponent, aslist=m.ElementList) - all_actors = property( - lambda self: self._model.search(SystemComponent).by_is_actor(True) - ) - all_missions = m.DeepProxyAccessor(Mission, aslist=m.ElementList) - all_functional_chains = property( - lambda self: self._model.search(fa.FunctionalChain, below=self) - ) +class SystemComponent(cs.Component, capellacore.InvolvedElement): + _xmltag = "ownedSystemComponents" - actor_exchanges = m.DirectProxyAccessor( - fa.ComponentExchange, - aslist=m.ElementList, - rootelem=SystemComponentPkg, - ) - component_exchanges = m.DeepProxyAccessor( - fa.ComponentExchange, - aslist=m.ElementList, - rootelem=[SystemComponentPkg, SystemComponent], + components = m.Containment["SystemComponent"]( + "ownedSystemComponents", (NS, "SystemComponent") ) - - all_capability_exploitations = m.DeepProxyAccessor( - CapabilityExploitation, aslist=m.ElementList + packages = m.Containment["SystemComponentPkg"]( + "ownedSystemComponentPkgs", (NS, "SystemComponentPkg") ) - all_function_exchanges = m.DeepProxyAccessor( - fa.FunctionalExchange, - aslist=m.ElementList, - rootelem=[SystemFunctionPkg, SystemFunction], + is_data_component = m.BoolPOD("dataComponent") + data_type = m.Single["capellacore.Classifier"]( + m.Association((ns.CAPELLACORE, "Classifier"), "dataType") ) - all_component_exchanges = m.DeepProxyAccessor( - fa.ComponentExchange, aslist=m.ElementList + allocated_functions = m.Allocation[SystemFunction]( + "ownedFunctionalAllocation", + fa.ComponentFunctionalAllocation, + attr="targetElement", + backattr="sourceElement", ) - - diagrams = m.DiagramAccessor( - "System Analysis", cacheattr="_MelodyModel__diagram_cache" + realized_entities = m.Alias["oa.Entity"]("realized_components") + realized_operational_entities = m.Alias["oa.Entity"]("realized_components") + realizing_logical_components = m.Backref["la.LogicalComponent"]( + (ns.LA, "LogicalComponent"), "realized_components" ) - - -SystemFunction.owner = m.Single( - m.Backref(SystemComponent, "allocated_functions") -) -SystemFunction.packages = m.DirectProxyAccessor( - SystemFunctionPkg, aslist=m.ElementList -) -oa.OperationalCapability.realizing_capabilities = m.Backref( - Capability, "realized_capabilities" -) -Capability.incoming_exploitations = m.Backref( - CapabilityExploitation, "capability" -) -oa.Entity.realizing_system_components = m.Backref( - SystemComponent, "realized_operational_entities" -) -oa.OperationalActivity.realizing_system_functions = m.Backref( - SystemFunction, "realized_operational_activities" -) -SystemFunction.involved_in = m.Backref(Capability, "involved_functions") -MissionPkg.packages = m.DirectProxyAccessor(MissionPkg, aslist=m.ElementList) -SystemComponent.components = m.DirectProxyAccessor( - SystemComponent, aslist=m.ElementList -) -SystemComponentPkg.packages = m.DirectProxyAccessor( - SystemComponentPkg, aslist=m.ElementList -) -SystemFunction.functions = m.DirectProxyAccessor( - SystemFunction, aslist=m.ElementList -) -SystemFunctionPkg.packages = m.DirectProxyAccessor( - SystemFunctionPkg, aslist=m.ElementList -) diff --git a/capellambse/metamodel/sharedmodel.py b/capellambse/metamodel/sharedmodel.py new file mode 100644 index 00000000..4864bf29 --- /dev/null +++ b/capellambse/metamodel/sharedmodel.py @@ -0,0 +1,33 @@ +# SPDX-FileCopyrightText: Copyright DB InfraGO AG +# SPDX-License-Identifier: Apache-2.0 +from __future__ import annotations + +import typing as t + +import capellambse.model as m + +from . import capellacore, capellamodeller +from . import namespaces as ns + +if t.TYPE_CHECKING: + from . import information # noqa: F401 + +NS = ns.SHARED_MODEL + + +class SharedPkg(capellacore.ReuseableStructure, capellamodeller.ModelRoot): + data_pkg = m.Containment["information.DataPkg"]( + "ownedDataPkg", (ns.INFORMATION, "DataPkg") + ) + generic_pkg = m.Containment["GenericPkg"]( + "ownedGenericPkg", (NS, "GenericPkg") + ) + + +class GenericPkg(capellacore.Structure): + packages = m.Containment["GenericPkg"]( + "subGenericPkgs", (NS, "GenericPkg") + ) + capella_elements = m.Containment["capellacore.CapellaElement"]( + "capellaElements", (ns.CAPELLACORE, "CapellaElement") + ) diff --git a/capellambse/model/_obj.py b/capellambse/model/_obj.py index 6840d3f3..6f60ef4a 100644 --- a/capellambse/model/_obj.py +++ b/capellambse/model/_obj.py @@ -52,6 +52,9 @@ from . import VIRTUAL_NAMESPACE_PREFIX, T, U, _descriptors, _pods, _styleclass +if t.TYPE_CHECKING: + import capellambse.metamodel as mm + LOGGER = logging.getLogger(__name__) CORE_VIEWPOINT = "org.polarsys.capella.core.viewpoint" @@ -538,11 +541,19 @@ class ModelElement(metaclass=_ModelElementMeta): ) parent = _descriptors.ParentAccessor() - constraints: _descriptors.Accessor[ElementList[t.Any]] - property_values: _descriptors.Accessor[ElementList[t.Any]] - property_value_groups: _descriptors.Accessor[ElementList[t.Any]] - applied_property_values: _descriptors.Accessor[ElementList[t.Any]] - applied_property_value_groups: _descriptors.Accessor[ElementList[t.Any]] + constraints: _descriptors.Containment[mm.capellacore.Constraint] + property_values: _descriptors.Containment[ + mm.capellacore.AbstractPropertyValue + ] + property_value_groups: _descriptors.Containment[ + mm.capellacore.PropertyValueGroup + ] + applied_property_values: _descriptors.Association[ + mm.capellacore.AbstractPropertyValue + ] + applied_property_value_groups: _descriptors.Association[ + mm.capellacore.PropertyValueGroup + ] _required_attrs = frozenset({"uuid", "xtype"}) _xmltag: str | None = None @@ -1986,7 +1997,8 @@ def wrap_xml( """ try: cls = model.resolve_class(element) - except (UnknownNamespaceError, MissingClassError): + except (UnknownNamespaceError, MissingClassError) as err: + LOGGER.warning("Current metamodel is incomplete: %s", err) cls = ModelElement if type is not None: diff --git a/pyproject.toml b/pyproject.toml index a0bfb7f6..6f13b47c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -124,7 +124,9 @@ modellingcore = "capellambse.metamodel.namespaces:MODELLINGCORE" oa = "capellambse.metamodel.namespaces:OA" pa = "capellambse.metamodel.namespaces:PA" pa_deployment = "capellambse.metamodel.namespaces:PA_DEPLOYMENT" +re = "capellambse.metamodel.namespaces:RE" sa = "capellambse.metamodel.namespaces:SA" +shared_model = "capellambse.metamodel.namespaces:SHARED_MODEL" capellarequirements = "capellambse.extensions.reqif:CapellaRequirementsNS" requirements = "capellambse.extensions.reqif:RequirementsNS" diff --git a/tests/test_decl.py b/tests/test_decl.py index f00f9e63..5011c5a9 100644 --- a/tests/test_decl.py +++ b/tests/test_decl.py @@ -312,21 +312,25 @@ def test_promises_are_resolved_during_the_second_attempt( - parent: !uuid {PHYS_COMPONENT} extend: physical_links: - - name: My new link + - _type: PhysicalLink + name: My new link exchanges: - !promise my_exchange - parent: !uuid {PHYS_COMPONENT} extend: exchanges: - - source: !promise first-port + - _type: FunctionalExchange + source: !promise first-port target: !promise second-port promise_id: my_exchange - parent: !uuid {PHYS_COMPONENT} extend: ports: - - name: First port + - _type: PhysicalPort + name: First port promise_id: first-port - - name: Second port + - _type: PhysicalPort + name: Second port promise_id: second-port """ diff --git a/tests/test_model_creation_deletion.py b/tests/test_model_creation_deletion.py index 312fd1c4..792ec14c 100644 --- a/tests/test_model_creation_deletion.py +++ b/tests/test_model_creation_deletion.py @@ -24,20 +24,7 @@ def model(): return m.MelodyModel(TEST_ROOT / TEST_MODEL) -def test_DirectProxyAccessor_created_elements_can_be_accessed_in_model( - model: m.MelodyModel, -): - parent = model.la.root_component - assert isinstance(type(parent).components, m.DirectProxyAccessor) - - newobj = parent.components.create(name="TestComponent") - - assert newobj is not None - assert isinstance(newobj, mm.la.LogicalComponent) - assert newobj in model.la.root_component.components - - -def test_Containment_created_elements_can_be_accessed_in_model( +def test_created_elements_can_be_accessed_in_model( model: m.MelodyModel, ): parent = model.by_uuid("df30d27f-0efb-4896-b6b6-0757145c7ad5")