From 06e2db553ea374cee606621720b913d16efcafac Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Wed, 26 Feb 2025 10:25:45 -0800 Subject: [PATCH 01/50] initial implementaiton with runtime validity checks --- MMDevice/DeviceBase.h | 57 ++++++++++++++++++++++++++++++++++++ MMDevice/MMDevice.h | 34 +++++++++++++++++++++ MMDevice/MMDeviceConstants.h | 1 + MMDevice/Property.cpp | 18 +++++++++++- MMDevice/Property.h | 45 +++++++++++++++++++++++++++- 5 files changed, 153 insertions(+), 2 deletions(-) diff --git a/MMDevice/DeviceBase.h b/MMDevice/DeviceBase.h index 84cef7177..73ca0de03 100644 --- a/MMDevice/DeviceBase.h +++ b/MMDevice/DeviceBase.h @@ -584,6 +584,62 @@ class CDeviceBase : public T return properties_.CreateProperty(name, value, eType, readOnly, pAct, isPreInitProperty); } + /** + * Creates a standard property -- a property with a predefined name, type, and possibly values. + * Standard properties can apply to all devices, or only some types, as specified in the + * switch statement below. + * Type, read-only and pre-init are fixed for the standard property + * + * @param property - the standard property enum + * @param value - initial value + * @param pAct - function object called on the property actions + */ + int CreateStandardProperty(MM::StandardProperty property, const char* value, MM::ActionFunctor* pAct = 0) + { + if (!IsValidStandardProperty(property)) { + return DEVICE_INVALID_PROPERTY; + } + + // TODO: check if value is valid? + + // Prepend the prefix marking it as a standard property and the delimiter + std::string fullName = g_KeywordStandardPropertyPrefix + "//" + property.name; + + return properties_.CreateStandardProperty(property, value, pAct); + } + + /** + * Checks if a standard property is applicable to this device type + * @param property - the standard property to check + * @return - true if applicable, false otherwise + */ + bool IsValidStandardProperty(MM::StandardProperty property) const + { + MM::DeviceType deviceType = T::Type; + return IsPropertyApplicableToDeviceType(property, deviceType); + } + + /** + * Helper function to check if a standard property is applicable to a specific device type + * @param property - the standard property to check + * @param deviceType - the device type to check against + * @return - true if the property is applicable to the device type, false otherwise + */ + bool IsPropertyApplicableToDeviceType(MM::StandardProperty property, MM::DeviceType deviceType) const + { + // Iterate through the standard property list to find if this property + // is applicable to this device type + for (const MM::StandardPropAssociation& association : MM::g_StandardPropertyList) + { + if (strcmp(association.property.name, property.name) == 0 && + association.deviceType == deviceType) + { + return true; + } + } + return false; + } + /** * Creates a new property for the device. * @param name - property name @@ -596,6 +652,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); } diff --git a/MMDevice/MMDevice.h b/MMDevice/MMDevice.h index c674ac0b7..795407057 100644 --- a/MMDevice/MMDevice.h +++ b/MMDevice/MMDevice.h @@ -43,6 +43,7 @@ #include "DeviceUtils.h" #include "ImageMetadata.h" #include "DeviceThreads.h" +#include "Property.h" #include #include @@ -205,6 +206,39 @@ namespace MM { }; + //////////////////////////// + ///// Standard properties + //////////////////////////// + + // Specific standard properties + static const MM::StandardProperty g_BinningStandardProperty( + "Binning", // name + String, // type + false, // isReadOnly + false, // isPreInit + nullptr, // allowedValues + PropertyLimitUndefined, // lowerLimit + PropertyLimitUndefined // upperLimit + ); + + // A struct to encode which which device types have/require which standard properties + struct StandardPropAssociation { + const MM::StandardProperty& property; + MM::DeviceType deviceType; // which device types can use + bool required; + }; + + // This data structure associates standard properties with device types. + // each entry defines one property-device association and whether the + // standard property is required + static const StandardPropAssociation g_StandardPropertyList[] = + { + // propertyname deviceTypes required + {g_BinningStandardProperty, MM::CameraDevice, false} + }; + + + /** * Generic device interface. */ diff --git a/MMDevice/MMDeviceConstants.h b/MMDevice/MMDeviceConstants.h index 65b0a5c90..149d72705 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 { diff --git a/MMDevice/Property.cpp b/MMDevice/Property.cpp index 7088a8f81..5cd5b7d78 100644 --- a/MMDevice/Property.cpp +++ b/MMDevice/Property.cpp @@ -326,7 +326,23 @@ 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::CreateStandardProperty(StandardProperty property, const char* value, ActionFunctor* pAct) +{ + return DoCreateProperty(property.name, value, property.type, property.isReadOnly, pAct, property.isPreInit); +} + +int MM::PropertyCollection::CreateProperty(const char* name, const char* value, PropertyType eType, bool bReadOnly, ActionFunctor* pAct=0, bool isPreInitProperty=false) +{ + // disallow properties that begin with the reserved prefix for standard properties + // std prefic + delimiter + property name + std::string prefixAndDelim = std::string(g_KeywordStandardPropertyPrefix) + std::string("//"); + if (std::string(name).find(prefixAndDelim) == 0) + return DEVICE_INVALID_PROPERTY; + + return DoCreateProperty(name, value, eType, bReadOnly, pAct, isPreInitProperty); +} + +int MM::PropertyCollection::DoCreateProperty(const char* pszName, const char* pszValue, MM::PropertyType eType, bool bReadOnly, MM::ActionFunctor* pAct, bool isPreInitProperty) { // check if the name already exists if (Find(pszName)) diff --git a/MMDevice/Property.h b/MMDevice/Property.h index 24c1a8920..320141695 100644 --- a/MMDevice/Property.h +++ b/MMDevice/Property.h @@ -30,6 +30,48 @@ 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 +class StandardProperty { +public: + StandardProperty( + const char* name, + PropertyType type, + bool isReadOnly, + bool isPreInit, + const char* const* allowedValues, + double lowerLimit, + double upperLimit + ) : + name(name), + type(type), + isReadOnly(isReadOnly), + isPreInit(isPreInit), + allowedValues(allowedValues), + lowerLimit(lowerLimit), + upperLimit(upperLimit) + {} + + // Helper to check if limits are defined + bool hasLimits() const { + return !std::isnan(lowerLimit) && !std::isnan(upperLimit); + } + + const char* 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 + const char* const* allowedValues; // Array of allowed string values (nullptr if not restricted) + double lowerLimit; // Lower limit for numeric properties (NaN if not limited) + double upperLimit; // Upper limit for numeric properties (NaN if not limited) +}; + + /** * Base API for all device properties. * This interface is used by action functors. @@ -437,6 +479,7 @@ class PropertyCollection PropertyCollection(); ~PropertyCollection(); + int CreateStandardProperty(StandardProperty property, const char* value, ActionFunctor* pAct=0); int CreateProperty(const char* name, const char* value, PropertyType eType, bool bReadOnly, ActionFunctor* pAct=0, bool isPreInitProperty=false); int RegisterAction(const char* name, ActionFunctor* fpAct); int SetAllowedValues(const char* name, std::vector& values); @@ -457,9 +500,9 @@ class PropertyCollection int Apply(const char* Name); private: + int DoCreateProperty(const char* name, const char* value, PropertyType eType, bool bReadOnly, ActionFunctor* pAct=0, bool isPreInitProperty=false); typedef std::map CPropArray; CPropArray properties_; }; - } // namespace MM From 19319939017c6877052010de37a6002d10235761 Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Thu, 27 Feb 2025 15:26:19 -0800 Subject: [PATCH 02/50] Working with runtime checks for required properties --- DeviceAdapters/DemoCamera/DemoCamera.cpp | 43 +++++ DeviceAdapters/DemoCamera/DemoCamera.h | 2 + MMCore/Devices/DeviceInstance.cpp | 8 + MMDevice/DeviceBase.h | 182 +++++++++++++----- MMDevice/MMDevice-SharedRuntime.vcxproj | 3 +- .../MMDevice-SharedRuntime.vcxproj.filters | 5 +- MMDevice/MMDevice-StaticRuntime.vcxproj | 3 +- .../MMDevice-StaticRuntime.vcxproj.filters | 5 +- MMDevice/MMDevice.cpp | 51 ----- MMDevice/MMDevice.h | 117 +++++++---- MMDevice/Property.cpp | 27 +-- MMDevice/Property.h | 53 +++-- 12 files changed, 313 insertions(+), 186 deletions(-) delete mode 100644 MMDevice/MMDevice.cpp diff --git a/DeviceAdapters/DemoCamera/DemoCamera.cpp b/DeviceAdapters/DemoCamera/DemoCamera.cpp index cde5082f7..c47eb1826 100644 --- a/DeviceAdapters/DemoCamera/DemoCamera.cpp +++ b/DeviceAdapters/DemoCamera/DemoCamera.cpp @@ -308,6 +308,16 @@ int CDemoCamera::Initialize() else LogMessage(NoHubError); + // 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..5e7a115ee 100644 --- a/DeviceAdapters/DemoCamera/DemoCamera.h +++ b/DeviceAdapters/DemoCamera/DemoCamera.h @@ -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/MMCore/Devices/DeviceInstance.cpp b/MMCore/Devices/DeviceInstance.cpp index a36ea75b7..cbee0d2a6 100644 --- a/MMCore/Devices/DeviceInstance.cpp +++ b/MMCore/Devices/DeviceInstance.cpp @@ -376,6 +376,14 @@ 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_->ImplementsRequiredStandardProperties(failedProperty)) { + ThrowError("Device " + GetLabel() + + " does not implement required standard property: " + + std::string(failedProperty)); + } initialized_ = true; } diff --git a/MMDevice/DeviceBase.h b/MMDevice/DeviceBase.h index 73ca0de03..2865cefe4 100644 --- a/MMDevice/DeviceBase.h +++ b/MMDevice/DeviceBase.h @@ -38,6 +38,7 @@ #include #include #include +#include // common error messages const char* const g_Msg_ERR = "Unknown error in the device"; @@ -569,6 +570,38 @@ class CDeviceBase : public T return true; } + bool ImplementsRequiredStandardProperties(char* failedProperty) const { + // Get the device type + MM::DeviceType deviceType = this->GetType(); + + // Look up properties for this device type + auto it = MM::GetDeviceTypeStandardPropertiesMap().find(deviceType); + if (it != MM::GetDeviceTypeStandardPropertiesMap().end()) { + // Iterate through all properties for this device type + const auto& properties = it->second; + for (const auto& prop : properties) { + // Check if this property is required + if (prop.required) { + // Construct the full property name with prefix + std::string fullName = MM::g_KeywordStandardPropertyPrefix; + fullName += prop.name; + + // 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 + return true; + } + + + /** * Creates a new property for the device. * @param name - property name @@ -584,60 +617,16 @@ class CDeviceBase : public T return properties_.CreateProperty(name, value, eType, readOnly, pAct, isPreInitProperty); } - /** - * Creates a standard property -- a property with a predefined name, type, and possibly values. - * Standard properties can apply to all devices, or only some types, as specified in the - * switch statement below. - * Type, read-only and pre-init are fixed for the standard property - * - * @param property - the standard property enum - * @param value - initial value - * @param pAct - function object called on the property actions - */ - int CreateStandardProperty(MM::StandardProperty property, const char* value, MM::ActionFunctor* pAct = 0) - { - if (!IsValidStandardProperty(property)) { - return DEVICE_INVALID_PROPERTY; - } - - // TODO: check if value is valid? - - // Prepend the prefix marking it as a standard property and the delimiter - std::string fullName = g_KeywordStandardPropertyPrefix + "//" + property.name; - - return properties_.CreateStandardProperty(property, value, pAct); - } - - /** - * Checks if a standard property is applicable to this device type - * @param property - the standard property to check - * @return - true if applicable, false otherwise - */ - bool IsValidStandardProperty(MM::StandardProperty property) const - { - MM::DeviceType deviceType = T::Type; - return IsPropertyApplicableToDeviceType(property, deviceType); + //// 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 LINK_STANDARD_PROP_TO_DEVICE_TYPE macro + int CreateTestStandardProperty(const char* value, MM::ActionFunctor* pAct = 0) { + return CreateStandardProperty(value, pAct); } - /** - * Helper function to check if a standard property is applicable to a specific device type - * @param property - the standard property to check - * @param deviceType - the device type to check against - * @return - true if the property is applicable to the device type, false otherwise - */ - bool IsPropertyApplicableToDeviceType(MM::StandardProperty property, MM::DeviceType deviceType) const - { - // Iterate through the standard property list to find if this property - // is applicable to this device type - for (const MM::StandardPropAssociation& association : MM::g_StandardPropertyList) - { - if (strcmp(association.property.name, property.name) == 0 && - association.deviceType == deviceType) - { - return true; - } - } - return false; + int CreateTestWithValuesStandardProperty(const char* value, MM::ActionFunctor* pAct = 0) { + return CreateStandardProperty(value, pAct, + MM::g_TestWithValuesStandardProperty.requiredValues); } /** @@ -1276,6 +1265,95 @@ class CDeviceBase : public T } private: + + // template + // struct TypePrinter; + + // // This will cause a compilation error that shows the type + // TypePrinter show_type; + + /** + * 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; + } + + // Set allowed values if they exist + if (!PropRef.allowedValues.empty()) { + // If the property has predefined allowed values, validate that all supplied values are allowed + if (std::find(PropRef.allowedValues.begin(), PropRef.allowedValues.end(), value) == PropRef.allowedValues.end()) { + return DEVICE_INVALID_PROPERTY_VALUE; + } + + for (const std::string& val : values) { + if (std::find(PropRef.allowedValues.begin(), PropRef.allowedValues.end(), val) == PropRef.allowedValues.end()) { + return DEVICE_INVALID_PROPERTY_VALUE; + } + } + } + // if there are required values, make sure they are all present + if (!PropRef.requiredValues.empty()) { + // throw an error if required values are not present + for (const std::string& val : PropRef.requiredValues) { + if (std::find(values.begin(), values.end(), val) == values.end()) { + return DEVICE_INVALID_PROPERTY_VALUE; + } + } + } + // now that all required values are present, and all supplied values are allowed, + // add the user-supplied values to the property + for (const std::string& val : values) { + ret = AddAllowedValue(fullName.c_str(), val.c_str()); + 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::IsStandardPropertyValid::value, + "This standard property is not valid for this device type. Check the LINK_STANDARD_PROP_TO_DEVICE_TYPE definitions in MMDevice.h"); + return DEVICE_UNSUPPORTED_COMMAND; // This line will never execute due to the static_assert + } + bool PropertyDefined(const char* propName) const { return properties_.Find(propName) != 0; 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 795407057..7a8d345da 100644 --- a/MMDevice/MMDevice.h +++ b/MMDevice/MMDevice.h @@ -48,10 +48,12 @@ #include #include #include +#include #include #include #include #include +#include // To be removed once the deprecated Get/SetModuleHandle() is removed: #ifdef _WIN32 @@ -209,35 +211,83 @@ namespace MM { //////////////////////////// ///// 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(); + } // Specific standard properties - static const MM::StandardProperty g_BinningStandardProperty( - "Binning", // name + static const MM::StandardProperty g_TestStandardProperty( + "Test", // name String, // type - false, // isReadOnly - false, // isPreInit - nullptr, // allowedValues + false, // isReadOnly + false, // isPreInit + {}, // allowedValues (empty vector) + {}, // requiredValues (empty vector) PropertyLimitUndefined, // lowerLimit - PropertyLimitUndefined // upperLimit + PropertyLimitUndefined, // upperLimit + false // required ); - // A struct to encode which which device types have/require which standard properties - struct StandardPropAssociation { - const MM::StandardProperty& property; - MM::DeviceType deviceType; // which device types can use - bool required; - }; + 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 + false // required + ); - // This data structure associates standard properties with device types. - // each entry defines one property-device association and whether the - // standard property is required - static const StandardPropAssociation g_StandardPropertyList[] = - { - // propertyname deviceTypes required - {g_BinningStandardProperty, MM::CameraDevice, false} + + // 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 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 }; + #define LINK_STANDARD_PROP_TO_DEVICE_TYPE(DeviceType, PropertyRef) \ + template <> \ + struct IsStandardPropertyValid { \ + static const bool value = true; \ + }; \ + namespace { \ + static const bool PropertyRef##_registered = (RegisterStandardProperty(PropertyRef, {DeviceType}), true); \ + } + + // This determines which standard properties are valid for which device types + LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_TestStandardProperty) + LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_TestWithValuesStandardProperty) /** * Generic device interface. @@ -260,6 +310,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 ImplementsRequiredStandardProperties(char* failedProperty) const = 0; /** * Sequences can be used for fast acquisitions, synchronized by TTLs rather than * computer commands. @@ -343,7 +394,7 @@ namespace MM { { public: virtual DeviceType GetType() const { return Type; } - static const DeviceType Type; + static constexpr DeviceType Type = GenericDevice; }; /** @@ -355,7 +406,7 @@ namespace MM { virtual ~Camera() {} virtual DeviceType GetType() const { return Type; } - static const DeviceType Type; + static constexpr DeviceType Type = CameraDevice; // Camera API /** @@ -591,7 +642,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; @@ -614,7 +665,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; @@ -714,7 +765,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 @@ -800,7 +851,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; @@ -826,7 +877,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; @@ -848,7 +899,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; @@ -874,7 +925,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; @@ -893,7 +944,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; @@ -977,7 +1028,7 @@ namespace MM { // MMDevice API virtual DeviceType GetType() const { return Type; } - static const DeviceType Type; + static constexpr DeviceType Type = MagnifierDevice; virtual double GetMagnification() = 0; }; @@ -997,7 +1048,7 @@ namespace MM { virtual ~SLM() {} virtual DeviceType GetType() const { return Type; } - static const DeviceType Type; + static constexpr DeviceType Type = SLMDevice; // SLM API /** @@ -1156,7 +1207,7 @@ namespace MM { virtual ~Galvo() {} virtual DeviceType GetType() const { return Type; } - static const DeviceType Type; + static constexpr DeviceType Type = GalvoDevice; //Galvo API: @@ -1283,7 +1334,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. diff --git a/MMDevice/Property.cpp b/MMDevice/Property.cpp index 5cd5b7d78..eff757530 100644 --- a/MMDevice/Property.cpp +++ b/MMDevice/Property.cpp @@ -326,28 +326,21 @@ unsigned MM::PropertyCollection::GetSize() const return (unsigned) properties_.size(); } -int MM::PropertyCollection::CreateStandardProperty(StandardProperty property, const char* value, ActionFunctor* pAct) -{ - return DoCreateProperty(property.name, value, property.type, property.isReadOnly, pAct, property.isPreInit); -} - -int MM::PropertyCollection::CreateProperty(const char* name, const char* value, PropertyType eType, bool bReadOnly, ActionFunctor* pAct=0, bool isPreInitProperty=false) -{ - // disallow properties that begin with the reserved prefix for standard properties - // std prefic + delimiter + property name - std::string prefixAndDelim = std::string(g_KeywordStandardPropertyPrefix) + std::string("//"); - if (std::string(name).find(prefixAndDelim) == 0) - return DEVICE_INVALID_PROPERTY; - - return DoCreateProperty(name, value, eType, bReadOnly, pAct, isPreInitProperty); -} - -int MM::PropertyCollection::DoCreateProperty(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) diff --git a/MMDevice/Property.h b/MMDevice/Property.h index 320141695..0494bdb07 100644 --- a/MMDevice/Property.h +++ b/MMDevice/Property.h @@ -27,11 +27,12 @@ #include #include #include +#include namespace MM { // Standard Properties -const char* const g_KeywordStandardPropertyPrefix = "api"; +const char* const g_KeywordStandardPropertyPrefix = "api//"; // Define NaN for use in property definitions const double PropertyLimitUndefined = std::numeric_limits::quiet_NaN(); @@ -39,22 +40,19 @@ const double PropertyLimitUndefined = std::numeric_limits::quiet_NaN(); // Standard property metadata structure class StandardProperty { public: - StandardProperty( - const char* name, - PropertyType type, - bool isReadOnly, - bool isPreInit, - const char* const* allowedValues, - double lowerLimit, - double upperLimit - ) : + StandardProperty(const char* name, PropertyType type, bool isReadOnly, + bool isPreInit, const std::vector& allowedValues, + const std::vector& requiredValues, + double lowerLimit, double upperLimit, bool required) : name(name), type(type), isReadOnly(isReadOnly), isPreInit(isPreInit), allowedValues(allowedValues), + requiredValues(requiredValues), lowerLimit(lowerLimit), - upperLimit(upperLimit) + upperLimit(upperLimit), + required(required) {} // Helper to check if limits are defined @@ -62,13 +60,28 @@ class StandardProperty { return !std::isnan(lowerLimit) && !std::isnan(upperLimit); } - const char* 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 - const char* const* allowedValues; // Array of allowed string values (nullptr if not restricted) - double lowerLimit; // Lower limit for numeric properties (NaN if not limited) - double upperLimit; // Upper limit for numeric properties (NaN if not limited) + // 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 && + required == other.required; + } + + 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; // if empty, no restrictions + std::vector requiredValues; // if empty, no restrictions + double lowerLimit; // Lower limit for numeric properties (NaN if not limited) + double upperLimit; // Upper limit for numeric properties (NaN if not limited) + bool required; // Whether to throw a runtime error if the property is not found on init }; @@ -479,8 +492,7 @@ class PropertyCollection PropertyCollection(); ~PropertyCollection(); - int CreateStandardProperty(StandardProperty property, const char* value, ActionFunctor* pAct=0); - 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); @@ -500,7 +512,6 @@ class PropertyCollection int Apply(const char* Name); private: - int DoCreateProperty(const char* name, const char* value, PropertyType eType, bool bReadOnly, ActionFunctor* pAct=0, bool isPreInitProperty=false); typedef std::map CPropArray; CPropArray properties_; }; From 30de537067bb71bf38a3e0474258c3046d746d71 Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Thu, 27 Feb 2025 15:28:33 -0800 Subject: [PATCH 03/50] add comment --- MMDevice/DeviceBase.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MMDevice/DeviceBase.h b/MMDevice/DeviceBase.h index 2865cefe4..275e8f67e 100644 --- a/MMDevice/DeviceBase.h +++ b/MMDevice/DeviceBase.h @@ -625,6 +625,8 @@ class CDeviceBase : public T } 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); } From 8789d6703cd446e6d7d3e92993767a1965565943 Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Fri, 28 Feb 2025 09:15:33 -0800 Subject: [PATCH 04/50] cleanup --- MMDevice/DeviceBase.h | 6 ---- MMDevice/MMDevice.h | 67 +++++++++++++++++++++++++++---------------- 2 files changed, 42 insertions(+), 31 deletions(-) diff --git a/MMDevice/DeviceBase.h b/MMDevice/DeviceBase.h index 275e8f67e..0a3679c9b 100644 --- a/MMDevice/DeviceBase.h +++ b/MMDevice/DeviceBase.h @@ -1268,12 +1268,6 @@ class CDeviceBase : public T private: - // template - // struct TypePrinter; - - // // This will cause a compilation error that shows the type - // TypePrinter show_type; - /** * Low-level implementation for creating standard properties. * diff --git a/MMDevice/MMDevice.h b/MMDevice/MMDevice.h index 7a8d345da..07697e3a7 100644 --- a/MMDevice/MMDevice.h +++ b/MMDevice/MMDevice.h @@ -212,6 +212,8 @@ namespace MM { ///// Standard properties //////////////////////////// + //// 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; @@ -236,6 +238,42 @@ namespace MM { 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 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 LINK_STANDARD_PROP_TO_DEVICE_TYPE(DeviceType, PropertyRef) \ + template <> \ + struct IsStandardPropertyValid { \ + static const bool value = true; \ + }; \ + namespace { \ + static const bool PropertyRef##_registered = (RegisterStandardProperty(PropertyRef, {DeviceType}), true); \ + } + + /////// Standard property definitions + // Each standard property is defined here, and then linked to one or more + // device types using the 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 @@ -249,6 +287,9 @@ namespace MM { false // required ); + 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 @@ -262,33 +303,9 @@ namespace MM { false // required ); - - // 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 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 - }; - - - #define LINK_STANDARD_PROP_TO_DEVICE_TYPE(DeviceType, PropertyRef) \ - template <> \ - struct IsStandardPropertyValid { \ - static const bool value = true; \ - }; \ - namespace { \ - static const bool PropertyRef##_registered = (RegisterStandardProperty(PropertyRef, {DeviceType}), true); \ - } - - // This determines which standard properties are valid for which device types - LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_TestStandardProperty) LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_TestWithValuesStandardProperty) + /** * Generic device interface. */ From 5759cc0985e86e3bc9bd69228de17e0037679c8f Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Fri, 28 Feb 2025 09:17:51 -0800 Subject: [PATCH 05/50] more cleanup --- MMDevice/DeviceBase.h | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/MMDevice/DeviceBase.h b/MMDevice/DeviceBase.h index 0a3679c9b..8293fd9ac 100644 --- a/MMDevice/DeviceBase.h +++ b/MMDevice/DeviceBase.h @@ -118,6 +118,20 @@ 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 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); + } + /** * Assigns description string for a device (for use only by the calling code). */ @@ -617,20 +631,6 @@ class CDeviceBase : public T return properties_.CreateProperty(name, value, eType, readOnly, pAct, isPreInitProperty); } - //// 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 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); - } - /** * Creates a new property for the device. * @param name - property name From 88833b169666b17847f5635e78562eebc115de3f Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Fri, 28 Feb 2025 09:47:38 -0800 Subject: [PATCH 06/50] change comment --- MMDevice/DeviceBase.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MMDevice/DeviceBase.h b/MMDevice/DeviceBase.h index 8293fd9ac..ef8979d90 100644 --- a/MMDevice/DeviceBase.h +++ b/MMDevice/DeviceBase.h @@ -1305,7 +1305,7 @@ class CDeviceBase : public T return ret; } - // Set allowed values if they exist + // Ensure only allowed values are set if (!PropRef.allowedValues.empty()) { // If the property has predefined allowed values, validate that all supplied values are allowed if (std::find(PropRef.allowedValues.begin(), PropRef.allowedValues.end(), value) == PropRef.allowedValues.end()) { From e5ea9ce51e49d64e3eb9dc97459dfc9da94c3d62 Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Tue, 4 Mar 2025 11:22:19 -0800 Subject: [PATCH 07/50] add code review fixes --- MMCore/Devices/DeviceInstance.cpp | 4 +- MMDevice/DeviceBase.h | 8 +-- MMDevice/MMDevice.h | 108 +++++++++++++++--------------- MMDevice/Property.h | 24 ++----- 4 files changed, 66 insertions(+), 78 deletions(-) diff --git a/MMCore/Devices/DeviceInstance.cpp b/MMCore/Devices/DeviceInstance.cpp index cbee0d2a6..bc21bda20 100644 --- a/MMCore/Devices/DeviceInstance.cpp +++ b/MMCore/Devices/DeviceInstance.cpp @@ -380,7 +380,9 @@ DeviceInstance::Initialize() // Check for that all required standard properties implemented char failedProperty[MM::MaxStrLength]; if (!pImpl_->ImplementsRequiredStandardProperties(failedProperty)) { - ThrowError("Device " + GetLabel() + + // shutdown the device + Shutdown(); + ThrowError("Device " + GetLabel() + " does not implement required standard property: " + std::string(failedProperty)); } diff --git a/MMDevice/DeviceBase.h b/MMDevice/DeviceBase.h index ef8979d90..9a9a2d6e1 100644 --- a/MMDevice/DeviceBase.h +++ b/MMDevice/DeviceBase.h @@ -120,7 +120,7 @@ class CDeviceBase : public T //// 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 LINK_STANDARD_PROP_TO_DEVICE_TYPE macro + // 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); } @@ -589,8 +589,8 @@ class CDeviceBase : public T MM::DeviceType deviceType = this->GetType(); // Look up properties for this device type - auto it = MM::GetDeviceTypeStandardPropertiesMap().find(deviceType); - if (it != MM::GetDeviceTypeStandardPropertiesMap().end()) { + 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) { @@ -1346,7 +1346,7 @@ class CDeviceBase : public T CreateStandardProperty(const char* /*value*/, MM::ActionFunctor* /*pAct*/ = 0, const std::vector& /*values*/ = std::vector()) { static_assert(MM::IsStandardPropertyValid::value, - "This standard property is not valid for this device type. Check the LINK_STANDARD_PROP_TO_DEVICE_TYPE definitions in MMDevice.h"); + "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 } diff --git a/MMDevice/MMDevice.h b/MMDevice/MMDevice.h index 07697e3a7..8ecf53398 100644 --- a/MMDevice/MMDevice.h +++ b/MMDevice/MMDevice.h @@ -212,64 +212,66 @@ namespace MM { ///// Standard properties //////////////////////////// - //// 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); + 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; } - } - // 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; + // 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); + } } - - 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 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 LINK_STANDARD_PROP_TO_DEVICE_TYPE(DeviceType, PropertyRef) \ - template <> \ - struct IsStandardPropertyValid { \ - static const bool value = true; \ - }; \ - namespace { \ - static const bool PropertyRef##_registered = (RegisterStandardProperty(PropertyRef, {DeviceType}), true); \ - } + // 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 IsStandardPropertyValid { \ + static const bool value = true; \ + }; \ + namespace { \ + static const bool PropertyRef##_registered = (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 LINK_STANDARD_PROP_TO_DEVICE_TYPE macro. + // 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. @@ -287,7 +289,7 @@ namespace MM { false // required ); - LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_TestStandardProperty) + MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_TestStandardProperty) static const std::vector testRequiredValues = {"value1", "value2", "value3"}; @@ -303,7 +305,7 @@ namespace MM { false // required ); - LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_TestWithValuesStandardProperty) + MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_TestWithValuesStandardProperty) /** diff --git a/MMDevice/Property.h b/MMDevice/Property.h index 0494bdb07..d8a9443b4 100644 --- a/MMDevice/Property.h +++ b/MMDevice/Property.h @@ -38,23 +38,7 @@ const char* const g_KeywordStandardPropertyPrefix = "api//"; const double PropertyLimitUndefined = std::numeric_limits::quiet_NaN(); // Standard property metadata structure -class StandardProperty { -public: - StandardProperty(const char* name, PropertyType type, bool isReadOnly, - bool isPreInit, const std::vector& allowedValues, - const std::vector& requiredValues, - double lowerLimit, double upperLimit, bool required) : - name(name), - type(type), - isReadOnly(isReadOnly), - isPreInit(isPreInit), - allowedValues(allowedValues), - requiredValues(requiredValues), - lowerLimit(lowerLimit), - upperLimit(upperLimit), - required(required) - {} - +struct StandardProperty { // Helper to check if limits are defined bool hasLimits() const { return !std::isnan(lowerLimit) && !std::isnan(upperLimit); @@ -79,9 +63,9 @@ class StandardProperty { bool isPreInit; // Whether property should be set before initialization std::vector allowedValues; // if empty, no restrictions std::vector requiredValues; // if empty, no restrictions - double lowerLimit; // Lower limit for numeric properties (NaN if not limited) - double upperLimit; // Upper limit for numeric properties (NaN if not limited) - bool required; // Whether to throw a runtime error if the property is not found on init + double lowerLimit = PropertyLimitUndefined; // Lower limit for numeric properties (NaN if not limited) + double upperLimit = PropertyLimitUndefined; // Upper limit for numeric properties (NaN if not limited) + bool required = false; // Whether to throw a runtime error if the property is not found on init }; From 3633c1f33914fd854e29ff7f4bf1e2b9ef024d4b Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Wed, 5 Mar 2025 09:37:36 -0800 Subject: [PATCH 08/50] split camera base class into Base and LegacyBase --- DeviceAdapters/ABS/ABSCamera.cpp | 2 +- DeviceAdapters/ABS/ABSCamera.h | 2 +- .../AlliedVisionCamera/AlliedVisionCamera.h | 2 +- DeviceAdapters/AmScope/AmScope.cpp | 2 +- DeviceAdapters/AmScope/AmScope.h | 2 +- DeviceAdapters/Andor/Andor.h | 2 +- DeviceAdapters/AndorSDK3/AndorSDK3.cpp | 2 +- DeviceAdapters/AndorSDK3/AndorSDK3.h | 2 +- .../ArduinoCounter/ArduinoCounter.h | 2 +- DeviceAdapters/Atik/Atik.cpp | 8 +- DeviceAdapters/Atik/Atik.h | 2 +- DeviceAdapters/Basler/BaslerPylonCamera.cpp | 2 +- DeviceAdapters/Basler/BaslerPylonCamera.h | 2 +- .../BaumerOptronic/BaumerOptronic.cpp | 2 +- .../BaumerOptronic/BaumerOptronic.h | 2 +- DeviceAdapters/DahengGalaxy/ClassGalaxy.cpp | 2 +- DeviceAdapters/DahengGalaxy/ClassGalaxy.h | 2 +- DeviceAdapters/DemoCamera/DemoCamera.cpp | 2 +- DeviceAdapters/DemoCamera/DemoCamera.h | 2 +- .../DirectElectron/src/DECamera/DECamera.cpp | 8 +- .../DirectElectron/src/DECamera/DECamera.h | 2 +- DeviceAdapters/FLICamera/FLICamera.cpp | 2 +- DeviceAdapters/FLICamera/FLICamera.h | 2 +- DeviceAdapters/FakeCamera/FakeCamera.cpp | 6 +- DeviceAdapters/FakeCamera/FakeCamera.h | 2 +- DeviceAdapters/Fli/FirstLightImagingCameras.h | 2 +- DeviceAdapters/GigECamera/GigECamera.cpp | 2 +- DeviceAdapters/GigECamera/GigECamera.h | 2 +- DeviceAdapters/Hikrobot/HikrobotCamera.cpp | 2 +- DeviceAdapters/Hikrobot/HikrobotCamera.h | 2 +- DeviceAdapters/IDSPeak/IDSPeak.cpp | 2 +- DeviceAdapters/IDSPeak/IDSPeak.h | 2 +- DeviceAdapters/IDS_uEye/IDS_uEye.cpp | 2 +- DeviceAdapters/IDS_uEye/IDS_uEye.h | 2 +- DeviceAdapters/IIDC/MMIIDCCamera.h | 2 +- DeviceAdapters/JAI/JAI.h | 2 +- DeviceAdapters/Lumenera/LumeneraCamera.cpp | 2 +- DeviceAdapters/Lumenera/LumeneraCamera.h | 2 +- .../MatrixVision/mvIMPACT_Acquire_Device.h | 2 +- .../Mightex_C_Cam/Mightex_USBCamera.cpp | 2 +- .../Mightex_C_Cam/Mightex_USBCamera.h | 2 +- DeviceAdapters/Motic/MoticCamera.cpp | 2 +- DeviceAdapters/Motic/MoticCamera.h | 2 +- .../OpenCVgrabber/OpenCVgrabber.cpp | 2 +- DeviceAdapters/OpenCVgrabber/OpenCVgrabber.h | 2 +- DeviceAdapters/PCO_Generic/MicroManager.cpp | 2 +- DeviceAdapters/PCO_Generic/MicroManager.h | 2 +- DeviceAdapters/PICAM/PICAMAdapter.h | 2 +- DeviceAdapters/PICAM/PICAMUniversal.cpp | 8 +- DeviceAdapters/PVCAM/PVCAMAdapter.h | 2 +- DeviceAdapters/PVCAM/PVCAMUniversal.cpp | 6 +- DeviceAdapters/Piper/CameraAdapter.h | 2 +- DeviceAdapters/Pixelink/Pixelink.h | 2 +- DeviceAdapters/PlayerOne/POACamera.cpp | 2 +- DeviceAdapters/PlayerOne/POACamera.h | 2 +- DeviceAdapters/PointGrey/PointGrey.h | 2 +- DeviceAdapters/PyDevice/PyCamera.h | 2 +- DeviceAdapters/QCam/QICamera.cpp | 2 +- DeviceAdapters/QCam/QICamera.h | 2 +- DeviceAdapters/QSI/QSICameraAdapter.h | 4 +- DeviceAdapters/RaptorEPIX/RaptorEPIX.cpp | 2 +- DeviceAdapters/RaptorEPIX/RaptorEPIX.h | 2 +- .../SequenceTester/SequenceTester.cpp | 6 +- .../SequenceTester/SequenceTester.h | 4 +- DeviceAdapters/SigmaKoki/Camera.cpp | 2 +- DeviceAdapters/SigmaKoki/Camera.h | 2 +- DeviceAdapters/Spinnaker/SpinnakerCamera.cpp | 2 +- DeviceAdapters/Spinnaker/SpinnakerCamera.h | 2 +- DeviceAdapters/TISCam/TIScamera.cpp | 2 +- DeviceAdapters/TISCam/TIScamera.h | 2 +- DeviceAdapters/TSI/TSI3Cam.h | 2 +- DeviceAdapters/TSI/TSICam.h | 2 +- DeviceAdapters/TUCam/MMTUCam.cpp | 2 +- DeviceAdapters/TUCam/MMTUCam.h | 2 +- .../TeensyPulseGenerator/CameraPulser.h | 2 +- .../ThorlabsUSBCamera/ThorlabsUSBCamera.cpp | 2 +- .../ThorlabsUSBCamera/ThorlabsUSBCamera.h | 2 +- DeviceAdapters/TwainCamera/TwainCamera.cpp | 4 +- DeviceAdapters/TwainCamera/TwainCamera.h | 4 +- DeviceAdapters/TwoPhoton/TwoPhoton.cpp | 2 +- DeviceAdapters/TwoPhoton/TwoPhoton.h | 2 +- DeviceAdapters/UniversalMMHubUsb/ummhUsb.h | 2 +- DeviceAdapters/Utilities/Utilities.h | 2 +- DeviceAdapters/Ximea/XIMEACamera.h | 2 +- DeviceAdapters/ZWO/MyASICam2.h | 2 +- DeviceAdapters/dc1394/dc1394.h | 2 +- MMDevice/DeviceBase.h | 186 ++++++++++++------ 87 files changed, 234 insertions(+), 162 deletions(-) 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..51f02a8f4 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(), + CLegacyCameraBase(), maxWidth_(0), maxHeight_(0), exposure_us_(0), diff --git a/DeviceAdapters/Basler/BaslerPylonCamera.h b/DeviceAdapters/Basler/BaslerPylonCamera.h index 22838e55a..34f5d516b 100644 --- a/DeviceAdapters/Basler/BaslerPylonCamera.h +++ b/DeviceAdapters/Basler/BaslerPylonCamera.h @@ -63,7 +63,7 @@ enum class CTempCameraEventHandler; class CircularBufferInserter; -class BaslerCamera : public CCameraBase { +class BaslerCamera : public CLegacyCameraBase { public: BaslerCamera(); ~BaslerCamera(); 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..0223a8b5e 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), diff --git a/DeviceAdapters/DemoCamera/DemoCamera.h b/DeviceAdapters/DemoCamera/DemoCamera.h index 1de99da9b..44a90b977 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(); 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/MMDevice/DeviceBase.h b/MMDevice/DeviceBase.h index 84cef7177..db04e619b 100644 --- a/MMDevice/DeviceBase.h +++ b/MMDevice/DeviceBase.h @@ -1279,6 +1279,7 @@ class CGenericBase : public CDeviceBase { }; + /** * Base class for creating camera device adapters. * This class has a functional constructor - must be invoked @@ -1288,19 +1289,8 @@ template class CCameraBase : 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) + CCameraBase() { // create and initialize common transpose properties std::vector allowedValues; @@ -1315,50 +1305,41 @@ class CCameraBase : public CDeviceBase 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_;} + 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; + virtual bool Busy() = 0; /** * Continuous sequence acquisition. * Default to sequence acquisition with a high number of images */ - virtual int StartSequenceAcquisition(double interval) - { - return StartSequenceAcquisition(LONG_MAX, interval, false); - } + virtual int StartSequenceAcquisition(double interval) = 0; /** * Stop and wait for the thread finished */ - virtual int StopSequenceAcquisition() - { - if (!thd_->IsStopped()) { - thd_->Stop(); - thd_->wait(); - } - - return DEVICE_OK; - } + virtual int StopSequenceAcquisition() = 0; /** - * 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) @@ -1415,23 +1396,13 @@ class CCameraBase : public CDeviceBase } // temporary debug methods - virtual int PrepareSequenceAcqusition() {return DEVICE_OK;} + virtual int PrepareSequenceAcqusition() = 0; /** * Default implementation. */ - 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 int StartSequenceAcquisition(long numImages, double interval_ms, + bool stopOnOverflow) = 0; virtual int GetExposureSequenceMaxLength(long& /*nrEvents*/) const { @@ -1463,14 +1434,13 @@ class CCameraBase : public CDeviceBase return DEVICE_UNSUPPORTED_COMMAND; } - virtual bool IsCapturing(){return !thd_->IsStopped();} + virtual bool IsCapturing() = 0; 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 +1489,109 @@ class CCameraBase : public CDeviceBase return metadata_.GetSingleTag(key).GetValue(); } +private: + + Metadata metadata_; + +}; + + + + +/** +* 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 CCameraBase +{ +public: + + CLegacyCameraBase() : CCameraBase(), busy_(false), stopWhenCBOverflows_(false), thd_(0) + { + // TODO: does this belong here or in the superclass? + // create and initialize common transpose properties + // std::vector allowedValues; + // allowedValues.push_back("0"); + // allowedValues.push_back("1"); + // CreateProperty(MM::g_Keyword_Transpose_SwapXY, "0", MM::Integer, false); + // SetAllowedValues(MM::g_Keyword_Transpose_SwapXY, allowedValues); + // CreateProperty(MM::g_Keyword_Transpose_MirrorX, "0", MM::Integer, false); + // SetAllowedValues(MM::g_Keyword_Transpose_MirrorX, allowedValues); + // CreateProperty(MM::g_Keyword_Transpose_MirrorY, "0", MM::Integer, false); + // 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 ~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 +1656,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 +1675,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 +1735,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 +1767,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 +1781,6 @@ class CCameraBase : public CDeviceBase bool busy_; bool stopWhenCBOverflows_; - Metadata metadata_; BaseSequenceThread * thd_; friend class BaseSequenceThread; From 652991cca051c5feb46816e925d05e5bc8b8296e Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Wed, 5 Mar 2025 09:59:05 -0800 Subject: [PATCH 09/50] restore using statements --- MMDevice/DeviceBase.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MMDevice/DeviceBase.h b/MMDevice/DeviceBase.h index db04e619b..b9e7c3b93 100644 --- a/MMDevice/DeviceBase.h +++ b/MMDevice/DeviceBase.h @@ -1290,6 +1290,13 @@ class CCameraBase : public CDeviceBase { public: + using CDeviceBase::CreateProperty; + using CDeviceBase::SetAllowedValues; + using CDeviceBase::GetBinning; + using CDeviceBase::GetCoreCallback; + using CDeviceBase::SetProperty; + using CDeviceBase::LogMessage; + CCameraBase() { // create and initialize common transpose properties From 40c3d023d1d4fe055531834f92f63312a175e89d Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Wed, 5 Mar 2025 10:40:15 -0800 Subject: [PATCH 10/50] initial commit resuming previous work on camera api. Still have a compile bug in mmdevice --- DeviceAdapters/Basler/BaslerPylonCamera.cpp | 303 +++++++++++++++++++- DeviceAdapters/Basler/BaslerPylonCamera.h | 57 +++- MMDevice/DeviceBase.h | 64 +++++ MMDevice/MMDevice.h | 95 ++++++ MMDevice/MMDeviceConstants.h | 84 ++++++ 5 files changed, 596 insertions(+), 7 deletions(-) diff --git a/DeviceAdapters/Basler/BaslerPylonCamera.cpp b/DeviceAdapters/Basler/BaslerPylonCamera.cpp index 51f02a8f4..857f7e20d 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() : - CLegacyCameraBase(), + CNewAPICameraBase(), maxWidth_(0), maxHeight_(0), exposure_us_(0), @@ -168,7 +168,8 @@ BaslerCamera::BaslerCamera() : nodeMap_(NULL), initialized_(false), pTempHandler_(NULL), - camera_(NULL) + camera_(NULL), + multiFrameAcqCount_(0) { // call the base class method to set-up default error codes/messages InitializeDefaultErrorMessages(); @@ -870,6 +871,300 @@ int BaslerCamera::Initialize() 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::SelectTrigger(const char* triggerSelector) { + CEnumerationPtr TriggerSelectorPtr(nodeMap_->GetNode("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 ((strcmp(triggerSelector, MM::TriggerSelectorFrameBurstStart) == 0) || + (strcmp(triggerSelector, MM::TriggerSelectorAcquisitionStart) == 0) ) { + if (CEnumParameter(nodeMap_, "TriggerSelector").CanSetValue(MM::TriggerSelectorFrameBurstStart) ) { + TriggerSelectorPtr->FromString(MM::TriggerSelectorFrameBurstStart); + } else if (CEnumParameter(nodeMap_, "TriggerSelector").CanSetValue(MM::TriggerSelectorAcquisitionStart)) { + TriggerSelectorPtr->FromString(MM::TriggerSelectorAcquisitionStart); + } else { + return DEVICE_ERR; + } + + + } else { + TriggerSelectorPtr->FromString(triggerSelector); + } + return DEVICE_OK; +} + +bool BaslerCamera:: HasTrigger(const char * triggerSelector) { + return CEnumParameter(nodeMap_, "TriggerSelector").CanSetValue(triggerSelector); +} + +int BaslerCamera::SetTriggerMode(const char* triggerSelector, bool triggerMode) { + SelectTrigger(triggerSelector); + if (triggerMode) { + CEnumParameter(nodeMap_, "TriggerMode").SetValue("On"); + } else { + CEnumParameter(nodeMap_, "TriggerMode").SetValue("Off"); + } + return DEVICE_OK; +} + +int BaslerCamera::SetTriggerSource(const char* triggerSelector, const char* triggerSource) { + SelectTrigger(triggerSelector); + CEnumerationPtr TriggerSourcePtr(nodeMap_->GetNode("TriggerSource")); + if (strcmp(triggerSource, MM::TriggerSourceSoftware) == 0) { + // software triggers + TriggerSourcePtr->FromString("Software"); + } else if (strcmp(triggerSource, MM::TriggerIOLine0) == 0) { + // external (TTL) triggers + // Might be good to also provide a device-specific mapping from Lines to pins + TriggerSourcePtr->FromString("Line0"); + } else if (strcmp(triggerSource, MM::TriggerIOLine1) == 0) { + TriggerSourcePtr->FromString("Line1"); + } else if (strcmp(triggerSource, MM::TriggerIOLine2) == 0) { + TriggerSourcePtr->FromString("Line2"); + } else if (strcmp(triggerSource, MM::TriggerIOLine3) == 0) { + TriggerSourcePtr->FromString("Line3"); + } else { + // No other trigger modes supported currently + return DEVICE_ERR; + } + return DEVICE_OK; +} + +int BaslerCamera::SetTriggerDelay(const char* triggerSelector, int triggerDelay) { + SelectTrigger(triggerSelector); + CFloatPtr ptr(nodeMap_->GetNode("TriggerDelay")); + ptr->SetValue(triggerDelay); + return DEVICE_OK; +} + +int BaslerCamera::SetTriggerActivation(const char* triggerSelector, const char* triggerActivation) { + SelectTrigger(triggerSelector); + // bool writable = CEnumParameter(nodeMap_, "TriggerActivation").IsWritable(); + CEnumerationPtr TriggerActivationPtr(nodeMap_->GetNode("TriggerActivation")); + TriggerActivationPtr->FromString(triggerActivation); + return DEVICE_OK; +} + +int BaslerCamera::GetTriggerMode(const char* triggerSelector, bool& triggerMode) { + SelectTrigger(triggerSelector); + std::string s = NodeToString("TriggerMode"); + triggerMode = strcmp("On", s.c_str()) == 0; + return DEVICE_OK; +} + +int BaslerCamera::GetTriggerSource(const char* triggerSelector, char* triggerSource) { + SelectTrigger(triggerSelector); + std::string s = NodeToString("TriggerSource"); + if (s.rfind("Line0", 0) == 0) { + strcpy(triggerSource, MM::TriggerIOLine0); + } else if (s.rfind("Line1", 0) == 0) { + strcpy(triggerSource, MM::TriggerIOLine1); + } else if (s.rfind("Line2", 0) == 0) { + strcpy(triggerSource, MM::TriggerIOLine2); + } else if (s.rfind("Line3", 0) == 0) { + strcpy(triggerSource, MM::TriggerIOLine2); + } else { + strcpy(triggerSource, MM::TriggerSourceSoftware); + } + return DEVICE_OK; +} + +int BaslerCamera::GetTriggerDelay(const char* triggerSelector, int& triggerDelay) { + SelectTrigger(triggerSelector); + triggerDelay = CFloatPtr(nodeMap_->GetNode("TriggerDelay"))->GetValue(); + return DEVICE_OK; +} + +int BaslerCamera::GetTriggerActivation(const char* triggerSelector, char* triggerActivation) { + // Select the trigger to get the values of + SelectTrigger(triggerSelector); + std::string s = NodeToString("TriggerActivation"); + strcpy(triggerActivation, s.c_str()); + return DEVICE_OK; +} + +// NA for current testing camera +// bool BaslerCamera::HasExposureMode(const char* exposureMode) { +// +// } +// +// int BaslerCamera::SetExposureMode(const char* exposureMode) { +// +// } +// +// int BaslerCamera::GetExposureMode(char* exposureMode) { +// +// } + +int BaslerCamera::SetBurstFrameCount(unsigned count) { + CIntegerParameter(nodeMap_, "AcquisitionBurstFrameCount").SetValue(count); + return DEVICE_OK; +} + + + +unsigned BaslerCamera::GetBurstFrameCount() const { + return CIntegerParameter(nodeMap_, "AcquisitionBurstFrameCount").GetValue(); +} + + +int BaslerCamera::TriggerSoftware(const char* triggerSelector){ + SelectTrigger(triggerSelector); + // 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"); + } + } + + ImageHandler_ = new CircularBufferInserter(this); + camera_->RegisterImageEventHandler(ImageHandler_, RegistrationMode_Append, Cleanup_Delete); + + return DEVICE_OK; +} + +int BaslerCamera::AcquisitionArm() +{ + return AcquisitionArm(0); +} + +int BaslerCamera::AcquisitionStart() +{ + //The GenICam AcquisitionStart gets called automatically by StartGrabbing + if (multiFrameAcqCount_ == 0) { + // acquire until told to stop + camera_->StartGrabbing(GrabStrategy_OneByOne, GrabLoop_ProvidedByInstantCamera); + } else { + // acquire a set number of frames + 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(); + camera_->DeregisterImageEventHandler(ImageHandler_); + return DEVICE_OK; +} + +int BaslerCamera::AcquisitionAbort() +{ + return AcquisitionStop(); +} + +int BaslerCamera::GetAcquisitionStatus(const char* statusSelector, bool& status) { + if (strcmp(statusSelector, MM::CameraStatusAcquisitionActive) == 0) { + CEnumParameter(nodeMap_, "AcquisitionStatusSelector").SetValue("AcquisitionActive"); + } else if (strcmp(statusSelector, MM::CameraStatusExposureActive) == 0) { + CEnumParameter(nodeMap_, "AcquisitionStatusSelector").SetValue("ExposureActive"); + } else if (strcmp(statusSelector, MM::CameraStatusExposureTriggerWait) == 0) { + CEnumParameter(nodeMap_, "AcquisitionStatusSelector").SetValue("ExposureTriggerWait"); + } else if (strcmp(statusSelector, MM::CameraStatusFrameBurstTriggerActive) == 0) { + CEnumParameter(nodeMap_, "AcquisitionStatusSelector").SetValue("FrameBurstActive"); + } else if (strcmp(statusSelector, MM::CameraStatusFrameBurstTriggerWait) == 0) { + CEnumParameter(nodeMap_, "AcquisitionStatusSelector").SetValue("FrameBurstTriggerWait"); + } else if (strcmp(statusSelector, MM::CameraStatusFrameTriggerWait) == 0) { + CEnumParameter(nodeMap_, "AcquisitionStatusSelector").SetValue("FrameTriggerWait"); + } else { + return DEVICE_ERR; //TODO more descriptive error + } + + status = CBooleanParameter(nodeMap_, "AcquisitionStatus").GetValue(); + return DEVICE_OK; +} + +int BaslerCamera::SetIOLineInverted(const char* lineSelector, bool invert) { + CEnumParameter(nodeMap_, "LineSelector").SetValue(lineSelector); + CBooleanParameter(nodeMap_, "LineInverter").SetValue(invert); + return DEVICE_OK; +} + +int BaslerCamera::SetLineAsOutput(const char* lineSelector, bool output) { + CEnumParameter(nodeMap_, "LineSelector").SetValue(lineSelector); + CEnumParameter(nodeMap_, "LineMode").SetValue(output ? "Output" : "Input"); + return DEVICE_OK; +} + + +int BaslerCamera::SetOutputLineSource(const char* lineSelector, const char* source) { + // TODO check it lineSelector is valid once convered to property + CEnumParameter(nodeMap_, "LineSelector").SetValue(lineSelector); + if (strcmp(source, MM::OutputLineSourceAcquisitionActive) == 0) { + CEnumParameter(nodeMap_, "LineSource").SetValue("AcquisitionActive"); + } else if (strcmp(source, MM::OutputLineSourceExposureActive) == 0) { + CEnumParameter(nodeMap_, "LineSource").SetValue("ExposureActive"); + } else if (strcmp(source, MM::OutputLineSourceFrameBurstActive) == 0) { + CEnumParameter(nodeMap_, "LineSource").SetValue("FrameBurstActive"); + } else if (strcmp(source, MM::OutputLineSourceFrameBurstTriggerWait) == 0) { + CEnumParameter(nodeMap_, "LineSource").SetValue("FrameBurstTriggerWait"); + } else if (strcmp(source, MM::OutputLineSourceFrameTriggerWait) == 0) { + CEnumParameter(nodeMap_, "LineSource").SetValue("FrameTriggerWait"); + } else { + return DEVICE_ERR; //TODO more descriptive error + } + return DEVICE_OK; +} + + +int BaslerCamera::GetLineStatus(const char* lineSelector, bool& high) { + CEnumParameter(nodeMap_, "LineSelector").SetValue(lineSelector); + high = CBooleanParameter(nodeMap_, "LineStatus").GetValue(); + return DEVICE_OK; +} + + +//TODO +double BaslerCamera::GetRollingShutterLineOffset() const +{ + return DEVICE_NOT_SUPPORTED; +} + +int BaslerCamera::SetRollingShutterLineOffset(double offset_us) +{ + return CCameraBase::SetRollingShutterLineOffset(offset_us); +} + +unsigned BaslerCamera::GetRollingShutterActiveLines() const +{ + return CCameraBase::GetRollingShutterActiveLines(); +} + +unsigned BaslerCamera::SetRollingShutterActiveLines(unsigned numLines) +{ + return CCameraBase::setRollingShutterActiveLines(numLines); +} + +////////////////////////////////////////////////////////////// int BaslerCamera::CheckForBinningMode(CPropertyAction* pAct) @@ -1232,7 +1527,9 @@ int BaslerCamera::StopSequenceAcquisition() { camera_->StopGrabbing(); GetCoreCallback()->AcqFinished(this, 0); - camera_->DeregisterImageEventHandler(ImageHandler_); + // TODO: this was commented in my previous implementation of new camera API. Why? + // Maybe it doesn't matter because this will be deleted + // camera_->DeregisterImageEventHandler(ImageHandler_); } return DEVICE_OK; } diff --git a/DeviceAdapters/Basler/BaslerPylonCamera.h b/DeviceAdapters/Basler/BaslerPylonCamera.h index 34f5d516b..88424ea93 100644 --- a/DeviceAdapters/Basler/BaslerPylonCamera.h +++ b/DeviceAdapters/Basler/BaslerPylonCamera.h @@ -63,7 +63,7 @@ enum class CTempCameraEventHandler; class CircularBufferInserter; -class BaslerCamera : public CLegacyCameraBase { +class BaslerCamera : public CNewAPICameraBase { public: BaslerCamera(); ~BaslerCamera(); @@ -109,9 +109,6 @@ class BaslerCamera : public CLegacyCameraBase { 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(); @@ -126,6 +123,57 @@ class BaslerCamera : public CLegacyCameraBase { //Genicam Callback void ResultingFramerateCallback(GenApi::INode* pNode); + ///////////////////////////////////// + ///////// New Camera API /////////// + bool IsNewAPIImplemented() { return true; }; + + bool HasTrigger(const char* triggerSelector); + + int SetTriggerMode(const char* triggerSelector, bool triggerMode); + int SetTriggerSource(const char* triggerSelector, const char* triggerSource); + int SetTriggerDelay(const char* triggerSelector, int triggerDelay); + int SetTriggerActivation(const char* triggerSelector, const char* triggerActivation); + + int GetTriggerMode(const char* triggerSelector, bool& triggerMode); + int GetTriggerSource(const char* triggerSelector, char* triggerSource); + int GetTriggerDelay(const char* triggerSelector, int& triggerDelay); + int GetTriggerActivation(const char* triggerSelector, char* triggerActivation); + + //NA for current camera + // virtual bool HasExposureMode(const char* exposureMode); + // virtual int SetExposureMode(const char* exposureMode); + // virtual int GetExposureMode(char* exposureMode); + + // TODO probably want to replace these with properties + virtual int SetBurstFrameCount(unsigned count); + virtual unsigned GetBurstFrameCount() const; + + int TriggerSoftware(const char* triggerSelector); + + int AcquisitionArm(int frameCount); + int AcquisitionArm(); + + int AcquisitionStart(); + int AcquisitionStop(); + int AcquisitionAbort(); + + int GetAcquisitionStatus(const char* statusSelector, bool& status); + + int SetIOLineInverted(const char* lineSelector, bool invert); + int SetLineAsOutput(const char* lineSelector, bool output); + int SetOutputLineSource(const char* lineSelector, const char* source); + int GetLineStatus(const char* lineSelector, bool& high); + + + double GetRollingShutterLineOffset() const; + int SetRollingShutterLineOffset(double offset_us); + unsigned GetRollingShutterActiveLines() const; + unsigned SetRollingShutterActiveLines(unsigned numLines); + + // Convenience functions + std::string NodeToString(const char* str) const; + int SelectTrigger(const char* triggerSelector); + // action interface // ---------------- @@ -164,6 +212,7 @@ class BaslerCamera : public CLegacyCameraBase { bool colorCamera_; unsigned maxWidth_, maxHeight_; + unsigned multiFrameAcqCount_; int64_t DeviceLinkThroughputLimit_; int64_t InterPacketDelay_; double ResultingFrameRatePrevious; diff --git a/MMDevice/DeviceBase.h b/MMDevice/DeviceBase.h index c1ce05b00..08811df94 100644 --- a/MMDevice/DeviceBase.h +++ b/MMDevice/DeviceBase.h @@ -1634,6 +1634,70 @@ class CCameraBase : public CDeviceBase }; +// /** +// * Base class for creating camera device adapters using the new Camera API. +// */ +// // TODO: dissallow old sty +// template +// class CNewAPICameraBase : public CCameraBase +// { +// public: + +// // TODO: need to call superclass constructor pending whether those +// // transpose properties are needed + + +// virtual bool isNewAPIImplemented() {return true;}; + +// virtual bool hasTrigger(const char* cameraLabel, const char* triggerSelector) = 0; + +// virtual bool HasTrigger(const char* triggerSelector) { return false; }; +// virtual int SetTriggerMode(const char* triggerSelector, bool triggerMode) { return DEVICE_NOT_YET_IMPLEMENTED; }; +// virtual int SetTriggerSource(const char* triggerSelector, const char* triggerSource) { return DEVICE_NOT_YET_IMPLEMENTED; }; +// virtual int SetTriggerDelay(const char* triggerSelector, int triggerDelay) { return DEVICE_NOT_YET_IMPLEMENTED; }; +// virtual int SetTriggerActivation(const char* triggerSelector, const char* triggerActivation) { return DEVICE_NOT_YET_IMPLEMENTED; }; + +// virtual int GetTriggerMode(const char* triggerSelector, bool& triggerMode) { return DEVICE_NOT_YET_IMPLEMENTED; }; +// virtual int GetTriggerSource(const char* triggerSelector, char* triggerSource) { return DEVICE_NOT_YET_IMPLEMENTED; }; +// virtual int GetTriggerDelay(const char* triggerSelector, int& triggerDelay) { return DEVICE_NOT_YET_IMPLEMENTED; }; +// virtual int GetTriggerActivation(const char* triggerSelector, char* triggerActivation) { return DEVICE_NOT_YET_IMPLEMENTED; }; + +// /** +// * Every camera must have at least Timed exposure mode, so these implementations default to only that +// */ +// virtual bool HasExposureMode(const char* exposureMode) { return strcmp(exposureMode, MM::ExposureModeTimed) == 0; }; +// virtual int SetExposureMode(const char* exposureMode) { return (strcmp(exposureMode, MM::ExposureModeTimed) == 0) ? +// DEVICE_OK : DEVICE_UNSUPPORTED_COMMAND; }; +// virtual int GetExposureMode(char* exposureMode) { +// strcpy(exposureMode, MM::ExposureModeTimed); +// return DEVICE_OK; +// }; + +// virtual int TriggerSoftware(const char* triggerSelector) {return DEVICE_NOT_YET_IMPLEMENTED;}; + +// virtual int SetBurstFrameCount(unsigned count) { return DEVICE_NOT_YET_IMPLEMENTED; }; +// virtual unsigned GetBurstFrameCount() const { return 1; }; + +// virtual int AcquisitionArm(int frameCount) {return DEVICE_NOT_YET_IMPLEMENTED;}; +// virtual int AcquisitionArm() { return DEVICE_NOT_YET_IMPLEMENTED; }; + +// virtual int AcquisitionStart() {return DEVICE_NOT_YET_IMPLEMENTED;}; +// virtual int AcquisitionStop() {return DEVICE_NOT_YET_IMPLEMENTED;}; +// virtual int AcquisitionAbort() {return DEVICE_NOT_YET_IMPLEMENTED;}; + +// virtual int GetAcquisitionStatus(const char* statusSelector, bool& status) { return DEVICE_NOT_YET_IMPLEMENTED; }; + +// virtual int SetIOLineInverted(const char* lineSelector, bool invert) { return DEVICE_NOT_YET_IMPLEMENTED; }; +// virtual int SetLineAsOutput(const char* lineSelector, bool output) { return DEVICE_NOT_YET_IMPLEMENTED; }; +// virtual int SetOutputLineSource(const char* lineSelector, const char* source) { return DEVICE_NOT_YET_IMPLEMENTED; }; +// virtual int GetLineStatus(const char* lineSelector, bool& high) { return DEVICE_NOT_YET_IMPLEMENTED; }; + +// virtual double GetRollingShutterLineOffset() const {return DEVICE_NOT_YET_IMPLEMENTED;}; +// virtual int SetRollingShutterLineOffset(double offset_us) {return DEVICE_NOT_YET_IMPLEMENTED;}; +// virtual unsigned GetRollingShutterActiveLines() const {return DEVICE_NOT_YET_IMPLEMENTED;}; +// virtual unsigned setRollingShutterActiveLines(unsigned numLines) {return DEVICE_NOT_YET_IMPLEMENTED;}; +// }; + /** diff --git a/MMDevice/MMDevice.h b/MMDevice/MMDevice.h index 8ecf53398..e7dbadc0f 100644 --- a/MMDevice/MMDevice.h +++ b/MMDevice/MMDevice.h @@ -427,6 +427,101 @@ namespace MM { virtual DeviceType GetType() const { return Type; } static constexpr DeviceType Type = CameraDevice; + // //// New Camera API //// + // virtual bool IsNewAPIImplemented() = 0; + + // ////////////////////////////// + // // Triggers + // ////////////////////////////// + + // //Check which of the possible trigger types are available + // virtual bool HasTrigger(const char* triggerSelector) = 0; + + // // These should return an error code if the type is not valid + // // They are not meant to do any work. + // virtual int SetTriggerMode(const char* triggerSelector, bool triggerMode) = 0; + // virtual int SetTriggerSource(const char* triggerSelector, const char* triggerSource) = 0; + // virtual int SetTriggerDelay(const char* triggerSelector, int triggerDelay) = 0; + // virtual int SetTriggerActivation(const char* triggerSelector, const char* triggerActivation) = 0; + + // virtual int GetTriggerMode(const char* triggerSelector, bool& triggerMode) = 0; + // virtual int GetTriggerSource(const char* triggerSelector, char* triggerSource) = 0; + // virtual int GetTriggerDelay(const char* triggerSelector, int& triggerDelay) = 0; + // virtual int GetTriggerActivation(const char* triggerSelector, char* triggerActivation) = 0; + + // virtual bool HasExposureMode(const char* exposureMode) = 0; + // virtual int SetExposureMode(const char* exposureMode) = 0; + // virtual int GetExposureMode(char* exposureMode) = 0; + + // // TODO probably want to replace these with properties + // virtual int SetBurstFrameCount(unsigned count) = 0; + // virtual unsigned GetBurstFrameCount() const = 0; + + // // Send of software of the supplied type + // virtual int TriggerSoftware(const char* triggerSelector) = 0; + + // ////////////////////////////// + // // Acquisitions + // ////////////////////////////// + + + + // // Acquisition functions + // ////////////////////////////// + + // // 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 + // // -1 --> acqMode is continuous + + // virtual int AcquisitionArm(int frameCount) = 0; + // virtual int AcquisitionArm() = 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; + + // virtual int GetAcquisitionStatus(const char* statusSelector, bool& status) = 0; + + + // virtual int SetIOLineInverted(const char* lineSelector, bool invert) = 0; + // virtual int SetLineAsOutput(const char* lineSelector, bool output) = 0; + // virtual int SetOutputLineSource(const char* lineSelector, const char* source) = 0; + // virtual int GetLineStatus(const char* lineSelector, bool& status) = 0; + + + // // Rolling shutter/Lightsheet mode + // virtual double GetRollingShutterLineOffset() const = 0; + // virtual int SetRollingShutterLineOffset(double offset_us) = 0; + + // virtual unsigned GetRollingShutterActiveLines() const = 0; + // virtual unsigned SetRollingShutterActiveLines(unsigned numLines) = 0; + + /////////////////////////////////////////////////////////////// + ///// End new camera API //////////////////////// + ////////////////////////////////////////////////////////////// + // Camera API /** * Performs exposure and grabs a single image. diff --git a/MMDevice/MMDeviceConstants.h b/MMDevice/MMDeviceConstants.h index 149d72705..dafee1fb1 100644 --- a/MMDevice/MMDeviceConstants.h +++ b/MMDevice/MMDeviceConstants.h @@ -175,6 +175,90 @@ 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 TriggerSelectorAcquisitionStart = "AcquisitionStart"; + const char* const TriggerSelectorAcquisitionEnd = "AcquisitionEnd"; + const char* const TriggerSelectorAcquisitionActive = "AcquisitionActive"; + const char* const TriggerSelectorFrameBurstStart = "FrameBurstStart"; + const char* const TriggerSelectorFrameBurstEnd = "FrameBurstEnd"; + const char* const TriggerSelectorFrameBurstActive = "FrameBurstActive"; + const char* const TriggerSelectorFrameStart = "FrameStart"; + const char* const TriggerSelectorFrameEnd = "FrameEnd"; + const char* const TriggerSelectorFrameActive = "FrameActive"; + const char* const TriggerSelectorExposureStart = "ExposureStart"; + const char* const TriggerSelectorExposureEnd = "ExposureEnd"; + const char* const TriggerSelectorExposureActive = "ExposureActive"; + + const char* const TriggerModeOn = "On"; + const char* const TriggerModeOff = "Off"; + + const char* const TriggerIOLine0 = "Line0"; + const char* const TriggerIOLine1 = "Line1"; + const char* const TriggerIOLine2 = "Line2"; + const char* const TriggerIOLine3 = "Line3"; + const char* const TriggerSourceSoftware = "Software"; + + const char* const TriggerActivationAnyEdge = "AnyEdge"; + const char* const TriggerActivationRisingEdge = "RisingEdge"; + const char* const TriggerActivationFallingEdge = "FallingEdge"; + const char* const TriggerActivationLevelLow = "Low"; + const char* const TriggerActivationLevelHigh = "High"; + + const char* const ExposureModeTimed = "Timed"; + const char* const ExposureModeTriggerWidth = "TriggerWidth"; + const char* const ExposureModeTriggerControlled = "TriggerControlled"; + + const char* const CameraStatusAcquisitionTriggerWait = "AcquisitionTriggerWait"; + const char* const CameraStatusFrameBurstTriggerWait = "FrameBurstTriggerWait"; + const char* const CameraStatusFrameTriggerWait = "FrameTriggerWait"; + const char* const CameraStatusExposureTriggerWait = "ExposureTriggerWait"; + const char* const CameraStatusAcquisitionActive = "AcquisitionActive"; + const char* const CameraStatusFrameBurstTriggerActive = "FrameBurstTriggerActive"; + const char* const CameraStatusFrameActive = "FrameActive"; + const char* const CameraStatusExposureActive = "ExposureActive"; + const char* const CameraStatusAcquisitionTransfer = "AcquisitionTransfer"; + + + const char* const OutputLineSourceOff = "Off"; + const char* const OutputLineSourceAcquisitionTriggerWait = "AcquisitionTriggerWait"; + const char* const OutputLineSourceAcquisitionTrigger = "AcquisitionTrigger"; + const char* const OutputLineSourceAcquisitionTriggerMissed = "AcquisitionTriggerMissed"; + const char* const OutputLineSourceAcquisitionActive = "AcquisitionActive"; + + const char* const OutputLineSourceFrameBurstTriggerWait = "FrameBurstTriggerWait"; + const char* const OutputLineSourceFrameBurstTrigger = "FrameBurstTrigger"; + const char* const OutputLineSourceFrameBurstTriggerMissed = "FrameBurstTriggerMissed"; + const char* const OutputLineSourceFrameBurstActive = "FrameBurstTriggerActive"; + + const char* const OutputLineSourceFrameTriggerWait = "FrameTriggerWait"; + const char* const OutputLineSourceFrameTrigger = "FrameTrigger"; + const char* const OutputLineSourceFrameTriggerMissed = "FrameTriggerMissed"; + const char* const OutputLineSourceFrameActive = "FrameActive"; + + const char* const OutputLineSourceExposureTriggerWait = "ExposureTriggerWait"; + const char* const OutputLineSourceExposureTrigger = "ExposureTrigger"; + const char* const OutputLineSourceExposureTriggerMissed = "ExposureTriggerMissed"; + const char* const OutputLineSourceExposureActive = "ExposureActive"; + + // Camera Events + const char* const CameraEventAcquisitionTrigger = "CameraEventAcquisitionTrigger"; + const char* const CameraEventAcquisitionStart = "CameraEventAcquisitionStart"; + const char* const CameraEventAcquisitionEnd = "CameraEventAcquisitionEnd"; + const char* const CameraEventAcquisitionTransferStart = "CameraEventAcquisitionTransferStart"; + const char* const CameraEventAcquisitionTransferEnd = "CameraEventAcquisitionTransferEnd"; + const char* const CameraEventAcquisitionError = "CameraEventAcquisitionError"; + const char* const CameraEventFrameTrigger = "CameraEventFrameTrigger"; + const char* const CameraEventFrameStart = "CameraEventFrameStart"; + const char* const CameraEventFrameEnd = "CameraEventFrameEnd"; + const char* const CameraEventFrameBurstStart = "CameraEventFrameBurstStart"; + const char* const CameraEventFrameBurstEnd = "CameraEventFrameBurstEnd"; + const char* const CameraEventFrameTransferStart = "CameraEventFrameTransferStart"; + const char* const CameraEventFrameTransferEnd = "CameraEventFrameTransferEnd"; + const char* const CameraEventExposureStart = "CameraEventExposureStart"; + const char* const CameraEventExposureEnd = "CameraEventExposureEnd"; + + // configuration file format constants const char* const g_FieldDelimiters = ","; const char* const g_CFGCommand_Device = "Device"; From 49b01c532eac38c2b03b139af4bf3a87ea62fb8d Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Wed, 5 Mar 2025 10:57:46 -0800 Subject: [PATCH 11/50] fix namespace and constructor errors from previous commits --- MMDevice/MMDevice.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/MMDevice/MMDevice.h b/MMDevice/MMDevice.h index 8ecf53398..3d254af14 100644 --- a/MMDevice/MMDevice.h +++ b/MMDevice/MMDevice.h @@ -261,11 +261,11 @@ namespace MM { // device initialization) #define MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(DeviceType, PropertyRef) \ template <> \ - struct IsStandardPropertyValid { \ + struct MM::internal::IsStandardPropertyValid { \ static const bool value = true; \ }; \ namespace { \ - static const bool PropertyRef##_registered = (RegisterStandardProperty(PropertyRef, {DeviceType}), true); \ + static const bool PropertyRef##_registered = (MM::internal::RegisterStandardProperty(PropertyRef, {DeviceType}), true); \ } } // namespace internal @@ -277,7 +277,7 @@ namespace MM { // properties are supported by a given device. // Specific standard properties - static const MM::StandardProperty g_TestStandardProperty( + static const MM::StandardProperty g_TestStandardProperty{ "Test", // name String, // type false, // isReadOnly @@ -287,13 +287,13 @@ namespace MM { PropertyLimitUndefined, // lowerLimit PropertyLimitUndefined, // upperLimit false // required - ); + }; 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( + static const MM::StandardProperty g_TestWithValuesStandardProperty{ "TestWithValues", // name String, // type false, // isReadOnly @@ -303,7 +303,7 @@ namespace MM { PropertyLimitUndefined, // lowerLimit PropertyLimitUndefined, // upperLimit false // required - ); + }; MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_TestWithValuesStandardProperty) From a85a54ff0ef4450bc10e1bf6d40adf545e0376b7 Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Wed, 5 Mar 2025 10:59:50 -0800 Subject: [PATCH 12/50] resotre commented code --- MMDevice/DeviceBase.h | 126 +++++++++++++++++++++--------------------- MMDevice/MMDevice.h | 126 +++++++++++++++++++++--------------------- 2 files changed, 126 insertions(+), 126 deletions(-) diff --git a/MMDevice/DeviceBase.h b/MMDevice/DeviceBase.h index 08811df94..92174357b 100644 --- a/MMDevice/DeviceBase.h +++ b/MMDevice/DeviceBase.h @@ -1634,69 +1634,69 @@ class CCameraBase : public CDeviceBase }; -// /** -// * Base class for creating camera device adapters using the new Camera API. -// */ -// // TODO: dissallow old sty -// template -// class CNewAPICameraBase : public CCameraBase -// { -// public: - -// // TODO: need to call superclass constructor pending whether those -// // transpose properties are needed - - -// virtual bool isNewAPIImplemented() {return true;}; - -// virtual bool hasTrigger(const char* cameraLabel, const char* triggerSelector) = 0; - -// virtual bool HasTrigger(const char* triggerSelector) { return false; }; -// virtual int SetTriggerMode(const char* triggerSelector, bool triggerMode) { return DEVICE_NOT_YET_IMPLEMENTED; }; -// virtual int SetTriggerSource(const char* triggerSelector, const char* triggerSource) { return DEVICE_NOT_YET_IMPLEMENTED; }; -// virtual int SetTriggerDelay(const char* triggerSelector, int triggerDelay) { return DEVICE_NOT_YET_IMPLEMENTED; }; -// virtual int SetTriggerActivation(const char* triggerSelector, const char* triggerActivation) { return DEVICE_NOT_YET_IMPLEMENTED; }; - -// virtual int GetTriggerMode(const char* triggerSelector, bool& triggerMode) { return DEVICE_NOT_YET_IMPLEMENTED; }; -// virtual int GetTriggerSource(const char* triggerSelector, char* triggerSource) { return DEVICE_NOT_YET_IMPLEMENTED; }; -// virtual int GetTriggerDelay(const char* triggerSelector, int& triggerDelay) { return DEVICE_NOT_YET_IMPLEMENTED; }; -// virtual int GetTriggerActivation(const char* triggerSelector, char* triggerActivation) { return DEVICE_NOT_YET_IMPLEMENTED; }; - -// /** -// * Every camera must have at least Timed exposure mode, so these implementations default to only that -// */ -// virtual bool HasExposureMode(const char* exposureMode) { return strcmp(exposureMode, MM::ExposureModeTimed) == 0; }; -// virtual int SetExposureMode(const char* exposureMode) { return (strcmp(exposureMode, MM::ExposureModeTimed) == 0) ? -// DEVICE_OK : DEVICE_UNSUPPORTED_COMMAND; }; -// virtual int GetExposureMode(char* exposureMode) { -// strcpy(exposureMode, MM::ExposureModeTimed); -// return DEVICE_OK; -// }; - -// virtual int TriggerSoftware(const char* triggerSelector) {return DEVICE_NOT_YET_IMPLEMENTED;}; - -// virtual int SetBurstFrameCount(unsigned count) { return DEVICE_NOT_YET_IMPLEMENTED; }; -// virtual unsigned GetBurstFrameCount() const { return 1; }; - -// virtual int AcquisitionArm(int frameCount) {return DEVICE_NOT_YET_IMPLEMENTED;}; -// virtual int AcquisitionArm() { return DEVICE_NOT_YET_IMPLEMENTED; }; - -// virtual int AcquisitionStart() {return DEVICE_NOT_YET_IMPLEMENTED;}; -// virtual int AcquisitionStop() {return DEVICE_NOT_YET_IMPLEMENTED;}; -// virtual int AcquisitionAbort() {return DEVICE_NOT_YET_IMPLEMENTED;}; - -// virtual int GetAcquisitionStatus(const char* statusSelector, bool& status) { return DEVICE_NOT_YET_IMPLEMENTED; }; - -// virtual int SetIOLineInverted(const char* lineSelector, bool invert) { return DEVICE_NOT_YET_IMPLEMENTED; }; -// virtual int SetLineAsOutput(const char* lineSelector, bool output) { return DEVICE_NOT_YET_IMPLEMENTED; }; -// virtual int SetOutputLineSource(const char* lineSelector, const char* source) { return DEVICE_NOT_YET_IMPLEMENTED; }; -// virtual int GetLineStatus(const char* lineSelector, bool& high) { return DEVICE_NOT_YET_IMPLEMENTED; }; - -// virtual double GetRollingShutterLineOffset() const {return DEVICE_NOT_YET_IMPLEMENTED;}; -// virtual int SetRollingShutterLineOffset(double offset_us) {return DEVICE_NOT_YET_IMPLEMENTED;}; -// virtual unsigned GetRollingShutterActiveLines() const {return DEVICE_NOT_YET_IMPLEMENTED;}; -// virtual unsigned setRollingShutterActiveLines(unsigned numLines) {return DEVICE_NOT_YET_IMPLEMENTED;}; -// }; +/** +* Base class for creating camera device adapters using the new Camera API. +*/ +// TODO: dissallow old sty +template +class CNewAPICameraBase : public CCameraBase +{ +public: + + // TODO: need to call superclass constructor pending whether those + // transpose properties are needed + + + virtual bool isNewAPIImplemented() {return true;}; + + virtual bool hasTrigger(const char* cameraLabel, const char* triggerSelector) = 0; + + virtual bool HasTrigger(const char* triggerSelector) { return false; }; + virtual int SetTriggerMode(const char* triggerSelector, bool triggerMode) { return DEVICE_NOT_YET_IMPLEMENTED; }; + virtual int SetTriggerSource(const char* triggerSelector, const char* triggerSource) { return DEVICE_NOT_YET_IMPLEMENTED; }; + virtual int SetTriggerDelay(const char* triggerSelector, int triggerDelay) { return DEVICE_NOT_YET_IMPLEMENTED; }; + virtual int SetTriggerActivation(const char* triggerSelector, const char* triggerActivation) { return DEVICE_NOT_YET_IMPLEMENTED; }; + + virtual int GetTriggerMode(const char* triggerSelector, bool& triggerMode) { return DEVICE_NOT_YET_IMPLEMENTED; }; + virtual int GetTriggerSource(const char* triggerSelector, char* triggerSource) { return DEVICE_NOT_YET_IMPLEMENTED; }; + virtual int GetTriggerDelay(const char* triggerSelector, int& triggerDelay) { return DEVICE_NOT_YET_IMPLEMENTED; }; + virtual int GetTriggerActivation(const char* triggerSelector, char* triggerActivation) { return DEVICE_NOT_YET_IMPLEMENTED; }; + + /** + * Every camera must have at least Timed exposure mode, so these implementations default to only that + */ + virtual bool HasExposureMode(const char* exposureMode) { return strcmp(exposureMode, MM::ExposureModeTimed) == 0; }; + virtual int SetExposureMode(const char* exposureMode) { return (strcmp(exposureMode, MM::ExposureModeTimed) == 0) ? + DEVICE_OK : DEVICE_UNSUPPORTED_COMMAND; }; + virtual int GetExposureMode(char* exposureMode) { + strcpy(exposureMode, MM::ExposureModeTimed); + return DEVICE_OK; + }; + + virtual int TriggerSoftware(const char* triggerSelector) {return DEVICE_NOT_YET_IMPLEMENTED;}; + + virtual int SetBurstFrameCount(unsigned count) { return DEVICE_NOT_YET_IMPLEMENTED; }; + virtual unsigned GetBurstFrameCount() const { return 1; }; + + virtual int AcquisitionArm(int frameCount) {return DEVICE_NOT_YET_IMPLEMENTED;}; + virtual int AcquisitionArm() { return DEVICE_NOT_YET_IMPLEMENTED; }; + + virtual int AcquisitionStart() {return DEVICE_NOT_YET_IMPLEMENTED;}; + virtual int AcquisitionStop() {return DEVICE_NOT_YET_IMPLEMENTED;}; + virtual int AcquisitionAbort() {return DEVICE_NOT_YET_IMPLEMENTED;}; + + virtual int GetAcquisitionStatus(const char* statusSelector, bool& status) { return DEVICE_NOT_YET_IMPLEMENTED; }; + + virtual int SetIOLineInverted(const char* lineSelector, bool invert) { return DEVICE_NOT_YET_IMPLEMENTED; }; + virtual int SetLineAsOutput(const char* lineSelector, bool output) { return DEVICE_NOT_YET_IMPLEMENTED; }; + virtual int SetOutputLineSource(const char* lineSelector, const char* source) { return DEVICE_NOT_YET_IMPLEMENTED; }; + virtual int GetLineStatus(const char* lineSelector, bool& high) { return DEVICE_NOT_YET_IMPLEMENTED; }; + + virtual double GetRollingShutterLineOffset() const {return DEVICE_NOT_YET_IMPLEMENTED;}; + virtual int SetRollingShutterLineOffset(double offset_us) {return DEVICE_NOT_YET_IMPLEMENTED;}; + virtual unsigned GetRollingShutterActiveLines() const {return DEVICE_NOT_YET_IMPLEMENTED;}; + virtual unsigned setRollingShutterActiveLines(unsigned numLines) {return DEVICE_NOT_YET_IMPLEMENTED;}; +}; diff --git a/MMDevice/MMDevice.h b/MMDevice/MMDevice.h index dcb59277b..576eb099c 100644 --- a/MMDevice/MMDevice.h +++ b/MMDevice/MMDevice.h @@ -427,96 +427,96 @@ namespace MM { virtual DeviceType GetType() const { return Type; } static constexpr DeviceType Type = CameraDevice; - // //// New Camera API //// - // virtual bool IsNewAPIImplemented() = 0; + //// New Camera API //// + virtual bool IsNewAPIImplemented() = 0; - // ////////////////////////////// - // // Triggers - // ////////////////////////////// + ////////////////////////////// + // Triggers + ////////////////////////////// - // //Check which of the possible trigger types are available - // virtual bool HasTrigger(const char* triggerSelector) = 0; + //Check which of the possible trigger types are available + virtual bool HasTrigger(const char* triggerSelector) = 0; - // // These should return an error code if the type is not valid - // // They are not meant to do any work. - // virtual int SetTriggerMode(const char* triggerSelector, bool triggerMode) = 0; - // virtual int SetTriggerSource(const char* triggerSelector, const char* triggerSource) = 0; - // virtual int SetTriggerDelay(const char* triggerSelector, int triggerDelay) = 0; - // virtual int SetTriggerActivation(const char* triggerSelector, const char* triggerActivation) = 0; + // These should return an error code if the type is not valid + // They are not meant to do any work. + virtual int SetTriggerMode(const char* triggerSelector, bool triggerMode) = 0; + virtual int SetTriggerSource(const char* triggerSelector, const char* triggerSource) = 0; + virtual int SetTriggerDelay(const char* triggerSelector, int triggerDelay) = 0; + virtual int SetTriggerActivation(const char* triggerSelector, const char* triggerActivation) = 0; - // virtual int GetTriggerMode(const char* triggerSelector, bool& triggerMode) = 0; - // virtual int GetTriggerSource(const char* triggerSelector, char* triggerSource) = 0; - // virtual int GetTriggerDelay(const char* triggerSelector, int& triggerDelay) = 0; - // virtual int GetTriggerActivation(const char* triggerSelector, char* triggerActivation) = 0; + virtual int GetTriggerMode(const char* triggerSelector, bool& triggerMode) = 0; + virtual int GetTriggerSource(const char* triggerSelector, char* triggerSource) = 0; + virtual int GetTriggerDelay(const char* triggerSelector, int& triggerDelay) = 0; + virtual int GetTriggerActivation(const char* triggerSelector, char* triggerActivation) = 0; - // virtual bool HasExposureMode(const char* exposureMode) = 0; - // virtual int SetExposureMode(const char* exposureMode) = 0; - // virtual int GetExposureMode(char* exposureMode) = 0; + virtual bool HasExposureMode(const char* exposureMode) = 0; + virtual int SetExposureMode(const char* exposureMode) = 0; + virtual int GetExposureMode(char* exposureMode) = 0; - // // TODO probably want to replace these with properties - // virtual int SetBurstFrameCount(unsigned count) = 0; - // virtual unsigned GetBurstFrameCount() const = 0; + // TODO probably want to replace these with properties + virtual int SetBurstFrameCount(unsigned count) = 0; + virtual unsigned GetBurstFrameCount() const = 0; - // // Send of software of the supplied type - // virtual int TriggerSoftware(const char* triggerSelector) = 0; + // Send of software of the supplied type + virtual int TriggerSoftware(const char* triggerSelector) = 0; - // ////////////////////////////// - // // Acquisitions - // ////////////////////////////// + ////////////////////////////// + // Acquisitions + ////////////////////////////// - // // Acquisition functions - // ////////////////////////////// + // Acquisition functions + ////////////////////////////// - // // 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. + // 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? + // 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 - // // -1 --> acqMode is continuous + // Don't acqMode because it can be inferred from frameCount + // if frameCount is: 1 --> acqMode is single + // > 1 --> acqMode is MultiFrame + // -1 --> acqMode is continuous - // virtual int AcquisitionArm(int frameCount) = 0; - // virtual int AcquisitionArm() = 0; + virtual int AcquisitionArm(int frameCount) = 0; + virtual int AcquisitionArm() = 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; + // 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; + // 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; + //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; - // virtual int GetAcquisitionStatus(const char* statusSelector, bool& status) = 0; + virtual int GetAcquisitionStatus(const char* statusSelector, bool& status) = 0; - // virtual int SetIOLineInverted(const char* lineSelector, bool invert) = 0; - // virtual int SetLineAsOutput(const char* lineSelector, bool output) = 0; - // virtual int SetOutputLineSource(const char* lineSelector, const char* source) = 0; - // virtual int GetLineStatus(const char* lineSelector, bool& status) = 0; + virtual int SetIOLineInverted(const char* lineSelector, bool invert) = 0; + virtual int SetLineAsOutput(const char* lineSelector, bool output) = 0; + virtual int SetOutputLineSource(const char* lineSelector, const char* source) = 0; + virtual int GetLineStatus(const char* lineSelector, bool& status) = 0; - // // Rolling shutter/Lightsheet mode - // virtual double GetRollingShutterLineOffset() const = 0; - // virtual int SetRollingShutterLineOffset(double offset_us) = 0; + // Rolling shutter/Lightsheet mode + virtual double GetRollingShutterLineOffset() const = 0; + virtual int SetRollingShutterLineOffset(double offset_us) = 0; - // virtual unsigned GetRollingShutterActiveLines() const = 0; - // virtual unsigned SetRollingShutterActiveLines(unsigned numLines) = 0; + virtual unsigned GetRollingShutterActiveLines() const = 0; + virtual unsigned SetRollingShutterActiveLines(unsigned numLines) = 0; /////////////////////////////////////////////////////////////// ///// End new camera API //////////////////////// From 9ff9c1f8e7066ff9608e659702a6560973ef76a3 Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Wed, 5 Mar 2025 11:01:08 -0800 Subject: [PATCH 13/50] fix more namespace issues --- MMDevice/DeviceBase.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MMDevice/DeviceBase.h b/MMDevice/DeviceBase.h index 9a9a2d6e1..94e123041 100644 --- a/MMDevice/DeviceBase.h +++ b/MMDevice/DeviceBase.h @@ -1285,7 +1285,7 @@ class CDeviceBase : public T * @return DEVICE_OK if successful, error code otherwise */ template - typename std::enable_if::value, int>::type + 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 @@ -1342,10 +1342,10 @@ class CDeviceBase : public T // 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 + typename std::enable_if::value, int>::type CreateStandardProperty(const char* /*value*/, MM::ActionFunctor* /*pAct*/ = 0, const std::vector& /*values*/ = std::vector()) { - static_assert(MM::IsStandardPropertyValid::value, + 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 } From 60bd4b4d9185460d86769df2f47f461489ccf4fa Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Wed, 5 Mar 2025 11:14:05 -0800 Subject: [PATCH 14/50] working build with basler camera --- DeviceAdapters/Basler/BaslerPylonCamera.cpp | 38 ++++++++++++--------- DeviceAdapters/Basler/BaslerPylonCamera.h | 8 ++--- MMDevice/DeviceBase.h | 8 ++--- MMDevice/MMDevice.h | 2 +- 4 files changed, 30 insertions(+), 26 deletions(-) diff --git a/DeviceAdapters/Basler/BaslerPylonCamera.cpp b/DeviceAdapters/Basler/BaslerPylonCamera.cpp index 857f7e20d..12e907a4e 100644 --- a/DeviceAdapters/Basler/BaslerPylonCamera.cpp +++ b/DeviceAdapters/Basler/BaslerPylonCamera.cpp @@ -1143,26 +1143,30 @@ int BaslerCamera::GetLineStatus(const char* lineSelector, bool& high) { } -//TODO -double BaslerCamera::GetRollingShutterLineOffset() const -{ - return DEVICE_NOT_SUPPORTED; -} +// //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 CCameraBase::SetRollingShutterLineOffset(offset_us); -} +// int BaslerCamera::SetRollingShutterLineOffset(double offset_us) +// { +// // Return error code as expected +// return DEVICE_NOT_SUPPORTED; +// } -unsigned BaslerCamera::GetRollingShutterActiveLines() const -{ - return CCameraBase::GetRollingShutterActiveLines(); -} +// unsigned BaslerCamera::GetRollingShutterActiveLines() const +// { +// // Return a default value of 0 since the feature is not supported +// return 0; +// } -unsigned BaslerCamera::SetRollingShutterActiveLines(unsigned numLines) -{ - return CCameraBase::setRollingShutterActiveLines(numLines); -} +// int BaslerCamera::SetRollingShutterActiveLines(unsigned numLines) +// { +// // Return error code as expected +// return DEVICE_NOT_SUPPORTED; +// } ////////////////////////////////////////////////////////////// diff --git a/DeviceAdapters/Basler/BaslerPylonCamera.h b/DeviceAdapters/Basler/BaslerPylonCamera.h index 88424ea93..2c90893c8 100644 --- a/DeviceAdapters/Basler/BaslerPylonCamera.h +++ b/DeviceAdapters/Basler/BaslerPylonCamera.h @@ -165,10 +165,10 @@ class BaslerCamera : public CNewAPICameraBase { int GetLineStatus(const char* lineSelector, bool& high); - double GetRollingShutterLineOffset() const; - int SetRollingShutterLineOffset(double offset_us); - unsigned GetRollingShutterActiveLines() const; - unsigned SetRollingShutterActiveLines(unsigned numLines); + // double GetRollingShutterLineOffset() const; + // int SetRollingShutterLineOffset(double offset_us); + // unsigned GetRollingShutterActiveLines() const; + // int SetRollingShutterActiveLines(unsigned numLines); // Convenience functions std::string NodeToString(const char* str) const; diff --git a/MMDevice/DeviceBase.h b/MMDevice/DeviceBase.h index 3fefe48d2..f152bb7a5 100644 --- a/MMDevice/DeviceBase.h +++ b/MMDevice/DeviceBase.h @@ -1649,7 +1649,7 @@ class CNewAPICameraBase : public CCameraBase virtual bool isNewAPIImplemented() {return true;}; - virtual bool hasTrigger(const char* cameraLabel, const char* triggerSelector) = 0; + virtual bool hasTrigger(const char* cameraLabel, const char* triggerSelector) {return false;}; virtual bool HasTrigger(const char* triggerSelector) { return false; }; virtual int SetTriggerMode(const char* triggerSelector, bool triggerMode) { return DEVICE_NOT_YET_IMPLEMENTED; }; @@ -1692,10 +1692,10 @@ class CNewAPICameraBase : public CCameraBase virtual int SetOutputLineSource(const char* lineSelector, const char* source) { return DEVICE_NOT_YET_IMPLEMENTED; }; virtual int GetLineStatus(const char* lineSelector, bool& high) { return DEVICE_NOT_YET_IMPLEMENTED; }; - virtual double GetRollingShutterLineOffset() const {return DEVICE_NOT_YET_IMPLEMENTED;}; + virtual double GetRollingShutterLineOffset() const {return 0.0;}; virtual int SetRollingShutterLineOffset(double offset_us) {return DEVICE_NOT_YET_IMPLEMENTED;}; - virtual unsigned GetRollingShutterActiveLines() const {return DEVICE_NOT_YET_IMPLEMENTED;}; - virtual unsigned setRollingShutterActiveLines(unsigned numLines) {return DEVICE_NOT_YET_IMPLEMENTED;}; + virtual unsigned GetRollingShutterActiveLines() const {return 0;}; + virtual int SetRollingShutterActiveLines(unsigned numLines) {return DEVICE_NOT_YET_IMPLEMENTED;}; }; diff --git a/MMDevice/MMDevice.h b/MMDevice/MMDevice.h index 576eb099c..9fa0d4458 100644 --- a/MMDevice/MMDevice.h +++ b/MMDevice/MMDevice.h @@ -516,7 +516,7 @@ namespace MM { virtual int SetRollingShutterLineOffset(double offset_us) = 0; virtual unsigned GetRollingShutterActiveLines() const = 0; - virtual unsigned SetRollingShutterActiveLines(unsigned numLines) = 0; + virtual int SetRollingShutterActiveLines(unsigned numLines) = 0; /////////////////////////////////////////////////////////////// ///// End new camera API //////////////////////// From d0b45a804414d1ec5397646ee6c01c7c3ee5c8a7 Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Wed, 5 Mar 2025 14:28:17 -0800 Subject: [PATCH 15/50] converted many methods to standard properties --- DeviceAdapters/Basler/BaslerPylonCamera.cpp | 201 +------------ DeviceAdapters/Basler/BaslerPylonCamera.h | 35 +-- MMDevice/DeviceBase.h | 84 +++--- MMDevice/MMDevice.h | 313 +++++++++++++++++--- MMDevice/MMDeviceConstants.h | 159 +++++----- 5 files changed, 408 insertions(+), 384 deletions(-) diff --git a/DeviceAdapters/Basler/BaslerPylonCamera.cpp b/DeviceAdapters/Basler/BaslerPylonCamera.cpp index 12e907a4e..250e4a968 100644 --- a/DeviceAdapters/Basler/BaslerPylonCamera.cpp +++ b/DeviceAdapters/Basler/BaslerPylonCamera.cpp @@ -883,145 +883,7 @@ std::string BaslerCamera::NodeToString(const char* str) const { return s; } -int BaslerCamera::SelectTrigger(const char* triggerSelector) { - CEnumerationPtr TriggerSelectorPtr(nodeMap_->GetNode("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 ((strcmp(triggerSelector, MM::TriggerSelectorFrameBurstStart) == 0) || - (strcmp(triggerSelector, MM::TriggerSelectorAcquisitionStart) == 0) ) { - if (CEnumParameter(nodeMap_, "TriggerSelector").CanSetValue(MM::TriggerSelectorFrameBurstStart) ) { - TriggerSelectorPtr->FromString(MM::TriggerSelectorFrameBurstStart); - } else if (CEnumParameter(nodeMap_, "TriggerSelector").CanSetValue(MM::TriggerSelectorAcquisitionStart)) { - TriggerSelectorPtr->FromString(MM::TriggerSelectorAcquisitionStart); - } else { - return DEVICE_ERR; - } - - - } else { - TriggerSelectorPtr->FromString(triggerSelector); - } - return DEVICE_OK; -} - -bool BaslerCamera:: HasTrigger(const char * triggerSelector) { - return CEnumParameter(nodeMap_, "TriggerSelector").CanSetValue(triggerSelector); -} - -int BaslerCamera::SetTriggerMode(const char* triggerSelector, bool triggerMode) { - SelectTrigger(triggerSelector); - if (triggerMode) { - CEnumParameter(nodeMap_, "TriggerMode").SetValue("On"); - } else { - CEnumParameter(nodeMap_, "TriggerMode").SetValue("Off"); - } - return DEVICE_OK; -} - -int BaslerCamera::SetTriggerSource(const char* triggerSelector, const char* triggerSource) { - SelectTrigger(triggerSelector); - CEnumerationPtr TriggerSourcePtr(nodeMap_->GetNode("TriggerSource")); - if (strcmp(triggerSource, MM::TriggerSourceSoftware) == 0) { - // software triggers - TriggerSourcePtr->FromString("Software"); - } else if (strcmp(triggerSource, MM::TriggerIOLine0) == 0) { - // external (TTL) triggers - // Might be good to also provide a device-specific mapping from Lines to pins - TriggerSourcePtr->FromString("Line0"); - } else if (strcmp(triggerSource, MM::TriggerIOLine1) == 0) { - TriggerSourcePtr->FromString("Line1"); - } else if (strcmp(triggerSource, MM::TriggerIOLine2) == 0) { - TriggerSourcePtr->FromString("Line2"); - } else if (strcmp(triggerSource, MM::TriggerIOLine3) == 0) { - TriggerSourcePtr->FromString("Line3"); - } else { - // No other trigger modes supported currently - return DEVICE_ERR; - } - return DEVICE_OK; -} - -int BaslerCamera::SetTriggerDelay(const char* triggerSelector, int triggerDelay) { - SelectTrigger(triggerSelector); - CFloatPtr ptr(nodeMap_->GetNode("TriggerDelay")); - ptr->SetValue(triggerDelay); - return DEVICE_OK; -} - -int BaslerCamera::SetTriggerActivation(const char* triggerSelector, const char* triggerActivation) { - SelectTrigger(triggerSelector); - // bool writable = CEnumParameter(nodeMap_, "TriggerActivation").IsWritable(); - CEnumerationPtr TriggerActivationPtr(nodeMap_->GetNode("TriggerActivation")); - TriggerActivationPtr->FromString(triggerActivation); - return DEVICE_OK; -} - -int BaslerCamera::GetTriggerMode(const char* triggerSelector, bool& triggerMode) { - SelectTrigger(triggerSelector); - std::string s = NodeToString("TriggerMode"); - triggerMode = strcmp("On", s.c_str()) == 0; - return DEVICE_OK; -} - -int BaslerCamera::GetTriggerSource(const char* triggerSelector, char* triggerSource) { - SelectTrigger(triggerSelector); - std::string s = NodeToString("TriggerSource"); - if (s.rfind("Line0", 0) == 0) { - strcpy(triggerSource, MM::TriggerIOLine0); - } else if (s.rfind("Line1", 0) == 0) { - strcpy(triggerSource, MM::TriggerIOLine1); - } else if (s.rfind("Line2", 0) == 0) { - strcpy(triggerSource, MM::TriggerIOLine2); - } else if (s.rfind("Line3", 0) == 0) { - strcpy(triggerSource, MM::TriggerIOLine2); - } else { - strcpy(triggerSource, MM::TriggerSourceSoftware); - } - return DEVICE_OK; -} - -int BaslerCamera::GetTriggerDelay(const char* triggerSelector, int& triggerDelay) { - SelectTrigger(triggerSelector); - triggerDelay = CFloatPtr(nodeMap_->GetNode("TriggerDelay"))->GetValue(); - return DEVICE_OK; -} - -int BaslerCamera::GetTriggerActivation(const char* triggerSelector, char* triggerActivation) { - // Select the trigger to get the values of - SelectTrigger(triggerSelector); - std::string s = NodeToString("TriggerActivation"); - strcpy(triggerActivation, s.c_str()); - return DEVICE_OK; -} - -// NA for current testing camera -// bool BaslerCamera::HasExposureMode(const char* exposureMode) { -// -// } -// -// int BaslerCamera::SetExposureMode(const char* exposureMode) { -// -// } -// -// int BaslerCamera::GetExposureMode(char* exposureMode) { -// -// } - -int BaslerCamera::SetBurstFrameCount(unsigned count) { - CIntegerParameter(nodeMap_, "AcquisitionBurstFrameCount").SetValue(count); - return DEVICE_OK; -} - - - -unsigned BaslerCamera::GetBurstFrameCount() const { - return CIntegerParameter(nodeMap_, "AcquisitionBurstFrameCount").GetValue(); -} - - -int BaslerCamera::TriggerSoftware(const char* triggerSelector){ - SelectTrigger(triggerSelector); +int BaslerCamera::TriggerSoftware(){ // Send the trigger camera_->ExecuteSoftwareTrigger(); return DEVICE_OK; @@ -1082,67 +944,6 @@ int BaslerCamera::AcquisitionAbort() return AcquisitionStop(); } -int BaslerCamera::GetAcquisitionStatus(const char* statusSelector, bool& status) { - if (strcmp(statusSelector, MM::CameraStatusAcquisitionActive) == 0) { - CEnumParameter(nodeMap_, "AcquisitionStatusSelector").SetValue("AcquisitionActive"); - } else if (strcmp(statusSelector, MM::CameraStatusExposureActive) == 0) { - CEnumParameter(nodeMap_, "AcquisitionStatusSelector").SetValue("ExposureActive"); - } else if (strcmp(statusSelector, MM::CameraStatusExposureTriggerWait) == 0) { - CEnumParameter(nodeMap_, "AcquisitionStatusSelector").SetValue("ExposureTriggerWait"); - } else if (strcmp(statusSelector, MM::CameraStatusFrameBurstTriggerActive) == 0) { - CEnumParameter(nodeMap_, "AcquisitionStatusSelector").SetValue("FrameBurstActive"); - } else if (strcmp(statusSelector, MM::CameraStatusFrameBurstTriggerWait) == 0) { - CEnumParameter(nodeMap_, "AcquisitionStatusSelector").SetValue("FrameBurstTriggerWait"); - } else if (strcmp(statusSelector, MM::CameraStatusFrameTriggerWait) == 0) { - CEnumParameter(nodeMap_, "AcquisitionStatusSelector").SetValue("FrameTriggerWait"); - } else { - return DEVICE_ERR; //TODO more descriptive error - } - - status = CBooleanParameter(nodeMap_, "AcquisitionStatus").GetValue(); - return DEVICE_OK; -} - -int BaslerCamera::SetIOLineInverted(const char* lineSelector, bool invert) { - CEnumParameter(nodeMap_, "LineSelector").SetValue(lineSelector); - CBooleanParameter(nodeMap_, "LineInverter").SetValue(invert); - return DEVICE_OK; -} - -int BaslerCamera::SetLineAsOutput(const char* lineSelector, bool output) { - CEnumParameter(nodeMap_, "LineSelector").SetValue(lineSelector); - CEnumParameter(nodeMap_, "LineMode").SetValue(output ? "Output" : "Input"); - return DEVICE_OK; -} - - -int BaslerCamera::SetOutputLineSource(const char* lineSelector, const char* source) { - // TODO check it lineSelector is valid once convered to property - CEnumParameter(nodeMap_, "LineSelector").SetValue(lineSelector); - if (strcmp(source, MM::OutputLineSourceAcquisitionActive) == 0) { - CEnumParameter(nodeMap_, "LineSource").SetValue("AcquisitionActive"); - } else if (strcmp(source, MM::OutputLineSourceExposureActive) == 0) { - CEnumParameter(nodeMap_, "LineSource").SetValue("ExposureActive"); - } else if (strcmp(source, MM::OutputLineSourceFrameBurstActive) == 0) { - CEnumParameter(nodeMap_, "LineSource").SetValue("FrameBurstActive"); - } else if (strcmp(source, MM::OutputLineSourceFrameBurstTriggerWait) == 0) { - CEnumParameter(nodeMap_, "LineSource").SetValue("FrameBurstTriggerWait"); - } else if (strcmp(source, MM::OutputLineSourceFrameTriggerWait) == 0) { - CEnumParameter(nodeMap_, "LineSource").SetValue("FrameTriggerWait"); - } else { - return DEVICE_ERR; //TODO more descriptive error - } - return DEVICE_OK; -} - - -int BaslerCamera::GetLineStatus(const char* lineSelector, bool& high) { - CEnumParameter(nodeMap_, "LineSelector").SetValue(lineSelector); - high = CBooleanParameter(nodeMap_, "LineStatus").GetValue(); - return DEVICE_OK; -} - - // //TODO // double BaslerCamera::GetRollingShutterLineOffset() const // { diff --git a/DeviceAdapters/Basler/BaslerPylonCamera.h b/DeviceAdapters/Basler/BaslerPylonCamera.h index 2c90893c8..364bed474 100644 --- a/DeviceAdapters/Basler/BaslerPylonCamera.h +++ b/DeviceAdapters/Basler/BaslerPylonCamera.h @@ -123,32 +123,12 @@ class BaslerCamera : public CNewAPICameraBase { //Genicam Callback void ResultingFramerateCallback(GenApi::INode* pNode); + ///////////////////////////////////// ///////// New Camera API /////////// bool IsNewAPIImplemented() { return true; }; - bool HasTrigger(const char* triggerSelector); - - int SetTriggerMode(const char* triggerSelector, bool triggerMode); - int SetTriggerSource(const char* triggerSelector, const char* triggerSource); - int SetTriggerDelay(const char* triggerSelector, int triggerDelay); - int SetTriggerActivation(const char* triggerSelector, const char* triggerActivation); - - int GetTriggerMode(const char* triggerSelector, bool& triggerMode); - int GetTriggerSource(const char* triggerSelector, char* triggerSource); - int GetTriggerDelay(const char* triggerSelector, int& triggerDelay); - int GetTriggerActivation(const char* triggerSelector, char* triggerActivation); - - //NA for current camera - // virtual bool HasExposureMode(const char* exposureMode); - // virtual int SetExposureMode(const char* exposureMode); - // virtual int GetExposureMode(char* exposureMode); - - // TODO probably want to replace these with properties - virtual int SetBurstFrameCount(unsigned count); - virtual unsigned GetBurstFrameCount() const; - - int TriggerSoftware(const char* triggerSelector); + int TriggerSoftware(); int AcquisitionArm(int frameCount); int AcquisitionArm(); @@ -157,19 +137,16 @@ class BaslerCamera : public CNewAPICameraBase { int AcquisitionStop(); int AcquisitionAbort(); - int GetAcquisitionStatus(const char* statusSelector, bool& status); - - int SetIOLineInverted(const char* lineSelector, bool invert); - int SetLineAsOutput(const char* lineSelector, bool output); - int SetOutputLineSource(const char* lineSelector, const char* source); - int GetLineStatus(const char* lineSelector, bool& high); - // double GetRollingShutterLineOffset() const; // int SetRollingShutterLineOffset(double offset_us); // unsigned GetRollingShutterActiveLines() const; // int SetRollingShutterActiveLines(unsigned numLines); + //////////////////////// + ///// End new Camera API + //////////////////////// + // Convenience functions std::string NodeToString(const char* str) const; int SelectTrigger(const char* triggerSelector); diff --git a/MMDevice/DeviceBase.h b/MMDevice/DeviceBase.h index f152bb7a5..928f5c3ce 100644 --- a/MMDevice/DeviceBase.h +++ b/MMDevice/DeviceBase.h @@ -132,6 +132,54 @@ class CDeviceBase : public T MM::g_TestWithValuesStandardProperty.requiredValues); } + // Camera trigger API standard properties + int CreateTriggerSelectorStandardProperty(const char* value, MM::ActionFunctor* pAct = 0) { + return CreateStandardProperty(value, pAct); + } + + int CreateTriggerModeStandardProperty(const char* value, MM::ActionFunctor* pAct = 0) { + return CreateStandardProperty(value, pAct); + } + + int CreateTriggerSourceStandardProperty(const char* value, MM::ActionFunctor* pAct = 0) { + return CreateStandardProperty(value, pAct); + } + + int CreateTriggerActivationStandardProperty(const char* value, MM::ActionFunctor* pAct = 0) { + return CreateStandardProperty(value, pAct); + } + + int CreateTriggerDelayStandardProperty(const char* value, MM::ActionFunctor* pAct = 0) { + return CreateStandardProperty(value, pAct); + } + + int CreateExposureModeStandardProperty(const char* value, MM::ActionFunctor* pAct = 0) { + return CreateStandardProperty(value, pAct); + } + + int CreateBurstFrameCountStandardProperty(const char* value, MM::ActionFunctor* pAct = 0) { + return CreateStandardProperty(value, pAct); + } + + int CreateLineSelectorStandardProperty(const char* value, MM::ActionFunctor* pAct = 0) { + return CreateStandardProperty(value, pAct); + } + + int CreateLineInverterStandardProperty(const char* value, MM::ActionFunctor* pAct = 0) { + return CreateStandardProperty(value, pAct); + } + + int CreateLineSourceStandardProperty(const char* value, MM::ActionFunctor* pAct = 0) { + return CreateStandardProperty(value, pAct); + } + + int CreateLineStatusStandardProperty(const char* value, MM::ActionFunctor* pAct = 0) { + // LineStatus has required values that are the same as its allowed values + return CreateStandardProperty(value, pAct, + MM::g_LineStatusProperty.requiredValues); + } + + /** * Assigns description string for a device (for use only by the calling code). */ @@ -1649,34 +1697,7 @@ class CNewAPICameraBase : public CCameraBase virtual bool isNewAPIImplemented() {return true;}; - virtual bool hasTrigger(const char* cameraLabel, const char* triggerSelector) {return false;}; - - virtual bool HasTrigger(const char* triggerSelector) { return false; }; - virtual int SetTriggerMode(const char* triggerSelector, bool triggerMode) { return DEVICE_NOT_YET_IMPLEMENTED; }; - virtual int SetTriggerSource(const char* triggerSelector, const char* triggerSource) { return DEVICE_NOT_YET_IMPLEMENTED; }; - virtual int SetTriggerDelay(const char* triggerSelector, int triggerDelay) { return DEVICE_NOT_YET_IMPLEMENTED; }; - virtual int SetTriggerActivation(const char* triggerSelector, const char* triggerActivation) { return DEVICE_NOT_YET_IMPLEMENTED; }; - - virtual int GetTriggerMode(const char* triggerSelector, bool& triggerMode) { return DEVICE_NOT_YET_IMPLEMENTED; }; - virtual int GetTriggerSource(const char* triggerSelector, char* triggerSource) { return DEVICE_NOT_YET_IMPLEMENTED; }; - virtual int GetTriggerDelay(const char* triggerSelector, int& triggerDelay) { return DEVICE_NOT_YET_IMPLEMENTED; }; - virtual int GetTriggerActivation(const char* triggerSelector, char* triggerActivation) { return DEVICE_NOT_YET_IMPLEMENTED; }; - - /** - * Every camera must have at least Timed exposure mode, so these implementations default to only that - */ - virtual bool HasExposureMode(const char* exposureMode) { return strcmp(exposureMode, MM::ExposureModeTimed) == 0; }; - virtual int SetExposureMode(const char* exposureMode) { return (strcmp(exposureMode, MM::ExposureModeTimed) == 0) ? - DEVICE_OK : DEVICE_UNSUPPORTED_COMMAND; }; - virtual int GetExposureMode(char* exposureMode) { - strcpy(exposureMode, MM::ExposureModeTimed); - return DEVICE_OK; - }; - - virtual int TriggerSoftware(const char* triggerSelector) {return DEVICE_NOT_YET_IMPLEMENTED;}; - - virtual int SetBurstFrameCount(unsigned count) { return DEVICE_NOT_YET_IMPLEMENTED; }; - virtual unsigned GetBurstFrameCount() const { return 1; }; + virtual int TriggerSoftware() {return DEVICE_NOT_YET_IMPLEMENTED;}; virtual int AcquisitionArm(int frameCount) {return DEVICE_NOT_YET_IMPLEMENTED;}; virtual int AcquisitionArm() { return DEVICE_NOT_YET_IMPLEMENTED; }; @@ -1685,13 +1706,6 @@ class CNewAPICameraBase : public CCameraBase virtual int AcquisitionStop() {return DEVICE_NOT_YET_IMPLEMENTED;}; virtual int AcquisitionAbort() {return DEVICE_NOT_YET_IMPLEMENTED;}; - virtual int GetAcquisitionStatus(const char* statusSelector, bool& status) { return DEVICE_NOT_YET_IMPLEMENTED; }; - - virtual int SetIOLineInverted(const char* lineSelector, bool invert) { return DEVICE_NOT_YET_IMPLEMENTED; }; - virtual int SetLineAsOutput(const char* lineSelector, bool output) { return DEVICE_NOT_YET_IMPLEMENTED; }; - virtual int SetOutputLineSource(const char* lineSelector, const char* source) { return DEVICE_NOT_YET_IMPLEMENTED; }; - virtual int GetLineStatus(const char* lineSelector, bool& high) { return DEVICE_NOT_YET_IMPLEMENTED; }; - virtual double GetRollingShutterLineOffset() const {return 0.0;}; virtual int SetRollingShutterLineOffset(double offset_us) {return DEVICE_NOT_YET_IMPLEMENTED;}; virtual unsigned GetRollingShutterActiveLines() const {return 0;}; diff --git a/MMDevice/MMDevice.h b/MMDevice/MMDevice.h index 9fa0d4458..cf0c0dce8 100644 --- a/MMDevice/MMDevice.h +++ b/MMDevice/MMDevice.h @@ -307,6 +307,273 @@ namespace MM { 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 std::vector triggerSelectorValues = { + g_keyword_TriggerSelectorAcquisitionStart, + g_keyword_TriggerSelectorAcquisitionEnd, + g_keyword_TriggerSelectorAcquisitionActive, + g_keyword_TriggerSelectorFrameBurstStart, + g_keyword_TriggerSelectorFrameBurstEnd, + g_keyword_TriggerSelectorFrameBurstActive, + g_keyword_TriggerSelectorFrameStart, + g_keyword_TriggerSelectorFrameEnd, + g_keyword_TriggerSelectorFrameActive, + g_keyword_TriggerSelectorExposureStart, + g_keyword_TriggerSelectorExposureEnd, + g_keyword_TriggerSelectorExposureActive + }; + static const MM::StandardProperty g_TriggerSelectorProperty{ + "TriggerSelector", // name + String, // type + false, // isReadOnly + false, // isPreInit + triggerSelectorValues, // allowedValues + {}, // no required values for now + PropertyLimitUndefined, // lowerLimit + PropertyLimitUndefined, // upperLimit + true // required + }; + 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 + true // required + }; + 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. + static const std::vector triggerSourceValues = { + g_keyword_TriggerSourceSoftware, + g_keyword_TriggerIOLine0, + g_keyword_TriggerIOLine1, + g_keyword_TriggerIOLine2, + g_keyword_TriggerIOLine3 + }; + static const MM::StandardProperty g_TriggerSourceProperty{ + "TriggerSource", // name + String, // type + false, // isReadOnly + false, // isPreInit + triggerSourceValues, // allowedValues + {}, // TODO: require software trigger? + PropertyLimitUndefined, // lowerLimit + PropertyLimitUndefined, // upperLimit + true // required + }; + 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 + true // required + }; + 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 + true // required + }; + MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_TriggerDelayProperty) + + + // 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 + true // required + }; + MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_ExposureModeProperty) + + + + // 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_BurstFrameCountProperty{ + "BurstFrameCount", // name + Integer, // type + false, // isReadOnly + false, // isPreInit + {}, // allowedValues + {}, // requiredValues + 1, // lowerLimit + PropertyLimitUndefined, // upperLimit + true // required + }; + MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_BurstFrameCountProperty) + + + // 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 std::vector lineSelectorValues = { + g_keyword_TriggerIOLine0, + g_keyword_TriggerIOLine1, + g_keyword_TriggerIOLine2, + g_keyword_TriggerIOLine3 + }; + static const MM::StandardProperty g_LineSelectorProperty{ + "LineSelector", // name + String, // type + false, // isReadOnly + false, // isPreInit + lineSelectorValues, // allowedValues + {}, // requiredValues + PropertyLimitUndefined, // lowerLimit + PropertyLimitUndefined, // upperLimit + true // required + }; + MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_LineSelectorProperty) + + + // 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 + true // required + }; + 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 std::vector lineSourceValues = { + g_keyword_OutputLineSourceOff, + g_keyword_OutputLineSourceAcquisitionTriggerWait, + g_keyword_OutputLineSourceAcquisitionActive, + g_keyword_OutputLineSourceFrameTriggerWait, + g_keyword_OutputLineSourceFrameActive, + g_keyword_OutputLineSourceExposureTriggerWait, + g_keyword_OutputLineSourceExposureActive + }; + static const MM::StandardProperty g_LineSourceProperty{ + "LineSource", // name + String, // type + false, // isReadOnly + false, // isPreInit + lineSourceValues, // allowedValues + {}, // requiredValues + PropertyLimitUndefined, // lowerLimit + PropertyLimitUndefined, // upperLimit + true // required + }; + 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 + true // required + }; + MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_LineStatusProperty) + + // TODO: implement this? + // // Acquisition status property + // static const std::vector acquisitionStatusSelectorValues = { + // g_keyword_CameraStatusAcquisitionTriggerWait, + // g_keyword_CameraStatusFrameBurstTriggerWait, + // g_keyword_CameraStatusFrameTriggerWait, + // g_keyword_CameraStatusExposureTriggerWait, + // g_keyword_CameraStatusAcquisitionActive, + // g_keyword_CameraStatusFrameBurstTriggerActive, + // g_keyword_CameraStatusFrameActive, + // g_keyword_CameraStatusExposureActive, + // g_keyword_CameraStatusAcquisitionTransfer + // }; + /** * Generic device interface. @@ -430,45 +697,14 @@ namespace MM { //// New Camera API //// virtual bool IsNewAPIImplemented() = 0; - ////////////////////////////// - // Triggers - ////////////////////////////// - - //Check which of the possible trigger types are available - virtual bool HasTrigger(const char* triggerSelector) = 0; - - // These should return an error code if the type is not valid - // They are not meant to do any work. - virtual int SetTriggerMode(const char* triggerSelector, bool triggerMode) = 0; - virtual int SetTriggerSource(const char* triggerSelector, const char* triggerSource) = 0; - virtual int SetTriggerDelay(const char* triggerSelector, int triggerDelay) = 0; - virtual int SetTriggerActivation(const char* triggerSelector, const char* triggerActivation) = 0; - virtual int GetTriggerMode(const char* triggerSelector, bool& triggerMode) = 0; - virtual int GetTriggerSource(const char* triggerSelector, char* triggerSource) = 0; - virtual int GetTriggerDelay(const char* triggerSelector, int& triggerDelay) = 0; - virtual int GetTriggerActivation(const char* triggerSelector, char* triggerActivation) = 0; - - virtual bool HasExposureMode(const char* exposureMode) = 0; - virtual int SetExposureMode(const char* exposureMode) = 0; - virtual int GetExposureMode(char* exposureMode) = 0; - - // TODO probably want to replace these with properties - virtual int SetBurstFrameCount(unsigned count) = 0; - virtual unsigned GetBurstFrameCount() const = 0; - - // Send of software of the supplied type - virtual int TriggerSoftware(const char* triggerSelector) = 0; + // Send a software trigger + virtual int TriggerSoftware() = 0; ////////////////////////////// // Acquisitions ////////////////////////////// - - - // Acquisition functions - ////////////////////////////// - // 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 @@ -502,16 +738,9 @@ namespace MM { // the current Frame or waiting on a trigger. If no Acquisition is in progress, the command is ignored. virtual int AcquisitionAbort() = 0; - virtual int GetAcquisitionStatus(const char* statusSelector, bool& status) = 0; - - - virtual int SetIOLineInverted(const char* lineSelector, bool invert) = 0; - virtual int SetLineAsOutput(const char* lineSelector, bool output) = 0; - virtual int SetOutputLineSource(const char* lineSelector, const char* source) = 0; - virtual int GetLineStatus(const char* lineSelector, bool& status) = 0; - - // Rolling shutter/Lightsheet mode + //// Rolling shutter mode for Lightsheet readout + // this is not part of GenICam, but is supported by some scientific cameras virtual double GetRollingShutterLineOffset() const = 0; virtual int SetRollingShutterLineOffset(double offset_us) = 0; diff --git a/MMDevice/MMDeviceConstants.h b/MMDevice/MMDeviceConstants.h index dafee1fb1..d1185672a 100644 --- a/MMDevice/MMDeviceConstants.h +++ b/MMDevice/MMDeviceConstants.h @@ -177,86 +177,89 @@ namespace MM { ////// New Camera API constants // trigger state constants - const char* const TriggerSelectorAcquisitionStart = "AcquisitionStart"; - const char* const TriggerSelectorAcquisitionEnd = "AcquisitionEnd"; - const char* const TriggerSelectorAcquisitionActive = "AcquisitionActive"; - const char* const TriggerSelectorFrameBurstStart = "FrameBurstStart"; - const char* const TriggerSelectorFrameBurstEnd = "FrameBurstEnd"; - const char* const TriggerSelectorFrameBurstActive = "FrameBurstActive"; - const char* const TriggerSelectorFrameStart = "FrameStart"; - const char* const TriggerSelectorFrameEnd = "FrameEnd"; - const char* const TriggerSelectorFrameActive = "FrameActive"; - const char* const TriggerSelectorExposureStart = "ExposureStart"; - const char* const TriggerSelectorExposureEnd = "ExposureEnd"; - const char* const TriggerSelectorExposureActive = "ExposureActive"; - - const char* const TriggerModeOn = "On"; - const char* const TriggerModeOff = "Off"; - - const char* const TriggerIOLine0 = "Line0"; - const char* const TriggerIOLine1 = "Line1"; - const char* const TriggerIOLine2 = "Line2"; - const char* const TriggerIOLine3 = "Line3"; - const char* const TriggerSourceSoftware = "Software"; - - const char* const TriggerActivationAnyEdge = "AnyEdge"; - const char* const TriggerActivationRisingEdge = "RisingEdge"; - const char* const TriggerActivationFallingEdge = "FallingEdge"; - const char* const TriggerActivationLevelLow = "Low"; - const char* const TriggerActivationLevelHigh = "High"; - - const char* const ExposureModeTimed = "Timed"; - const char* const ExposureModeTriggerWidth = "TriggerWidth"; - const char* const ExposureModeTriggerControlled = "TriggerControlled"; - - const char* const CameraStatusAcquisitionTriggerWait = "AcquisitionTriggerWait"; - const char* const CameraStatusFrameBurstTriggerWait = "FrameBurstTriggerWait"; - const char* const CameraStatusFrameTriggerWait = "FrameTriggerWait"; - const char* const CameraStatusExposureTriggerWait = "ExposureTriggerWait"; - const char* const CameraStatusAcquisitionActive = "AcquisitionActive"; - const char* const CameraStatusFrameBurstTriggerActive = "FrameBurstTriggerActive"; - const char* const CameraStatusFrameActive = "FrameActive"; - const char* const CameraStatusExposureActive = "ExposureActive"; - const char* const CameraStatusAcquisitionTransfer = "AcquisitionTransfer"; - - - const char* const OutputLineSourceOff = "Off"; - const char* const OutputLineSourceAcquisitionTriggerWait = "AcquisitionTriggerWait"; - const char* const OutputLineSourceAcquisitionTrigger = "AcquisitionTrigger"; - const char* const OutputLineSourceAcquisitionTriggerMissed = "AcquisitionTriggerMissed"; - const char* const OutputLineSourceAcquisitionActive = "AcquisitionActive"; - - const char* const OutputLineSourceFrameBurstTriggerWait = "FrameBurstTriggerWait"; - const char* const OutputLineSourceFrameBurstTrigger = "FrameBurstTrigger"; - const char* const OutputLineSourceFrameBurstTriggerMissed = "FrameBurstTriggerMissed"; - const char* const OutputLineSourceFrameBurstActive = "FrameBurstTriggerActive"; - - const char* const OutputLineSourceFrameTriggerWait = "FrameTriggerWait"; - const char* const OutputLineSourceFrameTrigger = "FrameTrigger"; - const char* const OutputLineSourceFrameTriggerMissed = "FrameTriggerMissed"; - const char* const OutputLineSourceFrameActive = "FrameActive"; - - const char* const OutputLineSourceExposureTriggerWait = "ExposureTriggerWait"; - const char* const OutputLineSourceExposureTrigger = "ExposureTrigger"; - const char* const OutputLineSourceExposureTriggerMissed = "ExposureTriggerMissed"; - const char* const OutputLineSourceExposureActive = "ExposureActive"; + 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_TriggerIOLine0 = "Line0"; + const char* const g_keyword_TriggerIOLine1 = "Line1"; + const char* const g_keyword_TriggerIOLine2 = "Line2"; + const char* const g_keyword_TriggerIOLine3 = "Line3"; + 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 = "Low"; + const char* const g_keyword_TriggerActivationLevelHigh = "High"; + + 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_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 = "Low"; + const char* const g_keyword_LineStatusHigh = "High"; // Camera Events - const char* const CameraEventAcquisitionTrigger = "CameraEventAcquisitionTrigger"; - const char* const CameraEventAcquisitionStart = "CameraEventAcquisitionStart"; - const char* const CameraEventAcquisitionEnd = "CameraEventAcquisitionEnd"; - const char* const CameraEventAcquisitionTransferStart = "CameraEventAcquisitionTransferStart"; - const char* const CameraEventAcquisitionTransferEnd = "CameraEventAcquisitionTransferEnd"; - const char* const CameraEventAcquisitionError = "CameraEventAcquisitionError"; - const char* const CameraEventFrameTrigger = "CameraEventFrameTrigger"; - const char* const CameraEventFrameStart = "CameraEventFrameStart"; - const char* const CameraEventFrameEnd = "CameraEventFrameEnd"; - const char* const CameraEventFrameBurstStart = "CameraEventFrameBurstStart"; - const char* const CameraEventFrameBurstEnd = "CameraEventFrameBurstEnd"; - const char* const CameraEventFrameTransferStart = "CameraEventFrameTransferStart"; - const char* const CameraEventFrameTransferEnd = "CameraEventFrameTransferEnd"; - const char* const CameraEventExposureStart = "CameraEventExposureStart"; - const char* const CameraEventExposureEnd = "CameraEventExposureEnd"; + const char* const g_keyword_CameraEventAcquisitionTrigger = "CameraEventAcquisitionTrigger"; + const char* const g_keyword_CameraEventAcquisitionStart = "CameraEventAcquisitionStart"; + const char* const g_keyword_CameraEventAcquisitionEnd = "CameraEventAcquisitionEnd"; + const char* const g_keyword_CameraEventAcquisitionTransferStart = "CameraEventAcquisitionTransferStart"; + const char* const g_keyword_CameraEventAcquisitionTransferEnd = "CameraEventAcquisitionTransferEnd"; + const char* const g_keyword_CameraEventAcquisitionError = "CameraEventAcquisitionError"; + const char* const g_keyword_CameraEventFrameTrigger = "CameraEventFrameTrigger"; + const char* const g_keyword_CameraEventFrameStart = "CameraEventFrameStart"; + const char* const g_keyword_CameraEventFrameEnd = "CameraEventFrameEnd"; + const char* const g_keyword_CameraEventFrameBurstStart = "CameraEventFrameBurstStart"; + const char* const g_keyword_CameraEventFrameBurstEnd = "CameraEventFrameBurstEnd"; + const char* const g_keyword_CameraEventFrameTransferStart = "CameraEventFrameTransferStart"; + const char* const g_keyword_CameraEventFrameTransferEnd = "CameraEventFrameTransferEnd"; + const char* const g_keyword_CameraEventExposureStart = "CameraEventExposureStart"; + const char* const g_keyword_CameraEventExposureEnd = "CameraEventExposureEnd"; // configuration file format constants From 979d571a0b7547f928d2272de713c251c23d712b Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Wed, 5 Mar 2025 14:42:36 -0800 Subject: [PATCH 16/50] added mechanism for skipping required standard property --- MMDevice/DeviceBase.h | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/MMDevice/DeviceBase.h b/MMDevice/DeviceBase.h index 94e123041..f5b24d96d 100644 --- a/MMDevice/DeviceBase.h +++ b/MMDevice/DeviceBase.h @@ -39,6 +39,7 @@ #include #include #include +#include // common error messages const char* const g_Msg_ERR = "Unknown error in the device"; @@ -132,6 +133,12 @@ class CDeviceBase : public T MM::g_TestWithValuesStandardProperty.requiredValues); } + // Required standard properties can be skipped by adding methods like this. + // The TestStandardProperty is not required, this is just an example. + // void SkipTestStandardProperty() { + // SkipStandardProperty(); + // } + /** * Assigns description string for a device (for use only by the calling code). */ @@ -600,6 +607,11 @@ class CDeviceBase : public T 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 @@ -610,7 +622,7 @@ class CDeviceBase : public T } } - // All required properties are implemented + // All required properties are implemented or explicitly skipped return true; } @@ -1350,6 +1362,17 @@ class CDeviceBase : public T 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); + } + } + bool PropertyDefined(const char* propName) const { return properties_.Find(propName) != 0; @@ -1392,6 +1415,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 From 79017f4702a98c51b6efa32df058ae6ab4278e4a Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Thu, 6 Mar 2025 08:23:16 -0800 Subject: [PATCH 17/50] remove required flag from standard properties. Default to required and add explicit skipping --- DeviceAdapters/DemoCamera/DemoCamera.cpp | 16 +++---- MMCore/Devices/DeviceInstance.cpp | 2 +- MMDevice/DeviceBase.h | 56 +++++++++++------------ MMDevice/MMDevice.h | 58 ++++++++++++------------ MMDevice/Property.h | 8 ++-- 5 files changed, 67 insertions(+), 73 deletions(-) diff --git a/DeviceAdapters/DemoCamera/DemoCamera.cpp b/DeviceAdapters/DemoCamera/DemoCamera.cpp index c47eb1826..827793408 100644 --- a/DeviceAdapters/DemoCamera/DemoCamera.cpp +++ b/DeviceAdapters/DemoCamera/DemoCamera.cpp @@ -308,14 +308,14 @@ int CDemoCamera::Initialize() else LogMessage(NoHubError); - // 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); + // 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 diff --git a/MMCore/Devices/DeviceInstance.cpp b/MMCore/Devices/DeviceInstance.cpp index bc21bda20..0c2602297 100644 --- a/MMCore/Devices/DeviceInstance.cpp +++ b/MMCore/Devices/DeviceInstance.cpp @@ -379,7 +379,7 @@ DeviceInstance::Initialize() // Check for that all required standard properties implemented char failedProperty[MM::MaxStrLength]; - if (!pImpl_->ImplementsRequiredStandardProperties(failedProperty)) { + if (!pImpl_->ImplementsOrSkipsStandardProperties(failedProperty)) { // shutdown the device Shutdown(); ThrowError("Device " + GetLabel() + diff --git a/MMDevice/DeviceBase.h b/MMDevice/DeviceBase.h index f5b24d96d..9cc71e2b0 100644 --- a/MMDevice/DeviceBase.h +++ b/MMDevice/DeviceBase.h @@ -121,20 +121,21 @@ class CDeviceBase : public T //// 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 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); - } + // 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); + // } - // Required standard properties can be skipped by adding methods like this. - // The TestStandardProperty is not required, this is just an example. + // Every standard property must either be created or explicitly skipped using + // a method like this // void SkipTestStandardProperty() { // SkipStandardProperty(); // } @@ -591,7 +592,7 @@ class CDeviceBase : public T return true; } - bool ImplementsRequiredStandardProperties(char* failedProperty) const { + bool ImplementsOrSkipsStandardProperties(char* failedProperty) const { // Get the device type MM::DeviceType deviceType = this->GetType(); @@ -601,23 +602,20 @@ class CDeviceBase : public T // Iterate through all properties for this device type const auto& properties = it->second; for (const auto& prop : properties) { - // Check if this property is required - if (prop.required) { - // 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; - } + // 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; } } } diff --git a/MMDevice/MMDevice.h b/MMDevice/MMDevice.h index 3d254af14..05dde50d0 100644 --- a/MMDevice/MMDevice.h +++ b/MMDevice/MMDevice.h @@ -277,35 +277,33 @@ namespace MM { // 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 - false // required - }; - - 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 - false // required - }; - - MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_TestWithValuesStandardProperty) + // 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) /** @@ -329,7 +327,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 ImplementsRequiredStandardProperties(char* failedProperty) const = 0; + virtual bool ImplementsOrSkipsStandardProperties(char* failedProperty) const = 0; /** * Sequences can be used for fast acquisitions, synchronized by TTLs rather than * computer commands. diff --git a/MMDevice/Property.h b/MMDevice/Property.h index d8a9443b4..ba65e69bd 100644 --- a/MMDevice/Property.h +++ b/MMDevice/Property.h @@ -53,19 +53,17 @@ struct StandardProperty { allowedValues == other.allowedValues && requiredValues == other.requiredValues && lowerLimit == other.lowerLimit && - upperLimit == other.upperLimit && - required == other.required; + 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; // if empty, no restrictions - std::vector requiredValues; // if empty, no restrictions + 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) - bool required = false; // Whether to throw a runtime error if the property is not found on init }; From 5e6ab94e5e3fe167dcc83fbaad7e8a53e2112c63 Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Thu, 6 Mar 2025 10:31:37 -0800 Subject: [PATCH 18/50] split out old and new camera api paths and clean up basler --- DeviceAdapters/Basler/BaslerPylonCamera.cpp | 169 ++-------------- DeviceAdapters/Basler/BaslerPylonCamera.h | 79 +++----- MMDevice/DeviceBase.h | 206 ++++++++++++++------ MMDevice/MMDevice.h | 49 +++-- 4 files changed, 225 insertions(+), 278 deletions(-) diff --git a/DeviceAdapters/Basler/BaslerPylonCamera.cpp b/DeviceAdapters/Basler/BaslerPylonCamera.cpp index 250e4a968..d02100a4b 100644 --- a/DeviceAdapters/Basler/BaslerPylonCamera.cpp +++ b/DeviceAdapters/Basler/BaslerPylonCamera.cpp @@ -159,7 +159,6 @@ BaslerCamera::BaslerCamera() : temperatureState_("Undefined"), reverseX_("0"), reverseY_("0"), - imgBuffer_(NULL), Buffer4ContinuesShot(NULL), colorCamera_(true), pixelType_("Undefined"), @@ -214,10 +213,7 @@ BaslerCamera::BaslerCamera() : BaslerCamera::~BaslerCamera() { - if (imgBuffer_ != NULL) - { - free(imgBuffer_); - } + if (Buffer4ContinuesShot != NULL) if (Buffer4ContinuesShot != NULL) { free(Buffer4ContinuesShot); @@ -854,10 +850,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) @@ -919,6 +911,14 @@ int BaslerCamera::AcquisitionArm() int BaslerCamera::AcquisitionStart() { + // 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_ == 0) { // acquire until told to stop @@ -936,11 +936,17 @@ int BaslerCamera::AcquisitionStop() // CCommandParameter(nodeMap_, "AcquisitionStop").Execute(); camera_->StopGrabbing(); camera_->DeregisterImageEventHandler(ImageHandler_); + // 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(); } @@ -1024,90 +1030,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"); @@ -1168,14 +1090,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 @@ -1298,64 +1212,11 @@ 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); - // TODO: this was commented in my previous implementation of new camera API. Why? - // Maybe it doesn't matter because this will be deleted - // 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 /////////////////////////////////////////////////////////////////////////////// diff --git a/DeviceAdapters/Basler/BaslerPylonCamera.h b/DeviceAdapters/Basler/BaslerPylonCamera.h index 364bed474..18b633ff8 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 @@ -77,57 +76,28 @@ class BaslerCamera : public CNewAPICameraBase { 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(); - - 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 /////////// - bool IsNewAPIImplemented() { return true; }; - int TriggerSoftware(); int AcquisitionArm(int frameCount); @@ -137,20 +107,10 @@ class BaslerCamera : public CNewAPICameraBase { int AcquisitionStop(); int AcquisitionAbort(); - - // double GetRollingShutterLineOffset() const; - // int SetRollingShutterLineOffset(double offset_us); - // unsigned GetRollingShutterActiveLines() const; - // int SetRollingShutterActiveLines(unsigned numLines); - //////////////////////// ///// End new Camera API //////////////////////// - // Convenience functions - std::string NodeToString(const char* str) const; - int SelectTrigger(const char* triggerSelector); - // action interface // ---------------- @@ -179,6 +139,30 @@ class BaslerCamera : public CNewAPICameraBase { 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 SelectTrigger(const char* triggerSelector); + + + 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; + CircularBufferInserter *ImageHandler_; + std::string EnumToString(EDeviceAccessiblityInfo DeviceAccessiblityInfo); + void* Buffer4ContinuesShot; + private: CBaslerUniversalInstantCamera *camera_; @@ -209,16 +193,17 @@ class BaslerCamera : public CNewAPICameraBase { std::string temperatureState_; - void* imgBuffer_; - long imgBufferSize_; - ImgBuffer img_; INodeMap* nodeMap_; bool initialized_; + //MM::MMTime startTime_; - void ResizeSnapBuffer(); + + + + }; diff --git a/MMDevice/DeviceBase.h b/MMDevice/DeviceBase.h index 8da70be73..b012957fa 100644 --- a/MMDevice/DeviceBase.h +++ b/MMDevice/DeviceBase.h @@ -231,6 +231,22 @@ class CDeviceBase : public T SkipStandardProperty(); } + 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). */ @@ -1528,12 +1544,12 @@ 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: @@ -1544,9 +1560,10 @@ class CCameraBase : public CDeviceBase using CDeviceBase::SetProperty; using CDeviceBase::LogMessage; - CCameraBase() + 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"); @@ -1558,32 +1575,8 @@ 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); - } - virtual ~CCameraBase() - { - - } - - 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; - virtual bool Busy() = 0; - - /** - * Continuous sequence acquisition. - * Default to sequence acquisition with a high number of images - */ - virtual int StartSequenceAcquisition(double interval) = 0; - - /** - * Stop and wait for the thread finished - */ - virtual int StopSequenceAcquisition() = 0; - /** * Returns binnings factor. Used to calculate current pixelsize * Not appropriately named. Implemented in DeviceBase.h @@ -1635,6 +1628,8 @@ class CCameraBase : public CDeviceBase return 0; } + virtual const unsigned char* GetImageBuffer() = 0; + virtual const unsigned int* GetImageBufferAsRGB32() { return 0; @@ -1649,14 +1644,11 @@ class CCameraBase : public CDeviceBase data.copy(serializedMetadata, data.size(), 0); } - // temporary debug methods - virtual int PrepareSequenceAcqusition() = 0; - - /** - * Default implementation. - */ - virtual int StartSequenceAcquisition(long numImages, double interval_ms, - bool stopOnOverflow) = 0; + virtual int IsExposureSequenceable(bool& isSequenceable) const + { + isSequenceable = false; + return DEVICE_OK; + } virtual int GetExposureSequenceMaxLength(long& /*nrEvents*/) const { @@ -1688,8 +1680,6 @@ class CCameraBase : public CDeviceBase return DEVICE_UNSUPPORTED_COMMAND; } - virtual bool IsCapturing() = 0; - virtual void AddTag(const char* key, const char* deviceLabel, const char* value) { metadata_.PutTag(key, deviceLabel, value); @@ -1750,34 +1740,138 @@ class CCameraBase : public CDeviceBase }; +/** +* 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(); + SkipExposureModeStandardProperty(); + SkipBurstFrameCountStandardProperty(); + SkipLineSelectorStandardProperty(); + SkipLineInverterStandardProperty(); + SkipLineSourceStandardProperty(); + SkipLineStatusStandardProperty(); + 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 void SetExposure(double exp_ms) = 0; + virtual double GetExposure() const = 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 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 AcquisitionArm() 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 sty +// TODO: dissallow old style camera devices from using this class template -class CNewAPICameraBase : public CCameraBase +class CNewAPICameraBase : public CAllCamerasBase { public: - // TODO: need to call superclass constructor pending whether those - // transpose properties are needed + // 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 void SetExposure(double exp_ms) = 0; + virtual double GetExposure() const = 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;} - virtual bool isNewAPIImplemented() {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 int TriggerSoftware() {return DEVICE_NOT_YET_IMPLEMENTED;}; - virtual int AcquisitionArm(int frameCount) {return DEVICE_NOT_YET_IMPLEMENTED;}; - virtual int AcquisitionArm() { return DEVICE_NOT_YET_IMPLEMENTED; }; + // New camera API: required + virtual int TriggerSoftware() = 0; - virtual int AcquisitionStart() {return DEVICE_NOT_YET_IMPLEMENTED;}; - virtual int AcquisitionStop() {return DEVICE_NOT_YET_IMPLEMENTED;}; - virtual int AcquisitionAbort() {return DEVICE_NOT_YET_IMPLEMENTED;}; + virtual int AcquisitionArm(int frameCount) = 0; + virtual int AcquisitionArm() = 0; - virtual double GetRollingShutterLineOffset() const {return 0.0;}; - virtual int SetRollingShutterLineOffset(double offset_us) {return DEVICE_NOT_YET_IMPLEMENTED;}; - virtual unsigned GetRollingShutterActiveLines() const {return 0;}; - virtual int SetRollingShutterActiveLines(unsigned numLines) {return DEVICE_NOT_YET_IMPLEMENTED;}; + virtual int AcquisitionStart() = 0; + virtual int AcquisitionStop() = 0; + virtual int AcquisitionAbort() = 0; }; @@ -1791,11 +1885,11 @@ class CNewAPICameraBase : public CCameraBase * from the derived class. */ template -class CLegacyCameraBase : public CCameraBase +class CLegacyCameraBase : public COldAPICameraBase { public: - CLegacyCameraBase() : CCameraBase(), busy_(false), stopWhenCBOverflows_(false), thd_(0) + CLegacyCameraBase() : COldAPICameraBase(), busy_(false), stopWhenCBOverflows_(false), thd_(0) { // TODO: does this belong here or in the superclass? // create and initialize common transpose properties diff --git a/MMDevice/MMDevice.h b/MMDevice/MMDevice.h index 4588522a7..9a9d8974f 100644 --- a/MMDevice/MMDevice.h +++ b/MMDevice/MMDevice.h @@ -335,7 +335,6 @@ namespace MM { {}, // no required values for now PropertyLimitUndefined, // lowerLimit PropertyLimitUndefined, // upperLimit - true // required }; MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_TriggerSelectorProperty) @@ -354,7 +353,6 @@ namespace MM { {}, // no required values for now PropertyLimitUndefined, // lowerLimit PropertyLimitUndefined, // upperLimit - true // required }; MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_TriggerModeProperty) @@ -377,7 +375,6 @@ namespace MM { {}, // TODO: require software trigger? PropertyLimitUndefined, // lowerLimit PropertyLimitUndefined, // upperLimit - true // required }; MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_TriggerSourceProperty) @@ -399,7 +396,6 @@ namespace MM { {}, // requiredValues PropertyLimitUndefined, // lowerLimit PropertyLimitUndefined, // upperLimit - true // required }; MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_TriggerActivationProperty) @@ -414,7 +410,6 @@ namespace MM { {}, // requiredValues 0, // lowerLimit PropertyLimitUndefined, // upperLimit - true // required }; MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_TriggerDelayProperty) @@ -446,7 +441,6 @@ namespace MM { {}, // requiredValues PropertyLimitUndefined, // lowerLimit PropertyLimitUndefined, // upperLimit - true // required }; MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_ExposureModeProperty) @@ -466,7 +460,6 @@ namespace MM { {}, // requiredValues 1, // lowerLimit PropertyLimitUndefined, // upperLimit - true // required }; MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_BurstFrameCountProperty) @@ -489,7 +482,6 @@ namespace MM { {}, // requiredValues PropertyLimitUndefined, // lowerLimit PropertyLimitUndefined, // upperLimit - true // required }; MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_LineSelectorProperty) @@ -507,7 +499,6 @@ namespace MM { {}, // requiredValues PropertyLimitUndefined, // lowerLimit PropertyLimitUndefined, // upperLimit - true // required }; MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_LineInverterProperty) @@ -532,7 +523,6 @@ namespace MM { {}, // requiredValues PropertyLimitUndefined, // lowerLimit PropertyLimitUndefined, // upperLimit - true // required }; MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_LineSourceProperty) @@ -554,7 +544,6 @@ namespace MM { lineStatusValues, // requiredValues PropertyLimitUndefined, // lowerLimit PropertyLimitUndefined, // upperLimit - true // required }; MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_LineStatusProperty) @@ -573,6 +562,34 @@ namespace MM { // }; + + //// Standard properties for rolling shutter lightsheet readout cameras + //// These are not part of GenICam, but are supported by some scientific cameras + + 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. */ @@ -718,8 +735,6 @@ namespace MM { virtual int AcquisitionArm(int frameCount) = 0; virtual int AcquisitionArm() = 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. @@ -737,14 +752,6 @@ namespace MM { virtual int AcquisitionAbort() = 0; - //// Rolling shutter mode for Lightsheet readout - // this is not part of GenICam, but is supported by some scientific cameras - virtual double GetRollingShutterLineOffset() const = 0; - virtual int SetRollingShutterLineOffset(double offset_us) = 0; - - virtual unsigned GetRollingShutterActiveLines() const = 0; - virtual int SetRollingShutterActiveLines(unsigned numLines) = 0; - /////////////////////////////////////////////////////////////// ///// End new camera API //////////////////////// ////////////////////////////////////////////////////////////// From a39a41c6eab445ebbd5f807357193c60c4c6e383 Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Thu, 6 Mar 2025 10:33:51 -0800 Subject: [PATCH 19/50] remove from skipped registry upon creation --- MMDevice/DeviceBase.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/MMDevice/DeviceBase.h b/MMDevice/DeviceBase.h index 9cc71e2b0..519df2852 100644 --- a/MMDevice/DeviceBase.h +++ b/MMDevice/DeviceBase.h @@ -1345,6 +1345,9 @@ class CDeviceBase : public T return ret; } + // Remove from skipped properties if it was previously marked as skipped + skippedStandardProperties_.erase(fullName); + return DEVICE_OK; } From aa32a0c67576d3dced587a521c026f38a6b70751 Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Thu, 6 Mar 2025 11:32:56 -0800 Subject: [PATCH 20/50] Basler: standard prop action handlers --- DeviceAdapters/Basler/BaslerPylonCamera.cpp | 418 ++++++++++++++++++-- DeviceAdapters/Basler/BaslerPylonCamera.h | 18 +- 2 files changed, 405 insertions(+), 31 deletions(-) diff --git a/DeviceAdapters/Basler/BaslerPylonCamera.cpp b/DeviceAdapters/Basler/BaslerPylonCamera.cpp index d02100a4b..6906bb470 100644 --- a/DeviceAdapters/Basler/BaslerPylonCamera.cpp +++ b/DeviceAdapters/Basler/BaslerPylonCamera.cpp @@ -303,6 +303,138 @@ int BaslerCamera::Initialize() } + + //// Create standard properties + + int code; + CPropertyAction* action; + + + // TriggerSelector + if (IsAvailable(camera_->TriggerSelector)) { + CPropertyAction* pAct = new CPropertyAction(this, &BaslerCamera::OnTriggerSelector); + std::string currentValue = camera_->TriggerSelector.ToString(); + action = new CPropertyAction(this, &BaslerCamera::OnTriggerSelector); + code = CreateTriggerSelectorStandardProperty(currentValue.c_str(), action); + assert(code == DEVICE_OK); + } else { + SkipTriggerSelectorStandardProperty(); + } + + // TriggerMode + if (IsAvailable(camera_->TriggerMode)) { + CPropertyAction* pAct = new CPropertyAction(this, &BaslerCamera::OnTriggerMode); + std::string currentValue = camera_->TriggerMode.ToString(); + action = new CPropertyAction(this, &BaslerCamera::OnTriggerMode); + code = CreateTriggerModeStandardProperty(currentValue.c_str(), action); + assert(code == DEVICE_OK); + } else { + SkipTriggerModeStandardProperty(); + } + + // TriggerSource + if (IsAvailable(camera_->TriggerSource)) { + CPropertyAction* pAct = new CPropertyAction(this, &BaslerCamera::OnTriggerSource); + std::string currentValue = camera_->TriggerSource.ToString(); + action = new CPropertyAction(this, &BaslerCamera::OnTriggerSource); + code = CreateTriggerSourceStandardProperty(currentValue.c_str(), action); + assert(code == DEVICE_OK); + } else { + SkipTriggerSourceStandardProperty(); + } + + // TriggerActivation + if (IsAvailable(camera_->TriggerActivation)) { + CPropertyAction* pAct = new CPropertyAction(this, &BaslerCamera::OnTriggerActivation); + std::string currentValue = camera_->TriggerActivation.ToString(); + action = new CPropertyAction(this, &BaslerCamera::OnTriggerActivation); + code = CreateTriggerActivationStandardProperty(currentValue.c_str(), action); + assert(code == DEVICE_OK); + } else { + SkipTriggerActivationStandardProperty(); + } + + // TriggerDelay + if (IsAvailable(camera_->TriggerDelay)) { + CPropertyAction* pAct = new CPropertyAction(this, &BaslerCamera::OnTriggerDelay); + double currentValue = camera_->TriggerDelay.GetValue(); + std::string strValue = CDeviceUtils::ConvertToString(currentValue); + action = new CPropertyAction(this, &BaslerCamera::OnTriggerDelay); + code = CreateTriggerDelayStandardProperty(strValue.c_str(), action); + assert(code == DEVICE_OK); + } else { + SkipTriggerDelayStandardProperty(); + } + + // ExposureMode + if (IsAvailable(camera_->ExposureMode)) { + CPropertyAction* pAct = new CPropertyAction(this, &BaslerCamera::OnExposureMode); + std::string currentValue = camera_->ExposureMode.ToString(); + action = new CPropertyAction(this, &BaslerCamera::OnExposureMode); + code = CreateExposureModeStandardProperty(currentValue.c_str(), action); + assert(code == DEVICE_OK); + } else { + SkipExposureModeStandardProperty(); + } + + // BurstFrameCount + // if (IsAvailable(camera_->AcquisitionBurstFrameCount)) { + // CPropertyAction* pAct = new CPropertyAction(this, &BaslerCamera::OnBurstFrameCount); + // ret = CreateBurstFrameCountStandardProperty(); + // assert(ret == DEVICE_OK); + // } else { + // Haven't made a handler for this because unable to test + SkipBurstFrameCountStandardProperty(); + // } + + // LineSelector + if (IsAvailable(camera_->LineSelector)) { + CPropertyAction* pAct = new CPropertyAction(this, &BaslerCamera::OnLineSelector); + std::string currentValue = camera_->LineSelector.ToString(); + action = new CPropertyAction(this, &BaslerCamera::OnLineSelector); + code = CreateLineSelectorStandardProperty(currentValue.c_str(), action); + assert(code == DEVICE_OK); + } else { + SkipLineSelectorStandardProperty(); + } + + // LineInverter + if (IsAvailable(camera_->LineInverter)) { + CPropertyAction* pAct = new CPropertyAction(this, &BaslerCamera::OnLineInverter); + std::string currentValue = camera_->LineInverter.ToString(); + action = new CPropertyAction(this, &BaslerCamera::OnLineInverter); + code = CreateLineInverterStandardProperty(currentValue.c_str(), action); + assert(code == DEVICE_OK); + } else { + SkipLineInverterStandardProperty(); + } + + // LineSource + if (IsAvailable(camera_->LineSource)) { + CPropertyAction* pAct = new CPropertyAction(this, &BaslerCamera::OnLineSource); + std::string currentValue = camera_->LineSource.ToString(); + action = new CPropertyAction(this, &BaslerCamera::OnLineSource); + code = CreateLineSourceStandardProperty(currentValue.c_str(), action); + assert(code == DEVICE_OK); + } else { + SkipLineSourceStandardProperty(); + } + + // LineStatus + if (IsAvailable(camera_->LineStatus)) { + CPropertyAction* pAct = new CPropertyAction(this, &BaslerCamera::OnLineStatus); + std::string currentValue = camera_->LineStatus.ToString(); + action = new CPropertyAction(this, &BaslerCamera::OnLineStatus); + code = CreateLineStatusStandardProperty(currentValue.c_str(), action); + assert(code == DEVICE_OK); + } else { + SkipLineStatusStandardProperty(); + } + + // Unclear if any basler cameras support these properties + SkipRollingShutterLineOffsetStandardProperty(); + SkipRollingShutterActiveLinesStandardProperty(); + stringstream msg; msg << "using camera " << camera_->GetDeviceInfo().GetFriendlyName(); AddToLog(msg.str()); @@ -1221,6 +1353,52 @@ bool BaslerCamera::IsCapturing() // 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()); + } + 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_; @@ -1242,6 +1420,217 @@ int BaslerCamera::OnTriggerSource(MM::PropertyBase* pProp, MM::ActionType eAct) 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()); + return DEVICE_ERR; + } + return DEVICE_OK; +} + +int BaslerCamera::OnTriggerActivation(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + try + { + string TriggerActivation_; + CEnumerationPtr TriggerActivation(nodeMap_->GetNode("TriggerActivation")); + if (TriggerActivation != NULL && IsAvailable(TriggerActivation)) + { + if (eAct == MM::AfterSet) { + pProp->Get(TriggerActivation_); + TriggerActivation->FromString(TriggerActivation_.c_str()); + pProp->Set(TriggerActivation->ToString().c_str()); + } + else if (eAct == MM::BeforeGet) { + pProp->Set(TriggerActivation->ToString().c_str()); + } + } + } + catch (const GenericException & e) + { + // Error handling. + AddToLog(e.GetDescription()); + return DEVICE_ERR; + } + return DEVICE_OK; +} + +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::OnExposureMode(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + try + { + string ExposureMode_; + CEnumerationPtr ExposureMode(nodeMap_->GetNode("ExposureMode")); + if (ExposureMode != NULL && IsAvailable(ExposureMode)) + { + if (eAct == MM::AfterSet) { + pProp->Get(ExposureMode_); + ExposureMode->FromString(ExposureMode_.c_str()); + pProp->Set(ExposureMode->ToString().c_str()); + } + else if (eAct == MM::BeforeGet) { + pProp->Set(ExposureMode->ToString().c_str()); + } + } + } + catch (const GenericException & e) + { + // Error handling. + AddToLog(e.GetDescription()); + 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()); + } + 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::OnLineSource(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + try + { + string LineSource_; + CEnumerationPtr LineSource(nodeMap_->GetNode("LineSource")); + if (LineSource != NULL && IsAvailable(LineSource)) + { + if (eAct == MM::AfterSet) { + pProp->Get(LineSource_); + LineSource->FromString(LineSource_.c_str()); + pProp->Set(LineSource->ToString().c_str()); + } + else if (eAct == MM::BeforeGet) { + pProp->Set(LineSource->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; +} + + +//// Non-standard property handlers + int BaslerCamera::OnBinningMode(MM::PropertyBase* pProp, MM::ActionType eAct) { CEnumerationPtr BinningModeHorizontal(nodeMap_->GetNode("BinningModeHorizontal")); @@ -1605,35 +1994,6 @@ int BaslerCamera::OnSensorReadoutMode(MM::PropertyBase* pProp, MM::ActionType eA 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; -} - int BaslerCamera::OnTemperature(MM::PropertyBase* pProp, MM::ActionType eAct) { if (eAct == MM::BeforeGet) { diff --git a/DeviceAdapters/Basler/BaslerPylonCamera.h b/DeviceAdapters/Basler/BaslerPylonCamera.h index 18b633ff8..15c2c0624 100644 --- a/DeviceAdapters/Basler/BaslerPylonCamera.h +++ b/DeviceAdapters/Basler/BaslerPylonCamera.h @@ -114,6 +114,22 @@ class BaslerCamera : public CNewAPICameraBase { // 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 OnLineInverter(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnLineSource(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnLineStatus(MM::PropertyBase* pProp, MM::ActionType eAct); + + // other + // int OnTriggerMode(MM::PropertyBase* pProp, MM::ActionType eAct); + // int OnTriggerSource(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnAcqFramerate(MM::PropertyBase* pProp, MM::ActionType eAct); int OnAcqFramerateEnable(MM::PropertyBase* pProp, MM::ActionType eAct); int OnAutoExpore(MM::PropertyBase* pProp, MM::ActionType eAct); @@ -135,8 +151,6 @@ class BaslerCamera : public CNewAPICameraBase { 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); From 758e20b9bf9bf86d8615cf8c373ed90a62a78826 Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Thu, 6 Mar 2025 11:42:26 -0800 Subject: [PATCH 21/50] standard properties must declare values at initialization --- MMDevice/DeviceBase.h | 2 +- MMDevice/Property.cpp | 40 ++++++++++++++++++++++++++++++++++++---- MMDevice/Property.h | 8 ++++---- 3 files changed, 41 insertions(+), 9 deletions(-) diff --git a/MMDevice/DeviceBase.h b/MMDevice/DeviceBase.h index 519df2852..52d0449b3 100644 --- a/MMDevice/DeviceBase.h +++ b/MMDevice/DeviceBase.h @@ -1340,7 +1340,7 @@ class CDeviceBase : public T // now that all required values are present, and all supplied values are allowed, // add the user-supplied values to the property for (const std::string& val : values) { - ret = AddAllowedValue(fullName.c_str(), val.c_str()); + ret = properties_.AddAllowedValue(fullName.c_str(), val.c_str(), true); if (ret != DEVICE_OK) return ret; } diff --git a/MMDevice/Property.cpp b/MMDevice/Property.cpp index eff757530..d53d31248 100644 --- a/MMDevice/Property.cpp +++ b/MMDevice/Property.cpp @@ -372,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()); @@ -385,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; } diff --git a/MMDevice/Property.h b/MMDevice/Property.h index ba65e69bd..5a06a3211 100644 --- a/MMDevice/Property.h +++ b/MMDevice/Property.h @@ -476,10 +476,10 @@ class PropertyCollection 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); From 5a4a89427fa9418d590374c89737aea04069fe7a Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Thu, 6 Mar 2025 12:32:48 -0800 Subject: [PATCH 22/50] basler progress --- DeviceAdapters/Basler/BaslerPylonCamera.cpp | 180 ++++++++++++-------- DeviceAdapters/Basler/BaslerPylonCamera.h | 5 + MMDevice/DeviceBase.h | 45 ++--- 3 files changed, 142 insertions(+), 88 deletions(-) diff --git a/DeviceAdapters/Basler/BaslerPylonCamera.cpp b/DeviceAdapters/Basler/BaslerPylonCamera.cpp index 6906bb470..d2c22ddf2 100644 --- a/DeviceAdapters/Basler/BaslerPylonCamera.cpp +++ b/DeviceAdapters/Basler/BaslerPylonCamera.cpp @@ -250,6 +250,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. */ @@ -304,127 +323,180 @@ int BaslerCamera::Initialize() - //// Create standard properties + stringstream msg; + msg << "using camera " << camera_->GetDeviceInfo().GetFriendlyName(); + AddToLog(msg.str()); + // initialize the pylon image formatter. + converter = new CImageFormatConverter(); + converter->OutputPixelFormat = PixelType_BGRA8packed; + + // Name + int ret = CreateProperty(MM::g_Keyword_Name, g_BaslerCameraDeviceName, MM::String, true); + if (DEVICE_OK != ret) + return ret; + + // Description + ret = CreateProperty(MM::g_Keyword_Description, "Basler Camera device adapter", MM::String, true); + if (DEVICE_OK != ret) + return ret; + + // Serial Number + ret = CreateProperty(MM::g_Keyword_CameraID, String_t(serialNumber), MM::String, true); + if (DEVICE_OK != ret) + return ret; + + //Pylon::String_t modelName = camera_->GetDeviceInfo().GetModelName(); + //Get information about camera (e.g. height, width, byte depth) + //check if given Camera support event. + camera_->GrabCameraEvents = true; + + camera_->Open(); + // Get the camera nodeMap_ object. + nodeMap_ = &camera_->GetNodeMap(); + + + //// Create standard properties int code; CPropertyAction* action; - // TriggerSelector - if (IsAvailable(camera_->TriggerSelector)) { + CEnumerationPtr triggerSelector(nodeMap_->GetNode("TriggerSelector")); + if (IsAvailable(triggerSelector)) { CPropertyAction* pAct = new CPropertyAction(this, &BaslerCamera::OnTriggerSelector); - std::string currentValue = camera_->TriggerSelector.ToString(); + std::string currentValue = triggerSelector->ToString(); action = new CPropertyAction(this, &BaslerCamera::OnTriggerSelector); - code = CreateTriggerSelectorStandardProperty(currentValue.c_str(), action); + // Get available values from the camera + std::vector values = GetAvailableEnumValues(*triggerSelector); + code = CreateTriggerSelectorStandardProperty(currentValue.c_str(), values, action); assert(code == DEVICE_OK); } else { SkipTriggerSelectorStandardProperty(); } // TriggerMode - if (IsAvailable(camera_->TriggerMode)) { + CEnumerationPtr triggerMode(nodeMap_->GetNode("TriggerMode")); + if (IsAvailable(triggerMode)) { CPropertyAction* pAct = new CPropertyAction(this, &BaslerCamera::OnTriggerMode); - std::string currentValue = camera_->TriggerMode.ToString(); + std::string currentValue = triggerMode->ToString(); action = new CPropertyAction(this, &BaslerCamera::OnTriggerMode); - code = CreateTriggerModeStandardProperty(currentValue.c_str(), action); + // Get available values from the camera + std::vector values = GetAvailableEnumValues(*triggerMode); + code = CreateTriggerModeStandardProperty(currentValue.c_str(), values, action); assert(code == DEVICE_OK); } else { SkipTriggerModeStandardProperty(); } // TriggerSource - if (IsAvailable(camera_->TriggerSource)) { + CEnumerationPtr triggerSource(nodeMap_->GetNode("TriggerSource")); + if (IsAvailable(triggerSource)) { CPropertyAction* pAct = new CPropertyAction(this, &BaslerCamera::OnTriggerSource); - std::string currentValue = camera_->TriggerSource.ToString(); + std::string currentValue = triggerSource->ToString(); action = new CPropertyAction(this, &BaslerCamera::OnTriggerSource); - code = CreateTriggerSourceStandardProperty(currentValue.c_str(), action); + // Get available values from the camera + std::vector values = GetAvailableEnumValues(*triggerSource); + code = CreateTriggerSourceStandardProperty(currentValue.c_str(), values, action); assert(code == DEVICE_OK); } else { SkipTriggerSourceStandardProperty(); } // TriggerActivation - if (IsAvailable(camera_->TriggerActivation)) { + CEnumerationPtr triggerActivation(nodeMap_->GetNode("TriggerActivation")); + if (IsAvailable(triggerActivation)) { CPropertyAction* pAct = new CPropertyAction(this, &BaslerCamera::OnTriggerActivation); - std::string currentValue = camera_->TriggerActivation.ToString(); + std::string currentValue = triggerActivation->ToString(); action = new CPropertyAction(this, &BaslerCamera::OnTriggerActivation); - code = CreateTriggerActivationStandardProperty(currentValue.c_str(), action); + // Get available values from the camera + std::vector values = GetAvailableEnumValues(*triggerActivation); + code = CreateTriggerActivationStandardProperty(currentValue.c_str(), values, action); assert(code == DEVICE_OK); } else { SkipTriggerActivationStandardProperty(); } // TriggerDelay - if (IsAvailable(camera_->TriggerDelay)) { + CFloatPtr triggerDelay(nodeMap_->GetNode("TriggerDelay")); + if (IsAvailable(triggerDelay)) { CPropertyAction* pAct = new CPropertyAction(this, &BaslerCamera::OnTriggerDelay); - double currentValue = camera_->TriggerDelay.GetValue(); + double currentValue = triggerDelay->GetValue(); + double minValue = triggerDelay->GetMin(); + double maxValue = triggerDelay->GetMax(); std::string strValue = CDeviceUtils::ConvertToString(currentValue); action = new CPropertyAction(this, &BaslerCamera::OnTriggerDelay); - code = CreateTriggerDelayStandardProperty(strValue.c_str(), action); + code = CreateTriggerDelayStandardProperty(strValue.c_str(), minValue, maxValue, action); assert(code == DEVICE_OK); } else { SkipTriggerDelayStandardProperty(); } // ExposureMode - if (IsAvailable(camera_->ExposureMode)) { + CEnumerationPtr exposureMode(nodeMap_->GetNode("ExposureMode")); + if (IsAvailable(exposureMode)) { CPropertyAction* pAct = new CPropertyAction(this, &BaslerCamera::OnExposureMode); - std::string currentValue = camera_->ExposureMode.ToString(); + std::string currentValue = exposureMode->ToString(); action = new CPropertyAction(this, &BaslerCamera::OnExposureMode); - code = CreateExposureModeStandardProperty(currentValue.c_str(), action); + // Get available values from the camera + std::vector values = GetAvailableEnumValues(*exposureMode); + code = CreateExposureModeStandardProperty(currentValue.c_str(), values, action); assert(code == DEVICE_OK); } else { SkipExposureModeStandardProperty(); } - // BurstFrameCount - // if (IsAvailable(camera_->AcquisitionBurstFrameCount)) { - // CPropertyAction* pAct = new CPropertyAction(this, &BaslerCamera::OnBurstFrameCount); - // ret = CreateBurstFrameCountStandardProperty(); - // assert(ret == DEVICE_OK); - // } else { - // Haven't made a handler for this because unable to test + // BurstFrameCount - Skipping as in original code SkipBurstFrameCountStandardProperty(); - // } // LineSelector - if (IsAvailable(camera_->LineSelector)) { + CEnumerationPtr lineSelector(nodeMap_->GetNode("LineSelector")); + if (IsAvailable(lineSelector)) { CPropertyAction* pAct = new CPropertyAction(this, &BaslerCamera::OnLineSelector); - std::string currentValue = camera_->LineSelector.ToString(); + std::string currentValue = lineSelector->ToString(); action = new CPropertyAction(this, &BaslerCamera::OnLineSelector); - code = CreateLineSelectorStandardProperty(currentValue.c_str(), action); + // Get available values from the camera + std::vector values = GetAvailableEnumValues(*lineSelector); + code = CreateLineSelectorStandardProperty(currentValue.c_str(), values, action); assert(code == DEVICE_OK); } else { SkipLineSelectorStandardProperty(); } // LineInverter - if (IsAvailable(camera_->LineInverter)) { + CBooleanPtr lineInverter(nodeMap_->GetNode("LineInverter")); + if (IsAvailable(lineInverter)) { CPropertyAction* pAct = new CPropertyAction(this, &BaslerCamera::OnLineInverter); - std::string currentValue = camera_->LineInverter.ToString(); + std::string currentValue = lineInverter->ToString(); action = new CPropertyAction(this, &BaslerCamera::OnLineInverter); - code = CreateLineInverterStandardProperty(currentValue.c_str(), action); + // Get available values for boolean + std::vector values = {"0", "1"}; + code = CreateLineInverterStandardProperty(currentValue.c_str(), values, action); assert(code == DEVICE_OK); } else { SkipLineInverterStandardProperty(); } // LineSource - if (IsAvailable(camera_->LineSource)) { + CEnumerationPtr lineSource(nodeMap_->GetNode("LineSource")); + if (IsAvailable(lineSource)) { CPropertyAction* pAct = new CPropertyAction(this, &BaslerCamera::OnLineSource); - std::string currentValue = camera_->LineSource.ToString(); + std::string currentValue = lineSource->ToString(); action = new CPropertyAction(this, &BaslerCamera::OnLineSource); - code = CreateLineSourceStandardProperty(currentValue.c_str(), action); + // Get available values from the camera + std::vector values = GetAvailableEnumValues(*lineSource); + code = CreateLineSourceStandardProperty(currentValue.c_str(), values, action); assert(code == DEVICE_OK); } else { SkipLineSourceStandardProperty(); } // LineStatus - if (IsAvailable(camera_->LineStatus)) { + CBooleanPtr lineStatus(nodeMap_->GetNode("LineStatus")); + if (IsAvailable(lineStatus)) { CPropertyAction* pAct = new CPropertyAction(this, &BaslerCamera::OnLineStatus); - std::string currentValue = camera_->LineStatus.ToString(); + std::string currentValue = lineStatus->ToString(); action = new CPropertyAction(this, &BaslerCamera::OnLineStatus); + // LineStatus is read-only code = CreateLineStatusStandardProperty(currentValue.c_str(), action); assert(code == DEVICE_OK); } else { @@ -435,36 +507,6 @@ int BaslerCamera::Initialize() SkipRollingShutterLineOffsetStandardProperty(); SkipRollingShutterActiveLinesStandardProperty(); - stringstream msg; - msg << "using camera " << camera_->GetDeviceInfo().GetFriendlyName(); - AddToLog(msg.str()); - // initialize the pylon image formatter. - converter = new CImageFormatConverter(); - converter->OutputPixelFormat = PixelType_BGRA8packed; - - // Name - int ret = CreateProperty(MM::g_Keyword_Name, g_BaslerCameraDeviceName, MM::String, true); - if (DEVICE_OK != ret) - return ret; - - // Description - ret = CreateProperty(MM::g_Keyword_Description, "Basler Camera device adapter", MM::String, true); - if (DEVICE_OK != ret) - return ret; - - // Serial Number - ret = CreateProperty(MM::g_Keyword_CameraID, String_t(serialNumber), MM::String, true); - if (DEVICE_OK != ret) - return ret; - - //Pylon::String_t modelName = camera_->GetDeviceInfo().GetModelName(); - //Get information about camera (e.g. height, width, byte depth) - //check if given Camera support event. - camera_->GrabCameraEvents = true; - - camera_->Open(); - // Get the camera nodeMap_ object. - nodeMap_ = &camera_->GetNodeMap(); if (camera_->EventSelector.IsWritable()) diff --git a/DeviceAdapters/Basler/BaslerPylonCamera.h b/DeviceAdapters/Basler/BaslerPylonCamera.h index 15c2c0624..6f46f5bfa 100644 --- a/DeviceAdapters/Basler/BaslerPylonCamera.h +++ b/DeviceAdapters/Basler/BaslerPylonCamera.h @@ -177,8 +177,13 @@ class BaslerCamera : public CNewAPICameraBase { std::string EnumToString(EDeviceAccessiblityInfo DeviceAccessiblityInfo); void* Buffer4ContinuesShot; + std::vector GetAvailableEnumValues(const GenApi::IEnumeration& node); + + private: + + CBaslerUniversalInstantCamera *camera_; CTempCameraEventHandler *pTempHandler_; diff --git a/MMDevice/DeviceBase.h b/MMDevice/DeviceBase.h index c1fcd7a00..e8643cefd 100644 --- a/MMDevice/DeviceBase.h +++ b/MMDevice/DeviceBase.h @@ -141,48 +141,54 @@ class CDeviceBase : public T // } // Camera triggering API standard properties - int CreateTriggerSelectorStandardProperty(const char* value, MM::ActionFunctor* pAct = 0) { - return CreateStandardProperty(value, pAct); + int CreateTriggerSelectorStandardProperty(const char* value, const std::vector& values, MM::ActionFunctor* pAct = 0) { + return CreateStandardProperty(value, pAct, values); } void SkipTriggerSelectorStandardProperty() { SkipStandardProperty(); } - int CreateTriggerModeStandardProperty(const char* value, MM::ActionFunctor* pAct = 0) { - return CreateStandardProperty(value, pAct); + int CreateTriggerModeStandardProperty(const char* value, const std::vector& values, MM::ActionFunctor* pAct = 0) { + return CreateStandardProperty(value, pAct, values); } void SkipTriggerModeStandardProperty() { SkipStandardProperty(); } - int CreateTriggerSourceStandardProperty(const char* value, MM::ActionFunctor* pAct = 0) { - return CreateStandardProperty(value, pAct); + int CreateTriggerSourceStandardProperty(const char* value, const std::vector& values, MM::ActionFunctor* pAct = 0) { + return CreateStandardProperty(value, pAct, values); } void SkipTriggerSourceStandardProperty() { SkipStandardProperty(); } - int CreateTriggerActivationStandardProperty(const char* value, MM::ActionFunctor* pAct = 0) { - return CreateStandardProperty(value, pAct); + int CreateTriggerActivationStandardProperty(const char* value, const std::vector& values, MM::ActionFunctor* pAct = 0) { + return CreateStandardProperty(value, pAct, values); } void SkipTriggerActivationStandardProperty() { SkipStandardProperty(); } - int CreateTriggerDelayStandardProperty(const char* value, MM::ActionFunctor* pAct = 0) { - return CreateStandardProperty(value, pAct); + 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, MM::ActionFunctor* pAct = 0) { - return CreateStandardProperty(value, pAct); + int CreateExposureModeStandardProperty(const char* value, const std::vector& values, MM::ActionFunctor* pAct = 0) { + return CreateStandardProperty(value, pAct, values); } void SkipExposureModeStandardProperty() { @@ -197,24 +203,24 @@ class CDeviceBase : public T SkipStandardProperty(); } - int CreateLineSelectorStandardProperty(const char* value, MM::ActionFunctor* pAct = 0) { - return CreateStandardProperty(value, pAct); + int CreateLineSelectorStandardProperty(const char* value, const std::vector& values, MM::ActionFunctor* pAct = 0) { + return CreateStandardProperty(value, pAct, values); } void SkipLineSelectorStandardProperty() { SkipStandardProperty(); } - int CreateLineInverterStandardProperty(const char* value, MM::ActionFunctor* pAct = 0) { - return CreateStandardProperty(value, pAct); + int CreateLineInverterStandardProperty(const char* value, const std::vector& values, MM::ActionFunctor* pAct = 0) { + return CreateStandardProperty(value, pAct, values); } void SkipLineInverterStandardProperty() { SkipStandardProperty(); } - int CreateLineSourceStandardProperty(const char* value, MM::ActionFunctor* pAct = 0) { - return CreateStandardProperty(value, pAct); + int CreateLineSourceStandardProperty(const char* value, const std::vector& values, MM::ActionFunctor* pAct = 0) { + return CreateStandardProperty(value, pAct, values); } void SkipLineSourceStandardProperty() { @@ -222,7 +228,8 @@ class CDeviceBase : public T } int CreateLineStatusStandardProperty(const char* value, MM::ActionFunctor* pAct = 0) { - // LineStatus has required values that are the same as its allowed values + // 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); } From efdf7f9996e24c8fb4c9f2f6edeaae6db2f6d6d4 Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Thu, 6 Mar 2025 12:43:47 -0800 Subject: [PATCH 23/50] successful auto-query of standard properties from basler --- MMDevice/MMDevice.h | 24 ++++++++---------------- MMDevice/MMDeviceConstants.h | 8 ++------ 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/MMDevice/MMDevice.h b/MMDevice/MMDevice.h index 9a9d8974f..31e675ad1 100644 --- a/MMDevice/MMDevice.h +++ b/MMDevice/MMDevice.h @@ -359,20 +359,18 @@ namespace MM { // Specifies the internal signal or physical input Line to use as the trigger source. // The selected trigger must have its TriggerMode set to On. - static const std::vector triggerSourceValues = { - g_keyword_TriggerSourceSoftware, - g_keyword_TriggerIOLine0, - g_keyword_TriggerIOLine1, - g_keyword_TriggerIOLine2, - g_keyword_TriggerIOLine3 - }; + // 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 - triggerSourceValues, // allowedValues - {}, // TODO: require software trigger? + {}, // allowedValues + {g_keyword_TriggerSourceSoftware}, PropertyLimitUndefined, // lowerLimit PropertyLimitUndefined, // upperLimit }; @@ -467,18 +465,12 @@ namespace MM { // 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 std::vector lineSelectorValues = { - g_keyword_TriggerIOLine0, - g_keyword_TriggerIOLine1, - g_keyword_TriggerIOLine2, - g_keyword_TriggerIOLine3 - }; static const MM::StandardProperty g_LineSelectorProperty{ "LineSelector", // name String, // type false, // isReadOnly false, // isPreInit - lineSelectorValues, // allowedValues + {}, // allowedValues {}, // requiredValues PropertyLimitUndefined, // lowerLimit PropertyLimitUndefined, // upperLimit diff --git a/MMDevice/MMDeviceConstants.h b/MMDevice/MMDeviceConstants.h index d1185672a..01bd51760 100644 --- a/MMDevice/MMDeviceConstants.h +++ b/MMDevice/MMDeviceConstants.h @@ -193,10 +193,6 @@ namespace MM { const char* const g_keyword_TriggerModeOn = "On"; const char* const g_keyword_TriggerModeOff = "Off"; - const char* const g_keyword_TriggerIOLine0 = "Line0"; - const char* const g_keyword_TriggerIOLine1 = "Line1"; - const char* const g_keyword_TriggerIOLine2 = "Line2"; - const char* const g_keyword_TriggerIOLine3 = "Line3"; const char* const g_keyword_TriggerSourceSoftware = "Software"; const char* const g_keyword_TriggerActivationAnyEdge = "AnyEdge"; @@ -241,8 +237,8 @@ namespace MM { const char* const g_keyword_OutputLineSourceExposureTriggerMissed = "ExposureTriggerMissed"; const char* const g_keyword_OutputLineSourceExposureActive = "ExposureActive"; - const char* const g_keyword_LineStatusLow = "Low"; - const char* const g_keyword_LineStatusHigh = "High"; + const char* const g_keyword_LineStatusLow = "0"; + const char* const g_keyword_LineStatusHigh = "1"; // Camera Events const char* const g_keyword_CameraEventAcquisitionTrigger = "CameraEventAcquisitionTrigger"; From 7766760036b2f951fd369c24a607396117fc101e Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Fri, 7 Mar 2025 09:32:19 -0800 Subject: [PATCH 24/50] Events work in progress --- DeviceAdapters/Basler/BaslerPylonCamera.cpp | 103 +++++++++++++++++++- DeviceAdapters/Basler/BaslerPylonCamera.h | 15 +-- MMDevice/DeviceBase.h | 16 +++ MMDevice/MMDevice.h | 50 ++++++++++ MMDevice/MMDeviceConstants.h | 24 ++++- 5 files changed, 194 insertions(+), 14 deletions(-) diff --git a/DeviceAdapters/Basler/BaslerPylonCamera.cpp b/DeviceAdapters/Basler/BaslerPylonCamera.cpp index d2c22ddf2..28b83f966 100644 --- a/DeviceAdapters/Basler/BaslerPylonCamera.cpp +++ b/DeviceAdapters/Basler/BaslerPylonCamera.cpp @@ -355,7 +355,7 @@ int BaslerCamera::Initialize() nodeMap_ = &camera_->GetNodeMap(); - //// Create standard properties + //// Create standard properties int code; CPropertyAction* action; @@ -508,6 +508,25 @@ int BaslerCamera::Initialize() SkipRollingShutterActiveLinesStandardProperty(); + // Event Standard Properties + CEnumerationPtr eventSelector(nodeMap_->GetNode("EventSelector")); + if (IsAvailable(eventSelector)) { + action = new CPropertyAction(this, &BaslerCamera::OnEventSelector); + std::string currentValue = eventSelector->ToString(); + std::vector values = GetAvailableEnumValues(*eventSelector); + code = CreateEventSelectorStandardProperty(currentValue.c_str(), values, action); + assert(code == DEVICE_OK); + } + + CEnumerationPtr eventNotification(nodeMap_->GetNode("EventNotification")); + if (IsAvailable(eventNotification)) { + action = new CPropertyAction(this, &BaslerCamera::OnEventNotification); + std::string currentValue = eventNotification->ToString(); + // This one has required values of On and Off + code = CreateEventNotificationStandardProperty(currentValue.c_str(), action); + assert(code == DEVICE_OK); + } + if (camera_->EventSelector.IsWritable()) { @@ -1072,7 +1091,7 @@ int BaslerCamera::AcquisitionArm(int frameCount) } } - ImageHandler_ = new CircularBufferInserter(this); + ImageHandler_ = new BufferInserter(this); camera_->RegisterImageEventHandler(ImageHandler_, RegistrationMode_Append, Cleanup_Delete); return DEVICE_OK; @@ -1447,7 +1466,27 @@ int BaslerCamera::OnTriggerSource(MM::PropertyBase* pProp, MM::ActionType eAct) if (eAct == MM::AfterSet) { pProp->Get(TriggerSource_); CEnumerationPtr TriggerSource(nodeMap_->GetNode("TriggerSource")); - TriggerSource->FromString(TriggerSource_.c_str()); + try { + if (TriggerSource != NULL && IsAvailable(TriggerSource)) { + if (TriggerSource->GetEntryByName(TriggerSource_.c_str()) != NULL) { + TriggerSource->FromString(TriggerSource_.c_str()); + } else { + // The requested trigger source is not available + return DEVICE_INVALID_PROPERTY_VALUE; + } + } else { + // TriggerSource is not available + return DEVICE_INVALID_PROPERTY_VALUE; + } + } catch (const GenericException& e) { + // Log the error and return a meaningful error code + AddToLog(e.GetDescription()); + return DEVICE_ERR; + } + + + // update other property values: + } else if (eAct == MM::BeforeGet) { CEnumerationPtr TriggerSource(nodeMap_->GetNode("TriggerSource")); @@ -1670,7 +1709,61 @@ int BaslerCamera::OnLineStatus(MM::PropertyBase* pProp, MM::ActionType eAct) 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()); + } + 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::OnEventNotification(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + try + { + string EventNotification_; + CEnumerationPtr EventNotification(nodeMap_->GetNode("EventNotification")); + if (EventNotification != NULL && IsAvailable(EventNotification)) + { + if (eAct == MM::AfterSet) { + pProp->Get(EventNotification_); + EventNotification->FromString(EventNotification_.c_str()); + pProp->Set(EventNotification->ToString().c_str()); + } + else if (eAct == MM::BeforeGet) { + pProp->Set(EventNotification->ToString().c_str()); + } + } + } + catch (const GenericException & e) + { + // Error handling. + AddToLog(e.GetDescription()); + return DEVICE_ERR; + } + return DEVICE_OK; +} +/////////////////////////////////// //// Non-standard property handlers int BaslerCamera::OnBinningMode(MM::PropertyBase* pProp, MM::ActionType eAct) @@ -2481,11 +2574,11 @@ void CTempCameraEventHandler::OnCameraEvent(CBaslerUniversalInstantCamera& camer } } -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]; diff --git a/DeviceAdapters/Basler/BaslerPylonCamera.h b/DeviceAdapters/Basler/BaslerPylonCamera.h index 6f46f5bfa..8ff834385 100644 --- a/DeviceAdapters/Basler/BaslerPylonCamera.h +++ b/DeviceAdapters/Basler/BaslerPylonCamera.h @@ -61,7 +61,7 @@ enum //Callback class for putting frames in circular buffer as they arrive class CTempCameraEventHandler; -class CircularBufferInserter; +class BufferInserter; class BaslerCamera : public CNewAPICameraBase { public: BaslerCamera(); @@ -126,10 +126,11 @@ class BaslerCamera : public CNewAPICameraBase { int OnLineSource(MM::PropertyBase* pProp, MM::ActionType eAct); int OnLineStatus(MM::PropertyBase* pProp, MM::ActionType eAct); - // other - // int OnTriggerMode(MM::PropertyBase* pProp, MM::ActionType eAct); - // int OnTriggerSource(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnEventSelector(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnEventNotification(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); @@ -173,7 +174,7 @@ class BaslerCamera : public CNewAPICameraBase { CImageFormatConverter *converter; - CircularBufferInserter *ImageHandler_; + BufferInserter *ImageHandler_; std::string EnumToString(EDeviceAccessiblityInfo DeviceAccessiblityInfo); void* Buffer4ContinuesShot; @@ -248,12 +249,12 @@ class CTempCameraEventHandler : public CBaslerUniversalCameraEventHandler }; -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/MMDevice/DeviceBase.h b/MMDevice/DeviceBase.h index e8643cefd..1b97c43f6 100644 --- a/MMDevice/DeviceBase.h +++ b/MMDevice/DeviceBase.h @@ -254,6 +254,22 @@ class CDeviceBase : public T SkipStandardProperty(); } + int CreateEventSelectorStandardProperty(const char* value, const std::vector& values, MM::ActionFunctor* pAct = 0) { + return CreateStandardProperty(value, pAct, values); + } + + void SkipEventSelectorStandardProperty() { + SkipStandardProperty(); + } + + int CreateEventNotificationStandardProperty(const char* value, MM::ActionFunctor* pAct = 0) { + return CreateStandardProperty(value, pAct, MM::g_EventNotificationProperty.requiredValues); + } + + void SkipEventNotificationStandardProperty() { + SkipStandardProperty(); + } + /** * Assigns description string for a device (for use only by the calling code). */ diff --git a/MMDevice/MMDevice.h b/MMDevice/MMDevice.h index 31e675ad1..63de8b25c 100644 --- a/MMDevice/MMDevice.h +++ b/MMDevice/MMDevice.h @@ -553,6 +553,56 @@ namespace MM { // g_keyword_CameraStatusAcquisitionTransfer // }; + // 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 diff --git a/MMDevice/MMDeviceConstants.h b/MMDevice/MMDeviceConstants.h index 01bd51760..9c3ff65b7 100644 --- a/MMDevice/MMDeviceConstants.h +++ b/MMDevice/MMDeviceConstants.h @@ -198,8 +198,8 @@ namespace MM { 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 = "Low"; - const char* const g_keyword_TriggerActivationLevelHigh = "High"; + 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"; @@ -241,21 +241,41 @@ namespace MM { 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 From da30e68604f674166a8e7fb539e7885f8c45e390 Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Fri, 7 Mar 2025 09:43:22 -0800 Subject: [PATCH 25/50] add method for updating standard property values --- MMDevice/DeviceBase.h | 89 ++++++++++++++++++++++++++++++------------- 1 file changed, 63 insertions(+), 26 deletions(-) diff --git a/MMDevice/DeviceBase.h b/MMDevice/DeviceBase.h index 52d0449b3..6b0fe9cca 100644 --- a/MMDevice/DeviceBase.h +++ b/MMDevice/DeviceBase.h @@ -1315,34 +1315,18 @@ class CDeviceBase : public T return ret; } - // Ensure only allowed values are set + // Ensure the initial value is allowed if the property has predefined allowed values if (!PropRef.allowedValues.empty()) { - // If the property has predefined allowed values, validate that all supplied values are allowed - if (std::find(PropRef.allowedValues.begin(), PropRef.allowedValues.end(), value) == PropRef.allowedValues.end()) { - return DEVICE_INVALID_PROPERTY_VALUE; - } - - for (const std::string& val : values) { - if (std::find(PropRef.allowedValues.begin(), PropRef.allowedValues.end(), val) == PropRef.allowedValues.end()) { - return DEVICE_INVALID_PROPERTY_VALUE; - } - } - } - // if there are required values, make sure they are all present - if (!PropRef.requiredValues.empty()) { - // throw an error if required values are not present - for (const std::string& val : PropRef.requiredValues) { - if (std::find(values.begin(), values.end(), val) == values.end()) { - return DEVICE_INVALID_PROPERTY_VALUE; - } - } + if (std::find(PropRef.allowedValues.begin(), PropRef.allowedValues.end(), value) == PropRef.allowedValues.end()) { + return DEVICE_INVALID_PROPERTY_VALUE; + } } - // now that all required values are present, and all supplied values are allowed, - // add the user-supplied values to the property - for (const std::string& val : values) { - ret = properties_.AddAllowedValue(fullName.c_str(), val.c_str(), true); - if (ret != DEVICE_OK) - return ret; + + // 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 @@ -1351,6 +1335,59 @@ class CDeviceBase : public T 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 = 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. From c3bd38eaf2e410cd26d5477b4bea0e4ae69cb920 Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Fri, 7 Mar 2025 10:23:43 -0800 Subject: [PATCH 26/50] fix bug --- MMDevice/DeviceBase.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MMDevice/DeviceBase.h b/MMDevice/DeviceBase.h index 6b0fe9cca..dfc7b77f5 100644 --- a/MMDevice/DeviceBase.h +++ b/MMDevice/DeviceBase.h @@ -1374,7 +1374,7 @@ class CDeviceBase : public T } // Clear existing values - int ret = ClearAllowedValues(fullName.c_str(), true); + int ret = properties_.ClearAllowedValues(fullName.c_str(), true); if (ret != DEVICE_OK) return ret; From 9eac5050ad410d3b7b3c6f4a345f422f1dc95a54 Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Fri, 7 Mar 2025 12:29:09 -0800 Subject: [PATCH 27/50] add checker method --- MMDevice/DeviceBase.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MMDevice/DeviceBase.h b/MMDevice/DeviceBase.h index dfc7b77f5..f3d1b5867 100644 --- a/MMDevice/DeviceBase.h +++ b/MMDevice/DeviceBase.h @@ -557,6 +557,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, From 2e4a9373ef5b1979facaa23b1f4ec507246eda4c Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Fri, 7 Mar 2025 12:35:10 -0800 Subject: [PATCH 28/50] WIP dynamic properties --- DeviceAdapters/Basler/BaslerPylonCamera.cpp | 916 ++++++++++++-------- DeviceAdapters/Basler/BaslerPylonCamera.h | 37 +- MMDevice/DeviceBase.h | 91 +- MMDevice/MMDevice.h | 67 +- MMDevice/MMDeviceConstants.h | 2 + 5 files changed, 719 insertions(+), 394 deletions(-) diff --git a/DeviceAdapters/Basler/BaslerPylonCamera.cpp b/DeviceAdapters/Basler/BaslerPylonCamera.cpp index 28b83f966..8ab1fb3a7 100644 --- a/DeviceAdapters/Basler/BaslerPylonCamera.cpp +++ b/DeviceAdapters/Basler/BaslerPylonCamera.cpp @@ -356,178 +356,31 @@ int BaslerCamera::Initialize() //// Create standard properties + InitTriggerSelectorStandardProperty(); + InitTriggerModeStandardProperty(); + InitTriggerSourceStandardProperty(); + InitTriggerActivationStandardProperty(); + InitTriggerDelayStandardProperty(); + InitTriggerOverlapStandardProperty(); + + InitExposureModeStandardProperty(); + InitExposureTimeStandardProperty(); + + InitLineSelectorStandardProperty(); + InitLineModeStandardProperty(); + InitLineInverterStandardProperty(); + InitLineSourceStandardProperty(); + InitLineStatusStandardProperty(); - int code; - CPropertyAction* action; - - // TriggerSelector - CEnumerationPtr triggerSelector(nodeMap_->GetNode("TriggerSelector")); - if (IsAvailable(triggerSelector)) { - CPropertyAction* pAct = new CPropertyAction(this, &BaslerCamera::OnTriggerSelector); - std::string currentValue = triggerSelector->ToString(); - action = new CPropertyAction(this, &BaslerCamera::OnTriggerSelector); - // Get available values from the camera - std::vector values = GetAvailableEnumValues(*triggerSelector); - code = CreateTriggerSelectorStandardProperty(currentValue.c_str(), values, action); - assert(code == DEVICE_OK); - } else { - SkipTriggerSelectorStandardProperty(); - } - - // TriggerMode - CEnumerationPtr triggerMode(nodeMap_->GetNode("TriggerMode")); - if (IsAvailable(triggerMode)) { - CPropertyAction* pAct = new CPropertyAction(this, &BaslerCamera::OnTriggerMode); - std::string currentValue = triggerMode->ToString(); - action = new CPropertyAction(this, &BaslerCamera::OnTriggerMode); - // Get available values from the camera - std::vector values = GetAvailableEnumValues(*triggerMode); - code = CreateTriggerModeStandardProperty(currentValue.c_str(), values, action); - assert(code == DEVICE_OK); - } else { - SkipTriggerModeStandardProperty(); - } - - // TriggerSource - CEnumerationPtr triggerSource(nodeMap_->GetNode("TriggerSource")); - if (IsAvailable(triggerSource)) { - CPropertyAction* pAct = new CPropertyAction(this, &BaslerCamera::OnTriggerSource); - std::string currentValue = triggerSource->ToString(); - action = new CPropertyAction(this, &BaslerCamera::OnTriggerSource); - // Get available values from the camera - std::vector values = GetAvailableEnumValues(*triggerSource); - code = CreateTriggerSourceStandardProperty(currentValue.c_str(), values, action); - assert(code == DEVICE_OK); - } else { - SkipTriggerSourceStandardProperty(); - } - - // TriggerActivation - CEnumerationPtr triggerActivation(nodeMap_->GetNode("TriggerActivation")); - if (IsAvailable(triggerActivation)) { - CPropertyAction* pAct = new CPropertyAction(this, &BaslerCamera::OnTriggerActivation); - std::string currentValue = triggerActivation->ToString(); - action = new CPropertyAction(this, &BaslerCamera::OnTriggerActivation); - // Get available values from the camera - std::vector values = GetAvailableEnumValues(*triggerActivation); - code = CreateTriggerActivationStandardProperty(currentValue.c_str(), values, action); - assert(code == DEVICE_OK); - } else { - SkipTriggerActivationStandardProperty(); - } - - // TriggerDelay - CFloatPtr triggerDelay(nodeMap_->GetNode("TriggerDelay")); - if (IsAvailable(triggerDelay)) { - CPropertyAction* pAct = new CPropertyAction(this, &BaslerCamera::OnTriggerDelay); - double currentValue = triggerDelay->GetValue(); - double minValue = triggerDelay->GetMin(); - double maxValue = triggerDelay->GetMax(); - std::string strValue = CDeviceUtils::ConvertToString(currentValue); - action = new CPropertyAction(this, &BaslerCamera::OnTriggerDelay); - code = CreateTriggerDelayStandardProperty(strValue.c_str(), minValue, maxValue, action); - assert(code == DEVICE_OK); - } else { - SkipTriggerDelayStandardProperty(); - } - - // ExposureMode - CEnumerationPtr exposureMode(nodeMap_->GetNode("ExposureMode")); - if (IsAvailable(exposureMode)) { - CPropertyAction* pAct = new CPropertyAction(this, &BaslerCamera::OnExposureMode); - std::string currentValue = exposureMode->ToString(); - action = new CPropertyAction(this, &BaslerCamera::OnExposureMode); - // Get available values from the camera - std::vector values = GetAvailableEnumValues(*exposureMode); - code = CreateExposureModeStandardProperty(currentValue.c_str(), values, action); - assert(code == DEVICE_OK); - } else { - SkipExposureModeStandardProperty(); - } - - // BurstFrameCount - Skipping as in original code - SkipBurstFrameCountStandardProperty(); - - // LineSelector - CEnumerationPtr lineSelector(nodeMap_->GetNode("LineSelector")); - if (IsAvailable(lineSelector)) { - CPropertyAction* pAct = new CPropertyAction(this, &BaslerCamera::OnLineSelector); - std::string currentValue = lineSelector->ToString(); - action = new CPropertyAction(this, &BaslerCamera::OnLineSelector); - // Get available values from the camera - std::vector values = GetAvailableEnumValues(*lineSelector); - code = CreateLineSelectorStandardProperty(currentValue.c_str(), values, action); - assert(code == DEVICE_OK); - } else { - SkipLineSelectorStandardProperty(); - } - - // LineInverter - CBooleanPtr lineInverter(nodeMap_->GetNode("LineInverter")); - if (IsAvailable(lineInverter)) { - CPropertyAction* pAct = new CPropertyAction(this, &BaslerCamera::OnLineInverter); - std::string currentValue = lineInverter->ToString(); - action = new CPropertyAction(this, &BaslerCamera::OnLineInverter); - // Get available values for boolean - std::vector values = {"0", "1"}; - code = CreateLineInverterStandardProperty(currentValue.c_str(), values, action); - assert(code == DEVICE_OK); - } else { - SkipLineInverterStandardProperty(); - } - - // LineSource - CEnumerationPtr lineSource(nodeMap_->GetNode("LineSource")); - if (IsAvailable(lineSource)) { - CPropertyAction* pAct = new CPropertyAction(this, &BaslerCamera::OnLineSource); - std::string currentValue = lineSource->ToString(); - action = new CPropertyAction(this, &BaslerCamera::OnLineSource); - // Get available values from the camera - std::vector values = GetAvailableEnumValues(*lineSource); - code = CreateLineSourceStandardProperty(currentValue.c_str(), values, action); - assert(code == DEVICE_OK); - } else { - SkipLineSourceStandardProperty(); - } - - // LineStatus - CBooleanPtr lineStatus(nodeMap_->GetNode("LineStatus")); - if (IsAvailable(lineStatus)) { - CPropertyAction* pAct = new CPropertyAction(this, &BaslerCamera::OnLineStatus); - std::string currentValue = lineStatus->ToString(); - action = new CPropertyAction(this, &BaslerCamera::OnLineStatus); - // LineStatus is read-only - code = CreateLineStatusStandardProperty(currentValue.c_str(), action); - assert(code == DEVICE_OK); - } else { - SkipLineStatusStandardProperty(); - } + InitEventSelectorStandardProperty(); + InitEventNotificationStandardProperty(); + + SkipBurstFrameCountStandardProperty(); - // Unclear if any basler cameras support these properties SkipRollingShutterLineOffsetStandardProperty(); SkipRollingShutterActiveLinesStandardProperty(); - // Event Standard Properties - CEnumerationPtr eventSelector(nodeMap_->GetNode("EventSelector")); - if (IsAvailable(eventSelector)) { - action = new CPropertyAction(this, &BaslerCamera::OnEventSelector); - std::string currentValue = eventSelector->ToString(); - std::vector values = GetAvailableEnumValues(*eventSelector); - code = CreateEventSelectorStandardProperty(currentValue.c_str(), values, action); - assert(code == DEVICE_OK); - } - - CEnumerationPtr eventNotification(nodeMap_->GetNode("EventNotification")); - if (IsAvailable(eventNotification)) { - action = new CPropertyAction(this, &BaslerCamera::OnEventNotification); - std::string currentValue = eventNotification->ToString(); - // This one has required values of On and Off - code = CreateEventNotificationStandardProperty(currentValue.c_str(), action); - assert(code == DEVICE_OK); - } - - if (camera_->EventSelector.IsWritable()) { if (camera_->EventSelector.CanSetValue(EventSelector_CriticalTemperature) && @@ -605,7 +458,6 @@ int BaslerCamera::Initialize() //Exposure CFloatPtr exposure(nodeMap_->GetNode("ExposureTime")); - CFloatPtr ExposureTimeAbs(nodeMap_->GetNode("ExposureTimeAbs")); if (IsAvailable(exposure)) { // USB cameras @@ -614,12 +466,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_); @@ -1319,6 +1165,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. */ @@ -1351,43 +1203,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. */ @@ -1445,6 +1260,13 @@ int BaslerCamera::OnTriggerSelector(MM::PropertyBase* pProp, MM::ActionType eAct // 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 + ReloadTriggerModeStandardPropertyValues(); + ReloadTriggerSourceStandardPropertyValues(); + ReloadTriggerActivationStandardPropertyValues(); + ReloadExposureModeStandardPropertyValues(); + ReloadEventSelectorStandardPropertyValues(); } else if (eAct == MM::BeforeGet) { pProp->Set(TriggerSelector->ToString().c_str()); @@ -1466,27 +1288,14 @@ int BaslerCamera::OnTriggerSource(MM::PropertyBase* pProp, MM::ActionType eAct) if (eAct == MM::AfterSet) { pProp->Get(TriggerSource_); CEnumerationPtr TriggerSource(nodeMap_->GetNode("TriggerSource")); - try { - if (TriggerSource != NULL && IsAvailable(TriggerSource)) { - if (TriggerSource->GetEntryByName(TriggerSource_.c_str()) != NULL) { - TriggerSource->FromString(TriggerSource_.c_str()); - } else { - // The requested trigger source is not available - return DEVICE_INVALID_PROPERTY_VALUE; - } - } else { - // TriggerSource is not available - return DEVICE_INVALID_PROPERTY_VALUE; - } - } catch (const GenericException& e) { - // Log the error and return a meaningful error code - AddToLog(e.GetDescription()); - return DEVICE_ERR; - } - + TriggerSource->FromString(TriggerSource_.c_str()); // update other property values: - + ReloadTriggerModeStandardPropertyValues(); + ReloadTriggerActivationStandardPropertyValues(); + ReloadExposureModeStandardPropertyValues(); + ReloadEventSelectorStandardPropertyValues(); + } else if (eAct == MM::BeforeGet) { CEnumerationPtr TriggerSource(nodeMap_->GetNode("TriggerSource")); @@ -1501,48 +1310,22 @@ int BaslerCamera::OnTriggerSource(MM::PropertyBase* pProp, MM::ActionType eAct) 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()); - return DEVICE_ERR; - } - return DEVICE_OK; -} - -int BaslerCamera::OnTriggerActivation(MM::PropertyBase* pProp, MM::ActionType eAct) +int BaslerCamera::HandleEnumerationProperty(MM::PropertyBase* pProp, MM::ActionType eAct, + const char* nodeName) { try { - string TriggerActivation_; - CEnumerationPtr TriggerActivation(nodeMap_->GetNode("TriggerActivation")); - if (TriggerActivation != NULL && IsAvailable(TriggerActivation)) + string valueStr; + CEnumerationPtr enumNode(nodeMap_->GetNode(nodeName)); + if (enumNode != NULL && IsAvailable(enumNode)) { if (eAct == MM::AfterSet) { - pProp->Get(TriggerActivation_); - TriggerActivation->FromString(TriggerActivation_.c_str()); - pProp->Set(TriggerActivation->ToString().c_str()); + pProp->Get(valueStr); + enumNode->FromString(valueStr.c_str()); + pProp->Set(enumNode->ToString().c_str()); } else if (eAct == MM::BeforeGet) { - pProp->Set(TriggerActivation->ToString().c_str()); + pProp->Set(enumNode->ToString().c_str()); } } } @@ -1555,6 +1338,41 @@ int BaslerCamera::OnTriggerActivation(MM::PropertyBase* pProp, MM::ActionType eA 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::OnLineMode(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + return HandleEnumerationProperty(pProp, eAct, "LineMode"); +} + int BaslerCamera::OnTriggerDelay(MM::PropertyBase* pProp, MM::ActionType eAct) { try @@ -1581,28 +1399,58 @@ int BaslerCamera::OnTriggerDelay(MM::PropertyBase* pProp, MM::ActionType eAct) return DEVICE_OK; } -int BaslerCamera::OnExposureMode(MM::PropertyBase* pProp, MM::ActionType eAct) + +int BaslerCamera::OnExposureTime(MM::PropertyBase* pProp, MM::ActionType eAct) { try { - string ExposureMode_; - CEnumerationPtr ExposureMode(nodeMap_->GetNode("ExposureMode")); - if (ExposureMode != NULL && IsAvailable(ExposureMode)) + if (eAct == MM::AfterSet) { - if (eAct == MM::AfterSet) { - pProp->Get(ExposureMode_); - ExposureMode->FromString(ExposureMode_.c_str()); - pProp->Set(ExposureMode->ToString().c_str()); + // Get the new exposure value + pProp->Get(exposure_us_); + + // Ensure exposure is within allowed range + if (exposure_us_ > exposureMax_) { + exposure_us_ = exposureMax_; } - else if (eAct == MM::BeforeGet) { - pProp->Set(ExposureMode->ToString().c_str()); + 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. + // Error handling AddToLog(e.GetDescription()); + cerr << "An exception occurred." << endl + << e.GetDescription() << endl; return DEVICE_ERR; } return DEVICE_OK; @@ -1620,6 +1468,11 @@ int BaslerCamera::OnLineSelector(MM::PropertyBase* pProp, MM::ActionType eAct) pProp->Get(LineSelector_); LineSelector->FromString(LineSelector_.c_str()); pProp->Set(LineSelector->ToString().c_str()); + + // Update other properties that may depend on the line selector + ReloadLineModeStandardPropertyValues(); + ReloadLineInverterStandardPropertyValues(); + ReloadLineSourceStandardPropertyValues(); } else if (eAct == MM::BeforeGet) { pProp->Set(LineSelector->ToString().c_str()); @@ -1661,32 +1514,6 @@ int BaslerCamera::OnLineInverter(MM::PropertyBase* pProp, MM::ActionType eAct) return DEVICE_OK; } -int BaslerCamera::OnLineSource(MM::PropertyBase* pProp, MM::ActionType eAct) -{ - try - { - string LineSource_; - CEnumerationPtr LineSource(nodeMap_->GetNode("LineSource")); - if (LineSource != NULL && IsAvailable(LineSource)) - { - if (eAct == MM::AfterSet) { - pProp->Get(LineSource_); - LineSource->FromString(LineSource_.c_str()); - pProp->Set(LineSource->ToString().c_str()); - } - else if (eAct == MM::BeforeGet) { - pProp->Set(LineSource->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) { @@ -1721,6 +1548,9 @@ int BaslerCamera::OnEventSelector(MM::PropertyBase* pProp, MM::ActionType eAct) pProp->Get(EventSelector_); EventSelector->FromString(EventSelector_.c_str()); pProp->Set(EventSelector->ToString().c_str()); + + // Update EventNotification property values which depend on EventSelector + ReloadEventNotificationStandardPropertyValues(); } else if (eAct == MM::BeforeGet) { pProp->Set(EventSelector->ToString().c_str()); @@ -1736,35 +1566,168 @@ int BaslerCamera::OnEventSelector(MM::PropertyBase* pProp, MM::ActionType eAct) return DEVICE_OK; } -int BaslerCamera::OnEventNotification(MM::PropertyBase* pProp, MM::ActionType eAct) -{ - try - { - string EventNotification_; - CEnumerationPtr EventNotification(nodeMap_->GetNode("EventNotification")); - if (EventNotification != NULL && IsAvailable(EventNotification)) - { - if (eAct == MM::AfterSet) { - pProp->Get(EventNotification_); - EventNotification->FromString(EventNotification_.c_str()); - pProp->Set(EventNotification->ToString().c_str()); - } - else if (eAct == MM::BeforeGet) { - pProp->Set(EventNotification->ToString().c_str()); - } - } + + +void BaslerCamera::ReloadTriggerModeStandardPropertyValues() { + CEnumerationPtr node(nodeMap_->GetNode("TriggerMode")); + bool cameraHasIt = IsAvailable(node); + bool propertyExists = HasStandardProperty("TriggerMode"); + if (cameraHasIt && !propertyExists) { + // It wasn't created during initialization because it only exists dependent on another + // property value + InitTriggerModeStandardProperty(); + } + if (cameraHasIt) { + std::vector values = GetAvailableEnumValues(*node); + int ret = SetTriggerModeStandardPropertyValues(values); + assert(ret == DEVICE_OK); } - catch (const GenericException & e) - { - // Error handling. - AddToLog(e.GetDescription()); - return DEVICE_ERR; +} + +void BaslerCamera::ReloadTriggerSourceStandardPropertyValues() { + CEnumerationPtr node(nodeMap_->GetNode("TriggerSource")); + bool cameraHasIt = IsAvailable(node); + bool propertyExists = HasStandardProperty("TriggerSource"); + if (cameraHasIt && !propertyExists) { + // It wasn't created during initialization because it only exists dependent on another + // property value + InitTriggerSourceStandardProperty(); + } + if (cameraHasIt) { + std::vector values = GetAvailableEnumValues(*node); + int ret = SetTriggerSourceStandardPropertyValues(values); + assert(ret == DEVICE_OK); } - return DEVICE_OK; } -/////////////////////////////////// -//// Non-standard property handlers +void BaslerCamera::ReloadTriggerActivationStandardPropertyValues() { + CEnumerationPtr node(nodeMap_->GetNode("TriggerActivation")); + bool cameraHasIt = IsAvailable(node); + bool propertyExists = HasStandardProperty("TriggerActivation"); + if (cameraHasIt && !propertyExists) { + // It wasn't created during initialization because it only exists dependent on another + // property value + InitTriggerActivationStandardProperty(); + } + if (cameraHasIt) { + std::vector values = GetAvailableEnumValues(*node); + int ret = SetTriggerActivationStandardPropertyValues(values); + assert(ret == DEVICE_OK); + } +} + +void BaslerCamera::ReloadExposureModeStandardPropertyValues() { + CEnumerationPtr node(nodeMap_->GetNode("ExposureMode")); + bool cameraHasIt = IsAvailable(node); + bool propertyExists = HasStandardProperty("ExposureMode"); + if (cameraHasIt && !propertyExists) { + // It wasn't created during initialization because it only exists dependent on another + // property value + InitExposureModeStandardProperty(); + } + if (cameraHasIt) { + std::vector values = GetAvailableEnumValues(*node); + int ret = SetExposureModeStandardPropertyValues(values); + assert(ret == DEVICE_OK); + } +} + +void BaslerCamera::ReloadEventSelectorStandardPropertyValues() { + CEnumerationPtr node(nodeMap_->GetNode("EventSelector")); + bool cameraHasIt = IsAvailable(node); + bool propertyExists = HasStandardProperty("EventSelector"); + if (cameraHasIt && !propertyExists) { + // It wasn't created during initialization because it only exists dependent on another + // property value + InitEventSelectorStandardProperty(); + } + if (cameraHasIt) { + std::vector values = GetAvailableEnumValues(*node); + int ret = SetEventSelectorStandardPropertyValues(values); + assert(ret == DEVICE_OK); + } +} + +void BaslerCamera::ReloadLineModeStandardPropertyValues() { + CEnumerationPtr node(nodeMap_->GetNode("LineMode")); + bool cameraHasIt = IsAvailable(node); + bool propertyExists = HasStandardProperty("LineMode"); + if (cameraHasIt && !propertyExists) { + // It wasn't created during initialization because it only exists dependent on another + // property value + InitLineModeStandardProperty(); + } + if (cameraHasIt) { + std::vector values = GetAvailableEnumValues(*node); + int ret = SetLineModeStandardPropertyValues(values); + assert(ret == DEVICE_OK); + } +} + +void BaslerCamera::ReloadLineSourceStandardPropertyValues() { + CEnumerationPtr node(nodeMap_->GetNode("LineSource")); + bool cameraHasIt = IsAvailable(node); + bool propertyExists = HasStandardProperty("LineSource"); + if (cameraHasIt && !propertyExists) { + // It wasn't created during initialization because it only exists dependent on another + // property value + InitLineSourceStandardProperty(); + } + if (cameraHasIt) { + std::vector values = GetAvailableEnumValues(*node); + int ret = SetLineSourceStandardPropertyValues(values); + assert(ret == DEVICE_OK); + } +} + +void BaslerCamera::ReloadEventNotificationStandardPropertyValues() { + CEnumerationPtr node(nodeMap_->GetNode("EventNotification")); + bool cameraHasIt = IsAvailable(node); + bool propertyExists = HasStandardProperty("EventNotification"); + if (cameraHasIt && !propertyExists) { + // It wasn't created during initialization because it only exists dependent on another + // property value + InitEventNotificationStandardProperty(); + } + if (cameraHasIt) { + std::vector values = GetAvailableEnumValues(*node); + int ret = SetEventNotificationStandardPropertyValues(values); + assert(ret == DEVICE_OK); + } +} + +void BaslerCamera::ReloadTriggerOverlapStandardPropertyValues() { + CEnumerationPtr node(nodeMap_->GetNode("TriggerOverlap")); + bool cameraHasIt = IsAvailable(node); + bool propertyExists = HasStandardProperty("TriggerOverlap"); + if (cameraHasIt && !propertyExists) { + // It wasn't created during initialization because it only exists dependent on another + // property value + InitTriggerOverlapStandardProperty(); + } + if (cameraHasIt) { + std::vector values = GetAvailableEnumValues(*node); + int ret = SetTriggerOverlapStandardPropertyValues(values); + assert(ret == DEVICE_OK); + } +} + + // We map GenICam booleans to "0" and "1" +void BaslerCamera::ReloadLineInverterStandardPropertyValues() { + CBooleanPtr lineInverter(nodeMap_->GetNode("LineInverter")); + bool propertyExists = HasStandardProperty("LineInverter"); + if (IsAvailable(lineInverter) && !propertyExists) { + // It wasn't created during initialization because it only exists dependent on another + // property value + InitLineInverterStandardProperty(); + } + if (IsAvailable(lineInverter)) { + std::string currentValue = lineInverter->ToString(); + std::vector values = {"0", "1"}; + int ret = SetLineInverterStandardPropertyValues(values); + assert(ret == DEVICE_OK); + } +} int BaslerCamera::OnBinningMode(MM::PropertyBase* pProp, MM::ActionType eAct) { @@ -1917,48 +1880,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")); @@ -2019,11 +1940,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(); @@ -2125,8 +2041,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; + return DEVICE_OK; } int BaslerCamera::OnTemperature(MM::PropertyBase* pProp, MM::ActionType eAct) @@ -2650,3 +2567,254 @@ void BufferInserter::OnImageGrabbed(CInstantCamera& /* camera */, const CGrabRes dev_->AddToLog(ss.str()); } } + +int BaslerCamera::InitTriggerSelectorStandardProperty() +{ + CEnumerationPtr triggerSelector(nodeMap_->GetNode("TriggerSelector")); + if (IsAvailable(triggerSelector)) { + std::string currentValue = triggerSelector->ToString(); + CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnTriggerSelector); + // Get available values from the camera + std::vector values = GetAvailableEnumValues(*triggerSelector); + int code = CreateTriggerSelectorStandardProperty(currentValue.c_str(), values, action); + assert(code == DEVICE_OK); + return code; + } else { + SkipTriggerSelectorStandardProperty(); + return DEVICE_OK; + } +} + +int BaslerCamera::InitTriggerModeStandardProperty() +{ + CEnumerationPtr triggerMode(nodeMap_->GetNode("TriggerMode")); + if (IsAvailable(triggerMode)) { + std::string currentValue = triggerMode->ToString(); + CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnTriggerMode); + // Get available values from the camera + std::vector values = GetAvailableEnumValues(*triggerMode); + int code = CreateTriggerModeStandardProperty(currentValue.c_str(), values, action); + assert(code == DEVICE_OK); + return code; + } else { + SkipTriggerModeStandardProperty(); + return DEVICE_OK; + } +} + +int BaslerCamera::InitTriggerSourceStandardProperty() +{ + CEnumerationPtr triggerSource(nodeMap_->GetNode("TriggerSource")); + if (IsAvailable(triggerSource)) { + std::string currentValue = triggerSource->ToString(); + CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnTriggerSource); + // Get available values from the camera + std::vector values = GetAvailableEnumValues(*triggerSource); + int code = CreateTriggerSourceStandardProperty(currentValue.c_str(), values, action); + assert(code == DEVICE_OK); + return code; + } else { + SkipTriggerSourceStandardProperty(); + return DEVICE_OK; + } +} + +int BaslerCamera::InitTriggerActivationStandardProperty() +{ + CEnumerationPtr triggerActivation(nodeMap_->GetNode("TriggerActivation")); + if (IsAvailable(triggerActivation)) { + std::string currentValue = triggerActivation->ToString(); + CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnTriggerActivation); + // Get available values from the camera + std::vector values = GetAvailableEnumValues(*triggerActivation); + int code = CreateTriggerActivationStandardProperty(currentValue.c_str(), values, action); + assert(code == DEVICE_OK); + return code; + } else { + SkipTriggerActivationStandardProperty(); + return DEVICE_OK; + } +} + +int BaslerCamera::InitTriggerDelayStandardProperty() +{ + CFloatPtr triggerDelay(nodeMap_->GetNode("TriggerDelay")); + if (IsAvailable(triggerDelay)) { + double currentValue = triggerDelay->GetValue(); + double minValue = triggerDelay->GetMin(); + double maxValue = triggerDelay->GetMax(); + std::string strValue = CDeviceUtils::ConvertToString(currentValue); + CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnTriggerDelay); + int code = CreateTriggerDelayStandardProperty(strValue.c_str(), minValue, maxValue, action); + assert(code == DEVICE_OK); + return code; + } else { + SkipTriggerDelayStandardProperty(); + return DEVICE_OK; + } +} + +int BaslerCamera::InitTriggerOverlapStandardProperty() +{ + CEnumerationPtr triggerOverlap(nodeMap_->GetNode("TriggerOverlap")); + if (IsAvailable(triggerOverlap)) { + std::string currentValue = triggerOverlap->ToString(); + CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnTriggerOverlap); + // Get available values from the camera + std::vector values = GetAvailableEnumValues(*triggerOverlap); + int code = CreateTriggerOverlapStandardProperty(currentValue.c_str(), values, action); + assert(code == DEVICE_OK); + return code; + } else { + SkipTriggerOverlapStandardProperty(); + return DEVICE_OK; + } +} + +int BaslerCamera::InitExposureModeStandardProperty() +{ + CEnumerationPtr exposureMode(nodeMap_->GetNode("ExposureMode")); + if (IsAvailable(exposureMode)) { + std::string currentValue = exposureMode->ToString(); + CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnExposureMode); + // Get available values from the camera + std::vector values = GetAvailableEnumValues(*exposureMode); + int code = CreateExposureModeStandardProperty(currentValue.c_str(), values, action); + assert(code == DEVICE_OK); + return code; + } else { + SkipExposureModeStandardProperty(); + return DEVICE_OK; + } +} + +int BaslerCamera::InitExposureTimeStandardProperty() +{ + CFloatPtr exposureTime(nodeMap_->GetNode("ExposureTime")); + if (IsAvailable(exposureTime)) { + double currentValue = exposureTime->GetValue(); + double minValue = exposureTime->GetMin(); + double maxValue = exposureTime->GetMax(); + + std::string strValue = CDeviceUtils::ConvertToString(currentValue); + CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnExposureTime); + int code = CreateExposureTimeStandardProperty(strValue.c_str(), minValue, maxValue, action); + assert(code == DEVICE_OK); + return code; + } else { + SkipExposureTimeStandardProperty(); + return DEVICE_OK; + } +} + +int BaslerCamera::InitLineSelectorStandardProperty() +{ + CEnumerationPtr lineSelector(nodeMap_->GetNode("LineSelector")); + if (IsAvailable(lineSelector)) { + std::string currentValue = lineSelector->ToString(); + CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnLineSelector); + // Get available values from the camera + std::vector values = GetAvailableEnumValues(*lineSelector); + int code = CreateLineSelectorStandardProperty(currentValue.c_str(), values, action); + assert(code == DEVICE_OK); + return code; + } else { + SkipLineSelectorStandardProperty(); + return DEVICE_OK; + } +} + +int BaslerCamera::InitLineModeStandardProperty() +{ + CEnumerationPtr lineMode(nodeMap_->GetNode("LineMode")); + if (IsAvailable(lineMode)) { + std::string currentValue = lineMode->ToString(); + CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnLineMode); + // Get available values from the camera + std::vector values = GetAvailableEnumValues(*lineMode); + int code = CreateLineModeStandardProperty(currentValue.c_str(), values, action); + assert(code == DEVICE_OK); + return code; + } else { + SkipLineModeStandardProperty(); + return DEVICE_OK; + } +} + +int BaslerCamera::InitLineInverterStandardProperty() +{ + CBooleanPtr lineInverter(nodeMap_->GetNode("LineInverter")); + if (IsAvailable(lineInverter)) { + std::string currentValue = lineInverter->ToString(); + CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnLineInverter); + // Get available values for boolean + std::vector values = {"0", "1"}; + int code = CreateLineInverterStandardProperty(currentValue.c_str(), values, action); + assert(code == DEVICE_OK); + return code; + } else { + SkipLineInverterStandardProperty(); + return DEVICE_OK; + } +} + +int BaslerCamera::InitLineSourceStandardProperty() +{ + CEnumerationPtr lineSource(nodeMap_->GetNode("LineSource")); + if (IsAvailable(lineSource)) { + std::string currentValue = lineSource->ToString(); + CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnLineSource); + // Get available values from the camera + std::vector values = GetAvailableEnumValues(*lineSource); + int code = CreateLineSourceStandardProperty(currentValue.c_str(), values, action); + assert(code == DEVICE_OK); + return code; + } else { + SkipLineSourceStandardProperty(); + return DEVICE_OK; + } +} + +int BaslerCamera::InitLineStatusStandardProperty() +{ + CBooleanPtr lineStatus(nodeMap_->GetNode("LineStatus")); + if (IsAvailable(lineStatus)) { + std::string currentValue = lineStatus->ToString(); + CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnLineStatus); + // LineStatus is read-only + int code = CreateLineStatusStandardProperty(currentValue.c_str(), action); + assert(code == DEVICE_OK); + return code; + } else { + SkipLineStatusStandardProperty(); + return DEVICE_OK; + } +} + +int BaslerCamera::InitEventSelectorStandardProperty() +{ + CEnumerationPtr eventSelector(nodeMap_->GetNode("EventSelector")); + if (IsAvailable(eventSelector)) { + CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnEventSelector); + std::string currentValue = eventSelector->ToString(); + std::vector values = GetAvailableEnumValues(*eventSelector); + int code = CreateEventSelectorStandardProperty(currentValue.c_str(), values, action); + assert(code == DEVICE_OK); + return code; + } + return DEVICE_OK; +} + +int BaslerCamera::InitEventNotificationStandardProperty() +{ + CEnumerationPtr eventNotification(nodeMap_->GetNode("EventNotification")); + if (IsAvailable(eventNotification)) { + CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnEventNotification); + std::string currentValue = eventNotification->ToString(); + // This one has required values of On and Off + int code = CreateEventNotificationStandardProperty(currentValue.c_str(), action); + assert(code == DEVICE_OK); + return code; + } + return DEVICE_OK; +} diff --git a/DeviceAdapters/Basler/BaslerPylonCamera.h b/DeviceAdapters/Basler/BaslerPylonCamera.h index 8ff834385..f115fd96d 100644 --- a/DeviceAdapters/Basler/BaslerPylonCamera.h +++ b/DeviceAdapters/Basler/BaslerPylonCamera.h @@ -85,8 +85,6 @@ class BaslerCamera : public CNewAPICameraBase { unsigned GetImageBytesPerPixel() const; unsigned GetBitDepth() 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(); @@ -122,9 +120,12 @@ class BaslerCamera : public CNewAPICameraBase { 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); @@ -138,7 +139,6 @@ class BaslerCamera : public CNewAPICameraBase { 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); @@ -155,9 +155,22 @@ class BaslerCamera : public CNewAPICameraBase { int OnWidth(MM::PropertyBase* pProp, MM::ActionType eAct); + // For dyanmic standard property value updates: + void ReloadTriggerModeStandardPropertyValues(); + void ReloadTriggerSourceStandardPropertyValues(); + void ReloadTriggerActivationStandardPropertyValues(); + void ReloadExposureModeStandardPropertyValues(); + void ReloadEventSelectorStandardPropertyValues(); + void ReloadLineModeStandardPropertyValues(); + void ReloadLineInverterStandardPropertyValues(); + void ReloadLineSourceStandardPropertyValues(); + void ReloadEventNotificationStandardPropertyValues(); + void ReloadTriggerOverlapStandardPropertyValues(); + // Convenience functions std::string NodeToString(const char* str) const; - int SelectTrigger(const char* triggerSelector); + + int HandleEnumerationProperty(MM::PropertyBase* pProp, MM::ActionType eAct, const char* nodeName); void ResizeSnapBuffer(); @@ -180,6 +193,22 @@ class BaslerCamera : public CNewAPICameraBase { std::vector GetAvailableEnumValues(const GenApi::IEnumeration& node); + // Standard property initialization functions + int InitTriggerSelectorStandardProperty(); + int InitTriggerModeStandardProperty(); + int InitTriggerSourceStandardProperty(); + int InitTriggerActivationStandardProperty(); + int InitTriggerDelayStandardProperty(); + int InitTriggerOverlapStandardProperty(); + int InitExposureModeStandardProperty(); + int InitExposureTimeStandardProperty(); + int InitLineSelectorStandardProperty(); + int InitLineModeStandardProperty(); + int InitLineInverterStandardProperty(); + int InitLineSourceStandardProperty(); + int InitLineStatusStandardProperty(); + int InitEventSelectorStandardProperty(); + int InitEventNotificationStandardProperty(); private: diff --git a/MMDevice/DeviceBase.h b/MMDevice/DeviceBase.h index 1783e8e97..77e519f9e 100644 --- a/MMDevice/DeviceBase.h +++ b/MMDevice/DeviceBase.h @@ -153,6 +153,10 @@ class CDeviceBase : public T return CreateStandardProperty(value, pAct, values); } + int SetTriggerModeStandardPropertyValues(const std::vector& values) { + return SetStandardPropertyValues(values); + } + void SkipTriggerModeStandardProperty() { SkipStandardProperty(); } @@ -161,6 +165,10 @@ class CDeviceBase : public T return CreateStandardProperty(value, pAct, values); } + int SetTriggerSourceStandardPropertyValues(const std::vector& values) { + return SetStandardPropertyValues(values); + } + void SkipTriggerSourceStandardProperty() { SkipStandardProperty(); } @@ -168,6 +176,10 @@ class CDeviceBase : public T 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(); @@ -191,6 +203,24 @@ class CDeviceBase : public T 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(); } @@ -211,10 +241,26 @@ class CDeviceBase : public T 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(); } @@ -223,6 +269,10 @@ class CDeviceBase : public T return CreateStandardProperty(value, pAct, values); } + int SetLineSourceStandardPropertyValues(const std::vector& values) { + return SetStandardPropertyValues(values); + } + void SkipLineSourceStandardProperty() { SkipStandardProperty(); } @@ -234,6 +284,10 @@ class CDeviceBase : public T MM::g_LineStatusProperty.requiredValues); } + int SetLineStatusStandardPropertyValues(const std::vector& values) { + return SetStandardPropertyValues(values); + } + void SkipLineStatusStandardProperty() { SkipStandardProperty(); } @@ -258,6 +312,10 @@ class CDeviceBase : public T return CreateStandardProperty(value, pAct, values); } + int SetEventSelectorStandardPropertyValues(const std::vector& values) { + return SetStandardPropertyValues(values); + } + void SkipEventSelectorStandardProperty() { SkipStandardProperty(); } @@ -266,10 +324,27 @@ class CDeviceBase : public T 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(); + } + + /** * Assigns description string for a device (for use only by the calling code). */ @@ -1842,6 +1917,9 @@ class COldAPICameraBase : public CAllCamerasBase SkipLineStatusStandardProperty(); SkipRollingShutterLineOffsetStandardProperty(); SkipRollingShutterActiveLinesStandardProperty(); + SkipEventNotificationStandardProperty(); + SkipTriggerOverlapStandardProperty(); + SkipExposureTimeStandardProperty(); } // Shared functionality with no default implementation @@ -1851,8 +1929,6 @@ class COldAPICameraBase : public CAllCamerasBase virtual unsigned GetBitDepth() const = 0; virtual int GetBinning() const = 0; virtual int SetBinning(int binSize) = 0; - virtual void SetExposure(double exp_ms) = 0; - virtual double GetExposure() const = 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; @@ -1862,6 +1938,9 @@ class COldAPICameraBase : public CAllCamerasBase 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; @@ -1909,8 +1988,6 @@ class CNewAPICameraBase : public CAllCamerasBase virtual unsigned GetBitDepth() const = 0; virtual int GetBinning() const = 0; virtual int SetBinning(int binSize) = 0; - virtual void SetExposure(double exp_ms) = 0; - virtual double GetExposure() const = 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; @@ -1931,7 +2008,11 @@ class CNewAPICameraBase : public CAllCamerasBase 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 const unsigned char* GetImageBuffer(unsigned /* channelNr */) final {return nullptr;} + // TODO: do this in terms of the standard property + // TODO: these are in ms but the standard property is in us + virtual void SetExposure(double exp_ms) final {return;} + virtual double GetExposure() const final {return -1;} // New camera API: required diff --git a/MMDevice/MMDevice.h b/MMDevice/MMDevice.h index 63de8b25c..e241d23c2 100644 --- a/MMDevice/MMDevice.h +++ b/MMDevice/MMDevice.h @@ -370,7 +370,7 @@ namespace MM { false, // isReadOnly false, // isPreInit {}, // allowedValues - {g_keyword_TriggerSourceSoftware}, + {}, PropertyLimitUndefined, // lowerLimit PropertyLimitUndefined, // upperLimit }; @@ -442,7 +442,39 @@ namespace MM { }; MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_ExposureModeProperty) + // 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) + // 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 @@ -478,6 +510,28 @@ namespace MM { 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. @@ -497,21 +551,12 @@ namespace MM { // Selects which internal acquisition or I/O source signal to output on the selected Line. // LineMode must be Output. - static const std::vector lineSourceValues = { - g_keyword_OutputLineSourceOff, - g_keyword_OutputLineSourceAcquisitionTriggerWait, - g_keyword_OutputLineSourceAcquisitionActive, - g_keyword_OutputLineSourceFrameTriggerWait, - g_keyword_OutputLineSourceFrameActive, - g_keyword_OutputLineSourceExposureTriggerWait, - g_keyword_OutputLineSourceExposureActive - }; static const MM::StandardProperty g_LineSourceProperty{ "LineSource", // name String, // type false, // isReadOnly false, // isPreInit - lineSourceValues, // allowedValues + {}, // allowedValues {}, // requiredValues PropertyLimitUndefined, // lowerLimit PropertyLimitUndefined, // upperLimit diff --git a/MMDevice/MMDeviceConstants.h b/MMDevice/MMDeviceConstants.h index 9c3ff65b7..a14bbd9a2 100644 --- a/MMDevice/MMDeviceConstants.h +++ b/MMDevice/MMDeviceConstants.h @@ -215,6 +215,8 @@ namespace MM { 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"; From a73d75ed0a8c58f14405a7bff507dfcd4d151d6a Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Fri, 7 Mar 2025 12:47:31 -0800 Subject: [PATCH 29/50] add ability to dynamically delete properties --- MMDevice/DeviceBase.h | 8 ++++++++ MMDevice/Property.cpp | 11 +++++++++++ MMDevice/Property.h | 1 + 3 files changed, 20 insertions(+) diff --git a/MMDevice/DeviceBase.h b/MMDevice/DeviceBase.h index f3d1b5867..55a25c830 100644 --- a/MMDevice/DeviceBase.h +++ b/MMDevice/DeviceBase.h @@ -1416,6 +1416,14 @@ class CDeviceBase : public T 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_.DeleteProperty(fullName.c_str()); + } } } diff --git a/MMDevice/Property.cpp b/MMDevice/Property.cpp index d53d31248..93fcf5575 100644 --- a/MMDevice/Property.cpp +++ b/MMDevice/Property.cpp @@ -540,3 +540,14 @@ 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; + + delete pProp; + + return DEVICE_OK; +} diff --git a/MMDevice/Property.h b/MMDevice/Property.h index 5a06a3211..1a3fa5f35 100644 --- a/MMDevice/Property.h +++ b/MMDevice/Property.h @@ -492,6 +492,7 @@ class PropertyCollection int ApplyAll(); int Update(const char* Name); int Apply(const char* Name); + int Delete(const char* pszName); private: typedef std::map CPropArray; From afca015ebcc52eae5a90f49fb5c563056caaac31 Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Fri, 7 Mar 2025 13:03:46 -0800 Subject: [PATCH 30/50] refactoring dynamic properties --- DeviceAdapters/Basler/BaslerPylonCamera.cpp | 761 ++++++++++---------- DeviceAdapters/Basler/BaslerPylonCamera.h | 31 +- MMDevice/DeviceBase.h | 8 + MMDevice/MMDevice.h | 16 +- 4 files changed, 390 insertions(+), 426 deletions(-) diff --git a/DeviceAdapters/Basler/BaslerPylonCamera.cpp b/DeviceAdapters/Basler/BaslerPylonCamera.cpp index 8ab1fb3a7..73f7b0251 100644 --- a/DeviceAdapters/Basler/BaslerPylonCamera.cpp +++ b/DeviceAdapters/Basler/BaslerPylonCamera.cpp @@ -356,24 +356,24 @@ int BaslerCamera::Initialize() //// Create standard properties - InitTriggerSelectorStandardProperty(); - InitTriggerModeStandardProperty(); - InitTriggerSourceStandardProperty(); - InitTriggerActivationStandardProperty(); - InitTriggerDelayStandardProperty(); - InitTriggerOverlapStandardProperty(); + InitOrSyncTriggerSelectorStandardProperty(); + InitOrSyncTriggerModeStandardProperty(); + InitOrSyncTriggerSourceStandardProperty(); + InitOrSyncTriggerActivationStandardProperty(); + InitOrSyncTriggerDelayStandardProperty(); + InitOrSyncTriggerOverlapStandardProperty(); - InitExposureModeStandardProperty(); - InitExposureTimeStandardProperty(); + InitOrSyncExposureModeStandardProperty(); + InitOrSyncExposureTimeStandardProperty(); - InitLineSelectorStandardProperty(); - InitLineModeStandardProperty(); - InitLineInverterStandardProperty(); - InitLineSourceStandardProperty(); - InitLineStatusStandardProperty(); + InitOrSyncLineSelectorStandardProperty(); + InitOrSyncLineModeStandardProperty(); + InitOrSyncLineInverterStandardProperty(); + InitOrSyncLineSourceStandardProperty(); + InitOrSyncLineStatusStandardProperty(); - InitEventSelectorStandardProperty(); - InitEventNotificationStandardProperty(); + InitOrSyncEventSelectorStandardProperty(); + InitOrSyncEventNotificationStandardProperty(); SkipBurstFrameCountStandardProperty(); @@ -1262,11 +1262,11 @@ int BaslerCamera::OnTriggerSelector(MM::PropertyBase* pProp, MM::ActionType eAct pProp->Set(TriggerSelector->ToString().c_str()); // Update other properties that may depend on the trigger selector - ReloadTriggerModeStandardPropertyValues(); - ReloadTriggerSourceStandardPropertyValues(); - ReloadTriggerActivationStandardPropertyValues(); - ReloadExposureModeStandardPropertyValues(); - ReloadEventSelectorStandardPropertyValues(); + InitOrSyncTriggerModeStandardProperty(); + InitOrSyncTriggerSourceStandardProperty(); + InitOrSyncTriggerActivationStandardProperty(); + InitOrSyncExposureModeStandardProperty(); + InitOrSyncEventSelectorStandardProperty(); } else if (eAct == MM::BeforeGet) { pProp->Set(TriggerSelector->ToString().c_str()); @@ -1291,10 +1291,10 @@ int BaslerCamera::OnTriggerSource(MM::PropertyBase* pProp, MM::ActionType eAct) TriggerSource->FromString(TriggerSource_.c_str()); // update other property values: - ReloadTriggerModeStandardPropertyValues(); - ReloadTriggerActivationStandardPropertyValues(); - ReloadExposureModeStandardPropertyValues(); - ReloadEventSelectorStandardPropertyValues(); + InitOrSyncTriggerModeStandardProperty(); + InitOrSyncTriggerActivationStandardProperty(); + InitOrSyncExposureModeStandardProperty(); + InitOrSyncEventSelectorStandardProperty(); } else if (eAct == MM::BeforeGet) { @@ -1470,9 +1470,9 @@ int BaslerCamera::OnLineSelector(MM::PropertyBase* pProp, MM::ActionType eAct) pProp->Set(LineSelector->ToString().c_str()); // Update other properties that may depend on the line selector - ReloadLineModeStandardPropertyValues(); - ReloadLineInverterStandardPropertyValues(); - ReloadLineSourceStandardPropertyValues(); + InitOrSyncLineModeStandardProperty(); + InitOrSyncLineInverterStandardProperty(); + InitOrSyncLineSourceStandardProperty(); } else if (eAct == MM::BeforeGet) { pProp->Set(LineSelector->ToString().c_str()); @@ -1550,7 +1550,7 @@ int BaslerCamera::OnEventSelector(MM::PropertyBase* pProp, MM::ActionType eAct) pProp->Set(EventSelector->ToString().c_str()); // Update EventNotification property values which depend on EventSelector - ReloadEventNotificationStandardPropertyValues(); + InitOrSyncEventNotificationStandardProperty(); } else if (eAct == MM::BeforeGet) { pProp->Set(EventSelector->ToString().c_str()); @@ -1567,168 +1567,6 @@ int BaslerCamera::OnEventSelector(MM::PropertyBase* pProp, MM::ActionType eAct) } - -void BaslerCamera::ReloadTriggerModeStandardPropertyValues() { - CEnumerationPtr node(nodeMap_->GetNode("TriggerMode")); - bool cameraHasIt = IsAvailable(node); - bool propertyExists = HasStandardProperty("TriggerMode"); - if (cameraHasIt && !propertyExists) { - // It wasn't created during initialization because it only exists dependent on another - // property value - InitTriggerModeStandardProperty(); - } - if (cameraHasIt) { - std::vector values = GetAvailableEnumValues(*node); - int ret = SetTriggerModeStandardPropertyValues(values); - assert(ret == DEVICE_OK); - } -} - -void BaslerCamera::ReloadTriggerSourceStandardPropertyValues() { - CEnumerationPtr node(nodeMap_->GetNode("TriggerSource")); - bool cameraHasIt = IsAvailable(node); - bool propertyExists = HasStandardProperty("TriggerSource"); - if (cameraHasIt && !propertyExists) { - // It wasn't created during initialization because it only exists dependent on another - // property value - InitTriggerSourceStandardProperty(); - } - if (cameraHasIt) { - std::vector values = GetAvailableEnumValues(*node); - int ret = SetTriggerSourceStandardPropertyValues(values); - assert(ret == DEVICE_OK); - } -} - -void BaslerCamera::ReloadTriggerActivationStandardPropertyValues() { - CEnumerationPtr node(nodeMap_->GetNode("TriggerActivation")); - bool cameraHasIt = IsAvailable(node); - bool propertyExists = HasStandardProperty("TriggerActivation"); - if (cameraHasIt && !propertyExists) { - // It wasn't created during initialization because it only exists dependent on another - // property value - InitTriggerActivationStandardProperty(); - } - if (cameraHasIt) { - std::vector values = GetAvailableEnumValues(*node); - int ret = SetTriggerActivationStandardPropertyValues(values); - assert(ret == DEVICE_OK); - } -} - -void BaslerCamera::ReloadExposureModeStandardPropertyValues() { - CEnumerationPtr node(nodeMap_->GetNode("ExposureMode")); - bool cameraHasIt = IsAvailable(node); - bool propertyExists = HasStandardProperty("ExposureMode"); - if (cameraHasIt && !propertyExists) { - // It wasn't created during initialization because it only exists dependent on another - // property value - InitExposureModeStandardProperty(); - } - if (cameraHasIt) { - std::vector values = GetAvailableEnumValues(*node); - int ret = SetExposureModeStandardPropertyValues(values); - assert(ret == DEVICE_OK); - } -} - -void BaslerCamera::ReloadEventSelectorStandardPropertyValues() { - CEnumerationPtr node(nodeMap_->GetNode("EventSelector")); - bool cameraHasIt = IsAvailable(node); - bool propertyExists = HasStandardProperty("EventSelector"); - if (cameraHasIt && !propertyExists) { - // It wasn't created during initialization because it only exists dependent on another - // property value - InitEventSelectorStandardProperty(); - } - if (cameraHasIt) { - std::vector values = GetAvailableEnumValues(*node); - int ret = SetEventSelectorStandardPropertyValues(values); - assert(ret == DEVICE_OK); - } -} - -void BaslerCamera::ReloadLineModeStandardPropertyValues() { - CEnumerationPtr node(nodeMap_->GetNode("LineMode")); - bool cameraHasIt = IsAvailable(node); - bool propertyExists = HasStandardProperty("LineMode"); - if (cameraHasIt && !propertyExists) { - // It wasn't created during initialization because it only exists dependent on another - // property value - InitLineModeStandardProperty(); - } - if (cameraHasIt) { - std::vector values = GetAvailableEnumValues(*node); - int ret = SetLineModeStandardPropertyValues(values); - assert(ret == DEVICE_OK); - } -} - -void BaslerCamera::ReloadLineSourceStandardPropertyValues() { - CEnumerationPtr node(nodeMap_->GetNode("LineSource")); - bool cameraHasIt = IsAvailable(node); - bool propertyExists = HasStandardProperty("LineSource"); - if (cameraHasIt && !propertyExists) { - // It wasn't created during initialization because it only exists dependent on another - // property value - InitLineSourceStandardProperty(); - } - if (cameraHasIt) { - std::vector values = GetAvailableEnumValues(*node); - int ret = SetLineSourceStandardPropertyValues(values); - assert(ret == DEVICE_OK); - } -} - -void BaslerCamera::ReloadEventNotificationStandardPropertyValues() { - CEnumerationPtr node(nodeMap_->GetNode("EventNotification")); - bool cameraHasIt = IsAvailable(node); - bool propertyExists = HasStandardProperty("EventNotification"); - if (cameraHasIt && !propertyExists) { - // It wasn't created during initialization because it only exists dependent on another - // property value - InitEventNotificationStandardProperty(); - } - if (cameraHasIt) { - std::vector values = GetAvailableEnumValues(*node); - int ret = SetEventNotificationStandardPropertyValues(values); - assert(ret == DEVICE_OK); - } -} - -void BaslerCamera::ReloadTriggerOverlapStandardPropertyValues() { - CEnumerationPtr node(nodeMap_->GetNode("TriggerOverlap")); - bool cameraHasIt = IsAvailable(node); - bool propertyExists = HasStandardProperty("TriggerOverlap"); - if (cameraHasIt && !propertyExists) { - // It wasn't created during initialization because it only exists dependent on another - // property value - InitTriggerOverlapStandardProperty(); - } - if (cameraHasIt) { - std::vector values = GetAvailableEnumValues(*node); - int ret = SetTriggerOverlapStandardPropertyValues(values); - assert(ret == DEVICE_OK); - } -} - - // We map GenICam booleans to "0" and "1" -void BaslerCamera::ReloadLineInverterStandardPropertyValues() { - CBooleanPtr lineInverter(nodeMap_->GetNode("LineInverter")); - bool propertyExists = HasStandardProperty("LineInverter"); - if (IsAvailable(lineInverter) && !propertyExists) { - // It wasn't created during initialization because it only exists dependent on another - // property value - InitLineInverterStandardProperty(); - } - if (IsAvailable(lineInverter)) { - std::string currentValue = lineInverter->ToString(); - std::vector values = {"0", "1"}; - int ret = SetLineInverterStandardPropertyValues(values); - assert(ret == DEVICE_OK); - } -} - int BaslerCamera::OnBinningMode(MM::PropertyBase* pProp, MM::ActionType eAct) { CEnumerationPtr BinningModeHorizontal(nodeMap_->GetNode("BinningModeHorizontal")); @@ -2568,253 +2406,384 @@ void BufferInserter::OnImageGrabbed(CInstantCamera& /* camera */, const CGrabRes } } -int BaslerCamera::InitTriggerSelectorStandardProperty() +int BaslerCamera::InitOrSyncTriggerSelectorStandardProperty() { - CEnumerationPtr triggerSelector(nodeMap_->GetNode("TriggerSelector")); - if (IsAvailable(triggerSelector)) { - std::string currentValue = triggerSelector->ToString(); - CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnTriggerSelector); - // Get available values from the camera - std::vector values = GetAvailableEnumValues(*triggerSelector); - int code = CreateTriggerSelectorStandardProperty(currentValue.c_str(), values, action); - assert(code == DEVICE_OK); - return code; - } else { - SkipTriggerSelectorStandardProperty(); - return DEVICE_OK; - } + 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); + } + + return DEVICE_OK; } -int BaslerCamera::InitTriggerModeStandardProperty() +int BaslerCamera::InitOrSyncTriggerModeStandardProperty() { - CEnumerationPtr triggerMode(nodeMap_->GetNode("TriggerMode")); - if (IsAvailable(triggerMode)) { - std::string currentValue = triggerMode->ToString(); - CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnTriggerMode); - // Get available values from the camera - std::vector values = GetAvailableEnumValues(*triggerMode); - int code = CreateTriggerModeStandardProperty(currentValue.c_str(), values, action); - assert(code == DEVICE_OK); - return code; - } else { - SkipTriggerModeStandardProperty(); - return DEVICE_OK; - } + 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); + } + + return DEVICE_OK; } -int BaslerCamera::InitTriggerSourceStandardProperty() +int BaslerCamera::InitOrSyncTriggerSourceStandardProperty() { - CEnumerationPtr triggerSource(nodeMap_->GetNode("TriggerSource")); - if (IsAvailable(triggerSource)) { - std::string currentValue = triggerSource->ToString(); - CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnTriggerSource); - // Get available values from the camera - std::vector values = GetAvailableEnumValues(*triggerSource); - int code = CreateTriggerSourceStandardProperty(currentValue.c_str(), values, action); - assert(code == DEVICE_OK); - return code; - } else { - SkipTriggerSourceStandardProperty(); - return DEVICE_OK; - } + 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); + } + + return DEVICE_OK; } -int BaslerCamera::InitTriggerActivationStandardProperty() +int BaslerCamera::InitOrSyncTriggerActivationStandardProperty() { - CEnumerationPtr triggerActivation(nodeMap_->GetNode("TriggerActivation")); - if (IsAvailable(triggerActivation)) { - std::string currentValue = triggerActivation->ToString(); - CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnTriggerActivation); - // Get available values from the camera - std::vector values = GetAvailableEnumValues(*triggerActivation); - int code = CreateTriggerActivationStandardProperty(currentValue.c_str(), values, action); - assert(code == DEVICE_OK); - return code; - } else { - SkipTriggerActivationStandardProperty(); - return DEVICE_OK; - } + 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); + } + + return DEVICE_OK; } -int BaslerCamera::InitTriggerDelayStandardProperty() +int BaslerCamera::InitOrSyncTriggerDelayStandardProperty() { - CFloatPtr triggerDelay(nodeMap_->GetNode("TriggerDelay")); - if (IsAvailable(triggerDelay)) { - double currentValue = triggerDelay->GetValue(); - double minValue = triggerDelay->GetMin(); - double maxValue = triggerDelay->GetMax(); - std::string strValue = CDeviceUtils::ConvertToString(currentValue); - CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnTriggerDelay); - int code = CreateTriggerDelayStandardProperty(strValue.c_str(), minValue, maxValue, action); - assert(code == DEVICE_OK); - return code; - } else { - SkipTriggerDelayStandardProperty(); - return DEVICE_OK; - } + 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(); + } + // For float properties, we don't need to update allowed values, as they're set by min/max + + return DEVICE_OK; } -int BaslerCamera::InitTriggerOverlapStandardProperty() +int BaslerCamera::InitOrSyncTriggerOverlapStandardProperty() { - CEnumerationPtr triggerOverlap(nodeMap_->GetNode("TriggerOverlap")); - if (IsAvailable(triggerOverlap)) { - std::string currentValue = triggerOverlap->ToString(); - CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnTriggerOverlap); - // Get available values from the camera - std::vector values = GetAvailableEnumValues(*triggerOverlap); - int code = CreateTriggerOverlapStandardProperty(currentValue.c_str(), values, action); - assert(code == DEVICE_OK); - return code; - } else { - SkipTriggerOverlapStandardProperty(); - return DEVICE_OK; - } + 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); + } + + return DEVICE_OK; } -int BaslerCamera::InitExposureModeStandardProperty() +int BaslerCamera::InitOrSyncExposureModeStandardProperty() { - CEnumerationPtr exposureMode(nodeMap_->GetNode("ExposureMode")); - if (IsAvailable(exposureMode)) { - std::string currentValue = exposureMode->ToString(); - CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnExposureMode); - // Get available values from the camera - std::vector values = GetAvailableEnumValues(*exposureMode); - int code = CreateExposureModeStandardProperty(currentValue.c_str(), values, action); - assert(code == DEVICE_OK); - return code; - } else { - SkipExposureModeStandardProperty(); - return DEVICE_OK; - } + 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); + } + + return DEVICE_OK; } -int BaslerCamera::InitExposureTimeStandardProperty() +int BaslerCamera::InitOrSyncExposureTimeStandardProperty() { - CFloatPtr exposureTime(nodeMap_->GetNode("ExposureTime")); - if (IsAvailable(exposureTime)) { - double currentValue = exposureTime->GetValue(); - double minValue = exposureTime->GetMin(); - double maxValue = exposureTime->GetMax(); - - std::string strValue = CDeviceUtils::ConvertToString(currentValue); - CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnExposureTime); - int code = CreateExposureTimeStandardProperty(strValue.c_str(), minValue, maxValue, action); - assert(code == DEVICE_OK); - return code; - } else { - SkipExposureTimeStandardProperty(); - return DEVICE_OK; - } + 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(); + } + // For float properties, we don't need to update allowed values, as they're set by min/max + + return DEVICE_OK; } -int BaslerCamera::InitLineSelectorStandardProperty() +int BaslerCamera::InitOrSyncLineSelectorStandardProperty() { - CEnumerationPtr lineSelector(nodeMap_->GetNode("LineSelector")); - if (IsAvailable(lineSelector)) { - std::string currentValue = lineSelector->ToString(); - CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnLineSelector); - // Get available values from the camera - std::vector values = GetAvailableEnumValues(*lineSelector); - int code = CreateLineSelectorStandardProperty(currentValue.c_str(), values, action); - assert(code == DEVICE_OK); - return code; - } else { - SkipLineSelectorStandardProperty(); - return DEVICE_OK; - } + 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); + } + + return DEVICE_OK; } -int BaslerCamera::InitLineModeStandardProperty() +int BaslerCamera::InitOrSyncLineModeStandardProperty() { - CEnumerationPtr lineMode(nodeMap_->GetNode("LineMode")); - if (IsAvailable(lineMode)) { - std::string currentValue = lineMode->ToString(); - CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnLineMode); - // Get available values from the camera - std::vector values = GetAvailableEnumValues(*lineMode); - int code = CreateLineModeStandardProperty(currentValue.c_str(), values, action); - assert(code == DEVICE_OK); - return code; - } else { - SkipLineModeStandardProperty(); - return DEVICE_OK; - } + 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); + } + + return DEVICE_OK; } -int BaslerCamera::InitLineInverterStandardProperty() +int BaslerCamera::InitOrSyncLineInverterStandardProperty() { - CBooleanPtr lineInverter(nodeMap_->GetNode("LineInverter")); - if (IsAvailable(lineInverter)) { - std::string currentValue = lineInverter->ToString(); - CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnLineInverter); - // Get available values for boolean - std::vector values = {"0", "1"}; - int code = CreateLineInverterStandardProperty(currentValue.c_str(), values, action); - assert(code == DEVICE_OK); - return code; - } else { - SkipLineInverterStandardProperty(); - return DEVICE_OK; - } + 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); + } + + return DEVICE_OK; } -int BaslerCamera::InitLineSourceStandardProperty() +int BaslerCamera::InitOrSyncLineSourceStandardProperty() { - CEnumerationPtr lineSource(nodeMap_->GetNode("LineSource")); - if (IsAvailable(lineSource)) { - std::string currentValue = lineSource->ToString(); - CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnLineSource); - // Get available values from the camera - std::vector values = GetAvailableEnumValues(*lineSource); - int code = CreateLineSourceStandardProperty(currentValue.c_str(), values, action); - assert(code == DEVICE_OK); - return code; - } else { - SkipLineSourceStandardProperty(); - return DEVICE_OK; - } + 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); + } + + return DEVICE_OK; } -int BaslerCamera::InitLineStatusStandardProperty() +int BaslerCamera::InitOrSyncLineStatusStandardProperty() { - CBooleanPtr lineStatus(nodeMap_->GetNode("LineStatus")); - if (IsAvailable(lineStatus)) { - std::string currentValue = lineStatus->ToString(); - CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnLineStatus); - // LineStatus is read-only - int code = CreateLineStatusStandardProperty(currentValue.c_str(), action); - assert(code == DEVICE_OK); - return code; - } else { - SkipLineStatusStandardProperty(); - return DEVICE_OK; - } + 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(); + } + // No need to update values for read-only properties + + return DEVICE_OK; } -int BaslerCamera::InitEventSelectorStandardProperty() +int BaslerCamera::InitOrSyncEventSelectorStandardProperty() { - CEnumerationPtr eventSelector(nodeMap_->GetNode("EventSelector")); - if (IsAvailable(eventSelector)) { - CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnEventSelector); - std::string currentValue = eventSelector->ToString(); - std::vector values = GetAvailableEnumValues(*eventSelector); - int code = CreateEventSelectorStandardProperty(currentValue.c_str(), values, action); - assert(code == DEVICE_OK); - return code; - } - return DEVICE_OK; + 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); + } + + return DEVICE_OK; } -int BaslerCamera::InitEventNotificationStandardProperty() +int BaslerCamera::InitOrSyncEventNotificationStandardProperty() { - CEnumerationPtr eventNotification(nodeMap_->GetNode("EventNotification")); - if (IsAvailable(eventNotification)) { - CPropertyAction* action = new CPropertyAction(this, &BaslerCamera::OnEventNotification); - std::string currentValue = eventNotification->ToString(); - // This one has required values of On and Off - int code = CreateEventNotificationStandardProperty(currentValue.c_str(), action); - assert(code == DEVICE_OK); - return code; - } - return DEVICE_OK; + 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); + } + + return DEVICE_OK; } diff --git a/DeviceAdapters/Basler/BaslerPylonCamera.h b/DeviceAdapters/Basler/BaslerPylonCamera.h index f115fd96d..70a7a6cdf 100644 --- a/DeviceAdapters/Basler/BaslerPylonCamera.h +++ b/DeviceAdapters/Basler/BaslerPylonCamera.h @@ -155,17 +155,7 @@ class BaslerCamera : public CNewAPICameraBase { int OnWidth(MM::PropertyBase* pProp, MM::ActionType eAct); - // For dyanmic standard property value updates: - void ReloadTriggerModeStandardPropertyValues(); - void ReloadTriggerSourceStandardPropertyValues(); - void ReloadTriggerActivationStandardPropertyValues(); - void ReloadExposureModeStandardPropertyValues(); - void ReloadEventSelectorStandardPropertyValues(); - void ReloadLineModeStandardPropertyValues(); - void ReloadLineInverterStandardPropertyValues(); - void ReloadLineSourceStandardPropertyValues(); - void ReloadEventNotificationStandardPropertyValues(); - void ReloadTriggerOverlapStandardPropertyValues(); + // Convenience functions std::string NodeToString(const char* str) const; @@ -249,11 +239,22 @@ class BaslerCamera : public CNewAPICameraBase { //MM::MMTime startTime_; + 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(); - - - - }; //Enumeration used for distinguishing different events. diff --git a/MMDevice/DeviceBase.h b/MMDevice/DeviceBase.h index 82164c8ff..d3d1155df 100644 --- a/MMDevice/DeviceBase.h +++ b/MMDevice/DeviceBase.h @@ -145,6 +145,10 @@ class CDeviceBase : public T return CreateStandardProperty(value, pAct, values); } + int SetTriggerSelectorStandardPropertyValues(const std::vector& values) { + return SetStandardPropertyValues(values); + } + void SkipTriggerSelectorStandardProperty() { SkipStandardProperty(); } @@ -237,6 +241,10 @@ class CDeviceBase : public T return CreateStandardProperty(value, pAct, values); } + int SetLineSelectorStandardPropertyValues(const std::vector& values) { + return SetStandardPropertyValues(values); + } + void SkipLineSelectorStandardProperty() { SkipStandardProperty(); } diff --git a/MMDevice/MMDevice.h b/MMDevice/MMDevice.h index e241d23c2..3c2c0e116 100644 --- a/MMDevice/MMDevice.h +++ b/MMDevice/MMDevice.h @@ -312,26 +312,12 @@ namespace MM { // Boolean values get mapped to "0" or "1" since MM properties do not support booleans // Selects the type of trigger to configure. - static const std::vector triggerSelectorValues = { - g_keyword_TriggerSelectorAcquisitionStart, - g_keyword_TriggerSelectorAcquisitionEnd, - g_keyword_TriggerSelectorAcquisitionActive, - g_keyword_TriggerSelectorFrameBurstStart, - g_keyword_TriggerSelectorFrameBurstEnd, - g_keyword_TriggerSelectorFrameBurstActive, - g_keyword_TriggerSelectorFrameStart, - g_keyword_TriggerSelectorFrameEnd, - g_keyword_TriggerSelectorFrameActive, - g_keyword_TriggerSelectorExposureStart, - g_keyword_TriggerSelectorExposureEnd, - g_keyword_TriggerSelectorExposureActive - }; static const MM::StandardProperty g_TriggerSelectorProperty{ "TriggerSelector", // name String, // type false, // isReadOnly false, // isPreInit - triggerSelectorValues, // allowedValues + {}, // allowedValues {}, // no required values for now PropertyLimitUndefined, // lowerLimit PropertyLimitUndefined, // upperLimit From 9ca35fa20e00217141a581cc2c82e8df324e0d80 Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Fri, 7 Mar 2025 13:04:10 -0800 Subject: [PATCH 31/50] fix method name --- MMDevice/DeviceBase.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MMDevice/DeviceBase.h b/MMDevice/DeviceBase.h index 55a25c830..9d452cf35 100644 --- a/MMDevice/DeviceBase.h +++ b/MMDevice/DeviceBase.h @@ -1422,7 +1422,7 @@ class CDeviceBase : public T // 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_.DeleteProperty(fullName.c_str()); + properties_.Delete(fullName.c_str()); } } } From 0a5b88b5770fc3a9d3294e695cb68123cd9b71b6 Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Fri, 7 Mar 2025 13:16:12 -0800 Subject: [PATCH 32/50] readd skips --- DeviceAdapters/Basler/BaslerPylonCamera.cpp | 78 +++++++++++++++++---- 1 file changed, 63 insertions(+), 15 deletions(-) diff --git a/DeviceAdapters/Basler/BaslerPylonCamera.cpp b/DeviceAdapters/Basler/BaslerPylonCamera.cpp index 73f7b0251..64af4fe32 100644 --- a/DeviceAdapters/Basler/BaslerPylonCamera.cpp +++ b/DeviceAdapters/Basler/BaslerPylonCamera.cpp @@ -2427,7 +2427,10 @@ int BaslerCamera::InitOrSyncTriggerSelectorStandardProperty() // 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; } @@ -2453,7 +2456,10 @@ int BaslerCamera::InitOrSyncTriggerModeStandardProperty() // 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; } @@ -2479,7 +2485,10 @@ int BaslerCamera::InitOrSyncTriggerSourceStandardProperty() // 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; } @@ -2505,7 +2514,10 @@ int BaslerCamera::InitOrSyncTriggerActivationStandardProperty() // 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; } @@ -2528,7 +2540,11 @@ int BaslerCamera::InitOrSyncTriggerDelayStandardProperty() 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; @@ -2555,7 +2571,10 @@ int BaslerCamera::InitOrSyncTriggerOverlapStandardProperty() // 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; } @@ -2581,7 +2600,10 @@ int BaslerCamera::InitOrSyncExposureModeStandardProperty() // 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; } @@ -2604,7 +2626,11 @@ int BaslerCamera::InitOrSyncExposureTimeStandardProperty() 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; @@ -2631,7 +2657,10 @@ int BaslerCamera::InitOrSyncLineSelectorStandardProperty() // 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; } @@ -2657,7 +2686,10 @@ int BaslerCamera::InitOrSyncLineModeStandardProperty() // 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; } @@ -2683,7 +2715,10 @@ int BaslerCamera::InitOrSyncLineInverterStandardProperty() // 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; } @@ -2709,7 +2744,10 @@ int BaslerCamera::InitOrSyncLineSourceStandardProperty() // 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; } @@ -2730,7 +2768,11 @@ int BaslerCamera::InitOrSyncLineStatusStandardProperty() 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; @@ -2757,7 +2799,10 @@ int BaslerCamera::InitOrSyncEventSelectorStandardProperty() // 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; } @@ -2783,7 +2828,10 @@ int BaslerCamera::InitOrSyncEventNotificationStandardProperty() // 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; } From e1ebc2766a65e14896210c1fffeeca9a95d4ddbc Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Fri, 7 Mar 2025 13:19:11 -0800 Subject: [PATCH 33/50] remove from map correctly --- MMDevice/Property.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/MMDevice/Property.cpp b/MMDevice/Property.cpp index 93fcf5575..32e8e72dd 100644 --- a/MMDevice/Property.cpp +++ b/MMDevice/Property.cpp @@ -546,6 +546,9 @@ 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; From f34266d6e45afa391fb400fdee9d8116bdfede67 Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Fri, 7 Mar 2025 15:53:28 -0800 Subject: [PATCH 34/50] generic event handler --- DeviceAdapters/Basler/BaslerPylonCamera.cpp | 47 ++++++++++++++++----- DeviceAdapters/Basler/BaslerPylonCamera.h | 8 ++-- 2 files changed, 41 insertions(+), 14 deletions(-) diff --git a/DeviceAdapters/Basler/BaslerPylonCamera.cpp b/DeviceAdapters/Basler/BaslerPylonCamera.cpp index 64af4fe32..25b7926df 100644 --- a/DeviceAdapters/Basler/BaslerPylonCamera.cpp +++ b/DeviceAdapters/Basler/BaslerPylonCamera.cpp @@ -166,7 +166,7 @@ BaslerCamera::BaslerCamera() : shutterMode_("None"), nodeMap_(NULL), initialized_(false), - pTempHandler_(NULL), + pEventHandler_(NULL), camera_(NULL), multiFrameAcqCount_(0) { @@ -383,15 +383,37 @@ int BaslerCamera::Initialize() if (camera_->EventSelector.IsWritable()) { + pEventHandler_ = new CMMCameraEventHandler(this); + if (camera_->EventSelector.CanSetValue(EventSelector_CriticalTemperature) && - camera_->EventSelector.CanSetValue(EventSelector_OverTemperature) - ) + 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); + // Temperature update events + camera_->RegisterCameraEventHandler(pEventHandler_, "EventCriticalTemperatureData", static_cast(0), RegistrationMode_Append, Cleanup_None); } + + + // Register the handler for each type of event that might be + std::vector eventSelectorValues = GetAvailableEnumValues(camera_->EventSelector); + for (const auto& eventValue : eventSelectorValues) { + // Construct the event data node name + std::string eventDataNodeName = "Event" + eventValue + "Data"; + + // Register the event handler + try { + camera_->RegisterCameraEventHandler( + pEventHandler_, + eventDataNodeName.c_str(), + static_cast(0), + RegistrationMode_Append, + Cleanup_None + ); + } + catch (const GenericException& e) { + return DEVICE_ERR; + } + } + } //Register Genicam Callback to be informed if on any changes on resulting frame rate. @@ -1370,7 +1392,12 @@ int BaslerCamera::OnEventNotification(MM::PropertyBase* pProp, MM::ActionType eA int BaslerCamera::OnLineMode(MM::PropertyBase* pProp, MM::ActionType eAct) { - return HandleEnumerationProperty(pProp, eAct, "LineMode"); + 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) @@ -2308,11 +2335,11 @@ 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; diff --git a/DeviceAdapters/Basler/BaslerPylonCamera.h b/DeviceAdapters/Basler/BaslerPylonCamera.h index 70a7a6cdf..08858a70b 100644 --- a/DeviceAdapters/Basler/BaslerPylonCamera.h +++ b/DeviceAdapters/Basler/BaslerPylonCamera.h @@ -60,7 +60,7 @@ enum ////////////////////////////////////////////////////////////////////////////// //Callback class for putting frames in circular buffer as they arrive -class CTempCameraEventHandler; +class CMMCameraEventHandler; class BufferInserter; class BaslerCamera : public CNewAPICameraBase { public: @@ -205,7 +205,7 @@ class BaslerCamera : public CNewAPICameraBase { CBaslerUniversalInstantCamera *camera_; - CTempCameraEventHandler *pTempHandler_; + CMMCameraEventHandler *pEventHandler_; int nComponents_; unsigned bitDepth_; @@ -269,12 +269,12 @@ 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); }; From e4c396df8ab3c59cb0c5adf81c797adda5cf52e2 Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Fri, 7 Mar 2025 16:26:06 -0800 Subject: [PATCH 35/50] backwards compat for sequences in new camera API --- DeviceAdapters/Basler/BaslerPylonCamera.cpp | 99 ++++++++++++++++++--- MMCore/Devices/CameraInstance.cpp | 58 +++++++++++- MMDevice/DeviceBase.h | 7 +- 3 files changed, 143 insertions(+), 21 deletions(-) diff --git a/DeviceAdapters/Basler/BaslerPylonCamera.cpp b/DeviceAdapters/Basler/BaslerPylonCamera.cpp index 25b7926df..94b7f1288 100644 --- a/DeviceAdapters/Basler/BaslerPylonCamera.cpp +++ b/DeviceAdapters/Basler/BaslerPylonCamera.cpp @@ -2341,19 +2341,92 @@ CMMCameraEventHandler::CMMCameraEventHandler(BaslerCamera* dev) : void CMMCameraEventHandler::OnCameraEvent(CBaslerUniversalInstantCamera& camera, intptr_t userProvidedId, GenApi::INode* pNode) { - { - (void)camera; - (void)userProvidedId; - CParameter value(pNode); - if (value.IsValid()) - { - stringstream msg; - msg << "Temperature State of camera has been changed to : " << camera.TemperatureState.ToString(); - dev_->AddToLog(msg.str()); - dev_->UpdateTemperature(); - } - std::cout << std::endl; - } + // Cast parameters to void to avoid unused parameter warnings + (void)userProvidedId; + + // Get the node name + CParameter parameter(pNode); + if (!parameter.IsValid()) + return; + + // Get the node name + std::string nodeName = parameter.GetNode()->GetName().c_str(); + nodeName += " "; + + // // Build a message with event details + // stringstream msg; + // msg << "Camera Event: " << nodeName; + + // // Handle specific event types + // if (nodeName.find("Temperature") != std::string::npos) { + // // Temperature event + // msg << " - Temperature State: " << camera.TemperatureState.ToString(); + // dev_->UpdateTemperature(); + // } + // else if (nodeName.find("ExposureEnd") != std::string::npos) { + // // Exposure end event + // if (IsAvailable(camera.EventExposureEndFrameID)) { + // msg << " - Frame ID: " << camera.EventExposureEndFrameID.GetValue(); + // } + // if (IsAvailable(camera.EventExposureEndTimestamp)) { + // msg << " - Timestamp: " << camera.EventExposureEndTimestamp.GetValue(); + // } + // } + // else if (nodeName.find("FrameStart") != std::string::npos) { + // // Frame start event + // if (IsAvailable(camera.EventFrameStartFrameID)) { + // msg << " - Frame ID: " << camera.EventFrameStartFrameID.GetValue(); + // } + // if (IsAvailable(camera.EventFrameStartTimestamp)) { + // msg << " - Timestamp: " << camera.EventFrameStartTimestamp.GetValue(); + // } + // } + // else if (nodeName.find("FrameTrigger") != std::string::npos) { + // // Frame trigger event + // if (IsAvailable(camera.EventFrameTriggerFrameID)) { + // msg << " - Frame ID: " << camera.EventFrameTriggerFrameID.GetValue(); + // } + // if (IsAvailable(camera.EventFrameTriggerTimestamp)) { + // msg << " - Timestamp: " << camera.EventFrameTriggerTimestamp.GetValue(); + // } + // } + // else if (nodeName.find("Line") != std::string::npos) { + // // Line event (e.g., LineRisingEdge, LineFallingEdge) + // if (nodeName.find("RisingEdge") != std::string::npos && IsAvailable(camera.EventLineRisingEdgeTimestamp)) { + // msg << " - Timestamp: " << camera.EventLineRisingEdgeTimestamp.GetValue(); + // } + // else if (nodeName.find("FallingEdge") != std::string::npos && IsAvailable(camera.EventLineFallingEdgeTimestamp)) { + // msg << " - Timestamp: " << camera.EventLineFallingEdgeTimestamp.GetValue(); + // } + // } + + // // Try to get any additional data from the event + // try { + // // For integer nodes + // CIntegerPtr intNode = dynamic_cast(pNode); + // if (intNode && IsReadable(intNode)) { + // msg << " - Value: " << intNode->GetValue(); + // } + + // // For float nodes + // CFloatPtr floatNode = dynamic_cast(pNode); + // if (floatNode && IsReadable(floatNode)) { + // msg << " - Value: " << floatNode->GetValue(); + // } + + // // For enum nodes + // CEnumerationPtr enumNode = dynamic_cast(pNode); + // if (enumNode && IsReadable(enumNode)) { + // msg << " - Value: " << enumNode->ToString(); + // } + // } + // catch (const GenericException& e) { + // // Silently ignore errors when trying to read values + // msg << " (Error reading value: " << e.GetDescription() << ")"; + // } + + // // Log the event + // dev_->AddToLog(msg.str()); } BufferInserter::BufferInserter(BaslerCamera* dev) : diff --git a/MMCore/Devices/CameraInstance.cpp b/MMCore/Devices/CameraInstance.cpp index 8633d1b5d..81d7133d4 100644 --- a/MMCore/Devices/CameraInstance.cpp +++ b/MMCore/Devices/CameraInstance.cpp @@ -127,10 +127,60 @@ 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; + } + } +} + +int CameraInstance::StartSequenceAcquisition(double interval_ms) { + RequireInitialized(__func__); + + if (!GetImpl()->IsNewAPIImplemented()) { + return GetImpl()->StartSequenceAcquisition(interval_ms); + } else { + int ret = GetImpl()->AcquisitionArm(); + if (ret != DEVICE_OK) { + return ret; + } + ret = GetImpl()->AcquisitionStart(); + if (ret != DEVICE_OK) { + return ret; + } + } +} + +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() diff --git a/MMDevice/DeviceBase.h b/MMDevice/DeviceBase.h index dff7cce48..6a620ec38 100644 --- a/MMDevice/DeviceBase.h +++ b/MMDevice/DeviceBase.h @@ -2012,18 +2012,17 @@ class CNewAPICameraBase : public CAllCamerasBase // 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 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 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;} // TODO: do this in terms of the standard property // TODO: these are in ms but the standard property is in us From 3d7c29d1af350b379fa858ae26917f0612937d72 Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Fri, 7 Mar 2025 17:47:48 -0800 Subject: [PATCH 36/50] fix bug and only register handler once --- DeviceAdapters/Basler/BaslerPylonCamera.cpp | 39 ++++++++++++--------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/DeviceAdapters/Basler/BaslerPylonCamera.cpp b/DeviceAdapters/Basler/BaslerPylonCamera.cpp index 94b7f1288..1c79fcf65 100644 --- a/DeviceAdapters/Basler/BaslerPylonCamera.cpp +++ b/DeviceAdapters/Basler/BaslerPylonCamera.cpp @@ -213,7 +213,6 @@ BaslerCamera::BaslerCamera() : BaslerCamera::~BaslerCamera() { - if (Buffer4ContinuesShot != NULL) if (Buffer4ContinuesShot != NULL) { free(Buffer4ContinuesShot); @@ -921,6 +920,12 @@ int BaslerCamera::Initialize() << e.GetDescription() << endl; return DEVICE_ERR; } + + + ImageHandler_ = new BufferInserter(this); + camera_->RegisterImageEventHandler(ImageHandler_, RegistrationMode_Append, Cleanup_Delete); + + return DEVICE_OK; } @@ -959,9 +964,6 @@ int BaslerCamera::AcquisitionArm(int frameCount) } } - ImageHandler_ = new BufferInserter(this); - camera_->RegisterImageEventHandler(ImageHandler_, RegistrationMode_Append, Cleanup_Delete); - return DEVICE_OK; } @@ -977,18 +979,21 @@ int BaslerCamera::AcquisitionStart() // 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; + if (ret != DEVICE_OK) + return ret; - //The GenICam AcquisitionStart gets called automatically by StartGrabbing - if (multiFrameAcqCount_ == 0) { - // acquire until told to stop - camera_->StartGrabbing(GrabStrategy_OneByOne, GrabLoop_ProvidedByInstantCamera); - } else { - // acquire a set number of frames - camera_->StartGrabbing(multiFrameAcqCount_, GrabStrategy_OneByOne, GrabLoop_ProvidedByInstantCamera); - } - return DEVICE_OK; + // 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() @@ -996,7 +1001,6 @@ int BaslerCamera::AcquisitionStop() // The GenICam AcquisitionStop gets called automatically by StopGrabbing // CCommandParameter(nodeMap_, "AcquisitionStop").Execute(); camera_->StopGrabbing(); - camera_->DeregisterImageEventHandler(ImageHandler_); // This tells the core that the acquisition is finished. // so that it can close the current shutter. int ret = GetCoreCallback()->AcqFinished(this, 0); @@ -1081,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_; } From f345c8ff4b8dd9237638a26840886aacbecacd09 Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Fri, 7 Mar 2025 17:48:29 -0800 Subject: [PATCH 37/50] allow snap to call insertImage instead of keeping its own buffer --- MMCore/CoreCallback.cpp | 24 +++++++++ MMCore/Devices/CameraInstance.cpp | 84 +++++++++++++++++++++++++++++-- MMCore/Devices/CameraInstance.h | 23 +++++++++ 3 files changed, 127 insertions(+), 4 deletions(-) diff --git a/MMCore/CoreCallback.cpp b/MMCore/CoreCallback.cpp index 572f9a0ce..4dd831693 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, 1); + 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, 1); + return DEVICE_OK; + } + } + try { Metadata md = AddCameraMetadata(caller, pMd); diff --git a/MMCore/Devices/CameraInstance.cpp b/MMCore/Devices/CameraInstance.cpp index 81d7133d4..de34f991f 100644 --- a/MMCore/Devices/CameraInstance.cpp +++ b/MMCore/Devices/CameraInstance.cpp @@ -22,10 +22,57 @@ #include "CameraInstance.h" -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(); } +int CameraInstance::SnapImage() { + RequireInitialized(__func__); + isSnapping_.store(true); + int ret = DEVICE_OK; + if (!GetImpl()->IsNewAPIImplemented()) { + ret = GetImpl()->SnapImage(); + } else { + ret = GetImpl()->AcquisitionArm(1); + if (ret != DEVICE_OK) { + isSnapping_.store(false); + return ret; + } + ret = GetImpl()->AcquisitionStart(); + + // Use condition variable to wait for image capture + std::unique_lock lock(imageMutex_); + imageAvailable_.wait(lock, [this]() { + return !GetImpl()->IsCapturing() || !isSnapping_.load(); + }); + } + 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(); +} + unsigned CameraInstance::GetNumberOfComponents() const { RequireInitialized(__func__); return GetImpl()->GetNumberOfComponents(); } std::string CameraInstance::GetComponentName(unsigned component) @@ -204,3 +251,32 @@ 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, unsigned nComponents) { + // Calculate total buffer size needed + size_t totalBytes = width * height * byteDepth * nComponents; + + // 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); + } + + // Assume channel 0 + snappedImage_.SetPixels(0, buf); + + // now ready for GetImage to be called + isSnapping_.store(false); + + // 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..2cd47f539 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 @@ -82,4 +86,23 @@ 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, unsigned nComponents); + +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_; }; From 6adb4bf8528a5f66de0bcd6657caf00d5ab986cb Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Fri, 7 Mar 2025 17:48:29 -0800 Subject: [PATCH 38/50] allow snap to call insertImage instead of keeping its own buffer --- MMCore/CoreCallback.cpp | 24 +++++++++ MMCore/Devices/CameraInstance.cpp | 84 +++++++++++++++++++++++++++++-- MMCore/Devices/CameraInstance.h | 23 +++++++++ 3 files changed, 127 insertions(+), 4 deletions(-) diff --git a/MMCore/CoreCallback.cpp b/MMCore/CoreCallback.cpp index 572f9a0ce..4dd831693 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, 1); + 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, 1); + return DEVICE_OK; + } + } + try { Metadata md = AddCameraMetadata(caller, pMd); diff --git a/MMCore/Devices/CameraInstance.cpp b/MMCore/Devices/CameraInstance.cpp index 8633d1b5d..9bf048f7e 100644 --- a/MMCore/Devices/CameraInstance.cpp +++ b/MMCore/Devices/CameraInstance.cpp @@ -22,10 +22,57 @@ #include "CameraInstance.h" -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(); } +int CameraInstance::SnapImage() { + RequireInitialized(__func__); + isSnapping_.store(true); + int ret = DEVICE_OK; + if (!GetImpl()->IsNewAPIImplemented()) { + ret = GetImpl()->SnapImage(); + } else { + ret = GetImpl()->AcquisitionArm(1); + if (ret != DEVICE_OK) { + isSnapping_.store(false); + return ret; + } + ret = GetImpl()->AcquisitionStart(); + + // Use condition variable to wait for image capture + std::unique_lock lock(imageMutex_); + imageAvailable_.wait(lock, [this]() { + return !GetImpl()->IsCapturing() || !isSnapping_.load(); + }); + } + 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(); +} + unsigned CameraInstance::GetNumberOfComponents() const { RequireInitialized(__func__); return GetImpl()->GetNumberOfComponents(); } std::string CameraInstance::GetComponentName(unsigned component) @@ -154,3 +201,32 @@ 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, unsigned nComponents) { + // Calculate total buffer size needed + size_t totalBytes = width * height * byteDepth * nComponents; + + // 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); + } + + // Assume channel 0 + snappedImage_.SetPixels(0, buf); + + // now ready for GetImage to be called + isSnapping_.store(false); + + // 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..2cd47f539 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 @@ -82,4 +86,23 @@ 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, unsigned nComponents); + +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_; }; From 8a0dd5de8c02ca93cbb6ee04377bec4b1a299c91 Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Sat, 8 Mar 2025 15:29:57 -0800 Subject: [PATCH 39/50] remove future new camera api changes --- MMCore/Devices/CameraInstance.cpp | 23 +---------------------- MMCore/Devices/CameraInstance.h | 5 ----- 2 files changed, 1 insertion(+), 27 deletions(-) diff --git a/MMCore/Devices/CameraInstance.cpp b/MMCore/Devices/CameraInstance.cpp index 9bf048f7e..791518525 100644 --- a/MMCore/Devices/CameraInstance.cpp +++ b/MMCore/Devices/CameraInstance.cpp @@ -25,23 +25,7 @@ int CameraInstance::SnapImage() { RequireInitialized(__func__); isSnapping_.store(true); - int ret = DEVICE_OK; - if (!GetImpl()->IsNewAPIImplemented()) { - ret = GetImpl()->SnapImage(); - } else { - ret = GetImpl()->AcquisitionArm(1); - if (ret != DEVICE_OK) { - isSnapping_.store(false); - return ret; - } - ret = GetImpl()->AcquisitionStart(); - - // Use condition variable to wait for image capture - std::unique_lock lock(imageMutex_); - imageAvailable_.wait(lock, [this]() { - return !GetImpl()->IsCapturing() || !isSnapping_.load(); - }); - } + int ret = GetImpl()->SnapImage(); isSnapping_.store(false); return ret; } @@ -224,9 +208,4 @@ void CameraInstance::StoreSnappedImage(const unsigned char* buf, unsigned width, // now ready for GetImage to be called isSnapping_.store(false); - // 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 2cd47f539..6e16383c1 100644 --- a/MMCore/Devices/CameraInstance.h +++ b/MMCore/Devices/CameraInstance.h @@ -21,8 +21,6 @@ #include "DeviceInstanceBase.h" #include -#include -#include #include "../FrameBuffer.h" @@ -102,7 +100,4 @@ class CameraInstance : public DeviceInstanceBase // Frame buffer to store captured images mm::FrameBuffer snappedImage_; - - std::mutex imageMutex_; - std::condition_variable imageAvailable_; }; From 6183045a54a0bb8b9ac019ac0f680eb067d33fd4 Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Sat, 8 Mar 2025 15:53:29 -0800 Subject: [PATCH 40/50] fix ncomponents --- MMCore/CoreCallback.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MMCore/CoreCallback.cpp b/MMCore/CoreCallback.cpp index 4dd831693..465b375aa 100644 --- a/MMCore/CoreCallback.cpp +++ b/MMCore/CoreCallback.cpp @@ -302,7 +302,7 @@ int CoreCallback::InsertImage(const MM::Device* caller, const unsigned char* buf 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, 1); + camera->StoreSnappedImage(buf, width, height, byteDepth, nComponents); return DEVICE_OK; } } From aed4e97c62b4ac51541d172ecb072534f4c7e1bb Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Sat, 8 Mar 2025 15:57:42 -0800 Subject: [PATCH 41/50] changes for multi-channel cameras --- MMCore/Devices/CameraInstance.cpp | 9 ++++----- MMCore/Devices/CameraInstance.h | 1 + 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/MMCore/Devices/CameraInstance.cpp b/MMCore/Devices/CameraInstance.cpp index 791518525..11ba9869f 100644 --- a/MMCore/Devices/CameraInstance.cpp +++ b/MMCore/Devices/CameraInstance.cpp @@ -25,6 +25,7 @@ int CameraInstance::SnapImage() { RequireInitialized(__func__); isSnapping_.store(true); + insertImageCounter_.store(0); int ret = GetImpl()->SnapImage(); isSnapping_.store(false); return ret; @@ -202,10 +203,8 @@ void CameraInstance::StoreSnappedImage(const unsigned char* buf, unsigned width, snappedImage_.Resize(width, height, byteDepth); } - // Assume channel 0 - snappedImage_.SetPixels(0, buf); + // For multi-channel cameras, insertImage will be called once for each channel + snappedImage_.SetPixels(insertImageCounter_.load(), buf); + insertImageCounter_.fetch_add(1); - // now ready for GetImage to be called - isSnapping_.store(false); - } diff --git a/MMCore/Devices/CameraInstance.h b/MMCore/Devices/CameraInstance.h index 6e16383c1..514ae4f91 100644 --- a/MMCore/Devices/CameraInstance.h +++ b/MMCore/Devices/CameraInstance.h @@ -100,4 +100,5 @@ class CameraInstance : public DeviceInstanceBase // Frame buffer to store captured images mm::FrameBuffer snappedImage_; + std::atomic insertImageCounter_{0}; }; From b385397ecdd0623eb2ff88dd9c1e22e78a800ca8 Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Sat, 8 Mar 2025 16:04:07 -0800 Subject: [PATCH 42/50] change variable name --- MMCore/Devices/CameraInstance.cpp | 6 +++--- MMCore/Devices/CameraInstance.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/MMCore/Devices/CameraInstance.cpp b/MMCore/Devices/CameraInstance.cpp index 11ba9869f..a24d26c43 100644 --- a/MMCore/Devices/CameraInstance.cpp +++ b/MMCore/Devices/CameraInstance.cpp @@ -25,7 +25,7 @@ int CameraInstance::SnapImage() { RequireInitialized(__func__); isSnapping_.store(true); - insertImageCounter_.store(0); + multiChannelImageCounter_.store(0); int ret = GetImpl()->SnapImage(); isSnapping_.store(false); return ret; @@ -204,7 +204,7 @@ void CameraInstance::StoreSnappedImage(const unsigned char* buf, unsigned width, } // For multi-channel cameras, insertImage will be called once for each channel - snappedImage_.SetPixels(insertImageCounter_.load(), buf); - insertImageCounter_.fetch_add(1); + snappedImage_.SetPixels(multiChannelImageCounter_.load(), buf); + multiChannelImageCounter_.fetch_add(1); } diff --git a/MMCore/Devices/CameraInstance.h b/MMCore/Devices/CameraInstance.h index 514ae4f91..b76f1268d 100644 --- a/MMCore/Devices/CameraInstance.h +++ b/MMCore/Devices/CameraInstance.h @@ -100,5 +100,5 @@ class CameraInstance : public DeviceInstanceBase // Frame buffer to store captured images mm::FrameBuffer snappedImage_; - std::atomic insertImageCounter_{0}; + std::atomic multiChannelImageCounter_{0}; }; From 4b47590d28eb7de15ebf594b3ec1913f2c151e57 Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Sat, 8 Mar 2025 16:13:12 -0800 Subject: [PATCH 43/50] always return --- MMCore/Devices/CameraInstance.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MMCore/Devices/CameraInstance.cpp b/MMCore/Devices/CameraInstance.cpp index 48a1056ff..b9f28bacc 100644 --- a/MMCore/Devices/CameraInstance.cpp +++ b/MMCore/Devices/CameraInstance.cpp @@ -188,6 +188,7 @@ int CameraInstance::StartSequenceAcquisition(long numImages, double interval_ms, if (ret != DEVICE_OK) { return ret; } + return DEVICE_OK; } } @@ -205,6 +206,7 @@ int CameraInstance::StartSequenceAcquisition(double interval_ms) { if (ret != DEVICE_OK) { return ret; } + return DEVICE_OK; } } From 75764f1ad33ddf1db9c6f7bd209c14dca924b5f1 Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Sat, 8 Mar 2025 16:14:44 -0800 Subject: [PATCH 44/50] delete unused --- MMCore/Devices/CameraInstance.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/MMCore/Devices/CameraInstance.cpp b/MMCore/Devices/CameraInstance.cpp index a24d26c43..bf3ba7b0b 100644 --- a/MMCore/Devices/CameraInstance.cpp +++ b/MMCore/Devices/CameraInstance.cpp @@ -193,9 +193,6 @@ bool CameraInstance::IsSnapping() const { void CameraInstance::StoreSnappedImage(const unsigned char* buf, unsigned width, unsigned height, unsigned byteDepth, unsigned nComponents) { - // Calculate total buffer size needed - size_t totalBytes = width * height * byteDepth * nComponents; - // If the buffer doesn't exist or has wrong dimensions, create/resize it if (snappedImage_.Width() != width || snappedImage_.Height() != height || From 224e5e1bd5988aad181f1a2a7fd7d8ea1ef25c7a Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Sat, 8 Mar 2025 16:17:58 -0800 Subject: [PATCH 45/50] fix compiler warnings --- MMCore/CoreCallback.cpp | 4 ++-- MMCore/Devices/CameraInstance.cpp | 2 +- MMCore/Devices/CameraInstance.h | 3 +-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/MMCore/CoreCallback.cpp b/MMCore/CoreCallback.cpp index 465b375aa..15c9ab225 100644 --- a/MMCore/CoreCallback.cpp +++ b/MMCore/CoreCallback.cpp @@ -258,7 +258,7 @@ int CoreCallback::InsertImage(const MM::Device* caller, const unsigned char* buf 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, 1); + camera->StoreSnappedImage(buf, width, height, byteDepth); return DEVICE_OK; } } @@ -302,7 +302,7 @@ int CoreCallback::InsertImage(const MM::Device* caller, const unsigned char* buf 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, nComponents); + camera->StoreSnappedImage(buf, width, height, byteDepth); return DEVICE_OK; } } diff --git a/MMCore/Devices/CameraInstance.cpp b/MMCore/Devices/CameraInstance.cpp index bf3ba7b0b..b35b89bf3 100644 --- a/MMCore/Devices/CameraInstance.cpp +++ b/MMCore/Devices/CameraInstance.cpp @@ -192,7 +192,7 @@ bool CameraInstance::IsSnapping() const { } void CameraInstance::StoreSnappedImage(const unsigned char* buf, unsigned width, unsigned height, - unsigned byteDepth, unsigned nComponents) { + unsigned byteDepth) { // If the buffer doesn't exist or has wrong dimensions, create/resize it if (snappedImage_.Width() != width || snappedImage_.Height() != height || diff --git a/MMCore/Devices/CameraInstance.h b/MMCore/Devices/CameraInstance.h index b76f1268d..5ce2a9aad 100644 --- a/MMCore/Devices/CameraInstance.h +++ b/MMCore/Devices/CameraInstance.h @@ -91,8 +91,7 @@ class CameraInstance : public DeviceInstanceBase * @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, unsigned nComponents); + 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 From 2db14979e4b55cfdb6a382eac483a148d88d2074 Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Sun, 9 Mar 2025 11:56:41 -0700 Subject: [PATCH 46/50] finished adding new camera API to core --- DeviceAdapters/Basler/BaslerPylonCamera.cpp | 5 - DeviceAdapters/Basler/BaslerPylonCamera.h | 2 - MMCore/Devices/CameraInstance.cpp | 66 +++- MMCore/Devices/CameraInstance.h | 14 +- MMCore/MMCore.cpp | 341 ++++++++++++++------ MMCore/MMCore.h | 21 ++ MMDevice/DeviceBase.h | 15 +- MMDevice/MMDevice.h | 3 +- 8 files changed, 339 insertions(+), 128 deletions(-) diff --git a/DeviceAdapters/Basler/BaslerPylonCamera.cpp b/DeviceAdapters/Basler/BaslerPylonCamera.cpp index 1c79fcf65..586a2eb87 100644 --- a/DeviceAdapters/Basler/BaslerPylonCamera.cpp +++ b/DeviceAdapters/Basler/BaslerPylonCamera.cpp @@ -967,11 +967,6 @@ int BaslerCamera::AcquisitionArm(int frameCount) return DEVICE_OK; } -int BaslerCamera::AcquisitionArm() -{ - return AcquisitionArm(0); -} - int BaslerCamera::AcquisitionStart() { // This tells the core to open the current shutter. diff --git a/DeviceAdapters/Basler/BaslerPylonCamera.h b/DeviceAdapters/Basler/BaslerPylonCamera.h index 08858a70b..89e5769d4 100644 --- a/DeviceAdapters/Basler/BaslerPylonCamera.h +++ b/DeviceAdapters/Basler/BaslerPylonCamera.h @@ -99,8 +99,6 @@ class BaslerCamera : public CNewAPICameraBase { int TriggerSoftware(); int AcquisitionArm(int frameCount); - int AcquisitionArm(); - int AcquisitionStart(); int AcquisitionStop(); int AcquisitionAbort(); diff --git a/MMCore/Devices/CameraInstance.cpp b/MMCore/Devices/CameraInstance.cpp index 7fa86fd67..c864932d6 100644 --- a/MMCore/Devices/CameraInstance.cpp +++ b/MMCore/Devices/CameraInstance.cpp @@ -21,6 +21,54 @@ #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__); @@ -31,16 +79,14 @@ int CameraInstance::SnapImage() { ret = GetImpl()->SnapImage(); } else { ret = GetImpl()->AcquisitionArm(1); - if (ret != DEVICE_OK) { - isSnapping_.store(false); - return ret; + 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); + } } - ret = GetImpl()->AcquisitionStart(); - // Use condition variable to wait for image capture - std::unique_lock lock(imageMutex_); - imageAvailable_.wait(lock, [this]() { - return (!GetImpl()->IsCapturing() && multiChannelImageCounter_.load() == GetImpl()->GetNumberOfChannels()); - }); } isSnapping_.store(false); return ret; @@ -198,7 +244,7 @@ int CameraInstance::StartSequenceAcquisition(double interval_ms) { if (!GetImpl()->IsNewAPIImplemented()) { return GetImpl()->StartSequenceAcquisition(interval_ms); } else { - int ret = GetImpl()->AcquisitionArm(); + int ret = GetImpl()->AcquisitionArm(0); // 0 means until stopped (e.g. live mode) if (ret != DEVICE_OK) { return ret; } diff --git a/MMCore/Devices/CameraInstance.h b/MMCore/Devices/CameraInstance.h index 355bffbe6..0b305edba 100644 --- a/MMCore/Devices/CameraInstance.h +++ b/MMCore/Devices/CameraInstance.h @@ -40,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); @@ -101,8 +111,10 @@ class CameraInstance : public DeviceInstanceBase // 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/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/MMDevice/DeviceBase.h b/MMDevice/DeviceBase.h index 6a620ec38..5d2fbc16e 100644 --- a/MMDevice/DeviceBase.h +++ b/MMDevice/DeviceBase.h @@ -1925,17 +1925,22 @@ class COldAPICameraBase : public CAllCamerasBase SkipTriggerSourceStandardProperty(); SkipTriggerActivationStandardProperty(); SkipTriggerDelayStandardProperty(); + SkipTriggerOverlapStandardProperty(); + SkipExposureModeStandardProperty(); + SkipExposureTimeStandardProperty(); + SkipBurstFrameCountStandardProperty(); SkipLineSelectorStandardProperty(); SkipLineInverterStandardProperty(); SkipLineSourceStandardProperty(); + SkipLineModeStandardProperty(); SkipLineStatusStandardProperty(); + SkipEventSelectorStandardProperty(); + SkipEventNotificationStandardProperty(); + SkipRollingShutterLineOffsetStandardProperty(); SkipRollingShutterActiveLinesStandardProperty(); - SkipEventNotificationStandardProperty(); - SkipTriggerOverlapStandardProperty(); - SkipExposureTimeStandardProperty(); } // Shared functionality with no default implementation @@ -1972,8 +1977,6 @@ class COldAPICameraBase : public CAllCamerasBase virtual int TriggerSoftware() final {return DEVICE_NOT_YET_IMPLEMENTED;}; virtual int AcquisitionArm(int /* frameCount */) final {return DEVICE_NOT_YET_IMPLEMENTED;}; - virtual int AcquisitionArm() 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;}; @@ -2034,8 +2037,6 @@ class CNewAPICameraBase : public CAllCamerasBase virtual int TriggerSoftware() = 0; virtual int AcquisitionArm(int frameCount) = 0; - virtual int AcquisitionArm() = 0; - virtual int AcquisitionStart() = 0; virtual int AcquisitionStop() = 0; virtual int AcquisitionAbort() = 0; diff --git a/MMDevice/MMDevice.h b/MMDevice/MMDevice.h index 3c2c0e116..19f06cefe 100644 --- a/MMDevice/MMDevice.h +++ b/MMDevice/MMDevice.h @@ -803,10 +803,9 @@ namespace MM { // Don't acqMode because it can be inferred from frameCount // if frameCount is: 1 --> acqMode is single // > 1 --> acqMode is MultiFrame - // -1 --> acqMode is continuous + // <= 0 --> acqMode is continuous virtual int AcquisitionArm(int frameCount) = 0; - virtual int AcquisitionArm() = 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, From 2a68d14b8d2d94d95693b048a5b57dca261c05fd Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Tue, 11 Mar 2025 11:06:46 -0700 Subject: [PATCH 47/50] camera events/callbacks and more standard properties --- DeviceAdapters/Basler/BaslerPylonCamera.cpp | 290 +++++++++++++------- DeviceAdapters/Basler/BaslerPylonCamera.h | 13 +- MMCore/CoreCallback.cpp | 9 + MMCore/CoreCallback.h | 2 +- MMCore/MMEventCallback.h | 5 + MMDevice/DeviceBase.h | 71 ++++- MMDevice/MMDevice.h | 124 ++++++--- 7 files changed, 377 insertions(+), 137 deletions(-) diff --git a/DeviceAdapters/Basler/BaslerPylonCamera.cpp b/DeviceAdapters/Basler/BaslerPylonCamera.cpp index 586a2eb87..4f956a3d8 100644 --- a/DeviceAdapters/Basler/BaslerPylonCamera.cpp +++ b/DeviceAdapters/Basler/BaslerPylonCamera.cpp @@ -374,6 +374,12 @@ int BaslerCamera::Initialize() InitOrSyncEventSelectorStandardProperty(); InitOrSyncEventNotificationStandardProperty(); + InitOrSyncAcquisitionFrameRateStandardProperty(); + InitOrSyncAcquisitionFrameRateEnableStandardProperty(); + + InitOrSyncAcquisitionStatusSelectorStandardProperty(); + InitOrSyncAcquisitionStatusStandardProperty(); + SkipBurstFrameCountStandardProperty(); SkipRollingShutterLineOffsetStandardProperty(); @@ -382,33 +388,34 @@ int BaslerCamera::Initialize() if (camera_->EventSelector.IsWritable()) { + pEventHandler_ = new CMMCameraEventHandler(this); - if (camera_->EventSelector.CanSetValue(EventSelector_CriticalTemperature) && - camera_->EventSelector.CanSetValue(EventSelector_OverTemperature)) - { - // Temperature update events - camera_->RegisterCameraEventHandler(pEventHandler_, "EventCriticalTemperatureData", static_cast(0), RegistrationMode_Append, Cleanup_None); - } - - // Register the handler for each type of event that might be std::vector eventSelectorValues = GetAvailableEnumValues(camera_->EventSelector); - for (const auto& eventValue : eventSelectorValues) { + 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(), - static_cast(0), + eventId, RegistrationMode_Append, Cleanup_None ); + + // Store the mapping between eventId and eventValue for later use in the handler + eventIdToName_[eventId] = eventValue; + + } - catch (const GenericException& e) { + catch (const GenericException&) { return DEVICE_ERR; } } @@ -418,7 +425,7 @@ int BaslerCamera::Initialize() //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)) { @@ -426,7 +433,7 @@ int BaslerCamera::Initialize() GenApi::Register(camera_->ResultingFrameRateAbs.GetNode(), *this, &BaslerCamera::ResultingFramerateCallback); } - //Register Camera events + // //Register Camera events if (IsAvailable(camera_->DeviceTemperature)) { @@ -969,6 +976,7 @@ int BaslerCamera::AcquisitionArm(int frameCount) 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 @@ -1390,6 +1398,32 @@ int BaslerCamera::OnEventNotification(MM::PropertyBase* pProp, MM::ActionType eA 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::OnLineMode(MM::PropertyBase* pProp, MM::ActionType eAct) { int ret = HandleEnumerationProperty(pProp, eAct, "LineMode"); @@ -2341,92 +2375,31 @@ CMMCameraEventHandler::CMMCameraEventHandler(BaslerCamera* dev) : void CMMCameraEventHandler::OnCameraEvent(CBaslerUniversalInstantCamera& camera, intptr_t userProvidedId, GenApi::INode* pNode) { - // Cast parameters to void to avoid unused parameter warnings - (void)userProvidedId; - - // Get the node name - CParameter parameter(pNode); - if (!parameter.IsValid()) - return; - - // Get the node name - std::string nodeName = parameter.GetNode()->GetName().c_str(); - nodeName += " "; + 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. - // // Build a message with event details - // stringstream msg; - // msg << "Camera Event: " << nodeName; - - // // Handle specific event types - // if (nodeName.find("Temperature") != std::string::npos) { - // // Temperature event - // msg << " - Temperature State: " << camera.TemperatureState.ToString(); - // dev_->UpdateTemperature(); - // } - // else if (nodeName.find("ExposureEnd") != std::string::npos) { - // // Exposure end event - // if (IsAvailable(camera.EventExposureEndFrameID)) { - // msg << " - Frame ID: " << camera.EventExposureEndFrameID.GetValue(); - // } - // if (IsAvailable(camera.EventExposureEndTimestamp)) { - // msg << " - Timestamp: " << camera.EventExposureEndTimestamp.GetValue(); - // } - // } - // else if (nodeName.find("FrameStart") != std::string::npos) { - // // Frame start event - // if (IsAvailable(camera.EventFrameStartFrameID)) { - // msg << " - Frame ID: " << camera.EventFrameStartFrameID.GetValue(); - // } - // if (IsAvailable(camera.EventFrameStartTimestamp)) { - // msg << " - Timestamp: " << camera.EventFrameStartTimestamp.GetValue(); - // } - // } - // else if (nodeName.find("FrameTrigger") != std::string::npos) { - // // Frame trigger event - // if (IsAvailable(camera.EventFrameTriggerFrameID)) { - // msg << " - Frame ID: " << camera.EventFrameTriggerFrameID.GetValue(); - // } - // if (IsAvailable(camera.EventFrameTriggerTimestamp)) { - // msg << " - Timestamp: " << camera.EventFrameTriggerTimestamp.GetValue(); - // } - // } - // else if (nodeName.find("Line") != std::string::npos) { - // // Line event (e.g., LineRisingEdge, LineFallingEdge) - // if (nodeName.find("RisingEdge") != std::string::npos && IsAvailable(camera.EventLineRisingEdgeTimestamp)) { - // msg << " - Timestamp: " << camera.EventLineRisingEdgeTimestamp.GetValue(); - // } - // else if (nodeName.find("FallingEdge") != std::string::npos && IsAvailable(camera.EventLineFallingEdgeTimestamp)) { - // msg << " - Timestamp: " << camera.EventLineFallingEdgeTimestamp.GetValue(); - // } - // } - - // // Try to get any additional data from the event - // try { - // // For integer nodes - // CIntegerPtr intNode = dynamic_cast(pNode); - // if (intNode && IsReadable(intNode)) { - // msg << " - Value: " << intNode->GetValue(); - // } - - // // For float nodes - // CFloatPtr floatNode = dynamic_cast(pNode); - // if (floatNode && IsReadable(floatNode)) { - // msg << " - Value: " << floatNode->GetValue(); - // } - - // // For enum nodes - // CEnumerationPtr enumNode = dynamic_cast(pNode); - // if (enumNode && IsReadable(enumNode)) { - // msg << " - Value: " << enumNode->ToString(); - // } - // } - // catch (const GenericException& e) { - // // Silently ignore errors when trying to read values - // msg << " (Error reading value: " << e.GetDescription() << ")"; - // } - - // // Log the event - // dev_->AddToLog(msg.str()); + if (eventName == "ExposureEnd") { + if (camera.EventExposureEndFrameID.IsReadable()) + { + 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()); + } + } } BufferInserter::BufferInserter(BaslerCamera* dev) : @@ -2445,6 +2418,9 @@ void BufferInserter::OnImageGrabbed(CInstantCamera& /* camera */, const CGrabRes 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()) { @@ -2503,6 +2479,13 @@ void BufferInserter::OnImageGrabbed(CInstantCamera& /* camera */, const CGrabRes 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); } } @@ -2935,3 +2918,114 @@ int BaslerCamera::InitOrSyncEventNotificationStandardProperty() 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; +} + + diff --git a/DeviceAdapters/Basler/BaslerPylonCamera.h b/DeviceAdapters/Basler/BaslerPylonCamera.h index 89e5769d4..b6fed5254 100644 --- a/DeviceAdapters/Basler/BaslerPylonCamera.h +++ b/DeviceAdapters/Basler/BaslerPylonCamera.h @@ -128,6 +128,9 @@ class BaslerCamera : public CNewAPICameraBase { 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); + // non-standard properties int OnAcqFramerate(MM::PropertyBase* pProp, MM::ActionType eAct); @@ -178,6 +181,11 @@ class BaslerCamera : public CNewAPICameraBase { 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); @@ -210,7 +218,6 @@ class BaslerCamera : public CNewAPICameraBase { bool colorCamera_; unsigned maxWidth_, maxHeight_; - unsigned multiFrameAcqCount_; int64_t DeviceLinkThroughputLimit_; int64_t InterPacketDelay_; double ResultingFrameRatePrevious; @@ -252,6 +259,10 @@ class BaslerCamera : public CNewAPICameraBase { int InitOrSyncLineStatusStandardProperty(); int InitOrSyncEventSelectorStandardProperty(); int InitOrSyncEventNotificationStandardProperty(); + int InitOrSyncAcquisitionFrameRateStandardProperty(); + int InitOrSyncAcquisitionFrameRateEnableStandardProperty(); + int InitOrSyncAcquisitionStatusSelectorStandardProperty(); + int InitOrSyncAcquisitionStatusStandardProperty(); }; diff --git a/MMCore/CoreCallback.cpp b/MMCore/CoreCallback.cpp index 15c9ab225..27fddb5fa 100644 --- a/MMCore/CoreCallback.cpp +++ b/MMCore/CoreCallback.cpp @@ -683,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/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 5d2fbc16e..c403a00d3 100644 --- a/MMDevice/DeviceBase.h +++ b/MMDevice/DeviceBase.h @@ -300,6 +300,48 @@ class CDeviceBase : public T 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 CreateRollingShutterLineOffsetStandardProperty(const char* value, MM::ActionFunctor* pAct = 0) { return CreateStandardProperty(value, pAct); } @@ -1936,6 +1978,13 @@ class COldAPICameraBase : public CAllCamerasBase SkipLineSourceStandardProperty(); SkipLineModeStandardProperty(); SkipLineStatusStandardProperty(); + + SkipAcquisitionFrameRateStandardProperty(); + SkipAcquisitionFrameRateEnableStandardProperty(); + + SkipAcquisitionStatusSelectorStandardProperty(); + SkipAcquisitionStatusStandardProperty(); + SkipEventSelectorStandardProperty(); SkipEventNotificationStandardProperty(); @@ -2027,11 +2076,25 @@ class CNewAPICameraBase : public CAllCamerasBase 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;} - // TODO: do this in terms of the standard property - // TODO: these are in ms but the standard property is in us - virtual void SetExposure(double exp_ms) final {return;} - virtual double GetExposure() const final {return -1;} + 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; diff --git a/MMDevice/MMDevice.h b/MMDevice/MMDevice.h index 19f06cefe..b4cdc825c 100644 --- a/MMDevice/MMDevice.h +++ b/MMDevice/MMDevice.h @@ -397,6 +397,26 @@ namespace MM { }; 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. @@ -428,26 +448,6 @@ namespace MM { }; MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_ExposureModeProperty) - // 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) - // ExposureTime property // Sets the Exposure time when ExposureMode is Timed and ExposureAuto is Off. static const MM::StandardProperty g_ExposureTimeProperty{ @@ -570,19 +570,72 @@ namespace MM { }; MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_LineStatusProperty) - // TODO: implement this? - // // Acquisition status property - // static const std::vector acquisitionStatusSelectorValues = { - // g_keyword_CameraStatusAcquisitionTriggerWait, - // g_keyword_CameraStatusFrameBurstTriggerWait, - // g_keyword_CameraStatusFrameTriggerWait, - // g_keyword_CameraStatusExposureTriggerWait, - // g_keyword_CameraStatusAcquisitionActive, - // g_keyword_CameraStatusFrameBurstTriggerActive, - // g_keyword_CameraStatusFrameActive, - // g_keyword_CameraStatusExposureActive, - // g_keyword_CameraStatusAcquisitionTransfer - // }; + // 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 @@ -1876,6 +1929,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. From 1f242d297fabfb2cfed8d734c894d09a1c7589a2 Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Tue, 11 Mar 2025 15:02:00 -0700 Subject: [PATCH 48/50] enable frame burst property --- DeviceAdapters/Basler/BaslerPylonCamera.cpp | 52 ++++++++++++++++++++- DeviceAdapters/Basler/BaslerPylonCamera.h | 19 ++------ MMDevice/DeviceBase.h | 10 ++-- MMDevice/MMDevice.h | 6 +-- 4 files changed, 62 insertions(+), 25 deletions(-) diff --git a/DeviceAdapters/Basler/BaslerPylonCamera.cpp b/DeviceAdapters/Basler/BaslerPylonCamera.cpp index 4f956a3d8..8c116e6f3 100644 --- a/DeviceAdapters/Basler/BaslerPylonCamera.cpp +++ b/DeviceAdapters/Basler/BaslerPylonCamera.cpp @@ -380,7 +380,7 @@ int BaslerCamera::Initialize() InitOrSyncAcquisitionStatusSelectorStandardProperty(); InitOrSyncAcquisitionStatusStandardProperty(); - SkipBurstFrameCountStandardProperty(); + InitOrSyncAcquisitionBurstFrameCountStandardProperty(); SkipRollingShutterLineOffsetStandardProperty(); SkipRollingShutterActiveLinesStandardProperty(); @@ -1424,6 +1424,32 @@ int BaslerCamera::OnAcquisitionStatus(MM::PropertyBase* pProp, MM::ActionType eA 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"); @@ -3028,4 +3054,28 @@ int BaslerCamera::InitOrSyncAcquisitionStatusStandardProperty() 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 b6fed5254..26ee0e004 100644 --- a/DeviceAdapters/Basler/BaslerPylonCamera.h +++ b/DeviceAdapters/Basler/BaslerPylonCamera.h @@ -131,6 +131,8 @@ class BaslerCamera : public CNewAPICameraBase { 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); @@ -189,22 +191,6 @@ class BaslerCamera : public CNewAPICameraBase { std::vector GetAvailableEnumValues(const GenApi::IEnumeration& node); - // Standard property initialization functions - int InitTriggerSelectorStandardProperty(); - int InitTriggerModeStandardProperty(); - int InitTriggerSourceStandardProperty(); - int InitTriggerActivationStandardProperty(); - int InitTriggerDelayStandardProperty(); - int InitTriggerOverlapStandardProperty(); - int InitExposureModeStandardProperty(); - int InitExposureTimeStandardProperty(); - int InitLineSelectorStandardProperty(); - int InitLineModeStandardProperty(); - int InitLineInverterStandardProperty(); - int InitLineSourceStandardProperty(); - int InitLineStatusStandardProperty(); - int InitEventSelectorStandardProperty(); - int InitEventNotificationStandardProperty(); private: @@ -263,6 +249,7 @@ class BaslerCamera : public CNewAPICameraBase { int InitOrSyncAcquisitionFrameRateEnableStandardProperty(); int InitOrSyncAcquisitionStatusSelectorStandardProperty(); int InitOrSyncAcquisitionStatusStandardProperty(); + int InitOrSyncAcquisitionBurstFrameCountStandardProperty(); }; diff --git a/MMDevice/DeviceBase.h b/MMDevice/DeviceBase.h index c403a00d3..3bc1c15b3 100644 --- a/MMDevice/DeviceBase.h +++ b/MMDevice/DeviceBase.h @@ -229,12 +229,12 @@ class CDeviceBase : public T SkipStandardProperty(); } - int CreateBurstFrameCountStandardProperty(const char* value, MM::ActionFunctor* pAct = 0) { - return CreateStandardProperty(value, pAct); + int CreateAcquisitionBurstFrameCountStandardProperty(const char* value, MM::ActionFunctor* pAct = 0) { + return CreateStandardProperty(value, pAct); } - void SkipBurstFrameCountStandardProperty() { - SkipStandardProperty(); + void SkipAcquisitionBurstFrameCountStandardProperty() { + SkipStandardProperty(); } int CreateLineSelectorStandardProperty(const char* value, const std::vector& values, MM::ActionFunctor* pAct = 0) { @@ -1972,7 +1972,7 @@ class COldAPICameraBase : public CAllCamerasBase SkipExposureModeStandardProperty(); SkipExposureTimeStandardProperty(); - SkipBurstFrameCountStandardProperty(); + SkipAcquisitionBurstFrameCountStandardProperty(); SkipLineSelectorStandardProperty(); SkipLineInverterStandardProperty(); SkipLineSourceStandardProperty(); diff --git a/MMDevice/MMDevice.h b/MMDevice/MMDevice.h index b4cdc825c..d60858bb1 100644 --- a/MMDevice/MMDevice.h +++ b/MMDevice/MMDevice.h @@ -467,8 +467,8 @@ namespace MM { // 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_BurstFrameCountProperty{ - "BurstFrameCount", // name + static const MM::StandardProperty g_AcquisitionBurstFrameCountProperty{ + "AcquisitionBurstFrameCount", // name Integer, // type false, // isReadOnly false, // isPreInit @@ -477,7 +477,7 @@ namespace MM { 1, // lowerLimit PropertyLimitUndefined, // upperLimit }; - MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_BurstFrameCountProperty) + MM_INTERNAL_LINK_STANDARD_PROP_TO_DEVICE_TYPE(CameraDevice, g_AcquisitionBurstFrameCountProperty) // Selects the physical line (or pin) of the external device connector to configure. From 9001af10e3dbf5471a8003db5f4c7b086c6ea698 Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Fri, 14 Mar 2025 14:56:25 -0700 Subject: [PATCH 49/50] comment out rolling shutter standard props for now --- DeviceAdapters/Basler/BaslerPylonCamera.cpp | 3 -- MMDevice/DeviceBase.h | 34 +++++++------- MMDevice/MMDevice.h | 49 +++++++++++---------- 3 files changed, 43 insertions(+), 43 deletions(-) diff --git a/DeviceAdapters/Basler/BaslerPylonCamera.cpp b/DeviceAdapters/Basler/BaslerPylonCamera.cpp index 8c116e6f3..01af747ba 100644 --- a/DeviceAdapters/Basler/BaslerPylonCamera.cpp +++ b/DeviceAdapters/Basler/BaslerPylonCamera.cpp @@ -382,9 +382,6 @@ int BaslerCamera::Initialize() InitOrSyncAcquisitionBurstFrameCountStandardProperty(); - SkipRollingShutterLineOffsetStandardProperty(); - SkipRollingShutterActiveLinesStandardProperty(); - if (camera_->EventSelector.IsWritable()) { diff --git a/MMDevice/DeviceBase.h b/MMDevice/DeviceBase.h index 3bc1c15b3..e69e51528 100644 --- a/MMDevice/DeviceBase.h +++ b/MMDevice/DeviceBase.h @@ -342,22 +342,6 @@ class CDeviceBase : public T SkipStandardProperty(); } - 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(); - } - int CreateEventSelectorStandardProperty(const char* value, const std::vector& values, MM::ActionFunctor* pAct = 0) { return CreateStandardProperty(value, pAct, values); } @@ -393,6 +377,24 @@ class CDeviceBase : public T 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(); + // } /** diff --git a/MMDevice/MMDevice.h b/MMDevice/MMDevice.h index d60858bb1..9fa99863d 100644 --- a/MMDevice/MMDevice.h +++ b/MMDevice/MMDevice.h @@ -691,30 +691,31 @@ namespace MM { //// Standard properties for rolling shutter lightsheet readout cameras //// These are not part of GenICam, but are supported by some scientific cameras - - 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) + // 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. From 221409bed7bb141f5e7600755f78e18b356f2d74 Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Fri, 14 Mar 2025 15:16:10 -0700 Subject: [PATCH 50/50] remove commented --- MMDevice/DeviceBase.h | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/MMDevice/DeviceBase.h b/MMDevice/DeviceBase.h index e69e51528..418bab984 100644 --- a/MMDevice/DeviceBase.h +++ b/MMDevice/DeviceBase.h @@ -2124,20 +2124,6 @@ class CLegacyCameraBase : public COldAPICameraBase CLegacyCameraBase() : COldAPICameraBase(), busy_(false), stopWhenCBOverflows_(false), thd_(0) { - // TODO: does this belong here or in the superclass? - // create and initialize common transpose properties - // std::vector allowedValues; - // allowedValues.push_back("0"); - // allowedValues.push_back("1"); - // CreateProperty(MM::g_Keyword_Transpose_SwapXY, "0", MM::Integer, false); - // SetAllowedValues(MM::g_Keyword_Transpose_SwapXY, allowedValues); - // CreateProperty(MM::g_Keyword_Transpose_MirrorX, "0", MM::Integer, false); - // SetAllowedValues(MM::g_Keyword_Transpose_MirrorX, allowedValues); - // CreateProperty(MM::g_Keyword_Transpose_MirrorY, "0", MM::Integer, false); - // 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); }