Merge remote-tracking branch 'upstream/master'
authorJustin Kruger <[email protected]>
Fri, 16 Aug 2013 08:46:41 +0000 (16:46 +0800)
committerJustin Kruger <[email protected]>
Fri, 16 Aug 2013 08:46:41 +0000 (16:46 +0800)
irc/log
server/Makefile
server/common.h [new file with mode: 0644]
server/log.h
server/main.c
server/query.c [new file with mode: 0644]
server/query.h [new file with mode: 0644]
server/sensor.c
server/sensor.h

diff --git a/irc/log b/irc/log
index 103a54c..c3d0aca 100644 (file)
--- a/irc/log
+++ b/irc/log
 21:26 -!- jtanx [[email protected]] has quit ["ChatZilla 0.9.89 [Firefox 22.0/20130618035212]"]
 21:34 -!- jtanx [[email protected]] has joined #mctxuwa_softdev
 22:54 -!- jtanx [[email protected]] has quit ["ChatZilla 0.9.89 [Firefox 22.0/20130618035212]"]
+--- Day changed Thu Aug 15 2013
+01:20 -!- justin_kruger [[email protected]] has joined #mctxuwa_softdev
+01:20 -!- justin_kruger [[email protected]] has quit [EOF From client]
+07:58 -!- jtanx [[email protected]] has joined #mctxuwa_softdev
+08:21 < jtanx> hey
+08:22 < jtanx> just a suggestion for the logging functions, but you can use macros
+08:22 < jtanx> to get the function name (and even file name)
+08:22 < jtanx> v
+08:22 < jtanx> http://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html
+08:23 < jtanx> so you can do stuff like #define Log(level, fmt, ...) LogReal(level, __func__, fmt, __VA_ARGS__)
+08:25 < jtanx> it should be c99 conformant
+09:15 < jtanx> I created a pull request anyway
+09:33 -!- jtanx [[email protected]] has quit ["ChatZilla 0.9.89 [Firefox 22.0/20130618035212]"]
+13:33 -!- jtanx [[email protected]] has joined #mctxuwa_softdev
+13:39 < sam_moore> Yeah, I probably should have looked at that
+13:46 < jtanx> back to the rpi+arduino
+13:47 < jtanx> didn't they know about the beagle board black, or can that not be used for some reason?
+13:48 < jtanx> beagle bone* black
+14:20 < sam_moore> I don't know what model they were looking at exactly
+14:20 < sam_moore> I told them the Beagle Bone was about $50, I haven't had much time to research it myself
+14:20 < sam_moore> I didn't know about the Beagle Board/Bone, which was why I suggeste the RPi + Arduino
+14:20 < sam_moore> They originally were just considering an Arduino
+14:21 < sam_moore> Which would not have been fun for the remote interfacing stuff
+14:21 < sam_moore> Well we could do it, but we wouldn't be able to have a nice web browser controlled GUI, probably some dedicated networked server/client with a GUI program installed on the client machine
+14:22 < sam_moore> Also image processing would have been interesting
+14:23 < sam_moore> Next time there's a combined groups meeting, hopefully we can talk to them more
+14:24 < jtanx> yeah
+14:24 < jtanx> there's quite a few
+14:24 < jtanx> there's a beagleboard which is ~150
+14:24 < jtanx> beaglebone which is ~90
+14:24 < jtanx> beaglebone black which is ~50
+14:24 < sam_moore> Right
+14:24 < sam_moore> So they were probably looking at the beaglebone for $90
+14:24 < jtanx> yeah probably
+14:24 < jtanx> it's weird because the black version has better specs, from what i can see
+14:25 < sam_moore> But still, even for $150, if it saves us 10 hours of writing code to interface the RPi with Arduinos, it will be worth it
+14:25 < jtanx> yeah exactly
+14:25 < sam_moore> Arduinos themselves cost a bit
+14:25 < jtanx> well
+14:25 < jtanx> you can get equivalents off ebay
+14:25 < jtanx> like an uno is $10
+14:26 < sam_moore> The only issue with the beaglebone to avoid having to use arduinos as well, is whether it has enough ADC by itself
+14:26 < jtanx> yeah
+14:26 < jtanx> well how many sensors are needed
+14:26 < sam_moore> If it doesn't, you might have to add an Arduino or two anyway
+14:26 < jtanx> you could just add on an adc chip
+14:26 < sam_moore> Yes, or something like that
+14:27 < jtanx> the beaglebone has like a bazillion gpio ports
+14:31 < sam_moore> Well without getting into the specific details, it sounds like we should recommend they use that
+14:32 < sam_moore> Apparently the sensors team will have a list ready by monday, which will be good
+14:33 < jtanx> that'd be good
+14:35 < jtanx> when are combined group meetings? just the 9am slot? 
+14:36 < sam_moore> 9am Tuesday is the "official" time picked by Adrian, 2pm Wednesday is the time that we agreed between the teams
+14:36 < sam_moore> I'm going to go to both, if more people want to come from the team that's also good
+14:37 < sam_moore> Realistically not everyone is going to come, so I'll have to send emails a lot
+14:38 < jtanx> ok
+14:39 < sam_moore> What account do you need to be invited to the dropbox?
+14:39 < sam_moore> I think Alex invited everyone using their student email
+14:40 < sam_moore> Oh right, you wouldn't have been in the list that he used if you enrolled late
+14:40 < jtanx> yeah, through student email would be good
+14:41 < jtanx> last year's experiment ran with an arduino diecimila which only had 5 analogue inputs
+14:41 < jtanx> any reason why we need more?
+14:41 < jtanx> sorry that's 6
+14:42 < sam_moore> I think the estimate was: 4-6 strain gauges, 1 temperature sensor, 1 microphone, 2 pressure gauges, 1 (maybe 2) USB webcam
+14:43 < jtanx> ok
+14:44 < jtanx> At that rate you would definitely need something with more analogue inputs
+14:45 < sam_moore> We also might need a DAC for one of the pneumatics team's devices
+14:46 < sam_moore> But you can't get that on a microcontroller, there'd have to be a seperate module
+14:46 < jtanx> yep
+14:48 < jtanx> it'd be no point interfacing an arduino to the rpi/beaglebone if all you want is more analog inputs
+14:49 < sam_moore> If you can get modules for ADC that can talk to a rpi/beaglebone, then yes
+14:49 < jtanx> yeah
+14:49 < jtanx> I don't think they're too hard to wire up
+14:50 < sam_moore> I think the electronics team should be considering all this, but I don't know since we haven't verbally spoken
+14:50 < sam_moore> Well not at length anyway
+14:51 < sam_moore> Just when I happen to bump into Omid
+14:51 < jtanx> hmm
+14:54 < sam_moore> This project is probably going to be a good lesson in "Why you need a project manager"
+14:55 < jtanx> so true
+14:59 < jtanx> with the web interface, what sort of update times are we looking at?
+15:00 < sam_moore> My tests with apache2 and the custom HTTP server showed it took about 50us for jQuery to get an AJAX request
+15:01 < sam_moore> There was only one data point returned each time though, we can probably optimise it a bit by returning multiple data points with a request
+15:01 < jtanx> yeah
+15:07 < jtanx> I wonder what sort of performance impact running one (or two) cameras would have on the rpi/beaglebone
+15:44 < jtanx> urgh
+15:45 < jtanx> I was wondering why my nginx config wasn't working
+15:45 < jtanx> until I realised that gedit was creating a backup copy of the config file
+15:45 < jtanx> so nginx was reading both the original and backup
+17:28 < sam_moore> That's wierd, you'd think it would only read one config file
+17:34 < jtanx> well it was in a config directory
+17:36 < sam_moore> Oh, ok
+18:49 < jtanx> so the current idea i have with the web thing is
+18:49 < jtanx> you can query it like http://domain/api/module?key=value&key=value2
+18:50 < jtanx> and then you could return something in json format or whatever is most suitable for the ajax query
+19:46 < jtanx> you can test it at http://mctx.us.to:8080/apoi
+19:46 < jtanx> woops, http://mctx.us.to:8080/api
+19:46 < jtanx> the only 'module' which will give a response of '200 OK' is sensors
+19:47 < jtanx> which currently spits back any arguments you pass to it
+19:47 < jtanx> eg http://mctx.us.to:8080/api/sensors?k=v
+19:50 < jtanx> hopefully it doesn't break
+20:44 < sam_moore> I'll take a look
+20:45 < sam_moore> Looks good
+20:45 < sam_moore> I'm writing a dummy thread for a sensor now
+21:04 < jtanx> the code behind it (in the cgi module) is a bit clunky right now though
+21:04 < jtanx> is there a meeting tomorrow?
+21:05 < sam_moore> I don't think so, sorry
+21:06 < jtanx> ok that's alright
+21:06 < sam_moore> Things aren't urgent (yet)
+21:07 < jtanx> heh
+21:12 < sam_moore> For the progress report: I'd like everyone to write a page individually, then we can summarize those in the group report
+21:12 < sam_moore> Well you don't have to write a whole page, and if you miss a week or so it's not a big problem
+21:16 < jtanx> ok
+21:17 < jtanx> i'll try to remember that
+21:18 < jtanx> do you think we need to keep track of how long we spend on this project
+21:18 < jtanx> for that 'cost estimate'
+21:28 -!- jtanx [[email protected]] has quit ["ChatZilla 0.9.89 [Firefox 22.0/20130618035212]"]
+21:34 -!- jtanx [[email protected]] has joined #mctxuwa_softdev
+22:23 < sam_moore> For the cost estimate: Yes, we are supposed to have a technical diary (hand written)
+22:23 < sam_moore> Including all notes and times worked on the project
+22:40 -!- jtanx [[email protected]] has quit ["ChatZilla 0.9.89 [Firefox 22.0/20130618035212]"]
index 196dfa1..009128b 100644 (file)
@@ -2,13 +2,13 @@
 CXX = gcc
 FLAGS = -std=c99 -Wall -Werror -pedantic -g
 LIB = -lpthread
-OBJ = log.o main.o
+OBJ = log.o sensor.o query.o main.o
 RM = rm -f
 
 BIN = server
 
 $(BIN) : $(OBJ)
-       $(CXX) $(FLAGS) -o $(BIN) $(OBJ)
+       $(CXX) $(FLAGS) -o $(BIN) $(OBJ) $(LIB)
 
 
 %.o : %.c
diff --git a/server/common.h b/server/common.h
new file mode 100644 (file)
index 0000000..2a6184a
--- /dev/null
@@ -0,0 +1,20 @@
+/**
+ * @file common.h
+ * @purpose Common header includes
+ */
+
+#ifndef _COMMON_H
+#define _COMMON_H
+
+#define _POSIX_C_SOURCE 200809L
+#define _BSD_SOURCE
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+#include <pthread.h>
+#include <unistd.h>
+
+#endif //_COMMON_H
index 6db52f6..005059c 100644 (file)
@@ -10,6 +10,7 @@
 #include <stdio.h>
 #include <stdbool.h>
 #include <stdarg.h>
+#include "common.h"
 
 //To get around a 'pedantic' C99 rule that you must have at least 1 variadic arg, combine fmt into that.
 #define Log(level, ...) LogEx(level, __func__, __VA_ARGS__)
index 61ba76d..5e2c6c8 100644 (file)
@@ -2,22 +2,21 @@
  * @file main.c
  * @purpose main and its helper functions, signal handling and cleanup functions
  */
+#include "common.h"
 
-#define _POSIX_C_SOURCE 200809L // For strsignal to work
 
 // --- Standard headers --- //
-#include <stdlib.h>
-#include <stdio.h>
 #include <signal.h> // for signal handling
-#include <string.h> // string functions
-#include <pthread.h>
 
 // --- Custom headers --- //
+#include "query.h"
 #include "log.h"
 #include "options.h"
+#include "sensor.h"
 
 // --- Variable definitions --- //
 Options g_options; // options passed to program through command line arguments
+Sensor g_sensors[NUMSENSORS]; // sensors array
 
 // --- Function definitions --- //
 
@@ -37,12 +36,14 @@ void ParseArguments(int argc, char ** argv)
  * Handle a signal
  * @param signal - The signal number
  */
+//TODO: Something that gets massively annoying with threads is that you can't predict which one gets the signal
+// There are ways to deal with this, but I can't remember them
 void SignalHandler(int signal)
 {
        // At the moment just always exit.
        // Call `exit` so that Cleanup will be called to... clean up.
-       Log(LOGWARN, "Got signal %d (%s). Exiting.", sig, strsignal(sig));
-       exit(sig);
+       Log(LOGWARN, "Got signal %d (%s). Exiting.", signal, strsignal(signal));
+       exit(signal);
 }
 
 /**
@@ -63,7 +64,17 @@ void Cleanup()
  */
 int main(int argc, char ** argv)
 {
-       ParseArguments(argc, argv, &g_options);
+       ParseArguments(argc, argv);
+
+       // start sensor threads
+       for (int i = 0; i < NUMSENSORS; ++i)
+       {
+               Sensor_Init(g_sensors+i, i);
+               pthread_create(&(g_sensors[i].thread), NULL, Sensor_Main, (void*)(g_sensors+i));
+       }
+
+       // run request thread in the main thread
+       Query_Main(NULL); //TODO: Replace with FastCGI code
        return 0;
 }
 
diff --git a/server/query.c b/server/query.c
new file mode 100644 (file)
index 0000000..8499e37
--- /dev/null
@@ -0,0 +1,81 @@
+/**
+ * @file query.c
+ * @purpose Temporary file to run a test thread that will query a sensors thread
+ * Code will probably be combined with Jeremy's FastCGI API
+ */
+
+
+
+#include "query.h"
+
+#include "sensor.h"
+#include "log.h"
+
+static DataPoint buffer[QUERY_BUFSIZ];
+
+/**
+ * Query sensor with id
+ * @param id - The index of the sensor in g_sensors
+ */
+void QuerySensor(int id) //TODO: This code will form the SensorHandler FastCGI function (I think?)
+{
+       Sensor * s = g_sensors+id;
+
+       int amount_read = 0;
+       //CRITICAL SECTION (Don't access file while sensor thread is writing to it!)
+       pthread_mutex_lock(&(s->mutex));
+
+               FILE * file = fopen(s->filename, "rb");
+               if (file == NULL)
+               {
+                       Log(LOGWARN, "Couldn't open file \"%s\" mode rb - %s", s->filename, strerror(errno));                   
+               }
+               else
+               {
+                       fseek(file, (s->read_offset)*sizeof(DataPoint), SEEK_SET);
+                       amount_read = fread(&buffer, sizeof(DataPoint), QUERY_BUFSIZ, file);
+                       s->read_offset += amount_read;
+                       Log(LOGDEBUG, "Read %d data points; offset now at %d", amount_read, s->read_offset);
+                       
+                       fclose(file);
+               }
+
+       pthread_mutex_unlock(&(s->mutex));
+       //End critical section
+
+       // So... we have a buffer
+       // I guess we'll want to JSON it or something?
+       // Just print it out for now
+       for (int i = 0; i < amount_read; ++i)
+       {
+               printf("%f\t%f\n", buffer[i].time, buffer[i].value);
+       }
+
+       // Will want to handle case where there actually wasn't anything new to respond with
+       // (In case we have a sensor that is slower than the rate of jQuery requests)
+       if (amount_read == 0)
+       {
+               Log(LOGWARN, "No data points read from sensor%s file");
+               printf("# No data\n");
+       }
+}
+
+/**
+ * Test function to simulate responding to HTTP requests
+ * @param args - IGNORED (void* required to pass function to pthread_create)
+ * @returns NULL (void* required to pass function to pthread_create)
+ */
+void * Query_Main(void * args)
+{
+       while (true) //TODO: Exit condition
+       {
+               
+               for (int i = 0; i < NUMSENSORS; ++i)
+               {
+                       printf("# Sensor %d\n", i);
+                       QuerySensor(i);
+                       printf("\n");   
+               }
+               usleep(REQUEST_RATE);
+       }
+}
diff --git a/server/query.h b/server/query.h
new file mode 100644 (file)
index 0000000..55a4991
--- /dev/null
@@ -0,0 +1,14 @@
+/**
+ * @file query.h
+ * @purpose Header for query stuff
+ */
+
+#ifndef _QUERY_H
+#define _QUERY_H
+
+#define REQUEST_RATE 2000000 // rate to make requests
+#define QUERY_BUFSIZ 10 // size of query buffer
+
+extern void * Query_Main(void * args);
+
+#endif //QUERY_H
index d1d8b9f..a3d3f0b 100644 (file)
  * TODO: Finalise implementation
  */
 
+
+
 #include "sensor.h"
+#include "log.h"
+#include <math.h>
+
+/**
+ * Read a data value from a sensor; block until value is read
+ * @param sensor_id - The ID of the sensor
+ * @returns The current value of the sensor
+ */
+DataPoint GetData(int sensor_id)
+{
+       // switch based on the sensor_id at the moment for testing;
+       // might be able to just directly access ADC from sensor_id?
+       //TODO: Implement for real sensors
+
+       DataPoint d;
+       //TODO: Deal with time stamps properly
+       static int count = 0;
+       d.time = count++;
+       switch (sensor_id)
+       {
+               case SENSOR_TEST0:
+                       d.value = count;
+                       break;
+               case SENSOR_TEST1:
+                       d.value = (float)(rand() % 100) / 100;
+                       break;
+               default:
+                       Fatal("Unknown sensor id: %d", sensor_id);
+                       break;
+       }       
+       usleep(100000); // simulate delay in sensor polling
+       return d;
+}
+
+
+/**
+ * Destroy a sensor
+ * @param s - Sensor to destroy
+ */
+void Destroy(Sensor * s)
+{
+       //TODO: Surely we'll need to do something here?
+       // Maybe move the binary file into long term file storage?
+}
+
+
+
+/**
+ * Initialise a sensor
+ * @param s - Sensor to initialise
+ */
+void Sensor_Init(Sensor * s, int id)
+{
+       s->write_index = 0;
+       s->read_offset = 0;
+       s->id = id;
+
+       if (s->id >= pow(10, FILENAMESIZE))
+       {
+               Fatal("Too many sensors! FILENAMESIZE is %d; increase it and recompile.", FILENAMESIZE);
+       }
+       sprintf(s->filename, "%d", s->id);
+       unlink(s->filename); //TODO: Move old files somewhere
+
+       Log(LOGDEBUG, "Initialised sensor %d; binary file is \"%s\"", id, s->filename);
+}
+
 
 /**
  * Run the main sensor polling loop
- * @param args - IGNORED (void* required to use the function with pthreads)
+ * @param arg - Cast to Sensor* - Sensor that the thread will handle
  * @returns NULL (void* required to use the function with pthreads)
  */
-void * Sensor_Main(void * args)
+void * Sensor_Main(void * arg)
 {
-       
+       Sensor * s = (Sensor*)(arg);
+
+       while (true) //TODO: Exit condition
+       {
+               // The sensor will write data to a buffer until it is full
+               // Then it will open a file and dump the buffer to the end of it.
+               // Rinse and repeat
+
+               // The reason I've added the buffer is because locks are expensive
+               // But maybe it's better to just write data straight to the file
+               // I'd like to do some tests by changing SENSOR_DATABUFSIZ
+
+               while (s->write_index < SENSOR_DATABUFSIZ)
+               {
+                       s->buffer[s->write_index] = GetData(s->id);
+                       s->write_index += 1;
+               }
+
+               //Log(LOGDEBUG, "Filled buffer");
+
+               // CRITICAL SECTION (no threads should be able to read/write the file at the same time)
+               pthread_mutex_lock(&(s->mutex));
+
+                       // Open binary file in append mode and dump buffer into it
+                       FILE * file = fopen(s->filename, "ab");
+                       if (file == NULL)
+                       {
+                               Fatal("Couldn't open file \"%s\" mode ab - %s", s->filename, strerror(errno));
+                       }
+                       int amount_written = fwrite(s->buffer, sizeof(DataPoint), SENSOR_DATABUFSIZ, file);
+                       if (amount_written != SENSOR_DATABUFSIZ)
+                       {
+                               Fatal("Wrote %d data points and expected to write %d to \"%s\" - %s", amount_written, SENSOR_DATABUFSIZ, strerror(errno));
+                       }
+
+                       Log(LOGDEBUG, "Wrote %d data points for sensor %d", amount_written, s->id);
+
+                       fclose(file);
+
+               pthread_mutex_unlock(&(s->mutex));
+               // End of critical section
+
+               s->write_index = 0; // reset position in buffer
+               
+       }
+       return NULL;
 }
+
+
index b9a5aad..e51ab0c 100644 (file)
@@ -9,9 +9,52 @@
 #ifndef _SENSOR_H
 #define _SENSOR_H
 
+#include "common.h"
+
+/** Number of data points to keep in sensor buffers **/
+#define SENSOR_DATABUFSIZ 10
+
+/** Number of sensors **/
+#define NUMSENSORS 1
+
+#define FILENAMESIZE 10
+
+/** Structure to represent data recorded by a sensor at an instant in time **/
+typedef struct
+{
+       /** Time at which data was taken **/
+       float time;
+       /** Value of data **/
+       float value;
+} DataPoint;
+
+/** Structure to represent a sensor **/
+typedef struct
+{
+       /** ID number of the sensor **/
+       enum {SENSOR_TEST0=0, SENSOR_TEST1=1} id;
+       /** Buffer to store data from the sensor **/
+       DataPoint buffer[SENSOR_DATABUFSIZ];
+       /** Index of last point written in the data buffer **/
+       int write_index;
+       /** Offset position in binary file for query thread to read from**/
+       int read_offset;
+       /** File to write data into when buffer is full **/
+       char filename[FILENAMESIZE];
+       /** Thread running the sensor **/
+       pthread_t thread;
+       /** Mutex to protect access to stuff **/
+       pthread_mutex_t mutex;
+
+       
+} Sensor;
+
+/** Array of Sensors **/
+extern Sensor g_sensors[];
+
+extern void Sensor_Init(Sensor * s, int id); // Initialise sensor
+extern void * Sensor_Main(void * args); // main loop for sensor thread; pass a Sensor* cast to void*
 
 
-extern void * Sensor_Main(void * args); // main loop for sensor thread
-
 #endif //_SENSOR_H
 

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