1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267 |
- Wrappers
- ========
- Several wrappers can be defined for the Modelverse, as the Modelverse is merely a service running externally.
- To communicate effectively, and automatically, a programming language wrapper is recommended.
- Nonetheless, it is possible to communicatie manually as well.
- These are some of the implemented wrappers.
- Prompt
- ------
- The simplest wrapper is the prompt wrapper, which merely sends the input and output directly to the user.
- This wrapper has almost no code, but requires users to manually decide which content to send next.
- It has no built-in integration with any model or action language compilers.
- Nonetheless, it is an easy way to test out the raw communication protocol manually.
- Python
- ------
- The first real wrapper is the Python-based wrapper.
- It provides a set of functions for use by Python code.
- 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.
- An overview of all functions and associatied exceptions is provided below.
- All operations happen *synchronously*, meaning that they block until the Modelverse has performed the requested operation.
- Note that some functions are only applicable in a certain *context*.
- 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.
- For each function, we provide an example to indicate how this operation can be used.
- In these first examples, assume that all referenced elements are present, all permissions are granted, etc.
- For each exception, then, we provide an example where it can occur.
- In these examples, the problem is often caused by an non-existing element or unsatisfied permissions.
- From the example, it should be clear what the problem is.
- Functions
- ^^^^^^^^^
- .. function:: init(address_param="http://127.0.0.1:8001", timeout=20.0)
- Start up the connection to the Modelverse, residing at *address_param*.
- This connection is an XML/HTTPRequest and will start up a new task at the Modelverse.
- Retries for *timeout* seconds until giving up.
- The timeout includes all HTTP errors, and will therefore keep retrying even on failed attempts.
- As this request is synchronous, like all others, it will block until a connection has been established.
- Examples:
- * To create a connection to a local Modelverse.
- >>> init()
- * To create a connection to the Modelverse running remotely.
- >>> init("http://modelverse.uantwerpen.be:8001")
- .. function:: login(username, password)
- Explanation
- Logs in the currently active Modelverse connection to the specified *username* and *password*.
- If the user does not exist, it will create a new user with the specified password.
- If the user already exists, it will try to log in with the provided password.
- Examples:
- * To login as user user1, with the specified password.
- >>> login("user1", "my_password")
- .. function:: model_add(model_name, metamodel_name, model_code=None)
- Explanation
- Upload a new model that can later be referred to as *model_name*, conforming to *metamodel_name*.
- The model itself is stored in the string *model_code*.
- This string is parsed using the HUTN compiler and subsequently sent to the Modelverse.
- When *model_code* is empty, an empty model is created.
- Examples:
- * To create a new model called PetriNets, conforming to SimpleClassDiagrams, and load the model stored in models/PetriNets.mvc.
- >>> model_add("formalisms/PetriNets", "formalisms/SimpleClassDiagrams", open("models/PetriNets.mvc", "r").read())
- * To create a minimal instance of the language afterwards, which only contains a single place (and no attributes).
- >>> model_add("models/my_pn", "formalisms/PetriNets", "Place p1 {}")
- * To create a less minimal instance of the language, stored in models/my_pn2.mvc.
- >>> model_add("models/my_pn2", "formalisms/PetriNets", open("models/my_pn2.mvc", "r").read())
- .. function:: upload_code(code)
- Upload a string of *code* in the Action Language formalism.
- This piece of code is compiled with the HUTN compiler and sent to the Modelverse directly.
- Makes the assumption that the **construct_function()** operation is currently running on the Modelverse, as otherwise the data will be misinterpreted.
- 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).
- Examples:
- * To upload the file models/code.alc.
- >>> upload_code(open("models/code.alc", "r").read())
- * To upload a code fragment inline.
- >>> upload_code("Void function a():\nreturn!")
- .. function:: model_delete(model_name)
- Delete the model referred to by name *model_name*.
- This is a non-cascading delete, with almost no checks: model transformations depending on this model will likely become corrupted.
- Examples:
- * To delete a previously created model.
- >>> model_delete("models/my_pn2")
- * Or to delete a metamodel, which is itself just a model.
- >>> model_delete("formalisms/PetriNets")
- * To delete a full folder.
- >>> model_delete("formalisms")
- .. function:: model_list(location)
- Returns a list of all models existing in the specified folder.
- Sub-folders can be recognized because they have a trailing forward slash in their name.
- Examples:
- * To get a list of all models in the formalisms directory.
- >>> model_list("formalisms")
- ["PetriNets", "SimpleClassDiagrams", "Bottom", "Tracability", ...]
- .. function:: model_list_full(location)
- Returns a detailed list of all models existing in the specified folder in the Modelverse.
- This list includes information on permissions, owner, and group.
- Examples:
- * To get a detailed list of all currently present models.
- >>> model_list_full("models")
- [("my_pn", "user1", "users", "200"), ("my_pn2", "user1", "users", "200"), ...]
- .. function:: verify(model_name, metamodel_name)
- Verify whether *model_name* conforms to *metammodel_name*, as both stored in the Modelverse.
- Returns either "OK" if the model conforms, or a string specifying the reason for non-conformance.
- Examples:
- * Verifying a conforming model.
- >>> verify("formalisms/PetriNets", "formalisms/SimpleClassDiagrams")
- OK
- * Or verify using the alternative conformance relation (conformance bottom).
-
- >>> verify("formalisms/PetriNets", "formalisms/Bottom")
- OK
- * Verifying a non-conforming model.
- >>> verify("models/my_pn")
- Lower cardinality violation for attribute "name" at Place p1.
- .. function:: model_overwrite(model_name, new_model_code=None)
- Overwrites the model previously known under the name *model_name* with the model code in *new_model_code*.
- This operation differs from first deleting the model and then recreating it, as all metadata of the model is kept, such as access permissions.
- The new model can be kept empty, in which case the model will be cleared.
- Examples:
- * To overwrite the PetriNets metamodel with a newer version, thereby also updating the metamodel of all existing instances ("my_pn" and "my_pn2").
- >>> model_overwrite("formalisms/PetriNets", open("models/PetriNets2.mvc", "r").read())
- * To overwrite an existing PetriNets instance.
- >>> model_overwrite("models/my_pn", """Place p2 {}""")
- .. function:: user_logout()
- Logs out the current user, thereby closing the task.
- Subsequent operations will no longer have any effect, as the task was terminated.
- To log in as a different user, the *init* operation has to be executed again.
- Examples:
- * To log out the current user and allow future logins by this user.
- >>> user_logout()
- .. function:: user_delete()
- Delete the current user and thereafter log out.
- This removes the current user, making it impossible to log in as this user again.
- Existing models tied to this user, such as those the user is an owner of, remain bound to the (removed) user.
- While it is possible to recreate a new user with the same name, model permissions will not be inherited to this new user with the same name.
- Examples:
- * To delete the current user, and make all owned models owner-less.
- >>> user_delete()
- .. function:: model_render(model_name, mapper_name)
- Render the model by name of *model_name* using the mapper by name of *mapper_name*.
- Both parameters have to be known models in the Modelverse.
- Outputs a JSON representation of the rendered model.
- This is basically just a shortcut for executing the specified operation and reading out the resulting model with a JSON representation.
- Examples:
- * To render the PetriNets instance using the PetriNetsMapper.
- >>> model_render("models/my_pn", "formalisms/PetriNetsMapper")
- [{"id": "__12345", "type": "Ellipse", "x": 100, "y": 150, "height": 20, "width: "20"}]
- .. function:: transformation_between(source, target)
- List all transformations that originate at *source* and end at *target*.
- Transformations can still be selected, even if they take more source models than those specified in the parameters.
- Examples:
- * To fetch all endogenous transformations on PetriNets, assuming that some were previously defined.
- >>> transformation_between("formalisms/PetriNets", "formalisms/PetriNets")
- ["PN_simulate", "PN_optimize"]
- * To fetch all transformations from a DSL to PetriNets, assuming that multiple people created different denotational semantics.
- >>> transformation_between("RPGame", "PetriNets")
- ["denotational1", "denotational_2", "denotational_3"]
- .. function:: transformation_add_MT(source_metamodels, target_metamodels, operation_name, code, callback=lambda: None)
- Create a new model transformation operation.
- The new transformation takes *source_metamodels* as input, and generates *target_metamodels* as output.
- Both parameters are dictionaries of the form {name: metamodel_name}.
- The name is used later on in the model transformation as a prefix to the type.
- A single metamodel_name can be used for multiple names.
- Note that the target metamodel names may overlap with the source metamodel names, but the metamodel type should be identical.
- The operation is henceforth known by *operation_name* and is provided as a model in the string *code*.
- Optionally, a callback is defined which performs some operations on the merged metamodel, for example to define tracability links between (previously unrelated) metamodels.
- In the background, this operation does all necessary RAMification and model merging.
- Examples:
- * To create a new model transformation for PetriNets simulation.
- >>> transformation_add_MT({"pn": "formalisms/PetriNets"}, {"pn": "formalisms/PetriNets"}, "models/pn_simulate", open("models/PN_simulate.mvc", "r").read())
- * To create a model transformation from a DSL to PetriNets, which requires tracability links.
- >>> def tracability_links():
- ... instantiate("Association", ID="Tile2Place", ("dsl/Tile", "pn/Place"))
- ... instantiate("Association", ID="Dirrection2Transition", ("dsl/Direction", "pn/Transition"))
- >>> transformation_add_MT({"dsl": "formalisms/RPGame"}, {"pn": "formalisms/PetriNets"}, "models/denotational_1", open("models/denotational_1.mvc", "r").read(), tracability_links)
- * To create a multi-input model transformation.
- >>> 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())
- .. function:: transformation_add_AL(source_metamodels, target_metamodels, operation_name, code, callback=lambda: None)
- Creates a new action language operation.
- Similar to *transformation_add_MT*, but now does not require RAMification.
- The *code* parameter also is not specified as a Modelverse model (.mvc), but as action language (.alc).
- Examples:
- * To create a new action language operation for PetriNets reachability analysis.
- >>> transformation_add_AL({"pn": "formalisms/PetriNets"}, {"graph": "formalisms/ReachabilityGraph"}, "models/pn_analyze", open("models/PN_reachability.alc", "r").read())
- * To create an action language operation from a Scheduling DSL to a list, which requires tracability links.
- >>> def tracability_links():
- ... instantiate("Association", ID="Task2Event", ("schedule/Task", "list/Event"))
- >>> transformation_add_AL({"schedule": "formalisms/SchedulingDSL"}, {"list": "formalisms/EventList"}, "models/sequentialize", open("models/sequentialize_schedule.alc", "r").read(), tracability_links)
- .. function:: transformation_add_MANUAL(source_metamodels, target_metamodels, operation_name, callback=lambda: None)
- Creates a new manual operation.
- Identical to *transformation_add_AL*, but does not take any code as content.
- Examples:
- * To create a manual refinement operation on PetriNets.
- >>> transformation_add_MANUAL({"pn": "formalisms/PetriNets"}, {"pn": "formalisms/PetriNets"}, "models/pn_refine")
- * To create a multi-input refinement operation on PetriNets.
- >>> transformation_add_MANUAL({"pn": "formalisms/PetriNets", "requirements": "formalisms/Requirements"}, {"pn": "formalisms/PetriNets"}, "models/pn_refine_req")
- .. function:: transformation_execute_AL(operation_name, input_models_dict, output_models_dict, statechart=None)
- Executes the Action Language model *operation_name* with *input_models_dict* as inputs and *output_models_dict* as outputs.
- For both dicts, the contents describe the mapping between the parameter names of the operation to the names in the Modelverse.
- Values in *input_models_dict* must be existing models, whereas values in *output_models_dict* can be non-existing upon invocation.
- A *statechart* tuple can be defined when the action language model requires user input or output.
- This is used to communicate with the executing code directly.
- For more information about this, refer to the execution of transformations.
- Examples:
- * To execute reachability analysis on an existing petri net.
- >>> transformation_execute_AL("models/pn_analyze", {"pn": "models/my_pn"}, {"graph": "models/my_pn_reachability"})
- .. function:: transformation_execute_MANUAL(operation_name, input_models_dict, output_models_dict, callback=lambda i: None)
- Executes the manual model operation *operation_name*.
- Furthermore, this is identical to *transformation_execute_AL*, with the exception of the *callback* function.
- In this case, the callback function can be just another series of Modelverse operations, though pertaining to a single model.
- As such, the *model_name* parameter of these operations **MUST** be set to *None*.
- Examples:
- * To execute a manual operation, which requires you to refine a PetriNets instance.
- >>> def callback():
- ... p1 = instantiate(None, "pn/Place")
- ... t1 = instantiate(None, "pn/Transition")
- ... instantiate(None, "pn/P2T", (p1, t1))
- >>> transformation_execute_MANUAL("models/pn_refine", {"pn": "models/my_pn"}, {"pn": "models/my_pn"}, callback)
- .. function:: transformation_execute_MT(operation_name, input_models_dict, output_models_dict, statechart=None)
- Executes the model transformation operation *operation_name*.
- Identical to *transformation_execute_AL*.
- Examples:
- * To execute a model transformation on a PetriNets instance, thereby putting the result in a different model.
- >>> transformation_execute_MT("models/pn_simulate", {"pn": "models/my_pn"}, {"pn": "models/my_simulated_pn"})
- .. function:: process_execute(process_name, prefix, callbacks)
- Execute the process model stored as *process_name*.
- All models are stored with their names prefixed with *prefix* to allow for multiple executions of the same model.
- Note that this applies to the resolution of input models as well.
- Optionally, a dictionary of *callbacks* can be defined with as key the operation that is being executed, and value the actual callback.
- The callbacks must be similarly defined just like how they were defined in the individual *transformation_execute* operations.
- This means that the callback might very well be a tuple for a statechart instance.
- Examples:
- * To execute a process model for the power window example.
- >>> process_execute("models/pm_powerwindow", "pw_")
- * To execute a process model for the power window example, which requires user input for some operations.
- >>> def refine_architecture():
- ... # Do some operation on the architecture model here
- ... node1 = instantiate(None, "Node")
- ... node2 = instantiate(None, "Node")
- ... instantiate(None, "Connection", (node1, node2))
- >>> def refine_control():
- ... # Do some operation on the control model here
- ... s1 = instantiate(None, "State")
- ... s2 = instantiate(None, "State")
- ... instantiate(None, "Transition", (s1, s2))
- >>> def refine_query():
- ... # Do some operation on the safety query model here
- ... p1 = instantiate(None, "Place")
- ... attr_assign(None, p1, "tokens", 2)
- >>> process_execute("models/pm_powerwindow", "pw_", {"models/refine_plant": refine_plant, "models/refine_control": refine_control, "models/refine_query": refine_query})
- .. function:: permission_modify(model_name, permissions)
- Change the permissions of *model_name* to *permissions*.
- The permissions is a string of three characters, each between 0 and 2.
- 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.
- Character 0 signifies no access, 1 read-only access, and 2 full read/write access.
- 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.
- Examples:
- * To modify the permissions of the PetriNets metamodel, allowing only the owner to read and write to it.
- >>> permission_modify("formalisms/PetriNets", "200")
- * To modify the permissions of a PetriNets model, granting everyone read/write access.
- >>> permission_modify("formalisms/PetriNets", "222")
- .. function:: permission_owner(model_name, owner)
- Change the owner of the model *model_name* to *owner*.
- 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.
- Examples:
- * To change the owning user of the PetriNets metamodel to user2.
- >>> permission_owner("formalisms/PetriNets", "user2")
- .. function:: permission_group(model_name, group)
- Change the owning group of the model *model_name* to *group*.
- The same remarks hold as for all other permission operations.
- Examples:
- * To change the owning group of the PetriNets metamodel to group1.
- >>> permission_group("formalisms/PetriNets", "group1")
- .. function:: group_create(group_name)
- Create a new group named *group_name*.
- You automatically become an administrator for this group.
- Examples:
- * To create a new group called group2.
- >>> group_create("group2")
- .. function:: group_delete(group_name)
- Delete the group named *group_name*.
- All users will automatically be kicked from the group, and all permissions previously granted by this group become void.
- Examples:
- * To delete the group group2.
- >>> group_delete("group2")
- .. function:: group_owner_add(group_name, user_name)
- Add user *user_name* as an owner of group *group_name*.
- This automatically makes the user join the specified group if this was not yet the case.
- Examples:
- * To add user user1 as a new owner, or group administrator, to group group1.
- >>> group_owner_add("group1", "user1")
- .. function:: group_owner_delete(group_name, user_name)
- Remove user *user_name* as an owner of group *group_name*.
- Examples:
- * To delete user1 as an owner, or group administrator, from group group1.
- >>> group_owner_delete("group1", "user1")
- .. function:: group_join(group_name, user_name)
- Have user *user_name* join the group *group_name* as an ordinary user.
- Examples:
-
- * To make user user1 join the group group1, of which the current user is an owner.
- >>> group_join("group1", "user1")
- .. function:: group_kick(group_name, user_name)
- Remove user *user_name* from the group *group_name*.
- If the user was an owner of the group, these permissions are also revoked.
- Examples:
- * To kick user user1 from group group1, of which the current user is an owner.
- >>> group_kick("group1, "user1")
- .. function:: admin_promote(user_name)
- Promote user *user_name* to admin status.
- Admin status grants users access to all operations, and provides all permission.
- Effectively, all groups and files have read/write access for the admin user, independent of the stored permissions.
- Examples:
- * To promote user1 to admin states.
- >>> admin_promote("user1")
- .. function:: admin_demote(user_name)
- Demote user *user_name* to ordinary user.
- Examples:
- * To demote user1 to a normal user.
- >>> admin_demote("user1")
- .. function:: element_list(model_name)
- Returns a list of all elements and their type specified in the model named *model_name*.
- This list can contain much more than only simple elements, but includes anonymous edges and attributes as well.
- It is therefore not recommended for general purpose use; use *element_list_nice* instead.
- Examples:
- * To get a list of all elements in the PetriNets metamodel.
- >>> element_list("formalisms/PetriNets")
- [("Place", "Class"), ("Transition", "Class"), ("P2T", "Association"), ("T2P", "Association"), ...]
- .. function:: types(model_name)
- Returns a list of all types usable in the model named *model_name*.
- This is similar to executing *element_list* on the metamodel of this model.
- It attempts to filter out most unusable elements.
- Examples:
- * To get a list of all types usable in the PetriNets metamodel (i.e., when altering the metamodel itself).
- >>> types("formalisms/PetriNets")
- ["Class", "Association", "SimpleAttribute", ...]
- .. function:: types_full(model_name)
- Returns a list of all types usable in the model named *model_name*.
- In contrast to *types*, this includes hidden elements as well (i.e., those starting with __)
- Examples:
- * To get a list of all types usable in the PetriNets metamodel (i.e., when altering the metamodel itself).
- >>> types("formalisms/PetriNets")
- ["Class", "Association", "SimpleAttribute", "__12345", ...]
- .. function:: read(model_name, ID)
- Read the content of model element *ID* in model *model_name*.
- This returns the type of the element, and the set of source and destination if the element is an edge (*None* otherwise).
- Examples:
- * To read out the P2T link in the PetriNets metamodel.
- >>> read("formalisms/PetriNets", "P2T")
- ["Association", ("Place", "Transition")]
- * To read out the Place node in the PetriNets metamodel.
- >>> read("formalisms/PetriNets", "Place")
- ["Class", None]
- * To read out some P2T instance in a PetriNets model.
- >>> read("my_pn", "p1_to_t1")
- ["P2T", ("p1", "t1")]
- * To read out some Place instance in a PetriNets model.
- >>> read("my_pn", "p1")
- ["Place", None]
- .. function:: read_attrs(model_name, ID)
- Return a dictionary of all attributes of model element *ID* in model *model_name*, containing their values.
- All values in the Modelverse are primitive types, and as such, this is also the case in this operation.
- The value is *None* in case the attribute is not set.
- Examples:
- * To read out the attributes of the Place class.
- >>> read_attrs("formalisms/PetriNets", "Place")
- {"lower_cardinality": None, "upper_cardinality": None}
- * To read out the attributes of a Place instance.
- >>> read_attrs("models/my_pn", "p1")
- {"name": "critical_section", "tokens": 1}
- .. function:: instantiate(model_name, typename, edge=None, ID="")
- Instantiate a new instance of *typename* in the model *model_name*.
- If the instance is an edge, provide a tuple containing the source and target as *edge*.
- 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).
- This operation returns the actually assigned ID.
- It is this ID that is used for all other operations on the model.
- Examples:
- * To create a new Place instance in a PetriNets model.
- >>> instantiate("models/my_pn", "Place")
- "__12345"
- * To create a new Place instance with a preferred ID, which is granted.
- >>> instantiate("models/my_pn", "Place", ID="critical_section")
- "critical_section"
- * To create a new Place instance with a preferred ID, which is not granted.
- >>> instantiate("models/my_pn", "Place", ID="critical_section")
- critical_section_12345"
- * To create a new P2T instance in a PetriNets model.
- >>> instantiate("models/my_pn", "P2T", ("p1", "t1"))
- "__12345"
- * To create a new concept in the PetriNets metamodel, which can later on be used in all instances immediately.
- >>> instantiate("formalisms/PetriNets", "Association", ("Place", "Transition"), ID="InhibitorArc")
- "InhibitorArc"
- >>> instantiate("models/my_pn", "InhibitorArc", ("p1", "t1"))
- "__12345"
- .. function:: delete_element(model_name, ID)
- Delete the element *ID* in the model *model_name*.
- This is a recursive delete, and all incoming and outgoing edges will be removed (recursively) as well.
- Examples:
- * To delete an existing element in a PetriNets model.
- >>> delete_element("models/my_pn", "critical_section")
- * To delete an existing exdge in a PetriNets model.
- >>> delete_element("models/my_pn", "p1_to_t1")
- * When deleting an element "p1", the arc "p1_to_t1" is also removed automatically.
- >>> delete_element("models/my_pn", "p1")
- >>> delete_element("models/my_pn", "p1_to_t1")
- UnknownIdentifierException("p1_to_t1")
- .. function:: attr_assign(model_name, ID, attr, value)
- Assign the value *value* to the attribute named *attr* of the element *ID* in the model named *model_name*.
- If the attribute already has an assigned value, the previous value is removed first.
- Examples:
- * To assign some attributes to a Place instance.
- >>> attr_assign("models/my_pn", "p1", "name", "my first place")
- >>> attr_assign("models/my_pn", "p1", "tokens", 1)
- * To assign some attributes to the Place class itself.
- >>> attr_assign("formalisms/PetriNets", "Place", "upper_cardinality", 1)
- .. function:: attr_assign_code(model_name, ID, attr, code)
- Assign the code block *code* to the attribute named *attr* of the element *ID* in the model named *model_name*.
- If the attribute already has an assigned value, the previous value is removed first.
- The assigned code is compiled to Action Language by the HUTN compiler.
- Examples:
- * To assign a piece of action code to a Statecharts transition, loaded from file.
- >>> attr_assign_code("models/my_sc", "t1", "script", open("models/t1_script", "r").read())
- * To assign a piece of action code to a Statecharts transition, defined inline.
- >>> code = \
- ... """
- ... Void function action(attributes : Element):
- ... dict_overwrite(attributes, "counter", 1)
- ... return!
- ... """
- >>> attr_assign_code("models/my_sc", "t1", "script", code)
- .. function:: attr_delete(model_name, ID, attr)
- Unset the attribute *attr* of model element *ID* in the model *model_name*.
- This is not necessary when assigning a new value, as *attr_assign* automatically does this when required.
- As such, this only is useful when dealing with an optional attribute, or when you want to create a non-conforming model.
- Examples:
- * To unset the name attribute of a place.
- >>> attr_delete("models/my_pn", "p1", "name")
- .. function:: read_outgoing(model_name, ID, typename)
- Returns a list of all outgoing associations of *ID*, typed by *typename*, in model *model_name*.
- Typename can be set to the empty string to indicate that all types must match.
- Note that this returns the association itself, **NOT** the destination.
- Examples:
- * To get all arcs starting in place p1.
- >>> read_outgoing("models/my_pn", "p1", "P2T")
- ["p1_to_t1"]
- * To get all allowed connections starting in a Place.
- >>> read_outgoing("formalisms/PetriNets", "Place", "Association")
- ["P2T", "InhibitorArc"]
- .. function:: read_incoming(model_name, ID, typename)
- Returns a list of all incoming associations of *ID*, typed by *typename*, in model *model_name*.
- Typename can be set to the empty string to indicate that all types must match.
- Note that this returns the association itself, **NOT** the source.
- Examples:
- * To get all arcs going to place p1.
- >>> read_incoming("models/my_pn", "p1", "T2P")
- ["t1_to_p1"]
- * To get all allowed connections going to a Place.
- >>> read_incoming("formalisms/PetriNets", "Place", "Association")
- ["T2P"]
- .. function:: read_association_source(model_name, ID)
- Returns the identifier of the source of association *ID* in model *model_name*.
- Examples:
- * To read out the source of the P2T link.
- >>> read_association_source("formalisms/PetriNets", "P2T")
- "Place"
- * To read out the source of an arc.
- >>> read_association_source("models/my_pn", "p1_to_t1")
- "p1"
- .. function:: read_association_destination(model_name, ID)
- Returns the identifier of the target of association *ID* in model *model_name*.
- Examples:
- * To read out the target of the P2T link.
- >>> read_association_destination("formalisms/PetriNets", "P2T")
- "Transition"
- * To read out the target of an arc.
- >>> read_association_destination("models/my_pn", "p1_to_t1")
- "t1"
- .. function:: service_register(name, function)
- Registers the current client-side thread as a service in the Modelverse.
- The specified *function* will be executed when users make a request to the service specified by *name*.
- This function will be executed on a different thread for each incoming request for a new instance of the service.
- If required, synchronization must be built in manually.
- After making this call, the thread should stay alive, but can do whatever other operation is required.
- Note that, upon termination, this client **MUST** call the *service_stop()* function, as otherwise the Modelverse is not notified of this service deregistering.
- The service is initially invoked with a Modelverse port to use for communication.
- This port must be used in all requests made to the Modelverse in the context of this service.
- The only two supported operations now are *service_set* (send data to the Modelverse) and *service_get* (fetch data from the Modelverse).
- Examples:
- * To register a Fibonacci service.
- >>> def fibonacci_service(port):
- ... def fibonacci(value):
- ... if value <= 2:
- ... return 1
- ... else:
- ... return fibonacci(value-1) + fibonacci(value-2)
- ... service_set(port, fibonacci, service_get(port))
- >>> service_register("fibonacci", fibonacci_service)
- .. function:: service_stop()
- Stops the current service from being available to the Modelverse.
- Existing connections will not be forcibly terminated, and these run on separate threads.
- Examples:
- * To stop the currently executing service.
- >>> service_stop()
- .. function:: service_get(port)
- When running a serivce, it is often necessary to request data from the Modelverse.
- This data can be sent whatever way in the Modelverse.
- Different Modelverse executions are identified by their differing *port*, which is received upon the client invoking the external service.
- Note that this call is blocking.
- That is, only when data is available, will this function return.
- Examples:
- * To get data from a currently executing Modelverse client.
- >>> service_get(port)
- 5
- .. function:: service_set(port, value)
- When running a service, it is often necessary to send data back to the Modelverse.
- This can be either for conversing with the user (e.g., prompting), or for sending the result back.
- Any primitive value can be sent to the Modelverse as a value.
- Examples:
- * To send the result back to the Modelverse.
- >>> service_set(port, 5)
- .. function:: user_password(user, password)
- Changes the password of user *user* to *password*.
- Permissions on this operation are of course rather strict:
- users can only modify their own password.
- Of course, an administrator can modify the password of every user.
- Active logins do not need to be refreshed, as the new username will automatically be used everywhere.
- From the next login, however, the new username has to be used, as the old one has been renamed.
- Examples:
- * To change the password of user *user1* to *my_password*.
- >>> user_password("user1", "my_password")
- .. function:: transformation_read_signature(transformation)
- Returns the signature of the transformation *transformation*.
- The signature consists of two dictionaries: the first specifies the input signature, and the second one specifies the output signature.
- 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.
- Examples:
- * To read out the signature of the "plant_refine" operation.
- >>> transformation_read_signature("models/plant_refine")
- ({"req": "formalisms/Requirements", "plant": "formalisms/Plant"}, {"plant": "formalisms/Plant"})
- .. function:: element_list_nice(model_name)
- Returns a complete representation of a model in a compact dicationary-like representation.
- This is basically a list of dictionaries, each dictionary containing the values usually obtained with *read_attrs*.
- 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).
- Examples:
- * To read out a list of a PetriNets instance model.
- >>> element_list_nice("models/my_pn")
- [{"id": "p1", "name": "a place", "tokens": 1}, {"id": "t1", "name": "a transition"}, {"id": "p1_to_t1", "name": "transition", "__source": "p1", "__target": "t1", "weight": 1}]
- .. function:: connections_between(model_name, source_element, target_element)
- Returns a list of all allowed connection types between *source_element* and *target_element* in a specified model.
- Both elements need to be part of the same model, as otherwise a merged model should be created beforehand.
- Examples:
- * To read out the allowed connections between elements "p1" and "t1".
- >>> connections_between("models/my_pn", "p1", "t1")
- ["P2T"]
- * To read out the allowed connections from the Place class to itself.
- >>> connections_between("formalisms/PetriNets", "Place", "Place")
- ["Association", "Inheritance"]
- .. function:: define_attribute(model_name, node, attr_name, attr_type)
- Defines a new attribute on the element *node* in the metamodel identified by *model_name*.
- The attribute is named *attr_name* and must conform to *attr_type*.
- Note that, while there are other ways of creating attributes than this function, this function is by far the most user friendly.
- The type being assigned needs to be specified in the metamodel as well, by instantiating the *SimpleAttribute* node.
- As models are updated immediately, without any recompilation step inbetween, all instances can immediately make use of this new attribute.
- Examples:
- * To define a new attribute "tokens" on a PetriNet Place, which is of a Natural type.
- >>> define_attribute("formalisms/PetriNets", "Place", "tokens", Natural)
- .. function:: all_instances(model_name, type_name)
- Returns a list of all instances of type *type_name* in the model *model_name*.
- This includes indirect instances, which are only typed by type_name through inheritance.
- Examples:
- * To get all places in a PetriNet model.
- >>> all_instances("models/my_pn", "Place")
- ["p1", "__12345"]
- * To get all nodes that can be instantiated directly for PetriNets.
- >>> all_instances("formalisms/PetriNets", "Class")
- ["Place", "Transition", "P2T", "T2P"]
- .. function:: service_poll(port)
- Polls whether there is any input for the service we are hosting for communication handle *port*.
- This returns either True or False, to get the actual data, a *service_get(port)* is required.
- This function does not block when there is not data to be read.
- Examples:
- * To check whether there is any data for our port, when there is none.
- >>> service_poll(port)
- False
- * To check whether there is any data for our port, when there is some.
- >>> service_poll(port)
- True
- .. function:: user_name(user, username)
- Changes the username of user *user* to *username*.
- From this point on, the user has been renamed, and all old permissions are inherited.
- Similar to password management, users can only alter their own username.
- The administrator can also alter the username of all users.
- Active logins do not need to be refreshed, as the new username will automatically be used everywhere.
- From the next login, however, the new username has to be used, as the old one has been renamed.
- Examples:
- * To change the username of user1 to user2.
- >>> user_name("user1", "user2")
- .. function:: alter_context(model_name, metamodel_name)
- Registers the model *model_name* to be interpreted in the context of *metamodel_name* as its metamodel.
- All future requests to the model will be made, using the assumption that the model is typed by the specified metamodel.
- When this is not the case, the request will throw an exception.
- 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.
- The context is automatically deduced from the previous commands: adding the model with a specific metamodel, automatically puts the model in that context.
- 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.
- Examples:
- * To change the default metamodel of PetriNets to Bottom.
- >>> alter_context("formalisms/PetriNets", "formalisms/Bottom")
- .. function:: allowed_metamodels(model_name)
- Fetch a set of all metamodels that are registered for *model_name*.
- This reads out the data in the Modelverse, and will not try to resolve new relations.
- It is possible for this list to be empty, in case the relation to *Bottom* was removed previously.
- Otherwise, every model will conform to at least *Bottom*.
- Other metamodels are optional.
- Examples:
- * To fetch the metamodels to which a PetriNet model conforms, where it is explicitly set that it also conforms to PetriNets which use inhibitor arcs.
- >>> allowed_metamodels("models/my_pn")
- ["formalisms/PetriNets", "formalisms/Bottom", "formalisms/PetriNets_Inhibitor"]
- * When all typing relations were explicitly removed.
- >>> allowed_metamodels("models/my_pn")
- []
- * Usual situation when there is no metamodel specified.
- >>> allowed_metamodels("models/my_pn")
- ["Bottom"]
- .. function:: remove_metamodel(model_name, metamodel_name)
- Explicitly removes the conformance relation between *model_name* and *metamodel_name*.
- It will prevent future uses of this model in the context of this metamodel, though does not prevent future operations from recreating this relation.
- Examples:
- * To remove the PetriNets_Inhibitor metamodel for my_pn.
- >>> remove_metamodel("models/my_pn", "formalisms/PetriNets_Inhibitor")
- .. function:: add_metamodel(model_name, metamodel_name, partial_type_mapping=None)
- Explicitly add the conformance relation between *model_name* and *metamodel_name*.
- It creates the relation, and will try to find a type mapping between them automatically.
- A *partial_type_mapping* can be passed, which will be used as a starting point when searching for a conformance relation.
- Multiple possible relations might be found, in which case an arbitrary type mapping is taken.
- TODO: allow for the partial type mapping
-
- Examples:
- * To try and make my_pn conform to PetriNets_Inhibitor again; this results in multiple options!
- >>> add_metamodel("models/my_pn", "formalisms/PetriNets_Inhibitor")
- Exceptions
- ^^^^^^^^^^
- Below is a list of all exceptions that the wrappers can raise, together with a summary of the kind of error that occured.
- .. exception:: ModelverseException
- Generic Modelverse Exception, of which all others inherit.
- This should be the only type of exception that the wrapper can raise.
- General Python exceptions could theoretically still occur, though this likely is a bug in the wrapper.
- This exception itself will never occur: it is abstract.
- .. exception:: UnknownError
- Unknown exception has occured.
- This is likely something wrong with the connection, such as the Modelverse that suddenly disconnected, or the request timed out.
- Note that this exception cannot be raised during *init*, as then the connection is nicely wrapped in a *ConnectionError*.
- Examples:
- * When the Modelverse is suddenly killed during execution, while there was an outstanding request.
- >>> element_list("formalisms/PetriNets") # <-- Modelverse killed during execution
- UnknownError()
- .. exception:: UnknownIdentifier
- The specified element identifier could not be resolved in the specified model in the Modelverse.
- The exception contains the identifier causing the problem, as there might be multiple identifiers used in a single request.
- Examples:
- * When reading out a non-existing element in a PetriNets model.
- >>> read("models/my_pn", "p0")
- UnknownIdentifier("p0")
- * When reading out the allowed connections between two elements, of which neither exists.
- >>> connections_between("models/my_pn", "p0", "t0")
- UnkownIdentifier("p0")
- * When reading out the allowed connections between two elements, of which the target doesn't exists.
- >>> connections_between("models/my_pn", "p1", "t0")
- UnkownIdentifier("t0")
- * When instantiating a non-existing element in the meta-model.
- >>> instantiate("models/my_pn", "CapacityConstrainedPlace")
- UnkownIdentifier("CapacityConstrainedPlace")
- .. exception:: UnsupportedValue
- The specified value is not a primitive that can be serialized.
- Supported types are: string, boolean, integer, and float.
- Examples:
- * When assigning a list as attribute.
- >>> attr_assign("models/my_pn", "p1", "tokens", [1, 2, 3])
- UnsupporteValue("[1, 2, 3] : list")
- * When assigning a None value to an attribute.
- >>> attr_assign("models/my_pn", "p1", "name", None)
- UnsupportedValue("None : NoneType")
- .. exception:: CompilationError
- Error in the HUTN compiler during compilation.
- This is mostly caused by a malformed expression in the specified code.
- The compilation error is contained in the content of the exception.
- Examples:
- * When assigning a code block which cannot be parsed as action language.
- >>> attr_assign_code("models/my_pn", "p1", "tokens", "1")
- CompilationError("Parsing error at line 1: ...")
- .. exception:: NoSuchAttribute
- The specified attribute does not exist for this element.
- While the attribute might exist as a type for the lower meta-level, this only looks at the current level.
- Examples:
- * When assigning a non-existing attribute.
- >>> attr_assign("models/my_pn", "p1", "capacity", 2)
- NoSuchAttribute("capacity")
- .. exception:: UnknownModel
- The specified model can not be resolved in the Modelverse.
- While the model might technically exist, this exception merely indicates that it cannot be referred to with the specified identifier.
- Examples:
- * When trying to execute a non-existing transformation.
- >>> transformation_execute_MT("models/pn_optimize", {"pn": "models/my_pn"}, {"pn": "models/my_optimized_pn"})
- UnknownModel("pn_optimize")
- .. exception:: ConnectionError
- Error during initialization of the connection to the Modelverse.
- The actual error is enclosed in the exception content.
- Despite possibly multiple errors, the *init* call will try for at least the amount of time specified in the timeout.
- This is done as the Modelverse might still be booting up and is not yet listening to incoming connections.
- Examples:
- * When trying to connect to a server which doesn't exist.
- >>> init("http://www.modelverse.be")
- ConnectionError("No such host")
- .. exception:: ModelExists
- The identifier to give to the newly created model already exists.
- Note that some operations, such as model transformation, will default to overwriting a model if it already exists, but still is used as a target.
- Of course, permissions of the overwritten model need to allow for this.
- Examples:
- * When the model "my_pn" already exists.
- >>> model_add("models/my_pn", "PetriNets")
- ModelExists("models/my_pn")
- .. exception:: PermissionDenied
- Permission denied to either write or read the specified resource.
- This resource can be anything: a model, an element, a user, a group, ...
- Examples:
- * When changing the password of another user, while we are not an administrator.
- >>> user_password("user2", "NewPassword")
- PermissionDenied("user2")
- * When listing the elements of a model which we aren't allowed to read.
- >>> element_list("models/secret_model")
- PermissionDenied("models/secret_model")
- * When altering a model which we are only allowed to read.
- >>> instantiate("formalisms/SimpleClassDiagrams", "NewClass")
- PermissionDenied("formalisms/SimpleClassDiagrams")
- .. exception:: InvalidMode
- An operation was executed in the wrong context.
- For example, all operations are only valid after a successful *init* and *login* call.
- This error can also result from a previous exception, which wasn't handled correctly.
- Examples:
- * When a *login* fails (thus raising a *PermissionDenied* exception), but the next operation assumes that the user is logged in.
- >>> login("admmin", "wrong_password")
- PermissionDenied("admin")
- >>> element_list("formalisms/SimpleClassDiagrams")
- InvalidMode()
- .. exception:: InterfaceMismatch
- The Modelverse responded with an unexpected response.
- As a response came, the Modelverse is likely still running, though we have no idea how to interpret the result.
- Likely, the wrapper is not up to date with the latest Modelverse operations.
- It is difficult to give an example, as this always indicates a bug in the wrapper itself.
- .. exception:: UnknownMetamodellingHierarchy
- The requested model and metamodel have no existing relation between them: it is unknown how to make the model conform to the metamodel.
- This might be because there is no possible relation, or just because there is no relation defined yet.
- Examples:
- * When erroneously trying to interpret a petrinet model as a Class Diagram.
-
- >>> alter_context("models/my_pn", "formalisms/SimpleClassDiagrams")
- >>> element_list()
- UnknownMetamodellingHierarchy("models/my_pn")
- SCCD
- ----
- An SCCD interface is created, which has exactly the same interface.
- Actually, the Python interface presented before is built using this SCCD interface.
- The operations are identical and have the same signature, although they are invoked differently.
- To make a request to the Modelverse, an event has to be raised called *action*.
- The first parameter of this event is the operation to invoke, as a string (*e.g.*, 'read_info').
- The second parameter specifies an ID that can be used in the response event to identify which event this is the response to.
- The third parameter is unused.
- The fourth parameter is a list of all the parameters to this function (*e.g.*, ["my_pn"]).
- For example::
- <raise event="action">
- <parameter expr="'read_info'"/>
- <parameter expr="None"/>
- <parameter expr="None"/>
- <parameter expr="['my_pn']"/>
- </raise>
- When this request is processed, the Modelverse will reply with the *mv_result* event, containing the result.
- The first parameter is the ID passed on in the original request.
- The second parameter contains the result.
- It is possible to wait on this event as follows::
- <transition event="mv_result" target=".">
- <parameter name="ID"/>
- <parameter name="result"/>
- <script>
- print("Response: " + str(result))
- </script>
- </transition>
- If something went wrong, the event *mv_exception* will be raised instead::
- <transition event="mv_exception" target=".">
- <parameter name="ID"/>
- <parameter name="exception_name"/>
- <parameter name="exception"/>
- <script>
- print("Exception: " + str(exception_name))
- print(exception)
- </script>
- </transition>
- The use of the SCCD interface is particularly interesting in case asynchronous communication is required.
- 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.
- 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.
- For this, we refer to the implementation.
- Custom
- ------
- Other wrappers can be made as desired, in whatever language required.
- This is due to the fact that the Modelverse communicates only through XML/HTTPRequests.
- As such, all languages that support this, can simply mimic the interface used by any of the implemented wrappers.
|