From cd61e8e27bd5be8c41b31e2a4c4ded4947b78d95 Mon Sep 17 00:00:00 2001 From: ypc <15051963820@163.com> Date: Mon, 8 Jun 2026 14:02:03 +0800 Subject: [PATCH] =?UTF-8?q?<=E4=BF=AE=E6=94=B9>=201=E3=80=81=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E5=8F=98=E5=8C=96=E4=B8=8A=E9=80=81=E6=97=B6=E7=9A=84?= =?UTF-8?q?=E5=93=81=E8=B4=A8=E4=B8=8E=E6=97=B6=E9=97=B4=EF=BC=9B2?= =?UTF-8?q?=E3=80=81=E8=B0=83=E6=95=B4=E8=A7=A3=E6=9E=90icd=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E8=82=BF=E8=8E=B7=E5=8F=96=E6=8A=A5=E5=91=8A=E4=B8=8B?= =?UTF-8?q?=E7=9A=84=E9=85=8D=E7=BD=AE=E6=95=B0=E6=8D=AE=EF=BC=9B3?= =?UTF-8?q?=E3=80=81=E8=B0=83=E8=AF=95=E4=BA=8B=E4=BB=B6=E6=8A=A5=E5=91=8A?= =?UTF-8?q?=E6=8E=A7=E5=88=B6=E5=9D=97=EF=BC=8C=E5=AE=9E=E7=8E=B0=E5=AE=A2?= =?UTF-8?q?=E6=88=B7=E7=AB=AF=E4=BD=BF=E8=83=BD=E5=90=8E=E4=B8=8A=E9=80=81?= =?UTF-8?q?=E5=8F=98=E5=8C=96=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/protocol/libmms_s/src/mms_s.cpp | 28 +++++++++++++++++++++ src/protocol/libmms_s/src/mms_s_icd.cpp | 20 +++++++-------- src/protocol/libmms_s/src/mms_s_model.cpp | 6 +++-- src/protocol/libmms_s/src/mms_s_value.cpp | 30 +++++++++++++++++++++++ src/system/libiec61850s/src/iec61850s.cpp | 2 +- 5 files changed, 73 insertions(+), 13 deletions(-) diff --git a/src/protocol/libmms_s/src/mms_s.cpp b/src/protocol/libmms_s/src/mms_s.cpp index 2ac6af0..804583a 100644 --- a/src/protocol/libmms_s/src/mms_s.cpp +++ b/src/protocol/libmms_s/src/mms_s.cpp @@ -44,6 +44,33 @@ LOCAL IedServer gp_iedServer = NULL; LOCAL bool g_dbg_switch = false; /** SIGINT 信号处理:通知后台线程退出 */ +/* RCB event handler for monitoring client report subscriptions */ +LOCAL void rcbEventHandler(void* parameter, ReportControlBlock* rcb, ClientConnection connection, IedServer_RCBEventType event, const char* parameterName, MmsDataAccessError serviceError) +{ + const char* name = ReportControlBlock_getName(rcb); + char* rptId = ReportControlBlock_getRptID(rcb); + char* dataSet = ReportControlBlock_getDataSet(rcb); + switch (event) + { + case RCB_EVENT_ENABLE: LOG_I("RCB %s ENABLED rptId=%s datSet=%s trigOps=0x%x", name, rptId, dataSet, rcb->trgOps); break; + case RCB_EVENT_DISABLE: LOG_I("RCB %s DISABLED", name); break; + case RCB_EVENT_RESERVED: LOG_I("RCB %s RESERVED", name); break; + case RCB_EVENT_UNRESERVED:LOG_I("RCB %s UNRESERVED", name); break; + case RCB_EVENT_GI: LOG_I("RCB %s GI triggered", name); break; + case RCB_EVENT_SET_PARAMETER: + LOG_I("RCB %s SET param=%s err=%d trigOps=0x%x (before)", name, parameterName ? parameterName : "?", serviceError, rcb->trgOps); + if (parameterName && strcmp(parameterName, "TrgOps") == 0) { + rcb->trgOps |= 0x01; + LOG_I("RCB %s forced dchg, trigOps=0x%x", name, rcb->trgOps); + } + break; + case RCB_EVENT_GET_PARAMETER: LOG_I("RCB %s GET param=%s err=%d", name, parameterName ? parameterName : "?", serviceError); break; + default: LOG_I("RCB %s event=%d", name, event); break; + } + free(rptId); + free(dataSet); +} + LOCAL void sigint_handler(int signum) { g_running = 0; @@ -268,6 +295,7 @@ int mms_s_init(const char *icd_path, int port) param_init(gp_iedServer, *gp_icd); file_init(gp_iedServer); + IedServer_setRCBEventHandler(gp_iedServer, rcbEventHandler, NULL); IedServer_start(gp_iedServer, port); diff --git a/src/protocol/libmms_s/src/mms_s_icd.cpp b/src/protocol/libmms_s/src/mms_s_icd.cpp index ac0b453..d7830bf 100644 --- a/src/protocol/libmms_s/src/mms_s_icd.cpp +++ b/src/protocol/libmms_s/src/mms_s_icd.cpp @@ -182,10 +182,10 @@ LOCAL int parse_ReportControl_TrgOps(XMLElement *ele_reportControl, stru_TrgOps return -1; } - trgOps.dchg = (ele_trgOps->Attribute(key_dchg) == nullptr ? "false" : ele_trgOps->Attribute(key_dchg)) == "true"; - trgOps.qchg = (ele_trgOps->Attribute(key_qchg) == nullptr ? "false" : ele_trgOps->Attribute(key_qchg)) == "true"; - trgOps.dupd = (ele_trgOps->Attribute(key_dupd) == nullptr ? "false" : ele_trgOps->Attribute(key_dupd)) == "true"; - trgOps.period = (ele_trgOps->Attribute(key_period) == nullptr ? "false" : ele_trgOps->Attribute(key_period)) == "true"; + trgOps.dchg = (ele_trgOps->Attribute(key_dchg) == nullptr ? false : strcmp(ele_trgOps->Attribute(key_dchg), "true") == 0); + trgOps.qchg = (ele_trgOps->Attribute(key_qchg) == nullptr ? false : strcmp(ele_trgOps->Attribute(key_qchg), "true") == 0); + trgOps.dupd = (ele_trgOps->Attribute(key_dupd) == nullptr ? false : strcmp(ele_trgOps->Attribute(key_dupd), "true") == 0); + trgOps.period = (ele_trgOps->Attribute(key_period) == nullptr ? false : strcmp(ele_trgOps->Attribute(key_period), "true") == 0); return 0; } @@ -211,12 +211,12 @@ LOCAL int parse_ReportControl_OptFields(XMLElement *ele_reportControl, stru_OptF return -1; } - optFields.seqNum = (ele_optFields->Attribute(key_seqNum) == nullptr ? "false" : ele_optFields->Attribute(key_seqNum)) == "true"; - optFields.timeStamp = (ele_optFields->Attribute(key_timeStamp) == nullptr ? "false" : ele_optFields->Attribute(key_timeStamp)) == "true"; - optFields.reasonCode = (ele_optFields->Attribute(key_reasonCode) == nullptr ? "false" : ele_optFields->Attribute(key_reasonCode)) == "true"; - optFields.dataRef = (ele_optFields->Attribute(key_dataRef) == nullptr ? "false" : ele_optFields->Attribute(key_dataRef)) == "true"; - optFields.entryID = (ele_optFields->Attribute(key_entryID) == nullptr ? "false" : ele_optFields->Attribute(key_entryID)) == "true"; - optFields.configRef = (ele_optFields->Attribute(key_configRef) == nullptr ? "false" : ele_optFields->Attribute(key_configRef)) == "true"; + optFields.seqNum = (ele_optFields->Attribute(key_seqNum) == nullptr ? false : strcmp(ele_optFields->Attribute(key_seqNum), "true") == 0); + optFields.timeStamp = (ele_optFields->Attribute(key_timeStamp) == nullptr ? false : strcmp(ele_optFields->Attribute(key_timeStamp), "true") == 0); + optFields.reasonCode = (ele_optFields->Attribute(key_reasonCode) == nullptr ? false : strcmp(ele_optFields->Attribute(key_reasonCode), "true") == 0); + optFields.dataRef = (ele_optFields->Attribute(key_dataRef) == nullptr ? false : strcmp(ele_optFields->Attribute(key_dataRef), "true") == 0); + optFields.entryID = (ele_optFields->Attribute(key_entryID) == nullptr ? false : strcmp(ele_optFields->Attribute(key_entryID), "true") == 0); + optFields.configRef = (ele_optFields->Attribute(key_configRef) == nullptr ? false : strcmp(ele_optFields->Attribute(key_configRef), "true") == 0); return 0; } diff --git a/src/protocol/libmms_s/src/mms_s_model.cpp b/src/protocol/libmms_s/src/mms_s_model.cpp index 202ba36..9ff413c 100644 --- a/src/protocol/libmms_s/src/mms_s_model.cpp +++ b/src/protocol/libmms_s/src/mms_s_model.cpp @@ -611,7 +611,7 @@ LOCAL int model_DataSet_init(LogicalNode *LLN0, stru_icd &icd, std::mapvec_fcda.begin(); it_fcda != p_dataset->vec_fcda.end(); it_fcda++) // 遍历数据集成员 { std::string variable = it_fcda->prefix + it_fcda->lnClass + it_fcda->lnInst + "$" + \ - it_fcda->fc + "$" + it_fcda->doName + "$" + it_fcda->daName; // 拼装 FCDA 变量名 + it_fcda->fc + "$" + it_fcda->doName + (it_fcda->daName.empty() ? "" : "$" + it_fcda->daName); // 拼装 FCDA 变量名 for(int i = 0; i < variable.size(); i++) // 将变量名中所有 '.' 替换为 '$' { @@ -663,7 +663,9 @@ LOCAL int model_ReportControlBlock_init(LogicalNode *LLN0, stru_icd &icd, std::m int Options = model_get_rpt_options(rcb.optFields); // 将 OptFields 结构转为位掩码 int trgOps = model_get_rpt_trgOps(rcb.trgOps); // 将 TrgOps 结构转为位掩码 - ReportControlBlock_create(it_rcb->c_str(), LLN0, rcb.rptID.c_str(), rcb.buffered, rcb.datSet.c_str(), rcb.confRev, trgOps, Options, rcb.bufTime, rcb.intgPd); // 创建 RCB + LOG_I("RCB CREATE: name=%s dchg=%d qchg=%d dupd=%d period=%d -> trgOps=0x%x", + it_rcb->c_str(), rcb.trgOps.dchg, rcb.trgOps.qchg, rcb.trgOps.dupd, rcb.trgOps.period, trgOps); + rcb.Rcb = ReportControlBlock_create(it_rcb->c_str(), LLN0, rcb.rptID.c_str(), rcb.buffered, rcb.datSet.c_str(), rcb.confRev, trgOps, Options, rcb.bufTime, rcb.intgPd); } return 0; diff --git a/src/protocol/libmms_s/src/mms_s_value.cpp b/src/protocol/libmms_s/src/mms_s_value.cpp index bfaa2dd..2ee1453 100644 --- a/src/protocol/libmms_s/src/mms_s_value.cpp +++ b/src/protocol/libmms_s/src/mms_s_value.cpp @@ -580,6 +580,9 @@ LOCAL void mms_s_value_update(const char *str_saddr, const char *str_val) DataAttribute *p_da = (DataAttribute *)p_node; + LOG_I("mms_s_value_update: saddr=%s val=%s da_type=%d da_name=%s", + saddr.c_str(), val.c_str(), p_da->type, p_da->name); + if(g_iec61850s_value_update_cb_map.find(p_da->type) == g_iec61850s_value_update_cb_map.end()) { LOG_E("EnumType %d not found in ICD", p_da->type); @@ -590,7 +593,34 @@ LOCAL void mms_s_value_update(const char *str_saddr, const char *str_val) if(NULL != g_iec61850s_value_update_cb_map[p_da->type] && p_ied_server != NULL) { + IedServer_lockDataModel(p_ied_server); + g_iec61850s_value_update_cb_map[p_da->type](p_ied_server, p_da, val); + + /* 向上查找父级 DO,同步更新时间戳(t)和品质(q) */ + ModelNode *parent = p_node->parent; + while (parent && parent->modelType != DataObjectModelType) + { + parent = parent->parent; + } + if (parent && parent->modelType == DataObjectModelType) + { + DataAttribute *child = (DataAttribute *)parent->firstChild; + while (child) + { + if (strcmp(child->name, "t") == 0) + { + IedServer_updateUTCTimeAttributeValue(p_ied_server, child, Hal_getTimeInMs()); + } + else if (strcmp(child->name, "q") == 0) + { + IedServer_updateQuality(p_ied_server, child, QUALITY_VALIDITY_GOOD); + } + child = (DataAttribute *)child->sibling; + } + } + + IedServer_unlockDataModel(p_ied_server); } } diff --git a/src/system/libiec61850s/src/iec61850s.cpp b/src/system/libiec61850s/src/iec61850s.cpp index 405301a..aa9c512 100644 --- a/src/system/libiec61850s/src/iec61850s.cpp +++ b/src/system/libiec61850s/src/iec61850s.cpp @@ -42,7 +42,7 @@ LOCAL void iec61850s_st_mx_change_callback(std::string saddr, uint8_t data_type, std::string val = dc_get_signal_val(p_data, data_type); if(val.empty()) { - LOG_E("val is empty"); + LOG_E("val is empty for saddr: %s", saddr.c_str()); return; }