# 01 - 总召与遥测遥信回调详解 > 源码: [DCU_HOST/applications/data/iec_itfc/YcItfc.c](../../../../../../DCU_HOST/applications/data/iec_itfc/YcItfc.c) > [DCU_HOST/applications/data/iec_itfc/YxItfc.c](../../../../../../DCU_HOST/applications/data/iec_itfc/YxItfc.c) --- ## 1. 总召触发与延迟机制 DCU_HOST 有一个特殊设计:**主站发起总召 → 终端需要先通过内部总线向采集板下发总召命令 → 等待采集板返回数据 → 再上报给主站**。 ```c 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 注册 ```c CS10x_S_GetYcByGroupHandler(pstCs10x, GetYcCountByGroup, GetYcValueByGroup); ``` ### 2.2 GetYcCountByGroup — 获取遥测数量 ```c 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 — 填充单条遥测值 ```c 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` 获取: ```c // 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 注册 ```c CS10x_S_GetYxByGroupHandler(pstCs10x, GetYxCountByGroup, GetYxStatusByGroup); ``` ### 3.2 GetYxCountByGroup — 获取遥信数量 ```c 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 — 填充单条遥信状态 ```c 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. 扰动遥测回调 ```c CS10x_S_DiturbYcHandler(pstCs10x, GetDiturbYc, GetDiturbYcSendNum, UpdateDiturbYcPtrOut); ``` 扰动遥测用于故障录波期间上送连续遥测数据,与 SOE 类似使用队列 + 指针模式: ```c 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. 总召完整时序 ```mermaid 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) ```