/* * PulserPVs.cpp * * Created on: Aug 19, 2015 * Author: Hovanes Egiyan */ #include "PulserPVs.hh" using namespace std; unsigned PulserPVs::ppMaxConnectChecks = 100; // Max tries for assign/connect checks unsigned PulserPVs::ppMaxReadbackChecks = 1000; // Max tries for readback during readback checks unsigned PulserPVs::ppSleepTime = 500; // Sleep time during readback checks double PulserPVs::ppReadbackTolerance = 0.1; // Tolerance for readback during readback checks // Explicitly instantiate the template methods from clas PulserPVs template short* PulserPVs::getPtr4Type(short fakePar); template unsigned* PulserPVs::getPtr4Type(unsigned fakePar); template short PulserPVs::getPV( const std::string label, enum compType syncType, short fakePar ); template unsigned PulserPVs::getPV( const std::string label, enum compType syncType, unsigned fakePar ); template short PulserPVs::setValue( std::string pvKey, const short value, enum compType syncType ) ; template unsigned PulserPVs::setValue( std::string pvKey, const unsigned value, enum compType syncType ) ; template bool PulserPVs::gotReadback( const short setValue, const std::string readChannel, double tolerance) ; template bool PulserPVs::gotReadback( const unsigned setValue, const std::string readChannel, double tolerance) ; //! Initialize mutex for individual objects void PulserPVs::initMutex() { pthread_mutexattr_init( &ppMtxAttr ); pthread_mutexattr_setpshared( &ppMtxAttr, PTHREAD_PROCESS_PRIVATE ); pthread_mutex_init( &ppMutex, &ppMtxAttr ); return; } //! Destroy mutex for individual objects void PulserPVs::closeMutex() { pthread_mutex_destroy( &ppMutex ); pthread_mutexattr_destroy( &ppMtxAttr ); return; } // Add a PV with label pvKey and var type pvType to the bookkeeping maps. void PulserPVs::addPV( const std::string pvType, const std::string pvKey, const std::string pvName ) { MtxLock objLock( ppMutex ); cout << "Adding PV called " << pvName << " of type " << pvType << " with key " << pvKey << endl; // Check if a PV with a label (key) have already be used. // If it has, then throw an exception. Otherwise, continue executing the function. if( ppIndex.count(pvKey) > 0 || ppName.count(pvKey) > 0 || ppType.count(pvKey) > 0 ) { // throw an exception stringstream errStream; errStream << "PulserPVs::addPV : PV with label " << pvKey << " has already been defined" ; throw std::runtime_error( errStream.str()); } // Assign all the maps except the ppIndex map which is defined at the // connection time. if( ppCurrentTypeIndex.count( pvType) > 0 ) { ppTypeIndex[pvKey] = ppCurrentTypeIndex[pvType]; ppName[pvKey] = pvName; ppType[pvKey] = pvType; ppCurrentIndex++; ppCurrentTypeIndex[pvType] += 1; } else { // throw an exception if pvType has not been defined a a key on the pvType map. stringstream errStream; errStream << "PulserPVs::addPV : unknown PV type " << pvType ; throw std::runtime_error( errStream.str() ); } return; } // Return the pointer to the PV arrray based on the argument type template T* PulserPVs::getPtr4Type(T fakePar) { int status; void* arrayPtr; std::string demangledTypeName = abi::__cxa_demangle(typeid(fakePar).name(),0,0,&status); if( demangledTypeName == "short" ) { arrayPtr = ppArrays.getShortPtr(); } else if( demangledTypeName == "unsigned int" ) { arrayPtr = ppArrays.getUnsignedPtr(); } else if( demangledTypeName == "double" ) { arrayPtr = ppArrays.getDoublePtr(); } else { // throw an exception std::stringstream errStream; errStream << "PulserPVs::getPtr4Type : Unknown type " << demangledTypeName << " for PV" ; throw std::runtime_error( errStream.str() ); } return static_cast( arrayPtr ); } // Return the value of short PV with label label template T PulserPVs::getPV( const std::string label, enum compType syncType, T fakePar ) { if( ppIndex.count( label ) > 0 ) { T* arrayPtr = getPtr4Type( fakePar ); return arrayPtr[ ppTypeIndex[label] ]; } else { // throw an exception std::stringstream errStream; errStream << "PulserPVs::getPV : PV with label " << label << " does not exists" ; throw std::runtime_error( errStream.str() ); } } // Set the value of the EPICS PV of type short given by the label (key) pvKey to the value value. template T PulserPVs::setValue( std::string pvKey, const T value, enum compType syncType ) { MtxLock objLock( ppMutex ); cout << "setValue receive value of " << value << endl; if( ppIndex.count(pvKey) > 0 ) { T* arrayPtr = getPtr4Type( T() ); arrayPtr[ ppTypeIndex[pvKey] ] = value; cout << "Will write for pvIndex " << ppIndex[pvKey] << endl; seq_pvPut( ppSSid, static_cast( ppIndex[pvKey] ), syncType ); } else { // throw an exception stringstream errStream; errStream << "PulserPVs::setValue : PV with label " << pvKey << " does not exists" ; throw std::runtime_error( errStream.str() ); } return value; } // Method that waits until the setpoint and // readback are the same within tolerance. If at the end the // matching readback is not achieved, returns false. Otherwise, // returns true. template bool PulserPVs::gotReadback( const T setValue, const std::string readChannel, double tolerance) { bool pvIsNotSet = true; unsigned trialNumber = 0; // Loop until the pv is connected of the number of trials is exhausted while( pvIsNotSet and (trialNumber < ppMaxReadbackChecks) ) { usleep(ppSleepTime); double valueDifference = getPV( readChannel, SYNC, T() ) - setValue; pvIsNotSet = (fabs( valueDifference ) > tolerance) ? true : false; trialNumber++; } if( trialNumber >= ppMaxReadbackChecks ) return false; else return true; } // Assign all PVs in this object using state sequence ID given by ssID. // Also set monitor for all those PVs. void PulserPVs::assignPVs() { MtxLock objLock( ppMutex ); // Get number if elements in the PV arrays. All arrays // are expected to have the smae length. unsigned arrayLength = ppArrays.getNElm(); // Get the offset of the first array, which is expected to be the // first array defined as a PV for C++ interface. The other arrays // are expected to follow the short array in the list of the PVs, // in other words not other PV should have been defined inbetween the // arrays for C++ interface. unsigned arrayOffset = ppArrays.getOffset(); cout << "Length of Arrays is " << arrayLength << " , offset is " << arrayOffset << endl; // Define the offsets in the pvIndex array for our short, unsigned and double arrays. // It consists of an overall offset plus offsets by the number of elements in each // arrays of given type. map offsetMap; offsetMap["short"] = arrayOffset + 0*arrayLength; offsetMap["unsigned"] = arrayOffset + 1*arrayLength; offsetMap["double"] = arrayOffset + 2*arrayLength; // Loop through all defined PVs and assign them, and then set monitor for( std::map::iterator it = ppTypeIndex.begin(); it != ppTypeIndex.end(); it++ ) { std::string pvLabel = it->first; // Get the key // Only connect PVs that have not been connected, do not reassign PVs if (ppIndex.count(pvLabel) == 0) { // This is the index for pvLabel variable that can be used to // find the value in the PV array of the given type . unsigned indexForType = ppTypeIndex[pvLabel]; // ppIndex for each PV that gets send to seq_ functions gets assigned the index value // for the within the array of its type plus an offset from zero for the array of that type. ppIndex[pvLabel] = offsetMap[ppType[pvLabel]] + indexForType; const std::string pvName = ppName[pvLabel]; // Get the PV name that needs to be assigned cout << "Assigning pvIndex " << ppIndex[pvLabel] << " to " << pvName << endl; bool pvIsDisconnected = true; unsigned trialCount = 0; // Loop until the PV is connected ot the number of trials is exhausted. while ( pvIsDisconnected && trialCount < 100 ) { seq_pvAssign(ppSSid, ppIndex[pvLabel], pvName.c_str()); usleep(ppSleepTime); short pvIsAssigned = seq_pvAssigned( ppSSid, ppIndex[pvLabel] ); short pvIsConnected = seq_pvConnected( ppSSid, ppIndex[pvLabel]) ; pvIsDisconnected = (!pvIsAssigned) || (!pvIsConnected); // cout << "Trial # " << trialCount << " , pvIsAssigned is " << pvIsAssigned << " , pvIsConnected is " << pvIsConnected << // " , pvIsDisconnected is " << pvIsDisconnected << endl; trialCount++; } // cout << "Trial count number is " << trialCount << endl; // If there is no connection after 100 attempts throw an exception if( trialCount >= ppMaxConnectChecks ) { std::stringstream errStream; errStream << "PulserPVs::assignPVs : PV with label " << pvLabel << " could not connect to " << pvName; throw std::runtime_error( errStream.str() ); } // We need to monitor all PVs to be able to access the values from the pointers // without doing pvGets. seq_pvMonitor(ppSSid, ppIndex[pvLabel]); usleep(50); // Sleeping after setting monitor seems to help, although may not be needed. } } return; } // Check if any of the PVs have changed bool PulserPVs::inputsChanged() { // cout << "Checking the inputs" << endl; bool changeTest = false; for( map::iterator it = ppType.begin(); it != ppType.end(); it++ ) { std::string pvLabel = it->first; std::string pvType = it->second; EV_ID pvFlag = 0; if( pvType == "short" ) pvFlag = ppArrays.getShortFlag(); else if( pvType == "unsigned") pvFlag = ppArrays.getUnsignedFlag(); else if( pvType == "double") pvFlag = ppArrays.getDoubleFlag(); // cout << "SSID is " << hex << ppSSid << " , pvType is " << pvType << " , pvFlag is " << pvFlag << endl; if( seq_efTestAndClear(ppSSid, pvFlag) > 0 ) changeTest |= true; } return changeTest; }