modelling.rst 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. Modelling
  2. =========
  3. Modelling is the core activity in the Modelverse.
  4. A model is based on a meta-model, or a language, which, for the sake of this tutorial, we assume exists already.
  5. Throughout this tutorial, we will focus on how to instantiate a simple Petri Net model.
  6. We assume that our language has a notion of *Place*, *Transition*, *P2T* arc, and *T2P* arc.
  7. The *Place* has a *name* and *tokens*, the *Transition* has a *name*.
  8. Both the *P2T* and *T2P* arcs have a *weight*.
  9. This information is stored in the metamodel, which we will define later on.
  10. For now, assume that the metamodel is stored in the Modelverse as *formalisms/PetriNets*.
  11. Model Management
  12. ----------------
  13. Modellers have access to a variety of model management operations, making it possible to create, read, update, and delete complete models.
  14. Create
  15. ^^^^^^
  16. Creating a new instance of PetriNets is simple, and can be done using the *model_add* operation of the wrapper::
  17. >>> model_add("models/my_pn", "formalisms/PetriNets")
  18. The first parameter of the operation indicates the name that we would like to give to our newly created model.
  19. This name is available to all users, and is therefore a unique identifier.
  20. Note that this operation creates a new model, and therefore the target location should be writable, as otherwise a *PermissionDenied* exception is raised.
  21. The second parameter is the metamodel that we want to use to conform to.
  22. The value needs to be a name that is available in the Modelverse, and readable to the current user.
  23. Read
  24. ^^^^
  25. To get a list of currently available models, users can query the Modelverse with the *model_list* operation::
  26. >>> model_list("")
  27. ["formalisms/", "models/", "administration/", "users/", "type mappings/"]
  28. This list contains the various entries in the specified location.
  29. Note that this operation does not specify anything about the permissions of the supplied entries.
  30. Entries that end with a forward slash (/) are folders, and can subsequently be listed (with or without trailing forward slash)::
  31. >>> model_list("formalisms")
  32. ["Bottom", "SimpleClassDiagrams", "Tracability", ...]
  33. >>> model_list("formalisms/")
  34. ["Bottom", "SimpleClassDiagrams", "Tracability", ...]
  35. >>> model_list("users/admin")
  36. []
  37. Depending on permissions, not all folders can be listed::
  38. >>> model_list("administration")
  39. PermissionDenied("administration")
  40. Update
  41. ^^^^^^
  42. Models can be updated in the sense that they can be moved.
  43. This is the operation *model_move*, which takes a model and moves it to the specified location.
  44. When invoked on a folder, the complete folder, including all subfolders, is moved to the new location.
  45. There is no output to this operation::
  46. >>> model_move("models/my_pn", "models/my_new_pn")
  47. Delete
  48. ^^^^^^
  49. Finally, models can be deleted using the *model_delete* operations.
  50. Model deletion is permanent and cannot be reverted::
  51. >>> model_delete("models/my_pn")
  52. Modifying a model
  53. -----------------
  54. 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.
  55. We again categorize them by their effects on the Modelverse: create, read, or delete.
  56. Updates are not allowed, and must thus be expanded in individual delete and create operation.
  57. 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.
  58. As will become clear, all these operations take the model in which they operate as first argument.
  59. Subsequent operations on the same model are optimized, offering a noticeable performance improvement.
  60. Create
  61. ^^^^^^
  62. 1. *instantiate* creates a new instance in the model, for example a new PetriNet place::
  63. >>> instantiate("models/my_pn", "Place")
  64. __12345
  65. The operation requires the model on which we are working, and the type of the element you want to instantiate.
  66. Note that this type is specified by the ID of the model that is being instantiated.
  67. When successful, the operation returns the identifier that can be used in future operations.
  68. This identifier has no value within the model, and should only be used as a handle to that specific model element.
  69. When instantiating an edge, the optional *edge* parameter must be passed with the identifiers to connect::
  70. >>> instantiate("models/my_pn", "Place")
  71. p1
  72. >>> instantiate("models/my_pn", "Transition")
  73. t1
  74. >>> instantiate("models/my_pn", "P2T", edge=("p1", "t1"))
  75. p2t1
  76. Optionally, users can suggest an ID themselves, through the *ID* parameter.
  77. Note that there is no guarantee that this will actually be the returned ID (e.g., if the ID is already in use)::
  78. >>> instantiate("models/my_pn", "Place", ID="p123")
  79. p123
  80. >>> instantiate("models/my_pn", "Place", ID="p1")
  81. p1_2
  82. 2. *attr_assign* assigns attributes of a specific model element.
  83. For example, it specifies the name and number of tokens of our PetriNet place::
  84. >>> p1 = instantiate("models/my_pn", "Place")
  85. >>> attr_assign("models/my_pn", p1, "name", "place 1")
  86. >>> attr_assign("models/my_pn", p1, "tokens", 2)
  87. The value of the attribute can be any simple primitive: string, integer, float, or boolean.
  88. When the attribute already exists, its value is overwritten.
  89. If it doesn't exist, it is created.
  90. Read
  91. ^^^^
  92. 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)::
  93. >>> instantiate("models/my_pn", "Place")
  94. p1
  95. >>> read_info("models/my_pn", "p1")
  96. ("Place, None)
  97. >>> instantiate("models/my_pn", "Transition")
  98. t1
  99. >>> instantiate("models/my_pn", "P2T", edge=("p1", "t1"))
  100. p2t1
  101. >>> read_info("models/my_pn", "p2t1")
  102. ("P2T", ("p1", "t1"))
  103. 2. *read_attrs* reads out the attributes of a specific element, in a dictionary form.
  104. This operation can be used to read out, for example, the number of tokens of a specific place::
  105. >>> instantiate("models/my_pn", "Place")
  106. p1
  107. >>> attr_assign("models/my_pn", "p1", "name", "place 1")
  108. >>> attr_assign("models/my_pn", "p1", "tokens", 2)
  109. >>> read_attrs("models/my_pn", "p1")
  110. {"name": "place 1", "tokens": 2}
  111. 3. *types* reads out the list of types that can be instantiated in this model.
  112. All calls to instantiate should act upon one of these types.
  113. For PetriNets, this returns the concepts of the domain::
  114. >>> types("models/my_pn")
  115. ["Place", "Transition", "P2T", "T2P", ...]
  116. 4. *element_list_nice* reads out a simple JSON-like representation of the model.
  117. This includes all information about the model and can be used to fetch the complete model in one go.
  118. For example, to read out a simple PetriNet::
  119. >>> element_list_nice("models/my_pn")
  120. [{"id": "p1", "type": "Place", "name": "place 1", "tokens": 1},
  121. {"id": "p2", "type": "Place", "name": "place 2", "tokens": 2},
  122. {"id": "t1", "type": "Transition"},
  123. {"id": "p2t", "type": "P2T", "__source": "p1", "__target": "t1", "weight": 1},
  124. {"id": "t2p", "type": "T2P", "__source": "t1", "__target": "p2", "weight": 2},
  125. ]
  126. 5. *read_outgoing* reads the outgoing associations of a certain type, for a specific element.
  127. This takes into account inheritance relation.
  128. For example, to read out all outgoing P2T links of a place::
  129. >>> read_outgoing("models/my_pn", "p1", "P2T")
  130. ["p2t"]
  131. It is possible to get all outgoing associations as well, by leaving the type empty (the empty string)::
  132. >>> read_outgoing("models/my_pn", "p1", "")
  133. ["p2t"]
  134. 6. *read_incoming* similarly reads out all incoming associations of a certain type, for a specific element.
  135. For example, to read out all incoming T2P links of a place::
  136. >>> read_incoming("models/my_pn", "p2", "T2P")
  137. ["t2p"]
  138. Again, the type can be set to the empty string to return all incoming associations::
  139. >>> read_incoming("models/my_pn", "p2", "")
  140. ["t2p"]
  141. 7. *read_association_source* reads out the source of a specific association, and can be used in conjunction with *read_outgoing* and *read_incoming*.
  142. For example, to read out which is the source of an arc::
  143. >>> read_association_source("models/my_pn", "p2t")
  144. p1
  145. 8. *read_association_destination* similarly reads out the destination of a specific association.
  146. For example, to read out the target of an arc::
  147. >>> read_association_destination("models/my_pn", "p2t")
  148. t1
  149. 9. *connections_between* reads out the set of all association types that can be created between two elements in the model.
  150. This also takes into account inheritance information.
  151. If no associations are allowed, the list is empty.
  152. For example, to find out which association types can connect a place and transition::
  153. >>> connections_between("models/my_pn", "p1", "t1")
  154. ["P2T"]
  155. 10. *all_instances* reads out the set of all instances of a specific type in the model.
  156. Again, inheritance information is taken into account.
  157. For example, to find all Places in our PetriNet model::
  158. >>> all_instances("models/my_pn", "Place")
  159. ["p1", "p2"]
  160. Delete
  161. ^^^^^^
  162. 1. *delete_element* deletes the element from the model and the type mapping.
  163. Note that currently, attribute values are not automatically removed from the model, and they will remain dangling.
  164. Therefore, to prevent strange errors, it is safest to first delete all attributes of an element before deleting it.
  165. Associations, however, are removed.
  166. For example, to remove place p1::
  167. >>> element_list_nice("models/my_pn")
  168. [{"id": "p1", "type": "Place", "name": "place 1", "tokens": 1},
  169. {"id": "p2", "type": "Place", "name": "place 2", "tokens": 2},
  170. {"id": "t1", "type": "Transition"},
  171. {"id": "p2t", "type": "P2T", "__source": "p1", "__target": "t1", "weight": 1},
  172. {"id": "t2p", "type": "T2P", "__source": "t1", "__target": "p2", "weight": 2},
  173. ]
  174. >>> delete_element("models/my_pn", "p1")
  175. >>> element_list_nice("models/my_pn")
  176. [{"id": "p2", "type": "Place", "name": "place 2", "tokens": 2},
  177. {"id": "t1", "type": "Transition"},
  178. {"id": "t2p", "type": "T2P", "__source": "t1", "__target": "p2", "weight": 2},
  179. ]
  180. 2. *attr_delete* deletes an attribute from a model element.
  181. The attribute is removed, basically rendering it undefined.
  182. Nonetheless, if the attribute is optional, a subsequent *attr_assign* call is required to make sure that the model conforms.
  183. Note that *attr_assign* automatically updates the attribute, so *attr_delete* is only necessary for optional attributes that must be unset.