wrappers.rst 66 KB

  1. Wrappers
  2. ========
  3. Several wrappers can be defined for the Modelverse, as the Modelverse is merely a service running externally.
  4. To communicate effectively and automatically, a programming language wrapper is recommended.
  5. Nonetheless, it is possible to communicatie manually as well.
  6. These are some of the implemented wrappers.
  7. Prompt
  8. ------
  9. The simplest wrapper is the prompt wrapper, which merely sends the input and output directly to the user.
  10. This wrapper has almost no code, but requires users to manually decide which content to send next.
  11. It has no built-in integration with any model or action language compilers.
  12. Nonetheless, it is an easy way to test out the raw communication protocol manually.
  13. Python
  14. ------
  15. The first real wrapper is the Python-based wrapper.
  16. It provides a set of functions for use by Python code.
  17. These functions wrap not only the interface, but also provides simple error handling through the use of Python exceptions and contains the model and action language compilers.
  18. Since it is relatively minimal and provides a complete implementation of a Modelverse Interface, it is considered as a reference implementation for the Modelverse protocol.
  19. An overview of all functions and associatied exceptions is provided below.
  20. All operations happen *synchronously*, meaning that they block until the Modelverse has performed the requested operation.
  21. Note that some functions are only applicable in a certain context.
  22. In practice, this means that you should first issue the *init* and *login* operations, as otherwise your connection with the Modelverse will not have started up yet.
  23. For each function, we provide an example to indicate how this operation can be used.
  24. In these first examples, we assume that all referenced elements are present, all permissions are granted, etc.
  25. Afterwards, we provide an overview of all exceptions, together with an example.
  26. In these examples, the problem is often caused by an non-existing element or unsatisfied permissions.
  27. Initialization Function
  28. ^^^^^^^^^^^^^^^^^^^^^^^
  29. .. function:: init(address_param="", timeout=20.0)
  30. Start up the connection to the Modelverse, residing at *address_param*.
  31. This connection is an XML/HTTPRequest and will start up a new task at the Modelverse.
  32. Retries for *timeout* seconds until giving up.
  33. The timeout includes all HTTP errors, and will therefore keep retrying even on failed attempts.
  34. As this request is synchronous, like all others, it will block until a connection has been established.
  35. Examples:
  36. * To create a connection to a local Modelverse.
  37. >>> init()
  38. * To create a connection to the Modelverse running remotely.
  39. >>> init("http://modelverse.uantwerpen.be:8001")
  40. Authentication
  41. ^^^^^^^^^^^^^^
  42. .. function:: login(username, password)
  43. Explanation
  44. Logs in the currently active Modelverse connection to the specified *username* and *password*.
  45. If the user does not exist, it will create a new user with the specified password.
  46. If the user already exists, it will try to log in with the provided password.
  47. Examples:
  48. * To login as user user1, with the specified password.
  49. >>> login("user1", "my_password")
  50. .. function:: user_logout()
  51. Logs out the current user, thereby closing the task.
  52. Subsequent operations will no longer have any effect, as the task was terminated.
  53. To log in as a different user, the *init* operation has to be executed again.
  54. Examples:
  55. * To log out the current user and allow future logins by this user.
  56. >>> user_logout()
  57. .. function:: user_name(user, username)
  58. Changes the username of user *user* to *username*.
  59. From this point on, the user has been renamed, and all old permissions are inherited.
  60. Similar to password management, users can only alter their own username.
  61. The administrator can also alter the username of all users.
  62. Active logins do not need to be refreshed, as the new username will automatically be used everywhere.
  63. From the next login, however, the new username has to be used, as the old one has been renamed.
  64. Examples:
  65. * To change the username of user1 to user2.
  66. >>> user_name("user1", "user2")
  67. .. function:: user_password(user, password)
  68. Changes the password of user *user* to *password*.
  69. Permissions on this operation are of course rather strict:
  70. users can only modify their own password.
  71. Of course, an administrator can modify the password of every user.
  72. Active logins do not need to be refreshed, as the new username will automatically be used everywhere.
  73. From the next login, however, the new username has to be used, as the old one has been renamed.
  74. Examples:
  75. * To change the password of user *user1* to *my_password*.
  76. >>> user_password("user1", "my_password")
  77. Model Management
  78. ^^^^^^^^^^^^^^^^
  79. .. function:: folder_create(folder_name)
  80. Create a folder with the specified name.
  81. If necessary, this operation recursively creates all parent folders as well.
  82. All created folders are owned by the creating users and permissions are set accordingly.
  83. Examples:
  84. * To create a folder *my_folder* in your user folder.
  85. >>> folder_create("users/user/my_folder")
  86. * To create a folder *my_subfolder* in the folder *parent_subfolder*, in your user folder.
  87. >>> folder_create("users/user/parent_subfolder/my_subfolder")
  88. .. function:: model_types(model_name)
  89. Returns typing information of the model.
  90. This information consists of a set of tuples, each tuple containing the metamodel, the type mapping model, and the conformance checking function used.
  91. Multiple entries might have the same metamodel or type mapping model, as there might be multiple ways in which a model conforms to a metamodel.
  92. Note that this only queries for existing typing relations, and does not consider potential relations.
  93. For example, all models will conform to *formalisms/Bottom*, but this will only be an entry in *model_types* if the relation was explicitly created (e.g., by opening the model as such).
  94. The type mapping model might be empty (i.e., *None*), in which case the type mapping model has to be dynamically constructed.
  95. If the conformance semantics is set to None, this means that the internal Modelverse semantics is used.
  96. Examples:
  97. * To query for the metamodels of model *my_pn*.
  98. >>> model_types("my_pn")
  99. set([("formalisms/PetriNets", "type mappings/1", None)])
  100. * To query for the metamodels of model *my_pn*, after it has been opened using *formalisms/Bottom*.
  101. >>> model_types("my_pn")
  102. set([("formalisms/PetriNets", "type mappings/1", None), ("formalisms/Bottom", "type mappings/20", None)])
  103. * To query for the metamodel of a metamodel *formalisms/PetriNets*.
  104. >>> model_types("formalisms/PetriNets")
  105. set([("formalisms/SimpleClassDiagrams", "type mappings/2", None)])
  106. .. function:: conformance_add(model_name, metamodel_name)
  107. Add a new conformance relation between a model stored at *model_name* and a metamodel stored at *metamodel_name*.
  108. While this adds the conformance relation, it does not specify the type mapping model, meaning that it has to be subsequently resolved before use.
  109. Where possible, type mapping models can be automatically deduced (e.g., conformance bottom, traceability, and type mapping).
  110. If no type mapping can be deduced, an exception will be thrown during the execution of the first command that tries to open the model in this way.
  111. After the *conformance_add* operation, the relation can already be seen using *model_types*, as it is stored at the server.
  112. This is in contrast to *alter_context*, which is only stored locally in the client.
  113. Examples:
  114. * To consider a model *my_pn* as a conformance bottom model.
  115. >>> conformance_add("my_pn", "formalisms/Bottom")
  116. .. function:: conformance_delete(model_name, metamodel_name, type_mapping_name)
  117. Delete a conformance relation between a model stored at *model_name* and a metamodel stored at *metamodel_name*, using the type mapping model stored at *type_mapping_name*.
  118. The *type_mapping_name* can be left empty (empty string) if no type mapping model is defined yet.
  119. All matching conformance relations are removed.
  120. After the conformance relation is removed, it might be possible that the relation lingers in the context, meaning that a new attempt will be made to deduce a typing relation from scratch.
  121. Examples:
  122. * To remove the previously considered conformance bottom relation for model "my_pn", with type mapping model "type mappings/123".
  123. >>> conformance_delete("my_pn", "formalisms/Bottom", "type mappings/123")
  124. * To remove the previously considered conformance bottom relation for model "my_pn", without a constructed type mapping model.
  125. >>> conformance_delete("my_pn", "formalisms/Bottom", "")
  126. .. function:: model_add(model_name, metamodel_name, model_code=None)
  127. Upload a new model that can later be referred to as *model_name*, conforming to *metamodel_name*.
  128. The model itself is stored in the string *model_code*.
  129. This string is parsed using the HUTN compiler and subsequently sent to the Modelverse.
  130. When *model_code* is empty, an empty model is created.
  131. Examples:
  132. * To create a new model called PetriNets, conforming to SimpleClassDiagrams, and load the model stored in models/PetriNets.mvc.
  133. >>> model_add("formalisms/PetriNets", "formalisms/SimpleClassDiagrams", open("models/PetriNets.mvc", "r").read())
  134. * To create a minimal instance of the language afterwards, which only contains a single place (and no attributes).
  135. >>> model_add("models/my_pn", "formalisms/PetriNets", "Place p1 {}")
  136. * To create a less minimal instance of the language, stored in models/my_pn2.mvc.
  137. >>> model_add("models/my_pn2", "formalisms/PetriNets", open("models/my_pn2.mvc", "r").read())
  138. .. function:: upload_code(code)
  139. Upload a string of *code* in the Action Language formalism.
  140. This piece of code is compiled with the HUTN compiler and sent to the Modelverse directly.
  141. Makes the assumption that the **construct_function()** operation is currently running on the Modelverse, as otherwise the data will be misinterpreted.
  142. This is normally only useful in model transformations, where you want to upload a piece of code on-the-fly (e.g., adding a breakpoint in Action Language).
  143. Examples:
  144. * To upload the file models/code.alc.
  145. >>> upload_code(open("models/code.alc", "r").read())
  146. * To upload a code fragment inline.
  147. >>> upload_code("Void function a():\nreturn!")
  148. .. function:: model_delete(model_name)
  149. Delete the model referred to by name *model_name*.
  150. This is a non-cascading delete, with almost no checks: model transformations depending on this model will likely become corrupted.
  151. Examples:
  152. * To delete a previously created model.
  153. >>> model_delete("models/my_pn2")
  154. * Or to delete a metamodel, which is itself just a model.
  155. >>> model_delete("formalisms/PetriNets")
  156. * To delete a full folder.
  157. >>> model_delete("formalisms")
  158. .. function:: model_list(location)
  159. Returns a list of all models existing in the specified folder.
  160. Sub-folders can be recognized because they have a trailing forward slash in their name.
  161. Examples:
  162. * To get a list of all models in the formalisms directory.
  163. >>> model_list("formalisms")
  164. ["PetriNets", "SimpleClassDiagrams", "Bottom", "Tracability", ...]
  165. .. function:: model_list_full(location)
  166. Returns a detailed list of all models existing in the specified folder in the Modelverse.
  167. This list includes information on permissions, owner, and group.
  168. Examples:
  169. * To get a detailed list of all currently present models.
  170. >>> model_list_full("models")
  171. [("my_pn", "user1", "users", "200"), ("my_pn2", "user1", "users", "200"), ...]
  172. .. function:: verify(model_name, metamodel_name)
  173. Verify whether *model_name* conforms to *metammodel_name*, as both stored in the Modelverse.
  174. Returns either "OK" if the model conforms, or a string specifying the reason for non-conformance.
  175. Examples:
  176. * Verifying a conforming model.
  177. >>> verify("formalisms/PetriNets", "formalisms/SimpleClassDiagrams")
  178. OK
  179. * Or verify using the alternative conformance relation (conformance bottom).
  180. >>> verify("formalisms/PetriNets", "formalisms/Bottom")
  181. OK
  182. * Verifying a non-conforming model.
  183. >>> verify("models/my_pn")
  184. Lower cardinality violation for attribute "name" at Place p1.
  185. .. function:: model_overwrite(model_name, new_model_code=None)
  186. Overwrites the model previously known under the name *model_name* with the model code in *new_model_code*.
  187. This operation differs from first deleting the model and then recreating it, as all metadata of the model is kept, such as access permissions.
  188. The new model can be kept empty, in which case the model will be cleared.
  189. Examples:
  190. * To overwrite the PetriNets metamodel with a newer version, thereby also updating the metamodel of all existing instances ("my_pn" and "my_pn2").
  191. >>> model_overwrite("formalisms/PetriNets", open("models/PetriNets2.mvc", "r").read())
  192. * To overwrite an existing PetriNets instance.
  193. >>> model_overwrite("models/my_pn", """Place p2 {}""")
  194. Transformations
  195. ^^^^^^^^^^^^^^^
  196. .. function:: model_render(model_name, mapper_name)
  197. Render the model by name of *model_name* using the mapper by name of *mapper_name*.
  198. Both parameters have to be known models in the Modelverse.
  199. Outputs a JSON representation of the rendered model.
  200. This is basically just a shortcut for executing the specified operation and reading out the resulting model with a JSON representation.
  201. Examples:
  202. * To render the PetriNets instance using the PetriNetsMapper.
  203. >>> model_render("models/my_pn", "formalisms/PetriNetsMapper")
  204. [{"id": "__12345", "type": "Ellipse", "x": 100, "y": 150, "height": 20, "width: "20"}]
  205. .. function:: transformation_between(source, target)
  206. List all transformations that take at least all elements in *source* as input, with an exact match in tags, and that generate at least all elements in *target*, again with the same tags.
  207. An empty dictionary means that there is no constraint on that part, but there must be at least one tag specified.
  208. Examples:
  209. * To fetch all endogenous transformations on PetriNets on the *PN* tag, assuming that some were previously defined.
  210. >>> transformation_between({"PN": "formalisms/PetriNets"}, {})
  211. ["PN_simulate", "PN_optimize", "PN_to_matrix"]
  212. * To fetch all endogenous transformations on PetriNets on the *PN* tag that modify the model in-place, assuming that some were previously defined.
  213. >>> transformation_between({"PN": "formalisms/PetriNets"}, {"PN": "formalisms/PetriNets"})
  214. ["PN_simulate", "PN_optimize"]
  215. .. function:: transformation_add_MT(source_metamodels, target_metamodels, operation_name, code, callback=lambda: None)
  216. Create a new model transformation operation.
  217. The new transformation takes *source_metamodels* as input, and generates *target_metamodels* as output.
  218. Both parameters are dictionaries of the form {name: metamodel_name}.
  219. The name is used later on in the model transformation as a prefix to the type.
  220. A single metamodel_name can be used for multiple names.
  221. Note that the target metamodel names may overlap with the source metamodel names, but the metamodel type should be identical.
  222. The operation is henceforth known by *operation_name* and is provided as a model in the string *code*.
  223. Optionally, a callback is defined which performs some operations on the merged metamodel, for example to define tracability links between (previously unrelated) metamodels.
  224. In the background, this operation does all necessary RAMification and model merging.
  225. Examples:
  226. * To create a new model transformation for PetriNets simulation.
  227. >>> transformation_add_MT({"pn": "formalisms/PetriNets"}, {"pn": "formalisms/PetriNets"}, "models/pn_simulate", open("models/PN_simulate.mvc", "r").read())
  228. * To create a model transformation from a DSL to PetriNets, which requires tracability links.
  229. >>> def tracability_links():
  230. ... instantiate("Association", ID="Tile2Place", ("dsl/Tile", "pn/Place"))
  231. ... instantiate("Association", ID="Dirrection2Transition", ("dsl/Direction", "pn/Transition"))
  232. >>> transformation_add_MT({"dsl": "formalisms/RPGame"}, {"pn": "formalisms/PetriNets"}, "models/denotational_1", open("models/denotational_1.mvc", "r").read(), tracability_links)
  233. * To create a multi-input model transformation.
  234. >>> transformation_add_MT({"pn_1": "formalisms/PetriNets", "pn_2": "formalisms/PetriNets", "architecture: "formalisms/Architecture"}, {"result": "formalisms/PetriNets"}, "models/PN_merge", open("models/PN_merge.mvc", "r").read())
  235. .. function:: transformation_add_AL(source_metamodels, target_metamodels, operation_name, code, callback=lambda: None)
  236. Creates a new action language operation.
  237. Similar to *transformation_add_MT*, but now does not require RAMification.
  238. The *code* parameter also is not specified as a Modelverse model (.mvc), but as action language (.alc).
  239. Examples:
  240. * To create a new action language operation for PetriNets reachability analysis.
  241. >>> transformation_add_AL({"pn": "formalisms/PetriNets"}, {"graph": "formalisms/ReachabilityGraph"}, "models/pn_analyze", open("models/PN_reachability.alc", "r").read())
  242. * To create an action language operation from a Scheduling DSL to a list, which requires tracability links.
  243. >>> def tracability_links():
  244. ... instantiate("Association", ID="Task2Event", ("schedule/Task", "list/Event"))
  245. >>> transformation_add_AL({"schedule": "formalisms/SchedulingDSL"}, {"list": "formalisms/EventList"}, "models/sequentialize", open("models/sequentialize_schedule.alc", "r").read(), tracability_links)
  246. .. function:: transformation_add_MANUAL(source_metamodels, target_metamodels, operation_name, callback=lambda: None)
  247. Creates a new manual operation.
  248. Identical to *transformation_add_AL*, but does not take any code as content.
  249. Examples:
  250. * To create a manual refinement operation on PetriNets.
  251. >>> transformation_add_MANUAL({"pn": "formalisms/PetriNets"}, {"pn": "formalisms/PetriNets"}, "models/pn_refine")
  252. * To create a multi-input refinement operation on PetriNets.
  253. >>> transformation_add_MANUAL({"pn": "formalisms/PetriNets", "requirements": "formalisms/Requirements"}, {"pn": "formalisms/PetriNets"}, "models/pn_refine_req")
  254. .. function:: transformation_execute_AL(operation_name, input_models_dict, output_models_dict, statechart=None, traceability_model="", fetch_output=True)
  255. Executes the Action Language model *operation_name* with *input_models_dict* as inputs and *output_models_dict* as outputs.
  256. For both dicts, the contents describe the mapping between the parameter names of the operation to the names in the Modelverse.
  257. Values in *input_models_dict* must be existing models, whereas values in *output_models_dict* can be non-existing upon invocation.
  258. A *statechart* tuple can be defined when the action language model requires user input or output.
  259. This is used to communicate with the executing code directly.
  260. For more information about this, refer to the execution of transformations.
  261. The *traceability* model can optionally be specified, which then loads in the selected model to create the traceability links between all models, and all changes are stored to this model as well.
  262. The *fetch_output* parameter ensures that the output thread is closed after this call. This is useful if the activity is forever running and no other communication has to happen with the MvI.
  263. Examples:
  264. * To execute reachability analysis on an existing petri net.
  265. >>> transformation_execute_AL("models/pn_analyze", {"pn": "models/my_pn"}, {"graph": "models/my_pn_reachability"})
  266. .. function:: transformation_execute_MANUAL(operation_name, input_models_dict, output_models_dict, callback=lambda i: None)
  267. Executes the manual model operation *operation_name*.
  268. Furthermore, this is identical to *transformation_execute_AL*, with the exception of the *callback* function.
  269. In this case, the callback function can be just another series of Modelverse operations, though pertaining to a single model, which is passed as a parameter.
  270. As such, the *model_name* parameter of these operations **MUST** be set to the parameter.
  271. Examples:
  272. * To execute a manual operation, which requires you to refine a PetriNets instance.
  273. >>> def callback(model):
  274. ... p1 = instantiate(model, "pn/Place")
  275. ... t1 = instantiate(model, "pn/Transition")
  276. ... instantiate(model, "pn/P2T", (p1, t1))
  277. >>> transformation_execute_MANUAL("models/pn_refine", {"pn": "models/my_pn"}, {"pn": "models/my_pn"}, callback)
  278. .. function:: transformation_execute_MT(operation_name, input_models_dict, output_models_dict, statechart=None, traceability_model="", fetch_output=True)
  279. Executes the model transformation operation *operation_name*.
  280. Identical to *transformation_execute_AL*.
  281. Examples:
  282. * To execute a model transformation on a PetriNets instance, thereby putting the result in a different model.
  283. >>> transformation_execute_MT("models/pn_simulate", {"pn": "models/my_pn"}, {"pn": "models/my_simulated_pn"})
  284. .. function:: transformation_read_signature(transformation)
  285. Returns the signature of the transformation *transformation*.
  286. The signature consists of two dictionaries: the first specifies the input signature, and the second one specifies the output signature.
  287. Both dictionaries have the same structure: the keys represent the names of the model while invoking the operation, and the values represent the expected type of the model.
  288. Examples:
  289. * To read out the signature of the "plant_refine" operation.
  290. >>> transformation_read_signature("models/plant_refine")
  291. ({"req": "formalisms/Requirements", "plant": "formalisms/Plant"}, {"plant": "formalisms/Plant"})
  292. Processes
  293. ^^^^^^^^^
  294. .. function:: process_execute(process_name, model_mapping, callbacks={})
  295. Execute the process model stored as *process_name*.
  296. Models are retrieved and stored in the Modelverse based on the names stored in the process model.
  297. When a name is present in the *model_mapping*, the dictionary value is used as the location where the model is retrieved or stored.
  298. If the name is not present in the mapping, the model is considered to be anonymous.
  299. Optionally, a dictionary of *callbacks* can be defined with as key the operation that is being executed, and value the actual callback.
  300. The callbacks must be similarly defined just like how they were defined in the individual *transformation_execute* operations.
  301. This means that the callback might very well be a tuple for a statechart instance.
  302. Examples:
  303. * To execute a process model for the power window example, thereby binding the "requirements" model to the Modelverse location "models/requirements".
  304. >>> process_execute("models/pm_powerwindow", {"requirements": "models/requirements"})
  305. * To execute a process model for the power window example, which requires user input for some operations.
  306. >>> def refine_architecture(model):
  307. ... # Do some operation on the architecture model here
  308. ... node1 = instantiate(model, "Node")
  309. ... node2 = instantiate(model, "Node")
  310. ... instantiate(None, "Connection", (node1, node2))
  311. >>> def refine_control(model):
  312. ... # Do some operation on the control model here
  313. ... s1 = instantiate(model, "State")
  314. ... s2 = instantiate(model, "State")
  315. ... instantiate(model, "Transition", (s1, s2))
  316. >>> def refine_query(model):
  317. ... # Do some operation on the safety query model here
  318. ... p1 = instantiate(model, "Place")
  319. ... attr_assign(model, p1, "tokens", 2)
  320. >>> process_execute("models/pm_powerwindow", {}, {"models/refine_plant": refine_plant, "models/refine_control": refine_control, "models/refine_query": refine_query})
  321. .. function:: process_signature(process_name)
  322. Fetch the process signature, specifying the list of all possible models and their metamodel.
  323. The returned dictionary works in two directions, as it is used for retrieving models and storing them again.
  324. All *Data* instances are listed here, with their *name* attribute as key and their *type* attribute as value.
  325. Examples:
  326. * To fetch all data artefacts from the process *pn_reachability*.
  327. >>> process_signature("pn_reachability")
  328. {"pn": "users/user/test/PetriNet", "reachability": "users/user/test/ReachabilityGraph"}
  329. User Access Control
  330. ^^^^^^^^^^^^^^^^^^^
  331. .. function:: read_permissions(model_name)
  332. Read the permissions of the current user for a specific model.
  333. In contrast to the *model_list_full* operation, which gives general permission information (e.g., owner and permissions for owner), this operation gives tailored information for this specific user (i.e., none, read, or write).
  334. As such, this operation does the necessary resolution of group membership and owning group deduction.
  335. The result is a string containing letters indicating permissions: "R" for read and "W" for write.
  336. If the model even cannot be read, the string is empty.
  337. Examples:
  338. * To determine the permissions for the *users/user/my_pn* model, which we own, for which we have read and write permissions.
  339. >>> read_permissions("user/user/my_pn")
  340. "RW"
  341. * To determine the permissions for the *formalisms/SimpleClassDiagrams* metamodel, for which we have read permissions, but no write permissions.
  342. >>> read_permissions("formalisms/SimpleClassDiagrams")
  343. "R"
  344. * To determine the permissions of the *administration/core* model, which we are not allowed to read or write.
  345. >>> read_permissions("administration/core")
  346. ""
  347. .. function:: permission_modify(model_name, permissions)
  348. Change the permissions of *model_name* to *permissions*.
  349. The permissions is a string of three characters, each between 0 and 2.
  350. The format is similar to the UNIX permission system: the leftmost character is the permission for the owning user, the middle character for members of the ownin group, and the rightmost character for all other users.
  351. Character 0 signifies no access, 1 read-only access, and 2 full read/write access.
  352. Note that changing permissions trickle down to the instances as well: if the metamodel is no longer readable to the user, all models conforming to it become unreadable in this specific context.
  353. Examples:
  354. * To modify the permissions of the PetriNets metamodel, allowing only the owner to read and write to it.
  355. >>> permission_modify("formalisms/PetriNets", "200")
  356. * To modify the permissions of a PetriNets model, granting everyone read/write access.
  357. >>> permission_modify("formalisms/PetriNets", "222")
  358. .. function:: permission_owner(model_name, owner)
  359. Change the owner of the model *model_name* to *owner*.
  360. Changing permissions trickles down to the instances as well: if the metamodel is no longer readable to the user, all models conforming to it become unreadable in this specific context.
  361. Examples:
  362. * To change the owning user of the PetriNets metamodel to user2.
  363. >>> permission_owner("formalisms/PetriNets", "user2")
  364. .. function:: permission_group(model_name, group)
  365. Change the owning group of the model *model_name* to *group*.
  366. The same remarks hold as for all other permission operations.
  367. Examples:
  368. * To change the owning group of the PetriNets metamodel to group1.
  369. >>> permission_group("formalisms/PetriNets", "group1")
  370. .. function:: group_create(group_name)
  371. Create a new group named *group_name*.
  372. You automatically become an administrator for this group.
  373. Examples:
  374. * To create a new group called group2.
  375. >>> group_create("group2")
  376. .. function:: group_delete(group_name)
  377. Delete the group named *group_name*.
  378. All users will automatically be kicked from the group, and all permissions previously granted by this group become void.
  379. Examples:
  380. * To delete the group group2.
  381. >>> group_delete("group2")
  382. .. function:: group_owner_add(group_name, user_name)
  383. Add user *user_name* as an owner of group *group_name*.
  384. This automatically makes the user join the specified group if this was not yet the case.
  385. Examples:
  386. * To add user user1 as a new owner, or group administrator, to group group1.
  387. >>> group_owner_add("group1", "user1")
  388. .. function:: group_owner_delete(group_name, user_name)
  389. Remove user *user_name* as an owner of group *group_name*.
  390. Examples:
  391. * To delete user1 as an owner, or group administrator, from group group1.
  392. >>> group_owner_delete("group1", "user1")
  393. .. function:: group_join(group_name, user_name)
  394. Have user *user_name* join the group *group_name* as an ordinary user.
  395. Examples:
  396. * To make user user1 join the group group1, of which the current user is an owner.
  397. >>> group_join("group1", "user1")
  398. .. function:: group_kick(group_name, user_name)
  399. Remove user *user_name* from the group *group_name*.
  400. If the user was an owner of the group, these permissions are also revoked.
  401. Examples:
  402. * To kick user user1 from group group1, of which the current user is an owner.
  403. >>> group_kick("group1, "user1")
  404. Administration
  405. ^^^^^^^^^^^^^^
  406. .. function:: admin_promote(user_name)
  407. Promote user *user_name* to admin status.
  408. Admin status grants users access to all operations, and provides all permission.
  409. Effectively, all groups and files have read/write access for the admin user, independent of the stored permissions.
  410. Examples:
  411. * To promote user1 to admin states.
  412. >>> admin_promote("user1")
  413. .. function:: admin_demote(user_name)
  414. Demote user *user_name* to ordinary user.
  415. Examples:
  416. * To demote user1 to a normal user.
  417. >>> admin_demote("user1")
  418. Modelling
  419. ^^^^^^^^^
  420. .. function:: element_list_nice(model_name)
  421. Returns a complete representation of a model in a compact dicationary-like representation.
  422. This is basically a list of dictionaries, each dictionary containing the values usually obtained with *read_attrs*.
  423. Additionally, some special keys are added: *id* (the identifier returned upon instantiation), *__source* (the source of the association, if it was an association), and *__target* (the target of the association, if it was an association).
  424. Examples:
  425. * To read out a list of a PetriNets instance model.
  426. >>> element_list_nice("models/my_pn")
  427. [{"id": "p1", "name": "a place", "tokens": 1}, {"id": "t1", "name": "a transition"}, {"id": "p1_to_t1", "name": "transition", "__source": "p1", "__target": "t1", "weight": 1}]
  428. .. function:: element_list(model_name)
  429. Returns a list of all elements and their type specified in the model named *model_name*.
  430. This list can contain much more than only simple elements, but includes anonymous edges and attributes as well.
  431. It is therefore not recommended for general purpose use; use *element_list_nice* instead.
  432. Examples:
  433. * To get a list of all elements in the PetriNets metamodel.
  434. >>> element_list("formalisms/PetriNets")
  435. [("Place", "Class"), ("Transition", "Class"), ("P2T", "Association"), ("T2P", "Association"), ...]
  436. .. function:: types(model_name)
  437. Returns a list of all types usable in the model named *model_name*.
  438. This is similar to executing *element_list* on the metamodel of this model.
  439. Examples:
  440. * To get a list of all types usable in the PetriNets metamodel (i.e., when altering the metamodel itself).
  441. >>> types("formalisms/PetriNets")
  442. ["Class", "Association", "SimpleAttribute", ...]
  443. .. function:: read_info(model_name, ID)
  444. Read the content of model element *ID* in model *model_name*.
  445. This returns the type of the element, and the set of source and destination if the element is an edge (*None* otherwise).
  446. Examples:
  447. * To read out the P2T link in the PetriNets metamodel.
  448. >>> read_info("formalisms/PetriNets", "P2T")
  449. ["Association", ("Place", "Transition")]
  450. * To read out the Place node in the PetriNets metamodel.
  451. >>> read_info("formalisms/PetriNets", "Place")
  452. ["Class", None]
  453. * To read out some P2T instance in a PetriNets model.
  454. >>> read_info("my_pn", "p1_to_t1")
  455. ["P2T", ("p1", "t1")]
  456. * To read out some Place instance in a PetriNets model.
  457. >>> read_info("my_pn", "p1")
  458. ["Place", None]
  459. .. function:: read_attrs(model_name, ID)
  460. Return a dictionary of all attributes of model element *ID* in model *model_name*, containing their values.
  461. All values in the Modelverse are primitive types, and as such, this is also the case in this operation.
  462. The value is *None* in case the attribute is not set.
  463. Examples:
  464. * To read out the attributes of the Place class.
  465. >>> read_attrs("formalisms/PetriNets", "Place")
  466. {"lower_cardinality": None, "upper_cardinality": None}
  467. * To read out the attributes of a Place instance.
  468. >>> read_attrs("models/my_pn", "p1")
  469. {"name": "critical_section", "tokens": 1}
  470. .. function:: instantiate(model_name, typename, edge=None, ID="")
  471. Instantiate a new instance of *typename* in the model *model_name*.
  472. If the instance is an edge, provide a tuple containing the source and target as *edge*.
  473. A preferred *ID* can be specified, though there is no guarantee that this name is actually used (e.g., if it is already taken by another element).
  474. This operation returns the actually assigned ID.
  475. It is this ID that is used for all other operations on the model.
  476. Examples:
  477. * To create a new Place instance in a PetriNets model.
  478. >>> instantiate("models/my_pn", "Place")
  479. "__12345"
  480. * To create a new Place instance with a preferred ID, which is granted.
  481. >>> instantiate("models/my_pn", "Place", ID="critical_section")
  482. "critical_section"
  483. * To create a new Place instance with a preferred ID, which is not granted.
  484. >>> instantiate("models/my_pn", "Place", ID="critical_section")
  485. critical_section_12345"
  486. * To create a new P2T instance in a PetriNets model.
  487. >>> instantiate("models/my_pn", "P2T", ("p1", "t1"))
  488. "__12345"
  489. * To create a new concept in the PetriNets metamodel, which can later on be used in all instances immediately.
  490. >>> instantiate("formalisms/PetriNets", "Association", ("Place", "Transition"), ID="InhibitorArc")
  491. "InhibitorArc"
  492. >>> instantiate("models/my_pn", "InhibitorArc", ("p1", "t1"))
  493. "__12345"
  494. .. function:: delete_element(model_name, ID)
  495. Delete the element *ID* in the model *model_name*.
  496. This is a recursive delete, and all incoming and outgoing edges will be removed (recursively) as well.
  497. Examples:
  498. * To delete an existing element in a PetriNets model.
  499. >>> delete_element("models/my_pn", "critical_section")
  500. * To delete an existing exdge in a PetriNets model.
  501. >>> delete_element("models/my_pn", "p1_to_t1")
  502. * When deleting an element "p1", the arc "p1_to_t1" is also removed automatically.
  503. >>> delete_element("models/my_pn", "p1")
  504. >>> delete_element("models/my_pn", "p1_to_t1")
  505. UnknownElement("p1_to_t1")
  506. Attribute Manipulation
  507. ^^^^^^^^^^^^^^^^^^^^^^
  508. .. function:: attribute_optional(model_name, node, attr_name, optionality)
  509. Alter the optionality of a defined attribute *attr_name* on a class *node* in the model *model_name* to *optionality*.
  510. The optionality has to be a boolean value.
  511. Examples:
  512. * To make the number of tokens in *formalisms/PetriNets* optional.
  513. >>> attribute_optional("formalisms/PetriNets", "Place", "tokens", True)
  514. * To make the number of tokens in *formalisms/PetriNets* mandatory.
  515. >>> attribute_optional("formalisms/PetriNets", "Place", "tokens", False)
  516. .. function:: attribute_name(model_name, node, attr_name, new_name)
  517. Change the name of a defined attribute *attr_name* on a class *node* in the model *model_name* to *new_name*.
  518. The new name has to be a string value, and no similarly named attribute should exist on that element yet.
  519. Examples:
  520. * To change the name of the *tokens* attribute to *nrTokens* in *formalisms/PetriNets*.
  521. >>> attribute_name("formalisms/PetriNets", "Place", "tokens", "nrTokens")
  522. .. function:: attribute_type(model_name, node, attr_name, new_type)
  523. Change the type of a defined attribute *attr_name* on a class *node* in the model *model_name* to *new_type*.
  524. The new type has to be a string value, refering to a type in the metamodel that defines the new type of the attribute.
  525. Examples:
  526. * To change the type of the *tokens* attribute *Integer* in *formalisms/PetriNets*, where *Integer* is defined as a *SimpleAttribute* instance.
  527. >>> attribute_name("formalisms/PetriNets", "Place", "tokens", "Integer")
  528. .. function:: read_defined_attrs(model_name, node)
  529. Retrieve a list of all defined attributes in class *node* in model *model_name*.
  530. The return value consists of a pair of dictionaries.
  531. The first dictionary containing all mandatory attributes, and the second dictionary containing all optional attributes.
  532. Both dictionaries are constructed the same way, consisting of the name of the attribute as key, and the name of the type as value.
  533. Examples:
  534. * To fetch all defined attributes of the *Place* class in *formalisms/PetriNets*, which has a mandatory name and number of tokens, with an optional capacity.
  535. >>> read_defined_attrs("formalisms/PetriNets", "Place")
  536. ({"name": "String", "tokens": "Natural"}, {"capacity": "Natural"})
  537. .. function:: define_attribute(model_name, node, attr_name, attr_type)
  538. Defines a new attribute on the element *node* in the metamodel identified by *model_name*.
  539. The attribute is named *attr_name* and must conform to *attr_type*.
  540. Note that, while there are other ways of creating attributes than this function, this function is by far the most user friendly.
  541. The type being assigned needs to be specified in the metamodel as well, by instantiating the *SimpleAttribute* node.
  542. As models are updated immediately, without any recompilation step inbetween, all instances can immediately make use of this new attribute.
  543. Examples:
  544. * To define a new attribute "tokens" on a PetriNet Place, which is of a Natural type.
  545. >>> define_attribute("formalisms/PetriNets", "Place", "tokens", Natural)
  546. .. function:: undefine_attribute(model_name, node, attr_name)
  547. Removes a defined attribute *attr_name* on *node* in model *model_name*.
  548. Upon removal, all information about the attribute is also removed, such as optionality, name, and typing information.
  549. Instances of the metamodel which make use of this attribute, will become non-conforming.
  550. Examples:
  551. * To undefine the *tokens* attribute in the *Place* class for *formalisms/PetriNets*.
  552. >>> undefine_attribute("formalisms/PetriNets", "Place", "tokens")
  553. .. function:: attr_assign(model_name, ID, attr, value)
  554. Assign the value *value* to the attribute named *attr* of the element *ID* in the model named *model_name*.
  555. If the attribute already has an assigned value, the previous value is removed first.
  556. Examples:
  557. * To assign some attributes to a Place instance.
  558. >>> attr_assign("models/my_pn", "p1", "name", "my first place")
  559. >>> attr_assign("models/my_pn", "p1", "tokens", 1)
  560. * To assign some attributes to the Place class itself.
  561. >>> attr_assign("formalisms/PetriNets", "Place", "upper_cardinality", 1)
  562. .. function:: attr_assign_code(model_name, ID, attr, code)
  563. Assign the code block *code* to the attribute named *attr* of the element *ID* in the model named *model_name*.
  564. If the attribute already has an assigned value, the previous value is removed first.
  565. The assigned code is compiled to Action Language by the HUTN compiler.
  566. Examples:
  567. * To assign a piece of action code to a Statecharts transition, loaded from file.
  568. >>> attr_assign_code("models/my_sc", "t1", "script", open("models/t1_script", "r").read())
  569. * To assign a piece of action code to a Statecharts transition, defined inline.
  570. >>> code = \
  571. ... """
  572. ... Void function action(attributes : Element):
  573. ... dict_overwrite(attributes, "counter", 1)
  574. ... return!
  575. ... """
  576. >>> attr_assign_code("models/my_sc", "t1", "script", code)
  577. .. function:: attr_delete(model_name, ID, attr)
  578. Unset the attribute *attr* of model element *ID* in the model *model_name*.
  579. This is not necessary when assigning a new value, as *attr_assign* automatically does this when required.
  580. As such, this only is useful when dealing with an optional attribute, or when you want to create a non-conforming model.
  581. Examples:
  582. * To unset the name attribute of a place.
  583. >>> attr_delete("models/my_pn", "p1", "name")
  584. Model Query
  585. ^^^^^^^^^^^
  586. .. function:: all_instances(model_name, type_name)
  587. Returns a list of all instances of type *type_name* in the model *model_name*.
  588. This includes indirect instances, which are only typed by type_name through inheritance.
  589. Examples:
  590. * To get all places in a PetriNet model.
  591. >>> all_instances("models/my_pn", "Place")
  592. ["p1", "__12345"]
  593. * To get all nodes that can be instantiated directly for PetriNets.
  594. >>> all_instances("formalisms/PetriNets", "Class")
  595. ["Place", "Transition", "P2T", "T2P"]
  596. .. function:: connections_between(model_name, source_element, target_element)
  597. Returns a list of all allowed connection types between *source_element* and *target_element* in a specified model.
  598. Both elements need to be part of the same model, as otherwise a merged model should be created beforehand.
  599. Examples:
  600. * To read out the allowed connections between elements "p1" and "t1".
  601. >>> connections_between("models/my_pn", "p1", "t1")
  602. ["P2T"]
  603. * To read out the allowed connections from the Place class to itself.
  604. >>> connections_between("formalisms/PetriNets", "Place", "Place")
  605. ["Association", "Inheritance"]
  606. .. function:: read_outgoing(model_name, ID, typename)
  607. Returns a list of all outgoing associations of *ID*, typed by *typename*, in model *model_name*.
  608. Typename can be set to the empty string to indicate that all types must match.
  609. Note that this returns the association itself, **NOT** the destination.
  610. Examples:
  611. * To get all arcs starting in place p1.
  612. >>> read_outgoing("models/my_pn", "p1", "P2T")
  613. ["p1_to_t1"]
  614. * To get all allowed connections starting in a Place.
  615. >>> read_outgoing("formalisms/PetriNets", "Place", "Association")
  616. ["P2T", "InhibitorArc"]
  617. .. function:: read_incoming(model_name, ID, typename)
  618. Returns a list of all incoming associations of *ID*, typed by *typename*, in model *model_name*.
  619. Typename can be set to the empty string to indicate that all types must match.
  620. Note that this returns the association itself, **NOT** the source.
  621. Examples:
  622. * To get all arcs going to place p1.
  623. >>> read_incoming("models/my_pn", "p1", "T2P")
  624. ["t1_to_p1"]
  625. * To get all allowed connections going to a Place.
  626. >>> read_incoming("formalisms/PetriNets", "Place", "Association")
  627. ["T2P"]
  628. .. function:: read_association_source(model_name, ID)
  629. Returns the identifier of the source of association *ID* in model *model_name*.
  630. Examples:
  631. * To read out the source of the P2T link.
  632. >>> read_association_source("formalisms/PetriNets", "P2T")
  633. "Place"
  634. * To read out the source of an arc.
  635. >>> read_association_source("models/my_pn", "p1_to_t1")
  636. "p1"
  637. .. function:: read_association_destination(model_name, ID)
  638. Returns the identifier of the target of association *ID* in model *model_name*.
  639. Examples:
  640. * To read out the target of the P2T link.
  641. >>> read_association_destination("formalisms/PetriNets", "P2T")
  642. "Transition"
  643. * To read out the target of an arc.
  644. >>> read_association_destination("models/my_pn", "p1_to_t1")
  645. "t1"
  646. Service Management
  647. ^^^^^^^^^^^^^^^^^^
  648. .. function:: service_register(name, function)
  649. Registers the current client-side thread as a service in the Modelverse.
  650. The specified *function* will be executed when users make a request to the service specified by *name*.
  651. This function will be executed on a different thread for each incoming request for a new instance of the service.
  652. If required, synchronization must be built in manually.
  653. After making this call, the thread should stay alive, but can do whatever other operation is required.
  654. Note that, upon termination, this client **MUST** call the *service_stop()* function, as otherwise the Modelverse is not notified of this service deregistering.
  655. The service is initially invoked with a Modelverse port to use for communication.
  656. This port must be used in all requests made to the Modelverse in the context of this service.
  657. The only two supported operations now are *service_set* (send data to the Modelverse) and *service_get* (fetch data from the Modelverse).
  658. Examples:
  659. * To register a Fibonacci service.
  660. >>> def fibonacci_service(port):
  661. ... def fibonacci(value):
  662. ... if value <= 2:
  663. ... return 1
  664. ... else:
  665. ... return fibonacci(value-1) + fibonacci(value-2)
  666. ... service_set(port, fibonacci, service_get(port))
  667. >>> service_register("fibonacci", fibonacci_service)
  668. .. function:: service_stop()
  669. Stops the current service from being available to the Modelverse.
  670. Existing connections will not be forcibly terminated, and these run on separate threads.
  671. Examples:
  672. * To stop the currently executing service.
  673. >>> service_stop()
  674. .. function:: service_get(port)
  675. When running a serivce, it is often necessary to request data from the Modelverse.
  676. This data can be sent whatever way in the Modelverse.
  677. Different Modelverse executions are identified by their differing *port*, which is received upon the client invoking the external service.
  678. Note that this call is blocking.
  679. That is, only when data is available, will this function return.
  680. Examples:
  681. * To get data from a currently executing Modelverse client.
  682. >>> service_get(port)
  683. 5
  684. .. function:: service_set(port, value)
  685. When running a service, it is often necessary to send data back to the Modelverse.
  686. This can be either for conversing with the user (e.g., prompting), or for sending the result back.
  687. Any primitive value can be sent to the Modelverse as a value.
  688. Examples:
  689. * To send the result back to the Modelverse.
  690. >>> service_set(port, 5)
  691. .. function:: service_poll(port)
  692. Polls whether there is any input for the service we are hosting for communication handle *port*.
  693. This returns either True or False, to get the actual data, a *service_get(port)* is required.
  694. This function does not block when there is not data to be read.
  695. Examples:
  696. * To check whether there is any data for our port, when there is none.
  697. >>> service_poll(port)
  698. False
  699. * To check whether there is any data for our port, when there is some.
  700. >>> service_poll(port)
  701. True
  702. Context Management
  703. ^^^^^^^^^^^^^^^^^^
  704. .. function:: alter_context(model_name, metamodel_name)
  705. Registers the model *model_name* to be interpreted in the context of *metamodel_name* as its metamodel.
  706. All future requests to the model will be made, using the assumption that the model is typed by the specified metamodel.
  707. When this is not the case, the request will throw an exception.
  708. Most of the time, this call must not be manually made, unless switching between metamodels, or when it is the first time using the model.
  709. The context is automatically deduced from the previous commands: adding the model with a specific metamodel, automatically puts the model in that context.
  710. When the model is opened for the first time, and it was not created by the client itself (e.g., by someone else, or through a model transformation), the metamodel needs to be specified.
  711. Examples:
  712. * To change the default metamodel of PetriNets to Bottom.
  713. >>> alter_context("formalisms/PetriNets", "formalisms/Bottom")
  714. .. function:: reset_context()
  715. Clears all previously registered context models.
  716. This is useful for when a model is removed automatically, without the knowledge of the client.
  717. When the context is reset, no metamodel is stored, and opening a model will then cause searching for the registered metamodel.
  718. Note that if a model still has an old metamodel registered, this might cause exceptions when opening the model, since the old metamodel is tried.
  719. Exceptions
  720. ^^^^^^^^^^
  721. Below is a list of all exceptions that the wrappers can raise, together with a summary of the kind of error that occured.
  722. For each exception, its superclasses are also indicated after the colon.
  723. .. exception:: ModelverseException
  724. Generic Modelverse Exception, of which all others inherit.
  725. This should be the only type of exception that the wrapper can raise.
  726. General Python exceptions could theoretically still occur, though this likely is a bug in the wrapper.
  727. This exception itself will never occur: it is abstract.
  728. .. exception:: SuperclassAttribute : ModelverseException
  729. The attribute that is being modified, was defined in a superclass, even though it is also available for this class.
  730. To prevent unexpected changes, the change to the attribute is disallowed and should be invoked on the superclass that actually defines the attribute.
  731. Examples:
  732. * Modifying the type of the weight attribute is disallowed, as the attribute was defined on an abstract class for both the P2T and T2P associations.
  733. >>> attribute_type("formalisms/PetriNets", "P2T", "weight", "Integer")
  734. SuperclassAttribute()
  735. .. exception:: CallbackOnEmptySignature : ModelverseException
  736. An activity is being added which has an empty signature for input and output metamodels, while it still defines an operation on the merged metamodel.
  737. As there is no merged metamodel, since there are no models being manipulated, it is impossible to perform the specified operation
  738. Examples:
  739. * When adding a manual operation without signature, but providing an operation
  740. >>> def operation(model):
  741. ... pass
  742. >>> transformation_add_MANUAL({}, {}, "users/user/test/a", operation)
  743. CallbackOnEmptySignature()
  744. .. exception:: NotAModel : ModelverseException
  745. The requested location does not contain a model, but is instead a folder, while a model was expected.
  746. Examples:
  747. * When creating a new instance of a "folder" metamodel
  748. >>> model_add("my_new_model", "formalisms")
  749. NotAModel()
  750. .. exception:: UserNotInGroup : ModelverseException
  751. The specified user is not part of the group, whereas it should be a member of this group.
  752. Examples:
  753. * When kicking a user from a group, while the user is not even a member.
  754. >>> group_kick("group_A", "user_B")
  755. UserNotInGroup()
  756. .. exception:: IncorrectFormat : ModelverseException
  757. An incorrect format is specified for some input.
  758. Examples:
  759. * When setting the permissions of a model, this should be a string containing three characters representing either 0, 1, or 2.
  760. >>> permission_modify("formalisms", "003")
  761. IncorrectFormat()
  762. * When querying for all transformations satisfying an input and output signature, they cannot both be empty, as that would return all transformations in the Modelverse.
  763. >>> transformation_between({}, {})
  764. IncorrectFormat()
  765. .. exception:: SignatureMismatch : ModelverseException
  766. Generic exception for when the signature that is provided does not match the expected signature in the Modelverse.
  767. This is superclass for many more specific exceptions, but can also occur in its own right.
  768. Examples:
  769. * When executing a process with a model mapping that contains an entry that is not used by the process.
  770. >>> process_execute("users/user/test/empty_process", {"a": "models/a"})
  771. SignatureMismatch()
  772. .. exception:: EmptySignature : SignatureMismatch
  773. Adding a model transformation with an empty input and output signature is not allowed, as no metamodel can be determined for RAMification and future execution.
  774. Examples:
  775. * When adding an empty model transformation.
  776. >>> transformation_add_MT({}, {}, "users/user/test/a", "...")
  777. EmptySignature()
  778. .. exception:: SourceModelNotBound : SignatureMismatch
  779. Executing an activity with an incomplete input signature.
  780. Examples:
  781. * When executing a reachability analysis activity, without specifying the petri net model as input.
  782. >>> transformation_execute_AL("users/user/reachability_analyse", {}, {"reachability_graph": "users/user/reachability_graph"})
  783. SourceModelNotBound()
  784. .. exception:: TargetModelNotBound : SignatureMismatch
  785. Executing an activity with an over-specified output signature.
  786. Examples:
  787. * When executing a reachability analysis activity, a second (undefined) output key is specified in the invocation.
  788. >>> transformation_execute_AL("users/user/initialize_pn", {}, {"pn": "users/user/pn", "reachability": "users/user/reachability"})
  789. TargetModelNotBound()
  790. .. exception:: DifferingModelsForKey : SignatureMismatch
  791. When the input and output signature of an activity are in conflict, because a key has a different type in the output than the input.
  792. Examples:
  793. * If the key A switches type in the input and output signature of a to be created activity.
  794. >>> transformation_add_MANUAL({"A": "users/user/A"}, {"A": "users/user/B"}, "users/user/activity")
  795. DifferingModelsForKey()
  796. .. exception:: TypeMismatch : SignatureMismatch
  797. The type of a model in the input or output signature does not match with the expected type.
  798. Examples:
  799. * When executing an activity that took an instance of "formalisms/PetriNets" as input, gets an instance of "formalisms/ProcessModel" instead.
  800. >>> transformation_execute_AL("users/user/reachability_analyse", {"PN": "users/user/my_pm"}, {})
  801. TypeMismatch()
  802. .. exception:: UnknownError : ModelverseException
  803. Unknown exception has occured.
  804. This is likely something wrong with the connection, such as the Modelverse that suddenly disconnected, or the request timed out.
  805. Note that this exception cannot be raised during *init*, as then the connection is nicely wrapped in a *ConnectionError*.
  806. Examples:
  807. * When the Modelverse is suddenly killed during execution, while there was an outstanding request.
  808. >>> element_list("formalisms/PetriNets") # <-- Modelverse killed during execution
  809. UnknownError()
  810. .. exception:: UnknownM3 : ModelverseException
  811. The M3 level of the model could not be determined, indicating that the metametamodel is not recognized as such.
  812. Examples:
  813. * When instantiating a model at the M1 level again
  814. >>> model_add("M2", "formalisms/SimpleClassDiagrams")
  815. >>> model_add("M1", "M2")
  816. >>> model_add("wrong", "M1")
  817. UnknownM3()
  818. .. exception:: UnknownIdentifier : ModelverseException
  819. The specified identifier could not be resolved in the Modelverse.
  820. This is an abstract exception, for which several specializations exist.
  821. The exception always contains the identifier causing the problem, as there might be multiple identifiers used in a single request.
  822. .. exception:: CompilationError : ModelverseException
  823. Error in the HUTN compiler during compilation.
  824. This is mostly caused by a malformed expression in the specified code.
  825. The compilation error is contained in the content of the exception.
  826. Examples:
  827. * When assigning a code block which cannot be parsed as action language.
  828. >>> attr_assign_code("models/my_pn", "p1", "tokens", "1")
  829. CompilationError("Parsing error at line 1: ...")
  830. .. exception:: NotAnActivity : ModelverseException
  831. An activity model was expected, but the provided model was not an executable activity.
  832. Examples:
  833. * When an ordinary model is passed to the transformation execution.
  834. >>> transformation_execute_AL("formalisms/PetriNets", {}, {})
  835. NotAnActivity()
  836. .. exception:: NotAProcess : ModelverseException
  837. A process model was expected, but the provided model was not typed as such.
  838. Examples:
  839. * When executing an ordinary model as if it were a process.
  840. >>> process_execute("formalisms/PetriNets", {})
  841. NotAProcess()
  842. .. exception:: NotAValidProcess : ModelverseException
  843. A process model was expected, and received, but it did not completely conform to the metamodel.
  844. For example, some constraints were not valid, no initial node was found, and so on.
  845. Examples:
  846. * When a process model is being executed that has not a single "Start" instance.
  847. >>> process_execute("models/my_empty_pm", {})
  848. NotAValidProcess()
  849. .. exception:: UnknownAttribute : UnknownIdentifier
  850. The specified attribute does not exist for this element.
  851. While the attribute might exist as a type for the lower meta-level, this only looks at the current level.
  852. Examples:
  853. * When assigning a non-existing attribute.
  854. >>> attr_assign("models/my_pn", "p1", "capacity", 2)
  855. UnknownAttribute("capacity")
  856. .. exception:: UnknownElement : UnknownIdentifier
  857. The specified model element is unknown in the model.
  858. Examples:
  859. * When instantiating an element of which the type element does not exist.
  860. >>> instantiate("models/my_pn", "CapacityConstrainedPlace")
  861. UnknownElement()
  862. .. exception:: UnknownModel : UnknownIdentifier
  863. The specified model can not be resolved in the Modelverse.
  864. While the model might technically exist, this exception merely indicates that it cannot be referred to with the specified identifier.
  865. Examples:
  866. * When trying to execute a non-existing transformation.
  867. >>> transformation_execute_MT("models/pn_optimize", {"pn": "models/my_pn"}, {"pn": "models/my_optimized_pn"})
  868. UnknownModel("pn_optimize")
  869. .. exception:: UnknownLocation : UnknownIdentifier
  870. The specified location could not be resolved, indicating that no element exists at that location.
  871. Note that there is no information as to the level at which the resolution goes wrong (e.g., a parent folder might already not exist).
  872. Examples:
  873. * When reading out the permissions of a non-existing location.
  874. >>> read_permissions("a/b/c/d/e/f/g")
  875. UnknownLocation()
  876. .. exception:: UnknownGroup : UnknownIdentifier
  877. The specified group does not exist.
  878. Examples:
  879. * When deleting a group that doesn't exist.
  880. >>> group_delete("no_such_group")
  881. UnknownGroup()
  882. .. exception:: UnknownUser : UnknownIdentifier
  883. The specified user does not exist.
  884. Examples:
  885. * When making a (non-existing) user join a group.
  886. >>> group_join("existing_group", "non_existing_user")
  887. UnknownUser()
  888. .. exception:: ConnectionError : ModelverseException
  889. Error during initialization of the connection to the Modelverse.
  890. The actual error is enclosed in the exception content.
  891. Despite possibly multiple errors, the *init* call will try for at least the amount of time specified in the timeout.
  892. This is done as the Modelverse might still be booting up and is not yet listening to incoming connections.
  893. Examples:
  894. * When trying to connect to a server which doesn't exist.
  895. >>> init("http://www.modelverse.be")
  896. ConnectionError()
  897. .. exception:: ExistsError : ModelverseException
  898. Abstract exception that indicates that a requested element to be created already exists.
  899. Never occurs on its own.
  900. .. exception:: AttributeExists : ExistsError
  901. A to-be-created attribute already exists, and could therefore not be created (or renamed to this).
  902. Examples:
  903. * Renaming an attribute to one that already exists.
  904. >>> attribute_name("formalisms/PetriNets", "Place", "tokens", "name")
  905. AttributeExists()
  906. .. exception:: ElementExists : ExistsError
  907. A to-be-created element already exists, and could therefore not be created.
  908. Examples:
  909. * Instantiating another element with ID Place, which already exists.
  910. >>> instantiate("formalisms/PetriNets", "Class", ID="Place")
  911. ElementExists()
  912. .. exception:: FolderExists : ExistsError
  913. The folder you want to create already exists.
  914. Examples:
  915. * When creating a folder that already exists.
  916. >>> folder_create("formalisms")
  917. FolderExists()
  918. .. exception:: GroupExists : ExistsError
  919. The group already exists.
  920. Examples:
  921. * When creating a new group that already exists.
  922. >>> group_create("users")
  923. GroupExists()
  924. .. exception:: UserExists : ExistsError
  925. The user already exists or is already a member of the group.
  926. Examples:
  927. * When adding a user to a group, while the user is already in there.
  928. >>> group_join("users", "user")
  929. UserExists()
  930. .. exception:: PermissionDenied : ModelverseException
  931. Permission denied to a specific artefact.
  932. This is an abstract exception that is further subclassed by other exceptions.
  933. .. exception:: ReadPermissionDenied : PermissionDenied
  934. Current user has no permissions to read the specified model or folder.
  935. Examples:
  936. * When reading the administration model, for which we have no permission as it contains user password hashes.
  937. >>> element_list("administration/core")
  938. ReadPermissionDenied()
  939. * When instantiating a non-readable metamodel, as we require access to the enclosed information.
  940. >>> model_add("my_model", "non-readable-model")
  941. ReadPermissionDenied()
  942. * When listing a folder that we do not have read permissions for.
  943. >>> model_list("users/admin/")
  944. ReadPermissionDenied()
  945. .. exception:: WritePermissionDenied : PermissionDenied
  946. Current user has no permissions to write to the specified model or folder.
  947. Examples:
  948. * When writing changes to a core formalism, which ordinary users cannot modify.
  949. >>> instantiate("formalisms/SimpleClassDiagrams", "Class")
  950. WritePermissionDenied()
  951. * When creating a model in a non-writeable folder.
  952. >>> model_add("formalisms/abc", "formalisms/SimpleClassDiagrams")
  953. WritePermissionDenied()
  954. .. exception:: ExecutePermissionDenied : PermissionDenied
  955. Current user has no permissions to execute the specified activity or process.
  956. For now, execute permissions equal read permissions, although this is prone to change in the future.
  957. Examples:
  958. * When executing a model transformation that we are not allowed to execute.
  959. >>> transformation_execute_MT("users/admin/my_transformation", {}, {})
  960. ExecutePermissionDenied()
  961. .. exception:: UserPermissionDenied : PermissionDenied
  962. Current user has no permission to make changes to the specified user or to the permissions of models (which can only be modified by the owner).
  963. Examples:
  964. * When changing the password of another user.
  965. >>> user_password("admin", "other_one")
  966. UserPermissionDenied()
  967. * When modifying the permissions of a non-owned model.
  968. >>> permission_modify("formalisms/SimpleClassDiagrams", "222")
  969. UserPermissionDenied()
  970. .. exception:: GroupPermissionDenied : PermissionDenied
  971. Current user has no permission to modify properties of the specified group.
  972. Examples:
  973. * When adding a member to a group that we are not an administrator of.
  974. >>> group_join("some_group", "other_user")
  975. GroupPermissionDenied()
  976. .. exception:: AdminPermissionDenied : PermissionDenied
  977. Current user has no administrator privileges and cannot change the administrator level of another user.
  978. Examples:
  979. * When promoting another user as admin, while we are not an administrator ourselves.
  980. >>> admin_promote("other_user")
  981. AdminPermissionDenied()
  982. .. exception:: UnknownMetamodellingHierarchy : ModelverseException
  983. The requested metamodelling hierarchy could not be resolved.
  984. This is likely because no typing relation could be found between the specified model and metamodel, or if this typing relation is only a partial mapping that cannot be automatically resolved.
  985. Note that this exception should never occur when opening the model using *formalisms/Bottom*.
  986. Examples:
  987. * When opening an arbitrary model as a SimpleClassDiagrams model.
  988. >>> alter_context("models/my_pn", "formalisms/SimpleClassDiagrams")
  989. >>> element_list("models/my_pn")
  990. UnknownMetamodellingHierarchy()
  991. .. exception:: NotAnAssociation : ModelverseException
  992. The requested element is not an association or link, although it should be.
  993. Examples:
  994. * When instantiating an association with a type that is not an association.
  995. >>> instantiate("models/my_pn", "Place", edge=("p1", "p2"))
  996. NotAnAssociation
  997. SCCD
  998. ----
  999. An SCCD interface is created, which has exactly the same interface.
  1000. Actually, the Python interface presented before is built using this SCCD interface.
  1001. The operations are identical and have the same signature, although they are invoked differently.
  1002. To make a request to the Modelverse, an event has to be raised called *action*.
  1003. The first parameter of this event is the operation to invoke, as a string (*e.g.*, ``"read_info"``).
  1004. The second parameter specifies an ID that can be used in the response event to identify which event this is the response to, or set to *None*.
  1005. The third parameter is a list of all the parameters to this function (*e.g.*, ``["my_pn"]``).
  1006. For example::
  1007. <raise event="action">
  1008. <parameter expr="'read_info'"/>
  1009. <parameter expr="None"/>
  1010. <parameter expr="['my_pn']"/>
  1011. </raise>
  1012. When this request is processed, the Modelverse will reply with the *mv_result* event, containing the result.
  1013. The first parameter is the ID passed on in the original request, or *None* if the ID was set to *None*.
  1014. The second parameter contains the result.
  1015. It is possible to wait on this event as follows::
  1016. <transition event="mv_result" target=".">
  1017. <parameter name="ID"/>
  1018. <parameter name="result"/>
  1019. <script>
  1020. print("Response: " + str(result))
  1021. </script>
  1022. </transition>
  1023. If something went wrong, the event *mv_exception* will be raised instead::
  1024. <transition event="mv_exception" target=".">
  1025. <parameter name="ID"/>
  1026. <parameter name="exception_name"/>
  1027. <parameter name="exception"/>
  1028. <script>
  1029. print("Exception: " + str(exception_name))
  1030. print(exception)
  1031. </script>
  1032. </transition>
  1033. The use of the SCCD interface is particularly interesting in case asynchronous communication is required.
  1034. In contrast to the Python interface, requests don't block, making it possible to do additional computations in the background, or even work in a multi-threaded context (using the *ID* parameter).
  1035. Note, however, that the SCCD interface is more difficult to use with callbacks and other statecharts, as you will have to manually make the linkage with ports.
  1036. For this, we refer to the implementation.
  1037. Custom
  1038. ------
  1039. Other wrappers can be made as desired, in whatever language required.
  1040. This is due to the fact that the Modelverse communicates only through XML/HTTPRequests.
  1041. As such, all languages that support this, can simply mimic the interface used by any of the implemented wrappers.
  1042. We refer to the Python wrapper as the reference implementation.