/** * Actuator control thread * @param arg - Cast to an Actuator* * @returns NULL to keep pthreads happy */ void * Actuator_Loop(void * arg) { Actuator * a = (Actuator*)(arg); // Loop until stopped while (a->activated) { pthread_mutex_lock(&(a->mutex)); while (!a->control_changed) { pthread_cond_wait(&(a->cond), &(a->mutex)); } a->control_changed = false; pthread_mutex_unlock(&(a->mutex)); if (!a->activated) break; 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) { clock_nanosleep(CLOCK_MONOTONIC, 0, &wait, NULL); a->control.start += a->control.stepsize; Actuator_SetValue(a, a->control.start, true); a->control.steps--; } if (a->control_changed) continue; 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, // You can call the Actuator's cleanup function immediately (and this loop should later just exit) // tl;dr This function isn't/shouldn't be responsible for the emergency Actuator stuff // (That should be handled by the Fatal function... at some point) } //TODO: Cleanup? // Keep pthreads happy return NULL; }
/** * Add and initialise a Sensor * @param name - Human readable name of the sensor * @param user_id - User identifier * @param read - Function to call whenever the sensor should be read * @param init - Function to call to initialise the sensor (may be NULL) * @param max_error - Maximum error threshold; program will exit if this is exceeded for the sensor reading * @param min_error - Minimum error threshold; program will exit if the sensor reading falls below this value * @param max_warn - Maximum warning threshold; program will log warnings if the value exceeds this threshold * @param min_warn - Minimum warning threshold; program will log warnings if the value falls below this threshold * @returns Number of actuators added so far */ int Sensor_Add(const char * name, int user_id, ReadFn read, InitFn init, CleanFn cleanup, SanityFn sanity) { if (++g_num_sensors > SENSORS_MAX) { Fatal("Too many sensors; Increase SENSORS_MAX from %d in sensor.h and recompile", SENSORS_MAX); // We could design the program to use realloc(3) // But since someone who adds a new sensor has to recompile the program anyway... } Sensor * s = &(g_sensors[g_num_sensors-1]); s->id = g_num_sensors-1; s->user_id = user_id; Data_Init(&(s->data_file)); s->name = name; s->read = read; // Set read function s->init = init; // Set init function // Start by averaging values taken over a second DOUBLE_TO_TIMEVAL(1, &(s->sample_time)); s->averages = 1; s->num_read = 0; // Set sanity function s->sanity = sanity; if (init != NULL) { if (!init(name, user_id)) Fatal("Couldn't init sensor %s", name); } s->current_data.time_stamp = 0; s->current_data.value = 0; s->averaged_data.time_stamp = 0; s->averaged_data.value = 0; return g_num_sensors; }
/** * Handle a request to the sensor module * @param context - The context to work in * @param params - Parameters passed */ void Sensor_Handler(FCGIContext *context, char * params) { struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); double current_time = TIMEVAL_DIFF(now, *Control_GetStartTime()); int id = 0; const char * name = ""; double start_time = 0; double end_time = current_time; const char * fmt_str; double sample_s = 0; // key/value pairs FCGIValue values[] = { {"id", &id, FCGI_INT_T}, {"name", &name, FCGI_STRING_T}, {"format", &fmt_str, FCGI_STRING_T}, {"start_time", &start_time, FCGI_DOUBLE_T}, {"end_time", &end_time, FCGI_DOUBLE_T}, {"sample_s", &sample_s, FCGI_DOUBLE_T} }; // enum to avoid the use of magic numbers typedef enum { ID, NAME, FORMAT, START_TIME, END_TIME, SAMPLE_S } SensorParams; // Fill values appropriately if (!FCGI_ParseRequest(context, params, values, sizeof(values)/sizeof(FCGIValue))) { // Error occured; FCGI_RejectJSON already called return; } Sensor * s = NULL; if (FCGI_RECEIVED(values[NAME].flags)) { if (FCGI_RECEIVED(values[ID].flags)) { FCGI_RejectJSON(context, "Can't supply both sensor id and name"); return; } s = Sensor_Identify(name); if (s == NULL) { FCGI_RejectJSON(context, "Unknown sensor name"); return; } } else if (!FCGI_RECEIVED(values[ID].flags)) { FCGI_RejectJSON(context, "No sensor id or name supplied"); return; } else if (id < 0 || id >= g_num_sensors) { FCGI_RejectJSON(context, "Invalid sensor id"); return; } else { s = &(g_sensors[id]); } // Adjust sample rate if necessary if (FCGI_RECEIVED(values[SAMPLE_S].flags)) { if (sample_s < 0) { FCGI_RejectJSON(context, "Negative sampling speed!"); return; } DOUBLE_TO_TIMEVAL(sample_s, &(s->sample_time)); } DataFormat format = Data_GetFormat(&(values[FORMAT])); // Begin response Sensor_BeginResponse(context, s, format); // Print Data Data_Handler(&(s->data_file), &(values[START_TIME]), &(values[END_TIME]), format, current_time); // Finish response Sensor_EndResponse(context, s, format); }