monocoque initial commit

This commit is contained in:
Paul Dino Jones
2022-10-31 15:11:02 +00:00
commit 9325e207be
65 changed files with 6352 additions and 0 deletions
+95
View File
@@ -0,0 +1,95 @@
#include <FastLED.h>
#include "../../monocoque/simulatorapi/simdata.h"
#define BYTE_SIZE sizeof(SimData)
#define LED_PIN 7
#define NUM_LEDS 6
#define BRIGHTNESS 40
CRGB leds[NUM_LEDS];
SimData sd;
int maxrpm = 0;
int rpm = 0;
int numlights = NUM_LEDS;
int pin = LED_PIN;
int lights[6];
void setup()
{
Serial.begin(9600);
FastLED.addLeds<WS2812, LED_PIN, GRB>(leds, NUM_LEDS);
FastLED.setMaxPowerInVoltsAndMilliamps(5, 500);
FastLED.setBrightness(BRIGHTNESS);
for (int i = 0; i < numlights; i++)
{
leds[i] = CRGB ( 0, 0, 0);
lights[i] = 0;
}
FastLED.clear();
sd.rpms = 0;
sd.maxrpm = 6500;
sd.altitude = 10;
sd.pulses = 40000;
sd.velocity = 10;
}
void loop()
{
int l = 0;
char buff[BYTE_SIZE];
if (Serial.available() >= BYTE_SIZE)
{
Serial.readBytes(buff, BYTE_SIZE);
memcpy(&sd, &buff, BYTE_SIZE);
rpm = sd.rpms;
maxrpm = sd.maxrpm;
}
while (l < numlights)
{
lights[l] = 0;
l++;
}
l = -1;
int rpmlights = 0;
while (rpm > rpmlights)
{
if (l>=0)
{
lights[l] = 1;
}
l++;
rpmlights = rpmlights + (((maxrpm-250)/numlights));
}
l = 0;
FastLED.clear();
while (l < numlights)
{
if (l >= numlights / 2)
{
leds[l] = CRGB ( 0, 0, 255);
}
if (l < numlights / 2)
{
leds[l] = CRGB ( 0, 255, 0);
}
if (l == numlights - 1)
{
leds[l] = CRGB ( 255, 0, 0);
}
if (lights[l] <= 0)
{
leds[l] = CRGB ( 0, 0, 0);
}
FastLED.show();
l++;
}
}
@@ -0,0 +1,93 @@
#include <FastLED.h>
#include "../../monocoque/simulatorapi/simdata.h"
#define BYTE_SIZE sizeof(SimData)
#define LED_PIN 7
#define NUM_LEDS 6
CRGB leds[NUM_LEDS];
SimData sd;
int maxrpm = 0;
int rpm = 0;
int numlights = NUM_LEDS;
int pin = LED_PIN;
int lights[6];
void setup() {
Serial.begin(9600);
FastLED.addLeds<WS2812, LED_PIN, GRB>(leds, NUM_LEDS);
FastLED.setMaxPowerInVoltsAndMilliamps(5, 500);
FastLED.setBrightness(40);
for (int i = 0; i < numlights; i++)
{
leds[i] = CRGB ( 0, 0, 0);
lights[i] = 0;
}
FastLED.clear();
sd.rpms = 0;
sd.maxrpm = 6500;
sd.altitude = 10;
sd.pulses = 40000;
sd.velocity = 10;
}
void loop() {
int l = 0;
char buff[BYTE_SIZE];
if (Serial.available() >= BYTE_SIZE) {
Serial.readBytes(buff, BYTE_SIZE);
memcpy(&sd, &buff, BYTE_SIZE);
rpm = sd.rpms;
maxrpm = sd.maxrpm;
}
while (l < numlights)
{
lights[l] = 0;
l++;
}
l = -1;
int rpmlights = 0;
while (rpm > rpmlights)
{
if (l>=0)
{
lights[l] = 1;
}
l++;
rpmlights = rpmlights + (((maxrpm-250)/numlights));
}
l = 0;
FastLED.clear();
while (l < numlights)
{
if (l >= numlights / 2)
{
leds[l] = CRGB ( 0, 0, 255);
}
if (l < numlights / 2)
{
leds[l] = CRGB ( 0, 255, 0);
}
if (l == numlights - 1)
{
leds[l] = CRGB ( 255, 0, 0);
}
if (lights[l] <= 0)
{
leds[l] = CRGB ( 0, 0, 0);
}
FastLED.show();
l++;
}
}
+22
View File
@@ -0,0 +1,22 @@
set(devices_source_files
simdevice.h
simdevice.c
usbdevice.h
usbdevice.c
sounddevice.h
sounddevice.c
serialdevice.h
serialdevice.c
tachdevice.h
tachdevice.c
usb/revburner.h
usb/revburner.c
sound/usb_generic_shaker.h
sound/usb_generic_shaker.c
serial/arduino.h
serial/arduino.c
)
include_directories("." "usb" "sound" "serial")
add_library(devices STATIC ${devices_source_files})
+79
View File
@@ -0,0 +1,79 @@
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include "arduino.h"
#include "../slog/slog.h"
#define arduino_timeout 2000
int arduino_update(SerialDevice* serialdevice, SimData* simdata)
{
int result = 1;
if (serialdevice->port)
{
result = check(sp_blocking_write(serialdevice->port, simdata, sizeof(SimData), arduino_timeout));
}
return result;
}
int arduino_init(SerialDevice* serialdevice)
{
slogi("initializing arduino serial device...");
int error = 0;
char* port_name = "/dev/ttyACM0";
slogd("Looking for port %s.\n", port_name);
error = check(sp_get_port_by_name(port_name, &serialdevice->port));
if (error != 0)
{
return error;
}
slogd("Opening port.\n");
check(sp_open(serialdevice->port, SP_MODE_READ_WRITE));
slogd("Setting port to 9600 8N1, no flow control.\n");
check(sp_set_baudrate(serialdevice->port, 9600));
check(sp_set_bits(serialdevice->port, 8));
check(sp_set_parity(serialdevice->port, SP_PARITY_NONE));
check(sp_set_stopbits(serialdevice->port, 1));
check(sp_set_flowcontrol(serialdevice->port, SP_FLOWCONTROL_NONE));
slogd("Successfully setup arduino serial device...");
return 0;
}
int arduino_free(SerialDevice* serialdevice)
{
check(sp_close(serialdevice->port));
sp_free_port(serialdevice->port);
}
int check(enum sp_return result)
{
/* For this example we'll just exit on any error by calling abort(). */
char* error_message;
switch (result)
{
case SP_ERR_ARG:
//printf("Error: Invalid argument.\n");
return 1;
//abort();
case SP_ERR_FAIL:
error_message = sp_last_error_message();
printf("Error: Failed: %s\n", error_message);
sp_free_error_message(error_message);
abort();
case SP_ERR_SUPP:
printf("Error: Not supported.\n");
abort();
case SP_ERR_MEM:
printf("Error: Couldn't allocate memory.\n");
abort();
case SP_OK:
default:
return result;
}
}
+11
View File
@@ -0,0 +1,11 @@
#ifndef _ARDUINO_H
#define _ARDUINO_H
#include "../serialdevice.h"
int arduino_update(SerialDevice* serialdevice, SimData* simdata);
int arduino_init(SerialDevice* serialdevice);
int arduino_free(SerialDevice* serialdevice);
int check(enum sp_return result);
#endif
+35
View File
@@ -0,0 +1,35 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "serialdevice.h"
#include "serial/arduino.h"
#include "../helper/parameters.h"
#include "../simulatorapi/simdata.h"
#include "../slog/slog.h"
int serialdev_update(SerialDevice* serialdevice, SimData* simdata)
{
arduino_update(serialdevice, simdata);
return 0;
}
int serialdev_free(SerialDevice* serialdevice)
{
arduino_free(serialdevice);
return 0;
}
int serialdev_init(SerialDevice* serialdevice)
{
slogi("initializing serial device...");
int error = 0;
serialdevice->type = SERIALDEV_UNKNOWN;
serialdevice->type = SERIALDEV_ARDUINO;
error = arduino_init(serialdevice);
return error;
}
+27
View File
@@ -0,0 +1,27 @@
#ifndef _SERIALDEVICE_H
#define _SERIALDEVICE_H
#include <libserialport.h>
#include "../helper/parameters.h"
#include "../simulatorapi/simdata.h"
typedef enum
{
SERIALDEV_UNKNOWN = 0,
SERIALDEV_ARDUINO = 1
}
SerialType;
typedef struct
{
int id;
SerialType type;
struct sp_port* port;
}
SerialDevice;
int serialdev_update(SerialDevice* serialdevice, SimData* simdata);
int serialdev_init(SerialDevice* serialdevice);
int serialdev_free(SerialDevice* serialdevice);
#endif
+85
View File
@@ -0,0 +1,85 @@
#include <stdio.h>
#include "simdevice.h"
#include "../helper/parameters.h"
#include "../helper/confighelper.h"
#include "../simulatorapi/simdata.h"
#include "../slog/slog.h"
int devupdate(SimDevice* simdevice, SimData* simdata)
{
if (simdevice->initialized==false)
{
return 0;
}
switch ( simdevice->type )
{
case SIMDEV_USB :
usbdev_update(&simdevice->d.usbdevice, simdata);
break;
case SIMDEV_SOUND :
sounddev_update(&simdevice->d.sounddevice, simdata);
break;
case SIMDEV_SERIAL :
serialdev_update(&simdevice->d.serialdevice, simdata);
break;
}
return 0;
}
int devfree(SimDevice* simdevice)
{
if (simdevice->initialized==false)
{
slogw("Attempt to free an uninitialized device");
return MONOCOQUE_ERROR_INVALID_DEV;
}
switch ( simdevice->type )
{
case SIMDEV_USB :
usbdev_free(&simdevice->d.usbdevice);
break;
case SIMDEV_SOUND :
sounddev_free(&simdevice->d.sounddevice);
break;
case SIMDEV_SERIAL :
serialdev_free(&simdevice->d.serialdevice);
break;
}
return 0;
}
int devinit(SimDevice* simdevice, DeviceSettings* ds)
{
slogi("initializing simdevice...");
simdevice->initialized = false;
int err = 0;
switch ( ds->dev_type )
{
case SIMDEV_USB :
simdevice->type = SIMDEV_USB;
simdevice->d.usbdevice.type = USBDEV_UNKNOWN;
err = usbdev_init(&simdevice->d.usbdevice, ds);
break;
case SIMDEV_SOUND :
simdevice->type = SIMDEV_SOUND;
err = sounddev_init(&simdevice->d.sounddevice);
break;
case SIMDEV_SERIAL :
simdevice->type = SIMDEV_SERIAL;
err = serialdev_init(&simdevice->d.serialdevice);
break;
default :
sloge("Unknown device type");
err = MONOCOQUE_ERROR_UNKNOWN_DEV;
break;
}
if (err==0)
{
simdevice->initialized = true;
}
return err;
}
+34
View File
@@ -0,0 +1,34 @@
#ifndef _SIMDEVICE_H
#define _SIMDEVICE_H
#include <stdbool.h>
#include "usbdevice.h"
#include "sounddevice.h"
#include "serialdevice.h"
#include "../helper/confighelper.h"
#include "../simulatorapi/simdata.h"
typedef struct
{
int id;
bool initialized;
DeviceType type;
union
{
USBDevice usbdevice;
SoundDevice sounddevice;
SerialDevice serialdevice;
} d;
}
SimDevice;
int devupdate(SimDevice* simdevice, SimData* simdata);
int devinit(SimDevice* simdevice, DeviceSettings* ds);
int devfree(SimDevice* simdevice);
#endif
@@ -0,0 +1,128 @@
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <unistd.h>
#include "portaudio.h"
#include "usb_generic_shaker.h"
#include "../sounddevice.h"
#define SAMPLE_RATE (48000)
#ifndef M_PI
#define M_PI (3.14159265)
#endif
int patestCallback(const void* inputBuffer,
void* outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void* userData)
{
PATestData* data = (PATestData*)userData;
float* out = (float*)outputBuffer;
memset(out, 0, framesPerBuffer * 2 * sizeof(float));
unsigned int i;
unsigned int n;
n = data->n;
(void) inputBuffer; /* Prevent unused argument warning. */
for( i=0; i<framesPerBuffer; i++,n++ )
{
float v = 0;
v = data->amp * sin (2 * M_PI * ((float) n) / (float) SAMPLE_RATE);
if ( data->gear_sound_data > 0 )
{
if (n>=1764)
{
n=0;
}
}
else
{
if (n>=data->table_size)
{
n=0;
}
}
if ( data->gear_sound_data > 0 )
{
// right channel only?
// i have my butt hooked up to right channel... make this configurable?
*out++ = v;
}
else
{
*out++ = v;
*out++ = v;
}
}
data->n=n;
return 0;
}
int usb_generic_shaker_free(SoundDevice* sounddevice)
{
int err = 0;
err = Pa_CloseStream( sounddevice->stream );
if( err != paNoError )
{
err = Pa_Terminate();
}
return err;
}
int usb_generic_shaker_init(SoundDevice* sounddevice)
{
PaError err;
err = paNoError;
err = Pa_Initialize();
if( err != paNoError )
{
goto error;
}
sounddevice->outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
sounddevice->outputParameters.channelCount = 2; /* stereo output */
sounddevice->outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
sounddevice->outputParameters.suggestedLatency = Pa_GetDeviceInfo( sounddevice->outputParameters.device )->defaultLowOutputLatency;
sounddevice->outputParameters.hostApiSpecificStreamInfo = NULL;
err = Pa_OpenStream( &sounddevice->stream,
NULL, /* No input. */
&sounddevice->outputParameters, /* As above. */
SAMPLE_RATE,
440, /* Frames per buffer. */
paClipOff, /* No out of range samples expected. */
patestCallback,
&sounddevice->sounddata );
if( err != paNoError )
{
goto error;
}
err = Pa_StartStream( sounddevice->stream );
if( err != paNoError )
{
goto error;
}
return err;
error:
Pa_Terminate();
//fprintf( stderr, "An error occured while using the portaudio stream\n" );
//fprintf( stderr, "Error number: %d\n", err );
//fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
return err;
}
@@ -0,0 +1,9 @@
#ifndef _USB_GENERIC_SHAKER_H
#define _USB_GENERIC_SHAKER_H
#include "../sounddevice.h"
int usb_generic_shaker_init(SoundDevice* sounddevice);
int usb_generic_shaker_free(SoundDevice* sounddevice);
#endif
+38
View File
@@ -0,0 +1,38 @@
#include <stdio.h>
#include "sounddevice.h"
#include "sound/usb_generic_shaker.h"
#include "../simulatorapi/simdata.h"
#include "../helper/parameters.h"
#include "../slog/slog.h"
int sounddev_update(SoundDevice* sounddevice, SimData* simdata)
{
sounddevice->sounddata.table_size = 44100/(simdata->rpms/60);
sounddevice->sounddata.gear_sound_data = 0;
if (sounddevice->sounddata.last_gear != simdata->gear)
{
sounddevice->sounddata.gear_sound_data = sounddevice->sounddata.amp;
}
sounddevice->sounddata.last_gear = simdata->gear;
}
int sounddev_free(SoundDevice* sounddevice)
{
return usb_generic_shaker_free(sounddevice);
}
int sounddev_init(SoundDevice* sounddevice)
{
slogi("initializing standalone sound device...");
sounddevice->sounddata.pitch = 1;
sounddevice->sounddata.pitch = 261.626;
sounddevice->sounddata.amp = 32;
sounddevice->sounddata.left_phase = sounddevice->sounddata.right_phase = 0;
sounddevice->sounddata.table_size = 44100/(100/60);
sounddevice->sounddata.last_gear = 0;
usb_generic_shaker_init(sounddevice);
}
+45
View File
@@ -0,0 +1,45 @@
#ifndef _SOUNDDEVICE_H
#define _SOUNDDEVICE_H
#include "portaudio.h"
#include "../simulatorapi/simdata.h"
#include "../helper/parameters.h"
typedef enum
{
SOUNDDEV_UNKNOWN = 0,
SOUNDDEV_SHAKER = 1
}
SoundType;
#define MAX_TABLE_SIZE (6000)
typedef struct
{
float sine[MAX_TABLE_SIZE];
float pitch;
int last_gear;
int left_phase;
int right_phase;
int n;
int table_size;
int amp;
int gear_sound_data;
}
PATestData;
typedef struct
{
int id;
SoundType type;
PATestData sounddata;
PaStreamParameters outputParameters;
PaStream* stream;
}
SoundDevice;
int sounddev_update(SoundDevice* sounddevice, SimData* simdata);
int sounddev_init(SoundDevice* sounddevice);
int sounddev_free(SoundDevice* sounddevice);
#endif
+80
View File
@@ -0,0 +1,80 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "tachdevice.h"
#include "revburner.h"
#include "../../helper/confighelper.h"
#include "../../simulatorapi/simdata.h"
#include "../../slog/slog.h"
int tachdev_update(TachDevice* tachdevice, SimData* simdata)
{
// current plan is to just use the revburner xml format for other possible tachometer devices
// with that assumption this same logic is assumed the same for other tachometer devices
// the only difference then being in communication to the physical device
int pulses = simdata->pulses;
switch ( tachdevice->type )
{
case TACHDEV_UNKNOWN :
case TACHDEV_REVBURNER :
if (tachdevice->tachsettings.use_pulses == false)
{
slogt("Getting pulses for current tachometer revs");
if (simdata->rpms < 500)
{
pulses = tachdevice->tachsettings.pulses_array[0];
}
else
{
slogt("Tach settings size %i",tachdevice->tachsettings.size);
int el = simdata->rpms / 1000;
if (tachdevice->tachsettings.granularity > 0)
{
el = simdata->rpms / (1000 / tachdevice->tachsettings.granularity);
}
if (el >= tachdevice->tachsettings.size - 1)
{
el = tachdevice->tachsettings.size - 1;
}
slogt("Retrieveing element %i", el);
pulses = tachdevice->tachsettings.pulses_array[el];
}
}
slogt("Settings tachometer pulses to %i", pulses);
revburner_update(tachdevice, pulses);
break;
}
return 0;
}
int tachdev_free(TachDevice* tachdevice)
{
switch ( tachdevice->type )
{
case TACHDEV_UNKNOWN :
case TACHDEV_REVBURNER :
revburner_update(tachdevice, 0);
revburner_free(tachdevice);
break;
}
return 0;
}
int tachdev_init(TachDevice* tachdevice, DeviceSettings* ds)
{
slogi("initializing tachometer device...");
int error = 0;
// detection of tach device model
tachdevice->type = TACHDEV_UNKNOWN;
tachdevice->type = TACHDEV_REVBURNER;
tachdevice->tachsettings = ds->tachsettings;
error = revburner_init(tachdevice);
return error;
}
+31
View File
@@ -0,0 +1,31 @@
#ifndef _TACHDEVICE_H
#define _TACHDEVICE_H
#include <hidapi/hidapi.h>
#include "../helper/confighelper.h"
#include "../simulatorapi/simdata.h"
//typedef int (*tachdev_update)(int revs);
typedef enum
{
TACHDEV_UNKNOWN = 0,
TACHDEV_REVBURNER = 1
}
TachType;
typedef struct
{
int id;
TachType type;
bool use_pulses;
hid_device* handle;
TachometerSettings tachsettings;
}
TachDevice;
int tachdev_update(TachDevice* tachdevice, SimData* simdata);
int tachdev_init(TachDevice* tachdevice, DeviceSettings* ds);
int tachdev_free(TachDevice* tachdevice);
#endif
+69
View File
@@ -0,0 +1,69 @@
#include <stdio.h>
#include <hidapi/hidapi.h>
#include "tachdevice.h"
#include "../slog/slog.h"
const int buf_size = 65;
int revburner_update(TachDevice* tachdevice, int pulses)
{
int res = 0;
unsigned char bytes[buf_size];
for (int x = 0; x < buf_size; x++)
{
bytes[x] = 0x00;
}
if ( pulses > 0 )
{
bytes[3] = (pulses >> 8) & 0xFF;
bytes[2] = pulses & 0xFF;
}
if (tachdevice->handle)
{
res = hid_write(tachdevice->handle, bytes, buf_size);
}
else
{
slogd("no handle");
}
return res;
}
int revburner_free(TachDevice* tachdevice)
{
int res = 0;
hid_close(tachdevice->handle);
res = hid_exit();
return res;
}
int revburner_init(TachDevice* tachdevice)
{
slogi("initializing revburner tachometer...");
//tachdevice->update_tachometer = revburner_device_update;
int res = 0;
res = hid_init();
tachdevice->handle = hid_open(0x4d8, 0x102, NULL);
if (!tachdevice->handle)
{
sloge("Could not find attached RevBurner tachometer");
res = hid_exit();
return 1;
}
slogd("Found RevBurner Tachometer...");
return res;
}
+8
View File
@@ -0,0 +1,8 @@
#ifndef _REVBURNER_H
#define _REVBURNER_H
int revburner_update(TachDevice* tachdevice, int pulses);
int revburner_init(TachDevice* tachdevice);
int revburner_free(TachDevice* tachdevice);
#endif
+49
View File
@@ -0,0 +1,49 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "usbdevice.h"
#include "../helper/parameters.h"
#include "../simulatorapi/simdata.h"
#include "../slog/slog.h"
int usbdev_update(USBDevice* usbdevice, SimData* simdata)
{
switch ( usbdevice->type )
{
case USBDEV_UNKNOWN :
case USBDEV_TACHOMETER :
tachdev_update(&usbdevice->u.tachdevice, simdata);
break;
}
return 0;
}
int usbdev_free(USBDevice* usbdevice)
{
switch ( usbdevice->type )
{
case USBDEV_UNKNOWN :
case USBDEV_TACHOMETER :
tachdev_free(&usbdevice->u.tachdevice);
break;
}
return 0;
}
int usbdev_init(USBDevice* usbdevice, DeviceSettings* ds)
{
slogi("initializing usb device...");
int error = 0;
switch ( usbdevice->type )
{
case USBDEV_UNKNOWN :
case USBDEV_TACHOMETER :
error = tachdev_init(&usbdevice->u.tachdevice, ds);
break;
}
return error;
}
+30
View File
@@ -0,0 +1,30 @@
#ifndef _USBDEVICE_H
#define _USBDEVICE_H
#include "tachdevice.h"
#include "../helper/confighelper.h"
#include "../simulatorapi/simdata.h"
typedef enum
{
USBDEV_UNKNOWN = 0,
USBDEV_TACHOMETER = 1
}
USBType;
typedef struct
{
int id;
USBType type;
union
{
TachDevice tachdevice;
} u;
}
USBDevice;
int usbdev_update(USBDevice* usbdevice, SimData* simdata);
int usbdev_init(USBDevice* usbdevice, DeviceSettings* ds);
int usbdev_free(USBDevice* usbdevice);
#endif
+11
View File
@@ -0,0 +1,11 @@
set(gameloop_source_files
gameloop.c
gameloop.h
tachconfig.c
tachconfig.h
)
set(LIBXML_INCLUDE_DIR /usr/include/libxml2)
include_directories("." ${LIBXML_INCLUDE_DIR})
add_library(gameloop STATIC ${gameloop_source_files})
+218
View File
@@ -0,0 +1,218 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <poll.h>
#include <termios.h>
#include "gameloop.h"
#include "../helper/parameters.h"
#include "../helper/confighelper.h"
#include "../devices/simdevice.h"
#include "../simulatorapi/simdata.h"
#include "../simulatorapi/simmapper.h"
#include "../slog/slog.h"
#define DEFAULT_UPDATE_RATE 120.0
int showstats(SimData* simdata)
{
printf("\r");
for (int i=0; i<4; i++)
{
if (i==0)
{
fputc('s', stdout);
fputc('p', stdout);
fputc('e', stdout);
fputc('e', stdout);
fputc('d', stdout);
fputc(':', stdout);
fputc(' ', stdout);
int speed = simdata->velocity;
int digits = 0;
while (speed > 0)
{
int mod = speed % 10;
speed = speed / 10;
digits++;
}
speed = simdata->velocity;
int s[digits];
int digit = 0;
while (speed > 0)
{
int mod = speed % 10;
s[digit] = mod;
speed = speed / 10;
digit++;
}
speed = simdata->velocity;
digit = digits;
while (digit > 0)
{
fputc(s[digit-1]+'0', stdout);
digit--;
}
fputc(' ', stdout);
}
if (i==1)
{
fputc('r', stdout);
fputc('p', stdout);
fputc('m', stdout);
fputc('s', stdout);
fputc(':', stdout);
fputc(' ', stdout);
int rpms = simdata->rpms;
int digits = 0;
while (rpms > 0)
{
int mod = rpms % 10;
rpms = rpms / 10;
digits++;
}
rpms = simdata->rpms;
int s[digits];
int digit = 0;
while (rpms > 0)
{
int mod = rpms % 10;
s[digit] = mod;
rpms = rpms / 10;
digit++;
}
rpms = simdata->rpms;
digit = digits;
while (digit > 0)
{
fputc(s[digit-1]+'0', stdout);
digit--;
}
fputc(' ', stdout);
}
if (i==2)
{
fputc('g', stdout);
fputc('e', stdout);
fputc('a', stdout);
fputc('r', stdout);
fputc(':', stdout);
fputc(' ', stdout);
fputc(simdata->gear+'0', stdout);
fputc(' ', stdout);
}
if (i==3)
{
fputc('a', stdout);
fputc('l', stdout);
fputc('t', stdout);
fputc(':', stdout);
fputc(' ', stdout);
int alt = simdata->altitude;
int digits = 0;
while (alt > 0)
{
int mod = alt % 10;
alt = alt / 10;
digits++;
}
alt = simdata->altitude;
int s[digits];
int digit = 0;
while (alt > 0)
{
int mod = alt % 10;
s[digit] = mod;
alt = alt / 10;
digit++;
}
alt = simdata->altitude;
digit = digits;
while (digit > 0)
{
fputc(s[digit-1]+'0', stdout);
digit--;
}
fputc(' ', stdout);
}
}
fflush(stdout);
}
int looper(SimDevice* devices[], int numdevices, Simulator simulator)
{
slogi("preparing game loop with %i devices...", numdevices);
SimData* simdata = malloc(sizeof(SimData));
SimMap* simmap = malloc(sizeof(SimMap));
int error = siminit(simdata, simmap, simulator);
if (error != MONOCOQUE_ERROR_NONE)
{
return error;
}
struct termios newsettings, canonicalmode;
tcgetattr(0, &canonicalmode);
newsettings = canonicalmode;
newsettings.c_lflag &= (~ICANON & ~ECHO);
newsettings.c_cc[VMIN] = 1;
newsettings.c_cc[VTIME] = 0;
tcsetattr(0, TCSANOW, &newsettings);
char ch;
struct pollfd mypoll = { STDIN_FILENO, POLLIN|POLLPRI };
double update_rate = DEFAULT_UPDATE_RATE;
int t=0;
int go = true;
while (go == true)
{
simdatamap(simdata, simmap, simulator);
showstats(simdata);
t++;
if(simdata->rpms<250)
{
simdata->rpms=250;
}
for (int x = 0; x < numdevices; x++)
{
if (devices[x]->type == SIMDEV_SERIAL)
{
if(t>=update_rate)
{
devupdate(devices[x], simdata);
}
}
else
{
devupdate(devices[x], simdata);
}
}
if(t>=update_rate)
{
t=0;
}
if( poll(&mypoll, 1, 1000.0/update_rate) )
{
scanf("%c", &ch);
if(ch == 'q')
{
go = false;
}
}
}
tcsetattr(0, TCSANOW, &canonicalmode);
free(simdata);
free(simmap);
return 0;
}
+4
View File
@@ -0,0 +1,4 @@
#include "../devices/simdevice.h"
#include "../helper/parameters.h"
int looper (SimDevice* devices[], int numdevices, Simulator simulator);
+190
View File
@@ -0,0 +1,190 @@
#include <stdio.h>
#include <unistd.h>
#include <sys/poll.h>
#include <sys/time.h>
#include <sys/select.h>
#include <fcntl.h>
#include <errno.h>
#include <linux/input.h>
#include <string.h>
#include <termios.h>
#include <poll.h>
#include <libxml/parser.h>
#include <libxml/xmlreader.h>
#include <libxml/tree.h>
#include "../devices/simdevice.h"
#include "../simulatorapi/simdata.h"
#include "../slog/slog.h"
#define DEFAULT_UPDATE_RATE 30.0
int WriteXmlFromArrays(int nodes, int rpm_array[], int values_array[], int maxrevs, const char* save_file)
{
xmlDocPtr doc = NULL;
xmlNodePtr rootnode = NULL, onenode = NULL, settingsvaluenode = NULL, maxdisplayvaluenode = NULL;
char buff[256];
int i, j;
doc = xmlNewDoc(BAD_CAST "1.0");
rootnode = xmlNewNode(NULL, BAD_CAST "TachometerSettings");
xmlDocSetRootElement(doc, rootnode);
settingsvaluenode = xmlNewNode(NULL, BAD_CAST "SettingsValues");
for(int i = 0; i< nodes; ++i)
{
onenode = xmlNewNode(NULL, BAD_CAST "SettingsItem");
char value[10];
sprintf(value, "%d", values_array[i]);
char rpm[10];
sprintf(rpm, "%d", rpm_array[i]);
xmlNewChild(onenode, NULL, BAD_CAST "Value", BAD_CAST value);
xmlNewChild(onenode, NULL, BAD_CAST "TimeValue", BAD_CAST rpm);
xmlAddChild(settingsvaluenode, onenode);
}
xmlAddChild(rootnode, settingsvaluenode);
char revs[10];
sprintf(revs, "%d", maxrevs);
maxdisplayvaluenode = xmlNewChild(rootnode, NULL, BAD_CAST "MaxDisplayValue", BAD_CAST revs);
xmlSaveFormatFileEnc(save_file, doc, "UTF-8", 1);
xmlFreeDoc(doc);
xmlCleanupParser();
return 0;
}
int config_tachometer(int max_revs, int granularity, const char* save_file, SimDevice* simdevice, SimData* simdata)
{
int pulses = 0;
int nodes = 0;
if (max_revs<2000)
{
fprintf(stderr, "revs must be at least 2000\n");
return 0;
}
int increment = 1000;
if (granularity == 2)
{
increment = 500;
}
if (granularity == 4)
{
increment = 250;
}
nodes = ((max_revs/1000)*granularity)+1;
if (granularity >= 4)
{
nodes--;
}
int rpm_array[nodes];
int values_array[nodes];
values_array[0]=250;
values_array[1]=increment;
if (granularity >= 4)
{
values_array[0] = increment;
values_array[1]= increment * 2;
}
for(int i=2; i<nodes; i++)
{
values_array[i]=values_array[i-1]+increment;
}
for(int i=0; i<nodes; i++)
{
struct termios newsettings, canonicalmode;
tcgetattr(0, &canonicalmode);
newsettings = canonicalmode;
newsettings.c_lflag &= (~ICANON & ~ECHO);
newsettings.c_cc[VMIN] = 1;
newsettings.c_cc[VTIME] = 0;
tcsetattr(0, TCSANOW, &newsettings);
char ch = ' ';
if (i==0)
{
fprintf(stdout, "Press Return to continue...\n");
scanf("%c",&ch);
}
sleep(2);
fprintf(stdout, "Set tachometer revs to %i: Press > to increase, < to decrease, and Return to accept (m increases by 1000, n decreases by 1000, c increases by 100, z decreases by 100...\n", values_array[i]);
struct pollfd mypoll = { STDIN_FILENO, POLLIN|POLLPRI };
double update_rate = DEFAULT_UPDATE_RATE;
int go=1;
while (go>0)
{
simdata->pulses = pulses;
devupdate(simdevice, simdata);
if( poll(&mypoll, 1, 1000.0/update_rate) )
{
ch = ' ';
scanf("%c", &ch);
if (ch == 'n')
{
pulses=pulses-1000;
}
if (ch == 'm')
{
pulses=pulses+1000;
}
if (ch == 'z')
{
pulses=pulses-100;
}
if (ch == 'c')
{
pulses=pulses+100;
}
if (ch == '<')
{
pulses--;
}
if (ch == '>')
{
pulses++;
}
if (ch == '\n')
{
go=0;
fprintf(stdout, "set pulses to %i\n", pulses);
rpm_array[i]=pulses;
}
}
}
tcsetattr(0, TCSANOW, &canonicalmode);
}
WriteXmlFromArrays(nodes, rpm_array, values_array, max_revs, save_file);
sleep(2);
simdata->pulses = 0;
devupdate(simdevice, simdata);
fflush(stdout);
return 0;
}
+9
View File
@@ -0,0 +1,9 @@
#ifndef _TACHCONFIG_H
#define _TACHCONFIG_H
#include "../devices/simdevice.h"
#include "../simulatorapi/simdata.h"
int config_tachometer(int max_revs, int granularity, const char* save_file, SimDevice* simdevice, SimData* simdata);
#endif
+13
View File
@@ -0,0 +1,13 @@
set(helper_source_files
parameters.c
parameters.h
dirhelper.c
dirhelper.h
confighelper.c
confighelper.h
)
set(LIBXML_INCLUDE_DIR /usr/include/libxml2)
include_directories("." ${LIBXML_INCLUDE_DIR})
add_library(helper STATIC ${helper_source_files})
+237
View File
@@ -0,0 +1,237 @@
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <stdint.h>
#include <libxml/parser.h>
#include <libxml/xmlreader.h>
#include <libxml/tree.h>
#include "confighelper.h"
#include "../slog/slog.h"
int strtogame(const char* game, MonocoqueSettings* ms)
{
slogd("Checking for %s in list of supported simulators.", game);
if (strcmp(game, "ac") == 0)
{
slogd("Setting simulator to Assetto Corsa");
ms->sim_name = SIMULATOR_ASSETTO_CORSA;
}
else
if (strcmp(game, "test") == 0)
{
slogd("Setting simulator to Test Data");
ms->sim_name = SIMULATOR_MONOCOQUE_TEST;
}
else
{
slogi("%s does not appear to be a supported simulator.", game);
return MONOCOQUE_ERROR_INVALID_SIM;
}
return MONOCOQUE_ERROR_NONE;
}
int strtodev(const char* device_type, DeviceSettings* ds)
{
ds->is_valid = false;
if (strcmp(device_type, "USB") == 0)
{
ds->dev_type = SIMDEV_USB;
}
else
if (strcmp(device_type, "Sound") == 0)
{
ds->dev_type = SIMDEV_SOUND;
}
else
if (strcmp(device_type, "Serial") == 0)
{
ds->dev_type = SIMDEV_SERIAL;
}
else
{
ds->is_valid = false;
slogi("%s does not appear to be a valid device type, but attempting to continue with other devices", device_type);
return MONOCOQUE_ERROR_INVALID_DEV;
}
ds->is_valid = true;
return MONOCOQUE_ERROR_NONE;
}
int strtodevtype(const char* device_subtype, DeviceSettings* ds)
{
ds->is_valid = false;
if (strcmp(device_subtype, "Tachometer") == 0)
{
ds->dev_subtype = SIMDEVTYPE_TACHOMETER;
}
else
if (strcmp(device_subtype, "ShiftLights") == 0)
{
ds->dev_subtype = SIMDEVTYPE_SHIFTLIGHTS;
}
else
if (strcmp(device_subtype, "Shaker") == 0)
{
ds->dev_subtype = SIMDEVTYPE_SHAKER;
}
else
{
ds->is_valid = false;
slogi("%s does not appear to be a valid device sub type, but attempting to continue with other devices", device_subtype);
return MONOCOQUE_ERROR_INVALID_DEV;
}
ds->is_valid = true;
return MONOCOQUE_ERROR_NONE;
}
int loadtachconfig(const char* config_file, DeviceSettings* ds)
{
xmlNode* rootnode = NULL;
xmlNode* curnode = NULL;
xmlNode* cursubnode = NULL;
xmlNode* cursubsubnode = NULL;
xmlNode* cursubsubsubnode = NULL;
xmlDoc* doc = NULL;
char* buf;
doc = xmlParseFile(config_file);
if (doc == NULL)
{
sloge("Could not read revburner xml config file %s", config_file);
return 1;
}
rootnode = xmlDocGetRootElement(doc);
if (rootnode == NULL)
{
xmlFreeDoc(doc);
xmlCleanupParser();
sloge("Invalid rev burner xml");
return 1;
}
int arraysize = 0;
for (curnode = rootnode; curnode; curnode = curnode->next)
{
for (cursubnode = curnode->children; cursubnode; cursubnode = cursubnode->next)
{
for (cursubsubnode = cursubnode->children; cursubsubnode; cursubsubnode = cursubsubnode->next)
{
if (cursubsubnode->type == XML_ELEMENT_NODE)
{
slogt("Xml Element name %s", cursubsubnode->name);
}
if (strcmp(cursubsubnode->name, "SettingsItem") == 0)
{
arraysize++;
}
}
}
}
uint32_t pulses_array[arraysize];
uint32_t rpms_array[arraysize];
slogt("rev burner settings array size %i", arraysize);
int i = 0;
for (curnode = rootnode; curnode; curnode = curnode->next)
{
if (curnode->type == XML_ELEMENT_NODE)
for (cursubnode = curnode->children; cursubnode; cursubnode = cursubnode->next)
{
for (cursubsubnode = cursubnode->children; cursubsubnode; cursubsubnode = cursubsubnode->next)
{
for (cursubsubsubnode = cursubsubnode->children; cursubsubsubnode; cursubsubsubnode = cursubsubsubnode->next)
{
if (strcmp(cursubsubsubnode->name, "Value") == 0)
{
xmlChar* a = xmlNodeGetContent(cursubsubsubnode);
rpms_array[i] = strtol((char*) a, &buf, 10);
xmlFree(a);
}
if (strcmp(cursubsubsubnode->name, "TimeValue") == 0)
{
xmlChar* a = xmlNodeGetContent(cursubsubsubnode);
pulses_array[i] = strtol((char*) a, &buf, 10);
xmlFree(a);
i++;
}
}
}
}
}
ds->tachsettings.pulses_array = malloc(sizeof(pulses_array));
ds->tachsettings.rpms_array = malloc(sizeof(rpms_array));
ds->tachsettings.size = arraysize;
memcpy(ds->tachsettings.pulses_array, pulses_array, sizeof(pulses_array));
memcpy(ds->tachsettings.rpms_array, rpms_array, sizeof(rpms_array));
xmlFreeDoc(doc);
xmlCleanupParser();
return 0;
}
int loadconfig(const char* config_file, DeviceSettings* ds)
{
if (ds->dev_subtype == SIMDEVTYPE_TACHOMETER)
{
return loadtachconfig(config_file, ds);
}
return 0;
}
int devsetup(const char* device_type, const char* device_subtype, const char* config_file, MonocoqueSettings* ms, DeviceSettings* ds, config_setting_t* device_settings)
{
int error = MONOCOQUE_ERROR_NONE;
slogi("Called device setup with %s %s %s", device_type, device_subtype, config_file);
ds->dev_type = SIMDEV_UNKNOWN;
ds->dev_subtype = SIMDEVTYPE_UNKNOWN;
error = strtodev(device_type, ds);
if (error != MONOCOQUE_ERROR_NONE)
{
return error;
}
error = strtodevtype(device_subtype, ds);
if (error != MONOCOQUE_ERROR_NONE)
{
return error;
}
if (ms->program_action == A_PLAY)
{
error = loadconfig(config_file, ds);
}
if (error != MONOCOQUE_ERROR_NONE)
{
return error;
}
if (ds->dev_subtype == SIMDEVTYPE_TACHOMETER)
{
if (device_settings != NULL)
{
config_setting_lookup_int(device_settings, "granularity", &ds->tachsettings.granularity);
if (ds->tachsettings.granularity < 0 || ds->tachsettings.granularity > 4 || ds->tachsettings.granularity == 3)
{
slogd("No or invalid valid set for tachometer granularity, setting to 1");
ds->tachsettings.granularity = 1;
}
slogi("Tachometer granularity set to %i", ds->tachsettings.granularity);
}
ds->tachsettings.use_pulses = true;
if (ms->program_action == A_PLAY)
{
ds->tachsettings.use_pulses = false;
}
}
return error;
}
+88
View File
@@ -0,0 +1,88 @@
#ifndef _CONFIGHELPER_H
#define _CONFIGHELPER_H
#include <stdbool.h>
#include <stdint.h>
#include <libconfig.h>
#include "parameters.h"
typedef enum
{
SIMDEV_UNKNOWN = 0,
SIMDEV_USB = 1,
SIMDEV_SOUND = 2,
SIMDEV_SERIAL = 3
}
DeviceType;
typedef enum
{
SIMDEVTYPE_UNKNOWN = 0,
SIMDEVTYPE_TACHOMETER = 1,
SIMDEVTYPE_SHAKER = 2,
SIMDEVTYPE_SHIFTLIGHTS = 3
}
DeviceSubType;
typedef enum
{
SIMULATOR_MONOCOQUE_TEST = 0,
SIMULATOR_ASSETTO_CORSA = 1
}
Simulator;
typedef enum
{
SIMULATOR_UPDATE_DEFAULT = 0,
SIMULATOR_UPDATE_RPMS = 1,
SIMULATOR_UPDATE_GEAR = 2,
SIMULATOR_UPDATE_PULSES = 3,
SIMULATOR_UPDATE_VELOCITY = 4,
SIMULATOR_UPDATE_ALTITUDE = 5
}
SimulatorUpdate;
typedef enum
{
MONOCOQUE_ERROR_NONE = 0,
MONOCOQUE_ERROR_UNKNOWN = 1,
MONOCOQUE_ERROR_INVALID_SIM = 2,
MONOCOQUE_ERROR_INVALID_DEV = 3,
MONOCOQUE_ERROR_NODATA = 4,
MONOCOQUE_ERROR_UNKNOWN_DEV = 5
}
MonocoqueError;
typedef struct
{
ProgramAction program_action;
Simulator sim_name;
}
MonocoqueSettings;
typedef struct
{
int size;
bool use_pulses;
int granularity;
uint32_t* rpms_array;
uint32_t* pulses_array;
}
TachometerSettings;
typedef struct
{
bool is_valid;
DeviceType dev_type;
DeviceSubType dev_subtype;
TachometerSettings tachsettings;
}
DeviceSettings;
int strtogame(const char* game, MonocoqueSettings* ms);
int devsetup(const char* device_type, const char* device_subtype, const char* config_files, MonocoqueSettings* ms, DeviceSettings* ds, config_setting_t* device_settings);
#endif
+202
View File
@@ -0,0 +1,202 @@
#include "dirhelper.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <dirent.h>
#include <pwd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
char* gethome()
{
char* homedir = getenv("HOME");
return homedir;
if (homedir != NULL)
{
printf("Home dir in enviroment");
printf("%s\n", homedir);
}
uid_t uid = getuid();
struct passwd* pw = getpwuid(uid);
if (pw == NULL)
{
printf("Failed\n");
exit(EXIT_FAILURE);
}
return pw->pw_dir;
}
time_t get_file_creation_time(char* path)
{
struct stat attr;
stat(path, &attr);
return attr.st_mtime;
}
void delete_dir(char* path)
{
struct dirent* de;
DIR* dr = opendir(path);
if (dr == NULL)
{
printf("Could not open current directory");
}
// Refer http://pubs.opengroup.org/onlinepubs/7990989775/xsh/readdir.html
while ((de = readdir(dr)) != NULL)
{
char* fullpath = ( char* ) malloc(1 + strlen(path) + strlen("/") + strlen(de->d_name));
strcpy(fullpath, path);
strcat(fullpath, "/");
strcat(fullpath, de->d_name);
unlink(fullpath);
free(fullpath);
}
closedir(dr);
rmdir(path);
}
void delete_oldest_dir(char* path)
{
char* oldestdir = path;
struct dirent* de;
DIR* dr = opendir(path);
if (dr == NULL)
{
printf("Could not open current directory");
}
// Refer http://pubs.opengroup.org/onlinepubs/7990989775/xsh/readdir.html
char filename_qfd[100] ;
char* deletepath = NULL;
time_t tempoldest = 0;
while ((de = readdir(dr)) != NULL)
{
struct stat stbuf;
if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
{
continue;
}
char* fullpath = ( char* ) malloc(1 + strlen(path) + strlen(de->d_name));
strcpy(fullpath, path);
strcat(fullpath, de->d_name);
stat(fullpath, &stbuf);
if (S_ISDIR(stbuf.st_mode))
{
strcpy(fullpath, path);
strcat(fullpath, de->d_name);
if (tempoldest == 0)
{
tempoldest = get_file_creation_time(fullpath);
free(deletepath);
deletepath = strdup(fullpath);
}
else
{
time_t t = get_file_creation_time(fullpath);
double diff = tempoldest - t;
if (diff > 0)
{
tempoldest = t;
free(deletepath);
deletepath = strdup(fullpath);
}
}
}
free(fullpath);
}
closedir(dr);
delete_dir(deletepath);
free(deletepath);
}
void restrict_folders_to_cache(char* path, int cachesize)
{
int numfolders = 0;
struct dirent* de;
DIR* dr = opendir(path);
if (dr == NULL)
{
printf("Could not open current directory");
}
// Refer http://pubs.opengroup.org/onlinepubs/7990989775/xsh/readdir.html
while ((de = readdir(dr)) != NULL)
{
if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
{
continue;
}
char* fullpath = ( char* ) malloc(1 + strlen(path) + strlen(de->d_name));
strcpy(fullpath, path);
strcat(fullpath, de->d_name);
strcat(fullpath, "/");
struct stat stbuf;
stat(fullpath,&stbuf);
if (S_ISDIR(stbuf.st_mode))
{
numfolders++;
}
free(fullpath);
}
while (numfolders >= cachesize)
{
delete_oldest_dir(path);
numfolders--;
}
closedir(dr);
}
bool does_directory_exist(char* path, char* dirname)
{
struct dirent* de;
DIR* dr = opendir(path);
if (dr == NULL)
{
printf("Could not open current directory");
return false;
}
// Refer http://pubs.opengroup.org/onlinepubs/7990989775/xsh/readdir.html
bool answer = false;
while ((de = readdir(dr)) != NULL)
{
if (strcmp(dirname,de->d_name) == 0)
{
answer = true;
}
}
closedir(dr);
return answer;
}
+12
View File
@@ -0,0 +1,12 @@
#ifndef _DIRHELPER_H
#define _DIRHELPER_H
#include <stdbool.h>
char* gethome();
char* str2md5(const char* str, int length);
bool does_directory_exist(char* path, char* dirname);
void restrict_folders_to_cache(char* path, int cachesize);
void delete_dir(char* path);
#endif
+153
View File
@@ -0,0 +1,153 @@
#include "parameters.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libconfig.h>
#include <argtable2.h>
#include <regex.h>
ConfigError getParameters(int argc, char** argv, Parameters* p)
{
ConfigError exitcode = E_SOMETHING_BAD;
// set return structure defaults
p->program_action = 0;
p->max_revs = 0;
p->verbosity_count = 0;
// setup argument handling structures
const char* progname = "monocoque";
struct arg_lit* arg_verbosity1 = arg_litn("v","verbose", 0, 2, "increase logging verbosity");
struct arg_lit* arg_verbosity2 = arg_litn("v","verbose", 0, 2, "increase logging verbosity");
struct arg_rex* cmd1 = arg_rex1(NULL, NULL, "play", NULL, REG_ICASE, NULL);
struct arg_str* arg_sim = arg_strn("s", "sim", "<gamename>", 0, 1, NULL);
struct arg_lit* help = arg_litn(NULL,"help", 0, 1, "print this help and exit");
struct arg_lit* vers = arg_litn(NULL,"version", 0, 1, "print version information and exit");
struct arg_end* end1 = arg_end(20);
void* argtable1[] = {cmd1,arg_sim,arg_verbosity1,help,vers,end1};
int nerrors1;
struct arg_rex* cmd2a = arg_rex1(NULL, NULL, "config", NULL, REG_ICASE, NULL);
struct arg_rex* cmd2b = arg_rex1(NULL, NULL, "tachometer", NULL, REG_ICASE, NULL);
struct arg_int* arg_max_revs = arg_int1("m", "max_revs",NULL,"specify max revs of tachometer");
struct arg_int* arg_granularity = arg_int0("g", "granularity",NULL,"1 every 1000 revs, 2 every 500 revs, 4 every 250 revs, default 1");
struct arg_file* arg_save = arg_filen("s", "savefile", "<savefile>", 1, 1, NULL);
struct arg_lit* help2 = arg_litn(NULL,"help", 0, 1, "print this help and exit");
struct arg_lit* vers2 = arg_litn(NULL,"version", 0, 1, "print version information and exit");
struct arg_end* end2 = arg_end(20);
void* argtable2[] = {cmd2a,cmd2b,arg_max_revs,arg_granularity,arg_save,arg_verbosity2,help2,vers2,end2};
int nerrors2;
struct arg_lit* help0 = arg_lit0(NULL,"help", "print this help and exit");
struct arg_lit* version0 = arg_lit0(NULL,"version", "print version information and exit");
struct arg_end* end0 = arg_end(20);
void* argtable0[] = {help0,version0,end0};
int nerrors0;
if (arg_nullcheck(argtable0) != 0)
{
printf("%s: insufficient memory\n",progname);
goto cleanup;
}
if (arg_nullcheck(argtable1) != 0)
{
printf("%s: insufficient memory\n",progname);
goto cleanup;
}
if (arg_nullcheck(argtable2) != 0)
{
printf("%s: insufficient memory\n",progname);
goto cleanup;
}
arg_granularity->ival[0] = 1;
nerrors0 = arg_parse(argc,argv,argtable0);
nerrors1 = arg_parse(argc,argv,argtable1);
nerrors2 = arg_parse(argc,argv,argtable2);
if (nerrors1==0)
{
p->program_action = A_PLAY;
p->sim_string = arg_sim->sval[0];
p->verbosity_count = arg_verbosity1->count;
exitcode = E_SUCCESS_AND_DO;
}
else
if (nerrors2==0)
{
p->program_action = A_CONFIG_TACH;
p->max_revs = arg_max_revs->ival[0];
p->granularity = 1;
if (arg_granularity->ival[0] > 0 && arg_granularity->ival[0] < 5 && arg_granularity->ival[0] != 3)
{
p->granularity=arg_granularity->ival[0];
}
p->save_file = *arg_save->filename;
p->verbosity_count = arg_verbosity2->count;
exitcode = E_SUCCESS_AND_DO;
}
else
{
if (cmd1->count > 0)
{
arg_print_errors(stdout,end1,progname);
printf("Usage: %s ", progname);
arg_print_syntax(stdout,argtable1,"\n");
}
else
if (cmd2a->count > 0)
{
arg_print_errors(stdout,end2,progname);
printf("Usage: %s ", progname);
arg_print_syntax(stdout,argtable2,"\n");
}
else
{
if (help->count==0 && vers->count==0)
{
printf("%s: missing <play|config> command.\n",progname);
printf("Usage 1: %s ", progname);
arg_print_syntax(stdout,argtable1,"\n");
printf("Usage 2: %s ", progname);
arg_print_syntax(stdout,argtable2,"\n");
}
}
exitcode = E_SUCCESS_AND_EXIT;
goto cleanup;
}
// interpret some special cases before we go through trouble of reading the config file
if (help->count > 0)
{
printf("Usage: %s\n", progname);
printf("Usage 1: %s ", progname);
arg_print_syntax(stdout,argtable1,"\n");
printf("Usage 2: %s ", progname);
arg_print_syntax(stdout,argtable2,"\n");
printf("\nReport bugs on the github github.com/spacefreak18/monocoque.\n");
exitcode = E_SUCCESS_AND_EXIT;
goto cleanup;
}
if (vers->count > 0)
{
printf("%s Simulator Hardware Manager\n",progname);
printf("October 2022, Paul Dino Jones\n");
exitcode = E_SUCCESS_AND_EXIT;
goto cleanup;
}
cleanup:
arg_freetable(argtable0,sizeof(argtable0)/sizeof(argtable0[0]));
arg_freetable(argtable1,sizeof(argtable1)/sizeof(argtable1[0]));
arg_freetable(argtable2,sizeof(argtable2)/sizeof(argtable2[0]));
return exitcode;
}
+44
View File
@@ -0,0 +1,44 @@
#ifndef _PARAMETERS_H
#define _PARAMETERS_H
typedef struct
{
int program_action;
const char* sim_string;
const char* save_file;
int max_revs;
int granularity;
int verbosity_count;
}
Parameters;
typedef enum
{
A_PLAY = 0,
A_CONFIG_TACH = 1,
A_CONFIG_SHAKER = 2
}
ProgramAction;
typedef enum
{
E_SUCCESS_AND_EXIT = 0,
E_SUCCESS_AND_DO = 1,
E_SOMETHING_BAD = 2
}
ConfigError;
ConfigError getParameters(int argc, char** argv, Parameters* p);
struct _errordesc
{
int code;
char* message;
} static errordesc[] =
{
{ E_SUCCESS_AND_EXIT, "No error and exiting" },
{ E_SUCCESS_AND_DO, "No error and continuing" },
{ E_SOMETHING_BAD, "Something bad happened" },
};
#endif
+233
View File
@@ -0,0 +1,233 @@
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <string.h>
#include <libconfig.h>
#include "gameloop/gameloop.h"
#include "gameloop/tachconfig.h"
#include "devices/simdevice.h"
#include "helper/parameters.h"
#include "helper/dirhelper.h"
#include "helper/confighelper.h"
#include "simulatorapi/simdata.h"
#include "slog/slog.h"
int create_dir(char* dir)
{
struct stat st = {0};
if (stat(dir, &st) == -1)
{
mkdir(dir, 0700);
}
}
char* create_user_dir(char* dirtype)
{
char* home_dir_str = gethome();
char* config_dir_str = ( char* ) malloc(1 + strlen(home_dir_str) + strlen(dirtype) + strlen("monocoque/"));
strcpy(config_dir_str, home_dir_str);
strcat(config_dir_str, dirtype);
strcat(config_dir_str, "monocoque");
create_dir(config_dir_str);
free(config_dir_str);
}
void display_banner()
{
printf("______ ______________ ___________________________________ ___________\n");
printf("___ |/ /_ __ \\__ | / /_ __ \\_ ____/_ __ \\_ __ \\_ / / /__ ____/\n");
printf("__ /|_/ /_ / / /_ |/ /_ / / / / _ / / / / / / / / /__ __/ \n");
printf("_ / / / / /_/ /_ /| / / /_/ // /___ / /_/ // /_/ // /_/ / _ /___ \n");
printf("/_/ /_/ \\____/ /_/ |_/ \\____/ \\____/ \\____/ \\___\\_\\\\____/ /_____/ \n");
}
int main(int argc, char** argv)
{
display_banner();
Parameters* p = malloc(sizeof(Parameters));
MonocoqueSettings* ms = malloc(sizeof(MonocoqueSettings));;
ConfigError ppe = getParameters(argc, argv, p);
if (ppe == E_SUCCESS_AND_EXIT)
{
goto cleanup_final;
}
ms->program_action = p->program_action;
char* home_dir_str = gethome();
create_user_dir("/.config/");
create_user_dir("/.cache/");
char* config_file_str = ( char* ) malloc(1 + strlen(home_dir_str) + strlen("/.config/") + strlen("monocoque/monocoque.config"));
char* cache_dir_str = ( char* ) malloc(1 + strlen(home_dir_str) + strlen("/.cache/monocoque/"));
strcpy(config_file_str, home_dir_str);
strcat(config_file_str, "/.config/");
strcpy(cache_dir_str, home_dir_str);
strcat(cache_dir_str, "/.cache/monocoque/");
strcat(config_file_str, "monocoque/monocoque.config");
slog_config_t slgCfg;
slog_config_get(&slgCfg);
slgCfg.eColorFormat = SLOG_COLORING_TAG;
slgCfg.eDateControl = SLOG_TIME_ONLY;
strcpy(slgCfg.sFileName, "monocoque.log");
strcpy(slgCfg.sFilePath, cache_dir_str);
slgCfg.nTraceTid = 0;
slgCfg.nToScreen = 1;
slgCfg.nUseHeap = 0;
slgCfg.nToFile = 1;
slgCfg.nFlush = 0;
slgCfg.nFlags = SLOG_FLAGS_ALL;
slog_config_set(&slgCfg);
if (p->verbosity_count < 2)
{
slog_disable(SLOG_TRACE);
}
if (p->verbosity_count < 1)
{
slog_disable(SLOG_DEBUG);
}
slogi("Loading configuration file: %s", config_file_str);
config_t cfg;
config_init(&cfg);
config_setting_t* config_devices = NULL;
if (!config_read_file(&cfg, config_file_str))
{
fprintf(stderr, "%s:%d - %s\n", config_error_file(&cfg), config_error_line(&cfg), config_error_text(&cfg));
}
else
{
slogi("Openend monocoque configuration file");
config_devices = config_lookup(&cfg, "devices");
}
free(config_file_str);
free(cache_dir_str);
if (p->program_action == A_CONFIG_TACH)
{
int error = 0;
SimDevice* tachdev = malloc(sizeof(SimDevice));
SimData* sdata = malloc(sizeof(SimData));
DeviceSettings* ds = malloc(sizeof(DeviceSettings));
error = devsetup("USB", "Tachometer", "None", ms, ds, NULL);
error = devinit(tachdev, ds);
slogi("configuring tachometer with max revs: %i, granularity: %i, saving to %s", p->max_revs, p->granularity, p->save_file);
if (error != MONOCOQUE_ERROR_NONE)
{
sloge("Could not proceed with tachometer configuration due to error: %i", error);
}
else
{
config_tachometer(p->max_revs, p->granularity, p->save_file, tachdev, sdata);
}
devfree(tachdev);
free(tachdev);
free(sdata);
free(ds);
}
else
{
slogi("running monocoque in gameloop mode..");
int error = 0;
error = strtogame(p->sim_string, ms);
if (error != MONOCOQUE_ERROR_NONE)
{
goto cleanup_final;
}
int configureddevices = config_setting_length(config_devices);
int numdevices = 0;
DeviceSettings* ds[configureddevices];
slogi("found %i devices in configuration", configureddevices);
int i = 0;
while (i<configureddevices)
{
error = MONOCOQUE_ERROR_NONE;
DeviceSettings* settings = malloc(sizeof(DeviceSettings));
ds[i] = settings;
config_setting_t* config_device = config_setting_get_elem(config_devices, i);
const char* device_type;
const char* device_subtype;
const char* device_config_file;
config_setting_lookup_string(config_device, "device", &device_type);
config_setting_lookup_string(config_device, "type", &device_subtype);
config_setting_lookup_string(config_device, "config", &device_config_file);
if (error == MONOCOQUE_ERROR_NONE)
{
error = devsetup(device_type, device_subtype, device_config_file, ms, ds[i], config_device);
}
if (error == MONOCOQUE_ERROR_NONE)
{
numdevices++;
}
i++;
}
i = 0;
int j = 0;
error = MONOCOQUE_ERROR_NONE;
SimDevice* devices[numdevices];
while (i<configureddevices)
{
if (ds[i]->is_valid == true)
{
SimDevice* device = malloc(sizeof(SimDevice));
devinit(device, ds[i]);
devices[j] = device;
j++;
}
i++;
}
error = looper(devices, numdevices, ms->sim_name);
if (error == MONOCOQUE_ERROR_NONE)
{
slogi("Game loop exited succesfully with error code: %i", error);
}
else
{
sloge("Game loop exited with error code: %i", error);
}
i = 0;
while (i<configureddevices)
{
if(ds[i]->dev_subtype == SIMDEV_USB)
{
free(ds[i]->tachsettings.pulses_array);
free(ds[i]->tachsettings.rpms_array);
}
free(ds[i]);
i++;
}
i = 0;
while (i<numdevices)
{
devfree(devices[i]);
free(devices[i]);
i++;
}
}
configcleanup:
config_destroy(&cfg);
cleanup_final:
free(ms);
free(p);
exit(0);
}
+10
View File
@@ -0,0 +1,10 @@
set(simulatorapi_source_files
simmapper.c
simmapper.h
simdata.h
test.h
simapi/acdata.h
ac.h
)
add_library(simulatorapi STATIC ${simulatorapi_source_files})
+21
View File
@@ -0,0 +1,21 @@
#ifndef _AC_H
#define _AC_H
#include <stdbool.h>
#include "simapi/acdata.h"
#define AC_PHYSICS_FILE "acpmf_physics"
#define AC_STATIC_FILE "acpmf_static"
typedef struct
{
bool has_physics;
bool has_static;
void* physics_map_addr;
void* static_map_addr;
struct SPageFilePhysics ac_physics;
struct SPageFileStatic ac_static;
}
ACMap;
#endif
+17
View File
@@ -0,0 +1,17 @@
#ifndef _SIMDATA_H
#define _SIMDATA_H
#include <stdint.h>
typedef struct
{
uint32_t velocity;
uint32_t rpms;
uint32_t gear;
uint32_t pulses;
uint32_t maxrpm;
uint32_t altitude;
}
SimData;
#endif
+97
View File
@@ -0,0 +1,97 @@
#include <stdio.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <string.h>
#include "simmapper.h"
#include "simdata.h"
#include "test.h"
#include "ac.h"
#include "../helper/confighelper.h"
#include "../slog/slog.h"
#include "simapi/acdata.h"
int simdatamap(SimData* simdata, SimMap* simmap, Simulator simulator)
{
switch ( simulator )
{
case SIMULATOR_MONOCOQUE_TEST :
memcpy(simdata, simmap->addr, sizeof(SimData));
break;
case SIMULATOR_ASSETTO_CORSA :
memcpy(&simmap->d.ac.ac_physics, simmap->d.ac.physics_map_addr, sizeof(simmap->d.ac.ac_physics));
if (simmap->d.ac.has_static == true )
{
memcpy(&simmap->d.ac.ac_static, simmap->d.ac.static_map_addr, sizeof(simmap->d.ac.ac_static));
simdata->maxrpm = simmap->d.ac.ac_static.maxRpm;
}
simdata->rpms = simmap->d.ac.ac_physics.rpms;
simdata->gear = simmap->d.ac.ac_physics.gear;
simdata->velocity = simmap->d.ac.ac_physics.speedKmh;
simdata->altitude = 1;
break;
}
}
int siminit(SimData* simdata, SimMap* simmap, Simulator simulator)
{
slogi("searching for simulator data...");
int error = MONOCOQUE_ERROR_NONE;
void* a;
switch ( simulator )
{
case SIMULATOR_MONOCOQUE_TEST :
simmap->fd = shm_open(TEST_MEM_FILE_LOCATION, O_RDONLY, S_IRUSR | S_IWUSR);
if (simmap->fd == -1)
{
return 10;
}
simmap->addr = mmap(NULL, sizeof(SimData), PROT_READ, MAP_SHARED, simmap->fd, 0);
if (simmap->addr == MAP_FAILED)
{
return 30;
}
slogi("found data for monocoque test...");
break;
case SIMULATOR_ASSETTO_CORSA :
simmap->d.ac.has_physics=false;
simmap->d.ac.has_static=false;
simmap->fd = shm_open(AC_PHYSICS_FILE, O_RDONLY, S_IRUSR | S_IWUSR);
if (simmap->fd == -1)
{
slogd("could not open Assetto Corsa physics engine");
return MONOCOQUE_ERROR_NODATA;
}
simmap->d.ac.physics_map_addr = mmap(NULL, sizeof(simmap->d.ac.ac_physics), PROT_READ, MAP_SHARED, simmap->fd, 0);
if (simmap->d.ac.physics_map_addr == MAP_FAILED)
{
slogd("could not retrieve Assetto Corsa physics data");
return 30;
}
simmap->d.ac.has_physics=true;
simmap->fd = shm_open(AC_STATIC_FILE, O_RDONLY, S_IRUSR | S_IWUSR);
if (simmap->fd == -1)
{
slogd("could not open Assetto Corsa static data");
return 10;
}
simmap->d.ac.static_map_addr = mmap(NULL, sizeof(simmap->d.ac.ac_static), PROT_READ, MAP_SHARED, simmap->fd, 0);
if (simmap->d.ac.static_map_addr == MAP_FAILED)
{
slogd("could not retrieve Assetto Corsa static data");
return 30;
}
simmap->d.ac.has_static=true;
slogi("found data for Assetto Corsa...");
break;
}
return error;
}
+25
View File
@@ -0,0 +1,25 @@
#ifndef _SIMMAPPER_H
#define _SIMMAPPEE_H
#include "ac.h"
#include "simdata.h"
#include "../helper/confighelper.h"
#include "simapi/acdata.h"
typedef struct
{
void* addr;
int fd;
union
{
ACMap ac;
} d;
}
SimMap;
int siminit(SimData* simdata, SimMap* simmap, Simulator simulator);
int simdatamap(SimData* simdata, SimMap* simmap, Simulator simulator);
#endif
+6
View File
@@ -0,0 +1,6 @@
#ifndef _TEST_H
#define _TEST_H
#define TEST_MEM_FILE_LOCATION "/monocoque_test"
#endif
+6
View File
@@ -0,0 +1,6 @@
set(slog_source_files
slog.c
slog.h
)
add_library(slog STATIC ${slog_source_files})
+546
View File
@@ -0,0 +1,546 @@
/*
* The MIT License (MIT)
*
* Copyleft (C) 2015-2020 Sun Dro (f4tb0y@protonmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <limits.h>
#include <errno.h>
#include <time.h>
#include "slog.h"
#if !defined(__APPLE__) && !defined(DARWIN) && !defined(WIN32)
#include <syscall.h>
#endif
#include <sys/time.h>
#ifdef WIN32
#include <windows.h>
#endif
#ifndef PTHREAD_MUTEX_RECURSIVE
#define PTHREAD_MUTEX_RECURSIVE PTHREAD_MUTEX_RECURSIVE_NP
#endif
typedef struct slog
{
unsigned int nTdSafe:1;
pthread_mutex_t mutex;
slog_config_t config;
} slog_t;
typedef struct XLogCtx
{
const char* pFormat;
slog_flag_t eFlag;
slog_date_t date;
uint8_t nFullColor;
uint8_t nNewLine;
} slog_context_t;
static slog_t g_slog;
static void slog_sync_init(slog_t* pSlog)
{
if (!pSlog->nTdSafe)
{
return;
}
pthread_mutexattr_t mutexAttr;
if (pthread_mutexattr_init(&mutexAttr) ||
pthread_mutexattr_settype(&mutexAttr, PTHREAD_MUTEX_RECURSIVE) ||
pthread_mutex_init(&pSlog->mutex, &mutexAttr) ||
pthread_mutexattr_destroy(&mutexAttr))
{
printf("<%s:%d> %s: [ERROR] Can not initialize mutex: %d\n",
__FILE__, __LINE__, __FUNCTION__, errno);
exit(EXIT_FAILURE);
}
}
static void slog_lock(slog_t* pSlog)
{
if (pSlog->nTdSafe && pthread_mutex_lock(&pSlog->mutex))
{
printf("<%s:%d> %s: [ERROR] Can not lock mutex: %d\n",
__FILE__, __LINE__, __FUNCTION__, errno);
exit(EXIT_FAILURE);
}
}
static void slog_unlock(slog_t* pSlog)
{
if (pSlog->nTdSafe && pthread_mutex_unlock(&pSlog->mutex))
{
printf("<%s:%d> %s: [ERROR] Can not unlock mutex: %d\n",
__FILE__, __LINE__, __FUNCTION__, errno);
exit(EXIT_FAILURE);
}
}
static const char* slog_get_indent(slog_flag_t eFlag)
{
slog_config_t* pCfg = &g_slog.config;
if (!pCfg->nIndent)
{
return SLOG_EMPTY;
}
switch (eFlag)
{
case SLOG_NOTAG:
return SLOG_INDENT;
case SLOG_NOTE:
case SLOG_INFO:
case SLOG_WARN:
return SLOG_SPACE;
case SLOG_DEBUG:
case SLOG_TRACE:
case SLOG_FATAL:
case SLOG_ERROR:
default:
break;
}
return SLOG_EMPTY;
}
static const char* slog_get_tag(slog_flag_t eFlag)
{
switch (eFlag)
{
case SLOG_NOTE:
return "note";
case SLOG_INFO:
return "info";
case SLOG_WARN:
return "warn";
case SLOG_DEBUG:
return "debug";
case SLOG_ERROR:
return "error";
case SLOG_TRACE:
return "trace";
case SLOG_FATAL:
return "fatal";
default:
break;
}
return NULL;
}
static const char* slog_get_color(slog_flag_t eFlag)
{
switch (eFlag)
{
case SLOG_NOTAG:
case SLOG_NOTE:
return SLOG_EMPTY;
case SLOG_INFO:
return SLOG_COLOR_GREEN;
case SLOG_WARN:
return SLOG_COLOR_YELLOW;
case SLOG_DEBUG:
return SLOG_COLOR_BLUE;
case SLOG_ERROR:
return SLOG_COLOR_RED;
case SLOG_TRACE:
return SLOG_COLOR_CYAN;
case SLOG_FATAL:
return SLOG_COLOR_MAGENTA;
default:
break;
}
return SLOG_EMPTY;
}
uint8_t slog_get_usec()
{
struct timeval tv;
if (gettimeofday(&tv, NULL) < 0)
{
return 0;
}
return (uint8_t)(tv.tv_usec / 10000);
}
void slog_get_date(slog_date_t* pDate)
{
struct tm timeinfo;
time_t rawtime = time(NULL);
#ifdef WIN32
localtime_s(&timeinfo, &rawtime);
#else
localtime_r(&rawtime, &timeinfo);
#endif
pDate->nYear = timeinfo.tm_year + 1900;
pDate->nMonth = timeinfo.tm_mon + 1;
pDate->nDay = timeinfo.tm_mday;
pDate->nHour = timeinfo.tm_hour;
pDate->nMin = timeinfo.tm_min;
pDate->nSec = timeinfo.tm_sec;
pDate->nUsec = slog_get_usec();
}
static uint32_t slog_get_tid()
{
#if defined(__APPLE__) || defined(DARWIN) || defined(WIN32)
return (uint32_t)pthread_self();
#else
return syscall(__NR_gettid);
#endif
}
static void slog_create_tag(char* pOut, size_t nSize, slog_flag_t eFlag, const char* pColor)
{
slog_config_t* pCfg = &g_slog.config;
pOut[0] = SLOG_NUL;
const char* pIndent = slog_get_indent(eFlag);
const char* pTag = slog_get_tag(eFlag);
if (pTag == NULL)
{
snprintf(pOut, nSize, pIndent);
return;
}
if (pCfg->eColorFormat != SLOG_COLORING_TAG)
{
snprintf(pOut, nSize, "<%s>%s", pTag, pIndent);
}
else
{
snprintf(pOut, nSize, "%s<%s>%s%s", pColor, pTag, SLOG_COLOR_RESET, pIndent);
}
}
static void slog_create_tid(char* pOut, int nSize, uint8_t nTraceTid)
{
if (!nTraceTid)
{
pOut[0] = SLOG_NUL;
}
else
{
snprintf(pOut, nSize, "(%u) ", slog_get_tid());
}
}
static void slog_display_message(const slog_context_t* pCtx, const char* pInfo, int nInfoLen, const char* pInput)
{
slog_config_t* pCfg = &g_slog.config;
int nCbVal = 1;
const char* pSeparator = nInfoLen > 0 ? pCfg->sSeparator : SLOG_EMPTY;
const char* pReset = pCtx->nFullColor ? SLOG_COLOR_RESET : SLOG_EMPTY;
const char* pNewLine = pCtx->nNewLine ? SLOG_NEWLINE : SLOG_EMPTY;
const char* pMessage = pInput != NULL ? pInput : SLOG_EMPTY;
if (pCfg->logCallback != NULL)
{
size_t nLength = 0;
char* pLog = NULL;
nLength += asprintf(&pLog, "%s%s%s%s%s", pInfo, pSeparator, pMessage, pReset, pNewLine);
if (pLog != NULL)
{
nCbVal = pCfg->logCallback(pLog, nLength, pCtx->eFlag, pCfg->pCallbackCtx);
free(pLog);
}
}
if (pCfg->nToScreen && nCbVal > 0)
{
printf("%s%s%s%s%s", pInfo, pSeparator, pMessage, pReset, pNewLine);
if (pCfg->nFlush)
{
fflush(stdout);
}
}
if (!pCfg->nToFile || nCbVal < 0)
{
return;
}
const slog_date_t* pDate = &pCtx->date;
char sFilePath[SLOG_PATH_MAX + SLOG_NAME_MAX + SLOG_DATE_MAX];
snprintf(sFilePath, sizeof(sFilePath), "%s/%s-%04d-%02d-%02d.log",
pCfg->sFilePath, pCfg->sFileName, pDate->nYear, pDate->nMonth, pDate->nDay);
FILE* pFile = fopen(sFilePath, "a");
if (pFile == NULL)
{
return;
}
fprintf(pFile, "%s%s%s%s%s", pInfo, pSeparator, pMessage, pReset, pNewLine);
fclose(pFile);
}
static int slog_create_info(const slog_context_t* pCtx, char* pOut, size_t nSize)
{
slog_config_t* pCfg = &g_slog.config;
const slog_date_t* pDate = &pCtx->date;
char sDate[SLOG_DATE_MAX + SLOG_NAME_MAX];
sDate[0] = SLOG_NUL;
if (pCfg->eDateControl == SLOG_TIME_ONLY)
{
snprintf(sDate, sizeof(sDate), "%02d:%02d:%02d.%03d ",
pDate->nHour,pDate->nMin, pDate->nSec, pDate->nUsec);
}
else
if (pCfg->eDateControl == SLOG_DATE_FULL)
{
snprintf(sDate, sizeof(sDate), "%04d.%02d.%02d-%02d:%02d:%02d.%03d ",
pDate->nYear, pDate->nMonth, pDate->nDay, pDate->nHour,
pDate->nMin, pDate->nSec, pDate->nUsec);
}
char sTid[SLOG_TAG_MAX], sTag[SLOG_TAG_MAX];
const char* pColorCode = slog_get_color(pCtx->eFlag);
const char* pColor = pCtx->nFullColor ? pColorCode : SLOG_EMPTY;
slog_create_tid(sTid, sizeof(sTid), pCfg->nTraceTid);
slog_create_tag(sTag, sizeof(sTag), pCtx->eFlag, pColorCode);
return snprintf(pOut, nSize, "%s%s%s%s", pColor, sTid, sDate, sTag);
}
static void slog_display_heap(const slog_context_t* pCtx, va_list args)
{
size_t nBytes = 0;
char* pMessage = NULL;
char sLogInfo[SLOG_INFO_MAX];
nBytes += vasprintf(&pMessage, pCtx->pFormat, args);
va_end(args);
if (pMessage == NULL)
{
printf("<%s:%d> %s<error>%s %s: Can not allocate memory for input: errno(%d)\n",
__FILE__, __LINE__, SLOG_COLOR_RED, SLOG_COLOR_RESET, __FUNCTION__, errno);
return;
}
int nLength = slog_create_info(pCtx, sLogInfo, sizeof(sLogInfo));
slog_display_message(pCtx, sLogInfo, nLength, pMessage);
if (pMessage != NULL)
{
free(pMessage);
}
}
static void slog_display_stack(const slog_context_t* pCtx, va_list args)
{
char sMessage[SLOG_MESSAGE_MAX];
char sLogInfo[SLOG_INFO_MAX];
vsnprintf(sMessage, sizeof(sMessage), pCtx->pFormat, args);
int nLength = slog_create_info(pCtx, sLogInfo, sizeof(sLogInfo));
slog_display_message(pCtx, sLogInfo, nLength, sMessage);
}
void slog_display(slog_flag_t eFlag, uint8_t nNewLine, const char* pFormat, ...)
{
slog_lock(&g_slog);
slog_config_t* pCfg = &g_slog.config;
if ((SLOG_FLAGS_CHECK(g_slog.config.nFlags, eFlag)) &&
(g_slog.config.nToScreen || g_slog.config.nToFile))
{
slog_context_t ctx;
slog_get_date(&ctx.date);
ctx.eFlag = eFlag;
ctx.pFormat = pFormat;
ctx.nNewLine = nNewLine;
ctx.nFullColor = pCfg->eColorFormat == SLOG_COLORING_FULL ? 1 : 0;
void(*slog_display_args)(const slog_context_t* pCtx, va_list args);
slog_display_args = pCfg->nUseHeap ? slog_display_heap : slog_display_stack;
va_list args;
va_start(args, pFormat);
slog_display_args(&ctx, args);
va_end(args);
}
slog_unlock(&g_slog);
}
size_t slog_version(char* pDest, size_t nSize, uint8_t nMin)
{
size_t nLength = 0;
/* Version short */
if (nMin)
nLength = snprintf(pDest, nSize, "%d.%d.%d",
SLOG_VERSION_MAJOR, SLOG_VERSION_MINOR, SLOG_BUILD_NUM);
/* Version long */
else
nLength = snprintf(pDest, nSize, "%d.%d build %d (%s)",
SLOG_VERSION_MAJOR, SLOG_VERSION_MINOR, SLOG_BUILD_NUM, __DATE__);
return nLength;
}
void slog_config_get(slog_config_t* pCfg)
{
slog_lock(&g_slog);
*pCfg = g_slog.config;
slog_unlock(&g_slog);
}
void slog_config_set(slog_config_t* pCfg)
{
slog_lock(&g_slog);
g_slog.config = *pCfg;
slog_unlock(&g_slog);
}
void slog_enable(slog_flag_t eFlag)
{
slog_lock(&g_slog);
if (!SLOG_FLAGS_CHECK(g_slog.config.nFlags, eFlag))
{
g_slog.config.nFlags |= eFlag;
}
slog_unlock(&g_slog);
}
void slog_disable(slog_flag_t eFlag)
{
slog_lock(&g_slog);
if (SLOG_FLAGS_CHECK(g_slog.config.nFlags, eFlag))
{
g_slog.config.nFlags &= ~eFlag;
}
slog_unlock(&g_slog);
}
void slog_separator_set(const char* pFormat, ...)
{
slog_lock(&g_slog);
slog_config_t* pCfg = &g_slog.config;
va_list args;
va_start(args, pFormat);
if (vsnprintf(pCfg->sSeparator, sizeof(pCfg->sSeparator), pFormat, args) <= 0)
{
pCfg->sSeparator[0] = ' ';
pCfg->sSeparator[1] = '\0';
}
va_end(args);
slog_unlock(&g_slog);
}
void slog_indent(uint8_t nEnable)
{
slog_lock(&g_slog);
g_slog.config.nIndent = nEnable;
slog_unlock(&g_slog);
}
void slog_callback_set(slog_cb_t callback, void* pContext)
{
slog_lock(&g_slog);
slog_config_t* pCfg = &g_slog.config;
pCfg->pCallbackCtx = pContext;
pCfg->logCallback = callback;
slog_unlock(&g_slog);
}
void slog_init(const char* pName, uint16_t nFlags, uint8_t nTdSafe)
{
/* Set up default values */
slog_config_t* pCfg = &g_slog.config;
pCfg->eColorFormat = SLOG_COLORING_TAG;
pCfg->eDateControl = SLOG_TIME_ONLY;
pCfg->pCallbackCtx = NULL;
pCfg->logCallback = NULL;
pCfg->sSeparator[0] = ' ';
pCfg->sSeparator[1] = '\0';
pCfg->sFilePath[0] = '.';
pCfg->sFilePath[1] = '\0';
pCfg->nTraceTid = 0;
pCfg->nToScreen = 1;
pCfg->nUseHeap = 0;
pCfg->nToFile = 0;
pCfg->nIndent = 0;
pCfg->nFlush = 0;
pCfg->nFlags = nFlags;
const char* pFileName = (pName != NULL) ? pName : SLOG_NAME_DEFAULT;
snprintf(pCfg->sFileName, sizeof(pCfg->sFileName), "%s", pFileName);
#ifdef WIN32
// Enable color support
HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD dwMode = 0;
GetConsoleMode(hOutput, &dwMode);
dwMode |= ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING;
SetConsoleMode(hOutput, dwMode);
#endif
/* Initialize mutex */
g_slog.nTdSafe = nTdSafe;
slog_sync_init(&g_slog);
}
void slog_destroy()
{
g_slog.config.pCallbackCtx = NULL;
g_slog.config.logCallback = NULL;
if (g_slog.nTdSafe)
{
pthread_mutex_destroy(&g_slog.mutex);
g_slog.nTdSafe = 0;
}
}
+195
View File
@@ -0,0 +1,195 @@
/*
* The MIT License (MIT)
*
* Copyleft (C) 2015-2020 Sun Dro (f4tb0y@protonmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE
*/
#ifndef __SLOG_H__
#define __SLOG_H__
#ifdef __cplusplus
extern "C" {
#endif
#include <inttypes.h>
#include <pthread.h>
/* SLog version information */
#define SLOG_VERSION_MAJOR 1
#define SLOG_VERSION_MINOR 8
#define SLOG_BUILD_NUM 26
/* Supported colors */
#define SLOG_COLOR_NORMAL "\x1B[0m"
#define SLOG_COLOR_RED "\x1B[31m"
#define SLOG_COLOR_GREEN "\x1B[32m"
#define SLOG_COLOR_YELLOW "\x1B[33m"
#define SLOG_COLOR_BLUE "\x1B[34m"
#define SLOG_COLOR_MAGENTA "\x1B[35m"
#define SLOG_COLOR_CYAN "\x1B[36m"
#define SLOG_COLOR_WHITE "\x1B[37m"
#define SLOG_COLOR_RESET "\033[0m"
/* Trace source location helpers */
#define SLOG_TRACE_LVL1(LINE) #LINE
#define SLOG_TRACE_LVL2(LINE) SLOG_TRACE_LVL1(LINE)
#define SLOG_THROW_LOCATION "[" __FILE__ ":" SLOG_TRACE_LVL2(__LINE__) "] "
/* SLog limits (To be safe while avoiding dynamic allocations) */
#define SLOG_MESSAGE_MAX 8196
#define SLOG_VERSION_MAX 128
#define SLOG_PATH_MAX 2048
#define SLOG_INFO_MAX 512
#define SLOG_NAME_MAX 256
#define SLOG_DATE_MAX 64
#define SLOG_TAG_MAX 32
#define SLOG_COLOR_MAX 16
#define SLOG_FLAGS_CHECK(c, f) (((c) & (f)) == (f))
#define SLOG_FLAGS_ALL 255
#define SLOG_NAME_DEFAULT "slog"
#define SLOG_NEWLINE "\n"
#define SLOG_INDENT " "
#define SLOG_SPACE " "
#define SLOG_EMPTY ""
#define SLOG_NUL '\0'
typedef struct SLogDate
{
uint16_t nYear;
uint8_t nMonth;
uint8_t nDay;
uint8_t nHour;
uint8_t nMin;
uint8_t nSec;
uint8_t nUsec;
} slog_date_t;
uint8_t slog_get_usec();
void slog_get_date(slog_date_t* pDate);
/* Log level flags */
typedef enum
{
SLOG_NOTAG = (1 << 0),
SLOG_NOTE = (1 << 1),
SLOG_INFO = (1 << 2),
SLOG_WARN = (1 << 3),
SLOG_DEBUG = (1 << 4),
SLOG_TRACE = (1 << 5),
SLOG_ERROR = (1 << 6),
SLOG_FATAL = (1 << 7)
} slog_flag_t;
typedef int(*slog_cb_t)(const char* pLog, size_t nLength, slog_flag_t eFlag, void* pCtx);
/* Output coloring control flags */
typedef enum
{
SLOG_COLORING_DISABLE = 0,
SLOG_COLORING_TAG,
SLOG_COLORING_FULL
} slog_coloring_t;
typedef enum
{
SLOG_TIME_DISABLE = 0,
SLOG_TIME_ONLY,
SLOG_DATE_FULL
} slog_date_ctrl_t;
#define slog(...) \
slog_display(SLOG_NOTAG, 1, __VA_ARGS__)
#define slogwn(...) \
slog_display(SLOG_NOTAG, 0, __VA_ARGS__)
#define slog_note(...) \
slog_display(SLOG_NOTE, 1, __VA_ARGS__)
#define slog_info(...) \
slog_display(SLOG_INFO, 1, __VA_ARGS__)
#define slog_warn(...) \
slog_display(SLOG_WARN, 1, __VA_ARGS__)
#define slog_debug(...) \
slog_display(SLOG_DEBUG, 1, __VA_ARGS__)
#define slog_error(...) \
slog_display(SLOG_ERROR, 1, __VA_ARGS__)
#define slog_trace(...) \
slog_display(SLOG_TRACE, 1, SLOG_THROW_LOCATION __VA_ARGS__)
#define slog_fatal(...) \
slog_display(SLOG_FATAL, 1, SLOG_THROW_LOCATION __VA_ARGS__)
/* Short name definitions */
#define slogn(...) slog_note(__VA_ARGS__)
#define slogi(...) slog_info(__VA_ARGS__)
#define slogw(...) slog_warn(__VA_ARGS__)
#define slogd(...) slog_debug( __VA_ARGS__)
#define sloge(...) slog_error( __VA_ARGS__)
#define slogt(...) slog_trace(__VA_ARGS__)
#define slogf(...) slog_fatal(__VA_ARGS__)
typedef struct SLogConfig
{
slog_date_ctrl_t eDateControl; // Display output with date format
slog_coloring_t eColorFormat; // Output color format control
slog_cb_t logCallback; // Log callback to collect logs
void* pCallbackCtx; // Data pointer passed to log callback
uint8_t nTraceTid; // Trace thread ID and display in output
uint8_t nToScreen; // Enable screen logging
uint8_t nUseHeap; // Use dynamic allocation
uint8_t nToFile; // Enable file logging
uint8_t nIndent; // Enable indentations
uint8_t nFlush; // Flush stdout after screen log
uint16_t nFlags; // Allowed log level flags
char sSeparator[SLOG_NAME_MAX]; // Separator between info and log
char sFileName[SLOG_NAME_MAX]; // Output file name for logs
char sFilePath[SLOG_PATH_MAX]; // Output file path for logs
} slog_config_t;
size_t slog_version(char* pDest, size_t nSize, uint8_t nMin);
void slog_config_get(slog_config_t* pCfg);
void slog_config_set(slog_config_t* pCfg);
void slog_separator_set(const char* pFormat, ...);
void slog_callback_set(slog_cb_t callback, void* pContext);
void slog_indent(uint8_t nEnable);
void slog_enable(slog_flag_t eFlag);
void slog_disable(slog_flag_t eFlag);
void slog_init(const char* pName, uint16_t nFlags, uint8_t nTdSafe);
void slog_display(slog_flag_t eFlag, uint8_t nNewLine, const char* pFormat, ...);
void slog_destroy(); // Needed only if the slog_init() function argument nTdSafe > 0
#ifdef __cplusplus
}
#endif
#endif /* __SLOG_H__ */