/**
 * @file    iec104.cpp
 * @brief   IEC 60870-5-104 服务端 — CS10x 初始化 + slot 管理 + 全部回调 + 收发桥接
 *
 * @details 聚合原工程 12 个子文件为单文件:
 *          - slot 管理: 3 路复用, 600s 超时自动回收
 *          - CS10x_104Init 绑定向 com_router 发送
 *          - 全部 CS10x 回调注册在 protocol_register_callbacks()
 *          - 环形缓冲区: soe(300) / disturb_yc(200) / fault(200) / dd(50)
 */

#include "mySystem.h"
#include "myLog.h"
#include "myFunc.h"
#include "myCmd.h"
#include "myDatacenter.h"
#include "iec104.h"
#include "iec104_cfg.h"

#include <string.h>
#include <time.h>
#include <semaphore.h>

/* 来自 iec104_point.cpp */
extern int iec104_point_init(void);

/* =========================== 常量 =========================== */

#define SOE_LEN           300     /**< SOE 记录缓冲区大小 */
#define DISTURB_YC_LEN    200     /**< 扰动遥测缓冲区大小 */
#define FAULT_EVENT_LEN   200     /**< 故障事件缓冲区大小 */
#define FAULT_YC_NUM       8      /**< 故障遥测最大数量 */
#define DD_LEN             50     /**< 电度记录缓冲区大小 */

#define YX_START_PORT   0x0001    /**< 遥信 IOA 起始 */
#define YC_START_PORT   0x4001    /**< 遥测 IOA 起始 */
#define DD_START_PORT   0x6401    /**< 电度 IOA 起始 */

#define LINK_OVER_TIME   600000   /**< 链路超时 600 秒 */

/* =========================== 环形缓冲区数据结构 =========================== */

/**
 * @brief 每通道输出指针 — 关联协议通道与环缓冲读取位置
 */
typedef struct
{
	uint8_t  protocol;    /**< 协议类型 */
	uint16_t out;         /**< 出指针 */
	uint32_t ch;          /**< 通道标识 */
} st_ring_out_ptr;

/**
 * @brief 扰动遥测环形缓冲区
 */
typedef struct
{
	bool                        init;
	sem_t                       sem;
	uint16_t                    in;
	std::vector<st_ring_out_ptr> out_ptrs;
	struct
	{
		uint16_t idx;
		float    val;
	} yc[DISTURB_YC_LEN];
} ring_disturb_yc_t;

/**
 * @brief SOE 事件环形缓冲区
 */
typedef struct
{
	bool                        init;
	sem_t                       sem;
	uint16_t                    in;
	std::vector<st_ring_out_ptr> out_ptrs;
	struct
	{
		uint8_t  st;
		uint8_t  ty;
		uint16_t pot;
		uint16_t ms;
		uint32_t sec;
	} soe[SOE_LEN];
} ring_soe_t;

/**
 * @brief 扰动电度环形缓冲区
 */
typedef struct
{
	bool                        init;
	sem_t                       sem;
	uint16_t                    in;
	std::vector<st_ring_out_ptr> out_ptrs;
	struct
	{
		uint16_t      idx;
		float         val;
		__CP56Time2a  tm;
		uint16_t      ms;
		uint32_t      sec;
	} dd[DD_LEN];
} ring_dd_t;

/**
 * @brief 故障事件环形缓冲区
 */
typedef struct
{
	bool                        init;
	sem_t                       sem;
	uint16_t                    in;
	std::vector<st_ring_out_ptr> out_ptrs;
	struct
	{
		iec_soe_info_t soe;
		uint8_t        yc_n;
		iec_yc_info_t  yc[FAULT_YC_NUM];
	} fault[FAULT_EVENT_LEN];
} ring_fault_t;

/* =========================== 全局变量 =========================== */

static stru_iec          g_iec[MAX_IEC_NUM];
static ring_disturb_yc_t g_yc_ring;
static ring_soe_t        g_soe_ring;
static ring_dd_t         g_dd_ring;
static ring_fault_t      g_fault_ring;

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

static int  iec104_data_tx(uint8_t *p_tx, uint16_t tx_len, void *arg);
static void protocol_init_104(stru_iec *p_iec);
static void protocol_register_callbacks(stru_iec *p_iec);
static void protocol_config(stru_iec *p_iec);

/* =========================== 环形缓冲工具 =========================== */

LOCAL void ring_sem_init(bool *p_init, sem_t *p_sem)
{
	if (!*p_init)
	{
		sem_init(p_sem, 0, 1);
		*p_init = true;
	}
}

LOCAL int ring_count(uint16_t in_ptr, uint16_t out_ptr, int size)
{
	if (in_ptr >= out_ptr)
	{
		return (int)(in_ptr - out_ptr);
	}

	return (int)(size - out_ptr + in_ptr);
}

LOCAL st_ring_out_ptr *ring_out_get(void *pv_param, std::vector<st_ring_out_ptr> &vec, uint8_t protocol)
{
	stru_iec *p_iec = iec104_get_by_interface(*(uint32_t *)pv_param);
	uint32_t  ch    = (NULL != p_iec) ? p_iec->interface : 0;
	uint32_t  idx;

	for (idx = 0; idx < vec.size(); idx++)
	{
		if (vec[idx].ch == ch && vec[idx].protocol == protocol)
		{
			return &vec[idx];
		}
	}

	st_ring_out_ptr new_ptr = { protocol, 0, ch };
	vec.push_back(new_ptr);
	return &vec.back();
}

LOCAL void cp56_from_ts(__CP56Time2a *p_time, uint32_t sec, uint16_t ms)
{
	struct tm st;
	uint16_t  total_ms;

	gmtime_r((time_t *)&sec, &st);
	total_ms = st.tm_sec * 1000 + ms;

	p_time->ucLMs   = total_ms & 0xFF;
	p_time->ucHMs   = (total_ms >> 8) & 0xFF;
	p_time->ucMin   = st.tm_min;
	p_time->ucHour  = st.tm_hour;
	p_time->ucWeek  = st.tm_mday;
	p_time->ucMonth = st.tm_mon + 1;
	p_time->ucYear  = st.tm_year - 100;
}

/* =========================== 时间 & COI 回调 =========================== */

LOCAL int cb_get_time(__CP56Time2a *p_time)
{
	struct timespec ts;
	struct tm       st;
	uint16_t        total_ms;

	if (NULL == p_time)
	{
		return 0;
	}

	clock_gettime(CLOCK_REALTIME, &ts);
	gmtime_r(&ts.tv_sec, &st);
	total_ms = st.tm_sec * 1000 + ts.tv_nsec / 1000000;

	p_time->ucLMs   = total_ms & 0xFF;
	p_time->ucHMs   = (total_ms >> 8) & 0xFF;
	p_time->ucMin   = st.tm_min;
	p_time->ucHour  = st.tm_hour;
	p_time->ucWeek  = st.tm_mday;
	p_time->ucMonth = st.tm_mon + 1;
	p_time->ucYear  = st.tm_year - 100;

	return 1;
}

LOCAL int cb_set_time(__CP56Time2a *p_time)
{
	struct tm       st;
	uint16_t        ms;
	struct timespec ts;

	if (NULL == p_time)
	{
		return 0;
	}

	memset(&st, 0, sizeof(st));
	st.tm_year = p_time->ucYear + 100;
	st.tm_mon  = p_time->ucMonth - 1;
	st.tm_mday = p_time->ucWeek;
	st.tm_hour = p_time->ucHour & 0x7F;
	st.tm_min  = p_time->ucMin & 0x3F;

	ms = p_time->ucLMs | ((p_time->ucHMs & 0x3F) << 8);

	ts.tv_sec  = timegm(&st);
	ts.tv_nsec = (long)(ms % 1000) * 1000000L;
	clock_settime(CLOCK_REALTIME, &ts);

	return 1;
}

LOCAL uint8_t cb_coi_read(void)
{
	return 0;
}

LOCAL void cb_coi_write(uint8_t coi)
{
	(void)coi;
}

/* =========================== YX (遥信) 回调 =========================== */

LOCAL int32_t cb_yx_count(uint16_t group, void *pv_param, uint32_t *p_start, uint32_t *p_end)
{
	std::vector<stru_iec_sig> &vec = iec_cfg_ptr_get()->vec_st;

	(void)group;
	(void)pv_param;

	*p_start = YX_START_PORT;
	*p_end   = *p_start + vec.size();

	return (int32_t)vec.size();
}

LOCAL int32_t cb_yx_val(uint16_t group, uint16_t idx, Yx_Info *pst_info, void *pv_param)
{
	std::vector<stru_iec_sig> &vec  = iec_cfg_ptr_get()->vec_st;
	stru_iec_sig              *p_sig;
	stru_iec                  *p_iec;

	(void)group;

	if (idx >= vec.size())
	{
		return 0;
	}

	p_iec = iec104_get_by_interface(*(uint32_t *)pv_param);
	p_sig = &vec[idx];

	pst_info->ucStatus    = (NULL != p_sig->p_data) ? *(uint8_t *)p_sig->p_data : 0;
	pst_info->uiInfoAddr  = p_sig->inf;
	pst_info->ucStateType = 0;

	/* 双点遥信 */
	if (NULL != p_iec && p_iec->cs10x.usYxType)
	{
		pst_info->ucStateType = 1;

		if (p_sig->invert)
		{
			if (pst_info->ucStatus == 2)
			{
				pst_info->ucStatus = 1;
			}
			else if (pst_info->ucStatus == 1)
			{
				pst_info->ucStatus = 2;
			}
		}
	}
	/* 单点遥信 + 取反 */
	else if (p_sig->invert)
	{
		pst_info->ucStatus = pst_info->ucStatus ? 0 : 1;
	}

	return 1;
}

/* =========================== YC (遥测) 回调 =========================== */

LOCAL int32_t cb_yc_count(uint16_t group, void *pv_param, uint32_t *p_start, uint32_t *p_end)
{
	std::vector<stru_iec_sig> &vec = iec_cfg_ptr_get()->vec_mx;

	(void)group;
	(void)pv_param;

	*p_start = YC_START_PORT;
	*p_end   = *p_start + vec.size();

	return (int32_t)vec.size();
}

LOCAL int32_t cb_yc_val(uint16_t group, uint16_t idx, Yc_Info *pst_info, void *pv_param)
{
	std::vector<stru_iec_sig> &vec  = iec_cfg_ptr_get()->vec_mx;
	stru_iec_sig              *p_sig;
	float                      raw_val;

	(void)group;
	(void)pv_param;

	if (idx >= vec.size())
	{
		return 0;
	}

	p_sig   = &vec[idx];
	raw_val = (NULL != p_sig->p_data) ? *(float *)p_sig->p_data : 0.0f;

	pst_info->fVal       = raw_val * p_sig->factor + p_sig->offset;
	pst_info->uiInfoAddr = p_sig->inf;

	return 1;
}

/* ---- 扰动遥测写入 (iec104_point.cpp 调用) ---- */

int cb_yc_disturb_write(uint16_t idx, float val)
{
	idx += YC_START_PORT;

	ring_sem_init(&g_yc_ring.init, &g_yc_ring.sem);
	sem_wait(&g_yc_ring.sem);

	g_yc_ring.yc[g_yc_ring.in].idx = idx;
	g_yc_ring.yc[g_yc_ring.in].val = val;
	g_yc_ring.in = (g_yc_ring.in + 1) % DISTURB_YC_LEN;

	sem_post(&g_yc_ring.sem);
	return 1;
}

/* ---- 扰动遥测读取 (CS10x 回调) ---- */

LOCAL int32_t cb_yc_disturb_get(Yc_Info *pst_info, void *pv_param, uint32_t pos)
{
	st_ring_out_ptr *p_out = ring_out_get(pv_param, g_yc_ring.out_ptrs, 1);
	uint16_t         read_idx;

	ring_sem_init(&g_yc_ring.init, &g_yc_ring.sem);
	sem_wait(&g_yc_ring.sem);

	read_idx = (pos + p_out->out) % DISTURB_YC_LEN;
	pst_info->uiInfoAddr = g_yc_ring.yc[read_idx].idx;
	pst_info->fVal       = g_yc_ring.yc[read_idx].val;

	sem_post(&g_yc_ring.sem);
	return 1;
}

LOCAL int32_t cb_yc_disturb_num(void *pv_param)
{
	st_ring_out_ptr *p_out = ring_out_get(pv_param, g_yc_ring.out_ptrs, 1);
	int              num;

	ring_sem_init(&g_yc_ring.init, &g_yc_ring.sem);
	sem_wait(&g_yc_ring.sem);

	num = ring_count(g_yc_ring.in, p_out->out, DISTURB_YC_LEN);

	sem_post(&g_yc_ring.sem);
	return num;
}

LOCAL int32_t cb_yc_disturb_upd(void *pv_param, uint32_t pos)
{
	st_ring_out_ptr *p_out;

	if (0 == pos || NULL == pv_param)
	{
		return 0;
	}

	p_out = ring_out_get(pv_param, g_yc_ring.out_ptrs, 1);
	ring_sem_init(&g_yc_ring.init, &g_yc_ring.sem);
	sem_wait(&g_yc_ring.sem);

	p_out->out = (p_out->out + pos) % DISTURB_YC_LEN;

	sem_post(&g_yc_ring.sem);
	return 1;
}

/* =========================== SOE 事件回调 =========================== */

int cb_soe_write(uint16_t idx, uint8_t status, uint32_t sec, uint16_t ms)
{
	idx += YX_START_PORT;

	ring_sem_init(&g_soe_ring.init, &g_soe_ring.sem);
	sem_wait(&g_soe_ring.sem);

	g_soe_ring.soe[g_soe_ring.in].st  = status;
	g_soe_ring.soe[g_soe_ring.in].pot = idx;
	g_soe_ring.soe[g_soe_ring.in].sec = sec;
	g_soe_ring.soe[g_soe_ring.in].ms  = ms;
	g_soe_ring.soe[g_soe_ring.in].ty  = 0;
	g_soe_ring.in = (g_soe_ring.in + 1) % SOE_LEN;

	sem_post(&g_soe_ring.sem);
	return 1;
}

LOCAL int32_t cb_soe_get(SOE_Info *pst_info, void *pv_param, uint32_t pos)
{
	st_ring_out_ptr *p_out = ring_out_get(pv_param, g_soe_ring.out_ptrs, 2);
	uint16_t         read_idx;

	ring_sem_init(&g_soe_ring.init, &g_soe_ring.sem);
	sem_wait(&g_soe_ring.sem);

	read_idx = (pos + p_out->out) % SOE_LEN;

	pst_info->uiInfoAddr  = g_soe_ring.soe[read_idx].pot;
	pst_info->ucStatus    = g_soe_ring.soe[read_idx].st;
	pst_info->ucStateType = g_soe_ring.soe[read_idx].ty;

	cp56_from_ts(&pst_info->stCTime, g_soe_ring.soe[read_idx].sec, g_soe_ring.soe[read_idx].ms);

	sem_post(&g_soe_ring.sem);
	return 1;
}

LOCAL int32_t cb_soe_num(void *pv_param)
{
	st_ring_out_ptr *p_out = ring_out_get(pv_param, g_soe_ring.out_ptrs, 2);
	int              num;

	ring_sem_init(&g_soe_ring.init, &g_soe_ring.sem);
	sem_wait(&g_soe_ring.sem);

	num = ring_count(g_soe_ring.in, p_out->out, SOE_LEN);

	sem_post(&g_soe_ring.sem);
	return num;
}

LOCAL int32_t cb_soe_upd(void *pv_param, uint32_t pos)
{
	st_ring_out_ptr *p_out;

	if (0 == pos || NULL == pv_param)
	{
		return 0;
	}

	p_out = ring_out_get(pv_param, g_soe_ring.out_ptrs, 2);
	ring_sem_init(&g_soe_ring.init, &g_soe_ring.sem);
	sem_wait(&g_soe_ring.sem);

	p_out->out = (p_out->out + pos) % SOE_LEN;

	sem_post(&g_soe_ring.sem);
	return 1;
}

/* =========================== DD (电度) 回调 =========================== */

LOCAL int32_t cb_dd_count(uint16_t group, void *pv_param, uint32_t *p_start, uint32_t *p_end, uint8_t time_en)
{
	std::vector<stru_iec_sig> &vec  = iec_cfg_ptr_get()->vec_dd;
	uint32_t                    num = (uint32_t)vec.size();
	int                         split;

	(void)pv_param;

	if (group == 0)
	{
		if (time_en)
		{
			return 0;
		}

		*p_start = DD_START_PORT;
		*p_end   = *p_start + num;

		return (int32_t)num;
	}

	/* group 1: 前 8 个不带时标, 其余带时标 */
	split = 8;

	if (time_en)
	{
		*p_start = DD_START_PORT + split;
		split    = (int)num - split;
		*p_end   = *p_start + split;

		return (split > 0) ? split : 0;
	}

	*p_start = DD_START_PORT;
	*p_end   = *p_start + split;

	return split;
}

LOCAL int32_t cb_dd_val(uint16_t group, uint16_t idx, TimeDd_Info *pst_info, void *pv_param, uint8_t time_en)
{
	std::vector<stru_iec_sig> &vec  = iec_cfg_ptr_get()->vec_dd;
	stru_iec_sig              *p_sig;

	(void)group;
	(void)time_en;
	(void)pv_param;

	if (idx >= vec.size())
	{
		return 0;
	}

	p_sig = &vec[idx];

	pst_info->uiInfoAddr = p_sig->inf;
	pst_info->fVal       = (NULL != p_sig->p_data) ? *(float *)p_sig->p_data : 0.0f;

	memset(&pst_info->stCTime, 0, sizeof(pst_info->stCTime));

	return 1;
}

/* ---- 扰动电度写入 (iec104_point.cpp 调用) ---- */

int cb_dd_disturb_write(uint16_t idx, float val, uint32_t sec, uint16_t ms)
{
	idx += DD_START_PORT;

	ring_sem_init(&g_dd_ring.init, &g_dd_ring.sem);
	sem_wait(&g_dd_ring.sem);

	g_dd_ring.dd[g_dd_ring.in].idx = idx;
	g_dd_ring.dd[g_dd_ring.in].val = val;
	g_dd_ring.dd[g_dd_ring.in].sec = sec;
	g_dd_ring.dd[g_dd_ring.in].ms  = ms;
	g_dd_ring.in = (g_dd_ring.in + 1) % DD_LEN;

	sem_post(&g_dd_ring.sem);
	return 1;
}

/* ---- 扰动电度读取 (CS10x 回调) ---- */

LOCAL int32_t cb_dd_disturb_get(TimeDd_Info *pst_info, void *pv_param, uint32_t pos)
{
	st_ring_out_ptr *p_out = ring_out_get(pv_param, g_dd_ring.out_ptrs, 3);
	uint16_t         read_idx;

	ring_sem_init(&g_dd_ring.init, &g_dd_ring.sem);
	sem_wait(&g_dd_ring.sem);

	read_idx = (pos + p_out->out) % DD_LEN;

	pst_info->uiInfoAddr = g_dd_ring.dd[read_idx].idx;
	pst_info->fVal       = g_dd_ring.dd[read_idx].val;

	cp56_from_ts(&pst_info->stCTime, g_dd_ring.dd[read_idx].sec, g_dd_ring.dd[read_idx].ms);

	sem_post(&g_dd_ring.sem);
	return 1;
}

LOCAL int32_t cb_dd_disturb_num(void *pv_param)
{
	st_ring_out_ptr *p_out = ring_out_get(pv_param, g_dd_ring.out_ptrs, 3);
	int              num;

	ring_sem_init(&g_dd_ring.init, &g_dd_ring.sem);
	sem_wait(&g_dd_ring.sem);

	num = ring_count(g_dd_ring.in, p_out->out, DD_LEN);

	sem_post(&g_dd_ring.sem);
	return num;
}

LOCAL int32_t cb_dd_disturb_upd(void *pv_param, uint32_t pos)
{
	st_ring_out_ptr *p_out;

	if (0 == pos || NULL == pv_param)
	{
		return 0;
	}

	p_out = ring_out_get(pv_param, g_dd_ring.out_ptrs, 3);
	ring_sem_init(&g_dd_ring.init, &g_dd_ring.sem);
	sem_wait(&g_dd_ring.sem);

	p_out->out = (p_out->out + pos) % DD_LEN;

	sem_post(&g_dd_ring.sem);
	return 1;
}

/* =========================== 故障事件回调 =========================== */

int cb_fault_write(iec_soe_info_t *p_soe, uint8_t yx_n, iec_yc_info_t *p_yc, uint8_t yc_n)
{
	uint8_t j;

	if (NULL == p_soe)
	{
		return 0;
	}

	ring_sem_init(&g_fault_ring.init, &g_fault_ring.sem);
	sem_wait(&g_fault_ring.sem);

	g_fault_ring.fault[g_fault_ring.in].soe = *p_soe;

	if (yc_n > FAULT_YC_NUM)
	{
		yc_n = FAULT_YC_NUM;
	}

	g_fault_ring.fault[g_fault_ring.in].yc_n = yc_n;

	for (j = 0; NULL != p_yc && j < yc_n; j++)
	{
		g_fault_ring.fault[g_fault_ring.in].yc[j] = p_yc[j];
	}

	g_fault_ring.in = (g_fault_ring.in + 1) % FAULT_EVENT_LEN;

	sem_post(&g_fault_ring.sem);
	(void)yx_n;
	return 1;
}

LOCAL int32_t cb_fault_get(FAULT_EVENT_Info *pst_info, void *pv_param, uint32_t pos)
{
	st_ring_out_ptr *p_out = ring_out_get(pv_param, g_fault_ring.out_ptrs, 4);
	uint16_t         read_idx;
	uint8_t          j;

	ring_sem_init(&g_fault_ring.init, &g_fault_ring.sem);
	sem_wait(&g_fault_ring.sem);

	read_idx = (pos + p_out->out) % FAULT_EVENT_LEN;

	pst_info->staSoe[0].uiInfoAddr  = g_fault_ring.fault[read_idx].soe.info_addr;
	pst_info->staSoe[0].ucStatus    = g_fault_ring.fault[read_idx].soe.status;
	pst_info->staSoe[0].ucStateType = g_fault_ring.fault[read_idx].soe.state_type;
	memset(&pst_info->staSoe[0].stCTime, 0, sizeof(pst_info->staSoe[0].stCTime));

	pst_info->usYcNum = g_fault_ring.fault[read_idx].yc_n;

	for (j = 0; j < pst_info->usYcNum && j < FAULT_YC_NUM; j++)
	{
		pst_info->staYc[j].uiInfoAddr = g_fault_ring.fault[read_idx].yc[j].info_addr;
		pst_info->staYc[j].fVal       = g_fault_ring.fault[read_idx].yc[j].val;
	}

	sem_post(&g_fault_ring.sem);
	return 1;
}

LOCAL int32_t cb_fault_num(void *pv_param)
{
	st_ring_out_ptr *p_out = ring_out_get(pv_param, g_fault_ring.out_ptrs, 4);
	int              num;

	ring_sem_init(&g_fault_ring.init, &g_fault_ring.sem);
	sem_wait(&g_fault_ring.sem);

	num = ring_count(g_fault_ring.in, p_out->out, FAULT_EVENT_LEN);

	sem_post(&g_fault_ring.sem);
	return num;
}

LOCAL int32_t cb_fault_upd(void *pv_param, uint32_t pos)
{
	st_ring_out_ptr *p_out;

	if (0 == pos || NULL == pv_param)
	{
		return 0;
	}

	p_out = ring_out_get(pv_param, g_fault_ring.out_ptrs, 4);
	ring_sem_init(&g_fault_ring.init, &g_fault_ring.sem);
	sem_wait(&g_fault_ring.sem);

	p_out->out = (p_out->out + pos) % FAULT_EVENT_LEN;

	sem_post(&g_fault_ring.sem);
	return 1;
}

/* =========================== YK (遥控) 回调 =========================== */

LOCAL uint8_t cb_yk_verify(void *pv_param)
{
	(void)pv_param;
	return 0;
}

LOCAL int cb_yk_set(Yk_Info *pst_info, void *pv_param)
{
	std::vector<stru_iec_sig> &vec = iec_cfg_ptr_get()->vec_co;
	uint32_t                    idx;

	(void)pv_param;

	for (idx = 0; idx < vec.size(); idx++)
	{
		if (vec[idx].inf != pst_info->uiInfoAddr || NULL == vec[idx].p_data)
		{
			continue;
		}

		/* 单点遥控: 状态值直接写入 */
		if (0 == ((stru_iec *)pv_param)->cs10x.usYxType)
		{
			*(uint8_t *)vec[idx].p_data = pst_info->ucStatus;
		}
		/* 双点遥控: 1→0, 2→1 */
		else
		{
			if (pst_info->ucStatus == 1)
			{
				*(uint8_t *)vec[idx].p_data = 0;
			}
			else if (pst_info->ucStatus == 2)
			{
				*(uint8_t *)vec[idx].p_data = 1;
			}
			else
			{
				*(uint8_t *)vec[idx].p_data = pst_info->ucStatus;
			}
		}
	}

	return 0;
}

/* =========================== Param (定值/参数) 回调 =========================== */

LOCAL uint16_t cb_param_area(uint8_t type, void *p_handle)
{
	(void)type;
	(void)p_handle;
	return 1;
}

LOCAL uint16_t cb_param_set_area(uint16_t area, void *p_handle)
{
	(void)p_handle;
	return area;
}

LOCAL uint8_t cb_param_add_iec(uint8_t all, uint8_t force, uint8_t type, IECPARAM_T *pst_val, void *p_param)
{
	(void)all;
	(void)force;
	(void)type;
	(void)pst_val;
	(void)p_param;
	return 1;
}

LOCAL uint8_t cb_param_rd_run(void *p_param)
{
	(void)p_param;
	return 1;
}

LOCAL uint8_t cb_param_wt_run(void *p_param)
{
	(void)p_param;
	return 1;
}

LOCAL uint16_t cb_param_total(void *p_param)
{
	std::vector<stru_iec_sig> &vec = iec_cfg_ptr_get()->vec_ao;

	(void)p_param;

	return (uint16_t)vec.size();
}

LOCAL uint8_t cb_param_get(uint8_t type, uint16_t idx, IECPARAM_T *pst_val, void *p_param)
{
	std::vector<stru_iec_sig> &vec = iec_cfg_ptr_get()->vec_ao;

	(void)type;
	(void)p_param;

	if (idx >= vec.size())
	{
		return 0;
	}

	pst_val->uiInfoAddr = vec[idx].inf;
	pst_val->ucLen      = 1;

	return 1;
}

/* =========================== 文件服务回调 (空桩) =========================== */

LOCAL bool cb_dir_get(tFILEDIR_RD_SET *pst_set, tDIR_FILE_INFO *pst_info, uint8_t type, uint16_t item, void *info)
{
	(void)pst_set;
	(void)pst_info;
	(void)type;
	(void)item;
	(void)info;
	return false;
}

LOCAL int cb_dir_num(tFILEDIR_RD_SET *pst_set)
{
	(void)pst_set;
	return 0;
}

LOCAL int cb_file_read(uint32_t offset, uint8_t *p_buf, uint16_t len, void *pv_param)
{
	(void)offset;
	(void)p_buf;
	(void)len;
	(void)pv_param;
	return 0;
}

LOCAL uint8_t cb_file_wr_check(tFILE_WD_SET *pst_set, void *p_param, void *p_user)
{
	(void)pst_set;
	(void)p_param;
	(void)p_user;
	return 0;
}

LOCAL int cb_file_write(uint32_t offset, uint8_t *p_buf, uint16_t len, void *p_file, void *p_user)
{
	(void)offset;
	(void)p_buf;
	(void)len;
	(void)p_file;
	(void)p_user;
	return 0;
}

LOCAL int cb_file_wr_finish(uint8_t *p_md5, void *p_file, void *p_user)
{
	(void)p_md5;
	(void)p_file;
	(void)p_user;
	return 0;
}

/* =========================== 协议栈初始化 =========================== */

LOCAL void protocol_init_104(stru_iec *p_iec)
{
	struct CS10x_AppLayerParameters app_param;
	struct CS104_APCIParameters     apci_param;

	/* 应用层参数 */
	memset(&app_param, 0, sizeof(app_param));
	app_param.iSzOfVSQ       = 1;
	app_param.iLinkAddr      = 1;
	app_param.iSzOfCOT       = 2;
	app_param.iSzOfCA        = 2;
	app_param.iPublicAddr    = 1;
	app_param.iMaxSzOfAsduLen = 249;
	app_param.iSzOfIOA       = 3;
	CS10x_SetAppParameters(&p_iec->cs10x, &app_param);

	/* APCI 参数 */
	memset(&apci_param, 0, sizeof(apci_param));
	apci_param.iK  = 12;
	apci_param.iW  = 8;
	apci_param.iT0 = 10;
	apci_param.iT1 = 12;
	apci_param.iT2 = 8;
	apci_param.iT3 = 15;
	CS10x_SetAPCIParameters(&p_iec->cs10x, &apci_param);

	/* 数据类型配置 */
	CS10x_SetYcType(&p_iec->cs10x, 2);          /* 浮点遥测 */
	CS10x_SetYxType(&p_iec->cs10x, 0);          /* 单点遥信 */
	CS10x_SetDdType(&p_iec->cs10x, 1);
	CS10x_FrameGap(&p_iec->cs10x, 10);          /* 帧间隔 */
	CS10x_SetMultFrameEn(&p_iec->cs10x, 0);      /* 多帧不使能 */
	CS10x_FaultEventEn(&p_iec->cs10x, 0);        /* 故障事件不使能 */
	CS10x_SetSendSoe(&p_iec->cs10x, 1);          /* SOE 发送使能 */
	CS10x_SetSendCos(&p_iec->cs10x, 1);          /* COS 发送使能 */
	CS10x_SetYKEendFrame(&p_iec->cs10x, 0);      /* YK 结束帧不使能 */
	CS10x_SetReadFileVSQ(&p_iec->cs10x, 0);      /* 读文件 VSQ */
	CS10x_SetPresetContType(&p_iec->cs10x, 0);   /* 参数预置 */
}

LOCAL void protocol_register_callbacks(stru_iec *p_iec)
{
	CS10x *p_cs = &p_iec->cs10x;

	/* 时间 & 复位 */
	CS10x_TimeRWHandler(p_cs, cb_get_time, cb_set_time);
	CS10x_WR_ResetHandler(p_cs, cb_coi_read, cb_coi_write);

	/* 遥测 */
	CS10x_S_GetYcByGroupHandler(p_cs, cb_yc_count, cb_yc_val);
	CS10x_S_DiturbYcHandler(p_cs, cb_yc_disturb_get, cb_yc_disturb_num, cb_yc_disturb_upd);

	/* 遥信 */
	CS10x_S_GetYxByGroupHandler(p_cs, cb_yx_count, cb_yx_val);

	/* SOE */
	CS10x_S_SOEHandler(p_cs, cb_soe_get, cb_soe_num, cb_soe_upd);

	/* 电度 */
	CS10x_S_GetDdByGroupHandler(p_cs, cb_dd_count, cb_dd_val);
	CS10x_S_DiturbDduHandler(p_cs, cb_dd_disturb_get, cb_dd_disturb_num, cb_dd_disturb_upd);

	/* 故障事件 */
	CS10x_S_FaultEventHandler(p_cs, cb_fault_num, cb_fault_get, cb_fault_upd);

	/* 遥控 */
	CS10x_S_YkVerifyHandler(p_cs, cb_yk_verify);
	CS10x_SetYkInforHandler(p_cs, cb_yk_set);

	/* 定值/参数 */
	CS10x_S_ParamAreaHandler(p_cs, cb_param_area, cb_param_set_area);
	CS10x_S_IECParamHandler(p_cs, cb_param_add_iec, cb_param_rd_run, cb_param_wt_run);
	CS10x_S_GetParamInfoHandler(p_cs, cb_param_total, cb_param_get);

	/* 文件服务 */
	CS10x_S_FileReadOprtHandler(p_cs, cb_dir_get, cb_dir_num, cb_file_read, NULL);
	CS10x_S_FileWriteOprtHandler(p_cs, cb_file_write, cb_file_wr_check, cb_file_wr_finish, NULL);
	CS10x_S_UpdateProcHandler(p_cs, NULL);
}

LOCAL void protocol_config(stru_iec *p_iec)
{
	CS10x_104Init(&p_iec->cs10x, iec104_data_tx, (void *)&p_iec->interface, CS104_TYPE_S);
	protocol_init_104(p_iec);
	protocol_register_callbacks(p_iec);
}

/* =========================== Slot 管理 =========================== */

LOCAL stru_iec *iec104_ptr_get(uint32_t interface, CS10x_Type type)
{
	uint32_t max_tm   = 0;
	uint8_t  max_idx  = 0;
	uint8_t  free_idx = MAX_IEC_NUM;
	uint8_t  i;
	uint8_t  idx;
	stru_iec *p_iec;

	for (i = 0; i < MAX_IEC_NUM; i++)
	{
		if (interface == g_iec[i].interface)
		{
			if (g_iec[i].is_busy)
			{
				g_iec[i].tm_out = 0;

				if (type == g_iec[i].iec_type)
				{
					return &g_iec[i];
				}

				g_iec[i].is_busy = 0;
			}
			else
			{
				free_idx = i;
			}
		}

		if (max_tm < g_iec[i].tm_out)
		{
			max_tm  = g_iec[i].tm_out;
			max_idx = i;
		}

		if (!g_iec[i].is_busy && free_idx == MAX_IEC_NUM)
		{
			free_idx = i;
		}
	}

	/* 确定用哪个 slot */
	idx   = (free_idx < MAX_IEC_NUM) ? free_idx : max_idx;
	p_iec = &g_iec[idx];

	/* 初始化 slot */
	memset(p_iec, 0, sizeof(stru_iec));
	p_iec->is_busy    = 1;
	p_iec->interface  = interface;
	p_iec->iec_type   = type;

	/* 绑定缓冲区 */
	p_iec->cs10x.stRecvBuf.pBuf        = p_iec->rx;
	p_iec->cs10x.stRecvBuf.usBufSize   = IEC_BUF_SIZE;
	p_iec->cs10x.stSendBuf.pBuf        = p_iec->tx;
	p_iec->cs10x.stSendBuf.usBufSize   = IEC_BUF_SIZE;
	p_iec->cs10x.stPubBuf.pBuf         = p_iec->pub;
	p_iec->cs10x.stPubBuf.usBufSize    = IEC_BUF_SIZE;
	p_iec->cs10x.stSendBakBuf.pBuf     = p_iec->bak;
	p_iec->cs10x.stSendBakBuf.usBufSize = IEC_BUF_SIZE;

	protocol_config(p_iec);

	LOG_I("iec104: slot[%d] inited, interface=0x%X", idx, interface);
	return p_iec;
}

stru_iec *iec104_get_by_interface(uint32_t interface)
{
	uint8_t i;

	for (i = 0; i < MAX_IEC_NUM; i++)
	{
		if (interface == g_iec[i].interface && g_iec[i].is_busy)
		{
			return &g_iec[i];
		}
	}

	return NULL;
}

/* =========================== 收发桥接 (MQ 模式) =========================== */

LOCAL int iec104_data_tx(uint8_t *p_tx, uint16_t tx_len, void *arg)
{
	stru_app           *p_app;
	stru_app_msg_queue *p_mq;
	stru_msg_head      *p_head;
	stru_mq_target      target;
	stru_app           *p_target;
	uint32_t            interface;

	if (tx_len + sizeof(stru_msg_head) > MSG_SIZE)
	{
		LOG_E("iec104: tx_len %d > MSG_SIZE", tx_len);
		return -1;
	}

	interface = *(uint32_t *)arg;

	p_app = app_get_ptr(ENUM_APP_IEC);

	if (NULL == p_app)
	{
		LOG_E("iec104: app_get_ptr(ENUM_APP_IEC) failed");
		return -1;
	}

	/* 封装消息头 */
	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);

	/* 投递到 MQ_IEC_TO_COM */
	p_mq = app_get_msg_queue_ptr(ENUM_MQ_IEC_TO_COM);

	if (NULL == p_mq)
	{
		LOG_E("iec104: MQ_IEC_TO_COM not found");
		return -1;
	}

	if (0 != task_msg_queue_send(p_mq->p_msg_queue, p_app->rtx.tx, p_mq->msg_size))
	{
		LOG_E("iec104: MQ send failed");
		return -1;
	}

	/* 通知 com_router 线程 */
	target   = app_get_mq_target(ENUM_MQ_IEC_TO_COM);
	p_target = app_get_ptr(target.app_id);

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

	return 0;
}

int iec104_data_rx(uint8_t *msg)
{
	stru_msg_head *p_head;
	stru_iec      *p_iec;
	uint32_t       interface;
	uint8_t       *p_rx;
	uint16_t       len;

	if (NULL == msg)
	{
		return -1;
	}

	p_head    = (stru_msg_head *)msg;
	interface = p_head->interface;
	p_rx      = p_head->data;
	len       = p_head->len;

	if (NULL == p_rx || len < 2)
	{
		return -1;
	}

	/* 仅处理 IEC-104 帧 (0x68 开头) */
	if (p_rx[0] != 0x68)
	{
		return -1;
	}

	p_iec = iec104_ptr_get(interface, CS104_TYPE_S);

	if (NULL == p_iec)
	{
		LOG_E("iec104: no free slot for interface 0x%X", interface);
		return -1;
	}

	p_iec->tm_out = 0;
	CS10x_DoRecv(&p_iec->cs10x, (char *)p_rx, len);

	return 0;
}

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

void iec104_task(uint16_t gap)
{
	uint8_t i;

	for (i = 0; i < MAX_IEC_NUM; i++)
	{
		if (!g_iec[i].is_busy)
		{
			continue;
		}

		g_iec[i].tm_out += gap;

		/* 超时回收 slot */
		if (g_iec[i].tm_out >= LINK_OVER_TIME)
		{
			LOG_I("iec104: slot[%d] timeout (%u ms), freeing", i, g_iec[i].tm_out);
			memset(&g_iec[i], 0, sizeof(stru_iec));
		}
		else
		{
			CS10x_TimerHandle(&g_iec[i].cs10x, gap);
		}
	}
}

/* =========================== 测试命令 =========================== */

LOCAL void cmd_iec(int argc, char *argv[])
{
	const char *help = "iec info        查看 IEC-104 连接状态\n"
		"iec reset       清除所有连接\n";
	int i;

	if (argc < 2)
	{
		printf("%s", help);
		return;
	}

	if (0 == strcmp(argv[1], "info"))
	{
		printf("===== IEC-104 Status =====\n");

		for (i = 0; i < MAX_IEC_NUM; i++)
		{
			printf("slot[%d]: busy=%d type=%d if=0x%X tm_out=%u\n",
				i, g_iec[i].is_busy, g_iec[i].iec_type, g_iec[i].interface, g_iec[i].tm_out);
		}

		printf("SOE: in=%d  YC-disturb: in=%d  DD-disturb: in=%d  Fault: in=%d\n",
			g_soe_ring.in, g_yc_ring.in, g_dd_ring.in, g_fault_ring.in);
	}
	else if (0 == strcmp(argv[1], "reset"))
	{
		for (i = 0; i < MAX_IEC_NUM; i++)
		{
			memset(&g_iec[i], 0, sizeof(stru_iec));
		}

		LOG_I("iec: all slots reset");
	}
	else
	{
		printf("unknown: %s\n%s", argv[1], help);
	}
}

LOCAL void cmd_iec_complete(const char *buf, char ***completions, int *ncomp)
{
	static const char *subs[] = { "info", "reset" };
	cmd_sub_complete(buf, completions, ncomp, subs, 2);
}

CMD_REGISTER_C("iec", cmd_iec, "IEC-104 查看/重置", cmd_iec_complete);

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

int app_iec_init1(void *arg)
{
	char dir[512] = { 0 };

	if (0 != func_proc_self_dir(dir, sizeof(dir)))
	{
		LOG_E("app_iec_init1: get_exe_dir failed");
		return -1;
	}

	std::string path = std::string(dir) + "/config/IEC60870/iec1014.xml";
	LOG_I("app_iec_init1: cfg path: %s", path.c_str());

	if (0 != iec_cfg_parse(path.c_str()))
	{
		LOG_E("app_iec_init1: cfg parse failed: %s", path.c_str());
		return -1;
	}

	stru_app *p_app = (stru_app *)arg;
	dc_signal_out("iec.run_cnt", "iec.run_cnt", DATA_TYPE_U32, &p_app->run_cnt, "iec104");

	LOG_I("app_iec_init1: ok");
	return 0;
}

int app_iec_init2(void *arg)
{
	(void)arg;

	if (0 != iec104_point_init())
	{
		LOG_E("app_iec_init2: iec104_point_init failed");
		return -1;
	}

	LOG_I("app_iec_init2: ok");
	return 0;
}

void *app_iec(void *arg)
{
	stru_app           *p_app;
	stru_app_msg_queue *p_mq;
	uint32_t            event;

	if (NULL == arg)
	{
		LOG_E("app_iec: NULL arg");
		return NULL;
	}

	p_app = (stru_app *)arg;

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

		/* MQ_COM_TO_IEC → decode */
		if (event & EV_IEC_RX_COM)
		{
			p_mq = app_get_msg_queue_ptr(ENUM_MQ_COM_TO_IEC);

			if (NULL != p_mq)
			{
				while (0 == task_msg_queue_try_recv(p_mq->p_msg_queue, p_app->rtx.rx, p_mq->msg_size))
				{
					iec104_data_rx(p_app->rtx.rx);
				}
			}
		}

		/* 10ms 定时器 */
		if (event & EV_TIMER1)
		{
			iec104_task(10);
		}

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

		/* 停止信号 */
		if (event & EV_STOP)
		{
			LOG_I("app_iec: received EV_STOP, exiting");
			break;
		}
	}

	return NULL;
}
