Skip to content

Commit

Permalink
Decode logfs CAN logs for Tracksight (#1295)
Browse files Browse the repository at this point in the history
### Changelist 
<!-- Give a list of the changes covered in this PR. This will help both
you and the reviewer keep this PR within scope. -->

Added a script to decode raw log files written to the SD card. This uses
JSONCAN to unpack the signals and writes them to a CSV file. For how to
use, run:

```
python3 software/tracksight/backend/scripts/decode_raw_log.py --help
```

These CSV log files are (by default) written to
`software/tracksight/backend/data/`. Files in here are mounted to the
Docker backend when running in local mode, and so can be uploaded to the
local InfluxDB server for viewing on the Tracksight frontend. Pass the
paths to the unpacked CSV log files to `run_local.sh` and they will be
uploaded automatically.

Also, added a few CAN signals/warnings so we can check the state of
logging over CAN.

### Testing Done
<!-- Outline the testing that was done to demonstrate the changes are
solid. This could be unit tests, integration tests, testing on the car,
etc. Include relevant code snippets, screenshots, etc as needed. -->

Tested this whole pipeline on my laptop with VC #2.
  • Loading branch information
gtaharaedmonds authored Jun 4, 2024
1 parent c2c247c commit 2e11be1
Show file tree
Hide file tree
Showing 16 changed files with 285 additions and 86 deletions.
2 changes: 0 additions & 2 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,8 @@ black = "*"
flask-socketio = "*"
pandas = "*"
pytest = "*"
asammdf = "*"
python-dateutil = "*"
requests = "*"
python-dotenv = "*"
influxdb-client = "*"
flask = "*"

Expand Down
8 changes: 8 additions & 0 deletions can_bus/quadruna/VC/VC_alerts.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@
"HighNumberOfCanDataLogs": {
"id": 561,
"description": "High number of CAN data logs (>200) on the SD card, which slows down mounting the filesystem."
},
"CanLoggingSdCardNotPresent": {
"id": 562,
"description": "CAN logging SD card is not detected."
},
"CanLoggingErrored": {
"id": 563,
"description": "CAN logging received multiple errors and has been disabled."
}
},
"faults": {
Expand Down
5 changes: 5 additions & 0 deletions can_bus/quadruna/VC/VC_tx.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
"resolution": 1,
"min": 0,
"max": 1000
},
"CanLoggingRemainingErrors": {
"resolution": 1,
"min": 0,
"max": 10
}
}
},
Expand Down
6 changes: 2 additions & 4 deletions environment/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,10 @@ rich

# Tracksight backend dependencies.
flask
Definitions
flask_cors
pandas
flask_socketio
pytest
asammdf
requests
python-dotenv
influxdb-client
influxdb-client
tzlocal
2 changes: 1 addition & 1 deletion firmware/dev/h7dev/src/cubemx/Src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -621,7 +621,7 @@ void runCanTxTask(void *argument)
for (int j = 0; j < 6; j++)
{
read_num++;
io_canLogging_pushTxMsgToQueue(&msg);
// io_canLogging_pushTxMsgToQueue(&msg);
}
osDelay(1);
}
Expand Down
1 change: 1 addition & 0 deletions firmware/quadruna/VC/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ elseif ("${TARGET}" STREQUAL "test")
# FakeIO
set(HEADERS_TO_FAKE
"${SHARED_IO_INCLUDE_DIR}/io_log.h"
"${SHARED_IO_INCLUDE_DIR}/io_canLogging.h"
"${CMAKE_CURRENT_SOURCE_DIR}/src/io/io_sbgEllipse.h"
"${CMAKE_CURRENT_SOURCE_DIR}/src/io/io_lowVoltageBattery.h"
"${CMAKE_CURRENT_SOURCE_DIR}/src/io/io_efuse.h"
Expand Down
6 changes: 6 additions & 0 deletions firmware/quadruna/VC/src/app/states/app_allStates.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// app
#include "app_sbgEllipse.h"
#include "app_canTx.h"
#include "app_canAlerts.h"
#include "app_heartbeatMonitor.h"
#include "app_lowVoltageBattery.h"
#include "app_currentSensing.h"
Expand All @@ -12,6 +13,7 @@
// io
#include "io_sbgEllipse.h"
#include "io_imu.h"
#include "io_canLogging.h"

#define IGNORE_HEARTBEAT_CYCLES 3U

Expand Down Expand Up @@ -56,4 +58,8 @@ void app_allStates_runOnTick100Hz(void)
void app_allStates_runOnTick1Hz(void)
{
app_canTx_VC_FlowRate_set(app_pumpControl_getFlowRate());

// Update SD card logging related signals.
app_canTx_VC_CanLoggingRemainingErrors_set(io_canLogging_errorsRemaining());
app_canAlerts_VC_Warning_CanLoggingErrored_set(io_canLogging_errorsRemaining() == 0);
}
41 changes: 17 additions & 24 deletions firmware/quadruna/VC/src/tasks.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,18 +54,16 @@ extern UART_HandleTypeDef huart1;
extern UART_HandleTypeDef huart3;
extern SD_HandleTypeDef hsd1;

static bool loggingEnabled(void); // TODO make this a general io logging happy function

static uint32_t can_logging_overflow_count = 0;
static uint32_t read_count = 0; // TODO debugging variables
static uint32_t write_count = 0; // TODO debugging variables
static bool can_logging_enable = true;
static bool sd_card_present = true;

static void canRxCallback(CanMsg *rx_msg)
{
io_can_pushRxMsgToQueue(rx_msg); // push to queue

if (loggingEnabled() && app_dataCapture_needsLog((uint16_t)rx_msg->std_id, io_time_getCurrentMs()))
if (sd_card_present && app_dataCapture_needsLog((uint16_t)rx_msg->std_id, io_time_getCurrentMs()))
{
io_canLogging_loggingQueuePush(rx_msg); // push to logging queue
read_count++;
Expand Down Expand Up @@ -154,11 +152,6 @@ static const Gpio nprogram_3v3 = { .port = NPROGRAM_3V3_GPIO_Port, .pin
static const Gpio sb_ilck_shdn_sns = { .port = SB_ILCK_SHDN_SNS_GPIO_Port, .pin = SB_ILCK_SHDN_SNS_Pin };
static const Gpio tsms_shdn_sns = { .port = TSMS_SHDN_SNS_GPIO_Port, .pin = TSMS_SHDN_SNS_Pin };

static bool loggingEnabled(void)
{
return !hw_gpio_readPin(&sd_present) && can_logging_enable;
}

const Gpio *id_to_gpio[] = { [VC_GpioNetName_BUZZER_PWR_EN] = &buzzer_pwr_en,
[VC_GpioNetName_BAT_I_SNS_NFLT] = &bat_i_sns_nflt,
[VC_GpioNetName_LED] = &led.gpio,
Expand Down Expand Up @@ -354,27 +347,18 @@ void tasks_preInitWatchdog(void)
{
hw_sd_init(&sd);

if (loggingEnabled())
sd_card_present = !hw_gpio_readPin(&sd_present);
if (sd_card_present)
{
if (io_fileSystem_init() == FILE_OK)
{
io_canLogging_init(&canLogging_config);

// Empirically, mounting slows down (takes ~500ms) at 200 CAN logs on disk.
// This is not correlated to the size of each file.
app_canTx_VC_NumberOfCanDataLogs_set(io_canLogging_getCurrentLog());
app_canAlerts_VC_Warning_HighNumberOfCanDataLogs_set(
io_canLogging_getCurrentLog() > HIGH_NUMBER_OF_LOGS_THRESHOLD);
}
else
{
can_logging_enable = false;
sd_card_present = false;
}
}
else
{
can_logging_enable = false;
}
}

void tasks_init(void)
Expand Down Expand Up @@ -423,6 +407,12 @@ void tasks_init(void)
app_canRx_init();
app_canDataCapture_init();

// Empirically, mounting slows down (takes ~500ms) at 200 CAN logs on disk.
// This is not correlated to the size of each file.
app_canTx_VC_NumberOfCanDataLogs_set(io_canLogging_getCurrentLog());
app_canAlerts_VC_Warning_HighNumberOfCanDataLogs_set(io_canLogging_getCurrentLog() > HIGH_NUMBER_OF_LOGS_THRESHOLD);
app_canAlerts_VC_Warning_CanLoggingSdCardNotPresent_set(!sd_card_present);

app_heartbeatMonitor_init(
heartbeatMonitorChecklist, heartbeatGetters, heartbeatUpdaters, &app_canTx_VC_Heartbeat_set,
heartbeatFaultSetters, heartbeatFaultGetters);
Expand Down Expand Up @@ -539,7 +529,11 @@ _Noreturn void tasks_runCanTx(void)
CanMsg tx_msg;
io_can_popTxMsgFromQueue(&tx_msg);
io_telemMessage_pushMsgtoQueue(&tx_msg);
io_canLogging_pushTxMsgToQueue(&tx_msg);

if (sd_card_present)
{
io_canLogging_loggingQueuePush(&tx_msg);
}

io_can_transmitMsgFromQueue(&tx_msg);
}
Expand All @@ -562,7 +556,6 @@ _Noreturn void tasks_runCanRx(void)
CanMsg rx_msg;
io_can_popRxMsgFromQueue(&rx_msg);
io_telemMessage_pushMsgtoQueue(&rx_msg);
io_canLogging_pushTxMsgToQueue(&rx_msg);

JsonCanMsg jsoncan_rx_msg;
io_jsoncan_copyFromCanMsg(&rx_msg, &jsoncan_rx_msg);
Expand All @@ -572,7 +565,7 @@ _Noreturn void tasks_runCanRx(void)

_Noreturn void tasks_runLogging(void)
{
if (!loggingEnabled())
if (!sd_card_present)
{
osThreadSuspend(osThreadGetId());
}
Expand Down
2 changes: 2 additions & 0 deletions firmware/quadruna/VC/test/test_vcBaseStateMachineTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "fake_io_sbgEllipse.hpp"
#include "fake_io_tsms.hpp"
#include "fake_io_pcm.hpp"
#include "fake_io_canLogging.hpp"

extern "C"
{
Expand Down Expand Up @@ -72,6 +73,7 @@ class VcBaseStateMachineTest : public BaseStateMachineTest
fake_io_efuse_standbyReset_reset();
fake_io_tsms_read_reset();
fake_io_pcm_set_reset();
fake_io_canLogging_errorsRemaining_reset();
}

void SetInitialState(const State *const initial_state)
Expand Down
59 changes: 26 additions & 33 deletions firmware/shared/src/io/io_canLogging.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ static uint8_t queue_buf[QUEUE_BYTES];
static uint32_t current_bootcount;
static int log_fd; // fd for the log file

static uint8_t logging_error_remaining = 5; // number of times to error before stopping logging
static uint8_t logging_error_remaining = 10; // number of times to error before stopping logging

static char current_path[10];

Expand Down Expand Up @@ -63,7 +63,7 @@ static void convertCanMsgToLog(const CanMsg *msg, CanMsgLog *log)

static bool isLoggingEnabled(void)
{
return (logging_error_remaining > 0);
return logging_error_remaining > 0 && config != NULL;
}

int io_canLogging_init(const CanConfig *can_config)
Expand All @@ -82,73 +82,61 @@ int io_canLogging_init(const CanConfig *can_config)
return initLoggingFileSystem();
}

int io_canLogging_pushTxMsgToQueue(const CanMsg *msg)
{
if (!isLoggingEnabled())
{
return 1;
}
static uint32_t tx_overflow_count = 0;

CanMsgLog log;
convertCanMsgToLog(msg, &log);

if (osMessageQueuePut(message_queue_id, &log, 0, 0) != osOK && config->tx_overflow_callback != NULL)
{
// If pushing to the queue failed, the queue is full. Discard the msg and invoke the TX overflow callback.
config->tx_overflow_callback(++tx_overflow_count);
return 1;
}
return 0;
}

int io_canLogging_recordMsgFromQueue(void)
{
if (!isLoggingEnabled())
{
return 1;
}

CanMsgLog tx_msg;
osMessageQueueGet(message_queue_id, &tx_msg, NULL, osWaitForever);

int err = io_fileSystem_write(log_fd, &tx_msg, sizeof(tx_msg));
if (err < 0)
if (err < 0 && logging_error_remaining > 0)
{
logging_error_remaining--;
return 1;
}
return 0;
}

void io_canLogging_loggingQueuePush(CanMsg *rx_msg)
void io_canLogging_loggingQueuePush(CanMsg *msg)
{
static uint32_t rx_overflow_count = 0;
CanMsgLog msg;
if (config == NULL)
if (!isLoggingEnabled())
{
return;
}

if (config->rx_msg_filter != NULL && !config->rx_msg_filter(rx_msg->std_id))
if (config->rx_msg_filter != NULL && !config->rx_msg_filter(msg->std_id))
{
// Early return if we don't care about this msg via configured filter func.
return;
}

static uint32_t overflow_count = 0;
CanMsgLog msg_log;
convertCanMsgToLog(msg, &msg_log);

// We defer reading the CAN RX message to another task by storing the
// message on the CAN RX queue.

convertCanMsgToLog(rx_msg, &msg);
if (osMessageQueuePut(message_queue_id, &msg, 0, 0) != osOK && config->rx_overflow_callback != NULL)
if (osMessageQueuePut(message_queue_id, &msg_log, 0, 0) != osOK && config->rx_overflow_callback != NULL)
{
// If pushing to the queue failed, the queue is full. Discard the msg and invoke the RX overflow callback.
config->rx_overflow_callback(++rx_overflow_count);
config->rx_overflow_callback(++overflow_count);
}
}

int io_canLogging_sync(void)
{
if (!isLoggingEnabled())
{
return 1;
}

// SAVe the seek before close
int err = io_fileSystem_sync(log_fd);
if (err < 0)
if (err < 0 && logging_error_remaining > 0)
{
logging_error_remaining--;
return 1;
Expand All @@ -159,4 +147,9 @@ int io_canLogging_sync(void)
uint32_t io_canLogging_getCurrentLog(void)
{
return current_bootcount;
}

uint32_t io_canLogging_errorsRemaining(void)
{
return logging_error_remaining;
}
25 changes: 18 additions & 7 deletions firmware/shared/src/io/io_canLogging.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
#pragma once

#include "io_can.h"

#ifdef TARGET_EMBEDDED
#include "hw_sd.h"
#endif

// Empirically determined number which corresponds to the time to mount the filesystem, plus the time to create a new
// blank CAN log, taking approximately 500ms. This was found using logfs (not littlefs, there is probably a different
// threshold). The time to mount/create a log both scale linearly with logfs, so for example if there are 400 logs then
// then the time will be ~1s. Proceed with caution creating a significant amount of logs above this threshold.
#define HIGH_NUMBER_OF_LOGS_THRESHOLD (200U)

// Empirically determined number which corresponds to the time to mount the filesystem, plus the time to create a new
// blank CAN log, taking approximately 500ms. This was found using logfs (not littlefs, there is probably a different
Expand All @@ -13,7 +24,7 @@ typedef struct
uint32_t dlc : 4; // Data length code [0,8]
uint32_t timestamp : 17; // sum up to 32 bits
uint8_t data[8];
uint8_t reserved[4]; // reserved byte to round up to 16 bytes
uint8_t reserved[4]; // reserved byte to round up to 16 bytes, TODO: Use this for error checking?
} CanMsgLog;

/**
Expand All @@ -24,11 +35,6 @@ typedef struct
*/
int io_canLogging_init(const CanConfig *can_config);

/**
* Create a push message to
*/
int io_canLogging_pushTxMsgToQueue(const CanMsg *msg);

/**
* write the can message to the sdcard
* pop the massage from queue
Expand All @@ -43,4 +49,9 @@ int io_canLogging_sync(void);
/**
* Return the number of the current CAN data log.
*/
uint32_t io_canLogging_getCurrentLog(void);
uint32_t io_canLogging_getCurrentLog(void);

/**
* Number of remaining errors before logging is disabled.
*/
uint32_t io_canLogging_errorsRemaining(void);
Loading

0 comments on commit 2e11be1

Please sign in to comment.