/* File: asynDrvChassis.hh * Author: Hovanes Egiyan, Jefferson Lab * Date: 03-Feb-2015 * * Purpose: * This module provides the driver support for the asyn device support layer * for the JLAB chassis EPICS interface using the driver support by * Paul Mattione. * * */ #ifndef _DRV_CHASSIS_HH_ #define _DRV_CHASSIS_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 using namespace std; #include "MtxLock.hh" #include "jlabDiscBoard.hh" #include "jlabFADC250Board.hh" #include "jlabSSPDIRCBoard.hh" #include "vmeChassis.hh" #include "MutexedClass.hh" /***************/ /* Definitions */ /***************/ template class chassisVAsynPar; template class chassisAsynPar; template class chassisAsynParString; template class chassisAsynParUInt32; // C-function with global scope that calls the member function //template //static void readSharedMemory( void *drvPvt ); // // AsynPortDriver for Chassis 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 asynDrvChassis : public asynPortDriver, MutexedClass { protected: vector*> adcParVec; // Vector of asyn parameters bool adcExists; // Flag indicating if already exists epicsEventId adcEventID; // Event ID for waking up reading thread vmeChassis* adcChassis; // Pointer to the VME chassis object static map adcChassisMap; // Map of all asyn port drivers for chassis public: // Name of the driver static string adcDriverName; static unsigned adcMaxValueBufferLength; // Main Constructor to be called when creation of a driver is requested from IOC script asynDrvChassis( const char *portName, vector*>& parVec ); // Destructor ~asynDrvChassis() ; // 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 readOctet( asynUser* pasynUser, char* value, size_t maxChars, size_t* nActual, int* eomReason ); // virtual asynStatus readInt32 ( asynUser* pasynUser, epicsInt32* value ) ; // virtual asynStatus writeInt32( asynUser *pasynUser, epicsInt32 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. void readSharedMemory(); // Creates new vector of parameters for this driver static vector*> createParVector(); static void threadFunction( void *drvPvt ); // Return driver name inline static string getDriverName() { MtxLock classLock(mcClassMutex); return adcDriverName; } // Return VME chassis object pointer inline vmeChassis* getChassis() {MtxLock objLock(mcMutex); return adcChassis;} // Return the pointer to the asyn driver for the chassis inline static asynDrvChassis* GetDriver4Chassis( const char* portName ) { MtxLock classLock(mcClassMutex); return adcChassisMap[portName];} // Return port name for this instance of the driver inline string getPortName() { MtxLock objLock(mcMutex); return string( portName );} }; /*Constructor */ template asynDrvChassis::asynDrvChassis( const char* asynPortName, vector*>& parVec ) : asynPortDriver( asynPortName, 1, parVec.size(), asynUInt32DigitalMask | asynOctetMask | asynDrvUserMask , asynUInt32DigitalMask | asynOctetMask , ASYN_CANBLOCK | ASYN_MULTIDEVICE, 1, 0, 0 ), adcParVec(parVec), adcExists(false), adcChassis(0) { cout << "Starting driver for " << asynPortName << " with " << this->maxAddr << " channel and " << parVec.size() << " parameters " << endl; string functionName = "asynDrvChassis::asynDrvChassis"; // Check if such a port for chassis exists. If it does, throw an exception if( adcChassisMap.count( asynPortName ) > 0 ) { stringstream ssMessage; ssMessage << functionName << " : asyn port called " << asynPortName << portName << " already exists"; throw std::runtime_error( ssMessage.str() ); } { MtxLock objLock(mcMutex); this->adcEventID = epicsEventCreate(epicsEventEmpty); } // Uncomment this line to enable asynTraceFlow during the constructor // pasynTrace->setTraceMask( pasynUserSelf, 0x11 ); pasynTrace->setTraceMask(pasynUserSelf, 0x255); cout << "Will connect the parameters for ASYN port " << asynPortName << endl; unsigned nPars = 0; { MtxLock objLock( mcMutex ); nPars = adcParVec.size(); } cout << "Parameter vector size is " << nPars << endl; // Connect all parameters in the parameter vector to this driver for( unsigned ix = 0; ix < nPars; ix++ ) { chassisVAsynPar* parPtr = 0; { MtxLock objLock( mcMutex ); parPtr = adcParVec[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 << "Creating chassis object " << endl; // Create vmeChassis object. try { adcChassis = new vmeChassis(); } catch (const runtime_error & e) { string errMsg = e.what(); cerr << errMsg << endl; throw e; } // If pointer is zero throw an exception if( adcChassis == 0 ) { stringstream ssMessage; ssMessage << "ERROR: Cannot create vmeChassis object for port " << portName ; throw std::runtime_error( ssMessage.str() ); } // Add this pointer to the map of chassis {MtxLock classLock( mcClassMutex ); adcChassisMap[portName] = this;} 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 " << asynDrvChassis::getDriverName() << " for asyn port " << portName ; throw std::runtime_error( ssMessage.str() ); } { MtxLock objLock( mcMutex ); adcExists = true; } return; } // Destructor template asynDrvChassis::~asynDrvChassis() { MtxLock objLock( mcMutex ); // Delete the vmeChassis object if( adcChassis != 0 ) delete adcChassis; // Unlock and destroy the mutex for this class objLock.Unlock(); // Remove this driver from the map of drivers MtxLock classLock( mcClassMutex); if( adcChassisMap[portName] > 0 ) adcChassisMap.erase( portName ); classLock.Unlock(); // Destroy objects mutex closeMutex(); } // Method to create the asyn parameter vector template vector*> asynDrvChassis::createParVector() { string functionName = "asynDrvChassis::createParVector"; cout << "In function " << functionName << endl; typedef typename chassisVAsynPar::cvapRdPtr readFun; typedef typename chassisVAsynPar::cvapWtPtr writeFun; vector< chassisVAsynPar* > tempVec; tempVec.push_back( new chassisAsynParString ("IP_ADDRESS" , tempVec.size() ,(readFun) &vmeChassis::GetIP ,(writeFun)0 ,true)); tempVec.push_back( new chassisAsynParUInt32 ("N_BOARDS" , tempVec.size() ,(readFun) &vmeChassis::GetNumberOfBoards ,(writeFun)0 ,true)); return tempVec; } template asynStatus asynDrvChassis::readUInt32Digital( asynUser* pasynUser, epicsUInt32* value, epicsUInt32 mask ) { string functionName = "asynDrvChassis::readUInt32Digital"; // cout << "In function " << functionName << endl; int function = pasynUser->reason; epicsUInt32 tempMask = mask; asynStatus aStat = asynError; unsigned nPars = 0; { MtxLock objLock( mcMutex ); nPars = adcParVec.size(); } // Find the parameter in the list and read the parameter value for( unsigned ix = 0; ix < nPars; ix++ ) { chassisVAsynPar* parPtr = 0; { MtxLock objLock( mcMutex ); parPtr = adcParVec[ix]; } if( function == parPtr->GetNumber() ) { aStat = parPtr->Read( this, value, &tempMask ); cout << "Read value " << value << " for parameter " << parPtr->GetName() << " for port " << portName << endl; if( aStat == asynError ) { asynPrint( pasynUser, ASYN_TRACE_ERROR, "%s:%s error, status=%d, function=%d, value=%d\n", asynDrvChassis::getDriverName().c_str(), functionName.c_str(), aStat, function, *value ); return aStat; } else { asynPrint( pasynUser, ASYN_TRACEIO_DRIVER, "%s:%s:: function=%d, value=%d\n", asynDrvChassis::getDriverName().c_str(), functionName.c_str(), function, *value ); callParamCallbacks( ); return aStat; } } } return aStat; } // Override the method for writing unsigned 32-bit integers template asynStatus asynDrvChassis::writeUInt32Digital( asynUser* pasynUser, epicsUInt32 value, epicsUInt32 mask ) { int command = pasynUser->reason; string functionName = "asynDrvChassis::writeUInt32Digital"; printf( "Now inside %s , command is %d, value is %d \n", functionName.c_str(), command, value ); chassisVAsynPar* parPtr = 0; { MtxLock objLock( mcMutex ); if ( !adcExists ) return asynError; parPtr = adcParVec[command]; } asynStatus aStat = parPtr->Write( this, static_cast(&value), mask ); if ( aStat == asynSuccess ) { printf("Calling callbacks after setting parameter %d named %s \n ", command, parPtr->GetName().c_str() ); aStat = callParamCallbacks(); } return aStat; } template asynStatus asynDrvChassis::readOctet( asynUser* pasynUser, char* value, size_t maxChars, size_t* nActual, int* eomReason ) { string functionName = "asynDrvChassis::readOctet"; // cout << "In function " << functionName << endl; int function = pasynUser->reason; asynStatus aStat = asynError; unsigned nPars = 0; { MtxLock objLock( mcMutex ); nPars = adcParVec.size(); } // Find the parameter in the list and read the parameter value for( unsigned ix = 0; ix < nPars; ix++ ) { chassisVAsynPar* parPtr = 0; { MtxLock objLock( mcMutex ); parPtr = adcParVec[ix]; } if( function == parPtr->GetNumber() ) { string tmpString; aStat = parPtr->Read( this, &tmpString ); strcpy( value, tmpString.c_str() ); *nActual = tmpString.size(); // cout << "Read value " << value << " for parameter " << parPtr->GetName() << " for port " << portName << endl; if( aStat == asynError ) { asynPrint( pasynUser, ASYN_TRACE_ERROR, "%s:%s error, status=%d, function=%d, value=%s\n", asynDrvChassis::getDriverName().c_str(), functionName.c_str(), aStat, function, value ); return aStat; } else { asynPrint( pasynUser, ASYN_TRACEIO_DRIVER, "%s:%s:: function=%d, value=%s\n", asynDrvChassis::getDriverName().c_str(), functionName.c_str(), function, value ); callParamCallbacks(); return aStat; } } } return aStat; } /* Report parameters */ template void asynDrvChassis::report( FILE *fp, int details ) { if ( details > 0 ) { // fprintf(fp, " Scratch Content = 0x%x\n", ChassisSratch_ ); } // Call the base class method asynPortDriver::report( fp, details ); return; } // This function reads the shard 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 asynDrvChassis::readSharedMemory() { string functionName = "asynDrvChassis::readSharedMemory"; cout << "In function " << functionName << endl; asynStatus aStat = asynError; // Set updating time to value in seconds double updateTime = 1.0; // while( taskDelay(sysClkRateGet()) ) { while( 1 ) { epicsEventId eventID; { MtxLock objLock( mcMutex ); eventID = this->adcEventID; } 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 = adcParVec.size(); } for( unsigned iPar = 0; iPar < nPars; iPar++ ) { chassisVAsynPar* parPtr = 0; { MtxLock objLock( mcMutex ); parPtr = adcParVec[iPar]; } epicsUInt32 tmpMask = chassisVAsynPar::GetAllBitMask(); uint32_t* buffer = new uint32_t[adcMaxValueBufferLength]; // Create a buffer to hold scalars, strings, arrays. aStat = parPtr->Read( this, 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. delete[] buffer; } // This will work because there are no channels for chassis (iPar = iChan is probably fine) callParamCallbacks( iChan ); } } } /* External functions */ /* iocsh functions */ extern "C" { char* asynDrvChassisConfig( const char *portName ); } #endif // _DRV_CHASSIS_HH_