def thermald_thread(): pm = messaging.PubMaster(['deviceState']) pandaState_timeout = int(1000 * 2.5 * DT_TRML) # 2.5x the expected pandaState frequency pandaState_sock = messaging.sub_sock('pandaStates', timeout=pandaState_timeout) sm = messaging.SubMaster( ["peripheralState", "gpsLocationExternal", "managerState"]) fan_speed = 0 count = 0 startup_conditions = { "ignition": False, } startup_conditions_prev = startup_conditions.copy() off_ts = None started_ts = None started_seen = False thermal_status = ThermalStatus.green usb_power = True network_type = NetworkType.none network_strength = NetworkStrength.unknown network_info = None modem_version = None registered_count = 0 nvme_temps = None modem_temps = None current_filter = FirstOrderFilter(0., CURRENT_TAU, DT_TRML) temp_filter = FirstOrderFilter(0., TEMP_TAU, DT_TRML) pandaState_prev = None should_start_prev = False in_car = False handle_fan = None is_uno = False ui_running_prev = False 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 startup_conditions["ignition"]: cloudlog.error("Lost panda connection while onroad") startup_conditions["ignition"] = False else: no_panda_cnt = 0 startup_conditions[ "ignition"] = pandaState.ignitionLine or pandaState.ignitionCan in_car = pandaState.harnessStatus != log.PandaState.HarnessStatus.notConnected usb_power = peripheralState.usbPowerMode != log.PeripheralState.UsbPowerMode.client # Setup fan handler on first connect to panda if 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, startup_conditions["ignition"]) msg.deviceState.fanSpeedPercentDesired = fan_speed is_offroad_for_5_min = (started_ts is None) and ( (not started_seen) or (off_ts is None) or (sec_since_boot() - off_ts > 60 * 5)) if is_offroad_for_5_min and max_comp_temp > OFFROAD_DANGER_TEMP: # If device is offroad we want to cool down before going onroad # since going onroad increases load and can make temps go over 107 thermal_status = ThermalStatus.danger else: current_band = THERMAL_BANDS[thermal_status] band_idx = list(THERMAL_BANDS.keys()).index(thermal_status) if current_band.min_temp is not None and max_comp_temp < current_band.min_temp: thermal_status = list(THERMAL_BANDS.keys())[band_idx - 1] elif current_band.max_temp is not None and max_comp_temp > current_band.max_temp: thermal_status = list(THERMAL_BANDS.keys())[band_idx + 1] # **** starting logic **** # Check for last update time and display alerts if needed now = datetime.datetime.utcnow() # show invalid date/time alert startup_conditions["time_valid"] = (now.year > 2020) or ( now.year == 2020 and now.month >= 10) set_offroad_alert_if_changed("Offroad_InvalidTime", (not startup_conditions["time_valid"])) # Show update prompt try: last_update = datetime.datetime.fromisoformat( params.get("LastUpdateTime", encoding='utf8')) except (TypeError, ValueError): last_update = now dt = now - last_update update_failed_count = params.get("UpdateFailedCount") update_failed_count = 0 if update_failed_count is None else int( update_failed_count) last_update_exception = params.get("LastUpdateException", encoding='utf8') if update_failed_count > 15 and last_update_exception is not None: if tested_branch: extra_text = "Ensure the software is correctly installed" else: extra_text = last_update_exception set_offroad_alert_if_changed("Offroad_ConnectivityNeeded", False) set_offroad_alert_if_changed("Offroad_ConnectivityNeededPrompt", False) set_offroad_alert_if_changed("Offroad_UpdateFailed", True, extra_text=extra_text) elif dt.days > DAYS_NO_CONNECTIVITY_MAX and update_failed_count > 1: set_offroad_alert_if_changed("Offroad_UpdateFailed", False) set_offroad_alert_if_changed("Offroad_ConnectivityNeededPrompt", False) set_offroad_alert_if_changed("Offroad_ConnectivityNeeded", True) elif dt.days > DAYS_NO_CONNECTIVITY_PROMPT: remaining_time = str(max(DAYS_NO_CONNECTIVITY_MAX - dt.days, 0)) set_offroad_alert_if_changed("Offroad_UpdateFailed", False) set_offroad_alert_if_changed("Offroad_ConnectivityNeeded", False) set_offroad_alert_if_changed("Offroad_ConnectivityNeededPrompt", True, extra_text=f"{remaining_time} days.") else: set_offroad_alert_if_changed("Offroad_UpdateFailed", False) set_offroad_alert_if_changed("Offroad_ConnectivityNeeded", False) set_offroad_alert_if_changed("Offroad_ConnectivityNeededPrompt", False) startup_conditions["up_to_date"] = params.get( "Offroad_ConnectivityNeeded") is None or params.get_bool( "DisableUpdates") 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 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_StorageMissing", (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() # Offroad power monitoring power_monitor.calculate(peripheralState, startup_conditions["ignition"]) msg.deviceState.offroadPowerUsageUwh = power_monitor.get_power_used() msg.deviceState.carBatteryCapacityUwh = max( 0, power_monitor.get_car_battery_capacity()) # Check if we need to disable charging (handled by boardd) msg.deviceState.chargingDisabled = power_monitor.should_disable_charging( startup_conditions["ignition"], in_car, off_ts) # Check if we need to shut down if power_monitor.should_shutdown(peripheralState, startup_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
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_bool("OpenpilotEnabledToggle", True) params.put_bool("Passive", False) params.put_bool("CommunityFeaturesToggle", True) os.environ['NO_RADAR_SLEEP'] = "1" os.environ['SKIP_FW_QUERY'] = "" os.environ['FINGERPRINT'] = "" for msg in lr: if msg.which() == 'carParams': if len(msg.carParams.carFw) and (msg.carParams.carFingerprint in FW_VERSIONS): params.put("CarParamsCache", msg.carParams.as_builder().to_bytes()) else: os.environ['SKIP_FW_QUERY'] = "1" os.environ['FINGERPRINT'] = msg.carParams.carFingerprint 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) 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() log_msgs.append(m) recv_cnt -= m.which() in recv_socks return log_msgs
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 wifiIpAddress = 'N/A' network_info = None modem_version = None registered_count = 0 current_filter = FirstOrderFilter(0., CURRENT_TAU, DT_TRML) cpu_temp_filter = FirstOrderFilter(0., CPU_TEMP_TAU, DT_TRML) pandaState_prev = None should_start_prev = False handle_fan = None is_uno = False ui_running_prev = False 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) 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"] = True 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 wifiIpAddress = HARDWARE.get_ip_address() # Log modem version once if modem_version is None: modem_version = HARDWARE.get_modem_version() # pylint: disable=assignment-from-none if modem_version is not None: cloudlog.warning(f"Modem version: {modem_version}") if TICI and (network_info.get('state', None) == "REGISTERED"): registered_count += 1 else: registered_count = 0 if registered_count > 10: cloudlog.warning( f"Modem stuck in registered state {network_info}. nmcli conn up lte" ) os.system("nmcli conn up lte") registered_count = 0 except Exception: cloudlog.exception("Error getting network status") msg.deviceState.freeSpacePercent = get_available_percent(default=100.0) msg.deviceState.memoryUsagePercent = int( round(psutil.virtual_memory().percent)) msg.deviceState.cpuUsagePercent = int(round(psutil.cpu_percent())) msg.deviceState.gpuUsagePercent = int( round(HARDWARE.get_gpu_usage_percent())) msg.deviceState.networkType = network_type msg.deviceState.networkStrength = network_strength msg.deviceState.wifiIpAddress = wifiIpAddress 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 \ 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() # 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) # Set EON charging disable # based on kegman, 차량 저압배터리의 전압, 이온 배터리 퍼센티지, if EON: from selfdrive.thermald.eon_battery_manager import setEONChargingStatus setEONChargingStatus(power_monitor.car_voltage_mV, msg.deviceState.batteryPercent) # 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() # dp - auto shutdown if off_ts is not None: shutdown_sec = 240 sec_now = sec_since_boot() - off_ts if (shutdown_sec - 5) < sec_now: msg.deviceState.chargingDisabled = True if shutdown_sec < sec_now: time.sleep(5) 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() # report to server once every 10 minutes if (count % int(600. / DT_TRML)) == 0: if EON and started_ts is None and msg.deviceState.memoryUsagePercent > 40: cloudlog.event("High offroad memory usage", mem=msg.deviceState.memoryUsagePercent) location = messaging.recv_sock(location_sock) cloudlog.event("STATUS_PACKET", count=count, pandaState=(strip_deprecated_keys( pandaState.to_dict()) if pandaState else None), location=(strip_deprecated_keys( location.gpsLocationExternal.to_dict()) if location else None), deviceState=strip_deprecated_keys(msg.to_dict())) count += 1
def manager_init(): # update system time from panda set_time(cloudlog) params = Params() params.manager_start() default_params = [ ("CompletedTrainingVersion", "0"), ("HasAcceptedTerms", "0"), ("LastUpdateTime", datetime.datetime.utcnow().isoformat().encode('utf8')), ("OpenpilotEnabledToggle", "1"), ] if TICI: default_params.append(("IsUploadRawEnabled", "1")) 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") 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 dongle id reg_res = register(show_spinner=True) if reg_res: dongle_id = reg_res else: raise Exception("server registration failed") os.environ['DONGLE_ID'] = dongle_id # Needed for swaglog and loggerd if not dirty: os.environ['CLEAN'] = '1' cloudlog.bind_global(dongle_id=dongle_id, version=version, dirty=dirty, device=HARDWARE.get_device_type()) crash.bind_user(id=dongle_id) crash.bind_extra(version=version, dirty=dirty, device=HARDWARE.get_device_type())
def main() -> NoReturn: first_run = True params = Params() while True: try: params.delete("PandaSignatures") # Flash all Pandas in DFU mode for p in PandaDFU.list(): cloudlog.info( f"Panda in DFU mode found, flashing recovery {p}") PandaDFU(p).recover() time.sleep(1) panda_serials = Panda.list() if len(panda_serials) == 0: if first_run: cloudlog.info("Resetting internal panda") HARDWARE.reset_internal_panda() time.sleep(2) # wait to come back up continue cloudlog.info( f"{len(panda_serials)} panda(s) found, connecting - {panda_serials}" ) # Flash pandas pandas = [] for serial in panda_serials: pandas.append(flash_panda(serial)) # check health for lost heartbeat for panda in pandas: health = panda.health() if health["heartbeat_lost"]: params.put_bool("PandaHeartbeatLost", True) cloudlog.event("heartbeat lost", deviceState=health, serial=panda.get_usb_serial()) #if first_run: # cloudlog.info(f"Resetting panda {panda.get_usb_serial()}") # panda.reset() # sort pandas to have deterministic order pandas.sort(key=cmp_to_key(panda_sort_cmp)) panda_serials = list(map(lambda p: p.get_usb_serial(), pandas)) # log panda fw versions params.put("PandaSignatures", b','.join(p.get_signature() for p in pandas)) # close all pandas for p in pandas: p.close() except (usb1.USBErrorNoDevice, usb1.USBErrorPipe): # a panda was disconnected while setting everything up. let's try again cloudlog.exception("Panda USB exception while setting up") continue first_run = False # run boardd with all connected serials as arguments os.environ['MANAGER_DAEMON'] = 'boardd' os.chdir(os.path.join(BASEDIR, "selfdrive/boardd")) subprocess.run(["./boardd", *panda_serials], check=True)
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"), ("OpenpilotEnabledToggle", "1"), ] if not PC: default_params.append( ("LastUpdateTime", datetime.datetime.utcnow().isoformat().encode('utf8'))) if TICI: default_params.append(("EnableLteOnroad", "1")) 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") 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 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())
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")
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"), ("HasAcceptedTerms", "0"), ("OpenpilotEnabledToggle", "1"), ("CommunityFeaturesToggle", "1"), ("IsMetric", "1"), # HKG ("UseClusterSpeed", "0"), ("LongControlEnabled", "0"), ("MadModeEnabled", "1"), ("IsLdwsCar", "0"), ("LaneChangeEnabled", "0"), ("AutoLaneChangeEnabled", "0"), ("SccSmootherSlowOnCurves", "0"), ("SccSmootherSyncGasPressed", "0"), ("StockNaviDecelEnabled", "0"), ("KeepSteeringTurnSignals", "0"), ("WarningOverSpeedLimit", "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())
class TestUpdated(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.neos_version = os.path.join(org_dir, "neos_version") self.neosupdate_dir = os.path.join(org_dir, "neosupdate") with open(self.neos_version, "w") as f: v = subprocess.check_output( r"bash -c 'source launch_env.sh && echo $REQUIRED_NEOS_VERSION'", cwd=BASEDIR, shell=True, encoding='utf8').strip() f.write(v) 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/ common/" ]) self.params = Params(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 os.environ["UPDATER_NEOS_VERSION"] = self.neos_version os.environ["UPDATER_NEOSUPDATE_DIR"] = self.neosupdate_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_bool("IsOffroad", offroad) 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 complete") 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) # *** test cases with NEOS updates *** # Run updated with no update, make sure it clears the old NEOS update def test_clear_neos_cache(self): # make the dir and some junk files os.mkdir(self.neosupdate_dir) for _ in range(15): with tempfile.NamedTemporaryFile(dir=self.neosupdate_dir, delete=False) as f: f.write(os.urandom(random.randrange(1, 1000000))) self._start_updater() self._wait_for_update(clear_param=True) self._check_update_state(False) self.assertFalse(os.path.isdir(self.neosupdate_dir)) # Let the updater run with no update for a cycle, then write an update @unittest.skip("TODO: only runs on device") def test_update_with_neos_update(self): # bump the NEOS version and commit it self._run([ "echo 'export REQUIRED_NEOS_VERSION=3' >> launch_env.sh", "git -c user.name='testy' -c user.email='*****@*****.**' \ commit -am 'a neos update'", ], cwd=self.git_remote_dir) # run for a cycle to get the update self._start_updater() self._wait_for_update(timeout=60, clear_param=True) self._check_update_state(True) # TODO: more comprehensive check self.assertTrue(os.path.isdir(self.neosupdate_dir))
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_loopback(self): # wait for boardd to init time.sleep(2) with Timeout(60, "boardd didn't start"): sm = messaging.SubMaster(['pandaStates']) while sm.rcv_frame['pandaStates'] < 1 and len( sm['pandaStates']) == 0: sm.update(1000) num_pandas = len(sm['pandaStates']) if TICI: self.assertGreater(num_pandas, 1, "connect another panda for multipanda tests") # boardd blocks on CarVin and CarParams cp = car.CarParams.new_message() safety_config = car.CarParams.SafetyConfig.new_message() safety_config.safetyModel = car.CarParams.SafetyModel.allOutput cp.safetyConfigs = [safety_config] * num_pandas params = Params() params.put("CarVin", b"0" * 17) params.put_bool("ControlsReady", True) params.put("CarParams", cp.to_bytes()) sendcan = messaging.pub_sock('sendcan') can = messaging.sub_sock('can', conflate=False, timeout=100) time.sleep(0.2) n = 1000 for i in range(n): self.spinner.update(f"boardd loopback {i}/{n}") sent_msgs = defaultdict(set) for _ in range(random.randrange(10)): to_send = [] for __ in range(random.randrange(100)): bus = random.choice( [b for b in range(3 * num_pandas) if b % 4 != 3]) addr = random.randrange(1, 1 << 29) dat = bytes([ random.getrandbits(8) for _ in range(random.randrange(1, 9)) ]) sent_msgs[bus].add((addr, dat)) to_send.append(make_can_msg(addr, dat, bus)) sendcan.send(can_list_to_can_capnp(to_send, msgtype='sendcan')) for _ in range(100 * 2): recvd = messaging.drain_sock(can, wait_for_one=True) for msg in recvd: for m in msg.can: if m.src >= 128: key = (m.address, m.dat) assert key in sent_msgs[ m.src - 128], f"got unexpected msg: {m.src=} {m.address=} {m.dat=}" sent_msgs[m.src - 128].discard(key) if all(len(v) == 0 for v in sent_msgs.values()): break # if a set isn't empty, messages got dropped for bus in sent_msgs.keys(): assert not len( sent_msgs[bus] ), f"loop {i}: bus {bus} missing {len(sent_msgs[bus])} messages"
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())
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")
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"), ("OpenpilotEnabledToggle", "1"), ("IsMetric", "1"), ("CommunityFeaturesToggle", "1"), ("EndToEndToggle", "1"), ("IsOpenpilotViewEnabled", "0"), ("OpkrAutoShutdown", "2"), ("OpkrForceShutdown", "5"), ("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", "1"), ("LdwsCarFix", "0"), ("LateralControlMethod", "0"), ("CruiseStatemodeSelInit", "1"), ("InnerLoopGain", "35"), ("OuterLoopGain", "20"), ("TimeConstant", "14"), ("ActuatorEffectiveness", "20"), ("Scale", "1500"), ("LqrKi", "15"), ("DcGain", "27"), ("IgnoreZone", "0"), ("PidKp", "20"), ("PidKi", "40"), ("PidKd", "150"), ("PidKf", "7"), ("CameraOffsetAdj", "60"), ("SteerRatioAdj", "155"), ("SteerRatioMaxAdj", "175"), ("SteerActuatorDelayAdj", "20"), ("SteerRateCostAdj", "35"), ("SteerLimitTimerAdj", "80"), ("TireStiffnessFactorAdj", "100"), ("SteerMaxBaseAdj", "384"), ("SteerMaxAdj", "384"), ("SteerDeltaUpBaseAdj", "3"), ("SteerDeltaUpAdj", "3"), ("SteerDeltaDownBaseAdj", "7"), ("SteerDeltaDownAdj", "7"), ("SteerMaxvAdj", "10"), ("OpkrBatteryChargingControl", "1"), ("OpkrBatteryChargingMin", "70"), ("OpkrBatteryChargingMax", "80"), ("LeftCurvOffsetAdj", "0"), ("RightCurvOffsetAdj", "0"), ("DebugUi1", "0"), ("DebugUi2", "0"), ("LongLogDisplay", "0"), ("OpkrBlindSpotDetect", "1"), ("OpkrMaxAngleLimit", "90"), ("OpkrSpeedLimitOffset", "0"), ("OpkrLiveSteerRatio", "1"), ("OpkrVariableSteerMax", "1"), ("OpkrVariableSteerDelta", "0"), ("FingerprintTwoSet", "0"), ("OpkrVariableCruiseProfile", "1"), ("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"), ("OpkrRunNaviOnBoot", "0"), ("OpkrApksEnable", "0"), ("CruiseGap1", "10"), ("CruiseGap2", "12"), ("CruiseGap3", "15"), ("CruiseGap4", "20"), ("DynamicTR", "2"), ("OpkrBattLess", "0"), ("LCTimingFactorUD", "0"), ("LCTimingFactor30", "10"), ("LCTimingFactor60", "20"), ("LCTimingFactor80", "70"), ("LCTimingFactor110", "110"), ] 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") if EON and params.get_bool("OpkrApksEnable"): update_apks(show_spinner=True) 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 = "maintenance" 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() # ensure shared libraries are readable by apks if EON and params.get_bool("OpkrApksEnable"): os.chmod(BASEDIR, 0o755) os.chmod("/dev/shm", 0o777) os.chmod(os.path.join(BASEDIR, "cereal"), 0o755) 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")
def update(self, c, can_strings): self.cp.update_strings(can_strings) ret = self.CS.update(self.cp) t = sec_since_boot() cruiseEnabled = self.CS.pcm_acc_status != AccState.OFF ret.cruiseState.enabled = cruiseEnabled ret.canValid = self.cp.can_valid ret.steeringRateLimited = self.CC.steer_rate_limited if self.CC is not None else False ret.engineRPM = self.CS.engineRPM buttonEvents = [] if self.CS.cruise_buttons != self.CS.prev_cruise_buttons and self.CS.prev_cruise_buttons != CruiseButtons.INIT: be = car.CarState.ButtonEvent.new_message() be.type = ButtonType.unknown if self.CS.cruise_buttons != CruiseButtons.UNPRESS: be.pressed = True but = self.CS.cruise_buttons else: be.pressed = False but = self.CS.prev_cruise_buttons if but == CruiseButtons.RES_ACCEL: if not (ret.cruiseState.enabled and ret.standstill): be.type = ButtonType.accelCruise # Suppress resume button if we're resuming from stop so we don't adjust speed. elif but == CruiseButtons.DECEL_SET: if not cruiseEnabled and not self.CS.lkMode: self.lkMode = True be.type = ButtonType.decelCruise elif but == CruiseButtons.CANCEL: be.type = ButtonType.cancel elif but == CruiseButtons.MAIN: be.type = ButtonType.altButton3 buttonEvents.append(be) ret.buttonEvents = buttonEvents if cruiseEnabled and self.CS.lka_button and self.CS.lka_button != self.CS.prev_lka_button: self.CS.lkMode = not self.CS.lkMode cloudlog.info("button press event: LKA button. new value: %i" % self.CS.lkMode) if t - self.params_check_last_t >= self.params_check_freq: self.params_check_last_t = t self.one_pedal_mode = self.CS._params.get_bool("OnePedalMode") # distance button is also used to toggle braking modes when in one-pedal-mode if self.CS.one_pedal_mode_active or self.CS.coast_one_pedal_mode_active: if self.CS.distance_button != self.CS.prev_distance_button: if not self.CS.distance_button and self.CS.one_pedal_mode_engaged_with_button and t - self.CS.distance_button_last_press_t < 0.8: #user just engaged one-pedal with distance button hold and immediately let off the button, so default to regen/engine braking. If they keep holding, it does hard braking cloudlog.info( "button press event: Engaging one-pedal mode with distance button." ) self.CS.one_pedal_brake_mode = 0 self.one_pedal_last_brake_mode = self.CS.one_pedal_brake_mode self.CS.one_pedal_mode_enabled = False self.CS.one_pedal_mode_active = False self.CS.coast_one_pedal_mode_active = True tmp_params = Params() tmp_params.put("OnePedalBrakeMode", str(self.CS.one_pedal_brake_mode)) tmp_params.put_bool("OnePedalMode", self.CS.one_pedal_mode_enabled) else: if not self.one_pedal_mode and self.CS.distance_button: # user lifted press of distance button while in coast-one-pedal mode, so turn on braking cloudlog.info( "button press event: Engaging one-pedal braking.") self.CS.one_pedal_last_switch_to_friction_braking_t = t self.CS.distance_button_last_press_t = t + 0.5 self.CS.one_pedal_brake_mode = 0 self.one_pedal_last_brake_mode = self.CS.one_pedal_brake_mode self.CS.one_pedal_mode_enabled = True self.CS.one_pedal_mode_active = True tmp_params = Params() tmp_params.put("OnePedalBrakeMode", str(self.CS.one_pedal_brake_mode)) tmp_params.put_bool("OnePedalMode", self.CS.one_pedal_mode_enabled) elif self.CS.distance_button and ( self.CS.pause_long_on_gas_press or self.CS.out.standstill ) and t - self.CS.distance_button_last_press_t < 0.4 and t - self.CS.one_pedal_last_switch_to_friction_braking_t > 1.: # on the second press of a double tap while the gas is pressed, turn off one-pedal braking # cycle the brake mode back to nullify the first press cloudlog.info( "button press event: Disengaging one-pedal mode with distace button double-press." ) self.CS.distance_button_last_press_t = t + 0.5 self.CS.one_pedal_brake_mode = 0 self.one_pedal_last_brake_mode = self.CS.one_pedal_brake_mode self.CS.one_pedal_mode_enabled = False self.CS.one_pedal_mode_active = False self.CS.coast_one_pedal_mode_active = True tmp_params = Params() tmp_params.put("OnePedalBrakeMode", str(self.CS.one_pedal_brake_mode)) tmp_params.put_bool("OnePedalMode", self.CS.one_pedal_mode_enabled) else: if self.CS.distance_button: self.CS.distance_button_last_press_t = t cloudlog.info( "button press event: Distance button pressed in one-pedal mode." ) else: # only make changes when user lifts press if self.CS.one_pedal_brake_mode == 2: cloudlog.info( "button press event: Disengaging one-pedal hard braking. Switching to moderate braking" ) self.CS.one_pedal_brake_mode = 1 tmp_params = Params() tmp_params.put( "OnePedalBrakeMode", str(self.CS.one_pedal_brake_mode)) elif t - self.CS.distance_button_last_press_t > 0. and t - self.CS.distance_button_last_press_t < 0.4: # only switch braking on a single tap (also allows for ignoring presses by setting last_press_t to be greater than t) self.CS.one_pedal_brake_mode = ( self.CS.one_pedal_brake_mode + 1) % 2 cloudlog.info( f"button press event: one-pedal braking. New value: {self.CS.one_pedal_brake_mode}" ) tmp_params = Params() tmp_params.put( "OnePedalBrakeMode", str(self.CS.one_pedal_brake_mode)) self.CS.one_pedal_mode_engaged_with_button = False elif self.CS.distance_button and t - self.CS.distance_button_last_press_t > 0.3: if self.CS.one_pedal_brake_mode < 2: cloudlog.info( "button press event: Engaging one-pedal hard braking.") self.one_pedal_last_brake_mode = self.CS.one_pedal_brake_mode self.CS.one_pedal_brake_mode = 2 self.CS.follow_level = self.CS.one_pedal_brake_mode + 1 else: # cruis is active, so just modify follow distance if self.CS.distance_button != self.CS.prev_distance_button: if self.CS.distance_button: self.CS.distance_button_last_press_t = t cloudlog.info( "button press event: Distance button pressed in cruise mode." ) else: # apply change on button lift self.CS.follow_level -= 1 if self.CS.follow_level < 1: self.CS.follow_level = 3 tmp_params = Params() tmp_params.put("FollowLevel", str(self.CS.follow_level)) cloudlog.info( "button press event: cruise follow distance button. new value: %r" % self.CS.follow_level) elif self.CS.distance_button and t - self.CS.distance_button_last_press_t > 0.5 and not ( self.CS.one_pedal_mode_active or self.CS.coast_one_pedal_mode_active): # user held follow button while in normal cruise, so engage one-pedal mode cloudlog.info( "button press event: distance button hold to engage one-pedal mode." ) self.CS.one_pedal_mode_engage_on_gas = True self.CS.one_pedal_mode_engaged_with_button = True self.CS.distance_button_last_press_t = t + 0.2 # gives the user X+0.3 seconds to release the distance button before hard braking is applied (which they may want, so don't want too long of a delay) ret.readdistancelines = self.CS.follow_level events = self.create_common_events(ret, pcm_enable=False) if ret.vEgo < self.CP.minEnableSpeed: events.add(EventName.belowEngageSpeed) if self.CS.pause_long_on_gas_press: events.add(EventName.gasPressed) if self.CS.park_brake: events.add(EventName.parkBrake) steer_paused = False if cruiseEnabled: if t - self.CS.last_pause_long_on_gas_press_t < 0.5 and t - self.CS.sessionInitTime > 10.: events.add(car.CarEvent.EventName.pauseLongOnGasPress) if not ret.standstill and self.CS.lkMode and self.CS.lane_change_steer_factor < 1.: events.add(car.CarEvent.EventName.blinkerSteeringPaused) steer_paused = True if ret.vEgo < self.CP.minSteerSpeed: if ret.standstill and cruiseEnabled and not ret.brakePressed and not self.CS.pause_long_on_gas_press and not self.CS.autoHoldActivated and not self.CS.disengage_on_gas and t - self.CS.sessionInitTime > 10.: events.add(car.CarEvent.EventName.stoppedWaitForGas) elif not steer_paused and self.CS.lkMode: events.add(car.CarEvent.EventName.belowSteerSpeed) if self.CS.autoHoldActivated: self.CS.lastAutoHoldTime = t events.add(car.CarEvent.EventName.autoHoldActivated) if self.CS.pcm_acc_status == AccState.FAULTED and t - self.CS.sessionInitTime > 10.0 and t - self.CS.lastAutoHoldTime > 1.0: events.add(EventName.accFaulted) # handle button presses for b in ret.buttonEvents: # do enable on both accel and decel buttons # The ECM will fault if resume triggers an enable while speed is set to 0 if b.type == ButtonType.accelCruise and c.hudControl.setSpeed > 0 and c.hudControl.setSpeed < 70 and not b.pressed: events.add(EventName.buttonEnable) if b.type == ButtonType.decelCruise and not b.pressed: events.add(EventName.buttonEnable) # do disable on button down if b.type == ButtonType.cancel and b.pressed: events.add(EventName.buttonCancel) # The ECM independently tracks a ‘speed is set’ state that is reset on main off. # To keep controlsd in sync with the ECM state, generate a RESET_V_CRUISE event on main cruise presses. if b.type == ButtonType.altButton3 and b.pressed: events.add(EventName.buttonMainCancel) ret.events = events.to_msg() # copy back carState packet to CS self.CS.out = ret.as_reader() return self.CS.out
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 modem_version = None registered_count = 0 wifiIpAddress = "N/A" 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 = 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 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"): 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
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", 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())
def thermald_thread(end_event, hw_queue): pm = messaging.PubMaster(['deviceState']) sm = messaging.SubMaster([ "peripheralState", "gpsLocationExternal", "controlsState", "pandaStates" ], poll=["pandaStates"]) 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 last_hw_state = HardwareState( network_type=NetworkType.none, network_metered=False, network_strength=NetworkStrength.unknown, network_info=None, nvme_temps=[], modem_temps=[], wifi_address='N/A', ) current_filter = FirstOrderFilter(0., CURRENT_TAU, DT_TRML) temp_filter = FirstOrderFilter(0., TEMP_TAU, DT_TRML) should_start_prev = False in_car = False engaged_prev = False params = Params() power_monitor = PowerMonitoring() HARDWARE.initialize_hardware() thermal_config = HARDWARE.get_thermal_config() fan_controller = None restart_triggered_ts = 0. panda_state_ts = 0. while not end_event.is_set(): sm.update(PANDA_STATES_TIMEOUT) pandaStates = sm['pandaStates'] peripheralState = sm['peripheralState'] msg = read_thermal(thermal_config) # neokii if sec_since_boot() - restart_triggered_ts < 5.: onroad_conditions["not_restart_triggered"] = False else: onroad_conditions["not_restart_triggered"] = True if params.get_bool("SoftRestartTriggered"): params.put_bool("SoftRestartTriggered", False) restart_triggered_ts = sec_since_boot() if sm.updated['pandaStates'] and len(pandaStates) > 0: # Set ignition based on any panda connected onroad_conditions["ignition"] = any( ps.ignitionLine or ps.ignitionCan for ps in pandaStates if ps.pandaType != log.PandaState.PandaType.unknown) pandaState = pandaStates[0] if pandaState.pandaType != log.PandaState.PandaType.unknown: panda_state_ts = sec_since_boot() in_car = pandaState.harnessStatus != log.PandaState.HarnessStatus.notConnected # Setup fan handler on first connect to panda if fan_controller is None and peripheralState.pandaType != log.PandaState.PandaType.unknown: if TICI: fan_controller = TiciFanController() elif (sec_since_boot() - sm.rcv_time['pandaStates'] / 1e9) > DISCONNECT_TIMEOUT: if onroad_conditions["ignition"]: onroad_conditions["ignition"] = False cloudlog.error("panda timed out onroad") try: last_hw_state = hw_queue.get_nowait() except queue.Empty: pass 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 = last_hw_state.network_type msg.deviceState.networkMetered = last_hw_state.network_metered msg.deviceState.networkStrength = last_hw_state.network_strength if last_hw_state.network_info is not None: msg.deviceState.networkInfo = last_hw_state.network_info msg.deviceState.nvmeTempC = last_hw_state.nvme_temps msg.deviceState.modemTempC = last_hw_state.modem_temps msg.deviceState.wifiIpAddress = last_hw_state.wifi_address msg.deviceState.screenBrightnessPercent = HARDWARE.get_screen_brightness( ) 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), max(msg.deviceState.pmicTempC))) if fan_controller is not None: msg.deviceState.fanSpeedPercentDesired = fan_controller.update( max_comp_temp, onroad_conditions["ignition"]) 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"] = True #(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"] = True #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"])) # TODO: this should move to TICI.initialize_hardware, but we currently can't import params there if TICI: if not os.path.isfile("/persist/comma/living-in-the-moment"): if not Path("/data/media").is_mount(): set_offroad_alert_if_changed("Offroad_StorageMissing", True) else: # check for bad NVMe try: with open("/sys/block/nvme0n1/device/model") as f: model = f.read().strip() if not model.startswith( "Samsung SSD 980") and params.get( "Offroad_BadNvme") is None: set_offroad_alert_if_changed( "Offroad_BadNvme", True) cloudlog.event("Unsupported NVMe", model=model, error=True) except Exception: pass # 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) params.put_bool("IsEngaged", False) engaged_prev = False HARDWARE.set_power_save(not should_start) if sm.updated['controlsState']: engaged = sm['controlsState'].enabled if engaged != engaged_prev: params.put_bool("IsEngaged", engaged) engaged_prev = engaged try: with open('/dev/kmsg', 'w') as kmsg: kmsg.write(f"<3>[thermald] engaged: {engaged}\n") except Exception: pass 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 if current_power_draw is not None: statlog.sample("power_draw", current_power_draw) msg.deviceState.powerDrawW = current_power_draw else: msg.deviceState.powerDrawW = 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.warning(f"shutting device down, offroad since {off_ts}") params.put_bool("DoShutdown", True) 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) should_start_prev = should_start startup_conditions_prev = startup_conditions.copy() # Log to statsd statlog.gauge("free_space_percent", msg.deviceState.freeSpacePercent) statlog.gauge("gpu_usage_percent", msg.deviceState.gpuUsagePercent) statlog.gauge("memory_usage_percent", msg.deviceState.memoryUsagePercent) for i, usage in enumerate(msg.deviceState.cpuUsagePercent): statlog.gauge(f"cpu{i}_usage_percent", usage) for i, temp in enumerate(msg.deviceState.cpuTempC): statlog.gauge(f"cpu{i}_temperature", temp) for i, temp in enumerate(msg.deviceState.gpuTempC): statlog.gauge(f"gpu{i}_temperature", temp) statlog.gauge("memory_temperature", msg.deviceState.memoryTempC) statlog.gauge("ambient_temperature", msg.deviceState.ambientTempC) for i, temp in enumerate(msg.deviceState.pmicTempC): statlog.gauge(f"pmic{i}_temperature", temp) for i, temp in enumerate(last_hw_state.nvme_temps): statlog.gauge(f"nvme_temperature{i}", temp) for i, temp in enumerate(last_hw_state.modem_temps): statlog.gauge(f"modem_temperature{i}", temp) statlog.gauge("fan_speed_percent_desired", msg.deviceState.fanSpeedPercentDesired) statlog.gauge("screen_brightness_percent", msg.deviceState.screenBrightnessPercent) # report to server once every 10 minutes if (count % int(600. / DT_TRML)) == 0: cloudlog.event( "STATUS_PACKET", count=count, pandaStates=[ strip_deprecated_keys(p.to_dict()) for p in pandaStates ], 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
def init_overlay() -> None: overlay_init_file = Path(os.path.join(BASEDIR, ".overlay_init")) # Re-create the overlay if BASEDIR/.git has changed since we created the overlay if overlay_init_file.is_file(): git_dir_path = os.path.join(BASEDIR, ".git") new_files = run( ["find", git_dir_path, "-newer", str(overlay_init_file)]) if not len(new_files.splitlines()): # A valid overlay already exists return else: cloudlog.info(".git directory changed, recreating overlay") cloudlog.info("preparing new safe staging area") params = Params() params.put_bool("UpdateAvailable", False) set_consistent_flag(False) dismount_overlay() if TICI: run(["sudo", "rm", "-rf", STAGING_ROOT]) if os.path.isdir(STAGING_ROOT): shutil.rmtree(STAGING_ROOT) for dirname in [ STAGING_ROOT, OVERLAY_UPPER, OVERLAY_METADATA, OVERLAY_MERGED ]: os.mkdir(dirname, 0o755) if os.lstat(BASEDIR).st_dev != os.lstat(OVERLAY_MERGED).st_dev: raise RuntimeError( "base and overlay merge directories are on different filesystems; not valid for overlay FS!" ) # Leave a timestamped canary in BASEDIR to check at startup. The device clock # should be correct by the time we get here. If the init file disappears, or # critical mtimes in BASEDIR are newer than .overlay_init, continue.sh can # assume that BASEDIR has used for local development or otherwise modified, # and skips the update activation attempt. consistent_file = Path(os.path.join(BASEDIR, ".overlay_consistent")) if consistent_file.is_file(): consistent_file.unlink() overlay_init_file.touch() os.sync() overlay_opts = f"lowerdir={BASEDIR},upperdir={OVERLAY_UPPER},workdir={OVERLAY_METADATA}" mount_cmd = [ "mount", "-t", "overlay", "-o", overlay_opts, "none", OVERLAY_MERGED ] if TICI: run(["sudo"] + mount_cmd) run(["sudo", "chmod", "755", os.path.join(OVERLAY_METADATA, "work")]) else: run(mount_cmd) git_diff = run(["git", "diff"], OVERLAY_MERGED, low_priority=True) params.put("GitDiff", git_diff) cloudlog.info(f"git diff output:\n{git_diff}")
from common.basedir import BASEDIR from common.params import Params from selfdrive.controls.lib.alertmanager import set_offroad_alert if __name__ == "__main__": params = Params() with open( os.path.join(BASEDIR, "selfdrive/controls/lib/alerts_offroad.json")) as f: offroad_alerts = json.load(f) t = 10 if len(sys.argv) < 2 else int(sys.argv[1]) while True: print("setting alert update") params.put_bool("UpdateAvailable", True) r = open(os.path.join(BASEDIR, "RELEASES.md")).read() r = r[:r.find('\n\n')] # Slice latest release notes params.put("ReleaseNotes", r + "\n") time.sleep(t) params.put_bool("UpdateAvailable", False) # cycle through normal alerts for a in offroad_alerts: print("setting alert:", a) set_offroad_alert(a, True) time.sleep(t) set_offroad_alert(a, False) print("no alert")