You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
PDO getitem unexpected returns PdoVariable on value 0 #581
Reading PDO maps from a node is a 1-indexed value. However, if the user use index 0 (which is a common expectation for many indexes), __getitem__ returns a PdoVariable instead of the expected PdoMap.
node=network.create_node(42, "mynode.eds")
node.rpdo.aread(from_od=False)
# Use correct 1 indexrpdo=node.rpdo[1] # Type returned is `PdoMap`# Use incorrect 0 indexrpdo=node.rpdo[0] # Type returned is a `PdoVariable`
ifisinstance(key, int) and (0x1A00<=key<=0x1BFFor# By TPDO ID (512)
0x1600<=key<=0x17FFor# By RPDO ID (512)
0<key<=512): # By PDO Index
returnself.map[key]
else:
forpdo_mapinself.map.values():
try:
returnpdo_map[key]
exceptKeyError:
# ignore if one specific PDO does not have the key and try the next one
continue
raiseKeyError(f"PDO: {key} was not found in any map")
We just spent an hour trying to debug why the PDO didn't work before we realized the obvious mistake. I think using 0 index is such a common expectation, that I think we should give an error or clearly warn the user.
What is the intent of the the else section in L48-L53? When is this used? I could not find any tests showing how this is used. Because of the else rpdo[0] as shown above will fetch the first PdoVariable from the first PDO, but rpdo[1] will not fetch the second PdoVariable.
It's a mechanism to search through all PDO mappings for a certain OD index entry, as I understand it. So you can access node.rpdo[0x6060] for example, without knowing which specific PDO has that object mapped. It should work even with strings as key.
Passing the special value 0 down to that mechanism doesn't make sense at all. Of course a PdoMap is rightfully indexable from 0 to 7. But of those possible values, 0 is the only one which could be passed down from PdoBase.__getitem__(). You are absolutely right that this is very error-prone and badly designed. I think we should explicitly filter out the 0 value in the else branch, as it is also left out from the previous branch and ends up there accidentally.
I have tested it further and I find it somewhat inconsistent. See the examples below.
The function returns PdoVariable for many input values. To be able to use it, e.g. to do PDO transmit, one can use .pdo_parent to access its PdoMap. However any object can be member of multiple PDOs, but this function will only be able find the first occurrence of the PDO variable.
Using int argument with an index to an array or record object will make the function return the first PdoVariable which is used in the PDOs. Its not possible to retrieve any other subindex objects this way.
Lastly, the code seems to suggest that using the COB-ID for the PDO mapping, e.g. 0x1600 should return the PdoMap for RPDO1, but this doesn't work.
# Returns the first PDO mapping (expected)print(node.rpdo[1]) # type: PdoMap# Returns the first PDO index in the RPDO list (unexpected)print(node.rpdo[0]) # type: PdoVariable# RPDO1. Fails with a KeyError, even if code seems to suggest it to be correct.print(node.rpdo[0x1600])
# 0x2015 is a record object. This returns the first occurrence of any# subindex present in the PDO. In this case, it returns 0x2015.2# as it happens to be the first subindex used in the PDOs.print(node.rpdo[0x2015]) # type: PdoVariable# Get the object by name.print(node.rpdo["Breathing.Respiratory rate"]) # type: PdoVariable# Getting an object that doesn't exist in any PDO fails with KeyError (expected)print(node.rpdo["Inrush delay"])
Alright, this is pretty convoluted. The access based on mapping parameter index (0x1600, 0x1A00) only works on the node.pdo: PdoBase object. It replaces its dedicated pdo.map: Optional[PdoMaps] attribute with a plain dictionary, adding only the index-based lookups. That even makes kind of sense (except for type mangling) because node.pdo[2] would be ambiguous whether we want RX or TX.
On the other hand, node.rpdo: PdoBase and node.tpdo: PdoBasedo use the PdoMaps wrapper, and its constructor generates the lookup for PdoMap only based on the relative index number (1 to 512, filtering out those where no communication parameter record is in the OD). This means that node.rpdo[0x1600] will NOT work, which is a bit unexpected, see #607@maxwai.
I intend to unify this in the linked PR #609. Just noting here for future reference where the confusion stems from.
Activity
acolomb commentedon May 20, 2025
It's a mechanism to search through all PDO mappings for a certain OD index entry, as I understand it. So you can access
node.rpdo[0x6060]for example, without knowing which specific PDO has that object mapped. It should work even with strings as key.Passing the special value 0 down to that mechanism doesn't make sense at all. Of course a
PdoMapis rightfully indexable from 0 to 7. But of those possible values, 0 is the only one which could be passed down fromPdoBase.__getitem__(). You are absolutely right that this is very error-prone and badly designed. I think we should explicitly filter out the 0 value in theelsebranch, as it is also left out from the previous branch and ends up there accidentally.sveinse commentedon May 21, 2025
I have tested it further and I find it somewhat inconsistent. See the examples below.
The function returns
PdoVariablefor many input values. To be able to use it, e.g. to do PDO transmit, one can use.pdo_parentto access itsPdoMap. However any object can be member of multiple PDOs, but this function will only be able find the first occurrence of the PDO variable.Using int argument with an index to an array or record object will make the function return the first
PdoVariablewhich is used in the PDOs. Its not possible to retrieve any other subindex objects this way.Lastly, the code seems to suggest that using the COB-ID for the PDO mapping, e.g. 0x1600 should return the
PdoMapfor RPDO1, but this doesn't work.acolomb commentedon Aug 18, 2025
Alright, this is pretty convoluted. The access based on mapping parameter index (0x1600, 0x1A00) only works on the
node.pdo: PdoBaseobject. It replaces its dedicatedpdo.map: Optional[PdoMaps]attribute with a plain dictionary, adding only the index-based lookups. That even makes kind of sense (except for type mangling) becausenode.pdo[2]would be ambiguous whether we want RX or TX.On the other hand,
node.rpdo: PdoBaseandnode.tpdo: PdoBasedo use thePdoMapswrapper, and its constructor generates the lookup forPdoMaponly based on the relative index number (1 to 512, filtering out those where no communication parameter record is in the OD). This means thatnode.rpdo[0x1600]will NOT work, which is a bit unexpected, see #607 @maxwai.I intend to unify this in the linked PR #609. Just noting here for future reference where the confusion stems from.
PdoBase: Forbid getting item with zero index (fixes canopen-python#581)…