merging (meta-)models works (but it's dirty!)
This commit is contained in:
parent
c31c8bf3ea
commit
9883e09ac2
17 changed files with 474 additions and 46 deletions
193
examples/semantics/translational/merged_mm.od
Normal file
193
examples/semantics/translational/merged_mm.od
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
# Auto-generated by /home/maestro/repos/MV2/examples/semantics/translational/regenerate_mm.py
|
||||
|
||||
# PlantUML visualization: https://deemz.org/plantuml/pdf/hPT1Zzem48Nl-HKMnqeWqcG1HLKFkuUg5nO9f1wHSSPiHB2HOofLxVxtsYOWTXKfByWSkCtdcNdFuqaYQjuqRFJ2JrnKzi-BLeqrl5AMZHXlsBJzRJl-_2-ZajZVXB7chJfT8QnWFvMbFPdaFMaFM2rNDHUqjjmIYgQdW5RduqOVI3LTt5_Q7CYi2SwNn1Lw2Usa3afJPexudl2Txvomx9uXppMCuTqOVJO2pKMcym2vgbfhSM3fP9A2uLaUccEh8tMrvPcCVHlI6pcRNzpXOiw-qsjhAhNlA7EZJoXpaT_N66o5XlBqFlJcdK4bySKz8xG43fNteJz8aU5M6pHyD_jG-79Zk6egMsa54yfEZwsMxjuh4fRlQhWF8k_sQwKEA88-oD7cuCePf8Uy3A2Z_asbSzYprZLnzSaW0uZvT7gREsitrJe7H3lEOA88XIQzrVWfyEtVUDpF_4fvFyxV5GZdvtXCd9CMmBdh2EAuZDWx_mW0sRbPYcO75EkV2KnPvx-eoieighBfF2ekYsjZoMCQ1L9sGB42A1OsYd-AWEm8lsJznORX2E9cCOsIPpcWh7-KmEnsPLGfPDJnwLRVg0DgDujxAu1f34lXNqVePSpOA6MJ2NFBx7ZytJsz8sohBfW6y_N8W9xwSxu0V1zLC6w0zyH_UTmEIE43tCvOCC7LwyalYuZBd2qUACID2NVERGL3wdaQBaXOXGfsKbj8ayL3cYoy9dk_NLTYMxjz55b55e-S5CIfceis_iEclsibFUI2e4xxVVqg_mC=
|
||||
|
||||
|
||||
CapacityConstraint:Class
|
||||
PNPlaceState:Class
|
||||
WorkerSet:Class
|
||||
State:Class
|
||||
Stateful:Class {
|
||||
abstract = True;
|
||||
}
|
||||
Source:Class {
|
||||
abstract = True;
|
||||
}
|
||||
Clock:Class {
|
||||
upper_cardinality = 1;
|
||||
lower_cardinality = 1;
|
||||
}
|
||||
BerthState:Class {
|
||||
constraint = ```
|
||||
errors = []
|
||||
numShips = get_slot_value(this, "numShips")
|
||||
status = get_slot_value(this, "status")
|
||||
if (numShips == 0) != (status == "empty"):
|
||||
errors.append(f"Inconsistent: numShips = {numShips}, but status = {status}")
|
||||
errors
|
||||
```;
|
||||
}
|
||||
Top:Class {
|
||||
abstract = True;
|
||||
}
|
||||
Place:Class
|
||||
WorkerSetState:Class
|
||||
Berth:Class
|
||||
Generator:Class
|
||||
PNTransition:Class
|
||||
PNConnectable:Class {
|
||||
abstract = True;
|
||||
}
|
||||
Sink:Class {
|
||||
abstract = True;
|
||||
}
|
||||
ConnectionState:Class
|
||||
PlaceState:Class
|
||||
PNPlace:Class
|
||||
shipCapacities:GlobalConstraint {
|
||||
constraint = ```
|
||||
errors = []
|
||||
for _, constr in get_all_instances("CapacityConstraint"):
|
||||
cap = get_slot_value(constr, "shipCapacity")
|
||||
total = 0
|
||||
place_names = [] # for debugging
|
||||
for lnk in get_outgoing(constr, "capacityOf"):
|
||||
place = get_target(lnk)
|
||||
place_names.append(get_name(place))
|
||||
place_state = get_source(get_incoming(place, "of")[0])
|
||||
total += get_slot_value(place_state, "numShips")
|
||||
if total > cap:
|
||||
errors.append(f"The number of ships in places {','.join(place_names)} ({total}) exceeds the capacity ({cap}) of CapacityConstraint {get_name(constr)}.")
|
||||
errors
|
||||
```;
|
||||
}
|
||||
operatingCapacities:GlobalConstraint {
|
||||
constraint = ```
|
||||
errors = []
|
||||
for _, workersetstate in get_all_instances("WorkerSetState"):
|
||||
workerset = get_target(get_outgoing(workersetstate, "of")[0])
|
||||
num_operating = len(get_outgoing(workersetstate, "isOperating"))
|
||||
num_workers = get_slot_value(workerset, "numWorkers")
|
||||
if num_operating > num_workers:
|
||||
errors.append(f"WorkerSet {get_name(workerset)} is operating more berths ({num_operating}) than there are workers ({num_workers})")
|
||||
errors
|
||||
```;
|
||||
}
|
||||
WorkerSet_numWorkers:AttributeLink (WorkerSet -> Integer) {
|
||||
optional = False;
|
||||
name = "numWorkers";
|
||||
constraint = `get_value(get_target(this)) >= 0`;
|
||||
}
|
||||
PlaceState_numShips:AttributeLink (PlaceState -> Integer) {
|
||||
name = "numShips";
|
||||
constraint = `get_value(get_target(this)) >= 0`;
|
||||
optional = False;
|
||||
}
|
||||
ConnectionState_moved:AttributeLink (ConnectionState -> Boolean) {
|
||||
constraint = ```
|
||||
result = True
|
||||
all_successors_moved = True
|
||||
moved = get_value(get_target(this))
|
||||
conn_state = get_source(this)
|
||||
conn = get_target(get_outgoing(conn_state, "of")[0])
|
||||
tgt_place = get_target(conn)
|
||||
next_conns = get_outgoing(tgt_place, "connection")
|
||||
for next_conn in next_conns:
|
||||
next_conn_state = get_source(get_incoming(next_conn, "of")[0])
|
||||
if not get_slot_value(next_conn_state, "moved"):
|
||||
all_successors_moved = False
|
||||
if moved and not all_successors_moved:
|
||||
result = f"Connection {get_name(conn)} played before its turn."
|
||||
result
|
||||
```;
|
||||
optional = False;
|
||||
name = "moved";
|
||||
}
|
||||
BerthState_status:AttributeLink (BerthState -> String) {
|
||||
constraint = ```
|
||||
(
|
||||
get_value(get_target(this)) in { "empty", "unserved", "served" }
|
||||
)
|
||||
```;
|
||||
optional = False;
|
||||
name = "status";
|
||||
}
|
||||
PNPlaceState_numTokens:AttributeLink (PNPlaceState -> Integer) {
|
||||
constraint = `"numTokens cannot be negative" if get_value(get_target(this)) < 0 else None`;
|
||||
optional = False;
|
||||
name = "numTokens";
|
||||
}
|
||||
Clock_time:AttributeLink (Clock -> Integer) {
|
||||
constraint = `get_value(get_target(this)) >= 0`;
|
||||
optional = False;
|
||||
name = "time";
|
||||
}
|
||||
CapacityConstraint_shipCapacity:AttributeLink (CapacityConstraint -> Integer) {
|
||||
constraint = `get_value(get_target(this)) >= 0`;
|
||||
optional = False;
|
||||
name = "shipCapacity";
|
||||
}
|
||||
of:Association (State -> Stateful) {
|
||||
source_upper_cardinality = 1;
|
||||
source_lower_cardinality = 1;
|
||||
target_upper_cardinality = 1;
|
||||
target_lower_cardinality = 1;
|
||||
}
|
||||
arc:Association (PNConnectable -> PNConnectable)
|
||||
canOperate:Association (WorkerSet -> Berth) {
|
||||
target_lower_cardinality = 1;
|
||||
}
|
||||
connection:Association (Source -> Sink)
|
||||
pn_of:Association (PNPlaceState -> PNPlace) {
|
||||
source_lower_cardinality = 1;
|
||||
target_upper_cardinality = 1;
|
||||
target_lower_cardinality = 1;
|
||||
source_upper_cardinality = 1;
|
||||
}
|
||||
generic_link:Association (Top -> Top)
|
||||
isOperating:Association (WorkerSetState -> Berth) {
|
||||
constraint = ```
|
||||
errors = []
|
||||
|
||||
# get status of Berth
|
||||
berth = get_target(this)
|
||||
berth_state = get_source(get_incoming(berth, "of")[0])
|
||||
status = get_slot_value(berth_state, "status")
|
||||
if status != "unserved":
|
||||
errors.append(f"Cannot operate {get_name(berth)} because there is no unserved ship there.")
|
||||
|
||||
# only operate Berts that we can operate
|
||||
workerset = get_target(get_outgoing(get_source(this), "of")[0])
|
||||
can_operate = [get_target(lnk) for lnk in get_outgoing(workerset, "canOperate")]
|
||||
if berth not in can_operate:
|
||||
errors.append(f"Cannot operate {get_name(berth)}.")
|
||||
|
||||
errors
|
||||
```;
|
||||
}
|
||||
capacityOf:Association (CapacityConstraint -> Place) {
|
||||
target_lower_cardinality = 1;
|
||||
}
|
||||
:Inheritance (connection -> Stateful)
|
||||
:Inheritance (CapacityConstraint -> Top)
|
||||
:Inheritance (Sink -> Top)
|
||||
:Inheritance (Berth -> Place)
|
||||
:Inheritance (WorkerSet -> Stateful)
|
||||
:Inheritance (Place -> Source)
|
||||
:Inheritance (PlaceState -> State)
|
||||
:Inheritance (State -> Top)
|
||||
:Inheritance (Source -> Top)
|
||||
:Inheritance (Clock -> Top)
|
||||
:Inheritance (Stateful -> Top)
|
||||
:Inheritance (Place -> Stateful)
|
||||
:Inheritance (PNConnectable -> Top)
|
||||
:Inheritance (WorkerSetState -> State)
|
||||
:Inheritance (Place -> Sink)
|
||||
:Inheritance (BerthState -> PlaceState)
|
||||
:Inheritance (generic_link -> Top)
|
||||
:Inheritance (PNTransition -> PNConnectable)
|
||||
:Inheritance (ConnectionState -> State)
|
||||
:Inheritance (PNPlaceState -> Top)
|
||||
:Inheritance (Generator -> Source)
|
||||
:Inheritance (Berth -> Stateful)
|
||||
:Inheritance (PNPlace -> PNConnectable)
|
||||
63
examples/semantics/translational/regenerate_mm.py
Normal file
63
examples/semantics/translational/regenerate_mm.py
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
from state.devstate import DevState
|
||||
from bootstrap.scd import bootstrap_scd
|
||||
from concrete_syntax.textual_od import renderer
|
||||
from concrete_syntax.plantuml.renderer import render_class_diagram
|
||||
from concrete_syntax.plantuml.make_url import make_url
|
||||
from api.od import ODAPI
|
||||
|
||||
from transformation.topify.topify import Topifier
|
||||
from transformation.merger import merge_models
|
||||
|
||||
from util import loader
|
||||
|
||||
from examples.semantics.operational.port import models
|
||||
|
||||
import os
|
||||
THIS_DIR = os.path.dirname(__file__)
|
||||
|
||||
# get file contents as string
|
||||
def read_file(filename):
|
||||
with open(THIS_DIR+'/'+filename) as file:
|
||||
return file.read()
|
||||
|
||||
if __name__ == "__main__":
|
||||
state = DevState()
|
||||
scd_mmm = bootstrap_scd(state)
|
||||
|
||||
# Load Petri Net meta-models
|
||||
pn_mm_cs = read_file('../../petrinet/metamodels/mm_design.od')
|
||||
pn_mm_rt_cs = pn_mm_cs + read_file('../../petrinet/metamodels/mm_runtime.od')
|
||||
pn_mm = loader.parse_and_check(state, pn_mm_cs, scd_mmm, "Petri-Net Design meta-model")
|
||||
pn_mm_rt = loader.parse_and_check(state, pn_mm_rt_cs, scd_mmm, "Petri-Net Runtime meta-model")
|
||||
|
||||
# Load Port meta-models
|
||||
port_mm = loader.parse_and_check(state, models.port_mm_cs, scd_mmm, "Port-MM")
|
||||
port_mm_rt = loader.parse_and_check(state, models.port_rt_mm_cs, scd_mmm, "Port-MM-RT")
|
||||
|
||||
# Merge Petri Net and Port meta-models
|
||||
print("merging...")
|
||||
merged_mm_rt = merge_models(state, mm=scd_mmm, models=[pn_mm_rt, port_mm_rt])
|
||||
print("done merging")
|
||||
|
||||
print()
|
||||
print("topifying... (may take a while)")
|
||||
topifier = Topifier(state)
|
||||
top_merged_mm_rt = topifier.topify_cd(merged_mm_rt)
|
||||
print("done topifying")
|
||||
|
||||
plantuml_url = make_url(render_class_diagram(state, top_merged_mm_rt))
|
||||
|
||||
print()
|
||||
print(plantuml_url)
|
||||
print()
|
||||
|
||||
txt = renderer.render_od(state, top_merged_mm_rt, scd_mmm)
|
||||
|
||||
filename = THIS_DIR+"/merged_mm.od"
|
||||
|
||||
with open(filename, "w") as file:
|
||||
file.write(f"# Auto-generated by {__file__}\n\n")
|
||||
file.write(f"# PlantUML visualization: {plantuml_url}\n\n")
|
||||
file.write(txt)
|
||||
|
||||
print("Wrote file", filename)
|
||||
2
examples/semantics/translational/rules/.gitignore
vendored
Normal file
2
examples/semantics/translational/rules/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
# Let's not accidently add the solution to assignment 5...
|
||||
r_*.od
|
||||
35
examples/semantics/translational/runner.py
Normal file
35
examples/semantics/translational/runner.py
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
from state.devstate import DevState
|
||||
from bootstrap.scd import bootstrap_scd
|
||||
from concrete_syntax.textual_od import parser, renderer
|
||||
from concrete_syntax.plantuml.renderer import render_object_diagram, render_class_diagram
|
||||
from concrete_syntax.plantuml.make_url import make_url
|
||||
from api.od import ODAPI
|
||||
|
||||
from transformation.ramify import ramify
|
||||
from transformation.topify.topify import Topifier
|
||||
from transformation.merger import merge_models
|
||||
|
||||
from util import loader
|
||||
|
||||
from examples.semantics.operational.simulator import Simulator, RandomDecisionMaker, InteractiveDecisionMaker
|
||||
from examples.semantics.operational.port import models
|
||||
from examples.semantics.operational.port.helpers import design_to_state, state_to_design, get_time
|
||||
from examples.semantics.operational.port.renderer import render_port_textual, render_port_graphviz
|
||||
|
||||
import os
|
||||
THIS_DIR = os.path.dirname(__file__)
|
||||
|
||||
# get file contents as string
|
||||
def read_file(filename):
|
||||
with open(THIS_DIR+'/'+filename) as file:
|
||||
return file.read()
|
||||
|
||||
if __name__ == "__main__":
|
||||
state = DevState()
|
||||
scd_mmm = bootstrap_scd(state)
|
||||
|
||||
# Load merged Petri Net and Port meta-model:
|
||||
merged_mm = loader.parse_and_check(state, read_file("merged_mm.od"), scd_mmm, "merged_mm.od")
|
||||
|
||||
# Load Port initial runtime model:
|
||||
port_m_rt_initial = loader.parse_and_check(state, models.port_rt_m_cs, merged_mm, "Port-M-RT-initial")
|
||||
Loading…
Add table
Add a link
Reference in a new issue