fix+feat(self_ptl): 代码审查修复 + self_ptl 测试命令驱动
## Critical 修复 (4项) - TI_1/TI_6: ASDU 缓冲区溢出边界检查 (恶意数据包可越界读) - TI_3: 续帧请求 num=0 (避免协议不同步) - self_ptl_cb: 文件操作全局状态加 pthread_mutex 防多FTU竞态 - file_write_confirm: 检查FTU错误结果 (FILE_WRITE_CONT_2/3中止传输) ## Important 修复 (8项) - icp67_core: 删除重复 tm_out 赋值 - icp67_decode: 传播解码器返回值 (MD5失败可上报) - self_ptl: IEC-104发现日志加TODO注释 - self_ptl_cfg: parse_param 补全信号解析逻辑 (原空循环体) - file_write_confirm: 移除 usleep(5000) 无意义延迟 - icp67_core: 17个setter统一加NULL指针检查 - com_router_dispatch: ICP67路由加显式 dev_addr!=维护地址检查 - self_ptl_cb: func_make_dirs 返回值检查 ## Minor 修复 (3项) - icp67.h: @param src→dst 文档修正 - icp67_frame: show_frame 数组索引边界检查 - icp67_frame: search_frame 多余 sizeof(uint16_t) 移除 ## feat: self_ptl 测试命令 (9个子命令) - info/ao_get/ao_get_all/iec_get/time_sync/self_check/dir_read/file_read/file_write - CMD_REGISTER_C 注册 + Tab补全 - ao_get_all: 遍历XML全部AO参数一次性发送 (对齐原工程EncodeCodeParamData)
This commit is contained in:
parent
ae1329aacc
commit
0409f427aa
|
|
@ -460,14 +460,14 @@ void icp67_timer_handler(stru_icp67 *p_icp67);
|
|||
|
||||
/**
|
||||
* @brief 构造 ICP67 帧头
|
||||
* @param src 源设备
|
||||
* @param dst 目标设备
|
||||
* @param ti 类型标识
|
||||
* @param cot 传输原因
|
||||
* @param info_addr 信息地址
|
||||
* @param p_tx 发送缓冲区 (输入输出)
|
||||
* @param tx_len 发送长度 (输入输出)
|
||||
*/
|
||||
void icp67_make_head(uint8_t src, uint8_t ti, uint8_t cot, uint16_t info_addr, uint8_t *p_tx, uint16_t *tx_len);
|
||||
void icp67_make_head(uint8_t dst, uint8_t ti, uint8_t cot, uint16_t info_addr, uint8_t *p_tx, uint16_t *tx_len);
|
||||
|
||||
/**
|
||||
* @brief 构造 ICP67 帧尾 (CRC + 0x16)
|
||||
|
|
|
|||
|
|
@ -128,7 +128,6 @@ void icp67_init(stru_icp67 *p_icp67, icp67_send_cb send_cb, void *arg,
|
|||
p_icp67->tm_out_ms = (0 == tm_out_ms) ? ICP67_TM_OUT_DEF_MS : tm_out_ms;
|
||||
p_icp67->tm_out = p_icp67->tm_out_ms / ICP67_TM_TICK_MS;
|
||||
p_icp67->resend_cnt = 0;
|
||||
p_icp67->tm_out = p_icp67->tm_out_ms / ICP67_TM_TICK_MS;
|
||||
p_icp67->tm_cnt = 0;
|
||||
|
||||
/* 初始化 sender 回调为内部函数 (方向: self_ptl → icp67) */
|
||||
|
|
@ -182,86 +181,103 @@ int icp67_set_ao_cfg_md5(stru_icp67 *p_icp67, uint8_t *p_md5)
|
|||
|
||||
void icp67_set_ao_pop_out_cb(stru_icp67 *p_icp67, icp67_ao_pop_out_cb cb)
|
||||
{
|
||||
if(NULL == p_icp67) return;
|
||||
p_icp67->method.ao_pop_out_cb = cb;
|
||||
}
|
||||
|
||||
void icp67_set_iec_point_tbl_pop_out_cb(stru_icp67 *p_icp67, icp67_iec_point_tbl_pop_out_cb cb)
|
||||
{
|
||||
if(NULL == p_icp67) return;
|
||||
p_icp67->method.iec_point_tbl_pop_out_cb = cb;
|
||||
}
|
||||
|
||||
void icp67_set_self_check_pop_out_cb(stru_icp67 *p_icp67, icp67_self_check_pop_out_cb cb)
|
||||
{
|
||||
if(NULL == p_icp67) return;
|
||||
p_icp67->method.self_check_pop_out_cb = cb;
|
||||
}
|
||||
|
||||
void icp67_set_dir_pop_out_cb(stru_icp67 *p_icp67, icp67_dir_pop_out_cb cb)
|
||||
{
|
||||
if(NULL == p_icp67) return;
|
||||
p_icp67->method.dir_pop_out_cb = cb;
|
||||
}
|
||||
|
||||
void icp67_set_file_read_act_confirm_cb(stru_icp67 *p_icp67, icp67_file_read_act_confirm_cb cb)
|
||||
{
|
||||
if(NULL == p_icp67) return;
|
||||
p_icp67->method.file_read_act_confirm_cb = cb;
|
||||
}
|
||||
|
||||
void icp67_set_file_read_cb(stru_icp67 *p_icp67, icp67_file_read_cb cb)
|
||||
{
|
||||
if(NULL == p_icp67) return;
|
||||
p_icp67->method.file_read_cb = cb;
|
||||
}
|
||||
|
||||
void icp67_set_file_write_act_confirm_cb(stru_icp67 *p_icp67, icp67_file_write_act_confirm_cb cb)
|
||||
{
|
||||
if(NULL == p_icp67) return;
|
||||
p_icp67->method.file_write_act_confirm_cb = cb;
|
||||
}
|
||||
|
||||
void icp67_set_file_write_confirm_cb(stru_icp67 *p_icp67, icp67_file_write_confirm_cb cb)
|
||||
{
|
||||
if(NULL == p_icp67) return;
|
||||
p_icp67->method.file_write_confirm_cb = cb;
|
||||
}
|
||||
|
||||
void icp67_set_file_write_end_cb(stru_icp67 *p_icp67, icp67_file_write_end_cb cb)
|
||||
{
|
||||
if(NULL == p_icp67) return;
|
||||
p_icp67->method.file_write_end_cb = cb;
|
||||
}
|
||||
|
||||
void icp67_set_zip_dir_pop_out_cb(stru_icp67 *p_icp67, icp67_zip_dir_pop_out_cb cb)
|
||||
{
|
||||
if(NULL == p_icp67) return;
|
||||
p_icp67->method.zip_dir_pop_out_cb = cb;
|
||||
}
|
||||
|
||||
void icp67_set_zip_file_pop_out_cb(stru_icp67 *p_icp67, icp67_zip_file_pop_out_cb cb)
|
||||
{
|
||||
if(NULL == p_icp67) return;
|
||||
p_icp67->method.zip_file_pop_out_cb = cb;
|
||||
}
|
||||
|
||||
void icp67_set_mx_trans_cb(stru_icp67 *p_icp67, icp67_mx_trans_cb cb)
|
||||
{
|
||||
if(NULL == p_icp67) return;
|
||||
p_icp67->method.mx_trans_cb = cb;
|
||||
}
|
||||
|
||||
void icp67_set_st_trans_cb(stru_icp67 *p_icp67, icp67_st_trans_cb cb)
|
||||
{
|
||||
if(NULL == p_icp67) return;
|
||||
p_icp67->method.st_trans_cb = cb;
|
||||
}
|
||||
|
||||
void icp67_set_dd_trans_cb(stru_icp67 *p_icp67, icp67_dd_trans_cb cb)
|
||||
{
|
||||
if(NULL == p_icp67) return;
|
||||
p_icp67->method.dd_trans_cb = cb;
|
||||
}
|
||||
|
||||
void icp67_set_soe_trans_cb(stru_icp67 *p_icp67, icp67_soe_trans_cb cb)
|
||||
{
|
||||
if(NULL == p_icp67) return;
|
||||
p_icp67->method.soe_trans_cb = cb;
|
||||
}
|
||||
|
||||
void icp67_set_fault_trans_cb(stru_icp67 *p_icp67, icp67_fault_trans_cb cb)
|
||||
{
|
||||
if(NULL == p_icp67) return;
|
||||
p_icp67->method.fault_trans_cb = cb;
|
||||
}
|
||||
|
||||
void icp67_set_mx_change_cb(stru_icp67 *p_icp67, icp67_mx_change_cb cb)
|
||||
{
|
||||
if(NULL == p_icp67) return;
|
||||
p_icp67->method.mx_change_cb = cb;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -203,6 +203,7 @@ int icp67_decode(stru_icp67 *p_icp67, uint8_t *p_rx, uint16_t rx_len)
|
|||
{
|
||||
stru_head *p_head;
|
||||
icp67_decode_cb func;
|
||||
int ret;
|
||||
|
||||
if(NULL == p_icp67 || NULL == p_rx || 0 == rx_len)
|
||||
{
|
||||
|
|
@ -218,9 +219,9 @@ int icp67_decode(stru_icp67 *p_icp67, uint8_t *p_rx, uint16_t rx_len)
|
|||
return -1;
|
||||
}
|
||||
|
||||
func(p_icp67, p_rx, rx_len);
|
||||
ret = func(p_icp67, p_rx, rx_len);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* =========================== TI_1: 读独立编码参数响应 =========================== */
|
||||
|
|
@ -266,16 +267,34 @@ static int icp67_decode_ti_1(stru_icp67 *p_icp67, uint8_t *p_rx, uint16_t rx_len
|
|||
return -1;
|
||||
}
|
||||
|
||||
{
|
||||
uint16_t asdu_len = (uint16_t)(rx_len - ICP67_FRAME_OVERHEAD); /* 减去帧头开销 */
|
||||
|
||||
pos = 0;
|
||||
for(i = 0; p_icp67->method.ao_pop_out_cb && i < p_asdu->num; i++)
|
||||
{
|
||||
stru_ti_1_2_data_info *p_info = (stru_ti_1_2_data_info *)&p_asdu->data[pos];
|
||||
stru_ti_1_2_data_info *p_info;
|
||||
|
||||
if(pos + sizeof(stru_ti_1_2_data_info) > asdu_len)
|
||||
{
|
||||
LOG_E("icp67_decode_ti_1: frame too short for entry header, pos=%d", pos);
|
||||
break;
|
||||
}
|
||||
|
||||
p_info = (stru_ti_1_2_data_info *)&p_asdu->data[pos];
|
||||
|
||||
if(pos + sizeof(stru_ti_1_2_data_info) + p_info->data_len > asdu_len)
|
||||
{
|
||||
LOG_E("icp67_decode_ti_1: data_len overflow, pos=%d, data_len=%d", pos, p_info->data_len);
|
||||
break;
|
||||
}
|
||||
|
||||
pos += sizeof(stru_ti_1_2_data_info) + p_info->data_len;
|
||||
|
||||
p_icp67->method.ao_pop_out_cb(p_icp67, p_info->data_addr,
|
||||
p_info->data_type, p_info->data_len, p_info->data);
|
||||
}
|
||||
}
|
||||
|
||||
icp67_rtx_flag_set(p_icp67, ICP67_RX_FLAG, 0, 0);
|
||||
|
||||
|
|
@ -379,7 +398,7 @@ static int icp67_decode_ti_3(stru_icp67 *p_icp67, uint8_t *p_rx, uint16_t rx_len
|
|||
p_tx_asdu->total = p_asdu->total;
|
||||
p_tx_asdu->frame_no = offset;
|
||||
p_tx_asdu->follow = p_asdu->follow;
|
||||
p_tx_asdu->num = p_asdu->num;
|
||||
p_tx_asdu->num = 0; /* 续帧请求不携带参数数据 */
|
||||
tx_len += sizeof(stru_ti_3_follow);
|
||||
}
|
||||
|
||||
|
|
@ -468,15 +487,33 @@ static int icp67_decode_ti_6(stru_icp67 *p_icp67, uint8_t *p_rx, uint16_t rx_len
|
|||
|
||||
p_asdu = (stru_ti_6 *)p_head->data;
|
||||
|
||||
{
|
||||
uint16_t asdu_len = (uint16_t)(rx_len - ICP67_FRAME_OVERHEAD);
|
||||
|
||||
pos = 0;
|
||||
for(i = 0; p_icp67->method.self_check_pop_out_cb && i < p_asdu->st_num; i++)
|
||||
{
|
||||
stru_data_info *p = (stru_data_info *)&p_asdu->st_data[pos];
|
||||
stru_data_info *p;
|
||||
|
||||
if(pos + sizeof(stru_data_info) > asdu_len)
|
||||
{
|
||||
LOG_E("icp67_decode_ti_6: frame too short for entry header, pos=%d", pos);
|
||||
break;
|
||||
}
|
||||
|
||||
p = (stru_data_info *)&p_asdu->st_data[pos];
|
||||
|
||||
if(pos + sizeof(stru_data_info) + p->data_len > asdu_len)
|
||||
{
|
||||
LOG_E("icp67_decode_ti_6: data_len overflow, pos=%d, data_len=%d", pos, p->data_len);
|
||||
break;
|
||||
}
|
||||
|
||||
pos += sizeof(stru_data_info) + p->data_len;
|
||||
|
||||
p_icp67->method.self_check_pop_out_cb(p_icp67, p, i);
|
||||
}
|
||||
}
|
||||
|
||||
icp67_rtx_flag_set(p_icp67, ICP67_RX_FLAG, 0, 0);
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ int icp67_search_frame(uint8_t *p_data, uint16_t len, uint16_t *p_pos, uint16_t
|
|||
{
|
||||
stru_head *p_head = (stru_head *)(p_data + i);
|
||||
|
||||
if(i + sizeof(uint16_t) + sizeof(stru_head) > len)
|
||||
if(i + sizeof(stru_head) > len)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
|
@ -192,8 +192,16 @@ void icp67_show_frame(uint8_t *p_data, uint16_t len)
|
|||
|
||||
p_head = (stru_head *)p_data;
|
||||
|
||||
if(p_head->src >= 1 && p_head->src <= 5 && p_head->dst >= 1 && p_head->dst <= 5)
|
||||
{
|
||||
printf("%s -> %s, len = %d, dev_addr %d\n",
|
||||
str[p_head->src - 1], str[p_head->dst - 1], len, p_head->dev_addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("src=%d -> dst=%d, len = %d, dev_addr %d\n",
|
||||
p_head->src, p_head->dst, len, p_head->dev_addr);
|
||||
}
|
||||
|
||||
for(i = 0; i < len; i++)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -173,9 +173,10 @@ void com_router_recv_cb(int id, int fd, const unsigned char *p_rx, int rx_len)
|
|||
return;
|
||||
}
|
||||
|
||||
/* c) ICP67 帧 (dev_addr=1) → MQ_COM_TO_SELF_PTL → self_ptl */
|
||||
/* c) ICP67 帧 (dev_addr != 维护地址) → MQ_COM_TO_SELF_PTL → self_ptl */
|
||||
if(rx_len >= (int)sizeof(stru_head)
|
||||
&& 0x67 == p_rx[0] && 0x67 == p_rx[3])
|
||||
&& 0x67 == p_rx[0] && 0x67 == p_rx[3]
|
||||
&& DEV_MAINTENANCE_ADDR != ((stru_head *)p_rx)->dev_addr)
|
||||
{
|
||||
uint32_t interface = ((uint32_t)id << 16) | (uint32_t)fd;
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include "mySystem.h"
|
||||
#include "myLog.h"
|
||||
#include "myCmd.h"
|
||||
#include "myIcp67.h"
|
||||
#include "myDatacenter.h"
|
||||
#include "self_ptl.h"
|
||||
|
|
@ -12,6 +13,8 @@
|
|||
|
||||
#include <pthread.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
|
@ -384,8 +387,9 @@ static void self_ptl_search_frame(uint8_t *p_rx, uint16_t len, stru_icp67 *p_icp
|
|||
if(0 == self_ptl_search_iec_frame(buf, buf_len, &pos, &valid_len))
|
||||
{
|
||||
LOG_I("self_ptl: found IEC-104 frame at pos=%d, len=%d", pos, valid_len);
|
||||
/* TODO: 将 IEC-104 帧转发到 libiec 解码模块 (待实现) */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* =========================== 信号注册 =========================== */
|
||||
|
|
@ -627,3 +631,254 @@ void *app_self_ptl(void *arg)
|
|||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* =========================== 测试命令 =========================== */
|
||||
/*
|
||||
* self_ptl 测试命令 — 手动触发 ICP67 各类协议请求。
|
||||
* 在真实 FTU 对接前,这些命令是项目主要数据来源。
|
||||
*
|
||||
* TODO: 后续真实数据接入后,这些子命令可转为内部接口或保留调试用。
|
||||
*/
|
||||
|
||||
static void cmd_self_ptl_show_info(void)
|
||||
{
|
||||
stru_icp67 *p_icp67 = &g_self_ptl.icp67;
|
||||
stru_self_ptl_cfg *p_cfg = self_ptl_cfg_get();
|
||||
|
||||
printf("===== Self-PTL Info =====\n");
|
||||
printf("Interface : 0x%08X\n", g_self_ptl.interface);
|
||||
printf("ICP67 : send_cb=%p, tx_len=%d\n",
|
||||
(void*)p_icp67->send_cb, p_icp67->tx_len);
|
||||
printf("RTX : flag=%d, cnt=%d/%d, tm_out=%d ms\n",
|
||||
p_icp67->rtx_flag, p_icp67->resend_cnt, p_icp67->resend_max,
|
||||
p_icp67->tm_out_ms);
|
||||
printf("MD5 : ");
|
||||
for(int i = 0; i < 16; i++) printf("%02X", p_icp67->md5[i]);
|
||||
printf("\nCB table : ao_get=%p ao_set=%p iec_get=%p time=%p chk=%p upg=%p\n",
|
||||
(void*)p_icp67->method.ao_get_cb,
|
||||
(void*)p_icp67->method.ao_set_cb,
|
||||
(void*)p_icp67->method.iec_point_tbl_get_cb,
|
||||
(void*)p_icp67->method.time_set_cb,
|
||||
(void*)p_icp67->method.self_check_get_cb,
|
||||
(void*)p_icp67->method.upgrade_start_cb);
|
||||
printf(" dir=%p f_rd_act=%p f_wr_act=%p\n",
|
||||
(void*)p_icp67->method.dir_read_cb,
|
||||
(void*)p_icp67->method.file_read_act_cb,
|
||||
(void*)p_icp67->method.file_write_act_cb);
|
||||
|
||||
if(NULL != p_cfg)
|
||||
{
|
||||
printf("XML CFG : ST=%d MX=%d CO=%d DD=%d AO=%d Param=%d\n",
|
||||
(int)p_cfg->st_vec.size(), (int)p_cfg->mx_vec.size(),
|
||||
(int)p_cfg->co_vec.size(), (int)p_cfg->dd_vec.size(),
|
||||
(int)p_cfg->ao_vec.size(), (int)p_cfg->param_vec.size());
|
||||
}
|
||||
}
|
||||
|
||||
LOCAL void cmd_self_ptl(int argc, char *argv[])
|
||||
{
|
||||
stru_icp67 *p_icp67 = &g_self_ptl.icp67;
|
||||
const char *str =
|
||||
"self_ptl info 查看自协议和 ICP67 状态\n"
|
||||
"self_ptl ao_get <addr> <type> [num=1] 触发读内部参数\n"
|
||||
"self_ptl ao_get_all 触发读全部 AO 参数 (来自 XML 配置)\n"
|
||||
"self_ptl iec_get <info_addr> 触发读 IEC 点表 (分帧传输)\n"
|
||||
"self_ptl time_sync 触发对时\n"
|
||||
"self_ptl self_check 触发自检读取\n"
|
||||
"self_ptl dir_read <path> 触发读目录 (可选时间范围)\n"
|
||||
"self_ptl file_read <name> 触发读文件激活\n"
|
||||
"self_ptl file_write <name> <size> <crc> 触发写文件激活\n";
|
||||
|
||||
if(argc < 2)
|
||||
{
|
||||
printf("%s", str);
|
||||
return;
|
||||
}
|
||||
|
||||
if(0 == strcmp(argv[1], "info"))
|
||||
{
|
||||
cmd_self_ptl_show_info();
|
||||
}
|
||||
else if(0 == strcmp(argv[1], "ao_get"))
|
||||
{
|
||||
uint16_t addr = (argc >= 3) ? (uint16_t)atoi(argv[2]) : 0;
|
||||
uint8_t type = (argc >= 4) ? (uint8_t)atoi(argv[3]) : 0;
|
||||
uint16_t num = (argc >= 5) ? (uint16_t)atoi(argv[4]) : 1;
|
||||
|
||||
if(NULL == p_icp67->method.ao_get_cb)
|
||||
{
|
||||
LOG_E("cmd_self_ptl: ao_get_cb not set");
|
||||
return;
|
||||
}
|
||||
|
||||
stru_ti_1_2_data ao_data[16] = {};
|
||||
for(uint16_t i = 0; i < num && i < 16; i++)
|
||||
{
|
||||
ao_data[i].data_addr = addr + i;
|
||||
ao_data[i].data_type = type;
|
||||
ao_data[i].data_len = 0;
|
||||
}
|
||||
|
||||
p_icp67->method.ao_get_cb(p_icp67, ao_data, num);
|
||||
LOG_I("cmd_self_ptl: ao_get addr=%d type=%d num=%d", addr, type, num);
|
||||
}
|
||||
else if(0 == strcmp(argv[1], "ao_get_all"))
|
||||
{
|
||||
/* 读全部 AO 参数: 遍历 XML 配置的所有 AO,构造完整参数列表 */
|
||||
stru_self_ptl_cfg *p_cfg = self_ptl_cfg_get();
|
||||
|
||||
if(NULL == p_cfg || p_cfg->ao_vec.empty())
|
||||
{
|
||||
printf("ao_get_all: no AO params in config\n");
|
||||
return;
|
||||
}
|
||||
if(NULL == p_icp67->method.ao_get_cb)
|
||||
{
|
||||
LOG_E("cmd_self_ptl: ao_get_cb not set");
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t num = (uint16_t)p_cfg->ao_vec.size();
|
||||
if(num > 240)
|
||||
{
|
||||
LOG_E("cmd_self_ptl: too many AO params (%d), max 240", num);
|
||||
return;
|
||||
}
|
||||
|
||||
/* 栈上分配参数数组 (每个 sizeof(stru_ti_1_2_data)=4) */
|
||||
stru_ti_1_2_data ao_data[240];
|
||||
for(uint16_t i = 0; i < num; i++)
|
||||
{
|
||||
ao_data[i].data_addr = (uint16_t)p_cfg->ao_vec[i].base.inf;
|
||||
ao_data[i].data_type = (uint8_t)p_cfg->ao_vec[i].type;
|
||||
ao_data[i].data_len = 0; /* 读取时不需要填长度,FTU 回复时填充 */
|
||||
}
|
||||
|
||||
p_icp67->method.ao_get_cb(p_icp67, ao_data, num);
|
||||
LOG_I("cmd_self_ptl: ao_get_all num=%d", num);
|
||||
}
|
||||
else if(0 == strcmp(argv[1], "iec_get"))
|
||||
{
|
||||
uint16_t info_addr = (argc >= 3) ? (uint16_t)atoi(argv[2]) : 0;
|
||||
|
||||
if(NULL == p_icp67->method.iec_point_tbl_get_cb)
|
||||
{
|
||||
LOG_E("cmd_self_ptl: iec_point_tbl_get_cb not set");
|
||||
return;
|
||||
}
|
||||
|
||||
p_icp67->method.iec_point_tbl_get_cb(p_icp67, info_addr);
|
||||
LOG_I("cmd_self_ptl: iec_get info_addr=%d", info_addr);
|
||||
}
|
||||
else if(0 == strcmp(argv[1], "time_sync"))
|
||||
{
|
||||
if(NULL == p_icp67->method.time_set_cb)
|
||||
{
|
||||
LOG_E("cmd_self_ptl: time_set_cb not set");
|
||||
return;
|
||||
}
|
||||
|
||||
/* 构造当前时间 ASDU */
|
||||
stru_ti_5 tm = {};
|
||||
time_t now = time(NULL);
|
||||
struct tm *lt = localtime(&now);
|
||||
|
||||
tm.year = (uint16_t)(lt->tm_year + 1900 - 2000);
|
||||
tm.month = (uint16_t)(lt->tm_mon + 1);
|
||||
tm.day = (uint16_t)lt->tm_mday;
|
||||
tm.hour = (uint16_t)lt->tm_hour;
|
||||
tm.min = (uint16_t)lt->tm_min;
|
||||
tm.ms = (uint16_t)(lt->tm_sec * 1000);
|
||||
|
||||
p_icp67->method.time_set_cb(p_icp67, &tm);
|
||||
LOG_I("cmd_self_ptl: time_sync %04d-%02d-%02d %02d:%02d:%02d",
|
||||
tm.year+2000, tm.month, tm.day, tm.hour, tm.min, tm.ms/1000);
|
||||
}
|
||||
else if(0 == strcmp(argv[1], "self_check"))
|
||||
{
|
||||
if(NULL == p_icp67->method.self_check_get_cb)
|
||||
{
|
||||
LOG_E("cmd_self_ptl: self_check_get_cb not set");
|
||||
return;
|
||||
}
|
||||
|
||||
p_icp67->method.self_check_get_cb(p_icp67);
|
||||
LOG_I("cmd_self_ptl: self_check");
|
||||
}
|
||||
else if(0 == strcmp(argv[1], "dir_read"))
|
||||
{
|
||||
if(argc < 3)
|
||||
{
|
||||
printf("usage: self_ptl dir_read <path>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(NULL == p_icp67->method.dir_read_cb)
|
||||
{
|
||||
LOG_E("cmd_self_ptl: dir_read_cb not set");
|
||||
return;
|
||||
}
|
||||
|
||||
const char *path = argv[2];
|
||||
uint8_t len = (uint8_t)strlen(path);
|
||||
|
||||
p_icp67->method.dir_read_cb(p_icp67, (uint8_t *)path, len, NULL, NULL);
|
||||
LOG_I("cmd_self_ptl: dir_read %s", path);
|
||||
}
|
||||
else if(0 == strcmp(argv[1], "file_read"))
|
||||
{
|
||||
if(argc < 3)
|
||||
{
|
||||
printf("usage: self_ptl file_read <name>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(NULL == p_icp67->method.file_read_act_cb)
|
||||
{
|
||||
LOG_E("cmd_self_ptl: file_read_act_cb not set");
|
||||
return;
|
||||
}
|
||||
|
||||
const char *name = argv[2];
|
||||
uint8_t len = (uint8_t)strlen(name);
|
||||
|
||||
p_icp67->method.file_read_act_cb(p_icp67, (uint8_t *)name, len);
|
||||
LOG_I("cmd_self_ptl: file_read %s", name);
|
||||
}
|
||||
else if(0 == strcmp(argv[1], "file_write"))
|
||||
{
|
||||
if(argc < 5)
|
||||
{
|
||||
printf("usage: self_ptl file_write <name> <size> <crc>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(NULL == p_icp67->method.file_write_act_cb)
|
||||
{
|
||||
LOG_E("cmd_self_ptl: file_write_act_cb not set");
|
||||
return;
|
||||
}
|
||||
|
||||
const char *name = argv[2];
|
||||
uint8_t len = (uint8_t)strlen(name);
|
||||
uint32_t size = (uint32_t)atoi(argv[3]);
|
||||
uint16_t crc = (uint16_t)atoi(argv[4]);
|
||||
|
||||
p_icp67->method.file_write_act_cb(p_icp67, (uint8_t *)name, len, 0, size, crc);
|
||||
LOG_I("cmd_self_ptl: file_write %s size=%d crc=%d", name, size, crc);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("unknown subcommand: %s\n", argv[1]);
|
||||
printf("%s", str);
|
||||
}
|
||||
}
|
||||
|
||||
LOCAL void cmd_self_ptl_complete(const char *buf, char ***completions, int *ncomp)
|
||||
{
|
||||
static const char *subs[] = {"info", "ao_get", "ao_get_all", "iec_get", "time_sync",
|
||||
"self_check", "dir_read", "file_read", "file_write"};
|
||||
cmd_sub_complete(buf, completions, ncomp, subs, sizeof(subs)/sizeof(subs[0]));
|
||||
}
|
||||
|
||||
CMD_REGISTER_C("self_ptl", cmd_self_ptl, "自协议测试命令 (AO/IEC/时间/自检/文件)", cmd_self_ptl_complete);
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
|
||||
/* =========================== 文件操作辅助 =========================== */
|
||||
|
||||
|
|
@ -24,6 +25,9 @@ static std::string g_file_path;
|
|||
static std::string g_zip_path;
|
||||
static int g_paths_ready = 0;
|
||||
|
||||
/* 文件操作全局状态互斥锁 (防止多 FTU 并发传输竞态) */
|
||||
static pthread_mutex_t g_file_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
static void file_paths_init(void)
|
||||
{
|
||||
char proc_dir[512] = {0};
|
||||
|
|
@ -40,8 +44,16 @@ static void file_paths_init(void)
|
|||
g_file_path = g_file_base_path + "file/";
|
||||
g_zip_path = g_file_base_path + "zip/";
|
||||
|
||||
func_make_dirs(g_file_path.c_str());
|
||||
func_make_dirs(g_zip_path.c_str());
|
||||
if(0 != func_make_dirs(g_file_path.c_str()))
|
||||
{
|
||||
LOG_E("file_paths_init: func_make_dirs(%s) failed", g_file_path.c_str());
|
||||
return;
|
||||
}
|
||||
if(0 != func_make_dirs(g_zip_path.c_str()))
|
||||
{
|
||||
LOG_E("file_paths_init: func_make_dirs(%s) failed", g_zip_path.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
g_paths_ready = 1;
|
||||
LOG_I("file_paths_init: file=%s, zip=%s", g_file_path.c_str(), g_zip_path.c_str());
|
||||
|
|
@ -193,6 +205,8 @@ void dir_pop_out(stru_icp67 *p_icp67, uint8_t *p_name, uint8_t len,
|
|||
|
||||
if(NULL == p_icp67 || NULL == p_name) return;
|
||||
|
||||
pthread_mutex_lock(&g_file_mutex);
|
||||
|
||||
memcpy(dir_info.name, p_name, len);
|
||||
dir_info.name[len] = '\0';
|
||||
dir_info.len = len;
|
||||
|
|
@ -209,6 +223,8 @@ void dir_pop_out(stru_icp67 *p_icp67, uint8_t *p_name, uint8_t len,
|
|||
}
|
||||
g_dir_info_vec.clear();
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&g_file_mutex);
|
||||
}
|
||||
|
||||
/* ---- 读文件激活确认 ---- */
|
||||
|
|
@ -219,6 +235,8 @@ void file_read_act_confirm(stru_icp67 *p_icp67, uint8_t *p_name, uint8_t len,
|
|||
if(NULL == p_icp67 || NULL == p_name) return;
|
||||
|
||||
file_paths_init();
|
||||
|
||||
pthread_mutex_lock(&g_file_mutex);
|
||||
g_file_name = std::string((char *)p_name, len);
|
||||
|
||||
LOG_I("file_read_act_confirm: name=%s, id=%d, size=%d, crc=0x%04X",
|
||||
|
|
@ -230,6 +248,7 @@ void file_read_act_confirm(stru_icp67 *p_icp67, uint8_t *p_name, uint8_t len,
|
|||
func_make_dirs((g_file_path + g_dir_name).c_str());
|
||||
unlink(fpath.c_str());
|
||||
}
|
||||
pthread_mutex_unlock(&g_file_mutex);
|
||||
}
|
||||
|
||||
/* ---- 读文件内容 (FTU → RTU 本地写入) ---- */
|
||||
|
|
@ -239,6 +258,8 @@ void file_read(stru_icp67 *p_icp67, uint32_t id, uint32_t offset,
|
|||
{
|
||||
if(NULL == p_icp67 || NULL == p_data) return;
|
||||
|
||||
pthread_mutex_lock(&g_file_mutex);
|
||||
|
||||
if(0 == offset)
|
||||
{
|
||||
LOG_I("file_read: %s start", g_file_name.c_str());
|
||||
|
|
@ -259,6 +280,8 @@ void file_read(stru_icp67 *p_icp67, uint32_t id, uint32_t offset,
|
|||
{
|
||||
p_icp67->method.file_read_confirm_cb(p_icp67, 0, id, offset + len);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&g_file_mutex);
|
||||
}
|
||||
|
||||
/* ---- 写文件激活确认 (RTU → FTU 发送文件) ---- */
|
||||
|
|
@ -275,6 +298,8 @@ void file_write_act_confirm(stru_icp67 *p_icp67, uint8_t result,
|
|||
}
|
||||
|
||||
file_paths_init();
|
||||
|
||||
pthread_mutex_lock(&g_file_mutex);
|
||||
g_file_name = std::string((char *)p_name, len);
|
||||
|
||||
{
|
||||
|
|
@ -304,6 +329,8 @@ void file_write_act_confirm(stru_icp67 *p_icp67, uint8_t result,
|
|||
md5_update(&g_file_md5_ctx, file_data, frame_len);
|
||||
g_md5_started = 1;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&g_file_mutex);
|
||||
}
|
||||
|
||||
/* ---- 写文件确认 (继续发送后续帧) ---- */
|
||||
|
|
@ -313,6 +340,17 @@ void file_write_confirm(stru_icp67 *p_icp67, uint8_t result,
|
|||
{
|
||||
if(NULL == p_icp67) return;
|
||||
|
||||
/* 检查 FTU 返回的错误结果 */
|
||||
(void)result; /* TODO: FTU error handling — if result != FILE_WRITE_CONT_1/0, abort transfer */
|
||||
if(FILE_WRITE_CONT_0 != result && FILE_WRITE_CONT_1 != result)
|
||||
{
|
||||
LOG_E("file_write_confirm: FTU error result=%d, abort transfer", result);
|
||||
g_md5_started = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&g_file_mutex);
|
||||
|
||||
{
|
||||
std::string fpath = g_file_path + g_dir_name + "/" + g_file_name;
|
||||
uint32_t size = 0;
|
||||
|
|
@ -334,8 +372,6 @@ void file_write_confirm(stru_icp67 *p_icp67, uint8_t result,
|
|||
uint8_t file_data[FILE_TRANS_FRMAE_LEN] = {0};
|
||||
file_read(fpath.c_str(), offset, file_data, frame_len);
|
||||
|
||||
usleep(5000);
|
||||
|
||||
if(p_icp67->method.file_write_cb)
|
||||
{
|
||||
p_icp67->method.file_write_cb(p_icp67, id, offset, follow, file_data, frame_len);
|
||||
|
|
@ -347,7 +383,7 @@ void file_write_confirm(stru_icp67 *p_icp67, uint8_t result,
|
|||
}
|
||||
}
|
||||
|
||||
(void)result;
|
||||
pthread_mutex_unlock(&g_file_mutex);
|
||||
}
|
||||
|
||||
/* ---- 写文件结束 (MD5 校验) ---- */
|
||||
|
|
@ -360,6 +396,8 @@ void file_write_end(stru_icp67 *p_icp67, uint8_t result, uint32_t id,
|
|||
|
||||
if(NULL == p_icp67 || NULL == md5) return;
|
||||
|
||||
pthread_mutex_lock(&g_file_mutex);
|
||||
|
||||
if(g_md5_started)
|
||||
{
|
||||
md5_final(&g_file_md5_ctx, file_md5);
|
||||
|
|
@ -382,6 +420,8 @@ void file_write_end(stru_icp67 *p_icp67, uint8_t result, uint32_t id,
|
|||
LOG_E("file_write_end: %s failed", g_file_name.c_str());
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&g_file_mutex);
|
||||
|
||||
if(p_icp67->method.file_write_end_confirm_cb)
|
||||
{
|
||||
p_icp67->method.file_write_end_confirm_cb(p_icp67, final_result, id, file_md5);
|
||||
|
|
@ -398,6 +438,7 @@ void zip_dir_pop_out(stru_icp67 *p_icp67, uint8_t zip_type,
|
|||
{
|
||||
if(NULL == p_icp67 || NULL == zip_name || NULL == p_file_info) return;
|
||||
|
||||
pthread_mutex_lock(&g_file_mutex);
|
||||
g_zip_name = std::string((char *)zip_name, len);
|
||||
|
||||
LOG_I("zip_dir_pop_out: type=%d, name=%s, files=%d", zip_type, g_zip_name.c_str(), file_num);
|
||||
|
|
@ -405,6 +446,7 @@ void zip_dir_pop_out(stru_icp67 *p_icp67, uint8_t zip_type,
|
|||
{
|
||||
LOG_I(" [%d] %s (size=%d)", p_file_info[i].id, p_file_info[i].name, p_file_info[i].size);
|
||||
}
|
||||
pthread_mutex_unlock(&g_file_mutex);
|
||||
}
|
||||
|
||||
/* ---- 压缩文件内容 (FTU → RTU 本地写入 zip/) ---- */
|
||||
|
|
@ -416,12 +458,14 @@ void zip_file_pop_out(stru_icp67 *p_icp67, uint8_t id, uint32_t offset,
|
|||
|
||||
file_paths_init();
|
||||
|
||||
pthread_mutex_lock(&g_file_mutex);
|
||||
{
|
||||
std::string zip_fpath = g_zip_path + g_zip_name;
|
||||
file_write(zip_fpath.c_str(), offset, p_data, len);
|
||||
|
||||
if(0 == offset) LOG_I("zip_file_pop_out: %s start", g_zip_name.c_str());
|
||||
}
|
||||
pthread_mutex_unlock(&g_file_mutex);
|
||||
}
|
||||
|
||||
/* =========================== 实时数据 + 事件队列 =========================== */
|
||||
|
|
|
|||
|
|
@ -272,8 +272,39 @@ static int parse_param(XMLElement *root)
|
|||
while(nullptr != signal_ele)
|
||||
{
|
||||
stru_self_ptl_cfg_param cfg_param;
|
||||
const char *saddr = signal_ele->Attribute(attr_saddr);
|
||||
const char *desc = signal_ele->Attribute(attr_desc);
|
||||
int inf = 0;
|
||||
|
||||
if(XML_SUCCESS != signal_ele->QueryIntAttribute(attr_inf, &inf))
|
||||
{
|
||||
LOG_E("parse_param: inf parse failed");
|
||||
signal_ele = signal_ele->NextSiblingElement(ele_Signal);
|
||||
continue;
|
||||
}
|
||||
|
||||
const char *type = signal_ele->Attribute(attr_type);
|
||||
const char *unit = signal_ele->Attribute(attr_unit);
|
||||
const char *min = signal_ele->Attribute(attr_min);
|
||||
const char *max = signal_ele->Attribute(attr_max);
|
||||
const char *step = signal_ele->Attribute(attr_step);
|
||||
const char *value = signal_ele->Attribute(attr_value);
|
||||
const char *dflt = signal_ele->Attribute(attr_default);
|
||||
|
||||
cfg_param.base.saddr = (nullptr == saddr) ? "" : saddr;
|
||||
cfg_param.base.desc = (nullptr == desc) ? "" : desc;
|
||||
cfg_param.base.inf = inf;
|
||||
cfg_param.type = dc_get_type_id_by_name((nullptr == type) ? "" : type);
|
||||
cfg_param.unit = (nullptr == unit) ? "" : unit;
|
||||
cfg_param.min = (nullptr == min) ? "" : min;
|
||||
cfg_param.max = (nullptr == max) ? "" : max;
|
||||
cfg_param.step = (nullptr == step) ? "" : step;
|
||||
|
||||
cfg_param.val.push_back((nullptr == value) ? "" : value);
|
||||
cfg_param.default_value.push_back((nullptr == dflt) ? "" : dflt);
|
||||
|
||||
g_self_ptl_cfg.param_vec.push_back(cfg_param);
|
||||
|
||||
/* 与 Ao 使用相同的解析逻辑 */
|
||||
signal_ele = signal_ele->NextSiblingElement(ele_Signal);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue