A fully working version of the scheduling language with added examples
This commit is contained in:
parent
ec42f74960
commit
ebfd85a666
126 changed files with 7235 additions and 981 deletions
489
transformation/schedule/Tests/Test_meta_model.py
Normal file
489
transformation/schedule/Tests/Test_meta_model.py
Normal file
|
|
@ -0,0 +1,489 @@
|
|||
import io
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
sys.path.insert(
|
||||
0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../"))
|
||||
)
|
||||
|
||||
from icecream import ic
|
||||
|
||||
from api.od import ODAPI
|
||||
from bootstrap.scd import bootstrap_scd
|
||||
from examples.schedule import rule_schedular
|
||||
from examples.schedule.rule_schedular import ScheduleActionGenerator
|
||||
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 = ScheduleActionGenerator(
|
||||
*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:]
|
||||
ic(errors)
|
||||
if len(errors) != len(expected_substr_err.keys()):
|
||||
ic("len total errors")
|
||||
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:
|
||||
ic("no matching key")
|
||||
ic(line)
|
||||
assert False
|
||||
expected = expected_substr_err[key]
|
||||
if (len(error_lines) - 1) != len(expected):
|
||||
ic("len substr errors")
|
||||
ic(line)
|
||||
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
|
||||
):
|
||||
ic("wrong substr error")
|
||||
ic(line)
|
||||
ic(error_lines)
|
||||
assert False
|
||||
expected_substr_err.pop(key)
|
||||
except AssertionError:
|
||||
raise
|
||||
except Exception as e:
|
||||
ic(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):
|
||||
self._test_conformance(
|
||||
"connections_start.od",
|
||||
{
|
||||
("Start", "start"): [
|
||||
["input exec", "foo_in", "exist"],
|
||||
["output exec", "out", "multiple"],
|
||||
["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):
|
||||
self._test_conformance(
|
||||
"connections_modify.od",
|
||||
{
|
||||
("Modify", "m_foo"): [
|
||||
["input exec", "in", "exist"],
|
||||
["input exec", "in", "exist"],
|
||||
["output exec", "out", "exist"],
|
||||
["input data", "foo_in", "exist"],
|
||||
["output data", "foo_out", "exist"],
|
||||
["input data", "in", "multiple"],
|
||||
]
|
||||
},
|
||||
)
|
||||
|
||||
def test_connections_merge(self):
|
||||
self._test_conformance(
|
||||
"connections_merge.od",
|
||||
{
|
||||
("Merge", "m_foo"): [
|
||||
["input exec", "in", "exist"],
|
||||
["input exec", "in", "exist"],
|
||||
["output exec", "out", "exist"],
|
||||
["input data", "foo_in", "exist"],
|
||||
["output data", "foo_out", "exist"],
|
||||
["input data", "in2", "multiple"],
|
||||
]
|
||||
},
|
||||
)
|
||||
|
||||
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"'): [
|
||||
["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"): [],
|
||||
("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()
|
||||
45
transformation/schedule/Tests/Test_xmlparser.py
Normal file
45
transformation/schedule/Tests/Test_xmlparser.py
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import io
|
||||
import os
|
||||
import unittest
|
||||
|
||||
from transformation.schedule import rule_scheduler
|
||||
from transformation.schedule.rule_scheduler import RuleSchedular
|
||||
from state.devstate import DevState
|
||||
|
||||
|
||||
class MyTestCase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
state = DevState()
|
||||
self.generator = RuleSchedular(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,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_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_foo) {from="success";to="in";}
|
||||
:Conn_exec (m2 -> m_foo) {from="fail";to="in";}
|
||||
|
||||
:Conn_exec (m_foo -> 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,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";
|
||||
}
|
||||
|
||||
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_foo) {from="success";to="in";}
|
||||
:Conn_exec (m2 -> m_foo) {from="fail";to="in";}
|
||||
|
||||
:Conn_exec (m_foo -> 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
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: ...
|
||||
195
transformation/schedule/models/scheduling_MM.od
Normal file
195
transformation/schedule/models/scheduling_MM.od
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
abstract class Exec
|
||||
|
||||
association Conn_exec [0..*] Exec -> Exec [0..*] {
|
||||
String from;
|
||||
String to;
|
||||
}
|
||||
|
||||
abstract class Data
|
||||
association Conn_data [0..*] Data -> Data [0..*] {
|
||||
Integer from;
|
||||
Integer to;
|
||||
}
|
||||
abstract class Node (Exec, Data)
|
||||
|
||||
class Start [1..1] (Node) {
|
||||
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] (Node) {
|
||||
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 (Node)
|
||||
{
|
||||
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 (Node)
|
||||
{
|
||||
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 (Node)
|
||||
{
|
||||
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 (Node)
|
||||
{
|
||||
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 (Node)
|
||||
{
|
||||
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 (Node)
|
||||
{
|
||||
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(Node)
|
||||
{
|
||||
```
|
||||
check_all_connections(this, [
|
||||
["in"],
|
||||
["it", "out"],
|
||||
["in"],
|
||||
["out"]
|
||||
])
|
||||
```;
|
||||
}
|
||||
|
||||
class Print(Node)
|
||||
{
|
||||
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 RuleSchedular:
|
||||
__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 RuleSchedular
|
||||
|
||||
class Schedule:
|
||||
__slots__ = {
|
||||
"start",
|
||||
"end",
|
||||
"nodes"
|
||||
}
|
||||
def __init__(self): ...
|
||||
|
||||
@staticmethod
|
||||
def get_matchers(): ...
|
||||
def init_schedule(self, schedular: RuleSchedular, rule_executor: RuleExecutor, matchers): ...
|
||||
def generate_dot(self, *args, **kwargs): ...
|
||||
41
transformation/schedule/schedule_lib/README.md
Normal file
41
transformation/schedule/schedule_lib/README.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.
|
||||
|
||||
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(),
|
||||
),
|
||||
}
|
||||
)
|
||||
35
transformation/schedule/schedule_lib/exec_node.py
Normal file
35
transformation/schedule/schedule_lib/exec_node.py
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
from abc import abstractmethod
|
||||
from api.od import ODAPI
|
||||
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
|
||||
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_{self.n}\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)
|
||||
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": "rewrite",
|
||||
"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 RuleSchedular
|
||||
|
||||
|
||||
class ScheduleState:
|
||||
def __init__(self) -> None:
|
||||
self.end_gate: str = ""
|
||||
|
||||
class SubSchedule(ExecNode, DataNode):
|
||||
def __init__(self, schedular: "RuleSchedular", file: str) -> None:
|
||||
self.schedule = schedular._load_schedule(file, _main=False)
|
||||
self.schedular = schedular
|
||||
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.schedular._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)
|
||||
60
transformation/schedule/templates/schedule_dot.j2
Normal file
60
transformation/schedule/templates/schedule_dot.j2
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
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=[]) %}
|
||||
subgraph cluster_{{ id }} {
|
||||
label = "{{ id }}__{{ 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>X</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(schedular, "{{ 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, schedular, 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