From 74250cb0e27bce9473620fad20ec83b1b3f12ea3 Mon Sep 17 00:00:00 2001 From: Lars Kool Date: Mon, 29 Apr 2024 10:50:22 +0200 Subject: [PATCH] Addition of Pump devices to MM --- MMCore/CoreUtils.h | 1 + MMCore/Devices/DeviceInstances.h | 1 + MMCore/Devices/PumpInstance.cpp | 42 +++ MMCore/Devices/PumpInstance.h | 59 +++ .../LoadableModules/LoadedDeviceAdapter.cpp | 2 + MMCore/MMCore.cpp | 350 ++++++++++++++++++ MMCore/MMCore.h | 27 ++ MMCore/MMCore.vcxproj | 2 + MMCore/MMCore.vcxproj.filters | 8 +- MMDevice/DeviceBase.h | 87 +++++ .../MMDevice-SharedRuntime.vcxproj.filters | 2 +- MMDevice/MMDevice.cpp | 1 + MMDevice/MMDevice.h | 160 ++++++++ MMDevice/MMDeviceConstants.h | 14 +- 14 files changed, 750 insertions(+), 6 deletions(-) create mode 100644 MMCore/Devices/PumpInstance.cpp create mode 100644 MMCore/Devices/PumpInstance.h diff --git a/MMCore/CoreUtils.h b/MMCore/CoreUtils.h index d8b6718e5..dc034872e 100644 --- a/MMCore/CoreUtils.h +++ b/MMCore/CoreUtils.h @@ -72,6 +72,7 @@ inline std::string ToString(const MM::DeviceType d) case MM::SLMDevice: return "SLM"; case MM::HubDevice: return "Hub"; case MM::GalvoDevice: return "Galvo"; + case MM::PumpDevice: return "Pump"; } return "Invalid"; } diff --git a/MMCore/Devices/DeviceInstances.h b/MMCore/Devices/DeviceInstances.h index 8dfacf4d7..d3b442b32 100644 --- a/MMCore/Devices/DeviceInstances.h +++ b/MMCore/Devices/DeviceInstances.h @@ -30,6 +30,7 @@ #include "ImageProcessorInstance.h" #include "SignalIOInstance.h" #include "MagnifierInstance.h" +#include "PumpInstance.h" #include "SLMInstance.h" #include "GalvoInstance.h" #include "HubInstance.h" diff --git a/MMCore/Devices/PumpInstance.cpp b/MMCore/Devices/PumpInstance.cpp new file mode 100644 index 000000000..16a55f134 --- /dev/null +++ b/MMCore/Devices/PumpInstance.cpp @@ -0,0 +1,42 @@ +// PROJECT: Micro-Manager +// SUBSYSTEM: MMCore +// +// DESCRIPTION: Pump device instance wrapper +// +// COPYRIGHT: Institut Pierre-Gilles de Gennes, Paris, 2024, +// All Rights reserved +// +// LICENSE: This file is distributed under the "Lesser GPL" (LGPL) 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. +// +// AUTHOR: Lars Kool, Institut Pierre-Gilles de Gennes + +#include "PumpInstance.h" + +// Volume controlled pump functions +int PumpInstance::Home() { return GetImpl()->Home(); } +int PumpInstance::Stop() { return GetImpl()->Stop(); } +int PumpInstance::invertDirection(bool state) { return GetImpl()->InvertDirection(state); } +int PumpInstance::isDirectionInverted(bool& state) { return GetImpl()->IsDirectionInverted(state); } +int PumpInstance::setVolumeUl(double volume) { return GetImpl()->SetVolumeUl(volume); } +int PumpInstance::getVolumeUl(double& volume) { return GetImpl()->GetVolumeUl(volume); } +int PumpInstance::setMaxVolumeUl(double volume) { return GetImpl()->SetMaxVolumeUl(volume); } +int PumpInstance::getMaxVolumeUl(double& volume) { return GetImpl()->GetMaxVolumeUl(volume); } +int PumpInstance::setFlowrateUlPerSec(double flowrate) { return GetImpl()->SetFlowrateUlPerSecond(flowrate); } +int PumpInstance::getFlowrateUlPerSec(double& flowrate) { return GetImpl()->GetFlowrateUlPerSecond(flowrate); } +int PumpInstance::Dispense() { return GetImpl()->Dispense(); } +int PumpInstance::DispenseDuration(double durSec) { return GetImpl()->DispenseDuration(durSec); } +int PumpInstance::DispenseVolume(double volUl) { return GetImpl()->DispenseVolume(volUl); } + +// Pressure controlled pump functions +int PumpInstance::Calibrate() { return GetImpl()->Calibrate(); } +int PumpInstance::setPressure(double pressure) { return GetImpl()->SetPressure(pressure); } +int PumpInstance::getPressure(double& pressure) { return GetImpl()->GetPressure(pressure); } \ No newline at end of file diff --git a/MMCore/Devices/PumpInstance.h b/MMCore/Devices/PumpInstance.h new file mode 100644 index 000000000..167f4da51 --- /dev/null +++ b/MMCore/Devices/PumpInstance.h @@ -0,0 +1,59 @@ +// PROJECT: Micro-Manager +// SUBSYSTEM: MMCore +// +// DESCRIPTION: Pump device instance wrapper +// +// COPYRIGHT: Institut Pierre-Gilles de Gennes, Paris, 2024, +// All Rights reserved +// +// LICENSE: This file is distributed under the "Lesser GPL" (LGPL) 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. +// +// AUTHOR: Lars Kool, Institut Pierre-Gilles de Gennes + +#pragma once + +#include "DeviceInstanceBase.h" + +class PumpInstance : public DeviceInstanceBase +{ +public: + PumpInstance(CMMCore* core, + std::shared_ptr adapter, + const std::string& name, + MM::Device* pDevice, + DeleteDeviceFunction deleteFunction, + const std::string& label, + mm::logging::Logger deviceLogger, + mm::logging::Logger coreLogger) : + DeviceInstanceBase(core, adapter, name, pDevice, deleteFunction, label, deviceLogger, coreLogger) + {} + + // Volume controlled pump specific + int Home(); + int Stop(); + int invertDirection(bool state); + int isDirectionInverted(bool& state); + int setVolumeUl(double volUl); + int getVolumeUl(double& volUl); + int setMaxVolumeUl(double volUl); + int getMaxVolumeUl(double& volUl); + int setFlowrateUlPerSec(double flowrate); + int getFlowrateUlPerSec(double& flowrate); + int Dispense(); + int DispenseDuration(double durSec); + int DispenseVolume(double volUl); + + // Pressure controller specific + int Calibrate(); + int setPressure(double pressure); + int getPressure(double& pressure); +}; diff --git a/MMCore/LoadableModules/LoadedDeviceAdapter.cpp b/MMCore/LoadableModules/LoadedDeviceAdapter.cpp index caf2a3404..7e090a7b2 100644 --- a/MMCore/LoadableModules/LoadedDeviceAdapter.cpp +++ b/MMCore/LoadableModules/LoadedDeviceAdapter.cpp @@ -185,6 +185,8 @@ LoadedDeviceAdapter::LoadDevice(CMMCore* core, const std::string& name, return std::make_shared(core, shared_this, name, pDevice, deleter, label, deviceLogger, coreLogger); case MM::HubDevice: return std::make_shared(core, shared_this, name, pDevice, deleter, label, deviceLogger, coreLogger); + case MM::PumpDevice: + return std::make_shared(core, shared_this, name, pDevice, deleter, label, deviceLogger, coreLogger); default: deleter(pDevice); throw CMMError("Device " + ToQuotedString(name) + diff --git a/MMCore/MMCore.cpp b/MMCore/MMCore.cpp index 4bef95471..a76c6e827 100644 --- a/MMCore/MMCore.cpp +++ b/MMCore/MMCore.cpp @@ -3228,6 +3228,19 @@ std::string CMMCore::getAutoFocusDevice() return std::string(); } +/** + * Returns the label of the currently selected pump device. + */ +string CMMCore::getPumpDevice() +{ + std::shared_ptr camera = currentPumpDevice_.lock(); + if (camera) + { + return camera->GetLabel(); + } + return std::string(); +} + /** * Sets the current auto-focus device. */ @@ -6214,6 +6227,343 @@ std::string CMMCore::getGalvoChannel(const char* deviceLabel) throw (CMMError) return pGalvo->GetChannel(); } +/* Pump device */ + +/** + * Sets the current pump device. + * @param pump the shutter device label + */ +void CMMCore::setPumpDevice(const char* deviceLabel) throw (CMMError) +{ + if (!deviceLabel || strlen(deviceLabel) > 0) // Allow empty label + CheckDeviceLabel(deviceLabel); + + // Nothing to do if this is the current shutter device: + if (getPumpDevice().compare(deviceLabel) == 0) + return; + + if (strlen(deviceLabel) > 0) + { + currentPumpDevice_ = + deviceManager_->GetDeviceOfType(deviceLabel); + + LOG_INFO(coreLogger_) << "Default shutter set to " << deviceLabel; + } + else + { + currentPumpDevice_.reset(); + LOG_INFO(coreLogger_) << "Default pump unset"; + } + properties_->Refresh(); // TODO: more efficient + std::string newPumpLabel = getPumpDevice(); + { + MMThreadGuard scg(stateCacheLock_); + stateCache_.addSetting(PropertySetting(MM::g_Keyword_CoreDevice, MM::g_Keyword_CorePump, newPumpLabel.c_str())); + } +} + +/** +* Homes the pump +*/ +void CMMCore::PumpHome(const char* deviceLabel) throw (CMMError) +{ + std::shared_ptr pPump = + deviceManager_->GetDeviceOfType(deviceLabel); + mm::DeviceModuleLockGuard guard(pPump); + + int ret = pPump->Home(); + + if (ret != DEVICE_OK) + { + logError(deviceLabel, getDeviceErrorText(ret, pPump).c_str()); + throw CMMError(getDeviceErrorText(ret, pPump)); + } +} + +/** +* Stops the pump +*/ +void CMMCore::PumpStop(const char* deviceLabel) throw (CMMError) +{ + std::shared_ptr pPump = + deviceManager_->GetDeviceOfType(deviceLabel); + mm::DeviceModuleLockGuard guard(pPump); + + int ret = pPump->Stop(); + + if (ret != DEVICE_OK) + { + logError(deviceLabel, getDeviceErrorText(ret, pPump).c_str()); + throw CMMError(getDeviceErrorText(ret, pPump)); + } +} + +/** +* Sets whether the pump direction needs to be inverted +*/ +void CMMCore::invertPumpDirection(const char* deviceLabel, bool invert) throw (CMMError) +{ + std::shared_ptr pPump = + deviceManager_->GetDeviceOfType(deviceLabel); + mm::DeviceModuleLockGuard guard(pPump); + + int ret = pPump->invertDirection(invert); + + if (ret != DEVICE_OK) + { + logError(deviceLabel, getDeviceErrorText(ret, pPump).c_str()); + throw CMMError(getDeviceErrorText(ret, pPump)); + } +} + +/** +* Gets whether the pump direction needs to be inverted +*/ +bool CMMCore::isPumpDirectionInverted(const char* deviceLabel) throw (CMMError) +{ + std::shared_ptr pPump = + deviceManager_->GetDeviceOfType(deviceLabel); + mm::DeviceModuleLockGuard guard(pPump); + + bool invert = false; + int ret = pPump->isDirectionInverted(invert); + + if (ret != DEVICE_OK) + { + logError(deviceLabel, getDeviceErrorText(ret, pPump).c_str()); + throw CMMError(getDeviceErrorText(ret, pPump)); + } + return invert; +} + +/** +* Sets the volume of fluid in the pump in uL. Note it does not withdraw upto +* this amount. It is merely to inform MM of the volume in a prefilled pump. +*/ +void CMMCore::setPumpVolume(const char* deviceLabel, double volUl) throw (CMMError) +{ + std::shared_ptr pPump = + deviceManager_->GetDeviceOfType(deviceLabel); + mm::DeviceModuleLockGuard guard(pPump); + + int ret = pPump->setVolumeUl(volUl); + + if (ret != DEVICE_OK) + { + logError(deviceLabel, getDeviceErrorText(ret, pPump).c_str()); + throw CMMError(getDeviceErrorText(ret, pPump)); + } +} + +/** +* Get the fluid volume in the pump in uL +*/ +double CMMCore::getPumpVolume(const char* deviceLabel) throw (CMMError) +{ + std::shared_ptr pPump = + deviceManager_->GetDeviceOfType(deviceLabel); + mm::DeviceModuleLockGuard guard(pPump); + + double volUl = 0; + int ret = pPump->getVolumeUl(volUl); + + if (ret != DEVICE_OK) + { + logError(deviceLabel, getDeviceErrorText(ret, pPump).c_str()); + throw CMMError(getDeviceErrorText(ret, pPump)); + } + return volUl; +} + +/** +* Sets the max volume of the pump in uL +*/ +void CMMCore::setPumpMaxVolume(const char* deviceLabel, double volUl) throw (CMMError) +{ + std::shared_ptr pPump = + deviceManager_->GetDeviceOfType(deviceLabel); + mm::DeviceModuleLockGuard guard(pPump); + + int ret = pPump->setMaxVolumeUl(volUl); + + if (ret != DEVICE_OK) + { + logError(deviceLabel, getDeviceErrorText(ret, pPump).c_str()); + throw CMMError(getDeviceErrorText(ret, pPump)); + } +} + +/** +* Gets the max volume of the pump in uL +*/ +double CMMCore::getPumpMaxVolume(const char* deviceLabel) throw (CMMError) +{ + std::shared_ptr pPump = + deviceManager_->GetDeviceOfType(deviceLabel); + mm::DeviceModuleLockGuard guard(pPump); + + double volUl = 0; + int ret = pPump->getMaxVolumeUl(volUl); + + if (ret != DEVICE_OK) + { + logError(deviceLabel, getDeviceErrorText(ret, pPump).c_str()); + throw CMMError(getDeviceErrorText(ret, pPump)); + } + return volUl; +} + +/** +* Sets the flowrate of the pump in uL per second +*/ +void CMMCore::setPumpFlowrate(const char* deviceLabel, double UlperSec) throw (CMMError) +{ + std::shared_ptr pPump = + deviceManager_->GetDeviceOfType(deviceLabel); + mm::DeviceModuleLockGuard guard(pPump); + + int ret = pPump->setFlowrateUlPerSec(UlperSec); + + if (ret != DEVICE_OK) + { + logError(deviceLabel, getDeviceErrorText(ret, pPump).c_str()); + throw CMMError(getDeviceErrorText(ret, pPump)); + } +} + +/** +* Gets the flowrate of the pump in uL per second +*/ +double CMMCore::getPumpFlowrate(const char* deviceLabel) throw (CMMError) +{ + std::shared_ptr pPump = + deviceManager_->GetDeviceOfType(deviceLabel); + mm::DeviceModuleLockGuard guard(pPump); + + double UlperSec = 0; + int ret = pPump->getFlowrateUlPerSec(UlperSec); + + if (ret != DEVICE_OK) + { + logError(deviceLabel, getDeviceErrorText(ret, pPump).c_str()); + throw CMMError(getDeviceErrorText(ret, pPump)); + } + return UlperSec; +} + +/** +* Start dispensing at the set flowrate until syringe is empty, or manually +* stopped (whichever occurs first). +*/ +void CMMCore::PumpDispense(const char* deviceLabel) throw (CMMError) +{ + std::shared_ptr pPump = + deviceManager_->GetDeviceOfType(deviceLabel); + mm::DeviceModuleLockGuard guard(pPump); + + int ret = pPump->Dispense(); + + if (ret != DEVICE_OK) + { + logError(deviceLabel, getDeviceErrorText(ret, pPump).c_str()); + throw CMMError(getDeviceErrorText(ret, pPump)); + } +} + +/** +* Dispenses for the provided duration (in seconds) at the set flowrate +*/ +void CMMCore::PumpDispenseDuration(const char* deviceLabel, double seconds) throw (CMMError) +{ + std::shared_ptr pPump = + deviceManager_->GetDeviceOfType(deviceLabel); + mm::DeviceModuleLockGuard guard(pPump); + + int ret = pPump->DispenseDuration(seconds); + + if (ret != DEVICE_OK) + { + logError(deviceLabel, getDeviceErrorText(ret, pPump).c_str()); + throw CMMError(getDeviceErrorText(ret, pPump)); + } +} + +/** +* Dispenses the provided volume (in uL) at the set flowrate +*/ +void CMMCore::PumpDispenseVolume(const char* deviceLabel, double microLiter) throw (CMMError) +{ + std::shared_ptr pPump = + deviceManager_->GetDeviceOfType(deviceLabel); + mm::DeviceModuleLockGuard guard(pPump); + + int ret = pPump->DispenseVolume(microLiter); + + if (ret != DEVICE_OK) + { + logError(deviceLabel, getDeviceErrorText(ret, pPump).c_str()); + throw CMMError(getDeviceErrorText(ret, pPump)); + } +} + +/** +* Calibrates the pump +*/ +void CMMCore::PumpCalibrate(const char* deviceLabel) throw (CMMError) +{ + std::shared_ptr pPump = + deviceManager_->GetDeviceOfType(deviceLabel); + mm::DeviceModuleLockGuard guard(pPump); + + int ret = pPump->Calibrate(); + + if (ret != DEVICE_OK) + { + logError(deviceLabel, getDeviceErrorText(ret, pPump).c_str()); + throw CMMError(getDeviceErrorText(ret, pPump)); + } +} + +/** +* Sets the pressure of the pump in kPa +*/ +void CMMCore::setPumpPressure(const char* deviceLabel, double pressurekPa) throw (CMMError) +{ + std::shared_ptr pPump = + deviceManager_->GetDeviceOfType(deviceLabel); + mm::DeviceModuleLockGuard guard(pPump); + + int ret = pPump->setPressure(pressurekPa); + + if (ret != DEVICE_OK) + { + logError(deviceLabel, getDeviceErrorText(ret, pPump).c_str()); + throw CMMError(getDeviceErrorText(ret, pPump)); + } +} + +/** +* Gets the pressure of the pump in kPa +*/ +double CMMCore::getPumpPressure(const char* deviceLabel) throw (CMMError) +{ + std::shared_ptr pPump = + deviceManager_->GetDeviceOfType(deviceLabel); + mm::DeviceModuleLockGuard guard(pPump); + + double pressurekPa = 0; + int ret = pPump->getPressure(pressurekPa); + + if (ret != DEVICE_OK) + { + logError(deviceLabel, getDeviceErrorText(ret, pPump).c_str()); + throw CMMError(getDeviceErrorText(ret, pPump)); + } + return pressurekPa; +} + + + /* SYSTEM STATE */ diff --git a/MMCore/MMCore.h b/MMCore/MMCore.h index d06d86013..85a65d442 100644 --- a/MMCore/MMCore.h +++ b/MMCore/MMCore.h @@ -100,6 +100,7 @@ class AutoFocusInstance; class CameraInstance; class DeviceInstance; class GalvoInstance; +class PumpInstance; class ImageProcessorInstance; class SLMInstance; class ShutterInstance; @@ -277,6 +278,7 @@ class CMMCore std::string getImageProcessorDevice(); std::string getSLMDevice(); std::string getGalvoDevice(); + std::string getPumpDevice(); std::string getChannelGroup(); void setCameraDevice(const char* cameraLabel) throw (CMMError); void setShutterDevice(const char* shutterLabel) throw (CMMError); @@ -286,6 +288,7 @@ class CMMCore void setImageProcessorDevice(const char* procLabel) throw (CMMError); void setSLMDevice(const char* slmLabel) throw (CMMError); void setGalvoDevice(const char* galvoLabel) throw (CMMError); + void setPumpDevice(const char* pumpLabel) throw (CMMError); void setChannelGroup(const char* channelGroup) throw (CMMError); ///@} @@ -577,6 +580,29 @@ class CMMCore std::vector imageSequence) throw (CMMError); ///@} + /** \name Pump control. + * + * Control of pump devices + */ + ///@{ + void PumpHome(const char* pumpLabel) throw (CMMError); + void PumpStop(const char* pumpLabel) throw (CMMError); + void invertPumpDirection(const char* pumpLabel, bool invert) throw (CMMError); + bool isPumpDirectionInverted(const char* pumpLabel) throw (CMMError); + void setPumpVolume(const char* pumpLabel, double volume) throw (CMMError); + double getPumpVolume(const char* pumpLabel) throw (CMMError); + void setPumpMaxVolume(const char* pumpLabel, double volume) throw (CMMError); + double getPumpMaxVolume(const char* pumpLabel) throw (CMMError); + void setPumpFlowrate(const char* pumpLabel, double volume) throw (CMMError); + double getPumpFlowrate(const char* pumpLabel) throw (CMMError); + void PumpDispense(const char* pumpLabel) throw (CMMError); + void PumpDispenseDuration(const char* pumpLabel, double seconds) throw (CMMError); + void PumpDispenseVolume(const char* pumpLabel, double microLiter) throw (CMMError); + void PumpCalibrate(const char* pumpLabel) throw (CMMError); + void setPumpPressure(const char* pumplabel, double pressure) throw (CMMError); + double getPumpPressure(const char* pumplabel) throw (CMMError); + ///@} + /** \name Galvo control. * * Control of beam-steering devices. @@ -646,6 +672,7 @@ class CMMCore std::weak_ptr currentAutofocusDevice_; std::weak_ptr currentSLMDevice_; std::weak_ptr currentGalvoDevice_; + std::weak_ptr currentPumpDevice_; std::weak_ptr currentImageProcessor_; std::string channelGroup_; diff --git a/MMCore/MMCore.vcxproj b/MMCore/MMCore.vcxproj index ebfdcb9ba..8fb685695 100644 --- a/MMCore/MMCore.vcxproj +++ b/MMCore/MMCore.vcxproj @@ -88,6 +88,7 @@ + @@ -131,6 +132,7 @@ + diff --git a/MMCore/MMCore.vcxproj.filters b/MMCore/MMCore.vcxproj.filters index 1920583b6..e0607a1f4 100644 --- a/MMCore/MMCore.vcxproj.filters +++ b/MMCore/MMCore.vcxproj.filters @@ -141,6 +141,9 @@ Source Files + + Source Files\Devices + @@ -305,5 +308,8 @@ Header Files + + Header Files\Devices + - + \ No newline at end of file diff --git a/MMDevice/DeviceBase.h b/MMDevice/DeviceBase.h index fc3d80c68..6470da93d 100644 --- a/MMDevice/DeviceBase.h +++ b/MMDevice/DeviceBase.h @@ -2480,6 +2480,93 @@ class CStateDeviceBase : public CDeviceBase std::map labels_; }; +/** +* Base class for creating pump device adapters. +*/ +template +class CPumpBase : public CDeviceBase +{ + int Home() + { + return DEVICE_UNSUPPORTED_COMMAND; + } + + int Stop() + { + return DEVICE_UNSUPPORTED_COMMAND; + } + + int InvertDirection(bool state) + { + return DEVICE_UNSUPPORTED_COMMAND; + } + + int IsDirectionInverted(bool& state) + { + return DEVICE_UNSUPPORTED_COMMAND; + } + + int SetVolumeUl(double volume) + { + return DEVICE_UNSUPPORTED_COMMAND; + } + + int GetVolumeUl(double& volume) + { + return DEVICE_UNSUPPORTED_COMMAND; + } + + int SetMaxVolumeUl(double volume) + { + return DEVICE_UNSUPPORTED_COMMAND; + } + + int GetMaxVolumeUl(double& volume) + { + return DEVICE_UNSUPPORTED_COMMAND; + } + + int SetFlowrateUlPerSecond(double flowrate) + { + return DEVICE_UNSUPPORTED_COMMAND; + } + + int GetFlowrateUlPerSecond(double& flowrate) + { + return DEVICE_UNSUPPORTED_COMMAND; + } + + int Dispense() + { + return DEVICE_UNSUPPORTED_COMMAND; + } + + int DispenseDuration(double durSec) + { + return DEVICE_UNSUPPORTED_COMMAND; + } + + int DispenseVolume(double volUl) + { + return DEVICE_UNSUPPORTED_COMMAND; + } + + int Calibrate() + { + return DEVICE_UNSUPPORTED_COMMAND; + } + + int SetPressure(double pressure) + { + return DEVICE_UNSUPPORTED_COMMAND; + } + + int GetPressure(double& pressure) + { + return DEVICE_UNSUPPORTED_COMMAND; + } +}; + // _t, a macro for timing single lines. // This macros logs the text of the line, x, measures diff --git a/MMDevice/MMDevice-SharedRuntime.vcxproj.filters b/MMDevice/MMDevice-SharedRuntime.vcxproj.filters index 2f38adf00..1f924c37b 100644 --- a/MMDevice/MMDevice-SharedRuntime.vcxproj.filters +++ b/MMDevice/MMDevice-SharedRuntime.vcxproj.filters @@ -62,4 +62,4 @@ Header Files - + \ No newline at end of file diff --git a/MMDevice/MMDevice.cpp b/MMDevice/MMDevice.cpp index 36c82ef97..805de8913 100644 --- a/MMDevice/MMDevice.cpp +++ b/MMDevice/MMDevice.cpp @@ -47,5 +47,6 @@ const DeviceType Magnifier::Type = MagnifierDevice; const DeviceType SLM::Type = SLMDevice; const DeviceType Galvo::Type = GalvoDevice; const DeviceType Hub::Type = HubDevice; +const DeviceType Pump::Type = PumpDevice; } // namespace MM diff --git a/MMDevice/MMDevice.h b/MMDevice/MMDevice.h index cddfebd00..57465c869 100644 --- a/MMDevice/MMDevice.h +++ b/MMDevice/MMDevice.h @@ -1251,6 +1251,166 @@ namespace MM { virtual int GetChannel(char* channelName) = 0; }; + /** + * Pump API + */ + class Pump : public Device + { + public: + Pump() {} + virtual ~Pump() {} + + // MMDevice API + virtual DeviceType GetType() const { return Type; } + static const DeviceType Type; + + /** + * Homes the pump. + */ + virtual int Home() = 0; + + /** + * Stops the pump. The implementation should halt any dispensing/withdrawal, + * and make the pump available again (make Busy() return false). + */ + virtual int Stop() = 0; + + /** + * Dispenses until volume is zero, or stopped + */ + virtual int Dispense() = 0; + + /** + * Dispenses/withdraws for the provided time, with the flowrate provided + * by GetFlowrate_uLperMin + * Dispensing for an undetermined amount of time can be done with DBL_MAX + * During the dispensing/withdrawal, Busy() should return "true". + * + * Required by MMPump API. + */ + virtual int DispenseDuration(double durSec) = 0; + + /** + * Dispenses/withdraws the provided volume. + * + * The implementation should cause positive volumes to be dispensed, whereas + * negative volumes should be withdrawn. The implementation should prevent + * the volume to go negative (i.e. stop the pump once the syringe is empty), + * or to go over the maximum volume (i.e. stop the pump once it is full). + * This automatically allows for dispensing/withdrawal for an undetermined + * amount of time by providing DBL_MAX for dispense, and DBL_MIN for + * withdraw. + * + * During the dispensing/withdrawal, Busy() should return "true". + * + * Required by MMPump API. + */ + virtual int DispenseVolume(double volUl) = 0; + + /** + * Gets the maximum volume of the pump in microliters (uL). + * + * Required by MMPump API. + */ + virtual int GetMaxVolumeUl(double& volUl) = 0; + + /** + * Sets the maximum volume of the pump in microliters (uL). + * + * Required by MMPump API. + */ + virtual int SetMaxVolumeUl(double volUl) = 0; + + /** + * Gets the current volume of the pump in microliters (uL). + * + * Required by MMPump API. + */ + virtual int GetVolumeUl(double& volUl) = 0; + + /** + * Sets the current volume of the pump in microliters (uL). + * + * Required by MMPump API. + */ + virtual int SetVolumeUl(double volUl) = 0; + + /** + * Sets the direction of the pump. Certain pump + * (e.g. peristaltic and DC pumps) don't have an apriori forward-reverse direction, + * as it depends on how it is connected. This function allows you to switch + * forward and reverse. + * + * The implementation of this function should allow two values, [1] and [-1], + * and should ignore all other values, where [1] indicates that the direction + * is left as-is, and [-1] indicates that the direction should be reversed. + * When the pump is uni-directional, the function should always assign [1] to + * [direction] + * + * Required by MMPump API. + */ + virtual int IsDirectionInverted(bool& inverted) = 0; + + /** + * Sets the direction of the pump. Certain pump + * (e.g. peristaltic and DC pumps) don't have an apriori forward-reverse direction, + * as it depends on how it is connected. This function allows you to switch + * forward and reverse. + * + * The implementation of this function should allow two values, 1 and -1, + * and should ignore all other values, where 1 indicates that the direction + * is left as-is, and -1 indicates that the direction should be reversed. If + * the pump is uni-directional, this function does not need to be + * implemented. + * + * Required by MMPump API. + */ + virtual int InvertDirection(bool inverted) = 0; + + /** + * Gets the flowrate in microliter (uL) per second. + * + * Required by MMPump API. + */ + virtual int GetFlowrateUlPerSecond(double& flowrate) = 0; + + /** + * Sets the flowrate in microliter (uL) per second. The implementation + * should convert the provided flowrate to whichever unit the pump desires + * (steps/s, mL/h, V). + * + * Required by MMPump API. + */ + virtual int SetFlowrateUlPerSecond(double flowrate) = 0; + + /** + * Calibrates the pressure controller. Might not be supported, if so + * either return DEVICE_UNSUPPORTED_COMMAND, or DEVICE_OK, depending + * on the desired behavior + * + * Required by MMPump API. + */ + virtual int Calibrate() = 0; + + /** + * Sets the pressure of the pressure controller. The provided value will + * be in kPa. The implementation should convert the unit from kPa to the + * desired unit by the device. + * + * Required by MMPump API. + */ + virtual int SetPressure(double pressure) = 0; + + /** + * Gets the pressure of the pressure controller. The returned value + * has to be in kPa. The implementation, therefore, should convert the + * value provided by the pressure controller to kPa. + * + * Required by MMPump API. + */ + virtual int GetPressure(double& pressure) = 0; + }; + /** * HUB device. Used for complex uber-device functionality in microscope stands * and managing auto-configuration (discovery) of other devices diff --git a/MMDevice/MMDeviceConstants.h b/MMDevice/MMDeviceConstants.h index 956b05474..b00075eca 100644 --- a/MMDevice/MMDeviceConstants.h +++ b/MMDevice/MMDeviceConstants.h @@ -75,6 +75,7 @@ #define DEVICE_SEQUENCE_TOO_LARGE 39 #define DEVICE_OUT_OF_MEMORY 40 #define DEVICE_NOT_YET_IMPLEMENTED 41 +#define DEVICE_PUMP_IS_RUNNING 42 namespace MM { @@ -130,6 +131,7 @@ namespace MM { const char* const g_Keyword_CoreImageProcessor = "ImageProcessor"; const char* const g_Keyword_CoreSLM = "SLM"; const char* const g_Keyword_CoreGalvo = "Galvo"; + const char* const g_Keyword_CorePump = "Pump"; const char* const g_Keyword_CoreTimeoutMs = "TimeoutMs"; const char* const g_Keyword_Channel = "Channel"; const char* const g_Keyword_Version = "Version"; @@ -138,9 +140,12 @@ namespace MM { const char* const g_Keyword_Transpose_MirrorX = "TransposeMirrorX"; const char* const g_Keyword_Transpose_MirrorY = "TransposeMirrorY"; const char* const g_Keyword_Transpose_Correction = "TransposeCorrection"; - const char* const g_Keyword_Closed_Position = "ClosedPosition"; - const char* const g_Keyword_HubID = "HubID"; - + const char* const g_Keyword_Closed_Position = "ClosedPosition"; + const char* const g_Keyword_HubID = "HubID"; + const char* const g_Keyword_Current_Volume = "Volume_uL"; + const char* const g_Keyword_Min_Volume = "Min_Volume_uL"; + const char* const g_Keyword_Max_Volume = "Max_Volume_uL"; + const char* const g_Keyword_Flowrate = "Flowrate_uL_per_sec"; // image annotations const char* const g_Keyword_Meatdata_Exposure = "Exposure-ms"; @@ -216,7 +221,8 @@ namespace MM { MagnifierDevice, SLMDevice, HubDevice, - GalvoDevice + GalvoDevice, + PumpDevice }; enum PropertyType {