diff --git a/DeviceAdapters/PVCAM/AcqConfig.h b/DeviceAdapters/PVCAM/AcqConfig.h index 00e4addf5..3f50e4f90 100644 --- a/DeviceAdapters/PVCAM/AcqConfig.h +++ b/DeviceAdapters/PVCAM/AcqConfig.h @@ -195,10 +195,6 @@ class AcqConfig */ bool CircBufSizeAuto{ true }; /** - * True if PVCAM callbacks are active, false to use polling - */ - bool CallbacksEnabled{ true }; - /** * Enables or disables custom streaming to disk. * Please note that this streaming is enabled for continuous acquisition only. * The streaming to disk is fully controlled by PVCAM adapter and should only diff --git a/DeviceAdapters/PVCAM/AcqThread.cpp b/DeviceAdapters/PVCAM/AcqThread.cpp index c4f1c2a06..d85b49584 100644 --- a/DeviceAdapters/PVCAM/AcqThread.cpp +++ b/DeviceAdapters/PVCAM/AcqThread.cpp @@ -44,7 +44,7 @@ int AcqThread::svc() { camera_->LogAdapterMessage("AcqThead loop started"); int nRet = DEVICE_OK; - while(!requestStop_) + while (!requestStop_) { resumeEvent_.Wait(); if (requestStop_) @@ -57,10 +57,8 @@ int AcqThread::svc() if (nRet != DEVICE_OK) continue; // Error logged, ignore and try again - // Frame successfully arrived and ready in the buffer. If we are - // not using callbacks we need to manually push it to the core. - if (!camera_->acqCfgCur_.CallbacksEnabled) - camera_->FrameAcquired(); + // Frame successfully arrived and ready in the buffer. + // Frame pushed to the MM core from PVCAM callback handler. } camera_->LogAdapterMessage("AcqThead loop exited"); return nRet; diff --git a/DeviceAdapters/PVCAM/Makefile.am b/DeviceAdapters/PVCAM/Makefile.am index 3a7fdd408..ba27928b8 100644 --- a/DeviceAdapters/PVCAM/Makefile.am +++ b/DeviceAdapters/PVCAM/Makefile.am @@ -15,8 +15,6 @@ libmmgr_dal_PVCAM_la_SOURCES = \ AcqThread.h \ Event.cpp \ Event.h \ - PollingThread.cpp \ - PollingThread.h \ PpParam.cpp \ PpParam.h \ PVCAMAdapter.cpp \ diff --git a/DeviceAdapters/PVCAM/PVCAM.vcxproj b/DeviceAdapters/PVCAM/PVCAM.vcxproj index 134f3544d..dce49dcad 100644 --- a/DeviceAdapters/PVCAM/PVCAM.vcxproj +++ b/DeviceAdapters/PVCAM/PVCAM.vcxproj @@ -96,7 +96,6 @@ - @@ -116,7 +115,6 @@ - diff --git a/DeviceAdapters/PVCAM/PVCAM.vcxproj.filters b/DeviceAdapters/PVCAM/PVCAM.vcxproj.filters index cc6aadaea..c4780a10a 100644 --- a/DeviceAdapters/PVCAM/PVCAM.vcxproj.filters +++ b/DeviceAdapters/PVCAM/PVCAM.vcxproj.filters @@ -33,9 +33,6 @@ Source Files - - Source Files - Source Files @@ -89,9 +86,6 @@ Header Files - - Header Files - Header Files diff --git a/DeviceAdapters/PVCAM/PVCAMAdapter.h b/DeviceAdapters/PVCAM/PVCAMAdapter.h index 8231a5d64..895346254 100644 --- a/DeviceAdapters/PVCAM/PVCAMAdapter.h +++ b/DeviceAdapters/PVCAM/PVCAMAdapter.h @@ -145,7 +145,6 @@ enum PvCameraModel //============================================================================= //======================================================== FORWARD DECLARATIONS -class PollingThread; class AcqThread; class StreamWriter; template class PvParam; @@ -501,11 +500,6 @@ class Universal : public CCameraBase */ int OnDiskStreamingCoreSkipRatio(MM::PropertyBase* pProp, MM::ActionType eAct); - /** - * Switches between Callbacks or Polling acquisition type. - */ - int OnAcquisitionMethod(MM::PropertyBase* pProp, MM::ActionType eAct); - /** * Post processing parameter handler. Post processing features and parameters are * read out from the camera dynamically. Based on the camera provided information @@ -621,19 +615,17 @@ class Universal : public CCameraBase protected: /** - * This method is called from the static PVCAM callback or polling thread. - * The method should finish as fast as possible to avoid blocking the PVCAM. - * If the execution of this method takes longer than frame readout + exposure, - * the FrameAcquired for the next frame may not be called. + * This method is called from the static PVCAM callback. */ int FrameAcquired(); /** * Called from FrameAcquired(), inserts the frame to the MMCore. */ - int ProcessFrame(const void* pData, size_t dataSz, const PvFrameInfo& frameNfo); - - int PollingThreadRun(void); - void PollingThreadExiting() throw(); + int ProcessFrame(const void* pData, const PvFrameInfo& frameNfo); + /** + * Called from ProcessFrame(), composes metadata for the MMCore. + */ + void BuildMetadata(MM::CameraImageMetadata& md, const PvFrameInfo& frameNfo); private: // Make object non-copyable @@ -691,9 +683,6 @@ class Universal : public CCameraBase * acquisition thread. */ int waitForFrameSeq(); - int waitForFrameSeqPolling(const MM::MMTime& timeout); - int waitForFrameSeqCallbacks(const MM::MMTime& timeout); - int waitForFrameConPolling(const MM::MMTime& timeout); int PrepareSeqAcq(); // Note: no longer a device interface function @@ -849,10 +838,8 @@ class Universal : public CCameraBase std::map> expTimeResLimits_{}; // [expTimeRes]={min,max} - friend class PollingThread; - std::unique_ptr pollingThd_{}; // Pointer to the sequencing thread friend class AcqThread; - std::unique_ptr acqThd_{}; // Non-CB live thread + std::unique_ptr acqThd_{}; // Non-circular buffer "live" acq. thread std::unique_ptr customDiskWriter_{}; // Writer for custom disk streaming feature bool customDiskWriterActive_{ false }; // Cached value updated after writer->Start diff --git a/DeviceAdapters/PVCAM/PVCAMUniversal.cpp b/DeviceAdapters/PVCAM/PVCAMUniversal.cpp index b0ae81d35..01db204f4 100644 --- a/DeviceAdapters/PVCAM/PVCAMUniversal.cpp +++ b/DeviceAdapters/PVCAM/PVCAMUniversal.cpp @@ -41,7 +41,6 @@ // Local #include "AcqThread.h" -#include "PollingThread.h" #include "PVCAMParam.h" #include "StreamWriter.h" #include "Version.h" @@ -141,9 +140,6 @@ const char* g_Keyword_Replication = "Nearest Neighbor Replication"; const char* g_Keyword_Bilinear = "Bilinear"; const char* g_Keyword_SmoothHue = "Smooth Hue"; const char* g_Keyword_AdaptiveSmoothHue = "Adaptive Smooth Hue (edge detecting)"; -const char* g_Keyword_AcqMethod = "AcquisitionMethod"; // Callbacks/Polling -const char* g_Keyword_AcqMethod_Callbacks = "Callbacks"; -const char* g_Keyword_AcqMethod_Polling = "Polling"; const char* g_Keyword_CircBufFrameCnt = "CircularBufferFrameCount"; const char* g_Keyword_CircBufSizeAuto = "CircularBufferAutoSize"; // ON/OFF const char* g_Keyword_CircBufFrameRecovery = "CircularBufferFrameRecovery"; // ON/OFF @@ -230,7 +226,6 @@ Universal::Universal(short cameraId, const char* deviceName) deviceName_(deviceName), // Sizes larger than 3 caused image tearing in ICX-674. Reason unknown. circBufFrameCount_(CIRC_BUF_FRAME_CNT_DEF), - pollingThd_(std::make_unique(this)), acqThd_(std::make_unique(this)), customDiskWriter_(std::make_unique(this)) { @@ -263,11 +258,6 @@ Universal::~Universal() if (initialized_) Shutdown(); } - if (!pollingThd_->GetStop()) - { - pollingThd_->SetStop(true); - pollingThd_->wait(); - } if (metaFrameStruct_) pl_md_release_frame_struct(metaFrameStruct_); @@ -1123,28 +1113,15 @@ int Universal::Initialize() // CALLBACKS // Check if we can use PVCAM callbacks. This is recommended way to get notified when the frame - // readout is finished. Otherwise we will fall back to old polling method. - acqCfgNew_.CallbacksEnabled = false; - if ( pl_cam_register_callback_ex3( hPVCAM_, PL_CALLBACK_EOF, (void*)&Universal::PvcamCallbackEofEx3, this ) == PV_OK ) - { - pAct = new CPropertyAction(this, &Universal::OnAcquisitionMethod); - nRet = CreateProperty(g_Keyword_AcqMethod, g_Keyword_AcqMethod_Polling, MM::String, false, pAct ); - AddAllowedValue(g_Keyword_AcqMethod, g_Keyword_AcqMethod_Polling); - AddAllowedValue(g_Keyword_AcqMethod, g_Keyword_AcqMethod_Callbacks); - LogAdapterMessage( "Using callbacks for frame acquisition" ); - acqCfgNew_.CallbacksEnabled = true; - } - else - { - LogAdapterMessage( "pl_cam_register_callback_ex3 failed! Using polling for frame acquisition" ); - } + // readout is finished. + if (!pl_cam_register_callback_ex3( + hPVCAM_, PL_CALLBACK_EOF, &Universal::PvcamCallbackEofEx3, this)) + return LogPvcamError(__LINE__, "Failed to register EOF callback handler"); // FRAME_INFO SUPPORT // Initialize the FRAME_INFO structure, this will contain the frame metadata provided by PVCAM - if ( !pl_create_frame_info_struct( &pFrameInfo_ ) ) - { + if (!pl_create_frame_info_struct(&pFrameInfo_)) return LogPvcamError(__LINE__, "Failed to initialize the FRAME_INFO structure"); - } // TRIGGER TABLE // We will create a property for every TrigTab and LastMuxed combination so we will end up with something similar @@ -1445,16 +1422,12 @@ int Universal::Shutdown() { if (initialized_) { - rs_bool ret; + if (!pl_cam_deregister_callback( hPVCAM_, PL_CALLBACK_EOF )) + LogPvcamError(__LINE__, "pl_cam_deregister_callback EOF"); - if ( acqCfgCur_.CallbacksEnabled ) - { - pl_cam_deregister_callback( hPVCAM_, PL_CALLBACK_EOF ); - } - ret = pl_cam_close(hPVCAM_); - if (!ret) + if (!pl_cam_close(hPVCAM_)) LogPvcamError(__LINE__, "pl_cam_close"); - assert(ret); + refCount_--; if (PVCAM_initialized_ && refCount_ <= 0) { @@ -1463,11 +1436,13 @@ int Universal::Shutdown() LogPvcamError(__LINE__, "pl_pvcam_uninit"); PVCAM_initialized_ = false; } + if ( pFrameInfo_ ) { pl_release_frame_info_struct( pFrameInfo_ ); pFrameInfo_ = nullptr; } + initialized_ = false; } return DEVICE_OK; @@ -1507,7 +1482,9 @@ bool Universal::GetErrorText(int errorCode, char* text) const int Universal::SnapImage() { int nRet = DEVICE_ERR; - MM::MMTime startTs = GetCurrentMMTime(); + MM::MMTime startTs; + MM::MMTime endTs; + { std::lock_guard acqGuard(acqLock_); START_METHOD("Universal::SnapImage"); @@ -1585,12 +1562,14 @@ int Universal::SnapImage() singleFrameModeReady_ = false; } - const MM::MMTime endTs = GetCurrentMMTime(); - LogTimeDiff(startTs, endTs, "SnapImage() took: ", true); - isAcquiring_ = false; - return nRet; + + endTs = GetCurrentMMTime(); } + + LogTimeDiff(startTs, endTs, "SnapImage() took: ", true); + + return nRet; } const unsigned char* Universal::GetImageBuffer() @@ -1944,10 +1923,6 @@ int Universal::StartSequenceAcquisition(long numImages, double interval_ms, bool // the callbacks will start coming pretty fast. Do not waste time here, what can be done // before start_cont() should be done there. - if ( !acqCfgCur_.CallbacksEnabled && acqCfgCur_.CircBufEnabled ) - { - pollingThd_->Start(); - } isAcquiring_ = true; std::ostringstream os; @@ -3281,24 +3256,6 @@ int Universal::OnDiskStreamingCoreSkipRatio(MM::PropertyBase* pProp, MM::ActionT return DEVICE_OK; } -int Universal::OnAcquisitionMethod(MM::PropertyBase* pProp, MM::ActionType eAct) -{ - START_ONPROPERTY("Universal::OnAcquisitionMethod", eAct); - if (eAct == MM::AfterSet) - { - std::string val; - pProp->Get(val); - - acqCfgNew_.CallbacksEnabled = (val.compare(g_Keyword_AcqMethod_Callbacks) == 0); - applyAcqConfig(); - } - else if (eAct == MM::BeforeGet) - { - pProp->Set(acqCfgCur_.CallbacksEnabled ? g_Keyword_AcqMethod_Callbacks : g_Keyword_AcqMethod_Polling); - } - return DEVICE_OK; -} - int Universal::OnPostProcProperties(MM::PropertyBase* pProp, MM::ActionType eAct, long index) { START_ONPROPERTY("Universal::OnPostProcProperties", eAct); @@ -3826,123 +3783,122 @@ int Universal::FrameAcquired() if (!isAcquiring_) return DEVICE_OK; - rs_bool rsbRet = FALSE; - void* pCurrFramePtr = nullptr; - PvFrameInfo currFrameNfo; - currFrameNfo.SetTimestampMsec(GetCurrentMMTime().getMsec()); + int ret = DEVICE_OK; + void* pCurrFramePtr = nullptr; + const MM::MMTime frameTs = GetCurrentMMTime(); { std::lock_guard pvcamGuard(g_pvcamLock); - rsbRet = pl_exp_get_latest_frame_ex(hPVCAM_, &pCurrFramePtr, pFrameInfo_ ); - if (rsbRet != PV_OK) - LogPvcamError(__LINE__, "pl_exp_get_latest_frame_ex() failed"); + if (!pl_exp_get_latest_frame_ex(hPVCAM_, &pCurrFramePtr, pFrameInfo_)) + ret = LogPvcamError(__LINE__, "pl_exp_get_latest_frame_ex() failed"); } - if (rsbRet == PV_OK) + if (ret != DEVICE_OK) { - currFrameNfo.SetPvHCam(pFrameInfo_->hCam); - currFrameNfo.SetPvFrameNr(pFrameInfo_->FrameNr); - currFrameNfo.SetPvReadoutTime(pFrameInfo_->ReadoutTime); - currFrameNfo.SetPvTimeStamp(pFrameInfo_->TimeStamp); - currFrameNfo.SetPvTimeStampBOF(pFrameInfo_->TimeStampBOF); + abortAcquisitionInternal(); + return ret; + } - if (acqCfgCur_.CircBufEnabled) + PvFrameInfo currFrameNfo; + currFrameNfo.SetTimestampMsec(frameTs.getMsec()); + currFrameNfo.SetPvHCam(pFrameInfo_->hCam); + currFrameNfo.SetPvFrameNr(pFrameInfo_->FrameNr); + currFrameNfo.SetPvReadoutTime(pFrameInfo_->ReadoutTime); + currFrameNfo.SetPvTimeStamp(pFrameInfo_->TimeStamp); + currFrameNfo.SetPvTimeStampBOF(pFrameInfo_->TimeStampBOF); + + if (acqCfgCur_.CircBufEnabled) + { + const int currFrameNr = currFrameNfo.PvFrameNr(); + const int prevFrameNr = lastPvFrameNr_; + if (currFrameNr == prevFrameNr) { - const int currFrameNr = currFrameNfo.PvFrameNr(); - const int prevFrameNr = lastPvFrameNr_; - if (currFrameNr == prevFrameNr) - { - // Received a duplicate callback? This seems like a bug in PVCAM, - // it occurs for optiMos at high frame rates. For now just silently ignore it, - // because the next one will correctly arrive right after that. - return DEVICE_OK; - } + // Received a duplicate callback? This seems like a bug in PVCAM, + // it occurs for optiMos at high frame rates. For now just silently ignore it, + // because the next one will correctly arrive right after that. + return DEVICE_OK; + } - // Check whether we haven't missed a callback - if (currFrameNr > prevFrameNr + 1) - { - const int missedCbCount = currFrameNr - prevFrameNr - 1; + // Check whether we haven't missed a callback + if (currFrameNr > prevFrameNr + 1) + { + const int missedCbCount = currFrameNr - prevFrameNr - 1; - if (circBufFrameRecoveryEnabled_) + if (circBufFrameRecoveryEnabled_) + { + // Get the last known frame index in the CB + const int lastFrIdx = circBuf_.LatestFrameIndex(); + if (lastFrIdx < 0) { - // Get the last known frame index in the CB - const int lastFrIdx = circBuf_.LatestFrameIndex(); - if (lastFrIdx < 0) - { - // We cannot perform frame recovery because we don't have a frame in the buffer yet - // so we cannot recover the metadata. This mostly happens with Polling acquisition - // because it can easily miss several frames when starting acquisition. - } - else + // We cannot perform frame recovery because we don't have a frame in the buffer yet + // so we cannot recover the metadata. This mostly happens with Polling acquisition + // because it can easily miss several frames when starting acquisition. + } + else + { + const PvFrameInfo& lastFrNfo = circBuf_.FrameInfo(lastFrIdx); + + // We need to re-create the FRAME_INFOs by averaging the known frame infos. + // This is not really nice way of fixing things but since the camera is running on + // constant rate the recovered data will be accurate enough. Plus, we mark the frame as recovered + // so the user will be aware of this. + const int recReadoutTm = static_cast((lastFrNfo.PvReadoutTime() + currFrameNfo.PvReadoutTime()) / 2); + const long long lastPvTimestampBOF = lastFrNfo.PvTimeStampBOF(); + const long long lastPvTimestampEOF = lastFrNfo.PvTimeStamp(); + const double lastApTimestampMsec = lastFrNfo.TimeStampMsec(); + const double div = missedCbCount + 1; + const double avgBofDiff = (currFrameNfo.PvTimeStampBOF() - lastPvTimestampBOF) / div; + const double avgEofDiff = (currFrameNfo.PvTimeStamp() - lastPvTimestampEOF) / div; + const double avgAppDiff = (currFrameNfo.TimeStampMsec() - lastApTimestampMsec) / div; + + for (int i = 0; i < missedCbCount; ++i) { - const PvFrameInfo& lastFrNfo = circBuf_.FrameInfo(lastFrIdx); - - // We need to re-create the FRAME_INFOs by averaging the known frame infos. - // This is not really nice way of fixing things but since the camera is running on - // constant rate the recovered data will be accurate enough. Plus, we mark the frame as recovered - // so the user will be aware of this. - const int recReadoutTm = static_cast((lastFrNfo.PvReadoutTime() + currFrameNfo.PvReadoutTime()) / 2); - const long long lastPvTimestampBOF = lastFrNfo.PvTimeStampBOF(); - const long long lastPvTimestampEOF = lastFrNfo.PvTimeStamp(); - const double lastApTimestampMsec = lastFrNfo.TimeStampMsec(); - const double div = missedCbCount + 1; - const double avgBofDiff = (currFrameNfo.PvTimeStampBOF() - lastPvTimestampBOF) / div; - const double avgEofDiff = (currFrameNfo.PvTimeStamp() - lastPvTimestampEOF) / div; - const double avgAppDiff = (currFrameNfo.TimeStampMsec() - lastApTimestampMsec) / div; - - for (int i = 0; i < missedCbCount; ++i) + // Get the index of the next frame in the CB. The data for this frame has been + // correctly delivered by the driver, however since we missed a callback we also + // missed the FRAME_INFO. Thus we need to recreate the FRAME_INFO ourselves. + // This can be removed once PVCAM implements better way of retrieving particular frames. + const unsigned int nextFrIdx = (lastFrIdx + i + 1) % circBuf_.Capacity(); + + // Retrieve the data pointer for the skipped callback + void* pRecFrameData = circBuf_.FrameData(nextFrIdx); + + // Re-create the FRAME_INFO + const short int recHCam = lastFrNfo.PvHCam(); + const int recFrameNr = prevFrameNr + i + 1; + const long long recTimeStampBOF = static_cast(lastPvTimestampBOF + ((i + 1)*avgBofDiff)); + const long long recTimeStampEOF = static_cast(lastPvTimestampEOF + ((i + 1)*avgEofDiff)); + const double recAppTimeStampEOF = lastApTimestampMsec + ((i + 1)*avgAppDiff); + + PvFrameInfo recFrNfo; + recFrNfo.SetPvHCam(recHCam); + recFrNfo.SetPvFrameNr(recFrameNr); + recFrNfo.SetPvReadoutTime(recReadoutTm); + recFrNfo.SetPvTimeStamp(recTimeStampEOF); + recFrNfo.SetPvTimeStampBOF(recTimeStampBOF); + recFrNfo.SetTimestampMsec(recAppTimeStampEOF); + recFrNfo.SetRecovered(true); + + imagesAcquired_++; + + // Process the new frame the same way as the frame + // would arrive correctly with a callback. + ret = ProcessFrame(pRecFrameData, recFrNfo); + if (ret != DEVICE_OK) { - // Get the index of the next frame in the CB. The data for this frame has been - // correctly delivered by the driver, however since we missed a callback we also - // missed the FRAME_INFO. Thus we need to recreate the FRAME_INFO ourselves. - // This can be removed once PVCAM implements better way of retrieving particular frames. - const unsigned int nextFrIdx = (lastFrIdx + i + 1) % circBuf_.Capacity(); - - // Retrieve the data pointer for the skipped callback - void* pRecFrameData = circBuf_.FrameData(nextFrIdx); - - // Re-create the FRAME_INFO - const short int recHCam = lastFrNfo.PvHCam(); - const int recFrameNr = prevFrameNr + i + 1; - const long long recTimeStampBOF = static_cast(lastPvTimestampBOF + ((i + 1)*avgBofDiff)); - const long long recTimeStampEOF = static_cast(lastPvTimestampEOF + ((i + 1)*avgEofDiff)); - const double recAppTimeStampEOF = lastApTimestampMsec + ((i + 1)*avgAppDiff); - - PvFrameInfo recFrNfo; - recFrNfo.SetPvHCam(recHCam); - recFrNfo.SetPvFrameNr(recFrameNr); - recFrNfo.SetPvReadoutTime(recReadoutTm); - recFrNfo.SetPvTimeStamp(recTimeStampEOF); - recFrNfo.SetPvTimeStampBOF(recTimeStampBOF); - recFrNfo.SetTimestampMsec(recAppTimeStampEOF); - recFrNfo.SetRecovered(true); - - // Notify our CB wrapper that a new frame has "arrived", it will increase - // its internal counters and indexes. - circBuf_.ReportFrameArrived(recFrNfo, pRecFrameData); - - // Process the new frame the same way as the frame - // would arrive correctly with a callback. - ProcessFrame(pRecFrameData, circBuf_.FrameSize(), recFrNfo); - imagesAcquired_++; - imagesRecovered_++; + abortAcquisitionInternal(); + return ret; } + + imagesInserted_++; + imagesRecovered_++; } } - else - { // Frame recovery is disabled - // TODO: Again, should we report an error? - } } - lastPvFrameNr_ = currFrameNr; + else + { // Frame recovery is disabled + // TODO: Again, should we report an error? + } } - } - - if ( rsbRet != PV_OK ) - { - std::lock_guard pvcamGuard(g_pvcamLock); - if (pl_exp_abort( hPVCAM_, CCS_CLEAR ) != PV_OK) - LogPvcamError(__LINE__, "pl_exp_abort() failed"); - return DEVICE_ERR; + lastPvFrameNr_ = currFrameNr; } imagesAcquired_++; // A new frame has been successfully retrieved from the camera @@ -3951,27 +3907,23 @@ int Universal::FrameAcquired() // so we have to check. In case of SnapImage the singleFrameBufRaw_ already // contains the data (since it's passed to pl_start_seq() and no PushImage() // is done - the single image is retrieved with GetImageBuffer(). - if ( !snappingSingleFrame_ ) + if (!snappingSingleFrame_) { - size_t currFrameSize = singleFrameBufRawSz_; - if (acqCfgCur_.CircBufEnabled) + ret = ProcessFrame(pCurrFramePtr, currFrameNfo); + if (ret != DEVICE_OK) { - currFrameSize = circBuf_.FrameSize(); - circBuf_.ReportFrameArrived(currFrameNfo, pCurrFramePtr); + abortAcquisitionInternal(); + return ret; } - ProcessFrame(pCurrFramePtr, currFrameSize, currFrameNfo); - } - else - { - // Single snap: just increase the number of actually acquired frames. - imagesInserted_++; } + imagesInserted_++; + eofEvent_.Set(); return DEVICE_OK; } -int Universal::ProcessFrame(const void* pData, size_t dataSz, const PvFrameInfo& frameNfo) +int Universal::ProcessFrame(const void* pData, const PvFrameInfo& frameNfo) { // Ignore inserts if we already have all images inserted. // This should never happen but stay on safe side. @@ -3982,15 +3934,18 @@ int Universal::ProcessFrame(const void* pData, size_t dataSz, const PvFrameInfo& if (!isAcquiring_) // Cannot guard it with acqLock_ return DEVICE_OK; + if (acqCfgCur_.CircBufEnabled) + { + // Notify our CB wrapper that a new frame has "arrived", + // it will increase its internal counters and indexes. + circBuf_.ReportFrameArrived(frameNfo, pData); + } + int ret = DEVICE_OK; ret = customDiskWriter_->WriteFrame(pData, frameNfo.PvFrameNr()); if (ret != DEVICE_OK) - { - StopSequenceAcquisition(); - // TODO: Display an error message? return ret; - } // Send the frame to MMCore if custom streaming to disk is not active, // otherwise send only every Nth frame to reduce unnecessary CPU/memory load @@ -3998,281 +3953,185 @@ int Universal::ProcessFrame(const void* pData, size_t dataSz, const PvFrameInfo& || (imagesInserted_ % acqCfgCur_.DiskStreamingCoreSkipRatio == 0); if (sendFrameToCore) { - // Build the metadata - MM::CameraImageMetadata md; - md.AddTag(MM::g_Keyword_Metadata_CameraLabel, deviceLabel_); - md.AddTag("TimeStampMsec", CDeviceUtils::ConvertToString(frameNfo.TimeStampMsec())); - - md.AddTag( "PVCAM-CameraHandle", frameNfo.PvHCam() ); - md.AddTag( "PVCAM-FrameNr", frameNfo.PvFrameNr() ); - md.AddTag( "PVCAM-ReadoutTime", frameNfo.PvReadoutTime() ); - md.AddTag( "PVCAM-TimeStamp", frameNfo.PvTimeStamp() ); - md.AddTag( "PVCAM-TimeStampBOF", frameNfo.PvTimeStampBOF() ); - if (circBufFrameRecoveryEnabled_) - { - md.AddTag( "PVCAM-FrameRecovered", frameNfo.IsRecovered() ); - md.AddTag( "PVCAM-FramesRecoveredTotal", imagesRecovered_ ); - } - - const double startTimeMsec = startTime_.getMsec(); + const double startTimeMsec = startTime_.getMsec(); const double elapsedTimeMsec = frameNfo.TimeStampMsec() - startTimeMsec; - - // The time elapsed since start of the acquisition until current frame readout - // Now added by MM automatically, no need to do it here. - // md.AddTag(MM::g_Keyword_Elapsed_Time_ms, CDeviceUtils::ConvertToString(elapsedTimeMsec)); - const double actualInterval = elapsedTimeMsec / imagesInserted_; SetProperty(MM::g_Keyword_ActualInterval_ms, CDeviceUtils::ConvertToString(actualInterval)); unsigned char* pOutBuf = nullptr; + const size_t dataSz = (acqCfgCur_.CircBufEnabled) + ? circBuf_.FrameSize() : singleFrameBufRawSz_; ret = postProcessSingleFrame(&pOutBuf, (unsigned char*)pData, dataSz); if (ret != DEVICE_OK) - { - StopSequenceAcquisition(); - // TODO: Display an error message? return ret; - } - // The post-processing done above also decodes the frame metadata if supported - if (acqCfgCur_.FrameMetadataEnabled) - { - // FMD stands for Frame-MetaData, we should somehow distinguish the embedded - // metadata from other metadata and keep them grouped or close together. - const md_frame_header* fHdr = metaFrameStruct_->header; - // Selected metadata from the frame header - md.AddTag("PVCAM-FMD-Version", fHdr->version); // Need to use uns16 because uns8 is displayed as char - md.AddTag("PVCAM-FMD-FrameNr", fHdr->frameNr); - md.AddTag("PVCAM-FMD-RoiCount", fHdr->roiCount); - md.AddTag("PVCAM-FMD-BitDepth", fHdr->bitDepth); // Need to use uns16 because uns8 is displayed as char - const char* cKeywordColorMask = "PVCAM-FMD-ColorMask"; - switch (fHdr->colorMask) - { - case COLOR_NONE: - md.AddTag(cKeywordColorMask, "None"); - break; - case COLOR_RGGB: - md.AddTag(cKeywordColorMask, "RGGB"); - break; - case COLOR_GRBG: - md.AddTag(cKeywordColorMask, "GRBG"); - break; - case COLOR_GBRG: - md.AddTag(cKeywordColorMask, "GBRG"); - break; - case COLOR_BGGR: - md.AddTag(cKeywordColorMask, "BGGR"); - break; - default: - md.AddTag(cKeywordColorMask, "Unknown"); - break; - } - md.AddTag("PVCAM-FMD-Flags", fHdr->flags); // Need to use uns16 because uns8 is displayed as char - if (fHdr->version >= 2) - { - md.AddTag("PVCAM-FMD-ImageFormat", getPvcamImageFormatString(fHdr->imageFormat)); - md.AddTag("PVCAM-FMD-ImageCompression", getPvcamImageCompressionString(fHdr->imageCompression)); - } - if (fHdr->version < 3) - { - md.AddTag( "PVCAM-FMD-ExposureTimeNs", - (ulong64)fHdr->exposureTime * fHdr->exposureTimeResNs ); - md.AddTag( "PVCAM-FMD-TimestampBofNs", - (ulong64)fHdr->timestampBOF * fHdr->timestampResNs ); - md.AddTag( "PVCAM-FMD-TimestampEofNs", - (ulong64)fHdr->timestampEOF * fHdr->timestampResNs ); - } - else - { - const md_frame_header_v3* fHdr3 = - reinterpret_cast(metaFrameStruct_->header); - // Version 3 of the metadata transfers the timestamps in picoseconds. - md.AddTag( "PVCAM-FMD-ExposureTimePs", - (ulong64)fHdr3->exposureTime ); - md.AddTag( "PVCAM-FMD-TimestampBofPs", - (ulong64)fHdr3->timestampBOF ); - md.AddTag( "PVCAM-FMD-TimestampEofPs", - (ulong64)fHdr3->timestampEOF ); - } - // Implied ROI - const rgn_type& iRoi = metaFrameStruct_->impliedRoi; - snprintf(metaRoiStr_, sizeof(metaRoiStr_), "[%u,%u,%u,%u,%u,%u]", - iRoi.s1, iRoi.s2, iRoi.sbin, iRoi.p1, iRoi.p2, iRoi.pbin); - md.AddTag("PVCAM-FMD-ImpliedRoi", metaRoiStr_); - // Per-ROI metadata - metaAllRoisStr_ = "["; - for (int i = 0; i < metaFrameStruct_->roiCount; ++i) - { - // Since we cannot add per-ROI metadata we will format the MD to a simple JSON array - // and add it as a per-Frame metadata TAG. Example: - // "[{"nr":1,"coords":[0,0,0,0,0,0]},{"nr":2,"coords":[0,0,0,0,0,0]}]" - const md_frame_roi& pRoi = metaFrameStruct_->roiArray[i]; - const md_frame_roi_header* rHdr = pRoi.header; - if (rHdr->flags & PL_MD_ROI_FLAG_INVALID) - continue; // Skip invalid regions - if (i > 0) - metaAllRoisStr_.append(","); - metaAllRoisStr_.append("{"); - snprintf(metaRoiStr_, sizeof(metaRoiStr_), - "\"nr\":%u,\"coords\":[%u,%u,%u,%u,%u,%u],\"flags\":%u", - rHdr->roiNr, - rHdr->roi.s1, rHdr->roi.s2, rHdr->roi.sbin, - rHdr->roi.p1, rHdr->roi.p2, rHdr->roi.pbin, - rHdr->flags); - metaAllRoisStr_.append(metaRoiStr_); - if (fHdr->flags & PL_MD_FRAME_FLAG_ROI_TS_SUPPORTED) - { - if (fHdr->version < 3) - { - snprintf(metaRoiStr_, sizeof(metaRoiStr_), - ",\"borNs\":%llu,\"eorNs\":%llu", - (ulong64)rHdr->timestampBOR * fHdr->roiTimestampResNs, - (ulong64)rHdr->timestampEOR * fHdr->roiTimestampResNs); - } - else - { - snprintf(metaRoiStr_, sizeof(metaRoiStr_), - ",\"bor\":%u,\"eor\":%u", - rHdr->timestampBOR, rHdr->timestampEOR); - } - metaAllRoisStr_.append(metaRoiStr_); - } - if (/*(rHdr->flags & PL_MD_ROI_FLAG_HEADER_ONLY) && */pRoi.extMdDataSize > 0) - { - const md_ext_item_collection& collection = metaFrameExtData_.at(rHdr->roiNr); - const md_ext_item* item_id = collection.map[PL_MD_EXT_TAG_PARTICLE_ID]; - if (item_id) - { - snprintf(metaRoiStr_, sizeof(metaRoiStr_), - ",\"tagParticleId\":%u", *((uint32_t*)item_id->value)); - metaAllRoisStr_.append(metaRoiStr_); - } - const md_ext_item* item_m0 = collection.map[PL_MD_EXT_TAG_PARTICLE_M0]; - if (item_m0) - { - snprintf(metaRoiStr_, sizeof(metaRoiStr_), - ",\"tagParticleM0\":%u", *((uint32_t*)item_m0->value)); - metaAllRoisStr_.append(metaRoiStr_); - } - const md_ext_item* item_m2 = collection.map[PL_MD_EXT_TAG_PARTICLE_M2]; - if (item_m2) - { - snprintf(metaRoiStr_, sizeof(metaRoiStr_), - ",\"tagParticleM2\":%u", *((uint32_t*)item_m2->value)); - metaAllRoisStr_.append(metaRoiStr_); - } - } - metaAllRoisStr_.append("}"); - } - metaAllRoisStr_.append("]"); - md.AddTag("PVCAM-FMD-RoiMD", metaAllRoisStr_); - } + MM::CameraImageMetadata md; + BuildMetadata(md, frameNfo); // This method inserts a new image into the circular buffer (residing in MMCore) ret = GetCoreCallback()->InsertImage(this, pOutBuf, GetImageWidth(), GetImageHeight(), GetImageBytesPerPixel(), md.Serialize()); if (ret != DEVICE_OK) - { - StopSequenceAcquisition(); - // TODO: Display an error message? return ret; - } } - imagesInserted_++; - // If we already have all frames inserted tell the camera to stop - if (acqCfgCur_.CallbacksEnabled && imagesInserted_ >= imagesToAcquire_) + if (imagesInserted_ + 1 >= imagesToAcquire_) { - // TODO: Can we replace this last occurrence with StopSequenceAcquisition()? abortAcquisitionInternal(); } return ret; } -/* -* Do actual capture -* Called from the acquisition thread function -*/ -int Universal::PollingThreadRun(void) +void Universal::BuildMetadata(MM::CameraImageMetadata& md, const PvFrameInfo& frameNfo) { - START_METHOD(">>>Universal::PollingThreadRun"); + md.AddTag(MM::g_Keyword_Metadata_CameraLabel, deviceLabel_); + md.AddTag("TimeStampMsec", CDeviceUtils::ConvertToString(frameNfo.TimeStampMsec())); - int ret = DEVICE_ERR; - char dbgBuf[128]; // Debug log buffer - pollingThd_->SetStop(false); // make sure this thread's status is updated properly. + md.AddTag("PVCAM-CameraHandle", frameNfo.PvHCam()); + md.AddTag("PVCAM-FrameNr", frameNfo.PvFrameNr()); + md.AddTag("PVCAM-ReadoutTime", frameNfo.PvReadoutTime()); + md.AddTag("PVCAM-TimeStamp", frameNfo.PvTimeStamp()); + md.AddTag("PVCAM-TimeStampBOF", frameNfo.PvTimeStampBOF()); - try + if (circBufFrameRecoveryEnabled_) { - const long long usec = triggerTimeout_ * 1000000LL - + getEstimatedMaxReadoutTimeMs() * 1000LL - + (long long)(4 * GetExposure() * 1000.0); - - const MM::MMTime timeout((long)(usec / 1000000L), (long)(usec % 1000000L)); + md.AddTag("PVCAM-FrameRecovered", frameNfo.IsRecovered()); + md.AddTag("PVCAM-FramesRecoveredTotal", imagesRecovered_); + } - do + // The post-processing done above also decodes the frame metadata if supported + if (acqCfgCur_.FrameMetadataEnabled) + { + // FMD stands for Frame-MetaData, we should somehow distinguish the embedded + // metadata from other metadata and keep them grouped or close together. + const md_frame_header* fHdr = metaFrameStruct_->header; + // Selected metadata from the frame header + md.AddTag("PVCAM-FMD-Version", fHdr->version); // Use uns16, uns8 is displayed as char + md.AddTag("PVCAM-FMD-FrameNr", fHdr->frameNr); + md.AddTag("PVCAM-FMD-RoiCount", fHdr->roiCount); + md.AddTag("PVCAM-FMD-BitDepth", fHdr->bitDepth); // Use uns16, uns8 is displayed as char + const char* cKeywordColorMask = "PVCAM-FMD-ColorMask"; + switch (fHdr->colorMask) + { + case COLOR_NONE: + md.AddTag(cKeywordColorMask, "None"); + break; + case COLOR_RGGB: + md.AddTag(cKeywordColorMask, "RGGB"); + break; + case COLOR_GRBG: + md.AddTag(cKeywordColorMask, "GRBG"); + break; + case COLOR_GBRG: + md.AddTag(cKeywordColorMask, "GBRG"); + break; + case COLOR_BGGR: + md.AddTag(cKeywordColorMask, "BGGR"); + break; + default: + md.AddTag(cKeywordColorMask, "Unknown"); + break; + } + md.AddTag("PVCAM-FMD-Flags", fHdr->flags); // Use uns16, uns8 is displayed as char + if (fHdr->version >= 2) { - ret = waitForFrameConPolling(timeout); - if (ret == DEVICE_OK) + md.AddTag("PVCAM-FMD-ImageFormat", + getPvcamImageFormatString(fHdr->imageFormat)); + md.AddTag("PVCAM-FMD-ImageCompression", + getPvcamImageCompressionString(fHdr->imageCompression)); + } + if (fHdr->version < 3) + { + md.AddTag("PVCAM-FMD-ExposureTimeNs", + (ulong64)fHdr->exposureTime * fHdr->exposureTimeResNs); + md.AddTag("PVCAM-FMD-TimestampBofNs", + (ulong64)fHdr->timestampBOF * fHdr->timestampResNs); + md.AddTag("PVCAM-FMD-TimestampEofNs", + (ulong64)fHdr->timestampEOF * fHdr->timestampResNs); + } + else + { + const md_frame_header_v3* fHdr3 = + reinterpret_cast(metaFrameStruct_->header); + // Version 3 of the metadata transfers the timestamps in picoseconds. + md.AddTag("PVCAM-FMD-ExposureTimePs", fHdr3->exposureTime); + md.AddTag("PVCAM-FMD-TimestampBofPs", fHdr3->timestampBOF); + md.AddTag("PVCAM-FMD-TimestampEofPs", fHdr3->timestampEOF); + } + // Implied ROI + const rgn_type& iRoi = metaFrameStruct_->impliedRoi; + snprintf(metaRoiStr_, sizeof(metaRoiStr_), "[%u,%u,%u,%u,%u,%u]", + iRoi.s1, iRoi.s2, iRoi.sbin, iRoi.p1, iRoi.p2, iRoi.pbin); + md.AddTag("PVCAM-FMD-ImpliedRoi", metaRoiStr_); + // Per-ROI metadata + metaAllRoisStr_ = "["; + for (int i = 0; i < metaFrameStruct_->roiCount; ++i) + { + // Since we cannot add per-ROI metadata we will format the MD to a simple + // JSON array and add it as a per-Frame metadata TAG. Example: + // "[{"nr":1,"coords":[0,0,0,0,0,0]},{"nr":2,"coords":[0,0,0,0,0,0]}]" + const md_frame_roi& pRoi = metaFrameStruct_->roiArray[i]; + const md_frame_roi_header* rHdr = pRoi.header; + if (rHdr->flags & PL_MD_ROI_FLAG_INVALID) + continue; // Skip invalid regions + if (i > 0) + metaAllRoisStr_.append(","); + metaAllRoisStr_.append("{"); + snprintf(metaRoiStr_, sizeof(metaRoiStr_), + "\"nr\":%u,\"coords\":[%u,%u,%u,%u,%u,%u],\"flags\":%u", + rHdr->roiNr, + rHdr->roi.s1, rHdr->roi.s2, rHdr->roi.sbin, + rHdr->roi.p1, rHdr->roi.p2, rHdr->roi.pbin, + rHdr->flags); + metaAllRoisStr_.append(metaRoiStr_); + if (fHdr->flags & PL_MD_FRAME_FLAG_ROI_TS_SUPPORTED) { - ret = FrameAcquired(); + if (fHdr->version < 3) + { + snprintf(metaRoiStr_, sizeof(metaRoiStr_), + ",\"borNs\":%llu,\"eorNs\":%llu", + (ulong64)rHdr->timestampBOR * fHdr->roiTimestampResNs, + (ulong64)rHdr->timestampEOR * fHdr->roiTimestampResNs); + } + else + { + snprintf(metaRoiStr_, sizeof(metaRoiStr_), + ",\"bor\":%u,\"eor\":%u", + rHdr->timestampBOR, rHdr->timestampEOR); + } + metaAllRoisStr_.append(metaRoiStr_); } - else + if (/*(rHdr->flags & PL_MD_ROI_FLAG_HEADER_ONLY) && */pRoi.extMdDataSize > 0) { - break; + const md_ext_item_collection& collection = metaFrameExtData_.at(rHdr->roiNr); + const md_ext_item* item_id = collection.map[PL_MD_EXT_TAG_PARTICLE_ID]; + if (item_id) + { + snprintf(metaRoiStr_, sizeof(metaRoiStr_), + ",\"tagParticleId\":%u", *((uint32_t*)item_id->value)); + metaAllRoisStr_.append(metaRoiStr_); + } + const md_ext_item* item_m0 = collection.map[PL_MD_EXT_TAG_PARTICLE_M0]; + if (item_m0) + { + snprintf(metaRoiStr_, sizeof(metaRoiStr_), + ",\"tagParticleM0\":%u", *((uint32_t*)item_m0->value)); + metaAllRoisStr_.append(metaRoiStr_); + } + const md_ext_item* item_m2 = collection.map[PL_MD_EXT_TAG_PARTICLE_M2]; + if (item_m2) + { + snprintf(metaRoiStr_, sizeof(metaRoiStr_), + ",\"tagParticleM2\":%u", *((uint32_t*)item_m2->value)); + metaAllRoisStr_.append(metaRoiStr_); + } } + metaAllRoisStr_.append("}"); } - while (DEVICE_OK == ret && !pollingThd_->GetStop() && imagesInserted_ < imagesToAcquire_); - - snprintf( dbgBuf, sizeof(dbgBuf), - "ACQ LOOP FINISHED: thdGetStop:%u, ret:%u, imagesInserted_: %lu, imagesToAcquire_: %lu", - pollingThd_->GetStop(), ret, imagesInserted_, imagesToAcquire_); - LogAdapterMessage( __LINE__, dbgBuf ); - - if (imagesInserted_ >= imagesToAcquire_) - imagesInserted_ = 0; - PollingThreadExiting(); - pollingThd_->SetStop(true); - - START_METHOD("<<AcqFinished(this, DEVICE_OK); - pollingThd_->SetStop(true); - return ret; - } -} - -void Universal::PollingThreadExiting() throw () -{ - try - { - { - std::lock_guard pvcamGuard(g_pvcamLock); - if (!pl_exp_stop_cont(hPVCAM_, CCS_HALT)) - LogPvcamError(__LINE__, "pl_exp_stop_cont"); - if (!pl_exp_finish_seq(hPVCAM_, circBuf_.Data(), 0)) - LogPvcamError(__LINE__, "pl_exp_finish_seq"); - } - - sequenceModeReady_ = false; - isAcquiring_ = false; - - LogAdapterMessage(g_Msg_SEQUENCE_ACQUISITION_THREAD_EXITING); - // TODO: Can it ever be null? - auto *core = GetCoreCallback(); - if (core) - core->AcqFinished(this, DEVICE_OK); - } - catch (...) - { - LogAdapterMessage(__LINE__, g_Msg_EXCEPTION_IN_ON_THREAD_EXITING); + metaAllRoisStr_.append("]"); + md.AddTag("PVCAM-FMD-RoiMD", metaAllRoisStr_); } } @@ -5185,87 +5044,12 @@ int Universal::waitForFrameSeq() { START_METHOD("Universal::waitForFrameSeq"); - int nRet = DEVICE_OK; - - const long long usec = triggerTimeout_ * 1000000LL - + getEstimatedMaxReadoutTimeMs() * 1000LL - + (long long)(4 * GetExposure() * 1000.0); - - const MM::MMTime timeout((long)(usec / 1000000L), (long)(usec % 1000000L)); - - if (!acqCfgCur_.CallbacksEnabled) - { - nRet = waitForFrameSeqPolling(timeout); - } - else - { - nRet = waitForFrameSeqCallbacks(timeout); - } - - return nRet; -} - -int Universal::waitForFrameSeqPolling(const MM::MMTime& timeout) -{ - // This function can be called very often so avoid any frequent - // logging or other expensive calls. - rs_bool pvRet = FALSE; - int16 pvErr = 0; - int16 pvStatus = READOUT_NOT_ACTIVE; - uns32 pvBytesArrived = 0; - - MM::MMTime timeElapsed(0,0); - const MM::MMTime startTime = GetCurrentMMTime(); - - // Poll PVCAM for status changes. If we miss the EXPOSURE_IN_PROGRESS we - // silently skip to check READOUT_IN_PROGRESS, after that we assume that - // the frame is ready. - do - { - CDeviceUtils::SleepMs(1); - { - std::lock_guard pvcamGuard(g_pvcamLock); - pvRet = pl_exp_check_status(hPVCAM_, &pvStatus, &pvBytesArrived); - if (pvRet != PV_OK) - pvErr = pl_error_code(); - } - timeElapsed = GetCurrentMMTime() - startTime; - } - while (pvRet == TRUE && pvStatus == EXPOSURE_IN_PROGRESS && timeElapsed < timeout); - - while (pvRet == TRUE && pvStatus == READOUT_IN_PROGRESS && timeElapsed < timeout) - { - CDeviceUtils::SleepMs(1); - { - std::lock_guard pvcamGuard(g_pvcamLock); - pvRet = pl_exp_check_status(hPVCAM_, &pvStatus, &pvBytesArrived); - if (pvRet != PV_OK) - pvErr = pl_error_code(); - } - timeElapsed = GetCurrentMMTime() - startTime; - } - - if (pvRet == TRUE && pvStatus != READOUT_FAILED && timeElapsed < timeout) - return DEVICE_OK; + const unsigned int msec = + static_cast(triggerTimeout_) * 1000U + + getEstimatedMaxReadoutTimeMs() + + static_cast(4 * GetExposure()); - { - std::lock_guard pvcamGuard(g_pvcamLock); - // Abort the acquisition (ignore error if abort fails, just log it) - if (!pl_exp_abort(hPVCAM_, CCS_HALT)) - LogPvcamError(__LINE__, "waitForFrameSeqPolling(): pl_exp_abort() failed"); - } - if (pvRet == FALSE) - return LogPvcamError(__LINE__, "waitForFrameSeqPolling(): pl_exp_check_status() failed", pvErr); - if (pvStatus == READOUT_FAILED) - return LogAdapterError(ERR_FRAME_READOUT_FAILED, __LINE__, "waitForFrameSeqPolling(): pvStatus == READOUT_FAILED"); - if (timeElapsed > timeout) - return LogAdapterError(ERR_OPERATION_TIMED_OUT, __LINE__, "waitForFrameSeqPolling(): timeElapsed > timeout"); - return DEVICE_ERR; -} - -int Universal::waitForFrameSeqCallbacks(const MM::MMTime& timeout) -{ - const bool arrivedInTime = eofEvent_.Wait(static_cast(timeout.getMsec())); + const bool arrivedInTime = eofEvent_.Wait(msec); if (arrivedInTime) return DEVICE_OK; @@ -5273,78 +5057,9 @@ int Universal::waitForFrameSeqCallbacks(const MM::MMTime& timeout) std::lock_guard pvcamGuard(g_pvcamLock); // Abort the acquisition (ignore error if abort fails, just log it) if (!pl_exp_abort(hPVCAM_, CCS_HALT)) - LogPvcamError(__LINE__, "waitForFrameSeqCallbacks(): pl_exp_abort() failed"); + LogPvcamError(__LINE__, "waitForFrameSeq(): pl_exp_abort() failed"); } - return LogAdapterError(ERR_OPERATION_TIMED_OUT, __LINE__, "waitForFrameSeqCallbacks(): Readout has timed out"); -} - -int Universal::waitForFrameConPolling(const MM::MMTime& timeout) -{ - rs_bool pvRet = FALSE; - int16 pvErr = 0; - int16 pvStatus = READOUT_NOT_ACTIVE; - uns32 pvBytesArrived = 0; - uns32 pvBufferCnt = 0; - - MM::MMTime timeElapsed(0,0); - const MM::MMTime startTime = GetCurrentMMTime(); - - bool bStop = false; - - do - { - CDeviceUtils::SleepMs(1); - { - std::lock_guard pvcamGuard(g_pvcamLock); - pvRet = pl_exp_check_cont_status(hPVCAM_, &pvStatus, &pvBytesArrived, &pvBufferCnt); - if (pvRet != PV_OK) - pvErr = pl_error_code(); - } - timeElapsed = GetCurrentMMTime() - startTime; - bStop = pollingThd_->GetStop(); - } - while (pvRet && (pvStatus == EXPOSURE_IN_PROGRESS || pvStatus == READOUT_NOT_ACTIVE) && timeElapsed < timeout && !bStop); - - while (pvRet && (pvStatus == READOUT_IN_PROGRESS) && timeElapsed < timeout && !bStop) - { - CDeviceUtils::SleepMs(1); - { - std::lock_guard pvcamGuard(g_pvcamLock); - pvRet = pl_exp_check_cont_status(hPVCAM_, &pvStatus, &pvBytesArrived, &pvBufferCnt); - if (pvRet != PV_OK) - pvErr = pl_error_code(); - } - timeElapsed = GetCurrentMMTime() - startTime; - bStop = pollingThd_->GetStop(); - } - - if (bStop) - { - LogAdapterMessage( "waitForFrameConPolling(): Stop called - breaking the loop", true); - return DEVICE_ERR; - } - if (pvRet == TRUE && timeElapsed < timeout && pvStatus != READOUT_FAILED) - { - // Because we could miss the FRAME_AVAILABLE and the camera could of gone back - // to EXPOSURE_IN_PROGRESS and so on depending on how long we could of been stalled - // in this thread we only check for READOUT_FAILED and assume that because we got here - // we have one or more frames ready. - return DEVICE_OK; - } - - { - std::lock_guard pvcamGuard(g_pvcamLock); - // Abort the acquisition (ignore error if abort fails, just log it) - if (!pl_exp_abort(hPVCAM_, CCS_HALT)) - LogPvcamError(__LINE__, "waitForFrameConPolling(): pl_exp_abort() failed"); - } - if (pvRet == FALSE) - return LogPvcamError(__LINE__, "waitForFrameConPolling(): pl_exp_check_cont_status() failed", pvErr); - if (pvStatus == READOUT_FAILED) - return LogAdapterError(ERR_FRAME_READOUT_FAILED, __LINE__, "waitForFrameConPolling(): pvStatus == READOUT_FAILED"); - if (timeElapsed > timeout) - return LogAdapterError(ERR_OPERATION_TIMED_OUT, __LINE__, "waitForFrameConPolling(): timeElapsed > timeout"); - return DEVICE_ERR; + return LogAdapterError(ERR_OPERATION_TIMED_OUT, __LINE__, "waitForFrameSeq(): Readout has timed out"); } int Universal::postProcessSingleFrame(unsigned char** pOutBuf, unsigned char* pInBuf, size_t inBufSz) @@ -5483,38 +5198,29 @@ int Universal::abortAcquisitionInternal() START_METHOD("Universal::abortAcquisitionInternal"); int nRet = DEVICE_OK; - // removed redundant calls to pl_exp_stop_cont & - // pl_exp_finish_seq because they get called automatically when the thread exits. - if(isAcquiring_) + if (isAcquiring_) { if (acqCfgCur_.CircBufEnabled) { - if (acqCfgCur_.CallbacksEnabled) { + std::lock_guard pvcamGuard(g_pvcamLock); + if (!pl_exp_stop_cont( hPVCAM_, CCS_CLEAR )) { - std::lock_guard pvcamGuard(g_pvcamLock); - if (!pl_exp_stop_cont( hPVCAM_, CCS_CLEAR )) - { - nRet = DEVICE_ERR; - LogPvcamError( __LINE__, "pl_exp_stop_cont() failed" ); - } + nRet = DEVICE_ERR; + LogPvcamError( __LINE__, "pl_exp_stop_cont() failed" ); } - sequenceModeReady_ = false; - // Inform the core that the acquisition has finished - // (this also closes the shutter if used) - GetCoreCallback()->AcqFinished(this, nRet); - } - else - { - pollingThd_->SetStop(true); - pollingThd_->wait(); } + sequenceModeReady_ = false; } else { acqThd_->Pause(); } + // Inform the core that the acquisition has finished + // (this also closes the shutter if used) + GetCoreCallback()->AcqFinished(this, nRet); + customDiskWriter_->Stop(); customDiskWriterActive_ = false; @@ -6629,31 +6335,6 @@ int Universal::applyAcqConfig(bool forceSetup) setupRequired = true; } - if (acqCfgNew_.CallbacksEnabled != acqCfgCur_.CallbacksEnabled) - { - configChanged = true; - setupRequired = true; - std::lock_guard pvcamGuard(g_pvcamLock); - if (acqCfgNew_.CallbacksEnabled) - { - if (pl_cam_register_callback_ex3(hPVCAM_, PL_CALLBACK_EOF, (void*)&Universal::PvcamCallbackEofEx3, this) != PV_OK) - { - acqCfgNew_ = acqCfgCur_; // New settings not accepted, reset it back to previous state - nRet = LogPvcamError(__LINE__, "pl_cam_register_callback_ex3() failed"); - return nRet; - } - } - else - { - if (pl_cam_deregister_callback(hPVCAM_, PL_CALLBACK_EOF) != PV_OK) - { - acqCfgNew_ = acqCfgCur_; // New settings not accepted, reset it back to previous state - nRet = LogPvcamError(__LINE__, "pl_cam_deregister_callback() failed"); - return nRet; - } - } - } - if (acqCfgNew_.DiskStreamingEnabled != acqCfgCur_.DiskStreamingEnabled) { configChanged = true; diff --git a/DeviceAdapters/PVCAM/PollingThread.cpp b/DeviceAdapters/PVCAM/PollingThread.cpp deleted file mode 100644 index f522e481a..000000000 --- a/DeviceAdapters/PVCAM/PollingThread.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include "PollingThread.h" - -#include "PVCAMAdapter.h" - -PollingThread::PollingThread(Universal* camera) - : camera_(camera) -{ -} - -PollingThread::~PollingThread() -{ -} - -void PollingThread::SetStop(bool stop) -{ - stop_ = stop; -} - -bool PollingThread::GetStop() const -{ - return stop_; -} - -void PollingThread::Start() -{ - stop_ = false; - activate(); -} - -int PollingThread::svc() -{ - int ret = DEVICE_ERR; - try - { - ret = camera_->PollingThreadRun(); - } - catch(...) - { - camera_->LogAdapterMessage(g_Msg_EXCEPTION_IN_THREAD, false); - } - return ret; -} diff --git a/DeviceAdapters/PVCAM/PollingThread.h b/DeviceAdapters/PVCAM/PollingThread.h deleted file mode 100644 index 86b6f9714..000000000 --- a/DeviceAdapters/PVCAM/PollingThread.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -// MMDevice -#include "DeviceThreads.h" - -class Universal; - -/** -* Acquisition thread used for polling acquisition only. -*/ -class PollingThread : public MMDeviceThreadBase -{ -public: - explicit PollingThread(Universal* camera); - virtual ~PollingThread(); - - void SetStop(bool stop); - bool GetStop() const; - void Start(); - - virtual int svc() override; // From MMDeviceThreadBase - -private: - Universal* const camera_; - bool stop_{ true }; -}; diff --git a/DeviceAdapters/PVCAM/PvCircularBuffer.cpp b/DeviceAdapters/PVCAM/PvCircularBuffer.cpp index fbc1d6153..4eb1f4ef1 100644 --- a/DeviceAdapters/PVCAM/PvCircularBuffer.cpp +++ b/DeviceAdapters/PVCAM/PvCircularBuffer.cpp @@ -76,7 +76,8 @@ void PvCircularBuffer::Resize(size_t frameSize, int count) Reset(); } -void PvCircularBuffer::ReportFrameArrived(const PvFrameInfo& frameNfo, void* pFrameData) +void PvCircularBuffer::ReportFrameArrived( + const PvFrameInfo& frameNfo, const void* pFrameData) { // Calculate the index of the received frame in our circular buffer const int curFrameIdx = static_cast diff --git a/DeviceAdapters/PVCAM/PvCircularBuffer.h b/DeviceAdapters/PVCAM/PvCircularBuffer.h index 097d692c6..200170fcd 100644 --- a/DeviceAdapters/PVCAM/PvCircularBuffer.h +++ b/DeviceAdapters/PVCAM/PvCircularBuffer.h @@ -77,7 +77,7 @@ class PvCircularBuffer * This function notifies the buffer that a new frame has arrived and assigns * the frame metadata to correct location. */ - void ReportFrameArrived(const PvFrameInfo& frameNfo, void* pFrameData); + void ReportFrameArrived(const PvFrameInfo& frameNfo, const void* pFrameData); private: std::unique_ptr pBuffer_{ nullptr }; diff --git a/DeviceAdapters/PVCAM/Version.h b/DeviceAdapters/PVCAM/Version.h index ce37a244e..720d3edda 100644 --- a/DeviceAdapters/PVCAM/Version.h +++ b/DeviceAdapters/PVCAM/Version.h @@ -1,5 +1,5 @@ #pragma once #define PVCAM_ADAPTER_VERSION_MAJOR 1 -#define PVCAM_ADAPTER_VERSION_MINOR 3 -#define PVCAM_ADAPTER_VERSION_REVISION 85 +#define PVCAM_ADAPTER_VERSION_MINOR 4 +#define PVCAM_ADAPTER_VERSION_REVISION 1