testconns/statdesktop/desktop/source/lib/src/statengine.cpp
changeset 4 b8d1455fddc0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/testconns/statdesktop/desktop/source/lib/src/statengine.cpp	Mon Oct 04 02:58:21 2010 +0300
@@ -0,0 +1,1580 @@
+/*
+* 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 );
+	}
+}
+
+
+
+
+
+
+
+ 
+