/**
 * @file    self_ptl.cpp
 * @brief   自协议层核心 — 初始化、双协议解复用、信号注册、任务调度
 */

#include "mySystem.h"
#include "myLog.h"
#include "myCmd.h"
#include "myIcp67.h"
#include "myDatacenter.h"
#include "self_ptl.h"
#include "self_ptl_cfg.h"

#include <pthread.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <string>
#include <vector>

/* =========================== 前向声明 =========================== */

/* 来自 self_ptl_cb.cpp 的回调函数 */
extern void ao_pop_out(stru_icp67 *p_icp67, uint16_t addr, uint8_t type, uint8_t len, uint8_t *p_data);
extern void iec_point_tbl_pop_out(stru_icp67 *p_icp67, uint16_t info_addr, uint16_t num, uint8_t *p_data);
extern void self_check_pop_out(stru_icp67 *p_icp67, stru_data_info *p_data, uint16_t addr);
extern void dir_pop_out(stru_icp67 *p_icp67, uint8_t *p_name, uint8_t len, uint32_t attr, uint32_t size, uint8_t follow);
extern void file_read_act_confirm(stru_icp67 *p_icp67, uint8_t *p_name, uint8_t len, uint32_t id, uint32_t size, uint16_t crc);
extern void file_read(stru_icp67 *p_icp67, uint32_t id, uint32_t offset, uint32_t follow, uint8_t *p_data, uint16_t len);
extern void file_write_act_confirm(stru_icp67 *p_icp67, uint8_t result, uint8_t *p_name, uint8_t len, uint32_t id, uint32_t size);
extern void file_write_confirm(stru_icp67 *p_icp67, uint8_t result, uint32_t id, uint32_t offset);
extern void file_write_end(stru_icp67 *p_icp67, uint8_t result, uint32_t id, uint32_t offset, uint8_t *md5);
extern void zip_dir_pop_out(stru_icp67 *p_icp67, uint8_t zip_type, uint8_t *zip_name, uint8_t len, uint8_t file_num, stru_zip_file_info *p_file_info);
extern void zip_file_pop_out(stru_icp67 *p_icp67, uint8_t id, uint32_t offset, uint8_t *p_data, uint16_t len);
extern void mx_trans(stru_icp67 *p_icp67, uint16_t info_addr, float p_data);
extern void st_trans(stru_icp67 *p_icp67, uint16_t info_addr, uint8_t p_data);
extern void dd_trans(stru_icp67 *p_icp67, uint16_t info_addr, float p_data);
extern void soe_trans(stru_icp67 *p_icp67, stru_st_info *p_data);
extern void fault_trans(stru_icp67 *p_icp67, uint16_t num, stru_fault_info *p_data);
extern void mx_change(stru_icp67 *p_icp67, stru_mx_info *p_data);

/* =========================== 环形缓冲区 =========================== */

typedef struct
{
	uint16_t      rptr;
	uint16_t      wptr;
	uint16_t      size;
	uint16_t      cnt;
	uint8_t       buf[SELF_PTL_BUF_SIZE];
	pthread_mutex_t mutex;
}stru_self_rx;

/* =========================== 全局实例 =========================== */

typedef struct
{
	uint32_t             interface;
	stru_icp67           icp67;
	stru_genneral_method method;
	stru_self_rx         icp67_rx;
	uint8_t              temp_buf[SELF_PTL_BUF_SIZE];
}stru_self_ptl;

static stru_self_ptl g_self_ptl =
{
	.method =
	{
		.ao_get_cb                = NULL,
		.ao_pop_out_cb            = ao_pop_out,
		.ao_set_cb                = NULL,
		.iec_point_tbl_get_cb     = NULL,
		.iec_point_tbl_pop_out_cb = iec_point_tbl_pop_out,
		.time_set_cb              = NULL,
		.self_check_get_cb        = NULL,
		.self_check_pop_out_cb    = self_check_pop_out,
		.upgrade_start_cb         = NULL,

		.dir_read_cb               = NULL,
		.dir_pop_out_cb            = dir_pop_out,
		.file_read_act_cb          = NULL,
		.file_read_act_confirm_cb  = file_read_act_confirm,
		.file_read_cb              = file_read,
		.file_read_confirm_cb      = NULL,
		.file_write_act_cb         = NULL,
		.file_write_act_confirm_cb = file_write_act_confirm,
		.file_write_cb             = NULL,
		.file_write_confirm_cb     = file_write_confirm,
		.file_write_end_cb         = file_write_end,
		.file_write_end_confirm_cb = NULL,

		.zip_dir_pop_out_cb  = zip_dir_pop_out,
		.zip_file_pop_out_cb = zip_file_pop_out,

		.mx_trans_cb   = mx_trans,
		.st_trans_cb   = st_trans,
		.dd_trans_cb   = dd_trans,
		.soe_trans_cb  = soe_trans,
		.fault_trans_cb = fault_trans,
		.mx_change_cb  = mx_change,
	},
	.icp67_rx =
	{
		.rptr  = 0,
		.wptr  = 0,
		.size  = SELF_PTL_BUF_SIZE,
		.cnt   = 0,
		.mutex = PTHREAD_MUTEX_INITIALIZER,
	},
};

static stru_self_ptl_cfg_data g_self_ptl_cfg_data = {};

/* ---- 前向声明内部回调 (C++ linkage) ---- */
/* ao_pop_out 等回调在 self_ptl_cb.cpp 中实现 */

/* =========================== 环形缓冲操作 =========================== */

static void put_rx_data(stru_self_rx *p_self_rx, uint8_t *p_rx, uint16_t len)
{
	uint16_t i;

	if(NULL == p_self_rx || NULL == p_rx || 0 == len)
	{
		LOG_E("put_rx_data: invalid param");
		return;
	}

	pthread_mutex_lock(&p_self_rx->mutex);

	for(i = 0; i < len; i++)
	{
		p_self_rx->buf[p_self_rx->wptr] = p_rx[i];
		p_self_rx->wptr = (p_self_rx->wptr + 1) % p_self_rx->size;

		if(p_self_rx->cnt >= p_self_rx->size)
		{
			/* 环形缓冲区已满: 丢弃最旧字节, 只警告一次 */
			static int overflow_warned = 0;
			if(!overflow_warned)
			{
				LOG_E("put_rx_data: ring buffer overflow, dropping old data");
				overflow_warned = 1;
			}
			p_self_rx->rptr = (p_self_rx->rptr + 1) % p_self_rx->size;
		}
		else
		{
			p_self_rx->cnt++;
		}
	}

	pthread_mutex_unlock(&p_self_rx->mutex);
}

__attribute__((unused))
static void get_rx_data(stru_self_rx *p_self_rx, uint8_t *p_data, uint16_t *p_data_len)
{
	uint16_t pos;
	uint16_t i;

	if(NULL == p_self_rx || NULL == p_data || NULL == p_data_len)
	{
		LOG_E("get_rx_data: invalid param");
		return;
	}

	pthread_mutex_lock(&p_self_rx->mutex);

	pos = p_self_rx->rptr;
	for(i = 0; i < p_self_rx->cnt; i++)
	{
		p_data[i] = p_self_rx->buf[pos];
		pos = (pos + 1) % p_self_rx->size;
	}

	(*p_data_len) = p_self_rx->cnt;

	pthread_mutex_unlock(&p_self_rx->mutex);
}

/* get_rx_data 的无锁版本 (调用者已持锁) */
static void get_rx_data_locked(stru_self_rx *p_self_rx, uint8_t *p_data, uint16_t *p_data_len)
{
	uint16_t pos;
	uint16_t i;

	if(NULL == p_self_rx || NULL == p_data || NULL == p_data_len)
	{
		return;
	}

	pos = p_self_rx->rptr;
	for(i = 0; i < p_self_rx->cnt; i++)
	{
		p_data[i] = p_self_rx->buf[pos];
		pos = (pos + 1) % p_self_rx->size;
	}

	(*p_data_len) = p_self_rx->cnt;
}

__attribute__((unused))
static void clear_rx_data(stru_self_rx *p_self_rx)
{
	if(NULL == p_self_rx)
	{
		return;
	}

	pthread_mutex_lock(&p_self_rx->mutex);
	p_self_rx->rptr = 0;
	p_self_rx->wptr = 0;
	p_self_rx->cnt  = 0;
	pthread_mutex_unlock(&p_self_rx->mutex);
}

/* =========================== IEC-104 帧检测 =========================== */

static int self_ptl_iec104_check_ctrl(stru_iec104_apci *p)
{
	if(NULL == p)
	{
		return ERR_FRAME;
	}

	if(p->len > IEC_APCI_MIN_LEN)
	{
		if(0 == (p->ctrl1 & 0x01) && 0 == (p->ctrl3 & 0x01))
		{
			return I_FRAME;
		}
	}
	else
	{
		if(1 == p->ctrl1 && 0 == p->ctrl2 && 0 == (p->ctrl3 & 0x01))
		{
			return S_FRAME;
		}

		if(0x03 == (p->ctrl1 & 0x03) && 0 == p->ctrl2 && 0 == p->ctrl3 && 0 == p->ctrl4)
		{
			return U_FRAME;
		}
	}

	return ERR_FRAME;
}

static int self_ptl_search_iec_frame(uint8_t *p_data, uint16_t len, uint16_t *p_pos, uint16_t *p_valid_len)
{
	uint16_t i;

	if(NULL == p_data || NULL == p_pos || NULL == p_valid_len || 0 == len)
	{
		return -1;
	}

	for(i = 0; i < len; i++)
	{
		stru_iec104_apci *p_apci = (stru_iec104_apci *)(p_data + i);

		if(i + sizeof(stru_iec104_apci) > len)
		{
			break;
		}

		if(IEC_START_1 != p_apci->start)
		{
			continue;
		}

		if(p_apci->len + IEC_FRAME_OVERHEAD > (len - i))
		{
			break;
		}

		if(ERR_FRAME == self_ptl_iec104_check_ctrl(p_apci))
		{
			continue;
		}

		(*p_pos)       = i;
		(*p_valid_len) = p_apci->len + IEC_FRAME_OVERHEAD;

		return 0;
	}

	return -1;
}

/* =========================== 发送回调 (→ 通讯层) =========================== */

/**
 * @brief 发送回调 — ICP67 协议引擎通过此函数发送数据到物理通道
 * @param p_tx    发送数据
 * @param tx_len  发送长度
 * @param arg     指向 g_self_ptl.interface 的指针
 *
 * @note arg 指向 g_self_ptl.interface,
 *       该值在 self_ptl_data_rx() 中更新为当前数据来源通道的标识。
 *       通过 interface 可反向定位 com_router 中的通道进行发送。
 */
/**
 * @brief 发送回调 — ICP67 协议引擎 → MQ_SELF_PTL_TO_COM → com_router
 *
 * @note 使用 stru_msg_head 封装 interface+len+data,
 *       对齐原工程的消息格式。
 */
static void self_ptl_data_tx(uint8_t *p_tx, uint16_t tx_len, void *arg)
{
	uint32_t interface = *(uint32_t *)arg;
	stru_app_msg_queue *p_mq;
	stru_app *p_app;
	stru_msg_head *p_head;

	if(NULL == p_tx || 0 == tx_len)
	{
		LOG_E("self_ptl_data_tx: invalid param");
		return;
	}

	p_app = app_get_ptr(ENUM_APP_SELF_PTL);
	if(NULL == p_app)
	{
		LOG_E("self_ptl_data_tx: app_get_ptr failed");
		return;
	}

	p_mq = app_get_msg_queue_ptr(ENUM_MQ_SELF_PTL_TO_COM);
	if(NULL == p_mq || NULL == p_mq->p_msg_queue)
	{
		LOG_E("self_ptl_data_tx: MQ not found");
		return;
	}

	/* 使用线程本地 rtx.tx 缓冲区封装 stru_msg_head */
	p_head = (stru_msg_head *)p_app->rtx.tx;
	p_head->interface = interface;
	p_head->len = tx_len;
	memcpy(p_head->data, p_tx, tx_len);

	if(0 != task_msg_queue_send(p_mq->p_msg_queue, p_head, p_mq->msg_size))
	{
		LOG_E("self_ptl_data_tx: MQ send failed");
		return;
	}

	/* 通知 com_router */
	{
		stru_mq_target target = app_get_mq_target(ENUM_MQ_SELF_PTL_TO_COM);
		stru_app *p_router = app_get_ptr(target.app_id);

		if(NULL != p_router && NULL != p_router->p_event)
		{
			task_event_send(p_router->p_event, target.event);
		}
	}
}

/* =========================== 双协议解复用 =========================== */

/**
 * @brief 双协议帧搜索 — 零拷贝直接从环形缓冲区扫描
 *
 * @details 使用 icp67_search_frame_in_ring() 直接操作环形缓冲区,
 *          不再做 get_rx_data() 全量拷贝。
 *          处理完一帧后 advance ring pointer 滑窗。
 */
static void self_ptl_search_frame(uint8_t *p_rx, uint16_t len, stru_icp67 *p_icp67)
{
	uint16_t pos       = 0;
	uint16_t valid_len = 0;
	stru_self_rx *ring = &g_self_ptl.icp67_rx;
	uint8_t  iec_buf[SELF_PTL_BUF_SIZE];
	uint16_t iec_buf_len = 0;

	if(NULL == p_rx || 0 == len || NULL == p_icp67)
	{
		return;
	}

	/* 写入环形缓冲区 */
	put_rx_data(ring, p_rx, len);

	/* 一次持锁: ICP67 帧处理 + IEC 帧快照, 避免竞态 */
	pthread_mutex_lock(&ring->mutex);

	/* ICP67 帧搜索: 直接从环形缓冲读取, 零拷贝 */
	while(0 == icp67_search_frame_in_ring(ring->buf, ring->size,
		ring->rptr, ring->cnt, &pos, &valid_len))
	{
		uint8_t frame_buf[ICP67_TX_BUF_SIZE];
		uint16_t i;

		for(i = 0; i < valid_len && i < sizeof(frame_buf); i++)
		{
			frame_buf[i] = ring->buf[(ring->rptr + pos + i) % ring->size];
		}

		p_icp67->decode_cb(p_icp67, frame_buf, valid_len);

		/* 滑窗 */
		pos += valid_len;
		ring->cnt  -= pos;
		ring->rptr  = (ring->rptr + pos) % ring->size;
		pos = 0;
	}

	/* IEC-104 帧快照 (持锁下拷贝完整剩余数据) */
	get_rx_data_locked(ring, iec_buf, &iec_buf_len);

	pthread_mutex_unlock(&ring->mutex);

	/* IEC-104 帧搜索 (锁外执行, 操作快照数据) */
	if(iec_buf_len > 0)
	{
		if(0 == self_ptl_search_iec_frame(iec_buf, iec_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 解码模块 (待实现) */
		}
	}
}

/* =========================== 信号注册 =========================== */

static int self_ptl_do_signal_out(void)
{
	stru_self_ptl_cfg *p_cfg = self_ptl_cfg_get();

	if(NULL == p_cfg)
	{
		LOG_E("self_ptl_do_signal_out: cfg not parsed");
		return -1;
	}

	/* 注册遥信信号 (ST) */
	for(auto &cfg : p_cfg->st_vec)
	{
		uint8_t val = 0;

		dc_signal_out(cfg.saddr.c_str(), cfg.desc.c_str(), DATA_TYPE_U8, &val, MODULE_SELF_PTL);
	}

	/* 注册遥测信号 (MX) */
	for(auto &cfg : p_cfg->mx_vec)
	{
		float val = 0.0f;

		dc_signal_out(cfg.saddr.c_str(), cfg.desc.c_str(), DATA_TYPE_F32, &val, MODULE_SELF_PTL);
	}

	/* 注册遥控信号 (CO) */
	for(auto &cfg : p_cfg->co_vec)
	{
		uint8_t val = 0;

		dc_signal_yk(cfg.saddr.c_str(), cfg.desc.c_str(), DATA_TYPE_U8,
			DC_CTRL_SBO, &val, NULL, MODULE_SELF_PTL);
	}

	/* 注册电度信号 (DD) */
	for(auto &cfg : p_cfg->dd_vec)
	{
		float val = 0.0f;

		dc_signal_out(cfg.saddr.c_str(), cfg.desc.c_str(), DATA_TYPE_F32, &val, MODULE_SELF_PTL);
	}

	/* 注册参数信号 (AO) */
	for(auto &cfg : p_cfg->ao_vec)
	{
		float val = 0.0f;

		dc_signal_ao(cfg.base.saddr.c_str(), cfg.base.desc.c_str(), cfg.type,
			DC_CTRL_SBO, &val, NULL, MODULE_SELF_PTL);
	}

	/* 注册定值信号 (Param) */
	for(auto &cfg : p_cfg->param_vec)
	{
		float val = 0.0f;
			void *p_data_arr[1] = {&val};

		dc_signal_param(cfg.base.saddr.c_str(), cfg.base.desc.c_str(), cfg.type,
			DC_CTRL_SBO, p_data_arr, 1, NULL, MODULE_SELF_PTL);
	}

	LOG_I("self_ptl_do_signal_out: ST=%d, MX=%d, CO=%d, DD=%d, AO=%d, Param=%d",
		(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());

	return 0;
}

/* =========================== 初始化 =========================== */

int self_ptl_init(uint32_t interface, const char *xml_path)
{
	stru_icp67           *p_icp67  = &g_self_ptl.icp67;
	stru_genneral_method *p_method = &g_self_ptl.method;

	g_self_ptl.interface = interface;

	/* 初始化 ICP67 协议引擎 */
	icp67_init(p_icp67, self_ptl_data_tx, &g_self_ptl.interface, 0, 0);

	/* 解析 XML 点表配置 */
	if(NULL != xml_path)
	{
		stru_self_ptl_cfg *p_cfg;

		if(0 != self_ptl_cfg_init(xml_path))
		{
			LOG_E("self_ptl_init: cfg parse failed");
			return -1;
		}

		p_cfg = self_ptl_cfg_get();
		if(NULL != p_cfg && p_cfg->ao_vec.size() > 0)
		{
			icp67_set_ao_cfg_md5(p_icp67, p_cfg->ao_md5);
		}
	}

	/* 注册 pop_out/trans 类回调 (方向: icp67 → self_ptl) */
	icp67_set_ao_pop_out_cb(p_icp67, p_method->ao_pop_out_cb);
	icp67_set_iec_point_tbl_pop_out_cb(p_icp67, p_method->iec_point_tbl_pop_out_cb);
	icp67_set_self_check_pop_out_cb(p_icp67, p_method->self_check_pop_out_cb);

	icp67_set_dir_pop_out_cb(p_icp67, p_method->dir_pop_out_cb);
	icp67_set_file_read_act_confirm_cb(p_icp67, p_method->file_read_act_confirm_cb);
	icp67_set_file_read_cb(p_icp67, p_method->file_read_cb);
	icp67_set_file_write_act_confirm_cb(p_icp67, p_method->file_write_act_confirm_cb);
	icp67_set_file_write_confirm_cb(p_icp67, p_method->file_write_confirm_cb);
	icp67_set_file_write_end_cb(p_icp67, p_method->file_write_end_cb);

	icp67_set_zip_dir_pop_out_cb(p_icp67, p_method->zip_dir_pop_out_cb);
	icp67_set_zip_file_pop_out_cb(p_icp67, p_method->zip_file_pop_out_cb);

	icp67_set_mx_trans_cb(p_icp67, p_method->mx_trans_cb);
	icp67_set_st_trans_cb(p_icp67, p_method->st_trans_cb);
	icp67_set_dd_trans_cb(p_icp67, p_method->dd_trans_cb);
	icp67_set_soe_trans_cb(p_icp67, p_method->soe_trans_cb);
	icp67_set_fault_trans_cb(p_icp67, p_method->fault_trans_cb);
	icp67_set_mx_change_cb(p_icp67, p_method->mx_change_cb);

	/* 向数据中心注册信号 */
	if(NULL != xml_path)
	{
		if(0 != self_ptl_do_signal_out())
		{
			LOG_E("self_ptl_init: signal registration failed");
			return -1;
		}
	}

	LOG_I("self_ptl_init: ok, interface=%d", interface);

	return 0;
}

/**
 * @brief 数据接收入口 — 从通讯层投递原始帧数据
 * @param interface  通道标识 (id<<16)|fd, 用于多 FTU 场景区分来源
 * @param p_data     帧数据
 * @param len        帧长度
 *
 * @note 收到数据时更新 g_self_ptl.interface,
 *       后续 self_ptl_data_tx 通过 arg 指针自动使用最新的 interface。
 *       调用链是同步的: data_rx → decode → sender → send_cb,
 *       因此 interface 在单次处理周期内不会变化。
 */
void self_ptl_data_rx(uint32_t interface, uint8_t *p_data, uint16_t len)
{
	if(NULL == p_data || 0 == len)
	{
		return;
	}

	g_self_ptl.interface = interface; /* 多 FTU: 记录当前数据来源通道 */
	self_ptl_search_frame(p_data, len, &g_self_ptl.icp67);
}

/* =========================== 定时器任务 =========================== */

void self_ptl_task(void)
{
	stru_icp67 *p_icp67 = &g_self_ptl.icp67;

	if(NULL != p_icp67->timer_handler_cb)
	{
		p_icp67->timer_handler_cb(p_icp67);
	}
}

/* =========================== 应用线程入口 =========================== */

/* ---- init1: 解析 XML + 初始化协议 ---- */

int app_self_ptl_init1(void *arg)
{
	(void)arg;
	const char *xml_path = "config/SELF_PTL/self_ptl.xml";

	return self_ptl_init(0, xml_path);
}

/* ---- init2: 信号注册已在 init1 中完成 ---- */

int app_self_ptl_init2(void *arg)
{
	(void)arg;
	return 0;
}

/* ---- 线程主循环 ---- */

void *app_self_ptl(void *arg)
{
	stru_app *p_app = (stru_app *)arg;
	uint32_t  event;

	while(1)
	{
		task_event_recv(p_app->p_event,
			EV_TIMER1 | EV_TIMER2 | EV_TIMER3 | EV_SELF_PTL_RX_COM | EV_STOP,
			TASK_EVENT_FLAG_OR | TASK_EVENT_FLAG_CLEAR,
			100,
			&event);

		/* EV_SELF_PTL_RX_COM: com_router 通过 MQ_COM_TO_SELF_PTL 投递数据 */
		if(EV_SELF_PTL_RX_COM & event)
		{
			stru_app_msg_queue *p_mq = app_get_msg_queue_ptr(ENUM_MQ_COM_TO_SELF_PTL);

			if(NULL != p_mq && NULL != p_mq->p_msg_queue)
			{
				while(0 == task_msg_queue_try_recv(p_mq->p_msg_queue, p_app->rtx.rx, p_mq->msg_size))
				{
					stru_msg_head *p_head = (stru_msg_head *)p_app->rtx.rx;

					self_ptl_data_rx(p_head->interface, p_head->data, p_head->len);
				}
			}
		}

		/* EV_TIMER1 (10ms): ICP67 重传定时器 */
		if(EV_TIMER1 & event)
		{
			self_ptl_task();
		}

		/* EV_TIMER3 (1s): 运行计数 */
		if(EV_TIMER3 & event)
		{
			p_app->run_cnt++;
		}

		/* EV_STOP: 优雅退出 */
		if(EV_STOP & event)
		{
			LOG_I("app_self_ptl: received EV_STOP, exiting");
			break;
		}
	}

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