Progress on Framework for Server software
authorSam Moore <[email protected]>
Thu, 15 Aug 2013 17:28:47 +0000 (01:28 +0800)
committerSam Moore <[email protected]>
Thu, 15 Aug 2013 17:28:47 +0000 (01:28 +0800)
We have:
 - Threaded stuff for sensors that stores data in a buffer and periodically dumps it to a binary file
   - The use of the buffer reduces the number of times the mutex must be acquired (mutex = expensive)
   - Will do some tests to see if the buffer improves performance
 - Dummy function for querying sensors
 - Sensor Handler function (QuerySensor) gets data from binary file, dumps to a buffer and then prints

There is something not quite right; QuerySensor appears to get the most recent dump that the sensor thread made.
eg: In the current version, dumps are made twice as often as queries, so you miss half the data.

Should be able to fix this after more sleep...

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

index 196dfa1..95cd03e 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 test_request.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..524f550 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 "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_Request(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..7a9ab16
--- /dev/null
@@ -0,0 +1,82 @@
+/**
+ * @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 "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, 0, SEEK_SET);
+                       rewind(file);
+                       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..f1ec5dc 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 and dump buffer into it
+                       FILE * file = fopen(s->filename, "wb");
+                       if (file == NULL)
+                       {
+                               Fatal("Couldn't open file \"%s\" mode wb - %s", s->filename, strerror(errno));
+                       }
+                       fseek(file, 0, SEEK_END);
+                       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