
/********************************************************************************
* File : Zebra32TestStepSample.cpp
* Copyright 2014-2015, Raytrex Technology Co., Ltd.	All Rights Reserved.
*
* A program calls ZebraDllWin32 to demo a linear testing step.
* This program run on Zebra card connecting.
*
* Author: T.J.Hsu
* Date: 2014.05.05
* To use, modify, reproduce, and distribute this file is strictly forbidden
* without the prior written permission of Raytrex Technology Co., Ltd.
********************************************************************************/
//
// Zebra32TestStepSample.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "windows.h"
#include "conio.h"
#include "stdlib.h"
#include "ZebraZ4U3DllWin32.h"


#define	__MONITORING__				0
#define __EXIT_STOP_MSG__			3

#define		__NO_ERROR_EXCHG__				0
#define		__ILLEGAL_STRM_DATA_SIZE__		-1
#define		__FAIL_ALLOCATED_RAW10_BUFFER__	-2

unsigned char *RawStrmToRaw10(unsigned char *pImgStrmBuffer, ULONG lImgStrmSiz, ULONG *lRaw10Siz, int *errState);
void freeRawExchgBuffer(unsigned char *pRawStrmBuffer);


//#define		__ChkZebraByThrd__

TCHAR    szDllVersion[_MAX_FNAME + _MAX_EXT + 16];
TCHAR	szZebraCardName[64];
TCHAR    szSensorName[64];
char    szTestStepsName[64];

int		nFoundZebras;
int		nAvailableIndx;
int				nOpenHanle = __USB_HANDLE_NOT_INIT__;
__HZHANDLE__	locTmpZHandle = 0;


#ifdef __ChkZebraByThrd__


HANDLE	hMonitorThrd;
DWORD	dw_MonitorThrdId;
HANDLE hMoniExitEvent;

static DWORD WINAPI lpZebraMonitorMsgThrd(LPVOID Arg)
{
	BOOL bRet;
	MSG msg;
	DWORD nCount;
	HANDLE pHandles[1];
	pHandles[0] = (HANDLE)Arg;
	nCount = 1;

	while (TRUE)
	{
		DWORD waitRet = MsgWaitForMultipleObjects(nCount, pHandles, FALSE, INFINITE, QS_POSTMESSAGE);
		if (waitRet == WAIT_OBJECT_0)	// be checked 1st for exit thread
		{
			break;
		}
		else if (waitRet == WAIT_OBJECT_0 + nCount)
		{
			MSG msg;
			if (GetMessage(&msg, NULL, 0, 0) >	0)
			{
				__PZEBRA_BOARD_INFO__ pZebraBoardInfo;
				__HZHANDLE__ hZhandle;
				//int			 nBoardId;

				switch (msg.message)
				{
				case WM_MIPI_DEVICEARRIVAL:
					hZhandle = (__HZHANDLE__)msg.wParam;
					//nBoardId = (int)msg.lParam;

					if (nOpenHanle >= 0)
						pZebraBoardInfo = getZebraBoardInfo(nOpenHanle, NULL);
					else
						pZebraBoardInfo = NULL;

					if (nOpenHanle < 0 ||
						(pZebraBoardInfo && pZebraBoardInfo->nZebraStatus != __Zebra_Open_DrvNoApp__ &&
						pZebraBoardInfo->nZebraStatus != __Zebra_Open_usable__))	// AP doesn't start now! ex. removed!
					{

						// open this new arrived board!
						if ((nOpenHanle = bInitUsbDevice(hZhandle, &locTmpZHandle)) >= 0)
						{
							// Ok, Zebra has open!
						}
						// if nOpenHanle can't open success, it would check by main loop 
						// while (...)
						// {
						//    postThreadMessage(dw_MonitorThrdId, WM_MIPI_PROGRESS_MONITOR, __MONITORING__, locTmpZHandle);
						// }
					}
					break;
				case WM_MIPI_DEVICEREMOVECOMPLETE:
					hZhandle = (__HZHANDLE__)msg.wParam;
					if (hZhandle == nOpenHanle)	// current active zebra removed
					{
						pZebraBoardInfo = getZebraBoardInfo(hZhandle, NULL);
						pZebraBoardInfo->nZebraStatus = __Zebra_Removed__;

						CloseUsbDevice(nOpenHanle);
						nOpenHanle = __USB_HANDLE_NOT_FOUND__;
					}
					break;

				case WM_MIPI_PROGRESS_MONITOR:
					if (nOpenHanle == __USB_DRV_OK_APP_NOT_READY__ && msg.wParam == __MONITORING__)
					{
						__HZHANDLE__ hTmpOpenHandle = (__HZHANDLE__)msg.lParam;
						if (__Zebra_Open_DrvNoApp__ == QueryZebraAppState(hTmpOpenHandle))	// check zebra application is ready on not!
						{
							// Application has start, restart connected by post arrial message
							// to re-open again
							CloseUsbDevice(hTmpOpenHandle);	// for stop the USB kernel state
							PostThreadMessage(dw_MonitorThrdId, WM_MIPI_DEVICEARRIVAL, hTmpOpenHandle, 0x0);		// To close mipi device
						}
					}
					else if (__EXIT_STOP_MSG__ == msg.wParam)
					{
						while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
							;
					}
					break;
				}	// switch
			}
		}
	}
	return 0;
}

#endif

int _tmain(int argc, _TCHAR* argv[])
{
	DWORD dwDllMSVer, dwDllLSVer;
	int nAliasIdCnt = 0, nMaxTestStep, selTestStep, TestCount;
	int mipiStreaming = __OPT_STREAMING_OFF__, hasStreamed = 0;
	char   AliasNames[64][64];	// current only allow maximum 64 alias items for this sample
	UCHAR   I2CcfgByte = 0;
	USHORT  i2cAddr;
	UCHAR   I2cSubAddr[4];
	int     errExchg;
	DWORD   dStartTicks;

	getDllVersion(szDllVersion, &dwDllMSVer, &dwDllLSVer);
	wsprintf(szDllVersion + wcslen(szDllVersion), L"(V%d.%d.%d.%d)", HIWORD(dwDllMSVer), LOWORD(dwDllMSVer), HIWORD(dwDllLSVer), LOWORD(dwDllLSVer));

	// Check Zebra is connected?
#ifdef __ChkZebraByThrd__
	__PZEBRA_BOARD_INFO__	pZebraBoardInfo;
	int						i;

	// Create a thread to monitor Zebra connection state
	hMoniExitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);		// Evernt for exit this Zebra monitor thread
	hMonitorThrd = (HANDLE)CreateThread(NULL, 0, lpZebraMonitorMsgThrd, hMoniExitEvent, 0, &dw_MonitorThrdId);

	// Set up this monitor thread to Zebra
	InitMipiUsbNotifyWin(NULL, dw_MonitorThrdId);

	nOpenHanle = __USB_HANDLE_NOT_INIT__;

	// looking all Zebras
	nFoundZebras = lookingViewableZebras();
	if (nFoundZebras == 0)
		_cprintf("Zebra card no found, please insert it!\n");

	for (i = 0; i < nFoundZebras; i++)
	{
		pZebraBoardInfo = getZebraBoardInfo((__HZHANDLE__)i, NULL);
		if (pZebraBoardInfo->nZebraStatus == __Zebra_NotYet_Init__)
		{
			nOpenHanle = bInitUsbDevice(i, &locTmpZHandle);
			break;
		}
	}

	while (nOpenHanle < 0)
	{
		// Kick monitor thread to monitor the Zebra Application
		Sleep(300);
		PostThreadMessage(dw_MonitorThrdId, WM_MIPI_PROGRESS_MONITOR, __MONITORING__, locTmpZHandle);		// To close mipi device
	}
#else
	// Set up library thread to monitor zebra - checking by getZebraStatus() function
	InitMipiUsbNotifyWin(NULL, NULL);	// without win-handle and thread id

	nOpenHanle = __USB_HANDLE_NOT_INIT__;

	nFoundZebras = lookingViewableZebras();
	if (nFoundZebras == 0)
		_cprintf("Zebra card no found, please insert it!\n");

	do
	{
		int ZebraStateRet;

		SZEBRASTATUS sZebraStatus;

		if (nFoundZebras == 0)
		{
			if ((nFoundZebras = ZebraCardNumber()) == 0)		// InitMipiUsbNotifyWin() would auto added new Zebra!
				continue;	// there is not a zebra found!
		}
		
		// looking a availabe zebra to open it!
		__PZEBRA_BOARD_INFO__	pZebraBoardInfo;
		int						i;
		for (i = 0; i < nFoundZebras; i++)
		{
			pZebraBoardInfo = getZebraBoardInfo((__HZHANDLE__)i, NULL);
			if (pZebraBoardInfo->nZebraStatus == __Zebra_NotYet_Init__)
			{
				nOpenHanle = bInitUsbDevice(i, &locTmpZHandle);
				break;
			}
		}
		if (i == nFoundZebras)	// no availabe device
		{
			Sleep(300);
			continue;
		}

		if (nOpenHanle == __USB_DRV_OK_APP_NOT_READY__)		// Kernel ready, Zebra not yet starting
		{
			_cprintf("Zebra Application not yet start!");
			while (TRUE)
			{
				if (1 == QueryZebraAppState(locTmpZHandle))
				{
					// release current kernel handle, the repon again
					CloseUsbDevice(locTmpZHandle);	// for stop the USB kernel state
					_cprintf("\n");
					break;
				}
				else
					Sleep(300);
			}	// recheck application connect?
		}
		else if (nOpenHanle >= 0)
			break;
	} while (nFoundZebras == 0 || nOpenHanle < 0);

#endif
	// here, Zebra is ready to accept command 
	_cprintf("downlaod Test steps Name\n(default:\"RTXCaptureTestSteps.ZebraCfg\" by type '*'):");
	scanf("%s", szTestStepsName);
	if (strlen(szTestStepsName) == 0)
		strcpy(szTestStepsName, "RTXCaptureTestSteps.ZebraCfg");
	else if (strlen(szTestStepsName) == 1 && *szTestStepsName == '*')
		strcpy(szTestStepsName, "RTXCaptureTestSteps.ZebraCfg");

	// Download ...
	_cprintf("Download test steps \"%s\"...", szTestStepsName);
	LoadRegisterFiles(nOpenHanle, szTestStepsName, NULL, 0);	// message callback is NULL, and  its arg to 0

	// find just download configuration how many test-steps
	nMaxTestStep = qureyZebraTestStepCount(nOpenHanle);

	// Collect the download steps alias name
	nAliasIdCnt = 0;
	if (getAliasRunStepsCntJustDownload(nOpenHanle))	// Check alias name ready for just download
	{
		char *pAlias;

		// find the 1st alias name that just download to Zebra
		pAlias = get1stDownloadStepAliasName(nOpenHanle);
		if (pAlias)
		{
			do
			{
				strcpy(AliasNames[nAliasIdCnt], pAlias);	// Save alias name
				nAliasIdCnt++;

				// Try find next alias name
			} while (pAlias = getNextDownloadStepAliasName(nOpenHanle));
		}
	}

	// Repeat following code for test one of test step that just downloaded.
	// Assume every useful test step provide a alias name
	do
	{
		int i;
		_cprintf("\n");
		for (i = 0; i < nAliasIdCnt; i++)	// show all alias name
			_cprintf("(%d):  %s\n", i, AliasNames[i]);

		_cprintf("\n(%d):  I2C read\n", i++);
		_cprintf("(%d):  I2C write\n\n", i++);
		_cprintf("(%d or more):  to exit\n", i);

		_cprintf("Select:");
		scanf("%d", &selTestStep);

		if (selTestStep < nAliasIdCnt)
		{
			int iRunId;

			_cprintf("Test count:");
			scanf("%d", &TestCount);
			
			_cprintf("\nmipi streaming mode (0: off, 1 = on):");
			scanf("%d", &mipiStreaming);
			if (mipiStreaming) mipiStreaming = __OPT_STREAMING_ON__;
			hasStreamed = 0;


			if (TestCount > 0)
			{
				_cprintf("Run test step \"%s\"...\n", AliasNames[selTestStep]);

				dStartTicks = GetTickCount();

				// Mapping alias name to a test id
				iRunId = getRunStepIdFromDownloadAliasList(nOpenHanle, AliasNames[selTestStep]);
				if (iRunId >= 0)
				{
					int ExAct = 0;

					for (int lp = 0; lp < TestCount; lp++)
					{
						// Run the test id (that mapping from alias name)
						_cprintf("Snap %d picture\n", lp);

						if (mipiStreaming)	// streaming mode is on?
						{
							if (0 == hasStreamed)	// not yet kick steaming
							{
								if (KickoffRunStep(nOpenHanle, iRunId, ExAct, mipiStreaming))
								{
									hasStreamed = 1;	// has streaming
								}
								else
								{
									_cprintf("KickoffRunStep error\n");
									continue;
								}
							}
						}
						else
						{
							if (FALSE == KickoffRunStep(nOpenHanle, iRunId, ExAct, mipiStreaming))
							{
								_cprintf("KickoffRunStep error\n");
								continue;
							}
						}

						// Check image is ready now?
						SRUNSTEPSTATE	sRunResult;
						int				nRet, k = 0;
						BOOL			bErrFlag = FALSE;
						SYSTEMTIME		LocalTime;
						HANDLE			hFile;
						char 			bmpFileName[128];
						char			*pSavName = "DemoSav";

						// repeat checking image snapping state
						while ((nRet = getRunStepstate(nOpenHanle, &sRunResult)) > 0)
						{
							/* if (sRunResult.runResultState == __Sta_step_snapping__)
							sRunResult.resultType == __AppendData__
							sRunResult.appDataLen = 2;
							sRunResult.appendBuffer [0] = how many image are snapped
							sRunResult.appendBuffer [1] = has snapped images count
							*/
							if (sRunResult.runResultState == __Sta_None_Imgage__)
							{
								// error, no image pic is snapped
								bErrFlag = TRUE;
								printf("No image error\n");
								break;
							}
							else if (sRunResult.runResultState == __Sta_step_snapping__ && sRunResult.resultType == __AppendData__)
							{
								// sRunResult.appendBuffer [0] --- picture count
								// sRunResult.appendBuffer [1] --- snapped pictured count

								if (sRunResult.appendBuffer[0] == 0)	// no image to snap
								{
									printf("No image to snap\n");
									bErrFlag = TRUE;
									break;
								}

								continue;
							}
							else if (sRunResult.runResultState == __Sta_step_succeed__ && sRunResult.resultType == __SnapPicture__)
							{
								// sRunResult.snapCnt = g_snappedPicCnt;									// simulate a snap count
								// sRunResult.pSnaPicSiz[i] = size of every picture
								break;
							}
						}	// while check image is ready ...

						// Get image from Zebra
						if (!bErrFlag)
						{
							UCHAR *pPic, errCode, *pPicRaw10;
							ULONG  uPicGotSiz, uRaw10Size;

							// Now!~image is ready!
							// Send command to get image picture
							GrabImageRequest(nOpenHanle, 0, sRunResult.snapCnt - 1, __Grab_opt_noneOperation__, 1);	//	only get assigned index only and wait

							for (k = 0; k < sRunResult.snapCnt; k++)
							{
								GetLocalTime(&LocalTime);
								sprintf(bmpFileName, "%s%02d%02d-%02d%02d%02d.%03d_Id#%d-%d.raw", pSavName, LocalTime.wMonth, LocalTime.wDay, LocalTime.wHour, LocalTime.wMinute, LocalTime.wSecond, LocalTime.wMilliseconds, iRunId, k);

								pPic = (UCHAR *)malloc(sRunResult.pSnaPicSiz[k]);
								uPicGotSiz = getPicture2Buffer(nOpenHanle, pPic, sRunResult.pSnaPicSiz[k], &errCode);

								pPicRaw10 = RawStrmToRaw10(pPic, uPicGotSiz, &uRaw10Size, &errExchg);
								if (errExchg == __NO_ERROR_EXCHG__)
								{
									// Save got picture to file
									hFile = CreateFileA(bmpFileName, // open One.txt
										GENERIC_WRITE,             // open for reading
										0,                        // do not share
										NULL,                     // no security
										CREATE_ALWAYS,            // existing file only
										FILE_ATTRIBUTE_NORMAL,    // normal file
										NULL);                    // no attr. template

									if (hFile != INVALID_HANDLE_VALUE)
									{
										DWORD wrLen;
										WriteFile(hFile, pPicRaw10, uRaw10Size, &wrLen, NULL);
										CloseHandle(hFile);
									}
									freeRawExchgBuffer(pPicRaw10);
								}
								free(pPic);
							}	// for all pictures
						}

					}	// for - get picture count

				}	// if - legal run id
			}
		}
		else if (selTestStep == nAliasIdCnt)
		{
			int RdLen, nRet;
			UCHAR i2cReadWriteBuffer[256];

			// i2c read
			I2CcfgByte = 0;		// default 7 bit address & ack must

			i2cAddr = 0x3c;						// address could be modified any 
			I2CcfgByte |= _ReadI2cDevice;		// set to read
			
			// sub-address option
			I2CcfgByte |= _2bytesRdSubAddr;		// 2 bytes sub-address, demo only
			I2cSubAddr[0] = 0x11;				// depend on '_2bytesRdSubAddr' option
			I2cSubAddr[1] = 0x22;				// 
			
			_cprintf("i2c read length:");
			scanf("%d", &RdLen);
			nRet = I2cRawDataRead2(nOpenHanle, I2CcfgByte, i2cAddr, I2cSubAddr, RdLen, i2cReadWriteBuffer);

			// Show i2c read result 
			if (nRet)
			{
				char szCaption[64];
				char szDatabuf[256 * 6] = { 0 };
				int i, j;
				sprintf(szCaption, "I2C read data");

				for (i = 0; i < nRet; i++)
				{
					j = strlen(szDatabuf);
					sprintf(szDatabuf + j, "0x%02x ", i2cReadWriteBuffer[i]);
				}
				i2cReadWriteBuffer[i] = 0;
				sprintf(szDatabuf + strlen(szDatabuf), "\n%s", i2cReadWriteBuffer);
				MessageBoxA(NULL, szDatabuf, szCaption, MB_OK);
			}
		}
		else if (selTestStep == (nAliasIdCnt + 1))
		{
			int WrLen, nRet;
			UCHAR i2cReadWriteBuffer[256];

			// i2c write
			I2CcfgByte = 0;		// default 7 bit address & ack must, default to write option
			i2cAddr = 0x3c;

			_cprintf("i2c write data:");
			scanf("%s", i2cReadWriteBuffer);
			WrLen = strlen((char *)i2cReadWriteBuffer);
			nRet = I2cRawDataWrite2(nOpenHanle, I2CcfgByte, i2cAddr, i2cReadWriteBuffer, WrLen);
		}



	} while (selTestStep < (nAliasIdCnt+2));	//+2 for I2c read/write



	//-------------
	CloseUsbDevice(nOpenHanle);
#ifdef __ChkZebraByThrd__
	::PostThreadMessage(dw_MonitorThrdId, WM_MIPI_PROGRESS_MONITOR, __EXIT_STOP_MSG__, 0);
	SetEvent(hMoniExitEvent);
	WaitForSingleObject(hMonitorThrd, INFINITE);	// Wait Thread exit
	CloseHandle(hMoniExitEvent);
	// Stop Zebra monitor setting
	StopMipiUsbNotifyWin();
#endif
	freeZebrasInfoListQueue();
	return 0;
}



unsigned char *RawStrmToRaw10(unsigned char *pImgStrmBuffer, ULONG lImgStrmSiz, ULONG *lRaw10Siz, int *errState)
{
	ULONG l, n;
	ULONG nRaw10Size = lImgStrmSiz / 5 * 8;
	unsigned char *pRaw10Buffer = NULL;
	unsigned char raw10Ext[4];

	if (lImgStrmSiz == 0)
	{
		if (errState) *errState = __ILLEGAL_STRM_DATA_SIZE__;
		return pRaw10Buffer;
	}

	if (lImgStrmSiz % 5) nRaw10Size += 8;	// to extra data size

	pRaw10Buffer = (unsigned char *)malloc(nRaw10Size);
	if (pRaw10Buffer == NULL)
	{
		if (errState) *errState = __FAIL_ALLOCATED_RAW10_BUFFER__;
		return pRaw10Buffer;
	}

	if (errState) *errState = __NO_ERROR_EXCHG__;
	*lRaw10Siz = nRaw10Size;

	l = n = 0;
	while (l < lImgStrmSiz)
	{
		int m;
		/*
		AAAA-AAAA BBBB-BBBB
		CCCC-CCCC DDDD-DDDD
		aabbccdd

		little ending raw8
		BBBB-BBBB AAAA-AAAA DDDD-DDDD CCCC-CCCC aabbccdd

		0000-00BB BBBB-BBbb
		0000-00AA AAAA-AAaa
		0000-00DD DDDD-DDdd
		0000-00CC CCCC-CCcc

		little ending => raw10
		BBBB-BBbb 0000-00BB AAAA-AAaa 0000-00AA DDDD-DDdd 0000-00DD CCCC-CCcc 0000-00CC
		*/
		raw10Ext[0] = (pImgStrmBuffer[l + 4] >> 4) & 0x03;
		raw10Ext[1] = (pImgStrmBuffer[l + 4] >> 6) & 0x03;
		raw10Ext[2] = (pImgStrmBuffer[l + 4] >> 0) & 0x03;
		raw10Ext[3] = (pImgStrmBuffer[l + 4] >> 2) & 0x03;
		for (m = 0; m < 4; m++)
		{
			pRaw10Buffer[n++] = (pImgStrmBuffer[l] << 2) + raw10Ext[m];	// low byte
			pRaw10Buffer[n++] = (pImgStrmBuffer[l++] >> 6);				// High byte only b1 b0
		}
		l++;	// skip padding
	}

	return pRaw10Buffer;
}

void freeRawExchgBuffer(unsigned char *pRawStrmBuffer)
{
	free(pRawStrmBuffer);
}
