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:
ypc 2026-07-10 10:02:25 +08:00
parent ae1329aacc
commit 0409f427aa
8 changed files with 421 additions and 29 deletions

View File

@ -460,14 +460,14 @@ void icp67_timer_handler(stru_icp67 *p_icp67);
/** /**
* @brief ICP67 * @brief ICP67
* @param src * @param dst
* @param ti * @param ti
* @param cot * @param cot
* @param info_addr * @param info_addr
* @param p_tx () * @param p_tx ()
* @param tx_len () * @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) * @brief ICP67 (CRC + 0x16)

View File

@ -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_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->tm_out = p_icp67->tm_out_ms / ICP67_TM_TICK_MS;
p_icp67->resend_cnt = 0; p_icp67->resend_cnt = 0;
p_icp67->tm_out = p_icp67->tm_out_ms / ICP67_TM_TICK_MS;
p_icp67->tm_cnt = 0; p_icp67->tm_cnt = 0;
/* 初始化 sender 回调为内部函数 (方向: self_ptl → icp67) */ /* 初始化 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) 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; 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) 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; 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) 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; 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) 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; 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) 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; p_icp67->method.file_read_act_confirm_cb = cb;
} }
void icp67_set_file_read_cb(stru_icp67 *p_icp67, icp67_file_read_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; 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) 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; 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) 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; 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) 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; 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) 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; 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) 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; p_icp67->method.zip_file_pop_out_cb = cb;
} }
void icp67_set_mx_trans_cb(stru_icp67 *p_icp67, icp67_mx_trans_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; p_icp67->method.mx_trans_cb = cb;
} }
void icp67_set_st_trans_cb(stru_icp67 *p_icp67, icp67_st_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; p_icp67->method.st_trans_cb = cb;
} }
void icp67_set_dd_trans_cb(stru_icp67 *p_icp67, icp67_dd_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; p_icp67->method.dd_trans_cb = cb;
} }
void icp67_set_soe_trans_cb(stru_icp67 *p_icp67, icp67_soe_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; p_icp67->method.soe_trans_cb = cb;
} }
void icp67_set_fault_trans_cb(stru_icp67 *p_icp67, icp67_fault_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; p_icp67->method.fault_trans_cb = cb;
} }
void icp67_set_mx_change_cb(stru_icp67 *p_icp67, icp67_mx_change_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; p_icp67->method.mx_change_cb = cb;
} }

View File

@ -203,6 +203,7 @@ int icp67_decode(stru_icp67 *p_icp67, uint8_t *p_rx, uint16_t rx_len)
{ {
stru_head *p_head; stru_head *p_head;
icp67_decode_cb func; icp67_decode_cb func;
int ret;
if(NULL == p_icp67 || NULL == p_rx || 0 == rx_len) 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; return -1;
} }
func(p_icp67, p_rx, rx_len); ret = func(p_icp67, p_rx, rx_len);
return 0; return ret;
} }
/* =========================== TI_1: 读独立编码参数响应 =========================== */ /* =========================== 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; return -1;
} }
{
uint16_t asdu_len = (uint16_t)(rx_len - ICP67_FRAME_OVERHEAD); /* 减去帧头开销 */
pos = 0; pos = 0;
for(i = 0; p_icp67->method.ao_pop_out_cb && i < p_asdu->num; i++) 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; 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_icp67->method.ao_pop_out_cb(p_icp67, p_info->data_addr,
p_info->data_type, p_info->data_len, p_info->data); p_info->data_type, p_info->data_len, p_info->data);
} }
}
icp67_rtx_flag_set(p_icp67, ICP67_RX_FLAG, 0, 0); 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->total = p_asdu->total;
p_tx_asdu->frame_no = offset; p_tx_asdu->frame_no = offset;
p_tx_asdu->follow = p_asdu->follow; 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); 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; p_asdu = (stru_ti_6 *)p_head->data;
{
uint16_t asdu_len = (uint16_t)(rx_len - ICP67_FRAME_OVERHEAD);
pos = 0; pos = 0;
for(i = 0; p_icp67->method.self_check_pop_out_cb && i < p_asdu->st_num; i++) 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; pos += sizeof(stru_data_info) + p->data_len;
p_icp67->method.self_check_pop_out_cb(p_icp67, p, i); p_icp67->method.self_check_pop_out_cb(p_icp67, p, i);
} }
}
icp67_rtx_flag_set(p_icp67, ICP67_RX_FLAG, 0, 0); icp67_rtx_flag_set(p_icp67, ICP67_RX_FLAG, 0, 0);

View File

@ -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); 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; break;
} }
@ -192,8 +192,16 @@ void icp67_show_frame(uint8_t *p_data, uint16_t len)
p_head = (stru_head *)p_data; 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", printf("%s -> %s, len = %d, dev_addr %d\n",
str[p_head->src - 1], str[p_head->dst - 1], len, p_head->dev_addr); 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++) for(i = 0; i < len; i++)
{ {

View File

@ -173,9 +173,10 @@ void com_router_recv_cb(int id, int fd, const unsigned char *p_rx, int rx_len)
return; 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) 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; uint32_t interface = ((uint32_t)id << 16) | (uint32_t)fd;

View File

@ -5,6 +5,7 @@
#include "mySystem.h" #include "mySystem.h"
#include "myLog.h" #include "myLog.h"
#include "myCmd.h"
#include "myIcp67.h" #include "myIcp67.h"
#include "myDatacenter.h" #include "myDatacenter.h"
#include "self_ptl.h" #include "self_ptl.h"
@ -12,6 +13,8 @@
#include <pthread.h> #include <pthread.h>
#include <string.h> #include <string.h>
#include <time.h>
#include <stdlib.h>
#include <string> #include <string>
#include <vector> #include <vector>
@ -384,6 +387,7 @@ 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)) 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); 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; 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);

View File

@ -16,6 +16,7 @@
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include <pthread.h>
/* =========================== 文件操作辅助 =========================== */ /* =========================== 文件操作辅助 =========================== */
@ -24,6 +25,9 @@ static std::string g_file_path;
static std::string g_zip_path; static std::string g_zip_path;
static int g_paths_ready = 0; static int g_paths_ready = 0;
/* 文件操作全局状态互斥锁 (防止多 FTU 并发传输竞态) */
static pthread_mutex_t g_file_mutex = PTHREAD_MUTEX_INITIALIZER;
static void file_paths_init(void) static void file_paths_init(void)
{ {
char proc_dir[512] = {0}; char proc_dir[512] = {0};
@ -40,8 +44,16 @@ static void file_paths_init(void)
g_file_path = g_file_base_path + "file/"; g_file_path = g_file_base_path + "file/";
g_zip_path = g_file_base_path + "zip/"; g_zip_path = g_file_base_path + "zip/";
func_make_dirs(g_file_path.c_str()); if(0 != func_make_dirs(g_file_path.c_str()))
func_make_dirs(g_zip_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; g_paths_ready = 1;
LOG_I("file_paths_init: file=%s, zip=%s", g_file_path.c_str(), g_zip_path.c_str()); 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; if(NULL == p_icp67 || NULL == p_name) return;
pthread_mutex_lock(&g_file_mutex);
memcpy(dir_info.name, p_name, len); memcpy(dir_info.name, p_name, len);
dir_info.name[len] = '\0'; dir_info.name[len] = '\0';
dir_info.len = len; 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(); 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; if(NULL == p_icp67 || NULL == p_name) return;
file_paths_init(); file_paths_init();
pthread_mutex_lock(&g_file_mutex);
g_file_name = std::string((char *)p_name, len); g_file_name = std::string((char *)p_name, len);
LOG_I("file_read_act_confirm: name=%s, id=%d, size=%d, crc=0x%04X", 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()); func_make_dirs((g_file_path + g_dir_name).c_str());
unlink(fpath.c_str()); unlink(fpath.c_str());
} }
pthread_mutex_unlock(&g_file_mutex);
} }
/* ---- 读文件内容 (FTU → RTU 本地写入) ---- */ /* ---- 读文件内容 (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; if(NULL == p_icp67 || NULL == p_data) return;
pthread_mutex_lock(&g_file_mutex);
if(0 == offset) if(0 == offset)
{ {
LOG_I("file_read: %s start", g_file_name.c_str()); 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); p_icp67->method.file_read_confirm_cb(p_icp67, 0, id, offset + len);
} }
pthread_mutex_unlock(&g_file_mutex);
} }
/* ---- 写文件激活确认 (RTU → FTU 发送文件) ---- */ /* ---- 写文件激活确认 (RTU → FTU 发送文件) ---- */
@ -275,6 +298,8 @@ void file_write_act_confirm(stru_icp67 *p_icp67, uint8_t result,
} }
file_paths_init(); file_paths_init();
pthread_mutex_lock(&g_file_mutex);
g_file_name = std::string((char *)p_name, len); 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); md5_update(&g_file_md5_ctx, file_data, frame_len);
g_md5_started = 1; 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; 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; std::string fpath = g_file_path + g_dir_name + "/" + g_file_name;
uint32_t size = 0; 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}; uint8_t file_data[FILE_TRANS_FRMAE_LEN] = {0};
file_read(fpath.c_str(), offset, file_data, frame_len); file_read(fpath.c_str(), offset, file_data, frame_len);
usleep(5000);
if(p_icp67->method.file_write_cb) if(p_icp67->method.file_write_cb)
{ {
p_icp67->method.file_write_cb(p_icp67, id, offset, follow, file_data, frame_len); 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 校验) ---- */ /* ---- 写文件结束 (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; if(NULL == p_icp67 || NULL == md5) return;
pthread_mutex_lock(&g_file_mutex);
if(g_md5_started) if(g_md5_started)
{ {
md5_final(&g_file_md5_ctx, file_md5); 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()); 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) if(p_icp67->method.file_write_end_confirm_cb)
{ {
p_icp67->method.file_write_end_confirm_cb(p_icp67, final_result, id, file_md5); 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; 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); 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); 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); 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/) ---- */ /* ---- 压缩文件内容 (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(); file_paths_init();
pthread_mutex_lock(&g_file_mutex);
{ {
std::string zip_fpath = g_zip_path + g_zip_name; std::string zip_fpath = g_zip_path + g_zip_name;
file_write(zip_fpath.c_str(), offset, p_data, len); 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()); if(0 == offset) LOG_I("zip_file_pop_out: %s start", g_zip_name.c_str());
} }
pthread_mutex_unlock(&g_file_mutex);
} }
/* =========================== 实时数据 + 事件队列 =========================== */ /* =========================== 实时数据 + 事件队列 =========================== */

View File

@ -272,8 +272,39 @@ static int parse_param(XMLElement *root)
while(nullptr != signal_ele) while(nullptr != signal_ele)
{ {
stru_self_ptl_cfg_param cfg_param; 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); signal_ele = signal_ele->NextSiblingElement(ele_Signal);
} }