/*
*********************************************************************************************************
*                                       IAR Development Kits
*                                              on the
*
*                                    M451
*
* Filename      : ext_data.c
* Version       : V1.00
* Programmer(s) : Qian Xianghong
*********************************************************************************************************
*/

/*
*********************************************************************************************************
*                                             INCLUDE FILES
*********************************************************************************************************
*/
#include "includes.h"

// 存储和上传到服务报文的循环缓冲（FRAM和SFlash存储）
loopbuff_t RF_DataM, RF_TranM;
// 上传数据的锁
SemaphoreHandle_t	   RF_TranLock = NULL; // 二值信号量句柄	

ext_gps_t  Ext_GPS;

ext_data_t Ext_Data_Rec, Ext_Data_Rec1;

ext_count_t bottleCnt, boxCnt;
ext_bottle_idx_t bottlePSN[FRAM_BOTTLE_COUNT];
ext_box_idx_t 	boxTGGU[FRAM_BOX_COUNT];
ext_box_idx_t 	boxPSN[FRAM_BOX_COUNT];

// 铁电和SFlash一致性记录
fram_sflash_valid_t FRAM_SFlash_valid = {0};
fram_sflash_valid_t SFlash_FRAM_valid = {0};

// 关联铁电和SFlash的一致性（用相对时间和绝对时间作为标记）
void CheckFramSFlashValid(uint32_t relative_time)
{
	if(FRAM_SFlash_valid.offset_seconds == 0)
	{
		// 写入铁电
		FRAM_SFlash_valid.relative_time = relative_time;
		FRAM_SFlash_valid.offset_seconds = RTC_offsetSeconds;
		FRAM_SaveInfo(FRAM_SFLASH_VALID_INFO_BASE, (uint8_t *) &FRAM_SFlash_valid, sizeof(fram_sflash_valid_t));
		// 写入SFlash
		SFlash_FRAM_valid.relative_time = relative_time;
		SFlash_FRAM_valid.offset_seconds = RTC_offsetSeconds;
		SFlash_SaveInfo(SFLASH_FRAM_VALID_INFO_BASE, (uint8_t *) &SFlash_FRAM_valid, sizeof(fram_sflash_valid_t));
	}
}

// TODO: 将精简数据转换成上传数据
void cacl_gprs_tran_data(ext_data_t *pGprs, data_sample_t *sample)
{
	rf_app_data_t *pData = (rf_app_data_t *) pGprs->payload;

	// 先计算其它数据
	memset(sample, 0, sizeof(data_sample_t));

	// 计算理论值
	Calculate_Theoretical_Params(pData->Sensor.bottle_type, pData->d, pData->L, pData->chargePct, pData->density * 0.001);
	sample->volumeTotal = Theoretical_Param.v - Theoretical_Param.v0;
	if(pData->Sensor.staDiff == RF_SENSOR_STATUS_NORMAL)
	{
		if(pData->Sensor.measure_type == 1)	// 称重
		{
			// 容积百分比
			sample->volumePct = pData->diff;
			// 体积
			sample->volume = sample->volumeTotal * (sample->volumePct * 0.0001);
			// 质量
			sample->weight = Vol2Quantity(sample->volume, pData->density * 0.001);
		}
		else
		{
			if(pData->Sensor.measure_type == 0)	// 差压
			{
				sample->diff = pData->diff * 0.01;
				// 液位
				sample->height = Diff2Level(sample->diff, pData->Sensor.bottle_type, pData->d, pData->L); // 单位mm
			}
			else // 电容
			{
				sample->height = pData->diff;
			}

			// 体积
			sample->volume = Level2Vol(sample->height, pData->Sensor.bottle_type, pData->d, pData->L);
			// 质量
			sample->weight = Vol2Quantity(sample->volume, pData->density * 0.001);
			// 充装比
			sample->volumePct = (float) sample->volume / sample->volumeTotal * 10000;
		}
	}				
}

// 将一个数据加入队列
// RF_DataM和RF_TranM用一个存储区，写指针始终相同，读指针可能不同
void Data_PutToQueue(ext_data_t *pGprs)
{
	int32_t nextPtr;
	uint32_t second1, second2;
	rf_app_data_t *pData = (rf_app_data_t *) pGprs->payload;
	int16_t idx;
	ext_data_t tran;
	static data_sample_t sample;
	S_RTC_TIME_DATA_T sRTC;
	char tggu[16], mmWC[8], kg[8], pct[8], press[8], tempr[8];
	ext_bottle_t	bottleRec;
			
	// 如果中继器有定位，且采集时间和当前时间相差在30分钟之内，用中继器的定位取代设备端的定位
	second1 = Calc_SecondsFromYear(INITIAL_YEAR, dcBuff.dtuData.sysTime.year + 2000, dcBuff.dtuData.sysTime.month, 
					dcBuff.dtuData.sysTime.day, dcBuff.dtuData.sysTime.hour, dcBuff.dtuData.sysTime.minute, dcBuff.dtuData.sysTime.second);
	second2 = pGprs->recvTime - pData->relative_time;
	if(dcBuff.dtuData.posState && (second1 + 1800 < second2 || second2 + 1800 < second1))
	{
		pData->longitude = dcBuff.dtuData.longitude;
		pData->latitude = dcBuff.dtuData.latitude;
	}

	// TODO: 将精简数据转换成上传数据
	cacl_gprs_tran_data(pGprs, &sample);
	pGprs->volumePct = sample.volumePct;
	pGprs->volume = sample.volume;
	// 判断是否满足报警条件
	pGprs->warn = 0;
	if(dcBuff.configBottle.fullPct > 0 && pData->Sensor.staDiff == RF_SENSOR_STATUS_NORMAL && sample.volumePct >= dcBuff.configBottle.fullPct)
		pGprs->warn = 1;
	else if(dcBuff.configBottle.emptyPct > 0 && pData->Sensor.staDiff == RF_SENSOR_STATUS_NORMAL && sample.volumePct <= dcBuff.configBottle.emptyPct)
		pGprs->warn = 1;
	else if(dcBuff.configBottle.warnPressH > 0 && pData->Sensor.staPress == RF_SENSOR_STATUS_NORMAL && pData->press >= dcBuff.configBottle.warnPressH)
		pGprs->warn = 1;
	else if(dcBuff.configBottle.warnPress > 0 && pData->Sensor.staPress == RF_SENSOR_STATUS_NORMAL && pData->press <= dcBuff.configBottle.warnPress)
		pGprs->warn = 1;
	else if(dcBuff.configBottle.warnTemprH > -300 && pData->Sensor.staETempr1 == RF_SENSOR_STATUS_NORMAL && pData->tempr >= dcBuff.configBottle.warnTemprH)
		pGprs->warn = 1;
	else if(dcBuff.configBottle.warnTempr > -300 && pData->Sensor.staETempr1 == RF_SENSOR_STATUS_NORMAL && pData->tempr <= dcBuff.configBottle.warnTempr)
		pGprs->warn = 1;
	else if(dcBuff.configBottle.warnVolt > 0 && pData->voltage <= dcBuff.configBottle.warnVolt)
		pGprs->warn = 1;
	
	// 判断是否故障
	pGprs->fault = 0;
	if(pData->Sensor.staDiff == RF_SENSOR_STATUS_UNDERFLOW || pData->Sensor.staDiff == RF_SENSOR_STATUS_OVERFLOW)
		pGprs->fault = 1;
	else if(pData->Sensor.staPress == RF_SENSOR_STATUS_UNDERFLOW || pData->Sensor.staPress == RF_SENSOR_STATUS_OVERFLOW)
		pGprs->fault = 1;
	else if(pData->Sensor.staETempr1 == RF_SENSOR_STATUS_UNDERFLOW || pData->Sensor.staETempr1 == RF_SENSOR_STATUS_OVERFLOW)
		pGprs->fault = 1;
	
	// 移动写指针
	nextPtr = LoopBuff_GetNextPtr(&RF_DataM, RF_DataM.info.wtPtr);
	
	// 如果发送队列已满
	if(nextPtr == RF_TranM.info.rdPtr)
	{
		// 移动读指针（丢弃第一条数据）
		RF_TranM.info.rdPtr = LoopBuff_GetNextPtr(&RF_TranM, RF_TranM.info.rdPtr);
	}
	// 如果数据队列已满
	if(nextPtr == RF_DataM.info.rdPtr)
	{
		// 读出第一条记录
		SFlash_LoadInfo(LoopBuff_GetDataPos(&RF_DataM, RF_DataM.info.rdPtr), (uint8_t *) &tran, sizeof(ext_data_t));

		// 修改储罐档案
		idx = Ext_Lookup_Bottle_PSN(tran.oriPSN, bottleCnt.count);
		if(idx >= 0)
		{
			FRAM_BufferRead(FRAM_BOTTLE_DATA_BASE + sizeof(ext_bottle_t) * bottlePSN[idx].recNo, (uint8_t *) &bottleRec, sizeof(ext_bottle_t));
			if(bottleRec.lastData == RF_DataM.info.rdPtr)
			{
				bottleRec.lastData = -1;
				FRAM_BufferWrite(FRAM_BOTTLE_DATA_BASE + sizeof(ext_bottle_t) * bottlePSN[idx].recNo, (uint8_t *) &bottleRec, sizeof(ext_bottle_t));
			}
		}

		// 移动读指针（丢弃第一条数据）
		RF_DataM.info.rdPtr = LoopBuff_GetNextPtr(&RF_DataM, RF_DataM.info.rdPtr);
	}
	
	// 建立单向链表，以提高查询速度（双向链表需要反复擦写SFlash）
	// 支持从后往前（按时间倒序）查，结束条件为以下3种之一：
	// 1. 无前一条记录；2. 前一条记录PSN不符；3. 前一条记录的时间戳比当前记录大
	pGprs->prevData = -1;
	
	// 查找储罐档案
	idx = Ext_Lookup_Bottle_PSN(pGprs->oriPSN, bottleCnt.count);
	if(idx >= 0)
	{
		FRAM_BufferRead(FRAM_BOTTLE_DATA_BASE + sizeof(ext_bottle_t) * bottlePSN[idx].recNo, (uint8_t *) &bottleRec, sizeof(ext_bottle_t));
		pGprs->prevData = bottleRec.lastData;
		
		// 修改储罐档案
		bottleRec.lastData = RF_DataM.info.wtPtr;
		bottleRec.warn = pGprs->warn;
		bottleRec.fault = pGprs->fault;
		bottleRec.recvTime = pGprs->recvTime;
		
		FRAM_BufferWrite(FRAM_BOTTLE_DATA_BASE + sizeof(ext_bottle_t) * bottlePSN[idx].recNo, (uint8_t *) &bottleRec, sizeof(ext_bottle_t));
	}
	
	// 将新数据加到最后
	SFlash_SaveInfo(LoopBuff_GetDataPos(&RF_DataM, RF_DataM.info.wtPtr), (uint8_t *) pGprs, sizeof(ext_data_t));	
	
	// 保存到铁电
	RF_DataM.info.wtPtr = nextPtr;
	FRAM_SaveInfo(RF_DataM.info_base, (uint8_t *) &RF_DataM.info, sizeof(RF_DataM.info));
	RF_TranM.info.wtPtr = nextPtr;
	FRAM_SaveInfo(RF_TranM.info_base, (uint8_t *) &RF_TranM.info, sizeof(RF_TranM.info));
	
	// 加入首页显示
	Wakeup_CalcUTCTime(pGprs->recvTime - pData->relative_time, &sRTC);
	idx = Ext_Lookup_Box_PSN(pGprs->oriPSN, boxCnt.count);
	if(idx >= 0)
		strcpy(tggu, boxPSN[idx].TGGU);
	else
		strcpy(tggu, "");
	if(pData->Sensor.staDiff == RF_SENSOR_STATUS_NOCONNECT)
	{
		strcpy(mmWC, "---");
		strcpy(kg, "---");
		strcpy(pct, "---");
	}
	else if(pData->Sensor.staDiff == RF_SENSOR_STATUS_UNDERFLOW)
	{
		strcpy(mmWC, "-EE");
		strcpy(kg, "-EE");
		strcpy(pct, "-EE");
	}
	else if(pData->Sensor.staDiff == RF_SENSOR_STATUS_OVERFLOW)
	{
		strcpy(mmWC, "+EE");
		strcpy(kg, "+EE");
		strcpy(pct, "+EE");
	}
	else
	{
		sprintf(mmWC, "%d", (uint32_t) KPa2mmH2O(pData->diff * 0.01));
		sprintf(kg, "%d", Vol2Quantity(pGprs->volume, pData->density * 0.001));
		sprintf(pct, "%.1f", pGprs->volumePct * 0.01);
	}
	if(pData->Sensor.staPress == RF_SENSOR_STATUS_NOCONNECT)
		strcpy(press, "---");
	else if(pData->Sensor.staPress == RF_SENSOR_STATUS_UNDERFLOW)
		strcpy(press, "-EE");
	else if(pData->Sensor.staPress == RF_SENSOR_STATUS_OVERFLOW)
		strcpy(press, "+EE");
	else
		sprintf(press, "%.2f", pData->press * 0.001);
	if(pData->Sensor.staETempr1 == RF_SENSOR_STATUS_NOCONNECT)
		strcpy(tempr, "---");
	else if(pData->Sensor.staETempr1 == RF_SENSOR_STATUS_UNDERFLOW)
		strcpy(tempr, "-EE");
	else if(pData->Sensor.staETempr1 == RF_SENSOR_STATUS_OVERFLOW)
		strcpy(tempr, "+EE");
	else
		sprintf(tempr, "%d", pData->tempr);
	sprintf(gridAll.rowStr[0], " %02d-%02d %02d:%02d  %11s  %7s    %7s  %5s  %7s   %6s   %2s   %2s    20%02d%02d%02d%02d%03d", 
		sRTC.u32Month, sRTC.u32Day, sRTC.u32Hour, sRTC.u32Minute, 
		tggu, mmWC, kg, pct, press, tempr, (pGprs->warn ? "W" : ""), (pGprs->fault ? "F" : ""),
		pGprs->oriPSN[0], pGprs->oriPSN[1], pGprs->oriPSN[2], pGprs->oriPSN[3], (pGprs->oriPSN[4] << 8) | pGprs->oriPSN[5]);
	Form_ScrollGrid(&gridAll);
	
	// 加入报警页显示
	if(pGprs->warn || pGprs->fault)
	{
		strcpy(gridWarn.rowStr[0], gridAll.rowStr[0]);
		Form_ScrollGrid(&gridWarn);
	}
	
	// 声光报警
	if(pGprs->warn && !IS_ALARM_ON())
		KZ_ALARM_ON();
	
	// 加入485发送缓冲
	LoopBuff_PutItem(&Modbus_SendM, (uint8_t *) pGprs);
	// 发消息给任务
	xSemaphoreGive(Modbus_SendQ);	
}

void Data_Open()
{
	int16_t i, j, len;
	uint8_t clearData = 0;
	ext_bottle_t	bottleRec;
	ext_box_t		boxRec;
	
	// 创建消息队列

	// 以下2个缓冲区共用存储区（RF_DataM为历史记录不删除，RF_TranM为上传成功以后删除）
	LoopBuff_Create(&RF_DataM, sizeof(ext_data_t), SFLASH_DATA_COUNT, FRAM_DATA_INFO_BASE, SFLASH_DATA_BASE);
	LoopBuff_Create(&RF_TranM, sizeof(ext_data_t), SFLASH_DATA_COUNT, FRAM_TRAN_INFO_BASE, SFLASH_DATA_BASE);
	
	// 创建互斥锁
	RF_TranLock = xSemaphoreCreateBinary();
	xSemaphoreGive(RF_TranLock);
	
#if 0
	// 测试代码，手动建立几个储罐档案
	bottleCnt.count = 500;
	for(i = 0; i < bottleCnt.count; i++)
	{
		memset(&bottleRec, 0, sizeof(ext_bottle_t));
		bottleRec.PSN[0] = 10;
		bottleRec.PSN[3] = (10 + i * 10) / 1000;
		bottleRec.PSN[4] = ((10 + i * 10) % 1000) >> 8;
		bottleRec.PSN[5] = ((10 + i * 10) % 1000) & 0xFF;
		bottleRec.lastData = -1;
		FRAM_BufferWrite(FRAM_BOTTLE_DATA_BASE + sizeof(ext_bottle_t) * i, (uint8_t *) &bottleRec, sizeof(ext_bottle_t));
	}	
	FRAM_SaveInfo(FRAM_BOTTLE_INFO_BASE, (uint8_t *) &bottleCnt, sizeof(ext_count_t));
#endif
	
#if 0
	// 测试代码，清除储罐档案
	bottleCnt.count = 0;
	//FRAM_SaveInfo(FRAM_BOTTLE_INFO_BASE, (uint8_t *) &bottleCnt, sizeof(ext_count_t));
	FRAM_SaveInfo(0, 0, FRAM_SIZE);
	
#endif
	
#if 0
	// 测试代码，手动建立几个储罐扩展档案
	boxCnt.count = 5;
	for(i = 0; i < boxCnt.count; i++)
	{
		memset(&boxRec, 0, sizeof(ext_box_t));
		boxRec.PSN[0] = 26;
		boxRec.PSN[3] = (0 + i * 1) / 1000;
		boxRec.PSN[4] = ((0 + i * 1) % 1000) >> 8;
		boxRec.PSN[5] = ((0 + i * 1) % 1000) & 0xFF;
		strcpy(boxRec.TGGU, "TGGU2000000");
		boxRec.TGGU[7] = '0' + (0 + i) / 100;
		boxRec.TGGU[8] = '0' + (0 + i) % 100 / 10;
		boxRec.TGGU[9] = '0' + (0 + i) % 10;
		FRAM_BufferWrite(FRAM_BOX_DATA_BASE + sizeof(ext_box_t) * i, (uint8_t *) &boxRec, sizeof(ext_box_t));
	}	
	FRAM_SaveInfo(FRAM_BOX_INFO_BASE, (uint8_t *) &boxCnt, sizeof(ext_count_t));
#endif
	
	// 从铁电读取储罐档案，建立按PSN的索引
	FRAM_LoadInfo(FRAM_BOTTLE_INFO_BASE, (uint8_t *) &bottleCnt, sizeof(ext_count_t));
	for(i = 0; i < bottleCnt.count; i++)
	{
		FRAM_BufferRead(FRAM_BOTTLE_DATA_BASE + sizeof(ext_bottle_t) * i, (uint8_t *) &bottleRec, sizeof(ext_bottle_t));
		memmove(bottlePSN[i].PSN, bottleRec.PSN, 6);
		bottlePSN[i].recNo = i;
	}
	Ext_Sort_Bottle_PSN(bottlePSN, 0, bottleCnt.count - 1);

	// 从铁电读取储罐扩展档案，建立按TGGU的索引
	FRAM_LoadInfo(FRAM_BOX_INFO_BASE, (uint8_t *) &boxCnt, sizeof(ext_count_t));
	for(i = 0; i < boxCnt.count; i++)
	{
		FRAM_BufferRead(FRAM_BOX_DATA_BASE + sizeof(ext_box_t) * i, (uint8_t *) &boxRec, sizeof(ext_box_t));
		// 转换成大写
		len = strlen(boxRec.TGGU);		
		for(j = 0; j < len; j++)
		{
			if(boxRec.TGGU[j] >= 'a' && boxRec.TGGU[j] <= 'z')
				boxRec.TGGU[j] -= 32;
		}
		strcpy(boxTGGU[i].TGGU, boxRec.TGGU);
		memmove(boxTGGU[i].PSN, boxRec.PSN, 6);
		boxTGGU[i].recNo = i;
		strcpy(boxPSN[i].TGGU, boxRec.TGGU);
		memmove(boxPSN[i].PSN, boxRec.PSN, 6);
		boxPSN[i].recNo = i;
	}
	Ext_Sort_Box_TGGU(boxTGGU, 0, boxCnt.count - 1);
	Ext_Sort_Box_PSN(boxPSN, 0, boxCnt.count - 1);
	
	// 从FRAM读取记录信息，并检查是否合法
	FRAM_LoadInfo(RF_DataM.info_base, (uint8_t *) &RF_DataM.info, sizeof(RF_DataM.info));
	FRAM_LoadInfo(RF_TranM.info_base, (uint8_t *) &RF_TranM.info, sizeof(RF_TranM.info));
	if(RF_DataM.info.rdPtr > RF_DataM.maxItemCount || RF_TranM.info.rdPtr > RF_TranM.maxItemCount 
		|| RF_DataM.info.wtPtr > RF_DataM.maxItemCount || RF_DataM.maxItemCount != RF_TranM.maxItemCount
		|| RF_DataM.data_base != RF_TranM.data_base || RF_DataM.itemSize != RF_TranM.itemSize
		|| RF_DataM.info.wtPtr != RF_TranM.info.wtPtr)
	{
		clearData = 1;
	}

	// 从FRAM和SFlash分别读取一致性记录，并比较是否一致
	if(!FRAM_LoadInfo(FRAM_SFLASH_VALID_INFO_BASE, (uint8_t *) &FRAM_SFlash_valid, sizeof(fram_sflash_valid_t))
		|| !SFlash_LoadInfo(SFLASH_FRAM_VALID_INFO_BASE, (uint8_t *) &SFlash_FRAM_valid, sizeof(fram_sflash_valid_t))
		|| FRAM_SFlash_valid.relative_time != SFlash_FRAM_valid.relative_time
		|| FRAM_SFlash_valid.offset_seconds != SFlash_FRAM_valid.offset_seconds)
	{
		// 如不一致，铁电里面的记录无效
		clearData = 1;
		// 清除一致性记录，待获取到UTC时间时重新设置
		memset(&FRAM_SFlash_valid, 0, sizeof(fram_sflash_valid_t));
		memset(&SFlash_FRAM_valid, 0, sizeof(fram_sflash_valid_t));
	}

	// 清除历史数据
	if(clearData)
	{
		memset(&RF_TranM.info, 0, sizeof(RF_TranM.info));
		FRAM_SaveInfo(RF_TranM.info_base, (uint8_t *) &RF_TranM.info, sizeof(RF_TranM.info));
		memset(&RF_DataM.info, 0, sizeof(RF_DataM.info));
		FRAM_SaveInfo(RF_DataM.info_base, (uint8_t *) &RF_DataM.info, sizeof(RF_DataM.info));
		for(i = 0; i < bottleCnt.count; i++)
		{
			FRAM_BufferRead(FRAM_BOTTLE_DATA_BASE + sizeof(ext_bottle_t) * i, (uint8_t *) &bottleRec, sizeof(ext_bottle_t));
			bottleRec.lastData = -1;
			bottleRec.warn = 0;
			bottleRec.fault = 0;
			FRAM_BufferWrite(FRAM_BOTTLE_DATA_BASE + sizeof(ext_bottle_t) * i, (uint8_t *) &bottleRec, sizeof(ext_bottle_t));
		}
	}
}

// 根据PSN查找档案序号，没有则返回-1
int16_t Ext_Lookup_Bottle_PSN(uint8_t PSN[], int16_t cnt)
{
    int16_t i0 = 0;
    int16_t i1 = cnt - 1;
    int16_t i;

	int16_t cmp;
	
	if(cnt <= 0)
		return -1;
	
    while(1)
    {
        i = (i0 + i1) / 2;
        cmp = memcmp(PSN, bottlePSN[i].PSN, 6);
		if(cmp == 0)
            return i;
        if(cmp > 0)
        {
            if(i == i1)
                return -1;
            i0 = i + 1;
        }
        else
        {
            if(i == i0)
                return -1;
            i1 = i - 1;
        }
    }

    return -1;
}

// 根据箱主序号查找档案序号，没有则返回-1
int16_t Ext_Lookup_Box_TGGU(char TGGU[], int16_t cnt)
{
    int16_t i0 = 0;
    int16_t i1 = cnt - 1;
    int16_t i;
	
	int16_t cmp;

	if(cnt <= 0)
		return -1;
	
    while(1)
    {
        i = (i0 + i1) / 2;
        cmp = strcmp(TGGU, boxTGGU[i].TGGU);
		if(cmp == 0)
            return i;
        if(cmp > 0)
        {
            if(i == i1)
                return -1;
            i0 = i + 1;
        }
        else
        {
            if(i == i0)
                return -1;
            i1 = i - 1;
        }
    }

    return -1;
}

// 根据PSN查找箱主序号，没有则返回-1
int16_t Ext_Lookup_Box_PSN(uint8_t PSN[], int16_t cnt)
{
    int16_t i0 = 0;
    int16_t i1 = cnt - 1;
    int16_t i;

	int16_t cmp;
	
	if(cnt <= 0)
		return -1;
	
    while(1)
    {
        i = (i0 + i1) / 2;
        cmp = memcmp(PSN, boxPSN[i].PSN, 6);
		if(cmp == 0)
            return i;
        if(cmp > 0)
        {
            if(i == i1)
                return -1;
            i0 = i + 1;
        }
        else
        {
            if(i == i0)
                return -1;
            i1 = i - 1;
        }
    }

    return -1;
}

// 快速排序法
int16_t partition_bottle_psn(ext_bottle_idx_t a[], int16_t lo, int16_t hi)
{
	int16_t i = lo;
	int16_t j = hi + 1;
	ext_bottle_idx_t tmp;
	
	while(1)
	{
		while(memcmp(a[++i].PSN, a[lo].PSN, 6) < 0)
		{
			if(i == hi)
				break;
		}
		
		while(memcmp(a[--j].PSN, a[lo].PSN, 6) > 0);
		
		if(i >= j)
			break;
		
		tmp = a[i];
		a[i] = a[j];
		a[j] = tmp;
	}

	tmp = a[lo];
	a[lo] = a[j];
	a[j] = tmp;
	
	return j;
}

void Ext_Sort_Bottle_PSN(ext_bottle_idx_t a[], int16_t lo, int16_t hi)
{
#if 0
	// 快速排序，递归函数在任务里需要Stack太大
	int16_t j;
	
	if(hi <= lo)
		return;
	
	j = partition_bottle_psn(a, lo, hi);
	Ext_Sort_Bottle_PSN(a, lo, j - 1);
	Ext_Sort_Bottle_PSN(a, j + 1, hi);
#else
	// 冒泡排序
	int16_t i, j, k;
	ext_bottle_idx_t tmp;

	for(i = 0; i < hi; i++)
	{
		// 找出后面最小的值
		k = i;
		for(j = i + 1; j <= hi; j++)
		{
			if(memcmp(a[j].PSN, a[k].PSN, 6) < 0)
				k = j;
		}
		
		// 交换
		if(k != i)
		{
			tmp = a[k];
			a[k] = a[i];
			a[i] = tmp;
		}
	}
#endif
}

// 快速排序法
int16_t partition_box_tggu(ext_box_idx_t a[], int16_t lo, int16_t hi)
{
	int16_t i = lo;
	int16_t j = hi + 1;
	ext_box_idx_t tmp;
	
	while(1)
	{
		while(strcmp(a[++i].TGGU, a[lo].TGGU) < 0)
		{
			if(i == hi)
				break;
		}
		
		while(strcmp(a[--j].TGGU, a[lo].TGGU) > 0);
		
		if(i >= j)
			break;
		
		tmp = a[i];
		a[i] = a[j];
		a[j] = tmp;
	}

	tmp = a[lo];
	a[lo] = a[j];
	a[j] = tmp;
	
	return j;
}

void Ext_Sort_Box_TGGU(ext_box_idx_t a[], int16_t lo, int16_t hi)
{
#if 0
	// 快速排序，递归函数在任务里需要Stack太大
	int16_t j;
	
	if(hi <= lo)
		return;
	
	j = partition_box_tggu(a, lo, hi);
	Ext_Sort_Box_TGGU(a, lo, j - 1);
	Ext_Sort_Box_TGGU(a, j + 1, hi);
#else
	// 冒泡排序
	int16_t i, j, k;
	ext_box_idx_t tmp;

	for(i = 0; i < hi; i++)
	{
		// 找出后面最小的值
		k = i;
		for(j = i + 1; j <= hi; j++)
		{
			if(strcmp(a[j].TGGU, a[k].TGGU) < 0)
				k = j;
		}
		
		// 交换
		if(k != i)
		{
			tmp = a[k];
			a[k] = a[i];
			a[i] = tmp;
		}
	}
#endif
}

// 快速排序法
int16_t partition_box_psn(ext_box_idx_t a[], int16_t lo, int16_t hi)
{
	int16_t i = lo;
	int16_t j = hi + 1;
	ext_box_idx_t tmp;
	
	while(1)
	{
		while(memcmp(a[++i].PSN, a[lo].PSN, 6) < 0)
		{
			if(i == hi)
				break;
		}
		
		while(memcmp(a[--j].PSN, a[lo].PSN, 6) > 0);
		
		if(i >= j)
			break;
		
		tmp = a[i];
		a[i] = a[j];
		a[j] = tmp;
	}

	tmp = a[lo];
	a[lo] = a[j];
	a[j] = tmp;
	
	return j;
}

void Ext_Sort_Box_PSN(ext_box_idx_t a[], int16_t lo, int16_t hi)
{
#if 0
	// 快速排序，递归函数在任务里需要Stack太大
	int16_t j;
	
	if(hi <= lo)
		return;
	
	j = partition_box_psn(a, lo, hi);
	Ext_Sort_Box_PSN(a, lo, j - 1);
	Ext_Sort_Box_PSN(a, j + 1, hi);
#else
	// 冒泡排序
	int16_t i, j, k;
	ext_box_idx_t tmp;

	for(i = 0; i < hi; i++)
	{
		// 找出后面最小的值
		k = i;
		for(j = i + 1; j <= hi; j++)
		{
			if(memcmp(a[j].PSN, a[k].PSN, 6) < 0)
				k = j;
		}
		
		// 交换
		if(k != i)
		{
			tmp = a[k];
			a[k] = a[i];
			a[i] = tmp;
		}
	}
#endif
}

// 更新从服务器下载的罐箱档案
// 数据格式为"<PSN后11位>,<罐箱号>;<PSN后11位>,<罐箱号>;....." (PSN前2位固定为"20")
// 如"21110180010,TGGU2000010;10000000020,TGGU2000020"
// 表示2个储罐：PSN: 2021110180010（TGGU2000010）；PSN: 2010000000020（TGGU2000020）
void Ext_Update_Boxes(char *buff)
{
	int16_t count = 0, len = strlen(buff);
	char *pChr;
	char record[32], psn[16];
	ext_box_t		boxRec;
	
	// 先删除所有储罐档案，以免在更新过程中出现数据混乱
	boxCnt.count = 0;
	
	while(len > 0)
	{
		// 先用';'查找一条记录
		pChr = strchr(buff, ';');
		if(pChr)
		{
			if(pChr >= buff + sizeof(record)) // 数据有误, 终止更新
				break;
			strncpy(record, buff, pChr - buff);
			record[pChr - buff] = 0;
			buff++; // 分号占的位置
		}
		else // 最后一条记录
		{
			if(len >= sizeof(record))		// 数据有误, 终止更新
				break;
			strcpy(record, buff);
		}
		
		// 更新循环条件
		buff += strlen(record);
		len = strlen(buff);
		
		// 初始化记录
		memset(&boxRec, 0, sizeof(ext_box_t));
		
		// 再用','查找字段
		pChr = strchr(record, ',');
		if(!pChr || pChr != record + 11)	// 数据有误, 终止更新
			break;
		strncpy(psn, record, pChr - record);
		psn[pChr - record] = 0;

		if(strlen(pChr + 1) >= sizeof(boxRec.TGGU)) // 数据有误, 终止更新
			break;		
		strcpy(boxRec.TGGU, pChr + 1);
		
		// 检查数据是否合法
		pChr = psn;
		while(*pChr)
		{
			if(!isdigit(*pChr)) // 数据有误, 终止更新
			{
				len = 0;
				break;
			}
			pChr++;
		}

		// 转换成大写
		pChr = boxRec.TGGU;
		while(*pChr)
		{
			if(*pChr >= 'a' && *pChr <= 'z')
				*pChr -= 32;
			pChr++;
		}
		
		// 添加记录
		boxRec.PSN[5] = atoi(psn + 8) & 0xFF;
		boxRec.PSN[4] = atoi(psn + 8) >> 8;
		psn[8] = 0;
		boxRec.PSN[3] = atoi(psn + 6);
		psn[6] = 0;
		boxRec.PSN[2] = atoi(psn + 4);
		psn[4] = 0;
		boxRec.PSN[1] = atoi(psn + 2);
		psn[2] = 0;
		boxRec.PSN[0] = atoi(psn);
		FRAM_BufferWrite(FRAM_BOX_DATA_BASE + sizeof(ext_box_t) * count, (uint8_t *) &boxRec, sizeof(ext_box_t));
		
		// 更新索引信息
		strcpy(boxTGGU[count].TGGU, boxRec.TGGU);
		memmove(boxTGGU[count].PSN, boxRec.PSN, 6);
		boxTGGU[count].recNo = count;
		strcpy(boxPSN[count].TGGU, boxRec.TGGU);
		memmove(boxPSN[count].PSN, boxRec.PSN, 6);
		boxPSN[count].recNo = count;
		
		// 总数加1
		count++;
	}

	// 重建索引
	Ext_Sort_Box_TGGU(boxTGGU, 0, count - 1);
	Ext_Sort_Box_PSN(boxPSN, 0, count - 1);
	
	// 更新储罐总数
	boxCnt.count = count;
	FRAM_SaveInfo(FRAM_BOX_INFO_BASE, (uint8_t *) &boxCnt, sizeof(ext_count_t));
}
