Skip to content

Commit

Permalink
Merge branch 'main' into feature-fix-validate-error
Browse files Browse the repository at this point in the history
  • Loading branch information
sveinse authored Dec 3, 2024
2 parents 10ab189 + b1f4cec commit 7323f94
Show file tree
Hide file tree
Showing 12 changed files with 514 additions and 353 deletions.
4 changes: 2 additions & 2 deletions src/objdictgen/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
from objdictgen.node import Node
from objdictgen.nodemanager import NodeManager

__version__ = "3.5.2"
__version_tuple__ = (3, 5, 2, 0)
__version__ = "3.5.3a1"
__version_tuple__ = (3, 5, 3, 1)
__copyright__ = "(c) 2024 Svein Seldal, Laerdal Medical AS, and several. Licensed under GPLv2.1."

# Shortcuts
Expand Down
54 changes: 1 addition & 53 deletions src/objdictgen/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,59 +119,6 @@ def _printlines(entries: TDiffEntries):
_printlines(diffs[index])


def list_od(
od: "Node",
name: str,
opts: argparse.Namespace) -> Generator[str, None, None]:
"""Generator for producing the output for odg list"""

# Get the indexes to print and determine the order
keys = od.GetAllIndices(sort=opts.sort)
if opts.index:
indexp = [jsonod.str_to_int(i) for i in opts.index]
keys = [k for k in keys if k in indexp]
missing = ", ".join((str(k) for k in indexp if k not in keys))
if missing:
raise ValueError(f"Unknown index {missing}")

profiles = []
if od.DS302:
loaded, equal = jsonod.compare_profile("DS-302", od.DS302)
if equal:
extra = "DS-302 (equal)"
elif loaded:
extra = "DS-302 (not equal)"
else:
extra = "DS-302 (not loaded)"
profiles.append(extra)

pname = od.ProfileName
if pname and pname != 'None':
loaded, equal = jsonod.compare_profile(pname, od.Profile, od.SpecificMenu)
if equal:
extra = f"{pname} (equal)"
elif loaded:
extra = f"{pname} (not equal)"
else:
extra = f"{pname} (not loaded)"
profiles.append(extra)

if not opts.compact:
yield f"{Fore.CYAN}File:{Style.RESET_ALL} {name}"
yield f"{Fore.CYAN}Name:{Style.RESET_ALL} {od.Name} [{od.Type.upper()}] {od.Description}"
tp = ", ".join(profiles) or None
yield f"{Fore.CYAN}Profiles:{Style.RESET_ALL} {tp}"
if od.ID:
yield f"{Fore.CYAN}ID:{Style.RESET_ALL} {od.ID}"
yield ""

# Print the parameters
yield from od.GetPrintEntry(
keys=keys, short=opts.short, compact=opts.compact, unused=opts.unused,
verbose=opts.all, raw=opts.raw
)


@debug_wrapper()
def main(debugopts: DebugOpts, args: Sequence[str]|None = None):
""" Main command dispatcher """
Expand Down Expand Up @@ -261,6 +208,7 @@ def main(debugopts: DebugOpts, args: Sequence[str]|None = None):
subp.add_argument('--raw', action="store_true", help="Show raw parameter values")
subp.add_argument('--short', action="store_true", help="Do not list sub-index")
subp.add_argument('--unused', action="store_true", help="Include unused profile parameters")
subp.add_argument('--internal', action="store_true", help="Show internal data")
subp.add_argument('-D', '--debug', **opt_debug) # type: ignore[arg-type]
subp.add_argument('--novalidate', **opt_novalidate) # type: ignore[arg-type]
subp.add_argument('--no-color', action='store_true', help="Disable colored output")
Expand Down
49 changes: 2 additions & 47 deletions src/objdictgen/jsonod.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
from objdictgen.maps import OD, ODMapping, ODMappingList
from objdictgen.typing import (TDiffNodes, TIndexEntry, TODJson, TODObjJson,
TODObj, TODSubObj, TODSubObjJson, TODValue, TParamEntry, TPath, TProfileMenu)
from objdictgen.utils import (copy_in_order, exc_amend, maybe_number,
str_to_int)

T = TypeVar('T')
M = TypeVar('M', bound=Mapping)
Expand Down Expand Up @@ -182,53 +184,6 @@ def _re_sub(match: re.Match[str]) -> str:
return RE_JSONC.sub(_re_sub, text,)


# FIXME: Move to generic utils/funcs?
def exc_amend(exc: Exception, text: str) -> Exception:
""" Helper to prefix text to an exception """
args = list(exc.args)
if len(args) > 0:
args[0] = text + str(args[0])
else:
args.append(text)
exc.args = tuple(args)
return exc


def str_to_int(string: str|int) -> int:
""" Convert string or int to int. Fail if not possible."""
i = maybe_number(string)
if not isinstance(i, int):
raise ValueError(f"Expected integer, got '{string}'")
return i


def maybe_number(string: str|int) -> int|str:
""" Convert string to a number, otherwise pass it through as-is"""
if isinstance(string, int):
return string
s = string.strip()
if s.startswith('0x') or s.startswith('-0x'):
return int(s.replace('0x', ''), 16)
if s.isdigit():
return int(string)
return string


def copy_in_order(d: M, order: Sequence[T]) -> M:
""" Remake dict d with keys in order """
out = {
k: d[k]
for k in order
if k in d
}
out.update({
k: v
for k, v in d.items()
if k not in out
})
return cast(M, out) # FIXME: For mypy


def remove_underscore(d: T) -> T:
""" Recursively remove any keys prefixed with '__' """
if isinstance(d, dict):
Expand Down
197 changes: 0 additions & 197 deletions src/objdictgen/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@
from pathlib import Path
from typing import Any, Generator, Iterable, Iterator

import colorama

# The following import needs care when importing node
from objdictgen import eds_utils, gen_cfile, jsonod, maps, nosis
from objdictgen.maps import OD, ODMapping, ODMappingList
Expand All @@ -33,9 +31,6 @@

log = logging.getLogger('objdictgen')

Fore = colorama.Fore
Style = colorama.Style


# ------------------------------------------------------------------------------
# Definition of Node Object
Expand Down Expand Up @@ -1000,198 +995,6 @@ def _warn(text: str):
subvals["name"] = f"Subindex {idx}"
_warn(f"FIX: Set name to '{subvals['name']}'")

# --------------------------------------------------------------------------
# Printing and output
# --------------------------------------------------------------------------

def GetPrintEntryHeader(
self, index: int, unused=False, compact=False, raw=False,
entry: TIndexEntry|None = None
) -> tuple[str, dict[str, str]]:

# Get the information about the index if it wasn't passed along
if not entry:
entry = self.GetIndexEntry(index, withbase=True)
obj = entry["object"]

# Get the flags for the entry
flags: set[str] = set()
for group in entry["groups"]:
v = {
"built-in": None,
"user": "User",
"ds302": "DS-302",
"profile": "Profile",
}.get(group, group)
if v:
flags.add(v)
if obj.get('need'):
flags.add("Mandatory")
if entry.get("params", {}).get("callback"):
flags.add('CB')
if "dictionary" not in entry:
if "ds302" in entry["groups"] or "profile" in entry["groups"]:
flags.add("Unused")
else:
flags.add("Missing")

# Skip printing if the entry is unused and we are not printing unused
if 'Unused' in flags and not unused:
return '', {}

# Replace flags for formatting
for _, flag in enumerate(flags.copy()):
if flag == 'Missing':
flags.discard('Missing')
flags.add(Fore.RED + ' *MISSING* ' + Style.RESET_ALL)

# Print formattings
idx = (index - entry.get("base", index)) // obj.get("incr", 1) + 1
t_name = obj['name']
if not raw:
t_name = maps.eval_name(t_name, idx=idx, sub=0)
t_flags = ', '.join(flags)
t_string = maps.ODStructTypes.to_string(obj['struct']) or '???'

# ** PRINT PARAMETER **
return "{pre}{key} {name} {struct}{flags}", {
'key': f"{Fore.LIGHTGREEN_EX}0x{index:04x} ({index}){Style.RESET_ALL}",
'name': f"{Fore.LIGHTWHITE_EX}{t_name}{Style.RESET_ALL}",
'struct': f"{Fore.LIGHTYELLOW_EX}[{t_string.upper()}]{Style.RESET_ALL}",
'flags': f" {Fore.MAGENTA}{t_flags}{Style.RESET_ALL}" if flags else '',
'pre': ' ' if not compact else '',
}

def GetPrintEntry(
self, keys: list[int]|None = None, short=False, compact=False,
unused=False, verbose=False, raw=False,
) -> Generator[str, None, None]:
"""
Generator for printing the dictionary values
"""

# Get the indexes to print and determine the order
keys = keys or self.GetAllIndices(sort=True)

index_range = None
for k in keys:

# Get the index entry information
param = self.GetIndexEntry(k, withbase=True)
obj = param["object"]

# Get the header for the entry
line, fmt = self.GetPrintEntryHeader(
k, unused=unused, compact=compact, entry=param, raw=raw
)
if not line:
continue

# Print the parameter range header
ir = maps.INDEX_RANGES.get_index_range(k)
if index_range != ir:
index_range = ir
if not compact:
yield Fore.YELLOW + ir.description + Style.RESET_ALL

# Yield the parameter header
yield line.format(**fmt)

# Omit printing sub index data if:
if short:
continue

# Fetch the dictionary values and the parameters, if present
if k in self.Dictionary:
values = self.GetEntry(k, aslist=True, compute=not raw)
else:
values = ['__N/A__'] * len(obj["values"])
if k in self.ParamsDictionary:
params = self.GetParamsEntry(k, aslist=True)
else:
params = [maps.DEFAULT_PARAMS] * len(obj["values"])
# For mypy to ensure that values and entries are lists
assert isinstance(values, list) and isinstance(params, list)

infos = []
for i, (value, param) in enumerate(zip(values, params)):

# Prepare data for printing
info = self.GetSubentryInfos(k, i)
typename = self.GetTypeName(info['type'])

# Type specific formatting of the value
if value == "__N/A__":
t_value = f'{Fore.LIGHTBLACK_EX}N/A{Style.RESET_ALL}'
elif isinstance(value, str):
length = len(value)
if typename == 'DOMAIN':
value = value.encode('unicode_escape').decode()
t_value = '"' + value + f'" ({length})'
elif i and index_range and index_range.name in ('rpdom', 'tpdom'):
# FIXME: In PDO mappings, the value is ints
assert isinstance(value, int)
index, subindex, _ = self.GetMapIndex(value)
try:
pdo = self.GetSubentryInfos(index, subindex)
t_v = f"{value:x}"
t_value = f"0x{t_v[0:4]}_{t_v[4:6]}_{t_v[6:]} {Fore.LIGHTCYAN_EX}{pdo['name']}{Style.RESET_ALL}"
except ValueError:
suffix = ' ???' if value else ''
t_value = f"0x{value:x}{suffix}"
elif i and value and (k in (4120, ) or 'COB ID' in info["name"]):
t_value = f"0x{value:x}"
else:
t_value = str(value)

# Add comment if present
t_comment = param['comment'] or ''
if t_comment:
t_comment = f"{Fore.LIGHTBLACK_EX}/* {t_comment} */{Style.RESET_ALL}"

# Omit printing the first element unless specifically requested
if (not verbose and i == 0
and obj['struct'] & OD.MultipleSubindexes
and not t_comment
):
continue

# Print formatting
infos.append({
'i': f"{Fore.GREEN}{i:02d}{Style.RESET_ALL}",
'access': info['access'],
'pdo': 'P' if info['pdo'] else ' ',
'name': info['name'],
'type': f"{Fore.LIGHTBLUE_EX}{typename}{Style.RESET_ALL}",
'value': t_value,
'comment': t_comment,
'pre': fmt['pre'],
})

# Must skip the next step if list is empty, as the first element is
# used for the header
if not infos:
continue

# Calculate the max width for each of the columns
w = {
col: max(len(str(row[col])) for row in infos) or ''
for col in infos[0]
}

# Generate a format string based on the calculcated column widths
# Legitimate use of % as this is making a string containing format specifiers
fmt = "{pre} {i:%ss} {access:%ss} {pdo:%ss} {name:%ss} {type:%ss} {value:%ss} {comment}" % (
w["i"], w["access"], w["pdo"], w["name"], w["type"], w["value"]
)

# Print each line using the generated format string
for infoentry in infos:
yield fmt.format(**infoentry)

if not compact and infos:
yield ""


# Register node with gnosis
nosis.add_class_to_store('Node', Node)
7 changes: 4 additions & 3 deletions src/objdictgen/nodelist.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@

import errno
import os
from pathlib import Path
import shutil
from dataclasses import dataclass
from pathlib import Path

from objdictgen import eds_utils
from objdictgen.node import Node
from objdictgen.nodemanager import NodeManager
from objdictgen.printing import format_node
from objdictgen.typing import TODObj, TODSubObj, TPath

# ------------------------------------------------------------------------------
Expand Down Expand Up @@ -269,11 +270,11 @@ def main(projectdir):
print("MasterNode :")
node = manager.CurrentNode
if node:
for line in node.GetPrintEntry(raw=True):
for line in format_node(node, "MasterNode", raw=True):
print(line)
print()
for nodeid, nodeinfo in nodelist.SlaveNodes.items():
print(f"SlaveNode name={nodeinfo.Name} id=0x{nodeid:02X} :")
for line in nodeinfo.Node.GetPrintEntry():
for line in format_node(nodeinfo.Node, nodeinfo.Name):
print(line)
print()
Loading

0 comments on commit 7323f94

Please sign in to comment.