From: Sam Moore Date: Mon, 21 Oct 2013 09:36:46 +0000 (+0800) Subject: Fix everything X-Git-Url: https://git.ucc.asn.au/?a=commitdiff_plain;h=edb6df04611c10dca29799a1e08096bd4ca28303;p=matches%2FMCTX3420.git Fix everything Jeremy helped* * He did most of it. --- diff --git a/server/actuator.c b/server/actuator.c index 1c105db..617136e 100644 --- a/server/actuator.c +++ b/server/actuator.c @@ -35,7 +35,7 @@ int Actuator_Add(const char * name, int user_id, SetFn set, InitFn init, CleanFn a->init = init; // Set init function a->sanity = sanity; - + a->cleanup = cleanup; pthread_mutex_init(&(a->mutex), NULL); if (init != NULL) @@ -51,16 +51,35 @@ int Actuator_Add(const char * name, int user_id, SetFn set, InitFn init, CleanFn /** - * One off initialisation of *all* Actuators + * Initialisation of *all* Actuators */ -#include "actuators/ledtest.h" -#include "actuators/filetest.h" +#include "actuators/pregulator.h" +#include "actuators/relays.h" void Actuator_Init() { //Actuator_Add("ledtest",0, Ledtest_Set, NULL,NULL,NULL); - Actuator_Add("filetest", 0, Filetest_Set, Filetest_Init, Filetest_Cleanup, Filetest_Sanity, 0); + //Actuator_Add("filetest", 0, Filetest_Set, Filetest_Init, Filetest_Cleanup, Filetest_Sanity, 0); + Actuator_Add("pregulator", 0, Pregulator_Set, Pregulator_Init, Pregulator_Cleanup, Pregulator_Sanity, 0); + Actuator_Add("can_select", RELAY_CANSELECT, Relay_Set, Relay_Init, Relay_Cleanup, Relay_Sanity, 0); + Actuator_Add("can_enable", RELAY_CANENABLE, Relay_Set, Relay_Init, Relay_Cleanup, Relay_Sanity, 0); + Actuator_Add("main_pressure", RELAY_MAIN, Relay_Set, Relay_Init, Relay_Cleanup, Relay_Sanity, 1); +} + +/** + * Deinitialise actuators + */ +void Actuator_Cleanup() +{ + for (int i = 0; i < g_num_actuators; ++i) + { + Actuator * a = g_actuators+i; + if (a->cleanup != NULL) + a->cleanup(a->user_id); + } + g_num_actuators = 0; } + /** * Sets the actuator to the desired mode. No checks are * done to see if setting to the desired mode will conflict with @@ -143,8 +162,12 @@ void Actuator_SetMode(Actuator * a, ControlModes mode, void *arg) */ void Actuator_SetModeAll(ControlModes mode, void * arg) { + if (mode == CONTROL_START) + Actuator_Init(); for (int i = 0; i < g_num_actuators; i++) Actuator_SetMode(&g_actuators[i], mode, arg); + if (mode == CONTROL_STOP) + Actuator_Cleanup(); } /** @@ -173,7 +196,7 @@ void * Actuator_Loop(void * arg) // Currently does discrete steps after specified time intervals struct timespec wait; - DOUBLE_TO_TIMEVAL(a->control.stepsize, &wait); + DOUBLE_TO_TIMEVAL(a->control.stepwait, &wait); while (!a->control_changed && a->control.steps > 0 && a->activated) { clock_nanosleep(CLOCK_MONOTONIC, 0, &wait, NULL); @@ -381,7 +404,7 @@ void Actuator_Handler(FCGIContext * context, char * params) ActuatorControl c = {0.0, 0.0, 0.0, 0}; // Need to set default values (since we don't require them all) // sscanf returns the number of fields successfully read... - int n = sscanf(set, "%lf,%lf,%lf,%d", &(c.start), &(c.stepwait), &(c.stepsize), &(c.steps)); // Set provided values in order + int n = sscanf(set, "%lf_%lf_%lf_%d", &(c.start), &(c.stepwait), &(c.stepsize), &(c.steps)); // Set provided values in order if (n != 4) { // If the user doesn't provide all 4 values, the Actuator will get set *once* using the first of the provided values diff --git a/server/actuator.h b/server/actuator.h index f818526..a5ac75b 100644 --- a/server/actuator.h +++ b/server/actuator.h @@ -67,13 +67,14 @@ typedef struct /** Sanity check function **/ SanityFn sanity; /** Cleanup function **/ - CleanFn clean; + CleanFn cleanup; /** Last setting **/ DataPoint last_setting; } Actuator; extern void Actuator_Init(); // One off initialisation of *all* Actuators +extern void Actuator_Cleanup(); extern void Actuator_SetModeAll(ControlModes mode, void *arg); extern void Actuator_SetMode(Actuator * a, ControlModes mode, void *arg); diff --git a/server/actuators/Makefile b/server/actuators/Makefile index a2ba5dc..dfc247c 100644 --- a/server/actuators/Makefile +++ b/server/actuators/Makefile @@ -3,7 +3,7 @@ CXX = gcc FLAGS = -std=c99 -Wall -pedantic -g -I../ #-I/usr/include/opencv -I/usr/include/opencv2/highgui For OpenCV LIB = -lpthread -OBJ = ledtest.o filetest.o +OBJ = relays.o pregulator.o HEADERS = $(wildcard *.h) RM = rm -f diff --git a/server/actuators/pregulator.c b/server/actuators/pregulator.c new file mode 100644 index 0000000..f93c407 --- /dev/null +++ b/server/actuators/pregulator.c @@ -0,0 +1,32 @@ +#include "pregulator.h" +#include "../bbb_pin.h" + +#define PREGULATOR_PWM ECAP0 +#define PREGULATOR_PERIOD 16666667 + +/** + * Initiliase the pressure regulator + */ +bool Pregulator_Init(const char * name, int id) +{ + return PWM_Export(PREGULATOR_PWM) && PWM_Set(PREGULATOR_PWM, false, PREGULATOR_PERIOD, 0); +} + +bool Pregulator_Cleanup(int id) +{ + if (!PWM_Set(PREGULATOR_PWM, false, PREGULATOR_PERIOD, 0)) + return false; + PWM_Unexport(PREGULATOR_PWM); + return true; +} + +bool Pregulator_Set(int id, double value) +{ + return PWM_Set(PREGULATOR_PWM, false, PREGULATOR_PERIOD, value*(PREGULATOR_PERIOD)); +} + +bool Pregulator_Sanity(int id, double value) +{ + return (value >= 0.0 && value <= 1.0); +} + diff --git a/server/actuators/pregulator.h b/server/actuators/pregulator.h new file mode 100644 index 0000000..c721140 --- /dev/null +++ b/server/actuators/pregulator.h @@ -0,0 +1,11 @@ +#ifndef _PREGULATOR + +#include "../common.h" + +extern bool Pregulator_Init(const char * name, int id); +extern bool Pregulator_Cleanup(int id); +extern bool Pregulator_Set(int id, double value); +extern bool Pregulator_Sanity(int id, double value); + + +#endif //_PREGULATOR diff --git a/server/actuators/relays.c b/server/actuators/relays.c new file mode 100644 index 0000000..dfa3d74 --- /dev/null +++ b/server/actuators/relays.c @@ -0,0 +1,48 @@ +#include "relays.h" +#include "../log.h" + +#include "../bbb_pin.h" + + +static int GetGPIO(int id) +{ + switch (id) + { + case RELAY_CANSELECT: + return 14; + case RELAY_CANENABLE: + return 115; + case RELAY_MAIN: + return 112; + } + Fatal("Unknown id %d", id); + return 0; +} + +bool Relay_Init(const char * name, int id) +{ + if (!GPIO_Export(GetGPIO(id))) + return false; + return GPIO_Set(GetGPIO(id), false); +} + +bool Relay_Cleanup(int id) +{ + bool err = GPIO_Set(GetGPIO(id), false); + GPIO_Unexport(GetGPIO(id)); + return err; +} + +bool Relay_Set(int id, double value) +{ + bool set = (bool)value; + return GPIO_Set(GetGPIO(id), set); +} + +bool Relay_Sanity(int id, double value) +{ + //bool set = (bool)value; + //TODO: Make a more sane sanity check + return true; + +} diff --git a/server/actuators/relays.h b/server/actuators/relays.h new file mode 100644 index 0000000..dcffbf9 --- /dev/null +++ b/server/actuators/relays.h @@ -0,0 +1,18 @@ +#ifndef _RELAY_H +#define _RELAY_H + +#include "../common.h" + +typedef enum +{ + RELAY_CANSELECT, + RELAY_CANENABLE, + RELAY_MAIN +} RelayID; + +extern bool Relay_Init(const char * name, int id); +extern bool Relay_Set(int id, double value); +extern bool Relay_Sanity(int id, double value); +extern bool Relay_Cleanup(int id); + +#endif //_RELAY_H diff --git a/server/bbb_pin.c b/server/bbb_pin.c index 2ffc9e5..bd31365 100644 --- a/server/bbb_pin.c +++ b/server/bbb_pin.c @@ -501,7 +501,8 @@ bool ADC_Read(int id, int *value) if (pread(g_adc[id].fd_value, adc_str, ADC_DIGITS-1, 0) == -1) { - AbortBool("ADC %d read failed: %s", id, strerror(errno)); + //AbortBool("ADC %d read failed: %s", id, strerror(errno)); + return false; } *value = strtol(adc_str, NULL, 10); diff --git a/server/control.h b/server/control.h index 7081c3f..0dfdc47 100644 --- a/server/control.h +++ b/server/control.h @@ -18,6 +18,8 @@ typedef enum ControlModes { /** The same as INVALID_CHARACTERS, except escaped for use in JSON strings **/ #define INVALID_CHARACTERS_JSON "\\\"*/:<>?\\\\|. " +#define NOAUTH_USERNAME "_anonymous_noauth" + extern void Control_Handler(FCGIContext *context, char *params); extern const char* Control_SetMode(ControlModes desired_mode, void * arg); extern ControlModes Control_GetMode(); diff --git a/server/fastcgi.c b/server/fastcgi.c index af92efd..75863c1 100644 --- a/server/fastcgi.c +++ b/server/fastcgi.c @@ -46,8 +46,7 @@ static void IdentifyHandler(FCGIContext *context, char *params) FCGI_BeginJSON(context, STATUS_OK); FCGI_JSONPair("description", "MCTX3420 Server API (2013)"); FCGI_JSONPair("build_date", __DATE__ " " __TIME__); - struct timespec t; - t.tv_sec = 0; t.tv_nsec = 0; + struct timespec t = {0}; clock_getres(CLOCK_MONOTONIC, &t); FCGI_JSONDouble("clock_getres", TIMEVAL_TO_DOUBLE(t)); FCGI_JSONLong("api_version", API_VERSION); @@ -65,7 +64,9 @@ static void IdentifyHandler(FCGIContext *context, char *params) if (i > 0) { FCGI_JSONValue(",\n\t\t"); } - FCGI_JSONValue("\"%d\" : \"%s\"", i, Sensor_GetName(i)); + DataPoint d = Sensor_LastData(i); + FCGI_JSONValue("\"%d\" : {\"name\" : \"%s\", \"value\" : [%f,%f] }", + i, Sensor_GetName(i), d.time_stamp, d.value); } FCGI_JSONValue("\n\t}"); } @@ -76,7 +77,9 @@ static void IdentifyHandler(FCGIContext *context, char *params) if (i > 0) { FCGI_JSONValue(",\n\t\t"); } - FCGI_JSONValue("\"%d\" : \"%s\"", i, Actuator_GetName(i)); + + DataPoint d = Sensor_LastData(i); + FCGI_JSONValue("\"%d\" : {\"name\" : \"%s\", \"value\" : [%f, %f] }", i, Actuator_GetName(i), d.time_stamp, d.value); } FCGI_JSONValue("\n\t}"); } @@ -591,13 +594,22 @@ void * FCGI_RequestLoop (void *data) if (module_handler) { - if (g_options.auth_method != AUTH_NONE && module_handler != Login_Handler && module_handler != IdentifyHandler && module_handler) + if (module_handler != Login_Handler && module_handler != IdentifyHandler && module_handler) //if (false) // Testing { if (!FCGI_HasControl(&context)) { - FCGI_RejectJSON(&context, "Please login. Invalid control key."); - continue; + if (g_options.auth_method == AUTH_NONE) + { //:( + Log(LOGWARN, "Locking control (no auth!)"); + FCGI_LockControl(&context, NOAUTH_USERNAME, USER_ADMIN); + FCGI_SendControlCookie(&context, true); + } + else + { + FCGI_RejectJSON(&context, "Please login. Invalid control key."); + continue; + } } //Escape all special characters. diff --git a/server/main.c b/server/main.c index fadcd3a..a4b7c5a 100644 --- a/server/main.c +++ b/server/main.c @@ -123,7 +123,7 @@ void Cleanup() { Log(LOGDEBUG, "Begin cleanup."); Sensor_Cleanup(); - //Actuator_Cleanup(); + Actuator_Cleanup(); Log(LOGDEBUG, "Finish cleanup."); } @@ -199,23 +199,22 @@ int main(int argc, char ** argv) - Sensor_Init(); - Actuator_Init(); Pin_Init(); // Try and start things - const char *ret; - if ((ret = Control_SetMode(CONTROL_START, "test")) != NULL) - Fatal("Control_SetMode failed with '%s'", ret); + //const char *ret; + //if ((ret = Control_SetMode(CONTROL_START, "test")) != NULL) + // Fatal("Control_SetMode failed with '%s'", ret); // run request thread in the main thread FCGI_RequestLoop(NULL); - if ((ret = Control_SetMode(CONTROL_STOP, "test")) != NULL) - Fatal("Control_SetMode failed with '%s'", ret); + Control_SetMode(CONTROL_STOP, NULL); + //if ((ret = Control_SetMode(CONTROL_STOP, "test")) != NULL) + // Fatal("Control_SetMode failed with '%s'", ret); //Sensor_StopAll(); //Actuator_StopAll(); diff --git a/server/parameters b/server/parameters index da48239..26e2892 100644 --- a/server/parameters +++ b/server/parameters @@ -23,7 +23,7 @@ pin_test="0" #auth_uri="ldap://192.168.1.1" #auth_uri="ldaps://ldap.pheme.uwa.edu.au" #UWA #auth_uri="/etc/shadow" -auth_uri="shadow" +#auth_uri="shadow" # Set to the dn of the LDAP server ldap_base_dn="ou=People,dc=daedalus" # Testing @@ -31,4 +31,5 @@ ldap_base_dn="ou=People,dc=daedalus" # Testing ## OPTIONS TO BE PASSED TO SERVER; DO NOT EDIT -parameters="-v $verbosity -p $pin_test -A $auth_uri -d $ldap_base_dn" +parameters="-v $verbosity -p $pin_test" +# -A $auth_uri -d $ldap_base_dn" diff --git a/server/sensor.c b/server/sensor.c index ae5cdf4..d146905 100644 --- a/server/sensor.c +++ b/server/sensor.c @@ -47,7 +47,7 @@ int Sensor_Add(const char * name, int user_id, ReadFn read, InitFn init, CleanFn s->init = init; // Set init function // Start by averaging values taken over a second - DOUBLE_TO_TIMEVAL(1e-4, &(s->sample_time)); + DOUBLE_TO_TIMEVAL(1e-3, &(s->sample_time)); s->averages = 1; s->num_read = 0; @@ -62,6 +62,8 @@ int Sensor_Add(const char * name, int user_id, ReadFn read, InitFn init, CleanFn s->current_data.time_stamp = 0; s->current_data.value = 0; + s->averaged_data.time_stamp = 0; + s->averaged_data.value = 0; return g_num_sensors; } @@ -75,16 +77,16 @@ int Sensor_Add(const char * name, int user_id, ReadFn read, InitFn init, CleanFn #include "sensors/pressure.h" void Sensor_Init() { - Sensor_Add("cpu_stime", RESOURCE_CPU_SYS, Resource_Read, NULL, NULL, NULL); - Sensor_Add("cpu_utime", RESOURCE_CPU_USER, Resource_Read, NULL, NULL, NULL); - //Sensor_Add("pressure_high0", PRES_HIGH0, Pressure_Read, Pressure_Init, Pressure_Cleanup, NULL); - //Sensor_Add("pressure_high1", PRES_HIGH1, Pressure_Read, Pressure_Init, Pressure_Cleanup, NULL); - //Sensor_Add("pressure_low0", PRES_LOW0, Pressure_Read, Pressure_Init, Pressure_Cleanup, NULL); + //Sensor_Add("cpu_stime", RESOURCE_CPU_SYS, Resource_Read, NULL, NULL, NULL); + //Sensor_Add("cpu_utime", RESOURCE_CPU_USER, Resource_Read, NULL, NULL, NULL); + Sensor_Add("pressure_high0", PRES_HIGH0, Pressure_Read, Pressure_Init, Pressure_Cleanup, NULL); + Sensor_Add("pressure_high1", PRES_HIGH1, Pressure_Read, Pressure_Init, Pressure_Cleanup, NULL); + Sensor_Add("pressure_low0", PRES_LOW0, Pressure_Read, Pressure_Init, Pressure_Cleanup, NULL); //Sensor_Add("../testing/count.py", 0, Piped_Read, Piped_Init, Piped_Cleanup, 1e50,-1e50,1e50,-1e50); - //Sensor_Add("strain0", STRAIN0, Strain_Read, Strain_Init, 5000,0,5000,0); - //Sensor_Add("strain1", STRAIN1, Strain_Read, Strain_Init, 5000,0,5000,0); - //Sensor_Add("strain2", STRAIN2, Strain_Read, Strain_Init, 5000,0,5000,0); - //Sensor_Add("strain3", STRAIN3, Strain_Read, Strain_Init, 5000,0,5000,0); + Sensor_Add("strain0", STRAIN0, Strain_Read, Strain_Init, Strain_Cleanup, Strain_Sanity); + Sensor_Add("strain1", STRAIN1, Strain_Read, Strain_Init, Strain_Cleanup, Strain_Sanity); + Sensor_Add("strain2", STRAIN2, Strain_Read, Strain_Init, Strain_Cleanup, Strain_Sanity); + Sensor_Add("strain3", STRAIN3, Strain_Read, Strain_Init, Strain_Cleanup, Strain_Sanity); //Sensor_Add("pressure0", PRESSURE0, Pressure_Read, Pressure_Init, 5000,0,5000,0); //Sensor_Add("pressure1", PRESSURE1, Pressure_Read, Pressure_Init, 5000,0,5000,0); //Sensor_Add("pressure_feedback", PRESSURE_FEEDBACK, Pressure_Read, Pressure_Init, 5000,0,5000,0); @@ -103,6 +105,7 @@ void Sensor_Cleanup() if (s->cleanup != NULL) s->cleanup(s->user_id); } + g_num_sensors = 0; } /** @@ -183,8 +186,12 @@ void Sensor_SetMode(Sensor * s, ControlModes mode, void * arg) */ void Sensor_SetModeAll(ControlModes mode, void * arg) { + if (mode == CONTROL_START) + Sensor_Init(); for (int i = 0; i < g_num_sensors; i++) Sensor_SetMode(&g_sensors[i], mode, arg); + if (mode == CONTROL_STOP) + Sensor_Cleanup(); } @@ -201,38 +208,40 @@ void * Sensor_Loop(void * arg) // Until the sensor is stopped, record data points while (s->activated) { - DataPoint d; - d.value = 0; - bool success = s->read(s->user_id, &(d.value)); + + bool success = s->read(s->user_id, &(s->current_data.value)); struct timespec t; clock_gettime(CLOCK_MONOTONIC, &t); - d.time_stamp = TIMEVAL_DIFF(t, *Control_GetStartTime()); + s->current_data.time_stamp = TIMEVAL_DIFF(t, *Control_GetStartTime()); if (success) { if (s->sanity != NULL) { - if (!s->sanity(s->user_id, d.value)) + if (!s->sanity(s->user_id, s->current_data.value)) { Fatal("Sensor %s (%d,%d) reads unsafe value", s->name, s->id, s->user_id); } } - s->current_data.time_stamp += d.time_stamp; - s->current_data.value += d.value; + s->averaged_data.time_stamp += s->current_data.time_stamp; + s->averaged_data.value = s->current_data.value; if (++(s->num_read) >= s->averages) { - s->current_data.time_stamp /= s->averages; - s->current_data.value /= s->averages; - Data_Save(&(s->data_file), &(s->current_data), 1); // Record it + s->averaged_data.time_stamp /= s->averages; + s->averaged_data.value /= s->averages; + Data_Save(&(s->data_file), &(s->averaged_data), 1); // Record it s->num_read = 0; - s->current_data.time_stamp = 0; - s->current_data.value = 0; + s->averaged_data.time_stamp = 0; + s->averaged_data.value = 0; } } else - Log(LOGWARN, "Failed to read sensor %s (%d,%d)", s->name, s->id,s->user_id); + { + // Silence because strain sensors fail ~50% of the time :S + //Log(LOGWARN, "Failed to read sensor %s (%d,%d)", s->name, s->id,s->user_id); + } clock_nanosleep(CLOCK_MONOTONIC, 0, &(s->sample_time), NULL); @@ -408,5 +417,10 @@ const char * Sensor_GetName(int id) return g_sensors[id].name; } +DataPoint Sensor_LastData(int id) +{ + Sensor * s = &(g_sensors[id]); + return s->current_data; +} diff --git a/server/sensor.h b/server/sensor.h index 0188361..3de48e0 100644 --- a/server/sensor.h +++ b/server/sensor.h @@ -61,6 +61,9 @@ typedef struct int averages; /** Current data **/ DataPoint current_data; + + /** Summed data **/ + DataPoint averaged_data; /** Number of points read so far before applying average **/ int num_read; @@ -82,6 +85,8 @@ extern Sensor * Sensor_Identify(const char * str); // Identify a Sensor from a s extern void Sensor_Handler(FCGIContext *context, char * params); // Handle a FCGI request for Sensor data +extern DataPoint Sensor_LastData(int id); + extern const char * Sensor_GetName(int id); diff --git a/server/sensors/pressure.c b/server/sensors/pressure.c index 61cdbcf..29b8d28 100644 --- a/server/sensors/pressure.c +++ b/server/sensors/pressure.c @@ -40,6 +40,9 @@ double Pressure_Callibrate(int id, int adc) { //double voltage = ADC_TO_VOLTS(adc); // convert reading to voltage + return (double)adc; + + //TODO: Fix this switch (id) { case PRES_HIGH0: diff --git a/server/sensors/strain.c b/server/sensors/strain.c index 9492de7..4278d6c 100644 --- a/server/sensors/strain.c +++ b/server/sensors/strain.c @@ -68,6 +68,24 @@ bool Strain_Init(const char * name, int id) return true; } +bool Strain_Cleanup(int id) +{ + static bool kill_adc = false; + if (!kill_adc) + { + kill_adc = true; + ADC_Unexport(STRAIN_ADC); + } + int gpio_num = Strain_To_GPIO(id); + GPIO_Unexport(gpio_num); + return true; +} + +bool Strain_Sanity(int id, double value) +{ + return true; +} + /** * Read from a Strain gauge * @param id - The strain gauge to read diff --git a/server/sensors/strain.h b/server/sensors/strain.h index 1afb625..21b926e 100644 --- a/server/sensors/strain.h +++ b/server/sensors/strain.h @@ -19,4 +19,7 @@ extern bool Strain_Init(const char * name, int id); // Read from a strain gauge extern bool Strain_Read(int id, double * value); +extern bool Strain_Cleanup(int id); +extern bool Strain_Sanity(int id, double value); + #endif //_STRAIN_H diff --git a/testing/MCTXWeb/public_html/control.html b/testing/MCTXWeb/public_html/control.html index 412f7f8..7d7b9ad 100644 --- a/testing/MCTXWeb/public_html/control.html +++ b/testing/MCTXWeb/public_html/control.html @@ -1,258 +1,256 @@ - - - - MCTX3420 Web Interface - - - - - - - - - - - - - -
- -
- - -
- - -
- - - - -
-
-
Experiment Overview
- - - - - - -
Experiment state - -
-
-   -
- -
Error and warning messages
- -
- -
-
Start an experiment
-
-

- - - - - -

-

-   -

-

- - -

-
-
- -
-
- - - - - - - - - - - - - - - - - -
-

-   -

-

- -

-
- -
- - - - - - - - - -
-

-   -

-

- -

-
-
- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Link
-
-
- -
- -
-
- - + + + + MCTX3420 Web Interface + + + + + + + + + + + + + +
+ +
+ + +
+ + +
+ + + + +
+
+
Experiment Overview
+ + + + + + +
Experiment state + +
+
+   +
+ +
Error and warning messages
+ +
+ +
+
Start an experiment
+
+

+ + + + + +

+

+   +

+

+ + +

+
+
+ +
+
+ + + + + + + + + + + + + + + + + +
+

+   +

+

+ +

+
+ +
+ + + + + + + + + +
+

+   +

+

+ +

+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Link
+
+
+ +
+ +
+
+ + diff --git a/testing/MCTXWeb/public_html/static/mctx.control.js b/testing/MCTXWeb/public_html/static/mctx.control.js index 1d5c744..de45e61 100644 --- a/testing/MCTXWeb/public_html/static/mctx.control.js +++ b/testing/MCTXWeb/public_html/static/mctx.control.js @@ -47,6 +47,7 @@ function setSampleRate(id, val, result) { $.fn.loadSensorList = function (result, input) { var select = this; + select.empty(); //Reset list $.ajax({ url : mctx.api + 'identify', @@ -56,7 +57,7 @@ $.fn.loadSensorList = function (result, input) { return; } for (var id in data.sensors) { - var option = $("