Files
StreamLens/analyzer/protocols/decoders/can_bus.py
2025-07-28 08:14:15 -04:00

105 lines
3.6 KiB
Python

"""
CAN Bus decoder for Chapter 10 data types
Supports Controller Area Network Bus (0x78)
"""
import struct
from typing import Dict, Any, Optional
from .base import DataTypeDecoder, DecodedPayload
class CANBusDecoder(DataTypeDecoder):
"""Decoder for CAN Bus type (0x78)"""
def __init__(self):
super().__init__()
self.data_type_base = 0x78
self.data_type_name = "CAN Bus"
self.supported_formats = [0x78]
def can_decode(self, data_type: int) -> bool:
return data_type == 0x78
def get_data_type_name(self, data_type: int) -> str:
return "Controller Area Network Bus"
def decode(self, payload: bytes, ch10_header: Dict[str, Any]) -> Optional[DecodedPayload]:
"""Decode CAN Bus payload"""
decoded_data = {}
errors = []
# Parse IPH
iph = self._parse_intra_packet_header(payload)
if iph:
decoded_data.update(iph)
data_start = iph['data_start']
else:
data_start = 0
errors.append("Failed to parse intra-packet header")
# Parse CAN messages
messages = []
offset = data_start
while offset + 16 <= len(payload):
can_header = self._safe_unpack('<IIII', payload, offset)
if not can_header:
break
can_timestamp, can_id_flags, data_length, reserved = can_header
# Parse CAN ID and flags
can_id = can_id_flags & 0x1FFFFFFF
extended_id = bool(can_id_flags & 0x80000000)
remote_frame = bool(can_id_flags & 0x40000000)
error_frame = bool(can_id_flags & 0x20000000)
message = {
'timestamp': can_timestamp,
'can_id': f'0x{can_id:x}',
'extended_id': extended_id,
'remote_frame': remote_frame,
'error_frame': error_frame,
'data_length': data_length
}
# Extract CAN data
can_data_start = offset + 16
if can_data_start + data_length <= len(payload):
can_data = payload[can_data_start:can_data_start + data_length]
message['data'] = can_data.hex()
message['data_bytes'] = list(can_data)
else:
message['data_error'] = 'Data extends beyond payload'
messages.append(message)
offset = can_data_start + max(data_length, 8) # CAN frames are padded to 8 bytes
if len(messages) >= 100: # Limit output size
break
decoded_data['can_messages'] = messages
decoded_data['message_count'] = len(messages)
# Statistics
if messages:
extended_count = sum(1 for msg in messages if msg['extended_id'])
remote_count = sum(1 for msg in messages if msg['remote_frame'])
error_count = sum(1 for msg in messages if msg['error_frame'])
decoded_data['statistics'] = {
'extended_frames': extended_count,
'remote_frames': remote_count,
'error_frames': error_count,
'standard_frames': len(messages) - extended_count
}
return DecodedPayload(
data_type=0x78,
data_type_name="Controller Area Network Bus",
format_version=0,
decoded_data=decoded_data,
raw_payload=payload,
errors=errors,
metadata={'decoder': 'CANBusDecoder'}
)