From: Urban Wallasch Date: Sat, 27 Jul 2019 11:59:39 +0000 (+0200) Subject: * Renamed plugin to teleshmem. X-Git-Tag: v0.1.0~68 X-Git-Url: https://git.packet-gain.de/?a=commitdiff_plain;h=cb0431dbb052f996dc0206cbc7327a74e535c6ff;p=ets2_tele.git * Renamed plugin to teleshmem. * (Re-)implemented logging to file in plugin. * Reactivated telemetry_configuration() callback. --- diff --git a/Makefile b/Makefile index 1ae7701..260e292 100644 --- a/Makefile +++ b/Makefile @@ -14,6 +14,7 @@ SDK_INCLUDES=\ -Isdk/include/eurotrucks2 CFLAGS=-Wall -Wextra -std=c99 -DDEBUG -I. +CPPFLAGS=-Wall -DLOGGING -I. UNAME:= $(shell uname -s) @@ -23,14 +24,14 @@ else LIB_NAME_OPTION=-soname endif -PLUGIN_SRC := telemetry.cpp shmget.c +PLUGIN_SRC := teleshmem.cpp shmget.c .PHONY: all clean -all: telehttpd telelogger telemetry.so +all: telehttpd telelogger teleshmem.so -telemetry.so: $(PLUGIN_SRC) $(SDK_HEADERS) - g++ -o $@ -fPIC -Wall --shared -Wl,$(LIB_NAME_OPTION),$@ $(SDK_INCLUDES) $(PLUGIN_SRC) +teleshmem.so: $(PLUGIN_SRC) $(SDK_HEADERS) + g++ -o $@ $(CPPFLAGS) -fPIC --shared -Wl,$(LIB_NAME_OPTION),$@ $(SDK_INCLUDES) $(PLUGIN_SRC) telehttpd: telehttpd.o shmget.o net.o fserv.o $(CC) $(LDFLAGS) -o $@ -pthread $^ diff --git a/telemetry.cpp b/telemetry.cpp deleted file mode 100644 index aa3802b..0000000 --- a/telemetry.cpp +++ /dev/null @@ -1,465 +0,0 @@ -/** - * @brief Simple logger. - * - * Writes the output into file inside the current directory. - */ - -// Windows stuff. - -#ifdef _WIN32 -# define WINVER 0x0500 -# define _WIN32_WINNT 0x0500 -# include -#endif - -#include -#include -#include -#include -#include - -// SDK - -#include "scssdk_telemetry.h" -#include "eurotrucks2/scssdk_eut2.h" -#include "eurotrucks2/scssdk_telemetry_eut2.h" -#include "amtrucks/scssdk_ats.h" -#include "amtrucks/scssdk_telemetry_ats.h" - -//LINUX - -#include -#include - -#define UNUSED(x) - -/** - * @brief Combined telemetry data and shared memory management. - */ - -#include "telemetry.h" -#include "shmget.h" - -static struct telemetry_state_t *telemetry = NULL; - -static bool init_shm(void) -{ - if ( NULL == telemetry ) { - void *p; - p = init_shmput( TELE_SHM_KEY, sizeof *telemetry ); - telemetry = static_cast(p); - } - assert( NULL != telemetry ); - return NULL != telemetry; -} - -static void drop_shm(void) -{ - if ( NULL != telemetry ) { - release_shm( TELE_SHM_KEY, telemetry ); - telemetry = NULL; - } -} - -/** - * @brief Last timestamp we received. - */ -scs_timestamp_t last_timestamp = static_cast(-1); - - -/** - * @brief Function writting message to the game internal log. - */ -/* -static void log_print(const char *const text, ...) -{ -} - -static void log_line(const char *const text, ...) -{ -} -*/ -// Handling of individual events. - -SCSAPI_VOID telemetry_frame_start(const scs_event_t UNUSED(event), const void *const event_info, const scs_context_t UNUSED(context)) -{ - const struct scs_telemetry_frame_start_t *const info = static_cast(event_info); - - // The following processing of the timestamps is done so the output - // from this plugin has continuous time, it is not necessary otherwise. - - // When we just initialized itself, assume that the time started - // just now. - - if (last_timestamp == static_cast(-1)) { - last_timestamp = info->paused_simulation_time; - } - - // The timer might be sometimes restarted (e.g. after load) while - // we want to provide continuous time on our output. - - if (info->flags & SCS_TELEMETRY_FRAME_START_FLAG_timer_restart) { - last_timestamp = 0; - } - - // Advance the timestamp by delta since last frame. - - telemetry->timestamp += (info->paused_simulation_time - last_timestamp); - last_timestamp = info->paused_simulation_time; - - // The raw values. - - telemetry->raw_rendering_timestamp = info->render_time; - telemetry->raw_simulation_timestamp = info->simulation_time; - telemetry->raw_paused_simulation_timestamp = info->paused_simulation_time; -} - -SCSAPI_VOID telemetry_frame_end(const scs_event_t UNUSED(event), const void *const UNUSED(event_info), const scs_context_t UNUSED(context)) -{ -} - -SCSAPI_VOID telemetry_pause(const scs_event_t event, const void *const UNUSED(event_info), const scs_context_t UNUSED(context)) -{ - telemetry->paused = (event == SCS_TELEMETRY_EVENT_paused); -} - -SCSAPI_VOID telemetry_configuration(const scs_event_t event, const void *const event_info, const scs_context_t UNUSED(context)) -{ -#if 0 - // Here we just print the configuration info. - - const struct scs_telemetry_configuration_t *const info = static_cast(event_info); - log_line("Configuration: %s", info->id); - - for (const scs_named_value_t *current = info->attributes; current->name; ++current) { - log_print(" %s", current->name); - if (current->index != SCS_U32_NIL) { - log_print("[%u]", static_cast(current->index)); - } - log_print(" : "); - switch (current->value.type) { - case SCS_VALUE_TYPE_INVALID: { - log_line("none"); - break; - } - case SCS_VALUE_TYPE_bool: { - log_line("bool = %s", current->value.value_bool.value ? "true" : "false"); - break; - } - case SCS_VALUE_TYPE_s32: { - log_line("s32 = %d", static_cast(current->value.value_s32.value)); - break; - } - case SCS_VALUE_TYPE_u32: { - log_line("u32 = %u", static_cast(current->value.value_u32.value)); - break; - } - case SCS_VALUE_TYPE_u64: { - log_line("u64 = %" SCS_PF_U64, current->value.value_u64.value); - break; - } - case SCS_VALUE_TYPE_float: { - log_line("float = %f", current->value.value_float.value); - break; - } - case SCS_VALUE_TYPE_double: { - log_line("double = %f", current->value.value_double.value); - break; - } - case SCS_VALUE_TYPE_fvector: { - log_line( - "fvector = (%f,%f,%f)", - current->value.value_fvector.x, - current->value.value_fvector.y, - current->value.value_fvector.z - ); - break; - } - case SCS_VALUE_TYPE_dvector: { - log_line( - "dvector = (%f,%f,%f)", - current->value.value_dvector.x, - current->value.value_dvector.y, - current->value.value_dvector.z - ); - break; - } - case SCS_VALUE_TYPE_euler: { - log_line( - "euler = h:%f p:%f r:%f", - current->value.value_euler.heading * 360.0f, - current->value.value_euler.pitch * 360.0f, - current->value.value_euler.roll * 360.0f - ); - break; - } - case SCS_VALUE_TYPE_fplacement: { - log_line( - "fplacement = (%f,%f,%f) h:%f p:%f r:%f", - current->value.value_fplacement.position.x, - current->value.value_fplacement.position.y, - current->value.value_fplacement.position.z, - current->value.value_fplacement.orientation.heading * 360.0f, - current->value.value_fplacement.orientation.pitch * 360.0f, - current->value.value_fplacement.orientation.roll * 360.0f - ); - break; - } - case SCS_VALUE_TYPE_dplacement: { - log_line( - "dplacement = (%f,%f,%f) h:%f p:%f r:%f", - current->value.value_dplacement.position.x, - current->value.value_dplacement.position.y, - current->value.value_dplacement.position.z, - current->value.value_dplacement.orientation.heading * 360.0f, - current->value.value_dplacement.orientation.pitch * 360.0f, - current->value.value_dplacement.orientation.roll * 360.0f - ); - break; - } - case SCS_VALUE_TYPE_string: { - log_line("string = %s", current->value.value_string.value); - break; - } - default: { - log_line("unknown"); - break; - } - } - } - - print_header = true; -#endif -} - -// Handling of individual channels. - -SCSAPI_VOID telemetry_store_dplacement(const scs_string_t name, const scs_u32_t index, const scs_value_t *const value, const scs_context_t context) -{ - assert(value); - assert(value->type == SCS_VALUE_TYPE_dplacement); - assert(context); - telemetry_state_t *const tele = static_cast(context); - - // This callback was registered with the SCS_TELEMETRY_CHANNEL_FLAG_no_value flag - // so it is called even when the value is not available. - if ( !value || value->type != SCS_VALUE_TYPE_dplacement ) { - tele->placement_available = false; - return; - } - tele->placement_available = true; - tele->x = value->value_dplacement.position.x; - tele->y = value->value_dplacement.position.y; - tele->z = value->value_dplacement.position.z; - tele->heading = value->value_dplacement.orientation.heading; - tele->pitch = value->value_dplacement.orientation.pitch; - tele->roll = value->value_dplacement.orientation.roll; -} - -SCSAPI_VOID telemetry_store_float(const scs_string_t name, const scs_u32_t index, const scs_value_t *const value, const scs_context_t context) -{ - // The SCS_TELEMETRY_CHANNEL_FLAG_no_value flag was not provided during registration - // so this callback is only called when a valid value is available. - - assert(value); - assert(value->type == SCS_VALUE_TYPE_float); - assert(context); - *static_cast(context) = value->value_float.value; -} - -SCSAPI_VOID telemetry_store_float_nz(const scs_string_t name, const scs_u32_t index, const scs_value_t *const value, const scs_context_t context) -{ - // The SCS_TELEMETRY_CHANNEL_FLAG_no_value flag was not provided during registration - // so this callback is only called when a valid value is available. - - assert(value); - assert(value->type == SCS_VALUE_TYPE_float); - assert(context); - if (value->value_float.value) - *static_cast(context) = value->value_float.value; -} - -SCSAPI_VOID telemetry_store_s32(const scs_string_t name, const scs_u32_t index, const scs_value_t *const value, const scs_context_t context) -{ - // The SCS_TELEMETRY_CHANNEL_FLAG_no_value flag was not provided during registration - // so this callback is only called when a valid value is available. - - assert(value); - assert(value->type == SCS_VALUE_TYPE_s32); - assert(context); - *static_cast(context) = value->value_s32.value; -} - -SCSAPI_VOID telemetry_store_bool(const scs_string_t name, const scs_u32_t index, const scs_value_t *const value, const scs_context_t context) -{ - // The SCS_TELEMETRY_CHANNEL_FLAG_no_value flag was not provided during registration - // so this callback is only called when a valid value is available. - - assert(value); - assert(value->type == SCS_VALUE_TYPE_bool); - assert(context); - *static_cast(context) = value->value_bool.value; -} - -/** - * @brief Telemetry API initialization function. - * - * See scssdk_telemetry.h - */ -SCSAPI_RESULT scs_telemetry_init(const scs_u32_t version, const scs_telemetry_init_params_t *const params) -{ - // We currently support only one version. - - if (version != SCS_TELEMETRY_VERSION_1_00) { - return SCS_RESULT_unsupported; - } - - const scs_telemetry_init_params_v100_t *const version_params = static_cast(params); - if ( !init_shm() ) { - version_params->common.log(SCS_LOG_TYPE_error, "Unable to initialize shared memory"); - return SCS_RESULT_generic_error; - } - - // Check application version. Note that this example uses fairly basic channels which are likely to be supported - // by any future SCS trucking game however more advanced application might want to at least warn the user if there - // is game or version they do not support. - snprintf( telemetry->game_id, sizeof telemetry->game_id, "%s", version_params->common.game_id ); - telemetry->game_major_ver = SCS_GET_MAJOR_VERSION(version_params->common.game_version); - telemetry->game_minor_ver = SCS_GET_MINOR_VERSION(version_params->common.game_version); - - if (strcmp(version_params->common.game_id, SCS_GAME_ID_EUT2) == 0) { - - // Below the minimum version there might be some missing features (only minor change) or - // incompatible values (major change). - - const scs_u32_t MINIMAL_VERSION = SCS_TELEMETRY_EUT2_GAME_VERSION_1_00; - if (version_params->common.game_version < MINIMAL_VERSION) { - //log_line("WARNING: Too old version of the game, some features might behave incorrectly"); - telemetry->game_ver_warn = true; - } - - // Future versions are fine as long the major version is not changed. - - const scs_u32_t IMPLEMENTED_VERSION = SCS_TELEMETRY_EUT2_GAME_VERSION_CURRENT; - if (SCS_GET_MAJOR_VERSION(version_params->common.game_version) > SCS_GET_MAJOR_VERSION(IMPLEMENTED_VERSION)) { - //log_line("WARNING: Too new major version of the game, some features might behave incorrectly"); - telemetry->game_ver_warn = true; - } - } - else if (strcmp(version_params->common.game_id, SCS_GAME_ID_ATS) == 0) { - - // Bellow the minimum version there might be some missing features (only minor change) or - // incompatible values (major change). - - const scs_u32_t MINIMAL_VERSION = SCS_TELEMETRY_ATS_GAME_VERSION_1_00; - if (version_params->common.game_version < MINIMAL_VERSION) { - //log_line("WARNING: Too old version of the game, some features might behave incorrectly"); - telemetry->game_ver_warn = true; - } - - // Future versions are fine as long the major version is not changed. - - const scs_u32_t IMPLEMENTED_VERSION = SCS_TELEMETRY_ATS_GAME_VERSION_CURRENT; - if (SCS_GET_MAJOR_VERSION(version_params->common.game_version) > SCS_GET_MAJOR_VERSION(IMPLEMENTED_VERSION)) { - //log_line("WARNING: Too new major version of the game, some features might behave incorrectly"); - telemetry->game_ver_warn = true; - } - } - else { - //log_line("WARNING: Unsupported game, some features or values might behave incorrectly"); - telemetry->game_ver_warn = true; - } - - // Register for events. Note that failure to register those basic events - // likely indicates invalid usage of the api or some critical problem. As the - // example requires all of them, we can not continue if the registration fails. - - const bool events_registered = - (version_params->register_for_event(SCS_TELEMETRY_EVENT_frame_start, telemetry_frame_start, NULL) == SCS_RESULT_ok) && - (version_params->register_for_event(SCS_TELEMETRY_EVENT_frame_end, telemetry_frame_end, NULL) == SCS_RESULT_ok) && - (version_params->register_for_event(SCS_TELEMETRY_EVENT_paused, telemetry_pause, NULL) == SCS_RESULT_ok) && - (version_params->register_for_event(SCS_TELEMETRY_EVENT_started, telemetry_pause, NULL) == SCS_RESULT_ok) - ; - if (! events_registered) { - - // Registrations created by unsuccessfull initialization are - // cleared automatically so we can simply exit. - - version_params->common.log(SCS_LOG_TYPE_error, "Unable to register event callbacks"); - return SCS_RESULT_generic_error; - } - - // Register for the configuration info. As this example only prints the retrieved - // data, it can operate even if that fails. - - version_params->register_for_event(SCS_TELEMETRY_EVENT_configuration, telemetry_configuration, NULL); - - // Register for channels. The channel might be missing if the game does not support - // it (SCS_RESULT_not_found) or if does not support the requested type - // (SCS_RESULT_unsupported_type). For purpose of this example we ignore the failues - // so the unsupported channels will remain at theirs default value. - - version_params->register_for_channel(SCS_TELEMETRY_TRUCK_CHANNEL_world_placement, SCS_U32_NIL, SCS_VALUE_TYPE_dplacement, SCS_TELEMETRY_CHANNEL_FLAG_no_value, telemetry_store_dplacement, telemetry); - - version_params->register_for_channel(SCS_TELEMETRY_TRUCK_CHANNEL_speed, SCS_U32_NIL, SCS_VALUE_TYPE_float, SCS_TELEMETRY_CHANNEL_FLAG_none, telemetry_store_float, &telemetry->speed); - version_params->register_for_channel(SCS_TELEMETRY_TRUCK_CHANNEL_cruise_control, SCS_U32_NIL, SCS_VALUE_TYPE_float, SCS_TELEMETRY_CHANNEL_FLAG_none, telemetry_store_float, &telemetry->cctrl); - version_params->register_for_channel(SCS_TELEMETRY_TRUCK_CHANNEL_engine_rpm, SCS_U32_NIL, SCS_VALUE_TYPE_float, SCS_TELEMETRY_CHANNEL_FLAG_none, telemetry_store_float, &telemetry->rpm); - version_params->register_for_channel(SCS_TELEMETRY_TRUCK_CHANNEL_engine_gear, SCS_U32_NIL, SCS_VALUE_TYPE_s32, SCS_TELEMETRY_CHANNEL_FLAG_none, telemetry_store_s32, &telemetry->gear); - version_params->register_for_channel(SCS_TELEMETRY_TRUCK_CHANNEL_displayed_gear, SCS_U32_NIL, SCS_VALUE_TYPE_s32, SCS_TELEMETRY_CHANNEL_FLAG_none, telemetry_store_s32, &telemetry->gear_disp); - version_params->register_for_channel(SCS_TELEMETRY_TRUCK_CHANNEL_effective_clutch, SCS_U32_NIL, SCS_VALUE_TYPE_float, SCS_TELEMETRY_CHANNEL_FLAG_none, telemetry_store_float, &telemetry->clutch_eff); - - version_params->register_for_channel(SCS_TELEMETRY_TRUCK_CHANNEL_fuel, SCS_U32_NIL, SCS_VALUE_TYPE_float, SCS_TELEMETRY_CHANNEL_FLAG_none, telemetry_store_float, &telemetry->fuel); - version_params->register_for_channel(SCS_TELEMETRY_TRUCK_CHANNEL_fuel_warning, SCS_U32_NIL, SCS_VALUE_TYPE_bool, SCS_TELEMETRY_CHANNEL_FLAG_none, telemetry_store_bool, &telemetry->fuel_warn); - version_params->register_for_channel(SCS_TELEMETRY_TRUCK_CHANNEL_fuel_average_consumption, SCS_U32_NIL, SCS_VALUE_TYPE_float, SCS_TELEMETRY_CHANNEL_FLAG_none, telemetry_store_float_nz, &telemetry->fuel_avg); - version_params->register_for_channel(SCS_TELEMETRY_TRUCK_CHANNEL_fuel_range, SCS_U32_NIL, SCS_VALUE_TYPE_float, SCS_TELEMETRY_CHANNEL_FLAG_none, telemetry_store_float, &telemetry->fuel_range); - - version_params->register_for_channel(SCS_TELEMETRY_TRUCK_CHANNEL_odometer, SCS_U32_NIL, SCS_VALUE_TYPE_float, SCS_TELEMETRY_CHANNEL_FLAG_none, telemetry_store_float, &telemetry->odometer); - version_params->register_for_channel(SCS_TELEMETRY_TRUCK_CHANNEL_navigation_distance, SCS_U32_NIL, SCS_VALUE_TYPE_float, SCS_TELEMETRY_CHANNEL_FLAG_none, telemetry_store_float, &telemetry->nav_dist); - version_params->register_for_channel(SCS_TELEMETRY_TRUCK_CHANNEL_navigation_time, SCS_U32_NIL, SCS_VALUE_TYPE_float, SCS_TELEMETRY_CHANNEL_FLAG_none, telemetry_store_float, &telemetry->nav_eta); - version_params->register_for_channel(SCS_TELEMETRY_TRUCK_CHANNEL_navigation_speed_limit, SCS_U32_NIL, SCS_VALUE_TYPE_float, SCS_TELEMETRY_CHANNEL_FLAG_none, telemetry_store_float, &telemetry->nav_slimit); - - // Set the structure with defaults. - - last_timestamp = static_cast(-1); - - // Initially the game is paused. - - telemetry->paused = true; - return SCS_RESULT_ok; -} - -/** - * @brief Telemetry API deinitialization function. - * - * See scssdk_telemetry.h - */ -SCSAPI_VOID scs_telemetry_shutdown(void) -{ - // Any cleanup needed. The registrations will be removed automatically - // so there is no need to do that manually. - - drop_shm(); -} - -// Cleanup - -#ifdef _WIN32 -BOOL APIENTRY DllMain( - HMODULE module, - DWORD reason_for_call, - LPVOID reseved -) -{ - if (reason_for_call == DLL_PROCESS_DETACH) { - drop_shm(); - } - return TRUE; -} -#endif - -#ifdef __linux__ -void __attribute__ ((destructor)) unload(void) -{ - drop_shm(); -} -#endif diff --git a/teleshmem.cpp b/teleshmem.cpp new file mode 100644 index 0000000..83b4385 --- /dev/null +++ b/teleshmem.cpp @@ -0,0 +1,500 @@ +/** + * @brief Simple logger. + * + * Writes the output into file inside the current directory. + */ + +// Windows stuff. + +#ifdef _WIN32 +# define WINVER 0x0500 +# define _WIN32_WINNT 0x0500 +# include +#endif + +#include +#include +#include +#include + +// SDK + +#include "scssdk_telemetry.h" +#include "eurotrucks2/scssdk_eut2.h" +#include "eurotrucks2/scssdk_telemetry_eut2.h" +#include "amtrucks/scssdk_ats.h" +#include "amtrucks/scssdk_telemetry_ats.h" + +//LINUX + +#include +#include + +#define UNUSED(x) + +/** + * @brief Combined telemetry data and shared memory management. + */ + +#include "telemetry.h" +#include "shmget.h" + +static struct telemetry_state_t *telemetry = NULL; + +static bool init_shm(void) +{ + if ( NULL == telemetry ) { + void *p; + p = init_shmput( TELE_SHM_KEY, sizeof *telemetry ); + telemetry = static_cast(p); + } + assert( NULL != telemetry ); + return NULL != telemetry; +} + +static void drop_shm(void) +{ + if ( NULL != telemetry ) { + release_shm( TELE_SHM_KEY, telemetry ); + telemetry = NULL; + } +} + +/** + * @brief Last timestamp we received. + */ +scs_timestamp_t last_timestamp = static_cast(-1); + + +/** + * @brief Logging to external file. + */ + +#ifdef LOGGING +#include + +static FILE *logfp = NULL; + +static int log_open( void ) { + if ( NULL == logfp ) { + int fd; + char tmpl[] = "/tmp/ets2tele_XXXXXX.log"; + if ( 0 > ( fd = mkstemps( tmpl, 4 ) ) ) + return -1; + logfp = fdopen( fd, "a" ); + } + return 0; +} + +static void log_close( void ) { + if ( logfp ) { + fclose( logfp ); + logfp = NULL; + } +} + +static void log_print(const char *const fmt, ...) +{ + if ( logfp ) { + va_list arglist; + va_start( arglist, fmt ); + vfprintf( logfp, fmt, arglist ); + va_end( arglist ); + fflush( logfp ); + } +} +#else +#define log_open() +#define log_close() +#define log_print(...) +#endif // def LOGGING + + +/** + * @brief Handling of individual events. + */ + +SCSAPI_VOID telemetry_frame_start(const scs_event_t UNUSED(event), const void *const event_info, const scs_context_t UNUSED(context)) +{ + const struct scs_telemetry_frame_start_t *const info = static_cast(event_info); + + // The following processing of the timestamps is done so the output + // from this plugin has continuous time, it is not necessary otherwise. + + // When we just initialized itself, assume that the time started + // just now. + + if (last_timestamp == static_cast(-1)) { + last_timestamp = info->paused_simulation_time; + } + + // The timer might be sometimes restarted (e.g. after load) while + // we want to provide continuous time on our output. + + if (info->flags & SCS_TELEMETRY_FRAME_START_FLAG_timer_restart) { + last_timestamp = 0; + } + + // Advance the timestamp by delta since last frame. + + telemetry->timestamp += (info->paused_simulation_time - last_timestamp); + last_timestamp = info->paused_simulation_time; + + // The raw values. + + telemetry->raw_rendering_timestamp = info->render_time; + telemetry->raw_simulation_timestamp = info->simulation_time; + telemetry->raw_paused_simulation_timestamp = info->paused_simulation_time; +} + +SCSAPI_VOID telemetry_frame_end(const scs_event_t UNUSED(event), const void *const UNUSED(event_info), const scs_context_t UNUSED(context)) +{ +} + +SCSAPI_VOID telemetry_pause(const scs_event_t event, const void *const UNUSED(event_info), const scs_context_t UNUSED(context)) +{ + telemetry->paused = (event == SCS_TELEMETRY_EVENT_paused); +} + +SCSAPI_VOID telemetry_configuration(const scs_event_t event, const void *const event_info, const scs_context_t UNUSED(context)) +{ + // Here we just print the configuration info. + + const struct scs_telemetry_configuration_t *const info = static_cast(event_info); + log_print("Configuration: %s\n", info->id); + + for (const scs_named_value_t *current = info->attributes; current->name; ++current) { + log_print(" %s", current->name); + if (current->index != SCS_U32_NIL) { + log_print("[%u]", static_cast(current->index)); + } + log_print(" : "); + switch (current->value.type) { + case SCS_VALUE_TYPE_INVALID: { + log_print("none\n"); + break; + } + case SCS_VALUE_TYPE_bool: { + log_print("bool = %s\n", current->value.value_bool.value ? "true" : "false"); + break; + } + case SCS_VALUE_TYPE_s32: { + log_print("s32 = %d\n", static_cast(current->value.value_s32.value)); + break; + } + case SCS_VALUE_TYPE_u32: { + log_print("u32 = %u\n", static_cast(current->value.value_u32.value)); + break; + } + case SCS_VALUE_TYPE_u64: { + log_print("u64 = %" SCS_PF_U64 "\n", current->value.value_u64.value); + break; + } + case SCS_VALUE_TYPE_float: { + log_print("float = %f\n", current->value.value_float.value); + break; + } + case SCS_VALUE_TYPE_double: { + log_print("double = %f\n", current->value.value_double.value); + break; + } + case SCS_VALUE_TYPE_fvector: { + log_print( + "fvector = (%f,%f,%f)\n", + current->value.value_fvector.x, + current->value.value_fvector.y, + current->value.value_fvector.z + ); + break; + } + case SCS_VALUE_TYPE_dvector: { + log_print( + "dvector = (%f,%f,%f)\n", + current->value.value_dvector.x, + current->value.value_dvector.y, + current->value.value_dvector.z + ); + break; + } + case SCS_VALUE_TYPE_euler: { + log_print( + "euler = h:%f p:%f r:%f\n", + current->value.value_euler.heading * 360.0f, + current->value.value_euler.pitch * 360.0f, + current->value.value_euler.roll * 360.0f + ); + break; + } + case SCS_VALUE_TYPE_fplacement: { + log_print( + "fplacement = (%f,%f,%f) h:%f p:%f r:%f\n", + current->value.value_fplacement.position.x, + current->value.value_fplacement.position.y, + current->value.value_fplacement.position.z, + current->value.value_fplacement.orientation.heading * 360.0f, + current->value.value_fplacement.orientation.pitch * 360.0f, + current->value.value_fplacement.orientation.roll * 360.0f + ); + break; + } + case SCS_VALUE_TYPE_dplacement: { + log_print( + "dplacement = (%f,%f,%f) h:%f p:%f r:%f\n", + current->value.value_dplacement.position.x, + current->value.value_dplacement.position.y, + current->value.value_dplacement.position.z, + current->value.value_dplacement.orientation.heading * 360.0f, + current->value.value_dplacement.orientation.pitch * 360.0f, + current->value.value_dplacement.orientation.roll * 360.0f + ); + break; + } + case SCS_VALUE_TYPE_string: { + log_print("string = %s\n", current->value.value_string.value); + break; + } + default: { + log_print("unknown\n"); + break; + } + } + } +} + +// Handling of individual channels. + +SCSAPI_VOID telemetry_store_dplacement(const scs_string_t name, const scs_u32_t index, const scs_value_t *const value, const scs_context_t context) +{ + assert(value); + assert(value->type == SCS_VALUE_TYPE_dplacement); + assert(context); + telemetry_state_t *const tele = static_cast(context); + + // This callback was registered with the SCS_TELEMETRY_CHANNEL_FLAG_no_value flag + // so it is called even when the value is not available. + if ( !value || value->type != SCS_VALUE_TYPE_dplacement ) { + tele->placement_available = false; + return; + } + tele->placement_available = true; + tele->x = value->value_dplacement.position.x; + tele->y = value->value_dplacement.position.y; + tele->z = value->value_dplacement.position.z; + tele->heading = value->value_dplacement.orientation.heading; + tele->pitch = value->value_dplacement.orientation.pitch; + tele->roll = value->value_dplacement.orientation.roll; +} + +SCSAPI_VOID telemetry_store_float(const scs_string_t name, const scs_u32_t index, const scs_value_t *const value, const scs_context_t context) +{ + // The SCS_TELEMETRY_CHANNEL_FLAG_no_value flag was not provided during registration + // so this callback is only called when a valid value is available. + + assert(value); + assert(value->type == SCS_VALUE_TYPE_float); + assert(context); + *static_cast(context) = value->value_float.value; +} + +SCSAPI_VOID telemetry_store_float_nz(const scs_string_t name, const scs_u32_t index, const scs_value_t *const value, const scs_context_t context) +{ + // The SCS_TELEMETRY_CHANNEL_FLAG_no_value flag was not provided during registration + // so this callback is only called when a valid value is available. + + assert(value); + assert(value->type == SCS_VALUE_TYPE_float); + assert(context); + if (value->value_float.value) + *static_cast(context) = value->value_float.value; +} + +SCSAPI_VOID telemetry_store_s32(const scs_string_t name, const scs_u32_t index, const scs_value_t *const value, const scs_context_t context) +{ + // The SCS_TELEMETRY_CHANNEL_FLAG_no_value flag was not provided during registration + // so this callback is only called when a valid value is available. + + assert(value); + assert(value->type == SCS_VALUE_TYPE_s32); + assert(context); + *static_cast(context) = value->value_s32.value; +} + +SCSAPI_VOID telemetry_store_bool(const scs_string_t name, const scs_u32_t index, const scs_value_t *const value, const scs_context_t context) +{ + // The SCS_TELEMETRY_CHANNEL_FLAG_no_value flag was not provided during registration + // so this callback is only called when a valid value is available. + + assert(value); + assert(value->type == SCS_VALUE_TYPE_bool); + assert(context); + *static_cast(context) = value->value_bool.value; +} + +/** + * @brief Telemetry API initialization function. + * + * See scssdk_telemetry.h + */ +SCSAPI_RESULT scs_telemetry_init(const scs_u32_t version, const scs_telemetry_init_params_t *const params) +{ + log_open(); + + // We currently support only one version. + + if (version != SCS_TELEMETRY_VERSION_1_00) { + return SCS_RESULT_unsupported; + } + + const scs_telemetry_init_params_v100_t *const version_params = static_cast(params); + if ( !init_shm() ) { + log_print( "ERROR: Unable to initialize shared memory\n" ); + version_params->common.log(SCS_LOG_TYPE_error, "Unable to initialize shared memory"); + return SCS_RESULT_generic_error; + } + + // Check application version. Note that this example uses fairly basic channels which are likely to be supported + // by any future SCS trucking game however more advanced application might want to at least warn the user if there + // is game or version they do not support. + snprintf( telemetry->game_id, sizeof telemetry->game_id, "%s", version_params->common.game_id ); + telemetry->game_major_ver = SCS_GET_MAJOR_VERSION(version_params->common.game_version); + telemetry->game_minor_ver = SCS_GET_MINOR_VERSION(version_params->common.game_version); + + if (strcmp(version_params->common.game_id, SCS_GAME_ID_EUT2) == 0) { + + // Below the minimum version there might be some missing features (only minor change) or + // incompatible values (major change). + + const scs_u32_t MINIMAL_VERSION = SCS_TELEMETRY_EUT2_GAME_VERSION_1_00; + if (version_params->common.game_version < MINIMAL_VERSION) { + log_print("WARNING: Too old version of the game, some features might behave incorrectly\n"); + telemetry->game_ver_warn = true; + } + + // Future versions are fine as long the major version is not changed. + + const scs_u32_t IMPLEMENTED_VERSION = SCS_TELEMETRY_EUT2_GAME_VERSION_CURRENT; + if (SCS_GET_MAJOR_VERSION(version_params->common.game_version) > SCS_GET_MAJOR_VERSION(IMPLEMENTED_VERSION)) { + log_print("WARNING: Too new major version of the game, some features might behave incorrectly\n"); + telemetry->game_ver_warn = true; + } + } + else if (strcmp(version_params->common.game_id, SCS_GAME_ID_ATS) == 0) { + + // Bellow the minimum version there might be some missing features (only minor change) or + // incompatible values (major change). + + const scs_u32_t MINIMAL_VERSION = SCS_TELEMETRY_ATS_GAME_VERSION_1_00; + if (version_params->common.game_version < MINIMAL_VERSION) { + log_print("WARNING: Too old version of the game, some features might behave incorrectly\n"); + telemetry->game_ver_warn = true; + } + + // Future versions are fine as long the major version is not changed. + + const scs_u32_t IMPLEMENTED_VERSION = SCS_TELEMETRY_ATS_GAME_VERSION_CURRENT; + if (SCS_GET_MAJOR_VERSION(version_params->common.game_version) > SCS_GET_MAJOR_VERSION(IMPLEMENTED_VERSION)) { + log_print("WARNING: Too new major version of the game, some features might behave incorrectly\n"); + telemetry->game_ver_warn = true; + } + } + else { + log_print("WARNING: Unsupported game, some features or values might behave incorrectly\n"); + telemetry->game_ver_warn = true; + } + + // Register for events. Note that failure to register those basic events + // likely indicates invalid usage of the api or some critical problem. As the + // example requires all of them, we can not continue if the registration fails. + + const bool events_registered = + (version_params->register_for_event(SCS_TELEMETRY_EVENT_frame_start, telemetry_frame_start, NULL) == SCS_RESULT_ok) && + (version_params->register_for_event(SCS_TELEMETRY_EVENT_frame_end, telemetry_frame_end, NULL) == SCS_RESULT_ok) && + (version_params->register_for_event(SCS_TELEMETRY_EVENT_paused, telemetry_pause, NULL) == SCS_RESULT_ok) && + (version_params->register_for_event(SCS_TELEMETRY_EVENT_started, telemetry_pause, NULL) == SCS_RESULT_ok) + ; + if (! events_registered) { + + // Registrations created by unsuccessfull initialization are + // cleared automatically so we can simply exit. + + version_params->common.log(SCS_LOG_TYPE_error, "Unable to register event callbacks"); + return SCS_RESULT_generic_error; + } + + // Register for the configuration info. As this example only prints the retrieved + // data, it can operate even if that fails. + + version_params->register_for_event(SCS_TELEMETRY_EVENT_configuration, telemetry_configuration, NULL); + + // Register for channels. The channel might be missing if the game does not support + // it (SCS_RESULT_not_found) or if does not support the requested type + // (SCS_RESULT_unsupported_type). For purpose of this example we ignore the failues + // so the unsupported channels will remain at theirs default value. + + version_params->register_for_channel(SCS_TELEMETRY_TRUCK_CHANNEL_world_placement, SCS_U32_NIL, SCS_VALUE_TYPE_dplacement, SCS_TELEMETRY_CHANNEL_FLAG_no_value, telemetry_store_dplacement, telemetry); + + version_params->register_for_channel(SCS_TELEMETRY_TRUCK_CHANNEL_speed, SCS_U32_NIL, SCS_VALUE_TYPE_float, SCS_TELEMETRY_CHANNEL_FLAG_none, telemetry_store_float, &telemetry->speed); + version_params->register_for_channel(SCS_TELEMETRY_TRUCK_CHANNEL_cruise_control, SCS_U32_NIL, SCS_VALUE_TYPE_float, SCS_TELEMETRY_CHANNEL_FLAG_none, telemetry_store_float, &telemetry->cctrl); + version_params->register_for_channel(SCS_TELEMETRY_TRUCK_CHANNEL_engine_rpm, SCS_U32_NIL, SCS_VALUE_TYPE_float, SCS_TELEMETRY_CHANNEL_FLAG_none, telemetry_store_float, &telemetry->rpm); + version_params->register_for_channel(SCS_TELEMETRY_TRUCK_CHANNEL_engine_gear, SCS_U32_NIL, SCS_VALUE_TYPE_s32, SCS_TELEMETRY_CHANNEL_FLAG_none, telemetry_store_s32, &telemetry->gear); + version_params->register_for_channel(SCS_TELEMETRY_TRUCK_CHANNEL_displayed_gear, SCS_U32_NIL, SCS_VALUE_TYPE_s32, SCS_TELEMETRY_CHANNEL_FLAG_none, telemetry_store_s32, &telemetry->gear_disp); + version_params->register_for_channel(SCS_TELEMETRY_TRUCK_CHANNEL_effective_clutch, SCS_U32_NIL, SCS_VALUE_TYPE_float, SCS_TELEMETRY_CHANNEL_FLAG_none, telemetry_store_float, &telemetry->clutch_eff); + + version_params->register_for_channel(SCS_TELEMETRY_TRUCK_CHANNEL_fuel, SCS_U32_NIL, SCS_VALUE_TYPE_float, SCS_TELEMETRY_CHANNEL_FLAG_none, telemetry_store_float, &telemetry->fuel); + version_params->register_for_channel(SCS_TELEMETRY_TRUCK_CHANNEL_fuel_warning, SCS_U32_NIL, SCS_VALUE_TYPE_bool, SCS_TELEMETRY_CHANNEL_FLAG_none, telemetry_store_bool, &telemetry->fuel_warn); + version_params->register_for_channel(SCS_TELEMETRY_TRUCK_CHANNEL_fuel_average_consumption, SCS_U32_NIL, SCS_VALUE_TYPE_float, SCS_TELEMETRY_CHANNEL_FLAG_none, telemetry_store_float_nz, &telemetry->fuel_avg); + version_params->register_for_channel(SCS_TELEMETRY_TRUCK_CHANNEL_fuel_range, SCS_U32_NIL, SCS_VALUE_TYPE_float, SCS_TELEMETRY_CHANNEL_FLAG_none, telemetry_store_float, &telemetry->fuel_range); + + version_params->register_for_channel(SCS_TELEMETRY_TRUCK_CHANNEL_odometer, SCS_U32_NIL, SCS_VALUE_TYPE_float, SCS_TELEMETRY_CHANNEL_FLAG_none, telemetry_store_float, &telemetry->odometer); + version_params->register_for_channel(SCS_TELEMETRY_TRUCK_CHANNEL_navigation_distance, SCS_U32_NIL, SCS_VALUE_TYPE_float, SCS_TELEMETRY_CHANNEL_FLAG_none, telemetry_store_float, &telemetry->nav_dist); + version_params->register_for_channel(SCS_TELEMETRY_TRUCK_CHANNEL_navigation_time, SCS_U32_NIL, SCS_VALUE_TYPE_float, SCS_TELEMETRY_CHANNEL_FLAG_none, telemetry_store_float, &telemetry->nav_eta); + version_params->register_for_channel(SCS_TELEMETRY_TRUCK_CHANNEL_navigation_speed_limit, SCS_U32_NIL, SCS_VALUE_TYPE_float, SCS_TELEMETRY_CHANNEL_FLAG_none, telemetry_store_float, &telemetry->nav_slimit); + + // Set the structure with defaults. + + last_timestamp = static_cast(-1); + + // Initially the game is paused. + + telemetry->paused = true; + return SCS_RESULT_ok; +} + +/** + * @brief Telemetry API deinitialization function. + * + * See scssdk_telemetry.h + */ +SCSAPI_VOID scs_telemetry_shutdown(void) +{ + // Any cleanup needed. The registrations will be removed automatically + // so there is no need to do that manually. + + drop_shm(); + log_close(); +} + +// Cleanup + +#ifdef _WIN32 +BOOL APIENTRY DllMain( + HMODULE module, + DWORD reason_for_call, + LPVOID reseved +) +{ + if (reason_for_call == DLL_PROCESS_DETACH) { + drop_shm(); + log_close(); + } + return TRUE; +} +#endif + +#ifdef __linux__ +void __attribute__ ((destructor)) unload(void) +{ + drop_shm(); + log_close(); +} +#endif