/*******************************************************************************
*                                                                              *
* Société de Transports de Montréal.                                    	   *
* 2012 - 2013                                                                        *
*                                                                              *
* Projet Zones Tests                                                           *
*                                                                              *
*                                                                              *
*                                                                              *
*******************************************************************************/
/*
	Description:
	Cette classe constitue un thread qui exécute la lecture des modules
	d'I/O externes via le port Ethernet.

*/

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

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

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

#include "ExtIOThread.h"

CExtIOThread::CExtIOThread() : QObject()
{
	mInputModule = 0;
	mOutputModule = 0;
	mAnalogInputModule = 0;

	mCurInputs = 0xFFFFFFFF;
	mCurAnalogInput = 0;
	mOutputDirty = false;
	mRun = true;
	mIsRunning = false;

	mCurAnalogChannel = 0;
	mAnalogModulePresent = false;
}

unsigned int CExtIOThread::BindPointers(CInputModule *InputModule, COutputModule *OutputModule, CAnalogInputModule *AnalogModule, int AnalogChannel)
{
	mInputModule = InputModule;
	mOutputModule = OutputModule;
	mAnalogInputModule = AnalogModule;
	mCurAnalogChannel = AnalogChannel;

	if(mAnalogInputModule != 0)
	{
		mAnalogModulePresent = true;
	}

	return RET_OK;
}

unsigned int CExtIOThread::GetInputs()
{
	unsigned int inputs;
	mMutex.lock();
	inputs = mCurInputs;
	mMutex.unlock();

	return inputs;
}

unsigned int CExtIOThread::GetInputBuf()
{
	return GetInputs();
}

unsigned int CExtIOThread::SetOutput(quint32 buffer)
{
    COutputTransaction NewTransaction;
    NewTransaction.mOutputsWord = buffer;
    NewTransaction.mOutputTransaction = OUTPUT_MODULE_SET_OUTPUT_WORD;
    mMutex.lock();
    mOutputTransactionList.append(NewTransaction);
    mMutex.unlock();

	return RET_OK;
}

unsigned int CExtIOThread::SetOutput(unsigned char *buffer)
{
    COutputTransaction NewTransaction;
    memcpy(&NewTransaction.mOutputsBuf,buffer,4);
    NewTransaction.mOutputTransaction = OUTPUT_MODULE_SET_OUTPUT_BUF;
    mMutex.lock();
    mOutputTransactionList.append(NewTransaction);
    mMutex.unlock();

	return RET_OK;
}

unsigned int CExtIOThread::SetOutputFlags(quint32 Flags)
{
    COutputTransaction NewTransaction;
    NewTransaction.mOutputsWord = Flags;
    NewTransaction.mOutputTransaction = OUTPUT_MODULE_SET_OUTPUT_FLAGS;
    mMutex.lock();
    mOutputTransactionList.append(NewTransaction);
    mMutex.unlock();

	return RET_OK;
}

unsigned int CExtIOThread::ClearOutputFlags(quint32 Flags)
{
    COutputTransaction NewTransaction;
    NewTransaction.mOutputsWord = Flags;
    NewTransaction.mOutputTransaction = OUTPUT_MODULE_CLEAR_OUTPUT_FLAGS;
    mMutex.lock();
    mOutputTransactionList.append(NewTransaction);
    mMutex.unlock();

	return RET_OK;
}

unsigned int CExtIOThread::ToggleOutputFlags(quint32 Flags)
{
    COutputTransaction NewTransaction;
    NewTransaction.mOutputsWord = Flags;
    NewTransaction.mOutputTransaction = OUTPUT_MODULE_TOGGLE_OUTPUT_FLAGS;
    mMutex.lock();
    mOutputTransactionList.append(NewTransaction);
    mMutex.unlock();

	return RET_OK;
}

///ATTENTION !!!!!!!!!!!!!!!!! ////
//Le paramètre Channel n'est pas géré en tant que tel
//lors de l'écriture de la fonction, un seul channel (SDF) est utilisé.
//Si d'autres entrées analogiques sont ajoutées, il faudra gérer les
//différents channels.
unsigned int CExtIOThread::GetAnalogInput(int Channel, int *data)
{
	Q_UNUSED(Channel)

	mMutex.lock();
	*data = mCurAnalogInput;
	mMutex.unlock();

	return RET_OK;
}

unsigned int CExtIOThread::GetAnalogInput(int Channel, double &data)
{
	Q_UNUSED(Channel)

	mMutex.lock();
	data = (double)mCurAnalogInput;
	mMutex.unlock();

	return RET_OK;
}


unsigned int CExtIOThread::StopAcquisition()
{
	mMutex.lock();
	mRun = false;
	mMutex.unlock();

	return RET_OK;
}

void CExtIOThread::DoAcquisition()
{
	bool Run = true;
	mDigitalInputsPeriodTimer.start();
	mAnalogInputsTimer.start();

#ifdef OUTPUT_EXTIO_SAMPLE_RATE
	mSampleRateTimer.start();
#endif

	mMutex.lock();
	mIsRunning = true;
	mMutex.unlock();

	while(Run == true)
	{
        //JFM 20140821
		//			Read analog inputs
		if(mAnalogInputModule != 0)
        {
            if(mAnalogInputsTimer.nsecsElapsed() >= EXT_ANALOG_INPUT_ACQUISITION_PERIOD)	//help relax the communication with the modules
            {
                int AnalogInput;
				mAnalogInputModule->GetAnalogInput(mCurAnalogChannel,&AnalogInput);

                mMutex.lock();
                mCurAnalogInput = AnalogInput;
                mMutex.unlock();
                mAnalogInputsTimer.restart();
            }
        }


		//Read discreete inputs
		if(mDigitalInputsPeriodTimer.elapsed() >= EXT_DIGITAL_INPUTS_ACQUISITION_PERIOD)
		{
			unsigned int Input = mInputModule->GetInputBuf();
			mMutex.lock();
			mCurInputs = Input;
			mMutex.unlock();
			mDigitalInputsPeriodTimer.restart();
		}


		//Refresh outputs if necessary
        bool OutputDirty = false;
        COutputTransaction OutputTransaction;
		mMutex.lock();
        if(mOutputTransactionList.size() != 0)
        {
            OutputDirty = true;
            OutputTransaction = mOutputTransactionList.takeFirst();
        }
		mMutex.unlock();

		if(OutputDirty == true)
		{
            switch(OutputTransaction.mOutputTransaction)
			{
				case OUTPUT_MODULE_SET_OUTPUT_BUF:
				{
                    mOutputModule->SetOutput(OutputTransaction.mOutputsBuf);
					break;
				}
				case OUTPUT_MODULE_SET_OUTPUT_WORD:
				{
                    mOutputModule->SetOutput(OutputTransaction.mOutputsWord);
					break;
				}
				case OUTPUT_MODULE_SET_OUTPUT_FLAGS:
				{
                    mOutputModule->SetOutputFlags(OutputTransaction.mOutputsWord);
					break;
				}
				case OUTPUT_MODULE_CLEAR_OUTPUT_FLAGS:
				{
                    mOutputModule->ClearOutputFlags(OutputTransaction.mOutputsWord);
					break;
				}
				case OUTPUT_MODULE_TOGGLE_OUTPUT_FLAGS:
				{
                    mOutputModule->ToggleOutputFlags(OutputTransaction.mOutputsWord);
					break;
				}
			}
		}


		mMutex.lock();
		Run = mRun;
		mMutex.unlock();

#ifdef OUTPUT_EXTIO_SAMPLE_RATE
		mMutex.lock();
		mDelay = mSampleRateTimer.elapsed();
		mMutex.unlock();
		mSampleRateTimer.restart();

#endif
	}

	mMutex.lock();
	mIsRunning = false;
	mMutex.unlock();
	//qDebug("Ext IO Thread exit");

}

bool CExtIOThread::IsAnalogModueDetected()
{
	return mAnalogModulePresent;
}

bool CExtIOThread::IsThreadRunning()
{
	bool Running;
	mMutex.lock();
	Running = mIsRunning;
	mMutex.unlock();

	return Running;
}

#ifdef OUTPUT_EXTIO_SAMPLE_RATE
qint64 CExtIOThread::GetSampleRate()
{
	qint64 temp;
	mMutex.lock();
	temp = mDelay;
	mMutex.unlock();
	return temp;
//	return mSampleRateTimer.nsecsElapsed();
}

#endif

COutputTransaction &COutputTransaction::operator =(const COutputTransaction& a)
{
    mOutputsBuf[0] = a.mOutputsBuf[0];
    mOutputsBuf[1] = a.mOutputsBuf[1];
    mOutputsBuf[2] = a.mOutputsBuf[2];
    mOutputsBuf[3] = a.mOutputsBuf[3];

    mOutputTransaction = a.mOutputTransaction;

    mOutputsWord = a.mOutputsWord;

    return *this;
}
