testtoolsconn/stat/desktop/source/lib/src/statengine.cpp
author Johnson Ma <johnson.ma@nokia.com>
Mon, 08 Mar 2010 15:04:18 +0800
changeset 0 3da2a79470a7
permissions -rw-r--r--
Initial EPL Contribution

/*
* Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:  
*
*/




#include "stdafx.h"
#include "STATEngine.h"
#include "STATCommon.h"
#include "INI.h"

#include <string>

//----------------------------------------------------------------------------
// Constructor
CSTATEngine::CSTATEngine()
: pImageVerify(NULL), pConverter(NULL), pComms(NULL), pDecoder(NULL), pLog(NULL),
  minimumdelay(100), maximumdelay(30000), eStopProcessing(STAT_RUN), iCurrentCommand(0), bMultithreaded(false),
  eConnectType(SymbianInvalid), pDeviceInfo(NULL), iDeviceCode(0), iMaxTimeLimit(STAT_MAXTIME), dataSocket(NULL), settingSocket(true), iTEFSharedData(NULL)
{	
	memset(&szAddress, 0, sizeof(szAddress));
	statIniFile.SetIniFileName(STAT_INI_NAME);
}


//----------------------------------------------------------------------------
// Destructor
CSTATEngine::~CSTATEngine()
{
	Release();
}


//----------------------------------------------------------------------------
// Set things up
int
CSTATEngine::Initialise(const STATCONNECTTYPE eConnect, const char *pAddress)
{
	iDelay = minimumdelay;




	// logging - should be the first to be created for all other classes that depend on it
	pLog = new CSTATLogFile;
 	if (!pLog)
 		return E_OUTOFMEM;

	int ret = SetScreenshotDefaultDirectory();
	if (ret != ITS_OK)
		return ret;

	// image verification
 	pImageVerify = new CSTATImageVerify(pLog);
 	if (!pImageVerify)
 		return E_OUTOFMEM;

	// bitmap conversion
	pConverter = new CSTATDataFormatConverter(pLog);
 	if (!pConverter)
 		return E_OUTOFMEM;

	// script decoder
	pDecoder = new CSTATScriptDecoder(pLog);
 	if (!pDecoder)
 		return E_OUTOFMEM;

	// communications
	pComms = new STATComms();
 	if (!pComms)
 		return E_OUTOFMEM;
	else
	{
		EnterCriticalSection(&CriticalSection);

		ret = pComms->SetTransport(eConnect);
		if (ret == ITS_OK)
		{
			ret = pComms->Initialise();

			if (ret == ITS_OK)
				ret = pComms->Connect(pAddress);

			if (ret == ITS_OK)
			{
				strcpy(szAddress, pAddress);	// save port in case of restart after error
				eConnectType = eConnect;
			}
		}

		LeaveCriticalSection(&CriticalSection);
	}
	return ret;
}



//----------------------------------------------------------------------------
// Release resources
int
CSTATEngine::Release(void)
{
	if (pImageVerify)
	{
		delete pImageVerify;
		pImageVerify = NULL;
	}

	if (pConverter)
	{
		delete pConverter;
		pConverter = NULL;
	}

	if (pDecoder)
	{
		delete pDecoder;
		pDecoder = NULL;
	}

	if (pDeviceInfo)
	{
		delete [] pDeviceInfo;
		pDeviceInfo = NULL;
	}

	if (iTEFSharedData)
	{
		delete [] iTEFSharedData;
		iTEFSharedData = NULL;
	}
	
	EnterCriticalSection(&CriticalSection);

	if (pComms)
	{
		delete pComms;
		pComms = NULL;
	}

	// release this one last in case other objects are using it...
	if (pLog)
	{
		pLog->CloseLogFile();
		delete pLog;
		pLog = NULL;
	}

	LeaveCriticalSection(&CriticalSection);
	return ITS_OK;
}


//----------------------------------------------------------------------------
// Sets up log file
int
CSTATEngine::SetLogging(const CString& logfilename, const char* prefix, bool append, bool bMessages, bool bFile,
	MessageReporter *const aMessageReporter)
{
	EnterCriticalSection(&CriticalSection);
	//set default logging path
	CString defaultDirectory=STAT_LOGFILEPATH_VALUE;
	//read from inifile if entry exists
	if(statIniFile.SectionExists(ST_TEST_KEY) )
	{
		CString setting;
		setting.Empty();
		setting=statIniFile.GetKeyValue(ST_LOGFILEPATH,ST_TEST_KEY);
		if(!setting.IsEmpty())
			defaultDirectory = setting;
	}
	int ret = pLog->CreateLogFile(logfilename,defaultDirectory, prefix, append, bMessages, bFile);

	pLog->SetMessageReporter(aMessageReporter);

	// in case our transport was initialised before logging was
	// display the initialisation message if there is one
	if (ret == LOG_FILE_OK)
		Message(pComms->Error());

	LeaveCriticalSection(&CriticalSection);
	return ret;
}


//----------------------------------------------------------------------------
// Sets up log file
void
CSTATEngine::SetCommandDelay(int iMillisecondDelay)
{
	iDelay = iMillisecondDelay;

	// set some limits
	if (iDelay < 100)
		iDelay = minimumdelay;

	if (iDelay > 30000)
		iDelay = maximumdelay;
}


//----------------------------------------------------------------------------
// Open, read and count the amount of commands on this script
int
CSTATEngine::GetCommandCount(CString file, int *pCount)
{
	EnterCriticalSection(&CriticalSection);

	int ret = pDecoder->Initialise(file, true);
	if (ret != ITS_OK)
		ret = pDecoder->Initialise(file, false);

	if (ret == ITS_OK)
	{
		CSTATScriptCommand *pSendCommand;
		int iCount = 0;
		
		// get a command from the script
		while (pDecoder->GetNextCommand(&pSendCommand) && ret == ITS_OK)
		{
			iCount++;
			if (pSendCommand->cCommandID == STAT_END)
				break;
		}

		// set the count
		if (ret == ITS_OK)
			(*pCount) = iCount;
	}

	pDecoder->Release();

	LeaveCriticalSection(&CriticalSection);
	return ret;
}


//----------------------------------------------------------------------------
// Open and read a script file's contents
int
CSTATEngine::OpenScriptFile(CString file, bool bIsFile)
{
	EnterCriticalSection(&CriticalSection);

	int ret = pDecoder->Initialise(file, bIsFile);

	LeaveCriticalSection(&CriticalSection);
	return ret;
}


//----------------------------------------------------------------------------
// Execute a script file
int
CSTATEngine::RunScript(ScriptProgressMonitor *const monitor)
{
	int ret = ITS_OK;
	iCurrentCommand = 0;
	eStopProcessing = STAT_RUN;
	iDeviceCode = 0;

	// anything smaller can cause problems and doesn't make sense anyway!
	if (iMaxTimeLimit < 1000)
		iMaxTimeLimit = 1000;

	// pointers to our command structures
	CSTATScriptCommand *pSendCommand;
	CSTATScriptCommand *pRecvCommand;

	char lastCommand = NULL;

	receivedData.Empty( );
	
	

	// get a command from the script
	while (pDecoder->GetNextCommand(&pSendCommand) && ret == ITS_OK)
	{
		iCurrentCommand++;

		if (StopProcessing())
		{
			pComms->Send(STAT_RESYNCID);
			ret = E_USERCANCEL;
			break;
		}

		if (lastCommand == STAT_REBOOT)
		{
			ret = ITS_OK;
			break;
		}

		switch(pSendCommand->cCommandID)
		{
			case 'P':
				Message(pSendCommand->Command());
				Sleep(atol(pSendCommand->Command()));
				break;
			case '/':
				Message(pSendCommand->Command());
				break;
			case '#':
				{
					Message(pSendCommand->Command());
					cScreenshotDirectory = pSendCommand->Command();
					if(cScreenshotDirectory.Right(1) != _T("\\"))
						cScreenshotDirectory += _T("\\");
					CreateAllDirectories(cScreenshotDirectory);
				}
				break;
			
			default:
				{

					// send the command and retrieve a response
					int iResyncErrors = 0;
					while ((ret = SendCommand(pSendCommand, &pRecvCommand)) == E_RESYNCCOMMAND)
					{
						Sleep(STAT_RETRYDELAY);
						iResyncErrors++;
						if (iResyncErrors > STAT_MAXERRORS)
						{
							Message("Too many resync errors - stopping");
							ret = E_COMMANDFAILED;
							break;
						}
						
					}

					if (ret == ITS_OK)
					{
						// perform special operations for these commands
						switch(pSendCommand->cCommandID)
						{
							case 'D':
								StoreData(pRecvCommand->Command(), pRecvCommand->Length(), pDeviceInfo);
								AppendCommandToSTATLog("*** DEVICE INFORMATION ***", pRecvCommand->Command(), pRecvCommand->Length());
								break;
							case 'S':
							{
								// convert and save the data returned in the response
								CString image = pSendCommand->Command();
								ret = ConvertAndSaveScreeenshot(image, pRecvCommand->Command(), pRecvCommand->Length());

								// imave verification
								if (ret == ITS_OK)
								{
									if (pImageVerify->IsActive() && pConverter->bWriteToFile)
									{
										ret = pImageVerify->VerifyImage(image);
										if (ret == VERIFICATION_PASS)
											ret = ITS_OK;
									}
								}
								break;
							}
						
							case 'T':
							{
								
								if(dataSocket==NULL)
								{

									// filename has been sent, now send the file itself
									CSTATScriptCommand oSendCommand;
									oSendCommand.cCommandID = pRecvCommand->cCommandID;
									
										// read and send the file contents
									if ((ret = ReadTransferFile(pSendCommand->Command(), &oSendCommand)) == ITS_OK)
									{
										int iResyncErrors = 0;
										while ((ret = SendCommand(&oSendCommand, &pRecvCommand)) == E_RESYNCCOMMAND)
										{
											Sleep(STAT_RETRYDELAY);
											iResyncErrors++;
											if (iResyncErrors > STAT_MAXERRORS)
											{
												Message("Too many resync errors - stopping");
												ret = E_COMMANDFAILED;
												break;
											}
										}
									}
									
								}
								else
								{
									//release the socket
									ret = ReleaseSocket();
									
								}
								

								break;
							}
							case 'R':
							case 'X':
							{	
								if(dataSocket==NULL)
								{
									// save the file contents
									ret = SaveTransferFile(pSendCommand->Command(), pRecvCommand->Command(), pRecvCommand->Length());
								}
								else
								{
									//release the socket
									ret = ReleaseSocket();
								}
								break;
							}
							case 'G':
							{
								// upload the device log file and write to STAT log file
								AppendCommandToSTATLog("*** DEVICE LOG ***", pRecvCommand->Command(), pRecvCommand->Length());
								break;
							}
							case STAT_REFRESH:
							case STAT_END:
							{
								ret = END_SCRIPT;
								break;
							}
							case 'N':
							{
								// Retrieve the TEF shared data
								StoreData(pRecvCommand->Command(), pRecvCommand->Length(), iTEFSharedData);
								AppendCommandToSTATLog("*** RETRIEVE TEF SHARED DATA ***", pRecvCommand->Command(), pRecvCommand->Length());
							}
							break;
							default:
							{
								Sleep(iDelay);
								break;
							}
						}
					}

					if (ret == ITS_OK)
					{
						// Data received from certain of the commands is stored
						// for retreival later.
						switch(pSendCommand->cCommandID)
						{
							case 'W':
							case 'V':
							//execute returns pid
							case 'J':
							//poll returns 0 1
							case '3':
								receivedData += oRecvCommand.Command();
								break;
							default:
								break;
						}
					}
				}
				break;
		}

		lastCommand = pSendCommand->cCommandID;

		if(monitor)
		{
			monitor->OnCompleteCommand( iCurrentCommand );
		}
	}

	pDecoder->Release();

	return ret;
}


//////////////////////////////////////////////////////////////////////////////////////////
// Converts a char * to it's Unicode equivalent
//
LPTSTR
CSTATEngine::ToUnicode(const char *string)
{
#ifdef UNICODE
	static TCHAR szBuffer[MAX_UNICODE_LEN + 1] = {0};
	szBuffer[0] = (TCHAR)0;

    // Convert to UNICODE.
    if (!MultiByteToWideChar(CP_ACP,					// conversion type
							 0,							// flags
							 string,					// source
							 -1,						// length
							 szBuffer,					// dest
							 MAX_UNICODE_LEN))			// length
    {
        return _T("Could not convert");
    }
    return szBuffer;
#else
	return (LPTSTR)string;
#endif
}


//////////////////////////////////////////////////////////////////////////////////////////
// Converts a Unicode to it's char * equivalent
//
char *
CSTATEngine::ToAnsi(LPCTSTR string)
{
#ifdef UNICODE
	static char szBuffer[MAX_UNICODE_LEN + 1] = {0};
	szBuffer[0] = (char)0;

    // Convert to ANSI.
    if (!WideCharToMultiByte(CP_ACP,					// conversion type
							 0,							// flags
							 string,					// source
							 -1,						// length
							 szBuffer,					// dest
							 MAX_UNICODE_LEN,			// length
							 NULL,
							 NULL ))
    {
        return "Could not convert";
    }
    return szBuffer;
#else
	return (char *)string;
#endif
}


//////////////////////////////////////////////////////////////////////////////////////////
// PRIVATE METHODS
//////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////////
// sets screenshot image directory
int CSTATEngine::SetScreenshotDefaultDirectory(void)
{
	//Set default logfile path
	CString path = ST_LOGFILEPATH_VALUE;
	//read from inifile if entry exists
	if(statIniFile.SectionExists(ST_TEST_KEY) )
	{
		CString setting;
		setting.Empty();
		setting=statIniFile.GetKeyValue(ST_LOGFILEPATH,ST_TEST_KEY);
		if(!setting.IsEmpty())
			path = setting;
	}

	
	cScreenshotDirectory = path;

	// add a backslash
	if (cScreenshotDirectory.Right(1) != _T('\\'))
		cScreenshotDirectory += _T('\\');

	cScreenshotDirectory += _T("Images\\");

	return ITS_OK;
}


//////////////////////////////////////////////////////////////////////////////////////
// Execute a complete command
//
// Sends a command, breaking it up into separate commands if required
//
// When sending, if larger than our buffer , break it up into correctly sized
// pieces and send these.  The first will be an empty one containing the total
// size, followed by buffer-sized pieces.  The last will be an empty command.
//

//////////////////////////////////////////////////////////////////////////////////////
int CSTATEngine::SendCommand(CSTATScriptCommand *pSendCommand, CSTATScriptCommand **ppRecvCommand)
{
	int ret = GENERAL_FAILURE;
	CSTATScriptCommand tempCommand;
	tempCommand.cCommandID = pSendCommand->cCommandID;


	// the first instance of a 'R' or 'T' command, it could set a data socket instead of a file
	if(pSendCommand->cCommandID=='T' || pSendCommand->cCommandID=='R')
	{
		if(dataSocket==NULL && settingSocket && pSendCommand->Command()!=NULL)
		{

			char *comma = strchr(pSendCommand->Command(),',');
			
			if(!(comma > pSendCommand->Command() + pSendCommand->Length()))
			{
	
				switch(pSendCommand->cCommandID)
				{
				case 'R':
					{
						char *colon = strchr(comma,':');
						
						if(colon==NULL)
							break;
	
						comma += 1;
						std::string ipAdd( comma, colon - comma );
						colon += 1;
						std::string ipPort( colon );
	
						ret = SetSocket(ipAdd.c_str() , atoi( ipPort.c_str() ));

						if(ret!=ITS_OK)
						{
							return ret;
						}
	
						break;
					}
	
				case 'T':
					{
						char *colon = strchr( pSendCommand->Command() , ':' );
	
						if(colon==NULL)
							break;
	
						std::string ipAdd( pSendCommand->Command() , colon - pSendCommand->Command() );
						colon += 1;
						
						if(comma < colon)
							break;
	
						std::string ipPort( colon , comma - colon );
	
						ret = SetSocket(ipAdd.c_str() , atoi(ipPort.c_str()));
						
						if(ret!=ITS_OK)
						{
							return ret;
						}

						break;
					}
				}
			}
		}

		settingSocket=false;
	}
	
	
	
	// send the first command
	if ((ret = SendSingleCommand(pSendCommand, ppRecvCommand)) == ITS_OK)
	{
		// check our response - if Serial/Infra-red, need to break it down
		if (pComms->GetMaxPacketSize() != 0)
		{

			
			// break up the command into buffer-sized chunks
			if (pSendCommand->Length() > pComms->GetMaxPacketSize() || (dataSocket!=NULL && pSendCommand->cCommandID=='T'))
			{
				int i = 0;
				unsigned long offset = 0;
				unsigned long ulTotalWritten = 0;
				unsigned long AmountToWrite = pComms->GetMaxPacketSize();
				unsigned long OriginalLength = pSendCommand->Length();
				
				if(dataSocket==NULL)
				{
					
					int iWrites = pSendCommand->Length() / pComms->GetMaxPacketSize() + 1;

					Message("Writing %d bytes of data in %d separate writes...", OriginalLength, iWrites);
					for (i=0;i<iWrites;i++)
					{
						offset = i * pComms->GetMaxPacketSize();

						if ((pSendCommand->Length() - offset) < pComms->GetMaxPacketSize())
							AmountToWrite = (pSendCommand->Length() - offset);

						if (AmountToWrite)
						{
							//Sleep(100);		// pause a bit for slower machines - probably doesn't need it but it can't hurt...

							// now send the command
							Message("Writing %d bytes from offset %d", AmountToWrite, offset);
							tempCommand.SetData(pSendCommand->Command() + ulTotalWritten, AmountToWrite);
							if ((ret = SendSingleCommand(&tempCommand, ppRecvCommand)) == ITS_OK)
							{
								//Message("%d bytes successfully written", AmountToWrite);
							}
							else
								return ret;

							ulTotalWritten += AmountToWrite;
						}
					}
					
				}
				else
				{

					//uses socket transmission

					char *pData = new char[AmountToWrite];
					
					tempCommand.ulLength = (unsigned long) -1;
					
					if((ret = SendSingleCommand(&tempCommand, ppRecvCommand)) != ITS_OK)
					{	
						delete [] pData;
						return ret;
					}

					while(true){


						int AmountToWrite = pComms->GetMaxPacketSize();
						
						ReadFromSocket(pData, &AmountToWrite );
											
						if(AmountToWrite>=0)
						{
							Message("Writing %d bytes", AmountToWrite);
							tempCommand.SetData(pData , AmountToWrite);
						
							if ((ret = SendSingleCommand(&tempCommand, ppRecvCommand)) == ITS_OK)
							{
							//Message("%d bytes successfully written", AmountToWrite);
							}
							else
							{
								settingSocket=true;
								delete [] pData;
								return ret;
							}

							ulTotalWritten +=AmountToWrite;
						}
						else
						{
							eStopProcessing = STAT_PAUSE;
							break;
						}
						
						if(AmountToWrite==0)
						{
							break;
						}
						
					}

					delete [] pData;
				
				}
			
				// once completely sent, send an empty command to show that we've finished
				
				
				if(dataSocket == NULL )
				{
					if (ulTotalWritten == OriginalLength)
					{
						// send the command that signals the end of our transmission
						Message("Sending completion command %c", tempCommand.cCommandID);
						tempCommand.SetData(NULL, 0);
						if ((ret = SendSingleCommand(&tempCommand, ppRecvCommand)) == ITS_OK)
						{
							//Message("Completion command successfully written");
						}
					}
					else
					{
						Message("Incorrect number of bytes written - expected %ld got %ld", ulTotalWritten, OriginalLength);
						ret = E_BADNUMBERBYTES;
					}
				}
			}
		}
	}

	if (ret == ITS_OK)
	{
		// check our response - if Serial/Infra-red, need to break it down
		// if < STAT_BUFFERSIZE, nothing more to do
		// if not, we need to read in as many times as it takes to
		// assemble our complete command response
		if (pComms->GetMaxPacketSize() != 0)
		{
			if (ppRecvCommand && (*ppRecvCommand)->Length() > pComms->GetMaxPacketSize())
			{
				unsigned long TotalLength = (*ppRecvCommand)->Length();

				Message("%s: About to read %d bytes of data", GetConnection(eConnectType), TotalLength);

				// allocate memory to hold entire command
				char *pTemp = new char [TotalLength];
				if (pTemp)
				{
					unsigned long ulTotalRead = 0;

					// empty packets
					tempCommand.SetData(NULL, 0);

					// now read data until we get an empty packet
					while((*ppRecvCommand)->Length())
					{
						//Sleep(100);		// pause a bit for slower machines - probably doesn't need it but it can't hurt...

						//Message("Sending continuation command %c", tempCommand.cCommandID);
						if ((ret = SendSingleCommand(&tempCommand, ppRecvCommand)) == ITS_OK)
						{
							if ((*ppRecvCommand)->Length())
							{
								Message("%s to offset %d", pComms->Error(), ulTotalRead);
								
								//copy the data into the buffer
								memcpy(pTemp + ulTotalRead, (*ppRecvCommand)->Command(), (*ppRecvCommand)->Length());
								
								if(dataSocket!=NULL)
								{
									int bytesSent = (int)(*ppRecvCommand)->Length();
								
									WriteToSocket((*ppRecvCommand)->Command() , &bytesSent);
									
									if(bytesSent <= 0)
									{
										eStopProcessing = STAT_PAUSE;
									}
								}
								
								

								//increment the pointer to the end of the first chunk received
								ulTotalRead += (*ppRecvCommand)->Length();
							}
						}
						else
						{
							settingSocket=true;
							delete [] pTemp;
							return ret;
						}
					}

					// make sure we got what we expected
					if (ulTotalRead == TotalLength)
					{
						// set our final response to the complete data transmission
						(*ppRecvCommand)->SetData(pTemp, ulTotalRead);
						//Message("Received successfully %ld bytes", ulTotalRead);
					}
					else
					{
						Message("Incorrect number of bytes read - expected %ld got %ld", ulTotalRead, TotalLength);
						ret = E_BADNUMBERBYTES;
					}

					// finished with it
					delete [] pTemp;
				}
				else
				{
					Message("Could not allocate %d bytes of memory", TotalLength);
					ret = E_OUTOFMEM;
				}
			}
			else if(dataSocket!=NULL && pSendCommand->cCommandID=='R')
			{
				int bytesSent = (int)(*ppRecvCommand)->Length();
				WriteToSocket((*ppRecvCommand)->Command(), &bytesSent);
			}
		}
	}

	

	return ret;
}

//////////////////////////////////////////////////////////////////////////////////////
// Execute a single command
//
// Once the command is sent, wait for a response.  If received and of the correct
// ID, save the contents (if any) and quit.  If no response, send RESYNC commands
// every few seconds to get response from other end.  On receipt of a RESYNC
// command, resend the previous command.  After no response for a minute or so, quit.
//////////////////////////////////////////////////////////////////////////////////////
int CSTATEngine::SendSingleCommand(CSTATScriptCommand *pSendCommand, CSTATScriptCommand **ppRecvCommand)
{
	int iTotalCommandTime = 0;
	int ret = GENERAL_FAILURE;

	LogDetails("Send", pSendCommand);

	// send the command
	EnterCriticalSection(&CriticalSection);
	ret = pComms->Send(pSendCommand->cCommandID, pSendCommand->Command(), pSendCommand->Length());
	LeaveCriticalSection(&CriticalSection);
	if (ret != ITS_OK)
	{
		Message(pComms->Error());
		return ret;
	}

	// The refresh command will not return anything due to the transport getting restarted.
	// So, don't wait for a response.
	if( (ret == ITS_OK) && (pSendCommand->cCommandID == STAT_REFRESH || pSendCommand->cCommandID == STAT_REBOOT)  )
	{
		return ret;
	}

	// wait for a response
	unsigned long ulLength = 0;
	char *pData = NULL;
	while(iTotalCommandTime < iMaxTimeLimit)
	{
		if (StopProcessing())
		{
			pComms->Send(STAT_RESYNCID);
			ret = E_USERCANCEL;
			break;
		}

		EnterCriticalSection(&CriticalSection);
		ret = pComms->Receive(&oRecvCommand.cCommandID, &pData, &ulLength);
		LeaveCriticalSection(&CriticalSection);
		
		if (ret == ITS_OK)
		{
			if (oRecvCommand.SetData(pData, ulLength))
			{
				oRecvCommand.ulLength = ulLength;
				*ppRecvCommand = &oRecvCommand;

				// invalid response received
				if (pSendCommand->cCommandID != oRecvCommand.cCommandID)
				{
					// need to resync back to start of this command
					if (oRecvCommand.cCommandID == STAT_RESYNCID)
					{
						Message("RESYNC response received - retrying command...");
						ret = E_RESYNCCOMMAND;
					}
					else
					{
						if (oRecvCommand.cCommandID == STAT_FAILURE)
						{
							if (oRecvCommand.Command() && oRecvCommand.Length())
							{
								Message("Command failed with error code %s",
										oRecvCommand.Command());
								iDeviceCode = atoi(oRecvCommand.Command());
							}
						}
						else
							Message("Invalid response received - expected %c received %c",
									pSendCommand->cCommandID, oRecvCommand.cCommandID);
						ret = GENERAL_FAILURE;
					}
				}
				else
					LogDetails("Receive", *ppRecvCommand);
			}
			else
				ret = E_OUTOFMEM;

			// at this point we have received something whether it's what we were expecting or not so exit loop
			break;
		}
		else if (ret == NO_DATA_AT_PORT)
		{
//			Message("Waiting for response");
			Sleep(STAT_RETRYDELAY);
			iTotalCommandTime += STAT_RETRYDELAY;
			ret = E_TOOMUCHTIME;
		}
		else
		{
			Message(pComms->Error());
			break;
		}
	}

	return ret;
}


//----------------------------------------------------------------------------
// Write command details to log
void CSTATEngine::LogDetails(const char *prefix, CSTATScriptCommand *pCommand)
{
	if (pCommand->Length() && (pCommand->Length() < MAX_LOG_MSG_LEN) && 
		pCommand->cCommandID != 'D' && pCommand->cCommandID != 'T' && pCommand->cCommandID != 'R' && pCommand->cCommandID != 'X' && pCommand->cCommandID != 'G')
	{
		char szContents[MAX_LOG_MSG_LEN + 1];
		unsigned long actuallen = MAX_LOG_MSG_LEN - 30 - strlen(prefix) - 6;  // max actual message length
		if (pCommand->Length() < actuallen)
			actuallen = pCommand->Length();

		strncpy(szContents, pCommand->Command(), actuallen);
		*(szContents + actuallen) = (char)0;
		Message("%s: ID: %c Contents: [%s] Length: [%lu]", prefix, pCommand->cCommandID, szContents, pCommand->Length());
	}
	else
		Message("%s: ID: %c Length: [%lu]", prefix, pCommand->cCommandID, pCommand->Length());
}


//----------------------------------------------------------------------------
// Read in a file's contents
//
// pFile will contain "pc path,device path" of which we want the 'pc path' part
int CSTATEngine::ReadTransferFile(const char *pFile, CSTATScriptCommand *pCommand)
{
	int ret = E_BADFILENAME;

	// get the default file location
	CString path = ST_WORKINGPATH_VALUE;
	//read from inifile if entry exists
	if(statIniFile.SectionExists(ST_TEST_KEY) )
	{
		CString setting;
		setting.Empty();
		setting=statIniFile.GetKeyValue(ST_WORKINGPATH,ST_TEST_KEY);
		if(!setting.IsEmpty())
			path = setting;
	}

	// add a backslash
	if (path.Right(1) != _T('\\'))
		path += _T('\\');

	// get the name of the file
	CString filename = pFile;
	int index = filename.ReverseFind(_T(','));
	if (index == -1)
		return E_BADFILENAME;

	// add the name
	path += filename.Left(index);

	Message("Opening file [%s]", ToAnsi(path));

	EnterCriticalSection(&CriticalSection);

	CFile script_file;
	if (script_file.Open(path, CFile::modeRead))
	{
		try
		{
			char *pData = new char [script_file.GetLength()];
			if (pData)
			{
				script_file.Read(pData, script_file.GetLength());

				if(pCommand->SetData(pData, script_file.GetLength()))
					ret = ITS_OK;
				else
					ret = E_OUTOFMEM;

				delete [] pData;
			}
			else
				ret = E_OUTOFMEM;
		}
		catch(CFileException *e)
		{
			e->Delete();
			ret = GENERAL_FAILURE;
		}

		script_file.Abort();
	}

	LeaveCriticalSection(&CriticalSection);
	return ret;
}







//----------------------------------------------------------------------------
// Save a data stream to file
//
// pFile will contain "device path,pc folder" of which we want the 'pc folder' part
int CSTATEngine::SaveTransferFile(const char *pFile, char *pContents, unsigned long ulLength)
{
	int ret = E_BADFILENAME;

	CString path = ST_WORKINGPATH_VALUE;
	//read from inifile if entry exists
	if(statIniFile.SectionExists(ST_TEST_KEY) )
	{
		CString setting;
		setting.Empty();
		setting=statIniFile.GetKeyValue(ST_WORKINGPATH,ST_TEST_KEY);
		if(!setting.IsEmpty())
			path = setting;
	}
	// add a backslash
	if (path.Right(1) != _T('\\'))
		path += _T('\\');

	// get any additional sub folders
	CString folder = pFile;
	int index = folder.Find(_T(','));
	if (index == -1)
		return E_BADFILENAME;
	// if the comma is the last char there are no folders to add
	if (folder.Right(1) != _T(','))
	{
		path += folder.Mid(index + 1);

		// add a backslash
		if (path.Right(1) != _T('\\'))
			path += _T('\\');
	}

	if (bMultithreaded)
	{
		// add a sub-folder for the connection type
		path += GetConnection(eConnectType);
		path += _T(".");
		path += szAddress;
		path += _T('\\');
	}

	// now extract the filename from the first argument
	CString filename = folder.Left(index);
	index = filename.ReverseFind(_T('\\'));
	if (index == -1)
		path += filename;
	else
		path += filename.Mid(index + 1);

	Message("Saving file [%s]", ToAnsi(path));

	ret = SaveTheFile(path, pContents, ulLength);
	if (ret != ITS_OK)
		Message("Error saving [%s] (%d)", ToAnsi(path), GetLastError());
	return ret;
}


//----------------------------------------------------------------------------
// Convert and save a screenshot
int CSTATEngine::ConvertAndSaveScreeenshot(CString &file, const char *pContents, const unsigned long ulLength)
{
	int ret = E_BADFILENAME;

	// clean out any previous mbm files lying around
	if (!RemoveLeftoverMBMFiles())
		return E_SCREENSHOT_LEFTOVERFILES;

	// get the destination screenshot name
	CString destfile = file;
	if (destfile.IsEmpty())
	{
		// construct a filename from date/time
		char szName[20] = {0};
		time_t curTime;
		time (&curTime);
		strftime (szName, 
					sizeof (szName),
					"%Y%m%d%H%M%S",
					localtime (&curTime));

		destfile = szName;
	}

	// get the full name of the source image
	CString SrcPath = cScreenshotDirectory;

	if (bMultithreaded)
	{
		// add a sub-folder for the connection type
		SrcPath += GetConnection(eConnectType);
		SrcPath += _T(".");
		SrcPath += szAddress;
		SrcPath += _T('\\');
	}

	// attach the filename
	CString path = SrcPath;

	path += destfile;
	path += _T(".mbm");

//	Message("Saving screenshot [%s]", ToAnsi(path));

	// save bitmap data to file
	if ((ret = SaveTheFile(path, pContents, ulLength)) == ITS_OK)
		ret = pConverter->ConvertScreenShot(SrcPath, SrcPath);

	// save the full path to the converted image
	if (ret == ITS_OK)
		file = SrcPath;
	return ret;
}


//----------------------------------------------------------------------------
// Save data to file
int CSTATEngine::SaveTheFile(CString path, const char *pContents, const unsigned long ulLength)
{
	int ret = E_BADFILENAME;

	EnterCriticalSection(&CriticalSection);

	// ensure the path exists
	CreateAllDirectories(path);

	DeleteFile(path);

	CFile script_file;
	if (script_file.Open(path, CFile::modeCreate | CFile::modeWrite | CFile::shareDenyWrite))
	{
		try
		{
			// might be a zero-length file
			if (pContents && ulLength)
				script_file.Write(pContents, ulLength);

			ret = ITS_OK;
		}
		catch(CFileException *e)
		{
			e->Delete();
			ret = GENERAL_FAILURE;
		}

		script_file.Abort();
	}

	LeaveCriticalSection(&CriticalSection);
	return ret;
}


//----------------------------------------------------------------------------
// Write a message to the log
void CSTATEngine::Message(const char *pMsg, ...)
{
	char szMessage[MAX_LOG_MSG_LEN + 1];

	if (pMsg)
	{
		memset(&szMessage, 0, sizeof(szMessage));
		va_list pCurrent = (va_list)0;
		va_start (pCurrent, pMsg);
		vsprintf (szMessage, pMsg, pCurrent);
		va_end (pCurrent);
	}
	else
		strcpy(szMessage, "No message associated with this error");

	EnterCriticalSection(&CriticalSection);
	pLog->Set(szMessage);
	LeaveCriticalSection(&CriticalSection);
}

//----------------------------------------------------------------------------
// Try to create all directories within a supplied path
void CSTATEngine::CreateAllDirectories(CString &fullpath)
{
	CString path;
	int index = fullpath.Find(_T('\\'));
	while (index != -1)
	{
		path = fullpath.Left(index);
		CreateDirectory(path, NULL);

		index = fullpath.Find(_T('\\'), index + 1);
	}
}

//----------------------------------------------------------------------------
// check if we want to quit
bool CSTATEngine::StopProcessing()
{
	// check if instructed to stop processing (will only happen on Desktop version)
	if (eStopProcessing == STAT_PAUSE)
	{
		eStopProcessing = STAT_STOP;
		return true;
	}

	return false;
}

//----------------------------------------------------------------------------
//Remove any leftover .mbm files prior to retrieving a screenshot
bool CSTATEngine::RemoveLeftoverMBMFiles()
{
	CFileFind imagefinder;
	CString imagedir = cScreenshotDirectory;
	bool valid = true;

	// add the search pattern
	if (imagedir.Right(1) != _T('\\'))
		imagedir += _T("\\");

	// now add a folder for the connection type
	if (bMultithreaded)
	{
		imagedir += GetConnection(eConnectType);
		imagedir += _T(".");
		imagedir += szAddress;
		imagedir += _T('\\');
	}

	imagedir += _T("*.mbm");

//	Message("Searching for [%s]", ToAnsi(imagedir));

	EnterCriticalSection(&CriticalSection);

	// locate files
	int iLeftFiles = imagefinder.FindFile(imagedir, 0);
	while (iLeftFiles)
	{
		iLeftFiles = imagefinder.FindNextFile();
//		Message("Deleting file [%s]", ToAnsi(imagefinder.GetFilePath()));

		if (!DeleteFile(imagefinder.GetFilePath()))
		{
			valid = false;
			break;
		}
	}

	imagefinder.Close();
	LeaveCriticalSection(&CriticalSection);
	return valid;
}


//----------------------------------------------------------------------------
// Append the device log to the end of the STAT log
void CSTATEngine::AppendCommandToSTATLog(char *heading, char *log, unsigned long length)
{
	if (length && log)
	{
		EnterCriticalSection(&CriticalSection);
		Message(DOTTED_LINE);
		Message(heading);

		char *start = log;
		char *eol = log;
		unsigned long count = 0;

		*(log + length) = (char)0;

		while (count < length - 1)
		{
			// find the end of the line
			while ((count < length - 1) && (*eol != '\r') && (*eol != '\n'))
			{
				count++;
				eol++;
			}

			// null it
			if (count < length - 1)
			{
				*eol = (char)0;
				count++;
				eol++;
			}

			if (start && *start)
				Message("%s", start);

			// step over newline
			while ((count < length - 1) && (*eol == '\r') || (*eol == '\n'))
			{
				count++;
				eol++;
			}

			if (count < length - 1)
				start = eol;
		}

		Message(DOTTED_LINE);
		LeaveCriticalSection(&CriticalSection);
	}
	else
		Message("No information returned from device");
}

//----------------------------------------------------------------------------
// Store some data in a public defined char* pointer
//  (currently iTEFSharedData and pDeviceInfo)
void CSTATEngine::StoreData( char* aData, unsigned long aLength, char*& aOutputPtr )
{

	receivedData.Empty();
	receivedData = aData;

	if( aOutputPtr )
	{
		delete [] aOutputPtr;
		aOutputPtr = NULL;
	}

	if( aData && aLength )
	{
		aOutputPtr = new char[aLength + 1];
		if( aOutputPtr )
		{
			// skip over leading CR/LF's
			char *p = aData;
			while (p && (*p) && (*p == '\r') || (*p == '\n'))
				p++;
			strcpy(aOutputPtr, p);
		}
	}
}

//----------------------------------------------------------------------------
// Translate a connection type to a string
CString CSTATEngine::GetConnection(STATCONNECTTYPE eConnectType)
{
	CString connection;

	switch(eConnectType)
	{
	case SymbianSocket:
		connection = _T("SymbianSocket");
		break;
	case SymbianSerial:
		connection = _T("SymbianSerial");
		break;
	case SymbianInfrared:
		connection = _T("SymbianInfrared");
		break;
	case SymbianBluetooth:
		connection = _T("SymbianBluetooth");
		break;
	case SymbianUsb:
		connection = _T("SymbianUsb");
		break;
	default:
		connection = _T("SymbianInvalid");
		break;
	};

	return connection;
}


//----------------------------------------------------------------------------
//  Sets a data socket to read/write file streams
//
int CSTATEngine::SetSocket(const char *ip, const int port)
{

	//initialise winsock
	WSAStartup( MAKEWORD(2,2), &wsaData );
	
	dataSocket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );

    if ( dataSocket == INVALID_SOCKET ) 
	{
        dataSocket=NULL;
		return E_SOCKETCREATE; 
    }

	// Connect to a server.
    sockaddr_in clientService;

    clientService.sin_family = AF_INET;
    clientService.sin_addr.s_addr = inet_addr( ip );
    clientService.sin_port = htons( port );

    if ( connect( dataSocket, (SOCKADDR*) &clientService, sizeof(clientService) ) == SOCKET_ERROR) 
	{
        ReleaseSocket();
		return E_SOCKETCONNECT;
    }
	else
	{
		return ITS_OK;
	}


	
}

int CSTATEngine::ReleaseSocket()
{
	int ret = ITS_OK;

	//release the socket
	if( closesocket(dataSocket) == SOCKET_ERROR)
	{
		ret = E_SOCKETCLOSE;
	}


	//cleanup winsock
	WSACleanup();

	dataSocket=NULL;

	return ret;
	
}


//----------------------------------------------------------------------------
// Read from the board and write to the data socket
//

void CSTATEngine::WriteToSocket(const char *data, int *length)
{
	// Send data.
   	*length = send( dataSocket, data, *length , 0 );
}



//----------------------------------------------------------------------------
// Read from the data socket and send to the board
//
void CSTATEngine::ReadFromSocket(char *data, int *length)
{
	int bufferSize = 0xFF;

	if(*length >= bufferSize)
	{
	
		int packetLength = *length;
		
		int totalRead = 0;
		int iterRead = 0;

		// Receive data to fill a packet
		while(totalRead <= packetLength - bufferSize)
		{
			iterRead = recv( dataSocket, data + totalRead, bufferSize, 0 );
			if(iterRead == 0)
				break;
			totalRead += iterRead;
		}

		*length = totalRead;
	}
	else
	{
		*length = recv( dataSocket, data, *length, 0 );
	}
}