//******************************************************************************
//    MARTe Library
//    $Log: ATCAadcDrv.h,v $
//    Revision 1.32  2009/12/03 14:52:13  ppcc_dev
//    Sleeps, if time is available, before start busy polling.
//    The time to sleep is given by the remaining time until the start of
//    the next pulse minus the worst jitter from a sleep and minus the
//    desired time, before the beginning of the next pulse, that we want to start
//    busy polling.
//    The worst jitter decays to zero in order to try to maintain a good performance
//
//    Revision 1.31  2009/08/07 09:31:47  aneto
//    Allow the autoSoftwareTrigger to work even if the softwareTrigger flag
//    is set to false
//
//    Revision 1.30  2009/05/21 15:18:37  ppcc_dev
//    DigIO does not have outputMap
//
//    Revision 1.29  2009/04/14 09:06:10  aneto
//    Allow the system to auto-trigger after a specified amount of time
//
//    Revision 1.28  2009/04/03 10:02:03  aneto
//    lastCycleUsecTime now is true 64 bits.
//    This uses the information from the headers to increment an internal counter
//
//    Revision 1.27  2009/04/01 15:10:36  aneto
//    Bug in the way the modulus was being calculated for the usec time. The bug was in converting from 64 to 32 bits of lastCycleUsecTime
//
//    Revision 1.26  2009/03/16 11:42:16  aneto
//    Corrected the polling mode in order to allow different acquisition frequencies
//
//    Revision 1.25  2009/03/11 12:31:54  aneto
//    Support an html output with information about the driver
//
//    Revision 1.24  2009/01/26 17:26:20  ppcc_dev
//    Small bugs solved
//
//    Revision 1.23  2009/01/26 09:20:38  aneto
//    linux support
//
//    Revision 1.22  2008/11/28 12:03:13  aneto
//    Added bufferNumber
//
//    Revision 1.21  2008/11/21 14:16:52  ppcc_dev
//    This version works with the new firmware: jet clock+trigger
//
//    Revision 1.19  2008/09/30 11:24:49  rvitelli
//    Added non-synchronous operating mode.
//
//    Revision 1.18  2008/09/22 17:20:36  fpiccolo
//    Solved minor bugs
//
//    Revision 1.17  2008/09/15 16:51:45  ppcc_dev
//    Solved few bugs
//
//    Revision 1.16  2008/09/09 09:29:15  fpiccolo
//    Modified driver structure.
//    Added SingleATCAModule class
//    Added Writing facilities
//
//    Revision 1.15  2008/08/20 16:34:36  ppcc_dev
//    Added PulseStart to reset the internal counter to 0 when using the SoftTrigger option
//
//    Revision 1.14  2008/08/15 10:41:35  fpiccolo
//    Minor stylish modifications.
//    Added TimeModule Interface
//
//    Revision 1.13  2008/08/01 14:09:28  rvitelli
//    First working version
//
//******************************************************************************

#ifndef ATCAADCDRV_H_
#define ATCAADCDRV_H_

#include "System.h"
#include "GenericAcqModule.h"
#include "FString.h"
#include "pcieAdc.h"
#include "pcieAdc_ioctl.h"
#ifdef _LINUX
#include <sys/mman.h>
#endif
#include "WebStatisticGAM.h"

class SingleATCAModule{
private:
    /** Module Identifier */
    int32        moduleIdentifier;

    /** Number of Analogue Input channels for this module (Maximum 32)*/
    int32        numberOfAnalogueInputChannels;

    /** Number of Digital Input channels for this module  (1 or 0)    */
    int32        numberOfDigitalInputChannels;
        
    /** Number of Analogue Output channels                (Maximum 8) */
    int32        numberOfAnalogueOutputChannels;

    /** Number of Digital Output channels                 ()          */
    int32        numberOfDigitalOutputChannels;

    /** Output Map. Used to map the output to a specific physical
        output channel. Channels are identified from 1 to 8.          */
    int32        outputMap[8];


    ////////////////////////////
    // Analogue Input Section //
    ////////////////////////////

    /** If true synchronize on data arrival, if false return latest completed buffer */
    bool             synchronizing;

    /** Pointers to the DMA memory allocated for data acquisition.
        The number of buffers is fixed to 4.
    */
    int32            *dmaBuffers[DMA_BUFFS];

    /** Current DMA buffer index [0-3].  */
    static int32     currentDMABufferIndex;

    /** The current master header (must be the same in all the boards) */
    static int32     currentMasterHeader;

    /** Estimated time of the next expected arrival in CPU Ticks of the acquired buffer.
        It is computed by adding a delay specified @param periodUsecSleep to
        the time of the previous completed acquisition. The system will sleep
        till this time elapses.
    */
    int64            nextExpectedAcquisitionCPUTicks;

    /** Specifies how long to sleep between acquisitions. It is specified in microseconds
        but it is internally converted in CPU ticks to avoid unecessary computations during
        realtime activities.
    */
    int64            boardInternalCycleTicks;

    /** Amount of time in microseconds after which the data stops waiting for data arrival 
        and reports an acquisition error. 
     */
    int64            dataAcquisitionUsecTimeOut;

    /** Length of a "Short Sleep" in seconds. It is used to monitor
        the data arrival on the master board and specifies a sleep time
        between checks of the data datagram arrival.
    */
    float            datagramArrivalFastMonitorSecSleep;

    /** The number of micro seconds incremented by the board in each cycle. It gives the board acquisition frequency. 
    */
    int32            boardInternalCycleTime;

    /**
     * The statistics info for these channels
     */
    StatSignalInfo  *channelStatistics;
    
    /** Find the currentDMABufferIndex and synchronize on data arrival
        
     */
    int32  CurrentBufferIndex();
    
    /** Find the latest completed buffer without synchronization*/
    int32 GetLatestBufferIndex();
    
public:

    SingleATCAModule();

    /** Initialises the SingleModule Parameter*/
    bool ObjectLoadSetup(ConfigurationDataBase &info,StreamInterface *err = NULL);

    /** Reads  NumberOfInputChannels() from the DMA Buffer.
        The first module must be the master board to assure
        correct data transfer.
    */
    bool GetData(int32 *&buffer);

    bool WriteData(const int32 *&buffer);

    /** Copies the pointers to the DMA Buffers */
#ifdef _LINUX
    bool InstallDMABuffers(int32 *mappedDmaMemoryLocation);
#else
    /** Copies the pointers to the DMA Buffers */
    bool InstallDMABuffers();
#endif
    
    /////////////////
    // Time Module //
    /////////////////

    int64             lastCycleUsecTime;

    int32             packetCounter;
    
    /** Is Master Board */
    bool             isMaster;


    /** Returns the sum of analogue and digital input channels */
    int32 NumberOfInputChannels(){
        return numberOfDigitalInputChannels + numberOfAnalogueInputChannels;
    }

    /** Returns the sum of analogue and digital output channels */
    int32 NumberOfOutputChannels(){
        return numberOfDigitalOutputChannels + numberOfAnalogueOutputChannels;
    }
    
    /** Returns the module Identifier */
    int32  BoardIdentifier(){return moduleIdentifier;}
    
    /**
     * Output an HTML table with the current value in mV of the acquired signals for this board
     */
    virtual bool ProcessHttpMessage(HttpStream &hStream);
    /**
     * Resets the statistics
     */
    bool ResetStatistics();
    /**
     * Polling method
     */
    virtual bool Poll();
    /**
     * Allow sleeping, when enough time is available, before start polling
     */
    bool allowPollSleeping;
    /**
     * Time to sleep before hard polling: pollSleepTime = CycleTime - time left to cycle time - pollSleepTimeWakeBeforeUs - worstPollSleepJitter;
     */
    float pollSleepTime;
    /**
     * Actually we want to start polling some us before reaching the cycle time
     */
    float pollSleepTimeWakeBeforeUs;
    /**
     * The worst jitter calculated in realtime of the actual time slept before polling and time meant to sleep
     */
    float worstPollSleepJitter;
    /**
     * The worst jitter will try to be recovered with a certain rate
     */
    float worstPollSleepJitterDecayRate;
};




OBJECT_DLL(ATCAadcDrv)
/** The high level driver for the ATCA ADC module */
class ATCAadcDrv:public GenericAcqModule{
private:

    /** Number of boards in the crate. Read during Initialisation */
    int32               numberOfBoards;

    /** Pointers to the ATCA modules */
    SingleATCAModule    *modules;

    /** Last cycle usec time */
    int64               lastCycleUsecTime;
    
    /** */
    bool                synchronizing;

    /** Software triggered acquisition. 
        0, which is the default value, means hardware trigger. 
    */
    int32               softwareTrigger;

	/** The master board index
     */
    int32               masterBoardIdx;

    /** If set to true the a software trigger will be sent every
     * AutoSoftwareTriggerAfterUs microseconds. For this to be 
     * true the value of AutoSoftwareTriggerAfterUs in the configuration file
     * must be > 1
     */
    bool                autoSoftwareTrigger;
    int32               autoSoftwareTriggerAfterUs;

#ifdef _LINUX
    /** Used only in Linux. The mmapped memory location.*/
    int32               *mappedDmaMemoryLocation;
    int32                mappedDmaMemorySize;
#endif
    /** The css for this page
     */
    const char *css;

public:

#ifdef _LINUX
    /**The page size*/
    static int32 pageSize;
    
    /**The file descriptor to access the driver*/
    static int32 fileDescriptor;
#endif
    
    // (De)Constructor
    ATCAadcDrv();

    virtual ~ATCAadcDrv(){
        if(modules != NULL) delete[] modules;
#ifdef _LINUX
	munmap(mappedDmaMemoryLocation, mappedDmaMemorySize);
        close(fileDescriptor);
#endif
    }
		
    // Standard GAM methods
    /* Load setup from CDB.
       This IOGAM peculiar parameters are PeriodSleep_usec and FastSleep_usec. 
       @param info: CDB from which load data
       @param err: not used 
       @returns true if all ok*/
    virtual bool ObjectLoadSetup(ConfigurationDataBase &info,StreamInterface *err);
		
    /* Print internal GAM informations 
       @param s: StreamInterface in which print infos
       @param full: not used
       @param err: not used 
       @returns true if all ok*/
    virtual bool ObjectDescription(StreamInterface &s,bool full = False, StreamInterface *err=NULL);
		
    /* Saves the data into the DDB
       @param usecTime: not used
       @param buffer: pointer to the data buffer to be filled
       @returns 1 if all ok*/
    int32 GetData(uint32 usecTime, int32 *buffer, int32 bufferNum = 0);
		
    bool WriteData(uint32 usecTime, const int32 *buffer);
		
    // Set board used as input
    virtual bool SetInputBoardInUse(bool on = False){
        if(inputBoardInUse && on){
            AssertErrorCondition(InitialisationError, "ATCAadcDrv::SetInputBoardInUse: Board %s is already in use", Name());
            return False;
        }
        inputBoardInUse  = on;
        return True;
    }

    virtual bool SetOutputBoardInUse(bool on = False){
        if(outputBoardInUse && on){
            AssertErrorCondition(InitialisationError, "ATCAadcDrv::SetOutputBoardInUse: Board %s is already in use", Name());
            return False;
        }
        outputBoardInUse = on;
        return True;
    }

    virtual bool EnableAcquisition();

    virtual bool DisableAcquisition();

    //////////////////////
    // From Time Module //
    //////////////////////
    
    // Get the Time
    int64 GetUsecTime(){return lastCycleUsecTime;}

    bool SoftwareTrigger(){    
        if(modules == NULL)return False;
#ifdef _RTAI
        SendSoftwareTrigger();
#elif defined(_LINUX)
        int ret = ioctl(fileDescriptor, PCIE_ATCA_ADC_IOCT_SEND_SOFT_TRG);
        if(ret != 0){
            AssertErrorCondition(InitialisationError,"ATCAadcDrv::PulseStart: Could send software trigger. ioctl returned : %d",ret);
            return False;
        }
#endif
        return True;
    }


    //////////////////////////////////
    // Simulation Purpose Functions //
    //////////////////////////////////

    bool PulseStart(){    
        if(modules == NULL)return False;
        if(softwareTrigger == 1){
            return SoftwareTrigger();
        }
        return True;
    }

    /**
     * Output an HTML page with the current value in mV of the acquired signals
     */
    virtual bool ProcessHttpMessage(HttpStream &hStream);
    /**
     * Polling method
     */
    virtual bool Poll();
private:

    OBJECT_DLL_STUFF(ATCAadcDrv);
};
#endif /*ATCAADCDRV_H_*/
