From: Jeremy Tan Date: Tue, 17 Sep 2013 07:57:21 +0000 (+0800) Subject: Merge branch 'master' of https://github.com/szmoore/MCTX3420.git X-Git-Url: https://git.ucc.asn.au/?a=commitdiff_plain;h=01d1e74d5b4cefd75d9ff4a5a2a404a71a225712;hp=e98b2d71aa507c8656b47752a529a3a2a619875a;p=matches%2FMCTX3420.git Merge branch 'master' of https://github.com/szmoore/MCTX3420.git Conflicts: server/actuator.c --- diff --git a/nginx-configs/README b/nginx-configs/README deleted file mode 100644 index 99499a5..0000000 --- a/nginx-configs/README +++ /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 index 51aa692..0000000 --- a/nginx-configs/fastcgi_params +++ /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 index 9d05612..0000000 --- a/nginx-configs/mime.types +++ /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 index 4da2b84..0000000 --- a/nginx-configs/sites-enabled/mctxconfig +++ /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 index 0000000..2b1c107 --- /dev/null +++ b/server-configs/README.txt @@ -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 index 0000000..99499a5 --- /dev/null +++ b/server-configs/nginx/README @@ -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 index 0000000..51aa692 --- /dev/null +++ b/server-configs/nginx/fastcgi_params @@ -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 index 0000000..9d05612 --- /dev/null +++ b/server-configs/nginx/mime.types @@ -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 index 0000000..1491152 --- /dev/null +++ b/server-configs/nginx/sites-enabled/mctxconfig @@ -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 index 0000000..ff4b097 --- /dev/null +++ b/server-configs/rsyslog.d/30-mctxserv.conf @@ -0,0 +1,3 @@ +$FileCreateMode 0644 +if $programname == 'mctxserv' then /var/log/mctxserv.log +$FileCreateMode 0640 diff --git a/server/actuator.c b/server/actuator.c index 759fffc..af69c7f 100644 --- a/server/actuator.c +++ b/server/actuator.c @@ -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); - } - - 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); } /** @@ -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,31 +160,11 @@ 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); - } - } - else perror("Pin value should be 1 or 0"); - } + case ACTUATOR_TEST0: break; case ACTUATOR_TEST1: break; @@ -180,7 +176,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 +228,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; diff --git a/server/actuator.h b/server/actuator.h index df946a0..81150e8 100644 --- a/server/actuator.h +++ b/server/actuator.h @@ -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 diff --git a/server/common.h b/server/common.h index a690149..97290d2 100644 --- a/server/common.h +++ b/server/common.h @@ -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)) diff --git a/server/control.c b/server/control.c index 1f33eea..2c0fd79 100644 --- a/server/control.c +++ b/server/control.c @@ -1,107 +1,193 @@ /** * @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 diff --git a/server/control.h b/server/control.h index 2de9b7f..5b51e95 100644 --- a/server/control.h +++ b/server/control.h @@ -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 diff --git a/server/data.c b/server/data.c index add8d9f..3824833 100644 --- a/server/data.c +++ b/server/data.c @@ -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)) { diff --git a/server/data.h b/server/data.h index 30d1253..e8d887f 100644 --- a/server/data.h +++ b/server/data.h @@ -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 diff --git a/server/fastcgi.c b/server/fastcgi.c index 62978ea..b7a3e6b 100644 --- a/server/fastcgi.c +++ b/server/fastcgi.c @@ -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"); diff --git a/server/fastcgi.h b/server/fastcgi.h index adb1db9..8678a77 100644 --- a/server/fastcgi.h +++ b/server/fastcgi.h @@ -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); diff --git a/server/log.c b/server/log.c index d964e61..d41021f 100644 --- a/server/log.c +++ b/server/log.c @@ -4,24 +4,20 @@ */ -#include -#include - // --- Custom headers --- // #include "common.h" #include "log.h" #include "options.h" -// --- Static variables --- // -static const char * unspecified_funct = "???"; - -// --- Function implementations --- // +#include +#include +#include -//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); } diff --git a/server/main.c b/server/main.c index 6ea256c..c032eb1 100644 --- a/server/main.c +++ b/server/main.c @@ -8,8 +8,10 @@ #include "options.h" #include "sensor.h" #include "actuator.h" +#include "control.h" // --- Standard headers --- // +#include // for system logging #include // 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; diff --git a/server/sensor.c b/server/sensor.c index b065708..5115f99 100644 --- a/server/sensor.c +++ b/server/sensor.c @@ -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); - } diff --git a/server/sensor.h b/server/sensor.h index 3f833d1..bdab0f9 100644 --- a/server/sensor.h +++ b/server/sensor.h @@ -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 diff --git a/server/valgrind.sh b/server/valgrind.sh index 062c490..e5214bd 100755 --- a/server/valgrind.sh +++ b/server/valgrind.sh @@ -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