Skip to content

Commit

Permalink
Support decode/encode signals with list type init values (#120)
Browse files Browse the repository at this point in the history
* Support encode/decode of signal of array type values
* Update test case to include logical value test
---------
Co-authored-by: Yan Du <YDU21@ford.com>
Co-authored-by: Balázs Eszes <c4deszes@gmail.com>
  • Loading branch information
nuts4coffee authored Sep 13, 2023
1 parent dac9b42 commit 94dc2c8
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 8 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
37 changes: 30 additions & 7 deletions ldfparser/frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]:
Expand Down Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
[metadata]
version = 0.20.0
version = 0.21.0
61 changes: 61 additions & 0 deletions tests/test_frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down

0 comments on commit 94dc2c8

Please sign in to comment.