1、mms客户端时间类型增加参数读写枚举;

2、mms服务端增加
This commit is contained in:
ypc 2026-05-27 17:16:45 +08:00
parent 04976dd01b
commit 3d59800656
22 changed files with 9876 additions and 1137 deletions

1
.gitignore vendored
View File

@ -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/

View File

@ -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
};

View File

@ -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 <stdint.h>
#include <stdbool.h>
#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 */

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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 <map>
@ -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<std::string> 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<std::string> 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<std::string> vec_bda;
std::map<std::string, stru_BDA> map_bda; // key:name
}stru_DAType;
typedef struct
/**
* 枚举类型(EnumType)定义
* @field vec_ord 枚举序号列表
* @field map_enumVal 枚举值映射表(key:序号, value:枚举文本)
*/
typedef struct
{
std::vector<int> vec_ord;
std::map<int, std::string> 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<std::string> vec_sdI;
@ -108,6 +233,14 @@ typedef struct _SDI
std::map<std::string, stru_DAI> 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<std::string, stru_DAI> 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<std::string, stru_all_DO *> 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<stru_FCDA> 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<stru_LN> vec_ln;
std::vector<stru_LN> 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<std::string, stru_setting_info> 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<std::string> 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);

View File

@ -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);

View File

@ -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);

View File

@ -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();

View File

@ -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 <signal.h>
@ -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;
}

View File

@ -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<stru_mms_s_control> 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<stru_contorl_DO>::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<stru_mms_s_control>::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<stru_contorl_DO>::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);

View File

@ -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())

View File

@ -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<std::string, stru_DataSet> &map_dataset, std::vector<std::string> &vec_dataset)
{
if(nullptr == ele_ln0)
@ -136,12 +161,12 @@ LOCAL int parse_DataSet(XMLElement *ele_ln0, std::map<std::string, stru_DataSet>
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<std::string, stru_ReportControl> &map_reportControl, std::vector<std::string> &vec_reportControl)
{
if(nullptr == ele_ln0)
@ -258,7 +284,7 @@ LOCAL int parse_ReportControl(XMLElement *ele_ln0, std::map<std::string, stru_Re
reportControl.buffered = (temp_string == "true") ? true : false;
temp_str = ele->Attribute(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<std::string, stru_Re
return 0;
}
/*
* parse_DAI - parse DAI element
input: ele - dai element
output: map_daI - map to store DA data structure
return: 0 - success, -1 - failure
*/
/**
* 解析 DAI 元素(数据属性实例)
* @param ele 父 XML 元素(DOI / SDI)
* @param map_daI 输出:DAI 映射表
* @param vec_daI 输出:DAI 名称列表
* @return 0 成功,-1 失败
*/
LOCAL int parse_DAI(XMLElement *ele, std::map<std::string, stru_DAI> &map_daI, std::vector<std::string> &vec_daI)
{
if(nullptr == ele)
@ -294,7 +321,7 @@ LOCAL int parse_DAI(XMLElement *ele, std::map<std::string, stru_DAI> &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<std::string, stru_DAI> &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<std::string, stru_SDI> &map_sdI, std::vector<std::string> &vec_sdI)
{
if(nullptr == ele_doi)
@ -341,12 +369,13 @@ LOCAL int parse_SDI(XMLElement *ele_doi, std::map<std::string, stru_SDI> &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<std::string, stru_DOI> &map_doI, std::vector<std::string> &vec_doI)
{
if(nullptr == ele_ln)
@ -366,7 +395,7 @@ LOCAL int parse_DOI(XMLElement *ele_ln, std::map<std::string, stru_DOI> &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<std::string, stru_DOI> &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<stru_LN> &vec_ln)
{
if(nullptr == ele_ldevice)
@ -465,25 +500,28 @@ LOCAL int parse_LN(XMLElement *ele_ldevice, std::vector<stru_LN> &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<std::string, stru_LNodeType> &map_lnType, std::vector<std::string> &vec_lnType)
{
if(nullptr == dataTypeTemplates)
@ -616,12 +662,17 @@ LOCAL int parse_LNodeType(XMLElement *dataTypeTemplates, std::map<std::string, s
return 0;
}
/*
* parse_DOType - parse DOType element
input: dataTypeTemplates - dataTypeTemplates element
output: map_doType - map to store DOType data structure
return: 0 - success, -1 - failure
*/
/**
* 解析 DOType 元素(数据对象类型模板)
*
* 遍历每个 DOType 的子 DA 和 SDO 元素。
* DA 中记录 bType/type/dchg/qchg/fc/text(text 从 Val 子元素获取)。
*
* @param dataTypeTemplates DataTypeTemplates XML 元素
* @param map_doType 输出:DOType 映射表
* @param vec_doType 输出:DOType id 列表
* @return 0 成功,-1 失败
*/
LOCAL int parse_DOType(XMLElement *dataTypeTemplates, std::map<std::string, stru_DOType> &map_doType, std::vector<std::string> &vec_doType)
{
if(nullptr == dataTypeTemplates)
@ -652,7 +703,7 @@ LOCAL int parse_DOType(XMLElement *dataTypeTemplates, std::map<std::string, stru
DA.dchg = ele_da->Attribute(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<std::string, stru
return 0;
}
/*
* parse_DAType - parse DAType element
input: dataTypeTemplates - dataTypeTemplates element
output: map_daType - map to store DAType data structure
return: 0 - success, -1 - failure
*/
/**
* 解析 DAType 元素(数据属性类型模板)
*
* 遍历每个 DAType 的子 BDA 元素,记录 bType/type。
*
* @param dataTypeTemplates DataTypeTemplates XML 元素
* @param map_daType 输出:DAType 映射表
* @param vec_daType 输出:DAType id 列表
* @return 0 成功,-1 失败
*/
LOCAL int parse_DAType(XMLElement *dataTypeTemplates, std::map<std::string, stru_DAType> &map_daType, std::vector<std::string> &vec_daType)
{
if(nullptr == dataTypeTemplates)
@ -727,12 +782,16 @@ LOCAL int parse_DAType(XMLElement *dataTypeTemplates, std::map<std::string, stru
return 0;
}
/*
* parse_EnumType - parse EnumType element
input: dataTypeTemplates - dataTypeTemplates element
output: map_enumType - map to store EnumType data structure
return: 0 - success, -1 - failure
*/
/**
* 解析 EnumType 元素(枚举类型模板)
*
* 遍历每个 EnumType 的子 EnumVal 元素,记录 ord 和文本值。
*
* @param dataTypeTemplates DataTypeTemplates XML 元素
* @param map_enumType 输出:EnumType 映射表
* @param vec_enumType 输出:EnumType id 列表
* @return 0 成功,-1 失败
*/
LOCAL int parse_EnumType(XMLElement *dataTypeTemplates, std::map<std::string, stru_EnumType> &map_enumType, std::vector<std::string> &vec_enumType)
{
if(nullptr == dataTypeTemplates)
@ -764,6 +823,15 @@ LOCAL int parse_EnumType(XMLElement *dataTypeTemplates, std::map<std::string, st
return 0;
}
/**
* 从 DAType 到 DAType 反向传播 fc/dchg/qchg 属性
*
* 当一个 BDA 的 type 指向另一个 DAType 时,将该 BDA 的 fc/dchg/qchg
* 传播到目标 DAType 的所有 BDA 上。这样保证嵌套 Struct 类型的 BDA
* 能正确继承父级的 FC 等属性。
*
* @param map_daType DAType 映射表
*/
LOCAL void parse_BDA_fc(std::map<std::string, stru_DAType> &map_daType)
{
for(std::map<std::string, stru_DAType>::iterator it_da = map_daType.begin(); it_da != map_daType.end(); it_da++)
@ -793,6 +861,15 @@ LOCAL void parse_BDA_fc(std::map<std::string, stru_DAType> &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<std::string, stru_DOType> &map_doType, std::map<std::string, stru_DAType> &map_daType)
{
for(std::map<std::string, stru_DOType>::iterator it_do = map_doType.begin(); it_do != map_doType.end(); it_do++)
@ -819,12 +896,16 @@ LOCAL void parse_BDA_fc(std::map<std::string, stru_DOType> &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<std::string> &vec_sdI, std::map<std::string, stru_SDI> &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<std::string> &vec_daI, std::map<std::string, stru_DAI> &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<std::string>::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<std::string> &vec_sdI, std::map<std::string, stru_SDI> &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<std::string>::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<std::string> &vec_doI, std::map<std::string, stru_DOI> &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++)
{

File diff suppressed because it is too large Load Diff

View File

@ -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 <math.h>
#include "myMms_s.h"
#include "mms_s_param.h"
// #include "cJSON.h"
LOCAL std::vector<stru_mms_s_param> 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<stru_mms_s_param>::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 &param = 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<std::string, stru_LDevice>::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;
}
}

View File

@ -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 <math.h>
#include "myMms_s.h"
#include "mms_s_setting.h"
// #include "cJSON.h"
LOCAL std::vector<stru_mms_s_setting> 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<stru_mms_s_setting>::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<stru_mms_s_setting>::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())

View File

@ -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<std::string, stru_DAI>::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<int, std::string>::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<std::string, stru_DAI>::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<int, std::string>::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<std::string, stru_all_DO *> &map_all_do, std::map<std::string, stru_DOI> &map_doI)
// {
// for(std::map<std::string, stru_DOI>::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<std::string, stru_DAI>::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<std::string, stru_LDevice>::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<stru_LN>::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<std::string, stru_all_DA *>::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<std::string, stru_all_DO *> &map_all_do)
{
for(std::map<std::string, stru_all_DO *>::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::map<std::string, stru_all_DO
}
}
/**
* 初始化 IED 模型中所有数据属性值(IedModel initializer 回调)
*
* 遍历所有逻辑设备、LLN0、普通 LN 的 DO 树,
* 为每个 DA 根据 ICD 中配置的默认值创建 MmsValue。
*
* 此函数作为 icd.ied.model->initializer 被注册,
* 由 libiec61850 在 IedServer 创建过程中自动调用。
*/
void mms_s_values_init()
{
stru_icd *p_icd = mms_s_get_icd_ptr();

View File

@ -51,7 +51,7 @@ LOCAL std::map<uint8_t, uint8_t> 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)

View File

@ -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);

7684
test/file/MMS/PCS.icd Normal file

File diff suppressed because it is too large Load Diff

View File

@ -29,7 +29,7 @@
<Item no="1" type="13" ctlModel="4" saddr="iec61850m.prj.ao.1" desc="参数02" LDev="PROT" LNode="LPHD1" DoName="DeviceName" fc="2" min="" max="" step="" unit="" value="1" default="1"/>
</Ao>
<Param desc="定值">
<Item no="0" type="5" ctlModel="4" saddr="iec61850m.prj.param.0" desc="定值01" LDev="PROT" LNode="PTOC1" DoName="StrVal.setMag.f" fc="2" min="0.05" max="150" step="0.001" unit="A" num="2">
<Item no="0" type="6" ctlModel="4" saddr="iec61850m.prj.param.0" desc="定值01" LDev="PROT" LNode="PTOC1" DoName="StrVal.setMag.f" fc="2" min="0.05" max="150" step="0.001" unit="A" num="2">
<SubItem value="15" default="15"/>
<SubItem value="27" default="27"/>
</Item>