# 04 - 参数定值与文件服务回调详解 > 源码: [DCU_HOST/applications/data/iec_itfc/GbParamItfs.c](../../../../../../DCU_HOST/applications/data/iec_itfc/GbParamItfs.c) (882行) --- ## 1. 回调注册 ```c // 定值区管理 CS10x_S_ParamAreaHandler(pstCs10x, GbParamGetParamArea, GbParamSetParamArea); // 参数读写 CS10x_S_IECParamHandler(pstCs10x, GbParamAddElement, GbParamRunRding, GbParamRunWting); // 参数信息查询 CS10x_S_GetParamInfoHandler(pstCs10x, GbParamGetTotalNum, GbParamGetIecParam); ``` --- ## 2. 定值区管理 ### 2.1 GbParamGetParamArea — 获取当前定值区号 ```c u16 GbParamGetParamArea(void *pParam) { return g_stParamMappingTable.stComCommunitParam.pucCurRunParamArea; // 返回当前运行区号 (0-255) } ``` ### 2.2 GbParamSetParamArea — 切换定值区 ```c s32 GbParamSetParamArea(u16 usAreaNum, void *pParam) { g_stParamMappingTable.stComCommunitParam.pucCurRunParamArea = (u8)usAreaNum; // 执行定值区切换逻辑... return 1; // 成功 } ``` **定值区概念**: - 电力装置通常有多个定值区 (0-3 或更多) - 0 区通常是当前运行区 - 主站通过 C_SR_NA (TypeID=200) 切换定值区 - 通过 C_RR_NA (TypeID=201) 读取当前区号 --- ## 3. 参数读写 (核心) ### 3.1 GbParamAddElement — 添加参数项到规约参数表 ```c s32 GbParamAddElement(IECPARAM_T *pstIecParam, void *pParam) { // 1. 从参数映射表中查找对应参数 u16 usIdx = GbParamSearchIndex(pstIecParam->uiInfoAddr); // 2. 填充 IECPARAM_T 的类型和长度 pstIecParam->ucType = g_stParamMap[usIdx].ucTagType; // TAG_TPYE_FLOAT 等 pstIecParam->ucLen = g_stParamMap[usIdx].ucDataLen; // 数据长度 // 3. 根据类型填充值 switch(pstIecParam->ucType) { case TAG_TPYE_FLOAT: pstIecParam->unVal.fVal = GetParamFloatValue(usIdx); break; case TAG_TPYE_INT: pstIecParam->unVal.lVal = GetParamIntValue(usIdx); break; // ... } return 1; } ``` **IECPARAM_T 结构体**: ```c typedef struct { unsigned char ucType; // 类型 (TAG_TPYE_FLOAT/INT/STR...) unsigned char ucLen; // 数据长度 union { unsigned char ucVal; char cVal; unsigned short usVal; short sVal; unsigned int ulVal; int lVal; float fVal; unsigned long long ullVal; long long llVal; double dVal; char caBuf[64]; // 字符串 } unVal; unsigned int uiInfoAddr; // 信息体地址 } IECPARAM_T; ``` ### 3.2 GbParamRunRding — 执行参数读取 ```c s32 GbParamRunRding(u16 usReqAddr, u16 usNum, void *pParam, u8 ucArea) { // 主站请求读取 usReqAddr 开始的 usNum 个参数 for(u16 i = 0; i < usNum; i++) { u16 usIdx = GbParamSearchIndex(usReqAddr + i); if(usIdx == 0xFFFF) continue; // 未找到 IECPARAM_T stParam; stParam.uiInfoAddr = usReqAddr + i; GbParamFillValue(&stParam, usIdx, ucArea); // 从指定定值区填充值 GbParamAddToResponse(&stParam); // 加入响应缓存 } return usNum; } ``` ### 3.3 GbParamRunWting — 执行参数写入 参数写入有**预置 → 固化**两步流程 (南网规约) 或**直接写入** (标准规约): ```c s32 GbParamRunWting(IECPARAM_T *pstParam, u8 ucType, void *pParam) { // ucType: WRITE_PARAM_YZ (预置) / WRITE_PARAM_GH (固化) u16 usIdx = GbParamSearchIndex(pstParam->uiInfoAddr); if(ucType == WRITE_PARAM_YZ) { // 预置: 暂存到编辑区,不立即生效 GbParamPresetValue(usIdx, &pstParam->unVal); return 1; } else if(ucType == WRITE_PARAM_GH) { // 固化: 将编辑区值写入运行区 GbParamSolidifyValue(usIdx); // ★ 触发参数生效逻辑 (如重启保护等) return 1; } return -1; } ``` ### 3.4 GbParamGetTotalNum / GbParamGetIecParam — 参数信息查询 ```c s32 GbParamGetTotalNum(void *pParam) { return g_stParamMapCount; // 返回参数表总数 } s32 GbParamGetIecParam(IECPARAM_T *pstParam, u16 usIdx, void *pParam) { pstParam->uiInfoAddr = g_stParamMap[usIdx].uiAddr; pstParam->ucType = g_stParamMap[usIdx].ucTagType; pstParam->ucLen = g_stParamMap[usIdx].ucDataLen; return 1; } ``` --- ## 4. 参数读写时序 (南网规约) ```mermaid sequenceDiagram participant Master as 主站 participant CBB as lib60870 participant App as 应用层(GbParamItfs) Note over Master,CBB: === 读参数 === Master->>CBB: C_RS_NA_NANWANG (TypeID=108, COT=5) CBB->>App: GbParamRunRding(起始地址, 数量) App-->>CBB: 返回参数值列表 CBB->>Master: 参数数据帧 Note over Master,CBB: === 写参数 (预置) === Master->>CBB: C_SP_NA (TypeID=55, 预置, COT=6) CBB->>App: GbParamRunWting(param, WRITE_PARAM_YZ) App-->>CBB: 预置成功 CBB->>Master: 预置确认 (COT=7) Note over Master,CBB: === 写参数 (固化) === Master->>CBB: C_SP_NA (TypeID=55, 固化, COT=6) CBB->>App: GbParamRunWting(param, WRITE_PARAM_GH) App-->>CBB: 固化成功 CBB->>Master: 固化确认 (COT=7) ``` **标准规约** (TypeID=202/203) 流程类似,但无需预置/固化两步,直接读/写即可。 --- ## 5. 文件服务回调 ```c CS10x_S_FileReadOprtHandler(pstCs10x, Fm_GetDirFile, Fm_SearchDirFileNum, Fm_ReadFile, &pstEntity->stReadParam); CS10x_S_FileWriteOprtHandler(pstCs10x, Cs10xWriteItfs, Cs10xWriteFileCheckItfs, Cs10xWriteFinishItfs, &pstEntity->stWriteParam); CS10x_S_UpdateProcHandler(pstCs10x, NULL); // 升级 (当前未实现) ``` 文件服务使用 TypeID=210 (F_FR_NA),支持: - **目录召唤**: 返回文件列表 - **读文件**: 分段传输 - **写文件**: 用于参数配置下发 - **软件升级**: TypeID=211 (F_SR_NA) ### RTU 适配建议 文件服务在 RTU 场景中可能不需要 (通过 Web/WebSocket 管理配置更常见),可以暂时注册 `NULL`: ```c CS10x_S_FileReadOprtHandler(pstCs10x, NULL, NULL, NULL, NULL); CS10x_S_FileWriteOprtHandler(pstCs10x, NULL, NULL, NULL, NULL); CS10x_S_UpdateProcHandler(pstCs10x, NULL); ``` --- ## 6. 参数类型映射表 (TAG_TYPE) CBB 库内部使用的参数类型编码: | 宏 | 值 | C 类型 | |----|-----|--------| | `TAG_TPYE_BOOL` | 1 | `bool` | | `TAG_TPYE_INT` | 2 | `int` | | `TAG_TPYE_STR` | 4 | `char[]` | | `TAG_TPYE_UTINY` | 32 | `uint8_t` | | `TAG_TPYE_SHORT` | 33 | `int16_t` | | `TAG_TPYE_UINT` | 35 | `uint32_t` | | `TAG_TPYE_LONG` | 36 | `int32_t` | | `TAG_TPYE_ULONG` | 37 | `uint32_t` | | `TAG_TPYE_FLOAT` | 38 | `float` | | `TAG_TPYE_DOUBLE` | 39 | `double` | | `TAG_TPYE_TINY` | 43 | `int8_t` | | `TAG_TPYE_USHORT` | 45 | `uint16_t` | --- ## 7. 加密参数配置 ```c void SetProtocolEncryParam(CS10x *pstCs10x, void *pvArg) { // 加密使能检查 (仅 4G 串口和维护口) if((isSlave) && (isEncryptInterface)) { CS10x_EncryptHandler(pstCs10x, Encryp_Encode); // 加密回调 CS10x_Md5ResultHandler(pstCs10x, Saft_Update_Md5); // MD5 校验 Saft_Config_Param(1, ...); // 加密参数配置 } else { CS10x_EncryptHandler(pstCs10x, NULL); CS10x_Md5ResultHandler(pstCs10x, NULL); } } ``` **RTU 注意**: 如果不需要加密,注册 NULL 即可。