Exemple #1
0
def main() -> NoReturn:
  first_run = True
  params = Params()

  while True:
    try:
      params.delete("PandaSignatures")

      # Flash all Pandas in DFU mode
      for p in PandaDFU.list():
        cloudlog.info(f"Panda in DFU mode found, flashing recovery {p}")
        PandaDFU(p).recover()
      time.sleep(1)

      panda_serials = Panda.list()
      if len(panda_serials) == 0:
        continue

      cloudlog.info(f"{len(panda_serials)} panda(s) found, connecting - {panda_serials}")

      # Flash pandas
      pandas = []
      for serial in panda_serials:
        pandas.append(flash_panda(serial))

      # check health for lost heartbeat
      for panda in pandas:
        health = panda.health()
        if health["heartbeat_lost"]:
          params.put_bool("PandaHeartbeatLost", True)
          cloudlog.event("heartbeat lost", deviceState=health, serial=panda.get_usb_serial())

        #if first_run:
        #  cloudlog.info(f"Resetting panda {panda.get_usb_serial()}")
        #  panda.reset()

      # sort pandas to have deterministic order
      pandas.sort(key=cmp_to_key(panda_sort_cmp))
      panda_serials = list(map(lambda p: p.get_usb_serial(), pandas))

      # log panda fw versions
      params.put("PandaSignatures", b','.join(p.get_signature() for p in pandas))

      # close all pandas
      for p in pandas:
        p.close()
    except (usb1.USBErrorNoDevice, usb1.USBErrorPipe):
      # a panda was disconnected while setting everything up. let's try again
      cloudlog.exception("Panda USB exception while setting up")
      continue

    first_run = False

    # run boardd with all connected serials as arguments
    os.chdir(os.path.join(BASEDIR, "selfdrive/boardd"))
    subprocess.run(["./boardd", *panda_serials], check=True)
Exemple #2
0
def set_params(new_version: bool, failed_count: int,
               exception: Optional[str]) -> None:
    params = Params()

    params.put("UpdateFailedCount", str(failed_count))

    last_update = datetime.datetime.utcnow()
    if failed_count == 0:
        t = last_update.isoformat()
        params.put("LastUpdateTime", t.encode('utf8'))
    else:
        try:
            t = params.get("LastUpdateTime", encoding='utf8')
            last_update = datetime.datetime.fromisoformat(t)
        except (TypeError, ValueError):
            pass

    if exception is None:
        params.delete("LastUpdateException")
    else:
        params.put("LastUpdateException", exception)

    # Write out release notes for new versions
    if new_version:
        try:
            with open(os.path.join(FINALIZED, "RELEASES.md"), "rb") as f:
                r = f.read().split(b'\n\n', 1)[0]  # Slice latest release notes
            try:
                params.put("ReleaseNotes", parse_markdown(r.decode("utf-8")))
            except Exception:
                params.put("ReleaseNotes", r + b"\n")
        except Exception:
            params.put("ReleaseNotes", "")
        params.put_bool("UpdateAvailable", True)

    # Handle user prompt
    for alert in ("Offroad_UpdateFailed", "Offroad_ConnectivityNeeded",
                  "Offroad_ConnectivityNeededPrompt"):
        set_offroad_alert(alert, False)

    now = datetime.datetime.utcnow()
    dt = now - last_update
    if failed_count > 15 and exception is not None:
        if is_tested_branch():
            extra_text = "Ensure the software is correctly installed"
        else:
            extra_text = exception
        set_offroad_alert("Offroad_UpdateFailed", True, extra_text=extra_text)
    elif dt.days > DAYS_NO_CONNECTIVITY_MAX and failed_count > 1:
        set_offroad_alert("Offroad_ConnectivityNeeded", True)
    elif dt.days > DAYS_NO_CONNECTIVITY_PROMPT:
        remaining = max(DAYS_NO_CONNECTIVITY_MAX - dt.days, 1)
        set_offroad_alert(
            "Offroad_ConnectivityNeededPrompt",
            True,
            extra_text=f"{remaining} day{'' if remaining == 1 else 's'}.")
Exemple #3
0
def snapshot():
    params = Params()
    front_camera_allowed = int(params.get("RecordFront"))

    params.put("IsTakingSnapshot", "1")
    params.put("Offroad_IsTakingSnapshot",
               json.dumps(OFFROAD_ALERTS["Offroad_IsTakingSnapshot"]))
    time.sleep(
        2.0
    )  # Give thermald time to read the param, or if just started give camerad time to start

    # Check if camerad is already started
    ps = subprocess.Popen("ps | grep camerad",
                          shell=True,
                          stdout=subprocess.PIPE)
    ret = list(
        filter(lambda x: 'grep ' not in x,
               ps.communicate()[0].decode('utf-8').strip().split("\n")))
    if len(ret) > 0:
        params.put("IsTakingSnapshot", "0")
        params.delete("Offroad_IsTakingSnapshot")
        return None

    proc = subprocess.Popen(os.path.join(BASEDIR, "selfdrive/camerad/camerad"),
                            cwd=os.path.join(BASEDIR, "selfdrive/camerad"))
    time.sleep(1.0)

    ret = None
    start_time = time.time()
    while time.time() - start_time < 5.0:
        try:
            ipc = VisionIPC()
            pic = ipc.get()
            del ipc

            if front_camera_allowed:
                ipc_front = VisionIPC(front=True)
                fpic = ipc_front.get()
                del ipc_front
            else:
                fpic = None

            ret = pic, fpic
            break
        except Exception:
            time.sleep(1)

    proc.send_signal(signal.SIGINT)
    proc.communicate()

    params.put("IsTakingSnapshot", "0")
    params.delete("Offroad_IsTakingSnapshot")
    return ret
Exemple #4
0
def manage_tokens(api):
  if not TICI:
    return

  try:
    params = Params()
    mapbox = api.get(f"/v1/tokens/mapbox/{api.dongle_id}/", timeout=5.0, access_token=api.get_token())
    if mapbox.status_code == 200:
      params.put("MapboxToken", mapbox.json()["token"])
    else:
      params.delete("MapboxToken")
  except Exception:
    cloudlog.exception("Failed to update tokens")
Exemple #5
0
def snapshot():
    params = Params()
    front_camera_allowed = int(params.get("RecordFront"))

    if params.get("IsOffroad") != b"1" or params.get(
            "IsTakingSnapshot") == b"1":
        print("Already taking snapshot")
        return None, None

    params.put("IsTakingSnapshot", "1")
    set_offroad_alert("Offroad_IsTakingSnapshot", True)
    time.sleep(
        2.0
    )  # Give thermald time to read the param, or if just started give camerad time to start

    # Check if camerad is already started
    try:
        subprocess.check_call(["pgrep", "camerad"])

        print("Camerad already running")
        params.put("IsTakingSnapshot", "0")
        params.delete("Offroad_IsTakingSnapshot")
        return None, None
    except subprocess.CalledProcessError:
        pass

    env = os.environ.copy()
    env["SEND_REAR"] = "1"
    env["SEND_WIDE"] = "1"
    env["SEND_FRONT"] = "1"
    proc = subprocess.Popen(os.path.join(BASEDIR, "selfdrive/camerad/camerad"),
                            cwd=os.path.join(BASEDIR, "selfdrive/camerad"),
                            env=env)
    time.sleep(3.0)

    frame = "wideRoadCameraState" if TICI else "roadCameraState"
    rear, front = get_snapshots(frame)

    proc.send_signal(signal.SIGINT)
    proc.communicate()

    params.put("IsTakingSnapshot", "0")
    set_offroad_alert("Offroad_IsTakingSnapshot", False)

    if not front_camera_allowed:
        front = None

    return rear, front
def main():
  params = Params()
  dongle_id = params.get("DongleId").decode('utf-8')
  cloudlog.bind_global(dongle_id=dongle_id, version=get_version(), dirty=is_dirty())

  try:
    while 1:
      cloudlog.info("starting athena daemon")
      proc = Process(name='athenad', target=launcher, args=('selfdrive.athena.athenad', 'athenad'))
      proc.start()
      proc.join()
      cloudlog.event("athenad exited", exitcode=proc.exitcode)
      time.sleep(5)
  except Exception:
    cloudlog.exception("manage_athenad.exception")
  finally:
    params.delete(ATHENA_MGR_PID_PARAM)
Exemple #7
0
def snapshot():
    params = Params()
    front_camera_allowed = params.get_bool("RecordFront")

    if (not params.get_bool("IsOffroad")
        ) or params.get_bool("IsTakingSnapshot"):
        print("Already taking snapshot")
        return None, None

    params.put_bool("IsTakingSnapshot", True)
    set_offroad_alert("Offroad_IsTakingSnapshot", True)
    time.sleep(
        2.0
    )  # Give thermald time to read the param, or if just started give camerad time to start

    # Check if camerad is already started
    try:
        subprocess.check_call(["pgrep", "camerad"])
        print("Camerad already running")
        params.put_bool("IsTakingSnapshot", False)
        params.delete("Offroad_IsTakingSnapshot")
        return None, None
    except subprocess.CalledProcessError:
        pass

    os.environ["SEND_ROAD"] = "1"
    os.environ["SEND_WIDE_ROAD"] = "1"
    if front_camera_allowed:
        os.environ["SEND_DRIVER"] = "1"

    try:
        managed_processes['camerad'].start()
        frame = "wideRoadCameraState" if TICI else "roadCameraState"
        front_frame = "driverCameraState" if front_camera_allowed else None
        focus_perc_threshold = 0. if TICI else 10 / 12.

        rear, front = get_snapshots(frame, front_frame, focus_perc_threshold)
    finally:
        managed_processes['camerad'].stop()
        params.put_bool("IsTakingSnapshot", False)
        set_offroad_alert("Offroad_IsTakingSnapshot", False)

    if not front_camera_allowed:
        front = None

    return rear, front
Exemple #8
0
def setup_env(simulation=False, CP=None, cfg=None, controlsState=None):
    params = Params()
    params.clear_all()
    params.put_bool("OpenpilotEnabledToggle", True)
    params.put_bool("Passive", False)
    params.put_bool("DisengageOnAccelerator", True)
    params.put_bool("WideCameraOnly", False)
    params.put_bool("DisableLogging", False)

    os.environ["NO_RADAR_SLEEP"] = "1"
    os.environ["REPLAY"] = "1"
    os.environ['SKIP_FW_QUERY'] = ""
    os.environ['FINGERPRINT'] = ""

    if cfg is not None:
        # Clear all custom processConfig environment variables
        for config in CONFIGS:
            for k, _ in config.environ.items():
                if k in os.environ:
                    del os.environ[k]

        os.environ.update(cfg.environ)
        os.environ['PROC_NAME'] = cfg.proc_name

    if simulation:
        os.environ["SIMULATION"] = "1"
    elif "SIMULATION" in os.environ:
        del os.environ["SIMULATION"]

    # Initialize controlsd with a controlsState packet
    if controlsState is not None:
        params.put("ReplayControlsState",
                   controlsState.as_builder().to_bytes())
    else:
        params.delete("ReplayControlsState")

    # Regen or python process
    if CP is not None:
        if CP.alternativeExperience == ALTERNATIVE_EXPERIENCE.DISABLE_DISENGAGE_ON_GAS:
            params.put_bool("DisengageOnAccelerator", False)

        if CP.fingerprintSource == "fw":
            params.put("CarParamsCache", CP.as_builder().to_bytes())
        else:
            os.environ['SKIP_FW_QUERY'] = "1"
            os.environ['FINGERPRINT'] = CP.carFingerprint
Exemple #9
0
def snapshot():
    params = Params()

    if (not params.get_bool("IsOffroad")
        ) or params.get_bool("IsTakingSnapshot"):
        print("Already taking snapshot")
        return None, None

    front_camera_allowed = params.get_bool("RecordFront")
    params.put_bool("IsTakingSnapshot", True)
    set_offroad_alert("Offroad_IsTakingSnapshot", True)
    time.sleep(
        2.0
    )  # Give thermald time to read the param, or if just started give camerad time to start

    # Check if camerad is already started
    try:
        subprocess.check_call(["pgrep", "camerad"])
        print("Camerad already running")
        params.put_bool("IsTakingSnapshot", False)
        params.delete("Offroad_IsTakingSnapshot")
        return None, None
    except subprocess.CalledProcessError:
        pass

    try:
        # Allow testing on replay on PC
        if not PC:
            managed_processes['camerad'].start()

        frame = "wideRoadCameraState"
        front_frame = "driverCameraState" if front_camera_allowed else None
        rear, front = get_snapshots(frame, front_frame)
    finally:
        managed_processes['camerad'].stop()
        params.put_bool("IsTakingSnapshot", False)
        set_offroad_alert("Offroad_IsTakingSnapshot", False)

    if not front_camera_allowed:
        front = None

    return rear, front
Exemple #10
0
def main():
    params = Params()
    dongle_id = params.get("DongleId", encoding='utf-8')
    crash.init()
    crash.bind_user(id=dongle_id)
    crash.bind_extra(dirty=dirty,
                     origin=origin,
                     branch=branch,
                     commit=commit,
                     device=HARDWARE.get_device_type())

    ws_uri = ATHENA_HOST + "/ws/v2/" + dongle_id

    api = Api(dongle_id)

    conn_retries = 0
    while 1:
        try:
            cloudlog.event("athenad.main.connecting_ws", ws_uri=ws_uri)
            ws = create_connection(ws_uri,
                                   cookie="jwt=" + api.get_token(),
                                   enable_multithread=True,
                                   timeout=1.0)
            cloudlog.event("athenad.main.connected_ws", ws_uri=ws_uri)
            ws.settimeout(1)
            conn_retries = 0
            handle_long_poll(ws)
        except (KeyboardInterrupt, SystemExit):
            break
        except (ConnectionError, TimeoutError, WebSocketException):
            conn_retries += 1
            params.delete("LastAthenaPingTime")
        except Exception:
            crash.capture_exception()
            cloudlog.exception("athenad.main.exception")

            conn_retries += 1
            params.delete("LastAthenaPingTime")

        time.sleep(backoff(conn_retries))
Exemple #11
0
def set_params(new_version: bool, failed_count: int, exception: Optional[str]) -> None:
  params = Params()

  params.put("UpdateFailedCount", str(failed_count))
  if failed_count == 0:
    t = datetime.datetime.utcnow().isoformat()
    params.put("LastUpdateTime", t.encode('utf8'))

  if exception is None:
    params.delete("LastUpdateException")
  else:
    params.put("LastUpdateException", exception)

  if new_version:
    try:
      with open(os.path.join(FINALIZED, "RELEASES.md"), "rb") as f:
        r = f.read()
      r = r[:r.find(b'\n\n')]  # Slice latest release notes
      params.put("ReleaseNotes", r + b"\n")
    except Exception:
      params.put("ReleaseNotes", "")
    params.put("UpdateAvailable", "1")
Exemple #12
0
def main():
    try:
        set_core_affinity([0, 1, 2, 3])
    except Exception:
        cloudlog.exception("failed to set core affinity")

    params = Params()
    dongle_id = params.get("DongleId", encoding='utf-8')
    UploadQueueCache.initialize(upload_queue)

    ws_uri = ATHENA_HOST + "/ws/v2/" + dongle_id
    api = Api(dongle_id)

    conn_retries = 0
    while 1:
        try:
            cloudlog.event("athenad.main.connecting_ws", ws_uri=ws_uri)
            ws = create_connection(ws_uri,
                                   cookie="jwt=" + api.get_token(),
                                   enable_multithread=True,
                                   timeout=30.0)
            cloudlog.event("athenad.main.connected_ws", ws_uri=ws_uri)

            conn_retries = 0
            cur_upload_items.clear()

            handle_long_poll(ws)
        except (KeyboardInterrupt, SystemExit):
            break
        except (ConnectionError, TimeoutError, WebSocketException):
            conn_retries += 1
            params.delete("LastAthenaPingTime")
        except socket.timeout:
            params.delete("LastAthenaPingTime")
        except Exception:
            cloudlog.exception("athenad.main.exception")

            conn_retries += 1
            params.delete("LastAthenaPingTime")

        time.sleep(backoff(conn_retries))
Exemple #13
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

  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 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 = 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, 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

    # **** 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 >= 2019
    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
    completed_training = params.get("CompletedTrainingVersion") == training_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"] = completed_training 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())

    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 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()
        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())

    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 #14
0
def thermald_thread():
    # prevent LEECO from undervoltage
    BATT_PERC_OFF = 10 if LEON else 3

    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

    network_type = NetworkType.none

    current_filter = FirstOrderFilter(0., CURRENT_TAU, DT_TRML)
    health_prev = None
    fw_version_match_prev = True
    current_connectivity_alert = None
    time_valid_prev = True
    should_start_prev = False

    is_uno = (read_tz(29, clip=False) < -1000)
    if is_uno or not ANDROID:
        handle_fan = handle_fan_uno
    else:
        setup_eon_fan()
        handle_fan = handle_fan_eon

    params = Params()

    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()

        # clear car params when panda gets disconnected
        if health is None and health_prev is not None:
            params.panda_disconnect()
        health_prev = health

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

        # get_network_type is an expensive call. update every 10s
        if (count % int(10. / DT_TRML)) == 0:
            try:
                network_type = get_network_type()
            except Exception:
                pass

        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

        try:
            with open("/sys/class/power_supply/battery/capacity") as f:
                msg.thermal.batteryPercent = int(f.read())
            with open("/sys/class/power_supply/battery/status") as f:
                msg.thermal.batteryStatus = f.read().strip()
            with open("/sys/class/power_supply/battery/current_now") as f:
                msg.thermal.batteryCurrent = int(f.read())
            with open("/sys/class/power_supply/battery/voltage_now") as f:
                msg.thermal.batteryVoltage = int(f.read())
            with open("/sys/class/power_supply/usb/present") as f:
                msg.thermal.usbOnline = bool(int(f.read()))
        except FileNotFoundError:
            pass

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

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

        # TODO: add car battery voltage check
        max_cpu_temp = 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.

        fan_speed = handle_fan(max_cpu_temp, bat_temp, fan_speed, ignition)
        msg.thermal.fanSpeed = fan_speed

        # thermal logic with hysterisis
        if max_cpu_temp > 107. or bat_temp >= 63.:
            # onroad not allowed
            thermal_status = ThermalStatus.danger
        elif max_comp_temp > 92.5 or bat_temp > 60.:  # CPU throttling starts around ~90C
            # hysteresis between onroad not allowed and engage not allowed
            thermal_status = clip(thermal_status, ThermalStatus.red,
                                  ThermalStatus.danger)
        elif max_cpu_temp > 87.5:
            # 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.now()

        # show invalid date/time alert
        time_valid = now.year >= 2019
        if time_valid and not time_valid_prev:
            params.delete("Offroad_InvalidTime")
        if not time_valid and time_valid_prev:
            params.put("Offroad_InvalidTime",
                       json.dumps(OFFROAD_ALERTS["Offroad_InvalidTime"]))
        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)

        if dt.days > DAYS_NO_CONNECTIVITY_MAX and update_failed_count > 1:
            if current_connectivity_alert != "expired":
                current_connectivity_alert = "expired"
                params.delete("Offroad_ConnectivityNeededPrompt")
                params.put(
                    "Offroad_ConnectivityNeeded",
                    json.dumps(OFFROAD_ALERTS["Offroad_ConnectivityNeeded"]))
        elif dt.days > DAYS_NO_CONNECTIVITY_PROMPT:
            remaining_time = str(max(DAYS_NO_CONNECTIVITY_MAX - dt.days, 0))
            if current_connectivity_alert != "prompt" + remaining_time:
                current_connectivity_alert = "prompt" + remaining_time
                alert_connectivity_prompt = copy.copy(
                    OFFROAD_ALERTS["Offroad_ConnectivityNeededPrompt"])
                alert_connectivity_prompt["text"] += remaining_time + " days."
                params.delete("Offroad_ConnectivityNeeded")
                params.put("Offroad_ConnectivityNeededPrompt",
                           json.dumps(alert_connectivity_prompt))
        elif current_connectivity_alert is not None:
            current_connectivity_alert = None
            params.delete("Offroad_ConnectivityNeeded")
            params.delete("Offroad_ConnectivityNeededPrompt")

        # start constellation of processes when the car starts
        ignition = health is not None and (health.health.ignitionLine
                                           or health.health.ignitionCan)

        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_taking_snapshot = params.get("IsTakingSnapshot") == b"1"
            should_start = should_start and (not is_taking_snapshot)

        if fw_version_match and not fw_version_match_prev:
            params.delete("Offroad_PandaFirmwareMismatch")
        if not fw_version_match and fw_version_match_prev:
            params.put(
                "Offroad_PandaFirmwareMismatch",
                json.dumps(OFFROAD_ALERTS["Offroad_PandaFirmwareMismatch"]))

        # 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:
                params.put(
                    "Offroad_TemperatureTooHigh",
                    json.dumps(OFFROAD_ALERTS["Offroad_TemperatureTooHigh"]))
        else:
            if thermal_status_prev >= ThermalStatus.danger:
                params.delete("Offroad_TemperatureTooHigh")

        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):
                params.put("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'
                )

            # 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 (sec_since_boot() - off_ts) > 60:
                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:
            params.put("Offroad_ChargeDisabled",
                       json.dumps(OFFROAD_ALERTS["Offroad_ChargeDisabled"]))
        elif usb_power and not usb_power_prev:
            params.delete("Offroad_ChargeDisabled")

        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
Exemple #15
0
class TestParams(unittest.TestCase):
  def setUp(self):
    self.tmpdir = tempfile.mkdtemp()
    print("using", self.tmpdir)
    self.params = Params(self.tmpdir)

  def tearDown(self):
    shutil.rmtree(self.tmpdir)

  def test_params_put_and_get(self):
    self.params.put("DongleId", "cb38263377b873ee")
    assert self.params.get("DongleId") == b"cb38263377b873ee"

  def test_persist_params_put_and_get(self):
    p = Params(persistent_params=True)
    p.put("DongleId", "cb38263377b873ee")
    assert p.get("DongleId") == b"cb38263377b873ee"

  def test_params_non_ascii(self):
    st = b"\xe1\x90\xff"
    self.params.put("CarParams", st)
    assert self.params.get("CarParams") == st

  def test_params_get_cleared_panda_disconnect(self):
    self.params.put("CarParams", "test")
    self.params.put("DongleId", "cb38263377b873ee")
    assert self.params.get("CarParams") == b"test"
    self.params.panda_disconnect()
    assert self.params.get("CarParams") is None
    assert self.params.get("DongleId") is not None

  def test_params_get_cleared_manager_start(self):
    self.params.put("CarParams", "test")
    self.params.put("DongleId", "cb38263377b873ee")
    assert self.params.get("CarParams") == b"test"
    self.params.manager_start()
    assert self.params.get("CarParams") is None
    assert self.params.get("DongleId") is not None

  def test_params_two_things(self):
    self.params.put("DongleId", "bob")
    self.params.put("AthenadPid", "123")
    assert self.params.get("DongleId") == b"bob"
    assert self.params.get("AthenadPid") == b"123"

  def test_params_get_block(self):
    def _delayed_writer():
      time.sleep(0.1)
      self.params.put("CarParams", "test")
    threading.Thread(target=_delayed_writer).start()
    assert self.params.get("CarParams") is None
    assert self.params.get("CarParams", True) == b"test"

  def test_params_unknown_key_fails(self):
    with self.assertRaises(UnknownKeyName):
      self.params.get("swag")

    with self.assertRaises(UnknownKeyName):
      self.params.get_bool("swag")

    with self.assertRaises(UnknownKeyName):
      self.params.put("swag", "abc")

    with self.assertRaises(UnknownKeyName):
      self.params.put_bool("swag", True)

  def test_params_permissions(self):
    permissions = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IWGRP | stat.S_IROTH | stat.S_IWOTH

    self.params.put("DongleId", "cb38263377b873ee")
    st_mode = os.stat(f"{self.tmpdir}/d/DongleId").st_mode
    assert (st_mode & permissions) == permissions

  def test_delete_not_there(self):
    assert self.params.get("CarParams") is None
    self.params.delete("CarParams")
    assert self.params.get("CarParams") is None

  def test_get_bool(self):
    self.params.delete("IsMetric")
    self.assertFalse(self.params.get_bool("IsMetric"))

    self.params.put_bool("IsMetric", True)
    self.assertTrue(self.params.get_bool("IsMetric"))

    self.params.put_bool("IsMetric", False)
    self.assertFalse(self.params.get_bool("IsMetric"))

    self.params.put("IsMetric", "1")
    self.assertTrue(self.params.get_bool("IsMetric"))

    self.params.put("IsMetric", "0")
    self.assertFalse(self.params.get_bool("IsMetric"))

  def test_put_non_blocking_with_get_block(self):
    q = Params(self.tmpdir)
    def _delayed_writer():
      time.sleep(0.1)
      put_nonblocking("CarParams", "test", self.tmpdir)
    threading.Thread(target=_delayed_writer).start()
    assert q.get("CarParams") is None
    assert q.get("CarParams", True) == b"test"
Exemple #16
0
def main():
    params = Params()

    if params.get("DisableUpdates") == b"1":
        raise RuntimeError("updates are disabled by the DisableUpdates param")

    if os.geteuid() != 0:
        raise RuntimeError("updated must be launched as root!")

    # Set low io priority
    proc = psutil.Process()
    if psutil.LINUX:
        proc.ionice(psutil.IOPRIO_CLASS_BE, value=7)

    ov_lock_fd = open(LOCK_FILE, 'w')
    try:
        fcntl.flock(ov_lock_fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
    except IOError:
        raise RuntimeError(
            "couldn't get overlay lock; is another updated running?")

    # Wait for IsOffroad to be set before our first update attempt
    wait_helper = WaitTimeHelper(proc)
    wait_helper.sleep(30)

    update_failed_count = 0
    update_available = False
    overlay_initialized = False
    while not wait_helper.shutdown:
        wait_helper.ready_event.clear()

        # Check for internet every 30s
        time_wrong = datetime.datetime.utcnow().year < 2019
        ping_failed = os.system(f"ping -W 4 -c 1 {TEST_IP}") != 0
        if ping_failed or time_wrong:
            wait_helper.sleep(30)
            continue

        # Attempt an update
        exception = None
        update_failed_count += 1
        try:
            # Re-create the overlay if BASEDIR/.git has changed since we created the overlay
            if overlay_initialized:
                overlay_init_fn = os.path.join(BASEDIR, ".overlay_init")
                git_dir_path = os.path.join(BASEDIR, ".git")
                new_files = run(
                    ["find", git_dir_path, "-newer", overlay_init_fn])

                if len(new_files.splitlines()):
                    cloudlog.info(".git directory changed, recreating overlay")
                    overlay_initialized = False

            if not overlay_initialized:
                init_ovfs()
                overlay_initialized = True

            if params.get("IsOffroad") == b"1":
                update_available = attempt_update(
                    wait_helper) or update_available
                update_failed_count = 0
                if not update_available and os.path.isdir(NEOSUPDATE_DIR):
                    shutil.rmtree(NEOSUPDATE_DIR)
            else:
                cloudlog.info("not running updater, openpilot running")

        except subprocess.CalledProcessError as e:
            cloudlog.event("update process failed",
                           cmd=e.cmd,
                           output=e.output,
                           returncode=e.returncode)
            exception = e
            overlay_initialized = False
        except Exception:
            cloudlog.exception("uncaught updated exception, shouldn't happen")

        params.put("UpdateFailedCount", str(update_failed_count))
        if exception is None:
            params.delete("LastUpdateException")
        else:
            params.put("LastUpdateException",
                       f"command failed: {exception.cmd}\n{exception.output}")

        # Wait 10 minutes between update attempts
        wait_helper.sleep(60 * 10)

    dismount_ovfs()
Exemple #17
0
#!/usr/bin/env python3
import sys
from cereal import car
from common.params import Params

# This script locks the safety model to a given value.
# When the safety model is locked, boardd will preset panda to the locked safety model

# run example:
# ./lock_safety_model.py gm

if __name__ == "__main__":

    params = Params()

    if len(sys.argv) < 2:
        params.delete("SafetyModelLock")
        print("Clear locked safety model")

    else:
        safety_model = getattr(car.CarParams.SafetyModel, sys.argv[1])
        if type(safety_model) != int:
            raise Exception("Invalid safety model: " + sys.argv[1])
        if safety_model == car.CarParams.SafetyModel.allOutput:
            raise Exception(
                "Locking the safety model to allOutput is not allowed")
        params.put("SafetyModelLock", str(safety_model))
        print("Locked safety model: " + sys.argv[1])
Exemple #18
0
def thermald_thread():
    setup_eon_fan()

    # prevent LEECO from undervoltage
    BATT_PERC_OFF = 10 if LEON else 3

    # now loop
    thermal_sock = messaging.pub_sock(service_list['thermal'].port)
    health_sock = messaging.sub_sock(service_list['health'].port)
    location_sock = messaging.sub_sock(service_list['gpsLocation'].port)
    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
    health_sock.RCVTIMEO = int(1000 * 2 *
                               DT_TRML)  # 2x the expected health frequency
    current_filter = FirstOrderFilter(0., CURRENT_TAU, DT_TRML)
    health_prev = None
    current_connectivity_alert = None

    params = Params()

    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()

        # clear car params when panda gets disconnected
        if health is None and health_prev is not None:
            params.panda_disconnect()
        health_prev = health

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

        # loggerd is gated based on free space
        avail = get_available_percent() / 100.0

        # thermal message now also includes free space
        msg.thermal.freeSpace = avail
        with open("/sys/class/power_supply/battery/capacity") as f:
            msg.thermal.batteryPercent = int(f.read())
        with open("/sys/class/power_supply/battery/status") as f:
            msg.thermal.batteryStatus = f.read().strip()
        with open("/sys/class/power_supply/battery/current_now") as f:
            msg.thermal.batteryCurrent = int(f.read())
        with open("/sys/class/power_supply/battery/voltage_now") as f:
            msg.thermal.batteryVoltage = int(f.read())
        with open("/sys/class/power_supply/usb/present") as f:
            msg.thermal.usbOnline = bool(int(f.read()))

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

        # TODO: add car battery voltage check
        max_cpu_temp = 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.
        fan_speed = handle_fan(max_cpu_temp, bat_temp, fan_speed)
        msg.thermal.fanSpeed = fan_speed

        # thermal logic with hysterisis
        if max_cpu_temp > 107. or bat_temp >= 63.:
            # onroad not allowed
            thermal_status = ThermalStatus.danger
        elif max_comp_temp > 92.5 or bat_temp > 60.:  # CPU throttling starts around ~90C
            # hysteresis between onroad not allowed and engage not allowed
            thermal_status = clip(thermal_status, ThermalStatus.red,
                                  ThermalStatus.danger)
        elif max_cpu_temp > 87.5:
            # 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.now()
        try:
            last_update = datetime.datetime.fromisoformat(
                params.get("LastUpdateTime", encoding='utf8'))
        except (TypeError, ValueError):
            last_update = now
        dt = now - last_update

        if dt.days > DAYS_NO_CONNECTIVITY_MAX:
            if current_connectivity_alert != "expired":
                current_connectivity_alert = "expired"
                params.delete("Offroad_ConnectivityNeededPrompt")
                params.put(
                    "Offroad_ConnectivityNeeded",
                    json.dumps(OFFROAD_ALERTS["Offroad_ConnectivityNeeded"]))
        elif dt.days > DAYS_NO_CONNECTIVITY_PROMPT:
            remaining_time = str(DAYS_NO_CONNECTIVITY_MAX - dt.days)
            if current_connectivity_alert != "prompt" + remaining_time:
                current_connectivity_alert = "prompt" + remaining_time
                alert_connectivity_prompt = copy.copy(
                    OFFROAD_ALERTS["Offroad_ConnectivityNeededPrompt"])
                alert_connectivity_prompt["text"] += remaining_time + " days."
                params.delete("Offroad_ConnectivityNeeded")
                params.put("Offroad_ConnectivityNeededPrompt",
                           json.dumps(alert_connectivity_prompt))
        elif current_connectivity_alert is not None:
            current_connectivity_alert = None
            params.delete("Offroad_ConnectivityNeeded")
            params.delete("Offroad_ConnectivityNeededPrompt")

        # start constellation of processes when the car starts
        ignition = health is not None and health.health.started

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

        should_start = ignition

        # have we seen a panda?
        passive = (params.get("Passive") == "1")

        # 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 (
            passive or completed_training) and (not do_uninstall)

        # 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:
                params.put(
                    "Offroad_TemperatureTooHigh",
                    json.dumps(OFFROAD_ALERTS["Offroad_TemperatureTooHigh"]))
        else:
            if thermal_status_prev >= ThermalStatus.danger:
                params.delete("Offroad_TemperatureTooHigh")

        if should_start:
            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:
            started_ts = None
            if off_ts is None:
                off_ts = sec_since_boot()
                os.system(
                    'echo powersave > /sys/class/devfreq/soc:qcom,cpubw/governor'
                )

            # 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 (sec_since_boot() - off_ts) > 60:
                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:
            params.put("Offroad_ChargeDisabled",
                       json.dumps(OFFROAD_ALERTS["Offroad_ChargeDisabled"]))
        elif usb_power and not usb_power_prev:
            params.delete("Offroad_ChargeDisabled")

        thermal_status_prev = thermal_status
        usb_power_prev = usb_power

        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
class TestUpdater(unittest.TestCase):
    def setUp(self):
        self.updated_proc = None

        self.tmp_dir = tempfile.TemporaryDirectory()
        org_dir = os.path.join(self.tmp_dir.name, "commaai")

        self.basedir = os.path.join(org_dir, "openpilot")
        self.git_remote_dir = os.path.join(org_dir, "openpilot_remote")
        self.staging_dir = os.path.join(org_dir, "safe_staging")
        for d in [
                org_dir, self.basedir, self.git_remote_dir, self.staging_dir
        ]:
            os.mkdir(d)

        self.upper_dir = os.path.join(self.staging_dir, "upper")
        self.merged_dir = os.path.join(self.staging_dir, "merged")
        self.finalized_dir = os.path.join(self.staging_dir, "finalized")

        # setup local submodule remotes
        submodules = subprocess.check_output(
            "git submodule --quiet foreach 'echo $name'",
            shell=True,
            cwd=BASEDIR,
            encoding='utf8').split()
        for s in submodules:
            sub_path = os.path.join(org_dir, s.split("_repo")[0])
            self._run(f"git clone {s} {sub_path}.git", cwd=BASEDIR)

        # setup two git repos, a remote and one we'll run updated in
        self._run([
            f"git clone {BASEDIR} {self.git_remote_dir}",
            f"git clone {self.git_remote_dir} {self.basedir}",
            f"cd {self.basedir} && git submodule init && git submodule update",
            f"cd {self.basedir} && scons -j{os.cpu_count()} cereal"
        ])

        self.params = Params(db=os.path.join(self.basedir, "persist/params"))
        self.params.clear_all()
        os.sync()

    def tearDown(self):
        try:
            if self.updated_proc is not None:
                self.updated_proc.terminate()
                self.updated_proc.wait(30)
        except Exception as e:
            print(e)
        self.tmp_dir.cleanup()

    # *** test helpers ***

    def _run(self, cmd, cwd=None):
        if not isinstance(cmd, list):
            cmd = (cmd, )

        for c in cmd:
            subprocess.check_output(c, cwd=cwd, shell=True)

    def _get_updated_proc(self):
        os.environ["PYTHONPATH"] = self.basedir
        os.environ["GIT_AUTHOR_NAME"] = "testy tester"
        os.environ["GIT_COMMITTER_NAME"] = "testy tester"
        os.environ["GIT_AUTHOR_EMAIL"] = "*****@*****.**"
        os.environ["GIT_COMMITTER_EMAIL"] = "*****@*****.**"
        os.environ["UPDATER_TEST_IP"] = "localhost"
        os.environ["UPDATER_LOCK_FILE"] = os.path.join(self.tmp_dir.name,
                                                       "updater.lock")
        os.environ["UPDATER_STAGING_ROOT"] = self.staging_dir
        updated_path = os.path.join(self.basedir, "selfdrive/updated.py")
        return subprocess.Popen(updated_path, env=os.environ)

    def _start_updater(self, offroad=True, nosleep=False):
        self.params.put("IsOffroad", "1" if offroad else "0")
        self.updated_proc = self._get_updated_proc()
        if not nosleep:
            time.sleep(1)

    def _update_now(self):
        self.updated_proc.send_signal(signal.SIGHUP)

    # TODO: this should be implemented in params
    def _read_param(self, key, timeout=1):
        ret = None
        start_time = time.monotonic()
        while ret is None:
            ret = self.params.get(key, encoding='utf8')
            if time.monotonic() - start_time > timeout:
                break
            time.sleep(0.01)
        return ret

    def _wait_for_update(self, timeout=30, clear_param=False):
        if clear_param:
            self.params.delete("LastUpdateTime")

        self._update_now()
        t = self._read_param("LastUpdateTime", timeout=timeout)
        if t is None:
            raise Exception("timed out waiting for update to complate")

    def _make_commit(self):
        all_dirs, all_files = [], []
        for root, dirs, files in os.walk(self.git_remote_dir):
            if ".git" in root:
                continue
            for d in dirs:
                all_dirs.append(os.path.join(root, d))
            for f in files:
                all_files.append(os.path.join(root, f))

        # make a new dir and some new files
        new_dir = os.path.join(self.git_remote_dir, "this_is_a_new_dir")
        os.mkdir(new_dir)
        for _ in range(random.randrange(5, 30)):
            for d in (new_dir, random.choice(all_dirs)):
                with tempfile.NamedTemporaryFile(dir=d, delete=False) as f:
                    f.write(os.urandom(random.randrange(1, 1000000)))

        # modify some files
        for f in random.sample(all_files, random.randrange(5, 50)):
            with open(f, "w+") as ff:
                txt = ff.readlines()
                ff.seek(0)
                for line in txt:
                    ff.write(line[::-1])

        # remove some files
        for f in random.sample(all_files, random.randrange(5, 50)):
            os.remove(f)

        # remove some dirs
        for d in random.sample(all_dirs, random.randrange(1, 10)):
            shutil.rmtree(d)

        # commit the changes
        self._run([
            "git add -A",
            "git commit -m 'an update'",
        ],
                  cwd=self.git_remote_dir)

    def _check_update_state(self, update_available):
        # make sure LastUpdateTime is recent
        t = self._read_param("LastUpdateTime")
        last_update_time = datetime.datetime.fromisoformat(t)
        td = datetime.datetime.utcnow() - last_update_time
        self.assertLess(td.total_seconds(), 10)
        self.params.delete("LastUpdateTime")

        # wait a bit for the rest of the params to be written
        time.sleep(0.1)

        # check params
        update = self._read_param("UpdateAvailable")
        self.assertEqual(update == "1", update_available,
                         f"UpdateAvailable: {repr(update)}")
        self.assertEqual(self._read_param("UpdateFailedCount"), "0")

        # TODO: check that the finalized update actually matches remote
        # check the .overlay_init and .overlay_consistent flags
        self.assertTrue(
            os.path.isfile(os.path.join(self.basedir, ".overlay_init")))
        self.assertEqual(
            os.path.isfile(
                os.path.join(self.finalized_dir, ".overlay_consistent")),
            update_available)

    # *** test cases ***

    # Run updated for 100 cycles with no update
    def test_no_update(self):
        self._start_updater()
        for _ in range(100):
            self._wait_for_update(clear_param=True)
            self._check_update_state(False)

    # Let the updater run with no update for a cycle, then write an update
    def test_update(self):
        self._start_updater()

        # run for a cycle with no update
        self._wait_for_update(clear_param=True)
        self._check_update_state(False)

        # write an update to our remote
        self._make_commit()

        # run for a cycle to get the update
        self._wait_for_update(timeout=60, clear_param=True)
        self._check_update_state(True)

        # run another cycle with no update
        self._wait_for_update(clear_param=True)
        self._check_update_state(True)

    # Let the updater run for 10 cycles, and write an update every cycle
    @unittest.skip("need to make this faster")
    def test_update_loop(self):
        self._start_updater()

        # run for a cycle with no update
        self._wait_for_update(clear_param=True)
        for _ in range(10):
            time.sleep(0.5)
            self._make_commit()
            self._wait_for_update(timeout=90, clear_param=True)
            self._check_update_state(True)

    # Test overlay re-creation after tracking a new file in basedir's git
    def test_overlay_reinit(self):
        self._start_updater()

        overlay_init_fn = os.path.join(self.basedir, ".overlay_init")

        # run for a cycle with no update
        self._wait_for_update(clear_param=True)
        self.params.delete("LastUpdateTime")
        first_mtime = os.path.getmtime(overlay_init_fn)

        # touch a file in the basedir
        self._run("touch new_file && git add new_file", cwd=self.basedir)

        # run another cycle, should have a new mtime
        self._wait_for_update(clear_param=True)
        second_mtime = os.path.getmtime(overlay_init_fn)
        self.assertTrue(first_mtime != second_mtime)

        # run another cycle, mtime should be same as last cycle
        self._wait_for_update(clear_param=True)
        new_mtime = os.path.getmtime(overlay_init_fn)
        self.assertTrue(second_mtime == new_mtime)

    # Make sure updated exits if another instance is running
    def test_multiple_instances(self):
        # start updated and let it run for a cycle
        self._start_updater()
        time.sleep(1)
        self._wait_for_update(clear_param=True)

        # start another instance
        second_updated = self._get_updated_proc()
        ret_code = second_updated.wait(timeout=5)
        self.assertTrue(ret_code is not None)
Exemple #20
0
def thermald_thread():
  # prevent LEECO from undervoltage
  BATT_PERC_OFF = 10 if LEON else 3

  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

  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()

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

    # 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

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

      # 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 (sec_since_boot() - off_ts) > 60:
        os.system('LD_LIBRARY_PATH="" svc power shutdown')

    # 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

    # 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 #21
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
    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)
    pandaState_prev = None
    should_start_prev = False
    handle_fan = None
    is_uno = False
    ui_running_prev = False

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

    thermal_config = HARDWARE.get_thermal_config()

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

    while 1:
        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 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.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)
            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.networkType = network_type
        msg.deviceState.networkStrength = network_strength
        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:
            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(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

        # **** 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["up_to_date"] = params.get(
            "Offroad_ConnectivityNeeded") is None or params.get(
                "DisableUpdates") == b"1"
        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.deviceState.freeSpacePercent > 2
        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"]))

        # Handle offroad/onroad transition
        should_start = all(startup_conditions.values())
        if should_start:
            if not should_start_prev:
                params.delete("IsOffroad")
                if TICI and DISABLE_LTE_ONROAD:
                    os.system("sudo systemctl stop --no-block lte")

            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")
                if TICI and DISABLE_LTE_ONROAD:
                    os.system("sudo systemctl start --no-block lte")

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

        # Offroad power monitoring
        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):
            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
        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))

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

  # if limitting battery to Min-Max%. edit /data/bb_openpilot.cfg
  car_set = CarSettings()
  limitBatteryMinMax = car_set.get_value("limitBatteryMinMax")
  batt_min = car_set.get_value("limitBattery_Min")
  batt_max = car_set.get_value("limitBattery_Max")

  setup_eon_fan()

  # prevent LEECO from undervoltage
  BATT_PERC_OFF = 10 if LEON else 3

  health_timeout = int(1000 * 2 * DT_TRML)  # 2x 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

  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_filter = FirstOrderFilter(0., CURRENT_TAU, DT_TRML)
  health_prev = None
  fw_version_match_prev = True
  current_connectivity_alert = None
  time_valid_prev = True

  # Make sure charging is enabled
  charging_disabled = False
  os.system('echo "1" > /sys/class/power_supply/battery/charging_enabled')

  params = Params()

  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()

    # clear car params when panda gets disconnected
    if health is None and health_prev is not None:
      params.panda_disconnect()
    health_prev = health

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

    # loggerd is gated based on free space
    avail = get_available_percent() / 100.0

    # thermal message now also includes free space
    msg.thermal.freeSpace = avail
    charger_off = False
    with open("/sys/class/power_supply/battery/capacity") as f:
      msg.thermal.batteryPercent = int(f.read())
    with open("/sys/class/power_supply/battery/status") as f:
      msg.thermal.batteryStatus = f.read().strip()
    with open("/sys/class/power_supply/battery/current_now") as f:
      msg.thermal.batteryCurrent = int(f.read())
    with open("/sys/class/power_supply/battery/voltage_now") as f:
      msg.thermal.batteryVoltage = int(f.read())
    with open("/sys/class/power_supply/usb/present") as f:
      msg.thermal.usbOnline = bool(int(f.read()))
    with open("/sys/class/power_supply/battery/charge_type") as f:
      charger_status = f.read().strip()
    charger_off = (charger_status == "N/A")
    current_filter.update(msg.thermal.batteryCurrent / 1e6)

    # TODO: add car battery voltage check
    max_cpu_temp = 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 health is not None and health.health.hwType == log.HealthData.HwType.uno:
      fan_speed = handle_fan_uno(max_cpu_temp, bat_temp, fan_speed)
    else:
      fan_speed = handle_fan_eon(max_cpu_temp, bat_temp, fan_speed)

    msg.thermal.fanSpeed = fan_speed

    # thermal logic with hysterisis
    if max_cpu_temp > 107. or bat_temp >= 63.:
      # onroad not allowed
      thermal_status = ThermalStatus.danger
    elif max_comp_temp > 92.5 or bat_temp > 60.: # CPU throttling starts around ~90C
      # hysteresis between onroad not allowed and engage not allowed
      thermal_status = clip(thermal_status, ThermalStatus.red, ThermalStatus.danger)
    elif max_cpu_temp > 87.5:
      # 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.now()

    # show invalid date/time alert
    time_valid = now.year >= 2019
    if time_valid and not time_valid_prev:
      params.delete("Offroad_InvalidTime")
    if not time_valid and time_valid_prev:
      params.put("Offroad_InvalidTime", json.dumps(OFFROAD_ALERTS["Offroad_InvalidTime"]))
    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

    if dt.days > DAYS_NO_CONNECTIVITY_MAX:
      if current_connectivity_alert != "expired":
        current_connectivity_alert = "expired"
        params.delete("Offroad_ConnectivityNeededPrompt")
        params.put("Offroad_ConnectivityNeeded", json.dumps(OFFROAD_ALERTS["Offroad_ConnectivityNeeded"]))
    elif dt.days > DAYS_NO_CONNECTIVITY_PROMPT:
      remaining_time = str(DAYS_NO_CONNECTIVITY_MAX - dt.days)
      if current_connectivity_alert != "prompt" + remaining_time:
        current_connectivity_alert = "prompt" + remaining_time
        alert_connectivity_prompt = copy.copy(OFFROAD_ALERTS["Offroad_ConnectivityNeededPrompt"])
        alert_connectivity_prompt["text"] += remaining_time + " days."
        params.delete("Offroad_ConnectivityNeeded")
        params.put("Offroad_ConnectivityNeededPrompt", json.dumps(alert_connectivity_prompt))
    elif current_connectivity_alert is not None:
      current_connectivity_alert = None
      params.delete("Offroad_ConnectivityNeeded")
      params.delete("Offroad_ConnectivityNeededPrompt")

    # start constellation of processes when the car starts
    ignition = health is not None and (health.health.ignitionLine or health.health.ignitionCan)

    do_uninstall = params.get("DoUninstall") == b"1"
    accepted_terms = params.get("HasAcceptedTerms") == terms_version
    completed_training = params.get("CompletedTrainingVersion") == training_version
    fw_version = params.get("PandaFirmware", encoding="utf8")
    fw_version_match = fw_version is None or fw_version.startswith(FW_VERSION)  # don't show alert is no panda is connected (None)

    should_start = ignition

    # have we seen a panda?
    passive = (params.get("Passive") == "1")

    # 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 (passive or 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

    if fw_version_match and not fw_version_match_prev:
      params.delete("Offroad_PandaFirmwareMismatch")
    if not fw_version_match and fw_version_match_prev:
      params.put("Offroad_PandaFirmwareMismatch", json.dumps(OFFROAD_ALERTS["Offroad_PandaFirmwareMismatch"]))

    # 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:
        params.put("Offroad_TemperatureTooHigh", json.dumps(OFFROAD_ALERTS["Offroad_TemperatureTooHigh"]))
    else:
      if thermal_status_prev >= ThermalStatus.danger:
        params.delete("Offroad_TemperatureTooHigh")

    if should_start:
      off_ts = None
      if started_ts is None:
        params.car_start()
        started_ts = sec_since_boot()
        started_seen = True
        os.system('echo performance > /sys/class/devfreq/soc:qcom,cpubw/governor')
    else:
      started_ts = None
      if off_ts is None:
        off_ts = sec_since_boot()
        os.system('echo powersave > /sys/class/devfreq/soc:qcom,cpubw/governor')

      # 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 (sec_since_boot() - off_ts) > 60:
        os.system('LD_LIBRARY_PATH="" svc power shutdown')

    charging_disabled = check_car_battery_voltage(should_start, health, charging_disabled, msg, limitBatteryMinMax, batt_min, batt_max)

    msg.thermal.chargingDisabled = charging_disabled
    #BB added "and not charging_disabled" below so we don't show red LED when not charging
    msg.thermal.chargingError = (current_filter.x > 0.) and (msg.thermal.batteryPercent < 90) and not charging_disabled   # if current is > 1A out, then charger might be off
     
    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:
      params.put("Offroad_ChargeDisabled", json.dumps(OFFROAD_ALERTS["Offroad_ChargeDisabled"]))
    elif usb_power and not usb_power_prev:
      params.delete("Offroad_ChargeDisabled")

    thermal_status_prev = thermal_status
    usb_power_prev = usb_power
    fw_version_match_prev = fw_version_match

    #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
Exemple #23
0
def manager_init() -> None:
  # update system time from panda
  set_time(cloudlog)

  # save boot log
  #subprocess.call("./bootlog", cwd=os.path.join(BASEDIR, "selfdrive/loggerd"))

  params = Params()
  params.clear_all(ParamKeyType.CLEAR_ON_MANAGER_START)

  default_params: List[Tuple[str, Union[str, bytes]]] = [
    ("CompletedTrainingVersion", "0"),
    ("DisengageOnAccelerator", "0"),
    ("HasAcceptedTerms", "0"),
    ("OpenpilotEnabledToggle", "1"),
    ("IsMetric", "1"),

    # HKG
    ("LateralControl", "TORQUE"),
    ("UseClusterSpeed", "0"),
    ("LongControlEnabled", "0"),
    ("MadModeEnabled", "1"),
    ("IsLdwsCar", "0"),
    ("LaneChangeEnabled", "0"),
    ("AutoLaneChangeEnabled", "0"),

    ("SccSmootherSlowOnCurves", "0"),
    ("SccSmootherSyncGasPressed", "0"),
    ("StockNaviDecelEnabled", "0"),
    ("KeepSteeringTurnSignals", "0"),
    ("HapticFeedbackWhenSpeedCamera", "0"),
    ("DisableOpFcw", "0"),
    ("ShowDebugUI", "0"),
    ("NewRadarInterface", "0"),
  ]
  if not PC:
    default_params.append(("LastUpdateTime", datetime.datetime.utcnow().isoformat().encode('utf8')))

  if params.get_bool("RecordFrontLock"):
    params.put_bool("RecordFront", True)

  if not params.get_bool("DisableRadar_Allow"):
    params.delete("DisableRadar")

  # set unset params
  for k, v in default_params:
    if params.get(k) is None:
      params.put(k, v)

  # is this dashcam?
  if os.getenv("PASSIVE") is not None:
    params.put_bool("Passive", bool(int(os.getenv("PASSIVE", "0"))))

  if params.get("Passive") is None:
    raise Exception("Passive must be set to continue")

  # Create folders needed for msgq
  try:
    os.mkdir("/dev/shm")
  except FileExistsError:
    pass
  except PermissionError:
    print("WARNING: failed to make /dev/shm")

  # set version params
  params.put("Version", get_version())
  params.put("TermsVersion", terms_version)
  params.put("TrainingVersion", training_version)
  params.put("GitCommit", get_commit(default=""))
  params.put("GitBranch", get_short_branch(default=""))
  params.put("GitRemote", get_origin(default=""))

  # set dongle id
  reg_res = register(show_spinner=True)
  if reg_res:
    dongle_id = reg_res
  else:
    serial = params.get("HardwareSerial")
    raise Exception(f"Registration failed for device {serial}")
  os.environ['DONGLE_ID'] = dongle_id  # Needed for swaglog

  if not is_dirty():
    os.environ['CLEAN'] = '1'

  # init logging
  sentry.init(sentry.SentryProject.SELFDRIVE)
  cloudlog.bind_global(dongle_id=dongle_id, version=get_version(), dirty=is_dirty(),
                       device=HARDWARE.get_device_type())
Exemple #24
0
    calibration_data = json.loads(calibration_data)
    calibration = np.array(calibration_data['calibration'], dtype=np.float32)
    if 'center_bias' in calibration_data:
        center_bias = calibration_data['center_bias']
    if 'model_bias' in calibration_data:
        model_bias = calibration_data['model_bias']

if calibration_data is None or len(calibration) != (len(calibration_items[0]) +
                                                    len(calibration_items[1])):
    calibration = [
        np.zeros(len(calibration_items[0]), dtype=np.float32),
        np.zeros(len(calibration_items[1]), dtype=np.float32)
    ]
    calibrated = False
    print("resetting calibration")
    params.delete("CalibrationParams")
else:
    calibration = [
        np.array(calibration[:len(calibration_items[0])], dtype=np.float32),
        np.array(calibration[len(calibration_items[0]):], dtype=np.float32)
    ]
    lane_width = calibration_data['lane_width']
    angle_bias = calibration_data['angle_bias']

print(calibration)

stock_cam_frame_prev = -1
combine_flags = 0
vehicle_array = []
first_model = 0
last_model = len(models) - 1
Exemple #25
0
def manager_init():
  # update system time from panda
  set_time(cloudlog)

  # save boot log
  subprocess.call("./bootlog", cwd=os.path.join(BASEDIR, "selfdrive/loggerd"))

  params = Params()
  params.clear_all(ParamKeyType.CLEAR_ON_MANAGER_START)

  default_params = [
    ("CompletedTrainingVersion", "0"),
    ("HasAcceptedTerms", "0"),
    ("OpenpilotEnabledToggle", "1"),
  ]
  if not PC:
    default_params.append(("LastUpdateTime", datetime.datetime.utcnow().isoformat().encode('utf8')))

  if params.get_bool("RecordFrontLock"):
    params.put_bool("RecordFront", True)

  if not params.get_bool("DisableRadar_Allow"):
    params.delete("DisableRadar")

  # set unset params
  for k, v in default_params:
    if params.get(k) is None:
      params.put(k, v)

  # is this dashcam?
  if os.getenv("PASSIVE") is not None:
    params.put_bool("Passive", bool(int(os.getenv("PASSIVE"))))

  if params.get("Passive") is None:
    raise Exception("Passive must be set to continue")

  # Create folders needed for msgq
  try:
    os.mkdir("/dev/shm")
  except FileExistsError:
    pass
  except PermissionError:
    print("WARNING: failed to make /dev/shm")

  # set version params
  params.put("Version", version)
  params.put("TermsVersion", terms_version)
  params.put("TrainingVersion", training_version)
  params.put("GitCommit", get_git_commit(default=""))
  params.put("GitBranch", get_git_branch(default=""))
  params.put("GitRemote", get_git_remote(default=""))

  # set dongle id
  reg_res = register(show_spinner=True)
  if reg_res:
    dongle_id = reg_res
  else:
    serial = params.get("HardwareSerial")
    raise Exception(f"Registration failed for device {serial}")
  os.environ['DONGLE_ID'] = dongle_id  # Needed for swaglog

  if not dirty:
    os.environ['CLEAN'] = '1'

  cloudlog.bind_global(dongle_id=dongle_id, version=version, dirty=dirty,
                       device=HARDWARE.get_device_type())

  if comma_remote and not (os.getenv("NOLOG") or os.getenv("NOCRASH") or PC):
    crash.init()
  crash.bind_user(id=dongle_id)
  crash.bind_extra(dirty=dirty, origin=origin, branch=branch, commit=commit,
                   device=HARDWARE.get_device_type())
Exemple #26
0
class TestParams(unittest.TestCase):
    def setUp(self):
        self.tmpdir = tempfile.mkdtemp()
        print("using", self.tmpdir)
        self.params = Params(self.tmpdir)

    def tearDown(self):
        shutil.rmtree(self.tmpdir)

    def test_params_put_and_get(self):
        self.params.put("DongleId", "cb38263377b873ee")
        assert self.params.get("DongleId") == b"cb38263377b873ee"

    def test_params_non_ascii(self):
        st = b"\xe1\x90\xff"
        self.params.put("CarParams", st)
        assert self.params.get("CarParams") == st

    def test_params_get_cleared_manager_start(self):
        self.params.put("CarParams", "test")
        self.params.put("DongleId", "cb38263377b873ee")
        assert self.params.get("CarParams") == b"test"
        self.params.clear_all(ParamKeyType.CLEAR_ON_MANAGER_START)
        assert self.params.get("CarParams") is None
        assert self.params.get("DongleId") is not None

    def test_params_two_things(self):
        self.params.put("DongleId", "bob")
        self.params.put("AthenadPid", "123")
        assert self.params.get("DongleId") == b"bob"
        assert self.params.get("AthenadPid") == b"123"

    def test_params_get_block(self):
        def _delayed_writer():
            time.sleep(0.1)
            self.params.put("CarParams", "test")

        threading.Thread(target=_delayed_writer).start()
        assert self.params.get("CarParams") is None
        assert self.params.get("CarParams", True) == b"test"

    def test_params_unknown_key_fails(self):
        with self.assertRaises(UnknownKeyName):
            self.params.get("swag")

        with self.assertRaises(UnknownKeyName):
            self.params.get_bool("swag")

        with self.assertRaises(UnknownKeyName):
            self.params.put("swag", "abc")

        with self.assertRaises(UnknownKeyName):
            self.params.put_bool("swag", True)

    def test_delete_not_there(self):
        assert self.params.get("CarParams") is None
        self.params.delete("CarParams")
        assert self.params.get("CarParams") is None

    def test_get_bool(self):
        self.params.delete("IsMetric")
        self.assertFalse(self.params.get_bool("IsMetric"))

        self.params.put_bool("IsMetric", True)
        self.assertTrue(self.params.get_bool("IsMetric"))

        self.params.put_bool("IsMetric", False)
        self.assertFalse(self.params.get_bool("IsMetric"))

        self.params.put("IsMetric", "1")
        self.assertTrue(self.params.get_bool("IsMetric"))

        self.params.put("IsMetric", "0")
        self.assertFalse(self.params.get_bool("IsMetric"))

    def test_put_non_blocking_with_get_block(self):
        q = Params(self.tmpdir)

        def _delayed_writer():
            time.sleep(0.1)
            put_nonblocking("CarParams", "test", self.tmpdir)

        threading.Thread(target=_delayed_writer).start()
        assert q.get("CarParams") is None
        assert q.get("CarParams", True) == b"test"

    def test_put_bool_non_blocking_with_get_block(self):
        q = Params(self.tmpdir)

        def _delayed_writer():
            time.sleep(0.1)
            put_bool_nonblocking("CarParams", True, self.tmpdir)

        threading.Thread(target=_delayed_writer).start()
        assert q.get("CarParams") is None
        assert q.get("CarParams", True) == b"1"
Exemple #27
0
        can_function(pm, speed, steer_angle, rk.frame, rk.frame % 500 == 499)
        if rk.frame % 5 == 0:
            throttle, brake, steer = sendcan_function(sendcan)
            steer_angle += steer / 10000.0  # torque
            vc = carla.VehicleControl(throttle=throttle,
                                      steer=steer_angle,
                                      brake=brake)
            vehicle.apply_control(vc)
            print(speed, steer_angle, vc)

        rk.keep_time()


if __name__ == "__main__":
    params = Params()
    params.delete("Offroad_ConnectivityNeeded")
    from selfdrive.version import terms_version, training_version
    params.put("HasAcceptedTerms", terms_version)
    params.put("CompletedTrainingVersion", training_version)
    params.put("CommunityFeaturesToggle", "1")

    threading.Thread(target=health_function).start()
    threading.Thread(target=fake_driver_monitoring).start()

    # no carla, still run
    try:
        import carla
    except ImportError:
        print("WARNING: NO CARLA")
        while 1:
            time.sleep(1)
Exemple #28
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
    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

    thermal_config = get_thermal_config()

    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'

    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(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
        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 = 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 = 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

        # 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.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
        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

        # **** 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 (not do_uninstall) and \
                       (completed_training or current_branch in ['dashcam', 'dashcam-staging'])

        # 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.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.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
Exemple #29
0
def main():
    params = Params()
    dongle_id = params.get("DongleId", encoding='utf-8')

    ws_uri = ATHENA_HOST + "/ws/v2/" + dongle_id
    api = Api(dongle_id)

    conn_retries = 0
    while 1:
        try:
            cloudlog.event("athenad.main.connecting_ws", ws_uri=ws_uri)
            ws = create_connection(ws_uri,
                                   cookie="jwt=" + api.get_token(),
                                   enable_multithread=True,
                                   timeout=30.0)
            cloudlog.event("athenad.main.connected_ws", ws_uri=ws_uri)
            params.delete("PrimeRedirected")

            manage_tokens(api)

            conn_retries = 0
            handle_long_poll(ws)
        except (KeyboardInterrupt, SystemExit):
            break
        except (ConnectionError, TimeoutError, WebSocketException):
            conn_retries += 1
            params.delete("PrimeRedirected")
            params.delete("LastAthenaPingTime")
        except socket.timeout:
            try:
                r = requests.get(
                    "http://api.commadotai.com/v1/me",
                    allow_redirects=False,
                    headers={"User-Agent": f"openpilot-{version}"},
                    timeout=15.0)
                if r.status_code == 302 and r.headers['Location'].startswith(
                        "http://u.web2go.com"):
                    params.put_bool("PrimeRedirected", True)
            except Exception:
                cloudlog.exception("athenad.socket_timeout.exception")
            params.delete("LastAthenaPingTime")
        except Exception:
            cloudlog.exception("athenad.main.exception")

            conn_retries += 1
            params.delete("PrimeRedirected")
            params.delete("LastAthenaPingTime")

        time.sleep(backoff(conn_retries))
Exemple #30
0
def fingerprint(logcan, sendcan, has_relay):
  params = Params()
  car_params = params.get("CarParams")

  if not travis:
    cached_fingerprint = params.get('CachedFingerprint')
  else:
    cached_fingerprint = None

  if car_params is not None:
    car_params = car.CarParams.from_bytes(car_params)
  fixed_fingerprint = os.environ.get('FINGERPRINT', "")
  skip_fw_query = os.environ.get('SKIP_FW_QUERY', False)

  if has_relay and not fixed_fingerprint and not skip_fw_query:
    # Vin query only reliably works thorugh OBDII
    bus = 1

    cached_params = Params().get("CarParamsCache")
    if cached_params is not None:
      cached_params = car.CarParams.from_bytes(cached_params)
      if cached_params.carName == "mock":
        cached_params = None

    if cached_params is not None and len(cached_params.carFw) > 0 and cached_params.carVin is not VIN_UNKNOWN:
      cloudlog.warning("Using cached CarParams")
      vin = cached_params.carVin
      car_fw = list(cached_params.carFw)
    else:
      cloudlog.warning("Getting VIN & FW versions")
      _, vin = get_vin(logcan, sendcan, bus)
      car_fw = get_fw_versions(logcan, sendcan, bus)

    fw_candidates = match_fw_to_car(car_fw)
  else:
    vin = VIN_UNKNOWN
    fw_candidates, car_fw = set(), []

  cloudlog.warning("VIN %s", vin)
  Params().put("CarVin", vin)

  finger = gen_empty_fingerprint()
  candidate_cars = {i: all_known_cars() for i in [0, 1]}  # attempt fingerprint on both bus 0 and 1
  frame = 0
  frame_fingerprint = 10  # 0.1s
  car_fingerprint = None
  done = False


  if cached_fingerprint is not None and use_car_caching:  # if we previously identified a car and fingerprint and user hasn't disabled caching
    cached_fingerprint = json.loads(cached_fingerprint)
    if cached_fingerprint[0] is None or len(cached_fingerprint) < 3:
      params.delete('CachedFingerprint')
    else:
      finger[0] = {int(key): value for key, value in cached_fingerprint[2].items()}
      source = car.CarParams.FingerprintSource.can
      return (str(cached_fingerprint[0]), finger, vin, car_fw, cached_fingerprint[1])



  while not done:
    a = messaging.get_one_can(logcan)

    for can in a.can:
      # need to independently try to fingerprint both bus 0 and 1 to work
      # for the combo black_panda and honda_bosch. Ignore extended messages
      # and VIN query response.
      # Include bus 2 for toyotas to disambiguate cars using camera messages
      # (ideally should be done for all cars but we can't for Honda Bosch)
      if can.src in range(0, 4):
        finger[can.src][can.address] = len(can.dat)
      for b in candidate_cars:
        if (can.src == b or (only_toyota_left(candidate_cars[b]) and can.src == 2)) and \
           can.address < 0x800 and can.address not in [0x7df, 0x7e0, 0x7e8]:
          candidate_cars[b] = eliminate_incompatible_cars(can, candidate_cars[b])

    # if we only have one car choice and the time since we got our first
    # message has elapsed, exit
    for b in candidate_cars:
      # Toyota needs higher time to fingerprint, since DSU does not broadcast immediately
      if only_toyota_left(candidate_cars[b]):
        frame_fingerprint = 100  # 1s
      if len(candidate_cars[b]) == 1:
        if frame > frame_fingerprint:
          # fingerprint done
          car_fingerprint = candidate_cars[b][0]
      elif len(candidate_cars[b]) < 4: # For the RAV4 2019 and Corolla 2020 LE Fingerprint problem
        if frame > 180:
          if any(("TOYOTA COROLLA TSS2 2019" in c) for c in candidate_cars[b]):
            car_fingerprint = "TOYOTA COROLLA TSS2 2019"
          if any(("TOYOTA COROLLA HYBRID TSS2 2019" in c) for c in candidate_cars[b]):
            car_fingerprint = "TOYOTA COROLLA HYBRID TSS2 2019"
          if any(("HYUNDAI IONIQ ELECTRIC LIMITED 2019" in c) for c in candidate_cars[b]):
            car_fingerprint = "HYUNDAI IONIQ ELECTRIC LIMITED 2019"

    # bail if no cars left or we've been waiting for more than 2s
    failed = all(len(cc) == 0 for cc in candidate_cars.values()) or frame > 200
    succeeded = car_fingerprint is not None
    done = failed or succeeded

    frame += 1

  source = car.CarParams.FingerprintSource.can

  # If FW query returns exactly 1 candidate, use it
  if len(fw_candidates) == 1:
    car_fingerprint = list(fw_candidates)[0]
    source = car.CarParams.FingerprintSource.fw

  if fixed_fingerprint:
    car_fingerprint = fixed_fingerprint
    source = car.CarParams.FingerprintSource.fixed

  cloudlog.warning("fingerprinted %s", car_fingerprint)
  params.put("CachedFingerprint", json.dumps([car_fingerprint, source, {int(key): value for key, value in finger[0].items()}]))
  return car_fingerprint, finger, vin, car_fw, source