243 lines
8.2 KiB
Markdown
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)
|
|
```
|