1497 lines
48 KiB
Markdown
1497 lines
48 KiB
Markdown
# libiec61850 服务端开发手册
|
||
|
||
> 基于 libiec61850 v1.5.3 完整 API 参考。本文档旨在做到脱离代码即可完成服务端开发。
|
||
|
||
---
|
||
|
||
## 目录
|
||
|
||
1. [架构概述](#1-架构概述)
|
||
2. [数据模型](#2-数据模型)
|
||
3. [服务器生命周期](#3-服务器生命周期)
|
||
4. [IedServerConfig 配置详解](#4-iedserverconfig-配置详解)
|
||
5. [服务器创建与销毁](#5-服务器创建与销毁)
|
||
6. [数据模型锁定与更新](#6-数据模型锁定与更新)
|
||
7. [数据读取函数](#7-数据读取函数)
|
||
8. [连接管理与认证](#8-连接管理与认证)
|
||
9. [控制模型 (Control)](#9-控制模型-control)
|
||
10. [报告控制块 (RCB)](#10-报告控制块-rcb)
|
||
11. [GOOSE 发布](#11-goose-发布)
|
||
12. [GOOSE 控制块 (GoCB)](#12-goose-控制块-gocb)
|
||
13. [Sampled Values 控制块 (SVCB)](#13-sampled-values-控制块-svcb)
|
||
14. [数据访问控制 (Access Control)](#14-数据访问控制-access-control)
|
||
15. [设置组 (Setting Groups)](#15-设置组-setting-groups)
|
||
16. [文件服务](#16-文件服务)
|
||
17. [日志服务 (Log Service)](#17-日志服务-log-service)
|
||
18. [TLS 安全通信](#18-tls-安全通信)
|
||
19. [单线程模式 (Threadless)](#19-单线程模式-threadless)
|
||
20. [MmsServer 底层接口](#20-mmsserver-底层接口)
|
||
21. [完整示例](#21-完整示例)
|
||
|
||
---
|
||
|
||
## 1. 架构概述
|
||
|
||
服务端核心职责:
|
||
|
||
```
|
||
应用程序数据 --> [MmsValue] --> IedServer --> TCP/MMS --> 客户端
|
||
|
|
||
+--> GOOSE 发布 (L2 以太网)
|
||
+--> SV 发布 (L2 以太网)
|
||
```
|
||
|
||
**关键类型**:
|
||
- `IedServer` — 服务器实例句柄(不透明指针)
|
||
- `IedServerConfig` — 服务器配置对象
|
||
- `IedModel` — 数据模型根节点
|
||
- `LogicalDevice` → `LogicalNode` → `DataObject` → `DataAttribute` — 数据模型层级
|
||
|
||
---
|
||
|
||
## 2. 数据模型
|
||
|
||
### 2.1 模型节点类型枚举
|
||
|
||
```c
|
||
typedef enum {
|
||
LogicalDeviceModelType, // 逻辑设备
|
||
LogicalNodeModelType, // 逻辑节点
|
||
DataObjectModelType, // 数据对象
|
||
DataAttributeModelType // 数据属性
|
||
} ModelNodeType;
|
||
```
|
||
|
||
### 2.2 创建数据模型的三种方式
|
||
|
||
#### 方式一: 静态模型生成器(推荐)
|
||
|
||
使用 Java 工具 `genmodel.jar` 从 SCL/ICD 文件自动生成:
|
||
- 生成 `static_model.c` 和 `static_model.h`
|
||
- 所有节点作为全局变量,编译时确定,效率最高
|
||
- 生成宏定义方便访问,如 `IEDMODEL_Device1_LLN0_Mod_stVal`
|
||
|
||
```c
|
||
#include "static_model.h"
|
||
|
||
// 直接使用生成的全局模型
|
||
IedServer iedServer = IedServer_create(&iedModel);
|
||
```
|
||
|
||
#### 方式二: API 动态创建
|
||
|
||
通过 `iec61850_dynamic_model.h` API 在运行时构建模型:
|
||
|
||
```c
|
||
#include "iec61850_dynamic_model.h"
|
||
|
||
// 1. 创建根模型
|
||
IedModel* model = IedModel_create("myIED");
|
||
|
||
// 2. 创建逻辑设备
|
||
LogicalDevice* ld = LogicalDevice_create("SENSORS", model);
|
||
|
||
// 3. 创建逻辑节点
|
||
LogicalNode* lln0 = LogicalNode_create("LLN0", ld);
|
||
LogicalNode* ttmp1 = LogicalNode_create("TTMP1", ld);
|
||
|
||
// 4. 使用 CDC 快捷函数创建数据对象
|
||
DataObject* mod = CDC_ENS_create("Mod", (ModelNode*)lln0, 0);
|
||
DataObject* health = CDC_ENS_create("Health", (ModelNode*)lln0, 0);
|
||
DataObject* tmpSv = CDC_SAV_create("TmpSv", (ModelNode*)ttmp1, 0, false);
|
||
|
||
// 5. 手动创建数据属性
|
||
DataAttribute* da = DataAttribute_create("stVal", parent,
|
||
IEC61850_INT32, IEC61850_FC_ST,
|
||
TRG_OPT_DATA_CHANGED, 0, 0);
|
||
|
||
// 6. 创建数据集
|
||
DataSet* ds = DataSet_create("events", lln0);
|
||
DataSetEntry_create(ds, "TTMP1$MX$TmpSv$instMag$f", -1, NULL);
|
||
|
||
// 7. 创建报告控制块
|
||
ReportControlBlock_create("events01", lln0, "events01", false, NULL, 1,
|
||
TRG_OPT_DATA_CHANGED, RPT_OPT_SEQ_NUM | RPT_OPT_TIME_STAMP, 50, 0);
|
||
|
||
// 8. 创建 GOOSE 控制块
|
||
GSEControlBlock_create("gse01", lln0, "events01", "events", 1, false, 200, 3000);
|
||
|
||
// 9. 创建设置组控制块
|
||
SettingGroupControlBlock_create(lln0, 1, 1);
|
||
|
||
// --- 使用完毕后 ---
|
||
IedServer_destroy(iedServer);
|
||
IedModel_destroy(model); // 仅动态模型需要
|
||
```
|
||
|
||
#### 方式三: 配置文件解析
|
||
|
||
使用 `genconfig.jar` 工具从 SCL 生成 `model.cfg` 配置文件,然后运行时加载:
|
||
|
||
```c
|
||
#include "iec61850_config_file_parser.h"
|
||
|
||
IedModel* model = ConfigFileParser_createModelFromConfigFileEx("model.cfg");
|
||
|
||
// 通过对象引用路径查找
|
||
DataAttribute* attr = (DataAttribute*)
|
||
IedModel_getModelNodeByObjectReference(model, "simpleIOGenericIO/GGIO1.AnIn1.mag.f");
|
||
|
||
// 通过短地址查找
|
||
DataAttribute* attr2 = (DataAttribute*)
|
||
IedModel_getModelNodeByShortAddress(model, 101);
|
||
```
|
||
|
||
### 2.3 数据属性类型枚举 (DataAttributeType)
|
||
|
||
| 枚举值 | 含义 | 枚举值 | 含义 |
|
||
|--------|------|--------|------|
|
||
| `IEC61850_BOOLEAN` | 布尔 | `IEC61850_INT8` | 8位有符号整数 |
|
||
| `IEC61850_INT16` | 16位有符号 | `IEC61850_INT32` | 32位有符号 |
|
||
| `IEC61850_INT64` | 64位有符号 | `IEC61850_INT8U` | 8位无符号 |
|
||
| `IEC61850_INT16U` | 16位无符号 | `IEC61850_INT32U` | 32位无符号 |
|
||
| `IEC61850_FLOAT32` | 32位浮点 | `IEC61850_FLOAT64` | 64位浮点 |
|
||
| `IEC61850_ENUMERATED` | 枚举 | `IEC61850_TIMESTAMP` | 时间戳 |
|
||
| `IEC61850_QUALITY` | 品质 | `IEC61850_CONSTRUCTED` | 构造类型(子属性) |
|
||
| `IEC61850_VISIBLE_STRING_32` | 可见字符串(32) | `IEC61850_VISIBLE_STRING_64` | 可见字符串(64) |
|
||
| `IEC61850_VISIBLE_STRING_255` | 可见字符串(255) | `IEC61850_UNICODE_STRING_255` | Unicode字符串 |
|
||
| `IEC61850_CHECK` | 检查位 | `IEC61850_CODEDENUM` | 编码枚举 |
|
||
| `IEC61850_OCTET_STRING_64` | 字节串(64) | `IEC61850_GENERIC_BITSTRING` | 位串 |
|
||
|
||
### 2.4 功能约束枚举 (FunctionalConstraint)
|
||
|
||
```c
|
||
typedef enum eFunctionalConstraint {
|
||
IEC61850_FC_ST = 0, // 状态信息(Status)
|
||
IEC61850_FC_MX = 1, // 测量值(Measurands)
|
||
IEC61850_FC_SP = 2, // 设定值(Setpoint)
|
||
IEC61850_FC_SV = 3, // 替代(Substitution)
|
||
IEC61850_FC_CF = 4, // 配置(Configuration)
|
||
IEC61850_FC_DC = 5, // 描述(Description)
|
||
IEC61850_FC_SG = 6, // 设置组(Setting Group) - 活动值
|
||
IEC61850_FC_SE = 7, // 设置组可编辑(Setting Group Editable) - 编辑值
|
||
IEC61850_FC_SR = 8, // 服务追踪(Service Response)
|
||
IEC61850_FC_OR = 9, // 操作接收(Operate Received)
|
||
IEC61850_FC_BL = 10, // 闭锁(Blocking)
|
||
IEC61850_FC_EX = 11, // 扩展定义(Extended)
|
||
IEC61850_FC_CO = 12, // 控制(Control)
|
||
IEC61850_FC_US = 13, // 单播SV
|
||
IEC61850_FC_MS = 14, // 组播SV
|
||
IEC61850_FC_RP = 15, // 非缓冲报告
|
||
IEC61850_FC_BR = 16, // 缓冲报告
|
||
IEC61850_FC_LG = 17, // 日志控制块
|
||
IEC61850_FC_GO = 18, // GOOSE控制块
|
||
IEC61850_FC_ALL = 99, // 所有FC(通配符)
|
||
IEC61850_FC_NONE = -1 // 无FC
|
||
} FunctionalConstraint;
|
||
```
|
||
|
||
### 2.5 品质位定义 (Quality)
|
||
|
||
```c
|
||
typedef uint16_t Quality;
|
||
|
||
// 有效性
|
||
#define QUALITY_VALIDITY_GOOD 0 // 好
|
||
#define QUALITY_VALIDITY_INVALID 2 // 无效
|
||
#define QUALITY_VALIDITY_RESERVED 1 // 保留
|
||
#define QUALITY_VALIDITY_QUESTIONABLE 3 // 可疑
|
||
|
||
// 详细品质
|
||
#define QUALITY_DETAIL_OVERFLOW 4
|
||
#define QUALITY_DETAIL_OUT_OF_RANGE 8
|
||
#define QUALITY_DETAIL_BAD_REFERENCE 16
|
||
#define QUALITY_DETAIL_OSCILLATORY 32
|
||
#define QUALITY_DETAIL_FAILURE 64
|
||
#define QUALITY_DETAIL_OLD_DATA 128
|
||
#define QUALITY_DETAIL_INCONSISTENT 256
|
||
#define QUALITY_DETAIL_INACCURATE 512
|
||
|
||
// 来源
|
||
#define QUALITY_SOURCE_SUBSTITUTED 1024
|
||
```
|
||
|
||
### 2.6 触发器选项 (Trigger Options)
|
||
|
||
```c
|
||
#define TRG_OPT_DATA_CHANGED 1 // 数据变化(dchg)
|
||
#define TRG_OPT_QUALITY_CHANGED 2 // 品质变化(qchg)
|
||
#define TRG_OPT_DATA_UPDATE 4 // 数据更新(dupd)
|
||
#define TRG_OPT_INTEGRITY 8 // 完整性周期
|
||
#define TRG_OPT_GI 16 // 总召唤(General Interrogation)
|
||
#define TRG_OPT_TRANSIENT 128 // 瞬变
|
||
```
|
||
|
||
### 2.7 报告选项 (Report Options)
|
||
|
||
```c
|
||
#define RPT_OPT_SEQ_NUM 1 // 序列号
|
||
#define RPT_OPT_TIME_STAMP 2 // 报告时间戳
|
||
#define RPT_OPT_REASON_FOR_INCLUSION 4 // 包含原因
|
||
#define RPT_OPT_DATA_SET 8 // 数据集引用
|
||
#define RPT_OPT_DATA_REFERENCE 16 // 数据引用
|
||
#define RPT_OPT_BUFFER_OVERFLOW 32 // 缓冲区溢出
|
||
#define RPT_OPT_ENTRY_ID 64 // 条目ID
|
||
#define RPT_OPT_CONF_REV 128 // 配置版本
|
||
```
|
||
|
||
---
|
||
|
||
## 3. 服务器生命周期
|
||
|
||
```c
|
||
// 1. 创建配置
|
||
IedServerConfig config = IedServerConfig_create();
|
||
// ... 配置 config ...
|
||
|
||
// 2. 创建服务器
|
||
IedServer iedServer = IedServer_createWithConfig(&iedModel, tlsConfig, config);
|
||
IedServerConfig_destroy(config); // 配置对象不再需要
|
||
|
||
// 3. 设置各种处理器
|
||
IedServer_setControlHandler(...);
|
||
IedServer_setConnectionIndicationHandler(...);
|
||
// ...
|
||
|
||
// 4. 启动服务器
|
||
IedServer_start(iedServer, 102); // TCP端口102
|
||
|
||
// 5. 主循环 - 更新数据
|
||
while (running) {
|
||
IedServer_lockDataModel(iedServer);
|
||
IedServer_updateFloatAttributeValue(iedServer, attr, value);
|
||
IedServer_unlockDataModel(iedServer);
|
||
Thread_sleep(100);
|
||
}
|
||
|
||
// 6. 停止
|
||
IedServer_stop(iedServer);
|
||
|
||
// 7. 销毁
|
||
IedServer_destroy(iedServer);
|
||
// IedModel_destroy(model); // 仅动态模型
|
||
```
|
||
|
||
---
|
||
|
||
## 4. IedServerConfig 配置详解
|
||
|
||
```c
|
||
typedef struct sIedServerConfig {
|
||
int reportBufferSize; // 缓冲报告缓冲区大小(字节)
|
||
int reportBufferSizeURCBs; // 非缓冲报告缓冲区大小
|
||
char* fileServiceBasepath; // 文件服务根路径
|
||
bool enableFileService; // 启用文件服务(默认true)
|
||
bool enableDynamicDataSetService; // 启 用动态数据集服务(默认true)
|
||
int maxAssociationSpecificDataSets; // 每连接最多关联数据集
|
||
int maxDomainSpecificDataSets; // 最多域数据集
|
||
int maxDataSetEntries; // 动态数据集最大条目数
|
||
bool enableLogService; // 启用日志服务(默认true)
|
||
bool useIntegratedGoosePublisher; // 使用集成GOOSE发布者(默认true)
|
||
uint8_t edition; // IEC 61850版本(0=Ed1,1=Ed2,2=Ed2.1)
|
||
int maxMmsConnections; // 最大MMS连接数
|
||
bool enableEditSG; // 启用EditSG服务(默认true)
|
||
bool enableResvTmsForSGCB; // SGCB.ResvTms可见
|
||
bool enableResvTmsForBRCB; // BRCB.ResvTms可见
|
||
bool enableOwnerForRCB; // RCB.Owner可见
|
||
bool syncIntegrityReportTimes; // 同步完整性报告时间
|
||
uint8_t reportSettingsWritable; // 报告设置可写标志
|
||
} IedServerConfig;
|
||
```
|
||
|
||
### 4.1 配置函数清单
|
||
|
||
```c
|
||
// 创建与销毁
|
||
IedServerConfig IedServerConfig_create(void);
|
||
void IedServerConfig_destroy(IedServerConfig self);
|
||
|
||
// 版本设置
|
||
void IedServerConfig_setEdition(IedServerConfig self, uint8_t edition);
|
||
uint8_t IedServerConfig_getEdition(IedServerConfig self);
|
||
// edition 可选值: IEC_61850_EDITION_1 (0), IEC_61850_EDITION_2 (1), IEC_61850_EDITION_2_1 (2)
|
||
|
||
// 报告缓冲区
|
||
void IedServerConfig_setReportBufferSize(IedServerConfig self, int reportBufferSize);
|
||
int IedServerConfig_getReportBufferSize(IedServerConfig self);
|
||
void IedServerConfig_setReportBufferSizeForURCBs(IedServerConfig self, int reportBufferSize);
|
||
int IedServerConfig_getReportBufferSizeForURCBs(IedServerConfig self);
|
||
// 默认值: 65536 字节 (由 CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE 控制)
|
||
|
||
// MMS连接数
|
||
void IedServerConfig_setMaxMmsConnections(IedServerConfig self, int maxConnections);
|
||
int IedServerConfig_getMaxMmsConnections(IedServerConfig self);
|
||
// 此值必须小于 CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS (若 != -1)
|
||
|
||
// 同步完整性报告时间
|
||
void IedServerConfig_setSyncIntegrityReportTimes(IedServerConfig self, bool enable);
|
||
bool IedServerConfig_getSyncIntegrityReportTimes(IedServerConfig self);
|
||
// 启用后完整性报告时间对齐UTC纪元
|
||
|
||
// 文件服务
|
||
void IedServerConfig_setFileServiceBasePath(IedServerConfig self, const char* basepath);
|
||
const char* IedServerConfig_getFileServiceBasePath(IedServerConfig self);
|
||
void IedServerConfig_enableFileService(IedServerConfig self, bool enable);
|
||
bool IedServerConfig_isFileServiceEnabled(IedServerConfig self);
|
||
|
||
// 动态数据集服务
|
||
void IedServerConfig_enableDynamicDataSetService(IedServerConfig self, bool enable);
|
||
bool IedServerConfig_isDynamicDataSetServiceEnabled(IedServerConfig self);
|
||
|
||
// 动态数据集限制
|
||
void IedServerConfig_setMaxAssociationSpecificDataSets(IedServerConfig self, int maxDataSets);
|
||
int IedServerConfig_getMaxAssociationSpecificDataSets(IedServerConfig self);
|
||
void IedServerConfig_setMaxDomainSpecificDataSets(IedServerConfig self, int maxDataSets);
|
||
int IedServerConfig_getMaxDomainSpecificDataSets(IedServerConfig self);
|
||
void IedServerConfig_setMaxDataSetEntries(IedServerConfig self, int maxDataSetEntries);
|
||
int IedServerConfig_getMaxDatasSetEntries(IedServerConfig self);
|
||
|
||
// 日志服务
|
||
void IedServerConfig_enableLogService(IedServerConfig self, bool enable);
|
||
bool IedServerConfig_isLogServiceEnabled(IedServerConfig self);
|
||
|
||
// EditSG 设置组编辑
|
||
void IedServerConfig_enableEditSG(IedServerConfig self, bool enable);
|
||
// 注意: 禁用后 SGCB.ResvTms 不可见
|
||
|
||
// ResvTms 可见性
|
||
void IedServerConfig_enableResvTmsForSGCB(IedServerConfig self, bool enable);
|
||
void IedServerConfig_enableResvTmsForBRCB(IedServerConfig self, bool enable);
|
||
bool IedServerConfig_isResvTmsForBRCBEnabled(IedServerConfig self);
|
||
|
||
// RCB Owner 属性
|
||
void IedServerConfig_enableOwnerForRCB(IedServerConfig self, bool enable);
|
||
bool IedServerConfig_isOwnerForRCBEnabled(IedServerConfig self);
|
||
|
||
// GOOSE 发布控制
|
||
void IedServerConfig_useIntegratedGoosePublisher(IedServerConfig self, bool enable);
|
||
// 默认启用; 如需使用独立的 GoosePublisher 可禁用
|
||
|
||
// 报告设置可写性
|
||
void IedServerConfig_setReportSetting(IedServerConfig self, uint8_t setting, bool isDyn);
|
||
bool IedServerConfig_getReportSetting(IedServerConfig self, uint8_t setting);
|
||
// setting 取值: IEC61850_REPORTSETTINGS_RPT_ID, _BUF_TIME, _DATSET, _TRG_OPS, _OPT_FIELDS, _INTG_PD
|
||
```
|
||
|
||
---
|
||
|
||
## 5. 服务器创建与销毁
|
||
|
||
```c
|
||
// 创建服务器实例
|
||
IedServer IedServer_create(IedModel* dataModel);
|
||
// 参数: dataModel - 指向 IedModel 数据结构的指针
|
||
// 返回: 新的 IedServer 实例
|
||
|
||
// 创建带 TLS 支持的服务器
|
||
IedServer IedServer_createWithTlsSupport(IedModel* dataModel, TLSConfiguration tlsConfiguration);
|
||
// 参数: tlsConfiguration - TLS 配置对象,或 NULL 表示不使用 TLS
|
||
|
||
// 创建带完整配置的服务器
|
||
IedServer IedServer_createWithConfig(IedModel* dataModel,
|
||
TLSConfiguration tlsConfiguration, IedServerConfig serverConfiguration);
|
||
// 参数: serverConfiguration - 高级配置对象
|
||
|
||
// 销毁服务器实例 (释放所有资源: 内存、TCP socket)
|
||
void IedServer_destroy(IedServer self);
|
||
|
||
// 添加额外的本地接入点 (多网卡监听)
|
||
bool IedServer_addAccessPoint(IedServer self,
|
||
const char* ipAddr, int tcpPort, TLSConfiguration tlsConfiguration);
|
||
// 返回: 成功 true,失败 false
|
||
|
||
// 设置本地监听 IP 地址
|
||
void IedServer_setLocalIpAddress(IedServer self, const char* localIpAddress);
|
||
// 内部会创建 IP 字符串的副本
|
||
|
||
// 设置服务器身份 (MMS identify 服务)
|
||
void IedServer_setServerIdentity(IedServer self,
|
||
const char* vendor, const char* model, const char* revision);
|
||
// 条件编译: CONFIG_IEC61850_SUPPORT_SERVER_IDENTITY
|
||
|
||
// 获取底层 MmsServer 实例 (用于高级操作)
|
||
MmsServer IedServer_getMmsServer(IedServer self);
|
||
|
||
// 获取数据模型指针
|
||
IedModel* IedServer_getDataModel(IedServer self);
|
||
```
|
||
|
||
### 启动与停止
|
||
|
||
```c
|
||
// === 多线程模式 ===
|
||
|
||
// 启动服务器 (创建后台线程)
|
||
void IedServer_start(IedServer self, int tcpPort);
|
||
// tcpPort: TCP 端口号,-1 使用默认 (102 for MMS, 3872 for TLS)
|
||
|
||
// 检查服务器是否在运行
|
||
bool IedServer_isRunning(IedServer self);
|
||
|
||
// 获取当前连接数
|
||
int IedServer_getNumberOfOpenConnections(IedServer self);
|
||
|
||
// 停止服务器
|
||
void IedServer_stop(IedServer self);
|
||
|
||
// === 单线程模式 ===
|
||
// (详见第19节)
|
||
void IedServer_startThreadless(IedServer self, int tcpPort);
|
||
int IedServer_waitReady(IedServer self, unsigned int timeoutMs);
|
||
void IedServer_processIncomingData(IedServer self);
|
||
void IedServer_performPeriodicTasks(IedServer self);
|
||
void IedServer_stopThreadless(IedServer self);
|
||
```
|
||
|
||
### 时间品质设置
|
||
|
||
```c
|
||
void IedServer_setTimeQuality(IedServer self,
|
||
bool leapSecondKnown, // 闰秒已知
|
||
bool clockFailure, // 时钟故障
|
||
bool clockNotSynchronized, // 时钟未同步
|
||
int subsecondPrecision); // 亚秒精度 (fractionOfSecond 的有效位数)
|
||
```
|
||
|
||
---
|
||
|
||
## 6. 数据模型锁定与更新
|
||
|
||
### 6.1 线程安全
|
||
|
||
**重要规则**: 在回调函数内部(ControlHandler、WriteAccessHandler 等),数据模型已经被锁住!不要在回调函数内部再调用 `lockDataModel`,否则会导致死锁。
|
||
|
||
```c
|
||
// 锁定数据模型 (暂停处理客户端请求)
|
||
void IedServer_lockDataModel(IedServer self);
|
||
|
||
// 解锁数据模型 (处理暂停的客户端请求)
|
||
void IedServer_unlockDataModel(IedServer self);
|
||
```
|
||
|
||
### 6.2 更新数据属性值
|
||
|
||
所有 `IedServer_update*` 函数都会自动:
|
||
- 检查触发条件是否满足 (dchg/qchg/dupd)
|
||
- 如果满足触发条件,通知相应的 RCB/GCB
|
||
- 自动更新内部缓存
|
||
|
||
```c
|
||
// === 通用更新函数 ===
|
||
void IedServer_updateAttributeValue(IedServer self,
|
||
DataAttribute* dataAttribute, MmsValue* value);
|
||
// 最通用的更新函数,接受 MmsValue 对象
|
||
|
||
// === 类型特定的便捷更新函数 ===
|
||
void IedServer_updateFloatAttributeValue(IedServer self,
|
||
DataAttribute* dataAttribute, float value);
|
||
|
||
void IedServer_updateInt32AttributeValue(IedServer self,
|
||
DataAttribute* dataAttribute, int32_t value);
|
||
|
||
void IedServer_updateInt64AttributeValue(IedServer self,
|
||
DataAttribute* dataAttribute, int64_t value);
|
||
|
||
void IedServer_updateUnsignedAttributeValue(IedServer self,
|
||
DataAttribute* dataAttribute, uint32_t value);
|
||
|
||
void IedServer_updateBooleanAttributeValue(IedServer self,
|
||
DataAttribute* dataAttribute, bool value);
|
||
|
||
void IedServer_updateBitStringAttributeValue(IedServer self,
|
||
DataAttribute* dataAttribute, uint32_t value);
|
||
|
||
void IedServer_updateVisibleStringAttributeValue(IedServer self,
|
||
DataAttribute* dataAttribute, char* value);
|
||
|
||
void IedServer_updateDbposValue(IedServer self,
|
||
DataAttribute* dataAttribute, Dbpos value);
|
||
// Dbpos 是双点位置 (Double Point Position)
|
||
|
||
// === 时间戳更新 ===
|
||
void IedServer_updateUTCTimeAttributeValue(IedServer self,
|
||
DataAttribute* dataAttribute, uint64_t value);
|
||
// value: 毫秒时间戳 (自Epoch以来的毫秒数, 如 Hal_getTimeInMs())
|
||
|
||
void IedServer_updateTimestampAttributeValue(IedServer self,
|
||
DataAttribute* dataAttribute, Timestamp* timestamp);
|
||
// timestamp: 完整 Timestamp 结构体 (含品质标志)
|
||
|
||
// === 品质更新 ===
|
||
void IedServer_updateQuality(IedServer self,
|
||
DataAttribute* dataAttribute, Quality quality);
|
||
```
|
||
|
||
---
|
||
|
||
## 7. 数据读取函数
|
||
|
||
```c
|
||
// 获取数据属性底层 MmsValue 对象 (谨慎使用, 直接修改不会触发报告)
|
||
MmsValue* IedServer_getAttributeValue(IedServer self,
|
||
DataAttribute* dataAttribute);
|
||
|
||
// 获取功能约束数据 (FCD) 的 MmsValue
|
||
MmsValue* IedServer_getFunctionalConstrainedData(IedServer self,
|
||
DataObject* dataObject, FunctionalConstraint fc);
|
||
// 直接获取 DataObject 指定 FC 下的全部 MmsValue
|
||
// 警告: 直接修改此值不会触发报告机制!
|
||
|
||
// === 类型特定读取函数 ===
|
||
bool IedServer_getBooleanAttributeValue(IedServer self,
|
||
const DataAttribute* dataAttribute);
|
||
|
||
int32_t IedServer_getInt32AttributeValue(IedServer self,
|
||
const DataAttribute* dataAttribute);
|
||
|
||
int64_t IedServer_getInt64AttributeValue(IedServer self,
|
||
const DataAttribute* dataAttribute);
|
||
|
||
uint32_t IedServer_getUInt32AttributeValue(IedServer self,
|
||
const DataAttribute* dataAttribute);
|
||
|
||
float IedServer_getFloatAttributeValue(IedServer self,
|
||
const DataAttribute* dataAttribute);
|
||
|
||
uint64_t IedServer_getUTCTimeAttributeValue(IedServer self,
|
||
const DataAttribute* dataAttribute);
|
||
|
||
uint32_t IedServer_getBitStringAttributeValue(IedServer self,
|
||
const DataAttribute* dataAttribute);
|
||
|
||
const char* IedServer_getStringAttributeValue(IedServer self,
|
||
const DataAttribute* dataAttribute);
|
||
```
|
||
|
||
### 7.1 数据更新典型模式
|
||
|
||
```c
|
||
while (running) {
|
||
uint64_t timestamp = Hal_getTimeInMs();
|
||
|
||
// 构建 IEC 61850 时间戳
|
||
Timestamp iecTimestamp;
|
||
Timestamp_clearFlags(&iecTimestamp);
|
||
Timestamp_setTimeInMilliseconds(&iecTimestamp, timestamp);
|
||
Timestamp_setLeapSecondKnown(&iecTimestamp, true);
|
||
|
||
IedServer_lockDataModel(iedServer);
|
||
|
||
// 更新测量值
|
||
IedServer_updateFloatAttributeValue(iedServer,
|
||
IEDMODEL_GenericIO_GGIO1_AnIn1_mag_f, sinf(t));
|
||
|
||
// 更新品质
|
||
IedServer_updateQuality(iedServer,
|
||
IEDMODEL_GenericIO_GGIO1_AnIn1_q, QUALITY_VALIDITY_GOOD);
|
||
|
||
// 更新时间戳
|
||
IedServer_updateTimestampAttributeValue(iedServer,
|
||
IEDMODEL_GenericIO_GGIO1_AnIn1_t, &iecTimestamp);
|
||
|
||
IedServer_unlockDataModel(iedServer);
|
||
|
||
Thread_sleep(100);
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 8. 连接管理与认证
|
||
|
||
### 8.1 连接指示处理器
|
||
|
||
```c
|
||
// 回调函数类型
|
||
typedef void (*IedConnectionIndicationHandler)(
|
||
IedServer self, // 服务器实例
|
||
ClientConnection connection, // 连接对象
|
||
bool connected, // true=新连接, false=连接关闭
|
||
void* parameter); // 用户参数
|
||
|
||
// 设置连接事件回调
|
||
void IedServer_setConnectionIndicationHandler(IedServer self,
|
||
IedConnectionIndicationHandler handler, void* parameter);
|
||
```
|
||
|
||
### 8.2 ClientConnection 操作
|
||
|
||
```c
|
||
// 获取客户端对端地址 (IP:Port)
|
||
const char* ClientConnection_getPeerAddress(ClientConnection self);
|
||
// 注意: 返回的字符串仅在连接存在期间有效
|
||
|
||
// 获取本地地址 (IP:Port)
|
||
const char* ClientConnection_getLocalAddress(ClientConnection self);
|
||
|
||
// 获取安全令牌 (认证阶段关联的)
|
||
void* ClientConnection_getSecurityToken(ClientConnection self);
|
||
// 如果没有认证器则返回 NULL
|
||
```
|
||
|
||
### 8.3 认证器 (Authenticator)
|
||
|
||
```c
|
||
// 设置认证回调
|
||
void IedServer_setAuthenticator(IedServer self,
|
||
AcseAuthenticator authenticator, void* authenticatorParameter);
|
||
// 每次客户端连接尝试时调用, 根据返回值决定接受或拒绝连接
|
||
// 如果不设置, 默认接受所有连接
|
||
|
||
// 认证机制枚举
|
||
typedef enum {
|
||
ACSE_AUTH_NONE = 0, // 无认证
|
||
ACSE_AUTH_PASSWORD = 1, // ACSE 密码
|
||
ACSE_AUTH_CERTIFICATE = 2, // ACSE 证书
|
||
ACSE_AUTH_TLS = 3 // TLS 证书
|
||
} AcseAuthenticationMechanism;
|
||
```
|
||
|
||
---
|
||
|
||
## 9. 控制模型 (Control)
|
||
|
||
### 9.1 控制模型枚举 (ControlModel)
|
||
|
||
```c
|
||
typedef enum {
|
||
CONTROL_MODEL_STATUS_ONLY = 0, // 仅状态 (不可控)
|
||
CONTROL_MODEL_DIRECT_NORMAL = 1, // 直接控制-普通安全
|
||
CONTROL_MODEL_SBO_NORMAL = 2, // 选择前操作-普通安全
|
||
CONTROL_MODEL_DIRECT_ENHANCED = 3, // 直接控制-增强安全 (含CommandTermination)
|
||
CONTROL_MODEL_SBO_ENHANCED = 4 // 选择前操作-增强安全
|
||
} ControlModel;
|
||
```
|
||
|
||
### 9.2 控制处理器回调类型
|
||
|
||
```c
|
||
// 1. 操作检查处理器 (PerformCheckHandler) - 静态测试 (可选)
|
||
typedef CheckHandlerResult (*ControlPerformCheckHandler)(
|
||
ControlAction action, // 控制动作上下文
|
||
void* parameter, // 用户参数
|
||
MmsValue* ctlVal, // 控制值
|
||
bool test, // 是否为测试命令
|
||
bool interlockCheck); // 是否请求联锁检查
|
||
|
||
// 返回值:
|
||
// CONTROL_ACCEPTED = -1 检查通过
|
||
// CONTROL_WAITING_FOR_SELECT = 0 等待选择
|
||
// CONTROL_HARDWARE_FAULT = 1 硬件故障
|
||
// CONTROL_TEMPORARILY_UNAVAILABLE = 2 暂时不可用(已被其他客户端控制)
|
||
// CONTROL_OBJECT_ACCESS_DENIED = 3 访问拒绝
|
||
// CONTROL_OBJECT_UNDEFINED = 4 对象未定义
|
||
// CONTROL_VALUE_INVALID = 11 值超出范围
|
||
|
||
|
||
// 2. 等待执行处理器 (WaitForExecutionHandler) - 动态测试 (可选)
|
||
typedef ControlHandlerResult (*ControlWaitForExecutionHandler)(
|
||
ControlAction action,
|
||
void* parameter,
|
||
MmsValue* ctlVal,
|
||
bool test,
|
||
bool synchroCheck); // 是否请求同步检查
|
||
|
||
// 返回值:
|
||
// CONTROL_RESULT_FAILED = 0 失败
|
||
// CONTROL_RESULT_OK = 1 成功
|
||
// CONTROL_RESULT_WAITING = 2 等待中 (稍后再次调用)
|
||
|
||
|
||
// 3. 控制执行处理器 (ControlHandler) - 实际执行 (必需)
|
||
typedef ControlHandlerResult (*ControlHandler)(
|
||
ControlAction action, // 控制动作上下文
|
||
void* parameter, // 用户参数 (通常是控制对象的 DataObject* 指针)
|
||
MmsValue* ctlVal, // 控制值
|
||
bool test); // 是否为测试命令
|
||
|
||
// 返回值: 同 WaitForExecutionHandler
|
||
|
||
|
||
// 4. 选择状态变化处理器 (v1.5 新增)
|
||
typedef void (*ControlSelectStateChangedHandler)(
|
||
ControlAction action,
|
||
void* parameter,
|
||
bool isSelected, // true=被选择, false=取消选择
|
||
SelectStateChangedReason reason); // 原因
|
||
|
||
typedef enum {
|
||
SELECT_STATE_REASON_SELECTED,
|
||
SELECT_STATE_REASON_CANCELED,
|
||
SELECT_STATE_REASON_TIMEOUT,
|
||
SELECT_STATE_REASON_OPERATED,
|
||
SELECT_STATE_REASON_OPERATE_FAILED,
|
||
SELECT_STATE_REASON_DISCONNECTED
|
||
} SelectStateChangedReason;
|
||
```
|
||
|
||
### 9.3 设置控制处理器
|
||
|
||
```c
|
||
// 设置控制处理器 (必需 - 在有控制模型中)
|
||
void IedServer_setControlHandler(IedServer self,
|
||
DataObject* node, // 可控数据对象
|
||
ControlHandler handler, // 控制回调函数
|
||
void* parameter); // 用户参数
|
||
|
||
// 设置操作检查处理器 (可选)
|
||
void IedServer_setPerformCheckHandler(IedServer self,
|
||
DataObject* node,
|
||
ControlPerformCheckHandler handler,
|
||
void* parameter);
|
||
|
||
// 设置等待执行处理器 (可选)
|
||
void IedServer_setWaitForExecutionHandler(IedServer self,
|
||
DataObject* node,
|
||
ControlWaitForExecutionHandler handler,
|
||
void* parameter);
|
||
|
||
// 设置选择状态变化处理器 (可选)
|
||
void IedServer_setSelectStateChangedHandler(IedServer self,
|
||
DataObject* node,
|
||
ControlSelectStateChangedHandler handler,
|
||
void* parameter);
|
||
```
|
||
|
||
### 9.4 ControlAction 上下文查询函数
|
||
|
||
在控制处理器内部,可以通过 ControlAction 获取更多上下文信息:
|
||
|
||
```c
|
||
// 获取客户端连接对象
|
||
ClientConnection ControlAction_getClientConnection(ControlAction self);
|
||
|
||
// 获取控制对象 (DataObject*)
|
||
DataObject* ControlAction_getControlObject(ControlAction self);
|
||
|
||
// 判断是否为 Select 命令 (vs Operate)
|
||
bool ControlAction_isSelect(ControlAction self);
|
||
|
||
// 获取控制序号
|
||
int ControlAction_getCtlNum(ControlAction self);
|
||
|
||
// 获取来源类别 (orCat)
|
||
int ControlAction_getOrCat(ControlAction self);
|
||
|
||
// 获取来源标识
|
||
uint8_t* ControlAction_getOrIdent(ControlAction self, int* orIdentSize);
|
||
|
||
// 获取同步检查位
|
||
bool ControlAction_getSynchroCheck(ControlAction self);
|
||
|
||
// 获取联锁检查位
|
||
bool ControlAction_getInterlockCheck(ControlAction self);
|
||
|
||
// 获取控制时间 (TimeActivatedControl), 非定时控制返回 0
|
||
uint64_t ControlAction_getControlTime(ControlAction self);
|
||
|
||
// 设置下一次 CommandTermination 的错误码
|
||
void ControlAction_setError(ControlAction self, ControlLastApplError error);
|
||
|
||
// 设置下一次 CommandTermination 的附加原因
|
||
void ControlAction_setAddCause(ControlAction self, ControlAddCause addCause);
|
||
```
|
||
|
||
### 9.5 更新控制模型
|
||
|
||
```c
|
||
void IedServer_updateCtlModel(IedServer self,
|
||
DataObject* ctlObject, ControlModel value);
|
||
// 同时更新 "ctlModel" 属性值和内部控制结构
|
||
// 注意: 对应的控制模型数据结构必须在数据模型中存在!
|
||
```
|
||
|
||
### 9.6 控制处理器典型实现
|
||
|
||
```c
|
||
static ControlHandlerResult
|
||
controlHandlerForBinaryOutput(ControlAction action, void* parameter,
|
||
MmsValue* value, bool test)
|
||
{
|
||
if (test)
|
||
return CONTROL_RESULT_FAILED; // 拒绝测试命令
|
||
|
||
if (MmsValue_getType(value) != MMS_BOOLEAN)
|
||
return CONTROL_RESULT_FAILED;
|
||
|
||
uint64_t timeStamp = Hal_getTimeInMs();
|
||
bool newState = MmsValue_getBoolean(value);
|
||
|
||
if (parameter == IEDMODEL_GenericIO_GGIO1_SPCSO1) {
|
||
// 执行实际 I/O 操作
|
||
setHardwareOutput(1, newState);
|
||
|
||
// 更新服务器状态
|
||
IedServer_updateUTCTimeAttributeValue(iedServer,
|
||
IEDMODEL_GenericIO_GGIO1_SPCSO1_t, timeStamp);
|
||
IedServer_updateAttributeValue(iedServer,
|
||
IEDMODEL_GenericIO_GGIO1_SPCSO1_stVal, value);
|
||
}
|
||
|
||
return CONTROL_RESULT_OK;
|
||
}
|
||
|
||
// 安装
|
||
IedServer_setControlHandler(iedServer,
|
||
IEDMODEL_GenericIO_GGIO1_SPCSO1,
|
||
(ControlHandler) controlHandlerForBinaryOutput,
|
||
IEDMODEL_GenericIO_GGIO1_SPCSO1);
|
||
```
|
||
|
||
---
|
||
|
||
## 10. 报告控制块 (RCB)
|
||
|
||
### 10.1 RCB 事件类型
|
||
|
||
```c
|
||
typedef enum {
|
||
RCB_EVENT_GET_PARAMETER, // 客户端读取参数
|
||
RCB_EVENT_SET_PARAMETER, // 客户端设置参数
|
||
RCB_EVENT_UNRESERVED, // RCB 取消预留
|
||
RCB_EVENT_RESERVED, // RCB 被预留
|
||
RCB_EVENT_ENABLE, // RCB 启用
|
||
RCB_EVENT_DISABLE, // RCB 禁用
|
||
RCB_EVENT_GI, // 总召唤触发
|
||
RCB_EVENT_PURGEBUF, // 清除缓冲区
|
||
RCB_EVENT_OVERFLOW, // 报告缓冲区溢出
|
||
RCB_EVENT_REPORT_CREATED // 新报告创建并写入缓冲区
|
||
} IedServer_RCBEventType;
|
||
```
|
||
|
||
### 10.2 RCB 事件处理器
|
||
|
||
```c
|
||
// 回调类型
|
||
typedef void (*IedServer_RCBEventHandler)(
|
||
void* parameter, // 用户参数
|
||
ReportControlBlock* rcb, // 受影响的 RCB
|
||
ClientConnection connection, // 相关客户端连接
|
||
IedServer_RCBEventType event, // 事件类型
|
||
const char* parameterName, // 参数名 (仅用于 SET_PARAMETER 事件)
|
||
MmsDataAccessError serviceError); // 服务错误 (仅用于 SET_PARAMETER 事件)
|
||
|
||
// 安装处理器
|
||
void IedServer_setRCBEventHandler(IedServer self,
|
||
IedServer_RCBEventHandler handler, void* parameter);
|
||
```
|
||
|
||
---
|
||
|
||
## 11. GOOSE 发布
|
||
|
||
### 11.1 GOOSE 发布控制
|
||
|
||
```c
|
||
// 启用所有 GOOSE 控制块 (设置 GoEna=true)
|
||
void IedServer_enableGoosePublishing(IedServer self);
|
||
// 在启动或重置时调用,否则 GCB 默认为非活跃
|
||
|
||
// 禁用所有 GOOSE 控制块 (设置 GoEna=false)
|
||
void IedServer_disableGoosePublishing(IedServer self);
|
||
|
||
// 设置 GOOSE 以太网接口
|
||
void IedServer_setGooseInterfaceId(IedServer self, const char* interfaceId);
|
||
// interfaceId: OS 相关, Linux 下如 "eth0"
|
||
// 如果未调用或 interfaceId=NULL,使用 stack_config.h 中的默认值
|
||
|
||
// 为特定 GCB 设置接口
|
||
void IedServer_setGooseInterfaceIdEx(IedServer self,
|
||
LogicalNode* ln, // 包含 GCB 的逻辑节点, NULL 表示为所有 GCB
|
||
const char* gcbName, // GCB 名称 (非对象引用!)
|
||
const char* interfaceId);
|
||
|
||
// 控制 GOOSE VLAN 标签
|
||
void IedServer_useGooseVlanTag(IedServer self,
|
||
LogicalNode* ln, // NULL 表示所有 GCB
|
||
const char* gcbName,
|
||
bool useVlanTag);
|
||
```
|
||
|
||
### 11.2 独立 GOOSE 发布者 (不依赖服务器模型)
|
||
|
||
当 `useIntegratedGoosePublisher` 配置为 false,或者需要独立控制时:
|
||
|
||
```c
|
||
#include "goose_publisher.h"
|
||
|
||
// 通信参数
|
||
typedef struct sCommParameters {
|
||
uint8_t vlanPriority;
|
||
uint16_t vlanId;
|
||
uint16_t appId;
|
||
uint8_t dstAddress[6]; // 目标 MAC 地址
|
||
} CommParameters;
|
||
|
||
// 创建发布者
|
||
GoosePublisher GoosePublisher_create(CommParameters* parameters,
|
||
const char* interfaceID);
|
||
|
||
// 扩展版: 可禁用 VLAN 标签
|
||
GoosePublisher GoosePublisher_createEx(CommParameters* parameters,
|
||
const char* interfaceID, bool useVlanTag);
|
||
|
||
// 销毁
|
||
void GoosePublisher_destroy(GoosePublisher self);
|
||
|
||
// 发布 GOOSE 消息
|
||
int GoosePublisher_publish(GoosePublisher self, LinkedList dataSet);
|
||
// dataSet: MmsValue* 链表
|
||
// 返回值: -1 表示发送错误
|
||
// 自动递增 sqNum
|
||
|
||
// 发布并保存到缓冲区 (调试用)
|
||
int GoosePublisher_publishAndDump(GoosePublisher self, LinkedList dataSet,
|
||
char* msgBuf, int32_t* msgLen, int32_t bufSize);
|
||
|
||
// 设置 GOOSE 消息字段
|
||
void GoosePublisher_setGoID(GoosePublisher self, char* goID);
|
||
void GoosePublisher_setGoCbRef(GoosePublisher self, char* goCbRef);
|
||
void GoosePublisher_setTimeAllowedToLive(GoosePublisher self, uint32_t timeAllowedToLive);
|
||
void GoosePublisher_setDataSetRef(GoosePublisher self, char* dataSetRef);
|
||
void GoosePublisher_setConfRev(GoosePublisher self, uint32_t confRev);
|
||
void GoosePublisher_setSimulation(GoosePublisher self, bool simulation);
|
||
void GoosePublisher_setNeedsCommission(GoosePublisher self, bool ndsCom);
|
||
|
||
// 状态管理
|
||
uint64_t GoosePublisher_increaseStNum(GoosePublisher self);
|
||
// 当数据集成员变化时调用, 重置 sqNum
|
||
void GoosePublisher_reset(GoosePublisher self);
|
||
// 重置 stNum=1, sqNum=0
|
||
|
||
// 仅用于测试
|
||
void GoosePublisher_setStNum(GoosePublisher self, uint32_t stNum);
|
||
void GoosePublisher_setSqNum(GoosePublisher self, uint32_t sqNum);
|
||
```
|
||
|
||
---
|
||
|
||
## 12. GOOSE 控制块 (GoCB)
|
||
|
||
```c
|
||
// GoCB 事件回调
|
||
typedef void (*GoCBEventHandler)(
|
||
MmsGooseControlBlock goCb, // GoCB 实例
|
||
int event, // IEC61850_GOCB_EVENT_ENABLE(1) 或 _DISABLE(0)
|
||
void* parameter);
|
||
|
||
// 设置 GoCB 事件处理器
|
||
void IedServer_setGoCBHandler(IedServer self,
|
||
GoCBEventHandler handler, void* parameter);
|
||
|
||
// GoCB 信息查询
|
||
char* MmsGooseControlBlock_getName(MmsGooseControlBlock self);
|
||
LogicalNode* MmsGooseControlBlock_getLogicalNode(MmsGooseControlBlock self);
|
||
DataSet* MmsGooseControlBlock_getDataSet(MmsGooseControlBlock self);
|
||
bool MmsGooseControlBlock_getGoEna(MmsGooseControlBlock self);
|
||
int MmsGooseControlBlock_getMinTime(MmsGooseControlBlock self);
|
||
int MmsGooseControlBlock_getMaxTime(MmsGooseControlBlock self);
|
||
bool MmsGooseControlBlock_getFixedOffs(MmsGooseControlBlock self);
|
||
bool MmsGooseControlBlock_getNdsCom(MmsGooseControlBlock self);
|
||
```
|
||
|
||
---
|
||
|
||
## 13. Sampled Values 控制块 (SVCB)
|
||
|
||
```c
|
||
// SVCB 事件回调
|
||
typedef void (*SVCBEventHandler)(
|
||
SVControlBlock* svcb, // SVCB 实例
|
||
int event, // IEC61850_SVCB_EVENT_ENABLE(1) 或 _DISABLE(0)
|
||
void* parameter);
|
||
|
||
// 设置 SVCB 事件处理器
|
||
void IedServer_setSVCBHandler(IedServer self,
|
||
SVControlBlock* svcb,
|
||
SVCBEventHandler handler, void* parameter);
|
||
```
|
||
|
||
---
|
||
|
||
## 14. 数据访问控制 (Access Control)
|
||
|
||
### 14.1 写访问处理器 (WriteAccessHandler)
|
||
|
||
```c
|
||
// 回调类型
|
||
typedef MmsDataAccessError (*WriteAccessHandler)(
|
||
DataAttribute* dataAttribute, // 被写入的数据属性
|
||
MmsValue* value, // 客户端想要写入的值
|
||
ClientConnection connection, // 发起操作的客户端连接
|
||
void* parameter); // 用户参数
|
||
|
||
// 返回值:
|
||
// DATA_ACCESS_ERROR_SUCCESS - 接受写入 (栈自动更新值)
|
||
// DATA_ACCESS_ERROR_SUCCESS_NO_UPDATE - 接受但不自动更新 (用户自行更新)
|
||
// DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED - 拒绝写入
|
||
|
||
// 安装写访问处理器 (仅指定属性)
|
||
void IedServer_handleWriteAccess(IedServer self,
|
||
DataAttribute* dataAttribute,
|
||
WriteAccessHandler handler, void* parameter);
|
||
// 注意: 如果 dataAttribute 有子属性, 此处理器不会传播到子属性
|
||
|
||
// 安装写访问处理器 (包含所有子属性)
|
||
void IedServer_handleWriteAccessForComplexAttribute(IedServer self,
|
||
DataAttribute* dataAttribute,
|
||
WriteAccessHandler handler, void* parameter);
|
||
```
|
||
|
||
### 14.2 FC 级写策略
|
||
|
||
```c
|
||
typedef enum {
|
||
ACCESS_POLICY_ALLOW, // 允许
|
||
ACCESS_POLICY_DENY // 拒绝
|
||
} AccessPolicy;
|
||
|
||
void IedServer_setWriteAccessPolicy(IedServer self,
|
||
FunctionalConstraint fc, AccessPolicy policy);
|
||
// 默认可对 FC=DC 和 FC=CF 的写访问都是拒绝的
|
||
// 如需允许客户端修改描述信息: IedServer_setWriteAccessPolicy(server, IEC61850_FC_DC, ACCESS_POLICY_ALLOW);
|
||
```
|
||
|
||
### 14.3 读访问处理器 (ReadAccessHandler)
|
||
|
||
```c
|
||
typedef MmsDataAccessError (*ReadAccessHandler)(
|
||
LogicalDevice* ld, // 被访问的逻辑设备
|
||
LogicalNode* ln, // 被访问的逻辑节点
|
||
DataObject* dataObject, // 被访问的数据对象
|
||
FunctionalConstraint fc, // 功能约束
|
||
ClientConnection connection, // 客户端连接
|
||
void* parameter); // 用户参数
|
||
|
||
void IedServer_setReadAccessHandler(IedServer self,
|
||
ReadAccessHandler handler, void* parameter);
|
||
// 全局读访问处理器: 每次客户端读取前调用
|
||
// 可用于实现基于角色的访问控制 (RBAC)
|
||
```
|
||
|
||
---
|
||
|
||
## 15. 设置组 (Setting Groups)
|
||
|
||
### 15.1 结构体
|
||
|
||
```c
|
||
struct sSettingGroupControlBlock {
|
||
LogicalNode* parent;
|
||
uint8_t actSG; // 当前活动设置组号
|
||
uint8_t numOfSGs; // 设置组总数
|
||
uint8_t editSG; // 当前编辑设置组号 (上电为0)
|
||
bool cnfEdit; // 编辑确认标志 (上电为false)
|
||
uint64_t timestamp;
|
||
uint16_t resvTms;
|
||
SettingGroupControlBlock* sibling; // 链表中下一个
|
||
};
|
||
```
|
||
|
||
### 15.2 活动设置组操作
|
||
|
||
```c
|
||
// 服务器内部主动切换活动设置组
|
||
void IedServer_changeActiveSettingGroup(IedServer self,
|
||
SettingGroupControlBlock* sgcb, uint8_t newActiveSg);
|
||
// 调用前用户应先更新 FC=SG 的数据属性
|
||
|
||
// 获取当前活动设置组号
|
||
uint8_t IedServer_getActiveSettingGroup(IedServer self,
|
||
SettingGroupControlBlock* sgcb);
|
||
```
|
||
|
||
### 15.3 设置组回调处理器
|
||
|
||
```c
|
||
// 活动设置组变化处理器
|
||
typedef bool (*ActiveSettingGroupChangedHandler)(
|
||
void* parameter,
|
||
SettingGroupControlBlock* sgcb,
|
||
uint8_t newActSg, // 新的活动设置组
|
||
ClientConnection connection); // 请求变更的客户端
|
||
// 返回 true 接受变更, false 拒绝
|
||
|
||
void IedServer_setActiveSettingGroupChangedHandler(IedServer self,
|
||
SettingGroupControlBlock* sgcb,
|
||
ActiveSettingGroupChangedHandler handler, void* parameter);
|
||
|
||
// 编辑设置组变化处理器
|
||
typedef bool (*EditSettingGroupChangedHandler)(
|
||
void* parameter,
|
||
SettingGroupControlBlock* sgcb,
|
||
uint8_t newEditSg, // 新的编辑设置组
|
||
ClientConnection connection);
|
||
// 应在此回调中更新 FC=SE 的数据属性
|
||
// 返回 true 接受变更, false 拒绝
|
||
|
||
void IedServer_setEditSettingGroupChangedHandler(IedServer self,
|
||
SettingGroupControlBlock* sgcb,
|
||
EditSettingGroupChangedHandler handler, void* parameter);
|
||
|
||
// 编辑设置组确认处理器
|
||
typedef void (*EditSettingGroupConfirmationHandler)(
|
||
void* parameter,
|
||
SettingGroupControlBlock* sgcb,
|
||
uint8_t editSg); // 被确认的编辑设置组
|
||
// 客户端确认编辑后将 SE 值写入对应的 SG 值
|
||
|
||
void IedServer_setEditSettingGroupConfirmationHandler(IedServer self,
|
||
SettingGroupControlBlock* sgcb,
|
||
EditSettingGroupConfirmationHandler handler, void* parameter);
|
||
```
|
||
|
||
---
|
||
|
||
## 16. 文件服务
|
||
|
||
### 16.1 基础配置
|
||
|
||
```c
|
||
// 设置文件服务根目录
|
||
void IedServer_setFilestoreBasepath(IedServer self, const char* basepath);
|
||
// 条件编译: CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME
|
||
|
||
// 安装文件访问处理器 (控制/监控文件访问)
|
||
void MmsServer_installFileAccessHandler(MmsServer self,
|
||
MmsFileAccessHandler handler, void* parameter);
|
||
// 通过 IedServer_getMmsServer() 获取 MmsServer
|
||
```
|
||
|
||
### 16.2 文件访问处理器
|
||
|
||
```c
|
||
typedef enum {
|
||
MMS_FILE_ACCESS_TYPE_READ_DIRECTORY, // 读取目录
|
||
MMS_FILE_ACCESS_TYPE_OPEN, // 打开文件
|
||
MMS_FILE_ACCESS_TYPE_OBTAIN, // 上传文件(ObtainFile)
|
||
MMS_FILE_ACCESS_TYPE_DELETE, // 删除文件
|
||
MMS_FILE_ACCESS_TYPE_RENAME // 重命名文件
|
||
} MmsFileServiceType;
|
||
|
||
typedef MmsError (*MmsFileAccessHandler)(
|
||
void* parameter, // 用户参数
|
||
MmsServerConnection connection, // 客户端连接
|
||
MmsFileServiceType service, // 服务类型
|
||
const char* localFilename, // 服务器端文件名
|
||
const char* otherFilename); // 另一个文件名参数
|
||
// 返回 MMS_ERROR_NONE 表示接受, 否则用适当的错误码
|
||
```
|
||
|
||
---
|
||
|
||
## 17. 日志服务 (Log Service)
|
||
|
||
```c
|
||
#include "logging_api.h"
|
||
|
||
// 关联日志存储到服务器日志对象
|
||
void IedServer_setLogStorage(IedServer self,
|
||
const char* logRef, // 日志对象引用 (如 "GenericIO/LLN0$EventLog")
|
||
LogStorage logStorage); // 日志存储实例
|
||
|
||
// SQLite 日志存储 (需包含 src/logging/drivers/sqlite/log_storage_sqlite.c)
|
||
extern LogStorage SqliteLogStorage_createInstance(const char* filename);
|
||
|
||
// 日志存储 API
|
||
void LogStorage_setMaxLogEntries(LogStorage self, int maxEntries);
|
||
uint64_t LogStorage_addEntry(LogStorage self, uint64_t timestamp);
|
||
void LogStorage_addEntryData(LogStorage self, uint64_t entryID,
|
||
const char* dataRef, const uint8_t* data, int dataSize, uint8_t reasonCode);
|
||
void LogStorage_getEntries(LogStorage self, uint64_t fromTime, uint64_t toTime,
|
||
LogEntryCallback entryCallback, LogEntryDataCallback entryDataCallback, void* parameter);
|
||
void LogStorage_destroy(LogStorage self);
|
||
```
|
||
|
||
---
|
||
|
||
## 18. TLS 安全通信
|
||
|
||
```c
|
||
#include "tls_config.h"
|
||
|
||
// 创建 TLS 配置
|
||
TLSConfiguration tlsConfig = TLSConfiguration_create();
|
||
|
||
// 加载服务器私钥
|
||
bool TLSConfiguration_setOwnKeyFromFile(TLSConfiguration self,
|
||
const char* keyFilename, const char* password);
|
||
// password: 私钥密码, 无密码时传 NULL
|
||
|
||
// 加载服务器证书
|
||
bool TLSConfiguration_setOwnCertificateFromFile(TLSConfiguration self,
|
||
const char* certFilename);
|
||
|
||
// 加载 CA 证书
|
||
bool TLSConfiguration_addCACertificateFromFile(TLSConfiguration self,
|
||
const char* certFilename);
|
||
|
||
// 加载允许的客户端证书 (白名单)
|
||
bool TLSConfiguration_addAllowedCertificateFromFile(TLSConfiguration self,
|
||
const char* certFilename);
|
||
|
||
// 证书链验证
|
||
void TLSConfiguration_setChainValidation(TLSConfiguration self, bool enable);
|
||
|
||
// 仅允许已知证书
|
||
void TLSConfiguration_setAllowOnlyKnownCertificates(TLSConfiguration self, bool enable);
|
||
|
||
// 安全事件处理器
|
||
void TLSConfiguration_setEventHandler(TLSConfiguration self,
|
||
TLSEventHandler handler, void* parameter);
|
||
|
||
// 使用 TLS 创建服务器
|
||
IedServer iedServer = IedServer_createWithTlsSupport(&iedModel, tlsConfig);
|
||
|
||
// 销毁 TLS 配置 (在 IedServer_destroy 之后)
|
||
TLSConfiguration_destroy(tlsConfig);
|
||
```
|
||
|
||
---
|
||
|
||
## 19. 单线程模式 (Threadless)
|
||
|
||
适用于资源受限的嵌入式环境,所有操作在单个线程中完成。
|
||
|
||
```c
|
||
// 启动 (不创建后台线程)
|
||
void IedServer_startThreadless(IedServer self, int tcpPort);
|
||
|
||
// 等待连接就绪 (带超时) - 可选
|
||
int IedServer_waitReady(IedServer self, unsigned int timeoutMs);
|
||
// 返回: 0 = 无连接就绪, !=0 = 至少一个连接就绪
|
||
// 等价于 Linux select()
|
||
|
||
// 处理收到的数据 - 必须周期性调用
|
||
void IedServer_processIncomingData(IedServer self);
|
||
|
||
// 执行周期后台任务 - 必须周期性调用
|
||
void IedServer_performPeriodicTasks(IedServer self);
|
||
|
||
// 停止
|
||
void IedServer_stopThreadless(IedServer self);
|
||
|
||
// 单线程模式主循环示例:
|
||
IedServer_startThreadless(iedServer, 102);
|
||
while (running) {
|
||
uint64_t now = Hal_getTimeInMs();
|
||
|
||
// 定期更新数据
|
||
if (now - lastUpdateTime >= 100) {
|
||
IedServer_lockDataModel(iedServer);
|
||
IedServer_updateFloatAttributeValue(iedServer, attr, value);
|
||
IedServer_unlockDataModel(iedServer);
|
||
lastUpdateTime = now;
|
||
}
|
||
|
||
// 处理网络事件
|
||
IedServer_processIncomingData(iedServer);
|
||
|
||
// 处理周期任务 (超时、报告等)
|
||
IedServer_performPeriodicTasks(iedServer);
|
||
|
||
Thread_sleep(1);
|
||
}
|
||
IedServer_stopThreadless(iedServer);
|
||
```
|
||
|
||
---
|
||
|
||
## 20. MmsServer 底层接口
|
||
|
||
通过 `IedServer_getMmsServer()` 获取后可用:
|
||
|
||
```c
|
||
// 设置最大连接数
|
||
void MmsServer_setMaxConnections(MmsServer self, int maxConnections);
|
||
|
||
// 文件服务
|
||
void MmsServer_setFilestoreBasepath(MmsServer self, const char* basepath);
|
||
void MmsServer_enableFileService(MmsServer self, bool enable);
|
||
|
||
// 待命名变量列表 (数据集) 服务
|
||
void MmsServer_enableDynamicNamedVariableListService(MmsServer self, bool enable);
|
||
void MmsServer_setMaxAssociationSpecificDataSets(MmsServer self, int maxDataSets);
|
||
void MmsServer_setMaxDomainSpecificDataSets(MmsServer self, int maxDataSets);
|
||
void MmsServer_setMaxDataSetEntries(MmsServer self, int maxDataSetEntries);
|
||
|
||
// 日志服务
|
||
void MmsServer_enableJournalService(MmsServer self, bool enable);
|
||
|
||
// 服务器身份
|
||
void MmsServer_setServerIdentity(MmsServer self,
|
||
char* vendorName, char* modelName, char* revision);
|
||
|
||
// VMD 状态
|
||
void MmsServer_setVMDStatus(MmsServer self,
|
||
int vmdLogicalStatus, int vmdPhysicalStatus);
|
||
// LOGICAL: 0=STATE_CHANGES_ALLOWED, 1=NO_CHANGES, 2=LIMITED_SERVICES, 3=SUPPORT_SERVICES
|
||
// PHYSICAL: 0=OPERATIONAL, 1=PARTIALLY, 2=INOPERATIONAL, 3=NEEDS_COMMISSIONING
|
||
```
|
||
|
||
---
|
||
|
||
## 21. 完整示例
|
||
|
||
### 21.1 最简单服务器
|
||
|
||
```c
|
||
#include "iec61850_server.h"
|
||
#include "hal_thread.h"
|
||
#include <signal.h>
|
||
#include "static_model.h"
|
||
|
||
static int running = 0;
|
||
|
||
void sigint_handler(int sig) { running = 0; }
|
||
|
||
int main(int argc, char** argv)
|
||
{
|
||
IedServer iedServer = IedServer_create(&iedModel);
|
||
IedServer_start(iedServer, 102);
|
||
|
||
if (!IedServer_isRunning(iedServer)) {
|
||
IedServer_destroy(iedServer);
|
||
return -1;
|
||
}
|
||
|
||
running = 1;
|
||
signal(SIGINT, sigint_handler);
|
||
|
||
while (running)
|
||
Thread_sleep(1);
|
||
|
||
IedServer_stop(iedServer);
|
||
IedServer_destroy(iedServer);
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
### 21.2 带控制、报告和 GOOSE 的完整服务器
|
||
|
||
```c
|
||
#include "iec61850_server.h"
|
||
#include "hal_thread.h"
|
||
#include <signal.h>
|
||
#include <math.h>
|
||
#include "static_model.h"
|
||
|
||
static int running = 0;
|
||
static IedServer iedServer = NULL;
|
||
|
||
// --- 控制处理器 ---
|
||
static ControlHandlerResult
|
||
controlHandler(ControlAction action, void* parameter, MmsValue* value, bool test)
|
||
{
|
||
if (test) return CONTROL_RESULT_FAILED;
|
||
if (MmsValue_getType(value) != MMS_BOOLEAN) return CONTROL_RESULT_FAILED;
|
||
|
||
uint64_t ts = Hal_getTimeInMs();
|
||
IedServer_updateUTCTimeAttributeValue(iedServer,
|
||
IEDMODEL_GenericIO_GGIO1_SPCSO1_t, ts);
|
||
IedServer_updateAttributeValue(iedServer,
|
||
IEDMODEL_GenericIO_GGIO1_SPCSO1_stVal, value);
|
||
|
||
return CONTROL_RESULT_OK;
|
||
}
|
||
|
||
// --- 连接处理器 ---
|
||
static void
|
||
connectionHandler(IedServer self, ClientConnection conn, bool connected, void* param)
|
||
{
|
||
printf("Client %s %s\n",
|
||
ClientConnection_getPeerAddress(conn),
|
||
connected ? "connected" : "disconnected");
|
||
}
|
||
|
||
// --- RCB 事件处理器 ---
|
||
static void
|
||
rcbHandler(void* param, ReportControlBlock* rcb, ClientConnection conn,
|
||
IedServer_RCBEventType event, const char* paramName,
|
||
MmsDataAccessError err)
|
||
{
|
||
printf("RCB %s event: %i\n", ReportControlBlock_getName(rcb), event);
|
||
}
|
||
|
||
// --- GoCB 事件处理器 ---
|
||
static void
|
||
goCbHandler(MmsGooseControlBlock goCb, int event, void* param)
|
||
{
|
||
printf("GoCB %s: %s\n",
|
||
MmsGooseControlBlock_getName(goCb),
|
||
event == IEC61850_GOCB_EVENT_ENABLE ? "ENABLED" : "DISABLED");
|
||
}
|
||
|
||
int main(int argc, char** argv)
|
||
{
|
||
int port = (argc > 1) ? atoi(argv[1]) : 102;
|
||
|
||
IedServerConfig cfg = IedServerConfig_create();
|
||
IedServerConfig_setReportBufferSize(cfg, 200000);
|
||
IedServerConfig_setMaxMmsConnections(cfg, 5);
|
||
|
||
iedServer = IedServer_createWithConfig(&iedModel, NULL, cfg);
|
||
IedServerConfig_destroy(cfg);
|
||
|
||
// 安装处理器
|
||
IedServer_setControlHandler(iedServer,
|
||
IEDMODEL_GenericIO_GGIO1_SPCSO1,
|
||
(ControlHandler) controlHandler,
|
||
IEDMODEL_GenericIO_GGIO1_SPCSO1);
|
||
|
||
IedServer_setConnectionIndicationHandler(iedServer,
|
||
(IedConnectionIndicationHandler) connectionHandler, NULL);
|
||
|
||
IedServer_setRCBEventHandler(iedServer, rcbHandler, NULL);
|
||
IedServer_setGoCBHandler(iedServer, goCbHandler, NULL);
|
||
|
||
// 允许客户端写 DC/CF
|
||
IedServer_setWriteAccessPolicy(iedServer, IEC61850_FC_DC, ACCESS_POLICY_ALLOW);
|
||
|
||
// 设置 GOOSE 接口并启动发布
|
||
IedServer_setGooseInterfaceId(iedServer, "eth0");
|
||
|
||
IedServer_start(iedServer, port);
|
||
|
||
if (!IedServer_isRunning(iedServer)) {
|
||
printf("Start failed!\n");
|
||
IedServer_destroy(iedServer);
|
||
return -1;
|
||
}
|
||
|
||
IedServer_enableGoosePublishing(iedServer);
|
||
|
||
running = 1;
|
||
signal(SIGINT, sigint_handler);
|
||
|
||
float t = 0.f;
|
||
while (running) {
|
||
uint64_t ts = Hal_getTimeInMs();
|
||
|
||
// 构建时间戳
|
||
Timestamp iecTs;
|
||
Timestamp_clearFlags(&iecTs);
|
||
Timestamp_setTimeInMilliseconds(&iecTs, ts);
|
||
Timestamp_setLeapSecondKnown(&iecTs, true);
|
||
|
||
IedServer_lockDataModel(iedServer);
|
||
|
||
IedServer_updateFloatAttributeValue(iedServer,
|
||
IEDMODEL_GenericIO_GGIO1_AnIn1_mag_f, sinf(t));
|
||
IedServer_updateTimestampAttributeValue(iedServer,
|
||
IEDMODEL_GenericIO_GGIO1_AnIn1_t, &iecTs);
|
||
IedServer_updateQuality(iedServer,
|
||
IEDMODEL_GenericIO_GGIO1_AnIn1_q, QUALITY_VALIDITY_GOOD);
|
||
|
||
IedServer_unlockDataModel(iedServer);
|
||
|
||
t += 0.1f;
|
||
Thread_sleep(100);
|
||
}
|
||
|
||
IedServer_stop(iedServer);
|
||
IedServer_destroy(iedServer);
|
||
return 0;
|
||
}
|
||
```
|