diff --git a/canopen/objectdictionary/eds.py b/canopen/objectdictionary/eds.py index 19c4b02f..25acfeca 100644 --- a/canopen/objectdictionary/eds.py +++ b/canopen/objectdictionary/eds.py @@ -204,17 +204,16 @@ def import_from_node(node_id: int, network: canopen.network.Network): return od -def _calc_bit_length(data_type): - if data_type == datatypes.INTEGER8: - return 8 - elif data_type == datatypes.INTEGER16: - return 16 - elif data_type == datatypes.INTEGER32: - return 32 - elif data_type == datatypes.INTEGER64: - return 64 - else: - raise ValueError(f"Invalid data_type '{data_type}', expecting a signed integer data_type.") +_SIGNED_BIT_LENGTHS = { + datatypes.INTEGER8: 8, + datatypes.INTEGER16: 16, + datatypes.INTEGER24: 24, + datatypes.INTEGER32: 32, + datatypes.INTEGER40: 40, + datatypes.INTEGER48: 48, + datatypes.INTEGER56: 56, + datatypes.INTEGER64: 64, +} def _signed_int_from_hex(hex_str, bit_length): @@ -292,20 +291,20 @@ def build_variable(eds, section, node_id, index, subindex=0, is_domain=False): try: min_string = eds.get(section, "LowLimit") if var.data_type in datatypes.SIGNED_TYPES: - var.min = _signed_int_from_hex(min_string, _calc_bit_length(var.data_type)) + var.min = _signed_int_from_hex(min_string, _SIGNED_BIT_LENGTHS[var.data_type]) else: var.min = int(min_string, 0) except ValueError: - pass + logger.warning("Failed to parse LowLimit for %s: %r", name, min_string) if eds.has_option(section, "HighLimit"): try: max_string = eds.get(section, "HighLimit") if var.data_type in datatypes.SIGNED_TYPES: - var.max = _signed_int_from_hex(max_string, _calc_bit_length(var.data_type)) + var.max = _signed_int_from_hex(max_string, _SIGNED_BIT_LENGTHS[var.data_type]) else: var.max = int(max_string, 0) except ValueError: - pass + logger.warning("Failed to parse HighLimit for %s: %r", name, max_string) if eds.has_option(section, "DefaultValue"): try: var.default_raw = eds.get(section, "DefaultValue") diff --git a/test/sample.eds b/test/sample.eds index ad00a12e..49c95ce7 100644 --- a/test/sample.eds +++ b/test/sample.eds @@ -976,6 +976,42 @@ HighLimit=0xFFFFFFFF LowLimit=0x80000000 PDOMapping=0 +[3031] +ParameterName=INTEGER24 value range -1 to 0 +ObjectType=0x7 +DataType=0x10 +AccessType=rw +HighLimit=0x000000 +LowLimit=0xFFFFFF +PDOMapping=0 + +[3032] +ParameterName=INTEGER40 value range -1 to 0 +ObjectType=0x7 +DataType=0x12 +AccessType=rw +HighLimit=0x0000000000 +LowLimit=0xFFFFFFFFFF +PDOMapping=0 + +[3033] +ParameterName=INTEGER48 value range -1 to 0 +ObjectType=0x7 +DataType=0x13 +AccessType=rw +HighLimit=0x000000000000 +LowLimit=0xFFFFFFFFFFFF +PDOMapping=0 + +[3034] +ParameterName=INTEGER56 value range -1 to 0 +ObjectType=0x7 +DataType=0x14 +AccessType=rw +HighLimit=0x00000000000000 +LowLimit=0xFFFFFFFFFFFFFF +PDOMapping=0 + [3040] ParameterName=INTEGER64 value range -10 to +10 ObjectType=0x7 diff --git a/test/test_eds.py b/test/test_eds.py index 7a19ffeb..9250197a 100644 --- a/test/test_eds.py +++ b/test/test_eds.py @@ -148,6 +148,28 @@ def test_record_with_limits(self): int64 = self.od[0x3040] self.assertEqual(int64.min, -10) self.assertEqual(int64.max, +10) + # Verify all remaining SIGNED_TYPES are handled (INTEGER24/40/48/56) + for index in (0x3031, 0x3032, 0x3033, 0x3034): + var = self.od[index] + self.assertEqual(var.min, -1, f"min mismatch at 0x{index:04X}") + self.assertEqual(var.max, 0, f"max mismatch at 0x{index:04X}") + + def test_invalid_limit_logs_warning(self): + import io + import logging + + with open(SAMPLE_EDS) as f: + content = f.read() + invalid_eds = content.replace( + "LowLimit=0x02\nPDOMapping=0\n\n[3030]", + "LowLimit=INVALID\nPDOMapping=0\n\n[3030]", + ) + with io.StringIO(invalid_eds) as buf: + buf.name = "mock.eds" + with self.assertLogs("canopen.objectdictionary.eds", level=logging.WARNING) as cm: + od = canopen.import_od(buf) + self.assertIsNone(od[0x3021].min) + self.assertTrue(any("LowLimit" in msg for msg in cm.output)) def test_signed_int_from_hex(self): for data_type, test_cases in self.test_data.items():