Exemplo n.º 1
0
    def __init__(self, sm=None, pm=None, can_sock=None, CI=None):
        config_realtime_process(4, Priority.CTRL_HIGH)

        # Ensure the current branch is cached, otherwise the first iteration of controlsd lags
        self.branch = get_short_branch("")

        # Setup sockets
        self.pm = pm
        if self.pm is None:
            self.pm = messaging.PubMaster([
                'sendcan', 'controlsState', 'carState', 'carControl',
                'carEvents', 'carParams'
            ])

        self.camera_packets = [
            "roadCameraState", "driverCameraState", "wideRoadCameraState"
        ]

        self.can_sock = can_sock
        if can_sock is None:
            can_timeout = None if os.environ.get('NO_CAN_TIMEOUT',
                                                 False) else 20
            self.can_sock = messaging.sub_sock('can', timeout=can_timeout)

        self.log_sock = messaging.sub_sock('androidLog')

        if CI is None:
            # wait for one pandaState and one CAN packet
            print("Waiting for CAN messages...")
            get_one_can(self.can_sock)

            self.CI, self.CP = get_car(self.can_sock, self.pm.sock['sendcan'])
        else:
            self.CI, self.CP = CI, CI.CP

        params = Params()
        self.joystick_mode = params.get_bool("JoystickDebugMode") or (
            self.CP.notCar and sm is None)
        joystick_packet = ['testJoystick'] if self.joystick_mode else []

        self.sm = sm
        if self.sm is None:
            ignore = ['driverCameraState', 'managerState'
                      ] if SIMULATION else None
            self.sm = messaging.SubMaster(
                [
                    'deviceState', 'pandaStates', 'peripheralState', 'modelV2',
                    'liveCalibration', 'driverMonitoringState',
                    'longitudinalPlan', 'lateralPlan', 'liveLocationKalman',
                    'managerState', 'liveParameters', 'radarState'
                ] + self.camera_packets + joystick_packet,
                ignore_alive=ignore,
                ignore_avg_freq=['radarState', 'longitudinalPlan'])

        # set alternative experiences from parameters
        self.disengage_on_accelerator = params.get_bool(
            "DisengageOnAccelerator")
        self.CP.alternativeExperience = 0
        if not self.disengage_on_accelerator:
            self.CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.DISABLE_DISENGAGE_ON_GAS

        if self.CP.dashcamOnly and params.get_bool("DashcamOverride"):
            self.CP.dashcamOnly = False

        # read params
        self.is_metric = params.get_bool("IsMetric")
        self.is_ldw_enabled = params.get_bool("IsLdwEnabled")
        openpilot_enabled_toggle = params.get_bool("OpenpilotEnabledToggle")
        passive = params.get_bool("Passive") or not openpilot_enabled_toggle

        # detect sound card presence and ensure successful init
        sounds_available = HARDWARE.get_sound_card_online()

        car_recognized = self.CP.carName != 'mock'

        controller_available = self.CI.CC is not None and not passive and not self.CP.dashcamOnly
        self.read_only = not car_recognized or not controller_available or self.CP.dashcamOnly
        if self.read_only:
            safety_config = car.CarParams.SafetyConfig.new_message()
            safety_config.safetyModel = car.CarParams.SafetyModel.noOutput
            self.CP.safetyConfigs = [safety_config]

        # Write CarParams for radard
        cp_bytes = self.CP.to_bytes()
        params.put("CarParams", cp_bytes)
        put_nonblocking("CarParamsCache", cp_bytes)

        self.CC = car.CarControl.new_message()
        self.CS_prev = car.CarState.new_message()
        self.AM = AlertManager()
        self.events = Events()

        self.LoC = LongControl(self.CP)
        self.VM = VehicleModel(self.CP)

        self.LaC: LatControl
        if self.CP.steerControlType == car.CarParams.SteerControlType.angle:
            self.LaC = LatControlAngle(self.CP, self.CI)
        elif self.CP.lateralTuning.which() == 'pid':
            self.LaC = LatControlPID(self.CP, self.CI)
        elif self.CP.lateralTuning.which() == 'indi':
            self.LaC = LatControlINDI(self.CP, self.CI)
        elif self.CP.lateralTuning.which() == 'torque':
            self.LaC = LatControlTorque(self.CP, self.CI)

        self.initialized = False
        self.state = State.disabled
        self.enabled = False
        self.active = False
        self.can_rcv_error = False
        self.soft_disable_timer = 0
        self.v_cruise_kph = 255
        self.v_cruise_kph_last = 0
        self.mismatch_counter = 0
        self.cruise_mismatch_counter = 0
        self.can_rcv_error_counter = 0
        self.last_blinker_frame = 0
        self.distance_traveled = 0
        self.last_functional_fan_frame = 0
        self.events_prev = []
        self.current_alert_types = [ET.PERMANENT]
        self.logged_comm_issue = None
        self.button_timers = {
            ButtonEvent.Type.decelCruise: 0,
            ButtonEvent.Type.accelCruise: 0
        }
        self.last_actuators = car.CarControl.Actuators.new_message()
        self.desired_curvature = 0.0
        self.desired_curvature_rate = 0.0

        # TODO: no longer necessary, aside from process replay
        self.sm['liveParameters'].valid = True

        self.startup_event = get_startup_event(car_recognized,
                                               controller_available,
                                               len(self.CP.carFw) > 0)

        if not sounds_available:
            self.events.add(EventName.soundsUnavailable, static=True)
        if not car_recognized:
            self.events.add(EventName.carUnrecognized, static=True)
            if len(self.CP.carFw) > 0:
                set_offroad_alert("Offroad_CarUnrecognized", True)
            else:
                set_offroad_alert("Offroad_NoFirmware", True)
        elif self.read_only:
            self.events.add(EventName.dashcamMode, static=True)
        elif self.joystick_mode:
            self.events.add(EventName.joystickDebug, static=True)
            self.startup_event = None

        # controlsd is driven by can recv, expected at 100Hz
        self.rk = Ratekeeper(100, print_delay_threshold=None)
        self.prof = Profiler(False)  # off by default
Exemplo n.º 2
0
def set_offroad_alert_if_changed(offroad_alert: str, show_alert: bool, extra_text: Optional[str]=None):
  if prev_offroad_states.get(offroad_alert, None) == (show_alert, extra_text):
    return
  prev_offroad_states[offroad_alert] = (show_alert, extra_text)
  set_offroad_alert(offroad_alert, show_alert, extra_text)
Exemplo n.º 3
0
def register(show_spinner=False) -> str:
  params = Params()
  if "commadotai" in API_HOST and (Params().get_bool("dp_jetson") or Params().get_bool("dp_atl")):
    return UNREGISTERED_DONGLE_ID
  if not params.get_bool('dp_reg'):
    return UNREGISTERED_DONGLE_ID
  params.put("SubscriberInfo", HARDWARE.get_subscriber_info())

  IMEI = params.get("IMEI", encoding='utf8')
  HardwareSerial = params.get("HardwareSerial", encoding='utf8')
  dongle_id = params.get("DongleId", encoding='utf8')
  needs_registration = None in (IMEI, HardwareSerial, dongle_id)

  # create a key for auth
  # your private key is kept on your device persist partition and never sent to our servers
  # do not erase your persist partition
  if not os.path.isfile(PERSIST+"/comma/id_rsa.pub"):
    needs_registration = True
    cloudlog.warning("generating your personal RSA key")
    mkdirs_exists_ok(PERSIST+"/comma")
    assert os.system("openssl genrsa -out "+PERSIST+"/comma/id_rsa.tmp 2048") == 0
    assert os.system("openssl rsa -in "+PERSIST+"/comma/id_rsa.tmp -pubout -out "+PERSIST+"/comma/id_rsa.tmp.pub") == 0
    os.rename(PERSIST+"/comma/id_rsa.tmp", PERSIST+"/comma/id_rsa")
    os.rename(PERSIST+"/comma/id_rsa.tmp.pub", PERSIST+"/comma/id_rsa.pub")

  if needs_registration:
    if show_spinner:
      spinner = Spinner()
      spinner.update("registering device")

    # Create registration token, in the future, this key will make JWTs directly
    with open(PERSIST+"/comma/id_rsa.pub") as f1, open(PERSIST+"/comma/id_rsa") as f2:
      public_key = f1.read()
      private_key = f2.read()

    # Block until we get the imei
    serial = HARDWARE.get_serial()
    start_time = time.monotonic()
    imei1, imei2 = None, None
    while imei1 is None and imei2 is None:
      try:
        imei1, imei2 = HARDWARE.get_imei(0), HARDWARE.get_imei(1)
      except Exception:
        cloudlog.exception("Error getting imei, trying again...")
        time.sleep(1)

      if time.monotonic() - start_time > 60 and show_spinner:
        spinner.update(f"registering device - serial: {serial}, IMEI: ({imei1}, {imei2})")

    params.put("IMEI", imei1)
    params.put("HardwareSerial", serial)

    backoff = 0
    start_time = time.monotonic()
    while True:
      try:
        register_token = jwt.encode({'register': True, 'exp': datetime.utcnow() + timedelta(hours=1)}, private_key, algorithm='RS256')
        cloudlog.info("getting pilotauth")
        resp = api_get("v2/pilotauth/", method='POST', timeout=15,
                       imei=imei1, imei2=imei2, serial=serial, public_key=public_key, register_token=register_token)

        if resp.status_code in (402, 403):
          cloudlog.info(f"Unable to register device, got {resp.status_code}")
          dongle_id = UNREGISTERED_DONGLE_ID
        else:
          dongleauth = json.loads(resp.text)
          dongle_id = dongleauth["dongle_id"]
        break
      except Exception:
        cloudlog.exception("failed to authenticate")
        return UNREGISTERED_DONGLE_ID

        # backoff = min(backoff + 1, 15)
        # time.sleep(backoff)

      # if time.monotonic() - start_time > 60 and show_spinner:
      #   spinner.update(f"registering device - serial: {serial}, IMEI: ({imei1}, {imei2})")

    if show_spinner:
      spinner.close()

  if dongle_id:
    params.put("DongleId", dongle_id)
    set_offroad_alert("Offroad_UnofficialHardware", dongle_id == UNREGISTERED_DONGLE_ID)
  return dongle_id
Exemplo n.º 4
0
def attempt_update(wait_helper):
    cloudlog.info("attempting git update inside staging overlay")

    setup_git_options(OVERLAY_MERGED)

    git_fetch_output = run(["git", "fetch"], OVERLAY_MERGED, low_priority=True)
    cloudlog.info("git fetch success: %s", git_fetch_output)

    cur_hash = run(["git", "rev-parse", "HEAD"], OVERLAY_MERGED).rstrip()
    upstream_hash = run(["git", "rev-parse", "@{u}"], OVERLAY_MERGED).rstrip()
    new_version = cur_hash != upstream_hash

    err_msg = "Failed to add the host to the list of known hosts (/data/data/com.termux/files/home/.ssh/known_hosts).\n"
    git_fetch_result = len(git_fetch_output) > 0 and (git_fetch_output !=
                                                      err_msg)

    cloudlog.info("comparing %s to %s" % (cur_hash, upstream_hash))
    if new_version or git_fetch_result:
        cloudlog.info("Running update")

        if new_version:
            cloudlog.info("git reset in progress")
            r = [
                run(["git", "reset", "--hard", "@{u}"],
                    OVERLAY_MERGED,
                    low_priority=True),
                run(["git", "clean", "-xdf"],
                    OVERLAY_MERGED,
                    low_priority=True),
                run(["git", "submodule", "init"],
                    OVERLAY_MERGED,
                    low_priority=True),
                run(["git", "submodule", "update"],
                    OVERLAY_MERGED,
                    low_priority=True),
            ]
            cloudlog.info("git reset success: %s", '\n'.join(r))

            # Download the accompanying NEOS version if it doesn't match the current version
            with open(NEOS_VERSION, "r") as f:
                cur_neos = f.read().strip()

            updated_neos = run([
                "bash", "-c",
                r"unset REQUIRED_NEOS_VERSION && source launch_env.sh && \
                           echo -n $REQUIRED_NEOS_VERSION"
            ], OVERLAY_MERGED).strip()

            cloudlog.info(f"NEOS version check: {cur_neos} vs {updated_neos}")
            if cur_neos != updated_neos:
                cloudlog.info(
                    f"Beginning background download for NEOS {updated_neos}")

                set_offroad_alert("Offroad_NeosUpdate", True)
                updater_path = os.path.join(OVERLAY_MERGED,
                                            "installer/updater/updater")
                update_manifest = f"file://{OVERLAY_MERGED}/installer/updater/update.json"

                neos_downloaded = False
                start_time = time.monotonic()
                # Try to download for one day
                while (time.monotonic() - start_time <
                       60 * 60 * 24) and not wait_helper.shutdown:
                    wait_helper.ready_event.clear()
                    try:
                        run([updater_path, "bgcache", update_manifest],
                            OVERLAY_MERGED,
                            low_priority=True)
                        neos_downloaded = True
                        break
                    except subprocess.CalledProcessError:
                        cloudlog.info(
                            "NEOS background download failed, retrying")
                        wait_helper.sleep(120)

                # If the download failed, we'll show the alert again when we retry
                set_offroad_alert("Offroad_NeosUpdate", False)
                if not neos_downloaded:
                    raise Exception("Failed to download NEOS update")

                cloudlog.info(
                    f"NEOS background download successful, took {time.monotonic() - start_time} seconds"
                )

        # Create the finalized, ready-to-swap update
        finalize_from_ovfs()
        cloudlog.info("openpilot update successful!")
    else:
        cloudlog.info("nothing new from git at this time")

    set_update_available_params(new_version)
    return new_version
Exemplo n.º 5
0
def register(show_spinner=False) -> Optional[str]:
    params = Params()
    params.put("SubscriberInfo", HARDWARE.get_subscriber_info())

    IMEI = params.get("IMEI", encoding='utf8')
    HardwareSerial = params.get("HardwareSerial", encoding='utf8')
    dongle_id: Optional[str] = params.get("DongleId", encoding='utf8')
    needs_registration = None in (IMEI, HardwareSerial, dongle_id)

    pubkey = Path(PERSIST + "/comma/id_rsa.pub")
    if not pubkey.is_file():
        dongle_id = UNREGISTERED_DONGLE_ID
        cloudlog.warning(f"missing public key: {pubkey}")
    elif needs_registration:
        if show_spinner:
            spinner = Spinner()
            spinner.update("registering device")

        # Create registration token, in the future, this key will make JWTs directly
        with open(PERSIST +
                  "/comma/id_rsa.pub") as f1, open(PERSIST +
                                                   "/comma/id_rsa") as f2:
            public_key = f1.read()
            private_key = f2.read()

        # Block until we get the imei
        serial = HARDWARE.get_serial()
        start_time = time.monotonic()
        imei1: Optional[str] = None
        imei2: Optional[str] = None
        while imei1 is None and imei2 is None:
            try:
                imei1, imei2 = HARDWARE.get_imei(0), HARDWARE.get_imei(1)
            except Exception:
                cloudlog.exception("Error getting imei, trying again...")
                time.sleep(1)

            if time.monotonic() - start_time > 60 and show_spinner:
                spinner.update(
                    f"registering device - serial: {serial}, IMEI: ({imei1}, {imei2})"
                )

        params.put("IMEI", imei1)
        params.put("HardwareSerial", serial)

        backoff = 0
        start_time = time.monotonic()
        while True:
            try:
                register_token = jwt.encode(
                    {
                        'register': True,
                        'exp': datetime.utcnow() + timedelta(hours=1)
                    },
                    private_key,
                    algorithm='RS256')
                cloudlog.info("getting pilotauth")
                resp = api_get("v2/pilotauth/",
                               method='POST',
                               timeout=15,
                               imei=imei1,
                               imei2=imei2,
                               serial=serial,
                               public_key=public_key,
                               register_token=register_token)

                if resp.status_code in (402, 403):
                    cloudlog.info(
                        f"Unable to register device, got {resp.status_code}")
                    dongle_id = UNREGISTERED_DONGLE_ID
                else:
                    dongleauth = json.loads(resp.text)
                    dongle_id = dongleauth["dongle_id"]
                break
            except Exception:
                cloudlog.exception("failed to authenticate")
                backoff = min(backoff + 1, 15)
                time.sleep(backoff)

            if time.monotonic() - start_time > 60 and show_spinner:
                spinner.update(
                    f"registering device - serial: {serial}, IMEI: ({imei1}, {imei2})"
                )

        if show_spinner:
            spinner.close()

    if dongle_id:
        params.put("DongleId", dongle_id)
        set_offroad_alert("Offroad_UnofficialHardware",
                          (dongle_id == UNREGISTERED_DONGLE_ID) and not PC)
    return dongle_id
Exemplo n.º 6
0
def thermald_thread():
  # prevent LEECO from undervoltage
  BATT_PERC_OFF = 100
  
  health_timeout = int(1000 * 2.5 * DT_TRML)  # 2.5x the expected health frequency

  # now loop
  thermal_sock = messaging.pub_sock('thermal')
  health_sock = messaging.sub_sock('health', timeout=health_timeout)
  location_sock = messaging.sub_sock('gpsLocation')

  ignition = False
  fan_speed = 0
  count = 0

  off_ts = None
  started_ts = None
  started_seen = False
  thermal_status = ThermalStatus.green
  thermal_status_prev = ThermalStatus.green
  usb_power = True
  usb_power_prev = True
  current_branch = get_git_branch()

  network_type = NetworkType.none
  network_strength = NetworkStrength.unknown

  current_filter = FirstOrderFilter(0., CURRENT_TAU, DT_TRML)
  cpu_temp_filter = FirstOrderFilter(0., CPU_TEMP_TAU, DT_TRML)
  health_prev = None
  fw_version_match_prev = True
  charging_disabled = False
  current_update_alert = None
  time_valid_prev = True
  should_start_prev = False
  handle_fan = None
  is_uno = False
  has_relay = False

  params = Params()
  pm = PowerMonitoring()
  no_panda_cnt = 0

  IsOpenpilotViewEnabled = 0
  ts_last_ip = 0
  ip_addr = '255.255.255.255'

  # sound trigger
  sound_trigger = 1
  opkrAutoShutdown = 0

  env = dict(os.environ)
  env['LD_LIBRARY_PATH'] = mediaplayer

  getoff_alert = Params().get('OpkrEnableGetoffAlert') == b'1'
  params = Params()
  if int(params.get('OpkrAutoShutdown')) == 0:
    opkrAutoShutdown = 0
  elif int(params.get('OpkrAutoShutdown')) == 1:
    opkrAutoShutdown = 5
  elif int(params.get('OpkrAutoShutdown')) == 2:
    opkrAutoShutdown = 30
  elif int(params.get('OpkrAutoShutdown')) == 3:
    opkrAutoShutdown = 60
  elif int(params.get('OpkrAutoShutdown')) == 4:
    opkrAutoShutdown = 180
  elif int(params.get('OpkrAutoShutdown')) == 5:
    opkrAutoShutdown = 300
  elif int(params.get('OpkrAutoShutdown')) == 6:
    opkrAutoShutdown = 600
  elif int(params.get('OpkrAutoShutdown')) == 7:
    opkrAutoShutdown = 1800
  elif int(params.get('OpkrAutoShutdown')) == 8:
    opkrAutoShutdown = 3600
  elif int(params.get('OpkrAutoShutdown')) == 9:
    opkrAutoShutdown = 10800
  else:
    opkrAutoShutdown = 18000
  
  while 1:
    ts = sec_since_boot()
    health = messaging.recv_sock(health_sock, wait=True)
    location = messaging.recv_sock(location_sock)
    location = location.gpsLocation if location else None
    msg = read_thermal()

    if health is not None:
      usb_power = health.health.usbPowerMode != log.HealthData.UsbPowerMode.client
      ignition = health.health.ignitionLine or health.health.ignitionCan

      # Setup fan handler on first connect to panda
      if handle_fan is None and health.health.hwType != log.HealthData.HwType.unknown:
        is_uno = health.health.hwType == log.HealthData.HwType.uno

        if is_uno or not ANDROID:
          cloudlog.info("Setting up UNO fan handler")
          handle_fan = handle_fan_uno
        else:
          cloudlog.info("Setting up EON fan handler")
          setup_eon_fan()
          handle_fan = handle_fan_eon

      # Handle disconnect
      if health_prev is not None:
        if health.health.hwType == log.HealthData.HwType.unknown and \
          health_prev.health.hwType != log.HealthData.HwType.unknown:
          params.panda_disconnect()
      health_prev = health

      # If we lose connection to the panda, wait 5 seconds before going offroad
      if health.health.hwType == log.HealthData.HwType.unknown:
        no_panda_cnt += 1
        if no_panda_cnt > DISCONNECT_TIMEOUT / DT_TRML:
          if ignition:
            cloudlog.error("Lost panda connection while onroad")
          ignition = False
      else:
        no_panda_cnt = 0
        ignition = health.health.ignitionLine or health.health.ignitionCan

      # Setup fan handler on first connect to panda
      if handle_fan is None and health.health.hwType != log.HealthData.HwType.unknown:
        is_uno = health.health.hwType == log.HealthData.HwType.uno
        has_relay = health.health.hwType in [log.HealthData.HwType.blackPanda, log.HealthData.HwType.uno, log.HealthData.HwType.dos]

        if is_uno or not ANDROID:
          cloudlog.info("Setting up UNO fan handler")
          handle_fan = handle_fan_uno
        else:
          cloudlog.info("Setting up EON fan handler")
          setup_eon_fan()
          handle_fan = handle_fan_eon

      # Handle disconnect
      if health_prev is not None:
        if health.health.hwType == log.HealthData.HwType.unknown and \
          health_prev.health.hwType != log.HealthData.HwType.unknown:
          params.panda_disconnect()
      health_prev = health
    elif ignition == False or IsOpenpilotViewEnabled:
      IsOpenpilotViewEnabled = int( params.get("IsOpenpilotViewEnabled") )      
      ignition = IsOpenpilotViewEnabled

    # get_network_type is an expensive call. update every 10s
    if (count % int(10. / DT_TRML)) == 0:
      try:
        network_type = get_network_type()
        network_strength = get_network_strength(network_type)
      except Exception:
        cloudlog.exception("Error getting network status")

    msg.thermal.freeSpace = get_available_percent(default=100.0) / 100.0
    msg.thermal.memUsedPercent = int(round(psutil.virtual_memory().percent))
    msg.thermal.cpuPerc = int(round(psutil.cpu_percent()))
    msg.thermal.networkType = network_type
    msg.thermal.networkStrength = network_strength
    msg.thermal.batteryPercent = get_battery_capacity()
    msg.thermal.batteryStatus = get_battery_status()
    msg.thermal.batteryCurrent = get_battery_current()
    msg.thermal.batteryVoltage = get_battery_voltage()
    msg.thermal.usbOnline = get_usb_present()

    # Fake battery levels on uno for frame
    if is_uno:
      msg.thermal.batteryPercent = 100
      msg.thermal.batteryStatus = "Charging"
      msg.thermal.bat = 0

    # update ip every 10 seconds
    ts = sec_since_boot()
    if ts - ts_last_ip >= 10.:
      try:
        result = subprocess.check_output(["ifconfig", "wlan0"], encoding='utf8')  # pylint: disable=unexpected-keyword-arg
        ip_addr = re.findall(r"inet addr:((\d+\.){3}\d+)", result)[0][0]
      except:
        ip_addr = 'N/A'
      ts_last_ip = ts
    msg.thermal.ipAddr = ip_addr

    current_filter.update(msg.thermal.batteryCurrent / 1e6)

    # TODO: add car battery voltage check
    max_cpu_temp = cpu_temp_filter.update(
      max(msg.thermal.cpu0,
          msg.thermal.cpu1,
          msg.thermal.cpu2,
          msg.thermal.cpu3) / 10.0)

    max_comp_temp = max(max_cpu_temp, msg.thermal.mem / 10., msg.thermal.gpu / 10.)
    bat_temp = msg.thermal.bat / 1000.

    if handle_fan is not None:
      fan_speed = handle_fan(max_cpu_temp, bat_temp, fan_speed, ignition)
      msg.thermal.fanSpeed = fan_speed

    # If device is offroad we want to cool down before going onroad
    # since going onroad increases load and can make temps go over 107
    # We only do this if there is a relay that prevents the car from faulting
    if max_cpu_temp > 107. or bat_temp >= 63. or (has_relay and (started_ts is None) and max_cpu_temp > 70.0):
      # onroad not allowed
      thermal_status = ThermalStatus.danger
    elif max_comp_temp > 96.0 or bat_temp > 60.:
      # hysteresis between onroad not allowed and engage not allowed
      thermal_status = clip(thermal_status, ThermalStatus.red, ThermalStatus.danger)
    elif max_cpu_temp > 94.0:
      # hysteresis between engage not allowed and uploader not allowed
      thermal_status = clip(thermal_status, ThermalStatus.yellow, ThermalStatus.red)
    elif max_cpu_temp > 80.0:
      # uploader not allowed
      thermal_status = ThermalStatus.yellow
    elif max_cpu_temp > 75.0:
      # hysteresis between uploader not allowed and all good
      thermal_status = clip(thermal_status, ThermalStatus.green, ThermalStatus.yellow)
    else:
      # all good
      thermal_status = ThermalStatus.green

    # **** starting logic ****

    # Check for last update time and display alerts if needed
    now = datetime.datetime.utcnow()

    # show invalid date/time alert
    time_valid = now.year >= 2019
    if time_valid and not time_valid_prev:
      set_offroad_alert("Offroad_InvalidTime", False)
    if not time_valid and time_valid_prev:
      set_offroad_alert("Offroad_InvalidTime", True)
    time_valid_prev = time_valid

    # Show update prompt
#    try:
#      last_update = datetime.datetime.fromisoformat(params.get("LastUpdateTime", encoding='utf8'))
#    except (TypeError, ValueError):
#      last_update = now
#    dt = now - last_update

#    update_failed_count = params.get("UpdateFailedCount")
#    update_failed_count = 0 if update_failed_count is None else int(update_failed_count)
#    last_update_exception = params.get("LastUpdateException", encoding='utf8')

#    if update_failed_count > 15 and last_update_exception is not None:
#      if current_branch in ["release2", "dashcam"]:
#        extra_text = "Ensure the software is correctly installed"
#      else:
#        extra_text = last_update_exception

#      if current_update_alert != "update" + extra_text:
#        current_update_alert = "update" + extra_text
#        set_offroad_alert("Offroad_ConnectivityNeeded", False)
#        set_offroad_alert("Offroad_ConnectivityNeededPrompt", False)
#        set_offroad_alert("Offroad_UpdateFailed", True, extra_text=extra_text)
#    elif dt.days > DAYS_NO_CONNECTIVITY_MAX and update_failed_count > 1:
#      if current_update_alert != "expired":
#        current_update_alert = "expired"
#        set_offroad_alert("Offroad_UpdateFailed", False)
#        set_offroad_alert("Offroad_ConnectivityNeededPrompt", False)
#        set_offroad_alert("Offroad_ConnectivityNeeded", True)
#    elif dt.days > DAYS_NO_CONNECTIVITY_PROMPT:
#      remaining_time = str(max(DAYS_NO_CONNECTIVITY_MAX - dt.days, 0))
#      if current_update_alert != "prompt" + remaining_time:
#        current_update_alert = "prompt" + remaining_time
#        set_offroad_alert("Offroad_UpdateFailed", False)
#        set_offroad_alert("Offroad_ConnectivityNeeded", False)
#        set_offroad_alert("Offroad_ConnectivityNeededPrompt", True, extra_text=f"{remaining_time} days.")
#    elif current_update_alert is not None:
#      current_update_alert = None
#      set_offroad_alert("Offroad_UpdateFailed", False)
#      set_offroad_alert("Offroad_ConnectivityNeeded", False)
#      set_offroad_alert("Offroad_ConnectivityNeededPrompt", False)

    do_uninstall = params.get("DoUninstall") == b"1"
    accepted_terms = params.get("HasAcceptedTerms") == terms_version
    completed_training = params.get("CompletedTrainingVersion") == training_version

    panda_signature = params.get("PandaFirmware")
    fw_version_match = (panda_signature is None) or (panda_signature == FW_SIGNATURE)   # don't show alert is no panda is connected (None)

    should_start = ignition

    # with 2% left, we killall, otherwise the phone will take a long time to boot
    should_start = should_start and msg.thermal.freeSpace > 0.02

    # confirm we have completed training and aren't uninstalling
    should_start = should_start and accepted_terms and completed_training and (not do_uninstall)

    # check for firmware mismatch
    #should_start = should_start and fw_version_match

    # check if system time is valid
    should_start = should_start and time_valid

    # don't start while taking snapshot
    if not should_start_prev:
      is_viewing_driver = params.get("IsDriverViewEnabled") == b"1"
      is_taking_snapshot = params.get("IsTakingSnapshot") == b"1"
      should_start = should_start and (not is_taking_snapshot) and (not is_viewing_driver)

    if fw_version_match and not fw_version_match_prev:
      set_offroad_alert("Offroad_PandaFirmwareMismatch", False)
    if not fw_version_match and fw_version_match_prev:
      set_offroad_alert("Offroad_PandaFirmwareMismatch", True)

    # if any CPU gets above 107 or the battery gets above 63, kill all processes
    # controls will warn with CPU above 95 or battery above 60
    if thermal_status >= ThermalStatus.danger:
      should_start = False
      if thermal_status_prev < ThermalStatus.danger:
        set_offroad_alert("Offroad_TemperatureTooHigh", True)
    else:
      if thermal_status_prev >= ThermalStatus.danger:
        set_offroad_alert("Offroad_TemperatureTooHigh", False)

    if should_start:
      if not should_start_prev:
        params.delete("IsOffroad")

      off_ts = None
      if started_ts is None:
        started_ts = sec_since_boot()
        started_seen = True
        os.system('echo performance > /sys/class/devfreq/soc:qcom,cpubw/governor')
    else:
      if should_start_prev or (count == 0):
        put_nonblocking("IsOffroad", "1")

      started_ts = None
      if off_ts is None:
        off_ts = sec_since_boot()
        os.system('echo powersave > /sys/class/devfreq/soc:qcom,cpubw/governor')

      if sound_trigger == 1 and msg.thermal.batteryStatus == "Discharging" and started_seen and (sec_since_boot() - off_ts) > 1 and getoff_alert:
        subprocess.Popen([mediaplayer + 'mediaplayer', '/data/openpilot/selfdrive/assets/sounds/eondetach.wav'], shell = False, stdin=None, stdout=None, stderr=None, env = env, close_fds=True)
        sound_trigger = 0
      # shutdown if the battery gets lower than 3%, it's discharging, we aren't running for
      # more than a minute but we were running
      if msg.thermal.batteryPercent <= BATT_PERC_OFF and msg.thermal.batteryStatus == "Discharging" and \
         started_seen and opkrAutoShutdown and (sec_since_boot() - off_ts) > opkrAutoShutdown:
        os.system('LD_LIBRARY_PATH="" svc power shutdown')

    charging_disabled = check_car_battery_voltage(should_start, health, charging_disabled, msg)

    if msg.thermal.batteryCurrent > 0:
      msg.thermal.batteryStatus = "Discharging"
    else:
      msg.thermal.batteryStatus = "Charging"

    
    msg.thermal.chargingDisabled = charging_disabled
    
    prebuiltlet = Params().get('PutPrebuiltOn') == b'1'
    if not os.path.isfile(prebuiltfile) and prebuiltlet:
      os.system("cd /data/openpilot; touch prebuilt")
    elif os.path.isfile(prebuiltfile) and not prebuiltlet:
      os.system("cd /data/openpilot; rm -f prebuilt")

    # Offroad power monitoring
    pm.calculate(health)
    msg.thermal.offroadPowerUsage = pm.get_power_used()

    msg.thermal.chargingError = current_filter.x > 0. and msg.thermal.batteryPercent < 90  # if current is positive, then battery is being discharged
    msg.thermal.started = started_ts is not None
    msg.thermal.startedTs = int(1e9*(started_ts or 0))

    msg.thermal.thermalStatus = thermal_status
    thermal_sock.send(msg.to_bytes())

    if usb_power_prev and not usb_power:
      set_offroad_alert("Offroad_ChargeDisabled", True)
    elif usb_power and not usb_power_prev:
      set_offroad_alert("Offroad_ChargeDisabled", False)

    thermal_status_prev = thermal_status
    usb_power_prev = usb_power
    fw_version_match_prev = fw_version_match
    should_start_prev = should_start

    print(msg)

    # report to server once per minute
    if (count % int(60. / DT_TRML)) == 0:
      cloudlog.event("STATUS_PACKET",
                     count=count,
                     health=(health.to_dict() if health else None),
                     location=(location.to_dict() if location else None),
                     thermal=msg.to_dict())

    count += 1
Exemplo n.º 7
0
def thermald_thread():
  health_timeout = int(1000 * 2.5 * DT_TRML)  # 2.5x the expected health frequency

  # now loop
  thermal_sock = messaging.pub_sock('thermal')
  health_sock = messaging.sub_sock('health', timeout=health_timeout)
  location_sock = messaging.sub_sock('gpsLocation')

  ignition = False
  fan_speed = 0
  count = 0

  off_ts = None
  started_ts = None
  started_seen = False
  thermal_status = ThermalStatus.green
  thermal_status_prev = ThermalStatus.green
  usb_power = True
  usb_power_prev = True
  current_branch = get_git_branch()

  network_type = NetworkType.none
  network_strength = NetworkStrength.unknown

  current_filter = FirstOrderFilter(0., CURRENT_TAU, DT_TRML)
  cpu_temp_filter = FirstOrderFilter(0., CPU_TEMP_TAU, DT_TRML)
  health_prev = None
  fw_version_match_prev = True
  current_update_alert = None
  time_valid_prev = True
  should_start_prev = False
  handle_fan = None
  is_uno = False
  has_relay = False

  params = Params()
  pm = PowerMonitoring()
  no_panda_cnt = 0

  thermal_config = get_thermal_config()

  while 1:
    health = messaging.recv_sock(health_sock, wait=True)
    location = messaging.recv_sock(location_sock)
    location = location.gpsLocation if location else None
    msg = read_thermal(thermal_config)

    if health is not None:
      usb_power = health.health.usbPowerMode != log.HealthData.UsbPowerMode.client

      # If we lose connection to the panda, wait 5 seconds before going offroad
      if health.health.hwType == log.HealthData.HwType.unknown:
        no_panda_cnt += 1
        if no_panda_cnt > DISCONNECT_TIMEOUT / DT_TRML:
          if ignition:
            cloudlog.error("Lost panda connection while onroad")
          ignition = False
      else:
        no_panda_cnt = 0
        ignition = health.health.ignitionLine or health.health.ignitionCan

      # Setup fan handler on first connect to panda
      if handle_fan is None and health.health.hwType != log.HealthData.HwType.unknown:
        is_uno = health.health.hwType == log.HealthData.HwType.uno
        has_relay = health.health.hwType in [log.HealthData.HwType.blackPanda, log.HealthData.HwType.uno, log.HealthData.HwType.dos]

        if (not EON) or is_uno:
          cloudlog.info("Setting up UNO fan handler")
          handle_fan = handle_fan_uno
        else:
          cloudlog.info("Setting up EON fan handler")
          setup_eon_fan()
          handle_fan = handle_fan_eon

      # Handle disconnect
      if health_prev is not None:
        if health.health.hwType == log.HealthData.HwType.unknown and \
          health_prev.health.hwType != log.HealthData.HwType.unknown:
          params.panda_disconnect()
      health_prev = health

    # get_network_type is an expensive call. update every 10s
    if (count % int(10. / DT_TRML)) == 0:
      try:
        network_type = get_network_type()
        network_strength = get_network_strength(network_type)
      except Exception:
        cloudlog.exception("Error getting network status")

    msg.thermal.freeSpace = get_available_percent(default=100.0) / 100.0
    msg.thermal.memUsedPercent = int(round(psutil.virtual_memory().percent))
    msg.thermal.cpuPerc = int(round(psutil.cpu_percent()))
    msg.thermal.networkType = network_type
    msg.thermal.networkStrength = network_strength
    msg.thermal.batteryPercent = get_battery_capacity()
    msg.thermal.batteryStatus = get_battery_status()
    msg.thermal.batteryCurrent = get_battery_current()
    msg.thermal.batteryVoltage = get_battery_voltage()
    msg.thermal.usbOnline = get_usb_present()

    # Fake battery levels on uno for frame
    if (not EON) or is_uno:
      msg.thermal.batteryPercent = 100
      msg.thermal.batteryStatus = "Charging"
      msg.thermal.bat = 0

    current_filter.update(msg.thermal.batteryCurrent / 1e6)

    # TODO: add car battery voltage check
    max_cpu_temp = cpu_temp_filter.update(max(msg.thermal.cpu))
    max_comp_temp = max(max_cpu_temp, msg.thermal.mem, max(msg.thermal.gpu))
    bat_temp = msg.thermal.bat

    if handle_fan is not None:
      fan_speed = handle_fan(max_cpu_temp, bat_temp, fan_speed, ignition)
      msg.thermal.fanSpeed = fan_speed

    # If device is offroad we want to cool down before going onroad
    # since going onroad increases load and can make temps go over 107
    # We only do this if there is a relay that prevents the car from faulting
    if max_cpu_temp > 107. or bat_temp >= 63. or (has_relay and (started_ts is None) and max_cpu_temp > 70.0):
      # onroad not allowed
      thermal_status = ThermalStatus.danger
    elif max_comp_temp > 96.0 or bat_temp > 60.:
      # hysteresis between onroad not allowed and engage not allowed
      thermal_status = clip(thermal_status, ThermalStatus.red, ThermalStatus.danger)
    elif max_cpu_temp > 94.0:
      # hysteresis between engage not allowed and uploader not allowed
      thermal_status = clip(thermal_status, ThermalStatus.yellow, ThermalStatus.red)
    elif max_cpu_temp > 80.0:
      # uploader not allowed
      thermal_status = ThermalStatus.yellow
    elif max_cpu_temp > 75.0:
      # hysteresis between uploader not allowed and all good
      thermal_status = clip(thermal_status, ThermalStatus.green, ThermalStatus.yellow)
    else:
      # all good
      thermal_status = ThermalStatus.green

    # **** starting logic ****

    # Check for last update time and display alerts if needed
    now = datetime.datetime.utcnow()

    # show invalid date/time alert
    time_valid = now.year >= 2019
    if time_valid and not time_valid_prev:
      set_offroad_alert("Offroad_InvalidTime", False)
    if not time_valid and time_valid_prev:
      set_offroad_alert("Offroad_InvalidTime", True)
    time_valid_prev = time_valid

    # Show update prompt
    try:
      last_update = datetime.datetime.fromisoformat(params.get("LastUpdateTime", encoding='utf8'))
    except (TypeError, ValueError):
      last_update = now
    dt = now - last_update

    update_failed_count = params.get("UpdateFailedCount")
    update_failed_count = 0 if update_failed_count is None else int(update_failed_count)
    last_update_exception = params.get("LastUpdateException", encoding='utf8')

    if update_failed_count > 15 and last_update_exception is not None:
      if current_branch in ["release2", "dashcam"]:
        extra_text = "Ensure the software is correctly installed"
      else:
        extra_text = last_update_exception

      if current_update_alert != "update" + extra_text:
        current_update_alert = "update" + extra_text
        set_offroad_alert("Offroad_ConnectivityNeeded", False)
        set_offroad_alert("Offroad_ConnectivityNeededPrompt", False)
        set_offroad_alert("Offroad_UpdateFailed", True, extra_text=extra_text)
    elif dt.days > DAYS_NO_CONNECTIVITY_MAX and update_failed_count > 1:
      if current_update_alert != "expired":
        current_update_alert = "expired"
        set_offroad_alert("Offroad_UpdateFailed", False)
        set_offroad_alert("Offroad_ConnectivityNeededPrompt", False)
        set_offroad_alert("Offroad_ConnectivityNeeded", True)
    elif dt.days > DAYS_NO_CONNECTIVITY_PROMPT:
      remaining_time = str(max(DAYS_NO_CONNECTIVITY_MAX - dt.days, 0))
      if current_update_alert != "prompt" + remaining_time:
        current_update_alert = "prompt" + remaining_time
        set_offroad_alert("Offroad_UpdateFailed", False)
        set_offroad_alert("Offroad_ConnectivityNeeded", False)
        set_offroad_alert("Offroad_ConnectivityNeededPrompt", True, extra_text=f"{remaining_time} days.")
    elif current_update_alert is not None:
      current_update_alert = None
      set_offroad_alert("Offroad_UpdateFailed", False)
      set_offroad_alert("Offroad_ConnectivityNeeded", False)
      set_offroad_alert("Offroad_ConnectivityNeededPrompt", False)

    do_uninstall = params.get("DoUninstall") == b"1"
    accepted_terms = params.get("HasAcceptedTerms") == terms_version
    completed_training = params.get("CompletedTrainingVersion") == training_version

    panda_signature = params.get("PandaFirmware")
    fw_version_match = (panda_signature is None) or (panda_signature == FW_SIGNATURE)   # don't show alert is no panda is connected (None)

    should_start = ignition

    # with 2% left, we killall, otherwise the phone will take a long time to boot
    should_start = should_start and msg.thermal.freeSpace > 0.02

    # confirm we have completed training and aren't uninstalling
    should_start = should_start and accepted_terms and completed_training and (not do_uninstall)

    # check for firmware mismatch
    should_start = should_start and fw_version_match

    # check if system time is valid
    should_start = should_start and time_valid

    # don't start while taking snapshot
    if not should_start_prev:
      is_viewing_driver = params.get("IsDriverViewEnabled") == b"1"
      is_taking_snapshot = params.get("IsTakingSnapshot") == b"1"
      should_start = should_start and (not is_taking_snapshot) and (not is_viewing_driver)

    if fw_version_match and not fw_version_match_prev:
      set_offroad_alert("Offroad_PandaFirmwareMismatch", False)
    if not fw_version_match and fw_version_match_prev:
      set_offroad_alert("Offroad_PandaFirmwareMismatch", True)

    # if any CPU gets above 107 or the battery gets above 63, kill all processes
    # controls will warn with CPU above 95 or battery above 60
    if thermal_status >= ThermalStatus.danger:
      should_start = False
      if thermal_status_prev < ThermalStatus.danger:
        set_offroad_alert("Offroad_TemperatureTooHigh", True)
    else:
      if thermal_status_prev >= ThermalStatus.danger:
        set_offroad_alert("Offroad_TemperatureTooHigh", False)

    if should_start:
      if not should_start_prev:
        params.delete("IsOffroad")

      off_ts = None
      if started_ts is None:
        started_ts = sec_since_boot()
        started_seen = True
        os.system('echo performance > /sys/class/devfreq/soc:qcom,cpubw/governor')
    else:
      if should_start_prev or (count == 0):
        put_nonblocking("IsOffroad", "1")

      started_ts = None
      if off_ts is None:
        off_ts = sec_since_boot()
        os.system('echo powersave > /sys/class/devfreq/soc:qcom,cpubw/governor')

    # Offroad power monitoring
    pm.calculate(health)
    msg.thermal.offroadPowerUsage = pm.get_power_used()
    msg.thermal.carBatteryCapacity = max(0, pm.get_car_battery_capacity())

    # Check if we need to disable charging (handled by boardd)
    msg.thermal.chargingDisabled = pm.should_disable_charging(health, off_ts)

    # Check if we need to shut down
    if pm.should_shutdown(health, off_ts, started_seen, LEON):
      cloudlog.info(f"shutting device down, offroad since {off_ts}")
      # TODO: add function for blocking cloudlog instead of sleep
      time.sleep(10)
      os.system('LD_LIBRARY_PATH="" svc power shutdown')

    msg.thermal.chargingError = current_filter.x > 0. and msg.thermal.batteryPercent < 90  # if current is positive, then battery is being discharged
    msg.thermal.started = started_ts is not None
    msg.thermal.startedTs = int(1e9*(started_ts or 0))

    msg.thermal.thermalStatus = thermal_status
    thermal_sock.send(msg.to_bytes())

    if usb_power_prev and not usb_power:
      set_offroad_alert("Offroad_ChargeDisabled", True)
    elif usb_power and not usb_power_prev:
      set_offroad_alert("Offroad_ChargeDisabled", False)

    thermal_status_prev = thermal_status
    usb_power_prev = usb_power
    fw_version_match_prev = fw_version_match
    should_start_prev = should_start

    # report to server once per minute
    if (count % int(60. / DT_TRML)) == 0:
      cloudlog.event("STATUS_PACKET",
                     count=count,
                     health=(health.to_dict() if health else None),
                     location=(location.to_dict() if location else None),
                     thermal=msg.to_dict())

    count += 1
Exemplo n.º 8
0
from common.basedir import BASEDIR
from common.params import Params
from selfdrive.controls.lib.alertmanager import set_offroad_alert

if __name__ == "__main__":
    params = Params()

    with open(
            os.path.join(BASEDIR,
                         "selfdrive/controls/lib/alerts_offroad.json")) as f:
        offroad_alerts = json.load(f)

    t = 10 if len(sys.argv) < 2 else int(sys.argv[1])
    while True:
        print("setting alert update")
        params.put("UpdateAvailable", "1")
        params.put("ReleaseNotes", "this is a new version")
        time.sleep(t)
        params.put("UpdateAvailable", "0")

        # cycle through normal alerts
        for a in offroad_alerts:
            print("setting alert:", a)
            set_offroad_alert(a, True)
            time.sleep(t)
            set_offroad_alert(a, False)

        print("no alert")
        time.sleep(t)
Exemplo n.º 9
0
  def __init__(self, sm=None, pm=None, can_sock=None):
    config_realtime_process(4 if TICI else 3, Priority.CTRL_HIGH)

    # Setup sockets
    self.pm = pm
    if self.pm is None:
      self.pm = messaging.PubMaster(['jvePilotState', 'sendcan', 'controlsState', 'carState',
                                     'carControl', 'carEvents', 'carParams'])

    self.camera_packets = ["roadCameraState", "driverCameraState"]
    if TICI:
      self.camera_packets.append("wideRoadCameraState")

    params = Params()
    self.joystick_mode = params.get_bool("JoystickDebugMode")
    joystick_packet = ['testJoystick'] if self.joystick_mode else []

    self.sm = sm
    if self.sm is None:
      ignore = ['driverCameraState', 'managerState'] if SIMULATION else None
      self.sm = messaging.SubMaster(['jvePilotUIState', 'deviceState', 'pandaStates', 'peripheralState', 'modelV2', 'liveCalibration',
                                     'driverMonitoringState', 'longitudinalPlan', 'lateralPlan', 'liveLocationKalman',
                                     'managerState', 'liveParameters', 'radarState'] + self.camera_packets + joystick_packet,
                                     ignore_alive=ignore, ignore_avg_freq=['radarState', 'longitudinalPlan'])

    self.can_sock = can_sock
    if can_sock is None:
      can_timeout = None if os.environ.get('NO_CAN_TIMEOUT', False) else 100
      self.can_sock = messaging.sub_sock('can', timeout=can_timeout)

    if TICI:
      self.log_sock = messaging.sub_sock('androidLog')

    # wait for one pandaState and one CAN packet
    print("Waiting for CAN messages...")
    get_one_can(self.can_sock)

    self.CI, self.CP = get_car(self.can_sock, self.pm.sock['sendcan'])

    # read params
    self.is_metric = params.get_bool("IsMetric")
    self.is_ldw_enabled = params.get_bool("IsLdwEnabled")
    community_feature_toggle = params.get_bool("CommunityFeaturesToggle")
    openpilot_enabled_toggle = params.get_bool("OpenpilotEnabledToggle")
    passive = params.get_bool("Passive") or not openpilot_enabled_toggle

    # detect sound card presence and ensure successful init
    sounds_available = HARDWARE.get_sound_card_online()

    car_recognized = self.CP.carName != 'mock'

    controller_available = self.CI.CC is not None and not passive and not self.CP.dashcamOnly
    community_feature = self.CP.communityFeature or \
                        self.CP.fingerprintSource == car.CarParams.FingerprintSource.can
    community_feature_disallowed = community_feature and (not community_feature_toggle)
    self.read_only = not car_recognized or not controller_available or \
                       self.CP.dashcamOnly or community_feature_disallowed
    if self.read_only:
      safety_config = car.CarParams.SafetyConfig.new_message()
      safety_config.safetyModel = car.CarParams.SafetyModel.noOutput
      self.CP.safetyConfigs = [safety_config]

    # Write CarParams for radard
    cp_bytes = self.CP.to_bytes()
    params.put("CarParams", cp_bytes)
    put_nonblocking("CarParamsCache", cp_bytes)

    self.CC = car.CarControl.new_message()
    self.AM = AlertManager()
    self.events = Events()

    self.LoC = LongControl(self.CP)
    self.VM = VehicleModel(self.CP)

    if self.CP.steerControlType == car.CarParams.SteerControlType.angle:
      self.LaC = LatControlAngle(self.CP)
    elif self.CP.lateralTuning.which() == 'pid':
      self.LaC = LatControlPID(self.CP, self.CI)
    elif self.CP.lateralTuning.which() == 'indi':
      self.LaC = LatControlINDI(self.CP)
    elif self.CP.lateralTuning.which() == 'lqr':
      self.LaC = LatControlLQR(self.CP)

    self.initialized = False
    self.state = State.disabled
    self.enabled = False
    self.active = False
    self.can_rcv_error = False
    self.soft_disable_timer = 0
    self.v_cruise_kph = 255
    self.v_cruise_kph_last = 0
    self.mismatch_counter = 0
    self.cruise_mismatch_counter = 0
    self.can_error_counter = 0
    self.last_blinker_frame = 0
    self.saturated_count = 0
    self.distance_traveled = 0
    self.last_functional_fan_frame = 0
    self.events_prev = []
    self.current_alert_types = [ET.PERMANENT]
    self.logged_comm_issue = False

    self.v_target = 0.
    self.buttonPressTimes = {}
    self.cachedParams = CachedParams()
    self.reverse_acc_button_change = self.cachedParams.get('jvePilot.settings.reverseAccSpeedChange', 0) == "1"
    self.jvePilotState = car.JvePilotState.new_message()
    self.jvePilotState.carControl.autoFollow = params.get_bool('jvePilot.settings.autoFollow')
    self.jvePilotState.carControl.useLaneLines = not params.get_bool('EndToEndToggle')
    self.jvePilotState.carControl.accEco = int(params.get('jvePilot.carState.accEco', encoding='utf8') or "1")
    self.ui_notify()

    # TODO: no longer necessary, aside from process replay
    self.sm['liveParameters'].valid = True

    self.startup_event = get_startup_event(car_recognized, controller_available, len(self.CP.carFw) > 0)

    if not sounds_available:
      self.events.add(EventName.soundsUnavailable, static=True)
    if community_feature_disallowed and car_recognized and not self.CP.dashcamOnly:
      self.events.add(EventName.communityFeatureDisallowed, static=True)
    if not car_recognized:
      self.events.add(EventName.carUnrecognized, static=True)
      if len(self.CP.carFw) > 0:
        set_offroad_alert("Offroad_CarUnrecognized", True)
      else:
        set_offroad_alert("Offroad_NoFirmware", True)
    elif self.read_only:
      self.events.add(EventName.dashcamMode, static=True)
    elif self.joystick_mode:
      self.events.add(EventName.joystickDebug, static=True)
      self.startup_event = None

    # controlsd is driven by can recv, expected at 100Hz
    self.rk = Ratekeeper(100, print_delay_threshold=None)
    self.prof = Profiler(False)  # off by default