From 622341b73e925ff21bfde346c07ffa76bb55f755 Mon Sep 17 00:00:00 2001 From: shi_jq Date: Thu, 13 Mar 2025 15:53:44 +0800 Subject: [PATCH] =?UTF-8?q?[ref]=E5=90=8C=E6=AD=A5711?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fes/iec61850/clientScout/clientScout.pro | 3 +- .../fes/iec61850/common/IEC850C_Resource.cpp | 23 +++ .../communication/IEC850C_CommService.cpp | 101 +++++++++ .../communication/IEC850C_CommService.h | 3 + .../iec61850/communication/IEC850C_Common.cpp | 20 +- .../communication/IEC850C_FESClient.cpp | 195 +++++++++++++++--- .../communication/IEC850C_FESClient.h | 3 +- .../communication/IEC850C_MonitorThread.cpp | 30 ++- .../communication/IEC850C_MonitorThread.h | 8 + .../communication/IEC850C_OperTracking.cpp | 16 ++ .../iec61850/communication/IEC850C_Report.cpp | 29 ++- .../iec61850/communication/iec61850dll.cpp | 5 + .../src/fes/iec61850/include/IEC61850c_pub.h | 8 +- .../src/fes/iec61850/include/IEC61850dll.h | 4 +- .../fes/iec61850/include/IEC850C_Resource.h | 21 ++ product/src/fes/iec61850/libs/libasn1.pro | 31 ++- product/src/fes/iec61850/libs/libcfgtool.pro | 3 + .../src/fes/iec61850/libs/libiec61850c.pro | 3 + product/src/fes/iec61850/libs/libutil.pro | 2 +- .../iec61850/sisco/mvl/usr/client/cli_rpt.c | 48 +---- 20 files changed, 436 insertions(+), 120 deletions(-) diff --git a/product/src/fes/iec61850/clientScout/clientScout.pro b/product/src/fes/iec61850/clientScout/clientScout.pro index ac637792..9b652359 100644 --- a/product/src/fes/iec61850/clientScout/clientScout.pro +++ b/product/src/fes/iec61850/clientScout/clientScout.pro @@ -26,6 +26,7 @@ LIBS += -llibcositcps \ -llibslog \ -lkbd61850dbinterface +LIBS += -lboost_system win32{ LIBS += -lws2_32 @@ -40,7 +41,7 @@ include(../iec860.pri) INCLUDEPATH += modules/Scout - +INCLUDEPATH += ../../include/ SOURCES += \ diff --git a/product/src/fes/iec61850/common/IEC850C_Resource.cpp b/product/src/fes/iec61850/common/IEC850C_Resource.cpp index ca29b244..19e2ed58 100644 --- a/product/src/fes/iec61850/common/IEC850C_Resource.cpp +++ b/product/src/fes/iec61850/common/IEC850C_Resource.cpp @@ -428,6 +428,29 @@ int GetYkDaInfobyChanNoAndPointID(int nChanNo, int pointid, tagYkTypeInfo &resul return EN_RET_FAILURE; } + +//通过测点ID和通道号,获取模拟量控制/混合量控制的其他信息 +int GetCtrlInfobyChanNoAndPointID(int nChanNo,eSignalInstType pntType,int pointid, tagYkTypeInfo &result) +{ + //通过通道号找到装置的tagName + //通过装置的TagName和pointid,找到遥控的属性 + if(g_mapChanNoToIedInfo.find(nChanNo) != g_mapChanNoToIedInfo.end()) + { + const CIedInfo &ied = g_mapChanNoToIedInfo[nChanNo]; + QString key = IEC850C::getCfgSignalInstKey(ied.m_strIedName, pntType,pointid); + if(g_mapCfgSignalInst.contains(key)) + { + const CCfgSignalInst *pSignal = g_mapCfgSignalInst[key]; + result.m_nTypeid = pSignal->m_nTypeid; + result.m_strRef = pSignal->m_strIedName + pSignal->m_strPointRef; + return EN_RET_SUCCESS; + } + else + return EN_RET_ERROR_POINTNO_ERROR; + } + return EN_RET_FAILURE; +} + void GetDeviceParaRefbyDzCode(int nChanNo,int nDZCode, QString &strDeviceReference) { //定值和装置参数如何区分?,DZCode是唯一的 diff --git a/product/src/fes/iec61850/communication/IEC850C_CommService.cpp b/product/src/fes/iec61850/communication/IEC850C_CommService.cpp index 77852610..9c18b6d1 100644 --- a/product/src/fes/iec61850/communication/IEC850C_CommService.cpp +++ b/product/src/fes/iec61850/communication/IEC850C_CommService.cpp @@ -571,6 +571,66 @@ static ST_VOID _addVariablesWithFc(const char *pszFC, char *pszLnName, char *psz } } +static ST_VOID _AO_tagControlParaToTagVariable(const tagControlPara *pSBOw, tagVariable *ptVarSBOw) +{ + assert(pSBOw != NULL); + assert(ptVarSBOw != NULL); + tagPrimitiveValue tPrim; + + ptVarSBOw->reference = pSBOw->ControlObjectReference; + + ptVarSBOw->mapRefToPrim.clear(); + tPrim = pSBOw->ctlVal; + ptVarSBOw->mapRefToPrim["$setMag$f"] = tPrim; + + if(pSBOw->operTm.secs != 0) + { + tPrim.basicType = EN_BasicType_UTC_TIME_STAMP; + memcpy(&tPrim.value.utc_time_value, &pSBOw->operTm, sizeof(tagUtcTimeStamp)); + ptVarSBOw->mapRefToPrim["$operTm"] = tPrim; + } + + tPrim.basicType = EN_BasicType_INT8; + tPrim.value.int8_value = pSBOw->origin.orCat; + ptVarSBOw->mapRefToPrim["$origin$orCat"] = tPrim; + + tPrim.basicType = EN_BasicType_OCTET_STRING; + tPrim.value.octet_string_value = pSBOw->origin.orIdent; + ptVarSBOw->mapRefToPrim["$origin$orIdent"] = tPrim; + + tPrim.basicType = EN_BasicType_INT8U; + tPrim.value.int8u_value = pSBOw->ctlNum; + ptVarSBOw->mapRefToPrim["$ctlNum"] = tPrim; + + tPrim.basicType = EN_BasicType_UTC_TIME_STAMP; + //tPrim.value.utc_time_value = pSBOw->T; + //2022-07-14 thxiao 遥控时间取当前的UTC时间,按照R80 61850的格式填写,不需要加时区 + M_UTC_TIME_STAMP localTime; + localTime.secs = (ST_UINT32)time(NULL); + localTime.fraction = 99; + localTime.qflags = 0xff; + + tPrim.value.utc_time_value = localTime; + + ptVarSBOw->mapRefToPrim["$T"] = tPrim; + + if(!ptVarSBOw->mapRefToPrim.contains("$operTm")) + { + ptVarSBOw->mapRefToPrim["$operTm"] = tPrim; + } + + tPrim.basicType = EN_BasicType_Boolean; + tPrim.value.boolean_value = pSBOw->Test; + ptVarSBOw->mapRefToPrim["$Test"] = tPrim; + + tPrim.basicType = EN_BaiscType_PACKED_LIST; + tPrim.value.packed_list_value = pSBOw->Check; + ptVarSBOw->mapRefToPrim["$Check"] = tPrim; + + ptVarSBOw->respTag = 0; //ASSUME FAILURE + ptVarSBOw->dataAccessError = 0; +} + static ST_VOID _tagControlParaToTagVariable(const tagControlPara *pSBOw, tagVariable *ptVarSBOw) { assert(pSBOw != NULL); @@ -614,6 +674,11 @@ static ST_VOID _tagControlParaToTagVariable(const tagControlPara *pSBOw, tagVari ptVarSBOw->mapRefToPrim["$T"] = tPrim; + if(!ptVarSBOw->mapRefToPrim.contains("$operTm")) + { + ptVarSBOw->mapRefToPrim["$operTm"] = tPrim; + } + tPrim.basicType = EN_BasicType_Boolean; tPrim.value.boolean_value = pSBOw->Test; ptVarSBOw->mapRefToPrim["$Test"] = tPrim; @@ -1934,6 +1999,42 @@ M_RET Operate(mvl_net_info *con, const tagControlPara *pOperate, M_ENUMERATED *p return nRetCode; } + +M_RET YtWithOperObject(mvl_net_info *con, const tagControlPara *pOperate, M_ENUMERATED *pAddCause, const int timeOut) +{ + M_RET nRetCode = EN_RET_FAILURE; + ST_INT8 nAddCause = -1; + tagVariable tVarOperate; + QVector vecVarOperate; + + tagControlPara tSBOwControl = *pOperate; + tSBOwControl.ControlObjectReference += "$Oper"; + + _AO_tagControlParaToTagVariable(&tSBOwControl, &tVarOperate); + + u_start_tracking_last_appl_error(tSBOwControl.ControlObjectReference.toLocal8Bit().data()); + + vecVarOperate.push_back(tVarOperate); + nRetCode = SetDataValues(con, vecVarOperate, timeOut); + if (EN_RET_SUCCESS == nRetCode) + { + if (vecVarOperate[0].respTag != 1) + { + //Write Failure + nAddCause = u_get_add_cause(); + nRetCode = EN_RET_FAILURE; + } + } + + if (pAddCause) + { + *pAddCause = nAddCause; + } + + return nRetCode; +} + + /*============================================================================== * 函数功能: 遥控时间激活操作 * 输入参数: con 通道连接 diff --git a/product/src/fes/iec61850/communication/IEC850C_CommService.h b/product/src/fes/iec61850/communication/IEC850C_CommService.h index 87e401ca..f179e698 100644 --- a/product/src/fes/iec61850/communication/IEC850C_CommService.h +++ b/product/src/fes/iec61850/communication/IEC850C_CommService.h @@ -526,6 +526,9 @@ M_RET Select(mvl_net_info *con, const QString &strControlObjectReference, cons M_RET SelectWithValue(mvl_net_info *con, const tagControlPara *ptSBOw, M_ENUMERATED *pAddCause, const int timeOut); // 带值选择 M_RET Cancel(mvl_net_info *con, const tagControlPara *ptCancel, M_ENUMERATED *pAddCause, const int timeOut); // 取消 M_RET Operate(mvl_net_info *con, const tagControlPara *ptOperate, M_ENUMERATED *pAddCause, const int timeOut); // 执行 +//用于实现模拟量和混合量的对象控制类型,部分设备的模拟量使用了增强型的控制对象类型,类似遥控的Oper对象 +M_RET YtWithOperObject(mvl_net_info *con, const tagControlPara *ptOperate, M_ENUMERATED *pAddCause, const int timeOut); // 执行 + M_RET TimeActivationOperate(mvl_net_info *con, const tagControlPara *ptOperate, M_ENUMERATED *pAddCause, const int timeOut); // 时间激活操作 // 文件传输模型 diff --git a/product/src/fes/iec61850/communication/IEC850C_Common.cpp b/product/src/fes/iec61850/communication/IEC850C_Common.cpp index cf68d814..54bc7ad8 100644 --- a/product/src/fes/iec61850/communication/IEC850C_Common.cpp +++ b/product/src/fes/iec61850/communication/IEC850C_Common.cpp @@ -100,13 +100,15 @@ int getFesDataType(int signalInstType) switch(signalInstType) { case eSignalInstType_Accuml: - return 3; + return ePointType_ACC; case eSignalInstType_Analog: - return 1; + return ePointType_AI; case eSignalInstType_Digital: - return 2; + return ePointType_DI; case eSignalInstType_Const: - return 4; + return ePointType_DZ; + case eSignalInstType_Mix: + return ePointType_Mi; default: return -1; } @@ -117,14 +119,16 @@ eSignalInstType getSignalInstType(int fesType) { switch(fesType) { - case 3: + case ePointType_ACC: return eSignalInstType_Accuml; - case 1: + case ePointType_AI: return eSignalInstType_Analog; - case 2: + case ePointType_DI: return eSignalInstType_Digital; - case 4: + case ePointType_DZ: return eSignalInstType_Const; + case ePointType_Mi: + return eSignalInstType_Mix; default: return eSignalInstType_Invalid; } diff --git a/product/src/fes/iec61850/communication/IEC850C_FESClient.cpp b/product/src/fes/iec61850/communication/IEC850C_FESClient.cpp index 835f3d16..d109df7d 100644 --- a/product/src/fes/iec61850/communication/IEC850C_FESClient.cpp +++ b/product/src/fes/iec61850/communication/IEC850C_FESClient.cpp @@ -34,7 +34,8 @@ IEC850C::ComtradeThread g_ComtradeThread; IEC850C::LogThread g_LogThread; IEC850C::doCommServiceThread g_doCommServiceThread; #endif -int _writeSEValue(const int ChanNo, const int Timeout,const int GroupNo, const std::vector& SetValue, std::vector& RespValue, eSignalInstType eSignalType); +//retCtrlSelect是否执行了定值区激活命令 +int _writeSEValue(const int ChanNo, const int Timeout,const int GroupNo, const std::vector& SetValue, std::vector& RespValue, eSignalInstType eSignalType, bool &bCtrlSelect); #ifdef DEBUG_SISCO @@ -619,13 +620,14 @@ int FES_ReportEnable(const int EnableFlag) return 0; } -bool GetTagControlPara(tagControlPara &tControlPara,const SIEC61850CtrlReq *pCmd,const tagYkTypeInfo &YkInfo) +bool GetTagControlPara(tagControlPara &tControlPara,const SIEC61850CtrlReq *pCmd,const tagYkTypeInfo &YkInfo,const QString &leaf) { bool ret = true; tControlPara.ControlObjectReference = YkInfo.m_strRef;//"PRS7367DPROT/LLN0$CO$FuncEna1"; int nTypeId = YkInfo.m_nTypeid; - EnBasicType basicType = GetBasicType(nTypeId, "$ctlVal"); + //EnBasicType basicType = GetBasicType(nTypeId, "$ctlVal"); + EnBasicType basicType = GetBasicType(nTypeId, leaf); //判断是否为不支持的基本类型,不支持则直接返回 switch(basicType) @@ -720,8 +722,9 @@ bool GetTagControlPara(tagControlPara &tControlPara,const SIEC61850CtrlReq *pCm int do_writeAOValue(const int ChanNo, const int Timeout, const std::vector& SetValue, std::vector& RespValue) { - int ret = _writeSEValue(ChanNo, Timeout, 0, SetValue, RespValue, eSignalInstType_AnalogCtrl); - if(0 == ret) + bool bCtrlSelect = false; + int ret = _writeSEValue(ChanNo, Timeout, 0, SetValue, RespValue, eSignalInstType_AnalogCtrl,bCtrlSelect); + if(0 == ret && bCtrlSelect) { // 下发定值确认修改命令 ret = FES_ConfirmEditSGValue(ChanNo, Timeout, 0); @@ -730,7 +733,21 @@ int do_writeAOValue(const int ChanNo, const int Timeout, const std::vector& SetValue, std::vector& RespValue) +{ + bool bCtrlSelect = false; + int ret = _writeSEValue(ChanNo, Timeout, 0, SetValue, RespValue, eSignalInstType_MixCtrl,bCtrlSelect); + if(0 == ret && bCtrlSelect) + { + // 下发定值确认修改命令 + ret = FES_ConfirmEditSGValue(ChanNo, Timeout, 0); + } + + return ret; +} + +eCtlModel GetCtlModel(mvl_net_info *conn, QString &ControlObjectReference, + const QStringList &ykFC,const QString &confFC, const int timeOut) { eCtlModel nCtlModel = eCtlModel_NULL; // 首先从资源区获取遥控测点的ctlModel值 @@ -740,7 +757,12 @@ eCtlModel GetCtlModel(mvl_net_info *conn, QString &ControlObjectReference, const // 资源区获取失败, 则在线读取 tagVariable tVariable; tVariable.reference = ControlObjectReference + QString("$ctlModel"); - tVariable.reference.replace("$CO$","$CF$"); //将遥控FC替换为配置FC + //tVariable.reference.replace("$CO$","$CF$"); //将遥控FC替换为配置FC + for(int nFCIdx = 0; nFCIdx < ykFC.size();nFCIdx++) + { + tVariable.reference.replace(ykFC.at(nFCIdx),confFC); //将遥控FC替换为配置FC + } + QVector vecVariables; vecVariables.push_back(tVariable); M_RET ret = GetDataValues(conn, vecVariables, timeOut); @@ -769,6 +791,64 @@ eCtlModel GetCtlModel(mvl_net_info *conn, QString &ControlObjectReference, const return nCtlModel; } +//Mo以对象形式控制,类似do控制,需要写整个控制对象 +int yt_OperObjControl(mvl_net_info *conn, const SIEC61850CtrlReq *pCmd,const tagYkTypeInfo &YkInfo, const int timeout) +{ + int ret = EN_RET_SUCCESS; + eCtlModel nCtlModel = eCtlModel_NULL; + M_ENUMERATED addCause = -1; + + //获取下发的遥控数据 + tagControlPara tControlPara; + if(!GetTagControlPara(tControlPara, pCmd, YkInfo,"$setMag$f")) + { + USER_LOG_ERR0("获取控制命令参数失败\n"); + return EN_RET_FAILURE; + } + + //获取遥控测点对象的ctlModel + const QStringList FClist = {"$SP$","$CO$"}; + nCtlModel = GetCtlModel(conn, tControlPara.ControlObjectReference,FClist,"$CF$", timeout); + if(nCtlModel == eCtlModel_NULL) + { + USER_LOG_ERR1("Get ctlModel for '%s' failed!", tControlPara.ControlObjectReference.toLocal8Bit().data()); + return EN_RET_FAILURE; + } + + // 根据约定的遥控类型, 结合ctlMode, 分别处理遥控命令 + switch(pCmd->CtrlActType) + { + case eCtrlType_excute: //执行类型 + { + switch (nCtlModel) + { + case eCtlModel_STATUS_ONLY: + USER_LOG_ERR1("对象 '%s' 为只读(ctlModel = 0), 不可遥控!", tControlPara.ControlObjectReference.toLocal8Bit().data()); + ret = EN_RET_FAILURE; + break; + case eCtlModel_DIRECT_WITH_NORMAL_SECURITY: + case eCtlModel_DIRECT_WITH_ENHANCED_SECURITY: + case eCtlModel_SBO_WITH_NORMAL_SECURITY: + case eCtlModel_SBO_WITH_ENHANCED_SECURITY: + ret = YtWithOperObject(conn, &tControlPara, &addCause, timeout); // 执行 + break; + default: + USER_LOG_ERR1("遥控类型[%d]错误\n", nCtlModel); + ret = EN_RET_FAILURE; + break; + } + break; + } + default: + USER_LOG_ERR1("遥控类型[%d]错误\n",pCmd->CtrlActType); + ret = EN_RET_FAILURE; + break; + } + + return ret; +} + + /*============================================================================== * 函数功能: 遥控处理函数 * 输入参数: conn 通道参数 @@ -786,14 +866,15 @@ int do_control(mvl_net_info *conn, const SIEC61850CtrlReq *pCmd,const tagYkType //获取下发的遥控数据 tagControlPara tControlPara; - if(!GetTagControlPara(tControlPara, pCmd, YkInfo)) + if(!GetTagControlPara(tControlPara, pCmd, YkInfo,"$ctlVal")) { USER_LOG_ERR0("获取控制命令参数失败\n"); return EN_RET_FAILURE; } //获取遥控测点对象的ctlModel - nCtlModel = GetCtlModel(conn, tControlPara.ControlObjectReference, timeout); + const QStringList FClist = {"$CO$"}; + nCtlModel = GetCtlModel(conn, tControlPara.ControlObjectReference,FClist,"$CF$", timeout); if(nCtlModel == eCtlModel_NULL) { USER_LOG_ERR1("Get ctlModel for '%s' failed!", tControlPara.ControlObjectReference.toLocal8Bit().data()); @@ -894,6 +975,64 @@ void get_ctrl_return(const SIEC61850CtrlReq *pCmd, SIEC61850CtrlResp &ControlRe ControlResult.retStatus = ret; } +//模拟量和混合量,类型遥控的Oper对象控制类型 +int _ytWithOperObject(const int ChanNo, const int Timeout,mvl_net_info *conn, + const SIEC61850CtrlReq *pControl,SIEC61850CtrlResp &ControlResult) +{ + tagYkTypeInfo resultInfo; + eSignalInstType instType = (pControl->PointType == ePointType_Ao) ? eSignalInstType_AnalogCtrl : eSignalInstType_MixCtrl; + + int ret = GetCtrlInfobyChanNoAndPointID(ChanNo,instType,pControl->PointId,resultInfo); + if(ret == EN_RET_SUCCESS) + ret = yt_OperObjControl(conn,pControl, resultInfo, Timeout); + get_ctrl_return(pControl, ControlResult, ret); + + return ret; +} + +//模拟量和混合量,值控制类型 +int _ytWithSingleValue(const int ChanNo, const int Timeout, + std::vector SetValue,std::vector &RespValue, + const SIEC61850CtrlReq *pControl, + SIEC61850CtrlResp &ControlResult) +{ + int ret = 0; + SetValue.clear(); + RespValue.clear(); + SIEC61850SGValue AOValue; + AOValue.DZCode = pControl->PointId; + AOValue.ValueType = pControl->ValueType; + memcpy(&(AOValue.Value) ,&(pControl->Value),sizeof(pControl->Value)); + SetValue.push_back(AOValue); + if(pControl->PointType == ePointType_Ao) + { + ret = do_writeAOValue(ChanNo, Timeout, SetValue, RespValue); + } + else + { + ret = do_writeMOValue(ChanNo, Timeout, SetValue, RespValue); + } + + // 拼接返回参数 + ControlResult.PointType = pControl->PointType; + ControlResult.PointId = pControl->PointId; + ControlResult.CtrlActType = pControl->CtrlActType; + + //不为0,可能没有下发成功,返回返回的ret值 + if(ret != 0) + { + USER_LOG_ERR2("PointID[%d] AO control failed. ret = '%d'",pControl->PointId, ret); + ControlResult.retStatus = ret; //返回状态 成功,则返回sucess(0),否则返回错误码(errcode) + } + else //为0,可能服务返回错误码 + { + if(!RespValue.empty()) + ControlResult.retStatus = RespValue.front().retStatus;; //返回状态 成功,则返回sucess(0),否则返回错误码(errcode) + } + + return ret; +} + int FES_Control(const int ChanNo, const int Timeout,const std::vector& CtrlReq,std::vector& CtrlResp) { int iotSuccess = EN_ARE_SUCCESS; @@ -926,30 +1065,14 @@ int FES_Control(const int ChanNo, const int Timeout,const std::vectorPointId; - AOValue.ValueType = pControl->ValueType; - memcpy(&(AOValue.Value) ,&(pControl->Value),sizeof(pControl->Value)); - SetValue.push_back(AOValue); - ret = do_writeAOValue(ChanNo, Timeout, SetValue, RespValue); - - // 拼接返回参数 - ControlResult.PointType = pControl->PointType; - ControlResult.PointId = pControl->PointId; - ControlResult.CtrlActType = pControl->CtrlActType; - - //不为0,可能没有下发成功,返回返回的ret值 - if(ret != 0) + case ePointType_Mo: + if(pControl->CtrlObjType == eCtrlObjType_SingleValue) { - USER_LOG_ERR2("PointID[%d] AO control failed. ret = '%d'",pControl->PointId, ret); - ControlResult.retStatus = ret; //返回状态 成功,则返回sucess(0),否则返回错误码(errcode) + ret = _ytWithSingleValue(ChanNo,Timeout,SetValue,RespValue,pControl,ControlResult); } - else //为0,可能服务返回错误码 + else { - if(!RespValue.empty()) - ControlResult.retStatus = RespValue.front().retStatus;; //返回状态 成功,则返回sucess(0),否则返回错误码(errcode) + ret = _ytWithOperObject(ChanNo,Timeout,conn,pControl,ControlResult); } break; } @@ -1456,7 +1579,7 @@ int _ConvertFESSGValuetoVecTagVariable(int ChanNo, const std::vector& SetValue, std::vector& RespValue, eSignalInstType eSignalType) +int _writeSEValue(const int ChanNo, const int Timeout,const int GroupNo, const std::vector& SetValue, std::vector& RespValue, eSignalInstType eSignalType, bool &bCtrlSelect) { int nRet = EN_RET_SUCCESS; mvl_net_info *conn = GetMvlNetInfobyChanNo(ChanNo); // 获取通道号的连接参数 @@ -1514,6 +1637,7 @@ int _writeSEValue(const int ChanNo, const int Timeout,const int GroupNo, const USER_LOG_ERR3("strSGCBReference=%s,nEditGroup=%d 激活定值区失败,错误码%d",strSGCBReference.toLocal8Bit().data(),nEditGroup,nRet); break; } + bCtrlSelect = true; //写定值 nRet = SetSGValues(conn, vecSettings, Timeout); @@ -1550,7 +1674,6 @@ int _writeSEValue(const int ChanNo, const int Timeout,const int GroupNo, const return nRet; } - /** * @brief FES_WriteSEValue FES接口 * @param ChanNo 输入参数,通道号 @@ -1561,7 +1684,8 @@ int _writeSEValue(const int ChanNo, const int Timeout,const int GroupNo, const */ int FES_WriteSEValue(const int ChanNo, const int Timeout,const int GroupNo, const std::vector& SetValue, std::vector& RespValue) { - return _writeSEValue(ChanNo, Timeout, GroupNo, SetValue, RespValue, eSignalInstType_Const); + bool bCtrlSelect = false; + return _writeSEValue(ChanNo, Timeout, GroupNo, SetValue, RespValue, eSignalInstType_Const, bCtrlSelect); } /** @@ -2167,4 +2291,9 @@ int save_osi_cfg(int maxCalling, const QString &CfgPath) } +void setPingMng(const iot_fes::CPingInterfacePtr ptrPingMng) +{ + g_MonitorThread.setPingMngPtr(ptrPingMng); +} + } diff --git a/product/src/fes/iec61850/communication/IEC850C_FESClient.h b/product/src/fes/iec61850/communication/IEC850C_FESClient.h index 4230d296..06cedc8d 100644 --- a/product/src/fes/iec61850/communication/IEC850C_FESClient.h +++ b/product/src/fes/iec61850/communication/IEC850C_FESClient.h @@ -51,7 +51,8 @@ bool releaseData(); int FES_Connect(const int ChanNo, const int NetAB, const int EnableReport); // 连接结果 void FES_ConnentStatus_Callback(const int ChanNo, const int retStatus); - +// 设置Ping功能对象,用于提高MonitorThread线程中重连速度 +void setPingMng(const iot_fes::CPingInterfacePtr ptrPingMng); // 报告使能结果 void FES_RptStatus_Callback(const int ChanNo, const char *RptName, const int retStatus); diff --git a/product/src/fes/iec61850/communication/IEC850C_MonitorThread.cpp b/product/src/fes/iec61850/communication/IEC850C_MonitorThread.cpp index 6d49da52..e0c62eea 100644 --- a/product/src/fes/iec61850/communication/IEC850C_MonitorThread.cpp +++ b/product/src/fes/iec61850/communication/IEC850C_MonitorThread.cpp @@ -209,7 +209,7 @@ void Monitor_ReEnable_and_CycleGI(int nChanno, CIedInfo &ied) } // 通道重连 -void Monitor_Channel_Reconnect(int nChanno, CIedInfo &ied) +void Monitor_Channel_Reconnect(int nChanno, bool bPingOk, CIedInfo &ied) { if(ied.m_nConnStauts != eConnStatus_OPEN_FAILURE) { @@ -249,7 +249,7 @@ void Monitor_Channel_Reconnect(int nChanno, CIedInfo &ied) S_W_LOCK_RESOURCE(ied, 3); // 发起通道连接 - if(0 == Connect(m_strDeviceTagName, nChanno, ied.m_nEnableReport, mmsconfig_ini.m_nChannelReConnectTimeout)) + if(bPingOk && 0 == Connect(m_strDeviceTagName, nChanno, ied.m_nEnableReport, mmsconfig_ini.m_nChannelReConnectTimeout)) { ied.m_nReConnectCount = 0; // 重连失败计数器清零 } @@ -354,11 +354,13 @@ void MonitorThread::onRun() // 通道状态监视 Monitor_ChannelConnStatus(nChanno, ied); - // 通道重连 - Monitor_Channel_Reconnect(nChanno, ied); + // 通道重连,先看一下是否ping通,ping的通才会调用connect函数 + bool bPingOk = pingOk(nChanno); + Monitor_Channel_Reconnect(nChanno,bPingOk,ied); // 按通道sleep, 释放资源, 避免过度占用 std::chrono::milliseconds t1(100); + //std::chrono::milliseconds t1(10); std::this_thread::sleep_for(t1); } @@ -371,6 +373,23 @@ void MonitorThread::onRun() return; } +//设置ping管理类指针 +void MonitorThread::setPingMngPtr(const iot_fes::CPingInterfacePtr ptrPingMng) +{ + m_ptrPingMng = ptrPingMng; +} + +//查询指定通道是否能够ping通 +bool MonitorThread::pingOk(const int nChannelNo) +{ + if(m_ptrPingMng != NULL) + { + return m_ptrPingMng->getPingResult(nChannelNo); + } + + return true; +} + MonitorThread::~MonitorThread() { USER_LOG_ERR0("MonitorThread.~MonitorThread 1\n"); @@ -378,6 +397,7 @@ MonitorThread::~MonitorThread() if (pthread) delete pthread; pthread = nullptr; + m_ptrPingMng.reset(); USER_LOG_ERR0("MonitorThread.~MonitorThread 2\n"); } @@ -418,6 +438,8 @@ void MonitorThread::close() } // _sem.wait(); } + + m_ptrPingMng.reset(); USER_LOG_ERR0("MonitorThread.close 2\n"); } diff --git a/product/src/fes/iec61850/communication/IEC850C_MonitorThread.h b/product/src/fes/iec61850/communication/IEC850C_MonitorThread.h index 5a255bb6..848a11d1 100644 --- a/product/src/fes/iec61850/communication/IEC850C_MonitorThread.h +++ b/product/src/fes/iec61850/communication/IEC850C_MonitorThread.h @@ -6,6 +6,7 @@ #include #include "CELLTimeStamp.hpp" #include "CELLSemaphore.hpp" +#include "fes_ping_api/PingInterface.h" namespace IEC850C { @@ -27,6 +28,8 @@ private: std::atomic_bool _bIsExit; CELLSemaphore _sem; CELLTimestamp m_Timer; + iot_fes::CPingInterfacePtr m_ptrPingMng; //Ping管理类指针 + public: std::thread *pthread = nullptr; public: @@ -37,7 +40,12 @@ public: void start(); void close(); void onRun(); + //设置ping管理指针,用于优化MonitorThread线程中重连效率,未ping通不执行connect + void setPingMngPtr(const iot_fes::CPingInterfacePtr ptrPingMng); +private: + //查询指定通道号是否能够ping通 + bool pingOk(const int nChannelNo); }; } diff --git a/product/src/fes/iec61850/communication/IEC850C_OperTracking.cpp b/product/src/fes/iec61850/communication/IEC850C_OperTracking.cpp index 7fd36525..ced9be9a 100644 --- a/product/src/fes/iec61850/communication/IEC850C_OperTracking.cpp +++ b/product/src/fes/iec61850/communication/IEC850C_OperTracking.cpp @@ -742,6 +742,14 @@ void OperTacking_Control(QString strCmdText, const int ChanNo, const int Timeout strPointRef = pSigalInst->m_strSignalRef; } } + else if(ePointType_Mo == req.PointType) + { + CCfgSignalInst *pSigalInst = GetSettingSignalbyDZNo(ChanNo,req.PointId,eSignalInstType_MixCtrl); + if(pSigalInst != NULL) + { + strPointRef = pSigalInst->m_strSignalRef; + } + } OperTacking.m_strDetail << "遥控通道: " << i << std::endl; OperTacking.m_strDetail << strSpace(4) << "61850路径: " << strPointRef.toStdString() << std::endl; @@ -805,6 +813,14 @@ void OperTacking_Control(QString strCmdText, const int ChanNo, const int Timeout strPointRef = pSigalInst->m_strSignalRef; } } + else if(ePointType_Mo == req.PointType) + { + CCfgSignalInst *pSigalInst = GetSettingSignalbyDZNo(ChanNo,req.PointId,eSignalInstType_MixCtrl); + if(pSigalInst != NULL) + { + strPointRef = pSigalInst->m_strSignalRef; + } + } OperTacking.m_strDetail << "遥控通道: " << i << std::endl; OperTacking.m_strDetail << strSpace(4) << "61850路径: " << strPointRef.toStdString() << std::endl; diff --git a/product/src/fes/iec61850/communication/IEC850C_Report.cpp b/product/src/fes/iec61850/communication/IEC850C_Report.cpp index fb20d370..66b556fa 100644 --- a/product/src/fes/iec61850/communication/IEC850C_Report.cpp +++ b/product/src/fes/iec61850/communication/IEC850C_Report.cpp @@ -799,7 +799,7 @@ void writeIndData(const tagRptParse *ptRptParse) // 将数据值保存起来 //2022-11-23 thxiao 7SJ686 故障启动信号包含general\dirGeneral\q\t, mapReferenceToDataValue数组大小最后会变为2, //在后面取值时,取了dirGeneral的值作为点值,导致DI点值常为0。此处增加判断,dirGeneral的值不保存在mapReferenceToDataValue - if (!strReference.contains("$dirGeneral")) + if (!strReference.contains("$dirGeneral") && !strReference.contains("$stSeld")) mapReferenceToDataValue[strReference] = sIECValue; } @@ -901,7 +901,12 @@ void writeIndData(const tagRptParse *ptRptParse) SIEC61850DataInfo dataInfo; strIEDName = cfgSignalInst->m_strIedName; // 获取对象的IEDName, 后续会使用 int FesType = getFesDataType(cfgSignalInst->m_nPointType); - if(FesType == -1) USER_LOG_ERR1("cfgSignalInst::strSignalRef= '%s' signalType is Wrong!", cfgSignalInst->m_strSignalRef.toLocal8Bit().data()); + if(FesType == -1) + { + USER_LOG_ERR1("cfgSignalInst::strSignalRef= '%s' signalType is Wrong!", cfgSignalInst->m_strSignalRef.toLocal8Bit().data()); + continue; + } + dataInfo.strPntRef = cfgSignalInst->m_strPointRef.toStdString(); dataInfo.PointType = FesType; dataInfo.PointId = cfgSignalInst->m_nPointid; dataInfo.ValueType = iter.value().ValueType; @@ -930,7 +935,7 @@ void writeIndData(const tagRptParse *ptRptParse) getChanNoByIedName(strIEDName, cChanNo); if(cChanNo == -1) { - //USER_LOG_ERR1("getChanNoByIedName '%s' return failed!", strIEDName.toLocal8Bit().data()); + USER_LOG_ERR1("getChanNoByIedName '%s' return failed!", strIEDName.toLocal8Bit().data()); return; } @@ -1221,11 +1226,11 @@ int rptIndHandleEx (MVL_COMM_EVENT *event) /* If not found, rcb_info will be == NULL. */ rcb_info = RCB_Info_Find((mvl_net_info *)event->net_info, saveRptID); if (!rcb_info) - { - USER_LOG_ERR1 ("RptID '%s' not recognized on this connection. Received report ignored.", saveRptID); - retcode = SD_FAILURE; - goto CLEANUP; - } + { + USER_LOG_ERR1 ("RptID '%s' not recognized on this connection. Received report ignored.", saveRptID); + retcode = SD_FAILURE; + goto CLEANUP; + } // SLOGALWAYS2("'%p' RptID %s recognized on this connection.\n", rcb_info, rcb_info->RptID); @@ -1281,7 +1286,13 @@ int rptIndHandleEx (MVL_COMM_EVENT *event) info_va[va_num++] = rcb_info->rcb_var.Inclusion; - assert (va_num < va_total); + assert (va_num <= va_total); + if (va_num > va_total) + { + USER_LOG_ERR3 ("RptID '%s' result size error.va_total:%d va_num:%d", saveRptID,va_total,va_num); + retcode = SD_FAILURE; + goto CLEANUP; + } /* Perform 2nd decode (up through "Inclusion"). */ mvl_info_data_to_local (event, va_num, info_va); diff --git a/product/src/fes/iec61850/communication/iec61850dll.cpp b/product/src/fes/iec61850/communication/iec61850dll.cpp index f57bc583..5bbdaeb9 100644 --- a/product/src/fes/iec61850/communication/iec61850dll.cpp +++ b/product/src/fes/iec61850/communication/iec61850dll.cpp @@ -71,6 +71,11 @@ int CIEC61850dll::FES_Connect(const int ChanNo, const int NetAB, const int Enabl return IEC850C::FES_Connect(ChanNo, NetAB, EnableReport); } +void CIEC61850dll::setPingMngPtr(const iot_fes::CPingInterfacePtr ptrPingMng) +{ + IEC850C::setPingMng(ptrPingMng); +} + int CIEC61850dll::FES_Disconnect(const int ChanNo) { IEC850C::OperTacking_ChanStatus("发起通道释放", ChanNo); diff --git a/product/src/fes/iec61850/include/IEC61850c_pub.h b/product/src/fes/iec61850/include/IEC61850c_pub.h index 1f408c9d..1edc1034 100644 --- a/product/src/fes/iec61850/include/IEC61850c_pub.h +++ b/product/src/fes/iec61850/include/IEC61850c_pub.h @@ -1,5 +1,6 @@ #pragma once #include +#include #include "DataType.h" const int CN_IEC61850cMaxRefSize = 128; @@ -7,11 +8,15 @@ const int CN_IEC61850cMaxRefSize = 128; //typedef unsigned __int64 uint64; typedef void *HANDLE; #define IEC61850_K_MAX_REFERENCE_SIZE 128 +//todo:此文件为什么要拷贝一份放在这?iec61850client2和fes/ice61850/include下各有一份了,暂时没敢合并,所以修改时记得2个文件同步 typedef struct { int PointType;//5:DO 6:AO int PointId; //Param1 - int ValueType;//1:int 2:float 3:int64 + int ValueType;//1:int 2:float 3:int64 对应Param2 + int CtrlObjType; //对应IEC850C_Resource.h中的eCtrlObjType,控制对象类型,用来标识当前控制点是值控制还是类似遥控的对象控制方式, + //有的设备将模拟量控制设置为一个与遥控一样的对象,$Oper$setMag$f + //用Param3标识,暂时只对混合量和模拟量生效,0代表值控制,1代表对象控制 int orCat; //for DO union { @@ -48,6 +53,7 @@ typedef struct { }SIEC61850SGResp; typedef struct { + std::string strPntRef; //不带IEDName int PointType;//1:AI 2:DI 3:ACC 4:DZ int PointId; //Param1 int ValueType;//1:int 2:float 3:int64 diff --git a/product/src/fes/iec61850/include/IEC61850dll.h b/product/src/fes/iec61850/include/IEC61850dll.h index f2eaa706..d9c51f83 100644 --- a/product/src/fes/iec61850/include/IEC61850dll.h +++ b/product/src/fes/iec61850/include/IEC61850dll.h @@ -1,8 +1,9 @@ #pragma once #include #include "IEC61850c_pub.h" - #include "common/Export.h" +#include "fes_ping_api/PingInterface.h" + #ifdef PROTOCOLBASE_API_EXPORT #define PROTOCOLBASE_API G_DECL_EXPORT #else @@ -75,6 +76,7 @@ public: static bool release(); void OperTacking(); int FES_Connect(const int ChanNo, const int NetAB, const int EnableReport); + void setPingMngPtr(const iot_fes::CPingInterfacePtr ptrPingMng); // 关闭指定通道 int FES_Disconnect(const int ChanNo); diff --git a/product/src/fes/iec61850/include/IEC850C_Resource.h b/product/src/fes/iec61850/include/IEC850C_Resource.h index 49eb5fa1..8fbf9a33 100644 --- a/product/src/fes/iec61850/include/IEC850C_Resource.h +++ b/product/src/fes/iec61850/include/IEC850C_Resource.h @@ -487,8 +487,25 @@ enum eCtrlType //DO/DA enum ePointType { + //todo:此定义不能修改,原来只定义了DO和AO,其余没有定义,不确定哪些地方使用了魔数,所以不要动原定义,只能增加 + ePointType_AI=1, + ePointType_DI=2, + ePointType_ACC=3, + ePointType_DZ=4, ePointType_Do=5, //Do,DataObject ePointType_Ao=6, //DA,DataAttribute + ePointType_Mo=7, + ePointType_Mi=8 +}; + +//控制对象类型,用来标识当前控制点是值控制还是类似遥控的对象控制方式, +//有的设备将模拟量控制设置为一个与遥控一样的对象,$Oper$setMag$f +//用Param3标识,暂时只对混合量和模拟量生效,0代表值控制,1代表对象控制 +//关联SIEC61850CtrlReq +enum eCtrlObjType +{ + eCtrlObjType_SingleValue = 0, + eCtrlObjType_OperObj }; enum eSignalModelType @@ -795,6 +812,10 @@ void *getGlobalRptTypeids(); int GetYkDoInfobyChanNoAndPointID(int nChanNo, int pointid, tagYkTypeInfo &result); //通过测点ID和通道号,获取模拟量的其他信息 int GetYkDaInfobyChanNoAndPointID(int nChanNo,int pointid, tagYkTypeInfo &result); + +//通过测点ID和通道号,获取模拟量控制/混合量控制的其他信息 +int GetCtrlInfobyChanNoAndPointID(int nChanNo,eSignalInstType pntType,int pointid, tagYkTypeInfo &result); + //通过GroupNo获取SGCB的Ref void GetSGCBRefbyGroupNo(int nChanNo, int GroupNo, QString &strSGCBReference); //通过DZCode获取定值Ref diff --git a/product/src/fes/iec61850/libs/libasn1.pro b/product/src/fes/iec61850/libs/libasn1.pro index 91818761..b6fb5683 100644 --- a/product/src/fes/iec61850/libs/libasn1.pro +++ b/product/src/fes/iec61850/libs/libasn1.pro @@ -5,22 +5,6 @@ CONFIG -= qt TARGET = libasn1 -QMAKE_CFLAGS_DEBUG += \ - -DMMS_LITE \ - -DDEBUG_SISCO - -QMAKE_CFLAGS_RELEASE += \ - -DMMS_LITE \ - -DDEBUG_SISCO - -QMAKE_CXXFLAGS_DEBUG += \ - -DMMS_LITE \ - -DDEBUG_SISCO - -QMAKE_CXXFLAGS_RELEASE += \ - -DMMS_LITE \ - -DDEBUG_SISCO - include(../iec860.pri) @@ -53,5 +37,20 @@ SOURCES += \ $$IEC61850PATH/sisco/src/asn1r_maxlen.c +QMAKE_CFLAGS_DEBUG += \ + -DMMS_LITE \ + -DDEBUG_SISCO + +QMAKE_CFLAGS_RELEASE += \ + -DMMS_LITE \ + -DDEBUG_SISCO + +QMAKE_CXXFLAGS_DEBUG += \ + -DMMS_LITE \ + -DDEBUG_SISCO + +QMAKE_CXXFLAGS_RELEASE += \ + -DMMS_LITE \ + -DDEBUG_SISCO diff --git a/product/src/fes/iec61850/libs/libcfgtool.pro b/product/src/fes/iec61850/libs/libcfgtool.pro index 9c829645..617f240f 100644 --- a/product/src/fes/iec61850/libs/libcfgtool.pro +++ b/product/src/fes/iec61850/libs/libcfgtool.pro @@ -28,6 +28,8 @@ LIBS += \ -llibslog \ -lkbd61850dbinterface +LIBS += -lboost_system + win32{ LIBS += -lws2_32 \ # -lxerces-c_3 @@ -38,6 +40,7 @@ linux-g++*{ } include(../iec860.pri) +INCLUDEPATH += ../../include/ SOURCES += \ diff --git a/product/src/fes/iec61850/libs/libiec61850c.pro b/product/src/fes/iec61850/libs/libiec61850c.pro index 7d5a961e..965cc04c 100644 --- a/product/src/fes/iec61850/libs/libiec61850c.pro +++ b/product/src/fes/iec61850/libs/libiec61850c.pro @@ -30,6 +30,8 @@ LIBS += -llibcositcps \ -llibslog \ -lkbd61850dbinterface +LIBS += -lboost_system -lfes_ping_api + win32{ LIBS += -lws2_32 } @@ -77,6 +79,7 @@ HEADERS += \ $$IEC61850_INCLUDE/IEC850C_DataInit.h\ $$IEC61850_INCLUDE/IEC850C_Osicfg.h +INCLUDEPATH += ../../include/ include($$IEC61850_EASY_TCP_CLIENT/EasyTcpClient.pri) win32{ diff --git a/product/src/fes/iec61850/libs/libutil.pro b/product/src/fes/iec61850/libs/libutil.pro index 58470a65..166ca622 100644 --- a/product/src/fes/iec61850/libs/libutil.pro +++ b/product/src/fes/iec61850/libs/libutil.pro @@ -44,7 +44,7 @@ win32{ SOURCES += \ $$IEC61850PATH/sisco/src/glbsem_w32.c } -linux-g++* { +linux-*g++* { SOURCES += \ $$IEC61850PATH/sisco/src/glbsem_unix.c } diff --git a/product/src/fes/iec61850/sisco/mvl/usr/client/cli_rpt.c b/product/src/fes/iec61850/sisco/mvl/usr/client/cli_rpt.c index 91e72329..d97e1389 100644 --- a/product/src/fes/iec61850/sisco/mvl/usr/client/cli_rpt.c +++ b/product/src/fes/iec61850/sisco/mvl/usr/client/cli_rpt.c @@ -708,9 +708,9 @@ ST_RET rcb_enable (MVL_NET_INFO *netInfo, ST_CHAR *domName, sprintf(varName, "%s$EntryID", rcbName); ret = named_var_read(netInfo, varName, DOM_SPEC, domName, rpt_typeids->ostring8, EntryIDStr, timeOut); if (ret == SD_SUCCESS) - USER_LOG_ERR2("%s %s named_var_read() OK", varName, domName); + USER_LOG_ERR2("%s %s named_var_write() OK", varName, domName); else - USER_LOG_ERR2("%s %s named_var_read() ERROR", varName, domName); + USER_LOG_ERR2("%s %s named_var_write() ERROR", varName, domName); } if (ret == SD_SUCCESS) @@ -722,49 +722,7 @@ ST_RET rcb_enable (MVL_NET_INFO *netInfo, ST_CHAR *domName, else USER_LOG_ERR2("%s %s named_var_write() ERROR", varName, domName); } - - //2023-05-08 thxiao 重复三次,保证写成功 - //if (ret == SD_SUCCESS) - { - sprintf(varName, "%s$EntryID", rcbName); - ret = named_var_read(netInfo, varName, DOM_SPEC, domName, rpt_typeids->ostring8, EntryIDStr, timeOut); - if (ret == SD_SUCCESS) - USER_LOG_ERR2("%s %s named_var_read() OK", varName, domName); - else - USER_LOG_ERR2("%s %s named_var_read() ERROR", varName, domName); - - } - if (ret == SD_SUCCESS) - { - sprintf(varName, "%s$EntryID", rcbName); - ret = named_var_write(netInfo, varName, DOM_SPEC, domName, rpt_typeids->ostring8, EntryIDStr, timeOut); - if (ret == SD_SUCCESS) - USER_LOG_ERR2("%s %s named_var_write() OK", varName, domName); - else - USER_LOG_ERR2("%s %s named_var_write() ERROR", varName, domName); - } - - //if (ret == SD_SUCCESS) - { - sprintf(varName, "%s$EntryID", rcbName); - ret = named_var_read(netInfo, varName, DOM_SPEC, domName, rpt_typeids->ostring8, EntryIDStr, timeOut); - if (ret == SD_SUCCESS) - USER_LOG_ERR2("%s %s named_var_read() OK", varName, domName); - else - USER_LOG_ERR2("%s %s named_var_read() ERROR", varName, domName); - - } - if (ret == SD_SUCCESS) - { - sprintf(varName, "%s$EntryID", rcbName); - ret = named_var_write(netInfo, varName, DOM_SPEC, domName, rpt_typeids->ostring8, EntryIDStr, timeOut); - if (ret == SD_SUCCESS) - USER_LOG_ERR2("%s %s named_var_write() OK", varName, domName); - else - USER_LOG_ERR2("%s %s named_var_write() ERROR", varName, domName); - } - - //2022-04-06 写EntryID的代码段,"ret == SD_SUCCESS"改为“ret = SD_SUCCESS”,否则会导致不支持EntryID读写的保护初始化不成功。 + //2022-04-06 写EntryID的代码段,"ret == SD_SUCCESS"改为“ret = SD_SUCCESS”,否则会导致不支持EntryID读写的保护初始化不成功。 ret = SD_SUCCESS;//如果设备相应失败,说明可能是不支持该方式,所以可以忽略。 }