From 3d59800656c117c28de96f1ebf56d2911b00e8ce Mon Sep 17 00:00:00 2001 From: ypc <15051963820@163.com> Date: Wed, 27 May 2026 17:16:45 +0800 Subject: [PATCH] =?UTF-8?q?1=E3=80=81mms=E5=AE=A2=E6=88=B7=E7=AB=AF?= =?UTF-8?q?=E6=97=B6=E9=97=B4=E7=B1=BB=E5=9E=8B=E5=A2=9E=E5=8A=A0=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E8=AF=BB=E5=86=99=E6=9E=9A=E4=B8=BE=EF=BC=9B=202?= =?UTF-8?q?=E3=80=81mms=E6=9C=8D=E5=8A=A1=E7=AB=AF=E5=A2=9E=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + release/inc/myMms_m.h | 2 + release/inc/myMms_s.h | 157 +- src/protocol/libmms_m/src/mms_m.cpp | 18 +- src/protocol/libmms_s/inc/mms_s_control.h | 19 + src/protocol/libmms_s/inc/mms_s_file.h | 20 +- src/protocol/libmms_s/inc/mms_s_icd.h | 324 +- src/protocol/libmms_s/inc/mms_s_model.h | 28 +- src/protocol/libmms_s/inc/mms_s_param.h | 30 +- src/protocol/libmms_s/inc/mms_s_value.h | 22 +- src/protocol/libmms_s/src/mms_s.cpp | 94 +- src/protocol/libmms_s/src/mms_s_control.cpp | 83 +- src/protocol/libmms_s/src/mms_s_file.cpp | 54 +- src/protocol/libmms_s/src/mms_s_icd.cpp | 449 +- src/protocol/libmms_s/src/mms_s_model.cpp | 1092 +-- src/protocol/libmms_s/src/mms_s_param.cpp | 383 +- src/protocol/libmms_s/src/mms_s_setting.cpp | 121 +- src/protocol/libmms_s/src/mms_s_value.cpp | 407 +- src/system/libiec61850m/src/iec61850m.cpp | 22 +- src/system/libiec61850s/src/iec61850s.cpp | 1 + test/file/MMS/PCS.icd | 7684 +++++++++++++++++++ test/file/MMS/prj.xml | 2 +- 22 files changed, 9876 insertions(+), 1137 deletions(-) create mode 100644 test/file/MMS/PCS.icd diff --git a/.gitignore b/.gitignore index 518ae45..f7023d7 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,4 @@ release/x86/exe/FTU_cfg_parse release/x86/exe/RTU src/system/uart_trans/ src/system/FTU_cfg_parse/ +libiec61850-1.5.3/ \ No newline at end of file diff --git a/release/inc/myMms_m.h b/release/inc/myMms_m.h index dc52ffd..c82bc3a 100644 --- a/release/inc/myMms_m.h +++ b/release/inc/myMms_m.h @@ -22,6 +22,8 @@ enum _MMS_M_EVENT_CO_CANCEL, _MMS_M_EVENT_AO_READ, _MMS_M_EVENT_AO_WRITE, + _MMS_M_EVENT_PARAM_READ, + _MMS_M_EVENT_PARAM_WRITE, _MMS_M_EVENT_END }; diff --git a/release/inc/myMms_s.h b/release/inc/myMms_s.h index 5b592c1..c519a50 100644 --- a/release/inc/myMms_s.h +++ b/release/inc/myMms_s.h @@ -1,101 +1,147 @@ +/** + * @file myMms_s.h + * @brief libmms_s 库对外的公共接口头文件 + * + * @details 本文件定义了: + * - 自定义数据类型码(DATA_TYPE_*),用于标识 RTU 系统中各种数据类型的枚举值 + * - 公共数据结构体:参数基类(stru_mms_s_param_base)、定值(stru_mms_s_setting)、 + * 参数组(stru_mms_s_param)、控制(stru_mms_s_control) + * - 回调函数类型定义 + * - 库初始化与注册接口函数声明 + */ + #pragma once #include #include #ifdef __cplusplus -extern "C" { +extern "C" { #endif /* __cplusplus */ +// ===================== 自定义数据类型码 ===================== + +/* 布尔类型 */ #ifndef DATA_TYPE_B #define DATA_TYPE_B 1 -#endif /* DATA_TYPE_B */ +#endif +/* 有符号8位整数 */ #ifndef DATA_TYPE_S8 #define DATA_TYPE_S8 43 -#endif /* DATA_TYPE_S8 */ +#endif +/* 无符号8位整数 */ #ifndef DATA_TYPE_U8 #define DATA_TYPE_U8 32 -#endif /* DATA_TYPE_U8 */ +#endif +/* 有符号16位整数 */ #ifndef DATA_TYPE_S16 #define DATA_TYPE_S16 33 -#endif /* DATA_TYPE_S16 */ +#endif +/* 无符号16位整数 */ #ifndef DATA_TYPE_U16 #define DATA_TYPE_U16 45 -#endif /* DATA_TYPE_U16 */ +#endif +/* 有符号32位整数 */ #ifndef DATA_TYPE_S32 #define DATA_TYPE_S32 2 -#endif /* DATA_TYPE_S32 */ +#endif +/* 无符号32位整数 */ #ifndef DATA_TYPE_U32 #define DATA_TYPE_U32 35 -#endif /* DATA_TYPE_U32 */ +#endif +/* 有符号64位整数 */ #ifndef DATA_TYPE_L64 #define DATA_TYPE_L64 36 -#endif /* DATA_TYPE_L64 */ +#endif +/* 无符号64位整数 */ #ifndef DATA_TYPE_UL64 #define DATA_TYPE_UL64 37 -#endif /* DATA_TYPE_UL64 */ +#endif +/* 单精度浮点数 */ #ifndef DATA_TYPE_F32 #define DATA_TYPE_F32 38 -#endif /* DATA_TYPE_F32 */ +#endif +/* 双精度浮点数 */ #ifndef DATA_TYPE_D64 #define DATA_TYPE_D64 39 -#endif /* DATA_TYPE_D64 */ +#endif +/* IP地址类型 */ #ifndef DATA_TYPE_IP #define DATA_TYPE_IP 100 -#endif /* DATA_TYPE_IP */ +#endif +/* MAC地址类型 */ #ifndef DATA_TYPE_MAC #define DATA_TYPE_MAC 101 -#endif /* DATA_TYPE_MAC */ +#endif +/* 8字符定长字符串 */ #ifndef DATA_TYPE_C8 #define DATA_TYPE_C8 102 -#endif /* DATA_TYPE_C8 */ +#endif +/* 32字符定长字符串 */ #ifndef DATA_TYPE_C32 #define DATA_TYPE_C32 103 -#endif /* DATA_TYPE_C32 */ +#endif +/* 64字符定长字符串 */ #ifndef DATA_TYPE_C64 #define DATA_TYPE_C64 104 -#endif /* DATA_TYPE_C64 */ +#endif +/* 128字符定长字符串 */ #ifndef DATA_TYPE_C128 #define DATA_TYPE_C128 105 -#endif /* DATA_TYPE_C128 */ +#endif +/* 单字符 */ #ifndef DATA_TYPE_C1 #define DATA_TYPE_C1 106 -#endif /* DATA_TYPE_C1 */ +#endif +/* 可变长字符串 */ #ifndef DATA_TYPE_STR #define DATA_TYPE_STR 107 -#endif /* DATA_TYPE_STR */ - +#endif +/* 短地址字符串最大长度 */ #define MMS_S_STR_LEN 64 +/* 参数组最大分组数(定值区最大数量) */ #define MMS_S_PARAM_MAX 16 - +/** + * 参数/定值/控制的基础信息结构体 + * @field saddr 短地址(映射到 IEC61850 模型中的 sAddr 属性) + * @field desc 描述信息 + * @field type 数据类型(对应 DATA_TYPE_* 枚举) + * @field ctrl_model 控制模型类型 + */ typedef struct { char saddr[MMS_S_STR_LEN]; char desc[MMS_S_STR_LEN]; uint8_t type; uint8_t ctrl_model; - // float min; - // float max; - // float step; - // char unit[MMS_S_STR_LEN]; }stru_mms_s_param_base; -// 定值仅支持一个参数 +/** + * 定值数据结构体(一个定值点对应一个数据值) + * @field base 基础信息(saddr / desc / type) + * @field data 指向实际数据值的指针 + */ typedef struct { stru_mms_s_param_base base; void *data; }stru_mms_s_setting; +/** 定值回调函数类型:当定值被写入时调用 */ typedef void (*mms_s_setting_cb)(stru_mms_s_setting *p_setting, const char *p_data); -// 参数支持多个值 +/** + * 参数数据结构体(一个参数点可对应多个定值区的值) + * @field base 基础信息(saddr / desc / type) + * @field param_num 有效参数组数量 + * @field data 各组参数数据指针数组,最多 MMS_S_PARAM_MAX 个 + */ typedef struct { stru_mms_s_param_base base; @@ -103,9 +149,16 @@ typedef struct void *data[MMS_S_PARAM_MAX]; }stru_mms_s_param; +/** 参数回调函数类型:当参数区切换或被确认时调用 */ typedef void (*mms_s_param_cb)(stru_mms_s_param *p_param, const char *p_data, int setting_zone); +/** + * 控制对象数据结构体 + * @field saddr 短地址 + * @field desc 描述信息 + * @field ctrl_model 控制模型类型 + */ typedef struct { char saddr[MMS_S_STR_LEN]; @@ -113,19 +166,59 @@ typedef struct uint8_t ctrl_model; }stru_mms_s_control; +/** 控制操作回调函数类型:当控制命令被执行时调用 */ typedef void (*mms_s_control_cb)(stru_mms_s_control *p_control, uint8_t state); + +/** + * 初始化 MMS 服务器 + * @param icd_path ICD 模型文件路径 + * @param port MMS 服务监听端口 + * @return 0 成功,-1 失败 + */ int mms_s_init(const char* icd_path, int port); + +/** + * 开启/关闭调试输出开关 + * @param on true 开启,false 关闭 + */ void mms_s_dbg_switch(bool on); +/** + * 注册控制对象列表 + * @param p_control 控制对象数组 + * @param num 数组元素个数 + * @param cb 控制操作回调函数(可选,传 NULL 则不注册) + * @return 0 成功,-1 失败 + */ int mms_s_control_register(stru_mms_s_control *p_control, uint16_t num, mms_s_control_cb cb); -int mms_s_setting_register(stru_mms_s_setting *p_setting, uint16_t num, mms_s_setting_cb cb); -int mms_s_param_register(stru_mms_s_param *p_param, uint16_t num, mms_s_param_cb cb); -int mms_s_file_path_set(const char* file_path); +/** + * 注册定值列表 + * @param p_setting 定值对象数组 + * @param num 数组元素个数 + * @param cb 定值写入回调函数(可选,传 NULL 则不注册) + * @return 0 成功,-1 失败 + */ +int mms_s_setting_register(stru_mms_s_setting *p_setting, uint16_t num, mms_s_setting_cb cb); + +/** + * 注册参数列表(参数支持多定值区) + * @param p_param 参数对象数组 + * @param num 数组元素个数 + * @param cb 参数回调函数(可选,传 NULL 则不注册) + * @return 0 成功,-1 失败 + */ +int mms_s_param_register(stru_mms_s_param *p_param, uint16_t num, mms_s_param_cb cb); + +/** + * 设置 MMS 文件服务的根路径 + * @param file_path 文件存储路径,传 NULL 或空串则禁用文件服务 + * @return 0 成功 + */ +int mms_s_file_path_set(const char* file_path); #ifdef __cplusplus } #endif /* __cplusplus */ - diff --git a/src/protocol/libmms_m/src/mms_m.cpp b/src/protocol/libmms_m/src/mms_m.cpp index b5c20f5..f0d42be 100644 --- a/src/protocol/libmms_m/src/mms_m.cpp +++ b/src/protocol/libmms_m/src/mms_m.cpp @@ -811,6 +811,11 @@ static void mms_m_do_send(stru_mms_m_obj &obj) case _MMS_M_EVENT_AO_WRITE: mms_m_send_set_callback(event, mms_m_send_ao_write(obj, event)); break; + case _MMS_M_EVENT_PARAM_READ: + break; + case _MMS_M_EVENT_PARAM_WRITE: + mms_m_send_set_callback(event, mms_m_send_param_write(obj, event)); + break; default: LOG_E("%s, unknown ctrl_type(%d)", obj.cfg_path.c_str(), event.ctrl_type); break; @@ -2149,7 +2154,6 @@ void *mms_m_create_data_ptr(uint8_t type) int64_t *p_i64 = nullptr; uint64_t *p_u64 = nullptr; float *p_f = nullptr; - double *p_d = nullptr; char *p_str = nullptr; switch(type) @@ -2167,9 +2171,9 @@ void *mms_m_create_data_ptr(uint8_t type) (*p_u32) = 0; return (void *)p_u32; case MMS_FLOAT: - p_d = new double; - (*p_d) = 0.0; - return (void *)p_d; + p_f = new float; + (*p_f) = 0.0; + return (void *)p_f; case MMS_STRING: p_str = new char[MMS_M_DATA_STRING_LEN]; memset(p_str, 0, MMS_M_DATA_STRING_LEN); @@ -2199,7 +2203,7 @@ int mms_m_set_data_value(void *srt, void *dst, uint8_t type) *(uint32_t *)dst = *(uint32_t *)srt; break; case MMS_FLOAT: - *(double *)dst = *(double *)srt; + *(float *)dst = *(float *)srt; break; case MMS_STRING: strncpy((char *)dst, (char *)srt, MMS_M_DATA_STRING_LEN); @@ -2233,7 +2237,7 @@ int mms_m_get_data_value_str(void *data, uint8_t type, char *str) snprintf(str, MMS_M_STR_LEN, "%u", *(uint32_t *)data); break; case MMS_FLOAT: - snprintf(str, MMS_M_STR_LEN, "%f", *(double *)data); + snprintf(str, MMS_M_STR_LEN, "%f", *(float *)data); break; case MMS_STRING: strncpy(str, (char *)data, MMS_M_DATA_STRING_LEN); @@ -2277,7 +2281,7 @@ int mms_m_set_data_by_str(void *data, uint8_t type, const char *str) *(uint32_t *)data = (uint32_t)atoi(str); break; case MMS_FLOAT: - *(double *)data = atof(str); + *(float *)data = atof(str); break; case MMS_STRING: strncpy((char *)data, str, MMS_M_DATA_STRING_LEN); diff --git a/src/protocol/libmms_s/inc/mms_s_control.h b/src/protocol/libmms_s/inc/mms_s_control.h index 97446f6..328aab4 100644 --- a/src/protocol/libmms_s/inc/mms_s_control.h +++ b/src/protocol/libmms_s/inc/mms_s_control.h @@ -1,5 +1,24 @@ +/** + * @file mms_s_control.h + * @brief IEC 61850 控制对象(Control)初始化接口 + * + * @details 负责为 ICD 中所有标记为控制类型的数据对象安装 + * control_handler(执行控制)和 check_handler(权限校验) + * 回调函数,使客户端可以通过 Select/Operate 操作控制 RTU 设备。 + */ + #pragma once #include "mms_s_icd.h" +/** + * 初始化控制功能 + * + * 遍历 ICD 中所有控制 DO(即包含 ctlModel DA 的 DO), + * 为每个控制 DO 安装 IEC 61850 控制回调和权限检查回调。 + * + * @param server IED 服务器指针 + * @param icd ICD 数据结构引用 + * @return 0 成功,-1 失败 + */ int control_init(IedServer server, stru_icd &icd); \ No newline at end of file diff --git a/src/protocol/libmms_s/inc/mms_s_file.h b/src/protocol/libmms_s/inc/mms_s_file.h index 2fd9665..2162d2e 100644 --- a/src/protocol/libmms_s/inc/mms_s_file.h +++ b/src/protocol/libmms_s/inc/mms_s_file.h @@ -1,6 +1,22 @@ +/** + * @file mms_s_file.h + * @brief IEC 61850 MMS 文件传输服务初始化接口 + * + * @details 配置文件服务的基础路径,安装文件访问控制回调, + * 限制客户端的文件删除/重命名等操作权限。 + */ + #pragma once #include "mms_s.h" -int file_init(IedServer server); - +/** + * 初始化 MMS 文件传输服务 + * + * 安装文件访问处理回调(限制删除/重命名操作) + * 和连接事件回调(打印连接/断开日志)。 + * + * @param server IED 服务器指针 + * @return 0 成功,-1 失败 + */ +int file_init(IedServer server); \ 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 6a016e6..4773d52 100644 --- a/src/protocol/libmms_s/inc/mms_s_icd.h +++ b/src/protocol/libmms_s/inc/mms_s_icd.h @@ -1,3 +1,43 @@ +/** + * @file mms_s_icd.h + * @brief IEC 61850 ICD(IED Capability Description)文件解析数据结构定义 + * + * @details 本文件定义了 ICD 文件解析的完整数据结构体系,对应 IEC 61850-6 SCL 标准: + * + * 【数据类型模板层 DataTypeTemplates】 + * - stru_LNodeType : 逻辑节点类型,包含 DO 列表 + * - stru_DO : 数据对象定义 + * - stru_DOType : 数据对象类型,包含 DA 和 SDO + * - stru_DA : 数据属性定义 + * - stru_DAType : 数据属性类型,包含 BDA + * - stru_BDA : 基本数据属性定义 + * - stru_EnumType : 枚举类型定义 + * + * 【实例化模型层 IED/Server】 + * - stru_ied : IED 整体模型 + * - stru_LDevice : 逻辑设备 + * - stru_LN0 : LLN0(逻辑节点零,含数据集、报告控制、定值控制) + * - stru_LN : 普通逻辑节点 + * - stru_DOI : 数据对象实例(含 DAI/SDI) + * - stru_SDI : 子数据对象实例 + * - stru_DAI : 数据属性实例 + * - stru_DataSet : 数据集 + * - stru_FCDA : 功能约束数据属性(数据集成员) + * - stru_ReportControl : 报告控制块 + * - stru_SettingControl: 定值控制块 + * + * 【运行时辅助结构】 + * - stru_all_DO : DO 及下属所有元素的完整树 + * - stru_all_DA : DA 及下属所有元素的完整树 + * - stru_contorl_DO: 控制 DO 的定位信息 + * - stru_saddr_point: sAddr 到模型节点的映射 + * - stru_setting_info: 定值信息 + * + * 【解析与展示接口】 + * - icd_parse() : 解析 ICD 文件 + * - icd_show() : 打印 ICD 数据结构内容(调试用) + */ + #pragma once #include @@ -7,13 +47,24 @@ #include "iec61850_server.h" -typedef struct +/** + * 数据对象(DO)定义(DataTypeTemplates 中使用) + * @field desc 描述 + * @field type DO 类型(指向 DOType id) + */ +typedef struct { std::string desc; std::string type; }stru_DO; -typedef struct +/** + * 逻辑节点类型(LNodeType)定义 + * @field lnClass 逻辑节点类(如 "LLN0", "LPHD", "MMXU") + * @field vec_do DO 名称列表(保持顺序) + * @field map_do DO 映射表(key: DO 名称) + */ +typedef struct { std::string lnClass; std::vector vec_do; @@ -21,7 +72,16 @@ typedef struct }stru_LNodeType; -typedef struct +/** + * 数据属性(DA)定义 + * @field bType 基本数据类型("BOOLEAN", "INT32", "FLOAT32", "Struct", "Enum" 等) + * @field type 类型引用(指向 DOType / DAType / EnumType 的 id) + * @field dchg 数据变化触发标志("true"/"false") + * @field qchg 品质变化触发标志("true"/"false") + * @field fc 功能约束("ST", "MX", "SP", "SG", "SE", "CF" 等) + * @field text 默认值文本(来自 Val 子元素) + */ +typedef struct { std::string bType; std::string type; @@ -31,7 +91,15 @@ typedef struct std::string text; }stru_DA; -typedef struct +/** + * 数据对象类型(DOType)定义 + * @field cdc 公共数据类(如 "SPS", "INC", "DPC") + * @field vec_da DA 名称列表(保持顺序) + * @field map_da DA 映射表(key: DA 名称) + * @field vec_sdo 子 DO 名称列表(保持顺序) + * @field map_sdo 子 DO 映射表(key: 子 DO 名称, value: DOType id) + */ +typedef struct { std::string cdc; std::vector vec_da; @@ -41,7 +109,15 @@ typedef struct }stru_DOType; -typedef struct +/** + * 基本数据属性(BDA)定义 + * @field bType 基本数据类型 + * @field type 类型引用 + * @field fc 功能约束(从上层 DA 继承填充) + * @field dchg 数据变化触发标志 + * @field qchg 品质变化触发标志 + */ +typedef struct { std::string bType; std::string type; @@ -50,14 +126,24 @@ typedef struct std::string qchg; }stru_BDA; -typedef struct +/** + * 数据属性类型(DAType)定义 + * @field vec_bda BDA 名称列表(保持顺序) + * @field map_bda BDA 映射表(key: BDA 名称) + */ +typedef struct { std::vector vec_bda; std::map map_bda; // key:name }stru_DAType; -typedef struct +/** + * 枚举类型(EnumType)定义 + * @field vec_ord 枚举序号列表 + * @field map_enumVal 枚举值映射表(key:序号, value:枚举文本) + */ +typedef struct { std::vector vec_ord; std::map map_enumVal; // key:ord @@ -67,7 +153,23 @@ typedef struct - +/** + * 全部 DA 的运行时结构(树形节点) + * + * 既用于保存 ICD 中解析出的 DA 信息,也维护 libiec61850 运行库中创建的 + * DataAttribute 指针。对于 Struct 类型的 DA,递归包含子 DA。 + * + * @field DA libiec61850 DataAttribute 指针 + * @field bType 基本数据类型 + * @field type 类型引用 + * @field fc 功能约束 + * @field dchg 数据变化触发标志 + * @field qchg 品质变化触发标志 + * @field text 默认值文本 + * @field sAddr 短地址(映射外部数据源) + * @field val 初始值 + * @field map_all_da 子 DA 映射表(key: 子 DA 名称),仅 Struct 类型使用 + */ typedef struct _all_DA { DataAttribute *DA; @@ -85,6 +187,15 @@ typedef struct _all_DA +/** + * 全部 DO 的运行时结构(树形节点) + * + * 维护 libiec61850 运行库中创建的 DataObject 指针,及其所有子 SDO 和 DA。 + * + * @field DO libiec61850 DataObject 指针 + * @field map_all_sdo 子 SDO 映射表(key: SDO 名称) + * @field map_all_da DA 映射表(key: DA 名称) + */ typedef struct _all_DO { DataObject *DO; @@ -93,6 +204,11 @@ typedef struct _all_DO }stru_all_DO; +/** + * 数据属性实例(DAI),对应 ICD 中 DOI 下的 DAI 元素 + * @field sAddr 短地址 + * @field val 初始值 + */ typedef struct _DAI { std::string sAddr; @@ -100,6 +216,15 @@ typedef struct _DAI }stru_DAI; +/** + * 子数据对象实例(SDI),对应 ICD 中 DOI 下的 SDI 元素 + * 可递归嵌套 + * + * @field vec_sdI 子 SDI 名称列表 + * @field map_sdI 子 SDI 映射表(key: SDI 名称) + * @field vec_daI DAI 名称列表 + * @field map_daI DAI 映射表(key: DAI 名称) + */ typedef struct _SDI { std::vector vec_sdI; @@ -108,6 +233,14 @@ typedef struct _SDI std::map map_daI; // key:name }stru_SDI; +/** + * 数据对象实例(DOI),对应 ICD 中 LN/LN0 下的 DOI 元素 + * @field desc 描述 + * @field vec_sdI 子 SDI 名称列表 + * @field map_sdI 子 SDI 映射表 + * @field vec_daI DAI 名称列表 + * @field map_daI DAI 映射表 + */ typedef struct _DOI { std::string desc; @@ -117,7 +250,17 @@ typedef struct _DOI std::map map_daI; // key:name }stru_DOI; -typedef struct +/** + * 控制 DO 的定位信息,用于在模型中快速定位控制对象 + * @field ldevice_inst 逻辑设备实例名 + * @field ln_type 逻辑节点类型 + * @field ln_class 逻辑节点类 + * @field ln_inst 逻辑节点实例号 + * @field ln_prefix 逻辑节点前缀 + * @field do_name 数据对象名称 + * @field p_all_do 指向运行时 all_DO 结构的指针 + */ +typedef struct { std::string ldevice_inst; std::string ln_type; @@ -129,7 +272,19 @@ typedef struct }stru_contorl_DO; -typedef struct +/** + * 逻辑节点(LN)实例 + * @field LN libiec61850 LogicalNode 指针 + * @field lnType 逻辑节点类型 id + * @field lnClass 逻辑节点类 + * @field desc 描述 + * @field inst 实例号 + * @field prefix 前缀 + * @field vec_doI DOI 名称列表 + * @field map_doI DOI 映射表 + * @field map_all_do 运行时 DO 映射表 + */ +typedef struct { LogicalNode *LN; std::string lnType; @@ -142,7 +297,18 @@ typedef struct std::map map_all_do; // key:name }stru_LN; -typedef struct +/** + * 功能约束数据属性(FCDA),数据集中的成员 + * @field fcda libiec61850 DataSetEntry 指针 + * @field ldInst 逻辑设备实例 + * @field lnClass 逻辑节点类 + * @field lnInst 逻辑节点实例 + * @field prefix 前缀 + * @field doName 数据对象名称 + * @field daName 数据属性名称 + * @field fc 功能约束 + */ +typedef struct { DataSetEntry *fcda; std::string ldInst; @@ -155,14 +321,27 @@ typedef struct }stru_FCDA; -typedef struct +/** + * 数据集(DataSet) + * @field dataset libiec61850 DataSet 指针 + * @field desc 描述 + * @field vec_fcda FCDA 列表 + */ +typedef struct { DataSet *dataset; std::string desc; std::vector vec_fcda; }stru_DataSet; -typedef struct +/** + * 触发选项(TrgOps) + * @field dchg 数据变化触发 + * @field qchg 品质变化触发 + * @field dupd 数据更新触发 + * @field period 周期触发 + */ +typedef struct { bool dchg; bool qchg; @@ -170,7 +349,17 @@ typedef struct bool period; }stru_TrgOps; -typedef struct +/** + * 报告选项字段(OptFields) + * @field seqNum 序号 + * @field timeStamp 时间戳 + * @field dataSet 数据集引用 + * @field reasonCode 原因码 + * @field dataRef 数据引用 + * @field entryID 条目ID + * @field configRef 配置版本引用 + */ +typedef struct { bool seqNum; bool timeStamp; @@ -181,7 +370,20 @@ typedef struct bool configRef; }stru_OptFields; -typedef struct +/** + * 报告控制块(ReportControl) + * @field Rcb libiec61850 ReportControlBlock 指针 + * @field datSet 关联数据集名称 + * @field intgPd 完整性周期(ms) + * @field rptID 报告 ID + * @field confRev 配置版本号 + * @field buffered 是否缓存报告 + * @field bufTime 缓存时间(ms) + * @field trgOps 触发选项 + * @field optFields 报告选项 + * @field RptEnabled_max 最大客户端实例数 + */ +typedef struct { ReportControlBlock *Rcb; std::string datSet; @@ -195,13 +397,35 @@ typedef struct int RptEnabled_max; }stru_ReportControl; -typedef struct +/** + * 定值控制块(SettingControl) + * @field actSG 当前激活定值组号 + * @field numOfSGs 定值组总数 + */ +typedef struct { uint8_t actSG; uint8_t numOfSGs; }stru_SettingControl; -typedef struct +/** + * LLN0(逻辑节点零)实例 + * + * LLN0 是每个逻辑设备中特殊的逻辑节点,承载数据集、报告控制块、定值控制块等。 + * + * @field LLN0 libiec61850 LogicalNode 指针 + * @field lnType LLN0 的 LNodeType id + * @field lnClass 固定为 "LLN0" + * @field vec_dataset 数据集名称列表 + * @field map_dataset 数据集映射表 + * @field vec_reportControl 报告控制块名称列表 + * @field map_reportControl 报告控制块映射表 + * @field vec_doI DOI 名称列表 + * @field map_doI DOI 映射表 + * @field map_all_do 运行时 DO 映射表 + * @field settingControl 定值控制块配置 + */ +typedef struct { LogicalNode *LLN0; std::string lnType; @@ -216,20 +440,38 @@ typedef struct stru_SettingControl settingControl; }stru_LN0; -typedef struct +/** + * 逻辑设备(LDevice)实例 + * @field Ldevice libiec61850 LogicalDevice 指针 + * @field sgcb libiec61850 SettingGroupControlBlock 指针 + * @field ln0 所属 LLN0 + * @field vec_ln 普通 LN 列表 + */ +typedef struct { LogicalDevice *Ldevice; SettingGroupControlBlock* sgcb; stru_LN0 ln0; - std::vector vec_ln; + std::vector vec_ln; }stru_LDevice; +/** + * sAddr(短地址)到模型节点的映射点 + * @field node libiec61850 模型节点指针 + */ typedef struct { ModelNode *node; }stru_saddr_point; -typedef struct +/** + * 定值信息 + * @field DA 数据属性指针 + * @field type 数据类型 + * @field str_val 字符串值 + * @field val 整数值 + */ +typedef struct { DataAttribute *DA; DataAttributeType type; @@ -237,7 +479,20 @@ typedef struct int val; }stru_setting_info; -typedef struct +/** + * IED 整体模型(顶层结构) + * @field model libiec61850 IedModel 指针 + * @field name IED 名称 + * @field desc IED 描述 + * @field type IED 类型 + * @field vec_ldevice 逻辑设备名称列表 + * @field map_ldevice 逻辑设备映射表(key: inst) + * @field vec_control_do 控制 DO 列表 + * @field vec_saddr 所有 sAddr 列表 + * @field map_saddr_point sAddr 到模型节点的映射 + * @field map_setting_info 定值信息映射表 + */ +typedef struct { IedModel *model; std::string name; @@ -251,7 +506,22 @@ typedef struct std::map map_setting_info; // key:name }stru_ied; -typedef struct +/** + * ICD 完整数据结构(顶层结构) + * + * 包含 IED 实例模型和 DataTypeTemplates 中的所有类型定义。 + * + * @field ied IED 实例数据 + * @field vec_lnType LNodeType id 列表 + * @field map_lnType LNodeType 映射表(key: id) + * @field vec_doType DOType id 列表 + * @field map_doType DOType 映射表(key: id) + * @field vec_daType DAType id 列表 + * @field map_daType DAType 映射表(key: id) + * @field vec_enumType EnumType id 列表 + * @field map_enumType EnumType 映射表(key: id) + */ +typedef struct { stru_ied ied; std::vector vec_lnType; @@ -265,5 +535,15 @@ typedef struct }stru_icd; +/** + * 解析 ICD 文件,构建完整的内存数据结构 + * @param icd_path ICD 文件路径 + * @return stru_icd* 成功返回数据指针,失败返回 nullptr + */ stru_icd *icd_parse(const char *icd_path); + +/** + * 打印 ICD 数据结构内容到标准输出(调试用) + * @param icd ICD 数据引用 + */ void icd_show(stru_icd &icd); \ No newline at end of file diff --git a/src/protocol/libmms_s/inc/mms_s_model.h b/src/protocol/libmms_s/inc/mms_s_model.h index ff75a44..13fa8a2 100644 --- a/src/protocol/libmms_s/inc/mms_s_model.h +++ b/src/protocol/libmms_s/inc/mms_s_model.h @@ -1,6 +1,32 @@ +/** + * @file mms_s_model.h + * @brief 动态创建 IEC 61850 数据模型的接口 + * + * @details 根据解析 ICD 得到的 stru_icd 结构,调用 libiec61850 的 API + * 创建完整的内存数据模型(IedModel),包括: + * - 逻辑设备、逻辑节点、数据对象、数据属性等层级结构 + * - 数据集和报告控制块 + * - sAddr 到模型节点的映射关系 + * - 控制 DO 的搜索与定位 + */ + #pragma once #include "iec61850_server.h" #include "mms_s_icd.h" -int model_init(stru_icd &icd); +/** + * 根据 ICD 数据初始化 IED 模型 + * + * 步骤: + * 1. 创建 IedModel + * 2. 遍历逻辑设备、逻辑节点,递归创建 DO/DA/SDO 等层次结构 + * 3. 创建数据集、报告控制块、定值控制块 + * 4. 同步 ICD 中的默认值到模型 + * 5. 建立 sAddr 到模型节点的映射 + * 6. 搜索所有控制类型的 DO + * + * @param icd ICD 数据结构引用(输出 icd.ied.model) + * @return 0 成功,-1 失败 + */ +int model_init(stru_icd &icd); \ No newline at end of file diff --git a/src/protocol/libmms_s/inc/mms_s_param.h b/src/protocol/libmms_s/inc/mms_s_param.h index 4ab563a..02d2ec7 100644 --- a/src/protocol/libmms_s/inc/mms_s_param.h +++ b/src/protocol/libmms_s/inc/mms_s_param.h @@ -1,5 +1,33 @@ +/** + * @file mms_s_param.h + * @brief IEC 61850 参数(Setting Group)管理初始化接口 + * + * @details 定值组(Setting Group)用于支持多套定值切换。本模块负责: + * - 将外部注册的参数数据加载到模型 SG/SE 数据属性 + * - 监听定值组激活/编辑切换事件 + * - 监听编辑确认事件(回读当前值) + * - 设置读访问权限 + * + * 定值组的两个 FC: + * - SG(Setting Group): 当前激活定值区的实际值 + * - SE(Setting Editable): 编辑缓冲区中的值 + */ + #pragma once #include "mms_s.h" -int param_init(IedServer server, stru_icd &icd); +/** + * 初始化参数(定值组)管理功能 + * + * 为每个逻辑设备的 SGCB 安装以下回调: + * - 激活定值组变更 → 将对应的参数数据加载到 SG DA + * - 编辑定值组变更 → 将对应的参数数据加载到 SE DA + * - 编辑确认 → 回读 SE DA 的当前值到参数数据 + * - 读访问控制 → 允许读取 + * + * @param server IED 服务器指针 + * @param icd ICD 数据结构引用 + * @return 0 成功,-1 失败 + */ +int param_init(IedServer server, stru_icd &icd); \ No newline at end of file diff --git a/src/protocol/libmms_s/inc/mms_s_value.h b/src/protocol/libmms_s/inc/mms_s_value.h index 28226a5..bd58585 100644 --- a/src/protocol/libmms_s/inc/mms_s_value.h +++ b/src/protocol/libmms_s/inc/mms_s_value.h @@ -1,7 +1,27 @@ +/** + * @file mms_s_value.h + * @brief IEC 61850 数据属性值初始化接口 + * + * @details 提供 mms_s_values_init() 作为 IedModel 的初始化回调。 + * 在 IedServer 创建后,遍历模型树中的所有 DA, + * 根据 ICD 中 DAI 的默认值和 bType,设置各 DA 的初始 mmsValue。 + * + * 支持的数据类型:BOOLEAN, INT8/16/32, INT8U/16U/32U, + * FLOAT32/64, Enum, VisString*, Unicode*, Timestamp, Dbpos + */ + #pragma once #include "mms_s_icd.h" #include "mms_s.h" +/** + * 初始化数据模型中的属性值(IedModel initializer 回调) + * + * 遍历 ICD 模型树中的每个 DA,根据其 bType 和 DAI 中配置的 val, + * 创建对应的 MmsValue 并设置到 DataAttribute 上。 + */ void mms_s_values_init(); -void mms_s_values_update(); + +/** 预留:值更新函数(当前未完整实现) */ +void mms_s_values_update(); \ No newline at end of file diff --git a/src/protocol/libmms_s/src/mms_s.cpp b/src/protocol/libmms_s/src/mms_s.cpp index 9c625c9..2ac6af0 100644 --- a/src/protocol/libmms_s/src/mms_s.cpp +++ b/src/protocol/libmms_s/src/mms_s.cpp @@ -1,3 +1,24 @@ +/** + * @file mms_s.cpp + * @brief libmms_s 核心模块:服务器初始化、主循环、类型转换 + * + * @details 本文件是库的主入口,实现了: + * - 全局状态管理(gp_icd, gp_iedServer, g_running, g_dbg_switch) + * - mms_s_init() 完整的服务器初始化流程 + * - 后台线程 mms_s_run_task() 保持 IED Server 运行 + * - 数据类型转字符串的工具函数 + * + * mms_s_init() 的执行流程: + * 1. 解析 ICD 文件 → stru_icd + * 2. 根据 ICD 创建动态数据模型 → IedModel + * 3. 创建 IedServer 配置(启用 SGCB 保留时间) + * 4. 创建 IedServer 实例 + * 5. 初始化控制、参数、文件服务 + * 6. 启动 IedServer + * 7. 初始化定值 + * 8. 启动后台运行线程 + */ + #include @@ -12,39 +33,53 @@ #include "hal_thread.h" +/* 后台线程运行标志,收到 SIGINT 后置 0 */ LOCAL int g_running = 0; +/* 全局 ICD 数据指针(单例) */ LOCAL stru_icd *gp_icd = NULL; +/* 全局 IED 服务器指针(单例) */ LOCAL IedServer gp_iedServer = NULL; +/* 调试输出开关 */ LOCAL bool g_dbg_switch = false; +/** SIGINT 信号处理:通知后台线程退出 */ LOCAL void sigint_handler(int signum) { g_running = 0; } +/** 获取全局 ICD 数据指针 */ stru_icd *mms_s_get_icd_ptr() { return gp_icd; } +/** 获取全局 IED 服务器指针 */ IedServer mms_s_get_ied_server_ptr() { return gp_iedServer; } +/** 设置调试输出开关 */ void mms_s_dbg_switch(bool on) { g_dbg_switch = on; } +/** 获取调试输出开关状态 */ bool mms_s_dbg_get() { return g_dbg_switch; } - +/** + * 将 MMS 数据类型转换为字符串(libiec61850 MmsType 枚举) + * @param p_data 原始数据指针 + * @param type MMS 类型:MMS_BOOLEAN / MMS_INTEGER / MMS_UNSIGNED / MMS_FLOAT / MMS_STRING + * @return 字符串,不支持的类型返回 "" + */ std::string mms_s_get_string_by_mms_type(void *p_data, int type) { if(nullptr == p_data) @@ -72,6 +107,14 @@ std::string mms_s_get_string_by_mms_type(void *p_data, int type) } } +/** + * 将自定义数据类型转换为字符串(DATA_TYPE_* 枚举) + * @param p_data 原始数据指针 + * @param type 自定义类型码: + * DATA_TYPE_B (bool), DATA_TYPE_S8/U8/S16/U16/S32/U32/L64/UL64 (整数类), + * DATA_TYPE_F32/D64 (浮点数), DATA_TYPE_C1/C8/C32/C64/C128/STR (字符串) + * @return 字符串,不支持的类型返回 "" + */ std::string mms_s_get_string_by_type(void *p_data, int type) { if(nullptr == p_data) @@ -122,31 +165,60 @@ std::string mms_s_get_string_by_type(void *p_data, int type) } +/** + * 后台运行线程函数 + * + * 持续循环持有/释放数据模型锁以保持服务器运行,同时监控 g_running 标志。 + * 线程退出时按序销毁 IedServer 和 IedModel。 + * + * @param parameter IedModel 指针(用于最终销毁) + */ LOCAL void *mms_s_run_task(void *parameter) { IedModel *iedModel = (IedModel *)parameter; while(g_running) { - IedServer_lockDataModel(gp_iedServer); + IedServer_lockDataModel(gp_iedServer); IedServer_unlockDataModel(gp_iedServer); - - Thread_sleep(100); + Thread_sleep(100); } IedServer_stop(gp_iedServer); - /* Cleanup - free all resources */ - IedServer_destroy(gp_iedServer); + /* Cleanup - free all resources */ + IedServer_destroy(gp_iedServer); - /* destroy dynamic data model */ - IedModel_destroy(iedModel); + /* destroy dynamic data model */ + IedModel_destroy(iedModel); return NULL; } +/** + * 初始化 MMS 服务器(完整流程) + * + * 执行顺序: + * 1. 解析 ICD 文件 + * 2. 创建 IED 数据模型 + * 3. 创建 IedServer 配置(启用 SGCB 保留时间) + * 4. 创建 IedServer 实例 + * 5. 设置时间品质 + * 6. 初始化控制功能 + * 7. 初始化参数(定值组)功能 + * 8. 初始化文件传输服务 + * 9. 启动 IedServer 监听指定端口 + * 10. 初始化定值功能 + * 11. 检查服务器运行状态 + * 12. 注册 SIGINT 信号处理 + * 13. 启动后台运行线程 + * + * @param icd_path ICD 模型文件路径 + * @param port MMS 服务端口号(通常为 102) + * @return 0 成功,-1 失败 + */ int mms_s_init(const char *icd_path, int port) { if(nullptr == icd_path) @@ -163,7 +235,7 @@ int mms_s_init(const char *icd_path, int port) if(mms_s_dbg_get()) { - icd_show(*gp_icd); + // icd_show(*gp_icd); } if(0 != model_init(*gp_icd)) @@ -208,12 +280,12 @@ int mms_s_init(const char *icd_path, int port) gp_iedServer = NULL; return -1; } - + g_running = 1; signal(SIGINT, sigint_handler); Thread mms_s_thread = Thread_create(mms_s_run_task, iedModel, false); Thread_start(mms_s_thread); - + return 0; } \ No newline at end of file diff --git a/src/protocol/libmms_s/src/mms_s_control.cpp b/src/protocol/libmms_s/src/mms_s_control.cpp index f7e74b3..f25601a 100644 --- a/src/protocol/libmms_s/src/mms_s_control.cpp +++ b/src/protocol/libmms_s/src/mms_s_control.cpp @@ -1,12 +1,51 @@ +/** + * @file mms_s_control.cpp + * @brief IEC 61850 控制对象(Control)功能实现 + * + * @details 实现 IEC 61850-7-2 中定义的 Select Before Operate(SBO)和 + * Direct Operate 控制模型。 + * + * 【核心结构】 + * - vec_control : 外部注册的控制对象列表(由 mms_s_control_register() 设置) + * - cb_control : 控制执行后的回调函数 + * + * 【两个关键回调】 + * - check_handler() : 权限/状态检查(Select 阶段或 Operate 阶段的 interlock check) + * 匹配到已注册的控制 DO 则返回 CONTROL_ACCEPTED + * - control_handler() : 操作执行(Operate 阶段) + * 根据 stVal 的类型(BOOLEAN / Dbpos)更新模型值,并触发外部回调 + * + * 【控制执行流程】 + * 1. 客户端发送 Select 请求 → check_handler() 检查权限 + * 2. 客户端发送 Operate 请求 → check_handler() 做 interlock 检查 + * 3. 执行 control_handler() → 更新 t(时间戳)+ stVal + * 4. 触发 cb_control() 通知外部应用层 + */ + #include "hal_time.h" #include "myMms_s.h" #include "mms_s_control.h" #include "mms_s.h" +/** 外部注册的控制对象列表 */ LOCAL std::vector vec_control; +/** 控制操作回调函数 */ LOCAL mms_s_control_cb cb_control = NULL; +/** + * 控制操作处理器(Operate 阶段) + * + * 在 ICD 中所有控制 DO 中查找与 parameter 匹配的 DO, + * 根据 stVal 的类型(BOOLEAN / Dbpos)更新模型值。 + * 更新后触发外部回调通知应用层。 + * + * @param action 控制动作句柄 + * @param parameter 指向被操作的 DataObject + * @param value 客户端发送的控制值 + * @param test 是否为测试模式 + * @return CONTROL_RESULT_OK 成功,CONTROL_RESULT_FAILED 失败 + */ LOCAL ControlHandlerResult control_handler(ControlAction action, void* parameter, MmsValue* value, bool test) { uint64_t timestamp = Hal_getTimeInMs(); @@ -45,7 +84,7 @@ LOCAL ControlHandlerResult control_handler(ControlAction action, void* parameter for(std::vector::iterator it = p_icd->ied.vec_control_do.begin(); it != p_icd->ied.vec_control_do.end(); ++it) { std::string ln_name = it->ln_prefix + it->ln_class + it->ln_inst; - + if(NULL == it->p_all_do) { LOG_E("ld:%s, ln:%s, do:%s, control all_DO is NULL", it->ldevice_inst.c_str(), ln_name.c_str(), it->do_name.c_str()); @@ -68,7 +107,7 @@ LOCAL ControlHandlerResult control_handler(ControlAction action, void* parameter if(it->p_all_do->DO == parameter) { - + if(it->p_all_do->map_all_da.find("t") != it->p_all_do->map_all_da.end() && it->p_all_do->map_all_da.find("stVal") != it->p_all_do->map_all_da.end()) { ret = 1; @@ -76,7 +115,7 @@ LOCAL ControlHandlerResult control_handler(ControlAction action, void* parameter DataAttribute *stVal = it->p_all_do->map_all_da["stVal"]->DA; std::string saddr = it->p_all_do->map_all_da["stVal"]->DA->sAddr; - + for(std::vector::iterator it_control = vec_control.begin(); it_control != vec_control.end(); ++it_control) { if(it_control->saddr == saddr) @@ -87,7 +126,7 @@ LOCAL ControlHandlerResult control_handler(ControlAction action, void* parameter } } } - + if(stVal->type == IEC61850_BOOLEAN) { IedServer_updateAttributeValue(server, stVal, value); @@ -114,7 +153,7 @@ LOCAL ControlHandlerResult control_handler(ControlAction action, void* parameter { LOG_E("ld:%s, ln:%s, do:%s, control failed", it->ldevice_inst.c_str(), ln_name.c_str(), it->do_name.c_str()); } - + break; } } @@ -123,6 +162,18 @@ LOCAL ControlHandlerResult control_handler(ControlAction action, void* parameter } +/** + * 控制权限/状态检查处理器(Select 阶段或 interlock check) + * + * 匹配 ICD 中已有的控制 DO,若存在则允许操作。 + * + * @param action 控制动作句柄 + * @param parameter 指向被操作的 DataObject + * @param ctlVal 控制值 + * @param test 是否为测试模式 + * @param interlockCheck 是否有联锁检查标志 + * @return CONTROL_ACCEPTED 允许,CONTROL_HARDWARE_FAULT 服务器错误,CONTROL_OBJECT_UNDEFINED 未知对象 + */ LOCAL CheckHandlerResult check_handler(ControlAction action, void* parameter, MmsValue* ctlVal, bool test, bool interlockCheck) { ClientConnection clientCon = ControlAction_getClientConnection(action); @@ -155,14 +206,14 @@ LOCAL CheckHandlerResult check_handler(ControlAction action, void* parameter, Mm if(NULL == (p_icd = mms_s_get_icd_ptr())) { - LOG_E("p_icd is NULL"); + LOG_E("p_icd is NULL"); return CONTROL_HARDWARE_FAULT; } for(std::vector::iterator it = p_icd->ied.vec_control_do.begin(); it != p_icd->ied.vec_control_do.end(); ++it) { std::string ln_name = it->ln_prefix + it->ln_class + it->ln_inst; - + if(NULL == it->p_all_do) { LOG_E("ld:%s, ln:%s, do:%s, control all_DO is NULL", it->ldevice_inst.c_str(), ln_name.c_str(), it->do_name.c_str()); @@ -187,6 +238,13 @@ LOCAL CheckHandlerResult check_handler(ControlAction action, void* parameter, Mm return CONTROL_OBJECT_UNDEFINED; } +/** + * 注册控制对象列表(由外部应用调用) + * @param p_control 控制对象数组 + * @param num 数组元素个数 + * @param cb 控制操作回调(可选) + * @return 0 成功,-1 参数无效 + */ int mms_s_control_register(stru_mms_s_control *p_control, uint16_t num, mms_s_control_cb cb) { if(NULL == p_control || num == 0) @@ -210,6 +268,15 @@ int mms_s_control_register(stru_mms_s_control *p_control, uint16_t num, mms_s_co return 0; } +/** + * 初始化控制功能 + * + * 遍历 ICD 中所有控制 DO,为每个安装 control_handler 和 check_handler。 + * + * @param server IED 服务器 + * @param icd ICD 数据 + * @return 0 成功,-1 失败(当 server 为 NULL 时) + */ int control_init(IedServer server, stru_icd &icd) { if(NULL == server) @@ -240,7 +307,7 @@ int control_init(IedServer server, stru_icd &icd) continue; } - + IedServer_setControlHandler(server, it->p_all_do->DO, (ControlHandler)control_handler, it->p_all_do->DO); IedServer_setPerformCheckHandler(server, it->p_all_do->DO, check_handler, it->p_all_do->DO); diff --git a/src/protocol/libmms_s/src/mms_s_file.cpp b/src/protocol/libmms_s/src/mms_s_file.cpp index cf1fe42..ac6ebc3 100644 --- a/src/protocol/libmms_s/src/mms_s_file.cpp +++ b/src/protocol/libmms_s/src/mms_s_file.cpp @@ -1,9 +1,28 @@ +/** + * @file mms_s_file.cpp + * @brief IEC 61850 MMS 文件传输服务实现 + * + * @details 通过 libiec61850 提供的 MMS 文件服务接口,实现: + * - 设置文件存储根目录 + * - 文件访问权限控制(禁止重命名、禁止删除特定文件) + * - 客户端连接/断开事件日志 + */ + + +#include "myMms_s.h" #include "mms_s_file.h" +/** 文件服务的根目录路径 */ LOCAL std::string g_file_path = ""; - +/** + * 客户端连接状态变化回调 + * @param self IedServer 实例 + * @param connection 客户端连接句柄 + * @param connected true 已连接,false 已断开 + * @param parameter 用户参数(未使用) + */ LOCAL void connectionHandler (IedServer self, ClientConnection connection, bool connected, void* parameter) { if (connected) @@ -12,6 +31,21 @@ LOCAL void connectionHandler (IedServer self, ClientConnection connection, bool LOG_I("Connection closed\n"); } +/** + * 文件访问权限控制回调 + * + * 限制规则: + * - 禁止客户端重命名文件 + * - 禁止删除文件 "IEDSERVER.BIN" + * - 其他操作允许 + * + * @param parameter 用户参数(未使用) + * @param connection MMS 服务器连接 + * @param service 文件服务类型(读/写/删除/重命名) + * @param localFilename 本地文件名 + * @param otherFilename 目标文件名(重命名时使用) + * @return MMS_ERROR_NONE 允许,其他错误码表示拒绝 + */ LOCAL MmsError fileAccessHandler (void* parameter, MmsServerConnection connection, MmsFileServiceType service, const char* localFilename, const char* otherFilename) { LOG_I("fileAccessHandler: service = %i, local-file: %s other-file: %s\n", service, localFilename, otherFilename); @@ -21,7 +55,7 @@ LOCAL MmsError fileAccessHandler (void* parameter, MmsServerConnection connectio return MMS_ERROR_FILE_FILE_ACCESS_DENIED; /* Don't allow client to delete file "IEDSERVER.BIN" */ - if (service == MMS_FILE_ACCESS_TYPE_DELETE) + if (service == MMS_FILE_ACCESS_TYPE_DELETE) { if (strcmp(localFilename, "IEDSERVER.BIN") == 0) return MMS_ERROR_FILE_FILE_ACCESS_DENIED; @@ -31,6 +65,11 @@ LOCAL MmsError fileAccessHandler (void* parameter, MmsServerConnection connectio return MMS_ERROR_NONE; } +/** + * 设置 MMS 文件服务根路径 + * @param file_path 文件存储路径,NULL/空串则跳过后续初始化 + * @return 0 成功 + */ int mms_s_file_path_set(const char* file_path) { if(nullptr == file_path) @@ -45,6 +84,17 @@ int mms_s_file_path_set(const char* file_path) return 0; } +/** + * 初始化 MMS 文件服务 + * + * 若 g_file_path 为空则跳过。否则: + * 1. 设置文件存储根目录 + * 2. 安装文件访问权限控制回调 + * 3. 安装客户端连接事件回调 + * + * @param server IED 服务器 + * @return 0 成功,-1 server 或 mmsServer 为 NULL + */ int file_init(IedServer server) { if(g_file_path.empty()) diff --git a/src/protocol/libmms_s/src/mms_s_icd.cpp b/src/protocol/libmms_s/src/mms_s_icd.cpp index 498e9c4..ac0b453 100644 --- a/src/protocol/libmms_s/src/mms_s_icd.cpp +++ b/src/protocol/libmms_s/src/mms_s_icd.cpp @@ -1,10 +1,32 @@ +/** + * @file mms_s_icd.cpp + * @brief IEC 61850 ICD 文件解析器 + * + * @details 使用 tinyxml2 库解析 SCL(Substation Configuration Language)格式的 + * ICD 文件,构建内存中的 stru_icd 数据结构。 + * + * 【解析流程】 + * icd_parse() → parse_ied() → parse_LDevice() → parse_LN0() / parse_LN() + * → parse_dataTypeTemplates() → parse_LNodeType / DOType / DAType / EnumType + * → check_ied_dataTypeTemplates() (校验 DOI 引用的数据类型一致性) + * + * 【关键设计】 + * - 使用 LOCAL 声明的静态函数实现内聚性,仅 icd_parse() / icd_show() 对外暴露 + * - BDA 的 fc/dchg/qchg 属性通过 parse_BDA_fc() 从父级 DA 向下传播填充 + * - 校验函数 check_*_dataTypeTemplates() 递归验证 ICD 中每个实例节点 + * 都能在 DataTypeTemplates 中找到对应的类型定义 + * - 解析失败时通过 goto ERR_EXIT 统一清理内存 + */ + #include "stdio.h" #include "tinyxml2.h" #include "mms_s_icd.h" #include "mms_s.h" -// ICD文件中的节点名称 +// ============================================================ +// ICD 文件中的 XML 元素名称常量 +// ============================================================ LOCAL const char *ele_SCL = "SCL"; LOCAL const char *ele_IED = "IED"; LOCAL const char *ele_AccessPoint = "AccessPoint"; @@ -39,7 +61,9 @@ LOCAL const char *ele_BDA = "BDA"; LOCAL const char *ele_EnumType = "EnumType"; LOCAL const char *ele_EnumVal = "EnumVal"; -// ICD文件中的属性名称 +// ============================================================ +// ICD 文件中的 XML 属性名称常量 +// ============================================================ LOCAL const char *key_name = "name"; LOCAL const char *key_desc = "desc"; LOCAL const char *key_type = "type"; @@ -57,7 +81,7 @@ LOCAL const char *key_intgPd = "intgPd"; LOCAL const char *key_rptId = "rptId"; LOCAL const char *key_confRev = "confRev"; LOCAL const char *key_buffered = "buffered"; -LOCAL const char *key_bufTime = "bufTime"; +LOCAL const char *key_bufTime = "bufTime"; LOCAL const char *key_dchg = "dchg"; LOCAL const char *key_qchg = "qchg"; LOCAL const char *key_dupd = "dupd"; @@ -85,12 +109,13 @@ LOCAL const char *key_ord = "ord"; using namespace tinyxml2; -/* - * parse_DataSet - parse DataSet element - input: ele_ln0 - ln0 element - output: map_dataset - DataSet data structure - return: 0 - success, -1 - failure -*/ +/** + * 解析 DataSet 元素及其所有 FCDA 子元素 + * @param ele_ln0 LLN0 XML 元素 + * @param map_dataset 输出:DataSet 映射表 + * @param vec_dataset 输出:DataSet 名称列表 + * @return 0 成功,-1 失败 + */ LOCAL int parse_DataSet(XMLElement *ele_ln0, std::map &map_dataset, std::vector &vec_dataset) { if(nullptr == ele_ln0) @@ -136,12 +161,12 @@ LOCAL int parse_DataSet(XMLElement *ele_ln0, std::map return 0; } -/* - * parse_ReportControl_TrgOps - parse TrgOps element - input: ele_reportControl - reportControl element - output: trgOps - TrgOps data structure - return: 0 - success, -1 - failure -*/ +/** + * 解析 ReportControl 下的 TrgOps 元素 + * @param ele_reportControl ReportControl XML 元素 + * @param trgOps 输出:触发选项 + * @return 0 成功,-1 失败 + */ LOCAL int parse_ReportControl_TrgOps(XMLElement *ele_reportControl, stru_TrgOps &trgOps) { if(nullptr == ele_reportControl) @@ -165,12 +190,12 @@ LOCAL int parse_ReportControl_TrgOps(XMLElement *ele_reportControl, stru_TrgOps return 0; } -/* - * parse_ReportControl_OptFields - parse OptFields element - input: ele_reportControl - reportControl element - output: optFields - OptFields data structure - return: 0 - success, -1 - failure -*/ +/** + * 解析 ReportControl 下的 OptFields 元素 + * @param ele_reportControl ReportControl XML 元素 + * @param optFields 输出:报告选项字段 + * @return 0 成功,-1 失败 + */ LOCAL int parse_ReportControl_OptFields(XMLElement *ele_reportControl, stru_OptFields &optFields) { if(nullptr == ele_reportControl) @@ -196,12 +221,12 @@ LOCAL int parse_ReportControl_OptFields(XMLElement *ele_reportControl, stru_OptF return 0; } -/* - * parse_ReportControl_RptEnable - parse RptEnabled element - input: ele_reportControl - reportControl element - output: RptEnabled_max - maximum number of reports - return: 0 - success, -1 - failure -*/ +/** + * 解析 ReportControl 下的 RptEnabled 元素,获取最大客户端实例数 + * @param ele_reportControl ReportControl XML 元素 + * @param RptEnabled_max 输出:最大实例数 + * @return 0 成功,-1 失败 + */ LOCAL int parse_ReportControl_RptEnable(XMLElement *ele_reportControl, int &RptEnabled_max) { if(nullptr == ele_reportControl) @@ -222,12 +247,13 @@ LOCAL int parse_ReportControl_RptEnable(XMLElement *ele_reportControl, int &RptE return 0; } -/* - * parse_ReportControl - parse ReportControl element - input: ele_ln0 - ln0 element - output: map_reportControl - map to store ReportControl data structure - return: 0 - success, -1 - failure -*/ +/** + * 解析 ReportControl 元素(含 TrgOps / OptFields / RptEnabled 子元素) + * @param ele_ln0 LLN0 XML 元素 + * @param map_reportControl 输出:报告控制块映射表 + * @param vec_reportControl 输出:报告控制块名称列表 + * @return 0 成功,-1 失败 + */ LOCAL int parse_ReportControl(XMLElement *ele_ln0, std::map &map_reportControl, std::vector &vec_reportControl) { if(nullptr == ele_ln0) @@ -258,7 +284,7 @@ LOCAL int parse_ReportControl(XMLElement *ele_ln0, std::mapAttribute(key_bufTime); reportControl.bufTime = temp_str == nullptr ? 0 : atoi(temp_str); - + parse_ReportControl_TrgOps(ele, reportControl.trgOps); parse_ReportControl_OptFields(ele, reportControl.optFields); parse_ReportControl_RptEnable(ele, reportControl.RptEnabled_max); @@ -270,12 +296,13 @@ LOCAL int parse_ReportControl(XMLElement *ele_ln0, std::map &map_daI, std::vector &vec_daI) { if(nullptr == ele) @@ -294,7 +321,7 @@ LOCAL int parse_DAI(XMLElement *ele, std::map &map_daI, s } dai.sAddr = ele_dai->Attribute(key_sAddr) == nullptr ? "" : ele_dai->Attribute(key_sAddr); - + XMLElement *ele_val = nullptr; if(nullptr != (ele_val = ele_dai->FirstChildElement(ele_Val))) { @@ -308,12 +335,13 @@ LOCAL int parse_DAI(XMLElement *ele, std::map &map_daI, s return 0; } -/* - * parse_SDI - parse SDI element - input: ele_doi - doi element - output: map_daI - map to store DA data structure - return: 0 - success, -1 - failure -*/ +/** + * 解析 SDI 元素(子数据对象实例),递归解析嵌套的 SDI 和 DAI + * @param ele_doi 父 XML 元素 + * @param map_sdI 输出:SDI 映射表 + * @param vec_sdI 输出:SDI 名称列表 + * @return 0 成功,-1 失败 + */ LOCAL int parse_SDI(XMLElement *ele_doi, std::map &map_sdI, std::vector &vec_sdI) { if(nullptr == ele_doi) @@ -341,12 +369,13 @@ LOCAL int parse_SDI(XMLElement *ele_doi, std::map &map_sd return 0; } -/* - * parse_DOI - parse DOI element - input: ele_ln - ln element - output: map_doI - map to store DOI data structure - return: 0 - success, -1 - failure -*/ +/** + * 解析 DOI 元素(数据对象实例) + * @param ele_ln 父 XML 元素(LN / LN0) + * @param map_doI 输出:DOI 映射表 + * @param vec_doI 输出:DOI 名称列表 + * @return 0 成功,-1 失败 + */ LOCAL int parse_DOI(XMLElement *ele_ln, std::map &map_doI, std::vector &vec_doI) { if(nullptr == ele_ln) @@ -366,7 +395,7 @@ LOCAL int parse_DOI(XMLElement *ele_ln, std::map &map_doI } doi.desc = ele_doi->Attribute(key_desc) == nullptr ? "" : ele_doi->Attribute(key_desc); - + parse_SDI(ele_doi, doi.map_sdI, doi.vec_sdI); parse_DAI(ele_doi, doi.map_daI, doi.vec_daI); @@ -377,12 +406,12 @@ LOCAL int parse_DOI(XMLElement *ele_ln, std::map &map_doI return 0; } -/* - * parse_SettingControl - parse SettingControl element - input: ele_ln0 - ln0 element - output: settingControl - settingControl data structure - return: 0 - success, -1 - failure -*/ +/** + * 解析 SettingControl 元素 + * @param ele_ln0 LLN0 XML 元素 + * @param settingControl 输出:定值控制块 + * @return 0 成功,-1 失败 + */ LOCAL int parse_SettingControl(XMLElement *ele_ln0, stru_SettingControl &settingControl) { if(nullptr == ele_ln0) @@ -406,12 +435,15 @@ LOCAL int parse_SettingControl(XMLElement *ele_ln0, stru_SettingControl &setting return 0; } -/* - * parse_LN0 - parse LN0 element - input: ele_ldevice - ldevice element - output: ln0 - ln0 data structure - return: 0 - success, -1 - failure -*/ +/** + * 解析 LN0 元素(逻辑节点零:LLN0) + * + * 包含:DataSet、ReportControl、DOI、SettingControl + * + * @param ele_ldevice LDevice XML 元素 + * @param ln0 输出:LN0 数据结构 + * @return 0 成功,-1 失败 + */ LOCAL int parse_LN0(XMLElement *ele_ldevice, stru_LN0 &ln0) { if(nullptr == ele_ldevice) @@ -440,12 +472,15 @@ LOCAL int parse_LN0(XMLElement *ele_ldevice, stru_LN0 &ln0) return 0; } -/* - * parse_LN - parse LN element - input: ele_ldevice - ldevice element - output: vec_ln - vector to store LN data structure - return: 0 - success, -1 - failure -*/ +/** + * 解析 LN 元素(普通逻辑节点) + * + * 遍历 LDevice 下所有 LN 元素,每个 LN 解析其属性和 DOI。 + * + * @param ele_ldevice LDevice XML 元素 + * @param vec_ln 输出:LN 列表 + * @return 0 成功,-1 失败 + */ LOCAL int parse_LN(XMLElement *ele_ldevice, std::vector &vec_ln) { if(nullptr == ele_ldevice) @@ -465,25 +500,28 @@ LOCAL int parse_LN(XMLElement *ele_ldevice, std::vector &vec_ln) ln.desc = ele_ln->Attribute(key_desc) == nullptr ? "" : ele_ln->Attribute(key_desc); ln.inst = ele_ln->Attribute(key_inst) == nullptr ? "" : ele_ln->Attribute(key_inst); ln.prefix = ele_ln->Attribute(key_prefix) == nullptr ? "" : ele_ln->Attribute(key_prefix); - + if(0 != parse_DOI(ele_ln, ln.map_doI, ln.vec_doI)) { LOG_E("parse DOI %s:%s failed", ln.lnClass.c_str(), ln.inst.c_str()); continue; } - + vec_ln.push_back(ln); } return 0; } -/* - * parse_LDevice - parse LDevice element - input: ele_ldevice - ldevice element - output: ldevice - ldevice data structure - return: 0 - success, -1 - failure -*/ +/** + * 解析 LDevice 元素(逻辑设备) + * + * 解析其 LN0 和所有 LN。 + * + * @param ele_ldevice LDevice XML 元素 + * @param ldevice 输出:LDevice 数据结构 + * @return 0 成功,-1 失败 + */ LOCAL int parse_LDevice(XMLElement *ele_ldevice, stru_LDevice &ldevice) { if(nullptr == ele_ldevice) @@ -509,12 +547,16 @@ LOCAL int parse_LDevice(XMLElement *ele_ldevice, stru_LDevice &ldevice) return 0; } -/* - * parse_ied - parse IED element - input: ele_ied - ied element - output: ied - ied data structure - return: 0 - success, -1 - failure -*/ +/** + * 解析 IED 元素 + * + * 沿着 SCL > IED > AccessPoint > Server > LDevice 路径, + * 遍历所有逻辑设备进行解析。 + * + * @param ele_ied IED XML 元素 + * @param ied 输出:IED 数据结构 + * @return 0 成功,-1 失败 + */ LOCAL int parse_ied(XMLElement *ele_ied, stru_ied &ied) { if(nullptr == ele_ied) @@ -522,7 +564,7 @@ LOCAL int parse_ied(XMLElement *ele_ied, stru_ied &ied) LOG_E("ied is null"); return -1; } - + ied.model = nullptr; ied.name = ele_ied->Attribute(key_name) == nullptr ? "" : ele_ied->Attribute(key_name); ied.desc = ele_ied->Attribute(key_desc) == nullptr ? "" : ele_ied->Attribute(key_desc); @@ -566,12 +608,16 @@ LOCAL int parse_ied(XMLElement *ele_ied, stru_ied &ied) return 0; } -/* - * parse_LNodeType - parse LNodeType element - input: dataTypeTemplates - dataTypeTemplates element - output: map_lnType - map to store LNodeType data structure - return: 0 - success, -1 - failure -*/ +/** + * 解析 LNodeType 元素(逻辑节点类型模板) + * + * 遍历每个 LNodeType 的子 DO 元素,记录 DO 的 name/type/desc。 + * + * @param dataTypeTemplates DataTypeTemplates XML 元素 + * @param map_lnType 输出:LNodeType 映射表 + * @param vec_lnType 输出:LNodeType id 列表 + * @return 0 成功,-1 失败 + */ LOCAL int parse_LNodeType(XMLElement *dataTypeTemplates, std::map &map_lnType, std::vector &vec_lnType) { if(nullptr == dataTypeTemplates) @@ -616,12 +662,17 @@ LOCAL int parse_LNodeType(XMLElement *dataTypeTemplates, std::map &map_doType, std::vector &vec_doType) { if(nullptr == dataTypeTemplates) @@ -652,7 +703,7 @@ LOCAL int parse_DOType(XMLElement *dataTypeTemplates, std::mapAttribute(key_dchg) == nullptr ? "" : ele_da->Attribute(key_dchg); DA.qchg = ele_da->Attribute(key_qchg) == nullptr ? "" : ele_da->Attribute(key_qchg); DA.fc = ele_da->Attribute(key_fc) == nullptr ? "" : ele_da->Attribute(key_fc); - + XMLElement *ele_val = nullptr; if(nullptr != (ele_val = ele_da->FirstChildElement(ele_Val))) { @@ -685,12 +736,16 @@ LOCAL int parse_DOType(XMLElement *dataTypeTemplates, std::map &map_daType, std::vector &vec_daType) { if(nullptr == dataTypeTemplates) @@ -727,12 +782,16 @@ LOCAL int parse_DAType(XMLElement *dataTypeTemplates, std::map &map_enumType, std::vector &vec_enumType) { if(nullptr == dataTypeTemplates) @@ -764,6 +823,15 @@ LOCAL int parse_EnumType(XMLElement *dataTypeTemplates, std::map &map_daType) { for(std::map::iterator it_da = map_daType.begin(); it_da != map_daType.end(); it_da++) @@ -793,6 +861,15 @@ LOCAL void parse_BDA_fc(std::map &map_daType) } } +/** + * 从 DOType 向 DAType 传播 fc/dchg/qchg 属性 + * + * 当 DA 的 type 指向某个 DAType(即 Struct 类型),将该 DA 的 fc/dchg/qchg + * 传播到目标 DAType 的所有 BDA 上。 + * + * @param map_doType DOType 映射表 + * @param map_daType DAType 映射表 + */ LOCAL void parse_BDA_fc(std::map &map_doType, std::map &map_daType) { for(std::map::iterator it_do = map_doType.begin(); it_do != map_doType.end(); it_do++) @@ -819,12 +896,16 @@ LOCAL void parse_BDA_fc(std::map &map_doType, std::map } } -/* - * parse_dataTypeTemplates - parse dataTypeTemplates element - input: dataTypeTemplates - dataTypeTemplates element - output: icd - icd data structure - return: 0 - success, -1 - failure -*/ +/** + * 解析 DataTypeTemplates 区域 + * + * 依次解析 LNodeType、DOType、DAType、EnumType, + * 然后传播 BDA 的 fc/dchg/qchg 属性。 + * + * @param dataTypeTemplates DataTypeTemplates XML 元素 + * @param icd 输出:icd 数据结构 + * @return 0 成功,-1 失败 + */ LOCAL int parse_dataTypeTemplates(XMLElement *dataTypeTemplates, stru_icd &icd) { if(nullptr == dataTypeTemplates) @@ -838,19 +919,19 @@ LOCAL int parse_dataTypeTemplates(XMLElement *dataTypeTemplates, stru_icd &icd) LOG_E("parse LNodeType failed"); return -1; } - + if(0 != parse_DOType(dataTypeTemplates, icd.map_doType, icd.vec_doType)) { LOG_E("parse DOType failed"); return -1; } - + if(0 != parse_DAType(dataTypeTemplates, icd.map_daType, icd.vec_daType)) { LOG_E("parse DAType failed"); return -1; } - + if(0 != parse_EnumType(dataTypeTemplates, icd.map_enumType, icd.vec_enumType)) { LOG_E("parse EnumType failed"); @@ -864,6 +945,18 @@ LOCAL int parse_dataTypeTemplates(XMLElement *dataTypeTemplates, stru_icd &icd) return 0; } +/** + * 递归校验 Struct DA 类型的实例与模板一致性(深度方向) + * + * 验证 SDI 实例中每个子 SDI 和数据属性在对应 DAType 模板中是否存在定义。 + * 对于 Struct 类型 BDA,递归校验其嵌套结构。 + * + * @param icd ICD 数据 + * @param da_type 当前 DAType id + * @param vec_sdI SDI 名称列表 + * @param map_sdI SDI 映射表 + * @return 0 通过,-1 存在问题 + */ LOCAL int check_da_dataTypeTemplates(stru_icd &icd, std::string da_type, std::vector &vec_sdI, std::map &map_sdI) { if(icd.map_daType.find(da_type) == icd.map_daType.end()) @@ -911,6 +1004,18 @@ LOCAL int check_da_dataTypeTemplates(stru_icd &icd, std::string da_type, std::ve return 0; } +/** + * 校验 DO 实例中的 DAI 与 DOType 模板的一致性 + * + * 验证 DOI 实例中每个 DAI 在对应 DOType 模板的 map_da 中是否存在定义。 + * 对于 Struct 类型的 DA,检查其引用的 DAType 是否存在。 + * + * @param icd ICD 数据 + * @param do_type 当前 DOType id + * @param vec_daI DAI 名称列表 + * @param map_daI DAI 映射表 + * @return 0 通过,-1 存在问题 + */ LOCAL int check_do_dataTypeTemplates(stru_icd &icd, std::string do_type, std::vector &vec_daI, std::map &map_daI) { if(icd.map_doType.find(do_type) == icd.map_doType.end()) @@ -918,7 +1023,7 @@ LOCAL int check_do_dataTypeTemplates(stru_icd &icd, std::string do_type, std::ve LOG_E("doType %s not found in dataTypeTemplates", do_type.c_str()); return -1; } - + stru_DOType *p_DOType = &icd.map_doType[do_type]; for(std::vector::iterator it_daI = vec_daI.begin(); it_daI != vec_daI.end(); it_daI++) @@ -928,7 +1033,7 @@ LOCAL int check_do_dataTypeTemplates(stru_icd &icd, std::string do_type, std::ve LOG_E("DAI %s not found in DOI", it_daI->c_str()); return -1; } - + stru_DAI *p_dai = &map_daI[*it_daI]; if(p_DOType->map_da.find(*it_daI) == p_DOType->map_da.end()) @@ -950,6 +1055,19 @@ LOCAL int check_do_dataTypeTemplates(stru_icd &icd, std::string do_type, std::ve } +/** + * 校验 DO 实例中的 SDI 与 DOType 模板的一致性(广度方向) + * + * 验证 DOI 实例中每个 SDI 在对应 DOType 模板的 map_sdo 或 map_da 中是否存在。 + * 对于 map_sdo 中的引用,递归校验嵌套的 SDI 和 DAI; + * 对于 map_da 中的引用(Struct 类型),进入 check_da_dataTypeTemplates() 深度校验。 + * + * @param icd ICD 数据 + * @param do_type 当前 DOType id + * @param vec_sdI SDI 名称列表 + * @param map_sdI SDI 映射表 + * @return 0 通过,-1 存在问题 + */ LOCAL int check_do_dataTypeTemplates(stru_icd &icd, std::string do_type, std::vector &vec_sdI, std::map &map_sdI) { if(icd.map_doType.find(do_type) == icd.map_doType.end()) @@ -957,7 +1075,7 @@ LOCAL int check_do_dataTypeTemplates(stru_icd &icd, std::string do_type, std::ve LOG_E("doType %s not found in dataTypeTemplates", do_type.c_str()); return -1; } - + stru_DOType *p_DOType = &icd.map_doType[do_type]; for(std::vector::iterator it_sdI = vec_sdI.begin(); it_sdI != vec_sdI.end(); it_sdI++) @@ -1009,7 +1127,7 @@ LOCAL int check_do_dataTypeTemplates(stru_icd &icd, std::string do_type, std::ve return -1; } } - } + } } return 0; @@ -1017,6 +1135,15 @@ LOCAL int check_do_dataTypeTemplates(stru_icd &icd, std::string do_type, std::ve +/** + * 校验 LN(LN0 或普通 LN)中 DOI 实例与 LNodeType 模板的一致性 + * + * @param icd ICD 数据 + * @param ln_type 当前 LNodeType id + * @param vec_doI DOI 名称列表 + * @param map_doI DOI 映射表 + * @return 0 通过,-1 存在问题 + */ LOCAL int check_ln_dataTypeTemplates(stru_icd &icd, std::string ln_type, std::vector &vec_doI, std::map &map_doI) { if(icd.map_lnType.find(ln_type) == icd.map_lnType.end()) @@ -1050,7 +1177,7 @@ LOCAL int check_ln_dataTypeTemplates(stru_icd &icd, std::string ln_type, std::ve LOG_E("check lntype %s do %s sdi failed", ln_type.c_str(), it_do->c_str()); return -1; } - + if(0 != check_do_dataTypeTemplates(icd, p_do->type, p_doi->vec_daI, p_doi->map_daI)) { LOG_E("check lntype %s do %s dai failed", ln_type.c_str(), it_do->c_str()); @@ -1063,6 +1190,14 @@ LOCAL int check_ln_dataTypeTemplates(stru_icd &icd, std::string ln_type, std::ve +/** + * 校验整个 IED 实例模型与 DataTypeTemplates 的一致性 + * + * 遍历所有逻辑设备,对 LLN0 和每个 LN 的 DOI 实例逐一校验。 + * + * @param p_icd ICD 数据指针 + * @return 0 通过,-1 存在问题 + */ LOCAL int check_ied_dataTypeTemplates(stru_icd *p_icd) { if(NULL == p_icd) @@ -1100,12 +1235,18 @@ LOCAL int check_ied_dataTypeTemplates(stru_icd *p_icd) return 0; } -/* - * parse_icd - parse icd file - input: icd_file - icd file path - output: - return: stru_icd* - icd data structure -*/ +/** + * 解析 ICD 文件主函数 + * + * 1. 加载 XML 文件 + * 2. 定位 SCL > IED,调用 parse_ied() + * 3. 定位 SCL > DataTypeTemplates,调用 parse_dataTypeTemplates() + * 4. 校验实例与模板的一致性 + * 5. 返回构建好的完整内存结构 + * + * @param icd_file ICD 文件路径 + * @return 成功返回 stru_icd 指针,失败(含 XML 加载错误/解析错误/校验失败)返回 nullptr + */ stru_icd *icd_parse(const char *icd_file) { if(nullptr == icd_file) @@ -1120,7 +1261,7 @@ stru_icd *icd_parse(const char *icd_file) LOG_E("new stru_icd failed"); return nullptr; } - + XMLDocument doc; XMLElement *scl, *ied, *dataTypeTemplates; XMLError err = doc.LoadFile(icd_file); @@ -1129,7 +1270,7 @@ stru_icd *icd_parse(const char *icd_file) LOG_E("load icd file failed, err:%d", err); goto ERR_EXIT; } - + scl = doc.FirstChildElement(ele_SCL); if(nullptr == scl) { @@ -1143,7 +1284,7 @@ stru_icd *icd_parse(const char *icd_file) LOG_E("no IED element"); goto ERR_EXIT; } - + dataTypeTemplates = scl->FirstChildElement(ele_DataTypeTemplates); if(nullptr == dataTypeTemplates) { @@ -1175,13 +1316,17 @@ stru_icd *icd_parse(const char *icd_file) ERR_EXIT: delete p_icd; - + return nullptr; } +/** + * 递归打印 SDI 数据结构(调试用) + * @param sdi SDI 引用 + */ LOCAL void show_icd_SDI(stru_SDI &sdi) { for(auto it_vec = sdi.vec_sdI.begin(); it_vec != sdi.vec_sdI.end(); it_vec++) @@ -1199,12 +1344,10 @@ LOCAL void show_icd_SDI(stru_SDI &sdi) } } -/* - * show_icd_DOI - show DOI data structure - input: doI - DOI data structure - output: - return: -*/ +/** + * 递归打印 DOI 数据结构(调试用) + * @param doI DOI 引用 + */ LOCAL void show_icd_DOI(stru_DOI &doI) { for(auto it_vec = doI.vec_sdI.begin(); it_vec != doI.vec_sdI.end(); it_vec++) @@ -1224,12 +1367,16 @@ LOCAL void show_icd_DOI(stru_DOI &doI) } -/* - * icd_show - show icd data structure - input: icd - icd data structure - output: - return: -*/ +/** + * 打印 ICD 完整数据结构(调试用) + * + * 打印内容包括: + * - IED 基本信息 + * - 各逻辑设备(LDevice)的详细信息:LN0/LN、数据集、报告控制、定值控制、DOI + * - 数据类型模板:LNodeType、DOType、DAType、EnumType + * + * @param icd ICD 数据引用 + */ void icd_show(stru_icd &icd) { @@ -1254,7 +1401,7 @@ void icd_show(stru_icd &icd) for(auto it_fcda = p_dataset->vec_fcda.begin(); it_fcda != p_dataset->vec_fcda.end(); it_fcda++) { { - printf("\t\t\t\tldInst %s, prefix %s, lnClass %s, lnInst %s, doName %s, daName %s, fc %s\n", + printf("\t\t\t\tldInst %s, prefix %s, lnClass %s, lnInst %s, doName %s, daName %s, fc %s\n", it_fcda->ldInst.c_str(), it_fcda->prefix.c_str(), it_fcda->lnClass.c_str(), it_fcda->lnInst.c_str(), it_fcda->doName.c_str(), it_fcda->daName.c_str(), it_fcda->fc.c_str()); } } @@ -1264,13 +1411,13 @@ void icd_show(stru_icd &icd) for(auto it_rpt_vec = p_ldevice->ln0.vec_reportControl.begin(); it_rpt_vec != p_ldevice->ln0.vec_reportControl.end(); it_rpt_vec++) { stru_ReportControl *p_rpt = &p_ldevice->ln0.map_reportControl[*it_rpt_vec]; - printf("\t\t\treportControl name:%s, datSet:%s, intgPd:%d, rptID:%s, confRev:%d, buffered:%d, bufTime:%d\n", + printf("\t\t\treportControl name:%s, datSet:%s, intgPd:%d, rptID:%s, confRev:%d, buffered:%d, bufTime:%d\n", it_rpt_vec->c_str(), p_rpt->datSet.c_str(), p_rpt->intgPd, p_rpt->rptID.c_str(), p_rpt->confRev, p_rpt->buffered, p_rpt->bufTime); } // for show ln0 settingControl info printf("\t\tsettingControl actSG:%d, numOfSGs:%d\n", p_ldevice->ln0.settingControl.actSG, p_ldevice->ln0.settingControl.numOfSGs); - + // for show ln0 doI info for(auto it_doi_vec = p_ldevice->ln0.vec_doI.begin(); it_doi_vec != p_ldevice->ln0.vec_doI.end(); it_doi_vec++) { diff --git a/src/protocol/libmms_s/src/mms_s_model.cpp b/src/protocol/libmms_s/src/mms_s_model.cpp index 5dc62c0..4143570 100644 --- a/src/protocol/libmms_s/src/mms_s_model.cpp +++ b/src/protocol/libmms_s/src/mms_s_model.cpp @@ -1,125 +1,159 @@ -#include "iec61850_server.h" -#include "string_utilities.h" +/** + * @file mms_s_model.cpp + * @brief 根据 ICD 解析结果动态创建 libiec61850 数据模型 + * + * @details 本文件是 ICD 数据结构到运行时 IedModel 的桥梁,核心流程: + * + * 【model_init() — 入口】 + * 1. 创建 IedModel + * 2. model_ldevice_init() — 遍历创建所有逻辑设备 + * - model_ln0_init() — 创建 LLN0(含 DA/SDO、数据集、报告控制、定值控制块) + * - model_LNodes_init() — 创建普通 LN + * 3. model_sync_ied_default_values() — 将 ICD 中 DAI 的 val/sAddr 同步到模型, + * 同时建立 vec_saddr / map_saddr_point 映射 + * 4. 打印 sAddr → 模型路径对应关系(调试信息) + * 5. model_search_control_DataObjects() — 搜索所有控制 DO + * 6. 注册 mms_s_values_init 为模型初始化回调 + * + * 【关键映射表】 + * - g_dataAttributeTypeMap — bType 字符串 → IEC61850 DataAttributeType + * - g_functionalConstraintMap — FC 字符串 → FunctionalConstraint 枚举 + * + * 【SG/SE 双数据属性设计】 + * 当 DA 的 FC=SG 时,除了创建 FC=SG 的 DA 外,还会额外创建一个 FC=SE 的同名 DA + * (名称为 "{name}_SE"),用于编辑缓冲区。当 FC=SE 时同理会创建 SG 版本。 + */ -#include "mms_s.h" -#include "mms_s_icd.h" -#include "mms_s_value.h" -#include "mms_s_model.h" +#include "iec61850_server.h" // libiec61850 服务器 API(IedModel/IedServer/DataAttribute 等) +#include "string_utilities.h" // 字符串工具(StringUtils_copyString 等) + +#include "mms_s.h" // 公共宏定义(LOCAL/LOG_I/LOG_E)和全局访问函数 +#include "mms_s_icd.h" // ICD 数据结构定义(stru_icd/stru_LDevice/stru_LN 等) +#include "mms_s_value.h" // DA 值初始化回调(mms_s_values_init) +#include "mms_s_model.h" // 本模块头文件(model_init 声明) +LOCAL std::string get_DA_path(ModelNode *node); +LOCAL std::string printf_(int cnt); -LOCAL std::map g_dataAttributeTypeMap = +/** + * bType(基本数据类型)字符串到 libiec61850 DataAttributeType 枚举的映射表 + * 用于将 ICD 中 XML 属性 bType 的字符串值转换为 libiec61850 内部枚举 + */ +LOCAL std::map g_dataAttributeTypeMap = { - {"BOOLEAN", IEC61850_BOOLEAN}, - {"INT8", IEC61850_INT8}, - {"INT16", IEC61850_INT16}, - {"INT32", IEC61850_INT32}, - {"INT64", IEC61850_INT64}, - {"INT128", IEC61850_INT128}, - {"INT8U", IEC61850_INT8U}, - {"INT16U", IEC61850_INT16U}, - {"INT24U", IEC61850_INT24U}, - {"INT32U", IEC61850_INT32U}, - {"FLOAT32", IEC61850_FLOAT32}, - {"FLOAT64", IEC61850_FLOAT64}, - {"Enum", IEC61850_ENUMERATED}, - {"Octet64", IEC61850_OCTET_STRING_64}, - {"Octet6", IEC61850_OCTET_STRING_6}, - {"Octet8", IEC61850_OCTET_STRING_8}, - {"VisString32", IEC61850_VISIBLE_STRING_32}, - {"VisString64", IEC61850_VISIBLE_STRING_64}, - {"VisString65", IEC61850_VISIBLE_STRING_65}, - {"VisString129", IEC61850_VISIBLE_STRING_129}, - {"VisString255", IEC61850_VISIBLE_STRING_255}, - {"Unicode255", IEC61850_UNICODE_STRING_255}, - {"Timestamp", IEC61850_TIMESTAMP}, - {"Quality", IEC61850_QUALITY}, - {"Check", IEC61850_CHECK}, - {"CodedEnum", IEC61850_CODEDENUM}, - {"BitString", IEC61850_GENERIC_BITSTRING}, - {"Struct", IEC61850_CONSTRUCTED}, - {"EntryTime", IEC61850_ENTRY_TIME}, - {"PhyComAddr", IEC61850_PHYCOMADDR}, - {"Currency", IEC61850_CURRENCY}, - {"OptFlds", IEC61850_OPTFLDS}, - {"TrgOps", IEC61850_TRGOPS}, - {"Dbpos", IEC61850_GENERIC_BITSTRING}, + {"BOOLEAN", IEC61850_BOOLEAN}, // 布尔型 + {"INT8", IEC61850_INT8}, // 8位有符号整数 + {"INT16", IEC61850_INT16}, // 16位有符号整数 + {"INT32", IEC61850_INT32}, // 32位有符号整数 + {"INT64", IEC61850_INT64}, // 64位有符号整数 + {"INT128", IEC61850_INT128}, // 128位有符号整数 + {"INT8U", IEC61850_INT8U}, // 8位无符号整数 + {"INT16U", IEC61850_INT16U}, // 16位无符号整数 + {"INT24U", IEC61850_INT24U}, // 24位无符号整数 + {"INT32U", IEC61850_INT32U}, // 32位无符号整数 + {"FLOAT32", IEC61850_FLOAT32}, // 32位浮点数 + {"FLOAT64", IEC61850_FLOAT64}, // 64位浮点数 + {"Enum", IEC61850_ENUMERATED}, // 枚举类型 + {"Octet64", IEC61850_OCTET_STRING_64}, // 64字节八位组字符串 + {"Octet6", IEC61850_OCTET_STRING_6}, // 6字节八位组字符串(MAC地址) + {"Octet8", IEC61850_OCTET_STRING_8}, // 8字节八位组字符串 + {"VisString32", IEC61850_VISIBLE_STRING_32}, // 32字符可视字符串 + {"VisString64", IEC61850_VISIBLE_STRING_64}, // 64字符可视字符串 + {"VisString65", IEC61850_VISIBLE_STRING_65}, // 65字符可视字符串 + {"VisString129", IEC61850_VISIBLE_STRING_129}, // 129字符可视字符串 + {"VisString255", IEC61850_VISIBLE_STRING_255}, // 255字符可视字符串 + {"Unicode255", IEC61850_UNICODE_STRING_255}, // 255字符Unicode字符串 + {"Timestamp", IEC61850_TIMESTAMP}, // 时间戳 + {"Quality", IEC61850_QUALITY}, // 品质 + {"Check", IEC61850_CHECK}, // 校验位 + {"CodedEnum", IEC61850_CODEDENUM}, // 编码枚举 + {"BitString", IEC61850_GENERIC_BITSTRING},// 位串(通用) + {"Struct", IEC61850_CONSTRUCTED}, // 结构体(需递归展开子BDA) + {"EntryTime", IEC61850_ENTRY_TIME}, // 条目时间 + {"PhyComAddr", IEC61850_PHYCOMADDR}, // 物理通信地址 + {"Currency", IEC61850_CURRENCY}, // 货币 + {"OptFlds", IEC61850_OPTFLDS}, // 报告选项字段位掩码 + {"TrgOps", IEC61850_TRGOPS}, // 报告触发选项位掩码 + {"Dbpos", IEC61850_GENERIC_BITSTRING},// 双位状态(用位串表示on/off/intermediate/bad) }; -LOCAL std::map g_functionalConstraintMap = +/** + * FC(功能约束)字符串到 libiec61850 FunctionalConstraint 枚举的映射表 + * FC 决定数据的用途和访问方式(状态/测量/控制/定值等) + */ +LOCAL std::map g_functionalConstraintMap = { - {"ST", IEC61850_FC_ST}, - {"MX", IEC61850_FC_MX}, - {"SP", IEC61850_FC_SP}, - {"SV", IEC61850_FC_SV}, - {"CF", IEC61850_FC_CF}, - {"DC", IEC61850_FC_DC}, - {"SG", IEC61850_FC_SG}, - {"SE", IEC61850_FC_SE}, - {"SR", IEC61850_FC_SR}, - {"OR", IEC61850_FC_OR}, - {"BL", IEC61850_FC_BL}, - {"EX", IEC61850_FC_EX}, - {"CO", IEC61850_FC_CO}, - {"US", IEC61850_FC_US}, - {"MS", IEC61850_FC_MS}, - {"RP", IEC61850_FC_RP}, - {"BR", IEC61850_FC_BR}, - {"LG", IEC61850_FC_LG}, - {"GO", IEC61850_FC_GO}, + {"ST", IEC61850_FC_ST}, // 状态(Status)— 遥信 + {"MX", IEC61850_FC_MX}, // 测量(Measurements)— 遥测 + {"SP", IEC61850_FC_SP}, // 定值(Setpoint)— 单一设定值 + {"SV", IEC61850_FC_SV}, // 取代(Substituted Values) + {"CF", IEC61850_FC_CF}, // 配置(Configuration) + {"DC", IEC61850_FC_DC}, // 描述(Description) + {"SG", IEC61850_FC_SG}, // 定值组(Setting Group)— 激活的定值区 + {"SE", IEC61850_FC_SE}, // 定值编辑(Setting Edit)— 编辑缓冲区 + {"SR", IEC61850_FC_SR}, // 服务跟踪(Service Tracking) + {"OR", IEC61850_FC_OR}, // 操作接收(Operate Received) + {"BL", IEC61850_FC_BL}, // 闭锁(Blocking) + {"EX", IEC61850_FC_EX}, // 扩展(Extension) + {"CO", IEC61850_FC_CO}, // 控制(Control) + {"US", IEC61850_FC_US}, // 未分类(Unclassified) + {"MS", IEC61850_FC_MS}, // 多播采样值(Multicast Sampled Values) + {"RP", IEC61850_FC_RP}, // 非多播采样值(Unicast Sampled Values) + {"BR", IEC61850_FC_BR}, // 缓冲报告(Buffered Report) + {"LG", IEC61850_FC_LG}, // 日志(Logging) + {"GO", IEC61850_FC_GO}, // GOOSE控制 }; -/* - * model_get_triggerOptions - Gets trigger options - input: da - data attribute structure - output: - return: trigger options -*/ +/** + * 根据 DA 的 dchg/qchg 标志组合生成 triggerOptions + * @param da DA 定义 + * @return triggerOptions 位掩码(TRG_OPT_DATA_CHANGED | TRG_OPT_QUALITY_CHANGED) + */ LOCAL int model_get_triggerOptions(stru_DA &da) { - int triggerOptions = 0; + int triggerOptions = 0; // 初始无触发 - if(da.dchg.compare("true") == 0) + if(da.dchg.compare("true") == 0) // DA 的数据变化标志为 true { - triggerOptions |= TRG_OPT_DATA_CHANGED; + triggerOptions |= TRG_OPT_DATA_CHANGED; // 添加数据变化触发 } - if(da.qchg.compare("true") == 0) + if(da.qchg.compare("true") == 0) // DA 的品质变化标志为 true { - triggerOptions |= TRG_OPT_QUALITY_CHANGED; + triggerOptions |= TRG_OPT_QUALITY_CHANGED; // 添加品质变化触发 } return triggerOptions; } -/* - * model_get_rpt_trgOps - Gets report trigger options - input: trgOps - trigger options structure - output: - return: trigger options -*/ +/** + * 根据 TrgOps 结构生成报告触发选项位掩码 + * @param trgOps 触发选项结构 + * @return 位掩码(TRG_OPT_DATA_CHANGED | TRG_OPT_QUALITY_CHANGED | TRG_OPT_DATA_UPDATE | TRG_OPT_INTEGRITY) + */ LOCAL int model_get_rpt_trgOps(stru_TrgOps &trgOps) { - int ret = 0; + int ret = 0; // 初始无触发 - if(trgOps.dchg) + if(trgOps.dchg) // 数据变化触发 (dchg) { ret |= TRG_OPT_DATA_CHANGED; } - if(trgOps.qchg) + if(trgOps.qchg) // 品质变化触发 (qchg) { ret |= TRG_OPT_QUALITY_CHANGED; } - if(trgOps.dupd) + if(trgOps.dupd) // 数据更新触发 (dupd) { ret |= TRG_OPT_DATA_UPDATE; } - if(trgOps.period) + if(trgOps.period) // 周期上送触发 (period) { ret |= TRG_OPT_INTEGRITY; } @@ -127,47 +161,46 @@ LOCAL int model_get_rpt_trgOps(stru_TrgOps &trgOps) return ret; } -/* - * model_get_rpt_options - Gets report options - input: optFields - report options structure - output: - return: report options -*/ +/** + * 根据 OptFields 结构生成报告选项位掩码 + * @param optFields 报告选项结构 + * @return 位掩码(RPT_OPT_* 系列) + */ LOCAL int model_get_rpt_options(stru_OptFields &optFields) { - int ret = 0; + int ret = 0; // 初始无选项 - if(optFields.seqNum) + if(optFields.seqNum) // 序号 { ret |= RPT_OPT_SEQ_NUM; } - if(optFields.timeStamp) + if(optFields.timeStamp) // 时间戳 { ret |= RPT_OPT_TIME_STAMP; } - if(optFields.reasonCode) + if(optFields.reasonCode) // 触发原因码 { ret |= RPT_OPT_REASON_FOR_INCLUSION; } - if(optFields.dataSet) + if(optFields.dataSet) // 数据集引用 { ret |= RPT_OPT_DATA_SET; } - if(optFields.dataRef) + if(optFields.dataRef) // 数据引用 { ret |= RPT_OPT_DATA_REFERENCE; } - if(optFields.entryID) + if(optFields.entryID) // 条目ID { ret |= RPT_OPT_ENTRY_ID; } - if(optFields.configRef) + if(optFields.configRef) // 配置版本号 { ret |= RPT_OPT_CONF_REV; } @@ -175,71 +208,77 @@ LOCAL int model_get_rpt_options(stru_OptFields &optFields) return ret; } -/* - * model_BDA_dataobject_create - Creates BDA data object - input: icd - ICD structure - type - BDA data object type - dataAttr - parent data attribute - fc - functional constraint - all_da - BDA data object structure - output: all_da.map_all_da - return: 0 on success, -1 on failure -*/ +/** + * 递归创建 BDA 数据属性(Struct 类型的子 DA) + * + * 根据 DAType 模板中定义的 BDA 列表,依次创建 DataAttribute。 + * 对于 bType=Struct 的 BDA,递归调用自身继续展开。 + * + * @param icd ICD 数据 + * @param type 当前 DAType id + * @param triggerOptions 触发选项位掩码 + * @param fc 功能约束 + * @param dataAttr 父 DataAttribute(作为 ModelNode parent) + * @param all_da 输出:当前层级的 all_DA 结构 + * @return 0 成功,-1 失败 + */ LOCAL int model_BDA_dataobject_create(stru_icd &icd, std::string type, int triggerOptions, FunctionalConstraint fc, DataAttribute *dataAttr, stru_all_DA &all_da) { - if(icd.map_daType.find(type) == icd.map_daType.end()) + if(icd.map_daType.find(type) == icd.map_daType.end()) // 检查 DAType id 是否已解析 { LOG_E("DAType %s not found in ICD", type.c_str()); return -1; } - stru_DAType &daType_item = icd.map_daType[type]; + stru_DAType &daType_item = icd.map_daType[type]; // 取 DAType 定义 for(std::vector::iterator it_bda_vec = daType_item.vec_bda.begin(); it_bda_vec!= daType_item.vec_bda.end(); it_bda_vec++) { - if(daType_item.map_bda.find(*it_bda_vec) == daType_item.map_bda.end()) + if(daType_item.map_bda.find(*it_bda_vec) == daType_item.map_bda.end()) // BDA 不存在 { LOG_E("BDA %s not found in DAType_item", it_bda_vec->c_str()); continue; } - stru_BDA &it_bda = daType_item.map_bda[*it_bda_vec]; + stru_BDA &it_bda = daType_item.map_bda[*it_bda_vec]; // 取 BDA 定义 - if(g_dataAttributeTypeMap.find(it_bda.bType) == g_dataAttributeTypeMap.end()) + if(g_dataAttributeTypeMap.find(it_bda.bType) == g_dataAttributeTypeMap.end()) // bType 未知 { LOG_E("Data attribute type %s not found in g_dataAttributeTypeMap", it_bda.bType.c_str()); continue; } - stru_all_DA *temp_all_da = new stru_all_DA; + stru_all_DA *temp_all_da = new stru_all_DA; // 分配运行时 DA 结构 - DataAttributeType btype = g_dataAttributeTypeMap[it_bda.bType]; + DataAttributeType btype = g_dataAttributeTypeMap[it_bda.bType]; // 字符串 → 枚举 temp_all_da->DA = DataAttribute_create(it_bda_vec->c_str(), (ModelNode*) dataAttr, btype, fc, triggerOptions, 0, NULL); - if(NULL == temp_all_da->DA) + if(NULL == temp_all_da->DA) // 创建失败 { LOG_E("Failed to create data attribute %s", it_bda_vec->c_str()); delete temp_all_da; continue; } - temp_all_da->bType = it_bda.bType; - temp_all_da->type = it_bda.type; - temp_all_da->fc = it_bda.fc; - temp_all_da->dchg = it_bda.dchg; - temp_all_da->qchg = it_bda.qchg; - temp_all_da->text = ""; - if(btype == IEC61850_CONSTRUCTED) + temp_all_da->bType = it_bda.bType; // 保存基本类型字符串 + temp_all_da->type = it_bda.type; // 保存模板类型引用(用于 Struct 递归) + temp_all_da->fc = it_bda.fc; // 保存功能约束 + temp_all_da->dchg = it_bda.dchg; // 保存数据变化标志 + temp_all_da->qchg = it_bda.qchg; // 保存品质变化标志 + temp_all_da->text = ""; // 文本描述(暂无) + if(btype == IEC61850_CONSTRUCTED) // Struct 类型需递归展开子 BDA { - model_BDA_dataobject_create(icd, it_bda.type, triggerOptions, fc, temp_all_da->DA, *temp_all_da); + model_BDA_dataobject_create(icd, it_bda.type, triggerOptions, fc, temp_all_da->DA, *temp_all_da); } - if(fc == IEC61850_FC_SE) + all_da.map_all_da[*it_bda_vec] = temp_all_da; + + if(fc == IEC61850_FC_SE) // FC=SE 的 BDA,key 加 "_SE" 后缀 { std::string se_name = *it_bda_vec + "_SE"; all_da.map_all_da[se_name] = temp_all_da; } - else + else // 普通 BDA,直接用名称做 key { all_da.map_all_da[*it_bda_vec] = temp_all_da; } @@ -248,25 +287,30 @@ LOCAL int model_BDA_dataobject_create(stru_icd &icd, std::string type, int trigg return 0; } -/* - * model_dataobject_create - Creates data object - input: icd - ICD structure - DOType_item - data object type item - node - parent node - do_name - data object name - all_do - data object structure - output: all_do.map_all_da - return: 0 on success, -1 on failure -*/ +/** + * 创建一个数据对象(DO)及其下所有 DA 和 SDO + * + * 1. 调用 DataObject_create() 创建 DO 节点 + * 2. 遍历 DOType 中 vec_da,为每个 DA 创建 DataAttribute + * 3. 对于 Struct 类型的 DA,递归调用 model_BDA_dataobject_create() + * 4. 对于 FC=SG 的 DA,额外创建一个 FC=SE 的镜像 DA(名称加 "_SE" 后缀) + * + * @param icd ICD 数据 + * @param DOType_item DO 类型定义 + * @param node 父 ModelNode(LN 或父 DO) + * @param do_name DO 名称 + * @param all_do 输出:创建的 all_DO 结构 + * @return 0 成功,-1 失败 + */ LOCAL int model_dataobject_create(stru_icd &icd, stru_DOType &DOType_item, ModelNode *node, std::string do_name, stru_all_DO &all_do) { - if(do_name.empty() || NULL == node) + if(do_name.empty() || NULL == node) // 参数校验 { LOG_E("do_name or node is NULL"); return -1; } - all_do.DO = DataObject_create(do_name.c_str(), node, 0); + all_do.DO = DataObject_create(do_name.c_str(), node, 0); // 创建 libiec61850 DO 节点 if(NULL == all_do.DO) { LOG_E("Failed to create data object %s", do_name.c_str()); @@ -275,57 +319,55 @@ LOCAL int model_dataobject_create(stru_icd &icd, stru_DOType &DOType_item, Model for(std::vector::iterator it_da_vec = DOType_item.vec_da.begin(); it_da_vec!= DOType_item.vec_da.end(); it_da_vec++) { - if(DOType_item.map_da.find(*it_da_vec) == DOType_item.map_da.end()) + if(DOType_item.map_da.find(*it_da_vec) == DOType_item.map_da.end()) // DA 定义不存在 { LOG_E("DA %s not found in DOType_item", it_da_vec->c_str()); continue; } - stru_DA &da_item = DOType_item.map_da[*it_da_vec]; - if(g_dataAttributeTypeMap.find(da_item.bType) == g_dataAttributeTypeMap.end()) + stru_DA &da_item = DOType_item.map_da[*it_da_vec]; // 取 DA 模板定义 + if(g_dataAttributeTypeMap.find(da_item.bType) == g_dataAttributeTypeMap.end()) // bType 未知 { LOG_E("Data attribute type %s not found in g_dataAttributeTypeMap", da_item.bType.c_str()); continue; } - if(g_functionalConstraintMap.find(da_item.fc) == g_functionalConstraintMap.end()) + if(g_functionalConstraintMap.find(da_item.fc) == g_functionalConstraintMap.end()) // FC 未知 { LOG_E("Functional constraint %s not found in g_functionalConstraintMap", da_item.fc.c_str()); continue; } - stru_all_DA *temp_all_da = new stru_all_DA; + stru_all_DA *temp_all_da = new stru_all_DA; // 分配运行时 DA - DataAttributeType bType = g_dataAttributeTypeMap[da_item.bType]; - FunctionalConstraint fc = g_functionalConstraintMap[da_item.fc]; - int triggerOptions = model_get_triggerOptions(da_item); + DataAttributeType bType = g_dataAttributeTypeMap[da_item.bType]; // bType 字符串→枚举 + FunctionalConstraint fc = g_functionalConstraintMap[da_item.fc]; // FC 字符串→枚举 + int triggerOptions = model_get_triggerOptions(da_item); // 根据 dchg/qchg 生成触发选项 temp_all_da->DA = DataAttribute_create(it_da_vec->c_str(), (ModelNode*) all_do.DO, bType, fc, triggerOptions, 0, NULL); - if(NULL == temp_all_da->DA) + if(NULL == temp_all_da->DA) // 创建失败,释放内存 { LOG_E("Failed to create data attribute %s", it_da_vec->c_str()); delete temp_all_da; continue; } - temp_all_da->bType = da_item.bType; - temp_all_da->type = da_item.type; - temp_all_da->fc = da_item.fc; - temp_all_da->dchg = da_item.dchg; - temp_all_da->qchg = da_item.qchg; - temp_all_da->text = da_item.text; - if(bType == IEC61850_CONSTRUCTED) + temp_all_da->bType = da_item.bType; // 保存基本类型 + temp_all_da->type = da_item.type; // 保存模板类型引用 + temp_all_da->fc = da_item.fc; // 保存功能约束字符串 + temp_all_da->dchg = da_item.dchg; // 保存数据变化标志 + temp_all_da->qchg = da_item.qchg; // 保存品质变化标志 + temp_all_da->text = da_item.text; // 保存描述文本 + if(bType == IEC61850_CONSTRUCTED) // Struct 类型递归展开子 BDA { - model_BDA_dataobject_create(icd, da_item.type, triggerOptions, fc, temp_all_da->DA, *temp_all_da); + model_BDA_dataobject_create(icd, da_item.type, triggerOptions, fc, temp_all_da->DA, *temp_all_da); } - all_do.map_all_da[*it_da_vec] = temp_all_da; + all_do.map_all_da[*it_da_vec] = temp_all_da; // 加入 DA 映射表 - if(fc == IEC61850_FC_SG) + if(fc == IEC61850_FC_SG) // FC=SG 时,额外创建 FC=SE 的镜像 DA(定值编辑缓冲区) { - LOG_I("Creating sub-data object for SG DA %s, do %s, ln %s", it_da_vec->c_str(), all_do.DO->name, all_do.DO->parent->name); - - stru_all_DA *p_all_da = new stru_all_DA; + stru_all_DA *p_all_da = new stru_all_DA; // 分配 SE 镜像 DA p_all_da->DA = DataAttribute_create(it_da_vec->c_str(), (ModelNode*) all_do.DO, bType, IEC61850_FC_SE, triggerOptions, 0, NULL); if(NULL == p_all_da->DA) { @@ -333,148 +375,152 @@ LOCAL int model_dataobject_create(stru_icd &icd, stru_DOType &DOType_item, Model delete p_all_da; continue; } - p_all_da->bType = da_item.bType; - p_all_da->type = da_item.type; - p_all_da->fc = "SE"; - p_all_da->dchg = da_item.dchg; - p_all_da->qchg = da_item.qchg; - p_all_da->text = da_item.text; - if(bType == IEC61850_CONSTRUCTED) + p_all_da->bType = da_item.bType; // 复制基本类型 + p_all_da->type = da_item.type; // 复制模板类型引用 + p_all_da->fc = "SE"; // 功能约束标记为 SE + p_all_da->dchg = da_item.dchg; // 复制数据变化标志 + p_all_da->qchg = da_item.qchg; // 复制品质变化标志 + p_all_da->text = da_item.text; // 复制描述文本 + + if(bType == IEC61850_CONSTRUCTED) // SE 版也递归展开 Struct { - model_BDA_dataobject_create(icd, da_item.type, triggerOptions, IEC61850_FC_SE, p_all_da->DA, *p_all_da); + model_BDA_dataobject_create(icd, da_item.type, triggerOptions, IEC61850_FC_SE, p_all_da->DA, *p_all_da); } - std::string se_name = *it_da_vec + "_SE"; - all_do.map_all_da[se_name] = p_all_da; + std::string se_name = *it_da_vec + "_SE"; // key 加 "_SE" 后缀区分 + all_do.map_all_da[se_name] = p_all_da; // 以 "_SE" 后缀 key 存入映射表 } } return 0; } -/* - * model_LNodeType_get - Gets the logical node type - input: lnType - logical node type - icd - ICD structure - output: lnType_item - logical node type item - return: 0 on success, -1 on failure -*/ +/** + * 从 ICD 中查找指定 id 的 LNodeType + * @param lnType LNodeType id + * @param lnType_item 输出:找到的 LNodeType + * @param icd ICD 数据 + * @return 0 成功,-1 未找到 + */ LOCAL int model_LNodeType_get(std::string lnType, stru_LNodeType &lnType_item, stru_icd &icd) { - if(lnType.empty()) + if(lnType.empty()) // 参数校验 { LOG_E("lnType is empty"); return -1; } - if(icd.map_lnType.find(lnType) == icd.map_lnType.end()) + if(icd.map_lnType.find(lnType) == icd.map_lnType.end()) // LNodeType id 未在 ICD 中定义 { LOG_E("lnType %s not found in ICD", lnType.c_str()); return -1; } - lnType_item = icd.map_lnType[lnType]; + lnType_item = icd.map_lnType[lnType]; // 拷贝 LNodeType 定义到输出参数 return 0; } -/* - * model_DOType_get - Gets the data object type - input: doType - data object type - icd - ICD structure - output: DOType_item - data object type item - return: 0 on success, -1 on failure -*/ +/** + * 从 ICD 中查找指定 id 的 DOType + * @param doType DOType id + * @param DOType_item 输出:找到的 DOType + * @param icd ICD 数据 + * @return 0 成功,-1 未找到 + */ LOCAL int model_DOType_get(std::string doType, stru_DOType &DOType_item, stru_icd &icd) { - if(doType.empty()) + if(doType.empty()) // 参数校验 { LOG_E("doType is empty"); return -1; } - if(icd.map_doType.find(doType) == icd.map_doType.end()) + if(icd.map_doType.find(doType) == icd.map_doType.end()) // DOType id 未在 ICD 中定义 { LOG_E("doType %s not found in ICD", doType.c_str()); return -1; } - - DOType_item = icd.map_doType[doType]; + + DOType_item = icd.map_doType[doType]; // 拷贝 DOType 定义到输出参数 return 0; } -/* - * model_DAType_get - Gets the data attribute type - input: daType - data attribute type - icd - ICD structure - output: daType_item - data attribute type item - return: 0 on success, -1 on failure -*/ +/** + * 从 ICD 中查找指定 id 的 DAType + * @param daType DAType id + * @param daType_item 输出:找到的 DAType + * @param icd ICD 数据 + * @return 0 成功,-1 未找到 + */ LOCAL int model_DAType_get(std::string daType, stru_DAType &daType_item, stru_icd &icd) { - if(daType.empty()) + if(daType.empty()) // 参数校验 { LOG_E("daType is empty"); return -1; } - if(icd.map_daType.find(daType) == icd.map_daType.end()) + if(icd.map_daType.find(daType) == icd.map_daType.end()) // DAType id 未在 ICD 中定义 { LOG_E("daType %s not found in ICD", daType.c_str()); return -1; } - daType_item = icd.map_daType[daType]; + daType_item = icd.map_daType[daType]; // 拷贝 DAType 定义到输出参数 return 0; } -/* - * model_EnumType_get - Gets the enumeration type - input: enumType - enumeration type - icd - ICD structure - output: enumType_item - enumeration type item - return: 0 on success, -1 on failure -*/ +/** + * 从 ICD 中查找指定 id 的 EnumType + * @param enumType EnumType id + * @param enumType_item 输出:找到的 EnumType + * @param icd ICD 数据 + * @return 0 成功,-1 未找到 + */ LOCAL int model_EnumType_get(std::string enumType, stru_EnumType &enumType_item, stru_icd &icd) { - if(enumType.empty()) + if(enumType.empty()) // 参数校验 { LOG_E("enumType is empty"); return -1; } - if(icd.map_enumType.find(enumType) == icd.map_enumType.end()) + if(icd.map_enumType.find(enumType) == icd.map_enumType.end()) // EnumType id 未在 ICD 中定义 { LOG_E("enumType %s not found in ICD", enumType.c_str()); return -1; } - enumType_item = icd.map_enumType[enumType]; + enumType_item = icd.map_enumType[enumType]; // 拷贝 EnumType 定义到输出参数 return 0; } -/* - * model_dataobject_init - Initializes the data objects - input: ln - logical node - icd - ICD structure - lnType - logical node type - output: map_all_do - map of all data objects - return: 0 on success, -1 on failure -*/ +/** + * 为一个逻辑节点初始化其所有 DO/SDO/DA + * + * 根据 LN 的 lnType,查找 LNodeType 模板,遍历其所有 DO,创建运行时结构。 + * + * @param ln libiec61850 LogicalNode 指针 + * @param icd ICD 数据 + * @param lnType LNodeType id + * @param map_all_do 输出:DO 到运行时 all_DO 结构的映射 + * @return 0 成功,-1 失败 + */ LOCAL int model_dataobject_init(LogicalNode *ln, stru_icd &icd, std::string &lnType, std::map &map_all_do) { - if(NULL == ln) + if(NULL == ln) // 参数校验 { LOG_E("ln is NULL"); return -1; } stru_LNodeType lnType_item; - if(0 != model_LNodeType_get(lnType, lnType_item, icd)) + if(0 != model_LNodeType_get(lnType, lnType_item, icd)) // 查找 LNodeType 模板 { LOG_E("Failed to get lnType %s", lnType.c_str()); return -1; @@ -482,220 +528,231 @@ LOCAL int model_dataobject_init(LogicalNode *ln, stru_icd &icd, std::string &lnT for(std::vector::iterator it_do_vec = lnType_item.vec_do.begin(); it_do_vec != lnType_item.vec_do.end(); it_do_vec++) { - if(lnType_item.map_do.find(*it_do_vec) == lnType_item.map_do.end()) + if(lnType_item.map_do.find(*it_do_vec) == lnType_item.map_do.end()) // DO 定义不存在 { LOG_E("DO %s not found in lnType %s", it_do_vec->c_str(), lnType.c_str()); continue; } - - stru_DO &DO_item = lnType_item.map_do[*it_do_vec]; + + stru_DO &DO_item = lnType_item.map_do[*it_do_vec]; // 取 DO 定义(含 type 引用) stru_DOType DOType_item; - if(0 != model_DOType_get(DO_item.type, DOType_item, icd)) + if(0 != model_DOType_get(DO_item.type, DOType_item, icd)) // 查找 DOType 模板 { LOG_E("Failed to get DOType %s", DO_item.type.c_str()); continue; } - stru_all_DO *temp_all_do = new stru_all_DO; - - model_dataobject_create(icd, DOType_item, (ModelNode *)ln, *it_do_vec, *temp_all_do); + stru_all_DO *temp_all_do = new stru_all_DO; // 分配运行时 DO + + model_dataobject_create(icd, DOType_item, (ModelNode *)ln, *it_do_vec, *temp_all_do); // 创建 DO 及所有 DA for(std::map::iterator it_sdo = DOType_item.map_sdo.begin(); it_sdo != DOType_item.map_sdo.end(); it_sdo++) { stru_DOType SDOType_item; - if(0 != model_DOType_get(it_sdo->second, SDOType_item, icd)) + if(0 != model_DOType_get(it_sdo->second, SDOType_item, icd)) // 查找子 DO 的 DOType { LOG_E("Failed to get SDOType %s", it_sdo->second.c_str()); continue; } - stru_all_DO *temp_all_sdo = new stru_all_DO; + stru_all_DO *temp_all_sdo = new stru_all_DO; // 分配运行时子 DO - model_dataobject_create(icd, SDOType_item, (ModelNode *)temp_all_do->DO, it_sdo->first, *temp_all_sdo); + model_dataobject_create(icd, SDOType_item, (ModelNode *)temp_all_do->DO, it_sdo->first, *temp_all_sdo); // 以父 DO 为 parent 创建子 DO - temp_all_do->map_all_sdo[it_sdo->first] = temp_all_sdo; + temp_all_do->map_all_sdo[it_sdo->first] = temp_all_sdo; // 子 DO 加入父 DO 的 SDO 映射表 } - map_all_do[*it_do_vec] = temp_all_do; + map_all_do[*it_do_vec] = temp_all_do; // DO 加入 LN 的映射表 } return 0; } -/* - * model_DataSet_init - Initializes the dataset - input: LLN0 - LLN0 node - icd - ICD structure - map_dataset - map of datasets - output: - return: 0 on success, -1 on failure -*/ +/** + * 初始化数据集(DataSet) + * + * 为每个 DataSet 创建 DataSet 对象,并为其每个 FCDA 创建 DataSetEntry。 + * FCDA 的变量名格式为:{prefix}{lnClass}{lnInst}${fc}${doName}${daName} + * + * @param LLN0 LLN0 LogicalNode 指针 + * @param icd ICD 数据 + * @param map_dataset 数据集映射表 + * @param vec_dataset 数据集名称列表 + * @return 0 成功,-1 失败 + */ LOCAL int model_DataSet_init(LogicalNode *LLN0, stru_icd &icd, std::map &map_dataset, std::vector &vec_dataset) { - if(NULL == LLN0) + if(NULL == LLN0) // 参数校验 { LOG_E("LLN0 is NULL"); return -1; } std::vector::iterator it_dataset; - for(it_dataset = vec_dataset.begin(); it_dataset != vec_dataset.end(); it_dataset++) + for(it_dataset = vec_dataset.begin(); it_dataset != vec_dataset.end(); it_dataset++) // 遍历所有数据集名称 { - // LOG_I(" Initializing dataset %s", it_dataset->c_str()); - std::map::iterator it_dataset_item = map_dataset.find(*it_dataset); - if(it_dataset_item == map_dataset.end()) + if(it_dataset_item == map_dataset.end()) // 数据集不存在 { LOG_E("Failed to find dataset %s", it_dataset->c_str()); continue; } - + stru_DataSet *p_dataset = &it_dataset_item->second; - p_dataset->dataset = DataSet_create(it_dataset->c_str(), LLN0); + p_dataset->dataset = DataSet_create(it_dataset->c_str(), LLN0); // 创建 libiec61850 DataSet if(NULL == p_dataset->dataset) { LOG_E("Failed to create dataset %s", it_dataset->c_str()); continue; } - // LOG_I(" Created dataset %p", p_dataset->dataset); std::vector::iterator it_fcda; - for(it_fcda = p_dataset->vec_fcda.begin(); it_fcda != p_dataset->vec_fcda.end(); it_fcda++) + for(it_fcda = p_dataset->vec_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; - - for(int i = 0; i < variable.size(); i++) + it_fcda->fc + "$" + it_fcda->doName + "$" + it_fcda->daName; // 拼装 FCDA 变量名 + + for(int i = 0; i < variable.size(); i++) // 将变量名中所有 '.' 替换为 '$' { if(variable[i] == '.') { variable[i] = '$'; } } - - if(NULL == (it_fcda->fcda = DataSetEntry_create(p_dataset->dataset, variable.c_str(), -1, NULL))) + + if(NULL == (it_fcda->fcda = DataSetEntry_create(p_dataset->dataset, variable.c_str(), -1, NULL))) // 创建数据集条目 { LOG_E(" Failed to create dataset entry %s", variable.c_str()); } - // LOG_I(" Initializing dataset entry %s, %p", variable.c_str(), it_fcda->fcda); } } return 0; } -/* - * model_ReportControlBlock_init - Initializes the report control block - input: LLN0 - LLN0 node - icd - ICD structure - map_rcb - map of report control blocks - output: - return: 0 on success, -1 on failure -*/ +/** + * 初始化报告控制块(ReportControlBlock) + * + * 为 LLN0 下的每个 ReportControl 创建对应的 RCB 对象。 + * + * @param LLN0 LLN0 LogicalNode 指针 + * @param icd ICD 数据 + * @param map_rcb 报告控制块映射表 + * @param vec_rcb 报告控制块名称列表 + * @return 0 成功,-1 失败 + */ LOCAL int model_ReportControlBlock_init(LogicalNode *LLN0, stru_icd &icd, std::map &map_rcb, std::vector &vec_rcb) { - if(NULL == LLN0) + if(NULL == LLN0) // 参数校验 { LOG_E("LLN0 is NULL"); return -1; } - - for(std::vector::iterator it_rcb = vec_rcb.begin(); it_rcb != vec_rcb.end(); it_rcb++) + + for(std::vector::iterator it_rcb = vec_rcb.begin(); it_rcb != vec_rcb.end(); it_rcb++) // 遍历所有报告控制块名称 { - // LOG_I(" Initializing report control block %s", it_rcb->c_str()); std::map::iterator it_rcb_item = map_rcb.find(*it_rcb); - if(it_rcb_item == map_rcb.end()) + if(it_rcb_item == map_rcb.end()) // 报告控制块不存在 { LOG_E("Failed to find report control block %s", it_rcb->c_str()); continue; } - stru_ReportControl &rcb = it_rcb_item->second; - - int Options = model_get_rpt_options(rcb.optFields); - int trgOps = model_get_rpt_trgOps(rcb.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); + stru_ReportControl &rcb = it_rcb_item->second; // 取报告控制块定义 + + 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 } return 0; } -/* - * model_ln0_init - Initializes the LLN0 - input: ld_model - logical device model - icd - ICD structure - ld - logical device structure - output: - return: 0 on success, -1 on failure -*/ +/** + * 初始化 LLN0(逻辑节点零) + * + * 顺序: + * 1. 创建 LLN0 LogicalNode + * 2. 初始化所有 DO/SDO/DA + * 3. 初始化所有数据集 + * 4. 初始化所有报告控制块 + * 5. 创建定值控制块(SGCB) + * + * @param ld_model libiec61850 LogicalDevice 指针 + * @param icd ICD 数据 + * @param ld 逻辑设备数据 + * @return 0 成功,-1 失败 + */ LOCAL int model_ln0_init(LogicalDevice *ld_model, stru_icd &icd, stru_LDevice &ld) { - if(NULL == ld_model) + if(NULL == ld_model) // 参数校验 { LOG_E("ld_model is NULL"); return -1; } - - ld.ln0.LLN0 = LogicalNode_create(ld.ln0.lnClass.c_str(), ld_model); + + ld.ln0.LLN0 = LogicalNode_create(ld.ln0.lnClass.c_str(), ld_model); // 以 lnClass(通常为 "LLN0")创建 LogicalNode if(NULL == ld.ln0.LLN0) { LOG_E("Failed to create LLN0"); return -1; } - if(0 != model_dataobject_init(ld.ln0.LLN0, icd, ld.ln0.lnType, ld.ln0.map_all_do)) + if(0 != model_dataobject_init(ld.ln0.LLN0, icd, ld.ln0.lnType, ld.ln0.map_all_do)) // 初始化 LLN0 下所有 DO/SDO/DA { LOG_E("Failed to init data objects for LLN0"); return -1; } - if(0 != model_DataSet_init(ld.ln0.LLN0, icd, ld.ln0.map_dataset, ld.ln0.vec_dataset)) + if(0 != model_DataSet_init(ld.ln0.LLN0, icd, ld.ln0.map_dataset, ld.ln0.vec_dataset)) // 初始化所有数据集 { LOG_E("Failed to init datasets for LLN0"); return -1; } - if(0 != model_ReportControlBlock_init(ld.ln0.LLN0, icd, ld.ln0.map_reportControl, ld.ln0.vec_reportControl)) + if(0 != model_ReportControlBlock_init(ld.ln0.LLN0, icd, ld.ln0.map_reportControl, ld.ln0.vec_reportControl)) // 初始化所有报告控制块 { LOG_E("Failed to init RCBs for LLN0"); return -1; } stru_SettingControl &settingControl = ld.ln0.settingControl; - ld.sgcb = SettingGroupControlBlock_create(ld.ln0.LLN0, settingControl.actSG, settingControl.numOfSGs); + ld.sgcb = SettingGroupControlBlock_create(ld.ln0.LLN0, settingControl.actSG, settingControl.numOfSGs); // 创建定值控制块(SGCB),设置激活定值区和定值区总数 return 0; } -/* - * model_LNodes_init - Initializes the logical nodes - input: icd - ICD structure - ld - logical device structure - ld_model - logical device model - output: - return: 0 on success, -1 on failure -*/ +/** + * 初始化逻辑设备下的所有普通逻辑节点 + * + * 每个 LN 的名称为 {prefix}{lnClass}{inst}。 + * + * @param icd ICD 数据 + * @param ld 逻辑设备数据 + * @param ld_model libiec61850 LogicalDevice 指针 + * @return 0 成功,-1 失败 + */ LOCAL int model_LNodes_init(stru_icd &icd, stru_LDevice &ld, LogicalDevice *ld_model) { - if(NULL == ld_model) + if(NULL == ld_model) // 参数校验 { LOG_E("ld_model is NULL"); return -1; } std::vector::iterator it_ln; - for(it_ln = ld.vec_ln.begin(); it_ln!= ld.vec_ln.end(); it_ln++) + for(it_ln = ld.vec_ln.begin(); it_ln!= ld.vec_ln.end(); it_ln++) // 遍历逻辑设备下所有 LN { - std::string ln_name = it_ln->prefix + it_ln->lnClass + it_ln->inst; - it_ln->LN = LogicalNode_create(ln_name.c_str(), ld_model); + std::string ln_name = it_ln->prefix + it_ln->lnClass + it_ln->inst; // 拼装 LN 完整名称 + it_ln->LN = LogicalNode_create(ln_name.c_str(), ld_model); // 创建 LogicalNode if(NULL == it_ln->LN) { LOG_E("Failed to create logical node %s", ln_name.c_str()); return -1; } - if(0 != model_dataobject_init(it_ln->LN, icd, it_ln->lnType, it_ln->map_all_do)) + if(0 != model_dataobject_init(it_ln->LN, icd, it_ln->lnType, it_ln->map_all_do)) // 初始化 LN 下所有 DO/SDO/DA { LOG_E("Failed to init data objects for logical node %s", ln_name.c_str()); return -1; @@ -705,16 +762,18 @@ LOCAL int model_LNodes_init(stru_icd &icd, stru_LDevice &ld, LogicalDevice *ld_m return 0; } -/* - * model_ldevice_init - Initializes the logical device - input: model - IED model - icd - ICD structure - output: - return: 0 on success, -1 on failure -*/ +/** + * 初始化所有逻辑设备 + * + * 遍历 IED.vec_ldevice,为每个逻辑设备调用 model_ln0_init() 和 model_LNodes_init()。 + * + * @param model libiec61850 IedModel 指针 + * @param icd ICD 数据 + * @return 0 成功,-1 失败 + */ LOCAL int model_ldevice_init(IedModel *model, stru_icd &icd) { - if(NULL == model) + if(NULL == model) // 参数校验 { LOG_E("model is NULL"); return -1; @@ -722,49 +781,67 @@ LOCAL int model_ldevice_init(IedModel *model, stru_icd &icd) stru_ied &ied = icd.ied; std::vector::iterator it_ld; - for(it_ld = ied.vec_ldevice.begin(); it_ld!= ied.vec_ldevice.end(); it_ld++) + for(it_ld = ied.vec_ldevice.begin(); it_ld!= ied.vec_ldevice.end(); it_ld++) // 遍历所有逻辑设备名称 { - // LOG_I("Initializing logical device %s", it_ld->c_str()); std::map::iterator it_ldevice = icd.ied.map_ldevice.find(*it_ld); - if(it_ldevice == icd.ied.map_ldevice.end()) + if(it_ldevice == icd.ied.map_ldevice.end()) // 逻辑设备不存在 { LOG_E("Failed to find logical device %s", it_ld->c_str()); continue; } stru_LDevice *p_ldevice = &it_ldevice->second; - p_ldevice->Ldevice = LogicalDevice_create(it_ld->c_str(), model); + p_ldevice->Ldevice = LogicalDevice_create(it_ld->c_str(), model); // 创建 LogicalDevice 并挂到 IedModel 下 if(NULL == p_ldevice->Ldevice) { LOG_E("Failed to create logical device %s", it_ld->c_str()); return -1; } - if(0 != model_ln0_init(p_ldevice->Ldevice, icd, *p_ldevice)) + if(0 != model_ln0_init(p_ldevice->Ldevice, icd, *p_ldevice)) // 初始化 LLN0(数据集、报告控制、定值控制) { LOG_E("Failed to create LLN0 for logical device %s", it_ld->c_str()); return -1; } - model_LNodes_init(icd, *p_ldevice, p_ldevice->Ldevice); + model_LNodes_init(icd, *p_ldevice, p_ldevice->Ldevice); // 初始化普通 LN } return 0; } -void model_sync_da_default_values(stru_icd &icd, stru_SDI &SDI_item, stru_all_DA *p_all_da) +/** + * 将 SDI 中的 DAI 默认值(sAddr / val)同步到模型树中的 all_DA 节点 + * + * 递归处理 SDI 的嵌套结构,为每个 DA 节点设置 sAddr 和 val, + * 同时建立 vec_saddr 和 map_saddr_point 映射。 + * + * FC=SG 的 DA 会将 sAddr 后缀加上 "_SG",FC=SE 的 DA 加 "_SE"。 + * + * @param icd ICD 数据 + * @param SDI_item SDI 实例 + * @param p_all_da all_DA 节点(当前层) + */ +void model_sync_da_default_values(stru_icd &icd, stru_SDI &SDI_item, stru_all_DA *p_all_da, int indent) { - if(NULL == p_all_da) + if(NULL == p_all_da) // 参数校验 { LOG_E("p_all_da is NULL"); return; } + // for(auto it : p_all_da->map_all_da) // 调试:打印当前层所有 DA 及其 sAddr + // { + // LOG_I("%sDA %s, sAddr %s", printf_(indent+1).c_str(), it.first.c_str(), it.second->sAddr.c_str()); + // } + for(std::map::iterator it_sdi = SDI_item.map_sdI.begin(); it_sdi != SDI_item.map_sdI.end(); it_sdi++) { - if(p_all_da->map_all_da.find(it_sdi->first) != p_all_da->map_all_da.end()) + // LOG_I("%sSDI %s", printf_(indent+1).c_str(), it_sdi->first.c_str()); + + if(p_all_da->map_all_da.find(it_sdi->first) != p_all_da->map_all_da.end()) // SDI 名称在当前层 DA 映射中存在(Struct 类型) { - stru_all_DA *p_all_da_temp = p_all_da->map_all_da[it_sdi->first]; + stru_all_DA *p_all_da_temp = p_all_da->map_all_da[it_sdi->first]; // 取 Struct DA 节点 stru_SDI &SDI_item = it_sdi->second; if(NULL == p_all_da_temp) @@ -772,53 +849,55 @@ void model_sync_da_default_values(stru_icd &icd, stru_SDI &SDI_item, stru_all_DA LOG_E("p_all_da is NULL"); continue; } - - model_sync_da_default_values(icd, SDI_item, p_all_da_temp); + + model_sync_da_default_values(icd, SDI_item, p_all_da_temp, indent+1); // 递归处理 Struct 子层 } } for(std::map::iterator it_daI = SDI_item.map_daI.begin(); it_daI != SDI_item.map_daI.end(); it_daI++) { - stru_DAI &DAI_item = it_daI->second; + // LOG_I("%sDAI %s", printf_(indent+1).c_str(), it_daI->first.c_str()); - if(p_all_da->map_all_da.find(it_daI->first) != p_all_da->map_all_da.end()) + stru_DAI &DAI_item = it_daI->second; // 取 DAI 定义(含 sAddr 和 val) + + if(p_all_da->map_all_da.find(it_daI->first) != p_all_da->map_all_da.end()) // DAI 名称在当前层 DA 映射中存在 { stru_all_DA *p_all_da_temp = p_all_da->map_all_da[it_daI->first]; - + if(p_all_da_temp) { - p_all_da_temp->sAddr = DAI_item.sAddr; - p_all_da_temp->val = DAI_item.val; + p_all_da_temp->sAddr = DAI_item.sAddr; // 同步 sAddr + p_all_da_temp->val = DAI_item.val; // 同步默认值 - if(!p_all_da_temp->sAddr.empty()) + if(!p_all_da_temp->sAddr.empty()) // 有 sAddr 才建立映射 { std::string saddr = p_all_da_temp->sAddr; - if(p_all_da_temp->DA->fc == IEC61850_FC_SG) + if(p_all_da_temp->DA->fc == IEC61850_FC_SG) // FC=SG 的 DA,sAddr 加 "_SG" 后缀 { saddr = saddr + "_SG"; } - p_all_da_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_temp->DA; + p_all_da_temp->DA->sAddr = StringUtils_copyString(saddr.c_str()); // 将 sAddr 写入 libiec61850 模型节点 + icd.ied.vec_saddr.push_back(saddr); // 加入 sAddr 列表 + icd.ied.map_saddr_point[saddr].node = (ModelNode *)p_all_da_temp->DA; // 建立 sAddr → 模型节点映射 } } } - if(p_all_da->map_all_da.find(it_daI->first + "_SE") != p_all_da->map_all_da.end()) + if(p_all_da->map_all_da.find(it_daI->first + "_SE") != p_all_da->map_all_da.end()) // 同时处理 SE 镜像 DA { stru_all_DA *p_all_da_se_temp = p_all_da->map_all_da[it_daI->first + "_SE"]; if(p_all_da_se_temp) { - p_all_da_se_temp->sAddr = DAI_item.sAddr; - p_all_da_se_temp->val = DAI_item.val; + p_all_da_se_temp->sAddr = DAI_item.sAddr; // 同步 sAddr + p_all_da_se_temp->val = DAI_item.val; // 同步默认值 if(!p_all_da_se_temp->sAddr.empty()) { std::string saddr = p_all_da_se_temp->sAddr; - if(p_all_da_se_temp->DA->fc == IEC61850_FC_SE) + if(p_all_da_se_temp->DA->fc == IEC61850_FC_SE) // FC=SE 的 DA,sAddr 加 "_SE" 后缀 { saddr = saddr + "_SE"; } @@ -832,9 +911,18 @@ void model_sync_da_default_values(stru_icd &icd, stru_SDI &SDI_item, stru_all_DA } } -void model_sync_SDO_default_values(stru_icd &icd, stru_SDI &map_sdi, stru_all_DO *map_all_do) +/** + * 同步 DOI 中 SDO 下的默认值到模型 + * + * 遍历 DOI.map_sdI,匹配 all_DO 中的 map_all_sdo 和 map_all_da。 + * + * @param icd ICD 数据 + * @param map_sdi SDI 映射 + * @param map_all_do all_DO 节点 + */ +void model_sync_SDO_default_values(stru_icd &icd, stru_SDI &map_sdi, stru_all_DO *map_all_do, int indent) { - if(NULL == map_all_do) + if(NULL == map_all_do) // 参数校验 { LOG_E("map_all_do is NULL"); return; @@ -842,9 +930,11 @@ void model_sync_SDO_default_values(stru_icd &icd, stru_SDI &map_sdi, stru_all_DO for(std::map::iterator it_sdi = map_sdi.map_sdI.begin(); it_sdi != map_sdi.map_sdI.end(); it_sdi++) { - if(map_all_do->map_all_sdo.find(it_sdi->first) != map_all_do->map_all_sdo.end()) + // LOG_I("%sSDI %s", printf_(indent+1).c_str(), it_sdi->first.c_str()); + + if(map_all_do->map_all_sdo.find(it_sdi->first) != map_all_do->map_all_sdo.end()) // SDI 名称匹配到子 DO { - stru_all_DO *p_all_sdo = map_all_do->map_all_sdo[it_sdi->first]; + stru_all_DO *p_all_sdo = map_all_do->map_all_sdo[it_sdi->first]; // 取子 DO 节点 stru_SDI &SDI_item = it_sdi->second; if(NULL == p_all_sdo) @@ -852,13 +942,13 @@ void model_sync_SDO_default_values(stru_icd &icd, stru_SDI &map_sdi, stru_all_DO LOG_E("p_all_sdo is NULL"); continue; } - - model_sync_SDO_default_values(icd, SDI_item, p_all_sdo); + + model_sync_SDO_default_values(icd, SDI_item, p_all_sdo, indent+1); // 递归处理子 DO 的 SDI } - if(map_all_do->map_all_da.find(it_sdi->first) != map_all_do->map_all_da.end()) + if(map_all_do->map_all_da.find(it_sdi->first) != map_all_do->map_all_da.end()) // SDI 名称匹配到 Struct DA { - stru_all_DA *p_all_da = map_all_do->map_all_da[it_sdi->first]; + stru_all_DA *p_all_da = map_all_do->map_all_da[it_sdi->first]; // 取 Struct DA 节点 stru_SDI &SDI_item = it_sdi->second; if(NULL == p_all_da) @@ -866,65 +956,86 @@ void model_sync_SDO_default_values(stru_icd &icd, stru_SDI &map_sdi, stru_all_DO LOG_E("p_all_da is NULL"); continue; } - model_sync_da_default_values(icd, SDI_item, p_all_da); + model_sync_da_default_values(icd, SDI_item, p_all_da, indent+1); // 递归处理 Struct DA 下各子 DA } } } +/** + * 同步一个 LN(LN0 或普通 LN)中 DOI 的默认值到 all_DA 节点 + * + * @param icd ICD 数据 + * @param map_doI DOI 映射表 + * @param map_all_do all_DO 映射表 + */ void model_sync_ln_default_values(stru_icd &icd, std::map &map_doI, std::map &map_all_do) { + // for(auto it : map_all_do) + // { + // LOG_I("%sall_DO %s", printf_(2).c_str(), it.first.c_str()); + // } + for(std::map::iterator it_doI = map_doI.begin(); it_doI != map_doI.end(); it_doI++) { - if(map_all_do.find(it_doI->first) == map_all_do.end()) + // LOG_I("%sDO %s", printf_(2).c_str(), it_doI->first.c_str()); + + if(map_all_do.find(it_doI->first) == map_all_do.end()) // DOI 对应的运行时 DO 不存在 { LOG_E("DO %s not found in map_all_do", it_doI->first.c_str()); continue; } - - stru_DOI &DOI_item = it_doI->second; - stru_all_DO *p_all_do = map_all_do[it_doI->first]; + + stru_DOI &DOI_item = it_doI->second; // 取 DOI 定义 + stru_all_DO *p_all_do = map_all_do[it_doI->first]; // 取运行时 DO + + // for(auto it : p_all_do->map_all_da) + // { + // LOG_I("%sall_DA %s", printf_(3).c_str(), it.first.c_str()); + // } for(std::map::iterator it_daI = DOI_item.map_daI.begin(); it_daI != DOI_item.map_daI.end(); it_daI++) { - stru_DAI &DAI_item = it_daI->second; + // LOG_I("%sDA %s", printf_(3).c_str(), it_daI->first.c_str()); - if(p_all_do->map_all_da.find(it_daI->first) != p_all_do->map_all_da.end()) + stru_DAI &DAI_item = it_daI->second; // 取 DAI 定义 + + if(p_all_do->map_all_da.find(it_daI->first) != p_all_do->map_all_da.end()) // DAI 名称匹配到 DA { stru_all_DA *p_all_da = p_all_do->map_all_da[it_daI->first]; if(p_all_da) { - p_all_da->sAddr = DAI_item.sAddr; - p_all_da->val = DAI_item.val; + p_all_da->sAddr = DAI_item.sAddr; // 同步 sAddr + p_all_da->val = DAI_item.val; // 同步默认值 - if(!p_all_da->sAddr.empty()) + if(!p_all_da->sAddr.empty()) // 有 sAddr 才建立映射 { std::string saddr = p_all_da->sAddr; - if(p_all_da->DA->fc == IEC61850_FC_SG) + if(p_all_da->DA->fc == IEC61850_FC_SG) // SG 加后缀 { saddr = saddr + "_SG"; } - 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; + 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; // 建立映射 } } } - if(p_all_do->map_all_da.find(it_daI->first + "_SE") != p_all_do->map_all_da.end()) + if(p_all_do->map_all_da.find(it_daI->first + "_SE") != p_all_do->map_all_da.end()) // 处理 SE 镜像 DA { stru_all_DA *p_all_da_se = p_all_do->map_all_da[it_daI->first + "_SE"]; if(p_all_da_se) { - p_all_da_se->sAddr = DAI_item.sAddr; - p_all_da_se->val = DAI_item.val; + p_all_da_se->sAddr = DAI_item.sAddr; // 同步 sAddr + p_all_da_se->val = DAI_item.val; // 同步默认值 if(!p_all_da_se->sAddr.empty()) { std::string saddr = p_all_da_se->sAddr; - if(p_all_da_se->DA->fc == IEC61850_FC_SE) + if(p_all_da_se->DA->fc == IEC61850_FC_SE) // SE 加后缀 { saddr = saddr + "_SE"; } @@ -939,7 +1050,9 @@ void model_sync_ln_default_values(stru_icd &icd, std::map for(std::map::iterator it_sdi = DOI_item.map_sdI.begin(); it_sdi != DOI_item.map_sdI.end(); it_sdi++) { - if(p_all_do->map_all_sdo.find(it_sdi->first) != p_all_do->map_all_sdo.end()) + // LOG_I("%sSDI %s", printf_(3).c_str(), it_sdi->first.c_str()); + + if(p_all_do->map_all_sdo.find(it_sdi->first) != p_all_do->map_all_sdo.end()) // SDI 名称匹配到子 DO { stru_all_DO *p_all_sdo = p_all_do->map_all_sdo[it_sdi->first]; stru_SDI &SDI_item = it_sdi->second; @@ -950,10 +1063,10 @@ void model_sync_ln_default_values(stru_icd &icd, std::map continue; } - model_sync_SDO_default_values(icd, SDI_item, p_all_sdo); + model_sync_SDO_default_values(icd, SDI_item, p_all_sdo, 3); // 递归处理子 DO } - if(p_all_do->map_all_da.find(it_sdi->first) != p_all_do->map_all_da.end()) + if(p_all_do->map_all_da.find(it_sdi->first) != p_all_do->map_all_da.end()) // SDI 名称匹配到 Struct DA { stru_all_DA *p_all_da = p_all_do->map_all_da[it_sdi->first]; stru_SDI &SDI_item = it_sdi->second; @@ -964,200 +1077,259 @@ void model_sync_ln_default_values(stru_icd &icd, std::map continue; } - model_sync_da_default_values(icd, SDI_item, p_all_da); + model_sync_da_default_values(icd, SDI_item, p_all_da, 3); // 递归处理 Struct DA + } + + if(p_all_do->map_all_da.find(it_sdi->first + "_SE") != p_all_do->map_all_da.end()) // 处理 SE 镜像 DA + { + stru_all_DA *p_all_da_se = p_all_do->map_all_da[it_sdi->first + "_SE"]; + stru_SDI &SDI_item = it_sdi->second; + + if(NULL == p_all_da_se) + { + LOG_E("p_all_da_se is NULL"); + continue; + } + + model_sync_da_default_values(icd, SDI_item, p_all_da_se, 3); // 递归处理 SE DA } } } } +/** + * 遍历所有逻辑设备,同步 IED 的默认值到模型 + * + * 对每个逻辑设备的 LLN0 和所有普通 LN,调用 model_sync_ln_default_values() + * 将 ICD 中 DAI 的 sAddr 和 val 同步到运行时模型节点。 + * + * @param icd ICD 数据 + */ void model_sync_ied_default_values(stru_icd &icd) { for(std::map::iterator it_ld = icd.ied.map_ldevice.begin(); it_ld!= icd.ied.map_ldevice.end(); it_ld++) { - model_sync_ln_default_values(icd, it_ld->second.ln0.map_doI, it_ld->second.ln0.map_all_do); + // LOG_I("%sLDevice %s", printf_(0).c_str(), it_ld->first.c_str()); + // LOG_I("%sLLN0", printf_(1).c_str()); + model_sync_ln_default_values(icd, it_ld->second.ln0.map_doI, it_ld->second.ln0.map_all_do); // 同步 LLN0 的默认值 for(std::vector::iterator it_ln = it_ld->second.vec_ln.begin(); it_ln != it_ld->second.vec_ln.end(); it_ln++) { - model_sync_ln_default_values(icd, it_ln->map_doI, it_ln->map_all_do); + // LOG_I("%sLN %s%s", printf_(1).c_str(), it_ln->lnClass.c_str(), it_ln->inst.c_str()); + model_sync_ln_default_values(icd, it_ln->map_doI, it_ln->map_all_do); // 同步每个 LN 的默认值 } } } -// void model_ied_saddr_point_init(stru_icd &icd) -// { -// for(std::vector::iterator it_saddr = icd.ied.vec_saddr.begin(); it_saddr != icd.ied.vec_saddr.end(); it_saddr++) -// { -// icd.ied.map_saddr_point[*it_saddr].node = IedModel_getModelNodeByShortAddress(icd.ied.model, it_saddr->c_str()); -// if(NULL == icd.ied.map_saddr_point[*it_saddr].node) -// { -// LOG_E("Failed to find node for sAddr %s", it_saddr->c_str()); -// continue; -// } -// } -// } - - +/** + * 递归搜索 DO 树中所有包含 ctlModel DA 的控制 DO + * + * 检查每个 all_DA,若其名称为 "ctlModel",则将该 DO 标记为控制类型, + * 添加到 icd.ied.vec_control_do 中。 + * + * @param icd ICD 数据 + * @param in_control_do 当前控制 DO 的定位信息 + * @param p_do 当前遍历的 all_DO 节点 + */ void model_search_ln_control_DataObjects(stru_icd &icd, stru_contorl_DO &in_control_do, stru_all_DO *p_do) { - if(NULL == p_do) + if(NULL == p_do) // 参数校验 { LOG_E("p_do is NULL"); return; } - stru_contorl_DO control_do = in_control_do; + stru_contorl_DO control_do = in_control_do; // 拷贝控制 DO 定位信息(ldevice_inst/ln_class/ln_inst/do_name) for(std::map::iterator it_do = p_do->map_all_sdo.begin(); it_do != p_do->map_all_sdo.end(); it_do++) { - model_search_ln_control_DataObjects(icd, control_do, it_do->second); + model_search_ln_control_DataObjects(icd, control_do, it_do->second); // 递归搜索子 SDO } for(std::map::iterator it_da = p_do->map_all_da.begin(); it_da != p_do->map_all_da.end(); it_da++) { - if(it_da->first.compare("ctlModel") == 0) + if(it_da->first.compare("ctlModel") == 0) // 发现 ctlModel 属性 → 该 DO 为控制对象 { - control_do.p_all_do = p_do; - icd.ied.vec_control_do.push_back(control_do); + control_do.p_all_do = p_do; // 记录运行时 DO 指针 + icd.ied.vec_control_do.push_back(control_do); // 加入控制 DO 列表 } } } +/** + * 遍历所有逻辑设备的 LN0 和 LN,搜索控制 DO + * + * 控制 DO 的识别依据:其下存在名为 "ctlModel" 的 DA。 + * 搜索结果存入 icd.ied.vec_control_do,供 control_init() 安装控制处理器时使用。 + * + * @param icd ICD 数据 + */ void model_search_control_DataObjects(stru_icd &icd) { - stru_contorl_DO control_do; + stru_contorl_DO control_do; // 控制 DO 定位信息(逐个字段填写) for(std::map::iterator it_ld = icd.ied.map_ldevice.begin(); it_ld!= icd.ied.map_ldevice.end(); it_ld++) { - control_do.ldevice_inst = it_ld->first; + control_do.ldevice_inst = it_ld->first; // 逻辑设备实例名 stru_LDevice &ld = it_ld->second; for(std::map::iterator it_do = ld.ln0.map_all_do.begin(); it_do != ld.ln0.map_all_do.end(); it_do++) { - control_do.ln_class = ld.ln0.lnClass; - control_do.ln_inst = ""; - control_do.ln_type = ld.ln0.lnType; - control_do.ln_prefix = ""; - control_do.do_name = it_do->first; - model_search_ln_control_DataObjects(icd, control_do, it_do->second); + control_do.ln_class = ld.ln0.lnClass; // LN 类名(LLN0) + control_do.ln_inst = ""; // LLN0 无实例号 + control_do.ln_type = ld.ln0.lnType; // LNodeType id + control_do.ln_prefix = ""; // LLN0 无前缀 + control_do.do_name = it_do->first; // DO 名称 + model_search_ln_control_DataObjects(icd, control_do, it_do->second); // 递归搜索 } for(std::vector::iterator it_ln = ld.vec_ln.begin(); it_ln != ld.vec_ln.end(); it_ln++) { - control_do.ln_class = it_ln->lnClass; - control_do.ln_inst = it_ln->inst; - control_do.ln_type = it_ln->lnType; - control_do.ln_prefix = it_ln->prefix; + control_do.ln_class = it_ln->lnClass; // LN 类名(如 CSWI、XCBR 等) + control_do.ln_inst = it_ln->inst; // LN 实例号 + control_do.ln_type = it_ln->lnType; // LNodeType id + control_do.ln_prefix = it_ln->prefix; // LN 前缀 for(std::map::iterator it_do = it_ln->map_all_do.begin(); it_do != it_ln->map_all_do.end(); it_do++) { - control_do.do_name = it_do->first; - model_search_ln_control_DataObjects(icd, control_do, it_do->second); + control_do.do_name = it_do->first; // DO 名称 + model_search_ln_control_DataObjects(icd, control_do, it_do->second); // 递归搜索 } } } } -void model_all_da_show(std::map map_all_da) +LOCAL std::string printf_(int cnt) { - for(auto it_map_all_da : map_all_da) + std::string str_; + for(int i = 0; i < cnt; i++) { - auto it = it_map_all_da.second; - printf("\t%s, btype %s, fc %s, sAddr %s\n", it_map_all_da.first.c_str(), it->bType.c_str(), it->fc.c_str(), it->sAddr.c_str()); + str_ += " "; } + return str_; } -void model_all_do_show(std::map map_all_do) -{ - for(auto it_map_all_do : map_all_do) - { - auto it = it_map_all_do.second; - printf("\t%s\n", it_map_all_do.first.c_str()); - model_all_do_show(it->map_all_sdo); - model_all_da_show(it->map_all_da); +LOCAL std::string get_DA_path(ModelNode *node) +{ + std::string str_temp = ""; + if(NULL == node) + { + return str_temp; } -} -void model_info_show(stru_icd &icd) -{ - for(auto it_vec_ld : icd.ied.vec_ldevice) + str_temp = node->name ? node->name : "?"; + ModelNode *temp_node = node->parent; // 向上遍历父节点 + + while(temp_node) // 逐级拼装完整路径 { - printf("ldInst %s\n", it_vec_ld.c_str()); - auto it_map_ld = icd.ied.map_ldevice.find(it_vec_ld); + str_temp = std::string(temp_node->name ? temp_node->name : "?") + "." + str_temp; // parent.name + "." + 当前路径 - for(auto itvec_ln : it_map_ld->second.vec_ln) + if(temp_node->modelType == LogicalDeviceModelType) // 到达逻辑设备层则停止 { - printf("\tlnClass %s, inst %s, lnType %s, prefix %s\n", itvec_ln.lnClass.c_str(), itvec_ln.inst.c_str(), itvec_ln.lnType.c_str(), itvec_ln.prefix.c_str()); - - for(auto it_map_all_do : itvec_ln.map_all_do) - { - model_all_do_show(it_map_all_do.second->map_all_sdo); - model_all_da_show(it_map_all_do.second->map_all_da); - } + break; } + temp_node = temp_node->parent; // 继续向上一级 + } + return str_temp; +} + +LOCAL void show_da(stru_all_DA *p_da, int cnt) +{ + for(auto it_dai : p_da->map_all_da) + { + LOG_I("%sDAI %s", printf_(cnt + 1).c_str(), it_dai.first.c_str()); + show_da(it_dai.second, cnt + 1); + } + + LOG_I("%sDA path: %s, fc %d,%s, saddr: %s", printf_(cnt + 1).c_str(), get_DA_path((ModelNode *)p_da->DA).c_str(), p_da->DA->fc, p_da->fc.c_str(), p_da->sAddr.c_str()); +} + +LOCAL void show_do(stru_all_DO *p_do, int cnt) +{ + for(auto it_sdo : p_do->map_all_sdo) + { + LOG_I("%sSDO %s", printf_(cnt + 1).c_str(), it_sdo.first.c_str()); + show_do(it_sdo.second, cnt + 1); + + for(auto it_da : it_sdo.second->map_all_da) + { + LOG_I("%sDA %s", printf_(cnt + 1).c_str(), it_da.first.c_str()); + show_da(it_da.second, cnt + 1); + } + } + + for(auto it_da : p_do->map_all_da) + { + LOG_I("%sDA %s", printf_(cnt + 1).c_str(), it_da.first.c_str()); + show_da(it_da.second, cnt + 1); } } -/* - * model_init - Initializes the IED model - input: icd - ICD structure - output: // icd.ied.model - IED model - return: 0 on success, -1 on failure -*/ +LOCAL void show(stru_icd &icd) +{ + for(auto it_ld : icd.ied.map_ldevice) + { + LOG_I("Logical device %s", it_ld.first.c_str()); + for(auto it_ln : it_ld.second.vec_ln) + { + LOG_I("%sLN %s", printf_(1).c_str(), it_ln.inst.c_str()); + + for(auto it_do : it_ln.map_all_do) + { + LOG_I("%sDO %s", printf_(2).c_str(), it_do.first.c_str()); + show_do(it_do.second, 2); + } + } + } +} + +/** + * 初始化 IED 数据模型(model_init 入口) + * + * 完整流程参见文件头部注释。 + * + * @param icd ICD 数据,输出 icd.ied.model + * @return 0 成功,-1 失败 + */ int model_init(stru_icd &icd) { - icd.ied.model = IedModel_create(icd.ied.name.c_str()); + icd.ied.model = IedModel_create(icd.ied.name.c_str()); // 创建顶层 IedModel(根节点) if(NULL == icd.ied.model) { LOG_E("Failed to create IedModel %s", icd.ied.name.c_str()); return -1; } - if(0 != model_ldevice_init(icd.ied.model, icd)) + if(0 != model_ldevice_init(icd.ied.model, icd)) // 创建所有逻辑设备(含 LLN0/LN/DO/DA/数据集/报告控制/SGCB) { LOG_E("Failed to init logical device %s", icd.ied.name.c_str()); - IedModel_destroy(icd.ied.model); + IedModel_destroy(icd.ied.model); // 失败则销毁已创建的模型 return -1; } - model_info_show(icd); - - model_sync_ied_default_values(icd); - // model_sync_ied_do_ptrs(icd); + // show(icd); - // model_ied_saddr_point_init(icd); + model_sync_ied_default_values(icd); // 将 ICD 中的 sAddr/val 同步到运行时模型节点 + // 打印 sAddr 与模型路径的对应关系(调试用) for(std::vector::iterator it_saddr = icd.ied.vec_saddr.begin(); it_saddr != icd.ied.vec_saddr.end(); it_saddr++) { - ModelNode *p_node = icd.ied.map_saddr_point[*it_saddr].node; + ModelNode *p_node = icd.ied.map_saddr_point[*it_saddr].node; // 取 sAddr 对应的模型节点 if(NULL == p_node) { LOG_E("saddr %s node is NULL", it_saddr->c_str()); continue; } - std::string str_temp = p_node->name; - - ModelNode *temp_node = p_node->parent; - - while(temp_node) - { - str_temp = std::string(temp_node->name ? temp_node->name : "?") + "." + str_temp; - - if(temp_node->modelType == LogicalDeviceModelType) - { - break; - } - temp_node = temp_node->parent; - } - - LOG_I("sAddr %s, node type %d, name %s", it_saddr->c_str(), p_node->modelType, str_temp.c_str()); + LOG_I("sAddr %s, node type %d, name %s", it_saddr->c_str(), p_node->modelType, get_DA_path(p_node).c_str()); } - model_search_control_DataObjects(icd); + model_search_control_DataObjects(icd); // 搜索所有控制 DO(含 ctlModel 的 DO),存入 vec_control_do + // 注册模型初始化回调函数,在 IedServer 创建后由 libiec61850 调用以设置 DA 初始值 icd.ied.model->initializer = mms_s_values_init; return 0; diff --git a/src/protocol/libmms_s/src/mms_s_param.cpp b/src/protocol/libmms_s/src/mms_s_param.cpp index 2aa159b..72ebbd5 100644 --- a/src/protocol/libmms_s/src/mms_s_param.cpp +++ b/src/protocol/libmms_s/src/mms_s_param.cpp @@ -1,12 +1,36 @@ +/** + * @file mms_s_param.cpp + * @brief IEC 61850 定值组参数(FC=SG/SE)管理实现 + * + * @details 定值组(Setting Group)允许多套定值间切换。本模块实现: + * + * 【数据结构】 + * - vec_params : 外部注册的参数列表,每个元素包含多组值(param_num 个) + * - g_param_cb : 参数回调函数 + * + * 【FC=SG vs FC=SE】 + * - SG(Setting Group): 当前激活定值区的实际值 ← 由激活切换时加载 + * - SE(Setting Editable): 编辑缓冲区值 ← 由编辑切换时加载 + * + * 【回调链】 + * - param_active_sg_changed_handler : 定值组激活切换 → 加载对应组数据到 SG DA + * - param_edit_sg_changed_handler : 编辑缓冲区切换 → 加载对应组数据到 SE DA + * - edit_sg_confirmation_handler : 编辑确认 → 回读 SE DA 当前值更新到参数数据 + * + * 【值的获取与验证】 + * - param_get_*() 系列函数:从 DA 读取值并做最小值/最大值/步长校验 + * - param_min_max_step_get() : 从同级 minVal/maxVal/stepSize DA 读取校验参数 + */ + #include #include "myMms_s.h" #include "mms_s_param.h" -// #include "cJSON.h" LOCAL std::vector vec_params; LOCAL mms_s_param_cb g_param_cb = NULL; +/** 更新 BOOLEAN 类型属性值 */ void param_update_BOOLEAN(IedServer server, DataAttribute* dataAttribute, std::string val) { if(NULL == server || NULL == dataAttribute) @@ -15,9 +39,10 @@ void param_update_BOOLEAN(IedServer server, DataAttribute* dataAttribute, std::s return; } - IedServer_updateBooleanAttributeValue(server, dataAttribute, val == "1" ? true : false); // call + IedServer_updateBooleanAttributeValue(server, dataAttribute, val == "1" ? true : false); } +/** 更新 INT32 类型属性值 */ void param_update_INT(IedServer server, DataAttribute* dataAttribute, std::string val) { if(NULL == server || NULL == dataAttribute) @@ -30,6 +55,7 @@ void param_update_INT(IedServer server, DataAttribute* dataAttribute, std::strin } +/** 更新 UINT32 类型属性值 */ void param_update_UINT(IedServer server, DataAttribute* dataAttribute, std::string val) { if(NULL == server || NULL == dataAttribute) @@ -41,6 +67,7 @@ void param_update_UINT(IedServer server, DataAttribute* dataAttribute, std::stri IedServer_updateUnsignedAttributeValue(server, dataAttribute, atoi(val.c_str())); } +/** 更新 FLOAT 类型属性值 */ void param_update_FLOAT(IedServer server, DataAttribute* dataAttribute, std::string val) { if(NULL == server || NULL == dataAttribute) @@ -52,6 +79,7 @@ void param_update_FLOAT(IedServer server, DataAttribute* dataAttribute, std::str IedServer_updateFloatAttributeValue(server, dataAttribute, atof(val.c_str())); } +/** 更新 VisibleString 类型属性值 */ void param_update_VISIBLE_STRING(IedServer server, DataAttribute* dataAttribute, std::string val) { if(NULL == server || NULL == dataAttribute) @@ -63,6 +91,7 @@ void param_update_VISIBLE_STRING(IedServer server, DataAttribute* dataAttribute, IedServer_updateVisibleStringAttributeValue(server, dataAttribute, (char *)val.c_str()); } +/** 更新 MMS String 类型属性值 */ void param_update_STRING(IedServer server, DataAttribute* dataAttribute, std::string val) { if(NULL == server || NULL == dataAttribute) @@ -74,11 +103,12 @@ void param_update_STRING(IedServer server, DataAttribute* dataAttribute, std::st IedServer_updateAttributeValue(server, dataAttribute, MmsValue_newMmsString((char *)val.c_str())); } -LOCAL struct +/** MMS 类型到更新函数的分发表 */ +LOCAL struct { MmsType type; void (*update_value)(IedServer server, DataAttribute* dataAttribute, std::string val); -}g_param_update_funcs[] = +}g_param_update_funcs[] = { {MMS_BOOLEAN, param_update_BOOLEAN}, {MMS_INTEGER, param_update_INT}, @@ -92,7 +122,15 @@ LOCAL int g_param_update_funcs_count = sizeof(g_param_update_funcs)/sizeof(g_par - +/** + * 将指定定值组号的参数数据加载到 SG DA + * + * 根据 vec_params 中每个参数的 data[actSG-1] 值,找到对应 sAddr+"_SG" 的 + * 模型节点,将值写入。 + * + * @param ld 逻辑设备 + * @param actSG 目标激活定值组号(1-based) + */ LOCAL void param_load_active_sg_values(stru_LDevice &ld, int actSG) { stru_icd *p_icd = mms_s_get_icd_ptr(); @@ -147,6 +185,14 @@ LOCAL void param_load_active_sg_values(stru_LDevice &ld, int actSG) LOG_I("Load active setting group %d values done", actSG); } +/** + * 将指定编辑定值组号的参数数据加载到 SE DA + * + * 与 param_load_active_sg_values() 类似,但目标 sAddr 后缀为 "_SE"。 + * + * @param ld 逻辑设备 + * @param editSG 目标编辑定值组号(1-based) + */ LOCAL void param_load_edit_sg_values(stru_LDevice &ld, int editSG) { stru_icd *p_icd = mms_s_get_icd_ptr(); @@ -200,6 +246,17 @@ LOCAL void param_load_edit_sg_values(stru_LDevice &ld, int editSG) } } +/** + * 激活定值组切换回调 + * + * 用户端或客户端切换激活定值组时触发,将新激活组的数据加载到 SG DA。 + * + * @param parameter 逻辑设备指针 + * @param sgcb 定值控制块 + * @param newActSg 新激活定值组号 + * @param connection 触发切换的客户端连接 + * @return true 允许切换 + */ LOCAL bool param_active_sg_changed_handler(void* parameter, SettingGroupControlBlock* sgcb, uint8_t newActSg, ClientConnection connection) { if(NULL == parameter || NULL == sgcb) @@ -207,7 +264,7 @@ LOCAL bool param_active_sg_changed_handler(void* parameter, SettingGroupControlB LOG_E("parameter %p or sgcb %p is NULL", parameter, sgcb); return false; } - + stru_LDevice *p_ld = (stru_LDevice*)parameter; LOG_I("Switch to setting group %i\n", (int) newActSg); @@ -217,6 +274,17 @@ LOCAL bool param_active_sg_changed_handler(void* parameter, SettingGroupControlB return true; } +/** + * 编辑定值组切换回调 + * + * 客户端切换到编辑缓冲区时触发,将对应组的数据加载到 SE DA。 + * + * @param parameter 逻辑设备指针 + * @param sgcb 定值控制块 + * @param newEditSg 新编辑定值组号 + * @param connection 触发切换的客户端连接 + * @return true 允许切换 + */ LOCAL bool param_edit_sg_changed_handler(void* parameter, SettingGroupControlBlock* sgcb, uint8_t newEditSg, ClientConnection connection) { if(NULL == parameter || NULL == sgcb) @@ -234,6 +302,17 @@ LOCAL bool param_edit_sg_changed_handler(void* parameter, SettingGroupControlBlo return true; } +/** + * 从同级 DA 中读取 minVal / maxVal / stepSize 校验参数 + * + * 遍历父节点(DO)下的所有子 DA,按名称匹配读取。 + * 对于浮点类型使用 MmsValue_toFloat(),整数类型使用 MmsValue_toInt32()。 + * + * @param node 待校验的 DA + * @param min 输出:最小值 + * @param max 输出:最大值 + * @param step 输出:步长 + */ LOCAL void param_min_max_step_get(DataAttribute *node, float &min, float &max, float &step) { if(node == NULL) @@ -258,78 +337,140 @@ LOCAL void param_min_max_step_get(DataAttribute *node, float &min, float &max, f return; } - DataAttribute *da_node = (DataAttribute *)node->parent; - - DataAttribute *da_first = (DataAttribute *)da_node->firstChild; - while(da_first != NULL) + ModelNode *p_node = (ModelNode *)node; + + while(p_node != NULL) { - if(strcmp(da_first->name, "minVal") == 0) + p_node = p_node->parent; + if(p_node == NULL) { - if(type == MMS_FLOAT) - { - min = MmsValue_toFloat(da_first->mmsValue); - } - else - { - min = (float)MmsValue_toInt32(da_first->mmsValue); - } - } - else if(strcmp(da_first->name, "maxVal") == 0) - { - if(type == MMS_FLOAT) - { - max = MmsValue_toFloat(da_first->mmsValue); - } - else - { - max = (float)MmsValue_toInt32(da_first->mmsValue); - } - } - else if(strcmp(da_first->name, "stepSize") == 0) - { - if(type == MMS_FLOAT) - { - step = MmsValue_toFloat(da_first->mmsValue); - } - else - { - step = (float)MmsValue_toInt32(da_first->mmsValue); - } + break; } - da_first = (DataAttribute *)da_first->sibling; + if(p_node->modelType == DataObjectModelType) + { + DataAttribute *da_first = (DataAttribute *)p_node->firstChild; + while(da_first != NULL) + { + DataAttribute *da_child = (DataAttribute *)da_first->firstChild; + + if(da_child != NULL) + { + if(strcmp(da_first->name, "minVal") == 0) + { + if(type == MMS_FLOAT) + { + min = MmsValue_toFloat(da_child->mmsValue); + } + else + { + min = (float)MmsValue_toInt32(da_child->mmsValue); + } + } + else if(strcmp(da_first->name, "maxVal") == 0) + { + if(type == MMS_FLOAT) + { + max = MmsValue_toFloat(da_child->mmsValue); + } + else + { + max = (float)MmsValue_toInt32(da_child->mmsValue); + } + } + else if(strcmp(da_first->name, "stepSize") == 0) + { + if(type == MMS_FLOAT) + { + step = MmsValue_toFloat(da_child->mmsValue); + } + else + { + step = (float)MmsValue_toInt32(da_child->mmsValue); + } + } + } + da_first = (DataAttribute *)da_first->sibling; + } + + break; + } } + // DataAttribute *da_node = (DataAttribute *)p_node->parent; + + + // DataAttribute *da_first = (DataAttribute *)da_node->firstChild; + // while(da_first != NULL) + // { + // if(strcmp(da_first->name, "minVal") == 0) + // { + // if(type == MMS_FLOAT) + // { + // min = MmsValue_toFloat(da_first->mmsValue); + // } + // else + // { + // min = (float)MmsValue_toInt32(da_first->mmsValue); + // } + // } + // else if(strcmp(da_first->name, "maxVal") == 0) + // { + // if(type == MMS_FLOAT) + // { + // max = MmsValue_toFloat(da_first->mmsValue); + // } + // else + // { + // max = (float)MmsValue_toInt32(da_first->mmsValue); + // } + // } + // else if(strcmp(da_first->name, "stepSize") == 0) + // { + // if(type == MMS_FLOAT) + // { + // step = MmsValue_toFloat(da_first->mmsValue); + // } + // else + // { + // step = (float)MmsValue_toInt32(da_first->mmsValue); + // } + // } + + // da_first = (DataAttribute *)da_first->sibling; + // } } -LOCAL void param_get_BOOLEAN(DataAttribute *node, std::string &val) +/** 读取 BOOLEAN 值到字符串 */ +LOCAL void param_get_BOOLEAN(DataAttribute *node_SE, DataAttribute *node_SG, std::string &val) { - if(node == NULL) + if(node_SE == NULL) { - LOG_E("node is NULL"); + LOG_E("node_SE is NULL"); return; } - val = MmsValue_getBoolean(node->mmsValue) ? "1" : "0"; + val = MmsValue_getBoolean(node_SE->mmsValue) ? "1" : "0"; } -LOCAL void param_get_INT(DataAttribute *node, std::string &val) +/** 读取 INT 值到字符串,做范围/步长校验 */ +LOCAL void param_get_INT(DataAttribute *node_SE, DataAttribute *node_SG, std::string &val) { - if(node == NULL) + if(node_SE == NULL) { - LOG_E("node is NULL"); + LOG_E("node_SE is NULL"); return; } float min, max, step; - param_min_max_step_get(node, min, max, step); + param_min_max_step_get(node_SG, min, max, step); - if((int)step == 0) + if((int)step == 0) { return; } - int intVal = MmsValue_toInt32(node->mmsValue); + 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); @@ -346,23 +487,24 @@ LOCAL void param_get_INT(DataAttribute *node, std::string &val) val = std::to_string(intVal); } -LOCAL void param_get_UINT(DataAttribute *node, std::string &val) +/** 读取 UINT 值到字符串,做范围/步长校验 */ +LOCAL void param_get_UINT(DataAttribute *node_SE, DataAttribute *node_SG, std::string &val) { - if(node == NULL) + if(node_SE == NULL) { - LOG_E("node is NULL"); + LOG_E("node_SE is NULL"); return; } float min, max, step; - param_min_max_step_get(node, min, max, step); + param_min_max_step_get(node_SG, min, max, step); - if((uint32_t)step == 0) + if((uint32_t)step == 0) { return; } - int uintVal = MmsValue_toUint32(node->mmsValue); + 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); @@ -379,26 +521,27 @@ LOCAL void param_get_UINT(DataAttribute *node, std::string &val) val = std::to_string(uintVal); } -LOCAL void param_get_FLOAT(DataAttribute *node, std::string &val) +/** 读取 FLOAT 值到字符串,做范围/步长校验(使用浮点比较容差) */ +LOCAL void param_get_FLOAT(DataAttribute *node_SE, DataAttribute *node_SG, std::string &val) { - if(node == NULL) + if(node_SE == NULL) { - LOG_E("node is NULL"); + LOG_E("node_SE is NULL"); return; } float min, max, step; - param_min_max_step_get(node, min, max, step); + param_min_max_step_get(node_SG, min, max, step); const float EPSILON = 1e-6; - float floatVal = MmsValue_toFloat(node->mmsValue); + float floatVal = MmsValue_toFloat(node_SE->mmsValue); if(floatVal < min - EPSILON || floatVal > max + EPSILON) { LOG_E("Value %f is out of range [%f, %f]", floatVal, min, max); return; } - + if(fabs(step) < EPSILON) { val = std::to_string(floatVal); @@ -417,23 +560,25 @@ LOCAL void param_get_FLOAT(DataAttribute *node, std::string &val) val = std::to_string(floatVal); } -LOCAL void param_get_STRING(DataAttribute *node, std::string &val) +/** 读取 STRING 值 */ +LOCAL void param_get_STRING(DataAttribute *node_SE, DataAttribute *node_SG, std::string &val) { - if(node == NULL) + if(node_SE == NULL) { - LOG_E("node is NULL"); + LOG_E("node_SE is NULL"); return; } - val = MmsValue_toString(node->mmsValue); + val = MmsValue_toString(node_SE->mmsValue); } -LOCAL struct +/** MMS 类型到读取函数的分发表 */ +LOCAL struct { MmsType type; - void (*get_value)(DataAttribute *node, std::string &val); -}g_param_get_funcs[] = + void (*get_value)(DataAttribute *node_SE, DataAttribute *node_SG, std::string &val); +}g_param_get_funcs[] = { {MMS_BOOLEAN, param_get_BOOLEAN}, {MMS_INTEGER, param_get_INT}, @@ -445,6 +590,16 @@ LOCAL struct LOCAL int g_param_get_funcs_count = sizeof(g_param_get_funcs)/sizeof(g_param_get_funcs[0]); +/** + * 编辑定值组确认回调 + * + * 客户端确认编辑值后触发,回读 SE DA 的当前值并更新到参数数据中。 + * 如果当前激活组就是编辑组,还需要额外刷新 SG DA。 + * + * @param parameter 逻辑设备指针 + * @param sgcb 定值控制块 + * @param editSg 编辑定值组号 + */ LOCAL void edit_sg_confirmation_handler(void* parameter, SettingGroupControlBlock* sgcb, uint8_t editSg) { if(NULL == parameter || NULL == sgcb) @@ -465,7 +620,7 @@ LOCAL void edit_sg_confirmation_handler(void* parameter, SettingGroupControlBloc } stru_icd *p_icd = mms_s_get_icd_ptr(); - if(NULL == p_icd) + if(NULL == p_icd) { LOG_E("p_icd is NULL"); return; @@ -474,33 +629,52 @@ LOCAL void edit_sg_confirmation_handler(void* parameter, SettingGroupControlBloc for(std::vector::iterator it_param = vec_params.begin(); it_param != vec_params.end(); it_param++) { - std::string saddr = std::string(it_param->base.saddr) + "_SE"; + std::string saddr_sg = std::string(it_param->base.saddr) + "_SG"; + std::string saddr_se = std::string(it_param->base.saddr) + "_SE"; if(it_param->param_num < editSg) { - LOG_E("param_num %d is not enough for saddr %s, editSg %d", it_param->param_num, saddr.c_str(), editSg); + LOG_E("param_num %d is not enough for saddr %s, editSg %d", it_param->param_num, saddr_se.c_str(), editSg); continue; } - std::string val = mms_s_get_string_by_type(it_param->data[editSg-1], it_param->base.type); + std::string val = ""; - if(p_icd->ied.map_saddr_point.find(saddr) == p_icd->ied.map_saddr_point.end()) + if(p_icd->ied.map_saddr_point.find(saddr_sg) == p_icd->ied.map_saddr_point.end()) { - LOG_E("sAddr %s not found in ICD", saddr.c_str()); + LOG_E("sAddr %s not found in ICD", saddr_sg.c_str()); continue; } - ModelNode *node = p_icd->ied.map_saddr_point[saddr].node; - if(node == NULL) + if(p_icd->ied.map_saddr_point.find(saddr_se) == p_icd->ied.map_saddr_point.end()) { - LOG_E("ModelNode for sAddr %s is NULL", saddr.c_str()); + LOG_E("sAddr %s not found in ICD", saddr_se.c_str()); + continue; + } + + ModelNode *node_sg = p_icd->ied.map_saddr_point[saddr_sg].node; + if(node_sg == NULL) + { + LOG_E("ModelNode for sAddr %s is NULL", saddr_sg.c_str()); + continue; + } + + ModelNode *node_se = p_icd->ied.map_saddr_point[saddr_se].node; + if(node_se == NULL) + { + LOG_E("ModelNode for sAddr %s is NULL", saddr_se.c_str()); continue; } int match = 0; for(int i = 0; i < g_param_get_funcs_count; i++) { - if(MmsValue_getType(((DataAttribute *)node)->mmsValue) == g_param_get_funcs[i].type) + if(MmsValue_getType(((DataAttribute *)node_se)->mmsValue) == g_param_get_funcs[i].type) { - g_param_get_funcs[i].get_value((DataAttribute *)node, val); + g_param_get_funcs[i].get_value((DataAttribute *)node_se, (DataAttribute *)node_sg, val); + + if(g_param_cb != NULL) + { + g_param_cb(&*it_param, val.c_str(), editSg - 1); + } match = 1; break; } @@ -508,7 +682,7 @@ LOCAL void edit_sg_confirmation_handler(void* parameter, SettingGroupControlBloc if(!match) { - LOG_E("No get function found for sAddr %s with MMS type %d", saddr.c_str(), MmsValue_getType(((DataAttribute *)node)->mmsValue)); + LOG_E("No get function found for sAddr %s with MMS type %d", saddr_se.c_str(), MmsValue_getType(((DataAttribute *)node_se)->mmsValue)); continue; } } @@ -519,26 +693,13 @@ LOCAL void edit_sg_confirmation_handler(void* parameter, SettingGroupControlBloc } } +/** 读访问控制回调(当前允许所有读取) */ LOCAL MmsDataAccessError read_access_handler(LogicalDevice* ld, LogicalNode* ln, DataObject* dataObject, FunctionalConstraint fc, ClientConnection connection, void* parameter) { - // if(NULL == ld || NULL == ln || NULL == connection || NULL == parameter) - // { - // LOG_E("parameter %p or ld %p or ln %p or connection %p is NULL", parameter, ld, ln, connection); - // return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; - // } - - // stru_LDevice *p_ld = (stru_LDevice*)parameter; - - // void* securityToken = ClientConnection_getSecurityToken(connection); - - // if (dataObject) - // printf("Read access to %s/%s.%s[%s]\n", ld->name, ln->name, dataObject->name, FunctionalConstraint_toString(fc)); - // else - // printf("Read access to %s/%s[%s]\n", ld->name, ln->name, FunctionalConstraint_toString(fc)); - return DATA_ACCESS_ERROR_SUCCESS; } +/** 调试用:打印所有参数信息 */ LOCAL void param_values_show() { if(vec_params.empty()) @@ -550,7 +711,7 @@ LOCAL void param_values_show() for(uint16_t i = 0; i < vec_params.size(); i++) { stru_mms_s_param ¶m = vec_params[i]; - + printf("saddr %s, desc %s, type %d\n", param.base.saddr, param.base.desc, param.base.type); for(uint8_t j = 0; j < param.param_num; j++) { @@ -561,6 +722,13 @@ LOCAL void param_values_show() } } +/** + * 注册参数列表 + * @param p_param 参数数组 + * @param num 数组元素个数 + * @param cb 参数回调(可选) + * @return 0 成功,-1 参数无效 + */ int mms_s_param_register(stru_mms_s_param *p_param, uint16_t num, mms_s_param_cb cb) { if(nullptr == p_param || num == 0) @@ -585,6 +753,15 @@ int mms_s_param_register(stru_mms_s_param *p_param, uint16_t num, mms_s_param_cb return 0; } +/** + * 初始化参数(定值组)功能 + * + * 为每个逻辑设备的 SGCB 安装各种回调,并初始加载激活定值组数据。 + * + * @param server IED 服务器 + * @param icd ICD 数据 + * @return 0 成功 + */ int param_init(IedServer server, stru_icd &icd) { if(vec_params.empty()) @@ -598,7 +775,7 @@ int param_init(IedServer server, stru_icd &icd) for(std::map::iterator it_ld = icd.ied.map_ldevice.begin(); it_ld!= icd.ied.map_ldevice.end(); it_ld++) { stru_LDevice *p_ld = &it_ld->second; - + if(NULL == p_ld->sgcb) { LOG_E("sgcb of %s is NULL", it_ld->first.c_str()); @@ -606,8 +783,8 @@ int param_init(IedServer server, stru_icd &icd) } LogicalNode *ln0 = p_ld->sgcb->parent; - LOG_I("ld %s sgcb ln0 %s, actSG %d, numofSGs %d, editSG %d, cnfEdit %d, timestamp %ld, resvTms %d, sibling %p", - it_ld->first.c_str(), ln0 ? ln0->name : "NULL", p_ld->sgcb->actSG, p_ld->sgcb->numOfSGs, p_ld->sgcb->editSG, + LOG_I("ld %s sgcb ln0 %s, actSG %d, numofSGs %d, editSG %d, cnfEdit %d, timestamp %ld, resvTms %d, sibling %p", + it_ld->first.c_str(), ln0 ? ln0->name : "NULL", p_ld->sgcb->actSG, p_ld->sgcb->numOfSGs, p_ld->sgcb->editSG, p_ld->sgcb->cnfEdit, p_ld->sgcb->timestamp, p_ld->sgcb->resvTms, p_ld->sgcb->sibling); param_load_active_sg_values(*p_ld, p_ld->sgcb->actSG); @@ -621,4 +798,4 @@ int param_init(IedServer server, stru_icd &icd) return 0; -} \ No newline at end of file +} \ No newline at end of file diff --git a/src/protocol/libmms_s/src/mms_s_setting.cpp b/src/protocol/libmms_s/src/mms_s_setting.cpp index 9dc5f2f..bce37f0 100644 --- a/src/protocol/libmms_s/src/mms_s_setting.cpp +++ b/src/protocol/libmms_s/src/mms_s_setting.cpp @@ -1,15 +1,39 @@ +/** + * @file mms_s_setting.cpp + * @brief IEC 61850 定值(SP)管理实现 + * + * @details 定值(Setting,FC=SP)单一值,不参与定值组切换。 + * 本模块实现以下功能: + * + * 【初始化阶段】 + * - setting_value_init() : 将外部注册的定值初始值写入对应的 SP DA + * - setting_handle_init() : 为每个注册的 SP sAddr 安装 writeAccessHandler + * - 设置 SP 的全局写权限为 ACCESS_POLICY_DENY, + * 仅通过 writeAccessHandler 精确控制已注册 sAddr 的写入 + * + * 【写操作阶段】 + * - writeAccessHandler() : 接收客户端写请求 + * 1. 按 sAddr 匹配注册的定值 + * 2. 调用 setting_get_*() 做范围/步长校验 + * 3. 校验通过后触发外部回调 cb_setting + * 4. 回调返回后由 libiec61850 更新 mmsValue + * + * 【校验函数】 + * - setting_get_BOOLEAN/INT/UINT/FLOAT/STRING : 类型校验 + 范围校验 + 步长校验 + * - setting_min_max_step_get() : 从同级 minVal/maxVal/stepSize DA 读取校验参数 + */ + #include #include "myMms_s.h" #include "mms_s_setting.h" -// #include "cJSON.h" - LOCAL std::vector vec_settings; LOCAL mms_s_setting_cb cb_setting = NULL; +/** 更新 BOOLEAN 属性值 */ void setting_update_BOOLEAN(IedServer server, DataAttribute* dataAttribute, std::string val) { if(NULL == server || NULL == dataAttribute) @@ -18,9 +42,10 @@ void setting_update_BOOLEAN(IedServer server, DataAttribute* dataAttribute, std: return; } - IedServer_updateBooleanAttributeValue(server, dataAttribute, val == "1" ? true : false); // call + IedServer_updateBooleanAttributeValue(server, dataAttribute, val == "1" ? true : false); } +/** 更新 INT32 属性值 */ void setting_update_INT(IedServer server, DataAttribute* dataAttribute, std::string val) { if(NULL == server || NULL == dataAttribute) @@ -33,6 +58,7 @@ void setting_update_INT(IedServer server, DataAttribute* dataAttribute, std::str } +/** 更新 UINT32 属性值 */ void setting_update_UINT(IedServer server, DataAttribute* dataAttribute, std::string val) { if(NULL == server || NULL == dataAttribute) @@ -44,6 +70,7 @@ void setting_update_UINT(IedServer server, DataAttribute* dataAttribute, std::st IedServer_updateUnsignedAttributeValue(server, dataAttribute, atoi(val.c_str())); } +/** 更新 FLOAT 属性值 */ void setting_update_FLOAT(IedServer server, DataAttribute* dataAttribute, std::string val) { if(NULL == server || NULL == dataAttribute) @@ -55,6 +82,7 @@ void setting_update_FLOAT(IedServer server, DataAttribute* dataAttribute, std::s IedServer_updateFloatAttributeValue(server, dataAttribute, atof(val.c_str())); } +/** 更新 VisibleString 属性值 */ void setting_update_VISIBLE_STRING(IedServer server, DataAttribute* dataAttribute, std::string val) { if(NULL == server || NULL == dataAttribute) @@ -66,6 +94,7 @@ void setting_update_VISIBLE_STRING(IedServer server, DataAttribute* dataAttribut IedServer_updateVisibleStringAttributeValue(server, dataAttribute, (char *)val.c_str()); } +/** 更新 MMS String 属性值 */ void setting_update_STRING(IedServer server, DataAttribute* dataAttribute, std::string val) { if(NULL == server || NULL == dataAttribute) @@ -77,11 +106,12 @@ void setting_update_STRING(IedServer server, DataAttribute* dataAttribute, std:: IedServer_updateAttributeValue(server, dataAttribute, MmsValue_newMmsString((char *)val.c_str())); } -LOCAL struct +/** MMS 类型到更新函数的分发表 */ +LOCAL struct { MmsType type; void (*update_value)(IedServer server, DataAttribute* dataAttribute, std::string val); -}g_setting_update_funcs[] = +}g_setting_update_funcs[] = { {MMS_BOOLEAN, setting_update_BOOLEAN}, {MMS_INTEGER, setting_update_INT}, @@ -95,6 +125,11 @@ LOCAL int g_setting_update_funcs_count = sizeof(g_setting_update_funcs)/sizeof(g +/** + * 初始化定值:将外部注册的定值数据加载到 SP DA + * @param server IED 服务器 + * @param icd ICD 数据 + */ LOCAL void setting_value_init(IedServer server, stru_icd &icd) { for(std::vector::iterator it = vec_settings.begin(); it != vec_settings.end(); it++) @@ -140,6 +175,16 @@ LOCAL void setting_value_init(IedServer server, stru_icd &icd) } +/** + * 从同级 DA 中读取 minVal / maxVal / stepSize 校验参数 + * + * 遍历父节点(DO)下的所有子 DA,按名称匹配读取。 + * + * @param node 待校验的 DA + * @param min 输出:最小值 + * @param max 输出:最大值 + * @param step 输出:步长 + */ LOCAL void setting_min_max_step_get(DataAttribute *node, float &min, float &max, float &step) { if(node == NULL) @@ -165,7 +210,7 @@ LOCAL void setting_min_max_step_get(DataAttribute *node, float &min, float &max, } DataAttribute *da_node = (DataAttribute *)node->parent; - + DataAttribute *da_first = (DataAttribute *)da_node->firstChild; while(da_first != NULL) { @@ -198,7 +243,7 @@ LOCAL void setting_min_max_step_get(DataAttribute *node, float &min, float &max, step = MmsValue_toFloat(da_first->mmsValue); } else - { + { step = (float)MmsValue_toInt32(da_first->mmsValue); } } @@ -207,6 +252,7 @@ LOCAL void setting_min_max_step_get(DataAttribute *node, float &min, float &max, } } +/** BOOLEAN 值校验和读取,返回 0 表示有效 */ LOCAL int setting_get_BOOLEAN(DataAttribute *node, MmsValue* value, std::string &val_str) { if(node == NULL || value == NULL) @@ -228,6 +274,7 @@ LOCAL int setting_get_BOOLEAN(DataAttribute *node, MmsValue* value, std::string return 0; } +/** INT 值校验(范围+步长),通过后返回 0 */ LOCAL int setting_get_INT(DataAttribute *node, MmsValue* value, std::string &val_str) { if(node == NULL || value == NULL) @@ -239,7 +286,7 @@ 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) + if((int)step == 0) { return -1; } @@ -263,6 +310,7 @@ LOCAL int setting_get_INT(DataAttribute *node, MmsValue* value, std::string &val return 0; } +/** UINT 值校验(范围+步长),通过后返回 0 */ LOCAL int setting_get_UINT(DataAttribute *node, MmsValue* value, std::string &val_str) { if(node == NULL || value == NULL) @@ -274,7 +322,7 @@ 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) + if((uint32_t)step == 0) { return -1; } @@ -298,6 +346,7 @@ LOCAL int setting_get_UINT(DataAttribute *node, MmsValue* value, std::string &va return 0; } +/** FLOAT 值校验(范围+步长,使用 EPSILON 容差),通过后返回 0 */ LOCAL int setting_get_FLOAT(DataAttribute *node, MmsValue* value, std::string &val_str) { if(node == NULL || value == NULL) @@ -317,7 +366,7 @@ LOCAL int setting_get_FLOAT(DataAttribute *node, MmsValue* value, std::string &v LOG_E("Value %f is out of range [%f, %f]", floatVal, min, max); return -1; } - + if(fabs(step) < EPSILON) { return 0; @@ -337,6 +386,7 @@ LOCAL int setting_get_FLOAT(DataAttribute *node, MmsValue* value, std::string &v return 0; } +/** STRING 值校验和读取,通过后返回 0 */ LOCAL int setting_get_STRING(DataAttribute *node, MmsValue* value, std::string &val_str) { if(node == NULL || value == NULL) @@ -351,11 +401,12 @@ LOCAL int setting_get_STRING(DataAttribute *node, MmsValue* value, std::string & } -LOCAL struct +/** MMS 类型到校验/读取函数的分发表 */ +LOCAL struct { MmsType type; int (*get_value)(DataAttribute *node, MmsValue* value, std::string &val_str); -}g_setting_get_funcs[] = +}g_setting_get_funcs[] = { {MMS_BOOLEAN, setting_get_BOOLEAN}, {MMS_INTEGER, setting_get_INT}, @@ -367,6 +418,19 @@ LOCAL struct LOCAL int g_setting_get_funcs_count = sizeof(g_setting_get_funcs)/sizeof(g_setting_get_funcs[0]); +/** + * 写访问处理器(SP 定值写入) + * + * 按 sAddr 匹配注册的定值点,做值校验,通过后触发外部回调。 + * 校验不通过返回 DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED, + * libiec61850 不会更新 mmsValue。 + * + * @param dataAttribute 被写入的 DataAttribute + * @param value 客户端写入的值 + * @param connection 客户端连接 + * @param parameter 用户参数(未使用) + * @return DATA_ACCESS_ERROR_SUCCESS 允许写入,DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED 拒绝 + */ LOCAL MmsDataAccessError writeAccessHandler (DataAttribute* dataAttribute, MmsValue* value, ClientConnection connection, void* parameter) { stru_icd *p_icd = mms_s_get_icd_ptr(); @@ -382,11 +446,10 @@ LOCAL MmsDataAccessError writeAccessHandler (DataAttribute* dataAttribute, MmsVa LOG_I("da name: %s, sAddr: %s, fc %d", dataAttribute->name, dataAttribute->sAddr, dataAttribute->fc); } - // for(int i = 0; i < vec_settings.size(); i++) for(std::vector::iterator it = vec_settings.begin(); it != vec_settings.end(); it++) { std::string sAddr = it->base.saddr; - + if(p_icd->ied.map_saddr_point.find(sAddr) == p_icd->ied.map_saddr_point.end()) { LOG_E("sAddr %s not found in ICD", sAddr.c_str()); @@ -430,6 +493,15 @@ LOCAL MmsDataAccessError writeAccessHandler (DataAttribute* dataAttribute, MmsVa } +/** + * 为注册的定值点安装写访问处理器 + * + * 遍历 vec_settings,按 sAddr 找到模型节点,调用 IedServer_handleWriteAccess()。 + * 仅处理 FC=SP 的节点。 + * + * @param server IED 服务器 + * @param icd ICD 数据 + */ LOCAL void setting_handle_init(IedServer server, stru_icd &icd) { for(int i = 0; i < vec_settings.size(); i++) @@ -468,16 +540,24 @@ LOCAL void setting_handle_init(IedServer server, stru_icd &icd) } } +/** 调试用:打印已注册的定值信息 */ LOCAL void setting_values_show() { for(int i = 0; i < vec_settings.size(); i++) { stru_mms_s_setting &setting = vec_settings[i]; - LOG_I("saddr %s, desc %s, type %d", + LOG_I("saddr %s, desc %s, type %d", setting.base.saddr, setting.base.desc, setting.base.type); } } +/** + * 注册定值列表 + * @param p_setting 定值数组 + * @param num 数组元素个数 + * @param cb 定值写入回调(可选) + * @return 0 成功,-1 参数无效 + */ int mms_s_setting_register(stru_mms_s_setting *p_setting, uint16_t num, mms_s_setting_cb cb) { if(p_setting == NULL || num == 0) @@ -502,6 +582,17 @@ int mms_s_setting_register(stru_mms_s_setting *p_setting, uint16_t num, mms_s_se return 0; } +/** + * 初始化定值功能 + * + * 1. 将注册的定值初始值写入 SP DA + * 2. 设置 SP 的全局写权限为 DENY + * 3. 为每个注册的 sAddr 安装 writeAccessHandler + * + * @param server IED 服务器 + * @param icd ICD 数据 + * @return 0 成功 + */ int setting_init(IedServer server, stru_icd &icd) { if(vec_settings.empty()) diff --git a/src/protocol/libmms_s/src/mms_s_value.cpp b/src/protocol/libmms_s/src/mms_s_value.cpp index 410c599..dbfaeff 100644 --- a/src/protocol/libmms_s/src/mms_s_value.cpp +++ b/src/protocol/libmms_s/src/mms_s_value.cpp @@ -1,6 +1,31 @@ +/** + * @file mms_s_value.cpp + * @brief IEC 61850 数据属性(DA)初始值设置 + * + * @details 本文件提供 mms_s_values_init() 函数,作为 IedModel 的 initializer 回调, + * 在 IedServer 创建过程中被 libiec61850 自动调用。 + * + * 【核心流程】 + * 1. 遍历所有逻辑设备 → 所有 LN0 和 LN → 所有 DO → 所有 SDO → 所有 DA + * 2. 对每个 DA,按 bType 匹配初始化函数 + * 3. 根据 ICD DAI 中配置的 val(默认值)创建对应的 MmsValue + * + * 【初始化函数表】 + * - g_iec61850s_value_init_cb : bType 字符串 → value_init 函数映射 + * - 支持类型:BOOLEAN, INT32, INT8U/16U/32U, FLOAT32, Enum, + * VisString*, Unicode*, Timestamp, Dbpos + * - 未支持的类型(NULL 回调)将跳过初始化 + * + * 【Enum 特殊处理】 + * - 按 val 的文本值或序号在 EnumType 中查找匹配的枚举值 + * - 找不到则默认为 0 + */ + #include "mms_s_value.h" +// ==================== 值更新回调(当前 Unused,预留给 mms_s_values_update) ==================== + LOCAL void model_da_value_update_Boolean(IedServer server, DataAttribute *da, void *value); LOCAL void model_da_value_update_INT32(IedServer server, DataAttribute *da, void *value); LOCAL void model_da_value_update_INT32U(IedServer server, DataAttribute *da, void *value); @@ -10,11 +35,11 @@ LOCAL void model_da_value_update_Visible_String(IedServer server, DataAttribute LOCAL void model_da_value_update_Unicode_String(IedServer server, DataAttribute *da, void *value); LOCAL void model_da_value_update_timesramp(IedServer server, DataAttribute *da, void *value); -LOCAL struct +LOCAL struct { const char *btype; void (*value_update_cb)(IedServer server, DataAttribute *da, void *value); -}g_iec61850s_value_update_cb[] = +}g_iec61850s_value_update_cb[] = { {"BOOLEAN", model_da_value_update_Boolean}, {"INT8", NULL}, @@ -37,8 +62,8 @@ LOCAL struct {"VisString129", model_da_value_update_Visible_String}, {"VisString255", model_da_value_update_Visible_String}, {"Unicode255", model_da_value_update_Unicode_String}, - {"Timestamp", model_da_value_update_timesramp}, - {"Quality", NULL}, + {"Timestamp", model_da_value_update_timesramp}, + {"Quality", NULL}, {"Check", NULL}, {"CodedEnum", NULL}, {"BitString", NULL}, @@ -52,12 +77,11 @@ LOCAL struct }; LOCAL int g_iec61850s_value_update_cb_count = sizeof(g_iec61850s_value_update_cb) / sizeof(g_iec61850s_value_update_cb[0]); - +/** 更新 BOOLEAN 属性值(调用 IedServer_updateBooleanAttributeValue) */ LOCAL void model_da_value_update_Boolean(IedServer server, DataAttribute *da, void *value) { if(NULL == server || NULL == da || NULL == value || strlen((char*)value) == 0) { - // LOG_E("server, da or value is NULL"); return; } @@ -76,75 +100,74 @@ LOCAL void model_da_value_update_Boolean(IedServer server, DataAttribute *da, vo } } +/** 更新 INT32 属性值 */ LOCAL void model_da_value_update_INT32(IedServer server, DataAttribute *da, void *value) { if(NULL == server || NULL == da || NULL == value || strlen((char*)value) == 0) { - // LOG_E("server, da or value is NULL"); return; } IedServer_updateInt32AttributeValue(server, da, atoi((char*)value)); } +/** 更新 UINT32 属性值 */ LOCAL void model_da_value_update_INT32U(IedServer server, DataAttribute *da, void *value) { if(NULL == server || NULL == da || NULL == value || strlen((char*)value) == 0) { - // LOG_E("server, da or value is NULL"); return; } IedServer_updateUnsignedAttributeValue(server, da, atoi((char*)value)); } +/** 更新 FLOAT32 属性值 */ LOCAL void model_da_value_update_FLOAT32(IedServer server, DataAttribute *da, void *value) { if(NULL == server || NULL == da || NULL == value || strlen((char*)value) == 0) { - // LOG_E("server, da or value is NULL"); return; } IedServer_updateFloatAttributeValue(server, da, atof((char*)value)); } +/** 更新 Enum 属性值(直接设置 mmsValue) */ LOCAL void model_da_value_update_Enum(IedServer server, DataAttribute *da, void *value) { if(NULL == server || NULL == da || NULL == value) { - // LOG_E("server, da or value is NULL"); return; } LOG_I("da %p, name %s, value %d", da, da->name, *(int *)value); - // IedServer_updateInt32AttributeValue(server, da, *(int *)value); da->mmsValue = MmsValue_newIntegerFromInt32(*(int *)value); } +/** 更新 VisibleString 属性值 */ LOCAL void model_da_value_update_Visible_String(IedServer server, DataAttribute *da, void *value) { if(NULL == server || NULL == da || NULL == value || strlen((char*)value) == 0) { - // LOG_E("server, da or value is NULL"); return; } IedServer_updateVisibleStringAttributeValue(server, da, (char*)value); } +/** 更新 Unicode String 属性值 */ LOCAL void model_da_value_update_Unicode_String(IedServer server, DataAttribute *da, void *value) { if(NULL == server || NULL == da || NULL == value || strlen((char*)value) == 0) { - // LOG_E("server, da or value is NULL"); return; } - // LOG_I("da %p, value %s", da, (char*)value); IedServer_updateAttributeValue(server, da, MmsValue_newMmsString((char*)value)); } +/** 更新 Timestamp 属性值(设为当前毫秒时间) */ LOCAL void model_da_value_update_timesramp(IedServer server, DataAttribute *da, void *value) { if(NULL == server || NULL == da) @@ -156,296 +179,8 @@ LOCAL void model_da_value_update_timesramp(IedServer server, DataAttribute *da, IedServer_updateUTCTimeAttributeValue(server, da, Hal_getTimeInMs()); } -// void model_dataAttribute_value_init(IedServer server, stru_icd &icd, std::string da_name, stru_DAI &DAI, stru_BDA &BDA_item) -// { -// if(NULL == server) -// { -// LOG_E("server is NULL"); -// return; -// } - -// DataAttribute *da = DAI.DA; -// if(NULL == da) -// { -// LOG_E("DA is NULL"); -// return; -// } - -// // LOG_I("daname %s, btype %s, value %s, da %p", da_name.c_str(), BDA_item.bType.c_str(), DAI.val.c_str(), da); - -// if(BDA_item.bType.compare("Struct") == 0) -// { -// if(icd.map_daType.find(BDA_item.type) == icd.map_daType.end()) -// { -// LOG_E("DAType %s not found in ICD", BDA_item.type.c_str()); -// return; -// } - -// std::map::iterator it_dai; -// for(it_dai = DAI.map_daI.begin(); it_dai != DAI.map_daI.end(); it_dai++) -// { -// model_dataAttribute_value_init(server, icd, it_dai->first, it_dai->second, icd.map_daType[BDA_item.type].map_bda[it_dai->first]); -// } - -// } -// else if(BDA_item.bType.compare("Enum") == 0) -// { -// if(icd.map_enumType.find(BDA_item.type) == icd.map_enumType.end()) -// { -// LOG_E("EnumType %s not found in ICD", BDA_item.type.c_str()); -// return; -// } - -// if(DAI.val.empty()) -// { -// int temp = 0; -// model_da_value_update_Enum(server, da, (void*)&temp); -// // LOG_I("Enum value is empty, set to 0"); -// return; -// } -// else -// { -// // LOG_I("Enum value %s", DAI.val.c_str()); -// } - -// std::map::iterator it_enum; -// for(it_enum = icd.map_enumType[BDA_item.type].map_enumVal.begin(); it_enum != icd.map_enumType[BDA_item.type].map_enumVal.end(); it_enum++) -// { -// if(it_enum->second.compare(DAI.val) == 0) -// { -// model_da_value_update_Enum(server, da, (void*)&it_enum->first); -// } -// else if(it_enum->first == atoi(DAI.val.c_str())) -// { -// model_da_value_update_Enum(server, da, (void*)&it_enum->first); -// } -// } -// } -// else -// { -// for(int i = 0; i < g_iec61850s_value_update_cb_count; i++) -// { -// if(BDA_item.bType.compare(g_iec61850s_value_update_cb[i].btype) == 0) -// { -// if(g_iec61850s_value_update_cb[i].value_update_cb != NULL) -// { -// g_iec61850s_value_update_cb[i].value_update_cb(server, da, (void*)DAI.val.c_str()); -// } -// else -// { -// LOG_E("No value update callback for bType %s", BDA_item.bType.c_str()); -// } -// break; -// } -// } -// } -// } - -// void model_dataAttribute_value_init(IedServer server, stru_icd &icd, std::string da_name, stru_DAI &DAI, stru_DA &DA_item) -// { -// if(NULL == server) -// { -// LOG_E("server is NULL"); -// return; -// } - -// DataAttribute *da = DAI.DA; -// if(NULL == da) -// { -// LOG_E("DA is NULL"); -// return; -// } - -// // LOG_I("daname %s, btype %s, value %s, da %p", da_name.c_str(), DA_item.bType.c_str(), DAI.val.c_str(), da); - -// if(DA_item.bType.compare("Struct") == 0) -// { -// if(icd.map_daType.find(DA_item.type) == icd.map_daType.end()) -// { -// LOG_E("DAType %s not found in ICD", DA_item.type.c_str()); -// return; -// } - -// std::map::iterator it_dai; -// for(it_dai = DAI.map_daI.begin(); it_dai != DAI.map_daI.end(); it_dai++) -// { -// model_dataAttribute_value_init(server, icd, it_dai->first, it_dai->second, icd.map_daType[DA_item.type].map_bda[it_dai->first]); -// } - -// } -// else if(DA_item.bType.compare("Enum") == 0) -// { -// if(icd.map_enumType.find(DA_item.type) == icd.map_enumType.end()) -// { -// LOG_E("EnumType %s not found in ICD", DA_item.type.c_str()); -// return; -// } - -// if(DAI.val.empty()) -// { -// int temp = 0; -// model_da_value_update_Enum(server, da, (void*)&temp); -// LOG_I("Enum value is empty, set to 0"); -// return; -// } -// else -// { -// // LOG_I("Enum value %s", DAI.val.c_str()); -// } - -// std::map::iterator it_enum; -// for(it_enum = icd.map_enumType[DA_item.type].map_enumVal.begin(); it_enum != icd.map_enumType[DA_item.type].map_enumVal.end(); it_enum++) -// { -// if(it_enum->second.compare(DAI.val) == 0) -// { -// model_da_value_update_Enum(server, da, (void*)&it_enum->first); -// } -// else if(it_enum->first == atoi(DAI.val.c_str())) -// { -// model_da_value_update_Enum(server, da, (void*)&it_enum->first); -// } -// } -// } -// else -// { -// for(int i = 0; i < g_iec61850s_value_update_cb_count; i++) -// { -// if(DA_item.bType.compare(g_iec61850s_value_update_cb[i].btype) == 0) -// { -// if(g_iec61850s_value_update_cb[i].value_update_cb != NULL) -// { -// g_iec61850s_value_update_cb[i].value_update_cb(server, da, (void*)DAI.val.c_str()); -// } -// else -// { -// LOG_E("No value update callback for bType %s", DA_item.bType.c_str()); -// } -// break; -// } -// } -// } - -// } - - -// /* -// * model_dataObject_value_init - initialize the values of the data objects in the IED model -// input: server - the IedServer object -// icd - the ICD object, contains all the information of the model and values -// lnType_item - the LNodeType object, contains all the information of the logical node type -// map_all_do - the map of all data objects in the IED model -// map_doI - the map of data object instances in the IED model -// output: -// return: 0 - success, other - failure -// */ -// int model_dataObject_value_init(IedServer server, stru_icd &icd, stru_LNodeType &lnType_item, std::map &map_all_do, std::map &map_doI) -// { -// for(std::map::iterator it_doI = map_doI.begin(); it_doI != map_doI.end(); it_doI++) -// { -// // LOG_I("doiname %s", it_doI->first.c_str()); -// if(map_all_do.find(it_doI->first) == map_all_do.end()) -// { -// LOG_E("Data object %s not found in map_all_do", it_doI->first.c_str()); -// continue; -// } -// stru_all_DO &all_DO_item = *map_all_do[it_doI->first]; - -// it_doI->second.DO = all_DO_item.DO; - -// if(lnType_item.map_do.find(it_doI->first) == lnType_item.map_do.end()) -// { -// LOG_E("Data object %s not found in lnType_item.map_do", it_doI->first.c_str()); -// continue; -// } -// stru_DO &DO_item = lnType_item.map_do[it_doI->first]; - -// if(icd.map_doType.find(DO_item.type) == icd.map_doType.end()) -// { -// LOG_E("DOType %s not found in ICD", DO_item.type.c_str()); -// continue; -// } -// stru_DOType &DOType_item = icd.map_doType[DO_item.type]; - -// for(std::map::iterator it_dai = it_doI->second.map_daI.begin(); it_dai != it_doI->second.map_daI.end(); it_dai++) -// { -// if(all_DO_item.map_all_sdo.find(it_dai->first) != all_DO_item.map_all_sdo.end()) -// { -// if(DOType_item.map_sdo.find(it_dai->first) == DOType_item.map_sdo.end()) -// { -// LOG_E("SDO %s not found in DOType %s", it_dai->first.c_str(), DO_item.type.c_str()); -// continue; -// } - - -// } -// else if(all_DO_item.map_all_da.find(it_dai->first) != all_DO_item.map_all_da.end()) -// { -// if(DOType_item.map_da.find(it_dai->first) == DOType_item.map_da.end()) -// { -// LOG_E("DA %s not found in DOType %s", it_dai->first.c_str(), DO_item.type.c_str()); -// continue; -// } -// model_dataAttribute_value_init(server, icd, it_dai->first, it_dai->second, DOType_item.map_da[it_dai->first]); -// } -// } - -// } - - - - -// return 0; -// } - - - -// /* -// * iec61850s_value_update - update the values of the data attributes in the IED model -// input: server - the IedServer object -// icd - the ICD object, contains all the information of the model and values -// output: -// return: -// */ -// void iec61850s_value_update(IedServer server, stru_icd &icd) -// { -// if(NULL == server) -// { -// LOG_E("server is NULL"); -// return; -// } - -// // for each logical device in the IED model -// for(std::map::iterator it_ld = icd.ied.map_ldevice.begin(); it_ld!= icd.ied.map_ldevice.end(); it_ld++) -// { -// // LOG_I("ldname %s", it_ld->first.c_str()); -// // LLN0 -// if(icd.map_lnType.find(it_ld->second.ln0.lnType) != icd.map_lnType.end()) -// { -// // LOG_I("ln0 lnType %s", it_ld->second.ln0.lnType.c_str()); -// model_dataObject_value_init(server, icd, icd.map_lnType[it_ld->second.ln0.lnType], it_ld->second.ln0.map_all_do, it_ld->second.ln0.map_doI); -// } -// else -// { -// LOG_E("lnType %s not found in ICD", it_ld->second.ln0.lnType.c_str()); -// } - -// // for each logical node in the logical device -// for(std::vector::iterator it_ln = it_ld->second.vec_ln.begin(); it_ln != it_ld->second.vec_ln.end(); it_ln++) -// { -// if(icd.map_lnType.find(it_ln->lnType) != icd.map_lnType.end()) -// { -// // LOG_I("ln lnType %s", it_ln->lnType.c_str()); -// model_dataObject_value_init(server, icd, icd.map_lnType[it_ln->lnType], it_ln->map_all_do, it_ln->map_doI); -// } -// else -// { -// LOG_E("lnType %s not found in ICD", it_ln->lnType.c_str()); -// } -// } -// } -// } - +// ==================== 值初始化回调(mms_s_values_init 使用) ==================== LOCAL void mms_s_da_value_init_BOOLEAN(DataAttribute *da, void *value); LOCAL void mms_s_da_value_init_INT32(DataAttribute *da, void *value); @@ -456,11 +191,17 @@ LOCAL void mms_s_da_value_init_Unicode_String(DataAttribute *da, void *value); LOCAL void mms_s_da_value_init_Timestamp(DataAttribute *da, void *value); LOCAL void mms_s_da_value_init_Dbpos(DataAttribute *da, void *value); -LOCAL struct +/** + * bType 到 DA 初始化函数的分发表 + * + * 每个 bType 字符串对应一个初始化函数,用于将字符串值转换为 MmsValue 并设置到 DA。 + * NULL 表示该类型暂不需要显式初始化(libiec61850 有默认行为)。 + */ +LOCAL struct { const char *bType; void (*value_init)(DataAttribute *da, void *value); -}g_iec61850s_value_init_cb[] = +}g_iec61850s_value_init_cb[] = { {"BOOLEAN", mms_s_da_value_init_BOOLEAN}, {"INT8", NULL}, @@ -483,8 +224,8 @@ LOCAL struct {"VisString129", mms_s_da_value_init_Visible_String}, {"VisString255", mms_s_da_value_init_Visible_String}, {"Unicode255", mms_s_da_value_init_Unicode_String}, - {"Timestamp", mms_s_da_value_init_Timestamp}, - {"Quality", NULL}, + {"Timestamp", mms_s_da_value_init_Timestamp}, + {"Quality", NULL}, {"Check", NULL}, {"CodedEnum", NULL}, {"BitString", NULL}, @@ -499,6 +240,7 @@ LOCAL struct LOCAL int g_iec61850s_value_init_cb_count = sizeof(g_iec61850s_value_init_cb) / sizeof(g_iec61850s_value_init_cb[0]); +/** 初始化 BOOLEAN:根据 val 字符串 "true"/"1" 创建 MmsValue */ LOCAL void mms_s_da_value_init_BOOLEAN(DataAttribute *da, void *value) { if(NULL == da) @@ -506,7 +248,7 @@ LOCAL void mms_s_da_value_init_BOOLEAN(DataAttribute *da, void *value) LOG_E("da is NULL"); return; } - + if(value == NULL || strlen((char*)value) == 0 || strcmp((char*)value, "false") == 0 || strcmp((char*)value, "0") == 0) { da->mmsValue = MmsValue_newBoolean(false); @@ -518,6 +260,7 @@ LOCAL void mms_s_da_value_init_BOOLEAN(DataAttribute *da, void *value) } } +/** 初始化 INT32:根据 val 字符串创建 MmsValue */ LOCAL void mms_s_da_value_init_INT32(DataAttribute *da, void *value) { if(NULL == da) @@ -536,6 +279,7 @@ LOCAL void mms_s_da_value_init_INT32(DataAttribute *da, void *value) } } +/** 初始化 UINT32:根据 val 字符串创建 MmsValue */ LOCAL void mms_s_da_value_init_INT32U(DataAttribute *da, void *value) { if(NULL == da) @@ -554,6 +298,7 @@ LOCAL void mms_s_da_value_init_INT32U(DataAttribute *da, void *value) } } +/** 初始化 FLOAT32:根据 val 字符串创建 MmsValue */ LOCAL void mms_s_da_value_init_FLOAT32(DataAttribute *da, void *value) { if(NULL == da) @@ -572,6 +317,7 @@ LOCAL void mms_s_da_value_init_FLOAT32(DataAttribute *da, void *value) } } +/** 初始化 VisibleString:根据 val 字符串创建 MmsValue */ LOCAL void mms_s_da_value_init_Visible_String(DataAttribute *da, void *value) { if(NULL == da) @@ -590,6 +336,7 @@ LOCAL void mms_s_da_value_init_Visible_String(DataAttribute *da, void *value) } } +/** 初始化 Unicode String:根据 val 字符串创建 MmsValue */ LOCAL void mms_s_da_value_init_Unicode_String(DataAttribute *da, void *value) { if(NULL == da) @@ -608,6 +355,7 @@ LOCAL void mms_s_da_value_init_Unicode_String(DataAttribute *da, void *value) } } +/** 初始化 Timestamp:设为当前毫秒时间 */ LOCAL void mms_s_da_value_init_Timestamp(DataAttribute *da, void *value) { if(NULL == da) @@ -619,6 +367,10 @@ LOCAL void mms_s_da_value_init_Timestamp(DataAttribute *da, void *value) da->mmsValue = MmsValue_newUtcTimeByMsTime(Hal_getTimeInMs()); } +/** + * 初始化 Dbpos(双点位置): + * val=0 → INTERMEDIATE_STATE, 1 → OFF, 2 → ON, 3/其他 → BAD_STATE + */ LOCAL void mms_s_da_value_init_Dbpos(DataAttribute *da, void *value) { if(NULL == da) @@ -663,6 +415,19 @@ LOCAL void mms_s_da_value_init_Dbpos(DataAttribute *da, void *value) LOG_I("da %s-%s, value %s, pos %d", da->parent->name, da->name, (char *)value, pos); } +/** + * 递归初始化单个 DA 节点及其子 DA(Struct 类型) + * + * 1. 先递归处理所有子 DA + * 2. 当前 DA 类型判断: + * - Struct : 跳过(不直接设置值) + * - Enum : 根据 val 文本或序号在 EnumType 中查找对应值 + * - 其他 : 查 g_iec61850s_value_init_cb 表调用初始化函数 + * + * @param icd ICD 数据 + * @param da_name DA 名称 + * @param p_all_da all_DA 节点指针 + */ LOCAL void mms_s_da_values_init(stru_icd &icd, std::string da_name, stru_all_DA *p_all_da) { if(NULL == p_all_da) @@ -670,7 +435,7 @@ LOCAL void mms_s_da_values_init(stru_icd &icd, std::string da_name, stru_all_DA LOG_E("da_name %s p_all_da is NULL", da_name.c_str()); return; } - + for(std::map::iterator it_da = p_all_da->map_all_da.begin(); it_da != p_all_da->map_all_da.end(); it_da++) { mms_s_da_values_init(icd, it_da->first, it_da->second); @@ -703,9 +468,8 @@ LOCAL void mms_s_da_values_init(stru_icd &icd, std::string da_name, stru_all_DA p_all_da->DA->mmsValue = MmsValue_newIntegerFromInt32(atoi(p_all_da->val.c_str())); return; } - + p_all_da->DA->mmsValue = MmsValue_newIntegerFromInt32(0); - // LOG_I("Enum value %s not found in EnumType %s, set to 0", p_all_da->val.c_str(), p_all_da->type.c_str()); } else { @@ -714,13 +478,11 @@ LOCAL void mms_s_da_values_init(stru_icd &icd, std::string da_name, stru_all_DA if(p_all_da->bType.compare(g_iec61850s_value_init_cb[i].bType) == 0) { if(NULL != g_iec61850s_value_init_cb[i].value_init) - { - // LOG_I("BDAType %s, value %s", p_all_da->bType.c_str(), p_all_da->val.c_str()); + { g_iec61850s_value_init_cb[i].value_init(p_all_da->DA, (void *)p_all_da->val.c_str()); } else { - // LOG_E("btype %s, value_init is NULL", p_all_da->bType.c_str()); } return; } @@ -730,6 +492,14 @@ LOCAL void mms_s_da_values_init(stru_icd &icd, std::string da_name, stru_all_DA } } +/** + * 初始化 DO 及其所有子 DA/SDO 的值 + * + * 递归遍历 DO 树,为每个 DA 调用 mms_s_da_values_init()。 + * + * @param icd ICD 数据 + * @param map_all_do all_DO 映射表 + */ LOCAL void mms_s_do_values_init(stru_icd &icd, std::map &map_all_do) { for(std::map::iterator it_do = map_all_do.begin(); it_do != map_all_do.end(); it_do++) @@ -746,6 +516,15 @@ LOCAL void mms_s_do_values_init(stru_icd &icd, std::mapinitializer 被注册, + * 由 libiec61850 在 IedServer 创建过程中自动调用。 + */ void mms_s_values_init() { stru_icd *p_icd = mms_s_get_icd_ptr(); diff --git a/src/system/libiec61850m/src/iec61850m.cpp b/src/system/libiec61850m/src/iec61850m.cpp index e274b1f..17397dc 100644 --- a/src/system/libiec61850m/src/iec61850m.cpp +++ b/src/system/libiec61850m/src/iec61850m.cpp @@ -51,7 +51,7 @@ LOCAL std::map g_mms_m_ctrl_type_to_local_ctrl_type = { -LOCAL void mms_send_control(int fd, const char *saddr, const char *val, uint8_t mms_type, uint8_t ctrl_type) +LOCAL void mms_send_control(int fd, const char *saddr, const char *val, uint8_t mms_type, uint8_t ctrl_type, int set_zone) { stru_mms_m_event *p_event = NULL; @@ -73,6 +73,7 @@ LOCAL void mms_send_control(int fd, const char *saddr, const char *val, uint8_t p_event->app_fd = fd; memset(p_event->ied, 0, MMS_M_STR_LEN); snprintf(p_event->saddr, MMS_M_STR_LEN, "%s", saddr); + p_event->set_zone = set_zone; p_event->ctrl_type = ctrl_type; p_event->value_type = mms_type; // p_event->val.b = val == 0 ? false : true; @@ -236,8 +237,10 @@ LOCAL int iec61850m_signal_change_decode(std::string saddr, SIGNAL_CTRL_STEP ste step_str = "DIRECT"; if(signal_type == "co") ctrl_type = _MMS_M_EVENT_CO_DIRECT; - else + else if(signal_type == "ao") ctrl_type = _MMS_M_EVENT_AO_WRITE; + else if(signal_type == "param") + ctrl_type = _MMS_M_EVENT_PARAM_WRITE; break; case SIGNAL_CTRL_STEP::CANCEL: step_str = "CANCEL"; @@ -274,7 +277,7 @@ LOCAL void iec61850m_signal_co_change_callback(std::string saddr, SIGNAL_CTRL_ST { std::string val = dc_get_signal_val(p_data, data_type); MY_LOG_I("iec61850m_signal_change_callback val %s, ctrl_type %d", val.c_str(), ctrl_type); - mms_send_control(p_info->fd, saddr.c_str(), val.c_str(), mms_type, ctrl_type); + mms_send_control(p_info->fd, saddr.c_str(), val.c_str(), mms_type, ctrl_type, 0); } } } @@ -306,7 +309,7 @@ LOCAL void iec61850m_signal_ao_change_callback(std::string saddr, SIGNAL_CTRL_ST { std::string val = dc_get_signal_val(p_data, data_type); MY_LOG_I("iec61850m_signal_change_callback val %s, ctrl_type %d", val.c_str(), ctrl_type); - mms_send_control(p_info->fd, saddr.c_str(), val.c_str(), mms_type, ctrl_type); + mms_send_control(p_info->fd, saddr.c_str(), val.c_str(), mms_type, ctrl_type, 0); } } } @@ -322,6 +325,11 @@ LOCAL void iec61850m_signal_param_change_callback(std::string saddr, SIGNAL_CTRL return; } + if(ctrl_type != _MMS_M_EVENT_PARAM_WRITE) + { + return; + } + for(int i = 0; i < g_vec_iec61850m_info.size(); i++) { stru_iec61850m_info *p_info = &g_vec_iec61850m_info[i]; @@ -333,7 +341,7 @@ LOCAL void iec61850m_signal_param_change_callback(std::string saddr, SIGNAL_CTRL { std::string val = dc_get_signal_val(p_data, data_type); MY_LOG_I("iec61850m_signal_change_callback val %s, ctrl_type %d", val.c_str(), ctrl_type); - mms_send_control(p_info->fd, saddr.c_str(), val.c_str(), mms_type, ctrl_type); + mms_send_control(p_info->fd, saddr.c_str(), val.c_str(), mms_type, ctrl_type, setting_zone+1); } } } @@ -447,7 +455,9 @@ int iec61850m_signal_init(stru_mms_m_config &cfg) param.unit = cfg.p_param_sig[i].unit; uint8_t type_local = g_mms_m_type_to_local_type[cfg.p_param_sig[i].sig.data_type]; - ret |= dc_signal_param(cfg.p_param_sig[i].sig.saddr, cfg.p_param_sig[i].sig.desc, type_local, param, cfg.p_param_sig[i].ctrl_type, cfg.p_param_sig[i].sig.vec_p_data, cfg.p_param_sig[i].vec_p_default, iec61850m_signal_param_change_callback); + uint8_t ctrl_type_local = g_mms_m_ctrl_type_to_local_ctrl_type[cfg.p_param_sig[i].ctrl_type]; + + ret |= dc_signal_param(cfg.p_param_sig[i].sig.saddr, cfg.p_param_sig[i].sig.desc, type_local, param, ctrl_type_local, cfg.p_param_sig[i].sig.vec_p_data, cfg.p_param_sig[i].vec_p_default, iec61850m_signal_param_change_callback); } if(ret != 0) diff --git a/src/system/libiec61850s/src/iec61850s.cpp b/src/system/libiec61850s/src/iec61850s.cpp index f7ab2b0..c674bdc 100644 --- a/src/system/libiec61850s/src/iec61850s.cpp +++ b/src/system/libiec61850s/src/iec61850s.cpp @@ -178,6 +178,7 @@ void iec61850s_param_callback(stru_mms_s_param *p_param, const char *p_data, int int app_iec61850s_init1(void *arg) { mms_s_dbg_switch(true); + mms_s_file_path_set("/mnt/RTU/test/"); mms_s_control_register(vec_control.data(), vec_control.size(), iec61850s_control_callback); diff --git a/test/file/MMS/PCS.icd b/test/file/MMS/PCS.icd new file mode 100644 index 0000000..bc0cdd8 --- /dev/null +++ b/test/file/MMS/PCS.icd @@ -0,0 +1,7684 @@ + + +
+ + + 10 + +
+

1,3,9999,33

+

33

+

00000001

+

0001

+

0001

+

127.0.0.1

+
+
+
+ + 10 + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + 0 + + + + + SGCC MODEL:2009 + + + + + + + + 0 + + + 30000 + + + + + + 1 + + + 30 + + + 1 + + + 定值区号 + + + SGCC MODEL:2009 + + + + + SGCC MODEL:2009 + + + + + + 被保护设备 + + + SGCC MODEL:2009 + + + + + + + 0 + + + + + + 过流Ⅰ段动作 + + + + + + + + + A + + + + + + 0.05 + + + + + 150 + + + + + 0.001 + + + + 过流Ⅰ段定值 + + + + + + 0 + + + 100000 + + + 1 + + + 过流Ⅰ段时间 + + + + + + 0 + + + 1 + + + 1 + + + 过流Ⅰ段经方向闭锁 + + + + + SGCC MODEL:2009 + + + + + SGCC MODEL:2009 + + + + + SGCC MODEL:2009 + + + + + SGCC MODEL:2009 + + + + + SGCC MODEL:2009 + + + + + SGCC MODEL:2009 + + + + + SGCC MODEL:2009 + + + + + SGCC MODEL:2009 + + + + + + 过流Ⅰ段经低压闭锁 + + + SGCC MODEL:2009 + + + + + SGCC MODEL:2009 + + + + + SGCC MODEL:2009 + + + + + SGCC MODEL:2009 + + + + + SGCC MODEL:2009 + + + + + + 过流Ⅰ段投入 + + + SGCC MODEL:2009 + + + + + SGCC MODEL:2009 + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + 0 + + + + + SGCC MODEL:2009 + + + + + + + + 0 + + + 30000 + + + + + SGCC MODEL:2009 + + + + + SGCC MODEL:2009 + + + + + SGCC MODEL:2009 + + + + + + + 0 + + + + + SGCC MODEL:2009 + + + + + + + + + + + + + + + 4 + + + 30000 + + + 遥控1双位置输出 + + + + + SGCC MODEL:2009 + + + + + SGCC MODEL:2009 + + + + + SGCC MODEL:2009 + + + + + SGCC MODEL:2009 + + + + + SGCC MODEL:2009 + + + + + + + 0 + + + + + SGCC MODEL:2009 + + + + + + + + + + + + + + + 4 + + + 30000 + + + 遥控2双位置输出 + + + + + SGCC MODEL:2009 + + + + + SGCC MODEL:2009 + + + + + SGCC MODEL:2009 + + + + + SGCC MODEL:2009 + + + + + SGCC MODEL:2009 + + + + + + + 0 + + + + + SGCC MODEL:2009 + + + + + + + + + + + + + + + 4 + + + 30000 + + + 遥控3双位置输出 + + + + + SGCC MODEL:2009 + + + + + SGCC MODEL:2009 + + + + + SGCC MODEL:2009 + + + + + SGCC MODEL:2009 + + + + + SGCC MODEL:2009 + + + + + + + 0 + + + + + SGCC MODEL:2009 + + + + + + + + + + + + + + + 4 + + + 30000 + + + 遥控4双位置输出 + + + + + SGCC MODEL:2009 + + + + + SGCC MODEL:2009 + + + + + SGCC MODEL:2009 + + + + + SGCC MODEL:2009 + + + + + SGCC MODEL:2009 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + + + + + + + + + + + + + + + + IEC 61850-7-4:2003 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SGCC MODEL:2009 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SGCC MODEL:2009 + + + + + + + SGCC MODEL:2009 + + + + + + + + + + + + SGCC MODEL:2009 + + + + + + + + + + SGCC MODEL:2009 + + + + + + + SGCC MODEL:2009 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SGCC MODEL:2009 + + + + + + + + + + + + + + + + + + + + + + + + + SGCC MODEL:2009 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SGCC MODEL:2009 + + + + + + + SGCC MODEL:2009 + + + SGCC MODEL:2009 + + + SGCC MODEL:2009 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SGCC MODEL:2009 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SGCC MODEL:2009 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SGCC MODEL:2009 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + on + blocked + test + test/blocked + off + + + status-only + direct-with-normal-security + sbo-with-normal-security + direct-with-enhanced-security + sbo-with-enhanced-security + + + on + blocked + test + test/blocked + off + + + Ok + Warning + Alarm + + + not-supported + bay-control + station-control + remote-control + automatic-bay + automatic-station + automatic-remote + maintenance + process + + + pulse + persistent + + + + m + kg + s + A + K + mol + cd + deg + rad + sr + Gy + q + °C + Sv + F + C + S + H + V + ohm + J + N + Hz + lx + Lm + Wb + T + W + Pa + m² + m³ + m/s + m/s² + m³/s + m/m³ + M + kg/m³ + m²/s + W/m K + J/K + ppm + 1/s + rad/s + VA + Watts + VAr + phi + cos_phi + Vs + V² + As + A² + A²t + VAh + Wh + VArh + V/Hz + Hz/s + + + y + z + a + f + p + n + µ + m + c + d + + da + h + k + M + G + T + P + E + Z + Y + + + operate-once + operate-many + + + unknown + forward + backward + both + + + Dead Line, Dead Bus + Live Line, Dead Bus + Dead Line, Live Bus + Dead Line, Dead Bus OR Live Line, Dead Bus + Dead Line, Dead Bus OR Dead Line, Live Bus + Live Line, Dead Bus OR Dead Line, Live Bus + Dead Line, Dead Bus OR Live Line, Dead Bus OR Dead Line, Live Bus + + + Ready + InProgress + Successful + + + V + A + other + + + Va + Vb + Vc + Aa + Ab + Ac + Vab + Vbc + Vca + Vother + Aother + + + pos-neg-zero + dir-quad-zero + + + diff --git a/test/file/MMS/prj.xml b/test/file/MMS/prj.xml index b00f296..ac137d8 100644 --- a/test/file/MMS/prj.xml +++ b/test/file/MMS/prj.xml @@ -29,7 +29,7 @@ - +