|
@@ -1,5 +1,3 @@
|
|
|
-.. TODO include examples!!
|
|
|
-
|
|
|
Operations
|
|
|
==========
|
|
|
|
|
@@ -23,6 +21,47 @@ For example, computing the reachability graph of a PetriNets instance can certai
|
|
|
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():
|
|
|
+ ... instantiate(None, "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():
|
|
|
+ ... instantiate(None, "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
|
|
|
---------------
|
|
@@ -40,6 +79,26 @@ 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():
|
|
|
+ ... instantiate(None, "Association", edge=("PetriNets/Transition", "ReachabilityGraph/Transition"), ID="PN2RG_Transition")
|
|
|
+ >>> code = \
|
|
|
+ ... """
|
|
|
+ ... include "primitives.alh"
|
|
|
+ ... Element function explore_state(model : Element, state : Element):
|
|
|
+ ... ...
|
|
|
+ ...
|
|
|
+ ... Element function main(model : Element):
|
|
|
+ ... String initial
|
|
|
+ ... initial = instantiate_node(model, "ReachabilityGraph/Initial")
|
|
|
+ ... instantiate_attribute(model, initial, "name", "state_0")
|
|
|
+ ... ...
|
|
|
+ ... return model!
|
|
|
+ ... """
|
|
|
+ >>> 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"})
|
|
|
+
|
|
|
Manual Operations
|
|
|
-----------------
|
|
|
|
|
@@ -53,6 +112,14 @@ The functions on action language are exposed using the *transformation_add_MANUA
|
|
|
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.
|
|
|
|
|
|
+For example, when defining an operation to refine or revise a model in a DSL with respect to the requirements, such as the RPGame language from before::
|
|
|
+
|
|
|
+ >>> def callback():
|
|
|
+ ... instantiate(None, "Association", edge=("Requirements/Actor", "RPGame/Player"))
|
|
|
+ >>> transformation_add_MANUAL({"RPGame": "formalisms/RPGame", "Requirements": "formalisms/Requirements"}, {"RPGame": "formalisms/RPGame"}, "revise_rpg", callback)
|
|
|
+
|
|
|
+Its execution, however, differs significantly from before, as we will see next.
|
|
|
+
|
|
|
Execution callbacks
|
|
|
^^^^^^^^^^^^^^^^^^^
|
|
|
|
|
@@ -65,6 +132,29 @@ Therefore, the callback function can do much more than just these calls: all exp
|
|
|
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():
|
|
|
+ ... tiles = [instantiate(None, "RPGame/Tile") for _ in range(100)]
|
|
|
+ ... left_links = [instantiate(None, "RPGame/left", edge=(tiles[i], tiles[i+1])) for i in range(len(tiles) - 1)]
|
|
|
+ ... ...
|
|
|
+ >>> transformation_execute_MANUAL("revise_rpg", {"RPGame": "models/my_rpg", "Requirements": "models/rpg_requirements"}, {"RPGame": "models/my_rpg"}, 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 (e.g., a *create random level* operation).
|
|
|
+
|
|
|
+ >>> def callback():
|
|
|
+ ... while True:
|
|
|
+ ... print("Please perform operation on RPGame!)"
|
|
|
+ ... inp = raw_input()
|
|
|
+ ... if inp == "new tile":
|
|
|
+ ... instantiate(None, "RPGame/Tile")
|
|
|
+ ... elif inp == "create random level":
|
|
|
+ ... ...
|
|
|
+ ... ...
|
|
|
+ >>> transformation_execute_MANUAL("revise_rpg", {"RPGame": "models/my_rpg", "Requirements": "models/rpg_requirements"}, {"RPGame": "models/my_rpg"}, callback)
|
|
|
+
|
|
|
Manual operations 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 function.
|
|
@@ -86,6 +176,74 @@ Apart from control flow, an FTG+PM also specifies data flow: which models are us
|
|
|
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::
|
|
|
+
|
|
|
+ >>> 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"})
|
|
|
+
|
|
|
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.
|