#include <regex.h>
#include <vector>
#include <string>
#include <map>
#include <time.h>
#include <sys/time.h>
#include <unistd.h>

#include "myMms_m.h"
#include "mms_m.h"
#include "mms_m_errstr.h"
#include "mms_m_parse_cfg.h"
#include "hal_thread.h"

// #include "conversions.h"

#define MMS_M_REASON_ALL_CALL 64
#define MMS_M_REASON_READ_PARAM 65
#define MMS_M_RECONNECT_INTERVAL 60 // seconds

static const std::string g_sgcb_edit_sg	= "LLN0.SGCB.EditSG";
static const std::string g_sgcb_cnf_edit = "LLN0.SGCB.CnfEdit";

static std::map<int, stru_mms_m_obj *> g_mms_m_obj_map;

/***************************************************************************** */
static void mms_m_timer_running(stru_mms_m_obj &obj);
static void mms_m_get_local_time(stru_mms_m_time &time);
/***************************************************************************** */

#ifdef __cplusplus
extern "C" {
#endif

void Conversions_msTimeToGeneralizedTime(unsigned long msTime, unsigned char* buffer);

#ifdef __cplusplus
}
#endif

static void sleep_ms(uint32_t ms)
{
    struct timespec rqtp;
	struct timespec rmtp;

	rqtp.tv_sec = ms / 1000;
	rqtp.tv_nsec = ms % 1000 * 1000000;
	
	for(;nanosleep(&rqtp, &rmtp);)
	{
		rqtp = rmtp;
	}
}

static int mms_m_push_event(stru_mms_m_obj &obj, stru_mms_m_event &event)
{
    if(event.ctrl_type >= _MMS_M_EVENT_AO_WRITE)
    {
        LOG_E("push event ctrl_type(%d) is not support", event.ctrl_type);
        return -1;
    }
    
    stru_event_queue &queue = obj.run.event_queue;

    if(queue.num >= queue.size)
    {
        LOG_E("%s: event queue is full", obj.cfg_path.c_str());
        return -1;
    }

    queue.event[queue.w_ptr++] = event;
    queue.w_ptr %= queue.size;
    queue.num++;

    return 0;
}

static int mms_m_pop_event(stru_mms_m_obj &obj, stru_mms_m_event &event)
{
    stru_event_queue &queue = obj.run.event_queue;

    if(queue.num <= 0)
    {
        return -1;
    }

    // mms_m_set_data_value((void *)event.val, queue.event[queue.r_ptr].val, queue.event[queue.r_ptr].value_type);
    event = queue.event[queue.r_ptr++];
    queue.r_ptr %= queue.size;
    queue.num--;

    return 0;
}

static void mms_m_put_value(stru_mms_m_obj &obj, stru_mms_m_out_value &out_value)
{
    std::vector<mms_m_out_value_cb>::iterator it;

    for(it = obj.run.out_cb_lists.begin(); it!= obj.run.out_cb_lists.end(); it++)
    {
        sem_wait(&obj.run.sem);
        (*it)(&out_value);
        sem_post(&obj.run.sem);
    }
}

static void mms_m_show_MmsValue(MmsValue* mms_value)
{
    if(NULL == mms_value)
    {
        return;
    }

    bool bVal;
    char *strVal;
    int iVal;
    unsigned int uVal;
    float fVal;

    MmsType mmsType = MmsValue_getType(mms_value);
    switch(mmsType)
    {
        case MMS_STRUCTURE:
            return mms_m_show_MmsValue(MmsValue_getElement(mms_value, 0));
            break;
        case MMS_BOOLEAN:
            bVal = MmsValue_getBoolean(mms_value);
            // LOG_I("show_mms_value bVal %d\r\n", bVal);
            break;
        case MMS_BIT_STRING:
            strVal= (char *)MmsValue_toString(mms_value);
            // LOG_I("show_mms_value strVal %s\r\n", strVal);
            break;
        case MMS_INTEGER:
            iVal = MmsValue_toInt32(mms_value);
            // LOG_I("show_mms_value iVal: %d\n", iVal);
            break;
        case MMS_UNSIGNED:
            uVal = MmsValue_toUint32(mms_value);
            // LOG_I("show_mms_value uVal: %u\n", uVal);
            break;
        case MMS_FLOAT:
            fVal = MmsValue_toFloat(mms_value);
            // LOG_I("show_mms_value fVal: %f\n", fVal);
            break;
        default:
            LOG_E("show_mms_value Failed to read value (error code: %i)\n", MmsValue_getDataAccessError(mms_value));
            break;
    }

}

static int mms_m_get_mmsValue(MmsValue *mms_value, stru_point_item &item)
{
    if(NULL == mms_value)
    {
        LOG_E("mms_value is NULL\r\n");
        return -1;
    }

    MmsType mms_type = MmsValue_getType(mms_value);

    switch(mms_type)
    {
        case MMS_STRUCTURE:
            return mms_m_get_mmsValue(MmsValue_getElement(mms_value, 0), item);
        case MMS_BOOLEAN:
            item.type = MMS_BOOLEAN;
            // item.value.val.b = MmsValue_getBoolean(mms_value);
            *(uint8_t *)item.value.p_val = MmsValue_getBoolean(mms_value);
            break;
        case MMS_BIT_STRING:
            // LOG_I("MMS_BIT_STRING %s\r\n", MmsValue_toString(mms_value));
            break;
        case MMS_INTEGER:
            item.type = MMS_INTEGER;
            // item.value.val.l = MmsValue_toInt32(mms_value);
            *(int32_t *)item.value.p_val = MmsValue_toInt32(mms_value);
            break;
        case MMS_UNSIGNED:
            item.type = MMS_UNSIGNED;
            // item.value.val.lu = MmsValue_toUint32(mms_value);
            *(uint32_t *)item.value.p_val = MmsValue_toUint32(mms_value);
            break;
        case MMS_FLOAT:
            item.type = MMS_FLOAT;
            // item.value.val.f = MmsValue_toFloat(mms_value);
            *(float *)item.value.p_val = MmsValue_toFloat(mms_value);
            break;
        default:
            LOG_E("mms_m_get_mmsValue Failed to read value (error code: %i)\n", MmsValue_getDataAccessError(mms_value));
            return -1;
    }

    return 0;
}

static int mms_m_param_value_sync(stru_point_item &item, stru_mms_m_event &event)
{
    if(item.type != event.value_type)
    {
        LOG_E("value type not match");
        return -1;
    }

    bool val_b = false;

    if(item.set_value == NULL)
    {
        switch(item.type)
        {
            case MMS_BOOLEAN:
                val_b = atoi(event.val) > 0 ? true : false;
                item.set_value = MmsValue_newBoolean(val_b);
                break;
            case MMS_INTEGER:
                item.set_value = MmsValue_newIntegerFromInt32(atoi(event.val));
                break;
            case MMS_UNSIGNED:
                item.set_value = MmsValue_newUnsignedFromUint32(atoi(event.val));
                break;
            case MMS_FLOAT:
                item.set_value = MmsValue_newFloat(atof(event.val));
                break;
            case MMS_STRING:
                item.set_value = MmsValue_newMmsString((char *)event.val);
                break;
            default:
                LOG_E("value type not support");
                return -1;
        }
    }
    else
    {
        switch(item.type)
        {
            case MMS_BOOLEAN:
                val_b = atoi(event.val) > 0 ? true : false;
                MmsValue_setBoolean(item.set_value, val_b);
                break;
            case MMS_INTEGER:
                MmsValue_setInt32(item.set_value, atoi(event.val));
                break;
            case MMS_UNSIGNED:
                MmsValue_setUint32(item.set_value, atoi(event.val));
                break;
            case MMS_FLOAT:
                MmsValue_setFloat(item.set_value, atof(event.val));
                break;
            case MMS_STRING:
                MmsValue_setMmsString(item.set_value, (char *)event.val);
                break;
            default:
                LOG_E("value type not support");
                return -1;
        }
    }

    return 0;
}

static int mms_m_send_ao_write(stru_mms_m_obj &obj, stru_mms_m_event &event)
{
    std::vector<stru_point_item>::iterator it;
    stru_point_item *p_item = NULL;
    IedClientError err;

    for(it = obj.p_cfg->point.ao.begin(); it!= obj.p_cfg->point.ao.end(); it++)
    {
        if(it->name.compare(event.name) == 0)
        {
            p_item = &(*it);
            break;
        }
    }
    if(NULL == p_item)
    {
        LOG_E("%s: %s not found in cfg\n", obj.cfg_path.c_str(), event.name);
        return -1;
    }
    size_t pos = p_item->reference.find("/");
    if(pos == std::string::npos)
    {
        LOG_E("%s: %s reference is not correct\n", obj.cfg_path.c_str(), p_item->reference.c_str());
        return -1;
    }

    std::string str_head = p_item->reference.substr(0, pos);
    std::string str_EditSG = str_head + "/" + g_sgcb_edit_sg;
    std::string str_CnfEdit = str_head + "/" + g_sgcb_cnf_edit;

    MmsValue *p_value = IedConnection_readObject(obj.run.con, &err, p_item->reference.c_str(), (FunctionalConstraint)p_item->fc);
    if(NULL != p_value)
    {
        if(MMS_M_DEBUG_PRINT_ON == obj.debug_print_flag)
        {
            mms_m_show_MmsValue(p_value);
        }
    }
    else
    {
        LOG_E("%s: %s read err:%s(%d)\n", obj.cfg_path.c_str(), p_item->reference.c_str(), mms_m_err_str(err), err);
    }
    MmsValue_delete(p_value);

    // select EditSG
    if(obj.param_zone == NULL)
    {
        obj.param_zone = MmsValue_newUnsignedFromUint32(event.set_zone);
    }
    else
    {   
        MmsValue_setUint32(obj.param_zone, event.set_zone);
    }

    IedConnection_writeObject(obj.run.con, &err, str_EditSG.c_str(), IEC61850_FC_SP, obj.param_zone);
    if(err != IED_ERROR_OK)
    {
        LOG_E("%s: %s select err:%s(%d)\n", obj.cfg_path.c_str(), str_EditSG.c_str(), mms_m_err_str(err), err);
        return -1;
    }


    // write
    if(0 != mms_m_param_value_sync((*p_item), event))
    {
        LOG_E("set value failed");
        return -1;
    }

    IedConnection_writeObject(obj.run.con, &err, p_item->reference.c_str(), IEC61850_FC_SE, p_item->set_value);
    if(err != IED_ERROR_OK)
    {
        LOG_E("%s: %s write err:%s(%d)\n", obj.cfg_path.c_str(), p_item->reference.c_str(), mms_m_err_str(err), err);
        return -1;
    }


    // confirm
    if(NULL == obj.set_confirm)
    {
        obj.set_confirm = MmsValue_newBoolean(true);
    }

    IedConnection_writeObject(obj.run.con, &err, str_CnfEdit.c_str(), IEC61850_FC_SP, obj.set_confirm);
    if(err != IED_ERROR_OK)
    {
        LOG_E("%s: %s confirm err:%s(%d)\n", obj.cfg_path.c_str(), str_CnfEdit.c_str(), mms_m_err_str(err), err);
        return -1;
    }
    
    return 0;
}

static void mms_m_send_read_ao(stru_mms_m_obj &obj, stru_mms_m_event &event)
{
    IedClientError err;
    MmsValue *p_value = NULL;

    stru_mms_m_out_value out_val;

    std::vector<stru_point_item>::iterator it;

    stru_point_item *p_item = NULL;

    for(it = obj.p_cfg->point.ao.begin(); it!= obj.p_cfg->point.ao.end(); it++)
    {
        if(strlen(event.name) != 0 && it->name.compare(event.name) == 0)
        {
            p_item = &(*it);
        }
        else if(strlen(event.name) == 0)
        {
            p_item = &(*it);
        }
        else
        {
            continue;
        }

        if(NULL == (p_value = IedConnection_readObject(obj.run.con, &err, p_item->reference.c_str(), (FunctionalConstraint)p_item->fc)))
        {
            LOG_E("%s: name %s, ref %s read err:%s(%d)\n", 
                obj.cfg_path.c_str(), p_item->name.c_str(), p_item->reference.c_str(), mms_m_err_str(err), err);
            continue;
        }

        if(MMS_M_DEBUG_PRINT_ON == obj.debug_print_flag)
        {
            mms_m_show_MmsValue(p_value);
        }

        if(0 != mms_m_get_mmsValue(p_value, *p_item))
        {
            LOG_E("get mms value failed");
            MmsValue_delete(p_value);
            continue;
        }

        mms_m_get_local_time(out_val.time);
        out_val.p_value = it->value.p_val;
        out_val.type = it->type;
        out_val.quality = 0;

        snprintf(out_val.name, sizeof(out_val.name) - 1, "%s", it->name.c_str());
        snprintf(out_val.desc, sizeof(out_val.desc) - 1, "%s", it->desc.c_str());
        snprintf(out_val.reference, sizeof(out_val.reference) - 1, "%s", it->reference.c_str());
        out_val.reason = MMS_M_REASON_READ_PARAM; // call param
        out_val.app_fd = obj.obj_fd;

        mms_m_put_value(obj, out_val);

        MmsValue_delete(p_value);
    }
}

static void mms_m_command_termination_handler(void *parameter, ControlObjectClient connection)
{
    LastApplError lastApplError = ControlObjectClient_getLastApplError(connection);

    /* if lastApplError.error != 0 this indicates a CommandTermination- */
    if (lastApplError.error != 0) 
    {
        LOG_E("Received CommandTermination-. LastApplError: %s, addCause %s", 
            mms_m_control_err_str(lastApplError.error), mms_m_ctl_add_cause_str(lastApplError.addCause));
    }
    else
        LOG_I("Received CommandTermination+.");
}

static int mms_m_send_co_then_check_result(stru_mms_m_obj &obj, stru_point_item &item)
{
    IedClientError err;
    MmsValue* stVal = NULL;

    if(NULL != (stVal = IedConnection_readObject(obj.run.con, &err, item.reference.c_str(), (FunctionalConstraint)item.fc)))
    {
        bool state = MmsValue_getBoolean(stVal);
        MmsValue_delete(stVal);

        if(state == MmsValue_getBoolean(item.set_value))
        {
            return 0;
        }

        LOG_E("%s: %s direct then read stVal(%d) is not equal to set value(%d)\n", obj.cfg_path.c_str(), item.reference.c_str(), state, MmsValue_getBoolean(item.set_value));
    }
    else
    {
        LOG_E("%s: %s direct then read stVal err:%s(%d)\n", obj.cfg_path.c_str(), item.reference.c_str(), mms_m_err_str(err), err);
    }

    return -1;
}

static int mms_m_send_co_select(stru_mms_m_obj &obj, stru_point_item &item)
{
    int ret = -1;

    if(NULL == item.control)
    {
        LOG_E("%s: %s control is not init\r\n", obj.cfg_path.c_str(), item.name.c_str());
        return ret;
    }

    if(CONTROL_MODEL_DIRECT_NORMAL == item.ctrl_model || CONTROL_MODEL_DIRECT_ENHANCED == item.ctrl_model)
    {
        LOG_E("%s: %s control model is %s(%d)\r\n", obj.cfg_path.c_str(), item.name.c_str(), mms_m_ctrl_model_str((ControlModel)item.ctrl_model), item.ctrl_model);
        return ret;
    }

    if(CONTROL_MODEL_SBO_NORMAL == item.ctrl_model && ControlObjectClient_select(item.control))
    {
        ret = 0;
    }
    else if(CONTROL_MODEL_SBO_ENHANCED == item.ctrl_model)
    {
        ControlObjectClient_setCommandTerminationHandler(item.control, mms_m_command_termination_handler, NULL);

        if(MMS_M_DEBUG_PRINT_ON == obj.debug_print_flag)
        {
            mms_m_show_MmsValue(item.set_value);
        }

        if (ControlObjectClient_selectWithValue(item.control, item.set_value)) 
        {
            ret = 0;
        }
    }   
    else
    {
        LOG_E("%s: %s control model is %s(%d)\r\n", obj.cfg_path.c_str(), item.name.c_str(), mms_m_ctrl_model_str((ControlModel)item.ctrl_model), item.ctrl_model);
    }

    if(0 == ret)
    {
        LOG_I("%s: co name %s, desc %s, ref %s  selected successfully, ctrl_model %s(%d)\n", 
            obj.cfg_path.c_str(), item.name.c_str(), item.desc.c_str(), item.reference.c_str(), mms_m_ctrl_model_str((ControlModel)item.ctrl_model), item.ctrl_model);
    }
    else
    {
        LOG_E("%s: failed to select %s\n", obj.cfg_path.c_str(), item.reference.c_str());
    }

    return ret;
}

static int mms_m_send_co_direct(stru_mms_m_obj &obj, stru_point_item &item)
{
    IedClientError err;
    int ret = -1;
    MmsValue* stVal = NULL;
        
    if(NULL == item.control)
    {
        LOG_E("%s: %s control is not init\r\n", obj.cfg_path.c_str(), item.name.c_str());
        return ret;
    }

    if(CONTROL_MODEL_DIRECT_ENHANCED == item.ctrl_model || CONTROL_MODEL_SBO_ENHANCED == item.ctrl_model)
    {
        ControlObjectClient_setCommandTerminationHandler(item.control, mms_m_command_termination_handler, NULL);
    }

    if (ControlObjectClient_operate(item.control,  item.set_value, 0 /* operate now */))
    {
        LOG_I("%s: co name %s, desc %s, ref %s  operated successfully\n", obj.cfg_path.c_str(), item.name.c_str(), item.desc.c_str(), item.reference.c_str());
        ret = 0;
    }
    else 
    {
        LOG_E("%s: failed to direct %s!\n", obj.cfg_path.c_str(), item.reference.c_str());
    }

    switch(item.ctrl_model)
    {
        case CONTROL_MODEL_DIRECT_NORMAL:
            ret = mms_m_send_co_then_check_result(obj, item);
            break;  
        case CONTROL_MODEL_SBO_NORMAL:
            break;
        case CONTROL_MODEL_DIRECT_ENHANCED:
            Thread_sleep(1000);
            ret = mms_m_send_co_then_check_result(obj, item);
            break;
        case CONTROL_MODEL_SBO_ENHANCED:
            Thread_sleep(1000);
            break;
        default:
            LOG_E("%s: %s control model is %s(%d)\r\n", obj.cfg_path.c_str(), item.name.c_str(), mms_m_ctrl_model_str((ControlModel)item.ctrl_model), item.ctrl_model);
            ret = -1;
            break;
    }

    return ret;
}

static int mms_m_send_co_cancel(stru_mms_m_obj &obj, stru_point_item &item)
{
    if(ControlObjectClient_cancel(item.control))
    {
        return 0;
    }

    LOG_E("%s: failed to cancel %s\n", obj.cfg_path.c_str(), item.reference.c_str());

    return -1;
}

static int mms_m_send_co(stru_mms_m_obj &obj, stru_mms_m_event &event)
{
    std::vector<stru_point_item>::iterator it;

    for(it = obj.p_cfg->point.co.begin(); it!= obj.p_cfg->point.co.end(); it++)
    {
        if(it->name.compare(event.name) == 0)
        {
            break;
        }
    }
    if(it == obj.p_cfg->point.co.end())
    {
        LOG_E("%s:co name(%s) is not exist", obj.cfg_path.c_str(), event.name);
        return -1;
    }

    if(NULL == it->control )
    {
        if(NULL != (it->control = ControlObjectClient_create(it->reference.c_str(), obj.run.con)))
        {
            ControlObjectClient_setOrigin(it->control, obj.cfg_path.c_str(), CONTROL_ORCAT_STATION_CONTROL);
        }
        else
        {
            LOG_E("%s: create control object failed, ref(%s)", obj.cfg_path.c_str(), it->reference.c_str());
            return -1;
        }
    }

    // bool b = (MY_GET_DATA_WITH_TYPE(((void *)event.val), uint8_t) > 0) ? true : false;
    bool b = atoi(event.val) > 0 ? true : false;

    if(it->set_value == NULL)
    {
        it->set_value = MmsValue_newBoolean(b);
    }
    else
    {
        MmsValue_setBoolean(it->set_value, b);
    }

    LOG_I("mms_m_send_co b %d, set_value %d", b, MmsValue_getBoolean(it->set_value));

    switch(event.ctrl_type)
    {
        case _MMS_M_EVENT_CO_SELECT:
            return mms_m_send_co_select(obj, (*it));
        case _MMS_M_EVENT_CO_DIRECT:
            return mms_m_send_co_direct(obj, (*it));
        case _MMS_M_EVENT_CO_CANCEL:
            return mms_m_send_co_cancel(obj, (*it));
        default:
            LOG_E("%s: ctrl_type(%d) is not support", obj.cfg_path.c_str(), event.ctrl_type);
            return -1;
    }
}

static void mms_m_send_set_callback(stru_mms_m_event &event, int ret)
{
    // int arg = ret;

    // if(event.p_func && event.arg)
    if(event.p_func)
    {
        // memcpy((int *)event.arg, &arg, sizeof(int));
        event.p_func(&event, ret);
    }
}

static void mms_m_send_set_gi(stru_mms_m_obj &obj)
{
    IedClientError err;

    std::vector<stru_ld_dataset>::iterator it_ld_ds;
    std::vector<stru_ln_rpt>::iterator it_ln_rpt;

    for(it_ld_ds = obj.ld_datasets.begin(); it_ld_ds!= obj.ld_datasets.end(); it_ld_ds++)
    {
        for(it_ln_rpt = it_ld_ds->ln_rpts.begin(); it_ln_rpt!= it_ld_ds->ln_rpts.end(); it_ln_rpt++)
        {
            if(it_ln_rpt->rcb == NULL)
            {
                continue;
            }

            ClientReportControlBlock_setGI(it_ln_rpt->rcb, true);
            IedConnection_setRCBValues(obj.run.con, &err, it_ln_rpt->rcb, RCB_ELEMENT_GI, true);

            if(err != IED_ERROR_OK)
            {
                LOG_E("set gi failed, err(%d:%s)", err, mms_m_err_str(err));
            }
        }
    }
}

static void mms_m_send_call_all(stru_mms_m_obj &obj)
{
    std::vector<stru_point_item> temp_list[2] = {obj.p_cfg->point.st, obj.p_cfg->point.mx};
    std::vector<stru_point_item>::iterator it;

    IedClientError err;
    MmsValue *p_value = NULL;

    stru_mms_m_out_value out_val;

    for(int i = 0; i < 2; i++)
    {
        for(it = temp_list[i].begin(); it!= temp_list[i].end(); it++)
        {
            if(NULL == (p_value  = IedConnection_readObject(obj.run.con, &err, it->reference.c_str(), (FunctionalConstraint)it->fc)))
            {
                LOG_E("read object %s failed, err(%d:%s)", it->reference.c_str(), err, mms_m_err_str(err));
                continue;
            }

            if(MMS_M_DEBUG_PRINT_ON == obj.debug_print_flag)
            {
                mms_m_show_MmsValue(p_value);
            }

            if(0 != mms_m_get_mmsValue(p_value, *it))
            {
                LOG_E("get mms value failed");
                MmsValue_delete(p_value);
                continue;
            }

            mms_m_get_local_time(out_val.time);
            out_val.p_value = it->value.p_val;
            out_val.type = it->type;
            out_val.quality = 0;

            snprintf(out_val.name, sizeof(out_val.name) - 1, "%s", it->name.c_str());
            snprintf(out_val.desc, sizeof(out_val.desc) - 1, "%s", it->desc.c_str());
            snprintf(out_val.reference, sizeof(out_val.reference) - 1, "%s", it->reference.c_str());
            out_val.reason = MMS_M_REASON_ALL_CALL; // call all
            out_val.app_fd = obj.obj_fd;

            mms_m_put_value(obj, out_val);

            MmsValue_delete(p_value);
        }
    }
}

static void mms_m_do_send(stru_mms_m_obj &obj)
{
    stru_mms_m_event &event = obj.run.event;

    while(0 == mms_m_pop_event(obj, event))
    {
        switch(event.ctrl_type)
        {
            case _MMS_M_EVENT_ALL_CALL:
                mms_m_send_call_all(obj);
                break;
            case _MMS_M_EVENT_GI_CALL:
                mms_m_send_set_gi(obj);
                break;
            case _MMS_M_EVENT_CO_SELECT:
            case _MMS_M_EVENT_CO_DIRECT:
            case _MMS_M_EVENT_CO_CANCEL:
                mms_m_send_set_callback(event, mms_m_send_co(obj, event));
                break;
            case _MMS_M_EVENT_AO_READ:
                mms_m_send_read_ao(obj, event);
                break;
            case _MMS_M_EVENT_AO_WRITE:
                mms_m_send_set_callback(event, mms_m_send_ao_write(obj, event));
                break;
            default:
                LOG_E("%s, unknown ctrl_type(%d)", obj.cfg_path.c_str(), event.ctrl_type);
                break;
        }

        memset(&event, 0, sizeof(stru_mms_m_event));
    }
}

static void mms_m_get_local_time(stru_mms_m_time &time)
{
    struct timeval tv;
    struct tm *tt;

	gettimeofday(&tv , NULL);
	tt = localtime(&tv.tv_sec);

    time.year = tt->tm_year + 1900;
    time.mon = tt->tm_mon + 1;
    time.day = tt->tm_mday;
    time.hour = tt->tm_hour;
    time.min = tt->tm_min;
    time.sec = tt->tm_sec;
    time.msec = tv.tv_usec / 1000;
}

static void mms_m_get_report_time(const std::string &str, stru_mms_m_time &time)
{
    regex_t regex;
    int ret;
    
    // 编译正则表达式
    ret = regcomp(&regex, "^[0-9]{4}[0-9]{2}[0-9]{2}[0-9]{2}[0-9]{2}[0-9]{2}\\.[0-9]{3}Z$", REG_EXTENDED);
    if (ret) 
    {
        LOG_E("regcomp error %s\r\n", str.c_str());
        return;
    }
    
    // 执行匹配
    ret = regexec(&regex, str.c_str(), 0, NULL, 0);
    regfree(&regex);
    
    if (ret != 0) 
    {
        LOG_E("regexec error %s\r\n", str.c_str());
        return;
    }
    
    // 如果格式匹配，再进行解析
    sscanf(str.c_str(), "%04d%02d%02d%02d%02d%02d.%03d", &time.year, &time.mon, &time.day, &time.hour, &time.min, &time.sec, &time.msec);
}

static int mms_m_get_MmsValue(stru_point_value &point_value, stru_mms_m_out_value &out_val, MmsValue *p_mms_values, int &tm_flag, int print_flag)
{
    if(NULL == p_mms_values)
    {
        LOG_E("MMS value is NULL\r\n");
        return -1;
    }
    std::string temp_str;

    switch(MmsValue_getType(p_mms_values))
    {
        case MMS_STRUCTURE:
        case MMS_ARRAY:
            // if(MMS_M_DEBUG_PRINT_ON == print_flag)
            // {
                // LOG_I("array or struct size %d\r\n", MmsValue_getArraySize(p_mms_values));
            // }
            for(int i = 0; i < MmsValue_getArraySize(p_mms_values); i++)
            {
                if(0 != mms_m_get_MmsValue(point_value, out_val, MmsValue_getElement(p_mms_values, i), tm_flag, print_flag))
                {
                    return -1;
                }
            }
            break;
        case MMS_BOOLEAN:
            out_val.type = MMS_BOOLEAN;
            if (MmsValue_getBoolean(p_mms_values)) 
            {
                // point_value.val.b = true;
                *(uint8_t *)point_value.p_val = 1;
            }
            else
            {
                // point_value.val.b = false;
                *(uint8_t *)point_value.p_val = 0;
            }
            break;
        case MMS_INTEGER:
            out_val.type = MMS_INTEGER;
            // point_value.val.l = MmsValue_toInt32(p_mms_values);
            *(int32_t *)point_value.p_val = MmsValue_toInt32(p_mms_values);
            break;
        case MMS_UNSIGNED:
            out_val.type = MMS_UNSIGNED;
            // point_value.val.lu = MmsValue_toUint32(p_mms_values);
            *(uint32_t *)point_value.p_val = MmsValue_toUint32(p_mms_values);
            break;
        case MMS_FLOAT:
            out_val.type = MMS_FLOAT;
            // point_value.val.f = MmsValue_toFloat(p_mms_values);
            *(float *)point_value.p_val = MmsValue_toFloat(p_mms_values);
            break;
        case MMS_BINARY_TIME:
            temp_str.clear();
            Conversions_msTimeToGeneralizedTime(MmsValue_getBinaryTimeAsUtcMs(p_mms_values), (uint8_t *)temp_str.c_str());
            mms_m_get_report_time(temp_str, point_value.time);
            tm_flag = 1;
            break;
        case MMS_UTC_TIME:
            temp_str.clear();
            Conversions_msTimeToGeneralizedTime(MmsValue_getUtcTimeInMs(p_mms_values), (uint8_t *)temp_str.c_str());
            mms_m_get_report_time(temp_str, point_value.time);
            tm_flag = 1;
            break;
        case MMS_BIT_STRING:
            point_value.quality = 0;
            for (int i = 0; i < MmsValue_getBitStringSize(p_mms_values); i++) 
            {
                if (MmsValue_getBitStringBit(p_mms_values, i))
                {
                    point_value.quality |= (1 << i);
                }
                else
                {
                    point_value.quality &= ~(1 << i);
                }
            }
            break;    
        default:
            break;
    }

    return 0;
}

static void mms_m_report_callback(void* parameter, ClientReport report)
{
    MmsValue *dataset_value = ClientReport_getDataSetValues(report);
    stru_ln_rpt *p_ln_rpt = (stru_ln_rpt *)parameter;
    stru_mms_m_obj *p_obj = (stru_mms_m_obj *)p_ln_rpt->p_app;
    if(NULL == p_obj)
    {
        LOG_E("mms_m_report_callback parameter(%p), report(%p), p_obj(%p)", parameter, report, p_obj);
    }

    if(NULL == dataset_value || NULL == p_ln_rpt)
    {
        LOG_E("mms_m_report_callback parameter(%p), report(%p)", dataset_value, p_ln_rpt);
        return;
    }

    int index = 0;
    std::vector<stru_member>::iterator it_mb;
    std::vector<stru_point_item *>::iterator it;

    for(it_mb = p_ln_rpt->p_dataset->members.begin(); it_mb!= p_ln_rpt->p_dataset->members.end(); it_mb++)
    {
        ReasonForInclusion reason = ClientReport_getReasonForInclusion(report, index);
        if(IEC61850_REASON_NOT_INCLUDED != reason)
        {
            char value_buffer[512] = "no value";
            MmsValue *mms_value = NULL;

            if(dataset_value && (NULL != (mms_value = MmsValue_getElement(dataset_value, index))))
            {
                MmsValue_printToBuffer(mms_value, value_buffer, sizeof(value_buffer));
            }

            it_mb->reason = reason;

            if(MMS_M_DEBUG_PRINT_ON == p_obj->debug_print_flag)
            {
                printf("  %s (included for reason %i): %s\r\n", it_mb->ref.c_str(), reason, value_buffer);
            }

            int tm_flag = 0;
            stru_mms_m_out_value out_value;
            stru_point_value point_value;
            memset(&out_value, 0, sizeof(out_value));
            memset(&point_value, 0, sizeof(point_value));
            char temp[256] = {0};
            point_value.p_val = (void *)temp;

            mms_m_get_MmsValue(point_value, out_value, mms_value, tm_flag, p_obj->debug_print_flag);

            if(!tm_flag)
            {
                mms_m_get_local_time(point_value.time);
            }

            out_value.reason = reason;
            out_value.p_value = point_value.p_val;
            out_value.app_fd = p_obj->obj_fd;
            out_value.time = point_value.time;

            if(it_mb->p_do_vec)
            {
                for(it = it_mb->p_do_vec->begin(); it!= it_mb->p_do_vec->end(); it++)
                {
                    snprintf(out_value.name, MMS_M_STR_LEN - 1, "%s", (*it)->name.c_str());
                    snprintf(out_value.desc, MMS_M_STR_LEN - 1, "%s", (*it)->desc.c_str());
                    snprintf(out_value.reference, MMS_M_REF_LEN - 1, "%s", (*it)->reference.c_str());

                    mms_m_put_value((*p_obj), out_value);
                }
            }
        }

        index++;
    }
}

static int mms_m_rcb_init(stru_mms_m_obj &obj)
{
	IedClientError err;
	IedConnection p_con = obj.run.con;

    std::vector<stru_ld_dataset>::iterator it_ld_ds;
    std::vector<stru_ln_dataset>::iterator it_ln_ds;
    std::vector<stru_ln_rpt>::iterator it_ln_rpt;

    for(it_ld_ds = obj.ld_datasets.begin(); it_ld_ds!= obj.ld_datasets.end(); it_ld_ds++)
    {
        for(it_ln_rpt = it_ld_ds->ln_rpts.begin(); it_ln_rpt!= it_ld_ds->ln_rpts.end(); it_ln_rpt++)
        {
            it_ln_rpt->rcb = IedConnection_getRCBValues(p_con, &err, it_ln_rpt->rpt_ref_with_no.c_str(), NULL);
            if (NULL == it_ln_rpt->rcb)
            {
                LOG_E("%s: ref_with_no %s, p_rpt->rcb %p, err %s, %d\r\n", 
                    obj.cfg_path.c_str(), it_ln_rpt->rpt_ref_with_no.c_str(), it_ln_rpt->rcb, mms_m_err_str(err), err);
                continue;
            }
            
            it_ln_rpt->ds_ref = ClientReportControlBlock_getDataSetReference(it_ln_rpt->rcb);

            for(it_ln_ds = it_ld_ds->ln_datasets.begin(); it_ln_ds!= it_ld_ds->ln_datasets.end(); it_ln_ds++)
            {
                if(it_ln_ds->dataset_ref.compare(it_ln_rpt->ds_ref) == 0)
                {
                    it_ln_rpt->p_dataset = &(*it_ln_ds);
                    break;
                }
            }
            it_ln_rpt->p_app = (void *)&obj;

            bool rpt_ena = ClientReportControlBlock_getRptEna(it_ln_rpt->rcb);

            ClientReportControlBlock_setResv(it_ln_rpt->rcb, true);
            ClientReportControlBlock_setTrgOps(it_ln_rpt->rcb, TRG_OPT_DATA_CHANGED | TRG_OPT_QUALITY_CHANGED | TRG_OPT_GI);

            ClientReportControlBlock_setDataSetReference(it_ln_rpt->rcb, it_ln_rpt->ds_ref.c_str());
            ClientReportControlBlock_setRptEna(it_ln_rpt->rcb, true);

            /* Install handler for reports */
            IedConnection_installReportHandler(p_con,  it_ln_rpt->rpt_ref.c_str(), ClientReportControlBlock_getRptId(it_ln_rpt->rcb), mms_m_report_callback, (void *)&(*it_ln_rpt));
            IedConnection_setRCBValues(p_con, &err, it_ln_rpt->rcb, RCB_ELEMENT_RPT_ENA | RCB_ELEMENT_TRG_OPS | RCB_ELEMENT_INTG_PD | RCB_ELEMENT_GI, true);

            ClientReportControlBlock_setGI(it_ln_rpt->rcb, true);
            IedConnection_setRCBValues(p_con, &err, it_ln_rpt->rcb, RCB_ELEMENT_GI, true);

            if(err != IED_ERROR_OK)
            {
                LOG_E("%s:ref %s, err %d, %s\r\n", obj.cfg_path.c_str(), it_ln_rpt->rpt_ref_with_no.c_str(), err, mms_m_err_str(err));
            }
        }
    }

    return 0;    
}

static void mms_m_ld_dataset_match_point_init(stru_mms_m_obj &obj)
{
    std::vector<stru_ld_dataset>::iterator it_ld_ds;
    std::vector<stru_ln_dataset>::iterator it_ln_ds;
    std::vector<stru_member>::iterator it_mb;

    for(it_ld_ds = obj.ld_datasets.begin(); it_ld_ds!= obj.ld_datasets.end(); it_ld_ds++)
    {
        for(it_ln_ds = it_ld_ds->ln_datasets.begin(); it_ln_ds!= it_ld_ds->ln_datasets.end(); it_ln_ds++)
        {
            for(it_mb = it_ln_ds->members.begin(); it_mb!= it_ln_ds->members.end(); it_mb++)
            {
                std::string ld, ln, d_name, ref;
                ref = it_mb->ref;
                
                size_t pos1 = ref.find("/");
                if(pos1 == std::string::npos || pos1 >= ref.length() - 1)
                {
                    continue;
                }

                ld = ref.substr(0, pos1);

                size_t pos2 = ref.find(".", pos1 + 1);
                if(pos2 == std::string::npos || pos2 >= ref.length() - 1)
                {
                    continue;
                }

                ln = ref.substr(pos1 + 1, pos2 - pos1 - 1);

                size_t pos3 = ref.find("[", pos2 + 1);
                if(pos3 == std::string::npos || pos3 >= ref.length() - 1)
                {
                    continue;
                }

                d_name = ref.substr(pos2 + 1, pos3 - pos2 - 1);

                if(!obj.ied_name.empty() && ld.find(obj.ied_name) == 0)
                {
                    ld.erase(0, obj.ied_name.length());
                }
                
                bool found = false;

                for(uint32_t i_ld = 0; !found && i_ld < obj.ldevs.size(); i_ld++)
                {
                    for(uint32_t i_ln = 0; !found &&  0 == obj.ldevs[i_ld].ld_name.compare(ld) && i_ln < obj.ldevs[i_ld].lnodes.size(); i_ln++)
                    {
                        for(uint32_t i_do = 0; !found && 0 == obj.ldevs[i_ld].lnodes[i_ln].ln_name.compare(ln) && i_do < obj.ldevs[i_ld].lnodes[i_ln].dobjs.size(); i_do++)
                        {
                            if(0 == obj.ldevs[i_ld].lnodes[i_ln].dobjs[i_do].do_name.compare(d_name))
                            {
                                it_mb->p_do_vec = &obj.ldevs[i_ld].lnodes[i_ln].dobjs[i_do].p_do_vec;
                                found = true;
                            }
                        }
                    }
                }
            }
        }
    }
}

static int mms_m_icd_dataset_init(stru_mms_m_obj &obj, stru_ld_dataset &ld_ds)
{
    IedClientError err;
    IedConnection p_con = obj.run.con;

    LinkedList dataSets = IedConnection_getLogicalNodeDirectory(p_con, &err, ld_ds.ref.c_str(), ACSI_CLASS_DATA_SET);
    if(IED_ERROR_OK != err)
    {
        LOG_E("mms_m_icd_dataset_init err(%s:%d)", mms_m_err_str(err), err);
    }
    LinkedList dataSet = LinkedList_getNext(dataSets);

    while (dataSet != NULL) 
    {
        bool isDeletable;

        stru_ln_dataset ln_dataset;
        memset(&ln_dataset, 0, sizeof(ln_dataset));

        ln_dataset.dataset_name = ld_ds.ref + "." + (char*) dataSet->data;
        ln_dataset.dataset_ref = ld_ds.ref + "$" + (char*) dataSet->data;

        LinkedList dataSetMembers = IedConnection_getDataSetDirectory(p_con, &err, ln_dataset.dataset_name.c_str(), &isDeletable);
        if(IED_ERROR_OK != err)
        {
            LOG_E("mms_m_icd_dataset_init err(%s:%d)", mms_m_err_str(err), err);
        }

        if(MMS_M_DEBUG_PRINT_ON == obj.debug_print_flag)
        {
            if (isDeletable)
            {
                printf("	Data set: %s (deletable)\n", ln_dataset.dataset_name.c_str());
            }
            else
            {
                printf("	Data set: %s (not deletable)\n", ln_dataset.dataset_name.c_str());
            }
        }
        LinkedList dataSetMemberRef = LinkedList_getNext(dataSetMembers);
        while (dataSetMemberRef != NULL) 
        {
            stru_member member;
            memset(&member, 0, sizeof(member));

            member.ref = (char*) dataSetMemberRef->data;
            ln_dataset.members.push_back(member);
            dataSetMemberRef = LinkedList_getNext(dataSetMemberRef);
        }   
        LinkedList_destroy(dataSetMembers);

        ld_ds.ln_datasets.push_back(ln_dataset);
        dataSet = LinkedList_getNext(dataSet);
    }

    LinkedList_destroy(dataSets);

    return 0;
}

static int mms_m_icd_report_init(stru_mms_m_obj &obj, stru_ld_dataset &ld_ds, ACSIClass acsiClass)
{
    if(ACSI_CLASS_URCB != acsiClass && ACSI_CLASS_BRCB != acsiClass)
    {
        LOG_E("%s:mms_m_init_icd_report error\n", obj.cfg_path.c_str());
        return -1;
    }

    std::string rpt_ref_str = (ACSI_CLASS_URCB == acsiClass) ? "RP" : "BR";

    IedClientError err;
    IedConnection p_con = obj.run.con;
  
    LinkedList reports = IedConnection_getLogicalNodeDirectory(p_con, &err, ld_ds.ref.c_str(), acsiClass);
    LinkedList report = LinkedList_getNext(reports);

    while (report != NULL) 
    {
        std::string rpt_ref = "";
        std::string rpt_name = "";
        std::string rpt_no = "";
        std::string rpt_data = (char*) report->data;

        if(rpt_data.length() < 2)
        {
            continue;
        }

        // 提取名称和编号
        rpt_name = rpt_data.substr(0, rpt_data.length() - 2);
        rpt_no = rpt_data.substr(rpt_data.length() - 2);

        rpt_ref = ld_ds.ref + "." + rpt_ref_str + ".";

        stru_ln_rpt ln_rpt;
        memset(&ln_rpt, 0, sizeof(ln_rpt));
        ln_rpt.type = acsiClass;

        if(0 == rpt_no.compare("01"))
        {
            ln_rpt.rpt_ref = rpt_ref + rpt_name;
            ln_rpt.rpt_ref_with_no = rpt_ref + rpt_name + rpt_no;

            ld_ds.ln_rpts.push_back(ln_rpt);

            if(MMS_M_DEBUG_PRINT_ON == obj.debug_print_flag)
            {
                printf("	RP: %s\n", (char*) report->data);
            }
        }
        else
        {
            // 其他缓冲 暂时不管
        }

        report = LinkedList_getNext(report);
    }

    LinkedList_destroy(reports);

    return 0;
}

static int mms_m_icd_init(stru_mms_m_obj &obj)
{
    IedClientError err;
    stru_mms_m_run &run = obj.run;
    IedConnection p_con = run.con;

    if(MMS_M_DEBUG_PRINT_ON == obj.debug_print_flag)
    {
        printf("Get logical devices list ...\n");
    }

    LinkedList deviceList = IedConnection_getLogicalDeviceList(p_con, &err);

    if (err != IED_ERROR_OK) 
    {
		LOG_E("%s:Failed to read device list (error %s, %d)\n", obj.cfg_path.c_str(), mms_m_err_str(err), err);
		return -1;
	}

    LinkedList device = LinkedList_getNext(deviceList);
    while (device != NULL) 
    {
        if(MMS_M_DEBUG_PRINT_ON == obj.debug_print_flag)
        {
            printf("LD: %s\n", (char*) device->data);
        }

		LinkedList logicalNodes = IedConnection_getLogicalDeviceDirectory(p_con, &err, (char*) device->data);
		LinkedList logicalNode = LinkedList_getNext(logicalNodes);

		while (logicalNode != NULL) 
        {
            if(MMS_M_DEBUG_PRINT_ON == obj.debug_print_flag)
            {
                printf("  LN: %s\n", (char*) logicalNode->data);
            }

            stru_ld_dataset ld_dataset;
            memset(&ld_dataset, 0, sizeof(ld_dataset));

            ld_dataset.ld_name = (char *)device->data;
            ld_dataset.ln_name = (char *)logicalNode->data;
            ld_dataset.ref = ld_dataset.ld_name + "/" + ld_dataset.ln_name;
            
            if(0 != mms_m_icd_dataset_init(obj, ld_dataset))
            {
                LOG_E("%s:mms_m_init_icd_dataset error\n", obj.cfg_path.c_str());
                return -1;
            }

            if(0 != mms_m_icd_report_init(obj, ld_dataset, ACSI_CLASS_URCB))
            {
                LOG_E("%s:mms_m_init_icd_report ACSI_CLASS_URCB error\n", obj.cfg_path.c_str());
                return -1;
            }

            if(0 != mms_m_icd_report_init(obj, ld_dataset, ACSI_CLASS_BRCB))
            {
                LOG_E("%s:mms_m_init_icd_report ACSI_CLASS_BRCB error\n", obj.cfg_path.c_str());
                return -1;
            }

            obj.ld_datasets.push_back(ld_dataset);
            logicalNode = LinkedList_getNext(logicalNode);
        }

        LinkedList_destroy(logicalNodes);
        device = LinkedList_getNext(device);
    }

    LinkedList_destroy(deviceList);

    return 0;    
}

static int mms_m_run_init(stru_mms_m_obj &obj)
{
    if(obj.run.running_init)
    {
        return 0;
    }

    if(0 != mms_m_icd_init(obj))
    {
        LOG_E("%s: mms_m_icd_init failed", obj.cfg_path.c_str());
        return -1;
    }
    
    mms_m_ld_dataset_match_point_init(obj);

    if(0 != mms_m_rcb_init(obj))
    {
        LOG_E("%s: mms_m_rcb_init failed", obj.cfg_path.c_str());
        return -1;
    }
    
    obj.run.running_init = true;
    return 0;
}

static void mms_m_run(stru_mms_m_obj &obj)
{
    stru_mms_m_run &run = obj.run;
    if(IED_STATE_CONNECTED != run.con_state)
    {
        return;
    }

    if(0 != mms_m_run_init(obj))
    {
        LOG_E("%s: mms_m_run_init failed", obj.cfg_path.c_str());
        return;
    }

    mms_m_do_send(obj);
    mms_m_timer_running(obj);
}

static void mms_m_control_init(stru_mms_m_obj &obj)
{
    std::vector<stru_point_item>::iterator it;
    
    for(it = obj.p_cfg->point.co.begin(); it!= obj.p_cfg->point.co.end(); it++)
    {
        if(it->control)
        {
            ControlObjectClient_destroy(it->control);
            it->control = NULL;
        }

        if(NULL != (it->control = ControlObjectClient_create(it->reference.c_str(), obj.run.con)))
        {
            ControlObjectClient_setOrigin(it->control, obj.cfg_path.c_str(), CONTROL_ORCAT_STATION_CONTROL);
        }
        else
        {
            LOG_E("%s: ControlObjectClient_create failed, %s", obj.cfg_path.c_str(), it->reference.c_str());
        }

        if(!it->set_value)
        {
            it->set_value = MmsValue_newBoolean(false);
        }
    }
}

static void mms_m_do_comm(stru_mms_m_obj &obj)
{
    IedClientError err;
    stru_mms_m_run &run = obj.run;

    run.con_state = IedConnection_getState(run.con);

    if(IED_STATE_CONNECTED != run.con_state && IED_STATE_CONNECTED != run.old_con_state)
    {
        IedConnection_connectAsync(run.con, &err, run.ip.c_str(), run.port);
        if(IED_ERROR_OK != err)
        {
            LOG_E("mms_m %s, err (%s:%d)", obj.cfg_path.c_str(), mms_m_err_str(err), err);
        }

        LOG_I("mms_m %s connect %s:%d", obj.ied_name.c_str(), obj.run.ip.c_str(), obj.run.port);
    }

    if(IED_STATE_CONNECTED == run.con_state && IED_STATE_CONNECTED != run.old_con_state)
    {
        LOG_I("mms_m %s connect %s:%d success", obj.ied_name.c_str(), obj.run.ip.c_str(), obj.run.port);

        mms_m_control_init(obj);

        if(run.out_status_cb)
        {
            run.out_status_cb(obj.obj_fd, MMS_M_ON_LINE);
        }
    }

    if(IED_STATE_CONNECTED != run.con_state && IED_STATE_CONNECTED == run.old_con_state)
    {
        LOG_I("mms_m %s connected %s:%d offline", obj.ied_name.c_str(), obj.run.ip.c_str(), obj.run.port);

        if(run.out_status_cb)
        {
            run.out_status_cb(obj.obj_fd, MMS_M_OFF_LINE);
        }
    }

    run.old_con_state = run.con_state;
}

static void mms_m_timer_running(stru_mms_m_obj &obj)
{
    stru_mms_m_timer *p_timer = obj.run.timer;

    for(int i = 0; i < _MMS_M_TIMER_END; i++)
    {
        if(p_timer[i].running)
        {
            p_timer[i].cnt--;
            if(p_timer[i].cnt == 0)
            {
                p_timer[i].running = false;
                if(p_timer[i].p_func)
                {
                    p_timer[i].p_func(p_timer[i].arg);
                }
            }
        }
    }
}

static void mms_m_timer_start(stru_mms_m_timer &timer)
{
    timer.running = true;
    timer.cnt = timer.tmout;
}

static void mms_m_do_call_all(void *arg)
{
    stru_mms_m_obj *p_obj = (stru_mms_m_obj *)arg;
    if(NULL == p_obj)
    {
        LOG_E("mms_m_do_call_all p_obj is NULL");
        return;
    }

    stru_mms_m_event event;

    event.ctrl_type = _MMS_M_EVENT_ALL_CALL;
    mms_m_push_event((*p_obj), event);
    mms_m_timer_start(p_obj->run.timer[_MMS_M_TIMER_T0]);
}

static void mms_m_do_call_GI(void *arg)
{
    stru_mms_m_obj *p_obj = (stru_mms_m_obj *)arg;
    if(NULL == p_obj)
    {
        LOG_E("mms_m_do_call_GI p_obj is NULL");
        return;
    }

    stru_mms_m_event event;

    event.ctrl_type = _MMS_M_EVENT_GI_CALL;
    mms_m_push_event((*p_obj), event);
    mms_m_timer_start(p_obj->run.timer[_MMS_M_TIMER_T1]);
}

static void mms_m_timer_init(stru_mms_m_obj &obj)
{
    stru_mms_m_timer *p_timer = obj.run.timer;

    for(int i = 0; i < _MMS_M_TIMER_END; i++)
    {
        p_timer[i].cnt = 0;
        p_timer[i].running = false;
    }

    p_timer[_MMS_M_TIMER_T0].tmout = MMS_M_TIMER_T0;
    p_timer[_MMS_M_TIMER_T0].arg = &obj;
    p_timer[_MMS_M_TIMER_T0].p_func = mms_m_do_call_all;


    p_timer[_MMS_M_TIMER_T1].tmout = MMS_M_TIMER_T1;
    p_timer[_MMS_M_TIMER_T1].arg = &obj;
    p_timer[_MMS_M_TIMER_T1].p_func = mms_m_do_call_GI;


    p_timer[_MMS_M_TIMER_T2].tmout = MMS_M_TIMER_T2;
    p_timer[_MMS_M_TIMER_T2].arg = NULL;
    p_timer[_MMS_M_TIMER_T2].p_func = NULL;

    p_timer[_MMS_M_TIMER_T3].tmout = MMS_M_TIMER_T3;
    p_timer[_MMS_M_TIMER_T3].arg = NULL;
    p_timer[_MMS_M_TIMER_T3].p_func = NULL;

    for(int i = 0; i < _MMS_M_TIMER_END; i++)
    {
        mms_m_timer_start(p_timer[i]);
    }
}

static void mms_m_state_changed_handler(void *arg, IedConnection con, IedConnectionState newState)
{
    IedClientError err;
    stru_mms_m_obj *p_obj = (stru_mms_m_obj *)arg;
    if(NULL == p_obj)
    {
        LOG_E("mms_m_state_changed_handler p_obj is NULL");
        return;
    }
    stru_mms_m_run &run = p_obj->run;

    switch(newState)
    {
        case IED_STATE_CLOSED:
            LOG_E("IED connection closed");
            break;
        case IED_STATE_CLOSING:
            LOG_I("IED connection closing");
            break;
        case IED_STATE_CONNECTING:
            LOG_I("IED connection connecting");
            break;
        case IED_STATE_CONNECTED:
            LOG_I("IED connection connected");
            break;
        default:
            break;
    }
}

static void *mms_m_run_thread(void *arg)
{
    if(NULL == arg)
    {
        LOG_E("mms_m_run_thread arg is NULL");
        return NULL;
    }

    stru_mms_m_obj *obj = (stru_mms_m_obj *)arg;

    LOG_I("obj %p", obj);
    mms_m_timer_init(*obj);

    stru_event_queue &queue = obj->run.event_queue;
    
    queue.r_ptr = 0;
    queue.w_ptr = 0;
    queue.size = EVENT_QUEUE_SIZE;
    queue.num = 0;
    memset(queue.event, 0, sizeof(stru_mms_m_event) * EVENT_QUEUE_SIZE);

    IedClientError err;
    
    obj->run.con = IedConnection_create();
    if(NULL == obj->run.con)
    {
        LOG_E("IedConnection_create failed");
        return NULL;
    }
    
    IedConnection_setConnectTimeout(obj->run.con, obj->connectionTimeout);
    IedConnection_installStateChangedHandler(obj->run.con, mms_m_state_changed_handler, (void *)obj);
    IedConnection_connectAsync(obj->run.con, &err, obj->run.ip.c_str(), obj->run.port);

    LOG_I("mms_m connect to %s:%d", obj->run.ip.c_str(), obj->run.port);
    if(IED_ERROR_OK != err)
    {
        LOG_E("%s: IedConnection_connectAsync failed, err (%s:%d)", obj->cfg_path.c_str(), mms_m_err_str(err), err);
    }

    while(1)
    {        
        mms_m_do_comm(*obj);
        if(IED_STATE_CONNECTED == IedConnection_getState(obj->run.con))
        {
            mms_m_run(*obj);
        }

        sleep_ms(MMS_M_THREAD_RUN_TM);
    }
}

static void mms_m_show_ied(stru_mms_m_obj &obj)
{
    printf("cfg %s, IED name: %s\n", obj.cfg_path.c_str(), obj.ied_name.c_str());

    for(std::vector<stru_ldev>::iterator it = obj.ldevs.begin(); it!= obj.ldevs.end(); it++)
    {
        printf("    LDEV name: %s\n", it->ld_name.c_str());

        for(std::vector<stru_lnode>::iterator it2 = it->lnodes.begin(); it2!= it->lnodes.end(); it2++)
        {
            printf("        LNODE name: %s\n", it2->ln_name.c_str());

            for(std::vector<stru_dobj>::iterator it3 = it2->dobjs.begin(); it3!= it2->dobjs.end(); it3++)
            {
                printf("            DO name:%s\n", it3->do_name.c_str());
                for(std::vector<stru_point_item *>::iterator it4 = it3->p_do_vec.begin(); it4!= it3->p_do_vec.end(); it4++)
                {
                    stru_point_item *p_item = *it4;
                    printf("                signal name: %s, type: %d, fc: %d, ctrl_model: %d, desc: %s, reference: %s\n", 
                        p_item->name.c_str(), p_item->type, p_item->fc, p_item->ctrl_model, p_item->desc.c_str(), p_item->reference.c_str());
                }
            }
        }
    }
}

static void mms_m_add_point_to_dobj(stru_lnode &lnode, stru_point_item &point)
{
    std::vector<stru_dobj>::iterator it;
    for(it = lnode.dobjs.begin(); it!= lnode.dobjs.end(); it++)
    {
        if(0 == it->do_name.compare(point.doname))
        {
            it->p_do_vec.push_back(&point);
            return;
        }
    }

    stru_dobj dobj;
    dobj.p_do_vec.push_back(&point);
    dobj.do_name = point.doname;
    lnode.dobjs.push_back(dobj);
}

static void mms_m_add_point_to_lnode(stru_ldev &ldev, stru_point_item &point)
{
    std::vector<stru_lnode>::iterator it;
    for(it = ldev.lnodes.begin(); it!= ldev.lnodes.end(); it++)
    {
        if(0 == it->ln_name.compare(point.lnode))
        {
            mms_m_add_point_to_dobj(*it, point);
            return;
        }
    }

    stru_lnode lnode;
    stru_dobj dobj;

    dobj.p_do_vec.push_back(&point);
    dobj.do_name = point.doname;
    lnode.dobjs.push_back(dobj);
    
    lnode.ln_name = point.lnode;
    ldev.lnodes.push_back(lnode);
}

static void mms_m_add_point_to_ldevs(stru_mms_m_obj &obj, stru_point_item &point)
{
    std::vector<stru_ldev>::iterator it;
    for(it = obj.ldevs.begin(); it!= obj.ldevs.end(); it++)
    {
        if(0 == it->ld_name.compare(point.ldev))
        {
            mms_m_add_point_to_lnode(*it, point);
            return;
        }
    }

    stru_ldev ldev;
    stru_lnode lnode;
    stru_dobj dobj;

    dobj.p_do_vec.push_back(&point);
    dobj.do_name = point.doname;
    lnode.dobjs.push_back(dobj);

    lnode.ln_name = point.lnode;
    ldev.lnodes.push_back(lnode);

    ldev.ld_name = point.ldev;
    obj.ldevs.push_back(ldev);
}

static int mms_m_ied_init(stru_mms_m_obj &obj)
{
    if(obj.p_cfg && !obj.p_cfg->para.ied.empty())
    {
        obj.ied_name = obj.p_cfg->para.ied;
    }
    else
    {
        LOG_E("IED name is empty\n");
        return -1;
    }

    obj.run.ip = obj.p_cfg->para.host_ip;
    obj.run.port = obj.p_cfg->para.host_port;

    LOG_I("cfg %s, ied_name %s, ip %s, port %d\n", obj.cfg_path.c_str(), obj.ied_name.c_str(), obj.run.ip.c_str(), obj.run.port);

    std::vector<stru_point_item>::iterator it;

    for(it = obj.p_cfg->point.st.begin(); it!= obj.p_cfg->point.st.end(); it++)
    {
        mms_m_add_point_to_ldevs(obj, *it);
    }

    for(it = obj.p_cfg->point.mx.begin(); it!= obj.p_cfg->point.mx.end(); it++)
    {
        mms_m_add_point_to_ldevs(obj, *it);
    }

    for(it = obj.p_cfg->point.co.begin(); it!= obj.p_cfg->point.co.end(); it++)
    {
        mms_m_add_point_to_ldevs(obj, *it);
    }

    for(it = obj.p_cfg->point.ao.begin(); it!= obj.p_cfg->point.ao.end(); it++)
    {
        mms_m_add_point_to_ldevs(obj, *it);
    }

    return 0;
}



int mms_m_out_init(const char *cfg_path, int debug_print_flag, uint32_t connectionTimeout)
{
    if(NULL == cfg_path)
    {
        LOG_E("Invalid config path\n");
        return -1;
    }

    for(std::map<int, stru_mms_m_obj *>::iterator it = g_mms_m_obj_map.begin(); it != g_mms_m_obj_map.end(); it++)
    {
        if(0 == it->second->cfg_path.compare(cfg_path))
        {
            LOG_E("IED with config file %s already exists\n", cfg_path);
            return -1;
        }
    }

    stru_mms_m_obj *p_obj = new stru_mms_m_obj;
    if(NULL == p_obj)
    {
        LOG_E("Failed to allocate memory for IED\n");
        return -1;
    }

    LOG_I("p_obj %p", p_obj);

    memset(p_obj, 0, sizeof(stru_mms_m_obj));
    p_obj->cfg_path = cfg_path;
    p_obj->debug_print_flag = debug_print_flag;
    p_obj->connectionTimeout = connectionTimeout;
    p_obj->p_cfg = parse_cfg(cfg_path);
    if(NULL == p_obj->p_cfg)
    {
        LOG_E("Failed to parse config file %s\n", cfg_path);
        delete p_obj;
        return -1;
    }

    if(p_obj->debug_print_flag)
    {
        LOG_I("Config file %s parsed successfully\n", cfg_path);
        parse_show_cfg(p_obj->p_cfg);
    }

    if(0 != mms_m_ied_init(*p_obj))
    {
        LOG_E("Failed to initialize IED, config file %s\n", cfg_path);
        delete p_obj->p_cfg;
        delete p_obj;
        return -1;
    }

    if(p_obj->debug_print_flag)
    {
        mms_m_show_ied(*p_obj);
    }

    if(0 != sem_init(&p_obj->run.sem, 0, 1))
    {
        LOG_E("Failed to initialize IED semaphore, config file %s\n", cfg_path);
        delete p_obj->p_cfg;
        delete p_obj;
        return -1;
    }

    LOG_I("p_obj %p", p_obj);

    if(0 != pthread_create(&p_obj->run.pthread_task, NULL, mms_m_run_thread, p_obj))
    {
        LOG_E("Failed to create IED thread, config file %s\n", cfg_path);
        delete p_obj->p_cfg;
        delete p_obj;
        return -1;
    }

    int id = g_mms_m_obj_map.size() + 1;
    g_mms_m_obj_map[id] = p_obj;

    g_mms_m_obj_map[id]->obj_fd = id;

    LOG_I("IED with config file %s added, id %d\n", cfg_path, id);

    return id;
}



int mms_m_out_get_signal_info(int app_fd, stru_mms_m_config *p_config)
{
    if(NULL == p_config)
    {
        LOG_E("Invalid config\n");
        return -1;
    }
    
    if(g_mms_m_obj_map.find(app_fd) == g_mms_m_obj_map.end())
    {
        LOG_E("IED with app_fd %d not found\n", app_fd);
        return -1;
    }

    stru_mms_m_obj &obj = *g_mms_m_obj_map[app_fd];

    if(NULL == obj.p_cfg)
    {
        LOG_E("IED with app_fd %d config is NULL\n", app_fd);
        return -1;
    }

    p_config->st_num = obj.p_cfg->point.st.size();

    p_config->p_st_sig = new stru_mms_m_gen_signal[p_config->st_num];
    if(NULL == p_config->p_st_sig)
    {
        LOG_E("Failed to allocate memory for ST signal\n");
        p_config->st_num = 0;
        return -1;
    }

    for(int i = 0; i < p_config->st_num; i++)
    {
        stru_point_item &point = obj.p_cfg->point.st[i];
        stru_mms_m_gen_signal &sig = p_config->p_st_sig[i];

        strncpy(sig.saddr, point.name.c_str(), sizeof(sig.saddr));
        strncpy(sig.desc, point.desc.c_str(), sizeof(sig.desc));
        sig.data_type = point.type;
        sig.p_data = point.value.p_val;
    }

    p_config->mx_num = obj.p_cfg->point.mx.size();

    p_config->p_mx_sig = new stru_mms_m_gen_signal[p_config->mx_num];
    if(NULL == p_config->p_mx_sig)
    {
        LOG_E("Failed to allocate memory for MX signal\n");
        p_config->mx_num = 0;
        return -1;
    }

    for(int i = 0; i < p_config->mx_num; i++)
    {
        stru_point_item &point = obj.p_cfg->point.mx[i];
        stru_mms_m_gen_signal &sig = p_config->p_mx_sig[i];

        strncpy(sig.saddr, point.name.c_str(), sizeof(sig.saddr));
        strncpy(sig.desc, point.desc.c_str(), sizeof(sig.desc));
        sig.data_type = point.type;
        sig.p_data = point.value.p_val;
    }

    p_config->co_num = obj.p_cfg->point.co.size();
    p_config->p_co_sig = new stru_mms_m_ctrl_signal[p_config->co_num];
    if(NULL == p_config->p_co_sig)
    {
        LOG_E("Failed to allocate memory for CO signal\n");
        p_config->co_num = 0;
        return -1;
    }

    for(int i = 0; i < p_config->co_num; i++)
    {
        stru_point_item &point = obj.p_cfg->point.co[i];
        stru_mms_m_ctrl_signal &co_sig = p_config->p_co_sig[i];

        strncpy(co_sig.sig.saddr, point.name.c_str(), sizeof(co_sig.sig.saddr));
        strncpy(co_sig.sig.desc, point.desc.c_str(), sizeof(co_sig.sig.desc));
        co_sig.sig.data_type = point.type;
        co_sig.sig.p_data = point.value.p_val;
        co_sig.ctrl_type = point.ctrl_model;
    }

    p_config->ao_num = obj.p_cfg->point.ao.size();
    p_config->p_ao_sig = new stru_mms_m_param_signal[p_config->ao_num];
    if(NULL == p_config->p_ao_sig)
    {
        LOG_E("Failed to allocate memory for AO signal\n");
        p_config->ao_num = 0;
        return -1;
    }
    
    for(int i = 0; i < p_config->ao_num; i++)
    {
        stru_point_item &point = obj.p_cfg->point.ao[i];
        stru_mms_m_param_signal &ao_sig = p_config->p_ao_sig[i];

        strncpy(ao_sig.sig.saddr, point.name.c_str(), sizeof(ao_sig.sig.saddr));
        strncpy(ao_sig.sig.desc, point.desc.c_str(), sizeof(ao_sig.sig.desc));
        ao_sig.sig.data_type = point.type;
        ao_sig.sig.p_data = point.value.p_val;
        ao_sig.ctrl_type = point.ctrl_model;

        ao_sig.min = point.min_value;
        ao_sig.max = point.max_value;
        ao_sig.step = point.step_value;
        strncpy(ao_sig.unit, point.unit.c_str(), sizeof(ao_sig.unit));
    }

    return 0;
}

int mms_m_out_get_connect_status(int app_fd, mms_m_out_status_cb p_func)
{
    if(g_mms_m_obj_map.find(app_fd) == g_mms_m_obj_map.end())
    {
        LOG_E("IED with app_fd %d not found\n", app_fd);
        return -1;
    }

    g_mms_m_obj_map[app_fd]->run.out_status_cb = p_func;

    return 0;
}

int mms_m_out_debug_print_swicth(int app_fd, int debug_print_flag)
{
    if(g_mms_m_obj_map.find(app_fd) == g_mms_m_obj_map.end())
    {
        LOG_E("IED with app_fd %d not found\n", app_fd);
        return -1;
    }

    g_mms_m_obj_map[app_fd]->debug_print_flag = debug_print_flag;

    LOG_I("IED with app_fd %d debug print flag set to %d, success\n", app_fd, debug_print_flag);

    return 0;
}

int mms_m_out_do_set_yk_or_param(stru_mms_m_event *p_event)
{
    if(NULL == p_event)
    {
        LOG_E("Invalid event\n");
        return -1;
    }

    if(p_event->app_fd > 0)
    {
        if(g_mms_m_obj_map.find(p_event->app_fd) == g_mms_m_obj_map.end())
        {
            LOG_E("IED with id %d not found\n", p_event->app_fd);
            return -1;
        }

        mms_m_push_event(*g_mms_m_obj_map[p_event->app_fd], *p_event);
        return 0;
    }

    for(std::map<int, stru_mms_m_obj *>::iterator it = g_mms_m_obj_map.begin(); it != g_mms_m_obj_map.end(); it++)
    {
        if(0 == it->second->ied_name.compare(p_event->ied))
        {
            mms_m_push_event(*it->second, *p_event);
            return 0;
        }
    }

    LOG_E("IED with name %s or fd %d not found\n", p_event->ied, p_event->app_fd);

    return -1;
}

int mms_m_out_read_params(int app_fd)
{
    if(g_mms_m_obj_map.find(app_fd) == g_mms_m_obj_map.end())
    {
        LOG_E("IED with id %d not found\n", app_fd);
        return -1;
    }

    stru_mms_m_obj &obj = *g_mms_m_obj_map[app_fd];

    stru_mms_m_event event;
    memset(&event, 0, sizeof(stru_mms_m_event));

    event.ctrl_type = _MMS_M_EVENT_AO_READ;
    mms_m_push_event(obj, event);

    return 0;
}

int mms_m_out_read_param(int app_fd, const char *param_name)
{
    if(g_mms_m_obj_map.find(app_fd) == g_mms_m_obj_map.end())
    {
        LOG_E("IED with id %d not found\n", app_fd);
        return -1;
    }

    stru_mms_m_obj &obj = *g_mms_m_obj_map[app_fd];

    stru_mms_m_event event;
    memset(&event, 0, sizeof(stru_mms_m_event));

    event.ctrl_type = _MMS_M_EVENT_AO_READ;
    strncpy(event.name, param_name, sizeof(param_name));
    mms_m_push_event(obj, event);

    return 0;
}



int mms_m_out_get_value(int fd, mms_m_out_value_cb p_func)
{
    if(g_mms_m_obj_map.find(fd) == g_mms_m_obj_map.end())
    {
        LOG_E("IED with id %d not found\n", fd);
        return -1;
    }
    
    stru_mms_m_obj &obj = *g_mms_m_obj_map[fd];

    if(NULL == p_func)
    {
        LOG_E("Invalid callback function\n");
        return -1;
    }

    obj.run.out_cb_lists.push_back(p_func);

    return 0;
}


void *mms_m_create_data_ptr(uint8_t type)
{
    switch(type)
    {
        case _MMS_M_data_type_bool:
            return (void *)new uint8_t;
        case _MMS_M_data_type_int8:
            return (void *)new int8_t;
        case _MMS_M_data_type_int16:
            return (void *)new int16_t;
        case _MMS_M_data_type_int32:
            return (void *)new int32_t;
        case _MMS_M_data_type_int64:
            return (void *)new int64_t;
        case _MMS_M_data_type_uint8:
            return (void *)new uint8_t;
        case _MMS_M_data_type_uint16:
            return (void *)new uint16_t;
        case _MMS_M_data_type_uint32:
            return (void *)new uint32_t;
        case _MMS_M_data_type_uint64:
            return (void *)new uint64_t;
        case _MMS_M_data_type_float:
            return (void *)new float;
        case _MMS_M_data_type_double:
            return (void *)new double;
        case _MMS_M_data_type_string:
            return (void *)new char[MMS_M_STR_LEN];
        default:
            return NULL;
    }
}

int mms_m_set_data_value(void *srt, void *dst, uint8_t type)
{
    if(NULL == srt || NULL == dst)
    {
        LOG_E("Invalid data pointer src %p, dst %p\n", srt, dst);
        return -1;
    }

    switch(type)
    {
        case _MMS_M_data_type_bool:
            *(uint8_t *)dst = *(uint8_t *)srt;
            break;
        case _MMS_M_data_type_int8:
            *(int8_t *)dst = *(int8_t *)srt;
            break;
        case _MMS_M_data_type_int16:
            *(int16_t *)dst = *(int16_t *)srt;
            break;
        case _MMS_M_data_type_int32:
            *(int32_t *)dst = *(int32_t *)srt;
            break;
        case _MMS_M_data_type_int64:
            *(int64_t *)dst = *(int64_t *)srt;
            break;
        case _MMS_M_data_type_uint8:
            *(uint8_t *)dst = *(uint8_t *)srt;
            break;
        case _MMS_M_data_type_uint16:
            *(uint16_t *)dst = *(uint16_t *)srt;
            break;
        case _MMS_M_data_type_uint32:
            *(uint32_t *)dst = *(uint32_t *)srt;
            break;
        case _MMS_M_data_type_uint64:
            *(uint64_t *)dst = *(uint64_t *)srt;
            break;
        case _MMS_M_data_type_float:
            *(float *)dst = *(float *)srt;
            break;
        case _MMS_M_data_type_double:
            *(double *)dst = *(double *)srt;
            break;
        case _MMS_M_data_type_string:
            strncpy((char *)dst, (char *)srt, MMS_M_STR_LEN);
            break;
        default:
            return -1;
    }

    return 0;
}

int mms_m_get_data_value_str(void *data, uint8_t type, char *str)
{
    if(NULL == data || NULL == str)
    {
        LOG_E("Invalid data pointer, data %p, str %p\n", data, str);
        return -1;
    }
    bool b;

    switch(type)
    {
        case _MMS_M_data_type_bool:
            b = *(uint8_t *)data > 0 ? true : false;
            strncpy(str, b ? "true" : "false", MMS_M_STR_LEN);
            break;
        case _MMS_M_data_type_int8:
            snprintf(str, MMS_M_STR_LEN, "%d", *(int8_t *)data);
            break;
        case _MMS_M_data_type_int16:
            snprintf(str, MMS_M_STR_LEN, "%d", *(int16_t *)data);
            break;
        case _MMS_M_data_type_int32:
            snprintf(str, MMS_M_STR_LEN, "%d", *(int32_t *)data);
            break;
        case _MMS_M_data_type_int64:
            snprintf(str, MMS_M_STR_LEN, "%ld", *(int64_t *)data);
            break;
        case _MMS_M_data_type_uint8:
            snprintf(str, MMS_M_STR_LEN, "%u", *(uint8_t *)data);
            break;
        case _MMS_M_data_type_uint16:
            snprintf(str, MMS_M_STR_LEN, "%u", *(uint16_t *)data);
            break;
        case _MMS_M_data_type_uint32:
            snprintf(str, MMS_M_STR_LEN, "%u", *(uint32_t *)data);
            break;
        case _MMS_M_data_type_uint64:
            snprintf(str, MMS_M_STR_LEN, "%lu", *(uint64_t *)data);
            break;
        case _MMS_M_data_type_float:
            snprintf(str, MMS_M_STR_LEN, "%f", *(float *)data);
            break;
        case _MMS_M_data_type_double:
            snprintf(str, MMS_M_STR_LEN, "%f", *(double *)data);
            break;
        case _MMS_M_data_type_string:
            strncpy(str, (char *)data, MMS_M_STR_LEN);
            break;
        default:
            return -1;
    }

    return 0;
}

static struct _mms_m_resaon_str
{
    ReasonForInclusion reason;
    const char *str;
}g_mms_m_reason_str[] = 
{
    // general errors
    {IEC61850_REASON_NOT_INCLUDED,  "IEC61850_REASON_NOT_INCLUDED"},
    {IEC61850_REASON_DATA_CHANGE,   "IEC61850_REASON_DATA_CHANGE"},
    {IEC61850_REASON_QUALITY_CHANGE,"IEC61850_REASON_QUALITY_CHANGE"},
    {IEC61850_REASON_DATA_UPDATE,   "IEC61850_REASON_DATA_UPDATE"},
    {IEC61850_REASON_INTEGRITY,     "IEC61850_REASON_INTEGRITY"},
    {IEC61850_REASON_GI,            "IEC61850_REASON_GI"},
    {IEC61850_REASON_UNKNOWN,       "IEC61850_REASON_UNKNOWN"},
    {MMS_M_REASON_ALL_CALL,     "MMS_M_REASON_ALL_CALL"},     
    {MMS_M_REASON_READ_PARAM,   "MMS_M_REASON_READ_PARAM"}                           
};

char *mms_m_out_reason_str(int reason)
{
    for(int i = 0; i < sizeof(g_mms_m_reason_str) / sizeof(struct _mms_m_resaon_str); i++)
    {
        if(g_mms_m_reason_str[i].reason == reason)
        {
            return (char *)g_mms_m_reason_str[i].str;
        }
    }

    return (char *)"IEC61850_REASON_UNKNOWN";
}