datasourceadaptation/gpsdatasourceadaptation/common/src/cadaptationpositioner.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 02 Feb 2010 01:50:39 +0200
changeset 0 9cfd9a3ee49c
permissions -rw-r--r--
Revision: 201002 Kit: 201005

// Copyright (c) 2008-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:
//

/**
 @file
 @InternalComponent
*/

#include <e32base.h>
#include <lbs/epos_mpositionerstatus.h>
#include <ecom/implementationproxy.h>
#include <lbssatellite.h>
#include "utilfunctions.h"

#include "crequesthandler.h"
#include "cresponsehandler.h"
#include "cadaptationpositioner.h"
#include "psypanic.h"
#include "psylogging.h"
#include "lbsprocessuiddefs.h"
#include "lbsdevloggermacros.h"

const TUint KWarmDownTimeout   = 10000000;//10s

CAdaptationPositioner::CAdaptationPositioner()
    {
    LBSLOG(ELogP1, "CAdaptationPositioner::CAdaptationPositioner()");
    iState = ERequestStateInitial;
    }

/**
* Destructor
*/
CAdaptationPositioner::~CAdaptationPositioner()
    {
    LBSLOG(ELogP1, "CAdaptationPositioner::~CAdaptationPositioner()");
    
	StopMaxFixTimer();
	delete iMaxFixTimer;
	iMaxFixTimer = NULL;
	StopWarmDownTimer();    
	delete iWarmDownTimer;
	iWarmDownTimer = NULL;
    
	CancelNotifyPositionUpdate();
    
    if(iEnvironment)
    	{
    	iEnvironment->CloseInstance();
    	}
	}

/**
* CancelNotifyPositionUpdate implements CAdaptationPositioner::CancelNotifyPositionUpdate
* Cancels position info request.
*/
void CAdaptationPositioner::CancelNotifyPositionUpdate()
    {
    LBSLOG(ELogP1, "CAdaptationPositioner::CancelNotifyPositionUpdate()");
    // Stop timers
	StopMaxFixTimer();
	StopWarmDownTimer();    
    CancelNotifyPositionUpdate(KErrCancel);
    }

/**
* CancelNotifyPositionUpdate implements CAdaptationPositioner::CancelNotifyPositionUpdate(TInt)
* Cancels position info request with reason. This reason allows the S60 Loc Server to distinguish between a real user cancel
* and a timeout - in the case of the latter the LS calls CancelNotifyPositionUpdate(KErrTimedOut), 
* in the former he passes no error.
*/
void CAdaptationPositioner::CancelNotifyPositionUpdate(TInt aError)
    {
    LBSLOG2(ELogP1, "CAdaptationPositioner::CancelNotifyPositionUpdate(aError) %d", aError);

    if( iActive )
        {
        LBSLOG2(ELogP1, "CAdaptationPositioner::Complete request error = %d", aError);

		UpdateFailed(KErrCancel);
        TRAP_IGNORE(iRequestHandler->ReMergeWithCancelRequestL(aError)); // Nothing we can do about a leave here
        if(aError == KErrTimedOut && IsTracking())
        	{
        	// The last tracking request timedout so we need to schedule one for the next interval
    		TTime timeNow;
    		timeNow.UniversalTime();   
    		iActive = ETrue;
        	UpdateTrackingTime(timeNow);
        	TRAPD(error, iRequestHandler->SubmitNewRequestL(this)); // Nothing we can do about a leave here
            if(!error)
                {
                TRAP_IGNORE(StartMaxFixTimerL());// (re)start maxfix timer so that we know when the module has stopped trying
                } 	
        	}
    	}
    }


/**
* Indicate if the PSY has overridden tracking. The default
* implementation returns EFalse.
* @return ETrue if PSY has own timer, otherwise EFalse.
*/
TBool CAdaptationPositioner::TrackingOverridden() const
    {
    return ETrue;
    }


/**
* Initiate a tracking session.
*
* @param aInterval Interval for position requests.
*/
void CAdaptationPositioner::StartTrackingL(const TTimeIntervalMicroSeconds& aInterval)
    {
	LBSLOG2(ELogP1, "CAdaptationPositioner::StartTrackingL, interval = %d", aInterval);

	TTime timeNow;
    
    TTime trackingTime = LbsTimerUtils::AddUpInt64TimersWithOverflowCheck(iTargetTime, aInterval);
	timeNow.UniversalTime();
    
	iTrackingInterval = aInterval;
	    
    if(! (trackingTime < timeNow)) // if this is not a default proxy call to StartTracking
	    {
    	LBSLOG(ELogP1, "Non-DefProxy Start");
	    SetupTrackingRequestL();
		iRequestHandler->SubmitNewRequestL(this);
	    }
    }
    
/**
* Stop a periodic update session.
*/
void CAdaptationPositioner::StopTracking()
    {
    LBSLOG(ELogP1, "CAdaptationPositioner::StopTracking");
    
    iTrackingInterval = 0;
    }

/**
* Restart tracking after a missed update
*/
void CAdaptationPositioner::UpdateTrackingTime(TTime& aTimeNow)
	{
	LBSLOG(ELogP1, "CAdaptationPositioner::UpdateTrackingTime restarting tracking");
	
	TUint multiplier = ( (aTimeNow.Int64() - iTargetTime.Int64()) / iTrackingInterval.Int64() );
	TTimeIntervalMicroSeconds timeToAdd = ( (multiplier + 1) * iTrackingInterval.Int64() );
	
	if (timeToAdd < 0)
		{
		iTargetTime = Time::MaxTTime();
		}
	else
		{
		iTargetTime = LbsTimerUtils::AddUpInt64TimersWithOverflowCheck(iTargetTime, timeToAdd);
		}
	}

/**
* Submit a request with the target time in the future.
* The positionerQ will deal with issuing a merged request from 
* all active positioners.
*/
void CAdaptationPositioner::SetupTrackingRequestL()
	{
    if(IsTracking())
    	{
    	LBSLOG(ELogP1, "CAdaptationPositioner::SetupTrackingRequestL");
    	
	    iPosInfo = NULL; // This will be filled in by the NPUD when it arrives
	    iActive = ETrue;
	    
	    iTargetTime.UniversalTime();

		#ifdef _DEBUG
	    TDateTime timeNow = iTargetTime.DateTime();
		LBSLOG3(ELogP1, "Time Now = %d.%d", timeNow.Second(), timeNow.MicroSecond());
		#endif
		
		iTargetTime = LbsTimerUtils::AddUpInt64TimersWithOverflowCheck(iTargetTime, iTrackingInterval);

		#ifdef _DEBUG
		timeNow = iTargetTime.DateTime();
		LBSLOG3(ELogP1, "Target Time = %d.%d", timeNow.Second(), timeNow.MicroSecond());
		#endif
		
		StartMaxFixTimerL();
    	}
	}
	
/**
* Check if we're tracking.
*
* @return ETrue if we are tracking
*/
TBool CAdaptationPositioner::IsTracking()
	{
	return (iTrackingInterval != 0);
	}
	
/**
* Attempt to use an old position
*
* @param aPosInfo The clients position info
* @return ETrue if the old position was used
*/
TBool CAdaptationPositioner::UseLastLocation(TPositionInfoBase& aPosInfo)
	{
    TTime maxAge;
	TBool result = EFalse;

    GetMaxAge(maxAge);

    if(maxAge > 0)
    	{
    	CResponseHandler* responseHandler = iEnvironment->GetResponseHandler(); 
    	
    	result = responseHandler->GetLastPosition(aPosInfo, maxAge, IsPartialUpdateAllowed());
    	
	    if( result ) // Use last location
	        {
	        TInt err = KErrNone;
	  	 	TPosition pos;
	  	 	static_cast<TPositionInfo&>(aPosInfo).GetPosition(pos);
	        if(Partial(pos))
	        	{
	        	err = KPositionPartialUpdate;
	        	}
	        LBSLOG(ELogP1, "CAdaptationPositioner::NotifyPositionUpdate returning old position");
	        // nb: this will deal with tracking requests properly:
	        if(CompleteRequest(static_cast<TPositionInfo&>(aPosInfo), err))
	        	{
	            TRAP_IGNORE(SetupTrackingRequestL());
	        	}
	        }
        }

	return result;
	}

/**
* Attempt to complete an outstanding request if there is one
*
* @param aPositionInfo The retrieved fix.
* @param aError The error code if the request is failed.
* @param aActualTime time of the location update
*/
void CAdaptationPositioner::RequestCompleteNotify(const TPositionInfo& aPositionInfo, TInt aError, TTime& aActualTime)
    {
    LBSLOG(ELogP1, "CAdaptationPositioner::RequestCompleteNotify start...");
    
    TBool startWarmDown = EFalse;
    TBool completed = EFalse;
    
    if(iActive)
	    {
	    TPositionModuleInfo::TTechnologyType mode = aPositionInfo.PositionMode();
	    
	    if( (aActualTime >= iTargetTime) || (aError == KPositionCalculationFutile) )
	       	{
        	// Check if the update is partial and if partial updates are allowed
    	 	TPosition pos;
			aPositionInfo.GetPosition(pos);

        	TBool partial = Partial(pos);
        	
			TBool isFNP = (aPositionInfo.PositionMode() == (TPositionModuleInfo::ETechnologyNetwork | TPositionModuleInfo::ETechnologyAssisted));
			
			if(isFNP)
				{
        		LBSLOG(ELogP1, "Complete with FNP");
        		/* removing this for now until we decide how to deal with the qualityloss issue on s60 
        		 * since on s60 qualityloss means no position was generated
        		 	if(partial)
        			{
        			CompleteRequest(aPositionInfo, KPositionQualityLoss);
        			}
        		
        		else
        		*/
        			{
        			completed = CompleteRequest(aPositionInfo, KErrNone);
        			
        			// Stop the AGPS Manager if there are no other outstanding requests
        	        TRAP_IGNORE(iRequestHandler->CancelRequestL());
        			}
				}
			else if(partial && IsPartialUpdateAllowed())
	        	{
        		LBSLOG(ELogP1, "Complete with partial");
        		completed = CompleteRequest(aPositionInfo, KPositionPartialUpdate);
        		startWarmDown = ETrue;
	        	}
	        else if(!partial)
		        {
    			LBSLOG(ELogP1, "Complete with non-partial");
    			completed = CompleteRequest(aPositionInfo, KErrNone);
    			
        		// did the module produce an accurate update or is it still trying?
        		if(!IsAccurate(aPositionInfo))
        			{
        			startWarmDown = ETrue;         			
        			}
        		else	// it might be an accurate reference position
        			{
        			TBool isRefPos = (aPositionInfo.ModuleId() == KLbsGpsLocManagerUid) && 
        								(aPositionInfo.PositionMode() == TPositionModuleInfo::ETechnologyNetwork);
        			if(isRefPos)
        				{
        				startWarmDown = ETrue;
        				}
        			}
	        	}
	        else if(aError == KPositionCalculationFutile)
	        	{
	        	// if autonomous return immediately otherwise wait for FNP
	        	if(mode & TPositionModuleInfo::ETechnologyTerminal)
		        	{
	        		LBSLOG(ELogP1, "Complete with futile");
	        		completed = CompleteRequest(aPositionInfo, KPositionQualityLoss);
		        	}
	        	}
	        else
	        	{
		        // position is not futile and is partial but partials are not
		        // allowed. In this case do nothing and wait for further updates
	        	}
        	}
		else
			{
        	LBSLOG(ELogP1, "CAdaptationPositioner::RequestCompleteNotify Ignoring early response");
			}
    	}
    
    if (completed)
    	{
        TRAP_IGNORE(SetupTrackingRequestL());
    	}
    
    if(startWarmDown)	// we need to start a warmdown timer
    	{
    	TRAP_IGNORE(StartWarmDownTimerL()); // not much we can do here if warmdown timer not started
    	}
    LBSLOG(ELogP1, "CAdaptationPositioner::RequestCompleteNotify end");
    }
    
/**
* Attempt to complete an outstanding request if there is one with an error
*
* @param aError The error code if the request is failed.
*/
void CAdaptationPositioner::UpdateFailed(TInt aError)
	{
	LBSLOG(ELogP1,  "CAdaptationPositioner::UpdateFailed()");
	if(iActive)
	    {
		LBSLOG2(ELogP1, "CAdaptationPositioner::Complete request error = %d", aError);

		iActive = EFalse;
		ClearPositionInfo(*iPosInfo);
		
		if(iClientStatus)
			{
			User::RequestComplete(iClientStatus, aError);
			iClientStatus = NULL;
			}
		LBSLOG2(ELogP1, "iState -> ERequestStatePostInitial from %d", iState);
	    iState = ERequestStatePostInitial;
	    }
	}

/**
* Complete the outstanding request and reset internal state
*
* @param aPositionInfo The retrieved fix.
* @param aError The error code if the request is failed.
*/
TBool CAdaptationPositioner::CompleteRequest(const TPositionInfo& aPositionInfo, TInt aError)
	{
	LBSLOG2(ELogP1, "CAdaptationPositioner::CompleteRequest() with error %d", aError);
	TBool completed = EFalse;
	iActive = EFalse; // Stop another request being sent for this instance of the PSY

	if(iPosInfo)
		{
		TInt error = CopyPositionTypes(*iPosInfo, aPositionInfo);
		__ASSERT_DEBUG(error == KErrNone,
						User::Panic(KAdaptationPanicCategory, EPanicPositionCopyFailed));
		error = error; // this is to stop arm build warnings

		SetModuleId(iPosInfo); // Delegate to the derived class
		User::RequestComplete(iClientStatus, aError);
		iClientStatus = NULL;
		LBSLOG2(ELogP1, "iState -> ERequestStatePostInitial from %d", iState);
	    iState = ERequestStatePostInitial;
        completed = ETrue;
		}
	else
		{
    	LBSLOG(ELogP1, "CAdaptationPositioner::RequestCompleteNotify iPosInfo not set");
		}
	
	return completed;
	}

/**
* CAdaptationPositioner::ReportStatus
* Reports the PSY status to the MLFW
*
* @param aStatus The new position module status
*/
void CAdaptationPositioner::ReportStatus(const TPositionModuleStatus& aStatus)
    {
    MPositionerStatus* statusInf = this->PositionerStatus();
    statusInf->ReportStatus(aStatus);
    }

/**
 * Clears the fields of the aPosInfo 
 * @param aPosInfo The position information to return
 */
void CAdaptationPositioner::ClearPositionInfo(TPositionInfoBase& aPosInfo)
    {
    if(&aPosInfo != NULL)
	    {
		if (aPosInfo.PositionClassType() & EPositionSatelliteInfoClass)
	        {
	        // TPositionSatelliteInfo
	        (void) new (&aPosInfo) (TPositionSatelliteInfo);
	        }
	    else if (aPosInfo.PositionClassType() & EPositionCourseInfoClass)
	        {
	        // TPositionCourseInfo
	        (void) new (&aPosInfo) (TPositionCourseInfo);
	        }
	    else if (aPosInfo.PositionClassType() & EPositionGenericInfoClass)
	        {
	        // HPositionGenericInfo
	        HPositionGenericInfo* genInfo =
	            static_cast<HPositionGenericInfo*> ( &aPosInfo );
	        
	        genInfo->ClearPositionData();
	        }
	    else if (aPosInfo.PositionClassType() & EPositionInfoClass)
	        {
	        // TPositionInfo
	        (void) new (&aPosInfo) (TPositionInfo);
	        }
	    else
	        {
	        // Unknown type, this should never happen
	        // --> Panic if we get here
			__ASSERT_DEBUG(0, User::Panic(KAdaptationPanicCategory, EPanicUnknownPositioningClass));
	        }

	    aPosInfo.SetModuleId(ImplementationUid());
	    }
    }


TBool CAdaptationPositioner::IsActive()
	{
	return iActive;
	}

TBool CAdaptationPositioner::IsWarmingDown()
	{
	if(iWarmDownTimer)
		{
		return iWarmDownTimer->IsActive();
		}
	return EFalse;
	}

void CAdaptationPositioner::StartMaxFixTimerL()
	{
	LBSLOG(ELogP1, "CAdaptationPositioner::StartMaxFixTimerL()");
	if(iMaxFixTimer)	// stop any that's currently running
		{
		iMaxFixTimer->Cancel();
		}
	else
		{
		iMaxFixTimer = CLbsCallbackTimer::NewL(*this);
		}
	
	iMaxFixTimer->EventAfter(iTimeOutInterval, EMaxFixTimerEvent);
	}

void CAdaptationPositioner::StopMaxFixTimer()
	{
	LBSLOG(ELogP1, "CAdaptationPositioner::StopMaxFixTimer()");
	if(iMaxFixTimer)
		{
		iMaxFixTimer->Cancel();
		}
	}

void CAdaptationPositioner::StartWarmDownTimerL()
	{
	TTimeIntervalMicroSeconds32 timeout = KWarmDownTimeout;
	LBSLOG(ELogP1, "CAdaptationPositioner::StartWarmDownTimerL()");
	if(iWarmDownTimer)	// stop any that's currently running
		{
		iWarmDownTimer->Cancel();
		}	
	else
		{
		iWarmDownTimer = CLbsCallbackTimer::NewL(*this);
		}
	iWarmDownTimer->EventAfter(timeout, EWarmDownTimerEvent);
	}

void CAdaptationPositioner::StopWarmDownTimer()
	{
	LBSLOG(ELogP1, "CAdaptationPositioner::StopWarmDownTimer()");
	if(iWarmDownTimer)
		{
		iWarmDownTimer->Cancel();
		}
	}

/**
MaxFixTimerEvent handling

Stop warmdown timer
*/	
void CAdaptationPositioner::MaxFixTimerEvent()
	{
    LBSLOG(ELogP1, "CAdaptationPositioner::MaxFixTimerEvent()");

    // Cancel the warmdown timer since the module has stopped trying
    StopWarmDownTimer();	
	}
	
/**
MaxFixTimer error handling
*/	
TInt CAdaptationPositioner::MaxFixTimerError(TInt /*aError*/)
	{
	return KErrNone;
	}

/**
MaxFixTimerEvent handling

Stop warmdown timer
*/	
void CAdaptationPositioner::WarmDownTimerEvent()
	{
    LBSLOG(ELogP1, "CAdaptationPositioner::MaxFixTimerEvent()");

    // tell positioner Q that our warmdown timer has fired
    iRequestHandler->WarmDownTimerExpired();
	}
	
/**
WarmDown error handling
*/	
TInt CAdaptationPositioner::WarmDownTimerError(TInt /*aError*/)
	{
	return KErrNone;
	}


/**
from MLbsCallbackTimerObserver

@param aTimerId Time event ID to identify different time events
*/
void CAdaptationPositioner::OnTimerEventL(TInt aTimerId)	
	{
	switch(aTimerId)
		{
	case EMaxFixTimerEvent:
		MaxFixTimerEvent();
		break;
	case EWarmDownTimerEvent:
		WarmDownTimerEvent();
		break;
	default:
		__ASSERT_DEBUG(0, User::Panic(KAdaptationPanicCategory, EPanicUnknownTimerEventId));
		}
	}
/**
Timer error handling

@param aError Time error code
@param aTimerId Time event ID to identify two different time events
*/	
TInt CAdaptationPositioner::OnTimerError(TInt aError, TInt aTimerId)
	{
	switch(aTimerId)
		{
	case EMaxFixTimerEvent:
		MaxFixTimerError(aError);
		break;
	case EWarmDownTimerEvent:
		WarmDownTimerError(aError);
		break;
	default:
		__ASSERT_DEBUG(0, User::Panic(KAdaptationPanicCategory, EPanicUnknownTimerEventId));
		}
	return KErrNone; // we have handled the error locally	
	}


TBool CAdaptationPositioner::IsAccurate(const TPositionInfo& aPositionInfo)
	{
	TBool accurate = ETrue;
	TPositionQuality reqQuality; 
	iCriteria.GetRequiredQuality(reqQuality);
	TPosition pos;
	
	aPositionInfo.GetPosition(pos);
	if(pos.HorizontalAccuracy() > reqQuality.HorizontalAccuracy() )
		{
		accurate = EFalse;
		}
	return accurate;
	}

TUint CAdaptationPositioner::InactivityTimeout()
	{
	return iInactivityTimeout;
	}


//  End of File