報(bào)告服務(wù)是IEC61850進(jìn)行數(shù)據(jù)交互的重要手段,libIEC61850提供了較為全面的報(bào)告手段,包括:數(shù)據(jù)變化、品質(zhì) 變化、數(shù)據(jù)更新、周期性以及總召,基本涵蓋了61850標(biāo)準(zhǔn)中的要求。
一、常用ClientReportControl API
- ClientReportControlBlock IedConnection_getRCBValues(IedConnection self, IedClientError* error, char* rcbReference, ClientReportControlBlock updateRcb)
獲取服務(wù)端指定報(bào)告控制塊信息,并將信息返回至 ClientReportControlBlock 對象。輸入?yún)?shù)中要注意rcbReference, 該參數(shù)是該報(bào)告控制塊的引用字符串信息,由:IEDName + LDevice inst + / + LLN0 + . + buffeded + . + report control name. 其中要注意buffered,如果該報(bào)告控制塊是unbuffered,就用"RP"表示,如果是buffered,就用BR表示,例如官方服務(wù)端例子server_example3中的報(bào)告控制塊就可以表示為"simpleIOGenericIO/LLN0.RP.EventsRCB".
- void IedConnection_installReportHandler(IedConnection self, char* rcbReference, char* rptId, ReportCallbackFunction handler, void* handlerParameter)
注冊報(bào)告控制塊的處理程序,其中rptId可以通過ClientReportControlBlock_getRptId(ClientReportControlBlock )函數(shù)獲得,也可以通過解析出ICD文件中rptID屬性獲得;ReportCallbackFunction handler是當(dāng)客戶端收到服務(wù)端報(bào)告時(shí)的回調(diào)函數(shù), ReportCallbackFunction 是一個(gè) void (void * parameter, ClientReport report)類型的函數(shù)指針,parameter是需要傳入的參數(shù),report是收到的報(bào)告信息,里面包含了數(shù)據(jù)值、時(shí)標(biāo)等信息。
- void ClientReportControlBlock_setTrgOps(ClientReportControlBlock self, int trgOps)
設(shè)置報(bào)告的觸發(fā)條件,使用TRG_OPT_DATA_CHANGED,TRG_OPT_QUALITY_CHANGED,TRG_OPT_DATA_UPDATE,TRG_OPT_INTEGRITY,TRG_OPT_GI宏設(shè)置條件參數(shù),中間可以使用 or 運(yùn)算設(shè)置多個(gè)觸發(fā)條件。
- void ClientReportControlBlock_setRptEna(ClientReportControlBlock self, bool rptEna)
使能報(bào)告控制塊。
- void ClientReportControlBlock_setIntgPd(ClientReportControlBlock self, uint32_t intgPd)
設(shè)置周期性上報(bào)時(shí)的周期值,以毫秒(ms)為單位。
- void IedConnection_setRCBValues(IedConnection self, IedClientError* error, ClientReportControlBlock rcb, uint32_t parametersMask, bool singleRequest)
設(shè)置報(bào)告控制塊參數(shù),獲取到的報(bào)告控制塊可以通過該方法設(shè)置參數(shù),parameterMask可以通過RCB_ELEMENT_系列宏設(shè)置相關(guān)參數(shù),由于該函數(shù)我還不是太深入理解,在此不作太多說明,待以后補(bǔ)完。
- void ClientReportControlBlock_setGI(ClientReportControlBlock self, bool gi)
使能總召報(bào)告。
- MmsValue* ClientReport_getDataSetValues(ClientReport self)
獲取報(bào)告中的數(shù)據(jù)值。報(bào)告控制塊是和數(shù)據(jù)集(DataSet)關(guān)聯(lián)的,因此這里獲取到的MmsValue是整個(gè)數(shù)據(jù)集的數(shù)據(jù)集合,可以通過MmsValue_getElement(MmsValue *)函數(shù)獲取其中的數(shù)據(jù)對象(DO)或者數(shù)據(jù)屬性(DA)。
這里有個(gè)吐槽的地方,就是這里獲取到的MmsValue是不包含DA或者DO的Reference的,因此如果沒有獲取數(shù)據(jù)集信息,就無法分辨是哪一個(gè)數(shù)據(jù),好在無論任何情況下返回的報(bào)告總是按照數(shù)據(jù)集里的DO或者DA的索引進(jìn)行排序的,通過這里的索引和獲取到的數(shù)據(jù)集索引進(jìn)行匹配,就可以分辨每個(gè)數(shù)據(jù)。
注:由于存在上面這個(gè)問題,因此最好是使用IedConnection_getDataSetDirectory()方法獲取服務(wù)端實(shí)時(shí)的數(shù)據(jù)集信息,而不是通過解析ICD文件獲取,因?yàn)镮CD文件中的順序和名稱很有可能是錯(cuò)誤的。
- ReasonForInclusion ClientReport_getReasonForInclusion(ClientReport self, int elementIndex)
獲取報(bào)告原因。報(bào)告原因就是包括了上面提到的5種觸發(fā)條件。elementIndex就是DO或者DA數(shù)據(jù)集中的索引。
二、使用方法
- 注冊報(bào)告控制塊
/創(chuàng)建連接/
IedClientError error;
IedConnection con = IedConnection_create();
IedConnection_connect(con, &error, hostname, tcpPort);
/獲取RCB的信息/
ClientReportControlBlock rcb = IedConnection_getRCBValues(con,&error,"simpleIOGenericIO/LLN0.RP.EventRCB", NULL);
/獲取服務(wù)端的實(shí)時(shí)數(shù)據(jù)集信息/
ClientDataSet clientDataSet = IedConnection_readDataSetValues(con, &error, "simpleIOGenericIO/LLN0$Events", NULL);
if (clientDataSet == NULL)
printf("failed to read dataset\n");
bool deletable;
/獲取數(shù)據(jù)集列表,這里要用LinkedList,順序不能錯(cuò)誤,不然在回調(diào)函數(shù)中匹配的數(shù)據(jù)信息就是錯(cuò)誤的/
LinkedList list = IedConnection_getDataSetDirectory(con,&error,"simpleIOGenericIO/LLN0$Events",&deletable);
/注冊收到報(bào)告時(shí)的回調(diào)函數(shù)/
IedConnection_installReportHandler(con, "simpleIOGenericIO/LLN0.RP.EventRCB", ClientReportControlBlock_getRptId(rcb), reportCallbackFunction, (void *)list);
/設(shè)置報(bào)告的觸發(fā)參數(shù),這里設(shè)置了周期性上報(bào)和數(shù)據(jù)改變時(shí)上報(bào)/
ClientReportControlBlock_setTrgOps(rcb, TRG_OPT_INTEGRITY | TRG_OPT_DATA_CHANGED);
/設(shè)置報(bào)告的觸發(fā)參數(shù),使能報(bào)告控制塊/
ClientReportControlBlock_setRptEna(rcb, true);
/設(shè)置報(bào)告的觸發(fā)參數(shù),設(shè)置周期性報(bào)告周期,這里設(shè)置為5000毫秒上報(bào)一次/
ClientReportControlBlock_setIntgPd(rcb, 5000);
/設(shè)置報(bào)告控制塊參數(shù)/
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);
/* trigger GI report /
/使能總召喚/
ClientReportControlBlock_setGI(rcb, true);
/使在報(bào)告控制塊里添加總召喚的參數(shù)*/
IedConnection_setRCBValues(con, &error, rcb, RCB_ELEMENT_GI, true);
while (state == running)
{
;
}
/關(guān)閉報(bào)告控制塊/
ClientReportControlBlock_setRptEna(rcb, false);
- 收到報(bào)告時(shí)的回調(diào)函數(shù)
void reportCallbackFunction(void* parameter, ClientReport report)
{
/獲取報(bào)告中的數(shù)據(jù)/
MmsValue* dataSetValues = ClientReport_getDataSetValues(report);
/獲得數(shù)據(jù)集列表/
LinkedList * list = (LinedList *) parameter;
int i;
/*遍歷數(shù)據(jù)集*/
while (LinkedList_getNext(list) != NULL)
{
/*獲取下一個(gè)節(jié)點(diǎn),可能是為了便于便利,獲取到的LinkedList的實(shí)際值時(shí)從第二個(gè)節(jié)點(diǎn)開始的*/
list = LinkedList_getNext(list);
/*獲取上報(bào)原因*/
ReasonForInclusion reason = ClientReport_getReasonForInclusion(report, i);
/*這里如果reason是REASON_NOT_INCLUDED,是不能獲取數(shù)據(jù)值的*/
/*例如像數(shù)據(jù)變化時(shí),可能只上報(bào)了1個(gè)數(shù)據(jù),但是dataSetValues中會有所有數(shù)據(jù)的索引,只不過其他數(shù)據(jù)的reason將會是REASON_NOT_INCLUDED*/
if (reason != REASON_NOT_INCLUDED) {
/*這里MmsValue_getBoolean只是假設(shè)返回的都是boolean類型,實(shí)際應(yīng)用中要根據(jù)實(shí)際數(shù)據(jù)類型調(diào)用不同的函數(shù)*/
printf(" %s : %i (included for reason %i)\n", list->data, i, MmsValue_getBoolean(MmsValue_getElement(dataSetValues, i)), reason);
}
}
}
作者:fujicororo
來源:CSDN
原文:https://blog.csdn.net/fujicororo/article/details/40741351
版權(quán)聲明:本文為博主原創(chuàng)文章,轉(zhuǎn)載請附上博文鏈接!