7.1 KiB
7.1 KiB
03 - 遥控与对时回调详解
1. 遥控回调注册
// 校验回调
CS10x_S_YkVerifyHandler(pstCs10x, Cs10xYkVerifyItfs);
// 遥控信息处理
CS10x_SetYkInforHandler(pstCs10x, SetYkInfor);
1.1 Cs10xYkVerifyItfs — 遥控操作前置校验
uint8_t Cs10xYkVerifyItfs(void *pvParam) {
uint32_t uiItfs = *((uint32_t *)pvParam);
CS10x* pstCs10x = GetEntityCs10x(uiItfs);
// 1. 蓝牙不响应遥控
if((uiItfs & 0xff) == 5) return 1;
// 2. 非从站不校验
if(CS101_TYPE_S != pstCs10x->eCS10x_Type && CS104_TYPE_S != pstCs10x->eCS10x_Type)
return 1;
// 3. 加密校验 (可选)
if(*pstParam->stComCommunitParam.pucYkWithEncryEn == 1) {
// ... 检查通道是否已加密 ...
}
// 4. ★ IP 白名单校验 (仅 104)
if(CS104_TYPE_S == pstCs10x->eCS10x_Type && *pstParam->stIec104Param.pucMasterYkEnable == 1) {
uiIp = GetRemoteIpByItfs(uiItfs);
if(uiIp != *pstParam->stIec104Param.puiMasterIp1
&& uiIp != *pstParam->stIec104Param.puiMasterIp2)
return 0; // IP 不在白名单
}
return 1; // 校验通过
}
返回值约定: 0 = 校验失败 (拒绝遥控), 1 = 校验通过。
1.2 SetYkInfor — 遥控命令分发
int SetYkInfor(Yk_Info *pstInfo, void *pvArg) {
uint32_t uiArg = *(uint32_t*)pvArg;
CS10x *pstSelf = GetEntityCs10x(uiArg);
// 1. ★ 遥控类型转换 (CBB库 → 业务层)
uint8_t ucYkOrder;
if(pstInfo->ucYkType == 0) ucYkOrder = YK_CMD_EXECUTE; // 执行
else if(pstInfo->ucYkType == 1) ucYkOrder = YK_CMD_SELECT; // 选择
else if(pstInfo->ucYkType == 2) ucYkOrder = YK_CMD_CANCEL; // 撤销
// 2. 通过信息体地址查找遥控点表索引
uint16_t usPos;
GetPosYkTableInform(&usPos, (uint16_t)pstInfo->uiInfoAddr);
// 3. ★ 双点遥控 → 单点转换
uint8_t ucState = pstInfo->ucStatus;
if(pstInfo->ucStateType == 1) ucState -= 1; // 双点 1/2 → 单点 0/1
// 4. 组装遥控命令,发送到遥控处理线程
stYK_CMD stCmd = {0};
stCmd.usOprtChannel = usPos;
stCmd.ucOprtType = ucYkOrder;
stCmd.ucOprtState = ucState;
stCmd.pfcNotify = YkResultNofify; // ★ 结果回调
stCmd.stIecInfo.usInfoAddr = pstInfo->uiInfoAddr;
stCmd.stIecInfo.ucStateType = pstInfo->ucStateType;
stCmd.stIecInfo.ucState = pstInfo->ucStatus;
SendYkToThread(&stCmd); // 异步处理
return 1;
}
2. 遥控结果通知 YkResultNofify
遥控执行后,DCU_HOST 通过 SetSendTask 主动触发 CBB 库发送确认帧:
void YkResultNofify(uint8_t ucResult, void *pvArg) {
stYK_CMD *pstCmd = (stYK_CMD *)pvArg;
CS10x *pstSelf = GetEntityCs10x(pstCmd->uiItfs);
switch(ucResult) {
case YK_EVT_SEL_OK: // 选择成功 → 激活确认
SetSendTask(pstSelf, TSKID_YkProcess, COT_ACTCON, ucStateType);
break;
case YK_EVT_SEL_ERR: // 选择失败 → 否定确认
SetSendTask(pstSelf, TSKID_YkProcess, COT_PN | COT_ACTCON, ucStateType);
break;
case YK_EVT_EXE_OK: // 执行成功
SetSendTask(pstSelf, TSKID_YkProcess, COT_ACTCON, ucStateType);
break;
case YK_EVT_EXE_ERR: // 执行失败
SetSendTask(pstSelf, TSKID_YkProcess, COT_PN | COT_ACTCON, ucStateType);
break;
case YK_EVT_TIMEOUT: // 超时 → 撤销确认 或 激活结束
SetSendTask(pstSelf, TSKID_YkProcess,
enableYkEndFrame ? COT_ACTTERM : COT_DEACTCON, ucStateType);
break;
case YK_EVT_CANCEL_OK: // 撤销成功
SetSendTask(pstSelf, TSKID_YkProcess, COT_DEACTCON, ucStateType);
break;
case YK_EVT_CANCEL_ERR: // 撤销失败
SetSendTask(pstSelf, TSKID_YkProcess, COT_PN | COT_DEACTCON, ucStateType);
break;
}
}
3. 遥控 SBO 时序
sequenceDiagram
participant Master as 主站
participant CBB as lib60870
participant App as 应用层
participant HW as 执行机构
Master->>CBB: C_SC_NA (Select, COT=6)
CBB->>App: Cs10xYkVerifyItfs()
App-->>CBB: 1 (允许)
CBB->>App: SetYkInfor(YK_CMD_SELECT)
App->>HW: 选择遥控对象
App->>CBB: YkResultNofify(YK_EVT_SEL_OK)
CBB->>Master: C_SC_NA (ActCon, COT=7)
Master->>CBB: C_SC_NA (Execute, COT=6)
CBB->>App: SetYkInfor(YK_CMD_EXECUTE)
App->>HW: 执行遥控动作
App->>CBB: YkResultNofify(YK_EVT_EXE_OK)
CBB->>Master: C_SC_NA (ActCon, COT=7)
opt 遥控结束帧使能
CBB->>Master: C_SC_NA (ActTerm, COT=10)
end
4. 对时回调 CS10x_TimeRWHandler
CS10x_TimeRWHandler(pstCs10x, GetTimeItfs, SetTimeItfs);
4.1 GetTimeItfs — 获取当前时间
// 参考实现: 从 RTC 获取时间并转为 CP56Time2a
s32 GetTimeItfs(__CP56Time2a *pstTime) {
struct tm stTm;
uint16_t usMs;
RTC_GetDateTime(&stTm, &usMs); // 从 RTC 获取
pstTime->ucYear = stTm.tm_year - 100; // 年 (0-99)
pstTime->ucMonth = stTm.tm_mon + 1;
pstTime->ucDay = stTm.tm_mday;
pstTime->ucHour = stTm.tm_hour;
pstTime->ucMin = stTm.tm_min;
uint16_t totalMs = stTm.tm_sec * 1000 + usMs;
pstTime->ucLMs = totalMs & 0xFF;
pstTime->ucHMs = (totalMs >> 8) & 0xFF;
return 1;
}
4.2 SetTimeItfs — 对时写入
s32 SetTimeItfs(__CP56Time2a *pstTime) {
struct tm stTm;
uint16_t usMs;
Cp562Stamp(pstTime, &stTm, &usMs); // CP56 → tm + ms
RTC_SetDateTime(&stTm, usMs); // 写入 RTC
return 1;
}
4.3 对时时序
主站 → C_CS_NA (时钟同步命令, COT=6)
→ CBB 库调用 GetTimeItfs() 获取当前时间 (用于组包)
→ CBB 库调用 SetTimeItfs() 设置收到的时间
→ 发送 C_CS_NA 响应 (COT=7)
5. RTU 适配:遥控集成 datacenter
RTU 的遥控应与 libdatacenter 的 SBO (Select-Before-Operate) 控制机制对接:
// RTU 版 Cs10xYkVerifyItfs
uint8_t Cs10xYkVerifyItfs(void *pvParam) {
// RTU 不需要 IP 白名单 (由 TCP 连接管理保证安全性)
return 1;
}
// RTU 版 SetYkInfor
int SetYkInfor(Yk_Info *pstInfo, void *pvArg) {
stru_iec_60870 *p_iec = iec60870_get_by_interface(*(uint32_t*)pvArg);
if(pstInfo->ucYkType == 1) { // SELECT
dc_sbo_select(p_iec, pstInfo->uiInfoAddr, &pstInfo->ucStatus, sizeof(uint8_t));
} else if(pstInfo->ucYkType == 0) { // EXECUTE
dc_sbo_operate(p_iec, pstInfo->uiInfoAddr, &pstInfo->ucStatus, sizeof(uint8_t));
} else { // CANCEL
dc_sbo_cancel(p_iec, pstInfo->uiInfoAddr);
}
return 1;
}
对时适配
s32 GetTimeItfs(__CP56Time2a *pstTime) {
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
timespec_to_cp56(&ts, pstTime);
return 1;
}
s32 SetTimeItfs(__CP56Time2a *pstTime) {
struct timespec ts;
cp56_to_timespec(pstTime, &ts);
clock_settime(CLOCK_REALTIME, &ts);
return 1;
}