RTU/mimo/工程/DCU_HOST/01-总召与遥测遥信.md

243 lines
8.2 KiB
Markdown

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