Skip to content

Commit

Permalink
Save the passed node ID to the resulting OD in import_od() (#484)
Browse files Browse the repository at this point in the history
Only for DCF-files (which include a DeviceComissioning section), the Node-ID from the file is stored in the ObjectDictionary instance.  When given an override value via the `import_od(..., node_id=...)` argument, that was however not applied to the OD attribute, but only used for $NODEID interpolation.

Make sure the interpolated values match the stored node_id in this case.

Also handle a missing NodeID option within the DeviceComissioning section gracefully, instead of raising an exception.
  • Loading branch information
erlend-aasland authored Jul 3, 2024
1 parent 2938a90 commit 6a4ca11
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 6 deletions.
7 changes: 5 additions & 2 deletions canopen/objectdictionary/eds.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,11 @@ def import_eds(source, node_id):

if eds.has_section("DeviceComissioning"):
od.bitrate = int(eds.get("DeviceComissioning", "Baudrate")) * 1000
od.node_id = int(eds.get("DeviceComissioning", "NodeID"), 0)
node_id = node_id or od.node_id

if node_id is None:
if val := eds.get("DeviceComissioning", "NodeID", fallback=None):
node_id = int(val, base=0)
od.node_id = node_id

for section in eds.sections():
# Match dummy definitions
Expand Down
2 changes: 1 addition & 1 deletion test/sample.eds
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ NrOfTXPDO=4
LSS_Supported=0

[DeviceComissioning]
NodeID=2
NodeID=0x10
NodeName=Some name
Baudrate=500
NetNumber=0
Expand Down
32 changes: 29 additions & 3 deletions test/test_eds.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
from canopen.objectdictionary.eds import _signed_int_from_hex
from canopen.utils import pretty_index

EDS_PATH = os.path.join(os.path.dirname(__file__), 'sample.eds')

SAMPLE_EDS = os.path.join(os.path.dirname(__file__), 'sample.eds')
DATATYPES_EDS = os.path.join(os.path.dirname(__file__), 'datatypes.eds')


class TestEDS(unittest.TestCase):
Expand Down Expand Up @@ -48,7 +50,7 @@ class TestEDS(unittest.TestCase):
}

def setUp(self):
self.od = canopen.import_od(EDS_PATH, 2)
self.od = canopen.import_od(SAMPLE_EDS, 2)

def test_load_nonexisting_file(self):
with self.assertRaises(IOError):
Expand All @@ -59,10 +61,34 @@ def test_load_unsupported_format(self):
canopen.import_od(__file__)

def test_load_file_object(self):
with open(EDS_PATH) as fp:
with open(SAMPLE_EDS) as fp:
od = canopen.import_od(fp)
self.assertTrue(len(od) > 0)

def test_load_implicit_nodeid(self):
# sample.eds has a DeviceComissioning section with NodeID set to 0x10.
od = canopen.import_od(SAMPLE_EDS)
self.assertEqual(od.node_id, 16)

def test_load_implicit_nodeid_fallback(self):
import io

# First, remove the NodeID option from DeviceComissioning.
with open(SAMPLE_EDS) as f:
lines = [L for L in f.readlines() if not L.startswith("NodeID=")]
with io.StringIO("".join(lines)) as buf:
buf.name = "mock.eds"
od = canopen.import_od(buf)
self.assertIsNone(od.node_id)

# Next, try an EDS file without a DeviceComissioning section.
od = canopen.import_od(DATATYPES_EDS)
self.assertIsNone(od.node_id)

def test_load_explicit_nodeid(self):
od = canopen.import_od(SAMPLE_EDS, node_id=3)
self.assertEqual(od.node_id, 3)

def test_variable(self):
var = self.od['Producer heartbeat time']
self.assertIsInstance(var, canopen.objectdictionary.ODVariable)
Expand Down

0 comments on commit 6a4ca11

Please sign in to comment.