+++ /dev/null
-This folder represents the files that should be placed under:
-/etc/nginx
-
-To install:
-* Delete all files under /etc/nginx/sites-enabled
-* Either:
- * Copy 'mctxconfig' (under sites-enabled) to /etc/nginx/sites-available/
- and create a symlink to that file under /etc/nginx/sites-enabled/
- * Create a symlink directly from 'mctxconfig' to
- /etc/etc/nginx/sites-enabled/
-* Replace /etc/nginx/fastcgi_params and /etc/nginx/mime.types with the
- provided files (either by copying or symlinking)
-
-Note:
-To get the login functionality working, you need to place a .htpasswd file
-under /usr/share/nginx/access (create folder if it doesn't exist). To generate
-the htpasswd file, install the apache2-utils package and use the 'htpasswd'
-executable.
-
-
-P.S: (I always forget these)
-Set file permissions to: 644
-Set folder permissions to: 755
\ No newline at end of file
+++ /dev/null
-fastcgi_param QUERY_STRING $query_string;
-fastcgi_param REQUEST_METHOD $request_method;
-fastcgi_param CONTENT_TYPE $content_type;
-fastcgi_param CONTENT_LENGTH $content_length;
-
-fastcgi_param SCRIPT_FILENAME $request_filename;
-fastcgi_param SCRIPT_NAME $fastcgi_script_name;
-fastcgi_param REQUEST_URI $request_uri;
-fastcgi_param DOCUMENT_URI $document_uri;
-fastcgi_param DOCUMENT_ROOT $document_root;
-fastcgi_param SERVER_PROTOCOL $server_protocol;
-
-fastcgi_param GATEWAY_INTERFACE CGI/1.1;
-fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
-fastcgi_param SERVER_HOSTNAME mctxsoft;
-
-fastcgi_param REMOTE_ADDR $remote_addr;
-fastcgi_param REMOTE_PORT $remote_port;
-fastcgi_param REMOTE_USER $remote_user;
-fastcgi_param SERVER_ADDR $server_addr;
-fastcgi_param SERVER_PORT $server_port;
-fastcgi_param SERVER_NAME $server_name;
-
-fastcgi_param HTTPS $https;
-
-# PHP only, required if PHP was built with --enable-force-cgi-redirect
-fastcgi_param REDIRECT_STATUS 200;
+++ /dev/null
-types {
- text/html html htm shtml;
- text/css css;
- text/xml xml rss;
- image/gif gif;
- image/jpeg jpeg jpg;
- application/x-javascript js;
- application/atom+xml atom;
-
- text/mathml mml;
- text/plain log;
- text/plain txt;
- text/vnd.sun.j2me.app-descriptor jad;
- text/vnd.wap.wml wml;
- text/x-component htc;
-
- image/png png;
- image/tiff tif tiff;
- image/vnd.wap.wbmp wbmp;
- image/x-icon ico;
- image/x-jng jng;
- image/x-ms-bmp bmp;
- image/svg+xml svg svgz;
-
- application/java-archive jar war ear;
- application/json json;
- application/mac-binhex40 hqx;
- application/msword doc;
- application/pdf pdf;
- application/postscript ps eps ai;
- application/rtf rtf;
- application/vnd.ms-excel xls;
- application/vnd.ms-powerpoint ppt;
- application/vnd.wap.wmlc wmlc;
- application/vnd.google-earth.kml+xml kml;
- application/vnd.google-earth.kmz kmz;
- application/x-7z-compressed 7z;
- application/x-cocoa cco;
- application/x-java-archive-diff jardiff;
- application/x-java-jnlp-file jnlp;
- application/x-makeself run;
- application/x-perl pl pm;
- application/x-pilot prc pdb;
- application/x-rar-compressed rar;
- application/x-redhat-package-manager rpm;
- application/x-sea sea;
- application/x-shockwave-flash swf;
- application/x-stuffit sit;
- application/x-tcl tcl tk;
- application/x-x509-ca-cert der pem crt;
- application/x-xpinstall xpi;
- application/xhtml+xml xhtml;
- application/zip zip;
-
- application/octet-stream bin exe dll;
- application/octet-stream deb;
- application/octet-stream dmg;
- application/octet-stream eot;
- application/octet-stream iso img;
- application/octet-stream msi msp msm;
- application/ogg ogx;
-
- audio/midi mid midi kar;
- audio/mpeg mpga mpega mp2 mp3 m4a;
- audio/ogg oga ogg spx;
- audio/x-realaudio ra;
- audio/webm weba;
-
- video/3gpp 3gpp 3gp;
- video/mp4 mp4;
- video/mpeg mpeg mpg mpe;
- video/ogg ogv;
- video/quicktime mov;
- video/webm webm;
- video/x-flv flv;
- video/x-mng mng;
- video/x-ms-asf asx asf;
- video/x-ms-wmv wmv;
- video/x-msvideo avi;
-}
+++ /dev/null
-# You may add here your
-# server {
-# ...
-# }
-# statements for each of your virtual hosts to this file
-
-##
-# You should look at the following URL's in order to grasp a solid understanding
-# of Nginx configuration files in order to fully unleash the power of Nginx.
-# http://wiki.nginx.org/Pitfalls
-# http://wiki.nginx.org/QuickStart
-# http://wiki.nginx.org/Configuration
-#
-# Generally, you will want to move this file somewhere, and start with a clean
-# file but keep this around for reference. Or just disable in sites-enabled.
-#
-# Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples.
-##
-
-server {
- listen 80;
- listen [::]:80 default_server ipv6only=on;
-
- root /usr/share/nginx/html;
- index index.php index.html index.htm;
-
- # Make site accessible from http://localhost/
- server_name localhost;
-
- location / {
- # First attempt to serve request as file, then
- # as directory, then fall back to displaying a 404.
- try_files $uri $uri/ =404;
- # Uncomment to enable naxsi on this location
- # include /etc/nginx/naxsi.rules
- }
-
- # Only for nginx-naxsi used with nginx-naxsi-ui : process denied requests
- #location /RequestDenied {
- # proxy_pass http://127.0.0.1:8080;
- #}
-
- #error_page 404 /404.html;
-
- # redirect server error pages to the static page /50x.html
- #
- #error_page 500 502 503 504 /50x.html;
- #location = /50x.html {
- # root /usr/share/nginx/html;
- #}
-
- # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
- #
- location ~ \.php$ {
- fastcgi_split_path_info ^(.+\.php)(/.+)$;
- # # NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini
- #
- # # With php5-cgi alone:
- # fastcgi_pass 127.0.0.1:9000;
- # # With php5-fpm:
- fastcgi_pass unix:/var/run/php5-fpm.sock;
- fastcgi_index index.php;
- include fastcgi_params;
- }
-
- # deny access to .htaccess files, if Apache's document root
- # concurs with nginx's one
- #
- location ~ /\.ht {
- deny all;
- }
-
- #MCTX API
- location /api {
- #Login area
- location ^~ /api/control {
- auth_basic "Restricted Access";
- auth_basic_user_file /usr/share/nginx/access/.htpasswd;
-
- fastcgi_pass 127.0.0.1:9005;
- fastcgi_param DOCUMENT_URI_LOCAL control;
- include fastcgi_params;
- }
- location ~ ^/api/?([^?]*) {
- fastcgi_pass 127.0.0.1:9005;
- fastcgi_param DOCUMENT_URI_LOCAL $1;
- include fastcgi_params;
- }
- }
-}
-
-
-# another virtual host using mix of IP-, name-, and port-based configuration
-#
-#server {
-# listen 8000;
-# listen somename:8080;
-# server_name somename alias another.alias;
-# root html;
-# index index.html index.htm;
-#
-# location / {
-# try_files $uri $uri/ =404;
-# }
-#}
-
-
-# HTTPS server
-#
-#server {
-# listen 443;
-# server_name localhost;
-#
-# root html;
-# index index.html index.htm;
-#
-# ssl on;
-# ssl_certificate cert.pem;
-# ssl_certificate_key cert.key;
-#
-# ssl_session_timeout 5m;
-#
-# ssl_protocols SSLv3 TLSv1;
-# ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv3:+EXP;
-# ssl_prefer_server_ciphers on;
-#
-# location / {
-# try_files $uri $uri/ =404;
-# }
-#}
--- /dev/null
+To get syslog and nginx to work correctly:
+* Update nginx with the latest config file
+* Copy the config file to /etc/rsyslog.d/
+* Restart rsyslog with /etc/init.d/rsyslog restart
--- /dev/null
+This folder represents the files that should be placed under:
+/etc/nginx
+
+To install:
+* Delete all files under /etc/nginx/sites-enabled
+* Either:
+ * Copy 'mctxconfig' (under sites-enabled) to /etc/nginx/sites-available/
+ and create a symlink to that file under /etc/nginx/sites-enabled/
+ * Create a symlink directly from 'mctxconfig' to
+ /etc/etc/nginx/sites-enabled/
+* Replace /etc/nginx/fastcgi_params and /etc/nginx/mime.types with the
+ provided files (either by copying or symlinking)
+
+Note:
+To get the login functionality working, you need to place a .htpasswd file
+under /usr/share/nginx/access (create folder if it doesn't exist). To generate
+the htpasswd file, install the apache2-utils package and use the 'htpasswd'
+executable.
+
+
+P.S: (I always forget these)
+Set file permissions to: 644
+Set folder permissions to: 755
\ No newline at end of file
--- /dev/null
+fastcgi_param QUERY_STRING $query_string;
+fastcgi_param REQUEST_METHOD $request_method;
+fastcgi_param CONTENT_TYPE $content_type;
+fastcgi_param CONTENT_LENGTH $content_length;
+
+fastcgi_param SCRIPT_FILENAME $request_filename;
+fastcgi_param SCRIPT_NAME $fastcgi_script_name;
+fastcgi_param REQUEST_URI $request_uri;
+fastcgi_param DOCUMENT_URI $document_uri;
+fastcgi_param DOCUMENT_ROOT $document_root;
+fastcgi_param SERVER_PROTOCOL $server_protocol;
+
+fastcgi_param GATEWAY_INTERFACE CGI/1.1;
+fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
+fastcgi_param SERVER_HOSTNAME mctxsoft;
+
+fastcgi_param REMOTE_ADDR $remote_addr;
+fastcgi_param REMOTE_PORT $remote_port;
+fastcgi_param REMOTE_USER $remote_user;
+fastcgi_param SERVER_ADDR $server_addr;
+fastcgi_param SERVER_PORT $server_port;
+fastcgi_param SERVER_NAME $server_name;
+
+fastcgi_param HTTPS $https;
+
+# PHP only, required if PHP was built with --enable-force-cgi-redirect
+fastcgi_param REDIRECT_STATUS 200;
--- /dev/null
+types {
+ text/html html htm shtml;
+ text/css css;
+ text/xml xml rss;
+ image/gif gif;
+ image/jpeg jpeg jpg;
+ application/x-javascript js;
+ application/atom+xml atom;
+
+ text/mathml mml;
+ text/plain log;
+ text/plain txt;
+ text/vnd.sun.j2me.app-descriptor jad;
+ text/vnd.wap.wml wml;
+ text/x-component htc;
+
+ image/png png;
+ image/tiff tif tiff;
+ image/vnd.wap.wbmp wbmp;
+ image/x-icon ico;
+ image/x-jng jng;
+ image/x-ms-bmp bmp;
+ image/svg+xml svg svgz;
+
+ application/java-archive jar war ear;
+ application/json json;
+ application/mac-binhex40 hqx;
+ application/msword doc;
+ application/pdf pdf;
+ application/postscript ps eps ai;
+ application/rtf rtf;
+ application/vnd.ms-excel xls;
+ application/vnd.ms-powerpoint ppt;
+ application/vnd.wap.wmlc wmlc;
+ application/vnd.google-earth.kml+xml kml;
+ application/vnd.google-earth.kmz kmz;
+ application/x-7z-compressed 7z;
+ application/x-cocoa cco;
+ application/x-java-archive-diff jardiff;
+ application/x-java-jnlp-file jnlp;
+ application/x-makeself run;
+ application/x-perl pl pm;
+ application/x-pilot prc pdb;
+ application/x-rar-compressed rar;
+ application/x-redhat-package-manager rpm;
+ application/x-sea sea;
+ application/x-shockwave-flash swf;
+ application/x-stuffit sit;
+ application/x-tcl tcl tk;
+ application/x-x509-ca-cert der pem crt;
+ application/x-xpinstall xpi;
+ application/xhtml+xml xhtml;
+ application/zip zip;
+
+ application/octet-stream bin exe dll;
+ application/octet-stream deb;
+ application/octet-stream dmg;
+ application/octet-stream eot;
+ application/octet-stream iso img;
+ application/octet-stream msi msp msm;
+ application/ogg ogx;
+
+ audio/midi mid midi kar;
+ audio/mpeg mpga mpega mp2 mp3 m4a;
+ audio/ogg oga ogg spx;
+ audio/x-realaudio ra;
+ audio/webm weba;
+
+ video/3gpp 3gpp 3gp;
+ video/mp4 mp4;
+ video/mpeg mpeg mpg mpe;
+ video/ogg ogv;
+ video/quicktime mov;
+ video/webm webm;
+ video/x-flv flv;
+ video/x-mng mng;
+ video/x-ms-asf asx asf;
+ video/x-ms-wmv wmv;
+ video/x-msvideo avi;
+}
--- /dev/null
+server {
+ listen 80;
+
+ #Change this to match your root directory
+ root /usr/share/nginx/html;
+ index index.php index.html index.htm;
+
+ # Make site accessible from http://localhost/
+ server_name localhost;
+
+ location / {
+ # First attempt to serve request as file, then
+ # as directory, then fall back to displaying a 404.
+ try_files $uri $uri/ =404;
+ # Uncomment to enable naxsi on this location
+ # include /etc/nginx/naxsi.rules
+ }
+
+ # Only for nginx-naxsi used with nginx-naxsi-ui : process denied requests
+ #location /RequestDenied {
+ # proxy_pass http://127.0.0.1:8080;
+ #}
+
+ #error_page 404 /404.html;
+
+ # redirect server error pages to the static page /50x.html
+ #
+ #error_page 500 502 503 504 /50x.html;
+ #location = /50x.html {
+ # root /usr/share/nginx/html;
+ #}
+
+ # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
+ #
+ location ~ \.php$ {
+ fastcgi_split_path_info ^(.+\.php)(/.+)$;
+ # # NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini
+ #
+ # # With php5-cgi alone:
+ # fastcgi_pass 127.0.0.1:9000;
+ # # With php5-fpm:
+ fastcgi_pass unix:/var/run/php5-fpm.sock;
+ fastcgi_index index.php;
+ include fastcgi_params;
+ }
+
+ # deny access to .htaccess files, if Apache's document root
+ # concurs with nginx's one
+ #
+ location ~ /\.ht {
+ deny all;
+ }
+
+ #MCTX API
+ location /api {
+ #Login area
+ location ^~ /api/control {
+ #Uncomment to add back login
+ #auth_basic "Restricted Access";
+ #auth_basic_user_file /usr/share/nginx/access/.htpasswd;
+
+ fastcgi_pass 127.0.0.1:9005;
+ fastcgi_param DOCUMENT_URI_LOCAL control;
+ include fastcgi_params;
+ }
+
+ #Program log
+ location ^~ /api/log {
+ alias /var/log/mctxserv.log;
+ default_type text/plain;
+ }
+
+ location ~ ^/api/?([^?]*) {
+ fastcgi_pass 127.0.0.1:9005;
+ fastcgi_param DOCUMENT_URI_LOCAL $1;
+ include fastcgi_params;
+ }
+ }
+}
+
+
+# another virtual host using mix of IP-, name-, and port-based configuration
+#
+#server {
+# listen 8000;
+# listen somename:8080;
+# server_name somename alias another.alias;
+# root html;
+# index index.html index.htm;
+#
+# location / {
+# try_files $uri $uri/ =404;
+# }
+#}
+
+
+# HTTPS server
+#
+#server {
+# listen 443;
+# server_name localhost;
+#
+# root html;
+# index index.html index.htm;
+#
+# ssl on;
+# ssl_certificate cert.pem;
+# ssl_certificate_key cert.key;
+#
+# ssl_session_timeout 5m;
+#
+# ssl_protocols SSLv3 TLSv1;
+# ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv3:+EXP;
+# ssl_prefer_server_ciphers on;
+#
+# location / {
+# try_files $uri $uri/ =404;
+# }
+#}
--- /dev/null
+$FileCreateMode 0644
+if $programname == 'mctxserv' then /var/log/mctxserv.log
+$FileCreateMode 0640
/**
* @file actuator.c
- * @purpose Implementation of Actuator related functionality
+ * @brief Implementation of Actuator related functionality
*/
#include "actuator.h"
}
/**
- * Start an Actuator
- * @param a - The Actuator to start
- * @param experiment_name - Prepended to DataFile filename
+ * Sets the actuator to the desired mode. No checks are
+ * done to see if setting to the desired mode will conflict with
+ * the current mode - the caller must guarantee this itself.
+ * @param a The actuator whose mode is to be changed
+ * @param mode The mode to be changed to
+ * @param arg An argument specific to the mode to be set.
+ * e.g for CONTROL_START it represents the experiment name.
*/
-void Actuator_Start(Actuator * a, const char * experiment_name)
+void Actuator_SetMode(Actuator * a, ControlModes mode, void *arg)
{
- // Set filename
- char filename[BUFSIZ];
- if (sprintf(filename, "%s_a%d", experiment_name, a->id) >= BUFSIZ)
+ switch (mode)
{
- Fatal("Experiment name \"%s\" too long (>%d)", experiment_name, BUFSIZ);
- }
-
- Log(LOGDEBUG, "Actuator %d with DataFile \"%s\"", a->id, filename);
- // Open DataFile
- Data_Open(&(a->data_file), filename);
-
- a->activated = true; // Don't forget this
-
- a->control_changed = false;
+ case CONTROL_START:
+ {
+ char filename[BUFSIZ];
+ const char *experiment_name = (const char*) arg;
+ int ret;
+
+ if (snprintf(filename, BUFSIZ, "%s_a%d", experiment_name, a->id) >= BUFSIZ)
+ {
+ Fatal("Experiment name \"%s\" too long (>%d)", experiment_name, BUFSIZ);
+ }
- // Create the thread
- pthread_create(&(a->thread), NULL, Actuator_Loop, (void*)(a));
-}
+ Log(LOGDEBUG, "Actuator %d with DataFile \"%s\"", a->id, filename);
+ // Open DataFile
+ Data_Open(&(a->data_file), filename);
-/**
- * Stop an Actuator
- * @param s - The Actuator to stop
- */
-void Actuator_Stop(Actuator * a)
-{
- // Stop
- a->activated = false;
- Actuator_SetControl(a, NULL);
- pthread_join(a->thread, NULL); // Wait for thread to exit
- Data_Close(&(a->data_file)); // Close DataFile
+ a->activated = true; // Don't forget this
+ a->allow_actuation = true;
-}
+ a->control_changed = false;
-/**
- * Stop all Actuators
- */
-void Actuator_StopAll()
-{
- for (int i = 0; i < NUMACTUATORS; ++i)
- Actuator_Stop(g_actuators+i);
+ // Create the thread
+ ret = pthread_create(&(a->thread), NULL, Actuator_Loop, (void*)(a));
+ if (ret != 0)
+ {
+ Fatal("Failed to create Actuator_Loop for Actuator %d", a->id);
+ }
+ }
+ break;
+
+ case CONTROL_EMERGENCY: //TODO add proper case for emergency
+ case CONTROL_PAUSE:
+ a->allow_actuation = false;
+ break;
+ case CONTROL_RESUME:
+ a->allow_actuation = true;
+ break;
+ case CONTROL_STOP:
+ a->allow_actuation = false;
+ a->activated = false;
+ Actuator_SetControl(a, NULL);
+ pthread_join(a->thread, NULL); // Wait for thread to exit
+ Data_Close(&(a->data_file)); // Close DataFile
+ break;
+ default:
+ Fatal("Unknown control mode: %d", mode);
+ }
}
/**
- * Start all Actuators
+ * Sets all actuators to the desired mode.
+ * @see Actuator_SetMode for more information.
+ * @param mode The mode to be changed to
+ * @param arg An argument specific to the mode to be set.
*/
-void Actuator_StartAll(const char * experiment_name)
+void Actuator_SetModeAll(ControlModes mode, void * arg)
{
- for (int i = 0; i < NUMACTUATORS; ++i)
- Actuator_Start(g_actuators+i, experiment_name);
+ for (int i = 0; i < NUMACTUATORS; i++)
+ Actuator_SetMode(&g_actuators[i], mode, arg);
}
/**
pthread_mutex_unlock(&(a->mutex));
if (!a->activated)
break;
- Log(LOGDEBUG, "About to Setvalue");
+ else if (!a->allow_actuation)
+ continue;
+
Actuator_SetValue(a, a->control.value);
}
if (c != NULL)
a->control = *c;
a->control_changed = true;
- Log(LOGDEBUG, "About to broadcast");
pthread_cond_broadcast(&(a->cond));
pthread_mutex_unlock(&(a->mutex));
struct timeval t;
gettimeofday(&t, NULL);
- DataPoint d = {TIMEVAL_DIFF(t, g_options.start_time), value};
- Log(LOGDEBUG, "id: %d", a->id);
+ DataPoint d = {TIMEVAL_DIFF(t, *Control_GetStartTime()), value};
//TODO: Set actuator
switch (a->id)
{
- case ACTUATOR_TEST0:
- {//LED actuator test code, should blink onboard LED next to Ethernet port
- FILE *LEDHandle = NULL; //code reference: http://learnbuildshare.wordpress.com/2013/05/19/beaglebone-black-controlling-user-leds-using-c/
- char *LEDBrightness = "/sys/class/leds/beaglebone:green:usr3/brightness";
- int val = (!!(int)value);
- Log(LOGDEBUG, "Val: %d", val);
- if(val == 1) {
- if((LEDHandle = fopen(LEDBrightness, "r+")) != NULL) {
- fwrite("1", sizeof(char), 1, LEDHandle);
- fclose(LEDHandle);
- } else perror("fail");
- }
- else if(val == 0) {
- if((LEDHandle = fopen(LEDBrightness, "r+")) != NULL) {
- fwrite("0", sizeof(char), 1, LEDHandle);
- fclose(LEDHandle);
- }
- }
- else perror("Pin value should be 1 or 0");
- }
+ case ACTUATOR_TEST0:
break;
case ACTUATOR_TEST1:
break;
Data_Save(&(a->data_file), &d, 1);
}
-
/**
* Helper: Begin Actuator response in a given format
* @param context - the FCGIContext
{
struct timeval now;
gettimeofday(&now, NULL);
- double current_time = TIMEVAL_DIFF(now, g_options.start_time);
+ double current_time = TIMEVAL_DIFF(now, *Control_GetStartTime());
int id = 0;
double set = 0;
double start_time = 0;
/**
* @file actuator.h
- * @purpose Declarations for actuator control
+ * @brief Declarations for actuator control
*/
#ifndef _ACTUATOR_H
pthread_cond_t cond;
/** Indicates whether the Actuator is running **/
bool activated;
+ /** Indicates whether the Actuator can be actuated or not **/
+ bool allow_actuation;
} Actuator;
extern void Actuator_Init(); // One off initialisation of *all* Actuators
-
-extern void Actuator_StartAll(const char * experiment_name); // Start all Actuators
-extern void Actuator_StopAll(); // Stop all Actuators
-
-extern void Actuator_Start(Actuator * a, const char * experiment_name); // Start a Actuator
-extern void Actuator_Stop(Actuator * a); // Stop an Actuator
-
+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
#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))
/**
* @file control.c
- * @brief Handles all client control requests (admin/actuator related)
+ * @brief Handles all client control requests (admin related)
*/
#include "common.h"
#include "control.h"
+#include "sensor.h"
+#include "actuator.h"
+typedef struct ControlData {
+ ControlModes current_mode;
+ pthread_mutex_t mutex;
+ struct timeval start_time;
+} ControlData;
-/**
- * Handles control of the actuators.
- *
-void ActuatorHandler(FCGIContext *context, ActuatorId id, const char *set_value) {
- char *ptr;
-
- switch(id) { //Add new actuators here
- case ACT_PRESSURE: //Suppose is pressure regulator. 0-700 input (kPa)
- {
- int value = strtol(set_value, &ptr, 10);
- if (*ptr == '\0' && value >= 0 && value <= 700) {
- FCGI_BeginJSON(context, STATUS_OK);
- FCGI_JSONKey("description");
- FCGI_JSONValue("\"Set pressure to %d kPa!\"", value);
- FCGI_EndJSON();
- } else {
- FCGI_RejectJSONEx(context,
- STATUS_ERROR, "Invalid pressure specified.");
- }
- } break;
- case ACT_SOLENOID1:
- {
- int value = strtol(set_value, &ptr, 10);
- if (*ptr == '\0') {
- const char *state = "off";
- if (value)
- state = "on";
- FCGI_BeginJSON(context, STATUS_OK);
- FCGI_JSONKey("description");
- FCGI_JSONValue("\"Solenoid 1 turned %s!\"", state);
- FCGI_EndJSON();
- } else {
- FCGI_RejectJSON(context, "Invalid actuator value specified");
- }
- } break;
- default:
- FCGI_RejectJSONEx(context,
- STATUS_ERROR, "Invalid actuator id specified.");
+ControlData g_controls = {CONTROL_STOP, PTHREAD_MUTEX_INITIALIZER, {0}};
+
+static bool PathExists(const char *path) {
+ FILE *fp = fopen(path, "r");
+ if (fp) {
+ fclose(fp);
+ return true;
}
+ return false;
}
-*/
/**
- * System control handler. This covers control over all aspects of the system.
- * E.g: Actuators, system commands (start/stop experiment/recording) etc
+ * System control handler. This covers high-level control, including
+ * admin related functions and starting/stopping experiments..
* @param context The context to work in
* @param params The input parameters
*/
void Control_Handler(FCGIContext *context, char *params) {
- const char *key, *value, *control_key = NULL;
- const char *action = NULL, *set_value = NULL;
+ const char *action, *key = "", *name = "";
bool force = false;
- char *ptr;
- int id = -1;
+ ControlModes desired_mode;
+
+ FCGIValue values[4] = {
+ {"action", &action, FCGI_REQUIRED(FCGI_STRING_T)},
+ {"key", &key, FCGI_STRING_T},
+ {"force", &force, FCGI_BOOL_T},
+ {"name", &name, FCGI_STRING_T}
+ };
+
+ if (!FCGI_ParseRequest(context, params, values, 4))
+ return;
- while ((params = FCGI_KeyPair(params, &key, &value))) {
- if (!strcmp(key, "action"))
- action = value;
- else if (!strcmp(key, "key"))
- control_key = value;
- else if (!strcmp(key, "force"))
- force = !force;
- else if (!strcmp(key, "id") && *value) { //Ensure non-empty value
- int parsed = strtol(value, &ptr, 10);
- if (*ptr == '\0') {
- id = parsed;
- }
- } else if (!strcmp(key, "value")) {
- set_value = value;
+ if (!strcmp(action, "lock")) {
+ FCGI_LockControl(context, force);
+ return;
+ } else if (!strcmp(action, "emergency")) {
+ desired_mode = CONTROL_EMERGENCY;
+ } else if (!strcmp(action, "query")) {
+ FCGI_BeginJSON(context, STATUS_OK);
+ FCGI_JSONPair("state", Control_GetModeName(Control_GetMode()));
+ FCGI_EndJSON();
+ return;
+ } else if (FCGI_HasControl(context, key)) {
+ if (!strcmp(action, "release")) {
+ FCGI_ReleaseControl(context);
+ } else if (!strcmp(action, "start")) {
+ desired_mode = CONTROL_START;
+ } else if (!strcmp(action, "pause")) {
+ desired_mode = CONTROL_PAUSE;
+ } else if (!strcmp(action, "resume")) {
+ desired_mode = CONTROL_RESUME;
+ } else if (!strcmp(action, "stop")) {
+ desired_mode = CONTROL_STOP;
+ } else {
+ FCGI_RejectJSON(context, "Unknown action specified.");
+ return;
}
+ } else {
+ FCGI_RejectJSONEx(context, STATUS_UNAUTHORIZED,
+ "Invalid control key specified.");
+ return;
}
- Log(LOGDEBUG, "Id %d", id); // to stop compiler complaining for now
-
- if (action == NULL) { //Must have an action
- FCGI_RejectJSON(context, "No action specified");
- } else if (!strcmp(action, "start")) {
- FCGI_BeginControl(context, force);
- } else if (!strcmp(action, "stop")) { //Don't require control key to stop...
- //EMERGENCY STOP!! TODO - replace!
+
+ void *arg = NULL;
+ if (desired_mode == CONTROL_START) {
+ int len = strlen(name);
+ if (len <= 0) {
+ FCGI_RejectJSON(context, "An experiment name must be specified.");
+ return;
+ } else if (PathExists(name) && !force) {
+ FCGI_RejectJSON(context, "An experiment with that name already exists.");
+ return;
+ }
+
+ arg = (void*)name;
+ }
+
+ const char *ret;
+ if ((ret = Control_SetMode(desired_mode, arg)) != NULL) {
+ FCGI_RejectJSON(context, ret);
+ } else {
FCGI_BeginJSON(context, STATUS_OK);
- FCGI_JSONPair("description", "stopped! (not)");
+ FCGI_JSONPair("description", "ok");
FCGI_EndJSON();
- } else { //Under this section, the user must have the current control key.
- if (!FCGI_HasControl(context, control_key)) {
- FCGI_RejectJSONEx(context,
- STATUS_UNAUTHORIZED, "Invalid control key specified.");
- } else if (!strcmp(action, "end")) {
- FCGI_EndControl(context);
- } else if (!strcmp(action, "set")) {
- if (set_value == NULL || *set_value == '\0') {
- FCGI_RejectJSONEx(context,
- STATUS_ERROR, "Set called but no value specified.");
- } else
- {
-// ActuatorHandler(context, id, set_value);
+ }
+}
+
+/**
+ * Sets the mode to the desired mode, if possible.
+ * @param desired_mode The mode to be set to
+ * @param arg An argument specific to the mode to be set.
+ * @return NULL on success, an error message on failure.
+ */
+const char* Control_SetMode(ControlModes desired_mode, void * arg)
+{
+ const char *ret = NULL;
+
+ pthread_mutex_lock(&(g_controls.mutex));
+ if (g_controls.current_mode == CONTROL_EMERGENCY && desired_mode != CONTROL_STOP) {
+ ret = "In emergency mode. Stop before doing anything else.";
+ } else if (g_controls.current_mode == desired_mode) {
+ ret = "Already in desired mode.";
+ } else if (desired_mode == CONTROL_START) {
+ if (g_controls.current_mode == CONTROL_STOP) {
+ //TODO Sanitise name (ensure it contains no special chars eg \ / .. .
+ FILE *fp = fopen((const char*) arg, "a");
+ if (fp) {
+ fclose(fp);
+ gettimeofday(&(g_controls.start_time), NULL);
+ } else {
+ ret = "Cannot open experiment name marker";
}
+ } else {
+ ret = "Cannot start when not in a stopped state.";
}
+ } else if (desired_mode == CONTROL_RESUME) {
+ if (g_controls.current_mode != CONTROL_PAUSE)
+ ret = "Cannot resume when not in a paused state.";
}
+
+ if (ret == NULL) {
+ Actuator_SetModeAll(desired_mode, arg);
+ Sensor_SetModeAll(desired_mode, arg);
+ if (desired_mode != CONTROL_RESUME)
+ g_controls.current_mode = desired_mode;
+ else
+ g_controls.current_mode = CONTROL_START;
+ }
+ pthread_mutex_unlock(&(g_controls.mutex));
+ return ret;
}
+
+/**
+ * Gets the current mode.
+ * @return The current mode
+ */
+ControlModes Control_GetMode() {
+ return g_controls.current_mode;
+}
+
+/**
+ * Gets a string representation of a mode
+ * @param mode The mode to get a string representation of
+ * @return The string representation of the mode
+ */
+const char * Control_GetModeName(ControlModes mode) {
+ const char * ret = "Unknown";
+
+ switch (mode) {
+ case CONTROL_START: ret = "Running"; break;
+ case CONTROL_PAUSE: ret = "Paused"; break;
+ case CONTROL_RESUME: ret = "Resumed"; break;
+ case CONTROL_STOP: ret = "Stopped"; break;
+ case CONTROL_EMERGENCY: ret = "Emergency mode"; break;
+ }
+ return ret;
+}
+
+/*
+bool Control_Lock() {
+ pthread_mutex_lock(&(g_controls.mutex));
+ if (g_controls.state == STATE_RUNNING || g_controls.state == STATE_PAUSED)
+ return true;
+ pthread_mutex_unlock(&(g_controls.mutex));
+ return false;
+}
+
+void Control_Unlock() {
+ pthread_mutex_unlock(&(g_controls.mutex));
+}*/
+
+/**
+ * Gets the start time for the current experiment
+ * @return the start time
+ */
+const struct timeval* Control_GetStartTime() {
+ return &g_controls.start_time;
+}
\ No newline at end of file
#ifndef _CONTROL_H
#define _CONTROL_H
-
+typedef enum ControlModes {
+ CONTROL_START,
+ CONTROL_PAUSE,
+ CONTROL_RESUME,
+ CONTROL_STOP,
+ CONTROL_EMERGENCY
+} ControlModes;
/** ID codes for all the actuators **/
extern void Control_Handler(FCGIContext *context, char *params);
+extern const char* Control_SetMode(ControlModes desired_mode, void * arg);
+extern ControlModes Control_GetMode();
+extern const char * Control_GetModeName(ControlModes mode);
+//extern bool Control_Lock();
+//extern void Control_Unlock();
+extern const struct timeval* Control_GetStartTime();
#endif
/**
* @file data.c
- * @purpose Implementation of data handling functions; saving, loading, displaying, selecting.
+ * @brief Implementation of data handling functions; saving, loading, displaying, selecting.
*/
#include "data.h"
*/
DataFormat Data_GetFormat(FCGIValue * fmt)
{
- char * fmt_str = *(char**)(fmt->value);
+ const char * fmt_str = *(const char**)(fmt->value);
// Check if format type was specified
if (FCGI_RECEIVED(fmt->flags))
{
/**
- * @file datapoint.h
- * @purpose Declaration of data handling functions; saving, loading, displaying, selecting.
+ * @file data.h
+ * @brief Declaration of data handling functions; saving, loading, displaying, selecting.
*/
#ifndef _DATAPOINT_H
#include "options.h"
#include "image.h"
-/**The time period (in seconds) before the control key expires @ */
+/**The time period (in seconds) before the control key expires */
#define CONTROL_TIMEOUT 180
/**Contextual information related to FCGI requests*/
*/
static void IdentifyHandler(FCGIContext *context, char *params) {
bool ident_sensors = false, ident_actuators = false;
- //const char *key, *value;
int i;
FCGIValue values[2] = {{"sensors", &ident_sensors, FCGI_BOOL_T},
{"actuators", &ident_actuators, FCGI_BOOL_T}};
-
if (!FCGI_ParseRequest(context, params, values, 2))
return;
- /*while ((params = FCGI_KeyPair(params, &key, &value))) {
- if (!strcmp(key, "sensors")) {
- ident_sensors = !ident_sensors;
- } else if (!strcmp(key, "actuators")) {
- ident_actuators = !ident_actuators;
- }
- }*/
-
FCGI_BeginJSON(context, STATUS_OK);
FCGI_JSONPair("description", "MCTX3420 Server API (2013)");
FCGI_JSONPair("build_date", __DATE__ " " __TIME__);
* @param context The context to work in
* @param force Whether to force key generation or not.
*/
-void FCGI_BeginControl(FCGIContext *context, bool force) {
+void FCGI_LockControl(FCGIContext *context, bool force) {
time_t now = time(NULL);
bool expired = now - context->control_timestamp > CONTROL_TIMEOUT;
* Revokes the current control key, if present.
* @param context The context to work in
*/
-void FCGI_EndControl(FCGIContext *context) {
+void FCGI_ReleaseControl(FCGIContext *context) {
*(context->control_key) = 0;
FCGI_BeginJSON(context, STATUS_OK);
FCGI_EndJSON();
}
/**
- * Aids in parsing request parameters. Expected keys along with their type
- * and whether or not they're required are provided. This function will then
- * parse the parameter string to find these keys.
+ * Aids in parsing request parameters.
+ * Input: The expected keys along with their type and whether or not
+ * they're required.
* @param context The context to work in
* @param params The parameter string to be parsed
* @param values An array of FCGIValue's that specify expected keys
long parsed = strtol(value, &ptr, 10);
if (!*value || *ptr) {
snprintf(buf, BUFSIZ, "Expected int for '%s' but got '%s'", key, value);
- FCGI_RejectJSON(context, FCGI_EscapeJSON(buf));
+ FCGI_RejectJSON(context, buf);
return false;
}
if (FCGI_TYPE(val->flags) == FCGI_INT_T)
- *((int*) val->value) = parsed;
+ *((int*) val->value) = (int) parsed;
else
*((long*) val->value) = parsed;
} break;
*((double*) val->value) = strtod(value, &ptr);
if (!*value || *ptr) {
snprintf(buf, BUFSIZ, "Expected float for '%s' but got '%s'", key, value);
- FCGI_RejectJSON(context, FCGI_EscapeJSON(buf));
+ FCGI_RejectJSON(context, buf);
return false;
}
break;
} //End for loop
if (i == count) {
snprintf(buf, BUFSIZ, "Unknown key '%s' specified", key);
- FCGI_RejectJSON(context, FCGI_EscapeJSON(buf));
+ FCGI_RejectJSON(context, buf);
return false;
}
}
printf("\r\n}\r\n");
}
-/**
- * Escapes a string so it can be used as a JSON string value.
- * Does not support unicode specifiers in the form of \uXXXX.
- * @param buf The string to be escaped
- * @return The escaped string (return value == buf)
- */
-char *FCGI_EscapeJSON(char *buf)
-{
- int length, i;
- length = strlen(buf);
-
- //Escape special characters. Must count down to escape properly
- for (i = length - 1; i >= 0; i--) {
- if (buf[i] < 0x20) { //Control characters
- buf[i] = ' ';
- } else if (buf[i] == '"') {
- if (i-1 >= 0 && buf[i-1] == '\\')
- i--;
- else
- buf[i] = '\'';
- } else if (buf[i] == '\\') {
- if (i-1 >= 0 && buf[i-1] == '\'')
- i--;
- else
- buf[i] = ' ';
- }
- }
- return buf;
-}
-
/**
* To be used when the input parameters are rejected. The return data
* will also have debugging information provided.
FCGI_BeginJSON(context, status);
FCGI_JSONPair("description", description);
FCGI_JSONLong("responsenumber", context->response_number);
- FCGI_JSONPair("params", getenv("QUERY_STRING"));
+ //FCGI_JSONPair("params", getenv("QUERY_STRING"));
FCGI_JSONPair("host", getenv("SERVER_HOSTNAME"));
FCGI_JSONPair("user", getenv("REMOTE_USER"));
FCGI_JSONPair("ip", getenv("REMOTE_ADDR"));
fwrite(data, size, num_elem, stdout);
}
+/**
+ * Escapes a string so it can be used safely.
+ * Currently escapes to ensure the validity for use as a JSON string
+ * Does not support unicode specifiers in the form of \uXXXX.
+ * @param buf The string to be escaped
+ * @return The escaped string (return value == buf)
+ */
+char *FCGI_EscapeText(char *buf)
+{
+ int length, i;
+ length = strlen(buf);
+
+ //Escape special characters. Must count down to escape properly
+ for (i = length - 1; i >= 0; i--) {
+ if (buf[i] < 0x20) { //Control characters
+ buf[i] = ' ';
+ } else if (buf[i] == '"') {
+ if (i-1 >= 0 && buf[i-1] == '\\')
+ i--;
+ else
+ buf[i] = '\'';
+ } else if (buf[i] == '\\') {
+ if (i-1 >= 0 && buf[i-1] == '\'')
+ i--;
+ else
+ buf[i] = ' ';
+ }
+ }
+ return buf;
+}
+
/**
* Main FCGI request loop that receives/responds to client requests.
* @param data Reserved.
if (lastchar > 0 && module[lastchar] == '/')
module[lastchar] = 0;
+ //Escape all special characters
+ FCGI_EscapeText(params);
+
//Default to the 'identify' module if none specified
if (!*module)
strcpy(module, "identify");
STATUS_OK = 1,
STATUS_ERROR = -1,
STATUS_UNAUTHORIZED = -2,
- STATUS_OUTOFRANGE = -3
+ STATUS_NOTRUNNING = -3,
+ STATUS_ALREADYEXISTS = -4
} StatusCodes;
#define FCGI_PARAM_REQUIRED (1 << 0)
typedef struct FCGIContext FCGIContext;
typedef void (*ModuleHandler) (FCGIContext *context, char *params);
-extern void FCGI_BeginControl(FCGIContext *context, bool force);
-extern void FCGI_EndControl(FCGIContext *context);
+extern void FCGI_LockControl(FCGIContext *context, bool force);
+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);
extern bool FCGI_ParseRequest(FCGIContext *context, char *params, FCGIValue values[], size_t count);
extern void FCGI_JSONKey(const char *key);
extern void FCGI_PrintRaw(const char *format, ...);
extern void FCGI_EndJSON();
-extern char *FCGI_EscapeJSON(char *buf);
extern void FCGI_RejectJSONEx(FCGIContext *context, StatusCodes status, const char *description);
+extern char *FCGI_EscapeText(char *buf);
extern void *FCGI_RequestLoop (void *data);
extern void FCGI_WriteBinary(void * data, size_t size, size_t num_elem);
*/
-#include <unistd.h>
-#include <stdarg.h>
-
// --- Custom headers --- //
#include "common.h"
#include "log.h"
#include "options.h"
-// --- Static variables --- //
-static const char * unspecified_funct = "???";
-
-// --- Function implementations --- //
+#include <unistd.h>
+#include <syslog.h>
+#include <stdarg.h>
-//TODO: Migrate to syslog (shouldn't be too hard; these functions basically do what syslog does)
-// Note that we will want to have a seperate log as well as syslog; give the user the option to view the log using the GUI
+static const char * unspecified_funct = "???";
/**
- * Print a message to stderr
+ * Print a message to stderr and log it via syslog. The message must be
+ * less than BUFSIZ characters long, or it will be truncated.
* @param level - Specify how severe the message is.
If level is higher (less urgent) than the program's verbosity (see options.h) no message will be printed
* @param funct - String indicating the function name from which this function was called.
*/
void LogEx(int level, const char * funct, ...)
{
+ //Todo: consider setlogmask(3) to filter messages
const char *fmt;
+ char buffer[BUFSIZ];
va_list va;
+
+ // Don't print the message unless we need to
+ if (level > g_options.verbosity)
+ return;
+
va_start(va, funct);
fmt = va_arg(va, const char*);
if (fmt == NULL) // sanity check
- FatalEx("Log", "Format string is NULL");
+ Fatal("Format string is NULL");
- // Don't print the message unless we need to
- if (level > g_options.verbosity)
- return;
+ vsnprintf(buffer, BUFSIZ, fmt, va);
+ va_end(va);
if (funct == NULL)
funct = unspecified_funct;
switch (level)
{
case LOGERR:
+ level = LOG_ERR;
severity = "ERROR";
break;
case LOGWARN:
+ level = LOG_WARNING;
severity = "WARNING";
break;
case LOGNOTE:
+ level = LOG_NOTICE;
severity = "NOTICE";
break;
case LOGINFO:
+ level = LOG_INFO;
severity = "INFO";
break;
default:
+ level = LOG_DEBUG;
severity = "DEBUG";
break;
}
- // Print: Program name, PID, severity string, function name first
- fprintf(stderr, "%s [%d] : %s : %s - ", g_options.program, getpid(), severity, funct);
-
- // Then pass additional arguments with the format string to vfprintf for printing
- vfprintf(stderr, fmt, va);
- va_end(va);
-
- // End log messages with a newline
- fprintf(stderr, "\n");
+ syslog(level, "%s: %s - %s", severity, funct, buffer);
}
/**
void FatalEx(const char * funct, ...)
{
const char *fmt;
+ char buffer[BUFSIZ];
va_list va;
va_start(va, funct);
fmt = va_arg(va, const char*);
{
// Fatal error in the Fatal function.
// (This really shouldn't happen unless someone does something insanely stupid)
- FatalEx("Fatal", "Format string is NULL");
+ Fatal("Format string is NULL");
return; // Should never get here
}
+ vsnprintf(buffer, BUFSIZ, fmt,va);
+ va_end(va);
+
if (funct == NULL)
funct = unspecified_funct;
- fprintf(stderr, "%s [%d] : %s : FATAL - ", g_options.program, getpid(), funct);
-
- vfprintf(stderr, fmt, va);
- va_end(va);
- fprintf(stderr, "\n");
-
+ syslog(LOG_CRIT, "FATAL: %s - %s", funct, buffer);
exit(EXIT_FAILURE);
}
#include "options.h"
#include "sensor.h"
#include "actuator.h"
+#include "control.h"
// --- Standard headers --- //
+#include <syslog.h> // for system logging
#include <signal.h> // for signal handling
// --- Variable definitions --- //
{
ParseArguments(argc, argv);
+ //Open the system log
+ openlog("mctxserv", LOG_PID | LOG_PERROR, LOG_USER);
+ Log(LOGINFO, "Server started");
// signal handler
//TODO: Make this work
/*
*/
Sensor_Init();
Actuator_Init();
- Sensor_StartAll("test");
- Actuator_StartAll("test");
+ //Sensor_StartAll("test");
+ //Actuator_StartAll("test");
+ 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);
- Sensor_StopAll();
- Actuator_StopAll();
+ if ((ret = Control_SetMode(CONTROL_STOP, "test")) != NULL)
+ Fatal("Control_SetMode failed with '%s'", ret);
+ //Sensor_StopAll();
+ //Actuator_StopAll();
Cleanup();
return 0;
{1,-1,1,-1}, // ANALOG_TEST0
{500,0,499,0}, // ANALOG_TEST1
{5,-5,4,-4}, // ANALOG_FAIL0
- {1,0,1,0}, // DIGITAL_TEST0
- {1,0,1,0}, // DIGITAL_TEST1
- {1,0,1,0} // DIGITAL_FAIL0
+ {1,0,1,0}, // DIGITAL_TEST0
+ {1,0,1,0}, // DIGITAL_TEST1
+ {1,0,1,0} // DIGITAL_FAIL0
};
/** Human readable names for the sensors **/
}
/**
- * Start a Sensor recording DataPoints
- * @param s - The Sensor to start
- * @param experiment_name - Prepended to DataFile filename
+ * Sets the sensor to the desired control mode. No checks are
+ * done to see if setting to the desired mode will conflict with
+ * the current mode - the caller must guarantee this itself.
+ * @param s The sensor whose mode is to be changed
+ * @param mode The mode to be changed to
+ * @param arg An argument specific to the mode to be set.
+ * e.g for CONTROL_START it represents the experiment name.
*/
-void Sensor_Start(Sensor * s, const char * experiment_name)
+void Sensor_SetMode(Sensor * s, ControlModes mode, void * arg)
{
- // Set filename
- char filename[BUFSIZ];
- if (sprintf(filename, "%s_s%d", experiment_name, s->id) >= BUFSIZ)
+ switch(mode)
{
- Fatal("Experiment name \"%s\" too long (>%d)", experiment_name, BUFSIZ);
- }
-
- Log(LOGDEBUG, "Sensor %d with DataFile \"%s\"", s->id, filename);
- // Open DataFile
- Data_Open(&(s->data_file), filename);
-
- s->record_data = true; // Don't forget this!
-
- // Create the thread
- pthread_create(&(s->thread), NULL, Sensor_Loop, (void*)(s));
-}
-
-/**
- * Stop a Sensor from recording DataPoints. Blocks until it has stopped.
- * @param s - The Sensor to stop
- */
-void Sensor_Stop(Sensor * s)
-{
- // Stop
- if (s->record_data)
- {
- s->record_data = false;
- pthread_join(s->thread, NULL); // Wait for thread to exit
- Data_Close(&(s->data_file)); // Close DataFile
- s->newest_data.time_stamp = 0;
- s->newest_data.value = 0;
+ case CONTROL_START:
+ {
+ // Set filename
+ char filename[BUFSIZ];
+ const char *experiment_name = (const char*) arg;
+ int ret;
+
+ if (snprintf(filename, BUFSIZ, "%s_s%d", experiment_name, s->id) >= BUFSIZ)
+ {
+ Fatal("Experiment name \"%s\" too long (>%d)", experiment_name, BUFSIZ);
+ }
+
+ Log(LOGDEBUG, "Sensor %d with DataFile \"%s\"", s->id, filename);
+ // Open DataFile
+ Data_Open(&(s->data_file), filename);
+
+ s->activated = true;
+ s->record_data = true; // Don't forget this!
+
+ // Create the thread
+ ret = pthread_create(&(s->thread), NULL, Sensor_Loop, (void*)(s));
+ if (ret != 0)
+ {
+ Fatal("Failed to create Sensor_Loop for Sensor %d", s->id);
+ }
+ }
+ break;
+ case CONTROL_EMERGENCY:
+ case CONTROL_PAUSE:
+ s->record_data = false;
+ break;
+ case CONTROL_RESUME:
+ s->record_data = true;
+ break;
+ case CONTROL_STOP:
+ s->activated = false;
+ s->record_data = false;
+ pthread_join(s->thread, NULL);
+
+ Data_Close(&(s->data_file)); // Close DataFile
+ s->newest_data.time_stamp = 0;
+ s->newest_data.value = 0;
+ break;
+ default:
+ Fatal("Unknown control mode: %d", mode);
}
}
/**
- * Stop all Sensors
+ * Sets all sensors to the desired mode.
+ * @see Sensor_SetMode for more information.
+ * @param mode The mode to be changed to
+ * @param arg An argument specific to the mode to be set.
*/
-void Sensor_StopAll()
+void Sensor_SetModeAll(ControlModes mode, void * arg)
{
- for (int i = 0; i < NUMSENSORS; ++i)
- Sensor_Stop(g_sensors+i);
-}
-
-/**
- * Start all Sensors
- */
-void Sensor_StartAll(const char * experiment_name)
-{
- for (int i = 0; i < NUMSENSORS; ++i)
- Sensor_Start(g_sensors+i, experiment_name);
+ for (int i = 0; i < NUMSENSORS; i++)
+ Sensor_SetMode(&g_sensors[i], mode, arg);
}
// Set time stamp
struct timeval t;
gettimeofday(&t, NULL);
- d->time_stamp = TIMEVAL_DIFF(t, g_options.start_time);
+ d->time_stamp = TIMEVAL_DIFF(t, *Control_GetStartTime());
// Read value based on Sensor Id
switch (s->id)
Log(LOGDEBUG, "Sensor %d starts", s->id);
// Until the sensor is stopped, record data points
- while (s->record_data)
+ while (s->activated)
{
- DataPoint d;
- //Log(LOGDEBUG, "Sensor %d reads data [%f,%f]", s->id, d.time_stamp, d.value);
- if (Sensor_Read(s, &d)) // If new DataPoint is read:
+ if (s->record_data)
+ {
+ DataPoint d;
+ //Log(LOGDEBUG, "Sensor %d reads data [%f,%f]", s->id, d.time_stamp, d.value);
+ if (Sensor_Read(s, &d)) // If new DataPoint is read:
+ {
+ //Log(LOGDEBUG, "Sensor %d saves data [%f,%f]", s->id, d.time_stamp, d.value);
+ Data_Save(&(s->data_file), &d, 1); // Record it
+ }
+ }
+ else
{
- //Log(LOGDEBUG, "Sensor %d saves data [%f,%f]", s->id, d.time_stamp, d.value);
- Data_Save(&(s->data_file), &d, 1); // Record it
+ //Do something? wait?
+ usleep(100000);
}
}
{
struct timeval now;
gettimeofday(&now, NULL);
- double current_time = TIMEVAL_DIFF(now, g_options.start_time);
+ double current_time = TIMEVAL_DIFF(now, *Control_GetStartTime());
int id = 0;
double start_time = 0;
return;
}
Sensor * s = g_sensors+id;
-
+
DataFormat format = Data_GetFormat(&(values[FORMAT]));
// Begin response
// Finish response
Sensor_EndResponse(context, id, format);
-
}
/** Number of sensors **/
#define NUMSENSORS 6
+/** Sensor ids - there should be correspondence with the names in g_sensor_names **/
typedef enum SensorId
{
ANALOG_TEST0,
DIGITAL_FAIL0
} SensorId;
-
-
/** Human readable names for the sensors **/
extern const char * g_sensor_names[NUMSENSORS];
-
/** Structure to represent a sensor **/
typedef struct
{
SensorId id;
/** DataFile to store sensor values in **/
DataFile data_file;
+ /** Indicates whether the Sensor is not stopped **/
+ bool activated;
/** Indicates whether the Sensor should record data **/
bool record_data;
/** Thread the Sensor is running in **/
pthread_t thread;
/** Most recently recorded data **/
DataPoint newest_data;
-
} Sensor;
// Structure to define the warning and error thresholds of the sensors
extern void Sensor_Init(); // One off initialisation of *all* sensors
-extern void Sensor_StartAll(const char * experiment_name); // Start all Sensors recording data
-extern void Sensor_StopAll(); // Stop all Sensors recording data
-extern void Sensor_Start(Sensor * s, const char * experiment_name); // Start a sensor recording datas
-extern void Sensor_Stop(Sensor * s); // Stop a Sensor from recording data
-
+extern void Sensor_SetModeAll(ControlModes mode, void * arg);
+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
#!/bin/bash
-valgrind --leak-check=full --track-origins=yes --show-reachable=yes ./server
+valgrind --leak-check=full --show-reachable=yes ./server