This commit is contained in:
firestarsdog
2026-11-04 00:46:58 -05:00
parent 08cfff03af
commit b0c03bdbda
30 changed files with 894 additions and 415 deletions
@@ -335,8 +335,8 @@ def test_actual_card_active_observation_requires_fresh_selfdrive_state(monkeypat
host.observe_ioniq6_long_authority = lambda *args: None
host.update_vehicle_state_context = lambda *args: None
def update_cruise_speed(_state, _pending, *, approved=None):
assert approved is None
def update_cruise_speed(_state, _pending, *, wheel_context=None):
assert wheel_context is None
host.update_cruise_speed = update_cruise_speed
host.v_cruise_helper = SimpleNamespace(slc_consumed_button=None, toyota=None)
host.startup_panda_configured = lambda: True
@@ -870,7 +870,7 @@ class TestIoniq6PrepublicationHandoff(unittest.TestCase):
card_owner.is_metric = True
card_owner.experimental_mode = False
card_owner.initial_cruise_speed = 40
card_owner.v_cruise_helper = SimpleNamespace(update_v_cruise=lambda *_, slc_enabled=False, slc_approved=None: None,
card_owner.v_cruise_helper = SimpleNamespace(update_v_cruise=lambda *_, slc_enabled=False, wheel_context=None: None,
initialize_v_cruise=lambda *_, **kwargs: None, slc_consumed_button=None, toyota=None,
slc_cruise_change=None, v_cruise_kph=60.0, v_cruise_cluster_kph=60.0)
card_owner.RI = SimpleNamespace(update=lambda _: None)
+3 -1
View File
@@ -333,7 +333,7 @@ struct SlcState @0xa1680744031fdb2d {
hasPendingAdjustedLimit @45 :Bool;
presentationAdjustedLimit @46 :Float32;
hasPresentationAdjustedLimit @47 :Bool;
overrideBasis @48 :Text; # none/pedal/persistent; qualified contribution only
overrideBasis @48 :Text; # none/pedal/persistent/wheel; qualified contribution only
retainedOverride @49 :Bool;
commandActionSequenceId @50 :UInt64; # UI sequence that issued this command
automaticAdoptionPending @51 :Bool;
@@ -347,6 +347,7 @@ struct SlcState @0xa1680744031fdb2d {
upcomingSpeedLimit @57 :Float32;
upcomingBoundaryId @58 :Text;
acceptedIsPreview @59 :Bool; # a temporary ceiling, ineligible for initializing SET
wheelOverrideSince @60 :UInt64; # nonzero while a wheel tap may cross the current adjusted limit
struct SourceReading {
source @0 :Text;
enabled @1 :Bool;
@@ -515,6 +516,7 @@ struct SlcCruiseEvent @0xcd96dafb67a082d0 {
trafficMode @13 :TrafficModeGesture;
wheelAction @14 :SlcAction.ControllerCruiseAction;
teslaStockCruise @15 :TeslaStockCruise;
wheelOverrideSince @16 :UInt64; # SLC eligibility captured by a physical + tap; zero for ordinary SET changes
struct TeslaStockCruise {
version @0 :UInt16;
+381 -347
View File
@@ -3741,17 +3741,17 @@ const ::capnp::_::RawSchema s_da96579883444c35 = {
0, 5, i_da96579883444c35, nullptr, nullptr, { &s_da96579883444c35, nullptr, nullptr, 0, 0, nullptr }, false
};
#endif // !CAPNP_LITE
static const ::capnp::_::AlignedData<1030> b_a1680744031fdb2d = {
static const ::capnp::_::AlignedData<1047> b_a1680744031fdb2d = {
{ 0, 0, 0, 0, 5, 0, 6, 0,
45, 219, 31, 3, 68, 7, 104, 161,
13, 0, 0, 0, 1, 0, 18, 0,
13, 0, 0, 0, 1, 0, 19, 0,
89, 10, 85, 29, 102, 186, 38, 181,
22, 0, 7, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
21, 0, 0, 0, 178, 0, 0, 0,
29, 0, 0, 0, 87, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
117, 0, 0, 0, 39, 13, 0, 0,
117, 0, 0, 0, 95, 13, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
99, 117, 115, 116, 111, 109, 46, 99,
@@ -3781,427 +3781,434 @@ static const ::capnp::_::AlignedData<1030> b_a1680744031fdb2d = {
67, 111, 110, 100, 105, 116, 105, 111,
110, 97, 108, 77, 111, 100, 101, 83,
116, 97, 116, 101, 0, 0, 0, 0,
240, 0, 0, 0, 3, 0, 4, 0,
244, 0, 0, 0, 3, 0, 4, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
129, 6, 0, 0, 58, 0, 0, 0,
157, 6, 0, 0, 58, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
124, 6, 0, 0, 3, 0, 1, 0,
136, 6, 0, 0, 2, 0, 1, 0,
152, 6, 0, 0, 3, 0, 1, 0,
164, 6, 0, 0, 2, 0, 1, 0,
1, 0, 0, 0, 1, 0, 0, 0,
0, 0, 1, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
133, 6, 0, 0, 74, 0, 0, 0,
161, 6, 0, 0, 74, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
132, 6, 0, 0, 3, 0, 1, 0,
144, 6, 0, 0, 2, 0, 1, 0,
160, 6, 0, 0, 3, 0, 1, 0,
172, 6, 0, 0, 2, 0, 1, 0,
2, 0, 0, 0, 2, 0, 0, 0,
0, 0, 1, 0, 2, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
141, 6, 0, 0, 66, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
136, 6, 0, 0, 3, 0, 1, 0,
148, 6, 0, 0, 2, 0, 1, 0,
3, 0, 0, 0, 3, 0, 0, 0,
0, 0, 1, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
145, 6, 0, 0, 106, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
144, 6, 0, 0, 3, 0, 1, 0,
156, 6, 0, 0, 2, 0, 1, 0,
4, 0, 0, 0, 4, 0, 0, 0,
0, 0, 1, 0, 4, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
153, 6, 0, 0, 58, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
148, 6, 0, 0, 3, 0, 1, 0,
160, 6, 0, 0, 2, 0, 1, 0,
5, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 5, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
157, 6, 0, 0, 114, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
156, 6, 0, 0, 3, 0, 1, 0,
168, 6, 0, 0, 2, 0, 1, 0,
6, 0, 0, 0, 5, 0, 0, 0,
0, 0, 1, 0, 6, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
165, 6, 0, 0, 82, 0, 0, 0,
169, 6, 0, 0, 66, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
164, 6, 0, 0, 3, 0, 1, 0,
176, 6, 0, 0, 2, 0, 1, 0,
7, 0, 0, 0, 1, 0, 0, 0,
0, 0, 1, 0, 7, 0, 0, 0,
3, 0, 0, 0, 3, 0, 0, 0,
0, 0, 1, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
173, 6, 0, 0, 114, 0, 0, 0,
173, 6, 0, 0, 106, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
172, 6, 0, 0, 3, 0, 1, 0,
184, 6, 0, 0, 2, 0, 1, 0,
8, 0, 0, 0, 128, 0, 0, 0,
0, 0, 1, 0, 8, 0, 0, 0,
4, 0, 0, 0, 4, 0, 0, 0,
0, 0, 1, 0, 4, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
181, 6, 0, 0, 66, 0, 0, 0,
181, 6, 0, 0, 58, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
176, 6, 0, 0, 3, 0, 1, 0,
188, 6, 0, 0, 2, 0, 1, 0,
9, 0, 0, 0, 129, 0, 0, 0,
0, 0, 1, 0, 9, 0, 0, 0,
5, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 5, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
185, 6, 0, 0, 98, 0, 0, 0,
185, 6, 0, 0, 114, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
184, 6, 0, 0, 3, 0, 1, 0,
196, 6, 0, 0, 2, 0, 1, 0,
10, 0, 0, 0, 6, 0, 0, 0,
0, 0, 1, 0, 10, 0, 0, 0,
6, 0, 0, 0, 5, 0, 0, 0,
0, 0, 1, 0, 6, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
193, 6, 0, 0, 130, 0, 0, 0,
193, 6, 0, 0, 82, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
192, 6, 0, 0, 3, 0, 1, 0,
204, 6, 0, 0, 2, 0, 1, 0,
11, 0, 0, 0, 7, 0, 0, 0,
0, 0, 1, 0, 11, 0, 0, 0,
7, 0, 0, 0, 1, 0, 0, 0,
0, 0, 1, 0, 7, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
201, 6, 0, 0, 58, 0, 0, 0,
201, 6, 0, 0, 114, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
196, 6, 0, 0, 3, 0, 1, 0,
208, 6, 0, 0, 2, 0, 1, 0,
12, 0, 0, 0, 5, 0, 0, 0,
0, 0, 1, 0, 12, 0, 0, 0,
200, 6, 0, 0, 3, 0, 1, 0,
212, 6, 0, 0, 2, 0, 1, 0,
8, 0, 0, 0, 128, 0, 0, 0,
0, 0, 1, 0, 8, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
205, 6, 0, 0, 90, 0, 0, 0,
209, 6, 0, 0, 66, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
204, 6, 0, 0, 3, 0, 1, 0,
216, 6, 0, 0, 2, 0, 1, 0,
13, 0, 0, 0, 6, 0, 0, 0,
0, 0, 1, 0, 13, 0, 0, 0,
9, 0, 0, 0, 129, 0, 0, 0,
0, 0, 1, 0, 9, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
213, 6, 0, 0, 154, 0, 0, 0,
213, 6, 0, 0, 98, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
216, 6, 0, 0, 3, 0, 1, 0,
228, 6, 0, 0, 2, 0, 1, 0,
14, 0, 0, 0, 130, 0, 0, 0,
0, 0, 1, 0, 14, 0, 0, 0,
212, 6, 0, 0, 3, 0, 1, 0,
224, 6, 0, 0, 2, 0, 1, 0,
10, 0, 0, 0, 6, 0, 0, 0,
0, 0, 1, 0, 10, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
225, 6, 0, 0, 98, 0, 0, 0,
221, 6, 0, 0, 130, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
220, 6, 0, 0, 3, 0, 1, 0,
232, 6, 0, 0, 2, 0, 1, 0,
11, 0, 0, 0, 7, 0, 0, 0,
0, 0, 1, 0, 11, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
229, 6, 0, 0, 58, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
224, 6, 0, 0, 3, 0, 1, 0,
236, 6, 0, 0, 2, 0, 1, 0,
15, 0, 0, 0, 7, 0, 0, 0,
0, 0, 1, 0, 15, 0, 0, 0,
12, 0, 0, 0, 5, 0, 0, 0,
0, 0, 1, 0, 12, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
233, 6, 0, 0, 58, 0, 0, 0,
233, 6, 0, 0, 90, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
228, 6, 0, 0, 3, 0, 1, 0,
240, 6, 0, 0, 2, 0, 1, 0,
16, 0, 0, 0, 8, 0, 0, 0,
0, 0, 1, 0, 16, 0, 0, 0,
232, 6, 0, 0, 3, 0, 1, 0,
244, 6, 0, 0, 2, 0, 1, 0,
13, 0, 0, 0, 6, 0, 0, 0,
0, 0, 1, 0, 13, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
237, 6, 0, 0, 106, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
236, 6, 0, 0, 3, 0, 1, 0,
248, 6, 0, 0, 2, 0, 1, 0,
17, 0, 0, 0, 131, 0, 0, 0,
0, 0, 1, 0, 17, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
245, 6, 0, 0, 90, 0, 0, 0,
241, 6, 0, 0, 154, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
244, 6, 0, 0, 3, 0, 1, 0,
0, 7, 0, 0, 2, 0, 1, 0,
18, 0, 0, 0, 9, 0, 0, 0,
0, 0, 1, 0, 18, 0, 0, 0,
14, 0, 0, 0, 130, 0, 0, 0,
0, 0, 1, 0, 14, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
253, 6, 0, 0, 146, 0, 0, 0,
253, 6, 0, 0, 98, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
252, 6, 0, 0, 3, 0, 1, 0,
8, 7, 0, 0, 2, 0, 1, 0,
15, 0, 0, 0, 7, 0, 0, 0,
0, 0, 1, 0, 15, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
5, 7, 0, 0, 58, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 7, 0, 0, 3, 0, 1, 0,
12, 7, 0, 0, 2, 0, 1, 0,
19, 0, 0, 0, 132, 0, 0, 0,
0, 0, 1, 0, 19, 0, 0, 0,
16, 0, 0, 0, 8, 0, 0, 0,
0, 0, 1, 0, 16, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
9, 7, 0, 0, 90, 0, 0, 0,
9, 7, 0, 0, 106, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
8, 7, 0, 0, 3, 0, 1, 0,
20, 7, 0, 0, 2, 0, 1, 0,
20, 0, 0, 0, 5, 0, 0, 0,
0, 0, 1, 0, 20, 0, 0, 0,
17, 0, 0, 0, 131, 0, 0, 0,
0, 0, 1, 0, 17, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
17, 7, 0, 0, 90, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
16, 7, 0, 0, 3, 0, 1, 0,
28, 7, 0, 0, 2, 0, 1, 0,
21, 0, 0, 0, 6, 0, 0, 0,
0, 0, 1, 0, 21, 0, 0, 0,
18, 0, 0, 0, 9, 0, 0, 0,
0, 0, 1, 0, 18, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
25, 7, 0, 0, 122, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
24, 7, 0, 0, 3, 0, 1, 0,
36, 7, 0, 0, 2, 0, 1, 0,
22, 0, 0, 0, 8, 0, 0, 0,
0, 0, 1, 0, 22, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
33, 7, 0, 0, 58, 0, 0, 0,
25, 7, 0, 0, 146, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
28, 7, 0, 0, 3, 0, 1, 0,
40, 7, 0, 0, 2, 0, 1, 0,
19, 0, 0, 0, 132, 0, 0, 0,
0, 0, 1, 0, 19, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
37, 7, 0, 0, 90, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
36, 7, 0, 0, 3, 0, 1, 0,
48, 7, 0, 0, 2, 0, 1, 0,
20, 0, 0, 0, 5, 0, 0, 0,
0, 0, 1, 0, 20, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
45, 7, 0, 0, 90, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
44, 7, 0, 0, 3, 0, 1, 0,
56, 7, 0, 0, 2, 0, 1, 0,
21, 0, 0, 0, 6, 0, 0, 0,
0, 0, 1, 0, 21, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
53, 7, 0, 0, 122, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
52, 7, 0, 0, 3, 0, 1, 0,
64, 7, 0, 0, 2, 0, 1, 0,
22, 0, 0, 0, 8, 0, 0, 0,
0, 0, 1, 0, 22, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
61, 7, 0, 0, 58, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
56, 7, 0, 0, 3, 0, 1, 0,
68, 7, 0, 0, 2, 0, 1, 0,
23, 0, 0, 0, 7, 0, 0, 0,
0, 0, 1, 0, 23, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
37, 7, 0, 0, 138, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
40, 7, 0, 0, 3, 0, 1, 0,
52, 7, 0, 0, 2, 0, 1, 0,
24, 0, 0, 0, 9, 0, 0, 0,
0, 0, 1, 0, 24, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
49, 7, 0, 0, 106, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
48, 7, 0, 0, 3, 0, 1, 0,
60, 7, 0, 0, 2, 0, 1, 0,
25, 0, 0, 0, 10, 0, 0, 0,
0, 0, 1, 0, 25, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
57, 7, 0, 0, 194, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
60, 7, 0, 0, 3, 0, 1, 0,
72, 7, 0, 0, 2, 0, 1, 0,
26, 0, 0, 0, 16, 0, 0, 0,
0, 0, 1, 0, 26, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
69, 7, 0, 0, 114, 0, 0, 0,
65, 7, 0, 0, 138, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
68, 7, 0, 0, 3, 0, 1, 0,
80, 7, 0, 0, 2, 0, 1, 0,
27, 0, 0, 0, 9, 0, 0, 0,
0, 0, 1, 0, 27, 0, 0, 0,
24, 0, 0, 0, 9, 0, 0, 0,
0, 0, 1, 0, 24, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
77, 7, 0, 0, 186, 0, 0, 0,
77, 7, 0, 0, 106, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
80, 7, 0, 0, 3, 0, 1, 0,
92, 7, 0, 0, 2, 0, 1, 0,
28, 0, 0, 0, 10, 0, 0, 0,
0, 0, 1, 0, 28, 0, 0, 0,
76, 7, 0, 0, 3, 0, 1, 0,
88, 7, 0, 0, 2, 0, 1, 0,
25, 0, 0, 0, 10, 0, 0, 0,
0, 0, 1, 0, 25, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
89, 7, 0, 0, 202, 0, 0, 0,
85, 7, 0, 0, 194, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
88, 7, 0, 0, 3, 0, 1, 0,
100, 7, 0, 0, 2, 0, 1, 0,
26, 0, 0, 0, 16, 0, 0, 0,
0, 0, 1, 0, 26, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
97, 7, 0, 0, 114, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
96, 7, 0, 0, 3, 0, 1, 0,
108, 7, 0, 0, 2, 0, 1, 0,
29, 0, 0, 0, 11, 0, 0, 0,
0, 0, 1, 0, 29, 0, 0, 0,
27, 0, 0, 0, 9, 0, 0, 0,
0, 0, 1, 0, 27, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
105, 7, 0, 0, 82, 0, 0, 0,
105, 7, 0, 0, 186, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
104, 7, 0, 0, 3, 0, 1, 0,
116, 7, 0, 0, 2, 0, 1, 0,
30, 0, 0, 0, 11, 0, 0, 0,
0, 0, 1, 0, 30, 0, 0, 0,
108, 7, 0, 0, 3, 0, 1, 0,
120, 7, 0, 0, 2, 0, 1, 0,
28, 0, 0, 0, 10, 0, 0, 0,
0, 0, 1, 0, 28, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
113, 7, 0, 0, 114, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
112, 7, 0, 0, 3, 0, 1, 0,
124, 7, 0, 0, 2, 0, 1, 0,
31, 0, 0, 0, 12, 0, 0, 0,
0, 0, 1, 0, 31, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
121, 7, 0, 0, 50, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
116, 7, 0, 0, 3, 0, 1, 0,
128, 7, 0, 0, 2, 0, 1, 0,
32, 0, 0, 0, 13, 0, 0, 0,
0, 0, 1, 0, 32, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
125, 7, 0, 0, 130, 0, 0, 0,
117, 7, 0, 0, 202, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
124, 7, 0, 0, 3, 0, 1, 0,
136, 7, 0, 0, 2, 0, 1, 0,
33, 0, 0, 0, 14, 0, 0, 0,
0, 0, 1, 0, 33, 0, 0, 0,
29, 0, 0, 0, 11, 0, 0, 0,
0, 0, 1, 0, 29, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
133, 7, 0, 0, 98, 0, 0, 0,
133, 7, 0, 0, 82, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
132, 7, 0, 0, 3, 0, 1, 0,
144, 7, 0, 0, 2, 0, 1, 0,
34, 0, 0, 0, 15, 0, 0, 0,
0, 0, 1, 0, 34, 0, 0, 0,
30, 0, 0, 0, 11, 0, 0, 0,
0, 0, 1, 0, 30, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
141, 7, 0, 0, 122, 0, 0, 0,
141, 7, 0, 0, 114, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
140, 7, 0, 0, 3, 0, 1, 0,
152, 7, 0, 0, 2, 0, 1, 0,
35, 0, 0, 0, 16, 0, 0, 0,
0, 0, 1, 0, 35, 0, 0, 0,
31, 0, 0, 0, 12, 0, 0, 0,
0, 0, 1, 0, 31, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
149, 7, 0, 0, 122, 0, 0, 0,
149, 7, 0, 0, 50, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
148, 7, 0, 0, 3, 0, 1, 0,
160, 7, 0, 0, 2, 0, 1, 0,
36, 0, 0, 0, 17, 0, 0, 0,
0, 0, 1, 0, 36, 0, 0, 0,
144, 7, 0, 0, 3, 0, 1, 0,
156, 7, 0, 0, 2, 0, 1, 0,
32, 0, 0, 0, 13, 0, 0, 0,
0, 0, 1, 0, 32, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
157, 7, 0, 0, 114, 0, 0, 0,
153, 7, 0, 0, 130, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
156, 7, 0, 0, 3, 0, 1, 0,
168, 7, 0, 0, 2, 0, 1, 0,
37, 0, 0, 0, 17, 0, 0, 0,
0, 0, 1, 0, 37, 0, 0, 0,
152, 7, 0, 0, 3, 0, 1, 0,
164, 7, 0, 0, 2, 0, 1, 0,
33, 0, 0, 0, 14, 0, 0, 0,
0, 0, 1, 0, 33, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
165, 7, 0, 0, 186, 0, 0, 0,
161, 7, 0, 0, 98, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
160, 7, 0, 0, 3, 0, 1, 0,
172, 7, 0, 0, 2, 0, 1, 0,
34, 0, 0, 0, 15, 0, 0, 0,
0, 0, 1, 0, 34, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
169, 7, 0, 0, 122, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
168, 7, 0, 0, 3, 0, 1, 0,
180, 7, 0, 0, 2, 0, 1, 0,
38, 0, 0, 0, 133, 0, 0, 0,
0, 0, 1, 0, 38, 0, 0, 0,
35, 0, 0, 0, 16, 0, 0, 0,
0, 0, 1, 0, 35, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
177, 7, 0, 0, 210, 0, 0, 0,
177, 7, 0, 0, 122, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
176, 7, 0, 0, 3, 0, 1, 0,
188, 7, 0, 0, 2, 0, 1, 0,
36, 0, 0, 0, 17, 0, 0, 0,
0, 0, 1, 0, 36, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
185, 7, 0, 0, 114, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
184, 7, 0, 0, 3, 0, 1, 0,
196, 7, 0, 0, 2, 0, 1, 0,
39, 0, 0, 0, 134, 0, 0, 0,
0, 0, 1, 0, 39, 0, 0, 0,
37, 0, 0, 0, 17, 0, 0, 0,
0, 0, 1, 0, 37, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
193, 7, 0, 0, 138, 0, 0, 0,
193, 7, 0, 0, 186, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
196, 7, 0, 0, 3, 0, 1, 0,
208, 7, 0, 0, 2, 0, 1, 0,
38, 0, 0, 0, 133, 0, 0, 0,
0, 0, 1, 0, 38, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
205, 7, 0, 0, 210, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
212, 7, 0, 0, 3, 0, 1, 0,
224, 7, 0, 0, 2, 0, 1, 0,
39, 0, 0, 0, 134, 0, 0, 0,
0, 0, 1, 0, 39, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
221, 7, 0, 0, 138, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
224, 7, 0, 0, 3, 0, 1, 0,
236, 7, 0, 0, 2, 0, 1, 0,
40, 0, 0, 0, 135, 0, 0, 0,
0, 0, 1, 0, 40, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
205, 7, 0, 0, 170, 0, 0, 0,
233, 7, 0, 0, 170, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
208, 7, 0, 0, 3, 0, 1, 0,
220, 7, 0, 0, 2, 0, 1, 0,
236, 7, 0, 0, 3, 0, 1, 0,
248, 7, 0, 0, 2, 0, 1, 0,
41, 0, 0, 0, 18, 0, 0, 0,
0, 0, 1, 0, 41, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
217, 7, 0, 0, 122, 0, 0, 0,
245, 7, 0, 0, 122, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
216, 7, 0, 0, 3, 0, 1, 0,
244, 7, 0, 0, 2, 0, 1, 0,
244, 7, 0, 0, 3, 0, 1, 0,
16, 8, 0, 0, 2, 0, 1, 0,
42, 0, 0, 0, 24, 0, 0, 0,
0, 0, 1, 0, 42, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
241, 7, 0, 0, 178, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
244, 7, 0, 0, 3, 0, 1, 0,
0, 8, 0, 0, 2, 0, 1, 0,
43, 0, 0, 0, 136, 0, 0, 0,
0, 0, 1, 0, 43, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
253, 7, 0, 0, 202, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
4, 8, 0, 0, 3, 0, 1, 0,
16, 8, 0, 0, 2, 0, 1, 0,
44, 0, 0, 0, 25, 0, 0, 0,
0, 0, 1, 0, 44, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
13, 8, 0, 0, 170, 0, 0, 0,
13, 8, 0, 0, 178, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
16, 8, 0, 0, 3, 0, 1, 0,
28, 8, 0, 0, 2, 0, 1, 0,
45, 0, 0, 0, 137, 0, 0, 0,
0, 0, 1, 0, 45, 0, 0, 0,
43, 0, 0, 0, 136, 0, 0, 0,
0, 0, 1, 0, 43, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
25, 8, 0, 0, 194, 0, 0, 0,
25, 8, 0, 0, 202, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
28, 8, 0, 0, 3, 0, 1, 0,
40, 8, 0, 0, 2, 0, 1, 0,
46, 0, 0, 0, 26, 0, 0, 0,
0, 0, 1, 0, 46, 0, 0, 0,
32, 8, 0, 0, 3, 0, 1, 0,
44, 8, 0, 0, 2, 0, 1, 0,
44, 0, 0, 0, 25, 0, 0, 0,
0, 0, 1, 0, 44, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
37, 8, 0, 0, 210, 0, 0, 0,
41, 8, 0, 0, 170, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
44, 8, 0, 0, 3, 0, 1, 0,
56, 8, 0, 0, 2, 0, 1, 0,
45, 0, 0, 0, 137, 0, 0, 0,
0, 0, 1, 0, 45, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
53, 8, 0, 0, 194, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
56, 8, 0, 0, 3, 0, 1, 0,
68, 8, 0, 0, 2, 0, 1, 0,
46, 0, 0, 0, 26, 0, 0, 0,
0, 0, 1, 0, 46, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
65, 8, 0, 0, 210, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
72, 8, 0, 0, 3, 0, 1, 0,
84, 8, 0, 0, 2, 0, 1, 0,
47, 0, 0, 0, 138, 0, 0, 0,
0, 0, 1, 0, 47, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
53, 8, 0, 0, 234, 0, 0, 0,
81, 8, 0, 0, 234, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
60, 8, 0, 0, 3, 0, 1, 0,
72, 8, 0, 0, 2, 0, 1, 0,
88, 8, 0, 0, 3, 0, 1, 0,
100, 8, 0, 0, 2, 0, 1, 0,
48, 0, 0, 0, 19, 0, 0, 0,
0, 0, 1, 0, 48, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
69, 8, 0, 0, 114, 0, 0, 0,
97, 8, 0, 0, 114, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
68, 8, 0, 0, 3, 0, 1, 0,
80, 8, 0, 0, 2, 0, 1, 0,
96, 8, 0, 0, 3, 0, 1, 0,
108, 8, 0, 0, 2, 0, 1, 0,
49, 0, 0, 0, 139, 0, 0, 0,
0, 0, 1, 0, 49, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
77, 8, 0, 0, 138, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
80, 8, 0, 0, 3, 0, 1, 0,
92, 8, 0, 0, 2, 0, 1, 0,
50, 0, 0, 0, 14, 0, 0, 0,
0, 0, 1, 0, 50, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
89, 8, 0, 0, 194, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
92, 8, 0, 0, 3, 0, 1, 0,
104, 8, 0, 0, 2, 0, 1, 0,
51, 0, 0, 0, 140, 0, 0, 0,
0, 0, 1, 0, 51, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
101, 8, 0, 0, 202, 0, 0, 0,
105, 8, 0, 0, 138, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
108, 8, 0, 0, 3, 0, 1, 0,
120, 8, 0, 0, 2, 0, 1, 0,
52, 0, 0, 0, 15, 0, 0, 0,
0, 0, 1, 0, 52, 0, 0, 0,
50, 0, 0, 0, 14, 0, 0, 0,
0, 0, 1, 0, 50, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
117, 8, 0, 0, 170, 0, 0, 0,
117, 8, 0, 0, 194, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
120, 8, 0, 0, 3, 0, 1, 0,
132, 8, 0, 0, 2, 0, 1, 0,
51, 0, 0, 0, 140, 0, 0, 0,
0, 0, 1, 0, 51, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
129, 8, 0, 0, 202, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
136, 8, 0, 0, 3, 0, 1, 0,
148, 8, 0, 0, 2, 0, 1, 0,
52, 0, 0, 0, 15, 0, 0, 0,
0, 0, 1, 0, 52, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
145, 8, 0, 0, 170, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
148, 8, 0, 0, 3, 0, 1, 0,
160, 8, 0, 0, 2, 0, 1, 0,
53, 0, 0, 0, 27, 0, 0, 0,
0, 0, 1, 0, 53, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
129, 8, 0, 0, 186, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
132, 8, 0, 0, 3, 0, 1, 0,
144, 8, 0, 0, 2, 0, 1, 0,
54, 0, 0, 0, 20, 0, 0, 0,
0, 0, 1, 0, 54, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
141, 8, 0, 0, 154, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
144, 8, 0, 0, 3, 0, 1, 0,
156, 8, 0, 0, 2, 0, 1, 0,
55, 0, 0, 0, 32, 0, 0, 0,
0, 0, 1, 0, 55, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
153, 8, 0, 0, 210, 0, 0, 0,
157, 8, 0, 0, 186, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
160, 8, 0, 0, 3, 0, 1, 0,
172, 8, 0, 0, 2, 0, 1, 0,
56, 0, 0, 0, 17, 0, 0, 0,
0, 0, 1, 0, 56, 0, 0, 0,
54, 0, 0, 0, 20, 0, 0, 0,
0, 0, 1, 0, 54, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
169, 8, 0, 0, 138, 0, 0, 0,
169, 8, 0, 0, 154, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
172, 8, 0, 0, 3, 0, 1, 0,
184, 8, 0, 0, 2, 0, 1, 0,
55, 0, 0, 0, 32, 0, 0, 0,
0, 0, 1, 0, 55, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
181, 8, 0, 0, 210, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
188, 8, 0, 0, 3, 0, 1, 0,
200, 8, 0, 0, 2, 0, 1, 0,
56, 0, 0, 0, 17, 0, 0, 0,
0, 0, 1, 0, 56, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
197, 8, 0, 0, 138, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
200, 8, 0, 0, 3, 0, 1, 0,
212, 8, 0, 0, 2, 0, 1, 0,
57, 0, 0, 0, 33, 0, 0, 0,
0, 0, 1, 0, 57, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
181, 8, 0, 0, 154, 0, 0, 0,
209, 8, 0, 0, 154, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
184, 8, 0, 0, 3, 0, 1, 0,
196, 8, 0, 0, 2, 0, 1, 0,
212, 8, 0, 0, 3, 0, 1, 0,
224, 8, 0, 0, 2, 0, 1, 0,
58, 0, 0, 0, 21, 0, 0, 0,
0, 0, 1, 0, 58, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
193, 8, 0, 0, 154, 0, 0, 0,
221, 8, 0, 0, 154, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
196, 8, 0, 0, 3, 0, 1, 0,
208, 8, 0, 0, 2, 0, 1, 0,
224, 8, 0, 0, 3, 0, 1, 0,
236, 8, 0, 0, 2, 0, 1, 0,
59, 0, 0, 0, 141, 0, 0, 0,
0, 0, 1, 0, 59, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
205, 8, 0, 0, 146, 0, 0, 0,
233, 8, 0, 0, 146, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
208, 8, 0, 0, 3, 0, 1, 0,
220, 8, 0, 0, 2, 0, 1, 0,
236, 8, 0, 0, 3, 0, 1, 0,
248, 8, 0, 0, 2, 0, 1, 0,
60, 0, 0, 0, 18, 0, 0, 0,
0, 0, 1, 0, 60, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
245, 8, 0, 0, 154, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
248, 8, 0, 0, 3, 0, 1, 0,
4, 9, 0, 0, 2, 0, 1, 0,
115, 108, 111, 116, 73, 100, 0, 0,
12, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
@@ -4771,6 +4778,16 @@ static const ::capnp::_::AlignedData<1030> b_a1680744031fdb2d = {
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
119, 104, 101, 101, 108, 79, 118, 101,
114, 114, 105, 100, 101, 83, 105, 110,
99, 101, 0, 0, 0, 0, 0, 0,
9, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
9, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, }
};
::capnp::word const* const bp_a1680744031fdb2d = b_a1680744031fdb2d.words;
@@ -4782,11 +4799,11 @@ static const ::capnp::_::RawSchema* const d_a1680744031fdb2d[] = {
&s_f58695a93b568caf,
&s_fb41e1a738d832cb,
};
static const uint16_t m_a1680744031fdb2d[] = {42, 59, 56, 35, 13, 23, 24, 51, 50, 29, 30, 32, 31, 20, 9, 40, 16, 37, 8, 7, 14, 43, 17, 38, 19, 45, 47, 39, 55, 54, 53, 10, 15, 48, 44, 52, 36, 18, 46, 21, 4, 49, 6, 0, 1, 11, 26, 27, 25, 41, 28, 12, 22, 34, 33, 58, 57, 2, 3, 5};
static const uint16_t i_a1680744031fdb2d[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59};
static const uint16_t m_a1680744031fdb2d[] = {42, 59, 56, 35, 13, 23, 24, 51, 50, 29, 30, 32, 31, 20, 9, 40, 16, 37, 8, 7, 14, 43, 17, 38, 19, 45, 47, 39, 55, 54, 53, 10, 15, 48, 44, 52, 36, 18, 46, 21, 4, 49, 6, 0, 1, 11, 26, 27, 25, 41, 28, 12, 22, 34, 33, 58, 57, 2, 3, 5, 60};
static const uint16_t i_a1680744031fdb2d[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60};
const ::capnp::_::RawSchema s_a1680744031fdb2d = {
0xa1680744031fdb2d, b_a1680744031fdb2d.words, 1030, d_a1680744031fdb2d, m_a1680744031fdb2d,
5, 60, i_a1680744031fdb2d, nullptr, nullptr, { &s_a1680744031fdb2d, nullptr, nullptr, 0, 0, nullptr }, false
0xa1680744031fdb2d, b_a1680744031fdb2d.words, 1047, d_a1680744031fdb2d, m_a1680744031fdb2d,
5, 61, i_a1680744031fdb2d, nullptr, nullptr, { &s_a1680744031fdb2d, nullptr, nullptr, 0, 0, nullptr }, false
};
#endif // !CAPNP_LITE
static const ::capnp::_::AlignedData<81> b_a7940385bedd1d4c = {
@@ -6891,17 +6908,17 @@ const ::capnp::_::RawSchema s_b8cbd9e666181b95 = {
};
#endif // !CAPNP_LITE
CAPNP_DEFINE_ENUM(Kind_b8cbd9e666181b95, b8cbd9e666181b95);
static const ::capnp::_::AlignedData<295> b_cd96dafb67a082d0 = {
static const ::capnp::_::AlignedData<312> b_cd96dafb67a082d0 = {
{ 0, 0, 0, 0, 5, 0, 6, 0,
208, 130, 160, 103, 251, 218, 150, 205,
13, 0, 0, 0, 1, 0, 7, 0,
13, 0, 0, 0, 1, 0, 8, 0,
89, 10, 85, 29, 102, 186, 38, 181,
6, 0, 7, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
21, 0, 0, 0, 226, 0, 0, 0,
33, 0, 0, 0, 87, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
113, 0, 0, 0, 135, 3, 0, 0,
113, 0, 0, 0, 191, 3, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
99, 117, 115, 116, 111, 109, 46, 99,
@@ -6930,119 +6947,126 @@ static const ::capnp::_::AlignedData<295> b_cd96dafb67a082d0 = {
77, 97, 110, 117, 97, 108, 77, 111,
100, 101, 71, 101, 115, 116, 117, 114,
101, 0, 0, 0, 0, 0, 0, 0,
64, 0, 0, 0, 3, 0, 4, 0,
68, 0, 0, 0, 3, 0, 4, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
177, 1, 0, 0, 66, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
172, 1, 0, 0, 3, 0, 1, 0,
184, 1, 0, 0, 2, 0, 1, 0,
1, 0, 0, 0, 1, 0, 0, 0,
0, 0, 1, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
181, 1, 0, 0, 138, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
184, 1, 0, 0, 3, 0, 1, 0,
196, 1, 0, 0, 2, 0, 1, 0,
2, 0, 0, 0, 4, 0, 0, 0,
0, 0, 1, 0, 2, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
193, 1, 0, 0, 98, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
192, 1, 0, 0, 3, 0, 1, 0,
204, 1, 0, 0, 2, 0, 1, 0,
3, 0, 0, 0, 5, 0, 0, 0,
0, 0, 1, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
201, 1, 0, 0, 98, 0, 0, 0,
205, 1, 0, 0, 66, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
200, 1, 0, 0, 3, 0, 1, 0,
212, 1, 0, 0, 2, 0, 1, 0,
4, 0, 0, 0, 12, 0, 0, 0,
0, 0, 1, 0, 4, 0, 0, 0,
1, 0, 0, 0, 1, 0, 0, 0,
0, 0, 1, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
209, 1, 0, 0, 58, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
204, 1, 0, 0, 3, 0, 1, 0,
216, 1, 0, 0, 2, 0, 1, 0,
5, 0, 0, 0, 208, 0, 0, 0,
0, 0, 1, 0, 5, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
213, 1, 0, 0, 82, 0, 0, 0,
209, 1, 0, 0, 138, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
212, 1, 0, 0, 3, 0, 1, 0,
224, 1, 0, 0, 2, 0, 1, 0,
6, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 6, 0, 0, 0,
2, 0, 0, 0, 4, 0, 0, 0,
0, 0, 1, 0, 2, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
221, 1, 0, 0, 146, 0, 0, 0,
221, 1, 0, 0, 98, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
224, 1, 0, 0, 3, 0, 1, 0,
236, 1, 0, 0, 2, 0, 1, 0,
7, 0, 0, 0, 14, 0, 0, 0,
0, 0, 1, 0, 7, 0, 0, 0,
220, 1, 0, 0, 3, 0, 1, 0,
232, 1, 0, 0, 2, 0, 1, 0,
3, 0, 0, 0, 5, 0, 0, 0,
0, 0, 1, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
233, 1, 0, 0, 42, 0, 0, 0,
229, 1, 0, 0, 98, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
228, 1, 0, 0, 3, 0, 1, 0,
240, 1, 0, 0, 2, 0, 1, 0,
8, 0, 0, 0, 1, 0, 0, 0,
0, 0, 1, 0, 8, 0, 0, 0,
4, 0, 0, 0, 12, 0, 0, 0,
0, 0, 1, 0, 4, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
237, 1, 0, 0, 82, 0, 0, 0,
237, 1, 0, 0, 58, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
236, 1, 0, 0, 3, 0, 1, 0,
248, 1, 0, 0, 2, 0, 1, 0,
9, 0, 0, 0, 4, 0, 0, 0,
0, 0, 1, 0, 9, 0, 0, 0,
232, 1, 0, 0, 3, 0, 1, 0,
244, 1, 0, 0, 2, 0, 1, 0,
5, 0, 0, 0, 208, 0, 0, 0,
0, 0, 1, 0, 5, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
245, 1, 0, 0, 90, 0, 0, 0,
241, 1, 0, 0, 82, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
244, 1, 0, 0, 3, 0, 1, 0,
0, 2, 0, 0, 2, 0, 1, 0,
10, 0, 0, 0, 5, 0, 0, 0,
0, 0, 1, 0, 10, 0, 0, 0,
240, 1, 0, 0, 3, 0, 1, 0,
252, 1, 0, 0, 2, 0, 1, 0,
6, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 6, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
253, 1, 0, 0, 122, 0, 0, 0,
249, 1, 0, 0, 146, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
252, 1, 0, 0, 3, 0, 1, 0,
8, 2, 0, 0, 2, 0, 1, 0,
7, 0, 0, 0, 14, 0, 0, 0,
0, 0, 1, 0, 7, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
5, 2, 0, 0, 42, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 2, 0, 0, 3, 0, 1, 0,
12, 2, 0, 0, 2, 0, 1, 0,
8, 0, 0, 0, 1, 0, 0, 0,
0, 0, 1, 0, 8, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
9, 2, 0, 0, 82, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
8, 2, 0, 0, 3, 0, 1, 0,
20, 2, 0, 0, 2, 0, 1, 0,
9, 0, 0, 0, 4, 0, 0, 0,
0, 0, 1, 0, 9, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
17, 2, 0, 0, 90, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
16, 2, 0, 0, 3, 0, 1, 0,
28, 2, 0, 0, 2, 0, 1, 0,
10, 0, 0, 0, 5, 0, 0, 0,
0, 0, 1, 0, 10, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
25, 2, 0, 0, 122, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
24, 2, 0, 0, 3, 0, 1, 0,
36, 2, 0, 0, 2, 0, 1, 0,
11, 0, 0, 0, 6, 0, 0, 0,
0, 0, 1, 0, 11, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
5, 2, 0, 0, 82, 0, 0, 0,
33, 2, 0, 0, 82, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
4, 2, 0, 0, 3, 0, 1, 0,
16, 2, 0, 0, 2, 0, 1, 0,
32, 2, 0, 0, 3, 0, 1, 0,
44, 2, 0, 0, 2, 0, 1, 0,
12, 0, 0, 0, 2, 0, 0, 0,
0, 0, 1, 0, 12, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
13, 2, 0, 0, 90, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
12, 2, 0, 0, 3, 0, 1, 0,
24, 2, 0, 0, 2, 0, 1, 0,
13, 0, 0, 0, 3, 0, 0, 0,
0, 0, 1, 0, 13, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
21, 2, 0, 0, 98, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
20, 2, 0, 0, 3, 0, 1, 0,
32, 2, 0, 0, 2, 0, 1, 0,
14, 0, 0, 0, 4, 0, 0, 0,
0, 0, 1, 0, 14, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
29, 2, 0, 0, 98, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
28, 2, 0, 0, 3, 0, 1, 0,
40, 2, 0, 0, 2, 0, 1, 0,
15, 0, 0, 0, 5, 0, 0, 0,
0, 0, 1, 0, 15, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
37, 2, 0, 0, 138, 0, 0, 0,
41, 2, 0, 0, 90, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
40, 2, 0, 0, 3, 0, 1, 0,
52, 2, 0, 0, 2, 0, 1, 0,
13, 0, 0, 0, 3, 0, 0, 0,
0, 0, 1, 0, 13, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
49, 2, 0, 0, 98, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
48, 2, 0, 0, 3, 0, 1, 0,
60, 2, 0, 0, 2, 0, 1, 0,
14, 0, 0, 0, 4, 0, 0, 0,
0, 0, 1, 0, 14, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
57, 2, 0, 0, 98, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
56, 2, 0, 0, 3, 0, 1, 0,
68, 2, 0, 0, 2, 0, 1, 0,
15, 0, 0, 0, 5, 0, 0, 0,
0, 0, 1, 0, 15, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
65, 2, 0, 0, 138, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
68, 2, 0, 0, 3, 0, 1, 0,
80, 2, 0, 0, 2, 0, 1, 0,
16, 0, 0, 0, 7, 0, 0, 0,
0, 0, 1, 0, 16, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
77, 2, 0, 0, 154, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
80, 2, 0, 0, 3, 0, 1, 0,
92, 2, 0, 0, 2, 0, 1, 0,
101, 118, 101, 110, 116, 73, 100, 0,
9, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
@@ -7185,6 +7209,16 @@ static const ::capnp::_::AlignedData<295> b_cd96dafb67a082d0 = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
16, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
119, 104, 101, 101, 108, 79, 118, 101,
114, 114, 105, 100, 101, 83, 105, 110,
99, 101, 0, 0, 0, 0, 0, 0,
9, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
9, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, }
};
@@ -7198,11 +7232,11 @@ static const ::capnp::_::RawSchema* const d_cd96dafb67a082d0[] = {
&s_efc922094a634c9f,
&s_fd07e0b91e3573c2,
};
static const uint16_t m_cd96dafb67a082d0[] = {4, 11, 9, 0, 7, 5, 12, 1, 10, 2, 6, 3, 8, 15, 13, 14};
static const uint16_t i_cd96dafb67a082d0[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
static const uint16_t m_cd96dafb67a082d0[] = {4, 11, 9, 0, 7, 5, 12, 1, 10, 2, 6, 3, 8, 15, 13, 14, 16};
static const uint16_t i_cd96dafb67a082d0[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
const ::capnp::_::RawSchema s_cd96dafb67a082d0 = {
0xcd96dafb67a082d0, b_cd96dafb67a082d0.words, 295, d_cd96dafb67a082d0, m_cd96dafb67a082d0,
6, 16, i_cd96dafb67a082d0, nullptr, nullptr, { &s_cd96dafb67a082d0, nullptr, nullptr, 0, 0, nullptr }, false
0xcd96dafb67a082d0, b_cd96dafb67a082d0.words, 312, d_cd96dafb67a082d0, m_cd96dafb67a082d0,
6, 17, i_cd96dafb67a082d0, nullptr, nullptr, { &s_cd96dafb67a082d0, nullptr, nullptr, 0, 0, nullptr }, false
};
#endif // !CAPNP_LITE
static const ::capnp::_::AlignedData<99> b_cc2db5640fb6dc61 = {
+40 -2
View File
@@ -730,7 +730,7 @@ struct SlcState {
struct ConditionalModeState;
struct _capnpPrivate {
CAPNP_DECLARE_STRUCT_HEADER(a1680744031fdb2d, 18, 22)
CAPNP_DECLARE_STRUCT_HEADER(a1680744031fdb2d, 19, 22)
#if !CAPNP_LITE
static constexpr ::capnp::_::RawBrandedSchema const* brand() { return &schema->defaultBrand; }
#endif // !CAPNP_LITE
@@ -878,7 +878,7 @@ struct SlcCruiseEvent {
struct ManualModeGesture;
struct _capnpPrivate {
CAPNP_DECLARE_STRUCT_HEADER(cd96dafb67a082d0, 7, 6)
CAPNP_DECLARE_STRUCT_HEADER(cd96dafb67a082d0, 8, 6)
#if !CAPNP_LITE
static constexpr ::capnp::_::RawBrandedSchema const* brand() { return &schema->defaultBrand; }
#endif // !CAPNP_LITE
@@ -4368,6 +4368,8 @@ public:
inline bool getAcceptedIsPreview() const;
inline ::uint64_t getWheelOverrideSince() const;
private:
::capnp::_::StructReader _reader;
template <typename, ::capnp::Kind>
@@ -4664,6 +4666,9 @@ public:
inline bool getAcceptedIsPreview();
inline void setAcceptedIsPreview(bool value);
inline ::uint64_t getWheelOverrideSince();
inline void setWheelOverrideSince( ::uint64_t value);
private:
::capnp::_::StructBuilder _builder;
template <typename, ::capnp::Kind>
@@ -5975,6 +5980,8 @@ public:
inline bool hasTeslaStockCruise() const;
inline ::cereal::SlcCruiseEvent::TeslaStockCruise::Reader getTeslaStockCruise() const;
inline ::uint64_t getWheelOverrideSince() const;
private:
::capnp::_::StructReader _reader;
template <typename, ::capnp::Kind>
@@ -6075,6 +6082,9 @@ public:
inline void adoptTeslaStockCruise(::capnp::Orphan< ::cereal::SlcCruiseEvent::TeslaStockCruise>&& value);
inline ::capnp::Orphan< ::cereal::SlcCruiseEvent::TeslaStockCruise> disownTeslaStockCruise();
inline ::uint64_t getWheelOverrideSince();
inline void setWheelOverrideSince( ::uint64_t value);
private:
::capnp::_::StructBuilder _builder;
template <typename, ::capnp::Kind>
@@ -19732,6 +19742,20 @@ inline void SlcState::Builder::setAcceptedIsPreview(bool value) {
::capnp::bounded<141>() * ::capnp::ELEMENTS, value);
}
inline ::uint64_t SlcState::Reader::getWheelOverrideSince() const {
return _reader.getDataField< ::uint64_t>(
::capnp::bounded<18>() * ::capnp::ELEMENTS);
}
inline ::uint64_t SlcState::Builder::getWheelOverrideSince() {
return _builder.getDataField< ::uint64_t>(
::capnp::bounded<18>() * ::capnp::ELEMENTS);
}
inline void SlcState::Builder::setWheelOverrideSince( ::uint64_t value) {
_builder.setDataField< ::uint64_t>(
::capnp::bounded<18>() * ::capnp::ELEMENTS, value);
}
inline bool SlcState::SourceReading::Reader::hasSource() const {
return !_reader.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS).isNull();
@@ -22107,6 +22131,20 @@ inline ::capnp::Orphan< ::cereal::SlcCruiseEvent::TeslaStockCruise> SlcCruiseEve
::capnp::bounded<5>() * ::capnp::POINTERS));
}
inline ::uint64_t SlcCruiseEvent::Reader::getWheelOverrideSince() const {
return _reader.getDataField< ::uint64_t>(
::capnp::bounded<7>() * ::capnp::ELEMENTS);
}
inline ::uint64_t SlcCruiseEvent::Builder::getWheelOverrideSince() {
return _builder.getDataField< ::uint64_t>(
::capnp::bounded<7>() * ::capnp::ELEMENTS);
}
inline void SlcCruiseEvent::Builder::setWheelOverrideSince( ::uint64_t value) {
_builder.setDataField< ::uint64_t>(
::capnp::bounded<7>() * ::capnp::ELEMENTS, value);
}
inline ::uint16_t SlcCruiseEvent::TeslaStockCruise::Reader::getVersion() const {
return _reader.getDataField< ::uint16_t>(
::capnp::bounded<0>() * ::capnp::ELEMENTS);
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1
View File
@@ -332,6 +332,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"SLCAdvanceHigherSeconds", {PERSISTENT, INT, "0"}},
{"SLCAdvanceLowerSeconds", {PERSISTENT, INT, "0"}},
{"SLCOverrideMode", {PERSISTENT, INT, "1"}},
{"SLCWheelOverride", {PERSISTENT, BOOL, "0"}},
{"SLCQualifiedHistory", {PERSISTENT, JSON}},
{"ConditionalModeConfig", {PERSISTENT | DONT_LOG, JSON}},
{"ConditionalManualState", {PERSISTENT | DONT_LOG, JSON}},
+15 -4
View File
@@ -31,7 +31,7 @@ from opendbc.car.hyundai.ioniq6_media import Ioniq6MediaButtons
from opendbc.car.hyundai.values import CAR as HYUNDAI_CAR
from opendbc.car.interfaces import CarInterfaceBase, RadarInterfaceBase
from openpilot.selfdrive.pandad import can_capnp_to_list, can_list_to_can_capnp
from openpilot.selfdrive.car.cruise import VCruiseHelper
from openpilot.selfdrive.car.cruise import SlcWheelContext, VCruiseHelper
from openpilot.starpilot.speed_limits import physical_actions as slc_physical
from openpilot.starpilot.aol.intent import AolProcessFaultContext, independent_axis_requested, read_settings
from openpilot.starpilot.aol.transport_pause import TransportPauseFeedback, cp_identity
@@ -573,7 +573,15 @@ class Car:
if self.slc_replay:
pending = slc_physical.pending_confirmation(
self.sm, now_ns, long_active=slc_long_active, after_ns=floor_ns)
self.update_cruise_speed(CS, pending)
wheel_context = None
slc_settings = getattr(self, 'slc_configuration', (False, None))[1]
cruise_helper = self.v_cruise_helper
if (self.slc_replay and getattr(slc_settings, 'wheel_override', False) and host_long_active and can_rcv_valid and
not self.CP.pcmCruise and pending is None and (cruise_helper.slc_wheel_press is not None or len(CS.buttonEvents) > 0) and
not (CS.brakePressed or CS.gasPressed or CS.standstill or CS.cruiseState.standstill)):
wheel_context = slc_physical.wheel_override_context(self.sm, now_ns, after_ns=floor_ns,
selected_kph=cruise_helper.v_cruise_kph, is_metric=self.is_metric)
self.update_cruise_speed(CS, pending, wheel_context=wheel_context)
if self.controller_cruise_sock is not None:
for _ in range(8):
request = messaging.recv_one_or_none(self.controller_cruise_sock)
@@ -788,9 +796,10 @@ class Car:
return CS, RD
def update_cruise_speed(self, CS, pending=None):
def update_cruise_speed(self, CS, pending=None, *, wheel_context: SlcWheelContext | None = None):
self.v_cruise_helper.update_v_cruise(CS, self.sm['carControl'].enabled, self.is_metric, pending,
slc_enabled=getattr(self, 'slc_control_enabled', getattr(self, 'slc_replay', False)))
slc_enabled=getattr(self, 'slc_control_enabled', getattr(self, 'slc_replay', False)),
wheel_context=wheel_context)
if self.sm['carControl'].enabled and not self.CC_prev.enabled:
target, initial = None, None
now_ns = int(self.can_log_mono_time) if REPLAY and hasattr(self, 'can_log_mono_time') else time.monotonic_ns()
@@ -1123,6 +1132,8 @@ class Car:
if kind == 'driverChange':
assert isinstance(detail, tuple) and len(detail) == 4
record.button, record.longPress = detail[2], detail[3]
if self.v_cruise_helper.slc_wheel_tap is not None:
record.sessionId, record.wheelOverrideSince = self.v_cruise_helper.slc_wheel_tap
elif kind in ('confirmationAccept', 'confirmationReject'):
record.button = detail.button
record.sessionId = detail.pending.session_id
+23 -2
View File
@@ -43,6 +43,9 @@ class SlcConsumedButton:
button: str
SlcWheelContext = tuple[str, int]
def validated_initial_cruise_speed(value: object) -> int:
if type(value) is int and V_CRUISE_INITIAL <= value <= V_CRUISE_MAX:
return value
@@ -70,18 +73,22 @@ class VCruiseHelper:
self.slc_consumed_button: SlcConsumedButton | None = None
self.slc_suppressed_buttons: set[ButtonType] = set()
self.slc_released_suppressed = False
self.slc_wheel_press: SlcWheelContext | None = None
self.slc_wheel_tap: SlcWheelContext | None = None
@property
def v_cruise_initialized(self):
return self.v_cruise_kph != V_CRUISE_UNSET
def update_v_cruise(self, CS, enabled, is_metric, slc_pending: SlcPendingConfirmation | None = None, *, slc_enabled: bool = False):
def update_v_cruise(self, CS, enabled, is_metric, slc_pending: SlcPendingConfirmation | None = None, *,
slc_enabled: bool = False, wheel_context: SlcWheelContext | None = None):
from opendbc.car.subaru.software_cruise import selected as subaru_software_selected
subaru_software = subaru_software_selected(self.CP)
software_cruise = not self.CP.pcmCruise or subaru_software
self.slc_cruise_change = None
self.slc_consumed_button = None
self.slc_released_suppressed = False
self.slc_wheel_tap = None
self.v_cruise_kph_last = self.v_cruise_kph
now_ns = monotonic_ns()
if not CS.cruiseState.available or now_ns - self.pending_resume_ns > 500_000_000:
@@ -97,6 +104,16 @@ class VCruiseHelper:
if software_cruise or slc_enabled and self.CP.openpilotLongitudinalControl or self.slc_suppressed_buttons:
self._update_slc_button_latches(CS, enabled, slc_pending)
if (not slc_enabled or not enabled or self.CP.pcmCruise or slc_pending is not None or
not CS.cruiseState.available or ButtonType.accelCruise in self.slc_suppressed_buttons):
wheel_context = None
if wheel_context != self.slc_wheel_press:
self.slc_wheel_press = None
if wheel_context is not None and self.button_timers[ButtonType.accelCruise] == 0:
pressed = {b.type.raw for b in CS.buttonEvents if b.pressed}
if ButtonType.accelCruise in pressed and ButtonType.resumeCruise not in pressed:
self.slc_wheel_press = wheel_context
if CS.cruiseState.available:
if software_cruise:
if subaru_software and (not enabled or not self.v_cruise_initialized):
@@ -122,6 +139,8 @@ class VCruiseHelper:
self.pcm_slc_button = ButtonType.unknown
if software_cruise:
self.update_button_timers(CS, enabled)
if self.button_timers[ButtonType.accelCruise] == 0:
self.slc_wheel_press = None
def _update_toyota_pcm_slc_change(self, CS, enabled: bool, now_ns: int) -> None:
"""Observe a physical PCM speed change without writing stock selected speed."""
@@ -211,7 +230,9 @@ class VCruiseHelper:
if not self.button_change_states[button_type]["enabled"]:
return
self.adjust_v_cruise(button_type, CS, is_metric, long_press=long_press)
changed = self.adjust_v_cruise(button_type, CS, is_metric, long_press=long_press)
if changed and button_type == ButtonType.accelCruise and not long_press and self.button_timers[button_type] > 0:
self.slc_wheel_tap = self.slc_wheel_press
def adjust_v_cruise(self, button_type, CS, is_metric: bool, *, long_press: bool = False) -> bool:
"""Apply the normal software cruise step without synthesizing button events."""
+12 -2
View File
@@ -124,6 +124,12 @@ def confirm_curve_frame(host: CurveHost | None, planner: LongitudinalPlanner, no
host.runtime.confirm_applied(now_ns, applied=bool(planner.last_curve_ceiling_applied))
def wheel_override_blocked(force_stop: ForceStopRuntime | None, curve_host: CurveHost | None, *, curve_backlog: bool = False) -> bool:
# Force Stop can consume RES on the tick it commits, before a forcing plan is published.
return ((force_stop is not None and (force_stop.policy.can_commit_next_frame or force_stop.plan.speed_ceiling_mps is not None)) or
(curve_host is not None and (curve_backlog or curve_host.runtime.was_controlling)))
def conditional_handoff_for_frame(host: ConditionalPlannerHost | None, sm, CP, now_ns: int,
drive_id: int) -> ConditionalHandoff | None:
if host is None or not CP.openpilotLongitudinalControl or CP.passive or CP.dashcamOnly:
@@ -444,7 +450,10 @@ def starpilot_main():
else:
wheel_coast.receive(event, params, CP, sm, now_ns=now_ns)
pending_wheel = waiting_wheel
slc_output = slc_lifecycle.step(sm, now_ns, pending_slc_actions, pending_slc_cruise, drive_changed=drive_changed)
# Curve observes the press before SLC sees the release. A queued Curve press can still own the tap.
curve_event = current_cruise_event(pending_curve_events, now_ns)
slc_output = slc_lifecycle.step(sm, now_ns, pending_slc_actions, pending_slc_cruise, drive_changed=drive_changed,
wheel_override_blocked=wheel_override_blocked(force_stop_owner, curve_host, curve_backlog=bool(pending_curve_events)))
cruise_ceiling = slc_output.result.ceiling if slc_output is not None else None
if slc_output is not None:
planner_status = slc_output.message
@@ -507,7 +516,6 @@ def starpilot_main():
lead_approach_key = lead_approach_for_frame(approach_preferences, sm, CP, now_ns)
if curve_preferences is not None and curve_host is not None:
curve_preferences.refresh(curve_host, now_ns)
curve_event = current_cruise_event(pending_curve_events, now_ns)
conditional_handoff = (conditional_handoff_for_frame(conditional_host, sm, CP, clock_pair[0], current_start_ns)
if clock_pair is not None else None)
wheel_coast.conditional_stop = bool(conditional_handoff is not None and conditional_host is not None and
@@ -586,6 +594,8 @@ def starpilot_main():
mode_owner.sample('switchback', sm, CP, now_ns=now_ns), session=mode_session,
sequence=mode_sequence, now_ns=now_ns, source_car_control_ns=int(sm.logMonoTime['carControl']))
if planner_status is not None:
if planner_status.slcState.wheelOverrideSince and wheel_override_blocked(force_stop_owner, curve_host):
planner_status.slcState.wheelOverrideSince = 0
pm.send('slcState', planner_status)
if current_start_ns > 0:
drive_start_ns = current_start_ns
@@ -63,7 +63,9 @@ DESCRIPTIONS = {
'curve_reset': 'Deletes saved cornering learning so Curve Speed Controller can learn your preferences again.',
'SpeedLimitController': 'Follows accepted limits plus offsets up to SET speed. Missing data uses the previous accepted limit.',
'SLCOnEngage': 'Starts software cruise SET at the SLC limit plus offset, including a saved limit when live data is missing. RES restores the previous SET.',
'SLCOverrideMode': 'Chooses whether pressing the gas past the adjusted speed limit holds the speed you reach, uses SET, or leaves the limit in control.',
'SLCOverrideMode': ('Cruise uses this target when you lift off the accelerator. ' +
'Holding your speed uses the highest speed you reached, up to SET.'),
'SLCWheelOverride': 'Press + to raise SET past the adjusted limit and use your new SET speed.',
'ShowSpeedLimits': 'Shows recognized speed-limit signs on the driving screen.',
'SLCConfirmation': ('Off adopts new limits automatically. Both, Higher only, and Lower only prompt relative to the last accepted limit. ' +
'+ accepts and − declines within 30 seconds.'),
@@ -85,6 +85,7 @@
"SLCPriority1",
"SLCPriority2",
"SLCPriority3",
"SLCWheelOverride",
"ScreenBrightness",
"ScreenBrightnessOnroad",
"ScreenTimeout",
@@ -7,6 +7,8 @@ from openpilot.common.realtime import DT_MDL
STANDSTILL_HOLD_NS = 2_000_000_000
STANDSTILL_CLEAR_NS = 750_000_000
STOP_COMMIT_TIME_S = .5
SIGN_CONFIRM_STEP_S = 2 * DT_MDL
SIGN_MAX_SPEED_MPS = 17.88
SIGN_MODEL_AGREE_M = 50.0
SIGN_SEED_M = 27.0
@@ -102,6 +104,10 @@ class ForceStop:
self.standstill_clear_ns = 0
self.standstill_reason = None
@property
def can_commit_next_frame(self) -> bool:
return self.timer + SIGN_CONFIRM_STEP_S >= STOP_COMMIT_TIME_S
def step(self, frame: StopFrame, tune: StopTune, offset_ft: int = 0) -> StopPlan:
now = frame.model_ns
if (type(now) is not int or now <= 0 or now <= self.last_model_ns or
@@ -177,16 +183,16 @@ class ForceStop:
if not available and not self.forcing:
self.timer = 0
elif active and not frame.standstill:
self.timer = min(2.0, self.timer + DT_MDL * (2 if frame.sign else 1))
self.timer = min(2.0, self.timer + (SIGN_CONFIRM_STEP_S if frame.sign else DT_MDL))
elif turn and not self.forcing and not frame.standstill:
self.timer = 0
elif held:
self.timer = max(self.timer, .5)
self.timer = max(self.timer, STOP_COMMIT_TIME_S)
elif self.forcing and frame.standstill and not frame.sign and not frame.light and not frame.raw_model_stopped:
self.timer = 0
else:
self.timer = max(0, self.timer - DT_MDL * .25)
force = self.timer >= .5 or self.forcing and not frame.standstill or held
force = self.timer >= STOP_COMMIT_TIME_S or self.forcing and not frame.standstill or held
light_cleared = (not held and self.forcing and self.from_light and not frame.standstill and
available and not frame.light and not frame.sign and go_scene)
low_speed_hold = (light_cleared and tune.low_speed_hold_mps is not None and self.entry_speed is not None and
@@ -7,11 +7,12 @@ from opendbc.car.honda.interface import CarInterface
from opendbc.car.honda.values import CAR
from openpilot.common.params import Params
from openpilot.selfdrive.controls.lib.longitudinal_planner import LongitudinalPlanner
from openpilot.selfdrive.controls.plannerd import update_curve_frame
from openpilot.selfdrive.controls.plannerd import update_curve_frame, wheel_override_blocked
from openpilot.starpilot.curve_speed.host import CurveHost
from openpilot.starpilot.longitudinal.force_stop import StopPlan
from openpilot.starpilot.longitudinal.force_stop import StopPlan, vehicle_tune
from openpilot.starpilot.longitudinal.force_stop_runtime import ForceStopRuntime, dashboard_stop_sign
from openpilot.starpilot.longitudinal.tests.test_cruise_ceiling import messages, snapshot
from openpilot.starpilot.longitudinal.tests.test_force_stop import frame as stop_frame
from openpilot.starpilot.longitudinal.tests.test_stop_resume import event as button_event
BASE = 100_000_000_000
@@ -61,6 +62,31 @@ class ForceStopRuntimeTests(unittest.TestCase):
self.assertTrue(output.forcing)
return output
def test_wheel_priority_requires_a_stop_cap_or_possible_commit_next_frame(self):
tune = vehicle_tune(str(self.cp.carFingerprint))
sign = {'light': False, 'sign': True, 'horizon_m': 40.}
self.owner.plan = self.owner.policy.step(stop_frame(0, **sign), tune)
self.assertIsNone(self.owner.plan.speed_ceiling_mps)
self.assertFalse(wheel_override_blocked(self.owner, None))
self.owner.plan = self.owner.policy.step(stop_frame(1, light=False, resume_requested=True), tune)
self.assertGreater(self.owner.policy.timer, 0.)
self.assertEqual(self.owner.policy.override_until_ns, 0)
self.assertFalse(wheel_override_blocked(self.owner, None))
self.owner.reset()
for tick in range(4):
self.owner.plan = self.owner.policy.step(stop_frame(tick, **sign), tune)
self.assertIsNone(self.owner.plan.speed_ceiling_mps)
self.assertTrue(wheel_override_blocked(self.owner, None))
self.owner.policy.step(stop_frame(4, resume_requested=True, **sign), tune)
self.assertGreater(self.owner.policy.override_until_ns, 0)
self.owner.reset()
self.owner.plan = self.owner.policy.step(stop_frame(0), tune)
self.assertFalse(self.owner.policy.can_commit_next_frame)
self.assertIsNotNone(self.owner.plan.speed_ceiling_mps)
self.assertTrue(wheel_override_blocked(self.owner, None))
def test_committed_stop_survives_valid_mode_settings_and_traffic_changes(self):
import json
from openpilot.starpilot.conditional_mode.policy import ModeChoice
@@ -1,12 +1,8 @@
# Driver inputs
Ordinary cruise +/- changes the driver's SET ceiling. It does not bypass SLC, create
a persistent override, or cross out the sign. The causal ledger still distinguishes
driver actions and consumed confirmations. SLC has no separate override reducer.
Normal accelerator handling belongs to the vehicle controls. SLC never raises or
latches its target from pedal speed. Confirmation remains available while the
driver is engaged, including when the native controls temporarily yield to the pedal.
Ordinary cruise +/- changes the driver's SET ceiling. With **Override speed limit
with +** enabled, a qualifying physical tap past the adjusted limit also lets cruise
use SET. The accelerator override has its own policy. See [SET speed and accelerator](README.md#set-speed-and-accelerator).
Source loss uses the previous accepted limit plus the current offset, up to SET.
That remembered limit survives re-engagement and restarts; applying it still needs
+13 -7
View File
@@ -176,9 +176,8 @@ cap while renewing pending actions. Disabling SLC still clears that constraint.
## SET speed and accelerator
Ordinary cruise +/- changes the driver's maximum; it does not bypass SLC or cross
out the sign. Confirmation presses remain consumed through hold and release and
do not also apply a software cruise step. Native PCM button effects remain native;
Cruise + and − change SET. Confirmation presses remain consumed through hold and
release and do not also apply a software cruise step. Native PCM button effects remain native;
StarPilot does not pretend it can suppress the vehicle's own SET adjustment.
Native standstill RES takes precedence over confirmation.
@@ -188,16 +187,23 @@ Subaru software-cruise mode remains independent of SLC.
The Volt conventional-cruise controller uses the fresh SLC cap beneath SET for its
cruise-button speed boundary, with its existing physical-source and timing checks.
**Gas pedal override** defaults to **Hold Reached Speed**. Pressing the gas
above the accepted limit plus offset lets cruise hold the fastest speed you reach,
up to SET. **Use Set Speed** uses SET as the override target. **Off** returns
to the accepted limit on release. The override clears when cruise disengages,
**After an accelerator override** defaults to **Hold the speed you reached**. Pressing
the gas above the accepted limit plus offset lets cruise hold the fastest speed you
reach, up to SET. **Use your SET speed** targets SET. **Resume the adjusted limit**
returns to the accepted limit plus offset on release. The override clears when cruise disengages,
the adjusted limit catches up, or the driver explicitly accepts or adopts a limit.
After an explicit choice, the current accelerator press cannot start another
override. Releasing and pressing again can start one.
SET and the accepted limit are separate from the override. Lead, curve and
stopping constraints still apply.
**Override speed limit with +** defaults to Off and is available when StarPilot
owns SET. A tap must move the displayed SET from at or below the adjusted limit
to above it. Cruise then uses SET until it returns to the adjusted limit, a different
limit is accepted, or engagement ends. A display-unit change or a stop while
cruise stays engaged preserves an active override. Confirmation, Curve Speed and
Force Stop take priority over this gesture.
The configured wheel action can explicitly adopt the displayed current limit. It
updates SLC acceptance, not SET. The onroad screen adds no new control.
@@ -43,6 +43,9 @@ class Frame:
pedal_override: PedalOverride = PedalOverride.OFF
preview_reverted: bool = False
confirmation_grace_ns: int = 0
wheel_override_enabled: bool = False
wheel_override_blocked: bool = False
wheel_override_since_ns: int = 0
@dataclass(frozen=True)
@@ -53,6 +56,8 @@ class State:
reset_required: bool = False
pedal_target_mps: float = 0.
pedal_blocked_until_release: bool = False
wheel_override_metric: bool | None = None
wheel_override_since_ns: int = 0
@dataclass(frozen=True)
@@ -86,7 +91,9 @@ def _state_valid(state: object) -> bool:
return False
return (type(state.session_id) is str and bool(state.session_id) and state.acceptance.session_id == state.session_id and
_number(state.pedal_target_mps) and (state.last_now_ns is None or _integer(state.last_now_ns)) and
type(state.reset_required) is bool and type(state.pedal_blocked_until_release) is bool)
type(state.reset_required) is bool and type(state.pedal_blocked_until_release) is bool and
(state.wheel_override_metric is None or type(state.wheel_override_metric) is bool) and
_integer(state.wheel_override_since_ns))
def _host_valid(host: object) -> bool:
@@ -119,9 +126,11 @@ def step(state: State, frame: Frame) -> Result:
not _host_valid(frame.host) or not acc.authority_is_valid(frame.authority) or
not isinstance(frame.acceptance_policy, acc.Policy) or
not isinstance(frame.pedal_override, PedalOverride) or
not _integer(frame.wheel_override_since_ns) or
any(type(value) is not bool for value in (frame.acceptance_policy.confirm_lower,
frame.acceptance_policy.confirm_higher,
frame.acceptance_policy.display_only)) or
frame.acceptance_policy.display_only,
frame.wheel_override_enabled, frame.wheel_override_blocked)) or
not isinstance(frame.selected_kind, sd.DomainStatus) or not isinstance(frame.ego_pair, sd.SpeedPair) or
(frame.is_metric is not None and type(frame.is_metric) is not bool)):
return Result(replace(state, reset_required=True), status="invalid_frame", errors=("invalid frame or clock",))
@@ -136,7 +145,7 @@ def step(state: State, frame: Frame) -> Result:
engaged_path = (frame.authority.can_control and (host_long or frame.authority.accelerator_override) and
selected_initialized and not frame.acceptance_policy.display_only)
active_path = engaged_path and not host.force_decel
base = replace(state, last_now_ns=frame.now_ns, pedal_target_mps=0.)
base = replace(state, last_now_ns=frame.now_ns, pedal_target_mps=0., wheel_override_metric=None, wheel_override_since_ns=0)
if frame.classified_cruise is not None:
classified = frame.classified_cruise
@@ -185,6 +194,8 @@ def step(state: State, frame: Frame) -> Result:
return Result(replace(base, reset_required=True), selected, acceptance, domain,
status="invalid_domain", errors=(domain.reason,))
clear_override = frame.preview_reverted or acceptance.basis in ('driver_accept', 'driver_adopt')
clear_wheel = clear_override or state.acceptance.accepted != acceptance.state.accepted
wheel_metric = state.wheel_override_metric if frame.wheel_override_enabled and not clear_wheel else None
previous = 0. if clear_override else state.pedal_target_mps
pedal_blocked = (state.pedal_blocked_until_release or clear_override) and (
frame.pedal_pressed is not False or ego.status is not sd.DomainStatus.VALID)
@@ -193,17 +204,39 @@ def step(state: State, frame: Frame) -> Result:
# Longitudinal output can resume one frame after the driver releases the pedal.
if (system_mode and host.selfdrive_enabled and not engaged_path and selected_initialized and
not (host.force_decel or frame.acceptance_policy.display_only) and
frame.pedal_override is not PedalOverride.OFF and ego.status is sd.DomainStatus.VALID and pair.status is sd.DomainStatus.VALID):
base = replace(base, pedal_target_mps=previous)
ego.status is sd.DomainStatus.VALID and pair.status is sd.DomainStatus.VALID):
base = replace(base, pedal_target_mps=previous if frame.pedal_override is not PedalOverride.OFF else 0.,
wheel_override_metric=wheel_metric)
return Result(base, selected, acceptance, domain, status='inactive_or_unavailable')
context = domain.context
assert context is not None
selected_cluster = context.selected_raw_mps + context.selected_delta_mps
if frame.wheel_override_enabled and frame.is_metric is not None:
since = 0 if clear_wheel else state.wheel_override_since_ns
pending = acceptance.state.pending
ready = (host_long and not frame.pedal_pressed and not frame.wheel_override_blocked and
(pending is None or pending.automatic))
adjusted_limit = context.effective_cluster_mps
change = frame.classified_cruise.change if frame.classified_cruise is not None else None
# A tap belongs to the eligibility interval at button-down, even if SET changes later.
if (wheel_metric is None and ready and since > 0 and frame.wheel_override_since_ns == since and change is not None and
change.disposition is arb.Disposition.DRIVER_INTENT and change.previous_mps is not None and
not sd.set_above_limit(change.previous_mps, adjusted_limit, frame.is_metric)):
wheel_metric = frame.is_metric
# A display-unit edit can round two different speeds to the same number.
if wheel_metric is not None and not sd.set_above_limit(selected_cluster, adjusted_limit, wheel_metric):
wheel_metric = None
base = replace(base, wheel_override_metric=wheel_metric,
wheel_override_since_ns=(since or frame.now_ns) if ready else 0)
if wheel_metric is not None:
coordinate = sd.PlannerCoordinate(None, 'wheel', selected_cluster, context.ego_delta_mps)
return Result(base, selected, acceptance, domain, coordinate, status=acceptance.basis)
pedal_target = 0.
if frame.pedal_override is not PedalOverride.OFF:
ego_cluster = context.ego_raw_mps + context.ego_delta_mps
gas_above_limit = not pedal_blocked and frame.authority.accelerator_override and ego_cluster > context.effective_cluster_mps
if gas_above_limit or previous > context.effective_cluster_mps:
selected_cluster = context.selected_raw_mps + context.selected_delta_mps
pedal_target = (selected_cluster if frame.pedal_override is PedalOverride.SET_SPEED else
min(selected_cluster, max(previous, ego_cluster if gas_above_limit else 0.)))
if pedal_target <= context.effective_cluster_mps:
@@ -3,9 +3,10 @@
import math
from openpilot.cereal.services import SERVICE_LIST
from openpilot.selfdrive.car.cruise import SlcPendingConfirmation, V_CRUISE_MIN, V_CRUISE_MAX
from openpilot.selfdrive.car.cruise import SlcPendingConfirmation, SlcWheelContext, V_CRUISE_MIN, V_CRUISE_MAX
from openpilot.starpilot.speed_limits.acceptance import CONFIRMATION_NS
from openpilot.starpilot.speed_limits.selection import Source
from openpilot.starpilot.speed_limits.speed_domain import set_above_limit
STATE_MAX_AGE_NS = int(2e9 / SERVICE_LIST['slcState'].frequency)
@@ -67,6 +68,17 @@ def active_ceiling(sm, now_ns: int, *, after_ns: int = 0) -> float | None:
return cap if finite_speed(cap) and cap <= V_CRUISE_MAX / 3.6 + 1e-5 else None
def wheel_override_context(sm, now_ns: int, *, selected_kph: float, is_metric: bool,
after_ns: int = 0) -> SlcWheelContext | None:
state = _state(sm, now_ns)
if (state is None or int(state.frameMonoTime) <= after_ns or not state.hasAcceptedAdjustedLimit or
not finite_speed(state.acceptedAdjustedLimit) or not V_CRUISE_MIN <= selected_kph <= V_CRUISE_MAX or
set_above_limit(selected_kph / 3.6, float(state.acceptedAdjustedLimit), is_metric)):
return None
since = int(state.wheelOverrideSince)
return (str(state.sessionId), since) if 0 < since <= int(state.frameMonoTime) else None
def engagement_state(sm, observation, producer_session: str, now_ns: int, *, after_ns: int = 0):
state = _current_state(sm, now_ns)
if (state is None or int(state.frameMonoTime) <= after_ns or int(state.presentationId) <= 0 or
@@ -69,6 +69,8 @@ class SavedSlcLifecycle:
if (settings.advance_higher_seconds != self.runtime.settings.advance_higher_seconds or
settings.advance_lower_seconds != self.runtime.settings.advance_lower_seconds):
self.runtime.update_advance(settings.advance_higher_seconds, settings.advance_lower_seconds)
if settings.wheel_override != self.runtime.settings.wheel_override:
self.runtime.state = replace(self.runtime.state, wheel_override_metric=None, wheel_override_since_ns=0)
self.runtime.settings = settings
self.runtime.vision_enabled = vision
self.runtime.vision_control_qualified = vision and configuration[3]
@@ -90,7 +92,7 @@ class SavedSlcLifecycle:
pending_cruise.clear()
return self.runtime, True
def step(self, sm, now_ns, pending_actions, pending_cruise, *, drive_changed):
def step(self, sm, now_ns, pending_actions, pending_cruise, *, drive_changed, wheel_override_blocked=False):
runtime = self.runtime
if runtime is None:
return None
@@ -105,7 +107,7 @@ class SavedSlcLifecycle:
pending_actions[0][0] <= now_ns else None)
previous_accepted = runtime.history_limit
output = runtime.step(sm, self.cp, now_ns=now_ns, request=request, cruise_event=cruise_event,
confirmation_grace_ns=ACTION_MAX_AGE_NS)
confirmation_grace_ns=ACTION_MAX_AGE_NS, wheel_override_blocked=wheel_override_blocked)
accepted = runtime.history_limit
if accepted != previous_accepted:
self.params.put("SLCQualifiedHistory", json.loads(history.encode(accepted, time.time_ns())) if accepted is not None else {})
@@ -125,7 +127,7 @@ def cruise_event_from_message(event):
return CruiseEvent(int(cruise.eventId), int(cruise.observedMonoTime), float(cruise.previousMps),
float(cruise.selectedMps), str(cruise.button), str(cruise.producerSessionId),
int(event.logMonoTime), str(cruise.kind), str(cruise.sessionId), int(cruise.decisionId),
int(cruise.presentationId), int(cruise.commandId))
int(cruise.presentationId), int(cruise.commandId), int(cruise.wheelOverrideSince), bool(cruise.longPress))
def action_from_message(event):
+27 -8
View File
@@ -6,6 +6,7 @@ from dataclasses import dataclass, replace
import openpilot.cereal.messaging as messaging
from openpilot.cereal.services import SERVICE_LIST
from openpilot.selfdrive.car.cruise import V_CRUISE_MIN, V_CRUISE_MAX
from openpilot.starpilot.aol.runtime import current_axis
from openpilot.starpilot.speed_limits import acceptance as acc
from openpilot.starpilot.speed_limits import action_arbitration as arb
@@ -100,6 +101,8 @@ class CruiseEvent:
decision_id: int = 0
presentation_id: int = 0
command_id: int = 0
wheel_override_since_ns: int = 0
long_press: bool = False
@dataclass(frozen=True)
@@ -175,7 +178,7 @@ class Runtime:
acceptance = replace(self.state.acceptance, observation=acc.Observation(acc.ObservationKind.UNKNOWN),
pending=None, presentation=None, timer_running=False)
self.current_presentation = None
self.state = replace(self.state, acceptance=acceptance)
self.state = replace(self.state, acceptance=acceptance, wheel_override_since_ns=0)
self._ui_receipt = None
def update_advance(self, higher_seconds: int, lower_seconds: int) -> None:
@@ -327,7 +330,7 @@ class Runtime:
return values
def _frame(self, sm, CP, now_ns: int, now_boot_ns: int | None, action: acc.DriverAction | None, adopt: acc.AdoptRequest | None,
classified: arb.Result | None) -> comp.Frame:
classified: arb.Result | None, *, wheel_override_blocked: bool) -> comp.Frame:
car_fresh = _fresh(sm, "carState", now_ns) and not sm["carState"].canTimeout
controls_fresh = _fresh(sm, "controlsState", now_ns)
selfdrive_fresh = _fresh(sm, "selfdriveState", now_ns)
@@ -353,11 +356,16 @@ class Runtime:
host = comp.HostState(bool(CP.openpilotLongitudinalControl), host_long, bool(selfdrive.enabled and selfdrive_fresh),
bool(controls.forceDecel) if controls_fresh else True, selected, selected_cluster)
observations = self._observations(sm, CP, now_ns, now_boot_ns)
wheel_enabled = (self.settings.wheel_override and not CP.pcmCruise and car_fresh and car_state.canValid and
V_CRUISE_MIN <= selected <= V_CRUISE_MAX and selected_cluster == selected and
not car_state.brakePressed)
wheel_blocked = wheel_override_blocked or car_state.standstill or car_state.cruiseState.standstill
frame = comp.Frame(now_ns, observations, self.settings.selection, authority,
self.settings.acceptance, self.settings.offsets,
sd.DomainStatus.VALID if car_fresh and selected_cluster is not None else sd.DomainStatus.STALE,
ego, bool(car_state.gasPressed) if car_fresh else False, host, action=action, adopt=adopt,
classified_cruise=classified, is_metric=self.is_metric, pedal_override=self.settings.pedal_override)
classified_cruise=classified, is_metric=self.is_metric, pedal_override=self.settings.pedal_override,
wheel_override_enabled=wheel_enabled, wheel_override_blocked=wheel_blocked)
maps_selected = self._maps_selected
encounter = self.advance.encounter
if not maps_selected and encounter is None:
@@ -432,7 +440,7 @@ class Runtime:
return acc.DriverAction(self.session_id, action_id, request.decision_id, kind, request.observed_ns), None, action_id
def step(self, sm, CP, *, now_ns: int, now_boot_ns: int | None = None, request: Action | None = None,
cruise_event: CruiseEvent | None = None, confirmation_grace_ns: int = 0) -> Output:
cruise_event: CruiseEvent | None = None, confirmation_grace_ns: int = 0, wheel_override_blocked: bool = False) -> Output:
classified = self._cruise(cruise_event, sm, now_ns) if request is None else None
action, adopt, action_id = self._action(request, now_ns)
if cruise_event is not None and request is None:
@@ -444,8 +452,16 @@ class Runtime:
adopt = replace(adopt, candidate=shown.candidate)
self.state = replace(self.state, acceptance=replace(self.state.acceptance, presentation=shown, pending=None, timer_running=False,
observation=acc.Observation(acc.ObservationKind.VALID, shown.candidate)))
frame = replace(self._frame(sm, CP, now_ns, now_boot_ns, action, adopt, classified),
confirmation_grace_ns=confirmation_grace_ns)
wheel_since = 0
if (classified is not None and cruise_event is not None and cruise_event.button == 'accel' and
not cruise_event.long_press and cruise_event.selected_mps > cruise_event.previous_mps and
abs(float(sm['carState'].vCruise) / 3.6 - cruise_event.selected_mps) <= 0.0001 and
0 < cruise_event.wheel_override_since_ns <= cruise_event.observed_ns and cruise_event.session_id == self.session_id and
_fresh(sm, 'slcDashboardObservation', now_ns) and
cruise_event.producer_session == str(sm['slcDashboardObservation'].producerSessionId)):
wheel_since = cruise_event.wheel_override_since_ns
frame = self._frame(sm, CP, now_ns, now_boot_ns, action, adopt, classified, wheel_override_blocked=wheel_override_blocked)
frame = replace(frame, confirmation_grace_ns=confirmation_grace_ns, wheel_override_since_ns=wheel_since)
result = comp.step(self.state, frame)
self.state = result.state
self.advance.observe(result)
@@ -541,8 +557,11 @@ class Runtime:
state.decisionId = pending.decision_id
state.automaticAdoptionPending = pending.automatic
state.pendingSinceMonoTime = now_ns - pending.active_elapsed_ns
state.driverOverrideActive = result.ceiling is not None and result.state.pedal_target_mps > 0.
state.overrideBasis = ('pedal' if frame.pedal_pressed else 'persistent') if state.driverOverrideActive else 'none'
wheel_active = result.coordinate is not None and result.coordinate.basis == 'wheel'
state.wheelOverrideSince = result.state.wheel_override_since_ns if not result.errors else 0
state.driverOverrideActive = wheel_active or result.ceiling is not None and result.state.pedal_target_mps > 0.
state.overrideBasis = 'wheel' if wheel_active else (
('pedal' if frame.pedal_pressed else 'persistent') if state.driverOverrideActive else 'none')
shown = result.state.acceptance.presentation
ego = frame.ego_pair
ego_delta = (max(0.0, ego.cluster_mps - ego.raw_mps)
@@ -22,8 +22,9 @@ ADVANCE_SECONDS = range(11)
ADVANCE_BYTES = tuple(str(seconds).encode() for seconds in ADVANCE_SECONDS)
PEDAL_OVERRIDE_KEY = 'SLCOverrideMode'
PEDAL_OVERRIDE_CHOICES = ('Off', 'Hold Reached Speed', 'Use Set Speed')
PEDAL_OVERRIDE_CHOICES = ('Resume the adjusted limit', 'Hold the speed you reached', 'Use your SET speed')
PEDAL_OVERRIDE_BYTES = (b'0', b'1', b'2')
WHEEL_OVERRIDE_KEY = 'SLCWheelOverride'
class PedalOverride(IntEnum):
@@ -45,6 +46,7 @@ class Settings:
advance_higher_seconds: int = 0
advance_lower_seconds: int = 0
pedal_override: PedalOverride = PedalOverride.OFF
wheel_override: bool = False
def _bool(values: Mapping[str, object], key: str, default: bool = False) -> bool:
@@ -95,7 +97,7 @@ def parse(values: Mapping[str, object]) -> Settings:
pedal_override = PedalOverride.OFF.value
return Settings(control, display, sel.SelectionPolicy(mode, slots, False), policy, offsets,
requested=control, advance_higher_seconds=higher, advance_lower_seconds=lower,
pedal_override=PedalOverride(pedal_override))
pedal_override=PedalOverride(pedal_override), wheel_override=values.get(WHEEL_OVERRIDE_KEY) is True)
except (ValueError, OverflowError) as error:
return _disabled(str(error))
@@ -105,7 +107,7 @@ SAVED_KEYS = ("SpeedLimitController", "ShowSpeedLimits", "IsMetric", *SOURCE_PRI
def read_params(params) -> Settings:
values = {}
values = {WHEEL_OVERRIDE_KEY: read_saved(params, WHEEL_OVERRIDE_KEY, 1) == (b'1', True)}
raw_override, readable_override = read_saved(params, PEDAL_OVERRIDE_KEY, 1)
if raw_override is not None or not readable_override:
values[PEDAL_OVERRIDE_KEY] = int(raw_override) if readable_override and raw_override in PEDAL_OVERRIDE_BYTES else 0
@@ -5,6 +5,7 @@ import struct
from dataclasses import dataclass
from enum import Enum
from openpilot.common.constants import CV
from openpilot.selfdrive.car.cruise import V_CRUISE_MIN, V_CRUISE_MAX, slc_target_kph
from openpilot.starpilot.speed_limits import acceptance as acc
@@ -165,6 +166,15 @@ def set_speed_matches(target_mps: float, raw_kph: float, cluster_kph: float | No
math.isclose(target_kph, cluster_kph, rel_tol=0, abs_tol=0.001))
def set_above_limit(selected_mps: float, adjusted_limit_mps: float, is_metric: bool) -> bool:
# Use the sign's wire precision and restore software SET's 0.1 km/h after transport.
limit_mps = struct.unpack('f', struct.pack('f', adjusted_limit_mps))[0]
selected_kph = round(selected_mps * CV.MS_TO_KPH, 1)
if is_metric:
return round(selected_kph) > round(limit_mps * CV.MS_TO_KPH)
return round(selected_kph * CV.KPH_TO_MPH) > round(limit_mps * CV.MS_TO_MPH)
def resolve(decision: acc.Decision, schedule: OffsetSchedule, selected: SpeedPair, ego: SpeedPair) -> DomainResolution:
"""Bind an explicit offset and contemporaneous speed pairs to an acceptance output."""
if (not isinstance(decision, acc.Decision) or not isinstance(decision.state, acc.State) or decision.errors or
@@ -71,6 +71,147 @@ def test_cruise_adjustments_change_only_the_driver_ceiling(drive, selected):
assert sm['carState'].vCruise == selected
def wheel_crossing(drive, *, metric=True, enabled=True, previous_kph=90., selected_kph=91., offset=0.):
sm, cp = drive
cp.pcmCruise = False
car_state = sm['carState']
car_state.canValid = True
car_state.vCruise = car_state.vCruiseCluster = previous_kph
settings = replace(controller(offset).settings, wheel_override=enabled)
runtime = Runtime(settings, is_metric=metric)
first = tick(runtime, sm, cp, 0).message.slcState
car_state.vCruise = car_state.vCruiseCluster = selected_kph
event = CruiseEvent(1, START + 50_000_000, previous_kph / 3.6, selected_kph / 3.6, 'accel',
'card-producer', START + 50_000_000, session_id=runtime.session_id,
wheel_override_since_ns=first.wheelOverrideSince)
return runtime, event
@pytest.mark.parametrize('metric,previous_kph,selected_kph,limit,offset', [
(True, 90., 91., 25., 0.),
(True, 54.5, 55.5, 54. / 3.6, 0.),
(False, 89., 90.6, 55. * CV.MPH_TO_MS, 0.),
(False, 86.9, 88.5, 55. * CV.MPH_TO_MS, -.5 * CV.MPH_TO_MS),
])
def test_wheel_tap_crosses_displayed_limit_and_uses_native_set(drive, metric, previous_kph, selected_kph, limit, offset):
from struct import pack, unpack
sm, cp = drive
sm['slcDashboardObservation'].speedMps = limit
sm['carState'].vEgoCluster += 1.0
runtime, event = wheel_crossing(drive, metric=metric, previous_kph=previous_kph, selected_kph=selected_kph, offset=offset)
event = replace(event, previous_mps=unpack('f', pack('f', event.previous_mps))[0])
assert event.wheel_override_since_ns == START
# Card publishes carState before the event. The new SET can be sampled first.
assert tick(runtime, sm, cp, 1).result.ceiling is not None
crossed = tick(runtime, sm, cp, 2, cruise_event=event)
status = crossed.message.slcState
assert crossed.result.ceiling is None and status.driverOverrideActive and status.overrideBasis == 'wheel'
assert status.effectiveClusterTarget == pytest.approx(selected_kph / 3.6)
assert not status.isLimitingMaxSet
planner = LongitudinalPlanner(cp)
planner.update(sm, cruise_ceiling=crossed.result.ceiling)
assert planner.last_cruise_ceiling_status == 'absent'
sm['carState'].vCruise = sm['carState'].vCruiseCluster = previous_kph
lowered = tick(runtime, sm, cp, 3)
assert lowered.result.ceiling is not None and not lowered.message.slcState.driverOverrideActive
sm['carState'].vCruise = sm['carState'].vCruiseCluster = selected_kph
assert not tick(runtime, sm, cp, 4, cruise_event=event).message.slcState.driverOverrideActive
@pytest.mark.parametrize('scenario', ['off', 'already above', 'controller', 'hold', 'foreign producer', 'foreign session',
'different SET', 'stop or curve', 'confirmation', 'new limit', 'disengaged', 'pcm',
'standstill', 'units changed'])
def test_wheel_override_requires_current_unconsumed_physical_crossing(drive, scenario):
sm, cp = drive
prior, selected = (100., 101.) if scenario == 'already above' else (90., 91.)
runtime, event = wheel_crossing(drive, enabled=scenario != 'off', previous_kph=prior, selected_kph=selected)
changes = {
'controller': {'wheel_override_since_ns': 0},
'hold': {'long_press': True},
'foreign producer': {'producer_session': 'old-card'},
'foreign session': {'session_id': 'old-slc'},
'different SET': {'selected_mps': event.selected_mps - .05},
}
event = replace(event, **changes.get(scenario, {}))
if scenario in ('confirmation', 'new limit'):
sm['slcDashboardObservation'].speedMps = 23.
if scenario == 'confirmation':
runtime.settings = replace(runtime.settings, acceptance=acc.Policy(confirm_lower=True))
if scenario == 'disengaged':
sm['carControl'].enabled = sm['carControl'].longActive = sm['selfdriveState'].enabled = False
if scenario == 'pcm':
cp.pcmCruise = True
if scenario == 'standstill':
sm['carState'].standstill = True
if scenario == 'units changed':
runtime.update_offsets(runtime.settings.offsets, is_metric=False)
output = tick(runtime, sm, cp, 1, cruise_event=event, wheel_override_blocked=scenario == 'stop or curve')
assert not output.message.slcState.driverOverrideActive
if scenario in ('stop or curve', 'standstill'):
assert output.message.slcState.wheelOverrideSince == 0
sm['carState'].standstill = False
assert not tick(runtime, sm, cp, 2, cruise_event=replace(event, event_id=2)).message.slcState.driverOverrideActive
@pytest.mark.parametrize('limit_kph', [51., 90.])
def test_wheel_override_survives_units_change_until_set_returns_to_limit(drive, limit_kph):
sm, cp = drive
sm['slcDashboardObservation'].speedMps = limit_kph / 3.6
runtime, event = wheel_crossing(drive, previous_kph=limit_kph, selected_kph=limit_kph + 1.)
assert tick(runtime, sm, cp, 1, cruise_event=event).message.slcState.overrideBasis == 'wheel'
# At 51 km/h both speeds round to 32 mph, even though SET remains higher.
runtime.update_offsets(runtime.settings.offsets, is_metric=False)
changed = tick(runtime, sm, cp, 2)
assert changed.result.ceiling is None
assert changed.message.slcState.overrideBasis == 'wheel'
assert changed.message.slcState.wheelOverrideSince != event.wheel_override_since_ns
sm['carState'].vCruise = sm['carState'].vCruiseCluster = limit_kph
returned = tick(runtime, sm, cp, 3)
assert returned.result.ceiling.speed_mps == pytest.approx(limit_kph / 3.6)
assert not returned.message.slcState.driverOverrideActive
@pytest.mark.parametrize('cruise_standstill', [False, True])
def test_wheel_override_survives_engaged_stop(drive, cruise_standstill):
sm, cp = drive
runtime, event = wheel_crossing(drive)
assert tick(runtime, sm, cp, 1, cruise_event=event).message.slcState.overrideBasis == 'wheel'
stopped = sm['carState'].cruiseState if cruise_standstill else sm['carState']
stopped.standstill = True
sm['carState'].vEgo = sm['carState'].vEgoCluster = 0.
sm['controlsState'].longControlState = 'stopping'
held = tick(runtime, sm, cp, 2)
assert held.message.slcState.overrideBasis == 'wheel'
assert held.message.slcState.wheelOverrideSince == 0
stopped.standstill = False
sm['controlsState'].longControlState = 'pid'
assert tick(runtime, sm, cp, 3).message.slcState.overrideBasis == 'wheel'
def test_wheel_override_survives_gas_handoff_without_latching_pedal_speed(drive):
sm, cp = drive
runtime, event = wheel_crossing(drive)
assert tick(runtime, sm, cp, 1, cruise_event=event).message.slcState.overrideBasis == 'wheel'
sm['carState'].vEgo = sm['carState'].vEgoCluster = 91. / 3.6
sm['carState'].gasPressed = True
sm['carControl'].longActive = False
sm['controlsState'].longControlState = 'off'
assert tick(runtime, sm, cp, 2).result.state.pedal_target_mps == 0.
sm['carState'].gasPressed = False
assert tick(runtime, sm, cp, 3).result.state.wheel_override_metric is True
sm['carControl'].longActive = True
sm['controlsState'].longControlState = 'pid'
assert tick(runtime, sm, cp, 4).message.slcState.overrideBasis == 'wheel'
sm['slcDashboardObservation'].speedMps = 23.
accepted = tick(runtime, sm, cp, 5)
assert accepted.result.ceiling.speed_mps == 23. and not accepted.message.slcState.driverOverrideActive
@pytest.mark.parametrize('missing', ['absent', 'unknown', 'stale'])
def test_source_gap_uses_the_previous_limit_and_current_offset(drive, missing):
sm, cp = drive
@@ -524,6 +524,68 @@ class RuntimeReplayTests(unittest.TestCase):
self.assertEqual(result.message.slcState.actionStatus, outcome)
def test_physical_wheel_override_receipt_through_card_and_planner_queue(self):
from openpilot.cereal import messaging
from openpilot.selfdrive.car import card
from openpilot.selfdrive.controls.plannerd import current_cruise_event, queue_cruise_event, wheel_override_blocked
from openpilot.starpilot.curve_speed.host import CurveHost
self.cp.pcmCruise = False
self.sm['carState'].canValid = True
self.sm['carState'].vCruise = self.sm['carState'].vCruiseCluster = 90.
settings = parse({'SpeedLimitController': True, 'SLCPriority1': 'Dashboard', 'SLCWheelOverride': True})
for curve_claims, curve_backlog in ((False, False), (True, False), (False, True)):
with self.subTest(curve_claims=curve_claims, curve_backlog=curve_backlog):
start = 2_000_000_000
self.sm.advance(start)
self.sm['carState'].vCruise = self.sm['carState'].vCruiseCluster = 90.
runtime = Runtime(settings, is_metric=True)
state = runtime.step(self.sm, self.cp, now_ns=start).message.slcState
fake, current_cs = self._card_with_slc_state(state, None, start)
fake.slc_configuration = fake.slc_requested_configuration = (True, settings)
fake.v_cruise_helper.v_cruise_kph = fake.v_cruise_helper.v_cruise_cluster_kph = 90.
fake.is_metric = True
fake.curve_replay = True
fake.car_params_published = True
fake.sm.frame = 1
fake.sm.all_checks = lambda services: True
fake.last_actuators_output = car.CarControl.Actuators()
fake.slc_cruise_event_id = 0
fake.radar_publish = mock.Mock()
fake.rk = SimpleNamespace(remaining=0.0)
fake.aol_replay = False
clock = [start + 10_000_000]
def can_batch(*args, clock=clock, **kwargs):
return [messaging.new_message('can', size=0, logMonoTime=clock[0]).to_bytes()]
with (mock.patch.object(card, 'REPLAY', True),
mock.patch.object(card.messaging, 'drain_sock_raw', side_effect=can_batch)):
for pressed in (True, False):
current_cs[0] = car.CarState(canValid=True, cruiseState={'available': True},
buttonEvents=[car.CarState.ButtonEvent(type='accelCruise', pressed=pressed)])
cs, radar = fake.state_update()
fake.state_publish(cs, radar)
clock[0] += 10_000_000
fake.sm.logMonoTime['carControl'] = clock[0]
slc_events, curve_events = deque(), deque()
for call in fake.pm.send.call_args_list:
name, message = call.args
if name == 'slcCruiseEvent':
queue_cruise_event(messaging.log_from_bytes(message.to_bytes()), slc_events, curve_events)
self.assertEqual(len(curve_events), 1)
self.assertEqual(len(slc_events), 1)
self.assertEqual(cs.vCruise, 91.)
self.sm['carState'].vCruise = self.sm['carState'].vCruiseCluster = cs.vCruise
self.sm.advance(clock[0])
event = current_cruise_event(slc_events, clock[0])
self.assertEqual(event.wheel_override_since_ns, start)
curve = CurveHost()
curve.runtime.was_controlling = curve_claims
output = runtime.step(self.sm, self.cp, now_ns=clock[0], cruise_event=event,
wheel_override_blocked=wheel_override_blocked(None, curve, curve_backlog=curve_backlog))
self.assertEqual(output.message.slcState.driverOverrideActive, not (curve_claims or curve_backlog))
def test_malformed_saved_priorities_disable_runtime_without_crashing(self):
for key in ('SLCPriority1', 'SLCPriority2'):
for malformed in ([], {}, 1, None):
@@ -151,6 +151,19 @@ class RuntimeSettingsTests(unittest.TestCase):
self.assertFalse(settings.enabled)
self.assertTrue(settings.errors)
def test_wheel_override_is_opt_in_and_bad_bytes_keep_slc_enabled(self):
with tempfile.TemporaryDirectory() as directory:
params = Params(directory)
params.put_bool('SpeedLimitController', True, block=True)
self.assertFalse(params.get_default_value('SLCWheelOverride'))
self.assertFalse(read_params(params).wheel_override)
params.put_bool('SLCWheelOverride', True, block=True)
self.assertTrue(read_params(params).wheel_override)
Path(params.get_param_path('SLCWheelOverride')).write_bytes(b'bad')
settings = read_params(params)
self.assertTrue(settings.enabled)
self.assertFalse(settings.wheel_override)
def test_retired_offsets_are_unregistered_and_ignored_with_zero_defaults(self):
for metric in (False, True):
with self.subTest(metric=metric), tempfile.TemporaryDirectory() as directory:
@@ -39,7 +39,7 @@ from openpilot.starpilot.ui.feature_settings_state import (
from openpilot.starpilot.speed_limits.offset_document import MPH_TO_MPS
from openpilot.starpilot.speed_limits.runtime_settings import (
ADVANCE_BYTES, ADVANCE_KEYS, CONFIRMATION_CHOICES,
PEDAL_OVERRIDE_BYTES, PEDAL_OVERRIDE_CHOICES, PEDAL_OVERRIDE_KEY, SOURCE_PRIORITY_KEYS,
PEDAL_OVERRIDE_BYTES, PEDAL_OVERRIDE_CHOICES, PEDAL_OVERRIDE_KEY, SOURCE_PRIORITY_KEYS, WHEEL_OVERRIDE_KEY, PedalOverride,
)
from openpilot.starpilot.aol.vehicle import policy_for as aol_policy_for
from openpilot.starpilot.feature_capabilities import longitudinal_supported as feature_longitudinal_supported
@@ -92,6 +92,7 @@ BOOL_DEFAULTS = {
"RemapCancelToDistance": False, "LongPitch": True, "DisableOpenpilotLongitudinal": False,
"SpeedLimitController": False, "ShowSpeedLimits": True,
"SLCOnEngage": False,
WHEEL_OVERRIDE_KEY: False,
"PauseLateralOnSignal": False,
"LaneCentering": True, "LaneCenteringPauseOnSignal": True,
"CustomPersonalities": False, PLANNER_SELECTION_KEY: True,
@@ -1129,6 +1130,9 @@ class FeatureSettingsOwner:
rows.append(self._bool_row("ShowSpeedLimits", "Show speed limit signs", self.authority("preferences"),
reason="Display signs independently of Speed Limit Controller; does not change cruise speed"))
rows.extend(policy_rows(self._value, self._readable, allowed=allowed))
if cp is not None and not cp.pcmCruise:
rows.append(self._bool_row(WHEEL_OVERRIDE_KEY, "Override speed limit with +", allowed and control == "1",
reason="Press + to raise SET past the adjusted limit and use your new SET speed."))
source_capability = self._source_capability()
vision_dev, maps_available = source_capability
source_allowed = configurable and self.authority("preferences")
@@ -1633,7 +1637,7 @@ class FeatureSettingsOwner:
encoded = str(SLC_ADVANCE_CHOICES.index(request.value))
elif key == PEDAL_OVERRIDE_KEY:
if request.value not in PEDAL_OVERRIDE_CHOICES or (
request.value != 'Off' and request.expected not in (None, *PEDAL_OVERRIDE_BYTES)):
request.value != PEDAL_OVERRIDE_CHOICES[PedalOverride.OFF] and request.expected not in (None, *PEDAL_OVERRIDE_BYTES)):
return False
encoded = PEDAL_OVERRIDE_CHOICES.index(request.value)
elif key in BOOL_DEFAULTS:
@@ -1642,7 +1646,7 @@ class FeatureSettingsOwner:
return False
if key == "SpeedLimitController" and request.value == "On" and not self.slc_offsets.ready_to_enable():
return False
if key == "SLCOnEngage":
if key in ("SLCOnEngage", WHEEL_OVERRIDE_KEY):
cp = self.vehicle_params()
if cp is None or cp.pcmCruise or not self.longitudinal_available() or (
request.value == "On" and self._raw("SpeedLimitController") != b"1"):
+5 -4
View File
@@ -26,12 +26,13 @@ def policy_rows(read_value, readable, *, allowed):
reason=f'Offer or adopt {direction} mapped limits this many seconds early at the current speed'
if valid_advance else 'Invalid saved advance time; restore Off'))
_, raw_override, readable_override = read_value(PEDAL_OVERRIDE_KEY)
resume_limit = PEDAL_OVERRIDE_CHOICES[PedalOverride.OFF]
default_override = PEDAL_OVERRIDE_CHOICES[PedalOverride.SPEED_REACHED]
valid_override = readable_override and (raw_override is None or raw_override in PEDAL_OVERRIDE_BYTES)
rows.append(FeatureRow(PEDAL_OVERRIDE_KEY, 'Gas pedal override',
rows.append(FeatureRow(PEDAL_OVERRIDE_KEY, 'After an accelerator override',
default_override if raw_override is None else PEDAL_OVERRIDE_CHOICES[int(raw_override)] if valid_override else 'Invalid',
raw_override, PEDAL_OVERRIDE_CHOICES if valid_override else ('Off',),
raw_override, PEDAL_OVERRIDE_CHOICES if valid_override else (resume_limit,),
available=allowed and readable(PEDAL_OVERRIDE_KEY), default_value=default_override,
repair_value='' if valid_override else 'Off',
reason='Choose what cruise holds after you press the gas past the adjusted speed limit'))
repair_value='' if valid_override else resume_limit,
reason='Cruise uses this target when you lift off the accelerator. Held speed stays at or below SET.'))
return rows
@@ -586,15 +586,41 @@ class FeatureSettingsOwnerTests(unittest.TestCase):
def test_gas_pedal_override_uses_the_shared_slc_settings_owner(self):
row = self._row('slc', 'SLCOverrideMode')
self.assertEqual(row.value, 'Hold Reached Speed')
self.assertEqual(row.choices, ('Off', 'Hold Reached Speed', 'Use Set Speed'))
request = required_change(row)
choices = ('Resume the adjusted limit', 'Hold the speed you reached', 'Use your SET speed')
self.assertEqual((row.label, row.value), ('After an accelerator override', choices[1]))
self.assertEqual(row.choices, choices)
self.allowed = False
self.assertFalse(self.owner.apply(request))
self.assertFalse(self.owner.apply(required_change(row)))
self.allowed = True
for mode in (2, 0, 1):
self.assertTrue(self.owner.apply(required_change(row)))
self.assertEqual(self.params.get('SLCOverrideMode'), mode)
row = self._row('slc', 'SLCOverrideMode')
self.assertEqual(row.value, choices[mode])
Path(self.params.get_param_path('SLCOverrideMode')).write_bytes(b'9')
repair = required_change(self._row('slc', 'SLCOverrideMode'))
self.assertEqual(repair.value, choices[0])
self.assertFalse(self.owner.apply(replace(repair, value=choices[2])))
self.assertTrue(self.owner.apply(repair))
self.assertEqual(self.params.get('SLCOverrideMode'), 0)
def test_wheel_override_is_off_and_only_configurable_for_software_set(self):
from opendbc.car.structs import car
cp = car.CarParams(carFingerprint=self.fingerprint, openpilotLongitudinalControl=True, pcmCruise=False)
self.owner.vehicle_params = lambda: cp
self.params.put_bool('SpeedLimitController', True, block=True)
row = self._row('slc', 'SLCWheelOverride')
self.assertEqual((row.label, row.value, row.default_value), ('Override speed limit with +', 'Off', 'Off'))
request = required_change(row)
cp.pcmCruise = True
self.assertFalse(self.owner.apply(request))
cp.pcmCruise = False
self.params.put_bool('SpeedLimitController', False, block=True)
self.assertFalse(self.owner.apply(request))
self.params.put_bool('SpeedLimitController', True, block=True)
self.assertTrue(self.owner.apply(request))
self.assertEqual(self.params.get('SLCOverrideMode'), 2)
self.assertEqual(self._row('slc', 'SLCOverrideMode').value, 'Use Set Speed')
self.assertTrue(self.params.get_bool('SLCWheelOverride'))
def test_limit_sign_preference_needs_parked_authority_not_long_control_or_vehicle(self):
self.fingerprint = None