Skip to content

Commit 4528b58

Browse files
author
Markus Konrad
committed
add support for container-pdus without sub-pdu Header (=static container-pdu). signals of such sub-pdus now get the correct start_bit
1 parent d1da989 commit 4528b58

File tree

2 files changed

+79
-33
lines changed

2 files changed

+79
-33
lines changed

src/canmatrix/canmatrix.py

Lines changed: 62 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -833,6 +833,8 @@ class Pdu(object):
833833
Whereas a PDU is the same than a frame on CAN bus, at flexray a frame may consist of
834834
multiple PDUs (a bit like multiple signal layout for multiplexed can frames).
835835
This class is only used for flexray busses.
836+
Note: since container-pdus are supported for arxml, this class is also used for arxml (but only
837+
for sub-pdus of container-pdus).
836838
"""
837839

838840
name = attr.ib(default="") # type: str
@@ -844,6 +846,8 @@ class Pdu(object):
844846
signals = attr.ib(factory=list) # type: typing.MutableSequence[Signal]
845847
signalGroups = attr.ib(factory=list) # type: typing.MutableSequence[SignalGroup]
846848
cycle_time = attr.ib(default=0) # type: int
849+
# offset is used for arxml, sub-pdu inside a static-container-pdu
850+
offset_bytes = attr.ib(default=0) # type: int
847851

848852
def add_signal(self, signal):
849853
# type: (Signal) -> Signal
@@ -1491,20 +1495,35 @@ def unpack(self, data: bytes,
14911495
f"Received message 0x{msg_id:04X} with wrong data size: {rx_length} instead of {self.size}")
14921496

14931497
if self.is_pdu_container:
1498+
# note: PDU-Container without header is possible for ARXML-Container-PDUs with NO-HEADER
1499+
# that mean this are not dynamic Container-PDUs rather than static ones. (each sub-pdu has
1500+
# a fixed offset in the container)
1501+
header_signals = []
14941502
header_id_signal = self.signal_by_name("Header_ID")
14951503
header_dlc_signal = self.signal_by_name("Header_DLC")
1496-
if header_id_signal is None or header_dlc_signal is None:
1497-
raise DecodingConatainerPdu(
1498-
'Received message 0x{:08X} without Header_ID or '
1499-
'Header_DLC signal'.format(self.arbitration_id.id)
1500-
)
1504+
1505+
if header_id_signal is not None:
1506+
header_signals.append(header_id_signal)
1507+
_header_id_signal_size = header_id_signal.size
1508+
else:
1509+
_header_id_signal_size = 0
1510+
if header_dlc_signal is not None:
1511+
header_signals.append(header_dlc_signal)
1512+
_header_dlc_signal_size = header_dlc_signal.size
1513+
else:
1514+
_header_dlc_signal_size = 0
15011515
# TODO: may be we need to check that ID/DLC signals are contiguous
1502-
header_size = header_id_signal.size + header_dlc_signal.size
1516+
if len(header_signals) > 0 and len(header_signals) != 2:
1517+
raise DecodingContainerPdu(
1518+
'Received message 0x{:08X} with incorrect Header-Defintiion. '
1519+
'Header_ID signal or Header_DLC is missing'.format(self.arbitration_id.id)
1520+
)
1521+
header_size = _header_id_signal_size + _header_dlc_signal_size
15031522
little, big = self.bytes_to_bitstrings(data)
15041523
size = self.size * 8
15051524
return_dict = dict({"pdus": []})
15061525
# decode signal which are not in PDUs
1507-
signals = [s for s in self.signals if s not in [header_id_signal, header_dlc_signal]]
1526+
signals = [s for s in self.signals if s not in header_signals]
15081527
if signals:
15091528
unpacked = self.bitstring_to_signal_list(signals, big, little, size)
15101529
for s, v in zip(signals, unpacked):
@@ -1527,20 +1546,52 @@ def unpack(self, data: bytes,
15271546
return_dict[s.name] = []
15281547
return_dict[s.name].append(DecodedSignal(v, s))
15291548
pdu = self.pdu_by_id(pdu_id)
1549+
offset = header_id_signal.start_bit if header_id_signal is not None else 0
1550+
no_header_next_pdu_idx = 0
1551+
# decode as long as there is data left to decode (if there is a header), or as long as there are sub-pdus
1552+
# left to decode (in case of static-container without pdu-headers)
1553+
while (offset + header_size) < size and no_header_next_pdu_idx < len(self.pdus):
1554+
if len(header_signals) > 0:
1555+
unpacked = self.bitstring_to_signal_list(
1556+
header_signals,
1557+
big[offset:offset + header_size],
1558+
little[size - offset - header_size:size - offset],
1559+
header_size
1560+
)
1561+
offset += header_size
1562+
pdu_id = unpacked[0]
1563+
pdu_dlc = unpacked[1]
1564+
for s, v in zip(header_signals, unpacked):
1565+
if s.name not in return_dict:
1566+
return_dict[s.name] = []
1567+
return_dict[s.name].append(DecodedSignal(v, s))
1568+
pdu = self.pdu_by_id(pdu_id)
1569+
else:
1570+
# if there is no pdu-header, then we have a static container-pdu
1571+
# we have to loop all sub-pdus and set the offset to the offset of the PDU
1572+
# note: order of processing sub-PDUs is not important, even if the sub-PDUs are not ordered
1573+
# by the pdu-offset (we just set the offset correct to the actual processed sub-PDU)
1574+
pdu = self.pdus[no_header_next_pdu_idx]
1575+
no_header_next_pdu_idx += 1
1576+
pdu_dlc = pdu.size
1577+
offset = pdu.offset_bytes * 8
1578+
decode_size_bits = pdu_dlc * 8
15301579
if pdu is None:
15311580
return_dict['pdus'].append(None)
15321581
else:
15331582
unpacked = self.bitstring_to_signal_list(
15341583
pdu.signals,
1535-
big[offset:offset + pdu_dlc * 8],
1536-
little[size - offset - pdu_dlc * 8:size - offset],
1537-
pdu_dlc * 8
1584+
big[offset:offset + decode_size_bits],
1585+
little[size - offset - decode_size_bits:size - offset],
1586+
decode_size_bits
15381587
)
15391588
pdu_dict = dict()
15401589
for s, v in zip(pdu.signals, unpacked):
15411590
pdu_dict[s.name] = DecodedSignal(v, s)
15421591
return_dict["pdus"].append({pdu.name: pdu_dict})
1543-
offset += (pdu_dlc * 8)
1592+
if len(header_signals) > 0:
1593+
# if there is a pdu-header, we have to set the offset to the start of the next pdu
1594+
offset += decode_size_bits
15441595
return return_dict
15451596
else:
15461597
little, big = self.bytes_to_bitstrings(data)

src/canmatrix/formats/arxml.py

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1110,7 +1110,10 @@ def ar_byteorder_is_little(in_string):
11101110

11111111
def get_signals(signal_array, frame, ea, multiplex_id, float_factory, bit_offset=0):
11121112
# type: (typing.Sequence[_Element], typing.Union[canmatrix.Frame, canmatrix.Pdu], Earxml, int, typing.Callable, int) -> None
1113-
"""Add signals from xml to the Frame."""
1113+
"""Add signals from xml to the Frame.
1114+
ATTENTION: be careful if you plan to use bit_offset != 0.
1115+
This will result in non-valid signal definitions (start-bit) in relation to the PDU.
1116+
Maybe a possible refactoring in the future to remove this bit_offset if no further need is identified?"""
11141117

11151118
group_id = 1
11161119
if signal_array is None: # Empty signalarray - nothing to do
@@ -1493,28 +1496,17 @@ def get_frame_from_container_ipdu(pdu, target_frame, ea, float_factory, headers_
14931496
value = ea.get_child(time_period, "VALUE")
14941497
if value is not None:
14951498
cycle_time = int(float_factory(value.text) * 1000)
1499+
# TODO maybe we need to refactor getting the offset for secured-i-pdus to get the offset BEFORE we
1500+
# copy the authentic/payload-PDU over the ipdu. need to be clarified. For the moment leave it as it was.
1501+
# until now we have not seen an non-cryptographic secured-i-pdu inside
1502+
# an static-container-PDU (=without header) (only in that veeeery specific situation it's important)
14961503
try:
1497-
if header_type == "SHORT-HEADER":
1498-
header_id = ea.get_child(ipdu, "HEADER-ID-SHORT-HEADER").text
1499-
elif header_type == "LONG-HEADER":
1500-
header_id = ea.get_child(ipdu, "HEADER-ID-LONG-HEADER").text
1501-
else:
1502-
# none type
1503-
header_id = None
1504-
except AttributeError:
1505-
header_id = None
1506-
if header_id is not None:
1507-
header_id = int(header_id, 0)
1508-
1509-
if ipdu is not None and 'SECURED-I-PDU' in ipdu.tag:
1510-
secured_i_pdu_name = ea.get_element_name(ipdu)
1511-
payload = ea.follow_ref(ipdu, "PAYLOAD-REF")
1512-
ipdu = ea.follow_ref(payload, "I-PDU-REF")
1513-
logger.info("found secured pdu '%s', dissolved to '%s'", secured_i_pdu_name, ea.get_element_name(ipdu))
1514-
try:
1515-
offset = int(ea.get_child(ipdu, "OFFSET").text, 0) * 8
1504+
offset_bytes = int(ea.get_child(ipdu, "OFFSET").text, 0)
15161505
except:
1517-
offset = 0
1506+
if header_id is None:
1507+
logger.error(f"PDU {ipdu_name} is a Container-sub-PDU with no header, but NO PDU offset was found! "
1508+
f"(will most likely result in wrong encoding/decoding!) - check ARXML file!!")
1509+
offset_bytes = 0
15181510

15191511
try:
15201512
pdu_type = ipdu.attrib["DEST"]
@@ -1530,8 +1522,11 @@ def get_frame_from_container_ipdu(pdu, target_frame, ea, float_factory, headers_
15301522
target_pdu = canmatrix.Pdu(name=ipdu_name, size=ipdu_length, id=header_id,
15311523
triggering_name=ipdu_triggering_name, pdu_type=pdu_type,
15321524
port_type=pdu_port_type, cycle_time=cycle_time)
1525+
port_type=pdu_port_type, cycle_time=cycle_time,
1526+
offset_bytes=offset_bytes)
15331527
pdu_sig_mapping = ea.get_children(ipdu, "I-SIGNAL-TO-I-PDU-MAPPING")
1534-
get_signals(pdu_sig_mapping, target_pdu, ea, None, float_factory, bit_offset=offset)
1528+
get_signals(pdu_sig_mapping, target_pdu, ea, None, float_factory, bit_offset=0,
1529+
generated_update_bits_init_to_1=generated_update_bits_init_to_1)
15351530
target_frame.add_pdu(target_pdu)
15361531

15371532

0 commit comments

Comments
 (0)