robotransform 0.0.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
File without changes
@@ -0,0 +1,286 @@
1
+ from __future__ import annotations
2
+ from typing import Type as ClassType, Any, List, Optional, Union
3
+
4
+
5
+ # TODO Flatten this a little bit
6
+
7
+ class Node:
8
+ def __init__(self, parent: Optional[Any] = None):
9
+ self.parent = parent
10
+
11
+
12
+ class ControllerDef(Node):
13
+ def __init__(self, name, uses, provides, requires, connections, machines, events, operations, variables,
14
+ parent: Any):
15
+ super().__init__(parent)
16
+ self.name = name
17
+ self.uses = uses
18
+ self.provides = provides
19
+ self.requires = requires
20
+ self.connections = connections
21
+ self.machines = machines
22
+ self.events = events
23
+ self.operations = operations
24
+ self.variables = variables
25
+
26
+ def __repr__(self):
27
+ use_count = len(self.uses)
28
+ provide_count = len(self.provides)
29
+ require_count = len(self.requires)
30
+ connection_count = len(self.connections)
31
+ machine_count = len(self.machines)
32
+ event_count = len(self.events)
33
+ operation_count = len(self.operations)
34
+ variable_count = len(self.variables)
35
+ return f"{self.name}: ({use_count} uses), ({provide_count} provides), ({require_count} requires), ({connection_count} connections), ({machine_count} machines, ({event_count} events), ({operation_count} operations), ({variable_count} variables)"
36
+
37
+
38
+ class Variable(Node):
39
+ def __init__(self, name: str, kind, initial, parent: Any):
40
+ super().__init__(parent)
41
+ self.name = name
42
+ self.type = kind
43
+ self.initial = initial
44
+
45
+
46
+ class Type(Node):
47
+ def __init__(self, source: FunctionType, target: Optional[Type], parent: Any):
48
+ super().__init__(parent)
49
+ self.source = source
50
+ self.target = target
51
+
52
+ def __repr__(self):
53
+ if self.target:
54
+ return f"{self.source} <-> {self.target}"
55
+ return f"{self.source}"
56
+
57
+
58
+ class Field(Node):
59
+ def __init__(self, name: str, kind: Type, parent: Any):
60
+ super().__init__(parent)
61
+ self.name = name
62
+ self.type = kind
63
+
64
+ @property
65
+ def typename(self) -> str:
66
+ return str(self.type)
67
+
68
+ def __repr__(self):
69
+ return f"{self.name}[{self.type}]"
70
+
71
+
72
+ class RecordType(Node):
73
+ def __init__(self, name: str, fields: List[Field], parent: Any):
74
+ # Can be datatype or record
75
+ super().__init__(parent)
76
+ self.name = name
77
+ self.fields = fields
78
+
79
+ def __repr__(self):
80
+ return f"{self.name}({self.fields})"
81
+
82
+
83
+ class Function(Node):
84
+ def __init__(self, name: str, parameters, kind, body, parent: Any):
85
+ super().__init__(parent)
86
+ self.name = name
87
+ self.parameters = parameters
88
+ self.kind = kind
89
+ self.body = body
90
+
91
+
92
+ class RCModule(Node):
93
+ def __init__(self, name: str, connections, nodes, parent: Any):
94
+ super().__init__(parent)
95
+ self.name = name
96
+ self.connections = connections
97
+ self.nodes = nodes
98
+
99
+
100
+ class VariableList(Node):
101
+ def __init__(self, modifier, variables, parent: Any):
102
+ super().__init__(parent)
103
+ self.modifier = modifier
104
+ self.variables = variables
105
+
106
+
107
+ class Interface(Node):
108
+ def __init__(self, name: str, operations, events, variables: List[VariableList], clocks, parent: Any):
109
+ super().__init__(parent)
110
+ self.name = name
111
+ self.operations = operations
112
+ self.events = events
113
+ self._variables = variables
114
+ self.clocks = clocks
115
+
116
+ @property
117
+ def variables(self) -> List[Variable]:
118
+ return [var for v in self._variables for var in v.variables]
119
+
120
+
121
+ class RoboticPlatformDef(Node):
122
+ def __init__(self, name: str, uses, provides, requires, variables, operations, events, parent: Any):
123
+ super().__init__(parent)
124
+ self.name = name
125
+ self.uses = uses
126
+ self.provides = provides
127
+ self.requires = requires
128
+ self._variables = variables
129
+ self.operations = operations
130
+ self.events = events
131
+
132
+ @property
133
+ def variables(self) -> List[Variable]:
134
+ return [var for v in self._variables for var in v.variables]
135
+
136
+
137
+ class OperationDef(Node):
138
+ def __init__(self, name: str, parent: Any):
139
+ super().__init__(parent)
140
+ self.name = name
141
+
142
+
143
+ class RCPackage(Node):
144
+ def __init__(self, name: QualifiedName, imports: List[Import], controllers: List[ControllerDef],
145
+ modules: List[RCModule], functions: List[Function], types: List[RecordType],
146
+ machines: List[StateMachineDef], interfaces: List[Interface], robots: List[RoboticPlatformDef],
147
+ operations: List[OperationDef]):
148
+ super().__init__()
149
+ self.name = name
150
+ self.imports = imports
151
+ self.controllers = controllers
152
+ self.modules = modules
153
+ self.functions = functions
154
+ self.types = types
155
+ self.machines = machines
156
+ self.interfaces = interfaces
157
+ self.robots = robots
158
+ self.operations = operations
159
+
160
+ def __repr__(self):
161
+ return f"{self.name}"
162
+
163
+
164
+ class QualifiedName(Node):
165
+ def __init__(self, parts: List[str], parent: Any):
166
+ super().__init__(parent)
167
+ self.parts = parts
168
+
169
+ def __repr__(self):
170
+ return "".join(self.parts)
171
+
172
+
173
+ class StateMachineDef(Node):
174
+ def __init__(self, name, interfaces, provides, requires, variables, events, nodes, transitions, clocks,
175
+ parent: Any):
176
+ super().__init__(parent)
177
+ self.name = name
178
+ self.interfaces = interfaces
179
+ self.provides = provides
180
+ self.requires = requires
181
+ self._variables = variables
182
+ self.events = events
183
+ self.nodes = nodes
184
+ self.clocks = clocks
185
+ self.transitions = transitions
186
+
187
+ @property
188
+ def variables(self) -> List[Variable]:
189
+ return [var for v in self._variables for var in v.variables]
190
+
191
+
192
+ class Transition(Node):
193
+ def __init__(self, name, source, target, trigger, reset, deadline, condition, action, parent: Any):
194
+ super().__init__(parent)
195
+ self.name = name
196
+ self.source = source
197
+ self.target = target
198
+
199
+
200
+ class QualifiedNameWithWildcard(Node):
201
+ def __init__(self, name, parent: Any):
202
+ super().__init__(parent)
203
+ self.name = name
204
+
205
+ def __repr__(self):
206
+ return str(self.name) + "::*"
207
+
208
+
209
+ class Import(Node):
210
+ def __init__(self, name, parent: Any):
211
+ super().__init__(parent)
212
+ self.name = name
213
+
214
+ def __repr__(self):
215
+ return str(self.name)
216
+
217
+
218
+ class FunctionType(Node):
219
+ def __init__(self, source: ProductType, target: Optional[FunctionType], parent: Any):
220
+ super().__init__(parent)
221
+ self.source = source
222
+ self.target = target
223
+
224
+ def __repr__(self):
225
+ if self.target:
226
+ return f"{self.source} -> {self.target}"
227
+ return f"{self.source}"
228
+
229
+
230
+ class TypeRef(Node):
231
+ def __init__(self, kind, parent: Any):
232
+ super().__init__(parent)
233
+ self.type = kind
234
+
235
+ def __repr__(self):
236
+ return f"{self.type}"
237
+
238
+
239
+ class VectorType(Node):
240
+ # def __init__(self, source: Union[VectorDef, MatrixDef, TypeRef], parent: Any):
241
+ def __init__(self, source: Union[TypeRef], parent: Any):
242
+ super().__init__(parent)
243
+ self.source = source
244
+
245
+ def __repr__(self):
246
+ return f"{self.source}"
247
+
248
+
249
+ class ProductType(Node):
250
+ def __init__(self, types: List[Union[VectorType, TypeRef]], parent: Any):
251
+ super().__init__(parent)
252
+ self.types = types
253
+
254
+ def __repr__(self):
255
+ if len(self.types) > 1:
256
+ joined = "*".join(map(str, self.types))
257
+ return f"{joined}]"
258
+ return f"{self.types[0]}"
259
+
260
+
261
+ class SeqType(Node):
262
+ def __init__(self, domain, parent: Any):
263
+ super().__init__(parent)
264
+ self.domain = domain
265
+
266
+ def __repr__(self):
267
+ return f"Seq({self.domain})"
268
+
269
+
270
+ class Event(Node):
271
+ def __init__(self, name: str, broadcast: bool, kind: Type, parent: Any):
272
+ super().__init__(parent)
273
+ self.name = name
274
+ self.broadcast = broadcast
275
+ self.type = kind
276
+
277
+ def __repr__(self):
278
+ return f"Event: {self.name} {self.broadcast, self.type}"
279
+
280
+
281
+ def all_concepts() -> List[ClassType]:
282
+ import inspect
283
+ return [
284
+ obj for _, obj in globals().items()
285
+ if inspect.isclass(obj) and obj.__module__ == __name__
286
+ ]
@@ -0,0 +1,12 @@
1
+ def typename_to_port(type_name: str) -> str:
2
+ event_types = {"event"}
3
+ data_types = {"int", "integer", "real", "float", "double", "string"}
4
+ mixed_types = {"event_data", "event data"}
5
+
6
+ if type_name in event_types:
7
+ return "event"
8
+ if type_name in data_types:
9
+ return "data"
10
+ if type_name in mixed_types:
11
+ return "event data"
12
+ return "data"
robotransform/main.py ADDED
@@ -0,0 +1,146 @@
1
+ from dataclasses import dataclass, fields
2
+ from functools import lru_cache
3
+ from pathlib import Path
4
+
5
+ import arklog
6
+ from textx import metamodel_from_str
7
+ from jinja2 import Environment, FileSystemLoader, Template
8
+ import io
9
+ from typing import Union, Optional, Iterable, Iterator
10
+
11
+ from robotransform.concepts import all_concepts, RCPackage
12
+ from robotransform.filters import typename_to_port
13
+
14
+
15
+ @dataclass
16
+ class Store:
17
+ packages: Iterable[Path]
18
+
19
+ def __iter__(self) -> Iterator[Path]:
20
+ for f in fields(self):
21
+ value = getattr(self, f.name)
22
+ if isinstance(value, Path):
23
+ yield value
24
+ continue
25
+ if isinstance(value, Iterable):
26
+ for item in value:
27
+ if isinstance(item, Path):
28
+ yield item
29
+ continue
30
+
31
+ def parse_imports(self, package: RCPackage, parent_path: Path, visited):
32
+ stack = [package]
33
+ while stack:
34
+ package = stack.pop()
35
+ for imp in package.imports:
36
+ name = imp.name.name.parts[-1]
37
+ if name in visited:
38
+ continue
39
+ path = parent_path / f"{name}.rct"
40
+ if not path.exists():
41
+ arklog.debug(f"Can't import {path}.")
42
+ continue
43
+ subpkg = parse_robochart(path)
44
+ visited[path] = subpkg
45
+ stack.append(subpkg)
46
+ return visited
47
+
48
+ def load(self, imports: bool = True) -> dict[Path, RCPackage]:
49
+ visited = {}
50
+ for path in self:
51
+ package = parse_robochart(path)
52
+ visited[path] = package
53
+ if imports:
54
+ visited |= self.parse_imports(package, path.parent, visited)
55
+ return visited
56
+
57
+
58
+ @dataclass
59
+ class MapleKStore(Store):
60
+ monitor: Path
61
+ analysis: Path
62
+ plan: Path
63
+ legitimate: Path
64
+ execute: Path
65
+ knowledge: Path
66
+
67
+ def __init__(self, monitor: Path, analysis: Path, plan: Path, legitimate: Path, execute: Path, knowledge: Path,
68
+ additional: Optional[Iterable[Path]] = None):
69
+ self.monitor = monitor
70
+ self.analysis = analysis
71
+ self.plan = plan
72
+ self.legitimate = legitimate
73
+ self.execute = execute
74
+ self.knowledge = knowledge
75
+ self.packages = [monitor, analysis, plan, legitimate, execute, knowledge]
76
+ if additional is not None:
77
+ self.packages += additional
78
+
79
+ def verify(self):
80
+ pass
81
+
82
+
83
+ @lru_cache(maxsize=1)
84
+ def get_robochart_metamodel(name: str = "robochart.tx"):
85
+ arklog.debug(f"Loading ({name}) metamodel.")
86
+ metamodel_path = Path(__file__).resolve().parent / name
87
+ metamodel = metamodel_from_str(metamodel_path.read_text(), classes=all_concepts(), memoization=True)
88
+ return metamodel
89
+
90
+
91
+ @lru_cache(maxsize=1)
92
+ def parse_robochart(source: Path | str) -> RCPackage:
93
+ arklog.debug(f"Parsing ({source}).")
94
+ metamodel = get_robochart_metamodel()
95
+ data = source.read_text() if isinstance(source, Path) else source
96
+ return metamodel.model_from_str(data)
97
+
98
+
99
+ @lru_cache(maxsize=2)
100
+ def get_template(name: str) -> Template:
101
+ environment = Environment(loader=FileSystemLoader(Path(__file__).resolve().parent / "templates"))
102
+ environment.filters["typename_to_port"] = typename_to_port # For use with pipes
103
+ # environment.globals["typename_to_port"] = typename_to_port # For use as a function
104
+ templates = {
105
+ "messages": "messages.aadl",
106
+ "logical": "logical.aadl",
107
+ }
108
+ if found := templates.get(name):
109
+ return environment.get_template(found)
110
+ else:
111
+ raise ValueError(f"No template found for name '{name}'")
112
+
113
+
114
+ def write_output(data: str, output: Optional[Union[io.TextIOBase, Path, str]] = None) -> str:
115
+ if output is None:
116
+ return data
117
+ if isinstance(output, (str, Path)):
118
+ output = Path(output)
119
+ output.parent.mkdir(parents=True, exist_ok=True)
120
+ output.write_text(data)
121
+ elif isinstance(output, io.TextIOBase):
122
+ output.write(data)
123
+ output.flush()
124
+ else:
125
+ raise TypeError(f"Unsupported output type: {type(output)}.")
126
+ return data
127
+
128
+
129
+ def dump_messages(store: Store, output: Optional[Union[io.TextIOBase, Path, str]] = None) -> None:
130
+ template = get_template("messages")
131
+ output = output if output else Path("output/generated/messages/messages.aadl")
132
+ write_output(template.render(packages=store.load().values()), output)
133
+
134
+
135
+ def dump_logical(store: Store, output: Optional[Union[io.TextIOBase, Path, str]] = None) -> None:
136
+ template = get_template("logical")
137
+ output = output if output else Path("output/generated/LogicalArchitecture.aadl")
138
+ write_output(template.render(packages=store.load().values()), output)
139
+
140
+
141
+ def main():
142
+ pass
143
+
144
+
145
+ if __name__ == "__main__":
146
+ main()