From: Jeremy Tan Date: Mon, 14 Oct 2013 14:37:03 +0000 (+0800) Subject: Merge branch 'master' of https://github.com/szmoore/MCTX3420.git X-Git-Url: https://git.ucc.asn.au/?a=commitdiff_plain;h=f7e1e1e4b7c22ef34702cff9b01025612809aab8;hp=436f212a07c30061ccca52ff0c107ccf99921d83;p=matches%2FMCTX3420.git Merge branch 'master' of https://github.com/szmoore/MCTX3420.git --- diff --git a/server/actuator.c b/server/actuator.c index ab48d19..9b9a1a8 100644 --- a/server/actuator.c +++ b/server/actuator.c @@ -23,7 +23,7 @@ static Actuator g_actuators[ACTUATORS_MAX]; * @param init - Function to call to initialise the actuator (may be NULL) * @returns Number of actuators added so far */ -int Actuator_Add(const char * name, int user_id, SetFn set, InitFn init, CleanFn cleanup, SanityFn sanity) +int Actuator_Add(const char * name, int user_id, SetFn set, InitFn init, CleanFn cleanup, SanityFn sanity, double initial_value) { if (++g_num_actuators > ACTUATORS_MAX) { @@ -36,12 +36,19 @@ int Actuator_Add(const char * name, int user_id, SetFn set, InitFn init, CleanFn a->name = name; a->set = set; // Set read function a->init = init; // Set init function - if (init != NULL) - init(name, user_id); // Call it + a->sanity = sanity; pthread_mutex_init(&(a->mutex), NULL); + if (init != NULL) + { + if (!init(name, user_id)) + Fatal("Couldn't initialise actuator %s", name); + } + + Actuator_SetValue(a, initial_value, false); + return g_num_actuators; } @@ -54,7 +61,7 @@ int Actuator_Add(const char * name, int user_id, SetFn set, InitFn init, CleanFn void Actuator_Init() { //Actuator_Add("ledtest",0, Ledtest_Set, NULL,NULL,NULL); - Actuator_Add("filetest", 0, Filetest_Set, Filetest_Init, Filetest_Cleanup, Filetest_Sanity); + Actuator_Add("filetest", 0, Filetest_Set, Filetest_Init, Filetest_Cleanup, Filetest_Sanity, 0); } /** @@ -160,16 +167,18 @@ void * Actuator_Loop(void * arg) if (!a->activated) break; - Actuator_SetValue(a, a->control.start); + Actuator_SetValue(a, a->control.start, true); // Currently does discrete steps after specified time intervals - while (a->control.steps > 0 && a->activated) + while (!a->control_changed && a->control.steps > 0 && a->activated) { usleep(1e6*(a->control.stepwait)); a->control.start += a->control.stepsize; - Actuator_SetValue(a, a->control.start); + Actuator_SetValue(a, a->control.start, true); a->control.steps--; } + if (a->control_changed) + continue; usleep(1e6*(a->control.stepwait)); //TODO: @@ -206,12 +215,13 @@ void Actuator_SetControl(Actuator * a, ActuatorControl * c) * @param a - The Actuator * @param value - The value to set */ -void Actuator_SetValue(Actuator * a, double value) +void Actuator_SetValue(Actuator * a, double value, bool record) { if (a->sanity != NULL && !a->sanity(a->user_id, value)) { //ARE YOU INSANE? - Fatal("Insane value %lf for actuator %s", value, a->name); + Log(LOGERR,"Insane value %lf for actuator %s", value, a->name); + return; } if (!(a->set(a->user_id, value))) { @@ -221,9 +231,17 @@ void Actuator_SetValue(Actuator * a, double value) // Set time stamp struct timeval t; gettimeofday(&t, NULL); - // Record and save DataPoint - DataPoint d = {TIMEVAL_DIFF(t, *Control_GetStartTime()), value}; - Data_Save(&(a->data_file), &d, 1); + DataPoint d = {TIMEVAL_DIFF(t, *Control_GetStartTime()), a->last_setting.value}; + // Record value change + if (record) + { + d.time_stamp -= 1e-6; + Data_Save(&(a->data_file), &d, 1); + d.value = value; + d.time_stamp += 1e-6; + Data_Save(&(a->data_file), &d, 1); + } + a->last_setting = d; } /** diff --git a/server/actuator.h b/server/actuator.h index de81eea..f818526 100644 --- a/server/actuator.h +++ b/server/actuator.h @@ -68,6 +68,8 @@ typedef struct SanityFn sanity; /** Cleanup function **/ CleanFn clean; + /** Last setting **/ + DataPoint last_setting; } Actuator; @@ -77,7 +79,7 @@ extern void Actuator_SetModeAll(ControlModes mode, void *arg); extern void Actuator_SetMode(Actuator * a, ControlModes mode, void *arg); extern void * Actuator_Loop(void * args); // Main loop for a thread that handles an Actuator -extern void Actuator_SetValue(Actuator * a, double value); // Set an actuator by value +extern void Actuator_SetValue(Actuator * a, double value, bool record); // Set an actuator by value extern void Actuator_SetControl(Actuator * a, ActuatorControl * c); // Set the control for an Actuator extern Actuator * Actuator_Identify(const char * str); // Identify a Sensor from a string Id diff --git a/server/actuators/filetest.c b/server/actuators/filetest.c index 697753a..8dcc9df 100644 --- a/server/actuators/filetest.c +++ b/server/actuators/filetest.c @@ -4,8 +4,10 @@ static FILE * f = NULL; bool Filetest_Init(const char * name, int id) { f = fopen(name, "w"); + if (f == NULL) + return false; setbuf(f, NULL); // Unbuffer - return (f != NULL); + return true; } bool Filetest_Set(int id, double value) diff --git a/server/bbb_pin_defines.h b/server/bbb_pin_defines.h index 2eb2d08..cb77dac 100644 --- a/server/bbb_pin_defines.h +++ b/server/bbb_pin_defines.h @@ -136,6 +136,9 @@ extern const unsigned char g_pin_index_to_gpio[GPIO_NUM_PINS]; #define ADC5 5 #define ADC6 6 #define ADC7 7 +#define ADC_VOLTAGE_MAX 1800 +#define ADC_RAW_MAX (2 << ADC_BITS) +#define ADC_TO_MVOLTS(x) ((double)((x)/2 << ADC_BITS) * (double)ADC_VOLTAGE_MAX) /** Number of ADC pins **/ #define ADC_NUM_PINS 8 diff --git a/server/control.c b/server/control.c index d9a7dc6..c6b5823 100644 --- a/server/control.c +++ b/server/control.c @@ -3,14 +3,17 @@ * @brief Handles all client control requests (admin related) */ #include "common.h" +#include "options.h" #include "control.h" #include "sensor.h" #include "actuator.h" +#include typedef struct ControlData { ControlModes current_mode; pthread_mutex_t mutex; struct timeval start_time; + char user_name[31]; // The user who owns the currently running experiment } ControlData; ControlData g_controls = {CONTROL_STOP, PTHREAD_MUTEX_INITIALIZER, {0}}; @@ -36,6 +39,9 @@ void Control_Handler(FCGIContext *context, char *params) { const char *name = ""; bool force = false; ControlModes desired_mode; + + + // Login/auth now handled entirely in fastcgi.c and login.c @@ -48,8 +54,47 @@ void Control_Handler(FCGIContext *context, char *params) { {"name", &name, FCGI_STRING_T} }; + if (!FCGI_ParseRequest(context, params, values, 3)) return; + + //HACKETY HACK HACK (should really be a seperate function) + if (strcmp(action, "list") == 0) + { + DIR * dir = opendir(context->user_name); + if (dir == NULL) + { + FCGI_RejectJSON(context, "Failed to open user directory"); + return; + } + struct dirent * ent; + FCGI_BeginJSON(context, STATUS_OK); + FCGI_JSONKey("experiments"); + FCGI_PrintRaw("["); + + bool first = true; + while ((ent = readdir(dir)) != NULL) + { + char * c; + for (c = ent->d_name; *c != '\0' && *c != '.'; ++c); + + if (*c != '\0' && strcmp(c, ".exp") == 0) + { + if (!first) + FCGI_PrintRaw(","); + *c = '\0'; // Ummm... probably not a great idea + FCGI_PrintRaw(ent->d_name); + first = false; + } + } + FCGI_PrintRaw("]"); + FCGI_EndJSON(); + + return; // Dear god this is terrible + } + //TODO: Need a "load" action to set data files (but not run) from a past experiment + + //TODO: Need a "delete" action so that people can overwrite experiments (without all this "force" shenanigans) if (!strcmp(action, "emergency")) { desired_mode = CONTROL_EMERGENCY; @@ -65,25 +110,68 @@ void Control_Handler(FCGIContext *context, char *params) { FCGI_RejectJSON(context, "Unknown action specified."); return; } + + if (*g_controls.user_name != '\0' && strcmp(g_controls.user_name,context->user_name) != 0) + { + if (context->user_type != USER_ADMIN) + { + FCGI_RejectJSON(context, "Another user has an experiment in progress."); + return; + } + + if (!force) + { + Log(LOGERR, "User %s is currently running an experiment!", g_controls.user_name); + FCGI_RejectJSON(context, "Pass \"force\" to take control over another user's experiment"); + return; + } + } + + + //HACK + chdir(context->user_name); + void *arg = NULL; if (desired_mode == CONTROL_START) { if (PathExists(name) && !force) { FCGI_RejectJSON(context, "An experiment with that name already exists."); + chdir(g_options.root_dir); // REVERSE HACK + return; + } + char * c = (char*)name; + for (c = (char*)name; *c != '\0' && *c != '.'; ++c); + if (*c == '.') + { + FCGI_RejectJSON(context, "Can't include '.' characters in experiment names (at this point we don't care anymore, go find someone who does)."); + chdir(g_options.root_dir); // REVERSE HACK return; } - arg = (void*)name; } + + const char *ret; - if ((ret = Control_SetMode(desired_mode, arg)) != NULL) { + if ((ret = Control_SetMode(desired_mode, arg)) != NULL) + { FCGI_RejectJSON(context, ret); - } else { + } + else + { + if (desired_mode == CONTROL_STOP) + g_controls.user_name[0] = '\0'; + else + { + snprintf(g_controls.user_name, sizeof(g_controls.user_name), context->user_name); + } FCGI_BeginJSON(context, STATUS_OK); FCGI_JSONPair("description", "ok"); FCGI_EndJSON(); } + + //REVERSE HACK + chdir(g_options.root_dir); } /** @@ -130,7 +218,7 @@ const char* Control_SetMode(ControlModes desired_mode, void * arg) break; case CONTROL_EMERGENCY: if (g_controls.current_mode != CONTROL_START) //pfft - ret = "Not running so how can there be an emergency."; + ret = "Not running so how can there be an emergency?"; break; default: break; diff --git a/server/fastcgi.c b/server/fastcgi.c index cdaef79..a77a3a4 100644 --- a/server/fastcgi.c +++ b/server/fastcgi.c @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include "common.h" #include "sensor.h" @@ -46,7 +48,7 @@ static void IdentifyHandler(FCGIContext *context, char *params) { FCGI_JSONPair("build_date", __DATE__ " " __TIME__); FCGI_JSONLong("api_version", API_VERSION); FCGI_JSONBool("logged_in", has_control); - FCGI_JSONPair("friendly_name", has_control ? context->friendly_name : ""); + FCGI_JSONPair("user_name", has_control ? context->user_name : ""); //Sensor and actuator information if (ident_sensors) { @@ -75,36 +77,61 @@ static void IdentifyHandler(FCGIContext *context, char *params) { } /** - * Gives the user a key that determines who has control over - * the system at any one time. The key can be forcibly generated, revoking - * any previous control keys. To be used in conjunction with HTTP - * basic authentication. + * Given an authorised user, attempt to set the control over the system. + * Modifies members in the context structure appropriately if successful. * @param context The context to work in - * @param force Whether to force key generation or not. - * @return true on success, false otherwise (eg someone else already in control) + * @param user_name - Name of the user + * @param user_type - Type of the user, passed after successful authentication + * @return true on success, false otherwise (eg someone else already in control) */ -bool FCGI_LockControl(FCGIContext *context, bool force) { +bool FCGI_LockControl(FCGIContext *context, const char * user_name, UserType user_type) +{ + // Get current time time_t now = time(NULL); bool expired = now - context->control_timestamp > CONTROL_TIMEOUT; - if (force || !*(context->control_key) || expired) + // Can't lock control if: User not actually logged in (sanity), or key is still valid and the user is not an admin + if (user_type == USER_UNAUTH || + (user_type != USER_ADMIN && !expired && *(context->control_key) != '\0')) + return false; + + // Release any existing control (if any) + FCGI_ReleaseControl(context); + + // Set timestamp + context->control_timestamp = now; + + // Generate a SHA1 hash for the user + SHA_CTX sha1ctx; + unsigned char sha1[20]; + int i = rand(); + SHA1_Init(&sha1ctx); + SHA1_Update(&sha1ctx, &now, sizeof(now)); + SHA1_Update(&sha1ctx, &i, sizeof(i)); + SHA1_Final(sha1, &sha1ctx); + for (i = 0; i < sizeof(sha1); i++) + sprintf(context->control_key + i * 2, "%02x", sha1[i]); + + // Set the IP address + snprintf(context->control_ip, 16, "%s", getenv("REMOTE_ADDR")); + // Set the user name + int uname_len = strlen(user_name); + if (snprintf(context->user_name, sizeof(context->user_name), "%s", user_name) < uname_len) + { + Log(LOGERR, "Username at %d characters too long (limit %d)", uname_len, sizeof(context->user_name)); + return false; // :-( + } + // Set the user type + context->user_type = user_type; + // Create directory + if (mkdir(user_name, 0777) != 0 && errno != EEXIST) { - SHA_CTX sha1ctx; - unsigned char sha1[20]; - int i = rand(); - - SHA1_Init(&sha1ctx); - SHA1_Update(&sha1ctx, &now, sizeof(now)); - SHA1_Update(&sha1ctx, &i, sizeof(i)); - SHA1_Final(sha1, &sha1ctx); - - context->control_timestamp = now; - for (i = 0; i < 20; i++) - sprintf(context->control_key + i * 2, "%02x", sha1[i]); - snprintf(context->control_ip, 16, "%s", getenv("REMOTE_ADDR")); - return true; + Log(LOGERR, "Couldn't create user directory %s/%s - %s", g_options.root_dir, user_name, strerror(errno)); + return false; // :-( } - return false; + + + return true; // :-) } /** @@ -133,6 +160,7 @@ bool FCGI_HasControl(FCGIContext *context, const char *key) { */ void FCGI_ReleaseControl(FCGIContext *context) { *(context->control_key) = 0; + // Note: context->user_name should *not* be cleared return; } @@ -507,8 +535,8 @@ void * FCGI_RequestLoop (void *data) if (module_handler) { - //if (module_handler != Login_Handler && module_handler != IdentifyHandler) - if (false) // Testing + if (module_handler != Login_Handler && module_handler != IdentifyHandler && module_handler) + //if (false) // Testing { if (!FCGI_HasControl(&context, cookie)) { diff --git a/server/fastcgi.h b/server/fastcgi.h index dd89594..85dab57 100644 --- a/server/fastcgi.h +++ b/server/fastcgi.h @@ -39,19 +39,21 @@ typedef struct FCGIValue { unsigned flags; } FCGIValue; +typedef enum {USER_UNAUTH, USER_NORMAL, USER_ADMIN} UserType; + /**Contextual information related to FCGI requests*/ typedef struct { /**The time of last valid user access possessing the control key**/ time_t control_timestamp; /**A SHA-1 hash that is the control key, determining who is logged in**/ - char control_key[41]; - /**Determines if the user is an admin or not**/ - bool is_admin; + char control_key[41]; /**The IPv4 address of the logged-in user**/ char control_ip[16]; - /**A friendly name for the logged-in user. Max length 30**/ - char friendly_name[31]; + /**Determines if the user is an admin or not**/ + UserType user_type; + /**Name of the logged in user**/ + char user_name[31]; /**The name of the current module**/ const char *current_module; /**For debugging purposes?**/ @@ -60,7 +62,7 @@ typedef struct typedef void (*ModuleHandler) (FCGIContext *context, char *params); -extern bool FCGI_LockControl(FCGIContext *context, bool force); +extern bool FCGI_LockControl(FCGIContext *context, const char * user_name, UserType user_type); extern void FCGI_ReleaseControl(FCGIContext *context); extern bool FCGI_HasControl(FCGIContext *context, const char *key); extern char *FCGI_KeyPair(char *in, const char **key, const char **value); diff --git a/server/login.c b/server/login.c index a2a11e9..78e31f9 100644 --- a/server/login.c +++ b/server/login.c @@ -16,24 +16,24 @@ /** * Attempt to login using a file formatted like /etc/shadow - * This is here for horrible hack purposes + * This is here... because all better options have been exhausted * @param user - The username * @param pass - The password - * @returns True if the login was successful, false otherwise + * @returns Privelage level of the user or USER_UNAUTH for failure to authenticate */ -bool Login_Shadow(const char * user, const char * pass, const char * shadow) +UserType Login_Shadow(const char * user, const char * pass, const char * shadow) { if (strlen(user) + strlen(pass) >= BUFSIZ-1) { Log(LOGERR, "User/Password too long!\n"); - return false; + return USER_UNAUTH; } FILE * f = fopen(shadow, "r"); if (f == NULL) { Log(LOGERR,"Can't open %s - %s\n", shadow, strerror(errno)); - return false; + return USER_UNAUTH; } char buffer[BUFSIZ]; @@ -42,9 +42,9 @@ bool Login_Shadow(const char * user, const char * pass, const char * shadow) while (fgets(buffer, BUFSIZ, f) != NULL) // NOTE: Restrict username+password strings to BUFSIZ... what could possibly go wrong? { - Log(LOGDEBUG,"Scanning %d: %s", strlen(buffer), buffer); + //Log(LOGDEBUG,"Scanning %d: %s", strlen(buffer), buffer); - for (int i = 0; i < BUFSIZ-1; ++i) + for (int i = 0; i < BUFSIZ-1 && buffer[i] != '\0'; ++i) { if (buffer[i] == ':') { @@ -56,7 +56,7 @@ bool Login_Shadow(const char * user, const char * pass, const char * shadow) if (strcmp(user,buffer) == 0) { - Log(LOGDEBUG,"User matches! %s\n", buffer); + //Log(LOGDEBUG,"User matches! %s\n", buffer); break; } passwd_index = -1; @@ -64,16 +64,31 @@ bool Login_Shadow(const char * user, const char * pass, const char * shadow) if (passwd_index <= 0) { - Log(LOGDEBUG,"No user found matching %s\n", user); - return false; + //Log(LOGDEBUG,"No user found matching %s\n", user); + return USER_UNAUTH; } - for (int i = passwd_index; i < BUFSIZ-1; ++i) + int gid_index = -1; + for (int i = passwd_index; i < BUFSIZ-1 && buffer[i] != '\0'; ++i) { - if (buffer[i] == ':' || buffer[i] == '\n') + if (buffer[i] == ':') { + gid_index = i+1; + buffer[i] = '\0'; + } + if (buffer[i] == '\n') buffer[i] = '\0'; - + } + char * end = buffer+gid_index; + UserType user_type = USER_NORMAL; + if (gid_index > passwd_index && gid_index < BUFSIZ-1) + { + int gid = strtol(buffer+gid_index, &end,10); + Log(LOGDEBUG, "Usertype %d %s", gid, buffer+gid_index); + if (*end == '\0' && gid == 0) + { + Log(LOGDEBUG, "Admin"); + user_type = USER_ADMIN; } } @@ -87,10 +102,14 @@ bool Login_Shadow(const char * user, const char * pass, const char * shadow) break; } - Log(LOGDEBUG,"Salted Entry: %s\n", buffer+passwd_index); - Log(LOGDEBUG,"Salted Attempt: %s\n", crypt(pass, salt)); +// Log(LOGDEBUG,"Salted Entry: %s\n", buffer+passwd_index); +// Log(LOGDEBUG,"Salted Attempt: %s\n", crypt(pass, salt)); - return (strcmp(crypt(pass, salt), buffer+passwd_index) == 0); + if (strcmp(crypt(pass, salt), buffer+passwd_index) == 0) + { + return user_type; + } + return USER_UNAUTH; } /** @@ -193,7 +212,7 @@ void Login_Handler(FCGIContext * context, char * params) user[i] = '\0'; - bool authenticated = true; + UserType user_type = USER_UNAUTH; switch (g_options.auth_method) { @@ -213,10 +232,10 @@ void Login_Handler(FCGIContext * context, char * params) //int len = sprintf(dn, "uid=%s,%s", user, g_options.ldap_base_dn); // At UWA (hooray) - char * user_type = "Students"; + char * user_group = "Students"; if (user[0] == '0') - user_type = "Staff"; - int len = sprintf(dn, "cn=%s,ou=%s,%s", user, user_type, g_options.ldap_base_dn); + user_group = "Staff"; + int len = sprintf(dn, "cn=%s,ou=%s,%s", user, user_group, g_options.ldap_base_dn); if (len >= BUFSIZ) @@ -225,41 +244,49 @@ void Login_Handler(FCGIContext * context, char * params) return; } - authenticated = (Login_LDAP_Bind(g_options.auth_uri, dn, pass) == LDAP_SUCCESS); + if (Login_LDAP_Bind(g_options.auth_uri, dn, pass) == LDAP_SUCCESS) + { + if (user[0] == '0') + user_type = USER_ADMIN; + else + user_type = USER_NORMAL; + } break; } case AUTH_SHADOW: { - authenticated = Login_Shadow(user, pass, g_options.auth_uri); + user_type = Login_Shadow(user, pass, g_options.auth_uri); break; } default: { Log(LOGWARN, "No authentication!"); + user_type = USER_ADMIN; break; } } // error check - if (!authenticated) + if (user_type == USER_UNAUTH) { + Log(LOGDEBUG, "Authentication failure for %s", user); FCGI_RejectJSONEx(context, STATUS_UNAUTHORIZED, "Authentication failure."); } else { - if (FCGI_LockControl(context, false)) + // Try and gain control over the system + if (FCGI_LockControl(context, user, user_type)) { - //Todo: change this to something better than the username if using LDAP. - snprintf(context->friendly_name, 31, "%s", user); - FCGI_EscapeText(context->friendly_name); //Don't break javascript pls - + FCGI_EscapeText(context->user_name); //Don't break javascript pls // Give the user a cookie FCGI_AcceptJSON(context, "Logged in", context->control_key); + Log(LOGDEBUG, "Successful authentication for %s", user); } else { - FCGI_RejectJSON(context, "Someone else is already logged in"); + Log(LOGDEBUG, "%s successfully authenticated but system was in use by %s", user, context->user_name); + FCGI_RejectJSON(context, "Someone else is already logged in (and you are not an admin)"); } } } diff --git a/server/main.c b/server/main.c index c2e5161..7933bab 100644 --- a/server/main.c +++ b/server/main.c @@ -33,6 +33,10 @@ void ParseArguments(int argc, char ** argv) g_options.program = argv[0]; // program name g_options.verbosity = LOGDEBUG; // default log level + // Set the main directory + 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 @@ -85,6 +89,7 @@ void ParseArguments(int argc, char ** argv) Log(LOGDEBUG, "Pin Module Enabled: %d", g_options.enable_pin); Log(LOGDEBUG, "Auth URI: %s", g_options.auth_uri); Log(LOGDEBUG, "LDAP Base DN: %s", g_options.ldap_base_dn); + Log(LOGDEBUG, "Root directory: %s", g_options.root_dir); if (g_options.auth_uri[0] != '\0') { diff --git a/server/options.h b/server/options.h index 839bc4d..bf668b3 100644 --- a/server/options.h +++ b/server/options.h @@ -31,6 +31,9 @@ typedef struct /** Authentication method **/ enum {AUTH_NONE, AUTH_LDAP, AUTH_SHADOW} auth_method; + /** Starting directory **/ + char root_dir[BUFSIZ]; + } Options; diff --git a/server/parameters b/server/parameters index 2fa6890..da48239 100644 --- a/server/parameters +++ b/server/parameters @@ -22,7 +22,8 @@ pin_test="0" # Set to the URI to use authentication #auth_uri="ldap://192.168.1.1" #auth_uri="ldaps://ldap.pheme.uwa.edu.au" #UWA -auth_uri="/etc/shadow" +#auth_uri="/etc/shadow" +auth_uri="shadow" # Set to the dn of the LDAP server ldap_base_dn="ou=People,dc=daedalus" # Testing diff --git a/server/sensor.c b/server/sensor.c index 792bbbd..e136988 100644 --- a/server/sensor.c +++ b/server/sensor.c @@ -29,7 +29,7 @@ int g_num_sensors = 0; * @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, double max_error, double min_error, double max_warn, double min_warn) +int Sensor_Add(const char * name, int user_id, ReadFn read, InitFn init, CleanFn cleanup, SanityFn sanity) { if (++g_num_sensors > SENSORS_MAX) { @@ -45,18 +45,20 @@ int Sensor_Add(const char * name, int user_id, ReadFn read, InitFn init, CleanFn s->name = name; s->read = read; // Set read function s->init = init; // Set init function - if (init != NULL) - init(name, user_id); // Call it // Start by averaging values taken over a second s->sample_us = 1e6; s->averages = 1; - // Set warning/error thresholds - s->thresholds.max_error = max_error; - s->thresholds.min_error = min_error; - s->thresholds.max_warn = max_warn; - s->thresholds.min_warn = min_warn; + // Set sanity function + s->sanity = sanity; + + if (init != NULL) + { + if (!init(name, user_id)) + Fatal("Couldn't init sensor %s", name); + } + return g_num_sensors; } @@ -68,11 +70,14 @@ int Sensor_Add(const char * name, int user_id, ReadFn read, InitFn init, CleanFn */ #include "sensors/resource.h" #include "sensors/strain.h" -#include "sensors/piped.h" +#include "sensors/pressure.h" void Sensor_Init() { - Sensor_Add("cpu_stime", RESOURCE_CPU_SYS, Resource_Read, NULL, NULL, 1e50,-1e50,1e50,-1e50); - Sensor_Add("cpu_utime", RESOURCE_CPU_USER, Resource_Read, NULL, NULL, 1e50,-1e50,1e50,-1e50); + 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("../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); @@ -177,26 +182,6 @@ void Sensor_SetModeAll(ControlModes mode, void * arg) } -/** - * Checks the sensor data for unsafe or unexpected results - * @param sensor_id - The ID of the sensor - * @param value - The value from the sensor to test - */ -void Sensor_CheckData(Sensor * s, double value) -{ - if( value > s->thresholds.max_error || value < s->thresholds.min_error) - { - Log(LOGERR, "Sensor %s at %f is above or below its safety value of %f or %f\n",s->name,value, s->thresholds.max_error, s->thresholds.min_error); - //new function that stops actuators? - //Control_SetMode(CONTROL_EMERGENCY, NULL) - } - else if( value > s->thresholds.max_warn || value < s->thresholds.min_warn) - { - Log(LOGWARN, "Sensor %s at %f is above or below its warning value of %f or %f\n", s->name,value,s->thresholds.max_warn, s->thresholds.min_warn); - } -} - - /** * Record data from a single Sensor; to be run in a seperate thread * @param arg - Cast to Sensor* - Sensor that the thread will handle @@ -220,9 +205,13 @@ void * Sensor_Loop(void * arg) if (success) { - - - Sensor_CheckData(s, d.value); + if (s->sanity != NULL) + { + if (!s->sanity(s->user_id, d.value)) + { + Fatal("Sensor %s (%d,%d) reads unsafe value", s->name, s->id, s->user_id); + } + } Data_Save(&(s->data_file), &d, 1); // Record it } else diff --git a/server/sensor.h b/server/sensor.h index bb2fb9e..0285252 100644 --- a/server/sensor.h +++ b/server/sensor.h @@ -51,15 +51,16 @@ typedef struct InitFn init; /** Function to cleanup the sensor **/ CleanFn cleanup; + /** Function to sanity check the sensor readings **/ + SanityFn sanity; /** Human readable name of the sensor **/ const char * name; - /** Thresholds on the sensor **/ - SensorThreshold thresholds; /** Sampling rate **/ int sample_us; - /** Averages per DataPoint **/ + /** Number of averages per sample **/ int averages; + } Sensor; @@ -73,7 +74,6 @@ extern void Sensor_SetMode(Sensor * s, ControlModes mode, void * arg); extern void * Sensor_Loop(void * args); // Main loop for a thread that handles a Sensor extern bool Sensor_Read(Sensor * s, DataPoint * d); // Read a single DataPoint, indicating if it has changed since the last one -extern void Sensor_CheckData(Sensor * s, double value); // Check a DataPoint extern Sensor * Sensor_Identify(const char * str); // Identify a Sensor from a string extern void Sensor_Handler(FCGIContext *context, char * params); // Handle a FCGI request for Sensor data diff --git a/server/sensors/Makefile b/server/sensors/Makefile index b72201d..a412f7c 100644 --- a/server/sensors/Makefile +++ b/server/sensors/Makefile @@ -3,7 +3,7 @@ CXX = gcc FLAGS = -std=c99 -Wall -pedantic -g -I../ #-I/usr/include/opencv -I/usr/include/opencv2/highgui For OpenCV LIB = -lpthread -OBJ = strain.o resource.o piped.o +OBJ = strain.o resource.o pressure.o HEADERS = $(wildcard *.h) RM = rm -f diff --git a/server/sensors/pressure.c b/server/sensors/pressure.c new file mode 100644 index 0000000..ddb46df --- /dev/null +++ b/server/sensors/pressure.c @@ -0,0 +1,103 @@ +/** + * @file pressure.c + * @purpose Implementation of Pressure reading functions + */ + +#include "pressure.h" +#include "../bbb_pin.h" +#include "../log.h" // For Fatal() + +#define PSI_TO_KPA 6.89475729 + +/** + * Get the ADC number of a Pressure sensor + * @param id - Id of the sensor + * @returns ADC as defined in bbb_pin_defines.h + */ +static int Pressure_GetADC(int id) +{ + switch (id) + { + case PRES_HIGH0: + return ADC1; + case PRES_HIGH1: + return ADC3; + case PRES_LOW0: + return ADC5; + default: + Fatal("Unknown Pressure id %d", id); + return -1; // Should never happen + } +} + +/** + * Convert an ADC voltage into a Pressure reading + * @param id - Sensor ID + * @param adc - ADC reading + * @returns Pressure in kPa + */ +double Pressure_Callibrate(int id, int adc) +{ + //double voltage = ADC_TO_VOLTS(adc); // convert reading to voltage + + switch (id) + { + case PRES_HIGH0: + case PRES_HIGH1: + { + static const double Vs = 5e3; // In mVs + static const double Pmin = 0.0; + 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; + } + case PRES_LOW0: + return (200.0 * (adc / ADC_RAW_MAX)); + default: + Fatal("Unknown Pressure id %d", id); + return -1; // Should never happen + } + +} + +/** + * Initialise a Pressure sensor + * @param name - Ignored + * @param id - The id of the Pressure sensor + * @returns true on success, false on error + */ +bool Pressure_Init(const char * name, int id) +{ + return ADC_Export(Pressure_GetADC(id)); +} + +/** + * Cleanup a Pressure Sensor + * @param id - The id of the sensor to cleanup + * @returns true on success, false on failure + */ +bool Pressure_Cleanup(int id) +{ + ADC_Unexport(Pressure_GetADC(id)); + return true; +} + +/** + * Read a Pressure Sensor + * @param id - id of the sensor to read + * @param value - Where the value will be stored on a successful read + * @returns true on success, false on failure + */ +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)) + { + *value = Pressure_Callibrate(id, *value); + result = true; + } + //pthread_mutex_unlock(&mutex); + return result; +} diff --git a/server/sensors/pressure.h b/server/sensors/pressure.h new file mode 100644 index 0000000..22d1adc --- /dev/null +++ b/server/sensors/pressure.h @@ -0,0 +1,23 @@ +/** + * @file pressure.h + * @purpose Declarations for Pressure Sensor functions + */ + +#ifndef _PRESSURE_H + +#include "../common.h" + +typedef enum +{ + PRES_HIGH0, + PRES_HIGH1, + PRES_LOW0 +} PressureId; + +extern bool Pressure_Init(const char * name, int id); +extern bool Pressure_Cleanup(int id); +extern bool Pressure_Read(int id, double * value); + +#endif //_PRESSURE_H + + diff --git a/server/shadow b/server/shadow new file mode 100644 index 0000000..49c685d --- /dev/null +++ b/server/shadow @@ -0,0 +1,3 @@ +admin:$1$Pga9JAbx$FvYbRAxzx6lLl6IGIxMsF./:admin@snoopy.com:0 +user1:$1$DY5atz7F$XsLeojV/lL1uiLAkvR8Xm0.:user1@hatemail.com:1 +user2:$1$ahKf1yrT$Dksl6fXzVfzJjdc55gkSZ.:user2@gratemail.com:1