/*************************** (C) COPYRIGHT 2017 Ping ****************************
 * 文件名  ：user_app.c
 * 描  述  ：用户应用程序
 * 版  本  ：V1.0
 * 作  者  ：XuZhongPing
 * 日  期  ：2017/09/04
*********************************************************************************/
#include "user_app.h"
#include "usart.h"
#include "delay.h"
#include "timer.h"
#include "display.h"

#include "sound.h"


static uint8_t putInPrompt = 0;					//投放信息

/*
***********************************************************************
*	函 数 名: showHexData
*	功能说明: 以十六进制显示数组里的内容
*	形    参:	无
*	返 回 值: 无
***********************************************************************
*/
void showHexData(uint8_t num[], uint8_t n)
{
    uint8_t i;
		printf("\r\n");
    for(i=0; i<n; i++)
		{
//       printf("%02X ",num[i]);
			printf("0x%02X ",num[i]);
		}
		printf("\r\n");
}

/*
***********************************************************************
*	函 数 名: GetPutInType
*	功能说明: 取得投放开门方式类型
*	形    参：无
*	返 回 值: 0x01->IC卡；0x02->二维码；0x03->条形码；0x04-> 按钮
***********************************************************************
*/
uint8_t GetPutInType(void)
{
	uint8_t retData;
	if(keyOpenDoorRequest == 1)											//开门按钮判断
	{
		retData = 0x04;
		keyOpenDoorRequest = 0;
	}else if((RFID_state == 1) || (scanIC_2CodeState == 1)){			//刷IC卡或IC卡二维码判断
		retData = 0x01;
		RFID_state = 0;
		scanIC_2CodeState = 0; 
	}else if(sweep2CodeState == 1){									//二维码扫码判断
		retData = 0x02;
		sweep2CodeState = 0;
	}else if(scanBarCodeState == 1){								//扫垃圾袋二维码
		retData = 0x03;
		scanBarCodeState = 0;
	}else if(scanLabelCodeState == 1){							//二维码贴纸（2018-04-28新增）
		retData = 0x05;
		scanLabelCodeState = 0;
	}else{
		retData = 0x00;
	}
	return retData;
}

/*
***********************************************************************
*	函 数 名: GetUserInfo
*	功能说明: 根据用户请求的开门方式来取得用户的相关信息（IC卡或二维码）
*	形    参：inputType->用户开门方式（IC卡或二维码）
*	返 回 值: 无（调用后改变了全局变量getDataLength、getDataBuf的值）
***********************************************************************
*/
void GetUserInfo(uint8_t inputType)
{
	switch(inputType)																	//根据开门请求方式加载对应的身份信息
	{
		case 0x01:		//刷IC卡开门
			getDataLength = 9;														//IC卡身份信息为9字节
			memcpy(getDataBuf, RFID_ID, 9);								//保存IC卡身份信息
			memcpy(get_IC_ID, RFID_ID, 9);								//保存IC卡身份信息
			memcpy(getCardNumberBit, cardNumberBit, 17);	//保存IC卡身份信息（按位表示）
// 			printf("\r\ngetDataLength = %d\r\n", getDataLength);
// 			printf("\r\nRFID = %x\r\n", RFID_ID[0]);
			break;
			
		case 0x02:		//扫二维码开门
			getDataLength = codeDataLength;								//二维码身份信息长度
			memcpy(getDataBuf, codeBuf, codeDataLength);	//保存二维码发送信息
			memset(codeBuf, 0x00, 35);										//清除缓存内容
// 			printf("\r\ngetDataLength = %d\r\n", getDataLength);
			break;
			
		case 0x03:		//垃圾袋条形码或二维码开门
			getDataLength = codeDataLength;								//二维码身份信息长度
			memcpy(getDataBuf, codeBuf, codeDataLength);	//保存二维码发送信息
			memset(codeBuf, 0x00, 35);										//清除缓存内容
			break;
			
		case 0x04:		//按钮开门无身份信息
			getDataLength = 0;								
			break;
			
		case 0x05:		//二维码贴纸开门
			getDataLength = codeDataLength;								//二维码身份信息长度
			memcpy(getDataBuf, codeBuf, codeDataLength);	//保存二维码发送信息
			memset(codeBuf, 0x00, 35);										//清除缓存内容
			break;
	}
}

/*
***********************************************************************
*	函 数 名: ShowPutInMSG
*	功能说明: 显示投放开门类型及身份识别相关信息 
*	形    参：投放类型0x01->IC卡；0x02->二维码；0x03->条形码；0x04-> 按钮
*	返 回 值: 无
***********************************************************************
*/
void ShowPutInInfo(uint8_t uType)
{
	switch(uType)																//根据开门请求方式加载对应的身份信息
	{
		case 0x01:		//刷IC卡开门
			printf("\r\n刷IC卡或扫IC卡二维码开门\r\n");
			break;
			
		case 0x02:		//扫二维码开门
			printf("\r\n扫二维码开门\r\n");
			break;
			
		case 0x03:		//垃圾袋条形码开门
			printf("\r\n扫垃圾袋条形码或二维码开门\r\n");
			break;
			
		case 0x04:		//按钮开门无身份信息
			printf("\r\n按钮开门\r\n");							
			break;
			
		case 0x05:		//二维码贴纸开门
			printf("\r\n二维码贴纸开门\r\n");							
			break;
	}
	
	printf("\r\n用户识别信息为：\r\n");	
	printf("\r\n <>开门类型：%d\r\n", getPutInType);
	printf("\r\n <>身份长度：%d\r\n", getDataLength);
// 	printf("\r\n <>内容：%s\r\n", getDataBuf);
// 	showHexData(getDataBuf, 33);
}

/*
***********************************************************************
*	函 数 名: ClearPutInInfo
*	功能说明: 投放完成后清除投放信息，为下一次请求做准备
*	形    参：无
*	返 回 值: 无
***********************************************************************
*/
void ClearPutInInfo(void)
{
	keyOpenDoorRequest = 0;													//清除按钮按下标志
	RFID_state = 0;																	//清除RFID_IC卡刷卡标志
	scanIC_2CodeState = 0;													//清除扫IC卡二维码卡号标志
	sweep2CodeState = 0;														//清除二维码扫描标志
	scanBarCodeState = 0;														//清除扫垃圾袋二维码标志
	scanLabelCodeState = 0;
	
	putInPrompt = 0;																//清除提示分类投放语音标识（用于一次只播放一次）
	
	getPutInType = 0x00;														//清除发送身份验证类型
	getDataLength = 0x00;														//清除发送身份验证信息的数据长度
	memset(getDataBuf, 0x00, 33);										//清除发送身份验证信息内容
}

/*
***********************************************************************
*	函 数 名: FaultCodeCheck
*	功能说明: 设备故障码检测，处理结束后1:表示有故障需上传故障码
*           0:表示没有故障不需上传故障码
*	形    参：无
*	返 回 值: 无
***********************************************************************
*/
#define codeLen 9
uint8_t faultCode[codeLen] = {11, 12, 13, 22, 23, 24, 32, 33, 34};		//回收箱故障代码

static void FaultCodeCheck(uint8_t devType)
{
	uint8_t i = 0;
	uint8_t faultMark = 0;
	
	switch(devType)
	{
		case DOUBLE_BARREL_480L:		//双桶480L
			for(i = 0; i < codeLen; i++)
			{
				if((faultCode_1 == faultCode[i]) || (faultCode_2 == faultCode[i]))
				{
					faultMark = 1;
					break;
				}
			}
			break;
			
		case DOUBLE_BARREL_BOTTLE_480L:		//双桶480L（含瓶子）
			for(i = 0; i < codeLen; i++)
			{
				if(faultCode_1 == faultCode[i])
				{
					faultMark = 1;
					break;
				}
			}
			break;
			
		case SINGLE_BARREL_660L:		//单桶660L
		case SINGLE_BARREL_480L:		//单桶480L
			for(i = 0; i < codeLen; i++)
			{
				if(faultCode_1 == faultCode[i])
				{
					faultMark = 1;
					break;
				}
			}
			break;
			
		default:
			break;
	}
	
	if(faultMark == 1)		//有故障则上传故障码
	{
		sendMsg.msgType = 6005;										//故障代码上传
		MessageSend(MSG_FIFO_PROTOCOL, sendMsg);
	}
}

/*
***********************************************************************
*	函 数 名: GetLimitKeyStatus
*	功能说明: 取得限位开关的状态
*	形    参：无
*	返 回 值: 返回限位开关限位状态正常与否（1：异常，0：正常）
***********************************************************************
*/
uint8_t GetLimitKeyStatus(void)
{
	uint8_t retData = 0;
	
	if(limitKeyExceptionMark > 0)
	{	
		switch(deviceType)
		{
			case DOUBLE_BARREL_480L:		//双桶480L
				if(limitKeyExceptionMark & 0x10)
				{
					faultCode_1 = 20;
				}
				if(limitKeyExceptionMark & 0x01)
				{
					faultCode_2 = 20;
				}
				break;
				
			case DOUBLE_BARREL_BOTTLE_480L:
				faultCode_1 = 0;										//目前瓶子桶没有设置故障码
				if(limitKeyExceptionMark & 0x10)		//共用左边的限位开关
				{
					faultCode_2 = 20;
				}
				break;
				
			case SINGLE_BARREL_660L:		//单桶660L	
			case SINGLE_BARREL_480L:		//单桶480L
				if(limitKeyExceptionMark & 0x10)
				{
					faultCode_1 = 20;
				}
				faultCode_2 = 0;
				break;
				
			default:
				break;
		}
		
		displayFaultCode = 20;
		retData = 1;
	}
	return retData;
}

/*
***********************************************************************
*	函 数 名: PutInOpenDoorTask
*	功能说明: 开门投放请求任务
*	形    参：无
*	返 回 值: 无
***********************************************************************
*/
void PutInOpenDoorTask(void)
{
	uint8_t tempData;
	static uint8_t putInStatus = 0x00;
	static uint8_t infoDisplayStatus = 0;
	
	static timer waitingPutInTimer;
	static timer displayTimer;
	static timer waitingUploadTimer;
	static timer outTimer;
	static timer waitingTimer4S;
	
	static uint8_t timer_ok = 0;

	if(timer_ok == 0)
	{
		timer_ok = 1;
		timer_set(&waitingPutInTimer, CLOCK_SECOND*60);
		timer_set(&displayTimer, CLOCK_SECOND*3);
		timer_set(&waitingUploadTimer, CLOCK_SECOND*10);
		timer_set(&outTimer, CLOCK_SECOND*2);
		timer_set(&waitingTimer4S, CLOCK_SECOND*4);
	}
	
	switch(putInStatus)
	{
		case 0x00:		//初始状态
			tempData = GetPutInType();									//检查是否有开门投放请求

//---------------------------------------------------			
			//老化测试
// 			tempData = 0x04;
//---------------------------------------------------
			if(tempData > 0)														//有开门请求
			{
				getPutInType = tempData;									//保存开门投放的类型（按钮、IC卡、二维码、垃圾袋条形码）
				tempData = 0;
				
				swipingCardStatus = 1;										//开门提示声	
				isOpenDoor = 1;														//标志要开门操作
				putInProgress = 1;												//标志设备正在进行操作
				
				GetUserInfo(getPutInType);								//根据开门方式取得用户相关信息
				ShowPutInInfo(getPutInType);							//显示投放请求相关信息
				
				//将用户身份识别信息保存到发送缓存
				sendPutInType = getPutInType;							//身份识别方式
				sendDataLength = getDataLength;						//发送身份信息的长度
				memcpy(sendDataBuf, getDataBuf, 33);			//加载身份信息
				
				audioNum = 0;
				audioOutputMark = 1;
				
				putInOKMark = 0;													//先清除投放完成标志
				disablePutInMark = 0;											//先清除装满禁止开门标志
				userInfoQueryStatus = 0;									//先清除从后台获取得的用户信息
				putInUploadSuccessMark = 0;								//先清除投放上传成功标志
				
				capitalAfter = 0;
				accountNum = 0;
				capital = 0;
				isAuth = 0;
				infoDisplayStatus = 0;										//用户信息每开一次门只显示一次
				
				//显示正在加载用户数据
				Screen_Landing();
				
				//向后台请求用户信息
				if(sendPutInType != 0x04)			//按键开门时不向后台请求用户信息
				{
					sendMsg.msgType = 4001;
					MessageSend(MSG_FIFO_PROTOCOL, sendMsg);	//向后台服务器发出请求获取用户信息指令
					printf("\r\n获取用户信息\r\n");
				}
				
				putInStatus = 0x01;
				
				printf("\r\n---------> 用户请求开门投放！！！\r\n");
				printf("\r\n正在进行投放操作......\r\n");
				timer_restart(&waitingPutInTimer);
				timer_restart(&waitingTimer4S);
				break;
			}
			
			if(GetLimitKeyStatus())		//MCU2返回设备故障指令（如果出现此类故障则停止使用，直到修复）
			{
				sendMsg.msgType = 6005;										//故障代码上传
				MessageSend(MSG_FIFO_PROTOCOL, sendMsg);
				printf("\r\n设备故障！！！\r\n");
				putInStatus = 0x10;
				break;
			}
			break;
			
		case 0x01:		//等待投放
			if(timer_expired(&waitingPutInTimer))
			{
				putInStatus = 0x00;
				ClearPutInInfo();

				Screen_Default();
				printf("\r\n等待投放完成超时返回\r\n");
				break;
			}
			
			//-----------------------------------------------------
			//语音提示用户按分类进行投放
			if(putInPrompt == 0)
			{
				if(timer_expired(&waitingTimer4S))
				{
					AudioOutput(1);
					putInPrompt = 1;
				}
			}
			
			//是否有延迟投放
			if(delayCloseDoorStatus == 1)
			{
				delayCloseDoorStatus = 0;
				timer_restart(&waitingPutInTimer);
// 				printf("\r\n延迟投放时间\r\n");
			}
			
			//显示倒计时
			if(getRemainingTimerStatus == 1)
			{
				getRemainingTimerStatus = 0;
				
				if(getUserInfoMark)		//是否已经收到后台返回的用户信息（当没有收到时一直显示正在加载...）
				{
					//显示用户信息
					if(accountNum > 0)			//账号有效
					{
						if(infoDisplayStatus == 0)		//用户信息每次开门只显示一次
						{
							switch(sendPutInType)
							{
								case 0x01:		//刷IC卡开门
									Screen_UserInfo(0x01, 1, getCardNumberBit, accountNum, capital);
									break;
								
								//扫APP二维码和垃圾袋二维码开门则显示账号
								case 0x02:		//扫二维码开门							
								case 0x03:		//垃圾袋条形码或二维码开门
								case 0x05:
									Screen_UserInfo(0x01, 0, NULL, accountNum, capital);
									break;
									
								case 0x04:		//按钮开门无身份信息
									break;
							}
							infoDisplayStatus = 1;			//标记已经显示过用户信息
						}
					}else{
						Screen_UserInfo(0x05, 0, NULL, 0, 0);
					}
				}else{
					if(infoDisplayStatus == 0)		//用户信息每次开门只显示一次
					{
						Screen_UserInfo(0x06, 0, NULL, 0, 0);
						infoDisplayStatus = 1;			//标记已经显示过用户信息
					}
				}
			
				//显示倒计时
				Screen_Countdown(remainingTimer);				

				//-----------------------------------------------------
				//提示箱门即将关闭
				if(remainingTimer == 6)
				{
					AudioOutput(2);
				}
			}
			
			//增加装满禁止开门
// 			if(disablePutInMark == 1)
// 			{
// 				disablePutInMark = 0;
// 				Screen_ShowFull();
// 				
// 				timer_restart(&displayTimer);
// 				printf("\r\n桶已装满\r\n");
// 				putInStatus = 0x06;
// 				break;
// 			}
			
			
			if(userInfoQueryStatus == 1)
			{
				userInfoQueryStatus = 0;
				infoDisplayStatus = 0;												//有收到用户信息，可以重新更新显示
				getUserInfoMark = 1;
				
				printf("\r\n获取用户信息OK\r\n");
				printf("\r\naccountNum = %d \r\n", accountNum);
				printf("\r\ncapital = %.2f \r\n", (float)capital/100);
// 				printf("\r\nbarcodeLength = %4x \r\n", barcodeLength);
// 				printf("\r\nbarcode = %s\r\n", barcode);
				
				if(accountNum > 0)			//账号有效
				{
					switch(sendPutInType)
					{
						case 0x01:		//刷IC卡开门
							Screen_Query(0x01, 1, getCardNumberBit, accountNum, capital);
							break;
						
						//扫APP二维码和垃圾袋二维码开门则显示账号
						case 0x02:		//扫二维码开门							
						case 0x03:		//垃圾袋条形码或二维码开门
						case 0x05:
							Screen_Query(0x01, 0, NULL, accountNum, capital);
							break;
							
						case 0x04:		//按钮开门无身份信息
							
							break;
					}
					
				}else{
					Screen_Query(0x05, 0, NULL, 0, 0);
				}
				
			}
			
			//判断是否投放完成
			if(putInOKMark == 1)
			{
				putInOKMark = 0;
				
				getWeight1 = uploadWeight1;
				getWeight2 = uploadWeight2;
				
				getWeightBefore1 = uploadBeforeWeight1;
				getWeightAfter1 = uploadAfterWeight1;
				getWeightBefore2 = uploadBeforeWeight2;
				getWeightAfter2 = uploadAfterWeight2;
	
				printf("\r\n收到投放完成，正在处理......\r\n");
				
				printf("\r\n---> 收到本次投放数据如下：\r\n");
				printf("\r\n开门类型：%d\r\n", sendPutInType);
				printf("\r\n身份长度：%d\r\n", sendDataLength);
				printf("\r\n内容：%s\r\n", sendDataBuf);
// 				showHexData(sendDataBuf, 33);
				
				switch(deviceType)
				{
					case DOUBLE_BARREL_480L:		//双桶480L
						printf("\r\n左桶重量: %2.2f Kg，%2.2f Kg -> %2.2f Kg，距离：%3.1f cm\r\n",\
									(float)getWeight1/100, (float)getWeightBefore1/100, (float)getWeightAfter1/100, (float)getDistance1);
						printf("\r\n右桶重量: %2.2f Kg，%2.2f Kg -> %2.2f Kg，距离：%3.1f cm\r\n",\
									(float)getWeight2/100, (float)getWeightBefore2/100, (float)getWeightAfter2/100, (float)getDistance2);
						
						printf("\r\n左桶故障码：%d\r\n", faultCode_1);
						printf("\r\n右桶故障码：%d\r\n", faultCode_2);
						
						//显示用户投放信息
						Screen_ShowPutInInfo(deviceType, leftBarrelType, rightBarrelType, getWeight1, getWeight2);
			
						//只要有开门就上传数据20181101修改
						sendMsg.msgType = 6011;														//向后台发送投放信息
						MessageSend(MSG_FIFO_PROTOCOL, sendMsg);

						break;
						
					case DOUBLE_BARREL_BOTTLE_480L:		//双桶480L含瓶子				
						smallBottleCount = smallBottleCount + bigBottleCount;		//当前不分大小瓶子
						
						printf("\r\n瓶罐: %d 个， 距离 = %3.1f cm\r\n", smallBottleCount, (float)getDistance1);
						printf("\r\n右桶重量: %2.2f Kg，%2.2f Kg -> %2.2f Kg，距离：%3.1f cm\r\n",\
									(float)getWeight2/100, (float)getWeightBefore2/100, (float)getWeightAfter2/100, (float)getDistance2);
						
						printf("\r\n瓶子故障码：%d\r\n", faultCode_1);
						printf("\r\n右桶故障码：%d\r\n", faultCode_2);
						
						
						//显示用户投放信息
						Screen_ShowPutInInfo(deviceType, leftBarrelType, rightBarrelType, smallBottleCount, getWeight2);
						
						//只要有开门就上传数据20181101修改
						sendMsg.msgType = 6011;														//向后台发送投放信息
						MessageSend(MSG_FIFO_PROTOCOL, sendMsg);
						
						break;
					
					case SINGLE_BARREL_480L:		//单桶480L
					case SINGLE_BARREL_660L:		//单桶660L					
						printf("\r\n左桶重量: %2.2f Kg，%2.2f Kg -> %2.2f Kg，距离：%3.1f cm\r\n",\
									(float)getWeight1/100, (float)getWeightBefore1/100, (float)getWeightAfter1/100, (float)getDistance1);
						
						printf("\r\n故障码：%d\r\n", faultCode_1);
						
						//显示用户投放信息
						Screen_ShowPutInInfo(deviceType, leftBarrelType, 0, getWeight1, 0);

						sendMsg.msgType = 6011;										//向后台发送投放信息
						MessageSend(MSG_FIFO_PROTOCOL, sendMsg);
						break;
						
					default:
						break;
				}

// 				FaultCodeCheck(deviceType);			//故障检测上传
				
				printf("\r\n---------->投放完成，谢谢使用！！！\r\n");
				
				putInStatus = 0x02;
				timer_restart(&displayTimer);
			}
			break;
			
		case 0x02:		//停留显示投放信息界面
			if(timer_expired(&displayTimer))
			{
				//如果有投放，则等待上传回复投放后的余额
				if((getWeight2 > 0) || (getWeight1 > 0) || (smallBottleCount > 0))
				{
					printf("\r\n本次开门有投放，等待上传回复投放后的余额\r\n");
					
					if((deviceType == SINGLE_BARREL_480L) && ((leftBarrelType == KITCHEN) || (leftBarrelType == OTHER)))		//厨余桶暂时不显示奖励金额
					{
						AudioOutput(3);
						Screen_Exit();											//显示“退出使用界面”
						printf("\r\n投放完成，退出！\r\n");
						putInStatus = 0x05;
						timer_restart(&outTimer);
					}else{
						putInStatus = 0x03;
						timer_restart(&waitingUploadTimer);
					}
				}else{																//没有投放则转到谢谢使用界面退出					
					AudioOutput(3);
				
					Screen_Exit();											//显示“退出使用界面”
					printf("\r\n显示投放时间到，且无投放退出\r\n");
					putInStatus = 0x05;
					timer_restart(&outTimer);
				}
				
			}
			break;
			
		case 0x03:		//等待上传成功显示投放后的余额
			if(putInUploadSuccessMark == 1)
			{
				putInUploadSuccessMark = 0;
				printf("\r\n之前余额 = %.2f \r\n", (float)capital/100);
				printf("\r\n之后余额 = %.2f \r\n", (float)capitalAfter/100);
				printf("\r\n奖励金额 = %.2f \r\n", (float)(capitalAfter - capital)/100);
				
				if(capitalAfter >= capital)
				{
					Screen_ShowPutInReward(capital, capitalAfter);
				
// 					printf("\r\n奖励金额 = %.2f \r\n", (float)(capitalAfter - capital)/100);
// 					printf("\r\n余    额 = %.2f \r\n", (float)capitalAfter/100);
				}
				
				putInStatus = 0x04;
				timer_restart(&displayTimer);
			}
			
			if(timer_expired(&waitingUploadTimer))
			{
				audioNum = 3;
				audioOutputMark = 1;
					
				Screen_Exit();											//显示“退出使用界面”
				printf("\r\n显示投放时间到\r\n");
				putInStatus = 0x05;
				timer_restart(&outTimer);
			}
			break;
			
		case 0x04:
			if(timer_expired(&displayTimer))
			{
				audioNum = 3;
				audioOutputMark = 1;
				
				Screen_Exit();											//显示“退出使用界面”
				printf("\r\n显示投放时间到\r\n");
				putInStatus = 0x05;
				timer_restart(&outTimer);
			}
			break;
			
		case 0x05:		//停留显示退出界面
			if(timer_expired(&outTimer))
			{
				printf("\r\n显示结束\r\n");
				Screen_Default();
				ClearPutInInfo();
				putInProgress = 0;
				putInStatus = 0x00;
			}
			break;
			
//-----------------------------------------------------------------------------------------	
		
		case 0x06:		//装满禁止开门时显示装满提示
			if(timer_expired(&displayTimer))
			{
				printf("\r\n显示结束\r\n");
				Screen_Default();
				ClearPutInInfo();
				putInStatus = 0x00;
			}
			break;
			
		case 0x10:		//设备故障转到此处等待处理
			Screen_Error(0, displayFaultCode, 0);
				
			putInStatus = 0x11;
			break;
			
		case 0x11:
			//当限位开关出现故障之后运行到此，直到修复
			break;
			
		default:
			putInStatus = 0x00;
			break;
	}
}

/******************** (C) COPYRIGHT 2017 Ping *****END OF FILE*******************/
