Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Zultron/2022 09 19 rebase for PRs: 5 pdo config #18

Draft
wants to merge 46 commits into
base: foxy-devel
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
8207612
Base tests: Minor updates
zultron Jun 8, 2022
04063cc
base class: Reset `feeback_in` interface in `ready()` method
zultron Jul 15, 2022
3f159f5
cia_301: Pass `**kwargs` through config to command class
zultron Sep 14, 2022
2ec70fe
lcec: Add option to suppress `ethercat` cmd stderr output
zultron Sep 14, 2022
0063009
cia_301: Add method to dump drive params to config object
zultron Sep 14, 2022
86f5e7a
lcec: Test fixture tweak
zultron Jun 11, 2022
541bb31
lcec: Accept negative numbers when setting int-type params
zultron Jul 21, 2022
06543b7
lcec: Add parsing of `ethercat upload -t string` output
zultron Sep 14, 2022
fb7286e
mgr: Catch KeyboardInterrupt in main loop
zultron Jun 24, 2022
d6c8ea8
cia_301: Redo device_config munging
zultron Jun 7, 2022
9e2db0a
cia_402: Tweak log messages
zultron Jul 15, 2022
dbc6679
cia_402: Don't print redundant "Goal not reached" logs
zultron Jul 21, 2022
e12f9ef
errors: Fix class bitrot
zultron Jun 24, 2022
9c8d297
errors: When error code changes, log error code & description
zultron Jul 21, 2022
f044723
devices: Update SV660 ESI, adding extra objects from manual
zultron Sep 13, 2022
4d5cb1d
device base class: Replace `index` attribute with `addr_slug`
zultron Jun 29, 2022
9829c98
hal: Generate HAL pin prefix from `address` attribute
zultron Jun 29, 2022
54d997b
hal: Conditionally use 64-bit int data types
zultron May 18, 2022
968030e
mgr: Following base class, remove `index` arg from `init()`
zultron Sep 19, 2022
de3d129
config_io: Initial commit
zultron May 19, 2022
ddbd99b
base class: Migrate YAML access to ConfigIO and `importlib` refs
zultron May 19, 2022
5d3d4cb
cia_301: Migrate YAML access to ConfigIO and `importlib` refs
zultron May 19, 2022
ba77ba8
cia_402: Migrate YAML access to ConfigIO and `importlib` refs
zultron Jun 5, 2022
d9a5441
errors: Migrate YAML access to ConfigIO and `importlib` refs
zultron May 19, 2022
2c9618f
errors: Use importlib to read device error data files
zultron Jun 3, 2022
752087e
cia_301: Add distributed clock configuration
zultron Jun 9, 2022
2697568
cia_301: Key SDO, DCs data by model_id, not name
zultron Jun 11, 2022
d12f0f7
cia_301: Update device config & tests
zultron Jun 15, 2022
6bf1cbc
cia_301: Add trivial test for lcec <complexEntry>
zultron Jun 20, 2022
1bf6c14
devices: Migrate YAML access to ConfigIO and `importlib` refs
zultron Jun 3, 2022
ff32bb0
cia_301: Fix tests to work without `test_category`
zultron Jun 20, 2022
c2b2914
ethercat: Migrate YAML and ESI XML to ConfigIO and `importlib` refs
zultron Jun 3, 2022
bd4a90c
ethercat: Parse ESI <Dc/> tags; redo file reading logic
zultron Jun 7, 2022
fa9ee6c
lcec: Migrate YAML and ESI XML to ConfigIO and `importlib` refs
zultron Jun 3, 2022
173e8f2
devices: Add DC tests
zultron Jun 11, 2022
d7a9ed4
mgr: Migrate YAML access to ConfigIO and `importlib` refs
zultron May 19, 2022
f17cbd8
ethercat: ESI partial support for PDO parsing
zultron Jun 14, 2022
55dbfba
mgr_ros: Migrate YAML access to ConfigIO and `importlib` refs
zultron May 19, 2022
9bcf0f7
lcec: Generate `ethercat.conf.xml` configuration from bus scan
zultron Jun 15, 2022
02edf82
mgr_hal: Migrate YAML access to ConfigIO and `importlib` refs
zultron Jun 3, 2022
663a4d6
lcec: Add finer control over <complexEntry> elements
zultron Jun 20, 2022
e502cda
mgr_ros_hal: Migrate YAML access to ConfigIO and `importlib` refs
zultron Jun 5, 2022
be32852
devices: Update Bogus device ESI descriptions
zultron Jun 15, 2022
aa3df9b
Merge remote-tracking branch 'origin/foxy-devel' into zultron/2022-09…
zultron Sep 22, 2022
e88697b
Merge branch 'zultron/2022-09-19-rebase_for_PRs-3-minor-core' into zu…
zultron Nov 3, 2022
6968245
Merge branch 'zultron/2022-09-19-rebase_for_PRs-4-config_io' into zul…
zultron Nov 3, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion hw_device_mgr/cia_301/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ def scan_bus(self, bus=0):
"""Scan bus, returning list of addresses and IDs for each device."""

@abc.abstractmethod
def upload(self, address=None, index=None, subindex=0, datatype=None):
def upload(
self, address=None, index=None, subindex=0, datatype=None, **kwargs
):
"""Upload a value from a device SDO."""

@abc.abstractmethod
Expand All @@ -36,6 +38,7 @@ def download(
subindex=0,
value=None,
datatype=None,
**kwargs,
):
"""Download a value to a device SDO."""

Expand Down
110 changes: 70 additions & 40 deletions hw_device_mgr/cia_301/config.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from .data_types import CiA301DataType
from .command import CiA301Command, CiA301SimCommand
from .command import CiA301Command, CiA301SimCommand, CiA301CommandException
from .sdo import CiA301SDO
from functools import cached_property


class CiA301Config:
Expand Down Expand Up @@ -28,11 +29,12 @@ class CiA301Config:

# Mapping of model_id to a dict of (index, subindex) to SDO object
_model_sdos = dict()
# Mapping of model_id to a dict of (index, subindex) to DC object
_model_dcs = dict()

def __init__(self, address=None, model_id=None):
self.address = address
self.model_id = self.format_model_id(model_id)
self._config = None

@classmethod
def format_model_id(cls, model_id):
Expand Down Expand Up @@ -98,28 +100,57 @@ def sdo_ix(cls, ix):
ix = (dtc.uint16(ix[0]), dtc.uint8(ix[1]))
return ix

@cached_property
def sdos(self):
assert self.model_id in self._model_sdos
return self._model_sdos[self.model_id].values()

def sdo(self, ix):
if isinstance(ix, self.sdo_class):
return ix
ix = self.sdo_ix(ix)
return self._model_sdos[self.model_id][ix]

@classmethod
def add_device_dcs(cls, dcs_data):
"""Add device model distributed clock descriptions."""
for model_id, dcs in dcs_data.items():
assert isinstance(dcs, list)
cls._model_dcs[model_id] = dcs
assert None not in cls._model_dcs

def dcs(self):
"""Get list of distributed clocks for this device."""
return self._model_dcs[self.model_id]

def dump_param_values(self):
res = dict()
for sdo in self.sdos:
try:
res[sdo] = self.upload(sdo, stderr_to_devnull=True)
except CiA301CommandException as e:
# Objects may not exist, like variable length PDO mappings
self.logger.debug(f"Upload {sdo} failed: {e}")
pass
return res

#
# Param read/write
#

def upload(self, sdo):
def upload(self, sdo, **kwargs):
# Get SDO object
sdo = self.sdo(sdo)
res_raw = self.command().upload(
address=self.address,
index=sdo.index,
subindex=sdo.subindex,
datatype=sdo.data_type,
**kwargs,
)
return sdo.data_type(res_raw)

def download(self, sdo, val, dry_run=False):
def download(self, sdo, val, dry_run=False, **kwargs):
# Get SDO object
sdo = self.sdo(sdo)
# Check before setting value to avoid unnecessary NVRAM writes
Expand All @@ -128,6 +159,7 @@ def download(self, sdo, val, dry_run=False):
index=sdo.index,
subindex=sdo.subindex,
datatype=sdo.data_type,
**kwargs,
)
if sdo.data_type(res_raw) == val:
return # SDO value already correct
Expand All @@ -140,6 +172,7 @@ def download(self, sdo, val, dry_run=False):
subindex=sdo.subindex,
value=val,
datatype=sdo.data_type,
**kwargs,
)

#
Expand Down Expand Up @@ -172,8 +205,7 @@ def set_device_config(cls, config):
- `pdo_mapping`: PDO mapping SM types only; `dict`:
- `index`: Index of PDO mapping object
- `entries`: Dictionary objects to be mapped; `dict`:
- `index`: Index of dictionary object
- `subindex`: Subindex of dictionary object (default 0)
- `index`: Index of dictionary object, e.g. "6041h" or "1A00-03h"
- `name`: Name, a handle for the data object
- `bits`: Instead of `name`, break out individual bits,
names specified by a `list`
Expand All @@ -187,48 +219,46 @@ def set_device_config(cls, config):
cls._device_config.clear()
cls._device_config.extend(config)

def munge_config(self, config_raw):
@classmethod
def munge_config(cls, config_raw, position):
config_cooked = config_raw.copy()
# Convert model ID ints
model_id = (config_raw["vendor_id"], config_raw["product_code"])
model_id = cls.format_model_id(model_id)
config_cooked["vendor_id"], config_cooked["product_code"] = model_id
# Flatten out param_values key
pv = dict()
config_cooked["param_values"] = dict()
for ix, val in config_raw.get("param_values", dict()).items():
ix = self.sdo_class.parse_idx_str(ix)
ix = cls.sdo_class.parse_idx_str(ix)
if isinstance(val, list):
pos_ix = config_raw["positions"].index(self.position)
pos_ix = config_raw["positions"].index(position)
val = val[pos_ix]
pv[ix] = val
dtc = self.data_type_class
config_raw["vendor_id"] = dtc.uint32(config_raw["vendor_id"])
config_raw["product_code"] = dtc.uint32(config_raw["product_code"])
config_cooked = dict(
vendor_id=config_raw["vendor_id"],
product_code=config_raw["product_code"],
param_values=pv,
sync_manager=config_raw.get("sync_manager", dict()),
)
config_cooked["param_values"][ix] = val
# Return pruned config dict
return config_cooked

@property
@classmethod
def gen_config(cls, model_id, address):
bus, position = address
# Find matching config
for conf in cls._device_config:
if "vendor_id" not in conf:
continue # In tests only
if model_id != (conf["vendor_id"], conf["product_code"]):
continue
if bus != conf["bus"]:
continue
if position not in conf["positions"]:
continue
break
else:
raise KeyError(f"No config for device at {address}")
# Prune & return config
return cls.munge_config(conf, position)

@cached_property
def config(self):
if self._config is None:
# Find matching config
for conf in self._device_config:
if "vendor_id" not in conf:
continue # In tests only
if self.model_id != (conf["vendor_id"], conf["product_code"]):
continue
if self.bus != conf["bus"]:
continue
if self.position not in conf["positions"]:
continue
break
else:
raise KeyError(f"No config for device at {self.address}")
# Prune & cache config
self._config = self.munge_config(conf)

# Return cached config
return self._config
return self.gen_config(self.model_id, self.address)

def write_config_param_values(self):
for sdo, value in self.config["param_values"].items():
Expand Down
17 changes: 14 additions & 3 deletions hw_device_mgr/cia_301/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ def log_operational_changes(self):
def munge_sdo_data(cls, sdo_data):
# Turn per-model name SDO data from YAML into per-model_id SDO data
res = dict()
for model_name, sd in sdo_data.items():
device_cls = cls.get_model_by_name(model_name)
for model_id, sd in sdo_data.items():
device_cls = cls.get_model(model_id)
model_id = device_cls.device_model_id()
res[model_id] = sd
assert res
Expand All @@ -98,6 +98,16 @@ def add_device_sdos(cls, sdo_data):
"""
cls.config_class.add_device_sdos(cls.munge_sdo_data(sdo_data))

@classmethod
def add_device_dcs(cls, dcs_data):
"""
Configure device distributed clocks.

Pass to the `Config` class the information needed to configure
DCs for this `model_id`.
"""
cls.config_class.add_device_dcs(dcs_data)

@classmethod
def get_device(cls, address=None, **kwargs):
registry = cls._address_registry.setdefault(cls.name, dict())
Expand Down Expand Up @@ -178,10 +188,11 @@ def sim_device_data_address(cls, sim_device_data):
return model_id

@classmethod
def init_sim(cls, *, sim_device_data, sdo_data):
def init_sim(cls, *, sim_device_data, sdo_data, dcs_data):
super().init_sim(sim_device_data=sim_device_data)
sim_device_data = cls._sim_device_data[cls.category]
cls.add_device_sdos(sdo_data)
cls.add_device_dcs(dcs_data)
cls.config_class.init_sim(sim_device_data=sim_device_data)

def set_sim_feedback(self, **kwargs):
Expand Down
Loading