8.2 KiB
8.2 KiB
01 - 总召与遥测遥信回调详解
源码: DCU_HOST/applications/data/iec_itfc/YcItfc.c DCU_HOST/applications/data/iec_itfc/YxItfc.c
1. 总召触发与延迟机制
DCU_HOST 有一个特殊设计:主站发起总召 → 终端需要先通过内部总线向采集板下发总召命令 → 等待采集板返回数据 → 再上报给主站。
unsigned int GetSummonSlaveDelay(void *pParam) {
const uint32_t uiDelayUpMs = 5000; // 延迟 5 秒上传
uint8_t cmd[2] = {1, 0};
CPC_DataSend(NODE_BROADCAST, CPC_SUMMON_FRAME, cmd, 2); // 下发总招广播
return uiDelayUpMs;
}
// 注册
CS10x_GetSummonSlaveDelayHandler(pstCs10x, GetSummonSlaveDelay);
关键理解: 此回调不是 CBB 库标准接口,是 DCU_HOST 扩展——主站召唤命令到达后,先广播给所有采集板,延迟 5s 后再开始组包上报。如果 RTU 的数据是直接可取的(如从 datacenter 读取),则不需要此延迟机制,直接返回 0 即可。
2. 遥测回调 CS10x_S_GetYcByGroupHandler
2.1 注册
CS10x_S_GetYcByGroupHandler(pstCs10x, GetYcCountByGroup, GetYcValueByGroup);
2.2 GetYcCountByGroup — 获取遥测数量
s32 GetYcCountByGroup(u16 usGroupID, void *pvParam, u32 *puiStartInf, u32 *puiEndInf) {
stPOT_CONFIG_NUM stPotConfigNum;
GetPotTableConfig(&stPotConfigNum); // 从配置表获取遥测配置数量
*puiStartInf = YC_START_PORT; // 起始信息体地址 (如 0x4001)
*puiEndInf = YC_START_PORT + stPotConfigNum.usYcConfigNum; // 结束信息体地址
return stPotConfigNum.usYcConfigNum; // 返回配置的遥测总数
}
关键理解:
usGroupID参数在此实现中未使用,所有遥测数据视为同一组返回puiStartInf/puiEndInf必须设置,规约库用它们来确定信息体地址范围- 返回
0表示该组无数据
2.3 GetYcValueByGroup — 填充单条遥测值
s32 GetYcValueByGroup(u16 usGroupID, u16 usIdx, Yc_Info *pstInfo, void *pvParam) {
u16 usInfoAddr = usIdx + YC_START_PORT; // 信息体地址 = 偏移 + 起始地址
u16 usTabIdx;
GetPosYcTable(&usTabIdx, usInfoAddr); // 通过信息体地址查找点表索引
if(usTabIdx >= g_stYcMaxNum.usYcTotal) {
pstInfo->fVal = 0; // 未配置 → 填0
} else {
UserGetYcValue(&pstInfo->fVal, usTabIdx, 1); // 从数据池获取遥测原始值
GetYcTableInform(&stYcTable, usTabIdx); // 获取变比
pstInfo->fVal = pstInfo->fVal * stYcTable.fScale; // ★ 乘以变比
}
pstInfo->uiInfoAddr = usInfoAddr; // ★ 必须填信息体地址
return 1; // 成功
}
关键理解:
usIdx从 0 开始递增,规约库会循环调用直到填满一帧 (48 个浮点遥测/帧)uiInfoAddr必须正确填写,与配置表中的点号对应- 品质描述词
ucQds在此未显式设置(默认 0 = 有效),如需标记无效数据应设IV位 - 变比乘法
fVal * fScale是电力行业常见处理
2.4 RTU 适配要点
RTU 项目中,遥测数据应从 libdatacenter 获取:
// RTU 版 GetYcValueByGroup 参考实现
s32 GetYcValueByGroup(u16 usGroupID, u16 usIdx, Yc_Info *pstInfo, void *pvParam) {
stru_iec_60870 *p_iec = iec60870_get_by_interface(*(uint32_t*)pvParam);
if(!p_iec) return 0;
// 从 datacenter 的 signal_out 表按索引获取信号
dc_signal *sig = dc_get_signal_by_index(SIGNAL_OUT, usIdx);
if(!sig) { pstInfo->fVal = 0; pstInfo->uiInfoAddr = usIdx + 0x4001; return 1; }
pstInfo->fVal = dc_get_signal_float(sig);
pstInfo->uiInfoAddr = sig->info_addr;
pstInfo->ucQds = 0x00; // 有效
return 1;
}
3. 遥信回调 CS10x_S_GetYxByGroupHandler
3.1 注册
CS10x_S_GetYxByGroupHandler(pstCs10x, GetYxCountByGroup, GetYxStatusByGroup);
3.2 GetYxCountByGroup — 获取遥信数量
s32 GetYxCountByGroup(u16 usGroupID, void *pvParam, u32 *puiStartInf, u32 *puiEndInf) {
stPOT_CONFIG_NUM stPotConfigNum;
GetPotTableConfig(&stPotConfigNum);
*puiStartInf = YX_START_PORT;
*puiEndInf = *puiStartInf + stPotConfigNum.usYxConfigNum;
return stPotConfigNum.usYxConfigNum; // 最多 127 个单点遥信/帧
}
3.3 GetYxStatusByGroup — 填充单条遥信状态
s32 GetYxStatusByGroup(u16 usGroupID, u16 usIdx, Yx_Info *pstInfo, void *pvParam) {
u16 usInfoAddr = usIdx + YX_START_PORT;
u16 usTabIdx;
GetPosYxTable(&usTabIdx, usInfoAddr);
if(UserGetYxState(&pstInfo->ucStatus, usTabIdx, 1)) {
pstInfo->uiInfoAddr = usInfoAddr;
pstInfo->ucStateType = 0; // 默认单点
}
stLIB60870ENTITY *pstEntity = GetEntityNum(*(u32*)pvParam);
// ★ 关键:根据 CS10x.usYxType 决定单点/双点处理
if(pstEntity->stCs10x.usYxType) { // 双点遥信
pstInfo->ucStateType = 1; // 双点标记
if(stYxTable.ucAttr) { // 取反属性
if(pstInfo->ucStatus == 2) pstInfo->ucStatus = 1;
else if(pstInfo->ucStatus == 1) pstInfo->ucStatus = 2;
}
} else { // 单点遥信
pstInfo->ucStateType = 0;
if(stYxTable.ucAttr) { // 取反属性
pstInfo->ucStatus = pstInfo->ucStatus ? 0 : 1;
}
}
return 1;
}
双点遥信值含义:
| ucStatus | 含义 |
|---|---|
| 0 | 不确定/中间状态 |
| 1 | 确定状态:开 (OFF) |
| 2 | 确定状态:合 (ON) |
| 3 | 不确定 |
双点遥信时,单点状态需要 +1 转换 (0→1=开, 1→2=合)。
3.4 COS (变化遥信) 处理
COS 由库内部自动管理。当应用层通过 CS10x_AddCOS 或 SOE 队列产生变位后,库自动在定时调度中加上 TSKID_SendCOS 任务。
4. 扰动遥测回调
CS10x_S_DiturbYcHandler(pstCs10x, GetDiturbYc, GetDiturbYcSendNum, UpdateDiturbYcPtrOut);
扰动遥测用于故障录波期间上送连续遥测数据,与 SOE 类似使用队列 + 指针模式:
s32 GetDiturbYcSendNum(void *pvArg) → 返回待发送扰动遥测条数
s32 GetDiturbYc(Yc_Info *pstInfo, void *pvParam, u32 uiPos) → 填充单条扰动遥测
s32 UpdateDiturbYcPtrOut(void *pvParam, u32 uiPos) → 主站确认后更新已发送指针
5. 数据类型配置速查
| CS10x_SetYcType | TypeID | 帧内格式 | 说明 |
|---|---|---|---|
| 0 | M_ME_NA (9) | 归一化值 (int16) | 整形遥测 |
| 1 | M_ME_NB (11) | 标度化值 (int16 + 标度) | 标度化 |
| 2 | M_ME_NC (13) | 短浮点 (float32) | 浮点遥测 — 99% 场合使用 |
| CS10x_SetYxType | TypeID | 帧内格式 | 说明 |
|---|---|---|---|
| 0 | M_SP_NA (1) | 1 bit (SIQ) | 单点遥信 |
| 1 | M_DP_NA (3) | 2 bit (DIQ) | 双点遥信 |
6. 数据源适配对比
| 项目 | 数据获取方式 | 变比处理 |
|---|---|---|
| DCU_HOST | UserGetYcValue() → 内部数据池 |
回调中 fVal * fScale |
| RTU (建议) | dc_get_signal_float() → datacenter |
存信号时已乘好 / 或此处乘 |
7. 总召完整时序
sequenceDiagram
participant Master as 主站
participant CBB as lib60870 CBB库
participant App as 应用层(iec_prtcl)
participant Data as 数据层(YcItfc/YxItfc)
Master->>CBB: C_IC_NA (总召激活, COT=6)
CBB->>App: GetSummonSlaveDelay() → 5000ms
Note over App: 向采集板下发总召广播 CPC_SUMMON_FRAME
Note over App,Data: 等待 5s...
loop 每帧最多48个遥测
CBB->>App: GetYcCountByGroup() → 遥测总数
CBB->>Data: GetYcValueByGroup(usIdx=0...N)
Data-->>CBB: 返回 Yc_Info (fVal + uiInfoAddr)
CBB->>Master: M_ME_NC 帧 (COT=20 响应总召)
end
loop 每帧最多127个遥信
CBB->>App: GetYxCountByGroup() → 遥信总数
CBB->>Data: GetYxStatusByGroup(usIdx=0...N)
Data-->>CBB: 返回 Yx_Info (ucStatus + ucStateType)
CBB->>Master: M_SP_NA 帧 (COT=20 响应总召)
end
CBB->>Master: C_IC_NA (总召结束, COT=10)