示例#1
0
def setup_env(simulation=False, CP=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 simulation:
    os.environ["SIMULATION"] = "1"
  elif "SIMULATION" in os.environ:
    del os.environ["SIMULATION"]

  # 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
示例#2
0
    def test_startup_alert(self, expected_event, car_model, toggle_enabled,
                           fw_versions):

        # TODO: this should be done without any real sockets
        controls_sock = messaging.sub_sock("controlsState")
        pm = messaging.PubMaster(['can', 'pandaStates'])

        params = Params()
        params.clear_all()
        params.put_bool("Passive", False)
        params.put_bool("OpenpilotEnabledToggle", True)
        params.put_bool("CommunityFeaturesToggle", toggle_enabled)

        # Build capnn version of FW array
        if fw_versions is not None:
            car_fw = []
            cp = car.CarParams.new_message()
            for ecu, addr, subaddress, version in fw_versions:
                f = car.CarParams.CarFw.new_message()
                f.ecu = ecu
                f.address = addr
                f.fwVersion = version

                if subaddress is not None:
                    f.subAddress = subaddress

                car_fw.append(f)
            cp.carVin = "1" * 17
            cp.carFw = car_fw
            params.put("CarParamsCache", cp.to_bytes())

        time.sleep(2)  # wait for controlsd to be ready

        msg = messaging.new_message('pandaStates', 1)
        msg.pandaStates[0].pandaType = log.PandaState.PandaType.uno
        pm.send('pandaStates', msg)

        # fingerprint
        if (car_model is None) or (fw_versions is not None):
            finger = {addr: 1 for addr in range(1, 100)}
        else:
            finger = _FINGERPRINTS[car_model][0]

        for _ in range(1000):
            msgs = [[addr, 0, b'\x00' * length, 0]
                    for addr, length in finger.items()]
            pm.send('can', can_list_to_can_capnp(msgs))

            time.sleep(0.01)
            msgs = messaging.drain_sock(controls_sock)
            if len(msgs):
                event_name = msgs[0].controlsState.alertType.split("/")[0]
                self.assertEqual(
                    EVENT_NAME[expected_event], event_name,
                    f"expected {EVENT_NAME[expected_event]} for '{car_model}', got {event_name}"
                )
                break
        else:
            self.fail(f"failed to fingerprint {car_model}")
示例#3
0
  def setUpClass(cls):
    if "DEBUG" in os.environ:
      segs = filter(lambda x: os.path.exists(os.path.join(x, "rlog")), Path(ROOT).iterdir())
      segs = sorted(segs, key=lambda x: x.stat().st_mtime)
      cls.lr = list(LogReader(os.path.join(segs[-1], "rlog")))
      return

    # setup env
    os.environ['REPLAY'] = "1"
    os.environ['SKIP_FW_QUERY'] = "1"
    os.environ['FINGERPRINT'] = "TOYOTA COROLLA TSS2 2019"

    params = Params()
    params.clear_all()
    set_params_enabled()

    # Make sure athena isn't running
    os.system("pkill -9 -f athena")

    # start manager and run openpilot for a minute
    proc = None
    try:
      manager_path = os.path.join(BASEDIR, "selfdrive/manager/manager.py")
      proc = subprocess.Popen(["python", manager_path])

      sm = messaging.SubMaster(['carState'])
      with Timeout(150, "controls didn't start"):
        while sm.rcv_frame['carState'] < 0:
          sm.update(1000)

      # make sure we get at least two full segments
      route = None
      cls.segments = []
      with Timeout(300, "timed out waiting for logs"):
        while route is None:
          route = params.get("CurrentRoute", encoding="utf-8")
          time.sleep(0.1)

        while len(cls.segments) < 3:
          segs = set()
          if Path(ROOT).exists():
            segs = set(Path(ROOT).glob(f"{route}--*"))
          cls.segments = sorted(segs, key=lambda s: int(str(s).rsplit('--')[-1]))
          time.sleep(2)

      # chop off last, incomplete segment
      cls.segments = cls.segments[:-1]

    finally:
      if proc is not None:
        proc.terminate()
        if proc.wait(60) is None:
          proc.kill()

    cls.lrs = [list(LogReader(os.path.join(str(s), "rlog"))) for s in cls.segments]

    # use the second segment by default as it's the first full segment
    cls.lr = list(LogReader(os.path.join(str(cls.segments[1]), "rlog")))
示例#4
0
    def setUpClass(cls):
        os.environ['SIMULATION'] = "1"
        os.environ['SKIP_FW_QUERY'] = "1"
        os.environ['NO_CAN_TIMEOUT'] = "1"

        params = Params()
        params.clear_all()
        params.put_bool("Passive", bool(os.getenv("PASSIVE")))
        params.put_bool("OpenpilotEnabledToggle", True)
示例#5
0
def setup_env():
  params = Params()
  params.clear_all()
  params.put_bool("OpenpilotEnabledToggle", True)
  params.put_bool("Passive", False)
  params.put_bool("CommunityFeaturesToggle", True)

  os.environ['NO_RADAR_SLEEP'] = "1"
  os.environ["SIMULATION"] = "1"
示例#6
0
    def setUpClass(cls):
        os.environ['SIMULATION'] = "1"
        os.environ['SKIP_FW_QUERY'] = "1"
        os.environ['NO_CAN_TIMEOUT'] = "1"

        setup_output()

        params = Params()
        params.clear_all()
        params.put("Passive", "1" if os.getenv("PASSIVE") else "0")
        params.put("OpenpilotEnabledToggle", "1")
        params.put("CommunityFeaturesToggle", "1")
示例#7
0
def setup_env(simulation=False):
    params = Params()
    params.clear_all()
    params.put_bool("OpenpilotEnabledToggle", True)
    params.put_bool("Passive", False)

    os.environ["NO_RADAR_SLEEP"] = "1"
    os.environ["REPLAY"] = "1"

    if simulation:
        os.environ["SIMULATION"] = "1"
    elif "SIMULATION" in os.environ:
        del os.environ["SIMULATION"]
示例#8
0
    def setUpClass(cls):
        os.environ['NO_CAN_TIMEOUT'] = "1"

        setup_output()

        params = Params()
        params.clear_all()
        params.put("Passive", "1" if os.getenv("PASSIVE") else "0")
        params.put("OpenpilotEnabledToggle", "1")
        params.put("CommunityFeaturesToggle", "1")

        manager.prepare_managed_process('radard')
        manager.prepare_managed_process('controlsd')
        manager.prepare_managed_process('plannerd')
        manager.prepare_managed_process('dmonitoringd')
示例#9
0
def setup_env(simulation=False):
    params = Params()
    params.clear_all()
    params.put_bool("OpenpilotEnabledToggle", True)
    params.put_bool("Passive", False)
    params.put_bool("DisengageOnAccelerator", True)
    params.put_bool("EnableWideCamera", False)
    params.put_bool("DisableLogging", False)

    os.environ["NO_RADAR_SLEEP"] = "1"
    os.environ["REPLAY"] = "1"

    if simulation:
        os.environ["SIMULATION"] = "1"
    elif "SIMULATION" in os.environ:
        del os.environ["SIMULATION"]
示例#10
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
示例#11
0
    def setUpClass(cls):
        if cls.car_model not in ROUTES:
            # TODO: get routes for missing cars and remove this
            if cls.car_model in non_tested_cars:
                print(f"Skipping tests for {cls.car_model}: missing route")
                raise unittest.SkipTest
            else:
                raise Exception(f"missing test route for car {cls.car_model}")

        params = Params()
        params.clear_all()

        for seg in [2, 1, 0]:
            try:
                lr = LogReader(get_url(ROUTES[cls.car_model], seg))
            except Exception:
                continue

            can_msgs = []
            fingerprint = {i: dict() for i in range(3)}
            for msg in lr:
                if msg.which() == "can":
                    for m in msg.can:
                        if m.src < 64:
                            fingerprint[m.src][m.address] = len(m.dat)
                    can_msgs.append(msg)
                elif msg.which() == "carParams":
                    if msg.carParams.openpilotLongitudinalControl:
                        params.put_bool("DisableRadar", True)

            if len(can_msgs):
                break
        else:
            raise Exception(
                "Route not found or no CAN msgs found. Is it uploaded?")

        cls.can_msgs = sorted(can_msgs, key=lambda msg: msg.logMonoTime)

        cls.CarInterface, cls.CarController, cls.CarState = interfaces[
            cls.car_model]
        cls.CP = cls.CarInterface.get_params(cls.car_model, fingerprint, [])
        assert cls.CP
示例#12
0
    def test_startup_alert(self, expected_event, car, toggle_enabled):

        # TODO: this should be done without any real sockets
        controls_sock = messaging.sub_sock("controlsState")
        pm = messaging.PubMaster(['can', 'health'])

        params = Params()
        params.clear_all()
        params.put("Passive", b"0")
        params.put("OpenpilotEnabledToggle", b"1")
        params.put("CommunityFeaturesToggle", b"1" if toggle_enabled else b"0")

        time.sleep(2)  # wait for controlsd to be ready

        health = messaging.new_message('health')
        health.health.hwType = log.HealthData.HwType.uno
        pm.send('health', health)

        # fingerprint
        if car is None:
            finger = {addr: 1 for addr in range(1, 100)}
        else:
            finger = _FINGERPRINTS[car][0]

        for _ in range(500):
            msgs = [[addr, 0, b'\x00' * length, 0]
                    for addr, length in finger.items()]
            pm.send('can', can_list_to_can_capnp(msgs))

            time.sleep(0.01)
            msgs = messaging.drain_sock(controls_sock)
            if len(msgs):
                event_name = msgs[0].controlsState.alertType.split("/")[0]
                self.assertEqual(
                    EVENT_NAME[expected_event], event_name,
                    f"expected {EVENT_NAME[expected_event]} for '{car}', got {event_name}"
                )
                break
        else:
            self.fail(f"failed to fingerprint {car}")
示例#13
0
def python_replay_process(cfg, lr):
    sub_sockets = [s for _, sub in cfg.pub_sub.items() for s in sub]
    pub_sockets = [s for s in cfg.pub_sub.keys() if s != 'can']

    fsm = FakeSubMaster(pub_sockets)
    fpm = FakePubMaster(sub_sockets)
    args = (fsm, fpm)
    if 'can' in list(cfg.pub_sub.keys()):
        can_sock = FakeSocket()
        args = (fsm, fpm, can_sock)

    all_msgs = sorted(lr, key=lambda msg: msg.logMonoTime)
    pub_msgs = [
        msg for msg in all_msgs if msg.which() in list(cfg.pub_sub.keys())
    ]

    params = Params()
    params.clear_all()
    params.manager_start()
    params.put("OpenpilotEnabledToggle", "1")
    params.put("Passive", "0")
    params.put("CommunityFeaturesToggle", "1")

    os.environ['NO_RADAR_SLEEP'] = "1"
    os.environ['SKIP_FW_QUERY'] = "1"
    os.environ['FINGERPRINT'] = ""
    for msg in lr:
        if msg.which() == 'carParams':
            # TODO: get a stock VW route
            if "Generic Volkswagen" not in msg.carParams.carFingerprint:
                os.environ['FINGERPRINT'] = msg.carParams.carFingerprint
            break

    manager.prepare_managed_process(cfg.proc_name, build=True)
    mod = importlib.import_module(manager.managed_processes[cfg.proc_name])
    thread = threading.Thread(target=mod.main, args=args)
    thread.daemon = True
    thread.start()

    if cfg.init_callback is not None:
        if 'can' not in list(cfg.pub_sub.keys()):
            can_sock = None
        cfg.init_callback(all_msgs, fsm, can_sock)

    CP = car.CarParams.from_bytes(params.get("CarParams", block=True))

    # wait for started process to be ready
    if 'can' in list(cfg.pub_sub.keys()):
        can_sock.wait_for_recv()
    else:
        fsm.wait_for_update()

    log_msgs, msg_queue = [], []
    for msg in tqdm(pub_msgs):
        if cfg.should_recv_callback is not None:
            recv_socks, should_recv = cfg.should_recv_callback(
                msg, CP, cfg, fsm)
        else:
            recv_socks = [
                s for s in cfg.pub_sub[msg.which()]
                if (fsm.frame + 1) % int(service_list[msg.which()].frequency /
                                         service_list[s].frequency) == 0
            ]
            should_recv = bool(len(recv_socks))

        if msg.which() == 'can':
            can_sock.send(msg.as_builder().to_bytes())
        else:
            msg_queue.append(msg.as_builder())

        if should_recv:
            fsm.update_msgs(0, msg_queue)
            msg_queue = []

            recv_cnt = len(recv_socks)
            while recv_cnt > 0:
                m = fpm.wait_for_msg()
                log_msgs.append(m)

                recv_cnt -= m.which() in recv_socks
    return log_msgs
示例#14
0
def regen_segment(lr, frs=None, outdir=FAKEDATA):

  lr = list(lr)
  if frs is None:
    frs = dict()

  # setup env
  params = Params()
  params.clear_all()
  params.put_bool("Passive", False)
  params.put_bool("OpenpilotEnabledToggle", True)
  params.put_bool("CommunityFeaturesToggle", True)
  params.put_bool("CommunityFeaturesToggle", True)
  cal = messaging.new_message('liveCalibration')
  cal.liveCalibration.validBlocks = 20
  cal.liveCalibration.rpyCalib = [0.0, 0.0, 0.0]
  params.put("CalibrationParams", cal.to_bytes())

  os.environ["LOG_ROOT"] = outdir
  os.environ["SIMULATION"] = "1"

  os.environ['SKIP_FW_QUERY'] = ""
  os.environ['FINGERPRINT'] = ""
  for msg in lr:
    if msg.which() == 'carParams':
      car_fingerprint = msg.carParams.carFingerprint
      if len(msg.carParams.carFw) and (car_fingerprint in FW_VERSIONS):
        params.put("CarParamsCache", msg.carParams.as_builder().to_bytes())
      else:
        os.environ['SKIP_FW_QUERY'] = "1"
        os.environ['FINGERPRINT'] = car_fingerprint

  #TODO: init car, make sure starts engaged when segment is engaged

  fake_daemons = {
    'sensord': [
      multiprocessing.Process(target=replay_service, args=('sensorEvents', lr)),
    ],
    'pandad': [
      multiprocessing.Process(target=replay_service, args=('can', lr)),
      multiprocessing.Process(target=replay_service, args=('pandaStates', lr)),
    ],
    #'managerState': [
    #  multiprocessing.Process(target=replay_service, args=('managerState', lr)),
    #],
    'thermald': [
      multiprocessing.Process(target=replay_service, args=('deviceState', lr)),
    ],
    'camerad': [
      *replay_cameras(lr, frs),
    ],

    # TODO: fix these and run them
    'paramsd': [
      multiprocessing.Process(target=replay_service, args=('liveParameters', lr)),
    ],
    'locationd': [
      multiprocessing.Process(target=replay_service, args=('liveLocationKalman', lr)),
    ],
  }

  try:
    # start procs up
    ignore = list(fake_daemons.keys()) + ['ui', 'manage_athenad', 'uploader']
    ensure_running(managed_processes.values(), started=True, not_run=ignore)
    for procs in fake_daemons.values():
      for p in procs:
        p.start()

    for _ in tqdm(range(60)):
      # ensure all procs are running
      for d, procs in fake_daemons.items():
        for p in procs:
          if not p.is_alive():
            raise Exception(f"{d}'s {p.name} died")
      time.sleep(1)
  finally:
    # kill everything
    for p in managed_processes.values():
      p.stop()
    for procs in fake_daemons.values():
      for p in procs:
        p.terminate()

  r = params.get("CurrentRoute", encoding='utf-8')
  return os.path.join(outdir, r + "--0")
示例#15
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())
示例#16
0
def regen_segment(lr, frs=None, outdir=FAKEDATA):
    lr = list(lr)
    if frs is None:
        frs = dict()

    # setup env
    params = Params()
    params.clear_all()
    params.put_bool("Passive", False)
    params.put_bool("OpenpilotEnabledToggle", True)

    os.environ["LOG_ROOT"] = outdir
    os.environ["REPLAY"] = "1"

    os.environ['SKIP_FW_QUERY'] = ""
    os.environ['FINGERPRINT'] = ""

    # TODO: remove after getting new route for mazda
    migration = {
        "Mazda CX-9 2021": "MAZDA CX-9 2021",
    }

    for msg in lr:
        if msg.which() == 'carParams':
            car_fingerprint = migration.get(msg.carParams.carFingerprint,
                                            msg.carParams.carFingerprint)
            if len(msg.carParams.carFw) and (car_fingerprint in FW_VERSIONS):
                params.put("CarParamsCache",
                           msg.carParams.as_builder().to_bytes())
            else:
                os.environ['SKIP_FW_QUERY'] = "1"
                os.environ['FINGERPRINT'] = car_fingerprint
        elif msg.which() == 'liveCalibration':
            params.put("CalibrationParams", msg.as_builder().to_bytes())

    vs, cam_procs = replay_cameras(lr, frs)

    fake_daemons = {
        'sensord': [
            multiprocessing.Process(target=replay_sensor_events,
                                    args=('sensorEvents', lr)),
        ],
        'pandad': [
            multiprocessing.Process(target=replay_service, args=('can', lr)),
            multiprocessing.Process(target=replay_service,
                                    args=('ubloxRaw', lr)),
            multiprocessing.Process(target=replay_panda_states,
                                    args=('pandaStates', lr)),
        ],
        'managerState': [
            multiprocessing.Process(target=replay_manager_state,
                                    args=('managerState', lr)),
        ],
        'thermald': [
            multiprocessing.Process(target=replay_device_state,
                                    args=('deviceState', lr)),
        ],
        'camerad': [
            *cam_procs,
        ],
    }

    try:
        # start procs up
        ignore = list(
            fake_daemons.keys()) + ['ui', 'manage_athenad', 'uploader']
        ensure_running(managed_processes.values(),
                       started=True,
                       not_run=ignore)
        for procs in fake_daemons.values():
            for p in procs:
                p.start()

        for _ in tqdm(range(60)):
            # ensure all procs are running
            for d, procs in fake_daemons.items():
                for p in procs:
                    if not p.is_alive():
                        raise Exception(f"{d}'s {p.name} died")
            time.sleep(1)
    finally:
        # kill everything
        for p in managed_processes.values():
            p.stop()
        for procs in fake_daemons.values():
            for p in procs:
                p.terminate()

    del vs

    r = params.get("CurrentRoute", encoding='utf-8')
    return os.path.join(outdir, r + "--0")
示例#17
0
def manager_init():

    # update system time from panda
    set_time(cloudlog)

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

    default_params = [
        ("CompletedTrainingVersion", "0"),
        ("HasAcceptedTerms", "0"),
        ("HandsOnWheelMonitoring", "0"),
        ("OpenpilotEnabledToggle", "1"),
        ("ShowDebugUI", "1"),
        ("SpeedLimitControl", "1"),
        ("SpeedLimitPercOffset", "1"),
        ("TurnSpeedControl", "1"),
        ("TurnVisionControl", "1"),
        ("GMAutoHold", "1"),
        ("CruiseSpeedOffset", "1"),
        ("StockSpeedAdjust", "1"),
        ("CustomSounds", "0"),
        ("SilentEngageDisengage", "0"),
        ("AccelModeButton", "0"),
        ("AccelMode", "0"),
        ("EndToEndToggle", "1"),
        ("LanelessMode", "2"),
        ("NudgelessLaneChange", "0"),
        ("Coasting", "0"),
        ("RegenBraking", "0"),
        ("OnePedalMode", "0"),
        ("OnePedalModeSimple", "0"),
        ("OnePedalModeEngageOnGas", "0"),
        ("OnePedalBrakeMode", "0"),
        ("OnePedalPauseBlinkerSteering", "1"),
        ("FollowLevel", "2"),
        ("CoastingBrakeOverSpeed", "0"),
        ("FrictionBrakePercent", "0"),
        ("BrakeIndicator", "1"),
        ("DisableOnroadUploads", "0"),
        ("MeasureNumSlots", "0"),
        ("MeasureSlot00", "12"),  # right column: percent grade
        ("MeasureSlot01", "10"),  # altitude
        ("MeasureSlot02", "2"),  # steering torque
        ("MeasureSlot03", "3"),  # engine rpm
        ("MeasureSlot04", "7"),  # engine coolant temperature
        ("MeasureSlot05", "16"),  # lead dist [s]
        ("MeasureSlot06", "15"),  # lead dist [m]
        ("MeasureSlot07", "20"),  # lead rel spd [mph]
        ("MeasureSlot08", "21"),  # lead spd [mph]
        ("MeasureSlot09", "23"),  # device cpu percent and temp
    ]
    if not PC:
        default_params.append(
            ("LastUpdateTime",
             datetime.datetime.utcnow().isoformat().encode('utf8')))

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

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

    # parameters set by Enviroment Varables
    if os.getenv("HANDSMONITORING") is not None:
        params.put_bool("HandsOnWheelMonitoring",
                        bool(int(os.getenv("HANDSMONITORING"))))

    # 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())
示例#18
0
文件: manager.py 项目: Donfyffe/op4
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())
示例#19
0
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)
示例#20
0
def manager_init():

    # update system time from panda
    set_time(cloudlog)

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

    default_params = [
        ("CompletedTrainingVersion", "0"),
        ("HasAcceptedTerms", "0"),
        ("LastUpdateTime",
         datetime.datetime.utcnow().isoformat().encode('utf8')),
        ("OpenpilotEnabledToggle", "1"),
        ("IsOpenpilotViewEnabled", "0"),
        ("OpkrAutoShutdown", "2"),
        ("OpkrAutoScreenOff", "0"),
        ("OpkrUIBrightness", "0"),
        ("OpkrUIBrightness", "0"),
        ("OpkrUIVolumeBoost", "0"),
        ("OpkrEnableDriverMonitoring", "1"),
        ("OpkrEnableLogger", "0"),
        ("OpkrEnableUploader", "0"),
        ("OpkrEnableGetoffAlert", "1"),
        ("OpkrAutoResume", "1"),
        ("OpkrVariableCruise", "1"),
        ("OpkrLaneChangeSpeed", "45"),
        ("OpkrAutoLaneChangeDelay", "0"),
        ("OpkrSteerAngleCorrection", "0"),
        ("PutPrebuiltOn", "0"),
        ("LdwsCarFix", "0"),
        ("LateralControlMethod", "0"),
        ("CruiseStatemodeSelInit", "1"),
        ("InnerLoopGain", "35"),
        ("OuterLoopGain", "20"),
        ("TimeConstant", "14"),
        ("ActuatorEffectiveness", "20"),
        ("Scale", "1500"),
        ("LqrKi", "15"),
        ("DcGain", "27"),
        ("IgnoreZone", "1"),
        ("PidKp", "20"),
        ("PidKi", "40"),
        ("PidKd", "150"),
        ("PidKf", "5"),
        ("CameraOffsetAdj", "60"),
        ("SteerRatioAdj", "155"),
        ("SteerRatioMaxAdj", "175"),
        ("SteerActuatorDelayAdj", "15"),
        ("SteerRateCostAdj", "35"),
        ("SteerLimitTimerAdj", "40"),
        ("TireStiffnessFactorAdj", "85"),
        ("SteerMaxBaseAdj", "300"),
        ("SteerMaxAdj", "384"),
        ("SteerDeltaUpBaseAdj", "3"),
        ("SteerDeltaUpAdj", "3"),
        ("SteerDeltaDownBaseAdj", "7"),
        ("SteerDeltaDownAdj", "7"),
        ("SteerMaxvAdj", "13"),
        ("OpkrBatteryChargingControl", "1"),
        ("OpkrBatteryChargingMin", "70"),
        ("OpkrBatteryChargingMax", "80"),
        ("LeftCurvOffsetAdj", "0"),
        ("RightCurvOffsetAdj", "0"),
        ("DebugUi1", "0"),
        ("DebugUi2", "0"),
        ("LongLogDisplay", "0"),
        ("OpkrBlindSpotDetect", "1"),
        ("OpkrMaxAngleLimit", "90"),
        ("OpkrSpeedLimitOffset", "0"),
        ("LimitSetSpeedCamera", "0"),
        ("LimitSetSpeedCameraDist", "0"),
        ("OpkrLiveSteerRatio", "1"),
        ("OpkrVariableSteerMax", "1"),
        ("OpkrVariableSteerDelta", "0"),
        ("FingerprintTwoSet", "1"),
        ("OpkrVariableCruiseProfile", "0"),
        ("OpkrLiveTune", "0"),
        ("OpkrDrivingRecord", "0"),
        ("OpkrTurnSteeringDisable", "0"),
        ("CarModel", ""),
        ("CarModelAbb", ""),
        ("OpkrHotspotOnBoot", "0"),
        ("OpkrSSHLegacy", "1"),
        ("ShaneFeedForward", "0"),
        ("CruiseOverMaxSpeed", "0"),
        ("JustDoGearD", "0"),
        ("LanelessMode", "0"),
        ("ComIssueGone", "0"),
        ("MaxSteer", "384"),
        ("MaxRTDelta", "112"),
        ("MaxRateUp", "3"),
        ("MaxRateDown", "7"),
        ("SteerThreshold", "150"),
        ("RecordingCount", "100"),
        ("RecordingQuality", "1"),
        ("CruiseGapAdjust", "0"),
        ("AutoEnable", "1"),
        ("CruiseAutoRes", "0"),
        ("AutoResOption", "0"),
        ("SteerWindDown", "0"),
        ("OpkrMonitoringMode", "0"),
        ("OpkrMonitorEyesThreshold", "75"),
        ("OpkrMonitorNormalEyesThreshold", "50"),
        ("OpkrMonitorBlinkThreshold", "50"),
        ("MadModeEnabled", "1"),
        ("OpkrFanSpeedGain", "0"),
        ("WhitePandaSupport", "0"),
        ("SteerWarningFix", "0"),
    ]

    if TICI:
        default_params.append(("IsUploadRawEnabled", "0"))

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

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

    if EON:
        update_apks()

    os.umask(0)  # Make sure we can create files with 777 permissions

    # 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
    elif not reg_res:
        dongle_id = ""
    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())

    # ensure shared libraries are readable by apks
    if EON:
        os.chmod(BASEDIR, 0o755)
        os.chmod("/dev/shm", 0o777)
        os.chmod(os.path.join(BASEDIR, "cereal"), 0o755)

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

    os.system("/data/openpilot/gitcommit.sh")
示例#21
0
def python_replay_process(cfg, lr, fingerprint=None):
    sub_sockets = [s for _, sub in cfg.pub_sub.items() for s in sub]
    pub_sockets = [s for s in cfg.pub_sub.keys() if s != 'can']

    fsm = FakeSubMaster(pub_sockets)
    fpm = FakePubMaster(sub_sockets)
    args = (fsm, fpm)
    if 'can' in list(cfg.pub_sub.keys()):
        can_sock = FakeSocket()
        args = (fsm, fpm, can_sock)

    all_msgs = sorted(lr, key=lambda msg: msg.logMonoTime)
    pub_msgs = [
        msg for msg in all_msgs if msg.which() in list(cfg.pub_sub.keys())
    ]

    params = Params()
    params.clear_all()
    params.put_bool("OpenpilotEnabledToggle", True)
    params.put_bool("Passive", False)
    params.put_bool("CommunityFeaturesToggle", True)

    os.environ['NO_RADAR_SLEEP'] = "1"
    os.environ["SIMULATION"] = "1"

    # TODO: remove after getting new route for civic & accord
    migration = {
        "HONDA CIVIC 2016 TOURING": "HONDA CIVIC 2016",
        "HONDA ACCORD 2018 SPORT 2T": "HONDA ACCORD 2018",
        "HONDA ACCORD 2T 2018": "HONDA ACCORD 2018",
    }

    if fingerprint is not None:
        os.environ['SKIP_FW_QUERY'] = "1"
        os.environ['FINGERPRINT'] = fingerprint
    else:
        os.environ['SKIP_FW_QUERY'] = ""
        os.environ['FINGERPRINT'] = ""
        for msg in lr:
            if msg.which() == 'carParams':
                car_fingerprint = migration.get(msg.carParams.carFingerprint,
                                                msg.carParams.carFingerprint)
                if len(msg.carParams.carFw) and (car_fingerprint
                                                 in FW_VERSIONS):
                    params.put("CarParamsCache",
                               msg.carParams.as_builder().to_bytes())
                else:
                    os.environ['SKIP_FW_QUERY'] = "1"
                    os.environ['FINGERPRINT'] = car_fingerprint

    assert (type(managed_processes[cfg.proc_name]) is PythonProcess)
    managed_processes[cfg.proc_name].prepare()
    mod = importlib.import_module(managed_processes[cfg.proc_name].module)

    thread = threading.Thread(target=mod.main, args=args)
    thread.daemon = True
    thread.start()

    if cfg.init_callback is not None:
        if 'can' not in list(cfg.pub_sub.keys()):
            can_sock = None
        cfg.init_callback(all_msgs, fsm, can_sock, fingerprint)

    CP = car.CarParams.from_bytes(params.get("CarParams", block=True))

    # wait for started process to be ready
    if 'can' in list(cfg.pub_sub.keys()):
        can_sock.wait_for_recv()
    else:
        fsm.wait_for_update()

    log_msgs, msg_queue = [], []
    for msg in tqdm(pub_msgs, disable=CI):
        if cfg.should_recv_callback is not None:
            recv_socks, should_recv = cfg.should_recv_callback(
                msg, CP, cfg, fsm)
        else:
            recv_socks = [
                s for s in cfg.pub_sub[msg.which()]
                if (fsm.frame + 1) % int(service_list[msg.which()].frequency /
                                         service_list[s].frequency) == 0
            ]
            should_recv = bool(len(recv_socks))

        if msg.which() == 'can':
            can_sock.send(msg.as_builder().to_bytes())
        else:
            msg_queue.append(msg.as_builder())

        if should_recv:
            fsm.update_msgs(0, msg_queue)
            msg_queue = []

            recv_cnt = len(recv_socks)
            while recv_cnt > 0:
                m = fpm.wait_for_msg().as_builder()
                m.logMonoTime = msg.logMonoTime
                m = m.as_reader()

                log_msgs.append(m)
                recv_cnt -= m.which() in recv_socks
    return log_msgs
示例#22
0
class TestRegistration(unittest.TestCase):
    def setUp(self):
        # clear params and setup key paths
        self.params = Params()
        self.params.clear_all()

        self.persist = tempfile.TemporaryDirectory()
        os.mkdir(os.path.join(self.persist.name, "comma"))
        self.priv_key = Path(os.path.join(self.persist.name, "comma/id_rsa"))
        self.pub_key = Path(os.path.join(self.persist.name,
                                         "comma/id_rsa.pub"))
        self.persist_patcher = mock.patch(
            "selfdrive.athena.registration.PERSIST", self.persist.name)
        self.persist_patcher.start()

    def tearDown(self):
        self.persist_patcher.stop()
        self.persist.cleanup()

    def _generate_keys(self):
        self.pub_key.touch()
        k = RSA.generate(2048)
        with open(self.priv_key, "wb") as f:
            f.write(k.export_key())
        with open(self.pub_key, "wb") as f:
            f.write(k.publickey().export_key())

    def test_valid_cache(self):
        # if all params are written, return the cached dongle id
        self.params.put("IMEI", "imei")
        self.params.put("HardwareSerial", "serial")
        self._generate_keys()

        with mock.patch("selfdrive.athena.registration.api_get",
                        autospec=True) as m:
            dongle = "DONGLE_ID_123"
            self.params.put("DongleId", dongle)
            self.assertEqual(register(), dongle)
            self.assertFalse(m.called)

    def test_missing_cache(self):
        # keys exist but no dongle id
        self._generate_keys()
        with mock.patch("selfdrive.athena.registration.api_get",
                        autospec=True) as m:
            dongle = "DONGLE_ID_123"
            m.return_value = MockResponse(json.dumps({'dongle_id': dongle}),
                                          200)
            self.assertEqual(register(), dongle)
            self.assertEqual(m.call_count, 1)

            # call again, shouldn't hit the API this time
            self.assertEqual(register(), dongle)
            self.assertEqual(m.call_count, 1)
        self.assertEqual(self.params.get("DongleId", encoding='utf-8'), dongle)

    def test_unregistered_pc(self):
        # no keys, no dongle id
        with mock.patch("selfdrive.athena.registration.api_get", autospec=True) as m, \
             mock.patch("selfdrive.athena.registration.PC", new=True):
            m.return_value = MockResponse(None, 402)
            dongle = register()
            self.assertGreater(len(dongle), 0)
            self.assertEqual(m.call_count, 1)
        self.assertEqual(self.params.get("DongleId", encoding='utf-8'), dongle)

    def test_unregistered_non_pc(self):
        # no keys, no dongle id
        with mock.patch("selfdrive.athena.registration.api_get", autospec=True) as m, \
             mock.patch("selfdrive.athena.registration.PC", new=False):
            m.return_value = MockResponse(None, 402)
            self.assertIs(register(), None)
            self.assertEqual(m.call_count, 1)
示例#23
0
def manager_init():

    # update system time from panda
    set_time(cloudlog)

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

    default_params = [
        ("OpenpilotEnabledToggle", "1"),
        ("CommunityFeaturesToggle", "1"),
        ("IsMetric", "1"),

        # HKG
        ("UseClusterSpeed", "1"),
        ("LongControlEnabled", "0"),
        ("MadModeEnabled", "1"),
        ("IsLdwsCar", "0"),
        ("LaneChangeEnabled", "0"),
        ("AutoLaneChangeEnabled", "0"),
        ("SccSmootherSlowOnCurves", "0"),
        ("SccSmootherSyncGasPressed", "0"),
        ("StockNaviDecelEnabled", "0"),
        ("ShowDebugUI", "0"),
        ("CustomLeadMark", "0")
    ]
    if not PC:
        default_params.append(
            ("LastUpdateTime",
             datetime.datetime.utcnow().isoformat().encode('utf8')))

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

    # 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())
示例#24
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
    network_info = None

    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_files = ["/sys/kernel/debug/regulator/pm8994_s11/voltage"
                     ] + cpr_files
        cpr_data = {}
        for cf in cpr_files:
            with open(cf, "r") as f:
                try:
                    cpr_data[str(cf)] = f.read().strip()
                except Exception:
                    pass
        cloudlog.event("CPR", data=cpr_data)

    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.clear_all(ParamKeyType.CLEAR_ON_PANDA_DISCONNECT)
            pandaState_prev = pandaState

        # get_network_type is an expensive call. update every 10s
        if (count % int(10. / DT_TRML)) == 0:
            try:
                network_type = HARDWARE.get_network_type()
                network_strength = HARDWARE.get_network_strength(network_type)
                network_info = HARDWARE.get_network_info()  # pylint: disable=assignment-from-none
            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
        if network_info is not None:
            msg.deviceState.networkInfo = network_info

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

        # Fake battery levels on uno for frame
        if (not EON) or is_uno:
            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_bool(
                "DisableUpdates")
        startup_conditions["not_uninstalling"] = not params.get_bool(
            "DoUninstall")
        startup_conditions["accepted_terms"] = params.get(
            "HasAcceptedTerms") == terms_version

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

        # with 2% left, we killall, otherwise the phone will take a long time to boot
        startup_conditions["free_space"] = msg.deviceState.freeSpacePercent > 2
        startup_conditions["completed_training"] = params.get("CompletedTrainingVersion") == training_version or \
                                                   (current_branch in ['dashcam', 'dashcam-staging'])
        startup_conditions["not_driver_view"] = not params.get_bool(
            "IsDriverViewEnabled")
        startup_conditions["not_taking_snapshot"] = not params.get_bool(
            "IsTakingSnapshot")
        # if any CPU gets above 107 or the battery gets above 63, kill all processes
        # controls will warn with CPU above 95 or battery above 60
        startup_conditions[
            "device_temp_good"] = thermal_status < ThermalStatus.danger
        set_offroad_alert_if_changed(
            "Offroad_TemperatureTooHigh",
            (not startup_conditions["device_temp_good"]))

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

        # Handle offroad/onroad transition
        should_start = all(startup_conditions.values())
        if should_start != should_start_prev or (count == 0):
            params.put_bool("IsOffroad", not should_start)
            HARDWARE.set_power_save(not should_start)
            if TICI and not params.get_bool("EnableLteOnroad"):
                fxn = "off" if should_start else "on"
                os.system(f"nmcli radio wwan {fxn}")

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

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

        # Offroad power monitoring
        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:
            if EON and started_ts is None and msg.deviceState.memoryUsagePercent > 40:
                cloudlog.event("High offroad memory usage",
                               mem=msg.deviceState.memoryUsagePercent)

            location = messaging.recv_sock(location_sock)
            cloudlog.event("STATUS_PACKET",
                           count=count,
                           pandaState=(strip_deprecated_keys(
                               pandaState.to_dict()) if pandaState else None),
                           location=(strip_deprecated_keys(
                               location.gpsLocationExternal.to_dict())
                                     if location else None),
                           deviceState=strip_deprecated_keys(msg.to_dict()))

        count += 1
示例#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 = [
        ("CommunityFeaturesToggle", "1"),
        ("jvePilot.carState.accEco", "1"),
        ("jvePilot.settings.accEco.speedAheadLevel1", "7"),
        ("jvePilot.settings.accEco.speedAheadLevel2", "5"),
        ("jvePilot.settings.autoFollow", "1"),
        ("jvePilot.settings.autoFollow.speed1-2Bars", "15"),
        ("jvePilot.settings.autoFollow.speed2-3Bars", "30"),
        ("jvePilot.settings.autoFollow.speed3-4Bars", "65"),
        ("jvePilot.settings.autoResume", "1"),
        ("jvePilot.settings.disableOnGas", "0"),
        ("jvePilot.settings.audioAlertOnSteeringLoss", "1"),
        ("jvePilot.settings.deviceOffset", "0.00"),
        ("jvePilot.settings.reverseAccSpeedChange", "1"),
        ("jvePilot.settings.slowInCurves", "1"),
        ("jvePilot.settings.slowInCurves.speedRatio", "1.0"),
        ("jvePilot.settings.slowInCurves.speedDropOff", "2.0"),
        ("jvePilot.settings.slowInCurves.speedDropOffAngle", "0.0"),
        ("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", 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 get_dirty():
        os.environ['CLEAN'] = '1'

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

    if get_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=get_dirty(),
                     origin=get_origin(),
                     branch=get_short_branch(),
                     commit=get_commit(),
                     device=HARDWARE.get_device_type())
示例#26
0
def thermald_thread() -> NoReturn:

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

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

  fan_speed = 0
  count = 0

  onroad_conditions: Dict[str, bool] = {
    "ignition": False,
  }
  startup_conditions: Dict[str, bool] = {}
  startup_conditions_prev: Dict[str, bool] = {}

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

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

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

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

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

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

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

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

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

    msg = read_thermal(thermal_config)

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

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

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

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

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

      # Handle disconnect
      if pandaState_prev is not None:
        if pandaState.pandaType == log.PandaState.PandaType.unknown and \
          pandaState_prev.pandaType != log.PandaState.PandaType.unknown:
          params.clear_all(ParamKeyType.CLEAR_ON_PANDA_DISCONNECT)
      pandaState_prev = pandaState

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

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

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

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

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

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

    msg.deviceState.screenBrightnessPercent = HARDWARE.get_screen_brightness()
    msg.deviceState.batteryPercent = HARDWARE.get_battery_capacity()
    msg.deviceState.batteryCurrent = HARDWARE.get_battery_current()
    msg.deviceState.usbOnline = HARDWARE.get_usb_present()
    current_filter.update(msg.deviceState.batteryCurrent / 1e6)

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

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

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

    # **** starting logic ****

    # Ensure date/time are valid
    now = datetime.datetime.utcnow()
    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"]))

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

    # with 2% left, we killall, otherwise the phone will take a long time to boot
    startup_conditions["free_space"] = msg.deviceState.freeSpacePercent > 2
    startup_conditions["completed_training"] = params.get("CompletedTrainingVersion") == training_version or \
                                               params.get_bool("Passive")
    startup_conditions["not_driver_view"] = not params.get_bool("IsDriverViewEnabled")
    startup_conditions["not_taking_snapshot"] = not params.get_bool("IsTakingSnapshot")
    # if any CPU gets above 107 or the battery gets above 63, kill all processes
    # controls will warn with CPU above 95 or battery above 60
    onroad_conditions["device_temp_good"] = thermal_status < ThermalStatus.danger
    set_offroad_alert_if_changed("Offroad_TemperatureTooHigh", (not onroad_conditions["device_temp_good"]))

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

    # Handle offroad/onroad transition
    should_start = all(onroad_conditions.values())
    if started_ts is None:
      should_start = should_start and all(startup_conditions.values())

    if should_start != should_start_prev or (count == 0):
      params.put_bool("IsOnroad", should_start)
      params.put_bool("IsOffroad", not should_start)
      HARDWARE.set_power_save(not should_start)

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

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

    # Offroad power monitoring
    power_monitor.calculate(peripheralState, onroad_conditions["ignition"])
    msg.deviceState.offroadPowerUsageUwh = power_monitor.get_power_used()
    msg.deviceState.carBatteryCapacityUwh = max(0, power_monitor.get_car_battery_capacity())
    current_power_draw = HARDWARE.get_current_power_draw()  # pylint: disable=assignment-from-none
    msg.deviceState.powerDrawW = current_power_draw if current_power_draw is not None else 0

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

    # Check if we need to shut down
    if power_monitor.should_shutdown(peripheralState, onroad_conditions["ignition"], in_car, 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
    ui_running = "ui" in (p.name for p in sm["managerState"].processes if p.running)
    if ui_running_prev and not ui_running:
      HARDWARE.set_screen_brightness(20)
    ui_running_prev = ui_running

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

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

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

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

    should_start_prev = should_start
    startup_conditions_prev = startup_conditions.copy()

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

      cloudlog.event("STATUS_PACKET",
                     count=count,
                     pandaStates=(strip_deprecated_keys(pandaStates.to_dict()) if pandaStates else None),
                     peripheralState=strip_deprecated_keys(peripheralState.to_dict()),
                     location=(strip_deprecated_keys(sm["gpsLocationExternal"].to_dict()) if sm.alive["gpsLocationExternal"] else None),
                     deviceState=strip_deprecated_keys(msg.to_dict()))

    count += 1
示例#27
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"
示例#28
0
            # TODO: skip these for now, but make sure any new ports get routes
            if car_model not in non_tested_cars:
                print("TEST FAILED: Missing route for car '%s'" % car_model)
                sys.exit(1)

    print("Preparing processes")
    for p in tested_procs:
        manager.prepare_managed_process(p)

    results = {}
    for route, checks in routes.items():
        get_route_log(route)

        params = Params()
        params.clear_all()
        params.manager_start()
        params.put("OpenpilotEnabledToggle", "1")
        params.put("CommunityFeaturesToggle", "1")
        params.put("Passive", "1" if route in passive_routes else "0")

        os.environ['SKIP_FW_QUERY'] = "1"
        if checks.get('fingerprintSource', None) == 'fixed':
            os.environ['FINGERPRINT'] = cast(str, checks['carFingerprint'])
        else:
            os.environ['FINGERPRINT'] = ""

        print("testing ", route, " ", checks['carFingerprint'])
        print("Starting processes")
        for p in tested_procs:
            manager.start_managed_process(p)
示例#29
0
def thermald_thread():

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

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

  fan_speed = 0
  count = 0

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

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

  network_type = NetworkType.none
  network_strength = NetworkStrength.unknown
  network_info = None
  modem_version = None
  registered_count = 0
  wifiIpAddress = "N/A"
  wifi_ssid = "---"

  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

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

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

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

  # sound trigger
  sound_trigger = 1
  opkrAutoShutdown = 0

  shutdown_trigger = 1
  is_openpilot_view_enabled = 0

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

  getoff_alert = params.get_bool("OpkrEnableGetoffAlert")

  hotspot_on_boot = params.get_bool("OpkrHotspotOnBoot")
  hotspot_run = False

  if int(params.get("OpkrAutoShutdown", encoding="utf8")) == 0:
    opkrAutoShutdown = 0
  elif int(params.get("OpkrAutoShutdown", encoding="utf8")) == 1:
    opkrAutoShutdown = 5
  elif int(params.get("OpkrAutoShutdown", encoding="utf8")) == 2:
    opkrAutoShutdown = 30
  elif int(params.get("OpkrAutoShutdown", encoding="utf8")) == 3:
    opkrAutoShutdown = 60
  elif int(params.get("OpkrAutoShutdown", encoding="utf8")) == 4:
    opkrAutoShutdown = 180
  elif int(params.get("OpkrAutoShutdown", encoding="utf8")) == 5:
    opkrAutoShutdown = 300
  elif int(params.get("OpkrAutoShutdown", encoding="utf8")) == 6:
    opkrAutoShutdown = 600
  elif int(params.get("OpkrAutoShutdown", encoding="utf8")) == 7:
    opkrAutoShutdown = 1800
  elif int(params.get("OpkrAutoShutdown", encoding="utf8")) == 8:
    opkrAutoShutdown = 3600
  elif int(params.get("OpkrAutoShutdown", encoding="utf8")) == 9:
    opkrAutoShutdown = 10800
  else:
    opkrAutoShutdown = 18000

  battery_charging_control = params.get_bool("OpkrBatteryChargingControl")
  battery_charging_min = int(params.get("OpkrBatteryChargingMin", encoding="utf8"))
  battery_charging_max = int(params.get("OpkrBatteryChargingMax", encoding="utf8"))

  is_openpilot_dir = True

  while 1:
    ts = sec_since_boot()
    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
          shutdown_trigger = 1
      else:
        no_panda_cnt = 0
        startup_conditions["ignition"] = pandaState.pandaState.ignitionLine or pandaState.pandaState.ignitionCan
        sound_trigger == 1
      #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.clear_all(ParamKeyType.CLEAR_ON_PANDA_DISCONNECT)
      pandaState_prev = pandaState
    elif params.get_bool("IsOpenpilotViewEnabled") and not params.get_bool("IsDriverViewEnabled") and is_openpilot_view_enabled == 0:
      is_openpilot_view_enabled = 1
      startup_conditions["ignition"] = True
    elif not params.get_bool("IsOpenpilotViewEnabled") and not params.get_bool("IsDriverViewEnabled") and is_openpilot_view_enabled == 1:
      shutdown_trigger = 0
      sound_trigger == 0
      is_openpilot_view_enabled = 0
      startup_conditions["ignition"] = False

    # 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, wifi_ssid = HARDWARE.get_network_strength(network_type)
        network_info = HARDWARE.get_network_info()  # pylint: disable=assignment-from-none

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

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

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

        wifiIpAddress = HARDWARE.get_ip_address()
        try:
          ping_test = subprocess.check_output(["ping", "-c", "1", "-W", "1", "google.com"])
          Params().put("LastAthenaPingTime", str(int(sec_since_boot() * 1e9))) if ping_test else False
        except Exception:
          Params().delete("LastAthenaPingTime")
      except Exception:
        cloudlog.exception("Error getting network status")

    msg.deviceState.freeSpacePercent = get_available_percent(default=100.0)
    msg.deviceState.memoryUsagePercent = int(round(psutil.virtual_memory().percent))
    msg.deviceState.cpuUsagePercent = int(round(psutil.cpu_percent()))
    msg.deviceState.gpuUsagePercent = int(round(HARDWARE.get_gpu_usage_percent()))
    msg.deviceState.networkType = network_type
    msg.deviceState.networkStrength = network_strength
    msg.deviceState.wifiSSID = wifi_ssid
    if network_info is not None:
      msg.deviceState.networkInfo = network_info
    msg.deviceState.wifiIpAddress = wifiIpAddress
    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"] = True if ((now.year > 2020) or (now.year == 2020 and now.month >= 10)) else True # set True for battery less EON otherwise, set False.
    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_bool("DisableUpdates")
    startup_conditions["not_uninstalling"] = not params.get_bool("DoUninstall")
    startup_conditions["accepted_terms"] = params.get("HasAcceptedTerms") == terms_version

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

    # with 2% left, we killall, otherwise the phone will take a long time to boot
    startup_conditions["free_space"] = msg.deviceState.freeSpacePercent > 2
    startup_conditions["completed_training"] = params.get("CompletedTrainingVersion") == training_version or \
                                               params.get_bool("Passive")
    startup_conditions["not_driver_view"] = not params.get_bool("IsDriverViewEnabled")
    startup_conditions["not_taking_snapshot"] = not params.get_bool("IsTakingSnapshot")
    # if any CPU gets above 107 or the battery gets above 63, kill all processes
    # controls will warn with CPU above 95 or battery above 60
    startup_conditions["device_temp_good"] = thermal_status < ThermalStatus.danger
    set_offroad_alert_if_changed("Offroad_TemperatureTooHigh", (not startup_conditions["device_temp_good"]))

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

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

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

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

      if shutdown_trigger == 1 and sound_trigger == 1 and msg.deviceState.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 shutdown_trigger == 1 and msg.deviceState.batteryStatus == "Discharging" and \
         started_seen and opkrAutoShutdown and (sec_since_boot() - off_ts) > opkrAutoShutdown and not os.path.isfile(pandaflash_ongoing):
        os.system('LD_LIBRARY_PATH="" svc power shutdown')

      if (count % int(1. / DT_TRML)) == 0:
        if int(params.get("OpkrForceShutdown", encoding="utf8")) != 0 and not started_seen and msg.deviceState.batteryStatus == "Discharging":
          shutdown_option = int(params.get("OpkrForceShutdown", encoding="utf8"))
          if shutdown_option == 1:
            opkrForceShutdown = 60
          elif shutdown_option == 2:
            opkrForceShutdown = 180
          elif shutdown_option == 3:
            opkrForceShutdown = 300
          elif shutdown_option == 4:
            opkrForceShutdown = 600
          else:
            opkrForceShutdown = 1800
          if (sec_since_boot() - off_ts) > opkrForceShutdown and params.get_bool("OpkrForceShutdownTrigger"):
            os.system('LD_LIBRARY_PATH="" svc power shutdown')
          elif not params.get_bool("OpkrForceShutdownTrigger"):
            off_ts = sec_since_boot()


    # opkr
    prebuiltlet = params.get_bool("PutPrebuiltOn")
    if not os.path.isdir("/data/openpilot"):
      if is_openpilot_dir:
        os.system("cd /data/params/d; rm -f DongleId") # Delete DongleID if the Openpilot directory disappears, Seems you want to switch fork/branch.
      is_openpilot_dir = False
    elif not os.path.isfile(prebuiltfile) and prebuiltlet and is_openpilot_dir:
      os.system("cd /data/openpilot; touch prebuilt")
    elif os.path.isfile(prebuiltfile) and not prebuiltlet:
      os.system("cd /data/openpilot; rm -f prebuilt")

    # opkr
    sshkeylet = params.get_bool("OpkrSSHLegacy")
    if not os.path.isfile(sshkeyfile) and sshkeylet:
      os.system("cp -f /data/openpilot/selfdrive/assets/addon/key/GithubSshKeys_legacy /data/params/d/GithubSshKeys; chmod 600 /data/params/d/GithubSshKeys; touch /data/public_key")
    elif os.path.isfile(sshkeyfile) and not sshkeylet:
      os.system("cp -f /data/openpilot/selfdrive/assets/addon/key/GithubSshKeys_new /data/params/d/GithubSshKeys; chmod 600 /data/params/d/GithubSshKeys; rm -f /data/public_key")

    # opkr hotspot
    if hotspot_on_boot and not hotspot_run and sec_since_boot() > 80:
      os.system("service call wifi 37 i32 0 i32 1 &")
      hotspot_run = True

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

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

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

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

    should_start_prev = should_start
    startup_conditions_prev = startup_conditions.copy()

    # atom
    if usb_power and battery_charging_control:
      power_monitor.charging_ctrl( msg, ts, battery_charging_max, battery_charging_min )    

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

      location = messaging.recv_sock(location_sock)
      cloudlog.event("STATUS_PACKET",
                     count=count,
                     pandaState=(strip_deprecated_keys(pandaState.to_dict()) if pandaState else None),
                     location=(strip_deprecated_keys(location.gpsLocationExternal.to_dict()) if location else None),
                     deviceState=strip_deprecated_keys(msg.to_dict()))

    count += 1