/****************************************************************************
** File name    :  HTIEC61850.cpp
** Description  :  IEC 61850 库API
**              ： 第三方库
** Create date  :  2019.09.01
** Auther by    :  Liuyx
** Version info :  V1.0.01
** Copyrigth By:  xi'an huatek, Inc Co., Ltd
** Update record:
** DATE         AUTHER   DESC
** -------------------------------------------------------------------------
** 2019.09.01   Liuyx    first build
****************************************************************************/
#include <stdio.h>
#include "HTGlobal.h"
#include "static_model.h"
static const char *_FILE_ = "HTIEC61850.cpp";

string vNextModelNode(ModelNode *pNode, string str)
{
    if (!pNode)  return str;    
    str.append(pNode->name).append("$");
    vNextModelNode(pNode->firstChild, str);
    return str;
}
/*
* ICD文件模型解析
*/
IedModel *LoadModelConfigFile(char *icd_file)
{
    int cnt = 0, i = 0;
    string szNodeStr = "", szSipl="";
    LogicalDevice *pLD = NULL;
    IedModel *model = ConfigFileParser_createModelFromConfigFileEx(icd_file);
    cnt = IedModel_getLogicalDeviceCount(model);
    int idx = 0;

    szNodeStr.append(model->name);
    szSipl.append(model->name);
    for (int i = 0; i < cnt; i++)
    {
        pLD = IedModel_getDeviceByIndex(model, idx);
        if (pLD) {
            szNodeStr.append(pLD->name).append("/");
            szSipl.append(pLD->name).append("/");
        }

        ModelNode *pNode = pLD->firstChild;
        szNodeStr = vNextModelNode(pNode, szNodeStr);
        szSipl = vNextModelNode(pNode->firstChild->sibling, szSipl);
        //while (pNode) {
        //    szNodeStr.append(pNode->name);
        //    switch (pNode->modelType)
        //    {
        //    case LogicalDeviceModelType:
        //        break;
        //    case LogicalNodeModelType:
        //        szNodeStr.append("$");
        //        szSipl.append(pNode->sibling->name);
        //        break;
        //    case DataObjectModelType:
        //        szNodeStr.append("$");
        //        break;
        //    case DataAttributeModelType:
        //        break;
        //    }
        //    pNode = pNode->firstChild;
        //}
        //ModelNode *pNodeSpl = pLD->sibling;
        //while (pNodeSpl) {
        //    szSipl.append(pNodeSpl->name); //.append("$")
        //    if (pNodeSpl->sibling) {
        //        szSipl.append("$");
        //    }
        //    pNodeSpl = pNodeSpl->sibling;
        //}
        printf_s("Node:%s\n", szNodeStr.c_str());
        printf((const char*)"Node: %s\n", szNodeStr.c_str());
        printf("Node: %s\n", szSipl.c_str());
    }//IedModel_getModelNodeByObjectReference(model, iedModel_MONT);
    //IedModel_getModelNodeByShortAddress
    return model;
}
void reportCallbackFunction(void* parameter, ClientReport report)
{
    char szTime[32] = { 0 };
    MmsValue* dataSetValues = ClientReport_getDataSetValues(report);

    printf("received report for: %s\n", ClientReport_getRcbReference(report));

    int i;
    for (i = 0; i < 4; i++) {
        ReasonForInclusion reason = ClientReport_getReasonForInclusion(report, i);
        
        vGetHostTimeFmt(szTime);
        if (reason != IEC61850_REASON_NOT_INCLUDED) {
            printf("  %s : GGIO1.SPCSO%d.stVal: %d (included for reason %d) rptid:%s\n", szTime, i,
                MmsValue_getBoolean(MmsValue_getElement(dataSetValues, i)), reason, ClientReport_getRptId(report));
        }
    }
}

void reportCallbackFunctions(void* parameter, ClientReport report)
{
	/*获取报告中的数据*/
	MmsValue* dataSetValues = ClientReport_getDataSetValues(report);
	/*获得数据集列表*/
	LinkedList *m_list = (LinkedList *)&parameter;

	int i = 0;
	/*遍历数据集*/
	while (LinkedList_getNext(*m_list) != NULL)
	{
		/*获取下一个节点，可能是为了便于便利，获取到的LinkedList的实际值时从第二个节点开始的*/
		*m_list = LinkedList_getNext(*m_list);
		/*获取上报原因*/
		ReasonForInclusion reason = ClientReport_getReasonForInclusion(report, i);
		/*这里如果reason是REASON_NOT_INCLUDED，是不能获取数据值的*/
		/*例如像数据变化时，可能只上报了1个数据，但是dataSetValues中会有所有数据的索引，只不过其他数据的reason将会是REASON_NOT_INCLUDED*/
		if (reason != REASON_NOT_INCLUDED) {
			/*这里MmsValue_getBoolean只是假设返回的都是boolean类型，实际应用中要根据实际数据类型调用不同的函数*/
			printf("   %s: GGIO1.SPCSO%d.stVal: %d (included for reason %d) rptid:%s\n",
			//printf("  %s : %i (included for reason %i)\n", 
				(char*)((*m_list)->data), i,
				MmsValue_getBoolean(MmsValue_getElement(dataSetValues, i)), reason,  ClientReport_getRptId(report));
		}
		i++;
	}
}
// 获取错误消息
const char *getIedError(int error)
{    
    switch (error)
    {
        /* general errors */
    case IED_ERROR_OK: //0,
        return "No error occurred - service request has been successful";
    case IED_ERROR_NOT_CONNECTED: // 1,
        return "The service request can not be executed because the client is not yet connected";
    case IED_ERROR_ALREADY_CONNECTED: // 2,
        return "Connect service not execute because the client is already connected";
    case IED_ERROR_CONNECTION_LOST: //3,
        return "The service request can not be executed caused by a loss of connection";
    case IED_ERROR_SERVICE_NOT_SUPPORTED: //4,
        return " The service or some given parameters are not supported by the client stack or by the server";
    case  IED_ERROR_CONNECTION_REJECTED: //5,
        return "Connection server failed";
    case IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED: //6,
        return "Cannot send request because outstanding call limit is reached";

        /* client side errors */
    case IED_ERROR_USER_PROVIDED_INVALID_ARGUMENT: // 10,
        return "API function has been called with an invalid argument";
    case IED_ERROR_ENABLE_REPORT_FAILED_DATASET_MISMATCH: // 11,
        return "The object report failed dataset mismatch";
    case IED_ERROR_OBJECT_REFERENCE_INVALID: //12,
        return "The object provided object reference is invalid (there is a syntactical error).";
    case IED_ERROR_UNEXPECTED_VALUE_RECEIVED:// 13,
        return "Received object is of unexpected type";

        /* service error - error reported by server */

    case IED_ERROR_TIMEOUT: //20,
        return "The communication to the server failed with a timeout";
    case IED_ERROR_ACCESS_DENIED:// 21,
        return "The server rejected the access to the requested object/service due to access control";
    case IED_ERROR_OBJECT_DOES_NOT_EXIST: //22,
        return "The server reported that the requested object does not exist (returned by server)";
    case IED_ERROR_OBJECT_EXISTS: // 23,
        return "The server reported that the requested object already exists";
    case IED_ERROR_OBJECT_ACCESS_UNSUPPORTED: // 24,
        return "The server does not support the requested access method (returned by server)";
    case  IED_ERROR_TYPE_INCONSISTENT:// 25,
        return "The server expected an object of another type (returned by server)";
    case IED_ERROR_TEMPORARILY_UNAVAILABLE:// 26,
        return "The object or service is temporarily unavailable (returned by server)";
    case IED_ERROR_OBJECT_UNDEFINED:// 27,
        return "The specified object is not defined in the server (returned by server)";
    case IED_ERROR_INVALID_ADDRESS:// 28,
        return "The specified address is invalid (returned by server)";
    case IED_ERROR_HARDWARE_FAULT:// 29,
        return "Service failed due to a hardware fault (returned by server)";
    case IED_ERROR_TYPE_UNSUPPORTED: //30,
        return "The requested data type is not supported by the server (returned by server)";
    case IED_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT:// 31,
        return "The provided attributes are inconsistent (returned by server)";
    case  IED_ERROR_OBJECT_VALUE_INVALID:// 32,
        return "The provided object value is invalid (returned by server)";
    case IED_ERROR_OBJECT_INVALIDATED:// 33,
        return "The object is invalidated (returned by server)";
    case IED_ERROR_MALFORMED_MESSAGE:// 34,
        return "Received an invalid response message from the server";
    case  IED_ERROR_SERVICE_NOT_IMPLEMENTED:// 98,
        return "Service not implemented";
    case IED_ERROR_UNKNOWN:// 99
    default:
        //IedConnection_getLastApplError(g_IedConn.hConnHandle).error;
        return "unknown error";
    }
}

// 删除文件
void deleteComtradeFile(IedConnection con, const char *filename)
{
    IedClientError error;

    /* Delete file at server */
    IedConnection_deleteFile(con, &error, filename);

    if (error != IED_ERROR_OK)
        vPrtLogMsg(LOG_DEBUG, error, "Failed to delete file! (code=%i)", error);
}

// 解析下载的录播文件并入库
int  unPackComtradeFile(char *pfile)
{
    CDBMySQL *pdbHandle = CDBMySQL::Instance();
    if (!pdbHandle) return -1;
    char *static_sql = NULL;
    FILE *fd = NULL;
    int n = 0;
    ST_COMTRAD_DATA stAlmi;

    static_sql = (char*)calloc((DEF_BUFFER_1K*DEF_BUFFER_1K), sizeof(char));
    if (static_sql == NULL) {
        vPrtLogMsg(LOG_ERROR, errno, "calloc buffer size=%d failed.", (DEF_BUFFER_1K*DEF_BUFFER_1K));
        return -1;
    }
    fd = fopen(pfile, "r+b");
    if (!fd) {
        vPrtLogMsg(LOG_ERROR, errno, "Open csg pdfile %s failed! msg:%s", pfile, strerror(errno));
        return errno;
    }

    int counter = 0;
    sprintf(static_sql, "INSERT into busi_ampli_comtrad(id, comtrad_file, d_time,comtrad_data) "
        "VALUES(guuid(),'%s',now(),'", pfile);
    while (!feof(fd))
    {
        memset(&stAlmi, 0x00, sizeof(ST_COMTRAD_DATA));
        n++;
        counter = fread(&stAlmi, sizeof(char), sizeof(ST_COMTRAD_DATA), fd);
        if (counter == sizeof(ST_COMTRAD_DATA))
        {
            sprintf(static_sql + strlen(static_sql),
                "%d,%d,%d,%d,"
                "%d,%d,%d,%d,"
                "%d,%d,%d,%d,"
                "%d,%d|",
                //"%d, %d, %d, %d}",
                (stAlmi.sno), (stAlmi.tme), (stAlmi.A1), (stAlmi.A2),
                (stAlmi.B1), (stAlmi.B2), (stAlmi.C1), (stAlmi.C2),
                (stAlmi.a1), (stAlmi.a2), (stAlmi.b1), (stAlmi.b2),
                (stAlmi.c1), (stAlmi.c2)); // , (stAlmi.nsi), (stAlmi.crt));
        }
    }
    fclose(fd);
    static_sql[strlen(static_sql) - 1] = 0x00;
    strcat(static_sql, "')");
    pdbHandle->InsertRecord((const char*)static_sql);

    vPrtLogMsg(LOG_DEBUG,0,"str_len = %d, SQL=%100s", strlen(static_sql), static_sql);
    if (static_sql)
        free(static_sql);
    static_sql = NULL;
    return n;
}

// IED录播文件浏览
void showComtradeFile(IedConnection con, const char *pszDirName )
{
    IedClientError error;
    bool moreFollows = false;    
    LinkedList rootDirectory;   /* Get the root directory */
    short no = 1;

    
    // 设置下载文件跟路径
    //IedConnection_setFilestoreBasepath(con, g_TConfig.getIEDComtradePath());
    rootDirectory = IedConnection_getFileDirectoryEx(con, &error, pszDirName, NULL, &moreFollows);

    if (error != IED_ERROR_OK) {
        vPrtLogMsg(LOG_DEBUG, error, "Error retrieving file directory,MSG:%s", getIedError(error));
        return;
    }

    LinkedList directoryEntry = LinkedList_getNext(rootDirectory);
    
    while (directoryEntry != NULL)
    {
        FileDirectoryEntry entry = (FileDirectoryEntry)directoryEntry->data;
        uint64_t last = FileDirectoryEntry_getLastModified(entry);
        vPrtLogMsg(LOG_DEBUG, 0, "get comtrad No:%i,filesize:%i,filename:%s",no, 
            FileDirectoryEntry_getFileSize(entry), FileDirectoryEntry_getFileName(entry));
            
        ST_FILES_ATTRIB stFiles;
        stFiles.bstat = HT_COMTRADE_REDOWN;   // 待下载
        stFiles.sNo = no++;
        //strcpy(stFiles.szFileName, FileDirectoryEntry_getFileName(entry));
        sprintf(stFiles.szFileName, "%s%s", pszDirName, FileDirectoryEntry_getFileName(entry));
        stFiles.iFileSize = FileDirectoryEntry_getFileSize(entry);
        mutex_lock(g_list_comtrad_files_mutex);
        g_list_comtrad_files.push_front(stFiles);   // 录播文件如队列
        mutex_unlock(g_list_comtrad_files_mutex);

        directoryEntry = LinkedList_getNext(directoryEntry);
    }
    LinkedList_destroyDeep(rootDirectory, (LinkedListValueDeleteFunction)FileDirectoryEntry_destroy);

    if (moreFollows)
        vPrtLogMsg(LOG_DEBUG, 0, "--> more Comtrade files available...\n");
}

// 上传文件--目前IED不支持上传文件
void setComtradeFile(IedConnection con, const char *filename)
{
    IedClientError error;
    char szDirName[MAX_PATH] = { 0 };
    char szFileName[MAX_PATH] = { 0 };
    char szRemotePath[260];

    char* dirc = _strdup(filename);
    char* basec = _strdup(filename);

    char* localDirName = getDirName(dirc, szDirName);
    char* localFileName = getBaseName(basec, szFileName);

    vPrtLogMsg(LOG_DEBUG, 0, "local dir: %s, local file: %s", localDirName, localFileName);
    
    int size = get_file_size(filename);
    /* IedConnection_setFilestoreBasepath requires the file separator at the end! */
    strcpy(dirc, localDirName);
    strcat(dirc, "/");
    sprintf(szRemotePath, "%s%s", g_TConfig.getIEDComtradePath(), localFileName);
    vPrtLogMsg(LOG_DEBUG, 0, "filestore basepath: %s", dirc);
    IedConnection_setFilestoreBasepath(con, g_TConfig.getIEDComtradePath());
    IedConnection_setFile(con, &error, filename, localFileName);
    free(dirc);
    free(basec);

    if (error != IED_ERROR_OK)
        vPrtLogMsg(LOG_ERROR, error, "Failed to upload file! (code=%i),MSG=%s", error, getIedError(error));
    else vPrtLogMsg(LOG_ERROR, error, "Upload file:%s successed!", localFileName);
}

// 下载录播文件
static bool downloadFileHandler(void* parameter, uint8_t* buffer, uint32_t bytesRead)
{
    FILE* fp = (FILE*)parameter;

    vPrtLogMsg(LOG_DEBUG, 0, "received %i bytes", bytesRead);

    if (fwrite(buffer, 1, bytesRead, fp) == bytesRead)
        return true;
    else {
        vPrtLogMsg(LOG_ERROR, errno, "Failed to write local file!");
        return false;
    }
}
// 获取录播文件
void getComtradeFile(IedConnection con)
{
    IedClientError error;
    ST_FILES_ATTRIB stFiles;
    char szLocalFile[MAX_PATH] = { 0 };
    char szFilePath[MAX_PATH] = { 0 };

    memset(&stFiles, 0x00, sizeof(ST_FILES_ATTRIB));

    list<ST_FILES_ATTRIB>::iterator ptr = g_list_comtrad_files.begin();
    
    while (ptr != g_list_comtrad_files.end())
    {
        mutex_lock(g_list_comtrad_files_mutex);
        printf("%d-%d-%s\n", (*ptr).bstat, (*ptr).iFileSize, (*ptr).szFileName);
        if (HT_COMTRADE_DOWNED == (*ptr).bstat) {  // 已经下载完成
            mutex_unlock(g_list_comtrad_files_mutex);
            _SLEEP(100);
            ptr++;
            continue;
        }

        vPrtLogMsg(LOG_DEBUG, 0, "getComtradeFiles file_count= %d", g_list_comtrad_files.size());
        memcpy(&stFiles, &(*ptr), sizeof(ST_FILES_ATTRIB));

        char* bname = _strdup((const char*)stFiles.szFileName);
        char* localFilename = getBaseName(bname, szLocalFile);
        strcpy(szFilePath, g_TConfig.getComtradePath());
        strcat(szFilePath, "/");
        strcat(szFilePath, szLocalFile);
        FILE* fp = fopen(szFilePath, "wb");

        if (fp != NULL) 
        {
            /* Download a file from the server */
            IedConnection_getFile(con, &error, stFiles.szFileName, downloadFileHandler, (void*)fp);
            if (error == IED_ERROR_OK) {
                if (g_TConfig.getIEDComtradeRemove())  // 下载完成后，是否删除IED上的文件
                    deleteComtradeFile(con, stFiles.szFileName);
                (*ptr).bstat = HT_COMTRADE_DOWNED;
                strcpy((*ptr).szFileName, szFilePath);  // 修改为本地路径+文件名
                ptr++;
            }
            else vPrtLogMsg(LOG_ERROR, error, "Failed to get file:%s! MSG:%s", stFiles.szFileName,getIedError(error));
            fclose(fp);
        }
        else vPrtLogMsg(LOG_ERROR, errno, "Failed to open file %s", szFilePath);
        free(bname);
        bname = NULL;
        mutex_unlock(g_list_comtrad_files_mutex);
        _SLEEP(50);
    }
}
//
//// 录播文件下载,解析入库线程
//void vDownLoadComtradeFiles(/*void *arg*/ ST_IED_CONN_HANDLE &stIedCon)
//{
//    IedClientError error;
//    IedConnection con = IedConnection_create();
//    ST_COMTRAD_FILES_ATTRIB stFiles;
//    //vPrtLogMsg(LOG_DEBUG, 0, "thread_iec61850_getcomtrade_files = %d startup...", GETTID());
//
//    IedConnection_connect(con, &error, g_TConfig.getModbusTcpAddr(), g_TConfig.getModbusTcpPort());
//    //IedConnection_connect(con, &error, "192.168.1.1", 102);
//    if (error != IED_ERROR_OK) {
//        printf("Failed to connect to %s:%i\n", g_TConfig.getModbusTcpAddr(), g_TConfig.getModbusTcpPort());
//        return ;
//    }
//    showComtradeFile(con, g_TConfig.getIEDComtradePath());   // 查看待下载的文件列表    
//    getComtradeFile(con);          // 下载文件
//    //setComtradeFile(con, "comtrad/sim_data1.dat");
//    // 解析录播文件并入库
//    memset(&stFiles, 0x00, sizeof(ST_COMTRAD_FILES_ATTRIB));
//    while (!g_list_comtrad_files.empty() || g_list_comtrad_files.size() > 0) 
//    {
//        vPrtLogMsg(LOG_DEBUG, 0, "getComtradeFiles count=%d, bstat=%d, file=%s,filesize=%d", 
//            g_list_comtrad_files.size(), 
//            g_list_comtrad_files.back().bstat,
//            g_list_comtrad_files.back().szFileName, g_list_comtrad_files.back().iFileSize);
//        if (HT_COMTRADE_REDOWN == g_list_comtrad_files.back().bstat) {  // 未下载完成
//            _SLEEP(100);
//            continue;
//        }
//        mutex_lock(g_list_comtrad_files_mutex);
//        memcpy(&stFiles, &(g_list_comtrad_files.back()), sizeof(ST_COMTRAD_FILES_ATTRIB));  // 由尾取出
//        g_list_comtrad_files.pop_back(); // 由尾删除         
//        mutex_unlock(g_list_comtrad_files_mutex);
//        //unPackComtradeFile(stFiles.szFileName);  // 解析文件并入库
//    }
//
//    printf("file download and parser complate....\n");
//    IedConnection_abort(con, &error);
//    IedConnection_destroy(con);
//    return ;
//}

// 录播文件下载,解析入库线程
void vDownLoadComtradeFiles(ST_IED_CONN_HANDLE &stIedCon)
{
    ST_FILES_ATTRIB stFiles;

    showComtradeFile(stIedCon.hConnHandle, g_TConfig.getIEDComtradePath());   // 查看待下载的文件列表    
    getComtradeFile(stIedCon.hConnHandle);          // 下载文件
    setComtradeFile(stIedCon.hConnHandle, "etc/setting.ini");
    // 解析录播文件并入库
    memset(&stFiles, 0x00, sizeof(ST_FILES_ATTRIB));
    while (!g_list_comtrad_files.empty() || g_list_comtrad_files.size() > 0)
    {
        vPrtLogMsg(LOG_DEBUG, 0, "getComtradeFiles count=%d, bstat=%d, file=%s,filesize=%d",
            g_list_comtrad_files.size(),
            g_list_comtrad_files.back().bstat,
            g_list_comtrad_files.back().szFileName, g_list_comtrad_files.back().iFileSize);
        if (HT_COMTRADE_REDOWN == g_list_comtrad_files.back().bstat) {  // 未下载完成
            _SLEEP(100);
            continue;
        }
        mutex_lock(g_list_comtrad_files_mutex);
        memcpy(&stFiles, &(g_list_comtrad_files.back()), sizeof(ST_FILES_ATTRIB));  // 由尾取出
        g_list_comtrad_files.pop_back(); // 由尾删除         
        mutex_unlock(g_list_comtrad_files_mutex);
        //unPackComtradeFile(stFiles.szFileName);  // 解析文件并入库
    }

    printf("file download and parser complate....\n");
    return;
}
/*****************************************************************************
* thread_iec61850_service_proc - iec61850 服务线程
******************************************************************************/
// 打印数据对象
void printDataDirectory(char* doRef, IedConnection con, int spaces)
{
    IedClientError error;

    LinkedList dataAttributes = IedConnection_getDataDirectory(con, &error, doRef);
    if (dataAttributes != NULL) 
    {
        LinkedList dataAttribute = LinkedList_getNext(dataAttributes);
        while (dataAttribute != NULL) 
        {
            char* daName = (char*)dataAttribute->data;
            //printSpaces(spaces);
            vPrtLogMsg(LOG_DEBUG, 0, "      DA: %s", (char*)dataAttribute->data);
            dataAttribute = LinkedList_getNext(dataAttribute);

            char daRef[130];
            sprintf(daRef, "%s.%s", doRef, daName);
            printDataDirectory(daRef, con, spaces + 2);
        }
    }
    LinkedList_destroy(dataAttributes);
}

// 获取IED的配置模型
int vGetRemoteIEDModelConfig(IedConnection con)
{
    IedClientError error;
    char lnRef[129] = { 0 }, doRef[129] = { 0 };

    vPrtLogMsg(LOG_DEBUG,0,"Get logical device list...");
    LinkedList deviceList = IedConnection_getLogicalDeviceList(con, &error);

    if (error != IED_ERROR_OK) {
        vPrtLogMsg(LOG_ERROR, error, "Failed to read device list (error code: %i)", error);
        return error;
    }
    // 获取逻辑设备模型LD
    LinkedList device = LinkedList_getNext(deviceList);
    while (device != NULL) 
    {
        vPrtLogMsg(LOG_DEBUG, error,"LD: %s", (char*)device->data);

        LinkedList logicalNodes = IedConnection_getLogicalDeviceDirectory(con, &error,(char*)device->data);
        LinkedList logicalNode = LinkedList_getNext(logicalNodes);
        // 获取逻辑节点模型LN
        while (logicalNode != NULL) 
        {
            vPrtLogMsg(LOG_DEBUG, error, "  LN: %s", (char*)logicalNode->data);            
            sprintf(lnRef, "%s/%s", (char*)device->data, (char*)logicalNode->data);
            LinkedList dataObjects = IedConnection_getLogicalNodeDirectory(con, &error,lnRef, ACSI_CLASS_DATA_OBJECT);
            LinkedList dataObject = LinkedList_getNext(dataObjects);
            // 获取数据对象模型DO
            while (dataObject != NULL) 
            {
                char* dataObjectName = (char*)dataObject->data;
                vPrtLogMsg(LOG_DEBUG, error, "    DO: %s", dataObjectName);

                dataObject = LinkedList_getNext(dataObject);
                sprintf(doRef, "%s/%s.%s", (char*)device->data, (char*)logicalNode->data, dataObjectName);
                printDataDirectory(doRef, con, 4);
            }
            LinkedList_destroy(dataObjects);

            LinkedList dataSets = IedConnection_getLogicalNodeDirectory(con, &error, lnRef, ACSI_CLASS_DATA_SET);
            LinkedList dataSet = LinkedList_getNext(dataSets);

            // 获取数据类型定义DS
            while (dataSet != NULL) 
            {
                char* dataSetName = (char*)dataSet->data;
                bool isDeletable;
                char dataSetRef[130];
                sprintf(dataSetRef, "%s.%s", lnRef, dataSetName);

                LinkedList dataSetMembers = IedConnection_getDataSetDirectory(con, &error, dataSetRef, &isDeletable);

                if (isDeletable)
                    vPrtLogMsg(LOG_DEBUG, error, "    Data set: %s (deletable)", dataSetName);
                else
                    vPrtLogMsg(LOG_DEBUG, error, "    Data set: %s (not deletable)", dataSetName);

                LinkedList dataSetMemberRef = LinkedList_getNext(dataSetMembers);
                while (dataSetMemberRef != NULL) 
                {
                    char* memberRef = (char*)dataSetMemberRef->data;
                    vPrtLogMsg(LOG_DEBUG, error, "      %s", memberRef);
                    dataSetMemberRef = LinkedList_getNext(dataSetMemberRef);
                }
                LinkedList_destroy(dataSetMembers);
                dataSet = LinkedList_getNext(dataSet);
            }
            LinkedList_destroy(dataSets);

            LinkedList reports = IedConnection_getLogicalNodeDirectory(con, &error, lnRef, ACSI_CLASS_URCB);
            LinkedList report = LinkedList_getNext(reports);

            while (report != NULL) 
            {
                char* reportName = (char*)report->data;
                vPrtLogMsg(LOG_DEBUG, error, "    RP: %s", reportName);
                report = LinkedList_getNext(report);
            }

            LinkedList_destroy(reports);
            reports = IedConnection_getLogicalNodeDirectory(con, &error, lnRef, ACSI_CLASS_BRCB);
            report = LinkedList_getNext(reports);
            while (report != NULL) 
            {
                char* reportName = (char*)report->data;
                vPrtLogMsg(LOG_DEBUG, error, "    BR: %s", reportName);
                report = LinkedList_getNext(report);
            }
            LinkedList_destroy(reports);
            logicalNode = LinkedList_getNext(logicalNode);
        }
        LinkedList_destroy(logicalNodes);
        device = LinkedList_getNext(device);
    }
    LinkedList_destroy(deviceList);
    return error;
}


// 初始化61850链接环境参数
void vIedInitIedEvent()
{ 
    int ied_count = 0;
    ST_IED_ADDR *pIed = NULL;
    char szKey[64] = { 0 };
    if (g_TConfig.getIEDCount() <= 0) {
        vPrtLogMsg(LOG_ERROR, 0, "Can't found IED device configure,please check communication parame...");
        return;
    }
    while (ied_count < g_TConfig.getIEDCount()) 
    {
        ST_IED_CONN_HANDLE stIedHandle;
        string strKey = "";
        pIed = g_TConfig.getIEDAddrsss(ied_count++);
        if (pIed != NULL) {
            memset(&stIedHandle, 0x00, sizeof(ST_IED_CONN_HANDLE));
            strcpy(stIedHandle.ied_addr, pIed->IedAddr);
            stIedHandle.ied_port = pIed->port;
            sprintf(szKey, "%s:%d", stIedHandle.ied_addr, stIedHandle.ied_port);            
            strKey = szKey;
            stIedHandle.hConnHandle = IedConnection_create();
            IedConnection_setConnectTimeout(stIedHandle.hConnHandle, 6000);  // connected timeout 6s
            stIedHandle.bWarn = true;
            mutex_lock(g_MapIedConn_mutex);
            g_MapIedConn.insert(map<string, ST_IED_CONN_HANDLE>::value_type(strKey, stIedHandle));
            mutex_unlock(g_MapIedConn_mutex);
        }
    }
}
// 释放链接及资源
void vIedFreeIedEvent(ST_IED_CONN_HANDLE &stIedHandle)
{
    if (stIedHandle.hConnHandle) {
        IedConnection_close(stIedHandle.hConnHandle);
        IedConnection_destroy(stIedHandle.hConnHandle);
    }
    stIedHandle.hConnHandle = NULL;
    if (stIedHandle.IedModel) {
        IedModel_destroy(stIedHandle.IedModel);
    }
    stIedHandle.IedModel = NULL;
}

// 根据icd节点名称，获取传感器绑定关系
bool bGetDeviceBindRelationship(char *pNodeName, ST_DB_BODY &pDb, ST_DEVICE_INFO &stBaseInfo)
{
    map<string, ST_PARAM_BIND>::iterator m_pIter;
    mutex_lock(g_map_parambind_mutex);
    m_pIter = g_map_parambind.find((char*)pNodeName);
    if (m_pIter == g_map_parambind.end()) {
        vPrtLogMsg(LOG_WARNG, RET_FAIL, "can not found:[%s] bind device information.", pNodeName);
        return false;
    }
    strcpy(pDb.szSensorID, (*m_pIter).second.sensorid);
    strcpy(pDb.szFrequency , (*m_pIter).second.szFrequency);
    mutex_unlock(g_map_parambind_mutex);

    string strKey = pDb.szSensorID;
    if (strlen(pDb.szFrequency) > 0) {   // 无频率时为噪声和中心点传感器
        strKey.append("-").append(pDb.szFrequency);
    }
    map<string, ST_DEVICE_INFO>::iterator m_pTIter;
    mutex_lock(g_map_device_mutex);
    m_pTIter = g_map_device.find((char*)strKey.c_str());
    if (m_pTIter == g_map_device.end()) {
        vPrtLogMsg(LOG_WARNG, RET_FAIL, "can not found:[%s],objname:[%s] threshold setting.", pDb.szSensorID, pNodeName);
        return false;
    }
    memcpy(&stBaseInfo, &(*m_pTIter).second, sizeof(ST_DEVICE_INFO));
    mutex_unlock(g_map_device_mutex);
    return true ;
}
// 获取振动,噪声,中心点监测值
float getAccPriValue(IedConnection conn, char *pobjname, FunctionalConstraint fc)
{
    IedClientError error;
    float fval = 0.0f;
    MmsValue* value = IedConnection_readObject(conn, &error, pobjname, fc);
    if (value != NULL) {
        fval = MmsValue_toFloat(value);
        vPrtLogMsg(LOG_DEBUG, error, "read [%s] float value: %f", pobjname, fval);
        MmsValue_delete(value);
    }
    return fval;
}
// 获取振动,噪声,中心点监测值
bool getIEDMoniValue(IedConnection conn, ST_PARAM_BIND &stBind, ST_IED_VALUES &stIedVal)
{
    FunctionalConstraint fc;
    IedClientError error;
    char szTime[32] = { 0 };
    bool ret = false;

    if (stringncasecmp((const char*)stBind.monitype, "MX", 2))
        fc = IEC61850_FC_MX;
    else if(stringncasecmp((const char*)stBind.monitype, "ST", 2))
        fc = IEC61850_FC_ST;
    else if(stringncasecmp((const char*)stBind.monitype, "SE", 2))
        fc = IEC61850_FC_SE;
    else {
        vPrtLogMsg(LOG_WARNG, 0, "read [%s],nodetype[MX|ST|SE] unkown!", stBind.nodename);
        return false;
    }
    MmsValue* value = IedConnection_readObject(conn, &error, stBind.nodename, fc);
    if (value != NULL && error == 0) {
        int n = MmsValue_getArraySize(value);
        MmsValue_getElement(value, 0);
        switch (MmsValue_getType(value))
        {
        case MMS_ARRAY: // 0
            break;
        case MMS_STRUCTURE: //1
            break;
        case MMS_BOOLEAN:
            stIedVal.ST_val = MmsValue_getBoolean(value);
            vPrtLogMsg(LOG_DEBUG, error, "read [%s] state bool value: %f", stBind.nodename, 
                (stIedVal.ST_val == true ? "true" : "false"));
            ret = true;
            break;
        case MMS_INTEGER:
            break;
        case MMS_UNSIGNED:
            break;
        case MMS_FLOAT:
            stIedVal.MX_f = MmsValue_toFloat(value);
            vPrtLogMsg(LOG_DEBUG, error, "read [%s] float value: %f", stBind.nodename, stIedVal.MX_f);
            ret = true;
            break;
        case MMS_BINARY_TIME:
            break;
        case MMS_BIT_STRING:
            break;
        case MMS_STRING : //13,
            break;
        case MMS_UTC_TIME: // 14            
            stIedVal.t = MmsValue_toUnixTimestamp(value);
            UTC2LocalTime(stIedVal.t, szTime);
            vPrtLogMsg(LOG_DEBUG, error, "read [%s] time value: %s", stBind.nodename, szTime);
            ret = true;
            break;
        default:
            vPrtLogMsg(LOG_WARNG, error, "read [%s], value type unkown!", stBind.nodename);
        }
        MmsValue_delete(value);
    }
    return ret;
}
// 获取振动,噪声,中心点监测时间值
time_t getTimeiValue(IedConnection conn, char *pobjname, FunctionalConstraint fc)
{
    IedClientError error;
    time_t fval ;
    MmsValue* value = IedConnection_readObject(conn, &error, pobjname, fc);
    if (value != NULL) {
        fval = MmsValue_toUnixTimestamp(value);
        vPrtLogMsg(LOG_DEBUG, error, "read [%s] time value: %i", pobjname, fval);
        MmsValue_delete(value);
    }
    return fval;
}
// 获取告警监测值
bool getAlarmValue(IedConnection conn, char *pobjname,FunctionalConstraint fc)
{
    IedClientError error;
    bool bval = false;
    MmsValue* value = IedConnection_readObject(conn, &error, pobjname, fc);
    if (value != NULL) {
        bval = MmsValue_getBoolean(value);        
        vPrtLogMsg(LOG_DEBUG, error, "read [%s] alarm bool value: %s", pobjname, (bval == true? "true":"false"));
        MmsValue_delete(value);
    }
    return bval;
}
// 添加入库队列
void Add_SetList(ST_DB_BODY *pData, ST_DEVICE_INFO *pstBase)
{
    ST_DB_BODY  stSetData;

    memset(&stSetData, 0x00, sizeof(ST_DB_BODY));
    memcpy(&stSetData, pData, sizeof(ST_DB_BODY));

    strcpy(stSetData.m_id, pstBase->szMID);
    stSetData.in_out = pstBase->cInOut;
    stSetData.phase = pstBase->cPhase;
    stSetData.side = pstBase->cSide;
    stSetData.szDevType = pstBase->cDevType;

    mutex_lock(g_list_db_body_mutex);
    g_list_db_body.push_front(stSetData);
    mutex_unlock(g_list_db_body_mutex);
}

// 添加阈值告警队列
void Add_SetWarning_List(ST_DB_WARN &stWarn)
{
    mutex_lock(g_list_db_warn_mutex);
    g_list_db_warn.push_front(stWarn);
    mutex_unlock(g_list_db_warn_mutex);
}
// 检查预警告警
bool bWarningCheck(ST_DB_BODY stBody, ST_DB_WARN &stWarn)
{
    string strKey = "";
    map<string, ST_THRES_CONF>::iterator m_pIter;

    strKey = stBody.szSensorID;
    if (stBody.szDevType == '1') { //1:振动装置 2:噪声装置 3:中心点装置
        strKey.append("-").append(stBody.szFrequency);
    }
    mutex_lock(g_thres_conf_mutex);
    m_pIter = g_thres_conf.find((char*)strKey.c_str());
    if (m_pIter == g_thres_conf.end())
    {
        mutex_unlock(g_thres_conf_mutex);
        return false;
    }
    //找到阈值
    if (stBody.value <= (*m_pIter).second.dThreshold) {
        mutex_unlock(g_thres_conf_mutex);
        return false;
    }
    stWarn.almtime = stBody.dtime;
    stWarn.level = 1;
    stWarn.state = 1;
    strcpy(stWarn.szDesc, (*m_pIter).second.szSDevName);
    strcpy(stWarn.s_id, (*m_pIter).second.szSubID);
    stWarn.value = stBody.value;
    if (stBody.szDevType == '1') { //1:振动装置 2:噪声装置 3:中心点装置
        strcpy(stWarn.valunit, "mm/s²");
        //1:通信异常告警 2 : 装置自检异常告警 3 : 信号异常告警 4 : 供电异常告警 5 : 振动阈值告警 6 : 噪声阈值告警 7 : 中心点阈值告警 8 : 日增长率告警 9 : 月增长率告警 10 : 月偏差率告警 11 : 服务器与IED通信异常 12 : 传感器振动告警 13 : 传感器噪声告警 14 : 传感器中心点告警
        stWarn.warn_type = 5;
    }
    else if (stBody.szDevType == '2') {
        strcpy(stWarn.valunit, "dB");
        stWarn.warn_type = 6;
    }
    else if (stBody.szDevType == '3') {
        strcpy(stWarn.valunit, "A");
        stWarn.warn_type = 7;
    }
    mutex_unlock(g_thres_conf_mutex);
    return true;
}
#if 1
// 获取实时监测数据-遥测
void vGetFCMXData_YC(IedConnection conn)
{
    ST_PARAM_BIND stBind;
    ST_IED_VALUES stIedVal;
    ST_DEVICE_INFO stBase;
    ST_DB_BODY stBody;

    map<string, ST_PARAM_BIND>::iterator m_pIter;

    mutex_lock(g_map_parambind_mutex);    
    for (m_pIter = g_map_parambind.begin(); m_pIter != g_map_parambind.end(); m_pIter++)
    {
        memset(&stBind, 0x00, sizeof(ST_PARAM_BIND));
        memset(&stIedVal, 0x00, sizeof(ST_IED_VALUES));
        memset(&stBody, 0x00, sizeof(ST_DB_BODY));
        memset(&stBase, 0x00, sizeof(ST_DEVICE_INFO));

        memcpy(&m_pIter->second, &stBind, sizeof(ST_PARAM_BIND));

        if(false == getIEDMoniValue(conn, stBind, stIedVal)) continue;  // 取振幅值
        stBody.value = stIedVal.MX_f;
        bGetDeviceBindRelationship(stBind.nodename, stBody, stBase);
        Add_SetList(&stBody,&stBase);

        // 预警判断
        ST_DB_WARN stWarn;
        memset(&stWarn, 0x00, sizeof(ST_DB_WARN));
        if (bWarningCheck(stBody,stWarn) == true) {
            Add_SetWarning_List(stWarn);
        }
    }
    mutex_unlock(g_map_parambind_mutex);
}

#else
// 获取实时监测数据-遥测
void vGetFCMXData_YC(IedConnection conn)
{
    ST_MONI_DATA stFCMX;
    memset(&stFCMX, 0x00, sizeof(ST_MONI_DATA));
    char szMXNode[256] = { 0 };
    string strKey;
    for (int i = 1; i <= 12; i++)    // SVBR01-SVBR16
    {
        // 振动分量监测值
        for (int n = 1; n <= 19; n++) 
        {
            ST_DB_BODY stBody;
            ST_DEVICE_INFO stBase;
            memset(&stBody, 0x00, sizeof(ST_DB_BODY));
            memset(&stBase, 0x00, sizeof(ST_DEVICE_INFO));

            sprintf(szMXNode, "TEMPLATEMONT/SVBR%02d.AccPri%d.mag.f", i, n); 
            if (i == 1 && n == 1)  strKey = szMXNode;
            //sprintf(szMXNode, "TEMPLATEMONT/SVBR%02d$AccPri%d$mag$f", i, n);
            switch (n)
            {
            case 1:
                stFCMX.AccPri1 = getAccPriValue(conn, szMXNode, IEC61850_FC_MX);  // 取振幅值
                stBody.value = stFCMX.AccPri1;
                bGetDeviceBindRelationship(szMXNode, stBody, stBase);

                sprintf(szMXNode, "TEMPLATEMONT/SVBR%02d.AccPri%d.mag.t", i, n);
                stBody.dtime = getTimeiValue(conn, szMXNode, IEC61850_FC_MX);
                Add_SetList(&stBody,&stBase);
                break;
            case 2:
                stFCMX.AccPri2 = getAccPriValue(conn, szMXNode, IEC61850_FC_MX);
                break;
            case 3:
                stFCMX.AccPri3 = getAccPriValue(conn, szMXNode, IEC61850_FC_MX);
                break;
            case 4:
                stFCMX.AccPri4 = getAccPriValue(conn, szMXNode, IEC61850_FC_MX);
                break;
            case 5:
                stFCMX.AccPri5 = getAccPriValue(conn, szMXNode, IEC61850_FC_MX);
                break;
            case 6:
                stFCMX.AccPri6 = getAccPriValue(conn, szMXNode, IEC61850_FC_MX);
                break;
            case 7:
                stFCMX.AccPri7 = getAccPriValue(conn, szMXNode, IEC61850_FC_MX);
                break;
            case 8:
                stFCMX.AccPri8 = getAccPriValue(conn, szMXNode, IEC61850_FC_MX);
                break;
            case 9:
                stFCMX.AccPri9 = getAccPriValue(conn, szMXNode, IEC61850_FC_MX);
                break;
            case 10:
                stFCMX.AccPri10 = getAccPriValue(conn, szMXNode, IEC61850_FC_MX);
                break;
            case 11:
                stFCMX.AccPri11 = getAccPriValue(conn, szMXNode, IEC61850_FC_MX);
                break;
            case 12:
                stFCMX.AccPri12 = getAccPriValue(conn, szMXNode, IEC61850_FC_MX);
                break;
            case 13:
                stFCMX.AccPri13 = getAccPriValue(conn, szMXNode, IEC61850_FC_MX);
                break;
            case 14:
                stFCMX.AccPri14 = getAccPriValue(conn, szMXNode, IEC61850_FC_MX);
                break;
            case 15:
                stFCMX.AccPri15 = getAccPriValue(conn, szMXNode, IEC61850_FC_MX);
                break;
            case 16:
                stFCMX.AccPri16 = getAccPriValue(conn, szMXNode, IEC61850_FC_MX);
                break;
            case 17:
                stFCMX.AccPri17 = getAccPriValue(conn, szMXNode, IEC61850_FC_MX);
                break;
            case 18:
                stFCMX.AccPri18 = getAccPriValue(conn, szMXNode, IEC61850_FC_MX);
                break;
            case 19:
                stFCMX.AccPri19 = getAccPriValue(conn, szMXNode, IEC61850_FC_MX);
                break;
            } // end switch
        }// end for (n)
        sprintf(szMXNode, "TEMPLATEMONT/SVBR%02d.EnvNoiPri.mag.f", i);  // 噪声值
        //sprintf(szMXNode, "TEMPLATEMONT/SVBR%02d$EnvNoiPri$mag$f", i);  // 噪声值
        stFCMX.EnvNoiPri = getAccPriValue(conn, szMXNode, IEC61850_FC_MX);
        sprintf(szMXNode, "TEMPLATEMONT/SVBR%02d.NeuCur.mag.f", i);      // 中心点电流
        //sprintf(szMXNode, "TEMPLATEMONT/SVBR%02d$NeuCur$mag$f", i);      // 中心点电流
        stFCMX.NeuCur = getAccPriValue(conn, szMXNode, IEC61850_FC_MX);
    }
}
#endif

// 获取实时监测数据-遥信
void vGetFCMXData_YX(IedConnection conn)
{
    ST_WARN_ALARM stFCST;
    memset(&stFCST, 0x00, sizeof(ST_WARN_ALARM));
    char szMXNode[256] = { 0 };
    for (int i = 1; i <= 12; i++)    // SVBR01-SVBR16
    {
        // 振动分量监测值
        for (int n = 1; n <= 19; n++)
        {
            sprintf(szMXNode, "TEMPLATEMONT/SVBR%02d.AccPriAlm%d.stVal", i, n);
            switch (n)
            {
            case 1:
                stFCST.AccPriAlm1 = getAlarmValue(conn, szMXNode, IEC61850_FC_ST);
                break;
            case 2:
                stFCST.AccPriAlm2 = getAlarmValue(conn, szMXNode, IEC61850_FC_ST);
                break;
            case 3:
                stFCST.AccPriAlm3 = getAlarmValue(conn, szMXNode, IEC61850_FC_ST);
                break;
            case 4:
                stFCST.AccPriAlm4 = getAlarmValue(conn, szMXNode, IEC61850_FC_ST);
                break;
            case 5:
                stFCST.AccPriAlm5 = getAlarmValue(conn, szMXNode, IEC61850_FC_ST);
                break;
            case 6:
                stFCST.AccPriAlm6 = getAlarmValue(conn, szMXNode, IEC61850_FC_ST);
                break;
            case 7:
                stFCST.AccPriAlm7 = getAlarmValue(conn, szMXNode, IEC61850_FC_ST);
                break;
            case 8:
                stFCST.AccPriAlm8 = getAlarmValue(conn, szMXNode, IEC61850_FC_ST);
                break;
            case 9:
                stFCST.AccPriAlm9 = getAlarmValue(conn, szMXNode, IEC61850_FC_ST);
                break;
            case 10:
                stFCST.AccPriAlm10 = getAlarmValue(conn, szMXNode, IEC61850_FC_ST);
                break;
            case 11:
                stFCST.AccPriAlm11 = getAlarmValue(conn, szMXNode, IEC61850_FC_ST);
                break;
            case 12:
                stFCST.AccPriAlm12 = getAlarmValue(conn, szMXNode, IEC61850_FC_ST);
                break;
            case 13:
                stFCST.AccPriAlm13 = getAlarmValue(conn, szMXNode, IEC61850_FC_ST);
                break;
            case 14:
                stFCST.AccPriAlm14 = getAlarmValue(conn, szMXNode, IEC61850_FC_ST);
                break;
            case 15:
                stFCST.AccPriAlm15 = getAlarmValue(conn, szMXNode, IEC61850_FC_ST);
                break;
            case 16:
                stFCST.AccPriAlm16 = getAlarmValue(conn, szMXNode, IEC61850_FC_ST);
                break;
            case 17:
                stFCST.AccPriAlm17 = getAlarmValue(conn, szMXNode, IEC61850_FC_ST);
                break;
            case 18:
                stFCST.AccPriAlm18 = getAlarmValue(conn, szMXNode, IEC61850_FC_ST);
                break;
            case 19:
                stFCST.AccPriAlm19 = getAlarmValue(conn, szMXNode, IEC61850_FC_ST);
                break;
            } // end switch
        }// end for (n)
        sprintf(szMXNode, "TEMPLATEMONT/SVBR%02d.MoDevConf.stVal", i);  // 通信异常值
        stFCST.MoDevConf = getAlarmValue(conn, szMXNode, IEC61850_FC_ST);

        sprintf(szMXNode, "TEMPLATEMONT/SVBR%02d.MoDevDetF.stVal", i);  // 传感器自检异常
        stFCST.MoDevDetF = getAlarmValue(conn, szMXNode, IEC61850_FC_ST);

        sprintf(szMXNode, "TEMPLATEMONT/SVBR%02d.MoDevSigF.stVal", i);  // 传感器信号异常
        stFCST.MoDevSigF = getAlarmValue(conn, szMXNode, IEC61850_FC_ST);

        sprintf(szMXNode, "TEMPLATEMONT/SVBR%02d.MoDevPowF.stVal", i);  // 传感器供电异常
        stFCST.MoDevPowF = getAlarmValue(conn, szMXNode, IEC61850_FC_ST);

        sprintf(szMXNode, "TEMPLATEMONT/SVBR%02d.EnvNoiPriAlm.stVal", i);  // 传感器供电异常
        stFCST.EnvNoiPriAlm = getAlarmValue(conn, szMXNode, IEC61850_FC_ST);

        sprintf(szMXNode, "TEMPLATEMONT/SVBR%02d.NeuCurAlm.stVal", i);  // 中性点电流告警
        stFCST.NeuCurAlm = getAlarmValue(conn, szMXNode, IEC61850_FC_ST);
    }
}

// 设置IED控制参数
int writeCtlValue(IedConnection conn, char *pObjname, FunctionalConstraint FC, int val)
{
    /* write a variable to the server */
    IedClientError error;
    MmsValue* value = MmsValue_newIntegerFromInt32(val); // MmsValue_newVisibleString("libiec61850.com");
    if (MmsValue_toInt32(value) != val) return -1;
    
    IedConnection_writeObject(conn, &error, pObjname, FC, value);
    if (error != IED_ERROR_OK)
        vPrtLogMsg(LOG_DEBUG, error, "write [%s] alarm bool value: %i, MSG=%s", pObjname, val, getIedError(error));
    MmsValue_delete(value);
    return error;
}

// 设置控制参数
void vSetFCSEData_CTRL(IedConnection conn)
{
    char szFCSECtrl[256] = { 0 };
    for (int i = 1; i < 12; i++) {
        // 缺失使能参数

        sprintf(szFCSECtrl, "TEMPLATEMONT/SVBR%02d.SmpProd.setVal",i);  // 采集频率
        writeCtlValue(conn, szFCSECtrl, IEC61850_FC_SE, 5666);

        sprintf(szFCSECtrl, "TEMPLATEMONT/SVBR%02d.SmpInt.setVal", i);  // 采样间隔(H)
        writeCtlValue(conn, szFCSECtrl, IEC61850_FC_SE, 5666);

        sprintf(szFCSECtrl, "TEMPLATEMONT/SVBR%02d.StartTime.setVal", i);//起始时间
        writeCtlValue(conn, szFCSECtrl, IEC61850_FC_SE, 5666);
    }
}

void vGetDataSetList(IedConnection conn)
{

#if 0
    /* read an analog measurement value from server */
    MmsValue* value = IedConnection_readObject(con, &error, "simpleIOGenericIO/GGIO1.AnIn1.mag.f", IEC61850_FC_MX);

    if (value != NULL) {
        float fval = MmsValue_toFloat(value);
        printf("read float value: %f\n", fval);
        //MmsValue_delete(value);
    }

    /* read data set 获取服务端的实时数据集信息*/
    ClientDataSet clientDataSet = IedConnection_readDataSetValues(con, &error, "simpleIOGenericIO/LLN0.Events", NULL);

    if (clientDataSet == NULL)
        printf("failed to read dataset\n");
    else {
        printf(" DataSet Ref: %s\n", (ClientDataSet_getReference(clientDataSet)));
        printf(" DataSet Val: %f\n", MmsValue_toFloat(ClientDataSet_getValues(clientDataSet)));
    }


    bool deletable;
    /*获取数据集列表，这里要用LinkedList，顺序不能错误，不然在回调函数中匹配的数据信息就是错误的*/
    LinkedList list = IedConnection_getDataSetDirectory(con, &error, "simpleIOGenericIO/LLN0$Events", &deletable);


    /* Read RCB values 获取RCB的信息 */
    ClientReportControlBlock rcb =
        IedConnection_getRCBValues(con, &error, "simpleIOGenericIO/LLN0.RP.EventsRCB01", NULL);

    printf(" RCBReference :%s\n", ClientReportControlBlock_getObjectReference(rcb));

    printf(" RCBisBuffered :%i\n", ClientReportControlBlock_isBuffered(rcb));

    printf(" RCBgetRptId  :%s\n", ClientReportControlBlock_getRptId(rcb));

    printf(" RCBgetResv  :%i\n", ClientReportControlBlock_getResv(rcb));

    printf(" RCBgetResv  :%s\n", ClientReportControlBlock_getDataSetReference(rcb));

    bool rptEna = ClientReportControlBlock_getRptEna(rcb);

    printf("RptEna = %i\n", rptEna);

    /* Install handler for reports 注册收到报告时的回调函数 */
    IedConnection_installReportHandler(con, "simpleIOGenericIO/LLN0.RP.EventsRCB01",
        ClientReportControlBlock_getRptId(rcb), reportCallbackFunctions, (void *)list);

    /* Set trigger options and enable report */
    //#define TRG_OPT_DATA_CHANGED    1    数据变换
    //#define TRG_OPT_QUALITY_CHANGED 2    数据质量变换
    //#define TRG_OPT_DATA_UPDATE     4    数据更新
    //#define TRG_OPT_INTEGRITY       8    周期上送
    //#define TRG_OPT_GI              16   一般请求(询问)
    //#define TRG_OPT_TRANSIENT       128  仅在上升沿触发报告（瞬态变量）
    //设置报告的触发参数，这里设置了周期性上报和数据改变时上报
    ClientReportControlBlock_setTrgOps(rcb, TRG_OPT_DATA_UPDATE | TRG_OPT_INTEGRITY | TRG_OPT_GI);
    //设置报告的触发参数，使能报告控制块
    ClientReportControlBlock_setRptEna(rcb, true);
    //设置报告的触发参数，设置周期性报告周期，这里设置为1000毫秒上报一次
    ClientReportControlBlock_setIntgPd(rcb, 1000);
    //设置报告控制块参数
    IedConnection_setRCBValues(con, &error, rcb, RCB_ELEMENT_RPT_ENA | RCB_ELEMENT_TRG_OPS | RCB_ELEMENT_INTG_PD | RCB_ELEMENT_DATSET, true);

    if (error != IED_ERROR_OK)
        printf("report activation failed (code: %i)\n", error);

    Sleep(1000);

    /* trigger GI report 使能总召唤 */
    ClientReportControlBlock_setGI(rcb, true);
    ///*使在报告控制块里添加总召唤的参数*/
    IedConnection_setRCBValues(con, &error, rcb, RCB_ELEMENT_GI, true);

    if (error != IED_ERROR_OK)
        printf("Error triggering a GI report (code: %i)\n", error);

    Sleep(60000);
    //IED_ERROR_TEMPORARILY_UNAVAILABLE
    /* disable reporting 关闭报告控制块 */
    ClientReportControlBlock_setRptEna(rcb, false);


    IedConnection_setRCBValues(con, &error, rcb, RCB_ELEMENT_RPT_ENA, true);

    if (error != IED_ERROR_OK)
        printf("disable reporting failed (code: %i)\n", error);


    ClientDataSet_destroy(clientDataSet);
    ClientReportControlBlock_destroy(rcb);
#endif

}