mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-09-11 10:43:46 +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.hyundai import hyundaicanfd, hyundaican
|
||||
from opendbc.car.hyundai.hyundaicanfd import CanBus
|
||||
from opendbc.car.hyundai.values import HyundaiFlags, HyundaiSafetyFlags, HyundaiStarPilotFlags, Buttons, CarControllerParams, CAR, CANFD_ANGLE_LONGITUDINAL_CAR, \
|
||||
CANFD_RADAR_LIVE_LONGITUDINAL_CAR, CANFD_ALT_BUTTONS_RESUME_CAR, kia_ev6_gt_line_longitudinal_tuning, \
|
||||
from opendbc.car.hyundai.values import HyundaiFlags, HyundaiSafetyFlags, HyundaiStarPilotFlags, Buttons, CarControllerParams, CAR, \
|
||||
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
|
||||
from opendbc.car.interfaces import CarControllerBase
|
||||
from opendbc.car.vehicle_model import VehicleModel
|
||||
@@ -859,10 +860,9 @@ class CarController(CarControllerBase):
|
||||
can_sends = []
|
||||
|
||||
lka_steering = self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING
|
||||
longitudinal_active = bool(self.long_active_ecu and getattr(CC, "longActive", False))
|
||||
lfa_status_cars = (CAR.HYUNDAI_IONIQ_6, CAR.GENESIS_GV70_ELECTRIFIED_1ST_GEN)
|
||||
persistent_lfa_status_cars = (CAR.HYUNDAI_IONIQ_6, CAR.GENESIS_GV70_ELECTRIFIED_1ST_GEN, CAR.KIA_EV6)
|
||||
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
|
||||
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 \
|
||||
@@ -889,11 +889,10 @@ class CarController(CarControllerBase):
|
||||
|
||||
gear = getattr(getattr(CS, "out", None), "gearShifter", None)
|
||||
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)
|
||||
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
|
||||
self.CP.carFingerprint == CAR.KIA_SPORTAGE_HEV_2026) and angle_lkas_alt and (
|
||||
forward_stock_lkas = self.CP.carFingerprint in CANFD_ANGLE_LONGITUDINAL_CAR and angle_lkas_alt and (
|
||||
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)
|
||||
@@ -920,7 +919,7 @@ class CarController(CarControllerBase):
|
||||
|
||||
# prevent LFA from activating on LKA steering cars by sending "no lane lines detected" to ADAS ECU
|
||||
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)))
|
||||
if self.frame % 5 == 0 and suppress_lfa:
|
||||
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
|
||||
# and stops publishing object tracks when it disappears.
|
||||
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,
|
||||
CS.out.brakePressed, CS.out.gasPressed,
|
||||
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["ADAS_ACIAnglTqRedcGainVal"] = apply_torque if lat_active else 0.0
|
||||
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 = {
|
||||
"LKA_OptUsmSta": 0,
|
||||
"LKA_RcgSta": 3,
|
||||
|
||||
@@ -7,7 +7,7 @@ from opendbc.car.hyundai.values import HyundaiFlags, CAR, CarControllerParams, \
|
||||
CANFD_UNSUPPORTED_LONGITUDINAL_CAR, \
|
||||
CANFD_SECURITYACCESS_CAR, \
|
||||
CANFD_ANGLE_LONGITUDINAL_CAR, \
|
||||
CANFD_RADAR_LIVE_LONGITUDINAL_CAR, \
|
||||
CANFD_RADAR_ECU_KEEPALIVE_CAR, \
|
||||
RADAR_LIVE_LONGITUDINAL_CAR, \
|
||||
UNSUPPORTED_LONGITUDINAL_CAR, HyundaiSafetyFlags, \
|
||||
LEGACY_LONGITUDINAL_CAR, \
|
||||
@@ -27,6 +27,15 @@ from openpilot.starpilot.common.testing_grounds import testing_ground
|
||||
ButtonType = structs.CarState.ButtonEvent.Type
|
||||
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
|
||||
ENABLE_BUTTONS = (ButtonType.accelCruise, ButtonType.decelCruise, ButtonType.cancel, ButtonType.mainCruise)
|
||||
|
||||
@@ -353,14 +362,7 @@ class CarInterface(CarInterfaceBase):
|
||||
params = Params()
|
||||
|
||||
if communication_control is None:
|
||||
if CP.carFingerprint in CANFD_RADAR_LIVE_LONGITUDINAL_CAR:
|
||||
# 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])
|
||||
communication_control = get_communication_control_request(CP.carFingerprint)
|
||||
|
||||
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
|
||||
|
||||
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.fw_versions import build_fw_dict, match_fw_to_car
|
||||
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
|
||||
from opendbc.car.hyundai.carstate import CarState, decode_canfd_camera_lead, decode_ioniq_6_blindspot_radar_state, \
|
||||
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.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, \
|
||||
@@ -129,6 +129,19 @@ def get_test_toggles() -> SimpleNamespace:
|
||||
|
||||
|
||||
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):
|
||||
CP = CarInterface.get_params(CAR.KIA_CARNIVAL_HEV_4TH_GEN, gen_empty_fingerprint(), [],
|
||||
False, False, False, None)
|
||||
@@ -2552,9 +2565,10 @@ class TestHyundaiFingerprint:
|
||||
if controller.packer.dbc.addr_to_msg[addr].name in ("LFA", "LKAS")]
|
||||
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.carFingerprint = CAR.HYUNDAI_IONIQ_6
|
||||
CP.carFingerprint = car
|
||||
CP.flags = int(HyundaiFlags.CANFD | HyundaiFlags.EV | HyundaiFlags.CANFD_LKA_STEERING)
|
||||
CP.openpilotLongitudinalControl = True
|
||||
|
||||
@@ -2575,6 +2589,38 @@ class TestHyundaiFingerprint:
|
||||
cc.hudControl, cs, cc, get_test_toggles(), lka_icon=1, lfa_icon=1)
|
||||
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):
|
||||
CP = CarParams.new_message()
|
||||
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
|
||||
|
||||
@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.carFingerprint = CAR.KIA_SPORTAGE_HEV_2026
|
||||
CP.flags = int(HyundaiFlags.CANFD | HyundaiFlags.HYBRID | HyundaiFlags.CANFD_ANGLE_STEERING |
|
||||
@@ -2715,16 +2761,67 @@ class TestHyundaiFingerprint:
|
||||
CP.openpilotLongitudinalControl = False
|
||||
|
||||
controller = CarController(DBC[CP.carFingerprint], CP)
|
||||
controller.frame = 5
|
||||
can_bus = CanBus(CP)
|
||||
cc = SimpleNamespace(enabled=False, latActive=False,
|
||||
actuators=SimpleNamespace(longControlState=LongCtrlState.off),
|
||||
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,
|
||||
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,
|
||||
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):
|
||||
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_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.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 | {
|
||||
CAR.HYUNDAI_IONIQ,
|
||||
|
||||
@@ -404,7 +404,6 @@ class Controls:
|
||||
self.turn_blinker_swept = 0.0
|
||||
self.twitch_guard_remaining = 0.0
|
||||
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_previous_cruise_enabled = False
|
||||
|
||||
@@ -504,11 +503,6 @@ class Controls:
|
||||
standstill = abs(CS.vEgo) <= max(self.CP.minSteerSpeed, 0.3) or CS.standstill
|
||||
if self.CP.carFingerprint == HYUNDAI_CAR.HYUNDAI_KONA_NON_SCC:
|
||||
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.enabled, self.sm['selfdriveState'].active,
|
||||
always_on_lateral_enabled,
|
||||
@@ -516,7 +510,6 @@ class Controls:
|
||||
standstill, self.CP.steerAtStandstill,
|
||||
self.sm['starpilotPlan'].lateralCheck,
|
||||
CS.steeringPressed, self.kona_non_scc_lateral_active,
|
||||
self.kona_non_scc_lateral_faulted,
|
||||
)
|
||||
self.kona_non_scc_lateral_active = CC.latActive
|
||||
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,
|
||||
steer_fault_temporary: bool, steer_fault_permanent: bool,
|
||||
standstill: bool, steer_at_standstill: bool, lateral_check: bool,
|
||||
steering_pressed: bool, previous_lateral_active: bool,
|
||||
steer_fault_latched: bool = False) -> bool:
|
||||
steering_pressed: bool, previous_lateral_active: bool) -> bool:
|
||||
"""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,
|
||||
steer_fault_temporary, steer_fault_permanent,
|
||||
standstill, steer_at_standstill, lateral_check)
|
||||
if not lateral_active or steer_fault_latched:
|
||||
if not lateral_active:
|
||||
return False
|
||||
|
||||
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(
|
||||
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 { WheelControls } from "../components/WheelControls.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 = {
|
||||
name: "Bluetooth",
|
||||
components: { BluetoothPanel, GalaxySection },
|
||||
components: { BluetoothPanel, WheelControls, GalaxySection, GalaxyTabs },
|
||||
setup() {
|
||||
return useTabRouting("/bluetooth", { bluetooth: "bluetooth", controllers: "controllers" })
|
||||
},
|
||||
data() { return { TABS } },
|
||||
template: `
|
||||
<div class="gx-view">
|
||||
<h2 style="margin-top:0;">Bluetooth</h2>
|
||||
<GalaxySection title="Bluetooth Devices" icon="bi-bluetooth" :collapsible="false">
|
||||
<BluetoothPanel />
|
||||
</GalaxySection>
|
||||
<GalaxyTabs :items="TABS" :active="tab" @select="selectTab" />
|
||||
|
||||
<template v-if="tab === 'bluetooth'">
|
||||
<GalaxySection title="Bluetooth Devices" icon="bi-bluetooth" :collapsible="false">
|
||||
<BluetoothPanel />
|
||||
</GalaxySection>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<WheelControls />
|
||||
</template>
|
||||
</div>
|
||||
`,
|
||||
}
|
||||
|
||||
@@ -4,11 +4,11 @@ import { PwaInstallSection, isFirestarOrigin } from "../components/PwaInstallSec
|
||||
|
||||
const isTunnel = () => isFirestarOrigin()
|
||||
|
||||
function localDeviceUrl(ip) {
|
||||
function localDeviceUrl(ip, route = "/") {
|
||||
const raw = String(ip || "").trim()
|
||||
if (!raw || raw === "unknown") return ""
|
||||
const host = raw.includes(":") && !raw.startsWith("[") ? `[${raw}]` : raw
|
||||
return `http://${host}:8082`
|
||||
return `http://${host}:8082/#${route}`
|
||||
}
|
||||
|
||||
export const Galaxy = {
|
||||
@@ -28,7 +28,7 @@ export const Galaxy = {
|
||||
if (this.isTunnel) {
|
||||
try {
|
||||
const status = await api.getDeviceStatus()
|
||||
this.localUrl = localDeviceUrl(status?.lanIp)
|
||||
this.localUrl = localDeviceUrl(status?.lanIp, "/galaxy")
|
||||
} catch (e) {}
|
||||
return
|
||||
}
|
||||
@@ -92,7 +92,10 @@ export const Galaxy = {
|
||||
<strong>Galaxy Pairing Unavailable via Galaxy</strong>
|
||||
<span>
|
||||
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>
|
||||
</div>
|
||||
|
||||
@@ -52,11 +52,11 @@ function normalizeRoute(r) {
|
||||
}
|
||||
}
|
||||
|
||||
function localDeviceUrl(ip) {
|
||||
function localDeviceUrl(ip, route = "/") {
|
||||
const raw = String(ip || "").trim()
|
||||
if (!raw || raw === "unknown") return ""
|
||||
const host = raw.includes(":") && !raw.startsWith("[") ? `[${raw}]` : raw
|
||||
return `http://${host}:8082`
|
||||
return `http://${host}:8082/#${route}`
|
||||
}
|
||||
|
||||
export const Recordings = {
|
||||
@@ -346,7 +346,7 @@ export const Recordings = {
|
||||
if (this.onFirestar) {
|
||||
try {
|
||||
const status = await api.getDeviceStatus()
|
||||
this.localUrl = localDeviceUrl(status?.lanIp)
|
||||
this.localUrl = localDeviceUrl(status?.lanIp, "/recordings")
|
||||
} catch (e) {}
|
||||
return
|
||||
}
|
||||
@@ -520,7 +520,10 @@ export const Recordings = {
|
||||
|
||||
<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:
|
||||
<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>
|
||||
</GxNotice>
|
||||
</div>
|
||||
|
||||
@@ -11,7 +11,7 @@ const TOOLS = [
|
||||
{ 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: "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))
|
||||
|
||||
export const Tools = {
|
||||
|
||||
@@ -1,34 +1,22 @@
|
||||
import { api, showSnackbar } from "../api.js"
|
||||
import { navigate, toolHref } from "../store.js"
|
||||
import { WheelControls } from "../components/WheelControls.js"
|
||||
import { GalaxySection } from "../components/GalaxySection.js"
|
||||
import { GalaxyTabs } from "../components/GalaxyTabs.js"
|
||||
import { useTabRouting } from "../composables.js"
|
||||
|
||||
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: "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 = {
|
||||
name: "Vehicle",
|
||||
components: { WheelControls, GalaxySection, GalaxyTabs },
|
||||
components: { GalaxySection },
|
||||
data() {
|
||||
return {
|
||||
TABS,
|
||||
features: FEATURES,
|
||||
featureStatus: {},
|
||||
busy: "",
|
||||
}
|
||||
},
|
||||
setup() {
|
||||
return useTabRouting("/vehicle", { controllers: "controllers", features: "features" })
|
||||
},
|
||||
computed: {
|
||||
featureList() { return this.features },
|
||||
},
|
||||
@@ -63,30 +51,22 @@ export const Vehicle = {
|
||||
<div class="gx-view">
|
||||
<h2 style="margin-top:0;">Vehicle Controls</h2>
|
||||
|
||||
<GalaxyTabs :items="TABS" :active="tab" @select="selectTab" />
|
||||
|
||||
<template v-if="tab === 'controllers'">
|
||||
<WheelControls />
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<GalaxySection title="Vehicle Features" icon="bi-check2-square">
|
||||
<div style="padding: var(--sp-3); display:grid; gap:8px;">
|
||||
<button v-for="f in featureList" :key="f.key" type="button"
|
||||
class="gx-row" style="width:100%; border:none; background:transparent; color:inherit; cursor:pointer; text-align:left;"
|
||||
@click="openFeature(f)">
|
||||
<div class="gx-row__info">
|
||||
<span class="gx-row__label"><i class="bi" :class="f.icon" style="margin-right:6px; color:var(--primary);"></i>{{ f.name }}</span>
|
||||
<span class="gx-row__desc">{{ f.desc }}</span>
|
||||
</div>
|
||||
<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>
|
||||
<GalaxySection title="Vehicle Features" icon="bi-check2-square">
|
||||
<div style="padding: var(--sp-3); display:grid; gap:8px;">
|
||||
<button v-for="f in featureList" :key="f.key" type="button"
|
||||
class="gx-row" style="width:100%; border:none; background:transparent; color:inherit; cursor:pointer; text-align:left;"
|
||||
@click="openFeature(f)">
|
||||
<div class="gx-row__info">
|
||||
<span class="gx-row__label"><i class="bi" :class="f.icon" style="margin-right:6px; color:var(--primary);"></i>{{ f.name }}</span>
|
||||
<span class="gx-row__desc">{{ f.desc }}</span>
|
||||
</div>
|
||||
<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>
|
||||
</div>
|
||||
`,
|
||||
}
|
||||
|
||||
@@ -126,8 +126,8 @@ def test_ui_ports_all_tool_views():
|
||||
assert ep in src, f"{rel} should use api.{ep}"
|
||||
vehicle = _read("js/views/Vehicle.js")
|
||||
bluetooth = _read("js/views/Bluetooth.js")
|
||||
assert "WheelControls" in vehicle and "BluetoothPanel" not in vehicle and "carFeaturesCheck" in vehicle
|
||||
assert "BluetoothPanel" in bluetooth
|
||||
assert "WheelControls" not in vehicle and "BluetoothPanel" not in vehicle and "carFeaturesCheck" in vehicle
|
||||
assert "BluetoothPanel" in bluetooth and "WheelControls" in bluetooth
|
||||
|
||||
|
||||
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 "LateralTuningPanel" in tuning and "LongitudinalManeuvers" not in tuning
|
||||
vehicle = _read("js/views/Vehicle.js")
|
||||
bluetooth = _read("js/views/Bluetooth.js")
|
||||
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 "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")
|
||||
assert "SettingTree" 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 "status?.lanIp" 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_css = _read("css/home.css")
|
||||
|
||||
Reference in New Issue
Block a user