From b0461e1109911659ba24551d7dc3c448548282a4 Mon Sep 17 00:00:00 2001 From: "Timothy Rule (VM/EMT3)" Date: Fri, 13 Dec 2024 15:49:29 +0100 Subject: [PATCH] Add CSV input example model. Signed-off-by: Timothy Rule (VM/EMT3) --- Makefile | 1 + dse/modelc/examples/CMakeLists.txt | 1 + dse/modelc/examples/csv/CMakeLists.txt | 48 ++++++++++ dse/modelc/examples/csv/model.c | 116 ++++++++++++++++++++++++ dse/modelc/examples/csv/model.yaml | 27 ++++++ dse/modelc/examples/csv/simulation.yaml | 46 ++++++++++ dse/modelc/examples/csv/valueset.csv | 5 + tests/testscript/e2e/csv.txtar | 26 ++++++ tests/testscript/e2e/gateway.txtar | 2 +- 9 files changed, 271 insertions(+), 1 deletion(-) create mode 100644 dse/modelc/examples/csv/CMakeLists.txt create mode 100644 dse/modelc/examples/csv/model.c create mode 100644 dse/modelc/examples/csv/model.yaml create mode 100644 dse/modelc/examples/csv/simulation.yaml create mode 100644 dse/modelc/examples/csv/valueset.csv create mode 100644 tests/testscript/e2e/csv.txtar diff --git a/Makefile b/Makefile index ac82e07..557faaf 100644 --- a/Makefile +++ b/Makefile @@ -61,6 +61,7 @@ export TESTSCRIPT_E2E_DIR ?= tests/testscript/e2e TESTSCRIPT_E2E_FILES = \ $(TESTSCRIPT_E2E_DIR)/minimal.txtar \ $(TESTSCRIPT_E2E_DIR)/extended.txtar \ + $(TESTSCRIPT_E2E_DIR)/csv.txtar \ $(TESTSCRIPT_E2E_DIR)/transform.txtar \ $(TESTSCRIPT_E2E_DIR)/binary.txtar \ $(TESTSCRIPT_E2E_DIR)/ncodec.txtar \ diff --git a/dse/modelc/examples/CMakeLists.txt b/dse/modelc/examples/CMakeLists.txt index 06a8956..ec9172e 100644 --- a/dse/modelc/examples/CMakeLists.txt +++ b/dse/modelc/examples/CMakeLists.txt @@ -5,6 +5,7 @@ # Example Models. add_subdirectory(binary) +add_subdirectory(csv) add_subdirectory(extended) add_subdirectory(gateway) add_subdirectory(gdb) diff --git a/dse/modelc/examples/csv/CMakeLists.txt b/dse/modelc/examples/csv/CMakeLists.txt new file mode 100644 index 0000000..6c318ab --- /dev/null +++ b/dse/modelc/examples/csv/CMakeLists.txt @@ -0,0 +1,48 @@ +# Copyright 2024 Robert Bosch GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.21) + +project(Csv) +set(MODEL_PATH "examples/csv") + +include(FetchContent) +FetchContent_Declare(dse_clib + URL $ENV{DSE_CLIB_URL} + HTTP_USERNAME $ENV{GHE_USER} + HTTP_PASSWORD $ENV{GHE_TOKEN} +) +FetchContent_MakeAvailable(dse_clib) +set(DSE_CLIB_INCLUDE_DIR ${dse_clib_SOURCE_DIR}) + +add_library(csv SHARED + model.c +) +target_include_directories(csv + PRIVATE + ${DSE_CLIB_INCLUDE_DIR} + ../../../.. +) +target_link_libraries(csv + PRIVATE + $<$:${modelc_link_lib}> +) +install(TARGETS csv + LIBRARY DESTINATION + ${MODEL_PATH}/lib + RUNTIME DESTINATION + ${MODEL_PATH}/lib + COMPONENT + csv +) +install( + FILES + model.yaml + simulation.yaml + valueset.csv + DESTINATION + ${MODEL_PATH}/data + COMPONENT + csv +) diff --git a/dse/modelc/examples/csv/model.c b/dse/modelc/examples/csv/model.c new file mode 100644 index 0000000..d97d8f8 --- /dev/null +++ b/dse/modelc/examples/csv/model.c @@ -0,0 +1,116 @@ +// Copyright 2024 Robert Bosch GmbH +// +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include +#include +#include +#include +#include + + +#define CSV_LINE_MAXLEN 1024 +#define CSV_DELIMITER ",;" +#define CSV_FILE_ENVAR "CSV_FILE" + + +typedef struct { + ModelDesc model; + + /* CSV specific members. */ + const char* file_name; + FILE* file; + char line[CSV_LINE_MAXLEN]; + double timestamp; + SignalVector* sv; +} CsvModelDesc; + + +static bool read_csv_line(CsvModelDesc* c) +{ + c->timestamp = -1; // Set a safe time (i.e. no valid value set). + while (c->timestamp < 0) { + // Read a line. + char* line = fgets(c->line, CSV_LINE_MAXLEN, c->file); + if (line == NULL) return false; + if (strlen(line) == 0) continue; + + // Get the timestamp. + log_trace("csv::%s", c->line); + errno = 0; + double ts = strtod(c->line, NULL); + if (errno) { + log_error("Bad line, timestamp conversion failed"); + log_error(c->line); + continue; + } + if (ts >= 0) c->timestamp = ts; + } + + return true; +} + + +ModelDesc* model_create(ModelDesc* model) +{ + /* Extend the ModelDesc object (using a shallow copy). */ + CsvModelDesc* c = calloc(1, sizeof(CsvModelDesc)); + memcpy(c, model, sizeof(ModelDesc)); + if (model->sv && model->sv->name) { + c->sv = model->sv; + } else { + log_fatal("No signal vector configured"); + } + + /* Open and prepare the CSV file (i.e. consume the first line). */ + c->file_name = getenv(CSV_FILE_ENVAR); + c->file = fopen(c->file_name, "r"); + if (c->file == NULL) { + log_fatal("Unable to open CSV file (%s)", c->file_name); + } + fgets(c->line, CSV_LINE_MAXLEN, c->file); + log_trace("csv::%s", c->line); + + /* Read the first CSV value set. */ + read_csv_line(c); + + /* Return the extended object. */ + return (ModelDesc*)c; +} + + +int model_step(ModelDesc* model, double* model_time, double stop_time) +{ + CsvModelDesc* c = (CsvModelDesc*)model; + + /* Apply value sets from CSV. */ + while ((c->timestamp >= 0) && (c->timestamp <= *model_time)) { + size_t idx = 0; + char* _saveptr = NULL; + char* _valueptr; + strtok_r(c->line, CSV_DELIMITER, &_saveptr); // Consume the timestamp. + while ((_valueptr = strtok_r(NULL, CSV_DELIMITER, &_saveptr))) { + errno = 0; + double v = strtod(_valueptr, NULL); + if (errno == 0) { + c->sv->scalar[idx] = v; + } + if (++idx >= c->sv->count) break; /* Too many values. */ + } + /* Load the next line. */ + if (read_csv_line(c) == false) break; + } + + /* Advance the model time. */ + *model_time = stop_time; + return 0; +} + + +void model_destroy(ModelDesc* model) +{ + CsvModelDesc* c = (CsvModelDesc*)model; + if (c->file) fclose(c->file); +} diff --git a/dse/modelc/examples/csv/model.yaml b/dse/modelc/examples/csv/model.yaml new file mode 100644 index 0000000..1dee048 --- /dev/null +++ b/dse/modelc/examples/csv/model.yaml @@ -0,0 +1,27 @@ +# Copyright 2024 Robert Bosch GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +--- +kind: Model +metadata: + name: Csv +spec: + runtime: + dynlib: + - os: linux + arch: amd64 + path: lib/libcsv.so + - os: linux + arch: x86 + path: lib/libcsv.so + - os: windows + arch: x64 + path: lib/libcsv.dll + - os: windows + arch: x86 + path: lib/libcsv.dll + channels: + - alias: scalar + selectors: + channel: scalar_channel diff --git a/dse/modelc/examples/csv/simulation.yaml b/dse/modelc/examples/csv/simulation.yaml new file mode 100644 index 0000000..7da54df --- /dev/null +++ b/dse/modelc/examples/csv/simulation.yaml @@ -0,0 +1,46 @@ +# Copyright 2024 Robert Bosch GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +--- +kind: Stack +metadata: + name: csv_stack +spec: + connection: + transport: + redispubsub: + uri: redis://localhost:6379 + timeout: 60 + models: + - name: simbus + model: + name: simbus + channels: + - name: scalar_channel + expectedModelCount: 1 + - name: csv_inst + uid: 42 + model: + name: Csv + runtime: + env: + CSV_FILE: data/valueset.csv + channels: + - name: scalar_channel + alias: scalar +--- +kind: Model +metadata: + name: simbus +--- +kind: SignalGroup +metadata: + name: scalar_channel + labels: + channel: scalar_channel +spec: + signals: + - signal: A + - signal: B + - signal: C diff --git a/dse/modelc/examples/csv/valueset.csv b/dse/modelc/examples/csv/valueset.csv new file mode 100644 index 0000000..760c13b --- /dev/null +++ b/dse/modelc/examples/csv/valueset.csv @@ -0,0 +1,5 @@ +Timestamp;A;B;C +0.0000;1.0;2.0;3.0 +0.0005;-1.1;2.1;3.1 +0.0010;1.2;-2.2;3.2 +0.0015;1.3;2.3;-3.3 diff --git a/tests/testscript/e2e/csv.txtar b/tests/testscript/e2e/csv.txtar new file mode 100644 index 0000000..217eff2 --- /dev/null +++ b/tests/testscript/e2e/csv.txtar @@ -0,0 +1,26 @@ +env NAME=csv_inst +env SIM=dse/modelc/build/_out/examples/csv + + +# TEST: csv example model +exec sh -e $WORK/test.sh + +stderr 'Using Valgrind' +stdout 'Load YAML File: data/simulation.yaml' +stdout 'Loading symbol: model_create ... ok' +stdout 'Loading symbol: model_step ... ok' +stdout 'Loading symbol: model_destroy ... ok' +stdout 'Run the Simulation ...' +stdout 'Controller exit ...' +stdout 'SignalValue: 3289118412 = 1.300000 \[name=A\]' +stdout 'SignalValue: 3339451269 = 2.300000 \[name=B\]' +stdout 'SignalValue: 3322673650 = -3.300000 \[name=C\]' + +-- test.sh -- +SIMER="${SIMER:-ghcr.io/boschglobal/dse-simer:latest}" +docker run --name simer -i --rm \ + -v $ENTRYHOSTDIR/$SIM:/sim \ + $SIMER \ + -valgrind $NAME \ + -env $NAME:SIMBUS_LOGLEVEL=2 \ + -logger 2 diff --git a/tests/testscript/e2e/gateway.txtar b/tests/testscript/e2e/gateway.txtar index fd727fc..b14de5f 100644 --- a/tests/testscript/e2e/gateway.txtar +++ b/tests/testscript/e2e/gateway.txtar @@ -35,5 +35,5 @@ docker run --name simer -i --rm \ --network=host \ $SIMER \ -env simbus:SIMBUS_LOGLEVEL=4 \ - -stack local + -stack local \ -timeout 5