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