123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- Modelling
- =========
- Modelling is the core activity in the Modelverse.
- A model is based on a meta-model, or a language, which, for the sake of this tutorial, we assume exists already.
- Throughout this tutorial, we will focus on how to instantiate a simple Petri Net model.
- We assume that our language has a notion of *Place*, *Transition*, *P2T* arc, and *T2P* arc.
- The *Place* has a *name* and *tokens*, the *Transition* has a *name*.
- Both the *P2T* and *T2P* arcs have a *weight*.
- This information is stored in the metamodel, which we will define later on.
- For now, assume that the metamodel is stored in the Modelverse as *formalisms/PetriNets*.
- Model Management
- ----------------
- Modellers have access to a variety of model management operations, making it possible to create, read, update, and delete complete models.
- Create
- ^^^^^^
- Creating a new instance of PetriNets is simple, and can be done using the *model_add* operation of the wrapper::
- >>> model_add("models/my_pn", "formalisms/PetriNets")
- The first parameter of the operation indicates the name that we would like to give to our newly created model.
- This name is available to all users, and is therefore a unique identifier.
- Note that this operation creates a new model, and therefore the target location should be writable, as otherwise a *PermissionDenied* exception is raised.
- The second parameter is the metamodel that we want to use to conform to.
- The value needs to be a name that is available in the Modelverse, and readable to the current user.
- Read
- ^^^^
- To get a list of currently available models, users can query the Modelverse with the *model_list* operation::
- >>> model_list("")
- ["formalisms/", "models/", "administration/", "users/", "type mappings/"]
- This list contains the various entries in the specified location.
- Note that this operation does not specify anything about the permissions of the supplied entries.
- Entries that end with a forward slash (/) are folders, and can subsequently be listed (with or without trailing forward slash)::
- >>> model_list("formalisms")
- ["Bottom", "SimpleClassDiagrams", "Tracability", ...]
- >>> model_list("formalisms/")
- ["Bottom", "SimpleClassDiagrams", "Tracability", ...]
- >>> model_list("users/admin")
- []
- Depending on permissions, not all folders can be listed::
-
- >>> model_list("administration")
- PermissionDenied("administration")
- Update
- ^^^^^^
- Models can be updated in the sense that they can be moved.
- This is the operation *model_move*, which takes a model and moves it to the specified location.
- When invoked on a folder, the complete folder, including all subfolders, is moved to the new location.
- There is no output to this operation::
- >>> model_move("models/my_pn", "models/my_new_pn")
- Delete
- ^^^^^^
- Finally, models can be deleted using the *model_delete* operations.
- Model deletion is permanent and cannot be reverted::
- >>> model_delete("models/my_pn")
- Modifying a model
- -----------------
- Apart from operations at the model management level, where models are considered as atomic, several operations are provided to operate on the model contents directly.
- We again categorize them by their effects on the Modelverse: create, read, or delete.
- Updates are not allowed, and must thus be expanded in individual delete and create operation.
- Create and delete operations are only allowed when the user has write permission to the model, which users have by default on models they created themselves.
- As will become clear, all these operations take the model in which they operate as first argument.
- Subsequent operations on the same model are optimized, offering a noticeable performance improvement.
- Create
- ^^^^^^
- 1. *instantiate* creates a new instance in the model, for example a new PetriNet place::
- >>> instantiate("models/my_pn", "Place")
- __12345
- The operation requires the model on which we are working, and the type of the element you want to instantiate.
- Note that this type is specified by the ID of the model that is being instantiated.
- When successful, the operation returns the identifier that can be used in future operations.
- This identifier has no value within the model, and should only be used as a handle to that specific model element.
- When instantiating an edge, the optional *edge* parameter must be passed with the identifiers to connect::
-
- >>> instantiate("models/my_pn", "Place")
- p1
- >>> instantiate("models/my_pn", "Transition")
- t1
- >>> instantiate("models/my_pn", "P2T", edge=("p1", "t1"))
- p2t1
- Optionally, users can suggest an ID themselves, through the *ID* parameter.
- Note that there is no guarantee that this will actually be the returned ID (e.g., if the ID is already in use)::
- >>> instantiate("models/my_pn", "Place", ID="p123")
- p123
- >>> instantiate("models/my_pn", "Place", ID="p1")
- p1_2
- 2. *attr_assign* assigns attributes of a specific model element.
- For example, it specifies the name and number of tokens of our PetriNet place::
-
- >>> p1 = instantiate("models/my_pn", "Place")
- >>> attr_assign("models/my_pn", p1, "name", "place 1")
- >>> attr_assign("models/my_pn", p1, "tokens", 2)
- The value of the attribute can be any simple primitive: string, integer, float, or boolean.
- When the attribute already exists, its value is overwritten.
- If it doesn't exist, it is created.
- Read
- ^^^^
- 1. *read_info* reads out basic information about a queried element, such as its type and the source and target (if it is an edge)::
- >>> instantiate("models/my_pn", "Place")
- p1
- >>> read_info("models/my_pn", "p1")
- ("Place, None)
- >>> instantiate("models/my_pn", "Transition")
- t1
- >>> instantiate("models/my_pn", "P2T", edge=("p1", "t1"))
- p2t1
- >>> read_info("models/my_pn", "p2t1")
- ("P2T", ("p1", "t1"))
- 2. *read_attrs* reads out the attributes of a specific element, in a dictionary form.
- This operation can be used to read out, for example, the number of tokens of a specific place::
- >>> instantiate("models/my_pn", "Place")
- p1
- >>> attr_assign("models/my_pn", "p1", "name", "place 1")
- >>> attr_assign("models/my_pn", "p1", "tokens", 2)
- >>> read_attrs("models/my_pn", "p1")
- {"name": "place 1", "tokens": 2}
-
- 3. *types* reads out the list of types that can be instantiated in this model.
- All calls to instantiate should act upon one of these types.
- For PetriNets, this returns the concepts of the domain::
- >>> types("models/my_pn")
- ["Place", "Transition", "P2T", "T2P", ...]
- 4. *element_list_nice* reads out a simple JSON-like representation of the model.
- This includes all information about the model and can be used to fetch the complete model in one go.
- For example, to read out a simple PetriNet::
- >>> element_list_nice("models/my_pn")
- [{"id": "p1", "type": "Place", "name": "place 1", "tokens": 1},
- {"id": "p2", "type": "Place", "name": "place 2", "tokens": 2},
- {"id": "t1", "type": "Transition"},
- {"id": "p2t", "type": "P2T", "__source": "p1", "__target": "t1", "weight": 1},
- {"id": "t2p", "type": "T2P", "__source": "t1", "__target": "p2", "weight": 2},
- ]
- 5. *read_outgoing* reads the outgoing associations of a certain type, for a specific element.
- This takes into account inheritance relation.
- For example, to read out all outgoing P2T links of a place::
- >>> read_outgoing("models/my_pn", "p1", "P2T")
- ["p2t"]
- It is possible to get all outgoing associations as well, by leaving the type empty (the empty string)::
- >>> read_outgoing("models/my_pn", "p1", "")
- ["p2t"]
- 6. *read_incoming* similarly reads out all incoming associations of a certain type, for a specific element.
- For example, to read out all incoming T2P links of a place::
- >>> read_incoming("models/my_pn", "p2", "T2P")
- ["t2p"]
- Again, the type can be set to the empty string to return all incoming associations::
- >>> read_incoming("models/my_pn", "p2", "")
- ["t2p"]
- 7. *read_association_source* reads out the source of a specific association, and can be used in conjunction with *read_outgoing* and *read_incoming*.
- For example, to read out which is the source of an arc::
- >>> read_association_source("models/my_pn", "p2t")
- p1
- 8. *read_association_destination* similarly reads out the destination of a specific association.
- For example, to read out the target of an arc::
- >>> read_association_destination("models/my_pn", "p2t")
- t1
- 9. *connections_between* reads out the set of all association types that can be created between two elements in the model.
- This also takes into account inheritance information.
- If no associations are allowed, the list is empty.
- For example, to find out which association types can connect a place and transition::
- >>> connections_between("models/my_pn", "p1", "t1")
- ["P2T"]
- 10. *all_instances* reads out the set of all instances of a specific type in the model.
- Again, inheritance information is taken into account.
- For example, to find all Places in our PetriNet model::
- >>> all_instances("models/my_pn", "Place")
- ["p1", "p2"]
- Delete
- ^^^^^^
- 1. *delete_element* deletes the element from the model and the type mapping.
- Note that currently, attribute values are not automatically removed from the model, and they will remain dangling.
- Therefore, to prevent strange errors, it is safest to first delete all attributes of an element before deleting it.
- Associations, however, are removed.
- For example, to remove place p1::
- >>> element_list_nice("models/my_pn")
- [{"id": "p1", "type": "Place", "name": "place 1", "tokens": 1},
- {"id": "p2", "type": "Place", "name": "place 2", "tokens": 2},
- {"id": "t1", "type": "Transition"},
- {"id": "p2t", "type": "P2T", "__source": "p1", "__target": "t1", "weight": 1},
- {"id": "t2p", "type": "T2P", "__source": "t1", "__target": "p2", "weight": 2},
- ]
- >>> delete_element("models/my_pn", "p1")
- >>> element_list_nice("models/my_pn")
- [{"id": "p2", "type": "Place", "name": "place 2", "tokens": 2},
- {"id": "t1", "type": "Transition"},
- {"id": "t2p", "type": "T2P", "__source": "t1", "__target": "p2", "weight": 2},
- ]
- 2. *attr_delete* deletes an attribute from a model element.
- The attribute is removed, basically rendering it undefined.
- Nonetheless, if the attribute is optional, a subsequent *attr_assign* call is required to make sure that the model conforms.
- Note that *attr_assign* automatically updates the attribute, so *attr_delete* is only necessary for optional attributes that must be unset.
|