commit
						70ddf6fd84
					
				
				 123 changed files with 2974 additions and 425 deletions
			
			
		| @ -1,9 +1,9 @@ | ||||
| # Updating your panda | ||||
| 
 | ||||
| Panda should update automatically via the [Chffr](http://chffr.comma.ai/) app ([apple](https://itunes.apple.com/us/app/chffr-dash-cam-that-remembers/id1146683979) and [android](https://play.google.com/store/apps/details?id=ai.comma.chffr)) | ||||
| Panda should update automatically via the [openpilot](http://openpilot.comma.ai/). | ||||
| 
 | ||||
| If it doesn't however,  you can use the following commands on linux or Mac OSX | ||||
|  `sudo pip install --upgrade pandacan` | ||||
| ` PYTHONPATH="" sudo python -c "import panda; panda.flash_release()"` | ||||
| 
 | ||||
| (You'll need to have `pip` and `sudo` installed.) | ||||
| On Linux or Mac OSX, you can manually update it using: | ||||
| ``` | ||||
| sudo pip install --upgrade pandacan` | ||||
| PYTHONPATH="" sudo python -c "import panda; panda.flash_release()"` | ||||
| ``` | ||||
|  | ||||
| @ -1 +1 @@ | ||||
| v1.5.3 | ||||
| v1.5.9 | ||||
| @ -1 +1 @@ | ||||
| from .python import Panda, PandaWifiStreaming, PandaDFU, ESPROM, CesantaFlasher, flash_release, BASEDIR, ensure_st_up_to_date, build_st, PandaSerial | ||||
| from .python import Panda, PandaWifiStreaming, PandaDFU, ESPROM, CesantaFlasher, flash_release, BASEDIR, ensure_st_up_to_date, build_st, PandaSerial  # noqa: F401 | ||||
|  | ||||
| @ -0,0 +1,247 @@ | ||||
| // ///////////// //
 | ||||
| // Uno + Harness //
 | ||||
| // ///////////// //
 | ||||
| 
 | ||||
| void uno_enable_can_transciever(uint8_t transciever, bool enabled) { | ||||
|   switch (transciever){ | ||||
|     case 1U: | ||||
|       set_gpio_output(GPIOC, 1, !enabled); | ||||
|       break; | ||||
|     case 2U: | ||||
|       set_gpio_output(GPIOC, 13, !enabled); | ||||
|       break; | ||||
|     case 3U: | ||||
|       set_gpio_output(GPIOA, 0, !enabled); | ||||
|       break; | ||||
|     case 4U: | ||||
|       set_gpio_output(GPIOB, 10, !enabled); | ||||
|       break; | ||||
|     default: | ||||
|       puts("Invalid CAN transciever ("); puth(transciever); puts("): enabling failed\n"); | ||||
|       break; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void uno_enable_can_transcievers(bool enabled) { | ||||
|   for(uint8_t i=1U; i<=4U; i++){ | ||||
|     uno_enable_can_transciever(i, enabled); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void uno_set_led(uint8_t color, bool enabled) { | ||||
|   switch (color){ | ||||
|     case LED_RED: | ||||
|       set_gpio_output(GPIOC, 9, !enabled); | ||||
|       break; | ||||
|      case LED_GREEN: | ||||
|       set_gpio_output(GPIOC, 7, !enabled); | ||||
|       break; | ||||
|     case LED_BLUE: | ||||
|       set_gpio_output(GPIOC, 6, !enabled); | ||||
|       break; | ||||
|     default: | ||||
|       break; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void uno_set_gps_load_switch(bool enabled) { | ||||
|   set_gpio_output(GPIOC, 12, enabled); | ||||
| } | ||||
| 
 | ||||
| void uno_set_usb_power_mode(uint8_t mode) { | ||||
|   UNUSED(mode); | ||||
|   puts("Setting USB mode makes no sense on UNO\n"); | ||||
| } | ||||
| 
 | ||||
| void uno_set_esp_gps_mode(uint8_t mode) { | ||||
|   switch (mode) { | ||||
|     case ESP_GPS_DISABLED: | ||||
|       // GPS OFF
 | ||||
|       set_gpio_output(GPIOB, 1, 0); | ||||
|       set_gpio_output(GPIOC, 5, 0); | ||||
|       uno_set_gps_load_switch(false); | ||||
|       break; | ||||
|     case ESP_GPS_ENABLED: | ||||
|       // GPS ON
 | ||||
|       set_gpio_output(GPIOB, 1, 1); | ||||
|       set_gpio_output(GPIOC, 5, 1); | ||||
|       uno_set_gps_load_switch(true); | ||||
|       break; | ||||
|     case ESP_GPS_BOOTMODE: | ||||
|       set_gpio_output(GPIOB, 1, 1); | ||||
|       set_gpio_output(GPIOC, 5, 0); | ||||
|       uno_set_gps_load_switch(true); | ||||
|       break; | ||||
|     default: | ||||
|       puts("Invalid ESP/GPS mode\n"); | ||||
|       break; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void uno_set_can_mode(uint8_t mode){ | ||||
|   switch (mode) { | ||||
|     case CAN_MODE_NORMAL: | ||||
|     case CAN_MODE_OBD_CAN2: | ||||
|       if ((bool)(mode == CAN_MODE_NORMAL) != (bool)(car_harness_status == HARNESS_STATUS_NORMAL)) { | ||||
|         // B12,B13: disable OBD mode
 | ||||
|         set_gpio_mode(GPIOB, 12, MODE_INPUT); | ||||
|         set_gpio_mode(GPIOB, 13, MODE_INPUT); | ||||
| 
 | ||||
|         // B5,B6: normal CAN2 mode
 | ||||
|         set_gpio_alternate(GPIOB, 5, GPIO_AF9_CAN2); | ||||
|         set_gpio_alternate(GPIOB, 6, GPIO_AF9_CAN2); | ||||
|       } else { | ||||
|         // B5,B6: disable normal CAN2 mode
 | ||||
|         set_gpio_mode(GPIOB, 5, MODE_INPUT); | ||||
|         set_gpio_mode(GPIOB, 6, MODE_INPUT); | ||||
| 
 | ||||
|         // B12,B13: OBD mode
 | ||||
|         set_gpio_alternate(GPIOB, 12, GPIO_AF9_CAN2); | ||||
|         set_gpio_alternate(GPIOB, 13, GPIO_AF9_CAN2); | ||||
|       } | ||||
|       break; | ||||
|     default: | ||||
|       puts("Tried to set unsupported CAN mode: "); puth(mode); puts("\n"); | ||||
|       break; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void uno_set_bootkick(bool enabled){ | ||||
|   set_gpio_output(GPIOB, 14, !enabled); | ||||
| } | ||||
| 
 | ||||
| void uno_usb_power_mode_tick(uint64_t tcnt){ | ||||
|   if(tcnt == 3U){ | ||||
|     uno_set_bootkick(false); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| bool uno_check_ignition(void){ | ||||
|   // ignition is checked through harness
 | ||||
|   return harness_check_ignition(); | ||||
| } | ||||
| 
 | ||||
| void uno_set_usb_switch(bool phone){ | ||||
|   set_gpio_output(GPIOB, 3, phone); | ||||
| } | ||||
| 
 | ||||
| void uno_set_ir_power(uint8_t percentage){ | ||||
|   pwm_set(TIM4, 2, percentage); | ||||
| } | ||||
| 
 | ||||
| void uno_set_fan_power(uint8_t percentage){ | ||||
|   // Enable fan power only if percentage is non-zero.
 | ||||
|   set_gpio_output(GPIOA, 1, (percentage != 0U)); | ||||
|   fan_set_power(percentage); | ||||
| } | ||||
| 
 | ||||
| uint32_t uno_read_current(void){ | ||||
|   // No current sense on Uno
 | ||||
|   return 0U; | ||||
| } | ||||
| 
 | ||||
| void uno_init(void) { | ||||
|   common_init_gpio(); | ||||
| 
 | ||||
|   // A8,A15: normal CAN3 mode
 | ||||
|   set_gpio_alternate(GPIOA, 8, GPIO_AF11_CAN3); | ||||
|   set_gpio_alternate(GPIOA, 15, GPIO_AF11_CAN3); | ||||
| 
 | ||||
|   // C0: OBD_SBU1 (orientation detection)
 | ||||
|   // C3: OBD_SBU2 (orientation detection)
 | ||||
|   set_gpio_mode(GPIOC, 0, MODE_ANALOG); | ||||
|   set_gpio_mode(GPIOC, 3, MODE_ANALOG); | ||||
| 
 | ||||
|   // C10: OBD_SBU1_RELAY (harness relay driving output)
 | ||||
|   // C11: OBD_SBU2_RELAY (harness relay driving output)
 | ||||
|   set_gpio_mode(GPIOC, 10, MODE_OUTPUT); | ||||
|   set_gpio_mode(GPIOC, 11, MODE_OUTPUT); | ||||
|   set_gpio_output_type(GPIOC, 10, OUTPUT_TYPE_OPEN_DRAIN); | ||||
|   set_gpio_output_type(GPIOC, 11, OUTPUT_TYPE_OPEN_DRAIN); | ||||
|   set_gpio_output(GPIOC, 10, 1); | ||||
|   set_gpio_output(GPIOC, 11, 1); | ||||
| 
 | ||||
|   // C8: FAN PWM aka TIM3_CH3
 | ||||
|   set_gpio_alternate(GPIOC, 8, GPIO_AF2_TIM3); | ||||
| 
 | ||||
|   // Turn on GPS load switch.
 | ||||
|   uno_set_gps_load_switch(true); | ||||
| 
 | ||||
|   // Turn on phone regulator
 | ||||
|   set_gpio_output(GPIOB, 4, 1); | ||||
| 
 | ||||
|   // Initialize IR PWM and set to 0%
 | ||||
|   set_gpio_alternate(GPIOB, 7, GPIO_AF2_TIM4); | ||||
|   pwm_init(TIM4, 2); | ||||
|   uno_set_ir_power(0U); | ||||
| 
 | ||||
|   // Initialize fan and set to 0%
 | ||||
|   fan_init(); | ||||
|   uno_set_fan_power(0U); | ||||
| 
 | ||||
|   // Initialize harness
 | ||||
|   harness_init(); | ||||
| 
 | ||||
|   // Initialize RTC
 | ||||
|   rtc_init(); | ||||
| 
 | ||||
|   // Enable CAN transcievers
 | ||||
|   uno_enable_can_transcievers(true); | ||||
| 
 | ||||
|   // Disable LEDs
 | ||||
|   uno_set_led(LED_RED, false); | ||||
|   uno_set_led(LED_GREEN, false); | ||||
|   uno_set_led(LED_BLUE, false); | ||||
| 
 | ||||
|   // Set normal CAN mode
 | ||||
|   uno_set_can_mode(CAN_MODE_NORMAL); | ||||
| 
 | ||||
|   // flip CAN0 and CAN2 if we are flipped
 | ||||
|   if (car_harness_status == HARNESS_STATUS_NORMAL) { | ||||
|     can_flip_buses(0, 2); | ||||
|   } | ||||
| 
 | ||||
|   // init multiplexer
 | ||||
|   can_set_obd(car_harness_status, false); | ||||
| 
 | ||||
|   // Switch to phone usb mode if harness connection is powered by less than 7V
 | ||||
|   if(adc_get_voltage() < 7000U){ | ||||
|     uno_set_usb_switch(true); | ||||
|   } else { | ||||
|     uno_set_usb_switch(false); | ||||
|   } | ||||
| 
 | ||||
|   // Bootkick phone
 | ||||
|   uno_set_bootkick(true); | ||||
| } | ||||
| 
 | ||||
| const harness_configuration uno_harness_config = { | ||||
|   .has_harness = true, | ||||
|   .GPIO_SBU1 = GPIOC, | ||||
|   .GPIO_SBU2 = GPIOC, | ||||
|   .GPIO_relay_normal = GPIOC, | ||||
|   .GPIO_relay_flipped = GPIOC, | ||||
|   .pin_SBU1 = 0, | ||||
|   .pin_SBU2 = 3, | ||||
|   .pin_relay_normal = 10, | ||||
|   .pin_relay_flipped = 11, | ||||
|   .adc_channel_SBU1 = 10, | ||||
|   .adc_channel_SBU2 = 13 | ||||
| }; | ||||
| 
 | ||||
| const board board_uno = { | ||||
|   .board_type = "Uno", | ||||
|   .harness_config = &uno_harness_config, | ||||
|   .init = uno_init, | ||||
|   .enable_can_transciever = uno_enable_can_transciever, | ||||
|   .enable_can_transcievers = uno_enable_can_transcievers, | ||||
|   .set_led = uno_set_led, | ||||
|   .set_usb_power_mode = uno_set_usb_power_mode, | ||||
|   .set_esp_gps_mode = uno_set_esp_gps_mode, | ||||
|   .set_can_mode = uno_set_can_mode, | ||||
|   .usb_power_mode_tick = uno_usb_power_mode_tick, | ||||
|   .check_ignition = uno_check_ignition, | ||||
|   .read_current = uno_read_current, | ||||
|   .set_fan_power = uno_set_fan_power, | ||||
|   .set_ir_power = uno_set_ir_power | ||||
| }; | ||||
| @ -0,0 +1,36 @@ | ||||
| void fan_init(void){ | ||||
|     // Init PWM speed control
 | ||||
|     pwm_init(TIM3, 3); | ||||
| 
 | ||||
|     // Init TACH interrupt
 | ||||
|     SYSCFG->EXTICR[0] = SYSCFG_EXTICR1_EXTI2_PD; | ||||
|     EXTI->IMR |= (1U << 2); | ||||
|     EXTI->RTSR |= (1U << 2); | ||||
|     EXTI->FTSR |= (1U << 2); | ||||
|     NVIC_EnableIRQ(EXTI2_IRQn); | ||||
| } | ||||
| 
 | ||||
| void fan_set_power(uint8_t percentage){ | ||||
|   pwm_set(TIM3, 3, percentage); | ||||
| } | ||||
| 
 | ||||
| uint16_t fan_tach_counter = 0U; | ||||
| uint16_t fan_rpm = 0U; | ||||
| 
 | ||||
| // Can be way more acurate than this, but this is probably good enough for our purposes.
 | ||||
| 
 | ||||
| // Call this every second
 | ||||
| void fan_tick(void){ | ||||
|     // 4 interrupts per rotation
 | ||||
|     fan_rpm = fan_tach_counter * 15U; | ||||
|     fan_tach_counter = 0U; | ||||
| } | ||||
| 
 | ||||
| // TACH interrupt handler
 | ||||
| void EXTI2_IRQHandler(void) { | ||||
|     volatile unsigned int pr = EXTI->PR & (1U << 2); | ||||
|     if ((pr & (1U << 2)) != 0U) { | ||||
|         fan_tach_counter++; | ||||
|     } | ||||
|     EXTI->PR = (1U << 2); | ||||
| } | ||||
| @ -0,0 +1,55 @@ | ||||
| #define PWM_COUNTER_OVERFLOW 2000U // To get ~50kHz
 | ||||
| 
 | ||||
| void pwm_init(TIM_TypeDef *TIM, uint8_t channel){ | ||||
|     // Enable timer and auto-reload
 | ||||
|     TIM->CR1 = TIM_CR1_CEN | TIM_CR1_ARPE; | ||||
| 
 | ||||
|     // Set channel as PWM mode 1 and enable output
 | ||||
|     switch(channel){ | ||||
|         case 1U: | ||||
|             TIM->CCMR1 |= (TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1PE); | ||||
|             TIM->CCER |= TIM_CCER_CC1E; | ||||
|             break; | ||||
|         case 2U: | ||||
|             TIM->CCMR1 |= (TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2PE); | ||||
|             TIM->CCER |= TIM_CCER_CC2E; | ||||
|             break; | ||||
|         case 3U: | ||||
|             TIM->CCMR2 |= (TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3PE); | ||||
|             TIM->CCER |= TIM_CCER_CC3E; | ||||
|             break; | ||||
|         case 4U: | ||||
|             TIM->CCMR2 |= (TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4PE); | ||||
|             TIM->CCER |= TIM_CCER_CC4E; | ||||
|             break; | ||||
|         default: | ||||
|             break; | ||||
|     } | ||||
| 
 | ||||
|     // Set max counter value
 | ||||
|     TIM->ARR = PWM_COUNTER_OVERFLOW; | ||||
| 
 | ||||
|     // Update registers and clear counter
 | ||||
|     TIM->EGR |= TIM_EGR_UG; | ||||
| } | ||||
| 
 | ||||
| // TODO: Implement for 32-bit timers
 | ||||
| void pwm_set(TIM_TypeDef *TIM, uint8_t channel, uint8_t percentage){ | ||||
|     uint16_t comp_value = (((uint16_t) percentage * PWM_COUNTER_OVERFLOW) / 100U); | ||||
|     switch(channel){ | ||||
|         case 1U: | ||||
|             TIM->CCR1 = comp_value; | ||||
|             break; | ||||
|         case 2U: | ||||
|             TIM->CCR2 = comp_value; | ||||
|             break; | ||||
|         case 3U: | ||||
|             TIM->CCR3 = comp_value; | ||||
|             break; | ||||
|         case 4U: | ||||
|             TIM->CCR4 = comp_value; | ||||
|             break; | ||||
|         default: | ||||
|             break; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,108 @@ | ||||
| #define RCC_BDCR_OPTIONS (RCC_BDCR_RTCEN | RCC_BDCR_RTCSEL_0 | RCC_BDCR_LSEON) | ||||
| #define RCC_BDCR_MASK (RCC_BDCR_RTCEN | RCC_BDCR_RTCSEL | RCC_BDCR_LSEMOD | RCC_BDCR_LSEBYP | RCC_BDCR_LSEON) | ||||
| 
 | ||||
| #define YEAR_OFFSET 2000U | ||||
| 
 | ||||
| typedef struct __attribute__((packed)) timestamp_t { | ||||
|   uint16_t year; | ||||
|   uint8_t month; | ||||
|   uint8_t day; | ||||
|   uint8_t weekday; | ||||
|   uint8_t hour; | ||||
|   uint8_t minute; | ||||
|   uint8_t second; | ||||
| } timestamp_t; | ||||
| 
 | ||||
| uint8_t to_bcd(uint16_t value){ | ||||
|     return (((value / 10U) & 0x0FU) << 4U) | ((value % 10U) & 0x0FU); | ||||
| } | ||||
| 
 | ||||
| uint16_t from_bcd(uint8_t value){ | ||||
|     return (((value & 0xF0U) >> 4U) * 10U) + (value & 0x0FU); | ||||
| } | ||||
| 
 | ||||
| void rtc_init(void){ | ||||
|     if(board_has_rtc()){ | ||||
|         // Initialize RTC module and clock if not done already.
 | ||||
|         if((RCC->BDCR & RCC_BDCR_MASK) != RCC_BDCR_OPTIONS){ | ||||
|             puts("Initializing RTC\n"); | ||||
|             // Reset backup domain
 | ||||
|             RCC->BDCR |= RCC_BDCR_BDRST; | ||||
| 
 | ||||
|             // Disable write protection
 | ||||
|             PWR->CR |= PWR_CR_DBP; | ||||
| 
 | ||||
|             // Clear backup domain reset
 | ||||
|             RCC->BDCR &= ~(RCC_BDCR_BDRST); | ||||
| 
 | ||||
|             // Set RTC options
 | ||||
|             RCC->BDCR = RCC_BDCR_OPTIONS | (RCC->BDCR & (~RCC_BDCR_MASK)); | ||||
| 
 | ||||
|             // Enable write protection
 | ||||
|             PWR->CR &= ~(PWR_CR_DBP); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void rtc_set_time(timestamp_t time){ | ||||
|     if(board_has_rtc()){ | ||||
|         puts("Setting RTC time\n"); | ||||
| 
 | ||||
|         // Disable write protection
 | ||||
|         PWR->CR |= PWR_CR_DBP; | ||||
|         RTC->WPR = 0xCA; | ||||
|         RTC->WPR = 0x53; | ||||
| 
 | ||||
|         // Enable initialization mode
 | ||||
|         RTC->ISR |= RTC_ISR_INIT; | ||||
|         while((RTC->ISR & RTC_ISR_INITF) == 0){} | ||||
|         
 | ||||
|         // Set time
 | ||||
|         RTC->TR = (to_bcd(time.hour) << RTC_TR_HU_Pos) | (to_bcd(time.minute) << RTC_TR_MNU_Pos) | (to_bcd(time.second) << RTC_TR_SU_Pos); | ||||
|         RTC->DR = (to_bcd(time.year - YEAR_OFFSET) << RTC_DR_YU_Pos) | (time.weekday << RTC_DR_WDU_Pos) | (to_bcd(time.month) << RTC_DR_MU_Pos) | (to_bcd(time.day) << RTC_DR_DU_Pos); | ||||
| 
 | ||||
|         // Set options
 | ||||
|         RTC->CR = 0U; | ||||
|         
 | ||||
|         // Disable initalization mode
 | ||||
|         RTC->ISR &= ~(RTC_ISR_INIT); | ||||
| 
 | ||||
|         // Wait for synchronization
 | ||||
|         while((RTC->ISR & RTC_ISR_RSF) == 0){} | ||||
| 
 | ||||
|         // Re-enable write protection
 | ||||
|         RTC->WPR = 0x00; | ||||
|         PWR->CR &= ~(PWR_CR_DBP); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| timestamp_t rtc_get_time(void){ | ||||
|     timestamp_t result; | ||||
|     // Init with zero values in case there is no RTC running
 | ||||
|     result.year = 0U; | ||||
|     result.month = 0U; | ||||
|     result.day = 0U; | ||||
|     result.weekday = 0U; | ||||
|     result.hour = 0U; | ||||
|     result.minute = 0U; | ||||
|     result.second = 0U; | ||||
| 
 | ||||
|     if(board_has_rtc()){ | ||||
|         // Wait until the register sync flag is set
 | ||||
|         while((RTC->ISR & RTC_ISR_RSF) == 0){} | ||||
| 
 | ||||
|         // Read time and date registers. Since our HSE > 7*LSE, this should be fine.
 | ||||
|         uint32_t time = RTC->TR; | ||||
|         uint32_t date = RTC->DR; | ||||
| 
 | ||||
|         // Parse values        
 | ||||
|         result.year = from_bcd((date & (RTC_DR_YT | RTC_DR_YU)) >> RTC_DR_YU_Pos) + YEAR_OFFSET; | ||||
|         result.month = from_bcd((date & (RTC_DR_MT | RTC_DR_MU)) >> RTC_DR_MU_Pos); | ||||
|         result.day = from_bcd((date & (RTC_DR_DT | RTC_DR_DU)) >> RTC_DR_DU_Pos); | ||||
|         result.weekday = ((date & RTC_DR_WDU) >> RTC_DR_WDU_Pos); | ||||
|         result.hour = from_bcd((time & (RTC_TR_HT | RTC_TR_HU)) >> RTC_TR_HU_Pos); | ||||
|         result.minute = from_bcd((time & (RTC_TR_MNT | RTC_TR_MNU)) >> RTC_TR_MNU_Pos); | ||||
|         result.second = from_bcd((time & (RTC_TR_ST | RTC_TR_SU)) >> RTC_TR_SU_Pos); | ||||
|     } | ||||
|     return result; | ||||
| } | ||||
| @ -1,3 +1,3 @@ | ||||
| #!/bin/bash | ||||
| sudo apt-get install gcc-arm-none-eabi python-pip | ||||
| sudo pip2 install libusb1 pycrypto requests | ||||
| sudo pip install libusb1 pycrypto requests | ||||
|  | ||||
| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1
 | ||||
| oid sha256:3eeeb716701baca7c9aec136ada74ac29fa5731d188c1728f66b13e54a69ab64 | ||||
| size 9482 | ||||
| @ -1,3 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1
 | ||||
| oid sha256:6c7e75d09887cb3035f18bc6f4fdbd7392a50379cd556315deb3bcb762d99078 | ||||
| size 40062 | ||||
| oid sha256:9bf43ac370e82ed7c9cc9a9fb5b624276fa03d15f96103550c7ab3b043f048f2 | ||||
| size 62628 | ||||
|  | ||||
| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1
 | ||||
| oid sha256:d649eaa2fb1c421add02950ab9c111ccabb8be27197f3d37e03f03357e817ff8 | ||||
| size 1677 | ||||
| @ -1,3 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1
 | ||||
| oid sha256:03a80f17ff18c5d0e3b234009f98698cd402b8ea972df687a6cfafd8b8a78654 | ||||
| size 102146 | ||||
| oid sha256:01793bb15ec31729c8d1d4422a2e86ce5e0728b6a8834b94f08ea46c4c43b3b5 | ||||
| size 109421 | ||||
|  | ||||
| @ -1,3 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1
 | ||||
| oid sha256:83c5d601dd5640a863fe4dc43cd228b46239bbf9f07b51102bf0c1c8f235cdfa | ||||
| size 112561 | ||||
| oid sha256:dd13b2caea9d4490a5d49cdb86fbfa3e0d7b01343dc0ad8560c10a924626320e | ||||
| size 120868 | ||||
|  | ||||
| @ -1,3 +0,0 @@ | ||||
| version https://git-lfs.github.com/spec/v1
 | ||||
| oid sha256:1fdd226d82bde2a90231ba47a59377158b8907e4cada2a6215f32dcf13ab35cd | ||||
| size 3533 | ||||
| @ -1,3 +0,0 @@ | ||||
| version https://git-lfs.github.com/spec/v1
 | ||||
| oid sha256:c9b8e36d05097da5d493f197aa7f81ce517f4532536925b568ccbd6e2c01eb8a | ||||
| size 3549 | ||||
| @ -1,3 +0,0 @@ | ||||
| version https://git-lfs.github.com/spec/v1
 | ||||
| oid sha256:5d338d36730ccbd138126078e17f69c308a015f0a88d85bd639af072e527d53d | ||||
| size 3566 | ||||
| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1
 | ||||
| oid sha256:16660b4b9af5c288949272e55dfc463fd704acd7bbbe34f0fa6acbdea2d41c22 | ||||
| size 11691 | ||||
| @ -0,0 +1,167 @@ | ||||
| const int VOLKSWAGEN_MAX_STEER = 250;               // 2.5 Nm (EPS side max of 3.0Nm with fault if violated)
 | ||||
| const int VOLKSWAGEN_MAX_RT_DELTA = 75;             // 4 max rate up * 50Hz send rate * 250000 RT interval / 1000000 = 50 ; 50 * 1.5 for safety pad = 75
 | ||||
| const uint32_t VOLKSWAGEN_RT_INTERVAL = 250000;     // 250ms between real time checks
 | ||||
| const int VOLKSWAGEN_MAX_RATE_UP = 4;               // 2.0 Nm/s available rate of change from the steering rack (EPS side delta-limit of 5.0 Nm/s)
 | ||||
| const int VOLKSWAGEN_MAX_RATE_DOWN = 10;            // 5.0 Nm/s available rate of change from the steering rack (EPS side delta-limit of 5.0 Nm/s)
 | ||||
| const int VOLKSWAGEN_DRIVER_TORQUE_ALLOWANCE = 80; | ||||
| const int VOLKSWAGEN_DRIVER_TORQUE_FACTOR = 3; | ||||
| 
 | ||||
| struct sample_t volkswagen_torque_driver;           // last few driver torques measured
 | ||||
| int volkswagen_rt_torque_last = 0; | ||||
| int volkswagen_desired_torque_last = 0; | ||||
| uint32_t volkswagen_ts_last = 0; | ||||
| int volkswagen_gas_prev = 0; | ||||
| 
 | ||||
| // Safety-relevant CAN messages for the Volkswagen MQB platform.
 | ||||
| #define MSG_EPS_01              0x09F | ||||
| #define MSG_MOTOR_20            0x121 | ||||
| #define MSG_ACC_06              0x122 | ||||
| #define MSG_HCA_01              0x126 | ||||
| #define MSG_GRA_ACC_01          0x12B | ||||
| #define MSG_LDW_02              0x397 | ||||
| #define MSG_KLEMMEN_STATUS_01   0x3C0 | ||||
| 
 | ||||
| static void volkswagen_init(int16_t param) { | ||||
|   UNUSED(param); // May use param in the future to indicate MQB vs PQ35/PQ46/NMS vs MLB, or wiring configuration.
 | ||||
|   controls_allowed = 0; | ||||
| } | ||||
| 
 | ||||
| static void volkswagen_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { | ||||
|   int bus = GET_BUS(to_push); | ||||
|   int addr = GET_ADDR(to_push); | ||||
| 
 | ||||
|   // Update driver input torque samples from EPS_01.Driver_Strain for absolute torque, and EPS_01.Driver_Strain_VZ
 | ||||
|   // for the direction.
 | ||||
|   if ((bus == 0) && (addr == MSG_EPS_01)) { | ||||
|     int torque_driver_new = GET_BYTE(to_push, 5) | ((GET_BYTE(to_push, 6) & 0x1F) << 8); | ||||
|     int sign = (GET_BYTE(to_push, 6) & 0x80) >> 7; | ||||
|     if (sign == 1) { | ||||
|       torque_driver_new *= -1; | ||||
|     } | ||||
| 
 | ||||
|     update_sample(&volkswagen_torque_driver, torque_driver_new); | ||||
|   } | ||||
| 
 | ||||
|   // Monitor ACC_06.ACC_Status_ACC for stock ACC status. Because the current MQB port is lateral-only, OP's control
 | ||||
|   // allowed state is directly driven by stock ACC engagement. Permit the ACC message to come from either bus, in
 | ||||
|   // order to accommodate future camera-side integrations if needed.
 | ||||
|   if (addr == MSG_ACC_06) { | ||||
|     int acc_status = (GET_BYTE(to_push, 7) & 0x70) >> 4; | ||||
|     controls_allowed = ((acc_status == 3) || (acc_status == 4) || (acc_status == 5)) ? 1 : 0; | ||||
|   } | ||||
| 
 | ||||
|   // exit controls on rising edge of gas press. Bits [12-20)
 | ||||
|   if (addr == MSG_MOTOR_20) { | ||||
|     int gas = (GET_BYTES_04(to_push) >> 12) & 0xFF; | ||||
|     if ((gas > 0) && (volkswagen_gas_prev == 0) && long_controls_allowed) { | ||||
|       controls_allowed = 0; | ||||
|     } | ||||
|     volkswagen_gas_prev = gas; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| static int volkswagen_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { | ||||
|   int addr = GET_ADDR(to_send); | ||||
|   int bus = GET_BUS(to_send); | ||||
|   int tx = 1; | ||||
| 
 | ||||
|   // Safety check for HCA_01 Heading Control Assist torque.
 | ||||
|   if (addr == MSG_HCA_01) { | ||||
|     bool violation = false; | ||||
| 
 | ||||
|     int desired_torque = GET_BYTE(to_send, 2) | ((GET_BYTE(to_send, 3) & 0x3F) << 8); | ||||
|     int sign = (GET_BYTE(to_send, 3) & 0x80) >> 7; | ||||
|     if (sign == 1) { | ||||
|       desired_torque *= -1; | ||||
|     } | ||||
| 
 | ||||
|     uint32_t ts = TIM2->CNT; | ||||
| 
 | ||||
|     if (controls_allowed) { | ||||
| 
 | ||||
|       // *** global torque limit check ***
 | ||||
|       violation |= max_limit_check(desired_torque, VOLKSWAGEN_MAX_STEER, -VOLKSWAGEN_MAX_STEER); | ||||
| 
 | ||||
|       // *** torque rate limit check ***
 | ||||
|       violation |= driver_limit_check(desired_torque, volkswagen_desired_torque_last, &volkswagen_torque_driver, | ||||
|         VOLKSWAGEN_MAX_STEER, VOLKSWAGEN_MAX_RATE_UP, VOLKSWAGEN_MAX_RATE_DOWN, | ||||
|         VOLKSWAGEN_DRIVER_TORQUE_ALLOWANCE, VOLKSWAGEN_DRIVER_TORQUE_FACTOR); | ||||
|       volkswagen_desired_torque_last = desired_torque; | ||||
| 
 | ||||
|       // *** torque real time rate limit check ***
 | ||||
|       violation |= rt_rate_limit_check(desired_torque, volkswagen_rt_torque_last, VOLKSWAGEN_MAX_RT_DELTA); | ||||
| 
 | ||||
|       // every RT_INTERVAL set the new limits
 | ||||
|       uint32_t ts_elapsed = get_ts_elapsed(ts, volkswagen_ts_last); | ||||
|       if (ts_elapsed > VOLKSWAGEN_RT_INTERVAL) { | ||||
|         volkswagen_rt_torque_last = desired_torque; | ||||
|         volkswagen_ts_last = ts; | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     // no torque if controls is not allowed
 | ||||
|     if (!controls_allowed && (desired_torque != 0)) { | ||||
|       violation = true; | ||||
|     } | ||||
| 
 | ||||
|     // reset to 0 if either controls is not allowed or there's a violation
 | ||||
|     if (violation || !controls_allowed) { | ||||
|       volkswagen_desired_torque_last = 0; | ||||
|       volkswagen_rt_torque_last = 0; | ||||
|       volkswagen_ts_last = ts; | ||||
|     } | ||||
| 
 | ||||
|     if (violation) { | ||||
|       tx = 0; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   // FORCE CANCEL: ensuring that only the cancel button press is sent when controls are off.
 | ||||
|   // This avoids unintended engagements while still allowing resume spam
 | ||||
|   if ((bus == 2) && (addr == MSG_GRA_ACC_01) && !controls_allowed) { | ||||
|     // disallow resume and set: bits 16 and 19
 | ||||
|     if ((GET_BYTE(to_send, 2) & 0x9) != 0) { | ||||
|       tx = 0; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   // 1 allows the message through
 | ||||
|   return tx; | ||||
| } | ||||
| 
 | ||||
| static int volkswagen_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { | ||||
|   int addr = GET_ADDR(to_fwd); | ||||
|   int bus_fwd = -1; | ||||
| 
 | ||||
|   // NOTE: Will need refactoring for other bus layouts, such as no-forwarding at camera or J533 running-gear CAN
 | ||||
| 
 | ||||
|   switch (bus_num) { | ||||
|     case 0: | ||||
|       // Forward all traffic from J533 gateway to Extended CAN devices.
 | ||||
|       bus_fwd = 2; | ||||
|       break; | ||||
|     case 2: | ||||
|       if ((addr == MSG_HCA_01) || (addr == MSG_LDW_02)) { | ||||
|         // OP takes control of the Heading Control Assist and Lane Departure Warning messages from the camera.
 | ||||
|         bus_fwd = -1; | ||||
|       } else { | ||||
|         // Forward all remaining traffic from Extended CAN devices to J533 gateway.
 | ||||
|         bus_fwd = 0; | ||||
|       } | ||||
|       break; | ||||
|     default: | ||||
|       // No other buses should be in use; fallback to do-not-forward.
 | ||||
|       bus_fwd = -1; | ||||
|       break; | ||||
|   } | ||||
| 
 | ||||
|   return bus_fwd; | ||||
| } | ||||
| 
 | ||||
| const safety_hooks volkswagen_hooks = { | ||||
|   .init = volkswagen_init, | ||||
|   .rx = volkswagen_rx_hook, | ||||
|   .tx = volkswagen_tx_hook, | ||||
|   .tx_lin = nooutput_tx_lin_hook, | ||||
|   .fwd = volkswagen_fwd_hook, | ||||
| }; | ||||
| @ -0,0 +1,59 @@ | ||||
| #!/usr/bin/env python3 | ||||
| from panda import Panda | ||||
| from panda.python.uds import UdsClient, NegativeResponseError, DATA_IDENTIFIER_TYPE | ||||
| 
 | ||||
| if __name__ == "__main__": | ||||
|   address = 0x18da30f1 # Honda EPS | ||||
|   panda = Panda() | ||||
|   uds_client = UdsClient(panda, address, debug=False) | ||||
| 
 | ||||
|   print("tester present ...") | ||||
|   uds_client.tester_present() | ||||
| 
 | ||||
|   try: | ||||
|     print("") | ||||
|     print("read data by id: boot software id ...") | ||||
|     data = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.BOOT_SOFTWARE_IDENTIFICATION) | ||||
|     print(data.decode('utf-8')) | ||||
|   except NegativeResponseError as e: | ||||
|     print(e) | ||||
| 
 | ||||
|   try: | ||||
|     print("") | ||||
|     print("read data by id: application software id ...") | ||||
|     data = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.APPLICATION_SOFTWARE_IDENTIFICATION) | ||||
|     print(data.decode('utf-8')) | ||||
|   except NegativeResponseError as e: | ||||
|     print(e) | ||||
| 
 | ||||
|   try: | ||||
|     print("") | ||||
|     print("read data by id: application data id ...") | ||||
|     data = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.APPLICATION_DATA_IDENTIFICATION) | ||||
|     print(data.decode('utf-8')) | ||||
|   except NegativeResponseError as e: | ||||
|     print(e) | ||||
| 
 | ||||
|   try: | ||||
|     print("") | ||||
|     print("read data by id: boot software fingerprint ...") | ||||
|     data = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.BOOT_SOFTWARE_FINGERPRINT) | ||||
|     print(data.decode('utf-8')) | ||||
|   except NegativeResponseError as e: | ||||
|     print(e) | ||||
| 
 | ||||
|   try: | ||||
|     print("") | ||||
|     print("read data by id: application software fingerprint ...") | ||||
|     data = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.APPLICATION_SOFTWARE_FINGERPRINT) | ||||
|     print(data.decode('utf-8')) | ||||
|   except NegativeResponseError as e: | ||||
|     print(e) | ||||
| 
 | ||||
|   try: | ||||
|     print("") | ||||
|     print("read data by id: application data fingerprint ...") | ||||
|     data = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.APPLICATION_DATA_FINGERPRINT) | ||||
|     print(data.decode('utf-8')) | ||||
|   except NegativeResponseError as e: | ||||
|     print(e) | ||||
| @ -0,0 +1,770 @@ | ||||
| #!/usr/bin/env python3 | ||||
| import time | ||||
| import struct | ||||
| from typing import NamedTuple, List | ||||
| from enum import IntEnum | ||||
| from queue import Queue, Empty | ||||
| from threading import Thread | ||||
| from binascii import hexlify | ||||
| 
 | ||||
| class SERVICE_TYPE(IntEnum): | ||||
|   DIAGNOSTIC_SESSION_CONTROL         = 0x10 | ||||
|   ECU_RESET                          = 0x11 | ||||
|   SECURITY_ACCESS                    = 0x27 | ||||
|   COMMUNICATION_CONTROL              = 0x28 | ||||
|   TESTER_PRESENT                     = 0x3E | ||||
|   ACCESS_TIMING_PARAMETER            = 0x83 | ||||
|   SECURED_DATA_TRANSMISSION          = 0x84 | ||||
|   CONTROL_DTC_SETTING                = 0x85 | ||||
|   RESPONSE_ON_EVENT                  = 0x86 | ||||
|   LINK_CONTROL                       = 0x87 | ||||
|   READ_DATA_BY_IDENTIFIER            = 0x22 | ||||
|   READ_MEMORY_BY_ADDRESS             = 0x23 | ||||
|   READ_SCALING_DATA_BY_IDENTIFIER    = 0x24 | ||||
|   READ_DATA_BY_PERIODIC_IDENTIFIER   = 0x2A | ||||
|   DYNAMICALLY_DEFINE_DATA_IDENTIFIER = 0x2C | ||||
|   WRITE_DATA_BY_IDENTIFIER           = 0x2E | ||||
|   WRITE_MEMORY_BY_ADDRESS            = 0x3D | ||||
|   CLEAR_DIAGNOSTIC_INFORMATION       = 0x14 | ||||
|   READ_DTC_INFORMATION               = 0x19 | ||||
|   INPUT_OUTPUT_CONTROL_BY_IDENTIFIER = 0x2F | ||||
|   ROUTINE_CONTROL                    = 0x31 | ||||
|   REQUEST_DOWNLOAD                   = 0x34 | ||||
|   REQUEST_UPLOAD                     = 0x35 | ||||
|   TRANSFER_DATA                      = 0x36 | ||||
|   REQUEST_TRANSFER_EXIT              = 0x37 | ||||
| 
 | ||||
| class SESSION_TYPE(IntEnum): | ||||
|   DEFAULT = 1 | ||||
|   PROGRAMMING = 2 | ||||
|   EXTENDED_DIAGNOSTIC = 3 | ||||
|   SAFETY_SYSTEM_DIAGNOSTIC = 4 | ||||
| 
 | ||||
| class RESET_TYPE(IntEnum): | ||||
|   HARD = 1 | ||||
|   KEY_OFF_ON = 2 | ||||
|   SOFT = 3 | ||||
|   ENABLE_RAPID_POWER_SHUTDOWN = 4 | ||||
|   DISABLE_RAPID_POWER_SHUTDOWN = 5 | ||||
| 
 | ||||
| class ACCESS_TYPE(IntEnum): | ||||
|   REQUEST_SEED = 1 | ||||
|   SEND_KEY = 2 | ||||
| 
 | ||||
| class CONTROL_TYPE(IntEnum): | ||||
|   ENABLE_RX_ENABLE_TX = 0 | ||||
|   ENABLE_RX_DISABLE_TX = 1 | ||||
|   DISABLE_RX_ENABLE_TX = 2 | ||||
|   DISABLE_RX_DISABLE_TX = 3 | ||||
| 
 | ||||
| class MESSAGE_TYPE(IntEnum): | ||||
|   NORMAL = 1 | ||||
|   NETWORK_MANAGEMENT = 2 | ||||
|   NORMAL_AND_NETWORK_MANAGEMENT = 3 | ||||
| 
 | ||||
| class TIMING_PARAMETER_TYPE(IntEnum): | ||||
|   READ_EXTENDED_SET = 1 | ||||
|   SET_TO_DEFAULT_VALUES = 2 | ||||
|   READ_CURRENTLY_ACTIVE = 3 | ||||
|   SET_TO_GIVEN_VALUES = 4 | ||||
| 
 | ||||
| class DTC_SETTING_TYPE(IntEnum): | ||||
|   ON = 1 | ||||
|   OFF = 2 | ||||
| 
 | ||||
| class RESPONSE_EVENT_TYPE(IntEnum): | ||||
|   STOP_RESPONSE_ON_EVENT = 0 | ||||
|   ON_DTC_STATUS_CHANGE = 1 | ||||
|   ON_TIMER_INTERRUPT = 2 | ||||
|   ON_CHANGE_OF_DATA_IDENTIFIER = 3 | ||||
|   REPORT_ACTIVATED_EVENTS = 4 | ||||
|   START_RESPONSE_ON_EVENT = 5 | ||||
|   CLEAR_RESPONSE_ON_EVENT = 6 | ||||
|   ON_COMPARISON_OF_VALUES = 7 | ||||
| 
 | ||||
| class LINK_CONTROL_TYPE(IntEnum): | ||||
|   VERIFY_BAUDRATE_TRANSITION_WITH_FIXED_BAUDRATE = 1 | ||||
|   VERIFY_BAUDRATE_TRANSITION_WITH_SPECIFIC_BAUDRATE = 2 | ||||
|   TRANSITION_BAUDRATE = 3 | ||||
| 
 | ||||
| class BAUD_RATE_TYPE(IntEnum): | ||||
|   PC9600 = 1 | ||||
|   PC19200 = 2 | ||||
|   PC38400 = 3 | ||||
|   PC57600 = 4 | ||||
|   PC115200 = 5 | ||||
|   CAN125000 = 16 | ||||
|   CAN250000 = 17 | ||||
|   CAN500000 = 18 | ||||
|   CAN1000000 = 19 | ||||
| 
 | ||||
| class DATA_IDENTIFIER_TYPE(IntEnum): | ||||
|   BOOT_SOFTWARE_IDENTIFICATION = 0xF180 | ||||
|   APPLICATION_SOFTWARE_IDENTIFICATION = 0xF181 | ||||
|   APPLICATION_DATA_IDENTIFICATION = 0xF182 | ||||
|   BOOT_SOFTWARE_FINGERPRINT = 0xF183 | ||||
|   APPLICATION_SOFTWARE_FINGERPRINT = 0xF184 | ||||
|   APPLICATION_DATA_FINGERPRINT = 0xF185 | ||||
|   ACTIVE_DIAGNOSTIC_SESSION = 0xF186 | ||||
|   VEHICLE_MANUFACTURER_SPARE_PART_NUMBER = 0xF187 | ||||
|   VEHICLE_MANUFACTURER_ECU_SOFTWARE_NUMBER = 0xF188 | ||||
|   VEHICLE_MANUFACTURER_ECU_SOFTWARE_VERSION_NUMBER = 0xF189 | ||||
|   SYSTEM_SUPPLIER_IDENTIFIER = 0xF18A | ||||
|   ECU_MANUFACTURING_DATE = 0xF18B | ||||
|   ECU_SERIAL_NUMBER = 0xF18C | ||||
|   SUPPORTED_FUNCTIONAL_UNITS = 0xF18D | ||||
|   VEHICLE_MANUFACTURER_KIT_ASSEMBLY_PART_NUMBER = 0xF18E | ||||
|   VIN = 0xF190 | ||||
|   VEHICLE_MANUFACTURER_ECU_HARDWARE_NUMBER = 0xF191 | ||||
|   SYSTEM_SUPPLIER_ECU_HARDWARE_NUMBER = 0xF192 | ||||
|   SYSTEM_SUPPLIER_ECU_HARDWARE_VERSION_NUMBER = 0xF193 | ||||
|   SYSTEM_SUPPLIER_ECU_SOFTWARE_NUMBER = 0xF194 | ||||
|   SYSTEM_SUPPLIER_ECU_SOFTWARE_VERSION_NUMBER = 0xF195 | ||||
|   EXHAUST_REGULATION_OR_TYPE_APPROVAL_NUMBER = 0xF196 | ||||
|   SYSTEM_NAME_OR_ENGINE_TYPE = 0xF197 | ||||
|   REPAIR_SHOP_CODE_OR_TESTER_SERIAL_NUMBER = 0xF198 | ||||
|   PROGRAMMING_DATE = 0xF199 | ||||
|   CALIBRATION_REPAIR_SHOP_CODE_OR_CALIBRATION_EQUIPMENT_SERIAL_NUMBER = 0xF19A | ||||
|   CALIBRATION_DATE = 0xF19B | ||||
|   CALIBRATION_EQUIPMENT_SOFTWARE_NUMBER = 0xF19C | ||||
|   ECU_INSTALLATION_DATE = 0xF19D | ||||
|   ODX_FILE = 0xF19E | ||||
|   ENTITY = 0xF19F | ||||
| 
 | ||||
| class TRANSMISSION_MODE_TYPE(IntEnum): | ||||
|   SEND_AT_SLOW_RATE = 1 | ||||
|   SEND_AT_MEDIUM_RATE = 2 | ||||
|   SEND_AT_FAST_RATE = 3 | ||||
|   STOP_SENDING = 4 | ||||
| 
 | ||||
| class DYNAMIC_DEFINITION_TYPE(IntEnum): | ||||
|   DEFINE_BY_IDENTIFIER = 1 | ||||
|   DEFINE_BY_MEMORY_ADDRESS = 2 | ||||
|   CLEAR_DYNAMICALLY_DEFINED_DATA_IDENTIFIER = 3 | ||||
| 
 | ||||
| class DynamicSourceDefinition(NamedTuple): | ||||
|   data_identifier: int | ||||
|   position: int | ||||
|   memory_size: int | ||||
|   memory_address: int | ||||
| 
 | ||||
| class DTC_GROUP_TYPE(IntEnum): | ||||
|   EMISSIONS = 0x000000 | ||||
|   ALL = 0xFFFFFF | ||||
| 
 | ||||
| class DTC_REPORT_TYPE(IntEnum): | ||||
|   NUMBER_OF_DTC_BY_STATUS_MASK = 0x01 | ||||
|   DTC_BY_STATUS_MASK = 0x02 | ||||
|   DTC_SNAPSHOT_IDENTIFICATION = 0x03 | ||||
|   DTC_SNAPSHOT_RECORD_BY_DTC_NUMBER = 0x04 | ||||
|   DTC_SNAPSHOT_RECORD_BY_RECORD_NUMBER = 0x05 | ||||
|   DTC_EXTENDED_DATA_RECORD_BY_DTC_NUMBER = 0x06 | ||||
|   NUMBER_OF_DTC_BY_SEVERITY_MASK_RECORD = 0x07 | ||||
|   DTC_BY_SEVERITY_MASK_RECORD = 0x08 | ||||
|   SEVERITY_INFORMATION_OF_DTC = 0x09 | ||||
|   SUPPORTED_DTC = 0x0A | ||||
|   FIRST_TEST_FAILED_DTC = 0x0B | ||||
|   FIRST_CONFIRMED_DTC = 0x0C | ||||
|   MOST_RECENT_TEST_FAILED_DTC = 0x0D | ||||
|   MOST_RECENT_CONFIRMED_DTC = 0x0E | ||||
|   MIRROR_MEMORY_DTC_BY_STATUS_MASK = 0x0F | ||||
|   MIRROR_MEMORY_DTC_EXTENDED_DATA_RECORD_BY_DTC_NUMBER = 0x10 | ||||
|   NUMBER_OF_MIRROR_MEMORY_DTC_BY_STATUS_MASK = 0x11 | ||||
|   NUMBER_OF_EMISSIONS_RELATED_OBD_DTC_BY_STATUS_MASK = 0x12 | ||||
|   EMISSIONS_RELATED_OBD_DTC_BY_STATUS_MASK = 0x13 | ||||
|   DTC_FAULT_DETECTION_COUNTER = 0x14 | ||||
|   DTC_WITH_PERMANENT_STATUS = 0x15 | ||||
| 
 | ||||
| class DTC_STATUS_MASK_TYPE(IntEnum): | ||||
|   TEST_FAILED = 0x01 | ||||
|   TEST_FAILED_THIS_OPERATION_CYCLE = 0x02 | ||||
|   PENDING_DTC = 0x04 | ||||
|   CONFIRMED_DTC = 0x08 | ||||
|   TEST_NOT_COMPLETED_SINCE_LAST_CLEAR = 0x10 | ||||
|   TEST_FAILED_SINCE_LAST_CLEAR = 0x20 | ||||
|   TEST_NOT_COMPLETED_THIS_OPERATION_CYCLE = 0x40 | ||||
|   WARNING_INDICATOR_REQUESTED = 0x80 | ||||
|   ALL = 0xFF | ||||
| 
 | ||||
| class DTC_SEVERITY_MASK_TYPE(IntEnum): | ||||
|   MAINTENANCE_ONLY = 0x20 | ||||
|   CHECK_AT_NEXT_HALT = 0x40 | ||||
|   CHECK_IMMEDIATELY = 0x80 | ||||
|   ALL = 0xE0 | ||||
| 
 | ||||
| class CONTROL_PARAMETER_TYPE(IntEnum): | ||||
|   RETURN_CONTROL_TO_ECU = 0 | ||||
|   RESET_TO_DEFAULT = 1 | ||||
|   FREEZE_CURRENT_STATE = 2 | ||||
|   SHORT_TERM_ADJUSTMENT = 3 | ||||
| 
 | ||||
| class ROUTINE_CONTROL_TYPE(IntEnum): | ||||
|   START = 1 | ||||
|   STOP = 2 | ||||
|   REQUEST_RESULTS = 3 | ||||
| 
 | ||||
| class ROUTINE_IDENTIFIER_TYPE(IntEnum): | ||||
|   ERASE_MEMORY = 0xFF00 | ||||
|   CHECK_PROGRAMMING_DEPENDENCIES = 0xFF01 | ||||
|   ERASE_MIRROR_MEMORY_DTCS = 0xFF02 | ||||
| 
 | ||||
| class MessageTimeoutError(Exception): | ||||
|   pass | ||||
| 
 | ||||
| class NegativeResponseError(Exception): | ||||
|   def __init__(self, message, service_id, error_code): | ||||
|     self.message = message | ||||
|     self.service_id = service_id | ||||
|     self.error_code = error_code | ||||
| 
 | ||||
|   def __str__(self): | ||||
|     return self.message | ||||
| 
 | ||||
| class InvalidServiceIdError(Exception): | ||||
|   pass | ||||
| 
 | ||||
| class InvalidSubFunctioneError(Exception): | ||||
|   pass | ||||
| 
 | ||||
| _negative_response_codes = { | ||||
|     0x00: 'positive response', | ||||
|     0x10: 'general reject', | ||||
|     0x11: 'service not supported', | ||||
|     0x12: 'sub-function not supported', | ||||
|     0x13: 'incorrect message length or invalid format', | ||||
|     0x14: 'response too long', | ||||
|     0x21: 'busy repeat request', | ||||
|     0x22: 'conditions not correct', | ||||
|     0x24: 'request sequence error', | ||||
|     0x25: 'no response from subnet component', | ||||
|     0x26: 'failure prevents execution of requested action', | ||||
|     0x31: 'request out of range', | ||||
|     0x33: 'security access denied', | ||||
|     0x35: 'invalid key', | ||||
|     0x36: 'exceed numebr of attempts', | ||||
|     0x37: 'required time delay not expired', | ||||
|     0x70: 'upload download not accepted', | ||||
|     0x71: 'transfer data suspended', | ||||
|     0x72: 'general programming failure', | ||||
|     0x73: 'wrong block sequence counter', | ||||
|     0x78: 'request correctly received - response pending', | ||||
|     0x7e: 'sub-function not supported in active session', | ||||
|     0x7f: 'service not supported in active session', | ||||
|     0x81: 'rpm too high', | ||||
|     0x82: 'rpm too low', | ||||
|     0x83: 'engine is running', | ||||
|     0x84: 'engine is not running', | ||||
|     0x85: 'engine run time too low', | ||||
|     0x86: 'temperature too high', | ||||
|     0x87: 'temperature too low', | ||||
|     0x88: 'vehicle speed too high', | ||||
|     0x89: 'vehicle speed too low', | ||||
|     0x8a: 'throttle/pedal too high', | ||||
|     0x8b: 'throttle/pedal too low', | ||||
|     0x8c: 'transmission not in neutral', | ||||
|     0x8d: 'transmission not in gear', | ||||
|     0x8f: 'brake switch(es) not closed', | ||||
|     0x90: 'shifter lever not in park', | ||||
|     0x91: 'torque converter clutch locked', | ||||
|     0x92: 'voltage too high', | ||||
|     0x93: 'voltage too low', | ||||
| } | ||||
| 
 | ||||
| class IsoTpMessage(): | ||||
|   def __init__(self, can_tx_queue: Queue, can_rx_queue: Queue, timeout: float, debug: bool=False): | ||||
|     self.can_tx_queue = can_tx_queue | ||||
|     self.can_rx_queue = can_rx_queue | ||||
|     self.timeout = timeout | ||||
|     self.debug = debug | ||||
| 
 | ||||
|   def send(self, dat: bytes) -> None: | ||||
|     self.tx_dat = dat | ||||
|     self.tx_len = len(dat) | ||||
|     self.tx_idx = 0 | ||||
|     self.tx_done = False | ||||
| 
 | ||||
|     if self.debug: print(f"ISO-TP: REQUEST - {hexlify(self.tx_dat)}") | ||||
|     self._tx_first_frame() | ||||
| 
 | ||||
|   def _tx_first_frame(self) -> None: | ||||
|     if self.tx_len < 8: | ||||
|       # single frame (send all bytes) | ||||
|       if self.debug: print("ISO-TP: TX - single frame") | ||||
|       msg = (bytes([self.tx_len]) + self.tx_dat).ljust(8, b"\x00") | ||||
|       self.tx_done = True | ||||
|     else: | ||||
|       # first frame (send first 6 bytes) | ||||
|       if self.debug: print("ISO-TP: TX - first frame") | ||||
|       msg = (struct.pack("!H", 0x1000 | self.tx_len) + self.tx_dat[:6]).ljust(8, b"\x00") | ||||
|     self.can_tx_queue.put(msg) | ||||
| 
 | ||||
|   def recv(self) -> bytes: | ||||
|     self.rx_dat = b"" | ||||
|     self.rx_len = 0 | ||||
|     self.rx_idx = 0 | ||||
|     self.rx_done = False | ||||
| 
 | ||||
|     try: | ||||
|       while True: | ||||
|         self._isotp_rx_next() | ||||
|         if self.tx_done and self.rx_done: | ||||
|           return self.rx_dat | ||||
|     except Empty: | ||||
|       raise MessageTimeoutError("timeout waiting for response") | ||||
|     finally: | ||||
|       if self.debug: print(f"ISO-TP: RESPONSE - {hexlify(self.rx_dat)}") | ||||
| 
 | ||||
|   def _isotp_rx_next(self) -> None: | ||||
|     rx_data = self.can_rx_queue.get(block=True, timeout=self.timeout) | ||||
| 
 | ||||
|     # single rx_frame | ||||
|     if rx_data[0] >> 4 == 0x0: | ||||
|       self.rx_len = rx_data[0] & 0xFF | ||||
|       self.rx_dat = rx_data[1:1+self.rx_len] | ||||
|       self.rx_idx = 0 | ||||
|       self.rx_done = True | ||||
|       if self.debug: print(f"ISO-TP: RX - single frame - idx={self.rx_idx} done={self.rx_done}") | ||||
|       return | ||||
| 
 | ||||
|     # first rx_frame | ||||
|     if rx_data[0] >> 4 == 0x1: | ||||
|       self.rx_len = ((rx_data[0] & 0x0F) << 8) + rx_data[1] | ||||
|       self.rx_dat = rx_data[2:] | ||||
|       self.rx_idx = 0 | ||||
|       self.rx_done = False | ||||
|       if self.debug: print(f"ISO-TP: RX - first frame - idx={self.rx_idx} done={self.rx_done}") | ||||
|       if self.debug: print(f"ISO-TP: TX - flow control continue") | ||||
|       # send flow control message (send all bytes) | ||||
|       msg = b"\x30\x00\x00".ljust(8, b"\x00") | ||||
|       self.can_tx_queue.put(msg) | ||||
|       return | ||||
|      | ||||
|     # consecutive rx frame | ||||
|     if rx_data[0] >> 4 == 0x2: | ||||
|       assert self.rx_done == False, "isotp - rx: consecutive frame with no active frame" | ||||
|       self.rx_idx += 1 | ||||
|       assert self.rx_idx & 0xF == rx_data[0] & 0xF, "isotp - rx: invalid consecutive frame index" | ||||
|       rx_size = self.rx_len - len(self.rx_dat) | ||||
|       self.rx_dat += rx_data[1:1+min(rx_size, 7)] | ||||
|       if self.rx_len == len(self.rx_dat): | ||||
|         self.rx_done = True | ||||
|       if self.debug: print(f"ISO-TP: RX - consecutive frame - idx={self.rx_idx} done={self.rx_done}") | ||||
|       return | ||||
| 
 | ||||
|     # flow control | ||||
|     if rx_data[0] >> 4 == 0x3: | ||||
|       assert self.tx_done == False, "isotp - rx: flow control with no active frame" | ||||
|       assert rx_data[0] != 0x32, "isotp - rx: flow-control overflow/abort" | ||||
|       assert rx_data[0] == 0x30 or rx_data[0] == 0x31, "isotp - rx: flow-control transfer state indicator invalid" | ||||
|       if rx_data[0] == 0x30: | ||||
|         if self.debug: print("ISO-TP: RX - flow control continue") | ||||
|         delay_ts = rx_data[2] & 0x7F | ||||
|         # scale is 1 milliseconds if first bit == 0, 100 micro seconds if first bit == 1 | ||||
|         delay_div = 1000. if rx_data[2] & 0x80 == 0 else 10000. | ||||
|         # first frame = 6 bytes, each consecutive frame = 7 bytes | ||||
|         start = 6 + self.tx_idx * 7 | ||||
|         count = rx_data[1] | ||||
|         end = start + count * 7 if count > 0 else self.tx_len | ||||
|         for i in range(start, end, 7): | ||||
|           if delay_ts > 0 and i > start: | ||||
|             delay_s = delay_ts / delay_div | ||||
|             if self.debug: print(f"ISO-TP: TX - delay - seconds={delay_s}") | ||||
|             time.sleep(delay_s) | ||||
|           self.tx_idx += 1 | ||||
|           # consecutive tx frames | ||||
|           msg = (bytes([0x20 | (self.tx_idx & 0xF)]) + self.tx_dat[i:i+7]).ljust(8, b"\x00") | ||||
|           self.can_tx_queue.put(msg) | ||||
|         if end >= self.tx_len: | ||||
|           self.tx_done = True | ||||
|         if self.debug: print(f"ISO-TP: TX - consecutive frame - idx={self.tx_idx} done={self.tx_done}") | ||||
|       elif rx_data[0] == 0x31: | ||||
|         # wait (do nothing until next flow control message) | ||||
|         if self.debug: print("ISO-TP: TX - flow control wait") | ||||
| 
 | ||||
| class UdsClient(): | ||||
|   def __init__(self, panda, tx_addr: int, rx_addr: int=None, bus: int=0, timeout: float=10, debug: bool=False): | ||||
|     self.panda = panda | ||||
|     self.bus = bus | ||||
|     self.tx_addr = tx_addr | ||||
|     if rx_addr == None: | ||||
|       if tx_addr < 0xFFF8: | ||||
|         # standard 11 bit response addr (add 8) | ||||
|         self.rx_addr = tx_addr+8 | ||||
|       elif tx_addr > 0x10000000 and tx_addr < 0xFFFFFFFF: | ||||
|         # standard 29 bit response addr (flip last two bytes) | ||||
|         self.rx_addr = (tx_addr & 0xFFFF0000) + (tx_addr<<8 & 0xFF00) + (tx_addr>>8 & 0xFF) | ||||
|       else: | ||||
|         raise ValueError("invalid tx_addr: {}".format(tx_addr)) | ||||
| 
 | ||||
|     self.can_tx_queue = Queue() | ||||
|     self.can_rx_queue = Queue() | ||||
|     self.timeout = timeout | ||||
|     self.debug = debug | ||||
| 
 | ||||
|     self.can_thread = Thread(target=self._can_thread, args=(self.debug,)) | ||||
|     self.can_thread.daemon = True | ||||
|     self.can_thread.start() | ||||
| 
 | ||||
|   def _can_thread(self, debug: bool=False): | ||||
|     try: | ||||
|       # allow all output | ||||
|       self.panda.set_safety_mode(0x1337) | ||||
|       # clear tx buffer | ||||
|       self.panda.can_clear(self.bus) | ||||
|       # clear rx buffer | ||||
|       self.panda.can_clear(0xFFFF) | ||||
| 
 | ||||
|       while True: | ||||
|         # send | ||||
|         while not self.can_tx_queue.empty(): | ||||
|           msg = self.can_tx_queue.get(block=False) | ||||
|           if debug: print("CAN-TX: {} - {}".format(hex(self.tx_addr), hexlify(msg))) | ||||
|           self.panda.can_send(self.tx_addr, msg, self.bus) | ||||
| 
 | ||||
|         # receive | ||||
|         msgs = self.panda.can_recv() | ||||
|         for rx_addr, rx_ts, rx_data, rx_bus in msgs: | ||||
|           if rx_bus != self.bus or rx_addr != self.rx_addr or len(rx_data) == 0: | ||||
|             continue | ||||
|           if debug: print("CAN-RX: {} - {}".format(hex(self.rx_addr), hexlify(rx_data))) | ||||
|           self.can_rx_queue.put(rx_data) | ||||
|         else: | ||||
|           time.sleep(0.01) | ||||
|     finally: | ||||
|       self.panda.close() | ||||
| 
 | ||||
|   # generic uds request | ||||
|   def _uds_request(self, service_type: SERVICE_TYPE, subfunction: int=None, data: bytes=None) -> bytes: | ||||
|     req = bytes([service_type]) | ||||
|     if subfunction is not None: | ||||
|       req += bytes([subfunction]) | ||||
|     if data is not None: | ||||
|       req += data | ||||
| 
 | ||||
|     # send request, wait for response | ||||
|     isotp_msg = IsoTpMessage(self.can_tx_queue, self.can_rx_queue, self.timeout, self.debug) | ||||
|     isotp_msg.send(req) | ||||
|     while True: | ||||
|       resp = isotp_msg.recv() | ||||
|       resp_sid = resp[0] if len(resp) > 0 else None | ||||
| 
 | ||||
|       # negative response | ||||
|       if resp_sid == 0x7F: | ||||
|         service_id = resp[1] if len(resp) > 1 else -1 | ||||
|         try: | ||||
|           service_desc = SERVICE_TYPE(service_id).name | ||||
|         except Exception: | ||||
|           service_desc = 'NON_STANDARD_SERVICE' | ||||
|         error_code = resp[2] if len(resp) > 2 else -1 | ||||
|         try: | ||||
|           error_desc = _negative_response_codes[error_code] | ||||
|         except Exception: | ||||
|           error_desc = resp[3:] | ||||
|         # wait for another message if response pending | ||||
|         if error_code == 0x78: | ||||
|           if self.debug: print("UDS-RX: response pending") | ||||
|           continue | ||||
|         raise NegativeResponseError('{} - {}'.format(service_desc, error_desc), service_id, error_code) | ||||
| 
 | ||||
|       # positive response | ||||
|       if service_type+0x40 != resp_sid: | ||||
|         resp_sid_hex = hex(resp_sid) if resp_sid is not None else None | ||||
|         raise InvalidServiceIdError('invalid response service id: {}'.format(resp_sid_hex)) | ||||
| 
 | ||||
|       if subfunction is not None: | ||||
|         resp_sfn = resp[1] if len(resp) > 1 else None | ||||
|         if subfunction != resp_sfn: | ||||
|           resp_sfn_hex = hex(resp_sfn) if resp_sfn is not None else None | ||||
|           raise InvalidSubFunctioneError('invalid response subfunction: {}'.format(hex(resp_sfn_hex))) | ||||
| 
 | ||||
|       # return data (exclude service id and sub-function id) | ||||
|       return resp[(1 if subfunction is None else 2):] | ||||
| 
 | ||||
|   # services | ||||
|   def diagnostic_session_control(self, session_type: SESSION_TYPE): | ||||
|     self._uds_request(SERVICE_TYPE.DIAGNOSTIC_SESSION_CONTROL, subfunction=session_type) | ||||
| 
 | ||||
|   def ecu_reset(self, reset_type: RESET_TYPE): | ||||
|     resp = self._uds_request(SERVICE_TYPE.ECU_RESET, subfunction=reset_type) | ||||
|     power_down_time = None | ||||
|     if reset_type == RESET_TYPE.ENABLE_RAPID_POWER_SHUTDOWN: | ||||
|       power_down_time = resp[0] | ||||
|       return power_down_time | ||||
| 
 | ||||
|   def security_access(self, access_type: ACCESS_TYPE, security_key: bytes=None): | ||||
|     request_seed = access_type % 2 != 0 | ||||
|     if request_seed and security_key is not None: | ||||
|       raise ValueError('security_key not allowed') | ||||
|     if not request_seed and security_key is None: | ||||
|       raise ValueError('security_key is missing') | ||||
|     resp = self._uds_request(SERVICE_TYPE.SECURITY_ACCESS, subfunction=access_type, data=security_key) | ||||
|     if request_seed: | ||||
|       security_seed = resp | ||||
|       return security_seed | ||||
| 
 | ||||
|   def communication_control(self, control_type: CONTROL_TYPE, message_type: MESSAGE_TYPE): | ||||
|     data = bytes([message_type]) | ||||
|     self._uds_request(SERVICE_TYPE.COMMUNICATION_CONTROL, subfunction=control_type, data=data) | ||||
| 
 | ||||
|   def tester_present(self, ): | ||||
|     self._uds_request(SERVICE_TYPE.TESTER_PRESENT, subfunction=0x00) | ||||
| 
 | ||||
|   def access_timing_parameter(self, timing_parameter_type: TIMING_PARAMETER_TYPE, parameter_values: bytes=None): | ||||
|     write_custom_values = timing_parameter_type == TIMING_PARAMETER_TYPE.SET_TO_GIVEN_VALUES | ||||
|     read_values = ( | ||||
|       timing_parameter_type == TIMING_PARAMETER_TYPE.READ_CURRENTLY_ACTIVE or | ||||
|       timing_parameter_type == TIMING_PARAMETER_TYPE.READ_EXTENDED_SET | ||||
|     ) | ||||
|     if not write_custom_values and parameter_values is not None: | ||||
|       raise ValueError('parameter_values not allowed') | ||||
|     if write_custom_values and parameter_values is None: | ||||
|       raise ValueError('parameter_values is missing') | ||||
|     resp = self._uds_request(SERVICE_TYPE.ACCESS_TIMING_PARAMETER, subfunction=timing_parameter_type, data=parameter_values) | ||||
|     if read_values: | ||||
|       # TODO: parse response into values? | ||||
|       parameter_values = resp | ||||
|       return parameter_values | ||||
| 
 | ||||
|   def secured_data_transmission(self, data: bytes): | ||||
|     # TODO: split data into multiple input parameters? | ||||
|     resp = self._uds_request(SERVICE_TYPE.SECURED_DATA_TRANSMISSION, subfunction=None, data=data) | ||||
|     # TODO: parse response into multiple output values? | ||||
|     return resp | ||||
| 
 | ||||
|   def control_dtc_setting(self, dtc_setting_type: DTC_SETTING_TYPE): | ||||
|     self._uds_request(SERVICE_TYPE.CONTROL_DTC_SETTING, subfunction=dtc_setting_type) | ||||
| 
 | ||||
|   def response_on_event(self, response_event_type: RESPONSE_EVENT_TYPE, store_event: bool, window_time: int, event_type_record: int, service_response_record: int): | ||||
|     if store_event: | ||||
|       response_event_type |= 0x20 | ||||
|     # TODO: split record parameters into arrays | ||||
|     data = bytes([window_time, event_type_record, service_response_record]) | ||||
|     resp = self._uds_request(SERVICE_TYPE.RESPONSE_ON_EVENT, subfunction=response_event_type, data=data) | ||||
| 
 | ||||
|     if response_event_type == RESPONSE_EVENT_TYPE.REPORT_ACTIVATED_EVENTS: | ||||
|       return { | ||||
|         "num_of_activated_events": resp[0], | ||||
|         "data": resp[1:], # TODO: parse the reset of response | ||||
|       } | ||||
| 
 | ||||
|     return { | ||||
|       "num_of_identified_events": resp[0], | ||||
|       "event_window_time": resp[1], | ||||
|       "data": resp[2:], # TODO: parse the reset of response | ||||
|     } | ||||
| 
 | ||||
|   def link_control(self, link_control_type: LINK_CONTROL_TYPE, baud_rate_type: BAUD_RATE_TYPE=None): | ||||
|     if link_control_type == LINK_CONTROL_TYPE.VERIFY_BAUDRATE_TRANSITION_WITH_FIXED_BAUDRATE: | ||||
|       # baud_rate_type = BAUD_RATE_TYPE | ||||
|       data = bytes([baud_rate_type]) | ||||
|     elif link_control_type == LINK_CONTROL_TYPE.VERIFY_BAUDRATE_TRANSITION_WITH_SPECIFIC_BAUDRATE: | ||||
|       # baud_rate_type = custom value (3 bytes big-endian) | ||||
|       data = struct.pack('!I', baud_rate_type)[1:] | ||||
|     else: | ||||
|       data = None | ||||
|     self._uds_request(SERVICE_TYPE.LINK_CONTROL, subfunction=link_control_type, data=data) | ||||
| 
 | ||||
|   def read_data_by_identifier(self, data_identifier_type: DATA_IDENTIFIER_TYPE): | ||||
|     # TODO: support list of identifiers | ||||
|     data = struct.pack('!H', data_identifier_type) | ||||
|     resp = self._uds_request(SERVICE_TYPE.READ_DATA_BY_IDENTIFIER, subfunction=None, data=data) | ||||
|     resp_id = struct.unpack('!H', resp[0:2])[0] if len(resp) >= 2 else None | ||||
|     if resp_id != data_identifier_type: | ||||
|       raise ValueError('invalid response data identifier: {}'.format(hex(resp_id))) | ||||
|     return resp[2:] | ||||
| 
 | ||||
|   def read_memory_by_address(self, memory_address: int, memory_size: int, memory_address_bytes: int=4, memory_size_bytes: int=1): | ||||
|     if memory_address_bytes < 1 or memory_address_bytes > 4: | ||||
|       raise ValueError('invalid memory_address_bytes: {}'.format(memory_address_bytes)) | ||||
|     if memory_size_bytes < 1 or memory_size_bytes > 4: | ||||
|       raise ValueError('invalid memory_size_bytes: {}'.format(memory_size_bytes)) | ||||
|     data = bytes([memory_size_bytes<<4 | memory_address_bytes]) | ||||
| 
 | ||||
|     if memory_address >= 1<<(memory_address_bytes*8): | ||||
|       raise ValueError('invalid memory_address: {}'.format(memory_address)) | ||||
|     data += struct.pack('!I', memory_address)[4-memory_address_bytes:] | ||||
|     if memory_size >= 1<<(memory_size_bytes*8): | ||||
|       raise ValueError('invalid memory_size: {}'.format(memory_size)) | ||||
|     data += struct.pack('!I', memory_size)[4-memory_size_bytes:] | ||||
| 
 | ||||
|     resp = self._uds_request(SERVICE_TYPE.READ_MEMORY_BY_ADDRESS, subfunction=None, data=data) | ||||
|     return resp | ||||
| 
 | ||||
|   def read_scaling_data_by_identifier(self, data_identifier_type: DATA_IDENTIFIER_TYPE): | ||||
|     data = struct.pack('!H', data_identifier_type) | ||||
|     resp = self._uds_request(SERVICE_TYPE.READ_SCALING_DATA_BY_IDENTIFIER, subfunction=None, data=data) | ||||
|     resp_id = struct.unpack('!H', resp[0:2])[0] if len(resp) >= 2 else None | ||||
|     if resp_id != data_identifier_type: | ||||
|       raise ValueError('invalid response data identifier: {}'.format(hex(resp_id))) | ||||
|     return resp[2:] # TODO: parse the response | ||||
| 
 | ||||
|   def read_data_by_periodic_identifier(self, transmission_mode_type: TRANSMISSION_MODE_TYPE, periodic_data_identifier: int): | ||||
|     # TODO: support list of identifiers | ||||
|     data = bytes([transmission_mode_type, periodic_data_identifier]) | ||||
|     self._uds_request(SERVICE_TYPE.READ_DATA_BY_PERIODIC_IDENTIFIER, subfunction=None, data=data) | ||||
| 
 | ||||
|   def dynamically_define_data_identifier(self, dynamic_definition_type: DYNAMIC_DEFINITION_TYPE, dynamic_data_identifier: int, source_definitions: List[DynamicSourceDefinition], memory_address_bytes: int=4, memory_size_bytes: int=1): | ||||
|     if memory_address_bytes < 1 or memory_address_bytes > 4: | ||||
|       raise ValueError('invalid memory_address_bytes: {}'.format(memory_address_bytes)) | ||||
|     if memory_size_bytes < 1 or memory_size_bytes > 4: | ||||
|       raise ValueError('invalid memory_size_bytes: {}'.format(memory_size_bytes)) | ||||
| 
 | ||||
|     data = struct.pack('!H', dynamic_data_identifier) | ||||
|     if dynamic_definition_type == DYNAMIC_DEFINITION_TYPE.DEFINE_BY_IDENTIFIER: | ||||
|       for s in source_definitions: | ||||
|         data += struct.pack('!H', s["data_identifier"]) + bytes([s["position"], s["memory_size"]]) | ||||
|     elif dynamic_definition_type == DYNAMIC_DEFINITION_TYPE.DEFINE_BY_MEMORY_ADDRESS: | ||||
|       data += bytes([memory_size_bytes<<4 | memory_address_bytes]) | ||||
|       for s in source_definitions: | ||||
|         if s["memory_address"] >= 1<<(memory_address_bytes*8): | ||||
|           raise ValueError('invalid memory_address: {}'.format(s["memory_address"])) | ||||
|         data += struct.pack('!I', s["memory_address"])[4-memory_address_bytes:] | ||||
|         if s["memory_size"] >= 1<<(memory_size_bytes*8): | ||||
|           raise ValueError('invalid memory_size: {}'.format(s["memory_size"])) | ||||
|         data += struct.pack('!I', s["memory_size"])[4-memory_size_bytes:] | ||||
|     elif dynamic_definition_type == DYNAMIC_DEFINITION_TYPE.CLEAR_DYNAMICALLY_DEFINED_DATA_IDENTIFIER: | ||||
|       pass | ||||
|     else: | ||||
|       raise ValueError('invalid dynamic identifier type: {}'.format(hex(dynamic_definition_type))) | ||||
|     self._uds_request(SERVICE_TYPE.DYNAMICALLY_DEFINE_DATA_IDENTIFIER, subfunction=dynamic_definition_type, data=data) | ||||
| 
 | ||||
|   def write_data_by_identifier(self, data_identifier_type: DATA_IDENTIFIER_TYPE, data_record: bytes): | ||||
|     data = struct.pack('!H', data_identifier_type) + data_record | ||||
|     resp = self._uds_request(SERVICE_TYPE.WRITE_DATA_BY_IDENTIFIER, subfunction=None, data=data) | ||||
|     resp_id = struct.unpack('!H', resp[0:2])[0] if len(resp) >= 2 else None | ||||
|     if resp_id != data_identifier_type: | ||||
|       raise ValueError('invalid response data identifier: {}'.format(hex(resp_id))) | ||||
| 
 | ||||
|   def write_memory_by_address(self, memory_address: int, memory_size: int, data_record: bytes, memory_address_bytes: int=4, memory_size_bytes: int=1): | ||||
|     if memory_address_bytes < 1 or memory_address_bytes > 4: | ||||
|       raise ValueError('invalid memory_address_bytes: {}'.format(memory_address_bytes)) | ||||
|     if memory_size_bytes < 1 or memory_size_bytes > 4: | ||||
|       raise ValueError('invalid memory_size_bytes: {}'.format(memory_size_bytes)) | ||||
|     data = bytes([memory_size_bytes<<4 | memory_address_bytes]) | ||||
| 
 | ||||
|     if memory_address >= 1<<(memory_address_bytes*8): | ||||
|       raise ValueError('invalid memory_address: {}'.format(memory_address)) | ||||
|     data += struct.pack('!I', memory_address)[4-memory_address_bytes:] | ||||
|     if memory_size >= 1<<(memory_size_bytes*8): | ||||
|       raise ValueError('invalid memory_size: {}'.format(memory_size)) | ||||
|     data += struct.pack('!I', memory_size)[4-memory_size_bytes:] | ||||
| 
 | ||||
|     data += data_record | ||||
|     self._uds_request(SERVICE_TYPE.WRITE_MEMORY_BY_ADDRESS, subfunction=0x00, data=data) | ||||
| 
 | ||||
|   def clear_diagnostic_information(self, dtc_group_type: DTC_GROUP_TYPE): | ||||
|     data = struct.pack('!I', dtc_group_type)[1:] # 3 bytes | ||||
|     self._uds_request(SERVICE_TYPE.CLEAR_DIAGNOSTIC_INFORMATION, subfunction=None, data=data) | ||||
| 
 | ||||
|   def read_dtc_information(self, dtc_report_type: DTC_REPORT_TYPE, dtc_status_mask_type: DTC_STATUS_MASK_TYPE=DTC_STATUS_MASK_TYPE.ALL, dtc_severity_mask_type: DTC_SEVERITY_MASK_TYPE=DTC_SEVERITY_MASK_TYPE.ALL, dtc_mask_record: int=0xFFFFFF, dtc_snapshot_record_num: int=0xFF, dtc_extended_record_num: int=0xFF): | ||||
|     data = b'' | ||||
|     # dtc_status_mask_type | ||||
|     if dtc_report_type == DTC_REPORT_TYPE.NUMBER_OF_DTC_BY_STATUS_MASK or \ | ||||
|       dtc_report_type == DTC_REPORT_TYPE.DTC_BY_STATUS_MASK or \ | ||||
|       dtc_report_type == DTC_REPORT_TYPE.MIRROR_MEMORY_DTC_BY_STATUS_MASK or \ | ||||
|       dtc_report_type == DTC_REPORT_TYPE.NUMBER_OF_MIRROR_MEMORY_DTC_BY_STATUS_MASK or \ | ||||
|       dtc_report_type == DTC_REPORT_TYPE.NUMBER_OF_EMISSIONS_RELATED_OBD_DTC_BY_STATUS_MASK or \ | ||||
|       dtc_report_type == DTC_REPORT_TYPE.EMISSIONS_RELATED_OBD_DTC_BY_STATUS_MASK: | ||||
|       data += bytes([dtc_status_mask_type]) | ||||
|     # dtc_mask_record | ||||
|     if dtc_report_type == DTC_REPORT_TYPE.DTC_SNAPSHOT_IDENTIFICATION or \ | ||||
|       dtc_report_type == DTC_REPORT_TYPE.DTC_SNAPSHOT_RECORD_BY_DTC_NUMBER or \ | ||||
|       dtc_report_type == DTC_REPORT_TYPE.DTC_EXTENDED_DATA_RECORD_BY_DTC_NUMBER or \ | ||||
|       dtc_report_type == DTC_REPORT_TYPE.MIRROR_MEMORY_DTC_EXTENDED_DATA_RECORD_BY_DTC_NUMBER or \ | ||||
|       dtc_report_type == DTC_REPORT_TYPE.SEVERITY_INFORMATION_OF_DTC: | ||||
|       data += struct.pack('!I', dtc_mask_record)[1:] # 3 bytes | ||||
|     # dtc_snapshot_record_num | ||||
|     if dtc_report_type == DTC_REPORT_TYPE.DTC_SNAPSHOT_IDENTIFICATION or \ | ||||
|       dtc_report_type == DTC_REPORT_TYPE.DTC_SNAPSHOT_RECORD_BY_DTC_NUMBER or \ | ||||
|       dtc_report_type == DTC_REPORT_TYPE.DTC_SNAPSHOT_RECORD_BY_RECORD_NUMBER: | ||||
|       data += ord(dtc_snapshot_record_num) | ||||
|     # dtc_extended_record_num | ||||
|     if dtc_report_type == DTC_REPORT_TYPE.DTC_EXTENDED_DATA_RECORD_BY_DTC_NUMBER or \ | ||||
|       dtc_report_type == DTC_REPORT_TYPE.MIRROR_MEMORY_DTC_EXTENDED_DATA_RECORD_BY_DTC_NUMBER: | ||||
|       data += bytes([dtc_extended_record_num]) | ||||
|     # dtc_severity_mask_type | ||||
|     if dtc_report_type == DTC_REPORT_TYPE.NUMBER_OF_DTC_BY_SEVERITY_MASK_RECORD or \ | ||||
|       dtc_report_type == DTC_REPORT_TYPE.DTC_BY_SEVERITY_MASK_RECORD: | ||||
|       data += bytes([dtc_severity_mask_type, dtc_status_mask_type]) | ||||
|      | ||||
|     resp = self._uds_request(SERVICE_TYPE.READ_DTC_INFORMATION, subfunction=dtc_report_type, data=data) | ||||
| 
 | ||||
|     # TODO: parse response | ||||
|     return resp | ||||
| 
 | ||||
|   def input_output_control_by_identifier(self, data_identifier_type: DATA_IDENTIFIER_TYPE, control_parameter_type: CONTROL_PARAMETER_TYPE, control_option_record: bytes, control_enable_mask_record: bytes=b''): | ||||
|     data = struct.pack('!H', data_identifier_type) + bytes([control_parameter_type]) + control_option_record + control_enable_mask_record | ||||
|     resp = self._uds_request(SERVICE_TYPE.INPUT_OUTPUT_CONTROL_BY_IDENTIFIER, subfunction=None, data=data) | ||||
|     resp_id = struct.unpack('!H', resp[0:2])[0] if len(resp) >= 2 else None | ||||
|     if resp_id != data_identifier_type: | ||||
|       raise ValueError('invalid response data identifier: {}'.format(hex(resp_id))) | ||||
|     return resp[2:] | ||||
| 
 | ||||
|   def routine_control(self, routine_control_type: ROUTINE_CONTROL_TYPE, routine_identifier_type: ROUTINE_IDENTIFIER_TYPE, routine_option_record: bytes=b''): | ||||
|     data = struct.pack('!H', routine_identifier_type) + routine_option_record | ||||
|     resp = self._uds_request(SERVICE_TYPE.ROUTINE_CONTROL, subfunction=routine_control_type, data=data) | ||||
|     resp_id = struct.unpack('!H', resp[0:2])[0] if len(resp) >= 2 else None | ||||
|     if resp_id != routine_identifier_type: | ||||
|       raise ValueError('invalid response routine identifier: {}'.format(hex(resp_id))) | ||||
|     return resp[2:] | ||||
| 
 | ||||
|   def request_download(self, memory_address: int, memory_size: int, memory_address_bytes: int=4, memory_size_bytes: int=4, data_format: int=0x00): | ||||
|     data = bytes([data_format]) | ||||
| 
 | ||||
|     if memory_address_bytes < 1 or memory_address_bytes > 4: | ||||
|       raise ValueError('invalid memory_address_bytes: {}'.format(memory_address_bytes)) | ||||
|     if memory_size_bytes < 1 or memory_size_bytes > 4: | ||||
|       raise ValueError('invalid memory_size_bytes: {}'.format(memory_size_bytes)) | ||||
|     data += bytes([memory_size_bytes<<4 | memory_address_bytes]) | ||||
| 
 | ||||
|     if memory_address >= 1<<(memory_address_bytes*8): | ||||
|       raise ValueError('invalid memory_address: {}'.format(memory_address)) | ||||
|     data += struct.pack('!I', memory_address)[4-memory_address_bytes:] | ||||
|     if memory_size >= 1<<(memory_size_bytes*8): | ||||
|       raise ValueError('invalid memory_size: {}'.format(memory_size)) | ||||
|     data += struct.pack('!I', memory_size)[4-memory_size_bytes:] | ||||
| 
 | ||||
|     resp = self._uds_request(SERVICE_TYPE.REQUEST_DOWNLOAD, subfunction=None, data=data) | ||||
|     max_num_bytes_len = resp[0] >> 4 if len(resp) > 0 else None | ||||
|     if max_num_bytes_len >= 1 and max_num_bytes_len <= 4: | ||||
|       max_num_bytes = struct.unpack('!I', (b"\x00"*(4-max_num_bytes_len))+resp[1:max_num_bytes_len+1])[0] | ||||
|     else: | ||||
|       raise ValueError('invalid max_num_bytes_len: {}'.format(max_num_bytes_len)) | ||||
| 
 | ||||
|     return max_num_bytes # max number of bytes per transfer data request | ||||
| 
 | ||||
|   def request_upload(self, memory_address: int, memory_size: int, memory_address_bytes: int=4, memory_size_bytes: int=4, data_format: int=0x00): | ||||
|     data = bytes([data_format]) | ||||
| 
 | ||||
|     if memory_address_bytes < 1 or memory_address_bytes > 4: | ||||
|       raise ValueError('invalid memory_address_bytes: {}'.format(memory_address_bytes)) | ||||
|     if memory_size_bytes < 1 or memory_size_bytes > 4: | ||||
|       raise ValueError('invalid memory_size_bytes: {}'.format(memory_size_bytes)) | ||||
|     data += bytes([memory_size_bytes<<4 | memory_address_bytes]) | ||||
| 
 | ||||
|     if memory_address >= 1<<(memory_address_bytes*8): | ||||
|       raise ValueError('invalid memory_address: {}'.format(memory_address)) | ||||
|     data += struct.pack('!I', memory_address)[4-memory_address_bytes:] | ||||
|     if memory_size >= 1<<(memory_size_bytes*8): | ||||
|       raise ValueError('invalid memory_size: {}'.format(memory_size)) | ||||
|     data += struct.pack('!I', memory_size)[4-memory_size_bytes:] | ||||
| 
 | ||||
|     resp = self._uds_request(SERVICE_TYPE.REQUEST_UPLOAD, subfunction=None, data=data) | ||||
|     max_num_bytes_len = resp[0] >> 4 if len(resp) > 0 else None | ||||
|     if max_num_bytes_len >= 1 and max_num_bytes_len <= 4: | ||||
|       max_num_bytes = struct.unpack('!I', (b"\x00"*(4-max_num_bytes_len))+resp[1:max_num_bytes_len+1])[0] | ||||
|     else: | ||||
|       raise ValueError('invalid max_num_bytes_len: {}'.format(max_num_bytes_len)) | ||||
| 
 | ||||
|     return max_num_bytes # max number of bytes per transfer data request | ||||
| 
 | ||||
|   def transfer_data(self, block_sequence_count: int, data: bytes=b''): | ||||
|     data = bytes([block_sequence_count]) + data | ||||
|     resp = self._uds_request(SERVICE_TYPE.TRANSFER_DATA, subfunction=None, data=data) | ||||
|     resp_id = resp[0] if len(resp) > 0 else None | ||||
|     if resp_id != block_sequence_count: | ||||
|       raise ValueError('invalid block_sequence_count: {}'.format(resp_id)) | ||||
|     return resp[1:] | ||||
| 
 | ||||
|   def request_transfer_exit(self): | ||||
|     self._uds_request(SERVICE_TYPE.REQUEST_TRANSFER_EXIT, subfunction=None) | ||||
| @ -1,7 +1,11 @@ | ||||
| libusb1 == 1.6.6 | ||||
| hexdump | ||||
| pycrypto | ||||
| tqdm | ||||
| numpy==1.17.2 | ||||
| hexdump>=3.3 | ||||
| pycrypto==2.6.1 | ||||
| tqdm>=4.14.0 | ||||
| nose | ||||
| parameterized | ||||
| requests | ||||
| flake8==3.7.8 | ||||
| pylint==2.4.2 | ||||
| cffi==1.11.4 | ||||
|  | ||||
| @ -0,0 +1,17 @@ | ||||
| #!/usr/bin/env python | ||||
| import os | ||||
| import sys | ||||
| import time | ||||
| 
 | ||||
| sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) | ||||
| from panda import Panda | ||||
| 
 | ||||
| power = 0 | ||||
| if __name__ == "__main__": | ||||
|   p = Panda() | ||||
|   while True: | ||||
|     p.set_fan_power(power) | ||||
|     time.sleep(5) | ||||
|     print("Power: ", power, "RPM: ", str(p.get_fan_rpm())) | ||||
|     power += 10 | ||||
|     power %=100 | ||||
| @ -1,17 +1,16 @@ | ||||
| #!/usr/bin/env python3 | ||||
| import time | ||||
| from panda import Panda | ||||
| 
 | ||||
| p = Panda() | ||||
| p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) | ||||
| p.set_gmlan(bus=2) | ||||
| #p.can_send(0xaaa, "\x00\x00", bus=3) | ||||
| #p.can_send(0xaaa, b"\x00\x00", bus=3) | ||||
| last_add = None | ||||
| while 1: | ||||
|   ret = p.can_recv() | ||||
|   if len(ret) > 0: | ||||
|     add = ret[0][0] | ||||
|     if last_add is not None and add != last_add+1: | ||||
|       print("MISS %d %d" % (last_add, add)) | ||||
|     if last_add is not None and add != last_add + 1: | ||||
|       print("MISS: ", last_add, add) | ||||
|     last_add = add | ||||
|     print(ret) | ||||
|  | ||||
| @ -0,0 +1,17 @@ | ||||
| #!/usr/bin/env python | ||||
| import os | ||||
| import sys | ||||
| import time | ||||
| 
 | ||||
| sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) | ||||
| from panda import Panda | ||||
| 
 | ||||
| power = 0 | ||||
| if __name__ == "__main__": | ||||
|   p = Panda() | ||||
|   while True: | ||||
|     p.set_ir_power(power) | ||||
|     print("Power: ", str(power)) | ||||
|     time.sleep(1) | ||||
|     power += 10 | ||||
|     power %=100 | ||||
| @ -0,0 +1,585 @@ | ||||
| [MASTER] | ||||
| 
 | ||||
| # A comma-separated list of package or module names from where C extensions may | ||||
| # be loaded. Extensions are loading into the active Python interpreter and may | ||||
| # run arbitrary code | ||||
| extension-pkg-whitelist=scipy | ||||
| 
 | ||||
| # Add files or directories to the blacklist. They should be base names, not | ||||
| # paths. | ||||
| ignore=CVS | ||||
| 
 | ||||
| # Add files or directories matching the regex patterns to the blacklist. The | ||||
| # regex matches against base names, not paths. | ||||
| ignore-patterns= | ||||
| 
 | ||||
| # Python code to execute, usually for sys.path manipulation such as | ||||
| # pygtk.require(). | ||||
| #init-hook= | ||||
| 
 | ||||
| # Use multiple processes to speed up Pylint. | ||||
| jobs=4 | ||||
| 
 | ||||
| # List of plugins (as comma separated values of python modules names) to load, | ||||
| # usually to register additional checkers. | ||||
| load-plugins= | ||||
| 
 | ||||
| # Pickle collected data for later comparisons. | ||||
| persistent=yes | ||||
| 
 | ||||
| # Specify a configuration file. | ||||
| #rcfile= | ||||
| 
 | ||||
| # When enabled, pylint would attempt to guess common misconfiguration and emit | ||||
| # user-friendly hints instead of false-positive error messages | ||||
| suggestion-mode=yes | ||||
| 
 | ||||
| # Allow loading of arbitrary C extensions. Extensions are imported into the | ||||
| # active Python interpreter and may run arbitrary code. | ||||
| unsafe-load-any-extension=no | ||||
| 
 | ||||
| 
 | ||||
| [MESSAGES CONTROL] | ||||
| 
 | ||||
| # Only show warnings with the listed confidence levels. Leave empty to show | ||||
| # all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED | ||||
| confidence= | ||||
| 
 | ||||
| # Disable the message, report, category or checker with the given id(s). You | ||||
| # can either give multiple identifiers separated by comma (,) or put this | ||||
| # option multiple times (only on the command line, not in the configuration | ||||
| # file where it should appear only once).You can also use "--disable=all" to | ||||
| # disable everything first and then reenable specific checks. For example, if | ||||
| # you want to run only the similarities checker, you can use "--disable=all | ||||
| # --enable=similarities". If you want to run only the classes checker, but have | ||||
| # no Warning level messages displayed, use"--disable=all --enable=classes | ||||
| # --disable=W" | ||||
| disable=print-statement, | ||||
|         parameter-unpacking, | ||||
|         unpacking-in-except, | ||||
|         old-raise-syntax, | ||||
|         backtick, | ||||
|         long-suffix, | ||||
|         old-ne-operator, | ||||
|         old-octal-literal, | ||||
|         import-star-module-level, | ||||
|         non-ascii-bytes-literal, | ||||
|         raw-checker-failed, | ||||
|         bad-inline-option, | ||||
|         locally-disabled, | ||||
|         locally-enabled, | ||||
|         file-ignored, | ||||
|         suppressed-message, | ||||
|         useless-suppression, | ||||
|         deprecated-pragma, | ||||
|         apply-builtin, | ||||
|         basestring-builtin, | ||||
|         buffer-builtin, | ||||
|         cmp-builtin, | ||||
|         coerce-builtin, | ||||
|         execfile-builtin, | ||||
|         file-builtin, | ||||
|         long-builtin, | ||||
|         raw_input-builtin, | ||||
|         reduce-builtin, | ||||
|         standarderror-builtin, | ||||
|         unicode-builtin, | ||||
|         xrange-builtin, | ||||
|         coerce-method, | ||||
|         delslice-method, | ||||
|         getslice-method, | ||||
|         setslice-method, | ||||
|         no-absolute-import, | ||||
|         old-division, | ||||
|         dict-iter-method, | ||||
|         dict-view-method, | ||||
|         next-method-called, | ||||
|         metaclass-assignment, | ||||
|         indexing-exception, | ||||
|         raising-string, | ||||
|         reload-builtin, | ||||
|         oct-method, | ||||
|         hex-method, | ||||
|         nonzero-method, | ||||
|         cmp-method, | ||||
|         input-builtin, | ||||
|         round-builtin, | ||||
|         intern-builtin, | ||||
|         unichr-builtin, | ||||
|         map-builtin-not-iterating, | ||||
|         zip-builtin-not-iterating, | ||||
|         range-builtin-not-iterating, | ||||
|         filter-builtin-not-iterating, | ||||
|         using-cmp-argument, | ||||
|         eq-without-hash, | ||||
|         div-method, | ||||
|         idiv-method, | ||||
|         rdiv-method, | ||||
|         exception-message-attribute, | ||||
|         invalid-str-codec, | ||||
|         sys-max-int, | ||||
|         bad-python3-import, | ||||
|         deprecated-string-function, | ||||
|         deprecated-str-translate-call, | ||||
|         deprecated-itertools-function, | ||||
|         deprecated-types-field, | ||||
|         next-method-defined, | ||||
|         dict-items-not-iterating, | ||||
|         dict-keys-not-iterating, | ||||
|         dict-values-not-iterating, | ||||
|         bad-indentation, | ||||
|         line-too-long, | ||||
|         missing-docstring, | ||||
|         multiple-statements, | ||||
|         bad-continuation, | ||||
|         invalid-name, | ||||
|         too-many-arguments, | ||||
|         too-many-locals, | ||||
|         superfluous-parens, | ||||
|         bad-whitespace, | ||||
|         too-many-instance-attributes, | ||||
|         wrong-import-position, | ||||
|         ungrouped-imports, | ||||
|         wrong-import-order, | ||||
|         protected-access, | ||||
|         trailing-whitespace, | ||||
|         too-many-branches, | ||||
|         too-few-public-methods, | ||||
|         too-many-statements, | ||||
|         trailing-newlines, | ||||
|         attribute-defined-outside-init, | ||||
|         too-many-return-statements, | ||||
|         too-many-public-methods, | ||||
|         unused-argument, | ||||
|         old-style-class, | ||||
|         no-init, | ||||
|         len-as-condition, | ||||
|         unneeded-not, | ||||
|         no-self-use, | ||||
|         multiple-imports, | ||||
|         no-else-return, | ||||
|         logging-not-lazy, | ||||
|         fixme, | ||||
|         redefined-outer-name, | ||||
|         unused-variable, | ||||
|         unsubscriptable-object, | ||||
|         expression-not-assigned, | ||||
|         too-many-boolean-expressions, | ||||
|         consider-using-ternary, | ||||
|         invalid-unary-operand-type, | ||||
|         relative-import, | ||||
|         deprecated-lambda | ||||
| 
 | ||||
| 
 | ||||
| # Enable the message, report, category or checker with the given id(s). You can | ||||
| # either give multiple identifier separated by comma (,) or put this option | ||||
| # multiple time (only on the command line, not in the configuration file where | ||||
| # it should appear only once). See also the "--disable" option for examples. | ||||
| enable=c-extension-no-member | ||||
| 
 | ||||
| 
 | ||||
| [REPORTS] | ||||
| 
 | ||||
| # Python expression which should return a note less than 10 (10 is the highest | ||||
| # note). You have access to the variables errors warning, statement which | ||||
| # respectively contain the number of errors / warnings messages and the total | ||||
| # number of statements analyzed. This is used by the global evaluation report | ||||
| # (RP0004). | ||||
| evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) | ||||
| 
 | ||||
| # Template used to display messages. This is a python new-style format string | ||||
| # used to format the message information. See doc for all details | ||||
| #msg-template= | ||||
| 
 | ||||
| # Set the output format. Available formats are text, parseable, colorized, json | ||||
| # and msvs (visual studio).You can also give a reporter class, eg | ||||
| # mypackage.mymodule.MyReporterClass. | ||||
| output-format=text | ||||
| 
 | ||||
| # Tells whether to display a full report or only the messages | ||||
| reports=no | ||||
| 
 | ||||
| # Activate the evaluation score. | ||||
| score=yes | ||||
| 
 | ||||
| 
 | ||||
| [REFACTORING] | ||||
| 
 | ||||
| # Maximum number of nested blocks for function / method body | ||||
| max-nested-blocks=5 | ||||
| 
 | ||||
| # Complete name of functions that never returns. When checking for | ||||
| # inconsistent-return-statements if a never returning function is called then | ||||
| # it will be considered as an explicit return statement and no message will be | ||||
| # printed. | ||||
| never-returning-functions=optparse.Values,sys.exit | ||||
| 
 | ||||
| 
 | ||||
| [LOGGING] | ||||
| 
 | ||||
| # Logging modules to check that the string format arguments are in logging | ||||
| # function parameter format | ||||
| logging-modules=logging | ||||
| 
 | ||||
| 
 | ||||
| [SPELLING] | ||||
| 
 | ||||
| # Limits count of emitted suggestions for spelling mistakes | ||||
| max-spelling-suggestions=4 | ||||
| 
 | ||||
| # Spelling dictionary name. Available dictionaries: none. To make it working | ||||
| # install python-enchant package. | ||||
| spelling-dict= | ||||
| 
 | ||||
| # List of comma separated words that should not be checked. | ||||
| spelling-ignore-words= | ||||
| 
 | ||||
| # A path to a file that contains private dictionary; one word per line. | ||||
| spelling-private-dict-file= | ||||
| 
 | ||||
| # Tells whether to store unknown words to indicated private dictionary in | ||||
| # --spelling-private-dict-file option instead of raising a message. | ||||
| spelling-store-unknown-words=no | ||||
| 
 | ||||
| 
 | ||||
| [MISCELLANEOUS] | ||||
| 
 | ||||
| # List of note tags to take in consideration, separated by a comma. | ||||
| notes=FIXME, | ||||
|       XXX, | ||||
|       TODO | ||||
| 
 | ||||
| 
 | ||||
| [SIMILARITIES] | ||||
| 
 | ||||
| # Ignore comments when computing similarities. | ||||
| ignore-comments=yes | ||||
| 
 | ||||
| # Ignore docstrings when computing similarities. | ||||
| ignore-docstrings=yes | ||||
| 
 | ||||
| # Ignore imports when computing similarities. | ||||
| ignore-imports=no | ||||
| 
 | ||||
| # Minimum lines number of a similarity. | ||||
| min-similarity-lines=4 | ||||
| 
 | ||||
| 
 | ||||
| [TYPECHECK] | ||||
| 
 | ||||
| # List of decorators that produce context managers, such as | ||||
| # contextlib.contextmanager. Add to this list to register other decorators that | ||||
| # produce valid context managers. | ||||
| contextmanager-decorators=contextlib.contextmanager | ||||
| 
 | ||||
| # List of members which are set dynamically and missed by pylint inference | ||||
| # system, and so shouldn't trigger E1101 when accessed. Python regular | ||||
| # expressions are accepted. | ||||
| generated-members=capnp.* cereal.* pygame.* zmq.* setproctitle.* smbus2.* usb1.* serial.* cv2.* | ||||
| 
 | ||||
| # Tells whether missing members accessed in mixin class should be ignored. A | ||||
| # mixin class is detected if its name ends with "mixin" (case insensitive). | ||||
| ignore-mixin-members=yes | ||||
| 
 | ||||
| # This flag controls whether pylint should warn about no-member and similar | ||||
| # checks whenever an opaque object is returned when inferring. The inference | ||||
| # can return multiple potential results while evaluating a Python object, but | ||||
| # some branches might not be evaluated, which results in partial inference. In | ||||
| # that case, it might be useful to still emit no-member and other checks for | ||||
| # the rest of the inferred objects. | ||||
| ignore-on-opaque-inference=yes | ||||
| 
 | ||||
| # List of class names for which member attributes should not be checked (useful | ||||
| # for classes with dynamically set attributes). This supports the use of | ||||
| # qualified names. | ||||
| ignored-classes=optparse.Values,thread._local,_thread._local | ||||
| 
 | ||||
| # List of module names for which member attributes should not be checked | ||||
| # (useful for modules/projects where namespaces are manipulated during runtime | ||||
| # and thus existing member attributes cannot be deduced by static analysis. It | ||||
| # supports qualified module names, as well as Unix pattern matching. | ||||
| ignored-modules=flask setproctitle usb1 flask.ext.socketio smbus2 usb1.* | ||||
| 
 | ||||
| # Show a hint with possible names when a member name was not found. The aspect | ||||
| # of finding the hint is based on edit distance. | ||||
| missing-member-hint=yes | ||||
| 
 | ||||
| # The minimum edit distance a name should have in order to be considered a | ||||
| # similar match for a missing member name. | ||||
| missing-member-hint-distance=1 | ||||
| 
 | ||||
| # The total number of similar names that should be taken in consideration when | ||||
| # showing a hint for a missing member. | ||||
| missing-member-max-choices=1 | ||||
| 
 | ||||
| 
 | ||||
| [VARIABLES] | ||||
| 
 | ||||
| # List of additional names supposed to be defined in builtins. Remember that | ||||
| # you should avoid to define new builtins when possible. | ||||
| additional-builtins= | ||||
| 
 | ||||
| # Tells whether unused global variables should be treated as a violation. | ||||
| allow-global-unused-variables=yes | ||||
| 
 | ||||
| # List of strings which can identify a callback function by name. A callback | ||||
| # name must start or end with one of those strings. | ||||
| callbacks=cb_, | ||||
|           _cb | ||||
| 
 | ||||
| # A regular expression matching the name of dummy variables (i.e. expectedly | ||||
| # not used). | ||||
| dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ | ||||
| 
 | ||||
| # Argument names that match this expression will be ignored. Default to name | ||||
| # with leading underscore | ||||
| ignored-argument-names=_.*|^ignored_|^unused_ | ||||
| 
 | ||||
| # Tells whether we should check for unused import in __init__ files. | ||||
| init-import=no | ||||
| 
 | ||||
| # List of qualified module names which can have objects that can redefine | ||||
| # builtins. | ||||
| redefining-builtins-modules=six.moves,past.builtins,future.builtins | ||||
| 
 | ||||
| 
 | ||||
| [FORMAT] | ||||
| 
 | ||||
| # Expected format of line ending, e.g. empty (any line ending), LF or CRLF. | ||||
| expected-line-ending-format= | ||||
| 
 | ||||
| # Regexp for a line that is allowed to be longer than the limit. | ||||
| ignore-long-lines=^\s*(# )?<?https?://\S+>?$ | ||||
| 
 | ||||
| # Number of spaces of indent required inside a hanging  or continued line. | ||||
| indent-after-paren=4 | ||||
| 
 | ||||
| # String used as indentation unit. This is usually "    " (4 spaces) or "\t" (1 | ||||
| # tab). | ||||
| indent-string='  ' | ||||
| 
 | ||||
| # Maximum number of characters on a single line. | ||||
| max-line-length=100 | ||||
| 
 | ||||
| # Maximum number of lines in a module | ||||
| max-module-lines=1000 | ||||
| 
 | ||||
| # List of optional constructs for which whitespace checking is disabled. `dict- | ||||
| # separator` is used to allow tabulation in dicts, etc.: {1  : 1,\n222: 2}. | ||||
| # `trailing-comma` allows a space between comma and closing bracket: (a, ). | ||||
| # `empty-line` allows space-only lines. | ||||
| no-space-check=trailing-comma, | ||||
|                dict-separator | ||||
| 
 | ||||
| # Allow the body of a class to be on the same line as the declaration if body | ||||
| # contains single statement. | ||||
| single-line-class-stmt=no | ||||
| 
 | ||||
| # Allow the body of an if to be on the same line as the test if there is no | ||||
| # else. | ||||
| single-line-if-stmt=no | ||||
| 
 | ||||
| 
 | ||||
| [BASIC] | ||||
| 
 | ||||
| # Naming style matching correct argument names | ||||
| argument-naming-style=snake_case | ||||
| 
 | ||||
| # Regular expression matching correct argument names. Overrides argument- | ||||
| # naming-style | ||||
| #argument-rgx= | ||||
| 
 | ||||
| # Naming style matching correct attribute names | ||||
| attr-naming-style=snake_case | ||||
| 
 | ||||
| # Regular expression matching correct attribute names. Overrides attr-naming- | ||||
| # style | ||||
| #attr-rgx= | ||||
| 
 | ||||
| # Bad variable names which should always be refused, separated by a comma | ||||
| bad-names=foo, | ||||
|           bar, | ||||
|           baz, | ||||
|           toto, | ||||
|           tutu, | ||||
|           tata | ||||
| 
 | ||||
| # Naming style matching correct class attribute names | ||||
| class-attribute-naming-style=any | ||||
| 
 | ||||
| # Regular expression matching correct class attribute names. Overrides class- | ||||
| # attribute-naming-style | ||||
| #class-attribute-rgx= | ||||
| 
 | ||||
| # Naming style matching correct class names | ||||
| class-naming-style=PascalCase | ||||
| 
 | ||||
| # Regular expression matching correct class names. Overrides class-naming-style | ||||
| #class-rgx= | ||||
| 
 | ||||
| # Naming style matching correct constant names | ||||
| const-naming-style=UPPER_CASE | ||||
| 
 | ||||
| # Regular expression matching correct constant names. Overrides const-naming- | ||||
| # style | ||||
| #const-rgx= | ||||
| 
 | ||||
| # Minimum line length for functions/classes that require docstrings, shorter | ||||
| # ones are exempt. | ||||
| docstring-min-length=-1 | ||||
| 
 | ||||
| # Naming style matching correct function names | ||||
| function-naming-style=snake_case | ||||
| 
 | ||||
| # Regular expression matching correct function names. Overrides function- | ||||
| # naming-style | ||||
| #function-rgx= | ||||
| 
 | ||||
| # Good variable names which should always be accepted, separated by a comma | ||||
| good-names=i, | ||||
|            j, | ||||
|            k, | ||||
|            ex, | ||||
|            Run, | ||||
|            _ | ||||
| 
 | ||||
| # Include a hint for the correct naming format with invalid-name | ||||
| include-naming-hint=no | ||||
| 
 | ||||
| # Naming style matching correct inline iteration names | ||||
| inlinevar-naming-style=any | ||||
| 
 | ||||
| # Regular expression matching correct inline iteration names. Overrides | ||||
| # inlinevar-naming-style | ||||
| #inlinevar-rgx= | ||||
| 
 | ||||
| # Naming style matching correct method names | ||||
| method-naming-style=snake_case | ||||
| 
 | ||||
| # Regular expression matching correct method names. Overrides method-naming- | ||||
| # style | ||||
| #method-rgx= | ||||
| 
 | ||||
| # Naming style matching correct module names | ||||
| module-naming-style=snake_case | ||||
| 
 | ||||
| # Regular expression matching correct module names. Overrides module-naming- | ||||
| # style | ||||
| #module-rgx= | ||||
| 
 | ||||
| # Colon-delimited sets of names that determine each other's naming style when | ||||
| # the name regexes allow several styles. | ||||
| name-group= | ||||
| 
 | ||||
| # Regular expression which should only match function or class names that do | ||||
| # not require a docstring. | ||||
| no-docstring-rgx=^_ | ||||
| 
 | ||||
| # List of decorators that produce properties, such as abc.abstractproperty. Add | ||||
| # to this list to register other decorators that produce valid properties. | ||||
| property-classes=abc.abstractproperty | ||||
| 
 | ||||
| # Naming style matching correct variable names | ||||
| variable-naming-style=snake_case | ||||
| 
 | ||||
| # Regular expression matching correct variable names. Overrides variable- | ||||
| # naming-style | ||||
| #variable-rgx= | ||||
| 
 | ||||
| 
 | ||||
| [DESIGN] | ||||
| 
 | ||||
| # Maximum number of arguments for function / method | ||||
| max-args=5 | ||||
| 
 | ||||
| # Maximum number of attributes for a class (see R0902). | ||||
| max-attributes=7 | ||||
| 
 | ||||
| # Maximum number of boolean expressions in a if statement | ||||
| max-bool-expr=5 | ||||
| 
 | ||||
| # Maximum number of branch for function / method body | ||||
| max-branches=12 | ||||
| 
 | ||||
| # Maximum number of locals for function / method body | ||||
| max-locals=15 | ||||
| 
 | ||||
| # Maximum number of parents for a class (see R0901). | ||||
| max-parents=7 | ||||
| 
 | ||||
| # Maximum number of public methods for a class (see R0904). | ||||
| max-public-methods=20 | ||||
| 
 | ||||
| # Maximum number of return / yield for function / method body | ||||
| max-returns=6 | ||||
| 
 | ||||
| # Maximum number of statements in function / method body | ||||
| max-statements=50 | ||||
| 
 | ||||
| # Minimum number of public methods for a class (see R0903). | ||||
| min-public-methods=2 | ||||
| 
 | ||||
| 
 | ||||
| [CLASSES] | ||||
| 
 | ||||
| # List of method names used to declare (i.e. assign) instance attributes. | ||||
| defining-attr-methods=__init__, | ||||
|                       __new__, | ||||
|                       setUp | ||||
| 
 | ||||
| # List of member names, which should be excluded from the protected access | ||||
| # warning. | ||||
| exclude-protected=_asdict, | ||||
|                   _fields, | ||||
|                   _replace, | ||||
|                   _source, | ||||
|                   _make | ||||
| 
 | ||||
| # List of valid names for the first argument in a class method. | ||||
| valid-classmethod-first-arg=cls | ||||
| 
 | ||||
| # List of valid names for the first argument in a metaclass class method. | ||||
| valid-metaclass-classmethod-first-arg=mcs | ||||
| 
 | ||||
| 
 | ||||
| [IMPORTS] | ||||
| 
 | ||||
| # Allow wildcard imports from modules that define __all__. | ||||
| allow-wildcard-with-all=no | ||||
| 
 | ||||
| # Analyse import fallback blocks. This can be used to support both Python 2 and | ||||
| # 3 compatible code, which means that the block might have code that exists | ||||
| # only in one or another interpreter, leading to false positives when analysed. | ||||
| analyse-fallback-blocks=no | ||||
| 
 | ||||
| # Deprecated modules which should not be used, separated by a comma | ||||
| deprecated-modules=regsub, | ||||
|                    TERMIOS, | ||||
|                    Bastion, | ||||
|                    rexec | ||||
| 
 | ||||
| # Create a graph of external dependencies in the given file (report RP0402 must | ||||
| # not be disabled) | ||||
| ext-import-graph= | ||||
| 
 | ||||
| # Create a graph of every (i.e. internal and external) dependencies in the | ||||
| # given file (report RP0402 must not be disabled) | ||||
| import-graph= | ||||
| 
 | ||||
| # Create a graph of internal dependencies in the given file (report RP0402 must | ||||
| # not be disabled) | ||||
| int-import-graph= | ||||
| 
 | ||||
| # Force import order to recognize a module as part of the standard | ||||
| # compatibility libraries. | ||||
| known-standard-library= | ||||
| 
 | ||||
| # Force import order to recognize a module as part of a third party library. | ||||
| known-third-party=enchant | ||||
| 
 | ||||
| 
 | ||||
| [EXCEPTIONS] | ||||
| 
 | ||||
| # Exceptions that will emit a warning when being caught. Defaults to | ||||
| # "Exception" | ||||
| overgeneral-exceptions=Exception | ||||
| @ -0,0 +1,19 @@ | ||||
| FROM ubuntu:16.04 | ||||
| 
 | ||||
| RUN apt-get update && apt-get install -y make python python-pip locales curl git zlib1g-dev libffi-dev bzip2 libssl-dev | ||||
| 
 | ||||
| RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen | ||||
| ENV LANG en_US.UTF-8 | ||||
| ENV LANGUAGE en_US:en | ||||
| ENV LC_ALL en_US.UTF-8 | ||||
| 
 | ||||
| RUN curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash | ||||
| 
 | ||||
| ENV PATH="/root/.pyenv/bin:/root/.pyenv/shims:${PATH}" | ||||
| RUN pyenv install 3.7.3 | ||||
| RUN pyenv global 3.7.3 | ||||
| RUN pyenv rehash | ||||
| 
 | ||||
| COPY requirements.txt /tmp/ | ||||
| RUN pip install -r /tmp/requirements.txt | ||||
| COPY . /panda | ||||
| @ -0,0 +1,8 @@ | ||||
| #!/usr/bin/env bash | ||||
| 
 | ||||
| RESULT=$(python3 -m flake8 --select=F $(find ../../ -type f | grep -v "/boardesp/" | grep -v "/cppcheck/" | grep "\.py$")) | ||||
| if [[ $RESULT  ]]; then | ||||
| 	echo "Pyflakes found errors in the code. Please fix and try again" | ||||
| 	echo "$RESULT" | ||||
| 	exit 1 | ||||
| fi | ||||
| @ -0,0 +1,11 @@ | ||||
| #!/usr/bin/env bash | ||||
| 
 | ||||
| python3 -m pylint --disable=R,C,W $(find ../../ -type f | grep -v "/boardesp/" | grep -v "/cppcheck/" | grep "\.py$") | ||||
| 
 | ||||
| exit_status=$? | ||||
| (( res = exit_status & 3 )) | ||||
| 
 | ||||
| if [[ $res != 0  ]]; then | ||||
| 	echo "Pylint found errors in the code. Please fix and try again" | ||||
| 	exit 1 | ||||
| fi | ||||
| @ -1,6 +1,19 @@ | ||||
| FROM ubuntu:16.04 | ||||
| 
 | ||||
| RUN apt-get update && apt-get install -y make python python-pip git | ||||
| COPY tests/safety/requirements.txt /panda/tests/safety/requirements.txt | ||||
| RUN pip install -r /panda/tests/safety/requirements.txt | ||||
| RUN apt-get update && apt-get install -y clang make python python-pip git curl locales zlib1g-dev libffi-dev bzip2 libssl-dev libbz2-dev libusb-1.0-0 | ||||
| 
 | ||||
| RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen | ||||
| ENV LANG en_US.UTF-8 | ||||
| ENV LANGUAGE en_US:en | ||||
| ENV LC_ALL en_US.UTF-8 | ||||
| 
 | ||||
| RUN curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash | ||||
| 
 | ||||
| ENV PATH="/root/.pyenv/bin:/root/.pyenv/shims:${PATH}" | ||||
| RUN pyenv install 3.7.3 | ||||
| RUN pyenv global 3.7.3 | ||||
| RUN pyenv rehash | ||||
| 
 | ||||
| COPY requirements.txt /tmp/ | ||||
| RUN pip install -r /tmp/requirements.txt | ||||
| COPY . /panda | ||||
|  | ||||
| @ -0,0 +1,13 @@ | ||||
| #!/usr/bin/env python | ||||
| import os | ||||
| import sys | ||||
| import datetime | ||||
| 
 | ||||
| sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) | ||||
| from panda import Panda | ||||
| 
 | ||||
| if __name__ == "__main__": | ||||
|   p = Panda() | ||||
|    | ||||
|   p.set_datetime(datetime.datetime.now()) | ||||
|   print(p.get_datetime()) | ||||
Some files were not shown because too many files have changed in this diff Show More
					Loading…
					
					
				
		Reference in new issue