diff --git a/release/inc/myMms_m.h b/release/inc/myMms_m.h index 464b300..b61c18c 100644 --- a/release/inc/myMms_m.h +++ b/release/inc/myMms_m.h @@ -171,6 +171,17 @@ int mms_m_set_data_value(void *srt, void *dst, uint8_t type); int mms_m_get_data_value_str(void *data, uint8_t type, char *str); int mms_m_set_data_by_str(void *data, uint8_t type, const char *str); + +typedef void (*mms_m_file_read_cb)(int fd, const char *fn, const uint8_t *d, int len, bool mf, int e); +typedef void (*mms_m_file_op_cb)(int fd, const char *fn, int e); +typedef void (*mms_m_server_cb)(int fd, const char *vendor, const char *model, const char *rev, int log_st, int phy_st, int e); +typedef void (*mms_m_sg_cb)(int fd, const char *ld, int act_sg, int num_sg, int e); + +int mms_m_read_file(int fd, const char *rf, mms_m_file_read_cb cb); +int mms_m_delete_file(int fd, const char *rf, mms_m_file_op_cb cb); +int mms_m_query_server(int fd, mms_m_server_cb cb); +int mms_m_read_sg_info(int fd, const char *ld, mms_m_sg_cb cb); + #ifdef __cplusplus } #endif diff --git a/src/protocol/libmms_m/inc/mms_m.h b/src/protocol/libmms_m/inc/mms_m.h index 20e0f7f..d0751d3 100644 --- a/src/protocol/libmms_m/inc/mms_m.h +++ b/src/protocol/libmms_m/inc/mms_m.h @@ -187,3 +187,5 @@ typedef struct + +void *mms_m_get_obj(int app_fd); diff --git a/src/protocol/libmms_m/inc/mms_m_ext.h b/src/protocol/libmms_m/inc/mms_m_ext.h new file mode 100644 index 0000000..8146087 --- /dev/null +++ b/src/protocol/libmms_m/inc/mms_m_ext.h @@ -0,0 +1,67 @@ +/** + * @file mms_m_ext.h + * @brief MMS 客户端扩展功能接口声明 + * + * @details 本文件声明文件服务、服务器信息查询、定值组操作等扩展功能。 + * 回调类型定义见 myMms_m.h + */ + +#pragma once + +#include "myMms_m.h" +#include "mms_m.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * 从远端 IED 读取文件 + * + * 通过 MMS 文件服务 (ObtainFile) 从服务器下载指定文件。 + * 文件内容通过回调分块返回。 + * + * @param app_fd 客户端连接句柄 + * @param rf 远端文件路径 + * @param cb 文件读取回调,分块返回文件数据 + * @return 0 成功发起请求,-1 参数无效或对象不存在 + */ +int mms_m_read_file(int app_fd, const char *rf, mms_m_file_read_cb cb); + +/** + * 删除远端 IED 上的文件 + * + * @param app_fd 客户端连接句柄 + * @param rf 远端文件路径 + * @param cb 操作结果回调 + * @return 0 成功,-1 参数无效或对象不存在 + */ +int mms_m_delete_file(int app_fd, const char *rf, mms_m_file_op_cb cb); + +/** + * 查询远端 IED 的服务器身份和运行状态 + * + * 同时获取 MMS identify 服务返回的厂商/型号/版本信息, + * 以及 VMD 逻辑状态和物理状态。 + * + * @param app_fd 客户端连接句柄 + * @param cb 查询结果回调 + * @return 0 成功,-1 参数无效或对象不存在 + */ +int mms_m_query_server(int app_fd, mms_m_server_cb cb); + +/** + * 读取远端 IED 的定值组信息 + * + * 通过 SGCB 获取当前激活定值组号和定值组总数。 + * + * @param app_fd 客户端连接句柄 + * @param ld 逻辑设备名(如 "PROT") + * @param cb 查询结果回调 + * @return 0 成功,-1 参数无效或对象不存在 + */ +int mms_m_read_sg_info(int app_fd, const char *ld, mms_m_sg_cb cb); + +#ifdef __cplusplus +} +#endif diff --git a/src/protocol/libmms_m/src/mms_m.cpp b/src/protocol/libmms_m/src/mms_m.cpp index 400f0d5..7cf02dc 100644 --- a/src/protocol/libmms_m/src/mms_m.cpp +++ b/src/protocol/libmms_m/src/mms_m.cpp @@ -336,6 +336,19 @@ static int mms_m_send_ao_write(stru_mms_m_obj &obj, stru_mms_m_event &event) return 0; } +static void demo_sg_cb(int fd, const char *ld_name, + int act_sg, int num_sg, int err) +{ + if (IED_ERROR_OK != err) + { + LOG_E("read SG info failed: %s, err=%d", ld_name, err); +/** 定值组信息读取回调 */ + return; + } + + LOG_I("%s: active SG=%d, total SG=%d", ld_name, act_sg, num_sg); +} + static int mms_m_send_param_write(stru_mms_m_obj &obj, stru_mms_m_event &event) { stru_point_item *p_item = NULL; @@ -356,6 +369,8 @@ static int mms_m_send_param_write(stru_mms_m_obj &obj, stru_mms_m_event &event) return -1; } + mms_m_read_sg_info(obj.obj_fd, p_item->ldev, demo_sg_cb); + std::string ref = p_item->reference; size_t pos = ref.find("/"); if(pos == std::string::npos) @@ -955,18 +970,8 @@ static int mms_m_get_MmsValue(stru_point_value &point_value, stru_mms_m_out_valu tm_flag = 1; break; case MMS_BIT_STRING: - point_value.quality = 0; - for (int i = 0; i < MmsValue_getBitStringSize(p_mms_values); i++) - { - if (MmsValue_getBitStringBit(p_mms_values, i)) - { - point_value.quality |= (1 << i); - } - else - { - point_value.quality &= ~(1 << i); - } - } + out_val.type = MMS_BIT_STRING; + *(uint32_t *)p_val = MmsValue_getBitStringAsIntegerBigEndian(p_mms_values); break; default: break; @@ -1477,7 +1482,8 @@ static void mms_m_do_comm(stru_mms_m_obj &obj) if(IED_STATE_CONNECTED != run.con_state && IED_STATE_CONNECTED == run.old_con_state) { - LOG_I("mms_m %s connected %s:%d offline", obj.ied_name.c_str(), obj.run.ip.c_str(), obj.run.port); + LOG_I("mms_m %s connected %s:%d offline, reset RCB subscription", obj.ied_name.c_str(), obj.run.ip.c_str(), obj.run.port); + obj.run.running_init = false; if(run.out_status_cb) { @@ -1803,6 +1809,11 @@ static int mms_m_ied_init(stru_mms_m_obj &obj) +void *mms_m_get_obj(int app_fd){ + auto it = g_mms_m_obj_map.find(app_fd); + return (it != g_mms_m_obj_map.end()) ? (void*)it->second : NULL; +} + int mms_m_out_init(stru_cfg *p_cfg, int debug_print_flag, uint32_t connectionTimeout) { if(NULL == p_cfg) diff --git a/src/protocol/libmms_m/src/mms_m_file.cpp b/src/protocol/libmms_m/src/mms_m_file.cpp new file mode 100644 index 0000000..93d5aa1 --- /dev/null +++ b/src/protocol/libmms_m/src/mms_m_file.cpp @@ -0,0 +1,127 @@ +/** + * @file mms_m_file.cpp + * @brief MMS 文件服务客户端实现 + * + * @details 基于 libiec61850 MmsConnection 底层接口实现: + * - 远端文件读取 (ObtainFile) + * - 远端文件删除 + * + * 文件读取流程: + * 1. MmsConnection_fileOpen() 打开远端文件,获取文件大小 + * 2. MmsConnection_fileRead() 循环分块读取文件内容 + * 3. MmsConnection_fileClose() 关闭文件句柄 + * 4. 通过回调将完整文件数据返回上层 + * + * 【当前状态】文件打开已验证,异步分块读取待实现。 + */ + +#include "myMms_m.h" +#include +#include + +#include "mms_m.h" +#include "mms_m_ext.h" +#include "iec61850_client.h" + +/** + * 从远端 IED 读取文件 + * + * 打开远端文件并获取文件大小。完整的分块异步读取流程待实现。 + * + * @param app_fd 客户端连接句柄 + * @param rf 远端文件路径 + * @param cb 文件读取回调,分块返回文件数据 + * @return 0 成功发起请求,-1 失败 + */ +int mms_m_read_file(int app_fd, const char *rf, mms_m_file_read_cb cb) +{ + if (NULL == rf || NULL == cb) + { + LOG_E("Invalid parameter, rf %p, cb %p", rf, cb); + return -1; + } + + stru_mms_m_obj *p_obj = (stru_mms_m_obj *)mms_m_get_obj(app_fd); + if (NULL == p_obj) + { + LOG_E("app_fd %d not found", app_fd); + return -1; + } + + /* 获取底层 MmsConnection 以使用文件服务 API */ + MmsConnection mc = IedConnection_getMmsConnection(p_obj->run.con); + if (NULL == mc) + { + LOG_E("%s: getMmsConnection failed", p_obj->cfg_path.c_str()); + return -1; + } + + MmsError me; + uint32_t file_size = 0; + uint64_t last_modified = 0; + + /* 打开远端文件,获取大小和修改时间 */ + int32_t frsm_id = MmsConnection_fileOpen(mc, &me, rf, 0, + &file_size, &last_modified); + + if (MMS_ERROR_NONE != me) + { + LOG_E("%s: fileOpen(%s) failed: %d", + p_obj->cfg_path.c_str(), rf, me); + cb(app_fd, rf, NULL, 0, false, IED_ERROR_UNKNOWN); + return -1; + } + + LOG_I("%s: file %s opened, size=%u, lastModified=%lu", + p_obj->cfg_path.c_str(), rf, file_size, last_modified); + + /* + * TODO: 实现异步分块读取 + * + * 需要: + * 1. 分配接收缓冲区 (malloc file_size) + * 2. 循环调用 MmsConnection_fileReadAsync() 读取数据块 + * 3. 在 read_cb 回调中追加数据到缓冲区 + * 4. moreFollows=false 时说明读取完毕,调用上层回调 + * 5. 读取完毕或出错时 MmsConnection_fileClose() + */ + MmsConnection_fileClose(mc, &me, frsm_id); + + cb(app_fd, rf, NULL, 0, false, IED_ERROR_SERVICE_NOT_IMPLEMENTED); + return -1; +} + +/** + * 删除远端 IED 上的文件 + * + * @param app_fd 客户端连接句柄 + * @param rf 远端文件路径 + * @param cb 操作结果回调,err==0 表示删除成功 + * @return 0 成功,-1 失败 + */ +int mms_m_delete_file(int app_fd, const char *rf, mms_m_file_op_cb cb) +{ + if (NULL == rf || NULL == cb) + { + LOG_E("Invalid parameter, rf %p, cb %p", rf, cb); + return -1; + } + + stru_mms_m_obj *p_obj = (stru_mms_m_obj *)mms_m_get_obj(app_fd); + if (NULL == p_obj) + { + LOG_E("app_fd %d not found", app_fd); + return -1; + } + + MmsConnection mc = IedConnection_getMmsConnection(p_obj->run.con); + MmsError me; + + MmsConnection_fileDelete(mc, &me, rf); + + IedClientError ie = (MMS_ERROR_NONE == me) ? IED_ERROR_OK + : IED_ERROR_UNKNOWN; + cb(app_fd, rf, ie); + + return (MMS_ERROR_NONE == me) ? 0 : -1; +} diff --git a/src/protocol/libmms_m/src/mms_m_server.cpp b/src/protocol/libmms_m/src/mms_m_server.cpp new file mode 100644 index 0000000..8581629 --- /dev/null +++ b/src/protocol/libmms_m/src/mms_m_server.cpp @@ -0,0 +1,71 @@ +/** + * @file mms_m_server.cpp + * @brief 远端 IED 服务器信息查询 + * + * @details 通过 MMS identify 和 getServerStatus 服务获取: + * - 厂商名称 (vendorName) + * - 型号名称 (modelName) + * - 版本号 (revision) + * - VMD 逻辑状态 (0=允许变更, 1=禁止变更, 2=受限, 3=支持服务) + * - VMD 物理状态 (0=运行, 1=部分, 2=停运, 3=待调试) + */ + +#include "myMms_m.h" +#include + +#include "mms_m.h" +#include "mms_m_ext.h" +#include "iec61850_client.h" + +/** + * 查询远端 IED 的服务器身份和运行状态 + * + * 同时获取 MMS identify 和 getServerStatus 的结果, + * 通过回调一次性返回。 + * + * @param app_fd 客户端连接句柄 + * @param cb 查询结果回调 + * @return 0 成功,-1 失败 + */ +int mms_m_query_server(int app_fd, mms_m_server_cb cb) +{ + if (NULL == cb) + { + LOG_E("cb is NULL"); + return -1; + } + + stru_mms_m_obj *p_obj = (stru_mms_m_obj *)mms_m_get_obj(app_fd); + if (NULL == p_obj) + { + LOG_E("app_fd %d not found", app_fd); + return -1; + } + + MmsConnection mc = IedConnection_getMmsConnection(p_obj->run.con); + MmsError me; + + /* MMS identify 服务:获取厂商/型号/版本 */ + MmsServerIdentity *p_id = MmsConnection_identify(mc, &me); + + /* MMS getServerStatus 服务:获取 VMD 运行状态 */ + int log_st = 0; + int phy_st = 0; + MmsConnection_getServerStatus(mc, &me, &log_st, &phy_st, false); + + const char *vendor = p_id ? p_id->vendorName : ""; + const char *model = p_id ? p_id->modelName : ""; + const char *rev = p_id ? p_id->revision : ""; + + IedClientError err = (MMS_ERROR_NONE != me) ? IED_ERROR_UNKNOWN + : IED_ERROR_OK; + + cb(app_fd, vendor, model, rev, log_st, phy_st, err); + + if (p_id) + { + MmsServerIdentity_destroy(p_id); + } + + return 0; +} diff --git a/src/protocol/libmms_m/src/mms_m_sg.cpp b/src/protocol/libmms_m/src/mms_m_sg.cpp new file mode 100644 index 0000000..906a074 --- /dev/null +++ b/src/protocol/libmms_m/src/mms_m_sg.cpp @@ -0,0 +1,83 @@ +/** + * @file mms_m_sg.cpp + * @brief 远端 IED 定值组 (Setting Group) 操作 + * + * @details 定值组允许 IED 维护多套运行参数并可在线切换。 + * 本模块通过 LLN0.SGCB 读取当前定值组状态。 + * + * SGCB (Setting Group Control Block) 关键属性: + * - ActSG : 当前激活的定值组号 + * - NumOfSG: 定值组总数 + * - EditSG : 当前编辑的定值组号 + * + * 后续可扩展: + * - 切换激活定值组 (Write ActSG) + * - 编辑确认 (Write CnfEdit) + */ + +#include "myMms_m.h" +#include + +#include "mms_m.h" +#include "mms_m_ext.h" +#include "iec61850_client.h" + +/** + * 读取远端 IED 的定值组信息 + * + * 通过读取 LLN0.SGCB 获取当前激活定值组号和定值组总数。 + * 读取失败时返回默认值 act_sg=1, num_sg=1。 + * + * @param app_fd 客户端连接句柄 + * @param ld 逻辑设备名(如 "PROT"、"CTRL") + * @param cb 查询结果回调 + * @return 0 成功,-1 失败 + */ +int mms_m_read_sg_info(int app_fd, const char *ld, mms_m_sg_cb cb) +{ + if (NULL == ld || NULL == cb) + { + LOG_E("Invalid parameter, ld %p, cb %p", ld, cb); + return -1; + } + + stru_mms_m_obj *p_obj = (stru_mms_m_obj *)mms_m_get_obj(app_fd); + if (NULL == p_obj) + { + LOG_E("app_fd %d not found", app_fd); + return -1; + } + + int act_sg = 1; + int num_sg = 1; + IedClientError err = IED_ERROR_OK; + + /* 拼接 SGCB 对象引用:{LD}/LLN0.SGCB */ + char sgcbref[256]; + snprintf(sgcbref, sizeof(sgcbref), "%s/LLN0.SGCB", ld); + + /* 读取 SGCB 的 FC=ST 属性(包含 ActSG, NumOfSG, EditSG) */ + MmsValue *p_val = IedConnection_readObject(p_obj->run.con, &err, + sgcbref, IEC61850_FC_ST); + + if (p_val && IED_ERROR_OK == err) + { + MmsValue *p_act = MmsValue_getElement(p_val, 0); + MmsValue *p_num = MmsValue_getElement(p_val, 1); + + if (p_act) + { + act_sg = MmsValue_toInt32(p_act); + } + if (p_num) + { + num_sg = MmsValue_toInt32(p_num); + } + + MmsValue_delete(p_val); + } + + cb(app_fd, ld, act_sg, num_sg, err); + + return (IED_ERROR_OK == err) ? 0 : -1; +} diff --git a/src/protocol/libmms_s/inc/mms_s_control.h b/src/protocol/libmms_s/inc/mms_s_control.h index 328aab4..1fa61d1 100644 --- a/src/protocol/libmms_s/inc/mms_s_control.h +++ b/src/protocol/libmms_s/inc/mms_s_control.h @@ -21,4 +21,17 @@ * @param icd ICD 数据结构引用 * @return 0 成功,-1 失败 */ + +/** + * 联锁检查回调类型 + * @param saddr 控制点短地址 + * @param state 请求的状态值 + * @return true 允许操作,false 拒绝 + */ +typedef bool (*mms_s_interlock_cb)(const char *saddr, int state); + +/** + * 设置联锁检查回调 + */ +void mms_s_set_interlock_cb(mms_s_interlock_cb cb); int control_init(IedServer server, stru_icd &icd); \ No newline at end of file diff --git a/src/protocol/libmms_s/inc/mms_s_icd.h b/src/protocol/libmms_s/inc/mms_s_icd.h index 4773d52..e99eafc 100644 --- a/src/protocol/libmms_s/inc/mms_s_icd.h +++ b/src/protocol/libmms_s/inc/mms_s_icd.h @@ -462,6 +462,8 @@ typedef struct typedef struct { ModelNode *node; + DataAttribute *da_t; + DataAttribute *da_q; }stru_saddr_point; /** diff --git a/src/protocol/libmms_s/src/mms_s.cpp b/src/protocol/libmms_s/src/mms_s.cpp index 804583a..8be8b88 100644 --- a/src/protocol/libmms_s/src/mms_s.cpp +++ b/src/protocol/libmms_s/src/mms_s.cpp @@ -43,34 +43,46 @@ 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); +{ + if(mms_s_dbg_get()) + { + 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: + if (serviceError != DATA_ACCESS_ERROR_SUCCESS) + { + LOG_E("RCB %s SET param=%s FAILED err=%d", name, parameterName ? parameterName : "?", serviceError); + } + else + { + LOG_I("RCB %s SET param=%s OK trigOps=0x%x", name, parameterName ? parameterName : "?", 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); + } } +/** SIGINT 信号处理:通知后台线程退出 */ LOCAL void sigint_handler(int signum) { g_running = 0; diff --git a/src/protocol/libmms_s/src/mms_s_control.cpp b/src/protocol/libmms_s/src/mms_s_control.cpp index aeade90..3c19354 100644 --- a/src/protocol/libmms_s/src/mms_s_control.cpp +++ b/src/protocol/libmms_s/src/mms_s_control.cpp @@ -32,6 +32,14 @@ LOCAL std::vector g_vec_control; /** 控制操作回调函数 */ LOCAL mms_s_control_cb cb_control = NULL; +/** 联锁检查回调 */ +LOCAL mms_s_interlock_cb cb_interlock = NULL; + +void mms_s_set_interlock_cb(mms_s_interlock_cb cb) +{ + cb_interlock = cb; +} + /** * 控制操作处理器(Operate 阶段) @@ -287,7 +295,7 @@ int control_init(IedServer server, stru_icd &icd) if(g_vec_control.empty()) { - LOG_E("g_vec_control is empty"); + LOG_I("g_vec_control is empty, no controls registered"); 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 9ff413c..22990ae 100644 --- a/src/protocol/libmms_s/src/mms_s_model.cpp +++ b/src/protocol/libmms_s/src/mms_s_model.cpp @@ -812,6 +812,24 @@ LOCAL int model_ldevice_init(IedModel *model, stru_icd &icd) return 0; } +LOCAL void find_tq_for_da(DataAttribute *da, DataAttribute* *t, DataAttribute* *q) +{ + *t = NULL; + *q = NULL; + if (!da) return; + ModelNode *parent = da->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) *t = child; + else if (strcmp(child->name, "q") == 0) *q = child; + child = (DataAttribute *)child->sibling; + } + } +} + /** * 将 SDI 中的 DAI 默认值(sAddr / val)同步到模型树中的 all_DA 节点 * @@ -906,7 +924,8 @@ void model_sync_da_default_values(stru_icd &icd, stru_SDI &SDI_item, stru_all_DA p_all_da_se_temp->DA->sAddr = StringUtils_copyString(saddr.c_str()); icd.ied.vec_saddr.push_back(saddr); - icd.ied.map_saddr_point[saddr].node = (ModelNode *)p_all_da_se_temp->DA; + icd.ied.map_saddr_point[saddr].node = (ModelNode *)p_all_da_se_temp->DA; + find_tq_for_da(p_all_da_se_temp->DA, &icd.ied.map_saddr_point[saddr].da_t, &icd.ied.map_saddr_point[saddr].da_q); } } } @@ -1021,7 +1040,8 @@ void model_sync_ln_default_values(stru_icd &icd, std::map p_all_da->DA->sAddr = StringUtils_copyString(saddr.c_str()); // 写入模型节点 icd.ied.vec_saddr.push_back(saddr); // 加入列表 - icd.ied.map_saddr_point[saddr].node = (ModelNode *)p_all_da->DA; // 建立映射 + icd.ied.map_saddr_point[saddr].node = (ModelNode *)p_all_da->DA; + find_tq_for_da(p_all_da->DA, &icd.ied.map_saddr_point[saddr].da_t, &icd.ied.map_saddr_point[saddr].da_q); // 建立映射 } } } diff --git a/src/protocol/libmms_s/src/mms_s_param.cpp b/src/protocol/libmms_s/src/mms_s_param.cpp index dd5f7e8..7e52122 100644 --- a/src/protocol/libmms_s/src/mms_s_param.cpp +++ b/src/protocol/libmms_s/src/mms_s_param.cpp @@ -313,6 +313,8 @@ LOCAL bool param_edit_sg_changed_handler(void* parameter, SettingGroupControlBlo * @param max 输出:最大值 * @param step 输出:步长 */ + +/* TODO: 与 mms_s_setting.cpp 中的校验函数重复,建议抽取到公共模块 */ LOCAL void param_min_max_step_get(DataAttribute *node, float &min, float &max, float &step) { if(node == NULL) @@ -465,12 +467,12 @@ LOCAL void param_get_INT(DataAttribute *node_SE, DataAttribute *node_SG, std::st float min, max, step; param_min_max_step_get(node_SG, min, max, step); + int intVal = MmsValue_toInt32(node_SE->mmsValue); if((int)step == 0) { + val = std::to_string(intVal); return; } - - int intVal = MmsValue_toInt32(node_SE->mmsValue); if(intVal < (int)min || intVal > (int)max) { LOG_E("Value %d is out of range [%d, %d]", intVal, (int)min, (int)max); @@ -499,12 +501,12 @@ LOCAL void param_get_UINT(DataAttribute *node_SE, DataAttribute *node_SG, std::s float min, max, step; param_min_max_step_get(node_SG, min, max, step); + int uintVal = MmsValue_toUint32(node_SE->mmsValue); if((uint32_t)step == 0) { + val = std::to_string(uintVal); return; } - - int uintVal = MmsValue_toUint32(node_SE->mmsValue); if(uintVal < (uint32_t)min || uintVal > (uint32_t)max) { LOG_E("Value %d is out of range [%d, %d]", uintVal, (uint32_t)min, (uint32_t)max); @@ -542,19 +544,16 @@ LOCAL void param_get_FLOAT(DataAttribute *node_SE, DataAttribute *node_SG, std:: return; } - if(fabs(step) < EPSILON) - { - val = std::to_string(floatVal); - return; - } - float gap = floatVal - min; + if(fabs(step) > EPSILON) + { float gap_mod = fmod(gap, step); - if(fabs(gap_mod) > EPSILON && fabs(gap_mod - step) > EPSILON) - { - LOG_E("Value %f does not conform to step size %f", floatVal, step); - return; + if(fabs(gap_mod) > EPSILON && fabs(gap_mod - step) > EPSILON) + { + LOG_E("Value %f does not conform to step size %f", floatVal, step); + return; + } } val = std::to_string(floatVal); @@ -704,7 +703,7 @@ LOCAL void param_values_show() { if(g_vec_params.empty()) { - LOG_E("g_vec_params is empty"); + LOG_I("g_vec_params is empty, no params registered"); return; } @@ -766,7 +765,7 @@ int param_init(IedServer server, stru_icd &icd) { if(g_vec_params.empty()) { - LOG_E("g_vec_params is empty"); + LOG_I("g_vec_params is empty, no params registered"); return 0; } diff --git a/src/protocol/libmms_s/src/mms_s_setting.cpp b/src/protocol/libmms_s/src/mms_s_setting.cpp index f016832..38da575 100644 --- a/src/protocol/libmms_s/src/mms_s_setting.cpp +++ b/src/protocol/libmms_s/src/mms_s_setting.cpp @@ -286,11 +286,6 @@ LOCAL int setting_get_INT(DataAttribute *node, MmsValue* value, std::string &val float min, max, step; setting_min_max_step_get(node, min, max, step); - if((int)step == 0) - { - return -1; - } - int intVal = MmsValue_toInt32(value); if(intVal < (int)min || intVal > (int)max) { @@ -322,11 +317,6 @@ LOCAL int setting_get_UINT(DataAttribute *node, MmsValue* value, std::string &va float min, max, step; setting_min_max_step_get(node, min, max, step); - if((uint32_t)step == 0) - { - return -1; - } - int uintVal = MmsValue_toUint32(value); if(uintVal < (uint32_t)min || uintVal > (uint32_t)max) { @@ -367,18 +357,16 @@ LOCAL int setting_get_FLOAT(DataAttribute *node, MmsValue* value, std::string &v return -1; } - if(fabs(step) < EPSILON) - { - return 0; - } - float gap = floatVal - min; + if(fabs(step) > EPSILON) + { float gap_mod = fmod(gap, step); - if(fabs(gap_mod) > EPSILON && fabs(gap_mod - step) > EPSILON) - { - LOG_E("Value %f does not conform to step size %f", floatVal, step); - return -1; + if(fabs(gap_mod) > EPSILON && fabs(gap_mod - step) > EPSILON) + { + LOG_E("Value %f does not conform to step size %f", floatVal, step); + return -1; + } } LOG_I("Float value for node %s is %f", node->name, floatVal); @@ -597,7 +585,7 @@ int setting_init(IedServer server, stru_icd &icd) { if(g_vec_settings.empty()) { - LOG_E("g_vec_settings is empty"); + LOG_I("g_vec_settings is empty, no settings registered"); 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 2ee1453..24e0e3a 100644 --- a/src/protocol/libmms_s/src/mms_s_value.cpp +++ b/src/protocol/libmms_s/src/mms_s_value.cpp @@ -353,13 +353,34 @@ LOCAL void mms_s_da_value_init_Unicode_String(DataAttribute *da, std::string val /** 初始化 Timestamp:设为当前毫秒时间 */ LOCAL void mms_s_da_value_init_Timestamp(DataAttribute *da, std::string val) { - if(NULL == da || val.empty()) + if(NULL == da) return; + da->mmsValue = MmsValue_newUtcTimeByMsTime(Hal_getTimeInMs()); +} + + +LOCAL std::string get_DA_path(ModelNode *node) +{ + std::string str_temp = ""; + if(NULL == node) { - LOG_E("da or val is empty, da %p, val %s", da, val.c_str()); - return; + return str_temp; } - da->mmsValue = MmsValue_newUtcTimeByMsTime(Hal_getTimeInMs()); + str_temp = node->name ? node->name : "?"; + ModelNode *temp_node = node->parent; // 向上遍历父节点 + + while(temp_node) // 逐级拼装完整路径 + { + str_temp = std::string(temp_node->name ? temp_node->name : "?") + "." + str_temp; // parent.name + "." + 当前路径 + + if(temp_node->modelType == LogicalDeviceModelType) // 到达逻辑设备层则停止 + { + break; + } + temp_node = temp_node->parent; // 继续向上一级 + } + + return str_temp; } /** @@ -368,19 +389,12 @@ LOCAL void mms_s_da_value_init_Timestamp(DataAttribute *da, std::string val) */ LOCAL void mms_s_da_value_init_Dbpos(DataAttribute *da, std::string val) { - if(NULL == da || val.empty()) - { - LOG_E("da or val is empty, da %p, val %s", da, val.c_str()); - return; - } + if(NULL == da) return; Dbpos pos = DBPOS_INTERMEDIATE_STATE; - if(val == "") - { - pos = DBPOS_INTERMEDIATE_STATE; - } - else + + if(!val.empty()) { int i = atoi(val.c_str()); if(i == 0) @@ -405,11 +419,20 @@ LOCAL void mms_s_da_value_init_Dbpos(DataAttribute *da, std::string val) } } - Dbpos_toMmsValue(da->mmsValue, pos); + IedServer server = mms_s_get_ied_server_ptr(); + if (server) + { + IedServer_updateDbposValue(server, da, pos); + } + else + { + da->mmsValue = Dbpos_toMmsValue(NULL, pos); + } - if(mms_s_dbg_get()) + // if(mms_s_dbg_get()) { - LOG_I("da %s-%s, value %s, pos %d", da->parent->name, da->name, val.c_str(), pos); + MmsType val_type = MmsValue_getType(da->mmsValue); + LOG_I("da %s-%s, value %s, pos %d, type %d, path %s", da->parent->name, da->name, val.c_str(), pos, val_type, get_DA_path((ModelNode *)da).c_str()); } } @@ -477,7 +500,7 @@ LOCAL void mms_s_da_values_init(stru_icd &icd, std::string da_name, stru_all_DA return; } - if(NULL != g_iec61850s_value_init_cb_map[p_all_da->bType] && !p_all_da->val.empty()) + if(NULL != g_iec61850s_value_init_cb_map[p_all_da->bType] && (!p_all_da->val.empty() || p_all_da->bType == "Timestamp" || p_all_da->bType == "Quality" || p_all_da->bType == "Dbpos")) { g_iec61850s_value_init_cb_map[p_all_da->bType](p_all_da->DA, p_all_da->val); } diff --git a/src/system/libiec61850m/src/iec61850m.cpp b/src/system/libiec61850m/src/iec61850m.cpp index 71eb0ca..f7e466f 100644 --- a/src/system/libiec61850m/src/iec61850m.cpp +++ b/src/system/libiec61850m/src/iec61850m.cpp @@ -7,6 +7,7 @@ #include "parse_xml.h" + LOCAL void mms_event_back(void *arg, int ret); LOCAL std::string get_base_path() @@ -177,6 +178,8 @@ LOCAL void mms_data_back(stru_mms_m_out_value *p) } } +LOCAL int iec61850m_ext_demo(int fd); + LOCAL void iec61850m_connect_status(int fd, int status) { std::string prj_name = ""; @@ -196,6 +199,11 @@ LOCAL void iec61850m_connect_status(int fd, int status) } LOG_I("iec61850m_connect_status, fd %d, prj_name %s, status %s", fd, prj_name.c_str(), status == MMS_M_ON_LINE? "ON_LINE" : "OFF_LINE"); + + if (MMS_M_ON_LINE == status) + { + iec61850m_ext_demo(fd); + } } @@ -588,6 +596,88 @@ int app_iec61850m_init1(void *arg) return 0; } + +/***************************************************************************** + * 新增功能演示回调 + *****************************************************************************/ + +/***************************************************************************** * 新增功能演示回调 * 连接上线后由 iec61850m_ext_demo() 调用,演示文件服务、服务器查询、定值组操作 *****************************************************************************/ +LOCAL void demo_file_read_cb(int fd, const char *filename, + const uint8_t *p_data, int data_len, bool more_follows, + int err) +/** 文件读取回调 */ +{ + if (IED_ERROR_OK != err) + { + LOG_E("file read failed: %s, err=%d", filename, err); + return; + } + + LOG_I("file %s read %d bytes", filename, data_len); + + /* TODO: 将文件数据写入本地磁盘 */ +} + +LOCAL void demo_file_delete_cb(int fd, const char *filename, int err) +{ + if (IED_ERROR_OK == err) + { +/** 文件删除回调 */ + LOG_I("file %s deleted successfully", filename); + } + else + { + LOG_E("file %s delete failed: %d", filename, err); + } +} + +LOCAL void demo_server_query_cb(int fd, const char *vendor, + const char *model, const char *revision, + int logical_status, int physical_status, int err) +{ + if (IED_ERROR_OK != err) +/** 服务器身份和状态查询回调 */ + { + LOG_E("server query failed: %d", err); + return; + } + + LOG_I("server: vendor=%s, model=%s, rev=%s, log_st=%d, phy_st=%d", + vendor, model, revision, logical_status, physical_status); +} + +LOCAL void demo_sg_cb(int fd, const char *ld_name, + int act_sg, int num_sg, int err) +{ + if (IED_ERROR_OK != err) + { + LOG_E("read SG info failed: %s, err=%d", ld_name, err); +/** 定值组信息读取回调 */ + return; + } + + LOG_I("%s: active SG=%d, total SG=%d", ld_name, act_sg, num_sg); +} + +LOCAL int iec61850m_ext_demo(int fd) +{ + /* 查询服务器信息 */ + mms_m_query_server(fd, demo_server_query_cb); + + /* 读取定值组信息 */ + mms_m_read_sg_info(fd, "PROT", demo_sg_cb); +/** * 新功能演示入口 * 查询服务器信息、读取定值组状态 */ + + /* 删除文件示例 */ + // mms_m_delete_file(fd, "IEDSERVER.BIN", demo_file_delete_cb); + + /* 读取文件示例 */ + // mms_m_read_file(fd, "COMTRADE/FAULT01.CFG", demo_file_read_cb); + + return 0; +} + + int app_iec61850m_init2(void *arg) { static uint32_t *p_iec_run_cnt = NULL;