Merge with Robbe's scheduling language
This commit is contained in:
commit
4ba0ed09b2
203 changed files with 8582 additions and 3886 deletions
|
|
@ -52,7 +52,7 @@ def merge_models(state, mm, models: list[UUID]):
|
|||
model = state.read_value(obj)
|
||||
scd = SCD(merged, state)
|
||||
created_obj = scd.create_model_ref(prefixed_obj_name, model)
|
||||
merged_odapi._ODAPI__recompute_mappings() # dirty!!
|
||||
merged_odapi.recompute_mappings() # dirty!!
|
||||
else:
|
||||
# create node or edge
|
||||
if state.is_edge(obj):
|
||||
|
|
|
|||
|
|
@ -149,13 +149,13 @@ def rewrite(state,
|
|||
if od.is_typed_by(bottom, rhs_type, class_type):
|
||||
obj_name = first_available_name(suggested_name)
|
||||
host_od._create_object(obj_name, host_type)
|
||||
host_odapi._ODAPI__recompute_mappings()
|
||||
host_odapi.recompute_mappings()
|
||||
rhs_match[rhs_name] = obj_name
|
||||
elif od.is_typed_by(bottom, rhs_type, assoc_type):
|
||||
_, _, host_src, host_tgt = get_src_tgt()
|
||||
link_name = first_available_name(suggested_name)
|
||||
host_od._create_link(link_name, host_type, host_src, host_tgt)
|
||||
host_odapi._ODAPI__recompute_mappings()
|
||||
host_odapi.recompute_mappings()
|
||||
rhs_match[rhs_name] = link_name
|
||||
elif od.is_typed_by(bottom, rhs_type, attr_link_type):
|
||||
host_src_name, _, host_src, host_tgt = get_src_tgt()
|
||||
|
|
@ -163,7 +163,7 @@ def rewrite(state,
|
|||
host_attr_name = host_mm_odapi.get_slot_value(host_attr_link, "name")
|
||||
link_name = f"{host_src_name}_{host_attr_name}" # must follow naming convention here
|
||||
host_od._create_link(link_name, host_type, host_src, host_tgt)
|
||||
host_odapi._ODAPI__recompute_mappings()
|
||||
host_odapi.recompute_mappings()
|
||||
rhs_match[rhs_name] = link_name
|
||||
elif rhs_type == rhs_mm_odapi.get("ActionCode"):
|
||||
# If we encounter ActionCode in our RHS, we assume that the code computes the value of an attribute...
|
||||
|
|
|
|||
502
transformation/schedule/Tests/Test_meta_model.py
Normal file
502
transformation/schedule/Tests/Test_meta_model.py
Normal file
|
|
@ -0,0 +1,502 @@
|
|||
import io
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
sys.path.insert(
|
||||
0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../"))
|
||||
)
|
||||
|
||||
from api.od import ODAPI
|
||||
from bootstrap.scd import bootstrap_scd
|
||||
from transformation.schedule.rule_scheduler import RuleScheduler
|
||||
from state.devstate import DevState
|
||||
from transformation.ramify import ramify
|
||||
from util import loader
|
||||
|
||||
|
||||
class Test_Meta_Model(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.dir = os.path.dirname(__file__)
|
||||
state = DevState()
|
||||
scd_mmm = bootstrap_scd(state)
|
||||
with open(f"{cls.dir}/models/mm_petrinet.od") as file:
|
||||
mm_s = file.read()
|
||||
with open(f"{cls.dir}/models/m_petrinet.od") as file:
|
||||
m_s = file.read()
|
||||
mm = loader.parse_and_check(state, mm_s, scd_mmm, "mm")
|
||||
m = loader.parse_and_check(state, m_s, mm, "m")
|
||||
mm_rt_ramified = ramify(state, mm)
|
||||
cls.model_param = (state, m, mm)
|
||||
cls.generator_param = (state, mm, mm_rt_ramified)
|
||||
|
||||
def setUp(self):
|
||||
self.model = ODAPI(*self.model_param)
|
||||
self.out = io.StringIO()
|
||||
self.generator = RuleScheduler(
|
||||
*self.generator_param,
|
||||
directory=self.dir + "/models",
|
||||
verbose=True,
|
||||
outstream=self.out,
|
||||
)
|
||||
|
||||
def _test_conformance(
|
||||
self, file: str, expected_substr_err: dict[tuple[str, str], list[list[str]]]
|
||||
) -> None:
|
||||
try:
|
||||
self.generator.load_schedule(f"schedule/{file}")
|
||||
errors = self.out.getvalue().split("\u25b8")[1:]
|
||||
if len(errors) != len(expected_substr_err.keys()):
|
||||
assert len(errors) == len(expected_substr_err.keys())
|
||||
for err in errors:
|
||||
error_lines = err.strip().split("\n")
|
||||
line = error_lines[0]
|
||||
for key_pattern in expected_substr_err.keys():
|
||||
if (key_pattern[0] in line) and (key_pattern[1] in line):
|
||||
key = key_pattern
|
||||
break
|
||||
else:
|
||||
assert False
|
||||
expected = expected_substr_err[key]
|
||||
if (len(error_lines) - 1) != len(expected):
|
||||
assert (len(error_lines) - 1) == len(expected)
|
||||
it = error_lines.__iter__()
|
||||
it.__next__()
|
||||
for err_line in it:
|
||||
if not any(
|
||||
all(exp in err_line for exp in line_exp)
|
||||
for line_exp in expected
|
||||
):
|
||||
assert False
|
||||
expected_substr_err.pop(key)
|
||||
except AssertionError:
|
||||
raise
|
||||
except Exception as e:
|
||||
assert False
|
||||
|
||||
def test_no_start(self):
|
||||
self._test_conformance("no_start.od", {("Start", "Cardinality"): []})
|
||||
|
||||
def test_no_end(self):
|
||||
self._test_conformance("no_end.od", {("End", "Cardinality"): []})
|
||||
|
||||
def test_multiple_start(self):
|
||||
self._test_conformance("multiple_start.od", {("Start", "Cardinality"): []})
|
||||
|
||||
def test_multiple_end(self):
|
||||
self._test_conformance("multiple_end.od", {("End", "Cardinality"): []})
|
||||
|
||||
def test_connections_start(self):
|
||||
# try to load the following schedule.
|
||||
# The schedules contains happy day nodes and faulty nodes.
|
||||
# Use the error messages to select error location and further validate the multiple reasons of failure.
|
||||
self._test_conformance(
|
||||
"connections_start.od",
|
||||
{
|
||||
("Start", "start"): [ # locate failure (contains these two substrings), make sure other do not fully overlap -> flakey test
|
||||
["input exec", "foo_in", "exist"], # 4 total reasons, a reason contains these three substrings
|
||||
["output exec", "out", "multiple"], # a reason will match to exactly one subnstring list
|
||||
["output exec", "foo_out", "exist"],
|
||||
["input data", "in", "exist"],
|
||||
]
|
||||
},
|
||||
)
|
||||
|
||||
def test_connections_end(self):
|
||||
self._test_conformance(
|
||||
"connections_end.od",
|
||||
{
|
||||
("End", "end"): [
|
||||
["input exec", "foo_in", "exist"],
|
||||
["output exec", "foo_out", "exist"],
|
||||
["input data", "in", "multiple"],
|
||||
["input data", "out2", "exist"],
|
||||
["output data", "out", "exist"],
|
||||
]
|
||||
},
|
||||
)
|
||||
|
||||
def test_connections_match(self):
|
||||
self._test_conformance(
|
||||
"connections_match.od",
|
||||
{
|
||||
("Match", "m_foo"): [
|
||||
["input exec", "foo_in", "exist"],
|
||||
["output exec", "foo", "exist"],
|
||||
["output exec", "fail", "multiple"],
|
||||
["input data", "foo_in", "exist"],
|
||||
["input data", "in", "multiple"],
|
||||
["output data", "foo_out", "exist"],
|
||||
]
|
||||
},
|
||||
)
|
||||
|
||||
def test_connections_rewrite(self):
|
||||
self._test_conformance(
|
||||
"connections_rewrite.od",
|
||||
{
|
||||
("Rewrite", "r_foo1"): [
|
||||
["input exec", "foo_in", "exist"],
|
||||
["output exec", "foo", "exist"],
|
||||
],
|
||||
("Rewrite", "r_foo2"): [
|
||||
["output exec", "out", "multiple"],
|
||||
["input data", "foo_in", "exist"],
|
||||
["input data", "in", "multiple"],
|
||||
["output data", "foo_out", "exist"],
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
def test_connections_action(self):
|
||||
self._test_conformance(
|
||||
"connections_action.od",
|
||||
{
|
||||
("Action", "a_foo1"): [
|
||||
["input exec", "foo_in", "exist"],
|
||||
["output exec", "out", "multiple"],
|
||||
["output exec", "foo", "exist"],
|
||||
["input data", "in1", "multiple"],
|
||||
],
|
||||
("Action", "a_foo2"): [
|
||||
["input exec", "in", "exist"],
|
||||
["output exec", "out3", "multiple"],
|
||||
["output exec", "out", "exist"],
|
||||
["input data", "in", "exist"],
|
||||
["output data", "out", "exist"],
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
def test_connections_modify(self):
|
||||
#TODO:
|
||||
# see test_connections_merge
|
||||
self._test_conformance(
|
||||
"connections_modify.od",
|
||||
{
|
||||
("Invalid source", "Conn_exec"): [],
|
||||
("Invalid target", "Conn_exec"): [],
|
||||
("Modify", "m_foo"): [
|
||||
["input data", "foo_in", "exist"],
|
||||
["output data", "foo_out", "exist"],
|
||||
["input data", "in", "multiple"],
|
||||
],
|
||||
("Modify", "m_exec"): [
|
||||
["input exec", "in", "exist"],
|
||||
["input exec", "in", "exist"],
|
||||
["output exec", "out", "exist"],
|
||||
]
|
||||
},
|
||||
)
|
||||
|
||||
def test_connections_merge(self):
|
||||
#TODO:
|
||||
# mm:
|
||||
# association Conn_exec [0..*] Exec -> Exec [0..*] {
|
||||
# ...;
|
||||
# }
|
||||
# m:
|
||||
# Conn_exec ( Data -> Exec) {...;} -> Invalid source type 'Merge' for link '__Conn_exec_3:Conn_exec' (1)
|
||||
# -> Invalid target type 'End' for link '__Conn_exec_3:Conn_exec' (2)
|
||||
# Conn_exec ( Exec -> Data) {...;} -> No error at all, inconsistent and unexpected behaviour (3)
|
||||
# different combinations behave unexpected
|
||||
|
||||
self._test_conformance(
|
||||
"connections_merge.od",
|
||||
{
|
||||
("Invalid source", "Conn_exec"): [], # (1), expected
|
||||
("Invalid target", "Conn_exec"): [], # (2), invalid error, should not be shown
|
||||
("Merge", "m_foo"): [
|
||||
["input data", "foo_in", "exist"],
|
||||
["input data", "in2", "multiple"],
|
||||
["output data", "foo_out", "exist"],
|
||||
],
|
||||
("Merge", "m_exec"): [ # (3), checked in Merge itself
|
||||
["input exec", "in", "exist"],
|
||||
["output exec", "out", "exist"],
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
def test_connections_store(self):
|
||||
self._test_conformance(
|
||||
"connections_store.od",
|
||||
{
|
||||
("Store", "s_foo"): [
|
||||
["input exec", "foo", "exist"],
|
||||
["output exec", "out", "multiple"],
|
||||
["output exec", "foo", "exist"],
|
||||
["input data", "foo_in", "exist"],
|
||||
["output data", "foo_out", "exist"],
|
||||
["input data", "2", "multiple"],
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
def test_connections_schedule(self):
|
||||
self._test_conformance(
|
||||
"connections_schedule.od",
|
||||
{
|
||||
("Schedule", "s_foo"): [
|
||||
["output exec", "out", "multiple"],
|
||||
["input data", "in2", "multiple"],
|
||||
]
|
||||
},
|
||||
)
|
||||
|
||||
def test_connections_loop(self):
|
||||
self._test_conformance(
|
||||
"connections_loop.od",
|
||||
{
|
||||
("Loop", "l_foo"): [
|
||||
["input exec", "foo_in", "exist"],
|
||||
["output exec", "out", "multiple"],
|
||||
["output exec", "foo", "exist"],
|
||||
["input data", "foo_in", "exist"],
|
||||
["output data", "foo_out", "exist"],
|
||||
["input data", "in", "multiple"],
|
||||
]
|
||||
},
|
||||
)
|
||||
|
||||
def test_connections_print(self):
|
||||
self._test_conformance(
|
||||
"connections_print.od",
|
||||
{
|
||||
("Print", "p_foo"): [
|
||||
["input exec", "foo_in", "exist"],
|
||||
["output exec", "out", "multiple"],
|
||||
["output exec", "foo", "exist"],
|
||||
["input data", "foo_in", "exist"],
|
||||
["output data", "out", "exist"],
|
||||
["input data", "in", "multiple"],
|
||||
]
|
||||
},
|
||||
)
|
||||
|
||||
def test_fields_start(self):
|
||||
self._test_conformance(
|
||||
"fields_start.od",
|
||||
{
|
||||
("Start", "Cardinality"): [],
|
||||
("Start", "string"): [
|
||||
["Unexpected type", "ports_exec_out", "str"],
|
||||
["Unexpected type", "ports_data_out", "str"],
|
||||
],
|
||||
("Start", '"int"'): [ # included " to avoid flakey test
|
||||
["Unexpected type", "ports_exec_out", "int"],
|
||||
["Unexpected type", "ports_data_out", "int"],
|
||||
],
|
||||
("Start", "tuple"): [
|
||||
["Unexpected type", "ports_exec_out", "tuple"],
|
||||
["Unexpected type", "ports_data_out", "tuple"],
|
||||
],
|
||||
("Start", "dict"): [
|
||||
["Unexpected type", "ports_exec_out", "dict"],
|
||||
["Unexpected type", "ports_data_out", "dict"],
|
||||
],
|
||||
("Start", "none"): [
|
||||
["Unexpected type", "ports_exec_out", "NoneType"],
|
||||
["Unexpected type", "ports_data_out", "NoneType"],
|
||||
],
|
||||
("Start", "invalid"): [
|
||||
["Invalid python", "ports_exec_out"],
|
||||
["Invalid python", "ports_data_out"],
|
||||
],
|
||||
("Start", "subtype"): [
|
||||
["Unexpected type", "ports_exec_out", "list"],
|
||||
["Unexpected type", "ports_data_out", "list"],
|
||||
],
|
||||
("Start", "code"): [
|
||||
["Unexpected type", "ports_exec_out"],
|
||||
["Unexpected type", "ports_data_out"],
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
def test_fields_end(self):
|
||||
self._test_conformance(
|
||||
"fields_end.od",
|
||||
{
|
||||
("End", "Cardinality"): [],
|
||||
("End", "string"): [
|
||||
["Unexpected type", "ports_exec_in", "str"],
|
||||
["Unexpected type", "ports_data_in", "str"],
|
||||
],
|
||||
("End", '"int"'): [
|
||||
["Unexpected type", "ports_exec_in", "int"],
|
||||
["Unexpected type", "ports_data_in", "int"],
|
||||
],
|
||||
("End", "tuple"): [
|
||||
["Unexpected type", "ports_exec_in", "tuple"],
|
||||
["Unexpected type", "ports_data_in", "tuple"],
|
||||
],
|
||||
("End", "dict"): [
|
||||
["Unexpected type", "ports_exec_in", "dict"],
|
||||
["Unexpected type", "ports_data_in", "dict"],
|
||||
],
|
||||
("End", "none"): [
|
||||
["Unexpected type", "ports_exec_in", "NoneType"],
|
||||
["Unexpected type", "ports_data_in", "NoneType"],
|
||||
],
|
||||
("End", "invalid"): [
|
||||
["Invalid python", "ports_exec_in"],
|
||||
["Invalid python", "ports_data_in"],
|
||||
],
|
||||
("End", "subtype"): [
|
||||
["Unexpected type", "ports_exec_in", "list"],
|
||||
["Unexpected type", "ports_data_in", "list"],
|
||||
],
|
||||
("End", "code"): [
|
||||
["Unexpected type", "ports_exec_in"],
|
||||
["Unexpected type", "ports_data_in"],
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
def test_fields_action(self):
|
||||
self._test_conformance(
|
||||
"fields_action.od",
|
||||
{
|
||||
("cardinality", "Action_action"): [],
|
||||
("Action", "string"): [
|
||||
["Unexpected type", "ports_exec_out", "str"],
|
||||
["Unexpected type", "ports_exec_in", "str"],
|
||||
["Unexpected type", "ports_data_out", "str"],
|
||||
["Unexpected type", "ports_data_in", "str"],
|
||||
],
|
||||
("Action", '"int"'): [
|
||||
["Unexpected type", "ports_exec_out", "int"],
|
||||
["Unexpected type", "ports_exec_in", "int"],
|
||||
["Unexpected type", "ports_data_out", "int"],
|
||||
["Unexpected type", "ports_data_in", "int"],
|
||||
],
|
||||
("Action", "tuple"): [
|
||||
["Unexpected type", "ports_exec_out", "tuple"],
|
||||
["Unexpected type", "ports_exec_in", "tuple"],
|
||||
["Unexpected type", "ports_data_out", "tuple"],
|
||||
["Unexpected type", "ports_data_in", "tuple"],
|
||||
],
|
||||
("Action", "dict"): [
|
||||
["Unexpected type", "ports_exec_out", "dict"],
|
||||
["Unexpected type", "ports_exec_in", "dict"],
|
||||
["Unexpected type", "ports_data_out", "dict"],
|
||||
["Unexpected type", "ports_data_in", "dict"],
|
||||
],
|
||||
("Action", "none"): [
|
||||
["Unexpected type", "ports_exec_out", "NoneType"],
|
||||
["Unexpected type", "ports_exec_in", "NoneType"],
|
||||
["Unexpected type", "ports_data_out", "NoneType"],
|
||||
["Unexpected type", "ports_data_in", "NoneType"],
|
||||
],
|
||||
('"Action"', '"invalid"'): [
|
||||
["Invalid python", "ports_exec_out"],
|
||||
["Invalid python", "ports_exec_in"],
|
||||
["Invalid python", "ports_data_out"],
|
||||
["Invalid python", "ports_data_in"],
|
||||
],
|
||||
('"Action_action"', '"invalid_action"'): [
|
||||
["Invalid python code"],
|
||||
["line"],
|
||||
],
|
||||
("Action", "subtype"): [
|
||||
["Unexpected type", "ports_exec_out", "list"],
|
||||
["Unexpected type", "ports_exec_in", "list"],
|
||||
["Unexpected type", "ports_data_out", "list"],
|
||||
["Unexpected type", "ports_data_in", "list"],
|
||||
],
|
||||
("Action", "code"): [
|
||||
["Unexpected type", "ports_exec_out"],
|
||||
["Unexpected type", "ports_exec_in"],
|
||||
["Unexpected type", "ports_data_out"],
|
||||
["Unexpected type", "ports_data_in"],
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
def test_fields_modify(self):
|
||||
self._test_conformance(
|
||||
"fields_modify.od",
|
||||
{
|
||||
("Modify", "string"): [
|
||||
["Unexpected type", "rename", "str"],
|
||||
["Unexpected type", "delete", "str"],
|
||||
],
|
||||
("Modify", "list"): [["Unexpected type", "rename", "list"]],
|
||||
("Modify", "set"): [["Unexpected type", "rename", "set"]],
|
||||
("Modify", "tuple"): [
|
||||
["Unexpected type", "rename", "tuple"],
|
||||
["Unexpected type", "delete", "tuple"],
|
||||
],
|
||||
("Modify", "dict"): [["Unexpected type", "delete", "dict"]],
|
||||
("Modify", "none"): [
|
||||
["Unexpected type", "rename", "NoneType"],
|
||||
["Unexpected type", "delete", "NoneType"],
|
||||
],
|
||||
("Modify", "invalid"): [
|
||||
["Invalid python", "rename"],
|
||||
["Invalid python", "delete"],
|
||||
],
|
||||
("Modify", "subtype"): [
|
||||
["Unexpected type", "rename", "dict"],
|
||||
["Unexpected type", "delete", "list"],
|
||||
],
|
||||
("Modify", "code"): [
|
||||
["Unexpected type", "rename"],
|
||||
["Unexpected type", "delete"],
|
||||
],
|
||||
("Modify", "joined"): [["rename", "delete", "disjoint"]],
|
||||
},
|
||||
)
|
||||
|
||||
def test_fields_merge(self):
|
||||
self._test_conformance(
|
||||
"fields_merge.od",
|
||||
{
|
||||
("cardinality", "Merge_ports_data_in"): [],
|
||||
("Merge", "string"): [["Unexpected type", "ports_data_in", "str"]],
|
||||
("Merge", "tuple"): [["Unexpected type", "ports_data_in", "tuple"]],
|
||||
("Merge", "dict"): [["Unexpected type", "ports_data_in", "dict"]],
|
||||
("Merge", "none"): [["Unexpected type", "ports_data_in", "NoneType"]],
|
||||
("Merge", "invalid"): [["Invalid python", "ports_data_in"]],
|
||||
("Merge", "subtype"): [["Unexpected type", "ports_data_in", "list"]],
|
||||
("Merge", "code"): [["Unexpected type", "ports_data_in"]],
|
||||
("Merge", "no"): [["Missing", "slot", "ports_data_in"]],
|
||||
},
|
||||
)
|
||||
|
||||
def test_fields_store(self):
|
||||
self._test_conformance(
|
||||
"fields_store.od",
|
||||
{
|
||||
("cardinality", "Store_ports"): [],
|
||||
("Store", "string"): [["Unexpected type", "ports", "str"]],
|
||||
("Store", "tuple"): [["Unexpected type", "ports", "tuple"]],
|
||||
("Store", "dict"): [["Unexpected type", "ports", "dict"]],
|
||||
("Store", "none"): [["Unexpected type", "ports", "NoneType"]],
|
||||
("Store", "invalid"): [["Invalid python", "ports"]],
|
||||
("Store", "subtype"): [["Unexpected type", "ports", "list"]],
|
||||
("Store", "code"): [["Unexpected type", "ports"]],
|
||||
("Store", "no"): [["Missing", "slot", "ports"]],
|
||||
},
|
||||
)
|
||||
|
||||
def test_fields_print(self):
|
||||
self._test_conformance(
|
||||
"fields_print.od",
|
||||
{
|
||||
("Print_custom", "list_custom"): [["Unexpected type", "custom", "list"]],
|
||||
("Print_custom", "set_custom"): [["Unexpected type", "custom", "set"]],
|
||||
("Print_custom", "tuple_custom"): [["Unexpected type", "custom", "tuple"]],
|
||||
("Print_custom", "dict_custom"): [["Unexpected type", "custom", "dict"]],
|
||||
("Print_custom", "none_custom"): [["Unexpected type", "custom", "NoneType"]],
|
||||
("Print_custom", "invalid_custom"): [["Invalid python", "custom"]],
|
||||
("Print_custom", "subtype_custom"): [["Unexpected type", "custom", "list"]],
|
||||
("Print_custom", "code_custom"): [["Unexpected type", "custom"]],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
43
transformation/schedule/Tests/Test_xmlparser.py
Normal file
43
transformation/schedule/Tests/Test_xmlparser.py
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import os
|
||||
import unittest
|
||||
|
||||
from transformation.schedule.rule_scheduler import RuleScheduler
|
||||
from state.devstate import DevState
|
||||
|
||||
|
||||
class MyTestCase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
state = DevState()
|
||||
self.generator = RuleScheduler(state, "", "")
|
||||
|
||||
def test_empty(self):
|
||||
try:
|
||||
self.generator.generate_schedule(
|
||||
f"{os.path.dirname(__file__)}/drawio/Empty.drawio"
|
||||
)
|
||||
# buffer = io.BytesIO()
|
||||
# self.generator.generate_dot(buffer)
|
||||
except Exception as e:
|
||||
assert False
|
||||
|
||||
def test_simple(self):
|
||||
try:
|
||||
self.generator.generate_schedule(
|
||||
f"{os.path.dirname(__file__)}/drawio/StartToEnd.drawio"
|
||||
)
|
||||
# buffer = io.BytesIO()
|
||||
# self.generator.generate_dot(buffer)
|
||||
except Exception as e:
|
||||
assert False
|
||||
|
||||
# def test_unsupported(self):
|
||||
# try:
|
||||
# self.generator.generate_schedule("Tests/drawio/Unsupported.drawio")
|
||||
# # buffer = io.BytesIO()
|
||||
# # self.generator.generate_dot(buffer)
|
||||
# except Exception as e:
|
||||
# assert(False)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
1
transformation/schedule/Tests/drawio/Empty.drawio
Normal file
1
transformation/schedule/Tests/drawio/Empty.drawio
Normal file
|
|
@ -0,0 +1 @@
|
|||
<mxGraphModel><root><mxCell id="0"/><mxCell id="1" parent="0"/></root></mxGraphModel>
|
||||
24
transformation/schedule/Tests/drawio/StartToEnd.drawio
Normal file
24
transformation/schedule/Tests/drawio/StartToEnd.drawio
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
<mxfile host="app.diagrams.net" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 Edg/136.0.0.0" version="26.2.14">
|
||||
<diagram id="EvjeMC12HsgBk4t1Z8cF" name="Page-1">
|
||||
<mxGraphModel dx="949" dy="540" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0" />
|
||||
<mxCell id="1" parent="0" />
|
||||
<mxCell id="ym0EkMZWyknAE99nMXu0-9" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="ym0EkMZWyknAE99nMXu0-7" target="ym0EkMZWyknAE99nMXu0-8">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ym0EkMZWyknAE99nMXu0-10" value="out -&gt; in" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="ym0EkMZWyknAE99nMXu0-9">
|
||||
<mxGeometry x="0.1167" y="1" relative="1" as="geometry">
|
||||
<mxPoint as="offset" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ym0EkMZWyknAE99nMXu0-7" value="Start" style="html=1;whiteSpace=wrap;" vertex="1" parent="1">
|
||||
<mxGeometry x="330" y="310" width="110" height="50" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ym0EkMZWyknAE99nMXu0-8" value="End" style="html=1;whiteSpace=wrap;" vertex="1" parent="1">
|
||||
<mxGeometry x="560" y="310" width="110" height="50" as="geometry" />
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
75
transformation/schedule/Tests/drawio/Unsupported.drawio
Normal file
75
transformation/schedule/Tests/drawio/Unsupported.drawio
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
<mxfile>
|
||||
<diagram id="prtHgNgQTEPvFCAcTncT" name="Page-1">
|
||||
<mxGraphModel dx="1223" dy="645" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0" />
|
||||
<mxCell id="1" parent="0" />
|
||||
<mxCell id="dNxyNK7c78bLwvsdeMH5-19" value="Pool" style="swimlane;html=1;childLayout=stackLayout;resizeParent=1;resizeParentMax=0;horizontal=0;startSize=20;horizontalStack=0;" parent="1" vertex="1">
|
||||
<mxGeometry x="120" y="120" width="450" height="360" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="dNxyNK7c78bLwvsdeMH5-27" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;endArrow=none;endFill=0;" parent="dNxyNK7c78bLwvsdeMH5-19" source="dNxyNK7c78bLwvsdeMH5-24" target="dNxyNK7c78bLwvsdeMH5-26" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="dNxyNK7c78bLwvsdeMH5-31" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=classic;endFill=1;" parent="dNxyNK7c78bLwvsdeMH5-19" source="dNxyNK7c78bLwvsdeMH5-28" target="dNxyNK7c78bLwvsdeMH5-30" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="dNxyNK7c78bLwvsdeMH5-35" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=classic;endFill=1;" parent="dNxyNK7c78bLwvsdeMH5-19" source="dNxyNK7c78bLwvsdeMH5-28" target="dNxyNK7c78bLwvsdeMH5-34" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="dNxyNK7c78bLwvsdeMH5-38" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=classic;endFill=1;" parent="dNxyNK7c78bLwvsdeMH5-19" source="dNxyNK7c78bLwvsdeMH5-26" target="dNxyNK7c78bLwvsdeMH5-36" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="180" y="340" />
|
||||
<mxPoint x="400" y="340" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="dNxyNK7c78bLwvsdeMH5-20" value="Lane 1" style="swimlane;html=1;startSize=20;horizontal=0;" parent="dNxyNK7c78bLwvsdeMH5-19" vertex="1">
|
||||
<mxGeometry x="20" width="430" height="120" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="dNxyNK7c78bLwvsdeMH5-25" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="dNxyNK7c78bLwvsdeMH5-20" source="dNxyNK7c78bLwvsdeMH5-23" target="dNxyNK7c78bLwvsdeMH5-24" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="dNxyNK7c78bLwvsdeMH5-23" value="" style="ellipse;whiteSpace=wrap;html=1;" parent="dNxyNK7c78bLwvsdeMH5-20" vertex="1">
|
||||
<mxGeometry x="40" y="40" width="40" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="dNxyNK7c78bLwvsdeMH5-24" value="" style="rounded=0;whiteSpace=wrap;html=1;fontFamily=Helvetica;fontSize=12;fontColor=#000000;align=center;" parent="dNxyNK7c78bLwvsdeMH5-20" vertex="1">
|
||||
<mxGeometry x="120" y="30" width="80" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="dNxyNK7c78bLwvsdeMH5-33" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=classic;endFill=1;" parent="dNxyNK7c78bLwvsdeMH5-20" source="dNxyNK7c78bLwvsdeMH5-30" target="dNxyNK7c78bLwvsdeMH5-32" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="dNxyNK7c78bLwvsdeMH5-30" value="" style="rounded=0;whiteSpace=wrap;html=1;fontFamily=Helvetica;fontSize=12;fontColor=#000000;align=center;" parent="dNxyNK7c78bLwvsdeMH5-20" vertex="1">
|
||||
<mxGeometry x="240" y="30" width="80" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="dNxyNK7c78bLwvsdeMH5-32" value="" style="ellipse;whiteSpace=wrap;html=1;" parent="dNxyNK7c78bLwvsdeMH5-20" vertex="1">
|
||||
<mxGeometry x="360" y="40" width="40" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="dNxyNK7c78bLwvsdeMH5-21" value="Lane 2" style="swimlane;html=1;startSize=20;horizontal=0;" parent="dNxyNK7c78bLwvsdeMH5-19" vertex="1">
|
||||
<mxGeometry x="20" y="120" width="430" height="120" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="dNxyNK7c78bLwvsdeMH5-29" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=classic;endFill=1;" parent="dNxyNK7c78bLwvsdeMH5-21" source="dNxyNK7c78bLwvsdeMH5-26" target="dNxyNK7c78bLwvsdeMH5-28" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="dNxyNK7c78bLwvsdeMH5-26" value="" style="rounded=0;whiteSpace=wrap;html=1;fontFamily=Helvetica;fontSize=12;fontColor=#000000;align=center;" parent="dNxyNK7c78bLwvsdeMH5-21" vertex="1">
|
||||
<mxGeometry x="120" y="30" width="80" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="dNxyNK7c78bLwvsdeMH5-28" value="" style="rhombus;whiteSpace=wrap;html=1;fontFamily=Helvetica;fontSize=12;fontColor=#000000;align=center;" parent="dNxyNK7c78bLwvsdeMH5-21" vertex="1">
|
||||
<mxGeometry x="260" y="40" width="40" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="dNxyNK7c78bLwvsdeMH5-22" value="Lane 3" style="swimlane;html=1;startSize=20;horizontal=0;" parent="dNxyNK7c78bLwvsdeMH5-19" vertex="1">
|
||||
<mxGeometry x="20" y="240" width="430" height="120" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="dNxyNK7c78bLwvsdeMH5-37" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=classic;endFill=1;" parent="dNxyNK7c78bLwvsdeMH5-22" source="dNxyNK7c78bLwvsdeMH5-34" target="dNxyNK7c78bLwvsdeMH5-36" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="dNxyNK7c78bLwvsdeMH5-34" value="" style="rounded=0;whiteSpace=wrap;html=1;fontFamily=Helvetica;fontSize=12;fontColor=#000000;align=center;" parent="dNxyNK7c78bLwvsdeMH5-22" vertex="1">
|
||||
<mxGeometry x="240" y="20" width="80" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="dNxyNK7c78bLwvsdeMH5-36" value="" style="rhombus;whiteSpace=wrap;html=1;fontFamily=Helvetica;fontSize=12;fontColor=#000000;align=center;" parent="dNxyNK7c78bLwvsdeMH5-22" vertex="1">
|
||||
<mxGeometry x="360" y="30" width="40" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
22
transformation/schedule/Tests/models/m_petrinet.od
Normal file
22
transformation/schedule/Tests/models/m_petrinet.od
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
p0:PNPlace
|
||||
p1:PNPlace
|
||||
|
||||
t0:PNTransition
|
||||
:arc (p0 -> t0)
|
||||
:arc (t0 -> p1)
|
||||
|
||||
t1:PNTransition
|
||||
:arc (p1 -> t1)
|
||||
:arc (t1 -> p0)
|
||||
|
||||
p0s:PNPlaceState {
|
||||
numTokens = 1;
|
||||
}
|
||||
|
||||
:pn_of (p0s -> p0)
|
||||
|
||||
p1s:PNPlaceState {
|
||||
numTokens = 0;
|
||||
}
|
||||
|
||||
:pn_of (p1s -> p1)
|
||||
31
transformation/schedule/Tests/models/mm_petrinet.od
Normal file
31
transformation/schedule/Tests/models/mm_petrinet.od
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
# Places, transitions, arcs (and only one kind of arc)
|
||||
|
||||
PNConnectable:Class { abstract = True; }
|
||||
|
||||
arc:Association (PNConnectable -> PNConnectable)
|
||||
|
||||
PNPlace:Class
|
||||
PNTransition:Class
|
||||
|
||||
# inhibitor arc
|
||||
inh_arc:Association (PNPlace -> PNTransition)
|
||||
|
||||
:Inheritance (PNPlace -> PNConnectable)
|
||||
:Inheritance (PNTransition -> PNConnectable)
|
||||
|
||||
# A place has a number of tokens, and that's it.
|
||||
|
||||
PNPlaceState:Class
|
||||
PNPlaceState_numTokens:AttributeLink (PNPlaceState -> Integer) {
|
||||
name = "numTokens";
|
||||
optional = False;
|
||||
constraint = `"numTokens cannot be negative" if get_value(get_target(this)) < 0 else None`;
|
||||
}
|
||||
|
||||
pn_of:Association (PNPlaceState -> PNPlace) {
|
||||
# one-to-one
|
||||
source_lower_cardinality = 1;
|
||||
source_upper_cardinality = 1;
|
||||
target_lower_cardinality = 1;
|
||||
target_upper_cardinality = 1;
|
||||
}
|
||||
13
transformation/schedule/Tests/models/rules/transitions.od
Normal file
13
transformation/schedule/Tests/models/rules/transitions.od
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
# A place with no tokens:
|
||||
|
||||
p:RAM_PNPlace
|
||||
ps:RAM_PNPlaceState {
|
||||
RAM_numTokens = `True`;
|
||||
}
|
||||
:RAM_pn_of (ps -> p)
|
||||
|
||||
# An incoming arc from that place to our transition:
|
||||
|
||||
t:RAM_PNTransition
|
||||
|
||||
:RAM_arc (p -> t)
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
start:Start {
|
||||
ports_data_out = `["1", "2", "3"]`;
|
||||
}
|
||||
|
||||
m:Match{
|
||||
file="rules/transition.od";
|
||||
}
|
||||
m2:Match{
|
||||
file="rules/transition.od";
|
||||
}
|
||||
m3:Match{
|
||||
file="rules/transition.od";
|
||||
}
|
||||
|
||||
a_void:Action{
|
||||
ports_data_in = `["in1", "in2"]`;
|
||||
ports_data_out = `["out1", "out2"]`;
|
||||
action=`print("hello foo1")`;
|
||||
}
|
||||
|
||||
a_foo1:Action{
|
||||
ports_data_in = `["in1", "in2"]`;
|
||||
ports_data_out = `["out1", "out2"]`;
|
||||
action=`print("hello foo1")`;
|
||||
}
|
||||
|
||||
a_foo2:Action{
|
||||
ports_exec_in = `["in2"]`;
|
||||
ports_exec_out = `["out2", "out3"]`;
|
||||
action=`print("hello foo2")`;
|
||||
}
|
||||
|
||||
end:End {
|
||||
ports_data_in = `["1", "2", "3"]`;
|
||||
}
|
||||
|
||||
:Conn_exec (start -> m) {from="out";to="in";}
|
||||
:Conn_exec (m -> m2) {from="fail";to="in";}
|
||||
:Conn_exec (m -> m3) {from="success";to="in";}
|
||||
|
||||
:Conn_exec (m2 -> a_foo1) {from="success";to="in";}
|
||||
:Conn_exec (m2 -> a_foo1) {from="fail";to="in";}
|
||||
:Conn_exec (m3 -> a_foo1) {from="success";to="foo_in";}
|
||||
:Conn_exec (m3 -> a_foo2) {from="fail";to="in2";}
|
||||
|
||||
:Conn_exec (a_foo1 -> a_foo2) {from="out";to="in";}
|
||||
:Conn_exec (a_foo1 -> a_foo2) {from="out";to="in2";}
|
||||
:Conn_exec (a_foo1 -> a_foo2) {from="foo";to="in2";}
|
||||
:Conn_exec (a_foo2 -> end) {from="out";to="in";}
|
||||
:Conn_exec (a_foo2 -> end) {from="out2";to="in";}
|
||||
:Conn_exec (a_foo2 -> end) {from="out3";to="in";}
|
||||
:Conn_exec (a_foo2 -> end) {from="out3";to="in";}
|
||||
|
||||
:Conn_data (start -> a_foo2) {from="1";to="in";}
|
||||
:Conn_data (a_foo2-> m2) {from="out";to="in";}
|
||||
|
||||
:Conn_data (start -> a_foo1) {from="1";to="in1";}
|
||||
:Conn_data (start -> a_foo1) {from="2";to="in1";}
|
||||
:Conn_data (start -> a_foo1) {from="3";to="in2";}
|
||||
:Conn_data (a_foo1 -> end) {from="out1";to="1";}
|
||||
:Conn_data (a_foo1 -> end) {from="out1";to="2";}
|
||||
:Conn_data (a_foo1 -> end) {from="out2";to="3";}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
start:Start
|
||||
|
||||
m:Match{
|
||||
file="rules/transition.od";
|
||||
}
|
||||
m2:Match{
|
||||
file="rules/transition.od";
|
||||
}
|
||||
m3:Match{
|
||||
file="rules/transition.od";
|
||||
}
|
||||
end:End {
|
||||
ports_exec_in = `["out", "in"]`;
|
||||
ports_data_in = `["out", "in"]`;
|
||||
}
|
||||
|
||||
:Conn_exec (start -> m) {from="out";to="in";}
|
||||
:Conn_exec (m -> m2) {from="fail";to="in";}
|
||||
:Conn_exec (m -> m3) {from="success";to="in";}
|
||||
|
||||
:Conn_exec (m2 -> end) {from="success";to="in";}
|
||||
:Conn_exec (m2 -> end) {from="fail";to="out";}
|
||||
:Conn_exec (m3 -> end) {from="success";to="out";}
|
||||
:Conn_exec (m3 -> end) {from="fail";to="foo_in";}
|
||||
:Conn_exec (end -> m) {from="foo_out";to="in";}
|
||||
|
||||
:Conn_data (m -> end) {from="out";to="in";}
|
||||
:Conn_data (m2 -> end) {from="out";to="in";}
|
||||
:Conn_data (m3 -> end) {from="out";to="out";}
|
||||
:Conn_data (m3 -> end) {from="out";to="out2";}
|
||||
:Conn_data (end -> m) {from="out";to="in";}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
start:Start {
|
||||
ports_data_out = `["1", "2", "3"]`;
|
||||
}
|
||||
|
||||
m:Match{
|
||||
file="rules/transition.od";
|
||||
}
|
||||
m2:Match{
|
||||
file="rules/transition.od";
|
||||
}
|
||||
m3:Match{
|
||||
file="rules/transition.od";
|
||||
}
|
||||
|
||||
l:Loop
|
||||
l_foo:Loop
|
||||
l_void:Loop
|
||||
|
||||
end:End {
|
||||
ports_data_in = `["1", "2", "3"]`;
|
||||
}
|
||||
|
||||
:Conn_exec (start -> m) {from="out";to="in";}
|
||||
:Conn_exec (m -> m2) {from="fail";to="in";}
|
||||
:Conn_exec (m -> m3) {from="success";to="in";}
|
||||
|
||||
:Conn_exec (m2 -> l_foo) {from="success";to="in";}
|
||||
:Conn_exec (m2 -> l_foo) {from="fail";to="in";}
|
||||
:Conn_exec (m3 -> l_foo) {from="success";to="foo_in";}
|
||||
|
||||
:Conn_exec (l_foo -> l_foo) {from="out";to="in";}
|
||||
:Conn_exec (l_foo -> end) {from="out";to="in";}
|
||||
:Conn_exec (l_foo -> end) {from="it";to="in";}
|
||||
:Conn_exec (l_foo -> end) {from="foo";to="in";}
|
||||
|
||||
:Conn_data (start -> l) {from="1";to="in";}
|
||||
:Conn_data (l -> m2) {from="out";to="in";}
|
||||
|
||||
:Conn_data (start -> l_foo) {from="1";to="in";}
|
||||
:Conn_data (start -> l_foo) {from="2";to="in";}
|
||||
:Conn_data (start -> l_foo) {from="3";to="foo_in";}
|
||||
:Conn_data (l_foo -> end) {from="out";to="1";}
|
||||
:Conn_data (l_foo -> end) {from="out";to="2";}
|
||||
:Conn_data (l_foo -> end) {from="foo_out";to="3";}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
start:Start {
|
||||
ports_data_out = `["1", "2", "3"]`;
|
||||
}
|
||||
|
||||
m:Match{
|
||||
file="rules/transition.od";
|
||||
}
|
||||
m2:Match{
|
||||
file="rules/transition.od";
|
||||
}
|
||||
m3:Match{
|
||||
file="rules/transition.od";
|
||||
}
|
||||
|
||||
m_foo:Match{
|
||||
file="rules/transition.od";
|
||||
}
|
||||
|
||||
m_void:Match{
|
||||
file="rules/transition.od";
|
||||
}
|
||||
|
||||
end:End {
|
||||
ports_data_in = `["1", "2", "3"]`;
|
||||
}
|
||||
|
||||
:Conn_exec (start -> m) {from="out";to="in";}
|
||||
:Conn_exec (m -> m2) {from="fail";to="in";}
|
||||
:Conn_exec (m -> m3) {from="success";to="in";}
|
||||
|
||||
:Conn_exec (m2 -> m_foo) {from="success";to="in";}
|
||||
:Conn_exec (m2 -> m_foo) {from="fail";to="in";}
|
||||
:Conn_exec (m3 -> m_foo) {from="success";to="foo_in";}
|
||||
:Conn_exec (m3 -> m_foo) {from="fail";to="in";}
|
||||
|
||||
:Conn_exec (m_foo -> end) {from="fail";to="in";}
|
||||
:Conn_exec (m_foo -> end) {from="success";to="in";}
|
||||
:Conn_exec (m_foo -> end) {from="fail";to="in";}
|
||||
:Conn_exec (m_foo -> end) {from="foo";to="in";}
|
||||
|
||||
:Conn_data (start -> m) {from="1";to="in";}
|
||||
:Conn_data (m -> m2) {from="out";to="in";}
|
||||
|
||||
:Conn_data (start -> m_foo) {from="1";to="in";}
|
||||
:Conn_data (start -> m_foo) {from="2";to="in";}
|
||||
:Conn_data (start -> m_foo) {from="3";to="foo_in";}
|
||||
:Conn_data (m_foo -> end) {from="out";to="1";}
|
||||
:Conn_data (m_foo -> end) {from="out";to="2";}
|
||||
:Conn_data (m_foo -> end) {from="foo_out";to="3";}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
start:Start {
|
||||
ports_data_out = `["1", "2", "3"]`;
|
||||
}
|
||||
|
||||
m:Match{
|
||||
file="rules/transition.od";
|
||||
}
|
||||
m2:Match{
|
||||
file="rules/transition.od";
|
||||
}
|
||||
m3:Match{
|
||||
file="rules/transition.od";
|
||||
}
|
||||
|
||||
m_exec:Merge {
|
||||
ports_data_in = `["in1", "in2"]`;
|
||||
}
|
||||
|
||||
m_foo:Merge {
|
||||
ports_data_in = `["in1", "in2"]`;
|
||||
}
|
||||
|
||||
m_void:Merge {
|
||||
ports_data_in = `["in1", "in2"]`;
|
||||
}
|
||||
|
||||
end:End {
|
||||
ports_data_in = `["1", "2", "3"]`;
|
||||
}
|
||||
|
||||
:Conn_exec (start -> m) {from="out";to="in";}
|
||||
:Conn_exec (m -> m2) {from="fail";to="in";}
|
||||
:Conn_exec (m -> m3) {from="success";to="in";}
|
||||
|
||||
:Conn_exec (m2 -> m_exec) {from="success";to="in";}
|
||||
:Conn_exec (m_exec -> end) {from="out";to="in";}
|
||||
|
||||
:Conn_data (start -> m_foo) {from="1";to="in1";}
|
||||
:Conn_data (start -> m_foo) {from="1";to="in2";}
|
||||
:Conn_data (start -> m_foo) {from="2";to="in2";}
|
||||
:Conn_data (start -> m_foo) {from="3";to="foo_in";}
|
||||
:Conn_data (m_foo -> end) {from="out";to="1";}
|
||||
:Conn_data (m_foo -> end) {from="out";to="2";}
|
||||
:Conn_data (m_foo -> end) {from="foo_out";to="3";}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
start:Start {
|
||||
ports_data_out = `["1", "2", "3"]`;
|
||||
}
|
||||
|
||||
m:Match{
|
||||
file="rules/transition.od";
|
||||
}
|
||||
m2:Match{
|
||||
file="rules/transition.od";
|
||||
}
|
||||
m3:Match{
|
||||
file="rules/transition.od";
|
||||
}
|
||||
|
||||
m_exec:Modify
|
||||
m_foo:Modify
|
||||
m_void:Modify
|
||||
|
||||
mo:Modify
|
||||
|
||||
end:End {
|
||||
ports_data_in = `["1", "2", "3"]`;
|
||||
}
|
||||
|
||||
:Conn_exec (start -> m) {from="out";to="in";}
|
||||
:Conn_exec (m -> m2) {from="fail";to="in";}
|
||||
:Conn_exec (m -> m3) {from="success";to="in";}
|
||||
|
||||
:Conn_exec (m2 -> m_exec) {from="success";to="in";}
|
||||
:Conn_exec (m2 -> m_exec) {from="fail";to="in";}
|
||||
|
||||
:Conn_exec (m_exec -> end) {from="out";to="in";}
|
||||
|
||||
:Conn_data (start -> mo) {from="1";to="in";}
|
||||
:Conn_data (mo -> m2) {from="out";to="in";}
|
||||
|
||||
:Conn_data (start -> m_foo) {from="1";to="in";}
|
||||
:Conn_data (start -> m_foo) {from="2";to="in";}
|
||||
:Conn_data (start -> m_foo) {from="3";to="foo_in";}
|
||||
:Conn_data (m_foo -> end) {from="out";to="1";}
|
||||
:Conn_data (m_foo -> end) {from="out";to="2";}
|
||||
:Conn_data (m_foo -> end) {from="foo_out";to="3";}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
start:Start {
|
||||
ports_data_out = `["1", "2", "3"]`;
|
||||
}
|
||||
|
||||
m:Match{
|
||||
file="rules/transition.od";
|
||||
}
|
||||
m2:Match{
|
||||
file="rules/transition.od";
|
||||
}
|
||||
m3:Match{
|
||||
file="rules/transition.od";
|
||||
}
|
||||
|
||||
p_foo:Print
|
||||
p_void:Print
|
||||
|
||||
p:Print
|
||||
|
||||
end:End
|
||||
|
||||
:Conn_exec (start -> m) {from="out";to="in";}
|
||||
:Conn_exec (m -> m2) {from="fail";to="in";}
|
||||
:Conn_exec (m -> m3) {from="success";to="in";}
|
||||
|
||||
:Conn_exec (m2 -> p_foo) {from="success";to="in";}
|
||||
:Conn_exec (m2 -> p_foo) {from="fail";to="in";}
|
||||
:Conn_exec (m3 -> p_foo) {from="success";to="foo_in";}
|
||||
:Conn_exec (m3 -> p) {from="fail";to="in";}
|
||||
:Conn_exec (p -> end) {from="out";to="in";}
|
||||
|
||||
:Conn_exec (p_foo -> p_foo) {from="out";to="in";}
|
||||
:Conn_exec (p_foo -> end) {from="out";to="in";}
|
||||
:Conn_exec (p_foo -> end) {from="foo";to="in";}
|
||||
|
||||
:Conn_data (start -> p) {from="1";to="in";}
|
||||
|
||||
:Conn_data (start -> p_foo) {from="1";to="in";}
|
||||
:Conn_data (start -> p_foo) {from="2";to="in";}
|
||||
:Conn_data (start -> p_foo) {from="3";to="foo_in";}
|
||||
:Conn_data (p_foo -> m2) {from="out";to="in";}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
start:Start {
|
||||
ports_data_out = `["1", "2", "3"]`;
|
||||
}
|
||||
m:Match{
|
||||
file="rules/transition.od";
|
||||
}
|
||||
m2:Match{
|
||||
file="rules/transition.od";
|
||||
}
|
||||
m3:Match{
|
||||
file="rules/transition.od";
|
||||
}
|
||||
|
||||
r_foo1:Rewrite{
|
||||
file="rules/transition.od";
|
||||
}
|
||||
|
||||
r_foo2:Rewrite{
|
||||
file="rules/transition.od";
|
||||
}
|
||||
r_void:Rewrite{
|
||||
file="rules/transition.od";
|
||||
}
|
||||
|
||||
end:End {
|
||||
ports_data_in = `["1", "2", "3"]`;
|
||||
}
|
||||
|
||||
|
||||
:Conn_exec (start -> m) {from="out";to="in";}
|
||||
:Conn_exec (m -> m2) {from="fail";to="in";}
|
||||
:Conn_exec (m -> m3) {from="success";to="in";}
|
||||
|
||||
:Conn_exec (m2 -> r_foo1) {from="success";to="in";}
|
||||
:Conn_exec (m2 -> r_foo1) {from="fail";to="in";}
|
||||
:Conn_exec (m3 -> r_foo1) {from="success";to="foo_in";}
|
||||
:Conn_exec (m3 -> r_foo1) {from="fail";to="in";}
|
||||
|
||||
:Conn_exec (r_foo1 -> r_foo2) {from="out";to="in";}
|
||||
:Conn_exec (r_foo1 -> end) {from="foo";to="in";}
|
||||
:Conn_exec (r_foo2 -> end) {from="out";to="in";}
|
||||
:Conn_exec (r_foo2 -> end) {from="out";to="in";}
|
||||
|
||||
:Conn_data (start -> r_foo1) {from="1";to="in";}
|
||||
:Conn_data (r_foo1-> m2) {from="out";to="in";}
|
||||
|
||||
:Conn_data (start -> r_foo2) {from="1";to="in";}
|
||||
:Conn_data (start -> r_foo2) {from="2";to="in";}
|
||||
:Conn_data (start -> r_foo2) {from="3";to="foo_in";}
|
||||
:Conn_data (r_foo2 -> end) {from="out";to="1";}
|
||||
:Conn_data (r_foo2 -> end) {from="out";to="2";}
|
||||
:Conn_data (r_foo2 -> end) {from="foo_out";to="3";}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
start:Start {
|
||||
ports_data_out = `["1", "2", "3"]`;
|
||||
}
|
||||
|
||||
m:Match{
|
||||
file="rules/transition.od";
|
||||
}
|
||||
m2:Match{
|
||||
file="rules/transition.od";
|
||||
}
|
||||
m3:Match{
|
||||
file="rules/transition.od";
|
||||
}
|
||||
|
||||
s_foo:Schedule{
|
||||
file="hello.od";
|
||||
}
|
||||
|
||||
s_void:Schedule{
|
||||
file="hello.od";
|
||||
}
|
||||
|
||||
end:End {
|
||||
ports_data_in = `["1", "2", "3"]`;
|
||||
}
|
||||
|
||||
:Conn_exec (start -> m) {from="out";to="in";}
|
||||
:Conn_exec (m -> m2) {from="fail";to="in";}
|
||||
:Conn_exec (m -> m3) {from="success";to="in";}
|
||||
|
||||
:Conn_exec (m2 -> s_foo) {from="success";to="in";}
|
||||
:Conn_exec (m2 -> s_foo) {from="fail";to="in";}
|
||||
:Conn_exec (m3 -> s_foo) {from="success";to="foo";}
|
||||
:Conn_exec (m3 -> s_foo) {from="fail";to="foo2";}
|
||||
|
||||
:Conn_exec (s_foo -> s_foo) {from="out";to="in";}
|
||||
:Conn_exec (s_foo -> s_foo) {from="out";to="in2";}
|
||||
:Conn_exec (s_foo -> s_foo) {from="foo";to="foo3";}
|
||||
:Conn_exec (s_foo -> end) {from="out4";to="in";}
|
||||
:Conn_exec (s_foo -> end) {from="out2";to="in";}
|
||||
:Conn_exec (s_foo -> end) {from="out5";to="in";}
|
||||
:Conn_exec (s_foo -> end) {from="out3";to="in";}
|
||||
|
||||
:Conn_data (start -> s_foo) {from="1";to="in1";}
|
||||
:Conn_data (start -> s_foo) {from="1";to="in2";}
|
||||
:Conn_data (start -> s_foo) {from="2";to="in2";}
|
||||
:Conn_data (start -> s_foo) {from="3";to="foo_in";}
|
||||
:Conn_data (s_foo -> end) {from="out";to="1";}
|
||||
:Conn_data (s_foo -> end) {from="out";to="2";}
|
||||
:Conn_data (s_foo -> end) {from="foo_out";to="3";}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
start:Start {
|
||||
ports_exec_out = `["out", "in"]`;
|
||||
ports_data_out = `["out", "in"]`;
|
||||
}
|
||||
|
||||
m:Match{
|
||||
file="rules/transition.od";
|
||||
}
|
||||
m2:Match{
|
||||
file="rules/transition.od";
|
||||
}
|
||||
m3:Match{
|
||||
file="rules/transition.od";
|
||||
}
|
||||
end:End
|
||||
|
||||
:Conn_exec (start -> m) {from="out";to="in";}
|
||||
:Conn_exec (start -> m) {from="out";to="in";}
|
||||
:Conn_exec (start -> m) {from="in";to="in";}
|
||||
:Conn_exec (start -> m) {from="foo_out";to="in";}
|
||||
:Conn_exec (m -> start) {from="fail";to="foo_in";}
|
||||
:Conn_exec (m -> end) {from="success";to="in";}
|
||||
|
||||
:Conn_data (start -> m) {from="out";to="in";}
|
||||
:Conn_data (start -> m2) {from="out";to="in";}
|
||||
:Conn_data (start -> m3) {from="in";to="in";}
|
||||
:Conn_data (m -> start) {from="out";to="in";}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
start:Start {
|
||||
ports_data_out = `["1", "2", "3"]`;
|
||||
}
|
||||
|
||||
m:Match{
|
||||
file="rules/transition.od";
|
||||
}
|
||||
m2:Match{
|
||||
file="rules/transition.od";
|
||||
}
|
||||
m3:Match{
|
||||
file="rules/transition.od";
|
||||
}
|
||||
|
||||
s_foo:Store {
|
||||
ports = `["1", "2", "3"]`;
|
||||
}
|
||||
|
||||
s_void:Store {
|
||||
ports = `["1", "2", "3"]`;
|
||||
}
|
||||
|
||||
end:End {
|
||||
ports_data_in = `["1", "2", "3"]`;
|
||||
}
|
||||
|
||||
:Conn_exec (start -> m) {from="out";to="in";}
|
||||
:Conn_exec (m -> m2) {from="fail";to="in";}
|
||||
:Conn_exec (m -> m3) {from="success";to="in";}
|
||||
|
||||
:Conn_exec (m2 -> s_foo) {from="success";to="in";}
|
||||
:Conn_exec (m2 -> s_foo) {from="fail";to="in";}
|
||||
:Conn_exec (m3 -> s_foo) {from="success";to="1";}
|
||||
:Conn_exec (m3 -> s_foo) {from="fail";to="foo";}
|
||||
|
||||
:Conn_exec (s_foo -> end) {from="out";to="in";}
|
||||
:Conn_exec (s_foo -> s_foo) {from="1";to="2";}
|
||||
:Conn_exec (s_foo -> end) {from="out";to="in";}
|
||||
:Conn_exec (s_foo -> s_foo) {from="foo";to="2";}
|
||||
|
||||
:Conn_data (start -> s_foo) {from="1";to="1";}
|
||||
:Conn_data (start -> s_foo) {from="1";to="2";}
|
||||
:Conn_data (start -> s_foo) {from="2";to="2";}
|
||||
:Conn_data (start -> s_foo) {from="3";to="foo_in";}
|
||||
:Conn_data (s_foo -> end) {from="out";to="1";}
|
||||
:Conn_data (s_foo -> end) {from="out";to="2";}
|
||||
:Conn_data (s_foo -> end) {from="foo_out";to="3";}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
string:Action {
|
||||
ports_exec_in = `'["out", "in"]'`;
|
||||
ports_exec_out = `'["out", "in"]'`;
|
||||
ports_data_in = `'["out", "in"]'`;
|
||||
ports_data_out = `'["out", "in"]'`;
|
||||
action = `'["out", "in"]'`;
|
||||
}
|
||||
|
||||
int:Action {
|
||||
ports_exec_in = `123`;
|
||||
ports_exec_out = `123`;
|
||||
ports_data_in = `123`;
|
||||
ports_data_out = `123`;
|
||||
action = `123`;
|
||||
}
|
||||
|
||||
list:Action {
|
||||
ports_exec_out = `["out", "in"]`;
|
||||
ports_exec_in = `["out", "in"]`;
|
||||
ports_data_out = `["out", "in"]`;
|
||||
ports_data_in = `["out", "in"]`;
|
||||
action = `["out", "in"]`;
|
||||
}
|
||||
set:Action {
|
||||
ports_exec_in = `{"out", "in"}`;
|
||||
ports_exec_out = `{"out", "in"}`;
|
||||
ports_data_in = `{"out", "in"}`;
|
||||
ports_data_out = `{"out", "in"}`;
|
||||
action = `{"out", "in"}`;
|
||||
}
|
||||
|
||||
tuple:Action {
|
||||
ports_exec_in = `("out", "in")`;
|
||||
ports_exec_out = `("out", "in")`;
|
||||
ports_data_in = `("out", "in")`;
|
||||
ports_data_out = `("out", "in")`;
|
||||
action = `("out", "in")`;
|
||||
}
|
||||
|
||||
dict:Action {
|
||||
ports_exec_in = `{"out": "in"}`;
|
||||
ports_exec_out = `{"out": "in"}`;
|
||||
ports_data_in = `{"out": "in"}`;
|
||||
ports_data_out = `{"out": "in"}`;
|
||||
action = `{"out": "in"}`;
|
||||
}
|
||||
|
||||
none:Action {
|
||||
ports_exec_in = `None`;
|
||||
ports_exec_out = `None`;
|
||||
ports_data_in = `None`;
|
||||
ports_data_out = `None`;
|
||||
action = `None`;
|
||||
}
|
||||
|
||||
invalid:Action {
|
||||
ports_exec_in = `[{a(0)['qkja("fyvka`;
|
||||
ports_exec_out = `[{a(0)['qkja("fyvka`;
|
||||
ports_data_in = `["", [{]]`;
|
||||
ports_data_out = `["", [{]]`;
|
||||
action = `hu(ja&{]8}]`;
|
||||
}
|
||||
|
||||
subtype:Action {
|
||||
ports_exec_in = `[1, 2]`;
|
||||
ports_exec_out = `[1, 2]`;
|
||||
ports_data_in = `[1, 2]`;
|
||||
ports_data_out = `[1, 2]`;
|
||||
action = `[1, 2]`;
|
||||
}
|
||||
|
||||
code:Action {
|
||||
ports_exec_in = `print("hello world")`;
|
||||
ports_exec_out = `print("hello world")`;
|
||||
ports_data_in = `print("hello world")`;
|
||||
ports_data_out = `print("hello world")`;
|
||||
action = `print("hello world")`;
|
||||
}
|
||||
|
||||
no:Action
|
||||
|
||||
start:Start
|
||||
end:End
|
||||
52
transformation/schedule/Tests/models/schedule/fields_end.od
Normal file
52
transformation/schedule/Tests/models/schedule/fields_end.od
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
start:Start
|
||||
|
||||
string:End {
|
||||
ports_exec_in = `'["out", "in"]'`;
|
||||
ports_data_in = `'["out", "in"]'`;
|
||||
}
|
||||
|
||||
int:End {
|
||||
ports_exec_in = `123`;
|
||||
ports_data_in = `123`;
|
||||
}
|
||||
|
||||
list:End {
|
||||
ports_exec_in = `["out", "in"]`;
|
||||
ports_data_in = `["out", "in"]`;
|
||||
}
|
||||
set:End {
|
||||
ports_exec_in = `{"out", "in"}`;
|
||||
ports_data_in = `{"out", "in"}`;
|
||||
}
|
||||
|
||||
tuple:End {
|
||||
ports_exec_in = `("out", "in")`;
|
||||
ports_data_in = `("out", "in")`;
|
||||
}
|
||||
|
||||
dict:End {
|
||||
ports_exec_in = `{"out": "in"}`;
|
||||
ports_data_in = `{"out": "in"}`;
|
||||
}
|
||||
|
||||
none:End {
|
||||
ports_exec_in = `None`;
|
||||
ports_data_in = `None`;
|
||||
}
|
||||
|
||||
invalid:End {
|
||||
ports_exec_in = `[{a(0)['qkja("fyvka`;
|
||||
ports_data_in = `["", [{]]`;
|
||||
}
|
||||
|
||||
subtype:End {
|
||||
ports_exec_in = `[1, 2]`;
|
||||
ports_data_in = `[1, 2]`;
|
||||
}
|
||||
|
||||
code:End {
|
||||
ports_exec_in = `print("hello world")`;
|
||||
ports_data_in = `print("hello world")`;
|
||||
}
|
||||
|
||||
no:End
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
string:Merge {
|
||||
ports_data_in = `'["out", "in"]'`;
|
||||
}
|
||||
|
||||
list:Merge {
|
||||
ports_data_in = `["out", "in"]`;
|
||||
}
|
||||
set:Merge {
|
||||
ports_data_in = `{"out", "in"}`;
|
||||
}
|
||||
|
||||
tuple:Merge {
|
||||
ports_data_in = `("out", "in")`;
|
||||
}
|
||||
|
||||
dict:Merge {
|
||||
ports_data_in = `{"out": "in"}`;
|
||||
}
|
||||
|
||||
none:Merge {
|
||||
ports_data_in = `None`;
|
||||
}
|
||||
|
||||
invalid:Merge {
|
||||
ports_data_in = `["", [{]]`;
|
||||
}
|
||||
|
||||
subtype:Merge {
|
||||
ports_data_in = `[1, 2]`;
|
||||
}
|
||||
|
||||
code:Merge {
|
||||
ports_data_in = `print("hello world")`;
|
||||
}
|
||||
|
||||
no:Merge
|
||||
|
||||
start:Start
|
||||
end:End
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
string:Modify {
|
||||
rename = `'["out", "in"]'`;
|
||||
delete = `'["out", "in"]'`;
|
||||
}
|
||||
|
||||
list:Modify {
|
||||
rename = `["out", "in"]`;
|
||||
delete = `["out", "in"]`;
|
||||
}
|
||||
set:Modify {
|
||||
rename = `{"out", "in"}`;
|
||||
delete = `{"out", "in"}`;
|
||||
}
|
||||
|
||||
tuple:Modify {
|
||||
rename = `("out", "in")`;
|
||||
delete = `("out", "in")`;
|
||||
}
|
||||
|
||||
dict:Modify {
|
||||
rename = `{"out": "in"}`;
|
||||
delete = `{"out": "in"}`;
|
||||
}
|
||||
|
||||
none:Modify {
|
||||
rename = `None`;
|
||||
delete = `None`;
|
||||
}
|
||||
|
||||
invalid:Modify {
|
||||
rename = `[{a(0)['qkja("fyvka`;
|
||||
delete = `["", [{]]`;
|
||||
}
|
||||
|
||||
subtype:Modify {
|
||||
rename = `{1: 2}`;
|
||||
delete = `[1, 2]`;
|
||||
}
|
||||
|
||||
code:Modify {
|
||||
rename = `print("hello world")`;
|
||||
delete = `print("hello world")`;
|
||||
}
|
||||
|
||||
joined:Modify {
|
||||
rename = `{"a":"1", "b":"2", "c":"3"}`;
|
||||
delete = `{"a", "d"}`;
|
||||
}
|
||||
|
||||
start:Start
|
||||
end:End
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
string:Print {
|
||||
custom = `'["port_out", "port_in"]'`;
|
||||
}
|
||||
|
||||
list:Print {
|
||||
custom = `["port_out", "port_in"]`;
|
||||
}
|
||||
set:Print {
|
||||
custom = `{"port_out", "port_in"}`;
|
||||
}
|
||||
|
||||
tuple:Print {
|
||||
custom = `("port_out", "port_in")`;
|
||||
}
|
||||
|
||||
dict:Print {
|
||||
custom = `{"port_out": "port_in"}`;
|
||||
}
|
||||
|
||||
none:Print {
|
||||
custom = `None`;
|
||||
}
|
||||
|
||||
invalid:Print {
|
||||
custom = `["", [{]]`;
|
||||
}
|
||||
|
||||
subtype:Print {
|
||||
custom = `[1, 2]`;
|
||||
}
|
||||
|
||||
code:Print {
|
||||
custom = `print("hello world")`;
|
||||
}
|
||||
|
||||
no:Print
|
||||
|
||||
start:Start
|
||||
end:End
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
string:Start {
|
||||
ports_exec_out = `'["out", "in"]'`;
|
||||
ports_data_out = `'["out", "in"]'`;
|
||||
}
|
||||
|
||||
int:Start {
|
||||
ports_exec_out = `123`;
|
||||
ports_data_out = `123`;
|
||||
}
|
||||
|
||||
list:Start {
|
||||
ports_exec_out = `["out", "in"]`;
|
||||
ports_data_out = `["out", "in"]`;
|
||||
}
|
||||
set:Start {
|
||||
ports_exec_out = `{"out", "in"}`;
|
||||
ports_data_out = `{"out", "in"}`;
|
||||
}
|
||||
|
||||
tuple:Start {
|
||||
ports_exec_out = `("out", "in")`;
|
||||
ports_data_out = `("out", "in")`;
|
||||
}
|
||||
|
||||
dict:Start {
|
||||
ports_exec_out = `{"out": "in"}`;
|
||||
ports_data_out = `{"out": "in"}`;
|
||||
}
|
||||
|
||||
none:Start {
|
||||
ports_exec_out = `None`;
|
||||
ports_data_out = `None`;
|
||||
}
|
||||
|
||||
invalid:Start {
|
||||
ports_exec_out = `[{a(0)['qkja("fyvka`;
|
||||
ports_data_out = `["", [{]]`;
|
||||
}
|
||||
|
||||
subtype:Start {
|
||||
ports_exec_out = `[1, 2]`;
|
||||
ports_data_out = `[1, 2]`;
|
||||
}
|
||||
|
||||
code:Start {
|
||||
ports_exec_out = `print("hello world")`;
|
||||
ports_data_out = `print("hello world")`;
|
||||
}
|
||||
|
||||
no:Start
|
||||
|
||||
end:End
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
string:Store {
|
||||
ports = `'["port_out", "port_in"]'`;
|
||||
}
|
||||
|
||||
list:Store {
|
||||
ports = `["port_out", "port_in"]`;
|
||||
}
|
||||
set:Store {
|
||||
ports = `{"port_out", "port_in"}`;
|
||||
}
|
||||
|
||||
tuple:Store {
|
||||
ports = `("port_out", "port_in")`;
|
||||
}
|
||||
|
||||
dict:Store {
|
||||
ports = `{"port_out": "port_in"}`;
|
||||
}
|
||||
|
||||
none:Store {
|
||||
ports = `None`;
|
||||
}
|
||||
|
||||
invalid:Store {
|
||||
ports = `["", [{]]`;
|
||||
}
|
||||
|
||||
subtype:Store {
|
||||
ports = `[1, 2]`;
|
||||
}
|
||||
|
||||
code:Store {
|
||||
ports = `print("hello world")`;
|
||||
}
|
||||
|
||||
no:Store
|
||||
|
||||
start:Start
|
||||
end:End
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
start:Start
|
||||
end:End
|
||||
end2:End
|
||||
|
||||
:Conn_exec (start -> end) {from="out";to="in";}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
start:Start
|
||||
start2:Start
|
||||
end:End
|
||||
|
||||
:Conn_exec (start -> end) {from="out";to="in";}
|
||||
1
transformation/schedule/Tests/models/schedule/no_end.od
Normal file
1
transformation/schedule/Tests/models/schedule/no_end.od
Normal file
|
|
@ -0,0 +1 @@
|
|||
start:Start
|
||||
|
|
@ -0,0 +1 @@
|
|||
end:End
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
start:Start
|
||||
end:End
|
||||
:Conn_exec (start -> end) {from="out";to="in";}
|
||||
0
transformation/schedule/__init__.py
Normal file
0
transformation/schedule/__init__.py
Normal file
BIN
transformation/schedule/doc/images/example_1.png
Normal file
BIN
transformation/schedule/doc/images/example_1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 37 KiB |
BIN
transformation/schedule/doc/images/example_2.png
Normal file
BIN
transformation/schedule/doc/images/example_2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 80 KiB |
BIN
transformation/schedule/doc/images/example_3.png
Normal file
BIN
transformation/schedule/doc/images/example_3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 94 KiB |
BIN
transformation/schedule/doc/images/geraniums-main.png
Normal file
BIN
transformation/schedule/doc/images/geraniums-main.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 44 KiB |
BIN
transformation/schedule/doc/images/geraniums-repot_flowers.png
Normal file
BIN
transformation/schedule/doc/images/geraniums-repot_flowers.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
260
transformation/schedule/doc/schedule.md
Normal file
260
transformation/schedule/doc/schedule.md
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
# Schedule Module
|
||||
|
||||
This module is used to define and execute model transformations using a schedule in the muMLE framework.
|
||||
The development of this module is port of a research project of Robbe Teughels with Joeri Exelmans and Hans Vangheluwe.
|
||||
|
||||
## Module Structure
|
||||
|
||||
The entire module is wrapped in single interface [schedule.py](../rule_scheduler.py) responsible for loading, executing and other optional functionalities, such as generating dot files.
|
||||
Loading modules (.py and .drawio) requires compilation. All these transformations are grouped together in [generator.py](../generator.py).
|
||||
The interactions with the muMLE framework uses the custom interface: [rule_executor.py](../rule_executor.py). This reduces the dependency between the module and the framework.
|
||||
|
||||
Schedules are compiled to python files. These files have a fixed interface defined in [schedule.pyi](../schedule.pyi).
|
||||
This interface includes functionalities that will setup the schedule structure and link patterns or other schedules from the module interface with the nodes.
|
||||
The compiled files do not include any functional implementation to reduce their size and compile time. They are linked to a libary [schedule_lib](../schedule_lib) including an implementation for each node type.
|
||||
This means that nodes can be treated as a black box by the schedule. This architecture allowing easier testing of the library as generation is fully independent of the core implementation.
|
||||
|
||||
The implementation of a given node is similar in the inheritance compared to the original meta-model to increasing traceability between the original instance and the compiled instance.
|
||||
|
||||
## Usage
|
||||
|
||||
### Running Module
|
||||
|
||||
```python
|
||||
|
||||
from state.devstate import DevState
|
||||
from bootstrap.scd import bootstrap_scd
|
||||
from util import loader
|
||||
from transformation.ramify import ramify
|
||||
from api.od import ODAPI
|
||||
from transformation.schedule.rule_scheduler import RuleScheduler
|
||||
|
||||
state = DevState()
|
||||
scd_mmm = bootstrap_scd(state)
|
||||
|
||||
# load model and meta-model
|
||||
metamodel_cs = open('your_metamodel.od', 'r', encoding="utf-8").read()
|
||||
model_cs = open('your_model.od', 'r', encoding="utf-8").read()
|
||||
|
||||
# Parse them
|
||||
metamodel = loader.parse_and_check(state, metamodel_cs, scd_mmm, "your_metamodel")
|
||||
model = loader.parse_and_check(state, model_cs, metamodel, "Example model")
|
||||
|
||||
# Ramified model
|
||||
metamodel_ramified = ramify(state, metamodel)
|
||||
|
||||
# scheduler
|
||||
scheduler = RuleScheduler(state, metamodel, metamodel_ramified)
|
||||
|
||||
# load schedule
|
||||
scheduler.load_schedule("your_schedule.od")
|
||||
# scheduler.load_schedule("your_schedule.py") # compiled version (without conformance checking)
|
||||
# scheduler.load_schedule("your_schedule.drawio") # main page will be executed
|
||||
|
||||
# execute model transformation
|
||||
api = ODAPI(state, model, metamodel)
|
||||
scheduler.run(api)
|
||||
```
|
||||
|
||||
#### Simple example schedules (.od format)
|
||||
|
||||
A schedule is executed from start to end or NullNode (reachable only from unconnected exec-gates).
|
||||
Given the following basic schedule (ARule without NAC), the first match of the pre-condition_pattern is used to rewrite the host graph.
|
||||
This schedule expect at least one match as the `fail' exec-gate of the match is not connected.
|
||||
Zero matches leads to a NullState, resulting in early termination.
|
||||
|
||||
```markdown
|
||||
start:Start
|
||||
end:End
|
||||
|
||||
# match once
|
||||
m:Match{
|
||||
file = "your_pre-condition_pattern.od";
|
||||
n = 1;
|
||||
}
|
||||
|
||||
# rewrite
|
||||
r:Rewrite{
|
||||
file = "your_post-condition_pattern.od";
|
||||
}
|
||||
|
||||
:Conn_exec (start -> m) {from="out"; to="in";}
|
||||
:Conn_exec (m -> r) {from="success"; to="in";}
|
||||
:Conn_exec (r -> end) {from="out"; to="in";}
|
||||
|
||||
:Conn_data (m -> r) {from="out"; to="in";}
|
||||
```
|
||||

|
||||
|
||||
With some small adjustments, all matches can be rewritten (FRule without NAC)
|
||||
|
||||
```markdown
|
||||
start:Start
|
||||
end:End
|
||||
|
||||
# match all
|
||||
m:Match{
|
||||
file = "your_pre-condition_pattern.od";
|
||||
# n = +INF (if missing: all matches)
|
||||
}
|
||||
|
||||
l:Loop
|
||||
|
||||
# rewrite
|
||||
r:Rewrite{
|
||||
file = "your_post-condition_pattern.od";
|
||||
}
|
||||
|
||||
:Conn_exec (start -> m) {from="out"; to="in";}
|
||||
:Conn_exec (m -> l) {from="success"; to="in";}
|
||||
:Conn_exec (l -> r) {from="it"; to="in";}
|
||||
:Conn_exec (r -> l) {from="out"; to="in";}
|
||||
:Conn_exec (l -> end) {from="out"; to="in";}
|
||||
|
||||
:Conn_data (m -> l) {from="out"; to="in";}
|
||||
:Conn_data (l -> r) {from="out"; to="in";}
|
||||
```
|
||||

|
||||
|
||||
Adding a NAC to this example: adding a match using the previous match and expecting it to fail. (FRule with NAC)
|
||||
|
||||
```markdown
|
||||
start:Start
|
||||
end:End
|
||||
|
||||
# match all
|
||||
m:Match{
|
||||
file = "your_pre-condition_pattern.od";
|
||||
# n = +INF (if missing: all matches)
|
||||
}
|
||||
|
||||
l:Loop
|
||||
|
||||
# NAC
|
||||
n:Match{
|
||||
file = "your_NAC_pre-condition_pattern.od";
|
||||
n = 1; # one fail is enough
|
||||
}
|
||||
|
||||
# rewrite
|
||||
r:Rewrite{
|
||||
file = "your_post-condition_pattern.od";
|
||||
}
|
||||
|
||||
:Conn_exec (start -> m) {from="out"; to="in";}
|
||||
:Conn_exec (m -> l) {from="success"; to="in";}
|
||||
:Conn_exec (l -> n) {from="it"; to="in";}
|
||||
:Conn_exec (n -> r) {from="fail"; to="in";}
|
||||
:Conn_exec (r -> l) {from="out"; to="in";}
|
||||
:Conn_exec (l -> end) {from="out"; to="in";}
|
||||
|
||||
:Conn_data (m -> l) {from="out"; to="in";}
|
||||
:Conn_data (l -> n) {from="out"; to="in";}
|
||||
:Conn_data (l -> r) {from="out"; to="in";}
|
||||
```
|
||||

|
||||
|
||||
## Node Types
|
||||
|
||||
### Start
|
||||
This node indicates the start of a schedule.
|
||||
It signature (additional ports) can be used to insert match sets or alternative exec-paths, increasing reusability.
|
||||
|
||||
[Start](schedule_lib/start.md)
|
||||
|
||||
### End
|
||||
Counterpart to Start node. Reaching this node result in successful termination of the schedule.
|
||||
It signature (additional ports) can be used to extract match sets or alternative exec-paths, increasing reusability.
|
||||
|
||||
[End](schedule_lib/end.md)
|
||||
|
||||
### Match
|
||||
Matches a pre-condition pattern on the host-graph. A primitive defined in T-Core
|
||||
|
||||
[Match](schedule_lib/match.md)
|
||||
|
||||
### Rewrite
|
||||
Rewrite the host-graph using a post-condition pattern. A primitive defined in T-Core
|
||||
|
||||
[Rewrite](schedule_lib/rewrite.md)
|
||||
|
||||
### Modify
|
||||
Modifies the match set. This allows patterns to name elements to their linking.
|
||||
This node modifies or deletes elements to be usable as pivot in another pattern with different names.
|
||||
An example usage can be found in [examples/geraniums](../../../examples/geraniums).
|
||||
|
||||
In the following schedule, a cracked filed was matched and no longer needed.
|
||||
The Modify node deletes this, allowing for the flowering flower match node to use a pattern without this element, reducing the size and making it more general.
|
||||

|
||||
|
||||
[Modify](schedule_lib/modify.md)
|
||||
|
||||
### Merge
|
||||
Combines multiple matches.
|
||||
Allowing patterns to be split into different parts or reuse a specific part with another match without recalculating.
|
||||
An example usage can be found in [examples/geraniums](../../../examples/geraniums).
|
||||
|
||||
In the following sub-schedule, a new pot and the flower with old pot and their connection, is combined to move the flower in a rewrite.
|
||||
Replanting multiple flowers into one new pot would require markers, making the matching harder in order to combine these elements without the use of this node.
|
||||
|
||||

|
||||
|
||||
[Merge](schedule_lib/merge.md)
|
||||
|
||||
### Store
|
||||
Combines matches (set) into a new match set.
|
||||
Use the exec port to insert the data on the associated data-port to the set.
|
||||
|
||||
The direct usage of this node is limited but invaluable for libraries.
|
||||
An example usage is petrinet-execution with user interface.
|
||||
This requires a list of all transitions that can fire.
|
||||
Matching "all transitions" followed by a loop to check the NAC leaves single matches.
|
||||
This nodes allows these matches to be recombined into a set that can be used to choose a transition from.
|
||||
|
||||
[Store](schedule_lib/store.md)
|
||||
|
||||
### Loop
|
||||
Iterate over a given match set.
|
||||
Nodes such as Match or Rewrite uses a single match as a pivot.
|
||||
Executing these nodes over all the element is possible with this node.
|
||||
See the examples in [Modify](#Modify) or [Merge](#Merge) for an example view.
|
||||
|
||||
[Loop](schedule_lib/loop.md)
|
||||
|
||||
### Print
|
||||
Print the input data. This is mainly used as a debugging/testing tool to validate intermediate information or state.
|
||||
|
||||
[Print](schedule_lib/print.md)
|
||||
|
||||
### Action
|
||||
This node allows for code to be injected into the schedule.
|
||||
This node can be used for general purpuse and even recreate all other nodes (except start and end).
|
||||
Not all functionalities can be described using the current nodes. For petrinets, an example can be to generate a visual overview of the petrinet-system.
|
||||
|
||||
[Action.md](schedule_lib/action.md)
|
||||
|
||||
## Edge Types
|
||||
Nodes can be connected using two different edges. The execution-edges define the execution flow of the schedule.
|
||||
These connections can only connect nodes that inherit form [ExecNode](schedule_lib/exec_node.md).
|
||||
Connecting nodes between execution-gates defined by the nodes, happens in a system of "one to many" for gates.
|
||||
The data-edges allows information to be distributed to other [DataNode](schedule_lib/data_node.md).
|
||||
This happens in the opposite way of "many to one" on data-gates.
|
||||
Data changes on a gate wil notify all connected nodes of the changes, allowing propagation through the system. Note: the data received is immutable to ensure consistent and reliable execution of the schedule.
|
||||
|
||||
|
||||
## file formats
|
||||
|
||||
### .od
|
||||
This is the original textual file format used by the framework. The main advantage of this format is the integration with the framework that allows conformance checking of the scheduling language.
|
||||
Therefore, all other formats are converted to this type for conformance checking before being compiled.
|
||||
|
||||
### .py
|
||||
All schedules are compiled to python after conformance checking. Allowing this format provides the benefit to load schedules without expensive compilation or conformance checking, reducing computational cost.
|
||||
This format is recommended in the deployment of applications where the schedule will not change.
|
||||
It is not advisable to implement schedules directly in this format as conformance checking guarantees proper working of the schedule module.
|
||||
|
||||
### .drawio
|
||||
A visual format for the drawio application.
|
||||
The library includes a drawio [library](../schedule_lib/Schedule_lib.xml) that includes a representation with additional fields for easy integration with the application.
|
||||
The main advantage of this format is the usage of pages that allows sub-schedules be easily created and organised within one schedule. (layers are not allowed)
|
||||
|
||||
1
transformation/schedule/doc/schedule_lib/action.md
Normal file
1
transformation/schedule/doc/schedule_lib/action.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
# Under construction
|
||||
1
transformation/schedule/doc/schedule_lib/data_node.md
Normal file
1
transformation/schedule/doc/schedule_lib/data_node.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
# Under construction
|
||||
1
transformation/schedule/doc/schedule_lib/end.md
Normal file
1
transformation/schedule/doc/schedule_lib/end.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
# Under construction
|
||||
1
transformation/schedule/doc/schedule_lib/exec_node.md
Normal file
1
transformation/schedule/doc/schedule_lib/exec_node.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
# Under construction
|
||||
1
transformation/schedule/doc/schedule_lib/loop.md
Normal file
1
transformation/schedule/doc/schedule_lib/loop.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
# Under construction
|
||||
1
transformation/schedule/doc/schedule_lib/match.md
Normal file
1
transformation/schedule/doc/schedule_lib/match.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
# Under construction
|
||||
1
transformation/schedule/doc/schedule_lib/merge.md
Normal file
1
transformation/schedule/doc/schedule_lib/merge.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
# Under construction
|
||||
1
transformation/schedule/doc/schedule_lib/modify.md
Normal file
1
transformation/schedule/doc/schedule_lib/modify.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
# Under construction
|
||||
41
transformation/schedule/doc/schedule_lib/node.md
Normal file
41
transformation/schedule/doc/schedule_lib/node.md
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
## Node Module
|
||||
|
||||
Defines the abstract base Node class for graph-based structures. Each Node is assigned
|
||||
a unique identifier via an external IdGenerator. The class provides an interface for
|
||||
managing execution state and generating DOT graph representations using Jinja2 templates.
|
||||
|
||||
### Class: `Node`
|
||||
|
||||
- **Attributes**
|
||||
- `id: int`: A unique identifier assigned to each instance upon initialization.
|
||||
|
||||
- **Methods**
|
||||
- `get_id`
|
||||
- returns: `int`, The unique node ID
|
||||
|
||||
Retrieves the unique identifier of the node.
|
||||
|
||||
- `generate_stack_frame`
|
||||
- exec_id: `int`, The ID of the execution context.
|
||||
- returns: `None`
|
||||
|
||||
Initializes a new state frame for a specific execution context.
|
||||
Designed to be overridden in subclasses that use execution state.
|
||||
|
||||
- `delete_stack_frame`
|
||||
- exec_id: `int`, The ID of the execution context.
|
||||
- returns: `None`
|
||||
|
||||
Deletes the state frame for a specific execution context.
|
||||
Designed to be overridden in subclasses that use execution state.
|
||||
|
||||
- `generate_dot`
|
||||
- nodes: `list[str]`, A list to append DOT node definitions to.
|
||||
- edges: `list[str]`, A list to append DOT edges definitions to.
|
||||
- visited: `set[str]`, A set of already visited node IDs to avoid duplicates or recursion.
|
||||
- template: `list[str]`, A Jinja2 template used to format the node's DOT representation.
|
||||
- returns: `None`
|
||||
|
||||
Generates the DOT graph representation for this node and its relationships.
|
||||
Must be implemented in subclasses.
|
||||
|
||||
1
transformation/schedule/doc/schedule_lib/print.md
Normal file
1
transformation/schedule/doc/schedule_lib/print.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
# Under construction
|
||||
1
transformation/schedule/doc/schedule_lib/rewrite.md
Normal file
1
transformation/schedule/doc/schedule_lib/rewrite.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
# Under construction
|
||||
1
transformation/schedule/doc/schedule_lib/rule.md
Normal file
1
transformation/schedule/doc/schedule_lib/rule.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
# Under construction
|
||||
1
transformation/schedule/doc/schedule_lib/schedule.md
Normal file
1
transformation/schedule/doc/schedule_lib/schedule.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
# Under construction
|
||||
1
transformation/schedule/doc/schedule_lib/start.md
Normal file
1
transformation/schedule/doc/schedule_lib/start.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
# Under construction
|
||||
1
transformation/schedule/doc/schedule_lib/store.md
Normal file
1
transformation/schedule/doc/schedule_lib/store.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
# Under construction
|
||||
197
transformation/schedule/generator.py
Normal file
197
transformation/schedule/generator.py
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
import sys
|
||||
import os
|
||||
from uuid import UUID
|
||||
|
||||
from black.trans import Callable
|
||||
from jinja2.runtime import Macro
|
||||
|
||||
from api.od import ODAPI
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
|
||||
|
||||
class schedule_generator:
|
||||
def __init__(self, odApi: ODAPI):
|
||||
self.env = Environment(
|
||||
loader=FileSystemLoader(
|
||||
os.path.join(os.path.dirname(__file__), "templates")
|
||||
)
|
||||
)
|
||||
self.env.trim_blocks = True
|
||||
self.env.lstrip_blocks = True
|
||||
self.template = self.env.get_template("schedule_template.j2")
|
||||
self.template_wrap = self.env.get_template("schedule_template_wrap.j2")
|
||||
self.api = odApi
|
||||
|
||||
|
||||
def _get_slot_value_default(item: UUID, slot: str, default):
|
||||
if slot in self.api.get_slots(item):
|
||||
return self.api.get_slot_value(item, slot)
|
||||
return default
|
||||
|
||||
conn_data_event = {
|
||||
"Match": lambda item: False,
|
||||
"Rewrite": lambda item: False,
|
||||
"Modify": lambda item: True,
|
||||
"Merge": lambda item: True,
|
||||
"Loop": lambda item: True,
|
||||
"Action": lambda item: _get_slot_value_default(item, "event", False),
|
||||
"Print": lambda item: _get_slot_value_default(item, "event", False),
|
||||
"Store": lambda item: False,
|
||||
"Schedule": lambda item: False,
|
||||
"End": lambda item: False,
|
||||
}
|
||||
|
||||
arg_map = {
|
||||
"Loop": (name_dict := lambda item: {"name": self.api.get_name(item)}),
|
||||
"Start": lambda item: {
|
||||
**name_dict(item),
|
||||
"ports_exec_out": eval(
|
||||
self.api.get_slot_value_default(item, "ports_exec_out", "['out']")
|
||||
),
|
||||
"ports_data_out": eval(
|
||||
self.api.get_slot_value_default(item, "ports_data_out", "[]")
|
||||
),
|
||||
},
|
||||
"End": lambda item: {
|
||||
**name_dict(item),
|
||||
"ports_exec_in": eval(
|
||||
self.api.get_slot_value_default(item, "ports_exec_in", "['in']")
|
||||
),
|
||||
"ports_data_in": eval(
|
||||
self.api.get_slot_value_default(item, "ports_data_in", "[]")
|
||||
),
|
||||
},
|
||||
"Rewrite": (
|
||||
file_dict := lambda item: {
|
||||
**name_dict(item),
|
||||
"file": self.api.get_slot_value(item, "file"),
|
||||
}
|
||||
),
|
||||
"Match": lambda item: {
|
||||
**file_dict(item),
|
||||
"n": self.api.get_slot_value_default(item, "n", 'float("inf")'),
|
||||
},
|
||||
"Action": lambda item: {
|
||||
**name_dict(item),
|
||||
"ports_exec_in": self.api.get_slot_value_default(item, "ports_exec_in", ["in"]),
|
||||
"ports_exec_out": self.api.get_slot_value_default(item, "ports_exec_out", ["out"]),
|
||||
"ports_data_in": self.api.get_slot_value_default(item, "ports_data_in", []),
|
||||
"ports_data_out": self.api.get_slot_value_default(item, "ports_data_out", []),
|
||||
"action": repr(self.api.get_slot_value(item, "action")),
|
||||
"init": repr(
|
||||
self.api.get_slot_value_default(item, "init", "")
|
||||
),
|
||||
},
|
||||
"Modify": lambda item: {
|
||||
**name_dict(item),
|
||||
"rename": eval(self.api.get_slot_value_default(item, "rename", "{}")),
|
||||
"delete": eval(self.api.get_slot_value_default(item, "delete", "{}")),
|
||||
},
|
||||
"Merge": lambda item: {
|
||||
**name_dict(item),
|
||||
"ports_data_in": eval(
|
||||
self.api.get_slot_value_default(item, "ports_data_in", "[]")
|
||||
),
|
||||
},
|
||||
"Store": lambda item: {
|
||||
**name_dict(item),
|
||||
"ports": eval(self.api.get_slot_value_default(item, "ports", "[]")),
|
||||
},
|
||||
"Schedule": file_dict,
|
||||
"Print": lambda item: {
|
||||
**name_dict(item),
|
||||
"label": self.api.get_slot_value_default(item, "label", ""),
|
||||
"custom": self.api.get_slot_value_default(item, "custom", ""),
|
||||
},
|
||||
"Conn_exec": (
|
||||
conn_dict := lambda item: {
|
||||
"name_from": self.api.get_name(self.api.get_source(item)),
|
||||
"name_to": self.api.get_name(self.api.get_target(item)),
|
||||
"from": self.api.get_slot_value_default(item, "from", 0),
|
||||
"to": self.api.get_slot_value_default(item, "to", 0),
|
||||
}
|
||||
),
|
||||
"Conn_data": lambda item: {
|
||||
**conn_dict(item),
|
||||
"event": conn_data_event[
|
||||
self.api.get_type_name(target := self.api.get_target(item))
|
||||
](target),
|
||||
},
|
||||
}
|
||||
self.macro_args = {
|
||||
tp: (macro, arg_map.get(tp))
|
||||
for tp, macro in self.template.module.__dict__.items()
|
||||
if type(macro) == Macro
|
||||
}
|
||||
|
||||
def _render(self, item):
|
||||
type_name = self.api.get_type_name(item)
|
||||
macro, arg_gen = self.macro_args[type_name]
|
||||
return macro(**arg_gen(item))
|
||||
|
||||
def _dfs(
|
||||
self, stack: list[UUID], get_links: Callable, get_next_node: Callable
|
||||
) -> tuple[set[UUID], list[UUID]]:
|
||||
visited = set()
|
||||
connections = list()
|
||||
while len(stack) > 0:
|
||||
obj = stack.pop()
|
||||
if obj in visited:
|
||||
continue
|
||||
visited.add(obj)
|
||||
for conn in get_links(self.api, obj):
|
||||
connections.append(conn)
|
||||
stack.append(get_next_node(self.api, conn))
|
||||
return visited, connections
|
||||
|
||||
def generate_schedule(self, stream=sys.stdout):
|
||||
start = self.api.get_all_instances("Start")[0][1]
|
||||
end = self.api.get_all_instances("End")[0][1]
|
||||
out = {
|
||||
"blocks": [],
|
||||
"blocks_name": [],
|
||||
"blocks_start_end": [],
|
||||
"exec_conn": [],
|
||||
"data_conn": [],
|
||||
"match_files": set(),
|
||||
"matchers": [],
|
||||
"start": self.api.get_name(start),
|
||||
"end": self.api.get_name(end),
|
||||
}
|
||||
|
||||
stack = [start, end]
|
||||
exec_blocks, conn_exec = self._dfs(
|
||||
stack,
|
||||
lambda api, node: api.get_outgoing(node, "Conn_exec"),
|
||||
lambda api, conn: api.get_target(conn),
|
||||
)
|
||||
|
||||
for name, p in self.api.get_all_instances("Print"):
|
||||
if self.api.has_slot(p, "event") and self.api.get_slot_value(p, "event"):
|
||||
exec_blocks.add(p)
|
||||
|
||||
stack = list(exec_blocks)
|
||||
blocks, conn_data = self._dfs(
|
||||
stack,
|
||||
lambda api, node: api.get_incoming(node, "Conn_data"),
|
||||
lambda api, conn: api.get_source(conn),
|
||||
)
|
||||
|
||||
for exec_c in conn_exec:
|
||||
out["exec_conn"].append(self._render(exec_c))
|
||||
|
||||
for data_c in conn_data:
|
||||
out["data_conn"].append(self._render(data_c))
|
||||
|
||||
for block in blocks:
|
||||
out["blocks_name"].append(self.api.get_name(block))
|
||||
if block in [start, end]:
|
||||
out["blocks_start_end"].append(self._render(block))
|
||||
continue
|
||||
out["blocks"].append(self._render(block))
|
||||
if self.api.is_instance(block, "Rule"):
|
||||
d = self.macro_args[self.api.get_type_name(block)][1](block)
|
||||
out["match_files"].add(d["file"])
|
||||
out["matchers"].append(d)
|
||||
|
||||
print(self.template_wrap.render(out), file=stream)
|
||||
151
transformation/schedule/models/eval_context.py
Normal file
151
transformation/schedule/models/eval_context.py
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
from typing import TYPE_CHECKING, get_origin, get_args
|
||||
from types import UnionType
|
||||
from uuid import UUID
|
||||
|
||||
from jinja2 import Template
|
||||
|
||||
from framework.conformance import eval_context_decorator
|
||||
from services.primitives.string_type import String
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from api.od_stub_readonly import get_outgoing, get_incoming, get_slot_value, get_value, get_target, has_slot
|
||||
from eval_context_stub import *
|
||||
|
||||
|
||||
@eval_context_decorator
|
||||
def _check_all_connections(this, labels: list[list[str] | str]) -> list[str]:
|
||||
err = []
|
||||
check_incoming_exec(this, err, labels[0])
|
||||
check_outgoing_exec(this, err, labels[1])
|
||||
check_incoming_data(this, err, labels[2])
|
||||
check_outgoing_data(this, err, labels[3])
|
||||
return err
|
||||
|
||||
@eval_context_decorator
|
||||
def _check_outgoing_exec(this, err: list[str], labels: list[str]) -> None:
|
||||
l = set(labels)
|
||||
gates = set()
|
||||
for y in get_outgoing(this, "Conn_exec"):
|
||||
if (x := get_slot_value(y, "from")) not in l:
|
||||
err.append(f"output exec gate '{x}' does not exist. Gates: {', '.join(labels)}.")
|
||||
if x in gates:
|
||||
err.append(f"output exec gate '{x}' is connected to multiple gates.")
|
||||
gates.add(x)
|
||||
|
||||
|
||||
@eval_context_decorator
|
||||
def _check_incoming_exec(this, err: list[str], labels: list[str]) -> None:
|
||||
l = set(labels)
|
||||
for y in get_incoming(this, "Conn_exec"):
|
||||
if (x := get_slot_value(y, "to")) not in l:
|
||||
err.append(f"input exec gate gate '{x}' does not exist. Gates: {', '.join(labels)}.")
|
||||
|
||||
|
||||
@eval_context_decorator
|
||||
def _check_outgoing_data(this, err: list[str], labels: list[str]) -> None:
|
||||
l = set(labels)
|
||||
for y in get_outgoing(this, "Conn_data"):
|
||||
if (x := get_slot_value(y, "from")) not in l:
|
||||
err.append(f"output data gate '{x}' does not exist. Gates: {', '.join(labels)}.")
|
||||
|
||||
|
||||
@eval_context_decorator
|
||||
def _check_incoming_data(this, err: list[str], labels: list[str]) -> None:
|
||||
l = set(labels)
|
||||
gates = set()
|
||||
for y in get_incoming(this, "Conn_data"):
|
||||
if (x := get_slot_value(y, "to")) not in l:
|
||||
err.append(f"input data gate '{x}' does not exist. Gates: {', '.join(labels)}.")
|
||||
if x in gates:
|
||||
err.append(f"input data gate '{x}' is connected to multiple gates.")
|
||||
gates.add(x)
|
||||
|
||||
def check_type(x: any, typ2: any) -> bool:
|
||||
origin = get_origin(typ2)
|
||||
if origin is None:
|
||||
return isinstance(x, typ2)
|
||||
args = get_args(typ2)
|
||||
if origin is UnionType:
|
||||
for tp in args:
|
||||
if check_type(x, tp):
|
||||
return True
|
||||
return False
|
||||
if not isinstance(x, origin):
|
||||
return False
|
||||
if origin in [list, set]:
|
||||
for value in x:
|
||||
if not check_type(value, args[0]):
|
||||
return False
|
||||
elif origin is tuple:
|
||||
if len(args) != len(x):
|
||||
return False
|
||||
for i, value in enumerate(x):
|
||||
if not check_type(value, args[i]):
|
||||
return False
|
||||
elif origin is dict:
|
||||
for key, value in x.items():
|
||||
if not (check_type(key, args[0]) and check_type(value, args[1])):
|
||||
return False
|
||||
return True
|
||||
|
||||
@eval_context_decorator
|
||||
def _check_slot_code_type(this: UUID, slot: str, typ: type, unique = False, *, mandatory: bool = False, blacklist: list[str] | None = None) -> list[str]:
|
||||
err = []
|
||||
if not (has_slot(this, slot)):
|
||||
if mandatory:
|
||||
err.append(f"Missing mandatory slot: '{slot}'.")
|
||||
return err
|
||||
try:
|
||||
try:
|
||||
x = eval(get_slot_value(this, slot))
|
||||
except Exception as _:
|
||||
err.append(f"Invalid python code for {slot}: {get_slot_value(this, slot)}")
|
||||
return err
|
||||
|
||||
if not check_type(x, typ):
|
||||
try:
|
||||
typ_rep = typ.__name__
|
||||
except AttributeError:
|
||||
typ_rep = str(typ)
|
||||
err.append(f"Unexpected type for {slot}: {type(x).__name__}, expected type: {typ_rep}")
|
||||
return err
|
||||
|
||||
if unique and len(set(x)) != len(x):
|
||||
err.append(f"elements must be unique")
|
||||
return err
|
||||
except Exception as e:
|
||||
err.append(f"Unexpected error: {e}")
|
||||
return err
|
||||
|
||||
|
||||
@eval_context_decorator
|
||||
def _check_jinja2_code(this: UUID, slot: str) -> list[str]:
|
||||
if len(err:= check_slot_code_type(this, slot, str, mandatory=True)) != 0:
|
||||
return err
|
||||
s = eval(get_slot_value(this, slot))
|
||||
try:
|
||||
template = Template(s)
|
||||
template.render(**{"data":[{}]})
|
||||
return []
|
||||
except Exception as e:
|
||||
return [f"Invalid Jinja2 syntax for '{slot}':\n{e}\n{s}"]
|
||||
|
||||
|
||||
@eval_context_decorator
|
||||
def _check_code_syntax(code) -> list[str]:
|
||||
try:
|
||||
compile(code, "<string>", "exec")
|
||||
return []
|
||||
except SyntaxError as e:
|
||||
return [f"Invalid python code for: `{code}` :\n{e}"]
|
||||
|
||||
mm_eval_context = {
|
||||
"check_all_connections": _check_all_connections,
|
||||
"check_outgoing_exec": _check_outgoing_exec,
|
||||
"check_incoming_exec": _check_incoming_exec,
|
||||
"check_outgoing_data": _check_outgoing_data,
|
||||
"check_incoming_data": _check_incoming_data,
|
||||
"check_slot_code_type": _check_slot_code_type,
|
||||
"check_code_syntax": _check_code_syntax,
|
||||
"check_jinja2_code": _check_jinja2_code,
|
||||
}
|
||||
6
transformation/schedule/models/eval_context_stub.pyi
Normal file
6
transformation/schedule/models/eval_context_stub.pyi
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
def check_outgoing_exec(this, err: list[str], labels: list[str]) -> bool: ...
|
||||
def check_incoming_exec(this, err: list[str], labels: list[str]) -> bool: ...
|
||||
def check_outgoing_data(this, err: list[str], labels: list[str]) -> bool: ...
|
||||
def check_incoming_data(this, err: list[str], labels: list[str]) -> bool: ...
|
||||
def check_is_type(s: str, typ: any) -> bool: ...
|
||||
def check_code_syntax(code) -> bool: ...
|
||||
194
transformation/schedule/models/scheduling_MM.od
Normal file
194
transformation/schedule/models/scheduling_MM.od
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
abstract class Exec
|
||||
|
||||
association Conn_exec [0..*] Exec -> Exec [0..*] {
|
||||
String from;
|
||||
String to;
|
||||
}
|
||||
|
||||
abstract class Data
|
||||
association Conn_data [0..*] Data -> Data [0..*] {
|
||||
String from;
|
||||
String to;
|
||||
}
|
||||
|
||||
class Start [1..1] (Exec, Data) {
|
||||
optional ActionCode ports_exec_out;
|
||||
optional ActionCode ports_data_out;
|
||||
```
|
||||
err = check_slot_code_type(this, "ports_exec_out", list[str] | set[str], True)
|
||||
err.extend(check_slot_code_type(this, "ports_data_out", list[str] | set[str], True))
|
||||
if len(err) == 0:
|
||||
err = check_all_connections(this, [
|
||||
[],
|
||||
eval(get_slot_value_default(this, "ports_exec_out", "['out']")),
|
||||
[],
|
||||
eval(get_slot_value_default(this, "ports_data_out", "[]"))
|
||||
])
|
||||
err
|
||||
```;
|
||||
}
|
||||
class End [1..1] (Exec, Data) {
|
||||
optional ActionCode ports_exec_in;
|
||||
optional ActionCode ports_data_in;
|
||||
```
|
||||
err = check_slot_code_type(this, "ports_exec_in", list[str] | set[str], True)
|
||||
err.extend(check_slot_code_type(this, "ports_data_in", list[str] | set[str], True))
|
||||
if len(err) == 0:
|
||||
err = check_all_connections(this, [
|
||||
eval(get_slot_value_default(this, "ports_exec_in", "['in']")),
|
||||
[],
|
||||
eval(get_slot_value_default(this, "ports_data_in", "[]")),
|
||||
[]
|
||||
])
|
||||
err
|
||||
```;
|
||||
}
|
||||
|
||||
abstract class Rule (Exec, Data)
|
||||
{
|
||||
String file;
|
||||
}
|
||||
|
||||
class Match (Rule)
|
||||
{
|
||||
optional Integer n;
|
||||
```
|
||||
check_all_connections(this, [
|
||||
["in"],
|
||||
["success", "fail"],
|
||||
["in"],
|
||||
["out"]
|
||||
])
|
||||
```;
|
||||
}
|
||||
|
||||
class Rewrite (Rule)
|
||||
{
|
||||
```
|
||||
check_all_connections(this, [
|
||||
["in"],
|
||||
["out"],
|
||||
["in"],
|
||||
["out"]
|
||||
])
|
||||
```;
|
||||
}
|
||||
|
||||
class Action (Exec, Data)
|
||||
{
|
||||
optional ActionCode ports_exec_in;
|
||||
optional ActionCode ports_exec_out;
|
||||
optional ActionCode ports_data_in;
|
||||
optional ActionCode ports_data_out;
|
||||
optional ActionCode init `check_code_syntax(get_value(get_target(this)))`;
|
||||
ActionCode action `check_code_syntax(get_value(get_target(this)))`;
|
||||
```
|
||||
err = check_slot_code_type(this, "ports_exec_in", list[str] | set[str], True)
|
||||
err.extend(check_slot_code_type(this, "ports_exec_out", list[str] | set[str], True))
|
||||
err.extend(check_slot_code_type(this, "ports_data_in", list[str] | set[str], True))
|
||||
err.extend(check_slot_code_type(this, "ports_data_out", list[str] | set[str], True))
|
||||
if len(err) == 0:
|
||||
err = check_all_connections(this, [
|
||||
eval(get_slot_value_default(this, "ports_exec_in", "['in']")),
|
||||
eval(get_slot_value_default(this, "ports_exec_out", "['out']")),
|
||||
eval(get_slot_value_default(this, "ports_data_in", "[]")),
|
||||
eval(get_slot_value_default(this, "ports_data_out", "[]"))
|
||||
])
|
||||
err
|
||||
```;
|
||||
|
||||
}
|
||||
|
||||
class Modify (Data)
|
||||
{
|
||||
optional ActionCode rename;
|
||||
optional ActionCode delete;
|
||||
```
|
||||
err = check_slot_code_type(this, "rename", dict[str,str])
|
||||
err.extend(check_slot_code_type(this, "delete", list[str] | set[str]))
|
||||
if len(err) == 0:
|
||||
if not (eval(get_slot_value_default(this, "rename", "dict()")).keys().isdisjoint(
|
||||
eval(get_slot_value_default(this, "delete", "set()")))
|
||||
):
|
||||
err.append("rename and delete should be disjoint.")
|
||||
err.extend(check_all_connections(this, [
|
||||
[],
|
||||
[],
|
||||
["in"],
|
||||
["out"]
|
||||
]))
|
||||
err
|
||||
```;
|
||||
}
|
||||
|
||||
class Merge (Data)
|
||||
{
|
||||
ActionCode ports_data_in;
|
||||
```
|
||||
err = check_slot_code_type(this, "ports_data_in", list[str] | set[str], True, mandatory = True)
|
||||
if len(err) == 0:
|
||||
err = check_all_connections(this, [
|
||||
[],
|
||||
[],
|
||||
eval(get_slot_value(this, "ports_data_in")),
|
||||
["out"]
|
||||
])
|
||||
err
|
||||
```;
|
||||
}
|
||||
|
||||
class Store (Exec, Data)
|
||||
{
|
||||
ActionCode ports;
|
||||
```
|
||||
err = check_slot_code_type(this, "ports", list[str] | set[str], True, mandatory = True, blacklist = ["in", "out"])
|
||||
if len(err) == 0:
|
||||
err = check_all_connections(this, [
|
||||
[*(ports:= eval(get_slot_value(this, "ports"))), "in"],
|
||||
[*ports, "out"],
|
||||
ports,
|
||||
["out"]
|
||||
])
|
||||
err
|
||||
```;
|
||||
}
|
||||
|
||||
class Schedule (Exec, Data)
|
||||
{
|
||||
String file;
|
||||
```
|
||||
check_all_connections(this, [
|
||||
{get_slot_value(conn, "to") for conn in get_incoming(this, "Conn_exec")},
|
||||
{get_slot_value(conn, "from") for conn in get_outgoing(this, "Conn_exec")},
|
||||
{get_slot_value(conn, "to") for conn in get_incoming(this, "Conn_data")},
|
||||
{get_slot_value(conn, "from") for conn in get_outgoing(this, "Conn_data")}
|
||||
])
|
||||
```;
|
||||
}
|
||||
|
||||
class Loop(Exec, Data)
|
||||
{
|
||||
```
|
||||
check_all_connections(this, [
|
||||
["in"],
|
||||
["it", "out"],
|
||||
["in"],
|
||||
["out"]
|
||||
])
|
||||
```;
|
||||
}
|
||||
|
||||
class Print(Exec, Data)
|
||||
{
|
||||
optional Boolean event;
|
||||
optional String label;
|
||||
optional ActionCode custom `check_jinja2_code(get_source(this), "custom")`;
|
||||
```
|
||||
check_all_connections(this, [
|
||||
["in"],
|
||||
["out"],
|
||||
["in"],
|
||||
[]
|
||||
])
|
||||
```;
|
||||
}
|
||||
46
transformation/schedule/rule_executor.py
Normal file
46
transformation/schedule/rule_executor.py
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
from typing import Any
|
||||
from uuid import UUID
|
||||
|
||||
from api.od import ODAPI
|
||||
from transformation.matcher import match_od
|
||||
from transformation.rewriter import rewrite
|
||||
from util.loader import parse_and_check
|
||||
|
||||
|
||||
class RuleExecutor:
|
||||
def __init__(self, state, mm: UUID, mm_ramified: UUID, eval_context={}):
|
||||
self.state = state
|
||||
self.mm = mm
|
||||
self.mm_ramified = mm_ramified
|
||||
self.eval_context = eval_context
|
||||
|
||||
# Generates matches.
|
||||
# Every match is a dictionary with entries LHS_element_name -> model_element_name
|
||||
def match_rule(self, m: UUID, lhs: UUID, *, pivot: dict[Any, Any]):
|
||||
lhs_matcher = match_od(
|
||||
self.state,
|
||||
host_m=m,
|
||||
host_mm=self.mm,
|
||||
pattern_m=lhs,
|
||||
pattern_mm=self.mm_ramified,
|
||||
eval_context=self.eval_context,
|
||||
pivot=pivot,
|
||||
)
|
||||
return lhs_matcher
|
||||
|
||||
def rewrite_rule(self, od: ODAPI, rhs: UUID, *, pivot: dict[Any, Any]):
|
||||
rhs = rewrite(
|
||||
self.state,
|
||||
rhs_m=rhs,
|
||||
pattern_mm=self.mm_ramified,
|
||||
lhs_match=pivot,
|
||||
host_m=od.m,
|
||||
host_mm=od.mm,
|
||||
eval_context=self.eval_context,
|
||||
)
|
||||
od.recompute_mappings()
|
||||
yield rhs
|
||||
|
||||
def load_match(self, file: str):
|
||||
with open(file, "r") as f:
|
||||
return parse_and_check(self.state, f.read(), self.mm_ramified, file)
|
||||
338
transformation/schedule/rule_scheduler.py
Normal file
338
transformation/schedule/rule_scheduler.py
Normal file
|
|
@ -0,0 +1,338 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import importlib.util
|
||||
import io
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
from pathlib import Path
|
||||
from time import time
|
||||
from typing import cast, TYPE_CHECKING
|
||||
|
||||
from jinja2 import FileSystemLoader, Environment
|
||||
|
||||
from concrete_syntax.textual_od import parser as parser_od
|
||||
from concrete_syntax.textual_cd import parser as parser_cd
|
||||
from api.od import ODAPI
|
||||
from bootstrap.scd import bootstrap_scd
|
||||
from transformation.schedule.rule_executor import RuleExecutor
|
||||
from transformation.schedule.generator import schedule_generator
|
||||
from transformation.schedule.models.eval_context import mm_eval_context
|
||||
from transformation.schedule.schedule_lib import ExecNode, Start
|
||||
from framework.conformance import Conformance, render_conformance_check_result, eval_context_decorator
|
||||
from state.devstate import DevState
|
||||
from examples.petrinet.renderer import render_petri_net_to_dot
|
||||
|
||||
from drawio2py import parser
|
||||
from drawio2py.abstract_syntax import DrawIOFile, Edge, Vertex, Cell
|
||||
from icecream import ic
|
||||
|
||||
from transformation.schedule.schedule_lib.funcs import IdGenerator
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from transformation.schedule.schedule import Schedule
|
||||
|
||||
|
||||
class RuleScheduler:
|
||||
__slots__ = (
|
||||
"rule_executor",
|
||||
"schedule_main",
|
||||
"loaded",
|
||||
"out",
|
||||
"verbose",
|
||||
"conformance",
|
||||
"directory",
|
||||
"eval_context",
|
||||
"_state",
|
||||
"_mmm_cs",
|
||||
"sub_schedules",
|
||||
"end_time",
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
state,
|
||||
mm_rt,
|
||||
mm_rt_ramified,
|
||||
*,
|
||||
outstream=sys.stdout,
|
||||
verbose: bool = False,
|
||||
conformance: bool = True,
|
||||
directory: str = "",
|
||||
eval_context: dict[str, any] = None,
|
||||
):
|
||||
self.rule_executor: RuleExecutor = RuleExecutor(state, mm_rt, mm_rt_ramified)
|
||||
self.schedule_main: Schedule | None = None
|
||||
self.out = outstream
|
||||
self.verbose: bool = verbose
|
||||
self.conformance: bool = conformance
|
||||
self.directory: Path = Path.cwd() / directory
|
||||
if eval_context is None:
|
||||
eval_context = {}
|
||||
self.eval_context: dict[str, any] = eval_context
|
||||
|
||||
self.loaded: dict[str, dict[str, any]] = {"od": {}, "py": {}, "drawio": {}, "rules": {}}
|
||||
|
||||
|
||||
self._state = DevState()
|
||||
self._mmm_cs = bootstrap_scd(self._state)
|
||||
|
||||
self.end_time = float("inf")
|
||||
self.sub_schedules = float("inf")
|
||||
|
||||
def load_schedule(self, filename):
|
||||
return self._load_schedule(filename, _main=True) is not None
|
||||
|
||||
|
||||
def _load_schedule(self, filename: str, *, _main = True) -> Schedule | None:
|
||||
if filename.endswith(".drawio"):
|
||||
if (filename := self._generate_schedule_drawio(filename)) is None:
|
||||
return None
|
||||
|
||||
if filename.endswith(".od"):
|
||||
if (filename := self._generate_schedule_od(filename)) is None:
|
||||
return None
|
||||
if filename.endswith(".py"):
|
||||
s = self._load_schedule_py(filename, _main=_main)
|
||||
return s
|
||||
|
||||
raise Exception(f"Error unknown file: {filename}")
|
||||
|
||||
def _load_schedule_py(self, filename: str, *, _main = True) -> "Schedule":
|
||||
if (s:= self.loaded["py"].get(filename, None)) is not None:
|
||||
return s
|
||||
|
||||
spec = importlib.util.spec_from_file_location(filename, str(self.directory / filename))
|
||||
schedule_module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(schedule_module)
|
||||
self.loaded["py"][filename] = (s:= schedule_module.Schedule())
|
||||
if _main:
|
||||
self.schedule_main = s
|
||||
self.load_matchers(s)
|
||||
return s
|
||||
|
||||
def _generate_schedule_od(self, filename: str) -> str | None:
|
||||
if (s:= self.loaded.get(("od", filename), None)) is not None:
|
||||
return s
|
||||
file = str(self.directory / filename)
|
||||
self._print("Generating schedule ...")
|
||||
with open(f"{os.path.dirname(__file__)}/models/scheduling_MM.od", "r") as f_MM:
|
||||
mm_cs = f_MM.read()
|
||||
try:
|
||||
with open(file, "r") as f_M:
|
||||
m_cs = f_M.read()
|
||||
except FileNotFoundError:
|
||||
self._print(f"File not found: {file}")
|
||||
return None
|
||||
|
||||
self._print("OK\n\nParsing models\n\tParsing meta model")
|
||||
try:
|
||||
scheduling_mm = parser_cd.parse_cd(
|
||||
self._state,
|
||||
m_text=mm_cs,
|
||||
)
|
||||
except Exception as e:
|
||||
self._print(
|
||||
f"Error while parsing meta-model: scheduling_MM.od\n\t{e}"
|
||||
)
|
||||
return None
|
||||
self._print(f"\tParsing '{filename}' model")
|
||||
try:
|
||||
scheduling_m = parser_od.parse_od(
|
||||
self._state, m_text=m_cs, mm=scheduling_mm
|
||||
)
|
||||
except Exception as e:
|
||||
self._print(f"\033[91mError while parsing model: {filename}\n\t{e}\033[0m")
|
||||
return None
|
||||
if self.conformance:
|
||||
success = True
|
||||
self._print("OK\n\tmeta-meta-model a valid class diagram")
|
||||
conf_err = Conformance(
|
||||
self._state, self._mmm_cs, self._mmm_cs
|
||||
).check_nominal()
|
||||
b = len(conf_err)
|
||||
success = success and not b
|
||||
self._print(
|
||||
f"\t\t{'\033[91m' if b else ''}{render_conformance_check_result(conf_err)}{'\033[0m' if b else ''}"
|
||||
)
|
||||
self._print(
|
||||
f"Is our '{filename}' model a valid 'scheduling_MM.od' diagram?"
|
||||
)
|
||||
conf_err = Conformance(
|
||||
self._state, scheduling_m, scheduling_mm, eval_context=mm_eval_context
|
||||
).check_nominal()
|
||||
b = len(conf_err)
|
||||
success = success and not b
|
||||
self._print(
|
||||
f"\t\t{'\033[91m' if b else ''}{render_conformance_check_result(conf_err)}{'\033[0m' if b else ''}"
|
||||
)
|
||||
if not success:
|
||||
return None
|
||||
od = ODAPI(self._state, scheduling_m, scheduling_mm)
|
||||
g = schedule_generator(od)
|
||||
|
||||
output_buffer = io.StringIO()
|
||||
g.generate_schedule(output_buffer)
|
||||
outfilename = f"{".".join(filename.split(".")[:-1])}.py"
|
||||
open(self.directory / outfilename, "w", encoding='utf-8').write(output_buffer.getvalue())
|
||||
self._print("Schedule generated")
|
||||
self.loaded[("od", filename)] = outfilename
|
||||
return outfilename
|
||||
|
||||
def _print(self, *args) -> None:
|
||||
if self.verbose:
|
||||
print(*args, file=self.out)
|
||||
|
||||
def load_matchers(self, schedule: "Schedule") -> None:
|
||||
matchers = dict()
|
||||
for file in schedule.get_matchers():
|
||||
if (r:= self.loaded.get(("rule", file), None)) is None:
|
||||
self.loaded[("rule", file)] = (r:= self.rule_executor.load_match(self.directory / file))
|
||||
matchers[file] = r
|
||||
schedule.init_schedule(self, self.rule_executor, matchers)
|
||||
|
||||
def generate_dot(self, file: str) -> None:
|
||||
env = Environment(
|
||||
loader=FileSystemLoader(
|
||||
os.path.join(os.path.dirname(__file__), "templates")
|
||||
)
|
||||
)
|
||||
env.trim_blocks = True
|
||||
env.lstrip_blocks = True
|
||||
template_dot = env.get_template("schedule_dot.j2")
|
||||
|
||||
nodes = []
|
||||
edges = []
|
||||
visit = set()
|
||||
for schedule in self.loaded["py"].values():
|
||||
schedule.generate_dot(nodes, edges, visit, template_dot)
|
||||
with open(self.directory / file, "w") as f_dot:
|
||||
f_dot.write(template_dot.render(nodes=nodes, edges=edges))
|
||||
|
||||
def run(self, model) -> tuple[int, str]:
|
||||
self._print("Start simulation")
|
||||
if 'pydevd' in sys.modules:
|
||||
self.end_time = time() + 1000
|
||||
else:
|
||||
self.end_time = time() + 10000
|
||||
return self._runner(model, self.schedule_main, "out", IdGenerator.generate_exec_id(), {})
|
||||
|
||||
def _runner(self, model, schedule: Schedule, exec_port: str, exec_id: int, data: dict[str, any]) -> tuple[int, any]:
|
||||
self._generate_stackframe(schedule, exec_id)
|
||||
cur_node = schedule.start
|
||||
cur_node.run_init(exec_port, exec_id, data)
|
||||
while self.end_time > time():
|
||||
cur_node, port = cur_node.nextState(exec_id)
|
||||
termination_reason = cur_node.execute(port, exec_id, model)
|
||||
if termination_reason is not None:
|
||||
self._delete_stackframe(schedule, exec_id)
|
||||
return termination_reason
|
||||
|
||||
self._delete_stackframe(schedule, exec_id)
|
||||
return -1, "limit reached"
|
||||
|
||||
|
||||
def _generate_stackframe(self, schedule: Schedule, exec_id: int) -> None:
|
||||
for node in schedule.nodes:
|
||||
node.generate_stack_frame(exec_id)
|
||||
|
||||
def _delete_stackframe(self, schedule: Schedule, exec_id: int) -> None:
|
||||
for node in schedule.nodes:
|
||||
node.delete_stack_frame(exec_id)
|
||||
|
||||
|
||||
def _generate_schedule_drawio(self, filename:str) -> str | None:
|
||||
if (s:= self.loaded["drawio"].get(filename, None)) is not None:
|
||||
return s
|
||||
env = Environment(
|
||||
loader=FileSystemLoader(
|
||||
os.path.join(os.path.dirname(__file__), "templates")
|
||||
)
|
||||
)
|
||||
env.trim_blocks = True
|
||||
env.lstrip_blocks = True
|
||||
template = env.get_template("schedule_muMLE.j2")
|
||||
main: bool = False
|
||||
|
||||
node_map: dict[str, list[str | dict[str,str]]]
|
||||
id_counter: int
|
||||
def _get_node_id_map(elem: Cell) -> list[str | dict[str,str]]:
|
||||
nonlocal node_map, id_counter
|
||||
if (e_id := node_map.get(elem.id, None)) is None:
|
||||
e_id = [f"{re.sub(r'[^a-zA-Z1-9_]', '', elem.properties["name"])}_{id_counter}", {}]
|
||||
id_counter += 1
|
||||
node_map[elem.id] = e_id
|
||||
return e_id
|
||||
|
||||
edges: list[tuple[tuple[str, str, str, str], tuple[str,str,str,str]]] = []
|
||||
def _parse_edge(elem: Edge):
|
||||
nonlocal edges
|
||||
try:
|
||||
edges.append((
|
||||
(
|
||||
_get_node_id_map(elem.source.parent.parent.parent)[0],
|
||||
elem.source.properties["label"],
|
||||
elem.source.properties["type"],
|
||||
elem.source.parent.value
|
||||
),
|
||||
(
|
||||
_get_node_id_map(elem.target.parent.parent.parent)[0],
|
||||
elem.target.properties["label"],
|
||||
elem.target.properties["type"],
|
||||
elem.target.parent.value
|
||||
)
|
||||
))
|
||||
except AttributeError as e:
|
||||
raise Exception(f"Missing attribute {e}")
|
||||
return
|
||||
|
||||
def _parse_vertex(elem: Vertex):
|
||||
nonlocal edges
|
||||
try:
|
||||
elem_map = _get_node_id_map(elem)
|
||||
elem_map[1] = elem.properties
|
||||
properties = elem_map[1]
|
||||
properties.pop("label")
|
||||
properties.pop("name")
|
||||
properties.pop("placeholders")
|
||||
if properties.get("type") == "Schedule":
|
||||
if not re.search(r'\.(py|od)$', properties["file"]):
|
||||
properties["file"] = f"{filename}/{properties["file"]}.od"
|
||||
except AttributeError as e:
|
||||
raise Exception(f"Missing attribute {e}")
|
||||
return
|
||||
|
||||
|
||||
abstract_syntax: DrawIOFile = parser.Parser.parse(str(self.directory / filename))
|
||||
filename = filename.removesuffix(".drawio")
|
||||
(self.directory / filename).mkdir(parents=False, exist_ok=True)
|
||||
for page in abstract_syntax.pages:
|
||||
if page.name == "main":
|
||||
main = True
|
||||
if len(page.root.children) != 1:
|
||||
raise Exception(f"Only 1 layer allowed (keybind: ctr+shift+L)")
|
||||
edges = []
|
||||
id_counter = 1
|
||||
node_map = {}
|
||||
|
||||
for element in page.root.children[0].children:
|
||||
match element.__class__.__name__:
|
||||
case "Edge":
|
||||
_parse_edge(cast(Edge, element))
|
||||
case "Vertex":
|
||||
_parse_vertex(cast(Vertex, element))
|
||||
for elem in element.children[0].children:
|
||||
if elem.__class__.__name__ == "Edge":
|
||||
_parse_edge(cast(Edge, elem))
|
||||
continue
|
||||
case _:
|
||||
raise Exception(f"Unexpected element: {element}")
|
||||
with open(self.directory / f"{filename}/{page.name}.od", "w", encoding="utf-8") as f:
|
||||
f.write(template.render(nodes=node_map, edges=edges))
|
||||
if main:
|
||||
self.loaded["drawio"][filename] = (filename_out := f"{filename}/main.od")
|
||||
return filename_out
|
||||
|
||||
self._print("drawio schedule requires main page to automatically load.")
|
||||
return None
|
||||
18
transformation/schedule/schedule.pyi
Normal file
18
transformation/schedule/schedule.pyi
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
from typing import TYPE_CHECKING
|
||||
from transformation.schedule.schedule_lib import *
|
||||
if TYPE_CHECKING:
|
||||
from transformation.schedule.rule_executor import RuleExecutor
|
||||
from rule_scheduler import RuleScheduler
|
||||
|
||||
class Schedule:
|
||||
__slots__ = {
|
||||
"start",
|
||||
"end",
|
||||
"nodes"
|
||||
}
|
||||
def __init__(self): ...
|
||||
|
||||
@staticmethod
|
||||
def get_matchers(): ...
|
||||
def init_schedule(self, scheduler: RuleScheduler, rule_executor: RuleExecutor, matchers): ...
|
||||
def generate_dot(self, *args, **kwargs): ...
|
||||
93
transformation/schedule/schedule_lib/Schedule_lib.xml
Normal file
93
transformation/schedule/schedule_lib/Schedule_lib.xml
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
<mxlibrary>[
|
||||
{
|
||||
"xml": "<mxGraphModel><root><mxCell id=\"0\"/><mxCell id=\"1\" parent=\"0\"/><object label=\"%name%: %type%\" placeholders=\"1\" name=\"start_name\" type=\"Start\" ports_exec_out=\"[&quot;out&quot;]\" ports_data_out=\"[]\" id=\"2\"><mxCell style=\"shape=table;childLayout=tableLayout;startSize=40;collapsible=0;recursiveResize=1;expand=0;fontStyle=1;editable=1;movable=1;resizable=1;rotatable=0;deletable=1;locked=0;connectable=0;allowArrows=0;pointerEvents=0;perimeter=rectanglePerimeter;rounded=1;container=1;dropTarget=0;swimlaneHead=1;swimlaneBody=1;top=1;noLabel=0;autosize=0;resizeHeight=0;spacing=2;metaEdit=1;resizeWidth=0;arcSize=10;\" vertex=\"1\" parent=\"1\"><mxGeometry width=\"160\" height=\"100\" as=\"geometry\"/></mxCell></object><mxCell id=\"3\" value=\"\" style=\"shape=tableRow;horizontal=0;swimlaneHead=0;swimlaneBody=0;top=0;left=0;strokeColor=inherit;bottom=0;right=0;dropTarget=0;fontStyle=0;fillColor=none;points=[[0,0.5],[1,0.5]];startSize=0;collapsible=0;recursiveResize=1;expand=0;rounded=0;allowArrows=0;connectable=0;autosize=1;resizeHeight=1;rotatable=0;\" vertex=\"1\" parent=\"2\"><mxGeometry y=\"40\" width=\"160\" height=\"60\" as=\"geometry\"/></mxCell><mxCell id=\"4\" value=\"Input\" style=\"swimlane;swimlaneHead=0;swimlaneBody=0;fontStyle=0;strokeColor=inherit;connectable=0;fillColor=none;startSize=40;collapsible=0;recursiveResize=1;expand=0;allowArrows=0;autosize=1;rotatable=0;noLabel=1;overflow=hidden;swimlaneLine=0;editable=0;\" vertex=\"1\" parent=\"3\"><mxGeometry width=\"80\" height=\"60\" as=\"geometry\"><mxRectangle width=\"80\" height=\"60\" as=\"alternateBounds\"/></mxGeometry></mxCell><mxCell id=\"5\" value=\"Output\" style=\"swimlane;swimlaneHead=0;swimlaneBody=0;fontStyle=0;strokeColor=inherit;connectable=0;fillColor=none;startSize=40;collapsible=0;recursiveResize=1;expand=0;allowArrows=0;autosize=1;rotatable=0;noLabel=1;overflow=hidden;swimlaneLine=0;editable=0;\" vertex=\"1\" parent=\"3\"><mxGeometry x=\"80\" width=\"80\" height=\"60\" as=\"geometry\"><mxRectangle width=\"80\" height=\"60\" as=\"alternateBounds\"/></mxGeometry></mxCell><object label=\"out\" type=\"exec\" id=\"6\"><mxCell style=\"rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;\" vertex=\"1\" parent=\"5\"><mxGeometry x=\"10\" y=\"10\" width=\"60\" height=\"40\" as=\"geometry\"/></mxCell></object></root></mxGraphModel>",
|
||||
"w": 160,
|
||||
"h": 100,
|
||||
"aspect": "fixed",
|
||||
"title": "Start Node"
|
||||
},
|
||||
{
|
||||
"xml": "<mxGraphModel><root><mxCell id=\"0\"/><mxCell id=\"1\" parent=\"0\"/><object label=\"%name%: %type%\" placeholders=\"1\" name=\"end_name\" type=\"End\" ports_exec_in=\"[&quot;in&quot;]\" ports_data_in=\"[]\" id=\"2\"><mxCell style=\"shape=table;childLayout=tableLayout;startSize=40;collapsible=0;recursiveResize=1;expand=0;fontStyle=1;editable=1;movable=1;resizable=1;rotatable=0;deletable=1;locked=0;connectable=0;allowArrows=0;pointerEvents=0;perimeter=rectanglePerimeter;rounded=1;container=1;dropTarget=0;swimlaneHead=1;swimlaneBody=1;top=1;noLabel=0;autosize=0;resizeHeight=0;spacing=2;metaEdit=1;resizeWidth=0;arcSize=10;\" vertex=\"1\" parent=\"1\"><mxGeometry width=\"160\" height=\"100\" as=\"geometry\"/></mxCell></object><mxCell id=\"3\" value=\"\" style=\"shape=tableRow;horizontal=0;swimlaneHead=0;swimlaneBody=0;top=0;left=0;strokeColor=inherit;bottom=0;right=0;dropTarget=0;fontStyle=0;fillColor=none;points=[[0,0.5],[1,0.5]];startSize=0;collapsible=0;recursiveResize=1;expand=0;rounded=0;allowArrows=0;connectable=0;autosize=1;resizeHeight=1;rotatable=0;\" vertex=\"1\" parent=\"2\"><mxGeometry y=\"40\" width=\"160\" height=\"60\" as=\"geometry\"/></mxCell><mxCell id=\"4\" value=\"Input\" style=\"swimlane;swimlaneHead=0;swimlaneBody=0;fontStyle=0;strokeColor=inherit;connectable=0;fillColor=none;startSize=40;collapsible=0;recursiveResize=1;expand=0;allowArrows=0;autosize=1;rotatable=0;noLabel=1;overflow=hidden;swimlaneLine=0;editable=0;\" vertex=\"1\" parent=\"3\"><mxGeometry width=\"80\" height=\"60\" as=\"geometry\"><mxRectangle width=\"80\" height=\"60\" as=\"alternateBounds\"/></mxGeometry></mxCell><object label=\"in\" type=\"exec\" id=\"5\"><mxCell style=\"rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;\" vertex=\"1\" parent=\"4\"><mxGeometry x=\"10\" y=\"10\" width=\"60\" height=\"40\" as=\"geometry\"/></mxCell></object><mxCell id=\"6\" value=\"Output\" style=\"swimlane;swimlaneHead=0;swimlaneBody=0;fontStyle=0;strokeColor=inherit;connectable=0;fillColor=none;startSize=40;collapsible=0;recursiveResize=1;expand=0;allowArrows=0;autosize=1;rotatable=0;noLabel=1;overflow=hidden;swimlaneLine=0;editable=0;\" vertex=\"1\" parent=\"3\"><mxGeometry x=\"80\" width=\"80\" height=\"60\" as=\"geometry\"><mxRectangle width=\"80\" height=\"60\" as=\"alternateBounds\"/></mxGeometry></mxCell></root></mxGraphModel>",
|
||||
"w": 160,
|
||||
"h": 100,
|
||||
"aspect": "fixed",
|
||||
"title": "End Node"
|
||||
},
|
||||
{
|
||||
"xml": "<mxGraphModel><root><mxCell id=\"0\"/><mxCell id=\"1\" parent=\"0\"/><object label=\"%name%: %type%&#10;%file%&#10;matches: %n%\" placeholders=\"1\" name=\"match_name\" type=\"Match\" file=\"rule_filename.od\" n=\"1\" id=\"2\"><mxCell style=\"shape=table;childLayout=tableLayout;startSize=60;collapsible=0;recursiveResize=1;expand=0;fontStyle=1;editable=1;movable=1;resizable=1;rotatable=0;deletable=1;locked=0;connectable=0;allowArrows=0;pointerEvents=0;perimeter=rectanglePerimeter;rounded=1;container=1;dropTarget=0;swimlaneHead=1;swimlaneBody=1;top=1;noLabel=0;autosize=0;resizeHeight=0;spacing=2;metaEdit=1;resizeWidth=0;arcSize=10;\" vertex=\"1\" parent=\"1\"><mxGeometry width=\"160\" height=\"220\" as=\"geometry\"/></mxCell></object><mxCell id=\"3\" value=\"\" style=\"shape=tableRow;horizontal=0;swimlaneHead=0;swimlaneBody=0;top=0;left=0;strokeColor=inherit;bottom=0;right=0;dropTarget=0;fontStyle=0;fillColor=none;points=[[0,0.5],[1,0.5]];startSize=0;collapsible=0;recursiveResize=1;expand=0;rounded=0;allowArrows=0;connectable=0;autosize=1;resizeHeight=1;rotatable=0;\" vertex=\"1\" parent=\"2\"><mxGeometry y=\"60\" width=\"160\" height=\"160\" as=\"geometry\"/></mxCell><mxCell id=\"4\" value=\"Input\" style=\"swimlane;swimlaneHead=0;swimlaneBody=0;fontStyle=0;strokeColor=inherit;connectable=0;fillColor=none;startSize=60;collapsible=0;recursiveResize=1;expand=0;allowArrows=0;autosize=1;rotatable=0;noLabel=1;overflow=hidden;swimlaneLine=0;editable=0;\" vertex=\"1\" parent=\"3\"><mxGeometry width=\"80\" height=\"160\" as=\"geometry\"><mxRectangle width=\"80\" height=\"160\" as=\"alternateBounds\"/></mxGeometry></mxCell><object label=\"in\" type=\"data\" id=\"5\"><mxCell style=\"rounded=0;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;\" vertex=\"1\" parent=\"4\"><mxGeometry x=\"10\" y=\"110\" width=\"60\" height=\"40\" as=\"geometry\"/></mxCell></object><object label=\"in\" type=\"exec\" id=\"6\"><mxCell style=\"rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;\" vertex=\"1\" parent=\"4\"><mxGeometry x=\"10\" y=\"10\" width=\"60\" height=\"40\" as=\"geometry\"/></mxCell></object><mxCell id=\"7\" value=\"Output\" style=\"swimlane;swimlaneHead=0;swimlaneBody=0;fontStyle=0;strokeColor=inherit;connectable=0;fillColor=none;startSize=40;collapsible=0;recursiveResize=1;expand=0;allowArrows=0;autosize=1;rotatable=0;noLabel=1;overflow=hidden;swimlaneLine=0;editable=0;\" vertex=\"1\" parent=\"3\"><mxGeometry x=\"80\" width=\"80\" height=\"160\" as=\"geometry\"><mxRectangle width=\"80\" height=\"160\" as=\"alternateBounds\"/></mxGeometry></mxCell><object label=\"out\" type=\"data\" id=\"8\"><mxCell style=\"rounded=0;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;\" vertex=\"1\" parent=\"7\"><mxGeometry x=\"10\" y=\"110\" width=\"60\" height=\"40\" as=\"geometry\"/></mxCell></object><object label=\"success\" type=\"exec\" id=\"9\"><mxCell style=\"rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;\" vertex=\"1\" parent=\"7\"><mxGeometry x=\"10\" y=\"10\" width=\"60\" height=\"40\" as=\"geometry\"/></mxCell></object><object label=\"fail\" type=\"exec\" id=\"10\"><mxCell style=\"rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;\" vertex=\"1\" parent=\"7\"><mxGeometry x=\"10\" y=\"60\" width=\"60\" height=\"40\" as=\"geometry\"/></mxCell></object></root></mxGraphModel>",
|
||||
"w": 160,
|
||||
"h": 220,
|
||||
"aspect": "fixed",
|
||||
"title": "Match Node"
|
||||
},
|
||||
{
|
||||
"xml": "<mxGraphModel><root><mxCell id=\"0\"/><mxCell id=\"1\" parent=\"0\"/><object label=\"%name%: %type%&#10;%file%\" placeholders=\"1\" name=\"rewrite_name\" type=\"Rewrite\" file=\"rule_filename.od\" id=\"2\"><mxCell style=\"shape=table;childLayout=tableLayout;startSize=40;collapsible=0;recursiveResize=1;expand=0;fontStyle=1;editable=1;movable=1;resizable=1;rotatable=0;deletable=1;locked=0;connectable=0;allowArrows=0;pointerEvents=0;perimeter=rectanglePerimeter;rounded=1;container=1;dropTarget=0;swimlaneHead=1;swimlaneBody=1;top=1;noLabel=0;autosize=0;resizeHeight=0;spacing=2;metaEdit=1;resizeWidth=0;arcSize=10;\" vertex=\"1\" parent=\"1\"><mxGeometry y=\"1.1368683772161603e-13\" width=\"160\" height=\"150\" as=\"geometry\"/></mxCell></object><mxCell id=\"3\" value=\"\" style=\"shape=tableRow;horizontal=0;swimlaneHead=0;swimlaneBody=0;top=0;left=0;strokeColor=inherit;bottom=0;right=0;dropTarget=0;fontStyle=0;fillColor=none;points=[[0,0.5],[1,0.5]];startSize=0;collapsible=0;recursiveResize=1;expand=0;rounded=0;allowArrows=0;connectable=0;autosize=1;resizeHeight=1;rotatable=0;\" vertex=\"1\" parent=\"2\"><mxGeometry y=\"40\" width=\"160\" height=\"110\" as=\"geometry\"/></mxCell><mxCell id=\"4\" value=\"Input\" style=\"swimlane;swimlaneHead=0;swimlaneBody=0;fontStyle=0;strokeColor=inherit;connectable=0;fillColor=none;startSize=60;collapsible=0;recursiveResize=1;expand=0;allowArrows=0;autosize=1;rotatable=0;noLabel=1;overflow=hidden;swimlaneLine=0;editable=0;\" vertex=\"1\" parent=\"3\"><mxGeometry width=\"80\" height=\"110\" as=\"geometry\"><mxRectangle width=\"80\" height=\"110\" as=\"alternateBounds\"/></mxGeometry></mxCell><object label=\"in\" type=\"exec\" id=\"5\"><mxCell style=\"rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;\" vertex=\"1\" parent=\"4\"><mxGeometry x=\"10\" y=\"10\" width=\"60\" height=\"40\" as=\"geometry\"/></mxCell></object><mxCell id=\"6\" value=\"Output\" style=\"swimlane;swimlaneHead=0;swimlaneBody=0;fontStyle=0;strokeColor=inherit;connectable=0;fillColor=none;startSize=40;collapsible=0;recursiveResize=1;expand=0;allowArrows=0;autosize=1;rotatable=0;noLabel=1;overflow=hidden;swimlaneLine=0;editable=0;\" vertex=\"1\" parent=\"3\"><mxGeometry x=\"80\" width=\"80\" height=\"110\" as=\"geometry\"><mxRectangle width=\"80\" height=\"110\" as=\"alternateBounds\"/></mxGeometry></mxCell><object label=\"out\" type=\"exec\" id=\"7\"><mxCell style=\"rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;\" vertex=\"1\" parent=\"6\"><mxGeometry x=\"10\" y=\"10\" width=\"60\" height=\"40\" as=\"geometry\"/></mxCell></object><object label=\"in\" type=\"data\" id=\"8\"><mxCell style=\"rounded=0;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;\" vertex=\"1\" parent=\"6\"><mxGeometry x=\"-70\" y=\"60\" width=\"60\" height=\"40\" as=\"geometry\"/></mxCell></object><object label=\"out\" type=\"data\" id=\"9\"><mxCell style=\"rounded=0;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;\" vertex=\"1\" parent=\"6\"><mxGeometry x=\"10\" y=\"60\" width=\"60\" height=\"40\" as=\"geometry\"/></mxCell></object></root></mxGraphModel>",
|
||||
"w": 160,
|
||||
"h": 150,
|
||||
"aspect": "fixed",
|
||||
"title": "Rewrite Node"
|
||||
},
|
||||
{
|
||||
"xml": "<mxGraphModel><root><mxCell id=\"0\"/><mxCell id=\"1\" parent=\"0\"/><object label=\"%name%: %type%\" placeholders=\"1\" name=\"action_name\" type=\"Action\" ports_exec_in=\"[&quot;in&quot;]\" ports_exec_out=\"[&quot;out&quot;]\" ports_data_in=\"[]\" ports_data_out=\"[]\" action=\"print(&quot;hello world&quot;)\" id=\"2\"><mxCell style=\"shape=table;childLayout=tableLayout;startSize=40;collapsible=0;recursiveResize=1;expand=0;fontStyle=1;editable=1;movable=1;resizable=1;rotatable=0;deletable=1;locked=0;connectable=0;allowArrows=0;pointerEvents=0;perimeter=rectanglePerimeter;rounded=1;container=1;dropTarget=0;swimlaneHead=1;swimlaneBody=1;top=1;noLabel=0;autosize=0;resizeHeight=0;spacing=2;metaEdit=1;resizeWidth=0;arcSize=10;\" vertex=\"1\" parent=\"1\"><mxGeometry width=\"160\" height=\"100\" as=\"geometry\"/></mxCell></object><mxCell id=\"3\" value=\"\" style=\"shape=tableRow;horizontal=0;swimlaneHead=0;swimlaneBody=0;top=0;left=0;strokeColor=inherit;bottom=0;right=0;dropTarget=0;fontStyle=0;fillColor=none;points=[[0,0.5],[1,0.5]];startSize=0;collapsible=0;recursiveResize=1;expand=0;rounded=0;allowArrows=0;connectable=0;autosize=1;resizeHeight=1;rotatable=0;\" vertex=\"1\" parent=\"2\"><mxGeometry y=\"40\" width=\"160\" height=\"60\" as=\"geometry\"/></mxCell><mxCell id=\"4\" value=\"Input\" style=\"swimlane;swimlaneHead=0;swimlaneBody=0;fontStyle=0;strokeColor=inherit;connectable=0;fillColor=none;startSize=60;collapsible=0;recursiveResize=1;expand=0;allowArrows=0;autosize=1;rotatable=0;noLabel=1;overflow=hidden;swimlaneLine=0;editable=0;\" vertex=\"1\" parent=\"3\"><mxGeometry width=\"80\" height=\"60\" as=\"geometry\"><mxRectangle width=\"80\" height=\"60\" as=\"alternateBounds\"/></mxGeometry></mxCell><object label=\"in\" type=\"exec\" id=\"5\"><mxCell style=\"rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;\" vertex=\"1\" parent=\"4\"><mxGeometry x=\"10\" y=\"10\" width=\"60\" height=\"40\" as=\"geometry\"/></mxCell></object><mxCell id=\"6\" value=\"Output\" style=\"swimlane;swimlaneHead=0;swimlaneBody=0;fontStyle=0;strokeColor=inherit;connectable=0;fillColor=none;startSize=40;collapsible=0;recursiveResize=1;expand=0;allowArrows=0;autosize=1;rotatable=0;noLabel=1;overflow=hidden;swimlaneLine=0;editable=0;\" vertex=\"1\" parent=\"3\"><mxGeometry x=\"80\" width=\"80\" height=\"60\" as=\"geometry\"><mxRectangle width=\"80\" height=\"60\" as=\"alternateBounds\"/></mxGeometry></mxCell><object label=\"out\" type=\"exec\" id=\"7\"><mxCell style=\"rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;\" vertex=\"1\" parent=\"6\"><mxGeometry x=\"10\" y=\"10\" width=\"60\" height=\"40\" as=\"geometry\"/></mxCell></object></root></mxGraphModel>",
|
||||
"w": 160,
|
||||
"h": 100,
|
||||
"aspect": "fixed",
|
||||
"title": "Action Node"
|
||||
},
|
||||
{
|
||||
"xml": "<mxGraphModel><root><mxCell id=\"0\"/><mxCell id=\"1\" parent=\"0\"/><object label=\"%name%: %type%\" placeholders=\"1\" name=\"modify_name\" type=\"Modify\" rename=\"{&quot;t&quot;:&quot;transition&quot;}\" delete=\"[]\" id=\"2\"><mxCell style=\"shape=table;childLayout=tableLayout;startSize=40;collapsible=0;recursiveResize=1;expand=0;fontStyle=1;editable=1;movable=1;resizable=1;rotatable=0;deletable=1;locked=0;connectable=0;allowArrows=0;pointerEvents=0;perimeter=rectanglePerimeter;rounded=1;container=1;dropTarget=0;swimlaneHead=1;swimlaneBody=1;top=1;noLabel=0;autosize=0;resizeHeight=0;spacing=2;metaEdit=1;resizeWidth=0;arcSize=10;\" vertex=\"1\" parent=\"1\"><mxGeometry width=\"160\" height=\"100\" as=\"geometry\"/></mxCell></object><mxCell id=\"3\" value=\"\" style=\"shape=tableRow;horizontal=0;swimlaneHead=0;swimlaneBody=0;top=0;left=0;strokeColor=inherit;bottom=0;right=0;dropTarget=0;fontStyle=0;fillColor=none;points=[[0,0.5],[1,0.5]];startSize=0;collapsible=0;recursiveResize=1;expand=0;rounded=0;allowArrows=0;connectable=0;autosize=1;resizeHeight=1;rotatable=0;\" vertex=\"1\" parent=\"2\"><mxGeometry y=\"40\" width=\"160\" height=\"60\" as=\"geometry\"/></mxCell><mxCell id=\"4\" value=\"Input\" style=\"swimlane;swimlaneHead=0;swimlaneBody=0;fontStyle=0;strokeColor=inherit;connectable=0;fillColor=none;startSize=60;collapsible=0;recursiveResize=1;expand=0;allowArrows=0;autosize=1;rotatable=0;noLabel=1;overflow=hidden;swimlaneLine=0;editable=0;\" vertex=\"1\" parent=\"3\"><mxGeometry width=\"80\" height=\"60\" as=\"geometry\"><mxRectangle width=\"80\" height=\"60\" as=\"alternateBounds\"/></mxGeometry></mxCell><object label=\"in\" type=\"data\" id=\"5\"><mxCell style=\"rounded=0;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;\" vertex=\"1\" parent=\"4\"><mxGeometry x=\"10\" y=\"10\" width=\"60\" height=\"40\" as=\"geometry\"/></mxCell></object><mxCell id=\"6\" value=\"Output\" style=\"swimlane;swimlaneHead=0;swimlaneBody=0;fontStyle=0;strokeColor=inherit;connectable=0;fillColor=none;startSize=40;collapsible=0;recursiveResize=1;expand=0;allowArrows=0;autosize=1;rotatable=0;noLabel=1;overflow=hidden;swimlaneLine=0;editable=0;\" vertex=\"1\" parent=\"3\"><mxGeometry x=\"80\" width=\"80\" height=\"60\" as=\"geometry\"><mxRectangle width=\"80\" height=\"60\" as=\"alternateBounds\"/></mxGeometry></mxCell><object label=\"out\" type=\"data\" id=\"7\"><mxCell style=\"rounded=0;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;\" vertex=\"1\" parent=\"6\"><mxGeometry x=\"10\" y=\"10\" width=\"60\" height=\"40\" as=\"geometry\"/></mxCell></object></root></mxGraphModel>",
|
||||
"w": 160,
|
||||
"h": 100,
|
||||
"aspect": "fixed",
|
||||
"title": "Modify Node"
|
||||
},
|
||||
{
|
||||
"xml": "<mxGraphModel><root><mxCell id=\"0\"/><mxCell id=\"1\" parent=\"0\"/><object label=\"%name%: %type%\" placeholders=\"1\" name=\"merge_name\" type=\"Merge\" ports_data_in=\"[&quot;input1&quot;, &quot;input2&quot;]\" id=\"2\"><mxCell style=\"shape=table;childLayout=tableLayout;startSize=40;collapsible=0;recursiveResize=1;expand=0;fontStyle=1;editable=1;movable=1;resizable=1;rotatable=0;deletable=1;locked=0;connectable=0;allowArrows=0;pointerEvents=0;perimeter=rectanglePerimeter;rounded=1;container=1;dropTarget=0;swimlaneHead=1;swimlaneBody=1;top=1;noLabel=0;autosize=0;resizeHeight=0;spacing=2;metaEdit=1;resizeWidth=0;arcSize=10;\" vertex=\"1\" parent=\"1\"><mxGeometry width=\"160\" height=\"150\" as=\"geometry\"/></mxCell></object><mxCell id=\"3\" value=\"\" style=\"shape=tableRow;horizontal=0;swimlaneHead=0;swimlaneBody=0;top=0;left=0;strokeColor=inherit;bottom=0;right=0;dropTarget=0;fontStyle=0;fillColor=none;points=[[0,0.5],[1,0.5]];startSize=0;collapsible=0;recursiveResize=1;expand=0;rounded=0;allowArrows=0;connectable=0;autosize=1;resizeHeight=1;rotatable=0;\" vertex=\"1\" parent=\"2\"><mxGeometry y=\"40\" width=\"160\" height=\"110\" as=\"geometry\"/></mxCell><mxCell id=\"4\" value=\"Input\" style=\"swimlane;swimlaneHead=0;swimlaneBody=0;fontStyle=0;strokeColor=inherit;connectable=0;fillColor=none;startSize=60;collapsible=0;recursiveResize=1;expand=0;allowArrows=0;autosize=1;rotatable=0;noLabel=1;overflow=hidden;swimlaneLine=0;editable=0;\" vertex=\"1\" parent=\"3\"><mxGeometry width=\"80\" height=\"110\" as=\"geometry\"><mxRectangle width=\"80\" height=\"110\" as=\"alternateBounds\"/></mxGeometry></mxCell><object label=\"input1\" type=\"data\" id=\"5\"><mxCell style=\"rounded=0;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;\" vertex=\"1\" parent=\"4\"><mxGeometry x=\"10\" y=\"10\" width=\"60\" height=\"40\" as=\"geometry\"/></mxCell></object><object label=\"input2\" type=\"data\" id=\"6\"><mxCell style=\"rounded=0;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;\" vertex=\"1\" parent=\"4\"><mxGeometry x=\"10\" y=\"60\" width=\"60\" height=\"40\" as=\"geometry\"/></mxCell></object><mxCell id=\"7\" value=\"Output\" style=\"swimlane;swimlaneHead=0;swimlaneBody=0;fontStyle=0;strokeColor=inherit;connectable=0;fillColor=none;startSize=40;collapsible=0;recursiveResize=1;expand=0;allowArrows=0;autosize=1;rotatable=0;noLabel=1;overflow=hidden;swimlaneLine=0;editable=0;\" vertex=\"1\" parent=\"3\"><mxGeometry x=\"80\" width=\"80\" height=\"110\" as=\"geometry\"><mxRectangle width=\"80\" height=\"110\" as=\"alternateBounds\"/></mxGeometry></mxCell><object label=\"out\" type=\"data\" id=\"8\"><mxCell style=\"rounded=0;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;\" vertex=\"1\" parent=\"7\"><mxGeometry x=\"10\" y=\"10\" width=\"60\" height=\"40\" as=\"geometry\"/></mxCell></object></root></mxGraphModel>",
|
||||
"w": 160,
|
||||
"h": 150,
|
||||
"aspect": "fixed",
|
||||
"title": "Merge Node"
|
||||
},
|
||||
{
|
||||
"xml": "<mxGraphModel><root><mxCell id=\"0\"/><mxCell id=\"1\" parent=\"0\"/><object label=\"%name%: %type%\" placeholders=\"1\" name=\"store_name\" type=\"Store\" ports=\"[&quot;input1&quot;]\" id=\"2\"><mxCell style=\"shape=table;childLayout=tableLayout;startSize=40;collapsible=0;recursiveResize=1;expand=0;fontStyle=1;editable=1;movable=1;resizable=1;rotatable=0;deletable=1;locked=0;connectable=0;allowArrows=0;pointerEvents=0;perimeter=rectanglePerimeter;rounded=1;container=1;dropTarget=0;swimlaneHead=1;swimlaneBody=1;top=1;noLabel=0;autosize=0;resizeHeight=0;spacing=2;metaEdit=1;resizeWidth=0;arcSize=10;\" vertex=\"1\" parent=\"1\"><mxGeometry width=\"160\" height=\"200\" as=\"geometry\"/></mxCell></object><mxCell id=\"3\" value=\"\" style=\"shape=tableRow;horizontal=0;swimlaneHead=0;swimlaneBody=0;top=0;left=0;strokeColor=inherit;bottom=0;right=0;dropTarget=0;fontStyle=0;fillColor=none;points=[[0,0.5],[1,0.5]];startSize=0;collapsible=0;recursiveResize=1;expand=0;rounded=0;allowArrows=0;connectable=0;autosize=1;resizeHeight=1;rotatable=0;\" vertex=\"1\" parent=\"2\"><mxGeometry y=\"40\" width=\"160\" height=\"160\" as=\"geometry\"/></mxCell><mxCell id=\"4\" value=\"Input\" style=\"swimlane;swimlaneHead=0;swimlaneBody=0;fontStyle=0;strokeColor=inherit;connectable=0;fillColor=none;startSize=60;collapsible=0;recursiveResize=1;expand=0;allowArrows=0;autosize=1;rotatable=0;noLabel=1;overflow=hidden;swimlaneLine=0;editable=0;\" vertex=\"1\" parent=\"3\"><mxGeometry width=\"80\" height=\"160\" as=\"geometry\"><mxRectangle width=\"80\" height=\"160\" as=\"alternateBounds\"/></mxGeometry></mxCell><object label=\"in\" type=\"exec\" id=\"5\"><mxCell style=\"rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;\" vertex=\"1\" parent=\"4\"><mxGeometry x=\"10\" y=\"10\" width=\"60\" height=\"40\" as=\"geometry\"/></mxCell></object><object label=\"input1\" type=\"exec\" id=\"6\"><mxCell style=\"rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;\" vertex=\"1\" parent=\"4\"><mxGeometry x=\"10\" y=\"60\" width=\"60\" height=\"40\" as=\"geometry\"/></mxCell></object><object label=\"input1\" type=\"data\" id=\"7\"><mxCell style=\"rounded=0;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;\" vertex=\"1\" parent=\"4\"><mxGeometry x=\"10\" y=\"110\" width=\"60\" height=\"40\" as=\"geometry\"/></mxCell></object><mxCell id=\"8\" value=\"Output\" style=\"swimlane;swimlaneHead=0;swimlaneBody=0;fontStyle=0;strokeColor=inherit;connectable=0;fillColor=none;startSize=40;collapsible=0;recursiveResize=1;expand=0;allowArrows=0;autosize=1;rotatable=0;noLabel=1;overflow=hidden;swimlaneLine=0;editable=0;\" vertex=\"1\" parent=\"3\"><mxGeometry x=\"80\" width=\"80\" height=\"160\" as=\"geometry\"><mxRectangle width=\"80\" height=\"160\" as=\"alternateBounds\"/></mxGeometry></mxCell><object label=\"out\" type=\"data\" id=\"9\"><mxCell style=\"rounded=0;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;\" vertex=\"1\" parent=\"8\"><mxGeometry x=\"10\" y=\"110\" width=\"60\" height=\"40\" as=\"geometry\"/></mxCell></object><object label=\"out\" type=\"exec\" id=\"10\"><mxCell style=\"rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;\" vertex=\"1\" parent=\"8\"><mxGeometry x=\"10\" y=\"10\" width=\"60\" height=\"40\" as=\"geometry\"/></mxCell></object><object label=\"input1\" type=\"exec\" id=\"11\"><mxCell style=\"rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;\" vertex=\"1\" parent=\"8\"><mxGeometry x=\"10\" y=\"60\" width=\"60\" height=\"40\" as=\"geometry\"/></mxCell></object></root></mxGraphModel>",
|
||||
"w": 160,
|
||||
"h": 200,
|
||||
"aspect": "fixed",
|
||||
"title": "Store Node"
|
||||
},
|
||||
{
|
||||
"xml": "<mxGraphModel><root><mxCell id=\"0\"/><mxCell id=\"1\" parent=\"0\"/><object label=\"%name%: %type%\" placeholders=\"1\" name=\"loop_name\" type=\"Loop\" id=\"2\"><mxCell style=\"shape=table;childLayout=tableLayout;startSize=40;collapsible=0;recursiveResize=1;expand=0;fontStyle=1;editable=1;movable=1;resizable=1;rotatable=0;deletable=1;locked=0;connectable=0;allowArrows=0;pointerEvents=0;perimeter=rectanglePerimeter;rounded=1;container=1;dropTarget=0;swimlaneHead=1;swimlaneBody=1;top=1;noLabel=0;autosize=0;resizeHeight=0;spacing=2;metaEdit=1;resizeWidth=0;arcSize=10;\" vertex=\"1\" parent=\"1\"><mxGeometry width=\"160\" height=\"200\" as=\"geometry\"/></mxCell></object><mxCell id=\"3\" value=\"\" style=\"shape=tableRow;horizontal=0;swimlaneHead=0;swimlaneBody=0;top=0;left=0;strokeColor=inherit;bottom=0;right=0;dropTarget=0;fontStyle=0;fillColor=none;points=[[0,0.5],[1,0.5]];startSize=0;collapsible=0;recursiveResize=1;expand=0;rounded=0;allowArrows=0;connectable=0;autosize=1;resizeHeight=1;rotatable=0;\" vertex=\"1\" parent=\"2\"><mxGeometry y=\"40\" width=\"160\" height=\"160\" as=\"geometry\"/></mxCell><mxCell id=\"4\" value=\"Input\" style=\"swimlane;swimlaneHead=0;swimlaneBody=0;fontStyle=0;strokeColor=inherit;connectable=0;fillColor=none;startSize=60;collapsible=0;recursiveResize=1;expand=0;allowArrows=0;autosize=1;rotatable=0;noLabel=1;overflow=hidden;swimlaneLine=0;editable=0;\" vertex=\"1\" parent=\"3\"><mxGeometry width=\"80\" height=\"160\" as=\"geometry\"><mxRectangle width=\"80\" height=\"160\" as=\"alternateBounds\"/></mxGeometry></mxCell><object label=\"in\" type=\"data\" id=\"5\"><mxCell style=\"rounded=0;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;\" vertex=\"1\" parent=\"4\"><mxGeometry x=\"10\" y=\"110\" width=\"60\" height=\"40\" as=\"geometry\"/></mxCell></object><object label=\"in\" type=\"exec\" id=\"6\"><mxCell style=\"rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;\" vertex=\"1\" parent=\"4\"><mxGeometry x=\"10\" y=\"10\" width=\"60\" height=\"40\" as=\"geometry\"/></mxCell></object><mxCell id=\"7\" value=\"Output\" style=\"swimlane;swimlaneHead=0;swimlaneBody=0;fontStyle=0;strokeColor=inherit;connectable=0;fillColor=none;startSize=40;collapsible=0;recursiveResize=1;expand=0;allowArrows=0;autosize=1;rotatable=0;noLabel=1;overflow=hidden;swimlaneLine=0;editable=0;\" vertex=\"1\" parent=\"3\"><mxGeometry x=\"80\" width=\"80\" height=\"160\" as=\"geometry\"><mxRectangle width=\"80\" height=\"160\" as=\"alternateBounds\"/></mxGeometry></mxCell><object label=\"out\" type=\"data\" id=\"8\"><mxCell style=\"rounded=0;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;\" vertex=\"1\" parent=\"7\"><mxGeometry x=\"10\" y=\"110\" width=\"60\" height=\"40\" as=\"geometry\"/></mxCell></object><object label=\"it\" type=\"exec\" id=\"9\"><mxCell style=\"rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;\" vertex=\"1\" parent=\"7\"><mxGeometry x=\"10\" y=\"10\" width=\"60\" height=\"40\" as=\"geometry\"/></mxCell></object><object label=\"out\" type=\"exec\" id=\"10\"><mxCell style=\"rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;\" vertex=\"1\" parent=\"7\"><mxGeometry x=\"10\" y=\"60\" width=\"60\" height=\"40\" as=\"geometry\"/></mxCell></object></root></mxGraphModel>",
|
||||
"w": 160,
|
||||
"h": 200,
|
||||
"aspect": "fixed",
|
||||
"title": "Loop Node"
|
||||
},
|
||||
{
|
||||
"xml": "<mxGraphModel><root><mxCell id=\"0\"/><mxCell id=\"1\" parent=\"0\"/><object label=\"%name%: %type%&#10;%file%\" placeholders=\"1\" name=\"schedule_name\" type=\"Schedule\" file=\"schedule_page-name\" id=\"2\"><mxCell style=\"shape=table;childLayout=tableLayout;startSize=40;collapsible=0;recursiveResize=1;expand=0;fontStyle=1;editable=1;movable=1;resizable=1;rotatable=0;deletable=1;locked=0;connectable=0;allowArrows=0;pointerEvents=0;perimeter=rectanglePerimeter;rounded=1;container=1;dropTarget=0;swimlaneHead=1;swimlaneBody=1;top=1;noLabel=0;autosize=0;resizeHeight=0;spacing=2;metaEdit=1;resizeWidth=0;arcSize=10;\" vertex=\"1\" parent=\"1\"><mxGeometry width=\"160\" height=\"100\" as=\"geometry\"/></mxCell></object><mxCell id=\"3\" value=\"\" style=\"shape=tableRow;horizontal=0;swimlaneHead=0;swimlaneBody=0;top=0;left=0;strokeColor=inherit;bottom=0;right=0;dropTarget=0;fontStyle=0;fillColor=none;points=[[0,0.5],[1,0.5]];startSize=0;collapsible=0;recursiveResize=1;expand=0;rounded=0;allowArrows=0;connectable=0;autosize=1;resizeHeight=1;rotatable=0;\" vertex=\"1\" parent=\"2\"><mxGeometry y=\"40\" width=\"160\" height=\"60\" as=\"geometry\"/></mxCell><mxCell id=\"4\" value=\"Input\" style=\"swimlane;swimlaneHead=0;swimlaneBody=0;fontStyle=0;strokeColor=inherit;connectable=0;fillColor=none;startSize=60;collapsible=0;recursiveResize=1;expand=0;allowArrows=0;autosize=1;rotatable=0;noLabel=1;overflow=hidden;swimlaneLine=0;editable=0;\" vertex=\"1\" parent=\"3\"><mxGeometry width=\"80\" height=\"60\" as=\"geometry\"><mxRectangle width=\"80\" height=\"60\" as=\"alternateBounds\"/></mxGeometry></mxCell><object label=\"out\" type=\"exec\" id=\"5\"><mxCell style=\"rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;\" vertex=\"1\" parent=\"4\"><mxGeometry x=\"10\" y=\"10\" width=\"60\" height=\"40\" as=\"geometry\"/></mxCell></object><mxCell id=\"6\" value=\"Output\" style=\"swimlane;swimlaneHead=0;swimlaneBody=0;fontStyle=0;strokeColor=inherit;connectable=0;fillColor=none;startSize=40;collapsible=0;recursiveResize=1;expand=0;allowArrows=0;autosize=1;rotatable=0;noLabel=1;overflow=hidden;swimlaneLine=0;editable=0;\" vertex=\"1\" parent=\"3\"><mxGeometry x=\"80\" width=\"80\" height=\"60\" as=\"geometry\"><mxRectangle width=\"80\" height=\"60\" as=\"alternateBounds\"/></mxGeometry></mxCell><object label=\"in\" type=\"exec\" id=\"7\"><mxCell style=\"rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;\" vertex=\"1\" parent=\"6\"><mxGeometry x=\"10\" y=\"10\" width=\"60\" height=\"40\" as=\"geometry\"/></mxCell></object></root></mxGraphModel>",
|
||||
"w": 160,
|
||||
"h": 100,
|
||||
"aspect": "fixed",
|
||||
"title": "Schedule Node"
|
||||
},
|
||||
{
|
||||
"xml": "<mxGraphModel><root><mxCell id=\"0\"/><mxCell id=\"1\" parent=\"0\"/><object label=\"%name%: %type%\" placeholders=\"1\" name=\"print_name\" type=\"Print\" event=\"False\" custom=\"{{ data }}\" id=\"2\"><mxCell style=\"shape=table;childLayout=tableLayout;startSize=40;collapsible=0;recursiveResize=1;expand=0;fontStyle=1;editable=1;movable=1;resizable=1;rotatable=0;deletable=1;locked=0;connectable=0;allowArrows=0;pointerEvents=0;perimeter=rectanglePerimeter;rounded=1;container=1;dropTarget=0;swimlaneHead=1;swimlaneBody=1;top=1;noLabel=0;autosize=0;resizeHeight=0;spacing=2;metaEdit=1;resizeWidth=0;arcSize=10;\" vertex=\"1\" parent=\"1\"><mxGeometry width=\"160\" height=\"150\" as=\"geometry\"/></mxCell></object><mxCell id=\"3\" value=\"\" style=\"shape=tableRow;horizontal=0;swimlaneHead=0;swimlaneBody=0;top=0;left=0;strokeColor=inherit;bottom=0;right=0;dropTarget=0;fontStyle=0;fillColor=none;points=[[0,0.5],[1,0.5]];startSize=0;collapsible=0;recursiveResize=1;expand=0;rounded=0;allowArrows=0;connectable=0;autosize=1;resizeHeight=1;rotatable=0;\" vertex=\"1\" parent=\"2\"><mxGeometry y=\"40\" width=\"160\" height=\"110\" as=\"geometry\"/></mxCell><mxCell id=\"4\" value=\"Input\" style=\"swimlane;swimlaneHead=0;swimlaneBody=0;fontStyle=0;strokeColor=inherit;connectable=0;fillColor=none;startSize=60;collapsible=0;recursiveResize=1;expand=0;allowArrows=0;autosize=1;rotatable=0;noLabel=1;overflow=hidden;swimlaneLine=0;editable=0;\" vertex=\"1\" parent=\"3\"><mxGeometry width=\"80\" height=\"110\" as=\"geometry\"><mxRectangle width=\"80\" height=\"110\" as=\"alternateBounds\"/></mxGeometry></mxCell><object label=\"in\" type=\"exec\" id=\"5\"><mxCell style=\"rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;\" vertex=\"1\" parent=\"4\"><mxGeometry x=\"10\" y=\"10\" width=\"60\" height=\"40\" as=\"geometry\"/></mxCell></object><mxCell id=\"6\" value=\"Output\" style=\"swimlane;swimlaneHead=0;swimlaneBody=0;fontStyle=0;strokeColor=inherit;connectable=0;fillColor=none;startSize=40;collapsible=0;recursiveResize=1;expand=0;allowArrows=0;autosize=1;rotatable=0;noLabel=1;overflow=hidden;swimlaneLine=0;editable=0;\" vertex=\"1\" parent=\"3\"><mxGeometry x=\"80\" width=\"80\" height=\"110\" as=\"geometry\"><mxRectangle width=\"80\" height=\"110\" as=\"alternateBounds\"/></mxGeometry></mxCell><object label=\"out\" type=\"exec\" id=\"7\"><mxCell style=\"rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;\" vertex=\"1\" parent=\"6\"><mxGeometry x=\"10\" y=\"10\" width=\"60\" height=\"40\" as=\"geometry\"/></mxCell></object><object label=\"in\" type=\"data\" id=\"8\"><mxCell style=\"rounded=0;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;\" vertex=\"1\" parent=\"6\"><mxGeometry x=\"-70\" y=\"60\" width=\"60\" height=\"40\" as=\"geometry\"/></mxCell></object></root></mxGraphModel>",
|
||||
"w": 160,
|
||||
"h": 150,
|
||||
"aspect": "fixed",
|
||||
"title": "Print Node"
|
||||
},
|
||||
{
|
||||
"xml": "<mxGraphModel><root><mxCell id=\"0\"/><mxCell id=\"1\" parent=\"0\"/><object label=\"out\" type=\"exec\" id=\"2\"><mxCell style=\"rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;\" vertex=\"1\" parent=\"1\"><mxGeometry width=\"60\" height=\"40\" as=\"geometry\"/></mxCell></object></root></mxGraphModel>",
|
||||
"w": 60,
|
||||
"h": 40,
|
||||
"aspect": "fixed",
|
||||
"title": "Exec Gate"
|
||||
},
|
||||
{
|
||||
"xml": "<mxGraphModel><root><mxCell id=\"0\"/><mxCell id=\"1\" parent=\"0\"/><object label=\"in\" type=\"data\" id=\"2\"><mxCell style=\"rounded=0;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;\" vertex=\"1\" parent=\"1\"><mxGeometry width=\"60\" height=\"40\" as=\"geometry\"/></mxCell></object></root></mxGraphModel>",
|
||||
"w": 60,
|
||||
"h": 40,
|
||||
"aspect": "fixed",
|
||||
"title": "Data Gate"
|
||||
}
|
||||
]</mxlibrary>
|
||||
31
transformation/schedule/schedule_lib/__init__.py
Normal file
31
transformation/schedule/schedule_lib/__init__.py
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
from .action import Action
|
||||
from .data_node import DataNode
|
||||
from .end import End
|
||||
from .exec_node import ExecNode
|
||||
from .loop import Loop
|
||||
from .match import Match
|
||||
from .merge import Merge
|
||||
from .modify import Modify
|
||||
from .null_node import NullNode
|
||||
from .print import Print
|
||||
from .rewrite import Rewrite
|
||||
from .start import Start
|
||||
from .store import Store
|
||||
from .sub_schedule import SubSchedule
|
||||
|
||||
__all__ = [
|
||||
"Action",
|
||||
"DataNode",
|
||||
"End",
|
||||
"ExecNode",
|
||||
"Loop",
|
||||
"Match",
|
||||
"Merge",
|
||||
"Modify",
|
||||
"NullNode",
|
||||
"Rewrite",
|
||||
"Print",
|
||||
"Start",
|
||||
"Store",
|
||||
"SubSchedule",
|
||||
]
|
||||
106
transformation/schedule/schedule_lib/action.py
Normal file
106
transformation/schedule/schedule_lib/action.py
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
from typing import List, override, Type
|
||||
|
||||
from jinja2 import Template
|
||||
|
||||
from api.od import ODAPI
|
||||
from .funcs import not_visited, generate_dot_node
|
||||
from .exec_node import ExecNode
|
||||
from .data_node import DataNode
|
||||
|
||||
class ActionState:
|
||||
def __init__(self):
|
||||
self.var = {"output_gate": "out"}
|
||||
|
||||
class Action(ExecNode, DataNode):
|
||||
def __init__(
|
||||
self,
|
||||
ports_exec_in: list[str],
|
||||
ports_exec_out: list[str],
|
||||
ports_data_in: list[str],
|
||||
ports_data_out: list[str],
|
||||
code: str = "",
|
||||
init: str = "",
|
||||
) -> None:
|
||||
self.gates: tuple[list[str], list[str], list[str], list[str]] = (ports_exec_in, ports_exec_out, ports_data_in, ports_data_out)
|
||||
super().__init__()
|
||||
self.state: dict[int, ActionState] = {}
|
||||
self.var_globals = {}
|
||||
self.code = code
|
||||
self.init = init
|
||||
|
||||
@override
|
||||
def get_exec_input_gates(self) -> list[str]:
|
||||
return self.gates[0]
|
||||
|
||||
@override
|
||||
def get_exec_output_gates(self) -> list[str]:
|
||||
return self.gates[1]
|
||||
|
||||
@override
|
||||
def get_data_input_gates(self) -> list[str]:
|
||||
return self.gates[2]
|
||||
|
||||
@override
|
||||
def get_data_output_gates(self) -> list[str]:
|
||||
return self.gates[3]
|
||||
|
||||
@override
|
||||
def nextState(self, exec_id: int) -> tuple["ExecNode", str]:
|
||||
state = self.get_state(exec_id)
|
||||
return self.next_node[state.var["output_gate"]]
|
||||
|
||||
def get_state(self, exec_id) -> ActionState:
|
||||
return self.state[exec_id]
|
||||
|
||||
@override
|
||||
def generate_stack_frame(self, exec_id: int) -> None:
|
||||
super().generate_stack_frame(exec_id)
|
||||
self.state[exec_id] = (state := ActionState())
|
||||
if self.init:
|
||||
exec (self.init, {"var": state.var}, {"globals": self.var_globals})
|
||||
@override
|
||||
def delete_stack_frame(self, exec_id: int) -> None:
|
||||
super().generate_stack_frame(exec_id)
|
||||
self.state.pop(exec_id)
|
||||
|
||||
def execute(self, port: str, exec_id: int, od: ODAPI) -> tuple[int, any] | None:
|
||||
state = self.get_state(exec_id)
|
||||
exec(
|
||||
self.code,
|
||||
{
|
||||
"api": od,
|
||||
"var": state.var,
|
||||
"data_in": {port: value.get_data(exec_id) for port, value in self.data_in.items() if value is not None},
|
||||
"data_out": {port: value.get_data(exec_id) for port, value in self.data_out.items() if value is not None},
|
||||
"globals": self.var_globals,
|
||||
},
|
||||
)
|
||||
for gate, d in self.data_out.items():
|
||||
DataNode.input_event(self, gate, exec_id)
|
||||
return None
|
||||
|
||||
def input_event(self, gate: str, exec_id: int) -> None:
|
||||
return
|
||||
|
||||
@not_visited
|
||||
def generate_dot(
|
||||
self, nodes: List[str], edges: List[str], visited: set[int], template: Template
|
||||
) -> None:
|
||||
generate_dot_node(
|
||||
self,
|
||||
nodes,
|
||||
template,
|
||||
**{
|
||||
"label": f"action",
|
||||
"ports_exec": (
|
||||
self.get_exec_input_gates(),
|
||||
self.get_exec_output_gates(),
|
||||
),
|
||||
"ports_data": (
|
||||
self.get_data_input_gates(),
|
||||
self.get_data_output_gates(),
|
||||
),
|
||||
},
|
||||
)
|
||||
ExecNode.generate_dot(self, nodes, edges, visited, template)
|
||||
DataNode.generate_dot(self, nodes, edges, visited, template)
|
||||
83
transformation/schedule/schedule_lib/data.py
Normal file
83
transformation/schedule/schedule_lib/data.py
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
from symtable import Class
|
||||
from typing import Any, Generator, Callable, Iterator, TYPE_CHECKING, override
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from transformation.schedule.schedule_lib import DataNode
|
||||
|
||||
|
||||
class DataState:
|
||||
def __init__(self, data: Any):
|
||||
self.data: list[dict[Any, Any]] = []
|
||||
|
||||
class Data:
|
||||
__slots__ = ("state", "_parent")
|
||||
|
||||
def __init__(self, parent: "DataNode") -> None:
|
||||
self.state: dict[int, DataState] = dict()
|
||||
self._parent = parent
|
||||
|
||||
def __dir__(self):
|
||||
return [attr for attr in super().__dir__() if attr != "_super"]
|
||||
|
||||
def get_data(self, exec_id: int) -> list[dict[str, str]]:
|
||||
state = self.get_state(exec_id)
|
||||
return state.data
|
||||
|
||||
def get_state(self, exec_id) -> DataState:
|
||||
return self.state[exec_id]
|
||||
|
||||
def store_data(self, exec_id: int, data_gen: Generator, n: int) -> bool:
|
||||
state = self.get_state(exec_id)
|
||||
state.data.clear()
|
||||
if n == 0:
|
||||
return True
|
||||
i: int = 0
|
||||
while (match := next(data_gen, None)) is not None:
|
||||
state.data.append(match)
|
||||
i += 1
|
||||
if i >= n:
|
||||
break
|
||||
else:
|
||||
if n == float("inf"):
|
||||
return bool(len(state.data))
|
||||
state.data.clear()
|
||||
return False
|
||||
return True
|
||||
|
||||
def get_parent(self) -> "DataNode":
|
||||
return self._parent
|
||||
|
||||
def replace(self, exec_id: int, data: list[dict[str, str]]) -> None:
|
||||
state = self.get_state(exec_id)
|
||||
state.data.clear()
|
||||
state.data.extend(data)
|
||||
|
||||
def append(self, exec_id: int, data: dict[str, str]) -> None:
|
||||
self.get_state(exec_id).data.append(data)
|
||||
|
||||
def extend(self, exec_id: int, data: list[dict[str, str]]) -> None:
|
||||
self.get_state(exec_id).data.extend(data)
|
||||
|
||||
def clear(self, exec_id: int) -> None:
|
||||
self.get_state(exec_id).data.clear()
|
||||
|
||||
def pop(self, exec_id: int, index: int =-1) -> Any:
|
||||
return self.get_state(exec_id).data.pop(index)
|
||||
|
||||
def empty(self, exec_id: int) -> bool:
|
||||
return len(self.get_state(exec_id).data) == 0
|
||||
|
||||
def __getitem__(self, index):
|
||||
raise NotImplementedError
|
||||
|
||||
def __iter__(self, exec_id: int) -> Iterator[dict[str, str]]:
|
||||
return self.get_state(exec_id).data.__iter__()
|
||||
|
||||
def __len__(self, exec_id: int) -> int:
|
||||
return self.get_state(exec_id).data.__len__()
|
||||
|
||||
def generate_stack_frame(self, exec_id: int) -> None:
|
||||
self.state[exec_id] = DataState(exec_id)
|
||||
|
||||
def delete_stack_frame(self, exec_id: int) -> None:
|
||||
self.state.pop(exec_id)
|
||||
101
transformation/schedule/schedule_lib/data_node.py
Normal file
101
transformation/schedule/schedule_lib/data_node.py
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
from abc import abstractmethod
|
||||
from typing import Any, Generator, List, override
|
||||
|
||||
from jinja2 import Template
|
||||
|
||||
from .data import Data
|
||||
from .funcs import generate_dot_edge
|
||||
from .node import Node
|
||||
|
||||
|
||||
class DataNodeState:
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
|
||||
|
||||
class DataNode(Node):
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self.eventsub: dict[str, list[tuple[DataNode, str]]] = {
|
||||
gate: [] for gate in self.get_data_output_gates()
|
||||
}
|
||||
self.data_out: dict[str, Data] = {
|
||||
name: Data(self) for name in self.get_data_output_gates()
|
||||
}
|
||||
self.data_in: dict[str, Data | None] = {
|
||||
name: None for name in self.get_data_input_gates()
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def get_data_input_gates() -> List[str]:
|
||||
return ["in"]
|
||||
|
||||
@staticmethod
|
||||
def get_data_output_gates() -> List[str]:
|
||||
return ["out"]
|
||||
|
||||
@override
|
||||
def generate_stack_frame(self, exec_id: int) -> None:
|
||||
super().generate_stack_frame(exec_id)
|
||||
for d in self.data_out.values():
|
||||
d.generate_stack_frame(exec_id)
|
||||
|
||||
@override
|
||||
def delete_stack_frame(self, exec_id: int) -> None:
|
||||
super().delete_stack_frame(exec_id)
|
||||
for d in self.data_out.values():
|
||||
d.delete_stack_frame(exec_id)
|
||||
|
||||
def connect_data(
|
||||
self, data_node: "DataNode", from_gate: str, to_gate: str, eventsub=True
|
||||
) -> None:
|
||||
if from_gate not in self.get_data_output_gates():
|
||||
raise Exception(f"from_gate {from_gate} is not a valid port")
|
||||
if to_gate not in data_node.get_data_input_gates():
|
||||
raise Exception(f"to_gate {to_gate} is not a valid port")
|
||||
data_node.data_in[to_gate] = self.data_out[from_gate]
|
||||
if eventsub:
|
||||
self.eventsub[from_gate].append((data_node, to_gate))
|
||||
|
||||
def store_data(self, exec_id, data_gen: Generator, port: str, n: int) -> None:
|
||||
self.data_out[port].store_data(exec_id, data_gen, n)
|
||||
for sub, gate in self.eventsub[port]:
|
||||
sub.input_event(gate, exec_id)
|
||||
|
||||
def get_input_data(self, gate: str, exec_id: int) -> list[dict[Any, Any]]:
|
||||
data = self.data_in[gate]
|
||||
if data is None:
|
||||
return [{}]
|
||||
return data.get_data(exec_id)
|
||||
|
||||
@abstractmethod
|
||||
def input_event(self, gate: str, exec_id: int) -> None:
|
||||
for sub, gate_sub in self.eventsub[gate]:
|
||||
sub.input_event(gate_sub, exec_id)
|
||||
|
||||
def generate_dot(
|
||||
self, nodes: List[str], edges: List[str], visited: set[int], template: Template
|
||||
) -> None:
|
||||
for port, data in self.data_in.items():
|
||||
if data is not None:
|
||||
source = data.get_parent()
|
||||
generate_dot_edge(
|
||||
source,
|
||||
self,
|
||||
edges,
|
||||
template,
|
||||
kwargs={
|
||||
"prefix": "d",
|
||||
"from_gate": [
|
||||
port
|
||||
for port, value in source.data_out.items()
|
||||
if value == data
|
||||
][0],
|
||||
"to_gate": port,
|
||||
"color": "green",
|
||||
},
|
||||
)
|
||||
data.get_parent().generate_dot(nodes, edges, visited, template)
|
||||
for gate_form, subs in self.eventsub.items():
|
||||
for sub, gate in subs:
|
||||
sub.generate_dot(nodes, edges, visited, template)
|
||||
80
transformation/schedule/schedule_lib/end.py
Normal file
80
transformation/schedule/schedule_lib/end.py
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
from typing import List, override, Type
|
||||
|
||||
from jinja2 import Template
|
||||
|
||||
from api.od import ODAPI
|
||||
from . import DataNode
|
||||
from .exec_node import ExecNode
|
||||
from .funcs import not_visited, generate_dot_node
|
||||
|
||||
class EndState:
|
||||
def __init__(self) -> None:
|
||||
self.end_gate: str = ""
|
||||
|
||||
class End(ExecNode, DataNode):
|
||||
@override
|
||||
def input_event(self, gate: str, exec_id: int) -> None:
|
||||
pass
|
||||
|
||||
def __init__(self, ports_exec: List[str], ports_data: List[str]) -> None:
|
||||
self.ports_exec = ports_exec
|
||||
self.ports_data = ports_data
|
||||
super().__init__()
|
||||
self.state: dict[int, EndState] = {}
|
||||
|
||||
@override
|
||||
def get_exec_input_gates(self):
|
||||
return self.ports_exec
|
||||
|
||||
@staticmethod
|
||||
@override
|
||||
def get_exec_output_gates():
|
||||
return []
|
||||
|
||||
@override
|
||||
def get_data_input_gates(self):
|
||||
return self.ports_data
|
||||
|
||||
@staticmethod
|
||||
@override
|
||||
def get_data_output_gates():
|
||||
return []
|
||||
|
||||
def execute(self, port: str, exec_id: int, od: ODAPI) -> tuple[int, any] | None:
|
||||
state = self.get_state(exec_id)
|
||||
state.end_gate = port
|
||||
return 1, {"exec_gate": state.end_gate, "data_out": {port: data.get_data(exec_id) for port, data in self.data_in.items()}}
|
||||
|
||||
def get_state(self, exec_id) -> EndState:
|
||||
return self.state[exec_id]
|
||||
|
||||
@override
|
||||
def generate_stack_frame(self, exec_id: int) -> None:
|
||||
super().generate_stack_frame(exec_id)
|
||||
self.state[exec_id] = EndState()
|
||||
|
||||
@override
|
||||
def delete_stack_frame(self, exec_id: int) -> None:
|
||||
super().delete_stack_frame(exec_id)
|
||||
self.state.pop(exec_id)
|
||||
|
||||
@not_visited
|
||||
def generate_dot(
|
||||
self, nodes: List[str], edges: List[str], visited: set[int], template: Template
|
||||
) -> None:
|
||||
generate_dot_node(
|
||||
self,
|
||||
nodes,
|
||||
template,
|
||||
**{
|
||||
"label": "end",
|
||||
"ports_exec": (
|
||||
self.get_exec_input_gates(),
|
||||
self.get_exec_output_gates(),
|
||||
),
|
||||
"ports_data": (
|
||||
self.get_data_input_gates(),
|
||||
self.get_data_output_gates(),
|
||||
),
|
||||
}
|
||||
)
|
||||
61
transformation/schedule/schedule_lib/exec_node.py
Normal file
61
transformation/schedule/schedule_lib/exec_node.py
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
from abc import abstractmethod
|
||||
from typing import override
|
||||
from jinja2 import Template
|
||||
|
||||
from api.od import ODAPI
|
||||
from .funcs import generate_dot_edge
|
||||
from .node import Node
|
||||
|
||||
|
||||
class ExecNode(Node):
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
|
||||
from .null_node import NullNode
|
||||
self.next_node: dict[str, tuple[ExecNode, str]] = {}
|
||||
for port in self.get_exec_output_gates():
|
||||
self.next_node[port] = (NullNode(), "in")
|
||||
|
||||
def nextState(self, exec_id: int) -> tuple["ExecNode", str]:
|
||||
return self.next_node["out"]
|
||||
|
||||
@staticmethod
|
||||
def get_exec_input_gates():
|
||||
return ["in"]
|
||||
|
||||
@staticmethod
|
||||
def get_exec_output_gates():
|
||||
return ["out"]
|
||||
|
||||
def connect(self, next_state: "ExecNode", from_gate: str, to_gate: str) -> None:
|
||||
if from_gate not in self.get_exec_output_gates():
|
||||
raise Exception(f"from_gate {from_gate} is not a valid port")
|
||||
if to_gate not in next_state.get_exec_input_gates():
|
||||
raise Exception(f"to_gate {to_gate} is not a valid port")
|
||||
self.next_node[from_gate] = (next_state, to_gate)
|
||||
|
||||
@abstractmethod
|
||||
def execute(self, port: str, exec_id: int, od: ODAPI) -> tuple[int, any] | None:
|
||||
return None
|
||||
|
||||
@override
|
||||
def generate_dot(
|
||||
self, nodes: list[str], edges: list[str], visited: set[int], template: Template
|
||||
) -> None:
|
||||
for out_port, edge in self.next_node.items():
|
||||
template.render()
|
||||
generate_dot_edge(
|
||||
self,
|
||||
edge[0],
|
||||
edges,
|
||||
template,
|
||||
kwargs={
|
||||
"prefix": "e",
|
||||
"from_gate": out_port,
|
||||
"to_gate": edge[1],
|
||||
"color": "darkblue",
|
||||
},
|
||||
)
|
||||
|
||||
for edge in self.next_node.values():
|
||||
edge[0].generate_dot(nodes, edges, visited, template)
|
||||
56
transformation/schedule/schedule_lib/funcs.py
Normal file
56
transformation/schedule/schedule_lib/funcs.py
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
from typing import Callable, List
|
||||
|
||||
from jinja2 import Template
|
||||
|
||||
from .singleton import Singleton
|
||||
|
||||
|
||||
class IdGenerator(metaclass=Singleton):
|
||||
exec_id = -1
|
||||
node_id = -1
|
||||
|
||||
@classmethod
|
||||
def generate_node_id(cls) -> int:
|
||||
cls.node_id +=1
|
||||
return cls.node_id
|
||||
|
||||
@classmethod
|
||||
def generate_exec_id(cls) -> int:
|
||||
cls.exec_id += 1
|
||||
return cls.exec_id
|
||||
|
||||
def generate_dot_wrap(func) -> Callable:
|
||||
def wrapper(self, *args, **kwargs) -> str:
|
||||
nodes = []
|
||||
edges = []
|
||||
self.reset_visited()
|
||||
func(self, nodes, edges, *args, **kwargs)
|
||||
return f"digraph G {{\n\t{"\n\t".join(nodes)}\n\t{"\n\t".join(edges)}\n}}"
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def not_visited(func) -> Callable:
|
||||
def wrapper(
|
||||
self, nodes: List[str], edges: List[str], visited: set[int], *args, **kwargs
|
||||
) -> None:
|
||||
if self in visited:
|
||||
return
|
||||
visited.add(self)
|
||||
func(self, nodes, edges, visited, *args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def generate_dot_node(self, nodes: List[str], template: Template, **kwargs) -> None:
|
||||
nodes.append(template.module.__getattribute__("Node")(**{**kwargs, "id": self.id}))
|
||||
|
||||
|
||||
def generate_dot_edge(
|
||||
self, target, edges: List[str], template: Template, kwargs
|
||||
) -> None:
|
||||
edges.append(
|
||||
template.module.__getattribute__("Edge")(
|
||||
**{**kwargs, "from_id": self.id, "to_id": target.id}
|
||||
)
|
||||
)
|
||||
74
transformation/schedule/schedule_lib/loop.py
Normal file
74
transformation/schedule/schedule_lib/loop.py
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
import functools
|
||||
from typing import List, Generator, override, Type
|
||||
|
||||
from jinja2 import Template
|
||||
|
||||
from api.od import ODAPI
|
||||
from .exec_node import ExecNode
|
||||
from .data_node import DataNode
|
||||
from .data_node import Data
|
||||
from .funcs import not_visited, generate_dot_node
|
||||
|
||||
class Loop(ExecNode, DataNode):
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self.cur_data: Data = Data(self)
|
||||
|
||||
@staticmethod
|
||||
@override
|
||||
def get_exec_output_gates():
|
||||
return ["it", "out"]
|
||||
|
||||
@override
|
||||
def generate_stack_frame(self, exec_id: int) -> None:
|
||||
super().generate_stack_frame(exec_id)
|
||||
self.cur_data.generate_stack_frame(exec_id)
|
||||
|
||||
@override
|
||||
def delete_stack_frame(self, exec_id: int) -> None:
|
||||
super().delete_stack_frame(exec_id)
|
||||
self.cur_data.delete_stack_frame(exec_id)
|
||||
|
||||
@override
|
||||
def nextState(self, exec_id: int) -> tuple[ExecNode, str]:
|
||||
return self.next_node["out" if self.data_out["out"].empty(exec_id) else "it"]
|
||||
|
||||
def execute(self, port: str, exec_id: int, od: ODAPI) -> tuple[int, any] | None:
|
||||
self.data_out["out"].clear(exec_id)
|
||||
|
||||
if not self.cur_data.empty(exec_id):
|
||||
self.data_out["out"].append(exec_id, self.cur_data.pop(exec_id,0))
|
||||
DataNode.input_event(self, "out", exec_id)
|
||||
return None
|
||||
|
||||
def input_event(self, gate: str, exec_id: int) -> None:
|
||||
self.cur_data.replace(exec_id, self.get_input_data(gate, exec_id))
|
||||
data_o = self.data_out["out"]
|
||||
if data_o.empty(exec_id):
|
||||
return
|
||||
data_o.clear(exec_id)
|
||||
DataNode.input_event(self, "out", exec_id)
|
||||
|
||||
|
||||
@not_visited
|
||||
def generate_dot(
|
||||
self, nodes: List[str], edges: List[str], visited: set[int], template: Template
|
||||
) -> None:
|
||||
generate_dot_node(
|
||||
self,
|
||||
nodes,
|
||||
template,
|
||||
**{
|
||||
"label": f"loop",
|
||||
"ports_exec": (
|
||||
self.get_exec_input_gates(),
|
||||
self.get_exec_output_gates(),
|
||||
),
|
||||
"ports_data": (
|
||||
self.get_data_input_gates(),
|
||||
self.get_data_output_gates(),
|
||||
),
|
||||
},
|
||||
)
|
||||
ExecNode.generate_dot(self, nodes, edges, visited, template)
|
||||
DataNode.generate_dot(self, nodes, edges, visited, template)
|
||||
67
transformation/schedule/schedule_lib/match.py
Normal file
67
transformation/schedule/schedule_lib/match.py
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
from typing import List, override, Type
|
||||
|
||||
from jinja2 import Template
|
||||
|
||||
from api.od import ODAPI
|
||||
from transformation.schedule.rule_executor import RuleExecutor
|
||||
from .exec_node import ExecNode
|
||||
from .data_node import DataNode
|
||||
from .funcs import not_visited, generate_dot_node
|
||||
|
||||
class Match(ExecNode, DataNode):
|
||||
def input_event(self, gate: str, exec_id: int) -> None:
|
||||
pass
|
||||
|
||||
def __init__(self, label: str, n: int | float) -> None:
|
||||
super().__init__()
|
||||
self.label: str = label
|
||||
self.n: int = n
|
||||
self.rule = None
|
||||
self.rule_executer: RuleExecutor | None = None
|
||||
|
||||
@override
|
||||
def nextState(self, exec_id: int) -> tuple[ExecNode, str]:
|
||||
return self.next_node["fail" if self.data_out["out"].empty(exec_id) else "success"]
|
||||
|
||||
@staticmethod
|
||||
@override
|
||||
def get_exec_output_gates():
|
||||
return ["success", "fail"]
|
||||
|
||||
def execute(self, port: str, exec_id: int, od: ODAPI) -> tuple[int, any] | None:
|
||||
pivot = {}
|
||||
if self.data_in is not None:
|
||||
pivot = self.get_input_data("in", exec_id)[0]
|
||||
# TODO: remove this print
|
||||
print(f"matching: {self.label}\n\tpivot: {pivot}")
|
||||
self.store_data( exec_id,
|
||||
self.rule_executer.match_rule(od.m, self.rule, pivot=pivot), "out", self.n
|
||||
)
|
||||
return None
|
||||
|
||||
def init_rule(self, rule, rule_executer):
|
||||
self.rule = rule
|
||||
self.rule_executer = rule_executer
|
||||
|
||||
@not_visited
|
||||
def generate_dot(
|
||||
self, nodes: List[str], edges: List[str], visited: set[int], template: Template
|
||||
) -> None:
|
||||
generate_dot_node(
|
||||
self,
|
||||
nodes,
|
||||
template,
|
||||
**{
|
||||
"label": f"match\n{self.label}\nn = {self.n}",
|
||||
"ports_exec": (
|
||||
self.get_exec_input_gates(),
|
||||
self.get_exec_output_gates(),
|
||||
),
|
||||
"ports_data": (
|
||||
self.get_data_input_gates(),
|
||||
self.get_data_output_gates(),
|
||||
),
|
||||
},
|
||||
)
|
||||
ExecNode.generate_dot(self, nodes, edges, visited, template)
|
||||
DataNode.generate_dot(self, nodes, edges, visited, template)
|
||||
57
transformation/schedule/schedule_lib/merge.py
Normal file
57
transformation/schedule/schedule_lib/merge.py
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
from typing import List, override, Type
|
||||
|
||||
from jinja2 import Template
|
||||
|
||||
from api.od import ODAPI
|
||||
from transformation.schedule.rule_executor import RuleExecutor
|
||||
from . import ExecNode
|
||||
from .exec_node import ExecNode
|
||||
from .data_node import DataNode, DataNodeState
|
||||
from .funcs import not_visited, generate_dot_node
|
||||
|
||||
class Merge(DataNode):
|
||||
def __init__(self, ports: list[str]) -> None:
|
||||
self.in_data_ports = ports # ports must be defined before super.__init__
|
||||
super().__init__()
|
||||
self.in_data_ports.reverse()
|
||||
|
||||
@override
|
||||
def get_data_input_gates(self) -> list[str]:
|
||||
return self.in_data_ports
|
||||
|
||||
@override
|
||||
def input_event(self, gate: str, exec_id: int) -> None:
|
||||
out = self.data_out["out"]
|
||||
b = (not out.empty(exec_id)) and (self.data_in[gate].empty(exec_id))
|
||||
out.clear(exec_id)
|
||||
if b:
|
||||
DataNode.input_event(self, "out", exec_id)
|
||||
return
|
||||
|
||||
# TODO: only first element or all?
|
||||
if any(data.empty(exec_id) for data in self.data_in.values()):
|
||||
return
|
||||
d: dict[str, str] = dict()
|
||||
for gate in self.in_data_ports:
|
||||
for key, value in self.data_in[gate].get_data(exec_id)[0].items():
|
||||
d[key] = value
|
||||
out.append(exec_id, d)
|
||||
DataNode.input_event(self, "out", exec_id)
|
||||
|
||||
@not_visited
|
||||
def generate_dot(
|
||||
self, nodes: List[str], edges: List[str], visited: set[int], template: Template
|
||||
) -> None:
|
||||
generate_dot_node(
|
||||
self,
|
||||
nodes,
|
||||
template,
|
||||
**{
|
||||
"label": f"merge",
|
||||
"ports_data": (
|
||||
self.get_data_input_gates()[::-1],
|
||||
self.get_data_output_gates(),
|
||||
),
|
||||
},
|
||||
)
|
||||
DataNode.generate_dot(self, nodes, edges, visited, template)
|
||||
49
transformation/schedule/schedule_lib/modify.py
Normal file
49
transformation/schedule/schedule_lib/modify.py
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
from typing import List, override
|
||||
|
||||
from jinja2 import Template
|
||||
|
||||
from transformation.schedule.schedule_lib.funcs import not_visited, generate_dot_node
|
||||
from .data_node import DataNode
|
||||
|
||||
|
||||
class Modify(DataNode):
|
||||
def __init__(self, rename: dict[str, str], delete: dict[str, str]) -> None:
|
||||
super().__init__()
|
||||
self.rename: dict[str, str] = rename
|
||||
self.delete: set[str] = set(delete)
|
||||
|
||||
@override
|
||||
def input_event(self, gate: str, exec_id: int) -> None:
|
||||
data_i = self.get_input_data(gate, exec_id)
|
||||
if len(data_i):
|
||||
self.data_out["out"].clear(exec_id)
|
||||
for data in data_i:
|
||||
self.data_out["out"].append(exec_id,
|
||||
{
|
||||
self.rename.get(key, key): value
|
||||
for key, value in data.items()
|
||||
if key not in self.delete
|
||||
}
|
||||
)
|
||||
else:
|
||||
if self.data_out["out"].empty(exec_id):
|
||||
return
|
||||
super().input_event("out", exec_id)
|
||||
|
||||
@not_visited
|
||||
def generate_dot(
|
||||
self, nodes: List[str], edges: List[str], visited: set[int], template: Template
|
||||
) -> None:
|
||||
generate_dot_node(
|
||||
self,
|
||||
nodes,
|
||||
template,
|
||||
**{
|
||||
"label": f"modify",
|
||||
"ports_data": (
|
||||
self.get_data_input_gates(),
|
||||
self.get_data_output_gates(),
|
||||
),
|
||||
},
|
||||
)
|
||||
DataNode.generate_dot(self, nodes, edges, visited, template)
|
||||
70
transformation/schedule/schedule_lib/node.py
Normal file
70
transformation/schedule/schedule_lib/node.py
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
"""
|
||||
node.py
|
||||
|
||||
Defines the abstract base Node class for graph-based structures. Each Node is assigned
|
||||
a unique identifier via an external IdGenerator. The class provides an interface for
|
||||
managing execution state and generating DOT graph representations.
|
||||
"""
|
||||
|
||||
from abc import abstractmethod
|
||||
from jinja2 import Template
|
||||
from .funcs import IdGenerator
|
||||
|
||||
|
||||
class Node:
|
||||
"""
|
||||
Abstract base class for graph nodes. Each Node has a unique ID and supports
|
||||
context-dependent state management for execution scenarios. Subclasses must
|
||||
implement the DOT graph generation logic.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def __init__(self) -> None:
|
||||
"""
|
||||
Initializes the Node instance with a unique ID.
|
||||
|
||||
Attributes:
|
||||
id (int): A unique identifier assigned by IdGenerator.
|
||||
"""
|
||||
self.id: int = IdGenerator.generate_node_id()
|
||||
|
||||
def get_id(self) -> int:
|
||||
"""
|
||||
Retrieves the unique identifier of the node.
|
||||
|
||||
Returns:
|
||||
int: The unique node ID.
|
||||
"""
|
||||
return self.id
|
||||
|
||||
def generate_stack_frame(self, exec_id: int) -> None:
|
||||
"""
|
||||
Initializes a new state frame for a specific execution context.
|
||||
Designed to be overridden in subclasses that use execution state.
|
||||
|
||||
Args:
|
||||
exec_id (int): The ID of the execution context.
|
||||
"""
|
||||
|
||||
def delete_stack_frame(self, exec_id: int) -> None:
|
||||
"""
|
||||
Deletes the state frame for a specific execution context.
|
||||
Designed to be overridden in subclasses that use execution state.
|
||||
|
||||
Args:
|
||||
exec_id (int): The ID of the execution context.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def generate_dot(
|
||||
self, nodes: list[str], edges: list[str], visited: set[int], template: Template
|
||||
) -> None:
|
||||
"""
|
||||
Generates the DOT graph representation for this node and its relationships.
|
||||
|
||||
Args:
|
||||
nodes (list[str]): A list to append DOT node definitions to.
|
||||
edges (list[str]): A list to append DOT edge definitions to.
|
||||
visited (set[int]): A set of already visited node IDs to avoid duplicates or recursion.
|
||||
template (Template): A Jinja2 template used to format the node's DOT representation.
|
||||
"""
|
||||
80
transformation/schedule/schedule_lib/null_node.py
Normal file
80
transformation/schedule/schedule_lib/null_node.py
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
"""
|
||||
null_node.py
|
||||
|
||||
Defines the NullNode class, a no-op singleton execution node used for open execution pins
|
||||
in the object diagram execution graph.
|
||||
"""
|
||||
|
||||
from abc import ABC
|
||||
from typing import List, Type
|
||||
from jinja2 import Template
|
||||
from api.od import ODAPI
|
||||
from .funcs import generate_dot_node
|
||||
from .singleton import Singleton
|
||||
from .exec_node import ExecNode
|
||||
|
||||
class NullNode(ExecNode, metaclass=Singleton):
|
||||
"""
|
||||
A no-op execution node representing a null operation.
|
||||
|
||||
This node is typically used to represent a placeholder or open execution pin.
|
||||
It always returns a fixed result and does not perform any operation.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
Initializes the NullNode instance.
|
||||
Inherits unique ID and state behavior from ExecNode.
|
||||
"""
|
||||
super().__init__()
|
||||
|
||||
def execute(self, port: str, exec_id: int, od: ODAPI) -> tuple[int, any] | None:
|
||||
"""
|
||||
Simulates execution by returning a static result indicating an open pin.
|
||||
|
||||
Args:
|
||||
port (str): The name of the input port.
|
||||
exec_id (int): The current execution ID.
|
||||
od (ODAPI): The Object Diagram API instance providing execution context.
|
||||
|
||||
Returns:
|
||||
tuple[int, str] | None: A tuple (-1, "open pin reached") indicating a no-op.
|
||||
"""
|
||||
return -1, "open pin reached"
|
||||
|
||||
@staticmethod
|
||||
def get_exec_output_gates():
|
||||
"""
|
||||
Returns the list of output gates for execution.
|
||||
|
||||
Returns:
|
||||
list: An empty list, as NullNode has no output gates.
|
||||
"""
|
||||
return []
|
||||
|
||||
def generate_dot(
|
||||
self, nodes: List[str], edges: List[str], visited: set[int], template: Template
|
||||
) -> None:
|
||||
"""
|
||||
Generates DOT graph representation for this node if it hasn't been visited.
|
||||
|
||||
Args:
|
||||
nodes (List[str]): A list to accumulate DOT node definitions.
|
||||
edges (List[str]): A list to accumulate DOT edge definitions.
|
||||
visited (set[int]): Set of already visited node IDs to avoid cycles.
|
||||
template (Template): A Jinja2 template used to render the node's DOT representation.
|
||||
"""
|
||||
if self.id in visited:
|
||||
return
|
||||
generate_dot_node(
|
||||
self,
|
||||
nodes,
|
||||
template,
|
||||
**{
|
||||
"label": "null",
|
||||
"ports_exec": (
|
||||
self.get_exec_input_gates(),
|
||||
self.get_exec_output_gates(),
|
||||
),
|
||||
}
|
||||
)
|
||||
60
transformation/schedule/schedule_lib/print.py
Normal file
60
transformation/schedule/schedule_lib/print.py
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
from typing import List, override
|
||||
|
||||
from jinja2 import Template
|
||||
|
||||
from api.od import ODAPI
|
||||
from transformation.schedule.schedule_lib.funcs import not_visited, generate_dot_node
|
||||
from .exec_node import ExecNode
|
||||
from .data_node import DataNode
|
||||
|
||||
|
||||
class Print(ExecNode, DataNode):
|
||||
def __init__(self, label: str = "", custom: str = "") -> None:
|
||||
super().__init__()
|
||||
self.label = label
|
||||
|
||||
if custom:
|
||||
template = Template(custom, trim_blocks=True, lstrip_blocks=True)
|
||||
self._print = (
|
||||
lambda self_, exec_id: print(template.render(data=self.get_input_data("in", exec_id)))
|
||||
).__get__(self, Print)
|
||||
|
||||
@staticmethod
|
||||
@override
|
||||
def get_data_output_gates():
|
||||
return []
|
||||
|
||||
def execute(self, port: str, exec_id: int, od: ODAPI) -> tuple[int, any] | None:
|
||||
self._print(exec_id)
|
||||
return
|
||||
|
||||
@override
|
||||
def input_event(self, gate: str, exec_id: int) -> None:
|
||||
if not self.data_in[gate].empty(exec_id):
|
||||
self._print(exec_id)
|
||||
|
||||
def _print(self, exec_id: int) -> None:
|
||||
print(f"{self.label}{self.get_input_data("in", exec_id)}")
|
||||
|
||||
@not_visited
|
||||
def generate_dot(
|
||||
self, nodes: List[str], edges: List[str], visited: set[int], template: Template
|
||||
) -> None:
|
||||
generate_dot_node(
|
||||
self,
|
||||
nodes,
|
||||
template,
|
||||
**{
|
||||
"label": f"print",
|
||||
"ports_exec": (
|
||||
self.get_exec_input_gates(),
|
||||
self.get_exec_output_gates(),
|
||||
),
|
||||
"ports_data": (
|
||||
self.get_data_input_gates(),
|
||||
self.get_data_output_gates(),
|
||||
),
|
||||
},
|
||||
)
|
||||
ExecNode.generate_dot(self, nodes, edges, visited, template)
|
||||
DataNode.generate_dot(self, nodes, edges, visited, template)
|
||||
56
transformation/schedule/schedule_lib/rewrite.py
Normal file
56
transformation/schedule/schedule_lib/rewrite.py
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
import functools
|
||||
from typing import List, Type
|
||||
|
||||
from jinja2 import Template
|
||||
|
||||
from api.od import ODAPI
|
||||
from .exec_node import ExecNode
|
||||
from .data_node import DataNode
|
||||
from .funcs import not_visited, generate_dot_node
|
||||
from ..rule_executor import RuleExecutor
|
||||
|
||||
class Rewrite(ExecNode, DataNode):
|
||||
|
||||
def __init__(self, label: str) -> None:
|
||||
super().__init__()
|
||||
self.label = label
|
||||
self.rule = None
|
||||
self.rule_executor: RuleExecutor | None = None
|
||||
|
||||
def init_rule(self, rule, rule_executer):
|
||||
self.rule = rule
|
||||
self.rule_executor = rule_executer
|
||||
|
||||
def execute(self, port: str, exec_id: int, od: ODAPI) -> tuple[int, any] | None:
|
||||
pivot = {}
|
||||
if self.data_in is not None:
|
||||
pivot = self.get_input_data("in", exec_id)[0]
|
||||
# TODO: remove print
|
||||
print(f"rewrite: {self.label}\n\tpivot: {pivot}")
|
||||
self.store_data( exec_id,
|
||||
self.rule_executor.rewrite_rule(od, self.rule, pivot=pivot), "out", 1
|
||||
)
|
||||
return None
|
||||
|
||||
@not_visited
|
||||
def generate_dot(
|
||||
self, nodes: List[str], edges: List[str], visited: set[int], template: Template
|
||||
) -> None:
|
||||
generate_dot_node(
|
||||
self,
|
||||
nodes,
|
||||
template,
|
||||
**{
|
||||
"label": f"rewrite\n{self.label}",
|
||||
"ports_exec": (
|
||||
self.get_exec_input_gates(),
|
||||
self.get_exec_output_gates(),
|
||||
),
|
||||
"ports_data": (
|
||||
self.get_data_input_gates(),
|
||||
self.get_data_output_gates(),
|
||||
),
|
||||
},
|
||||
)
|
||||
ExecNode.generate_dot(self, nodes, edges, visited, template)
|
||||
DataNode.generate_dot(self, nodes, edges, visited, template)
|
||||
9
transformation/schedule/schedule_lib/singleton.py
Normal file
9
transformation/schedule/schedule_lib/singleton.py
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
from abc import ABCMeta
|
||||
|
||||
class Singleton(ABCMeta):
|
||||
_instances = {}
|
||||
|
||||
def __call__(cls, *args, **kwargs):
|
||||
if cls not in cls._instances:
|
||||
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
|
||||
return cls._instances[cls]
|
||||
83
transformation/schedule/schedule_lib/start.py
Normal file
83
transformation/schedule/schedule_lib/start.py
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
from typing import List, override
|
||||
|
||||
from jinja2 import Template
|
||||
|
||||
from . import DataNode
|
||||
from .exec_node import ExecNode
|
||||
from .funcs import not_visited, generate_dot_node
|
||||
|
||||
class StartState:
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self.start_gate: str = ""
|
||||
|
||||
class Start(ExecNode, DataNode):
|
||||
def __init__(self, ports_exec: List[str], ports_data: List[str]) -> None:
|
||||
self.state: dict[int, StartState] = {}
|
||||
self.ports_exec = ports_exec
|
||||
self.ports_data = ports_data
|
||||
super().__init__()
|
||||
|
||||
def run_init(self, gate: str, exec_id: int, data: dict[str, any]) -> None:
|
||||
state = self.get_state(exec_id)
|
||||
state.start_gate = gate
|
||||
for port, d in data.items():
|
||||
self.data_out[port].replace(exec_id, d)
|
||||
DataNode.input_event(self, port, exec_id)
|
||||
|
||||
def nextState(self, exec_id: int) -> tuple["ExecNode", str]:
|
||||
state = self.get_state(exec_id)
|
||||
return self.next_node[state.start_gate]
|
||||
|
||||
def get_state(self, exec_id) -> StartState:
|
||||
return self.state[exec_id]
|
||||
|
||||
@override
|
||||
def generate_stack_frame(self, exec_id: int) -> None:
|
||||
super().generate_stack_frame(exec_id)
|
||||
self.state[exec_id] = StartState()
|
||||
|
||||
@override
|
||||
def delete_stack_frame(self, exec_id: int) -> None:
|
||||
super().generate_stack_frame(exec_id)
|
||||
self.state.pop(exec_id)
|
||||
|
||||
@staticmethod
|
||||
@override
|
||||
def get_exec_input_gates():
|
||||
return []
|
||||
|
||||
@override
|
||||
def get_exec_output_gates(self):
|
||||
return self.ports_exec
|
||||
|
||||
@staticmethod
|
||||
@override
|
||||
def get_data_input_gates():
|
||||
return []
|
||||
|
||||
@override
|
||||
def get_data_output_gates(self):
|
||||
return self.ports_data
|
||||
|
||||
@not_visited
|
||||
def generate_dot(
|
||||
self, nodes: List[str], edges: List[str], visited: set[int], template: Template
|
||||
) -> None:
|
||||
generate_dot_node(
|
||||
self,
|
||||
nodes,
|
||||
template,
|
||||
**{
|
||||
"label": "start",
|
||||
"ports_exec": (
|
||||
self.get_exec_input_gates(),
|
||||
self.get_exec_output_gates(),
|
||||
),
|
||||
"ports_data": (
|
||||
self.get_data_input_gates(),
|
||||
self.get_data_output_gates(),
|
||||
),
|
||||
}
|
||||
)
|
||||
super().generate_dot(nodes, edges, visited, template)
|
||||
92
transformation/schedule/schedule_lib/store.py
Normal file
92
transformation/schedule/schedule_lib/store.py
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
from typing import List, override
|
||||
|
||||
from jinja2 import Template
|
||||
|
||||
from api.od import ODAPI
|
||||
from .data import Data
|
||||
from .exec_node import ExecNode
|
||||
from .data_node import DataNode
|
||||
from .funcs import not_visited, generate_dot_node
|
||||
|
||||
class StoreState:
|
||||
def __init__(self) -> None:
|
||||
self.last_port: str = "in"
|
||||
|
||||
class Store(ExecNode, DataNode):
|
||||
def __init__(self, ports: list[str]) -> None:
|
||||
self.ports = ports
|
||||
super().__init__()
|
||||
self.state: dict[int, StoreState] = {}
|
||||
self.cur_data: Data = Data(self)
|
||||
|
||||
@override
|
||||
def get_exec_input_gates(self) -> list[str]:
|
||||
return [*self.ports, "in"]
|
||||
|
||||
@override
|
||||
def get_exec_output_gates(self) -> list[str]:
|
||||
return [*self.ports, "out"]
|
||||
|
||||
@override
|
||||
def get_data_input_gates(self) -> list[str]:
|
||||
return self.ports
|
||||
|
||||
@override
|
||||
def nextState(self, exec_id: int) -> tuple[ExecNode, str]:
|
||||
return self.next_node[self.get_state(exec_id).last_port]
|
||||
|
||||
@override
|
||||
def input_event(self, gate: str, exec_id: int) -> None:
|
||||
return
|
||||
|
||||
def get_state(self, exec_id) -> StoreState:
|
||||
return self.state[exec_id]
|
||||
|
||||
@override
|
||||
def generate_stack_frame(self, exec_id: int) -> None:
|
||||
super().generate_stack_frame(exec_id)
|
||||
self.state[exec_id] = StoreState()
|
||||
self.cur_data.generate_stack_frame(exec_id)
|
||||
|
||||
@override
|
||||
def delete_stack_frame(self, exec_id: int) -> None:
|
||||
super().generate_stack_frame(exec_id)
|
||||
self.state.pop(exec_id)
|
||||
self.cur_data.delete_stack_frame(exec_id)
|
||||
|
||||
|
||||
@override
|
||||
def execute(self, port: str, exec_id: int, od: ODAPI) -> tuple[int, any] | None:
|
||||
state = self.get_state(exec_id)
|
||||
if port == "in":
|
||||
self.data_out["out"].replace(exec_id, self.cur_data.get_data(exec_id))
|
||||
self.cur_data.clear(exec_id)
|
||||
DataNode.input_event(self, "out", True)
|
||||
state.last_port = "out"
|
||||
return None
|
||||
self.cur_data.extend(exec_id, self.get_input_data(port, exec_id))
|
||||
state.last_port = port
|
||||
return None
|
||||
|
||||
@not_visited
|
||||
def generate_dot(
|
||||
self, nodes: List[str], edges: List[str], visited: set[int], template: Template
|
||||
) -> None:
|
||||
generate_dot_node(
|
||||
self,
|
||||
nodes,
|
||||
template,
|
||||
**{
|
||||
"label": f"store",
|
||||
"ports_exec": (
|
||||
self.get_exec_input_gates(),
|
||||
self.get_exec_output_gates(),
|
||||
),
|
||||
"ports_data": (
|
||||
self.get_data_input_gates(),
|
||||
self.get_data_output_gates(),
|
||||
),
|
||||
},
|
||||
)
|
||||
ExecNode.generate_dot(self, nodes, edges, visited, template)
|
||||
DataNode.generate_dot(self, nodes, edges, visited, template)
|
||||
107
transformation/schedule/schedule_lib/sub_schedule.py
Normal file
107
transformation/schedule/schedule_lib/sub_schedule.py
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
from typing import List, override, TYPE_CHECKING
|
||||
|
||||
from jinja2 import Template
|
||||
|
||||
from api.od import ODAPI
|
||||
from . import DataNode
|
||||
from .exec_node import ExecNode
|
||||
from .funcs import not_visited, generate_dot_node, IdGenerator
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..rule_scheduler import RuleScheduler
|
||||
|
||||
|
||||
class ScheduleState:
|
||||
def __init__(self) -> None:
|
||||
self.end_gate: str = ""
|
||||
|
||||
class SubSchedule(ExecNode, DataNode):
|
||||
def __init__(self, scheduler: "RuleScheduler", file: str) -> None:
|
||||
self.schedule = scheduler._load_schedule(file, _main=False)
|
||||
self.scheduler = scheduler
|
||||
super().__init__()
|
||||
self.state: dict[int, ScheduleState] = {}
|
||||
|
||||
@override
|
||||
def nextState(self, exec_id: int) -> tuple["ExecNode", str]:
|
||||
return self.next_node[self.get_state(exec_id).end_gate]
|
||||
|
||||
@override
|
||||
def get_exec_input_gates(self) -> "List[ExecNode]":
|
||||
return self.schedule.start.get_exec_output_gates()
|
||||
|
||||
@override
|
||||
def get_exec_output_gates(self) -> "List[ExecNode]":
|
||||
return [*self.schedule.end.get_exec_input_gates()]
|
||||
|
||||
@override
|
||||
def get_data_input_gates(self) -> "List[ExecNode]":
|
||||
return self.schedule.start.get_data_output_gates()
|
||||
|
||||
@override
|
||||
def get_data_output_gates(self) -> "List[ExecNode]":
|
||||
return self.schedule.end.get_data_input_gates()
|
||||
|
||||
def get_state(self, exec_id) -> ScheduleState:
|
||||
return self.state[exec_id]
|
||||
|
||||
@override
|
||||
def generate_stack_frame(self, exec_id: int) -> None:
|
||||
super().generate_stack_frame(exec_id)
|
||||
self.state[exec_id] = ScheduleState()
|
||||
|
||||
@override
|
||||
def delete_stack_frame(self, exec_id: int) -> None:
|
||||
super().delete_stack_frame(exec_id)
|
||||
self.state.pop(exec_id)
|
||||
|
||||
|
||||
@override
|
||||
def execute(self, port: str, exec_id: int, od: ODAPI) -> tuple[int, any] | None:
|
||||
runstatus, result = self.scheduler._runner(
|
||||
od,
|
||||
self.schedule,
|
||||
port,
|
||||
IdGenerator.generate_exec_id(),
|
||||
{
|
||||
port: self.get_input_data(port, exec_id)
|
||||
for port, value in self.data_in.items()
|
||||
if value is not None and not value.empty(exec_id)
|
||||
},
|
||||
)
|
||||
if runstatus != 1:
|
||||
return runstatus, result
|
||||
self.get_state(exec_id).end_gate = result["exec_gate"]
|
||||
results_data = result["data_out"]
|
||||
for port, data in self.data_out.items():
|
||||
if port in results_data:
|
||||
self.data_out[port].replace(exec_id, results_data[port])
|
||||
DataNode.input_event(self, port, exec_id)
|
||||
continue
|
||||
|
||||
if not data.empty(exec_id):
|
||||
data.clear(exec_id)
|
||||
DataNode.input_event(self, port, exec_id)
|
||||
return None
|
||||
|
||||
@not_visited
|
||||
def generate_dot(
|
||||
self, nodes: List[str], edges: List[str], visited: set[int], template: Template
|
||||
) -> None:
|
||||
generate_dot_node(
|
||||
self,
|
||||
nodes,
|
||||
template,
|
||||
**{
|
||||
"label": "rrrrrrrrrr",
|
||||
"ports_exec": (
|
||||
self.get_exec_input_gates(),
|
||||
self.get_exec_output_gates(),
|
||||
),
|
||||
"ports_data": (
|
||||
self.get_data_input_gates(),
|
||||
self.get_data_output_gates(),
|
||||
),
|
||||
}
|
||||
)
|
||||
super().generate_dot(nodes, edges, visited, template)
|
||||
65
transformation/schedule/templates/schedule_dot.j2
Normal file
65
transformation/schedule/templates/schedule_dot.j2
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
digraph G {
|
||||
rankdir=LR;
|
||||
compound=true;
|
||||
node [shape=rect];
|
||||
{% for node in nodes %}
|
||||
{{ node }}
|
||||
{% endfor %}
|
||||
|
||||
{% for edge in edges %}
|
||||
{{ edge }}
|
||||
{% endfor %}
|
||||
}
|
||||
|
||||
{% macro Node(label, id, ports_exec=[], ports_data=[], debug = False) %}
|
||||
subgraph cluster_{{ id }} {
|
||||
label = "
|
||||
{%- if debug %}
|
||||
{{ id }}_
|
||||
{%- endif -%}
|
||||
{{ label }}"
|
||||
|
||||
style = rounded;
|
||||
input_{{ id }} [
|
||||
shape=rect;
|
||||
label= {{ Gate_Table(ports_exec[0], ports_data[0]) }}
|
||||
];
|
||||
output_{{ id }} [
|
||||
shape=rect;
|
||||
label= {{ Gate_Table(ports_exec[1], ports_data[1]) }}
|
||||
];
|
||||
input_{{ id }}->output_{{ id }} [style=invis];
|
||||
}
|
||||
{%- endmacro %}
|
||||
|
||||
{%- macro Edge(from_id, to_id, from_gate, to_gate, prefix, color) %}
|
||||
output_{{ from_id }}:{{ prefix }}_{{ from_gate }} -> input_{{ to_id }}:{{ prefix }}_{{ to_gate }} [color = {{ color }}]
|
||||
{%- endmacro %}
|
||||
|
||||
{%- macro Gate_Table(ports_exec, ports_data) %}
|
||||
|
||||
<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0">
|
||||
{% if ports_exec or ports_data %}
|
||||
{% if ports_exec %}
|
||||
<TR><TD>
|
||||
<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" COLOR="darkblue">
|
||||
{% for port_e in ports_exec %}
|
||||
<TR><TD PORT="e_{{ port_e }}">{{ port_e }}</TD></TR>
|
||||
{% endfor %}
|
||||
</TABLE>
|
||||
</TD></TR>
|
||||
{% endif %}
|
||||
{% if ports_data %}
|
||||
<TR><TD>
|
||||
<TABLE BORDER="1" CELLBORDER="1" CELLSPACING="0" COLOR="green">
|
||||
{% for port_d in ports_data %}
|
||||
<TR><TD PORT="d_{{ port_d }}">{{ port_d }}</TD></TR>
|
||||
{% endfor %}
|
||||
</TABLE>
|
||||
</TD></TR>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<TR><TD> </TD></TR>
|
||||
{% endif %}
|
||||
</TABLE>>
|
||||
{%- endmacro %}
|
||||
28
transformation/schedule/templates/schedule_muMLE.j2
Normal file
28
transformation/schedule/templates/schedule_muMLE.j2
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
{% for id, param in nodes.items() -%}
|
||||
{{ param[0] }}:{{ param[1].pop("type") }}
|
||||
{%- if param[1] %}
|
||||
{
|
||||
{% for key, value in param[1].items() %}
|
||||
{% if value %}
|
||||
{% if key in ["file"] %}
|
||||
{% set value = '"' ~ value ~ '"' %}
|
||||
{% elif key in ["custom"] %}
|
||||
{% set value = '`"' ~ value.replace('\n', '\\n') ~ '"`' %}
|
||||
{% elif key in ["action", "init"] %}
|
||||
{% set value = '\n```\n' ~ value ~ '\n```' %}
|
||||
{% elif key in ["ports", "ports_exec_in", "ports_exec_out", "ports_data_in", "ports_data_out", "rename", "delete"] %}
|
||||
{% set value = '`' ~ value.replace('\n', '\\n') ~ '`' %}
|
||||
{% endif %}
|
||||
{{ key }} = {{ value }};
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
{% endfor %}
|
||||
|
||||
{%- for edge in edges %}
|
||||
{% set source = edge[0] %}
|
||||
{% set target = edge[1] %}
|
||||
:Conn_{{ source[2] }} ({{ source[0] }} -> {{ target[0] }}) {from="{{ source[1] }}"; to="{{ target[1] }}";}
|
||||
{% endfor -%}
|
||||
51
transformation/schedule/templates/schedule_template.j2
Normal file
51
transformation/schedule/templates/schedule_template.j2
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
{% macro Start(name, ports_exec_out, ports_data_out) %}
|
||||
{{ name }} = Start({{ ports_exec_out }}, {{ ports_data_out }})
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro End(name, ports_exec_in, ports_data_in) %}
|
||||
{{ name }} = End({{ ports_exec_in }}, {{ ports_data_in }})
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro Match(name, file, n) %}
|
||||
{{ name }} = Match("{{ file }}", {{ n }})
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro Rewrite(name, file) %}
|
||||
{{ name }} = Rewrite("{{ file }}")
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro Action(name, ports_exec_in, ports_exec_out, ports_data_in, ports_data_out, action, init) %}
|
||||
{{ name }} = Action({{ ports_exec_in }}, {{ ports_exec_out }}, {{ ports_data_in }}, {{ ports_data_out }}, {{ action }}, {{ init }})
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro Modify(name, rename, delete) %}
|
||||
{{ name }} = Modify({{ rename }}, {{ delete }})
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro Merge(name, ports_data_in) %}
|
||||
{{ name }} = Merge({{ ports_data_in }})
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro Store(name, ports) %}
|
||||
{{ name }} = Store({{ ports }})
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro Schedule(name, file) %}
|
||||
{{ name }} = SubSchedule(scheduler, "{{ file }}")
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro Loop(name) %}
|
||||
{{ name }} = Loop()
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro Print(name, label, custom) %}
|
||||
{{ name }} = Print("{{ label }}", {{ custom }})
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro Conn_exec(name_from, name_to, from, to) %}
|
||||
{{ name_from }}.connect({{ name_to }},"{{ from }}","{{ to }}")
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro Conn_data(name_from, name_to, from, to, event) %}
|
||||
{{ name_from }}.connect_data({{ name_to }}, "{{ from }}", "{{ to }}", {{ event }})
|
||||
{%- endmacro %}
|
||||
48
transformation/schedule/templates/schedule_template_wrap.j2
Normal file
48
transformation/schedule/templates/schedule_template_wrap.j2
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
#generated from somewhere i do not now but it here so live with it
|
||||
|
||||
from transformation.schedule.schedule_lib import *
|
||||
|
||||
class Schedule:
|
||||
def __init__(self):
|
||||
self.start: Start | None = None
|
||||
self.end: End | None = None
|
||||
self.nodes: list[DataNode] = []
|
||||
|
||||
@staticmethod
|
||||
def get_matchers():
|
||||
return [
|
||||
{% for file in match_files %}
|
||||
"{{ file }}",
|
||||
{% endfor %}
|
||||
]
|
||||
|
||||
def init_schedule(self, scheduler, rule_executer, matchers):
|
||||
{% for block in blocks_start_end%}
|
||||
{{ block }}
|
||||
{% endfor %}
|
||||
self.start = {{ start }}
|
||||
self.end = {{ end }}
|
||||
{% for block in blocks%}
|
||||
{{ block }}
|
||||
{% endfor %}
|
||||
|
||||
{% for conn in exec_conn%}
|
||||
{{ conn }}
|
||||
{% endfor %}
|
||||
{% for conn_d in data_conn%}
|
||||
{{ conn_d }}
|
||||
{% endfor %}
|
||||
|
||||
{% for match in matchers %}
|
||||
{{ match["name"] }}.init_rule(matchers["{{ match["file"] }}"], rule_executer)
|
||||
{% endfor %}
|
||||
|
||||
self.nodes = [
|
||||
{% for name in blocks_name%}
|
||||
{{ name }},
|
||||
{% endfor %}
|
||||
]
|
||||
return None
|
||||
|
||||
def generate_dot(self, *args, **kwargs):
|
||||
return self.start.generate_dot(*args, **kwargs)
|
||||
Loading…
Add table
Add a link
Reference in a new issue