RTU/mimo/工程/DCU_HOST/03-遥控与对时.md

7.1 KiB
Raw Permalink Blame History

03 - 遥控与对时回调详解

源码: DCU_HOST/applications/data/iec_itfc/YkItfc.c


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;
}