61 KiB
61 KiB
libiec61850 客户端开发手册
基于 libiec61850 v1.5.3 完整 API 参考。本文档旨在做到脱离代码即可完成客户端开发。
目录
- 概述与连接生命周期
- IedConnection 连接管理
- 数据读写操作
- 数据集 (DataSet) 操作
- 报告 (Report) 控制
- 控制操作 (Control)
- 服务器目录浏览与发现
- 文件服务
- 日志服务 (Journal)
- GOOSE 订阅
- GOOSE 发布 (独立)
- Sampled Values 客户端
- SV 控制块 (SVCB)
- MmsValue 完整操作手册
- MmsVariableSpecification
- TLS 安全连接
- 单线程模式 (非线程模式)
- MmsConnection 底层接口
- 完整代码示例
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;
}