diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b872b6..2ff818a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.21.0] - 2023-09-13 + +### Fixes + +- Add support for encode/decode frame with byte array signals + ## [0.20.0] - 2023-06-12 ### Added diff --git a/ldfparser/frame.py b/ldfparser/frame.py index 648b7f3..29ef1a5 100644 --- a/ldfparser/frame.py +++ b/ldfparser/frame.py @@ -132,16 +132,29 @@ def encode(self, as is (float and string values) """ converted = {} + + def default_encoder(_value, _signal): + if isinstance(_value, (int, list)): + return _value + + raise ValueError(f'No encoding type found for {_signal} ({value})') + for (signal_name, value) in data.items(): signal = self._get_signal(signal_name) + encoder = default_encoder if encoding_types is not None and signal_name in encoding_types: - converted[signal_name] = encoding_types[signal_name].encode(value, signal) + encoder = encoding_types[signal_name].encode elif signal.encoding_type is not None: - converted[signal_name] = signal.encoding_type.encode(value, signal) - elif isinstance(value, int): + encoder = signal.encoding_type.encode + + if signal.is_array(): + if not isinstance(value, list): + num_of_bytes = int(signal.width / 8) + value = [b for b in int.to_bytes(encoder(value, signal), num_of_bytes, "big")] converted[signal_name] = value else: - raise ValueError(f'No encoding type found for {signal_name} ({value})') + converted[signal_name] = encoder(value, signal) + return self.encode_raw(converted) def _signal_map_to_message(self, signals: Dict[str, int]) -> List[int]: @@ -206,16 +219,26 @@ def decode(self, frame_layout = u6p2u8u1u1p6 """ + def default_decoder(_value, *args): + return _value + parsed = self.decode_raw(data) converted = {} for (signal_name, value) in parsed.items(): signal = self._get_signal(signal_name) + decoder = default_decoder if encoding_types is not None and signal_name in encoding_types: - converted[signal_name] = encoding_types[signal_name].decode(value, signal, keep_unit) + decoder = encoding_types[signal_name].decode elif signal.encoding_type is not None: - converted[signal_name] = signal.encoding_type.decode(value, signal, keep_unit) + decoder = signal.encoding_type.decode + + if signal.is_array(): + if decoder is not default_decoder: + value = int.from_bytes(value, "big") + converted[signal_name] = decoder(value, signal, keep_unit) else: - converted[signal_name] = value + converted[signal_name] = decoder(value, signal, keep_unit) + return converted def decode_raw(self, diff --git a/setup.cfg b/setup.cfg index 64a8e80..2c5f59e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,2 +1,2 @@ [metadata] -version = 0.20.0 +version = 0.21.0 diff --git a/tests/test_frame.py b/tests/test_frame.py index 9ce65c8..54ba6e2 100644 --- a/tests/test_frame.py +++ b/tests/test_frame.py @@ -270,6 +270,67 @@ def test_encode_error_type(self, frame, value): with pytest.raises(ValueError): frame.encode({'MotorSpeed': value}) +@pytest.mark.unit +class TestEncodeDecodeArray: + """Test encode/decode of signal with array values""" + @pytest.mark.parametrize("use_converter", [True, False]) + def test_encode_decode_array(self, use_converter): + signal = LinSignal('BattCurr', 24, [0, 0, 2]) + encoding_type = LinSignalEncodingType( + "BattCurrCoding", + [ + PhysicalValue(0, 182272, 0.00390625, -512, "A"), + LogicalValue(16777215, "Invalid") + ] + ) + converters = {} + if use_converter: + converters["BattCurr"] = encoding_type + else: + signal.encoding_type = encoding_type + + frame = LinUnconditionalFrame(0x20, "LinStatus", 3, {0: signal}) + + # Logical value + invalid_value = {"BattCurr": "Invalid"} + encoded = frame.encode(invalid_value, converters) + assert encoded == bytearray([255, 255, 255]) + decoded = frame.decode(encoded, converters) + assert decoded == invalid_value + + # Physical value + valid_value = {"BattCurr": -254.99609375} + encoded = frame.encode(valid_value, converters) + assert encoded == bytearray([1, 1, 1]) + decoded = frame.decode(encoded, converters) + assert decoded == valid_value + + def test_encode_decode_array_no_converter(self): + signal = LinSignal('BattCurr', 24, [0, 0, 2]) + frame = LinUnconditionalFrame(0x20, "LinStatus", 3, {0: signal}) + raw = {"BattCurr": [1, 1, 1]} + encoded_expected = bytearray([1, 1, 1]) + + encoded = frame.encode(raw) + assert encoded == encoded_expected + + decoded = frame.decode(encoded) + assert decoded == raw + + def test_encode_decode_array_default_no_converter(self): + signal = LinSignal('BattCurr', 24, [0, 0, 2]) + frame = LinUnconditionalFrame(0x20, "LinStatus", 3, {0: signal}) + + encoded_expected = bytearray([0, 0, 2]) + decode_expected = {'BattCurr': [0, 0, 2]} + + encoded = frame.encode({}) + assert encoded == encoded_expected + + decoded = frame.decode(encoded) + assert decoded == decode_expected + + @pytest.mark.unit class TestLinUnconditionalFrameDecodingRaw: