# 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, // rptId,NULL = 使用默认(对象引用) 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。