# libiec61850 客户端开发手册 > 基于 libiec61850 v1.5.3 完整 API 参考。本文档旨在做到脱离代码即可完成客户端开发。 --- ## 目录 1. [概述与连接生命周期](#1-概述与连接生命周期) 2. [IedConnection 连接管理](#2-iedconnection-连接管理) 3. [数据读写操作](#3-数据读写操作) 4. [数据集 (DataSet) 操作](#4-数据集-dataset-操作) 5. [报告 (Report) 控制](#5-报告-report-控制) 6. [控制操作 (Control)](#6-控制操作-control) 7. [服务器目录浏览与发现](#7-服务器目录浏览与发现) 8. [文件服务](#8-文件服务) 9. [日志服务 (Journal)](#9-日志服务-journal) 10. [GOOSE 订阅](#10-goose-订阅) 11. [GOOSE 发布 (独立)](#11-goose-发布-独立) 12. [Sampled Values 客户端](#12-sampled-values-客户端) 13. [SV 控制块 (SVCB)](#13-sv-控制块-svcb) 14. [MmsValue 完整操作手册](#14-mmsvalue-完整操作手册) 15. [MmsVariableSpecification](#15-mmsvariablespecification) 16. [TLS 安全连接](#16-tls-安全连接) 17. [单线程模式 (非线程模式)](#17-单线程模式-非线程模式) 18. [MmsConnection 底层接口](#18-mmsconnection-底层接口) 19. [完整代码示例](#19-完整代码示例) --- ## 1. 概述与连接生命周期 ``` IedConnection_create() // 创建连接实例 ↓ [可选] 设置本地地址/超时 ↓ IedConnection_connect() // 连接到服务器 (阻塞) ↓ 读写操作 / 报告订阅 / 控制操作 ↓ IedConnection_close() // 关闭连接 (推荐) or IedConnection_abort() // 中断连接 or IedConnection_release() // 正常释放 (MMS Conclude) ↓ IedConnection_destroy() // 销毁并释放所有资源 ``` ### 1.1 连接状态枚举 ```c typedef enum { IED_STATE_CLOSED = 0, // 关闭/空闲 IED_STATE_CONNECTING, // 正在连接 IED_STATE_CONNECTED, // 已连接 IED_STATE_CLOSING // 正在关闭 } IedConnectionState; ``` ### 1.2 错误码枚举 (IedClientError) | 错误码 | 值 | 含义 | |--------|-----|------| | `IED_ERROR_OK` | 0 | 成功 | | `IED_ERROR_NOT_CONNECTED` | 1 | 未连接 | | `IED_ERROR_ALREADY_CONNECTED` | 2 | 已连接 | | `IED_ERROR_CONNECTION_LOST` | 3 | 连接丢失 | | `IED_ERROR_SERVICE_NOT_SUPPORTED` | 4 | 服务/参数不支持 | | `IED_ERROR_CONNECTION_REJECTED` | 5 | 连接被拒绝 | | `IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED` | 6 | 未完成调用达到上限 | | `IED_ERROR_USER_PROVIDED_INVALID_ARGUMENT` | 10 | 无效参数 | | `IED_ERROR_ENABLE_REPORT_FAILED_DATASET_MISMATCH` | 11 | 报告启用: 数据集不匹配 | | `IED_ERROR_OBJECT_REFERENCE_INVALID` | 12 | 对象引用语法错误 | | `IED_ERROR_UNEXPECTED_VALUE_RECEIVED` | 13 | 收到意外类型 | | `IED_ERROR_TIMEOUT` | 20 | 超时 | | `IED_ERROR_ACCESS_DENIED` | 21 | 访问拒绝 | | `IED_ERROR_OBJECT_DOES_NOT_EXIST` | 22 | 对象不存在 | | `IED_ERROR_OBJECT_EXISTS` | 23 | 对象已存在 | | `IED_ERROR_OBJECT_ACCESS_UNSUPPORTED` | 24 | 不支持的访问方法 | | `IED_ERROR_TYPE_INCONSISTENT` | 25 | 类型不一致 | | `IED_ERROR_TEMPORARILY_UNAVAILABLE` | 26 | 暂时不可用 | | `IED_ERROR_OBJECT_UNDEFINED` | 27 | 对象未定义 | | `IED_ERROR_INVALID_ADDRESS` | 28 | 无效地址 | | `IED_ERROR_HARDWARE_FAULT` | 29 | 硬件故障 | | `IED_ERROR_TYPE_UNSUPPORTED` | 30 | 类型不支持 | | `IED_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT` | 31 | 属性不一致 | | `IED_ERROR_OBJECT_VALUE_INVALID` | 32 | 值无效 | | `IED_ERROR_OBJECT_INVALIDATED` | 33 | 对象已失效 | | `IED_ERROR_MALFORMED_MESSAGE` | 34 | 格式错误的消息 | | `IED_ERROR_SERVICE_NOT_IMPLEMENTED` | 98 | 服务未实现 | | `IED_ERROR_UNKNOWN` | 99 | 未知错误 | --- ## 2. IedConnection 连接管理 ### 2.1 创建连接实例 ```c // 基本创建 (多线程模式, 非TLS) IedConnection IedConnection_create(void); // 扩展创建 IedConnection IedConnection_createEx( TLSConfiguration tlsConfig, // TLS 配置, NULL 表示非TLS bool useThreads); // true=多线程, false=单线程 // useThreads=false 时需要使用异步API + IedConnection_tick() // 创建 TLS 连接 (已废弃, 推荐使用 createEx) IedConnection IedConnection_createWithTlsSupport(TLSConfiguration tlsConfig); // 销毁连接 (自动关闭已连接) void IedConnection_destroy(IedConnection self); ``` ### 2.2 连接配置 ```c // 设置本地地址/端口 void IedConnection_setLocalAddress(IedConnection self, const char* localIpAddress, int localPort); // localPort < 1 时由OS自动选择端口 // 必须在 connect 前调用 // 设置连接超时 (ms) void IedConnection_setConnectTimeout(IedConnection self, uint32_t timeoutInMs); // 必须在 connect 前调用 // 设置请求超时 (ms) - 可随时调用 void IedConnection_setRequestTimeout(IedConnection self, uint32_t timeoutInMs); // 获取请求超时 uint32_t IedConnection_getRequestTimeout(IedConnection self); // 设置客户端生成的所有时间戳的时间品质 void IedConnection_setTimeQuality(IedConnection self, bool leapSecondKnown, bool clockFailure, bool clockNotSynchronized, int subsecondPrecision); ``` ### 2.3 连接与断开 ```c // === 同步连接 (阻塞直到连接建立或超时) === void IedConnection_connect(IedConnection self, IedClientError* error, // 输出: 错误码 const char* hostname, // 主机名或IP地址 int tcpPort); // TCP端口 // === 异步连接 (立即返回) === void IedConnection_connectAsync(IedConnection self, IedClientError* error, const char* hostname, int tcpPort); // 需要通过状态变化处理器或轮询 IedConnection_getState() 来跟踪状态 // === 获取连接状态 === IedConnectionState IedConnection_getState(IedConnection self); // === 获取最后一次应用错误 === LastApplError IedConnection_getLastApplError(IedConnection self); // LastApplError 结构: // int ctlNum; // ControlLastApplError error; // ControlAddCause addCause; // === 连接断开方法 === // 关闭连接 (简单关闭TCP, 无握手) - 推荐 void IedConnection_close(IedConnection self); // Abort (发送 ACSE abort 消息后关闭) void IedConnection_abort(IedConnection self, IedClientError* error); // 异步 Abort void IedConnection_abortAsync(IedConnection self, IedClientError* error); // Release (发送 MMS conclude 消息 - 正常释放) void IedConnection_release(IedConnection self, IedClientError* error); // 注意: 服务器可能拒绝, 此时连接仍保持 // 异步 Release void IedConnection_releaseAsync(IedConnection self, IedClientError* error); ``` ### 2.4 连接状态回调 ```c // 状态变化处理器 (推荐) typedef void (*IedConnection_StateChangedHandler)( void* parameter, IedConnection connection, IedConnectionState newState); void IedConnection_installStateChangedHandler(IedConnection self, IedConnection_StateChangedHandler handler, void* parameter); // 旧版连接关闭处理器 (已废弃) typedef void (*IedConnectionClosedHandler)( void* parameter, IedConnection connection); void IedConnection_installConnectionClosedHandler(IedConnection self, IedConnectionClosedHandler handler, void* parameter); ``` ### 2.5 底层连接访问 ```c // 获取底层 MmsConnection (用于直接调用 MMS 级服务) MmsConnection IedConnection_getMmsConnection(IedConnection self); // 单线程模式: 周期性调用以处理连接事件 bool IedConnection_tick(IedConnection self); // 返回: true = 连接等待中可挂起, false = 忙需尽快再调用 ``` --- ## 3. 数据读写操作 ### 3.1 同步读取 ```c // 读取单个对象 MmsValue* IedConnection_readObject(IedConnection self, IedClientError* error, const char* objectReference, // 对象引用路径, 如 "simpleIOGenericIO/GGIO1.AnIn1.mag.f" FunctionalConstraint fc); // 功能约束, 如 IEC61850_FC_MX // 返回: MmsValue* 或 NULL (失败) // 注意: 即使 mmsError=OK, 返回值也可能为 NULL (服务器返回空结果列表) // 读取数组元素 (支持索引语法) // "testComplexArray/MHAI1.HA.phsAHar(2).cVal.mag.f" // 数组第2个元素的子属性 ``` ### 3.2 异步读取 ```c // 回调类型 typedef void (*IedConnection_ReadObjectHandler)( uint32_t invokeId, // 请求调用ID void* parameter, // 用户参数 IedClientError err, // 错误码 MmsValue* value); // 读取的值 (需由用户 MmsValue_delete 释放!) // 异步读取 void IedConnection_readObjectAsync(IedConnection self, IedClientError* error, const char* objectReference, FunctionalConstraint fc, IedConnection_ReadObjectHandler handler, void* parameter); ``` ### 3.3 同步写入 ```c // 写入单个对象 void IedConnection_writeObject(IedConnection self, IedClientError* error, const char* objectReference, FunctionalConstraint fc, MmsValue* value); // 示例: 写 Visible String MmsValue* val = MmsValue_newVisibleString("libiec61850.com"); IedConnection_writeObject(con, &error, "simpleIOGenericIO/GGIO1.NamPlt.vendor", IEC61850_FC_DC, val); MmsValue_delete(val); ``` ### 3.4 异步写入 ```c // 通用回调 typedef void (*IedConnection_GenericServiceHandler)( uint32_t invokeId, void* parameter, IedClientError err); void IedConnection_writeObjectAsync(IedConnection self, IedClientError* error, const char* objectReference, FunctionalConstraint fc, MmsValue* value, IedConnection_GenericServiceHandler handler, void* parameter); ``` --- ## 4. 数据集 (DataSet) 操作 ### 4.1 ClientDataSet 类型 ```c typedef struct sClientDataSet* ClientDataSet; // 读取数据集值 ClientDataSet IedConnection_readDataSetValues(IedConnection self, IedClientError* error, const char* dataSetReference, // 如 "simpleIOGenericIO/LLN0.Events" ClientDataSet dataSet); // 可复用已有实例或传 NULL // 返回: ClientDataSet 实例或 NULL // 异步版本 void IedConnection_readDataSetValuesAsync(IedConnection self, IedClientError* error, const char* dataSetReference, ClientDataSet dataSet, IedConnection_ReadDataSetHandler handler, void* parameter); // 获取数据集大小 int ClientDataSet_getDataSetSize(ClientDataSet self); // 获取数据集值 (返回 MMS_ARRAY 类型的 MmsValue*) MmsValue* ClientDataSet_getValues(ClientDataSet self); // 获取数据集目录 (返回 LinkedList 元素名称列表) LinkedList IedConnection_getDataSetDirectory(IedConnection self, IedClientError* error, const char* dataSetReference, ClientDataSet dataSet); // 写入数据集值 void IedConnection_writeDataSetValuesAsync(IedConnection self, IedClientError* error, const char* dataSetReference, LinkedList /* */ values, IedConnection_WriteDataSetHandler handler, void* parameter); // 销毁数据集 void ClientDataSet_destroy(ClientDataSet self); ``` --- ## 5. 报告 (Report) 控制 ### 5.1 报告回调 ```c // 报告回调函数类型 typedef void (*ReportCallbackFunction)( void* parameter, // 用户参数 ClientReport report); // 报告对象 // 安装报告处理器 void IedConnection_installReportHandler(IedConnection self, const char* rcbReference, // RCB引用, 如 "simpleIOGenericIO/LLN0.RP.EventsRCB01" const char* rptId, // 报告ID (从 ClientReportControlBlock_getRptId 获取) ReportCallbackFunction handler, void* parameter); ``` ### 5.2 ClientReport 读取 ```c // 获取报告中的数据集值 MmsValue* ClientReport_getDataSetValues(ClientReport report); // 获取 RCB 引用 const char* ClientReport_getRcbReference(ClientReport report); // 获取报告ID const char* ClientReport_getRptId(ClientReport report); // 检查报告是否包含时间戳 bool ClientReport_hasTimestamp(ClientReport report); // 获取报告时间戳 (Unix 毫秒时间戳) uint64_t ClientReport_getTimestamp(ClientReport report); // 获取某个数据成员的包含原因 ReasonForInclusion ClientReport_getReasonForInclusion(ClientReport report, int index); // 包含原因枚举值: // IEC61850_REASON_NOT_INCLUDED - 未包含 // IEC61850_REASON_DATA_CHANGE - 数据变化 // IEC61850_REASON_QUALITY_CHANGE - 品质变化 // IEC61850_REASON_DATA_UPDATE - 数据更新 // IEC61850_REASON_INTEGRITY - 完整性 // IEC61850_REASON_GI - 总召唤 // IEC61850_REASON_APPLICATION_TRIGGER - 应用触发 ``` ### 5.3 ClientReportControlBlock 操作 ```c // RCB 元素标志 (用于 setRCBValues) #define RCB_ELEMENT_RPT_ENA 1 #define RCB_ELEMENT_RESV 2 #define RCB_ELEMENT_DATSET 4 #define RCB_ELEMENT_TRG_OPS 8 #define RCB_ELEMENT_OPT_FLDS 16 #define RCB_ELEMENT_BUF_TIME 32 #define RCB_ELEMENT_INTG_PD 64 #define RCB_ELEMENT_GI 128 #define RCB_ELEMENT_PURGE_BUF 256 #define RCB_ELEMENT_ENTRY_ID 512 // 创建 RCB 客户端对象 ClientReportControlBlock ClientReportControlBlock_create(const char* objectReference); // 读取 RCB 值 (从服务器) ClientReportControlBlock IedConnection_getRCBValues(IedConnection self, IedClientError* error, const char* rcbReference, ClientReportControlBlock rcb); // 已有对象或 NULL // 异步读取 RCB void IedConnection_getRCBValuesAsync(IedConnection self, IedClientError* error, const char* rcbReference, ClientReportControlBlock rcb, IedConnection_GetRCBValuesHandler handler, void* parameter); // 设置 RCB 值 (写入服务器) void IedConnection_setRCBValues(IedConnection self, IedClientError* error, ClientReportControlBlock rcb, uint32_t parameterMask, // 要写入的参数掩码 (RCB_ELEMENT_*) bool singleRequest); // true=单请求, false=分多请求 // 异步设置 RCB void IedConnection_setRCBValuesAsync(IedConnection self, IedClientError* error, ClientReportControlBlock rcb, uint32_t parameterMask, bool singleRequest, IedConnection_GenericServiceHandler handler, void* parameter); // === ClientReportControlBlock getters/setters === const char* ClientReportControlBlock_getObjectReference(ClientReportControlBlock self); const char* ClientReportControlBlock_getRptId(ClientReportControlBlock self); bool ClientReportControlBlock_getRptEna(ClientReportControlBlock self); void ClientReportControlBlock_setRptEna(ClientReportControlBlock self, bool value); void ClientReportControlBlock_setRptId(ClientReportControlBlock self, const char* rptId); void ClientReportControlBlock_setDataSetReference(ClientReportControlBlock self, const char* dataSetRef); void ClientReportControlBlock_setTrgOps(ClientReportControlBlock self, uint8_t trgOps); void ClientReportControlBlock_setOptFlds(ClientReportControlBlock self, uint8_t optFlds); void ClientReportControlBlock_setBufTm(ClientReportControlBlock self, uint32_t bufTm); void ClientReportControlBlock_setIntgPd(ClientReportControlBlock self, uint32_t intgPd); void ClientReportControlBlock_setGI(ClientReportControlBlock self, bool value); void ClientReportControlBlock_setResv(ClientReportControlBlock self, bool value); void ClientReportControlBlock_setPurgeBuf(ClientReportControlBlock self, bool value); void ClientReportControlBlock_setEntryId(ClientReportControlBlock self, uint8_t* entryId); // 销毁 void ClientReportControlBlock_destroy(ClientReportControlBlock self); ``` ### 5.4 报告配置典型流程 ```c // 1. 获取当前 RCB 值 ClientReportControlBlock rcb = IedConnection_getRCBValues(con, &error, "simpleIOGenericIO/LLN0.RP.EventsRCB01", NULL); // 2. 获取 rptId (用于报告回调匹配) const char* rptId = ClientReportControlBlock_getRptId(rcb); // 3. 安装报告回调 IedConnection_installReportHandler(con, "simpleIOGenericIO/LLN0.RP.EventsRCB01", rptId, reportCallback, NULL); // 4. 配置 RCB 参数 ClientReportControlBlock_setTrgOps(rcb, TRG_OPT_DATA_UPDATE | TRG_OPT_INTEGRITY | TRG_OPT_GI); ClientReportControlBlock_setRptEna(rcb, true); ClientReportControlBlock_setIntgPd(rcb, 5000); // 5秒完整性周期 // 5. 写入服务器 IedConnection_setRCBValues(con, &error, rcb, RCB_ELEMENT_RPT_ENA | RCB_ELEMENT_TRG_OPS | RCB_ELEMENT_INTG_PD, true); // 6. 触发总召唤 (GI) Thread_sleep(1000); ClientReportControlBlock_setGI(rcb, true); IedConnection_setRCBValues(con, &error, rcb, RCB_ELEMENT_GI, true); // 7. 一段时间后禁用报告 ClientReportControlBlock_setRptEna(rcb, false); IedConnection_setRCBValues(con, &error, rcb, RCB_ELEMENT_RPT_ENA, true); // 8. 清理 ClientReportControlBlock_destroy(rcb); ``` --- ## 6. 控制操作 (Control) ### 6.1 ControlObjectClient 创建与销毁 ```c // 创建控制对象客户端 (多线程模式) ControlObjectClient ControlObjectClient_create( const char* objectReference, // 如 "simpleIOGenericIO/GGIO1.SPCSO1" IedConnection connection); // 创建控制对象客户端 (非线程模式 - 需提供 varSpec) ControlObjectClient ControlObjectClient_createEx( const char* objectReference, IedConnection connection, ControlModel controlModel, // 控制模型 MmsVariableSpecification* varSpec); // 变量类型规格 // 销毁控制对象 void ControlObjectClient_destroy(ControlObjectClient self); ``` ### 6.2 控制操作函数 ```c // === 直接操作 (Direct Operate) === bool ControlObjectClient_operate(ControlObjectClient self, MmsValue* ctlVal, // 控制值 uint64_t timeStamp); // 定时操作的时间戳, 0=立即执行 // 返回: true=成功, false=失败 // === 选择 (Select) === bool ControlObjectClient_select(ControlObjectClient self); // 返回: true=选择成功 // === 带值选择 (Select With Value) === bool ControlObjectClient_selectWithValue(ControlObjectClient self, MmsValue* ctlVal); // === 取消 (Cancel) === bool ControlObjectClient_cancel(ControlObjectClient self); // === 异步操作 === void ControlObjectClient_operateAsync(ControlObjectClient self, IedClientError* error, MmsValue* ctlVal, uint64_t timeStamp, ControlAsyncHandler handler, void* parameter); void ControlObjectClient_selectAsync(ControlObjectClient self, IedClientError* error, ControlAsyncHandler handler, void* parameter); void ControlObjectClient_selectWithValueAsync(ControlObjectClient self, IedClientError* error, MmsValue* ctlVal, ControlAsyncHandler handler, void* parameter); // 异步回调类型 typedef void (*ControlAsyncHandler)( uint32_t invokeId, void* parameter, IedClientError err, ControlActionType type, // select/operate/cancel bool success); ``` ### 6.3 CommandTermination (增强安全) ```c // 命令终止处理器 typedef void (*CommandTerminationHandler)( void* parameter, ControlObjectClient connection); void ControlObjectClient_setCommandTerminationHandler( ControlObjectClient self, CommandTerminationHandler handler, void* parameter); // 获取上次应用错误 LastApplError ControlObjectClient_getLastApplError(ControlObjectClient self); // LastApplError 包含: .ctlNum, .error, .addCause // 当 error != 0 表示 CommandTermination- // 当 error == 0 表示 CommandTermination+ ``` ### 6.4 来源设置 ```c // 设置控制来源 void ControlObjectClient_setOrigin(ControlObjectClient self, const char* orIdent, // 来源标识字符串 int orCat); // 来源类别 (orCat) // 来源类别常量 (orCat): // CONTROL_ORCAT_NOT_SUPPORTED = 0 // CONTROL_ORCAT_BAY_CONTROL = 1 // CONTROL_ORCAT_STATION_CONTROL = 2 // CONTROL_ORCAT_REMOTE_CONTROL = 3 // CONTROL_ORCAT_AUTOMATIC_BAY = 4 // CONTROL_ORCAT_AUTOMATIC_STATION = 5 // CONTROL_ORCAT_AUTOMATIC_REMOTE = 6 // CONTROL_ORCAT_MAINTENANCE = 7 // CONTROL_ORCAT_PROCESS = 8 ``` ### 6.5 四种控制方式示例 ```c // 1. 直接控制 (Direct Normal) ControlObjectClient ctl = ControlObjectClient_create(ref, con); MmsValue* val = MmsValue_newBoolean(true); ControlObjectClient_setOrigin(ctl, NULL, 3); ControlObjectClient_operate(ctl, val, 0); MmsValue_delete(val); ControlObjectClient_destroy(ctl); // 2. 选择前操作 (SBO Normal) ControlObjectClient ctl = ControlObjectClient_create(ref, con); if (ControlObjectClient_select(ctl)) { MmsValue* val = MmsValue_newBoolean(true); ControlObjectClient_operate(ctl, val, 0); MmsValue_delete(val); } ControlObjectClient_destroy(ctl); // 3. 直接控制-增强安全 (Direct Enhanced) ControlObjectClient ctl = ControlObjectClient_create(ref, con); ControlObjectClient_setCommandTerminationHandler(ctl, termHandler, NULL); MmsValue* val = MmsValue_newBoolean(true); ControlObjectClient_operate(ctl, val, 0); // 等待 CommandTermination 回调 Thread_sleep(1000); MmsValue_delete(val); ControlObjectClient_destroy(ctl); // 4. 选择前操作-增强安全 (SBO Enhanced) ControlObjectClient ctl = ControlObjectClient_create(ref, con); ControlObjectClient_setCommandTerminationHandler(ctl, termHandler, NULL); MmsValue* val = MmsValue_newBoolean(true); if (ControlObjectClient_selectWithValue(ctl, val)) { ControlObjectClient_operate(ctl, val, 0); Thread_sleep(1000); } MmsValue_delete(val); ControlObjectClient_destroy(ctl); ``` --- ## 7. 服务器目录浏览与发现 ### 7.1 同步函数 ```c // 获取服务器目录 (逻辑设备列表) LinkedList IedConnection_getServerDirectory(IedConnection self, IedClientError* error, const char* continueAfter, // 续传点, 首次调用传 NULL LinkedList result); // 存储结果, 传 NULL 创建新列表 // 获取逻辑设备变量列表 LinkedList IedConnection_getLogicalDeviceVariables(IedConnection self, IedClientError* error, const char* ldName, // 逻辑设备名称 const char* continueAfter, LinkedList result); // 获取逻辑设备数据集列表 LinkedList IedConnection_getLogicalDeviceDataSets(IedConnection self, IedClientError* error, const char* ldName, const char* continueAfter, LinkedList result); // 获取变量类型规格 (用于非线程模式预获取 varSpec) MmsVariableSpecification* IedConnection_getVariableSpecification( IedConnection self, IedClientError* error, const char* objectReference, FunctionalConstraint fc); ``` ### 7.2 异步函数 ```c // NameList 回调类型 typedef void (*IedConnection_GetNameListHandler)( uint32_t invokeId, void* parameter, IedClientError err, LinkedList nameList, // 名称列表 (LinkedList) bool moreFollows); // 是否有更多数据 void IedConnection_getServerDirectoryAsync(IedConnection self, IedClientError* error, const char* continueAfter, LinkedList result, IedConnection_GetNameListHandler handler, void* parameter); void IedConnection_getLogicalDeviceVariablesAsync(IedConnection self, IedClientError* error, const char* ldName, const char* continueAfter, LinkedList result, IedConnection_GetNameListHandler handler, void* parameter); void IedConnection_getLogicalDeviceDataSetsAsync(IedConnection self, IedClientError* error, const char* ldName, const char* continueAfter, LinkedList result, IedConnection_GetNameListHandler handler, void* parameter); // 变量规格回调 typedef void (*IedConnection_GetVariableSpecificationHandler)( uint32_t invokeId, void* parameter, IedClientError err, MmsVariableSpecification* spec); void IedConnection_getVariableSpecificationAsync(IedConnection self, IedClientError* error, const char* objectReference, FunctionalConstraint fc, IedConnection_GetVariableSpecificationHandler handler, void* parameter); ``` --- ## 8. 文件服务 ```c // 获取文件目录 bool IedConnection_getFileDirectory(IedConnection self, IedClientError* error, const char* fileSpecification, // 文件匹配模式 (NULL=根目录) const char* continueAfter, // 续传点 (NULL=首次) IedConnection_FileDirectoryHandler handler, void* parameter); // 打开文件 (返回 FRSM ID) int32_t IedConnection_fileOpen(IedConnection self, IedClientError* error, const char* filename, uint32_t initialPosition, // 初始位置 uint32_t* fileSize, // 输出: 文件大小 uint64_t* lastModified); // 输出: 最后修改时间 // 读取文件数据块 bool IedConnection_fileRead(IedConnection self, IedClientError* error, int32_t frsmId, // FRSM ID IedConnection_FileReadHandler handler, void* parameter); // 返回: true=还有更多数据, false=已读完 // 关闭文件 void IedConnection_fileClose(IedConnection self, IedClientError* error, int32_t frsmId); // 删除文件 void IedConnection_fileDelete(IedConnection self, IedClientError* error, const char* filename); // 重命名文件 void IedConnection_fileRename(IedConnection self, IedClientError* error, const char* currentFileName, const char* newFileName); // 下载文件 (ObtainFile) void IedConnection_obtainFile(IedConnection self, IedClientError* error, const char* sourceFile, // 客户端源文件 const char* destinationFile); // 服务器目标文件 // 设置文件服务本地存储路径 void IedConnection_setFilestoreBasepath(IedConnection self, const char* basepath); ``` --- ## 9. 日志服务 (Journal) ```c // 按时间范围读取日志 LinkedList IedConnection_readJournalTimeRange(IedConnection self, IedClientError* error, const char* ldName, // 逻辑设备名 (domainId) const char* journalRef, // 日志引用 MmsValue* startTime, // 开始时间 (MmsValue of type BINARY_TIME) MmsValue* endTime, // 结束时间 bool* moreFollows); // 输出: 是否还有更多 // 按条目读取日志 LinkedList IedConnection_readJournalStartAfter(IedConnection self, IedClientError* error, const char* ldName, const char* journalRef, MmsValue* timeSpecification, // 时间起点 MmsValue* entrySpecification, // 条目起点 bool* moreFollows); // 日志条目访问: uint64_t IedConnection_JournalEntry_getEntryID(JournalEntry self); uint64_t IedConnection_JournalEntry_getTimestamp(JournalEntry self); LinkedList IedConnection_JournalEntry_getVariables(JournalEntry self); ``` --- ## 10. GOOSE 订阅 ### 10.1 GOOSE 订阅者 API ```c #include "goose_receiver.h" #include "goose_subscriber.h" // 创建 GOOSE 接收器 GooseReceiver GooseReceiver_create(void); // 设置网络接口 void GooseReceiver_setInterfaceId(GooseReceiver self, const char* interfaceId); // 创建 GOOSE 订阅者 GooseSubscriber GooseSubscriber_create( char* goCbRef, // GoCB 引用, 如 "simpleIOGenericIO/LLN0$GO$gcbAnalogValues" MmsValue* dataSetValues); // 数据集值容器或 NULL // 若为 NULL, 收到第一个匹配消息后自动创建 // 设置订阅者过滤器 void GooseSubscriber_setDstMac(GooseSubscriber self, uint8_t dstMac[6]); // 设置后只接收目标MAC匹配的消息 void GooseSubscriber_setAppId(GooseSubscriber self, uint16_t appId); // 设置后只接收 APPID 匹配的消息 // 设置为观察者模式 (接收所有GOOSE消息) void GooseSubscriber_setObserver(GooseSubscriber self); // 设置监听回调 typedef void (*GooseListener)(GooseSubscriber subscriber, void* parameter); void GooseSubscriber_setListener(GooseSubscriber self, GooseListener listener, void* parameter); // 添加订阅者到接收器 void GooseReceiver_addSubscriber(GooseReceiver self, GooseSubscriber subscriber); // 启动接收 (创建后台线程) void GooseReceiver_start(GooseReceiver self); // 检查运行状态 bool GooseReceiver_isRunning(GooseReceiver self); // 停止接收 void GooseReceiver_stop(GooseReceiver self); // 销毁接收器 (同时销毁所有订阅者) void GooseReceiver_destroy(GooseReceiver self); ``` ### 10.2 GooseSubscriber 信息获取 ```c // 消息有效性 bool GooseSubscriber_isValid(GooseSubscriber self); GooseParseError GooseSubscriber_getParseError(GooseSubscriber self); // 消息元数据 char* GooseSubscriber_getGoId(GooseSubscriber self); char* GooseSubscriber_getGoCbRef(GooseSubscriber self); char* GooseSubscriber_getDataSet(GooseSubscriber self); uint32_t GooseSubscriber_getStNum(GooseSubscriber self); uint32_t GooseSubscriber_getSqNum(GooseSubscriber self); uint64_t GooseSubscriber_getTimestamp(GooseSubscriber self); // ms 时间戳 uint32_t GooseSubscriber_getTimeAllowedToLive(GooseSubscriber self); uint32_t GooseSubscriber_getConfRev(GooseSubscriber self); bool GooseSubscriber_isTest(GooseSubscriber self); bool GooseSubscriber_needsCommission(GooseSubscriber self); // MAC 地址 void GooseSubscriber_getSrcMac(GooseSubscriber self, uint8_t* buffer); void GooseSubscriber_getDstMac(GooseSubscriber self, uint8_t* buffer); // VLAN bool GooseSubscriber_isVlanSet(GooseSubscriber self); uint16_t GooseSubscriber_getVlanId(GooseSubscriber self); uint8_t GooseSubscriber_getVlanPrio(GooseSubscriber self); // 数据集值 MmsValue* GooseSubscriber_getDataSetValues(GooseSubscriber self); // 注意: 如果接收器运行在独立线程, 此 MmsValue 仅应在回调函数内使用! // 获取 APPID int32_t GooseSubscriber_getAppId(GooseSubscriber self); ``` ### 10.3 GOOSE 回调示例 ```c static void gooseListener(GooseSubscriber subscriber, void* parameter) { printf("GOOSE: stNum=%u sqNum=%u\n", GooseSubscriber_getStNum(subscriber), GooseSubscriber_getSqNum(subscriber)); MmsValue* values = GooseSubscriber_getDataSetValues(subscriber); char buf[1024]; MmsValue_printToBuffer(values, buf, 1024); printf(" allData: %s\n", buf); } ``` --- ## 11. GOOSE 发布 (独立) (与服务端手册第11.2节相同) ```c #include "goose_publisher.h" // 通信参数 typedef struct sCommParameters { uint8_t vlanPriority; uint16_t vlanId; uint16_t appId; uint8_t dstAddress[6]; } CommParameters; // 创建 GoosePublisher GoosePublisher_create(CommParameters* params, const char* interfaceID); GoosePublisher GoosePublisher_createEx(CommParameters* params, const char* interfaceID, bool useVlanTag); // 配置 void GoosePublisher_setGoID(GoosePublisher self, char* goID); void GoosePublisher_setGoCbRef(GoosePublisher self, char* goCbRef); void GoosePublisher_setDataSetRef(GoosePublisher self, char* dataSetRef); void GoosePublisher_setConfRev(GoosePublisher self, uint32_t confRev); void GoosePublisher_setTimeAllowedToLive(GoosePublisher self, uint32_t ttl); void GoosePublisher_setSimulation(GoosePublisher self, bool simulation); void GoosePublisher_setNeedsCommission(GoosePublisher self, bool ndsCom); // 发布 int GoosePublisher_publish(GoosePublisher self, LinkedList dataSet); // dataSet 是 MmsValue* 链表 // 状态数管理 uint64_t GoosePublisher_increaseStNum(GoosePublisher self); // 数据变化时调用 void GoosePublisher_reset(GoosePublisher self); // 重置 stNum/sqNum void GoosePublisher_setStNum(GoosePublisher self, uint32_t stNum); // 测试用 void GoosePublisher_setSqNum(GoosePublisher self, uint32_t sqNum); // 测试用 // 销毁 void GoosePublisher_destroy(GoosePublisher self); ``` --- ## 12. Sampled Values 客户端 ### 12.1 SV 发布者 (独立) ```c #include "sv_publisher.h" // 创建 SVPublisher SVPublisher_create(CommParameters* params, const char* interfaceId); SVPublisher SVPublisher_createEx(CommParameters* params, const char* interfaceId, bool useVlanTag); // 添加 ASDU SVPublisher_ASDU SVPublisher_addASDU(SVPublisher self, const char* svId, // SV ID 字符串 const char* datset, // 数据集引用 uint32_t confRev); // 配置版本 // 向 ASDU 添加数据点 int SVPublisher_ASDU_addFLOAT(SVPublisher_ASDU asdu); int SVPublisher_ASDU_addINT32(SVPublisher_ASDU asdu); int SVPublisher_ASDU_addINT64(SVPublisher_ASDU asdu); int SVPublisher_ASDU_addTimestamp(SVPublisher_ASDU asdu); int SVPublisher_ASDU_addQuality(SVPublisher_ASDU asdu); // 完成配置 (锁定, 不能再添加数据点) void SVPublisher_setupComplete(SVPublisher self); // 设置数据点值 void SVPublisher_ASDU_setFLOAT(SVPublisher_ASDU asdu, int index, float value); void SVPublisher_ASDU_setINT32(SVPublisher_ASDU asdu, int index, int32_t value); void SVPublisher_ASDU_setINT64(SVPublisher_ASDU asdu, int index, int64_t value); void SVPublisher_ASDU_setTimestamp(SVPublisher_ASDU asdu, int index, Timestamp value); void SVPublisher_ASDU_setQuality(SVPublisher_ASDU asdu, int index, Quality value); // 递增采样计数 void SVPublisher_ASDU_increaseSmpCnt(SVPublisher_ASDU asdu); // 发送 SV 消息 void SVPublisher_publish(SVPublisher self); // 销毁 void SVPublisher_destroy(SVPublisher self); ``` ### 12.2 SV 订阅者 ```c #include "sv_subscriber.h" // 创建接收器 SVReceiver SVReceiver_create(void); void SVReceiver_setInterfaceId(SVReceiver self, const char* interfaceId); // 创建订阅者 SVSubscriber SVSubscriber_create(const char* svId, uint16_t appId); // svId 为 NULL 时不按 SV ID 过滤 // 设置监听回调 typedef void (*SVUpdateListener)( SVSubscriber subscriber, void* parameter, SVSubscriber_ASDU asdu); void SVSubscriber_setListener(SVSubscriber self, SVUpdateListener listener, void* parameter); // 添加订阅者到接收器 void SVReceiver_addSubscriber(SVReceiver self, SVSubscriber subscriber); // 启动/停止 void SVReceiver_start(SVReceiver self); bool SVReceiver_isRunning(SVReceiver self); void SVReceiver_stop(SVReceiver self); void SVReceiver_destroy(SVReceiver self); // ASDU 信息获取 const char* SVSubscriber_ASDU_getSvId(SVSubscriber_ASDU asdu); uint32_t SVSubscriber_ASDU_getSmpCnt(SVSubscriber_ASDU asdu); uint32_t SVSubscriber_ASDU_getConfRev(SVSubscriber_ASDU asdu); uint32_t SVSubscriber_ASDU_getDataSize(SVSubscriber_ASDU asdu); // 获取数据 (按字节偏移量) float SVSubscriber_ASDU_getFLOAT32(SVSubscriber_ASDU asdu, uint32_t byteOffset); int32_t SVSubscriber_ASDU_getINT32(SVSubscriber_ASDU asdu, uint32_t byteOffset); int64_t SVSubscriber_ASDU_getINT64(SVSubscriber_ASDU asdu, uint32_t byteOffset); Timestamp SVSubscriber_ASDU_getTimestamp(SVSubscriber_ASDU asdu, uint32_t byteOffset); Quality SVSubscriber_ASDU_getQuality(SVSubscriber_ASDU asdu, uint32_t byteOffset); // 注意: 需要先验知识了解数据集结构! ``` --- ## 13. SV 控制块 (SVCB) ```c // 创建 SVCB 客户端对象 ClientSVControlBlock ClientSVControlBlock_create( IedConnection connection, const char* reference); void ClientSVControlBlock_destroy(ClientSVControlBlock self); // 判断是否为组播 bool ClientSVControlBlock_isMulticast(ClientSVControlBlock self); // 获取最后一次通讯错误 IedClientError ClientSVControlBlock_getLastComError(ClientSVControlBlock self); // 启用/禁用 SV bool ClientSVControlBlock_setSvEna(ClientSVControlBlock self, bool value); bool ClientSVControlBlock_getSvEna(ClientSVControlBlock self); // 预留 bool ClientSVControlBlock_setResv(ClientSVControlBlock self, bool value); bool ClientSVControlBlock_getResv(ClientSVControlBlock self); // MSV/Usv ID char* ClientSVControlBlock_getMsvID(ClientSVControlBlock self); ``` --- ## 14. MmsValue 完整操作手册 ### 14.1 MmsValue 类型定义 (MmsType) ```c typedef enum { MMS_ARRAY = 0, MMS_STRUCTURE = 1, MMS_BOOLEAN = 2, MMS_BIT_STRING = 3, MMS_INTEGER = 4, MMS_UNSIGNED = 5, MMS_FLOAT = 6, MMS_OCTET_STRING = 7, MMS_VISIBLE_STRING = 8, MMS_GENERALIZED_TIME = 9, MMS_BINARY_TIME = 10, MMS_BCD = 11, MMS_BOOLEAN_ARRAY = 12, MMS_OBJ_ID = 13, MMS_STRING = 14, MMS_UTC_TIME = 15, MMS_DATA_ACCESS_ERROR = 16, MMS_INTEGER_64 = 17 } MmsType; ``` ### 14.2 创建 MmsValue ```c // === 基本类型创建 === MmsValue* MmsValue_newBoolean(bool value); MmsValue* MmsValue_newFloat(float value); MmsValue* MmsValue_newDouble(double value); MmsValue* MmsValue_newIntegerFromInt8(int8_t value); MmsValue* MmsValue_newIntegerFromInt16(int16_t value); MmsValue* MmsValue_newIntegerFromInt32(int32_t value); MmsValue* MmsValue_newIntegerFromInt64(int64_t value); MmsValue* MmsValue_newUnsignedFromUint32(uint32_t value); // === 字符串 === MmsValue* MmsValue_newVisibleString(const char* string); MmsValue* MmsValue_newVisibleStringWithSize(int size); MmsValue* MmsValue_newString(const char* string); MmsValue* MmsValue_newStringWithSize(int size); // === 时间 === MmsValue* MmsValue_newUtcTimeByMsTime(uint64_t msTime); MmsValue* MmsValue_newUtcTime(uint32_t timeInSecWithQuality); MmsValue* MmsValue_newBinaryTime(bool timeOfDay); // === 位串 === MmsValue* MmsValue_newBitString(int bitSize); // === 字节串 === MmsValue* MmsValue_newOctetString(int size, int maxSize); // === 数组/结构体 === MmsValue* MmsValue_createArray(const MmsVariableSpecification* elementType, int size); MmsValue* MmsValue_createEmptyArray(int size); MmsValue* MmsValue_createEmptyStructure(int size); ``` ### 14.3 读取 MmsValue ```c // === 获取类型 === MmsType MmsValue_getType(const MmsValue* self); // === 类型转换读取 === int64_t MmsValue_toInt64(const MmsValue* self); int32_t MmsValue_toInt32(const MmsValue* self); uint32_t MmsValue_toUint32(const MmsValue* self); double MmsValue_toDouble(const MmsValue* self); float MmsValue_toFloat(const MmsValue* self); bool MmsValue_getBoolean(const MmsValue* self); // === 字符串 === const char* MmsValue_toString(MmsValue* self); int MmsValue_getStringSize(MmsValue* self); // === 时间 === uint32_t MmsValue_toUnixTimestamp(const MmsValue* self); // === 数组/结构体 === uint32_t MmsValue_getArraySize(const MmsValue* self); MmsValue* MmsValue_getElement(const MmsValue* array, int index); // === 位串 === bool MmsValue_getBitStringBit(const MmsValue* self, int bitPos); int MmsValue_getBitStringSize(const MmsValue* self); int MmsValue_getBitStringByteSize(const MmsValue* self); // === 数据访问错误 === MmsDataAccessError MmsValue_getDataAccessError(const MmsValue* self); ``` ### 14.4 修改 MmsValue ```c // === 设置值 === void MmsValue_setFloat(MmsValue* self, float newValue); void MmsValue_setDouble(MmsValue* self, double newValue); void MmsValue_setBoolean(MmsValue* self, bool value); void MmsValue_setInt8(MmsValue* self, int8_t value); void MmsValue_setInt16(MmsValue* self, int16_t value); void MmsValue_setInt32(MmsValue* self, int32_t value); void MmsValue_setInt64(MmsValue* self, int64_t value); void MmsValue_setUint8(MmsValue* self, uint8_t value); void MmsValue_setUint16(MmsValue* self, uint16_t value); void MmsValue_setUint32(MmsValue* self, uint32_t value); // === 字符串 === void MmsValue_setVisibleString(MmsValue* self, const char* string); void MmsValue_setString(MmsValue* self, const char* string); // === 时间 === void MmsValue_setUtcTimeMs(MmsValue* self, uint64_t msTime); void MmsValue_setUtcTimeByBuffer(MmsValue* self, const uint8_t* buf); void MmsValue_setBinaryTime(MmsValue* self, bool timeOfDay); // === 位串 === void MmsValue_setBitStringBit(MmsValue* self, int bitPos, bool value); void MmsValue_deleteAllBitStringBits(MmsValue* self); void MmsValue_setBitStringFromInteger(MmsValue* self, uint32_t intValue); // === 数组/结构体元素 === void MmsValue_setElement(MmsValue* complexValue, int index, MmsValue* elementValue); // 替换现有元素,调用者负责释放被替换的值 ``` ### 14.5 序列化与打印 ```c // 打印到缓冲区 void MmsValue_printToBuffer(const MmsValue* self, char* buffer, int bufferSize); // MMS 数据编解码 int MmsValue_encodeMmsData(const MmsValue* self, uint8_t* buffer, int bufOffset, bool berEncoding); MmsValue* MmsValue_decodeMmsData(const uint8_t* buffer, int bufOffset, int bufLength); ``` ### 14.6 内存管理 ```c // 删除单个 MmsValue (只删除自身,不删除子元素) void MmsValue_delete(MmsValue* value); // 深度删除 MmsValue (递归删除所有子元素) void MmsValue_deleteDeep(MmsValue* value); ``` --- ## 15. MmsVariableSpecification 用于描述 MMS 变量的类型规格,在获取变量规格或创建数据集时使用。 ```c // 销毁 void MmsVariableSpecification_destroy(MmsVariableSpecification* self); // 获取类型 MmsType MmsVariableSpecification_getType(MmsVariableSpecification* self); // 获取名称 const char* MmsVariableSpecification_getName(MmsVariableSpecification* self); // 获取大小 (结构体/数组的元素数, 或整数/字符串的位/字节大小) int MmsVariableSpecification_getSize(MmsVariableSpecification* self); // 检查值是否匹配此类型 bool MmsVariableSpecification_isValueOfType( MmsVariableSpecification* self, const MmsValue* value); // 获取子规格 MmsVariableSpecification* MmsVariableSpecification_getChildSpecificationByIndex( MmsVariableSpecification* self, int index); MmsVariableSpecification* MmsVariableSpecification_getChildSpecificationByName( MmsVariableSpecification* self, const char* name, int* index); MmsVariableSpecification* MmsVariableSpecification_getArrayElementSpecification( MmsVariableSpecification* self); // 递归查找命名变量 MmsVariableSpecification* MmsVariableSpecification_getNamedVariableRecursive( MmsVariableSpecification* self, const char* nameId); // 获取子值 MmsValue* MmsVariableSpecification_getChildValue( MmsVariableSpecification* self, MmsValue* value, const char* childId); // 获取结构体元素名列表 LinkedList MmsVariableSpecification_getStructureElements( MmsVariableSpecification* self); // 浮点数格式信息 int MmsVariableSpecification_getExponentWidth(MmsVariableSpecification* self); ``` ### 15.1 MmsVariableAccessSpecification (数据集条目规格) ```c // 创建简单条目 MmsVariableAccessSpecification* MmsVariableAccessSpecification_create( char* domainId, char* itemId); // 创建带可选访问的条目 (数组元素或数组元素组件) MmsVariableAccessSpecification* MmsVariableAccessSpecification_createAlternateAccess( char* domainId, char* itemId, int32_t index, // 数组索引 char* componentName); // 组件名 (NULL 表示整个数组元素) // 销毁 void MmsVariableAccessSpecification_destroy(MmsVariableAccessSpecification* self); ``` --- ## 16. TLS 安全连接 ```c #include "tls_config.h" // 创建 TLS 配置 TLSConfiguration tlsConfig = TLSConfiguration_create(); // 设置为客户端模式 void TLSConfiguration_setClientMode(TLSConfiguration self); // 加载 CA 证书 bool TLSConfiguration_addCACertificateFromFile(TLSConfiguration self, const char* certFilename); // 加载客户端私钥 bool TLSConfiguration_setOwnKeyFromFile(TLSConfiguration self, const char* keyFilename, const char* password); // 加载客户端证书 bool TLSConfiguration_setOwnCertificateFromFile(TLSConfiguration self, const char* certFilename); // 证书链验证 void TLSConfiguration_setChainValidation(TLSConfiguration self, bool enable); // 仅允许已知证书 void TLSConfiguration_setAllowOnlyKnownCertificates(TLSConfiguration self, bool enable); // 安全事件处理器 void TLSConfiguration_setEventHandler(TLSConfiguration self, TLSEventHandler handler, void* parameter); // 创建 TLS 连接 IedConnection con = IedConnection_createWithTlsSupport(tlsConfig); // 或 IedConnection con = IedConnection_createEx(tlsConfig, true); // 连接后销毁配置 TLSConfiguration_destroy(tlsConfig); ``` ### TLS 事件级别 ```c typedef enum { TLS_SEC_EVT_INFO = 0, // 信息 TLS_SEC_EVT_WARNING = 1, // 警告 TLS_SEC_EVT_INCIDENT = 2 // 事件 } TLSEventLevel; // 事件代码: // TLS_EVENT_CODE_ALM_ALGO_NOT_SUPPORTED 1 // TLS_EVENT_CODE_ALM_UNSECURE_COMMUNICATION 2 // TLS_EVENT_CODE_ALM_CERT_UNAVAILABLE 3 ``` --- ## 17. 单线程模式 (非线程模式) 不使用后台线程,适合嵌入式环境。 ```c // 1. 创建非线程连接 IedConnection con = IedConnection_createEx(NULL, false); // 2. 安装状态变化处理器 (跟踪连接状态) IedConnection_installStateChangedHandler(con, stateChangedHandler, NULL); // 3. 异步连接 IedConnection_connectAsync(con, &error, hostname, port); // 4. 主循环: 调用 IedConnection_tick() 直到连接建立 while (IedConnection_getState(con) != IED_STATE_CONNECTED) { if (IedConnection_getState(con) == IED_STATE_CLOSED) { // 连接失败 break; } if (IedConnection_tick(con) == true) Thread_sleep(10); } // 5. 使用所有异步API (*Async) 变体 IedConnection_readObjectAsync(con, &error, ref, fc, readHandler, param); // 6. 周期性调用 tick 处理消息 uint64_t start = Hal_getTimeInMs(); while (Hal_getTimeInMs() < start + 1000) { if (IedConnection_tick(con) == true) Thread_sleep(10); } // 7. 异步释放 IedConnection_releaseAsync(con, &error); while (IedConnection_getState(con) != IED_STATE_CLOSED) { if (IedConnection_tick(con) == true) Thread_sleep(10); } // 8. 销毁 IedConnection_destroy(con); ``` ### 异步回调通用模式 ```c // 读取回调 static void readObjectHandler(uint32_t invokeId, void* parameter, IedClientError err, MmsValue* value) { if (err == IED_ERROR_OK) { printf("Value: %f\n", MmsValue_toFloat(value)); MmsValue_delete(value); // 用户负责释放! } } // 通用服务回调 static void genericHandler(uint32_t invokeId, void* parameter, IedClientError err) { printf("Operation result: %i\n", err); } ``` --- ## 18. MmsConnection 底层接口 通过 `IedConnection_getMmsConnection()` 获取,提供 MMS 级精细控制。 ### 18.1 连接管理 ```c // 创建 MmsConnection MmsConnection_create(void); MmsConnection MmsConnection_createSecure(TLSConfiguration tlsConfig); MmsConnection MmsConnection_createNonThreaded(TLSConfiguration tlsConfig); // 连接 bool MmsConnection_connect(MmsConnection self, MmsError* error, const char* serverName, int serverPort); void MmsConnection_connectAsync(MmsConnection self, MmsError* error, const char* serverName, int serverPort); // 非线程模式: 周期性调用 bool MmsConnection_tick(MmsConnection self); // 关闭/Abort/Conclude void MmsConnection_close(MmsConnection self); void MmsConnection_abort(MmsConnection self, MmsError* error); void MmsConnection_abortAsync(MmsConnection self, MmsError* error); void MmsConnection_conclude(MmsConnection self, MmsError* error); void MmsConnection_concludeAsync(MmsConnection self, MmsError* error, MmsConnection_ConcludeAbortHandler handler, void* parameter); // 销毁 void MmsConnection_destroy(MmsConnection self); ``` ### 18.2 配置 ```c // 设置请求超时 void MmsConnection_setRequestTimeout(MmsConnection self, uint32_t timeoutInMs); uint32_t MmsConnection_getRequestTimeout(MmsConnection self); // 设置连接超时 void MmsConnection_setConnectTimeout(MmsConnection self, uint32_t timeoutInMs); // 设置 MMS PDU 最大尺寸 (localDetail) void MmsConnection_setLocalDetail(MmsConnection self, int32_t localDetail); int32_t MmsConnection_getLocalDetail(MmsConnection self); // 设置文件存储路径 void MmsConnection_setFilestoreBasepath(MmsConnection self, const char* basepath); // 获取 ISO/MMS 连接参数 IsoConnectionParameters MmsConnection_getIsoConnectionParameters(MmsConnection self); MmsConnectionParameters MmsConnection_getMmsConnectionParameters(MmsConnection self); void MmsConnection_setIsoConnectionParameters(MmsConnection self, IsoConnectionParameters* params); // 安装连接状态变化处理器 void MmsConnection_setConnectionStateChangedHandler(MmsConnection self, MmsConnectionStateChangedHandler handler, void* parameter); // 安装连接丢失处理器 typedef void (*MmsConnectionLostHandler)(MmsConnection connection, void* parameter); void MmsConnection_setConnectionLostHandler(MmsConnection self, MmsConnectionLostHandler handler, void* parameter); // 安装 InformationReport 处理器 (接收服务器主动发送的未请求消息) typedef void (*MmsInformationReportHandler)(void* parameter, char* domainName, char* variableListName, MmsValue* value, bool isVariableListName); void MmsConnection_setInformationReportHandler(MmsConnection self, MmsInformationReportHandler handler, void* parameter); // 安装原始消息处理器 (调试用) typedef void (*MmsRawMessageHandler)(void* parameter, uint8_t* message, int messageLength, bool received); void MmsConnection_setRawMessageHandler(MmsConnection self, MmsRawMessageHandler handler, void* parameter); // 需要 CONFIG_MMS_RAW_MESSAGE_LOGGING ``` ### 18.3 MMS 读写操作 ```c // 读取变量 MmsValue* MmsConnection_readVariable(MmsConnection self, MmsError* error, const char* domainId, const char* itemId); void MmsConnection_readVariableAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* error, const char* domainId, const char* itemId, MmsConnection_ReadVariableHandler handler, void* parameter); // 读取变量组件 MmsValue* MmsConnection_readVariableComponent(MmsConnection self, MmsError* error, const char* domainId, const char* itemId, const char* componentId); // 读取数组元素 MmsValue* MmsConnection_readArrayElements(MmsConnection self, MmsError* error, const char* domainId, const char* itemId, uint32_t startIndex, uint32_t numberOfElements); // 读取单个数组元素及组件 MmsValue* MmsConnection_readSingleArrayElementWithComponent( MmsConnection self, MmsError* error, const char* domainId, const char* itemId, uint32_t index, const char* componentId); // 读取多个变量 MmsValue* MmsConnection_readMultipleVariables(MmsConnection self, MmsError* error, const char* domainId, LinkedList /**/ items); // 写入变量 MmsDataAccessError MmsConnection_writeVariable(MmsConnection self, MmsError* error, const char* domainId, const char* itemId, MmsValue* value); // 写入变量组件 MmsDataAccessError MmsConnection_writeVariableComponent(MmsConnection self, MmsError* error, const char* domainId, const char* itemId, const char* componentId, MmsValue* value); // 写入数组元素 MmsDataAccessError MmsConnection_writeArrayElements(MmsConnection self, MmsError* error, const char* domainId, const char* itemId, int index, int numberOfElements, MmsValue* value); // 写入单个数组元素组件 MmsDataAccessError MmsConnection_writeSingleArrayElementWithComponent( MmsConnection self, MmsError* error, const char* domainId, const char* itemId, uint32_t arrayIndex, const char* componentId, MmsValue* value); // 写入多个变量 void MmsConnection_writeMultipleVariables(MmsConnection self, MmsError* error, const char* domainId, LinkedList /**/ items, LinkedList /**/ values, LinkedList* /**/ accessResults); ``` ### 18.4 命名变量列表 (Named Variable List) 操作 ```c // 读取命名变量列表 MmsValue* MmsConnection_readNamedVariableListValues(MmsConnection self, MmsError* error, const char* domainId, const char* listName, bool specWithResult); MmsValue* MmsConnection_readNamedVariableListValuesAssociationSpecific( MmsConnection self, MmsError* error, const char* listName, bool specWithResult); // 写入命名变量列表 void MmsConnection_writeNamedVariableList(MmsConnection self, MmsError* error, bool isAssociationSpecific, const char* domainId, const char* itemId, LinkedList /**/ values, LinkedList* /**/ accessResults); // 定义命名变量列表 (创建数据集) void MmsConnection_defineNamedVariableList(MmsConnection self, MmsError* error, const char* domainId, const char* listName, LinkedList /**/ variableSpecs); void MmsConnection_defineNamedVariableListAssociationSpecific( MmsConnection self, MmsError* error, const char* listName, LinkedList variableSpecs); // 读取命名变量列表目录 LinkedList MmsConnection_readNamedVariableListDirectory(MmsConnection self, MmsError* error, const char* domainId, const char* listName, bool* deletable); LinkedList MmsConnection_readNamedVariableListDirectoryAssociationSpecific( MmsConnection self, MmsError* error, const char* listName, bool* deletable); // 删除命名变量列表 bool MmsConnection_deleteNamedVariableList(MmsConnection self, MmsError* error, const char* domainId, const char* listName); bool MmsConnection_deleteAssociationSpecificNamedVariableList( MmsConnection self, MmsError* error, const char* listName); ``` ### 18.5 目录浏览 ```c // 获取 VMD 变量名列表 LinkedList MmsConnection_getVMDVariableNames(MmsConnection self, MmsError* error); // 获取域列表 LinkedList MmsConnection_getDomainNames(MmsConnection self, MmsError* error); // 获取域内变量名列表 LinkedList MmsConnection_getDomainVariableNames(MmsConnection self, MmsError* error, const char* domainId); // 获取域内数据集名列表 LinkedList MmsConnection_getDomainVariableListNames(MmsConnection self, MmsError* error, const char* domainId); // 获取域内日志名列表 LinkedList MmsConnection_getDomainJournals(MmsConnection self, MmsError* error, const char* domainId); // 获取关联特定数据集名列表 LinkedList MmsConnection_getVariableListNamesAssociationSpecific( MmsConnection self, MmsError* error); // 获取变量类型规格 MmsVariableSpecification* MmsConnection_getVariableAccessAttributes( MmsConnection self, MmsError* error, const char* domainId, const char* itemId); ``` ### 18.6 服务器状态与身份 ```c // 获取服务器身份 MmsServerIdentity* MmsConnection_identify(MmsConnection self, MmsError* error); // MmsServerIdentity: { char* vendorName; char* modelName; char* revision; } void MmsServerIdentity_destroy(MmsServerIdentity* self); // 获取服务器状态 void MmsConnection_getServerStatus(MmsConnection self, MmsError* error, int* vmdLogicalStatus, int* vmdPhysicalStatus, bool extendedDerivation); // vmdLogicalStatus: 0-3 (STATE_CHANGES_ALLOWED/NO_CHANGES/LIMITED/SUPPORT) // vmdPhysicalStatus: 0-3 (OPERATIONAL/PARTIALLY/INOPERATIONAL/NEEDS_COMMISSIONING) ``` ### 18.7 MMS 文件服务 ```c // 获取文件目录 bool MmsConnection_getFileDirectory(MmsConnection self, MmsError* error, const char* fileSpecification, const char* continueAfter, MmsFileDirectoryHandler handler, void* handlerParameter); // 打开文件 int32_t MmsConnection_fileOpen(MmsConnection self, MmsError* error, const char* filename, uint32_t initialPosition, uint32_t* fileSize, uint64_t* lastModified); // 读取文件块 bool MmsConnection_fileRead(MmsConnection self, MmsError* error, int32_t frsmId, MmsFileReadHandler handler, void* handlerParameter); // 关闭文件 void MmsConnection_fileClose(MmsConnection self, MmsError* error, int32_t frsmId); // 删除文件 void MmsConnection_fileDelete(MmsConnection self, MmsError* error, const char* fileName); // 重命名文件 void MmsConnection_fileRename(MmsConnection self, MmsError* error, const char* currentFileName, const char* newFileName); // 下载文件 (ObtainFile) void MmsConnection_obtainFile(MmsConnection self, MmsError* error, const char* sourceFile, const char* destinationFile); ``` ### 18.8 MMS 日志服务 ```c // 按时间范围读取 LinkedList MmsConnection_readJournalTimeRange(MmsConnection self, MmsError* error, const char* domainId, const char* itemId, MmsValue* startTime, MmsValue* endTime, bool* moreFollows); // 按条目读取 LinkedList MmsConnection_readJournalStartAfter(MmsConnection self, MmsError* error, const char* domainId, const char* itemId, MmsValue* timeSpecification, MmsValue* entrySpecification, bool* moreFollows); // 日志条目结构 struct sMmsJournalEntry { MmsValue* entryID; // MMS_OCTET_STRING MmsValue* occurenceTime; // MMS_BINARY_TIME LinkedList journalVariables;// LinkedList }; struct sMmsJournalVariable { char* tag; MmsValue* value; }; // 访问日志条目 MmsValue* MmsJournalEntry_getEntryID(MmsJournalEntry self); MmsValue* MmsJournalEntry_getOccurenceTime(MmsJournalEntry self); LinkedList MmsJournalEntry_getJournalVariables(MmsJournalEntry self); const char* MmsJournalVariable_getTag(MmsJournalVariable self); MmsValue* MmsJournalVariable_getValue(MmsJournalVariable self); void MmsJournalEntry_destroy(MmsJournalEntry self); ``` --- ## 19. 完整代码示例 ### 19.1 基本客户端 (同步模式) ```c #include "iec61850_client.h" #include "hal_thread.h" #include #include void reportCallback(void* parameter, ClientReport report) { MmsValue* values = ClientReport_getDataSetValues(report); printf("Report received for %s\n", ClientReport_getRcbReference(report)); for (int i = 0; i < 4; i++) { ReasonForInclusion reason = ClientReport_getReasonForInclusion(report, i); if (reason != IEC61850_REASON_NOT_INCLUDED) { printf(" [%i] = %i (reason=%i)\n", i, MmsValue_getBoolean(MmsValue_getElement(values, i)), reason); } } } int main(int argc, char** argv) { const char* host = (argc > 1) ? argv[1] : "localhost"; int port = (argc > 2) ? atoi(argv[2]) : 102; IedClientError error; IedConnection con = IedConnection_create(); IedConnection_connect(con, &error, host, port); if (error != IED_ERROR_OK) { printf("Connect failed: %i\n", error); IedConnection_destroy(con); return -1; } // --- 读取模拟量 --- MmsValue* val = IedConnection_readObject(con, &error, "simpleIOGenericIO/GGIO1.AnIn1.mag.f", IEC61850_FC_MX); if (val) { printf("AnIn1.mag.f = %f\n", MmsValue_toFloat(val)); MmsValue_delete(val); } // --- 写入 --- MmsValue* strVal = MmsValue_newVisibleString("libiec61850.com"); IedConnection_writeObject(con, &error, "simpleIOGenericIO/GGIO1.NamPlt.vendor", IEC61850_FC_DC, strVal); MmsValue_delete(strVal); // --- 读取数据集 --- ClientDataSet ds = IedConnection_readDataSetValues(con, &error, "simpleIOGenericIO/LLN0.Events", NULL); if (ds) { printf("DataSet has %d entries\n", ClientDataSet_getDataSetSize(ds)); ClientDataSet_destroy(ds); } // --- 报告 --- ClientReportControlBlock rcb = IedConnection_getRCBValues(con, &error, "simpleIOGenericIO/LLN0.RP.EventsRCB01", NULL); if (rcb) { IedConnection_installReportHandler(con, "simpleIOGenericIO/LLN0.RP.EventsRCB01", ClientReportControlBlock_getRptId(rcb), reportCallback, NULL); ClientReportControlBlock_setTrgOps(rcb, TRG_OPT_DATA_UPDATE | TRG_OPT_INTEGRITY | TRG_OPT_GI); ClientReportControlBlock_setRptEna(rcb, true); ClientReportControlBlock_setIntgPd(rcb, 5000); IedConnection_setRCBValues(con, &error, rcb, RCB_ELEMENT_RPT_ENA | RCB_ELEMENT_TRG_OPS | RCB_ELEMENT_INTG_PD, true); if (error != IED_ERROR_OK) printf("Report activation failed: %i\n", error); Thread_sleep(1000); // 触发 GI ClientReportControlBlock_setGI(rcb, true); IedConnection_setRCBValues(con, &error, rcb, RCB_ELEMENT_GI, true); ClientReportControlBlock_destroy(rcb); } // --- 控制 --- ControlObjectClient ctl = ControlObjectClient_create( "simpleIOGenericIO/GGIO1.SPCSO1", con); if (ctl) { MmsValue* ctlVal = MmsValue_newBoolean(true); ControlObjectClient_setOrigin(ctl, NULL, CONTROL_ORCAT_REMOTE_CONTROL); if (ControlObjectClient_operate(ctl, ctlVal, 0)) printf("Control OK\n"); else printf("Control FAILED\n"); // 读取状态确认 MmsValue* stVal = IedConnection_readObject(con, &error, "simpleIOGenericIO/GGIO1.SPCSO1.stVal", IEC61850_FC_ST); if (stVal) { printf("Status = %i\n", MmsValue_getBoolean(stVal)); MmsValue_delete(stVal); } MmsValue_delete(ctlVal); ControlObjectClient_destroy(ctl); } IedConnection_close(con); IedConnection_destroy(con); return 0; } ``` ### 19.2 GOOSE 订阅者完整示例 ```c #include "goose_receiver.h" #include "goose_subscriber.h" #include "hal_thread.h" #include #include static int running = 1; void sigint_handler(int sig) { running = 0; } static void gooseListener(GooseSubscriber sub, void* param) { printf("GOOSE: stNum=%u sqNum=%u TTL=%u %s\n", GooseSubscriber_getStNum(sub), GooseSubscriber_getSqNum(sub), GooseSubscriber_getTimeAllowedToLive(sub), GooseSubscriber_isValid(sub) ? "VALID" : "INVALID"); MmsValue* vals = GooseSubscriber_getDataSetValues(sub); char buf[1024]; MmsValue_printToBuffer(vals, buf, 1024); printf(" Data: %s\n", buf); } int main(int argc, char** argv) { GooseReceiver rx = GooseReceiver_create(); GooseReceiver_setInterfaceId(rx, (argc > 1) ? argv[1] : "eth0"); uint8_t dstMac[6] = {0x01, 0x0c, 0xcd, 0x01, 0x00, 0x01}; GooseSubscriber sub = GooseSubscriber_create( "simpleIOGenericIO/LLN0$GO$gcbAnalogValues", NULL); GooseSubscriber_setDstMac(sub, dstMac); GooseSubscriber_setAppId(sub, 1000); GooseSubscriber_setListener(sub, gooseListener, NULL); GooseReceiver_addSubscriber(rx, sub); GooseReceiver_start(rx); if (!GooseReceiver_isRunning(rx)) { printf("Failed to start receiver\n"); GooseReceiver_destroy(rx); return -1; } signal(SIGINT, sigint_handler); while (running) Thread_sleep(100); GooseReceiver_stop(rx); GooseReceiver_destroy(rx); return 0; } ```