diff --git a/DeviceAdapters/CNCMicroscope/RAMPSStage/XYStage.h b/DeviceAdapters/CNCMicroscope/RAMPSStage/XYStage.h index c8d671de3..a340f38c8 100644 --- a/DeviceAdapters/CNCMicroscope/RAMPSStage/XYStage.h +++ b/DeviceAdapters/CNCMicroscope/RAMPSStage/XYStage.h @@ -66,7 +66,6 @@ class RAMPSXYStage : public CXYStageBase int IsXYStageSequenceable(bool& isSequenceable) const; - // action interface // ---------------- int OnPosition(MM::PropertyBase* pProp, MM::ActionType eAct); diff --git a/DeviceAdapters/Cephla/Squid.h b/DeviceAdapters/Cephla/Squid.h index 9de90b272..b1bf35020 100644 --- a/DeviceAdapters/Cephla/Squid.h +++ b/DeviceAdapters/Cephla/Squid.h @@ -284,6 +284,7 @@ class SquidXYStage : public CXYStageBase int IsXYStageSequenceable(bool& isSequenceable) const { isSequenceable = false; return DEVICE_OK; } + int UsesOnXYStagePositionChanged(bool& result) const { result = true; return DEVICE_OK; } // action interface // ---------------- @@ -354,6 +355,8 @@ class SquidZStage : public CStageBase } bool IsContinuousFocusDrive() const { return false; }; + int UsesOnStagePositionChanged(bool& result) const { result = true; return DEVICE_OK; } + int OnAcceleration(MM::PropertyBase* pProp, MM::ActionType eAct); int OnMaxVelocity(MM::PropertyBase* pProp, MM::ActionType eAct); diff --git a/DeviceAdapters/ChuoSeiki_MD5000/ChuoSeiki_MD_XYZ.h b/DeviceAdapters/ChuoSeiki_MD5000/ChuoSeiki_MD_XYZ.h index d8e5c3aba..83ad90796 100644 --- a/DeviceAdapters/ChuoSeiki_MD5000/ChuoSeiki_MD_XYZ.h +++ b/DeviceAdapters/ChuoSeiki_MD5000/ChuoSeiki_MD_XYZ.h @@ -119,6 +119,7 @@ class MD_SingleStage: // Checking device functions int IsStageSequenceable(bool& isSequenceable) const {isSequenceable = false; return DEVICE_OK;} bool IsContinuousFocusDrive() const {return DEVICE_OK;} + int UsesOnStagePositionChanged(bool& result) const { result = true; return DEVICE_OK; } bool SupportsDeviceDetection(void); MM::DeviceDetectionStatus DetectDevice(void); diff --git a/DeviceAdapters/DemoCamera/DemoCamera.h b/DeviceAdapters/DemoCamera/DemoCamera.h index f6fc343ed..eb238f008 100644 --- a/DeviceAdapters/DemoCamera/DemoCamera.h +++ b/DeviceAdapters/DemoCamera/DemoCamera.h @@ -498,6 +498,8 @@ class CDemoStage : public CStageBase bool IsContinuousFocusDrive() const {return false;} + int UsesOnStagePositionChanged(bool& result) const { result = true; return DEVICE_OK; } + // action interface // ---------------- int OnPosition(MM::PropertyBase* pProp, MM::ActionType eAct); @@ -578,6 +580,7 @@ class CDemoXYStage : public CXYStageBase int IsXYStageSequenceable(bool& isSequenceable) const {isSequenceable = false; return DEVICE_OK;} + int UsesOnXYStagePositionChanged(bool& result) const { result = true; return DEVICE_OK; } // action interface // ---------------- diff --git a/DeviceAdapters/ESP32/esp32.h b/DeviceAdapters/ESP32/esp32.h index 60101a955..892f58995 100644 --- a/DeviceAdapters/ESP32/esp32.h +++ b/DeviceAdapters/ESP32/esp32.h @@ -389,6 +389,8 @@ class CESP32XYStage : public CXYStageBase int IsXYStageSequenceable(bool& isSequenceable) const { isSequenceable = false; return DEVICE_OK; } + int UsesOnXYStagePositionChanged(bool& result) const { result = true; return DEVICE_OK; } + int OnXStageMinPos(MM::PropertyBase* pProp, MM::ActionType eAct); int OnXStageMaxPos(MM::PropertyBase* pProp, MM::ActionType eAct); int OnYStageMinPos(MM::PropertyBase* pProp, MM::ActionType eAct); diff --git a/DeviceAdapters/OpenFlexure/OpenFlexure.h b/DeviceAdapters/OpenFlexure/OpenFlexure.h index c72e8bd81..103139cb8 100644 --- a/DeviceAdapters/OpenFlexure/OpenFlexure.h +++ b/DeviceAdapters/OpenFlexure/OpenFlexure.h @@ -165,6 +165,7 @@ class ZStage : public CStageBase int GetLimits(double& lower, double& upper) { return DEVICE_UNSUPPORTED_COMMAND;} // nah int IsStageSequenceable(bool& isSequenceable) const { isSequenceable = false; return DEVICE_OK;} bool IsContinuousFocusDrive() const { return false; } + bool Busy() { return false; } // Helper functions diff --git a/DeviceAdapters/PriorPureFocus/PureFocus.h b/DeviceAdapters/PriorPureFocus/PureFocus.h index 5e94fd27a..bdb901d55 100644 --- a/DeviceAdapters/PriorPureFocus/PureFocus.h +++ b/DeviceAdapters/PriorPureFocus/PureFocus.h @@ -170,6 +170,7 @@ class PureFocusOffset : public CStageBase { bool IsContinuousFocusDrive() const { return false; }; + void CallbackPositionSteps(long steps); void RemoveHub() { pHub_ = 0; }; diff --git a/DeviceAdapters/PyDevice/PyStage.h b/DeviceAdapters/PyDevice/PyStage.h index dc0fa9430..4ac22f728 100644 --- a/DeviceAdapters/PyDevice/PyStage.h +++ b/DeviceAdapters/PyDevice/PyStage.h @@ -118,6 +118,11 @@ class CPyXYStage : public PyXYStageClass { return DEVICE_UNSUPPORTED_COMMAND; } + int UsesOnXYStagePositionChanged(bool& result) const override { + result = false; + return DEVICE_OK; + } + protected: double origin_x_ = 0.0; double origin_y_ = 0.0; diff --git a/DeviceAdapters/StandaStage/StandaStage.h b/DeviceAdapters/StandaStage/StandaStage.h index ae20308cc..d5feac1da 100644 --- a/DeviceAdapters/StandaStage/StandaStage.h +++ b/DeviceAdapters/StandaStage/StandaStage.h @@ -266,7 +266,6 @@ class CStandaXYStage : public CXYStageBase int IsXYStageSequenceable(bool& isSequenceable) const {isSequenceable = false; return DEVICE_OK;} - // action interface // ---------------- diff --git a/DeviceAdapters/WieneckeSinske/ZPiezoCanDevice.h b/DeviceAdapters/WieneckeSinske/ZPiezoCanDevice.h index 889e6c613..c22daaf05 100644 --- a/DeviceAdapters/WieneckeSinske/ZPiezoCanDevice.h +++ b/DeviceAdapters/WieneckeSinske/ZPiezoCanDevice.h @@ -77,7 +77,6 @@ class ZPiezoCANDevice : public CStageBase int SetOrigin(); int IsStageSequenceable(bool& isSequenceable) const {isSequenceable = false; return DEVICE_OK;} - // action interface // ---------------- int OnPort(MM::PropertyBase* pProp, MM::ActionType eAct); diff --git a/DeviceAdapters/WieneckeSinske/ZPiezoWSDevice.h b/DeviceAdapters/WieneckeSinske/ZPiezoWSDevice.h index e99c444a7..1a47c3b1d 100644 --- a/DeviceAdapters/WieneckeSinske/ZPiezoWSDevice.h +++ b/DeviceAdapters/WieneckeSinske/ZPiezoWSDevice.h @@ -77,7 +77,6 @@ class ZPiezoWSDevice : public CStageBase int SetOrigin(); int IsStageSequenceable(bool& isSequenceable) const {isSequenceable = false; return DEVICE_OK;} - // action interface // ---------------- int OnPort(MM::PropertyBase* pProp, MM::ActionType eAct); diff --git a/DeviceAdapters/ZeissCAN29/ZeissCAN29.h b/DeviceAdapters/ZeissCAN29/ZeissCAN29.h index 79ef67051..f251d11d3 100644 --- a/DeviceAdapters/ZeissCAN29/ZeissCAN29.h +++ b/DeviceAdapters/ZeissCAN29/ZeissCAN29.h @@ -836,6 +836,8 @@ class Axis : public CStageBase, public ZeissAxis int IsStageSequenceable(bool& isSequenceable) const {isSequenceable = false; return DEVICE_OK;} bool IsContinuousFocusDrive() const {return false;} + int UsesOnStagePositionChanged(bool& result) const { result = true; return DEVICE_OK; } + // action interface // ---------------- int OnPosition(MM::PropertyBase* pProp, MM::ActionType eAct); @@ -890,6 +892,8 @@ class XYStage : public CXYStageBase, public ZeissAxis double GetStepSizeYUm() {return stepSize_um_;} int IsXYStageSequenceable(bool& isSequenceable) const {isSequenceable = false; return DEVICE_OK;} + int UsesOnXYStagePositionChanged(bool& result) const { result = true; return DEVICE_OK; } + // action interface // ---------------- int OnMoveMode(MM::PropertyBase* pProp, MM::ActionType eAct); diff --git a/MMCore/Devices/StageInstance.cpp b/MMCore/Devices/StageInstance.cpp index 666f8f9b0..d620f7116 100644 --- a/MMCore/Devices/StageInstance.cpp +++ b/MMCore/Devices/StageInstance.cpp @@ -60,6 +60,7 @@ StageInstance::SetFocusDirection(MM::FocusDirection direction) focusDirectionHasBeenSet_ = true; } +int StageInstance::UsesOnStagePositionChanged(bool& result) const { RequireInitialized(__func__); return GetImpl()->UsesOnStagePositionChanged(result); } int StageInstance::IsStageSequenceable(bool& isSequenceable) const { RequireInitialized(__func__); return GetImpl()->IsStageSequenceable(isSequenceable); } int StageInstance::IsStageLinearSequenceable(bool& isSequenceable) const { RequireInitialized(__func__); return GetImpl()->IsStageLinearSequenceable(isSequenceable); } bool StageInstance::IsContinuousFocusDrive() const { RequireInitialized(__func__); return GetImpl()->IsContinuousFocusDrive(); } diff --git a/MMCore/Devices/StageInstance.h b/MMCore/Devices/StageInstance.h index b94b1e6bf..b4e2cff93 100644 --- a/MMCore/Devices/StageInstance.h +++ b/MMCore/Devices/StageInstance.h @@ -57,6 +57,7 @@ class StageInstance : public DeviceInstanceBase int GetLimits(double& lower, double& upper); MM::FocusDirection GetFocusDirection(); void SetFocusDirection(MM::FocusDirection direction); + int UsesOnStagePositionChanged(bool& result) const; int IsStageSequenceable(bool& isSequenceable) const; int IsStageLinearSequenceable(bool& isSequenceable) const; bool IsContinuousFocusDrive() const; diff --git a/MMCore/Devices/XYStageInstance.cpp b/MMCore/Devices/XYStageInstance.cpp index 56f06844a..0b18bce63 100644 --- a/MMCore/Devices/XYStageInstance.cpp +++ b/MMCore/Devices/XYStageInstance.cpp @@ -42,6 +42,7 @@ int XYStageInstance::SetYOrigin() { RequireInitialized(__func__); return GetImpl int XYStageInstance::GetStepLimits(long& xMin, long& xMax, long& yMin, long& yMax) { RequireInitialized(__func__); return GetImpl()->GetStepLimits(xMin, xMax, yMin, yMax); } double XYStageInstance::GetStepSizeXUm() { RequireInitialized(__func__); return GetImpl()->GetStepSizeXUm(); } double XYStageInstance::GetStepSizeYUm() { RequireInitialized(__func__); return GetImpl()->GetStepSizeYUm(); } +int XYStageInstance::UsesOnXYStagePositionChanged(bool& result) const { RequireInitialized(__func__); return GetImpl()->UsesOnXYStagePositionChanged(result); } int XYStageInstance::IsXYStageSequenceable(bool& isSequenceable) const { RequireInitialized(__func__); return GetImpl()->IsXYStageSequenceable(isSequenceable); } int XYStageInstance::GetXYStageSequenceMaxLength(long& nrEvents) const { RequireInitialized(__func__); return GetImpl()->GetXYStageSequenceMaxLength(nrEvents); } int XYStageInstance::StartXYStageSequence() { RequireInitialized(__func__); return GetImpl()->StartXYStageSequence(); } diff --git a/MMCore/Devices/XYStageInstance.h b/MMCore/Devices/XYStageInstance.h index 9dd21a46e..b6819359f 100644 --- a/MMCore/Devices/XYStageInstance.h +++ b/MMCore/Devices/XYStageInstance.h @@ -56,6 +56,7 @@ class XYStageInstance : public DeviceInstanceBase int GetStepLimits(long& xMin, long& xMax, long& yMin, long& yMax); double GetStepSizeXUm(); double GetStepSizeYUm(); + int UsesOnXYStagePositionChanged(bool& result) const; int IsXYStageSequenceable(bool& isSequenceable) const; int GetXYStageSequenceMaxLength(long& nrEvents) const; int StartXYStageSequence(); diff --git a/MMCore/MMCore.cpp b/MMCore/MMCore.cpp index 0ac29080a..a73b32827 100644 --- a/MMCore/MMCore.cpp +++ b/MMCore/MMCore.cpp @@ -106,7 +106,7 @@ namespace mmi = mmcore::internal; * (Keep the 3 numbers on one line to make it easier to look at diffs when * merging/rebasing.) */ -const int MMCore_versionMajor = 11, MMCore_versionMinor = 11, MMCore_versionPatch = 0; +const int MMCore_versionMajor = 11, MMCore_versionMinor = 12, MMCore_versionPatch = 0; /////////////////////////////////////////////////////////////////////////////// @@ -2265,6 +2265,24 @@ void CMMCore::loadExposureSequence(const char* cameraLabel, std::vector throw CMMError(getDeviceErrorText(ret, pCamera)); } +/** +* Queries whether this stage uses callbacks to signal position changes +* When false, use polling to stay updated about the positionf of the stage +*/ +bool CMMCore::isStageUsingCallbacks(const char* label) MMCORE_LEGACY_THROW(CMMError) +{ + std::shared_ptr pStage = + deviceManager_->GetDeviceOfType(label); + + mmi::DeviceModuleLockGuard guard(pStage); + + bool result; + int ret = pStage->UsesOnStagePositionChanged(result); + if (ret != DEVICE_OK) + throw CMMError(getDeviceErrorText(ret, pStage)); + return result; +} + /** * Queries stage if it can be used in a sequence @@ -2417,6 +2435,26 @@ void CMMCore::setStageLinearSequence(const char* label, double dZ_um, int nSlice throw CMMError(getDeviceErrorText(ret, pStage)); } + +/** +* Queries whether this XYStage uses callbacks to signal position changes +* When false, use polling to stay updated about the positionf of the stage +*/ +bool CMMCore::isXYStageUsingCallbacks(const char* label) MMCORE_LEGACY_THROW(CMMError) +{ + std::shared_ptr pStage = + deviceManager_->GetDeviceOfType(label); + + mmi::DeviceModuleLockGuard guard(pStage); + + bool result; + int ret = pStage->UsesOnXYStagePositionChanged(result); + if (ret != DEVICE_OK) + throw CMMError(getDeviceErrorText(ret, pStage)); + + return result; +} + /** * Queries XY stage if it can be used in a sequence * @param label the XY stage device label diff --git a/MMCore/MMCore.h b/MMCore/MMCore.h index b8d68e056..302255505 100644 --- a/MMCore/MMCore.h +++ b/MMCore/MMCore.h @@ -499,6 +499,7 @@ class CMMCore void setFocusDirection(const char* stageLabel, int sign); int getFocusDirection(const char* stageLabel) MMCORE_LEGACY_THROW(CMMError); + bool isStageUsingCallbacks(const char* stageLabel) MMCORE_LEGACY_THROW(CMMError); bool isStageSequenceable(const char* stageLabel) MMCORE_LEGACY_THROW(CMMError); bool isStageLinearSequenceable(const char* stageLabel) MMCORE_LEGACY_THROW(CMMError); void startStageSequence(const char* stageLabel) MMCORE_LEGACY_THROW(CMMError); @@ -536,6 +537,7 @@ class CMMCore double newXUm, double newYUm) MMCORE_LEGACY_THROW(CMMError); void setAdapterOriginXY(double newXUm, double newYUm) MMCORE_LEGACY_THROW(CMMError); + bool isXYStageUsingCallbacks(const char* xyStageLabel) MMCORE_LEGACY_THROW(CMMError); bool isXYStageSequenceable(const char* xyStageLabel) MMCORE_LEGACY_THROW(CMMError); void startXYStageSequence(const char* xyStageLabel) MMCORE_LEGACY_THROW(CMMError); void stopXYStageSequence(const char* xyStageLabel) MMCORE_LEGACY_THROW(CMMError); diff --git a/MMDevice/DeviceBase.h b/MMDevice/DeviceBase.h index 8fb7aab1e..055dbda33 100644 --- a/MMDevice/DeviceBase.h +++ b/MMDevice/DeviceBase.h @@ -1893,6 +1893,15 @@ class CStageBase : public CDeviceBase return DEVICE_OK; } + /** + * @brief Return true when your device adapter uses OnStagePositionChanged callbacks. + */ + virtual int UsesOnStagePositionChanged(bool& result) const + { + result = false; + return DEVICE_OK; + } + virtual int IsStageLinearSequenceable(bool& isSequenceable) const { isSequenceable = false; @@ -2103,6 +2112,15 @@ class CXYStageBase : public CDeviceBase return this->SetPositionSteps(xSteps+x, ySteps+y); } + /** + * @brief Return true when your device adapter uses OnXYStagePositionChanged callbacks. + */ + virtual int UsesOnXYStagePositionChanged(bool& result) const + { + result = false; + return DEVICE_OK; + } + virtual int Move(double /*vx*/, double /*vy*/) { return DEVICE_UNSUPPORTED_COMMAND; diff --git a/MMDevice/MMDevice.h b/MMDevice/MMDevice.h index 0027736ea..23eb2c4bd 100644 --- a/MMDevice/MMDevice.h +++ b/MMDevice/MMDevice.h @@ -28,7 +28,7 @@ // Header version // If any of the class definitions changes, the interface version // must be incremented -#define DEVICE_INTERFACE_VERSION 74 +#define DEVICE_INTERFACE_VERSION 75 /////////////////////////////////////////////////////////////////////////////// // N.B. @@ -603,6 +603,17 @@ namespace MM { virtual int SetOrigin() = 0; virtual int GetLimits(double& lower, double& upper) = 0; + /** + * @brief Stages can use the OnStagePositionChanged callback to signal + * updates about their position. Some adapters do so, others do not, + * in which case the UI code should use polling. This function signals whether + * the device adapters uses callbacks, so that the UI knows it does not need + * to poll this device. It is best practice to not change the result of this + * function (i.e. use a const value) as the UI will query the function + * only once. + */ + virtual int UsesOnStagePositionChanged(bool& result) const = 0; + /** * @brief Return the focus direction. * @@ -709,6 +720,17 @@ namespace MM { virtual int Home() = 0; virtual int Stop() = 0; + /** + * @brief XY stages can use the OnXYStagePositionChanged callback to signal + * updates about their position. Some stage adapters do so, others do not, + * in which case the UI code can use polling. This function signals whether + * the device adapters uses callbacks, so that the UI knows it does not need + * to poll this device. It is best practice to not change the result of this + * function (i.e. use a const value) as the UI will query the function + * only once. + */ + virtual int UsesOnXYStagePositionChanged(bool &result) const = 0; + /** * @brief Define the current position as the (hardware) origin (0, 0). */