testexecfw/statsrv/device/source/statapi/src/stat_engine.cpp
changeset 0 3e07fef1e154
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/testexecfw/statsrv/device/source/statapi/src/stat_engine.cpp	Mon Mar 08 15:03:44 2010 +0800
@@ -0,0 +1,428 @@
+/*
+* 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: 
+*
+*/
+
+
+
+ /********************************************************************************
+ *
+ * System Includes
+ *
+ ********************************************************************************/
+#include <e32std.h>
+#include <e32base.h>
+
+/********************************************************************************
+ *
+ * Local Includes
+ *
+ ********************************************************************************/
+#include "assert.h"
+#include "stat.h"
+#include "stat_engine.h"
+
+/********************************************************************************
+ *
+ * Macro functions
+ *
+ ********************************************************************************/
+
+/********************************************************************************
+ *
+ * Definitions
+ *
+ ********************************************************************************/
+const TUint	KSyncMax		= 5;
+
+/********************************************************************************
+ *
+ * CStatEngine -- construction
+ *
+ ********************************************************************************/
+CStatEngine *CStatEngine::NewL( MNotifyStatController *aController, RFs *const aSession, MNotifyLogMessage *const aMsg )
+{
+	CStatEngine *self = new (ELeave) CStatEngine();
+	CleanupStack::PushL( self );
+	self->ConstructL( aController, aSession, aMsg );
+	CleanupStack::Pop();
+	return self;
+}
+
+void CStatEngine::ConstructL( MNotifyStatController *aController, RFs *const aSession, MNotifyLogMessage *const aMsg )
+{	
+	// check params
+	asserte( aController != NULL );
+
+	// setup all params
+	iController = aController;
+	iEngineStatus = EIdle;
+	iRemoteHost = NULL;
+	iResyncCount = 0;
+	iFs =	aSession;
+	iMsg = aMsg;
+	iCommandDecoder = CStatApiCommandDecoder::NewL( iFs, iMsg );
+}
+
+CStatEngine::CStatEngine() : iDataSupplier( NULL ), iMsg( NULL ), iFs( NULL ), iDeleteLastFile(EFalse)
+{
+}
+
+CStatEngine::~CStatEngine()
+{
+	// clean up all params
+	if( iRemoteHost != NULL ) {
+		delete iRemoteHost;
+	}
+	delete iCommandDecoder;
+
+	if(iDataSupplier)
+		{
+		iDataSupplier->Delete( );
+		iDataSupplier = NULL;
+		}
+}
+
+/********************************************************************************
+ *
+ * CStatEngine -- MStatEngine implementation
+ *
+ ********************************************************************************/
+void CStatEngine::StartEngine( MStatApiTransport *aStatTransport, 
+	TStatConnectType aConnectType, TDesC *aRemoteHost )
+{
+	TInt exception, ret = KErrNone;
+	iTransport = aStatTransport;
+
+	// save the remote host address string
+	iRemoteHost = new TPtrC( *aRemoteHost );
+
+	// create the message log 
+
+	// start the initialisation procedure the ugly second line means that if an
+	// exception is thrown the ret will have the exception, otherwise it will have
+	// the return code. Exceptions are always -ve and return values are always +ve
+	// so the two number spaces don't overlap (as zero means success in both).
+	iMsg->Msg( _L("ENGINE: Calling InitialiseL, %d (%S)."), aConnectType, aRemoteHost );
+	SetState( EInitialising );
+	TRAP( exception, (ret = iTransport->InitialiseL()) );
+	ret = ((exception == KErrNone) ? ret : exception);
+	HandleStateChange( KOpInitialise, ret );
+}
+
+// the controller calls stop whenever he (yes the controller is male) wants the engine
+// to arrange itself and the transport into a state where he can safetly shut it down. It
+// can be called at any time -- i.e. when the engine is in any state.
+void CStatEngine::StopEngine( void )
+{
+	TInt exception, ret = KErrNone;
+
+	switch( iEngineStatus ) {
+		
+	case EIdle:
+		;
+		break;
+
+	case EConnecting:
+	case EConnected:
+	case EReceivePending:
+	case ESendPending:
+		iMsg->Msg( _L("ENGINE: Calling Disconnect.") );
+		SetState( EDisconnecting );
+		TRAP( exception, (ret = iTransport->Disconnect()) );
+		ret = ((exception == KErrNone) ? ret : exception);
+		HandleStateChange( KOpDisconnect, ret );
+		break;
+
+	case EDisconnecting:
+	case EDisconnected:
+	case EReleasing:
+		// nothing I can really do -- there is already a disconnect call
+		// pending -- just return and wait for notification -- or force
+		// disconnect by just deleting the object
+		break;
+
+	case EInitialising:
+	case EInitialised:
+		;
+		break;
+	case ELast:
+		;
+		break;
+	}
+}
+
+/********************************************************************************
+ *
+ * CStatEngine - Higher level transport handling code. All errors and startup
+ * shutdown issues are handled by the MNotifyStatEngine implementation.
+ *
+ ********************************************************************************/
+void CStatEngine::OnConnect( void )
+{	
+	iMsg->Msg( _L("ENGINE: Waiting for next command.") );
+	SetState( EReceivePending );
+	TInt err = iTransport->RequestReceive();
+	asserte( err == KSTErrAsynchronous );
+}
+
+void CStatEngine::OnRecvCommand( TUint aCommand, MDataConsumer *const aDataConsumer )
+{
+	TInt err = KErrNone;
+	TInt dataLength = 0;
+
+	if (aCommand == 'B')
+		iDeleteLastFile = EFalse;
+	else if (aCommand == 'S' || aCommand == 'X')
+		iDeleteLastFile = ETrue;
+	
+	// Clean up the old data supplier.
+	// If we are getting a command then it is safe to assume
+	// that everyone has finished with the old data supplier as
+	// they are valid only for the context of the current
+	// command.
+	if(iDataSupplier)
+		{
+		iDataSupplier->Delete( );
+		iDataSupplier = NULL;
+		}
+	
+	// if this is a resync command then we have already done all we need to do so
+	// just return and wait for the next command. If we reach the maximum of 
+	// resync commands disconnect and reset.
+	if( aCommand == RESYNC_ID ) {
+		iResyncCount++;
+		if( iResyncCount > KSyncMax ) {
+			iMsg->Msg( _L("ENGINE: Resync limit reached, calling Disconnect().") );
+			SetState( EDisconnecting );
+			err = iTransport->Disconnect();
+			iMsg->Msg( _L("ENGINE: Synchronous response from Disconnect(%d)."), err );
+			HandleStateChange( KOpDisconnect, err );
+		}
+		SetState( ESendPending );
+		return;
+	}
+	iResyncCount = 0;
+
+	if(aCommand == REFRESH_ID)
+	{
+		SetState( EDisconnecting );
+		err = iTransport->Disconnect();
+		HandleStateChange( KOpDisconnect, err );
+		SetState( ESendPending );
+
+		return;
+	}
+
+	// otherwise execute the command
+	err = iCommandDecoder->ExecuteCommand( aCommand, aDataConsumer, &iDataSupplier );
+
+	if( err == KErrNone )
+		{
+		if(iDataSupplier)
+			{
+			err = iDataSupplier->GetTotalSize( dataLength );
+			}
+		}
+
+	// if there was an error then the result code is FAILED_ID
+	if( err != KErrNone )
+		{
+		aCommand = FAILED_ID;
+		}
+
+	// now send the response and return
+	iMsg->Msg( _L("ENGINE: Sending reply (%c, %d)."), (char)aCommand, dataLength );
+	SetState( ESendPending );
+	err = iTransport->RequestSend( aCommand, iDataSupplier );
+	asserte( err == KSTErrAsynchronous );
+
+	return;
+}
+
+/********************************************************************************
+ *
+ * CStatEngine -- MNotifyStatEngine implementation -- this is the lower level 
+ * which maintains the startup / shutdown protocol with the transport to keep 
+ * them happy and deal with the asynchronous behaviour. 
+ *
+ ********************************************************************************/
+void CStatEngine::HandleStateChange( TStateOp aOperation, TInt aResult ) 
+{
+	TPtrC opname[] = {	_L("ENGINE: <null> - response (%d)."), 
+						_L("ENGINE: InitialiseL - response (%d)."),
+						_L("ENGINE: ConnectL - response (%d)."),
+						_L("ENGINE: Disconnect - response (%d)."),
+						_L("ENGINE: Release - response (%d).") };
+
+	iMsg->Msg( opname[aOperation], aResult );
+	while( (aResult != KSTErrAsynchronous) ) {
+		if( aResult != KSTErrSuccess )
+			iController->HandleError( aResult, NULL );
+		HandleSingleStateChange( &aOperation, &aResult );
+		iMsg->Msg( opname[aOperation], aResult );
+	}
+}
+
+void CStatEngine::HandleSingleStateChange( TStateOp *aOperation, TInt *aResult )
+{
+	TStateOp thisOperation = *aOperation;
+	TInt exception, ret = KErrNone, thisResult = *aResult;
+
+	// should never get in here with a result of async
+	asserte( *aResult != KSTErrAsynchronous );
+
+	// set the operation to none by default and asynchronous which will break out of the above loop
+	*aOperation = KOpNone;
+	*aResult = KSTErrAsynchronous;
+
+	// handle each operation
+	switch( thisOperation ) {
+
+	// if initialise failed then call release, otherwise call connect
+	case KOpInitialise:			
+		if( thisResult == KSTErrSuccess ) {
+			SetState( EInitialised );
+			*aOperation = KOpConnect;
+			SetState( EConnecting );
+			TRAP( exception, (ret = iTransport->ConnectL(iRemoteHost)) );
+			*aResult = ((exception == KErrNone) ? ret : exception);
+		} else {
+			*aOperation = KOpRelease;
+			SetState( EReleasing );
+			*aResult = iTransport->Release();
+		}
+		break;
+
+	// if connect failed call disconnect, otherwise call the internal OnConnect function
+	case KOpConnect:
+		if( thisResult != KSTErrSuccess ) {
+			SetState( EDisconnecting );
+			*aOperation = KOpDisconnect;
+			*aResult = iTransport->Disconnect();
+		} else { 
+			SetState( EConnected );
+			OnConnect();
+		}
+		break;
+
+	// call release after disconnect regardless of result
+	case KOpDisconnect:
+		*aOperation = KOpRelease;
+		SetState( EReleasing );
+		*aResult = iTransport->Release();
+		break;
+
+	// exit after release
+	case KOpRelease:
+		SetState( EIdle );
+		break;
+
+	case KOpNone:
+		;
+		break;
+	}
+}
+
+void CStatEngine::HandleInitialise( TInt aResult )
+{
+	HandleStateChange( KOpInitialise, aResult );
+}
+
+void CStatEngine::HandleConnect( TInt aResult )
+{
+	HandleStateChange( KOpConnect, aResult );
+}
+
+void CStatEngine::HandleDisconnect( TInt aResult )
+{
+	HandleStateChange( KOpDisconnect, aResult );
+}
+
+void CStatEngine::HandleRelease( TInt aResult )
+{
+	HandleStateChange( KOpRelease, aResult );
+}
+
+void CStatEngine::HandleSend( TInt aResult )
+{
+	TInt err;
+
+	// we must either be sending an ack or a response 
+	asserte( iEngineStatus == ESendPending );
+	
+	if (iDeleteLastFile)
+		{
+		iCommandDecoder->DeleteLastFile();
+		iDeleteLastFile = EFalse;
+		}
+	// now that the reply has been sent wait for the next command
+	iMsg->Msg( _L("ENGINE: Reply sent (%d)."), aResult );
+	iMsg->Msg( _L("ENGINE: Waiting for next command.") );
+	asserte( iEngineStatus == ESendPending );
+	SetState( EReceivePending );
+	err = iTransport->RequestReceive();
+	asserte( err == KSTErrAsynchronous );
+}
+
+void CStatEngine::HandleReceive( TInt aResult, const TUint aCommand,
+		MDataConsumer *const aDataConsumer )
+{
+	// log the receipt
+	TInt dataLength = 0;
+	
+	if(aDataConsumer)
+		{
+		aDataConsumer->GetTotalSize( dataLength );
+		}
+	iMsg->Msg( _L("ENGINE: Command Received (%d, %c, %d)."), aResult, (char)(aCommand), dataLength );
+
+	// verify that the engine state is as expected
+	asserte( iEngineStatus == EReceivePending );
+
+	// process the command
+	iMsg->Msg( _L("ENGINE: Processing command (%c)."), (char)(aCommand) );
+	OnRecvCommand( aCommand, aDataConsumer );
+}
+
+void CStatEngine::HandleError( TInt aError, void* aErrorData )
+{
+	// log the error
+	iMsg->Msg( _L("ENGINE: Error encountered (%d)."), aError );
+
+	// call the upper layers so that the UI can notify the user that an error has
+	// occured.
+	iController->HandleError( aError, aErrorData );
+	
+	// on an error the engine takes the responsibility of closing itself down -- and 
+	// then informing the controller than it can clean up the objects.
+	SetState( EDisconnecting );
+	TInt err = iTransport->Disconnect();
+	HandleStateChange( KOpDisconnect, err );
+}
+
+void CStatEngine::HandleInfo( const TDesC *aInfo )
+{
+	iController->HandleInfo( aInfo );
+}
+
+void CStatEngine::SetState( TCommStatus aNewStatus )
+{
+	// change the internal status and notify the controller of the change
+	iEngineStatus = aNewStatus;
+	asserte( iController != NULL );
+	iController->HandleStatusChange( iEngineStatus );
+}
+