A fully working version of the scheduling language with added examples

This commit is contained in:
robbe 2025-06-27 12:21:41 +02:00
parent ec42f74960
commit ebfd85a666
126 changed files with 7235 additions and 981 deletions

View 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)