From: Sam Moore Date: Sat, 19 Oct 2013 15:40:22 +0000 (+0800) Subject: Merge branch 'master' of github:szmoore/MCTX3420 X-Git-Url: https://git.ucc.asn.au/?a=commitdiff_plain;h=544b54c409f3731b8e1581af95072a64ff393a28;hp=cf4f872e4a4d739408bd2f0d71887d27ebe598c9;p=matches%2FMCTX3420.git Merge branch 'master' of github:szmoore/MCTX3420 Conflicts: server/control.c --- diff --git a/server/Makefile b/server/Makefile index c5be05f..39a71fb 100644 --- a/server/Makefile +++ b/server/Makefile @@ -1,6 +1,6 @@ # Makefile for server software CXX = gcc -FLAGS = -std=c99 -Wall -pedantic -g -I/usr/include/opencv -I/usr/include/opencv2/highgui -L/usr/lib +FLAGS = -std=gnu99 -Wall -pedantic -g -I/usr/include/opencv -I/usr/include/opencv2/highgui -L/usr/lib LIB = -lfcgi -lssl -lcrypto -lpthread -lm -lopencv_highgui -lopencv_core -lopencv_ml -lopencv_imgproc -lldap -lcrypt OBJ = log.o control.o data.o fastcgi.o main.o sensor.o actuator.o image.o bbb_pin.o pin_test.o login.o sensors/sensors.a actuators/actuators.a RM = rm -f diff --git a/server/actuator.c b/server/actuator.c index 2be4194..1c105db 100644 --- a/server/actuator.c +++ b/server/actuator.c @@ -171,9 +171,12 @@ void * Actuator_Loop(void * arg) Actuator_SetValue(a, a->control.start, true); // Currently does discrete steps after specified time intervals + + struct timespec wait; + DOUBLE_TO_TIMEVAL(a->control.stepsize, &wait); while (!a->control_changed && a->control.steps > 0 && a->activated) { - usleep(1e6*(a->control.stepwait)); + clock_nanosleep(CLOCK_MONOTONIC, 0, &wait, NULL); a->control.start += a->control.stepsize; Actuator_SetValue(a, a->control.start, true); @@ -181,7 +184,7 @@ void * Actuator_Loop(void * arg) } if (a->control_changed) continue; - usleep(1e6*(a->control.stepwait)); + clock_nanosleep(CLOCK_MONOTONIC, 0, &wait, NULL); //TODO: // Note that although this loop has a sleep in it which would seem to make it hard to enforce urgent shutdowns, @@ -231,8 +234,8 @@ void Actuator_SetValue(Actuator * a, double value, bool record) } // Set time stamp - struct timeval t; - gettimeofday(&t, NULL); + struct timespec t; + clock_gettime(CLOCK_MONOTONIC, &t); DataPoint d = {TIMEVAL_DIFF(t, *Control_GetStartTime()), a->last_setting.value}; // Record value change if (record) @@ -296,8 +299,8 @@ void Actuator_EndResponse(FCGIContext * context, Actuator * a, DataFormat format */ void Actuator_Handler(FCGIContext * context, char * params) { - struct timeval now; - gettimeofday(&now, NULL); + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); double current_time = TIMEVAL_DIFF(now, *Control_GetStartTime()); int id = 0; char * name = ""; diff --git a/server/common.h b/server/common.h index fa997c5..0875b34 100644 --- a/server/common.h +++ b/server/common.h @@ -8,9 +8,9 @@ /** Defines required to allow various C standard functions to be used **/ #define _POSIX_C_SOURCE 200809L -#define _BSD_SOURCE +//#define _BSD_SOURCE #define _XOPEN_SOURCE 600 - +#define _GNU_SOURCE /** Determine if we're running on the BBB **/ #ifdef __arm__ #define _BBB @@ -21,6 +21,7 @@ /** The current API version **/ #define API_VERSION 0 +//#define REALTIME_VERSION @@ -34,21 +35,25 @@ #include #include #include +#include #include "log.h" #include "fastcgi.h" #include "control.h" /**Converts a timeval to a double**/ -#define TIMEVAL_TO_DOUBLE(tv) ((tv).tv_sec + 1e-6 * ((tv).tv_usec)) +#define TIMEVAL_TO_DOUBLE(tv) ((tv).tv_sec + 1e-9 * ((tv).tv_nsec)) /**Takes the tv1-tv2 between two timevals and returns the result as a double*/ -#define TIMEVAL_DIFF(tv1, tv2) ((tv1).tv_sec - (tv2).tv_sec + 1e-6 * ((tv1).tv_usec - (tv2).tv_usec)) - +#define TIMEVAL_DIFF(tv1, tv2) ((tv1).tv_sec - (tv2).tv_sec + 1e-9 * ((tv1).tv_nsec - (tv2).tv_nsec)) +/** Converts a double time value (in seconds) to a timespec **/ +#define DOUBLE_TO_TIMEVAL(value, tv) { \ + (tv)->tv_sec = (int)(value); \ + (tv)->tv_nsec = ((value) - (int)(value))*1e9; \ + } extern bool PathExists(const char * path); extern bool DirExists(const char * path); - #endif //_COMMON_H diff --git a/server/control.c b/server/control.c index 4342c8c..6ebaba7 100644 --- a/server/control.c +++ b/server/control.c @@ -14,7 +14,7 @@ typedef struct ControlData { ControlModes current_mode; pthread_mutex_t mutex; - struct timeval start_time; + struct timespec start_time; char user_name[31]; // The user who owns the currently running experiment char experiment_dir[BUFSIZ]; //Directory for experiment char experiment_name[BUFSIZ]; @@ -225,7 +225,7 @@ const char* Control_SetMode(ControlModes desired_mode, void * arg) path, strerror(errno)); ret = "Couldn't create experiment directory."; } else { - gettimeofday(&(g_controls.start_time), NULL); + clock_gettime(CLOCK_MONOTONIC, &(g_controls.start_time)); } } else ret = "Cannot start when not in a stopped state."; @@ -280,6 +280,6 @@ const char * Control_GetModeName() { * Gets the start time for the current experiment * @return the start time */ -const struct timeval* Control_GetStartTime() { +const struct timespec * Control_GetStartTime() { return &g_controls.start_time; } diff --git a/server/control.h b/server/control.h index 3ceea9f..7081c3f 100644 --- a/server/control.h +++ b/server/control.h @@ -24,6 +24,6 @@ extern ControlModes Control_GetMode(); extern const char * Control_GetModeName(); //extern bool Control_Lock(); //extern void Control_Unlock(); -extern const struct timeval* Control_GetStartTime(); +extern const struct timespec* Control_GetStartTime(); #endif diff --git a/server/data.c b/server/data.c index dae3172..edfd4c3 100644 --- a/server/data.c +++ b/server/data.c @@ -161,13 +161,13 @@ void Data_PrintByIndexes(DataFile * df, int start_index, int end_index, DataForm switch (format) { case JSON: - fmt_string = "[%f,%f]"; + fmt_string = "[%.9f,%f]"; separator = ','; // For JSON we need an opening bracket FCGI_PrintRaw("["); break; case TSV: - fmt_string = "%f\t%f"; + fmt_string = "%.9f\t%f"; separator = '\n'; break; } diff --git a/server/fastcgi.c b/server/fastcgi.c index fd5839e..0a6783f 100644 --- a/server/fastcgi.c +++ b/server/fastcgi.c @@ -50,9 +50,14 @@ static void IdentifyHandler(FCGIContext *context, char *params) { FCGI_BeginJSON(context, STATUS_OK); FCGI_JSONPair("description", "MCTX3420 Server API (2013)"); FCGI_JSONPair("build_date", __DATE__ " " __TIME__); + struct timespec t; + t.tv_sec = 0; t.tv_nsec = 0; + clock_getres(CLOCK_MONOTONIC, &t); + FCGI_JSONDouble("clock_getres", TIMEVAL_TO_DOUBLE(t)); FCGI_JSONLong("api_version", API_VERSION); FCGI_JSONBool("logged_in", has_control); FCGI_JSONPair("user_name", has_control ? context->user_name : ""); + //Sensor and actuator information if (ident_sensors) { @@ -328,8 +333,8 @@ void FCGI_BeginJSON(FCGIContext *context, StatusCodes status_code) printf("\t\"module\" : \"%s\"", context->current_module); FCGI_JSONLong("status", status_code); //Time and running statistics - struct timeval now; - gettimeofday(&now, NULL); + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); FCGI_JSONDouble("start_time", TIMEVAL_TO_DOUBLE(g_options.start_time)); FCGI_JSONDouble("current_time", TIMEVAL_TO_DOUBLE(now)); FCGI_JSONDouble("running_time", TIMEVAL_DIFF(now, g_options.start_time)); @@ -383,7 +388,7 @@ void FCGI_JSONLong(const char *key, long value) */ void FCGI_JSONDouble(const char *key, double value) { - printf(",\r\n\t\"%s\" : %f", key, value); + printf(",\r\n\t\"%s\" : %.9f", key, value); } /** @@ -554,8 +559,8 @@ void * FCGI_RequestLoop (void *data) if (module_handler) { - if (module_handler != Login_Handler && module_handler != IdentifyHandler && module_handler) - //if (false) // Testing + //if (module_handler != Login_Handler && module_handler != IdentifyHandler && module_handler) + if (false) // Testing { if (!FCGI_HasControl(&context, control_key)) { diff --git a/server/image.h b/server/image.h index b62a7bd..3c1ab69 100644 --- a/server/image.h +++ b/server/image.h @@ -8,7 +8,9 @@ #include "common.h" +extern void Image_Init(); extern void Image_Handler(FCGIContext * context, char * params); +extern void Image_Cleanup(); #endif //_IMAGE_H diff --git a/server/main.c b/server/main.c index e7edc7e..fadcd3a 100644 --- a/server/main.c +++ b/server/main.c @@ -17,6 +17,13 @@ #include // for signal handling +#ifdef REALTIME_VERSION +#include +#include +#include +#include +#endif //REALTIME_VERSION + // --- Variable definitions --- // Options g_options; // options passed to program through command line arguments @@ -35,7 +42,7 @@ void ParseArguments(int argc, char ** argv) //if (getcwd(g_options.root_dir, sizeof(g_options.root_dir)) == NULL) // Fatal("Couldn't get current working directory - %s", strerror(errno)); - gettimeofday(&(g_options.start_time), NULL); // Start time + clock_gettime(CLOCK_MONOTONIC, &(g_options.start_time)); // Start time g_options.auth_method = AUTH_NONE; // Don't use authentication @@ -120,6 +127,38 @@ void Cleanup() Log(LOGDEBUG, "Finish cleanup."); } + +#ifdef REALTIME_VERSION + +#define MAX_SAFE_STACK (8*1024) +#define NSEC_PER_SEC (1000000000) + +void stack_prefault() +{ + unsigned char dummy[MAX_SAFE_STACK]; + memset(dummy, 0, MAX_SAFE_STACK); + return; +} + +bool is_realtime() +{ + struct utsname u; + bool crit1 = 0; + bool crit2 = 0; + FILE * f; + uname(&u); + crit1 = (strcasestr(u.version, "PREEMPT RT") != NULL); + if (crit1 && ((f = fopen("/sys/kernel/realtime", "r")) != NULL)) + { + int flag; + crit2 = ((fscanf(f, "%d", &flag) == 1) && (flag == 1)); + fclose(f); + } + return (crit1 && crit2); +} + +#endif //REALTIME_VERSION + /** * Main entry point; start worker threads, setup signal handling, wait for threads to exit, exit * @param argc - Num args @@ -129,30 +168,55 @@ void Cleanup() */ int main(int argc, char ** argv) { + // Open log before calling ParseArguments (since ParseArguments may call the Log functions) openlog("mctxserv", LOG_PID | LOG_PERROR, LOG_USER); - Log(LOGINFO, "Server started"); ParseArguments(argc, argv); // Setup the g_options structure from program arguments + Log(LOGINFO, "Server started"); + + + + #ifdef REALTIME_VERSION + + if (is_realtime()) + { + Log(LOGDEBUG, "Running under realtime kernel"); + } + else + { + Fatal("Not running under realtime kernel"); + } + struct sched_param param; + param.sched_priority = 49; + if (sched_setscheduler(0, SCHED_FIFO, ¶m) < 0) + Fatal("sched_setscheduler failed - %s", strerror(errno)); + if (mlockall(MCL_CURRENT | MCL_FUTURE) == -1) + Fatal("mlockall failed - %s", strerror(errno)); + stack_prefault(); + #endif //REALTIME_VERSION + + + Sensor_Init(); Actuator_Init(); Pin_Init(); // Try and start things - /* + const char *ret; if ((ret = Control_SetMode(CONTROL_START, "test")) != NULL) Fatal("Control_SetMode failed with '%s'", ret); - */ + // run request thread in the main thread FCGI_RequestLoop(NULL); - /* + if ((ret = Control_SetMode(CONTROL_STOP, "test")) != NULL) Fatal("Control_SetMode failed with '%s'", ret); - */ + //Sensor_StopAll(); //Actuator_StopAll(); diff --git a/server/options.h b/server/options.h index 385a05b..0e0b598 100644 --- a/server/options.h +++ b/server/options.h @@ -15,9 +15,9 @@ typedef struct /** Determines at what level log messages are shown **/ int verbosity; /** Time at which program begins to run **/ - struct timeval start_time; + struct timespec start_time; /** Time at which program exits **/ - struct timeval end_time; + struct timespec end_time; /** Whether or not to enable the pin_test module **/ bool enable_pin; diff --git a/server/sensor.c b/server/sensor.c index c41d39e..ae5cdf4 100644 --- a/server/sensor.c +++ b/server/sensor.c @@ -47,8 +47,9 @@ int Sensor_Add(const char * name, int user_id, ReadFn read, InitFn init, CleanFn s->init = init; // Set init function // Start by averaging values taken over a second - s->sample_us = 1e6; + DOUBLE_TO_TIMEVAL(1e-4, &(s->sample_time)); s->averages = 1; + s->num_read = 0; // Set sanity function s->sanity = sanity; @@ -59,7 +60,8 @@ int Sensor_Add(const char * name, int user_id, ReadFn read, InitFn init, CleanFn Fatal("Couldn't init sensor %s", name); } - + s->current_data.time_stamp = 0; + s->current_data.value = 0; return g_num_sensors; } @@ -75,9 +77,9 @@ void Sensor_Init() { Sensor_Add("cpu_stime", RESOURCE_CPU_SYS, Resource_Read, NULL, NULL, NULL); Sensor_Add("cpu_utime", RESOURCE_CPU_USER, Resource_Read, NULL, NULL, NULL); - Sensor_Add("pressure_high0", PRES_HIGH0, Pressure_Read, Pressure_Init, Pressure_Cleanup, NULL); - Sensor_Add("pressure_high1", PRES_HIGH1, Pressure_Read, Pressure_Init, Pressure_Cleanup, NULL); - Sensor_Add("pressure_low0", PRES_LOW0, Pressure_Read, Pressure_Init, Pressure_Cleanup, NULL); + //Sensor_Add("pressure_high0", PRES_HIGH0, Pressure_Read, Pressure_Init, Pressure_Cleanup, NULL); + //Sensor_Add("pressure_high1", PRES_HIGH1, Pressure_Read, Pressure_Init, Pressure_Cleanup, NULL); + //Sensor_Add("pressure_low0", PRES_LOW0, Pressure_Read, Pressure_Init, Pressure_Cleanup, NULL); //Sensor_Add("../testing/count.py", 0, Piped_Read, Piped_Init, Piped_Cleanup, 1e50,-1e50,1e50,-1e50); //Sensor_Add("strain0", STRAIN0, Strain_Read, Strain_Init, 5000,0,5000,0); //Sensor_Add("strain1", STRAIN1, Strain_Read, Strain_Init, 5000,0,5000,0); @@ -203,8 +205,8 @@ void * Sensor_Loop(void * arg) d.value = 0; bool success = s->read(s->user_id, &(d.value)); - struct timeval t; - gettimeofday(&t, NULL); + struct timespec t; + clock_gettime(CLOCK_MONOTONIC, &t); d.time_stamp = TIMEVAL_DIFF(t, *Control_GetStartTime()); if (success) @@ -216,12 +218,25 @@ void * Sensor_Loop(void * arg) Fatal("Sensor %s (%d,%d) reads unsafe value", s->name, s->id, s->user_id); } } - Data_Save(&(s->data_file), &d, 1); // Record it + s->current_data.time_stamp += d.time_stamp; + s->current_data.value += d.value; + + if (++(s->num_read) >= s->averages) + { + s->current_data.time_stamp /= s->averages; + s->current_data.value /= s->averages; + Data_Save(&(s->data_file), &(s->current_data), 1); // Record it + s->num_read = 0; + s->current_data.time_stamp = 0; + s->current_data.value = 0; + } } else Log(LOGWARN, "Failed to read sensor %s (%d,%d)", s->name, s->id,s->user_id); - usleep(s->sample_us); + + clock_nanosleep(CLOCK_MONOTONIC, 0, &(s->sample_time), NULL); + } // Needed to keep pthreads happy @@ -292,8 +307,8 @@ void Sensor_EndResponse(FCGIContext * context, Sensor * s, DataFormat format) */ void Sensor_Handler(FCGIContext *context, char * params) { - struct timeval now; - gettimeofday(&now, NULL); + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); double current_time = TIMEVAL_DIFF(now, *Control_GetStartTime()); int id = 0; const char * name = ""; @@ -367,7 +382,7 @@ void Sensor_Handler(FCGIContext *context, char * params) FCGI_RejectJSON(context, "Negative sampling speed!"); return; } - s->sample_us = 1e6*sample_s; + DOUBLE_TO_TIMEVAL(sample_s, &(s->sample_time)); } diff --git a/server/sensor.h b/server/sensor.h index 0285252..0fe2136 100644 --- a/server/sensor.h +++ b/server/sensor.h @@ -56,7 +56,7 @@ typedef struct /** Human readable name of the sensor **/ const char * name; /** Sampling rate **/ - int sample_us; + struct timespec sample_time; /** Number of averages per sample **/ int averages; diff --git a/server/sensors/pressure.c b/server/sensors/pressure.c index ddb46df..61cdbcf 100644 --- a/server/sensors/pressure.c +++ b/server/sensors/pressure.c @@ -46,7 +46,7 @@ double Pressure_Callibrate(int id, int adc) case PRES_HIGH1: { static const double Vs = 5e3; // In mVs - static const double Pmin = 0.0; + static const double Pmin = 0.0 * PSI_TO_KPA; static const double Pmax = 150.0 * PSI_TO_KPA; double Vout = ADC_TO_MVOLTS(adc); return ((Vout - 0.1*Vs)/(0.8*Vs))*(Pmax - Pmin) + Pmin; @@ -93,9 +93,10 @@ bool Pressure_Read(int id, double * value) //static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; //pthread_mutex_lock(&mutex); bool result = false; - if (ADC_Read(Pressure_GetADC(id), value)) + int adc = 0; + if (ADC_Read(Pressure_GetADC(id), &adc)) { - *value = Pressure_Callibrate(id, *value); + *value = Pressure_Callibrate(id, adc); result = true; } //pthread_mutex_unlock(&mutex); diff --git a/server/sensors/resource.c b/server/sensors/resource.c index fa52562..80c8594 100644 --- a/server/sensors/resource.c +++ b/server/sensors/resource.c @@ -17,10 +17,10 @@ bool Resource_Read(int id, double * value) switch (id) { case RESOURCE_CPU_USER: - *value = TIMEVAL_TO_DOUBLE(usage.ru_utime); + *value = usage.ru_utime.tv_sec + 1e-6*usage.ru_utime.tv_usec; break; case RESOURCE_CPU_SYS: - *value = TIMEVAL_TO_DOUBLE(usage.ru_stime); + *value = usage.ru_stime.tv_sec + 1e-6*usage.ru_stime.tv_usec; break; default: Log(LOGWARN, "Unknown id %d", id); diff --git a/server/sensors/strain.c b/server/sensors/strain.c index 3ee1624..9492de7 100644 --- a/server/sensors/strain.c +++ b/server/sensors/strain.c @@ -33,7 +33,7 @@ static int Strain_To_GPIO(StrainID id) default: Fatal("Unknown StrainID %d", id); return -1; // Should never happen - } + } } /**