RTU/claude/工程/libiec61850_MMS服务端API开发手册.md

1295 lines
44 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# libiec61850-1.5.3 MMS 服务端 API 开发手册
> 本文档基于 libiec61850 v1.5.3 源码,覆盖 IEC 61850 服务端全部公开 C API达到开发者脱离源码即可进行项目开发的标准。
---
## 目录
1. [架构概述](#1-架构概述)
2. [数据模型创建(动态模型)](#2-数据模型创建动态模型)
3. [服务器配置IedServerConfig](#3-服务器配置iedserverconfig)
4. [服务器生命周期管理](#4-服务器生命周期管理)
5. [数据值读取与更新](#5-数据值读取与更新)
6. [控制模型回调](#6-控制模型回调)
7. [报告控制块RCB事件处理](#7-报告控制块rcb事件处理)
8. [读写访问控制](#8-读写访问控制)
9. [定值组SGCB处理](#9-定值组sgcb处理)
10. [GOOSE 发布](#10-goose-发布)
11. [SV 控制块](#11-sv-控制块)
12. [连接管理与认证](#12-连接管理与认证)
13. [日志服务](#13-日志服务)
14. [运行模式:线程模式 vs 非线程模式](#14-运行模式线程模式-vs-非线程模式)
15. [与 RTU 项目对照](#15-与-rtu-项目对照)
16. [附录:公共数据类型速查](#16-附录公共数据类型速查)
---
## 1. 架构概述
服务端 API 提供两个抽象层级:
| 层级 | API 头文件 | 描述 |
|------|-----------|------|
| **高层 IEC 61850** | `iec61850_server.h` | 封装 IEC 61850 语义LD/LN/DO/DA/RCB/SGCB推荐使用 |
| **底层 MMS** | `mms_server.h` | MMS 协议层服务器,一般不需要直接使用 |
核心不透明句柄为 `IedServer`,所有服务端操作围绕它展开。
### 关键头文件依赖
```
iec61850_server.h
├── iec61850_dynamic_model.h ← 动态创建数据模型IedModel/LD/LN/DO/DA/RCB/DataSet
├── iec61850_model.h ← 静态模型(自动生成)
├── iec61850_common.h ← FC/Quality/Timestamp/ControlModel
├── mms_server.h ← 底层 MMS 服务器
├── mms_value.h ← MmsValue 数据类型
└── iso_connection_parameters.h ← ISO 连接参数
```
### 服务端完整工作流
```
1. 创建数据模型 (IedModel → LD → LN → DO → DA → RCB → DataSet)
2. 创建配置 (IedServerConfig)
3. 创建服务器 (IedServer_createWithConfig)
4. 设置各种回调 (控制/RCB/写访问/读访问/连接认证)
5. 启动服务器 (IedServer_start) 或非线程模式启动 (IedServer_startThreadless)
6. 运行时更新数据值 (IedServer_update*AttributeValue)
7. 停止/销毁 (IedServer_stop + IedServer_destroy)
```
---
## 2. 数据模型创建(动态模型)
### 2.1 模型层级结构
```
IedModel (IED)
└─ LogicalDevice (LD) "TEMPLATE"
├─ LogicalNode (LN) "LLN0" ← 每个LD必须包含LLN0
│ ├─ DataObject (DO) "Mod" ← 模式
│ ├─ DataObject (DO) "Beh" ← 行为
│ ├─ DataObject (DO) "Health"
│ ├─ ReportControlBlock (RCB)
│ ├─ DataSet
│ └─ SettingGroupControlBlock (SGCB)
└─ LogicalNode (LN) "GGIO1" ← 通用IO
├─ DataObject (DO) "Ind1" (array=0)
│ ├─ DataAttribute (DA) "stVal" [FC=ST, type=FLOAT32, trgOps=dchg]
│ └─ DataAttribute (DA) "q" [FC=ST, type=QUALITY]
└─ DataObject (DO) "SPCSO1"
├─ DataAttribute (DA) "ctlVal" [FC=CO, type=BOOLEAN]
└─ DataAttribute (DA) "stVal" [FC=ST, type=BOOLEAN, trgOps=dchg]
```
### 2.2 完整创建流程
```c
// 1. 创建 IED 模型
IedModel* model = IedModel_create("MyIED");
IedModel_setIedNameForDynamicModel(model, "RTU"); // 必须在 IedServer_create 前调用
// 2. 创建逻辑设备
LogicalDevice* ld = LogicalDevice_create("TEMPLATE", model);
// 3. 创建 LLN0 逻辑节点(每个 LD 必须包含)
LogicalNode* ln0 = LogicalNode_create("LLN0", ld);
// 4. 创建 LLN0 下的常用数据对象
DataObject* mod = DataObject_create("Mod", ln0, 0);
DataObject* beh = DataObject_create("Beh", ln0, 0);
DataObject* health = DataObject_create("Health", ln0, 0);
// 5. 创建应用逻辑节点 GGIO1
LogicalNode* ggio1 = LogicalNode_create("GGIO1", ld);
// 6. 创建数据对象 Ind1单点遥信
DataObject* ind1 = DataObject_create("Ind1", ggio1, 0);
// 7. 创建数据属性 stVal状态值+ q品质+ t时标
// DataAttribute_create(名称, 父节点, 类型, FC, 触发选项, 数组大小, 短地址)
DataAttribute* stVal = DataAttribute_create("stVal", ind1,
IEC61850_FLOAT32, // 类型
IEC61850_FC_ST, // 功能约束
TRG_OPT_DATA_CHANGED, // 数据变化时触发报告
0, // 非数组
NULL); // 无短地址
DataAttribute* q = DataAttribute_create("q", ind1,
IEC61850_QUALITY, IEC61850_FC_ST, 0, 0, NULL);
DataAttribute* t = DataAttribute_create("t", ind1,
IEC61850_TIMESTAMP, IEC61850_FC_ST, 0, 0, NULL);
// 8. 创建可控数据对象(遥控)
DataObject* spcso1 = DataObject_create("SPCSO1", ggio1, 0);
DataAttribute* ctlVal = DataAttribute_create("ctlVal", spcso1,
IEC61850_BOOLEAN, IEC61850_FC_CO, 0, 0, NULL);
DataAttribute* stVal2 = DataAttribute_create("stVal", spcso1,
IEC61850_BOOLEAN, IEC61850_FC_ST, TRG_OPT_DATA_CHANGED, 0, NULL);
DataAttribute* q2 = DataAttribute_create("q", spcso1,
IEC61850_QUALITY, IEC61850_FC_ST, 0, 0, NULL);
DataAttribute* t2 = DataAttribute_create("t", spcso1,
IEC61850_TIMESTAMP, IEC61850_FC_ST, 0, 0, NULL);
```
### 2.3 创建报告控制块RCB
```c
// 在 LLN0 下创建 RCB
ReportControlBlock* rcb = ReportControlBlock_create(
"EventsRCB01", // 名称(会变成 LLN0.RP.EventsRCB01 或 .BR.
ln0, // 父节点 LLN0
NULL, // rptIdNULL = 使用默认(对象引用)
false, // isBuffered: false=URCB, true=BRCB
"TEMPLATE/LLN0$Events", // 数据集引用
1, // confRef: 配置版本
TRG_OPT_DATA_CHANGED | TRG_OPT_INTEGRITY | TRG_OPT_GI, // trgOps
RPT_OPT_SEQ_NUM | RPT_OPT_TIME_STAMP |
RPT_OPT_REASON_FOR_INCLUSION | RPT_OPT_DATA_SET |
RPT_OPT_DATA_REFERENCE, // options (OptFlds)
100, // bufTm: 缓冲时间 100ms
60000 // intgPd: 完整性周期 60s
);
// 设置预配置客户端可选用于指定哪些客户端可以使用此RCB
// uint8_t ipv4[4] = {192, 168, 1, 100};
// ReportControlBlock_setPreconfiguredClient(rcb, 4, ipv4);
// RCB 信息查询
const char* name = ReportControlBlock_getName(rcb);
bool isBuffered = ReportControlBlock_isBuffered(rcb); // true=BRCB, false=URCB
LogicalNode* parent = ReportControlBlock_getParent(rcb);
char* rptId = ReportControlBlock_getRptID(rcb); // 需手动 free
bool ena = ReportControlBlock_getRptEna(rcb); // 当前是否使能
char* ds = ReportControlBlock_getDataSet(rcb); // 需手动 free
uint32_t confRev = ReportControlBlock_getConfRev(rcb);
uint32_t optFlds = ReportControlBlock_getOptFlds(rcb);
uint32_t bufTm = ReportControlBlock_getBufTm(rcb);
uint16_t sqNum = ReportControlBlock_getSqNum(rcb); // 当前序列号
uint32_t trgOps = ReportControlBlock_getTrgOps(rcb);
uint32_t intgPd = ReportControlBlock_getIntgPd(rcb);
bool gi = ReportControlBlock_getGI(rcb);
bool purgeBuf = ReportControlBlock_getPurgeBuf(rcb);
MmsValue* entryId = ReportControlBlock_getEntryId(rcb);
uint64_t timeOfEntry = ReportControlBlock_getTimeofEntry(rcb);
int16_t resvTms = ReportControlBlock_getResvTms(rcb);
bool resv = ReportControlBlock_getResv(rcb);
MmsValue* owner = ReportControlBlock_getOwner(rcb);
```
### 2.4 创建数据集DataSet
```c
// 创建数据集
DataSet* ds = DataSet_create("Events", ln0); // 名称 + LLN0 父节点
// 添加数据集成员FCDA 引用)
// DataSetEntry_create(数据集, 变量名, 索引, 组件名)
// 变量名格式: "LN名$FC$DO名$DA名"(用$而非.分隔不要LD名前缀
DataSetEntry_create(ds, "GGIO1$ST$Ind1$stVal", -1, NULL);
DataSetEntry_create(ds, "GGIO1$ST$Ind1$q", -1, NULL);
DataSetEntry_create(ds, "GGIO1$ST$Ind1$t", -1, NULL);
// 数据集查询
const char* dsName = DataSet_getName(ds);
int dsSize = DataSet_getSize(ds); // 成员数
DataSetEntry* first = DataSet_getFirstEntry(ds);
DataSetEntry* next = DataSetEntry_getNext(first);
```
### 2.5 设置数据属性默认值
```c
// 在创建 IedServer 之前可以设置默认值
MmsValue* defaultVal = MmsValue_newFloat(0.0f);
DataAttribute_setValue(stVal, defaultVal);
MmsValue_delete(defaultVal);
```
### 2.6 DA 属性查询
```c
DataAttributeType DataAttribute_getType(DataAttribute* self);
FunctionalConstraint DataAttribute_getFC(DataAttribute* self);
uint8_t DataAttribute_getTrgOps(DataAttribute* self);
```
### 2.7 创建其他控制块
```c
// 定值组控制块
SettingGroupControlBlock* sgcb = SettingGroupControlBlock_create(
ln0, // 父节点 LLN0
1, // actSG: 启动时活跃定值组
8); // numOfSGs: 定值组数量
// GOOSE 控制块
GSEControlBlock* gcb = GSEControlBlock_create(
"gcbEvents", ln0, "appId", "TEMPLATE/LLN0$GooseDS",
1, // confRev
false, // fixedOffs
-1, // minTime-1=使用默认
-1); // maxTime-1=使用默认
GSEControlBlock_addPhyComAddress(gcb, phyComAddress);
// Sampled Values 控制块
SVControlBlock* svcb = SVControlBlock_create(
"MSVCB01", ln0, "svID", "TEMPLATE/LLN0$SvDS",
1, // confRev
IEC61850_SV_SMPMOD_SAMPLES_PER_PERIOD, // smpMod
80, // smpRate (如 80 采样/周期)
IEC61850_SV_OPT_REFRESH_TIME | IEC61850_SV_OPT_SAMPLE_SYNC, // optFlds
false); // isUnicast: false=multicast
SVControlBlock_addPhyComAddress(svcb, phyComAddress);
// 日志控制块
LogControlBlock* lcb = LogControlBlock_create(
"LogCB01", ln0, "TEMPLATE/LLN0$Events",
"TEMPLATE/LLN0$MyLog", TRG_OPT_DATA_CHANGED, 60000, false, true);
// 日志对象
Log* log = Log_create("MyLog", ln0);
// PhyComAddress
uint8_t mac[6] = {0x01, 0x0C, 0xCD, 0x01, 0x00, 0x01};
PhyComAddress* addr = PhyComAddress_create(
4, // vlanPriority
100, // vlanId
0x4000, // appId
mac); // dstAddress
```
### 2.8 模型销毁
```c
// 销毁动态创建的数据模型
// 注意:一定要在 IedServer_destroy 之后调用,否则会导致资源泄漏
IedModel_destroy(model);
```
### 2.9 数据属性类型枚举DataAttributeType
创建 DA 时使用的类型常量:
```c
IEC61850_BOOLEAN // MMS_BOOLEAN
IEC61850_INT8 // MMS_INTEGER (8bit)
IEC61850_INT16 // MMS_INTEGER (16bit)
IEC61850_INT32 // MMS_INTEGER (32bit)
IEC61850_INT64 // MMS_INTEGER (64bit)
IEC61850_INT8U // MMS_UNSIGNED (8bit)
IEC61850_INT16U // MMS_UNSIGNED (16bit)
IEC61850_INT32U // MMS_UNSIGNED (32bit)
IEC61850_FLOAT32 // MMS_FLOAT (32bit)
IEC61850_FLOAT64 // MMS_FLOAT (64bit)
IEC61850_QUALITY // MMS_BIT_STRING (13bit, 品质)
IEC61850_TIMESTAMP // MMS_UTC_TIME
IEC61850_VISSTRING32 // MMS_VISIBLE_STRING (max 32)
IEC61850_VISSTRING64 // MMS_VISIBLE_STRING (max 64)
IEC61850_VISSTRING129 // MMS_VISIBLE_STRING (max 129)
IEC61850_VISSTRING255 // MMS_VISIBLE_STRING (max 255)
IEC61850_DBPOS // MMS_BIT_STRING (双点位置)
IEC61850_CONSTRUCTED // 复合类型(如 AnalogueValue
```
---
## 3. 服务器配置IedServerConfig
### 3.1 配置结构体字段
```c
typedef struct sIedServerConfig {
int reportBufferSize; // BRCB 报告缓冲区大小
int reportBufferSizeURCBs; // URCB 报告缓冲区大小
char* fileServiceBasepath; // 文件服务根目录
bool enableFileService; // 是否启用文件服务
bool enableDynamicDataSetService; // 是否允许动态数据集
int maxAssociationSpecificDataSets; // 每连接最大关联数据集数
int maxDomainSpecificDataSets; // 最大域数据集数
int maxDataSetEntries; // 数据集最大条目数
bool enableLogService; // 是否启用日志服务
bool useIntegratedGoosePublisher; // 是否使用内置 GOOSE 发布器
uint8_t edition; // IEC 61850 版本0=E1, 1=E2, 2=E2.1
int maxMmsConnections; // 最大 MMS 连接数
bool enableEditSG; // 是否允许 EditSG 服务
bool enableResvTmsForSGCB; // SGCB 是否可见 ResvTms
bool enableResvTmsForBRCB; // BRCB 是否可见 ResvTms
bool enableOwnerForRCB; // RCB 是否可见 owner 属性
bool syncIntegrityReportTimes; // 是否同步完整性报告时间
uint8_t reportSettingsWritable; // 哪些报告设置可写(位掩码)
} IedServerConfig;
```
### 3.2 配置 API
```c
IedServerConfig config = IedServerConfig_create();
// 标准配置
IedServerConfig_setReportBufferSize(config, 100); // BRCB 缓冲区
IedServerConfig_setReportBufferSizeForURCBs(config, 10); // URCB 缓冲区
IedServerConfig_setMaxMmsConnections(config, 5); // 最大连接数
IedServerConfig_setFileServiceBasePath(config, "./files");
IedServerConfig_enableFileService(config, true);
IedServerConfig_setEdition(config, IEC_61850_EDITION_2); // 版本
// 动态数据集
IedServerConfig_enableDynamicDataSetService(config, true);
IedServerConfig_setMaxAssociationSpecificDataSets(config, 10);
IedServerConfig_setMaxDomainSpecificDataSets(config, 5);
IedServerConfig_setMaxDataSetEntries(config, 128);
// 日志
IedServerConfig_enableLogService(config, false);
// 定值组
IedServerConfig_enableEditSG(config, true);
IedServerConfig_enableResvTmsForSGCB(config, true);
// RCB
IedServerConfig_enableResvTmsForBRCB(config, true);
IedServerConfig_enableOwnerForRCB(config, false);
// GOOSE
IedServerConfig_useIntegratedGoosePublisher(config, true);
// 完整性报告时间同步
IedServerConfig_setSyncIntegrityReportTimes(config, false);
// 报告设置可写性IEC61850_REPORTSETTINGS_* 常量组合)
IedServerConfig_setReportSetting(config, IEC61850_REPORTSETTINGS_RPT_ID, true); // 允许客户端修改 RptID
IedServerConfig_setReportSetting(config, IEC61850_REPORTSETTINGS_DATSET, true); // 允许客户端修改数据集
// 销毁配置
IedServerConfig_destroy(config);
```
**报告设置可写性常量**
```c
#define IEC61850_REPORTSETTINGS_RPT_ID 1
#define IEC61850_REPORTSETTINGS_BUF_TIME 2
#define IEC61850_REPORTSETTINGS_DATSET 4
#define IEC61850_REPORTSETTINGS_TRG_OPS 8
#define IEC61850_REPORTSETTINGS_OPT_FIELDS 16
#define IEC61850_REPORTSETTINGS_INTG_PD 32
```
### 3.3 查询配置
```c
uint8_t edition = IedServerConfig_getEdition(config);
int bufSize = IedServerConfig_getReportBufferSize(config);
int urcbBufSize = IedServerConfig_getReportBufferSizeForURCBs(config);
int maxConn = IedServerConfig_getMaxMmsConnections(config);
const char* path = IedServerConfig_getFileServiceBasePath(config);
bool fsEnabled = IedServerConfig_isFileServiceEnabled(config);
bool dsEnabled = IedServerConfig_isDynamicDataSetServiceEnabled(config);
int maxAssocDS = IedServerConfig_getMaxAssociationSpecificDataSets(config);
int maxDomainDS = IedServerConfig_getMaxDomainSpecificDataSets(config);
int maxDsEntries = IedServerConfig_getMaxDatasSetEntries(config);
bool logEnabled = IedServerConfig_isLogServiceEnabled(config);
bool syncRt = IedServerConfig_getSyncIntegrityReportTimes(config);
bool resvTmsBRCB = IedServerConfig_isResvTmsForBRCBEnabled(config);
bool ownerRCB = IedServerConfig_isOwnerForRCBEnabled(config);
bool reportSetting = IedServerConfig_getReportSetting(config, IEC61850_REPORTSETTINGS_TRG_OPS);
```
---
## 4. 服务器生命周期管理
### 4.1 创建服务器
```c
// 简单创建(仅需数据模型)
IedServer server = IedServer_create(model);
// TLS 支持
IedServer server = IedServer_createWithTlsSupport(model, tlsConfig);
// 完整配置创建(推荐)
IedServerConfig config = IedServerConfig_create();
// ... 配置 config ...
IedServer server = IedServer_createWithConfig(model, NULL, config); // NULL=无TLS
// IedServerConfig_destroy(config); // 创建后可销毁 config
```
### 4.2 附加访问点
```c
// 为服务器添加额外的监听地址(可在 start 前多次添加)
// 返回 true 成功false 失败
IedServer_addAccessPoint(server, "192.168.2.1", 102, NULL);
```
### 4.3 设置服务器属性
```c
// 设置监听地址(默认监听所有接口)
IedServer_setLocalIpAddress(server, "0.0.0.0");
// 设置 MMS identify 服务响应(需要 CONFIG_IEC61850_SUPPORT_SERVER_IDENTITY
IedServer_setServerIdentity(server, "VendorName", "ModelName", "1.0");
// 设置运行时文件服务根目录(需要 CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME
IedServer_setFilestoreBasepath(server, "/data/files");
// 设置时间品质(可在运行时更新)
IedServer_setTimeQuality(server,
true, // leapSecondKnown 位7: 闰秒已知
false, // clockFailure 位6: 时钟故障
false, // clockNotSynchronized 位5: 时钟未同步
10); // subsecondPrecision 位0-4: 亚秒精度fractionOfSecond 有效位数)
```
### 4.4 启动/停止(线程模式)
```c
// 启动(线程模式)—— 内部创建后台线程
IedServer_start(server, 102); // 102 = MMS 默认端口,-1 = 使用默认端口
// 检查运行状态
bool running = IedServer_isRunning(server);
// 获取当前连接数
int connections = IedServer_getNumberOfOpenConnections(server);
// 停止
IedServer_stop(server);
// 销毁
IedServer_destroy(server);
// 注意:需要单独销毁数据模型 IedModel_destroy(model)
```
### 4.5 启动/停止(非线程模式)
```c
// 启动(非线程模式)—— 用户驱动消息循环
IedServer_startThreadless(server, 102);
// 主循环
while (running) {
// 等待连接就绪(可选,类似 select
int ready = IedServer_waitReady(server, 100); // 超时 100ms
if (ready != 0) {
// 处理收到的数据
IedServer_processIncomingData(server);
}
// 执行周期性任务(报告生成、超时检查等)
IedServer_performPeriodicTasks(server);
}
// 停止
IedServer_stopThreadless(server);
// 销毁
IedServer_destroy(server);
```
### 4.6 底层访问
```c
// 获取数据模型
IedModel* model = IedServer_getDataModel(server);
// 获取底层 MmsServer谨慎使用直接操作可能干扰 IedServer
MmsServer mmsServer = IedServer_getMmsServer(server);
```
---
## 5. 数据值读取与更新
### 5.1 读取属性值
```c
// 通用读取(返回 MmsValue*
MmsValue* val = IedServer_getAttributeValue(server, stVal);
// 类型便捷读取
bool b = IedServer_getBooleanAttributeValue(server, da);
float f = IedServer_getFloatAttributeValue(server, da);
int32_t i32 = IedServer_getInt32AttributeValue(server, da);
int64_t i64 = IedServer_getInt64AttributeValue(server, da);
uint32_t u32 = IedServer_getUInt32AttributeValue(server, da);
uint64_t utc = IedServer_getUTCTimeAttributeValue(server, da); // ms
uint32_t bs = IedServer_getBitStringAttributeValue(server, da);
const char* s = IedServer_getStringAttributeValue(server, da); // VISIBLE_STRING/STRING
// 获取某个 FC 下的 FCD 对象(绕过报告通知机制,直接操作底层值)
MmsValue* fcd = IedServer_getFunctionalConstrainedData(server, dataObject, IEC61850_FC_ST);
// 警告:直接操作 FCD 不会触发报告,需谨慎使用
```
### 5.2 更新属性值(自动触发报告和 GOOSE
```c
// 通用更新
IedServer_updateAttributeValue(server, da, mmsValue);
// 类型便捷更新 — 这些函数会自动检查触发条件dchg/qchg/dupd并触发报告
IedServer_updateFloatAttributeValue(server, da, 35.5f);
IedServer_updateInt32AttributeValue(server, da, 42);
IedServer_updateInt64AttributeValue(server, da, 12345678901234LL);
IedServer_updateUnsignedAttributeValue(server, da, 100);
IedServer_updateBooleanAttributeValue(server, da, true);
IedServer_updateVisibleStringAttributeValue(server, da, "Hello");
IedServer_updateBitStringAttributeValue(server, da, bitStringInt);
IedServer_updateUTCTimeAttributeValue(server, da, msTimestamp);
IedServer_updateTimestampAttributeValue(server, da, timestamp);
IedServer_updateDbposValue(server, da, DBPOS_ON); // 双点ON/OFF/中间态/坏态
IedServer_updateQuality(server, da, quality); // 品质更新(触发 qchg
```
### 5.3 批量更新(加锁)
```c
// 更新多个值时加锁以提高效率
IedServer_lockDataModel(server);
IedServer_updateFloatAttributeValue(server, da1, 1.5f);
IedServer_updateFloatAttributeValue(server, da2, 2.5f);
IedServer_updateFloatAttributeValue(server, da3, 3.5f);
IedServer_unlockDataModel(server); // 解锁后将触发一次通知
// 警告:绝对不要在库回调函数内部调用 lockDataModel
// 库回调中数据模型已经锁定,再次锁定会导致死锁
```
### 5.4 更新控制模型
```c
// 更新可控数据对象的控制模型
IedServer_updateCtlModel(server, ctlObject, CONTROL_MODEL_SBO_NORMAL);
// 注意:对应的控制结构必须在数据模型中存在
```
---
## 6. 控制模型回调
服务端通过三个层级的回调实现 IEC 61850 的遥控机制。
### 6.1 控制操作完整流程
```
客户端发送控制命令
PerformCheck 回调(静态测试:联锁、权限等)
↓ (通过)
WaitForExecution 回调(动态测试:同步检查等)
↓ (通过)
Control 回调(实际执行:操作继电器等)
CommandTermination 响应返回客户端
```
### 6.2 回调返回值
```c
// PerformCheck 回调返回值
typedef enum {
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 // ctlVal 超出范围
} CheckHandlerResult;
// Control / WaitForExecution 回调返回值
typedef enum {
CONTROL_RESULT_FAILED = 0, // 失败
CONTROL_RESULT_OK = 1, // 成功
CONTROL_RESULT_WAITING = 2 // 等待中(异步执行,稍后再次调用)
} ControlHandlerResult;
```
### 6.3 ControlAction 上下文信息
```c
typedef void* ControlAction;
// 设置错误附加信息(在回调中使用)
void ControlAction_setError(ControlAction self, ControlLastApplError error);
void ControlAction_setAddCause(ControlAction self, ControlAddCause addCause);
// 获取客户端上下文
int ControlAction_getOrCat(ControlAction self); // 发起者类别
uint8_t* ControlAction_getOrIdent(ControlAction self, int* size); // 发起者标识
int ControlAction_getCtlNum(ControlAction self); // 控制序号
bool ControlAction_getSynchroCheck(ControlAction self); // 同步检查位
bool ControlAction_getInterlockCheck(ControlAction self); // 联锁检查位
bool ControlAction_isSelect(ControlAction self); // 是否是 Select 操作
ClientConnection ControlAction_getClientConnection(ControlAction self); // 客户端连接
DataObject* ControlAction_getControlObject(ControlAction self); // 控制对象
uint64_t ControlAction_getControlTime(ControlAction self); // 时间激活控制时间0=立即)
```
### 6.4 注册控制回调
```c
// 实际执行回调(必须注册)
CheckHandlerResult myPerformCheck(ControlAction action, void* param,
MmsValue* ctlVal, bool test, bool interlockCheck) {
// 静态测试:检查联锁条件、访问权限等
if (!interlock_ok) {
ControlAction_setAddCause(action, ADD_CAUSE_BLOCKED_BY_INTERLOCKING);
return CONTROL_OBJECT_ACCESS_DENIED;
}
return CONTROL_ACCEPTED;
}
ControlHandlerResult myWaitForExecution(ControlAction action, void* param,
MmsValue* ctlVal, bool test, bool synchroCheck) {
// 动态测试:检查同步条件等
// 如果测试不能立即完成,返回 CONTROL_RESULT_WAITING稍后会再次调用
if (needs_wait) return CONTROL_RESULT_WAITING;
return CONTROL_RESULT_OK;
}
ControlHandlerResult myControl(ControlAction action, void* param,
MmsValue* ctlVal, bool test) {
// 实际执行:控制继电器、输出信号等
bool val = MmsValue_getBoolean(ctlVal);
if (test) {
// 测试模式:不影响实际物理过程
return CONTROL_RESULT_OK;
}
set_output(val); // 控制硬件
return CONTROL_RESULT_OK;
}
// 注册三个层级的回调
IedServer_setControlHandler(server, spcso1, myControl, myParam);
IedServer_setPerformCheckHandler(server, spcso1, myPerformCheck, myParam);
IedServer_setWaitForExecutionHandler(server, spcso1, myWaitForExecution, myParam);
```
### 6.5 Select 状态变化回调
```c
typedef enum {
SELECT_STATE_REASON_SELECTED, // 被选中
SELECT_STATE_REASON_CANCELED, // 取消
SELECT_STATE_REASON_TIMEOUT, // 超时sboTimeout
SELECT_STATE_REASON_OPERATED, // 操作成功
SELECT_STATE_REASON_OPERATE_FAILED, // 操作失败
SELECT_STATE_REASON_DISCONNECTED // 选中客户端断开连接
} SelectStateChangedReason;
void mySelectStateChanged(ControlAction action, void* param,
bool isSelected, SelectStateChangedReason reason) {
if (isSelected) {
// 被客户端选中
} else {
// 取消选中(原因见 reason
}
}
IedServer_setSelectStateChangedHandler(server, spcso1, mySelectStateChanged, myParam);
```
### 6.6 AddCause 完整枚举
```c
typedef enum {
ADD_CAUSE_UNKNOWN = 0,
ADD_CAUSE_NOT_SUPPORTED = 1,
ADD_CAUSE_BLOCKED_BY_SWITCHING_HIERARCHY = 2,
ADD_CAUSE_SELECT_FAILED = 3,
ADD_CAUSE_INVALID_POSITION = 4,
ADD_CAUSE_POSITION_REACHED = 5,
ADD_CAUSE_PARAMETER_CHANGE_IN_EXECUTION = 6,
ADD_CAUSE_STEP_LIMIT = 7,
ADD_CAUSE_BLOCKED_BY_MODE = 8,
ADD_CAUSE_BLOCKED_BY_PROCESS = 9,
ADD_CAUSE_BLOCKED_BY_INTERLOCKING = 10,
ADD_CAUSE_BLOCKED_BY_SYNCHROCHECK = 11,
ADD_CAUSE_COMMAND_ALREADY_IN_EXECUTION = 12,
ADD_CAUSE_BLOCKED_BY_HEALTH = 13,
ADD_CAUSE_1_OF_N_CONTROL = 14,
ADD_CAUSE_ABORTION_BY_CANCEL = 15,
ADD_CAUSE_TIME_LIMIT_OVER = 16,
ADD_CAUSE_ABORTION_BY_TRIP = 17,
ADD_CAUSE_OBJECT_NOT_SELECTED = 18,
ADD_CAUSE_OBJECT_ALREADY_SELECTED = 19,
ADD_CAUSE_NO_ACCESS_AUTHORITY = 20,
ADD_CAUSE_ENDED_WITH_OVERSHOOT = 21,
ADD_CAUSE_ABORTION_DUE_TO_DEVIATION = 22,
ADD_CAUSE_ABORTION_BY_COMMUNICATION_LOSS = 23,
ADD_CAUSE_ABORTION_BY_COMMAND = 24,
ADD_CAUSE_NONE = 25,
ADD_CAUSE_INCONSISTENT_PARAMETERS = 26,
ADD_CAUSE_LOCKED_BY_OTHER_CLIENT = 27
} ControlAddCause;
```
---
## 7. 报告控制块RCB事件处理
### 7.1 RCB 事件类型
```c
typedef enum {
RCB_EVENT_GET_PARAMETER, // 参数被读取(暂未实现)
RCB_EVENT_SET_PARAMETER, // 参数被客户端设置
RCB_EVENT_UNRESERVED, // 取消预留
RCB_EVENT_RESERVED, // 被预留
RCB_EVENT_ENABLE, // 被使能
RCB_EVENT_DISABLE, // 被停用
RCB_EVENT_GI, // 总召触发
RCB_EVENT_PURGEBUF, // 清除缓冲区
RCB_EVENT_OVERFLOW, // 报告缓冲区溢出
RCB_EVENT_REPORT_CREATED // 新报告创建并插入缓冲区
} IedServer_RCBEventType;
```
### 7.2 完整 RCB 事件回调
```c
void rcbEventHandler(void* parameter, ReportControlBlock* rcb,
ClientConnection connection, IedServer_RCBEventType event,
const char* parameterName, MmsDataAccessError serviceError) {
const char* rcbName = ReportControlBlock_getName(rcb);
char* rptId = ReportControlBlock_getRptID(rcb);
char* dataSet = ReportControlBlock_getDataSet(rcb);
switch (event) {
case RCB_EVENT_ENABLE:
// 客户端使能了 RCB → 可以开始上送数据
printf("RCB %s (RptID=%s) 已使能\n", rcbName, rptId);
break;
case RCB_EVENT_DISABLE:
// 客户端停用了 RCB → 暂停上送
break;
case RCB_EVENT_RESERVED:
// 客户端独占了 URCB
break;
case RCB_EVENT_UNRESERVED:
// 客户端释放了 URCB
break;
case RCB_EVENT_GI:
// 客户端触发了总召
// 库会自动执行总召,这里可以记录日志
break;
case RCB_EVENT_SET_PARAMETER:
if (serviceError != DATA_ACCESS_ERROR_SUCCESS) {
// 参数设置失败
printf("RCB 参数设置失败: param=%s, err=%d\n",
parameterName, serviceError);
} else {
// 参数设置成功
if (parameterName && strcmp(parameterName, "RptEna") == 0) {
bool ena = ReportControlBlock_getRptEna(rcb);
// ...
}
if (parameterName && strcmp(parameterName, "DatSet") == 0) {
// 客户端改变了数据集 → 需要更新本地 report 数据结构
// 重新调用 IedConnection_installReportHandler客户端场景
}
}
break;
case RCB_EVENT_OVERFLOW:
// 报告缓冲区溢出(仅 BRCB
// 可能需要重新配置缓冲区大小或检查周期性积分周期
break;
case RCB_EVENT_REPORT_CREATED:
// 新报告已创建(可用于调试/监控)
break;
case RCB_EVENT_PURGEBUF:
// 客户端清除了缓冲区
break;
default:
break;
}
free(rptId);
free(dataSet);
}
// 注册
IedServer_setRCBEventHandler(server, rcbEventHandler, myParam);
```
---
## 8. 读写访问控制
### 8.1 写访问控制(单个数据属性)
```c
// 写访问回调
MmsDataAccessError myWriteAccessHandler(
DataAttribute* dataAttribute, MmsValue* value,
ClientConnection connection, void* parameter) {
// 检查客户端是否有写权限
if (!client_has_permission(connection)) {
return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
}
// 检查值是否在允许范围内(可选)
if (MmsValue_toFloat(value) > 100.0f) {
return DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID;
}
return DATA_ACCESS_ERROR_SUCCESS; // 接受,库自动更新值
// 或 return DATA_ACCESS_ERROR_SUCCESS_NO_UPDATE; // 接受但库不更新(自定义逻辑)
}
// 为单个数据属性注册写访问控制
IedServer_handleWriteAccess(server, stVal, myWriteAccessHandler, myParam);
// 为复合属性及其所有子属性注册写访问控制
IedServer_handleWriteAccessForComplexAttribute(server, complexDA, myWriteAccessHandler, myParam);
```
### 8.2 全局写访问策略
```c
typedef enum {
ACCESS_POLICY_ALLOW, // 允许
ACCESS_POLICY_DENY // 拒绝
} AccessPolicy;
// 设置某个 FC 的全局默认写访问策略
IedServer_setWriteAccessPolicy(server, IEC61850_FC_SP, ACCESS_POLICY_DENY);
IedServer_setWriteAccessPolicy(server, IEC61850_FC_SE, ACCESS_POLICY_ALLOW);
```
### 8.3 读访问控制(全局)
```c
// 全局读访问回调(对每个读请求调用)
MmsDataAccessError myReadAccessHandler(
LogicalDevice* ld, LogicalNode* ln, DataObject* dataObject,
FunctionalConstraint fc, ClientConnection connection, void* parameter) {
// 示例:禁止特定客户端读取 CO 数据
if (fc == IEC61850_FC_CO && is_restricted_client(connection)) {
return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
}
return DATA_ACCESS_ERROR_SUCCESS;
}
IedServer_setReadAccessHandler(server, myReadAccessHandler, myParam);
```
---
## 9. 定值组SGCB处理
### 9.1 内部切换定值组
```c
// 内部事件导致活跃定值组变化
IedServer_changeActiveSettingGroup(server, sgcb, 3); // 切换到第3组
// 获取当前活跃定值组号
uint8_t activeSG = IedServer_getActiveSettingGroup(server, sgcb);
```
### 9.2 定值组变化回调
```c
// 活跃定值组切换前回调(可拒绝切换)
bool myActiveSGChanged(void* parameter, SettingGroupControlBlock* sgcb,
uint8_t newActSg, ClientConnection connection) {
if (newActSg > 8) return false; // 拒绝无效的定值组
// 更新 SG 相关数据属性FC=SG
return true; // 接受
}
IedServer_setActiveSettingGroupChangedHandler(server, sgcb, myActiveSGChanged, param);
// 编辑定值组切换前回调
bool myEditSGChanged(void* parameter, SettingGroupControlBlock* sgcb,
uint8_t newEditSg, ClientConnection connection) {
// 更新 SE 数据属性
return true;
}
IedServer_setEditSettingGroupChangedHandler(server, sgcb, myEditSGChanged, param);
// 编辑定值组确认回调
void myEditSGConfirmed(void* parameter, SettingGroupControlBlock* sgcb, uint8_t editSg) {
// 编辑组已确认,将 SG 数据复制到 SE 组
}
IedServer_setEditSettingGroupConfirmationHandler(server, sgcb, myEditSGConfirmed, param);
```
---
## 10. GOOSE 发布
### 10.1 启用/禁用 GOOSE
```c
// 批量启用所有 GoCB
IedServer_enableGoosePublishing(server);
// 批量禁用所有 GoCB
IedServer_disableGoosePublishing(server);
// 设置 GOOSE 网络接口(操作系统相关)
IedServer_setGooseInterfaceId(server, "eth0");
// 设置特定 GoCB 的网络接口
IedServer_setGooseInterfaceIdEx(server, someLN, "gcbEvents", "eth1");
// 启用/禁用 VLAN 标签(全局或特定 GoCB
IedServer_useGooseVlanTag(server, NULL, NULL, true); // 全部启用
IedServer_useGooseVlanTag(server, someLN, "gcbEvents", false); // 特定 GoCB 禁用
```
### 10.2 GoCB 事件回调
```c
void goCBEventHandler(MmsGooseControlBlock goCb, int event, void* parameter) {
if (event == IEC61850_GOCB_EVENT_ENABLE) {
char* name = MmsGooseControlBlock_getName(goCb);
LogicalNode* ln = MmsGooseControlBlock_getLogicalNode(goCb);
DataSet* ds = MmsGooseControlBlock_getDataSet(goCb);
bool enabled = MmsGooseControlBlock_getGoEna(goCb);
int minTime = MmsGooseControlBlock_getMinTime(goCb);
int maxTime = MmsGooseControlBlock_getMaxTime(goCb);
bool fixedOffs = MmsGooseControlBlock_getFixedOffs(goCb);
bool ndsCom = MmsGooseControlBlock_getNdsCom(goCb);
free(name);
}
// event == IEC61850_GOCB_EVENT_DISABLE 停用
}
IedServer_setGoCBHandler(server, goCBEventHandler, myParam);
```
### 10.3 GoCB 信息查询
```c
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);
```
---
## 11. SV 控制块
```c
// SV 控制块回调
void svcBEventHandler(SVControlBlock* svcb, int event, void* parameter) {
if (event == IEC61850_SVCB_EVENT_ENABLE) {
// SV 被客户端使能
}
// event == IEC61850_SVCB_EVENT_DISABLE 停用
}
IedServer_setSVCBHandler(server, svcb, svcBEventHandler, myParam);
```
---
## 12. 连接管理与认证
### 12.1 连接认证
```c
// ACSE 认证回调
bool myAuthenticator(void* parameter, AcseAuthenticationParameter* authParameter) {
// 从 authParameter 中提取认证信息
// 返回 true 接受连接false 拒绝连接
return true;
}
IedServer_setAuthenticator(server, myAuthenticator, authParam);
```
### 12.2 连接状态变化回调
```c
void connHandler(IedServer self, ClientConnection connection, bool connected, void* parameter) {
const char* peer = ClientConnection_getPeerAddress(connection);
const char* local = ClientConnection_getLocalAddress(connection);
void* token = ClientConnection_getSecurityToken(connection); // 认证令牌
if (connected) {
printf("新客户端连接: %s\n", peer);
} else {
printf("客户端断开: %s\n", peer);
}
}
IedServer_setConnectionIndicationHandler(server, connHandler, myParam);
```
### 12.3 ClientConnection API
```c
const char* ClientConnection_getPeerAddress(ClientConnection self); // 客户端IP
const char* ClientConnection_getLocalAddress(ClientConnection self); // 服务端本地IP
void* ClientConnection_getSecurityToken(ClientConnection self); // 认证令牌
```
---
## 13. 日志服务
```c
// 设置日志存储(将日志控制块关联到日志存储实例)
IedServer_setLogStorage(server, "TEMPLATE/LLN0$MyLog", logStorage);
```
---
## 14. 运行模式:线程模式 vs 非线程模式
### 线程模式(默认,推荐)
```c
IedServer server = IedServer_createWithConfig(model, NULL, config);
IedServer_start(server, 102);
// 库内部创建线程处理所有客户端连接和周期性任务
// 用户只需调用 update 系列函数更新数据
IedServer_updateFloatAttributeValue(server, da, value); // 安全,随时可调用
```
### 非线程模式(嵌入式/特殊场景)
```c
IedServer server = IedServer_createWithConfig(model, NULL, config);
IedServer_startThreadless(server, 102);
while (running) {
int ready = IedServer_waitReady(server, 100);
if (ready) {
IedServer_processIncomingData(server);
}
IedServer_performPeriodicTasks(server); // 报告、超时等
}
IedServer_stopThreadless(server);
```
**非线程模式的限制**
- 必须周期性调用 `processIncomingData``performPeriodicTasks`
- 这些函数的调用频率直接影响响应速度和定时精度
- 报告周期精度取决于 `performPeriodicTasks` 的调用间隔
---
## 15. 与 RTU 项目对照
RTU 项目在 `src/protocol/libmms_s/` 中封装了服务端 API。
### 核心对应关系
| RTU 封装层 | libiec61850 API |
|-----------|----------------|
| `mms_s_init(icd_path, port)` | 完整初始化流程 |
| `mms_s_get_ied_server_ptr()``gp_iedServer` | `IedServer` 全局句柄 |
| `mms_s_model.cpp : model_init()` | `IedModel_create()``LogicalDevice_create()` → ... |
| `mms_s_value.cpp : mms_s_values_init()` | `IedServer` initializer 回调 → 遍历 DA 设置默认值 |
| `mms_s_control.cpp : control_init()` | `IedServer_setControlHandler()` |
| `mms_s_param.cpp : param_init()` | `IedServer_setActiveSettingGroupChangedHandler()` + EditSG |
| `mms_s_setting.cpp : setting_init()` | 初始化定值组数据 |
| `mms_s_file.cpp : file_init()` | `IedServerConfig_enableFileService()` |
| `rcbEventHandler()` (在 mms_s.cpp 中) | `IedServer_setRCBEventHandler()` |
| `mms_s_run_task()` 后台线程 | 线程模式下自动处理RTU 仅用锁持有维持运行) |
| `mms_s_dbg_switch()` | `LOG_I` 条件输出开关 |
### RTU 初始化流程(对应 libiec61850 API 调用)
```cpp
// RTU 中的 mms_s_init() 流程
// 1. icd_parse(icd_path) → 解析 ICD XML 文件为 stru_icd 结构
// 2. model_init(*gp_icd) → IedModel_create() + 遍历 ICD 创建 LD/LN/DO/DA/RCB/DataSet
// 3. IedServerConfig_create() → IedServerConfig_enableResvTmsForSGCB(true)
// 4. IedServer_createWithConfig(iedModel, NULL, serverConfig)
// 5. IedServer_setTimeQuality(server, true, false, false, 10)
// 6. control_init() → 遍历可控 DO安装 ControlHandler
// 7. param_init() → 安装 SGCB 回调
// 8. file_init() → 配置文件服务
// 9. IedServer_setRCBEventHandler(server, rcbEventHandler, NULL)
// 10. IedServer_start(server, port)
// 11. setting_init() → 初始化定值组数据
// 12. Thread_create(mms_s_run_task, iedModel, false) → 后台线程
```
### RTU 后台线程的特殊处理
```cpp
// RTU 使用线程模式,但创建了额外的后台线程来持有锁:
void* mms_s_run_task(void* parameter) {
while(g_running) {
IedServer_lockDataModel(gp_iedServer);
IedServer_unlockDataModel(gp_iedServer);
Thread_sleep(100);
}
IedServer_stop(gp_iedServer);
IedServer_destroy(gp_iedServer);
IedModel_destroy(iedModel); // 销毁数据模型
return NULL;
}
```
---
## 16. 附录:公共数据类型速查
### 16.1 Quality 操作
```c
typedef uint16_t Quality;
// 有效性
#define QUALITY_VALIDITY_GOOD 0
#define QUALITY_VALIDITY_INVALID 2
#define QUALITY_VALIDITY_QUESTIONABLE 3
// 详情标志
#define QUALITY_DETAIL_OVERFLOW 4
#define QUALITY_DETAIL_OUT_OF_RANGE 8
#define QUALITY_DETAIL_FAILURE 64
#define QUALITY_DETAIL_OLD_DATA 128
#define QUALITY_SOURCE_SUBSTITUTED 1024
#define QUALITY_TEST 2048
#define QUALITY_OPERATOR_BLOCKED 4096
Validity Quality_getValidity(Quality* self);
void Quality_setValidity(Quality* self, Validity validity);
void Quality_setFlag(Quality* self, int flag);
void Quality_unsetFlag(Quality* self, int flag);
bool Quality_isFlagSet(Quality* self, int flag);
```
### 16.2 Timestamp 操作
```c
typedef union { uint8_t val[8]; } Timestamp;
Timestamp* Timestamp_create(void);
void Timestamp_destroy(Timestamp* self);
uint32_t Timestamp_getTimeInSeconds(Timestamp* self);
uint64_t Timestamp_getTimeInMs(Timestamp* self);
void Timestamp_setTimeInMilliseconds(Timestamp* self, uint64_t msTime);
void Timestamp_setLeapSecondKnown(Timestamp* self, bool value);
void Timestamp_setClockFailure(Timestamp* self, bool value);
void Timestamp_setClockNotSynchronized(Timestamp* self, bool value);
void Timestamp_setSubsecondPrecision(Timestamp* self, int precision);
```
### 16.3 Dbpos双点位置
```c
typedef enum {
DBPOS_INTERMEDIATE_STATE = 0, // 中间态
DBPOS_OFF = 1, // 分
DBPOS_ON = 2, // 合
DBPOS_BAD_STATE = 3 // 坏态
} Dbpos;
```
### 16.4 触发选项
```c
#define TRG_OPT_DATA_CHANGED 1 // 数据变化触发
#define TRG_OPT_QUALITY_CHANGED 2 // 品质变化触发
#define TRG_OPT_DATA_UPDATE 4 // 数据更新触发
#define TRG_OPT_INTEGRITY 8 // 周期性触发
#define TRG_OPT_GI 16 // 总召触发
#define TRG_OPT_TRANSIENT 128 // 仅上升沿触发
```
### 16.5 报告选项
```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
#define RPT_OPT_CONF_REV 128
```
### 16.6 控制模型
```c
typedef enum {
CONTROL_MODEL_STATUS_ONLY = 0,
CONTROL_MODEL_DIRECT_NORMAL = 1,
CONTROL_MODEL_SBO_NORMAL = 2,
CONTROL_MODEL_DIRECT_ENHANCED = 3,
CONTROL_MODEL_SBO_ENHANCED = 4
} ControlModel;
```
### 16.7 MmsValue 基础构造(服务端常用)
```c
MmsValue* MmsValue_newBoolean(bool);
MmsValue* MmsValue_newFloat(float);
MmsValue* MmsValue_newDouble(double);
MmsValue* MmsValue_newIntegerFromInt32(int32_t);
MmsValue* MmsValue_newIntegerFromInt64(int64_t);
MmsValue* MmsValue_newUnsignedFromUint32(uint32_t);
MmsValue* MmsValue_newVisibleString(const char*);
MmsValue* MmsValue_newBitString(int bitSize);
MmsValue* MmsValue_newUtcTimeByMsTime(uint64_t msTime);
void MmsValue_delete(MmsValue*);
```
### 16.8 数据访问错误
```c
typedef enum {
DATA_ACCESS_ERROR_SUCCESS_NO_UPDATE = -3,
DATA_ACCESS_ERROR_NO_RESPONSE = -2,
DATA_ACCESS_ERROR_SUCCESS = -1,
DATA_ACCESS_ERROR_OBJECT_INVALIDATED = 0,
DATA_ACCESS_ERROR_HARDWARE_FAULT = 1,
DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE = 2,
DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED = 3,
DATA_ACCESS_ERROR_OBJECT_UNDEFINED = 4,
DATA_ACCESS_ERROR_INVALID_ADDRESS = 5,
DATA_ACCESS_ERROR_TYPE_UNSUPPORTED = 6,
DATA_ACCESS_ERROR_TYPE_INCONSISTENT = 7,
DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT = 8,
DATA_ACCESS_ERROR_OBJECT_ACCESS_UNSUPPORTED = 9,
DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT = 10,
DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID = 11,
DATA_ACCESS_ERROR_UNKNOWN = 12
} MmsDataAccessError;
```
---
> 本文档基于 `libiec61850-1.5.3/src/iec61850/inc/iec61850_server.h` (1896行)、`iec61850_dynamic_model.h` (539行)、`iec61850_common.h` (548行) 和 `mms_value.h` (1062行) 完整归纳,覆盖所有公开 C API。