/******************************************************************************
版权所有：
文件名称：	equ.c
文件版本：	01.01
创建作者:	   sunxi
创建日期：	2013-03-25
功能说明：	装置信息管理维护
其它说明：
修改记录：
*/
/*------------------------------- 头文件 --------------------------------------
 */
#include "head.h"

/*------------------------------- 宏定义 --------------------------------------
 */
#define EQU_SLOT_CHECK_TIMEOUT 4 // 子板检查超时，以秒为单位,不能小于4s

/*------------------------------ 类型结构 -------------------------------------
 */
// 子板信息帧结构
struct board_info_frame
{
    u8 type;        // 子板类型
    u8 status : 4;  // 子板状态
    u8 errcode : 4; // 子板错误代码
    u16 crc;        // 子板CRC
    u32 version;    // 子板版本
};

/*------------------------------ 全局变量 -------------------------------------
 */
// 控制板类型
u32 g_brd_type_kz;

// 采样板索引和插槽的对应关系
const int g_ac_index_to_slot[EQU_SLOT_AC_NUM] = {1};

// 采样板起始槽位，每种机箱不一样。
u32 g_ac_slot_begin;

// 记录频率对应ui通道的索引值
int g_ui_freq[CFG_FREQ_NUM];

// 模拟量对应关系
static const s8 g_ac_index[BOARD_TYPE_KZ_NUM][EQU_SLOT_AC_CHN] =
    {
        {0X01, 0X03, 0X05, 0X07, 0XFF, 0XFF, 0XFF, 0XFF, 0X00, 0X02, 0X04, 0X06, 0X0E, 0X0C, 0X0A, 0X08}, // 24款分布式 lch
};

// 遥信转换表
s8 g_change_di_index[BOARD_TYPE_KZ_NUM][EQU_DB_YX_NUM] =
    {
        {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32},
};

// 开出对应关系
static const s8 g_do_index[BOARD_TYPE_KZ_NUM][DO_NUM] =
    {
        {0, 1, 2, 3, 4, 5, 6, 7, 8, -1},
};

/*------------------------------ 函数声明 -------------------------------------
 */
unsigned int change_di_ch(unsigned int di)
{
    register int i, rts = 0;
#if 0
	static int di_bak;
	if(di_bak!=di)
	{
		rt_printf_time("change_di_ch di=%x \r\n",di);
		di_bak=di;
	}
#endif
    for (i = 0; i < EQU_DB_YX_NUM; i++)
    {
        if (g_change_di_index[g_brd_type_kz][i] != 0 && g_change_di_index[g_brd_type_kz][i] <= EQU_DB_YX_NUM)
        {
            if (di >> (g_change_di_index[g_brd_type_kz][i] - 1) & 0x01)
                rts |= (1 << i);
        }
    }
    // rt_printf("%d %d  。。",rts,g_brd_type_kz);
    return rts;
}

/*------------------------------ 外部函数 -------------------------------------
外部函数供其它实体文件引用，必须仔细检查传入参数的合法性.
*/

/**
 * @brief          远动参数投入之后 遥信防抖时间的更新
 * @return         * int
 * @retval         none
 *
 * @warning        none
 * @note           none
 */
int equ_config_di_yd(void)
{
    u32 i;
    struct equ_config_di *ecd;

    // 用户配置的保持时间、拥有者、类型
    ecd = g_equ_config_di;
    for (i = 0; i < g_equ_config->di_num; i++)
    {
        g_di_st[ecd->slot][ecd->index].tm_filter = (u32)tRunPara.wYXFD;
        ecd++;
    }

    return 0;
}

int equ_config_do_yd(void)
{
    int i, index;
    struct equ_config_do *ecd;
    // 用户配置的保持时间
    ecd = g_equ_config_do;
    for (i = 0; i < g_equ_config->do_num; i++)
    {
        u32 dotime = (u32)ecd->time_pulse;
        if (ecd->slot == g_do_slot)
        {
            // 如果定值参数中设置了分闸时间，覆盖
            if (ecd->owner && ((ecd->type == (SW_DO_BHT + 1)) || (ecd->type == (SW_DO_YKT + 1))))
            {
                if (pRunSet->bTT_RmtPara)
                {
                    dotime = tRunPara.wFZMC;
                }
            }
            // 如果定值参数中设置了合闸时间，覆盖
            if (ecd->owner && ((ecd->type == (SW_DO_BHH + 1)) || (ecd->type == (SW_DO_YKH + 1))))
            {
                if (pRunSet->bTT_RmtPara)
                {
                    dotime = tRunPara.wHZMC;
                }
            }
            index = equ_get_do_channel(ecd->index);
            g_do_st[index].us_keep = (u32)dotime;
            g_do_st[index].us_keep *= 1000; // ms转换为us
            dp_err_n_c("g_do_st[%d].us_keep = %d", index, g_do_st[index].us_keep);
        }
        ecd++;
    }
    return 0;
}

int equ_init_di(void)
{
    int i, ui;
    struct equ_config_di *ecd;

    // 采样板起始槽位
    g_ac_slot_begin = EQU_SLOT_AC_BEGIN;

#if !defined CPU_FUXI /* 注意t536版fuxi不使用这个配置 */
    // 检查频率硬件通道对应的UI。
    g_ui_freq[0] = UI_NUM;
    g_ui_freq[1] = UI_NUM;
    for (i = 0; i < g_equ_config->ac_num; i++)
    {
        // 频率通道必须是配置了的公共电压类型
        if (g_equ_config_ac[i].type == 0 || g_equ_config_ac[i].owner != 0)
        {
            continue;
        }

        ui = g_equ_config_ac[i].type - 1;
        if (ui >= PUB_AC_NUM)
        {
            continue;
        }

        // 确定频率对应电压通道
        if (ui < PUB_AC_NUM)
        {
            if (g_equ_config_ac[i].slot == 9 && g_equ_config_ac[i].index == 0)
            {
                g_ui_freq[0] = ui;
            }

            if (g_equ_config_ac[i].slot == 10 && g_equ_config_ac[i].index == 0)
            {
                g_ui_freq[1] = ui;
            }
        }
    }
#else
    g_ui_freq[0] = PUB_AC_UA1;
    g_ui_freq[1] = PUB_AC_UA2;
#endif

    // 初始化开入g_di_st结构
    memset((unsigned char *)g_di_st, 0, sizeof(g_di_st));

    ecd = g_equ_config_di;
    for (i = 0; i < g_equ_config->di_num; i++)
    {
        g_di_st[ecd->slot][ecd->index].tm_filter = ecd->filter_time;
        g_di_st[ecd->slot][ecd->index].owner = ecd->owner;
        g_di_st[ecd->slot][ecd->index].type = ecd->type;
        ecd++;
    }
    return 0;
}

int equ_config_do_keeptime(void)
{
    u32 i, index;
    struct equ_config_do *ecd;

    memset(&g_do_st, 0, sizeof(struct do_struct) * DO_NUM);

    // 用户配置的保持时间
    ecd = g_equ_config_do;
    for (i = 0; i < g_equ_config->do_num; i++)
    {
        if (ecd->slot == g_do_slot)
        {
            index = equ_get_do_channel(ecd->index);
            g_do_st[index].us_keep = (u32)ecd->time_pulse;
            g_do_st[index].us_keep *= 1000; // ms转换为us
        }
        ecd++;
    }

    return 0;
}

#ifdef BSP_CAN_ENABLE
/******************************************************************************
函数名称：	equ_board_info_update
函数版本：	01.01
创建作者:	   sunxi
创建日期：	2013-08-08
函数说明：	接收到子板的对时应答帧后，更新子板信息
参数说明：
    can_bus 从哪条can总线接收到的帧。
    frame	接收帧的buf指针
返回值：
    0：		成功
    其它：	失败
修改记录：
*/
int equ_board_info_update(int can_bus, u8 *frame)
{
    struct can_frame_head *cfh;
    struct board_info_frame *bif;
    struct board_info *bi;

    // 检查帧内容
    cfh = (struct can_frame_head *)frame;
#ifdef CAN_SLAVE_BOARD
    if (cfh->src >= (EQU_SLOT_NUM_MAX + CAN_BOARD_NUM))
#else
    if (cfh->src >= EQU_SLOT_NUM_MAX)
#endif
    {
        return -1;
    }

    if (cfh->len != 8)
    {
        return -2;
    }

    // 比较子板类型
    bi = &g_board_info[cfh->src];
    bif = (struct board_info_frame *)(frame + sizeof(cfh));
    if (bif->type != bi->type)
    {
        //	rt_printf("board type error(no=%d,bi_type=%d,gbi_type=%d)\r\n",cfh->src,bif->type,bi->type);
        //	print_mem("frame",frame,16);
        return -3;
    }

    // 得到子板信息
    bi->status = bif->status;
    bi->errcode = bif->errcode;
    bi->crc = bif->crc;
    bi->version = bif->version;
    // rt_printf("%02d\t%02d\t%02d\t%04x\t%08x\t",bi->type,bi->status,bi->errcode,bi->crc,bi->version);

    // 得到子板时间
    clk_time_get(&bi->update_time);
    bi->us0 = ustimer_get_origin();
    bi->is_ok = 1;

    bi->can_bus = can_bus;

    return 0;
}
#endif

/******************************************************************************
函数名称：	equ_ac_channel_is_ok
函数版本：	01.01
创建作者:	   sunxi
创建日期：	2013-08-08
函数说明：	得到插槽中子板的模拟量通道对应的ADC采样通道。
参数说明：
    slot	插槽索引号
    index	子板上模拟量通道索引号
返回值：	ADC采样通道索引号
修改记录：
*/
int equ_ac_channel_is_ok(u32 index)
{
    // 检查参数
    if (index >= CFG_ADC_CHANNEL)
    {
        return 0;
    }

    //  直接返回ok 板卡没有配置检测io
    return 1;
    //	return g_ac_slot_is_ok[index/EQU_SLOT_AC_CHN];
}

/******************************************************************************
函数名称：	equ_get_ac_channel
函数版本：	01.01
创建作者:	   sunxi
创建日期：	2013-08-08
函数说明：	得到插槽中子板的模拟量通道对应的ADC采样通道。
参数说明：
    slot	插槽索引号
    index	子板上模拟量通道索引号
返回值：	ADC采样通道索引号
修改记录：
*/
int equ_get_ac_channel(u32 slot, u32 index)
{
    // 检查参数
#if (0) /* 不检查 插槽索引号 lch 2025年12月5日17:29:28 */
    if (slot != BOARD_TYPE_FUXI_AC)
    {
        return -1;
    }
#endif

    if (index >= CFG_ADC_CHANNEL)
    {
        return -2;
    }

    return g_ac_index[g_brd_type_kz][index];
}

/******************************************************************************
函数名称：	equ_get_ac_slot
函数版本：	01.01
创建作者：	xxxxxx
创建日期：	2014-10-09
函数说明：	得到采样通道对应的板卡
参数说明：
    owner	归属公共、开关
    type	采样类型
返回值：	ADC采样通道索引号
修改记录：
*/
int equ_get_ac_slot(u32 owner, u32 type)
{
    u32 no;
    if (owner)
    {
        no = g_sw[owner].ac_cfg_index[type];
    }
    else
    {
        no = g_sw_pub.ac_cfg_index[type];
    }
    return g_equ_config_ac[no].slot;
}

/******************************************************************************
函数名称：	equ_ac_index_to_slot
函数版本：	01.01
创建作者:	   sunxi
创建日期：	2013-08-08
函数说明：	将采样板索引号(和ADC通道号对应)转换为插槽号。
参数说明：
    ac_index	采样板索引号
返回值：	插槽号
修改记录：
*/
int equ_ac_index_to_slot(int ac_index)
{
    if ((u32)ac_index >= EQU_SLOT_AC_NUM)
    {
        return 0;
    }

    return g_ac_index_to_slot[ac_index];
}

/******************************************************************************
函数名称：	equ_get_do_channel
函数版本：	01.01
创建作者:	   sunxi
创建日期：	2013-08-08
函数说明：	配置子板上的开出资源
参数说明：
    slot	插槽索引号
返回值：	插槽号
    0：		成功
    其它：	失败
修改记录：
*/
int equ_get_do_channel(u32 index)
{
    return g_do_index[g_brd_type_kz][index];
}

#ifdef CPU_FUXI
/**
 * @brief          初始化rv核数据
 * @author         lch (lch_work@foxmail.com)
 * @version        1.0
 * @date           20251222
 * @return         * int
 * @retval         none
 *
 * @warning        none
 * @note           none
 */
int equ_init_rv_di(void)
{
    struct t_shmdata_di_astime t_di_cfg = {0};
    struct equ_config_di *ecd;

    ecd = g_equ_config_di;
    for (int i = 0; i < g_equ_config->di_num; i++)
    {
        t_di_cfg.us_as_time[i] = g_di_st[ecd->slot][ecd->index].tm_filter;
        // rt_printf("[%02d] g_di_st[%02d][%02d].tm_filter = %d\r\n", i, ecd->slot, ecd->index, g_di_st[ecd->slot][ecd->index].tm_filter);
        ecd++;
    }
    return shm_packet_write_v2(SHM_ADDR_R_DI_ASTIME, (uint8_t *)&t_di_cfg, sizeof(struct t_shmdata_di_astime));
}

/**
 * @brief          初始化rv核数据
 * @author         lch (lch_work@foxmail.com)
 * @version        1.0
 * @date           20251222
 * @return         * int
 * @retval         none
 *
 * @warning        none
 * @note           none
 */
int equ_reinit_rv_di(void)
{
    struct t_shmdata_di_astime t_di_cfg = {0};
    for (int i = 0; i < g_equ_config->di_num; i++)
    {
        t_di_cfg.us_as_time[i] = tRunPara.wYXFD;
    }
    return shm_packet_write_v2(SHM_ADDR_R_DI_ASTIME, (uint8_t *)&t_di_cfg, sizeof(struct t_shmdata_di_astime));
}
#endif

/*------------------------------ 内部函数 -------------------------------------
内部函数以下划线‘_’开头，不需要检查参数的合法性.
*/

/*------------------------------ 测试函数 -------------------------------------
一个实体文件必须带一个本模块的测试函数来进行单元测试，如果的确不方便在本模块中
进行单元测试，必须在此注明实际的测试位置（例如在哪个实体文件中使用哪个测试函数）.
*/

/*------------------------------ 文件结束 -------------------------------------
 */
