diff --git a/.cache/clangd/index/duckdb.h.63D53BDB55D39D0A.idx b/.cache/clangd/index/duckdb.h.63D53BDB55D39D0A.idx new file mode 100644 index 0000000..41cb69d Binary files /dev/null and b/.cache/clangd/index/duckdb.h.63D53BDB55D39D0A.idx differ diff --git a/.cache/clangd/index/lapinfo.c.F9698B9BD3295BA4.idx b/.cache/clangd/index/lapinfo.c.F9698B9BD3295BA4.idx new file mode 100644 index 0000000..93cb040 Binary files /dev/null and b/.cache/clangd/index/lapinfo.c.F9698B9BD3295BA4.idx differ diff --git a/.cache/clangd/index/lapinfo.h.702DAF4EDA4E0E47.idx b/.cache/clangd/index/lapinfo.h.702DAF4EDA4E0E47.idx new file mode 100644 index 0000000..69a30bb Binary files /dev/null and b/.cache/clangd/index/lapinfo.h.702DAF4EDA4E0E47.idx differ diff --git a/.cache/clangd/index/main.c.8770424B51F7A3DB.idx b/.cache/clangd/index/main.c.8770424B51F7A3DB.idx new file mode 100644 index 0000000..54fd646 Binary files /dev/null and b/.cache/clangd/index/main.c.8770424B51F7A3DB.idx differ diff --git a/Makefile b/Makefile index 3509c4a..dd76e24 100644 --- a/Makefile +++ b/Makefile @@ -3,32 +3,51 @@ SRCDIR = src BUILDDIR = build VENDORDIR = vendor -target = linux +target ?= linux +profile ?= debug +valgrind ?= false ifeq ($(target), windows) CC = x86_64-w64-mingw32-gcc - CXX = x86_64-w64-mingw32-g++ EXT = .exe - - CFLAGS = -Wall -o2 -I$(SRCDIR) -I$(VENDORDIR)/mariadb/include -I$(VENDORDIR)/duckdb/windows/include + + OPT_FLAGS = -O2 -DNDEBUG + + INCLUDES = -I$(SRCDIR) -I$(VENDORDIR)/mariadb/include -I$(VENDORDIR)/duckdb/windows/include LIBS = -L$(VENDORDIR)/mariadb/lib -llibmariadb -L$(VENDORDIR)/duckdb/windows/lib -lduckdb + + # Command to copy DLL after build + POST_BUILD_CMD = @cp $(VENDORDIR)/duckdb/windows/lib/duckdb.dll $(BUILDDIR) + else CC = gcc - CXX = g++ EXT = - CFLAGS = -Wall -O2 -I$(SRCDIR) `pkg-config --cflags libmariadb` -I$(VENDORDIR)/duckdb/linux/include - LIBS = `pkg-config --libs libmariadb` -L$(VENDORDIR)/duckdb/linux/lib -lduckdb_static + + INCLUDES = -I$(SRCDIR) `pkg-config --cflags libmariadb` -I$(VENDORDIR)/duckdb/linux/include + LIBS = `pkg-config --libs libmariadb` -L$(VENDORDIR)/duckdb/linux/lib -lduckdb + POST_BUILD_CMD = @echo "Build complete." + + # Profile Logic + ifeq ($(profile), release) + OPT_FLAGS = -O3 -DNDEBUG -march=native + else + OPT_FLAGS = -O0 -g -DDEBUG -Wall -Wextra + endif endif +# Combine Flags +CFLAGS = -Wall $(OPT_FLAGS) $(INCLUDES) + SRC = $(shell find $(SRCDIR) -name "*.c") OBJ = $(SRC:$(SRCDIR)/%.c=$(BUILDDIR)/%.o) all: $(BUILDDIR)/$(APPNAME)$(EXT) $(BUILDDIR)/$(APPNAME)$(EXT): $(OBJ) - @echo "Linking $@" + @echo "Linking $@ (Target: $(target), Profile: $(profile))" @mkdir -p $(dir $@) $(CC) $(OBJ) -o $@ $(LIBS) + $(POST_BUILD_CMD) $(BUILDDIR)/%.o: $(SRCDIR)/%.c @echo "Compiling $<" @@ -38,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 - ./$(BUILDDIR)/$(APPNAME)$(EXT) + $(RUN_CMD) ./data/Circuit.duckdb .PHONY: all clean run diff --git a/compile_commands.json b/compile_commands.json new file mode 100644 index 0000000..a8f4cf3 --- /dev/null +++ b/compile_commands.json @@ -0,0 +1,44 @@ +[ + { + "arguments": [ + "/usr/bin/gcc", + "-Wall", + "-O0", + "-g", + "-DDEBUG", + "-Wall", + "-Wextra", + "-Isrc", + "-I/usr/include/mysql/", + "-Ivendor/duckdb/linux/include", + "-c", + "-o", + "build/lapinfo.o", + "src/lapinfo.c" + ], + "directory": "/home/tom/Dev/HardCompound", + "file": "/home/tom/Dev/HardCompound/src/lapinfo.c", + "output": "/home/tom/Dev/HardCompound/build/lapinfo.o" + }, + { + "arguments": [ + "/usr/bin/gcc", + "-Wall", + "-O0", + "-g", + "-DDEBUG", + "-Wall", + "-Wextra", + "-Isrc", + "-I/usr/include/mysql/", + "-Ivendor/duckdb/linux/include", + "-c", + "-o", + "build/main.o", + "src/main.c" + ], + "directory": "/home/tom/Dev/HardCompound", + "file": "/home/tom/Dev/HardCompound/src/main.c", + "output": "/home/tom/Dev/HardCompound/build/main.o" + } +] diff --git a/data/Circuit.duckdb b/data/Circuit.duckdb new file mode 100644 index 0000000..83f1cc7 Binary files /dev/null and b/data/Circuit.duckdb differ diff --git a/data/Silverstone Circuit_P_2025-12-13T19_18_41Z.duckdb b/data/Silverstone Circuit_P_2025-12-13T19_18_41Z.duckdb deleted file mode 100644 index 3f5f043..0000000 Binary files a/data/Silverstone Circuit_P_2025-12-13T19_18_41Z.duckdb and /dev/null differ diff --git a/src/exit_code.h b/src/exit_code.h new file mode 100644 index 0000000..d582963 --- /dev/null +++ b/src/exit_code.h @@ -0,0 +1,9 @@ +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; diff --git a/src/lapinfo.c b/src/lapinfo.c new file mode 100644 index 0000000..f7894dd --- /dev/null +++ b/src/lapinfo.c @@ -0,0 +1,18 @@ +#include +#include + +#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); +} diff --git a/src/lapinfo.h b/src/lapinfo.h new file mode 100644 index 0000000..e1e7d30 --- /dev/null +++ b/src/lapinfo.h @@ -0,0 +1,31 @@ +#pragma once + +#include + +typedef enum { + BOOL, + INT, + FLOAT, + DOUBLE, +} TelemetryDataType; + +typedef struct { + TelemetryDataType type; + int8_t frequency; + void *data; + uint64_t *validity; + uint32_t data_size; + uint32_t data_last_index; +} TelemetryInfo; + +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; +} LapInfo; + +void free_telemetry_info(TelemetryInfo *t); +void destroyLapinfo(LapInfo *info); diff --git a/src/main.c b/src/main.c index 130b059..e82be1f 100644 --- a/src/main.c +++ b/src/main.c @@ -1,6 +1,196 @@ +#include +#include #include +#include +#include +#include -int main(){ - printf("Hello World"); - return 0; +#include "exit_code.h" +#include "lapinfo.h" + +bool is_valid_path(const char *path) { + if (path == NULL) { + return false; + } + + 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; +} + +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); + } + duckdb_destroy_result(&result); +} + +int main(int argc, char **argv) { + printf("\nHello from HardCompound!\n"); + + if (argc < 2) { + printf("You need to specify a duckdb file path! Terminating!\n"); + exit(ERR_ARGUMENT); + } + + if (!is_valid_path(argv[1])) { + printf("The file you gave is not valid. Exiting!"); + exit(ERR_FILE); + } + + 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(ERR_DATABASE); + } + + duckdb_connection conn; + if (duckdb_connect(db, &conn) == DuckDBError) { + printf("Error connecting to the duckdb database! Terminating!\n"); + duckdb_close(&db); + exit(ERR_CONNECTION); + } + + // 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"); + duckdb_disconnect(&conn); + duckdb_close(&db); + exit(ERR_QUERY); + } + + // set column + duckdb_vector col1 = duckdb_data_chunk_get_vector(data_chunk, 0); + int32_t *col1_data = (int32_t *)duckdb_vector_get_data(col1); + uint64_t *col1_validity = duckdb_vector_get_validity(col1); + idx_t row_count = duckdb_data_chunk_get_size(data_chunk); // number of rows from the data chunk + + for (idx_t row = 0; row < row_count; row++) { + if (duckdb_validity_row_is_valid(col1_validity, 0)) { + lap_count = col1_data[0]; + } + } + + duckdb_destroy_data_chunk(&data_chunk); + duckdb_destroy_result(&lap_count_res); + + printf("Lap count: %d\n", lap_count); + LapInfo *session_data = calloc(lap_count, sizeof(LapInfo)); + double *lap_timestamps = calloc(lap_count, sizeof(double)); + + if (!session_data) { + printf("Session data failed to allocate!"); + exit(ERR_MEMORY); + } + + if (!lap_timestamps) { + printf("Lap timestaps array failed to init!"); + exit(ERR_MEMORY); + } + + duckdb_result lap_timestamps_res; + if (duckdb_query(conn, "Select ts as DOUBLE from Lap\n", &lap_timestamps_res) == DuckDBError) { + exit(ERR_QUERY); + } + + int current_index = 0; + while (true) { + duckdb_data_chunk data_chunk_2 = duckdb_fetch_chunk(lap_timestamps_res); + if (!data_chunk_2) { + break; + } + + idx_t current_chunk_size = duckdb_data_chunk_get_size(data_chunk_2); + + duckdb_vector col2 = duckdb_data_chunk_get_vector(data_chunk_2, 0); + double *ts_data = (double *)duckdb_vector_get_data(col2); + uint64_t *col2_validity = duckdb_vector_get_validity(col2); + + for (idx_t row = 0; row < current_chunk_size; row++) { + if (!col2_validity || duckdb_validity_row_is_valid(col2_validity, row)) { + if (current_index < lap_count) { + lap_timestamps[current_index++] = ts_data[row]; + } + } + } + + duckdb_destroy_data_chunk(&data_chunk_2); + } + + duckdb_destroy_result(&lap_timestamps_res); + + for (int i = 0; i < lap_count; i++) { + printf("%f, ", lap_timestamps[i]); + } + printf("\n"); + + free(session_data); + printf("Shutting down HardCompound!\n"); + + printf("Closing database and any connection.\n"); + duckdb_disconnect(&conn); + duckdb_close(&db); + + return EXIT_OK; } diff --git a/src/tables.c b/src/tables.c new file mode 100644 index 0000000..2f42e43 --- /dev/null +++ b/src/tables.c @@ -0,0 +1,111 @@ +const char *channel_tables[] = {"Ambient Temperature", + "Brake Pos", + "Brake Pos Unfiltered", + "Brake Thickness", + "Brakes Air Temp", + "Brakes Force", + "Brakes Temp", + "Clutch Pos", + "Clutch Pos Unfiltered", + "Clutch RPM", + "Drag", + "Engine Oil Temp", + "Engine RPM", + "Engine Water Temp", + "FFB Output", + "Front3rdDeflection", + "FrontDownForce", + "FrontRideHeight", + "FrontWingHeight", + "Fuel Level", + "G Force Lat", + "G Force Long", + "G Force Vert", + "GPS Latitude", + "GPS Longitude", + "GPS Speed", + "GPS Time", + "Ground Speed", + "Lap Dist", + "Lateral Acceleration", + "Longitudinal Acceleration", + "OverheatingState", + "Path Lateral", + "ReadDownForce", + "Rear3rdDeflection", + "RearRideHeight", + "Regen Rate", + "RideHeights", + "SoC", + "Steered Angle", + "Steering Pos", + "Steering Pos Unfiltered", + "Steering Shaft Torque", + "Susp Pos", + "Throttle Pos", + "Throttle Pos Unfiltered", + "Time Behind Next", + "Total Dist", + "Track Edge", + "Track Temperature", + "Turbo Boost Pressure", + "Tyres Wear", + "TyresCarcassTemp", + "TyresPressure", + "TyresRimTemp", + "TyresRubberTemp", + "TyresTempCentre", + "TyresTempLeft", + "TyresTempRight", + "Virtual Energy", + "Wheel Speed", + "Wind Heading", + "Wind Speed", + "Yaw Rate"}; + +const int channel_table_count = sizeof(channel_tables) / sizeof(channel_tables[0]); + +const char *event_tables[] = {"ABS", + "ABSLevel", + "AntiStall Activated", + "Best LapTime", + "Best Sector1", + "Best Sector2", + "Brake Bias Rear", + "Brake Migration", + "CloudDarkness", + "Current LapTime", + "Current Sector", + "Current Sector1", + "Current Sector2", + "Engine Max RPM", + "Finish Status", + "FrontFlapActivated", + "FuelMixtureMap", + "Gear", + "Headlights State", + "In Pits", + "Lap", + "Lap Time", + "Last Sector1", + "Last Sector2", + "LastImpactMagnitude", + "LaunchControlActive", + "Minimum Path Wetness", + "OffpathWetness", + "RearFlapActivated", + "RearFlapLegalStatus", + "Sector1 Flag", + "Sector2 Flag", + "Sector3 Flag", + "Speed Limiter", + "SurfaceTypes", + "TC", + "TCCut", + "TCLevel", + "TCSlipAngle", + "TyresCompound", + "WheelsDetached", + "Yellow Flag State"}; + +const int event_table_count = sizeof(event_tables) / sizeof(event_tables[0]);