/* File: asynDrvBoard.hh * Author: Hovanes Egiyan, Jefferson Lab * Date: 18-Feb-2015 * * Purpose: * This module provides the driver support for the asyn device support layer * for the JLAB board EPICS interface using the driver support by * Paul Mattione. * * */ #ifndef _DRV_BOARD_HH_ #define _DRV_BOARD_HH_ /************/ /* Includes */ /************/ #include #include #include #include #include #include /* EPICS includes */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; #include "MtxLock.hh" #include "MutexedClass.hh" #include "jlabBoard.hh" #include "jlabDiscBoard.hh" #include "jlabFADC250Board.hh" #include "jlabSSPDIRCBoard.hh" #include "vmeChassis.hh" #include "asynDrvChassis.hh" /***************/ /* Definitions */ /***************/ template class boardVAsynPar; template class boardAsynPar; template class boardAsynParString; template class boardAsynParUInt32; // AsynPortDriver for Board configuration parameters // it inherits from asynPortDriver. The template is the // JLAB board class. This portDriver class is supposed to // work for VME crates of JLAB discriminators and JLAB FADC250 // boards. template class asynDrvBoard : public asynPortDriver, MutexedClass { protected: unsigned adbSlot; // Slot number for this board vector*> adbParVec; // Vector of asyn parameters bool adbExists; // Flag indicating if already exists epicsEventId adbEventID; // Event ID for waking up reading thread BoardClass* adbBoard; // Pointer to the VME board object asynDrvChassis* adbChassis; // Pointer to the chassis asyn driver to which this board belongs static unsigned adbParamNumber; // Number of parameters for this type of board public: // Name of the driver static string adbDriverName; // Name of this driver static unsigned adbMaxValueBufferLength; // Maximum length for the value buffer static unsigned adbMaxChannels; // Maximal slot number // Main Constructor to be called when creation of a driver is requested from IOC script asynDrvBoard( const char* chassisName, const char *portName, unsigned slot ); // Destructor virtual ~asynDrvBoard() ; // These are the methods we override from asynPortDriver virtual asynStatus readUInt32Digital ( asynUser* pasynUser, epicsUInt32* value, epicsUInt32 mask ) ; virtual asynStatus writeUInt32Digital( asynUser *pasynUser, epicsUInt32 value, epicsUInt32 mask ); virtual asynStatus readFloat64 (asynUser *pasynUser, epicsFloat64 *value); virtual asynStatus writeFloat64 (asynUser *pasynUser, epicsFloat64 value); // Print out the report for this AsynPortDriver virtual void report( FILE *fp, int details ); // This is the main function for reading. It is executed // in a separate thread and fills the values. It reads what is in // the shared memory using Paul's interface. virtual void readSharedMemory(); // Creates new vector of parameters for this driver virtual void createParVector(); // // Bind functions for parameters // virtual void bindParameterFunctions(); static void threadFunction( void *drvPvt ); // function to be executed in the new thread // Return driver name static string getDriverName() { MtxLock classLock(mcClassMutex); return adbDriverName; } // Return VME board object pointer virtual BoardClass* getBoard() {MtxLock objLock(mcMutex); return adbBoard;} // Return port name for this instance of the driver virtual string getPortName() { MtxLock objLock(mcMutex); return string( portName );} bool driverExists() { MtxLock objLock(mcMutex); return adbExists; } }; /*Constructor */ template asynDrvBoard::asynDrvBoard( const char* chassisName, const char* asynPortName, unsigned slot ) : asynPortDriver( asynPortName, BoardClass::GetNumberOfChannels(), adbParamNumber, asynUInt32DigitalMask | asynOctetMask | asynFloat64Mask | asynDrvUserMask , asynUInt32DigitalMask | asynOctetMask | asynFloat64Mask , ASYN_CANBLOCK | ASYN_MULTIDEVICE, 1, 0, 0), adbSlot(slot), adbParVec(), adbExists(false), adbBoard(0), adbChassis( asynDrvChassis::GetDriver4Chassis(chassisName) ) { string functionName = "asynDrvBoard::asynDrvBoard"; cout << functionName << " : Starting driver for " << asynPortName << endl; cout << "Chassis driver object pointer is " << adbChassis << endl; cout << "Max number channels is " << maxAddr << " , max number of parameters " << adbParamNumber << endl; { MtxLock objLock(mcMutex); this->adbEventID = epicsEventCreate(epicsEventEmpty); } // Uncomment this line to enable asynTraceFlow during the constructor // pasynTrace->setTraceMask( pasynUserSelf, 0x11 ); // pasynTrace->setTraceMask(pasynUserSelf, 0x255); cout << "Getting board object " << endl; if( adbChassis != 0 ) { adbBoard = adbChassis->getChassis()->GetBoard( adbSlot ); } else { // if the pointer to the crate driver object is zero then throw a runtime exception. std::stringstream errStream; errStream << "asynDrvBoard<>::asynDrvBoard: Chassis driver pointer is " << hex << showbase << adbChassis << dec << " for chassis object called " << chassisName; cerr << errStream.str() << endl; // Need to return because there seems to be a problem when throwing exception. // Apparently asynPortDriver does not like being created then destroyed. return; } if( adbBoard == 0 ) { // if the pointer to the board object is zero then throw a runtime exception. std::stringstream errStream; errStream << "asynDrvBoard<>::asynDrvBoard: Board object pointer in crate " << chassisName << " in slot " << adbSlot << " that was sent to board asyn driver is " << hex << showbase << adbBoard << dec; cerr << errStream.str() << endl; // Need to return because there seems to be a problem when throwing exception. // Apparently asynPortDriver does not like being created then destroyed. // { MtxLock objLock(mcMutex); epicsEventDestroy(this->adbEventID); } // throw std::runtime_error( errStream.str() ); return; } // Bind parameter functions using the address of this object. this->createParVector(); cout << "Will connect the parameters for ASYN port " << asynPortName << endl; unsigned nPars = 0; { MtxLock objLock( mcMutex ); nPars = adbParVec.size(); } cout << "Actual parameter vector size is " << nPars << " max size is " << adbParamNumber << endl; // Connect all parameters in the parameter vector to this driver for( unsigned ix = 0; ix < nPars; ix++ ) { boardVAsynPar* parPtr = 0; { MtxLock objLock( mcMutex ); parPtr = adbParVec[ix]; } cout << "At parameter number " << ix << endl; cout << functionName << ": Connecting parameter " << parPtr->GetName() << endl; parPtr->Connect2Driver(this); usleep(10000); cout << "Parameter " << parPtr->GetName() << " is connected " << endl; } cout << "Launching thread" << endl; int status = 0; /* Create the thread that reads the values from the shared memory in the background */ status = (asynStatus)( epicsThreadCreate(portName, epicsThreadPriorityMedium, epicsThreadGetStackSize(epicsThreadStackMedium), (EPICSTHREADFUNC) threadFunction, this) == NULL); if ( status ) { printf( "%s: epicsThreadCreate failure\n", functionName.c_str() ); stringstream ssMessage; ssMessage << "Could not start thread from " << asynDrvBoard::getDriverName() << " for asyn port " << portName << " , slot " << adbSlot ; throw std::runtime_error( ssMessage.str() ); } { MtxLock objLock( mcMutex ); adbExists = true; } return; } // Destructor template asynDrvBoard::~asynDrvBoard() { MtxLock objLock( mcMutex ); // Delete the board object if( adbBoard != 0 ) delete adbBoard; // Unlock and destroy the mutex for this class objLock.Unlock(); closeMutex(); } // Method to create the asyn parameter vector template void asynDrvBoard::createParVector() { string functionName = "asynDrvBoard::createParVector"; cout << "In function " << functionName << endl; cout << "WARNING : should not be in " << functionName << endl; // vector< boardVAsynPar* > tempVec; return; } template asynStatus asynDrvBoard::readUInt32Digital( asynUser* pasynUser, epicsUInt32* value, epicsUInt32 mask ) { string functionName = "asynDrvBoard::readUInt32Digital"; // cout << "In function " << functionName << endl; int function = pasynUser->reason; int chanNum; getAddress( pasynUser, &chanNum ); epicsUInt32 tempMask = mask; asynStatus aStat = asynError; unsigned nPars = 0; { MtxLock objLock( mcMutex ); nPars = adbParVec.size(); } // Find the parameter in the list and read the parameter value for( unsigned ix = 0; ix < nPars; ix++ ) { boardVAsynPar* parPtr = 0; { MtxLock objLock( mcMutex ); parPtr = adbParVec[ix]; } if( function == parPtr->GetNumber() ) { aStat = parPtr->Read( this, chanNum, value, &tempMask ); cout << "Read value " << value << " for parameter " << parPtr->GetName() << " for port " << portName << " , channel " << chanNum << endl; if( aStat == asynError ) { asynPrint( pasynUser, ASYN_TRACE_ERROR, "%s:%s error, status=%d address=%d, function=%d, value=%d\n", asynDrvBoard::getDriverName().c_str(), functionName.c_str(), aStat, chanNum, function, *value ); return aStat; } else { asynPrint( pasynUser, ASYN_TRACEIO_DRIVER, "%s:%s:: address=%d, function=%d, value=%d\n", asynDrvBoard::getDriverName().c_str(), functionName.c_str(), chanNum, function, *value ); callParamCallbacks( chanNum ); return aStat; } } } return aStat; } // Override the method for writing unsigned 32-bit integers template asynStatus asynDrvBoard::writeUInt32Digital( asynUser* pasynUser, epicsUInt32 value, epicsUInt32 mask ) { int command = pasynUser->reason; int channel; getAddress( pasynUser, &channel ); string functionName = "asynDrvBoard::writeUInt32Digital"; printf( "Now inside %s , command is %d, value for port %s channel %d is %d \n", functionName.c_str(), command, portName, channel, value ); boardVAsynPar* parPtr = 0; { MtxLock objLock( mcMutex ); if ( !adbExists ) return asynError; parPtr = adbParVec[command]; } asynStatus aStat = parPtr->Write( this, static_cast(channel), static_cast(&value), mask ); if ( aStat == asynSuccess ) { printf("Calling callbacks after setting parameter %d named %s for channel %d \n ", command, parPtr->GetName().c_str(), channel ); aStat = callParamCallbacks( channel ); } return aStat; } // Override method for reading doubles template asynStatus asynDrvBoard::readFloat64( asynUser* pasynUser, epicsFloat64* value ) { string functionName = "asynDrvBoard::readFloat64"; cout << "In function " << functionName << endl; int function = pasynUser->reason; int chanNum; getAddress( pasynUser, &chanNum ); asynStatus aStat = asynError; unsigned nPars = 0; { MtxLock objLock( mcMutex ); nPars = adbParVec.size(); } // Find the parameter in the list and read the parameter value for( unsigned ix = 0; ix < nPars; ix++ ) { boardVAsynPar* parPtr = 0; { MtxLock objLock( mcMutex ); parPtr = adbParVec[ix]; } if( function == parPtr->GetNumber() ) { aStat = parPtr->Read( this, chanNum, value ); cout << "Read value " << value << " for parameter " << parPtr->GetName() << " for port " << portName << " , channel " << chanNum << endl; if( aStat == asynError ) { asynPrint( pasynUser, ASYN_TRACE_ERROR, "%s:%s error, status=%d address=%d, function=%d, value=%f\n", asynDrvBoard::getDriverName().c_str(), functionName.c_str(), aStat, chanNum, function, *value ); return aStat; } else { asynPrint( pasynUser, ASYN_TRACEIO_DRIVER, "%s:%s:: address=%d, function=%d, value=%f\n", asynDrvBoard::getDriverName().c_str(), functionName.c_str(), chanNum, function, *value ); callParamCallbacks( chanNum ); return aStat; } } } return aStat; } // Override the method for writing doubles template asynStatus asynDrvBoard::writeFloat64( asynUser* pasynUser, epicsFloat64 value ) { int command = pasynUser->reason; int channel; getAddress( pasynUser, &channel ); string functionName = "asynDrvBoard::writeFloat64"; printf( "Now inside %s , command is %d, value for port %s channel %d is %f \n", functionName.c_str(), command, portName, channel, value ); boardVAsynPar* parPtr = 0; { MtxLock objLock( mcMutex ); if ( !adbExists ) return asynError; parPtr = adbParVec[command]; } asynStatus aStat = parPtr->Write( this, static_cast(channel), static_cast(&value) ); if ( aStat == asynSuccess ) { printf("Calling callbacks after setting parameter %d named %s for channel %d \n ", command, parPtr->GetName().c_str(), channel ); aStat = callParamCallbacks( channel ); } return aStat; } /* Report parameters */ template void asynDrvBoard::report( FILE *fp, int details ) { if ( details > 0 ) { // fprintf(fp, " Scratch Content = 0x%x\n", BoardSratch_ ); } // Call the base class method asynPortDriver::report( fp, details ); return; } // This function reads the shared memmory wrapper and calls the callbacks // It is supposed to run in a separate thread for each VME crate. // Because the shared memory is read here we do not need to read // in our new readUInt32Digital function, just call the base version in // case the SCAN field is set to some fixed rate scanning or for initialization. // The values read here will be moved to the appropriate records. template void asynDrvBoard::readSharedMemory() { string functionName = "asynDrvBoard::readSharedMemory"; cout << "In function " << functionName << endl; asynStatus aStat = asynError; // Set updating time to value in seconds for particular board type double updateTime = adbBoard->GetUpdateTime(); uint32_t* buffer = new uint32_t[adbMaxValueBufferLength]; // Create a buffer to hold scalars, strings, arrays. // while( taskDelay(sysClkRateGet()) ) { while( 1 ) { epicsEventId eventID; { MtxLock objLock( mcMutex ); eventID = this->adbEventID; } epicsEventWaitWithTimeout( eventID, updateTime ); // usleep( 100000 ); // if (run) epicsEventWaitWithTimeout(this->eventId, updateTime); // else epicsEventWait(this->eventId); // /* run could have changed while we were waiting */ // getIntegerParam(P_Run, &run); // if (!run) continue; // Loop over all asyn parameters and read them out for( unsigned iChan = 0; iChan < static_cast(this->maxAddr); iChan++ ) { unsigned nPars = 0; { MtxLock objLock( mcMutex ); nPars = adbParVec.size(); } nPars = adbParVec.size(); for( unsigned iPar = 0; iPar < nPars; iPar++ ) { boardVAsynPar* parPtr = 0; { MtxLock objLock( mcMutex ); parPtr = adbParVec[iPar]; } parPtr = adbParVec[iPar]; epicsUInt32 tmpMask = boardVAsynPar::GetAllBitMask(); aStat = parPtr->Read( this, iChan, buffer, &tmpMask ); // Do not do anything with the returned buffer, everything related to // assigning value to the records is taken care in the parPtr->Read function. // Just go ahead and deallocate the buffer without using it. } callParamCallbacks( iChan ); } } delete[] buffer; } /* External functions */ /* iocsh functions */ extern "C" { char* asynDrvDiscBoardConfig(const char* chassisPort, const char *portName, int slot); char* asynDrvFADCBoardConfig(const char* chassisPort, const char *portName, int slot); char* asynDrvSSPDIRCBoardConfig(const char* chassisPort, const char *portName, int slot); } #endif // _DRV_BOARD_HH_