Merge pull request #37 from jtanx/master
authorJeremy Tan <[email protected]>
Wed, 18 Sep 2013 01:08:08 +0000 (18:08 -0700)
committerJeremy Tan <[email protected]>
Wed, 18 Sep 2013 01:08:08 +0000 (18:08 -0700)
Update control code
Todo: Must add issues outlined and reconsider use of syslog

24 files changed:
nginx-configs/README [deleted file]
nginx-configs/fastcgi_params [deleted file]
nginx-configs/mime.types [deleted file]
nginx-configs/sites-enabled/mctxconfig [deleted file]
server-configs/README.txt [new file with mode: 0644]
server-configs/nginx/README [new file with mode: 0644]
server-configs/nginx/fastcgi_params [new file with mode: 0644]
server-configs/nginx/mime.types [new file with mode: 0644]
server-configs/nginx/sites-enabled/mctxconfig [new file with mode: 0644]
server-configs/rsyslog.d/30-mctxserv.conf [new file with mode: 0644]
server/actuator.c
server/actuator.h
server/common.h
server/control.c
server/control.h
server/data.c
server/data.h
server/fastcgi.c
server/fastcgi.h
server/log.c
server/main.c
server/sensor.c
server/sensor.h
server/valgrind.sh

diff --git a/nginx-configs/README b/nginx-configs/README
deleted file mode 100644 (file)
index 99499a5..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-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
diff --git a/nginx-configs/fastcgi_params b/nginx-configs/fastcgi_params
deleted file mode 100644 (file)
index 51aa692..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-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;
diff --git a/nginx-configs/mime.types b/nginx-configs/mime.types
deleted file mode 100644 (file)
index 9d05612..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-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;
-}
diff --git a/nginx-configs/sites-enabled/mctxconfig b/nginx-configs/sites-enabled/mctxconfig
deleted file mode 100644 (file)
index 4da2b84..0000000
+++ /dev/null
@@ -1,130 +0,0 @@
-# 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;
-#      }
-#}
diff --git a/server-configs/README.txt b/server-configs/README.txt
new file mode 100644 (file)
index 0000000..2b1c107
--- /dev/null
@@ -0,0 +1,4 @@
+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
diff --git a/server-configs/nginx/README b/server-configs/nginx/README
new file mode 100644 (file)
index 0000000..99499a5
--- /dev/null
@@ -0,0 +1,23 @@
+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
diff --git a/server-configs/nginx/fastcgi_params b/server-configs/nginx/fastcgi_params
new file mode 100644 (file)
index 0000000..51aa692
--- /dev/null
@@ -0,0 +1,27 @@
+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;
diff --git a/server-configs/nginx/mime.types b/server-configs/nginx/mime.types
new file mode 100644 (file)
index 0000000..9d05612
--- /dev/null
@@ -0,0 +1,80 @@
+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;
+}
diff --git a/server-configs/nginx/sites-enabled/mctxconfig b/server-configs/nginx/sites-enabled/mctxconfig
new file mode 100644 (file)
index 0000000..1491152
--- /dev/null
@@ -0,0 +1,119 @@
+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;
+#      }
+#}
diff --git a/server-configs/rsyslog.d/30-mctxserv.conf b/server-configs/rsyslog.d/30-mctxserv.conf
new file mode 100644 (file)
index 0000000..ff4b097
--- /dev/null
@@ -0,0 +1,3 @@
+$FileCreateMode 0644
+if $programname == 'mctxserv' then /var/log/mctxserv.log
+$FileCreateMode 0640
index 759fffc..9d2ad13 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * @file actuator.c
- * @purpose Implementation of Actuator related functionality
+ * @brief Implementation of Actuator related functionality
  */
 
 #include "actuator.h"
@@ -28,61 +28,76 @@ void Actuator_Init()
 }
 
 /**
- * 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);
-       }
+               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);
+                               }
 
-       Log(LOGDEBUG, "Actuator %d with DataFile \"%s\"", a->id, filename);
-       // Open DataFile
-       Data_Open(&(a->data_file), filename);
+                               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;
+                               a->activated = true; // Don't forget this
+                               a->allow_actuation = true;
 
-       // Create the thread
-       pthread_create(&(a->thread), NULL, Actuator_Loop, (void*)(a));
-}
+                               a->control_changed = false;
 
-/**
- * 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
-
-}
-
-/**
- * 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);
 }
 
 /**
@@ -106,7 +121,9 @@ void * Actuator_Loop(void * 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);
        }
 
@@ -127,7 +144,6 @@ void Actuator_SetControl(Actuator * a, ActuatorControl * c)
        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));
        
@@ -144,30 +160,31 @@ void Actuator_SetValue(Actuator * a, double value)
        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);
+               case ACTUATOR_TEST0: 
+                       {
+                               FILE *led_handle = NULL;        //code reference: http://learnbuildshare.wordpress.com/2013/05/19/beaglebone-black-controlling-user-leds-using-c/
+                               const char *led_format = "/sys/class/leds/beaglebone:green:usr%d/brightness";
+                               char buf[50];
+                               bool turn_on = value;
+
+                               for (int i = 0; i < 4; i++) 
+                               {
+                                       snprintf(buf, 50, led_format, i);
+                                       if ((led_handle = fopen(buf, "w")) != NULL)
+                                       {
+                                               if (turn_on)
+                                                       fwrite("1", sizeof(char), 1, led_handle);
+                                               else
+                                                       fwrite("0", sizeof(char), 1, led_handle);
+                                               fclose(led_handle);
                                        }
+                                       else
+                                               Log(LOGDEBUG, "LED fopen failed: %s", strerror(errno)); 
                                }
-                               else perror("Pin value should be 1 or 0");
                        }
                        break;
                case ACTUATOR_TEST1:
@@ -180,7 +197,6 @@ void Actuator_SetValue(Actuator * a, double value)
        Data_Save(&(a->data_file), &d, 1);
 }
 
-
 /**
  * Helper: Begin Actuator response in a given format
  * @param context - the FCGIContext
@@ -233,7 +249,7 @@ void Actuator_Handler(FCGIContext * context, char * params)
 {
        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;
index df946a0..81150e8 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * @file actuator.h
- * @purpose Declarations for actuator control
+ * @brief Declarations for actuator control
  */
 
 #ifndef _ACTUATOR_H
@@ -52,18 +52,15 @@ typedef struct
        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
index a690149..97290d2 100644 (file)
@@ -26,6 +26,7 @@
 
 #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))
index 1f33eea..2c0fd79 100644 (file)
 /**
  * @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
index 2de9b7f..5b51e95 100644 (file)
@@ -5,9 +5,21 @@
 #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
index add8d9f..3824833 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * @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"
@@ -344,7 +344,7 @@ void Data_Handler(DataFile * df, FCGIValue * start, FCGIValue * end, DataFormat
  */
 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))
        {
index 30d1253..e8d887f 100644 (file)
@@ -1,6 +1,6 @@
 /**
- * @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
index 62978ea..b7a3e6b 100644 (file)
@@ -17,7 +17,7 @@
 #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*/
@@ -41,24 +41,14 @@ struct FCGIContext {
  */ 
 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__);
@@ -99,7 +89,7 @@ static void IdentifyHandler(FCGIContext *context, char *params) {
  * @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;
        
@@ -155,7 +145,7 @@ bool FCGI_HasControl(FCGIContext *context, const char *key) {
  * 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();
@@ -202,9 +192,9 @@ char *FCGI_KeyPair(char *in, const char **key, const char **value)
 }
 
 /**
- * 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
@@ -243,12 +233,12 @@ bool FCGI_ParseRequest(FCGIContext *context, char *params, FCGIValue values[], s
                                                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;
@@ -256,7 +246,7 @@ bool FCGI_ParseRequest(FCGIContext *context, char *params, FCGIValue values[], s
                                                *((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;
@@ -271,7 +261,7 @@ bool FCGI_ParseRequest(FCGIContext *context, char *params, FCGIValue values[], s
                } //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;
                }
        }
@@ -365,36 +355,6 @@ void FCGI_EndJSON()
        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.
@@ -411,7 +371,7 @@ void FCGI_RejectJSONEx(FCGIContext *context, StatusCodes status, const char *des
        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"));
@@ -445,6 +405,37 @@ void FCGI_WriteBinary(void * data, size_t size, size_t num_elem)
        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.
@@ -470,6 +461,9 @@ void * FCGI_RequestLoop (void *data)
                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");
index adb1db9..8678a77 100644 (file)
@@ -17,7 +17,8 @@ typedef enum StatusCodes {
        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)
@@ -41,8 +42,8 @@ typedef struct FCGIValue {
 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);
@@ -54,8 +55,8 @@ extern void FCGI_JSONBool(const char *key, bool value);
 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);
index d964e61..d41021f 100644 (file)
@@ -4,24 +4,20 @@
  */
 
 
-#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.
@@ -31,17 +27,23 @@ static const char * unspecified_funct = "???";
  */
 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;
@@ -51,31 +53,28 @@ void LogEx(int level, const char * 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);
 }
 
 /**
@@ -88,6 +87,7 @@ void LogEx(int level, const char * funct, ...)
 void FatalEx(const char * funct, ...)
 {
        const char *fmt;
+       char buffer[BUFSIZ];
        va_list va;
        va_start(va, funct);
        fmt = va_arg(va, const char*);
@@ -96,19 +96,17 @@ void FatalEx(const char * funct, ...)
        {
                // 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);
 }
 
index 6ea256c..c032eb1 100644 (file)
@@ -8,8 +8,10 @@
 #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 --- //
@@ -67,6 +69,9 @@ int main(int argc, char ** argv)
 {
        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
        /*
@@ -78,14 +83,19 @@ int main(int argc, char ** argv)
        */
        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;
index b065708..5115f99 100644 (file)
@@ -18,9 +18,9 @@ const SensorThreshold thresholds[NUMSENSORS]= {
        {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 **/
@@ -44,62 +44,76 @@ void Sensor_Init()
 }
 
 /**
- * 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);
 }
 
 
@@ -134,7 +148,7 @@ bool Sensor_Read(Sensor * s, DataPoint * d)
        // 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)
@@ -195,14 +209,22 @@ void * Sensor_Loop(void * arg)
        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);
                }
        }
        
@@ -284,7 +306,7 @@ void Sensor_Handler(FCGIContext *context, char * params)
 {
        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;
@@ -321,7 +343,7 @@ void Sensor_Handler(FCGIContext *context, char * params)
                return;
        }
        Sensor * s = g_sensors+id;
-       
+
        DataFormat format = Data_GetFormat(&(values[FORMAT]));
 
        // Begin response
@@ -332,7 +354,6 @@ void Sensor_Handler(FCGIContext *context, char * params)
        
        // Finish response
        Sensor_EndResponse(context, id, format);
-       
 }
 
 
index 3f833d1..bdab0f9 100644 (file)
@@ -11,6 +11,7 @@
 /** Number of sensors **/
 #define NUMSENSORS 6 
 
+/** Sensor ids - there should be correspondence with the names in g_sensor_names **/
 typedef enum SensorId 
 {
        ANALOG_TEST0,
@@ -21,12 +22,9 @@ typedef enum SensorId
        DIGITAL_FAIL0
 } SensorId;
 
-
-
 /** Human readable names for the sensors **/
 extern const char * g_sensor_names[NUMSENSORS];
 
-
 /** Structure to represent a sensor **/
 typedef struct
 {
@@ -34,13 +32,14 @@ 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
@@ -54,11 +53,8 @@ typedef struct
 
 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
index 062c490..e5214bd 100755 (executable)
@@ -1,2 +1,2 @@
 #!/bin/bash
-valgrind --leak-check=full --track-origins=yes --show-reachable=yes ./server
+valgrind --leak-check=full --show-reachable=yes ./server

UCC git Repository :: git.ucc.asn.au