Added some documentation, fixed test and missing schedule
This commit is contained in:
parent
ebfd85a666
commit
fd6c8b4277
26 changed files with 1284 additions and 72 deletions
|
|
@ -7,12 +7,9 @@ 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 transformation.schedule.rule_scheduler import RuleScheduler
|
||||
from state.devstate import DevState
|
||||
from transformation.ramify import ramify
|
||||
from util import loader
|
||||
|
|
@ -37,7 +34,7 @@ class Test_Meta_Model(unittest.TestCase):
|
|||
def setUp(self):
|
||||
self.model = ODAPI(*self.model_param)
|
||||
self.out = io.StringIO()
|
||||
self.generator = ScheduleActionGenerator(
|
||||
self.generator = RuleScheduler(
|
||||
*self.generator_param,
|
||||
directory=self.dir + "/models",
|
||||
verbose=True,
|
||||
|
|
@ -50,9 +47,7 @@ class Test_Meta_Model(unittest.TestCase):
|
|||
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")
|
||||
|
|
@ -62,13 +57,9 @@ class Test_Meta_Model(unittest.TestCase):
|
|||
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__()
|
||||
|
|
@ -77,15 +68,11 @@ class Test_Meta_Model(unittest.TestCase):
|
|||
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):
|
||||
|
|
@ -101,12 +88,15 @@ class Test_Meta_Model(unittest.TestCase):
|
|||
self._test_conformance("multiple_end.od", {("End", "Cardinality"): []})
|
||||
|
||||
def test_connections_start(self):
|
||||
# try to load the following schedule.
|
||||
# The schedules contains happy day nodes and faulty nodes.
|
||||
# Use the error messages to select error location and further validate the multiple reasons of failure.
|
||||
self._test_conformance(
|
||||
"connections_start.od",
|
||||
{
|
||||
("Start", "start"): [
|
||||
["input exec", "foo_in", "exist"],
|
||||
["output exec", "out", "multiple"],
|
||||
("Start", "start"): [ # locate failure (contains these two substrings), make sure other do not fully overlap -> flakey test
|
||||
["input exec", "foo_in", "exist"], # 4 total reasons, a reason contains these three substrings
|
||||
["output exec", "out", "multiple"], # a reason will match to exactly one subnstring list
|
||||
["output exec", "foo_out", "exist"],
|
||||
["input data", "in", "exist"],
|
||||
]
|
||||
|
|
@ -180,32 +170,52 @@ class Test_Meta_Model(unittest.TestCase):
|
|||
)
|
||||
|
||||
def test_connections_modify(self):
|
||||
#TODO:
|
||||
# see test_connections_merge
|
||||
self._test_conformance(
|
||||
"connections_modify.od",
|
||||
{
|
||||
("Invalid source", "Conn_exec"): [],
|
||||
("Invalid target", "Conn_exec"): [],
|
||||
("Modify", "m_foo"): [
|
||||
["input 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"],
|
||||
],
|
||||
("Modify", "m_exec"): [
|
||||
["input exec", "in", "exist"],
|
||||
["input exec", "in", "exist"],
|
||||
["output exec", "out", "exist"],
|
||||
]
|
||||
},
|
||||
)
|
||||
|
||||
def test_connections_merge(self):
|
||||
#TODO:
|
||||
# mm:
|
||||
# association Conn_exec [0..*] Exec -> Exec [0..*] {
|
||||
# ...;
|
||||
# }
|
||||
# m:
|
||||
# Conn_exec ( Data -> Exec) {...;} -> Invalid source type 'Merge' for link '__Conn_exec_3:Conn_exec' (1)
|
||||
# -> Invalid target type 'End' for link '__Conn_exec_3:Conn_exec' (2)
|
||||
# Conn_exec ( Exec -> Data) {...;} -> No error at all, inconsistent and unexpected behaviour (3)
|
||||
# different combinations behave unexpected
|
||||
|
||||
self._test_conformance(
|
||||
"connections_merge.od",
|
||||
{
|
||||
("Invalid source", "Conn_exec"): [], # (1), expected
|
||||
("Invalid target", "Conn_exec"): [], # (2), invalid error, should not be shown
|
||||
("Merge", "m_foo"): [
|
||||
["input exec", "in", "exist"],
|
||||
["input data", "foo_in", "exist"],
|
||||
["input data", "in2", "multiple"],
|
||||
["output data", "foo_out", "exist"],
|
||||
],
|
||||
("Merge", "m_exec"): [ # (3), checked in Merge itself
|
||||
["input exec", "in", "exist"],
|
||||
["output exec", "out", "exist"],
|
||||
["input data", "foo_in", "exist"],
|
||||
["output data", "foo_out", "exist"],
|
||||
["input data", "in2", "multiple"],
|
||||
]
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
|
|
@ -274,7 +284,7 @@ class Test_Meta_Model(unittest.TestCase):
|
|||
["Unexpected type", "ports_exec_out", "str"],
|
||||
["Unexpected type", "ports_data_out", "str"],
|
||||
],
|
||||
("Start", '"int"'): [
|
||||
("Start", '"int"'): [ # included " to avoid flakey test
|
||||
["Unexpected type", "ports_exec_out", "int"],
|
||||
["Unexpected type", "ports_data_out", "int"],
|
||||
],
|
||||
|
|
@ -380,13 +390,16 @@ class Test_Meta_Model(unittest.TestCase):
|
|||
["Unexpected type", "ports_data_out", "NoneType"],
|
||||
["Unexpected type", "ports_data_in", "NoneType"],
|
||||
],
|
||||
("Action", '"invalid"'): [
|
||||
('"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_action"', '"invalid_action"'): [
|
||||
["Invalid python code"],
|
||||
["line"],
|
||||
],
|
||||
("Action", "subtype"): [
|
||||
["Unexpected type", "ports_exec_out", "list"],
|
||||
["Unexpected type", "ports_exec_in", "list"],
|
||||
|
|
|
|||
|
|
@ -1,16 +1,14 @@
|
|||
import io
|
||||
import os
|
||||
import unittest
|
||||
|
||||
from transformation.schedule import rule_scheduler
|
||||
from transformation.schedule.rule_scheduler import RuleSchedular
|
||||
from transformation.schedule.rule_scheduler import RuleScheduler
|
||||
from state.devstate import DevState
|
||||
|
||||
|
||||
class MyTestCase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
state = DevState()
|
||||
self.generator = RuleSchedular(state, "", "")
|
||||
self.generator = RuleScheduler(state, "", "")
|
||||
|
||||
def test_empty(self):
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -12,6 +12,10 @@ m3:Match{
|
|||
file="rules/transition.od";
|
||||
}
|
||||
|
||||
m_exec:Merge {
|
||||
ports_data_in = `["in1", "in2"]`;
|
||||
}
|
||||
|
||||
m_foo:Merge {
|
||||
ports_data_in = `["in1", "in2"]`;
|
||||
}
|
||||
|
|
@ -28,10 +32,8 @@ end:End {
|
|||
: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_exec (m2 -> m_exec) {from="success";to="in";}
|
||||
:Conn_exec (m_exec -> end) {from="out";to="in";}
|
||||
|
||||
:Conn_data (start -> m_foo) {from="1";to="in1";}
|
||||
:Conn_data (start -> m_foo) {from="1";to="in2";}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ m3:Match{
|
|||
file="rules/transition.od";
|
||||
}
|
||||
|
||||
m_exec:Modify
|
||||
m_foo:Modify
|
||||
m_void:Modify
|
||||
|
||||
|
|
@ -25,10 +26,10 @@ end:End {
|
|||
: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 (m2 -> m_exec) {from="success";to="in";}
|
||||
:Conn_exec (m2 -> m_exec) {from="fail";to="in";}
|
||||
|
||||
:Conn_exec (m_foo -> end) {from="out";to="in";}
|
||||
:Conn_exec (m_exec -> end) {from="out";to="in";}
|
||||
|
||||
:Conn_data (start -> mo) {from="1";to="in";}
|
||||
:Conn_data (mo -> m2) {from="out";to="in";}
|
||||
|
|
|
|||
BIN
transformation/schedule/doc/images/example_1.png
Normal file
BIN
transformation/schedule/doc/images/example_1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 37 KiB |
BIN
transformation/schedule/doc/images/example_2.png
Normal file
BIN
transformation/schedule/doc/images/example_2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 80 KiB |
BIN
transformation/schedule/doc/images/example_3.png
Normal file
BIN
transformation/schedule/doc/images/example_3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 94 KiB |
BIN
transformation/schedule/doc/images/geraniums-main.png
Normal file
BIN
transformation/schedule/doc/images/geraniums-main.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 44 KiB |
BIN
transformation/schedule/doc/images/geraniums-repot_flowers.png
Normal file
BIN
transformation/schedule/doc/images/geraniums-repot_flowers.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
251
transformation/schedule/doc/schedule.md
Normal file
251
transformation/schedule/doc/schedule.md
Normal file
|
|
@ -0,0 +1,251 @@
|
|||
# Schedule Module
|
||||
|
||||
This module is used to define and execute model transformations using a schedule in the muMLE framework.
|
||||
The development of this module is port of a research project of Robbe Teughels with Joeri Exelmans and Hans Vangheluwe.
|
||||
|
||||
## Module Structure
|
||||
|
||||
The entire module is wrapped in single interface [schedule.py](../rule_scheduler.py) responsible for loading, executing and other optional functionalities, such as generating dot files.
|
||||
Loading modules (.py and .drawio) requires compilation. All these transformations are grouped together in [generator.py](../generator.py).
|
||||
The interactions with the muMLE framework uses the custom interface: [rule_executor.py](../rule_executor.py). This reduces the dependency between the module and the framework.
|
||||
|
||||
Schedules are compiled to python files. These files have a fixed interface defined in [schedule.pyi](../schedule.pyi).
|
||||
This interface includes functionalities that will setup the schedule structure and link patterns or other schedules from the module interface with the nodes.
|
||||
The compiled files do not include any functional implementation to reduce their size and compile time. They are linked to a libary [schedule_lib](../schedule_lib) including an implementation for each node type.
|
||||
This means that nodes can be treated as a black box by the schedule. This architecture allowing easier testing of the library as generation is fully independent of the core implementation.
|
||||
|
||||
The implementation of a given node is similar in the inheritance compared to the original meta-model to increasing traceability between the original instance and the compiled instance.
|
||||
|
||||
## Usage
|
||||
|
||||
### Running Module
|
||||
|
||||
```python
|
||||
|
||||
from state.devstate import DevState
|
||||
from bootstrap.scd import bootstrap_scd
|
||||
from util import loader
|
||||
from transformation.ramify import ramify
|
||||
from api.od import ODAPI
|
||||
from transformation.schedule.rule_scheduler import RuleScheduler
|
||||
|
||||
state = DevState()
|
||||
scd_mmm = bootstrap_scd(state)
|
||||
|
||||
# load model and meta-model
|
||||
metamodel_cs = open('your_metamodel.od', 'r', encoding="utf-8").read()
|
||||
model_cs = open('your_model.od', 'r', encoding="utf-8").read()
|
||||
|
||||
# Parse them
|
||||
metamodel = loader.parse_and_check(state, metamodel_cs, scd_mmm, "your_metamodel")
|
||||
model = loader.parse_and_check(state, model_cs, metamodel, "Example model")
|
||||
|
||||
# Ramified model
|
||||
metamodel_ramified = ramify(state, metamodel)
|
||||
|
||||
# scheduler
|
||||
scheduler = RuleScheduler(state, metamodel, metamodel_ramified)
|
||||
|
||||
# load schedule
|
||||
scheduler.load_schedule("your_schedule.od")
|
||||
# scheduler.load_schedule("your_schedule.py") # compiled version (without conformance checking)
|
||||
# scheduler.load_schedule("your_schedule.drawio") # main page will be executed
|
||||
|
||||
# execute model transformation
|
||||
api = ODAPI(state, model, metamodel)
|
||||
scheduler.run(api)
|
||||
```
|
||||
|
||||
#### Simple example schedules (.od format)
|
||||
|
||||
A schedule is executed from start to end or NullNode (reachable only from unconnected exec-gates).
|
||||
Given the following basic schedule (ARule without NAC), the first match of the pre-condition_pattern is used to rewrite the host graph.
|
||||
This schedule expect at least one match as the `fail' exec-gate of the match is not connected.
|
||||
Zero matches leads to a NullState, resulting in early termination.
|
||||
|
||||
```markdown
|
||||
start:Start
|
||||
end:End
|
||||
|
||||
# match once
|
||||
m:Match{
|
||||
file = "your_pre-condition_pattern.od";
|
||||
n = 1;
|
||||
}
|
||||
|
||||
# rewrite
|
||||
r:Rewrite{
|
||||
file = "your_post-condition_pattern.od";
|
||||
}
|
||||
|
||||
:Conn_exec (start -> m) {from="out"; to="in";}
|
||||
:Conn_exec (m -> r) {from="success"; to="in";}
|
||||
:Conn_exec (r -> end) {from="out"; to="in";}
|
||||
|
||||
:Conn_data (m -> r) {from="out"; to="in";}
|
||||
```
|
||||

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

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

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

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

|
||||
|
||||
[Merge](schedule_lib/merge.md)
|
||||
|
||||
### Store
|
||||
Combines matches (set) into a new match set.
|
||||
Use the exec port to insert the data on the associated data-port to the set.
|
||||
|
||||
The direct usage of this node is limited but invaluable for libraries.
|
||||
An example usage is petrinet-execution with user interface.
|
||||
This requires a list of all transitions that can fire.
|
||||
Matching "all transitions" followed by a loop to check the NAC leaves single matches.
|
||||
This nodes allows these matches to be recombined into a set that can be used to choose a transition from.
|
||||
|
||||
[Store](schedule_lib/store.md)
|
||||
|
||||
### Loop
|
||||
Iterate over a given match set.
|
||||
Nodes such as Match or Rewrite uses a single match as a pivot.
|
||||
Executing these nodes over all the element is possible with this node.
|
||||
See the examples in [Modify](#Modify) or [Merge](#Merge) for an example view.
|
||||
|
||||
[Loop](schedule_lib/loop.md)
|
||||
|
||||
### Print
|
||||
Print the input data. This is mainly used as a debugging/testing tool to validate intermediate information or state.
|
||||
|
||||
[Print](schedule_lib/print.md)
|
||||
|
||||
### Action
|
||||
This node allows for code to be injected into the schedule.
|
||||
This node can be used for general purpuse and even recreate all other nodes (except start and end).
|
||||
Not all functionalities can be described using the current nodes. For petrinets, an example can be to generate a visual overview of the petrinet-system.
|
||||
|
||||
[Action.md](schedule_lib/action.md)
|
||||
|
||||
## file formats
|
||||
|
||||
### .od
|
||||
This is the original textual file format used by the framework. The main advantage of this format is the integration with the framework that allows conformance checking of the scheduling language.
|
||||
Therefore, all other formats are converted to this type for conformance checking before being compiled.
|
||||
|
||||
### .py
|
||||
All schedules are compiled to python after conformance checking. Allowing this format provides the benefit to load schedules without expensive compilation or conformance checking, reducing computational cost.
|
||||
This format is recommended in the deployment of applications where the schedule will not change.
|
||||
It is not advisable to implement schedules directly in this format as conformance checking guarantees proper working of the schedule module.
|
||||
|
||||
### .drawio
|
||||
A visual format for the drawio application.
|
||||
The library includes a drawio [library](../schedule_lib/Schedule_lib.xml) that includes a representation with additional fields for easy integration with the application.
|
||||
The main advantage of this format is the usage of pages that allows sub-schedules be easily created and organised within one schedule. (layers are not allowed)
|
||||
|
||||
1
transformation/schedule/doc/schedule_lib/end.md
Normal file
1
transformation/schedule/doc/schedule_lib/end.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
# Under construction
|
||||
1
transformation/schedule/doc/schedule_lib/start.md
Normal file
1
transformation/schedule/doc/schedule_lib/start.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
# Under construction
|
||||
|
|
@ -7,12 +7,11 @@ association Conn_exec [0..*] Exec -> Exec [0..*] {
|
|||
|
||||
abstract class Data
|
||||
association Conn_data [0..*] Data -> Data [0..*] {
|
||||
Integer from;
|
||||
Integer to;
|
||||
String from;
|
||||
String to;
|
||||
}
|
||||
abstract class Node (Exec, Data)
|
||||
|
||||
class Start [1..1] (Node) {
|
||||
class Start [1..1] (Exec, Data) {
|
||||
optional ActionCode ports_exec_out;
|
||||
optional ActionCode ports_data_out;
|
||||
```
|
||||
|
|
@ -28,7 +27,7 @@ class Start [1..1] (Node) {
|
|||
err
|
||||
```;
|
||||
}
|
||||
class End [1..1] (Node) {
|
||||
class End [1..1] (Exec, Data) {
|
||||
optional ActionCode ports_exec_in;
|
||||
optional ActionCode ports_data_in;
|
||||
```
|
||||
|
|
@ -45,7 +44,7 @@ class End [1..1] (Node) {
|
|||
```;
|
||||
}
|
||||
|
||||
abstract class Rule (Node)
|
||||
abstract class Rule (Exec, Data)
|
||||
{
|
||||
String file;
|
||||
}
|
||||
|
|
@ -75,7 +74,7 @@ class Rewrite (Rule)
|
|||
```;
|
||||
}
|
||||
|
||||
class Action (Node)
|
||||
class Action (Exec, Data)
|
||||
{
|
||||
optional ActionCode ports_exec_in;
|
||||
optional ActionCode ports_exec_out;
|
||||
|
|
@ -100,7 +99,7 @@ class Action (Node)
|
|||
|
||||
}
|
||||
|
||||
class Modify (Node)
|
||||
class Modify (Data)
|
||||
{
|
||||
optional ActionCode rename;
|
||||
optional ActionCode delete;
|
||||
|
|
@ -122,7 +121,7 @@ class Modify (Node)
|
|||
```;
|
||||
}
|
||||
|
||||
class Merge (Node)
|
||||
class Merge (Data)
|
||||
{
|
||||
ActionCode ports_data_in;
|
||||
```
|
||||
|
|
@ -138,7 +137,7 @@ class Merge (Node)
|
|||
```;
|
||||
}
|
||||
|
||||
class Store (Node)
|
||||
class Store (Exec, Data)
|
||||
{
|
||||
ActionCode ports;
|
||||
```
|
||||
|
|
@ -154,7 +153,7 @@ class Store (Node)
|
|||
```;
|
||||
}
|
||||
|
||||
class Schedule (Node)
|
||||
class Schedule (Exec, Data)
|
||||
{
|
||||
String file;
|
||||
```
|
||||
|
|
@ -167,7 +166,7 @@ class Schedule (Node)
|
|||
```;
|
||||
}
|
||||
|
||||
class Loop(Node)
|
||||
class Loop(Exec, Data)
|
||||
{
|
||||
```
|
||||
check_all_connections(this, [
|
||||
|
|
@ -179,7 +178,7 @@ class Loop(Node)
|
|||
```;
|
||||
}
|
||||
|
||||
class Print(Node)
|
||||
class Print(Exec, Data)
|
||||
{
|
||||
optional Boolean event;
|
||||
optional String label;
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ if TYPE_CHECKING:
|
|||
from transformation.schedule.schedule import Schedule
|
||||
|
||||
|
||||
class RuleSchedular:
|
||||
class RuleScheduler:
|
||||
__slots__ = (
|
||||
"rule_executor",
|
||||
"schedule_main",
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ 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
|
||||
from rule_scheduler import RuleScheduler
|
||||
|
||||
class Schedule:
|
||||
__slots__ = {
|
||||
|
|
@ -14,5 +14,5 @@ class Schedule:
|
|||
|
||||
@staticmethod
|
||||
def get_matchers(): ...
|
||||
def init_schedule(self, schedular: RuleSchedular, rule_executor: RuleExecutor, matchers): ...
|
||||
def init_schedule(self, scheduler: RuleScheduler, rule_executor: RuleExecutor, matchers): ...
|
||||
def generate_dot(self, *args, **kwargs): ...
|
||||
|
|
@ -1,5 +1,9 @@
|
|||
from abc import abstractmethod
|
||||
from typing import override
|
||||
from jinja2 import Template
|
||||
|
||||
from api.od import ODAPI
|
||||
from .funcs import generate_dot_edge
|
||||
from .node import Node
|
||||
|
||||
|
||||
|
|
@ -33,3 +37,25 @@ class ExecNode(Node):
|
|||
@abstractmethod
|
||||
def execute(self, port: str, exec_id: int, od: ODAPI) -> tuple[int, any] | None:
|
||||
return None
|
||||
|
||||
@override
|
||||
def generate_dot(
|
||||
self, nodes: list[str], edges: list[str], visited: set[int], template: Template
|
||||
) -> None:
|
||||
for out_port, edge in self.next_node.items():
|
||||
template.render()
|
||||
generate_dot_edge(
|
||||
self,
|
||||
edge[0],
|
||||
edges,
|
||||
template,
|
||||
kwargs={
|
||||
"prefix": "e",
|
||||
"from_gate": out_port,
|
||||
"to_gate": edge[1],
|
||||
"color": "darkblue",
|
||||
},
|
||||
)
|
||||
|
||||
for edge in self.next_node.values():
|
||||
edge[0].generate_dot(nodes, edges, visited, template)
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ class Match(ExecNode, DataNode):
|
|||
nodes,
|
||||
template,
|
||||
**{
|
||||
"label": f"match_{self.n}\n{self.label}",
|
||||
"label": f"match\n{self.label}\nn = {self.n}",
|
||||
"ports_exec": (
|
||||
self.get_exec_input_gates(),
|
||||
self.get_exec_output_gates(),
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ class Rewrite(ExecNode, DataNode):
|
|||
nodes,
|
||||
template,
|
||||
**{
|
||||
"label": "rewrite",
|
||||
"label": f"rewrite\n{self.label}",
|
||||
"ports_exec": (
|
||||
self.get_exec_input_gates(),
|
||||
self.get_exec_output_gates(),
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ from .exec_node import ExecNode
|
|||
from .funcs import not_visited, generate_dot_node, IdGenerator
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..rule_scheduler import RuleSchedular
|
||||
from ..rule_scheduler import RuleScheduler
|
||||
|
||||
|
||||
class ScheduleState:
|
||||
|
|
@ -16,9 +16,9 @@ class ScheduleState:
|
|||
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
|
||||
def __init__(self, scheduler: "RuleScheduler", file: str) -> None:
|
||||
self.schedule = scheduler._load_schedule(file, _main=False)
|
||||
self.scheduler = scheduler
|
||||
super().__init__()
|
||||
self.state: dict[int, ScheduleState] = {}
|
||||
|
||||
|
|
@ -58,7 +58,7 @@ class SubSchedule(ExecNode, DataNode):
|
|||
|
||||
@override
|
||||
def execute(self, port: str, exec_id: int, od: ODAPI) -> tuple[int, any] | None:
|
||||
runstatus, result = self.schedular._runner(
|
||||
runstatus, result = self.scheduler._runner(
|
||||
od,
|
||||
self.schedule,
|
||||
port,
|
||||
|
|
|
|||
|
|
@ -11,9 +11,14 @@ digraph G {
|
|||
{% endfor %}
|
||||
}
|
||||
|
||||
{% macro Node(label, id, ports_exec=[], ports_data=[]) %}
|
||||
{% macro Node(label, id, ports_exec=[], ports_data=[], debug = False) %}
|
||||
subgraph cluster_{{ id }} {
|
||||
label = "{{ id }}__{{ label }}";
|
||||
label = "
|
||||
{%- if debug %}
|
||||
{{ id }}_
|
||||
{%- endif -%}
|
||||
{{ label }}"
|
||||
|
||||
style = rounded;
|
||||
input_{{ id }} [
|
||||
shape=rect;
|
||||
|
|
@ -54,7 +59,7 @@ output_{{ from_id }}:{{ prefix }}_{{ from_gate }} -> input_{{ to_id }}:{{ prefix
|
|||
</TD></TR>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<TR><TD>X</TD></TR>
|
||||
<TR><TD> </TD></TR>
|
||||
{% endif %}
|
||||
</TABLE>>
|
||||
{%- endmacro %}
|
||||
|
|
@ -31,7 +31,7 @@
|
|||
{%- endmacro %}
|
||||
|
||||
{% macro Schedule(name, file) %}
|
||||
{{ name }} = SubSchedule(schedular, "{{ file }}")
|
||||
{{ name }} = SubSchedule(scheduler, "{{ file }}")
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro Loop(name) %}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ class Schedule:
|
|||
{% endfor %}
|
||||
]
|
||||
|
||||
def init_schedule(self, schedular, rule_executer, matchers):
|
||||
def init_schedule(self, scheduler, rule_executer, matchers):
|
||||
{% for block in blocks_start_end%}
|
||||
{{ block }}
|
||||
{% endfor %}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue