48 KiB
48 KiB
libiec61850 服务端开发手册
基于 libiec61850 v1.5.3 完整 API 参考。本文档旨在做到脱离代码即可完成服务端开发。
目录
- 架构概述
- 数据模型
- 服务器生命周期
- IedServerConfig 配置详解
- 服务器创建与销毁
- 数据模型锁定与更新
- 数据读取函数
- 连接管理与认证
- 控制模型 (Control)
- 报告控制块 (RCB)
- GOOSE 发布
- GOOSE 控制块 (GoCB)
- Sampled Values 控制块 (SVCB)
- 数据访问控制 (Access Control)
- 设置组 (Setting Groups)
- 文件服务
- 日志服务 (Log Service)
- TLS 安全通信
- 单线程模式 (Threadless)
- MmsServer 底层接口
- 完整示例
1. 架构概述
服务端核心职责:
应用程序数据 --> [MmsValue] --> IedServer --> TCP/MMS --> 客户端
|
+--> GOOSE 发布 (L2 以太网)
+--> SV 发布 (L2 以太网)
关键类型:
IedServer— 服务器实例句柄(不透明指针)IedServerConfig— 服务器配置对象IedModel— 数据模型根节点LogicalDevice→LogicalNode→DataObject→DataAttribute— 数据模型层级
2. 数据模型
2.1 模型节点类型枚举
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
#include "static_model.h"
// 直接使用生成的全局模型
IedServer iedServer = IedServer_create(&iedModel);
方式二: API 动态创建
通过 iec61850_dynamic_model.h API 在运行时构建模型:
#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 配置文件,然后运行时加载:
#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)
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)
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)
#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)
#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. 服务器生命周期
// 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 配置详解
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 配置函数清单
// 创建与销毁
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. 服务器创建与销毁
// 创建服务器实例
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);
启动与停止
// === 多线程模式 ===
// 启动服务器 (创建后台线程)
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);
时间品质设置
void IedServer_setTimeQuality(IedServer self,
bool leapSecondKnown, // 闰秒已知
bool clockFailure, // 时钟故障
bool clockNotSynchronized, // 时钟未同步
int subsecondPrecision); // 亚秒精度 (fractionOfSecond 的有效位数)
6. 数据模型锁定与更新
6.1 线程安全
重要规则: 在回调函数内部(ControlHandler、WriteAccessHandler 等),数据模型已经被锁住!不要在回调函数内部再调用 lockDataModel,否则会导致死锁。
// 锁定数据模型 (暂停处理客户端请求)
void IedServer_lockDataModel(IedServer self);
// 解锁数据模型 (处理暂停的客户端请求)
void IedServer_unlockDataModel(IedServer self);
6.2 更新数据属性值
所有 IedServer_update* 函数都会自动:
- 检查触发条件是否满足 (dchg/qchg/dupd)
- 如果满足触发条件,通知相应的 RCB/GCB
- 自动更新内部缓存
// === 通用更新函数 ===
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. 数据读取函数
// 获取数据属性底层 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 数据更新典型模式
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 连接指示处理器
// 回调函数类型
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 操作
// 获取客户端对端地址 (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)
// 设置认证回调
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)
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 控制处理器回调类型
// 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 设置控制处理器
// 设置控制处理器 (必需 - 在有控制模型中)
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 获取更多上下文信息:
// 获取客户端连接对象
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 更新控制模型
void IedServer_updateCtlModel(IedServer self,
DataObject* ctlObject, ControlModel value);
// 同时更新 "ctlModel" 属性值和内部控制结构
// 注意: 对应的控制模型数据结构必须在数据模型中存在!
9.6 控制处理器典型实现
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 事件类型
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 事件处理器
// 回调类型
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 发布控制
// 启用所有 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,或者需要独立控制时:
#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)
// 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)
// 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)
// 回调类型
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 级写策略
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)
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 结构体
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 活动设置组操作
// 服务器内部主动切换活动设置组
void IedServer_changeActiveSettingGroup(IedServer self,
SettingGroupControlBlock* sgcb, uint8_t newActiveSg);
// 调用前用户应先更新 FC=SG 的数据属性
// 获取当前活动设置组号
uint8_t IedServer_getActiveSettingGroup(IedServer self,
SettingGroupControlBlock* sgcb);
15.3 设置组回调处理器
// 活动设置组变化处理器
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 基础配置
// 设置文件服务根目录
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 文件访问处理器
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)
#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 安全通信
#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)
适用于资源受限的嵌入式环境,所有操作在单个线程中完成。
// 启动 (不创建后台线程)
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() 获取后可用:
// 设置最大连接数
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 最简单服务器
#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 的完整服务器
#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;
}