openpilot v0.9.5 release

date: 2023-11-17T23:53:40
master commit: d3aad9ca46
This commit is contained in:
Vehicle Researcher
2023-11-17 23:53:40 +00:00
parent eff388b1b6
commit b2f2dabe71
861 changed files with 33808 additions and 28421 deletions

View File

@@ -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