123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320 |
- Activities
- ==========
- While model transformations are in many tools considered to be the only types of activity on models, this is not necessarily the case in the Modelverse.
- Indeed, MPM advocates to use the most appropriate formalism, and, as model transformations are models themselves, this should also apply to specifying activities.
- As such, we extend the notion of model transformations to more general activities.
- Activities come in three types, depending on how they are ideally specified: model transformations, manual activities, and action language.
- In the remainder of this section, we will show all types, and what situation they are ideally suited for.
- Model Transformations
- ---------------------
- When model matching and rewriting is involved, model transformations are clearly the ideal solution in most cases.
- The LHS is used to match elements in the host model, and the RHS is put in its place.
- As this could also be combined with a visual concrete syntax, it could even become possible for domain experts to create a model transformation without too much programming knowledge.
- Nonetheless, model transformations are a completely different paradigm, based solely on matching and rewriting.
- Some formalisms have a long history in computer science, and have therefore efficient and/or simple procedural algorithms.
- For example, computing the reachability graph of a PetriNets instance can certainly be done with model transformations, but will be overly difficult.
- On the other hand, procedural algorithms are relatively easy (when implemented naively).
- The functions on model transformations are exposed using the *transformation_add_MT* and *transformation_execute_MT* operations.
- Examples as shown in the previous section::
- >>> def callback(model):
- ... instantiate(model, "Association", edge=("PetriNets/Transition", "ReachabilityGraph/Transition"), ID="PN2RG_Transition")
- >>> transformation_add_MT({"PetriNets": "formalisms/PetriNets"}, {"ReachabilityGraph": "formalisms/ReachabilityGraph"}, "models/pn_analyse", open("models/pn_analyse.mvc", "r").read(), callback)
- A situation in which model transformations are effective, is when the transformation is to map a Domain Specific Language (DSL) to a General Purpose Language (GPL).
- For example, when defining a mapping between a RPGame DSL and the PetriNets formalism, to facilitate formal analysis::
- >>> def callback(model):
- ... instantiate(model, "Association", edge=("RPGame/Tile", "PetriNets/Place"), ID="Tracability_link")
- >>> rule = \
- ... """
- ... Composite composite {
- ... {Contains} ForAll create_places_for_tiles {
- ... LHS {
- ... Pre_RPGame/Tile pre_t1 {
- ... label = "tile"
- ... }
- ... }
- ... RHS {
- ... Post_RPGame/Tile post_t1 {
- ... label = "tile"
- ... }
- ... Post_PetriNets/Place post_p1 {
- ... label = "place"
- ... }
- ... Post_Tracability_link (post_t1, post_p1) {
- ... label = "tracability"
- ... }
- ... }
- ... }
- ... ...
- ... }
- ... Initial (composite, create_places_for_tiles) {}
- ... OnSuccess (create_places_for_tiles, ...) {}
- ... OnFailure (create_places_for_tiles, ...) {}
- ... ...
- ... """
- >>> transformation_add_MT({"RPGame": "formalisms/RPGame"}, {"PetriNets": "formalisms/PetriNets"}, "models/rpg_to_pn", rule, callback)
- >>> transformation_execute_MT("models/rpg_to_pn", {"RPGame": "models/my_rpg"}, {"PetriNets": "models/exported_rpg"})
- Action Language
- ---------------
- Doing operations in a procedural way is supported in the Modelverse through the use of action language models.
- As action language is explicitly modelled, we can create models consisting solely of action language.
- They are therefore similar to model transformations, which are also models, but only define a different operational semantics.
- Instead of having a model transformation interpreter apply the schedule and rules, we merely call the action language functions enclosed in the model.
- The functions on action language are exposed using the *transformation_add_AL* and *transformation_execute_AL* operations.
- Their signature is identical to that of model transformations.
- The action language model itself requires a single *main* function, or it will execute the topmost function in the specified code.
- This function should take a single argument, being the merged model.
- From this model, the merged metamodel can easily be accessed.
- As with model transformations, the names of model entities are prefixed with their tags.
- For example, when defining the PetriNets analysis in action language, which is a much better match than model transformations::
- >>> def callback(model):
- ... instantiate(model, "Association", edge=("PetriNets/Transition", "ReachabilityGraph/Transition"), ID="PN2RG_Transition")
- >>> code = \
- ... """
- ... include "primitives.alh"
- ... include "modelling.alh"
- ... ...
- ...
- ... Element function explore_state(model : Element, state : Element):
- ... ...
- ...
- ... Boolean function main(model : Element):
- ... String initial
- ... initial = instantiate_node(model, "ReachabilityGraph/Initial")
- ... instantiate_attribute(model, initial, "name", "state_0")
- ... ...
- ... return True!
- ... """
- >>> transformation_add_AL({"PetriNets": "formalisms/PetriNets"}, {"ReachabilityGraph": "formalisms/ReachabilityGraph"}, "models/pn_analyse", code, callback)
- >>> transformation_execute_AL("models/pn_analyse", {"PetriNets": "models/my_pn"}, {"ReachabilityGraph": "models/generated_reachability"})
- An activity will have to return a boolean, similar to how a model transformation had to end in either success or failure.
- If the boolean is true, the activity is deemed to have terminated successfully and the changes are made to the models listed in the output dictionary.
- If the boolean is false, the activity is deemed to have failed and no changes are made to the models in the output dictionary.
- Manual Activities
- -----------------
- Some activities simply cannot be automated.
- For example, translating natural language descriptions of requirements of a model to the actual model is not something that can trivially be automated.
- The only solution, therefore, is to do this manually, thereby requiring user intervention.
- Even though they are manual, they can still be partially automated: merging and splitting based on tags can still be done before and after the manual activity.
- The functions on action language are exposed using the *transformation_add_MANUAL* and *transformation_execute_MANUAL* operations.
- Their signature is identical to that of model transformations, except that now there is no model to provide with the *transformation_add_MANUAL*.
- As with model transformations, the names of model entities are prefixed with their tags.
- This has to be taken into account when doing the manual changes to the model.
- For example, when defining an activity to refine or revise a model in a DSL with respect to the requirements, such as the PetriNets language from before::
- >>> def callback(model):
- ... instantiate(model, "Association", edge=("Requirements/Actor", "PetriNets/Place"))
- >>> transformation_add_MANUAL({"PetriNets": "formalisms/PetriNets", "Requirements": "formalisms/Requirements"}, {"PetriNets": "formalisms/PetriNets"}, "revise_pn", callback)
- Its execution, however, differs significantly from before, as we will see next.
- Manual activities are deemed to always terminate successfully, and must therefore not return a boolean.
- Execution callbacks
- ^^^^^^^^^^^^^^^^^^^
- As specified before, manual activities require user intervention during execution.
- The *transformation_execute_MANUAL* operation is therefore not a simple function that is executed and returns after some time: user input must be given.
- For this, an additional argument is provided as callback function.
- This callback function can be any sequence of operations again, such that it is possible to create or alter the model.
- Certainly, if it were to be possible automatically, we could have done it that way in the Modelverse.
- Therefore, the callback function can do much more than just these calls: all expressive power of Python is available, such as the possibility for user input (*raw_input()*).
- Based on this, the callback can define whatever structure is desired, possibly even going as far as starting up a dialog box or using a graphical interface.
- As before, the first parameter of the operations (*model_name*) should be set to *None*.
- For example, to specify the operations that have to be done in a manual operation, all in Python syntax::
- >>> def callback(model):
- ... places = [instantiate(model, "PetriNets/Place") for _ in range(100)]
- ... ...
- >>> transformation_execute_MANUAL("revise_pn", {"PetriNets": "models/my_pn", "Requirements": "models/pn_requirements"}, {"PetriNets": "models/my_pn"}, callback)
- Alternatively, the operations might only be known at runtime, thereby requiring user interaction.
- Note that, in this case, the code in the callback is effectively the user interface offered to the user in this specific context.
- As such, the code can be completely tailored to the domain and problem at hand::
- >>> def callback(model):
- ... while True:
- ... print("Please perform operation on PetriNet!)"
- ... inp = raw_input()
- ... if inp == "new place":
- ... instantiate(model, "PetriNets/Place")
- ... ...
- ... ...
- >>> transformation_execute_MANUAL("revise_pn", {"PetriNets": "models/my_pn", "Requirements": "models/pn_requirements"}, {"PetriNets": "models/my_pn"}, callback)
- Manual activities are not the only operations which might require user input.
- Model transformations and action language can just as well query the users for input.
- This is done the same way: by defining a callback.
- Note that they are slightly different, in the sense that their callback is not a function in Python, but governed by a Statechart, as discussed next.
- Statechart callbacks
- ^^^^^^^^^^^^^^^^^^^^
- While manual activities can refer to a Python function as callback, this is not the case for the execution of a model transformation or action language fragment.
- In this case, a more complex interaction is required, which depends on the use of SCCD (Statecharts + Class Diagrams).
- When executing the activity, a reference to a running statechart is passed, instead of a callback function.
- This statechart can have an input and output port, which can be used to communicate with the Modelverse.
- Data sent on the output port will arrive in the running activity (in the *input()* call), and data sent out in the activity with *output(X)* will be received on the input port.
- An example is given below of a model transformation that prints out a Petri Net using a model transformation::
- >>> ctrl = log_output.Controller(keep_running=False)
- >>> thrd = threading.Thread(target=ctrl.start)
- >>> thrd.daemon = True
- >>> thrd.start()
- >>> transformation_execute_MT("test/print_pn", {"PetriNet": "test/my_pn"}, {}, (ctrl, "inp", "outp"))
- This command starts up the model transformation and immediately connects it to a running Statechart called *log_output*.
- Its definition is shown below::
- <?xml version="1.0" encoding="UTF-8"?>
- <diagram author="Yentl Van Tendeloo" name="Logging">
- <description>
- Print all incoming data
- </description>
-
- <inport name="inp"/>
- <outport name="outp"/>
-
- <class name="Logging" default="true">
- <scxml initial="init">
- <state id="init">
- <transition event="input" port="inp" target=".">
- <parameter name="value"/>
- <script>
- print(value)
- </script>
- </transition>
-
- <transition after="0.1" target="."/>
-
- <transition event="terminate" port="inp" target="../finished">
- <script>
- print("Terminating")
- </script>
- </transition>
- </state>
-
- <state id="finished"/>
- </scxml>
- </class>
- </diagram>
- Similarly, the SCCD model could have raised input to its output port (*outp*), which would then be received in the activity.
- Note that the call to *transformation_execute_MT* is always blocking and always returns whether or not the activity was successfully terminated.
- Process Model
- -------------
- A next logical step is the chaining of these various operations, or activities.
- For this reason, the FTG+PM was previously developed.
- The Modelverse provides both modelling and enactment support for the FTG+PM.
- When modelling, the FTG+PM is just a metamodel like all others, and instances can easily be created of it.
- There are notions of activities, decision nodes, forks, joins, and so on.
- Apart from control flow, an FTG+PM also specifies data flow: which models are used in this scope, and how are they interrelated.
- Indeed, we could define the sequence of activities to execute, but we still need information on what are the input models of the various activities.
- For example, the same activity might execute multiple times, but each time on different input models and generating different output models.
- A simple process model is shown, which executes the operations stored in *models/A* and *models/B* in parallel.
- Both operations use some data, where *models/A* modifies one of its data inputs in-place, and *models/B* generates new data::
- Start start {}
- Finish finish {}
- Fork fork1 {}
- Exec activity_A {
- name = "models/A"
- }
- Exec activity_B {
- name = "models/B"
- }
- Join join1{}
- Data data_a1 {
- name = "models/data_a1"
- type = "formalisms/Bottom"
- }
- Data data_a2 {
- name = "models/data_a2"
- type = "formalisms/Bottom"
- }
- Data data_b1 {
- name = "models/data_b1"
- type = "formalisms/SimpleClassDiagrams"
- }
- Data data_b2 {
- name = "models/data_b2"
- type = "formalisms/Bottom"
- }
- Next (start, fork1) {}
- Next (fork1, activity_A) {}
- Next (fork1, activity_B) {}
- Next (activity_A, join1) {}
- Next (activity_B, join1) {}
- Next (join1, finish) {}
- Consumes (activity_A, data_a1) {
- name = "First"
- }
- Consumes (activity_A, data_a2) {
- name = "Second"
- }
- Produces (activity_A, data_a2) {
- name = "Second"
- }
- Consumes (activity_B, data_b1) {
- name = "input"
- }
- Produces (activity_B, data_b2) {
- name = "output"
- }
- This simple Process Model is equivalent to the following statements in the code.
- Note that we do not know the type of the activity in the operations, and therefore put an asterisk (\*) there.
- Also, the order in which these operations execute is undefined, as either order is fine.
- We therefore leave it up to the Modelverse to decide upon an order::
- >>> execute_transformation_*("models/A", {"First": "my_models/data_a1", "Second": "my_models/data_a2"}, {"Second": "my_models/data_a2"})
- >>> execute_transformation_*("models/B", {"input": "my_models/data_b1"}, {"output": "my_models/data_b2"})
- In this case, it doesn't seem useful to use processes, though it is possible for such processes to contain *Decision* nodes, which can be used for arbitrary control flows.
- As everything runs on the Modelverse, instead of constantly requiring communication with the client, performance should be higher due to reduced network latency.
- When enacting, the FTG+PM is executed by starting at the initial node.
- The first element it points to is executed, thereby branching, deciding, or executing an activity.
- Due to the branching, it is possible for multiple activities to be scheduled concurrently.
- As for now, the Modelverse sequentializes the various activities internally.
- When executing an activity, which can either be a model transformation, action language, or manual operation, the operation is executed with the required operation.
- More information on enactment was previously given.
|