mirror of
https://github.com/dragonpilot/dragonpilot.git
synced 2026-02-19 22:53:57 +08:00
openpilot v0.8.11 release
This commit is contained in:
@@ -8,6 +8,7 @@ import os
|
||||
import time
|
||||
import traceback
|
||||
import sys
|
||||
from functools import wraps
|
||||
from .dfu import PandaDFU, MCU_TYPE_F2, MCU_TYPE_F4, MCU_TYPE_H7 # pylint: disable=import-error
|
||||
from .flash_release import flash_release # noqa pylint: disable=import-error
|
||||
from .update import ensure_st_up_to_date # noqa pylint: disable=import-error
|
||||
@@ -15,28 +16,105 @@ from .serial import PandaSerial # noqa pylint: disable=import-error
|
||||
from .isotp import isotp_send, isotp_recv # pylint: disable=import-error
|
||||
from .config import DEFAULT_FW_FN, DEFAULT_H7_FW_FN # noqa pylint: disable=import-error
|
||||
|
||||
__version__ = '0.0.9'
|
||||
__version__ = '0.0.10'
|
||||
|
||||
BASEDIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../")
|
||||
|
||||
DEBUG = os.getenv("PANDADEBUG") is not None
|
||||
|
||||
def parse_can_buffer(dat):
|
||||
ret = []
|
||||
for j in range(0, len(dat), 0x10):
|
||||
ddat = dat[j:j + 0x10]
|
||||
f1, f2 = struct.unpack("II", ddat[0:8])
|
||||
extended = 4
|
||||
if f1 & extended:
|
||||
address = f1 >> 3
|
||||
else:
|
||||
address = f1 >> 21
|
||||
dddat = ddat[8:8 + (f2 & 0xF)]
|
||||
CANPACKET_HEAD_SIZE = 0x5
|
||||
DLC_TO_LEN = [0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 48, 64]
|
||||
LEN_TO_DLC = {length: dlc for (dlc, length) in enumerate(DLC_TO_LEN)}
|
||||
|
||||
def pack_can_buffer(arr):
|
||||
snds = [b'']
|
||||
idx = 0
|
||||
for address, _, dat, bus in arr:
|
||||
assert len(dat) in LEN_TO_DLC
|
||||
if DEBUG:
|
||||
print(f" R 0x{address:x}: 0x{dddat.hex()}")
|
||||
ret.append((address, f2 >> 16, dddat, (f2 >> 4) & 0xFF))
|
||||
print(f" W 0x{address:x}: 0x{dat.hex()}")
|
||||
extended = 1 if address >= 0x800 else 0
|
||||
data_len_code = LEN_TO_DLC[len(dat)]
|
||||
header = bytearray(5)
|
||||
word_4b = address << 3 | extended << 2
|
||||
header[0] = (data_len_code << 4) | (bus << 1)
|
||||
header[1] = word_4b & 0xFF
|
||||
header[2] = (word_4b >> 8) & 0xFF
|
||||
header[3] = (word_4b >> 16) & 0xFF
|
||||
header[4] = (word_4b >> 24) & 0xFF
|
||||
snds[idx] += header + dat
|
||||
if len(snds[idx]) > 256: # Limit chunks to 256 bytes
|
||||
snds.append(b'')
|
||||
idx += 1
|
||||
|
||||
#Apply counter to each 64 byte packet
|
||||
for idx in range(len(snds)):
|
||||
tx = b''
|
||||
counter = 0
|
||||
for i in range (0, len(snds[idx]), 63):
|
||||
tx += bytes([counter]) + snds[idx][i:i+63]
|
||||
counter += 1
|
||||
snds[idx] = tx
|
||||
return snds
|
||||
|
||||
def unpack_can_buffer(dat):
|
||||
ret = []
|
||||
counter = 0
|
||||
tail = bytearray()
|
||||
for i in range(0, len(dat), 64):
|
||||
if counter != dat[i]:
|
||||
print("CAN: LOST RECV PACKET COUNTER")
|
||||
break
|
||||
counter+=1
|
||||
chunk = tail + dat[i+1:i+64]
|
||||
tail = bytearray()
|
||||
pos = 0
|
||||
while pos<len(chunk):
|
||||
data_len = DLC_TO_LEN[(chunk[pos]>>4)]
|
||||
pckt_len = CANPACKET_HEAD_SIZE + data_len
|
||||
if pckt_len <= len(chunk[pos:]):
|
||||
header = chunk[pos:pos+CANPACKET_HEAD_SIZE]
|
||||
if len(header) < 5:
|
||||
print("CAN: MALFORMED USB RECV PACKET")
|
||||
break
|
||||
bus = (header[0] >> 1) & 0x7
|
||||
address = (header[4] << 24 | header[3] << 16 | header[2] << 8 | header[1]) >> 3
|
||||
returned = (header[1] >> 1) & 0x1
|
||||
rejected = header[1] & 0x1
|
||||
data = chunk[pos + CANPACKET_HEAD_SIZE:pos + CANPACKET_HEAD_SIZE + data_len]
|
||||
if returned:
|
||||
bus += 128
|
||||
if rejected:
|
||||
bus += 192
|
||||
if DEBUG:
|
||||
print(f" R 0x{address:x}: 0x{data.hex()}")
|
||||
ret.append((address, 0, data, bus))
|
||||
pos += pckt_len
|
||||
else:
|
||||
tail = chunk[pos:]
|
||||
break
|
||||
return ret
|
||||
|
||||
def ensure_health_packet_version(fn):
|
||||
@wraps(fn)
|
||||
def wrapper(self, *args, **kwargs):
|
||||
if self.health_version < self.HEALTH_PACKET_VERSION:
|
||||
raise RuntimeError("Panda firmware has outdated health packet definition. Reflash panda firmware.")
|
||||
elif self.health_version > self.HEALTH_PACKET_VERSION:
|
||||
raise RuntimeError("Panda python library has outdated health packet definition. Update panda python library.")
|
||||
return fn(self, *args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
def ensure_can_packet_version(fn):
|
||||
@wraps(fn)
|
||||
def wrapper(self, *args, **kwargs):
|
||||
if self.can_version < self.CAN_PACKET_VERSION:
|
||||
raise RuntimeError("Panda firmware has outdated CAN packet definition. Reflash panda firmware.")
|
||||
elif self.can_version > self.CAN_PACKET_VERSION:
|
||||
raise RuntimeError("Panda python library has outdated CAN packet definition. Update panda python library.")
|
||||
return fn(self, *args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
class PandaWifiStreaming(object):
|
||||
def __init__(self, ip="192.168.0.10", port=1338):
|
||||
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
@@ -55,7 +133,7 @@ class PandaWifiStreaming(object):
|
||||
try:
|
||||
dat, addr = self.sock.recvfrom(0x200 * 0x10)
|
||||
if addr == (self.ip, self.port):
|
||||
ret += parse_can_buffer(dat)
|
||||
ret += unpack_can_buffer(dat)
|
||||
except socket.error as e:
|
||||
if e.errno != 35 and e.errno != 11:
|
||||
traceback.print_exc()
|
||||
@@ -140,6 +218,9 @@ class Panda(object):
|
||||
HW_TYPE_DOS = b'\x06'
|
||||
HW_TYPE_RED_PANDA = b'\x07'
|
||||
|
||||
CAN_PACKET_VERSION = 2
|
||||
HEALTH_PACKET_VERSION = 1
|
||||
|
||||
F2_DEVICES = [HW_TYPE_PEDAL]
|
||||
F4_DEVICES = [HW_TYPE_WHITE_PANDA, HW_TYPE_GREY_PANDA, HW_TYPE_BLACK_PANDA, HW_TYPE_UNO, HW_TYPE_DOS]
|
||||
H7_DEVICES = [HW_TYPE_RED_PANDA]
|
||||
@@ -150,7 +231,13 @@ class Panda(object):
|
||||
|
||||
FLAG_HONDA_ALT_BRAKE = 1
|
||||
FLAG_HONDA_BOSCH_LONG = 2
|
||||
FLAG_HONDA_NIDEC_ALT = 4
|
||||
|
||||
FLAG_HYUNDAI_EV_GAS = 1
|
||||
FLAG_HYUNDAI_HYBRID_GAS = 2
|
||||
FLAG_HYUNDAI_LONG = 4
|
||||
FLAG_TESLA_POWERTRAIN = 1
|
||||
FLAG_TESLA_LONG_CONTROL = 2
|
||||
|
||||
def __init__(self, serial=None, claim=True):
|
||||
self._serial = serial
|
||||
@@ -201,6 +288,7 @@ class Panda(object):
|
||||
break
|
||||
context = usb1.USBContext() # New context needed so new devices show up
|
||||
assert(self._handle is not None)
|
||||
self.health_version, self.can_version = self.get_packets_versions()
|
||||
print("connected")
|
||||
|
||||
def reset(self, enter_bootstub=False, enter_bootloader=False):
|
||||
@@ -239,6 +327,8 @@ class Panda(object):
|
||||
if not success:
|
||||
raise Exception("reconnect failed")
|
||||
|
||||
|
||||
|
||||
@staticmethod
|
||||
def flash_static(handle, code):
|
||||
# confirm flasher is present
|
||||
@@ -342,6 +432,7 @@ class Panda(object):
|
||||
|
||||
# ******************* health *******************
|
||||
|
||||
@ensure_health_packet_version
|
||||
def health(self):
|
||||
dat = self._handle.controlRead(Panda.REQUEST_IN, 0xd2, 0, 0, 44)
|
||||
a = struct.unpack("<IIIIIIIIBBBBBBBHBBB", dat)
|
||||
@@ -392,6 +483,15 @@ class Panda(object):
|
||||
def get_type(self):
|
||||
return self._handle.controlRead(Panda.REQUEST_IN, 0xc1, 0, 0, 0x40)
|
||||
|
||||
# Returns tuple with health packet version and CAN packet/USB packet version
|
||||
def get_packets_versions(self):
|
||||
dat = self._handle.controlRead(Panda.REQUEST_IN, 0xdd, 0, 0, 2)
|
||||
if dat:
|
||||
a = struct.unpack("BB", dat)
|
||||
return (a[0], a[1])
|
||||
else:
|
||||
return (0, 0)
|
||||
|
||||
def is_white(self):
|
||||
return self.get_type() == Panda.HW_TYPE_WHITE_PANDA
|
||||
|
||||
@@ -409,7 +509,7 @@ class Panda(object):
|
||||
|
||||
def is_dos(self):
|
||||
return self.get_type() == Panda.HW_TYPE_DOS
|
||||
|
||||
|
||||
def is_red(self):
|
||||
return self.get_type() == Panda.HW_TYPE_RED_PANDA
|
||||
|
||||
@@ -429,12 +529,18 @@ class Panda(object):
|
||||
def has_canfd(self):
|
||||
return self._mcu_type in Panda.H7_DEVICES
|
||||
|
||||
def is_internal(self):
|
||||
return self.get_type() in [Panda.HW_TYPE_UNO, Panda.HW_TYPE_DOS]
|
||||
|
||||
def get_serial(self):
|
||||
dat = self._handle.controlRead(Panda.REQUEST_IN, 0xd0, 0, 0, 0x20)
|
||||
hashsig, calc_hash = dat[0x1c:], hashlib.sha1(dat[0:0x1c]).digest()[0:4]
|
||||
assert(hashsig == calc_hash)
|
||||
return [dat[0:0x10].decode("utf8"), dat[0x10:0x10 + 10].decode("utf8")]
|
||||
|
||||
def get_usb_serial(self):
|
||||
return self._serial
|
||||
|
||||
def get_secret(self):
|
||||
return self._handle.controlRead(Panda.REQUEST_IN, 0xd0, 1, 0, 0x10)
|
||||
|
||||
@@ -459,10 +565,6 @@ class Panda(object):
|
||||
self.set_heartbeat_disabled()
|
||||
self.set_power_save(0)
|
||||
|
||||
def set_can_forwarding(self, from_bus, to_bus):
|
||||
# TODO: This feature may not work correctly with saturated buses
|
||||
self._handle.controlWrite(Panda.REQUEST_OUT, 0xdd, from_bus, to_bus, b'')
|
||||
|
||||
def set_gmlan(self, bus=2):
|
||||
# TODO: check panda type
|
||||
if bus is None:
|
||||
@@ -488,6 +590,15 @@ class Panda(object):
|
||||
def set_can_data_speed_kbps(self, bus, speed):
|
||||
self._handle.controlWrite(Panda.REQUEST_OUT, 0xf9, bus, int(speed * 10), b'')
|
||||
|
||||
# CAN FD and BRS status
|
||||
def get_canfd_status(self, bus):
|
||||
dat = self._handle.controlRead(Panda.REQUEST_IN, 0xfa, bus, 0, 2)
|
||||
if dat:
|
||||
a = struct.unpack("BB", dat)
|
||||
return (a[0], a[1])
|
||||
else:
|
||||
return (None, None)
|
||||
|
||||
def set_uart_baud(self, uart, rate):
|
||||
self._handle.controlWrite(Panda.REQUEST_OUT, 0xe4, uart, int(rate / 300), b'')
|
||||
|
||||
@@ -505,29 +616,22 @@ class Panda(object):
|
||||
# Timeout is in ms. If set to 0, the timeout is infinite.
|
||||
CAN_SEND_TIMEOUT_MS = 10
|
||||
|
||||
@ensure_can_packet_version
|
||||
def can_send_many(self, arr, timeout=CAN_SEND_TIMEOUT_MS):
|
||||
snds = []
|
||||
transmit = 1
|
||||
extended = 4
|
||||
for addr, _, dat, bus in arr:
|
||||
assert len(dat) <= 8
|
||||
if DEBUG:
|
||||
print(f" W 0x{addr:x}: 0x{dat.hex()}")
|
||||
if addr >= 0x800:
|
||||
rir = (addr << 3) | transmit | extended
|
||||
else:
|
||||
rir = (addr << 21) | transmit
|
||||
snd = struct.pack("II", rir, len(dat) | (bus << 4)) + dat
|
||||
snd = snd.ljust(0x10, b'\x00')
|
||||
snds.append(snd)
|
||||
|
||||
snds = pack_can_buffer(arr)
|
||||
while True:
|
||||
try:
|
||||
if self.wifi:
|
||||
for s in snds:
|
||||
self._handle.bulkWrite(3, s)
|
||||
else:
|
||||
self._handle.bulkWrite(3, b''.join(snds), timeout=timeout)
|
||||
for tx in snds:
|
||||
while True:
|
||||
bs = self._handle.bulkWrite(3, tx, timeout=timeout)
|
||||
tx = tx[bs:]
|
||||
if len(tx) == 0:
|
||||
break
|
||||
print("CAN: PARTIAL SEND MANY, RETRYING")
|
||||
break
|
||||
except (usb1.USBErrorIO, usb1.USBErrorOverflow):
|
||||
print("CAN: BAD SEND MANY, RETRYING")
|
||||
@@ -535,16 +639,17 @@ class Panda(object):
|
||||
def can_send(self, addr, dat, bus, timeout=CAN_SEND_TIMEOUT_MS):
|
||||
self.can_send_many([[addr, None, dat, bus]], timeout=timeout)
|
||||
|
||||
@ensure_can_packet_version
|
||||
def can_recv(self):
|
||||
dat = bytearray()
|
||||
while True:
|
||||
try:
|
||||
dat = self._handle.bulkRead(1, 0x10 * 256)
|
||||
dat = self._handle.bulkRead(1, 16384) # Max receive batch size + 2 extra reserve frames
|
||||
break
|
||||
except (usb1.USBErrorIO, usb1.USBErrorOverflow):
|
||||
print("CAN: BAD RECV, RETRYING")
|
||||
time.sleep(0.1)
|
||||
return parse_can_buffer(dat)
|
||||
return unpack_can_buffer(dat)
|
||||
|
||||
def can_clear(self, bus):
|
||||
"""Clears all messages from the specified internal CAN ringbuffer as
|
||||
|
||||
Reference in New Issue
Block a user