class Layer(PASSProcessModelElement): """ A superclass for all layers in a PASS process model. A layer is an abstract definition of a model. :version: 2015-12-07 :author: Kai Hartung & Lukas Block """ """ ATTRIBUTES See definition in ontology. Exactly one reference is given! belongsTo (public) See definition in ontology hasModelComponent (public) Is a property - Returns all message exchanges that are assigned by has model component. See documentation MessageExchange. messageExchanges (public) Is a property - Returns all ActiveProcessComponents that are assigned to this layer by hasModelComponent. See ActiveProcessDocumentation documentation for further information. activeComponents (public) Is a property - Returns all the Behavior classes that are assigned to this lyer via hasModelComponent. For further information see the Behavior documentation. behaviors (public) Is a property - Returns all Subjects that are assigned to this layer by hasModelComponent. See Subject documentation for further information. subjects (public) Is a property - Returns all ExternalSubjects that are assigned to this layer by hasModelComponent. See ExternalSubject documentation for further information. externalSubjects (public) """ def __init__(self, manager, uri = None, isBlank = False, blankNodeId = None, parentModel = None): """ Constructor - Should never be called directly from outside the model framwork. @param ModelManager manger : The parent element (if one exists). @param string uri : @param bool isBlank : @param string blankNodeId : @return : @author """ PASSProcessModelElement.__init__(self, manager, uri, isBlank, blankNodeId) if((parentModel is not None) and (not isinstance(parentModel, PASSProcessModel))): raise Exception("ParentModel parameter must be of type PASSProcessModel!") self.belongsTo = parentModel self.hasModelComponent = ListenerList([], self, "hasModelComponent") def getAttrMultiplicity(self, attributeName): if(attributeName == "belongsTo"): AttributeMultiplicity.UNIQUE else: return PASSProcessModelElement.getAttrMultiplicity(self, attributeName) @property def messageExchanges(self): result = [] for c in self.hasModelComponent: if (isinstance(c, MessageExchange)): result.append(c) return result @property def activeComponents(self): result = [] for c in self.hasModelComponent: if (isinstance(c, ActiveProcessComponent)): result.append(c) return result @property def behaviors(self): result = [] for c in self.hasModelComponent: if (isinstance(c, Behavior)): result.append(c) return result @property def subjects(self): result = [] for c in self.hasModelComponent: if (isinstance(c, Subject)): result.append(c) return result @property def externalSubjects(self): result = [] for c in self.hasModelComponent: if (isinstance(c, ExternalSubject)): result.append(c) return result @property def activeProcessComponents(self): result = [] for c in self.hasModelComponent: if (isinstance(c, ActiveProcessComponent)): result.append(c) return result def addSubject(self, behaviorToAssign = None): """ Adds a new subject to this layer. @param Behavior behaviorToAssign : The behavior that should be assigned to the subject. @return Subject : @author """ if((behaviorToAssign is not None) and (not isinstance(behaviorToAssign, Behavior))): raise Exception("'BehaviorToAssign' must be of type Behavior!") if(behaviorToAssign is None): behaviorToAssign = self.addBehavior() newSubject = Subject(self.modelManager, behavior=behaviorToAssign) self.hasModelComponent.append(newSubject) return newSubject def addExternalSubject(self, uri): """ @param short uri : Uri where this process modell is stored. @return ExternalSubject : @author """ if(not isinstance(uri, str)): raise Exception("'Uri' must be of type string!") newExSub = ExternalSubject(self.modelManager, referenceUri = uri) self.hasModelComponent.append(newExSub) return newExSub def removeActiveComponent(self, componentToRemove, removeBehavior = True): """ @param ActiveProcessComponent componentToRemove : The subject, external subject, ... to remove from this layer @param bool removeBehavior : Tries to also delete the behavior of this active component, if it is of type subject. @return : @author """ if(not isinstance(componentToRemove, ActiveProcessComponent)): raise Exception("'Uri' must be of type Behavior!") if(not isinstance(removeBehavior, bool)): raise Exception("'removeBehavior' must be of type bool!") #Remove incident message exchanges for e in self.messageExchanges: if((e.sender is componentToRemove) or (e.receiver is componentToRemove)): self.removeMessageExchange(e) #Remove behavior if requested if((removeBehavior) and isinstance(componentToRemove, Actor)): behaviorToRemove = componentToRemove.hasBehavior self.removeBehavior(behaviorToRemove) #Remove process element self.hasModelComponent.remove(componentToRemove) def addMessageExchange(self, sender, receiver, messageType = None): """ Adds a new message exchange to this layer. @param Actor sender : The sender of the message exchange @param Actor receiver : The receiver of this message exchange @param MessageType messageType : The type of the message this message exchanges should be of. If none is given a new message type is created. @return MessageExchange : @author """ if(not isinstance(sender, ActiveProcessComponent)): raise Exception("'Sender' must be of type Actor!") if(not isinstance(receiver, ActiveProcessComponent)): raise Exception("'Receiver' must be of type Actor!") if((messageType is not None) and (not isinstance(messageType, MessageType))): raise Exception("'MessageType' must be of type MessageType!") if(messageType is None): messageType = MessageType(self.modelManager) newExchange = MessageExchange(self.modelManager, sender = sender, receiver = receiver, messageType = messageType) self.hasModelComponent.append(newExchange) return newExchange def removeMessageExchange(self, messageExchange): """ Invers function of addMessageExchange - Removes a message exchange from this layer. @param MessageExchange messageExchange : The messageExchange to remove from this layer @return : @author """ if(not isinstance(messageExchange, MessageExchange)): raise Exception("'MessageExchange' must be of type MessageExchange!") self.hasModelComponent.remove(messageExchange) def addBehavior(self): """ Function to add a new Behavior to this layer and to directly assign it to an actor, if needed. @param Actor asignToActor : The actor that should have this behavior. @return Behavior : @author """ newBehavior = Behavior(self.modelManager) self.hasModelComponent.append(newBehavior) return newBehavior def removeBehavior(self, behaviorToRemove): """ Invers function of addBehavior. Removes a behavior from this layer and (if it was assigned to one) also from the actor. @param Behavior behaviorToRemove : The behavior that should be removed from this layer. @return : @author """ if(not isinstance(behaviorToRemove, Behavior)): raise Exception("BehaviorToRemove must be of type Behavior! Actual item: {}".format(behaviorToRemove)) for a in self.activeComponents: if(isinstance(a, Actor) and (a.hasBehavior is behaviorToRemove)): a.hasBehavior = None print("WARNING! Behavior was removed that belonged to a subject!") self.hasModelComponent.remove(behaviorToRemove) def duplicateActiveProcessComponent(self, component): """ Duplicates a given active process component and adds it again to this layer. @param ActiveProcessComponent component : The component to duplicate. @return : @author """ result = deepcopy(component) self.hasModelComponent.append(result) return result def getBoundingBox2D(self): """ Returns the bounding box of all active process elements in their 2D coordinate system. @return : @author """ #Helper variables maxX = float("-inf") maxY = float("-inf") minX = float("inf") minY = float("inf") #Now iterate over all active process components for active in (self.activeProcessComponents + self.messageExchanges): if(hasattr(active, "hasAbstractVisualRepresentation")): point = active.hasAbstractVisualRepresentation.getPoint2D() #Max tests if(maxX < point[0]): maxX = point[0] if(maxY < point[1]): maxY = point[1] #Min tests if(minX > point[0]): minX = point[0] if(minY > point[1]): minY = point[1] #inf tests if(math.isinf(maxX)): maxX = 0 minX = 0 if(math.isinf(maxY)): maxY = 0 minY = 0 return [[minX, minY], [maxX, maxY]] def getBoundingBox3D(self): """ Returns the bounding box of all active process elements in their 3D coordinate system. @return : @author """ #Helper variables maxX = float("-inf") maxY = float("-inf") maxZ = float("-inf") minX = float("inf") minY = float("inf") minZ = float("inf") #Now iterate over all active process components for active in (self.activeProcessComponents + self.messageExchanges): if(hasattr(active, "hasAbstractVisualRepresentation")): point = active.hasAbstractVisualRepresentation.getPoint3D() #Max tests if(maxX < point[0]): maxX = point[0] if(maxY < point[1]): maxY = point[1] if(maxZ < point[2]): maxZ = point[2] #Min tests if(minX > point[0]): minX = point[0] if(minY > point[1]): minY = point[1] if(minZ > point[2]): minZ = point[2] #inf tests if(math.isinf(maxX)): maxX = 0 minX = 0 if(math.isinf(maxY)): maxY = 0 minY = 0 if(math.isinf(maxZ)): maxZ = 0 minZ = 0 return [[minX, minY, minZ], [maxX, maxY, maxZ]]
class Behavior(PASSProcessModelElement): """ A behavior is a state diagram that defines how an subject reacts when it receives or sends messages. :version: 2015-12-07 :author: Kai Hartung & Lukas Block """ """ ATTRIBUTES All edges of this behavior description. Variable is defined by ontology. hasEdge (public) All states of this behavior description. Variable is defined by ontology. hasState (public) """ def __init__(self, manager, uri=None, isBlank=False, blankNodeId=None): """ Constructor - Should never be called directly from outside the model framwork. @param ModelManager manger : The parent element (if one exists). @param string uri : @param bool isBlank : @param string blankNodeId : @return : @author """ PASSProcessModelElement.__init__(self, manager, uri, isBlank, blankNodeId) self.hasEdge = ListenerList([], self, "hasEdge") self.hasState = ListenerList([], self, "hasState") self._fireChangeEvents = True self.fireChangeEvent() def addFunctionState(self): """ Adds a state of type function to this Behavior. @return : @author """ newFunc = FunctionState(self.modelManager) self.hasState.append(newFunc) #Check if we should set the initial state if (len(self.hasState) == 1): self.setInitialState(newFunc) return newFunc def addReceiveState(self): """ Adds a state of type receive to this Behavior. @return : @author """ newS = ReceiveState(self.modelManager) self.hasState.append(newS) #Check if we should set the initial state if (len(self.hasState) == 1): self.setInitialState(newS) return newS def addSendState(self): """ Adds a state of type send to this Behavior. @return : @author """ newS = SendState(self.modelManager) self.hasState.append(newS) #Check if we should set the initial state if (len(self.hasState) == 1): self.setInitialState(newS) return newS def removeState(self, state): """ Removes a state from the current behavior. @param State state : The state to remove. @return : @author """ if (not isinstance(state, State)): raise Exception("The state must be of type State!") #Delete incident edges tempEdges = list(self.hasEdge) for e in tempEdges: if ((e.hasSourceState is state) or (e.hasTargetState is state)): self.removeTransition(e) #Delete state self.hasState.remove(state) #Check if it was the initial state - if yes set another initial state if there are other states if (state.isInitialState and (len(self.hasState) > 0)): self.setInitialState(self.hasState[0]) def addStandardTransition(self, from_, to): """ Adds a standard transtition to the behavior. @param FunctionState from : The function state the transition should start at. @param State to : The target state of the transition @return StandardTransition : @author """ if (not isinstance(from_, FunctionState)): raise Exception("'From' must be of type FunctionState!") if (not isinstance(to, State)): raise Exception("'To' must be of type State!") newT = StandardTransition(self.modelManager, sourceState=from_, targetState=to) self.hasEdge.append(newT) return newT def addReceiveTransition(self, from_, to, refersTo): """ Adds a receiving transition to the receive state. @param ReceiveState from : The start state of the transition @param State to : The target state of the transition. @param MessageExchange refersTo : The message exchange this transition should refer to. @return ReceiveTransition : @author """ if (not isinstance(from_, ReceiveState)): raise Exception("'From' must be of type ReceiveState!") if (not isinstance(to, State)): raise Exception("'To' must be of type State!") if (not isinstance(refersTo, MessageExchange)): raise Exception("'RefersTo' must be of type MessageExchange!") if (self.getParent(ActiveProcessComponent) != refersTo.receiver): raise Exception( "The MessageExchange that should be done must be start at the subject assigned to this Behavior!" ) #Now create the transition newT = ReceiveTransition(self.modelManager, sourceState=from_, targetState=to, refersTo=refersTo) self.hasEdge.append(newT) return newT def addSendTransition(self, from_, to, refersTo): """ Adds a sending transition between the given states. @param SendState from : The send state the transition starts at. @param State to : The target state of the transition. @param MessageExchange refersTo : The message exchange this transition should refer to. @return SendTransition : @author """ if (not isinstance(from_, SendState)): raise Exception("'From' must be of type ReceiveState!") if (not isinstance(to, State)): raise Exception("'To' must be of type State!") if (not isinstance(refersTo, MessageExchange)): raise Exception("'RefersTos' must be of type MessageExchange!") if (self.getParent(ActiveProcessComponent) != refersTo.sender): raise Exception( "The MessageExchange that should be done must be start at the subject assigned to this Behavior!" ) #Now create the transition newT = SendTransition(self.modelManager, sourceState=from_, targetState=to, refersTo=refersTo) self.hasEdge.append(newT) return newT def removeTransition(self, transitionToRemove): """ Removes an existing transition. @param TransitionEdge transitionToRemove : The transition edge to remove. @return : @author """ if (not isinstance(transitionToRemove, TransitionEdge)): raise Exception( "The transitionToRemove must be of type TransitionEdge!") self.hasEdge.remove(transitionToRemove) def setInitialState(self, state): """ Sets the initail state (= the state where the behavior automata starts). Only one state can be of type initial state. A state is set to an initial state by manipulating the rdfs:type variable. @param State state : The state that should be used to start the actor's behavior. @return : @author """ #Variable for the initial state initState = "http://www.purl.org/s-scm-ont#InitialState" for eState in self.hasState: if (eState.isInitialState): #Remove current inital state eState.type.remove(initState) if (eState is state): #Set to initial state eState.type.append(initState) def duplicateState(self, state): """ Duplicates a given state and adds it again to this behavior. @param State state : The state to duplicate. @return : @author """ result = deepcopy(state) self.hasState.append(result) return result def getBoundingBox2D(self): """ Returns the bounding box of all state elements in their 2D coordinate system. @return : @author """ #Helper variables maxX = float("-inf") maxY = float("-inf") minX = float("inf") minY = float("inf") #Now iterate over all active process components for active in (self.hasState + self.hasEdge): if (hasattr(active, "hasAbstractVisualRepresentation")): point = active.hasAbstractVisualRepresentation.getPoint2D() #Max tests if (maxX < point[0]): maxX = point[0] if (maxY < point[1]): maxY = point[1] #Min tests if (minX > point[0]): minX = point[0] if (minY > point[1]): minY = point[1] #inf tests if (math.isinf(maxX)): maxX = 0 minX = 0 if (math.isinf(maxY)): maxY = 0 minY = 0 return [[minX, minY], [maxX, maxY]] def getBoundingBox3D(self): """ Returns the bounding box of all state elements in their 3D coordinate system. @return : @author """ #Helper variables maxX = float("-inf") maxY = float("-inf") maxZ = float("-inf") minX = float("inf") minY = float("inf") minZ = float("inf") #Now iterate over all active process components for active in (self.hasState + self.hasEdge): if (hasattr(active, "hasAbstractVisualRepresentation")): point = active.hasAbstractVisualRepresentation.getPoint3D() #Max tests if (maxX < point[0]): maxX = point[0] if (maxY < point[1]): maxY = point[1] if (maxZ < point[2]): maxZ = point[2] #Min tests if (minX > point[0]): minX = point[0] if (minY > point[1]): minY = point[1] if (minZ > point[2]): minZ = point[2] #inf tests if (math.isinf(maxX)): maxX = 0 minX = 0 if (math.isinf(maxY)): maxY = 0 minY = 0 if (math.isinf(maxZ)): maxZ = 0 minZ = 0 return [[minX, minY, minZ], [maxX, maxY, maxZ]]
class PASSProcessModelElement(Resource): """ This class is the superclass for all Elements in a PASS process. It is responsible for their representation, their meta data and their linking. :version: 2015-12-04 :author: Lukas Block """ """ ATTRIBUTES Is a property so in fact returns the type of PASSProcessElement-subclass as a string. Uses ClassMapper and rdfs:type property to determine own class type or python's instance of. classType (public) See definition of hasComponentID in ontologie hasComponentID (public) Property - onyl get is allowed here. Returns the parent instance of this object. The parent instance is the instance that is the next upper class in the visual hierachy. Might be null! parent (public) The label should be uses to display itself to the outside. Might contain different languages. For all ActiveProcessComponents, MessageTypes and all states this label is also the words displayed on it. label (public) Represents all meta content this element has. The array contains the different MetaContent instances with their keys and values. A key isn't secured to be unique. Better use the getMetaContent or setMetaContant function. hasMetaContent (public) Links to a Visual Reperesentation object. See documentation of visual representation for further reference. hasVisualRepresentation (public) Reference to an abstract visual representation that only shows the positions of the elements. See AbstractVisualRepresentation for further reference. hasAbstractVisualRepresentation (public) """ def __init__(self, manager, uri = None, isBlank = False, blankNodeId = None): """ Constructor including the parent element @param ModelManager manger : The parent element (if one exists). @param string uri : @param bool isBlank : @param string blankNodeId : @param PASSProcessModelElement : The hierarchical parent of this process element. @return : @author """ #Call super constructor Resource.__init__(self, manager, uri, isBlank, blankNodeId) #Generate the component id and the empty array for the meta content self.hasComponentID = randomXMLName() self.hasMetaContent = ListenerList([], self, "hasMetaContent") self.hasAbstractVisualRepresentation = None self.hasVisualRepresentation = None self.label = ListenerList([], self, "label") @property def uri(self): return self.modelManager.getBaseUri() + "#" + type(self).__name__ + "-" + self.hasComponentID @uri.setter def uri(self, value): print("WARNING! URI cannot be set for a PASSProcessModelElement!") @property def classType(self): type(self).__name__ @property def parent(self): return self.getParent() def getAttrMultiplicity(self, attributeName): if(attributeName == "hasComponentID"): return AttributeMultiplicity.UNIQUE if(attributeName == "hasAbstractVisualRepresentation"): return AttributeMultiplicity.UNIQUE else: return Resource.getAttrMultiplicity(self, attributeName) def setMetaContent(self, key, value, override = True): """ Sets a new meta content object or updates a given one. @param string key : The key of the meta content to set. If the key exists the override parameter specifies what to do. Otherwise a new MetaContent instance will be created and appended to hasMetaContent. @param undef value : The value of the meta content. Can be of any type that is specified in XMLLiteral. @param bool override : Determines if the current value should be overriden by the given one, if a MetaContent instance with the given key already exists (default behavior). If set to false a new isntance will be created and two meta content objects will exist now. @return : @author """ for element in self.hasMetaContent: if(element.hasKey == key): if (override): element.hasValue = value #Now terminate return else: #Otherwise stop iterating, because we for sure must create a new one break #If not already terminated, we have to insert a new one self.hasMetaContent.append(MetaContent(self.modelManager, key, value)) #No need to fire a change event beacause it is automatically fired by the newly created or the changed one. def getMetaContent(self, key): """ Returns an array of XMLLiterals containing all values that belong to MetaContent instances with this key. As Meta Content keys do not need to be unique, but should be, the array normally has the size 1. @param string key : The key for which the MetaContent should be returned. @return : @author """ results = [] #Iterate over all meta content and add it to the list for value in self.hasMetaContent: if(value.hasKey == key): results.append(value.hasValue) return results def getMetaKeys(self): """ Function that returns all registered meta content keys. @return : @author """ results = [] for value in self.hasMetaContent: if(not value.hasKey in results): results.append(value.hasKey) return results def removeMetaContent(self, key, onlyFirst = False): """ Function that removes all registered meta content with that key. @return : @author """ for content in self.hasMetaContent: if(content.hasKey == key): self.hasMetaContent.remove(content) #Stop deleting if we only want to delete the first occurence if(onlyFirst): break def _uniqueAttr(self): result = Resource._uniqueAttr(self) result["hasComponentID"] = randomXMLName() return result