From 9b5d2ea87329e47ef2fcdb080f4c165ab3ccd0e8 Mon Sep 17 00:00:00 2001 From: dragonpilot Date: Sun, 15 Mar 2020 12:22:58 +1000 Subject: [PATCH 1/2] merge fixes --- selfdrive/car/honda/interface.py | 0 selfdrive/thermald/thermald.py | 0 selfdrive/updated.py | 0 3 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 selfdrive/car/honda/interface.py mode change 100644 => 100755 selfdrive/thermald/thermald.py mode change 100644 => 100755 selfdrive/updated.py diff --git a/selfdrive/car/honda/interface.py b/selfdrive/car/honda/interface.py old mode 100644 new mode 100755 diff --git a/selfdrive/thermald/thermald.py b/selfdrive/thermald/thermald.py old mode 100644 new mode 100755 diff --git a/selfdrive/updated.py b/selfdrive/updated.py old mode 100644 new mode 100755 From df8ece10a7be643f8f1642647ff53016e520a13d Mon Sep 17 00:00:00 2001 From: dragonpilot Date: Sun, 15 Mar 2020 12:22:58 +1000 Subject: [PATCH 2/2] dp 0.7.4 merge fixes dp 0.7.4 merge fixes --- cereal/log.capnp | 2 +- panda/board/safety/safety_honda.h | 2 +- panda/board/safety/safety_toyota.h | 2 +- reset_update.sh | 2 +- selfdrive/assets/images/button_home.png | Bin 1501 -> 29416 bytes selfdrive/car/car_helpers.py | 5 +- selfdrive/car/honda/carcontroller.py | 12 ++++- selfdrive/car/honda/interface.py | 1 + selfdrive/car/hyundai/carcontroller.py | 12 ++++- selfdrive/car/interfaces.py | 47 +++++++++++++++-- selfdrive/car/subaru/carcontroller.py | 12 ++++- selfdrive/car/subaru/interface.py | 1 + selfdrive/car/toyota/carcontroller.py | 25 ++++----- selfdrive/car/toyota/carstate.py | 47 ++++++++++++++--- selfdrive/car/toyota/interface.py | 1 + selfdrive/controls/controlsd.py | 9 ++-- selfdrive/controls/dmonitoringd.py | 9 ++-- selfdrive/controls/lib/pathplanner.py | 7 +-- selfdrive/dragonpilot/appd/appd.py | 11 +--- selfdrive/dragonpilot/dragonconf/__init__.py | 2 +- selfdrive/thermald/thermald.py | 13 ++--- selfdrive/ui/sidebar.cc | 51 +++++++++++++++---- selfdrive/ui/ui.cc | 1 + selfdrive/ui/ui.hpp | 2 + selfdrive/updated.py | 0 25 files changed, 200 insertions(+), 76 deletions(-) mode change 100644 => 100755 selfdrive/car/honda/interface.py mode change 100644 => 100755 selfdrive/thermald/thermald.py mode change 100644 => 100755 selfdrive/updated.py diff --git a/cereal/log.capnp b/cereal/log.capnp index a37a58923..522a9da5c 100644 --- a/cereal/log.capnp +++ b/cereal/log.capnp @@ -296,7 +296,7 @@ struct ThermalData { memUsedPercent @19 :Int8; cpuPerc @20 :Int8; - ipAddr @24 :Text; # dragonpilot + ipAddr @25 :Text; # dragonpilot enum ThermalStatus { green @0; # all processes run diff --git a/panda/board/safety/safety_honda.h b/panda/board/safety/safety_honda.h index aa097f491..75ecae214 100644 --- a/panda/board/safety/safety_honda.h +++ b/panda/board/safety/safety_honda.h @@ -194,7 +194,7 @@ static int honda_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { // and the the latching controls_allowed flag is True //int pedal_pressed = gas_pressed_prev || (gas_interceptor_prev > HONDA_GAS_INTERCEPTOR_THRESHOLD) || // (brake_pressed_prev && honda_moving); - int pedal_pressed = brake_pressed_prev && honda_moving;; + int pedal_pressed = brake_pressed_prev && honda_moving; bool current_controls_allowed = controls_allowed && !(pedal_pressed); // BRAKE: safety check diff --git a/panda/board/safety/safety_toyota.h b/panda/board/safety/safety_toyota.h index 4fe1fe9fd..5aedb3764 100644 --- a/panda/board/safety/safety_toyota.h +++ b/panda/board/safety/safety_toyota.h @@ -112,7 +112,7 @@ static int toyota_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { int byte = (addr == 0x224) ? 0 : 4; bool brake_pressed = ((GET_BYTE(to_push, byte) >> 5) & 1) != 0; if (brake_pressed && (!brake_pressed_prev || toyota_moving)) { - controls_allowed = 0; + controls_allowed = 1; } brake_pressed_prev = brake_pressed; } diff --git a/reset_update.sh b/reset_update.sh index 6bfe86d88..18d77f9d3 100755 --- a/reset_update.sh +++ b/reset_update.sh @@ -3,4 +3,4 @@ export LD_LIBRARY_PATH=/data/data/com.termux/files/usr/lib export HOME=/data/data/com.termux/files/home export PATH=/usr/local/bin:/data/data/com.termux/files/usr/bin:/data/data/com.termux/files/usr/sbin:/data/data/com.termux/files/usr/bin/applets:/bin:/sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin:/data/data/com.termux/files/usr/bin/git -cd /data/openpilot && git reset --hard @{u} && git clean -xdf && git pull && reboot \ No newline at end of file +cd /data/openpilot && git reset --hard @{u} && git clean -xdf && git pull && scons --clean && reboot \ No newline at end of file diff --git a/selfdrive/assets/images/button_home.png b/selfdrive/assets/images/button_home.png index 9f52faf9e2da488ab9ba4b7e11cae9fed4f9c47c..07f47ab5d96242e5cab2130184863eaf43dd8ab2 100644 GIT binary patch literal 29416 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͘YqBflCsnk~fao9Vo(zx(u%J5UTA#X{6UMA3 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
UvOsx^@-r`h(m4Zo<8+MvMfgu*!2>+h^M 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 diff --git a/selfdrive/car/car_helpers.py b/selfdrive/car/car_helpers.py index f0815dd98..8ef75a4ce 100644 --- a/selfdrive/car/car_helpers.py +++ b/selfdrive/car/car_helpers.py @@ -181,16 +181,15 @@ def fingerprint(logcan, sendcan, has_relay): frame += 1 + source = car.CarParams.FingerprintSource.can + if dragon_has_cache: car_fingerprint = dragon_car_fingerprint finger = dragon_finger vin = dragon_vin car_fw = dragon_car_fw source = dragon_source - else: - source = car.CarParams.FingerprintSource.can - # If FW query returns exactly 1 candidate, use it if len(fw_candidates) == 1: car_fingerprint = list(fw_candidates)[0] diff --git a/selfdrive/car/honda/carcontroller.py b/selfdrive/car/honda/carcontroller.py index 6db935c6a..956f73cdd 100644 --- a/selfdrive/car/honda/carcontroller.py +++ b/selfdrive/car/honda/carcontroller.py @@ -104,6 +104,7 @@ class CarController(): self.dragon_enable_steering_on_signal = False self.dragon_lat_ctrl = True self.dp_last_modified = None + self.lane_change_enabled = True def update(self, enabled, CS, frame, actuators, \ pcm_speed, pcm_override, pcm_cancel_cmd, pcm_accel, \ @@ -112,8 +113,15 @@ class CarController(): if frame % 500 == 0: modified = dp_get_last_modified() if self.dp_last_modified != modified: - self.dragon_enable_steering_on_signal = True if params.get("DragonEnableSteeringOnSignal", encoding='utf8') == "1" else False self.dragon_lat_ctrl = False if params.get("DragonLatCtrl", encoding='utf8') == "0" else True + if self.dragon_lat_ctrl: + self.lane_change_enabled = False if params.get("LaneChangeEnabled", encoding='utf8') == "1" else False + if not self.lane_change_enabled: + self.dragon_enable_steering_on_signal = True if params.get("DragonEnableSteeringOnSignal", encoding='utf8') == "1" else False + else: + self.dragon_enable_steering_on_signal = False + else: + self.dragon_enable_steering_on_signal = False self.dp_last_modified = modified P = self.params @@ -163,7 +171,7 @@ class CarController(): # dragonpilot if enabled: if self.dragon_enable_steering_on_signal: - if CS.left_blinker_on == 0 and CS.right_blinker_on == 0: + if not CS.out.leftBlinker and not CS.out.rightBlinker: self.turning_signal_timer = 0 else: self.turning_signal_timer = 100 diff --git a/selfdrive/car/honda/interface.py b/selfdrive/car/honda/interface.py old mode 100644 new mode 100755 index 5d7851219..ef7da044e --- a/selfdrive/car/honda/interface.py +++ b/selfdrive/car/honda/interface.py @@ -404,6 +404,7 @@ class CarInterface(CarInterfaceBase): # returns a car.CarState def update(self, c, can_strings): + self.dp_load_params('honda') # ******************* do can recv ******************* self.cp.update_strings(can_strings) self.cp_cam.update_strings(can_strings) diff --git a/selfdrive/car/hyundai/carcontroller.py b/selfdrive/car/hyundai/carcontroller.py index 519ea9ace..285e835ae 100644 --- a/selfdrive/car/hyundai/carcontroller.py +++ b/selfdrive/car/hyundai/carcontroller.py @@ -21,6 +21,7 @@ class CarController(): self.dragon_enable_steering_on_signal = False self.dragon_lat_ctrl = True self.dp_last_modified = None + self.lane_change_enabled = True def update(self, enabled, CS, frame, actuators, pcm_cancel_cmd, hud_alert): @@ -28,8 +29,15 @@ class CarController(): if frame % 500 == 0: modified = dp_get_last_modified() if self.dp_last_modified != modified: - self.dragon_enable_steering_on_signal = True if params.get("DragonEnableSteeringOnSignal", encoding='utf8') == "1" else False self.dragon_lat_ctrl = False if params.get("DragonLatCtrl", encoding='utf8') == "0" else True + if self.dragon_lat_ctrl: + self.lane_change_enabled = False if params.get("LaneChangeEnabled", encoding='utf8') == "1" else False + if not self.lane_change_enabled: + self.dragon_enable_steering_on_signal = True if params.get("DragonEnableSteeringOnSignal", encoding='utf8') == "1" else False + else: + self.dragon_enable_steering_on_signal = False + else: + self.dragon_enable_steering_on_signal = False self.dp_last_modified = modified ### Steering Torque @@ -49,7 +57,7 @@ class CarController(): # dragonpilot if enabled: if self.dragon_enable_steering_on_signal: - if CS.left_blinker_on == 0 and CS.right_blinker_on == 0: + if not CS.out.leftBlinker and not CS.out.rightBlinker: self.turning_signal_timer = 0 else: self.turning_signal_timer = 100 diff --git a/selfdrive/car/interfaces.py b/selfdrive/car/interfaces.py index a25df91f9..ac78d1207 100644 --- a/selfdrive/car/interfaces.py +++ b/selfdrive/car/interfaces.py @@ -6,6 +6,10 @@ from common.realtime import DT_CTRL from selfdrive.car import gen_empty_fingerprint from selfdrive.controls.lib.drive_helpers import EventTypes as ET, create_event from selfdrive.controls.lib.vehicle_model import VehicleModel +from common.realtime import sec_since_boot +from common.params import Params +params = Params() +from selfdrive.dragonpilot.dragonconf import dp_get_last_modified GearShifter = car.CarState.GearShifter @@ -30,6 +34,14 @@ class CarInterfaceBase(): if CarController is not None: self.CC = CarController(self.cp.dbc_name, CP, self.VM) + # dragonpilot + self.dragon_toyota_stock_dsu = False + self.dragon_enable_steering_on_signal = False + self.dragon_allow_gas = False + self.ts_last_check = 0. + self.dragon_lat_ctrl = True + self.dp_last_modified = None + @staticmethod def calc_accel_override(a_ego, a_target, v_ego, v_target): return 1. @@ -81,6 +93,23 @@ class CarInterfaceBase(): def apply(self, c): raise NotImplementedError + def dp_load_params(self, car_name): + # dragonpilot, don't check for param too often as it's a kernel call + ts = sec_since_boot() + if ts - self.ts_last_check >= 5.: + modified = dp_get_last_modified() + if self.dp_last_modified != modified: + self.dragon_lat_ctrl = False if params.get("DragonLatCtrl", encoding='utf8') == "0" else True + if self.dragon_lat_ctrl: + self.dragon_enable_steering_on_signal = True if params.get("DragonEnableSteeringOnSignal", encoding='utf8') == "1" else False + else: + self.dragon_enable_steering_on_signal = True + self.dragon_allow_gas = True if params.get("DragonAllowGas", encoding='utf8') == "1" else False + if car_name == 'toyota': + self.dragon_toyota_stock_dsu = True if params.get("DragonToyotaStockDSU", encoding='utf8') == "1" else False + self.dp_last_modified = modified + self.ts_last_check = ts + def create_common_events(self, cs_out, extra_gears=[], gas_resume_speed=-1): events = [] @@ -100,7 +129,11 @@ class CarInterfaceBase(): events.append(create_event('pedalPressed', [ET.PRE_ENABLE])) # TODO: move this stuff to the capnp strut - if getattr(self.CS, "steer_error", False): + if not self.dragon_lat_ctrl: + events.append(create_event('manualSteeringRequired', [ET.WARNING])) + elif self.dragon_enable_steering_on_signal and (cs_out.leftBlinker or cs_out.rightBlinker): + events.append(create_event('manualSteeringRequiredBlinkersOn', [ET.WARNING])) + elif getattr(self.CS, "steer_error", False): events.append(create_event('steerUnavailable', [ET.NO_ENTRY, ET.IMMEDIATE_DISABLE, ET.PERMANENT])) elif getattr(self.CS, "steer_warning", False): events.append(create_event('steerTempUnavailable', [ET.NO_ENTRY, ET.WARNING])) @@ -108,9 +141,15 @@ class CarInterfaceBase(): # Disable on rising edge of gas or brake. Also disable on brake when speed > 0. # Optionally allow to press gas at zero speed to resume. # e.g. Chrysler does not spam the resume button yet, so resuming with gas is handy. FIXME! - if (cs_out.gasPressed and (not self.gas_pressed_prev) and cs_out.vEgo > gas_resume_speed) or \ - (cs_out.brakePressed and (not self.brake_pressed_prev or not cs_out.standstill)): - events.append(create_event('pedalPressed', [ET.NO_ENTRY, ET.USER_DISABLE])) + if not self.dragon_toyota_stock_dsu: + # DragonAllowGas + if not self.dragon_allow_gas: + if (cs_out.gasPressed and (not self.gas_pressed_prev) and cs_out.vEgo > gas_resume_speed) or \ + (cs_out.brakePressed and (not self.brake_pressed_prev or not cs_out.standstill)): + events.append(create_event('pedalPressed', [ET.NO_ENTRY, ET.USER_DISABLE])) + else: + if cs_out.brakePressed and (not self.brake_pressed_prev or not cs_out.standstill): + events.append(create_event('pedalPressed', [ET.NO_ENTRY, ET.USER_DISABLE])) return events diff --git a/selfdrive/car/subaru/carcontroller.py b/selfdrive/car/subaru/carcontroller.py index bbafcae41..23cca1df5 100644 --- a/selfdrive/car/subaru/carcontroller.py +++ b/selfdrive/car/subaru/carcontroller.py @@ -37,6 +37,7 @@ class CarController(): self.dragon_enable_steering_on_signal = False self.dragon_lat_ctrl = True self.dp_last_modified = None + self.lane_change_enabled = True def update(self, enabled, CS, frame, actuators, pcm_cancel_cmd, visual_alert, left_line, right_line): """ Controls thread """ @@ -45,8 +46,15 @@ class CarController(): if frame % 500 == 0: modified = dp_get_last_modified() if self.dp_last_modified != modified: - self.dragon_enable_steering_on_signal = True if params.get("DragonEnableSteeringOnSignal", encoding='utf8') == "1" else False self.dragon_lat_ctrl = False if params.get("DragonLatCtrl", encoding='utf8') == "0" else True + if self.dragon_lat_ctrl: + self.lane_change_enabled = False if params.get("LaneChangeEnabled", encoding='utf8') == "1" else False + if not self.lane_change_enabled: + self.dragon_enable_steering_on_signal = True if params.get("DragonEnableSteeringOnSignal", encoding='utf8') == "1" else False + else: + self.dragon_enable_steering_on_signal = False + else: + self.dragon_enable_steering_on_signal = False self.dp_last_modified = modified P = self.params @@ -75,7 +83,7 @@ class CarController(): # dragonpilot if enabled: if self.dragon_enable_steering_on_signal: - if CS.left_blinker_on == 0 and CS.right_blinker_on == 0: + if not CS.out.leftBlinker and not CS.out.rightBlinker: self.turning_signal_timer = 0 else: self.turning_signal_timer = 100 diff --git a/selfdrive/car/subaru/interface.py b/selfdrive/car/subaru/interface.py index 0f2f4176c..0c71cb13e 100644 --- a/selfdrive/car/subaru/interface.py +++ b/selfdrive/car/subaru/interface.py @@ -49,6 +49,7 @@ class CarInterface(CarInterfaceBase): # returns a car.CarState def update(self, c, can_strings): + self.dp_load_params('subaru') self.cp.update_strings(can_strings) self.cp_cam.update_strings(can_strings) diff --git a/selfdrive/car/toyota/carcontroller.py b/selfdrive/car/toyota/carcontroller.py index 6062457ee..29fbcb339 100644 --- a/selfdrive/car/toyota/carcontroller.py +++ b/selfdrive/car/toyota/carcontroller.py @@ -71,6 +71,7 @@ class CarController(): self.dragon_lane_departure_warning = True self.dragon_toyota_sng_mod = False self.dp_last_modified = None + self.lane_change_enabled = True def update(self, enabled, CS, frame, actuators, pcm_cancel_cmd, hud_alert, left_line, right_line, lead, left_lane_depart, right_lane_depart): @@ -79,10 +80,17 @@ class CarController(): if frame % 500 == 0: modified = dp_get_last_modified() if self.dp_last_modified != modified: - self.dragon_enable_steering_on_signal = True if params.get("DragonEnableSteeringOnSignal", encoding='utf8') == "1" else False - self.dragon_lat_ctrl = False if params.get("DragonLatCtrl", encoding='utf8') == "0" else True self.dragon_lane_departure_warning = False if params.get("DragonToyotaLaneDepartureWarning", encoding='utf8') == "0" else True self.dragon_toyota_sng_mod = True if params.get("DragonToyotaSnGMod", encoding='utf8') == "1" else False + self.dragon_lat_ctrl = False if params.get("DragonLatCtrl", encoding='utf8') == "0" else True + if self.dragon_lat_ctrl: + self.lane_change_enabled = False if params.get("LaneChangeEnabled", encoding='utf8') == "1" else False + if not self.lane_change_enabled: + self.dragon_enable_steering_on_signal = True if params.get("DragonEnableSteeringOnSignal", encoding='utf8') == "1" else False + else: + self.dragon_enable_steering_on_signal = False + else: + self.dragon_enable_steering_on_signal = False self.dp_last_modified = modified # *** compute control surfaces *** @@ -137,7 +145,7 @@ class CarController(): # dragonpilot if enabled: if self.dragon_enable_steering_on_signal: - if CS.left_blinker_on == 0 and CS.right_blinker_on == 0: + if not CS.out.leftBlinker and not CS.out.rightBlinker: self.turning_signal_timer = 0 else: self.turning_signal_timer = 100 @@ -150,17 +158,6 @@ class CarController(): if not self.dragon_lat_ctrl: apply_steer_req = 0 - else: - if CS.v_ego > 12.5: - if right_lane_depart and not CS.right_blinker_on: - apply_steer = self.last_steer + 3 - apply_steer = min(apply_steer , 800) - apply_steer_req = 1 - - if left_lane_depart and not CS.left_blinker_on: - apply_steer = self.last_steer - 3 - apply_steer = max(apply_steer , -800) - apply_steer_req = 1 #*** control msgs *** #print("steer {0} {1} {2} {3}".format(apply_steer, min_lim, max_lim, CS.steer_torque_motor) diff --git a/selfdrive/car/toyota/carstate.py b/selfdrive/car/toyota/carstate.py index 2ec77405c..f22101640 100644 --- a/selfdrive/car/toyota/carstate.py +++ b/selfdrive/car/toyota/carstate.py @@ -5,7 +5,10 @@ from selfdrive.car.interfaces import CarStateBase from opendbc.can.parser import CANParser from selfdrive.config import Conversions as CV from selfdrive.car.toyota.values import CAR, DBC, STEER_THRESHOLD, TSS2_CAR, NO_STOP_TIMER_CAR - +from common.realtime import sec_since_boot +from common.params import Params +from selfdrive.dragonpilot.dragonconf import dp_get_last_modified +params = Params() class CarState(CarStateBase): def __init__(self, CP): @@ -19,10 +22,23 @@ class CarState(CarStateBase): # On NO_DSU cars but not TSS2 cars the cp.vl["STEER_TORQUE_SENSOR"]['STEER_ANGLE'] # is zeroed to where the steering angle is at start. # Need to apply an offset as soon as the steering angle measurements are both received - self.needs_angle_offset = CP.carFingerprint not in TSS2_CAR + self.needs_angle_offset = CP.carFingerprint not in TSS2_CAR or CP.carFingerprint == CAR.LEXUS_ISH self.angle_offset = 0. + # dragonpilot + self.dragon_toyota_stock_dsu = False + self.ts_last_check = 0. + self.last_modifed = None def update(self, cp, cp_cam): + # dragonpilot, don't check for param too often as it's a kernel call + ts = sec_since_boot() + if ts - self.ts_last_check >= 5.: + modified = dp_get_last_modified() + if self.last_modifed != modified: + self.dragon_toyota_stock_dsu = True if params.get("DragonToyotaStockDSU", encoding='utf8') == "1" else False + self.last_modifed = modified + self.ts_last_check = ts + ret = car.CarState.new_message() ret.doorOpen = any([cp.vl["SEATS_DOORS"]['DOOR_OPEN_FL'], cp.vl["SEATS_DOORS"]['DOOR_OPEN_FR'], @@ -34,8 +50,11 @@ class CarState(CarStateBase): if self.CP.enableGasInterceptor: ret.gas = (cp.vl["GAS_SENSOR"]['INTERCEPTOR_GAS'] + cp.vl["GAS_SENSOR"]['INTERCEPTOR_GAS2']) / 2. ret.gasPressed = ret.gas > 15 + elif self.CP.carFingerprint in [CAR.LEXUS_ISH, CAR.LEXUS_GSH]: + self.pedal_gas = cp.vl["GAS_PEDAL_ALT"]['GAS_PEDAL'] + ret.gasPressed = ret.gas > 1e-5 else: - ret.gas = cp.vl["GAS_PEDAL"]['GAS_PEDAL'] + self.pedal_gas = cp.vl["GAS_PEDAL"]['GAS_PEDAL'] ret.gasPressed = ret.gas > 1e-5 ret.wheelSpeeds.fl = cp.vl["WHEEL_SPEEDS"]['WHEEL_SPEED_FL'] * CV.KPH_TO_MS @@ -77,11 +96,19 @@ class CarState(CarStateBase): ret.cruiseState.available = cp.vl["DSU_CRUISE"]['MAIN_ON'] != 0 ret.cruiseState.speed = cp.vl["DSU_CRUISE"]['SET_SPEED'] * CV.KPH_TO_MS self.low_speed_lockout = False + elif self.CP.carFingerprint in [CAR.LEXUS_ISH, CAR.LEXUS_GSH]: + ret.cruiseState.available = cp.vl["PCM_CRUISE_ALT"]['MAIN_ON'] != 0 + ret.cruiseState.speed = cp.vl["PCM_CRUISE_ALT"]['SET_SPEED'] * CV.KPH_TO_MS + self.low_speed_lockout = False else: ret.cruiseState.available = cp.vl["PCM_CRUISE_2"]['MAIN_ON'] != 0 ret.cruiseState.speed = cp.vl["PCM_CRUISE_2"]['SET_SPEED'] * CV.KPH_TO_MS self.low_speed_lockout = cp.vl["PCM_CRUISE_2"]['LOW_SPEED_LOCKOUT'] == 2 - self.pcm_acc_status = cp.vl["PCM_CRUISE"]['CRUISE_STATE'] + if self.CP.carFingerprint in [CAR.LEXUS_ISH, CAR.LEXUS_GSH]: + # Lexus ISH does not have CRUISE_STATUS value (always 0), so we use CRUISE_ACTIVE value instead + self.pcm_acc_status = cp.vl["PCM_CRUISE"]['CRUISE_ACTIVE'] + else: + self.pcm_acc_status = cp.vl["PCM_CRUISE"]['CRUISE_STATE'] if self.CP.carFingerprint in NO_STOP_TIMER_CAR or self.CP.enableGasInterceptor: # ignore standstill in hybrid vehicles, since pcm allows to restart without # receiving any special command. Also if interceptor is detected @@ -92,6 +119,8 @@ class CarState(CarStateBase): if self.CP.carFingerprint == CAR.PRIUS: ret.genericToggle = cp.vl["AUTOPARK_STATUS"]['STATE'] != 0 + elif self.CP.carFingerprint in [CAR.LEXUS_ISH, CAR.LEXUS_GSH]: + ret.genericToggle = bool(cp.vl["LIGHT_STALK_ISH"]['AUTO_HIGH_BEAM']) else: ret.genericToggle = bool(cp.vl["LIGHT_STALK"]['AUTO_HIGH_BEAM']) ret.stockAeb = bool(cp_cam.vl["PRE_COLLISION"]["PRECOLLISION_ACTIVE"] and cp_cam.vl["PRE_COLLISION"]["FORCE"] < -1e-5) @@ -101,6 +130,12 @@ class CarState(CarStateBase): self.steer_state = cp.vl["EPS_STATUS"]['LKA_STATE'] self.steer_warning = cp.vl["EPS_STATUS"]['LKA_STATE'] not in [1, 5] + if self.dragon_toyota_stock_dsu and ret.genericToggle and ret.cruiseState.available: + enable_acc = True + if ret.seatbeltUnlatched or ret.doorOpen: + enable_acc = False + ret.cruiseState.enabled = enable_acc + return ret @staticmethod @@ -127,6 +162,7 @@ class CarState(CarStateBase): ("CRUISE_STATE", "PCM_CRUISE", 0), ("STEER_TORQUE_DRIVER", "STEER_TORQUE_SENSOR", 0), ("STEER_TORQUE_EPS", "STEER_TORQUE_SENSOR", 0), + ("STEER_ANGLE", "STEER_TORQUE_SENSOR", 0), ("TURN_SIGNALS", "STEERING_LEVERS", 3), # 3 is no blinkers ("LKA_STATE", "EPS_STATUS", 0), ("IPAS_STATE", "EPS_STATUS", 1), @@ -171,9 +207,6 @@ class CarState(CarStateBase): signals.append(("LOW_SPEED_LOCKOUT", "PCM_CRUISE_2", 0)) checks.append(("PCM_CRUISE_2", 33)) - if CP.carFingerprint in NO_DSU_CAR or CP.carFingerprint == CAR.LEXUS_ISH: - signals += [("STEER_ANGLE", "STEER_TORQUE_SENSOR", 0)] - if CP.carFingerprint == CAR.PRIUS: signals += [("STATE", "AUTOPARK_STATUS", 0)] diff --git a/selfdrive/car/toyota/interface.py b/selfdrive/car/toyota/interface.py index afe91c46b..152cae2bb 100644 --- a/selfdrive/car/toyota/interface.py +++ b/selfdrive/car/toyota/interface.py @@ -303,6 +303,7 @@ class CarInterface(CarInterfaceBase): # returns a car.CarState def update(self, c, can_strings): + self.dp_load_params('toyota') # ******************* do can recv ******************* self.cp.update_strings(can_strings) self.cp_cam.update_strings(can_strings) diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index 8c95b64a5..4c521ae35 100755 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -554,7 +554,10 @@ def controlsd_thread(sm=None, pm=None, can_sock=None): if dp_last_modified != modified: dragon_toyota_stock_dsu = True if params.get("DragonToyotaStockDSU", encoding='utf8') == "1" else False dragon_lat_control = False if params.get("DragonLatCtrl", encoding='utf8') == "0" else True - dragon_display_steering_limit_alert = False if params.get("DragonDisplaySteeringLimitAlert", encoding='utf8') == "0" else True + if dragon_lat_control: + dragon_display_steering_limit_alert = False if params.get("DragonDisplaySteeringLimitAlert", encoding='utf8') == "0" else True + else: + dragon_display_steering_limit_alert = False dragon_lead_car_moving_alert = True if params.get("DragonEnableLeadCarMovingAlert", encoding='utf8') == "1" else False dp_last_modified = modified ts_last_check = ts @@ -609,9 +612,9 @@ def controlsd_thread(sm=None, pm=None, can_sock=None): else: dragon_stopped_has_lead_count = 0 - # when we detect lead car over a sec and the lead car is started moving, we are ready to send alerts + # when we detect lead car over 3 secs and the lead car is started moving, we are ready to send alerts # once the condition is triggered, we want to keep the trigger - if dragon_stopped_has_lead_count >= 100: + if dragon_stopped_has_lead_count >= 300: if abs(sm['plan'].vTargetFuture) >= 0.1: events.append(create_event('leadCarMoving', [ET.WARNING])) else: diff --git a/selfdrive/controls/dmonitoringd.py b/selfdrive/controls/dmonitoringd.py index 762ee8011..0a6a3a792 100755 --- a/selfdrive/controls/dmonitoringd.py +++ b/selfdrive/controls/dmonitoringd.py @@ -59,20 +59,17 @@ def dmonitoringd_thread(sm=None, pm=None): # load driver monitor val only when safety is on if dp_enable_driver_safety_check: dp_enable_driver_monitoring = False if params.get("DragonEnableDriverMonitoring", encoding='utf8') == "0" else True - # load steering monitor timer val only when driver monitor is on - if dp_enable_driver_safety_check: try: dp_awareness_time = int(params.get("DragonSteeringMonitorTimer", encoding='utf8')) except TypeError: dp_awareness_time = 0. driver_status.awareness_time = 86400 if dp_awareness_time <= 0. else dp_awareness_time * 60. + else: + dp_enable_driver_monitoring = False + driver_status.awareness_time = 86400 dp_last_modified = modified last_ts = cur_time - if not dp_enable_driver_safety_check: - dp_enable_driver_monitoring = False - driver_status.awareness_time = 86400 - # reset all awareness val and set to rhd region, this will enforce steering monitor. if not dp_enable_driver_monitoring: driver_status.is_rhd_region = True diff --git a/selfdrive/controls/lib/pathplanner.py b/selfdrive/controls/lib/pathplanner.py index a42a5c8d4..03bb2b8a0 100644 --- a/selfdrive/controls/lib/pathplanner.py +++ b/selfdrive/controls/lib/pathplanner.py @@ -62,7 +62,6 @@ class PathPlanner(): # dragonpilot self.params = Params() - self.dragon_assisted_lc_enabled = False self.dragon_auto_lc_enabled = False self.dragon_auto_lc_allowed = False self.dragon_auto_lc_timer = None @@ -94,8 +93,8 @@ class PathPlanner(): if cur_time - self.last_ts >= 5.: modified = dp_get_last_modified() if self.dp_last_modified != modified: - self.dragon_assisted_lc_enabled = self.lane_change_enabled - if self.dragon_assisted_lc_enabled: + self.lane_change_enabled = True if self.params.get("LaneChangeEnabled", encoding='utf8') == "1" else False + if self.lane_change_enabled: self.dragon_auto_lc_enabled = True if self.params.get("DragonEnableAutoLC", encoding='utf8') == "1" else False # adjustable assisted lc min speed self.dragon_assisted_lc_min_mph = int(self.params.get("DragonAssistedLCMinMPH", encoding='utf8')) * CV.MPH_TO_MS @@ -113,6 +112,8 @@ class PathPlanner(): self.dragon_auto_lc_delay = int(self.params.get("DragonAutoLCDelay", encoding='utf8')) if self.dragon_auto_lc_delay < 0: self.dragon_auto_lc_delay = 0 + else: + self.dragon_auto_lc_enabled = False self.dp_last_modified = modified self.last_ts = cur_time diff --git a/selfdrive/dragonpilot/appd/appd.py b/selfdrive/dragonpilot/appd/appd.py index 86c97e282..8da5ff928 100644 --- a/selfdrive/dragonpilot/appd/appd.py +++ b/selfdrive/dragonpilot/appd/appd.py @@ -20,10 +20,6 @@ class App(): TYPE_FULLSCREEN = 3 TYPE_UTIL = 4 - # frame app - FRAME = "ai.comma.plus.frame" - FRAME_MAIN = ".MainActivity" - # offroad app OFFROAD = "ai.comma.plus.offroad" OFFROAD_MAIN = ".MainActivity" @@ -58,7 +54,7 @@ class App(): # read manual run param self.manual_ctrl_param = manual_ctrl_param # if it's a service app, we do not kill if device is too hot - # if it's a full screen app, we need to do extra process on frame/offroad + # if it's a full screen app, we need to do extra process on offroad self.app_type = app_type # app permissions self.permissions = permissions @@ -119,7 +115,6 @@ class App(): if force or not self.is_running: # if it's a full screen app, we need to stop frame and offroad to get keyboard access if self.app_type == self.TYPE_FULLSCREEN: - self.system("pm disable %s" % self.FRAME) self.system("am start -n %s/%s" % (self.OFFROAD, self.OFFROAD_MAIN)) self.system("pm enable %s" % self.app) @@ -143,12 +138,10 @@ class App(): # only kill app if it's running if force or self.is_running: - # if it's a full screen app, we need to restart offroad and frame + # if it's a full screen app, we need to restart offroad if self.app_type == self.TYPE_FULLSCREEN: self.system("pm disable %s" % self.OFFROAD) self.system("pm enable %s" % self.OFFROAD) - self.system("pm enable %s" % self.FRAME) - self.system("am start -n %s/%s" % (self.FRAME, self.FRAME_MAIN)) if self.app_type == self.TYPE_GPS_SERVICE: self.appops_set(self.app, "android:mock_location", "deny") diff --git a/selfdrive/dragonpilot/dragonconf/__init__.py b/selfdrive/dragonpilot/dragonconf/__init__.py index 9437308ba..c440cfb55 100644 --- a/selfdrive/dragonpilot/dragonconf/__init__.py +++ b/selfdrive/dragonpilot/dragonconf/__init__.py @@ -69,7 +69,7 @@ default_conf = { 'DragonBTG': 0, 'DragonBootHotspot': 0, 'DragonAccelProfile': '0', - 'DragonLastModified': str(floor(time.time())) + 'DragonLastModified': str(floor(time.time())), } deprecated_conf = { diff --git a/selfdrive/thermald/thermald.py b/selfdrive/thermald/thermald.py old mode 100644 new mode 100755 index e04f7a7c6..33d0af159 --- a/selfdrive/thermald/thermald.py +++ b/selfdrive/thermald/thermald.py @@ -17,7 +17,7 @@ from selfdrive.swaglog import cloudlog import cereal.messaging as messaging from selfdrive.loggerd.config import get_available_percent from selfdrive.pandad import get_expected_signature -from selfdrive.thermald.power_monitoring import PowerMonitoring, get_battery_capacity, get_battery_status, get_battery_current, get_battery_voltage, get_usb_present +from selfdrive.thermald.power_monitoring import PowerMonitoring, get_battery_capacity, get_battery_status, get_battery_current, get_battery_voltage, get_usb_present, set_battery_charging, get_battery_charging FW_SIGNATURE = get_expected_signature() @@ -443,12 +443,13 @@ def thermald_thread(): # we update charging status once every min if ts_last_charging_ctrl is None or ts - ts_last_charging_ctrl >= 60.: if dragon_charging_ctrl: - if msg.thermal.batteryPercent >= dragon_to_discharge: - os.system('echo "0" > /sys/class/power_supply/battery/charging_enabled') - if msg.thermal.batteryPercent <= dragon_to_charge: - os.system('echo "1" > /sys/class/power_supply/battery/charging_enabled') + if msg.thermal.batteryPercent >= dragon_to_discharge and get_battery_charging(): + set_battery_charging(False) + if msg.thermal.batteryPercent <= dragon_to_charge and not get_battery_charging(): + set_battery_charging(True) else: - os.system('echo "1" > /sys/class/power_supply/battery/charging_enabled') + if not get_battery_charging(): + set_battery_charging(True) ts_last_charging_ctrl = ts # report to server once per minute diff --git a/selfdrive/ui/sidebar.cc b/selfdrive/ui/sidebar.cc index 717449dee..cd5ce49b6 100644 --- a/selfdrive/ui/sidebar.cc +++ b/selfdrive/ui/sidebar.cc @@ -52,6 +52,20 @@ static void ui_draw_sidebar_network_strength(UIState *s, bool hasSidebar) { nvgFill(s->vg); } +static void ui_draw_sidebar_ip_addr(UIState *s, bool hasSidebar) { + const int network_ip_w = 176; + const int network_ip_x = hasSidebar ? 58 : -(sbr_w); + const int network_ip_y = 255; + + char network_ip_str[20]; + snprintf(network_ip_str, sizeof(network_ip_str), "%s", s->scene.ipAddr); + nvgFillColor(s->vg, COLOR_WHITE); + nvgFontSize(s->vg, 32); + nvgFontFace(s->vg, "sans-regular"); + nvgTextAlign(s->vg, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE); + nvgTextBox(s->vg, network_ip_x, network_ip_y, network_ip_w, network_ip_str, NULL); +} + static void ui_draw_sidebar_battery_icon(UIState *s, bool hasSidebar) { const int battery_img_h = 36; const int battery_img_w = 76; @@ -75,9 +89,24 @@ static void ui_draw_sidebar_battery_icon(UIState *s, bool hasSidebar) { nvgFill(s->vg); } +static void ui_draw_sidebar_battery_text(UIState *s, bool hasSidebar) { + const int battery_img_h = 36; + const int battery_img_w = 76; + const int battery_img_x = hasSidebar ? 150 : -(sbr_w); + const int battery_img_y = 305; + + char battery_str[6]; + snprintf(battery_str, sizeof(battery_str), "%d%%", s->scene.batteryPercent); + nvgFillColor(s->vg, strcmp(s->scene.batteryStatus, "Charging") == 0? COLOR_GREEN : s->scene.batteryPercent <= 50? COLOR_YELLOW : s->scene.batteryPercent <= 15? COLOR_RED : COLOR_WHITE); + nvgFontSize(s->vg, 40); + nvgFontFace(s->vg, "sans-regular"); + nvgTextAlign(s->vg, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE); + nvgTextBox(s->vg, battery_img_x, battery_img_y, battery_img_w, battery_str, NULL); +} + static void ui_draw_sidebar_network_type(UIState *s, bool hasSidebar) { const int network_x = hasSidebar ? 50 : -(sbr_w); - const int network_y = 273; + const int network_y = 303; const int network_w = 100; const int network_h = 100; const char *network_types[6] = {"--", "WiFi", "2G", "3G", "4G", "5G"}; @@ -200,20 +229,21 @@ static void ui_draw_sidebar_panda_metric(UIState *s, bool hasSidebar) { snprintf(panda_message_str, sizeof(panda_message_str), "%s", "NO PANDA"); } else if (s->scene.hwType == cereal_HealthData_HwType_whitePanda) { panda_severity = 0; - snprintf(panda_message_str, sizeof(panda_message_str), "%s", "PANDA ACTIVE"); + snprintf(panda_message_str, sizeof(panda_message_str), "%s", "PANDA\nACTIVE"); } else if ( (s->scene.hwType == cereal_HealthData_HwType_greyPanda) || (s->scene.hwType == cereal_HealthData_HwType_blackPanda) || (s->scene.hwType == cereal_HealthData_HwType_uno)) { if (s->scene.satelliteCount == -1) { panda_severity = 0; - snprintf(panda_message_str, sizeof(panda_message_str), "%s", "PANDA ACTIVE"); - } else if (s->scene.satelliteCount < 6) { - panda_severity = 1; - snprintf(panda_message_str, sizeof(panda_message_str), "%s", "PANDA\nNO GPS"); - } else if (s->scene.satelliteCount >= 6) { - panda_severity = 0; - snprintf(panda_message_str, sizeof(panda_message_str), "%s", "PANDA GOOD GPS"); + snprintf(panda_message_str, sizeof(panda_message_str), "%s", "PANDA\nACTIVE"); + } else { + if (s->scene.satelliteCount < 6) { + panda_severity = 1; + } else if (s->scene.satelliteCount >= 6) { + panda_severity = 0; + } + snprintf(panda_message_str, sizeof(panda_message_str), "%s %d", "PANDA\nGPS:", s->scene.satelliteCount); } } @@ -226,7 +256,8 @@ void ui_draw_sidebar(UIState *s) { ui_draw_sidebar_settings_button(s, hasSidebar); ui_draw_sidebar_home_button(s, hasSidebar); ui_draw_sidebar_network_strength(s, hasSidebar); - ui_draw_sidebar_battery_icon(s, hasSidebar); + ui_draw_sidebar_ip_addr(s, hasSidebar); + ui_draw_sidebar_battery_text(s, hasSidebar); ui_draw_sidebar_network_type(s, hasSidebar); ui_draw_sidebar_storage_metric(s, hasSidebar); ui_draw_sidebar_temp_metric(s, hasSidebar); diff --git a/selfdrive/ui/ui.cc b/selfdrive/ui/ui.cc index d3acfed5f..3c0a7c50d 100644 --- a/selfdrive/ui/ui.cc +++ b/selfdrive/ui/ui.cc @@ -524,6 +524,7 @@ void handle_message(UIState *s, Message * msg) { s->scene.freeSpace = datad.freeSpace; s->scene.thermalStatus = datad.thermalStatus; s->scene.paTemp = datad.pa0; + snprintf(s->scene.ipAddr, sizeof(s->scene.ipAddr), "%s", datad.ipAddr.str); } else if (eventd.which == cereal_Event_ubloxGnss) { struct cereal_UbloxGnss datad; cereal_read_UbloxGnss(&datad, eventd.ubloxGnss); diff --git a/selfdrive/ui/ui.hpp b/selfdrive/ui/ui.hpp index fb400f9ad..384f469c2 100644 --- a/selfdrive/ui/ui.hpp +++ b/selfdrive/ui/ui.hpp @@ -43,6 +43,7 @@ #define COLOR_WHITE_ALPHA nvgRGBA(255, 255, 255, 85) #define COLOR_YELLOW nvgRGBA(218, 202, 37, 255) #define COLOR_RED nvgRGBA(201, 34, 49, 255) +#define COLOR_GREEN nvgRGBA(34, 201, 49, 255) #ifndef QCOM #define UI_60FPS @@ -160,6 +161,7 @@ typedef struct UIScene { // for minimal UI float angleSteersDes; float angleSteers; + char ipAddr[20]; // for blinker, from kegman bool leftBlinker; diff --git a/selfdrive/updated.py b/selfdrive/updated.py old mode 100644 new mode 100755