/******************************************************************************
版权所有：	
文件名称：	can_app.c
文件版本：	01.01
创建作者:	   sunxi
创建日期：	2013-02-28
功能说明：	can传输应用层
其它说明：
修改记录：
*/
/*------------------------------- 头文件 --------------------------------------
*/
#include "head.h"

#ifdef BSP_CAN_ENABLE

 
/*------------------------------- 宏定义 --------------------------------------
*/


/*------------------------------ 类型结构 -------------------------------------
*/


/*------------------------------ 全局变量 -------------------------------------
*/
int g_sb_monitor;

/*------------------------------ 函数声明 -------------------------------------
*/
int can_app_callback(u32 can_bus,u8 * buf);


/*------------------------------ 外部函数 -------------------------------------
外部函数供其它实体文件引用，必须仔细检查传入参数的合法性.
*/
int can_app_init(void)
{
	can_regester_recv_callback(can_app_callback);

	return 0;
}

int can_app_exit(void)
{
	can_regester_recv_callback(NULL);

	return 0;
}


int can_app_callback(u32 can_bus,u8 * buf)
{
	struct can_frame_head *cfh;

	// 检查参数
	if(can_bus >= CAN_BUS_NUM)
	{
		return -1;
	}

	if(buf == NULL)
	{
		return -2;
	}

	cfh = (struct can_frame_head *)buf;
	switch(cfh->type)
	{
	case CAN_FRAME_TYPE_DI_INIT:
	case CAN_FRAME_TYPE_DI_ON:
	case CAN_FRAME_TYPE_DI_OFF:
	case CAN_FRAME_TYPE_DI_INIT1:
	case CAN_FRAME_TYPE_DI_ON1:
	case CAN_FRAME_TYPE_DI_OFF1:
		dido_di_update(buf);
	break;

	case CAN_FRAME_TYPE_SB_INFO:
#if defined(CAN_SLAVE_BOARD)
		equ_board_info_update(can_bus,buf);
#endif
	break;
	
	default:
		return 0;
	break;
	}

	return 1;
}


u8 can_app_checksum(u8 *buf,u8 len)
{
	u8 i,sum;

	sum = 0;
	for(i=0; i<len; i++)
	{
		sum += *buf++;
	}

	return sum;
}


int can_app_timing(void)
{
	static long g_timing_sec = 0;
	
	int i,ret;
	u8 *buf;
	struct timespec ts;

	clk_time_get(&ts);
	
	// 对时帧必须在内部对时秒脉冲发出10ms后再发送，每秒发送一次。
	if(ts.tv_sec == g_timing_sec || ts.tv_nsec < 10*NSEC_PER_MS)
	{
		return 0;
	}

	// 帧头
	buf = can_request_tx_buf(CAN_FRAME_TYPE_TIMING);
	buf[0] = CAN_FRAME_TYPE_TIMING;
	buf[1] = CAN_BUS_ADDR_BCAST;
	buf[2] = 0;
	buf[3] = sizeof(int); // long -> int 8byte -> 4 byte, 暂时按回4字节传输，待确认 EWen 

	// 得到时间
	memcpy(buf+4,&ts.tv_sec,sizeof(int));//noted by sunxi: 这里需要注意大小端问题
	
	// 发送
	for(i=0;i<CAN_BUS_NUM;i++)
	{
		ret = can_send(i,buf);
		if(ret < 0)
		{
			// rt_printf("can_app_timing:ret=%d\r\n",ret);
		}
	}

	g_timing_sec = ts.tv_sec;
	
	return 0;
}


int can_app_reset(int is_watchdog)
{
	int i ;
	u8 *buf;

	// 帧头
	buf = can_request_tx_buf(CAN_FRAME_TYPE_RESET);
	buf[0] = CAN_FRAME_TYPE_RESET;
	buf[1] = CAN_BUS_ADDR_BCAST;
	buf[2] = 0;
	buf[3] = 1;
	buf[4] = (u8)is_watchdog;

	// 发送
	for(i=0;i<CAN_BUS_NUM;i++)
	{
		can_send(i,buf);
	}

	// 子板复位时更新时间，避免产生子板通讯异常错误。
	for(i=0;i<EQU_SLOT_NUM_MAX;i++)
	{
		g_board_info[i].us0 = ustimer_get_origin();
	}
 
	return 0;
}


int can_app_sb_monitor(void)
{
	g_sb_monitor = 1;
	
	return 0;
}

int can_app_sb_monitor_send(void)
{
	u8 buf[4];
	
	buf[0] = CAN_FRAME_TYPE_SB_MONITOR;
	buf[1] = CAN_BUS_ADDR_BCAST;
	buf[2] = 0;
	buf[3] = 0;

	// 发送
	can_send(0,buf);
	can_send(1,buf);
	
	return 0;
}

//can子板查询 oujie add 20200420
int can_app_sb_discover(void)
{
	u8 buf[4];
	u32 slot;
	slot = equ_get_slot_by_type(BOARD_TYPE_METERING);
	if(slot >0)
	{
		buf[0] = CAN_FRAME_TYPE_DISCOVER;
		buf[1] = CAN_BUS_ADDR_BCAST;
		buf[2] = 0;
		buf[3] = 0;
#ifdef CAN_BOARD_DEBUG
		rt_printf("查询子板\r\n");
#endif
		// 发送
		can_send(0,buf);
		//can_send(1,buf);
	}
	
	return 0;
}

#if 1
//分配can子板卡槽号 oujie add 20200420
int can_app_sb_give_slot(u32 no, u8* buf)
{
	u8 send_buf[32];
	char id_string[64] = {0};
	struct can_board_res* can_brd;

	Hex2Str(&buf[5],  id_string, CHIP_ID_SIZE*4);
#ifdef CAN_BOARD_DEBUG
	rt_printf("子板id字符串 = %s\r\n", id_string);
	rt_printf("获取卡槽号\r\n");
#endif
	can_brd = equ_get_can_slot(buf[4], buf[5], &buf[6], sizeof(can_brd->chip_id));
	if(can_brd != NULL)
	{
		can_brd->type = buf[5];
		send_buf[0] = CAN_FRAME_TYPE_GIVE_CAN_SLOT;
		send_buf[1] = CAN_BUS_ADDR_BCAST;
		send_buf[2] = 0;
		send_buf[3] = sizeof(can_brd->chip_id)+1;
		send_buf[4] = can_brd->slot;
		memcpy(&send_buf[5], can_brd->chip_id, sizeof(can_brd->chip_id));
		
#ifdef CAN_BOARD_DEBUG	
		rt_printf("CAN发送卡槽号 slot = %d\r\n", can_brd->slot);
		rt_printf("CAN卡类型 type = %d\r\n", can_brd->type);
#endif
		
		// 发送
		can_send(no, send_buf);
	}
	else
	{
#ifdef CAN_BOARD_DEBUG
		rt_printf("卡槽号已存在，不再发送\r\n");
#endif
	}
	
	return 0;
}
#endif



struct sb_monitor
{
	// 系统
	u32 slot;
	u32 type;
	u32 start_sec;
	
	//中断时间
	u32 us_gps_min;
	u32 us_gps_max;
	u32 us_100us_min;
	u32 us_100us_max;
	u32 us_can_min;
	u32 us_can_max;
	
	// 开出
	u32 do_delay_min;
	u32 do_delay_max;

	// CAN总线
	u32 can_rx_dropped;		
	u32 can_tx_dropped;		
	u32 can_hw_bus_errors;
	u32 can_hw_errors;
	u32 can_hw_overrun;
	u32 can_overrun;
};

extern struct rt_stat 	g_stat_do_delay;

int can_app_sb_monitor_recv(u8 * buf)
{
	struct sb_monitor sm;
	struct timespec ts;

	memcpy(&sm,&buf[4],sizeof(sm));

	rt_printf("\r\n子板监视信息[slot=%02d,type=%02d]:\r\n",sm.slot,sm.type);

	ts.tv_sec = sm.start_sec;
	ts.tv_nsec = 0;
	rt_printf("上电时间: ");
	rt_printf_time2(ts);

	rt_printf("  GPS中断时间:\tmin=%d,\tmax=%d\r\n",sm.us_gps_min,sm.us_gps_max);
	rt_printf("100us中断时间:\tmin=%d,\tmax=%d\r\n",sm.us_100us_min,sm.us_100us_max);
	rt_printf("  CAN中断时间:\tmin=%d,\tmax=%d\r\n",sm.us_can_min,sm.us_can_max);
	rt_printf(" 开出延时时间:\tmin=%d,\tmax=%d\r\n",sm.do_delay_min,sm.do_delay_max);
	rt_stat_in(&g_stat_do_delay,sm.do_delay_max);

	rt_printf("CAN总线接收丢包:%d\r\n",sm.can_rx_dropped);
	rt_printf("CAN总线发送丢包:%d\r\n",sm.can_tx_dropped);
	rt_printf("CAN总线总线错误:%d\r\n",sm.can_hw_bus_errors);
	rt_printf("CAN总线硬件错误:%d\r\n",sm.can_hw_errors);
	rt_printf("CAN总线硬件溢出:%d\r\n",sm.can_hw_overrun);
	rt_printf("CAN总线接收溢出:%d\r\n",sm.can_overrun);
	
	return 0;

}

int can_app_sb_discover_recv(u8 * buf)
{
	struct can_board_res* can_brd;
#ifdef CAN_BOARD_DEBUG
		uint8_t i;
#endif

	memcpy((u8*)can_board[0].chip_id, &buf[6], sizeof(can_brd->chip_id));

#ifdef CAN_BOARD_DEBUG
	rt_printf("\r\n接收到子板chip_id: ");
	for(i=0; i<sizeof(can_brd->chip_id); i++)
	{
		rt_printf("%x ", buf[6+i]);
	}
	rt_printf("\r\n");
#endif
	return 0;
}

int can_app_sb_slot_recv(u8 * buf)
{
#ifdef CAN_BOARD_DEBUG	
	rt_printf("接收到子板分配卡槽的应答\r\n");
#endif
	uint8_t i;

	for(i=0; i<CAN_BOARD_NUM; i++)
	{
		if(can_board[i].file_have)
		{
			if((memcmp((u8*)can_board[i].chip_id, &buf[5], sizeof(can_board[i].chip_id)) == 0)
				&& (can_board[i].slot == buf[4]))
			{
#ifdef CAN_BOARD_DEBUG
				rt_printf("分配子板号完成\r\n");
#endif
				can_board[i].given_slot = 1;
			}
		}
	}

	return 0;
}





int errcount_crc=0;
int errcount_sb=0;
int errcount_no=0;
int errcount_sb_no=0;
int errcount_check=0;

static u32 g_no_send[CAN_BUS_NUM];
static u32 g_no_recv[EQU_SLOT_NUM_MAX];

int can_app_test_send(void)
{
	u8 *buf_tx;
	int i,can,no,len,ret;
	
	len = 200;
	buf_tx = can_request_tx_buf(CAN_FRAME_TYPE_TEST);
	for(can=0;can<CAN_BUS_NUM;can++)
	{
 		no = g_no_send[can];
		buf_tx[0] = CAN_FRAME_TYPE_TEST;
		buf_tx[1] = CAN_BUS_ADDR_BCAST;
		buf_tx[2] = 0;
		buf_tx[3] = len;
		
		buf_tx[4] = 0;
		buf_tx[5] = no;
		buf_tx[6] = no>>8;
		buf_tx[7] = no>>16;
		buf_tx[8] = no>>24;
		for(i=0;i<(len-6);i++)
		{
			buf_tx[i+9] = i;
		}
		buf_tx[len+3] = can_app_checksum(buf_tx+4,len-1);
	
		// 发送
		ret = can_send(can,buf_tx);
		if(ret < 0)
		{
			if(ret != -7)
			{
				rt_printf("can_app_test error:ret=%d\r\n",ret);
			}
		}
		else
		{
			g_no_send[can]++;
		}
	}
	
	return 0;
}

int can_app_test_recv(u8 * buf)
{
	u8 len,crc,crc_check;
	int i,no,slot;

	slot = buf[2];
	len = buf[3];
	crc = buf[len+3];
	crc_check = can_app_checksum(buf+4,len-1);
	no = buf[5] | ((int)buf[6])<<8 | ((int)buf[7])<<16 | ((int)buf[8])<<24;

	// 检查CRC
	if(crc != crc_check)            
	{
		errcount_crc++;
		rt_printf("can_app_test_recv:crc error!slot=%d,type=%d,no=%d,   crc=%x,crc_check=%x,errcount_crc=%d\r\n",slot,buf[4],no,crc,crc_check,errcount_crc);
		print_msg("RX_CAN:",buf,buf[3] + 4);
		return -1;
	}

	// 检查子板接收错误
	if(buf[4] != 0)     
	{
		errcount_sb++;
		rt_printf("can_app_test_recv:sb error!     slot=%d,type=%d,no=%d,   errcount_sb=%d\r\n",slot,buf[4],no,errcount_sb);
		print_msg("RX_CAN:",buf,buf[3] + 4);
		return -2;
	}

	// 检查内容
	for(i=0;i<(len-6);i++)
	{
		if(buf[i+9] != i)
		{
			break;
		}
	}
	if(i != (len-6))
	{
		errcount_check++;
		rt_printf("can_app_test_recv:content error!slot=%d,no=%d,errcount_check=%d\r\n",slot,no,errcount_check);
		print_msg("RX_CAN:",buf,buf[3] + 4);
		return -4;
	}

	// 检查序号
	if(no != g_no_recv[slot])
	{
		errcount_sb_no++;
		rt_printf("can_app_test_recv:no error!slot=%d,no=%d,g_no_recv[slot]=%d,errcount_sb_no=%d\r\n",slot,no,g_no_recv[slot],errcount_sb_no);
		print_msg("RX_CAN:",buf,buf[3] + 4);
		return -5;
	}

	// 检查ok，序号加一
	g_no_recv[slot]++;
	return 0;
}


int can_task(void)
{
	int i;
	u8 * buf;
	struct can_frame_head 	*cfh;
	static unsigned char send_discover = 0;
#ifdef CAN_SLAVE_BOARD		
	static unsigned long us0 = 0;
	static unsigned long us1 = 0;
	static unsigned char send_discover = 0;
#endif
//	static unsigned long us1 = 0;
	u8 can_no = 0;

	if(g_sb_monitor)
	{
		can_app_sb_monitor_send();
		g_sb_monitor = 0;
	}

	buf = can_request_tx_buf(CAN_FRAME_TYPE_PROGRAM_UPDATE);

	// 最多一次接收8帧
	for(i=0; i<128; i++)
	{
		if(can_recv(0,buf,CAN_FRAME_LEN_MAX) <= 0)
		{
			if(can_recv(1,buf,CAN_FRAME_LEN_MAX) <= 0)
			{
				prog_recv_timeout();
				break;
			}
			else
				can_no = 1;
		}
		else
			can_no = 0;

		// 检查帧内容
		cfh = (struct can_frame_head *)buf;
#if 0
		if(cfh->src >= EQU_SLOT_NUM_MAX)
		{
			continue;
		}
#endif
		if(cfh->src >= (EQU_SLOT_NUM_MAX+CAN_BOARD_NUM))
		{
			continue;
		}

		// 处理不同类型的帧
		switch(cfh->type)
		{
		case CAN_FRAME_TYPE_PROGRAM_UPDATE:
			prog_recv(buf);
			break;
		case CAN_FRAME_TYPE_SB_MONITOR:
			can_app_sb_monitor_recv(buf);
			break;
		case CAN_FRAME_TYPE_TEST:
			if(can_app_test_recv(buf) != 0)
			{
				g_test_on = 0;
				g_print_can = 0;
			}
			
			break;
		case CAN_FRAME_TYPE_DISCOVER:
			can_app_sb_discover_recv(buf);
			can_app_sb_give_slot(can_no, buf);
			send_discover = 0;
			can_brd_error_clear(buf[4]);
			break;
		case CAN_FRAME_TYPE_GIVE_CAN_SLOT: //收到子板应答
			can_app_sb_slot_recv(buf);
			break;
		default:
			break;
		}
	}	


	// 子板程序更新发送
	prog_send();

#ifdef CAN_SLAVE_BOARD	
	if(!is_prog_down())
	{
		if(us0 == 0)
			us0 = ustimer_get_origin();
		if(ustimer_delay_origin2(&us0, 3*USTIMER_SEC))
		{
			us0 = 0;
			can_app_sb_discover();
			send_discover++;
			us1 = ustimer_get_origin();
		}
		if(send_discover>=2)
		{
			if(ustimer_delay_origin2(&us1, 2*USTIMER_SEC))
			{
				send_discover = 0;
				us1 = ustimer_get_origin();
				can_brd_error_set();
	//			rt_printf("can_brd_error_set\r\n");
			}			
		}
	}
#endif
#if defined(METERING_ENERGY) && defined(CAN_SLAVE_BOARD)
	if(!is_prog_down()&&!send_discover)
		can_metering_send_app();
#endif

	if(g_test_on)
	{
//		can_app_test_send();
	}
	
	return 0;
}


int can_app_test_printf(void)
{
	int i;
	for(i=0;i<CAN_BUS_NUM;i++)
	{
		rt_printf("slot%02d:%d.\r\n",i,g_no_send[i]);
	}
	
	rt_printf("g_no_recv:\r\n");
	for(i=0;i<EQU_SLOT_NUM_MAX;i++)
	{
		rt_printf("slot%02d:%d.\r\n",i,g_no_recv[i]);
	}

	return 0;
}

#endif

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


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


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


