RTU/mimo/工程/lib60870_CBB库解析.md

693 lines
27 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# lib60870 CBB 库详细解析
> **库版本**: IECVer2.01.059_260202 (2026-02-02)
> **厂家**: 科大智能电气有限公司 (2013-2026)
> **用途**: IEC 60870-5-101 / 60870-5-104 / 60870-5-103 电力远动规约协议栈
> **源码路径**: [src/protocol/lib60870/](../../src/protocol/lib60870/)
> **编译兼容头**: [cbb_compat.h](../../release/inc/cbb_compat.h) (通过 gcc `-include` 注入,不修改库文件)
---
## 1. 模块总览
### 1.1 文件清单
```
src/protocol/lib60870/
├── ChangeList.md # 版本变更记录 (V2.01.035 → V2.01.059)
├── 规约参数说明.md # 参数配置字典 (必读)
├── 规约库默认参数.md # 初始化的默认值
├── inc/ # 头文件目录
│ ├── lib60870_inc.h # 基础包含 (stdint/stdio) + 字节序宏
│ ├── lib60870_common.h # ★ 核心头文件: CS10x 结构体 + 全部 API 声明 + 常量
│ ├── lib60870_public.h # 公共工具: TaskFlag/checksum/CP56Time 编解码
│ ├── lib60870_process.h # 公共流程: SOE/故障/扰动检查与调度
│ ├── Asdu.h # ASDU 数据域解析: 品质描述词 + 发送/解码函数
│ ├── gb101.h # ★ IEC 101 链路层: 帧编解码/重发/链路状态
│ ├── gb104.h # ★ IEC 104 APCI 层: I/S/U帧/计时器
│ ├── gb103.h # IEC 103 规约: 保护通信/通用分类服务
│ └── md5.h # MD5 校验
└── src/ # 源文件目录
├── lib60870_common.c # CS10x 公共接口实现 + 回调注册
├── lib60870_public.c # 工具函数实现
├── lib60870_process.c # 公共流程实现
├── Asdu.c # ASDU 编解码 + 业务数据发送
├── gb101.c # 101 链路层实现
├── gb104.c # 104 APCI 层实现
├── gb103.c # 103 规约实现
└── md5.c # MD5 实现
```
### 1.2 头文件依赖关系
```mermaid
graph TD
A[lib60870_inc.h] --> B[lib60870_common.h]
B --> C[lib60870_public.h]
B --> D[lib60870_process.h]
B --> E[Asdu.h]
B --> F[gb101.h]
B --> G[gb104.h]
B --> H[gb103.h]
C --> E
D --> E
E --> F
E --> G
```
`lib60870_common.h` 是所有模块的**单一依赖入口**。外部只需 `#include "lib60870_common.h"` 即可获得全部类型和 API。
---
## 2. 核心数据结构
### 2.1 协议类型枚举 `CS10x_Type`
```c
typedef enum {
CS101_TYPE_S = 0, // CS101 从站 (被控站/终端)
CS101_TYPE_M, // CS101 主站
CS104_TYPE_S, // CS104 从站 (被控站/终端)
CS104_TYPE_M, // CS104 主站
} CS10x_Type;
```
**理解要点**
- `_S` = slave/从站 = 终端侧 = **本项目 RTU 使用的模式**
- `_M` = master/主站 = 调度侧
- 101 和 104 共用同一个 `CS10x` 结构体,通过类型枚举区分行为
### 2.2 核心结构体 `CS10x`(约 300 行,[源码](src/protocol/lib60870/inc/lib60870_common.h#L1300-L1599)
```c
struct CS10x_t { ... } CS10x; // 结构体定义 + typedef
typedef struct CS10x_t *CS10xHandle_t; // 句柄指针
```
**关键成员分类**
| 分类 | 成员 | 说明 |
|------|------|------|
| **协议标识** | `CS10x_Type eCS10x_Type` | 当前协议类型 (101/104 + 主/从) |
| **101 链路** | `ucAddr`, `ucReSend_Num`, `usReSend_Gap` | 链路地址、重发次数、重发间隔 |
| | `uc101InitFlag`, `uc101LinkState` | 101 链路初始化状态 |
| | `CONTROL101 stControl101Up/Down` | 101 控制域位域 |
| **104 链路** | `usSendNum/usRecvNum`, `usAckSendNum/usAckRecvNum` | 104 发送/接收序号 |
| | `VIec104Timer stM_vTimer[4]` | T0/T1/T2/T3 定时器 |
| **数据缓冲** | `VCommBuf stRecvBuf/SendBuf` | 接收/发送环形缓冲区 |
| **ASDU 参数** | `CS10x_AppLayerParameters stParam` | ASDU 字节长度配置 |
| | `CS104_APCIParameters st104Param` | 104 K/W/T 参数 |
| **任务标志** | `VFLAGS stTaskFlags` | 128 位任务位图 |
| **回调函数** | `pfnSend`, `pfnGetTime`, `pfnSetTime` | ★ 核心回调接口(见 3.1 |
| | `pfnGetYcCountByGroup` / `pfnGetYcValueByGroup` | 遥测回调 |
| | `pfnGetYxCountByGroup` / `pfnGetYxStatusByGroup` | 遥信回调 |
| | `pfnGetSOE` / `pfnUpdata_SOE_Pout` / `pfnGetSOE_SendNum` | SOE 回调 |
| | `pfnGetFaultEvent` / `pfnUpdata_FaultEvent_Pout` | 故障事件回调 |
| | `pfnFcYkVerify` | 遥控校验回调 |
| | `pfnGetDdCountByGroup` / `pfnGetDdValueByGroup` | 电度回调 |
| | `pfnGetDiturbYc` / `pfnGetTimeDdu` | 扰动遥测/电度时标回调 |
| **参数读写** | `pfnGetParamArea` / `pfnSetParamArea` | 定值区切换 |
| | `pfnAddIECParam` / `pfnRunReadIECParam` / `pfnRunWriteIECParam` | 参数读写 |
| | `pfnGetParamCount` / `pfnGetParamInfoByIndx` | 参数信息查询 |
| **文件传输** | `pfnGetProDirFile` / `pfnReadFile` / `pfnWrite` 等 | 文件服务回调(*已废弃旧接口,新接口为 `CS10x_S_FileReadOprtHandler`* |
| **软件升级** | `pfnOnlineProg` / `pfnUpdateOprt` | 在线升级回调 |
| **配置参数** | `usYcType(0/1/2)` / `usYxType(0/1)` / `usDdType(0/1)` | 数据类型选择 |
| | `ucSOEEn`, `ucCOSEn`, `ucYkEndFrameEn` | SOE/COS/遥控结束帧使能 |
| | `ucMutilFrameEn` | 104 多帧发送使能 |
| | `ulSummon_Gap`, `ulHeart_Gap`, `ulSyn_Gap` | 定时器间隔(ms) |
| | `ucInitType`, `ucFileAckType` | 初始化类型/文件确认类型 |
### 2.3 ASDU 信息体数据结构
```c
// 遥信—库发出给应用层填充数据
typedef struct {
unsigned int uiInfoAddr; // 信息体地址 (点号)
unsigned char ucStatus; // 状态 (0=分/1=合)
unsigned char ucStateType; // 0=单点 / 1=双点
} Yx_Info;
// 遥测—库发出给应用层填充数据
typedef struct {
unsigned int uiInfoAddr; // 信息体地址
float fVal; // 遥测值 (浮点)
unsigned char ucQds; // 品质描述词
} Yc_Info;
// SOE—事件顺序记录
typedef struct {
unsigned int uiInfoAddr; // 信息体地址
unsigned char ucStatus; // 状态
unsigned char ucStateType; // 单点/双点
__CP56Time2a stCTime; // 时标 (7字节 CP56 格式)
} SOE_Info;
// 遥控—库传递给应用层校验
typedef struct {
unsigned int uiInfoAddr; // 信息体地址
unsigned char ucStatus; // 命令值 (0=分/1=合)
unsigned char ucStateType; // 单点遥控/双点遥控
unsigned char ucYkType; // 选择/执行
} Yk_Info;
// 电度
typedef struct { unsigned int uiInfoAddr; float fVal; } Dd_Info;
// 带时标电度
typedef struct { unsigned int uiInfoAddr; float fVal; __CP56Time2a stCTime; } TimeDd_Info;
// 故障事件
typedef struct {
unsigned short usSoeNum;
SOE_Info staSoe[MAX_FAULT_EVENT_SOE_SIZE];
unsigned short usYcNum;
Yc_Info staYc[MAX_FAULT_EVENT_YC_SIZE];
} FAULT_EVENT_Info;
// 参数/定值
typedef struct {
unsigned char ucType; // TAG_TYPE_xxx
unsigned char ucLen; // 数据长度
union { ... } unVal; // 值 (支持多种类型)
unsigned int uiInfoAddr; // 信息体地址
} IECPARAM_T;
```
### 2.4 CP56Time2a 时间格式
```c
typedef struct {
unsigned char ucLMs; // 毫秒低位
unsigned char ucHMs; // 毫秒高位 (含 IV 标志位)
unsigned char ucMin; // 分钟 (含 IV 标志)
unsigned char ucHour; // 小时 (含 SU 夏令时标志)
unsigned char ucWeek; // 星期 (0=未用)
unsigned char ucMonth; // 月
unsigned char ucYear; // 年 (0-99)
} __CP56Time2a;
// 编解码工具函数
unsigned char CP56Time2a_Pack2Buf(unsigned char *out, const __CP56Time2a *cp56);
unsigned char CP56Time2a_GetFromBuf(const unsigned char *in, __CP56Time2a *cp56);
```
### 2.5 帧格式定义
```c
// 104 帧
typedef struct {
unsigned char byStartCode; // 0x68
unsigned char byAPDULen; // APDU 长度
unsigned char byControl1..4; // 控制域 (4字节)
unsigned char byASDU[249]; // ASDU 数据
} VIec104Frame;
// 101 帧—联合体:短帧(单字节地址/双字节地址) / 长帧
typedef union {
VFrame10 stFrame10; // 双字节地址短帧
VFrame10_S stFrame10_S; // 单字节地址短帧
VFrame68 stFrame68; // 长帧
} VIec101Frame;
```
---
## 3. API 体系
### 3.1 生命周期 API必须调用
| 函数 | 说明 |
|------|------|
| **`CS10x_101Init(pstSelf, cbSend, arg, CS101_TYPE_S/M)`** | ★ 101 从站初始化 |
| **`CS10x_104Init(pstSelf, cbSend, arg, CS104_TYPE_S/M)`** | ★ 104 从站初始化 |
| **`CS10x_DoRecv(pstSelf, pBuf, usLen)`** | ★ 接收数据入口 (每收到一包调用一次) |
| **`CS10x_TimerHandle(pstSelf, usGap)`** | ★ 定时器驱动入口 (每 usGap ms 调用一次) |
> `cbSend` 类型: `int (*)(unsigned char *buf, unsigned short len, void *arg)`
> 库通过此回调将编码好的报文交给应用层发送。
### 3.2 参数配置 API初始化后、运行前调用
```c
// ★ 核心 ASDU 参数
void CS10x_SetAppParameters(CS10xHandle_t pstSelf, CS10x_AppLayerParameters *param);
// → 链路地址字节数(1-2)、传输原因字节数(1-2)、公共地址字节数(1-2)、
// 信息体地址字节数(2-3)、链路地址、公共地址
// ★ 104 APCI 参数
void CS10x_SetAPCIParameters(CS10xHandle_t pstSelf, CS104_APCIParameters *param);
// → K(0-600)、W(1-60)、T0/T1/T2/T3(2-120s)
// 数据类型
void CS10x_SetYcType(pstSelf, usType); // 0=整形 1=标度化 2=浮点
void CS10x_SetYxType(pstSelf, usType); // 0=单点 1=双点
void CS10x_SetDdType(pstSelf, usType); // 0=不带时标 1=带时标
// 时间间隔 (ms)
void CS10x_SetSummonGap(pstSelf, uiGap); // 总召间隔 (默认300s)
void CS10x_SetESummonGap(pstSelf, uiGap); // 电度总召间隔 (默认60s)
void CS10x_SetHeartTimeGap(pstSelf, uiGap); // 心跳间隔 (默认90s)
void CS10x_FrameGap(pstSelf, uiGap); // 发送帧间隔 (默认100ms)
// 101 特殊
void CS10x_ResendGap(pstSelf, uiGap); // 重发超时 (默认5s)
void CS10x_ResendCnt(pstSelf, ucCnt); // 重发次数 (默认5)
void CS10x_SetInitType(pstSelf, bType); // 简单初始化投退 (1=投入)
void CS10x_SetFileAckType(pstSelf, ucType); // 文件需确认帧 (0=需要, 1=不需要)
// 功能使能
void CS10x_SetMultFrameEn(pstSelf, ucEn); // 104 多帧发送 (0=关闭 1=开启)
void CS10x_FaultEventEn(pstSelf, ucEn); // 故障事件 (0=关, 1/2/3=开)
void CS10x_SetSendSoe(pstSelf, ucEn); // SOE 发送 (0/1)
void CS10x_SetSendCos(pstSelf, ucEn); // COS 发送 (0/1)
void CS10x_SetYKEendFrame(pstSelf, ucEn); // 遥控结束帧 (0/1)
void CS10x_SetReadFileVSQ(pstSelf, ucEn); // 文件传输VSQ状态值
void CS10x_SetPresetContType(pstSelf, ucEn); // 参数预置忽略后续帧 (淮北主站=1)
```
### 3.3 回调注册 API按业务域分组
> **约定**: 以 `CS10x_S_` 为前缀的新接口是**推荐使用**的从站回调注册 API。旧的无前缀接口如 `CS10x_GetYcValueByGroupHandler`)已标记为"待删除"。
```c
// ===== 遥测 (YC) =====
void CS10x_S_GetYcByGroupHandler(pstSelf,
CS10x_GetYcCountByGroup cbGetYcCount, // → 返回该组遥测数量
CS10x_GetYcValueByGroup cbGetYcValue); // → 填充特定遥测值 (Yc_Info*)
// ===== 遥信 (YX) =====
void CS10x_S_GetYxByGroupHandler(pstSelf,
CS10x_GetYxCountByGroup cbGetYxCount, // → 返回该组遥信数量
CS10x_GetYxStatusByGroup cbGetYxStatus); // → 填充特定遥信状态 (Yx_Info*)
// ===== SOE (事件顺序记录) =====
void CS10x_S_SOEHandler(pstSelf,
CS10x_GetSOE cbGetSOE, // → 获取SOE数据
CS10x_GET_SOE_SendNum cbSendNum, // → 返回待发送SOE条数
CS10x_Updata_SOE_Pout cbUpdataSOEPout); // → 更新已发送指针
// ===== 遥控 (YK) =====
void CS10x_S_YkVerifyHandler(pstSelf,
CS10x_YkVerify cbYkVerify); // → 校验遥控命令合法性
// ===== 电度 (DD) =====
void CS10x_S_GetDdByGroupHandler(pstSelf,
CS10x_GetDdCountByGroup cbGetDdCount,
CS10x_GetDdValueByGroup cbGetDdValue);
// ===== 扰动遥测 (Disturb YC) =====
void CS10x_S_DiturbYcHandler(pstSelf,
CS10x_GetDiturbYc cbGetDiturbYc,
CS10x_GET_DiturbYc_SendNum cbSendNum,
CS10x_Updata_DiturbYc_Pout cbUpdataPout);
// ===== 时标电度 =====
void CS10x_S_DiturbDduHandler(pstSelf,
CS10x_GetTimeDdu cbGetTimeDdu,
CS10x_GetTimeDdu_SendNum cbSendNum,
CS10x_Updata_TimeDdu_Pout cbUpdataPout);
// ===== 故障事件 =====
void CS10x_S_FaultEventHandler(pstSelf,
CS10x_GET_Event_SendNum cbSendNum,
CS10x_GetFaultEvent cbGetFaultEvent,
CS10x_Updata_FaultEvent_Pout cbUpdataPout);
// ===== 参数/定值 =====
void CS10x_S_ParamAreaHandler(pstSelf,
CS10x_GetParamArea cbGetParamArea, // 读取当前定值区号
CS10x_SetParamArea cbSetParamArea); // 切换定值区
void CS10x_S_IECParamHandler(pstSelf,
CS10x_AddIECParam cbAddIECParam, // 添加参数项
CS10x_RunReadIECParam cbReadIECParam, // 读参数执行
CS10x_RunWriteIECParam cbWriteIECParam); // 写参数执行
void CS10x_S_GetParamInfoHandler(pstSelf,
CS10x_GetParamCount cbGetParamCount, // 参数总数
CS10x_GetParamInfoByIndx cbGetParamInfo); // 按索引获取参数信息
// ===== 时间 (对时) =====
void CS10x_TimeRWHandler(pstSelf,
CS10x_GetTime cbGetTime, // 获取当前时间 (填充 __CP56Time2a)
CS10x_SetTime cbSetTime); // → 设置时间 (解析 __CP56Time2a)
// ===== 文件服务 =====
void CS10x_S_FileReadOprtHandler(pstSelf, cbGetPro, cbGetNum, cbRead, pvParam);
void CS10x_S_FileWriteOprtHandler(pstSelf, cbWrite, cbCheck, cbFinish, pvParam);
void CS10x_S_UpdateProcHandler(pstSelf, cbUpdateProc); // 软件升级
```
### 3.4 主动发送 API应用层调用库提供
```c
// 任务类
unsigned char CS10x_SendTask(pstSelf); // 调度所有待发送任务
// 遥控主动下发(主站模式使用)
void CS10x_SetYkTask(pstSelf, usInfoAddr, ucSE, ucCmd, ucDcs, ucQU, bEncrypt);
```
### 3.5 公共工具函数
```c
// TaskFlag 操作
void SetTaskFlag(VFLAGS *flags, unsigned int flagNo);
unsigned int GetTaskFlag(VFLAGS *flags, unsigned int flagNo);
void ClearTaskFlag(VFLAGS *flags, unsigned int flagNo);
// 字节序编解码
unsigned char IntBytes_Pack2Buf(unsigned char *out, unsigned int val, unsigned char size);
unsigned int IntBytes_GetFromBuf(const unsigned char *in, unsigned char size);
unsigned char Float_Pack2Buf(unsigned char *out, float val);
float Float_GetFromBuf(const unsigned char *in);
unsigned char U16_Pack2Buf(unsigned char *out, unsigned short val);
unsigned short U16_GetFromBuf(const unsigned char *in);
// 校验
unsigned char checksum_8(unsigned char *buf, unsigned char len);
```
## 4. 典型使用流程
### 4.1 IEC 104 从站完整流程
```c
// ============ 步骤 1: 创建规约实例并初始化 ============
CS10x cs10x;
memset(&cs10x, 0, sizeof(CS10x));
CS10x_104Init(&cs10x, MySendCallback, myAppData, CS104_TYPE_S);
// ============ 步骤 2: 配置 ASDU 参数 ============
CS10x_AppLayerParameters appParam = {
.iSzOfLinkAddr = 0, // 104 无链路地址
.iSzOfTypeId = 1,
.iSzOfVSQ = 1,
.iSzOfCOT = 2, // 传输原因 2 字节
.iSzOfCA = 2, // 公共地址 2 字节
.iSzOfIOA = 3, // 信息体地址 3 字节
.iLinkAddr = 0,
.iPublicAddr = 1,
.iMaxSzOfAsduLen = 249
};
CS10x_SetAppParameters(&cs10x, &appParam);
// ============ 步骤 3: 配置 104 APCI 参数 ============
CS104_APCIParameters apciParam = {
.iK = 12,
.iW = 8,
.iT0 = 10, // 连接建立超时 10s
.iT1 = 12, // 发送超时 12s
.iT2 = 8, // 确认超时 8s
.iT3 = 15 // 空闲测试超时 15s
};
CS10x_SetAPCIParameters(&cs10x, &apciParam);
// ============ 步骤 4: 配置数据类型 ============
CS10x_SetYcType(&cs10x, 2); // 浮点遥测
CS10x_SetYxType(&cs10x, 0); // 单点遥信
CS10x_SetDdType(&cs10x, 1); // 带时标电度
CS10x_FrameGap(&cs10x, 10); // 10ms 帧间隔
CS10x_SetSummonGap(&cs10x, 300000); // 300s 总召
// ============ 步骤 5: 注册回调 ============
CS10x_S_GetYcByGroupHandler(&cs10x, MyGetYcCount, MyGetYcValue);
CS10x_S_GetYxByGroupHandler(&cs10x, MyGetYxCount, MyGetYxStatus);
CS10x_S_SOEHandler(&cs10x, MyGetSOE, MySOESendNum, MyUpdateSOEPout);
CS10x_S_YkVerifyHandler(&cs10x, MyYkVerify);
CS10x_TimeRWHandler(&cs10x, MyGetTime, MySetTime);
// ============ 步骤 6: 主循环驱动 ============
while(running) {
// 接收数据
int len = recv(sock, buf, sizeof(buf), 0);
if(len > 0) {
CS10x_DoRecv(&cs10x, buf, len); // ★ 喂给规约栈
}
// 定时器驱动 (每 10ms 调用一次)
CS10x_TimerHandle(&cs10x, 10); // ★ 驱动所有定时任务
}
```
### 4.2 回调函数签名参考
```c
// 发送回调 → 库将编码好的帧传给此函数
int MySendCallback(unsigned char *buf, unsigned short len, void *arg) {
return send(mySock, buf, len, 0);
}
// 获取遥测数量 (uiParam=组号)
int MyGetYcCount(void *param, unsigned int uiParam) {
return (uiParam == 1) ? g_ycCount : 0;
}
// 获取特定遥测值 (usPos=组内偏移)
int MyGetYcValue(Yc_Info *info, void *param, unsigned int usPos) {
info->uiInfoAddr = g_ycTable[usPos].addr; // 信息体地址
info->fVal = g_ycTable[usPos].value; // 遥测值
info->ucQds = 0x00; // 品质: 有效
return 0;
}
// 获取时间 (对时使用)
int MyGetTime(__CP56Time2a *pTime) {
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
struct tm *t = gmtime(&ts.tv_sec);
pTime->ucYear = t->tm_year - 100;
pTime->ucMonth = t->tm_mon + 1;
pTime->ucDay = t->tm_mday;
pTime->ucHour = t->tm_hour;
pTime->ucMin = t->tm_min;
int ms = ts.tv_nsec / 1000000 + t->tm_sec % 60 * 1000;
pTime->ucLMs = ms & 0xFF;
pTime->ucHMs = (ms >> 8) & 0xFF;
return 0;
}
// 遥控校验
int MyYkVerify(Yk_Info *info, void *param) {
// 校验成功后执行遥控,失败返回 -1
return executeYk(info->uiInfoAddr, info->ucStatus, info->ucYkType) ? 0 : -1;
}
```
## 5. ASDU 类型标识码映射表
### 5.1 监视方向(终端 → 主站)
| TypeID | 宏 | 说明 | 品质描述词 |
|--------|-----|------|------------|
| 1 | `M_SP_NA` | 单点信息 | SIQ |
| 3 | `M_DP_NA` | 双点信息 | DIQ |
| 5 | `M_ST_NA` | 步位置信息 | VTI |
| 7 | `M_BO_NA` | 32位位串 | QDS |
| 9 | `M_ME_NA` | 测量值(归一化) | QDS |
| 11 | `M_ME_NB` | 测量值(标度化) | QDS |
| 13 | `M_ME_NC` | 测量值(短浮点) | QDS |
| 15 | `M_IT_NA` | 电能脉冲计数量 | - |
| 20 | `M_PS_NA` | 带状态变位检出的成组单点 | SIQ |
| 21 | `M_ME_ND` | 不带品质的测量值 | - |
| 30 | `M_SP_TB` | 带长时标单点 | SIQ |
| 31 | `M_DP_TB` | 带长时标双点 | DIQ |
| 34 | `M_ME_TD` | 带长时标测量值 | QDS |
| 42 | `M_FT_NA` | 故障事件 | QDP |
| 70 | `M_EI_NA` | 初始化结束 | COI |
| 206 | `M_IT_NB` | 不带时标累计量(浮点) | - |
| 207 | `M_IT_TC` | 带时标累计量(浮点) | - |
### 5.2 控制方向(主站 → 终端)
| TypeID | 宏 | 说明 |
|--------|-----|------|
| 45 | `C_SC_NA` | 单点遥控命令 |
| 46 | `C_DC_NA` | 双点遥控命令 |
| 47 | `C_RC_NA` | 升降命令 |
| 48 | `C_SE_NA` | 设定命令 |
| 55 | `C_SP_NA` | 参数预置(南网) |
| 100 | `C_IC_NA` | 总召唤命令 |
| 101 | `C_CI_NA` | 电度召唤命令 |
| 102 | `C_RD_NA` | 读数据命令 |
| 103 | `C_CS_NA` | 时钟同步命令 |
| 104 | `C_TS_NA` | 测试命令 |
| 105 | `C_RP_NA` | 复位进程命令 |
| 106 | `C_CD_NA` | 延时获得命令(南网) |
| 107 | `C_TS_TA` | 带时标测试命令 |
| 108 | `C_RS_NA_NANWANG` | 读参数命令(南网) |
| 112 | `P_ME_NC` | 参数预置 |
| 113 | `P_AC_NA` | 参数固化 |
| 200 | `C_SR_NA` | 切换定值区 |
| 201 | `C_RR_NA` | 读定值区号 |
| 202 | `C_RS_NA` | 读参数和定值 |
| 203 | `C_WS_NA` | 写参数和定值 |
| 210 | `F_FR_NA` | 文件升级 |
| 211 | `F_SR_NA` | 软件升级 |
## 6. 传送原因 (COT) 速查
| COT | 宏 | 说明 |
|------|-----|------|
| 1 | `COT_PERCYC` | 周期/循环 |
| 2 | `COT_BACK` | 背景扫描 |
| 3 | `COT_SPONT` | 突发(变位) |
| 4 | `COT_INIT` | 初始化 |
| 5 | `COT_REQ` | 请求或被请求 |
| 6 | `COT_ACT` | 激活 |
| 7 | `COT_ACTCON` | 激活确认 |
| 8 | `COT_DEACT` | 停止激活 |
| 9 | `COT_DEACTCON` | 停止激活确认 |
| 10 | `COT_ACTTERM` | 激活结束 |
| 20 | `COT_INTROGEN` | 响应总召唤 |
| 37 | `COT_REQCOGCN` | 响应计数量总召唤 |
| 44 | `COT_E_TYPE` | 未知类型标识 |
| 45 | `COT_E_REASON` | 未知传送原因 |
| 46 | `COT_E_CADDR` | 未知公共地址 |
| 47 | `COT_E_IADDR` | 未知信息体地址 |
## 7. 定时器与任务调度
### 7.1 104 定时器
| 定时器 | 默认值 | 说明 |
|--------|--------|------|
| T0 | 10s | 连接建立超时—发送 STARTDT 后等待确认 |
| T1 | 12s | 发送 I 帧后等待确认超时 |
| T2 | 8s | 接收 I 帧后延迟确认 ACK |
| T3 | 15s | 空闲测试超时—发送 TESTFR |
**定时器处理流程**:
1. `CS10x_TimerHandle()` 被调用 → 遍历 stM_vTimer[4]
2. T0 超时 → 重试连接 / T1 超时 → 断开链路 / T3 超时 → 发送测试帧
### 7.2 任务标志系统
库内部使用 128 位位图 (`VFLAGS`) 管理所有待处理任务:
| 任务 ID | 枚举值 | 说明 |
|---------|--------|------|
| `TSKID_SummonYx` | - | 总招遥信响应 |
| `TSKID_SummonYc` | - | 总招遥测响应 |
| `TSKID_SendCOS` | - | 发送变位遥信 |
| `TSKID_SendSOE` | - | 发送 SOE |
| `TSKID_TimeSyn` | - | 时钟同步 |
| `TSKID_HeartBeat` | - | 心跳 |
| `TSKID_DdSummon` | - | 总招电能量 |
| `TSKID_FaultSoe` | - | 故障事件 |
| `TSKID_YcDisturb` | - | 扰动数据 |
| `TSKID_ChgConst` | - | 修改定值区 |
| `TSKID_ReadConst` | - | 读取定值区 |
**任务调度流程**: `CS10x_TimerHandle` → 累计各定时计数 → 超时时 `SetSendTask` 置位 → `CS10x_SendTask` → 遍历任务标志 → 调用对应的 `Do_*` 函数。
## 8. 品质描述词 (Quality Descriptor)
库提供了 5 种品质描述词的位域定义,封装在 `ASDU_CMD_T` union 中:
| 类型 | 字段 | 位 | 说明 |
|------|------|-----|------|
| **SIQ** (单点) | SPI | 0 | 0=开, 1=合 |
| | BL | 4 | 0=未锁定, 1=锁定 |
| | SB | 5 | 0=未取代, 1=取代 |
| | NT | 6 | 0=当前值, 1=非当前 |
| | IV | 7 | 0=有效, 1=无效 |
| **DIQ** (双点) | DPI | 0-1 | 0/3=不确定, 1=开, 2=合 |
| **QDS** (测量值) | OV | 0 | 0=未溢出, 1=溢出 |
| **QDP** (保护事件) | EI | 4 | 0=动作时间有效, 1=无效 |
| **SCO** (单命令) | SCS | 0 | 0=开, 1=合 |
| | QU | 1-5 | 0=无定义, 1=短脉冲, 2=长脉冲, 3=持续 |
| | SE | 6 | 0=执行, 1=选择 |
| **DCO** (双命令) | DCS | 0-1 | 1=开, 2=合 |
| | SE | 6 | 0=执行, 1=选择 |
| **QOS** (设定命令) | QL | 0-6 | 0=缺省 |
| | SE | 6 | 0=执行, 1=选择 |
| **COI** (初始化) | UI7 | 0-6 | 0=电源合上, 1=手动复位, 2=远方复位 |
## 9. 101 与 104 的关键差异
| 特性 | IEC 101 | IEC 104 |
|------|---------|---------|
| **传输层** | 串口 (异步字节流) | TCP/IP 网络 |
| **链路层帧** | `0x10` 短帧 / `0x68` 长帧 | APCI 6字节头 (I/S/U帧) |
| **地址** | 链路地址 (1-2 byte) | 无链路地址 |
| **帧同步** | 起始+结束字符 (0x68/0x10+0x16) | APDU 长度字段 |
| **重传机制** | FCB/FCV + 超时重发 | 发送序号 + 接收序号 + T1 超时 |
| **链路管理** | REQUEST_LINK / RESET_LINK 帧 | STARTDT/STOPDT/TESTFR U帧 |
| **初始化** | 复位链路 → 简单/完整初始化 (可选) | TCP 连接 → STARTDT 激活 |
| **初始化函数** | `CS10x_101Init(..., CS101_TYPE_S)` | `CS10x_104Init(..., CS104_TYPE_S)` |
| **缓存** | 固定 255 字节帧 | APCI 6 + ASDU max 249 |
| **多帧** | 101 连帧 (FCB 位机制) | 104 I 帧序号连续发送 |
## 10. 文件服务与升级流程
库内建了基于 IEC 60870-5 文件传输服务的文件操作能力:
| 操作 | TypeID / 说明 |
|------|---------------|
| 目录召唤 | 读目录 → 返回文件列表 |
| 读文件 | 激活 → 分段传输数据 → 传输确认 |
| 写文件 | 激活 → 分段写数据 → 写确认 |
| 软件升级 | 211 `F_SR_NA``CS10x_S_UpdateProcHandler` |
**注意**: 旧文件服务接口 (`CS10x_FileReadOprtHandler` 不带 `S_` 前缀) 在库中标记为"待删除",应使用 `CS10x_S_FileReadOprtHandler` / `CS10x_S_FileWriteOprtHandler`
## 11. 注意事项与踩坑指南
### 11.1 类型兼容
- CBB 库使用 `u8/u16/u32/s16/s32/f32/BOOL/TRUE/FALSE` 自定义类型
- **不能修改库文件** — 通过 `release/inc/cbb_compat.h` + gcc `-include` 注入解决
- 本项目已在 [makefile](../../release/src/protocol/lib60870/makefile) 中配置
### 11.2 回调约定
- 所有回调返回 `0` = 成功,非 `0` = 失败
- `cbSend` 回调中不要阻塞
- 库内部是**同步调用**`CS10x_DoRecv` 内部会立即调用回调获取数据
### 11.3 线程安全
- 库本身**不是线程安全**的,所有 API 调用建议在同一线程
- 典型架构:通信线程收数据 → CS10x_DoRecv → 定时器线程驱动 CS10x_TimerHandle
### 11.4 多实例支持
- 每个通信链路创建一个 `CS10x` 实例
- 调用方通过 `void *arg` (初始化参数) 和 `void *pParam` (回调参数) 传递业务上下文
### 11.5 从站 vs 主站
- **此项目 RTU 作为从站** (`_S` 类型) — 回调负责**提供数据**
- 如果作为主站 (`_M` 类型) — 回调负责**处理收到的数据**
### 11.6 101 序列号与链路状态
- FCB 位每发送一帧翻转
- 重发帧 FCB 位不变
- 链路重置时 FCB 复位
### 11.7 104 序号机制
- 发送序号 `usSendNum` 与接收序号 `usRecvNum` 各自独立递增
-`usSendNum - usAckRecvNum >= K` 时停止发送 (流量控制)
- T1 超时断开链路,清初始化完成标识
## 12. 版本关键变更摘要
| 版本 | 日期 | 关键修改 |
|------|------|----------|
| V2.01.059 | 2026-02-02 | 增加 YC 上传品质描述词字段 |
| V2.01.058 | 2026-01-31 | 增加确认帧通知回调接口(透传给下行设备) |
| V2.01.057 | 2025-12-25 | T0-T3 最大范围 120→200 |
| V2.01.056 | 2025-12-09 | 添加关闭链路接口 |
| V2.01.055 | 2025-10-30 | 优化序号匹配,>=K 时继续闭锁 |
| V2.01.054 | 2025-10-27 | 修复总召不上送、序号不匹配问题 |
| V2.01.044 | 2025-09-03 | 参数预置忽略后续帧 + 修复 T1/T3 超时 |
| V2.01.043 | 2025-08-11 | 添加普洱/贵州参数读写流程 |
| V2.01.042 | 2025-07-17 | 南网规约参数读写和延时获得命令 |
| V2.01.039 | 2025-04-21 | 101 错误帧继续解析后续报文 |
| V2.01.038 | 2025-03-19 | 101 回复长帧 FCV 默认置 1 |