21:26 -!- jtanx [
[email protected]] has quit ["ChatZilla 0.9.89 [Firefox 22.0/20130618035212]"]
22:54 -!- jtanx [
[email protected]] has quit ["ChatZilla 0.9.89 [Firefox 22.0/20130618035212]"]
+--- Day changed Thu Aug 15 2013
+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: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]"]
+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]"]
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
--- /dev/null
+/**
+ * @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
#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__)
* @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 --- //
* 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);
}
/**
*/
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;
}
--- /dev/null
+/**
+ * @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);
+ }
+}
--- /dev/null
+/**
+ * @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
* 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;
}
+
+
#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