mirror of
https://github.com/dragonpilot/dragonpilot.git
synced 2026-02-19 22:53:57 +08:00
openpilot v0.9.5 release
date: 2023-11-17T23:53:40
master commit: d3aad9ca46
This commit is contained in:
@@ -7,9 +7,8 @@ import struct
|
||||
import hashlib
|
||||
import binascii
|
||||
import datetime
|
||||
import warnings
|
||||
import logging
|
||||
from functools import wraps
|
||||
from functools import wraps, partial
|
||||
from typing import Optional
|
||||
from itertools import accumulate
|
||||
|
||||
@@ -91,53 +90,21 @@ def unpack_can_buffer(dat):
|
||||
|
||||
return (ret, dat)
|
||||
|
||||
def ensure_health_packet_version(fn):
|
||||
|
||||
def ensure_version(desc, lib_field, panda_field, 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.")
|
||||
lib_version = getattr(self, lib_field)
|
||||
panda_version = getattr(self, panda_field)
|
||||
if lib_version != panda_version:
|
||||
raise RuntimeError(f"{desc} packet version mismatch: panda's firmware v{panda_version}, library v{lib_version}. Reflash panda.")
|
||||
return fn(self, *args, **kwargs)
|
||||
return wrapper
|
||||
ensure_can_packet_version = partial(ensure_version, "CAN", "CAN_PACKET_VERSION", "can_version")
|
||||
ensure_can_health_packet_version = partial(ensure_version, "CAN health", "CAN_HEALTH_PACKET_VERSION", "can_health_version")
|
||||
ensure_health_packet_version = partial(ensure_version, "health", "HEALTH_PACKET_VERSION", "health_version")
|
||||
|
||||
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
|
||||
|
||||
def ensure_can_health_packet_version(fn):
|
||||
@wraps(fn)
|
||||
def wrapper(self, *args, **kwargs):
|
||||
if self.can_health_version < self.CAN_HEALTH_PACKET_VERSION:
|
||||
raise RuntimeError("Panda firmware has outdated CAN health packet definition. Reflash panda firmware.")
|
||||
elif self.can_health_version > self.CAN_HEALTH_PACKET_VERSION:
|
||||
raise RuntimeError("Panda python library has outdated CAN health packet definition. Update panda python library.")
|
||||
return fn(self, *args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
def parse_timestamp(dat):
|
||||
a = struct.unpack("HBBBBBB", dat)
|
||||
if a[0] == 0:
|
||||
return None
|
||||
|
||||
try:
|
||||
return datetime.datetime(a[0], a[1], a[2], a[4], a[5], a[6])
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
def unpack_log(dat):
|
||||
return {
|
||||
'id': struct.unpack("H", dat[:2])[0],
|
||||
'timestamp': parse_timestamp(dat[2:10]),
|
||||
'uptime': struct.unpack("I", dat[10:14])[0],
|
||||
'msg': bytes(dat[14:]).decode('utf-8', 'ignore').strip('\x00'),
|
||||
}
|
||||
|
||||
class ALTERNATIVE_EXPERIENCE:
|
||||
DEFAULT = 0
|
||||
@@ -184,6 +151,7 @@ class Panda:
|
||||
GMLAN_CAN2 = 1
|
||||
GMLAN_CAN3 = 2
|
||||
|
||||
USB_PIDS = (0xddee, 0xddcc)
|
||||
REQUEST_IN = usb1.ENDPOINT_IN | usb1.TYPE_VENDOR | usb1.RECIPIENT_DEVICE
|
||||
REQUEST_OUT = usb1.ENDPOINT_OUT | usb1.TYPE_VENDOR | usb1.RECIPIENT_DEVICE
|
||||
|
||||
@@ -201,12 +169,12 @@ class Panda:
|
||||
CAN_PACKET_VERSION = 4
|
||||
HEALTH_PACKET_VERSION = 14
|
||||
CAN_HEALTH_PACKET_VERSION = 5
|
||||
HEALTH_STRUCT = struct.Struct("<IIIIIIIIIBBBBBBHBBBHfBBHBHH")
|
||||
HEALTH_STRUCT = struct.Struct("<IIIIIIIIIBBBBBBHBBBHfBBHBHHB")
|
||||
CAN_HEALTH_STRUCT = struct.Struct("<BIBBBBBBBBIIIIIIIHHBBBIIII")
|
||||
|
||||
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, HW_TYPE_RED_PANDA_V2, HW_TYPE_TRES)
|
||||
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, HW_TYPE_RED_PANDA_V2, HW_TYPE_TRES]
|
||||
|
||||
INTERNAL_DEVICES = (HW_TYPE_UNO, HW_TYPE_DOS, HW_TYPE_TRES)
|
||||
HAS_OBD = (HW_TYPE_BLACK_PANDA, HW_TYPE_UNO, HW_TYPE_DOS, HW_TYPE_RED_PANDA, HW_TYPE_RED_PANDA_V2, HW_TYPE_TRES)
|
||||
@@ -238,6 +206,7 @@ class Panda:
|
||||
FLAG_HYUNDAI_CANFD_HDA2 = 16
|
||||
FLAG_HYUNDAI_CANFD_ALT_BUTTONS = 32
|
||||
FLAG_HYUNDAI_ALT_LIMITS = 64
|
||||
FLAG_HYUNDAI_CANFD_HDA2_ALT_STEERING = 128
|
||||
|
||||
FLAG_TESLA_POWERTRAIN = 1
|
||||
FLAG_TESLA_LONG_CONTROL = 2
|
||||
@@ -248,6 +217,11 @@ class Panda:
|
||||
FLAG_CHRYSLER_RAM_HD = 2
|
||||
|
||||
FLAG_SUBARU_GEN2 = 1
|
||||
FLAG_SUBARU_LONG = 2
|
||||
|
||||
FLAG_SUBARU_PREGLOBAL_REVERSED_DRIVER_TORQUE = 1
|
||||
|
||||
FLAG_NISSAN_ALT_EPS_BUS = 1
|
||||
|
||||
FLAG_GM_HW_CAM = 1
|
||||
FLAG_GM_HW_CAM_LONG = 2
|
||||
@@ -279,6 +253,8 @@ class Panda:
|
||||
if self._handle_open:
|
||||
self._handle.close()
|
||||
self._handle_open = False
|
||||
if self._context is not None:
|
||||
self._context.close()
|
||||
|
||||
def connect(self, claim=True, wait=False):
|
||||
self.close()
|
||||
@@ -286,9 +262,9 @@ class Panda:
|
||||
self._handle = None
|
||||
while self._handle is None:
|
||||
# try USB first, then SPI
|
||||
self._handle, serial, self.bootstub, bcd = self.usb_connect(self._connect_serial, claim=claim)
|
||||
self._context, self._handle, serial, self.bootstub, bcd = self.usb_connect(self._connect_serial, claim=claim)
|
||||
if self._handle is None:
|
||||
self._handle, serial, self.bootstub, bcd = self.spi_connect(self._connect_serial)
|
||||
self._context, self._handle, serial, self.bootstub, bcd = self.spi_connect(self._connect_serial)
|
||||
if not wait:
|
||||
break
|
||||
|
||||
@@ -324,8 +300,8 @@ class Panda:
|
||||
self.set_heartbeat_disabled()
|
||||
self.set_power_save(0)
|
||||
|
||||
@staticmethod
|
||||
def spi_connect(serial, ignore_version=False):
|
||||
@classmethod
|
||||
def spi_connect(cls, serial, ignore_version=False):
|
||||
# get UID to confirm slave is present and up
|
||||
handle = None
|
||||
spi_serial = None
|
||||
@@ -364,16 +340,16 @@ class Panda:
|
||||
err = f"panda protocol mismatch: expected {handle.PROTOCOL_VERSION}, got {spi_version}. reflash panda"
|
||||
raise PandaProtocolMismatch(err)
|
||||
|
||||
return handle, spi_serial, bootstub, None
|
||||
return None, handle, spi_serial, bootstub, None
|
||||
|
||||
@staticmethod
|
||||
def usb_connect(serial, claim=True):
|
||||
@classmethod
|
||||
def usb_connect(cls, serial, claim=True):
|
||||
handle, usb_serial, bootstub, bcd = None, None, None, None
|
||||
context = usb1.USBContext()
|
||||
context.open()
|
||||
try:
|
||||
for device in context.getDeviceList(skip_on_error=True):
|
||||
if device.getVendorID() == 0xbbaa and device.getProductID() in (0xddcc, 0xddee):
|
||||
if device.getVendorID() == 0xbbaa and device.getProductID() in cls.USB_PIDS:
|
||||
try:
|
||||
this_serial = device.getSerialNumber()
|
||||
except Exception:
|
||||
@@ -383,7 +359,7 @@ class Panda:
|
||||
logging.debug("opening device %s %s", this_serial, hex(device.getProductID()))
|
||||
|
||||
usb_serial = this_serial
|
||||
bootstub = device.getProductID() == 0xddee
|
||||
bootstub = (device.getProductID() & 0xF0) == 0xe0
|
||||
handle = device.open()
|
||||
if sys.platform not in ("win32", "cygwin", "msys", "darwin"):
|
||||
handle.setAutoDetachKernelDriver(True)
|
||||
@@ -406,36 +382,36 @@ class Panda:
|
||||
else:
|
||||
context.close()
|
||||
|
||||
return usb_handle, usb_serial, bootstub, bcd
|
||||
return context, usb_handle, usb_serial, bootstub, bcd
|
||||
|
||||
@staticmethod
|
||||
def list():
|
||||
ret = Panda.usb_list()
|
||||
ret += Panda.spi_list()
|
||||
@classmethod
|
||||
def list(cls): # noqa: A003
|
||||
ret = cls.usb_list()
|
||||
ret += cls.spi_list()
|
||||
return list(set(ret))
|
||||
|
||||
@staticmethod
|
||||
def usb_list():
|
||||
@classmethod
|
||||
def usb_list(cls):
|
||||
ret = []
|
||||
try:
|
||||
with usb1.USBContext() as context:
|
||||
for device in context.getDeviceList(skip_on_error=True):
|
||||
if device.getVendorID() == 0xbbaa and device.getProductID() in (0xddcc, 0xddee):
|
||||
if device.getVendorID() == 0xbbaa and device.getProductID() in cls.USB_PIDS:
|
||||
try:
|
||||
serial = device.getSerialNumber()
|
||||
if len(serial) == 24:
|
||||
if len(serial) == 24 or serial == "pedal":
|
||||
ret.append(serial)
|
||||
else:
|
||||
warnings.warn(f"found device with panda descriptors but invalid serial: {serial}", RuntimeWarning)
|
||||
logging.warning(f"found device with panda descriptors but invalid serial: {serial}", RuntimeWarning)
|
||||
except Exception:
|
||||
continue
|
||||
except Exception:
|
||||
logging.exception("exception while listing pandas")
|
||||
return ret
|
||||
|
||||
@staticmethod
|
||||
def spi_list():
|
||||
_, serial, _, _ = Panda.spi_connect(None, ignore_version=True)
|
||||
@classmethod
|
||||
def spi_list(cls):
|
||||
_, _, serial, _, _ = cls.spi_connect(None, ignore_version=True)
|
||||
if serial is not None:
|
||||
return [serial, ]
|
||||
return []
|
||||
@@ -463,23 +439,17 @@ class Panda:
|
||||
def reconnect(self):
|
||||
if self._handle_open:
|
||||
self.close()
|
||||
time.sleep(1.0)
|
||||
|
||||
success = False
|
||||
# wait up to 15 seconds
|
||||
for i in range(0, 15):
|
||||
for _ in range(15*10):
|
||||
try:
|
||||
self.connect()
|
||||
success = True
|
||||
break
|
||||
except Exception:
|
||||
logging.debug("reconnecting is taking %d seconds...", i + 1)
|
||||
try:
|
||||
dfu = PandaDFU(self.get_dfu_serial())
|
||||
dfu.recover()
|
||||
except Exception:
|
||||
pass
|
||||
time.sleep(1.0)
|
||||
pass
|
||||
time.sleep(0.1)
|
||||
if not success:
|
||||
raise Exception("reconnect failed")
|
||||
|
||||
@@ -524,6 +494,10 @@ class Panda:
|
||||
pass
|
||||
|
||||
def flash(self, fn=None, code=None, reconnect=True):
|
||||
if self.up_to_date(fn=fn):
|
||||
logging.debug("flash: already up to date")
|
||||
return
|
||||
|
||||
if not fn:
|
||||
fn = os.path.join(FW_PATH, self._mcu_type.config.app_fn)
|
||||
assert os.path.isfile(fn)
|
||||
@@ -588,9 +562,10 @@ class Panda:
|
||||
serials = Panda.list()
|
||||
return True
|
||||
|
||||
def up_to_date(self) -> bool:
|
||||
def up_to_date(self, fn=None) -> bool:
|
||||
current = self.get_signature()
|
||||
fn = os.path.join(FW_PATH, self.get_mcu_type().config.app_fn)
|
||||
if fn is None:
|
||||
fn = os.path.join(FW_PATH, self.get_mcu_type().config.app_fn)
|
||||
expected = Panda.get_signature_from_firmware(fn)
|
||||
return (current == expected)
|
||||
|
||||
@@ -631,6 +606,7 @@ class Panda:
|
||||
"fan_stall_count": a[24],
|
||||
"sbu1_voltage_mV": a[25],
|
||||
"sbu2_voltage_mV": a[26],
|
||||
"som_reset_triggered": a[27],
|
||||
}
|
||||
|
||||
@ensure_can_health_packet_version
|
||||
@@ -772,13 +748,6 @@ class Panda:
|
||||
def enable_deepsleep(self):
|
||||
self._handle.controlWrite(Panda.REQUEST_OUT, 0xfb, 0, 0, b'')
|
||||
|
||||
def set_esp_power(self, on):
|
||||
self._handle.controlWrite(Panda.REQUEST_OUT, 0xd9, int(on), 0, b'')
|
||||
|
||||
def esp_reset(self, bootmode=0):
|
||||
self._handle.controlWrite(Panda.REQUEST_OUT, 0xda, int(bootmode), 0, b'')
|
||||
time.sleep(0.2)
|
||||
|
||||
def set_safety_mode(self, mode=SAFETY_SILENT, param=0):
|
||||
self._handle.controlWrite(Panda.REQUEST_OUT, 0xdc, mode, param, b'')
|
||||
|
||||
@@ -894,7 +863,7 @@ class Panda:
|
||||
|
||||
def serial_write(self, port_number, ln):
|
||||
ret = 0
|
||||
if type(ln) == str:
|
||||
if isinstance(ln, str):
|
||||
ln = bytes(ln, 'utf-8')
|
||||
for i in range(0, len(ln), 0x20):
|
||||
ret += self._handle.bulkWrite(2, struct.pack("B", port_number) + ln[i:i + 0x20])
|
||||
@@ -987,7 +956,8 @@ class Panda:
|
||||
|
||||
def get_datetime(self):
|
||||
dat = self._handle.controlRead(Panda.REQUEST_IN, 0xa0, 0, 0, 8)
|
||||
return parse_timestamp(dat)
|
||||
a = struct.unpack("HBBBBBB", dat)
|
||||
return datetime.datetime(a[0], a[1], a[2], a[4], a[5], a[6])
|
||||
|
||||
# ****************** Timer *****************
|
||||
def get_microsecond_timer(self):
|
||||
@@ -1019,14 +989,12 @@ class Panda:
|
||||
def set_green_led(self, enabled):
|
||||
self._handle.controlWrite(Panda.REQUEST_OUT, 0xf7, int(enabled), 0, b'')
|
||||
|
||||
# ****************** Logging *****************
|
||||
def get_logs(self, last_id=None, get_all=False):
|
||||
assert (last_id is None) or (0 <= last_id < 0xFFFF)
|
||||
def set_clock_source_period(self, period):
|
||||
self._handle.controlWrite(Panda.REQUEST_OUT, 0xe6, period, 0, b'')
|
||||
|
||||
logs = []
|
||||
dat = self._handle.controlRead(Panda.REQUEST_IN, 0xfd, 1 if get_all else 0, last_id if last_id is not None else 0xFFFF, 0x40)
|
||||
while len(dat) > 0:
|
||||
if len(dat) == 0x40:
|
||||
logs.append(unpack_log(dat))
|
||||
dat = self._handle.controlRead(Panda.REQUEST_IN, 0xfd, 0, 0xFFFF, 0x40)
|
||||
return logs
|
||||
def force_relay_drive(self, intercept_relay_drive, ignition_relay_drive):
|
||||
self._handle.controlWrite(Panda.REQUEST_OUT, 0xc5, (int(intercept_relay_drive) | int(ignition_relay_drive) << 1), 0, b'')
|
||||
|
||||
def read_som_gpio(self) -> bool:
|
||||
r = self._handle.controlRead(Panda.REQUEST_IN, 0xc6, 0, 0, 1)
|
||||
return r[0] == 1
|
||||
|
||||
Reference in New Issue
Block a user