Exemple #1
0
    def update_d_poly(self, v_ego):
        # only offset left and right lane lines; offsetting p_poly does not make sense
        self.last_modified_check, self.modified = get_last_modified(
            LAST_MODIFIED_LANE_PLANNER, self.last_modified_check,
            self.modified)
        if self.last_modified != self.modified:
            self.dp_camera_offset, self.last_modified_dp_camera_offset = param_get_if_updated(
                "dp_camera_offset", "int", self.dp_camera_offset,
                self.last_modified_dp_camera_offset)
            self.last_modified = self.modified
        offset = self.dp_camera_offset * 0.01 if self.dp_camera_offset != 0 else 0
        self.l_poly[3] += offset
        self.r_poly[3] += offset
        self.p_poly[3] += offset

        # Reduce reliance on lanelines that are too far apart or
        # will be in a few seconds
        l_prob, r_prob = self.l_prob, self.r_prob
        width_poly = self.l_poly - self.r_poly
        prob_mods = []
        for t_check in [0.0, 1.5, 3.0]:
            width_at_t = eval_poly(width_poly, t_check * (v_ego + 7))
            # dp note
            # [3.5, 4.5] = from 3m to 4m (lane width)
            # [1.0, 0.0] = "use lane line" to "not use lane line at all"
            prob_mods.append(interp(width_at_t, [3.5, 4.5], [1.0, 0.0]))
        mod = min(prob_mods)
        l_prob *= mod
        r_prob *= mod

        # Reduce reliance on uncertain lanelines
        l_std_mod = interp(self.l_std, [.15, .3], [1.0, 0.0])
        r_std_mod = interp(self.r_std, [.15, .3], [1.0, 0.0])
        l_prob *= l_std_mod
        r_prob *= r_std_mod

        # Find current lanewidth
        self.lane_width_certainty += 0.05 * (l_prob * r_prob -
                                             self.lane_width_certainty)
        current_lane_width = abs(self.l_poly[3] - self.r_poly[3])
        self.lane_width_estimate += 0.005 * (current_lane_width -
                                             self.lane_width_estimate)
        speed_lane_width = interp(v_ego, [0., 14., 20.],
                                  [2.5, 3., 3.5])  # German Standards
        self.lane_width = self.lane_width_certainty * self.lane_width_estimate + \
                          (1 - self.lane_width_certainty) * speed_lane_width

        clipped_lane_width = min(4.0, self.lane_width)
        path_from_left_lane = self.l_poly.copy()
        path_from_left_lane[3] -= clipped_lane_width / 2.0
        path_from_right_lane = self.r_poly.copy()
        path_from_right_lane[3] += clipped_lane_width / 2.0

        lr_prob = l_prob + r_prob - l_prob * r_prob

        d_poly_lane = (l_prob * path_from_left_lane + r_prob *
                       path_from_right_lane) / (l_prob + r_prob + 0.0001)
        self.d_poly = lr_prob * d_poly_lane + (1.0 -
                                               lr_prob) * self.p_poly.copy()
Exemple #2
0
 def _get_live_params(self):
     self.last_modified_check, self.modified = get_last_modified(
         LAST_MODIFIED_DYNAMIC_FOLLOW, self.last_modified_check,
         self.modified)
     if self.last_modified != self.modified:
         #self.dp_dynamic_follow, self.dp_dynamic_follow_last_modified = param_get_if_updated("dp_dynamic_follow", "int", self.dp_dynamic_follow, self.dp_dynamic_follow_last_modified)
         self.global_df_mod, self.dp_dynamic_follow_multiplier_last_modified = param_get_if_updated(
             "dp_dynamic_follow_multiplier", "float", self.global_df_mod,
             self.dp_dynamic_follow_multiplier_last_modified)
         if self.global_df_mod != 1.:
             self.global_df_mod = clip(self.global_df_mod, .85, 1.2)
         self.min_TR, self.dp_dynamic_follow_min_tr_last_modified = param_get_if_updated(
             "dp_dynamic_follow_min_tr", "float", self.min_TR,
             self.dp_dynamic_follow_min_tr_last_modified)
         if self.min_TR != .9:
             self.min_TR = clip(self.min_TR, .85, 1.6)
         self.last_modified = self.modified
Exemple #3
0
def uploader_fn(exit_event):
  cloudlog.info("uploader_fn")

  params = Params()
  dongle_id = params.get("DongleId")

  if dongle_id is None:
    return
    # cloudlog.info("uploader missing dongle_id")
    # raise Exception("uploader can't start without dongle id")
  else:
    dongle_id = dongle_id.decode('utf8')

  uploader = Uploader(dongle_id, ROOT)

  # dp
  dp_upload_on_mobile = False
  dp_last_modified_upload_on_mobile = None
  dp_upload_on_hotspot = False
  dp_last_modified_upload_on_hotspot = None

  modified = None
  last_modified = None
  last_modified_check = None

  backoff = 0.1
  counter = 0
  should_upload = False
  while not exit_event.is_set():
    offroad = params.get("IsOffroad") == b'1'
    allow_raw_upload = (params.get("IsUploadRawEnabled") != b"0") and offroad
    check_network = (counter % 12 == 0 if offroad else True)
    if check_network:
      on_hotspot = is_on_hotspot()
      on_wifi = is_on_wifi()

      # dp - load temp monitor conf
      last_modified_check, modified = get_last_modified(LAST_MODIFIED_UPLOADER, last_modified_check, modified)
      if last_modified != modified:
        dp_upload_on_mobile, dp_last_modified_upload_on_mobile = param_get_if_updated("dp_upload_on_mobile", "bool", dp_upload_on_mobile, dp_last_modified_upload_on_mobile)
        dp_upload_on_hotspot, dp_last_modified_upload_on_hotspot = param_get_if_updated("dp_upload_on_hotspot", "bool", dp_upload_on_hotspot, dp_last_modified_upload_on_hotspot)
        last_modified = modified

      should_upload = on_wifi and not on_hotspot

    d = uploader.next_file_to_upload(with_raw=allow_raw_upload and should_upload)
    counter += 1
    if d is None:  # Nothing to upload
      time.sleep(60 if offroad else 5)
      continue

    key, fn = d

    cloudlog.event("uploader_netcheck", is_on_hotspot=on_hotspot, is_on_wifi=on_wifi)
    cloudlog.info("to upload %r", d)
    success = uploader.upload(key, fn)
    if success:
      backoff = 0.1
    else:
      cloudlog.info("backoff %r", backoff)
      time.sleep(backoff + random.uniform(0, backoff))
      backoff = min(backoff*2, 120)
    cloudlog.info("upload done, success=%r", success)
def confd_thread():
    sm = messaging.SubMaster(['thermal'])
    pm = messaging.PubMaster(['dragonConf'])

    last_dp_msg = None
    frame = 0
    update_params = False
    modified = None
    last_modified = None
    last_modified_check = None
    started = False
    free_space = 1
    battery_percent = 0
    overheat = False
    last_charging_ctrl = False
    last_started = False
    dashcam = Dashcam()
    last_dashcam_recorded = False

    while True:
        start_sec = sec_since_boot()
        msg = messaging.new_message('dragonConf')
        if last_dp_msg is not None:
            msg.dragonConf = last_dp_msg
        '''
    ===================================================
    load thermald data every 3 seconds
    ===================================================
    '''
        if frame % (HERTZ * 3) == 0:
            started, free_space, battery_percent, overheat = pull_thermald(
                frame, sm, started, free_space, battery_percent, overheat)
        setattr(msg.dragonConf, get_struct_name('dp_thermal_started'), started)
        setattr(msg.dragonConf, get_struct_name('dp_thermal_overheat'),
                overheat)
        '''
    ===================================================
    hotspot on boot
    we do it after 30 secs just in case
    ===================================================
    '''
        if frame == (HERTZ * 30) and param_get("dp_hotspot_on_boot", "bool",
                                               False):
            os.system("service call wifi 37 i32 0 i32 1 &")
        '''
    ===================================================
    check dp_last_modified every second
    ===================================================
    '''
        if not update_params:
            last_modified_check, modified = get_last_modified(
                LAST_MODIFIED_SYSTEMD, last_modified_check, modified)
            if last_modified != modified:
                update_params = True
                last_modified = modified
        '''
    ===================================================
    conditionally set update_params to true 
    ===================================================
    '''
        # force updating param when `started` changed
        if last_started != started:
            update_params = True
            last_started = started

        if frame == 0:
            update_params = True
        '''
    ===================================================
    conditionally update dp param base on stock param 
    ===================================================
    '''
        if update_params and params.get("LaneChangeEnabled") == b"1":
            params.put("dp_steering_on_signal", "0")
        '''
    ===================================================
    push param vals to message
    ===================================================
    '''
        if update_params:
            msg = update_conf_all(confs, msg, frame == 0)
            update_params = False
        '''
    ===================================================
    push once
    ===================================================
    '''
        if frame == 0:
            setattr(msg.dragonConf, get_struct_name('dp_locale'), get_locale())
            put_nonblocking('dp_is_updating', '0')
        '''
    ===================================================
    push ip addr every 10 secs
    ===================================================
    '''
        if frame % (HERTZ * 10) == 0:
            msg = update_ip(msg)
        '''
    ===================================================
    push is_updating status every 5 secs
    ===================================================
    '''
        if frame % (HERTZ * 5) == 0:
            msg = update_updating(msg)
        '''
    ===================================================
    update msg based on some custom logic
    ===================================================
    '''
        msg = update_custom_logic(msg)
        '''
    ===================================================
    battery ctrl every 30 secs
    PowerMonitor in thermald turns back on every mins
    so lets turn it off more frequent
    ===================================================
    '''
        if frame % (HERTZ * 30) == 0:
            last_charging_ctrl = process_charging_ctrl(msg, last_charging_ctrl,
                                                       battery_percent)
        '''
    ===================================================
    dashcam
    ===================================================
    '''
        if msg.dragonConf.dpDashcam and frame % HERTZ == 0:
            dashcam.run(started, free_space)
            last_dashcam_recorded = True
        if last_dashcam_recorded and not msg.dragonConf.dpDashcam:
            dashcam.stop()
            last_dashcam_recorded = False
        '''
    ===================================================
    finalise
    ===================================================
    '''
        last_dp_msg = msg.dragonConf
        pm.send('dragonConf', msg)
        frame += 1
        sleep = DELAY - (sec_since_boot() - start_sec)
        if sleep > 0:
            time.sleep(sleep)
Exemple #5
0
def thermald_thread():

  pm = messaging.PubMaster(['deviceState'])

  pandaState_timeout = int(1000 * 2.5 * DT_TRML)  # 2.5x the expected pandaState frequency
  pandaState_sock = messaging.sub_sock('pandaStates', timeout=pandaState_timeout)
  sm = messaging.SubMaster(["peripheralState", "gpsLocationExternal", "managerState"])

  fan_speed = 0
  count = 0

  startup_conditions = {
    "ignition": False,
  }
  startup_conditions_prev = startup_conditions.copy()

  off_ts = None
  started_ts = None
  started_seen = False
  thermal_status = ThermalStatus.green
  usb_power = True

  network_type = NetworkType.none
  network_strength = NetworkStrength.unknown
  network_info = None
  modem_version = None
  registered_count = 0
  nvme_temps = None
  modem_temps = None

  current_filter = FirstOrderFilter(0., CURRENT_TAU, DT_TRML)
  temp_filter = FirstOrderFilter(0., TEMP_TAU, DT_TRML)
  pandaState_prev = None
  should_start_prev = False
  in_car = False
  handle_fan = None
  is_uno = False
  ui_running_prev = False

  power_monitor = PowerMonitoring()
  no_panda_cnt = 0

  HARDWARE.initialize_hardware()
  thermal_config = HARDWARE.get_thermal_config()

  # TODO: use PI controller for UNO
  controller = PIController(k_p=0, k_i=2e-3, neg_limit=-80, pos_limit=0, rate=(1 / DT_TRML))

  # Leave flag for loggerd to indicate device was left onroad
  if params.get_bool("IsOnroad"):
    params.put_bool("BootedOnroad", True)

  # dp
  peripheralStateLast = None
  dp_no_batt = params.get_bool("dp_no_batt")
  dp_temp_monitor = True
  dp_last_modified_temp_monitor = None

  dp_auto_shutdown = False
  dp_last_modified_auto_shutdown = None

  dp_auto_shutdown_in = 90
  dp_last_modified_auto_shutdown_in = None

  dp_fan_mode = 0
  dp_fan_mode_last = None

  modified = None
  last_modified = None
  last_modified_check = None

  dp_no_offroad_fix = params.get_bool('dp_no_offroad_fix')
  if JETSON:
    handle_fan = handle_fan_jetson

  while True:
    # dp - load temp monitor conf
    last_modified_check, modified = get_last_modified(LAST_MODIFIED_THERMALD, last_modified_check, modified)
    if last_modified != modified:
      dp_temp_monitor, dp_last_modified_temp_monitor = param_get_if_updated("dp_temp_monitor", "bool", dp_temp_monitor, dp_last_modified_temp_monitor)
      dp_auto_shutdown, dp_last_modified_auto_shutdown = param_get_if_updated("dp_auto_shutdown", "bool", dp_auto_shutdown, dp_last_modified_auto_shutdown)
      dp_auto_shutdown_in, dp_last_modified_auto_shutdown_in = param_get_if_updated("dp_auto_shutdown_in", "int", dp_auto_shutdown_in, dp_last_modified_auto_shutdown_in)
      dp_fan_mode, dp_fan_mode_last = param_get_if_updated("dp_fan_mode", "int", dp_fan_mode, dp_fan_mode_last)
      last_modified = modified

    pandaStates = messaging.recv_sock(pandaState_sock, wait=True)

    sm.update(0)
    peripheralState = sm['peripheralState']

    msg = read_thermal(thermal_config)

    if pandaStates is not None and len(pandaStates.pandaStates) > 0:
      pandaState = pandaStates.pandaStates[0]

      # If we lose connection to the panda, wait 5 seconds before going offroad
      if pandaState.pandaType == log.PandaState.PandaType.unknown:
        no_panda_cnt += 1
        if no_panda_cnt > DISCONNECT_TIMEOUT / DT_TRML:
          if startup_conditions["ignition"]:
            cloudlog.error("Lost panda connection while onroad")
          startup_conditions["ignition"] = False
      else:
        no_panda_cnt = 0
        startup_conditions["ignition"] = pandaState.ignitionLine or pandaState.ignitionCan

      in_car = pandaState.harnessStatus != log.PandaState.HarnessStatus.notConnected
      usb_power = peripheralState.usbPowerMode != log.PeripheralState.UsbPowerMode.client

      # Setup fan handler on first connect to panda
      if not JETSON and handle_fan is None and peripheralState.pandaType != log.PandaState.PandaType.unknown:
        is_uno = peripheralState.pandaType == log.PandaState.PandaType.uno

        if TICI:
          cloudlog.info("Setting up TICI fan handler")
          handle_fan = handle_fan_tici
        elif is_uno or PC:
          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 pandaState_prev is not None:
        if pandaState.pandaType == log.PandaState.PandaType.unknown and \
          pandaState_prev.pandaType != log.PandaState.PandaType.unknown:
          params.clear_all(ParamKeyType.CLEAR_ON_PANDA_DISCONNECT)
      pandaState_prev = pandaState

    # these are expensive calls. update every 10s
    if (count % int(10. / DT_TRML)) == 0:
      try:
        network_type = HARDWARE.get_network_type()
        network_strength = HARDWARE.get_network_strength(network_type)
        network_info = HARDWARE.get_network_info()  # pylint: disable=assignment-from-none
        nvme_temps = HARDWARE.get_nvme_temperatures()
        modem_temps = HARDWARE.get_modem_temperatures()

        # Log modem version once
        if modem_version is None:
          modem_version = HARDWARE.get_modem_version()  # pylint: disable=assignment-from-none
          if modem_version is not None:
            cloudlog.warning(f"Modem version: {modem_version}")

        if TICI and (network_info.get('state', None) == "REGISTERED"):
          registered_count += 1
        else:
          registered_count = 0

        if registered_count > 10:
          cloudlog.warning(f"Modem stuck in registered state {network_info}. nmcli conn up lte")
          os.system("nmcli conn up lte")
          registered_count = 0

      except Exception:
        cloudlog.exception("Error getting network status")

    msg.deviceState.freeSpacePercent = get_available_percent(default=100.0)
    msg.deviceState.memoryUsagePercent = int(round(psutil.virtual_memory().percent))
    msg.deviceState.cpuUsagePercent = [int(round(n)) for n in psutil.cpu_percent(percpu=True)]
    msg.deviceState.gpuUsagePercent = int(round(HARDWARE.get_gpu_usage_percent()))
    msg.deviceState.networkType = network_type
    msg.deviceState.networkStrength = network_strength
    if network_info is not None:
      msg.deviceState.networkInfo = network_info
    if nvme_temps is not None:
      msg.deviceState.nvmeTempC = nvme_temps
    if modem_temps is not None:
      msg.deviceState.modemTempC = modem_temps

    msg.deviceState.screenBrightnessPercent = HARDWARE.get_screen_brightness()
    msg.deviceState.batteryPercent = HARDWARE.get_battery_capacity()
    msg.deviceState.batteryCurrent = HARDWARE.get_battery_current()
    msg.deviceState.usbOnline = HARDWARE.get_usb_present()

    # Fake battery levels on uno for frame
    if dp_no_batt:
      msg.deviceState.batteryPercent = 100

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

    max_comp_temp = temp_filter.update(
      max(max(msg.deviceState.cpuTempC), msg.deviceState.memoryTempC, max(msg.deviceState.gpuTempC))
    )

    if handle_fan is not None:
      fan_speed = handle_fan(dp_fan_mode, controller, max_comp_temp, fan_speed, startup_conditions["ignition"])
      msg.deviceState.fanSpeedPercentDesired = fan_speed

    is_offroad_for_5_min = (started_ts is None) and ((not started_seen) or (off_ts is None) or (sec_since_boot() - off_ts > 60 * 5))
    if is_offroad_for_5_min and max_comp_temp > OFFROAD_DANGER_TEMP:
      # If device is offroad we want to cool down before going onroad
      # since going onroad increases load and can make temps go over 107
      thermal_status = ThermalStatus.danger
    else:
      current_band = THERMAL_BANDS[thermal_status]
      band_idx = list(THERMAL_BANDS.keys()).index(thermal_status)
      if current_band.min_temp is not None and max_comp_temp < current_band.min_temp:
        thermal_status = list(THERMAL_BANDS.keys())[band_idx - 1]
      elif current_band.max_temp is not None and max_comp_temp > current_band.max_temp:
        thermal_status = list(THERMAL_BANDS.keys())[band_idx + 1]

    if not dp_temp_monitor and thermal_status in [ThermalStatus.red, ThermalStatus.danger]:
      thermal_status = ThermalStatus.yellow
    # **** starting logic ****

    # Check for last update time and display alerts if needed
    # now = datetime.datetime.utcnow()
    #
    # # show invalid date/time alert
    # startup_conditions["time_valid"] = (now.year > 2020) or (now.year == 2020 and now.month >= 10)
    # set_offroad_alert_if_changed("Offroad_InvalidTime", (not startup_conditions["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 tested_branch:
    #     extra_text = "Ensure the software is correctly installed"
    #   else:
    #     extra_text = last_update_exception
    #
    #   set_offroad_alert_if_changed("Offroad_ConnectivityNeeded", False)
    #   set_offroad_alert_if_changed("Offroad_ConnectivityNeededPrompt", False)
    #   set_offroad_alert_if_changed("Offroad_UpdateFailed", True, extra_text=extra_text)
    # elif dt.days > DAYS_NO_CONNECTIVITY_MAX and update_failed_count > 1:
    #   set_offroad_alert_if_changed("Offroad_UpdateFailed", False)
    #   set_offroad_alert_if_changed("Offroad_ConnectivityNeededPrompt", False)
    #   set_offroad_alert_if_changed("Offroad_ConnectivityNeeded", True)
    # elif dt.days > DAYS_NO_CONNECTIVITY_PROMPT:
    #   remaining_time = str(max(DAYS_NO_CONNECTIVITY_MAX - dt.days, 0))
    #   set_offroad_alert_if_changed("Offroad_UpdateFailed", False)
    #   set_offroad_alert_if_changed("Offroad_ConnectivityNeeded", False)
    #   set_offroad_alert_if_changed("Offroad_ConnectivityNeededPrompt", True, extra_text=f"{remaining_time} days.")
    # else:
    #   set_offroad_alert_if_changed("Offroad_UpdateFailed", False)
    #   set_offroad_alert_if_changed("Offroad_ConnectivityNeeded", False)
    #   set_offroad_alert_if_changed("Offroad_ConnectivityNeededPrompt", False)

    startup_conditions["up_to_date"] = params.get("Offroad_ConnectivityNeeded") is None or params.get_bool("DisableUpdates")
    startup_conditions["not_uninstalling"] = not params.get_bool("DoUninstall")
    startup_conditions["accepted_terms"] = params.get("HasAcceptedTerms") == terms_version

    panda_signature = params.get("PandaFirmware")
    startup_conditions["fw_version_match"] = (panda_signature is None) or (panda_signature == FW_SIGNATURE)   # don't show alert is no panda is connected (None)
    set_offroad_alert_if_changed("Offroad_PandaFirmwareMismatch", (not startup_conditions["fw_version_match"]))

    # with 2% left, we killall, otherwise the phone will take a long time to boot
    startup_conditions["free_space"] = msg.deviceState.freeSpacePercent > 2
    startup_conditions["completed_training"] = params.get("CompletedTrainingVersion") == training_version or \
                                               params.get_bool("Passive")
    startup_conditions["not_driver_view"] = not params.get_bool("IsDriverViewEnabled")
    startup_conditions["not_taking_snapshot"] = not params.get_bool("IsTakingSnapshot")
    # 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
    startup_conditions["device_temp_good"] = thermal_status < ThermalStatus.danger
    set_offroad_alert_if_changed("Offroad_TemperatureTooHigh", (not startup_conditions["device_temp_good"]))

    if TICI:
      set_offroad_alert_if_changed("Offroad_NvmeMissing", (not Path("/data/media").is_mount()))

    # Handle offroad/onroad transition
    should_start = all(startup_conditions.values())
    # dp - check usb_present to fix not going offroad on "EON/LEON + battery - Comma Power"
    if dp_no_offroad_fix:
      should_start = should_start and HARDWARE.get_usb_present()
    if should_start != should_start_prev or (count == 0):
      params.put_bool("IsOnroad", should_start)
      params.put_bool("IsOffroad", not should_start)
      HARDWARE.set_power_save(not should_start)

    if should_start:
      off_ts = None
      if started_ts is None:
        started_ts = sec_since_boot()
        started_seen = True
    else:
      if startup_conditions["ignition"] and (startup_conditions != startup_conditions_prev):
        cloudlog.event("Startup blocked", startup_conditions=startup_conditions)

      started_ts = None
      if off_ts is None:
        off_ts = sec_since_boot()

    # Offroad power monitoring
    if not dp_no_batt:
      power_monitor.calculate(peripheralState, startup_conditions["ignition"])
      msg.deviceState.offroadPowerUsageUwh = power_monitor.get_power_used()
      msg.deviceState.carBatteryCapacityUwh = max(0, power_monitor.get_car_battery_capacity())

    # Check if we need to disable charging (handled by boardd)
    msg.deviceState.chargingDisabled = power_monitor.should_disable_charging(startup_conditions["ignition"], in_car, off_ts, dp_auto_shutdown, dp_auto_shutdown_in)

    # dp - for battery powered device
    # when peripheralState is not changing (panda offline), and usb is not present (not charging)
    if dp_no_offroad_fix and (peripheralStateLast == peripheralState) and not msg.deviceState.usbOnline:
      if (sec_since_boot() - off_ts) > dp_auto_shutdown_in * 60:
        time.sleep(10)
        HARDWARE.shutdown()
    peripheralStateLast = peripheralState

    # Check if we need to shut down
    if power_monitor.should_shutdown(peripheralState, startup_conditions["ignition"], in_car, off_ts, started_seen, LEON, dp_auto_shutdown, dp_auto_shutdown_in):
      cloudlog.info(f"shutting device down, offroad since {off_ts}")
      # TODO: add function for blocking cloudlog instead of sleep
      time.sleep(10)
      HARDWARE.shutdown()

    # If UI has crashed, set the brightness to reasonable non-zero value
    ui_running = "ui" in (p.name for p in sm["managerState"].processes if p.running)
    if ui_running_prev and not ui_running:
      HARDWARE.set_screen_brightness(20)
    ui_running_prev = ui_running

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

    last_ping = params.get("LastAthenaPingTime")
    if last_ping is not None:
      msg.deviceState.lastAthenaPingTime = int(last_ping)

    msg.deviceState.thermalStatus = thermal_status
    pm.send("deviceState", msg)

    if EON and not is_uno:
      set_offroad_alert_if_changed("Offroad_ChargeDisabled", (not usb_power))

    should_start_prev = should_start
    startup_conditions_prev = startup_conditions.copy()

    # report to server once every 10 minutes
    # if (count % int(600. / DT_TRML)) == 0:
    #   if EON and started_ts is None and msg.deviceState.memoryUsagePercent > 40:
    #     cloudlog.event("High offroad memory usage", mem=msg.deviceState.memoryUsagePercent)
    #
    #   cloudlog.event("STATUS_PACKET",
    #                  count=count,
    #                  pandaStates=(strip_deprecated_keys(pandaStates.to_dict()) if pandaStates else None),
    #                  peripheralState=strip_deprecated_keys(peripheralState.to_dict()),
    #                  location=(strip_deprecated_keys(sm["gpsLocationExternal"].to_dict()) if sm.alive["gpsLocationExternal"] else None),
    #                  deviceState=strip_deprecated_keys(msg.to_dict()))

    count += 1
Exemple #6
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')

    fan_speed = 0
    count = 0

    startup_conditions = {
        "ignition": False,
    }
    startup_conditions_prev = startup_conditions.copy()

    off_ts = None
    started_ts = None
    started_seen = False
    thermal_status = ThermalStatus.green
    usb_power = 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
    should_start_prev = False
    handle_fan = None
    is_uno = False
    has_relay = False

    pm = PowerMonitoring()
    no_panda_cnt = 0

    thermal_config = get_thermal_config()

    # dp
    dp_temp_monitor = True
    dp_last_modified_temp_monitor = None
    dp_last_modified_auto_shutdown = None
    dp_last_modified_auto_shutdown_in = None
    dp_auto_shutdown = False
    dp_auto_shutdown_in = 90
    dp_allow_shutdown = True
    dp_allow_shutdown_last = True
    modified = None
    last_modified = None
    last_modified_check = None

    while 1:
        # dp - load temp monitor conf
        last_modified_check, modified = get_last_modified(
            LAST_MODIFIED_THERMALD, last_modified_check, modified)
        if last_modified != modified:
            dp_temp_monitor, dp_last_modified_temp_monitor = param_get_if_updated(
                "dp_temp_monitor", "bool", dp_temp_monitor,
                dp_last_modified_temp_monitor)
            dp_auto_shutdown, dp_last_modified_auto_shutdown = param_get_if_updated(
                "dp_auto_shutdown", "bool", dp_auto_shutdown,
                dp_last_modified_auto_shutdown)
            dp_auto_shutdown_in, dp_last_modified_auto_shutdown_in = param_get_if_updated(
                "dp_auto_shutdown_in", "int", dp_auto_shutdown_in,
                dp_last_modified_auto_shutdown_in)
            last_modified = modified

        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 startup_conditions["ignition"]:
                        cloudlog.error("Lost panda connection while onroad")
                    startup_conditions["ignition"] = False
            else:
                no_panda_cnt = 0
                startup_conditions[
                    "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 = HARDWARE.get_network_type()
                network_strength = HARDWARE.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 = HARDWARE.get_battery_capacity()
        msg.thermal.batteryStatus = HARDWARE.get_battery_status()
        msg.thermal.batteryCurrent = HARDWARE.get_battery_current()
        msg.thermal.batteryVoltage = HARDWARE.get_battery_voltage()
        msg.thermal.usbOnline = HARDWARE.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,
                                   startup_conditions["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
        is_offroad_for_5_min = (started_ts is None) and (
            (not started_seen) or (off_ts is None) or
            (sec_since_boot() - off_ts > 60 * 5))
        if max_cpu_temp > 107. or bat_temp >= 63. or (has_relay
                                                      and is_offroad_for_5_min
                                                      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

        if not dp_temp_monitor and thermal_status in [
                ThermalStatus.yellow, ThermalStatus.red, ThermalStatus.danger
        ]:
            thermal_status = ThermalStatus.yellow
        # **** starting logic ****

        # Check for last update time and display alerts if needed
        # now = datetime.datetime.utcnow()
        #
        # # show invalid date/time alert
        # startup_conditions["time_valid"] = (now.year > 2020) or (now.year == 2020 and now.month >= 10)
        # set_offroad_alert_if_changed("Offroad_InvalidTime", (not startup_conditions["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
        #
        #   set_offroad_alert_if_changed("Offroad_ConnectivityNeeded", False)
        #   set_offroad_alert_if_changed("Offroad_ConnectivityNeededPrompt", False)
        #   set_offroad_alert_if_changed("Offroad_UpdateFailed", True, extra_text=extra_text)
        # elif dt.days > DAYS_NO_CONNECTIVITY_MAX and update_failed_count > 1:
        #   set_offroad_alert_if_changed("Offroad_UpdateFailed", False)
        #   set_offroad_alert_if_changed("Offroad_ConnectivityNeededPrompt", False)
        #   set_offroad_alert_if_changed("Offroad_ConnectivityNeeded", True)
        # elif dt.days > DAYS_NO_CONNECTIVITY_PROMPT:
        #   remaining_time = str(max(DAYS_NO_CONNECTIVITY_MAX - dt.days, 0))
        #   set_offroad_alert_if_changed("Offroad_UpdateFailed", False)
        #   set_offroad_alert_if_changed("Offroad_ConnectivityNeeded", False)
        #   set_offroad_alert_if_changed("Offroad_ConnectivityNeededPrompt", True, extra_text=f"{remaining_time} days.")
        # else:
        #   set_offroad_alert_if_changed("Offroad_UpdateFailed", False)
        #   set_offroad_alert_if_changed("Offroad_ConnectivityNeeded", False)
        #   set_offroad_alert_if_changed("Offroad_ConnectivityNeededPrompt", False)

        startup_conditions["not_uninstalling"] = not params.get(
            "DoUninstall") == b"1"
        startup_conditions["accepted_terms"] = params.get(
            "HasAcceptedTerms") == terms_version

        panda_signature = params.get("PandaFirmware")
        startup_conditions["fw_version_match"] = (panda_signature is None) or (
            panda_signature == FW_SIGNATURE
        )  # don't show alert is no panda is connected (None)
        set_offroad_alert_if_changed(
            "Offroad_PandaFirmwareMismatch",
            (not startup_conditions["fw_version_match"]))

        # with 2% left, we killall, otherwise the phone will take a long time to boot
        startup_conditions["free_space"] = msg.thermal.freeSpace > 0.02
        startup_conditions["completed_training"] = params.get("CompletedTrainingVersion") == training_version or \
                                                   (current_branch in ['dashcam', 'dashcam-staging'])
        startup_conditions["not_driver_view"] = not params.get(
            "IsDriverViewEnabled") == b"1"
        startup_conditions["not_taking_snapshot"] = not params.get(
            "IsTakingSnapshot") == b"1"
        # 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
        startup_conditions[
            "device_temp_good"] = thermal_status < ThermalStatus.danger
        set_offroad_alert_if_changed(
            "Offroad_TemperatureTooHigh",
            (not startup_conditions["device_temp_good"]))
        should_start = all(startup_conditions.values())

        startup_conditions["hardware_supported"] = True
        set_offroad_alert_if_changed(
            "Offroad_HardwareUnsupported", health is not None
            and not startup_conditions["hardware_supported"])

        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
        else:
            if startup_conditions["ignition"] and (startup_conditions !=
                                                   startup_conditions_prev):
                cloudlog.event("Startup blocked",
                               startup_conditions=startup_conditions)
            if should_start_prev or (count == 0):
                params.put("IsOffroad", "1")

            started_ts = None
            if off_ts is None:
                off_ts = sec_since_boot()

        # 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')

        # dp
        if health is None and msg.thermal.usbOnline:
            # happen when boot up without panda connected
            dp_allow_shutdown = False
        elif health is not None and msg.thermal.usbOnline and health.health.hwType == log.HealthData.HwType.unknown:
            # happen when panda was there and gone
            dp_allow_shutdown = False
        else:
            dp_allow_shutdown = True

        # if allow shutdown status change, we reset off_ts
        if dp_allow_shutdown != dp_allow_shutdown_last and off_ts is not None:
            off_ts = sec_since_boot()
            dp_allow_shutdown_last = dp_allow_shutdown

        if dp_allow_shutdown and off_ts is not None and dp_auto_shutdown and sec_since_boot(
        ) - off_ts >= dp_auto_shutdown_in * 60:
            msg.thermal.chargingDisabled = True
            shutdown = False
            if health is not None:
                if health.health.usbPowerMode in [
                        log.HealthData.UsbPowerMode.client,
                        log.HealthData.UsbPowerMode.none
                ]:
                    shutdown = True
            else:
                shutdown = True
            if shutdown:
                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())

        set_offroad_alert_if_changed("Offroad_ChargeDisabled", (not usb_power))

        should_start_prev = should_start
        startup_conditions_prev = startup_conditions.copy()

        # 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
Exemple #7
0
def thermald_thread():

    pm = messaging.PubMaster(['deviceState'])

    pandaState_timeout = int(1000 * 2.5 *
                             DT_TRML)  # 2.5x the expected pandaState frequency
    pandaState_sock = messaging.sub_sock('pandaState',
                                         timeout=pandaState_timeout)
    location_sock = messaging.sub_sock('gpsLocationExternal')
    managerState_sock = messaging.sub_sock('managerState', conflate=True)

    fan_speed = 0
    count = 0

    startup_conditions = {
        "ignition": False,
    }
    startup_conditions_prev = startup_conditions.copy()

    off_ts = None
    started_ts = None
    started_seen = False
    thermal_status = ThermalStatus.green
    usb_power = True

    network_type = NetworkType.none
    network_strength = NetworkStrength.unknown
    network_info = None
    modem_version = None
    registered_count = 0

    current_filter = FirstOrderFilter(0., CURRENT_TAU, DT_TRML)
    cpu_temp_filter = FirstOrderFilter(0., CPU_TEMP_TAU, DT_TRML)
    pandaState_prev = None
    should_start_prev = False
    handle_fan = None
    is_uno = False
    ui_running_prev = False

    power_monitor = PowerMonitoring()
    no_panda_cnt = 0

    HARDWARE.initialize_hardware()
    thermal_config = HARDWARE.get_thermal_config()

    if params.get_bool("IsOnroad"):
        cloudlog.event("onroad flag not cleared")

    # dp
    dp_no_batt = params.get_bool("dp_no_batt")
    dp_temp_monitor = True
    dp_last_modified_temp_monitor = None

    dp_auto_shutdown = False
    dp_last_modified_auto_shutdown = None
    dp_auto_shutdown_last = False

    dp_auto_shutdown_in = 90
    dp_last_modified_auto_shutdown_in = None
    dp_auto_shutdown_in_last = 90

    dp_fan_mode = 0
    dp_fan_mode_last = None

    modified = None
    last_modified = None
    last_modified_check = None

    #dp
    prebuilt_file = '/data/openpilot/prebuilt'
    dp_prebuilt = params.get_bool("dp_prebuilt")
    if not os.path.isfile(prebuilt_file) and dp_prebuilt:
        os.system("touch /data/openpilot/prebuilt")
    elif os.path.isfile(prebuilt_file) and not dp_prebuilt:
        os.system("rm -fr /data/openpilot/prebuilt")

    # CPR3 logging
    if EON:
        base_path = "/sys/kernel/debug/cpr3-regulator/"
        cpr_files = [p for p in Path(base_path).glob("**/*") if p.is_file()]
        cpr_files = ["/sys/kernel/debug/regulator/pm8994_s11/voltage"
                     ] + cpr_files
        cpr_data = {}
        for cf in cpr_files:
            with open(cf, "r") as f:
                try:
                    cpr_data[str(cf)] = f.read().strip()
                except Exception:
                    pass
        cloudlog.event("CPR", data=cpr_data)

    # dp - light up = not started
    if JETSON:
        HARDWARE.led(True)

    while 1:

        # dp - load temp monitor conf
        last_modified_check, modified = get_last_modified(
            LAST_MODIFIED_THERMALD, last_modified_check, modified)
        if last_modified != modified:
            dp_temp_monitor, dp_last_modified_temp_monitor = param_get_if_updated(
                "dp_temp_monitor", "bool", dp_temp_monitor,
                dp_last_modified_temp_monitor)
            dp_auto_shutdown, dp_last_modified_auto_shutdown = param_get_if_updated(
                "dp_auto_shutdown", "bool", dp_auto_shutdown,
                dp_last_modified_auto_shutdown)
            dp_auto_shutdown_in, dp_last_modified_auto_shutdown_in = param_get_if_updated(
                "dp_auto_shutdown_in", "int", dp_auto_shutdown_in,
                dp_last_modified_auto_shutdown_in)
            dp_fan_mode, dp_fan_mode_last = param_get_if_updated(
                "dp_fan_mode", "int", dp_fan_mode, dp_fan_mode_last)
            last_modified = modified

        pandaState = messaging.recv_sock(pandaState_sock, wait=True)
        msg = read_thermal(thermal_config)

        if pandaState is not None:
            usb_power = pandaState.pandaState.usbPowerMode != log.PandaState.UsbPowerMode.client

            # If we lose connection to the panda, wait 5 seconds before going offroad
            if pandaState.pandaState.pandaType == log.PandaState.PandaType.unknown:
                no_panda_cnt += 1
                if no_panda_cnt > DISCONNECT_TIMEOUT / DT_TRML:
                    if startup_conditions["ignition"]:
                        cloudlog.error("Lost panda connection while onroad")
                    startup_conditions["ignition"] = False
            else:
                no_panda_cnt = 0
                startup_conditions[
                    "ignition"] = pandaState.pandaState.ignitionLine or pandaState.pandaState.ignitionCan

            startup_conditions[
                "hardware_supported"] = pandaState.pandaState.pandaType not in [
                    log.PandaState.PandaType.whitePanda,
                    log.PandaState.PandaType.greyPanda
                ]
            set_offroad_alert_if_changed(
                "Offroad_HardwareUnsupported",
                not startup_conditions["hardware_supported"])

            # Setup fan handler on first connect to panda
            if not JETSON and handle_fan is None and pandaState.pandaState.pandaType != log.PandaState.PandaType.unknown:
                is_uno = pandaState.pandaState.pandaType == log.PandaState.PandaType.uno

                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 pandaState_prev is not None:
                if pandaState.pandaState.pandaType == log.PandaState.PandaType.unknown and \
                  pandaState_prev.pandaState.pandaType != log.PandaState.PandaType.unknown:
                    params.clear_all(ParamKeyType.CLEAR_ON_PANDA_DISCONNECT)
            pandaState_prev = pandaState

        # get_network_type is an expensive call. update every 10s
        if (count % int(10. / DT_TRML)) == 0:
            try:
                network_type = HARDWARE.get_network_type()
                network_strength = HARDWARE.get_network_strength(network_type)
                network_info = HARDWARE.get_network_info()  # pylint: disable=assignment-from-none

                # Log modem version once
                if modem_version is None:
                    modem_version = HARDWARE.get_modem_version()  # pylint: disable=assignment-from-none
                    if modem_version is not None:
                        cloudlog.warning(f"Modem version: {modem_version}")

                if TICI and (network_info.get('state', None) == "REGISTERED"):
                    registered_count += 1
                else:
                    registered_count = 0

                if registered_count > 10:
                    cloudlog.warning(
                        f"Modem stuck in registered state {network_info}. nmcli conn up lte"
                    )
                    os.system("nmcli conn up lte")
                    registered_count = 0

            except Exception:
                cloudlog.exception("Error getting network status")

        msg.deviceState.freeSpacePercent = get_available_percent(default=100.0)
        msg.deviceState.memoryUsagePercent = int(
            round(psutil.virtual_memory().percent))
        msg.deviceState.cpuUsagePercent = int(round(psutil.cpu_percent()))
        msg.deviceState.gpuUsagePercent = int(
            round(HARDWARE.get_gpu_usage_percent()))
        msg.deviceState.networkType = network_type
        msg.deviceState.networkStrength = network_strength
        if network_info is not None:
            msg.deviceState.networkInfo = network_info

        msg.deviceState.batteryPercent = HARDWARE.get_battery_capacity()
        msg.deviceState.batteryStatus = HARDWARE.get_battery_status()
        msg.deviceState.batteryCurrent = HARDWARE.get_battery_current()
        msg.deviceState.batteryVoltage = HARDWARE.get_battery_voltage()
        msg.deviceState.usbOnline = HARDWARE.get_usb_present()

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

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

        # TODO: add car battery voltage check
        max_cpu_temp = cpu_temp_filter.update(max(msg.deviceState.cpuTempC))
        max_comp_temp = max(max_cpu_temp, msg.deviceState.memoryTempC,
                            max(msg.deviceState.gpuTempC))
        bat_temp = msg.deviceState.batteryTempC

        if handle_fan is not None:
            fan_speed = handle_fan(dp_fan_mode, max_cpu_temp, bat_temp,
                                   fan_speed, startup_conditions["ignition"])
            msg.deviceState.fanSpeedPercentDesired = 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
        is_offroad_for_5_min = (started_ts is None) and (
            (not started_seen) or (off_ts is None) or
            (sec_since_boot() - off_ts > 60 * 5))
        if max_cpu_temp > 107. or bat_temp >= 63. or (is_offroad_for_5_min
                                                      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:
            thermal_status = ThermalStatus.green  # default to good condition

        if not dp_temp_monitor and thermal_status in [
                ThermalStatus.red, ThermalStatus.danger
        ]:
            thermal_status = ThermalStatus.yellow
        # **** starting logic ****

        # Check for last update time and display alerts if needed
        # now = datetime.datetime.utcnow()
        #
        # # show invalid date/time alert
        # startup_conditions["time_valid"] = (now.year > 2020) or (now.year == 2020 and now.month >= 10)
        # set_offroad_alert_if_changed("Offroad_InvalidTime", (not startup_conditions["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 tested_branch:
        #     extra_text = "Ensure the software is correctly installed"
        #   else:
        #     extra_text = last_update_exception
        #
        #   set_offroad_alert_if_changed("Offroad_ConnectivityNeeded", False)
        #   set_offroad_alert_if_changed("Offroad_ConnectivityNeededPrompt", False)
        #   set_offroad_alert_if_changed("Offroad_UpdateFailed", True, extra_text=extra_text)
        # elif dt.days > DAYS_NO_CONNECTIVITY_MAX and update_failed_count > 1:
        #   set_offroad_alert_if_changed("Offroad_UpdateFailed", False)
        #   set_offroad_alert_if_changed("Offroad_ConnectivityNeededPrompt", False)
        #   set_offroad_alert_if_changed("Offroad_ConnectivityNeeded", True)
        # elif dt.days > DAYS_NO_CONNECTIVITY_PROMPT:
        #   remaining_time = str(max(DAYS_NO_CONNECTIVITY_MAX - dt.days, 0))
        #   set_offroad_alert_if_changed("Offroad_UpdateFailed", False)
        #   set_offroad_alert_if_changed("Offroad_ConnectivityNeeded", False)
        #   set_offroad_alert_if_changed("Offroad_ConnectivityNeededPrompt", True, extra_text=f"{remaining_time} days.")
        # else:
        #   set_offroad_alert_if_changed("Offroad_UpdateFailed", False)
        #   set_offroad_alert_if_changed("Offroad_ConnectivityNeeded", False)
        #   set_offroad_alert_if_changed("Offroad_ConnectivityNeededPrompt", False)

        startup_conditions["up_to_date"] = params.get(
            "Offroad_ConnectivityNeeded") is None or params.get_bool(
                "DisableUpdates")
        startup_conditions["not_uninstalling"] = not params.get_bool(
            "DoUninstall")
        startup_conditions["accepted_terms"] = params.get(
            "HasAcceptedTerms") == terms_version

        panda_signature = params.get("PandaFirmware")
        startup_conditions["fw_version_match"] = (panda_signature is None) or (
            panda_signature == FW_SIGNATURE
        )  # don't show alert is no panda is connected (None)
        set_offroad_alert_if_changed(
            "Offroad_PandaFirmwareMismatch",
            (not startup_conditions["fw_version_match"]))

        # with 2% left, we killall, otherwise the phone will take a long time to boot
        startup_conditions["free_space"] = msg.deviceState.freeSpacePercent > 2
        startup_conditions["completed_training"] = params.get("CompletedTrainingVersion") == training_version or \
                                                   params.get_bool("Passive")
        startup_conditions["not_driver_view"] = not params.get_bool(
            "IsDriverViewEnabled")
        startup_conditions["not_taking_snapshot"] = not params.get_bool(
            "IsTakingSnapshot")
        # 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
        startup_conditions[
            "device_temp_good"] = thermal_status < ThermalStatus.danger
        set_offroad_alert_if_changed(
            "Offroad_TemperatureTooHigh",
            (not startup_conditions["device_temp_good"]))

        if TICI:
            set_offroad_alert_if_changed("Offroad_NvmeMissing",
                                         (not Path("/data/media").is_mount()))

        # Handle offroad/onroad transition
        should_start = all(startup_conditions.values())
        if should_start != should_start_prev or (count == 0):
            params.put_bool("IsOnroad", should_start)
            params.put_bool("IsOffroad", not should_start)
            HARDWARE.set_power_save(not should_start)

        if should_start:
            off_ts = None
            if started_ts is None:
                started_ts = sec_since_boot()
                started_seen = True
        else:
            if startup_conditions["ignition"] and (startup_conditions !=
                                                   startup_conditions_prev):
                cloudlog.event("Startup blocked",
                               startup_conditions=startup_conditions)

            started_ts = None
            if off_ts is None:
                off_ts = sec_since_boot()

        # dp - show led when off and vice versa
        if should_start != should_start_prev:
            HARDWARE.led(False if should_start else True)

        # Offroad power monitoring
        if not dp_no_batt:
            power_monitor.calculate(pandaState)
            msg.deviceState.offroadPowerUsageUwh = power_monitor.get_power_used(
            )
            msg.deviceState.carBatteryCapacityUwh = max(
                0, power_monitor.get_car_battery_capacity())

        # Check if we need to disable charging (handled by boardd)
        msg.deviceState.chargingDisabled = power_monitor.should_disable_charging(
            pandaState, off_ts)

        # Check if we need to shut down
        if power_monitor.should_shutdown(pandaState, 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)
            HARDWARE.shutdown()

        # dp - auto shutdown
        # reset off_ts if we change auto shutdown related params
        if off_ts is not None:
            if dp_auto_shutdown:
                shutdown_sec = dp_auto_shutdown_in * 60
                sec_now = sec_since_boot() - off_ts
                if (shutdown_sec - 5) < sec_now:
                    msg.deviceState.chargingDisabled = True
                if shutdown_sec < sec_now:
                    time.sleep(10)
                    HARDWARE.shutdown()

            if dp_auto_shutdown_in_last != dp_auto_shutdown_in or dp_auto_shutdown_last != dp_auto_shutdown:
                off_ts = sec_since_boot()
            dp_auto_shutdown_last = dp_auto_shutdown
            dp_auto_shutdown_in_last = dp_auto_shutdown_in

        # If UI has crashed, set the brightness to reasonable non-zero value
        manager_state = messaging.recv_one_or_none(managerState_sock)
        if manager_state is not None:
            ui_running = "ui" in (p.name
                                  for p in manager_state.managerState.processes
                                  if p.running)
            if ui_running_prev and not ui_running:
                HARDWARE.set_screen_brightness(20)
            ui_running_prev = ui_running

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

        last_ping = params.get("LastAthenaPingTime")
        if last_ping is not None:
            msg.deviceState.lastAthenaPingTime = int(last_ping)

        msg.deviceState.thermalStatus = thermal_status
        pm.send("deviceState", msg)

        if EON and not is_uno:
            set_offroad_alert_if_changed("Offroad_ChargeDisabled",
                                         (not usb_power))

        should_start_prev = should_start
        startup_conditions_prev = startup_conditions.copy()

        # report to server once every 10 minutes
        # if (count % int(600. / DT_TRML)) == 0:
        #   if EON and started_ts is None and msg.deviceState.memoryUsagePercent > 40:
        #     cloudlog.event("High offroad memory usage", mem=msg.deviceState.memoryUsagePercent)
        #
        #   location = messaging.recv_sock(location_sock)
        #   cloudlog.event("STATUS_PACKET",
        #                  count=count,
        #                  pandaState=(strip_deprecated_keys(pandaState.to_dict()) if pandaState else None),
        #                  location=(strip_deprecated_keys(location.gpsLocationExternal.to_dict()) if location else None),
        #                  deviceState=strip_deprecated_keys(msg.to_dict()))

        count += 1
def confd_thread():
  sm = messaging.SubMaster(['deviceState'])
  pm = messaging.PubMaster(['dragonConf'])

  last_dp_msg = None
  frame = 0
  update_params = False
  modified = None
  last_modified = None
  last_modified_check = None
  started = False
  free_space = 1
  last_started = False
  dashcamd = Dashcamd()
  is_eon = EON
  rk = Ratekeeper(HERTZ, print_delay_threshold=None)  # Keeps rate at 2 hz
  uploader_thread = None

  while True:
    if uploader_thread is None:
      uploader_thread = threading.Thread(target=gpx_uploader_thread)
      uploader_thread.start()

    msg = messaging.new_message('dragonConf')
    if last_dp_msg is not None:
      msg.dragonConf = last_dp_msg

    '''
    ===================================================
    load thermalState data every 3 seconds
    ===================================================
    '''
    if frame % (HERTZ * 3) == 0:
      sm.update(0)
      if sm.updated['deviceState']:
        started = sm['deviceState'].started
        free_space = sm['deviceState'].freeSpacePercent
    '''
    ===================================================
    hotspot on boot
    we do it after 30 secs just in case
    ===================================================
    '''
    if is_eon and frame == (HERTZ * 30) and param_get("dp_hotspot_on_boot", "bool", False):
      os.system("service call wifi 37 i32 0 i32 1 &")
    '''
    ===================================================
    check dp_last_modified every second
    ===================================================
    '''
    if not update_params:
      last_modified_check, modified = get_last_modified(LAST_MODIFIED_SYSTEMD, last_modified_check, modified)
      if last_modified != modified:
        update_params = True
        last_modified = modified
    '''
    ===================================================
    conditionally set update_params to true 
    ===================================================
    '''
    # force updating param when `started` changed
    if last_started != started:
      update_params = True

    if frame == 0:
      update_params = True
    '''
    ===================================================
    push param vals to message
    ===================================================
    '''
    if update_params:
      msg = update_conf_all(confs, msg, frame == 0)
      update_params = False
    '''
    ===================================================
    push once
    ===================================================
    '''
    if frame == 0:
      setattr(msg.dragonConf, get_struct_name('dp_locale'), params.get("dp_locale"))
      # mirror EndToEndToggle to dp_lane_less_model_ctrl first time, after all
      put_nonblocking('dp_lane_less_mode_ctrl', params.get('EndToEndToggle'))
    '''
    ===================================================
    push ip addr every 10 secs
    ===================================================
    '''
    if frame % (HERTZ * 10) == 0:
      msg = update_ip(msg)
    '''
    ===================================================
    update msg based on some custom logic
    ===================================================
    '''
    msg = update_custom_logic(msg)
    '''
    ===================================================
    battery ctrl every 30 secs
    PowerMonitor in thermald turns back on every mins
    so lets turn it off more frequent
    ===================================================
    '''
    # if frame % (HERTZ * 30) == 0:
    #   last_charging_ctrl = process_charging_ctrl(msg, last_charging_ctrl, battery_percent)
    '''
    ===================================================
    dashcam
    ===================================================
    '''
    if msg.dragonConf.dpDashcamd and frame % HERTZ == 0:
      dashcamd.run(started, free_space)
    # '''
    # ===================================================
    # appd
    # ===================================================
    # '''
    # if msg.dragonConf.dpAppd:
    #   appd.update(started)
    '''
    ===================================================
    finalise
    ===================================================
    '''
    last_dp_msg = msg.dragonConf
    last_started = started
    pm.send('dragonConf', msg)
    frame += 1
    rk.keep_time()