Add Actuator related code
authorSam Moore <[email protected]>
Fri, 13 Sep 2013 13:42:00 +0000 (21:42 +0800)
committerSam Moore <[email protected]>
Fri, 13 Sep 2013 13:42:00 +0000 (21:42 +0800)
Made generalised Data_Handler function to reduce copy-paste.
(So you can query an Actuator for what values it has been set to).

Still a lot of copy-paste going on though.

Also added a few reports while I'm at it.

I suspect I will have to merge this with stuff other people did?

19 files changed:
reports/week3/sam.pdf [new file with mode: 0644]
reports/week3/summary.pdf [new file with mode: 0644]
reports/week4/summary.pdf [new file with mode: 0644]
reports/week5/gui.png [new file with mode: 0644]
reports/week5/gui2.pdf [new file with mode: 0644]
reports/week5/gui2.png [new file with mode: 0644]
reports/week5/summary.pdf [new file with mode: 0644]
reports/week6/summary.pdf [new file with mode: 0644]
server/Makefile
server/actuator.c [new file with mode: 0644]
server/actuator.h [new file with mode: 0644]
server/control.c
server/control.h
server/data.c
server/data.h
server/fastcgi.c
server/main.c
server/sensor.c
server/valgrind.sh

diff --git a/reports/week3/sam.pdf b/reports/week3/sam.pdf
new file mode 100644 (file)
index 0000000..1122c95
Binary files /dev/null and b/reports/week3/sam.pdf differ
diff --git a/reports/week3/summary.pdf b/reports/week3/summary.pdf
new file mode 100644 (file)
index 0000000..06e0e82
Binary files /dev/null and b/reports/week3/summary.pdf differ
diff --git a/reports/week4/summary.pdf b/reports/week4/summary.pdf
new file mode 100644 (file)
index 0000000..a5547a0
Binary files /dev/null and b/reports/week4/summary.pdf differ
diff --git a/reports/week5/gui.png b/reports/week5/gui.png
new file mode 100644 (file)
index 0000000..5f1b170
Binary files /dev/null and b/reports/week5/gui.png differ
diff --git a/reports/week5/gui2.pdf b/reports/week5/gui2.pdf
new file mode 100644 (file)
index 0000000..fd7aa5a
Binary files /dev/null and b/reports/week5/gui2.pdf differ
diff --git a/reports/week5/gui2.png b/reports/week5/gui2.png
new file mode 100644 (file)
index 0000000..65cef7b
Binary files /dev/null and b/reports/week5/gui2.png differ
diff --git a/reports/week5/summary.pdf b/reports/week5/summary.pdf
new file mode 100644 (file)
index 0000000..7fd7382
Binary files /dev/null and b/reports/week5/summary.pdf differ
diff --git a/reports/week6/summary.pdf b/reports/week6/summary.pdf
new file mode 100644 (file)
index 0000000..cf6c40c
Binary files /dev/null and b/reports/week6/summary.pdf differ
index 74ba473..bb70c4f 100644 (file)
@@ -2,7 +2,7 @@
 CXX = gcc
 FLAGS = -std=c99 -Wall -Werror -pedantic -g
 LIB = -lfcgi -lssl -lcrypto -lpthread -lm
-OBJ = log.o control.o data.o fastcgi.o main.o sensor.o
+OBJ = log.o control.o data.o fastcgi.o main.o sensor.o actuator.o
 RM = rm -f
 
 BIN = server
diff --git a/server/actuator.c b/server/actuator.c
new file mode 100644 (file)
index 0000000..b3cb361
--- /dev/null
@@ -0,0 +1,278 @@
+/**
+ * @file actuator.c
+ * @purpose Implementation of Actuator related functionality
+ */
+
+#include "actuator.h"
+#include "options.h"
+
+/** Array of Actuators (global to this file) initialised by Actuator_Init **/
+static Actuator g_actuators[NUMACTUATORS];
+
+/** Human readable names for the Actuators **/
+const char * g_actuator_names[NUMACTUATORS] = {        
+       "actuator_test0", "actuator_test1"
+};
+
+/**
+ * One off initialisation of *all* Actuators
+ */
+void Actuator_Init()
+{
+       for (int i = 0; i < NUMACTUATORS; ++i)
+       {
+               g_actuators[i].id = i;
+               Data_Init(&(g_actuators[i].data_file));
+               pthread_mutex_init(&(g_actuators[i].mutex), NULL);
+       }
+}
+
+/**
+ * Start an Actuator
+ * @param a - The Actuator to start
+ * @param experiment_name - Prepended to DataFile filename
+ */
+void Actuator_Start(Actuator * a, const char * experiment_name)
+{
+       // Set filename
+       char filename[BUFSIZ];
+       if (sprintf(filename, "%s_a%d", experiment_name, a->id) >= BUFSIZ)
+       {
+               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;
+
+       // Create the thread
+       pthread_create(&(a->thread), NULL, Actuator_Loop, (void*)(a));
+}
+
+/**
+ * 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
+
+}
+
+/**
+ * Stop all Actuators
+ */
+void Actuator_StopAll()
+{
+       for (int i = 0; i < NUMACTUATORS; ++i)
+               Actuator_Stop(g_actuators+i);
+}
+
+/**
+ * Start all Actuators
+ */
+void Actuator_StartAll(const char * experiment_name)
+{
+       for (int i = 0; i < NUMACTUATORS; ++i)
+               Actuator_Start(g_actuators+i, experiment_name);
+}
+
+/**
+ * Actuator control thread
+ * @param arg - Cast to an Actuator*
+ * @returns NULL to keep pthreads happy
+ */
+void * Actuator_Loop(void * arg)
+{
+       Actuator * a = (Actuator*)(arg);
+       
+       // Loop until stopped
+       while (a->activated)
+       {
+               pthread_mutex_lock(&(a->mutex));
+               while (!a->control_changed)
+               {
+                       pthread_cond_wait(&(a->cond), &(a->mutex));
+               }
+               a->control_changed = false;
+               pthread_mutex_unlock(&(a->mutex));
+               if (!a->activated)
+                       break;
+
+               Actuator_SetValue(a, a->control.value);
+       }
+
+       //TODO: Cleanup?
+       
+       // Keep pthreads happy
+       return NULL;
+}
+
+/**
+ * Set an Actuators control variable
+ * @param a - Actuator to control 
+ * @param c - Control to set to
+ */
+void Actuator_SetControl(Actuator * a, ActuatorControl * c)
+{
+       pthread_mutex_lock(&(a->mutex));
+       if (c != NULL)
+               a->control = *c;
+       a->control_changed = true;
+       pthread_cond_broadcast(&(a->cond));
+       pthread_mutex_unlock(&(a->mutex));
+       
+}
+
+/**
+ * Set an Actuator value
+ * @param a - The Actuator
+ * @param value - The value to set
+ */
+void Actuator_SetValue(Actuator * a, double value)
+{
+       // Set time stamp
+       struct timeval t;
+       gettimeofday(&t, NULL);
+
+       DataPoint d = {TIMEVAL_DIFF(t, g_options.start_time), value};
+       //TODO: Set actuator
+       switch (a->id)
+       {
+               case ACTUATOR_TEST0:
+                       break;
+               case ACTUATOR_TEST1:
+                       break;
+       }
+
+       Log(LOGDEBUG, "Actuator %s set to %f", g_actuator_names[a->id], value);
+
+       // Record the value
+       Data_Save(&(a->data_file), &d, 1);
+}
+
+/**
+ * Helper: Begin Actuator response in a given format
+ * @param context - the FCGIContext
+ * @param format - Format
+ * @param id - ID of Actuator
+ */
+void Actuator_BeginResponse(FCGIContext * context, ActuatorId id, DataFormat format)
+{
+       // Begin response
+       switch (format)
+       {
+               case JSON:
+                       FCGI_BeginJSON(context, STATUS_OK);
+                       FCGI_JSONLong("id", id);
+                       break;
+               default:
+                       FCGI_PrintRaw("Content-type: text/plain\r\n\r\n");
+                       break;
+       }
+}
+
+/**
+ * Helper: End Actuator response in a given format
+ * @param context - the FCGIContext
+ * @param id - ID of the Actuator
+ * @param format - Format
+ */
+void Actuator_EndResponse(FCGIContext * context, ActuatorId id, DataFormat format)
+{
+       // End response
+       switch (format)
+       {
+               case JSON:
+                       FCGI_EndJSON();
+                       break;
+               default:
+                       break;
+       }
+}
+
+
+
+
+/**
+ * Handle a request for an Actuator
+ * @param context - FCGI context
+ * @param params - Parameters passed
+ */
+void Actuator_Handler(FCGIContext * context, char * params)
+{
+       struct timeval now;
+       gettimeofday(&now, NULL);
+       double current_time = TIMEVAL_DIFF(now, g_options.start_time);
+       int id = 0;
+       double set = 0;
+       double start_time = 0;
+       double end_time = current_time;
+       char * fmt_str;
+
+       // key/value pairs
+       FCGIValue values[] = {
+               {"id", &id, FCGI_REQUIRED(FCGI_LONG_T)}, 
+               {"set", &set, FCGI_DOUBLE_T},
+               {"start_time", &start_time, FCGI_DOUBLE_T},
+               {"end_time", &end_time, FCGI_DOUBLE_T},
+               {"format", &fmt_str, FCGI_STRING_T}
+       };
+
+       // enum to avoid the use of magic numbers
+       typedef enum {
+               ID,
+               SET,
+               START_TIME,
+               END_TIME,
+               FORMAT
+       } ActuatorParams;
+       
+       // Fill values appropriately
+       if (!FCGI_ParseRequest(context, params, values, sizeof(values)/sizeof(FCGIValue)))
+       {
+               // Error occured; FCGI_RejectJSON already called
+               return;
+       }       
+
+       // Get the Actuator identified
+       Actuator * a = NULL;
+       if (id < 0 || id >= NUMACTUATORS)
+       {
+               FCGI_RejectJSON(context, "Invalid Actuator id");
+               return;
+       }
+       
+       a = g_actuators+id;
+
+       DataFormat format = Data_GetFormat(&(values[FORMAT]));
+
+       // Begin response
+       Actuator_BeginResponse(context, id, format);
+
+       // Set?
+       if (FCGI_RECEIVED(values[SET].flags))
+       {
+               if (format == JSON)
+                       FCGI_JSONDouble("set", set);
+       
+               ActuatorControl c;
+               c.value = set;
+
+               Actuator_SetControl(a, &c);
+       }
+
+       // Print Data
+       Data_Handler(&(a->data_file), &(values[START_TIME]), &(values[END_TIME]), format, current_time);
+       
+       // Finish response
+       Actuator_EndResponse(context, id, format);
+}
diff --git a/server/actuator.h b/server/actuator.h
new file mode 100644 (file)
index 0000000..df946a0
--- /dev/null
@@ -0,0 +1,77 @@
+/**
+ * @file actuator.h
+ * @purpose Declarations for actuator control
+ */
+
+#ifndef _ACTUATOR_H
+#define _ACTUATOR_H
+
+#include "common.h"
+#include "data.h"
+
+//NOTE: Functionality is very similar to Sensor stuff
+//             BUT it's probably very unwise to try and generalise Sensors and Actuators to the same thing (ie: Device)
+//             Might be OK in C++ but not easy in C
+
+/** Number of actuators **/
+#define NUMACTUATORS 2
+
+/** List of actuator ids (should be of size NUMACTUATORS) **/
+typedef enum
+{
+       ACTUATOR_TEST0,
+       ACTUATOR_TEST1
+} ActuatorId;
+
+/** Human readable names for the Actuators **/
+extern const char * g_actuator_names[NUMACTUATORS];
+
+/** Control structure for Actuator setting **/
+typedef struct
+{
+       //TODO: Add functionality as needed
+       /** Simple value for Actuator **/
+       double value;
+} ActuatorControl;
+
+typedef struct
+{
+       /** ID number of the actuator **/
+       ActuatorId id;
+       /** Control parameters for the Actuator **/
+       ActuatorControl control;
+       /** Flag indicates if ActuatorControl has been changed **/
+       bool control_changed;
+       /** DataFile to store actuator settings **/
+       DataFile data_file;
+       /** Thread the Actuator is controlled by **/
+       pthread_t thread;
+       /** Mutex around ActuatorControl **/
+       pthread_mutex_t mutex;
+       /** Used to wake up Actuator control thread **/
+       pthread_cond_t cond;
+       /** Indicates whether the Actuator is running **/
+       bool activated;
+
+} 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_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
+extern void Actuator_SetControl(Actuator * a, ActuatorControl * c); // Set the control for an Actuator
+extern Actuator * Actuator_Identify(const char * str); // Identify a Sensor from a string Id
+
+extern void Actuator_Handler(FCGIContext *context, char * params); // Handle a FCGI request for Actuator control
+
+#endif //_ACTUATOR_H
+
+//EOF
index 4da1f75..1f33eea 100644 (file)
@@ -5,13 +5,10 @@
 #include "common.h"
 #include "control.h"
 
-const char * g_actuator_names[NUMACTUATORS] = {        
-       "Pressure regulator", "Solenoid 1" 
-};
 
 /**
  * Handles control of the actuators.
- */
+ *
 void ActuatorHandler(FCGIContext *context, ActuatorId id, const char *set_value) {
        char *ptr;
        
@@ -49,6 +46,7 @@ void ActuatorHandler(FCGIContext *context, ActuatorId id, const char *set_value)
                                STATUS_ERROR, "Invalid actuator id specified.");
        }
 }
+*/
 
 /**
  * System control handler. This covers control over all aspects of the system.
@@ -79,6 +77,7 @@ void Control_Handler(FCGIContext *context, char *params) {
                        set_value = value;
                }
        }
+       Log(LOGDEBUG, "Id %d", id); // to stop compiler complaining for now
        
        if (action == NULL) { //Must have an action
                FCGI_RejectJSON(context, "No action specified");
@@ -99,8 +98,9 @@ void Control_Handler(FCGIContext *context, char *params) {
                        if (set_value == NULL || *set_value == '\0') {
                                FCGI_RejectJSONEx(context, 
                                        STATUS_ERROR, "Set called but no value specified.");
-                       } else {
-                               ActuatorHandler(context, id, set_value);
+                       } else 
+                       {
+//                             ActuatorHandler(context, id, set_value);
                        }
                }
        }
index a1aa5c7..2de9b7f 100644 (file)
@@ -5,17 +5,7 @@
 #ifndef _CONTROL_H
 #define _CONTROL_H
 
-/** Number of actuators **/
-#define NUMACTUATORS 2
 
-/** List of actuator ids (should be of size NUMACTUATORS) **/
-typedef enum ActuatorId {
-       ACT_PRESSURE,
-       ACT_SOLENOID1
-} ActuatorId;
-
-/** Human readable names for the actuator ids **/
-extern const char * g_actuator_names[NUMACTUATORS];
 
 /** ID codes for all the actuators **/
 extern void Control_Handler(FCGIContext *context, char *params);
index 82aadb6..8bfd8b1 100644 (file)
@@ -16,6 +16,7 @@ void Data_Init(DataFile * df)
        df->filename = NULL;
        df->read_file = NULL;
        df->write_file = NULL;
+       pthread_mutex_init(&(df->mutex), NULL);
 }
 
 /**
@@ -35,7 +36,7 @@ void Data_Open(DataFile * df, const char * filename)
        df->num_points = 0; 
 
        // Set write FILE*
-       df->write_file = fopen(filename, "w+");
+       df->write_file = fopen(filename, "wb+");
        if (df->write_file == NULL)
        {
                Fatal("Error opening DataFile %s - %s", filename, strerror(errno));
@@ -47,7 +48,7 @@ void Data_Open(DataFile * df, const char * filename)
        //NOTE: Opening the same file in read mode gives funny results; fread generally reads less than expected
        // The strerror is: "Transport endpoint is not connected"
        /*
-       fopen(filename, "r");
+       df->read_file = fopen(filename, "rb");
        if (df->read_file == NULL)
        {
                Fatal("Error opening DataFile %s - %s", filename, strerror(errno));
@@ -318,3 +319,74 @@ int Data_FindByTime(DataFile * df, double time_stamp, DataPoint * closest)
        return index;
        
 }
+
+/**
+ * Helper; handle FCGI response that requires data
+ * Should be called first.
+ * @param df - DataFile to access
+ * @param start - Info about start_time param 
+ * @param end - Info about end_time param
+ * @param fmt - Info about format param
+ * @param current_time - Current time
+ */
+void Data_Handler(DataFile * df, FCGIValue * start, FCGIValue * end, DataFormat format, double current_time)
+{
+       double start_time = *(double*)(start->value);
+       double end_time = *(double*)(end->value);
+
+       if (format == JSON)
+       {
+               FCGI_JSONKey("data");
+       }
+
+       // If a time was specified
+       if (FCGI_RECEIVED(start->flags) || FCGI_RECEIVED(end->flags))
+       {
+               // Wrap times relative to the current time
+               if (start_time < 0)
+                       start_time += current_time;
+               if (end_time < 0)
+                       end_time += current_time;
+
+               // Print points by time range
+               Data_PrintByTimes(df, start_time, end_time, format);
+
+       }
+       else // No time was specified; just return a recent set of points
+       {
+               pthread_mutex_lock(&(df->mutex));
+                       int start_index = df->num_points-DATA_BUFSIZ;
+                       int end_index = df->num_points-1;
+               pthread_mutex_unlock(&(df->mutex));
+
+               // Bounds check
+               if (start_index < 0)
+                       start_index = 0;
+               if (end_index < 0)
+                       end_index = 0;
+
+               // Print points by indexes
+               Data_PrintByIndexes(df, start_index, end_index, format);
+       }
+
+}
+
+/**
+ * Helper - Convert human readable format string to DataFormat
+ * @param fmt - FCGIValue to use
+ */
+DataFormat Data_GetFormat(FCGIValue * fmt)
+{
+       char * fmt_str = *(char**)(fmt->value);
+       // Check if format type was specified
+       if (FCGI_RECEIVED(fmt->flags))
+       {
+               if (strcmp(fmt_str, "json") == 0)
+                       return JSON;
+               else if (strcmp(fmt_str, "tsv") == 0)
+                       return TSV;
+               else
+                       Log(LOGERR, "Unknown format type \"%s\"", fmt_str);
+       }
+       return JSON;
+}
index e52dadc..9731a2d 100644 (file)
@@ -51,4 +51,7 @@ extern void Data_PrintByIndexes(DataFile * df, int start_index, int end_index, D
 extern void Data_PrintByTimes(DataFile * df, double start_time, double end_time, DataFormat format); // Print data between time values
 extern int Data_FindByTime(DataFile * df, double time_stamp, DataPoint * closest); // Find index of DataPoint with the closest timestamp to that given
 
+extern void Data_Handler(DataFile * df, FCGIValue * start, FCGIValue * end, DataFormat format, double current_time); // Helper; given FCGI params print data
+extern DataFormat Data_GetFormat(FCGIValue * fmt); // Helper; convert human readable format string to DataFormat
+
 #endif //_DATAPOINT_H
index b58ba79..989d16a 100644 (file)
@@ -12,6 +12,7 @@
 
 #include "common.h"
 #include "sensor.h"
+#include "actuator.h"
 #include "control.h"
 #include "options.h"
 
@@ -464,6 +465,8 @@ void * FCGI_RequestLoop (void *data)
                        module_handler = Control_Handler;
                } else if (!strcmp("sensors", module)) {
                        module_handler = Sensor_Handler;
+               } else if (!strcmp("actuators", module)) {
+                       module_handler = Actuator_Handler;
                }
 
                context.current_module = module;
index d1aaa4c..6ea256c 100644 (file)
@@ -7,6 +7,7 @@
 #include "common.h"
 #include "options.h"
 #include "sensor.h"
+#include "actuator.h"
 
 // --- Standard headers --- //
 #include <signal.h> // for signal handling
@@ -76,12 +77,15 @@ int main(int argc, char ** argv)
        }
        */
        Sensor_Init();
+       Actuator_Init();
        Sensor_StartAll("test");
+       Actuator_StartAll("test");
 
        // run request thread in the main thread
        FCGI_RequestLoop(NULL);
 
        Sensor_StopAll();
+       Actuator_StopAll();
 
        Cleanup();
        return 0;
index 227ea42..55a9eaa 100644 (file)
@@ -40,7 +40,7 @@ void Sensor_Start(Sensor * s, const char * experiment_name)
 {
        // Set filename
        char filename[BUFSIZ];
-       if (sprintf(filename, "%s_%d", experiment_name, s->id) >= BUFSIZ)
+       if (sprintf(filename, "%s_s%d", experiment_name, s->id) >= BUFSIZ)
        {
                Fatal("Experiment name \"%s\" too long (>%d)", experiment_name, BUFSIZ);
        }
@@ -236,7 +236,6 @@ void Sensor_BeginResponse(FCGIContext * context, SensorId id, DataFormat format)
                case JSON:
                        FCGI_BeginJSON(context, STATUS_OK);
                        FCGI_JSONLong("id", id);
-                       FCGI_JSONKey("data");
                        break;
                default:
                        FCGI_PrintRaw("Content-type: text/plain\r\n\r\n");
@@ -302,67 +301,22 @@ void Sensor_Handler(FCGIContext *context, char * params)
                return;
        }
 
-       // Get Sensor
-       Sensor * s = NULL;
 
        // Error checking on sensor id
        if (id < 0 || id >= NUMSENSORS)
        {
-               Log(LOGERR, "Invalid id %d", id);
-       }
-       else
-       {
-               s = g_sensors+id;
+               FCGI_RejectJSON(context, "Invalid sensor id");
+               return;
        }
+       Sensor * s = g_sensors+id;
        
-       DataFormat format = JSON;
+       DataFormat format = Data_GetFormat(&(values[FORMAT]));
 
-       // Check if format type was specified
-       if (FCGI_RECEIVED(values[FORMAT].flags))
-       {
-               if (strcmp(fmt_str, "json") == 0)
-                       format = JSON;
-               else if (strcmp(fmt_str, "tsv") == 0)
-                       format = TSV;
-               else
-                       Log(LOGERR, "Unknown format type \"%s\"", fmt_str);
-       }
-
-       
-       
        // Begin response
        Sensor_BeginResponse(context, id, format);
-       
-       // If a time was specified
-       if ((s != NULL) && (FCGI_RECEIVED(values[START_TIME].flags) || FCGI_RECEIVED(values[END_TIME].flags)))
-       {
-               // Wrap times relative to the current time
-               if (start_time < 0)
-                       start_time += current_time;
-               if (end_time < 0)
-                       end_time += current_time;
-
-               // Print points by time range
-               Data_PrintByTimes(&(s->data_file), start_time, end_time, format);
 
-       }
-       else if (s != NULL) // No time was specified; just return a recent set of points
-       {
-               pthread_mutex_lock(&(s->data_file.mutex));
-                       int start_index = s->data_file.num_points-DATA_BUFSIZ;
-                       int end_index = s->data_file.num_points-1;
-               pthread_mutex_unlock(&(s->data_file.mutex));
-
-               // Bounds check
-               if (start_index < 0)
-                       start_index = 0;
-               if (end_index < 0)
-                       end_index = 0;
-
-               // Print points by indexes
-               Log(LOGDEBUG, "Sensor %d file \"%s\" indexes %d->%d", s->id, s->data_file.filename, start_index, end_index);
-               Data_PrintByIndexes(&(s->data_file), start_index, end_index, format);
-       }
+       // Print Data
+       Data_Handler(&(s->data_file), &(values[START_TIME]), &(values[END_TIME]), format, current_time);
        
        // Finish response
        Sensor_EndResponse(context, id, format);
index 3262b72..062c490 100755 (executable)
@@ -1,2 +1,2 @@
 #!/bin/bash
-valgrind --leak-check=full --track-origins=yes ./server
+valgrind --leak-check=full --track-origins=yes --show-reachable=yes ./server

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