Skip to content

Commit ecce2da

Browse files
refactor: button event handling with ButtonEventConfig and ButtonMap
1 parent 2e15594 commit ecce2da

File tree

10 files changed

+87
-104
lines changed

10 files changed

+87
-104
lines changed

opendbc/car/__init__.py

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from dataclasses import dataclass, field
44
from enum import IntFlag, ReprEnum, StrEnum, EnumType, auto
55
from dataclasses import replace
6+
from typing import NamedTuple
67

78
from opendbc.car import structs, uds
89
from opendbc.car.can_definitions import CanData
@@ -25,21 +26,28 @@ def apply_hysteresis(val: float, val_steady: float, hyst_gap: float) -> float:
2526
val_steady = val + hyst_gap
2627
return val_steady
2728

28-
29-
def create_button_events(cur_btn: int, prev_btn: int, buttons_dict: dict[int, structs.CarState.ButtonEvent.Type],
30-
unpressed_btn: int = 0) -> list[structs.CarState.ButtonEvent]:
31-
events: list[structs.CarState.ButtonEvent] = []
32-
33-
if cur_btn == prev_btn:
34-
return events
35-
36-
# Add events for button presses, multiple when a button switches without going to unpressed
37-
for pressed, btn in ((False, prev_btn), (True, cur_btn)):
38-
if btn != unpressed_btn:
39-
events.append(structs.CarState.ButtonEvent(pressed=pressed,
40-
type=buttons_dict.get(btn, ButtonType.unknown)))
41-
return events
42-
29+
class ButtonMap(NamedTuple):
30+
event: structs.CarState.ButtonEvent.Type
31+
val: int = 1
32+
33+
class ButtonEvent(NamedTuple):
34+
current_btn: int
35+
button_maps: list[ButtonMap]
36+
unpressed_btn: int = 0
37+
38+
_prev_btns: dict[int, int] = {}
39+
40+
def create_button_events(ret, button_list):
41+
ev = []
42+
for cur, maps, up in button_list:
43+
k = id(maps); prev = _prev_btns.setdefault(k, cur)
44+
if cur == prev: continue
45+
for pressed, b in ((False, prev), (True, cur)):
46+
if b != up:
47+
etype = next((m.event for m in maps if m.val == b), ButtonType.unknown)
48+
ev.append(structs.CarState.ButtonEvent(pressed=pressed, type=etype))
49+
_prev_btns[k] = cur
50+
ret.buttonEvents = ev
4351

4452
def gen_empty_fingerprint():
4553
return {i: {} for i in range(8)}

opendbc/car/chrysler/carstate.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from opendbc.can import CANDefine, CANParser
2-
from opendbc.car import Bus, create_button_events, structs
2+
from opendbc.car import Bus, create_button_events, structs, ButtonEvent
33
from opendbc.car.chrysler.values import DBC, STEER_THRESHOLD, RAM_CARS
44
from opendbc.car.common.conversions import Conversions as CV
55
from opendbc.car.interfaces import CarStateBase
@@ -22,17 +22,12 @@ def __init__(self, CP):
2222
else:
2323
self.shifter_values = can_define.dv["GEAR"]["PRNDL"]
2424

25-
self.distance_button = 0
26-
2725
def update(self, can_parsers) -> structs.CarState:
2826
cp = can_parsers[Bus.pt]
2927
cp_cam = can_parsers[Bus.cam]
3028

3129
ret = structs.CarState()
3230

33-
prev_distance_button = self.distance_button
34-
self.distance_button = cp.vl["CRUISE_BUTTONS"]["ACC_Distance_Dec"]
35-
3631
# lock info
3732
ret.doorOpen = any([cp.vl["BCM_1"]["DOOR_OPEN_FL"],
3833
cp.vl["BCM_1"]["DOOR_OPEN_FR"],
@@ -95,7 +90,7 @@ def update(self, can_parsers) -> structs.CarState:
9590
self.lkas_car_model = cp_cam.vl["DAS_6"]["CAR_MODEL"]
9691
self.button_counter = cp.vl["CRUISE_BUTTONS"]["COUNTER"]
9792

98-
ret.buttonEvents = create_button_events(self.distance_button, prev_distance_button, {1: ButtonType.gapAdjustCruise})
93+
create_button_events(ret, [ButtonEvent(cp.vl["CRUISE_BUTTONS"]["ACC_Distance_Dec"], self.dist_btn_map)])
9994

10095
return ret
10196

opendbc/car/ford/carstate.py

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from opendbc.can import CANDefine, CANParser
2-
from opendbc.car import Bus, create_button_events, structs
2+
from opendbc.car import Bus, create_button_events, structs, ButtonEvent
33
from opendbc.car.common.conversions import Conversions as CV
44
from opendbc.car.ford.fordcan import CanBus
55
from opendbc.car.ford.values import DBC, CarControllerParams, FordFlags
@@ -17,9 +17,6 @@ def __init__(self, CP):
1717
if CP.transmissionType == TransmissionType.automatic:
1818
self.shifter_values = can_define.dv["PowertrainData_10"]["TrnRng_D_Rq"]
1919

20-
self.distance_button = 0
21-
self.lc_button = 0
22-
2320
def update(self, can_parsers) -> structs.CarState:
2421
cp = can_parsers[Bus.pt]
2522
cp_cam = can_parsers[Bus.cam]
@@ -86,10 +83,6 @@ def update(self, can_parsers) -> structs.CarState:
8683
ret.rightBlinker = cp.vl["Steering_Data_FD1"]["TurnLghtSwtch_D_Stat"] == 2
8784
# TODO: block this going to the camera otherwise it will enable stock TJA
8885
ret.genericToggle = bool(cp.vl["Steering_Data_FD1"]["TjaButtnOnOffPress"])
89-
prev_distance_button = self.distance_button
90-
prev_lc_button = self.lc_button
91-
self.distance_button = cp.vl["Steering_Data_FD1"]["AccButtnGapTogglePress"]
92-
self.lc_button = bool(cp.vl["Steering_Data_FD1"]["TjaButtnOnOffPress"])
9386

9487
# lock info
9588
ret.doorOpen = any([cp.vl["BodyInfo_3_FD1"]["DrStatDrv_B_Actl"], cp.vl["BodyInfo_3_FD1"]["DrStatPsngr_B_Actl"],
@@ -108,10 +101,10 @@ def update(self, can_parsers) -> structs.CarState:
108101
self.acc_tja_status_stock_values = cp_cam.vl["ACCDATA_3"]
109102
self.lkas_status_stock_values = cp_cam.vl["IPMA_Data"]
110103

111-
ret.buttonEvents = [
112-
*create_button_events(self.distance_button, prev_distance_button, {1: ButtonType.gapAdjustCruise}),
113-
*create_button_events(self.lc_button, prev_lc_button, {1: ButtonType.lkas}),
114-
]
104+
create_button_events(ret, [
105+
ButtonEvent(cp.vl["Steering_Data_FD1"]["AccButtnGapTogglePress"], self.dist_btn_map),
106+
ButtonEvent(cp.vl["Steering_Data_FD1"]["TjaButtnOnOffPress"], self.lkas_btn_map)
107+
])
115108

116109
return ret
117110

opendbc/car/gm/carstate.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import copy
22
from opendbc.can import CANDefine, CANParser
3-
from opendbc.car import Bus, create_button_events, structs
3+
from opendbc.car import Bus, create_button_events, structs, ButtonEvent, ButtonMap
44
from opendbc.car.common.conversions import Conversions as CV
55
from opendbc.car.interfaces import CarStateBase
66
from opendbc.car.gm.values import DBC, AccState, CruiseButtons, STEER_THRESHOLD, SDGM_CAR, ALT_ACCS
@@ -11,8 +11,12 @@
1111

1212
STANDSTILL_THRESHOLD = 10 * 0.0311
1313

14-
BUTTONS_DICT = {CruiseButtons.RES_ACCEL: ButtonType.accelCruise, CruiseButtons.DECEL_SET: ButtonType.decelCruise,
15-
CruiseButtons.MAIN: ButtonType.mainCruise, CruiseButtons.CANCEL: ButtonType.cancel}
14+
BUTTON_MAPS: list[ButtonMap] = (
15+
ButtonMap(ButtonType.accelCruise, CruiseButtons.RES_ACCEL),
16+
ButtonMap(ButtonType.decelCruise, CruiseButtons.DECEL_SET),
17+
ButtonMap(ButtonType.mainCruise, CruiseButtons.MAIN),
18+
ButtonMap(ButtonType.cancel, CruiseButtons.CANCEL)
19+
)
1620

1721

1822
class CarState(CarStateBase):
@@ -48,9 +52,7 @@ def update(self, can_parsers) -> structs.CarState:
4852
ret = structs.CarState()
4953

5054
prev_cruise_buttons = self.cruise_buttons
51-
prev_distance_button = self.distance_button
5255
self.cruise_buttons = pt_cp.vl["ASCMSteeringButton"]["ACCButtons"]
53-
self.distance_button = pt_cp.vl["ASCMSteeringButton"]["DistanceButton"]
5456
self.buttons_counter = pt_cp.vl["ASCMSteeringButton"]["RollingCounter"]
5557
self.pscm_status = copy.copy(pt_cp.vl["PSCMStatus"])
5658

@@ -148,12 +150,10 @@ def update(self, can_parsers) -> structs.CarState:
148150

149151
# Don't add event if transitioning from INIT, unless it's to an actual button
150152
if self.cruise_buttons != CruiseButtons.UNPRESS or prev_cruise_buttons != CruiseButtons.INIT:
151-
ret.buttonEvents = [
152-
*create_button_events(self.cruise_buttons, prev_cruise_buttons, BUTTONS_DICT,
153-
unpressed_btn=CruiseButtons.UNPRESS),
154-
*create_button_events(self.distance_button, prev_distance_button,
155-
{1: ButtonType.gapAdjustCruise})
156-
]
153+
create_button_events(ret, [
154+
ButtonEvent(self.cruise_buttons, BUTTON_MAPS, CruiseButtons.UNPRESS),
155+
ButtonEvent(pt_cp.vl["ASCMSteeringButton"]["DistanceButton"], self.dist_btn_map),
156+
])
157157

158158
if ret.vEgo < self.CP.minSteerSpeed:
159159
ret.lowSpeedAlert = True

opendbc/car/honda/carstate.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from collections import defaultdict
33

44
from opendbc.can import CANDefine, CANParser
5-
from opendbc.car import Bus, create_button_events, structs
5+
from opendbc.car import Bus, create_button_events, structs, ButtonEvent, ButtonMap
66
from opendbc.car.common.conversions import Conversions as CV
77
from opendbc.car.honda.hondacan import CanBus
88
from opendbc.car.honda.values import CAR, DBC, STEER_THRESHOLD, HONDA_BOSCH, HONDA_BOSCH_CANFD, \
@@ -13,9 +13,16 @@
1313
TransmissionType = structs.CarParams.TransmissionType
1414
ButtonType = structs.CarState.ButtonEvent.Type
1515

16-
BUTTONS_DICT = {CruiseButtons.RES_ACCEL: ButtonType.accelCruise, CruiseButtons.DECEL_SET: ButtonType.decelCruise,
17-
CruiseButtons.MAIN: ButtonType.mainCruise, CruiseButtons.CANCEL: ButtonType.cancel}
18-
SETTINGS_BUTTONS_DICT = {CruiseSettings.DISTANCE: ButtonType.gapAdjustCruise, CruiseSettings.LKAS: ButtonType.lkas}
16+
BUTTON_MAPS: list[ButtonMap] = (
17+
ButtonMap(ButtonType.accelCruise, CruiseButtons.RES_ACCEL),
18+
ButtonMap(ButtonType.decelCruise, CruiseButtons.DECEL_SET),
19+
ButtonMap(ButtonType.mainCruise, CruiseButtons.MAIN),
20+
ButtonMap(ButtonType.cancel, CruiseButtons.CANCEL)
21+
)
22+
SETTINGS_BUTTON_MAPS: list[ButtonMap] = (
23+
ButtonMap(ButtonType.gapAdjustCruise, CruiseSettings.DISTANCE),
24+
ButtonMap(ButtonType.lkas, CruiseSettings.LKAS)
25+
)
1926

2027

2128
class CarState(CarStateBase):
@@ -39,7 +46,6 @@ def __init__(self, CP):
3946
self.brake_switch_active = False
4047

4148
self.dynamic_v_cruise_units = self.CP.carFingerprint in (HONDA_BOSCH_RADARLESS | HONDA_BOSCH_CANFD)
42-
self.cruise_setting = 0
4349
self.v_cruise_pcm_prev = 0
4450

4551
# When available we use cp.vl["CAR_SPEED"]["ROUGH_CAR_SPEED_2"] to populate vEgoCluster
@@ -58,12 +64,6 @@ def update(self, can_parsers) -> structs.CarState:
5864
v_weight_v = [0., 1.] # don't trust smooth speed at low values to avoid premature zero snapping
5965
v_weight_bp = [1., 6.] # smooth blending, below ~0.6m/s the smooth speed snaps to zero
6066

61-
# update prevs, update must run once per loop
62-
prev_cruise_buttons = self.cruise_buttons
63-
prev_cruise_setting = self.cruise_setting
64-
self.cruise_setting = cp.vl["SCM_BUTTONS"]["CRUISE_SETTING"]
65-
self.cruise_buttons = cp.vl["SCM_BUTTONS"]["CRUISE_BUTTONS"]
66-
6767
# used for car hud message
6868
self.is_metric = not cp.vl["CAR_SPEED"]["IMPERIAL_UNIT"]
6969
self.v_cruise_factor = CV.MPH_TO_MS if self.dynamic_v_cruise_units and not self.is_metric else CV.KPH_TO_MS
@@ -199,11 +199,11 @@ def update(self, can_parsers) -> structs.CarState:
199199
# more info here: https://github.com/commaai/openpilot/pull/1867
200200
ret.leftBlindspot = cp_body.vl["BSM_STATUS_LEFT"]["BSM_ALERT"] == 1
201201
ret.rightBlindspot = cp_body.vl["BSM_STATUS_RIGHT"]["BSM_ALERT"] == 1
202+
create_button_events(ret, [
203+
ButtonEvent(cp.vl["SCM_BUTTONS"]["CRUISE_BUTTONS"], BUTTON_MAPS),
204+
ButtonEvent(cp.vl["SCM_BUTTONS"]["CRUISE_SETTING"], SETTINGS_BUTTON_MAPS)
205+
])
202206

203-
ret.buttonEvents = [
204-
*create_button_events(self.cruise_buttons, prev_cruise_buttons, BUTTONS_DICT),
205-
*create_button_events(self.cruise_setting, prev_cruise_setting, SETTINGS_BUTTONS_DICT),
206-
]
207207

208208
return ret
209209

opendbc/car/hyundai/carstate.py

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import math
44

55
from opendbc.can import CANDefine, CANParser
6-
from opendbc.car import Bus, create_button_events, structs
6+
from opendbc.car import Bus, create_button_events, structs, ButtonEvent, ButtonMap
77
from opendbc.car.common.conversions import Conversions as CV
88
from opendbc.car.hyundai.hyundaicanfd import CanBus
99
from opendbc.car.hyundai.values import HyundaiFlags, CAR, DBC, Buttons, CarControllerParams
@@ -17,8 +17,12 @@
1717

1818
# Cancel button can sometimes be ACC pause/resume button, main button can also enable on some cars
1919
ENABLE_BUTTONS = (Buttons.RES_ACCEL, Buttons.SET_DECEL, Buttons.CANCEL)
20-
BUTTONS_DICT = {Buttons.RES_ACCEL: ButtonType.accelCruise, Buttons.SET_DECEL: ButtonType.decelCruise,
21-
Buttons.GAP_DIST: ButtonType.gapAdjustCruise, Buttons.CANCEL: ButtonType.cancel}
20+
BUTTON_MAPS: list[ButtonMap] = (
21+
ButtonMap(ButtonType.accelCruise, Buttons.RES_ACCEL),
22+
ButtonMap(ButtonType.decelCruise, Buttons.SET_DECEL),
23+
ButtonMap(ButtonType.gapAdjustCruise, Buttons.GAP_DIST),
24+
ButtonMap(ButtonType.cancel, Buttons.CANCEL)
25+
)
2226

2327

2428
class CarState(CarStateBase):
@@ -28,6 +32,7 @@ def __init__(self, CP):
2832

2933
self.cruise_buttons: deque = deque([Buttons.NONE] * PREV_BUTTON_SAMPLES, maxlen=PREV_BUTTON_SAMPLES)
3034
self.main_buttons: deque = deque([Buttons.NONE] * PREV_BUTTON_SAMPLES, maxlen=PREV_BUTTON_SAMPLES)
35+
self.main_btn_map = [ButtonMap(ButtonType.mainCruise)]
3136
self.lda_button = 0
3237

3338
self.gear_msg_canfd = "ACCELERATOR" if CP.flags & HyundaiFlags.EV else \
@@ -181,17 +186,16 @@ def update(self, can_parsers) -> structs.CarState:
181186
self.lkas11 = copy.copy(cp_cam.vl["LKAS11"])
182187
self.clu11 = copy.copy(cp.vl["CLU11"])
183188
self.steer_state = cp.vl["MDPS12"]["CF_Mdps_ToiActive"] # 0 NOT ACTIVE, 1 ACTIVE
184-
prev_cruise_buttons = self.cruise_buttons[-1]
185-
prev_main_buttons = self.main_buttons[-1]
186-
prev_lda_button = self.lda_button
187189
self.cruise_buttons.extend(cp.vl_all["CLU11"]["CF_Clu_CruiseSwState"])
188190
self.main_buttons.extend(cp.vl_all["CLU11"]["CF_Clu_CruiseSwMain"])
189191
if self.CP.flags & HyundaiFlags.HAS_LDA_BUTTON:
190192
self.lda_button = cp.vl["BCM_PO_11"]["LDA_BTN"]
191193

192-
ret.buttonEvents = [*create_button_events(self.cruise_buttons[-1], prev_cruise_buttons, BUTTONS_DICT),
193-
*create_button_events(self.main_buttons[-1], prev_main_buttons, {1: ButtonType.mainCruise}),
194-
*create_button_events(self.lda_button, prev_lda_button, {1: ButtonType.lkas})]
194+
create_button_events(ret, [
195+
ButtonEvent(self.cruise_buttons[-1], BUTTON_MAPS),
196+
ButtonEvent(self.main_buttons[-1], self.main_btn_map),
197+
ButtonEvent(self.lda_button, self.lkas_btn_map)
198+
])
195199

196200
ret.blockPcmEnable = not self.recent_button_interaction()
197201

@@ -274,9 +278,6 @@ def update_canfd(self, can_parsers) -> structs.CarState:
274278
if self.CP.flags & HyundaiFlags.EV:
275279
ret.cruiseState.nonAdaptive = cp.vl["MANUAL_SPEED_LIMIT_ASSIST"]["MSLA_ENABLED"] == 1
276280

277-
prev_cruise_buttons = self.cruise_buttons[-1]
278-
prev_main_buttons = self.main_buttons[-1]
279-
prev_lda_button = self.lda_button
280281
self.cruise_buttons.extend(cp.vl_all[self.cruise_btns_msg_canfd]["CRUISE_BUTTONS"])
281282
self.main_buttons.extend(cp.vl_all[self.cruise_btns_msg_canfd]["ADAPTIVE_CRUISE_MAIN_BTN"])
282283
self.lda_button = cp.vl[self.cruise_btns_msg_canfd]["LDA_BTN"]
@@ -287,9 +288,11 @@ def update_canfd(self, can_parsers) -> structs.CarState:
287288
self.lfa_block_msg = copy.copy(cp_cam.vl["CAM_0x362"] if self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING_ALT
288289
else cp_cam.vl["CAM_0x2a4"])
289290

290-
ret.buttonEvents = [*create_button_events(self.cruise_buttons[-1], prev_cruise_buttons, BUTTONS_DICT),
291-
*create_button_events(self.main_buttons[-1], prev_main_buttons, {1: ButtonType.mainCruise}),
292-
*create_button_events(self.lda_button, prev_lda_button, {1: ButtonType.lkas})]
291+
create_button_events(ret, [
292+
ButtonEvent(self.cruise_buttons[-1], BUTTON_MAPS),
293+
ButtonEvent(self.main_buttons[-1], self.main_btn_map),
294+
ButtonEvent(self.lda_button, self.lkas_btn_map)
295+
])
293296

294297
ret.blockPcmEnable = not self.recent_button_interaction()
295298

opendbc/car/interfaces.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from functools import cache
1010

1111
from opendbc.car import DT_CTRL, apply_hysteresis, gen_empty_fingerprint, scale_rot_inertia, scale_tire_stiffness, STD_CARGO_KG
12-
from opendbc.car import structs
12+
from opendbc.car import structs, ButtonMap
1313
from opendbc.car.can_definitions import CanData, CanRecvCallable, CanSendCallable
1414
from opendbc.car.common.basedir import BASEDIR
1515
from opendbc.car.common.conversions import Conversions as CV
@@ -271,6 +271,8 @@ def __init__(self, CP: structs.CarParams):
271271
self.CP = CP
272272
self.car_fingerprint = CP.carFingerprint
273273
self.out = structs.CarState()
274+
self.dist_btn_map = [ButtonMap(ButtonType.gapAdjustCruise)]
275+
self.lkas_btn_map = [ButtonMap(ButtonType.lkas)]
274276

275277
self.cruise_buttons = 0
276278
self.left_blinker_cnt = 0

opendbc/car/mazda/carstate.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from opendbc.can import CANDefine, CANParser
2-
from opendbc.car import Bus, create_button_events, structs
2+
from opendbc.car import Bus, create_button_events, structs, ButtonEvent
33
from opendbc.car.common.conversions import Conversions as CV
44
from opendbc.car.interfaces import CarStateBase
55
from opendbc.car.mazda.values import DBC, LKAS_LIMITS
@@ -18,17 +18,12 @@ def __init__(self, CP):
1818
self.acc_active_last = False
1919
self.lkas_allowed_speed = False
2020

21-
self.distance_button = 0
22-
2321
def update(self, can_parsers) -> structs.CarState:
2422
cp = can_parsers[Bus.pt]
2523
cp_cam = can_parsers[Bus.cam]
2624

2725
ret = structs.CarState()
2826

29-
prev_distance_button = self.distance_button
30-
self.distance_button = cp.vl["CRZ_BTNS"]["DISTANCE_LESS"]
31-
3227
self.parse_wheel_speeds(ret,
3328
cp.vl["WHEEL_SPEEDS"]["FL"],
3429
cp.vl["WHEEL_SPEEDS"]["FR"],
@@ -113,7 +108,7 @@ def update(self, can_parsers) -> structs.CarState:
113108
ret.steerFaultPermanent = cp_cam.vl["CAM_LKAS"]["ERR_BIT_1"] == 1
114109

115110
# TODO: add button types for inc and dec
116-
ret.buttonEvents = create_button_events(self.distance_button, prev_distance_button, {1: ButtonType.gapAdjustCruise})
111+
create_button_events(ret, [ButtonEvent(cp.vl["CRZ_BTNS"]["DISTANCE_LESS"], self.dist_btn_map)])
117112

118113
return ret
119114

0 commit comments

Comments
 (0)