RTU/libiec61850/libiec61850手册/libiec61850_客户端开发手册.md

61 KiB

libiec61850 客户端开发手册

基于 libiec61850 v1.5.3 完整 API 参考。本文档旨在做到脱离代码即可完成客户端开发。


目录

  1. 概述与连接生命周期
  2. IedConnection 连接管理
  3. 数据读写操作
  4. 数据集 (DataSet) 操作
  5. 报告 (Report) 控制
  6. 控制操作 (Control)
  7. 服务器目录浏览与发现
  8. 文件服务
  9. 日志服务 (Journal)
  10. GOOSE 订阅
  11. GOOSE 发布 (独立)
  12. Sampled Values 客户端
  13. SV 控制块 (SVCB)
  14. MmsValue 完整操作手册
  15. MmsVariableSpecification
  16. TLS 安全连接
  17. 单线程模式 (非线程模式)
  18. MmsConnection 底层接口
  19. 完整代码示例

1. 概述与连接生命周期

IedConnection_create()        // 创建连接实例
    ↓
[可选] 设置本地地址/超时
    ↓
IedConnection_connect()        // 连接到服务器 (阻塞)
    ↓
读写操作 / 报告订阅 / 控制操作
    ↓
IedConnection_close()          // 关闭连接 (推荐)
  or IedConnection_abort()     // 中断连接
  or IedConnection_release()  // 正常释放 (MMS Conclude)
    ↓
IedConnection_destroy()        // 销毁并释放所有资源

1.1 连接状态枚举

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 创建连接实例

// 基本创建 (多线程模式, 非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 连接配置

// 设置本地地址/端口
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 连接与断开

// === 同步连接 (阻塞直到连接建立或超时) ===
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 连接状态回调

// 状态变化处理器 (推荐)
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 底层连接访问

// 获取底层 MmsConnection (用于直接调用 MMS 级服务)
MmsConnection IedConnection_getMmsConnection(IedConnection self);

// 单线程模式: 周期性调用以处理连接事件
bool IedConnection_tick(IedConnection self);
// 返回: true = 连接等待中可挂起, false = 忙需尽快再调用

3. 数据读写操作

3.1 同步读取

// 读取单个对象
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 异步读取

// 回调类型
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 同步写入

// 写入单个对象
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 异步写入

// 通用回调
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 类型

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<char*> 元素名称列表)
LinkedList IedConnection_getDataSetDirectory(IedConnection self,
    IedClientError* error,
    const char* dataSetReference,
    ClientDataSet dataSet);

// 写入数据集值
void IedConnection_writeDataSetValuesAsync(IedConnection self,
    IedClientError* error,
    const char* dataSetReference,
    LinkedList /* <MmsValue*> */ values,
    IedConnection_WriteDataSetHandler handler, void* parameter);

// 销毁数据集
void ClientDataSet_destroy(ClientDataSet self);

5. 报告 (Report) 控制

5.1 报告回调

// 报告回调函数类型
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 读取

// 获取报告中的数据集值
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 操作

// 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 报告配置典型流程

// 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 创建与销毁

// 创建控制对象客户端 (多线程模式)
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 控制操作函数

// === 直接操作 (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 (增强安全)

// 命令终止处理器
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 来源设置

// 设置控制来源
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 四种控制方式示例

// 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 同步函数

// 获取服务器目录 (逻辑设备列表)
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 异步函数

// NameList 回调类型
typedef void (*IedConnection_GetNameListHandler)(
    uint32_t invokeId, void* parameter,
    IedClientError err,
    LinkedList nameList,        // 名称列表 (LinkedList<char*>)
    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. 文件服务

// 获取文件目录
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)

// 按时间范围读取日志
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

#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 信息获取

// 消息有效性
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 回调示例

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节相同)

#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 发布者 (独立)

#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 订阅者

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

// 创建 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)

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

// === 基本类型创建 ===
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

// === 获取类型 ===
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

// === 设置值 ===
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 序列化与打印

// 打印到缓冲区
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 内存管理

// 删除单个 MmsValue (只删除自身,不删除子元素)
void MmsValue_delete(MmsValue* value);

// 深度删除 MmsValue (递归删除所有子元素)
void MmsValue_deleteDeep(MmsValue* value);

15. MmsVariableSpecification

用于描述 MMS 变量的类型规格,在获取变量规格或创建数据集时使用。

// 销毁
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 (数据集条目规格)

// 创建简单条目
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 安全连接

#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 事件级别

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. 单线程模式 (非线程模式)

不使用后台线程,适合嵌入式环境。

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

异步回调通用模式

// 读取回调
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 连接管理

// 创建
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 配置

// 设置请求超时
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 读写操作

// 读取变量
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 /*<char*>*/ 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 /*<char*>*/ items,
    LinkedList /*<MmsValue*>*/ values,
    LinkedList* /*<MmsValue*>*/ accessResults);

18.4 命名变量列表 (Named Variable List) 操作

// 读取命名变量列表
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 /*<MmsValue*>*/ values,
    LinkedList* /*<MmsValue*>*/ accessResults);

// 定义命名变量列表 (创建数据集)
void MmsConnection_defineNamedVariableList(MmsConnection self, MmsError* error,
    const char* domainId, const char* listName,
    LinkedList /*<MmsVariableAccessSpecification*>*/ 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 目录浏览

// 获取 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 服务器状态与身份

// 获取服务器身份
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 文件服务

// 获取文件目录
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 日志服务

// 按时间范围读取
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<MmsJournalVariable>
};

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 基本客户端 (同步模式)

#include "iec61850_client.h"
#include "hal_thread.h"
#include <stdlib.h>
#include <stdio.h>

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 订阅者完整示例

#include "goose_receiver.h"
#include "goose_subscriber.h"
#include "hal_thread.h"
#include <signal.h>
#include <stdio.h>

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