Skip to content

Commit b73daa6

Browse files
Merge pull request #655 from FedericoSpada/export_domain_objects
Memorized DOMAIN ObjectType for a proper export
2 parents c05b49d + aaa495e commit b73daa6

4 files changed

Lines changed: 78 additions & 5 deletions

File tree

canopen/objectdictionary/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,8 @@ def __init__(self, name: str, index: int, subindex: int = 0):
368368
self.data_type: Optional[int] = None
369369
#: Access type, should be "rw", "ro", "wo", or "const"
370370
self.access_type: str = "rw"
371+
#: The variable represents a DOMAIN ObjectType
372+
self.is_domain: bool = False
371373
#: Description of variable
372374
self.description: str = ""
373375
#: Dictionary of value descriptions

canopen/objectdictionary/eds.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ def import_eds(source, node_id):
129129
storage_location = None
130130

131131
if object_type in (objectcodes.VAR, objectcodes.DOMAIN):
132-
var = build_variable(eds, section, node_id, index)
132+
var = build_variable(eds, section, node_id, index, is_domain=object_type == objectcodes.DOMAIN)
133133
od.add_object(var)
134134
elif object_type == objectcodes.ARRAY and eds.has_option(section, "CompactSubObj"):
135135
arr = ODArray(name, index)
@@ -158,7 +158,11 @@ def import_eds(source, node_id):
158158
subindex = int(match.group(2), 16)
159159
entry = od[index]
160160
if isinstance(entry, (ODRecord, ODArray)):
161-
var = build_variable(eds, section, node_id, index, subindex)
161+
try:
162+
object_type = int(eds.get(section, "ObjectType"), 0)
163+
except NoOptionError:
164+
object_type = objectcodes.VAR
165+
var = build_variable(eds, section, node_id, index, subindex, is_domain=object_type == objectcodes.DOMAIN)
162166
entry.add_member(var)
163167

164168
# Match [index]Name
@@ -252,13 +256,14 @@ def _revert_variable(var_type, value):
252256
return f"0x{value:02X}"
253257

254258

255-
def build_variable(eds, section, node_id, index, subindex=0):
259+
def build_variable(eds, section, node_id, index, subindex=0, is_domain=False):
256260
"""Creates a object dictionary entry.
257261
:param eds: String stream of the eds file
258262
:param section:
259263
:param node_id: Node ID
260264
:param index: Index of the CANOpen object
261-
:param subindex: Subindex of the CANOpen object (if presente, else 0)
265+
:param subindex: Subindex of the CANOpen object (if present, else 0)
266+
:param is_domain: variable represents a DOMAIN ObjectType (if present, else False)
262267
"""
263268
name = eds.get(section, "ParameterName")
264269
var = ODVariable(name, index, subindex)
@@ -268,6 +273,7 @@ def build_variable(eds, section, node_id, index, subindex=0):
268273
var.storage_location = None
269274
var.data_type = int(eds.get(section, "DataType"), 0)
270275
var.access_type = eds.get(section, "AccessType").lower()
276+
var.is_domain = is_domain
271277
if var.data_type > 0x1B:
272278
# The object dictionary editor from CANFestival creates an optional object if min max values are used
273279
# This optional object is then placed in the eds under the section [A0] (start point, iterates for more)
@@ -370,7 +376,8 @@ def export_variable(var, eds):
370376
section = f"{var.index:04X}sub{var.subindex:X}"
371377

372378
export_common(var, eds, section)
373-
eds.set(section, "ObjectType", f"0x{objectcodes.VAR:X}")
379+
object_type = objectcodes.DOMAIN if var.is_domain else objectcodes.VAR
380+
eds.set(section, "ObjectType", f"0x{object_type:X}")
374381
if var.data_type:
375382
eds.set(section, "DataType", f"0x{var.data_type:04X}")
376383
if var.access_type:

test/sample.eds

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1017,3 +1017,30 @@ PDOMapping=0x0
10171017
Factor=ERROR
10181018
Description=
10191019
Unit=
1020+
1021+
[3063]
1022+
ParameterName=DOMAIN object
1023+
ObjectType=0x2
1024+
DataType=0x0007
1025+
AccessType=rw
1026+
PDOMapping=0
1027+
1028+
[3064]
1029+
ParameterName=Record with DOMAIN sub-object
1030+
SubNumber=0x2
1031+
ObjectType=0x9
1032+
1033+
[3064sub0]
1034+
ParameterName=Highest subindex
1035+
ObjectType=0x7
1036+
DataType=0x0005
1037+
AccessType=ro
1038+
DefaultValue=0x01
1039+
PDOMapping=0
1040+
1041+
[3064sub1]
1042+
ParameterName=DOMAIN sub-object
1043+
ObjectType=0x2
1044+
DataType=0x0007
1045+
AccessType=rw
1046+
PDOMapping=0

test/test_eds.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ def test_variable(self):
111111
self.assertEqual(var.name, 'Producer heartbeat time')
112112
self.assertEqual(var.data_type, canopen.objectdictionary.UNSIGNED16)
113113
self.assertEqual(var.access_type, 'rw')
114+
self.assertFalse(var.is_domain)
114115
self.assertEqual(var.default, 0)
115116
self.assertFalse(var.relative)
116117

@@ -132,6 +133,7 @@ def test_record(self):
132133
self.assertEqual(var.subindex, 1)
133134
self.assertEqual(var.data_type, canopen.objectdictionary.UNSIGNED32)
134135
self.assertEqual(var.access_type, 'ro')
136+
self.assertFalse(var.is_domain)
135137

136138
def test_record_with_limits(self):
137139
int8 = self.od[0x3020]
@@ -166,6 +168,7 @@ def test_array_compact_subobj(self):
166168
self.assertEqual(var.subindex, 5)
167169
self.assertEqual(var.data_type, canopen.objectdictionary.UNSIGNED32)
168170
self.assertEqual(var.access_type, 'ro')
171+
self.assertFalse(var.is_domain)
169172

170173
def test_explicit_name_subobj(self):
171174
name = self.od[0x3004].name
@@ -197,6 +200,7 @@ def test_dummy_variable(self):
197200
self.assertEqual(var.name, 'Dummy0003')
198201
self.assertEqual(var.data_type, canopen.objectdictionary.INTEGER16)
199202
self.assertEqual(var.access_type, 'const')
203+
self.assertFalse(var.is_domain)
200204
self.assertEqual(len(var), 16)
201205

202206
def test_dummy_variable_undefined(self):
@@ -213,6 +217,39 @@ def test_reading_factor(self):
213217
self.assertEqual(var2.factor, 1)
214218
self.assertEqual(var2.unit, '')
215219

220+
def test_read_domain_object(self):
221+
var = self.od[0x3063]
222+
self.assertIsInstance(var, canopen.objectdictionary.ODVariable)
223+
self.assertEqual(var.index, 0x3063)
224+
self.assertEqual(var.subindex, 0)
225+
self.assertEqual(var.name, 'DOMAIN object')
226+
self.assertEqual(var.data_type, canopen.objectdictionary.UNSIGNED32)
227+
self.assertEqual(var.access_type, 'rw')
228+
self.assertTrue(var.is_domain)
229+
230+
def test_read_domain_subobject(self):
231+
record = self.od[0x3064]
232+
var = record[1]
233+
self.assertIsInstance(var, canopen.objectdictionary.ODVariable)
234+
self.assertEqual(var.index, 0x3064)
235+
self.assertEqual(var.subindex, 1)
236+
self.assertEqual(var.name, 'DOMAIN sub-object')
237+
self.assertEqual(var.data_type, canopen.objectdictionary.UNSIGNED32)
238+
self.assertEqual(var.access_type, 'rw')
239+
self.assertTrue(var.is_domain)
240+
241+
def test_roundtrip_domain_objects(self):
242+
# ObjectType==DOMAIN survive an EDS export/import round-trip
243+
import io
244+
with io.StringIO() as dest:
245+
canopen.export_od(self.od, dest, 'eds')
246+
dest.name = 'mock.eds'
247+
dest.seek(0)
248+
od2 = canopen.import_od(dest)
249+
self.assertFalse(od2['Producer heartbeat time'].is_domain)
250+
self.assertFalse(od2['Identity object']['Vendor-ID'].is_domain)
251+
self.assertTrue(od2[0x3063].is_domain)
252+
self.assertTrue(od2[0x3064][1].is_domain)
216253

217254

218255
def test_comments(self):

0 commit comments

Comments
 (0)