예제 #1
0
  def test_honda_checksum(self):
    """Test checksums for Honda standard and extended CAN ids"""
    dbc_file = "honda_accord_2018_can_generated"

    signals = [
      ("CHECKSUM", "LKAS_HUD"),
      ("CHECKSUM", "LKAS_HUD_A"),
    ]
    checks = [("LKAS_HUD", 0), ("LKAS_HUD_A", 0)]

    parser = CANParser(dbc_file, signals, checks, 0)
    packer = CANPacker(dbc_file)

    values = {
      'SET_ME_X41': 0x41,
      'STEERING_REQUIRED': 1,
      'SOLID_LANES': 1,
      'BEEP': 0,
    }

    # known correct checksums according to the above values
    checksum_std = [11, 10, 9, 8]
    checksum_ext = [4, 3, 2, 1]

    for std, ext in zip(checksum_std, checksum_ext):
      msgs = [
        packer.make_can_msg("LKAS_HUD", 0, values),
        packer.make_can_msg("LKAS_HUD_A", 0, values),
      ]
      can_strings = [can_list_to_can_capnp(msgs), ]
      parser.update_strings(can_strings)

      self.assertEqual(parser.vl['LKAS_HUD']['CHECKSUM'], std)
      self.assertEqual(parser.vl['LKAS_HUD_A']['CHECKSUM'], ext)
예제 #2
0
    def test_packer_parser(self):

        signals = [
            ("COUNTER", "STEERING_CONTROL"),
            ("CHECKSUM", "STEERING_CONTROL"),
            ("STEER_TORQUE", "STEERING_CONTROL"),
            ("STEER_TORQUE_REQUEST", "STEERING_CONTROL"),
            ("COUNTER", "CAN_FD_MESSAGE"),
            ("64_BIT_LE", "CAN_FD_MESSAGE"),
            ("64_BIT_BE", "CAN_FD_MESSAGE"),
            ("SIGNED", "CAN_FD_MESSAGE"),
        ]
        checks = [("STEERING_CONTROL", 0), ("CAN_FD_MESSAGE", 0)]

        packer = CANPacker(TEST_DBC)
        parser = CANParser(TEST_DBC, signals, checks, 0)

        for steer in range(-256, 255):
            for active in (1, 0):
                v1 = {
                    "STEER_TORQUE": steer,
                    "STEER_TORQUE_REQUEST": active,
                }
                m1 = packer.make_can_msg("STEERING_CONTROL", 0, v1)

                v2 = {
                    "SIGNED": steer,
                    "64_BIT_LE": random.randint(0, 100),
                    "64_BIT_BE": random.randint(0, 100),
                }
                m2 = packer.make_can_msg("CAN_FD_MESSAGE", 0, v2)

                bts = can_list_to_can_capnp([m1, m2])
                parser.update_string(bts)

                for key, val in v1.items():
                    self.assertAlmostEqual(parser.vl["STEERING_CONTROL"][key],
                                           val)

                for key, val in v2.items():
                    self.assertAlmostEqual(parser.vl["CAN_FD_MESSAGE"][key],
                                           val)

                # also check address
                for sig in ("STEER_TORQUE", "STEER_TORQUE_REQUEST", "COUNTER",
                            "CHECKSUM"):
                    self.assertEqual(parser.vl["STEERING_CONTROL"][sig],
                                     parser.vl[228][sig])
예제 #3
0
  def test_civic(self):

    dbc_file = "honda_civic_touring_2016_can_generated"

    signals = [
      ("STEER_TORQUE", "STEERING_CONTROL", 0),
      ("STEER_TORQUE_REQUEST", "STEERING_CONTROL", 0),
    ]
    checks = [("STEERING_CONTROL", 50)]

    parser = CANParser(dbc_file, signals, checks, 0)
    packer = CANPacker(dbc_file)

    idx = 0

    for steer in range(-256, 255):
      for active in [1, 0]:
        values = {
          "STEER_TORQUE": steer,
          "STEER_TORQUE_REQUEST": active,
        }

        msgs = packer.make_can_msg("STEERING_CONTROL", 0, values, idx)
        bts = can_list_to_can_capnp([msgs])

        parser.update_string(bts)

        self.assertAlmostEqual(parser.vl["STEERING_CONTROL"]["STEER_TORQUE"], steer)
        self.assertAlmostEqual(parser.vl["STEERING_CONTROL"]["STEER_TORQUE_REQUEST"], active)
        self.assertAlmostEqual(parser.vl["STEERING_CONTROL"]["COUNTER"], idx % 4)

        idx += 1
예제 #4
0
    def test_parser_can_valid(self):
        signals = [
            ("COUNTER", "CAN_FD_MESSAGE"),
        ]
        checks = [
            ("CAN_FD_MESSAGE", 10),
        ]
        packer = CANPacker(TEST_DBC)
        parser = CANParser(TEST_DBC, signals, checks, 0)

        # shouldn't be valid initially
        self.assertFalse(parser.can_valid)

        # not valid until the message is seen
        for _ in range(100):
            dat = can_list_to_can_capnp([])
            parser.update_string(dat)
            self.assertFalse(parser.can_valid)

        # valid once seen
        for i in range(1, 100):
            t = int(0.01 * i * 1e9)
            msg = packer.make_can_msg("CAN_FD_MESSAGE", 0, {})
            dat = can_list_to_can_capnp([
                msg,
            ], logMonoTime=t)
            parser.update_string(dat)
            self.assertTrue(parser.can_valid)
예제 #5
0
    def test_packer(self):
        packer = CANPacker(TEST_DBC)

        for b in range(6):
            for i in range(256):
                values = {"COUNTER": i}
                addr, _, dat, bus = packer.make_can_msg(
                    "CAN_FD_MESSAGE", b, values)
                self.assertEqual(addr, 245)
                self.assertEqual(bus, b)
                self.assertEqual(dat[0], i)
예제 #6
0
    def test_packer_counter(self):
        signals = [
            ("COUNTER", "CAN_FD_MESSAGE"),
        ]
        checks = [
            ("CAN_FD_MESSAGE", 0),
        ]
        packer = CANPacker(TEST_DBC)
        parser = CANParser(TEST_DBC, signals, checks, 0)

        # packer should increment the counter
        for i in range(1000):
            msg = packer.make_can_msg("CAN_FD_MESSAGE", 0, {})
            dat = can_list_to_can_capnp([
                msg,
            ])
            parser.update_string(dat)
            self.assertEqual(parser.vl["CAN_FD_MESSAGE"]["COUNTER"], i % 256)

        # setting COUNTER should override
        for _ in range(100):
            cnt = random.randint(0, 255)
            msg = packer.make_can_msg("CAN_FD_MESSAGE", 0, {
                "COUNTER": cnt,
            })
            dat = can_list_to_can_capnp([
                msg,
            ])
            parser.update_string(dat)
            self.assertEqual(parser.vl["CAN_FD_MESSAGE"]["COUNTER"], cnt)

        # then, should resume counting from the override value
        cnt = parser.vl["CAN_FD_MESSAGE"]["COUNTER"]
        for i in range(100):
            msg = packer.make_can_msg("CAN_FD_MESSAGE", 0, {})
            dat = can_list_to_can_capnp([
                msg,
            ])
            parser.update_string(dat)
            self.assertEqual(parser.vl["CAN_FD_MESSAGE"]["COUNTER"],
                             (cnt + i) % 256)
예제 #7
0
    def test_subaru(self):
        # Subuaru is little endian

        dbc_file = "subaru_global_2017_generated"

        signals = [
            ("Counter", "ES_LKAS", 0),
            ("LKAS_Output", "ES_LKAS", 0),
            ("LKAS_Request", "ES_LKAS", 0),
            ("SET_1", "ES_LKAS", 0),
        ]

        checks = []

        parser = CANParser(dbc_file, signals, checks, 0)
        packer = CANPacker(dbc_file)

        idx = 0

        for steer in range(-256, 255):
            for active in [1, 0]:
                values = {
                    "Counter": idx,
                    "LKAS_Output": steer,
                    "LKAS_Request": active,
                    "SET_1": 1
                }

                msgs = packer.make_can_msg("ES_LKAS", 0, values)
                bts = can_list_to_can_capnp([msgs])
                parser.update_string(bts)

                self.assertAlmostEqual(parser.vl["ES_LKAS"]["LKAS_Output"],
                                       steer)
                self.assertAlmostEqual(parser.vl["ES_LKAS"]["LKAS_Request"],
                                       active)
                self.assertAlmostEqual(parser.vl["ES_LKAS"]["SET_1"], 1)
                self.assertAlmostEqual(parser.vl["ES_LKAS"]["Counter"],
                                       idx % 16)

                idx += 1
예제 #8
0
    def test_scale_offset(self):
        """Test that both scale and offset are correctly preserved"""
        dbc_file = "honda_civic_touring_2016_can_generated"

        signals = [
            ("USER_BRAKE", "VSA_STATUS"),
        ]
        checks = [("VSA_STATUS", 50)]

        parser = CANParser(dbc_file, signals, checks, 0)
        packer = CANPacker(dbc_file)

        for brake in range(0, 100):
            values = {"USER_BRAKE": brake}
            msgs = packer.make_can_msg("VSA_STATUS", 0, values)
            bts = can_list_to_can_capnp([msgs])

            parser.update_string(bts)

            self.assertAlmostEqual(parser.vl["VSA_STATUS"]["USER_BRAKE"],
                                   brake)
예제 #9
0
    def test_updated(self):
        """Test updated value dict"""
        dbc_file = "honda_civic_touring_2016_can_generated"

        signals = [("USER_BRAKE", "VSA_STATUS")]
        checks = [("VSA_STATUS", 50)]

        parser = CANParser(dbc_file, signals, checks, 0)
        packer = CANPacker(dbc_file)

        # Make sure nothing is updated
        self.assertEqual(len(parser.vl_all["VSA_STATUS"]["USER_BRAKE"]), 0)

        idx = 0
        for _ in range(10):
            # Ensure CANParser holds the values of any duplicate messages over multiple frames
            user_brake_vals = [
                random.randrange(100) for _ in range(random.randrange(5, 10))
            ]
            half_idx = len(user_brake_vals) // 2
            can_msgs = [[], []]
            for frame, brake_vals in enumerate(
                (user_brake_vals[:half_idx], user_brake_vals[half_idx:])):
                for user_brake in brake_vals:
                    values = {"USER_BRAKE": user_brake}
                    can_msgs[frame].append(
                        packer.make_can_msg("VSA_STATUS", 0, values))
                    idx += 1

            can_strings = [can_list_to_can_capnp(msgs) for msgs in can_msgs]
            parser.update_strings(can_strings)
            vl_all = parser.vl_all["VSA_STATUS"]["USER_BRAKE"]

            self.assertEqual(vl_all, user_brake_vals)
            if len(user_brake_vals):
                self.assertEqual(vl_all[-1],
                                 parser.vl["VSA_STATUS"]["USER_BRAKE"])
예제 #10
0
hyundai_checksum = crcmod.mkCrcFun(0x11D, initCrc=0xFD, rev=False, xorOut=0xdf)
dbc_name = 'kia_forte_koup_2013'

packer = CANPacker(dbc_name)
values = {
    "CF_Spas_Stat": 5,
    "CF_Spas_TestMode": 0,
    "CR_Spas_StrAngCmd": 18.5,
    "CF_Spas_BeepAlarm": 0,
    "CF_Spas_Mode_Seq": 2,
    "CF_Spas_AliveCnt": 205,
    "CF_Spas_Chksum": 0,
    "CF_Spas_PasVol": 0
}
dat = packer.make_can_msg("SPAS11", 0, values)[2]
print(dat)
crc = hyundai_checksum(dat)

values["CF_Spas_Chksum"] = sum(dat[:6]) % 256

#dat = dat[:6]
cs6b = (sum(dat[:6]) % 256)
#cs7b = ((sum(dat[:6]) + dat[7]) % 256)

print(dat)
print(crc)
print(cs6b)
print(values["CF_Spas_Chksum"])
"""
예제 #11
0
class CarController():
  def __init__(self, dbc_name, CP, VW):
    
    # state
    self.acc_enabled_prev = 0

    # steering related
    self.angle_request_prev = 0
    
    # Direction change statemachine
    self.UNBLOCKED = 0
    self.BLOCKED = 1
    self.BLOCK_LEN = CCP.BLOCK_LEN  # Block steer direction change for x samples

    self.dir_state = 0
    self.block_steering = 0
    self.steer_direction_bf_block = 0
    self.des_steer_direction_prev = 0

    # SteerCommand
    self.SteerCommand = SteerCommand
    self.trq_fifo = deque([])  
    self.fault_frame = -200

    # Diag
    self.doDTCRequests = True  # Turn on and off DTC requests
    self.checkPN = False       # Check partnumbers
    self.clearDtcs = False     # Set false to stop sending diagnostic requests 
    self.timeout = 0           # Set to 0 as init
    self.diagRequest = { 
      "byte0": 0x03,
      "byte1": 0x19,
      "byte2": 0x02,
      "byte3": 0x02,
      }
    self.flowControl = { 
      "byte0": 0x30,
      "byte1": 0x00,
      "byte2": 0x00,
      "byte3": 0x00,
      }
    self.clearDTC = {
      "byte0": 0x04,
      "byte1": 0x14,
      "byte2": 0xFF,
      "byte3": 0xFF,
      "byte4": 0xFF,
      }

    # Part number
    self.cnt = 0          # Init at 0 always
    self.sndNxtFrame = 0  # Init at low value 
    self.dictKeys = ["byte"+str(x) for x in range(8)]
    startdid = 0xf1a1     # Start with this DID (Data IDentifier, read UDS Spec for more info)
    self.dids = [x for x in range(startdid, startdid+9)]

    # Setup detection helper. Routes commands to
    # an appropriate CAN bus number.
    self.CP = CP
    self.packer = CANPacker(DBC[CP.carFingerprint]['pt'])


  def max_angle_req(self, current_steer_angle, angle_request_prev, CCP):
    """ 
    Calculate maximum angle request delta/offset from current steering angle. 
    
    This is just a helper function that calculates the boundary for min and max
    steering angle request. It uses the parameters CCP.MAX_ACT_ANGLE_REQUEST_DIFF
    and CCP.STEER_ANGLE_DELTA_REQ_DIFF. To calculate the max and min allowed delta/offset request.

    The delta request is just a rate limiter. The request angle cant change more 
    than CCP.STEER_ANGLE_DELTA_REQ_DIFF per loop. 
    
    """

    # determine max and min allowed lka angle request
    # based on delta per sample
    max_delta_right = angle_request_prev-CCP.STEER_ANGLE_DELTA_REQ_DIFF 
    max_delta_left = angle_request_prev+CCP.STEER_ANGLE_DELTA_REQ_DIFF

    # based on distance from actual steering angle
    max_right = current_steer_angle-CCP.MAX_ACT_ANGLE_REQUEST_DIFF 
    max_left = current_steer_angle+CCP.MAX_ACT_ANGLE_REQUEST_DIFF

    return max_right, max_left, max_delta_right, max_delta_left

  def dir_change(self, steer_direction, error):
    """ Filters out direction changes
    
    Uses a simple state machine to determine if we should 
    block or allow the steer_direction bits to pass thru.

    """
    
    dessd = steer_direction
    dzError = 0 if abs(error) < CCP.DEADZONE else error 
    tState = -1 

    # Update prev with desired if just enabled.
    self.des_steer_direction_prev = steer_direction if not self.acc_enabled_prev else self.des_steer_direction_prev
    
    # Check conditions for state change
    if self.dir_state == self.UNBLOCKED:
      tState = self.BLOCKED if (steer_direction != self.des_steer_direction_prev and dzError != 0) else tState
    elif self.dir_state == self.BLOCKED:
      if (steer_direction == self.steer_direction_bf_block) or (self.block_steering <= 0) or (dzError == 0):
        tState = self.UNBLOCKED

    # State transition
    if tState == self.UNBLOCKED:
      self.dir_state = self.UNBLOCKED
    elif tState == self.BLOCKED:
      self.steer_direction_bf_block = self.des_steer_direction_prev  
      self.block_steering = self.BLOCK_LEN
      self.dir_state = self.BLOCKED

    #  Run actions in state
    if self.dir_state == self.UNBLOCKED:
      if dzError == 0:
        steer_direction = self.des_steer_direction_prev # Set old request when inside deadzone
    if self.dir_state == self.BLOCKED:
      self.block_steering -= 1
      steer_direction = CCP.STEER_NO

    #print("State:{} Sd:{} Sdp:{} Bs:{} Dz:{:.2f} Err:{:.2f}".format(self.dir_state, steer_direction, self.des_steer_direction_prev, self.block_steering, dzError, error))
    return steer_direction

  def update(self, enabled, CS, frame,
             actuators, 
             visualAlert, leftLaneVisible,
             rightLaneVisible, leadVisible,
             leftLaneDepart, rightLaneDepart):
    """ Controls thread """
    
    # Send CAN commands.
    can_sends = []

    # run at 50hz
    if (frame % 2 == 0):
      fingerprint = self.CP.carFingerprint
      
      if enabled and CS.out.vEgo > self.CP.minSteerSpeed:
        current_steer_angle = CS.out.steeringAngleDeg
        self.SteerCommand.angle_request = actuators.steeringAngleDeg # Desired value from pathplanner
        
        # # windup slower
        if self.angle_request_prev * self.SteerCommand.angle_request > 0. and abs(self.SteerCommand.angle_request) > abs(self.angle_request_prev):
          angle_rate_lim = interp(CS.out.vEgo, CCP.ANGLE_DELTA_BP, CCP.ANGLE_DELTA_V)
        else:
          angle_rate_lim = interp(CS.out.vEgo, CCP.ANGLE_DELTA_BP, CCP.ANGLE_DELTA_VU)

        self.SteerCommand.angle_request = clip(self.SteerCommand.angle_request, self.angle_request_prev - angle_rate_lim, self.angle_request_prev + angle_rate_lim)

        # Create trqlim from angle request (before constraints)
        self.SteerCommand.trqlim = 0
        if fingerprint in PLATFORM.C1:
          #self.SteerCommand.trqlim = -127 if current_steer_angle > self.SteerCommand.angle_request else 127
          self.SteerCommand.steer_direction = CCP.STEER
        elif fingerprint in PLATFORM.EUCD:
          # MIGHT be needed for EUCD
          self.SteerCommand.steer_direction = CCP.STEER_RIGHT if current_steer_angle > self.SteerCommand.angle_request else CCP.STEER_LEFT
          self.SteerCommand.steer_direction = self.dir_change(self.SteerCommand.steer_direction, current_steer_angle-self.SteerCommand.angle_request) # Filter the direction change 
          
      else:
        self.SteerCommand.steer_direction = CCP.STEER_NO
        self.SteerCommand.trqlim = 0
        if fingerprint in PLATFORM.C1:
          self.SteerCommand.angle_request = clip(CS.out.steeringAngleDeg, -359.95, 359.90)  # Cap values at max min values (Cap 2 steps from max min). Max=359.99445, Min=-360.0384 
        else:
          self.SteerCommand.angle_request = 0

      
      # Count no of consequtive samples of zero torque by lka.
      # Try to recover, blocking steering request for 2 seconds.
      if fingerprint in PLATFORM.C1:
        if enabled and CS.out.vEgo > self.CP.minSteerSpeed:
          self.trq_fifo.append(CS.PSCMInfo.LKATorque)
          if len(self.trq_fifo) > CCP.N_ZERO_TRQ:
            self.trq_fifo.popleft()
        else:
          self.trq_fifo.clear()
          self.fault_frame = -200

        if (self.trq_fifo.count(0) >= CCP.N_ZERO_TRQ) and (self.fault_frame == -200):
          self.fault_frame = frame+100

        if enabled and (frame < self.fault_frame):
          self.SteerCommand.steer_direction = CCP.STEER_NO

        if frame > self.fault_frame+8:  # Ignore steerWarning for another 8 samples.
          self.fault_frame = -200     


      # update stored values
      self.acc_enabled_prev = enabled
      self.angle_request_prev = self.SteerCommand.angle_request
      if self.SteerCommand.steer_direction == CCP.STEER_RIGHT or self.SteerCommand.steer_direction == CCP.STEER_LEFT: # TODO: Move this inside dir_change, think it should work?
        self.des_steer_direction_prev = self.SteerCommand.steer_direction  # Used for dir_change function
      
      # Manipulate data from servo to FSM
      # Avoid fault codes, that will stop LKA
      can_sends.append(volvocan.manipulateServo(self.packer, self.CP.carFingerprint, CS))
    
      # send can, add to list.
      can_sends.append(volvocan.create_steering_control(self.packer, frame, self.CP.carFingerprint, self.SteerCommand, CS.FSMInfo))

    
    # Cancel ACC if engaged when OP is not.
    if not enabled and CS.out.cruiseState.enabled:
      can_sends.append(volvocan.cancelACC(self.packer, self.CP.carFingerprint, CS))


    # Send diagnostic requests
    if(self.doDTCRequests):
      if(frame % 100 == 0) and (not self.clearDtcs):
        # Request diagnostic codes, 2 Hz
        can_sends.append(self.packer.make_can_msg("diagFSMReq", 2, self.diagRequest))
        #can_sends.append(self.packer.make_can_msg("diagGlobalReq", 2, self.diagRequest))
        can_sends.append(self.packer.make_can_msg("diagGlobalReq", 0, self.diagRequest))
        #can_sends.append(self.packer.make_can_msg("diagPSCMReq", 0, self.diagRequest))
        #can_sends.append(self.packer.make_can_msg("diagCEMReq", 0, self.diagRequest))
        #can_sends.append(self.packer.make_can_msg("diagCVMReq", 0, self.diagRequest))
        self.timeout = frame + 5 # Set wait time 
      
      # Handle flow control in case of many DTC
      if frame > self.timeout and self.timeout > 0: # Wait fix time before sending flow control, otherwise just spamming...
        self.timeout = 0 
        if (CS.diag.diagFSMResp & 0x10000000):
          can_sends.append(self.packer.make_can_msg("diagFSMReq", 2, self.flowControl))
        if (CS.diag.diagCEMResp & 0x10000000):
          can_sends.append(self.packer.make_can_msg("diagCEMReq", 0, self.flowControl))
        if (CS.diag.diagPSCMResp & 0x10000000):
          can_sends.append(self.packer.make_can_msg("diagPSCMReq", 0, self.flowControl))
        if (CS.diag.diagCVMResp & 0x10000000):
          can_sends.append(self.packer.make_can_msg("diagCVMReq", 0, self.flowControl))

      # Check part numbers
      if self.checkPN and frame > 100 and frame > self.sndNxtFrame:
        if self.cnt < len(self.dids):
          did = [0x03, 0x22, (self.dids[self.cnt] & 0xff00)>>8, self.dids[self.cnt] & 0x00ff] # Create diagnostic command
          did.extend([0]*(8-len(did))) 
          diagReq = dict(zip(self.dictKeys,did))
          #can_sends.append(self.packer.make_can_msg("diagGlobalReq", 2, diagReq))
          #can_sends.append(self.packer.make_can_msg("diagGlobalReq", 0, diagReq))
          can_sends.append(self.packer.make_can_msg("diagFSMReq", 2, diagReq))
          can_sends.append(self.packer.make_can_msg("diagCEMReq", 0, diagReq))
          can_sends.append(self.packer.make_can_msg("diagPSCMReq", 0, diagReq))
          can_sends.append(self.packer.make_can_msg("diagCVMReq", 0, diagReq))
          self.cnt += 1
          self.timeout = frame+5             # When to send flowControl
          self.sndNxtFrame = self.timeout+5  # When to send next part number request

        elif True:                           # Stop when list has been looped thru.
          self.checkPN = False

      # Clear DTCs in FSM on start
      # TODO check for engine running before clearing dtc.
      if(self.clearDtcs and (frame > 0) and (frame % 500 == 0)):
        can_sends.append(self.packer.make_can_msg("diagGlobalReq", 0, self.clearDTC))
        can_sends.append(self.packer.make_can_msg("diagFSMReq", 2, self.clearDTC))
        #can_sends.append(self.packer.make_can_msg("diagPSCMReq", 0, self.clearDTC))
        #can_sends.append(self.packer.make_can_msg("diagCEMReq", 0, self.clearDTC))
        self.clearDtcs = False
      
    return can_sends
예제 #12
0
p = Panda()
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT)

dumpsafety = p.health()
if dumpsafety['safety_mode'] == 0:
    print(
        'Sorry, u need change for some dev branch, if u deserv this copy and past this command:\n\n\necho -en "1" > /data/params/d/DisableUpdates; cd ..; rm -rf openpilot; git clone -b master --recurse-submodules https://github.com/commaai/openpilot; reboot'
    )
    exit(1)

packer = CANPacker(dbc_name)
values = {
    "ACC_TYPE": 1,
}
data = packer.make_can_msg(
    "ACC_CONTROL", 0,
    values)  # maybe u need change this ACC_CONTROL confirm in your odbc


def main():

    for i in range(0, max):
        print(f"PCM exploit :{data}")
        print(
            ' __      __                                  .___\n/  \    /  \ ____     ____   ____   ____   __| _/\n\   \/\/   // __ \   /    \_/ __ \_/ __ \ / __ | \n \        /\  ___/  |   |  \  ___/\  ___// /_/ | \n  \__/\  /  \___  > |___|  /\___  >\___  >____ | \n       \/       \/       \/     \/     \/     \/ \n  _________        ________ \n /   _____/ ____  /  _____/ \n \_____  \ /    \/   \  ___ \n /        \   |  \    \_\  \\\n/_______  /___|  /\______  /\n        \/     \/        \/ \n'
        )
        p.can_send(data[0], data[2], data[3])
        progress(i, max, 'start your engine before reach the end')
        if i < (max - 1):
            os.system('clear')