﻿#include "jidu.h"
#include "jidu_dataCenter.h"
#include "tracer.h"
#include "algorithm.h"
#include "MyStringUtils.h"
#include <stdio.h>
#include "StationDataReportRecord.hpp"
#include "ReportRecordCollector.hpp"
#include "ReportTransporter.hpp"
#include "otxproxy.h"

vector<string> blockIdList;

string printReport;
bool optimizePrintReport(otx::jidu::PrintResultReportRecord* record);
bool optimizePrintReport(string record);
static std::mutex mItemLock, mBlockLock;
class ReportCollectorEventHandlerImpl : virtual public otx::jidu::ReportCollectorEventHandler {
public:
	ReportCollectorEventHandlerImpl() = default;
	ReportCollectorEventHandlerImpl(const ReportCollectorEventHandlerImpl&) = default;
	ReportCollectorEventHandlerImpl& operator=(const ReportCollectorEventHandlerImpl&) = default;
	virtual ~ReportCollectorEventHandlerImpl() = default;

public:
	virtual void onBlockCompleted(otx::jidu::StationDataReportRecord* newBlock) override {
		std::string text = newBlock->toJsonString();
		otx::jidu::ReportTransporter* transporter = otx::jidu::ReportTransporter::getDefault();
		transporter->getQueue()->enque(new std::string(text));
	}
	virtual void onStationResultCompleted(otx::jidu::StationResultReportRecord* newResult) override {
		std::string text = newResult->toJsonString();
		otx::jidu::ReportTransporter* transporter = otx::jidu::ReportTransporter::getDefault();
		transporter->getQueue()->enque(new std::string(text));
	}
	virtual void onPrintResultCompleted(otx::jidu::PrintResultReportRecord* newResult) override {
		std::string text = newResult->toJsonString();
		optimizePrintReport(text);
		otx::jidu::ReportTransporter* transporter = otx::jidu::ReportTransporter::getDefault();
		transporter->getQueue()->enque(new std::string(text));
	}
};
class ReportTransportActionImpl : virtual public otx::jidu::ReportTransportAction {
public:
	ReportTransportActionImpl() = default;
	ReportTransportActionImpl(const ReportTransportActionImpl&) = default;
	ReportTransportActionImpl& operator=(const ReportTransportActionImpl&) = default;
	virtual ~ReportTransportActionImpl() = default;
public:
	virtual int doTransport(std::string* reportText) override {
		cJSON* obj = cJSON_Parse(reportText->c_str());
		if (cJSON_IsObject(obj)) {
			if (cJSON_HasObjectItem(obj, "testTimeUtc")) {
				string timeString = cJSON_GetObjectItem(obj, "testTimeUtc")->valuestring;
				MyStringUtils::replace(timeString, " ", "T");
				MyStringUtils::replace(timeString, timeString.substr(timeString.find('.'), 4), "Z");

				cJSON_ReplaceItemInObject(obj, "testTimeUtc", cJSON_CreateString(timeString.c_str()));
			}
			if (cJSON_HasObjectItem(obj, "executionTimeUtc")) {
				string timeString = cJSON_GetObjectItem(obj, "executionTimeUtc")->valuestring;
				MyStringUtils::replace(timeString, " ", "T");
				MyStringUtils::replace(timeString, timeString.substr(timeString.find('.'), 4), "Z");
				cJSON_ReplaceItemInObject(obj, "executionTimeUtc", cJSON_CreateString(timeString.c_str()));
			}

			string resultType = "01";
			if (cJSON_HasObjectItem(obj, "resultType")) {
				resultType = cJSON_GetObjectItem(obj, "resultType")->valuestring;
			}

			char* temp = cJSON_PrintUnformatted(obj);
			if (resultType._Equal("03")) {
				callServer(31, temp, NULL, 0);
			}
			else {
				callServer(2, temp, NULL, 0);
			}

			cJSON_free(temp);
			cJSON_Delete(obj);
		}

		return 0;
	}
};

static ReportTransportActionImpl* action = new ReportTransportActionImpl();
otx::jidu::ReportTransporter* transporter = otx::jidu::ReportTransporter::getDefault();
otx::jidu::ReportRecordCollector collector;
string collector_vin;
string collector_serialNo;
int collector_ngCount = 0;
string stationName = "ECOS";
ReportCollectorEventHandlerImpl handler;

class ReportCache
{
public:
	string blockId;
	uint8_t* cmdReq;
	int reqLen;
	uint8_t* cmdAck;
	int ackLen;
	int result;
	string failReason;
	bool valid = true;
};

vector<string> m_vecReportCacheKey;
map<string, ReportCache> m_mapReportCache;

int jd_report_setStation(const char* station)
{
	if (0 == strncmp(station, "00", 2))
		stationName = "preFota";
	else if (0 == strncmp(station, "01", 2))
		stationName = "ECOS";
	else if (0 == strncmp(station, "02", 2))
		stationName = "FHC";
	else if (0 == strncmp(station, "03", 2))
		stationName = "返修";
	else if (0 == strncmp(station, "04", 2))
		stationName = "QFC";
	else
	{
		Tracer::error("invalid station code", station);
		return -1;
	}
	collector.collectStation(station);
	return int();
}

int jd_report_setReworkStation(const char* station)
{
	collector.collectReworkStation(station);
	return int();
}

int jd_report_setMixNo(const char* mixNo)
{
	collector.collectMixNo(mixNo);
	return int();
}

int jd_report_setCarConfig(const char* carConfig)
{
	collector.collectCarConfig(carConfig);
	return int();
}

int jd_report_setSerialNo(const char* serialNo)
{
	collector_serialNo = serialNo;
	collector.collectSerialNo(serialNo);
	return int();
}

int jd_report_setVin(const char* vin)
{
	collector_vin = vin;
	collector.collectVin(vin);
	return int();
}

int jd_report_setVehicleType(const char* vehicleType)
{
	collector.collectVehicleType(vehicleType);
	DataCenter::getInstance()->m_vehicleInfo.vehicleType = vehicleType;
	return int();
}

int jd_report_setCell(const char* cell)
{
	collector.collectCell(cell);
	return int();
}

int jd_report_getBncmFeedbackResult(char* outBuffer, int outBufferSize)
{
	string resultString = "NOK";
	string req = string("vin=").append(collector_vin);
	char* rBuffer = (char*)malloc(4096);

	for (int i = 0; i < 3; i++)
	{
		if (callServer(33, req.c_str(), rBuffer, 4096) > 0) {
			cJSON* objRes = cJSON_Parse(rBuffer);
			if (objRes != NULL) {
				if (cJSON_HasObjectItem(objRes, "data")) {
					cJSON* objData = cJSON_GetObjectItem(objRes, "data");
					if (cJSON_HasObjectItem(objData, "receiveStatus")) {
						cJSON* objStatus = cJSON_GetObjectItem(objData, "receiveStatus");
						if (cJSON_IsString(objStatus))
						{
							resultString = objStatus->valuestring;

						}
					}
				}
			}
			cJSON_Delete(objRes);
			break;
		}
		delay(1000);
	}
	free(rBuffer);


	memset(outBuffer, 0, outBufferSize);
	if (outBufferSize > resultString.length()) {
		memcpy(outBuffer, resultString.c_str(), resultString.length());
		return resultString.length();
	}
	return -1;
}

int jd_report_station_begin()
{
	std::lock_guard<std::mutex> lock(mItemLock);

	transporter->setAction(action);
	transporter->start();

	collector.addEventHandler(&handler);
	collector.collectBegin();

	blockIdList.clear();
	printReport.clear();

	for (auto it = m_mapReportCache.begin(); it != m_mapReportCache.end(); it++) {
		ReportCache reportCache = it->second;
		free(reportCache.cmdReq);
		free(reportCache.cmdAck);
	}
	m_mapReportCache.clear();

	m_vecReportCacheKey.clear();
	return int();
}

string getTpmsInfo(string stationName, string vehicleType) {

	static string tpmsInfo;
	tpmsInfo.clear();
	char tpmsBuffer[256] = { 0 };
	string vTpmsId = DataCenter::m_vehicleInfo.tpmsId;	//车上胎压
	string oTpmsId = DataCenter::m_VehicleOrderInfo.strTpmsId;//订单胎压

	//TODO 胎压信息HEX字符串用 空格 分割
	uint8_t u8Array[128] = { 0 };
	if (!vTpmsId.empty())
	{
		int rLen = radix_hex2Array(vTpmsId.c_str(), vTpmsId.length(), u8Array, 128);
		if (rLen > 0)
			MyStringUtils::radix_array2Hex(u8Array, rLen, vTpmsId, " ");
	}
	else {
		vTpmsId = "";
	}

	if (!oTpmsId.empty())
	{
		int rLen = radix_hex2Array(oTpmsId.c_str(), oTpmsId.length(), u8Array, 128);
		if (rLen > 0)
			MyStringUtils::radix_array2Hex(u8Array, rLen, oTpmsId, " ");
	}

	string supplierId = DataCenter::m_VehicleOrderInfo.strTpmsSupplierId;//供应商Id
	if (stationName._Equal("ECOS"))
	{
		if (oTpmsId.empty())
		{
			if (!supplierId.empty() && supplierId._Equal("00000000")) {
				sprintf(tpmsBuffer, "%s ", oTpmsId.c_str());
			}
			else {
				sprintf(tpmsBuffer, "%s轮胎混装，请返修。供应商ID: %s", vTpmsId.c_str(), supplierId.c_str());
			}
		}
		else if (oTpmsId._Equal(vTpmsId))
		{
			sprintf(tpmsBuffer, "%s ", oTpmsId.c_str());
		}
	}
	else if (stationName._Equal("FHC")) {
		if (oTpmsId.empty()) {
			if (!supplierId.empty() && supplierId._Equal("00000000")) {
				sprintf(tpmsBuffer, "%s", vTpmsId.c_str());
			}
			else {
				sprintf(tpmsBuffer, "%s 轮胎混装，请返修。供应商ID: %s", vTpmsId.c_str(), supplierId.c_str());
			}
		}
		else {
			if (oTpmsId._Equal(vTpmsId))
			{
				sprintf(tpmsBuffer, "%s ", oTpmsId.c_str());
			}
			else
			{
				sprintf(tpmsBuffer, "%s 轮胎混装，请返修。供应商ID: %s", vTpmsId.c_str(), supplierId.c_str());
			}
		}
	}

	tpmsInfo = tpmsBuffer;
	return tpmsInfo;
}

int jd_report_station_end()
{
	std::lock_guard<std::mutex> lock(mItemLock);

	//TODO 补充信息

	//TODO 上报胎压信息
	string tpmsId = getTpmsInfo(stationName, DataCenter::getInstance()->m_vehicleInfo.vehicleType);

	if (stationName._Equal("ECOS"))
	{
		collector.collectTpmsId(tpmsId);
	}
	else if (stationName._Equal("FHC"))
	{
		collector.collectTpmsId(tpmsId);

		//TODO 上报剩余电量
		string electricity = DataCenter::m_vehicleInfo.batteryRemain;
		collector.collectElectricity(electricity);
	}
	else if (stationName._Equal("preFota"))
	{
		//TODO 上报充电口盖打开状态
		string chargingIsOpen = (DataCenter::m_vehicleInfo.chargingIsOpen == 1) ? ("成功") : ("失败");
		collector.collectChargingIsOpen(chargingIsOpen);

		if (DataCenter::getInstance()->m_vehicleInfo.vehicleType._Equal("Venus"))
		{
			//TODO 手套箱打开状态
			string gloveBoxIsOpen = (DataCenter::m_vehicleInfo.gloveboxIsOpen == 1) ? ("成功") : ("失败");
			collector.collectGloveboxIsOpen(gloveBoxIsOpen);
		}
	}

	collector.collectEnd();
	std::this_thread::sleep_for(std::chrono::milliseconds(500));
	collector.removeEventHandler(&handler);
	return int();
}


int jd_report_block_begin(char* itemInfo)
{
	std::lock_guard<std::mutex> lock(mItemLock);
	if (itemInfo == NULL) {
		return -1;
	}

	JD_VEHICLE_ORDER_t vehicleOrderInfo = DataCenter::getInstance()->m_VehicleOrderInfo;
	string mixNo = vehicleOrderInfo.strMixNo;
	if (mixNo.empty()) {
		mixNo = "jmeter";
		Tracer::error("mixNo", "empty");
	}
	collector.collectMixNo(mixNo);

	vector<string> target;
	MyStringUtils::split(target, itemInfo, ",");
	if (target.size() < 4) {
		Tracer::error("param Err.", "itemInfo must have \"statisticId, statisticName, blockId, blockName\"");
		return -1;
	}

	string statisticId = target.at(0);
	string statisticName = target.at(1);
	string blockId = target.at(2);
	string blockName = target.at(3);

	auto it = std::find(blockIdList.begin(), blockIdList.end(), blockId);
	if (it != blockIdList.end()) {
		Tracer::warn("repeat call block begin", blockId);
		return -1;
	}
	Tracer::info("jd_report_block_begin", blockId);

	blockIdList.push_back(blockId);
	collector.blockBegin(statisticId, statisticName, blockId, blockName);
	return int();
}

int jd_report_block_end(char* itemInfo)
{
	std::lock_guard<std::mutex> lock(mBlockLock);
	if (itemInfo == NULL) {
		return -1;
	}

	vector<string> target;
	MyStringUtils::split(target, itemInfo, ",");
	if (target.size() < 2) {
		Tracer::error("param Err.", "itemInfo must have \"statisticId,blockId\"");
		return -1;
	}

	string statisticId = target.at(0);
	string blockId = target.at(1);
	Tracer::info("jd_report_block_end", blockId);

	auto it = std::find(blockIdList.begin(), blockIdList.end(), blockId);
	if (it != blockIdList.end()) {
		//TODO 添加缓存项
		for (int index = 0; index < m_vecReportCacheKey.size(); index++) {
			string tempKey = m_vecReportCacheKey.at(index);
			if (m_mapReportCache.find(tempKey) != m_mapReportCache.end())
			{
				ReportCache report = m_mapReportCache.at(tempKey);
				if (report.blockId == blockId && report.valid == true) {
					if (report.result) {
						jd_report_addOkItem((char*)tempKey.c_str(), report.cmdReq, report.reqLen, report.cmdAck, report.ackLen);
					}
					else {
						jd_report_addNokItem((char*)tempKey.c_str(), report.cmdReq, report.reqLen, report.cmdAck, report.ackLen, (char*)report.failReason.c_str());
					}

					report.valid = false;
					m_mapReportCache[tempKey] = report;
				}
			}
		}

		collector.blockEnd(statisticId, blockId);
		blockIdList.erase(it);//TODO 小项上报结束
	}
	else {
		Tracer::error("jd_report_block_end, invalid blockId ", blockId);
	}
	return int();
}

int jd_report_addOkItem(char* itemInfo, uint8_t* cmdReq, int reqLen, uint8_t* cmdAck, int ackLen)
{
	std::lock_guard<std::mutex> lock(mItemLock);
	Tracer::debug("OK itemInfo", itemInfo);

	if (itemInfo == NULL || cmdReq == NULL || reqLen < 1) {
		return -1;
	}

	string tempString = itemInfo;
	vector<string> target;
	MyStringUtils::split(target, tempString, ",");

	if (target.size() < 6) {
		Tracer::error("param Err.", "itemInfo must have \"blockId, itemName, itemId, ecuName, ecuAddr, expectedData\"");
		return -1;
	}

	string blockId = target.at(0);

	auto it = std::find(blockIdList.begin(), blockIdList.end(), blockId);
	if (it == blockIdList.end()) {
		Tracer::error("invalid blockId: " + blockId, "you need to call jd_report_block_begin at first");
		return -1;
	}

	collector.collectItemStepOK(blockId, target[2], target[1], target[3], target[4], target[5], cmdReq, reqLen, cmdAck, ackLen);

	return int();
}



int jd_report_addNokItem(char* itemInfo, uint8_t* cmdReq, int reqLen, uint8_t* cmdAck, int ackLen, char* failReason)
{
	std::lock_guard<std::mutex> lock(mItemLock);
	Tracer::debug("NG itemInfo", string(itemInfo) + "|" + failReason);

	if (itemInfo == NULL || cmdReq == NULL || reqLen < 1) {
		return -1;
	}

	vector<string> target;
	MyStringUtils::split(target, itemInfo, ",");
	if (target.size() < 6) {
		Tracer::error("param Err.", "itemInfo must have \"blockId, itemName, itemId, ecuName, ecuAddr, expectedData\"");
		return -1;
	}

	string blockId = target[0];
	auto it = std::find(blockIdList.begin(), blockIdList.end(), blockId);
	if (it == blockIdList.end()) {
		Tracer::error("invalid blockId: " + blockId, "you need to call jd_report_block_begin at first");
		return -1;
	}

	string failMsg = failReason;
	MyStringUtils::trim(failMsg);
	if (ackLen == 0 && MyStringUtils::endWith(failMsg, ":")) {
		failMsg.append(" N/A");
	}

	if (failMsg.find("请求") != string::npos) {

#if false
		vector<string> vec;
		MyStringUtils::split(vec, failMsg, ",");
		for (string tempFailMsg : vec) {
			MyStringUtils::trim(tempFailMsg);
			collector.collectItemStepNOK(target[0], target[2], target[1], target[3], target[4], target[5], tempFailMsg, cmdReq, reqLen, cmdAck, ackLen);
		}
#else
		MyStringUtils::replace(failMsg, ",", "\n");
		collector.collectItemStepNOK(target[0], target[2], target[1], target[3], target[4], target[5], failMsg, cmdReq, reqLen, cmdAck, ackLen);
#endif
	}
	else {
		collector.collectItemStepNOK(target[0], target[2], target[1], target[3], target[4], target[5], failMsg, cmdReq, reqLen, cmdAck, ackLen);
	}

	return int();
}

int jd_report_addCacheItem(char* itemInfo, uint8_t* cmdReq, int reqLen, uint8_t* cmdAck, int ackLen, int result, char* failReason)
{
	std::lock_guard<std::mutex> lock(mItemLock);
	if (itemInfo == NULL || cmdReq == NULL || reqLen < 1) {
		return -1;
	}

	string tempString = itemInfo;//"blockId, itemName, itemId, ecuName, ecuAddr, expectedData"
	vector<string> target;
	MyStringUtils::split(target, tempString, ",");

	if (target.size() < 6) {
		Tracer::error("param Err.", "itemInfo must have \"blockId, itemName, itemId, ecuName, ecuAddr, expectedData\"");
		return -1;
	}

	char* cmdHex = (char*)malloc(2 * reqLen + 1);
	memset(cmdHex, 0, 2 * reqLen + 1);
	radix_array2Hex(cmdReq, reqLen, cmdHex, 2 * reqLen + 1);
	string key = tempString + "," + cmdHex;
	free(cmdHex);

	if (m_mapReportCache.find(key) == m_mapReportCache.end()) {
		Tracer::debug("no catch key", key);
		m_vecReportCacheKey.push_back(key);

		string blockId = target.at(0);
		ReportCache report;
		report.blockId = blockId;

		report.reqLen = reqLen;
		report.cmdReq = (uint8_t*)malloc(reqLen);
		memcpy(report.cmdReq, cmdReq, reqLen);

		report.ackLen = ackLen;
		report.cmdAck = (uint8_t*)malloc(ackLen);
		memcpy(report.cmdAck, cmdAck, ackLen);
		report.result = result;
		if (result == 0)
			report.failReason.assign(failReason);
		m_mapReportCache[key] = report;
	}
	else {
		Tracer::debug("exist catch key", key);
		ReportCache report = m_mapReportCache.at(key);

		free(report.cmdReq);
		report.reqLen = reqLen;
		report.cmdReq = (uint8_t*)malloc(reqLen);
		memcpy(report.cmdReq, cmdReq, reqLen);

		free(report.cmdAck);
		report.ackLen = ackLen;
		report.cmdAck = (uint8_t*)malloc(ackLen);
		memcpy(report.cmdAck, cmdAck, ackLen);
		//report.valid = true;
		report.result = result;
		if (result == 0)
			report.failReason.assign(failReason);
		m_mapReportCache[key] = report;
	}

	return int();
}

bool optimizePrintReport(string record)
{
	cJSON* objRoot = cJSON_Parse(record.c_str());
	if (objRoot == NULL) {
		return false;
	}

	string serialNo = (cJSON_HasObjectItem(objRoot, "serialNo") ? cJSON_GetObjectItem(objRoot, "serialNo")->valuestring : "");
	string vehicleType = (cJSON_HasObjectItem(objRoot, "vehicleType") ? cJSON_GetObjectItem(objRoot, "vehicleType")->valuestring : "");
	string vin = (cJSON_HasObjectItem(objRoot, "vin") ? cJSON_GetObjectItem(objRoot, "vin")->valuestring : "");
	string cell = (cJSON_HasObjectItem(objRoot, "cell") ? cJSON_GetObjectItem(objRoot, "cell")->valuestring : "");
	string carConfig = (cJSON_HasObjectItem(objRoot, "carConfig") ? cJSON_GetObjectItem(objRoot, "carConfig")->valuestring : "");

	string executionTimeUtc = (cJSON_HasObjectItem(objRoot, "executionTimeUtc") ? cJSON_GetObjectItem(objRoot, "executionTimeUtc")->valuestring : "");
	string testTimeUtc = (cJSON_HasObjectItem(objRoot, "testTimeUtc") ? cJSON_GetObjectItem(objRoot, "testTimeUtc")->valuestring : "");
	string stationTime = (cJSON_HasObjectItem(objRoot, "stationTime") ? cJSON_GetObjectItem(objRoot, "stationTime")->valuestring : "");

	string testBlockTotalNum = (cJSON_HasObjectItem(objRoot, "testBlockTotalNum") ? cJSON_GetObjectItem(objRoot, "testBlockTotalNum")->valuestring : "0");
	string testBlockFailNum = (cJSON_HasObjectItem(objRoot, "testBlockFailNum") ? cJSON_GetObjectItem(objRoot, "testBlockFailNum")->valuestring : "0");
	string testStatisticTotalNum = (cJSON_HasObjectItem(objRoot, "testStatisticTotalNum") ? cJSON_GetObjectItem(objRoot, "testStatisticTotalNum")->valuestring : "0");
	string testStatisticFailNum = (cJSON_HasObjectItem(objRoot, "testStatisticFailNum") ? cJSON_GetObjectItem(objRoot, "testStatisticFailNum")->valuestring : "0");

	string sshStatus = (DataCenter::m_vehicleInfo.sshStaus == 1) ? ("打开") : ("关闭");
	string chargingIsOpen = (DataCenter::m_vehicleInfo.chargingIsOpen == 1) ? ("成功") : ("失败");
	string gloveboxIsOpen = (DataCenter::m_vehicleInfo.gloveboxIsOpen == 1) ? ("成功") : ("失败");

	cJSON* objHead = cJSON_CreateObject();
	cJSON* objBody = cJSON_CreateObject();
	cJSON* objTail = cJSON_CreateObject();

	cJSON_AddStringToObject(objHead, "工位", stationName.c_str());
	cJSON_AddStringToObject(objHead, "车辆代码", vin.c_str());
	cJSON_AddStringToObject(objHead, "车辆型号", vehicleType.c_str());
	cJSON_AddStringToObject(objHead, "车型配置", carConfig.c_str());
	cJSON_AddStringToObject(objHead, "设备号", cell.c_str());
	cJSON_AddStringToObject(objHead, "测试时间", testTimeUtc.c_str());
	cJSON_AddStringToObject(objHead, "工位工时", stationTime.c_str());

	cJSON* arrayTestStatisticList = cJSON_GetObjectItem(objRoot, "testStatisticList");
	for (int i = 0; i < cJSON_GetArraySize(arrayTestStatisticList); i++) {
		cJSON* objTestStatistic = cJSON_GetArrayItem(arrayTestStatisticList, i);
		string testStatisticName = (cJSON_HasObjectItem(objTestStatistic, "testStatisticName") ? cJSON_GetObjectItem(objTestStatistic, "testStatisticName")->valuestring : "");
		int testStatisticId = (cJSON_HasObjectItem(objTestStatistic, "testStatisticId") ? cJSON_GetObjectItem(objTestStatistic, "testStatisticId")->valueint : 0);
		string testStatisticResult = (cJSON_HasObjectItem(objTestStatistic, "testStatisticResult") ? cJSON_GetObjectItem(objTestStatistic, "testStatisticResult")->valuestring : "");
		//TODO 以下大项不显示在打印报告
		if (testStatisticId == 0x22/*充电口盖*/ || testStatisticId == 0x25/*手套箱*/) {
			continue;
		}

		if (testStatisticResult._Equal("NOK")) {
			cJSON* objStatistic = cJSON_CreateObject();
			if (cJSON_HasObjectItem(objTestStatistic, "testBlockList"))
			{
				cJSON* arrayTestBlockList = cJSON_GetObjectItem(objTestStatistic, "testBlockList");
				for (int j = 0; j < cJSON_GetArraySize(arrayTestBlockList); j++) {
					cJSON* objTestBlock = cJSON_GetArrayItem(arrayTestBlockList, j);
					string testBlockName = (cJSON_HasObjectItem(objTestBlock, "testBlockName") ? cJSON_GetObjectItem(objTestBlock, "testBlockName")->valuestring : "");
					string testBlockId = (cJSON_HasObjectItem(objTestBlock, "testBlockId") ? cJSON_GetObjectItem(objTestBlock, "testBlockId")->valuestring : "0");
					string testBlockResult = (cJSON_HasObjectItem(objTestBlock, "testBlockResult") ? cJSON_GetObjectItem(objTestBlock, "testBlockResult")->valuestring : "");
					int blockId = MyStringUtils::toLong(testBlockId, 16);

					if (cJSON_HasObjectItem(objTestBlock, "failReason")) {
						//TODO 以下小项不显示在打印报告
						if (blockId == 0x0601 || blockId == 0x0901)
						{
							continue;
						}
						else {
							cJSON* arrayFailReason = cJSON_GetObjectItem(objTestBlock, "failReason");
							cJSON* objFailReason = cJSON_CreateObject();
							for (int k = 0; k < cJSON_GetArraySize(arrayFailReason); k++) {
								string failReason = cJSON_GetArrayItem(arrayFailReason, k)->valuestring;
								//TODO 以下小项 失败原因不包含请求和响应字段
								if (blockId == 0x0C || blockId == 0x33 || blockId == 0x75) {
									if (failReason.find("请求") == string::npos && failReason.find("响应") == string::npos) {
										cJSON_AddStringToObject(objFailReason, failReason.c_str(), "");
									}
								}
								else {
									if (failReason.find(":请求") == string::npos && failReason.find(":响应") == string::npos) {
										cJSON_AddStringToObject(objFailReason, failReason.c_str(), "");
									}
									else {
										string subString = failReason.substr(0, 32);
										if (failReason.length() > 32)
										{
											subString.append("...");
										}
										cJSON_AddStringToObject(objFailReason, subString.c_str(), "");
									}
								}
							}
							cJSON_AddItemToObject(objStatistic, testBlockName.c_str(), objFailReason);
						}
					}
					else {
						cJSON_AddItemToObject(objStatistic, testBlockName.c_str(), cJSON_CreateNull());
					}
				}
			}

			if (cJSON_GetArraySize(objStatistic) > 0)
				cJSON_AddItemToObject(objBody, testStatisticName.c_str(), objStatistic);
			else
			{
				cJSON_AddStringToObject(objBody, testStatisticName.c_str(), "OK");
			}
		}
		else {
			cJSON_AddStringToObject(objBody, testStatisticName.c_str(), "OK");
		}
	}

	//TODO 补充信息
	string tpmsId = getTpmsInfo(stationName, DataCenter::getInstance()->m_vehicleInfo.vehicleType);

	if (stationName._Equal("ECOS"))
	{
		cJSON_AddStringToObject(objBody, ("胎压信息: " + tpmsId).c_str(), "");
	}
	else if (stationName._Equal("FHC"))
	{
		cJSON_AddStringToObject(objBody, ("电量: " + DataCenter::m_vehicleInfo.batteryRemain).c_str(), "");
		cJSON_AddStringToObject(objBody, ("胎压信息: " + tpmsId).c_str(), "");
	}
	else if (stationName._Equal("preFota"))
	{
		cJSON_AddStringToObject(objBody, ("充电口盖打开: " + chargingIsOpen).c_str(), "");
		cJSON_AddStringToObject(objBody, ("手套箱打开: " + gloveboxIsOpen).c_str(), "");
	}

	char tailBuffer[128] = { 0 };
	collector_ngCount = MyStringUtils::toLong(testStatisticFailNum, 16);
	sprintf(tailBuffer, "大项总数: %s, 不合格数:%s;", testStatisticTotalNum.c_str(), testStatisticFailNum.c_str());
	cJSON_AddStringToObject(objTail, tailBuffer, "");
	memset(tailBuffer, 0, 128);
	sprintf(tailBuffer, "小项总数: %s, 不合格数:%s.", testBlockTotalNum.c_str(), testBlockFailNum.c_str());
	cJSON_AddStringToObject(objTail, tailBuffer, "");

	cJSON* objTarget = cJSON_CreateObject();
	cJSON_AddStringToObject(objTarget, "reportId", serialNo.c_str());
	cJSON_AddItemToObject(objTarget, "head", objHead);
	cJSON_AddItemToObject(objTarget, "body", objBody);
	cJSON_AddItemToObject(objTarget, "tail", objTail);

	char* temp = cJSON_PrintUnformatted(objTarget);
	printReport = temp;
	cJSON_free(temp);

	cJSON_Delete(objTarget);
	return true;
}


//TODO mask报告转打印报告
static bool mask2PrintReport(cJSON* objMask) {

	cJSON* objHead = cJSON_CreateObject();
	cJSON* objBody = cJSON_CreateObject();
	cJSON* objTail = cJSON_CreateObject();

	if (cJSON_HasObjectItem(objMask, "station"))		cJSON_AddStringToObject(objHead, "工位", cJSON_GetObjectItem(objMask, "station")->valuestring);
	if (cJSON_HasObjectItem(objMask, "vehicleType"))	cJSON_AddStringToObject(objHead, "车辆型号", cJSON_GetObjectItem(objMask, "vehicleType")->valuestring);
	if (cJSON_HasObjectItem(objMask, "vin"))			cJSON_AddStringToObject(objHead, "车辆代码", cJSON_GetObjectItem(objMask, "vin")->valuestring);
	if (cJSON_HasObjectItem(objMask, "carConfig"))		cJSON_AddStringToObject(objHead, "车型配置", cJSON_GetObjectItem(objMask, "carConfig")->valuestring);
	if (cJSON_HasObjectItem(objMask, "cell"))			cJSON_AddStringToObject(objHead, "设备号", cJSON_GetObjectItem(objMask, "cell")->valuestring);
	if (cJSON_HasObjectItem(objMask, "testTimeUtc"))	cJSON_AddStringToObject(objHead, "测试时间", cJSON_GetObjectItem(objMask, "testTimeUtc")->valuestring);
	if (cJSON_HasObjectItem(objMask, "stationTime"))	cJSON_AddStringToObject(objHead, "工位工时", cJSON_GetObjectItem(objMask, "stationTime")->valuestring);

	cJSON* arrayMaskStatisticList = cJSON_GetObjectItem(objMask, "statisticList");
	for (int i = 0; i < cJSON_GetArraySize(arrayMaskStatisticList); i++) {
		cJSON* objMaskStatistic = cJSON_GetArrayItem(arrayMaskStatisticList, i);
		string testStatisticName = cJSON_GetObjectItem(objMaskStatistic, "testStatisticName")->valuestring;
		string testStatisticId = cJSON_GetObjectItem(objMaskStatistic, "testStatisticId")->valuestring;
		string testStatisticResult = cJSON_GetObjectItem(objMaskStatistic, "testStatisticResult")->valuestring;
		if (testStatisticResult._Equal("OK")) {
			cJSON_AddStringToObject(objBody, testStatisticName.c_str(), "OK");
		}
		else if (testStatisticResult._Equal("NG")) {
			cJSON* arrayMaskBlockList = cJSON_GetObjectItem(objMaskStatistic, "blockList");
			cJSON* objStatistic = cJSON_CreateObject();
			for (int j = 0; j < cJSON_GetArraySize(arrayMaskBlockList); j++) {
				cJSON* objMaskBlock = cJSON_GetArrayItem(arrayMaskBlockList, j);

				string testBlockName = cJSON_GetObjectItem(objMaskBlock, "testBlockName")->valuestring;
				string testBlockId = cJSON_GetObjectItem(objMaskBlock, "testBlockId")->valuestring;
				string testBlockResult = cJSON_GetObjectItem(objMaskBlock, "testBlockResult")->valuestring;
				if (testBlockResult._Equal("OK")) {
					cJSON_AddStringToObject(objStatistic, testBlockName.c_str(), "OK");
				}
				else if (testBlockResult._Equal("NG")) {
					cJSON* arrayMaskFailReasonList = cJSON_GetObjectItem(objMaskBlock, "failReasonList");
					cJSON* objFailReasonList = cJSON_CreateObject();
					for (int k = 0; k < cJSON_GetArraySize(arrayMaskFailReasonList); k++) {
						cJSON* objMaskFailReason = cJSON_GetArrayItem(arrayMaskFailReasonList, k);
						string failReason = cJSON_GetObjectItem(objMaskFailReason, "failReason")->valuestring;
						failReason.erase(0, failReason.find_first_not_of(" "));
						failReason.erase(failReason.find_last_not_of(" ") + 1);

						if (failReason.find(":请求") == string::npos && failReason.find(":响应") == string::npos) {
							cJSON_AddStringToObject(objFailReasonList, failReason.c_str(), "");
						}
						else {
							string subString = failReason.substr(0, 32);
							if (failReason.length() > 32)
							{
								subString.append("...");
							}
							cJSON_AddStringToObject(objFailReasonList, subString.c_str(), "");
						}
					}
					cJSON_AddItemToObject(objStatistic, testBlockName.c_str(), objFailReasonList);
				}
			}
			cJSON_AddItemToObject(objBody, testStatisticName.c_str(), objStatistic);
		}
	}

	//TODO 补充信息
	if (stationName._Equal("ECOS"))
	{
		if (cJSON_HasObjectItem(objMask, "tpmsId") && cJSON_IsString(cJSON_GetObjectItem(objMask, "tpmsId")))
		{
			cJSON_AddStringToObject(objBody, (string("胎压信息: ") + string(cJSON_GetObjectItem(objMask, "tpmsId")->valuestring)).c_str(), "");
		}
		else {
			cJSON_AddStringToObject(objBody, "胎压信息: ", "");
		}
	}
	else if (stationName._Equal("FHC"))
	{
		if (cJSON_HasObjectItem(objMask, "tpmsId") && cJSON_IsString(cJSON_GetObjectItem(objMask, "tpmsId")))
		{
			cJSON_AddStringToObject(objBody, (string("胎压信息: ") + string(cJSON_GetObjectItem(objMask, "tpmsId")->valuestring)).c_str(), "");
		}
		else {
			cJSON_AddStringToObject(objBody, "胎压信息: ", "");
		}

		if (cJSON_HasObjectItem(objMask, "elecDisplay") && cJSON_IsString(cJSON_GetObjectItem(objMask, "elecDisplay")))
			cJSON_AddStringToObject(objBody, (string("电量: ") + string(cJSON_GetObjectItem(objMask, "elecDisplay")->valuestring)).c_str(), "");
	}
	else if (stationName._Equal("preFota"))
	{
		if (cJSON_HasObjectItem(objMask, "chargingIsOpen") && cJSON_IsString(cJSON_GetObjectItem(objMask, "chargingIsOpen")))
			cJSON_AddStringToObject(objBody, (string("充电口盖打开: ") + string(cJSON_GetObjectItem(objMask, "chargingIsOpen")->valuestring)).c_str(), "");

		if (cJSON_HasObjectItem(objMask, "gloveboxIsOpen") && cJSON_IsString(cJSON_GetObjectItem(objMask, "gloveboxIsOpen")))
			cJSON_AddStringToObject(objBody, (string("手套箱打开: ") + string(cJSON_GetObjectItem(objMask, "gloveboxIsOpen")->valuestring)).c_str(), "");

	}

	int testStatisticTotalNum = 0, testStatisticFailNum = 0, testBlockTotalNum = 0, testBlockFailNum = 0;
	if (cJSON_HasObjectItem(objMask, "testStatisticTotalNum"))	testStatisticTotalNum = cJSON_GetObjectItem(objMask, "testStatisticTotalNum")->valueint;
	if (cJSON_HasObjectItem(objMask, "testStatisticFailNum"))	testStatisticFailNum = cJSON_GetObjectItem(objMask, "testStatisticFailNum")->valueint;
	if (cJSON_HasObjectItem(objMask, "testBlockTotalNum"))		testBlockTotalNum = cJSON_GetObjectItem(objMask, "testBlockTotalNum")->valueint;
	if (cJSON_HasObjectItem(objMask, "testBlockFailNum"))		testBlockFailNum = cJSON_GetObjectItem(objMask, "testBlockFailNum")->valueint;
	cJSON_AddStringToObject(objTail, (string("大项总数: ") + to_string(testStatisticTotalNum) + string(", 不合格数: ") + to_string(testStatisticFailNum)).c_str(), "");
	cJSON_AddStringToObject(objTail, (string("小项总数: ") + to_string(testBlockTotalNum) + string(", 不合格数: ") + to_string(testBlockFailNum)).c_str(), "");

	cJSON* objTarget = cJSON_CreateObject();
	cJSON_AddStringToObject(objTarget, "reportId", cJSON_GetObjectItem(objMask, "serialNo")->valuestring);
	cJSON_AddItemToObject(objTarget, "head", objHead);
	cJSON_AddItemToObject(objTarget, "body", objBody);
	cJSON_AddItemToObject(objTarget, "tail", objTail);

	char* temp = cJSON_PrintUnformatted(objTarget);
	printReport = temp;
	cJSON_free(temp);

	cJSON_Delete(objTarget);

	return true;
}

int jd_report_getResult(char* outBuffer, int outBufferSize)
{
	if (printReport.empty()) {
		Tracer::warn("report", "loading...");
		return 0;
	}

	//TODO MarsOne的打印报告优先使用服务端生成的mask
	if (collector_ngCount > 0 && (!stationName._Equal("返修"))) {
		otx::jidu::ReportTransporter* transporter = otx::jidu::ReportTransporter::getDefault();
		while (true) {
			if (transporter->getQueue()->size() == 0)
			{
				break;
			}
		}

		string req = string("vin=").append(collector_vin).append("&serialNo=").append(collector_serialNo);
		char* rBuffer = (char*)malloc(409600);
		for (int retry = 0; retry < 3; retry++) {
			std::this_thread::sleep_for(std::chrono::milliseconds(2000));

			if (callServer(32, req.c_str(), rBuffer, 409600) > 0) {
				cJSON* objRes = cJSON_Parse(rBuffer);
				if (objRes != NULL) {
					if (cJSON_HasObjectItem(objRes, "data")) {
						cJSON* objData = cJSON_GetObjectItem(objRes, "data");
						if (cJSON_IsTrue(cJSON_GetObjectItem(objData, "isMask")) && cJSON_HasObjectItem(objData, "transReportStationDataResp")) {
							mask2PrintReport(cJSON_GetObjectItem(objData, "transReportStationDataResp"));
						}
					}
				}
				cJSON_Delete(objRes);
				break;
			}
		}
		free(rBuffer);
	}

	string tempString = printReport;
	int rLen = tempString.length();
	if (rLen < outBufferSize && rLen != 0)
	{
		memcpy(outBuffer, tempString.c_str(), rLen);
		return rLen;
	}
	else {
		Tracer::error("outBufferSize", "too small to receive.");
		return -1;
	}
}

int jd_report_getQueueSize()
{
	std::lock_guard<std::mutex> lock(mItemLock);
	otx::jidu::ReportTransporter* transporter = otx::jidu::ReportTransporter::getDefault();
	return transporter->getQueue()->size();
}


