From: Justin Kruger <20767264@student.uwa.edu.au> Date: Fri, 16 Aug 2013 08:46:41 +0000 (+0800) Subject: Merge remote-tracking branch 'upstream/master' X-Git-Url: https://git.ucc.asn.au/?a=commitdiff_plain;h=0b95dfaf594f4e6866ff8e18040c62382cad7d08;hp=542d6b25bd51894204002803f2a3a6b2ee9236c8;p=matches%2FMCTX3420.git Merge remote-tracking branch 'upstream/master' --- diff --git a/irc/log b/irc/log index 103a54c..c3d0aca 100644 --- a/irc/log +++ b/irc/log @@ -866,3 +866,123 @@ 21:26 -!- jtanx [~asfa@124-169-120-181.dyn.iinet.net.au] has quit ["ChatZilla 0.9.89 [Firefox 22.0/20130618035212]"] 21:34 -!- jtanx [~asfa@124-169-120-181.dyn.iinet.net.au] has joined #mctxuwa_softdev 22:54 -!- jtanx [~asfa@124-169-120-181.dyn.iinet.net.au] has quit ["ChatZilla 0.9.89 [Firefox 22.0/20130618035212]"] +--- Day changed Thu Aug 15 2013 +01:20 -!- justin_kruger [~justinkru@125.253.101.228] has joined #mctxuwa_softdev +01:20 -!- justin_kruger [~justinkru@125.253.101.228] has quit [EOF From client] +07:58 -!- jtanx [~asfa@124-169-120-181.dyn.iinet.net.au] 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 [~asfa@124-169-120-181.dyn.iinet.net.au] has quit ["ChatZilla 0.9.89 [Firefox 22.0/20130618035212]"] +13:33 -!- jtanx [~asfa@124-169-120-181.dyn.iinet.net.au] 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 [~asfa@124-169-120-181.dyn.iinet.net.au] has quit ["ChatZilla 0.9.89 [Firefox 22.0/20130618035212]"] +21:34 -!- jtanx [~asfa@124-169-120-181.dyn.iinet.net.au] 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 [~asfa@124-169-120-181.dyn.iinet.net.au] has quit ["ChatZilla 0.9.89 [Firefox 22.0/20130618035212]"] diff --git a/server/Makefile b/server/Makefile index 196dfa1..009128b 100644 --- a/server/Makefile +++ b/server/Makefile @@ -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 index 0000000..2a6184a --- /dev/null +++ b/server/common.h @@ -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 +#include +#include +#include +#include +#include +#include + +#endif //_COMMON_H diff --git a/server/log.h b/server/log.h index 6db52f6..005059c 100644 --- a/server/log.h +++ b/server/log.h @@ -10,6 +10,7 @@ #include #include #include +#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__) diff --git a/server/main.c b/server/main.c index 61ba76d..5e2c6c8 100644 --- a/server/main.c +++ b/server/main.c @@ -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 -#include #include // for signal handling -#include // string functions -#include // --- 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 index 0000000..8499e37 --- /dev/null +++ b/server/query.c @@ -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 index 0000000..55a4991 --- /dev/null +++ b/server/query.h @@ -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 diff --git a/server/sensor.c b/server/sensor.c index d1d8b9f..a3d3f0b 100644 --- a/server/sensor.c +++ b/server/sensor.c @@ -4,14 +4,130 @@ * TODO: Finalise implementation */ + + #include "sensor.h" +#include "log.h" +#include + +/** + * 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; } + + diff --git a/server/sensor.h b/server/sensor.h index b9a5aad..e51ab0c 100644 --- a/server/sensor.h +++ b/server/sensor.h @@ -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