Compare commits

...

15 Commits

10 changed files with 661 additions and 80 deletions

View File

@@ -5,6 +5,7 @@ VENDORDIR = vendor
target ?= linux
profile ?= debug
valgrind ?= false
ifeq ($(target), windows)
CC = x86_64-w64-mingw32-gcc
@@ -56,7 +57,13 @@ $(BUILDDIR)/%.o: $(SRCDIR)/%.c
clean:
rm -rf $(BUILDDIR)
ifeq ($(valgrind), true)
RUN_CMD = valgrind --leak-check=full ./$(BUILDDIR)/$(APPNAME)$(EXT)
else
RUN_CMD = ./$(BUILDDIR)/$(APPNAME)$(EXT)
endif
run: all
valgrind --leak-check=full ./$(BUILDDIR)/$(APPNAME)$(EXT) ./data/Circuit.duckdb
$(RUN_CMD) ./data/Circuit.duckdb
.PHONY: all clean run

View File

@@ -8,6 +8,12 @@ A Le Mans Ultimate telemetry viewer with a hosted database.
`sudo pacman -S mariadb-libs`
### Valgrind
`sudo pacman -S valgrind`
## Running:
`make run` -> run a linux binary with a sample data
`make run valgrind=true` -> run the linux binary with valgrind
## Compiling:
@@ -27,4 +33,4 @@ A Le Mans Ultimate telemetry viewer with a hosted database.
#### Linux (Default):
`make target=linux`
`make target=linux` or just `make`

56
src/array_utils.c Normal file
View File

@@ -0,0 +1,56 @@
#include <duckdb.h>
#include <stdio.h>
#include "arrays.h"
void allocate_array_for_db_data(float_array *array, const char *table, duckdb_connection *conn) {
char query[100];
sprintf(query, "Select count(*) from \"%s\"", table);
duckdb_result result;
duckdb_query(*conn, query, &result);
duckdb_data_chunk chunk = duckdb_fetch_chunk(result);
if (!chunk) {
return;
}
duckdb_vector col1 = duckdb_data_chunk_get_vector(chunk, 0);
int32_t *col1_data = (int32_t *)duckdb_vector_get_data(col1);
uint64_t *col1_validity = duckdb_vector_get_validity(col1);
if (duckdb_validity_row_is_valid(col1_validity, 0)) {
printf("capycity of array: %d\n", col1_data[0]);
float_array_allocate(array, col1_data[0]);
}
duckdb_destroy_data_chunk(&chunk);
duckdb_destroy_result(&result);
}
void get_data_from_db(const char *query, duckdb_connection *conn, float_array *info) {
duckdb_result result;
duckdb_query(*conn, query, &result);
while (true) {
duckdb_data_chunk data_chunk = duckdb_fetch_chunk(result);
if (!data_chunk) {
break;
}
idx_t row_count = duckdb_data_chunk_get_size(data_chunk); // number of rows from the data chunk
// set column
duckdb_vector col1 = duckdb_data_chunk_get_vector(data_chunk, 0);
void *col1_data = duckdb_vector_get_data(col1);
uint64_t *col1_validity = duckdb_vector_get_validity(col1);
for (idx_t row = 0; row < row_count; row++) {
if (duckdb_validity_row_is_valid(col1_validity, row)) {
float_array_set(info, ((float *)col1_data)[row], -1);
}
}
duckdb_destroy_data_chunk(&data_chunk);
}
duckdb_destroy_result(&result);
}

6
src/array_utils.h Normal file
View File

@@ -0,0 +1,6 @@
#include <duckdb.h>
#include "arrays.h"
void allocate_array_for_db_data(float_array *array, const char *table, duckdb_connection *conn);
void get_data_from_db(const char *query, duckdb_connection *conn, float_array *info);

330
src/arrays.c Normal file
View File

@@ -0,0 +1,330 @@
#include "arrays.h"
#include <stdint.h>
#include <stdlib.h>
bool bool_array_get(const bool_array *array, int32_t index) {
if (index >= 0 && index < array->length) {
return array->items[index];
}
return 0;
}
//@param index: set to -1 to put it at the end of the array
void bool_array_set(bool_array *array, const bool value, int32_t index) {
if (!array) {
return;
}
if (array->capacity <= array->length && index <= 0 && index < array->length) {
return;
}
if (index == -1) {
array->items[array->length - 1] = value;
return;
}
array->items[index] = value;
}
char char_array_get(const char_array *array, int32_t index) {
if (index >= 0 && index < array->length) {
return array->items[index];
}
return 0;
}
//@param index: set to -1 to put it at the end of the array
void char_array_set(char_array *array, const char value, int32_t index) {
if (!array) {
return;
}
if (array->capacity <= array->length) {
return;
}
if (index == -1) {
array->items[array->length - 1] = value;
return;
}
array->items[index] = value;
}
int8_t int8_array_get(const int8_array *array, int32_t index) {
if (index >= 0 && index < array->length) {
return array->items[index];
}
return 0;
}
//@param index: set to -1 to put it at the end of the array
void int8_array_set(int8_array *array, const int8_t value, int32_t index) {
if (!array) {
return;
}
if (array->capacity <= array->length) {
return;
}
if (index == -1) {
array->items[array->length - 1] = value;
return;
}
array->items[index] = value;
}
int16_t int16_array_get(const int16_array *array, int32_t index) {
if (index >= 0 && index < array->length) {
return array->items[index];
}
return 0;
}
//@param index: set to -1 to put it at the end of the array
void int16_array_set(int16_array *array, const int16_t value, int32_t index) {
if (!array) {
return;
}
if (array->capacity <= array->length) {
return;
}
if (index == -1) {
array->items[array->length - 1] = value;
return;
}
array->items[index] = value;
}
int32_t int32_array_get(const int32_array *array, int32_t index) {
if (index >= 0 && index < array->length) {
return array->items[index];
}
return 0;
}
//@param index: set to -1 to put it at the end of the array
void int32_array_set(int32_array *array, const int32_t value, int32_t index) {
if (!array) {
return;
}
if (array->capacity <= array->length) {
return;
}
if (index == -1) {
array->items[array->length - 1] = value;
return;
}
array->items[index] = value;
}
int64_t int64_array_get(const int64_array *array, int32_t index) {
if (index >= 0 && index < array->length) {
return array->items[index];
}
return 0;
}
//@param index: set to -1 to put it at the end of the array
void int64_array_set(int64_array *array, const int64_t value, int32_t index) {
if (!array) {
return;
}
if (array->capacity <= array->length) {
return;
}
if (index == -1) {
array->items[array->length - 1] = value;
return;
}
array->items[index] = value;
}
uint8_t uint8_array_get(const uint8_array *array, int32_t index) {
if (index >= 0 && index < array->length) {
return array->items[index];
}
return 0;
}
//@param index: set to -1 to put it at the end of the array
void uint8_array_set(uint8_array *array, const uint8_t value, int32_t index) {
if (!array) {
return;
}
if (array->capacity <= array->length) {
return;
}
if (index == -1) {
array->items[array->length - 1] = value;
return;
}
array->items[index] = value;
}
uint16_t uint16_array_get(const uint16_array *array, int32_t index) {
if (index >= 0 && index < array->length) {
return array->items[index];
}
return 0;
}
//@param index: set to -1 to put it at the end of the array
void uint16_array_set(uint16_array *array, const uint16_t value, int32_t index) {
if (!array) {
return;
}
if (array->capacity <= array->length) {
return;
}
if (index == -1) {
array->items[array->length - 1] = value;
return;
}
array->items[index] = value;
}
uint32_t uint32_array_get(const uint32_array *array, int32_t index) {
if (index >= 0 && index < array->length) {
return array->items[index];
}
return 0;
}
//@param index: set to -1 to put it at the end of the array
void uint32_array_set(uint32_array *array, const uint32_t value, int32_t index) {
if (!array) {
return;
}
if (array->capacity <= array->length) {
return;
}
if (index == -1) {
array->items[array->length - 1] = value;
return;
}
array->items[index] = value;
}
uint64_t uint64_array_get(const uint64_array *array, int32_t index) {
if (index >= 0 && index < array->length) {
return array->items[index];
}
return 0;
}
//@param index: set to -1 to put it at the end of the array
void uint64_array_set(uint64_array *array, const uint64_t value, int32_t index) {
if (!array) {
return;
}
if (array->capacity <= array->length) {
return;
}
if (index == -1) {
array->items[array->length - 1] = value;
return;
}
array->items[index] = value;
}
float float_array_get(const float_array *array, int32_t index) {
if (index >= 0 && index < array->length) {
return array->items[index];
}
return 0;
}
//@param index: set to -1 to put it at the end of the array
void float_array_set(float_array *array, const float value, int32_t index) {
if (!array) {
return;
}
if (array->capacity <= array->length) {
return;
}
if (index == -1) {
array->items[array->length - 1] = value;
array->length++;
return;
}
array->items[index] = value;
}
void float_array_allocate(float_array *array, int32_t capacity) {
array->items = calloc(capacity, sizeof(float));
array->capacity = capacity;
}
void float_array_init(float_array *array) {
array->capacity = 0;
array->length = 0;
}
void float_array_free(float_array *array) {
free(array->items);
float_array_init(array);
}
double double_array_get(const double_array *array, int32_t index) {
if (index >= 0 && index < array->length) {
return array->items[index];
}
return 0;
}
//@param index: set to -1 to put it at the end of the array
void double_array_set(double_array *array, const double value, int32_t index) {
if (!array) {
return;
}
if (array->capacity <= array->length) {
return;
}
if (index == -1) {
array->items[array->length - 1] = value;
return;
}
array->items[index] = value;
}

123
src/arrays.h Normal file
View File

@@ -0,0 +1,123 @@
#ifndef ARRAYS_H
#define ARRAYS_H
#include <stdbool.h>
#include <stdint.h>
// --- Boolean ---
typedef struct {
bool *items;
int32_t length;
int32_t capacity;
} bool_array;
bool bool_array_get(const bool_array *array, int32_t index);
void bool_array_set(bool_array *array, const bool value, int32_t index);
// --- Char (Strings/Bytes) ---
typedef struct {
char *items;
int32_t length;
int32_t capacity;
} char_array;
char char_array_get(const char_array *array, int32_t index);
void char_array_set(char_array *array, const char value, int32_t index);
// --- Signed Integers ---
typedef struct {
int8_t *items;
int32_t length;
int32_t capacity;
} int8_array;
int8_t int8_array_get(const int8_array *array, int32_t index);
void int8_array_set(int8_array *array, const int8_t value, int32_t index);
typedef struct {
int16_t *items;
int32_t length;
int32_t capacity;
} int16_array;
int16_t int16_array_get(const int16_array *array, int32_t index);
void int16_array_set(int16_array *array, const int16_t value, int32_t index);
typedef struct {
int32_t *items;
int32_t length;
int32_t capacity;
} int32_array;
int32_t int32_array_get(const int32_array *array, int32_t index);
void int32_array_set(int32_array *array, const int32_t value, int32_t index);
typedef struct {
int64_t *items;
int32_t length;
int32_t capacity;
} int64_array;
int64_t int64_array_get(const int64_array *array, int32_t index);
void int64_array_set(int64_array *array, const int64_t value, int32_t index);
// --- Unsigned Integers ---
typedef struct {
uint8_t *items;
int32_t length;
int32_t capacity;
} uint8_array;
uint8_t uint8_array_get(const uint8_array *array, int32_t index);
void uint8_array_set(uint8_array *array, const uint8_t value, int32_t index);
typedef struct {
uint16_t *items;
int32_t length;
int32_t capacity;
} uint16_array;
uint16_t uint16_array_get(const uint16_array *array, int32_t index);
void uint16_array_set(uint16_array *array, const uint16_t value, int32_t index);
typedef struct {
uint32_t *items;
int32_t length;
int32_t capacity;
} uint32_array;
uint32_t uint32_array_get(const uint32_array *array, int32_t index);
void uint32_array_set(uint32_array *array, const uint32_t value, int32_t index);
typedef struct {
uint64_t *items;
int32_t length;
int32_t capacity;
} uint64_array;
uint64_t uint64_array_get(const uint64_array *array, int32_t index);
void uint64_array_set(uint64_array *array, const uint64_t value, int32_t index);
// --- Floating Point ---
typedef struct {
float *items;
int32_t length;
int32_t capacity;
} float_array;
float float_array_get(const float_array *array, int32_t index);
void float_array_set(float_array *array, const float value, int32_t index);
void float_array_allocate(float_array *array, int32_t capacity);
void float_array_init(float_array *array);
void float_array_free(float_array *array);
typedef struct {
double *items;
int32_t length;
int32_t capacity;
} double_array;
double double_array_get(const double_array *array, int32_t index);
void double_array_set(double_array *array, const double value, int32_t index);
#endif

14
src/exit_code.h Normal file
View File

@@ -0,0 +1,14 @@
#ifndef EXIT_CODE_H
#define EXIT_CODE_H
typedef enum {
EXIT_OK = 0,
ERR_ARGUMENT = 1,
ERR_FILE = 2,
ERR_DATABASE = 3,
ERR_CONNECTION = 4,
ERR_MEMORY = 5,
ERR_QUERY = 6,
} exit_code;
#endif

View File

@@ -3,16 +3,10 @@
#include "lapinfo.h"
void free_telemetry_info(TelemetryInfo *t) {
free(t->data);
free(t->validity);
// free(t);
}
void destroyLapinfo(LapInfo *info) {
free_telemetry_info(&info->throttle_pos);
free_telemetry_info(&info->brake_pos);
free_telemetry_info(&info->steering_pos);
free_telemetry_info(&info->speed);
// free(info);
free(info->throttle_pos.items);
free(info->brake_pos.items);
free(info->steering_pos.items);
free(info->speed.items);
free(info);
}

View File

@@ -1,7 +1,10 @@
#pragma once
#ifndef LAPINFO_H
#define LAPINFO_H
#include <stdint.h>
#include "arrays.h"
typedef enum {
BOOL,
INT,
@@ -21,11 +24,13 @@ typedef struct {
typedef struct {
int lap_number;
double start_time;
TelemetryInfo throttle_pos; // freq: 50
TelemetryInfo brake_pos; // freq: 50
TelemetryInfo steering_pos; // freq: 100
TelemetryInfo speed;
float_array throttle_pos;
float_array brake_pos;
float_array steering_pos; // freq: 100
float_array speed;
} LapInfo;
void free_telemetry_info(TelemetryInfo *t);
void destroyLapinfo(LapInfo *info);
#endif

View File

@@ -3,88 +3,74 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include "array_utils.h"
#include "arrays.h"
#include "exit_code.h"
#include "lapinfo.h"
void get_data_from_db(const char *query, duckdb_connection *conn, TelemetryInfo *info) {
duckdb_result result;
duckdb_query(*conn, query, &result);
while (true) {
duckdb_data_chunk data_chunk = duckdb_fetch_chunk(result);
if (!data_chunk) {
break;
}
idx_t row_count = duckdb_data_chunk_get_size(data_chunk); // number of rows from the data chunk
// set column
duckdb_vector col1 = duckdb_data_chunk_get_vector(data_chunk, 0);
void *col1_data = duckdb_vector_get_data(col1);
uint64_t *col1_validity = duckdb_vector_get_validity(col1);
for (idx_t row = 0; row < row_count; row++) {
if (duckdb_validity_row_is_valid(col1_validity, row)) {
switch (info->type) {
case FLOAT:
((float *)info->data)[info->data_last_index] = ((float *)col1_data)[row];
info->data_last_index++;
break;
case BOOL:
((bool *)info->data)[info->data_last_index] = ((bool *)col1_data)[row];
info->data_last_index++;
break;
case DOUBLE:
((double *)info->data)[info->data_last_index] = ((double *)col1_data)[row];
info->data_last_index++;
break;
case INT:
((int32_t *)info->data)[info->data_last_index] = ((int32_t *)col1_data)[row];
info->data_last_index++;
break;
}
// printf("%f, ", ((float *)col1_data)[row]);
} else {
printf("NULL");
}
}
duckdb_destroy_data_chunk(&data_chunk);
bool is_valid_path(const char *path) {
if (path == NULL) {
return false;
}
duckdb_destroy_result(&result);
const char *dot = strrchr(path, '.'); // find the last dot which is for the extension
if (!dot || strcmp(dot, ".duckdb") != 0) {
return false;
}
struct stat path_stat;
if (stat(path, &path_stat) != 0) {
return false; // path does not exist
}
if (!S_ISREG(path_stat.st_mode)) {
return false; // checks if it is a regular file
}
return true;
}
int main(int argc, char **argv) {
printf("Hello from HardCompound!\n");
duckdb_database db;
duckdb_connection conn;
duckdb_result lap_count_res;
LapInfo *session_data = NULL;
double *lap_timestamps = NULL;
printf("\nHello from HardCompound!\n");
if (argc < 2) {
printf("You need to specify a duckdb file path! Terminating!\n");
exit(1);
goto cleanup;
}
if (!is_valid_path(argv[1])) {
printf("The file you gave is not valid. Exiting!");
goto cleanup;
}
printf("First argument: %s\n", argv[1]);
duckdb_database db;
if (duckdb_open(argv[1], &db) == DuckDBError) {
printf("Error opening duckdb file, terminating!\n");
exit(2);
goto cleanup;
}
duckdb_connection conn;
if (duckdb_connect(db, &conn) == DuckDBError) {
printf("Error connecting to the duckdb database! Terminating!\n");
exit(3);
goto cleanup;
}
// get how many laps is in the session and allocate that many LapInfo-s
duckdb_result lap_count_res;
int32_t lap_count = 0;
duckdb_query(conn, "Select count(value) from Lap\n", &lap_count_res);
duckdb_data_chunk data_chunk = duckdb_fetch_chunk(lap_count_res);
if (!data_chunk) {
printf("Error in retrieving laps");
exit(4);
goto cleanup;
}
// set column
@@ -103,14 +89,22 @@ int main(int argc, char **argv) {
duckdb_destroy_result(&lap_count_res);
printf("Lap count: %d\n", lap_count);
LapInfo *session_data = malloc(sizeof(LapInfo) * lap_count);
session_data = calloc(lap_count, sizeof(LapInfo));
lap_timestamps = calloc(lap_count, sizeof(double));
double lap_timestamps[lap_count];
memset(lap_timestamps, 0, sizeof(lap_timestamps));
if (!session_data) {
printf("Session data failed to allocate!");
goto cleanup;
}
if (!lap_timestamps) {
printf("Lap timestaps array failed to init!");
goto cleanup;
}
duckdb_result lap_timestamps_res;
if (duckdb_query(conn, "Select ts as DOUBLE from Lap\n", &lap_timestamps_res) == DuckDBError) {
exit(5);
goto cleanup;
}
int current_index = 0;
@@ -144,12 +138,58 @@ int main(int argc, char **argv) {
}
printf("\n");
free(session_data);
printf("Shutting down HardCompound!\n");
// throttle
float_array throttle_data_all;
float_array_init(&throttle_data_all);
allocate_array_for_db_data(&throttle_data_all, "Throttle Pos", &conn);
get_data_from_db("Select value as FLOAT from \"Throttle Pos\"\n", &conn, &throttle_data_all);
for (int32_t i = 0; i < throttle_data_all.length; ++i) {
// printf(" %f", float_array_get(&throttle_data_all, i));
}
// brake
float_array brake_data_all;
float_array_init(&brake_data_all);
allocate_array_for_db_data(&brake_data_all, "Brake Pos", &conn);
get_data_from_db("Select value as FLOAT from \"Brake Pos\"\n", &conn, &brake_data_all);
for (int32_t i = 0; i < brake_data_all.length; ++i) {
// printf(" %f", float_array_get(&brake_data_all, i));
}
// steering
float_array steering_data_all;
float_array_init(&steering_data_all);
allocate_array_for_db_data(&steering_data_all, "Steering Pos", &conn);
get_data_from_db("Select value as FLOAT from \"Steering Pos\"\n", &conn, &steering_data_all);
for (int32_t i = 0; i < steering_data_all.length; ++i) {
// printf(" %f", float_array_get(&steering_data_all, i));
}
// select avg(((value1+value2+value3+value4)/4)*3.6) as speed from "Wheel Speed"
// speed
float_array speed_data_all;
float_array_init(&speed_data_all);
allocate_array_for_db_data(&speed_data_all, "Wheel Speed", &conn);
get_data_from_db("select ((value1+value2+value3+value4)/4)*3.6 as speed from \"Wheel Speed\"", &conn,
&speed_data_all);
for (int32_t i = 0; i < speed_data_all.length; ++i) {
// printf(" %f", float_array_get(&speed_data_all, i));
}
printf("\nShutting down HardCompound!\n");
printf("Closing database and any connection.\n");
goto cleanup;
exit:
return EXIT_OK;
cleanup:
free(NULL);
free(session_data);
free(lap_timestamps);
float_array_free(&throttle_data_all);
float_array_free(&brake_data_all);
float_array_free(&steering_data_all);
float_array_free(&speed_data_all);
duckdb_disconnect(&conn);
duckdb_close(&db);
return 0;
goto exit;
}