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