/* * ProfilerPlane.hh * * Templated class for profiler plane object for x- and y-plane. * This class inherits from the virtual blase class ProfilerPlaneV * and adds methods that require knowing the string that defines which * plane it is. The template argument is of type "char* const P" because * current version of C++ compiler does not allow for template arguments * of type string. * * Created on: Sep 29, 2015 * Author: Hovanes Egiyan */ #ifndef PROFILERPLANE_HH_ #define PROFILERPLANE_HH_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "MutexedClass.hh" #include "ProfilerPlaneData.hh" #include "ProfilerDetComponent.hh" #include "ProfilerPlaneDataFitter.hh" #include "ProfilerPlaneV.hh" using namespace std; template class ProfilerPlane: public ProfilerPlaneV { public: // Main constructor ProfilerPlane( ProfilerDetector* det, const double centerPos, const double fiberWidth, const bool reversedAxis = false ) : MutexedClass(), ProfilerPlaneData( reversedAxis ), ProfilerPlaneV( det, reversedAxis ) { cout << " Entering good " << __FUNCTION__ << endl; ppdCenterPosition = centerPos; ppdFiberWidth = fiberWidth; ppdName = getDetectorName() + ":" + P ; // Define the x-axis this->defineAxis(); cout << " Exiting good " << __FUNCTION__ << " with name " << getName() << endl; return; } // Copy constructor ProfilerPlane( const ProfilerPlane& p ) : MutexedClass( p ), ProfilerPlaneData( p ), ProfilerPlaneV( p ) { return; } // Destructor virtual ~ProfilerPlane() { return; } // Assignment operator virtual ProfilerPlane& operator=( const ProfilerPlane& p ) { ProfilerPlaneV::operator =( p ); return *this; } virtual void createSlices() { ProfilerPlaneDataFitter::createSlices(); } virtual void startMainThread(); virtual void notifyInputFlagWatchers(); virtual void transferFIFO(); virtual void eraseAndStartScalers(); virtual void waitForAcquisition(); virtual void waitForBusy(); static void* threadFunc( void* argPtr ); }; // Method to launch the main thread that will monitor for input changes // and trigger analysis of the scaler data template void ProfilerPlane

::startMainThread() { MtxLock objMutex( mcMutex ); pthread_t thID; int thStat = pthread_create( &thID, NULL, (void* (*)(void*))ProfilerPlane

::threadFunc, (void*) this ); // If fails to create the threat throw and exception if ( thStat != 0 ) { std::stringstream errStream; errStream << "ProfilerPlane<" << P << ">::startMainThread: Failed to create thread " << hex << showbase << thID << dec << " for profiler plane " << getName(); throw std::runtime_error( errStream.str() ); } return; } // Read the scalers, correct for efficiency and transfer the scaler rates into the objects vectors // for instantaneous, integrated and accumulated values. template void ProfilerPlane

::transferFIFO() { // here we want to lock the mutex for the ProfilerPlaneData since we // are changing its members. MtxLock objLock( mcMutex ); // Determine the efficiency correction calibrated values from EPICS if not simulation std::string effcLabel = std::string( "effc_" ) + P ; std::string disableLabel = std::string( "disable_" ) + P ; if( this->isSimulation() ) { // For simulation only use 100% efficiency ppdEffcCorr.assign( ppdEffcCorr.size(), 1.0 ); ppdDisableFlags.assign( ppdDisableFlags.size(), false ); } else { // Calculate the efficiency constants from the EPICS record showing the rates that were generated from // a uniform exposure of all fibers. vector flatRate( ppdEffcCorr.size() ); pdcPV->getPV( effcLabel, SYNC, flatRate ); double flatRateAvg = std::accumulate( flatRate.begin(), flatRate.end(), 0.0 ) / double( flatRate.size() ); for ( unsigned iFibr = 1; iFibr <= ppdValues.size(); iFibr++ ) { if( flatRate[iFibr-1] > 1. ) ppdEffcCorr[iFibr-1] = flatRateAvg / flatRate[iFibr-1]; if( 20.0 < ppdEffcCorr[iFibr-1] || ppdEffcCorr[iFibr-1] < 0.05 ) ppdEffcCorr[iFibr-1] = 1.0 ; } // Read the disable flags records from EPICS into a short array then assign to boolean member array. vector diableFlagShort( ppdDisableFlags.size() ); pdcPV->getPV( disableLabel, SYNC, diableFlagShort ); copy( diableFlagShort.begin(), diableFlagShort.end(), ppdDisableFlags.begin() ); } // cout << "Plane " << std::string(P) << " will use corrections from " << effcLabel << endl; // for ( unsigned iFibr = 1; iFibr <= ppdValues.size(); iFibr++ ) { // cout << " " << ppdEffcCorr[iFibr-1]; // } // cout << endl; // cout << "Size for " << std::string(P) << " vector is " << ppdValues.size() << endl; // Get the dwell-time setting for this plane from EPICS. std::string dwellLabel = std::string( "dwell_" ) + P ; double dwellTime = pdcPV->getPV( dwellLabel, SYNC, double(0) ); ppdTotalRate = 0; // Loop over fibers and read the arrays into the vector for the plane std::string pvLabel; for ( unsigned iFibr = 1; iFibr <= ppdValues.size(); iFibr++ ) { stringstream ssFibr; ssFibr << iFibr; std::string sFibr = ssFibr.str(); if ( this->isSimulation() ) { // Will read from the simulation waveform pvLabel = std::string( "sim_" ) + P + std::string( ":" ) + sFibr; } else { // Will read from the real data in the MCA records pvLabel = std::string( "mca_" ) + P + std::string( ":" ) + sFibr; } // cout << "Will use record fopr label " << pvLabel << endl; // cout << "Size for X["<< iFibr-1 <<"] vector is " << ppdValues[iFibr-1].size() << endl; // Fill the vector from the content of the MCA record pdcPV->getPV( pvLabel, SYNC, ppdValues[iFibr - 1] ); // cout << "Fiber #" << iFibr << " for plane " << getName() << " is " << endl; ppdSummedValues[iFibr - 1] = 0; // Need to zero non-accumulating summed values. // Now add the new values to the accumulated values by looping over all FIFO elements for ( unsigned iFIFO = 0; iFIFO < ppdAccumValues[iFibr - 1].size(); iFIFO++ ) { // If fiber is disabled in EPICS disable waveform then set the count for this // fiber to zero for all FIFO elements. if( fiberIsDisabled(iFibr-1) ) ppdValues[iFibr - 1][iFIFO] = 0; // Correct for the efficiency if the efficiency value is not zero if( ( ppdEffcCorr[iFibr-1] > 1.0e-10 ) && ppdSmoothing ) { ppdValues[iFibr - 1][iFIFO] = ppdEffcCorr[iFibr-1] * ppdValues[iFibr - 1][iFIFO]; } // Calculate accumulated values ppdAccumValues[iFibr - 1][iFIFO] += ppdValues[iFibr - 1][iFIFO] ; // Calculate the summed values ppdSummedValues[iFibr - 1] += ppdValues[iFibr - 1][iFIFO]; // Calculate the summed accumulated values ppdAccumSummedValues[iFibr -1 ] += ppdValues[iFibr - 1][iFIFO]; // Turn instantaneous values into rates if the dwell time is reasonable // if( dwellTime > 1.0e-20 ) ppdValues[iFibr - 1][iFIFO] /= dwellTime; } // Turn summed values into rates if the dwell time is reasonable. Need to divide by the product // of the DWEL time and the FIFO length. if( dwellTime > 1.0e-20 ) ppdSummedValues[iFibr -1 ] /= ( ppdAccumValues[iFibr - 1].size() * dwellTime ); // Calculate the total rate as the sum of the rates in all fibers if( this->isAccumulating() ) { ppdTotalRate += ppdAccumSummedValues[iFibr -1 ]; } else { ppdTotalRate += ppdSummedValues[iFibr -1 ]; } } // Calculate the total rate as the sum of the rates in all fibers // ppdTotalRate = std::accumulate( ppdSummedValues.begin(), ppdSummedValues.end(), 0.0 ); // If the size of ppdSlices array is still zero then the PVs for the main SNL have not been // assigned, therefore there is no need to fill array. Will start filling once the PVs are // assigned. if ( ppdSlices.size() == 0 ) { cout << "The slices have not been created, will skip filling values for now" << endl; return; } // Find the multiplier between the number of slices and the FIFO depth. // This number has to be an integer, it is is being checked when setting the number of slices. // When the number of FIFO elements is changed (probably never), then the the number of // slices is set to the new FIFO depth. unsigned multiplier = getNFIFO() / this->getSlices().size(); double scaleFactor = 1.0; if( dwellTime > 1.0e-20 && multiplier > 0 ) scaleFactor = 1.0 / ( double( multiplier ) * dwellTime ); cout << "Scale factor is " << scaleFactor << endl; // Loop over all slice to accumulate their counts // cout << "Plane " << ppdName << " has " << ppdSlices.size() << " slices " << endl; // cout << " FIFO depth is " << this->getNFIFO() << " , number of slices is " << this->getNSlices() // << " , multiplier is " << multiplier << endl; for ( unsigned iSlice = 0; iSlice < ppdSlices.size(); iSlice++ ) { // cout << "Looking at slice " << iSlice << " with slice object at " << hex << showbase // << ppdSlices[iSlice] << dec << endl; // remove const-ness from the reference to the value vector to be able to change them vector& valueVec = const_cast&>( ppdSlices[iSlice]->getValues() ); // cout << "Reference has size " << ppdSlices[iSlice]->getValues().size() << endl; // cout << "Picked up vector with length " << valueVec.size() << endl; // Zero each slice content for all fiber numbers valueVec.assign( valueVec.size(), 0.0 ); // cout << "Set all elements to zero" << endl; double sumOverFibers = 0; // Loop over FIFO elements of the given slice and add the content to the slice counter for ( unsigned iFIFO = (iSlice * multiplier); iFIFO < ((iSlice+1) * multiplier); iFIFO++ ) { // Loop over the fiber number of the profiler for ( unsigned iFibr = 0; iFibr < getChanNumber(); iFibr++ ) { if ( this->isAccumulating() ) { // Increment the value of the counts by the accumulated values valueVec[iFibr] += ppdAccumValues[iFibr][iFIFO]; sumOverFibers += ppdAccumValues[iFibr][iFIFO]; } else { // increment the value of the counts in the slice by the instantaneous values valueVec[iFibr] += ppdValues[iFibr][iFIFO] * scaleFactor; sumOverFibers += ppdValues[iFibr][iFIFO]; } // cout << "Set slice value for slice " << iSlice << " and fiber " << iFibr << " to " << valueVec[iFibr] << endl; } // cout << "Done with FIFO element " << iFIFO << " for " << getName() << endl; } if( this->isAccumulating() && dwellTime < 1.0e-20 ) ppdSlices[iSlice]->setSum( sumOverFibers ); else ppdSlices[iSlice]->setSum( sumOverFibers * scaleFactor ); // // Caclulate the sum over the fiber for each slice // ppdSlices[iSlice]->SumOverFibers(); } // Fill the content of the integrated slice vector& valueVec = const_cast&>( ppdIntegratedSlice->getValues() ); if( this->isAccumulating() ) { std::copy( ppdAccumSummedValues.begin(), ppdAccumSummedValues.end(), valueVec.begin() ); } else { std::copy( ppdSummedValues.begin(), ppdSummedValues.end(), valueVec.begin() ); } return; } // Sets the InputChange flags for all registered SSID so that the clients in SNL can read // the new data. The clients are supposed to clear the InputChange flags. template void ProfilerPlane

::notifyInputFlagWatchers() { for ( map >::iterator it = ppvFlag.begin(); it != ppvFlag.end(); it++ ) { SS_ID ssID = it->first; map& flagMap = it->second; if ( flagMap.count( "InputChange" ) > 0 ) { EV_ID flagID = flagMap["InputChange"]; cout << "ProfilerPlane<" << P << ">::notifyInputFlagWatchers: Notifying SSID " << ssID << " using flag ID " << flagID << " for plane " << P << endl; seq_efSet( ssID, flagID ); // Set the flag received from SNL during registration } else { cout << "ProfilerPlane<" << P << ">::notifyInputFlagWatchers: no Input Change flag found yet. Noone will be notified." << endl; } } } template void ProfilerPlane

::eraseAndStartScalers() { pdcPV->setValue( std::string( "erase_" ) + P, short( 1 ), SYNC ); // cout << "Starting scalers for X" << endl; pdcPV->setValue( std::string( "start_" ) + P, short( 1 ), SYNC ); // cout << "Scalers for X restarted" << endl; } // Function that executes in a separate thread and checks if the PVs have changed // and sets the signal for SNL code to process PVs. template void* ProfilerPlane

::threadFunc( void* argPtr ) { ProfilerPlane

* planePtr = (ProfilerPlane

*) (argPtr); // infinite loop while ( true ) { planePtr->waitForAcquisition(); planePtr->waitForBusy(); // usleep(ppvSleepTime); // Sleep for a while } return 0; } template void ProfilerPlane

::waitForAcquisition() { // Check if the acquiring is complete, and if it is transfer the FIFO content from // the MCA FIFO are to the local area for this object and notify the watchers in the // state code. cout << "Entering Acquisition Loop for " << P << endl; while ( true ) { if ( pdcPV->pvCheckEvtFlag( std::string( "acqcmp_" ) + P ) ) { // cout << "Flag for acquire completion is up" << endl; pdcPV->pvClearEvtFlag( std::string( "acqcmp_" ) + P ); if ( pdcPV->getPV( std::string( "acqcmp_" ) + P, SYNC, short() ) == 1 ) { // cout << "Acquire is complete" << endl; // There was a flag amongst short input PVs cout << "SoftAcquiring Changed for " << P << endl; transferFIFO(); notifyInputFlagWatchers(); break; } } usleep( getSleepTime() ); // Sleep for a while } cout << "Exited Acquisition Loop for " << P << endl; return; } template void ProfilerPlane

::waitForBusy() { cout << "Entering Busy Loop for " << P << endl; // cout << "Busy flag is " << ppvPV->getPV( std::string("busy_")+P, SYNC, short() ) << endl; EV_ID flagID; SS_ID ssID; unsigned maxWaitCounts = 10000; bool flagFound = false; unsigned wait4flagCounter = 0; // Wait for a while until busy flag registers and give up after waiting maxWaitCounts cycles. while ( (!flagFound) && (wait4flagCounter < maxWaitCounts) ) { for ( map >::iterator it = ppvFlag.begin(); it != ppvFlag.end(); it++ ) { map& flagMap = it->second; if ( flagMap.count( "BusyClear" ) > 0 ) { ssID = it->first; flagID = flagMap["BusyClear"]; flagFound = true; break; } } // cout << "ProfilerPlane<"<< P << ">::waitForBusy: no busy flag found yet. Waiting ... " << wait4flagCounter << endl; usleep( ppvSleepTime ); wait4flagCounter++; } // Throw an exception if the busy flag never shows as a registered flag if ( (!flagFound) && (wait4flagCounter >= maxWaitCounts) ) { // throw exception if no busy event flag found std::stringstream errStream; errStream << "ProfilerPlane<" << P << ">::waitForBusy: Failed to find busy flag for profiler plane " << getName(); throw std::runtime_error( errStream.str() ); } // cout << "Flag found for " << getName() << " , will wait for busy clear flag" << endl; // Check if the client wait is complete and erase and restart the scalers. The state code // is supposed to clear InputChanged and set the BusyClar flag so that this thread knows // that the scalers can be erased and new data can be acquired. while ( true ) { if ( seq_efTest( ssID, flagID ) == 1 ) { eraseAndStartScalers(); cout << "Erased and started scalers for " << getName() << endl; // After the acquisition is complete the waitdone records gets set to busy, // and the scaler will not take data. So it will be up to the SNL code to change it to 1, // and then we will get back into this block to erase and restart the scalers. cout << "Will clear busy flag for ssID " << ssID << " and flag " << flagID << endl; seq_efClear( ssID, flagID ); break; } usleep( getSleepTime() ); // Sleep for a while } cout << "Exited Busy Loop for " << P << endl; return; } #endif /* PROFILERPLANE_HH_ */