parent
04976dd01b
commit
3d59800656
|
|
@ -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/
|
||||
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,15 @@
|
|||
/**
|
||||
* @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>
|
||||
|
|
@ -7,95 +19,129 @@
|
|||
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 */
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
@ -1,6 +1,22 @@
|
|||
/**
|
||||
* @file mms_s_file.h
|
||||
* @brief IEC 61850 MMS 文件传输服务初始化接口
|
||||
*
|
||||
* @details 配置文件服务的基础路径,安装文件访问控制回调,
|
||||
* 限制客户端的文件删除/重命名等操作权限。
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "mms_s.h"
|
||||
|
||||
/**
|
||||
* 初始化 MMS 文件传输服务
|
||||
*
|
||||
* 安装文件访问处理回调(限制删除/重命名操作)
|
||||
* 和连接事件回调(打印连接/断开日志)。
|
||||
*
|
||||
* @param server IED 服务器指针
|
||||
* @return 0 成功,-1 失败
|
||||
*/
|
||||
int file_init(IedServer server);
|
||||
|
||||
|
|
|
|||
|
|
@ -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,12 +47,23 @@
|
|||
#include "iec61850_server.h"
|
||||
|
||||
|
||||
/**
|
||||
* 数据对象(DO)定义(DataTypeTemplates 中使用)
|
||||
* @field desc 描述
|
||||
* @field type DO 类型(指向 DOType id)
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
std::string desc;
|
||||
std::string type;
|
||||
}stru_DO;
|
||||
|
||||
/**
|
||||
* 逻辑节点类型(LNodeType)定义
|
||||
* @field lnClass 逻辑节点类(如 "LLN0", "LPHD", "MMXU")
|
||||
* @field vec_do DO 名称列表(保持顺序)
|
||||
* @field map_do DO 映射表(key: DO 名称)
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
std::string lnClass;
|
||||
|
|
@ -21,6 +72,15 @@ typedef struct
|
|||
}stru_LNodeType;
|
||||
|
||||
|
||||
/**
|
||||
* 数据属性(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;
|
||||
|
|
@ -31,6 +91,14 @@ typedef struct
|
|||
std::string text;
|
||||
}stru_DA;
|
||||
|
||||
/**
|
||||
* 数据对象类型(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;
|
||||
|
|
@ -41,6 +109,14 @@ typedef struct
|
|||
}stru_DOType;
|
||||
|
||||
|
||||
/**
|
||||
* 基本数据属性(BDA)定义
|
||||
* @field bType 基本数据类型
|
||||
* @field type 类型引用
|
||||
* @field fc 功能约束(从上层 DA 继承填充)
|
||||
* @field dchg 数据变化触发标志
|
||||
* @field qchg 品质变化触发标志
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
std::string bType;
|
||||
|
|
@ -50,6 +126,11 @@ typedef struct
|
|||
std::string qchg;
|
||||
}stru_BDA;
|
||||
|
||||
/**
|
||||
* 数据属性类型(DAType)定义
|
||||
* @field vec_bda BDA 名称列表(保持顺序)
|
||||
* @field map_bda BDA 映射表(key: BDA 名称)
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
std::vector<std::string> vec_bda;
|
||||
|
|
@ -57,6 +138,11 @@ typedef struct
|
|||
}stru_DAType;
|
||||
|
||||
|
||||
/**
|
||||
* 枚举类型(EnumType)定义
|
||||
* @field vec_ord 枚举序号列表
|
||||
* @field map_enumVal 枚举值映射表(key:序号, value:枚举文本)
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
std::vector<int> vec_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,6 +250,16 @@ typedef struct _DOI
|
|||
std::map<std::string, stru_DAI> map_daI; // key:name
|
||||
}stru_DOI;
|
||||
|
||||
/**
|
||||
* 控制 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;
|
||||
|
|
@ -129,6 +272,18 @@ typedef struct
|
|||
}stru_contorl_DO;
|
||||
|
||||
|
||||
/**
|
||||
* 逻辑节点(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;
|
||||
|
|
@ -142,6 +297,17 @@ typedef struct
|
|||
std::map<std::string, stru_all_DO *> map_all_do; // key:name
|
||||
}stru_LN;
|
||||
|
||||
/**
|
||||
* 功能约束数据属性(FCDA),数据集中的成员
|
||||
* @field fcda libiec61850 DataSetEntry 指针
|
||||
* @field ldInst 逻辑设备实例
|
||||
* @field lnClass 逻辑节点类
|
||||
* @field lnInst 逻辑节点实例
|
||||
* @field prefix 前缀
|
||||
* @field doName 数据对象名称
|
||||
* @field daName 数据属性名称
|
||||
* @field fc 功能约束
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
DataSetEntry *fcda;
|
||||
|
|
@ -155,6 +321,12 @@ typedef struct
|
|||
}stru_FCDA;
|
||||
|
||||
|
||||
/**
|
||||
* 数据集(DataSet)
|
||||
* @field dataset libiec61850 DataSet 指针
|
||||
* @field desc 描述
|
||||
* @field vec_fcda FCDA 列表
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
DataSet *dataset;
|
||||
|
|
@ -162,6 +334,13 @@ typedef struct
|
|||
std::vector<stru_FCDA> vec_fcda;
|
||||
}stru_DataSet;
|
||||
|
||||
/**
|
||||
* 触发选项(TrgOps)
|
||||
* @field dchg 数据变化触发
|
||||
* @field qchg 品质变化触发
|
||||
* @field dupd 数据更新触发
|
||||
* @field period 周期触发
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
bool dchg;
|
||||
|
|
@ -170,6 +349,16 @@ typedef struct
|
|||
bool period;
|
||||
}stru_TrgOps;
|
||||
|
||||
/**
|
||||
* 报告选项字段(OptFields)
|
||||
* @field seqNum 序号
|
||||
* @field timeStamp 时间戳
|
||||
* @field dataSet 数据集引用
|
||||
* @field reasonCode 原因码
|
||||
* @field dataRef 数据引用
|
||||
* @field entryID 条目ID
|
||||
* @field configRef 配置版本引用
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
bool seqNum;
|
||||
|
|
@ -181,6 +370,19 @@ typedef struct
|
|||
bool configRef;
|
||||
}stru_OptFields;
|
||||
|
||||
/**
|
||||
* 报告控制块(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;
|
||||
|
|
@ -195,12 +397,34 @@ typedef struct
|
|||
int RptEnabled_max;
|
||||
}stru_ReportControl;
|
||||
|
||||
/**
|
||||
* 定值控制块(SettingControl)
|
||||
* @field actSG 当前激活定值组号
|
||||
* @field numOfSGs 定值组总数
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
uint8_t actSG;
|
||||
uint8_t numOfSGs;
|
||||
}stru_SettingControl;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
|
@ -216,6 +440,13 @@ typedef struct
|
|||
stru_SettingControl settingControl;
|
||||
}stru_LN0;
|
||||
|
||||
/**
|
||||
* 逻辑设备(LDevice)实例
|
||||
* @field Ldevice libiec61850 LogicalDevice 指针
|
||||
* @field sgcb libiec61850 SettingGroupControlBlock 指针
|
||||
* @field ln0 所属 LLN0
|
||||
* @field vec_ln 普通 LN 列表
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
LogicalDevice *Ldevice;
|
||||
|
|
@ -224,11 +455,22 @@ typedef struct
|
|||
std::vector<stru_LN> vec_ln;
|
||||
}stru_LDevice;
|
||||
|
||||
/**
|
||||
* sAddr(短地址)到模型节点的映射点
|
||||
* @field node libiec61850 模型节点指针
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
ModelNode *node;
|
||||
}stru_saddr_point;
|
||||
|
||||
/**
|
||||
* 定值信息
|
||||
* @field DA 数据属性指针
|
||||
* @field type 数据类型
|
||||
* @field str_val 字符串值
|
||||
* @field val 整数值
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
DataAttribute *DA;
|
||||
|
|
@ -237,6 +479,19 @@ typedef struct
|
|||
int val;
|
||||
}stru_setting_info;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
|
@ -251,6 +506,21 @@ typedef struct
|
|||
std::map<std::string, stru_setting_info> map_setting_info; // key:name
|
||||
}stru_ied;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
|
@ -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);
|
||||
|
|
@ -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"
|
||||
|
||||
/**
|
||||
* 根据 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);
|
||||
|
|
@ -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"
|
||||
|
||||
/**
|
||||
* 初始化参数(定值组)管理功能
|
||||
*
|
||||
* 为每个逻辑设备的 SGCB 安装以下回调:
|
||||
* - 激活定值组变更 → 将对应的参数数据加载到 SG DA
|
||||
* - 编辑定值组变更 → 将对应的参数数据加载到 SE DA
|
||||
* - 编辑确认 → 回读 SE DA 的当前值到参数数据
|
||||
* - 读访问控制 → 允许读取
|
||||
*
|
||||
* @param server IED 服务器指针
|
||||
* @param icd ICD 数据结构引用
|
||||
* @return 0 成功,-1 失败
|
||||
*/
|
||||
int param_init(IedServer server, stru_icd &icd);
|
||||
|
|
@ -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();
|
||||
|
|
@ -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,6 +165,14 @@ 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;
|
||||
|
|
@ -132,7 +183,6 @@ LOCAL void *mms_s_run_task(void *parameter)
|
|||
IedServer_unlockDataModel(gp_iedServer);
|
||||
|
||||
|
||||
|
||||
Thread_sleep(100);
|
||||
}
|
||||
|
||||
|
|
@ -147,6 +197,28 @@ LOCAL void *mms_s_run_task(void *parameter)
|
|||
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))
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
@ -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);
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
@ -85,11 +109,12 @@ 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)
|
||||
{
|
||||
|
|
@ -136,11 +161,11 @@ 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)
|
||||
{
|
||||
|
|
@ -165,11 +190,11 @@ 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)
|
||||
{
|
||||
|
|
@ -196,11 +221,11 @@ 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)
|
||||
{
|
||||
|
|
@ -222,11 +247,12 @@ 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)
|
||||
{
|
||||
|
|
@ -270,11 +296,12 @@ 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)
|
||||
{
|
||||
|
|
@ -308,11 +335,12 @@ 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)
|
||||
{
|
||||
|
|
@ -341,11 +369,12 @@ 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)
|
||||
{
|
||||
|
|
@ -377,11 +406,11 @@ 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)
|
||||
{
|
||||
|
|
@ -406,11 +435,14 @@ 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)
|
||||
{
|
||||
|
|
@ -440,11 +472,14 @@ 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)
|
||||
{
|
||||
|
|
@ -478,11 +513,14 @@ LOCAL int parse_LN(XMLElement *ele_ldevice, std::vector<stru_LN> &vec_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)
|
||||
{
|
||||
|
|
@ -509,11 +547,15 @@ 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)
|
||||
{
|
||||
|
|
@ -566,11 +608,15 @@ 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)
|
||||
{
|
||||
|
|
@ -616,11 +662,16 @@ 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)
|
||||
{
|
||||
|
|
@ -685,11 +736,15 @@ 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)
|
||||
{
|
||||
|
|
@ -727,11 +782,15 @@ 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)
|
||||
{
|
||||
|
|
@ -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,11 +896,15 @@ 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)
|
||||
{
|
||||
|
|
@ -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())
|
||||
|
|
@ -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())
|
||||
|
|
@ -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())
|
||||
|
|
@ -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,11 +1235,17 @@ 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)
|
||||
{
|
||||
|
|
@ -1182,6 +1323,10 @@ ERR_EXIT:
|
|||
|
||||
|
||||
|
||||
/**
|
||||
* 递归打印 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,11 +1344,9 @@ 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)
|
||||
{
|
||||
|
|
@ -1224,11 +1367,15 @@ 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)
|
||||
{
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -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,6 +103,7 @@ void param_update_STRING(IedServer server, DataAttribute* dataAttribute, std::st
|
|||
IedServer_updateAttributeValue(server, dataAttribute, MmsValue_newMmsString((char *)val.c_str()));
|
||||
}
|
||||
|
||||
/** MMS 类型到更新函数的分发表 */
|
||||
LOCAL struct
|
||||
{
|
||||
MmsType type;
|
||||
|
|
@ -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)
|
||||
|
|
@ -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;
|
||||
ModelNode *p_node = (ModelNode *)node;
|
||||
|
||||
DataAttribute *da_first = (DataAttribute *)da_node->firstChild;
|
||||
while(p_node != NULL)
|
||||
{
|
||||
p_node = p_node->parent;
|
||||
if(p_node == NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
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_first->mmsValue);
|
||||
min = MmsValue_toFloat(da_child->mmsValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
min = (float)MmsValue_toInt32(da_first->mmsValue);
|
||||
min = (float)MmsValue_toInt32(da_child->mmsValue);
|
||||
}
|
||||
}
|
||||
else if(strcmp(da_first->name, "maxVal") == 0)
|
||||
{
|
||||
if(type == MMS_FLOAT)
|
||||
{
|
||||
max = MmsValue_toFloat(da_first->mmsValue);
|
||||
max = MmsValue_toFloat(da_child->mmsValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
max = (float)MmsValue_toInt32(da_first->mmsValue);
|
||||
max = (float)MmsValue_toInt32(da_child->mmsValue);
|
||||
}
|
||||
}
|
||||
else if(strcmp(da_first->name, "stepSize") == 0)
|
||||
{
|
||||
if(type == MMS_FLOAT)
|
||||
{
|
||||
step = MmsValue_toFloat(da_first->mmsValue);
|
||||
step = MmsValue_toFloat(da_child->mmsValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
step = (float)MmsValue_toInt32(da_first->mmsValue);
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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,20 +521,21 @@ 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);
|
||||
|
|
@ -417,22 +560,24 @@ 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);
|
||||
}
|
||||
|
||||
|
||||
/** MMS 类型到读取函数的分发表 */
|
||||
LOCAL struct
|
||||
{
|
||||
MmsType type;
|
||||
void (*get_value)(DataAttribute *node, std::string &val);
|
||||
void (*get_value)(DataAttribute *node_SE, DataAttribute *node_SG, std::string &val);
|
||||
}g_param_get_funcs[] =
|
||||
{
|
||||
{MMS_BOOLEAN, param_get_BOOLEAN},
|
||||
|
|
@ -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)
|
||||
|
|
@ -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())
|
||||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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,6 +106,7 @@ void setting_update_STRING(IedServer server, DataAttribute* dataAttribute, std::
|
|||
IedServer_updateAttributeValue(server, dataAttribute, MmsValue_newMmsString((char *)val.c_str()));
|
||||
}
|
||||
|
||||
/** MMS 类型到更新函数的分发表 */
|
||||
LOCAL struct
|
||||
{
|
||||
MmsType type;
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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,6 +401,7 @@ LOCAL int setting_get_STRING(DataAttribute *node, MmsValue* value, std::string &
|
|||
}
|
||||
|
||||
|
||||
/** MMS 类型到校验/读取函数的分发表 */
|
||||
LOCAL struct
|
||||
{
|
||||
MmsType type;
|
||||
|
|
@ -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,7 +446,6 @@ 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;
|
||||
|
|
@ -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,6 +540,7 @@ LOCAL void setting_handle_init(IedServer server, stru_icd &icd)
|
|||
}
|
||||
}
|
||||
|
||||
/** 调试用:打印已注册的定值信息 */
|
||||
LOCAL void setting_values_show()
|
||||
{
|
||||
for(int i = 0; i < vec_settings.size(); i++)
|
||||
|
|
@ -478,6 +551,13 @@ LOCAL void setting_values_show()
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册定值列表
|
||||
* @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())
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
@ -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,6 +191,12 @@ 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);
|
||||
|
||||
/**
|
||||
* bType 到 DA 初始化函数的分发表
|
||||
*
|
||||
* 每个 bType 字符串对应一个初始化函数,用于将字符串值转换为 MmsValue 并设置到 DA。
|
||||
* NULL 表示该类型暂不需要显式初始化(libiec61850 有默认行为)。
|
||||
*/
|
||||
LOCAL struct
|
||||
{
|
||||
const char *bType;
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -705,7 +470,6 @@ LOCAL void mms_s_da_values_init(stru_icd &icd, std::string da_name, stru_all_DA
|
|||
}
|
||||
|
||||
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
|
||||
{
|
||||
|
|
@ -715,12 +479,10 @@ LOCAL void mms_s_da_values_init(stru_icd &icd, std::string da_name, stru_all_DA
|
|||
{
|
||||
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();
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Reference in New Issue