mirror of https://github.com/commaai/openpilot.git
131 lines
4.1 KiB
Python
131 lines
4.1 KiB
Python
import time
|
|
from collections import defaultdict
|
|
from functools import partial
|
|
from typing import Optional
|
|
|
|
import cereal.messaging as messaging
|
|
from selfdrive.swaglog import cloudlog
|
|
from selfdrive.boardd.boardd import can_list_to_can_capnp
|
|
from panda.python.uds import CanClient, IsoTpMessage, FUNCTIONAL_ADDRS, get_rx_addr_for_tx_addr
|
|
|
|
|
|
class IsoTpParallelQuery:
|
|
def __init__(self, sendcan, logcan, bus, addrs, request, response, response_offset=0x8, functional_addr=False, debug=False):
|
|
self.sendcan = sendcan
|
|
self.logcan = logcan
|
|
self.bus = bus
|
|
self.request = request
|
|
self.response = response
|
|
self.debug = debug
|
|
self.functional_addr = functional_addr
|
|
|
|
self.real_addrs = []
|
|
for a in addrs:
|
|
if isinstance(a, tuple):
|
|
self.real_addrs.append(a)
|
|
else:
|
|
self.real_addrs.append((a, None))
|
|
|
|
self.msg_addrs = {tx_addr: get_rx_addr_for_tx_addr(tx_addr[0], rx_offset=response_offset) for tx_addr in self.real_addrs}
|
|
self.msg_buffer = defaultdict(list)
|
|
|
|
def rx(self):
|
|
"""Drain can socket and sort messages into buffers based on address"""
|
|
can_packets = messaging.drain_sock(self.logcan, wait_for_one=True)
|
|
|
|
for packet in can_packets:
|
|
for msg in packet.can:
|
|
if msg.src == self.bus:
|
|
if self.functional_addr:
|
|
if (0x7E8 <= msg.address <= 0x7EF) or (0x18DAF100 <= msg.address <= 0x18DAF1FF):
|
|
fn_addr = next(a for a in FUNCTIONAL_ADDRS if msg.address - a <= 32)
|
|
self.msg_buffer[fn_addr].append((msg.address, msg.busTime, msg.dat, msg.src))
|
|
elif msg.address in self.msg_addrs.values():
|
|
self.msg_buffer[msg.address].append((msg.address, msg.busTime, msg.dat, msg.src))
|
|
|
|
def _can_tx(self, tx_addr, dat, bus):
|
|
"""Helper function to send single message"""
|
|
msg = [tx_addr, 0, dat, bus]
|
|
self.sendcan.send(can_list_to_can_capnp([msg], msgtype='sendcan'))
|
|
|
|
def _can_rx(self, addr, sub_addr=None):
|
|
"""Helper function to retrieve message with specified address and subadress from buffer"""
|
|
keep_msgs = []
|
|
|
|
if sub_addr is None:
|
|
msgs = self.msg_buffer[addr]
|
|
else:
|
|
# Filter based on subadress
|
|
msgs = []
|
|
for m in self.msg_buffer[addr]:
|
|
first_byte = m[2][0]
|
|
if first_byte == sub_addr:
|
|
msgs.append(m)
|
|
else:
|
|
keep_msgs.append(m)
|
|
|
|
self.msg_buffer[addr] = keep_msgs
|
|
return msgs
|
|
|
|
def _drain_rx(self):
|
|
messaging.drain_sock(self.logcan)
|
|
self.msg_buffer = defaultdict(list)
|
|
|
|
def get_data(self, timeout):
|
|
self._drain_rx()
|
|
|
|
# Create message objects
|
|
msgs = {}
|
|
request_counter = {}
|
|
request_done = {}
|
|
for tx_addr, rx_addr in self.msg_addrs.items():
|
|
# rx_addr not set when using functional tx addr
|
|
id_addr = rx_addr or tx_addr[0]
|
|
sub_addr = tx_addr[1]
|
|
|
|
can_client = CanClient(self._can_tx, partial(self._can_rx, id_addr, sub_addr=sub_addr), tx_addr[0], rx_addr,
|
|
self.bus, sub_addr=sub_addr, debug=self.debug)
|
|
|
|
max_len = 8 if sub_addr is None else 7
|
|
|
|
msg = IsoTpMessage(can_client, timeout=0, max_len=max_len, debug=self.debug)
|
|
msg.send(self.request[0])
|
|
|
|
msgs[tx_addr] = msg
|
|
request_counter[tx_addr] = 0
|
|
request_done[tx_addr] = False
|
|
|
|
results = {}
|
|
start_time = time.time()
|
|
while True:
|
|
self.rx()
|
|
|
|
if all(request_done.values()):
|
|
break
|
|
|
|
for tx_addr, msg in msgs.items():
|
|
dat: Optional[bytes] = msg.recv()
|
|
|
|
if not dat:
|
|
continue
|
|
|
|
counter = request_counter[tx_addr]
|
|
expected_response = self.response[counter]
|
|
response_valid = dat[:len(expected_response)] == expected_response
|
|
|
|
if response_valid:
|
|
if counter + 1 < len(self.request):
|
|
msg.send(self.request[counter + 1])
|
|
request_counter[tx_addr] += 1
|
|
else:
|
|
results[tx_addr] = dat[len(expected_response):]
|
|
request_done[tx_addr] = True
|
|
else:
|
|
request_done[tx_addr] = True
|
|
cloudlog.warning(f"iso-tp query bad response: 0x{dat.hex()}")
|
|
|
|
if time.time() - start_time > timeout:
|
|
break
|
|
|
|
return results
|