/*******************************************************************************
*                                                                              *
* Société de Transports de Montréal.                                    	   *
* 2012                                                                         *
*                                                                              *
* Projet Zones Tests                                                           *
*                                                                              *
*                                                                              *
*                                                                              *
*******************************************************************************/
/*
	Description:
	Machine à état principale de la ZT.  Elle lit les entrées des CDV et des CI
	et démarre le thread d'analyse lorsqu'un train procède sur la ZT.  La classe
	gère aussi la compilation des déclenchements et des logs de passage.

*/

/* ************************************************************************** */
/* Revision:
### YYYMMDD JFM
	Verision d'origine.

### YYYYMMDD Description du besoin ou du bug
	Description du changement.
 */

/* ************************************************************************** */

#include "ZTStateMachine.h"
#include "ZTSimulator.h"
#include <QTextStream>
#include <QTextCodec>
#include <TrainLogFileMgr.h>
#include "ZTLog.h"
#include "math.h"
#include "MaintenancePage.h"
#include "RamMonitor.h"
#include "Zonetest.h"
//#include "ModbusCCDefs.h"

CZTStateMachine::CZTStateMachine(CZoneTest *ZTPtr, QObject *parent) :
	QObject(parent)
{
	QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));

    mProgramPtr = ZTPtr;

	mZT1State = ZT1_WAIT_FOR_CI_STATE;
	mZT2State = ZT2_WAIT_FOR_CI_STATE;
	mZTState = ZT_INIT_STATE;
    mZT2Present = true;

//	mZTDataPtr = new CZTData;
	mZTStationPtr = 0;
    mZTDetectionConfig = 0;

	mLastInputs = 0;
	mZtSimPtr = 0;
    mLogMgr = 0;
	mCalibrationPassagesCount = 0;
    mModbusCCMgr = 0;
    //mNetworkDriveMgr = 0;
	//mExtIOInterface = 0;

	mNbPassages = 0;
	mNbTriggers = 0;
//	mZT1Active = false;	//JFM ztstatus
//	mZT2Active = false;
	mZT1ActiveStatus = SB_ZT_INACTIVE_STATUS;
	mZT2ActiveStatus = SB_ZT_INACTIVE_STATUS;

	mCIZT1Active = false;

	mLogDelay = ZT_SM_DEFAULT_LOG_DELAY;

	mAutoExportZT1CSV = false;
	mAutoExportZT2CSV = false;

    //mTKGenerator = new CTKGenerator();

	mIsPGCalibON = false;
	mIsMaintenanceModeON = false;

	//Create the ZT1 train analysis thread
	mZT1SMThread = new QThread;
    mZT1WorkerThread = new CZT1AnalysisThread();
    mZT1WorkerThread->moveToThread(mZT1SMThread);
    connect(mZT1SMThread,SIGNAL(started()),mZT1WorkerThread,SLOT(AnalyzeZT1Train()));
    connect(mZT1WorkerThread,SIGNAL(ZT1DataUpdate(CZT1ThreadData*,bool,int)),this,SLOT(ZT1StateChanged(CZT1ThreadData*,bool,int)));
    connect(mZT1WorkerThread,SIGNAL(DetectionTriggered(CZTDetectionData*)),this,SLOT(ZT1NewDetection(CZTDetectionData*)));
    connect(mZT1WorkerThread,SIGNAL(NewPGCalibrationData(CZTPGCalibrationData*)),this,SLOT(NewPGCalibrationData(CZTPGCalibrationData*)));
	connect(mZT1WorkerThread,SIGNAL(NewZT1Flag(int,CZT1FlagsData*)),this,SLOT(ZT1NewFlag(int,CZT1FlagsData*)));
	connect(mZT1WorkerThread,SIGNAL(AddLogString(QString)),this,SLOT(AddLogStringFromThread(QString)));

    //Create the ZT2 train analysis thread (even if the station has no ZT2... it doesn't hurt anyone)
    mZT2SMThread = new QThread;
    mZT2WorkerThread = new CZT2AnalysisThread();
    mZT2WorkerThread->moveToThread(mZT2SMThread);
    connect(mZT2SMThread,SIGNAL(started()),mZT2WorkerThread,SLOT(AnalyzeZT2Train()));
    connect(mZT2WorkerThread,SIGNAL(ZT2DataUpdate(CZT2ThreadData*)),this,SLOT(ZT2StateChanged(CZT2ThreadData*)));
    connect(mZT2WorkerThread,SIGNAL(DetectionTriggered(CZTDetectionData*)),this,SLOT(ZT2NewDetection(CZTDetectionData*)));
	connect(mZT2WorkerThread,SIGNAL(AddLogString(QString)),this,SLOT(AddLogStringFromThread(QString)));


	mZT1LogDelayTimer.start();

    mTimeToForceSyncFS = false;
    mFSSyncTimer.start();
}

CZTStateMachine::~CZTStateMachine()
{
	mZT1SMThread->quit();
	mZT1SMThread->wait(1000);
	delete mZT1SMThread;
	delete mZT1WorkerThread;
	mZT2SMThread->quit();
	mZT2SMThread->wait(1000);
	delete mZT2SMThread;
	delete mZT2WorkerThread;

    //delete mTKGenerator;

	DestroyZT1Log();
    DestroyZT2Log();
	DestroyCalibrationData();
}

unsigned int CZTStateMachine::ZTStateMachine(unsigned int Event, unsigned int SubEvent)
{

	mCurInputs = mInputModule->GetInputs();

	switch(mZTState)
	{
		case ZT_INIT_STATE:
		{
			//mZTPagePTr->SetZTStatus(mNbPassages,mNbTriggers,mZT1Active,mZT2Active);
			mZTPagePTr->SetZTStatus(mNbPassages,mNbTriggers,mZT1ActiveStatus,mZT2ActiveStatus);
			mZTStationPtr->UpdateStationMasks(mCurInputs);
			mZTStationPtr->UpdateCDVs(mCurInputs);

            //Wait for ZT1 and ZT2 CDVs to be free before starting the State Machine...
            if(!mZTStationPtr->IsZT1CDVOccupied() && !mZTStationPtr->IsZT2CDVOccupied())
            {
                mZTState = ZT_EXECUTE_STATE;
            }
            else
            {
                mZTPagePTr->UpdateCDVDisplay();
                if(mZtSimPtr != 0)	//ptr will be 0 if not in sim mode
                {
                    mZtSimPtr->UpdateSimulatorDisplay();
                }
            }
			break;
		}
		case ZT_EXECUTE_STATE:
		{

            if(mCurInputs != mLastInputs)
			{
				//Update masks
				mZTStationPtr->UpdateStationMasks(mCurInputs);

				//Update variables according to new inputs
				mZTStationPtr->UpdateCDVs(mCurInputs);

                mCIZT1Active = mZTStationPtr->IsCIZT1ITIActive(mCurInputs);
                mCIZT2Active = mZTStationPtr->IsCIZT2ITIActive(mCurInputs);
//				mCIZT1Active = ((mCurInputs & mZTInputMasks->InputZT1ITIMask) != 0);
//				mCIZT2Active = ((mCurInputs & mZTInputMasks->InputZT2ITIMask) != 0);

                bool AN1State, AN2State;

                //If the ModbusCCPtr pointer is not 0, then we are using Modbus and AN states will come from there.
                if(mModbusCCMgr == 0)
                {
                    AN1State = (mCurInputs & mZTInputMasks->InputZT1ANMask) != 0;
                    AN2State = (mCurInputs &mZTInputMasks->InputZT2ANMask) != 0;
                }
                mTKGenerator->SetInputStates(AN1State,mZTStationPtr->IsZT1AlarmAutoAcquireCDVOccupied(),
                                             AN2State,mZTStationPtr->IsZT2AlarmAutoAcquireCDVOccupied());
			}


			if(mIsMaintenanceModeON == false)
			{
				ZT1StateMachine(Event,SubEvent);

				if(mZT2Present) //if there is no ZT2, don't execute the ZT2 SM
				{
					ZT2StateMachine(Event,SubEvent);
				}
			}
			else
			{
				//Do the data acquisition for the PP detection while in maintenance mode.
				unsigned int Inputs = mPCIIOPtr->GetInputs();

				if((Inputs & mZTInputMasks->InputZT1PIMask) == 0)
				{
					if(mMaintenancePPIZT1Latch == false)
					{
						emit MaintenancePPActivated(MAINTENANCE_PPI_ZT1_ID);
						SendTKToPCC(DETECTION_PPI_DETECTION, 27);
						mMaintenancePPIZT1Latch = true;
					}
				}

				if((Inputs & mZTInputMasks->InputZT1PEMask) == 0)
				{
					if(mMaintenancePPEZT1Latch == false)
					{
						SendTKToPCC(DETECTION_PPE_DETECTION, 18);
						emit MaintenancePPActivated(MAINTENANCE_PPE_ZT1_ID);
						mMaintenancePPEZT1Latch = true;
					}
				}

                if(mZT2Present) //if there is no ZT2, don't check the pedals
                {
                    if((Inputs & mZTInputMasks->InputZT2PIMask) == 0)
                    {
                        if(mMaintenancePPIZT2Latch == false)
                        {
                            SendTKToPCC(DETECTION_ZT2_PPI_DETECTION, 27);
                            emit MaintenancePPActivated(MAINTENANCE_PPI_ZT2_ID);
                            mMaintenancePPIZT2Latch = true;
                        }
                    }

                    if((Inputs & mZTInputMasks->InputZT2PEMask) == 0)
                    {
                        if(mMaintenancePPEZT2Latch == false)
                        {
                            SendTKToPCC(DETECTION_ZT2_PPE_DETECTION, 18);
                            emit MaintenancePPActivated(MAINTENANCE_PPE_ZT2_ID);
                            mMaintenancePPEZT2Latch = true;
                        }
                    }
                }

			}


			mZTPagePTr->UpdateCDVDisplay();
			if(mZtSimPtr != 0)	//ptr will be 0 if not in sim mode
			{
				mZtSimPtr->UpdateSimulatorDisplay();
			}

            if(mTimeToForceSyncFS == true)
            {
                mFSSyncTimer.start();
                system("sync");
                mTimeToForceSyncFS = false;
                qDebug() << "Sync FS took: " << mFSSyncTimer.elapsed() << "ms";
                mFSSyncTimer.start();

            }

            break;
		}
	}

	if(mCurInputs != mLastInputs)
		mLastInputs = mCurInputs;

	return RET_OK;
}

unsigned int CZTStateMachine::ZT1StateMachine(unsigned int Event, unsigned int SubEvent)
{
	Q_UNUSED(Event)
	Q_UNUSED(SubEvent)

	switch(mZT1State)
	{
		case ZT1_WAIT_FOR_CI_STATE:
		{
			if(mCIZT1Active)
			{	
				mZT1State = ZT1_WAIT_FOR_TRAIN_STATE;
			}
			else
			{
			}
			break;
		}
		case ZT1_WAIT_FOR_TRAIN_STATE:
		{
			if(mCIZT1Active == false)
			{
				mZT1State = ZT1_WAIT_FOR_CI_STATE;
			}
            if(!mZTStationPtr->IsZT1ApproachPresent())//When there is no approach CDV (Montmorency), use the subsequent CDV to detect maintenance vehicles going backward
            {
                if(mZTStationPtr->IsZT1SubsequentCDVOccupied()) //If subsequent CDV is occupied when waiting for train = vehicle going backward
                {
                    mZT1State = ZT1_WAIT_FOR_SUBSEQUENT_LIBERATION;
                }
                else if(mZTStationPtr->IsZT1CDVOccupied())  //since the approach is the ZT CDV, just consider the latter
                {
                    DestroyZT1Log();
                    mEventsRefTimer.start();
                    mNbPassages++;
                    InsertZT1LogItem();
                    mZT1Log.mZT1Flags.mPGCalibrationON = (mIsPGCalibON == true);
                    mZT1Log.mZT1Flags.mZTDetectionsConfig = mZTDetectionConfig->GetDetectionsConfigMask();
                    mZT1State = ZT1_TRAIN_ON_APPROACH_CDV_STATE;
                }
            }
            else if(mZTStationPtr->IsZT1ApproachCDVOccupied() && !mZTStationPtr->IsZT1CDVOccupied())   //Normal situation
			{
                DestroyZT1Log();
                mEventsRefTimer.start();
                mNbPassages++;
                InsertZT1LogItem();
                mZT1Log.mZT1Flags.mPGCalibrationON = (mIsPGCalibON == true);
                mZT1Log.mZT1Flags.mZTDetectionsConfig = mZTDetectionConfig->GetDetectionsConfigMask();
                mZT1State = ZT1_TRAIN_ON_APPROACH_CDV_STATE;
            }
			break;
		}
        case ZT1_WAIT_FOR_SUBSEQUENT_LIBERATION:    //Subsequent CDV should never be occupied before ZT CDV.
        {
            if(mCIZT1Active == false)
            {
                mZT1State = ZT1_WAIT_FOR_CI_STATE;
                mProgramPtr->SetZT1InhibitionState(false);
            }
            if(!mZTStationPtr->IsZT1CDVOccupied() && !mZTStationPtr->IsZT1SubsequentCDVOccupied())
            {
                mZT1State = ZT1_WAIT_FOR_TRAIN_STATE;
                mProgramPtr->SetZT1InhibitionState(false);
            }

            break;
        }
		case ZT1_TRAIN_ON_APPROACH_CDV_STATE:
		{
			if(mCIZT1Active == false)
			{
				mZT1State = ZT1_WAIT_FOR_CI_STATE;
			}
			else if(!mZTStationPtr->IsZT1ApproachCDVOccupied())
			{
				mZT1State = ZT1_WAIT_FOR_TRAIN_STATE;
			}
            else if(mZTStationPtr->IsZT1CDVOccupied())
            {
                bool abort = false;

                if(mModbusCCMgr != 0)
                {
                    mModbusCCMgr->SetActivatedITI(mZTStationPtr->GetCurrentITI());

                    if(mModbusCCMgr->GetZT1InhibitionFlag() == true)    //Check if the CC wants us to analyze the passage
                    {
                        abort = true;
                        CZTLog::instance()->AddLogString("Passage de train ZT1 inhibé par le SACL",true);
                        mZT1State = ZT1_WAIT_FOR_SUBSEQUENT_LIBERATION;   //Wait for the train to leave the ZT
                        if(mModbusCCMgr != 0)
                        {
                            //update train info received from the CC Modbus interface
                            mTrainTypeDetected = (unsigned)mModbusCCMgr->GetZT1TrainType();
                            mZT1TrainComposition = mModbusCCMgr->GetZT1TrainComposition();
                            mZTPagePTr->SetZT1TrainData(mTrainTypeDetected,mZT1TrainComposition);
                        }

                        mProgramPtr->SetZT1InhibitionState(true);
                    }
                }
                if(abort == false)
                {
                    QString TrainCompo = "Composition : inconnue";
                    mZT1ActiveStatus = SB_ZT_ACTIVE_STATUS;
                    mZTPagePTr->SetZTStatus(mNbPassages,mNbTriggers,mZT1ActiveStatus,mZT2ActiveStatus);
                    mZTPagePTr->SetZT1ActivationState(true);
                    mZTActivationDateTime = QDateTime::currentDateTime();

                    mZTPagePTr->ResetZT1Stats();
                    if(mModbusCCMgr != 0)
                    {
                        //update train info received from the CC Modbus interface
                        mTrainTypeDetected = (unsigned)mModbusCCMgr->GetZT1TrainType();
                        mZT1TrainComposition = mModbusCCMgr->GetZT1TrainComposition();
                        mZT1Log.mZT1Flags.mTrainCompo1 = (qint32)mZT1TrainComposition.at(0);
                        mZT1Log.mZT1Flags.mTrainCompo2 = (qint32)mZT1TrainComposition.at(1);
                        mZT1Log.mZT1Flags.mTrainCompo3 = (qint32)mZT1TrainComposition.at(2);
                        mZT1Log.mZT1Flags.mModbusTrainType = mModbusCCMgr->GetZT1RawTrainType();
                        mZT1Log.mZT1Flags.mZT1ITI = mZTStationPtr->GetCurrentITI();
                        mZTPagePTr->SetZT1TrainData(mTrainTypeDetected,mZT1TrainComposition);
                        if((mZT1TrainComposition[0] == 0) &&
                           (mZT1TrainComposition[1] == 0) &&
                           (mZT1TrainComposition[2] == 0))
                        {
                            //Composition invalide...
                            TrainCompo = "Composition : invalide";
                        }
                        else
                        {
                            TrainCompo.clear();
                            TrainCompo = QString("Composition : %1-%2-%3").arg((int)mZT1TrainComposition[0],3,10,QChar('0')).arg(mZT1TrainComposition[1],3,10,QChar('0')).arg(mZT1TrainComposition[2],3,10,QChar('0'));
                        }

                    }

                    InsertZT1LogItem();

                    mZT1WorkerThread->UpdateDetectionConfig(mZTDetectionConfig);
                    mTKGenerator->ClearTKForNewTrain();
                    mTKGenerator->UpdateDetectionConfig(mZTDetectionConfig);
                    mZT1SMThread->start(QThread::NormalPriority);

                    mZT1PassageTimeLimitTimer.start();
                    //CZTLog::instance()->AddLogString("ZT1 activée",true);


                    CZTLog::instance()->ClearBufferString();
                    //TLog::instance()->AddBufferString("ZT1 activée",true);
                    TrainCompo.prepend("ZT1 activée. ");
                    CZTLog::instance()->AddBufferString(TrainCompo,true);

                    if(mIsPGCalibON)
                    {
                        emit PGCalibrationStatus(mCalibrationPassagesCount+1,mPGNbTotalPassages);
                    }
                    mZT1State = ZT1_ANALYZE_TRAIN_STATE;
                }

			}
			break;
		}
		case ZT1_ANALYZE_TRAIN_STATE:
		{
            if(mZTStationPtr->IsZT1AnalysisFinished())  //Normally, this is true when the train leaves CDV ZT1 (except in Snowdon)...
			{

				mZT1ActiveStatus = SB_ZT_INACTIVE_STATUS;
				mZTPagePTr->SetZTStatus(mNbPassages,mNbTriggers,mZT1ActiveStatus,mZT2ActiveStatus);

				//The train is leaving the ZT1 CDV.  Stop the analysis thread.
                mZT1WorkerThread->TerminateAnalysis();
				mZT1SMThread->quit();

                InsertZT1LogItem();

                CZTLog::instance()->AddBufferString("ZT1 désactivée",true);

				mZT1State = ZT1_POST_DETECTION_STATE;
			}
			else if(mZT1PassageTimeLimitTimer.elapsed() > ZT1_MAX_PASSAGE_DELAY)	//Si le train prend plus de 5 minutes à passer = ignorer le passage.
			{
				mZT1ActiveStatus = SB_ZT_INACTIVE_STATUS;
				mZTPagePTr->SetZTStatus(mNbPassages,mNbTriggers,mZT1ActiveStatus,mZT2ActiveStatus);

				//Stop the analysis thread.
				mZT1WorkerThread->TerminateAnalysis();
				mZT1SMThread->quit();

				DestroyZT1Log();
				CZTLog::instance()->AddLogString("Délai de passage expiré: ZT1 désactivée",true);


				mZT1State = ZT1_WAIT_FOR_CI_STATE;
			}
			break;
		}
        case ZT1_POST_DETECTION_STATE:
		{
			if(AnalyzeZT1PostDetection() == RET_POST_DETECTION_VALID)
			{
				SaveZT1EventsLog(&mZT1Log);
				mTKGenerator->BeginTKEmission();

				if(mIsPGCalibON)
				{
					mCalibrationPassagesCount++;
					if(mCalibrationPassagesCount < mPGNbTotalPassages)
					{
					}
					else
					{
						ComputePGCalibration();
						QuitPGCalibrationMode();
					}
				}
			}
			else	//We detected a véhicule travaux.
			{

				DestroyZT1Log();
			}

            if(mZT2Present == false && (mFSSyncTimer.elapsed() > FILESYSTEM_FORCED_SYNC_TIMEOUT))
            {
                mTimeToForceSyncFS = true;
            }

			mZT1State = ZT1_WAIT_FOR_CI_STATE;
			break;
		}
		default:
		{
			break;
		}
	}

	return RET_OK;
}

unsigned int CZTStateMachine::AnalyzeZT1PostDetection()
{
	//Get useful information from  detections log...


	bool MagSensorCountError = false;
	bool PEQ1Error = false;
	int NbPGDetections = 0;
	int NbFNDetections = 0;
    mZT1Log.mZT1Flags.mIsProblematicPassage = 0;

	for(int i = 0; i < mZT1DetectionsLog.size(); i++)
	{
		if(mZT1DetectionsLog.at(i)->mDetectionID == DETECTION_PG_DETECTION)
		{
			NbPGDetections++;
		}
		if(mZT1DetectionsLog.at(i)->mDetectionID == DETECTION_MAGNETIC_SENSOR_COUNT)
		{
			MagSensorCountError = true;
            CEngLog::instance()->AddLogString("[Post-Détection]--> Flag de détection d'erreur de comptage actif",3);
		}
        if(mZT1DetectionsLog.at(i)->mDetectionID == DETECTION_PEQ1_DETECTION)
		{
            CEngLog::instance()->AddLogString("[Post-Détection]--> Flag de détection PEQ1 actif",3);
            PEQ1Error = true;
		}
	}



    //2015-03-03
    //Computer crash may be related to addition of this section
    ///START

    //If the thread did not detect any count error, check if the rank count is valid...

    if(MagSensorCountError == false)
    {
        quint32 S1Count = 0, S2Count = 0;
        for(int sample = 0; sample < mZT1Log.mZT1LogData.size(); sample++)
        {
            if(mZT1Log.mZT1LogData.at(sample)->mZT1ThreadData != 0)
            {
                if(mZT1Log.mZT1LogData.at(sample)->mZT1ThreadData->mS1Count > S1Count)
                {
                    S1Count = mZT1Log.mZT1LogData.at(sample)->mZT1ThreadData->mS1Count;
                }
                if(mZT1Log.mZT1LogData.at(sample)->mZT1ThreadData->mS2Count > S2Count)
                {
                    S2Count = mZT1Log.mZT1LogData.at(sample)->mZT1ThreadData->mS2Count;
                }
            }
        }

        //Thread should have detected S1 S2 count discrepancy but it's easy to re-check here.
        if(S1Count != S2Count)
        {
            MagSensorCountError = true;
        }
        else if(S1Count != 12 && S1Count != 24 && S1Count != 36)    //Make shure we counted all the ranks
        {
            MagSensorCountError = true;
        }
        else if(S2Count != 12 && S2Count != 24 && S2Count != 36)  //Make shure we counted all the ranks
        {
            MagSensorCountError = true;
        }

    }
    ///END


    bool PostAnalyseData = MagSensorCountError; //If we have a count error, do a post-analysis of the data

    if(PEQ1Error == true && mZTDetectionConfig->mZTDetectionConfig[DETECTION_FCT_ZT1].AnalysisActive)	//If we have a PEQ1 detection, clear all other errors and emit PEQ1 only
	{
        //If we have a counting error AND the PEQ1 is FN stuck (most of the time this will happen because
        //of the sensor count error), then ignore the PEQ1...
        if(MagSensorCountError == true && mZT1PEQType == PEQ1_FN_STUCK_LOW_TYPE)
        {
            PostAnalyseData = true; //This flag is used to re-analyze the data in case of a mag sensor count error
        }
                //If the detection is de-activated, ignore it
        else if((mZT1PEQType == PEQ1_FN_PREDETECTION_TYPE && mZTDetectionConfig->mZTDetectionConfig[DETECTION_FCT_FN].TKActive == false) ||
                (mZT1PEQType == PEQ1_FN_STUCK_LOW_TYPE && mZTDetectionConfig->mZTDetectionConfig[DETECTION_FCT_FN].TKActive == false) ||
                (mZT1PEQType == PEQ1_PEXT_PREDETECTION_TYPE && mZTDetectionConfig->mZTDetectionConfig[DETECTION_FCT_PP].TKActive == false) ||
                (mZT1PEQType == PEQ1_PINT_PREDETECTION_TYPE && mZTDetectionConfig->mZTDetectionConfig[DETECTION_FCT_PP].TKActive == false) ||
                (mZT1PEQType == PEQ1_LAZER_SENSOR_EXT_PREDETECTION_TYPE && mZTDetectionConfig->mZTDetectionConfig[DETECTION_FCT_PG].TKActive == false) ||
                (mZT1PEQType == PEQ1_LAZER_SENSOR_INT_PREDETECTION_TYPE && mZTDetectionConfig->mZTDetectionConfig[DETECTION_FCT_PG].TKActive == false) ||
                (mZT1PEQType == PEQ1_PG_PREDETECTION_TYPE && mZTDetectionConfig->mZTDetectionConfig[DETECTION_FCT_PG].TKActive == false))
        {
            //The PEQ1 is invalid because analysis was deactivated.  Remove it from detection buffer.
            for(int i = 0; i < mZT1DetectionsLog.size(); i++)
            {
                if(mZT1DetectionsLog.at(i)->mDetectionID == DETECTION_PEQ1_DETECTION)
                {
                    mZT1DetectionsLog.remove(i);
                    break;
                }
            }
            PEQ1Error = false;
            PostAnalyseData = false;
            CZTLog::instance()->AddBufferString("[POST DETECTION]--> Annulation de la Panne Équipement ZT1 car l'analyse de cette panne est désactivée.");
        }
        else    //We need to consider PEQ1.  Clear all detections and send only PEQ1...
        {
            PostAnalyseData = false;

            ClearZT1DetectionsList();
            NbPGDetections = 0;

            if(mZTDetectionConfig->mZTDetectionConfig[DETECTION_FCT_ZT1].AnalysisActive)
            {

                if(mZT1PEQType == PEQ1_PG_PREDETECTION_TYPE)
                {
                    CZTLog::instance()->AddBufferString("Panne équipement ZT1: Pré-détection Pneu de guidage",true);
                }
                if(mZT1PEQType == PEQ1_LAZER_SENSOR_INT_PREDETECTION_TYPE)
                {
                    CZTLog::instance()->AddBufferString("Panne équipement ZT1: Pré-détection Sonde Lazer Intérieure",true);
                }
                if(mZT1PEQType == PEQ1_LAZER_SENSOR_EXT_PREDETECTION_TYPE)
                {
                    CZTLog::instance()->AddBufferString("Panne équipement ZT1: Pré-détection Sonde Lazer Extérieure",true);
                }
                if(mZT1PEQType == PEQ1_PINT_PREDETECTION_TYPE)
                {
                    CZTLog::instance()->AddBufferString("Panne équipement ZT1: Pré-détection Pneu Porteur Intérieur",true);
                }
                if(mZT1PEQType == PEQ1_PEXT_PREDETECTION_TYPE)
                {
                    CZTLog::instance()->AddBufferString("Panne équipement ZT1: Pré-détection Pneu Porteur Extérieur",true);
                }
                if(mZT1PEQType == PEQ1_FN_PREDETECTION_TYPE)
                {
                    CZTLog::instance()->AddBufferString("Panne équipement ZT1: Pré-détection frotteur négatif",true);
                }
                if(mZT1PEQType == PEQ1_FN_STUCK_LOW_TYPE)
                {
                    CZTLog::instance()->AddBufferString("Panne équipement ZT1: SDF <collé> à 1",true);
                }
                CZTDetectionData *NewDetection = new CZTDetectionData();
                NewDetection->mDetectionID = DETECTION_PEQ1_DETECTION;
                NewDetection->mTimeStamp = 0;
                NewDetection->mRank = ZT_DEFAULT_DETECTION_RANK;
                mZT1DetectionsLog.prepend(NewDetection);


            }
        }
    }

    if(PostAnalyseData == true)	//check if we need to do a post-analysis of the data.  ONLY IF THERE ARE MAG SENSOR COUNT ERROR!!!
	{
		int S1Count = 0, S2Count = 0, FNCount = 0, PGCount = 0, PPICount = 0, PPECount = 0;

		int S1 = 0, S2 = 0, FN = 0, PPI = 0, PPE = 0;

		bool PGActive = false;
		bool PGCalibrationON = mZT1Log.mZT1Flags.mPGCalibrationON == 1;
		qint32 PGIOffset = mZT1Log.mZT1Flags.mIntPGOffset;
		qint32 PGEOffset = mZT1Log.mZT1Flags.mExtPGOffset;
		qint32 PGValueTreshold = mZT1Log.mZT1Flags.mPGTresholdValue;
		qint32 Quadra;
		int PGNonDetectionSampleCount = 0;

        CEngLog::instance()->AddLogString("[Post-Détection]--> Début de l'analyse en Post-Détection",3);


		//First, clear the detections list.
		ClearZT1DetectionsList();

		CZT1ThreadData *Data;
		//Analyze the log and add detections to the list if any.
		for(int sample = 0; sample < mZT1Log.mZT1LogData.size(); sample++)
		{
			if(mZT1Log.mZT1LogData.at(sample)->mZT1ThreadData != 0)
			{
				Data = mZT1Log.mZT1LogData.at(sample)->mZT1ThreadData;

				//Count magnetic sensors.
				//Detect High to Low transitions
				if(S1 == 1 && Data->mS1 == 0)
				{
					S1Count++;
				}
				S1 = Data->mS1;

				//Detect High to Low transitions
				if(S2 == 1 && Data->mS2 == 0)
				{
					S2Count++;
				}
				S2 = Data->mS2;

				//Count FN occurences
				//Detect High to Low transitions
				if(FN == 1 && Data->mFN == 0)
				{
					FNCount++;
				}
				FN = Data->mFN;

				//If we were calibrating, ignore the PG data.
				if(PGCalibrationON == false)
				{
					//Compute and verify if the PG is detected.
					Quadra = ((Data->mPGIntValue - PGIOffset) * (Data->mPGExtValue - PGEOffset));
					if(Quadra > PGValueTreshold)
					{
						if(PGActive == false)
						{
							PGCount++;
							PGActive = true;
						}
						PGNonDetectionSampleCount = 0;
					}
					else
					{
						if(PGActive == true)
						{
							if(PGNonDetectionSampleCount >= 3)	//To avoid false detections when glitches or rebounds appear, we need to have at least 3 consecutive samples
							{									//without detection to consider the PG inactive
								PGActive = false;
							}
							PGNonDetectionSampleCount++;
						}

					}
				}


                //Check for PP detections if needed
				if(mZTDetectionConfig->mZTDetectionConfig[DETECTION_FCT_PP].AnalysisActive == true)
				{
					if(PPI == 1 && Data->mPInt == 0)
					{
						PPICount++;

						//register the detection in the list.
						CZTDetectionData *NewDetection = new CZTDetectionData();
						NewDetection->mDetectionID = DETECTION_PPI_DETECTION;
						NewDetection->mTimeStamp = Data->mTimeStamp;
						NewDetection->mRank = ZT_DEFAULT_DETECTION_RANK;
						mZT1DetectionsLog.append(NewDetection);

					}
					PPI = Data->mPInt;

					if(PPE == 1 && Data->mPExt == 0)
					{
						PPECount++;

						//register the detection in the list.
						CZTDetectionData *NewDetection = new CZTDetectionData();
						NewDetection->mDetectionID = DETECTION_PPE_DETECTION;
						NewDetection->mTimeStamp = Data->mTimeStamp;
						NewDetection->mRank = ZT_DEFAULT_DETECTION_RANK;
						mZT1DetectionsLog.append(NewDetection);
					}
					PPE = Data->mPExt;
				}
            }
		}

        CEngLog::instance()->AddLogString(QString().sprintf("[Post-Détection]--> Résultat des données brutes: [S1Count = %d], [S2Count = %d], [PGCount = %d], [FNCount = %d], [PPICount = %d], [PPECount = %d]",S1Count,S2Count,PGCount,FNCount,PPICount,PPECount),3);

        //First, try to detect a false occupation of the ZT CDV.
        //After, try to detect a véhicule travaux.  If we have a PG count of 0, it is a véhicule travaux and we
        //just totally ignore this passage
		if(PGCalibrationON == false)
		{

            if(S1Count == 0 && S2Count == 0)
            {
                CZTLog::instance()->AddLogString("[POST DETECTION]--> Fausse occupation détectée.  Annulation et fin de l'analyse ZT1",true);
                ClearZT1DetectionsList();
                return RET_POST_DETECTION_FALSE_OCCUPATION;
            }

            if(PGCount == 0 && mZTDetectionConfig->mZTDetectionConfig[DETECTION_FCT_PG].AnalysisActive == true)
            {
                CZTLog::instance()->AddLogString("[POST DETECTION]--> Véhicule travaux détecté.  Annulation et fin de l'analyse ZT1",true);
                ClearZT1DetectionsList();
				return RET_POST_DETECTION_WORK_VEHICLE;
			}



		}


		//Analyze the PG count if necessary
		if(mZTDetectionConfig->mZTDetectionConfig[DETECTION_FCT_PG].AnalysisActive == true)
		{
			if(PGCalibrationON == false)
			{


				if(PGCount == 12 || PGCount == 24 || PGCount == 36)
				{
					//Everything is fine with the PG.
					NbPGDetections = 0;
				}
				else
                {
                    CEngLog::instance()->AddLogString(QString().sprintf("[Post-Détection]--> Compte PG invalide : PGCount = %d",PGCount),3);
					if(PGCount > 24)
					{
						NbPGDetections = 36 - PGCount;
					}
					else if(PGCount > 12)
					{
						NbPGDetections = 24 - PGCount;
					}
					else
					{
						NbPGDetections = 12 - PGCount;
					}
				}

				int alarms = NbPGDetections;

                if(alarms < 0)
                {
                    alarms = -1 * alarms;
                }

				for(int i = 0; i < alarms; i++)
				{
					CZTDetectionData *NewDetection = new CZTDetectionData();
					NewDetection->mDetectionID = DETECTION_PG_DETECTION;
					NewDetection->mTimeStamp = 0;
					NewDetection->mRank = ZT_DEFAULT_DETECTION_RANK;
					mZT1DetectionsLog.append(NewDetection);
				}
			}
		}



		//Analyze FN count if necessary
		if(mZTDetectionConfig->mZTDetectionConfig[DETECTION_FCT_FN].AnalysisActive == true)
        {

            if(FNCount == 6 || FNCount == 12 || FNCount == 18)
            {
                //Everything is fine with the FN.
                NbFNDetections = 0;
            }
            else
            {
                CEngLog::instance()->AddLogString(QString().sprintf("[Post-Détection]--> Compte FN invalide : FNCount = %d",FNCount),3);
                if(FNCount > 12)
                {
                    NbFNDetections = 18 - FNCount;
                }
                else if(FNCount > 6)
                {
                    NbFNDetections = 12 - FNCount;
                }
                else
                {
                    NbFNDetections = 6 - FNCount;
                }
            }

            int alarms = NbFNDetections;

            if(alarms < 0)
            {
                alarms = -1 * alarms;
            }

            if(alarms != 0)
            {
                //With Modbus interface, we send as many alarms as there are detections with rank 99 (ZT_DEFAULT_DETECTION_RANK)
                for(int i = 0; i < alarms; i++)
                {
                    CZTDetectionData *NewDetection = new CZTDetectionData();
                    NewDetection->mDetectionID = DETECTION_FN_DETECTION;
                    NewDetection->mTimeStamp = 0;
                    NewDetection->mRank = ZT_DEFAULT_DETECTION_RANK;
                    mZT1DetectionsLog.append(NewDetection);
                }
//                //Send two detections only (rank 1 & 18) when detecting FN errors while in V00
//                QString string;
//                string.sprintf("[POST DETECTION]--> Déclenchement FN avec erreur de comptage (FNCount = %d).  Envoi d'alarmes aux rangs 1 et 18.",FNCount);
//                //CZTLog::instance()->AddBufferString("Déclenchement FN avec erreur de comptage.  Envoi d'alarmes aux rangs 1 et 18.",true);
//                CZTLog::instance()->AddBufferString(string,true);
//                CZTDetectionData *NewDetection = new CZTDetectionData();
//                NewDetection->mDetectionID = DETECTION_FN_DETECTION;
//                NewDetection->mTimeStamp = 0;
//                NewDetection->mRank = 1;
//                mZT1DetectionsLog.append(NewDetection);

//                NewDetection = new CZTDetectionData();
//                NewDetection->mDetectionID = DETECTION_FN_DETECTION;
//                NewDetection->mTimeStamp = 0;
//                NewDetection->mRank = 18;
//                mZT1DetectionsLog.append(NewDetection);
            }
        }

		if(mZT1DetectionsLog.size() != 0)
		{
			if(mZTDetectionConfig->mZTDetectionConfig[DETECTION_FCT_ZT1].AnalysisActive)
			{
				//If here, we had at least one detection.  We must emit the V00 alarm.

				CZTDetectionData *NewDetection = new CZTDetectionData();
				NewDetection->mDetectionID = DETECTION_MAGNETIC_SENSOR_COUNT;
				NewDetection->mTimeStamp = 0;
				NewDetection->mRank = ZT_DEFAULT_DETECTION_RANK;
                mZT1DetectionsLog.append(NewDetection);
			}
		}
		else
		{
			//If here, we did not detect other errors than mag. sensor count.
			//We can tolerate 2 miss

			if(mZTDetectionConfig->mZTDetectionConfig[DETECTION_FCT_ZT1].AnalysisActive)
			{
				int SensorCount = S1Count;
				if(SensorCount > S2Count)
					SensorCount = S2Count;	//Use the least of the 2 counts...

				bool Error = false;
                if(S1Count > 36 || S2Count > 36)
                {
                    //Counted more than 36 ranks on at least one sensor...
                    Error = true;
                }
				if(SensorCount > 24 && SensorCount < 34)
				{
					//more than 2 miss for a 3 elements train
					Error = true;
				}
				else if(SensorCount > 12 && SensorCount < 22)
				{
					//more than 2 miss for a 2 elements train
					Error = true;
				}
				else if(SensorCount < 10)
				{
					//more than 2 miss for a 1 element train
					Error = true;
				}

				if(Error)
				{
					CZTDetectionData *NewDetection = new CZTDetectionData();
					NewDetection->mDetectionID = DETECTION_MAGNETIC_SENSOR_COUNT;
					NewDetection->mTimeStamp = 0;
					NewDetection->mRank = ZT_DEFAULT_DETECTION_RANK;
					mZT1DetectionsLog.append(NewDetection);
				}
				else
				{
                    QString string;
                    string.sprintf("[POST DETECTION]--> Erreur de comptage ZT1 : [S1=%d] [S2=%d] essieux comptés (TK non émise au PCC car <= 2 essieux sans autre détection )",S1Count, S2Count);
                    CZTLog::instance()->AddBufferString(string,true,true);
                    mZT1Log.mZT1Flags.mIsProblematicPassage = 1;
				}
			}
		}

	}
	else//No magnetic sensors count error
	{
		//If we have more than 3 PG detections, check if they happened on consecutive ranks
		//if so, remove them from the list and emit a PEQ1 alarm instead.
        if(NbPGDetections >= 3 && mZT1DetectionsLog.size() >= 3)
		{
			//List the detections
            int DetectionIDs[1024/*NbPGDetections*/];
			int index = 0;

			for(int i = 0; i < mZT1DetectionsLog.size(); i++)
			{
				if(mZT1DetectionsLog.at(i)->mDetectionID == DETECTION_PG_DETECTION)
				{
					DetectionIDs[index++] = i;
				}
			}

			//Now check if the ranks are consecutive.  If so, remove the events from the list.
			if((mZT1DetectionsLog.at(DetectionIDs[0])->mRank+1 == mZT1DetectionsLog.at(DetectionIDs[1])->mRank) &&
			   (mZT1DetectionsLog.at(DetectionIDs[1])->mRank+1 == mZT1DetectionsLog.at(DetectionIDs[2])->mRank))
			{
				//remove all PG detections.
				for(int i = NbPGDetections-1 ; i >= 0  ; i--)
				{
					delete mZT1DetectionsLog.at(DetectionIDs[i]);
					mZT1DetectionsLog.remove(DetectionIDs[i]);
				}

                CZTLog::instance()->AddBufferString("[POST DETECTION]--> Plus de 3 PG consécutifs détectés (TK non émise au PCC)",true,true);
                mZT1Log.mZT1Flags.mIsProblematicPassage = 1;

			}

		}

		//Check if the rank count is valid even without mag. sensor error
		unsigned int S1Count = 0, S2Count = 0;
		for(int i = 0; i < mZT1Log.mZT1LogData.size(); i++)
		{
			if(mZT1Log.mZT1LogData.at(i)->mZT1ThreadData != 0)
			{
				if(mZT1Log.mZT1LogData.at(i)->mZT1ThreadData->mS1Count > S1Count)
				{
					S1Count = mZT1Log.mZT1LogData.at(i)->mZT1ThreadData->mS1Count;
				}
				if(mZT1Log.mZT1LogData.at(i)->mZT1ThreadData->mS2Count > S2Count)
				{
					S2Count = mZT1Log.mZT1LogData.at(i)->mZT1ThreadData->mS2Count;
				}
			}
		}

		if((S1Count == 12 || S1Count == 24 || S1Count == 36) &&
		   (S2Count == 12 || S2Count == 24 || S2Count == 36))
		{
			//Everything is fine, no sensor count error...
		}
		else
		{
			if(mZT1DetectionsLog.size() != 0)
			{
				//If here, we had at least one detection.  We must emit the V00 alarm.
                CEngLog::instance()->AddLogString(QString().sprintf("[Post-Détection]--> Erreur de comptage sans flag en temps réel"),3);

				CZTDetectionData *NewDetection = new CZTDetectionData();
				NewDetection->mDetectionID = DETECTION_MAGNETIC_SENSOR_COUNT;
				NewDetection->mTimeStamp = 0;
				NewDetection->mRank = ZT_DEFAULT_DETECTION_RANK;
				mZT1DetectionsLog.prepend(NewDetection); //Put the V00 alarm first (prepend)

				//Clear the detections rank of all the detections since it may not be valid.
				for(int i = 0; i < mZT1DetectionsLog.size(); i++)
				{
					mZT1DetectionsLog.at(i)->mRank = ZT_DEFAULT_DETECTION_RANK;
				}

                //JFM 20160112 V1.10
                //If there was FN detection, replace it with two alarms at rank 1 and 18.
                bool FNDetection = false;
                int i = 0;
                while(i < mZT1DetectionsLog.size())
                {
                    if(mZT1DetectionsLog.at(i)->mDetectionID == DETECTION_FN_DETECTION)
                    {
                        CEngLog::instance()->AddLogString(QString().sprintf("[Post-Détection]--> Déclenchement FN détecté dans liste des alarmes"),3);
                        FNDetection = true;
                        delete mZT1DetectionsLog.at(i);
                        mZT1DetectionsLog.remove(i);        //remove this alarm
                    }
                    else
                    {
                        i++;
                    }
                };
                if(FNDetection == true)
                {
                    CZTLog::instance()->AddBufferString("[Post-Détection]--> Déclenchement FN avec erreur de comptage.  Envoi d'alarmes aux rangs 1 et 18.",true);
                    CZTDetectionData *NewDetection = new CZTDetectionData();
                    NewDetection->mDetectionID = DETECTION_FN_DETECTION;
                    NewDetection->mTimeStamp = 0;
                    NewDetection->mRank = 1;
                    mZT1DetectionsLog.append(NewDetection);

                    NewDetection = new CZTDetectionData();
                    NewDetection->mDetectionID = DETECTION_FN_DETECTION;
                    NewDetection->mTimeStamp = 0;
                    NewDetection->mRank = 18;
                    mZT1DetectionsLog.append(NewDetection);
                }
			}
			else
			{
				//If here, we did not detect other errors than mag. sensor count.
				//We can tolerate 2 miss

				unsigned int SensorCount = S1Count;
				if(SensorCount > S2Count)
					SensorCount = S2Count;	//Use the least of the 2 counts...

				bool Error = false;
                if(S1Count > 36 || S2Count > 36)
                {
                    //Counted more than 36 ranks...
                    Error = true;
                }
                else if(SensorCount > 24 && SensorCount < 34)
				{
					//more than 2 miss for a 3 elements train
					Error = true;
				}
				else if(SensorCount > 12 && SensorCount < 22)
				{
					//more than 2 miss for a 2 elements train
					Error = true;
				}
				else if(SensorCount < 10)
				{
					//more than 2 miss for a 1 element train
					Error = true;
				}

				if(Error)
				{
					CZTDetectionData *NewDetection = new CZTDetectionData();
					NewDetection->mDetectionID = DETECTION_MAGNETIC_SENSOR_COUNT;
					NewDetection->mTimeStamp = 0;
					NewDetection->mRank = ZT_DEFAULT_DETECTION_RANK;
					mZT1DetectionsLog.append(NewDetection);

				}
				else
				{
                    QString string;
                    string.sprintf("[POST DETECTION]--> Erreur de comptage ZT1 : [S1=%d] et [S2=%d] essieux comptés (TK non émise au PCC car <= 2 essieux sans autre détection )",S1Count, S2Count);
                    CZTLog::instance()->AddBufferString(string,true,true);
                    mZT1Log.mZT1Flags.mIsProblematicPassage =1;

				}
			}
		}
	}


	//Process detections and send TKs to PCC/PCM
	for(int i = 0; i < mZT1DetectionsLog.size(); i++)
	{
		mTKGenerator->ProcessDetectionsTK(mZT1DetectionsLog.at(i));

		mNbTriggers++;

		QString str("");
		if(mZT1DetectionsLog.at(i)->mDetectionID == DETECTION_FN_DETECTION)
			str.sprintf("[POST DETECTION]--> %s au bogie %d (rang %d ou %d)",CZTData::GetErrorString(mZT1DetectionsLog.at(i)->mDetectionID),mZT1DetectionsLog.at(i)->mRank,(mZT1DetectionsLog.at(i)->mRank*2)-1,mZT1DetectionsLog.at(i)->mRank*2);
		else
			str.sprintf("[POST DETECTION]--> %s au rang %d",CZTData::GetErrorString(mZT1DetectionsLog.at(i)->mDetectionID),mZT1DetectionsLog.at(i)->mRank);
        CZTLog::instance()->AddBufferString(str,true);
	}

    if(mZT1DetectionsLog.size() != 0 || CZTLog::instance()->IsWriteForced())
    {
        CZTLog::instance()->WriteBufferToLog();
    }
    else
    {
        CZTLog::instance()->ClearBufferString(); //2015-03-03 To avoid potention memory leak.  Computer Crash problem resolution tentative...
    }

    CZTPassageInfo PassageInfo;
    PassageInfo.mDetections = &mZT1DetectionsLog;
    PassageInfo.mNbPassages = mNbPassages;
    PassageInfo.mNbDeclenchements = mNbTriggers;
    PassageInfo.mTrainType = mZT1Log.mZT1Flags.mModbusTrainType;
    PassageInfo.mTrainCompo1 = mZT1Log.mZT1Flags.mTrainCompo1;
    PassageInfo.mTrainCompo2 = mZT1Log.mZT1Flags.mTrainCompo2;
    PassageInfo.mTrainCompo3 = mZT1Log.mZT1Flags.mTrainCompo3;
    PassageInfo.mPassageDateTime = mZTActivationDateTime;


    mProgramPtr->NewZT1Passage(PassageInfo);

	mZTPagePTr->SetZTStatus(mNbPassages,mNbTriggers,mZT1ActiveStatus,mZT2ActiveStatus);

	return RET_POST_DETECTION_VALID;
}

unsigned int CZTStateMachine::ZT2StateMachine(unsigned int Event, unsigned int SubEvent)
{
	Q_UNUSED(Event)
	Q_UNUSED(SubEvent)

	if(IsZT1Active() == false)
	{
		switch(mZT2State)
		{
			case ZT2_WAIT_FOR_CI_STATE:
			{
				if(mCIZT2Active)
				{
                    if(mZTStationPtr->IsZT2CDVOccupied() == false)    //the ZT2 CDV must be free.
                    {
                        mZT2State = ZT2_WAIT_FOR_TRAIN_STATE;
                    }
                    else
                    {
                        mZT2State = ZT2_WAIT_FOR_APPROACH_LIBERATION_STATE;
                    }

				}
                break;
			}
            case ZT2_WAIT_FOR_APPROACH_LIBERATION_STATE:
            {
                if(mZTStationPtr->IsZT2ApproachCDVOccupied() == false)
                {
                    mZT2State = ZT2_WAIT_FOR_CI_STATE;
                }
                break;
            }
			case ZT2_WAIT_FOR_TRAIN_STATE:
			{
				if(mCIZT2Active == false)
				{
					mZT2State = ZT2_WAIT_FOR_CI_STATE;
				}
                else if(mZTStationPtr->IsZT2ApproachCDVOccupied()) //À certaines stations, la ZT2 doit être activée à l'occupation du CDV d'approche.
				{
                    bool abort = false;

                    if(mModbusCCMgr != 0)
                    {
                        if(mModbusCCMgr->GetZT2InhibitionFlag() == true)    //Check if the CC wants us to analyze the passage
                        {
                            abort = true;
                            CZTLog::instance()->AddLogString("Passage de train ZT2 inhibé par le SACL",true);
                            mZT2State = ZT2_WAIT_FOR_CDV_LIBERATION_STATE;   //Wait for the train to leave the ZT

                            //update train info received from the CC Modbus interface
                            mZT2TrainType = (unsigned)mModbusCCMgr->GetZT2TrainType();
                            mZT2TrainComposition = mModbusCCMgr->GetZT2TrainComposition();
                            mZTPagePTr->SetZT2TrainData(mZT2TrainType,mZT2TrainComposition);

                            mProgramPtr->SetZT2InhibitionState(true);
                        }
                    }


                    if(abort == false)
                    {
                        DestroyZT2Log();
                        mEventsRefTimer.start();
                        mZTPagePTr->SetZT2ActivationSTate(true);
                        mZT2ActiveStatus = SB_ZT_ACTIVE_STATUS;
                        mZTActivationDateTime = QDateTime::currentDateTime();
                        mZTPagePTr->SetZTStatus(mNbPassages,mNbTriggers,mZT1ActiveStatus,mZT2ActiveStatus);
                        mZTPagePTr->ResetZT2Stats();
                        mZT2Log.mZT2Flags.mZTDetectionsConfig = mZTDetectionConfig->GetDetectionsConfigMask();
                        if(mModbusCCMgr != 0)
                        {
                            //update train info received from the CC Modbus interface
                            mZT2TrainType = (unsigned)mModbusCCMgr->GetZT2TrainType();
                            mZT2TrainComposition = mModbusCCMgr->GetZT2TrainComposition();
                            mZT2Log.mZT2Flags.mTrainCompo1 = (qint32)mZT2TrainComposition.at(0);
                            mZT2Log.mZT2Flags.mTrainCompo2 = (qint32)mZT2TrainComposition.at(1);
                            mZT2Log.mZT2Flags.mTrainCompo3 = (qint32)mZT2TrainComposition.at(2);
                            mZT2Log.mZT2Flags.mModbusTrainType = mModbusCCMgr->GetZT2RawTrainType();
                            mZTPagePTr->SetZT2TrainData(mZT2TrainType,mZT2TrainComposition);
                        }

                        InsertZT2LogItem();
                        mZT2WorkerThread->UpdateDetectionConfig(mZTDetectionConfig);
                        mTKGenerator->UpdateDetectionConfig(mZTDetectionConfig);
                        mZT2SMThread->start(QThread::NormalPriority);

                        mZT2PassageTimeLimitTimer.start();
                        mZT2State = ZT2_ANALYZE_TRAIN_STATE;

                        CZTLog::instance()->ClearBufferString();
                        CZTLog::instance()->AddBufferString("ZT2 activée",true);
                    }
				}
				break;
			}
			case ZT2_ANALYZE_TRAIN_STATE:
			{
                if(!mZTStationPtr->IsZT2CDVOccupied() && !mZTStationPtr->IsZT2ApproachCDVOccupied())  //train quit the ZT2 cdv.
				{
					mZT2ActiveStatus = SB_ZT_INACTIVE_STATUS;
					mZTPagePTr->SetZTStatus(mNbPassages,mNbTriggers,mZT1ActiveStatus,mZT2ActiveStatus);

					//The train is leaving the ZT2 CDV.  Stop the analysis thread.
					mZT2WorkerThread->TerminateAnalysis();
					mZT2SMThread->quit();

					InsertZT2LogItem();

                    CZTLog::instance()->AddBufferString("ZT2 désactivée",true);

					mZT2State = ZT2_POST_DETECTION_STATE;
				}
				if(mZT2PassageTimeLimitTimer.elapsed() > ZT2_MAX_PASSAGE_DELAY )
				{
					mZT2ActiveStatus = SB_ZT_INACTIVE_STATUS;
					mZTPagePTr->SetZTStatus(mNbPassages,mNbTriggers,mZT1ActiveStatus,mZT2ActiveStatus);

					//Stop the analysis thread.
					mZT2WorkerThread->TerminateAnalysis();
					mZT2SMThread->quit();

					DestroyZT2Log();
                    CZTLog::instance()->AddLogString("Délai de passage expiré: Analyse ZT2 annulée.  Attente de libération du CDV",true);

                    mZT2State = ZT2_WAIT_FOR_CDV_LIBERATION_STATE;
				}
				break;
			}
            case ZT2_WAIT_FOR_CDV_LIBERATION_STATE:
            {
                if(!mZTStationPtr->IsZT2CDVOccupied() && !mZTStationPtr->IsZT2ApproachCDVOccupied())
                {
                    CZTLog::instance()->AddLogString("CDV ZT2 Libéré.  ZT2 prête",true);
                    mZT2State = ZT2_WAIT_FOR_CI_STATE;
                    mProgramPtr->SetZT2InhibitionState(false);
                }
                break;
            }
			case ZT2_POST_DETECTION_STATE:
			{
				//InsertZT2LogItem();	//JFM 2014-06-11 Not sure if necessary...
				AnalyzeZT2PostDetection();
				SaveZT2EventsLog(&mZT2Log);
                mTKGenerator->BeginTKEmission();

				mZT2State = ZT2_WAIT_FOR_CI_STATE;


                if(mFSSyncTimer.elapsed() > FILESYSTEM_FORCED_SYNC_TIMEOUT)
                {
                    mTimeToForceSyncFS = true;
                }
				break;
			}
			default:
			{
				break;
			}

		}
	}
	return RET_OK;
}


unsigned int CZTStateMachine::AnalyzeZT2PostDetection()
{
	bool PEQ2 = false;
	qint64 PEQ2Timestamp = 0;
    mZT2Log.mZT2Flags.mIsProblematicPassage = 0;

	for(int i = 0; i < mZT2DetectionsLog.size(); i++)
	{
		if(mZT2DetectionsLog.at(i)->mDetectionID == DETECTION_PEQ2_DETECTION)
		{
			PEQ2 = true;
			PEQ2Timestamp = mZT2DetectionsLog.at(i)->mTimeStamp;
		}
	}

	//If we have a PEQ2, we remove all detections an leave only the PEQ2 one
    if(PEQ2 == true && mZTDetectionConfig->mZTDetectionConfig[DETECTION_FCT_ZT2].AnalysisActive && mZTDetectionConfig->mZTDetectionConfig[DETECTION_FCT_PP2].AnalysisActive)
    {
        for(int i = 0; i < mZT2DetectionsLog.size(); i++)
        {
            delete mZT2DetectionsLog.at(i);
        }
        mZT2DetectionsLog.clear();

        CZTDetectionData *NewDetection = new CZTDetectionData();
        NewDetection->mDetectionID = DETECTION_PEQ2_DETECTION;
        NewDetection->mTimeStamp = PEQ2Timestamp;
        NewDetection->mRank = ZT_DEFAULT_DETECTION_RANK;
        mZT2DetectionsLog.prepend(NewDetection);
	}
	else
	{

		//Check for magnetic sensor count error.
		int S1Count = 0;
        for(int i = 0; i < mZT2Log.mZT2LogData.size(); i++)
		{
            if(mZT2Log.mZT2LogData.at(i)->mZT2ThreadData != 0)
			{
                if((int)mZT2Log.mZT2LogData.at(i)->mZT2ThreadData->mS1Count > S1Count)
				{
                    S1Count = mZT2Log.mZT2LogData.at(i)->mZT2ThreadData->mS1Count;
				}
			}
		}

		if(S1Count == 12 || S1Count == 24 || S1Count == 36)
		{
			//Everything is fine !

		}
		else
		{
			//We had a sensor count error.  Emit V02 only if we also have a PP detection.
            if(mZT2DetectionsLog.size() == 0)
			{
                //No PP detection.
                QString string("");
                string.sprintf("[POST DETECTION]--> Erreur de comptage ZT2 : %d essieux comptés (TK non émise au PCC car pas de déclenchement PP)",S1Count);
                CZTLog::instance()->AddBufferString(string,true,true);
                mZT2Log.mZT2Flags.mIsProblematicPassage = 1;
			}
			else
			{
				//There has been a PP detection.  Add V02 alarm to the list (at the beginning)

				CZTDetectionData *NewDetection = new CZTDetectionData();
                NewDetection->mDetectionID = DETECTION_ZT2_MAGNETIC_SENSOR_COUNT;
				NewDetection->mTimeStamp = 0;
				NewDetection->mRank = ZT_DEFAULT_DETECTION_RANK;
                mZT2DetectionsLog.append(NewDetection);

				//Clear the rank value for the other detections since it may not be valid
				for(int i = 0; i < mZT2DetectionsLog.size(); i++)
				{
					mZT2DetectionsLog.at(i)->mRank = ZT_DEFAULT_DETECTION_RANK;
				}

			}
		}
	}


	//Process detections and send TKs to PCC/PCM
	for(int i = 0; i < mZT2DetectionsLog.size(); i++)
	{
		mTKGenerator->ProcessDetectionsTK(mZT2DetectionsLog.at(i));

		mNbTriggers++;
		QString str("");
		str.sprintf("[POST DETECTION]--> %s au rang %d",CZTData::GetErrorString(mZT2DetectionsLog.at(i)->mDetectionID),mZT2DetectionsLog.at(i)->mRank);
//		CZTLog::instance()->AddLogString(str,true);
        CZTLog::instance()->AddBufferString(str,true);
	}

    if(mZT2DetectionsLog.size() != 0 || CZTLog::instance()->IsWriteForced())
    {
        CZTLog::instance()->WriteBufferToLog();
    }
    else
    {
        CZTLog::instance()->ClearBufferString();    //2015-03-03 To avoid potention memory leak.  Computer Crash problem resolution tentative...
    }

    CZTPassageInfo PassageInfo;
    PassageInfo.mDetections = &mZT2DetectionsLog;
    PassageInfo.mNbPassages = mNbPassages;
    PassageInfo.mNbDeclenchements = mNbTriggers;
    PassageInfo.mTrainType = mZT2Log.mZT2Flags.mModbusTrainType;
    PassageInfo.mTrainCompo1 = mZT2Log.mZT2Flags.mTrainCompo1;
    PassageInfo.mTrainCompo2 = mZT2Log.mZT2Flags.mTrainCompo2;
    PassageInfo.mTrainCompo3 = mZT2Log.mZT2Flags.mTrainCompo3;
    PassageInfo.mPassageDateTime = mZTActivationDateTime;
    mProgramPtr->NewZT2Passage(PassageInfo);

	//mZTPagePTr->SetZTStatus(mNbPassages,mNbTriggers,mZT1Active,mZT2Active);
	mZTPagePTr->SetZTStatus(mNbPassages,mNbTriggers,mZT1ActiveStatus,mZT2ActiveStatus);

	return RET_OK;
}

bool CZTStateMachine::IsZT1Active()
{
    if(mZT1State == ZT1_TRAIN_ON_APPROACH_CDV_STATE || mZT1State == ZT1_ANALYZE_TRAIN_STATE)
	{
        return true;
	}

    return false;
}

bool CZTStateMachine::IsZT2Active()
{
	if(mZT2State == ZT2_WAIT_FOR_CI_STATE || mZT2State == ZT2_WAIT_FOR_TRAIN_STATE)
	{
		return false;
	}

	return true;
}


void CZTStateMachine::BindPointers(CStation *ptr,CInputModule *InputModule,COutputModule *OutputModule,CZTPage *ZTPagePTr,CPCIIOMgr *PCIIOPtr,CAbstractLazerProbe *PGIntProbePtr, CAbstractLazerProbe *PGExtProbePTr,CLogMgr *LogMgr,CTKTransportInterface *TKTransport,CZTSimulator *ZTSimPtr,CAnalogInputModule *DataQInterface)
{
    Q_UNUSED(OutputModule)

	mZTStationPtr = ptr;
	mSDFAnalogMonitorInterface = DataQInterface;
	mInputModule = InputModule;
	mZTPagePTr = ZTPagePTr;
	mZtSimPtr = ZTSimPtr;
	mZTInputMasks = mZTStationPtr->GetInputMasks();
	mPCIIOPtr = PCIIOPtr;
	mPGExtProbePtr = PGExtProbePTr;
	mPGIntProbePtr = PGIntProbePtr;
    mLogMgr = LogMgr;

	mZT1WorkerThread->Init(PCIIOPtr,mZTInputMasks,mPGExtProbePtr,mPGIntProbePtr,&mEventsRefTimer,mSDFAnalogMonitorInterface);
    mZT2WorkerThread->Init(PCIIOPtr,mZTInputMasks,&mEventsRefTimer);


    //mTKGenerator->BindPointers(mZTStationPtr->GetOutputMasks(),OutputModule);
    //mTKGenerator->BindTransportInterface(TKTransport);
    mTKGenerator = TKTransport;
}

void CZTStateMachine::BindModbusCCMgrPtr(CModbusCCMgr *ModbusCCPtr)
{
    mModbusCCMgr = ModbusCCPtr;
}

//void CZTStateMachine::BindNetworkDrivePtr(CNetDriveMgr *mNetDrivePtr)
//{
//    mNetDrivePtr = mNetDrivePtr;
//}

unsigned int CZTStateMachine::SetPGTreshold(qint32 PGTreshold)
{
	mZT1WorkerThread->SetPGTreshold(PGTreshold);
	return RET_OK;
}

void CZTStateMachine::ResetNbTriggers()
{
	mNbTriggers = 0;

	mZTPagePTr->SetZTStatus(mNbPassages,mNbTriggers,mZT1ActiveStatus,mZT2ActiveStatus);
}

void CZTStateMachine::ResetNbPassages()
{
    mNbPassages = 0;
    mZTPagePTr->SetZTStatus(mNbPassages,mNbTriggers,mZT1ActiveStatus,mZT2ActiveStatus);
}

void CZTStateMachine::ZT1StateChanged(CZT1ThreadData *ZT1DataPtr,bool ProbesOnly,int SDF)
{
	//We want to limit the sampling rate in the log file
	//BUT we want to log EVERY signal transition, except lazer probes (which sample at 1Khz).

    //JFM 2016-05-31
    //Add this to avoid having queued signals processed after the S.M. finished,
    //causing data corruption in the train file.
    if(IsZT1Active() == false)
        return;

	if(ProbesOnly == true )
	{
        //JFM 20140821
		if(mLogDelay != 0)
		{
			if(mZT1LogDelayTimer.elapsed() < mLogDelay)
			{
				delete ZT1DataPtr;
				return;
			}
		}

		mZTPagePTr->SetZT1Data(ZT1DataPtr);	//update display with the new data.
	}
	else
	{
		mZTPagePTr->SetZT1Data(ZT1DataPtr,true);	//force update display with the new data.
	}


    InsertZT1LogItem(ZT1DataPtr,SDF);

	mZT1LogDelayTimer.start();
	//qDebug("TimeStamp: %f",(float)ZT1DataPtr->mTimeStamp/1000000000);
}

void CZTStateMachine::ZT2StateChanged(CZT2ThreadData *ZT2DataPtr)
{
    mZTPagePTr->SetZT2Data(ZT2DataPtr);
    InsertZT2LogItem(ZT2DataPtr);
}

void CZTStateMachine::SetZT2Presence(bool Presence)
{
    mZT2Present = Presence;
}

void CZTStateMachine::ZT1NewDetection(CZTDetectionData *NewDetection)
{

	mZT1DetectionsLog.append(NewDetection);

	QString str("");
	if(NewDetection->mDetectionID == DETECTION_FN_DETECTION)
		str.sprintf("%s au bogie %d (rang %d ou %d)",CZTData::GetErrorString(NewDetection->mDetectionID),NewDetection->mRank,(NewDetection->mRank*2)-1,NewDetection->mRank*2);
	else
		str.sprintf("%s au rang %d",CZTData::GetErrorString(NewDetection->mDetectionID),NewDetection->mRank);
//	CZTLog::instance()->AddLogString(str,true);
    CZTLog::instance()->AddBufferString(str,true);

}

void CZTStateMachine::ZT2NewDetection(CZTDetectionData *NewDetection)
{
 //   mNbTriggers++;
    mZT2DetectionsLog.append(NewDetection);
/*    mZTPagePTr->SetZTStatus(mNbPassages,mNbTriggers,mZT1Active,mZT2Active);
    mTKGenerator->ProcessDetectionsTK(NewDetection);
	QString str("");*/
	QString str("");
    str.sprintf("%s au rang %d",CZTData::GetErrorString(NewDetection->mDetectionID),NewDetection->mRank);
//	CZTLog::instance()->AddLogString(str,true);
    CZTLog::instance()->AddBufferString(str,true);
}


unsigned int CZTStateMachine::DestroyZT1Log()
{

	for(int i = 0; i < mZT1Log.mZT1LogData.size(); i++)
	{
		delete mZT1Log.mZT1LogData.at(i);
	}
	//qDeleteAll(mZT1Log.mZT1LogData);
	mZT1Log.mZT1LogData.clear();
//	mZT1Log.mZT1LogData.squeeze();

	ClearZT1DetectionsList();
//	for(int i = 0; i < mZT1DetectionsLog.size(); i++)
//	{
//		delete mZT1DetectionsLog.at(i);
//	}
//	mZT1DetectionsLog.clear();


	mZT1Log.mZT1Flags.mExtPGOffset = 0;
	mZT1Log.mZT1Flags.mIntPGOffset = 0;
	mZT1Log.mZT1Flags.mPGCalibrationON = false;
	mZT1Log.mZT1Flags.mPGTresholdValue = 0;
	mZT1Log.mZT1Flags.mAnalogTracePresent = (mSDFAnalogMonitorInterface != 0);
    mZT1Log.mZT1Flags.mIsProblematicPassage = 0;
    mZT1Log.mZT1Flags.mTrainCompo1 = 0;
    mZT1Log.mZT1Flags.mTrainCompo2 = 0;
    mZT1Log.mZT1Flags.mTrainCompo3 = 0;
    mZT1Log.mZT1Flags.mModbusTrainType = MODBUS_CC_TRAIN_TYPE_INVALID_NOT_UPDATED;

    mZT1PEQType = PEQ1_UNKNOWN_TYPE;

	return RET_OK;
}

unsigned int CZTStateMachine::InsertZT1LogItem(CZT1ThreadData *ThreadData,int SDFReading)
{
	CZT1LogData *NewItem = new CZT1LogData(ThreadData);

	if(ThreadData == 0)	//Avoid accessing mEventsRefTimer while thread is running !
		NewItem->mTimestamp = mEventsRefTimer.nsecsElapsed();
	else
		NewItem->mTimestamp = ThreadData->mTimeStamp;

	NewItem->mDateTime = QDateTime::currentDateTime();
	NewItem->mCDVApproach_ZT1 = mZTStationPtr->IsZT1ApproachCDVOccupied();
	NewItem->mCDVARM_ZT1 = mZTStationPtr->IsZT1CDVOccupied();
	NewItem->mCIZT1 = (unsigned int)mCIZT1Active;

    NewItem->mAnalogData = SDFReading;
	mZT1Log.mZT1LogData.append(NewItem);

    return RET_OK;
}

unsigned int CZTStateMachine::InsertZT2LogItem(CZT2ThreadData *ThreadData)
{
    CZT2LogData *NewItem = new CZT2LogData(ThreadData);

    if(ThreadData == 0)	//Avoid accessing mEventsRefTimer while thread is running !
        NewItem->mTimestamp = mEventsRefTimer.nsecsElapsed();
    else
        NewItem->mTimestamp = ThreadData->mTimeStamp;

    NewItem->mDateTime = QDateTime::currentDateTime();
    NewItem->mCDVARM_ZT2 = mZTStationPtr->IsZT2CDVOccupied();
	NewItem->mCDVApproach_ZT2 = mZTStationPtr->IsZT2ApproachCDVOccupied();
    NewItem->mCIZT2 = (unsigned int)mCIZT2Active;
    mZT2Log.mZT2LogData.append(NewItem);
    return RET_OK;
}
unsigned int CZTStateMachine::DestroyZT2Log()
{
    mZT2Log.mZT2Flags.mIsProblematicPassage = 0;
    for(int i = 0; i < mZT2Log.mZT2LogData.size(); i++)
    {
        delete mZT2Log.mZT2LogData.at(i);
    }
    mZT2Log.mZT2LogData.clear();
    mZT2Log.mZT2Flags.mModbusTrainType = MODBUS_CC_TRAIN_TYPE_INVALID_NOT_UPDATED;
    mZT2Log.mZT2Flags.mTrainCompo1 = 0;
    mZT2Log.mZT2Flags.mTrainCompo2 = 0;
    mZT2Log.mZT2Flags.mTrainCompo3 = 0;

    for(int i = 0; i < mZT2DetectionsLog.size(); i++)
    {
        delete mZT2DetectionsLog.at(i);
    }
    mZT2DetectionsLog.clear();
    return RET_OK;
}

unsigned int CZTStateMachine::DestroyCalibrationData()
{
	for(unsigned int j = 0; j < ZT_SM_MAX_NB_PG_CALIB_PASSAGES; j++)
	{
		for(int i = 0; i < mZTPGCalibrationData[j].size(); i++)
		{
			delete mZTPGCalibrationData[j].at(i);
		}

		mZTPGCalibrationData[j].clear();
	}
	return RET_OK;
}

unsigned int CZTStateMachine::SaveZT1EventsLog(CZT1Log *Log)
{

    QString FileName1 = "LOGZT1_";
    FileName1 += QDateTime::currentDateTime().toString("yyyy-MM-dd-hh-mm-ss-zzz");
	QString FileName = FileName1 + ".bin";
    QString FilePath = FileName;
    FilePath.prepend("./Trains/");
	qDebug("%s",FileName.toAscii().data());

    CTrainLogFileMgr::instance()->SaveTrainLog(FilePath,Log,&mZT1DetectionsLog,mZTStationPtr->GetStationTextualName());
    mLogMgr->ParseNewLog(FilePath);


	if(mAutoExportZT1CSV)
	{
        FilePath = FileName1 + ".csv";
        FilePath.prepend("./Trains/");
        CTrainLogFileMgr::instance()->SaveCSVFile(FilePath,Log,&mZT1DetectionsLog,mZTStationPtr->GetStationTextualName());
	}

    bool Detection = !mZT1DetectionsLog.isEmpty();

    DestroyZT1Log();

    emit NewTrainLogSaved(FileName, Detection);


	return RET_OK;
}

unsigned int CZTStateMachine::SaveZT2EventsLog(CZT2Log *Log)
{
    QString FileName1 = "LOGZT2_";
    FileName1 += QDateTime::currentDateTime().toString("yyyy-MM-dd-hh-mm-ss-zzz");
    QString FileName = FileName1 + ".bin";
    QString FilePath = FileName;
    FilePath.prepend("./Trains/");
    qDebug("%s",FileName.toAscii().data());

    CTrainLogFileMgr::instance()->SaveTrainLog(FilePath,Log,&mZT2DetectionsLog,mZTStationPtr->GetStationTextualName());
    mLogMgr->ParseNewLog(FilePath);

	if(mAutoExportZT2CSV)
	{
        FilePath = FileName1 + ".csv";
        FilePath.prepend("./Trains/");
        CTrainLogFileMgr::instance()->SaveCSVFile(FilePath,Log,&mZT2DetectionsLog,mZTStationPtr->GetStationTextualName());
	}

    bool Detection = !mZT1DetectionsLog.isEmpty();

    DestroyZT2Log();

    emit NewTrainLogSaved(FileName, Detection);


    return RET_OK;
}

bool CZTStateMachine::EnterPGCalibrationMode(int NbPassages)
{
    if(IsZT1Active())
		return false;

	if(mZT1WorkerThread->SetPGCalibration(true) == false)
		return false;

	mIsPGCalibON = true;
	mCalibrationPassagesCount = 0;
	mPGNbTotalPassages = NbPassages;
	DestroyCalibrationData();
	return true;
}

bool CZTStateMachine::QuitPGCalibrationMode()
{
    if(IsZT1Active())
		return false;

    if(mZT1WorkerThread->SetPGCalibration(false) == false)
		return false;

	mIsPGCalibON = false;
	mZT1WorkerThread->SetPGCalibration(false);
	DestroyCalibrationData();

	return true;
}

void CZTStateMachine::NewPGCalibrationData(CZTPGCalibrationData *NewData)
{
//    qDebug("NewData %d | %d",NewData->mPGExtValue,NewData->mPGIntValue);
    mZTPGCalibrationData[mCalibrationPassagesCount].append(NewData);
}

unsigned int CZTStateMachine::ComputePGCalibration()
{
	//Pour déterminer le seuil de détection du pneu de guidage nous
	//appliquons la recette suivante.
	//Pour chaque pneu, on multiplie les valeurs intérieur et extérieur
	//Ensuite on fait moyenne de ce résultat pour tous les frotteurs d'un même train
	//On multiplie cette valeur par un facteur déterminé empiriquement (0.35)
	//On fait ensuite la moyenne de ce résultat pour tous les trains
	//La valeur que lon recherche correspond alors à la racine carrée de cette valeur

	double Treshold = 0.0;

	for(unsigned int i = 0; i < mPGNbTotalPassages; i++)
	{
		double Temp = 0;
		for(int sample = 0; sample < mZTPGCalibrationData[i].size(); sample++)
		{
			Temp += (mZTPGCalibrationData[i].at(sample)->mPGExtValue * mZTPGCalibrationData[i].at(sample)->mPGIntValue);	//Multiply int and ext samples
		}
		Temp /= mZTPGCalibrationData[i].size();	//Compute the mean value for this train.
		Temp *= 0.35;	//multiply by the scaling factor.

		Treshold += Temp;
	}

    Treshold /= mPGNbTotalPassages;	//Compute the mean value for all the trains.
	Treshold = sqrt(Treshold);	//Compute the square root, and we have the treshold value.
	int PGTreshold = (int)Treshold;
    qDebug("Calibration result: %d",PGTreshold);

	DestroyCalibrationData();

	emit PGCalibrationFinished(PGTreshold);

    return RET_OK;
}


void CZTStateMachine::SetLogResolution(bool HighResON)
{
    if(HighResON == true)
    {
		mLogDelay = 0;  //Log each data sample.
    }
    else
    {
        mLogDelay = ZT_SM_DEFAULT_LOG_DELAY;
    }
}

void CZTStateMachine::ZT1NewFlag(int FlagID, CZT1FlagsData *FlagData)
{
	switch(FlagID)
	{
		case ZT1_INT_PG_OFFSET_FLAG_ID:
		{
			mZT1Log.mZT1Flags.mIntPGOffset = FlagData->mIntPGOffset;
			break;
		}
		case ZT1_EXT_PG_OFFSET_FLAG_ID:
		{
			mZT1Log.mZT1Flags.mExtPGOffset = FlagData->mExtPGOffset;
			break;
		}
		case ZT1_PG_TRESHOLD_VALUE_FLAG_ID:
		{
			mZT1Log.mZT1Flags.mPGTresholdValue = FlagData->mPGTresholdValue;
			break;
		}
		case ZT1_PG_CALIB_ON_FLAG_ID:
		{
			mZT1Log.mZT1Flags.mPGCalibrationON = FlagData->mPGCalibrationON;
			break;
		}
        case ZT1_PEQ_TYPE_FLAG_ID:
        {
            mZT1PEQType = FlagData->mPEQ1Type;
            break;
        }
	}

	delete FlagData;	//We MUST delete this pointer, because the thread will not.
}


unsigned int CZTStateMachine::SendTKToPCC(int DetectionID, int Rank)
{
	CZTDetectionData *Detection = new CZTDetectionData();
	Detection->mDetectionID = DetectionID;
	Detection->mRank = Rank;

	mTKGenerator->UpdateDetectionConfig(mZTDetectionConfig);

	mTKGenerator->ProcessDetectionsTK(Detection);
	delete Detection;

	mTKGenerator->BeginTKEmission();

	return RET_OK;
}

unsigned int CZTStateMachine::EnterMaintenanceMode()
{
	if(IsZT1Active() || IsZT2Active())
	{
		return RET_ERROR;
	}

	if(mIsPGCalibON == true)
	{
		return RET_ERROR;
	}

	if(mTKGenerator->EnterMaintenance() == RET_ERROR)
	{
		return RET_ERROR;
	}

	mIsMaintenanceModeON = true;
	mZT1ActiveStatus = SB_ZT_DISABLED_STATUS;
	mZT2ActiveStatus = SB_ZT_DISABLED_STATUS;
	mZTPagePTr->SetZTStatus(mNbPassages,mNbTriggers,mZT1ActiveStatus,mZT2ActiveStatus);
	mMaintenancePPIZT1Latch = false;
	mMaintenancePPEZT1Latch = false;
	mMaintenancePPIZT2Latch = false;
	mMaintenancePPEZT2Latch = false;

	return RET_OK;
}

unsigned int CZTStateMachine::QuitMaintenanceMode()
{
	mIsMaintenanceModeON = false;
	mTKGenerator->ExitMaintenance();

	mZT1ActiveStatus = SB_ZT_INACTIVE_STATUS;
	mZT2ActiveStatus = SB_ZT_INACTIVE_STATUS;
	mZTPagePTr->SetZTStatus(mNbPassages,mNbTriggers,mZT1ActiveStatus,mZT2ActiveStatus);

	return RET_OK;
}

unsigned int CZTStateMachine::ClearMaintenanceCurrentTK()
{
    return mTKGenerator->CancelMaintenanceCurrentTK();
}


void CZTStateMachine::ClearZT1DetectionsList()
{
	for(int i = 0; i < mZT1DetectionsLog.size(); i++)
	{
		delete mZT1DetectionsLog.at(i);
	}
	mZT1DetectionsLog.clear();
}

void CZTStateMachine::AddLogStringFromThread(QString logstring)
{
    //CZTLog::instance()->AddLogString(logstring,true);
    CZTLog::instance()->AddBufferString(logstring,true);
}

void CZTStateMachine::ModbusCCANUpdate()
{
//    bool AN1State = false, AN2State = false;
//    AN1State = (mCCModbusRepo->GetSingleReg(MODBUS_CC_AN_BASE_REG_ADD) & MODBUS_CC_AN1_FLAG_MASK) != 0;
//    AN2State = (mCCModbusRepo->GetSingleReg(MODBUS_CC_AN_BASE_REG_ADD) & MODBUS_CC_AN2_FLAG_MASK) != 0;
//    mTKGenerator->SetInputStates(AN1State,mZTStationPtr->IsZT1AlarmAutoAcquireCDVOccupied(),
//                                 AN2State,mZTStationPtr->IsZT2AlarmAutoAcquireCDVOccupied());

  //  qDebug("Modbus changes: AN1 = %d, AN2 = %d",AN1State,AN2State);
}

















