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

44 KiB
Raw Permalink Blame History

libiec61850-1.5.3 MMS 服务端 API 开发手册

本文档基于 libiec61850 v1.5.3 源码,覆盖 IEC 61850 服务端全部公开 C API达到开发者脱离源码即可进行项目开发的标准。


目录

  1. 架构概述
  2. 数据模型创建(动态模型)
  3. 服务器配置IedServerConfig
  4. 服务器生命周期管理
  5. 数据值读取与更新
  6. 控制模型回调
  7. 报告控制块RCB事件处理
  8. 读写访问控制
  9. 定值组SGCB处理
  10. GOOSE 发布
  11. SV 控制块
  12. 连接管理与认证
  13. 日志服务
  14. 运行模式:线程模式 vs 非线程模式
  15. 与 RTU 项目对照
  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 完整创建流程

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

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

// 创建数据集
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 设置数据属性默认值

// 在创建 IedServer 之前可以设置默认值
MmsValue* defaultVal = MmsValue_newFloat(0.0f);
DataAttribute_setValue(stVal, defaultVal);
MmsValue_delete(defaultVal);

2.6 DA 属性查询

DataAttributeType  DataAttribute_getType(DataAttribute* self);
FunctionalConstraint DataAttribute_getFC(DataAttribute* self);
uint8_t            DataAttribute_getTrgOps(DataAttribute* self);

2.7 创建其他控制块

// 定值组控制块
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 模型销毁

// 销毁动态创建的数据模型
// 注意:一定要在 IedServer_destroy 之后调用,否则会导致资源泄漏
IedModel_destroy(model);

2.9 数据属性类型枚举DataAttributeType

创建 DA 时使用的类型常量:

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 配置结构体字段

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

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

报告设置可写性常量

#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 查询配置

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 创建服务器

// 简单创建(仅需数据模型)
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 附加访问点

// 为服务器添加额外的监听地址(可在 start 前多次添加)
// 返回 true 成功false 失败
IedServer_addAccessPoint(server, "192.168.2.1", 102, NULL);

4.3 设置服务器属性

// 设置监听地址(默认监听所有接口)
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 启动/停止(线程模式)

// 启动(线程模式)—— 内部创建后台线程
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 启动/停止(非线程模式)

// 启动(非线程模式)—— 用户驱动消息循环
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 底层访问

// 获取数据模型
IedModel* model = IedServer_getDataModel(server);

// 获取底层 MmsServer谨慎使用直接操作可能干扰 IedServer
MmsServer mmsServer = IedServer_getMmsServer(server);

5. 数据值读取与更新

5.1 读取属性值

// 通用读取(返回 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

// 通用更新
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 批量更新(加锁)

// 更新多个值时加锁以提高效率
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 更新控制模型

// 更新可控数据对象的控制模型
IedServer_updateCtlModel(server, ctlObject, CONTROL_MODEL_SBO_NORMAL);
// 注意:对应的控制结构必须在数据模型中存在

6. 控制模型回调

服务端通过三个层级的回调实现 IEC 61850 的遥控机制。

6.1 控制操作完整流程

客户端发送控制命令
    ↓
PerformCheck 回调(静态测试:联锁、权限等)
    ↓ (通过)
WaitForExecution 回调(动态测试:同步检查等)
    ↓ (通过)
Control 回调(实际执行:操作继电器等)
    ↓
CommandTermination 响应返回客户端

6.2 回调返回值

// 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 上下文信息

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 注册控制回调

// 实际执行回调(必须注册)
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 状态变化回调

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 完整枚举

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 事件类型

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 事件回调

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 写访问控制(单个数据属性)

// 写访问回调
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 全局写访问策略

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 读访问控制(全局)

// 全局读访问回调(对每个读请求调用)
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 内部切换定值组

// 内部事件导致活跃定值组变化
IedServer_changeActiveSettingGroup(server, sgcb, 3);  // 切换到第3组

// 获取当前活跃定值组号
uint8_t activeSG = IedServer_getActiveSettingGroup(server, sgcb);

9.2 定值组变化回调

// 活跃定值组切换前回调(可拒绝切换)
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

// 批量启用所有 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 事件回调

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 信息查询

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 控制块

// 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 连接认证

// ACSE 认证回调
bool myAuthenticator(void* parameter, AcseAuthenticationParameter* authParameter) {
    // 从 authParameter 中提取认证信息
    // 返回 true 接受连接false 拒绝连接
    return true;
}

IedServer_setAuthenticator(server, myAuthenticator, authParam);

12.2 连接状态变化回调

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

const char* ClientConnection_getPeerAddress(ClientConnection self);   // 客户端IP
const char* ClientConnection_getLocalAddress(ClientConnection self);  // 服务端本地IP
void*       ClientConnection_getSecurityToken(ClientConnection self); // 认证令牌

13. 日志服务

// 设置日志存储(将日志控制块关联到日志存储实例)
IedServer_setLogStorage(server, "TEMPLATE/LLN0$MyLog", logStorage);

14. 运行模式:线程模式 vs 非线程模式

线程模式(默认,推荐)

IedServer server = IedServer_createWithConfig(model, NULL, config);
IedServer_start(server, 102);
// 库内部创建线程处理所有客户端连接和周期性任务
// 用户只需调用 update 系列函数更新数据

IedServer_updateFloatAttributeValue(server, da, value);  // 安全,随时可调用

非线程模式(嵌入式/特殊场景)

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

非线程模式的限制

  • 必须周期性调用 processIncomingDataperformPeriodicTasks
  • 这些函数的调用频率直接影响响应速度和定时精度
  • 报告周期精度取决于 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 调用)

// 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 后台线程的特殊处理

// 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 操作

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 操作

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双点位置

typedef enum {
    DBPOS_INTERMEDIATE_STATE = 0,  // 中间态
    DBPOS_OFF = 1,                 // 分
    DBPOS_ON = 2,                  // 合
    DBPOS_BAD_STATE = 3            // 坏态
} Dbpos;

16.4 触发选项

#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 报告选项

#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 控制模型

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 基础构造(服务端常用)

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 数据访问错误

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。