/**************************************************************************** ** 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 #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 *)¶meter; 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::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::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::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::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::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::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 }