12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229 |
- 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()
- Done
- * 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, callback=lambda i: 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 *callback* function can be defined when the action language model requires user input or output.
- This callback function can be used to communicate with the executing action language directly.
- 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"})
- * To execute reachability analysis which prompts the user, for example because it is a debugging prompt.
- >>> def callback(value):
- ... print(value) # Prints out the prompt of the execution of the Action Language fragment
- ... return raw_input() # Sends a raw request from the user to the Modelverse, consumed in the Action Language
- >>> transformation_execute_AL("models/pn_simulate", {"pn": "models/my_pn"}, {"graph": "models/my_pn_reachability"}, callback)
- .. 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, callback=lambda i: 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"})
- * To execute a model transformation which prompts the user.
- >>> def callback(value):
- ... print(value)
- ... return raw_input()
- >>> transformation_execute_MT("models/pn_simulate_prompt", {"pn": "models/my_pn"}, {"pn": "models/my_simulated_pn"}, callback)
- .. 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.
- 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")
- 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.
|