mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-09-11 18:53:47 +08:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ac3fb6a4d8 | |||
| 79b791a26b | |||
| 1c35e376e9 |
@@ -10,8 +10,9 @@ from opendbc.car.lateral import apply_driver_steer_torque_limits, apply_steer_an
|
|||||||
from opendbc.car.common.conversions import Conversions as CV
|
from opendbc.car.common.conversions import Conversions as CV
|
||||||
from opendbc.car.hyundai import hyundaicanfd, hyundaican
|
from opendbc.car.hyundai import hyundaicanfd, hyundaican
|
||||||
from opendbc.car.hyundai.hyundaicanfd import CanBus
|
from opendbc.car.hyundai.hyundaicanfd import CanBus
|
||||||
from opendbc.car.hyundai.values import HyundaiFlags, HyundaiSafetyFlags, HyundaiStarPilotFlags, Buttons, CarControllerParams, CAR, CANFD_ANGLE_LONGITUDINAL_CAR, \
|
from opendbc.car.hyundai.values import HyundaiFlags, HyundaiSafetyFlags, HyundaiStarPilotFlags, Buttons, CarControllerParams, CAR, \
|
||||||
CANFD_RADAR_LIVE_LONGITUDINAL_CAR, CANFD_ALT_BUTTONS_RESUME_CAR, kia_ev6_gt_line_longitudinal_tuning, \
|
CANFD_ANGLE_LONGITUDINAL_CAR, CANFD_RADAR_ECU_KEEPALIVE_CAR, CANFD_ALT_BUTTONS_RESUME_CAR, \
|
||||||
|
kia_ev6_gt_line_longitudinal_tuning, \
|
||||||
KIA_EV6_GT_LINE_LONG_TUNING_TESTING_GROUND_ID
|
KIA_EV6_GT_LINE_LONG_TUNING_TESTING_GROUND_ID
|
||||||
from opendbc.car.interfaces import CarControllerBase
|
from opendbc.car.interfaces import CarControllerBase
|
||||||
from opendbc.car.vehicle_model import VehicleModel
|
from opendbc.car.vehicle_model import VehicleModel
|
||||||
@@ -859,10 +860,9 @@ class CarController(CarControllerBase):
|
|||||||
can_sends = []
|
can_sends = []
|
||||||
|
|
||||||
lka_steering = self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING
|
lka_steering = self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING
|
||||||
longitudinal_active = bool(self.long_active_ecu and getattr(CC, "longActive", False))
|
persistent_lfa_status_cars = (CAR.HYUNDAI_IONIQ_6, CAR.GENESIS_GV70_ELECTRIFIED_1ST_GEN, CAR.KIA_EV6)
|
||||||
lfa_status_cars = (CAR.HYUNDAI_IONIQ_6, CAR.GENESIS_GV70_ELECTRIFIED_1ST_GEN)
|
|
||||||
lfa_longitudinal_active = self.CP.openpilotLongitudinalControl \
|
lfa_longitudinal_active = self.CP.openpilotLongitudinalControl \
|
||||||
if self.CP.carFingerprint in lfa_status_cars else longitudinal_active
|
if self.CP.carFingerprint in persistent_lfa_status_cars else self.long_active_ecu
|
||||||
lka_steering_long = lka_steering and lfa_longitudinal_active
|
lka_steering_long = lka_steering and lfa_longitudinal_active
|
||||||
ccnc_non_hda2 = self.CP.flags & HyundaiFlags.CCNC and not lka_steering
|
ccnc_non_hda2 = self.CP.flags & HyundaiFlags.CCNC and not lka_steering
|
||||||
use_egmp_dynamic_long_tuning = egmp_dynamic_longitudinal_tuning(self.CP) and self.long_active_ecu and \
|
use_egmp_dynamic_long_tuning = egmp_dynamic_longitudinal_tuning(self.CP) and self.long_active_ecu and \
|
||||||
@@ -889,11 +889,10 @@ class CarController(CarControllerBase):
|
|||||||
|
|
||||||
gear = getattr(getattr(CS, "out", None), "gearShifter", None)
|
gear = getattr(getattr(CS, "out", None), "gearShifter", None)
|
||||||
drive_gear = gear == structs.CarState.GearShifter.drive
|
drive_gear = gear == structs.CarState.GearShifter.drive
|
||||||
if angle_lkas_alt:
|
if angle_lkas_alt and self.CP.carFingerprint != CAR.KIA_SPORTAGE_HEV_2026:
|
||||||
steering_msg_active = bool(steering_msg_active and drive_gear)
|
steering_msg_active = bool(steering_msg_active and drive_gear)
|
||||||
angle_lkas_alt_standstill_handoff = bool(getattr(CS.out, "standstill", False) and not CC.latActive)
|
angle_lkas_alt_standstill_handoff = bool(getattr(CS.out, "standstill", False) and not CC.latActive)
|
||||||
forward_stock_lkas = (self.CP.carFingerprint in CANFD_ANGLE_LONGITUDINAL_CAR or
|
forward_stock_lkas = self.CP.carFingerprint in CANFD_ANGLE_LONGITUDINAL_CAR and angle_lkas_alt and (
|
||||||
self.CP.carFingerprint == CAR.KIA_SPORTAGE_HEV_2026) and angle_lkas_alt and (
|
|
||||||
angle_lkas_alt_standstill_handoff or not (drive_gear and (CC.latActive or CC.enabled))
|
angle_lkas_alt_standstill_handoff or not (drive_gear and (CC.latActive or CC.enabled))
|
||||||
)
|
)
|
||||||
preserve_stock_lfa_status = preserve_stock_canfd_lfa_status(self.CP.carFingerprint)
|
preserve_stock_lfa_status = preserve_stock_canfd_lfa_status(self.CP.carFingerprint)
|
||||||
@@ -920,7 +919,7 @@ class CarController(CarControllerBase):
|
|||||||
|
|
||||||
# prevent LFA from activating on LKA steering cars by sending "no lane lines detected" to ADAS ECU
|
# prevent LFA from activating on LKA steering cars by sending "no lane lines detected" to ADAS ECU
|
||||||
suppress_lfa = bool(lka_steering)
|
suppress_lfa = bool(lka_steering)
|
||||||
if angle_lkas_alt:
|
if angle_lkas_alt and self.CP.carFingerprint != CAR.KIA_SPORTAGE_HEV_2026:
|
||||||
suppress_lfa = bool(lka_steering and drive_gear and (CC.latActive or (ccnc_angle_long and CC.enabled)))
|
suppress_lfa = bool(lka_steering and drive_gear and (CC.latActive or (ccnc_angle_long and CC.enabled)))
|
||||||
if self.frame % 5 == 0 and suppress_lfa:
|
if self.frame % 5 == 0 and suppress_lfa:
|
||||||
can_sends.append(hyundaicanfd.create_suppress_lfa(self.packer, self.CAN, CS.lfa_block_msg,
|
can_sends.append(hyundaicanfd.create_suppress_lfa(self.packer, self.CAN, CS.lfa_block_msg,
|
||||||
@@ -995,7 +994,7 @@ class CarController(CarControllerBase):
|
|||||||
# The front radar treats ADAS_DRV's 0x100 broadcast as its host heartbeat
|
# The front radar treats ADAS_DRV's 0x100 broadcast as its host heartbeat
|
||||||
# and stops publishing object tracks when it disappears.
|
# and stops publishing object tracks when it disappears.
|
||||||
radar_heartbeat_step = 1 if ccnc_angle_long else 4
|
radar_heartbeat_step = 1 if ccnc_angle_long else 4
|
||||||
if self.CP.carFingerprint in CANFD_RADAR_LIVE_LONGITUDINAL_CAR and self.frame % radar_heartbeat_step == 0:
|
if self.CP.carFingerprint in CANFD_RADAR_ECU_KEEPALIVE_CAR and self.frame % radar_heartbeat_step == 0:
|
||||||
can_sends.append(hyundaicanfd.create_accelerator_brake_alt_spoof(0, self.frame // radar_heartbeat_step,
|
can_sends.append(hyundaicanfd.create_accelerator_brake_alt_spoof(0, self.frame // radar_heartbeat_step,
|
||||||
CS.out.brakePressed, CS.out.gasPressed,
|
CS.out.brakePressed, CS.out.gasPressed,
|
||||||
self.CP.carFingerprint))
|
self.CP.carFingerprint))
|
||||||
|
|||||||
@@ -142,7 +142,21 @@ def create_steering_messages(packer, CP, CAN, enabled, lat_active, apply_torque,
|
|||||||
lkas_values["LKAS_ANGLE_ACTIVE"] = 2 if lat_active else 1
|
lkas_values["LKAS_ANGLE_ACTIVE"] = 2 if lat_active else 1
|
||||||
lkas_values["ADAS_ACIAnglTqRedcGainVal"] = apply_torque if lat_active else 0.0
|
lkas_values["ADAS_ACIAnglTqRedcGainVal"] = apply_torque if lat_active else 0.0
|
||||||
if angle_lkas_alt:
|
if angle_lkas_alt:
|
||||||
if lat_active:
|
if CP.carFingerprint == CAR.KIA_SPORTAGE_HEV_2026:
|
||||||
|
lkas_values = {
|
||||||
|
"LKA_OptUsmSta": 0,
|
||||||
|
"LKA_SysIndReq": 2 if enabled else 1,
|
||||||
|
"StrTqReqVal": 0,
|
||||||
|
"LKA_SysWrn": 0,
|
||||||
|
"ActToiSta": 0,
|
||||||
|
"LKA_UsmMod": 0,
|
||||||
|
"LKA_RcgSta": 3 if lat_active else 0,
|
||||||
|
"Damping_Gain": 100,
|
||||||
|
"ADAS_StrAnglReqVal": apply_angle,
|
||||||
|
"LKAS_ANGLE_ACTIVE": 2 if lat_active else 1,
|
||||||
|
"ADAS_ACIAnglTqRedcGainVal": apply_torque if lat_active else 0.0,
|
||||||
|
}
|
||||||
|
elif lat_active:
|
||||||
lkas_values = {
|
lkas_values = {
|
||||||
"LKA_OptUsmSta": 0,
|
"LKA_OptUsmSta": 0,
|
||||||
"LKA_RcgSta": 3,
|
"LKA_RcgSta": 3,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from opendbc.car.hyundai.values import HyundaiFlags, CAR, CarControllerParams, \
|
|||||||
CANFD_UNSUPPORTED_LONGITUDINAL_CAR, \
|
CANFD_UNSUPPORTED_LONGITUDINAL_CAR, \
|
||||||
CANFD_SECURITYACCESS_CAR, \
|
CANFD_SECURITYACCESS_CAR, \
|
||||||
CANFD_ANGLE_LONGITUDINAL_CAR, \
|
CANFD_ANGLE_LONGITUDINAL_CAR, \
|
||||||
CANFD_RADAR_LIVE_LONGITUDINAL_CAR, \
|
CANFD_RADAR_ECU_KEEPALIVE_CAR, \
|
||||||
RADAR_LIVE_LONGITUDINAL_CAR, \
|
RADAR_LIVE_LONGITUDINAL_CAR, \
|
||||||
UNSUPPORTED_LONGITUDINAL_CAR, HyundaiSafetyFlags, \
|
UNSUPPORTED_LONGITUDINAL_CAR, HyundaiSafetyFlags, \
|
||||||
LEGACY_LONGITUDINAL_CAR, \
|
LEGACY_LONGITUDINAL_CAR, \
|
||||||
@@ -27,6 +27,15 @@ from openpilot.starpilot.common.testing_grounds import testing_ground
|
|||||||
ButtonType = structs.CarState.ButtonEvent.Type
|
ButtonType = structs.CarState.ButtonEvent.Type
|
||||||
Ecu = structs.CarParams.Ecu
|
Ecu = structs.CarParams.Ecu
|
||||||
|
|
||||||
|
|
||||||
|
def get_communication_control_request(car_fingerprint):
|
||||||
|
if car_fingerprint in CANFD_RADAR_ECU_KEEPALIVE_CAR:
|
||||||
|
return bytes([uds.SERVICE_TYPE.COMMUNICATION_CONTROL, uds.CONTROL_TYPE.ENABLE_RX_DISABLE_TX,
|
||||||
|
uds.MESSAGE_TYPE.NORMAL])
|
||||||
|
|
||||||
|
return bytes([uds.SERVICE_TYPE.COMMUNICATION_CONTROL, 0x80 | uds.CONTROL_TYPE.DISABLE_RX_DISABLE_TX,
|
||||||
|
uds.MESSAGE_TYPE.NORMAL])
|
||||||
|
|
||||||
# Cancel button can sometimes be ACC pause/resume button, main button can also enable on some cars
|
# Cancel button can sometimes be ACC pause/resume button, main button can also enable on some cars
|
||||||
ENABLE_BUTTONS = (ButtonType.accelCruise, ButtonType.decelCruise, ButtonType.cancel, ButtonType.mainCruise)
|
ENABLE_BUTTONS = (ButtonType.accelCruise, ButtonType.decelCruise, ButtonType.cancel, ButtonType.mainCruise)
|
||||||
|
|
||||||
@@ -353,14 +362,7 @@ class CarInterface(CarInterfaceBase):
|
|||||||
params = Params()
|
params = Params()
|
||||||
|
|
||||||
if communication_control is None:
|
if communication_control is None:
|
||||||
if CP.carFingerprint in CANFD_RADAR_LIVE_LONGITUDINAL_CAR:
|
communication_control = get_communication_control_request(CP.carFingerprint)
|
||||||
# Don't use 0x80 suppress bit so we can read the ECU response.
|
|
||||||
# Use ENABLE_RX_DISABLE_TX (0x01) so the ECU can still receive from rear radars for BSM
|
|
||||||
# while blocking SCC TX.
|
|
||||||
communication_control = bytes([uds.SERVICE_TYPE.COMMUNICATION_CONTROL, uds.CONTROL_TYPE.ENABLE_RX_DISABLE_TX, uds.MESSAGE_TYPE.NORMAL])
|
|
||||||
else:
|
|
||||||
# 0x80 silences response for other cars (original behavior)
|
|
||||||
communication_control = bytes([uds.SERVICE_TYPE.COMMUNICATION_CONTROL, 0x80 | uds.CONTROL_TYPE.DISABLE_RX_DISABLE_TX, uds.MESSAGE_TYPE.NORMAL])
|
|
||||||
|
|
||||||
ecu_log(f"=== init() called: opLong={CP.openpilotLongitudinalControl}, flags=0x{CP.flags:x}, safetyParam={CP.safetyConfigs[-1].safetyParam} ===")
|
ecu_log(f"=== init() called: opLong={CP.openpilotLongitudinalControl}, flags=0x{CP.flags:x}, safetyParam={CP.safetyConfigs[-1].safetyParam} ===")
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from types import SimpleNamespace
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from opendbc.can import CANPacker, CANParser
|
from opendbc.can import CANPacker, CANParser
|
||||||
from opendbc.car import Bus, ButtonType, gen_empty_fingerprint, structs
|
from opendbc.car import Bus, ButtonType, gen_empty_fingerprint, structs, uds
|
||||||
from opendbc.car.structs import CarControl, CarParams
|
from opendbc.car.structs import CarControl, CarParams
|
||||||
from opendbc.car.fw_versions import build_fw_dict, match_fw_to_car
|
from opendbc.car.fw_versions import build_fw_dict, match_fw_to_car
|
||||||
from opendbc.car.hyundai.carcontroller import CarController, CANCEL_BUTTON_DELAY_FRAMES, Ioniq6LongitudinalTuningState, GenesisG90LongitudinalTuningState, \
|
from opendbc.car.hyundai.carcontroller import CarController, CANCEL_BUTTON_DELAY_FRAMES, Ioniq6LongitudinalTuningState, GenesisG90LongitudinalTuningState, \
|
||||||
@@ -24,7 +24,7 @@ from opendbc.car.hyundai.carcontroller import CarController, CANCEL_BUTTON_DELAY
|
|||||||
clear_ioniq_6_torque_when_request_inactive
|
clear_ioniq_6_torque_when_request_inactive
|
||||||
from opendbc.car.hyundai.carstate import CarState, decode_canfd_camera_lead, decode_ioniq_6_blindspot_radar_state, \
|
from opendbc.car.hyundai.carstate import CarState, decode_canfd_camera_lead, decode_ioniq_6_blindspot_radar_state, \
|
||||||
get_canfd_cruise_available
|
get_canfd_cruise_available
|
||||||
from opendbc.car.hyundai.interface import CarInterface, KIA_EV9_ACCEL_MAX
|
from opendbc.car.hyundai.interface import CarInterface, KIA_EV9_ACCEL_MAX, get_communication_control_request
|
||||||
from opendbc.car.hyundai import hyundaican, hyundaicanfd
|
from opendbc.car.hyundai import hyundaican, hyundaicanfd
|
||||||
from opendbc.car.hyundai.hyundaicanfd import CanBus, hkg_can_fd_checksum
|
from opendbc.car.hyundai.hyundaicanfd import CanBus, hkg_can_fd_checksum
|
||||||
from opendbc.car.hyundai.radar_interface import MRREVO14F_RADAR_START_ADDR, MRR30_RADAR_START_ADDR, MRR35_RADAR_START_ADDR, \
|
from opendbc.car.hyundai.radar_interface import MRREVO14F_RADAR_START_ADDR, MRR30_RADAR_START_ADDR, MRR35_RADAR_START_ADDR, \
|
||||||
@@ -129,6 +129,19 @@ def get_test_toggles() -> SimpleNamespace:
|
|||||||
|
|
||||||
|
|
||||||
class TestHyundaiFingerprint:
|
class TestHyundaiFingerprint:
|
||||||
|
@pytest.mark.parametrize("car", [
|
||||||
|
CAR.HYUNDAI_IONIQ_5,
|
||||||
|
CAR.KIA_EV6,
|
||||||
|
CAR.GENESIS_GV70_ELECTRIFIED_1ST_GEN,
|
||||||
|
])
|
||||||
|
def test_hda2_cars_without_radar_keepalive_use_stock_communication_control(self, car):
|
||||||
|
stock_request = bytes([uds.SERVICE_TYPE.COMMUNICATION_CONTROL, 0x83, uds.MESSAGE_TYPE.NORMAL])
|
||||||
|
radar_keepalive_request = bytes([uds.SERVICE_TYPE.COMMUNICATION_CONTROL,
|
||||||
|
uds.CONTROL_TYPE.ENABLE_RX_DISABLE_TX, uds.MESSAGE_TYPE.NORMAL])
|
||||||
|
|
||||||
|
assert get_communication_control_request(car) == stock_request
|
||||||
|
assert get_communication_control_request(CAR.HYUNDAI_IONIQ_6) == radar_keepalive_request
|
||||||
|
|
||||||
def test_carnival_hev_low_speed_torque_rate_limits(self):
|
def test_carnival_hev_low_speed_torque_rate_limits(self):
|
||||||
CP = CarInterface.get_params(CAR.KIA_CARNIVAL_HEV_4TH_GEN, gen_empty_fingerprint(), [],
|
CP = CarInterface.get_params(CAR.KIA_CARNIVAL_HEV_4TH_GEN, gen_empty_fingerprint(), [],
|
||||||
False, False, False, None)
|
False, False, False, None)
|
||||||
@@ -2552,9 +2565,10 @@ class TestHyundaiFingerprint:
|
|||||||
if controller.packer.dbc.addr_to_msg[addr].name in ("LFA", "LKAS")]
|
if controller.packer.dbc.addr_to_msg[addr].name in ("LFA", "LKAS")]
|
||||||
assert steering_names == [("LFA", can_bus.ECAN), ("LKAS", can_bus.ACAN)]
|
assert steering_names == [("LFA", can_bus.ECAN), ("LKAS", can_bus.ACAN)]
|
||||||
|
|
||||||
def test_ioniq_6_keeps_lfa_status_when_longitudinal_is_inactive(self):
|
@pytest.mark.parametrize("car", [CAR.HYUNDAI_IONIQ_6, CAR.KIA_EV6, CAR.GENESIS_GV70_ELECTRIFIED_1ST_GEN])
|
||||||
|
def test_egmp_keeps_lfa_status_when_longitudinal_is_inactive(self, car):
|
||||||
CP = CarParams.new_message()
|
CP = CarParams.new_message()
|
||||||
CP.carFingerprint = CAR.HYUNDAI_IONIQ_6
|
CP.carFingerprint = car
|
||||||
CP.flags = int(HyundaiFlags.CANFD | HyundaiFlags.EV | HyundaiFlags.CANFD_LKA_STEERING)
|
CP.flags = int(HyundaiFlags.CANFD | HyundaiFlags.EV | HyundaiFlags.CANFD_LKA_STEERING)
|
||||||
CP.openpilotLongitudinalControl = True
|
CP.openpilotLongitudinalControl = True
|
||||||
|
|
||||||
@@ -2575,6 +2589,38 @@ class TestHyundaiFingerprint:
|
|||||||
cc.hudControl, cs, cc, get_test_toggles(), lka_icon=1, lfa_icon=1)
|
cc.hudControl, cs, cc, get_test_toggles(), lka_icon=1, lfa_icon=1)
|
||||||
assert any(addr == 0x12A for addr, _, _ in msgs)
|
assert any(addr == 0x12A for addr, _, _ in msgs)
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(("car", "powertrain_flag"), [
|
||||||
|
(CAR.HYUNDAI_IONIQ_5, HyundaiFlags.EV),
|
||||||
|
(CAR.KIA_CARNIVAL_2025, 0),
|
||||||
|
(CAR.KIA_CARNIVAL_HEV_4TH_GEN, HyundaiFlags.HYBRID),
|
||||||
|
])
|
||||||
|
def test_hda2_keeps_lfa_status_while_longitudinal_ecu_is_disabled(self, car, powertrain_flag):
|
||||||
|
CP = CarParams.new_message()
|
||||||
|
CP.carFingerprint = car
|
||||||
|
CP.flags = int(HyundaiFlags.CANFD | HyundaiFlags.CANFD_LKA_STEERING | powertrain_flag)
|
||||||
|
CP.openpilotLongitudinalControl = True
|
||||||
|
|
||||||
|
controller = CarController(DBC[CP.carFingerprint], CP)
|
||||||
|
controller.frame = 1
|
||||||
|
controller.long_active_ecu = True
|
||||||
|
cc = SimpleNamespace(
|
||||||
|
enabled=False, latActive=False, longActive=False,
|
||||||
|
actuators=SimpleNamespace(longControlState=LongCtrlState.off),
|
||||||
|
leftBlinker=False, rightBlinker=False, hudControl=SimpleNamespace(),
|
||||||
|
)
|
||||||
|
cs = SimpleNamespace(
|
||||||
|
stock_lfa_msg=None, stock_lkas_msg=None,
|
||||||
|
left_blindspot_from_radar=False, right_blindspot_from_radar=False,
|
||||||
|
out=SimpleNamespace(
|
||||||
|
brakePressed=False, gasPressed=False,
|
||||||
|
gearShifter=structs.CarState.GearShifter.park,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
msgs = controller.create_canfd_msgs(0, False, 0.0, 0.0, 0.0, 0.0, False,
|
||||||
|
cc.hudControl, cs, cc, get_test_toggles(), lka_icon=1, lfa_icon=1)
|
||||||
|
assert any(addr == 0x12A for addr, _, _ in msgs)
|
||||||
|
|
||||||
def test_gv70_electrified_longitudinal_uses_hda2_scc_contract(self):
|
def test_gv70_electrified_longitudinal_uses_hda2_scc_contract(self):
|
||||||
CP = CarParams.new_message()
|
CP = CarParams.new_message()
|
||||||
CP.carFingerprint = CAR.GENESIS_GV70_ELECTRIFIED_1ST_GEN
|
CP.carFingerprint = CAR.GENESIS_GV70_ELECTRIFIED_1ST_GEN
|
||||||
@@ -2707,7 +2753,7 @@ class TestHyundaiFingerprint:
|
|||||||
assert len([msg for msg in msgs if msg[0] == 0x110]) == expected_lkas_msgs
|
assert len([msg for msg in msgs if msg[0] == 0x110]) == expected_lkas_msgs
|
||||||
|
|
||||||
@pytest.mark.parametrize("standstill", [False, True])
|
@pytest.mark.parametrize("standstill", [False, True])
|
||||||
def test_sportage_angle_lkas_alt_forwards_stock_status_when_inactive(self, standstill):
|
def test_sportage_angle_lkas_alt_keeps_status_and_suppression_alive(self, standstill):
|
||||||
CP = CarParams.new_message()
|
CP = CarParams.new_message()
|
||||||
CP.carFingerprint = CAR.KIA_SPORTAGE_HEV_2026
|
CP.carFingerprint = CAR.KIA_SPORTAGE_HEV_2026
|
||||||
CP.flags = int(HyundaiFlags.CANFD | HyundaiFlags.HYBRID | HyundaiFlags.CANFD_ANGLE_STEERING |
|
CP.flags = int(HyundaiFlags.CANFD | HyundaiFlags.HYBRID | HyundaiFlags.CANFD_ANGLE_STEERING |
|
||||||
@@ -2715,16 +2761,67 @@ class TestHyundaiFingerprint:
|
|||||||
CP.openpilotLongitudinalControl = False
|
CP.openpilotLongitudinalControl = False
|
||||||
|
|
||||||
controller = CarController(DBC[CP.carFingerprint], CP)
|
controller = CarController(DBC[CP.carFingerprint], CP)
|
||||||
|
controller.frame = 5
|
||||||
|
can_bus = CanBus(CP)
|
||||||
cc = SimpleNamespace(enabled=False, latActive=False,
|
cc = SimpleNamespace(enabled=False, latActive=False,
|
||||||
actuators=SimpleNamespace(longControlState=LongCtrlState.off),
|
actuators=SimpleNamespace(longControlState=LongCtrlState.off),
|
||||||
leftBlinker=False, rightBlinker=False, hudControl=SimpleNamespace())
|
leftBlinker=False, rightBlinker=False, hudControl=SimpleNamespace())
|
||||||
cs = SimpleNamespace(stock_lfa_msg=None, stock_lkas_msg={},
|
lfa_block_msg = {f"BYTE{i}": 0 for i in range(3, 32) if i != 7}
|
||||||
|
lfa_block_msg["COUNTER"] = 0
|
||||||
|
cs = SimpleNamespace(stock_lfa_msg=None, stock_lkas_msg={}, lfa_block_msg=lfa_block_msg,
|
||||||
out=SimpleNamespace(standstill=standstill, steeringAngleDeg=0.0,
|
out=SimpleNamespace(standstill=standstill, steeringAngleDeg=0.0,
|
||||||
gearShifter=structs.CarState.GearShifter.drive))
|
gearShifter=structs.CarState.GearShifter.drive))
|
||||||
|
|
||||||
msgs = controller.create_canfd_msgs(0, False, 0.0, 0.0, 0.0, 0.0, False, cc.hudControl, cs, cc,
|
msgs = controller.create_canfd_msgs(0, False, 0.0, 0.0, 0.0, 0.0, False, cc.hudControl, cs, cc,
|
||||||
get_test_toggles(), lka_icon=1, lfa_icon=1)
|
get_test_toggles(), lka_icon=1, lfa_icon=1)
|
||||||
assert not [msg for msg in msgs if msg[0] in (0x110, 0x12A)]
|
lkas_msgs = [msg for msg in msgs if msg[0] == 0x110]
|
||||||
|
assert len(lkas_msgs) == 1
|
||||||
|
assert len([msg for msg in msgs if msg[0] == 0x362]) == 1
|
||||||
|
|
||||||
|
parser = CANParser(DBC[CP.carFingerprint][Bus.pt], [("LKAS_ALT", 0)], can_bus.ACAN)
|
||||||
|
parser.update([(1, lkas_msgs)])
|
||||||
|
assert parser.can_valid
|
||||||
|
assert parser.vl["LKAS_ALT"]["LKA_OptUsmSta"] == 0
|
||||||
|
assert parser.vl["LKAS_ALT"]["LKA_SysIndReq"] == 1
|
||||||
|
assert parser.vl["LKAS_ALT"]["LKA_RcgSta"] == 0
|
||||||
|
assert parser.vl["LKAS_ALT"]["Damping_Gain"] == 100
|
||||||
|
assert parser.vl["LKAS_ALT"]["LKAS_ANGLE_ACTIVE"] == 1
|
||||||
|
assert parser.vl["LKAS_ALT"]["ADAS_ACIAnglTqRedcGainVal"] == 0.0
|
||||||
|
|
||||||
|
def test_sportage_angle_lkas_alt_active_status_matches_vehicle_contract(self):
|
||||||
|
CP = CarParams.new_message()
|
||||||
|
CP.carFingerprint = CAR.KIA_SPORTAGE_HEV_2026
|
||||||
|
CP.flags = int(HyundaiFlags.CANFD | HyundaiFlags.HYBRID | HyundaiFlags.CANFD_ANGLE_STEERING |
|
||||||
|
HyundaiFlags.CANFD_LKA_STEERING | HyundaiFlags.CANFD_LKA_STEERING_ALT)
|
||||||
|
CP.openpilotLongitudinalControl = False
|
||||||
|
|
||||||
|
controller = CarController(DBC[CP.carFingerprint], CP)
|
||||||
|
controller.frame = 5
|
||||||
|
can_bus = CanBus(CP)
|
||||||
|
cc = SimpleNamespace(enabled=True, latActive=True,
|
||||||
|
actuators=SimpleNamespace(longControlState=LongCtrlState.off),
|
||||||
|
leftBlinker=False, rightBlinker=False, hudControl=SimpleNamespace())
|
||||||
|
lfa_block_msg = {f"BYTE{i}": 0 for i in range(3, 32) if i != 7}
|
||||||
|
lfa_block_msg["COUNTER"] = 0
|
||||||
|
cs = SimpleNamespace(stock_lfa_msg=None, stock_lkas_msg={}, lfa_block_msg=lfa_block_msg,
|
||||||
|
out=SimpleNamespace(standstill=False, steeringAngleDeg=10.0,
|
||||||
|
gearShifter=structs.CarState.GearShifter.drive))
|
||||||
|
|
||||||
|
msgs = controller.create_canfd_msgs(0, True, 0.4, 12.0, 0.0, 0.0, False, cc.hudControl, cs, cc,
|
||||||
|
get_test_toggles(), lka_icon=2, lfa_icon=2)
|
||||||
|
lkas_msgs = [msg for msg in msgs if msg[0] == 0x110]
|
||||||
|
assert len(lkas_msgs) == 1
|
||||||
|
assert len([msg for msg in msgs if msg[0] == 0x362]) == 1
|
||||||
|
|
||||||
|
parser = CANParser(DBC[CP.carFingerprint][Bus.pt], [("LKAS_ALT", 0)], can_bus.ACAN)
|
||||||
|
parser.update([(1, lkas_msgs)])
|
||||||
|
assert parser.can_valid
|
||||||
|
assert parser.vl["LKAS_ALT"]["LKA_OptUsmSta"] == 0
|
||||||
|
assert parser.vl["LKAS_ALT"]["LKA_SysIndReq"] == 2
|
||||||
|
assert parser.vl["LKAS_ALT"]["LKA_RcgSta"] == 3
|
||||||
|
assert parser.vl["LKAS_ALT"]["Damping_Gain"] == 100
|
||||||
|
assert parser.vl["LKAS_ALT"]["LKAS_ANGLE_ACTIVE"] == 2
|
||||||
|
assert parser.vl["LKAS_ALT"]["ADAS_ACIAnglTqRedcGainVal"] == pytest.approx(0.4)
|
||||||
|
|
||||||
def test_ev9_inactive_angle_steering_does_not_suppress_stock_lfa(self):
|
def test_ev9_inactive_angle_steering_does_not_suppress_stock_lfa(self):
|
||||||
CP = CarParams.new_message()
|
CP = CarParams.new_message()
|
||||||
|
|||||||
@@ -1218,6 +1218,13 @@ CANFD_ALT_BUTTONS_RESUME_CAR = {CAR.KIA_CARNIVAL_2025, CAR.KIA_CARNIVAL_HEV_4TH_
|
|||||||
CANFD_CORNER_RADAR_BSM_CAR = {CAR.HYUNDAI_IONIQ_6, CAR.HYUNDAI_IONIQ_5_PE, CAR.KIA_EV9}
|
CANFD_CORNER_RADAR_BSM_CAR = {CAR.HYUNDAI_IONIQ_6, CAR.HYUNDAI_IONIQ_5_PE, CAR.KIA_EV9}
|
||||||
CANFD_RADAR_LIVE_LONGITUDINAL_CAR = {
|
CANFD_RADAR_LIVE_LONGITUDINAL_CAR = {
|
||||||
CAR.HYUNDAI_IONIQ_5, CAR.HYUNDAI_IONIQ_5_PE, CAR.HYUNDAI_IONIQ_6, CAR.KIA_EV6, CAR.KIA_EV9, CAR.GENESIS_GV60_EV_1ST_GEN,
|
CAR.HYUNDAI_IONIQ_5, CAR.HYUNDAI_IONIQ_5_PE, CAR.HYUNDAI_IONIQ_6, CAR.KIA_EV6, CAR.KIA_EV9, CAR.GENESIS_GV60_EV_1ST_GEN,
|
||||||
|
CAR.GENESIS_GV70_ELECTRIFIED_1ST_GEN,
|
||||||
|
}
|
||||||
|
CANFD_RADAR_ECU_KEEPALIVE_CAR = {
|
||||||
|
CAR.HYUNDAI_IONIQ_5_PE,
|
||||||
|
CAR.HYUNDAI_IONIQ_6,
|
||||||
|
CAR.KIA_EV9,
|
||||||
|
CAR.GENESIS_GV60_EV_1ST_GEN,
|
||||||
}
|
}
|
||||||
RADAR_LIVE_LONGITUDINAL_CAR = CANFD_RADAR_LIVE_LONGITUDINAL_CAR | {
|
RADAR_LIVE_LONGITUDINAL_CAR = CANFD_RADAR_LIVE_LONGITUDINAL_CAR | {
|
||||||
CAR.HYUNDAI_IONIQ,
|
CAR.HYUNDAI_IONIQ,
|
||||||
|
|||||||
@@ -404,7 +404,6 @@ class Controls:
|
|||||||
self.turn_blinker_swept = 0.0
|
self.turn_blinker_swept = 0.0
|
||||||
self.twitch_guard_remaining = 0.0
|
self.twitch_guard_remaining = 0.0
|
||||||
self.kona_non_scc_lateral_active = False
|
self.kona_non_scc_lateral_active = False
|
||||||
self.kona_non_scc_lateral_faulted = False
|
|
||||||
self.elantra_hev_2024_lateral_faulted = False
|
self.elantra_hev_2024_lateral_faulted = False
|
||||||
self.elantra_hev_2024_previous_cruise_enabled = False
|
self.elantra_hev_2024_previous_cruise_enabled = False
|
||||||
|
|
||||||
@@ -504,11 +503,6 @@ class Controls:
|
|||||||
standstill = abs(CS.vEgo) <= max(self.CP.minSteerSpeed, 0.3) or CS.standstill
|
standstill = abs(CS.vEgo) <= max(self.CP.minSteerSpeed, 0.3) or CS.standstill
|
||||||
if self.CP.carFingerprint == HYUNDAI_CAR.HYUNDAI_KONA_NON_SCC:
|
if self.CP.carFingerprint == HYUNDAI_CAR.HYUNDAI_KONA_NON_SCC:
|
||||||
always_on_lateral_enabled = self.sm['starpilotCarState'].alwaysOnLateralEnabled
|
always_on_lateral_enabled = self.sm['starpilotCarState'].alwaysOnLateralEnabled
|
||||||
lateral_requested = (CC.enabled and self.sm['selfdriveState'].active) or always_on_lateral_enabled
|
|
||||||
if not lateral_requested:
|
|
||||||
self.kona_non_scc_lateral_faulted = False
|
|
||||||
elif CS.steerFaultTemporary:
|
|
||||||
self.kona_non_scc_lateral_faulted = True
|
|
||||||
CC.latActive = get_kona_non_scc_lateral_active(
|
CC.latActive = get_kona_non_scc_lateral_active(
|
||||||
CC.enabled, self.sm['selfdriveState'].active,
|
CC.enabled, self.sm['selfdriveState'].active,
|
||||||
always_on_lateral_enabled,
|
always_on_lateral_enabled,
|
||||||
@@ -516,7 +510,6 @@ class Controls:
|
|||||||
standstill, self.CP.steerAtStandstill,
|
standstill, self.CP.steerAtStandstill,
|
||||||
self.sm['starpilotPlan'].lateralCheck,
|
self.sm['starpilotPlan'].lateralCheck,
|
||||||
CS.steeringPressed, self.kona_non_scc_lateral_active,
|
CS.steeringPressed, self.kona_non_scc_lateral_active,
|
||||||
self.kona_non_scc_lateral_faulted,
|
|
||||||
)
|
)
|
||||||
self.kona_non_scc_lateral_active = CC.latActive
|
self.kona_non_scc_lateral_active = CC.latActive
|
||||||
elif self.CP.carFingerprint == HYUNDAI_CAR.HYUNDAI_ELANTRA_HEV_2024:
|
elif self.CP.carFingerprint == HYUNDAI_CAR.HYUNDAI_ELANTRA_HEV_2024:
|
||||||
|
|||||||
@@ -89,13 +89,12 @@ def update_lateral_fault_latch(previous_latched: bool, lateral_requested: bool,
|
|||||||
def get_kona_non_scc_lateral_active(enabled: bool, active: bool, always_on_lateral_enabled: bool,
|
def get_kona_non_scc_lateral_active(enabled: bool, active: bool, always_on_lateral_enabled: bool,
|
||||||
steer_fault_temporary: bool, steer_fault_permanent: bool,
|
steer_fault_temporary: bool, steer_fault_permanent: bool,
|
||||||
standstill: bool, steer_at_standstill: bool, lateral_check: bool,
|
standstill: bool, steer_at_standstill: bool, lateral_check: bool,
|
||||||
steering_pressed: bool, previous_lateral_active: bool,
|
steering_pressed: bool, previous_lateral_active: bool) -> bool:
|
||||||
steer_fault_latched: bool = False) -> bool:
|
|
||||||
"""Avoid the Kona EPS torque fault when AOL is enabled over driver steering input."""
|
"""Avoid the Kona EPS torque fault when AOL is enabled over driver steering input."""
|
||||||
lateral_active = get_lateral_active(enabled, active, always_on_lateral_enabled,
|
lateral_active = get_lateral_active(enabled, active, always_on_lateral_enabled,
|
||||||
steer_fault_temporary, steer_fault_permanent,
|
steer_fault_temporary, steer_fault_permanent,
|
||||||
standstill, steer_at_standstill, lateral_check)
|
standstill, steer_at_standstill, lateral_check)
|
||||||
if not lateral_active or steer_fault_latched:
|
if not lateral_active:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
aol_rising_edge = always_on_lateral_enabled and not enabled and not previous_lateral_active
|
aol_rising_edge = always_on_lateral_enabled and not enabled and not previous_lateral_active
|
||||||
|
|||||||
@@ -35,9 +35,15 @@ def test_kona_non_scc_aol_gate_does_not_change_fault_or_normal_lateral_gates():
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_kona_non_scc_does_not_retry_after_a_latched_temporary_fault():
|
def test_kona_non_scc_recovers_after_temporary_fault_clears():
|
||||||
assert not get_kona_non_scc_lateral_active(
|
assert not get_kona_non_scc_lateral_active(
|
||||||
False, False, True, False, False, False, False, True, False, False, True,
|
False, False, True, True, False, False, False, True, False, True,
|
||||||
|
)
|
||||||
|
assert not get_kona_non_scc_lateral_active(
|
||||||
|
False, False, True, False, False, False, False, True, True, False,
|
||||||
|
)
|
||||||
|
assert get_kona_non_scc_lateral_active(
|
||||||
|
False, False, True, False, False, False, False, True, False, False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,35 @@
|
|||||||
import { BluetoothPanel } from "../components/BluetoothPanel.js"
|
import { BluetoothPanel } from "../components/BluetoothPanel.js"
|
||||||
|
import { WheelControls } from "../components/WheelControls.js"
|
||||||
import { GalaxySection } from "../components/GalaxySection.js"
|
import { GalaxySection } from "../components/GalaxySection.js"
|
||||||
|
import { GalaxyTabs } from "../components/GalaxyTabs.js"
|
||||||
|
import { useTabRouting } from "../composables.js"
|
||||||
|
|
||||||
|
const TABS = {
|
||||||
|
bluetooth: "Bluetooth",
|
||||||
|
controllers: "Controllers",
|
||||||
|
}
|
||||||
|
|
||||||
export const Bluetooth = {
|
export const Bluetooth = {
|
||||||
name: "Bluetooth",
|
name: "Bluetooth",
|
||||||
components: { BluetoothPanel, GalaxySection },
|
components: { BluetoothPanel, WheelControls, GalaxySection, GalaxyTabs },
|
||||||
|
setup() {
|
||||||
|
return useTabRouting("/bluetooth", { bluetooth: "bluetooth", controllers: "controllers" })
|
||||||
|
},
|
||||||
|
data() { return { TABS } },
|
||||||
template: `
|
template: `
|
||||||
<div class="gx-view">
|
<div class="gx-view">
|
||||||
<h2 style="margin-top:0;">Bluetooth</h2>
|
<h2 style="margin-top:0;">Bluetooth</h2>
|
||||||
<GalaxySection title="Bluetooth Devices" icon="bi-bluetooth" :collapsible="false">
|
<GalaxyTabs :items="TABS" :active="tab" @select="selectTab" />
|
||||||
<BluetoothPanel />
|
|
||||||
</GalaxySection>
|
<template v-if="tab === 'bluetooth'">
|
||||||
|
<GalaxySection title="Bluetooth Devices" icon="bi-bluetooth" :collapsible="false">
|
||||||
|
<BluetoothPanel />
|
||||||
|
</GalaxySection>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-else>
|
||||||
|
<WheelControls />
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
`,
|
`,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ import { PwaInstallSection, isFirestarOrigin } from "../components/PwaInstallSec
|
|||||||
|
|
||||||
const isTunnel = () => isFirestarOrigin()
|
const isTunnel = () => isFirestarOrigin()
|
||||||
|
|
||||||
function localDeviceUrl(ip) {
|
function localDeviceUrl(ip, route = "/") {
|
||||||
const raw = String(ip || "").trim()
|
const raw = String(ip || "").trim()
|
||||||
if (!raw || raw === "unknown") return ""
|
if (!raw || raw === "unknown") return ""
|
||||||
const host = raw.includes(":") && !raw.startsWith("[") ? `[${raw}]` : raw
|
const host = raw.includes(":") && !raw.startsWith("[") ? `[${raw}]` : raw
|
||||||
return `http://${host}:8082`
|
return `http://${host}:8082/#${route}`
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Galaxy = {
|
export const Galaxy = {
|
||||||
@@ -28,7 +28,7 @@ export const Galaxy = {
|
|||||||
if (this.isTunnel) {
|
if (this.isTunnel) {
|
||||||
try {
|
try {
|
||||||
const status = await api.getDeviceStatus()
|
const status = await api.getDeviceStatus()
|
||||||
this.localUrl = localDeviceUrl(status?.lanIp)
|
this.localUrl = localDeviceUrl(status?.lanIp, "/galaxy")
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -92,7 +92,10 @@ export const Galaxy = {
|
|||||||
<strong>Galaxy Pairing Unavailable via Galaxy</strong>
|
<strong>Galaxy Pairing Unavailable via Galaxy</strong>
|
||||||
<span>
|
<span>
|
||||||
Galaxy pairing requires a direct connection. If you are on the same local network, connect here:
|
Galaxy pairing requires a direct connection. If you are on the same local network, connect here:
|
||||||
<a v-if="localUrl" :href="localUrl" style="color:inherit; font-weight:var(--fw-bold); overflow-wrap:anywhere;">{{ localUrl }}</a>
|
<br />
|
||||||
|
<a v-if="localUrl" class="gx-btn gx-btn--tonal" :href="localUrl" style="margin-top:var(--sp-3);">
|
||||||
|
<i class="bi bi-box-arrow-up-right"></i> Open Galaxy Locally
|
||||||
|
</a>
|
||||||
<span v-else>your device's local IP on port 8082.</span>
|
<span v-else>your device's local IP on port 8082.</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -52,11 +52,11 @@ function normalizeRoute(r) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function localDeviceUrl(ip) {
|
function localDeviceUrl(ip, route = "/") {
|
||||||
const raw = String(ip || "").trim()
|
const raw = String(ip || "").trim()
|
||||||
if (!raw || raw === "unknown") return ""
|
if (!raw || raw === "unknown") return ""
|
||||||
const host = raw.includes(":") && !raw.startsWith("[") ? `[${raw}]` : raw
|
const host = raw.includes(":") && !raw.startsWith("[") ? `[${raw}]` : raw
|
||||||
return `http://${host}:8082`
|
return `http://${host}:8082/#${route}`
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Recordings = {
|
export const Recordings = {
|
||||||
@@ -346,7 +346,7 @@ export const Recordings = {
|
|||||||
if (this.onFirestar) {
|
if (this.onFirestar) {
|
||||||
try {
|
try {
|
||||||
const status = await api.getDeviceStatus()
|
const status = await api.getDeviceStatus()
|
||||||
this.localUrl = localDeviceUrl(status?.lanIp)
|
this.localUrl = localDeviceUrl(status?.lanIp, "/recordings")
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -520,7 +520,10 @@ export const Recordings = {
|
|||||||
|
|
||||||
<GxNotice v-else tone="info" icon="bi-satellite" title="Recordings unavailable via Galaxy">
|
<GxNotice v-else tone="info" icon="bi-satellite" title="Recordings unavailable via Galaxy">
|
||||||
Recordings are unavailable via Galaxy for bandwidth reasons. If you are on the same local network, connect here:
|
Recordings are unavailable via Galaxy for bandwidth reasons. If you are on the same local network, connect here:
|
||||||
<a v-if="localUrl" :href="localUrl" style="color:inherit; font-weight:var(--fw-bold); overflow-wrap:anywhere;">{{ localUrl }}</a>
|
<br />
|
||||||
|
<a v-if="localUrl" class="gx-btn gx-btn--tonal" :href="localUrl" style="margin-top:var(--sp-3);">
|
||||||
|
<i class="bi bi-box-arrow-up-right"></i> Open Recordings Locally
|
||||||
|
</a>
|
||||||
<span v-else>your device's local IP on port 8082.</span>
|
<span v-else>your device's local IP on port 8082.</span>
|
||||||
</GxNotice>
|
</GxNotice>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ const TOOLS = [
|
|||||||
{ name: "System Tools", link: "/system", icon: "bi-arrow-repeat", desc: "Backup, restore, updates" },
|
{ name: "System Tools", link: "/system", icon: "bi-arrow-repeat", desc: "Backup, restore, updates" },
|
||||||
{ name: "Theme Maker", link: "/theme_maker", icon: "bi-palette-fill", desc: "Customize the look" },
|
{ name: "Theme Maker", link: "/theme_maker", icon: "bi-palette-fill", desc: "Customize the look" },
|
||||||
{ name: "Tuning, Plots & Testing", link: "/tuning", icon: "bi-sign-turn-right", desc: "Steering & speed tuning, live plots, testing grounds" },
|
{ name: "Tuning, Plots & Testing", link: "/tuning", icon: "bi-sign-turn-right", desc: "Steering & speed tuning, live plots, testing grounds" },
|
||||||
{ name: "Vehicle Controls", link: "/vehicle", icon: "bi-car-front", desc: "Controllers & vehicle features" },
|
{ name: "Vehicle Controls", link: "/vehicle", icon: "bi-car-front", desc: "Vehicle features" },
|
||||||
].sort((a, b) => a.name.localeCompare(b.name))
|
].sort((a, b) => a.name.localeCompare(b.name))
|
||||||
|
|
||||||
export const Tools = {
|
export const Tools = {
|
||||||
|
|||||||
@@ -1,34 +1,22 @@
|
|||||||
import { api, showSnackbar } from "../api.js"
|
import { api, showSnackbar } from "../api.js"
|
||||||
import { navigate, toolHref } from "../store.js"
|
import { navigate, toolHref } from "../store.js"
|
||||||
import { WheelControls } from "../components/WheelControls.js"
|
|
||||||
import { GalaxySection } from "../components/GalaxySection.js"
|
import { GalaxySection } from "../components/GalaxySection.js"
|
||||||
import { GalaxyTabs } from "../components/GalaxyTabs.js"
|
|
||||||
import { useTabRouting } from "../composables.js"
|
|
||||||
|
|
||||||
const FEATURES = [
|
const FEATURES = [
|
||||||
{ key: "doors", name: "Lock/Unlock Doors", icon: "bi-door-closed", desc: "Send lock or unlock commands remotely to your vehicle.", embed: "/manage_doors" },
|
{ key: "doors", name: "Lock/Unlock Doors", icon: "bi-door-closed", desc: "Send lock or unlock commands remotely to your vehicle.", embed: "/manage_doors" },
|
||||||
{ key: "tsk", name: "Toyota Security Keys", icon: "bi-key-fill", desc: "Manage and apply security keys for secOC protected devices.", embed: "/manage_tsk" },
|
{ key: "tsk", name: "Toyota Security Keys", icon: "bi-key-fill", desc: "Manage and apply security keys for secOC protected devices.", embed: "/manage_tsk" },
|
||||||
]
|
]
|
||||||
|
|
||||||
const TABS = {
|
|
||||||
controllers: "Controllers",
|
|
||||||
features: "Vehicle Features",
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Vehicle = {
|
export const Vehicle = {
|
||||||
name: "Vehicle",
|
name: "Vehicle",
|
||||||
components: { WheelControls, GalaxySection, GalaxyTabs },
|
components: { GalaxySection },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
TABS,
|
|
||||||
features: FEATURES,
|
features: FEATURES,
|
||||||
featureStatus: {},
|
featureStatus: {},
|
||||||
busy: "",
|
busy: "",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup() {
|
|
||||||
return useTabRouting("/vehicle", { controllers: "controllers", features: "features" })
|
|
||||||
},
|
|
||||||
computed: {
|
computed: {
|
||||||
featureList() { return this.features },
|
featureList() { return this.features },
|
||||||
},
|
},
|
||||||
@@ -63,30 +51,22 @@ export const Vehicle = {
|
|||||||
<div class="gx-view">
|
<div class="gx-view">
|
||||||
<h2 style="margin-top:0;">Vehicle Controls</h2>
|
<h2 style="margin-top:0;">Vehicle Controls</h2>
|
||||||
|
|
||||||
<GalaxyTabs :items="TABS" :active="tab" @select="selectTab" />
|
<GalaxySection title="Vehicle Features" icon="bi-check2-square">
|
||||||
|
<div style="padding: var(--sp-3); display:grid; gap:8px;">
|
||||||
<template v-if="tab === 'controllers'">
|
<button v-for="f in featureList" :key="f.key" type="button"
|
||||||
<WheelControls />
|
class="gx-row" style="width:100%; border:none; background:transparent; color:inherit; cursor:pointer; text-align:left;"
|
||||||
</template>
|
@click="openFeature(f)">
|
||||||
|
<div class="gx-row__info">
|
||||||
<template v-else>
|
<span class="gx-row__label"><i class="bi" :class="f.icon" style="margin-right:6px; color:var(--primary);"></i>{{ f.name }}</span>
|
||||||
<GalaxySection title="Vehicle Features" icon="bi-check2-square">
|
<span class="gx-row__desc">{{ f.desc }}</span>
|
||||||
<div style="padding: var(--sp-3); display:grid; gap:8px;">
|
</div>
|
||||||
<button v-for="f in featureList" :key="f.key" type="button"
|
<span v-if="busy === f.key" class="gx-chip" style="background:var(--surface-variant);">Checking...</span>
|
||||||
class="gx-row" style="width:100%; border:none; background:transparent; color:inherit; cursor:pointer; text-align:left;"
|
<span v-else-if="statusOf(f.key) === 'denied'" class="gx-chip" style="background:var(--error);">Not supported</span>
|
||||||
@click="openFeature(f)">
|
<i v-else class="bi bi-chevron-right" style="color:var(--text-muted);"></i>
|
||||||
<div class="gx-row__info">
|
</button>
|
||||||
<span class="gx-row__label"><i class="bi" :class="f.icon" style="margin-right:6px; color:var(--primary);"></i>{{ f.name }}</span>
|
<p style="color:var(--text-muted); margin:0;">These features verify vehicle compatibility when launched.</p>
|
||||||
<span class="gx-row__desc">{{ f.desc }}</span>
|
</div>
|
||||||
</div>
|
</GalaxySection>
|
||||||
<span v-if="busy === f.key" class="gx-chip" style="background:var(--surface-variant);">Checking...</span>
|
|
||||||
<span v-else-if="statusOf(f.key) === 'denied'" class="gx-chip" style="background:var(--error);">Not supported</span>
|
|
||||||
<i v-else class="bi bi-chevron-right" style="color:var(--text-muted);"></i>
|
|
||||||
</button>
|
|
||||||
<p style="color:var(--text-muted); margin:0;">These features verify vehicle compatibility when launched.</p>
|
|
||||||
</div>
|
|
||||||
</GalaxySection>
|
|
||||||
</template>
|
|
||||||
</div>
|
</div>
|
||||||
`,
|
`,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -126,8 +126,8 @@ def test_ui_ports_all_tool_views():
|
|||||||
assert ep in src, f"{rel} should use api.{ep}"
|
assert ep in src, f"{rel} should use api.{ep}"
|
||||||
vehicle = _read("js/views/Vehicle.js")
|
vehicle = _read("js/views/Vehicle.js")
|
||||||
bluetooth = _read("js/views/Bluetooth.js")
|
bluetooth = _read("js/views/Bluetooth.js")
|
||||||
assert "WheelControls" in vehicle and "BluetoothPanel" not in vehicle and "carFeaturesCheck" in vehicle
|
assert "WheelControls" not in vehicle and "BluetoothPanel" not in vehicle and "carFeaturesCheck" in vehicle
|
||||||
assert "BluetoothPanel" in bluetooth
|
assert "BluetoothPanel" in bluetooth and "WheelControls" in bluetooth
|
||||||
|
|
||||||
|
|
||||||
def test_ui_routes_ported_views_natively_no_classic_fallback():
|
def test_ui_routes_ported_views_natively_no_classic_fallback():
|
||||||
@@ -233,9 +233,13 @@ def test_ui_schema_driven_param_engine_reused():
|
|||||||
assert "GalaxyEmbed" not in tuning and 'src="/tuning"' not in tuning, "Tuning must be native, not a classic embed"
|
assert "GalaxyEmbed" not in tuning and 'src="/tuning"' not in tuning, "Tuning must be native, not a classic embed"
|
||||||
assert "LateralTuningPanel" in tuning and "LongitudinalManeuvers" not in tuning
|
assert "LateralTuningPanel" in tuning and "LongitudinalManeuvers" not in tuning
|
||||||
vehicle = _read("js/views/Vehicle.js")
|
vehicle = _read("js/views/Vehicle.js")
|
||||||
|
bluetooth = _read("js/views/Bluetooth.js")
|
||||||
assert "ParamSections" not in vehicle, "Vehicle must not render redundant toggles"
|
assert "ParamSections" not in vehicle, "Vehicle must not render redundant toggles"
|
||||||
assert "WheelControls" in vehicle and "BluetoothPanel" not in vehicle
|
assert "WheelControls" not in vehicle and "BluetoothPanel" not in vehicle
|
||||||
assert "GalaxySection" in vehicle
|
assert "GalaxySection" in vehicle
|
||||||
|
assert "WheelControls" in bluetooth and "BluetoothPanel" in bluetooth
|
||||||
|
assert bluetooth.index('bluetooth: "Bluetooth"') < bluetooth.index('controllers: "Controllers"')
|
||||||
|
assert 'useTabRouting("/bluetooth"' in bluetooth
|
||||||
engine = _read("js/components/ParamSections.js")
|
engine = _read("js/components/ParamSections.js")
|
||||||
assert "SettingTree" in engine
|
assert "SettingTree" in engine
|
||||||
assert "isSettingVisible" in engine
|
assert "isSettingVisible" in engine
|
||||||
@@ -517,6 +521,10 @@ def test_ui_mobile_polish_regressions():
|
|||||||
assert "bandwidth reasons" in recordings and "status?.lanIp" in recordings
|
assert "bandwidth reasons" in recordings and "status?.lanIp" in recordings
|
||||||
assert "status?.lanIp" in galaxy
|
assert "status?.lanIp" in galaxy
|
||||||
assert ':href="localUrl"' in recordings and ':href="localUrl"' in galaxy
|
assert ':href="localUrl"' in recordings and ':href="localUrl"' in galaxy
|
||||||
|
assert 'localDeviceUrl(status?.lanIp, "/recordings")' in recordings
|
||||||
|
assert 'localDeviceUrl(status?.lanIp, "/galaxy")' in galaxy
|
||||||
|
assert "gx-btn gx-btn--tonal" in recordings and "Open Recordings Locally" in recordings
|
||||||
|
assert "gx-btn gx-btn--tonal" in galaxy and "Open Galaxy Locally" in galaxy
|
||||||
|
|
||||||
home = _read("js/views/Home.js")
|
home = _read("js/views/Home.js")
|
||||||
home_css = _read("css/home.css")
|
home_css = _read("css/home.css")
|
||||||
|
|||||||
Reference in New Issue
Block a user