@h2loop/autosar 1.9.0 → 1.9.1
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.
package/package.json
CHANGED
|
@@ -20,7 +20,7 @@ from typing import Optional
|
|
|
20
20
|
from . import defs as D
|
|
21
21
|
from .model import Elem, Project
|
|
22
22
|
from .validate import _resolve_def
|
|
23
|
-
from .values import int_forms, to_json
|
|
23
|
+
from .values import derived, int_forms, to_json
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
def key_of(e: Elem) -> str:
|
|
@@ -42,6 +42,10 @@ def value_info(project: Project, e: Elem, pv) -> dict:
|
|
|
42
42
|
nums = int_forms(pd, pv.value, pv.is_ref, numerical, pv.def_dest)
|
|
43
43
|
if nums:
|
|
44
44
|
out.update(nums)
|
|
45
|
+
if pd is not None and pd.kind == "FLOAT":
|
|
46
|
+
dv = derived(pd.name, pv.value)
|
|
47
|
+
if dv:
|
|
48
|
+
out["means"] = dv
|
|
45
49
|
return out
|
|
46
50
|
|
|
47
51
|
|
|
@@ -49,6 +53,8 @@ def _put(out: dict, name: str, items: list, multi: bool):
|
|
|
49
53
|
one = lambda xs: xs if multi else xs[0] # noqa: E731
|
|
50
54
|
out[name] = one([x["value"] for x in items])
|
|
51
55
|
out[f"@at:{name}"] = one([x["at"] for x in items])
|
|
56
|
+
if any("means" in x for x in items):
|
|
57
|
+
out[f"@means:{name}"] = one([x.get("means") for x in items])
|
|
52
58
|
if any("hex" in x for x in items):
|
|
53
59
|
out[f"@hex:{name}"] = one([x.get("hex") for x in items])
|
|
54
60
|
if any("dec" in x and x["value"] != x["dec"] for x in items):
|
|
@@ -24,7 +24,7 @@ from typing import Optional
|
|
|
24
24
|
from . import defs as D
|
|
25
25
|
from .model import CONTAINER_VALUE, Elem, Project
|
|
26
26
|
from .validate import _resolve_def, validate
|
|
27
|
-
from .values import check_value, expected_value_tag, from_json
|
|
27
|
+
from .values import check_value, derived, expected_value_tag, from_json
|
|
28
28
|
from .xmltree import Node, escape, line_indent
|
|
29
29
|
|
|
30
30
|
CONTAINER_ORDER = ["DEFINITION-REF", "PARAMETER-VALUES", "REFERENCE-VALUES", "SUB-CONTAINERS", "VARIATION-POINT"]
|
|
@@ -412,6 +412,65 @@ def _unwrap_ref(v):
|
|
|
412
412
|
return v
|
|
413
413
|
|
|
414
414
|
|
|
415
|
+
def _kids(project: Project, path: str) -> dict:
|
|
416
|
+
"""{SHORT-NAME: child path} of a value path ('' = package level: the module configurations)."""
|
|
417
|
+
out = {}
|
|
418
|
+
for e in project.find(path):
|
|
419
|
+
for c in e.children:
|
|
420
|
+
out[c.name] = c.path
|
|
421
|
+
return out
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
def _child_def_names(project: Project, path: str) -> set:
|
|
425
|
+
"""Container def names a container at ``path`` may hold (from its def, else from its present children)."""
|
|
426
|
+
names = set()
|
|
427
|
+
for e in project.find(path):
|
|
428
|
+
d = _resolve_def(project.defs, e.def_ref) if project.defs else None
|
|
429
|
+
if d is not None:
|
|
430
|
+
names |= {project.defs.by_path[x].name for x in d.containers if x in project.defs.by_path}
|
|
431
|
+
names |= {(c.def_ref or "").rsplit("/", 1)[-1] for c in e.children}
|
|
432
|
+
return names - {""}
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
def canonical_path(project: Project, path: str):
|
|
436
|
+
"""Rewrite the path dialects agents write into the value path, or None.
|
|
437
|
+
|
|
438
|
+
Handles container DEF names used as path segments ('.../GptChannelConfigSet/GptChannelConfiguration/
|
|
439
|
+
GptChannel_Delay' -> '.../GptChannelConfigSet/GptChannel_Delay') and a missing module segment
|
|
440
|
+
('/ActiveEcuC/GptDriverConfiguration' -> '/ActiveEcuC/Gpt/GptDriverConfiguration'). Unresolvable trailing
|
|
441
|
+
segments are kept (a parameter name, or the container an add creates). Returns (path, dropped_def) where
|
|
442
|
+
dropped_def is a def name dropped at the END of the path (the def of the container an add should create)."""
|
|
443
|
+
segs = [s for s in path.strip().strip("/").split("/") if s]
|
|
444
|
+
if len(segs) < 2:
|
|
445
|
+
return None
|
|
446
|
+
mods = {e.name: e.path for es in project.by_path.values() for e in es if e.parent is None}
|
|
447
|
+
pkg = "/" + segs[0]
|
|
448
|
+
if not any(p.startswith(pkg + "/") for p in mods.values()):
|
|
449
|
+
return None
|
|
450
|
+
cur, i, dropped, changed = None, 1, None, False
|
|
451
|
+
rest: list = []
|
|
452
|
+
while i < len(segs):
|
|
453
|
+
s = segs[i]
|
|
454
|
+
kids = {n: p for n, p in mods.items() if p.startswith(pkg + "/")} if cur is None else _kids(project, cur)
|
|
455
|
+
if s in kids:
|
|
456
|
+
cur, dropped = kids[s], None
|
|
457
|
+
elif cur is None:
|
|
458
|
+
owners = [mp for mp in kids.values() if s in _kids(project, mp)]
|
|
459
|
+
if len(owners) != 1:
|
|
460
|
+
return None
|
|
461
|
+
cur, changed = _kids(project, owners[0])[s], True
|
|
462
|
+
elif s in _child_def_names(project, cur):
|
|
463
|
+
dropped, changed = s, True
|
|
464
|
+
else:
|
|
465
|
+
rest = segs[i:]
|
|
466
|
+
break
|
|
467
|
+
i += 1
|
|
468
|
+
if not changed or cur is None:
|
|
469
|
+
return None
|
|
470
|
+
out = cur + ("/" + "/".join(rest) if rest else "")
|
|
471
|
+
return out, (dropped if not rest else None)
|
|
472
|
+
|
|
473
|
+
|
|
415
474
|
def normalize_ops(project: Project, ops, notes: list):
|
|
416
475
|
"""Accept the shapes agents use for ECUC edits (arxml_edit's keys, SHORT-NAME given separately, refs as objects,
|
|
417
476
|
params added to a container that does not exist yet) and rewrite them into ecuc_edit's own; note each rewrite."""
|
|
@@ -428,6 +487,21 @@ def normalize_ops(project: Project, ops, notes: list):
|
|
|
428
487
|
if "path" not in op and isinstance(op.get("parent_path"), str):
|
|
429
488
|
op["path"] = op.pop("parent_path")
|
|
430
489
|
notes.append(f"op {i}: 'parent_path' read as 'path'")
|
|
490
|
+
for rk in ("refs", "references", "reference_values"):
|
|
491
|
+
if isinstance(op.get(rk), dict):
|
|
492
|
+
rv = op.pop(rk)
|
|
493
|
+
op["values"] = {**(op.get("values") if isinstance(op.get("values"), dict) else {}), **rv}
|
|
494
|
+
notes.append(f"op {i}: '{rk}' merged into values")
|
|
495
|
+
pth = op.get("path")
|
|
496
|
+
if isinstance(pth, str) and pth.startswith("/") and not project.find(pth.rstrip("/")):
|
|
497
|
+
cp = canonical_path(project, pth)
|
|
498
|
+
if cp is not None:
|
|
499
|
+
op["path"], ddef = cp
|
|
500
|
+
notes.append(f"op {i}: path {pth} read as {op['path']}")
|
|
501
|
+
c = op.get("container")
|
|
502
|
+
if ddef and op.get("op") == "add" and isinstance(c, str) and ":" not in c:
|
|
503
|
+
op["container"] = f"{ddef}:{c}"
|
|
504
|
+
notes.append(f"op {i}: container {c!r} read as {op['container']!r}")
|
|
431
505
|
sn = None
|
|
432
506
|
for k in ("short_name", "name", "shortName"):
|
|
433
507
|
if isinstance(op.get(k), str):
|
|
@@ -560,6 +634,16 @@ def edit(project: Project, ops: list, dry_run: bool = False, force: bool = False
|
|
|
560
634
|
try:
|
|
561
635
|
ops = normalize_ops(project, ops, ed.notes)
|
|
562
636
|
ed.apply(ops, force=force)
|
|
637
|
+
for op in ops if isinstance(ops, list) else []:
|
|
638
|
+
if not isinstance(op, dict):
|
|
639
|
+
continue
|
|
640
|
+
vals = dict(op.get("values") or {}) if isinstance(op.get("values"), dict) else {}
|
|
641
|
+
if op.get("op") == "set" and "value" in op:
|
|
642
|
+
vals[op.get("param") or str(op.get("path", "")).rstrip("/").rsplit("/", 1)[-1]] = op["value"]
|
|
643
|
+
for k, v in vals.items():
|
|
644
|
+
dv = derived(str(k), v)
|
|
645
|
+
if dv:
|
|
646
|
+
ed.notes.append(f"{k} = {v}: {dv}; check this matches the requirement")
|
|
563
647
|
except EditError as e:
|
|
564
648
|
return {"ok": False, "error": str(e), "written": False, "notes": ed.notes}
|
|
565
649
|
changes = ed.diffs()
|
|
@@ -170,3 +170,26 @@ def int_forms(d: Optional[DefNode], raw: Optional[str], is_ref: bool, numerical:
|
|
|
170
170
|
if v is None:
|
|
171
171
|
return None
|
|
172
172
|
return {"dec": v, "hex": hex_str(v)}
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _si(x: float, unit: str) -> str:
|
|
176
|
+
for f, pre in ((1e9, "G"), (1e6, "M"), (1e3, "k"), (1, ""), (1e-3, "m"), (1e-6, "\u00b5"), (1e-9, "n"), (1e-12, "p")):
|
|
177
|
+
if abs(x) >= f:
|
|
178
|
+
return f"{x / f:.4g} {pre}{unit}"
|
|
179
|
+
return f"{x:.4g} {unit}"
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def derived(name: str, raw) -> Optional[str]:
|
|
183
|
+
"""Human reading of a frequency / baud rate value, with its period: helps catch unit slips
|
|
184
|
+
(100 ns resolution is a 10 MHz tick, not 100 kHz). None when the param is not one of those."""
|
|
185
|
+
try:
|
|
186
|
+
v = float(str(raw).strip())
|
|
187
|
+
except (TypeError, ValueError):
|
|
188
|
+
return None
|
|
189
|
+
if v <= 0:
|
|
190
|
+
return None
|
|
191
|
+
if name.endswith("Frequency"):
|
|
192
|
+
return f"{_si(v, 'Hz')} = {_si(1 / v, 's')} per tick"
|
|
193
|
+
if name.endswith("BaudRate"): # ECUC CAN/LIN baud rates are in kbit/s
|
|
194
|
+
return f"{_si(v * 1e3, 'bit/s')} = {_si(1 / (v * 1e3), 's')} per bit"
|
|
195
|
+
return None
|