Erick / Mbed 2 deprecated ICE-F412

Dependencies:   mbed-rtos mbed

Revision:
0:61364762ee0e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ICE-Application/src/ConfigurationHandler/ConfigurationHandler.cpp	Tue Jan 24 19:05:33 2017 +0000
@@ -0,0 +1,733 @@
+/******************************************************************************
+ *
+ * File:                ConfigurationHandler.cpp
+ * Desciption:          source for the ICE Configuration Handler
+ *
+ *****************************************************************************/
+#include "ConfigurationHandler.h"
+#include "global.h"
+#include "SetpointControl.h"
+#include "TimerControl.h"
+#include "CompositeControl.h"
+#include "CompositeAlgorithm.h"
+#include "SensorErrorControl.h"
+#include "cJSON.h"
+#include "utilities.h"
+#include "ICELog.h"
+#include <algorithm>
+#include <stdlib.h>
+   
+//
+// The control maps
+//StringPIDMap            PIDTable;               // PID control object table
+StringSetpointMap       setpointTable;          // setpoint control object table
+StringTimerMap          timerTable;             // timer control object table
+StringManualMap         manualTable;            // manual control object table
+StringCompositeMap      compositeTable;         // composite control object table
+StringFailsafeMap       failsafeTable;          // failsafe control object table
+StringAlgorithmMap      algorithmTable;         // composite control algorithms
+StringSequenceMap       sequenceTable;          // sequence control object table
+StringSensorErrorMap    sensorErrorTable;       // sensor error object table
+
+Mutex                   manual_mutex;
+Mutex                   setpoint_mutex;
+Mutex                   timer_mutex;
+Mutex                   failsafe_mutex;
+Mutex                   composite_mutex;
+Mutex                   sequence_mutex;
+Mutex                   sensorError_mutex;
+
+// local function prototypes
+static int loadPersistentControls(void);        // load the controls on flash
+
+// local helper functions
+static int createControl(const ConfigMessage_t *msg);
+static int modifyControl(const ConfigMessage_t *msg);
+static int destroyControl(const ConfigMessage_t *msg);
+
+static unsigned int getManualControlType(const char *filename);
+
+/*****************************************************************************
+ * Function:            ConfigurationHandler()
+ * Description:         The ICE Configuration Handler
+ *
+ * @param               args (unused)
+ * @return              none
+ *****************************************************************************/
+void ConfigurationHandler(void const *args)
+{
+    (void)(args);
+#ifdef LOAD_PERSISTENT_CONFIGURATIONS
+    loadPersistentControls();
+#endif 
+    osSignalSet(mainThreadId, sig_control_continue);
+
+    printf("\rConfigurationHandler has started...\n");
+
+    while ( true ) {
+        // wait for an event
+        osEvent evt = ConfigHandlerMailBox.get();
+        if (evt.status == osEventMail) {
+            ConfigMessage_t *msg = (ConfigMessage_t*) evt.value.p;
+
+            logInfo("\r%s: msg->action      = %d\n", __func__, msg->action);
+            logInfo("\r%s: msg->control     = %d\n", __func__, msg->control);
+            logInfo("\r%s: msg->controlFile = %s\n", __func__, msg->controlFile);
+
+            switch ( msg->action ) {
+                case ACTION_CREATE: {
+                    (void)createControl(msg);
+                    break;
+                }
+                case ACTION_MODIFY: {
+                    (void)modifyControl(msg);
+                    break;
+                }
+                case ACTION_DESTROY: {
+                    (void)destroyControl(msg);
+                    break;
+                }
+                default:
+                    break;
+            }
+
+            // free the message
+            ConfigHandlerMailBox.free(msg);
+        }
+    }
+}
+
+//
+// function:        ConfigurationHandler_showSetpointControls
+// description:     display contents of the setpoint control table
+//
+void ConfigurationHandler_showSetpointControls(void)
+{
+    setpoint_mutex.lock();
+    if ( !setpointTable.empty() ) {
+        printf("\r\n");
+        StringSetpointMap::const_iterator pos;
+        for ( pos = setpointTable.begin(); pos != setpointTable.end(); ++pos ) {
+            pos->second->display();
+        }
+    }
+    setpoint_mutex.unlock();
+}
+
+//
+// function:        ConfigurationHandler_showManualControls
+// description:     display contents of the manual control table
+//
+void ConfigurationHandler_showManualControls(void)
+{
+    manual_mutex.lock();
+    if ( !manualTable.empty() ) {
+        printf("\r\n");
+        StringManualMap::const_iterator pos;
+        for ( pos = manualTable.begin(); pos != manualTable.end(); ++pos ) {
+            pos->second->display();
+        }
+    }
+    manual_mutex.unlock();
+}
+
+//
+// function:        ConfigurationHandler_showTimerControls
+// description:     display contents of the timer control table
+//
+void ConfigurationHandler_showTimerControls(void)
+{
+    timer_mutex.lock();
+    if ( !timerTable.empty() ) {
+        printf("\r\n");
+        StringTimerMap::const_iterator pos;
+        for ( pos = timerTable.begin(); pos != timerTable.end(); ++pos ) {
+            pos->second->display();
+        }
+    }
+    timer_mutex.unlock();
+}
+
+//
+// function:        ConfigurationHandler_showCompositeControls
+// description:     display contents of the composite control table
+//
+void ConfigurationHandler_showCompositeControls(void )
+{
+    composite_mutex.lock();
+    if ( !compositeTable.empty() ) {
+        printf("\r\n");
+        StringCompositeMap::const_iterator pos;
+        for ( pos = compositeTable.begin(); pos != compositeTable.end(); ++pos) {
+            pos->second->display();
+        }
+    }
+    composite_mutex.unlock();
+}
+
+//
+// function:        ConfigurationHandler_showFailsafeControls
+// description:     display contents of the failsafe control table
+//
+// @param[in]       none
+// @param[out]      none
+// @return          none
+//
+void ConfigurationHandler_showFailsafeControls(void)
+{
+    failsafe_mutex.lock();
+    if ( !failsafeTable.empty() ) {
+        printf("\r\n");
+        StringFailsafeMap::const_iterator pos;
+        for ( pos = failsafeTable.begin(); pos != failsafeTable.end(); ++pos ) {
+            pos->second->display();
+        }
+    }
+    failsafe_mutex.unlock();
+}
+
+//
+// function:        ConfigurationHandler_showFailsafeControls
+// description:     display contents of the failsafe control table
+//
+// @param[in]       none
+// @param[out]      none
+// @return          none
+//
+void ConfigurationHandler_showSensorErrorControls(void)
+{
+    sensorError_mutex.lock();
+    if ( !sensorErrorTable.empty() ) {
+        printf("\r\n");
+        StringSensorErrorMap::const_iterator pos;
+        for ( pos = sensorErrorTable.begin(); pos != sensorErrorTable.end(); ++pos ) {
+            pos->second->display();
+        }
+    }
+    sensorError_mutex.unlock();
+}
+
+
+//
+// function:            ConfigurationHandler_showSequenceControls()
+// description:         display contents of the sequence control table
+//
+// @param[in]           none
+// @param[out]          none
+// @return              none
+//
+void ConfigurationHandler_showSequenceControls(void)
+{
+    sequence_mutex.lock();
+    if ( !sequenceTable.empty() ) {
+        printf("\r\n");
+        StringSequenceMap::const_iterator pos;
+        for ( pos = sequenceTable.begin(); pos != sequenceTable.end(); ++pos ) {
+            pos->second->display();
+        }
+    }
+    sequence_mutex.unlock();
+}
+
+//
+// Function:            ConfigurationHandler_showControls()
+// Description:         show the controls
+//
+// @param[in]           none
+// @return              none
+//
+void ConfigurationHandler_showControls(void)
+{
+    ConfigurationHandler_showSetpointControls();
+    ConfigurationHandler_showTimerControls();
+    ConfigurationHandler_showManualControls();
+    ConfigurationHandler_showFailsafeControls();
+    ConfigurationHandler_showCompositeControls();
+    ConfigurationHandler_showSequenceControls();
+    ConfigurationHandler_showSensorErrorControls();
+    // TODO: PID controls
+}
+
+//
+// function:        ConfigurationHandler_showAlgorithms
+// description:     display the control algorithms
+//
+// @param           none
+// @return          none
+//
+void ConfigurationHandler_showAlgorithms(void)
+{
+    StringAlgorithmMap::const_iterator pos;
+
+    for ( pos = algorithmTable.begin(); pos != algorithmTable.end(); ++pos ) {
+        pos->second->display();
+    }
+}
+
+/*****************************************************************************
+ * Function:            loadPersistentControls()
+ * Description:         load persistent controls from flash
+ *
+ * @param               none
+ * @return              none
+ *****************************************************************************/
+static int loadPersistentControls(void)
+{
+    static bool loaded = false;
+
+    if ( !loaded ) {        // lazy protection
+
+        printf("\rLoading persistent controls: \n");
+        std::vector<std::string> file_list = GLOBAL_mdot->listUserFiles();
+
+        loaded = true;
+
+        for (std::vector<std::string>::const_iterator i = file_list.begin(); i != file_list.end(); ++i) {
+            if( strncmp( i->c_str(), CONTROL_SP_STR, strlen(CONTROL_SP_STR)) == 0 ) {
+                // create the setpoint control
+                ConfigMessage_t msg;
+                msg.control = CONTROL_SETPOINT;
+                strncpy(msg.controlFile, i->c_str(), sizeof(msg.controlFile));
+                int rc = createControl(&msg);
+                if ( rc != 0 ) {
+                    logError("%s: failed to load %s", __func__, msg.controlFile);
+                } else {
+                    printf("\r   setpoint control %s loaded.\n", msg.controlFile);
+                }
+            } else if ( strncmp( i->c_str(), CONTROL_TM_STR, strlen(CONTROL_TM_STR)) == 0  ) {
+                // create the timer control
+                ConfigMessage_t msg;
+                msg.control = CONTROL_TIMER;
+                strncpy(msg.controlFile, i->c_str(), sizeof(msg.controlFile));
+                int rc = createControl(&msg);
+                if ( rc != 0 ) {
+                    logError("%s: failed to load %s", __func__, msg.controlFile);
+
+                } else {
+                    printf("\r   timer control %s loaded.\n", msg.controlFile);
+                }
+            } else if ( strncmp( i->c_str(), CONTROL_COMP_STR, strlen(CONTROL_COMP_STR)) == 0 ) {
+                ConfigMessage_t msg;
+                msg.control = CONTROL_COMPOSITE;
+                strncpy(msg.controlFile, i->c_str(), sizeof(msg.controlFile));
+                int rc = createControl(&msg);
+                if ( rc != 0 ) {
+                    logError("%s: failed to load %s\n", __func__, msg.controlFile);
+                } else {
+                    printf("\r   composite control %s loaded.\n", msg.controlFile);
+                }
+            } else if ( strncmp( i->c_str(), CONTROL_CA_STR, strlen(CONTROL_CA_STR)) == 0 ) {
+                ConfigMessage_t msg;
+                msg.control = CONTROL_ALGORITHM;
+                strncpy(msg.controlFile, i->c_str(), sizeof(msg.controlFile));
+                int rc = createControl(&msg);
+                if ( rc != 0 ) {
+                    logError("%s: failed to load %s\n", __func__, msg.controlFile);
+                } else {
+                    printf("\r   algorithmic control %s loaded.\n", msg.controlFile);
+                }
+            } else if ( strncmp( i->c_str(), CONTROL_FS_STR, strlen(CONTROL_FS_STR)) == 0 ) {
+                ConfigMessage_t msg;
+                msg.control = CONTROL_FAILSAFE;
+                strncpy(msg.controlFile, i->c_str(), sizeof(msg.controlFile));
+                int rc = createControl(&msg);
+                if ( rc != 0 ) {
+                    logError("%s: failed to load %s\n", __func__, msg.controlFile);
+                } else {
+                    printf("\r   failsafe control %s loaded.\n", msg.controlFile);
+                }
+            } else if ( strncmp( i->c_str(), CONTROL_PID_STR, strlen(CONTROL_PID_STR)) == 0 ) {
+                // TODO:
+            } else if ( strncmp( i->c_str(), CONTROL_MN_STR, strlen(CONTROL_MN_STR)) == 0 ) {
+                // TODO: delete any timed manual control, not continuous...
+                if  ( getManualControlType(i->c_str()) == MANUAL_CONTROL_TYPE_CONTINUOUS ) {
+                    ConfigMessage_t msg;
+                    msg.control = CONTROL_MANUAL;
+                    strncpy(msg.controlFile, i->c_str(), sizeof(msg.controlFile));
+                    int rc = createControl(&msg);
+                    if ( rc != 0 ) {
+                        logError("%s: failed to load %s\n", __func__, msg.controlFile);
+                    } else {
+                        printf("\r   manual control %s loaded\n", msg.controlFile);
+                    }
+                } else {
+                    GLOBAL_mdot->deleteUserFile(i->c_str());
+                }
+            } else if ( strncmp( i->c_str(), CONTROL_SEQ_STR, strlen(CONTROL_SEQ_STR)) == 0 ) {
+                ConfigMessage_t msg;
+                msg.control = CONTROL_SEQUENCE;
+                strncpy(msg.controlFile, i->c_str(), sizeof(msg.controlFile));
+                int rc = createControl(&msg);
+                if ( rc != 0 ) {
+                    logError("%s: failed to load %s\n", __func__, msg.controlFile);
+                } else {
+                    printf("\r   sequence control %s loaded.\n", msg.controlFile);
+                }
+            } else if ( strncmp( i->c_str(), CONTROL_SE_STR, strlen(CONTROL_SE_STR)) == 0 ) {
+                ConfigMessage_t msg;
+                msg.control = CONTROL_SENSOR_ERROR;
+                strncpy(msg.controlFile, i->c_str(), sizeof(msg.controlFile));
+                int rc = createControl(&msg);
+                if ( rc != 0 ) {
+                    logError("%s: failed to load %s\n", __func__, msg.controlFile);
+                } else {
+                    printf("\r    sensor error control %s loaded.\n", msg.controlFile);
+                }
+            } else {
+                logInfo("\rNot A Control File%s\r\n", i->c_str());
+                // not a control file
+            }
+        }
+    }
+    return 0;
+}
+
+/*****************************************************************************
+ * Function:            createControl()
+ * Description:         creates a new control
+ *
+ * @param               none
+ * @return              0 on success; -1 otherwise
+ *****************************************************************************/
+static int createControl(const ConfigMessage_t *msg)
+{
+    int status = 0;
+
+    logInfo("\r%s invoked\n", __func__);
+
+    switch (msg->control) {
+        case CONTROL_SETPOINT: {
+            SetpointControl *setpointControl = new SetpointControl;
+            bool rc = setpointControl->load(msg->controlFile);
+            if ( rc != true ) {
+                logError("%s: failed to load %s\n", __func__, msg->controlFile);
+                delete setpointControl;
+                GLOBAL_mdot->deleteUserFile(msg->controlFile);
+                status = -1;
+            } else {
+                setpoint_mutex.lock();
+                setpointTable[msg->controlFile] = setpointControl;
+                setpoint_mutex.unlock();
+                setpointControl->start();
+            }
+            break;
+        }
+        case CONTROL_TIMER: {
+            TimerControl *timerControl = new TimerControl;
+            bool rc = timerControl->load(msg->controlFile);
+            if ( rc != true ) {
+                logError("%s: failed to load %s\n", __func__, msg->controlFile);
+                delete timerControl;
+                GLOBAL_mdot->deleteUserFile(msg->controlFile);
+                status = -1;
+            } else {
+                timer_mutex.lock();
+                timerTable[msg->controlFile] = timerControl;
+                timer_mutex.unlock();
+                timerControl->start();
+            }
+            break;
+        }
+        case CONTROL_MANUAL: {
+            ManualControl *manualControl = new ManualControl;
+            bool rc = manualControl->load(msg->controlFile);
+            if ( rc != true ) {
+                logError("%s: failed to load %s\n", __func__, msg->controlFile);
+                delete manualControl;
+                GLOBAL_mdot->deleteUserFile(msg->controlFile);
+                status = -1;
+
+            } else {
+                manual_mutex.lock();
+                manualTable[msg->controlFile] = manualControl;
+                manual_mutex.unlock();
+                manualControl-> start();
+            }
+            break;
+        }
+
+        case CONTROL_PID: {
+            // TODO: PID
+            break;
+        }
+        case CONTROL_COMPOSITE: {
+            CompositeControl *compositeControl = new CompositeControl;
+            bool rc = compositeControl->load(msg->controlFile);
+            if ( rc != true ) {
+                logError("%s: failed to load %s\n", __func__, msg->controlFile);
+                delete compositeControl;
+                GLOBAL_mdot->deleteUserFile(msg->controlFile);
+                status = -1;
+            } else {
+                composite_mutex.lock();
+                compositeTable[msg->controlFile] = compositeControl;
+                composite_mutex.unlock();
+                compositeControl->start();
+            }
+            break;
+        }
+
+        case CONTROL_SEQUENCE: {
+            SequenceControl *sequenceControl = new SequenceControl;
+            bool rc = sequenceControl->load(msg->controlFile);
+            if ( rc != true ) {
+                logError("%s: failed to load %s", __func__, msg->controlFile);
+                delete sequenceControl;
+                //GLOBAL_mdot->deleteUserFile(msg->controlFile);
+                status = -1;
+            } else {
+                sequence_mutex.lock();
+                sequenceTable[msg->controlFile] = sequenceControl;
+                sequence_mutex.unlock();
+                sequenceControl->start();
+            }
+            break;
+        }
+        case CONTROL_FAILSAFE: {
+            FailsafeControl *failsafeControl = new FailsafeControl;
+            bool rc = failsafeControl->load(msg->controlFile);
+            if ( rc != true ) {
+                logError("%s: failed to load %s\n", __func__, msg->controlFile);
+                delete failsafeControl;
+                GLOBAL_mdot->deleteUserFile(msg->controlFile);
+                status = -1;
+            } else {
+                failsafe_mutex.lock();
+                failsafeTable[msg->controlFile] = failsafeControl;
+                failsafe_mutex.unlock();
+                failsafeControl->start();
+            }
+            break;
+        }
+        case CONTROL_SENSOR_ERROR: {
+            SensorErrorControl *sensorErrorControl = new SensorErrorControl;
+            bool rc = sensorErrorControl->load(msg->controlFile);
+            if ( rc != true ) {
+                logError("%s: failed to load %s\n", __func__, msg->controlFile);
+                delete sensorErrorControl;
+                GLOBAL_mdot->deleteUserFile(msg->controlFile);
+                status = -1;
+            } else {
+                sensorError_mutex.lock();
+                sensorErrorTable[msg->controlFile] = sensorErrorControl;
+                sensorError_mutex.unlock();
+                sensorErrorControl->start();
+            }
+            break;
+        }
+        case CONTROL_ALGORITHM: {
+            CompositeAlgorithm *compositeAlgorithm = new CompositeAlgorithm;
+            bool rc = compositeAlgorithm->load(msg->controlFile);
+            if ( rc != true ) {
+                logError("%s: failed to load %s\n", __func__, msg->controlFile);
+                status = -1;
+            } else {
+                // add this algorithm to the table
+                algorithmTable[compositeAlgorithm->getId()] = compositeAlgorithm;
+            }
+            break;
+        }
+        default:
+            logInfo("\r%s: control type %d not implemented yet...\n",
+                    __func__, msg->control);
+            break;
+    }
+    return status;
+}
+
+/*****************************************************************************
+ * Function:            modifyControl()
+ * Description:         modifies a control
+ *
+ * @param               msg
+ * @return              none
+ *****************************************************************************/
+static int modifyControl(const ConfigMessage_t *msg)
+{
+    logInfo("\r%s invoked\n", __func__);
+
+    switch (msg->control) {
+        case CONTROL_SETPOINT: {
+            // find the control in the table
+            StringSetpointMap::const_iterator pos;
+            setpoint_mutex.lock();
+            pos = setpointTable.find(msg->controlFile);
+            if ( pos != setpointTable.end() ) {
+                bool rc = pos->second->load(msg->controlFile);
+                if ( rc != true ) {
+                    logError("\rFailed to reload the setpoint control %s\n", msg->controlFile);
+                } else {
+                    logInfo("\rReloaded the setpoint control %s\n", msg->controlFile);
+                }
+            }
+            setpoint_mutex.unlock();
+            break;
+        }
+        case CONTROL_MANUAL: {
+            // find the manual control in the table
+            StringManualMap::const_iterator pos;
+            manual_mutex.lock();
+            pos = manualTable.find(msg->controlFile);
+            if ( pos != manualTable.end() ) {
+                bool rc = pos->second->load(msg->controlFile);
+                if ( rc != true ) {
+                    logError("\rFailed to reload the manual control %s\n", msg->controlFile);
+                } else {
+                    logInfo("\rReloaded the manual control %s\n", msg->controlFile);
+                }
+            }
+            manual_mutex.unlock();
+            break;
+        }
+        default:
+            logError("%s: unknown control %d\n", __func__, msg->control);
+            break;
+    }
+
+    return 0;
+}
+
+/*****************************************************************************
+ * Function:            destroyControl()
+ * Description:         destroys a controls
+ *
+ * @param               msg
+ * @return              none
+ *****************************************************************************/
+static int destroyControl(const ConfigMessage_t *msg)
+{
+    logInfo("\r%s invoked\n", __func__);
+
+    switch ( msg->control ) {
+        case CONTROL_SETPOINT: {
+            StringSetpointMap::iterator pos;
+            setpoint_mutex.lock();
+            pos = setpointTable.find(msg->controlFile);
+            if ( pos != setpointTable.end() ) {
+                if ( !Util_isSequenceSubControl(msg->controlFile) ) {
+                    GLOBAL_mdot->deleteUserFile(msg->controlFile);
+                }
+                logInfo("%s: deleted %s", __func__, msg->controlFile);
+                pos->second->unregisterControl();
+                delete (pos->second);
+                setpointTable.erase(pos);
+            } else {
+                logError("%s: unable to find %s\n", __func__, msg->controlFile);
+            }
+            setpoint_mutex.unlock();
+            break;
+        }
+        case CONTROL_TIMER: {
+            StringTimerMap::iterator pos;
+            timer_mutex.lock();
+            pos = timerTable.find(msg->controlFile);
+            if ( pos != timerTable.end() ) {
+                if ( !Util_isSequenceSubControl(msg->controlFile) ) {
+                    GLOBAL_mdot->deleteUserFile(msg->controlFile);
+                }
+                logInfo("%s: deleted %s", __func__, msg->controlFile);
+                pos->second->unregisterControl();
+                delete (pos->second);
+                timerTable.erase(pos);
+            } else {
+                logError("%s: unable to find %s\n", __func__, msg->controlFile);
+            }
+            timer_mutex.unlock();
+            break;
+        }
+        case CONTROL_MANUAL: {
+            StringManualMap::iterator pos;
+            manual_mutex.lock();
+            pos = manualTable.find(msg->controlFile);
+            if ( pos != manualTable.end() ) {
+                GLOBAL_mdot->deleteUserFile(msg->controlFile);
+                logInfo("%s: deleted %s", __func__, msg->controlFile);
+                // unregister with the output thread
+                pos->second->unregisterControl();
+                delete (pos->second);
+                manualTable.erase(pos);
+            } else {
+                logError("%s: unable to find %s", __func__, msg->controlFile);
+            }
+            manual_mutex.unlock();
+            break;
+        }
+        case CONTROL_COMPOSITE: {
+            StringCompositeMap::iterator pos;
+            composite_mutex.lock();
+            pos = compositeTable.find(msg->controlFile);
+            if ( pos != compositeTable.end() ) {
+                GLOBAL_mdot->deleteUserFile(msg->controlFile);
+                logInfo("%s: deleted %s", __func__, msg->controlFile);
+                pos->second->unregisterControls();
+                delete (pos->second);
+                compositeTable.erase(pos);
+            } else {
+                logError("%s: unable to find %s", __func__, msg->controlFile);
+            }
+            composite_mutex.unlock();
+            break;
+        }
+        case CONTROL_SEQUENCE: {
+            StringSequenceMap::iterator pos;
+            sequence_mutex.lock();
+            pos = sequenceTable.find(msg->controlFile);
+            if ( pos != sequenceTable.end() ) {
+                GLOBAL_mdot->deleteUserFile(msg->controlFile);
+                logInfo("%s: delete %s", __func__, msg->controlFile);
+                // TODO:
+                // pos->second->unregisterControls();
+                delete (pos->second);
+                sequenceTable.erase(pos);
+            } else {
+                logError("%s: unable to find %s", __func__, msg->controlFile);
+            }
+            sequence_mutex.unlock();
+            break;
+        }
+        case CONTROL_SENSOR_ERROR: {
+            StringSensorErrorMap::iterator pos;
+            sensorError_mutex.lock();
+            pos = sensorErrorTable.find(msg->controlFile);
+            if ( pos != sensorErrorTable.end() ) {
+                GLOBAL_mdot->deleteUserFile(msg->controlFile);
+                logInfo("%s: deleted %s", __func__, msg->controlFile);
+                pos->second->unregisterControl();
+                delete (pos->second);
+                sensorErrorTable.erase(pos);
+            } else {
+                logError("%s: unable to find %s", __func__, msg->controlFile);
+            }
+            sensorError_mutex.unlock();
+            break;
+        }
+        case CONTROL_PID: {
+            // TODO:
+            break;
+        }
+        default:
+            break;
+    }
+    return 0;
+}
+//
+// function:            getManualControlType
+// description:         extract the manual control type (continuous or timed)
+//
+// @param[in]           filename
+// @return              control type (int)
+//
+static unsigned int getManualControlType(const char *filename)
+{
+    char buf[MAX_FILE_SIZE];
+    unsigned int type = MANUAL_CONTROL_TYPE_NONE;
+    bool rc = GLOBAL_mdot->readUserFile(filename, buf, MAX_FILE_SIZE);
+    if ( rc != true ) {
+        logError("%s: failed to read %s", __func__, filename);
+    } else {
+        cJSON * root = cJSON_Parse(buf);
+        type = atoi(cJSON_GetObjectItem(root,"type")->valuestring);
+        cJSON_Delete(root);
+    }
+    return type;
+}