diff --git a/DeviceAdapters/ABS/ABSCamera.cpp b/DeviceAdapters/ABS/ABSCamera.cpp index f1199c9d9..e5b268673 100644 --- a/DeviceAdapters/ABS/ABSCamera.cpp +++ b/DeviceAdapters/ABS/ABSCamera.cpp @@ -175,7 +175,7 @@ const char* g_IOPort_None = " none"; * perform most of the initialization in the Initialize() method. */ CABSCamera::CABSCamera() : -CCameraBase (), +CLegacyCameraBase (), dPhase_(0), initialized_(false), readoutUs_(1.0), diff --git a/DeviceAdapters/ABS/ABSCamera.h b/DeviceAdapters/ABS/ABSCamera.h index 702dc0428..7bc5be1c4 100644 --- a/DeviceAdapters/ABS/ABSCamera.h +++ b/DeviceAdapters/ABS/ABSCamera.h @@ -81,7 +81,7 @@ typedef std::map CIOPortNameToIndexMap; // io-po typedef std::vector CStringVector; // property names to handle transpose functions //! abs camera class -class CABSCamera : public CCameraBase +class CABSCamera : public CLegacyCameraBase { friend class CABSCameraSequenceThread; diff --git a/DeviceAdapters/AlliedVisionCamera/AlliedVisionCamera.h b/DeviceAdapters/AlliedVisionCamera/AlliedVisionCamera.h index 80505f0d6..6a8ba3796 100755 --- a/DeviceAdapters/AlliedVisionCamera/AlliedVisionCamera.h +++ b/DeviceAdapters/AlliedVisionCamera/AlliedVisionCamera.h @@ -268,7 +268,7 @@ class PixelFormatConverter /** * @brief Main Allied Vision Camera class */ -class AlliedVisionCamera : public AlliedVisionDeviceBase, AlliedVisionCamera> +class AlliedVisionCamera : public AlliedVisionDeviceBase, AlliedVisionCamera> { /////////////////////////////////////////////////////////////////////////////// // PUBLIC diff --git a/DeviceAdapters/AmScope/AmScope.cpp b/DeviceAdapters/AmScope/AmScope.cpp index 77a043190..1464d473c 100644 --- a/DeviceAdapters/AmScope/AmScope.cpp +++ b/DeviceAdapters/AmScope/AmScope.cpp @@ -105,7 +105,7 @@ MODULE_API void DeleteDevice(MM::Device* pDevice) * perform most of the initialization in the Initialize() method. */ AmScope::AmScope() : - CCameraBase (), + CLegacyCameraBase (), dPhase_(0), binning_(1), autoExposure_(1), diff --git a/DeviceAdapters/AmScope/AmScope.h b/DeviceAdapters/AmScope/AmScope.h index 5f40626e0..a86ba1378 100644 --- a/DeviceAdapters/AmScope/AmScope.h +++ b/DeviceAdapters/AmScope/AmScope.h @@ -22,7 +22,7 @@ class SequenceThread; -class AmScope : public CCameraBase +class AmScope : public CLegacyCameraBase { public: AmScope(); diff --git a/DeviceAdapters/Andor/Andor.h b/DeviceAdapters/Andor/Andor.h index 8d97ca506..f809f8cd2 100644 --- a/DeviceAdapters/Andor/Andor.h +++ b/DeviceAdapters/Andor/Andor.h @@ -74,7 +74,7 @@ class SRRFAndorCamera; ////////////////////////////////////////////////////////////////////////////// // Implementation of the MMDevice and MMCamera interfaces // -class AndorCamera : public CCameraBase +class AndorCamera : public CLegacyCameraBase { public: friend class AcqSequenceThread; diff --git a/DeviceAdapters/AndorSDK3/AndorSDK3.cpp b/DeviceAdapters/AndorSDK3/AndorSDK3.cpp index a0a0a3221..116808296 100644 --- a/DeviceAdapters/AndorSDK3/AndorSDK3.cpp +++ b/DeviceAdapters/AndorSDK3/AndorSDK3.cpp @@ -137,7 +137,7 @@ MODULE_API void DeleteDevice(MM::Device * pDevice) * perform most of the initialization in the Initialize() method. */ CAndorSDK3Camera::CAndorSDK3Camera() -: CCameraBase (), +: CLegacyCameraBase (), deviceManager(NULL), cameraDevice(NULL), bufferControl(NULL), diff --git a/DeviceAdapters/AndorSDK3/AndorSDK3.h b/DeviceAdapters/AndorSDK3/AndorSDK3.h index a7ae179be..82c0a3d06 100644 --- a/DeviceAdapters/AndorSDK3/AndorSDK3.h +++ b/DeviceAdapters/AndorSDK3/AndorSDK3.h @@ -69,7 +69,7 @@ class SRRFAndorSDK3Camera; ////////////////////////////////////////////////////////////////////////////// -class CAndorSDK3Camera : public CCameraBase +class CAndorSDK3Camera : public CLegacyCameraBase { public: CAndorSDK3Camera(); diff --git a/DeviceAdapters/ArduinoCounter/ArduinoCounter.h b/DeviceAdapters/ArduinoCounter/ArduinoCounter.h index b6eab6f59..785ee04a8 100644 --- a/DeviceAdapters/ArduinoCounter/ArduinoCounter.h +++ b/DeviceAdapters/ArduinoCounter/ArduinoCounter.h @@ -72,7 +72,7 @@ class CameraSnapThread : public MMDeviceThreadBase /* * ArduinoCounter: */ -class ArduinoCounterCamera : public CCameraBase +class ArduinoCounterCamera : public CLegacyCameraBase { public: ArduinoCounterCamera(); diff --git a/DeviceAdapters/Atik/Atik.cpp b/DeviceAdapters/Atik/Atik.cpp index 5165f8385..ef687a8b1 100644 --- a/DeviceAdapters/Atik/Atik.cpp +++ b/DeviceAdapters/Atik/Atik.cpp @@ -844,7 +844,7 @@ int Atik::PrepareSequenceAcqusition() int Atik::StartSequenceAcquisition(double interval) { //log(""); - return CCameraBase::StartSequenceAcquisition(interval); + return CLegacyCameraBase::StartSequenceAcquisition(interval); } /** @@ -853,7 +853,7 @@ int Atik::StartSequenceAcquisition(double interval) { int Atik::StopSequenceAcquisition() { //log(""); - return CCameraBase::StopSequenceAcquisition(); + return CLegacyCameraBase::StopSequenceAcquisition(); } /** @@ -864,7 +864,7 @@ int Atik::StopSequenceAcquisition() int Atik::StartSequenceAcquisition(long numImages, double interval_ms, bool stopOnOverflow) { //log(""); - return CCameraBase::StartSequenceAcquisition(numImages, interval_ms, stopOnOverflow); + return CLegacyCameraBase::StartSequenceAcquisition(numImages, interval_ms, stopOnOverflow); } /* @@ -905,7 +905,7 @@ int Atik::InsertImage() bool Atik::IsCapturing() { log(""); - return CCameraBase::IsCapturing(); + return CLegacyCameraBase::IsCapturing(); } diff --git a/DeviceAdapters/Atik/Atik.h b/DeviceAdapters/Atik/Atik.h index e8a24673e..a3f696fc0 100644 --- a/DeviceAdapters/Atik/Atik.h +++ b/DeviceAdapters/Atik/Atik.h @@ -37,7 +37,7 @@ class SequenceThread; -class Atik : public CCameraBase +class Atik : public CLegacyCameraBase { public: Atik(); diff --git a/DeviceAdapters/Basler/BaslerPylonCamera.cpp b/DeviceAdapters/Basler/BaslerPylonCamera.cpp index c0b71a755..01af747ba 100644 --- a/DeviceAdapters/Basler/BaslerPylonCamera.cpp +++ b/DeviceAdapters/Basler/BaslerPylonCamera.cpp @@ -147,7 +147,7 @@ MODULE_API void DeleteDevice(MM::Device* pDevice) * Constructor. */ BaslerCamera::BaslerCamera() : - CCameraBase(), + CNewAPICameraBase(), maxWidth_(0), maxHeight_(0), exposure_us_(0), @@ -159,7 +159,6 @@ BaslerCamera::BaslerCamera() : temperatureState_("Undefined"), reverseX_("0"), reverseY_("0"), - imgBuffer_(NULL), Buffer4ContinuesShot(NULL), colorCamera_(true), pixelType_("Undefined"), @@ -167,8 +166,9 @@ BaslerCamera::BaslerCamera() : shutterMode_("None"), nodeMap_(NULL), initialized_(false), - pTempHandler_(NULL), - camera_(NULL) + pEventHandler_(NULL), + camera_(NULL), + multiFrameAcqCount_(0) { // call the base class method to set-up default error codes/messages InitializeDefaultErrorMessages(); @@ -213,10 +213,6 @@ BaslerCamera::BaslerCamera() : BaslerCamera::~BaslerCamera() { - if (imgBuffer_ != NULL) - { - free(imgBuffer_); - } if (Buffer4ContinuesShot != NULL) { free(Buffer4ContinuesShot); @@ -253,6 +249,25 @@ std::string BaslerCamera::EnumToString(EDeviceAccessiblityInfo AccessiblityInfo) return "Unknown"; } +std::vector BaslerCamera::GetAvailableEnumValues(const GenApi::IEnumeration& node) { + std::vector values; + + // Create a non-const copy to work with + GenApi::IEnumeration* ptrNode = const_cast(&node); + + NodeList_t entries; + ptrNode->GetEntries(entries); + + for (NodeList_t::iterator it = entries.begin(); it != entries.end(); ++it) { + CEnumEntryPtr pEnumEntry(*it); + if (IsAvailable(*it)) { + values.push_back(pEnumEntry->GetSymbolic().c_str()); + } + } + return values; +} + + /** * Initializes the hardware. */ @@ -306,6 +321,7 @@ int BaslerCamera::Initialize() } + stringstream msg; msg << "using camera " << camera_->GetDeviceInfo().GetFriendlyName(); AddToLog(msg.str()); @@ -338,23 +354,75 @@ int BaslerCamera::Initialize() nodeMap_ = &camera_->GetNodeMap(); + //// Create standard properties + InitOrSyncTriggerSelectorStandardProperty(); + InitOrSyncTriggerModeStandardProperty(); + InitOrSyncTriggerSourceStandardProperty(); + InitOrSyncTriggerActivationStandardProperty(); + InitOrSyncTriggerDelayStandardProperty(); + InitOrSyncTriggerOverlapStandardProperty(); + + InitOrSyncExposureModeStandardProperty(); + InitOrSyncExposureTimeStandardProperty(); + + InitOrSyncLineSelectorStandardProperty(); + InitOrSyncLineModeStandardProperty(); + InitOrSyncLineInverterStandardProperty(); + InitOrSyncLineSourceStandardProperty(); + InitOrSyncLineStatusStandardProperty(); + + InitOrSyncEventSelectorStandardProperty(); + InitOrSyncEventNotificationStandardProperty(); + + InitOrSyncAcquisitionFrameRateStandardProperty(); + InitOrSyncAcquisitionFrameRateEnableStandardProperty(); + + InitOrSyncAcquisitionStatusSelectorStandardProperty(); + InitOrSyncAcquisitionStatusStandardProperty(); + + InitOrSyncAcquisitionBurstFrameCountStandardProperty(); + + if (camera_->EventSelector.IsWritable()) { - if (camera_->EventSelector.CanSetValue(EventSelector_CriticalTemperature) && - camera_->EventSelector.CanSetValue(EventSelector_OverTemperature) - ) - { - pTempHandler_ = new CTempCameraEventHandler(this); - // register camera events - camera_->RegisterCameraEventHandler(pTempHandler_, "EventCriticalTemperatureData", TempCritical, RegistrationMode_Append, Cleanup_None); - camera_->RegisterCameraEventHandler(pTempHandler_, "EventOverTemperatureData", TempOverTemp, RegistrationMode_Append, Cleanup_None); + + pEventHandler_ = new CMMCameraEventHandler(this); + + // Register the handler for each type of event that might be + std::vector eventSelectorValues = GetAvailableEnumValues(camera_->EventSelector); + for (size_t i = 0; i < eventSelectorValues.size(); i++) { + const auto& eventValue = eventSelectorValues[i]; + // Construct the event data node name + std::string eventDataNodeName = "Event" + eventValue + "Data"; + + intptr_t eventId = static_cast(i + 1); // +1 to avoid 0 + + // Register the event handler + try { + camera_->RegisterCameraEventHandler( + pEventHandler_, + eventDataNodeName.c_str(), + eventId, + RegistrationMode_Append, + Cleanup_None + ); + + // Store the mapping between eventId and eventValue for later use in the handler + eventIdToName_[eventId] = eventValue; + + + } + catch (const GenericException&) { + return DEVICE_ERR; + } } + } //Register Genicam Callback to be informed if on any changes on resulting frame rate. if (IsAvailable(camera_->ResultingFrameRate)) { - GenApi::Register(camera_->ResultingFrameRate.GetNode(), *this, &BaslerCamera::ResultingFramerateCallback); + GenApi::Register(camera_->ResultingFrameRate.GetNode(), *this, &BaslerCamera::ResultingFramerateCallback); } else if (IsAvailable(camera_->ResultingFrameRateAbs)) { @@ -362,7 +430,7 @@ int BaslerCamera::Initialize() GenApi::Register(camera_->ResultingFrameRateAbs.GetNode(), *this, &BaslerCamera::ResultingFramerateCallback); } - //Register Camera events + // //Register Camera events if (IsAvailable(camera_->DeviceTemperature)) { @@ -415,7 +483,6 @@ int BaslerCamera::Initialize() //Exposure CFloatPtr exposure(nodeMap_->GetNode("ExposureTime")); - CFloatPtr ExposureTimeAbs(nodeMap_->GetNode("ExposureTimeAbs")); if (IsAvailable(exposure)) { // USB cameras @@ -424,12 +491,6 @@ int BaslerCamera::Initialize() exposureMin_ = exposure->GetMin(); } - else if (IsAvailable(ExposureTimeAbs)) - { // GigE - exposure_us_ = ExposureTimeAbs->GetValue(); - exposureMax_ = ExposureTimeAbs->GetMax(); - exposureMin_ = ExposureTimeAbs->GetMin(); - } /* CPropertyAction* pAct = new CPropertyAction(this, &BaslerCamera::OnExposure); ret = CreateProperty("Exposure", CDeviceUtils::ConvertToString((long)exposure->GetValue()), MM::Float, false, pAct); SetPropertyLimits("Exposure", exposureMin_, exposureMax_); @@ -853,10 +914,6 @@ int BaslerCamera::Initialize() if (ret != DEVICE_OK) return ret; - //preparation for snaps - ResizeSnapBuffer(); - //preparation for sequences - //camera_->RegisterImageEventHandler( &ImageHandler_, RegistrationMode_Append, Cleanup_Delete); initialized_ = true; } catch (const GenericException & e) @@ -867,9 +924,123 @@ int BaslerCamera::Initialize() << e.GetDescription() << endl; return DEVICE_ERR; } + + + ImageHandler_ = new BufferInserter(this); + camera_->RegisterImageEventHandler(ImageHandler_, RegistrationMode_Append, Cleanup_Delete); + + + return DEVICE_OK; +} + +////////////////////////////////////////////////////////////// +///////////////////// New Camera API //////////////////////// +////////////////////////////////////////////////////////////// + + +std::string BaslerCamera::NodeToString(const char* str) const { + CEnumerationPtr ptr(nodeMap_->GetNode(str)); + gcstring val = ptr->ToString(); + std::string s = val.c_str(); + return s; +} + +int BaslerCamera::TriggerSoftware(){ + // Send the trigger + camera_->ExecuteSoftwareTrigger(); + return DEVICE_OK; +} + +int BaslerCamera::AcquisitionArm(int frameCount) +{ + multiFrameAcqCount_ = frameCount; + if (frameCount == 1) { + // 1 frame + CEnumParameter(nodeMap_, "AcquisitionMode").SetValue("SingleFrame"); + } else { + if (frameCount <= 0) { + // Arbitrary number of frames + CEnumParameter(nodeMap_, "AcquisitionMode").SetValue("Continuous"); + } else { + // A GenICam "MultiFrame" acquisition mode + // Basler does not implement GenICam exactly here, so this is also "Continuous" mode + CEnumParameter(nodeMap_, "AcquisitionMode").SetValue("Continuous"); + } + } + return DEVICE_OK; } +int BaslerCamera::AcquisitionStart() +{ + sequenceFrameCounter_ = 0; + // This tells the core to open the current shutter. + // Perhaps not entirely neccessary with the new API since acquisitions should + // be explicitly armed before starting, which means that the application + // can open the shutter as needed just before starting the acquisition. + int ret = GetCoreCallback()->PrepareForAcq(this); + if (ret != DEVICE_OK) + return ret; + + // The GenICam AcquisitionStart gets called automatically by StartGrabbing + if (multiFrameAcqCount_ == 1) { + // For single frame acquisition, still use background thread but limit to 1 frame + camera_->StartGrabbing(1, GrabStrategy_OneByOne, GrabLoop_ProvidedByInstantCamera); + } else if (multiFrameAcqCount_ <= 0) { + // For continuous acquisition (until stopped) + camera_->StartGrabbing(GrabStrategy_OneByOne, GrabLoop_ProvidedByInstantCamera); + } else { + // For multi-frame acquisition with specific count + camera_->StartGrabbing(multiFrameAcqCount_, GrabStrategy_OneByOne, GrabLoop_ProvidedByInstantCamera); + } + return DEVICE_OK; +} + +int BaslerCamera::AcquisitionStop() +{ + // The GenICam AcquisitionStop gets called automatically by StopGrabbing + // CCommandParameter(nodeMap_, "AcquisitionStop").Execute(); + camera_->StopGrabbing(); + // This tells the core that the acquisition is finished. + // so that it can close the current shutter. + int ret = GetCoreCallback()->AcqFinished(this, 0); + if (ret != DEVICE_OK) + return ret; + return DEVICE_OK; +} + +int BaslerCamera::AcquisitionAbort() +{ + // TODO: should this be different? + return AcquisitionStop(); +} + +// //TODO +// double BaslerCamera::GetRollingShutterLineOffset() const +// { +// // Return a default value of 0 since the feature is not supported +// return 0.0; +// } + +// int BaslerCamera::SetRollingShutterLineOffset(double offset_us) +// { +// // Return error code as expected +// return DEVICE_NOT_SUPPORTED; +// } + +// unsigned BaslerCamera::GetRollingShutterActiveLines() const +// { +// // Return a default value of 0 since the feature is not supported +// return 0; +// } + +// int BaslerCamera::SetRollingShutterActiveLines(unsigned numLines) +// { +// // Return error code as expected +// return DEVICE_NOT_SUPPORTED; +// } + +////////////////////////////////////////////////////////////// int BaslerCamera::CheckForBinningMode(CPropertyAction* pAct) @@ -914,8 +1085,9 @@ int BaslerCamera::SetProperty(const char* name, const char* value) */ int BaslerCamera::Shutdown() { - if (!camera_) + if (camera_) { + camera_->DeregisterImageEventHandler(ImageHandler_); camera_->Close(); delete camera_; } @@ -924,90 +1096,6 @@ int BaslerCamera::Shutdown() return DEVICE_OK; } -int BaslerCamera::SnapImage() -{ - try - { - camera_->StartGrabbing(1, GrabStrategy_OneByOne, GrabLoop_ProvidedByUser); - // This smart pointer will receive the grab result data. - //When all smart pointers referencing a Grab Result Data object go out of scope, the grab result's image buffer is reused for grabbing - CGrabResultPtr ptrGrabResult; - int timeout_ms = 5000; - if (!camera_->RetrieveResult(timeout_ms, ptrGrabResult, TimeoutHandling_ThrowException)) { - return DEVICE_ERR; - } - if (!ptrGrabResult->GrabSucceeded()) { - return DEVICE_ERR; - } - if (ptrGrabResult->GetPayloadSize() != imgBufferSize_) - {// due to parameter change on binning - ResizeSnapBuffer(); - } - CopyToImageBuffer(ptrGrabResult); - - } - catch (const GenericException & e) - { - // Error handling. - AddToLog(e.GetDescription()); - cerr << "An exception occurred." << endl - << e.GetDescription() << endl; - } - return DEVICE_OK; -} - -void BaslerCamera::CopyToImageBuffer(CGrabResultPtr ptrGrabResult) -{ - const char* subject("Bayer"); - bool IsByerFormat = false; - string currentPixelFormat = Pylon::CPixelTypeMapper::GetNameByPixelType(ptrGrabResult->GetPixelType()); - std::size_t found = currentPixelFormat.find(subject); - if (found != std::string::npos) - { - IsByerFormat = true; - } - if (ptrGrabResult->GetPixelType() == PixelType_Mono8) - { - // Workaround : OnPixelType call back will not be fired always. - nComponents_ = 1; - bitDepth_ = 8; - - //copy image buffer to a snap buffer allocated by device adapter - const void* buffer = ptrGrabResult->GetBuffer(); - memcpy(imgBuffer_, buffer, GetImageBufferSize()); - SetProperty(MM::g_Keyword_PixelType, g_PixelType_8bit); - } - else if (ptrGrabResult->GetPixelType() == PixelType_Mono16 || ptrGrabResult->GetPixelType() == PixelType_Mono10 || ptrGrabResult->GetPixelType() == PixelType_Mono12) - { - //copy image buffer to a snap buffer allocated by device adapter - void* buffer = ptrGrabResult->GetBuffer(); - memcpy(imgBuffer_, buffer, ptrGrabResult->GetPayloadSize()); - SetProperty(MM::g_Keyword_PixelType, g_PixelType_12bit); - } - else if (IsByerFormat || ptrGrabResult->GetPixelType() == PixelType_RGB8packed) - { - nComponents_ = 4; - bitDepth_ = 8; - converter->Convert(imgBuffer_, GetImageBufferSize(), ptrGrabResult); - SetProperty(MM::g_Keyword_PixelType, g_PixelType_8bitRGBA); - } - else if (ptrGrabResult->GetPixelType() == PixelType_BGR8packed) - { - nComponents_ = 4; - bitDepth_ = 8; - SetProperty(MM::g_Keyword_PixelType, g_PixelType_8bitBGR); - RGBPackedtoRGB(imgBuffer_, ptrGrabResult); - } -} - -/** -* Returns pixel data. -*/ -const unsigned char* BaslerCamera::GetImageBuffer() -{ - return (unsigned char*)imgBuffer_; -} - unsigned BaslerCamera::GetImageWidth() const { const CIntegerPtr width = nodeMap_->GetNode("Width"); @@ -1068,14 +1156,6 @@ unsigned BaslerCamera::GetBitDepth() const return 0; } -/** -* Returns the size in bytes of the image buffer. -*/ -long BaslerCamera::GetImageBufferSize() const -{ - return GetImageWidth() * GetImageHeight() * GetImageBytesPerPixel(); -} - /** * Sets the camera Region Of Interest. * @param x - top-left corner coordinate @@ -1112,6 +1192,12 @@ int BaslerCamera::SetROI(unsigned x, unsigned y, unsigned xSize, unsigned ySize) return DEVICE_OK; } + +unsigned BaslerCamera::GetNumberOfComponents() const +{ + return nComponents_; +}; + /** * Returns the actual dimensions of the current ROI. */ @@ -1144,43 +1230,6 @@ int BaslerCamera::ClearROI() return DEVICE_OK; } -/** -* Returns the current exposure setting in milliseconds. -* Required by the MM::Camera API. -*/ -double BaslerCamera::GetExposure() const -{ - return exposure_us_ / 1000.0; -} - -/** -* Sets exposure in milliseconds. -* Required by the MM::Camera API. -*/ -void BaslerCamera::SetExposure(double exp) -{ - exp *= 1000; //convert to us - if (exp > exposureMax_) { - exp = exposureMax_; - } - else if (exp < exposureMin_) { - exp = exposureMin_; - } - - CFloatPtr ExposureTime(nodeMap_->GetNode("ExposureTime")); - CIntegerPtr ExposureTimeRaw(nodeMap_->GetNode("ExposureTimeRaw")); - if (camera_->IsGigE() && IsWritable(ExposureTimeRaw)) - { - ExposureTimeRaw->SetValue((int64_t)exp); - exposure_us_ = exp; - } - else if (camera_->IsUsb() && IsWritable(ExposureTime)) - { - ExposureTime->SetValue(exp); - exposure_us_ = exp; - } -} - /** * Returns the current binning factor. */ @@ -1198,66 +1247,68 @@ int BaslerCamera::SetBinning(int binFactor) return DEVICE_UNSUPPORTED_COMMAND; } -int BaslerCamera::StartSequenceAcquisition(long numImages, double /* interval_ms */, bool /* stopOnOverflow */) { - - ImageHandler_ = new CircularBufferInserter(this); - camera_->RegisterImageEventHandler(ImageHandler_, RegistrationMode_Append, Cleanup_Delete); - int ret = GetCoreCallback()->PrepareForAcq(this); - if (ret != DEVICE_OK) { - return ret; - } - camera_->StartGrabbing(numImages, GrabStrategy_OneByOne, GrabLoop_ProvidedByInstantCamera); - return DEVICE_OK; -} - -int BaslerCamera::StartSequenceAcquisition(double /* interval_ms */) { - ImageHandler_ = new CircularBufferInserter(this); - camera_->RegisterImageEventHandler(ImageHandler_, RegistrationMode_Append, Cleanup_Delete); - int ret = GetCoreCallback()->PrepareForAcq(this); - if (ret != DEVICE_OK) { - return ret; - } - camera_->StartGrabbing(GrabStrategy_OneByOne, GrabLoop_ProvidedByInstantCamera); - return DEVICE_OK; -} - bool BaslerCamera::IsCapturing() { return camera_->IsGrabbing(); } -int BaslerCamera::StopSequenceAcquisition() -{ - if (camera_->IsGrabbing()) - { - camera_->StopGrabbing(); - GetCoreCallback()->AcqFinished(this, 0); - camera_->DeregisterImageEventHandler(ImageHandler_); - } - return DEVICE_OK; -} - -int BaslerCamera::PrepareSequenceAcqusition() -{ - // nothing to prepare - return DEVICE_OK; -} - -void BaslerCamera::ResizeSnapBuffer() { - - free(imgBuffer_); - long bytes = GetImageBufferSize(); - imgBuffer_ = malloc(bytes); - imgBufferSize_ = bytes; -} - - - - ////// // Action handlers /////////////////////////////////////////////////////////////////////////////// + +int BaslerCamera::OnTriggerSelector(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + try + { + string TriggerSelector_; + CEnumerationPtr TriggerSelector(nodeMap_->GetNode("TriggerSelector")); + if (TriggerSelector != NULL && IsAvailable(TriggerSelector)) + { + if (eAct == MM::AfterSet) { + pProp->Get(TriggerSelector_); + + // On Basler cameras, FrameBurstStart and Acquisition Start are identical and + // the name available depends on the camera model. So here, we swap them as needed + if ((TriggerSelector_ == MM::g_keyword_TriggerSelectorFrameBurstStart) || + (TriggerSelector_ == MM::g_keyword_TriggerSelectorAcquisitionStart)) { + + if (CEnumParameter(nodeMap_, "TriggerSelector").CanSetValue(MM::g_keyword_TriggerSelectorFrameBurstStart)) { + TriggerSelector->FromString(MM::g_keyword_TriggerSelectorFrameBurstStart); + } else if (CEnumParameter(nodeMap_, "TriggerSelector").CanSetValue(MM::g_keyword_TriggerSelectorAcquisitionStart)) { + TriggerSelector->FromString(MM::g_keyword_TriggerSelectorAcquisitionStart); + } else { + return DEVICE_ERR; + } + } else { + // For all other trigger selectors, set directly + TriggerSelector->FromString(TriggerSelector_.c_str()); + } + + // Update the property with the actual value that was set + pProp->Set(TriggerSelector->ToString().c_str()); + + // Update other properties that may depend on the trigger selector + InitOrSyncTriggerModeStandardProperty(); + InitOrSyncTriggerSourceStandardProperty(); + InitOrSyncTriggerActivationStandardProperty(); + InitOrSyncExposureModeStandardProperty(); + InitOrSyncEventSelectorStandardProperty(); + } + else if (eAct == MM::BeforeGet) { + pProp->Set(TriggerSelector->ToString().c_str()); + } + } + } + catch (const GenericException & e) + { + // Error handling. + AddToLog(e.GetDescription()); + return DEVICE_ERR; + } + return DEVICE_OK; +} + int BaslerCamera::OnTriggerSource(MM::PropertyBase* pProp, MM::ActionType eAct) { string TriggerSource_; @@ -1265,6 +1316,13 @@ int BaslerCamera::OnTriggerSource(MM::PropertyBase* pProp, MM::ActionType eAct) pProp->Get(TriggerSource_); CEnumerationPtr TriggerSource(nodeMap_->GetNode("TriggerSource")); TriggerSource->FromString(TriggerSource_.c_str()); + + // update other property values: + InitOrSyncTriggerModeStandardProperty(); + InitOrSyncTriggerActivationStandardProperty(); + InitOrSyncExposureModeStandardProperty(); + InitOrSyncEventSelectorStandardProperty(); + } else if (eAct == MM::BeforeGet) { CEnumerationPtr TriggerSource(nodeMap_->GetNode("TriggerSource")); @@ -1279,6 +1337,320 @@ int BaslerCamera::OnTriggerSource(MM::PropertyBase* pProp, MM::ActionType eAct) return DEVICE_OK; } +int BaslerCamera::HandleEnumerationProperty(MM::PropertyBase* pProp, MM::ActionType eAct, + const char* nodeName) +{ + try + { + string valueStr; + CEnumerationPtr enumNode(nodeMap_->GetNode(nodeName)); + if (enumNode != NULL && IsAvailable(enumNode)) + { + if (eAct == MM::AfterSet) { + pProp->Get(valueStr); + enumNode->FromString(valueStr.c_str()); + pProp->Set(enumNode->ToString().c_str()); + } + else if (eAct == MM::BeforeGet) { + pProp->Set(enumNode->ToString().c_str()); + } + } + } + catch (const GenericException & e) + { + // Error handling. + AddToLog(e.GetDescription()); + return DEVICE_ERR; + } + return DEVICE_OK; +} + +int BaslerCamera::OnTriggerMode(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + return HandleEnumerationProperty(pProp, eAct, "TriggerMode"); +} + +int BaslerCamera::OnTriggerActivation(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + return HandleEnumerationProperty(pProp, eAct, "TriggerActivation"); +} + +int BaslerCamera::OnTriggerOverlap(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + return HandleEnumerationProperty(pProp, eAct, "TriggerOverlap"); +} + +int BaslerCamera::OnExposureMode(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + return HandleEnumerationProperty(pProp, eAct, "ExposureMode"); +} + +int BaslerCamera::OnLineSource(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + return HandleEnumerationProperty(pProp, eAct, "LineSource"); +} + +int BaslerCamera::OnEventNotification(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + return HandleEnumerationProperty(pProp, eAct, "EventNotification"); +} + +int BaslerCamera::OnAcquisitionStatusSelector(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + return HandleEnumerationProperty(pProp, eAct, "AcquisitionStatusSelector"); +} + +int BaslerCamera::OnAcquisitionStatus(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + try + { + if (eAct == MM::BeforeGet) { + CBooleanPtr AcquisitionStatus(nodeMap_->GetNode("AcquisitionStatus")); + if (AcquisitionStatus != NULL && IsAvailable(AcquisitionStatus)) { + pProp->Set(AcquisitionStatus->ToString().c_str()); + } + } + // No AfterSet implementation since AcquisitionStatus is typically read-only + } + catch (const GenericException & e) + { + // Error handling. + AddToLog(e.GetDescription()); + return DEVICE_ERR; + } + return DEVICE_OK; +} + +int BaslerCamera::OnAcquisitionBurstFrameCount(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + try + { + CIntegerPtr AcquisitionBurstFrameCount(nodeMap_->GetNode("AcquisitionBurstFrameCount")); + if (AcquisitionBurstFrameCount != NULL && IsAvailable(AcquisitionBurstFrameCount)) + { + if (eAct == MM::AfterSet) { + std::string countStr; + pProp->Get(countStr); + AcquisitionBurstFrameCount->SetValue(std::stoi(countStr)); + } + else if (eAct == MM::BeforeGet) { + pProp->Set(std::to_string(AcquisitionBurstFrameCount->GetValue()).c_str()); + } + } + } + catch (const GenericException & e) + { + // Error handling. + AddToLog(e.GetDescription()); + return DEVICE_ERR; + } + return DEVICE_OK; +} + +int BaslerCamera::OnLineMode(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + int ret = HandleEnumerationProperty(pProp, eAct, "LineMode"); + if (ret != DEVICE_OK) { + return ret; + } + // Line source is only valid if line mode is set to Output + return InitOrSyncLineSourceStandardProperty(); +} + +int BaslerCamera::OnTriggerDelay(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + try + { + CFloatPtr TriggerDelay(nodeMap_->GetNode("TriggerDelay")); + if (TriggerDelay != NULL && IsAvailable(TriggerDelay)) + { + if (eAct == MM::AfterSet) { + double delay; + pProp->Get(delay); + TriggerDelay->SetValue(delay); + } + else if (eAct == MM::BeforeGet) { + pProp->Set(TriggerDelay->GetValue()); + } + } + } + catch (const GenericException & e) + { + // Error handling. + AddToLog(e.GetDescription()); + return DEVICE_ERR; + } + return DEVICE_OK; +} + + +int BaslerCamera::OnExposureTime(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + try + { + if (eAct == MM::AfterSet) + { + // Get the new exposure value + pProp->Get(exposure_us_); + + // Ensure exposure is within allowed range + if (exposure_us_ > exposureMax_) { + exposure_us_ = exposureMax_; + } + else if (exposure_us_ < exposureMin_) { + exposure_us_ = exposureMin_; + } + + // Try to set the exposure value - use TrySetValue which is safer + // as it will return the actual value that was set + if (IsWritable(camera_->ExposureTime)) + { + exposure_us_ = camera_->ExposureTime.TrySetValue(exposure_us_); + } + else if (IsWritable(camera_->ExposureTimeAbs)) + { + exposure_us_ = camera_->ExposureTimeAbs.TrySetValue(exposure_us_); + } + + // Update the property display with the actual value that was set + pProp->Set(exposure_us_); + } + else if (eAct == MM::BeforeGet) + { + // Get the current exposure value + if (IsAvailable(camera_->ExposureTime)) + { + exposure_us_ = camera_->ExposureTime.GetValue(); + } + else if (IsAvailable(camera_->ExposureTimeAbs)) + { + exposure_us_ = camera_->ExposureTimeAbs.GetValue(); + } + pProp->Set(exposure_us_); + } + } + catch (const GenericException & e) + { + // Error handling + AddToLog(e.GetDescription()); + cerr << "An exception occurred." << endl + << e.GetDescription() << endl; + return DEVICE_ERR; + } + return DEVICE_OK; +} + +int BaslerCamera::OnLineSelector(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + try + { + string LineSelector_; + CEnumerationPtr LineSelector(nodeMap_->GetNode("LineSelector")); + if (LineSelector != NULL && IsAvailable(LineSelector)) + { + if (eAct == MM::AfterSet) { + pProp->Get(LineSelector_); + LineSelector->FromString(LineSelector_.c_str()); + pProp->Set(LineSelector->ToString().c_str()); + + // Update other properties that may depend on the line selector + InitOrSyncLineModeStandardProperty(); + InitOrSyncLineInverterStandardProperty(); + InitOrSyncLineSourceStandardProperty(); + } + else if (eAct == MM::BeforeGet) { + pProp->Set(LineSelector->ToString().c_str()); + } + } + } + catch (const GenericException & e) + { + // Error handling. + AddToLog(e.GetDescription()); + return DEVICE_ERR; + } + return DEVICE_OK; +} + +int BaslerCamera::OnLineInverter(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + try + { + CBooleanPtr LineInverter(nodeMap_->GetNode("LineInverter")); + if (LineInverter != NULL && IsAvailable(LineInverter)) + { + if (eAct == MM::AfterSet) { + string value; + pProp->Get(value); + LineInverter->FromString(value.c_str()); + } + else if (eAct == MM::BeforeGet) { + pProp->Set(LineInverter->ToString().c_str()); + } + } + } + catch (const GenericException & e) + { + // Error handling. + AddToLog(e.GetDescription()); + return DEVICE_ERR; + } + return DEVICE_OK; +} + + +int BaslerCamera::OnLineStatus(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + try + { + if (eAct == MM::BeforeGet) { + CBooleanPtr LineStatus(nodeMap_->GetNode("LineStatus")); + if (LineStatus != NULL && IsAvailable(LineStatus)) { + pProp->Set(LineStatus->ToString().c_str()); + } + } + // No AfterSet implementation since LineStatus is typically read-only + } + catch (const GenericException & e) + { + // Error handling. + AddToLog(e.GetDescription()); + return DEVICE_ERR; + } + return DEVICE_OK; +} + +int BaslerCamera::OnEventSelector(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + try + { + string EventSelector_; + CEnumerationPtr EventSelector(nodeMap_->GetNode("EventSelector")); + if (EventSelector != NULL && IsAvailable(EventSelector)) + { + if (eAct == MM::AfterSet) { + pProp->Get(EventSelector_); + EventSelector->FromString(EventSelector_.c_str()); + pProp->Set(EventSelector->ToString().c_str()); + + // Update EventNotification property values which depend on EventSelector + InitOrSyncEventNotificationStandardProperty(); + } + else if (eAct == MM::BeforeGet) { + pProp->Set(EventSelector->ToString().c_str()); + } + } + } + catch (const GenericException & e) + { + // Error handling. + AddToLog(e.GetDescription()); + return DEVICE_ERR; + } + return DEVICE_OK; +} + + int BaslerCamera::OnBinningMode(MM::PropertyBase* pProp, MM::ActionType eAct) { CEnumerationPtr BinningModeHorizontal(nodeMap_->GetNode("BinningModeHorizontal")); @@ -1430,48 +1802,6 @@ int BaslerCamera::OnWidth(MM::PropertyBase* pProp, MM::ActionType eAct) } -int BaslerCamera::OnExposure(MM::PropertyBase* pProp, MM::ActionType eAct) -{ - if (eAct == MM::AfterSet) - { - if (IsWritable(camera_->ExposureTime) || IsWritable(camera_->ExposureTimeAbs)) - { - try - { - pProp->Get(exposure_us_); - exposure_us_ = camera_->ExposureTime.TrySetValue(exposure_us_); - exposure_us_ = camera_->ExposureTimeAbs.TrySetValue(exposure_us_); - } - catch (const GenericException & e) - { - // Error handling. - AddToLog(e.GetDescription()); - cerr << "An exception occurred." << endl - << e.GetDescription() << endl; - } - } - } - else if (eAct == MM::BeforeGet) { - - try { - if (IsAvailable(camera_->ExposureTime) && IsAvailable(camera_->ExposureTimeAbs)) - { - exposure_us_ = camera_->ExposureTime.GetValueOrDefault(exposure_us_); - exposure_us_ = camera_->ExposureTimeAbs.GetValueOrDefault(exposure_us_); - pProp->Set(exposure_us_); - } - } - catch (const GenericException & e) - { - // Error handling. - AddToLog(e.GetDescription()); - cerr << "An exception occurred." << endl - << e.GetDescription() << endl; - } - } - return DEVICE_OK; -} - int BaslerCamera::OnBinning(MM::PropertyBase* pProp, MM::ActionType eAct) { CIntegerPtr BinningHorizontal(nodeMap_->GetNode("BinningHorizontal")); @@ -1532,11 +1862,6 @@ int BaslerCamera::OnBinning(MM::PropertyBase* pProp, MM::ActionType eAct) return DEVICE_OK; } -unsigned BaslerCamera::GetNumberOfComponents() const -{ - return nComponents_; -}; - int BaslerCamera::OnPixelType(MM::PropertyBase* pProp, MM::ActionType eAct) { bool isGrabing = camera_->IsGrabbing(); @@ -1638,37 +1963,9 @@ int BaslerCamera::OnSensorReadoutMode(MM::PropertyBase* pProp, MM::ActionType eA cout << "An exception occurred." << endl << e.GetDescription() << endl; cerr << "An exception occurred." << endl << e.GetDescription() << endl; + return DEVICE_ERR; } - return DEVICE_OK; -} - -int BaslerCamera::OnTriggerMode(MM::PropertyBase* pProp, MM::ActionType eAct) -{ - try - { - string TriggerMode_; - CEnumerationPtr TriggerMode(nodeMap_->GetNode("TriggerMode")); - if (TriggerMode != NULL && IsAvailable(TriggerMode)) - { - if (eAct == MM::AfterSet) { - pProp->Get(TriggerMode_); - TriggerMode->FromString(TriggerMode_.c_str()); - pProp->Set(TriggerMode->ToString().c_str()); - } - else if (eAct == MM::BeforeGet) { - pProp->Set(TriggerMode->ToString().c_str()); - } - } - } - catch (const GenericException & e) - { - // Error handling. - AddToLog(e.GetDescription()); - cout << "An exception occurred." << endl << e.GetDescription() << endl; - cerr << "An exception occurred." << endl - << e.GetDescription() << endl; - } - return DEVICE_OK; + return DEVICE_OK; } int BaslerCamera::OnTemperature(MM::PropertyBase* pProp, MM::ActionType eAct) @@ -2095,32 +2392,44 @@ void BaslerCamera::UpdateTemperature() } } -CTempCameraEventHandler::CTempCameraEventHandler(BaslerCamera* dev) : +CMMCameraEventHandler::CMMCameraEventHandler(BaslerCamera* dev) : dev_(dev) {} -void CTempCameraEventHandler::OnCameraEvent(CBaslerUniversalInstantCamera& camera, intptr_t userProvidedId, GenApi::INode* pNode) +void CMMCameraEventHandler::OnCameraEvent(CBaslerUniversalInstantCamera& camera, intptr_t userProvidedId, GenApi::INode* pNode) { - { - (void)camera; - (void)userProvidedId; - CParameter value(pNode); - if (value.IsValid()) + std::string eventName = dev_->eventIdToName_[userProvidedId]; + + CParameter value( pNode ); + + // Convert Events from the Camera to callbacks to MMCore + // The events available are camera-model specific. More can be added here. + + if (eventName == "ExposureEnd") { + if (camera.EventExposureEndFrameID.IsReadable()) { - stringstream msg; - msg << "Temperature State of camera has been changed to : " << camera.TemperatureState.ToString(); - dev_->AddToLog(msg.str()); - dev_->UpdateTemperature(); + int64_t frameId = camera.EventExposureEndFrameID.GetValue(); + int64_t timestamp = camera.EventExposureEndTimestamp.GetValue(); + std::string msg = "Exposure End event. FrameID: " + std::to_string(frameId) + " Timestamp: " + std::to_string(timestamp); + dev_->GetCoreCallback()->OnCameraEvent(dev_, eventName.c_str(), + static_cast(timestamp), static_cast(frameId), msg.c_str()); + } + } else if (eventName == "FrameStart") { + if (camera.EventFrameStartFrameID.IsReadable()) { + int64_t frameId = camera.EventFrameStartFrameID.GetValue(); + int64_t timestamp = camera.EventFrameStartTimestamp.GetValue(); + std::string msg = "Frame Start event. FrameID: " + std::to_string(frameId) + " Timestamp: " + std::to_string(timestamp); + dev_->GetCoreCallback()->OnCameraEvent(dev_, eventName.c_str(), + static_cast(timestamp), static_cast(frameId), msg.c_str()); } - std::cout << std::endl; } } -CircularBufferInserter::CircularBufferInserter(BaslerCamera* dev) : +BufferInserter::BufferInserter(BaslerCamera* dev) : dev_(dev) {} -void CircularBufferInserter::OnImageGrabbed(CInstantCamera& /* camera */, const CGrabResultPtr& ptrGrabResult) +void BufferInserter::OnImageGrabbed(CInstantCamera& /* camera */, const CGrabResultPtr& ptrGrabResult) { // char label[MM::MaxStrLength]; @@ -2132,6 +2441,9 @@ void CircularBufferInserter::OnImageGrabbed(CInstantCamera& /* camera */, const md.put(MM::g_Keyword_Metadata_ROI_Y, CDeviceUtils::ConvertToString((long)ptrGrabResult->GetHeight())); md.put(MM::g_Keyword_Metadata_ImageNumber, CDeviceUtils::ConvertToString((long)ptrGrabResult->GetImageNumber())); md.put(MM::g_Keyword_Metadata_Exposure, dev_->GetExposure()); + //int64_t frameId, timestamp; + //GetEventFrameIdAndTimestamp(nodeName, camera, frameId, timestamp); + //md.put(MM::g_Keyword_Metadata_TimeInCore, CDeviceUtils::ConvertToString((long)timestamp)); // Image grabbed successfully? if (ptrGrabResult->GrabSucceeded()) { @@ -2190,5 +2502,577 @@ void CircularBufferInserter::OnImageGrabbed(CInstantCamera& /* camera */, const std::stringstream ss; ss << "Error: " << ptrGrabResult->GetErrorCode() << " " << ptrGrabResult->GetErrorDescription() << endl; dev_->AddToLog(ss.str()); + // Tell the camera to stop grabbing + dev_->AcquisitionStop(); + } + dev_->sequenceFrameCounter_++; + if (dev_->sequenceFrameCounter_ == dev_->multiFrameAcqCount_) + { + dev_->GetCoreCallback()->AcqFinished(dev_, 0); + } +} + +int BaslerCamera::InitOrSyncTriggerSelectorStandardProperty() +{ + CEnumerationPtr node(nodeMap_->GetNode("TriggerSelector")); + bool cameraHasIt = IsAvailable(node); + bool propertyExists = HasStandardProperty("TriggerSelector"); + + if (cameraHasIt && !propertyExists) { + // Camera has the feature but property doesn't exist yet - initialize it + std::string currentValue = node->ToString(); + CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnTriggerSelector); + std::vector values = GetAvailableEnumValues(*node); + return CreateTriggerSelectorStandardProperty(currentValue.c_str(), values, action); + } + else if (!cameraHasIt && propertyExists) { + // Camera doesn't have the feature but property exists - remove it + SkipTriggerSelectorStandardProperty(); + } + else if (cameraHasIt && propertyExists) { + // Both exist - just update the values + std::vector values = GetAvailableEnumValues(*node); + return SetTriggerSelectorStandardPropertyValues(values); + } else { + // It doesn't exist, mark to skip + SkipTriggerSelectorStandardProperty(); + } + + return DEVICE_OK; +} + +int BaslerCamera::InitOrSyncTriggerModeStandardProperty() +{ + CEnumerationPtr node(nodeMap_->GetNode("TriggerMode")); + bool cameraHasIt = IsAvailable(node); + bool propertyExists = HasStandardProperty("TriggerMode"); + + if (cameraHasIt && !propertyExists) { + // Camera has the feature but property doesn't exist yet - initialize it + std::string currentValue = node->ToString(); + CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnTriggerMode); + std::vector values = GetAvailableEnumValues(*node); + return CreateTriggerModeStandardProperty(currentValue.c_str(), values, action); + } + else if (!cameraHasIt && propertyExists) { + // Camera doesn't have the feature but property exists - remove it + SkipTriggerModeStandardProperty(); + } + else if (cameraHasIt && propertyExists) { + // Both exist - just update the values + std::vector values = GetAvailableEnumValues(*node); + return SetTriggerModeStandardPropertyValues(values); + } else { + // It doesn't exist, mark to skip + SkipTriggerModeStandardProperty(); + } + + return DEVICE_OK; +} + +int BaslerCamera::InitOrSyncTriggerSourceStandardProperty() +{ + CEnumerationPtr node(nodeMap_->GetNode("TriggerSource")); + bool cameraHasIt = IsAvailable(node); + bool propertyExists = HasStandardProperty("TriggerSource"); + + if (cameraHasIt && !propertyExists) { + // Camera has the feature but property doesn't exist yet - initialize it + std::string currentValue = node->ToString(); + CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnTriggerSource); + std::vector values = GetAvailableEnumValues(*node); + return CreateTriggerSourceStandardProperty(currentValue.c_str(), values, action); + } + else if (!cameraHasIt && propertyExists) { + // Camera doesn't have the feature but property exists - remove it + SkipTriggerSourceStandardProperty(); + } + else if (cameraHasIt && propertyExists) { + // Both exist - just update the values + std::vector values = GetAvailableEnumValues(*node); + return SetTriggerSourceStandardPropertyValues(values); + } else { + // It doesn't exist, mark to skip + SkipTriggerSourceStandardProperty(); + } + + return DEVICE_OK; +} + +int BaslerCamera::InitOrSyncTriggerActivationStandardProperty() +{ + CEnumerationPtr node(nodeMap_->GetNode("TriggerActivation")); + bool cameraHasIt = IsAvailable(node); + bool propertyExists = HasStandardProperty("TriggerActivation"); + + if (cameraHasIt && !propertyExists) { + // Camera has the feature but property doesn't exist yet - initialize it + std::string currentValue = node->ToString(); + CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnTriggerActivation); + std::vector values = GetAvailableEnumValues(*node); + return CreateTriggerActivationStandardProperty(currentValue.c_str(), values, action); + } + else if (!cameraHasIt && propertyExists) { + // Camera doesn't have the feature but property exists - remove it + SkipTriggerActivationStandardProperty(); + } + else if (cameraHasIt && propertyExists) { + // Both exist - just update the values + std::vector values = GetAvailableEnumValues(*node); + return SetTriggerActivationStandardPropertyValues(values); + } else { + // It doesn't exist, mark to skip + SkipTriggerActivationStandardProperty(); + } + + return DEVICE_OK; +} + +int BaslerCamera::InitOrSyncTriggerDelayStandardProperty() +{ + CFloatPtr node(nodeMap_->GetNode("TriggerDelay")); + bool cameraHasIt = IsAvailable(node); + bool propertyExists = HasStandardProperty("TriggerDelay"); + + if (cameraHasIt && !propertyExists) { + // Camera has the feature but property doesn't exist yet - initialize it + double currentValue = node->GetValue(); + double minValue = node->GetMin(); + double maxValue = node->GetMax(); + std::string strValue = CDeviceUtils::ConvertToString(currentValue); + CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnTriggerDelay); + return CreateTriggerDelayStandardProperty(strValue.c_str(), minValue, maxValue, action); + } + else if (!cameraHasIt && propertyExists) { + // Camera doesn't have the feature but property exists - remove it + SkipTriggerDelayStandardProperty(); + } else { + // It doesn't exist, mark to skip + SkipTriggerDelayStandardProperty(); + } + + // For float properties, we don't need to update allowed values, as they're set by min/max + + return DEVICE_OK; +} + +int BaslerCamera::InitOrSyncTriggerOverlapStandardProperty() +{ + CEnumerationPtr node(nodeMap_->GetNode("TriggerOverlap")); + bool cameraHasIt = IsAvailable(node); + bool propertyExists = HasStandardProperty("TriggerOverlap"); + + if (cameraHasIt && !propertyExists) { + // Camera has the feature but property doesn't exist yet - initialize it + std::string currentValue = node->ToString(); + CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnTriggerOverlap); + std::vector values = GetAvailableEnumValues(*node); + return CreateTriggerOverlapStandardProperty(currentValue.c_str(), values, action); + } + else if (!cameraHasIt && propertyExists) { + // Camera doesn't have the feature but property exists - remove it + SkipTriggerOverlapStandardProperty(); + } + else if (cameraHasIt && propertyExists) { + // Both exist - just update the values + std::vector values = GetAvailableEnumValues(*node); + return SetTriggerOverlapStandardPropertyValues(values); + } else { + // It doesn't exist, mark to skip + SkipTriggerOverlapStandardProperty(); + } + + return DEVICE_OK; +} + +int BaslerCamera::InitOrSyncExposureModeStandardProperty() +{ + CEnumerationPtr node(nodeMap_->GetNode("ExposureMode")); + bool cameraHasIt = IsAvailable(node); + bool propertyExists = HasStandardProperty("ExposureMode"); + + if (cameraHasIt && !propertyExists) { + // Camera has the feature but property doesn't exist yet - initialize it + std::string currentValue = node->ToString(); + CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnExposureMode); + std::vector values = GetAvailableEnumValues(*node); + return CreateExposureModeStandardProperty(currentValue.c_str(), values, action); + } + else if (!cameraHasIt && propertyExists) { + // Camera doesn't have the feature but property exists - remove it + SkipExposureModeStandardProperty(); + } + else if (cameraHasIt && propertyExists) { + // Both exist - just update the values + std::vector values = GetAvailableEnumValues(*node); + return SetExposureModeStandardPropertyValues(values); + } else { + // It doesn't exist, mark to skip + SkipExposureModeStandardProperty(); + } + + return DEVICE_OK; +} + +int BaslerCamera::InitOrSyncExposureTimeStandardProperty() +{ + CFloatPtr node(nodeMap_->GetNode("ExposureTime")); + bool cameraHasIt = IsAvailable(node); + bool propertyExists = HasStandardProperty("ExposureTime"); + + if (cameraHasIt && !propertyExists) { + // Camera has the feature but property doesn't exist yet - initialize it + double currentValue = node->GetValue(); + double minValue = node->GetMin(); + double maxValue = node->GetMax(); + std::string strValue = CDeviceUtils::ConvertToString(currentValue); + CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnExposureTime); + return CreateExposureTimeStandardProperty(strValue.c_str(), minValue, maxValue, action); + } + else if (!cameraHasIt && propertyExists) { + // Camera doesn't have the feature but property exists - remove it + SkipExposureTimeStandardProperty(); + } else { + // It doesn't exist, mark to skip + SkipExposureTimeStandardProperty(); + } + + // For float properties, we don't need to update allowed values, as they're set by min/max + + return DEVICE_OK; +} + +int BaslerCamera::InitOrSyncLineSelectorStandardProperty() +{ + CEnumerationPtr node(nodeMap_->GetNode("LineSelector")); + bool cameraHasIt = IsAvailable(node); + bool propertyExists = HasStandardProperty("LineSelector"); + + if (cameraHasIt && !propertyExists) { + // Camera has the feature but property doesn't exist yet - initialize it + std::string currentValue = node->ToString(); + CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnLineSelector); + std::vector values = GetAvailableEnumValues(*node); + return CreateLineSelectorStandardProperty(currentValue.c_str(), values, action); + } + else if (!cameraHasIt && propertyExists) { + // Camera doesn't have the feature but property exists - remove it + SkipLineSelectorStandardProperty(); + } + else if (cameraHasIt && propertyExists) { + // Both exist - just update the values + std::vector values = GetAvailableEnumValues(*node); + return SetLineSelectorStandardPropertyValues(values); + } else { + // It doesn't exist, mark to skip + SkipLineSelectorStandardProperty(); + } + + return DEVICE_OK; +} + +int BaslerCamera::InitOrSyncLineModeStandardProperty() +{ + CEnumerationPtr node(nodeMap_->GetNode("LineMode")); + bool cameraHasIt = IsAvailable(node); + bool propertyExists = HasStandardProperty("LineMode"); + + if (cameraHasIt && !propertyExists) { + // Camera has the feature but property doesn't exist yet - initialize it + std::string currentValue = node->ToString(); + CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnLineMode); + std::vector values = GetAvailableEnumValues(*node); + return CreateLineModeStandardProperty(currentValue.c_str(), values, action); + } + else if (!cameraHasIt && propertyExists) { + // Camera doesn't have the feature but property exists - remove it + SkipLineModeStandardProperty(); + } + else if (cameraHasIt && propertyExists) { + // Both exist - just update the values + std::vector values = GetAvailableEnumValues(*node); + return SetLineModeStandardPropertyValues(values); + } else { + // It doesn't exist, mark to skip + SkipLineModeStandardProperty(); + } + + return DEVICE_OK; +} + +int BaslerCamera::InitOrSyncLineInverterStandardProperty() +{ + CBooleanPtr node(nodeMap_->GetNode("LineInverter")); + bool cameraHasIt = IsAvailable(node); + bool propertyExists = HasStandardProperty("LineInverter"); + + if (cameraHasIt && !propertyExists) { + // Camera has the feature but property doesn't exist yet - initialize it + std::string currentValue = node->ToString(); + CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnLineInverter); + std::vector values = {"0", "1"}; + return CreateLineInverterStandardProperty(currentValue.c_str(), values, action); + } + else if (!cameraHasIt && propertyExists) { + // Camera doesn't have the feature but property exists - remove it + SkipLineInverterStandardProperty(); + } + else if (cameraHasIt && propertyExists) { + // Both exist - just update the values + std::vector values = {"0", "1"}; + return SetLineInverterStandardPropertyValues(values); + } else { + // It doesn't exist, mark to skip + SkipLineInverterStandardProperty(); + } + + return DEVICE_OK; +} + +int BaslerCamera::InitOrSyncLineSourceStandardProperty() +{ + CEnumerationPtr node(nodeMap_->GetNode("LineSource")); + bool cameraHasIt = IsAvailable(node); + bool propertyExists = HasStandardProperty("LineSource"); + + if (cameraHasIt && !propertyExists) { + // Camera has the feature but property doesn't exist yet - initialize it + std::string currentValue = node->ToString(); + CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnLineSource); + std::vector values = GetAvailableEnumValues(*node); + return CreateLineSourceStandardProperty(currentValue.c_str(), values, action); + } + else if (!cameraHasIt && propertyExists) { + // Camera doesn't have the feature but property exists - remove it + SkipLineSourceStandardProperty(); + } + else if (cameraHasIt && propertyExists) { + // Both exist - just update the values + std::vector values = GetAvailableEnumValues(*node); + return SetLineSourceStandardPropertyValues(values); + } else { + // It doesn't exist, mark to skip + SkipLineSourceStandardProperty(); + } + + return DEVICE_OK; +} + +int BaslerCamera::InitOrSyncLineStatusStandardProperty() +{ + CBooleanPtr node(nodeMap_->GetNode("LineStatus")); + bool cameraHasIt = IsAvailable(node); + bool propertyExists = HasStandardProperty("LineStatus"); + + if (cameraHasIt && !propertyExists) { + // Camera has the feature but property doesn't exist yet - initialize it + std::string currentValue = node->ToString(); + CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnLineStatus); + // LineStatus is read-only + return CreateLineStatusStandardProperty(currentValue.c_str(), action); + } + else if (!cameraHasIt && propertyExists) { + // Camera doesn't have the feature but property exists - remove it + SkipLineStatusStandardProperty(); + } else { + // It doesn't exist, mark to skip + SkipLineStatusStandardProperty(); + } + + // No need to update values for read-only properties + + return DEVICE_OK; +} + +int BaslerCamera::InitOrSyncEventSelectorStandardProperty() +{ + CEnumerationPtr node(nodeMap_->GetNode("EventSelector")); + bool cameraHasIt = IsAvailable(node); + bool propertyExists = HasStandardProperty("EventSelector"); + + if (cameraHasIt && !propertyExists) { + // Camera has the feature but property doesn't exist yet - initialize it + std::string currentValue = node->ToString(); + CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnEventSelector); + std::vector values = GetAvailableEnumValues(*node); + return CreateEventSelectorStandardProperty(currentValue.c_str(), values, action); + } + else if (!cameraHasIt && propertyExists) { + // Camera doesn't have the feature but property exists - remove it + SkipEventSelectorStandardProperty(); + } + else if (cameraHasIt && propertyExists) { + // Both exist - just update the values + std::vector values = GetAvailableEnumValues(*node); + return SetEventSelectorStandardPropertyValues(values); + } else { + // It doesn't exist, mark to skip + SkipEventSelectorStandardProperty(); + } + + return DEVICE_OK; +} + +int BaslerCamera::InitOrSyncEventNotificationStandardProperty() +{ + CEnumerationPtr node(nodeMap_->GetNode("EventNotification")); + bool cameraHasIt = IsAvailable(node); + bool propertyExists = HasStandardProperty("EventNotification"); + + if (cameraHasIt && !propertyExists) { + // Camera has the feature but property doesn't exist yet - initialize it + std::string currentValue = node->ToString(); + CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnEventNotification); + // This one has required values of On and Off + return CreateEventNotificationStandardProperty(currentValue.c_str(), action); + } + else if (!cameraHasIt && propertyExists) { + // Camera doesn't have the feature but property exists - remove it + SkipEventNotificationStandardProperty(); + } + else if (cameraHasIt && propertyExists) { + // Both exist - just update the values + std::vector values = GetAvailableEnumValues(*node); + return SetEventNotificationStandardPropertyValues(values); + } else { + // It doesn't exist, mark to skip + SkipEventNotificationStandardProperty(); + } + + return DEVICE_OK; +} + +int BaslerCamera::InitOrSyncAcquisitionFrameRateStandardProperty() +{ + CFloatPtr node(nodeMap_->GetNode("AcquisitionFrameRate")); + bool cameraHasIt = IsAvailable(node); + bool propertyExists = HasStandardProperty("AcquisitionFrameRate"); + + if (cameraHasIt && !propertyExists) { + // Camera has the feature but property doesn't exist yet - initialize it + double currentValue = node->GetValue(); + double minValue = node->GetMin(); + double maxValue = node->GetMax(); + std::string strValue = CDeviceUtils::ConvertToString(currentValue); + CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnAcqFramerate); + return CreateAcquisitionFrameRateStandardProperty(strValue.c_str(), minValue, maxValue, action); + } + else if (!cameraHasIt && propertyExists) { + // Camera doesn't have the feature but property exists - remove it + SkipAcquisitionFrameRateStandardProperty(); + } + else if (cameraHasIt && propertyExists) { + + } else { + // It doesn't exist, mark to skip + SkipAcquisitionFrameRateStandardProperty(); } + + return DEVICE_OK; } + +int BaslerCamera::InitOrSyncAcquisitionFrameRateEnableStandardProperty() +{ + CBooleanPtr node(nodeMap_->GetNode("AcquisitionFrameRateEnable")); + bool cameraHasIt = IsAvailable(node); + bool propertyExists = HasStandardProperty("AcquisitionFrameRateEnable"); + + if (cameraHasIt && !propertyExists) { + // Camera has the feature but property doesn't exist yet - initialize it + std::string currentValue = node->ToString(); + CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnAcqFramerateEnable); + return CreateAcquisitionFrameRateEnableStandardProperty(currentValue.c_str(), action); + } + else if (!cameraHasIt && propertyExists) { + // Camera doesn't have the feature but property exists - remove it + SkipAcquisitionFrameRateEnableStandardProperty(); + } + else if (cameraHasIt && propertyExists) { + + } else { + // It doesn't exist, mark to skip + SkipAcquisitionFrameRateEnableStandardProperty(); + } + + return DEVICE_OK; +} + +int BaslerCamera::InitOrSyncAcquisitionStatusSelectorStandardProperty() +{ + CEnumerationPtr node(nodeMap_->GetNode("AcquisitionStatusSelector")); + bool cameraHasIt = IsAvailable(node); + bool propertyExists = HasStandardProperty("AcquisitionStatusSelector"); + + if (cameraHasIt && !propertyExists) { + // Camera has the feature but property doesn't exist yet - initialize it + std::string currentValue = node->ToString(); + CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnAcquisitionStatusSelector); + std::vector values = GetAvailableEnumValues(*node); + return CreateAcquisitionStatusSelectorStandardProperty(currentValue.c_str(), values, action); + } + else if (!cameraHasIt && propertyExists) { + // Camera doesn't have the feature but property exists - remove it + SkipAcquisitionStatusSelectorStandardProperty(); + } + else if (cameraHasIt && propertyExists) { + // Both exist - just update the values + std::vector values = GetAvailableEnumValues(*node); + return SetAcquisitionStatusSelectorStandardPropertyValues(values); + } + else { + // It doesn't exist, mark to skip + SkipAcquisitionStatusSelectorStandardProperty(); + } + + return DEVICE_OK; +} + +int BaslerCamera::InitOrSyncAcquisitionStatusStandardProperty() +{ + CBooleanPtr node(nodeMap_->GetNode("AcquisitionStatus")); + bool cameraHasIt = IsAvailable(node); + bool propertyExists = HasStandardProperty("AcquisitionStatus"); + + if (cameraHasIt && !propertyExists) { + // Camera has the feature but property doesn't exist yet - initialize it + std::string currentValue = node->ToString(); + CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnAcquisitionStatus); + return CreateAcquisitionStatusStandardProperty(currentValue.c_str(), action); + } + else if (!cameraHasIt && propertyExists) { + // Camera doesn't have the feature but property exists - remove it + SkipAcquisitionStatusStandardProperty(); + } + else { + // It doesn't exist, mark to skip + SkipAcquisitionStatusStandardProperty(); + } + + return DEVICE_OK; +} + +int BaslerCamera::InitOrSyncAcquisitionBurstFrameCountStandardProperty() +{ + CIntegerPtr node(nodeMap_->GetNode("AcquisitionBurstFrameCount")); + bool cameraHasIt = IsAvailable(node); + bool propertyExists = HasStandardProperty("AcquisitionBurstFrameCount"); + + if (cameraHasIt && !propertyExists) { + // Camera has the feature but property doesn't exist yet - initialize it + std::string currentValue = node->ToString(); + CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnAcquisitionBurstFrameCount); + return CreateAcquisitionBurstFrameCountStandardProperty(currentValue.c_str(), action); + } + else if (!cameraHasIt && propertyExists) { + // Camera doesn't have the feature but property exists - remove it + SkipAcquisitionBurstFrameCountStandardProperty(); + } + else { + // It doesn't exist, mark to skip + SkipAcquisitionBurstFrameCountStandardProperty(); + } + + return DEVICE_OK; +} + + diff --git a/DeviceAdapters/Basler/BaslerPylonCamera.h b/DeviceAdapters/Basler/BaslerPylonCamera.h index 22838e55a..26ee0e004 100644 --- a/DeviceAdapters/Basler/BaslerPylonCamera.h +++ b/DeviceAdapters/Basler/BaslerPylonCamera.h @@ -40,7 +40,6 @@ #include #include #include "ImageMetadata.h" -#include "ImgBuffer.h" #include #include @@ -61,9 +60,9 @@ enum ////////////////////////////////////////////////////////////////////////////// //Callback class for putting frames in circular buffer as they arrive -class CTempCameraEventHandler; -class CircularBufferInserter; -class BaslerCamera : public CCameraBase { +class CMMCameraEventHandler; +class BufferInserter; +class BaslerCamera : public CNewAPICameraBase { public: BaslerCamera(); ~BaslerCamera(); @@ -77,58 +76,65 @@ class BaslerCamera : public CCameraBase { bool Busy() {return false;} - // MMCamera API + // MMCamera API (shared between old and new API) // ------------ - int SnapImage(); - const unsigned char* GetImageBuffer(); - void* Buffer4ContinuesShot; - unsigned GetNumberOfComponents() const; + unsigned GetNumberOfComponents() const; unsigned GetImageWidth() const; unsigned GetImageHeight() const; unsigned GetImageBytesPerPixel() const; unsigned GetBitDepth() const; - long GetImageBufferSize() const; - double GetExposure() const; - void SetExposure(double exp); int SetROI(unsigned x, unsigned y, unsigned xSize, unsigned ySize); int GetROI(unsigned& x, unsigned& y, unsigned& xSize, unsigned& ySize); int ClearROI(); - void ReduceImageSize(int64_t Width, int64_t Height); int GetBinning() const; int SetBinning(int binSize); - int IsExposureSequenceable(bool& seq) const {seq = false; return DEVICE_OK;} - void RGBPackedtoRGB(void* destbuffer, const CGrabResultPtr& ptrGrabResult); - //int SetProperty(const char* name, const char* value); - int CheckForBinningMode(CPropertyAction *pAct); - void AddToLog(std::string msg); - void CopyToImageBuffer(CGrabResultPtr image); - CImageFormatConverter *converter; - CircularBufferInserter *ImageHandler_; - std::string EnumToString(EDeviceAccessiblityInfo DeviceAccessiblityInfo); - void UpdateTemperature(); - - /** - * Starts continuous acquisition. - */ - int StartSequenceAcquisition(long numImages, double interval_ms, bool stopOnOverflow); - int StartSequenceAcquisition(double interval_ms); - int StopSequenceAcquisition(); - int PrepareSequenceAcqusition(); - - /** - * Flag to indicate whether Sequence Acquisition is currently running. - * Return true when Sequence acquisition is active, false otherwise - */ bool IsCapturing(); - //Genicam Callback - void ResultingFramerateCallback(GenApi::INode* pNode); + + + ///////////////////////////////////// + ///////// New Camera API /////////// + int TriggerSoftware(); + + int AcquisitionArm(int frameCount); + int AcquisitionStart(); + int AcquisitionStop(); + int AcquisitionAbort(); + + //////////////////////// + ///// End new Camera API + //////////////////////// // action interface // ---------------- + // Standard properties + int OnTriggerSelector(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnTriggerMode(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnTriggerSource(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnTriggerActivation(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnTriggerDelay(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnExposureMode(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnLineSelector(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnLineMode(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnLineInverter(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnLineSource(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnLineStatus(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnTriggerOverlap(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnExposureTime(MM::PropertyBase* pProp, MM::ActionType eAct); + + int OnEventSelector(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnEventNotification(MM::PropertyBase* pProp, MM::ActionType eAct); + + int OnAcquisitionStatusSelector(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnAcquisitionStatus(MM::PropertyBase* pProp, MM::ActionType eAct); + + int OnAcquisitionBurstFrameCount(MM::PropertyBase* pProp, MM::ActionType eAct); + + + // non-standard properties int OnAcqFramerate(MM::PropertyBase* pProp, MM::ActionType eAct); int OnAcqFramerateEnable(MM::PropertyBase* pProp, MM::ActionType eAct); int OnAutoExpore(MM::PropertyBase* pProp, MM::ActionType eAct); @@ -136,7 +142,6 @@ class BaslerCamera : public CCameraBase { int OnBinning(MM::PropertyBase* pProp, MM::ActionType eAct); int OnBinningMode(MM::PropertyBase* pProp, MM::ActionType eAct); int OnDeviceLinkThroughputLimit(MM::PropertyBase* pProp, MM::ActionType eAct); - int OnExposure(MM::PropertyBase* pProp, MM::ActionType eAct); int OnGain(MM::PropertyBase* pProp, MM::ActionType eAct); int OnHeight(MM::PropertyBase* pProp, MM::ActionType eAct); int OnInterPacketDelay(MM::PropertyBase* pProp, MM::ActionType eAct); @@ -150,14 +155,49 @@ class BaslerCamera : public CCameraBase { int OnShutterMode(MM::PropertyBase* pProp, MM::ActionType eAct); int OnTemperature(MM::PropertyBase* pProp, MM::ActionType eAct); int OnTemperatureState(MM::PropertyBase* pProp, MM::ActionType eAct); - int OnTriggerMode(MM::PropertyBase* pProp, MM::ActionType eAct); - int OnTriggerSource(MM::PropertyBase* pProp, MM::ActionType eAct); int OnWidth(MM::PropertyBase* pProp, MM::ActionType eAct); + + + + // Convenience functions + std::string NodeToString(const char* str) const; + + int HandleEnumerationProperty(MM::PropertyBase* pProp, MM::ActionType eAct, const char* nodeName); + + + void ResizeSnapBuffer(); + void ReduceImageSize(int64_t Width, int64_t Height); + int CheckForBinningMode(CPropertyAction *pAct); + void AddToLog(std::string msg); + void CopyToImageBuffer(CGrabResultPtr image); + void UpdateTemperature(); + void RGBPackedtoRGB(void* destbuffer, const CGrabResultPtr& ptrGrabResult); + + + //Genicam Callback + void ResultingFramerateCallback(GenApi::INode* pNode); + + + CImageFormatConverter *converter; + BufferInserter *ImageHandler_; + std::string EnumToString(EDeviceAccessiblityInfo DeviceAccessiblityInfo); + void* Buffer4ContinuesShot; + std::map eventIdToName_; + unsigned sequenceFrameCounter_; // Counter for frames in current sequence + unsigned multiFrameAcqCount_; + + + + std::vector GetAvailableEnumValues(const GenApi::IEnumeration& node); + + private: + + CBaslerUniversalInstantCamera *camera_; - CTempCameraEventHandler *pTempHandler_; + CMMCameraEventHandler *pEventHandler_; int nComponents_; unsigned bitDepth_; @@ -183,17 +223,34 @@ class BaslerCamera : public CCameraBase { std::string temperatureState_; - void* imgBuffer_; - long imgBufferSize_; - ImgBuffer img_; INodeMap* nodeMap_; bool initialized_; + //MM::MMTime startTime_; - void ResizeSnapBuffer(); - + int InitOrSyncTriggerSelectorStandardProperty(); + int InitOrSyncTriggerModeStandardProperty(); + int InitOrSyncTriggerSourceStandardProperty(); + int InitOrSyncTriggerActivationStandardProperty(); + int InitOrSyncTriggerDelayStandardProperty(); + int InitOrSyncTriggerOverlapStandardProperty(); + int InitOrSyncExposureModeStandardProperty(); + int InitOrSyncExposureTimeStandardProperty(); + int InitOrSyncLineSelectorStandardProperty(); + int InitOrSyncLineModeStandardProperty(); + int InitOrSyncLineInverterStandardProperty(); + int InitOrSyncLineSourceStandardProperty(); + int InitOrSyncLineStatusStandardProperty(); + int InitOrSyncEventSelectorStandardProperty(); + int InitOrSyncEventNotificationStandardProperty(); + int InitOrSyncAcquisitionFrameRateStandardProperty(); + int InitOrSyncAcquisitionFrameRateEnableStandardProperty(); + int InitOrSyncAcquisitionStatusSelectorStandardProperty(); + int InitOrSyncAcquisitionStatusStandardProperty(); + int InitOrSyncAcquisitionBurstFrameCountStandardProperty(); + }; //Enumeration used for distinguishing different events. @@ -208,22 +265,22 @@ static const uint32_t c_countOfImagesToGrab = 5; // Example handler for camera events. -class CTempCameraEventHandler : public CBaslerUniversalCameraEventHandler +class CMMCameraEventHandler : public CBaslerUniversalCameraEventHandler { private: BaslerCamera* dev_; public: - CTempCameraEventHandler(BaslerCamera* dev); + CMMCameraEventHandler(BaslerCamera* dev); virtual void OnCameraEvent(CBaslerUniversalInstantCamera& camera, intptr_t userProvidedId, GenApi::INode* pNode); }; -class CircularBufferInserter : public CImageEventHandler { +class BufferInserter : public CImageEventHandler { private: BaslerCamera* dev_; public: - CircularBufferInserter(BaslerCamera* dev); + BufferInserter(BaslerCamera* dev); virtual void OnImageGrabbed( CInstantCamera& camera, const CGrabResultPtr& ptrGrabResult); }; diff --git a/DeviceAdapters/BaumerOptronic/BaumerOptronic.cpp b/DeviceAdapters/BaumerOptronic/BaumerOptronic.cpp index 6fc0276e4..67b81d2fc 100644 --- a/DeviceAdapters/BaumerOptronic/BaumerOptronic.cpp +++ b/DeviceAdapters/BaumerOptronic/BaumerOptronic.cpp @@ -1544,7 +1544,7 @@ unsigned int __stdcall mSeqEventHandler(void* pArguments) * perform most of the initialization in the Initialize() method. */ CBaumerOptronic::CBaumerOptronic() : - CCameraBase(), + CLegacyCameraBase(), initialized_(false), pWorkerThread_(NULL), stopOnOverflow_(false) diff --git a/DeviceAdapters/BaumerOptronic/BaumerOptronic.h b/DeviceAdapters/BaumerOptronic/BaumerOptronic.h index ab167b63a..f0fc7a2e5 100644 --- a/DeviceAdapters/BaumerOptronic/BaumerOptronic.h +++ b/DeviceAdapters/BaumerOptronic/BaumerOptronic.h @@ -59,7 +59,7 @@ enum WorkerState { class BOImplementationThread; -class CBaumerOptronic : public CCameraBase +class CBaumerOptronic : public CLegacyCameraBase { public: diff --git a/DeviceAdapters/DahengGalaxy/ClassGalaxy.cpp b/DeviceAdapters/DahengGalaxy/ClassGalaxy.cpp index 43189b0ec..2698af124 100644 --- a/DeviceAdapters/DahengGalaxy/ClassGalaxy.cpp +++ b/DeviceAdapters/DahengGalaxy/ClassGalaxy.cpp @@ -46,7 +46,7 @@ MODULE_API void DeleteDevice(MM::Device* pDevice) } ClassGalaxy::ClassGalaxy() : - CCameraBase(), + CLegacyCameraBase(), ImageHandler_(0), Width_(0), Height_(0), diff --git a/DeviceAdapters/DahengGalaxy/ClassGalaxy.h b/DeviceAdapters/DahengGalaxy/ClassGalaxy.h index 66724fc96..f43149640 100644 --- a/DeviceAdapters/DahengGalaxy/ClassGalaxy.h +++ b/DeviceAdapters/DahengGalaxy/ClassGalaxy.h @@ -31,7 +31,7 @@ class CircularBufferInserter; -class MODULE_API ClassGalaxy : public CCameraBase +class MODULE_API ClassGalaxy : public CLegacyCameraBase { public: diff --git a/DeviceAdapters/DemoCamera/DemoCamera.cpp b/DeviceAdapters/DemoCamera/DemoCamera.cpp index cde5082f7..549d871d4 100644 --- a/DeviceAdapters/DemoCamera/DemoCamera.cpp +++ b/DeviceAdapters/DemoCamera/DemoCamera.cpp @@ -210,7 +210,7 @@ MODULE_API void DeleteDevice(MM::Device* pDevice) * perform most of the initialization in the Initialize() method. */ CDemoCamera::CDemoCamera() : - CCameraBase (), + CLegacyCameraBase (), exposureMaximum_(10000.0), dPhase_(0), initialized_(false), @@ -308,6 +308,16 @@ int CDemoCamera::Initialize() else LogMessage(NoHubError); + // Example of how to create standard properties + // CPropertyAction *pActsp = new CPropertyAction (this, &CDemoCamera::OnTestStandardProperty); + // int nRett = CreateTestStandardProperty("123", pActsp); + // assert(nRett == DEVICE_OK); + + // CPropertyAction *pActsp2 = new CPropertyAction (this, &CDemoCamera::OnTestWithValuesStandardProperty); + // int nRettt = CreateTestWithValuesStandardProperty("value1", pActsp2); + // assert(nRettt == DEVICE_OK); + + // set property list // ----------------- @@ -1384,6 +1394,39 @@ int CDemoCamera::OnBinning(MM::PropertyBase* pProp, MM::ActionType eAct) return ret; } +int CDemoCamera::OnTestStandardProperty(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::BeforeGet) + { + pProp->Set("test"); + return DEVICE_OK; + } + else if (eAct == MM::AfterSet) + { + std::string val; + pProp->Get(val); + return DEVICE_OK; + } + return DEVICE_OK; +} + +int CDemoCamera::OnTestWithValuesStandardProperty(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::BeforeGet) + { + pProp->Set("value1"); + return DEVICE_OK; + } + else if (eAct == MM::AfterSet) + { + std::string val; + pProp->Get(val); + return DEVICE_OK; + } + return DEVICE_OK; +} + + /** * Handles "PixelType" property. */ diff --git a/DeviceAdapters/DemoCamera/DemoCamera.h b/DeviceAdapters/DemoCamera/DemoCamera.h index 1de99da9b..c5f078317 100644 --- a/DeviceAdapters/DemoCamera/DemoCamera.h +++ b/DeviceAdapters/DemoCamera/DemoCamera.h @@ -113,7 +113,7 @@ class DemoHub : public HubBase class MySequenceThread; -class CDemoCamera : public CCameraBase +class CDemoCamera : public CLegacyCameraBase { public: CDemoCamera(); @@ -175,6 +175,8 @@ class CDemoCamera : public CCameraBase // ---------------- int OnMaxExposure(MM::PropertyBase* pProp, MM::ActionType eAct); int OnTestProperty(MM::PropertyBase* pProp, MM::ActionType eAct, long); + int OnTestStandardProperty(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnTestWithValuesStandardProperty(MM::PropertyBase* pProp, MM::ActionType eAct); int OnAsyncFollower(MM::PropertyBase* pProp, MM::ActionType eAct); int OnAsyncLeader(MM::PropertyBase* pProp, MM::ActionType eAct); void SlowPropUpdate(std::string leaderValue); diff --git a/DeviceAdapters/DirectElectron/src/DECamera/DECamera.cpp b/DeviceAdapters/DirectElectron/src/DECamera/DECamera.cpp index d1d867c90..5700d52ba 100644 --- a/DeviceAdapters/DirectElectron/src/DECamera/DECamera.cpp +++ b/DeviceAdapters/DirectElectron/src/DECamera/DECamera.cpp @@ -114,7 +114,7 @@ MODULE_API void DeleteDevice(MM::Device* pDevice) * perform most of the initialization in the Initialize() method. */ CDECamera::CDECamera() : - CCameraBase (), + CLegacyCameraBase (), initialized_(false), initializedProperties_(false), readoutUs_(0.0), @@ -350,7 +350,7 @@ int CDECamera::StartSequenceAcquisition(long numImages, double interval_ms, bool if (IsCapturing()) return DEVICE_CAMERA_BUSY_ACQUIRING; - int ret = GetCoreCallback()->PrepareForAcq((CCameraBase*)this); + int ret = GetCoreCallback()->PrepareForAcq((CLegacyCameraBase*)this); if (ret != DEVICE_OK) return ret; @@ -371,7 +371,7 @@ int CDECamera::StartSequenceAcquisition(long numImages, double interval_ms, bool } // Start thread. - CCameraBase::StartSequenceAcquisition(numImages, interval_ms, stopOnOverflow); + CLegacyCameraBase::StartSequenceAcquisition(numImages, interval_ms, stopOnOverflow); return DEVICE_OK; } @@ -384,7 +384,7 @@ int CDECamera::StopSequenceAcquisition() try { // Call base method to finish up acquisition. - CCameraBase::StopSequenceAcquisition(); + CLegacyCameraBase::StopSequenceAcquisition(); if (this->HasProperty(g_Property_DE_AcquisitionMode)) this->SetProperty(g_Property_DE_AcquisitionMode, g_Property_DE_Acquisition_SingleCapture); diff --git a/DeviceAdapters/DirectElectron/src/DECamera/DECamera.h b/DeviceAdapters/DirectElectron/src/DECamera/DECamera.h index f00ffe399..5fe2aa96c 100644 --- a/DeviceAdapters/DirectElectron/src/DECamera/DECamera.h +++ b/DeviceAdapters/DirectElectron/src/DECamera/DECamera.h @@ -31,7 +31,7 @@ using namespace DirectElectronPlugin; // CDECamera class // Simulation of the Camera device ////////////////////////////////////////////////////////////////////////////// -class CDECamera : public CCameraBase +class CDECamera : public CLegacyCameraBase { public: // Packet types diff --git a/DeviceAdapters/FLICamera/FLICamera.cpp b/DeviceAdapters/FLICamera/FLICamera.cpp index 65a1f3e31..34f774eea 100644 --- a/DeviceAdapters/FLICamera/FLICamera.cpp +++ b/DeviceAdapters/FLICamera/FLICamera.cpp @@ -111,7 +111,7 @@ MODULE_API void DeleteDevice(MM::Device* pDevice) } CFLICamera::CFLICamera() : - CCameraBase (), + CLegacyCameraBase (), initialized_(false), pDemoResourceLock_(0), dev_(FLI_INVALID_DEVICE), diff --git a/DeviceAdapters/FLICamera/FLICamera.h b/DeviceAdapters/FLICamera/FLICamera.h index 073bf7c52..a3cd58fea 100644 --- a/DeviceAdapters/FLICamera/FLICamera.h +++ b/DeviceAdapters/FLICamera/FLICamera.h @@ -31,7 +31,7 @@ #include #include -class CFLICamera : public CCameraBase +class CFLICamera : public CLegacyCameraBase { public: CFLICamera(); diff --git a/DeviceAdapters/FakeCamera/FakeCamera.cpp b/DeviceAdapters/FakeCamera/FakeCamera.cpp index eb9c14707..64cda9a51 100644 --- a/DeviceAdapters/FakeCamera/FakeCamera.cpp +++ b/DeviceAdapters/FakeCamera/FakeCamera.cpp @@ -248,19 +248,19 @@ ERRH_END int FakeCamera::StartSequenceAcquisition(long numImages, double interval_ms, bool stopOnOverflow) { capturing_ = true; - return CCameraBase::StartSequenceAcquisition(numImages, interval_ms, stopOnOverflow); + return CLegacyCameraBase::StartSequenceAcquisition(numImages, interval_ms, stopOnOverflow); } int FakeCamera::StopSequenceAcquisition() { capturing_ = false; - return CCameraBase::StopSequenceAcquisition(); + return CLegacyCameraBase::StopSequenceAcquisition(); } void FakeCamera::OnThreadExiting() throw() { capturing_ = false; - CCameraBase::OnThreadExiting(); + CLegacyCameraBase::OnThreadExiting(); } int FakeCamera::OnPath(MM::PropertyBase * pProp, MM::ActionType eAct) diff --git a/DeviceAdapters/FakeCamera/FakeCamera.h b/DeviceAdapters/FakeCamera/FakeCamera.h index 2dc508579..a1227dfd2 100644 --- a/DeviceAdapters/FakeCamera/FakeCamera.h +++ b/DeviceAdapters/FakeCamera/FakeCamera.h @@ -50,7 +50,7 @@ extern const char* label_CV_16UC4; class parse_error : public std::exception {}; -class FakeCamera : public CCameraBase +class FakeCamera : public CLegacyCameraBase { public: FakeCamera(); diff --git a/DeviceAdapters/Fli/FirstLightImagingCameras.h b/DeviceAdapters/Fli/FirstLightImagingCameras.h index 19e91cce2..92e146e3f 100644 --- a/DeviceAdapters/Fli/FirstLightImagingCameras.h +++ b/DeviceAdapters/Fli/FirstLightImagingCameras.h @@ -30,7 +30,7 @@ class FliThreadImp; -class FirstLightImagingCameras : public CCameraBase +class FirstLightImagingCameras : public CLegacyCameraBase { public: diff --git a/DeviceAdapters/GigECamera/GigECamera.cpp b/DeviceAdapters/GigECamera/GigECamera.cpp index 3bc6735cd..34f54d225 100644 --- a/DeviceAdapters/GigECamera/GigECamera.cpp +++ b/DeviceAdapters/GigECamera/GigECamera.cpp @@ -77,7 +77,7 @@ MODULE_API void DeleteDevice(MM::Device* pDevice) * perform most of the initialization in the Initialize() method. */ CGigECamera::CGigECamera() : -CCameraBase (), +CLegacyCameraBase (), readoutUs_(0.0), scanMode_(1), bitDepth_(8), diff --git a/DeviceAdapters/GigECamera/GigECamera.h b/DeviceAdapters/GigECamera/GigECamera.h index ae2f370ce..bb945409f 100644 --- a/DeviceAdapters/GigECamera/GigECamera.h +++ b/DeviceAdapters/GigECamera/GigECamera.h @@ -66,7 +66,7 @@ extern const char* g_CameraDeviceName; ////////////////////////////////////////////////////////////////////////////// // CGigECamera class ////////////////////////////////////////////////////////////////////////////// -class CGigECamera : public CCameraBase +class CGigECamera : public CLegacyCameraBase { public: CGigECamera(); diff --git a/DeviceAdapters/Hikrobot/HikrobotCamera.cpp b/DeviceAdapters/Hikrobot/HikrobotCamera.cpp index 353c4f78e..731b2edf8 100644 --- a/DeviceAdapters/Hikrobot/HikrobotCamera.cpp +++ b/DeviceAdapters/Hikrobot/HikrobotCamera.cpp @@ -84,7 +84,7 @@ MODULE_API void DeleteDevice(MM::Device* pDevice) * Constructor. */ HikrobotCamera::HikrobotCamera() : - CCameraBase(), + CLegacyCameraBase(), maxWidth_(0), maxHeight_(0), exposure_us_(0), diff --git a/DeviceAdapters/Hikrobot/HikrobotCamera.h b/DeviceAdapters/Hikrobot/HikrobotCamera.h index 162168e6e..0357e8718 100644 --- a/DeviceAdapters/Hikrobot/HikrobotCamera.h +++ b/DeviceAdapters/Hikrobot/HikrobotCamera.h @@ -63,7 +63,7 @@ enum //class CTempCameraEventHandler; //class CircularBufferInserter; -class HikrobotCamera : public CCameraBase { +class HikrobotCamera : public CLegacyCameraBase { public: HikrobotCamera(); ~HikrobotCamera(); diff --git a/DeviceAdapters/IDSPeak/IDSPeak.cpp b/DeviceAdapters/IDSPeak/IDSPeak.cpp index b0158fbeb..f94627992 100644 --- a/DeviceAdapters/IDSPeak/IDSPeak.cpp +++ b/DeviceAdapters/IDSPeak/IDSPeak.cpp @@ -366,7 +366,7 @@ bool IDSPeakHub::Busy() * perform most of the initialization in the Initialize() method. */ CIDSPeak::CIDSPeak(int idx) : - CCameraBase(), + CLegacyCameraBase(), initialized_(false), readoutUs_(0.0), bitDepth_(8), diff --git a/DeviceAdapters/IDSPeak/IDSPeak.h b/DeviceAdapters/IDSPeak/IDSPeak.h index 89af79881..3dcedf9b8 100644 --- a/DeviceAdapters/IDSPeak/IDSPeak.h +++ b/DeviceAdapters/IDSPeak/IDSPeak.h @@ -112,7 +112,7 @@ class IDSPeakHub : public HubBase class MySequenceThread; -class CIDSPeak : public CCameraBase +class CIDSPeak : public CLegacyCameraBase { public: CIDSPeak(int deviceIdx); diff --git a/DeviceAdapters/IDS_uEye/IDS_uEye.cpp b/DeviceAdapters/IDS_uEye/IDS_uEye.cpp index d88cda6b8..1e68cca50 100644 --- a/DeviceAdapters/IDS_uEye/IDS_uEye.cpp +++ b/DeviceAdapters/IDS_uEye/IDS_uEye.cpp @@ -138,7 +138,7 @@ MODULE_API void DeleteDevice(MM::Device* pDevice) * perform most of the initialization in the Initialize() method. */ CIDS_uEye::CIDS_uEye() : - CCameraBase (), + CLegacyCameraBase (), dPhase_(0), initialized_(false), readoutUs_(0.0), diff --git a/DeviceAdapters/IDS_uEye/IDS_uEye.h b/DeviceAdapters/IDS_uEye/IDS_uEye.h index 3d43d571f..8b46e6feb 100644 --- a/DeviceAdapters/IDS_uEye/IDS_uEye.h +++ b/DeviceAdapters/IDS_uEye/IDS_uEye.h @@ -217,7 +217,7 @@ string triggerModeToString(int triggerMode){ class MySequenceThread; -class CIDS_uEye : public CCameraBase +class CIDS_uEye : public CLegacyCameraBase { public: diff --git a/DeviceAdapters/IIDC/MMIIDCCamera.h b/DeviceAdapters/IIDC/MMIIDCCamera.h index 964dee6cc..3bde63ccc 100644 --- a/DeviceAdapters/IIDC/MMIIDCCamera.h +++ b/DeviceAdapters/IIDC/MMIIDCCamera.h @@ -34,7 +34,7 @@ class MMIIDCHub; // Not a Micro-Manager "Hub" device -class MMIIDCCamera : public CCameraBase +class MMIIDCCamera : public CLegacyCameraBase { boost::shared_ptr hub_; diff --git a/DeviceAdapters/JAI/JAI.h b/DeviceAdapters/JAI/JAI.h index b1904e7d6..8a4ebd716 100644 --- a/DeviceAdapters/JAI/JAI.h +++ b/DeviceAdapters/JAI/JAI.h @@ -132,7 +132,7 @@ class PvImage; // Implementation of the MMDevice and MMCamera interfaces // for all JAI eBus compatible cameras // -class JAICamera : public CCameraBase +class JAICamera : public CLegacyCameraBase { friend AcqSequenceThread; diff --git a/DeviceAdapters/Lumenera/LumeneraCamera.cpp b/DeviceAdapters/Lumenera/LumeneraCamera.cpp index d5d301723..8f951e54c 100644 --- a/DeviceAdapters/Lumenera/LumeneraCamera.cpp +++ b/DeviceAdapters/Lumenera/LumeneraCamera.cpp @@ -209,7 +209,7 @@ MODULE_API void DeleteDevice(MM::Device* pDevice) * Constructor. */ LumeneraCamera::LumeneraCamera() : - CCameraBase(), + CLegacyCameraBase(), components_(1), bitDepth_(Imaging::IMAGE_BIT_DEPTH::EIGHT_BIT), colorCamera_(true), diff --git a/DeviceAdapters/Lumenera/LumeneraCamera.h b/DeviceAdapters/Lumenera/LumeneraCamera.h index e5efbd344..b258cbf13 100644 --- a/DeviceAdapters/Lumenera/LumeneraCamera.h +++ b/DeviceAdapters/Lumenera/LumeneraCamera.h @@ -81,7 +81,7 @@ enum //class CircularBufferInserter; class VideoSequenceThread; -class LumeneraCamera : public CCameraBase { +class LumeneraCamera : public CLegacyCameraBase { friend class VideoSequenceThread; diff --git a/DeviceAdapters/MatrixVision/mvIMPACT_Acquire_Device.h b/DeviceAdapters/MatrixVision/mvIMPACT_Acquire_Device.h index 37b6fe185..5ac9862f0 100644 --- a/DeviceAdapters/MatrixVision/mvIMPACT_Acquire_Device.h +++ b/DeviceAdapters/MatrixVision/mvIMPACT_Acquire_Device.h @@ -49,7 +49,7 @@ typedef std::set StringSet; class MySequenceThread; //----------------------------------------------------------------------------- -class mvIMPACT_Acquire_Device : public CCameraBase +class mvIMPACT_Acquire_Device : public CLegacyCameraBase //----------------------------------------------------------------------------- { public: diff --git a/DeviceAdapters/Mightex_C_Cam/Mightex_USBCamera.cpp b/DeviceAdapters/Mightex_C_Cam/Mightex_USBCamera.cpp index 6e47d7168..8b0d5f38d 100755 --- a/DeviceAdapters/Mightex_C_Cam/Mightex_USBCamera.cpp +++ b/DeviceAdapters/Mightex_C_Cam/Mightex_USBCamera.cpp @@ -610,7 +610,7 @@ int CMightex_BUF_USBCCDCamera::GetCameraBufferCount(int width, int height) * perform most of the initialization in the Initialize() method. */ CMightex_BUF_USBCCDCamera::CMightex_BUF_USBCCDCamera() : - CCameraBase (), + CLegacyCameraBase (), dPhase_(0), initialized_(false), readoutUs_(0.0), diff --git a/DeviceAdapters/Mightex_C_Cam/Mightex_USBCamera.h b/DeviceAdapters/Mightex_C_Cam/Mightex_USBCamera.h index 3f9196be2..a30322f49 100755 --- a/DeviceAdapters/Mightex_C_Cam/Mightex_USBCamera.h +++ b/DeviceAdapters/Mightex_C_Cam/Mightex_USBCamera.h @@ -57,7 +57,7 @@ struct FrmSize{ int width; int height;}; class MySequenceThread; -class CMightex_BUF_USBCCDCamera : public CCameraBase +class CMightex_BUF_USBCCDCamera : public CLegacyCameraBase { public: CMightex_BUF_USBCCDCamera(); diff --git a/DeviceAdapters/Motic/MoticCamera.cpp b/DeviceAdapters/Motic/MoticCamera.cpp index f3d800f83..b6e38926d 100644 --- a/DeviceAdapters/Motic/MoticCamera.cpp +++ b/DeviceAdapters/Motic/MoticCamera.cpp @@ -866,7 +866,7 @@ int CMoticCamera::InsertImage() bool CMoticCamera::IsCapturing() { - if(CCameraBase::IsCapturing()) + if(CLegacyCameraBase::IsCapturing()) { #ifdef _LOG_OUT_ OutputDebugString("IsCapturing true"); diff --git a/DeviceAdapters/Motic/MoticCamera.h b/DeviceAdapters/Motic/MoticCamera.h index 9fbd45640..4f7e84836 100644 --- a/DeviceAdapters/Motic/MoticCamera.h +++ b/DeviceAdapters/Motic/MoticCamera.h @@ -56,7 +56,7 @@ using namespace std; class SequenceThread; -class CMoticCamera : public CCameraBase +class CMoticCamera : public CLegacyCameraBase { public: CMoticCamera(); diff --git a/DeviceAdapters/OpenCVgrabber/OpenCVgrabber.cpp b/DeviceAdapters/OpenCVgrabber/OpenCVgrabber.cpp index 4d8f581a3..ed0f4117d 100644 --- a/DeviceAdapters/OpenCVgrabber/OpenCVgrabber.cpp +++ b/DeviceAdapters/OpenCVgrabber/OpenCVgrabber.cpp @@ -138,7 +138,7 @@ MODULE_API void DeleteDevice(MM::Device* pDevice) * perform most of the initialization in the Initialize() method. */ COpenCVgrabber::COpenCVgrabber() : - CCameraBase (), + CLegacyCameraBase (), cameraID_(0), initialized_(false), readoutUs_(0.0), diff --git a/DeviceAdapters/OpenCVgrabber/OpenCVgrabber.h b/DeviceAdapters/OpenCVgrabber/OpenCVgrabber.h index f9e802fc9..0424ef82e 100644 --- a/DeviceAdapters/OpenCVgrabber/OpenCVgrabber.h +++ b/DeviceAdapters/OpenCVgrabber/OpenCVgrabber.h @@ -68,7 +68,7 @@ class MySequenceThread; -class COpenCVgrabber : public CCameraBase +class COpenCVgrabber : public CLegacyCameraBase { public: COpenCVgrabber(); diff --git a/DeviceAdapters/PCO_Generic/MicroManager.cpp b/DeviceAdapters/PCO_Generic/MicroManager.cpp index 5eeb1e2ed..5c86b721a 100644 --- a/DeviceAdapters/PCO_Generic/MicroManager.cpp +++ b/DeviceAdapters/PCO_Generic/MicroManager.cpp @@ -94,7 +94,7 @@ MODULE_API MM::Device* CreateDevice(const char* pszDeviceName) // CPCOCam constructor/destructor CPCOCam::CPCOCam() : -CCameraBase(), +CLegacyCameraBase(), m_bSequenceRunning(false), m_bInitialized(false), m_bBusy(false), diff --git a/DeviceAdapters/PCO_Generic/MicroManager.h b/DeviceAdapters/PCO_Generic/MicroManager.h index 2fa255e34..b0c68a1e2 100644 --- a/DeviceAdapters/PCO_Generic/MicroManager.h +++ b/DeviceAdapters/PCO_Generic/MicroManager.h @@ -59,7 +59,7 @@ ////////////////////////////////////////////////////////////////////////////// // Implementation of the MMDevice and MMCamera interfaces // -class CPCOCam : public CCameraBase +class CPCOCam : public CLegacyCameraBase { public: CPCOCam(); diff --git a/DeviceAdapters/PICAM/PICAMAdapter.h b/DeviceAdapters/PICAM/PICAMAdapter.h index df97181c6..f4382acb4 100644 --- a/DeviceAdapters/PICAM/PICAMAdapter.h +++ b/DeviceAdapters/PICAM/PICAMAdapter.h @@ -202,7 +202,7 @@ class PProc /*** * Implementation of the MMDevice and MMCamera interfaces for all PICAM cameras */ -class Universal : public CCameraBase +class Universal : public CLegacyCameraBase { public: diff --git a/DeviceAdapters/PICAM/PICAMUniversal.cpp b/DeviceAdapters/PICAM/PICAMUniversal.cpp index f2d0489d1..66905ebd9 100644 --- a/DeviceAdapters/PICAM/PICAMUniversal.cpp +++ b/DeviceAdapters/PICAM/PICAMUniversal.cpp @@ -205,7 +205,7 @@ const int g_UniversalParamsCount = sizeof(g_UniversalParams)/sizeof(ParamNameIdP // The name parameter is only used to return the device name. The physical // camera to use is determined by the cameraId parameter. Universal::Universal(short cameraId, const char* name) : - CCameraBase (), + CLegacyCameraBase (), initialized_(false), curImageCnt_(0), hPICAM_(0), @@ -1835,7 +1835,7 @@ int Universal::ClearROI() bool Universal::GetErrorText(int errorCode, char* text) const { - if (CCameraBase::GetErrorText(errorCode, text)) + if (CLegacyCameraBase::GetErrorText(errorCode, text)) return true; // base message return false; @@ -2504,7 +2504,7 @@ void Universal::OnThreadExiting() throw () isAcquiring_ = false; // The AcqFinished is called inside the parent OnThreadExiting() - CCameraBase::OnThreadExiting(); + CLegacyCameraBase::OnThreadExiting(); } catch (...) { @@ -2592,7 +2592,7 @@ int Universal::LogMMError(int errCode, int lineNr, std::string message, bool deb try { char strText[MM::MaxStrLength]; - if (!CCameraBase::GetErrorText(errCode, strText)) + if (!CLegacyCameraBase::GetErrorText(errCode, strText)) { CDeviceUtils::CopyLimitedString(strText, "Unknown"); } diff --git a/DeviceAdapters/PVCAM/PVCAMAdapter.h b/DeviceAdapters/PVCAM/PVCAMAdapter.h index cb6f0fd9e..e6d3c7866 100644 --- a/DeviceAdapters/PVCAM/PVCAMAdapter.h +++ b/DeviceAdapters/PVCAM/PVCAMAdapter.h @@ -176,7 +176,7 @@ class PvEnumParam; /** * Implementation of the MMDevice and MMCamera interfaces for all PVCAM cameras */ -class Universal : public CCameraBase +class Universal : public CLegacyCameraBase { public: // Constructors, destructor Universal(short cameraId, const char* deviceName); diff --git a/DeviceAdapters/PVCAM/PVCAMUniversal.cpp b/DeviceAdapters/PVCAM/PVCAMUniversal.cpp index a87a7a9b3..c7911505e 100644 --- a/DeviceAdapters/PVCAM/PVCAMUniversal.cpp +++ b/DeviceAdapters/PVCAM/PVCAMUniversal.cpp @@ -226,7 +226,7 @@ const int g_UniversalParamsCount = sizeof(g_UniversalParams)/sizeof(ParamNameIdP //=================================================================== Universal Universal::Universal(short cameraId, const char* deviceName) - : CCameraBase(), + : CLegacyCameraBase(), cameraId_(cameraId), deviceName_(deviceName), initialized_(false), @@ -1635,7 +1635,7 @@ bool Universal::Busy() bool Universal::GetErrorText(int errorCode, char* text) const { - if (CCameraBase::GetErrorText(errorCode, text)) + if (CLegacyCameraBase::GetErrorText(errorCode, text)) return true; // base message char buf[ERROR_MSG_LEN]; @@ -3922,7 +3922,7 @@ int Universal::LogAdapterError(int mmErrCode, int lineNr, const std::string& mes try { char mmErrMsg[MM::MaxStrLength]; - if (!CCameraBase::GetErrorText(mmErrCode, mmErrMsg)) + if (!CLegacyCameraBase::GetErrorText(mmErrCode, mmErrMsg)) { CDeviceUtils::CopyLimitedString(mmErrMsg, "Unknown"); } diff --git a/DeviceAdapters/Piper/CameraAdapter.h b/DeviceAdapters/Piper/CameraAdapter.h index 04801f667..8d8d4fe68 100755 --- a/DeviceAdapters/Piper/CameraAdapter.h +++ b/DeviceAdapters/Piper/CameraAdapter.h @@ -46,7 +46,7 @@ // CCameraAdapter class // Simulation of the Camera device ////////////////////////////////////////////////////////////////////////////// -class CCameraAdapter : public CCameraBase +class CCameraAdapter : public CLegacyCameraBase { public: CCameraAdapter( LPCTSTR pszName ); diff --git a/DeviceAdapters/Pixelink/Pixelink.h b/DeviceAdapters/Pixelink/Pixelink.h index abe4aa85a..fb72a3a27 100644 --- a/DeviceAdapters/Pixelink/Pixelink.h +++ b/DeviceAdapters/Pixelink/Pixelink.h @@ -44,7 +44,7 @@ struct PixelAddressing float GetPixelSize(U32 pixelFormat); U32 DetermineRawImageSize(HANDLE hCamera); -class Pixelink : public CCameraBase +class Pixelink : public CLegacyCameraBase { public: Pixelink(const char* deviceName); diff --git a/DeviceAdapters/PlayerOne/POACamera.cpp b/DeviceAdapters/PlayerOne/POACamera.cpp index 91894d391..2112ba75f 100644 --- a/DeviceAdapters/PlayerOne/POACamera.cpp +++ b/DeviceAdapters/PlayerOne/POACamera.cpp @@ -179,7 +179,7 @@ MODULE_API void DeleteDevice(MM::Device* pDevice) * perform most of the initialization in the Initialize() method. */ POACamera::POACamera() : - CCameraBase(), + CLegacyCameraBase(), exposureMaximum_(2000000.0), initialized_(false), roiX_(0), diff --git a/DeviceAdapters/PlayerOne/POACamera.h b/DeviceAdapters/PlayerOne/POACamera.h index 6651a0de4..bc2ebe7fb 100644 --- a/DeviceAdapters/PlayerOne/POACamera.h +++ b/DeviceAdapters/PlayerOne/POACamera.h @@ -84,7 +84,7 @@ class DemoHub : public HubBase class MySequenceThread; -class POACamera : public CCameraBase +class POACamera : public CLegacyCameraBase { public: POACamera(); diff --git a/DeviceAdapters/PointGrey/PointGrey.h b/DeviceAdapters/PointGrey/PointGrey.h index 802157910..ac8e3f44d 100644 --- a/DeviceAdapters/PointGrey/PointGrey.h +++ b/DeviceAdapters/PointGrey/PointGrey.h @@ -52,7 +52,7 @@ using namespace FlyCapture2; class SequenceThread; -class PointGrey : public CCameraBase +class PointGrey : public CLegacyCameraBase { public: PointGrey(const char* deviceName); diff --git a/DeviceAdapters/PyDevice/PyCamera.h b/DeviceAdapters/PyDevice/PyCamera.h index 0fcb01de7..6afb5c01e 100644 --- a/DeviceAdapters/PyDevice/PyCamera.h +++ b/DeviceAdapters/PyDevice/PyCamera.h @@ -2,7 +2,7 @@ #include "PyDevice.h" #include "buffer.h" -using PyCameraClass = CPyDeviceTemplate>; +using PyCameraClass = CPyDeviceTemplate>; class CPyCamera : public PyCameraClass { Py_buffer lastFrame_; PyObj read_; // the read() method of the camera object diff --git a/DeviceAdapters/QCam/QICamera.cpp b/DeviceAdapters/QCam/QICamera.cpp index 20d580575..9f80e1c6c 100644 --- a/DeviceAdapters/QCam/QICamera.cpp +++ b/DeviceAdapters/QCam/QICamera.cpp @@ -568,7 +568,7 @@ QIDriver::Access::~Access() * perform most of the initialization in the Initialize() method. */ QICamera::QICamera() -:CCameraBase () +:CLegacyCameraBase () ,m_isInitialized(false) ,m_softwareTrigger(false) ,m_rgbColor(false) diff --git a/DeviceAdapters/QCam/QICamera.h b/DeviceAdapters/QCam/QICamera.h index 28e47fa06..b06e124ff 100644 --- a/DeviceAdapters/QCam/QICamera.h +++ b/DeviceAdapters/QCam/QICamera.h @@ -161,7 +161,7 @@ class QIDriver ////////////////////////////////////////////////////////////////////////////// // QICamera class ////////////////////////////////////////////////////////////////////////////// -class QICamera : public CCameraBase +class QICamera : public CLegacyCameraBase { public: QICamera(); diff --git a/DeviceAdapters/QSI/QSICameraAdapter.h b/DeviceAdapters/QSI/QSICameraAdapter.h index 435d9bd25..eee7006d7 100644 --- a/DeviceAdapters/QSI/QSICameraAdapter.h +++ b/DeviceAdapters/QSI/QSICameraAdapter.h @@ -18,11 +18,11 @@ #include "QSICameraCLib.h" #include "QSIError.h" -class QSICameraAdapter : public CCameraBase +class QSICameraAdapter : public CLegacyCameraBase { private: - typedef CCameraBase base; + typedef CLegacyCameraBase base; public: diff --git a/DeviceAdapters/RaptorEPIX/RaptorEPIX.cpp b/DeviceAdapters/RaptorEPIX/RaptorEPIX.cpp index 571859adb..e0be7d033 100644 --- a/DeviceAdapters/RaptorEPIX/RaptorEPIX.cpp +++ b/DeviceAdapters/RaptorEPIX/RaptorEPIX.cpp @@ -912,7 +912,7 @@ MODULE_API void DeleteDevice(MM::Device* pDevice) * perform most of the initialization in the Initialize() method. */ CRaptorEPIX::CRaptorEPIX(int nCameraType) : - CCameraBase (), + CLegacyCameraBase (), cameraType_(0), dPhase_(0), exposure_(0), diff --git a/DeviceAdapters/RaptorEPIX/RaptorEPIX.h b/DeviceAdapters/RaptorEPIX/RaptorEPIX.h index 3c49638ef..de1777a79 100644 --- a/DeviceAdapters/RaptorEPIX/RaptorEPIX.h +++ b/DeviceAdapters/RaptorEPIX/RaptorEPIX.h @@ -158,7 +158,7 @@ class CRaptorPleora : public PvGenEventSink, PvAcquisitionStateEventSink #endif -class CRaptorEPIX : public CCameraBase +class CRaptorEPIX : public CLegacyCameraBase { public: CRaptorEPIX() {CRaptorEPIX(0);}; diff --git a/DeviceAdapters/SequenceTester/SequenceTester.cpp b/DeviceAdapters/SequenceTester/SequenceTester.cpp index 87d638318..949c18d7d 100644 --- a/DeviceAdapters/SequenceTester/SequenceTester.cpp +++ b/DeviceAdapters/SequenceTester/SequenceTester.cpp @@ -251,14 +251,14 @@ TesterCamera::TesterCamera(const std::string& name) : stopSequence_(true) { // For pre-init properties only, we use the traditional method to set up. - CCameraBase::CreateStringProperty("ImageMode", "HumanReadable", + CLegacyCameraBase::CreateStringProperty("ImageMode", "HumanReadable", false, 0, true); AddAllowedValue("ImageMode", "HumanReadable"); AddAllowedValue("ImageMode", "MachineReadable"); - CCameraBase::CreateIntegerProperty("ImageWidth", imageWidth_, + CLegacyCameraBase::CreateIntegerProperty("ImageWidth", imageWidth_, false, 0, true); SetPropertyLimits("ImageWidth", 32, 4096); - CCameraBase::CreateIntegerProperty("ImageHeight", imageHeight_, + CLegacyCameraBase::CreateIntegerProperty("ImageHeight", imageHeight_, false, 0, true); SetPropertyLimits("ImageHeight", 32, 4096); } diff --git a/DeviceAdapters/SequenceTester/SequenceTester.h b/DeviceAdapters/SequenceTester/SequenceTester.h index d2c328b4e..39454867e 100644 --- a/DeviceAdapters/SequenceTester/SequenceTester.h +++ b/DeviceAdapters/SequenceTester/SequenceTester.h @@ -127,10 +127,10 @@ class TesterHub : public TesterBase }; -class TesterCamera : public TesterBase +class TesterCamera : public TesterBase { typedef TesterCamera Self; - typedef TesterBase< ::CCameraBase, TesterCamera > Super; + typedef TesterBase< ::CLegacyCameraBase, TesterCamera > Super; public: TesterCamera(const std::string& name); diff --git a/DeviceAdapters/SigmaKoki/Camera.cpp b/DeviceAdapters/SigmaKoki/Camera.cpp index 8f485a75c..161a5b7dd 100644 --- a/DeviceAdapters/SigmaKoki/Camera.cpp +++ b/DeviceAdapters/SigmaKoki/Camera.cpp @@ -118,7 +118,7 @@ int ClearPort(MM::Device& device, MM::Core& core, std::string port) Camera::Camera() : // initialization of parameters SigmaBase(this), - CCameraBase(), + CLegacyCameraBase(), initialized_(false), isMonochrome_(false), enabledROI_(false), diff --git a/DeviceAdapters/SigmaKoki/Camera.h b/DeviceAdapters/SigmaKoki/Camera.h index cc40a655a..07f67490d 100644 --- a/DeviceAdapters/SigmaKoki/Camera.h +++ b/DeviceAdapters/SigmaKoki/Camera.h @@ -56,7 +56,7 @@ extern const char* g_CameraDeviceName; #pragma endregion Camera_Err_MSG -class Camera : public CCameraBase, public SigmaBase +class Camera : public CLegacyCameraBase, public SigmaBase { #pragma region Constructor_Des public: diff --git a/DeviceAdapters/Spinnaker/SpinnakerCamera.cpp b/DeviceAdapters/Spinnaker/SpinnakerCamera.cpp index 2cae743bf..9aa9a5048 100644 --- a/DeviceAdapters/Spinnaker/SpinnakerCamera.cpp +++ b/DeviceAdapters/Spinnaker/SpinnakerCamera.cpp @@ -123,7 +123,7 @@ MODULE_API void DeleteDevice(MM::Device* pDevice) } SpinnakerCamera::SpinnakerCamera(GENICAM::gcstring name) - : CCameraBase(), + : CLegacyCameraBase(), m_system(nullptr), m_cam(nullptr), m_imageBuff(nullptr), diff --git a/DeviceAdapters/Spinnaker/SpinnakerCamera.h b/DeviceAdapters/Spinnaker/SpinnakerCamera.h index bd2e7d445..fd0df40b8 100644 --- a/DeviceAdapters/Spinnaker/SpinnakerCamera.h +++ b/DeviceAdapters/Spinnaker/SpinnakerCamera.h @@ -15,7 +15,7 @@ class SpinnakerAcquisitionThread; -class SpinnakerCamera : public CCameraBase +class SpinnakerCamera : public CLegacyCameraBase { public: SpinnakerCamera(GENICAM::gcstring serialNumber); diff --git a/DeviceAdapters/TISCam/TIScamera.cpp b/DeviceAdapters/TISCam/TIScamera.cpp index f1ce5e0ee..c06be6da9 100644 --- a/DeviceAdapters/TISCam/TIScamera.cpp +++ b/DeviceAdapters/TISCam/TIScamera.cpp @@ -155,7 +155,7 @@ As a general guideline Micro-Manager devices do not access hardware in the the constructor. We should do as little as possible in the constructor and perform most of the initialization in the Initialize() method. ==============================================================================*/ -CTIScamera::CTIScamera() : CCameraBase (), +CTIScamera::CTIScamera() : CLegacyCameraBase (), initialized_(false), pSelectDevice(NULL), diff --git a/DeviceAdapters/TISCam/TIScamera.h b/DeviceAdapters/TISCam/TIScamera.h index b786c9a38..47fe66a7b 100644 --- a/DeviceAdapters/TISCam/TIScamera.h +++ b/DeviceAdapters/TISCam/TIScamera.h @@ -59,7 +59,7 @@ class AcqSequenceThread; ////////////////////////////////////////////////////////////////////////////// // Implementation of the MMDevice and MMCamera interfaces // -class CTIScamera : public CCameraBase +class CTIScamera : public CLegacyCameraBase { public: diff --git a/DeviceAdapters/TSI/TSI3Cam.h b/DeviceAdapters/TSI/TSI3Cam.h index 07455c0ec..5db54af00 100644 --- a/DeviceAdapters/TSI/TSI3Cam.h +++ b/DeviceAdapters/TSI/TSI3Cam.h @@ -101,7 +101,7 @@ static const char* dllLoadErr = "Error loading color processing functions from t // Implementation of the MMDevice and MMCamera interfaces // for all TSI SDK 3 api compatible cameras // -class Tsi3Cam : public CCameraBase +class Tsi3Cam : public CLegacyCameraBase { public: diff --git a/DeviceAdapters/TSI/TSICam.h b/DeviceAdapters/TSI/TSICam.h index d996a5fac..3b786c72d 100644 --- a/DeviceAdapters/TSI/TSICam.h +++ b/DeviceAdapters/TSI/TSICam.h @@ -68,7 +68,7 @@ static const double TSI_MAX_EXPOSURE_MS = 20000; // 20 sec max exposure // Implementation of the MMDevice and MMCamera interfaces // for all TSI api compatible cameras // -class TsiCam : public CCameraBase +class TsiCam : public CLegacyCameraBase { friend AcqSequenceThread; diff --git a/DeviceAdapters/TUCam/MMTUCam.cpp b/DeviceAdapters/TUCam/MMTUCam.cpp index 43b7f0676..536e748d0 100755 --- a/DeviceAdapters/TUCam/MMTUCam.cpp +++ b/DeviceAdapters/TUCam/MMTUCam.cpp @@ -251,7 +251,7 @@ int CMMTUCam::s_nCntCam = 0; * perform most of the initialization in the Initialize() method. */ CMMTUCam::CMMTUCam() : - CCameraBase (), + CLegacyCameraBase (), exposureMaximum_(10000.0), exposureMinimum_(0.0), dPhase_(0), diff --git a/DeviceAdapters/TUCam/MMTUCam.h b/DeviceAdapters/TUCam/MMTUCam.h index 03bfa01a6..f865d38b9 100755 --- a/DeviceAdapters/TUCam/MMTUCam.h +++ b/DeviceAdapters/TUCam/MMTUCam.h @@ -197,7 +197,7 @@ typedef struct _tagTUCAM_RSPARA DOUBLE dbLineInvalTm; // Line interval time }TUCAM_RSPARA; -class CMMTUCam : public CCameraBase +class CMMTUCam : public CLegacyCameraBase { public: CMMTUCam(); diff --git a/DeviceAdapters/TeensyPulseGenerator/CameraPulser.h b/DeviceAdapters/TeensyPulseGenerator/CameraPulser.h index 042a3c723..7174f24d7 100644 --- a/DeviceAdapters/TeensyPulseGenerator/CameraPulser.h +++ b/DeviceAdapters/TeensyPulseGenerator/CameraPulser.h @@ -14,7 +14,7 @@ #define MAX_NUMBER_PHYSICAL_CAMERAS 1 -class CameraPulser : public CCameraBase +class CameraPulser : public CLegacyCameraBase { public: CameraPulser(); diff --git a/DeviceAdapters/ThorlabsUSBCamera/ThorlabsUSBCamera.cpp b/DeviceAdapters/ThorlabsUSBCamera/ThorlabsUSBCamera.cpp index 6e64abd4f..a689e8b94 100644 --- a/DeviceAdapters/ThorlabsUSBCamera/ThorlabsUSBCamera.cpp +++ b/DeviceAdapters/ThorlabsUSBCamera/ThorlabsUSBCamera.cpp @@ -94,7 +94,7 @@ MODULE_API void DeleteDevice(MM::Device* pDevice) * perform most of the initialization in the Initialize() method. */ ThorlabsUSBCam::ThorlabsUSBCam() : - CCameraBase (), + CLegacyCameraBase (), initialized_(false), bitDepth_(8), roiX_(0), diff --git a/DeviceAdapters/ThorlabsUSBCamera/ThorlabsUSBCamera.h b/DeviceAdapters/ThorlabsUSBCamera/ThorlabsUSBCamera.h index 7ed8bec9e..9550deca8 100644 --- a/DeviceAdapters/ThorlabsUSBCamera/ThorlabsUSBCamera.h +++ b/DeviceAdapters/ThorlabsUSBCamera/ThorlabsUSBCamera.h @@ -49,7 +49,7 @@ class MySequenceThread; -class ThorlabsUSBCam : public CCameraBase +class ThorlabsUSBCam : public CLegacyCameraBase { public: ThorlabsUSBCam(); diff --git a/DeviceAdapters/TwainCamera/TwainCamera.cpp b/DeviceAdapters/TwainCamera/TwainCamera.cpp index 7802bf91d..8712ee8bb 100644 --- a/DeviceAdapters/TwainCamera/TwainCamera.cpp +++ b/DeviceAdapters/TwainCamera/TwainCamera.cpp @@ -147,7 +147,7 @@ MODULE_API void DeleteDevice(MM::Device* pDevice) * perform most of the initialization in the Initialize() method. */ TwainCamera::TwainCamera() : -CCameraBase (), +CLegacyCameraBase (), initialized_(false), busy_(false), readoutUs_(0), @@ -1280,7 +1280,7 @@ int TwainCamera::PushImage() } int TwainCamera::StopSequenceAcquisition() { - int nRet = this->CCameraBase::StopSequenceAcquisition(); + int nRet = this->CLegacyCameraBase::StopSequenceAcquisition(); return nRet; } diff --git a/DeviceAdapters/TwainCamera/TwainCamera.h b/DeviceAdapters/TwainCamera/TwainCamera.h index f563ed581..ccf33335e 100644 --- a/DeviceAdapters/TwainCamera/TwainCamera.h +++ b/DeviceAdapters/TwainCamera/TwainCamera.h @@ -62,7 +62,7 @@ class TwainBad // exception to throw upon error in Twain device // TwainCamera class //streaming Camera device ////////////////////////////////////////////////////////////////////////////// -class TwainCamera : public CCameraBase +class TwainCamera : public CLegacyCameraBase { public: TwainCamera(); @@ -108,7 +108,7 @@ class TwainCamera : public CCameraBase // expose CDeviceBase accessors, so that PImpl can use them MM::MMTime GetCurrentMMTime() { - return CCameraBase::GetCurrentMMTime(); + return CLegacyCameraBase::GetCurrentMMTime(); }; diff --git a/DeviceAdapters/TwoPhoton/TwoPhoton.cpp b/DeviceAdapters/TwoPhoton/TwoPhoton.cpp index 62df9a22e..0f15cfc48 100644 --- a/DeviceAdapters/TwoPhoton/TwoPhoton.cpp +++ b/DeviceAdapters/TwoPhoton/TwoPhoton.cpp @@ -101,7 +101,7 @@ MODULE_API void DeleteDevice(MM::Device* pDevice) * Constructor. */ BitFlowCamera::BitFlowCamera(bool dual) : - CCameraBase (), + CLegacyCameraBase (), initialized_(false), inputChannel_(0), expNumFrames_(1), diff --git a/DeviceAdapters/TwoPhoton/TwoPhoton.h b/DeviceAdapters/TwoPhoton/TwoPhoton.h index c66d48109..92b698cda 100644 --- a/DeviceAdapters/TwoPhoton/TwoPhoton.h +++ b/DeviceAdapters/TwoPhoton/TwoPhoton.h @@ -56,7 +56,7 @@ // Frame-grabber video mode adapter for BitFlow ////////////////////////////////////////////////////////////////////////////// -class BitFlowCamera : public CCameraBase { +class BitFlowCamera : public CLegacyCameraBase { public: BitFlowCamera(bool dual); ~BitFlowCamera(); diff --git a/DeviceAdapters/UniversalMMHubUsb/ummhUsb.h b/DeviceAdapters/UniversalMMHubUsb/ummhUsb.h index bb68e6e26..80272b532 100644 --- a/DeviceAdapters/UniversalMMHubUsb/ummhUsb.h +++ b/DeviceAdapters/UniversalMMHubUsb/ummhUsb.h @@ -395,7 +395,7 @@ class UmmhGeneric : public UmmhDeviceUtilities, public CGenericBase int CreatePropertyBasedOnDescription(mmpropertydescription); }; -class UmmhCamera : public UmmhDeviceUtilities, public CCameraBase +class UmmhCamera : public UmmhDeviceUtilities, public CLegacyCameraBase { public: UmmhCamera(const char* name); diff --git a/DeviceAdapters/Utilities/Utilities.h b/DeviceAdapters/Utilities/Utilities.h index 9b7864728..b35f64ce9 100644 --- a/DeviceAdapters/Utilities/Utilities.h +++ b/DeviceAdapters/Utilities/Utilities.h @@ -138,7 +138,7 @@ class CameraSnapThread : public MMDeviceThreadBase /* * MultiCamera: Combines multiple physical cameras into one logical device */ -class MultiCamera : public CCameraBase +class MultiCamera : public CLegacyCameraBase { public: MultiCamera(); diff --git a/DeviceAdapters/Ximea/XIMEACamera.h b/DeviceAdapters/Ximea/XIMEACamera.h index bd4c76bd3..babd3746f 100644 --- a/DeviceAdapters/Ximea/XIMEACamera.h +++ b/DeviceAdapters/Ximea/XIMEACamera.h @@ -49,7 +49,7 @@ class XimeaParam; ////////////////////////////////////////////////////////////////////////////// -class XimeaCamera : public CCameraBase +class XimeaCamera : public CLegacyCameraBase { public: XimeaCamera(const char* name); diff --git a/DeviceAdapters/ZWO/MyASICam2.h b/DeviceAdapters/ZWO/MyASICam2.h index 6710bf6b1..385c30435 100644 --- a/DeviceAdapters/ZWO/MyASICam2.h +++ b/DeviceAdapters/ZWO/MyASICam2.h @@ -9,7 +9,7 @@ class SequenceThread; -class CMyASICam:public CCameraBase +class CMyASICam:public CLegacyCameraBase { public: CMyASICam(void); diff --git a/DeviceAdapters/dc1394/dc1394.h b/DeviceAdapters/dc1394/dc1394.h index 7b6fa30e2..b08137934 100644 --- a/DeviceAdapters/dc1394/dc1394.h +++ b/DeviceAdapters/dc1394/dc1394.h @@ -114,7 +114,7 @@ class DC1394Context class AcqSequenceThread; -class Cdc1394 : public CCameraBase +class Cdc1394 : public CLegacyCameraBase { friend class AcqSequenceThread; diff --git a/MMCore/CoreCallback.cpp b/MMCore/CoreCallback.cpp index 572f9a0ce..27fddb5fa 100644 --- a/MMCore/CoreCallback.cpp +++ b/MMCore/CoreCallback.cpp @@ -251,6 +251,18 @@ int CoreCallback::InsertImage(const MM::Device* caller, const unsigned char* buf int CoreCallback::InsertImage(const MM::Device* caller, const unsigned char* buf, unsigned width, unsigned height, unsigned byteDepth, const Metadata* pMd, bool doProcess) { + // Check if the camera is currently snapping an image, if its calling InsertImage, + // then its circumventing having its own buffer. This allows camera device adapters to + // not have to implement their own buffers, and can instead just copy data in directly. + { + std::shared_ptr camera = std::static_pointer_cast(core_->deviceManager_->GetDevice(caller)); + if (camera->IsSnapping()) { + // Don't do any metadata or image processing, for consistency with the existing SnapImage()/GetImage() methods + camera->StoreSnappedImage(buf, width, height, byteDepth); + return DEVICE_OK; + } + } + try { Metadata md = AddCameraMetadata(caller, pMd); @@ -283,6 +295,18 @@ int CoreCallback::InsertImage(const MM::Device* caller, const unsigned char* buf int CoreCallback::InsertImage(const MM::Device* caller, const unsigned char* buf, unsigned width, unsigned height, unsigned byteDepth, unsigned nComponents, const Metadata* pMd, bool doProcess) { + // Check if the camera is currently snapping an image, if its calling InsertImage, + // then its circumventing having its own buffer. This allows camera device adapters to + // not have to implement their own buffers, and can instead just copy data in directly. + { + std::shared_ptr camera = std::static_pointer_cast(core_->deviceManager_->GetDevice(caller)); + if (camera->IsSnapping()) { + // Don't do any metadata or image processing, for consistency with the existing SnapImage()/GetImage() methods + camera->StoreSnappedImage(buf, width, height, byteDepth); + return DEVICE_OK; + } + } + try { Metadata md = AddCameraMetadata(caller, pMd); @@ -659,6 +683,15 @@ int CoreCallback::OnMagnifierChanged(const MM::Device* /* device */) return DEVICE_OK; } +int CoreCallback::OnCameraEvent(const MM::Device* device, const char* eventName, unsigned long timestamp, unsigned long eventId, const char* data) +{ + if (core_->externalCallback_) { + char label[MM::MaxStrLength]; + device->GetLabel(label); + core_->externalCallback_->onCameraEvent(label, eventName, timestamp, eventId, data); + } + return DEVICE_OK; +} int CoreCallback::SetSerialProperties(const char* portName, diff --git a/MMCore/CoreCallback.h b/MMCore/CoreCallback.h index 275493b42..b407c0394 100644 --- a/MMCore/CoreCallback.h +++ b/MMCore/CoreCallback.h @@ -120,7 +120,7 @@ class CoreCallback : public MM::Core int OnExposureChanged(const MM::Device* device, double newExposure); int OnSLMExposureChanged(const MM::Device* device, double newExposure); int OnMagnifierChanged(const MM::Device* device); - + int OnCameraEvent(const MM::Device* device, const char* eventName, unsigned long timestamp, unsigned long eventId, const char* data); void NextPostedError(int& errorCode, char* pMessage, int maxlen, int& messageLength); void PostError(const int errorCode, const char* pMessage); diff --git a/MMCore/Devices/CameraInstance.cpp b/MMCore/Devices/CameraInstance.cpp index 8633d1b5d..c864932d6 100644 --- a/MMCore/Devices/CameraInstance.cpp +++ b/MMCore/Devices/CameraInstance.cpp @@ -21,11 +21,104 @@ #include "CameraInstance.h" +//// New Camera API + int CameraInstance::TriggerSoftware() { + if (!GetImpl()->IsNewAPIImplemented()) { + // Exception + throw CMMError("TriggerSoftware is not implemented in the old camera API"); + } else { + return GetImpl()->TriggerSoftware(); + } + } + + int CameraInstance::AcquisitionStart() { + if (!GetImpl()->IsNewAPIImplemented()) { + if (frameCount_ <= 0) { + // Go until stopped + return GetImpl()->StartSequenceAcquisition(0); + } else { + return GetImpl()->StartSequenceAcquisition(frameCount_, 0, true); + } + } else { + return GetImpl()->AcquisitionStart(); + } + } + + int CameraInstance::AcquisitionArm(int frameCount) { + if (!GetImpl()->IsNewAPIImplemented()) { + frameCount_ = frameCount; + return GetImpl()->PrepareSequenceAcqusition(); + } else { + return GetImpl()->AcquisitionArm(frameCount); + } + } + + int CameraInstance::AcquisitionStop() { + if (!GetImpl()->IsNewAPIImplemented()) { + return GetImpl()->StopSequenceAcquisition(); + } else { + return GetImpl()->AcquisitionStop(); + } + } + + int CameraInstance::AcquisitionAbort() { + if (!GetImpl()->IsNewAPIImplemented()) { + return GetImpl()->StopSequenceAcquisition(); + } else { + return GetImpl()->AcquisitionAbort(); + } + } +// End New Camera API + +int CameraInstance::SnapImage() { + RequireInitialized(__func__); + isSnapping_.store(true); + multiChannelImageCounter_.store(0); + int ret = DEVICE_OK; + if (!GetImpl()->IsNewAPIImplemented()) { + ret = GetImpl()->SnapImage(); + } else { + ret = GetImpl()->AcquisitionArm(1); + if (ret == DEVICE_OK) { + ret = GetImpl()->AcquisitionStart(); + // Use condition variable to wait for image capture + std::unique_lock lock(imageMutex_); + while (multiChannelImageCounter_.load() != (int) GetImpl()->GetNumberOfChannels()) { + imageAvailable_.wait(lock); + } + } + } + isSnapping_.store(false); + return ret; +} + +const unsigned char* CameraInstance::GetImageBuffer() { + RequireInitialized(__func__); + const unsigned char* snappedPixels = snappedImage_.GetPixels(0); + if (snappedPixels != nullptr) { + return snappedPixels; + } + return GetImpl()->GetImageBuffer(); +} + +const unsigned char* CameraInstance::GetImageBuffer(unsigned channelNr) { + RequireInitialized(__func__); + const unsigned char* snappedPixels = snappedImage_.GetPixels(channelNr); + if (snappedPixels != nullptr) { + return snappedPixels; + } + return GetImpl()->GetImageBuffer(channelNr); +} + +const unsigned int* CameraInstance::GetImageBufferAsRGB32() { + RequireInitialized(__func__); + const unsigned int* snappedPixels = reinterpret_cast(snappedImage_.GetPixels(0)); + if (snappedPixels != nullptr) { + return snappedPixels; + } + return GetImpl()->GetImageBufferAsRGB32(); +} -int CameraInstance::SnapImage() { RequireInitialized(__func__); return GetImpl()->SnapImage(); } -const unsigned char* CameraInstance::GetImageBuffer() { RequireInitialized(__func__); return GetImpl()->GetImageBuffer(); } -const unsigned char* CameraInstance::GetImageBuffer(unsigned channelNr) { RequireInitialized(__func__); return GetImpl()->GetImageBuffer(channelNr); } -const unsigned int* CameraInstance::GetImageBufferAsRGB32() { RequireInitialized(__func__); return GetImpl()->GetImageBufferAsRGB32(); } unsigned CameraInstance::GetNumberOfComponents() const { RequireInitialized(__func__); return GetImpl()->GetNumberOfComponents(); } std::string CameraInstance::GetComponentName(unsigned component) @@ -127,10 +220,62 @@ int CameraInstance::GetMultiROI(unsigned* xs, unsigned* ys, unsigned* widths, return GetImpl()->GetMultiROI(xs, ys, widths, heights, length); } -int CameraInstance::StartSequenceAcquisition(long numImages, double interval_ms, bool stopOnOverflow) { RequireInitialized(__func__); return GetImpl()->StartSequenceAcquisition(numImages, interval_ms, stopOnOverflow); } -int CameraInstance::StartSequenceAcquisition(double interval_ms) { RequireInitialized(__func__); return GetImpl()->StartSequenceAcquisition(interval_ms); } -int CameraInstance::StopSequenceAcquisition() { RequireInitialized(__func__); return GetImpl()->StopSequenceAcquisition(); } -int CameraInstance::PrepareSequenceAcqusition() { RequireInitialized(__func__); return GetImpl()->PrepareSequenceAcqusition(); } +int CameraInstance::StartSequenceAcquisition(long numImages, double interval_ms, bool stopOnOverflow) { + RequireInitialized(__func__); + + if (!GetImpl()->IsNewAPIImplemented()) { + return GetImpl()->StartSequenceAcquisition(numImages, interval_ms, stopOnOverflow); + } else { + int ret = GetImpl()->AcquisitionArm(numImages); + if (ret != DEVICE_OK) { + return ret; + } + ret = GetImpl()->AcquisitionStart(); + if (ret != DEVICE_OK) { + return ret; + } + return DEVICE_OK; + } +} + +int CameraInstance::StartSequenceAcquisition(double interval_ms) { + RequireInitialized(__func__); + + if (!GetImpl()->IsNewAPIImplemented()) { + return GetImpl()->StartSequenceAcquisition(interval_ms); + } else { + int ret = GetImpl()->AcquisitionArm(0); // 0 means until stopped (e.g. live mode) + if (ret != DEVICE_OK) { + return ret; + } + ret = GetImpl()->AcquisitionStart(); + if (ret != DEVICE_OK) { + return ret; + } + return DEVICE_OK; + } +} + +int CameraInstance::StopSequenceAcquisition() { + RequireInitialized(__func__); + if (!GetImpl()->IsNewAPIImplemented()) { + return GetImpl()->StopSequenceAcquisition(); + } else { + return GetImpl()->AcquisitionStop(); + } +} + +int CameraInstance::PrepareSequenceAcqusition() { + RequireInitialized(__func__); + if (!GetImpl()->IsNewAPIImplemented()) { + return GetImpl()->PrepareSequenceAcqusition(); + } else { + // nothing to do here. The new API has an arm method, but it requires the number of frames to acquire + // so it can't be called here. + return DEVICE_OK; + } +} + bool CameraInstance::IsCapturing() { RequireInitialized(__func__); return GetImpl()->IsCapturing(); } std::string CameraInstance::GetTags() @@ -154,3 +299,27 @@ int CameraInstance::StopExposureSequence() { RequireInitialized(__func__); retur int CameraInstance::ClearExposureSequence() { RequireInitialized(__func__); return GetImpl()->ClearExposureSequence(); } int CameraInstance::AddToExposureSequence(double exposureTime_ms) { RequireInitialized(__func__); return GetImpl()->AddToExposureSequence(exposureTime_ms); } int CameraInstance::SendExposureSequence() const { RequireInitialized(__func__); return GetImpl()->SendExposureSequence(); } + +bool CameraInstance::IsSnapping() const { + return isSnapping_.load(); +} + +void CameraInstance::StoreSnappedImage(const unsigned char* buf, unsigned width, unsigned height, + unsigned byteDepth) { + // If the buffer doesn't exist or has wrong dimensions, create/resize it + if (snappedImage_.Width() != width || + snappedImage_.Height() != height || + snappedImage_.Depth() != byteDepth) { + snappedImage_.Resize(width, height, byteDepth); + } + + // For multi-channel cameras, insertImage will be called once for each channel + snappedImage_.SetPixels(multiChannelImageCounter_.load(), buf); + multiChannelImageCounter_.fetch_add(1); + + // Notify any waiting threads that the image is available + { + std::lock_guard lock(imageMutex_); + imageAvailable_.notify_all(); + } +} diff --git a/MMCore/Devices/CameraInstance.h b/MMCore/Devices/CameraInstance.h index f228a37e6..0b305edba 100644 --- a/MMCore/Devices/CameraInstance.h +++ b/MMCore/Devices/CameraInstance.h @@ -20,6 +20,10 @@ #pragma once #include "DeviceInstanceBase.h" +#include +#include +#include +#include "../FrameBuffer.h" class CameraInstance : public DeviceInstanceBase @@ -36,6 +40,16 @@ class CameraInstance : public DeviceInstanceBase DeviceInstanceBase(core, adapter, name, pDevice, deleteFunction, label, deviceLogger, coreLogger) {} + + // New Camera API + int TriggerSoftware(); + int AcquisitionStart(); + int AcquisitionArm(int frameCount); + int AcquisitionArm(); + int AcquisitionStop(); + int AcquisitionAbort(); + /// End New Camera API + int SnapImage(); const unsigned char* GetImageBuffer(); const unsigned char* GetImageBuffer(unsigned channelNr); @@ -82,4 +96,25 @@ class CameraInstance : public DeviceInstanceBase int ClearExposureSequence(); int AddToExposureSequence(double exposureTime_ms); int SendExposureSequence() const; + + /** + * Checks if the camera is currently snapping an image. + * Thread-safe method that can be called from any thread. + * @return true if the camera is currently snapping an image, false otherwise + */ + bool IsSnapping() const; + void StoreSnappedImage(const unsigned char* buf, unsigned width, unsigned height, unsigned byteDepth); + +private: + // Atomic flag to track if the camera is currently snapping an image + std::atomic isSnapping_{false}; + + // Frame buffer to store captured images + mm::FrameBuffer snappedImage_; + std::mutex imageMutex_; + std::condition_variable imageAvailable_; + std::atomic multiChannelImageCounter_{0}; + + // Used for interconversion between old and new camera API + int frameCount_; }; diff --git a/MMCore/Devices/DeviceInstance.cpp b/MMCore/Devices/DeviceInstance.cpp index a36ea75b7..0c2602297 100644 --- a/MMCore/Devices/DeviceInstance.cpp +++ b/MMCore/Devices/DeviceInstance.cpp @@ -376,6 +376,16 @@ DeviceInstance::Initialize() ThrowError("Device already initialized (or initialization already attempted)"); initializeCalled_ = true; ThrowIfError(pImpl_->Initialize()); + + // Check for that all required standard properties implemented + char failedProperty[MM::MaxStrLength]; + if (!pImpl_->ImplementsOrSkipsStandardProperties(failedProperty)) { + // shutdown the device + Shutdown(); + ThrowError("Device " + GetLabel() + + " does not implement required standard property: " + + std::string(failedProperty)); + } initialized_ = true; } diff --git a/MMCore/MMCore.cpp b/MMCore/MMCore.cpp index a768c7cdf..637bec1c6 100644 --- a/MMCore/MMCore.cpp +++ b/MMCore/MMCore.cpp @@ -2799,6 +2799,222 @@ long CMMCore::getImageBufferSize() return 0; } + +std::shared_ptr CMMCore::GetCurrentCameraDevice() throw (CMMError) +{ + std::shared_ptr camera = currentCameraDevice_.lock(); + if (!camera) + { + throw CMMError(getCoreErrorText(MMERR_CameraNotAvailable).c_str(), MMERR_CameraNotAvailable); + } + return camera; +} + +// Helper function to check if camera is capturing +void CMMCore::CheckCameraCapturing(std::shared_ptr camera) throw (CMMError) +{ + if (camera->IsCapturing()) + { + throw CMMError(getCoreErrorText(MMERR_NotAllowedDuringSequenceAcquisition).c_str(), + MMERR_NotAllowedDuringSequenceAcquisition); + } +} + +// Helper function to initialize circular buffer +void CMMCore::InitializeCircularBufferForCamera(std::shared_ptr camera) throw (CMMError) +{ + try + { + if (!cbuf_->Initialize(camera->GetNumberOfChannels(), camera->GetImageWidth(), + camera->GetImageHeight(), camera->GetImageBytesPerPixel())) + { + logError(getDeviceName(camera).c_str(), getCoreErrorText(MMERR_CircularBufferFailedToInitialize).c_str()); + throw CMMError(getCoreErrorText(MMERR_CircularBufferFailedToInitialize).c_str(), + MMERR_CircularBufferFailedToInitialize); + } + cbuf_->Clear(); + } + catch (std::bad_alloc& ex) + { + std::ostringstream messs; + messs << getCoreErrorText(MMERR_OutOfMemory).c_str() << " " << ex.what() << '\n'; + throw CMMError(messs.str().c_str(), MMERR_OutOfMemory); + } +} + +//// New camera API + +/** + * Trigger the camera to capture an image via software. + */ +void CMMCore::triggerCamera() throw (CMMError) +{ + std::shared_ptr camera = GetCurrentCameraDevice(); + mm::DeviceModuleLockGuard guard(camera); + + int ret = camera->TriggerSoftware(); + if (ret != DEVICE_OK) + throw CMMError(getDeviceErrorText(ret, camera).c_str(), MMERR_DEVICE_GENERIC); +} + +/** + * Trigger the specified camera to capture an image via software. + */ +void CMMCore::triggerCamera(const char* cameraLabel) throw (CMMError) +{ + std::shared_ptr pCamera = + deviceManager_->GetDeviceOfType(cameraLabel); + + mm::DeviceModuleLockGuard guard(pCamera); + int ret = pCamera->TriggerSoftware(); + if (ret != DEVICE_OK) + throw CMMError(getDeviceErrorText(ret, pCamera).c_str(), MMERR_DEVICE_GENERIC); +} + +void CMMCore::acquisitionStart() throw (CMMError) +{ + { + MMThreadGuard g(*pPostedErrorsLock_); + postedErrors_.clear(); + } + + std::shared_ptr camera = GetCurrentCameraDevice(); + mm::DeviceModuleLockGuard guard(camera); + + CheckCameraCapturing(camera); + + int ret = camera->AcquisitionStart(); + if (ret != DEVICE_OK) + throw CMMError(getDeviceErrorText(ret, camera).c_str(), MMERR_DEVICE_GENERIC); +} + +void CMMCore::acquisitionStart(const char* cameraLabel) throw (CMMError) +{ + { + MMThreadGuard g(*pPostedErrorsLock_); + postedErrors_.clear(); + } + + std::shared_ptr pCamera = + deviceManager_->GetDeviceOfType(cameraLabel); + + mm::DeviceModuleLockGuard guard(pCamera); + + CheckCameraCapturing(pCamera); + + int ret = pCamera->AcquisitionStart(); + if (ret != DEVICE_OK) + throw CMMError(getDeviceErrorText(ret, pCamera).c_str(), MMERR_DEVICE_GENERIC); +} + +void CMMCore::acquisitionArm(int frameCount) throw (CMMError) +{ + std::shared_ptr camera = GetCurrentCameraDevice(); + mm::DeviceModuleLockGuard guard(camera); + + + CheckCameraCapturing(camera); + InitializeCircularBufferForCamera(camera); + + int ret = camera->AcquisitionArm(frameCount); + if (ret != DEVICE_OK) + throw CMMError(getDeviceErrorText(ret, camera).c_str(), MMERR_DEVICE_GENERIC); +} + +void CMMCore::acquisitionArm(const char* cameraLabel, int frameCount) throw (CMMError) +{ + std::shared_ptr pCamera = + deviceManager_->GetDeviceOfType(cameraLabel); + + mm::DeviceModuleLockGuard guard(pCamera); + + CheckCameraCapturing(pCamera); + + InitializeCircularBufferForCamera(pCamera); + + int ret = pCamera->AcquisitionArm(frameCount); + if (ret != DEVICE_OK) + throw CMMError(getDeviceErrorText(ret, pCamera).c_str(), MMERR_DEVICE_GENERIC); +} + +void CMMCore::acquisitionStop() throw (CMMError) +{ + std::shared_ptr camera = GetCurrentCameraDevice(); + mm::DeviceModuleLockGuard guard(camera); + + int ret = camera->AcquisitionStop(); + if (ret != DEVICE_OK) + throw CMMError(getDeviceErrorText(ret, camera).c_str(), MMERR_DEVICE_GENERIC); +} + +void CMMCore::acquisitionStop(const char* cameraLabel) throw (CMMError) +{ + std::shared_ptr pCamera = + deviceManager_->GetDeviceOfType(cameraLabel); + + mm::DeviceModuleLockGuard guard(pCamera); + int ret = pCamera->AcquisitionStop(); + if (ret != DEVICE_OK) + throw CMMError(getDeviceErrorText(ret, pCamera).c_str(), MMERR_DEVICE_GENERIC); +} + +void CMMCore::acquisitionAbort() throw (CMMError) +{ + std::shared_ptr camera = GetCurrentCameraDevice(); + mm::DeviceModuleLockGuard guard(camera); + + int ret = camera->AcquisitionAbort(); + if (ret != DEVICE_OK) + throw CMMError(getDeviceErrorText(ret, camera).c_str(), MMERR_DEVICE_GENERIC); +} + +void CMMCore::acquisitionAbort(const char* cameraLabel) throw (CMMError) +{ + std::shared_ptr pCamera = + deviceManager_->GetDeviceOfType(cameraLabel); + + mm::DeviceModuleLockGuard guard(pCamera); + int ret = pCamera->AcquisitionAbort(); + if (ret != DEVICE_OK) + throw CMMError(getDeviceErrorText(ret, pCamera).c_str(), MMERR_DEVICE_GENERIC); +} + +bool CMMCore::isAcquisitionRunning() throw (CMMError) +{ + std::shared_ptr camera = currentCameraDevice_.lock(); + if (camera) + { + try + { + mm::DeviceModuleLockGuard guard(camera); + return camera->IsCapturing(); + } + catch (const CMMError&) // Possibly uninitialized camera + { + // Fall through + } + } + return false; +}; + +/** + * Check if the specified camera is acquiring the sequence + * Returns false when the sequence is done + */ +bool CMMCore::isAcquisitionRunning(const char* label) throw (CMMError) +{ + std::shared_ptr pCam = + deviceManager_->GetDeviceOfType(label); + + mm::DeviceModuleLockGuard guard(pCam); + return pCam->IsCapturing(); +}; + + +// End new camera API + + + /** * Starts streaming camera sequence acquisition. * This command does not block the calling thread for the duration of the acquisition. @@ -2815,43 +3031,16 @@ void CMMCore::startSequenceAcquisition(long numImages, double intervalMs, bool s postedErrors_.clear(); } - std::shared_ptr camera = currentCameraDevice_.lock(); - if (camera) - { - if(camera->IsCapturing()) - { - throw CMMError(getCoreErrorText( - MMERR_NotAllowedDuringSequenceAcquisition).c_str() - ,MMERR_NotAllowedDuringSequenceAcquisition); - } + std::shared_ptr camera = GetCurrentCameraDevice(); - try - { - if (!cbuf_->Initialize(camera->GetNumberOfChannels(), camera->GetImageWidth(), camera->GetImageHeight(), camera->GetImageBytesPerPixel())) - { - logError(getDeviceName(camera).c_str(), getCoreErrorText(MMERR_CircularBufferFailedToInitialize).c_str()); - throw CMMError(getCoreErrorText(MMERR_CircularBufferFailedToInitialize).c_str(), MMERR_CircularBufferFailedToInitialize); - } - cbuf_->Clear(); - mm::DeviceModuleLockGuard guard(camera); + CheckCameraCapturing(camera); + InitializeCircularBufferForCamera(camera); - LOG_DEBUG(coreLogger_) << "Will start sequence acquisition from default camera"; - int nRet = camera->StartSequenceAcquisition(numImages, intervalMs, stopOnOverflow); - if (nRet != DEVICE_OK) - throw CMMError(getDeviceErrorText(nRet, camera).c_str(), MMERR_DEVICE_GENERIC); - } - catch (std::bad_alloc& ex) - { - std::ostringstream messs; - messs << getCoreErrorText(MMERR_OutOfMemory).c_str() << " " << ex.what() << '\n'; - throw CMMError(messs.str().c_str() , MMERR_OutOfMemory); - } - } - else - { - logError(getDeviceName(camera).c_str(), getCoreErrorText(MMERR_CameraNotAvailable).c_str()); - throw CMMError(getCoreErrorText(MMERR_CameraNotAvailable).c_str(), MMERR_CameraNotAvailable); - } + mm::DeviceModuleLockGuard guard(camera); + LOG_DEBUG(coreLogger_) << "Will start sequence acquisition from default camera"; + int nRet = camera->StartSequenceAcquisition(numImages, intervalMs, stopOnOverflow); + if (nRet != DEVICE_OK) + throw CMMError(getDeviceErrorText(nRet, camera).c_str(), MMERR_DEVICE_GENERIC); LOG_DEBUG(coreLogger_) << "Did start sequence acquisition from default camera"; } @@ -2863,21 +3052,12 @@ void CMMCore::startSequenceAcquisition(long numImages, double intervalMs, bool s */ void CMMCore::startSequenceAcquisition(const char* label, long numImages, double intervalMs, bool stopOnOverflow) throw (CMMError) { - std::shared_ptr pCam = - deviceManager_->GetDeviceOfType(label); + std::shared_ptr pCam = deviceManager_->GetDeviceOfType(label); mm::DeviceModuleLockGuard guard(pCam); - if(pCam->IsCapturing()) - throw CMMError(getCoreErrorText(MMERR_NotAllowedDuringSequenceAcquisition).c_str(), - MMERR_NotAllowedDuringSequenceAcquisition); + CheckCameraCapturing(pCam); + InitializeCircularBufferForCamera(pCam); - if (!cbuf_->Initialize(pCam->GetNumberOfChannels(), pCam->GetImageWidth(), pCam->GetImageHeight(), pCam->GetImageBytesPerPixel())) - { - logError(getDeviceName(pCam).c_str(), getCoreErrorText(MMERR_CircularBufferFailedToInitialize).c_str()); - throw CMMError(getCoreErrorText(MMERR_CircularBufferFailedToInitialize).c_str(), MMERR_CircularBufferFailedToInitialize); - } - cbuf_->Clear(); - LOG_DEBUG(coreLogger_) << "Will start sequence acquisition from camera " << label; int nRet = pCam->StartSequenceAcquisition(numImages, intervalMs, stopOnOverflow); @@ -2898,9 +3078,7 @@ void CMMCore::prepareSequenceAcquisition(const char* label) throw (CMMError) deviceManager_->GetDeviceOfType(label); mm::DeviceModuleLockGuard guard(pCam); - if(pCam->IsCapturing()) - throw CMMError(getCoreErrorText(MMERR_NotAllowedDuringSequenceAcquisition).c_str(), - MMERR_NotAllowedDuringSequenceAcquisition); + CheckCameraCapturing(pCam); LOG_DEBUG(coreLogger_) << "Will prepare camera " << label << " for sequence acquisition"; @@ -2922,12 +3100,7 @@ void CMMCore::initializeCircularBuffer() throw (CMMError) if (camera) { mm::DeviceModuleLockGuard guard(camera); - if (!cbuf_->Initialize(camera->GetNumberOfChannels(), camera->GetImageWidth(), camera->GetImageHeight(), camera->GetImageBytesPerPixel())) - { - logError(getDeviceName(camera).c_str(), getCoreErrorText(MMERR_CircularBufferFailedToInitialize).c_str()); - throw CMMError(getCoreErrorText(MMERR_CircularBufferFailedToInitialize).c_str(), MMERR_CircularBufferFailedToInitialize); - } - cbuf_->Clear(); + InitializeCircularBufferForCamera(camera); } else { @@ -2963,33 +3136,16 @@ void CMMCore::stopSequenceAcquisition(const char* label) throw (CMMError) */ void CMMCore::startContinuousSequenceAcquisition(double intervalMs) throw (CMMError) { - std::shared_ptr camera = currentCameraDevice_.lock(); - if (camera) - { - mm::DeviceModuleLockGuard guard(camera); - if(camera->IsCapturing()) - { - throw CMMError(getCoreErrorText( - MMERR_NotAllowedDuringSequenceAcquisition).c_str() - ,MMERR_NotAllowedDuringSequenceAcquisition); - } + std::shared_ptr camera = GetCurrentCameraDevice(); + mm::DeviceModuleLockGuard guard(camera); + CheckCameraCapturing(camera); + InitializeCircularBufferForCamera(camera); + + LOG_DEBUG(coreLogger_) << "Will start continuous sequence acquisition from current camera"; + int nRet = camera->StartSequenceAcquisition(intervalMs); + if (nRet != DEVICE_OK) + throw CMMError(getDeviceErrorText(nRet, camera).c_str(), MMERR_DEVICE_GENERIC); - if (!cbuf_->Initialize(camera->GetNumberOfChannels(), camera->GetImageWidth(), camera->GetImageHeight(), camera->GetImageBytesPerPixel())) - { - logError(getDeviceName(camera).c_str(), getCoreErrorText(MMERR_CircularBufferFailedToInitialize).c_str()); - throw CMMError(getCoreErrorText(MMERR_CircularBufferFailedToInitialize).c_str(), MMERR_CircularBufferFailedToInitialize); - } - cbuf_->Clear(); - LOG_DEBUG(coreLogger_) << "Will start continuous sequence acquisition from current camera"; - int nRet = camera->StartSequenceAcquisition(intervalMs); - if (nRet != DEVICE_OK) - throw CMMError(getDeviceErrorText(nRet, camera).c_str(), MMERR_DEVICE_GENERIC); - } - else - { - logError("no camera available", getCoreErrorText(MMERR_CameraNotAvailable).c_str()); - throw CMMError(getCoreErrorText(MMERR_CameraNotAvailable).c_str(), MMERR_CameraNotAvailable); - } LOG_DEBUG(coreLogger_) << "Did start continuous sequence acquisition from current camera"; } @@ -3025,20 +3181,7 @@ void CMMCore::stopSequenceAcquisition() throw (CMMError) */ bool CMMCore::isSequenceRunning() throw () { - std::shared_ptr camera = currentCameraDevice_.lock(); - if (camera) - { - try - { - mm::DeviceModuleLockGuard guard(camera); - return camera->IsCapturing(); - } - catch (const CMMError&) // Possibly uninitialized camera - { - // Fall through - } - } - return false; + return isAcquisitionRunning(); }; /** @@ -3047,11 +3190,7 @@ bool CMMCore::isSequenceRunning() throw () */ bool CMMCore::isSequenceRunning(const char* label) throw (CMMError) { - std::shared_ptr pCam = - deviceManager_->GetDeviceOfType(label); - - mm::DeviceModuleLockGuard guard(pCam); - return pCam->IsCapturing(); + return isAcquisitionRunning(label); }; /** diff --git a/MMCore/MMCore.h b/MMCore/MMCore.h index 80d463f07..457ad60d5 100644 --- a/MMCore/MMCore.h +++ b/MMCore/MMCore.h @@ -388,10 +388,26 @@ class CMMCore double getExposure() throw (CMMError); double getExposure(const char* label) throw (CMMError); + // TODO: deprecate? void snapImage() throw (CMMError); void* getImage() throw (CMMError); void* getImage(unsigned numChannel) throw (CMMError); + // New Camera API methods + void triggerCamera() throw (CMMError); + void triggerCamera(const char* cameraLabel) throw (CMMError); + void acquisitionStart() throw (CMMError); + void acquisitionStart(const char* cameraLabel) throw (CMMError); + void acquisitionArm(int frameCount) throw (CMMError); + void acquisitionArm(const char* cameraLabel, int frameCount) throw (CMMError); + void acquisitionStop() throw (CMMError); + void acquisitionStop(const char* cameraLabel) throw (CMMError); + void acquisitionAbort() throw (CMMError); + void acquisitionAbort(const char* cameraLabel) throw (CMMError); + bool isAcquisitionRunning() throw (CMMError); + bool isAcquisitionRunning(const char* cameraLabel) throw (CMMError); + // End new camera API + unsigned getImageWidth(); unsigned getImageHeight(); unsigned getBytesPerPixel(); @@ -685,6 +701,11 @@ class CMMCore mutable std::deque > postedErrors_; private: + // Helper functions for sequence acquisitions + std::shared_ptr GetCurrentCameraDevice() throw (CMMError); + void CheckCameraCapturing(std::shared_ptr camera) throw (CMMError); + void InitializeCircularBufferForCamera(std::shared_ptr camera) throw (CMMError); + void InitializeErrorMessages(); void CreateCoreProperties(); diff --git a/MMCore/MMEventCallback.h b/MMCore/MMEventCallback.h index c0ccb8a57..9c908346f 100644 --- a/MMCore/MMEventCallback.h +++ b/MMCore/MMEventCallback.h @@ -86,4 +86,9 @@ class MMEventCallback std::cout << "onSLMExposureChanged()" << name << " " << newExposure << '\n'; } + virtual void onCameraEvent(const char* name, const char* eventName, unsigned long timestamp, unsigned long frameId, const char* data) + { + std::cout << "onCameraEvent()" << name << " " << eventName << " " << timestamp << " " << frameId << " " << data << '\n'; + } + }; diff --git a/MMDevice/DeviceBase.h b/MMDevice/DeviceBase.h index 84cef7177..418bab984 100644 --- a/MMDevice/DeviceBase.h +++ b/MMDevice/DeviceBase.h @@ -38,6 +38,8 @@ #include #include #include +#include +#include // common error messages const char* const g_Msg_ERR = "Unknown error in the device"; @@ -117,6 +119,284 @@ class CDeviceBase : public T CDeviceUtils::CopyLimitedString(name, moduleName_.c_str()); } + //// Standard properties are created using only these dedicated functions + // Such functions should all be defined here, and which device types they apply + // + // to is handled in MMDevice.h using the MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE macro + // int CreateTestStandardProperty(const char* value, MM::ActionFunctor* pAct = 0) { + // return CreateStandardProperty(value, pAct); + // } + + // int CreateTestWithValuesStandardProperty(const char* value, MM::ActionFunctor* pAct = 0) { + // // just make the values the required ones here. Also option to add + // // additional ones in real situations + // return CreateStandardProperty(value, pAct, + // MM::g_TestWithValuesStandardProperty.requiredValues); + // } + + // Every standard property must either be created or explicitly skipped using + // a method like this + // void SkipTestStandardProperty() { + // SkipStandardProperty(); + // } + + // Camera triggering API standard properties + int CreateTriggerSelectorStandardProperty(const char* value, const std::vector& values, MM::ActionFunctor* pAct = 0) { + return CreateStandardProperty(value, pAct, values); + } + + int SetTriggerSelectorStandardPropertyValues(const std::vector& values) { + return SetStandardPropertyValues(values); + } + + void SkipTriggerSelectorStandardProperty() { + SkipStandardProperty(); + } + + int CreateTriggerModeStandardProperty(const char* value, const std::vector& values, MM::ActionFunctor* pAct = 0) { + return CreateStandardProperty(value, pAct, values); + } + + int SetTriggerModeStandardPropertyValues(const std::vector& values) { + return SetStandardPropertyValues(values); + } + + void SkipTriggerModeStandardProperty() { + SkipStandardProperty(); + } + + int CreateTriggerSourceStandardProperty(const char* value, const std::vector& values, MM::ActionFunctor* pAct = 0) { + return CreateStandardProperty(value, pAct, values); + } + + int SetTriggerSourceStandardPropertyValues(const std::vector& values) { + return SetStandardPropertyValues(values); + } + + void SkipTriggerSourceStandardProperty() { + SkipStandardProperty(); + } + + int CreateTriggerActivationStandardProperty(const char* value, const std::vector& values, MM::ActionFunctor* pAct = 0) { + return CreateStandardProperty(value, pAct, values); + } + + int SetTriggerActivationStandardPropertyValues(const std::vector& values) { + return SetStandardPropertyValues(values); + } + + void SkipTriggerActivationStandardProperty() { + SkipStandardProperty(); + } + + int CreateTriggerDelayStandardProperty(const char* value, double minValue, double maxValue, MM::ActionFunctor* pAct = 0) { + int ret = CreateStandardProperty(value, pAct); + if (ret != DEVICE_OK) + return ret; + + std::string fullName = MM::g_KeywordStandardPropertyPrefix; + fullName += MM::g_TriggerDelayProperty.name; + return SetPropertyLimits(fullName.c_str(), minValue, maxValue); + } + + void SkipTriggerDelayStandardProperty() { + SkipStandardProperty(); + } + + int CreateExposureModeStandardProperty(const char* value, const std::vector& values, MM::ActionFunctor* pAct = 0) { + return CreateStandardProperty(value, pAct, values); + } + + int SetExposureModeStandardPropertyValues(const std::vector& values) { + return SetStandardPropertyValues(values); + } + + int CreateExposureTimeStandardProperty(const char* value, double min, double max, MM::ActionFunctor* pAct = 0) { + int ret = CreateStandardProperty(value, pAct); + if (ret != DEVICE_OK) + return ret; + + std::string fullName = MM::g_KeywordStandardPropertyPrefix; + fullName += MM::g_ExposureTimeProperty.name; + return SetPropertyLimits(fullName.c_str(), min, max); + } + + void SkipExposureTimeStandardProperty() { + SkipStandardProperty(); + } + + void SkipExposureModeStandardProperty() { + SkipStandardProperty(); + } + + int CreateAcquisitionBurstFrameCountStandardProperty(const char* value, MM::ActionFunctor* pAct = 0) { + return CreateStandardProperty(value, pAct); + } + + void SkipAcquisitionBurstFrameCountStandardProperty() { + SkipStandardProperty(); + } + + int CreateLineSelectorStandardProperty(const char* value, const std::vector& values, MM::ActionFunctor* pAct = 0) { + return CreateStandardProperty(value, pAct, values); + } + + int SetLineSelectorStandardPropertyValues(const std::vector& values) { + return SetStandardPropertyValues(values); + } + + void SkipLineSelectorStandardProperty() { + SkipStandardProperty(); + } + + int CreateLineModeStandardProperty(const char* value, const std::vector& values, MM::ActionFunctor* pAct = 0) { + return CreateStandardProperty(value, pAct, values); + } + + int SetLineModeStandardPropertyValues(const std::vector& values) { + return SetStandardPropertyValues(values); + } + + void SkipLineModeStandardProperty() { + SkipStandardProperty(); + } + + int CreateLineInverterStandardProperty(const char* value, const std::vector& values, MM::ActionFunctor* pAct = 0) { + return CreateStandardProperty(value, pAct, values); + } + + int SetLineInverterStandardPropertyValues(const std::vector& values) { + return SetStandardPropertyValues(values); + } + + void SkipLineInverterStandardProperty() { + SkipStandardProperty(); + } + + int CreateLineSourceStandardProperty(const char* value, const std::vector& values, MM::ActionFunctor* pAct = 0) { + return CreateStandardProperty(value, pAct, values); + } + + int SetLineSourceStandardPropertyValues(const std::vector& values) { + return SetStandardPropertyValues(values); + } + + void SkipLineSourceStandardProperty() { + SkipStandardProperty(); + } + + int CreateLineStatusStandardProperty(const char* value, MM::ActionFunctor* pAct = 0) { + // LineStatus has required values that are the same as its allowed values, so no need + // to take values from the user + return CreateStandardProperty(value, pAct, + MM::g_LineStatusProperty.requiredValues); + } + + int SetLineStatusStandardPropertyValues(const std::vector& values) { + return SetStandardPropertyValues(values); + } + + void SkipLineStatusStandardProperty() { + SkipStandardProperty(); + } + + int CreateAcquisitionFrameRateStandardProperty(const char* value, double min, double max, MM::ActionFunctor* pAct = 0) { + int ret = CreateStandardProperty(value, pAct); + if (ret != DEVICE_OK) + return ret; + + std::string fullName = MM::g_KeywordStandardPropertyPrefix; + fullName += MM::g_AcquisitionFrameRateProperty.name; + return SetPropertyLimits(fullName.c_str(), min, max); + } + + void SkipAcquisitionFrameRateStandardProperty() { + SkipStandardProperty(); + } + + int CreateAcquisitionFrameRateEnableStandardProperty(const char* value, MM::ActionFunctor* pAct = 0) { + return CreateStandardProperty(value, pAct, {"0", "1"}); + } + + void SkipAcquisitionFrameRateEnableStandardProperty() { + SkipStandardProperty(); + } + + int CreateAcquisitionStatusSelectorStandardProperty(const char* value, const std::vector& values, MM::ActionFunctor* pAct = 0) { + return CreateStandardProperty(value, pAct, values); + } + + int SetAcquisitionStatusSelectorStandardPropertyValues(const std::vector& values) { + return SetStandardPropertyValues(values); + } + + void SkipAcquisitionStatusSelectorStandardProperty() { + SkipStandardProperty(); + } + + int CreateAcquisitionStatusStandardProperty(const char* value, MM::ActionFunctor* pAct = 0) { + return CreateStandardProperty(value, pAct, MM::g_AcquisitionStatusProperty.requiredValues); + } + + void SkipAcquisitionStatusStandardProperty() { + SkipStandardProperty(); + } + + int CreateEventSelectorStandardProperty(const char* value, const std::vector& values, MM::ActionFunctor* pAct = 0) { + return CreateStandardProperty(value, pAct, values); + } + + int SetEventSelectorStandardPropertyValues(const std::vector& values) { + return SetStandardPropertyValues(values); + } + + void SkipEventSelectorStandardProperty() { + SkipStandardProperty(); + } + + int CreateEventNotificationStandardProperty(const char* value, MM::ActionFunctor* pAct = 0) { + return CreateStandardProperty(value, pAct, MM::g_EventNotificationProperty.requiredValues); + } + + int SetEventNotificationStandardPropertyValues(const std::vector& values) { + return SetStandardPropertyValues(values); + } + + void SkipEventNotificationStandardProperty() { + SkipStandardProperty(); + } + + int CreateTriggerOverlapStandardProperty(const char* value, const std::vector& values, MM::ActionFunctor* pAct = 0) { + return CreateStandardProperty(value, pAct, values); + } + + int SetTriggerOverlapStandardPropertyValues(const std::vector& values) { + return SetStandardPropertyValues(values); + } + + void SkipTriggerOverlapStandardProperty() { + SkipStandardProperty(); + } + + + // TODO: implement when someone who uses this feature can test it + // int CreateRollingShutterLineOffsetStandardProperty(const char* value, MM::ActionFunctor* pAct = 0) { + // return CreateStandardProperty(value, pAct); + // } + + // void SkipRollingShutterLineOffsetStandardProperty() { + // SkipStandardProperty(); + // } + + // int CreateRollingShutterActiveLinesStandardProperty(const char* value, MM::ActionFunctor* pAct = 0) { + // return CreateStandardProperty(value, pAct); + // } + + // void SkipRollingShutterActiveLinesStandardProperty() { + // SkipStandardProperty(); + // } + + /** * Assigns description string for a device (for use only by the calling code). */ @@ -534,6 +814,14 @@ class CDeviceBase : public T return false; } + virtual bool HasStandardProperty(const char* name) const + { + // prepend standard property prefix to name + std::string fullName = MM::g_KeywordStandardPropertyPrefix; + fullName += name; + return HasProperty(fullName.c_str()); + } + /** * Returns the number of allowed property values. * If the set of property values is not defined, not bounded, @@ -569,6 +857,40 @@ class CDeviceBase : public T return true; } + bool ImplementsOrSkipsStandardProperties(char* failedProperty) const { + // Get the device type + MM::DeviceType deviceType = this->GetType(); + + // Look up properties for this device type + auto it = MM::internal::GetDeviceTypeStandardPropertiesMap().find(deviceType); + if (it != MM::internal::GetDeviceTypeStandardPropertiesMap().end()) { + // Iterate through all properties for this device type + const auto& properties = it->second; + for (const auto& prop : properties) { + // Construct the full property name with prefix + std::string fullName = MM::g_KeywordStandardPropertyPrefix; + fullName += prop.name; + + // Skip checking if this property is in the skipped list + if (skippedStandardProperties_.find(fullName) != skippedStandardProperties_.end()) { + continue; + } + + // Check if the device has implemented it + if (!HasProperty(fullName.c_str())) { + // If not, copy in the name of the property and return false + CDeviceUtils::CopyLimitedString(failedProperty, fullName.c_str()); + return false; + } + } + } + + // All required properties are implemented or explicitly skipped + return true; + } + + + /** * Creates a new property for the device. * @param name - property name @@ -596,6 +918,7 @@ class CDeviceBase : public T */ int CreatePropertyWithHandler(const char* name, const char* value, MM::PropertyType eType, bool readOnly, int(U::*memberFunction)(MM::PropertyBase* pProp, MM::ActionType eAct), bool isPreInitProperty=false) { + // Check for reserved delimiter (handled in CreateProperty) CPropertyAction* pAct = new CPropertyAction((U*) this, memberFunction); return CreateProperty(name, value, eType, readOnly, pAct, isPreInitProperty); } @@ -1219,6 +1542,148 @@ class CDeviceBase : public T } private: + + /** + * Low-level implementation for creating standard properties. + * + * This template method uses SFINAE (Substitution Failure Is Not An Error) to ensure + * that standard properties can only be created for device types they're valid for. + * The IsStandardPropertyValid template specializations determine which properties + * are valid for which device types at compile time. + * + * Note: This is a private implementation method. Device implementations should use + * the specific convenience methods like CreateStandardBinningProperty() instead. + * + * @param PropPtr - Pointer to the standard property definition + * @param value - Initial value for the property + * @param pAct - Optional action functor to handle property changes + * @return DEVICE_OK if successful, error code otherwise + */ + template + typename std::enable_if::value, int>::type + CreateStandardProperty(const char* value, MM::ActionFunctor* pAct = 0, const std::vector& values = {}) { + + // Create the full property name with prefix + std::string fullName = MM::g_KeywordStandardPropertyPrefix; + fullName += PropRef.name; + + // Create the property with all appropriate fields + int ret = properties_.CreateProperty(fullName.c_str(), value, PropRef.type, + PropRef.isReadOnly, pAct, PropRef.isPreInit, true); + if (ret != DEVICE_OK) + return ret; + + // Set limits if they exist + if (PropRef.hasLimits()) { + ret = SetPropertyLimits(fullName.c_str(), PropRef.lowerLimit, PropRef.upperLimit); + if (ret != DEVICE_OK) + return ret; + } + + // Ensure the initial value is allowed if the property has predefined allowed values + if (!PropRef.allowedValues.empty()) { + if (std::find(PropRef.allowedValues.begin(), PropRef.allowedValues.end(), value) == PropRef.allowedValues.end()) { + return DEVICE_INVALID_PROPERTY_VALUE; + } + } + + // Set the allowed values using the existing SetStandardPropertyValues function + if (!values.empty() || !PropRef.requiredValues.empty()) { + ret = SetStandardPropertyValues(values); + if (ret != DEVICE_OK) + return ret; + } + + // Remove from skipped properties if it was previously marked as skipped + skippedStandardProperties_.erase(fullName); + + return DEVICE_OK; + } + + /** + * Sets allowed values for a standard property, clearing any existing values first. + * Performs the same validation as when creating the property. + * + * @param PropRef - Reference to the standard property definition + * @param values - Vector of values to set as allowed values + * @return DEVICE_OK if successful, error code otherwise + */ + template + typename std::enable_if::value, int>::type + SetStandardPropertyValues(const std::vector& values) { + // Create the full property name with prefix + std::string fullName = MM::g_KeywordStandardPropertyPrefix; + fullName += PropRef.name; + + // Check if the property exists + if (!HasProperty(fullName.c_str())) { + return DEVICE_INVALID_PROPERTY; + } + + // Ensure all supplied values are allowed if the property has predefined allowed values + if (!PropRef.allowedValues.empty()) { + for (const std::string& val : values) { + if (std::find(PropRef.allowedValues.begin(), PropRef.allowedValues.end(), val) == PropRef.allowedValues.end()) { + return DEVICE_INVALID_PROPERTY_VALUE; + } + } + } + + // Check if all required values are present + if (!PropRef.requiredValues.empty()) { + for (const std::string& val : PropRef.requiredValues) { + if (std::find(values.begin(), values.end(), val) == values.end()) { + return DEVICE_INVALID_PROPERTY_VALUE; + } + } + } + + // Clear existing values + int ret = properties_.ClearAllowedValues(fullName.c_str(), true); + if (ret != DEVICE_OK) + return ret; + + // Add the new values + for (const std::string& val : values) { + ret = properties_.AddAllowedValue(fullName.c_str(), val.c_str(), true); + if (ret != DEVICE_OK) + return ret; + } + + return DEVICE_OK; + } + + // This one is purely for providing better error messages at compile time + // When an function for setting an invalid standard property is called, + // this function will be called and will cause a compilation error. + template + typename std::enable_if::value, int>::type + CreateStandardProperty(const char* /*value*/, MM::ActionFunctor* /*pAct*/ = 0, + const std::vector& /*values*/ = std::vector()) { + static_assert(MM::internal::IsStandardPropertyValid::value, + "This standard property is not valid for this device type. Check the MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE definitions in MMDevice.h"); + return DEVICE_UNSUPPORTED_COMMAND; // This line will never execute due to the static_assert + } + + // Helper method to mark a required standard property as skipped + template + void SkipStandardProperty() { + // Only allow skipping properties that are valid for this device type + if (MM::internal::IsStandardPropertyValid::value) { + std::string fullName = MM::g_KeywordStandardPropertyPrefix; + fullName += PropRef.name; + skippedStandardProperties_.insert(fullName); + // Check if the property already exists. If so, delete it. + // This is needed because standard properties may be created dynamically not during + // initialization. For example, if they depend on the value of another property, + // and this is not known other than by setting that value on the device. In this case, + // the standard property will be created and destroyed as the values change. + if (HasProperty(fullName.c_str())) { + properties_.Delete(fullName.c_str()); + } + } + } + bool PropertyDefined(const char* propName) const { return properties_.Find(propName) != 0; @@ -1261,6 +1726,10 @@ class CDeviceBase : public T // specific information about the errant property, etc. mutable std::string morePropertyErrorInfo_; std::string parentID_; + + // Set to track which standard properties are explicitly skipped + std::set skippedStandardProperties_; + }; // Forbid instantiation of CDeviceBase @@ -1279,30 +1748,28 @@ class CGenericBase : public CDeviceBase { }; + /** -* Base class for creating camera device adapters. -* This class has a functional constructor - must be invoked -* from the derived class. +* Base class for both old and new camera APIs. +* Implements common functionality like tags and default implementations +* for optional features like multi-ROI and exposure sequence. */ template -class CCameraBase : public CDeviceBase +class CAllCamerasBase : public CDeviceBase { public: + using CDeviceBase::CreateProperty; using CDeviceBase::SetAllowedValues; using CDeviceBase::GetBinning; using CDeviceBase::GetCoreCallback; using CDeviceBase::SetProperty; using CDeviceBase::LogMessage; - virtual const unsigned char* GetImageBuffer() = 0; - virtual unsigned GetImageWidth() const = 0; - virtual unsigned GetImageHeight() const = 0; - virtual unsigned GetImageBytesPerPixel() const = 0; - virtual int SnapImage() = 0; - CCameraBase() : busy_(false), stopWhenCBOverflows_(false), thd_(0) + CAllCamerasBase() { // create and initialize common transpose properties + // TODO: if these are indeed required, should they be converted to standard properties? std::vector allowedValues; allowedValues.push_back("0"); allowedValues.push_back("1"); @@ -1314,51 +1781,18 @@ class CCameraBase : public CDeviceBase SetAllowedValues(MM::g_Keyword_Transpose_MirrorY, allowedValues); CreateProperty(MM::g_Keyword_Transpose_Correction, "0", MM::Integer, false); SetAllowedValues(MM::g_Keyword_Transpose_Correction, allowedValues); - - thd_ = new BaseSequenceThread(this); - } - - virtual ~CCameraBase() - { - if (!thd_->IsStopped()) { - thd_->Stop(); - thd_->wait(); - } - delete thd_; - } - - virtual bool Busy() {return busy_;} - - /** - * Continuous sequence acquisition. - * Default to sequence acquisition with a high number of images - */ - virtual int StartSequenceAcquisition(double interval) - { - return StartSequenceAcquisition(LONG_MAX, interval, false); - } - - /** - * Stop and wait for the thread finished - */ - virtual int StopSequenceAcquisition() - { - if (!thd_->IsStopped()) { - thd_->Stop(); - thd_->wait(); - } - - return DEVICE_OK; } /** - * Default implementation of the pixel size scaling. - */ + * Returns binnings factor. Used to calculate current pixelsize + * Not appropriately named. Implemented in DeviceBase.h + * TODO: This should perhaps be deprecated and removed. + */ virtual double GetPixelSizeUm() const {return GetBinning();} virtual unsigned GetNumberOfComponents() const { - return 1; + return 1; // Default to monochrome (ie not RGB) } virtual int GetComponentName(unsigned channel, char* name) @@ -1400,6 +1834,8 @@ class CCameraBase : public CDeviceBase return 0; } + virtual const unsigned char* GetImageBuffer() = 0; + virtual const unsigned int* GetImageBufferAsRGB32() { return 0; @@ -1414,22 +1850,9 @@ class CCameraBase : public CDeviceBase data.copy(serializedMetadata, data.size(), 0); } - // temporary debug methods - virtual int PrepareSequenceAcqusition() {return DEVICE_OK;} - - /** - * Default implementation. - */ - virtual int StartSequenceAcquisition(long numImages, double interval_ms, bool stopOnOverflow) + virtual int IsExposureSequenceable(bool& isSequenceable) const { - if (IsCapturing()) - return DEVICE_CAMERA_BUSY_ACQUIRING; - - int ret = GetCoreCallback()->PrepareForAcq(this); - if (ret != DEVICE_OK) - return ret; - thd_->Start(numImages,interval_ms); - stopWhenCBOverflows_ = stopOnOverflow; + isSequenceable = false; return DEVICE_OK; } @@ -1463,14 +1886,11 @@ class CCameraBase : public CDeviceBase return DEVICE_UNSUPPORTED_COMMAND; } - virtual bool IsCapturing(){return !thd_->IsStopped();} - virtual void AddTag(const char* key, const char* deviceLabel, const char* value) { metadata_.PutTag(key, deviceLabel, value); } - virtual void RemoveTag(const char* key) { metadata_.RemoveTag(key); @@ -1519,6 +1939,256 @@ class CCameraBase : public CDeviceBase return metadata_.GetSingleTag(key).GetValue(); } +private: + + Metadata metadata_; + +}; + + +/** +* Base class for creating camera device adapters. +* This class has a functional constructor - must be invoked +* from the derived class. +*/ +template +class COldAPICameraBase : public CAllCamerasBase +{ +public: + + // Invokes superclass constructor to get the properties declared there + COldAPICameraBase() : CAllCamerasBase() + { + // Old style cameras explicitly skip these standard properties + // to make them not be required. However, they can still implement + // them with the CreateXXXXStandardProperty methods. When the + // corresponding functionality it present, this is encouraged, + // because it allows them to move closer to the new style camera API. + SkipTriggerSelectorStandardProperty(); + SkipTriggerModeStandardProperty(); + SkipTriggerSourceStandardProperty(); + SkipTriggerActivationStandardProperty(); + SkipTriggerDelayStandardProperty(); + SkipTriggerOverlapStandardProperty(); + + SkipExposureModeStandardProperty(); + SkipExposureTimeStandardProperty(); + + SkipAcquisitionBurstFrameCountStandardProperty(); + SkipLineSelectorStandardProperty(); + SkipLineInverterStandardProperty(); + SkipLineSourceStandardProperty(); + SkipLineModeStandardProperty(); + SkipLineStatusStandardProperty(); + + SkipAcquisitionFrameRateStandardProperty(); + SkipAcquisitionFrameRateEnableStandardProperty(); + + SkipAcquisitionStatusSelectorStandardProperty(); + SkipAcquisitionStatusStandardProperty(); + + SkipEventSelectorStandardProperty(); + SkipEventNotificationStandardProperty(); + + SkipRollingShutterLineOffsetStandardProperty(); + SkipRollingShutterActiveLinesStandardProperty(); + } + + // Shared functionality with no default implementation + virtual unsigned GetImageWidth() const = 0; + virtual unsigned GetImageHeight() const = 0; + virtual unsigned GetImageBytesPerPixel() const = 0; + virtual unsigned GetBitDepth() const = 0; + virtual int GetBinning() const = 0; + virtual int SetBinning(int binSize) = 0; + virtual int SetROI(unsigned x, unsigned y, unsigned xSize, unsigned ySize) = 0; + virtual int GetROI(unsigned& x, unsigned& y, unsigned& xSize, unsigned& ySize) = 0; + virtual int ClearROI() = 0; + virtual bool IsCapturing() = 0; + + // Final so derived classes cannot override + virtual bool IsNewAPIImplemented() final {return false;} + + // Old camera API: required + virtual void SetExposure(double exp_ms) = 0; + virtual double GetExposure() const = 0; + + virtual const unsigned char* GetImageBuffer() = 0; + virtual long GetImageBufferSize() const = 0; + virtual int SnapImage() = 0; + + virtual int StartSequenceAcquisition(double interval) = 0; + virtual int StopSequenceAcquisition() = 0; + virtual int PrepareSequenceAcqusition() = 0; + virtual int StartSequenceAcquisition(long numImages, double interval_ms, + bool stopOnOverflow) = 0; + + + // New camera API: disabled + virtual int TriggerSoftware() final {return DEVICE_NOT_YET_IMPLEMENTED;}; + + virtual int AcquisitionArm(int /* frameCount */) final {return DEVICE_NOT_YET_IMPLEMENTED;}; + virtual int AcquisitionStart() final {return DEVICE_NOT_YET_IMPLEMENTED;}; + virtual int AcquisitionStop() final {return DEVICE_NOT_YET_IMPLEMENTED;}; + virtual int AcquisitionAbort() final {return DEVICE_NOT_YET_IMPLEMENTED;}; + +}; + + +/** +* Base class for creating camera device adapters using the new Camera API. +*/ +// TODO: dissallow old style camera devices from using this class +template +class CNewAPICameraBase : public CAllCamerasBase +{ +public: + + // Invokes superclass constructor to get the properties declared there + CNewAPICameraBase() : CAllCamerasBase() + { + + } + + + // Shared functionality with no default implementation + virtual unsigned GetImageWidth() const = 0; + virtual unsigned GetImageHeight() const = 0; + virtual unsigned GetImageBytesPerPixel() const = 0; + virtual unsigned GetBitDepth() const = 0; + virtual int GetBinning() const = 0; + virtual int SetBinning(int binSize) = 0; + virtual int SetROI(unsigned x, unsigned y, unsigned xSize, unsigned ySize) = 0; + virtual int GetROI(unsigned& x, unsigned& y, unsigned& xSize, unsigned& ySize) = 0; + virtual int ClearROI() = 0; + virtual bool IsCapturing() = 0; + + + // Final so derived classes cannot override + virtual bool IsNewAPIImplemented() final {return true;} + // Old camera API: disabled + virtual const unsigned char* GetImageBuffer() final {return nullptr;} + virtual long GetImageBufferSize() const final {return 0;} + virtual int SnapImage() final {return DEVICE_NOT_YET_IMPLEMENTED;} + virtual const unsigned int* GetImageBufferAsRGB32() final {return nullptr;} + + virtual int StartSequenceAcquisition(double /* interval */) final {return DEVICE_NOT_YET_IMPLEMENTED;} + virtual int StopSequenceAcquisition() final {return DEVICE_NOT_YET_IMPLEMENTED;} + virtual int PrepareSequenceAcqusition() final {return DEVICE_NOT_YET_IMPLEMENTED;} + virtual int StartSequenceAcquisition(long /* numImages */, double /* interval_ms */, + bool /* stopOnOverflow */) final {return DEVICE_NOT_YET_IMPLEMENTED;} + virtual const unsigned char* GetImageBuffer(unsigned /* channelNr */) final {return nullptr;} + + virtual void SetExposure(double exp_ms) final { + std::string fullName = MM::g_KeywordStandardPropertyPrefix; + fullName += MM::g_ExposureTimeProperty.name; + std::string value = CDeviceUtils::ConvertToString(exp_ms * 1000); + this->SetProperty(fullName.c_str(), value.c_str()); + // Get the accepted value + char val[MM::MaxStrLength]; + this->GetProperty(fullName.c_str(), val); + this->OnPropertyChanged(fullName.c_str(), val); + } + + virtual double GetExposure() const final { + std::string fullName = MM::g_KeywordStandardPropertyPrefix; + fullName += MM::g_ExposureTimeProperty.name; + char val[MM::MaxStrLength]; + this->GetProperty(fullName.c_str(), val); + return atof(val) / 1000.0; + } + + // New camera API: required + virtual int TriggerSoftware() = 0; + + virtual int AcquisitionArm(int frameCount) = 0; + virtual int AcquisitionStart() = 0; + virtual int AcquisitionStop() = 0; + virtual int AcquisitionAbort() = 0; +}; + + + +/** +* Legacy base class for creating camera device adapters. +* Newer camera device adapters should inherit from CCameraBase. +* This class contains suboptimal methods for implementing sequence acquisition +* using a series of snaps. +* This class has a functional constructor - must be invoked +* from the derived class. +*/ +template +class CLegacyCameraBase : public COldAPICameraBase +{ +public: + + CLegacyCameraBase() : COldAPICameraBase(), busy_(false), stopWhenCBOverflows_(false), thd_(0) + { + thd_ = new BaseSequenceThread(this); + } + + virtual ~CLegacyCameraBase() + { + if (!thd_->IsStopped()) { + thd_->Stop(); + thd_->wait(); + } + delete thd_; + } + + + virtual bool Busy() {return busy_;} + + /** + * Continuous sequence acquisition. + * Default to sequence acquisition with a high number of images + */ + virtual int StartSequenceAcquisition(double interval) + { + return StartSequenceAcquisition(LONG_MAX, interval, false); + } + + /** + * Stop and wait for the thread finished + */ + virtual int StopSequenceAcquisition() + { + if (!thd_->IsStopped()) { + thd_->Stop(); + thd_->wait(); + } + + return DEVICE_OK; + } + + // Implementation of a sequence acquisition as a series of snaps + // This was a temporary method used for debugging, which is why its now + // implemented in this legacy class. It's preferable that camera devices + // inherit directly from CCameraBase and not use these default implementations. + virtual int PrepareSequenceAcqusition() {return DEVICE_OK;} + + + virtual int StartSequenceAcquisition(long numImages, double interval_ms, bool stopOnOverflow) + { + if (IsCapturing()) + return DEVICE_CAMERA_BUSY_ACQUIRING; + + int ret = GetCoreCallback()->PrepareForAcq(this); + if (ret != DEVICE_OK) + return ret; + thd_->Start(numImages,interval_ms); + stopWhenCBOverflows_ = stopOnOverflow; + return DEVICE_OK; + } + + virtual bool IsCapturing(){return !thd_->IsStopped();} + + +protected: + ///////////////////////////////////////////// + // utility methods for use by derived classes + // ////////////////////////////////////////// + // Do actual capturing // Called from inside the thread virtual int ThreadRun (void) @@ -1583,10 +2253,10 @@ class CCameraBase : public CDeviceBase class CaptureRestartHelper { bool restart_; - CCameraBase* pCam_; + CLegacyCameraBase* pCam_; public: - CaptureRestartHelper(CCameraBase* pCam) + CaptureRestartHelper(CLegacyCameraBase* pCam) :pCam_(pCam) { restart_=pCam_->IsCapturing(); @@ -1602,10 +2272,10 @@ class CCameraBase : public CDeviceBase //////////////////////////////////////////////////////////////////////////// class BaseSequenceThread : public MMDeviceThreadBase { - friend class CCameraBase; + friend class CLegacyCameraBase; enum { default_numImages=1, default_intervalMS = 100 }; public: - BaseSequenceThread(CCameraBase* pCam) + BaseSequenceThread(CLegacyCameraBase* pCam) :intervalMs_(default_intervalMS) ,numImages_(default_numImages) ,imageCounter_(0) @@ -1662,7 +2332,7 @@ class CCameraBase : public CDeviceBase MM::MMTime GetStartTime(){return startTime_;} MM::MMTime GetActualDuration(){return actualDuration_;} - CCameraBase* GetCamera() {return camera_;} + CLegacyCameraBase* GetCamera() {return camera_;} long GetNumberOfImages() {return numImages_;} void UpdateActualDuration() {actualDuration_ = camera_->GetCurrentMMTime() - startTime_;} @@ -1694,7 +2364,7 @@ class CCameraBase : public CDeviceBase long imageCounter_; bool stop_; bool suspend_; - CCameraBase* camera_; + CLegacyCameraBase* camera_; MM::MMTime startTime_; MM::MMTime actualDuration_; MM::MMTime lastFrameTime_; @@ -1708,7 +2378,6 @@ class CCameraBase : public CDeviceBase bool busy_; bool stopWhenCBOverflows_; - Metadata metadata_; BaseSequenceThread * thd_; friend class BaseSequenceThread; diff --git a/MMDevice/MMDevice-SharedRuntime.vcxproj b/MMDevice/MMDevice-SharedRuntime.vcxproj index 8df751203..a0ff43c76 100644 --- a/MMDevice/MMDevice-SharedRuntime.vcxproj +++ b/MMDevice/MMDevice-SharedRuntime.vcxproj @@ -14,7 +14,6 @@ - @@ -88,4 +87,4 @@ - + \ No newline at end of file diff --git a/MMDevice/MMDevice-SharedRuntime.vcxproj.filters b/MMDevice/MMDevice-SharedRuntime.vcxproj.filters index 2f38adf00..80e136161 100644 --- a/MMDevice/MMDevice-SharedRuntime.vcxproj.filters +++ b/MMDevice/MMDevice-SharedRuntime.vcxproj.filters @@ -20,9 +20,6 @@ Source Files - - Source Files - Source Files @@ -62,4 +59,4 @@ Header Files - + \ No newline at end of file diff --git a/MMDevice/MMDevice-StaticRuntime.vcxproj b/MMDevice/MMDevice-StaticRuntime.vcxproj index b1317341a..0770c8ce7 100644 --- a/MMDevice/MMDevice-StaticRuntime.vcxproj +++ b/MMDevice/MMDevice-StaticRuntime.vcxproj @@ -14,7 +14,6 @@ - @@ -90,4 +89,4 @@ - + \ No newline at end of file diff --git a/MMDevice/MMDevice-StaticRuntime.vcxproj.filters b/MMDevice/MMDevice-StaticRuntime.vcxproj.filters index 2f38adf00..80e136161 100644 --- a/MMDevice/MMDevice-StaticRuntime.vcxproj.filters +++ b/MMDevice/MMDevice-StaticRuntime.vcxproj.filters @@ -20,9 +20,6 @@ Source Files - - Source Files - Source Files @@ -62,4 +59,4 @@ Header Files - + \ No newline at end of file diff --git a/MMDevice/MMDevice.cpp b/MMDevice/MMDevice.cpp deleted file mode 100644 index 36c82ef97..000000000 --- a/MMDevice/MMDevice.cpp +++ /dev/null @@ -1,51 +0,0 @@ -// AUTHOR: Mark Tsuchida, May 2014 -// -// COPYRIGHT: University of California, San Francisco, 2014 -// -// LICENSE: This file is distributed under the BSD license. -// License text is included with the source distribution. -// -// This file is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty -// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// -// IN NO EVENT SHALL THE COPYRIGHT OWNER OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES. - -#include "MMDevice.h" - -namespace MM { - -// Definitions for static const data members. -// -// Note: Do not try to move these initializers to the header. The C++ standard -// allows initializing a static const enum data member inline (inside the class -// definition, where the member is _declared_), but still requires a -// _definition_ (in which case, the definition should not have an initializer). -// However, Microsoft VC++ has a nonstandard extension that allows you to leave -// out the definition altogether, if an initializer is supplied at the -// declaration. Because of that nonstandard behavior, VC++ issues a warning -// (LNK4006) if the initializer is supplied with the declaration _and_ a -// definition is (correctly) provided. So, to compile correctly with a -// standards-conformant compiler _and_ avoid warnings from VC++, we need to -// leave the initializers out of the declarations, and supply them here with -// the definitions. See: -// http://connect.microsoft.com/VisualStudio/feedback/details/802091/lnk4006-reported-for-static-const-members-that-is-initialized-in-the-class-definition - -const DeviceType Generic::Type = GenericDevice; -const DeviceType Camera::Type = CameraDevice; -const DeviceType Shutter::Type = ShutterDevice; -const DeviceType Stage::Type = StageDevice; -const DeviceType XYStage::Type = XYStageDevice; -const DeviceType State::Type = StateDevice; -const DeviceType Serial::Type = SerialDevice; -const DeviceType AutoFocus::Type = AutoFocusDevice; -const DeviceType ImageProcessor::Type = ImageProcessorDevice; -const DeviceType SignalIO::Type = SignalIODevice; -const DeviceType Magnifier::Type = MagnifierDevice; -const DeviceType SLM::Type = SLMDevice; -const DeviceType Galvo::Type = GalvoDevice; -const DeviceType Hub::Type = HubDevice; - -} // namespace MM diff --git a/MMDevice/MMDevice.h b/MMDevice/MMDevice.h index c674ac0b7..9fa99863d 100644 --- a/MMDevice/MMDevice.h +++ b/MMDevice/MMDevice.h @@ -43,14 +43,17 @@ #include "DeviceUtils.h" #include "ImageMetadata.h" #include "DeviceThreads.h" +#include "Property.h" #include #include #include +#include #include #include #include #include +#include // To be removed once the deprecated Get/SetModuleHandle() is removed: #ifdef _WIN32 @@ -205,6 +208,515 @@ namespace MM { }; + //////////////////////////// + ///// Standard properties + //////////////////////////// + + namespace internal { + //// Helper functions/macros for compile time and runtime checking of standard properties + + // Map from device types to standard properties + inline std::unordered_map>& GetDeviceTypeStandardPropertiesMap() { + static std::unordered_map> devicePropertiesMap; + return devicePropertiesMap; + } + + // Register a standard property with its valid device types + inline void RegisterStandardProperty(const StandardProperty& prop, std::initializer_list deviceTypes) { + for (MM::DeviceType deviceType : deviceTypes) { + GetDeviceTypeStandardPropertiesMap()[deviceType].push_back(prop); + } + } + + // Check if a property is valid for a specific device type + inline bool IsPropertyValidForDeviceType(const StandardProperty& prop, MM::DeviceType deviceType) { + auto it = GetDeviceTypeStandardPropertiesMap().find(deviceType); + if (it == GetDeviceTypeStandardPropertiesMap().end()) { + return false; + } + + const auto& validProperties = it->second; + return std::find(validProperties.begin(), validProperties.end(), prop) != validProperties.end(); + } + + // The below code is a way to enable compile time checking of which + // standard properties are are valid for which device types. This enables + // methods for setting these standard properties defined once + // in CDeviceBase and then conditionally enabled in child classes based on + // the MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE macro below + // In addition to making this link, a dedicated method for the standard property + // should be created in CDeviceBase. + template + struct IsStandardPropertyValid { + static const bool value = false; // Default to false + }; + + // The top struct enables compile time checking of standard property + // creation methods (ie that you can call the CreateXXXXStandardProperty + // in a device type that doesn't support it) + // The bottom part adds the standard property to a runtime registry + // which enables higher level code the query the standard properties + // associated with a particular device type and check that everything + // is correct (ie all required properties are implemented after + // device initialization) + #define MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(DeviceType, PropertyRef) \ + template <> \ + struct MM::internal::IsStandardPropertyValid { \ + static const bool value = true; \ + }; \ + namespace { \ + static const bool PropertyRef##_registered = (MM::internal::RegisterStandardProperty(PropertyRef, {DeviceType}), true); \ + } + } // namespace internal + + /////// Standard property definitions + // Each standard property is defined here, and then linked to one or more + // device types using the MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE macro. + // This allows compile time checking that the property is valid for a + // particular device type, and also allows runtime querying of which + // properties are supported by a given device. + + // Specific standard properties + // static const MM::StandardProperty g_TestStandardProperty{ + // "Test", // name + // String, // type + // false, // isReadOnly + // false, // isPreInit + // {}, // allowedValues (empty vector) + // {}, // requiredValues (empty vector) + // PropertyLimitUndefined, // lowerLimit + // PropertyLimitUndefined, // upperLimit + // }; + + // MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_TestStandardProperty) + + + // static const std::vector testRequiredValues = {"value1", "value2", "value3"}; + // static const MM::StandardProperty g_TestWithValuesStandardProperty{ + // "TestWithValues", // name + // String, // type + // false, // isReadOnly + // false, // isPreInit + // {}, // allowedValues (empty vector) + // testRequiredValues, // requiredValues + // PropertyLimitUndefined, // lowerLimit + // PropertyLimitUndefined, // upperLimit + // }; + + // MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_TestWithValuesStandardProperty) + + //////////////////////////// + ///// Camera trigger API + // These are directly based on the GenICam Standard Feature Naming convention + // see https://www.emva.org/wp-content/uploads/GenICam_SFNC_2_2.pdf + // Boolean values get mapped to "0" or "1" since MM properties do not support booleans + + // Selects the type of trigger to configure. + static const MM::StandardProperty g_TriggerSelectorProperty{ + "TriggerSelector", // name + String, // type + false, // isReadOnly + false, // isPreInit + {}, // allowedValues + {}, // no required values for now + PropertyLimitUndefined, // lowerLimit + PropertyLimitUndefined, // upperLimit + }; + MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_TriggerSelectorProperty) + + + // Controls if the selected trigger is active. + static const std::vector triggerModeValues = { + g_keyword_TriggerModeOff, + g_keyword_TriggerModeOn + }; + static const MM::StandardProperty g_TriggerModeProperty{ + "TriggerMode", // name + String, // type + false, // isReadOnly + false, // isPreInit + triggerModeValues, // allowedValues + {}, // no required values for now + PropertyLimitUndefined, // lowerLimit + PropertyLimitUndefined, // upperLimit + }; + MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_TriggerModeProperty) + + + // Specifies the internal signal or physical input Line to use as the trigger source. + // The selected trigger must have its TriggerMode set to On. + // Standard names are: Software, Line0, Line1, Line2, Line3 + // and many others including Counter0Start, Timer0Start, etc. + // Require the ability to send a software trigger, + // but other than than not sure its possible to to standaradize + // in part because some cameras may start at line0 and others at line1 + static const MM::StandardProperty g_TriggerSourceProperty{ + "TriggerSource", // name + String, // type + false, // isReadOnly + false, // isPreInit + {}, // allowedValues + {}, + PropertyLimitUndefined, // lowerLimit + PropertyLimitUndefined, // upperLimit + }; + MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_TriggerSourceProperty) + + + // Specifies the activation mode of the trigger. + static const std::vector triggerActivationValues = { + g_keyword_TriggerActivationAnyEdge, + g_keyword_TriggerActivationRisingEdge, + g_keyword_TriggerActivationFallingEdge, + g_keyword_TriggerActivationLevelLow, + g_keyword_TriggerActivationLevelHigh + }; + static const MM::StandardProperty g_TriggerActivationProperty{ + "TriggerActivation", // name + String, // type + false, // isReadOnly + false, // isPreInit + triggerActivationValues, // allowedValues + {}, // requiredValues + PropertyLimitUndefined, // lowerLimit + PropertyLimitUndefined, // upperLimit + }; + MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_TriggerActivationProperty) + + + // Specifies the delay in microseconds (us) to apply after the trigger reception before activating it. + static const MM::StandardProperty g_TriggerDelayProperty{ + "TriggerDelay", // name + Float, // type + false, // isReadOnly + false, // isPreInit + {}, // allowedValues + {}, // requiredValues + 0, // lowerLimit + PropertyLimitUndefined, // upperLimit + }; + MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_TriggerDelayProperty) + + // TriggerOverlap property + // Specifies the type trigger overlap permitted with the previous frame or line. + // static const std::vector triggerOverlapValues = { + // "Off", // No trigger overlap is permitted + // "ReadOut", // Trigger is accepted immediately after exposure period + // "PreviousFrame", // Trigger is accepted at any time during capture of previous frame + // "PreviousLine" // Trigger is accepted at any time during capture of previous line + // }; + static const MM::StandardProperty g_TriggerOverlapProperty{ + "TriggerOverlap", // name + String, // type + false, // isReadOnly + false, // isPreInit + {}, // allowedValues + {}, // requiredValues + PropertyLimitUndefined, // lowerLimit + PropertyLimitUndefined, // upperLimit + }; + MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_TriggerOverlapProperty) + + + // Exposure Mode property + // Sets the operation mode of the Exposure. + // Possible values are: + // Timed: Timed exposure. + // TriggerWidth: Uses the width of the current Frame trigger signal pulse to control the exposure duration. + // Note that if the Frame or Line TriggerActivation is RisingEdge or LevelHigh, the exposure + // duration will be the time the trigger stays High. If TriggerActivation is FallingEdge or + // LevelLow, the exposure time will last as long as the trigger stays Low. + // TriggerControlled: Uses one or more trigger signal(s) to control the exposure duration independently from + // the current Frame or Line triggers. See ExposureStart, ExposureEnd and ExposureActive + // of the TriggerSelector feature. Note also that ExposureMode as priority over the Exposure + // Trigger settings defined using TriggerSelector=Exposure... and defines which trigger + // (if any) is active. + static const std::vector exposureModeValues = { + g_keyword_ExposureModeTimed, + g_keyword_ExposureModeTriggerWidth, + g_keyword_ExposureModeTriggerControlled + }; + static const MM::StandardProperty g_ExposureModeProperty{ + "ExposureMode", // name + String, // type + false, // isReadOnly + false, // isPreInit + exposureModeValues, // allowedValues + {}, // requiredValues + PropertyLimitUndefined, // lowerLimit + PropertyLimitUndefined, // upperLimit + }; + MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_ExposureModeProperty) + + // ExposureTime property + // Sets the Exposure time when ExposureMode is Timed and ExposureAuto is Off. + static const MM::StandardProperty g_ExposureTimeProperty{ + "ExposureTime", // name + Float, // type + false, // isReadOnly + false, // isPreInit + {}, // allowedValues + {}, // requiredValues + 0.0, // lowerLimit + PropertyLimitUndefined, // upperLimit + }; + MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_ExposureTimeProperty) + + // Burst Frame Count property + // Number of frames to acquire for each FrameBurstStart trigger. This feature is used only if + // the FrameBurstStart trigger is enabled and the FrameBurstEnd trigger is disabled. Note that + // the total number of frames captured is also conditioned by AcquisitionFrameCount if + //AcquisitionMode is MultiFrame and ignored if AcquisitionMode is Single. + static const MM::StandardProperty g_AcquisitionBurstFrameCountProperty{ + "AcquisitionBurstFrameCount", // name + Integer, // type + false, // isReadOnly + false, // isPreInit + {}, // allowedValues + {}, // requiredValues + 1, // lowerLimit + PropertyLimitUndefined, // upperLimit + }; + MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_AcquisitionBurstFrameCountProperty) + + + // Selects the physical line (or pin) of the external device connector to configure. + // When a Line is selected, all the other Line features will be applied to its associated + //I/O control block and will condition the resulting input or output signal. + static const MM::StandardProperty g_LineSelectorProperty{ + "LineSelector", // name + String, // type + false, // isReadOnly + false, // isPreInit + {}, // allowedValues + {}, // requiredValues + PropertyLimitUndefined, // lowerLimit + PropertyLimitUndefined, // upperLimit + }; + MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_LineSelectorProperty) + + + // Controls if the physical Line is used to Input or Output a signal. When a Line supports + // input and output mode, the default state is Input to avoid possible electrical contention. + // Possible values are: + // Input: The selected physical line is used to Input an electrical signal. + // Output: The selected physical line is used to Output an electrical signal. + static const std::vector lineModeValues = { + g_keyword_LineModeInput, + g_keyword_LineModeOutput + }; + static const MM::StandardProperty g_LineModeProperty{ + "LineMode", // name + String, // type + false, // isReadOnly + false, // isPreInit + lineModeValues, // allowedValues + {}, // requiredValues + PropertyLimitUndefined, // lowerLimit + PropertyLimitUndefined, // upperLimit + }; + MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_LineModeProperty) + + + // Controls the inversion of the signal of the selected input or output Line. + // Possible values are: + // False (0): The Line signal is not inverted. + // True (1): The Line signal is inverted. + static const MM::StandardProperty g_LineInverterProperty{ + "LineInverter", // name + String, // type + false, // isReadOnly + false, // isPreInit + {"0", "1"}, // allowedValues + {}, // requiredValues + PropertyLimitUndefined, // lowerLimit + PropertyLimitUndefined, // upperLimit + }; + MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_LineInverterProperty) + + + // Selects which internal acquisition or I/O source signal to output on the selected Line. + // LineMode must be Output. + static const MM::StandardProperty g_LineSourceProperty{ + "LineSource", // name + String, // type + false, // isReadOnly + false, // isPreInit + {}, // allowedValues + {}, // requiredValues + PropertyLimitUndefined, // lowerLimit + PropertyLimitUndefined, // upperLimit + }; + MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_LineSourceProperty) + + + // Returns the current status of the selected input or output Line. The status of the + // signal is taken after the input Line inverter of the I/O control block. + // Low: The Line signal is Low. + // High: The Line signal is High. + static const std::vector lineStatusValues = { + g_keyword_LineStatusLow, + g_keyword_LineStatusHigh + }; + static const MM::StandardProperty g_LineStatusProperty{ + "LineStatus", // name + String, // type + true, // isReadOnly + false, // isPreInit + lineStatusValues, // allowedValues + lineStatusValues, // requiredValues + PropertyLimitUndefined, // lowerLimit + PropertyLimitUndefined, // upperLimit + }; + MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_LineStatusProperty) + + // Controls the acquisition rate (in Hertz) at which the frames are captured. + static const MM::StandardProperty g_AcquisitionFrameRateProperty{ + "AcquisitionFrameRate", // name + Float, // type + false, // isReadOnly + false, // isPreInit + {}, // allowedValues + {}, // requiredValues + 0, // lowerLimit + PropertyLimitUndefined, // upperLimit + }; + MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_AcquisitionFrameRateProperty) + + + // Controls if the AcquisitionFrameRate feature is writable and used to control the acquisition + // rate. Otherwise, the acquisition rate is implicitly controlled by the combination of other + // features like ExposureTime, etc... + static const MM::StandardProperty g_AcquisitionFrameRateEnableProperty{ + "AcquisitionFrameRateEnable", // name + String, // type + false, // isReadOnly + false, // isPreInit + {"1", "0"}, // allowedValues + {}, // requiredValues + PropertyLimitUndefined, // lowerLimit + PropertyLimitUndefined, // upperLimit + }; + MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_AcquisitionFrameRateEnableProperty) + + + // Acquisition status selector property + // Selects the internal acquisition signal to read using AcquisitionStatus. + // Possible values are: + // AcquisitionTriggerWait: Device is currently waiting for a trigger for the capture of one or many frames. + // AcquisitionActive: Device is currently doing an acquisition of one or many frames. + // AcquisitionTransfer: Device is currently transferring an acquisition of one or many frames. + // FrameTriggerWait: Device is currently waiting for a frame start trigger. + // FrameActive: Device is currently doing the capture of a frame. + // ExposureActive: Device is doing the exposure of a frame. + static const MM::StandardProperty g_AcquisitionStatusSelectorProperty{ + "AcquisitionStatusSelector", // name + String, // type + false, // isReadOnly + false, // isPreInit + {}, // allowedValues + {}, // requiredValues + PropertyLimitUndefined, // lowerLimit + PropertyLimitUndefined, // upperLimit + }; + MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_AcquisitionStatusSelectorProperty) + + + // Acquisition status property + // Reads the state of the internal acquisition signal selected using AcquisitionStatusSelector. + static const MM::StandardProperty g_AcquisitionStatusProperty{ + "AcquisitionStatus", // name + String, // type + true, // isReadOnly + false, // isPreInit + {"0", "1"}, // allowedValues + {"0", "1"}, // requiredValues + PropertyLimitUndefined, // lowerLimit + PropertyLimitUndefined, // upperLimit + }; + MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_AcquisitionStatusProperty) + + + // GenICam style event properties + // For now, allow events with any name, because cameras may implement ones other than + // these + // static const std::vector eventSelectorValues = { + // g_keyword_CameraEventAcquisitionTrigger, + // g_keyword_CameraEventAcquisitionStart, + // g_keyword_CameraEventAcquisitionEnd, + // g_keyword_CameraEventAcquisitionTransferStart, + // g_keyword_CameraEventAcquisitionTransferEnd, + // g_keyword_CameraEventAcquisitionError, + // g_keyword_CameraEventFrameTrigger, + // g_keyword_CameraEventFrameStart, + // g_keyword_CameraEventFrameEnd, + // g_keyword_CameraEventFrameBurstStart, + // g_keyword_CameraEventFrameBurstEnd, + // g_keyword_CameraEventFrameTransferStart, + // g_keyword_CameraEventFrameTransferEnd, + // g_keyword_CameraEventExposureStart, + // g_keyword_CameraEventExposureEnd, + // g_keyword_CameraEventError + // }; + static const MM::StandardProperty g_EventSelectorProperty{ + "EventSelector", // name + String, // type + false, // isReadOnly + false, // isPreInit + {}, // allowedValues + {}, // requiredValues + PropertyLimitUndefined, // lowerLimit + PropertyLimitUndefined, // upperLimit + }; + MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_EventSelectorProperty) + + // Event notification: whether the event actually produced + static const std::vector eventNotificationValues = { + g_keyword_CameraEventNotificationOff, + g_keyword_CameraEventNotificationOn + }; + static const MM::StandardProperty g_EventNotificationProperty{ + "EventNotification", // name + String, // type + false, // isReadOnly + false, // isPreInit + {}, // allowedValues + eventNotificationValues, // requiredValues + PropertyLimitUndefined, // lowerLimit + PropertyLimitUndefined, // upperLimit + }; + MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_EventNotificationProperty) + + + + //// Standard properties for rolling shutter lightsheet readout cameras + //// These are not part of GenICam, but are supported by some scientific cameras + // TODO: implement when someone who uses this feature can test it + + // static const MM::StandardProperty g_RollingShutterLineOffsetProperty{ + // "RollingShutterLineOffset", // name + // Float, // type + // false, // isReadOnly + // false, // isPreInit + // {}, // allowedValues + // {}, // requiredValues + // PropertyLimitUndefined, // lowerLimit + // PropertyLimitUndefined, + // }; + // MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_RollingShutterLineOffsetProperty) + + // static const MM::StandardProperty g_RollingShutterActiveLinesProperty{ + // "RollingShutterActiveLines", // name + // Integer, // type + // false, // isReadOnly + // false, // isPreInit + // {}, // allowedValues + // {}, // requiredValues + // PropertyLimitUndefined, // lowerLimit + // PropertyLimitUndefined, // upperLimit + // }; + // MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_RollingShutterActiveLinesProperty) + /** * Generic device interface. */ @@ -226,6 +738,7 @@ namespace MM { virtual int GetPropertyType(const char* name, MM::PropertyType& pt) const = 0; virtual unsigned GetNumberOfPropertyValues(const char* propertyName) const = 0; virtual bool GetPropertyValueAt(const char* propertyName, unsigned index, char* value) const = 0; + virtual bool ImplementsOrSkipsStandardProperties(char* failedProperty) const = 0; /** * Sequences can be used for fast acquisitions, synchronized by TTLs rather than * computer commands. @@ -309,7 +822,7 @@ namespace MM { { public: virtual DeviceType GetType() const { return Type; } - static const DeviceType Type; + static constexpr DeviceType Type = GenericDevice; }; /** @@ -321,7 +834,53 @@ namespace MM { virtual ~Camera() {} virtual DeviceType GetType() const { return Type; } - static const DeviceType Type; + static constexpr DeviceType Type = CameraDevice; + + //// New Camera API //// + virtual bool IsNewAPIImplemented() = 0; + + + // Send a software trigger + virtual int TriggerSoftware() = 0; + + ////////////////////////////// + // Acquisitions + ////////////////////////////// + + // Arms the device before an AcquisitionStart command. This optional command validates all + // the current features for consistency and prepares the device for a fast start of the Acquisition. + // If not used explicitly, this command will be automatically executed at the first + // AcquisitionStart but will not be repeated for the subsequent ones unless a feature is changed in the device. + + // TODO: the above logic needs to be implemented in core? + + // Don't acqMode because it can be inferred from frameCount + // if frameCount is: 1 --> acqMode is single + // > 1 --> acqMode is MultiFrame + // <= 0 --> acqMode is continuous + + virtual int AcquisitionArm(int frameCount) = 0; + + // Starts the Acquisition of the device. The number of frames captured is specified by AcquisitionMode. + // Note that unless the AcquisitionArm was executed since the last feature change, + // the AcquisitionStart command must validate all the current features for consistency before starting the Acquisition. + virtual int AcquisitionStart() = 0; + + // Stops the Acquisition of the device at the end of the current Frame. It is mainly + // used when AcquisitionMode is Continuous but can be used in any acquisition mode. + // If the camera is waiting for a trigger, the pending Frame will be cancelled. + // If no Acquisition is in progress, the command is ignored. + virtual int AcquisitionStop() = 0; + + + //Aborts the Acquisition immediately. This will end the capture without completing + // the current Frame or waiting on a trigger. If no Acquisition is in progress, the command is ignored. + virtual int AcquisitionAbort() = 0; + + + /////////////////////////////////////////////////////////////// + ///// End new camera API //////////////////////// + ////////////////////////////////////////////////////////////// // Camera API /** @@ -557,7 +1116,7 @@ namespace MM { // Device API virtual DeviceType GetType() const { return Type; } - static const DeviceType Type; + static constexpr DeviceType Type = ShutterDevice; // Shutter API virtual int SetOpen(bool open = true) = 0; @@ -580,7 +1139,7 @@ namespace MM { // Device API virtual DeviceType GetType() const { return Type; } - static const DeviceType Type; + static constexpr DeviceType Type = StageDevice; // Stage API virtual int SetPositionUm(double pos) = 0; @@ -680,7 +1239,7 @@ namespace MM { // Device API virtual DeviceType GetType() const { return Type; } - static const DeviceType Type; + static constexpr DeviceType Type = XYStageDevice; // XYStage API // it is recommended that device adapters implement the "Steps" methods @@ -766,7 +1325,7 @@ namespace MM { // MMDevice API virtual DeviceType GetType() const { return Type; } - static const DeviceType Type; + static constexpr DeviceType Type = StateDevice; // MMStateDevice API virtual int SetPosition(long pos) = 0; @@ -792,7 +1351,7 @@ namespace MM { // MMDevice API virtual DeviceType GetType() const { return Type; } - static const DeviceType Type; + static constexpr DeviceType Type = SerialDevice; // Serial API virtual PortType GetPortType() const = 0; @@ -814,7 +1373,7 @@ namespace MM { // MMDevice API virtual DeviceType GetType() const { return Type; } - static const DeviceType Type; + static constexpr DeviceType Type = AutoFocusDevice; // AutoFocus API virtual int SetContinuousFocusing(bool state) = 0; @@ -840,7 +1399,7 @@ namespace MM { // MMDevice API virtual DeviceType GetType() const { return Type; } - static const DeviceType Type; + static constexpr DeviceType Type = ImageProcessorDevice; // image processor API virtual int Process(unsigned char* buffer, unsigned width, unsigned height, unsigned byteDepth) = 0; @@ -859,7 +1418,7 @@ namespace MM { // MMDevice API virtual DeviceType GetType() const { return Type; } - static const DeviceType Type; + static constexpr DeviceType Type = SignalIODevice; // signal io API virtual int SetGateOpen(bool open = true) = 0; @@ -943,7 +1502,7 @@ namespace MM { // MMDevice API virtual DeviceType GetType() const { return Type; } - static const DeviceType Type; + static constexpr DeviceType Type = MagnifierDevice; virtual double GetMagnification() = 0; }; @@ -963,7 +1522,7 @@ namespace MM { virtual ~SLM() {} virtual DeviceType GetType() const { return Type; } - static const DeviceType Type; + static constexpr DeviceType Type = SLMDevice; // SLM API /** @@ -1122,7 +1681,7 @@ namespace MM { virtual ~Galvo() {} virtual DeviceType GetType() const { return Type; } - static const DeviceType Type; + static constexpr DeviceType Type = GalvoDevice; //Galvo API: @@ -1249,7 +1808,7 @@ namespace MM { // MMDevice API virtual DeviceType GetType() const { return Type; } - static const DeviceType Type; + static constexpr DeviceType Type = HubDevice; /** * Instantiate all available child peripheral devices. @@ -1371,6 +1930,11 @@ namespace MM { * Magnifiers can use this to signal changes in magnification */ virtual int OnMagnifierChanged(const Device* caller) = 0; + /** + * This callback is used to handle various types of camera events + */ + virtual int OnCameraEvent(const Device* caller, const char* eventName, unsigned long timestamp, unsigned long frameId, const char* data) = 0; + // Deprecated: Return value overflows in ~72 minutes on Windows. // Prefer std::chrono::steady_clock for time delta measurements. diff --git a/MMDevice/MMDeviceConstants.h b/MMDevice/MMDeviceConstants.h index 65b0a5c90..a14bbd9a2 100644 --- a/MMDevice/MMDeviceConstants.h +++ b/MMDevice/MMDeviceConstants.h @@ -88,6 +88,7 @@ #define DEVICE_SEQUENCE_TOO_LARGE 39 #define DEVICE_OUT_OF_MEMORY 40 #define DEVICE_NOT_YET_IMPLEMENTED 41 +#define DEVICE_MISSING_REQUIRED_PROPERTY 42 namespace MM { @@ -174,6 +175,111 @@ namespace MM { const char* const g_Keyword_Metadata_ROI_Y = "ROI-Y-start"; const char* const g_Keyword_Metadata_TimeInCore = "TimeReceivedByCore"; + ////// New Camera API constants + // trigger state constants + const char* const g_keyword_TriggerSelectorAcquisitionStart = "AcquisitionStart"; + const char* const g_keyword_TriggerSelectorAcquisitionEnd = "AcquisitionEnd"; + const char* const g_keyword_TriggerSelectorAcquisitionActive = "AcquisitionActive"; + const char* const g_keyword_TriggerSelectorFrameBurstStart = "FrameBurstStart"; + const char* const g_keyword_TriggerSelectorFrameBurstEnd = "FrameBurstEnd"; + const char* const g_keyword_TriggerSelectorFrameBurstActive = "FrameBurstActive"; + const char* const g_keyword_TriggerSelectorFrameStart = "FrameStart"; + const char* const g_keyword_TriggerSelectorFrameEnd = "FrameEnd"; + const char* const g_keyword_TriggerSelectorFrameActive = "FrameActive"; + const char* const g_keyword_TriggerSelectorExposureStart = "ExposureStart"; + const char* const g_keyword_TriggerSelectorExposureEnd = "ExposureEnd"; + const char* const g_keyword_TriggerSelectorExposureActive = "ExposureActive"; + + const char* const g_keyword_TriggerModeOn = "On"; + const char* const g_keyword_TriggerModeOff = "Off"; + + const char* const g_keyword_TriggerSourceSoftware = "Software"; + + const char* const g_keyword_TriggerActivationAnyEdge = "AnyEdge"; + const char* const g_keyword_TriggerActivationRisingEdge = "RisingEdge"; + const char* const g_keyword_TriggerActivationFallingEdge = "FallingEdge"; + const char* const g_keyword_TriggerActivationLevelLow = "LevelLow"; + const char* const g_keyword_TriggerActivationLevelHigh = "LevelHigh"; + + const char* const g_keyword_ExposureModeTimed = "Timed"; + const char* const g_keyword_ExposureModeTriggerWidth = "TriggerWidth"; + const char* const g_keyword_ExposureModeTriggerControlled = "TriggerControlled"; + + const char* const g_keyword_CameraStatusAcquisitionTriggerWait = "AcquisitionTriggerWait"; + const char* const g_keyword_CameraStatusFrameBurstTriggerWait = "FrameBurstTriggerWait"; + const char* const g_keyword_CameraStatusFrameTriggerWait = "FrameTriggerWait"; + const char* const g_keyword_CameraStatusExposureTriggerWait = "ExposureTriggerWait"; + const char* const g_keyword_CameraStatusAcquisitionActive = "AcquisitionActive"; + const char* const g_keyword_CameraStatusFrameBurstTriggerActive = "FrameBurstTriggerActive"; + const char* const g_keyword_CameraStatusFrameActive = "FrameActive"; + const char* const g_keyword_CameraStatusExposureActive = "ExposureActive"; + const char* const g_keyword_CameraStatusAcquisitionTransfer = "AcquisitionTransfer"; + + const char* const g_keyword_LineModeInput = "Input"; + const char* const g_keyword_LineModeOutput = "Output"; + + const char* const g_keyword_OutputLineSourceOff = "Off"; + const char* const g_keyword_OutputLineSourceAcquisitionTriggerWait = "AcquisitionTriggerWait"; + const char* const g_keyword_OutputLineSourceAcquisitionTrigger = "AcquisitionTrigger"; + const char* const g_keyword_OutputLineSourceAcquisitionTriggerMissed = "AcquisitionTriggerMissed"; + const char* const g_keyword_OutputLineSourceAcquisitionActive = "AcquisitionActive"; + + const char* const g_keyword_OutputLineSourceFrameBurstTriggerWait = "FrameBurstTriggerWait"; + const char* const g_keyword_OutputLineSourceFrameBurstTrigger = "FrameBurstTrigger"; + const char* const g_keyword_OutputLineSourceFrameBurstTriggerMissed = "FrameBurstTriggerMissed"; + const char* const g_keyword_OutputLineSourceFrameBurstActive = "FrameBurstTriggerActive"; + + const char* const g_keyword_OutputLineSourceFrameTriggerWait = "FrameTriggerWait"; + const char* const g_keyword_OutputLineSourceFrameTrigger = "FrameTrigger"; + const char* const g_keyword_OutputLineSourceFrameTriggerMissed = "FrameTriggerMissed"; + const char* const g_keyword_OutputLineSourceFrameActive = "FrameActive"; + + const char* const g_keyword_OutputLineSourceExposureTriggerWait = "ExposureTriggerWait"; + const char* const g_keyword_OutputLineSourceExposureTrigger = "ExposureTrigger"; + const char* const g_keyword_OutputLineSourceExposureTriggerMissed = "ExposureTriggerMissed"; + const char* const g_keyword_OutputLineSourceExposureActive = "ExposureActive"; + + const char* const g_keyword_LineStatusLow = "0"; + const char* const g_keyword_LineStatusHigh = "1"; + + // Camera Events + // Device just received a trigger for the Acquisition of one or many Frames. + const char* const g_keyword_CameraEventAcquisitionTrigger = "CameraEventAcquisitionTrigger"; + // Device just started the Acquisition of one or many Frames. + const char* const g_keyword_CameraEventAcquisitionStart = "CameraEventAcquisitionStart"; + // Device just completed the Acquisition of one or many Frames. + const char* const g_keyword_CameraEventAcquisitionEnd = "CameraEventAcquisitionEnd"; + // Device just started the transfer of one or many Frames. + const char* const g_keyword_CameraEventAcquisitionTransferStart = "CameraEventAcquisitionTransferStart"; + // Device just completed the transfer of one or many Frames. + const char* const g_keyword_CameraEventAcquisitionTransferEnd = "CameraEventAcquisitionTransferEnd"; + // Device just detected an error during the active Acquisition. + const char* const g_keyword_CameraEventAcquisitionError = "CameraEventAcquisitionError"; + // Device just received a trigger to start the capture of one Frame. + const char* const g_keyword_CameraEventFrameTrigger = "CameraEventFrameTrigger"; + // Device just started the capture of one Frame. + const char* const g_keyword_CameraEventFrameStart = "CameraEventFrameStart"; + // Device just completed the capture of one Frame. + const char* const g_keyword_CameraEventFrameEnd = "CameraEventFrameEnd"; + // Device just started the capture of a burst of Frames. + const char* const g_keyword_CameraEventFrameBurstStart = "CameraEventFrameBurstStart"; + // Device just completed the capture of a burst of Frames. + const char* const g_keyword_CameraEventFrameBurstEnd = "CameraEventFrameBurstEnd"; + // Device just started the transfer of one Frame. + const char* const g_keyword_CameraEventFrameTransferStart = "CameraEventFrameTransferStart"; + // Device just completed the transfer of one Frame. + const char* const g_keyword_CameraEventFrameTransferEnd = "CameraEventFrameTransferEnd"; + // Device just started the exposure of one Frame (or Line). + const char* const g_keyword_CameraEventExposureStart = "CameraEventExposureStart"; + // Device just completed the exposure of one Frame (or Line). + const char* const g_keyword_CameraEventExposureEnd = "CameraEventExposureEnd"; + // Device just detected an error. + const char* const g_keyword_CameraEventError = "CameraEventError"; + + const char* const g_keyword_CameraEventNotificationOff = "Off"; + const char* const g_keyword_CameraEventNotificationOn = "On"; + + // configuration file format constants const char* const g_FieldDelimiters = ","; const char* const g_CFGCommand_Device = "Device"; diff --git a/MMDevice/Property.cpp b/MMDevice/Property.cpp index 7088a8f81..32e8e72dd 100644 --- a/MMDevice/Property.cpp +++ b/MMDevice/Property.cpp @@ -326,12 +326,21 @@ unsigned MM::PropertyCollection::GetSize() const return (unsigned) properties_.size(); } -int MM::PropertyCollection::CreateProperty(const char* pszName, const char* pszValue, MM::PropertyType eType, bool bReadOnly, MM::ActionFunctor* pAct, bool isPreInitProperty) +int MM::PropertyCollection::CreateProperty(const char* pszName, const char* pszValue, MM::PropertyType eType, + bool bReadOnly, MM::ActionFunctor* pAct, bool isPreInitProperty, bool standard) { // check if the name already exists if (Find(pszName)) return DEVICE_DUPLICATE_PROPERTY; + if (!standard) + { + // make sure it doesn't begin with the reserved prefix for standard properties + std::string prefixAndDelim = std::string(g_KeywordStandardPropertyPrefix); + if (std::string(pszName).find(prefixAndDelim) == 0) + return DEVICE_INVALID_PROPERTY; + } + MM::Property* pProp=0; switch(eType) @@ -363,12 +372,20 @@ int MM::PropertyCollection::CreateProperty(const char* pszName, const char* pszV return DEVICE_OK; } -int MM::PropertyCollection::SetAllowedValues(const char* pszName, std::vector& values) +int MM::PropertyCollection::SetAllowedValues(const char* pszName, std::vector& values, bool standard) { MM::Property* pProp = Find(pszName); if (!pProp) return DEVICE_INVALID_PROPERTY; // name not found + if (!standard) + { + // make sure it doesn't begin with the reserved prefix for standard properties + std::string prefixAndDelim = std::string(g_KeywordStandardPropertyPrefix); + if (std::string(pszName).find(prefixAndDelim) == 0) + return DEVICE_INVALID_PROPERTY; + } + pProp->ClearAllowedValues(); for (unsigned i=0; iAddAllowedValue(values[i].c_str()); @@ -376,32 +393,56 @@ int MM::PropertyCollection::SetAllowedValues(const char* pszName, std::vectorClearAllowedValues(); return DEVICE_OK; } -int MM::PropertyCollection::AddAllowedValue(const char* pszName, const char* value, long data) +int MM::PropertyCollection::AddAllowedValue(const char* pszName, const char* value, long data, bool standard) { MM::Property* pProp = Find(pszName); if (!pProp) return DEVICE_INVALID_PROPERTY; // name not found + if (!standard) + { + // make sure it doesn't begin with the reserved prefix for standard properties + std::string prefixAndDelim = std::string(g_KeywordStandardPropertyPrefix); + if (std::string(pszName).find(prefixAndDelim) == 0) + return DEVICE_INVALID_PROPERTY; + } + pProp->AddAllowedValue(value, data); return DEVICE_OK; } -int MM::PropertyCollection::AddAllowedValue(const char* pszName, const char* value) +int MM::PropertyCollection::AddAllowedValue(const char* pszName, const char* value, bool standard) { MM::Property* pProp = Find(pszName); if (!pProp) return DEVICE_INVALID_PROPERTY; // name not found + if (!standard) + { + // make sure it doesn't begin with the reserved prefix for standard properties + std::string prefixAndDelim = std::string(g_KeywordStandardPropertyPrefix); + if (std::string(pszName).find(prefixAndDelim) == 0) + return DEVICE_INVALID_PROPERTY; + } + pProp->AddAllowedValue(value); return DEVICE_OK; } @@ -499,3 +540,17 @@ int MM::PropertyCollection::Apply(const char* pszName) return pProp->Apply(); } + +int MM::PropertyCollection::Delete(const char* pszName) +{ + MM::Property* pProp = Find(pszName); + if (!pProp) + return DEVICE_INVALID_PROPERTY; + + // remove it from the map + properties_.erase(pszName); + + delete pProp; + + return DEVICE_OK; +} diff --git a/MMDevice/Property.h b/MMDevice/Property.h index 24c1a8920..1a3fa5f35 100644 --- a/MMDevice/Property.h +++ b/MMDevice/Property.h @@ -27,9 +27,46 @@ #include #include #include +#include namespace MM { +// Standard Properties +const char* const g_KeywordStandardPropertyPrefix = "api//"; + +// Define NaN for use in property definitions +const double PropertyLimitUndefined = std::numeric_limits::quiet_NaN(); + +// Standard property metadata structure +struct StandardProperty { + // Helper to check if limits are defined + bool hasLimits() const { + return !std::isnan(lowerLimit) && !std::isnan(upperLimit); + } + + // Equality operator for comparison in containers + bool operator==(const StandardProperty& other) const { + return name == other.name && + type == other.type && + isReadOnly == other.isReadOnly && + isPreInit == other.isPreInit && + allowedValues == other.allowedValues && + requiredValues == other.requiredValues && + lowerLimit == other.lowerLimit && + upperLimit == other.upperLimit; + } + + std::string name; // Full property name (without prefix) + PropertyType type; // Float, String, or Integer + bool isReadOnly; // Whether property is read-only + bool isPreInit; // Whether property should be set before initialization + std::vector allowedValues; // (for String properties) if empty, no restrictions + std::vector requiredValues; // (for String properties) if empty, no restrictions + double lowerLimit = PropertyLimitUndefined; // Lower limit for numeric properties (NaN if not limited) + double upperLimit = PropertyLimitUndefined; // Upper limit for numeric properties (NaN if not limited) +}; + + /** * Base API for all device properties. * This interface is used by action functors. @@ -437,12 +474,12 @@ class PropertyCollection PropertyCollection(); ~PropertyCollection(); - int CreateProperty(const char* name, const char* value, PropertyType eType, bool bReadOnly, ActionFunctor* pAct=0, bool isPreInitProperty=false); + int CreateProperty(const char* name, const char* value, PropertyType eType, bool bReadOnly, ActionFunctor* pAct=0, bool isPreInitProperty=false, bool standard=false); int RegisterAction(const char* name, ActionFunctor* fpAct); - int SetAllowedValues(const char* name, std::vector& values); - int ClearAllowedValues(const char* name); - int AddAllowedValue(const char* name, const char* value, long data); - int AddAllowedValue(const char* name, const char* value); + int SetAllowedValues(const char* name, std::vector& values, bool standard=false); + int ClearAllowedValues(const char* name, bool standard=false); + int AddAllowedValue(const char* name, const char* value, long data, bool standard=false); + int AddAllowedValue(const char* name, const char* value, bool standard=false); int GetPropertyData(const char* name, const char* value, long& data); int GetCurrentPropertyData(const char* name, long& data); int Set(const char* propName, const char* Value); @@ -455,11 +492,11 @@ class PropertyCollection int ApplyAll(); int Update(const char* Name); int Apply(const char* Name); + int Delete(const char* pszName); private: typedef std::map CPropArray; CPropArray properties_; }; - } // namespace MM