diff --git a/docs/CARS.md b/docs/CARS.md index c3278d7066..96d966ba98 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -231,7 +231,7 @@ A supported vehicle is one that just works when you install a comma device. Ever 3When the Driver Support Unit (DSU) is disconnected, openpilot Adaptive Cruise Control (ACC) will replace stock Adaptive Cruise Control (ACC). NOTE: disconnecting the DSU disables Automatic Emergency Braking (AEB).
4openpilot operates above 28mph for Camry 4CYL L, 4CYL LE and 4CYL SE which don't have Full-Speed Range Dynamic Radar Cruise Control.
5Not including the China market Kamiq, which is based on the (currently) unsupported PQ34 platform.
-6Not including the USA/China market Passat, which is based on the (currently) unsupported PQ35/NMS platform.
+6Refers only to the MQB-based European B8 Passat, not the NMS Passat in the USA/China/Mideast markets.
7Model-years 2021 and beyond may have a new camera harness design, which isn't yet available from the comma store. Before ordering, remove the Lane Assist camera cover and check to see if the connector is black (older design) or light brown (newer design). In the interim, if your car has a J533 connector CAN gateway inside the dashboard, choose "VW J533 Development" from the vehicle drop-down for a suitable harness. (Some newer models are also observed to not have a J533 connector.)
8Includes versions with extra rear cargo space (may be called Variant, Estate, SportWagen, Shooting Brake, etc.)
diff --git a/selfdrive/car/volkswagen/carcontroller.py b/selfdrive/car/volkswagen/carcontroller.py index 99a3ce55f4..0923ac0313 100644 --- a/selfdrive/car/volkswagen/carcontroller.py +++ b/selfdrive/car/volkswagen/carcontroller.py @@ -1,8 +1,8 @@ from cereal import car from opendbc.can.packer import CANPacker from selfdrive.car import apply_std_steer_torque_limits -from selfdrive.car.volkswagen import volkswagencan -from selfdrive.car.volkswagen.values import DBC_FILES, CANBUS, MQB_LDW_MESSAGES, CarControllerParams as P +from selfdrive.car.volkswagen import mqbcan +from selfdrive.car.volkswagen.values import CANBUS, CarControllerParams VisualAlert = car.CarControl.HUDControl.VisualAlert @@ -10,11 +10,12 @@ VisualAlert = car.CarControl.HUDControl.VisualAlert class CarController: def __init__(self, dbc_name, CP, VM): self.CP = CP + self.CCP = CarControllerParams(CP) + self.CCS = mqbcan + self.packer_pt = CANPacker(dbc_name) + self.apply_steer_last = 0 self.frame = 0 - - self.packer_pt = CANPacker(DBC_FILES.mqb) - self.hcaSameTorqueCount = 0 self.hcaEnabledFrameCount = 0 @@ -26,7 +27,7 @@ class CarController: # **** Steering Controls ************************************************ # - if self.frame % P.HCA_STEP == 0: + if self.frame % self.CCP.HCA_STEP == 0: # Logic to avoid HCA state 4 "refused": # * Don't steer unless HCA is in state 3 "ready" or 5 "active" # * Don't steer at standstill @@ -38,21 +39,21 @@ class CarController: # when exceeding ~1/3 the 360 second timer. if CC.latActive: - new_steer = int(round(actuators.steer * P.STEER_MAX)) - apply_steer = apply_std_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorque, P) + new_steer = int(round(actuators.steer * self.CCP.STEER_MAX)) + apply_steer = apply_std_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorque, self.CCP) if apply_steer == 0: hcaEnabled = False self.hcaEnabledFrameCount = 0 else: self.hcaEnabledFrameCount += 1 - if self.hcaEnabledFrameCount >= 118 * (100 / P.HCA_STEP): # 118s + if self.hcaEnabledFrameCount >= 118 * (100 / self.CCP.HCA_STEP): # 118s hcaEnabled = False self.hcaEnabledFrameCount = 0 else: hcaEnabled = True if self.apply_steer_last == apply_steer: self.hcaSameTorqueCount += 1 - if self.hcaSameTorqueCount > 1.9 * (100 / P.HCA_STEP): # 1.9s + if self.hcaSameTorqueCount > 1.9 * (100 / self.CCP.HCA_STEP): # 1.9s apply_steer -= (1, -1)[apply_steer < 0] self.hcaSameTorqueCount = 0 else: @@ -62,32 +63,29 @@ class CarController: apply_steer = 0 self.apply_steer_last = apply_steer - can_sends.append(volkswagencan.create_mqb_steering_control(self.packer_pt, CANBUS.pt, apply_steer, hcaEnabled)) + can_sends.append(self.CCS.create_steering_control(self.packer_pt, CANBUS.pt, apply_steer, hcaEnabled)) # **** HUD Controls ***************************************************** # - if self.frame % P.LDW_STEP == 0: + if self.frame % self.CCP.LDW_STEP == 0: + hud_alert = 0 if hud_control.visualAlert in (VisualAlert.steerRequired, VisualAlert.ldw): - hud_alert = MQB_LDW_MESSAGES["laneAssistTakeOverSilent"] - else: - hud_alert = MQB_LDW_MESSAGES["none"] + hud_alert = self.CCP.LDW_MESSAGES["laneAssistTakeOver"] - can_sends.append(volkswagencan.create_mqb_hud_control(self.packer_pt, CANBUS.pt, CC.enabled, - CS.out.steeringPressed, hud_alert, hud_control.leftLaneVisible, - hud_control.rightLaneVisible, CS.ldw_stock_values, - hud_control.leftLaneDepart, hud_control.rightLaneDepart)) + can_sends.append(self.CCS.create_lka_hud_control(self.packer_pt, CANBUS.pt, CS.ldw_stock_values, CC.enabled, + CS.out.steeringPressed, hud_alert, hud_control)) - # **** ACC Button Controls ********************************************** # + # **** Stock ACC Button Controls **************************************** # - if self.CP.pcmCruise and self.frame % P.GRA_ACC_STEP == 0: + if self.CP.pcmCruise and self.frame % self.CCP.GRA_ACC_STEP == 0: idx = (CS.gra_stock_values["COUNTER"] + 1) % 16 if CC.cruiseControl.cancel: - can_sends.append(volkswagencan.create_mqb_acc_buttons_control(self.packer_pt, ext_bus, CS.gra_stock_values, idx, cancel=True)) + can_sends.append(self.CCS.create_acc_buttons_control(self.packer_pt, ext_bus, CS.gra_stock_values, idx, cancel=True)) elif CC.cruiseControl.resume: - can_sends.append(volkswagencan.create_mqb_acc_buttons_control(self.packer_pt, ext_bus, CS.gra_stock_values, idx, resume=True)) + can_sends.append(self.CCS.create_acc_buttons_control(self.packer_pt, ext_bus, CS.gra_stock_values, idx, resume=True)) new_actuators = actuators.copy() - new_actuators.steer = self.apply_steer_last / P.STEER_MAX + new_actuators.steer = self.apply_steer_last / self.CCP.STEER_MAX self.frame += 1 return new_actuators, can_sends diff --git a/selfdrive/car/volkswagen/carstate.py b/selfdrive/car/volkswagen/carstate.py index 052a1262e4..65e3de169c 100644 --- a/selfdrive/car/volkswagen/carstate.py +++ b/selfdrive/car/volkswagen/carstate.py @@ -3,20 +3,14 @@ from cereal import car from common.conversions import Conversions as CV from selfdrive.car.interfaces import CarStateBase from opendbc.can.parser import CANParser -from opendbc.can.can_define import CANDefine -from selfdrive.car.volkswagen.values import DBC_FILES, CANBUS, NetworkLocation, TransmissionType, GearShifter, \ - CarControllerParams, MQB_BUTTONS +from selfdrive.car.volkswagen.values import DBC, CANBUS, NetworkLocation, TransmissionType, GearShifter, CarControllerParams + class CarState(CarStateBase): def __init__(self, CP): super().__init__(CP) - self.button_states = {button.event_type: False for button in MQB_BUTTONS} - can_define = CANDefine(DBC_FILES.mqb) - if CP.transmissionType == TransmissionType.automatic: - self.shifter_values = can_define.dv["Getriebe_11"]["GE_Fahrstufe"] - elif CP.transmissionType == TransmissionType.direct: - self.shifter_values = can_define.dv["EV_Gearshift"]["GearPosition"] - self.hca_status_values = can_define.dv["LH_EPS_03"]["EPS_HCA_Status"] + self.CCP = CarControllerParams(CP) + self.button_states = {button.event_type: False for button in self.CCP.BUTTONS} def update(self, pt_cp, cam_cp, ext_cp, trans_type): ret = car.CarState.new_message() @@ -37,11 +31,11 @@ class CarState(CarStateBase): ret.steeringAngleDeg = pt_cp.vl["LWI_01"]["LWI_Lenkradwinkel"] * (1, -1)[int(pt_cp.vl["LWI_01"]["LWI_VZ_Lenkradwinkel"])] ret.steeringRateDeg = pt_cp.vl["LWI_01"]["LWI_Lenkradw_Geschw"] * (1, -1)[int(pt_cp.vl["LWI_01"]["LWI_VZ_Lenkradw_Geschw"])] ret.steeringTorque = pt_cp.vl["LH_EPS_03"]["EPS_Lenkmoment"] * (1, -1)[int(pt_cp.vl["LH_EPS_03"]["EPS_VZ_Lenkmoment"])] - ret.steeringPressed = abs(ret.steeringTorque) > CarControllerParams.STEER_DRIVER_ALLOWANCE + ret.steeringPressed = abs(ret.steeringTorque) > self.CCP.STEER_DRIVER_ALLOWANCE ret.yawRate = pt_cp.vl["ESP_02"]["ESP_Gierrate"] * (1, -1)[int(pt_cp.vl["ESP_02"]["ESP_VZ_Gierrate"])] * CV.DEG_TO_RAD # Verify EPS readiness to accept steering commands - hca_status = self.hca_status_values.get(pt_cp.vl["LH_EPS_03"]["EPS_HCA_Status"]) + hca_status = self.CCP.hca_status_values.get(pt_cp.vl["LH_EPS_03"]["EPS_HCA_Status"]) ret.steerFaultPermanent = hca_status in ("DISABLED", "FAULT") ret.steerFaultTemporary = hca_status in ("INITIALIZING", "REJECTED") @@ -54,9 +48,9 @@ class CarState(CarStateBase): # Update gear and/or clutch position data. if trans_type == TransmissionType.automatic: - ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(pt_cp.vl["Getriebe_11"]["GE_Fahrstufe"], None)) + ret.gearShifter = self.parse_gear_shifter(self.CCP.shifter_values.get(pt_cp.vl["Getriebe_11"]["GE_Fahrstufe"], None)) elif trans_type == TransmissionType.direct: - ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(pt_cp.vl["EV_Gearshift"]["GearPosition"], None)) + ret.gearShifter = self.parse_gear_shifter(self.CCP.shifter_values.get(pt_cp.vl["EV_Gearshift"]["GearPosition"], None)) elif trans_type == TransmissionType.manual: ret.clutchPressed = not pt_cp.vl["Motor_14"]["MO_Kuppl_schalter"] if bool(pt_cp.vl["Gateway_72"]["BCM1_Rueckfahrlicht_Schalter"]): @@ -120,7 +114,7 @@ class CarState(CarStateBase): ret.rightBlinker = bool(pt_cp.vl["Blinkmodi_02"]["Comfort_Signal_Right"]) self.gra_stock_values = pt_cp.vl["GRA_ACC_01"] buttonEvents = [] - for button in MQB_BUTTONS: + for button in self.CCP.BUTTONS: state = pt_cp.vl[button.can_addr][button.can_msg] in button.values if self.button_states[button.event_type] != state: event = car.CarState.ButtonEvent.new_message() @@ -218,7 +212,7 @@ class CarState(CarStateBase): signals += MqbExtraSignals.bsm_radar_signals checks += MqbExtraSignals.bsm_radar_checks - return CANParser(DBC_FILES.mqb, signals, checks, CANBUS.pt) + return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, CANBUS.pt) @staticmethod def get_cam_can_parser(CP): @@ -246,7 +240,7 @@ class CarState(CarStateBase): signals += MqbExtraSignals.bsm_radar_signals checks += MqbExtraSignals.bsm_radar_checks - return CANParser(DBC_FILES.mqb, signals, checks, CANBUS.cam) + return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, CANBUS.cam) class MqbExtraSignals: # Additional signal and message lists for optional or bus-portable controllers diff --git a/selfdrive/car/volkswagen/mqbcan.py b/selfdrive/car/volkswagen/mqbcan.py new file mode 100644 index 0000000000..1a4ca339ab --- /dev/null +++ b/selfdrive/car/volkswagen/mqbcan.py @@ -0,0 +1,38 @@ +def create_steering_control(packer, bus, apply_steer, lkas_enabled): + values = { + "SET_ME_0X3": 0x3, + "Assist_Torque": abs(apply_steer), + "Assist_Requested": lkas_enabled, + "Assist_VZ": 1 if apply_steer < 0 else 0, + "HCA_Available": 1, + "HCA_Standby": not lkas_enabled, + "HCA_Active": lkas_enabled, + "SET_ME_0XFE": 0xFE, + "SET_ME_0X07": 0x07, + } + return packer.make_can_msg("HCA_01", bus, values) + + +def create_lka_hud_control(packer, bus, ldw_stock_values, enabled, steering_pressed, hud_alert, hud_control): + values = ldw_stock_values.copy() + + values.update({ + "LDW_Status_LED_gelb": 1 if enabled and steering_pressed else 0, + "LDW_Status_LED_gruen": 1 if enabled and not steering_pressed else 0, + "LDW_Lernmodus_links": 3 if hud_control.leftLaneDepart else 1 + hud_control.leftLaneVisible, + "LDW_Lernmodus_rechts": 3 if hud_control.rightLaneDepart else 1 + hud_control.rightLaneVisible, + "LDW_Texte": hud_alert, + }) + return packer.make_can_msg("LDW_02", bus, values) + + +def create_acc_buttons_control(packer, bus, gra_stock_values, idx, cancel=False, resume=False): + values = gra_stock_values.copy() + + values.update({ + "COUNTER": idx, + "GRA_Abbrechen": cancel, + "GRA_Tip_Wiederaufnahme": resume, + }) + + return packer.make_can_msg("GRA_ACC_01", bus, values) diff --git a/selfdrive/car/volkswagen/values.py b/selfdrive/car/volkswagen/values.py index 8b2c7e93cc..af0139e7bb 100755 --- a/selfdrive/car/volkswagen/values.py +++ b/selfdrive/car/volkswagen/values.py @@ -4,6 +4,7 @@ from enum import Enum from typing import Dict, List, Union from cereal import car +from opendbc.can.can_define import CANDefine from selfdrive.car import dbc_dict from selfdrive.car.docs_definitions import CarFootnote, CarInfo, Column, Harness @@ -15,54 +16,56 @@ Button = namedtuple('Button', ['event_type', 'can_addr', 'can_msg', 'values']) class CarControllerParams: - HCA_STEP = 2 # HCA_01 message frequency 50Hz - LDW_STEP = 10 # LDW_02 message frequency 10Hz - GRA_ACC_STEP = 3 # GRA_ACC_01 message frequency 33Hz + HCA_STEP = 2 # HCA_01/HCA_1 message frequency 50Hz + GRA_ACC_STEP = 3 # GRA_ACC_01/GRA_Neu message frequency 33Hz - # Observed documented MQB limits: 3.00 Nm max, rate of change 5.00 Nm/sec. - # Limiting rate-of-change based on real-world testing and Comma's safety - # requirements for minimum time to lane departure. - STEER_MAX = 300 # Max heading control assist torque 3.00 Nm - STEER_DELTA_UP = 4 # Max HCA reached in 1.50s (STEER_MAX / (50Hz * 1.50)) - STEER_DELTA_DOWN = 10 # Min HCA reached in 0.60s (STEER_MAX / (50Hz * 0.60)) - STEER_DRIVER_ALLOWANCE = 80 - STEER_DRIVER_MULTIPLIER = 3 # weight driver torque heavily - STEER_DRIVER_FACTOR = 1 # from dbc + def __init__(self, CP): + # Documented lateral limits: 3.00 Nm max, rate of change 5.00 Nm/sec. + # MQB vs PQ maximums are shared, but rate-of-change limited differently + # based on safety requirements driven by lateral accel testing. + self.STEER_MAX = 300 # Max heading control assist torque 3.00 Nm + self.STEER_DRIVER_MULTIPLIER = 3 # weight driver torque heavily + self.STEER_DRIVER_FACTOR = 1 # from dbc + + can_define = CANDefine(DBC[CP.carFingerprint]["pt"]) + + if True: # pylint: disable=using-constant-test + self.LDW_STEP = 10 # LDW_02 message frequency 10Hz + self.STEER_DRIVER_ALLOWANCE = 80 # Driver intervention threshold 0.8 Nm + self.STEER_DELTA_UP = 4 # Max HCA reached in 1.50s (STEER_MAX / (50Hz * 1.50)) + self.STEER_DELTA_DOWN = 10 # Min HCA reached in 0.60s (STEER_MAX / (50Hz * 0.60)) + if CP.transmissionType == TransmissionType.automatic: + self.shifter_values = can_define.dv["Getriebe_11"]["GE_Fahrstufe"] + elif CP.transmissionType == TransmissionType.direct: + self.shifter_values = can_define.dv["EV_Gearshift"]["GearPosition"] + self.hca_status_values = can_define.dv["LH_EPS_03"]["EPS_HCA_Status"] -class CANBUS: - pt = 0 - cam = 2 - - -class DBC_FILES: - mqb = "vw_mqb_2010" # Used for all cars with MQB-style CAN messaging - - -DBC: Dict[str, Dict[str, str]] = defaultdict(lambda: dbc_dict(DBC_FILES.mqb, None)) + self.BUTTONS = [ + Button(car.CarState.ButtonEvent.Type.setCruise, "GRA_ACC_01", "GRA_Tip_Setzen", [1]), + Button(car.CarState.ButtonEvent.Type.resumeCruise, "GRA_ACC_01", "GRA_Tip_Wiederaufnahme", [1]), + Button(car.CarState.ButtonEvent.Type.accelCruise, "GRA_ACC_01", "GRA_Tip_Hoch", [1]), + Button(car.CarState.ButtonEvent.Type.decelCruise, "GRA_ACC_01", "GRA_Tip_Runter", [1]), + Button(car.CarState.ButtonEvent.Type.cancel, "GRA_ACC_01", "GRA_Abbrechen", [1]), + Button(car.CarState.ButtonEvent.Type.gapAdjustCruise, "GRA_ACC_01", "GRA_Verstellung_Zeitluecke", [1]), + ] + self.LDW_MESSAGES = { + "none": 0, # Nothing to display + "laneAssistUnavailChime": 1, # "Lane Assist currently not available." with chime + "laneAssistUnavailNoSensorChime": 3, # "Lane Assist not available. No sensor view." with chime + "laneAssistTakeOverUrgent": 4, # "Lane Assist: Please Take Over Steering" with urgent beep + "emergencyAssistUrgent": 6, # "Emergency Assist: Please Take Over Steering" with urgent beep + "laneAssistTakeOverChime": 7, # "Lane Assist: Please Take Over Steering" with chime + "laneAssistTakeOver": 8, # "Lane Assist: Please Take Over Steering" silent + "emergencyAssistChangingLanes": 9, # "Emergency Assist: Changing lanes..." with urgent beep + "laneAssistDeactivated": 10, # "Lane Assist deactivated." silent with persistent icon afterward + } -MQB_BUTTONS = [ - Button(car.CarState.ButtonEvent.Type.setCruise, "GRA_ACC_01", "GRA_Tip_Setzen", [1]), - Button(car.CarState.ButtonEvent.Type.resumeCruise, "GRA_ACC_01", "GRA_Tip_Wiederaufnahme", [1]), - Button(car.CarState.ButtonEvent.Type.accelCruise, "GRA_ACC_01", "GRA_Tip_Hoch", [1]), - Button(car.CarState.ButtonEvent.Type.decelCruise, "GRA_ACC_01", "GRA_Tip_Runter", [1]), - Button(car.CarState.ButtonEvent.Type.cancel, "GRA_ACC_01", "GRA_Abbrechen", [1]), - Button(car.CarState.ButtonEvent.Type.gapAdjustCruise, "GRA_ACC_01", "GRA_Verstellung_Zeitluecke", [1]), -] - -MQB_LDW_MESSAGES = { - "none": 0, # Nothing to display - "laneAssistUnavailChime": 1, # "Lane Assist currently not available." with chime - "laneAssistUnavailNoSensorChime": 3, # "Lane Assist not available. No sensor view." with chime - "laneAssistTakeOverUrgent": 4, # "Lane Assist: Please Take Over Steering" with urgent beep - "emergencyAssistUrgent": 6, # "Emergency Assist: Please Take Over Steering" with urgent beep - "laneAssistTakeOverChime": 7, # "Lane Assist: Please Take Over Steering" with chime - "laneAssistTakeOverSilent": 8, # "Lane Assist: Please Take Over Steering" silent - "emergencyAssistChangingLanes": 9, # "Emergency Assist: Changing lanes..." with urgent beep - "laneAssistDeactivated": 10, # "Lane Assist deactivated." silent with persistent icon afterward -} +class CANBUS: + pt = 0 + cam = 2 # Check the 7th and 8th characters of the VIN before adding a new CAR. If the @@ -96,12 +99,15 @@ class CAR: SKODA_OCTAVIA_MK3 = "SKODA OCTAVIA 3RD GEN" # Chassis NE, Mk3 Skoda Octavia and variants +DBC: Dict[str, Dict[str, str]] = defaultdict(lambda: dbc_dict("vw_mqb_2010", None)) + + class Footnote(Enum): KAMIQ = CarFootnote( "Not including the China market Kamiq, which is based on the (currently) unsupported PQ34 platform.", Column.MODEL) PASSAT = CarFootnote( - "Not including the USA/China market Passat, which is based on the (currently) unsupported PQ35/NMS platform.", + "Refers only to the MQB-based European B8 Passat, not the NMS Passat in the USA/China/Mideast markets.", Column.MODEL) VW_HARNESS = CarFootnote( "Model-years 2021 and beyond may have a new camera harness design, which isn't yet available from the comma " + diff --git a/selfdrive/car/volkswagen/volkswagencan.py b/selfdrive/car/volkswagen/volkswagencan.py deleted file mode 100644 index f4c21eeed4..0000000000 --- a/selfdrive/car/volkswagen/volkswagencan.py +++ /dev/null @@ -1,42 +0,0 @@ -# CAN controls for MQB platform Volkswagen, Audi, Skoda, and SEAT. -# PQ35/PQ46/NMS, and any future MLB, to come later. - -def create_mqb_steering_control(packer, bus, apply_steer, lkas_enabled): - values = { - "SET_ME_0X3": 0x3, - "Assist_Torque": abs(apply_steer), - "Assist_Requested": lkas_enabled, - "Assist_VZ": 1 if apply_steer < 0 else 0, - "HCA_Available": 1, - "HCA_Standby": not lkas_enabled, - "HCA_Active": lkas_enabled, - "SET_ME_0XFE": 0xFE, - "SET_ME_0X07": 0x07, - } - return packer.make_can_msg("HCA_01", bus, values) - -def create_mqb_hud_control(packer, bus, enabled, steering_pressed, hud_alert, left_lane_visible, right_lane_visible, - ldw_stock_values, left_lane_depart, right_lane_depart): - # Lane color reference: - # 0 (LKAS disabled) - off - # 1 (LKAS enabled, no lane detected) - dark gray - # 2 (LKAS enabled, lane detected) - light gray on VW, green or white on Audi depending on year or virtual cockpit. On a color MFD on a 2015 A3 TDI it is white, virtual cockpit on a 2018 A3 e-Tron its green. - # 3 (LKAS enabled, lane departure detected) - white on VW, red on Audi - values = ldw_stock_values.copy() - values.update({ - "LDW_Status_LED_gelb": 1 if enabled and steering_pressed else 0, - "LDW_Status_LED_gruen": 1 if enabled and not steering_pressed else 0, - "LDW_Lernmodus_links": 3 if left_lane_depart else 1 + left_lane_visible, - "LDW_Lernmodus_rechts": 3 if right_lane_depart else 1 + right_lane_visible, - "LDW_Texte": hud_alert, - }) - return packer.make_can_msg("LDW_02", bus, values) - -def create_mqb_acc_buttons_control(packer, bus, gra_stock_values, idx, cancel=False, resume=False): - values = gra_stock_values.copy() - - values["COUNTER"] = idx - values["GRA_Abbrechen"] = cancel - values["GRA_Tip_Wiederaufnahme"] = resume - - return packer.make_can_msg("GRA_ACC_01", bus, values)