27 KiB
lib60870 CBB 库详细解析
库版本: IECVer2.01.059_260202 (2026-02-02) 厂家: 科大智能电气有限公司 (2013-2026) 用途: IEC 60870-5-101 / 60870-5-104 / 60870-5-103 电力远动规约协议栈 源码路径: src/protocol/lib60870/ 编译兼容头: 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 头文件依赖关系
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
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 行,源码)
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 信息体数据结构
// 遥信—库发出给应用层填充数据
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 时间格式
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 帧格式定义
// 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(初始化后、运行前调用)
// ★ 核心 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)已标记为"待删除"。
// ===== 遥测 (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(应用层调用,库提供)
// 任务类
unsigned char CS10x_SendTask(pstSelf); // 调度所有待发送任务
// 遥控主动下发(主站模式使用)
void CS10x_SetYkTask(pstSelf, usInfoAddr, ucSE, ucCmd, ucDcs, ucQU, bEncrypt);
3.5 公共工具函数
// 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 从站完整流程
// ============ 步骤 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 回调函数签名参考
// 发送回调 → 库将编码好的帧传给此函数
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 |
定时器处理流程:
CS10x_TimerHandle()被调用 → 遍历 stM_vTimer[4]- 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 中配置
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 |