Merge remote-tracking branch 'origin/master' into development
# Conflicts: # api/od.py
This commit is contained in:
commit
9475b1fdc5
38 changed files with 1028 additions and 15 deletions
|
|
@ -53,7 +53,7 @@ class CDAPI:
|
|||
return self.bottom.read_outgoing_elements(self.m, type_name)[0]
|
||||
|
||||
def is_direct_subtype(self, super_type_name: str, sub_type_name: str):
|
||||
return sub_type_name in self.direct_sub_types[super_type]
|
||||
return sub_type_name in self.direct_sub_types[super_type_name]
|
||||
|
||||
def is_direct_supertype(self, sub_type_name: str, super_type_name: str):
|
||||
return super_type_name in self.direct_super_types[sub_type_name]
|
||||
|
|
@ -83,3 +83,6 @@ class CDAPI:
|
|||
result = self.find_attribute_type(supertype, attr_name)
|
||||
if result != None:
|
||||
return result
|
||||
|
||||
def get_type(self, type_name: str):
|
||||
return next(k for k, v in self.type_model_names.items() if v == type_name)
|
||||
|
|
|
|||
11
api/od.py
11
api/od.py
|
|
@ -146,7 +146,7 @@ class ODAPI:
|
|||
typ = self.cdapi.get_type(type_name)
|
||||
types = set(typ) if not include_subtypes else self.cdapi.transitive_sub_types[type_name]
|
||||
for type_of_obj in self.bottom.read_outgoing_elements(obj, "Morphism"):
|
||||
if type_of_obj in types:
|
||||
if self.get_name(type_of_obj) in types:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
|
@ -154,10 +154,9 @@ class ODAPI:
|
|||
self.bottom.delete_element(obj)
|
||||
self.__recompute_mappings()
|
||||
|
||||
# Does the class of the object have the given attribute?
|
||||
# Does the the object have the given attribute?
|
||||
def has_slot(self, obj: UUID, attr_name: str):
|
||||
class_name = self.get_name(self.get_type(obj))
|
||||
return self.od.get_attr_link_name(class_name, attr_name) != None
|
||||
return self.od.get_slot_link(obj, attr_name) != None
|
||||
|
||||
def get_slots(self, obj: UUID) -> list[str]:
|
||||
return [attr_name for attr_name, _ in self.od.get_slots(obj)]
|
||||
|
|
@ -285,6 +284,7 @@ def bind_api_readonly(odapi):
|
|||
'get_target': odapi.get_target,
|
||||
'get_source': odapi.get_source,
|
||||
'get_slot': odapi.get_slot,
|
||||
'get_slots': odapi.get_slots,
|
||||
'get_slot_value': odapi.get_slot_value,
|
||||
'get_slot_value_default': odapi.get_slot_value_default,
|
||||
'get_all_instances': odapi.get_all_instances,
|
||||
|
|
@ -292,7 +292,8 @@ def bind_api_readonly(odapi):
|
|||
'get_type_name': odapi.get_type_name,
|
||||
'get_outgoing': odapi.get_outgoing,
|
||||
'get_incoming': odapi.get_incoming,
|
||||
'has_slot': odapi.has_slot
|
||||
'has_slot': odapi.has_slot,
|
||||
'is_instance': odapi.is_instance,
|
||||
}
|
||||
return funcs
|
||||
|
||||
|
|
|
|||
66
examples/petrinet/models/schedule.od
Normal file
66
examples/petrinet/models/schedule.od
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
start:Start
|
||||
end:End
|
||||
|
||||
transitions:Match{
|
||||
file = "operational_semantics/transition";
|
||||
}
|
||||
|
||||
|
||||
d:Data_modify
|
||||
{
|
||||
modify_dict = '
|
||||
{
|
||||
"tr": "t"
|
||||
}';
|
||||
}
|
||||
|
||||
nac_input_without:Match{
|
||||
file = "operational_semantics/all_input_have_token";
|
||||
n = "1";
|
||||
}
|
||||
|
||||
inputs:Match{
|
||||
file = "operational_semantics/all_inputs";
|
||||
}
|
||||
|
||||
rewrite_incoming:Rewrite
|
||||
{
|
||||
file = "operational_semantics/remove_incoming";
|
||||
}
|
||||
|
||||
loop_trans:Loop
|
||||
loop_input:Loop
|
||||
|
||||
p:Print
|
||||
{
|
||||
event = True;
|
||||
label = "transition: ";
|
||||
}
|
||||
|
||||
p2:Print
|
||||
{
|
||||
event = True;
|
||||
label = "inputs: ";
|
||||
}
|
||||
|
||||
:Exec_con(start -> transitions){gate_from = 0;gate_to = 0;}
|
||||
:Exec_con(transitions -> end){gate_from = 1;gate_to = 0;}
|
||||
:Exec_con(transitions -> loop_trans){gate_from = 0;gate_to = 0;}
|
||||
:Exec_con(loop_trans -> nac_input_without){gate_from = 0;gate_to = 0;}
|
||||
|
||||
[//]: # (:Exec_con(nac_input_without -> loop_trans){gate_from = 0;gate_to = 0;})
|
||||
:Exec_con(nac_input_without -> inputs){gate_from = 1;gate_to = 0;}
|
||||
:Exec_con(inputs -> loop_input){gate_from = 0;gate_to = 0;}
|
||||
:Exec_con(inputs -> loop_trans){gate_from = 1;gate_to = 0;}
|
||||
|
||||
:Exec_con(loop_trans -> end){gate_from = 1;gate_to = 0;}
|
||||
|
||||
:Data_con(transitions -> loop_trans)
|
||||
:Data_con(nac_input_without -> p)
|
||||
:Data_con(d -> nac_input_without)
|
||||
:Data_con(loop_trans -> d)
|
||||
:Data_con(loop_trans -> rewrite_incoming)
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
# A place with no tokens:
|
||||
|
||||
p:RAM_PNPlace
|
||||
ps:RAM_PNPlaceState {
|
||||
RAM_numTokens = `get_value(this) == 0`;
|
||||
}
|
||||
:RAM_pn_of (ps -> p)
|
||||
|
||||
# An incoming arc from that place to our transition:
|
||||
|
||||
t:RAM_PNTransition
|
||||
|
||||
:RAM_arc (p -> t)
|
||||
13
examples/petrinet/operational_semantics/all_inputs.od
Normal file
13
examples/petrinet/operational_semantics/all_inputs.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)
|
||||
13
examples/petrinet/operational_semantics/all_output_places.od
Normal file
13
examples/petrinet/operational_semantics/all_output_places.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 (t -> p)
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
# A place with no tokens:
|
||||
|
||||
p:RAM_PNPlace
|
||||
ps:RAM_PNPlaceState {
|
||||
RAM_numTokens = `set_value(this, get_value(this) + 1)`;
|
||||
}
|
||||
:RAM_pn_of (ps -> p)
|
||||
|
||||
# An incoming arc from that place to our transition:
|
||||
|
||||
t:RAM_PNTransition
|
||||
|
||||
:RAM_arc (t -> p)
|
||||
0
examples/petrinet/operational_semantics/delete_all.od
Normal file
0
examples/petrinet/operational_semantics/delete_all.od
Normal file
1
examples/petrinet/operational_semantics/transition.od
Normal file
1
examples/petrinet/operational_semantics/transition.od
Normal file
|
|
@ -0,0 +1 @@
|
|||
tr:RAM_PNTransition
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
from examples.schedule.RuleExecuter import RuleExecuter
|
||||
from state.devstate import DevState
|
||||
from api.od import ODAPI
|
||||
from concrete_syntax.textual_od.renderer import render_od
|
||||
|
|
@ -9,6 +10,10 @@ from transformation.ramify import ramify
|
|||
from examples.semantics.operational import simulator
|
||||
from examples.petrinet.renderer import show_petri_net
|
||||
|
||||
from examples.schedule.ScheduledActionGenerator import *
|
||||
from examples.schedule.RuleExecuter import *
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import os
|
||||
|
|
@ -46,19 +51,24 @@ if __name__ == "__main__":
|
|||
mm_rt_ramified,
|
||||
["fire_transition"]) # only 1 rule :(
|
||||
|
||||
matcher_rewriter = RuleMatcherRewriter(state, mm_rt, mm_rt_ramified)
|
||||
action_generator = ActionGenerator(matcher_rewriter, rules)
|
||||
# matcher_rewriter = RuleMatcherRewriter(state, mm_rt, mm_rt_ramified)
|
||||
# action_generator = ActionGenerator(matcher_rewriter, rules)
|
||||
|
||||
matcher_rewriter2 = RuleExecuter(state, mm_rt, mm_rt_ramified)
|
||||
action_generator = ScheduleActionGenerator(matcher_rewriter2, f"models/schedule.od")
|
||||
|
||||
def render_callback(od):
|
||||
show_petri_net(od)
|
||||
# return render_od(state, od.m, od.mm)
|
||||
return render_od_jinja2(state, od.m, od.mm)
|
||||
|
||||
sim = simulator.Simulator(
|
||||
action_generator.generate_dot()
|
||||
|
||||
sim = simulator.MinimalSimulator(
|
||||
action_generator=action_generator,
|
||||
decision_maker=simulator.InteractiveDecisionMaker(auto_proceed=False),
|
||||
# decision_maker=simulator.RandomDecisionMaker(seed=0),
|
||||
renderer=render_callback,
|
||||
termination_condition=action_generator.termination_condition,
|
||||
# renderer=lambda od: render_od(state, od.m, od.mm),
|
||||
)
|
||||
|
||||
|
|
|
|||
49
examples/schedule/RuleExecuter.py
Normal file
49
examples/schedule/RuleExecuter.py
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
from concrete_syntax.textual_od.renderer import render_od
|
||||
|
||||
import pprint
|
||||
from typing import Generator, Callable, Any
|
||||
from uuid import UUID
|
||||
import functools
|
||||
|
||||
from api.od import ODAPI
|
||||
from concrete_syntax.common import indent
|
||||
from transformation.matcher import match_od
|
||||
from transformation.rewriter import rewrite
|
||||
from transformation.cloner import clone_od
|
||||
from util.timer import Timer
|
||||
from util.loader import parse_and_check
|
||||
|
||||
class RuleExecuter:
|
||||
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, m: UUID, rhs: UUID, *, pivot:dict[Any, Any]):
|
||||
yield rewrite(self.state,
|
||||
rhs_m=rhs,
|
||||
pattern_mm=self.mm_ramified,
|
||||
lhs_match=pivot,
|
||||
host_m=m,
|
||||
host_mm=self.mm,
|
||||
eval_context=self.eval_context,
|
||||
)
|
||||
|
||||
|
||||
def load_match(self, file: str):
|
||||
with open(file, "r") as f:
|
||||
return parse_and_check(self.state, f.read(), self.mm_ramified, file)
|
||||
104
examples/schedule/ScheduledActionGenerator.py
Normal file
104
examples/schedule/ScheduledActionGenerator.py
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
import importlib.util
|
||||
import io
|
||||
import os
|
||||
|
||||
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 examples.schedule.generator import schedule_generator
|
||||
from examples.schedule.schedule_lib import End, NullNode
|
||||
from framework.conformance import Conformance, render_conformance_check_result
|
||||
from state.devstate import DevState
|
||||
|
||||
|
||||
class ScheduleActionGenerator:
|
||||
def __init__(self, rule_executer, schedulefile:str):
|
||||
self.rule_executer = rule_executer
|
||||
self.rule_dict = {}
|
||||
self.schedule: "Schedule"
|
||||
|
||||
|
||||
self.state = DevState()
|
||||
self.load_schedule(schedulefile)
|
||||
|
||||
def load_schedule(self, filename):
|
||||
print("Loading schedule ...")
|
||||
scd_mmm = bootstrap_scd(self.state)
|
||||
with open("../schedule/models/scheduling_MM.od", "r") as f_MM:
|
||||
mm_cs = f_MM.read()
|
||||
with open(f"{filename}", "r") as f_M:
|
||||
m_cs = f_M.read()
|
||||
print("OK")
|
||||
|
||||
print("\nParsing models")
|
||||
|
||||
print(f"\tParsing meta model")
|
||||
scheduling_mm = parser_cd.parse_cd(
|
||||
self.state,
|
||||
m_text=mm_cs,
|
||||
)
|
||||
print(f"\tParsing '{filename}_M.od' model")
|
||||
scheduling_m = parser_od.parse_od(
|
||||
self.state,
|
||||
m_text=m_cs,
|
||||
mm=scheduling_mm
|
||||
)
|
||||
print(f"OK")
|
||||
|
||||
print("\tmeta-meta-model a valid class diagram")
|
||||
conf = Conformance(self.state, scd_mmm, scd_mmm)
|
||||
print(render_conformance_check_result(conf.check_nominal()))
|
||||
print(f"Is our '{filename}_M.od' model a valid '{filename}_MM.od' diagram?")
|
||||
conf = Conformance(self.state, scheduling_m, scheduling_mm)
|
||||
print(render_conformance_check_result(conf.check_nominal()))
|
||||
print("OK")
|
||||
|
||||
od = ODAPI(self.state, scheduling_m, scheduling_mm)
|
||||
g = schedule_generator(od)
|
||||
|
||||
output_buffer = io.StringIO()
|
||||
g.generate_schedule(output_buffer)
|
||||
open(f"schedule.py", "w").write(output_buffer.getvalue())
|
||||
spec = importlib.util.spec_from_file_location("schedule", "schedule.py")
|
||||
scedule_module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(scedule_module)
|
||||
self.schedule = scedule_module.Schedule(self.rule_executer)
|
||||
self.load_matchers()
|
||||
|
||||
def load_matchers(self):
|
||||
matchers = dict()
|
||||
for file in self.schedule.get_matchers():
|
||||
matchers[file] = self.rule_executer.load_match(file)
|
||||
self.schedule.init_schedule(matchers)
|
||||
|
||||
def __call__(self, api: ODAPI):
|
||||
exec_op = self.schedule(api)
|
||||
yield from exec_op
|
||||
|
||||
def termination_condition(self, api: ODAPI):
|
||||
if type(self.schedule.cur) == End:
|
||||
return "jay"
|
||||
if type(self.schedule.cur) == NullNode:
|
||||
return "RRRR"
|
||||
return None
|
||||
|
||||
def generate_dot(self):
|
||||
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()
|
||||
self.schedule.generate_dot(nodes, edges, visit)
|
||||
print("Nodes:")
|
||||
print(nodes)
|
||||
print("\nEdges:")
|
||||
print(edges)
|
||||
|
||||
with open("test.dot", "w") as f_dot:
|
||||
f_dot.write(template_dot.render({"nodes": nodes, "edges": edges}))
|
||||
0
examples/schedule/__init__.py
Normal file
0
examples/schedule/__init__.py
Normal file
129
examples/schedule/generator.py
Normal file
129
examples/schedule/generator.py
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
import sys
|
||||
import os
|
||||
import json
|
||||
from uuid import UUID
|
||||
|
||||
from jinja2.runtime import Macro
|
||||
|
||||
from api.od import ODAPI
|
||||
from jinja2 import Environment, FileSystemLoader, meta
|
||||
|
||||
|
||||
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
|
||||
|
||||
name_dict = lambda item: {"name": self.api.get_name(item)}
|
||||
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)),
|
||||
"gate_from": self.api.get_slot_value(item, "gate_from"),
|
||||
"gate_to": self.api.get_slot_value(item, "gate_to"),
|
||||
}
|
||||
|
||||
conn_data_event = {"Match": lambda item: False,
|
||||
"Rewrite": lambda item: False,
|
||||
"Data_modify": lambda item: True,
|
||||
"Loop": lambda item: True,
|
||||
"Print": lambda item: get_slot_value_default(item, "event", False)
|
||||
}
|
||||
conn_data_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)),
|
||||
"event": conn_data_event[self.api.get_type_name(target := self.api.get_target(item))](target)
|
||||
}
|
||||
rewrite_dict = lambda item: {"name": self.api.get_name(item),
|
||||
"file": self.api.get_slot_value(item, "file"),
|
||||
}
|
||||
match_dict = lambda item: {"name": self.api.get_name(item),
|
||||
"file": self.api.get_slot_value(item, "file"),
|
||||
"n": self.api.get_slot_value(item, "n") \
|
||||
if "n" in self.api.get_slots(item) else 'float("inf")'
|
||||
}
|
||||
data_modify_dict = lambda item: {"name": self.api.get_name(item),
|
||||
"dict": json.loads(self.api.get_slot_value(item, "modify_dict"))
|
||||
}
|
||||
loop_dict = lambda item: {"name": self.api.get_name(item),
|
||||
"choise": get_slot_value_default(item, "choise", False)}
|
||||
print_dict = lambda item: {"name": self.api.get_name(item),
|
||||
"label": get_slot_value_default(item, "label", "")}
|
||||
arg_map = {"Start": name_dict, "End": name_dict,
|
||||
"Match": match_dict, "Rewrite": rewrite_dict,
|
||||
"Data_modify": data_modify_dict, "Loop": loop_dict,
|
||||
"Exec_con": conn_dict, "Data_con": conn_data_dict,
|
||||
"Print": print_dict}
|
||||
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 generate_schedule(self, stream = sys.stdout):
|
||||
start = self.api.get_all_instances("Start")[0][1]
|
||||
stack = [start]
|
||||
out = {"blocks":[], "exec_conn":[], "data_conn":[], "match_files":set(), "matchers":[], "start":self.api.get_name(start)}
|
||||
execBlocks = set()
|
||||
exec_conn = list()
|
||||
|
||||
while len(stack) > 0:
|
||||
exec_obj = stack.pop()
|
||||
if exec_obj in execBlocks:
|
||||
continue
|
||||
execBlocks.add(exec_obj)
|
||||
for conn in self.api.get_outgoing(exec_obj, "Exec_con"):
|
||||
exec_conn.append(conn)
|
||||
stack.append(self.api.get_target(conn))
|
||||
|
||||
stack = list(execBlocks)
|
||||
data_blocks = set()
|
||||
for name, p in self.api.get_all_instances("Print"):
|
||||
if "event" in (event := self.api.get_slots(p)) and event:
|
||||
stack.append(p)
|
||||
execBlocks.add(p)
|
||||
|
||||
|
||||
data_conn = set()
|
||||
while len(stack) > 0:
|
||||
obj = stack.pop()
|
||||
for data_c in self.api.get_incoming(obj, "Data_con"):
|
||||
data_conn.add(data_c)
|
||||
source = self.api.get_source(data_c)
|
||||
if not self.api.is_instance(source, "Exec") and \
|
||||
source not in execBlocks and \
|
||||
source not in data_blocks:
|
||||
stack.append(source)
|
||||
data_blocks.add(source)
|
||||
|
||||
for exec_item in execBlocks:
|
||||
out["blocks"].append(self._render(exec_item))
|
||||
if self.api.is_instance(exec_item, "Rule"):
|
||||
d = self.macro_args[self.api.get_type_name(exec_item)][1](exec_item)
|
||||
out["match_files"].add(d["file"])
|
||||
out["matchers"].append(d)
|
||||
for exec_c in exec_conn:
|
||||
out["exec_conn"].append(self._render(exec_c))
|
||||
|
||||
for data_c in data_conn:
|
||||
out["data_conn"].append(self._render(data_c))
|
||||
|
||||
for data_b in data_blocks:
|
||||
out["blocks"].append(self._render(data_b))
|
||||
|
||||
print(self.template_wrap.render(out), file=stream)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# print("with open('test.dot', 'w') as f:", file=stream)
|
||||
# print(f"\tf.write({self.api.get_name(start)}.generate_dot())", file=stream)
|
||||
26
examples/schedule/models/README.md
Normal file
26
examples/schedule/models/README.md
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
|
||||
### association Exec_con
|
||||
Integer gate_from;
|
||||
Integer gate_to;
|
||||
|
||||
### association Data_con
|
||||
|
||||
### class Start [1..1]
|
||||
### class End [1..*]
|
||||
|
||||
|
||||
### class Match
|
||||
optional Integer n;
|
||||
|
||||
### class Rewrite
|
||||
|
||||
### class Data_modify
|
||||
String modify_dict;
|
||||
|
||||
### class Loop
|
||||
optional Boolean choise;
|
||||
|
||||
## debugging tools
|
||||
|
||||
### class Print(In_Exec, Out_Exec, In_Data)
|
||||
optional Boolean event;
|
||||
46
examples/schedule/models/scheduling_MM.od
Normal file
46
examples/schedule/models/scheduling_MM.od
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
abstract class Exec
|
||||
abstract class In_Exec(Exec)
|
||||
abstract class Out_Exec(Exec)
|
||||
|
||||
association Exec_con [0..*] Out_Exec -> In_Exec [0..*] {
|
||||
Integer gate_from;
|
||||
Integer gate_to;
|
||||
}
|
||||
|
||||
abstract class Data
|
||||
abstract class In_Data(Data)
|
||||
abstract class Out_Data(Data)
|
||||
association Data_con [0..*] Out_Data -> In_Data [0..*]
|
||||
|
||||
class Start [1..1] (Out_Exec)
|
||||
class End [1..*] (In_Exec)
|
||||
|
||||
|
||||
abstract class Rule (In_Exec, Out_Exec, In_Data, Out_Data)
|
||||
{
|
||||
String file;
|
||||
}
|
||||
class Match (Rule)
|
||||
{
|
||||
optional Integer n;
|
||||
}
|
||||
|
||||
class Rewrite (Rule)
|
||||
|
||||
class Data_modify(In_Data, Out_Data)
|
||||
{
|
||||
String modify_dict;
|
||||
}
|
||||
|
||||
class Loop(In_Exec, Out_Exec, In_Data, Out_Data)
|
||||
{
|
||||
optional Boolean choise;
|
||||
}
|
||||
|
||||
# debugging tools
|
||||
|
||||
class Print(In_Exec, Out_Exec, In_Data)
|
||||
{
|
||||
optional Boolean event;
|
||||
optional String label;
|
||||
}
|
||||
12
examples/schedule/schedule_lib/__init__.py
Normal file
12
examples/schedule/schedule_lib/__init__.py
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
from .data_node import DataNode
|
||||
from .data_modify import DataModify
|
||||
from .end import End
|
||||
from .exec_node import ExecNode
|
||||
from .loop import Loop
|
||||
from .match import Match
|
||||
from .null_node import NullNode
|
||||
from .print import Print
|
||||
from .rewrite import Rewrite
|
||||
from .start import Start
|
||||
|
||||
__all__ = ["DataNode", "End", "ExecNode", "Loop", "Match", "NullNode", "Rewrite", "Print", "DataModify", "Start"]
|
||||
63
examples/schedule/schedule_lib/data.py
Normal file
63
examples/schedule/schedule_lib/data.py
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
import functools
|
||||
from typing import Any, Generator, Callable
|
||||
|
||||
|
||||
class Data:
|
||||
def __init__(self, super) -> None:
|
||||
self.data: list[dict[Any, Any]] = list()
|
||||
self.success: bool = False
|
||||
self.super = super
|
||||
|
||||
@staticmethod
|
||||
def store_output(func: Callable) -> Callable:
|
||||
def wrapper(self, *args, **kwargs) -> Any:
|
||||
output = func(self, *args, **kwargs)
|
||||
self.success = output
|
||||
return output
|
||||
return wrapper
|
||||
|
||||
@store_output
|
||||
def store_data(self, data_gen: Generator, n: int) -> bool:
|
||||
self.data.clear()
|
||||
if n == 0:
|
||||
return True
|
||||
i: int = 0
|
||||
while (match := next(data_gen, None)) is not None:
|
||||
self.data.append(match)
|
||||
i+=1
|
||||
if i >= n:
|
||||
break
|
||||
else:
|
||||
if n == float("inf"):
|
||||
return bool(len(self.data))
|
||||
self.data.clear()
|
||||
return False
|
||||
return True
|
||||
|
||||
def get_super(self) -> int:
|
||||
return self.super
|
||||
|
||||
def replace(self, data: "Data") -> None:
|
||||
self.data.clear()
|
||||
self.data.extend(data.data)
|
||||
|
||||
def append(self, data: Any) -> None:
|
||||
self.data.append(data)
|
||||
|
||||
def clear(self) -> None:
|
||||
self.data.clear()
|
||||
|
||||
def pop(self, index = -1) -> Any:
|
||||
return self.data.pop(index)
|
||||
|
||||
def empty(self) -> bool:
|
||||
return len(self.data) == 0
|
||||
|
||||
def __getitem__(self, index):
|
||||
return self.data[index]
|
||||
|
||||
def __iter__(self):
|
||||
return self.data.__iter__()
|
||||
|
||||
def __len__(self):
|
||||
return self.data.__len__()
|
||||
26
examples/schedule/schedule_lib/data_modify.py
Normal file
26
examples/schedule/schedule_lib/data_modify.py
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import functools
|
||||
from typing import TYPE_CHECKING, Callable, List
|
||||
|
||||
from api.od import ODAPI
|
||||
from examples.schedule.RuleExecuter import RuleExecuter
|
||||
from .exec_node import ExecNode
|
||||
from .data_node import DataNode
|
||||
|
||||
|
||||
class DataModify(DataNode):
|
||||
def __init__(self, modify_dict: dict[str,str]) -> None:
|
||||
DataNode.__init__(self)
|
||||
self.modify_dict: dict[str,str] = modify_dict
|
||||
|
||||
def input_event(self, success: bool) -> None:
|
||||
if success or self.data_out.success:
|
||||
self.data_out.data.clear()
|
||||
for data in self.data_in.data:
|
||||
self.data_out.append({self.modify_dict[key]: value for key, value in data.items() if key in self.modify_dict.keys()})
|
||||
DataNode.input_event(self, success)
|
||||
|
||||
def generate_dot(self, nodes: List[str], edges: List[str], visited: set[int]) -> None:
|
||||
if self.id in visited:
|
||||
return
|
||||
nodes.append(f"{self.id}[label=modify]")
|
||||
super().generate_dot(nodes, edges, visited)
|
||||
47
examples/schedule/schedule_lib/data_node.py
Normal file
47
examples/schedule/schedule_lib/data_node.py
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
from typing import Any, Generator, List
|
||||
|
||||
from examples.schedule.schedule_lib.id_generator import IdGenerator
|
||||
from .data import Data
|
||||
|
||||
class DataNode:
|
||||
def __init__(self) -> None:
|
||||
if not hasattr(self, 'id'):
|
||||
self.id = IdGenerator().generate_id()
|
||||
self.data_out : Data = Data(self)
|
||||
self.data_in: Data | None = None
|
||||
self.eventsub: list[DataNode] = list()
|
||||
|
||||
def connect_data(self, data_node: "DataNode", eventsub=True) -> None:
|
||||
data_node.data_in = self.data_out
|
||||
if eventsub:
|
||||
self.eventsub.append(data_node)
|
||||
|
||||
def store_data(self, data_gen: Generator, n: int) -> None:
|
||||
success: bool = self.data_out.store_data(data_gen, n)
|
||||
for sub in self.eventsub:
|
||||
sub.input_event(success)
|
||||
|
||||
def get_input_data(self) -> list[dict[Any, Any]]:
|
||||
if not self.data_in.success:
|
||||
raise Exception("Invalid input data: matching has failed")
|
||||
data = self.data_in.data
|
||||
if len(data) == 0:
|
||||
raise Exception("Invalid input data: no data present")
|
||||
return data
|
||||
|
||||
def input_event(self, success: bool) -> None:
|
||||
self.data_out.success = success
|
||||
for sub in self.eventsub:
|
||||
sub.input_event(success)
|
||||
|
||||
def get_id(self) -> int:
|
||||
return self.id
|
||||
|
||||
def generate_dot(self, nodes: List[str], edges: List[str], visited: set[int]) -> None:
|
||||
visited.add(self.id)
|
||||
if self.data_in is not None:
|
||||
edges.append(f"{self.data_in.get_super().get_id()} -> {self.get_id()} [color = green]")
|
||||
self.data_in.get_super().generate_dot(nodes, edges, visited)
|
||||
for sub in self.eventsub:
|
||||
sub.generate_dot(nodes, edges, visited)
|
||||
|
||||
21
examples/schedule/schedule_lib/end.py
Normal file
21
examples/schedule/schedule_lib/end.py
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import functools
|
||||
from typing import TYPE_CHECKING, List, Callable, Generator
|
||||
|
||||
from api.od import ODAPI
|
||||
from .exec_node import ExecNode
|
||||
|
||||
class End(ExecNode):
|
||||
def __init__(self) -> None:
|
||||
super().__init__(out_connections=1)
|
||||
|
||||
def execute(self, od: ODAPI) -> Generator | None:
|
||||
return self.terminate(od)
|
||||
|
||||
@staticmethod
|
||||
def terminate(od: ODAPI) -> Generator:
|
||||
yield f"end:", functools.partial(lambda od:(od, ""), od)
|
||||
|
||||
def generate_dot(self, nodes: List[str], edges: List[str], visited: set[int]) -> None:
|
||||
if self.id in visited:
|
||||
return
|
||||
nodes.append(f"{self.id}[label=end]")
|
||||
34
examples/schedule/schedule_lib/exec_node.py
Normal file
34
examples/schedule/schedule_lib/exec_node.py
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
from typing import TYPE_CHECKING, List, Callable, Generator
|
||||
from api.od import ODAPI
|
||||
|
||||
from .id_generator import IdGenerator
|
||||
|
||||
class ExecNode:
|
||||
def __init__(self, out_connections: int = 1) -> None:
|
||||
from .null_node import NullNode
|
||||
self.next_state: list[ExecNode] = []
|
||||
if out_connections > 0:
|
||||
self.next_state = [NullNode()]*out_connections
|
||||
self.id: int = IdGenerator().generate_id()
|
||||
|
||||
def nextState(self) -> "ExecNode":
|
||||
return self.next_state[0]
|
||||
|
||||
def connect(self, next_state: "ExecNode", from_gate: int = 0, to_gate: int = 0) -> None:
|
||||
if from_gate >= len(self.next_state):
|
||||
raise IndexError
|
||||
self.next_state[from_gate] = next_state
|
||||
|
||||
def execute(self, od: ODAPI) -> Generator | None:
|
||||
return None
|
||||
|
||||
def get_id(self) -> int:
|
||||
return self.id
|
||||
|
||||
def generate_dot(self, nodes: List[str], edges: List[str], visited: set[int]) -> None:
|
||||
visited.add(self.id)
|
||||
for edge in self.next_state:
|
||||
edges.append(f"{self.id} -> {edge.get_id()}")
|
||||
for next in self.next_state:
|
||||
next.generate_dot(nodes, edges, visited)
|
||||
|
||||
10
examples/schedule/schedule_lib/funcs.py
Normal file
10
examples/schedule/schedule_lib/funcs.py
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
from typing import Callable
|
||||
|
||||
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
|
||||
8
examples/schedule/schedule_lib/id_generator.py
Normal file
8
examples/schedule/schedule_lib/id_generator.py
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
from .singleton import Singleton
|
||||
|
||||
class IdGenerator(metaclass=Singleton):
|
||||
def __init__(self):
|
||||
self.id = -1
|
||||
def generate_id(self) -> int:
|
||||
self.id += 1
|
||||
return self.id
|
||||
57
examples/schedule/schedule_lib/loop.py
Normal file
57
examples/schedule/schedule_lib/loop.py
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
import functools
|
||||
from random import choice
|
||||
from typing import TYPE_CHECKING, Callable, List, Generator
|
||||
|
||||
from api.od import ODAPI
|
||||
from examples.schedule.RuleExecuter import RuleExecuter
|
||||
from .exec_node import ExecNode
|
||||
from .data_node import DataNode
|
||||
from .data_node import Data
|
||||
|
||||
|
||||
class Loop(ExecNode, DataNode):
|
||||
def __init__(self, choice) -> None:
|
||||
ExecNode.__init__(self, out_connections=2)
|
||||
DataNode.__init__(self)
|
||||
self.choice: bool = choice
|
||||
self.cur_data: Data = Data(-1)
|
||||
|
||||
def nextState(self) -> ExecNode:
|
||||
return self.next_state[not self.data_out.success]
|
||||
|
||||
def execute(self, od: ODAPI) -> Generator | None:
|
||||
if self.cur_data.empty():
|
||||
self.data_out.clear()
|
||||
self.data_out.success = False
|
||||
DataNode.input_event(self, False)
|
||||
return None
|
||||
|
||||
if self.choice:
|
||||
def select_data() -> Generator:
|
||||
for i in range(len(self.cur_data)):
|
||||
yield f"choice: {self.cur_data[i]}", functools.partial(self.select_next,od, i)
|
||||
return select_data()
|
||||
else:
|
||||
self.select_next(od, -1)
|
||||
return None
|
||||
|
||||
def input_event(self, success: bool) -> None:
|
||||
if (b := self.data_out.success) or success:
|
||||
self.cur_data.replace(self.data_in)
|
||||
self.data_out.clear()
|
||||
self.data_out.success = False
|
||||
if b:
|
||||
DataNode.input_event(self, False)
|
||||
|
||||
def select_next(self,od: ODAPI, index: int) -> tuple[ODAPI, list[str]]:
|
||||
self.data_out.clear()
|
||||
self.data_out.append(self.cur_data.pop(index))
|
||||
DataNode.input_event(self, True)
|
||||
return (od, ["data selected"])
|
||||
|
||||
def generate_dot(self, nodes: List[str], edges: List[str], visited: set[int]) -> None:
|
||||
if self.id in visited:
|
||||
return
|
||||
nodes.append(f"{self.id}[label=Loop]")
|
||||
ExecNode.generate_dot(self, nodes, edges, visited)
|
||||
DataNode.generate_dot(self, nodes, edges, visited)
|
||||
42
examples/schedule/schedule_lib/match.py
Normal file
42
examples/schedule/schedule_lib/match.py
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import functools
|
||||
from typing import TYPE_CHECKING, Callable, List, Generator
|
||||
|
||||
from api.od import ODAPI
|
||||
from examples.schedule.RuleExecuter import RuleExecuter
|
||||
from .exec_node import ExecNode
|
||||
from .data_node import DataNode
|
||||
|
||||
|
||||
class Match(ExecNode, DataNode):
|
||||
def __init__(self, label: str, n: int | float) -> None:
|
||||
ExecNode.__init__(self, out_connections=2)
|
||||
DataNode.__init__(self)
|
||||
self.label: str = label
|
||||
self.n:int = n
|
||||
self.rule = None
|
||||
self.rule_executer : RuleExecuter
|
||||
|
||||
def nextState(self) -> ExecNode:
|
||||
return self.next_state[not self.data_out.success]
|
||||
|
||||
def execute(self, od: ODAPI) -> Generator | None:
|
||||
self.match(od)
|
||||
return None
|
||||
|
||||
def init_rule(self, rule, rule_executer):
|
||||
self.rule = rule
|
||||
self.rule_executer = rule_executer
|
||||
|
||||
def match(self, od: ODAPI) -> None:
|
||||
pivot = {}
|
||||
if self.data_in is not None:
|
||||
pivot = self.get_input_data()[0]
|
||||
print(f"matching: {self.label}\n\tpivot: {pivot}")
|
||||
self.store_data(self.rule_executer.match_rule(od.m, self.rule, pivot=pivot), self.n)
|
||||
|
||||
def generate_dot(self, nodes: List[str], edges: List[str], visited: set[int]) -> None:
|
||||
if self.id in visited:
|
||||
return
|
||||
nodes.append(f"{self.id}[label=M_{self.label.split("/")[-1]}_{self.n}]")
|
||||
ExecNode.generate_dot(self, nodes, edges, visited)
|
||||
DataNode.generate_dot(self, nodes, edges, visited)
|
||||
25
examples/schedule/schedule_lib/null_node.py
Normal file
25
examples/schedule/schedule_lib/null_node.py
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import functools
|
||||
from symtable import Function
|
||||
from typing import List, Callable, Generator
|
||||
|
||||
from api.od import ODAPI
|
||||
from .singleton import Singleton
|
||||
|
||||
from .exec_node import ExecNode
|
||||
|
||||
class NullNode(ExecNode, metaclass=Singleton):
|
||||
def __init__(self):
|
||||
ExecNode.__init__(self, out_connections=0)
|
||||
|
||||
def execute(self, od: ODAPI) -> Generator | None:
|
||||
raise Exception('Null node should already have terminated the schedule')
|
||||
|
||||
@staticmethod
|
||||
def terminate(od: ODAPI):
|
||||
return None
|
||||
yield # verrrry important line, dont remove this unreachable code
|
||||
|
||||
def generate_dot(self, nodes: List[str], edges: List[str], visited: set[int]) -> None:
|
||||
if self.id in visited:
|
||||
return
|
||||
nodes.append(f"{self.id}[label=Null]")
|
||||
28
examples/schedule/schedule_lib/print.py
Normal file
28
examples/schedule/schedule_lib/print.py
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import functools
|
||||
from typing import TYPE_CHECKING, Callable, List, Generator
|
||||
|
||||
from api.od import ODAPI
|
||||
from examples.schedule.RuleExecuter import RuleExecuter
|
||||
from .exec_node import ExecNode
|
||||
from .data_node import DataNode
|
||||
|
||||
|
||||
class Print(ExecNode, DataNode):
|
||||
def __init__(self, label: str = "") -> None:
|
||||
ExecNode.__init__(self, out_connections=1)
|
||||
DataNode.__init__(self)
|
||||
self.label = label
|
||||
|
||||
def execute(self, od: ODAPI) -> Generator | None:
|
||||
self.input_event(True)
|
||||
return None
|
||||
|
||||
def input_event(self, success: bool) -> None:
|
||||
print(f"{self.label}{self.data_in.data}")
|
||||
|
||||
def generate_dot(self, nodes: List[str], edges: List[str], visited: set[int]) -> None:
|
||||
if self.id in visited:
|
||||
return
|
||||
nodes.append(f"{self.id}[label=Print_{self.label.replace(":", "")}]")
|
||||
ExecNode.generate_dot(self, nodes, edges, visited)
|
||||
DataNode.generate_dot(self, nodes, edges, visited)
|
||||
38
examples/schedule/schedule_lib/rewrite.py
Normal file
38
examples/schedule/schedule_lib/rewrite.py
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import functools
|
||||
from typing import List, Callable, Generator
|
||||
|
||||
from api.od import ODAPI
|
||||
from .exec_node import ExecNode
|
||||
from .data_node import DataNode
|
||||
from ..RuleExecuter import RuleExecuter
|
||||
|
||||
|
||||
class Rewrite(ExecNode, DataNode):
|
||||
def __init__(self, label: str) -> None:
|
||||
ExecNode.__init__(self, out_connections=1)
|
||||
DataNode.__init__(self)
|
||||
self.label = label
|
||||
self.rule = None
|
||||
self.rule_executer : RuleExecuter
|
||||
|
||||
def init_rule(self, rule, rule_executer):
|
||||
self.rule = rule
|
||||
self.rule_executer= rule_executer
|
||||
|
||||
def execute(self, od: ODAPI) -> Generator | None:
|
||||
yield "ghello", functools.partial(self.rewrite, od)
|
||||
|
||||
def rewrite(self, od):
|
||||
print("rewrite" + self.label)
|
||||
pivot = {}
|
||||
if self.data_in is not None:
|
||||
pivot = self.get_input_data()[0]
|
||||
self.store_data(self.rule_executer.rewrite_rule(od.m, self.rule, pivot=pivot), 1)
|
||||
return ODAPI(od.state, od.m, od.mm),[f"rewrite {self.label}\n\tpivot: {pivot}\n\t{"success" if self.data_out.success else "failure"}\n"]
|
||||
|
||||
def generate_dot(self, nodes: List[str], edges: List[str], visited: set[int]) -> None:
|
||||
if self.id in visited:
|
||||
return
|
||||
nodes.append(f"{self.id}[label=R_{self.label.split("/")[-1]}]")
|
||||
ExecNode.generate_dot(self, nodes, edges, visited)
|
||||
DataNode.generate_dot(self, nodes, edges, visited)
|
||||
8
examples/schedule/schedule_lib/singleton.py
Normal file
8
examples/schedule/schedule_lib/singleton.py
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
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]
|
||||
16
examples/schedule/schedule_lib/start.py
Normal file
16
examples/schedule/schedule_lib/start.py
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
from typing import TYPE_CHECKING, Callable, List, Any
|
||||
|
||||
from .funcs import generate_dot_wrap
|
||||
|
||||
from .exec_node import ExecNode
|
||||
|
||||
|
||||
class Start(ExecNode):
|
||||
def __init__(self) -> None:
|
||||
ExecNode.__init__(self, out_connections=1)
|
||||
|
||||
def generate_dot(self, nodes: List[str], edges: List[str], visited: set[int]) -> None:
|
||||
if self.id in visited:
|
||||
return
|
||||
nodes.append(f"{self.id}[label=start]")
|
||||
super().generate_dot(nodes, edges, visited)
|
||||
9
examples/schedule/templates/schedule_dot.j2
Normal file
9
examples/schedule/templates/schedule_dot.j2
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
digraph G {
|
||||
{% for node in nodes %}
|
||||
{{ node }}
|
||||
{% endfor %}
|
||||
|
||||
{% for edge in edges %}
|
||||
{{ edge }}
|
||||
{% endfor %}
|
||||
}
|
||||
35
examples/schedule/templates/schedule_template.j2
Normal file
35
examples/schedule/templates/schedule_template.j2
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
{% macro Start(name) %}
|
||||
{{ name }} = Start()
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro End(name) %}
|
||||
{{ name }} = End()
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro Match(name, file, n) %}
|
||||
{{ name }} = Match("{{ file }}", {{ n }})
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro Rewrite(name, file) %}
|
||||
{{ name }} = Rewrite("{{ file }}")
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro Data_modify(name, dict) %}
|
||||
{{ name }} = DataModify({{ dict }})
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro Exec_con(name_from, name_to, gate_from, gate_to) %}
|
||||
{{ name_from }}.connect({{ name_to }},{{ gate_from }},{{ gate_to }})
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro Data_con(name_from, name_to, event) %}
|
||||
{{ name_from }}.connect_data({{ name_to }}, {{ event }})
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro Loop(name, choise) %}
|
||||
{{ name }} = Loop({{ choise }})
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro Print(name, label) %}
|
||||
{{ name }} = Print("{{ label }}")
|
||||
{%- endmacro %}
|
||||
47
examples/schedule/templates/schedule_template_wrap.j2
Normal file
47
examples/schedule/templates/schedule_template_wrap.j2
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
from examples.schedule.schedule_lib import *
|
||||
|
||||
class Schedule:
|
||||
def __init__(self, rule_executer):
|
||||
self.start: Start
|
||||
self.cur: ExecNode = None
|
||||
self.rule_executer = rule_executer
|
||||
|
||||
def __call__(self, od):
|
||||
self.cur = self.cur.nextState()
|
||||
while not isinstance(self.cur, NullNode):
|
||||
action_gen = self.cur.execute(od)
|
||||
if action_gen is not None:
|
||||
# if (action_gen := self.cur.execute(od)) is not None:
|
||||
return action_gen
|
||||
self.cur = self.cur.nextState()
|
||||
return NullNode.terminate(od)
|
||||
|
||||
@staticmethod
|
||||
def get_matchers():
|
||||
return [
|
||||
{% for file in match_files %}
|
||||
"{{ file }}.od",
|
||||
{% endfor %}
|
||||
]
|
||||
|
||||
def init_schedule(self, matchers):
|
||||
{% for block in blocks%}
|
||||
{{ block }}
|
||||
{% endfor %}
|
||||
|
||||
{% for conn in exec_conn%}
|
||||
{{ conn }}
|
||||
{% endfor %}
|
||||
{% for conn_d in data_conn%}
|
||||
{{ conn_d }}
|
||||
{% endfor %}
|
||||
self.start = {{ start }}
|
||||
self.cur = {{ start }}
|
||||
|
||||
{% for match in matchers %}
|
||||
{{ match["name"] }}.init_rule(matchers["{{ match["file"] }}.od"], self.rule_executer)
|
||||
{% endfor %}
|
||||
return None
|
||||
|
||||
def generate_dot(self, *args, **kwargs):
|
||||
return self.start.generate_dot(*args, **kwargs)
|
||||
|
|
@ -22,7 +22,6 @@ class TryAgainNextRound(Exception):
|
|||
|
||||
# Rewrite is performed in-place (modifying `host_m`)
|
||||
def rewrite(state,
|
||||
lhs_m: UUID, # LHS-pattern
|
||||
rhs_m: UUID, # RHS-pattern
|
||||
pattern_mm: UUID, # meta-model of both patterns (typically the RAMified host_mm)
|
||||
lhs_match: dict, # a match, morphism, from lhs_m to host_m (mapping pattern name -> host name), typically found by the 'match_od'-function.
|
||||
|
|
|
|||
|
|
@ -117,7 +117,6 @@ class RuleMatcherRewriter:
|
|||
|
||||
try:
|
||||
rhs_match = rewrite(self.state,
|
||||
lhs_m=lhs,
|
||||
rhs_m=rhs,
|
||||
pattern_mm=self.mm_ramified,
|
||||
lhs_match=lhs_match,
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@ class RandomDecisionMaker(DecisionMaker):
|
|||
|
||||
def __call__(self, actions):
|
||||
arr = [action for descr, action in actions]
|
||||
if len(arr) == 0:
|
||||
return
|
||||
i = math.floor(self.r.random()*len(arr))
|
||||
return arr[i]
|
||||
|
||||
|
|
@ -91,7 +93,7 @@ class MinimalSimulator:
|
|||
self._print("Start simulation")
|
||||
self._print(f"Decision maker: {self.decision_maker}")
|
||||
step_counter = 0
|
||||
while True:
|
||||
while step_counter < 10:
|
||||
termination_reason = self.termination_condition(model)
|
||||
if termination_reason != None:
|
||||
self._print(f"Termination condition satisfied.\nReason: {termination_reason}.")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue