Erick / Mbed 2 deprecated ICE-F412

Dependencies:   mbed-rtos mbed

ICE-Application/src/ConfigurationHandler/Controls/ManualControl.cpp

Committer:
jmarkel44
Date:
2017-01-24
Revision:
0:61364762ee0e

File content as of revision 0:61364762ee0e:

/******************************************************************************
 *
 * File:                ManualControl.cpp
 * Desciption:          ICE Manual Control Class implementation
 *
 *****************************************************************************/
#include "ManualControl.h"
#include "ICELog.h"
#include "cJSON.h"
#include "ModbusMasterApi.h"
#include "global.h"
#include <string>
#include <iostream>
#include <iomanip>
#include <stdarg.h>
#include <stdlib.h>
#include <time.h>

using namespace std;

// for debugging - this can be set via the console command:
//      "debug-man 1 or debug-man 0
bool debugManualControl = false;

static void debug(const char *fmt, ...)
{
    if ( debugManualControl ) {
        va_list vargs;
        va_start(vargs, fmt);
        vfprintf(stdout, fmt, vargs);
        va_end(vargs);
    }
}

#ifdef MDOT_ICE
extern mDot *GLOBAL_mdot;
#endif

//
// method:          load
// description:     open the configuration file and assign data to the
//                  setpoint control object
//
// @param           controlFile -> name of the control file
// @return          true if data is assigned; false on error
//
bool ManualControl::load(std::string _controlFile)
{
    controlFile = _controlFile;
    endTime = 0;

    char dataBuf[MAX_FILE_SIZE];
    
    // read the data into the buffer
    bool rc = GLOBAL_mdot->readUserFile(controlFile.c_str(), (void *)dataBuf, sizeof(dataBuf));
    if ( rc != true ) {
        logError("%s: failed to read %s", __func__, controlFile.c_str());
        // caller should destroy the object
        return false;
    }

    // validate the JSON tags in the control file
    if ( validateControlData(dataBuf) != true ) {
        logError("%s: invalid control data.", __func__);
        return false;
    }

    // assign object data from control file
    copyControlData(dataBuf);
    
    ModbusValue val;
    // validate the output
    if ( ModbusMasterReadRegister(output, &val) == false ) {
        logError("%s failed to find output %s", id.c_str(), output.c_str());
        return false;
    }

    return true;
}

//
// method:          validateControlData
// description:     validate JSON formatted data
//
// @param[in]       buf -> JSON formatted string
// @param[out]      none
// @return          true if valid; false otherwise
//
bool ManualControl::validateControlData(const char* buf)
{
    bool rc = true;
    cJSON * root = cJSON_Parse(buf);

    if ( !cJSON_HasObjectItem(root, "id") ||
            !cJSON_HasObjectItem(root, "output") ||
            !cJSON_HasObjectItem(root, "type") ||
            !cJSON_HasObjectItem(root, "priority") ||
            !cJSON_HasObjectItem(root, "duration") ||
            !cJSON_HasObjectItem(root, "setpoint") ||
            !cJSON_HasObjectItem(root, "state") ||
            !cJSON_HasObjectItem(root, "percent") ) {
        logError("%s: control file is missing expected tags", __func__);
        rc = false;
    }
    cJSON_Delete(root);
    return rc;
}

//
// method:          copyControlData
// description:     copy JSON formatted control data to object
//
// @param[in]       buf -> JSON formatted string
// @param[out]      none
// @return          none
//
void ManualControl::copyControlData(const char *buf)
{
    cJSON * root = cJSON_Parse(buf);

    id          = cJSON_GetObjectItem(root,"id")->valuestring;
    output      = cJSON_GetObjectItem(root,"output")->valuestring;
    type        = atoi(cJSON_GetObjectItem(root,"type")->valuestring);
    priority    = atoi(cJSON_GetObjectItem(root, "priority")->valuestring);
    duration    = atoi(cJSON_GetObjectItem(root, "duration")->valuestring);
    setpoint    = atof(cJSON_GetObjectItem(root, "setpoint")->valuestring);
    state       = atoi(cJSON_GetObjectItem(root, "state")->valuestring);
    percent     = atoi(cJSON_GetObjectItem(root, "percent")->valuestring);

    cJSON_Delete(root);
}

//
// method:          start
// description:     start the manual control
//
// @param           none
// @return          none
//
void ManualControl::start(void)
{
    currentState = STATE_STARTUP;
    if ( type == MANUAL_CONTROL_TYPE_TIMED ) {
        debug("\r%s:%s-> manual control is timed\n", __func__, id.c_str());
        endTime = time(NULL) + duration;
    }
}

//
// method:          update
// description:     the manual control's state machine
//
// @param           none
// @return          none
//
ManualControlError_t ManualControl::update(void)
{
    ManualControlError_t rc = MANUAL_CONTROL_OK;

    switch ( this->currentState ) {
        case STATE_INIT:
            // do nothing
            break;
        case STATE_STARTUP:
            if ( state & 0x01 ) {
                this->currentState = STATE_CONTROL_ON;
                debug("\r%s: [STARTUP]->manual on->[ON]\n", id.c_str());
            } else {
                this->currentState = STATE_CONTROL_OFF;
                debug("\r%s: [STARTUP]->manual off->[OFF]\n", id.c_str());

            }
            //this->currentState = (state & 0x01) ? STATE_CONTROL_ON : STATE_CONTROL_OFF;
            this->powerOutput();
            break;
        case STATE_CONTROL_ON:
            if ( !(state & 0x1) ) {
                this->currentState = STATE_CONTROL_OFF;
                // user has changed state, so this is (or becomes) a pure
                // manual control
                endTime = 0;
                this->powerOutput();
                debug("\r%s: [ON]->manual off->[OFF]\n", id.c_str());
            } else if ( endTime != 0 && time(NULL) >= endTime ) {
                // timed interval has elapsed
                currentState = STATE_CONTROL_FINISHED;
                debug("\r%s: [ON]->timer expired->[FINISHED]\n", id.c_str());
            }
            break;
        case STATE_CONTROL_OFF:
            if ( state & 0x1 ) {
                this->currentState = STATE_CONTROL_ON;
                // user has changed the state, so this (or becomes) a pure
                // manual control
                endTime = 0;
                this->powerOutput();
                debug("\r%s: [OFF]->manual on->[ON]\n", id.c_str());
            } else if ( endTime != 0 && time(NULL) >= endTime ) {
                // timed interval has elapsed
                currentState = STATE_CONTROL_FINISHED;
                debug("\r%s: [OFF]->timer expired->[FINISHED]\n", id.c_str());
            }
            break;
        case STATE_CONTROL_FINISHED:
            // mark for deletion (control task will do the cleanup)
            rc = MANUAL_CONTROL_DESTROY;
            break;
        default:
            logError("%s unknown state %d\n", __func__, this->currentState);
            rc = MANUAL_CONTROL_ERROR;
            break;
    }
    return rc;
}

//
// method:              unregisterControl
// description:         unregister this control with the output master
//
// @param               none
// @return              none
//
int ManualControl::unregisterControl(void)
{
    logInfo("%s: Attempting to unregister %s\n",
            __func__, controlFile.c_str());

    OutputControlMsg_t *output_mail = OutputMasterMailBox.alloc();
    memset(output_mail, 0, sizeof(OutputControlMsg_t));

    output_mail->controlType = CONTROL_MANUAL;
    output_mail->action      = ACTION_CONTROL_UNREGISTER;
    output_mail->priority    = this->priority;
    strncpy(output_mail->output_tag, this->output.c_str(), sizeof(output_mail->output_tag)-1);
    strncpy(output_mail->id, this->id.c_str(), sizeof(output_mail->id)-1);

    OutputMasterMailBox.put(output_mail);
    return 0;
}

//
// method:          powerOutput
// description:     send a message to the output thread to power the relay
//
// @param           none
// @return          -1 on error; 0 otherwise
//
int ManualControl::powerOutput(void)
{
    printf("\r%s-> id:%s attempting to manually turn %s relay %s\n",
           __func__, id.c_str(), (state & 0x1) ? "ON" : "OFF", output.c_str());

    OutputControlMsg_t *output_mail = OutputMasterMailBox.alloc();
    memset(output_mail, 0, sizeof(OutputControlMsg_t));

    output_mail->controlType    = CONTROL_MANUAL;
    output_mail->action         = (state & 0x1) ? ACTION_CONTROL_ON : ACTION_CONTROL_OFF;
    output_mail->priority       = this->priority;
    strncpy(output_mail->output_tag, this->output.c_str(), sizeof(output_mail->output_tag)-1);
    strncpy(output_mail->id, this->id.c_str(), sizeof(output_mail->id)-1);

    OutputMasterMailBox.put(output_mail);
    return 0;
}

//
// method:          display
// description:     display the pertinents
//
// @param           none
// @return          none
//
void ManualControl::display(void)
{
    string mapper[] = { "INIT",
                        "STARTUP",
                        "CONTROL_ON",
                        "CONTROL_OFF"
                      };

    printf("\r\n");
    std::cout << left << setw(10) << setfill(' ') << "manual: ";
    std::cout << left << setw(40) << setfill(' ') << controlFile;
    std::cout << left << setw(20) << setfill(' ') << id;
    std::cout << left << setw(20) << setfill(' ') << output;
    std::cout << left << setw(6)  << setfill(' ') << type;
    std::cout << left << setw(6)  << setfill(' ') << priority;
    std::cout << left << setw(8)  << setfill(' ') << duration;
    //std::cout << left << setw(8)  << setfill(' ') << percent;
    std::cout << left << setw(16) << setfill(' ') << mapper[currentState];
    if ( type == MANUAL_CONTROL_TYPE_TIMED ) {
        std::cout << left << setw(8) << setfill(' ') << "Time Remaining (secs): " << endTime - time(NULL);
    }
    std::cout.flush();
}