From 3c25760cc91beaa148fa214fea1f7a6e2eccb5a6 Mon Sep 17 00:00:00 2001 From: Vehicle Researcher Date: Tue, 23 Apr 2019 01:34:19 +0000 Subject: [PATCH] Squashed 'panda/' changes from 38dc4e678..1282e8f5a 1282e8f5a cap libusb1 version in setup (#183) 64bcc89a9 Subaru: 545 msg must be generated 9159df9a5 Merge branch '0.5.10-chyrsler' f8ab74a1c L-line relay (#166) 11c4cdcc4 Cleanup leftover jenkins command 22572d949 Fix Jenkins build dockerfiles with same name 1d2f8f0ab Jenkins (#179) f383eee96 Power saving: wake on RX and don't print durint IRQ 9540db744 Chrysler safety: better to mention messages we don't want to forward 104950264 chrysler: forward bus 0 to bus 2 (#177) 4276c380e Additional Power saving (#170) git-subtree-dir: panda git-subtree-split: 1282e8f5a0904b1aaa50f382db2e27f20e74a154 --- .dockerignore | 3 + Dockerfile | 64 ++++++++++ Jenkinsfile | 55 +++++++++ VERSION | 2 +- board/drivers/can.h | 75 ++++++++--- board/drivers/lline_relay.h | 88 +++++++++++++ board/gpio.h | 7 +- board/main.c | 26 ++++ board/pedal/main.c | 2 +- board/power_saving.h | 157 ++++++++++++++++++++++++ board/safety.h | 10 ++ board/safety/safety_cadillac.h | 4 + board/safety/safety_chrysler.h | 4 + board/safety/safety_defaults.h | 16 +++ board/safety/safety_elm327.h | 1 + board/safety/safety_ford.h | 1 + board/safety/safety_gm.h | 5 +- board/safety/safety_gm_ascm.h | 1 + board/safety/safety_honda.h | 8 ++ board/safety/safety_hyundai.h | 4 + board/safety/safety_subaru.h | 13 +- board/safety/safety_tesla.h | 4 + board/safety/safety_toyota.h | 8 ++ board/safety/safety_toyota_ipas.h | 2 +- boardesp/get_sdk_ci.sh | 5 + python/__init__.py | 19 ++- requirements.txt | 5 +- run_automated_tests.sh | 8 +- setup.py | 2 +- tests/automated/1_program.py | 16 ++- tests/automated/2_usb_to_can.py | 49 +++++--- tests/automated/3_wifi.py | 55 ++++++--- tests/automated/4_wifi_functionality.py | 25 ++-- tests/automated/5_wifi_udp.py | 43 +++++-- tests/automated/6_two_panda.py | 121 ++++++++++++++++++ tests/automated/helpers.py | 131 +++++++++++++++++--- tests/safety/test.c | 9 ++ 37 files changed, 944 insertions(+), 104 deletions(-) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 Jenkinsfile create mode 100644 board/drivers/lline_relay.h create mode 100644 board/power_saving.h create mode 100755 boardesp/get_sdk_ci.sh create mode 100644 tests/automated/6_two_panda.py diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..f04ae5c0a --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +.git +.DS_Store +boardesp/esp-open-sdk diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..100509f51 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,64 @@ +FROM ubuntu:16.04 +ENV PYTHONUNBUFFERED 1 + +RUN apt-get update && apt-get install -y \ + autoconf \ + automake \ + bash \ + bison \ + bzip2 \ + curl \ + dfu-util \ + flex \ + g++ \ + gawk \ + gcc \ + git \ + gperf \ + help2man \ + iputils-ping \ + libexpat-dev \ + libstdc++-arm-none-eabi-newlib \ + libtool \ + libtool-bin \ + libusb-1.0-0 \ + make \ + ncurses-dev \ + network-manager \ + python-dev \ + python-serial \ + sed \ + texinfo \ + unrar-free \ + unzip \ + wget \ + build-essential \ + python-dev \ + python-pip \ + screen \ + vim \ + wget \ + wireless-tools + +RUN pip install --upgrade pip==18.0 + +COPY requirements.txt /tmp/ +RUN pip install -r /tmp/requirements.txt + +RUN mkdir -p /home/batman +ENV HOME /home/batman + +ENV PYTHONPATH /tmp:$PYTHONPATH + +COPY ./boardesp/get_sdk_ci.sh /tmp/panda/boardesp/ + +RUN useradd --system -s /sbin/nologin pandauser +RUN mkdir -p /tmp/panda/boardesp/esp-open-sdk +RUN chown pandauser /tmp/panda/boardesp/esp-open-sdk +USER pandauser +RUN cd /tmp/panda/boardesp && ./get_sdk_ci.sh +USER root + +COPY ./xx/pandaextra /tmp/pandaextra + +ADD ./panda.tar.gz /tmp/panda diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 000000000..b90a8ca38 --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,55 @@ +pipeline { + agent any + environment { + AUTHOR = """${sh( + returnStdout: true, + script: "git --no-pager show -s --format='%an' ${GIT_COMMIT}" + ).trim()}""" + + DOCKER_IMAGE_TAG = "panda:build-${env.BUILD_ID}" + DOCKER_NAME = "panda-test-${env.BUILD_ID}" + } + stages { + stage('Build Docker Image') { + steps { + timeout(time: 60, unit: 'MINUTES') { + script { + sh 'git clone --no-checkout --depth 1 git@github.com:commaai/xx.git || true' + sh 'cd xx && git fetch origin && git checkout origin/master -- pandaextra && cd ..' // Needed for certs for panda flashing + sh 'git archive -v -o panda.tar.gz --format=tar.gz HEAD' + dockerImage = docker.build("${env.DOCKER_IMAGE_TAG}") + } + } + } + } + stage('Test Dev Build') { + steps { + lock(resource: "Pandas", inversePrecedence: true, quantity:1){ + timeout(time: 60, unit: 'MINUTES') { + sh "docker run --name ${env.DOCKER_NAME} --privileged --volume /dev/bus/usb:/dev/bus/usb --volume /var/run/dbus:/var/run/dbus --net host ${env.DOCKER_IMAGE_TAG} bash -c 'cd /tmp/panda; ./run_automated_tests.sh '" + } + } + } + } + stage('Test EON Build') { + steps { + lock(resource: "Pandas", inversePrecedence: true, quantity:1){ + timeout(time: 60, unit: 'MINUTES') { + sh "docker cp ${env.DOCKER_NAME}:/tmp/panda/nosetests.xml test_results_dev.xml" + sh "touch EON && docker cp EON ${env.DOCKER_NAME}:/EON" + sh "docker start -a ${env.DOCKER_NAME}" + } + } + } + } + } + post { + always { + script { + sh "docker cp ${env.DOCKER_NAME}:/tmp/panda/nosetests.xml test_results_EON.xml" + sh "docker rm ${env.DOCKER_NAME}" + } + junit "test_results*.xml" + } + } +} \ No newline at end of file diff --git a/VERSION b/VERSION index 0408c30b4..24e56e03c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v1.2.0 \ No newline at end of file +v1.2.1 \ No newline at end of file diff --git a/board/drivers/can.h b/board/drivers/can.h index b837094c9..9f02c2eba 100644 --- a/board/drivers/can.h +++ b/board/drivers/can.h @@ -3,6 +3,8 @@ #define ALL_CAN_BUT_MAIN_SILENT 0xFE #define ALL_CAN_LIVE 0 +#include "lline_relay.h" + int can_live = 0, pending_can_live = 0, can_loopback = 0, can_silent = ALL_CAN_SILENT; // ********************* instantiate queues ********************* @@ -23,6 +25,11 @@ can_buffer(tx2_q, 0x100) can_ring *can_queues[] = {&can_tx1_q, &can_tx2_q}; #endif +#ifdef PANDA +// Forward declare +void power_save_reset_timer(); +#endif + // ********************* interrupt safe queue ********************* int can_pop(can_ring *q, CAN_FIFOMailBox_TypeDef *elem) { @@ -213,7 +220,7 @@ void can_init(uint8_t can_number) { CAN->FMR &= ~(CAN_FMR_FINIT); // enable certain CAN interrupts - CAN->IER |= CAN_IER_TMEIE | CAN_IER_FMPIE0; + CAN->IER |= CAN_IER_TMEIE | CAN_IER_FMPIE0 | CAN_IER_WKUIE; switch (can_number) { case 0: @@ -293,7 +300,6 @@ void can_set_gmlan(int bus) { void can_sce(CAN_TypeDef *CAN) { enter_critical_section(); - can_err_cnt += 1; #ifdef DEBUG if (CAN==CAN1) puts("CAN1: "); if (CAN==CAN2) puts("CAN2: "); @@ -315,16 +321,32 @@ void can_sce(CAN_TypeDef *CAN) { uint8_t can_number = CAN_NUM_FROM_CANIF(CAN); uint8_t bus_number = BUS_NUM_FROM_CAN_NUM(can_number); - if (can_autobaud_enabled[bus_number] && (CAN->ESR & CAN_ESR_LEC)) { - can_autobaud_speed_increment(can_number); - can_set_speed(can_number); + + if (CAN->MSR & CAN_MSR_WKUI) { + //Waking from sleep + #ifdef DEBUG + puts("WAKE\n"); + #endif + set_can_enable(CAN, 1); + CAN->MSR &= ~(CAN_MSR_WKUI); + CAN->MSR = CAN->MSR; +#ifdef PANDA + power_save_reset_timer(); +#endif + } else { + can_err_cnt += 1; + + + if (can_autobaud_enabled[bus_number] && (CAN->ESR & CAN_ESR_LEC)) { + can_autobaud_speed_increment(can_number); + can_set_speed(can_number); + } + + // clear current send + CAN->TSR |= CAN_TSR_ABRQ0; + CAN->MSR &= ~(CAN_MSR_ERRI); + CAN->MSR = CAN->MSR; } - - // clear current send - CAN->TSR |= CAN_TSR_ABRQ0; - CAN->MSR &= ~(CAN_MSR_ERRI); - CAN->MSR = CAN->MSR; - exit_critical_section(); } @@ -332,6 +354,9 @@ void can_sce(CAN_TypeDef *CAN) { void process_can(uint8_t can_number) { if (can_number == 0xff) return; +#ifdef PANDA + power_save_reset_timer(); +#endif enter_critical_section(); @@ -375,6 +400,13 @@ void process_can(uint8_t can_number) { } if (can_pop(can_queues[bus_number], &to_send)) { + if (CAN->MCR & CAN_MCR_SLEEP) { + set_can_enable(CAN, 1); + CAN->MCR &= ~(CAN_MCR_SLEEP); + CAN->MCR |= CAN_MCR_INRQ; + while((CAN->MSR & CAN_MSR_INAK) != CAN_MSR_INAK); + CAN->MCR &= ~(CAN_MCR_INRQ); + } can_tx_cnt += 1; // only send if we have received a packet CAN->sTxMailBox[0].TDLR = to_send.RDLR; @@ -390,6 +422,9 @@ void process_can(uint8_t can_number) { // CAN receive handlers // blink blue when we are receiving CAN messages void can_rx(uint8_t can_number) { + #ifdef PANDA + power_save_reset_timer(); + #endif CAN_TypeDef *CAN = CANIF_FROM_CAN_NUM(can_number); uint8_t bus_number = BUS_NUM_FROM_CAN_NUM(can_number); while (CAN->RF0R & CAN_RF0R_FMP0) { @@ -420,14 +455,16 @@ void can_rx(uint8_t can_number) { // forwarding (panda only) #ifdef PANDA - int bus_fwd_num = can_forwarding[bus_number] != -1 ? can_forwarding[bus_number] : safety_fwd_hook(bus_number, &to_push); - if (bus_fwd_num != -1) { - CAN_FIFOMailBox_TypeDef to_send; - to_send.RIR = to_push.RIR | 1; // TXRQ - to_send.RDTR = to_push.RDTR; - to_send.RDLR = to_push.RDLR; - to_send.RDHR = to_push.RDHR; - can_send(&to_send, bus_fwd_num); + if ((get_lline_status() != 0) || !relay_control) { //Relay engaged or relay isn't controlled, allow fwd + int bus_fwd_num = can_forwarding[bus_number] != -1 ? can_forwarding[bus_number] : safety_fwd_hook(bus_number, &to_push); + if (bus_fwd_num != -1) { + CAN_FIFOMailBox_TypeDef to_send; + to_send.RIR = to_push.RIR | 1; // TXRQ + to_send.RDTR = to_push.RDTR; + to_send.RDLR = to_push.RDLR; + to_send.RDHR = to_push.RDHR; + can_send(&to_send, bus_fwd_num); + } } #endif diff --git a/board/drivers/lline_relay.h b/board/drivers/lline_relay.h new file mode 100644 index 000000000..217e7dfe3 --- /dev/null +++ b/board/drivers/lline_relay.h @@ -0,0 +1,88 @@ +#ifdef PANDA + +int relay_control = 0; // True if relay is controlled through l-line + +/* Conrol a relay connected to l-line pin */ + +// 160us cycles, 1 high, 25 low + +volatile int turn_on_relay = 0; +volatile int on_cycles = 25; + +//5s timeout +#define LLINE_TIMEOUT_CYCLES 31250 +volatile int timeout_cycles = LLINE_TIMEOUT_CYCLES; + +void TIM5_IRQHandler(void) { + if (TIM5->SR & TIM_SR_UIF) { + on_cycles--; + timeout_cycles--; + if (timeout_cycles == 0) { + turn_on_relay = 0; + } + if (on_cycles > 0) { + if (turn_on_relay) { + set_gpio_output(GPIOC, 10, 0); + } + } + else { + set_gpio_output(GPIOC, 10, 1); + on_cycles = 25; + } + } + TIM5->ARR = 160-1; + TIM5->SR = 0; +} + +void lline_relay_init (void) { + set_lline_output(0); + relay_control = 1; + set_gpio_output(GPIOC, 10, 1); + + // setup + TIM5->PSC = 48-1; // tick on 1 us + TIM5->CR1 = TIM_CR1_CEN; // enable + TIM5->ARR = 50-1; // 50 us + TIM5->DIER = TIM_DIER_UIE; // update interrupt + TIM5->CNT = 0; + + NVIC_EnableIRQ(TIM5_IRQn); + +#ifdef DEBUG + puts("INIT LLINE\n"); + puts(" SR "); + putui(TIM5->SR); + puts(" PSC "); + putui(TIM5->PSC); + puts(" CR1 "); + putui(TIM5->CR1); + puts(" ARR "); + putui(TIM5->ARR); + puts(" DIER "); + putui(TIM5->DIER); + puts(" SR "); + putui(TIM5->SR); + puts(" CNT "); + putui(TIM5->CNT); + puts("\n"); +#endif +} + +void lline_relay_release (void) { + set_lline_output(0); + relay_control = 0; + puts("RELEASE LLINE\n"); + set_gpio_alternate(GPIOC, 10, GPIO_AF7_USART3); + NVIC_DisableIRQ(TIM5_IRQn); +} + +void set_lline_output(int to_set) { + timeout_cycles = LLINE_TIMEOUT_CYCLES; + turn_on_relay = to_set; +} + +int get_lline_status() { + return turn_on_relay; +} + +#endif diff --git a/board/gpio.h b/board/gpio.h index 7bd24ecbb..ede6c9b3e 100644 --- a/board/gpio.h +++ b/board/gpio.h @@ -120,6 +120,8 @@ void periph_init() { RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; RCC->APB1ENR |= RCC_APB1ENR_TIM4EN; + RCC->APB1ENR |= RCC_APB1ENR_TIM5EN; + RCC->APB1ENR |= RCC_APB1ENR_TIM6EN; RCC->APB2ENR |= RCC_APB2ENR_USART1EN; RCC->AHB2ENR |= RCC_AHB2ENR_OTGFSEN; RCC->APB2ENR |= RCC_APB2ENR_TIM1EN; @@ -390,7 +392,9 @@ void gpio_init() { set_gpio_output(GPIOA, 14, 1); // C10,C11: L-Line setup on USART 3 - set_gpio_alternate(GPIOC, 10, GPIO_AF7_USART3); + // LLine now used for relay output + set_gpio_output(GPIOC, 10, 1); + //set_gpio_alternate(GPIOC, 10, GPIO_AF7_USART3); set_gpio_alternate(GPIOC, 11, GPIO_AF7_USART3); set_gpio_pullup(GPIOC, 11, PULL_UP); #endif @@ -475,4 +479,3 @@ void early() { enter_bootloader_mode = ENTER_SOFTLOADER_MAGIC; } } - diff --git a/board/main.c b/board/main.c index 8ee1bb0c8..478f8b942 100644 --- a/board/main.c +++ b/board/main.c @@ -20,6 +20,8 @@ #include "drivers/spi.h" #include "drivers/timer.h" +#include "power_saving.h" + // ***************************** fan ***************************** @@ -141,6 +143,7 @@ void usb_cb_ep2_out(uint8_t *usbdata, int len, int hardwired) { uart_ring *ur = get_ring_by_number(usbdata[0]); if (!ur) return; if ((usbdata[0] < 2) || safety_tx_lin_hook(usbdata[0]-2, usbdata+1, len-1)) { + if (ur == &esp_ring) power_save_reset_timer(); for (int i = 1; i < len; i++) while (!putc(ur, usbdata[i])); } } @@ -160,6 +163,14 @@ void usb_cb_ep3_out(uint8_t *usbdata, int len, int hardwired) { uint8_t bus_number = (to_push.RDTR >> 4) & CAN_BUS_NUM_MASK; can_send(&to_push, bus_number); + + #ifdef PANDA + // Enable relay on can message if allowed. + // Temporary until OP has support for relay + if (safety_relay_hook()) { + set_lline_output(1); + } + #endif } } @@ -441,6 +452,16 @@ int usb_cb_control_msg(USB_Setup_TypeDef *setup, uint8_t *resp, int hardwired) { } break; } + // **** 0xf3: set l-line relay + case 0xf3: + { + #ifdef PANDA + if (safety_relay_hook()) { + set_lline_output(setup->b.wValue.w == 1); + } + #endif + break; + } default: puts("NO HANDLER "); puth(setup->b.bRequest); @@ -572,6 +593,9 @@ int main() { #ifdef PANDA spi_init(); #endif +#ifdef DEBUG + puts("DEBUG ENABLED\n"); +#endif // set PWM fan_init(); @@ -581,6 +605,8 @@ int main() { __enable_irq(); + power_save_init(); + // if the error interrupt is enabled to quickly when the CAN bus is active // something bad happens and you can't connect to the device over USB delay(10000000); diff --git a/board/pedal/main.c b/board/pedal/main.c index 13a221a87..6b55ca780 100644 --- a/board/pedal/main.c +++ b/board/pedal/main.c @@ -295,6 +295,7 @@ int main() { puts("**** INTERRUPTS ON ****\n"); __enable_irq(); + // main pedal loop while (1) { pedal(); @@ -302,4 +303,3 @@ int main() { return 0; } - diff --git a/board/power_saving.h b/board/power_saving.h new file mode 100644 index 000000000..c6f83457f --- /dev/null +++ b/board/power_saving.h @@ -0,0 +1,157 @@ +#define POWER_SAVE_STATUS_DISABLED 0 +//Moving to enabled, but can wakeup not yet enabled +#define POWER_SAVE_STATUS_SWITCHING 1 +#define POWER_SAVE_STATUS_ENABLED 2 + +volatile int power_save_status = POWER_SAVE_STATUS_DISABLED; + +void power_save_enable(void) { + power_save_status = POWER_SAVE_STATUS_SWITCHING; + puts("Saving power\n"); + //Turn off can transciever + set_can_enable(CAN1, 0); + set_can_enable(CAN2, 0); +#ifdef PANDA + set_can_enable(CAN3, 0); +#endif + + //Turn off GMLAN + set_gpio_output(GPIOB, 14, 0); + set_gpio_output(GPIOB, 15, 0); + +#ifdef PANDA + //Turn off LIN K + if (revision == PANDA_REV_C) { + set_gpio_output(GPIOB, 7, 0); // REV C + } else { + set_gpio_output(GPIOB, 4, 0); // REV AB + } + // LIN L + set_gpio_output(GPIOA, 14, 0); +#endif + + if (is_grey_panda) { + char* UBLOX_SLEEP_MSG = "\xb5\x62\x06\x04\x04\x00\x01\x00\x08\x00\x17\x78"; + int len = 12; + uart_ring *ur = get_ring_by_number(1); + for (int i = 0; i < len; i++) while (!putc(ur, UBLOX_SLEEP_MSG[i])); + } + + //Setup timer for can enable + TIM6->PSC = 48-1; // tick on 1 us + + TIM6->ARR = 12; // 12us + // Enable, One-Pulse Mode, Only overflow interrupt + TIM6->CR1 = TIM_CR1_CEN | TIM_CR1_OPM | TIM_CR1_URS; + TIM6->EGR = TIM_EGR_UG; + TIM6->CR1 |= TIM_CR1_CEN; +} + +void power_save_enable_can_wake(void) { + // CAN Automatic Wake must be done a little while after the sleep + // On some cars turning off the can transciver can trigger the wakeup + power_save_status = POWER_SAVE_STATUS_ENABLED; + puts("Turning can off\n"); + CAN1->MCR |= CAN_MCR_SLEEP; + CAN1->MCR |= CAN_MCR_AWUM; + + CAN2->MCR |= CAN_MCR_SLEEP; + CAN2->MCR |= CAN_MCR_AWUM; +#ifdef PANDA + CAN3->MCR |= CAN_MCR_SLEEP; + CAN3->MCR |= CAN_MCR_AWUM; +#endif + + //set timer back + TIM6->PSC = 48000-1; // tick on 1 ms + TIM6->ARR = 10000; // 10s + // Enable, One-Pulse Mode, Only overflow interrupt + TIM6->CR1 = TIM_CR1_OPM | TIM_CR1_URS; + TIM6->EGR = TIM_EGR_UG; +} + +void power_save_disable(void) { + power_save_status = POWER_SAVE_STATUS_DISABLED; + puts("not Saving power\n"); + TIM6->CR1 |= TIM_CR1_CEN; //Restart timer + TIM6->CNT = 0; + + //Turn on can + set_can_enable(CAN1, 1); + set_can_enable(CAN2, 1); + +#ifdef PANDA + set_can_enable(CAN3, 1); +#endif + + //Turn on GMLAN + set_gpio_output(GPIOB, 14, 1); + set_gpio_output(GPIOB, 15, 1); + +#ifdef PANDA + //Turn on LIN K + if (revision == PANDA_REV_C) { + set_gpio_output(GPIOB, 7, 1); // REV C + } else { + set_gpio_output(GPIOB, 4, 1); // REV AB + } + // LIN L + set_gpio_output(GPIOA, 14, 1); +#endif + + if (is_grey_panda) { + char* UBLOX_WAKE_MSG = "\xb5\x62\x06\x04\x04\x00\x01\x00\x09\x00\x18\x7a"; + int len = 12; + uart_ring *ur = get_ring_by_number(1); + for (int i = 0; i < len; i++) while (!putc(ur, UBLOX_WAKE_MSG[i])); + } + + //set timer back + TIM6->PSC = 48000-1; // tick on 1 ms + TIM6->ARR = 10000; // 10s + // Enable, One-Pulse Mode, Only overflow interrupt + TIM6->CR1 = TIM_CR1_CEN | TIM_CR1_OPM | TIM_CR1_URS; + TIM6->EGR = TIM_EGR_UG; + TIM6->CR1 |= TIM_CR1_CEN; +} + + + +// Reset timer when activity +void power_save_reset_timer() { + TIM6->CNT = 0; + if (power_save_status != POWER_SAVE_STATUS_DISABLED){ + power_save_disable(); + } +} + +void power_save_init(void) { + puts("Saving power init\n"); + TIM6->PSC = 48000-1; // tick on 1 ms + + + TIM6->ARR = 10000; // 10s + // Enable, One-Pulse Mode, Only overflow interrupt + TIM6->CR1 = TIM_CR1_CEN | TIM_CR1_OPM | TIM_CR1_URS; + TIM6->EGR = TIM_EGR_UG; + NVIC_EnableIRQ(TIM6_DAC_IRQn); + puts("Saving power init done\n"); + TIM6->DIER = TIM_DIER_UIE; + TIM6->CR1 |= TIM_CR1_CEN; +} + +void TIM6_DAC_IRQHandler(void) { + //Timeout switch to power saving mode. + if (TIM6->SR & TIM_SR_UIF) { + TIM6->SR = 0; +#ifdef EON + if (power_save_status == POWER_SAVE_STATUS_DISABLED) { + power_save_enable(); + } else if (power_save_status == POWER_SAVE_STATUS_SWITCHING) { + power_save_enable_can_wake(); + } +#endif + } else { + TIM6->CR1 |= TIM_CR1_CEN; + } +} diff --git a/board/safety.h b/board/safety.h index 6e5dc8e36..5543333e3 100644 --- a/board/safety.h +++ b/board/safety.h @@ -29,6 +29,10 @@ int driver_limit_check(int val, int val_last, struct sample_t *val_driver, int rt_rate_limit_check(int val, int val_last, const int MAX_RT_DELTA); #ifdef PANDA float interpolate(struct lookup_t xy, float x); + +void lline_relay_init (void); +void lline_relay_release (void); +void set_lline_output(int to_set); #endif typedef void (*safety_hook_init)(int16_t param); @@ -37,6 +41,7 @@ typedef int (*tx_hook)(CAN_FIFOMailBox_TypeDef *to_send); typedef int (*tx_lin_hook)(int lin_num, uint8_t *data, int len); typedef int (*ign_hook)(); typedef int (*fwd_hook)(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd); +typedef int (*relay_hook)(); typedef struct { safety_hook_init init; @@ -45,6 +50,7 @@ typedef struct { tx_hook tx; tx_lin_hook tx_lin; fwd_hook fwd; + relay_hook relay; } safety_hooks; // This can be set by the safety hooks. @@ -91,6 +97,10 @@ int safety_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { return current_hooks->fwd(bus_num, to_fwd); } +int safety_relay_hook(void) { + return current_hooks->relay(); +} + typedef struct { uint16_t id; const safety_hooks *hooks; diff --git a/board/safety/safety_cadillac.h b/board/safety/safety_cadillac.h index 2a2d8b985..0e6636a1a 100644 --- a/board/safety/safety_cadillac.h +++ b/board/safety/safety_cadillac.h @@ -115,6 +115,9 @@ static int cadillac_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { static void cadillac_init(int16_t param) { controls_allowed = 0; cadillac_ign = 0; + #ifdef PANDA + lline_relay_release(); + #endif } static int cadillac_ign_hook() { @@ -128,4 +131,5 @@ const safety_hooks cadillac_hooks = { .tx_lin = nooutput_tx_lin_hook, .ignition = cadillac_ign_hook, .fwd = alloutput_fwd_hook, + .relay = nooutput_relay_hook, }; diff --git a/board/safety/safety_chrysler.h b/board/safety/safety_chrysler.h index 4af588401..a8826b915 100644 --- a/board/safety/safety_chrysler.h +++ b/board/safety/safety_chrysler.h @@ -127,6 +127,9 @@ static int chrysler_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { static void chrysler_init(int16_t param) { chrysler_camera_detected = 0; + #ifdef PANDA + lline_relay_release(); + #endif } static int chrysler_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { @@ -150,4 +153,5 @@ const safety_hooks chrysler_hooks = { .tx_lin = nooutput_tx_lin_hook, .ignition = default_ign_hook, .fwd = chrysler_fwd_hook, + .relay = nooutput_relay_hook, }; diff --git a/board/safety/safety_defaults.h b/board/safety/safety_defaults.h index 196df65d2..88d31a7de 100644 --- a/board/safety/safety_defaults.h +++ b/board/safety/safety_defaults.h @@ -8,6 +8,9 @@ int default_ign_hook() { static void nooutput_init(int16_t param) { controls_allowed = 0; + #ifdef PANDA + lline_relay_release(); + #endif } static int nooutput_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { @@ -22,6 +25,10 @@ static int nooutput_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { return -1; } +static int nooutput_relay_hook(int to_set) { + return false; +} + const safety_hooks nooutput_hooks = { .init = nooutput_init, .rx = default_rx_hook, @@ -29,12 +36,16 @@ const safety_hooks nooutput_hooks = { .tx_lin = nooutput_tx_lin_hook, .ignition = default_ign_hook, .fwd = nooutput_fwd_hook, + .relay = nooutput_relay_hook, }; // *** all output safety mode *** static void alloutput_init(int16_t param) { controls_allowed = 1; + #ifdef PANDA + lline_relay_release(); + #endif } static int alloutput_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { @@ -49,6 +60,10 @@ static int alloutput_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { return -1; } +static int alloutput_relay_hook(int to_set) { + return true; +} + const safety_hooks alloutput_hooks = { .init = alloutput_init, .rx = default_rx_hook, @@ -56,4 +71,5 @@ const safety_hooks alloutput_hooks = { .tx_lin = alloutput_tx_lin_hook, .ignition = default_ign_hook, .fwd = alloutput_fwd_hook, + .relay = alloutput_relay_hook, }; diff --git a/board/safety/safety_elm327.h b/board/safety/safety_elm327.h index 98dce6532..e89783bcc 100644 --- a/board/safety/safety_elm327.h +++ b/board/safety/safety_elm327.h @@ -42,4 +42,5 @@ const safety_hooks elm327_hooks = { .tx_lin = elm327_tx_lin_hook, .ignition = default_ign_hook, .fwd = elm327_fwd_hook, + .relay = nooutput_relay_hook, }; diff --git a/board/safety/safety_ford.h b/board/safety/safety_ford.h index 075029fb6..1d3f16d06 100644 --- a/board/safety/safety_ford.h +++ b/board/safety/safety_ford.h @@ -90,4 +90,5 @@ const safety_hooks ford_hooks = { .tx_lin = nooutput_tx_lin_hook, .ignition = default_ign_hook, .fwd = nooutput_fwd_hook, + .relay = nooutput_relay_hook, }; diff --git a/board/safety/safety_gm.h b/board/safety/safety_gm.h index f35b26b4e..396fb8770 100644 --- a/board/safety/safety_gm.h +++ b/board/safety/safety_gm.h @@ -228,6 +228,9 @@ static int gm_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { static void gm_init(int16_t param) { controls_allowed = 0; gm_ignition_started = 0; + #ifdef PANDA + lline_relay_release(); + #endif } static int gm_ign_hook() { @@ -241,5 +244,5 @@ const safety_hooks gm_hooks = { .tx_lin = nooutput_tx_lin_hook, .ignition = gm_ign_hook, .fwd = nooutput_fwd_hook, + .relay = nooutput_relay_hook, }; - diff --git a/board/safety/safety_gm_ascm.h b/board/safety/safety_gm_ascm.h index 70a042ec5..b145466d9 100644 --- a/board/safety/safety_gm_ascm.h +++ b/board/safety/safety_gm_ascm.h @@ -48,5 +48,6 @@ const safety_hooks gm_ascm_hooks = { .tx_lin = nooutput_tx_lin_hook, .ignition = default_ign_hook, .fwd = gm_ascm_fwd_hook, + .relay = nooutput_relay_hook, }; diff --git a/board/safety/safety_honda.h b/board/safety/safety_honda.h index fbee6cfe8..40c6917b0 100644 --- a/board/safety/safety_honda.h +++ b/board/safety/safety_honda.h @@ -136,6 +136,9 @@ static void honda_init(int16_t param) { controls_allowed = 0; bosch_hardware = false; honda_alt_brake_msg = false; + #ifdef PANDA + lline_relay_release(); + #endif } static void honda_bosch_init(int16_t param) { @@ -143,6 +146,9 @@ static void honda_bosch_init(int16_t param) { bosch_hardware = true; // Checking for alternate brake override from safety parameter honda_alt_brake_msg = param == 1 ? true : false; + #ifdef PANDA + lline_relay_release(); + #endif } static int honda_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { @@ -176,6 +182,7 @@ const safety_hooks honda_hooks = { .tx_lin = nooutput_tx_lin_hook, .ignition = default_ign_hook, .fwd = honda_fwd_hook, + .relay = nooutput_relay_hook, }; const safety_hooks honda_bosch_hooks = { @@ -185,4 +192,5 @@ const safety_hooks honda_bosch_hooks = { .tx_lin = nooutput_tx_lin_hook, .ignition = default_ign_hook, .fwd = honda_bosch_fwd_hook, + .relay = nooutput_relay_hook, }; diff --git a/board/safety/safety_hyundai.h b/board/safety/safety_hyundai.h index b67632141..9470e34d1 100644 --- a/board/safety/safety_hyundai.h +++ b/board/safety/safety_hyundai.h @@ -152,6 +152,9 @@ static int hyundai_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { static void hyundai_init(int16_t param) { controls_allowed = 0; hyundai_giraffe_switch_2 = 0; + #ifdef PANDA + lline_relay_release(); + #endif } const safety_hooks hyundai_hooks = { @@ -161,4 +164,5 @@ const safety_hooks hyundai_hooks = { .tx_lin = nooutput_tx_lin_hook, .ignition = default_ign_hook, .fwd = hyundai_fwd_hook, + .relay = nooutput_relay_hook, }; diff --git a/board/safety/safety_subaru.h b/board/safety/safety_subaru.h index 00fe1abf9..9fedf16c6 100644 --- a/board/safety/safety_subaru.h +++ b/board/safety/safety_subaru.h @@ -14,6 +14,11 @@ int subaru_desired_torque_last = 0; uint32_t subaru_ts_last = 0; struct sample_t subaru_torque_driver; // last few driver torques measured +static void subaru_init(int16_t param) { + #ifdef PANDA + lline_relay_init(); + #endif +} static void subaru_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { int bus_number = (to_push->RDTR >> 4) & 0xFF; @@ -100,6 +105,7 @@ static int subaru_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { // forward CAN 0 > 1 if (bus_num == 0) { + return 2; // ES CAN } // forward CAN 1 > 0, except ES_LKAS @@ -113,6 +119,10 @@ static int subaru_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { if (addr == 0x122) { return -1; } + // ES Distance + if (addr == 545) { + return -1; + } return 0; // Main CAN } @@ -122,10 +132,11 @@ static int subaru_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { } const safety_hooks subaru_hooks = { - .init = nooutput_init, + .init = subaru_init, .rx = subaru_rx_hook, .tx = subaru_tx_hook, .tx_lin = nooutput_tx_lin_hook, .ignition = default_ign_hook, .fwd = subaru_fwd_hook, + .relay = alloutput_relay_hook, }; diff --git a/board/safety/safety_tesla.h b/board/safety/safety_tesla.h index 78c450e85..882a509f5 100644 --- a/board/safety/safety_tesla.h +++ b/board/safety/safety_tesla.h @@ -230,6 +230,9 @@ static void tesla_init(int16_t param) controls_allowed = 0; tesla_ignition_started = 0; gmlan_switch_init(1); //init the gmlan switch with 1s timeout enabled + #ifdef PANDA + lline_relay_release(); + #endif } static int tesla_ign_hook() @@ -284,4 +287,5 @@ const safety_hooks tesla_hooks = { .tx_lin = tesla_tx_lin_hook, .ignition = tesla_ign_hook, .fwd = tesla_fwd_hook, + .relay = nooutput_relay_hook, }; diff --git a/board/safety/safety_toyota.h b/board/safety/safety_toyota.h index 50d457ab6..02fcb36e5 100644 --- a/board/safety/safety_toyota.h +++ b/board/safety/safety_toyota.h @@ -160,6 +160,9 @@ static void toyota_init(int16_t param) { toyota_giraffe_switch_1 = 0; toyota_camera_forwarded = 0; toyota_dbc_eps_torque_factor = param; + #ifdef PANDA + lline_relay_release(); + #endif } static int toyota_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { @@ -181,6 +184,7 @@ const safety_hooks toyota_hooks = { .tx_lin = nooutput_tx_lin_hook, .ignition = default_ign_hook, .fwd = toyota_fwd_hook, + .relay = nooutput_relay_hook, }; static void toyota_nolimits_init(int16_t param) { @@ -189,6 +193,9 @@ static void toyota_nolimits_init(int16_t param) { toyota_giraffe_switch_1 = 0; toyota_camera_forwarded = 0; toyota_dbc_eps_torque_factor = param; + #ifdef PANDA + lline_relay_release(); + #endif } const safety_hooks toyota_nolimits_hooks = { @@ -198,4 +205,5 @@ const safety_hooks toyota_nolimits_hooks = { .tx_lin = nooutput_tx_lin_hook, .ignition = default_ign_hook, .fwd = toyota_fwd_hook, + .relay = nooutput_relay_hook, }; diff --git a/board/safety/safety_toyota_ipas.h b/board/safety/safety_toyota_ipas.h index ff4158e3c..13e664c0f 100644 --- a/board/safety/safety_toyota_ipas.h +++ b/board/safety/safety_toyota_ipas.h @@ -152,5 +152,5 @@ const safety_hooks toyota_ipas_hooks = { .tx_lin = nooutput_tx_lin_hook, .ignition = default_ign_hook, .fwd = toyota_fwd_hook, + .relay = nooutput_relay_hook, }; - diff --git a/boardesp/get_sdk_ci.sh b/boardesp/get_sdk_ci.sh new file mode 100755 index 000000000..b11cb099a --- /dev/null +++ b/boardesp/get_sdk_ci.sh @@ -0,0 +1,5 @@ +#!/bin/bash +git clone --recursive https://github.com/pfalcon/esp-open-sdk.git +cd esp-open-sdk +git checkout 03f5e898a059451ec5f3de30e7feff30455f7cec +LD_LIBRARY_PATH="" make STANDALONE=y diff --git a/python/__init__.py b/python/__init__.py index b3610e6c3..8c0bc5a9a 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -182,6 +182,7 @@ class Panda(object): traceback.print_exc() if wait == False or self._handle != None: break + context = usb1.USBContext() #New context needed so new devices show up assert(self._handle != None) print("connected") @@ -280,11 +281,14 @@ class Panda(object): if reconnect: self.reconnect() - def recover(self): + def recover(self, timeout=None): self.reset(enter_bootloader=True) + t_start = time.time() while len(PandaDFU.list()) == 0: print("waiting for DFU...") time.sleep(0.1) + if timeout is not None and (time.time() - t_start) > timeout: + return False dfu = PandaDFU(PandaDFU.st_serial_to_dfu_serial(self._serial)) dfu.recover() @@ -292,6 +296,7 @@ class Panda(object): # reflash after recover self.connect(True, True) self.flash() + return True @staticmethod def flash_ota_st(): @@ -300,8 +305,9 @@ class Panda(object): return ret==0 @staticmethod - def flash_ota_wifi(): - ret = os.system("cd %s && make clean && make ota" % (os.path.join(BASEDIR, "boardesp"))) + def flash_ota_wifi(release=False): + release_str = "RELEASE=1" if release else "" + ret = os.system("cd {} && make clean && {} make ota".format(os.path.join(BASEDIR, "boardesp"),release_str)) time.sleep(1) return ret==0 @@ -386,10 +392,17 @@ class Panda(object): elif bus in [Panda.GMLAN_CAN2, Panda.GMLAN_CAN3]: self._handle.controlWrite(Panda.REQUEST_OUT, 0xdb, 1, bus, b'') + def set_lline_relay(self, enable): + self._handle.controlWrite(Panda.REQUEST_OUT, 0xf3, int(enable), 0, b'') + def set_can_loopback(self, enable): # set can loopback mode for all buses self._handle.controlWrite(Panda.REQUEST_OUT, 0xe5, int(enable), 0, b'') + def set_can_enable(self, bus_num, enable): + # sets the can transciever enable pin + self._handle.controlWrite(Panda.REQUEST_OUT, 0xf4, int(bus_num), int(enable), b'') + def set_can_speed_kbps(self, bus, speed): self._handle.controlWrite(Panda.REQUEST_OUT, 0xde, bus, int(speed*10), b'') diff --git a/requirements.txt b/requirements.txt index e968bfb6b..ad6b4c76e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,7 @@ -libusb1 +libusb1 == 1.6.6 hexdump pycrypto tqdm +nose +parameterized +requests diff --git a/run_automated_tests.sh b/run_automated_tests.sh index d7b754190..63d9b6531 100755 --- a/run_automated_tests.sh +++ b/run_automated_tests.sh @@ -1,3 +1,9 @@ #!/bin/bash -PYTHONPATH="." nosetests -x -s tests/automated/$1*.py +TEST_FILENAME=${TEST_FILENAME:-nosetests.xml} +if [ ! -f "/EON" ]; then + TESTSUITE_NAME="Panda_Test-EON" +else + TESTSUITE_NAME="Panda_Test-DEV" +fi +PYTHONPATH="." nosetests -v --with-xunit --xunit-file=./$TEST_FILENAME --xunit-testsuite-name=$TESTSUITE_NAME -s tests/automated/$1*.py diff --git a/setup.py b/setup.py index 312fdca42..2acd9b9e1 100644 --- a/setup.py +++ b/setup.py @@ -46,7 +46,7 @@ setup( platforms='any', license='MIT', install_requires=[ - 'libusb1 >= 1.6.4', + 'libusb1 == 1.6.6', 'hexdump >= 3.3', 'pycrypto >= 2.6.1', 'tqdm >= 4.14.0', diff --git a/tests/automated/1_program.py b/tests/automated/1_program.py index 7da801bcd..1e0beb8ae 100644 --- a/tests/automated/1_program.py +++ b/tests/automated/1_program.py @@ -1,11 +1,15 @@ import os from panda import Panda +from helpers import panda_color_to_serial, test_white_and_grey -def test_recover(): - p = Panda() - p.recover() +@test_white_and_grey +@panda_color_to_serial +def test_recover(serial=None): + p = Panda(serial=serial) + assert p.recover(timeout=30) -def test_flash(): - p = Panda() +@test_white_and_grey +@panda_color_to_serial +def test_flash(serial=None): + p = Panda(serial=serial) p.flash() - diff --git a/tests/automated/2_usb_to_can.py b/tests/automated/2_usb_to_can.py index 481564240..7860d3290 100644 --- a/tests/automated/2_usb_to_can.py +++ b/tests/automated/2_usb_to_can.py @@ -3,14 +3,16 @@ import os import sys import time from panda import Panda -from nose.tools import timed, assert_equal, assert_less, assert_greater -from helpers import time_many_sends, connect_wo_esp +from nose.tools import assert_equal, assert_less, assert_greater +from helpers import time_many_sends, connect_wo_esp, test_white_and_grey, panda_color_to_serial SPEED_NORMAL = 500 SPEED_GMLAN = 33.3 -def test_can_loopback(): - p = connect_wo_esp() +@test_white_and_grey +@panda_color_to_serial +def test_can_loopback(serial=None): + p = connect_wo_esp(serial) # enable output mode p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) @@ -42,8 +44,10 @@ def test_can_loopback(): assert 0x1aa == sr[0][0] == lb[0][0] assert "message" == sr[0][2] == lb[0][2] -def test_safety_nooutput(): - p = connect_wo_esp() +@test_white_and_grey +@panda_color_to_serial +def test_safety_nooutput(serial=None): + p = connect_wo_esp(serial) # enable output mode p.set_safety_mode(Panda.SAFETY_NOOUTPUT) @@ -59,8 +63,10 @@ def test_safety_nooutput(): r = p.can_recv() assert len(r) == 0 -def test_reliability(): - p = connect_wo_esp() +@test_white_and_grey +@panda_color_to_serial +def test_reliability(serial=None): + p = connect_wo_esp(serial) LOOP_COUNT = 100 MSG_COUNT = 100 @@ -97,8 +103,10 @@ def test_reliability(): sys.stdout.write("P") sys.stdout.flush() -def test_throughput(): - p = connect_wo_esp() +@test_white_and_grey +@panda_color_to_serial +def test_throughput(serial=None): + p = connect_wo_esp(serial) # enable output mode p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) @@ -120,8 +128,10 @@ def test_throughput(): print("loopback 100 messages at speed %d, comp speed is %.2f, percent %.2f" % (speed, comp_kbps, saturation_pct)) -def test_gmlan(): - p = connect_wo_esp() +@test_white_and_grey +@panda_color_to_serial +def test_gmlan(serial=None): + p = connect_wo_esp(serial) if p.legacy: return @@ -135,7 +145,7 @@ def test_gmlan(): p.set_can_speed_kbps(1, SPEED_NORMAL) p.set_can_speed_kbps(2, SPEED_NORMAL) p.set_can_speed_kbps(3, SPEED_GMLAN) - + # set gmlan on CAN2 for bus in [Panda.GMLAN_CAN2, Panda.GMLAN_CAN3, Panda.GMLAN_CAN2, Panda.GMLAN_CAN3]: p.set_gmlan(bus) @@ -150,8 +160,10 @@ def test_gmlan(): print("%d: %.2f kbps vs %.2f kbps" % (bus, comp_kbps_gmlan, comp_kbps_normal)) -def test_gmlan_bad_toggle(): - p = connect_wo_esp() +@test_white_and_grey +@panda_color_to_serial +def test_gmlan_bad_toggle(serial=None): + p = connect_wo_esp(serial) if p.legacy: return @@ -178,9 +190,10 @@ def test_gmlan_bad_toggle(): # this will fail if you have hardware serial connected -def test_serial_debug(): - p = connect_wo_esp() +@test_white_and_grey +@panda_color_to_serial +def test_serial_debug(serial=None): + p = connect_wo_esp(serial) junk = p.serial_read(Panda.SERIAL_DEBUG) p.call_control_api(0xc0) assert(p.serial_read(Panda.SERIAL_DEBUG).startswith("can ")) - diff --git a/tests/automated/3_wifi.py b/tests/automated/3_wifi.py index c6f4154ba..6578903aa 100644 --- a/tests/automated/3_wifi.py +++ b/tests/automated/3_wifi.py @@ -1,33 +1,60 @@ from __future__ import print_function import os +import time from panda import Panda -from helpers import connect_wifi +from helpers import connect_wifi, test_white, test_white_and_grey, panda_color_to_serial import requests -def test_get_serial(): - p = Panda() +@test_white_and_grey +@panda_color_to_serial +def test_get_serial(serial=None): + p = Panda(serial) print(p.get_serial()) -def test_get_serial_in_flash_mode(): - p = Panda() +@test_white_and_grey +@panda_color_to_serial +def test_get_serial_in_flash_mode(serial=None): + p = Panda(serial) p.reset(enter_bootstub=True) assert(p.bootstub) print(p.get_serial()) p.reset() -def test_connect_wifi(): - connect_wifi() +@test_white +@panda_color_to_serial +def test_connect_wifi(serial=None): + connect_wifi(serial) -def test_flash_wifi(): - Panda.flash_ota_wifi() - connect_wifi() +@test_white +@panda_color_to_serial +def test_flash_wifi(serial=None): + connect_wifi(serial) + assert Panda.flash_ota_wifi(release=True), "OTA Wifi Flash Failed" + connect_wifi(serial) -def test_wifi_flash_st(): - Panda.flash_ota_st() +@test_white +@panda_color_to_serial +def test_wifi_flash_st(serial=None): + connect_wifi(serial) + assert Panda.flash_ota_st(), "OTA ST Flash Failed" + connected = False + st = time.time() + while not connected and (time.time() - st) < 20: + try: + p = Panda(serial=serial) + p.get_serial() + connected = True + except: + time.sleep(1) -def test_webpage_fetch(): + if not connected: + assert False, "Panda failed to connect on USB after flashing" + +@test_white +@panda_color_to_serial +def test_webpage_fetch(serial=None): + connect_wifi(serial) r = requests.get("http://192.168.0.10/") print(r.text) assert "This is your comma.ai panda" in r.text - diff --git a/tests/automated/4_wifi_functionality.py b/tests/automated/4_wifi_functionality.py index 68686008e..0cf42d1f3 100644 --- a/tests/automated/4_wifi_functionality.py +++ b/tests/automated/4_wifi_functionality.py @@ -1,17 +1,22 @@ from __future__ import print_function import time from panda import Panda -from helpers import time_many_sends, connect_wifi +from helpers import time_many_sends, connect_wifi, test_white, panda_color_to_serial from nose.tools import timed, assert_equal, assert_less, assert_greater -def test_get_serial_wifi(): - connect_wifi() +@test_white +@panda_color_to_serial +def test_get_serial_wifi(serial=None): + connect_wifi(serial) p = Panda("WIFI") print(p.get_serial()) -def test_throughput(): - p = Panda() +@test_white +@panda_color_to_serial +def test_throughput(serial=None): + connect_wifi(serial) + p = Panda(serial) # enable output mode p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) @@ -24,7 +29,7 @@ def test_throughput(): for speed in [100,250,500,750,1000]: # set bus 0 speed to speed p.set_can_speed_kbps(0, speed) - time.sleep(0.05) + time.sleep(0.1) comp_kbps = time_many_sends(p, 0) @@ -35,8 +40,11 @@ def test_throughput(): print("WIFI loopback 100 messages at speed %d, comp speed is %.2f, percent %.2f" % (speed, comp_kbps, saturation_pct)) -def test_recv_only(): - p = Panda() +@test_white +@panda_color_to_serial +def test_recv_only(serial=None): + connect_wifi(serial) + p = Panda(serial) p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) p.set_can_loopback(True) pwifi = Panda("WIFI") @@ -49,4 +57,3 @@ def test_recv_only(): saturation_pct = (comp_kbps/speed) * 100.0 print("HT WIFI loopback %d messages at speed %d, comp speed is %.2f, percent %.2f" % (msg_count, speed, comp_kbps, saturation_pct)) - diff --git a/tests/automated/5_wifi_udp.py b/tests/automated/5_wifi_udp.py index 7d6ccba66..d55baa659 100644 --- a/tests/automated/5_wifi_udp.py +++ b/tests/automated/5_wifi_udp.py @@ -1,14 +1,16 @@ from __future__ import print_function import sys import time -from helpers import time_many_sends, connect_wifi +from helpers import time_many_sends, connect_wifi, test_white, panda_color_to_serial from panda import Panda, PandaWifiStreaming from nose.tools import timed, assert_equal, assert_less, assert_greater -def test_udp_doesnt_drop(): - connect_wifi() +@test_white +@panda_color_to_serial +def test_udp_doesnt_drop(serial=None): + connect_wifi(serial) - p = Panda() + p = Panda(serial) p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) p.set_can_loopback(True) @@ -18,6 +20,7 @@ def test_udp_doesnt_drop(): break for msg_count in [1, 100]: + saturation_pcts = [] for i in range({1: 0x80, 100: 0x20}[msg_count]): pwifi.kick() @@ -31,9 +34,33 @@ def test_udp_doesnt_drop(): sys.stdout.flush() else: print("UDP WIFI loopback %d messages at speed %d, comp speed is %.2f, percent %.2f" % (msg_count, speed, comp_kbps, saturation_pct)) - assert_greater(saturation_pct, 40) + assert_greater(saturation_pct, 20) #sometimes the wifi can be slow... assert_less(saturation_pct, 100) - print("") - - + saturation_pcts.append(saturation_pct) + if len(saturation_pcts) > 0: + assert_greater(sum(saturation_pcts)/len(saturation_pcts), 60) + time.sleep(5) + usb_ok_cnt = 0 + REQ_USB_OK_CNT = 500 + st = time.time() + msg_id = 0x1bb + bus = 0 + last_missing_msg = 0 + while usb_ok_cnt < REQ_USB_OK_CNT and (time.time() - st) < 40: + p.can_send(msg_id, "message", bus) + time.sleep(0.01) + r = [1] + missing = True + while len(r) > 0: + r = p.can_recv() + r = filter(lambda x: x[3] == bus and x[0] == msg_id, r) + if len(r) > 0: + missing = False + usb_ok_cnt += len(r) + if missing: + last_missing_msg = time.time() + et = time.time() - st + last_missing_msg = last_missing_msg - st + print("waited {} for panda to recv can on usb, {} msgs, last missing at {}".format(et, usb_ok_cnt, last_missing_msg)) + assert usb_ok_cnt >= REQ_USB_OK_CNT, "Unable to recv can on USB after UDP" diff --git a/tests/automated/6_two_panda.py b/tests/automated/6_two_panda.py new file mode 100644 index 000000000..3c29a0e7a --- /dev/null +++ b/tests/automated/6_two_panda.py @@ -0,0 +1,121 @@ +from __future__ import print_function +import time +from panda import Panda +from nose.tools import assert_equal, assert_less, assert_greater +from helpers import time_many_sends, test_two_panda, panda_color_to_serial + +@test_two_panda +@panda_color_to_serial +def test_send_recv(serial_sender=None, serial_reciever=None): + p_send = Panda(serial_sender) + p_recv = Panda(serial_reciever) + + p_send.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + p_send.set_can_loopback(False) + + p_recv.set_can_loopback(False) + + assert not p_send.legacy + assert not p_recv.legacy + + p_send.can_send_many([(0x1ba, 0, "message", 0)]*2) + time.sleep(0.05) + p_recv.can_recv() + p_send.can_recv() + + busses = [0,1,2] + + for bus in busses: + for speed in [100, 250, 500, 750, 1000]: + p_send.set_can_speed_kbps(bus, speed) + p_recv.set_can_speed_kbps(bus, speed) + time.sleep(0.05) + + comp_kbps = time_many_sends(p_send, bus, p_recv, two_pandas=True) + + saturation_pct = (comp_kbps/speed) * 100.0 + assert_greater(saturation_pct, 80) + assert_less(saturation_pct, 100) + + print("two pandas bus {}, 100 messages at speed {:4d}, comp speed is {:7.2f}, percent {:6.2f}".format(bus, speed, comp_kbps, saturation_pct)) + +@test_two_panda +@panda_color_to_serial +def test_latency(serial_sender=None, serial_reciever=None): + p_send = Panda(serial_sender) + p_recv = Panda(serial_reciever) + + p_send.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + p_send.set_can_loopback(False) + + p_recv.set_can_loopback(False) + + assert not p_send.legacy + assert not p_recv.legacy + + p_send.set_can_speed_kbps(0, 100) + p_recv.set_can_speed_kbps(0, 100) + time.sleep(0.05) + + p_send.can_send_many([(0x1ba, 0, "testmsg", 0)]*10) + time.sleep(0.05) + p_recv.can_recv() + p_send.can_recv() + + busses = [0,1,2] + + for bus in busses: + for speed in [100, 250, 500, 750, 1000]: + p_send.set_can_speed_kbps(bus, speed) + p_recv.set_can_speed_kbps(bus, speed) + time.sleep(0.1) + #clear can buffers + r = [1] + while len(r) > 0: + r = p_send.can_recv() + r = [1] + while len(r) > 0: + r = p_recv.can_recv() + time.sleep(0.05) + + latencies = [] + comp_kbps_list = [] + saturation_pcts = [] + + num_messages = 100 + + for i in range(num_messages): + st = time.time() + p_send.can_send(0x1ab, "message", bus) + r = [] + while len(r) < 1 and (time.time() - st) < 5: + r = p_recv.can_recv() + et = time.time() + r_echo = [] + while len(r_echo) < 1 and (time.time() - st) < 10: + r_echo = p_send.can_recv() + + if len(r) == 0 or len(r_echo) == 0: + print("r: {}, r_echo: {}".format(r, r_echo)) + + assert_equal(len(r),1) + assert_equal(len(r_echo),1) + + et = (et - st)*1000.0 + comp_kbps = (1+11+1+1+1+4+8*8+15+1+1+1+7) / et + latency = et - ((1+11+1+1+1+4+8*8+15+1+1+1+7) / speed) + + assert_less(latency, 5.0) + + saturation_pct = (comp_kbps/speed) * 100.0 + latencies.append(latency) + comp_kbps_list.append(comp_kbps) + saturation_pcts.append(saturation_pct) + + average_latency = sum(latencies)/num_messages + assert_less(average_latency, 1.0) + average_comp_kbps = sum(comp_kbps_list)/num_messages + average_saturation_pct = sum(saturation_pcts)/num_messages + + print("two pandas bus {}, {} message average at speed {:4d}, latency is {:5.3f}ms, comp speed is {:7.2f}, percent {:6.2f}"\ + .format(bus, num_messages, speed, average_latency, average_comp_kbps, average_saturation_pct)) diff --git a/tests/automated/helpers.py b/tests/automated/helpers.py index 1ddced117..9e92f56bf 100644 --- a/tests/automated/helpers.py +++ b/tests/automated/helpers.py @@ -4,12 +4,34 @@ import time import random import subprocess import requests +from functools import wraps from panda import Panda from nose.tools import timed, assert_equal, assert_less, assert_greater +from parameterized import parameterized, param -def connect_wo_esp(): +test_white_and_grey = parameterized([param(panda_color="White"), + param(panda_color="Grey")]) +test_white = parameterized([param(panda_color="White")]) +test_grey = parameterized([param(panda_color="Grey")]) +test_two_panda = parameterized([param(panda_color=["Grey", "White"]), + param(panda_color=["White", "Grey"])]) + +_serials = {} +def get_panda_serial(is_grey=None): + global _serials + if is_grey not in _serials: + for serial in Panda.list(): + p = Panda(serial=serial) + if is_grey is None or p.is_grey() == is_grey: + _serials[is_grey] = serial + return serial + raise IOError("Panda not found. is_grey: {}".format(is_grey)) + else: + return _serials[is_grey] + +def connect_wo_esp(serial=None): # connect to the panda - p = Panda() + p = Panda(serial=serial) # power down the ESP p.set_esp_power(False) @@ -20,15 +42,28 @@ def connect_wo_esp(): return p -def connect_wifi(): - p = Panda() +def connect_wifi(serial=None): + p = Panda(serial=serial) + p.set_esp_power(True) dongle_id, pw = p.get_serial() assert(dongle_id.isalnum()) _connect_wifi(dongle_id, pw) +FNULL = open(os.devnull, 'w') def _connect_wifi(dongle_id, pw, insecure_okay=False): ssid = str("panda-" + dongle_id) + r = subprocess.call(["ping", "-W", "4", "-c", "1", "192.168.0.10"], stdout=FNULL, stderr=subprocess.STDOUT) + if not r: + #Can already ping, try connecting on wifi + try: + p = Panda("WIFI") + p.get_serial() + print("Already connected") + return + except: + pass + print("WIFI: connecting to %s" % ssid) while 1: @@ -39,8 +74,8 @@ def _connect_wifi(dongle_id, pw, insecure_okay=False): cnt = 0 MAX_TRIES = 10 while cnt < MAX_TRIES: - print "WIFI: scanning %d" % cnt - os.system("sudo iwlist %s scanning > /dev/null" % wlan_interface) + print("WIFI: scanning %d" % cnt) + os.system("iwlist %s scanning > /dev/null" % wlan_interface) os.system("nmcli device wifi rescan") wifi_scan = filter(lambda x: ssid in x, subprocess.check_output(["nmcli","dev", "wifi", "list"]).split("\n")) if len(wifi_scan) != 0: @@ -51,45 +86,107 @@ def _connect_wifi(dongle_id, pw, insecure_okay=False): assert cnt < MAX_TRIES if "-pair" in wifi_scan[0]: os.system("nmcli d wifi connect %s-pair" % (ssid)) + connect_cnt = 0 + MAX_TRIES = 20 + while connect_cnt < MAX_TRIES: + connect_cnt += 1 + r = subprocess.call(["ping", "-W", "4", "-c", "1", "192.168.0.10"], stdout=FNULL, stderr=subprocess.STDOUT) + if r: + print("Waiting for panda to ping...") + time.sleep(0.1) + else: + break if insecure_okay: break # fetch webpage - print "connecting to insecure network to secure" - r = requests.get("http://192.168.0.10/") + print("connecting to insecure network to secure") + try: + r = requests.get("http://192.168.0.10/") + except requests.ConnectionError: + r = requests.get("http://192.168.0.10/") assert r.status_code==200 - print "securing" + print("securing") try: r = requests.get("http://192.168.0.10/secure", timeout=0.01) except requests.exceptions.Timeout: + print("timeout http request to secure") pass else: - os.system("nmcli d wifi connect %s password %s" % (ssid, pw)) - break - + ret = os.system("nmcli d wifi connect %s password %s" % (ssid, pw)) + if os.WEXITSTATUS(ret) == 0: + #check ping too + ping_ok = False + connect_cnt = 0 + MAX_TRIES = 10 + while connect_cnt < MAX_TRIES: + connect_cnt += 1 + r = subprocess.call(["ping", "-W", "4", "-c", "1", "192.168.0.10"], stdout=FNULL, stderr=subprocess.STDOUT) + if r: + print("Waiting for panda to ping...") + time.sleep(0.1) + else: + ping_ok = True + break + if ping_ok: + break + # TODO: confirm that it's connected to the right panda -def time_many_sends(p, bus, precv=None, msg_count=100, msg_id=None): +def time_many_sends(p, bus, precv=None, msg_count=100, msg_id=None, two_pandas=False): if precv == None: precv = p if msg_id == None: msg_id = random.randint(0x100, 0x200) + if p == precv and two_pandas: + raise ValueError("Cannot have two pandas that are the same panda") st = time.time() p.can_send_many([(msg_id, 0, "\xaa"*8, bus)]*msg_count) r = [] + r_echo = [] + r_len_expected = msg_count if two_pandas else msg_count*2 + r_echo_len_exected = msg_count if two_pandas else 0 - while len(r) < (msg_count*2) and (time.time() - st) < 3: + while len(r) < r_len_expected and (time.time() - st) < 5: r.extend(precv.can_recv()) + et = time.time() + if two_pandas: + while len(r_echo) < r_echo_len_exected and (time.time() - st) < 10: + r_echo.extend(p.can_recv()) sent_echo = filter(lambda x: x[3] == 0x80 | bus and x[0] == msg_id, r) - loopback_resp = filter(lambda x: x[3] == bus and x[0] == msg_id, r) + sent_echo.extend(filter(lambda x: x[3] == 0x80 | bus and x[0] == msg_id, r_echo)) + resp = filter(lambda x: x[3] == bus and x[0] == msg_id, r) + leftovers = filter(lambda x: (x[3] != 0x80 | bus and x[3] != bus) or x[0] != msg_id, r) + assert_equal(len(leftovers), 0) + + assert_equal(len(resp), msg_count) assert_equal(len(sent_echo), msg_count) - assert_equal(len(loopback_resp), msg_count) - et = (time.time()-st)*1000.0 + et = (et-st)*1000.0 comp_kbps = (1+11+1+1+1+4+8*8+15+1+1+1+7)*msg_count / et return comp_kbps + +def panda_color_to_serial(fn): + @wraps(fn) + def wrapper(panda_color=None, **kwargs): + pandas_is_grey = [] + if panda_color is not None: + if not isinstance(panda_color, list): + panda_color = [panda_color] + panda_color = [s.lower() for s in panda_color] + for p in panda_color: + if p is None: + pandas_is_grey.append(None) + elif p in ["grey", "gray"]: + pandas_is_grey.append(True) + elif p in ["white"]: + pandas_is_grey.append(False) + else: + raise ValueError("Invalid Panda Color {}".format(p)) + return fn(*[get_panda_serial(is_grey) for is_grey in pandas_is_grey], **kwargs) + return wrapper diff --git a/tests/safety/test.c b/tests/safety/test.c index 9b4c807d7..04e6576fb 100644 --- a/tests/safety/test.c +++ b/tests/safety/test.c @@ -246,3 +246,12 @@ void reset_gmlan_switch_timeout(void){ void gmlan_switch_init(int timeout_enable){ } + +void lline_relay_init (void) { +} + +void lline_relay_release (void) { +} + +void set_lline_output(int to_set) { +}