diff --git a/DeviceAdapters/Zaber/ConnectionManager.cpp b/DeviceAdapters/Zaber/ConnectionManager.cpp new file mode 100644 index 000000000..b99add048 --- /dev/null +++ b/DeviceAdapters/Zaber/ConnectionManager.cpp @@ -0,0 +1,39 @@ +#include "ConnectionManager.h" + +std::shared_ptr ConnectionManager::getConnection(std::string port) +{ + std::lock_guard lockGuard(lock_); + if (connections_.count(port) > 0) { + if (auto connectionPtr = connections_.at(port).lock()) { + return connectionPtr; + } + } + + auto connection = std::make_shared(zml::Connection::openSerialPort(port)); + auto id = connection->getInterfaceId(); + connection->getDisconnected().subscribe([=](std::shared_ptr) { + removeConnection(port, id); + }); + connections_[port] = connection; + + return connection; +} + +bool ConnectionManager::removeConnection(std::string port, int interfaceId) +{ + std::lock_guard lockGuard(lock_); + if (connections_.count(port) == 0) { + return false; + } + + if (interfaceId != -1) { + if (auto connection = connections_.at(port).lock()) { + if (connection->getInterfaceId() != interfaceId) { + return false; + } + } + } + + connections_.erase(port); + return true; +} \ No newline at end of file diff --git a/DeviceAdapters/Zaber/ConnectionManager.h b/DeviceAdapters/Zaber/ConnectionManager.h new file mode 100644 index 000000000..a0006a046 --- /dev/null +++ b/DeviceAdapters/Zaber/ConnectionManager.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace zmlbase = zaber::motion; +namespace zml = zaber::motion::ascii; + +class ConnectionManager +{ +public: + std::shared_ptr getConnection(std::string port); + bool removeConnection(std::string port, int interfaceId = -1); +private: + std::mutex lock_; + std::map> connections_; +}; + diff --git a/DeviceAdapters/Zaber/FilterCubeTurret.cpp b/DeviceAdapters/Zaber/FilterCubeTurret.cpp index 8c2d7876f..5e08e77cd 100644 --- a/DeviceAdapters/Zaber/FilterCubeTurret.cpp +++ b/DeviceAdapters/Zaber/FilterCubeTurret.cpp @@ -42,11 +42,7 @@ FilterCubeTurret::FilterCubeTurret() this->LogMessage("FilterCubeTurret::FilterCubeTurret\n", true); InitializeDefaultErrorMessages(); - SetErrorText(ERR_PORT_CHANGE_FORBIDDEN, g_Msg_PORT_CHANGE_FORBIDDEN); - SetErrorText(ERR_DRIVER_DISABLED, g_Msg_DRIVER_DISABLED); - SetErrorText(ERR_BUSY_TIMEOUT, g_Msg_BUSY_TIMEOUT); - SetErrorText(ERR_COMMAND_REJECTED, g_Msg_COMMAND_REJECTED); - SetErrorText(ERR_SETTING_FAILED, g_Msg_SETTING_FAILED); + ZaberBase::setErrorMessages([&](auto code, auto message) { this->SetErrorText(code, message); }); EnableDelay(); // signals that the delay setting will be used @@ -56,7 +52,7 @@ FilterCubeTurret::FilterCubeTurret() CreateProperty(MM::g_Keyword_Description, "Zaber filter cube turret device adapter", MM::String, true); CPropertyAction* pAct = new CPropertyAction(this, &FilterCubeTurret::PortGetSet); - CreateProperty(MM::g_Keyword_Port, "COM1", MM::String, false, pAct, true); + CreateProperty("Zaber Serial Port", port_.c_str(), MM::String, false, pAct, true); pAct = new CPropertyAction (this, &FilterCubeTurret::DeviceAddressGetSet); CreateIntegerProperty("Controller Device Number", deviceAddress_, false, pAct, true); @@ -83,33 +79,18 @@ void FilterCubeTurret::GetName(char* name) const int FilterCubeTurret::Initialize() { - if (initialized_) + if (initialized_) { return DEVICE_OK; } core_ = GetCoreCallback(); - - this->LogMessage("FilterCubeTurret::Initialize\n", true); - int ret = ClearPort(); - if (ret != DEVICE_OK) - { - this->LogMessage("Clearing the serial port receive buffer failed. Are the port settings correct?\n", true); - return ret; - } - - // Disable alert messages. - ret = SetSetting(deviceAddress_, 0, "comm.alert", 0); - if (ret != DEVICE_OK) - { - this->LogMessage("Initial attempt to communicate with device failed.\n", true); - return ret; - } + this->LogMessage("FilterCubeTurret::Initialize\n", true); // Home the device to make sure it has its index. - ret = SendAndPollUntilIdle(deviceAddress_, 0, "home", 10000); - if (ret != DEVICE_OK) + auto ret = SendAndPollUntilIdle(deviceAddress_, 0, "home"); + if (ret != DEVICE_OK) { this->LogMessage("Attempt to detect filter holder type failed; is this device an X-FWR?\n", true); return ret; @@ -117,7 +98,7 @@ int FilterCubeTurret::Initialize() long index = -1; ret = GetRotaryIndexedDeviceInfo(deviceAddress_, 0, numPositions_, index); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { this->LogMessage("Attempt to detect filter cube turret state and number of positions failed.\n", true); return ret; @@ -143,7 +124,7 @@ int FilterCubeTurret::Initialize() } ret = UpdateStatus(); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { return ret; } @@ -197,7 +178,7 @@ int FilterCubeTurret::GetPositionLabel(long pos, char* label) const { std::string str("Filter Cube "); char numBuf[15]; - snprintf(numBuf, 15, "%ld", pos); + snprintf(numBuf, 15, "%ld", pos + 1); str.append(numBuf); CDeviceUtils::CopyLimitedString(label, str.c_str()); } @@ -281,7 +262,7 @@ int FilterCubeTurret::PositionGetSet(MM::PropertyBase* pProp, MM::ActionType eAc if (initialized_) { int ret = GetSetting(deviceAddress_, 0, "motion.index.num", index); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { return ret; } @@ -301,7 +282,7 @@ int FilterCubeTurret::PositionGetSet(MM::PropertyBase* pProp, MM::ActionType eAc if ((index >= 0) && (index < numPositions_)) { int ret = SendMoveCommand(deviceAddress_, 0, "index", index + 1); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { return ret; } @@ -317,4 +298,3 @@ int FilterCubeTurret::PositionGetSet(MM::PropertyBase* pProp, MM::ActionType eAc return DEVICE_OK; } - diff --git a/DeviceAdapters/Zaber/FilterWheel.cpp b/DeviceAdapters/Zaber/FilterWheel.cpp index 28ed4e8fa..008dcb78f 100644 --- a/DeviceAdapters/Zaber/FilterWheel.cpp +++ b/DeviceAdapters/Zaber/FilterWheel.cpp @@ -41,11 +41,7 @@ FilterWheel::FilterWheel() this->LogMessage("FilterWheel::FilterWheel\n", true); InitializeDefaultErrorMessages(); - SetErrorText(ERR_PORT_CHANGE_FORBIDDEN, g_Msg_PORT_CHANGE_FORBIDDEN); - SetErrorText(ERR_DRIVER_DISABLED, g_Msg_DRIVER_DISABLED); - SetErrorText(ERR_BUSY_TIMEOUT, g_Msg_BUSY_TIMEOUT); - SetErrorText(ERR_COMMAND_REJECTED, g_Msg_COMMAND_REJECTED); - SetErrorText(ERR_SETTING_FAILED, g_Msg_SETTING_FAILED); + ZaberBase::setErrorMessages([&](auto code, auto message) { this->SetErrorText(code, message); }); EnableDelay(); // signals that the delay setting will be used @@ -55,7 +51,7 @@ FilterWheel::FilterWheel() CreateProperty(MM::g_Keyword_Description, "Zaber filter wheel device adapter", MM::String, true); CPropertyAction* pAct = new CPropertyAction(this, &FilterWheel::PortGetSet); - CreateProperty(MM::g_Keyword_Port, "COM1", MM::String, false, pAct, true); + CreateProperty("Zaber Serial Port", port_.c_str(), MM::String, false, pAct, true); pAct = new CPropertyAction (this, &FilterWheel::DeviceAddressGetSet); CreateIntegerProperty("Controller Device Number", deviceAddress_, false, pAct, true); @@ -82,33 +78,18 @@ void FilterWheel::GetName(char* name) const int FilterWheel::Initialize() { - if (initialized_) + if (initialized_) { return DEVICE_OK; } core_ = GetCoreCallback(); - - this->LogMessage("FilterWheel::Initialize\n", true); - int ret = ClearPort(); - if (ret != DEVICE_OK) - { - this->LogMessage("Clearing the serial port receive buffer failed. Are the port settings correct?\n", true); - return ret; - } - - // Disable alert messages. - ret = SetSetting(deviceAddress_, 0, "comm.alert", 0); - if (ret != DEVICE_OK) - { - this->LogMessage("Initial attempt to communicate with device failed.\n", true); - return ret; - } + this->LogMessage("FilterWheel::Initialize\n", true); // Make the Filter Wheel detect the current holder. - ret = SendAndPollUntilIdle(deviceAddress_, 0, "tools detectholder", 60000); - if (ret != DEVICE_OK) + auto ret = SendAndPollUntilIdle(deviceAddress_, 0, "tools detectholder"); + if (ret != DEVICE_OK) { this->LogMessage("Attempt to detect filter holder type failed; is this device an X-FWR?\n", true); return ret; @@ -117,7 +98,7 @@ int FilterWheel::Initialize() // Get the number of positions and the current position. long index = -1; ret = GetRotaryIndexedDeviceInfo(deviceAddress_, 0, numPositions_, index); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { this->LogMessage("Attempt to detect filter wheel state and number of positions failed.\n", true); return ret; @@ -143,7 +124,7 @@ int FilterWheel::Initialize() } ret = UpdateStatus(); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { return ret; } @@ -193,7 +174,7 @@ int FilterWheel::GetPositionLabel(long pos, char* label) const { std::string str("Filter "); char numBuf[15]; - snprintf(numBuf, 15, "%ld", pos); + snprintf(numBuf, 15, "%ld", pos + 1); str.append(numBuf); CDeviceUtils::CopyLimitedString(label, str.c_str()); } @@ -276,7 +257,7 @@ int FilterWheel::PositionGetSet(MM::PropertyBase* pProp, MM::ActionType eAct) if (initialized_) { int ret = GetSetting(deviceAddress_, 0, "motion.index.num", index); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { return ret; } @@ -296,7 +277,7 @@ int FilterWheel::PositionGetSet(MM::PropertyBase* pProp, MM::ActionType eAct) if ((index >= 0) && (index < numPositions_)) { int ret = SendMoveCommand(deviceAddress_, 0, "index", index + 1); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { return ret; } @@ -312,4 +293,3 @@ int FilterWheel::PositionGetSet(MM::PropertyBase* pProp, MM::ActionType eAct) return DEVICE_OK; } - diff --git a/DeviceAdapters/Zaber/Illuminator.cpp b/DeviceAdapters/Zaber/Illuminator.cpp index 2594c8c72..9d5b2ab24 100644 --- a/DeviceAdapters/Zaber/Illuminator.cpp +++ b/DeviceAdapters/Zaber/Illuminator.cpp @@ -32,8 +32,6 @@ const char* g_IlluminatorDescription = "Zaber Illuminator"; using namespace std; -const char* g_Msg_FIRMWARE_UNSUPPORTED = "The Zaber Illuminator driver only works with Firmware version 7.14 or later; please see the documentation for information on how to update the firmware."; - Illuminator::Illuminator() : ZaberBase(this) , deviceAddress_(1) @@ -48,18 +46,7 @@ Illuminator::Illuminator() LogMessage("Illuminator::Illuminator\n", true); InitializeDefaultErrorMessages(); - SetErrorText(ERR_PORT_CHANGE_FORBIDDEN, g_Msg_PORT_CHANGE_FORBIDDEN); - SetErrorText(ERR_DRIVER_DISABLED, g_Msg_DRIVER_DISABLED); - SetErrorText(ERR_BUSY_TIMEOUT, g_Msg_BUSY_TIMEOUT); - SetErrorText(ERR_COMMAND_REJECTED, g_Msg_COMMAND_REJECTED); - SetErrorText(ERR_SETTING_FAILED, g_Msg_SETTING_FAILED); - SetErrorText(ERR_AXIS_COUNT, g_Msg_AXIS_COUNT); - SetErrorText(ERR_LAMP_DISCONNECTED, g_Msg_LAMP_DISCONNECTED); - SetErrorText(ERR_LAMP_OVERHEATED, g_Msg_LAMP_OVERHEATED); - SetErrorText(ERR_PERIPHERAL_DISCONNECTED, g_Msg_PERIPHERAL_DISCONNECTED); - SetErrorText(ERR_PERIPHERAL_UNSUPPORTED, g_Msg_PERIPHERAL_UNSUPPORTED); - SetErrorText(ERR_DRIVER_DISABLED, g_Msg_DRIVER_DISABLED); - SetErrorText(ERR_FIRMWARE_UNSUPPORTED, g_Msg_FIRMWARE_UNSUPPORTED); + ZaberBase::setErrorMessages([&](auto code, auto message) { this->SetErrorText(code, message); }); // Pre-initialization properties CreateProperty(MM::g_Keyword_Name, g_IlluminatorName, MM::String, true); @@ -67,7 +54,7 @@ Illuminator::Illuminator() CreateProperty(MM::g_Keyword_Description, "Zaber illuminator device adapter", MM::String, true); CPropertyAction* pAct = new CPropertyAction(this, &Illuminator::PortGetSet); - CreateProperty(MM::g_Keyword_Port, "COM1", MM::String, false, pAct, true); + CreateProperty("Zaber Serial Port", port_.c_str(), MM::String, false, pAct, true); pAct = new CPropertyAction (this, &Illuminator::DeviceAddressGetSet); CreateIntegerProperty("Controller Device Number", deviceAddress_, false, pAct, true); @@ -94,29 +81,22 @@ void Illuminator::GetName(char* name) const int Illuminator::Initialize() { - if (initialized_) + if (initialized_) { return DEVICE_OK; } core_ = GetCoreCallback(); - - LogMessage("Illuminator::Initialize\n", true); - int ret = ClearPort(); - if (ret != DEVICE_OK) - { - LogMessage("Clearing the serial port receive buffer failed. Are the port settings correct?\n", true); - return ret; - } + LogMessage("Illuminator::Initialize\n", true); // Check the firmware version, ignoring any warning flags. double version; - ret = GetFirmwareVersion(deviceAddress_, version); // Error code deliberately ignored; the version is sufficient. - if (version == 0.0) + auto ret = GetFirmwareVersion(deviceAddress_, version); + if (ret != DEVICE_OK) { LogMessage("Firmware version read failed.\n", true); - return (ret == DEVICE_OK) ? ERR_COMMAND_REJECTED : ret; + return ret; } else if (version < 7.14) { @@ -126,24 +106,16 @@ int Illuminator::Initialize() // Activate any recently changed peripherals. ret = ActivatePeripheralsIfNeeded(deviceAddress_); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { LogMessage("Peripheral activation check failed.\n", true); return ret; } - // Disable alert messages. - ret = SetSetting(deviceAddress_, 0, "comm.alert", 0); - if (ret != DEVICE_OK) - { - LogMessage("Initial attempt to communicate with device failed.\n", true); - return ret; - } - // Detect the number of LEDs. long data = 0; ret = GetSetting(deviceAddress_, 0, "system.axiscount", data); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { LogMessage("Failed to detect the number of lamps in the illuminator.\n", true); return ret; @@ -151,10 +123,10 @@ int Illuminator::Initialize() else if ((data < 1) || (data > 256)) // Arbitrary upper limit. { LogMessage("Number of lamps is out of range.\n", true); - return ERR_AXIS_COUNT; + return DEVICE_NOT_SUPPORTED; } - - numLamps_ = (int)data; + + numLamps_ = (int)data; currentFlux_ = new double[numLamps_]; maxFlux_ = new double[numLamps_]; lampExists_ = new bool[numLamps_]; @@ -187,7 +159,7 @@ int Illuminator::Initialize() if (lampExists_[i]) { ret = GetSetting(deviceAddress_, i + 1, "lamp.wavelength.peak", data); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { LogMessage("Failed to detect the peak wavelength of a lamp.\n", true); return ret; @@ -201,7 +173,7 @@ int Illuminator::Initialize() if (lampExists_[i]) { ret = GetSetting(deviceAddress_, i + 1, "lamp.wavelength.fwhm", data); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { LogMessage("Failed to detect the full-width half-magnitude of a lamp.\n", true); return ret; @@ -216,7 +188,7 @@ int Illuminator::Initialize() if (lampExists_[i]) { ret = GetSetting(deviceAddress_, i + 1, "lamp.flux", analogData); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { LogMessage("Failed to read flux of a lamp.\n", true); return ret; @@ -228,7 +200,7 @@ int Illuminator::Initialize() if (lampExists_[i]) { ret = GetSetting(deviceAddress_, i + 1, "lamp.flux.max", analogData); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { LogMessage("Failed to read max flux of a lamp.\n", true); return ret; @@ -244,7 +216,7 @@ int Illuminator::Initialize() } ret = UpdateStatus(); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { return ret; } @@ -313,7 +285,7 @@ int Illuminator::SetOpen(bool open) { if (canUseDeviceLampOnCommand_) { - int ret = SendCommand(deviceAddress_, 0, "lamp on"); + int ret = Command(deviceAddress_, 0, "lamp on"); if (ret == DEVICE_OK) { isOpen_ = true; @@ -331,10 +303,10 @@ int Illuminator::SetOpen(bool open) else { for (int i = 0; i < numLamps_; ++i) - { + { if (lampExists_[i] && !lampIsOn_[i] && (currentFlux_[i] > 0.0)) { - int ret = SendCommand(deviceAddress_, i + 1, "lamp on"); + int ret = Command(deviceAddress_, i + 1, "lamp on"); if (ret != DEVICE_OK) { return ret; @@ -350,7 +322,7 @@ int Illuminator::SetOpen(bool open) } else { - int ret = SendCommand(deviceAddress_, 0, "lamp off"); // Always works at device scope. + int ret = Command(deviceAddress_, 0, "lamp off"); // Always works at device scope. if (ret == DEVICE_OK) { isOpen_ = false; @@ -441,7 +413,7 @@ int Illuminator::IntensityGetSet(MM::PropertyBase* pProp, MM::ActionType eAct, l if (initialized_ && lampExists_[index]) { int ret = GetSetting(deviceAddress_, index + 1, "lamp.flux", flux); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { return ret; } @@ -460,7 +432,7 @@ int Illuminator::IntensityGetSet(MM::PropertyBase* pProp, MM::ActionType eAct, l double flux = (maxFlux_[index] * intensity) / 100.0; int ret = SetSetting(deviceAddress_, index + 1, "lamp.flux", flux, 3); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { return ret; } @@ -470,8 +442,8 @@ int Illuminator::IntensityGetSet(MM::PropertyBase* pProp, MM::ActionType eAct, l // Turn the lamp on if the shutter is open. if (isOpen_ && !lampIsOn_[index] && (flux > 0.0)) { - ret = SendCommand(deviceAddress_, index + 1, "lamp on"); - if (ret != DEVICE_OK) + ret = Command(deviceAddress_, index + 1, "lamp on"); + if (ret != DEVICE_OK) { return ret; } @@ -489,58 +461,51 @@ int Illuminator::IntensityGetSet(MM::PropertyBase* pProp, MM::ActionType eAct, l int Illuminator::RefreshLampStatus() { - ostringstream cmd; - cmd << cmdPrefix_ << deviceAddress_ << " get lamp.status"; - vector resp; - int ret = QueryCommand(cmd.str().c_str(), resp); + std::vector statusAll; + int ret = GetSettings(deviceAddress_, 0, "lamp.status", statusAll); int secondaryResult = DEVICE_OK; - if (ret != DEVICE_OK) { - // BADCOMMAND in response to "get lamp.status" means no lamps are connected. - if ((ret == ERR_COMMAND_REJECTED) && (resp.size() > 5) && (resp[5] == "BADCOMMAND")) - { - canUseDeviceLampOnCommand_ = false; - for (int i = 0; i < numLamps_; i++) - { - lampExists_[i] = false; - lampIsOn_[i] = false; - } + return ret; + } - return DEVICE_OK; - } - else + if (statusAll.empty()) + { + canUseDeviceLampOnCommand_ = false; + for (int i = 0; i < numLamps_; i++) { - return ret; + lampExists_[i] = false; + lampIsOn_[i] = false; } + + return DEVICE_OK; } isOpen_ = false; canUseDeviceLampOnCommand_ = true; - for (int i = 5; i < resp.size(); ++i) + for (int index = 0; index < statusAll.size(); index++) { - int index = i - 5; - - if (resp[i] == "NA") // Inactive axis. + auto status = statusAll[index]; + if (status != status) // Inactive axis. { lampExists_[index] = false; lampIsOn_[index] = false; } else { - switch (resp[i][0]) + switch (static_cast(status)) { - case '1': + case 1: lampExists_[index] = true; lampIsOn_[index] = false; break; - case '2': + case 2: lampExists_[index] = true; lampIsOn_[index] = true; isOpen_ = true; break; - case '3': + case 3: secondaryResult = ERR_LAMP_OVERHEATED; lampExists_[index] = true; lampIsOn_[index] = false; diff --git a/DeviceAdapters/Zaber/Makefile.am b/DeviceAdapters/Zaber/Makefile.am index 937c7424b..0f8f24692 100644 --- a/DeviceAdapters/Zaber/Makefile.am +++ b/DeviceAdapters/Zaber/Makefile.am @@ -1,20 +1,44 @@ +# We require C++17 because libzml is C++17. Setting the C++ standard for +# individual device adapters is a hack and should not be imitated except when +# there is no other option. +AM_CXXFLAGS = $(MMDEVAPI_CXXFLAGS) -I$(MMTHIRDPARTYPUBLIC)/Zaber/zaber-motion/include -std=c++17 -Wno-dynamic-exception-spec -AM_CXXFLAGS = $(MMDEVAPI_CXXFLAGS) deviceadapter_LTLIBRARIES = libmmgr_dal_Zaber.la libmmgr_dal_Zaber_la_SOURCES = \ - Illuminator.cpp \ - Illuminator.h \ - FilterCubeTurret.cpp \ - FilterCubeTurret.h \ - FilterWheel.cpp \ - FilterWheel.h \ - XYStage.cpp \ - XYStage.h \ - Zaber.cpp \ - Zaber.h \ - Stage.cpp \ - Stage.h + ObjectiveChanger.cpp \ + ObjectiveChanger.h \ + Illuminator.cpp \ + Illuminator.h \ + FilterCubeTurret.cpp \ + FilterCubeTurret.h \ + FilterWheel.cpp \ + FilterWheel.h \ + XYStage.cpp \ + XYStage.h \ + Zaber.cpp \ + Zaber.h \ + ConnectionManager.cpp \ + ConnectionManager.h \ + Stage.cpp \ + Stage.h libmmgr_dal_Zaber_la_LIBADD = $(MMDEVAPI_LIBADD) -libmmgr_dal_Zaber_la_LDFLAGS = $(MMDEVAPI_LDFLAGS) + +if HOST_LINUX +ZML_DIR_LIB = $(MMTHIRDPARTYPUBLIC)/Zaber/zaber-motion/linux-amd64/lib +RPATH_FLAGS = -Wl,-rpath -Wl,\$$ORIGIN +dist_deviceadapter_DATA = \ + $(ZML_DIR_LIB)/libzml.so.3.4 \ + $(ZML_DIR_LIB)/libzaber-motion-lib-linux-amd64.so.3.4.3 +endif + +if HOST_OSX +ZML_DIR_LIB = $(MMTHIRDPARTYPUBLIC)/Zaber/zaber-motion/darwin/lib +RPATH_FLAGS = -Wl,-rpath -Wl,@loader_path +dist_deviceadapter_DATA = \ + $(ZML_DIR_LIB)/libzaber-motion-lib-darwin-uni.3.4.3.dylib \ + $(ZML_DIR_LIB)/libzml.3.4.dylib +endif + +libmmgr_dal_Zaber_la_LDFLAGS = $(MMDEVAPI_LDFLAGS) -L$(ZML_DIR_LIB) -lzml $(RPATH_FLAGS) EXTRA_DIST = Zaber.vcxproj Zaber.vcxproj.filters license.txt diff --git a/DeviceAdapters/Zaber/ObjectiveChanger.cpp b/DeviceAdapters/Zaber/ObjectiveChanger.cpp new file mode 100644 index 000000000..fbeebd556 --- /dev/null +++ b/DeviceAdapters/Zaber/ObjectiveChanger.cpp @@ -0,0 +1,366 @@ +/////////////////////////////////////////////////////////////////////////////// +// FILE: ObjectiveChanger.cpp +// PROJECT: Micro-Manager +// SUBSYSTEM: DeviceAdapters +//----------------------------------------------------------------------------- +// DESCRIPTION: Device adapter for Zaber's X-MOR objective changer. +// +// AUTHOR: Martin Zak (contact@zaber.com) + +// COPYRIGHT: Zaber Technologies, 2023 + +// 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. + +#ifdef WIN32 +#pragma warning(disable: 4355) +#endif +#include "FixSnprintf.h" + +#include "ObjectiveChanger.h" + +const char* g_ObjectiveChangerName = "ObjectiveChanger"; +const char* g_ObjectiveChangerDescription = "Zaber Objective Changer device adapter"; + +using namespace std; + +ObjectiveChanger::ObjectiveChanger() + : ZaberBase(this) + , xMorAddress_(1) + , xLdaAddress_(2) + , numPositions_(0) + , currentObjective_(1) + , focusOffset_(0) + , changedTime_(0.0) +{ + this->LogMessage("ObjectiveChanger::ObjectiveChanger\n", true); + + InitializeDefaultErrorMessages(); + ZaberBase::setErrorMessages([&](auto code, auto message) { this->SetErrorText(code, message); }); + + EnableDelay(); // signals that the delay setting will be used + + // Pre-initialization properties + CreateProperty(MM::g_Keyword_Name, g_ObjectiveChangerName, MM::String, true); + + CreateProperty(MM::g_Keyword_Description, g_ObjectiveChangerDescription, MM::String, true); + + CPropertyAction* pAct = new CPropertyAction(this, &ObjectiveChanger::PortGetSet); + CreateProperty("Zaber Serial Port", port_.c_str(), MM::String, false, pAct, true); + + pAct = new CPropertyAction(this, &ObjectiveChanger::XMorAddressGetSet); + CreateIntegerProperty("Objective Changer Device Number", xMorAddress_, false, pAct, true); + SetPropertyLimits("Objective Changer Device Number", 1, 99); + + pAct = new CPropertyAction(this, &ObjectiveChanger::XLdaAddressGetSet); + CreateIntegerProperty("Focus State Device Number", xLdaAddress_, false, pAct, true); + SetPropertyLimits("Focus State Device Number", 1, 99); +} + + +ObjectiveChanger::~ObjectiveChanger() +{ + this->LogMessage("ObjectiveChanger::~ObjectiveChanger\n", true); + Shutdown(); +} + + +/////////////////////////////////////////////////////////////////////////////// +// Stage & Device API methods +/////////////////////////////////////////////////////////////////////////////// + +void ObjectiveChanger::GetName(char* name) const +{ + CDeviceUtils::CopyLimitedString(name, g_ObjectiveChangerDescription); +} + + +int ObjectiveChanger::Initialize() +{ + if (initialized_) + { + return DEVICE_OK; + } + + core_ = GetCoreCallback(); + + this->LogMessage("ObjectiveChanger::Initialize\n", true); + + auto ret = handleException([=]() { + ensureConnected(); + if (!this->changer_.getFocusAxis().isHomed()) { + this->changer_.change(1); + } + }); + if (ret != DEVICE_OK) + { + this->LogMessage("Attempt to detect and home objective changer failed.\n", true); + return ret; + } + + // Get the number of positions and the current position. + long index = -1; + ret = GetRotaryIndexedDeviceInfo(xMorAddress_, 0, numPositions_, index); + if (ret != DEVICE_OK) + { + this->LogMessage("Attempt to detect objective changer state and number of positions failed.\n", true); + return ret; + } + + CreateIntegerProperty("Number of Positions", numPositions_, true, 0, false); + + auto pAct = new CPropertyAction(this, &ObjectiveChanger::FocusOffsetGetSet); + CreateFloatProperty("Objective Focus [mm]", focusOffset_, false, pAct); + + pAct = new CPropertyAction(this, &ObjectiveChanger::PositionGetSet); + CreateIntegerProperty(MM::g_Keyword_State, index, false, pAct, false); + + pAct = new CPropertyAction(this, &ObjectiveChanger::DelayGetSet); + ret = CreateProperty(MM::g_Keyword_Delay, "0.0", MM::Float, false, pAct); + if (ret != DEVICE_OK) + { + return ret; + } + + pAct = new CPropertyAction(this, &CStateBase::OnLabel); + ret = CreateProperty(MM::g_Keyword_Label, "", MM::String, false, pAct); + if (ret != DEVICE_OK) + { + return ret; + } + + ret = UpdateStatus(); + if (ret != DEVICE_OK) + { + return ret; + } + + if (ret == DEVICE_OK) + { + initialized_ = true; + return DEVICE_OK; + } + else + { + return ret; + } +} + + +int ObjectiveChanger::Shutdown() +{ + this->LogMessage("ObjectiveChanger::Shutdown\n", true); + + if (initialized_) + { + initialized_ = false; + } + + return DEVICE_OK; +} + + +bool ObjectiveChanger::Busy() +{ + this->LogMessage("ObjectiveChanger::Busy\n", true); + + MM::MMTime interval = GetCurrentMMTime() - changedTime_; + MM::MMTime delay(GetDelayMs() * 1000.0); + + if (interval < delay) { + return true; + } + else + { + return IsBusy(xMorAddress_) || IsBusy(xLdaAddress_); + } +} + + +int ObjectiveChanger::GetPositionLabel(long pos, char* label) const +{ + if (DEVICE_OK != CStateDeviceBase::GetPositionLabel(pos, label)) + { + std::stringstream labelStr("Objective "); + labelStr << pos + 1; + CDeviceUtils::CopyLimitedString(label, labelStr.str().c_str()); + } + + return DEVICE_OK; +} + + +/////////////////////////////////////////////////////////////////////////////// +// Action handlers +// Handle changes and updates to property values. +/////////////////////////////////////////////////////////////////////////////// + +int ObjectiveChanger::DelayGetSet(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::BeforeGet) + { + pProp->Set(this->GetDelayMs()); + } + else if (eAct == MM::AfterSet) + { + double delay; + pProp->Get(delay); + this->SetDelayMs(delay); + } + + return DEVICE_OK; +} + +int ObjectiveChanger::PortGetSet(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + ostringstream os; + os << "ObjectiveChanger::PortGetSet(" << pProp << ", " << eAct << ")\n"; + this->LogMessage(os.str().c_str(), false); + + if (eAct == MM::BeforeGet) + { + pProp->Set(port_.c_str()); + } + else if (eAct == MM::AfterSet) + { + if (initialized_) + { + // revert + pProp->Set(port_.c_str()); + return ERR_PORT_CHANGE_FORBIDDEN; + } + + pProp->Get(port_); + } + + return DEVICE_OK; +} + + +int ObjectiveChanger::XMorAddressGetSet(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + this->LogMessage("ObjectiveChanger::XMorAddressGetSet\n", true); + + if (eAct == MM::AfterSet) + { + if (initialized_) + { + resetConnection(); + } + + pProp->Get(xMorAddress_); + } + else if (eAct == MM::BeforeGet) + { + pProp->Set(xMorAddress_); + } + + return DEVICE_OK; +} + + +int ObjectiveChanger::XLdaAddressGetSet(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + this->LogMessage("ObjectiveChanger::XLdaAddressGetSet\n", true); + + if (eAct == MM::AfterSet) + { + if (initialized_) + { + resetConnection(); + } + + pProp->Get(xLdaAddress_); + } + else if (eAct == MM::BeforeGet) + { + pProp->Set(xLdaAddress_); + } + + return DEVICE_OK; +} + + +int ObjectiveChanger::PositionGetSet(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + this->LogMessage("ObjectiveChanger::PositionGetSet\n", true); + + if (eAct == MM::BeforeGet) + { + if (initialized_) + { + int ret = GetSetting(xMorAddress_, 0, "motion.index.num", currentObjective_); + if (ret != DEVICE_OK) + { + return ret; + } + } + + // MM uses 0-based indices for states, but the Zaber index + // numbers position indices starting at 1. + pProp->Set(currentObjective_ - 1); + } + else if (eAct == MM::AfterSet) + { + long indexToSet; + pProp->Get(indexToSet); + + if (initialized_) + { + if ((indexToSet >= 0) && (indexToSet < numPositions_)) + { + return setObjective(indexToSet + 1, false); + } + else + { + this->LogMessage("Requested position is outside the legal range.\n", true); + return DEVICE_UNKNOWN_POSITION; + } + } + } + + return DEVICE_OK; +} + +int ObjectiveChanger::FocusOffsetGetSet(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + this->LogMessage("ObjectiveChanger::FocusOffsetGetSet\n", true); + + if (eAct == MM::AfterSet) + { + pProp->Get(focusOffset_); + } + else if (eAct == MM::BeforeGet) + { + pProp->Set(focusOffset_); + return setObjective(currentObjective_, true); + } + + return DEVICE_OK; +} + +int ObjectiveChanger::setObjective(long objective, bool applyOffset) { + return handleException([=]() { + ensureConnected(); + zmlbase::Measurement offset; + if (applyOffset) { + offset = zmlbase::Measurement(focusOffset_ * ObjectiveChanger_::xLdaNativePerMm); + } + this->changer_.change(objective, offset); + currentObjective_ = objective; + changedTime_ = GetCurrentMMTime(); + }); +} + +void ObjectiveChanger::onNewConnection() { + ZaberBase::onNewConnection(); + changer_ = zmlmi::ObjectiveChanger::find(*this->connection_, static_cast(xMorAddress_), static_cast(xLdaAddress_)); +} diff --git a/DeviceAdapters/Zaber/ObjectiveChanger.h b/DeviceAdapters/Zaber/ObjectiveChanger.h new file mode 100644 index 000000000..05c8b15f8 --- /dev/null +++ b/DeviceAdapters/Zaber/ObjectiveChanger.h @@ -0,0 +1,80 @@ +#pragma once +/////////////////////////////////////////////////////////////////////////////// +// FILE: ObjectiveChanger.h +// PROJECT: Micro-Manager +// SUBSYSTEM: DeviceAdapters +//----------------------------------------------------------------------------- +// DESCRIPTION: Device adapter for Zaber's X-MOR objective changer. +// +// AUTHOR: Martin Zak (contact@zaber.com) + +// COPYRIGHT: Zaber Technologies, 2023 + +// 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 "Zaber.h" + +extern const char* g_ObjectiveChangerName; +extern const char* g_ObjectiveChangerDescription; + +namespace ObjectiveChanger_ { + const double xLdaNativePerMm = 1000000.0; +} + +class ObjectiveChanger : public CStateDeviceBase, public ZaberBase +{ +public: + ObjectiveChanger(); + ~ObjectiveChanger(); + + // Device API + // ---------- + int Initialize(); + int Shutdown(); + void GetName(char* name) const; + bool Busy(); + + // Stage API + // --------- + unsigned long GetNumberOfPositions() const + { + return numPositions_; + } + + // Base class overrides + // ---------------- + virtual int GetPositionLabel(long pos, char* label) const; + + // ZaverBase class overrides + // ---------------- + virtual void onNewConnection(); + + // Properties + // ---------------- + int DelayGetSet(MM::PropertyBase* pProp, MM::ActionType eAct); + int PortGetSet(MM::PropertyBase* pProp, MM::ActionType eAct); + int XMorAddressGetSet(MM::PropertyBase* pProp, MM::ActionType eAct); + int XLdaAddressGetSet(MM::PropertyBase* pProp, MM::ActionType eAct); + int PositionGetSet(MM::PropertyBase* pProp, MM::ActionType eAct); + int FocusOffsetGetSet(MM::PropertyBase* pProp, MM::ActionType eAct); + + int setObjective(long objective, bool applyOffset); +private: + long xMorAddress_; + long xLdaAddress_; + long numPositions_; + double focusOffset_; + long currentObjective_; + MM::MMTime changedTime_; + + zmlmi::ObjectiveChanger changer_; +}; diff --git a/DeviceAdapters/Zaber/Stage.cpp b/DeviceAdapters/Zaber/Stage.cpp index 891ce7466..2f60785ec 100644 --- a/DeviceAdapters/Zaber/Stage.cpp +++ b/DeviceAdapters/Zaber/Stage.cpp @@ -37,7 +37,6 @@ Stage::Stage() : deviceAddress_(1), axisNumber_(1), lockstepGroup_(0), - homingTimeoutMs_(20000), stepSizeUm_(0.15625), convFactor_(1.6384), // not very informative name cmdPrefix_("/"), @@ -48,13 +47,7 @@ Stage::Stage() : this->LogMessage("Stage::Stage\n", true); InitializeDefaultErrorMessages(); - SetErrorText(ERR_PORT_CHANGE_FORBIDDEN, g_Msg_PORT_CHANGE_FORBIDDEN); - SetErrorText(ERR_DRIVER_DISABLED, g_Msg_DRIVER_DISABLED); - SetErrorText(ERR_BUSY_TIMEOUT, g_Msg_BUSY_TIMEOUT); - SetErrorText(ERR_COMMAND_REJECTED, g_Msg_COMMAND_REJECTED); - SetErrorText(ERR_SETTING_FAILED, g_Msg_SETTING_FAILED); - SetErrorText(ERR_PERIPHERAL_DISCONNECTED, g_Msg_PERIPHERAL_DISCONNECTED); - SetErrorText(ERR_PERIPHERAL_UNSUPPORTED, g_Msg_PERIPHERAL_UNSUPPORTED); + ZaberBase::setErrorMessages([&](auto code, auto message) { this->SetErrorText(code, message); }); // Pre-initialization properties CreateProperty(MM::g_Keyword_Name, g_StageName, MM::String, true); @@ -62,7 +55,7 @@ Stage::Stage() : CreateProperty(MM::g_Keyword_Description, "Zaber stage driver adapter", MM::String, true); CPropertyAction* pAct = new CPropertyAction (this, &Stage::OnPort); - CreateProperty(MM::g_Keyword_Port, "COM1", MM::String, false, pAct, true); + CreateProperty("Zaber Serial Port", port_.c_str(), MM::String, false, pAct, true); pAct = new CPropertyAction (this, &Stage::OnDeviceAddress); CreateIntegerProperty("Controller Device Number", deviceAddress_, false, pAct, true); @@ -103,33 +96,20 @@ int Stage::Initialize() if (initialized_) return DEVICE_OK; core_ = GetCoreCallback(); - - this->LogMessage("Stage::Initialize\n", true); - int ret = ClearPort(); - if (ret != DEVICE_OK) - { - return ret; - } + this->LogMessage("Stage::Initialize\n", true); // Activate any recently changed peripherals. - ret = ActivatePeripheralsIfNeeded(deviceAddress_); - if (ret != DEVICE_OK) + auto ret = ActivatePeripheralsIfNeeded(deviceAddress_); + if (ret != DEVICE_OK) { LogMessage("Peripheral activation check failed.\n", true); return ret; } - // Disable alert messages. - ret = SetSetting(deviceAddress_, 0, "comm.alert", 0); - if (ret != DEVICE_OK) - { - return ret; - } - // Calculate step size. ret = GetSetting(deviceAddress_, axisNumber_, "resolution", resolution_); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { return ret; } @@ -139,21 +119,21 @@ int Stage::Initialize() // Initialize Speed (in mm/s) pAct = new CPropertyAction (this, &Stage::OnSpeed); ret = CreateFloatProperty("Speed [mm/s]", 0.0, false, pAct); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { return ret; } - // Initialize Acceleration (in m/s²) + // Initialize Acceleration (in m/s²) pAct = new CPropertyAction (this, &Stage::OnAccel); ret = CreateFloatProperty("Acceleration [m/s^2]", 0.0, false, pAct); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { return ret; } ret = UpdateStatus(); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { return ret; } @@ -185,10 +165,10 @@ bool Stage::Busy() int Stage::GetPositionUm(double& pos) { this->LogMessage("Stage::GetPositionUm\n", true); - + long steps; int ret = GetSetting(deviceAddress_, axisNumber_, "pos", steps); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { return ret; } @@ -261,11 +241,11 @@ int Stage::Home() { ostringstream cmd; cmd << "lockstep " << lockstepGroup_ << " home"; - return SendAndPollUntilIdle(deviceAddress_, 0, cmd.str().c_str(), homingTimeoutMs_); + return SendAndPollUntilIdle(deviceAddress_, 0, cmd.str()); } else { - return SendAndPollUntilIdle(deviceAddress_, axisNumber_, "home", homingTimeoutMs_); + return SendAndPollUntilIdle(deviceAddress_, axisNumber_, "home"); } } @@ -382,7 +362,7 @@ int Stage::OnSpeed (MM::PropertyBase* pProp, MM::ActionType eAct) { long speedData; int ret = GetSetting(deviceAddress_, axisNumber_, "maxspeed", speedData); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { return ret; } @@ -401,7 +381,7 @@ int Stage::OnSpeed (MM::PropertyBase* pProp, MM::ActionType eAct) if (speedData == 0 && speed != 0) speedData = 1; // Avoid clipping to 0. int ret = SetSetting(deviceAddress_, axisNumber_, "maxspeed", speedData); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { return ret; } @@ -417,12 +397,12 @@ int Stage::OnAccel (MM::PropertyBase* pProp, MM::ActionType eAct) { long accelData; int ret = GetSetting(deviceAddress_, axisNumber_, "accel", accelData); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { return ret; } - // convert to m/s² + // convert to m/s² double accel = (accelData*10/convFactor_)*stepSizeUm_/1000; pProp->Set(accel); } @@ -436,7 +416,7 @@ int Stage::OnAccel (MM::PropertyBase* pProp, MM::ActionType eAct) if (accelData == 0 && accel != 0) accelData = 1; // Only set accel to 0 if user intended it. int ret = SetSetting(deviceAddress_, axisNumber_, "accel", accelData); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { return ret; } diff --git a/DeviceAdapters/Zaber/Stage.h b/DeviceAdapters/Zaber/Stage.h index 7d4ca6a3f..ca9b7f06c 100644 --- a/DeviceAdapters/Zaber/Stage.h +++ b/DeviceAdapters/Zaber/Stage.h @@ -74,7 +74,6 @@ class Stage : public CStageBase, public ZaberBase long deviceAddress_; long axisNumber_; long lockstepGroup_; - int homingTimeoutMs_; double stepSizeUm_; double convFactor_; // not very informative name std::string cmdPrefix_; diff --git a/DeviceAdapters/Zaber/XYStage.cpp b/DeviceAdapters/Zaber/XYStage.cpp index c62979ed7..e294da976 100644 --- a/DeviceAdapters/Zaber/XYStage.cpp +++ b/DeviceAdapters/Zaber/XYStage.cpp @@ -4,9 +4,9 @@ // SUBSYSTEM: DeviceAdapters //----------------------------------------------------------------------------- // DESCRIPTION: XYStage Device Adapter -// +// // AUTHOR: Soleil Lapierre, David Goosen & Athabasca Witschi (contact@zaber.com) -// +// // COPYRIGHT: Zaber Technologies Inc., 2017 // // LICENSE: This file is distributed under the BSD license. @@ -62,28 +62,19 @@ XYStage::XYStage() : this->LogMessage("XYStage::XYStage\n", true); InitializeDefaultErrorMessages(); - SetErrorText(ERR_PORT_CHANGE_FORBIDDEN, g_Msg_PORT_CHANGE_FORBIDDEN); - SetErrorText(ERR_DRIVER_DISABLED, g_Msg_DRIVER_DISABLED); - SetErrorText(ERR_BUSY_TIMEOUT, g_Msg_BUSY_TIMEOUT); - SetErrorText(ERR_AXIS_COUNT, g_Msg_AXIS_COUNT); - SetErrorText(ERR_COMMAND_REJECTED, g_Msg_COMMAND_REJECTED); - SetErrorText(ERR_NO_REFERENCE_POS, g_Msg_NO_REFERENCE_POS); - SetErrorText(ERR_SETTING_FAILED, g_Msg_SETTING_FAILED); - SetErrorText(ERR_INVALID_DEVICE_NUM, g_Msg_INVALID_DEVICE_NUM); - SetErrorText(ERR_PERIPHERAL_DISCONNECTED, g_Msg_PERIPHERAL_DISCONNECTED); - SetErrorText(ERR_PERIPHERAL_UNSUPPORTED, g_Msg_PERIPHERAL_UNSUPPORTED); + ZaberBase::setErrorMessages([&](auto code, auto message) { this->SetErrorText(code, message); }); // Pre-initialization properties CreateProperty(MM::g_Keyword_Name, g_XYStageName, MM::String, true); CreateProperty(MM::g_Keyword_Description, "Zaber XY stage driver adapter", MM::String, true); CPropertyAction* pAct = new CPropertyAction (this, &XYStage::OnPort); - CreateProperty(MM::g_Keyword_Port, "Undefined", MM::String, false, pAct, true); + CreateProperty("Zaber Serial Port", port_.c_str(), MM::String, false, pAct, true); pAct = new CPropertyAction (this, &XYStage::OnDeviceAddress); CreateIntegerProperty("Controller Device Number", deviceAddressX_, false, pAct, true); SetPropertyLimits("Controller Device Number", 1, 99); - + pAct = new CPropertyAction (this, &XYStage::OnDeviceAddressY); CreateStringProperty("Controller Device Number (Y Axis)", "", false, pAct, true); @@ -133,47 +124,28 @@ XYStage::~XYStage() int XYStage::Initialize() { - if (initialized_) + if (initialized_) { return DEVICE_OK; } core_ = GetCoreCallback(); - - this->LogMessage("XYStage::Initialize\n", true); - int ret = ClearPort(); - if (ret != DEVICE_OK) - { - return ret; - } + this->LogMessage("XYStage::Initialize\n", true); // Activate any recently changed peripherals. - ret = ActivatePeripheralsIfNeeded(deviceAddressX_); - if (ret != DEVICE_OK) + auto ret = ActivatePeripheralsIfNeeded(deviceAddressX_); + if (ret != DEVICE_OK) { LogMessage("Peripheral activation check failed.\n", true); return ret; } - // Disable alert messages. - ret = SetSetting(deviceAddressX_, 0, "comm.alert", 0); - if (ret != DEVICE_OK) - { - return ret; - } - if (!IsSingleController()) { - ret = SetSetting(deviceAddressY_, 0, "comm.alert", 0); - if (ret != DEVICE_OK) - { - return ret; - } - // Activate any recently changed peripherals. ret = ActivatePeripheralsIfNeeded(deviceAddressY_); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { LogMessage("Peripheral activation check failed.\n", true); return ret; @@ -184,26 +156,26 @@ int XYStage::Initialize() // Ensure dual-axis controller. long axisCount; ret = GetSetting(deviceAddressX_, 0, "system.axiscount", axisCount); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { return ret; } if (axisCount < 2) { - return ERR_AXIS_COUNT; + return DEVICE_NOT_SUPPORTED; } } // Calculate step size. ret = GetSetting(deviceAddressX_, axisX_, "resolution", resolutionX_); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { return ret; } ret = GetSetting(deviceAddressY_, axisY_, "resolution", resolutionY_); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { return ret; } @@ -215,35 +187,35 @@ int XYStage::Initialize() // Initialize Speed (in mm/s) pAct = new CPropertyAction (this, &XYStage::OnSpeedX); ret = CreateFloatProperty("Speed X [mm/s]", 0.0, false, pAct); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { return ret; } pAct = new CPropertyAction (this, &XYStage::OnSpeedY); ret = CreateFloatProperty("Speed Y [mm/s]", 0.0, false, pAct); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { return ret; } - // Initialize Acceleration (in m/s²) + // Initialize Acceleration (in m/s²) pAct = new CPropertyAction (this, &XYStage::OnAccelX); ret = CreateFloatProperty("Acceleration X [m/s^2]", 0.0, false, pAct); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { return ret; } pAct = new CPropertyAction (this, &XYStage::OnAccelY); ret = CreateFloatProperty("Acceleration Y [m/s^2]", 0.0, false, pAct); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { return ret; } ret = UpdateStatus(); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { return ret; } @@ -285,7 +257,7 @@ int XYStage::GetPositionSteps(long& x, long& y) this->LogMessage("XYStage::GetPositionSteps\n", true); int ret = GetSetting(deviceAddressX_, axisX_, "pos", x); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { return ret; } @@ -302,7 +274,7 @@ int XYStage::SetPositionSteps(long x, long y) int XYStage::SetRelativePositionSteps(long x, long y) -{ +{ this->LogMessage("XYStage::SetRelativePositionSteps\n", true); return SendXYMoveCommand("rel", x, y); } @@ -345,7 +317,7 @@ int XYStage::Stop() /** Calibrates the stage then moves it to Micro-Manager's origin. - * + * * Assumes the stage is oriented with the long/X axis travelling left-right. * "Default" orientation is with cable connectors at the top right, * transposeMirrorX = 0, and transposeMirrorY = 0. @@ -367,14 +339,14 @@ int XYStage::Home() { ostringstream cmd; cmd << "lockstep " << lockstepGroupX_ << " home"; - ret = SendAndPollUntilIdle(deviceAddressX_, 0, cmd.str().c_str(), homingTimeoutMs_); + ret = SendAndPollUntilIdle(deviceAddressX_, 0, cmd.str()); } else { - ret = SendAndPollUntilIdle(deviceAddressX_, 0, "tools findrange", homingTimeoutMs_); - if (ret == ERR_COMMAND_REJECTED) // Some stages don't support the findrange command. Instead we have to home them before moving. + ret = SendAndPollUntilIdle(deviceAddressX_, 0, "tools findrange"); + if (ret == DEVICE_UNSUPPORTED_COMMAND) // Some stages don't support the findrange command. Instead we have to home them before moving. { - ret = SendAndPollUntilIdle(deviceAddressX_, 0, "home", homingTimeoutMs_); + ret = SendAndPollUntilIdle(deviceAddressX_, 0, "home"); } } @@ -389,14 +361,14 @@ int XYStage::Home() { ostringstream cmd; cmd << "lockstep " << lockstepGroupY_ << " home"; - ret = SendAndPollUntilIdle(deviceAddressY_, 0, cmd.str().c_str(), homingTimeoutMs_); + ret = SendAndPollUntilIdle(deviceAddressY_, 0, cmd.str()); } else { - ret = SendAndPollUntilIdle(deviceAddressY_, 0, "tools findrange", homingTimeoutMs_); - if (ret == ERR_COMMAND_REJECTED) // Some stages don't support the findrange command. Instead we have to home them before moving. + ret = SendAndPollUntilIdle(deviceAddressY_, 0, "tools findrange"); + if (ret == DEVICE_UNSUPPORTED_COMMAND) // Some stages don't support the findrange command. Instead we have to home them before moving. { - ret = SendAndPollUntilIdle(deviceAddressY_, 0, "home", homingTimeoutMs_); + ret = SendAndPollUntilIdle(deviceAddressY_, 0, "home"); } if (ret != DEVICE_OK) @@ -421,8 +393,8 @@ int XYStage::Home() } cmdX << (mirrorX ? "move max" : "move min"); - ret = SendAndPollUntilIdle(deviceAddressX_, xAxis, cmdX.str().c_str(), homingTimeoutMs_); - if (ret != DEVICE_OK) + ret = SendAndPollUntilIdle(deviceAddressX_, xAxis, cmdX.str().c_str()); + if (ret != DEVICE_OK) { return ret; } @@ -434,13 +406,13 @@ int XYStage::Home() cmdY << "lockstep " << lockstepGroupY_ << " "; yAxis = 0; } - + cmdY << (mirrorY ? "move max" : "move min"); - ret = SendAndPollUntilIdle(deviceAddressY_, yAxis, cmdY.str().c_str(), homingTimeoutMs_); - if (ret != DEVICE_OK) + ret = SendAndPollUntilIdle(deviceAddressY_, yAxis, cmdY.str().c_str()); + if (ret != DEVICE_OK) { return ret; - } + } this->LogMessage("XYStage::Home COMPLETE!\n", true); return DEVICE_OK; @@ -452,8 +424,8 @@ int XYStage::SetOrigin() * The Zaber coordinate system is NOT zeroed. */ { - this->LogMessage("XYStage::SetOrigin\n", true); - this->LogMessage("XYStage::SetOrigin calling SetAdapterOriginUm(0,0)\n", true); + this->LogMessage("XYStage::SetOrigin\n", true); + this->LogMessage("XYStage::SetOrigin calling SetAdapterOriginUm(0,0)\n", true); return SetAdapterOriginUm(0,0); } @@ -466,9 +438,9 @@ int XYStage::GetStepLimits(long& xMin, long& xMax, long& yMin, long& yMax) { return ERR_NO_REFERENCE_POS; } - + int ret = GetLimits(deviceAddressX_, axisX_, xMin, xMax); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { return ret; } @@ -479,13 +451,13 @@ int XYStage::GetStepLimits(long& xMin, long& xMax, long& yMin, long& yMax) int XYStage::GetLimitsUm(double& xMin, double& xMax, double& yMin, double& yMax) { - this->LogMessage("XYStage::GetLimitsUm\n", true); + this->LogMessage("XYStage::GetLimitsUm\n", true); if (!rangeMeasured_) return ERR_NO_REFERENCE_POS; long xMinSteps, xMaxSteps, yMinSteps, yMaxSteps; int ret = GetStepLimits(xMinSteps, xMaxSteps, yMinSteps, yMaxSteps); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { return ret; } @@ -504,14 +476,14 @@ int XYStage::GetLimitsUm(double& xMin, double& xMax, double& yMin, double& yMax) // Private helper functions /////////////////////////////////////////////////////////////////////////////// -int XYStage::SendXYMoveCommand(string type, long x, long y) const +int XYStage::SendXYMoveCommand(string type, long x, long y) { this->LogMessage("XYStage::SendXYMoveCommand\n", true); bool lockstepX = (lockstepGroupX_ > 0); long xAxis = lockstepX ? lockstepGroupX_ : axisX_; int ret = SendMoveCommand(deviceAddressX_, xAxis, type, x, lockstepX); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { return ret; } @@ -522,7 +494,7 @@ int XYStage::SendXYMoveCommand(string type, long x, long y) const } -int XYStage::OnSpeed(long address, long axis, MM::PropertyBase* pProp, MM::ActionType eAct) const +int XYStage::OnSpeed(long address, long axis, MM::PropertyBase* pProp, MM::ActionType eAct) { this->LogMessage("XYStage::OnSpeed\n", true); @@ -532,7 +504,7 @@ int XYStage::OnSpeed(long address, long axis, MM::PropertyBase* pProp, MM::Actio { long speedData; int ret = GetSetting(address, axis, "maxspeed", speedData); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { return ret; } @@ -551,7 +523,7 @@ int XYStage::OnSpeed(long address, long axis, MM::PropertyBase* pProp, MM::Actio if (speedData == 0 && speed != 0) speedData = 1; // Avoid clipping to 0. int ret = SetSetting(address, axis, "maxspeed", speedData); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { return ret; } @@ -561,7 +533,7 @@ int XYStage::OnSpeed(long address, long axis, MM::PropertyBase* pProp, MM::Actio } -int XYStage::OnAccel(long address, long axis, MM::PropertyBase* pProp, MM::ActionType eAct) const +int XYStage::OnAccel(long address, long axis, MM::PropertyBase* pProp, MM::ActionType eAct) { this->LogMessage("XYStage::OnAccel\n", true); @@ -571,12 +543,12 @@ int XYStage::OnAccel(long address, long axis, MM::PropertyBase* pProp, MM::Actio { long accelData; int ret = GetSetting(address, axis, "accel", accelData); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { return ret; } - // convert to m/s² + // convert to m/s² double accel = (accelData*10/convFactor_)*stepSize/1000; pProp->Set(accel); } @@ -590,7 +562,7 @@ int XYStage::OnAccel(long address, long axis, MM::PropertyBase* pProp, MM::Actio if (accelData == 0 && accel != 0) accelData = 1; // Only set accel to 0 if user intended it. int ret = SetSetting(address, axis, "accel", accelData); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { return ret; } @@ -600,7 +572,7 @@ int XYStage::OnAccel(long address, long axis, MM::PropertyBase* pProp, MM::Actio } -void XYStage::GetOrientation(bool& mirrorX, bool& mirrorY) +void XYStage::GetOrientation(bool& mirrorX, bool& mirrorY) { // copied from DeviceBase.h this->LogMessage("XYStage::GetOrientation\n", true); @@ -622,7 +594,7 @@ void XYStage::GetOrientation(bool& mirrorX, bool& mirrorY) /////////////////////////////////////////////////////////////////////////////// int XYStage::OnPort(MM::PropertyBase* pProp, MM::ActionType eAct) -{ +{ ostringstream os; os << "XYStage::OnPort(" << pProp << ", " << eAct << ")\n"; this->LogMessage(os.str().c_str(), false); @@ -648,7 +620,7 @@ int XYStage::OnPort(MM::PropertyBase* pProp, MM::ActionType eAct) int XYStage::OnSpeedX(MM::PropertyBase* pProp, MM::ActionType eAct) -{ +{ this->LogMessage("XYStage::OnSpeedX\n", true); return OnSpeed(deviceAddressX_, axisX_, pProp, eAct); @@ -858,7 +830,7 @@ int XYStage::OnDeviceAddressY(MM::PropertyBase* pProp, MM::ActionType eAct) long val = atol(numBuf.c_str()); if ((val < 1) || (val > 99)) { - return ERR_INVALID_DEVICE_NUM; + return DEVICE_INVALID_PROPERTY_VALUE; } deviceAddressY_ = val; diff --git a/DeviceAdapters/Zaber/XYStage.h b/DeviceAdapters/Zaber/XYStage.h index 1e705593e..b4048b958 100644 --- a/DeviceAdapters/Zaber/XYStage.h +++ b/DeviceAdapters/Zaber/XYStage.h @@ -77,9 +77,9 @@ class XYStage : public CXYStageBase, public ZaberBase int OnDeviceAddressY (MM::PropertyBase* pProp, MM::ActionType eAct); // Composite XY (two controllers) private: - int SendXYMoveCommand(std::string type, long x, long y) const; - int OnSpeed(long address, long axis, MM::PropertyBase* pProp, MM::ActionType eAct) const; - int OnAccel(long address, long axis, MM::PropertyBase* pProp, MM::ActionType eAct) const; + int SendXYMoveCommand(std::string type, long x, long y); + int OnSpeed(long address, long axis, MM::PropertyBase* pProp, MM::ActionType eAct); + int OnAccel(long address, long axis, MM::PropertyBase* pProp, MM::ActionType eAct); void GetOrientation(bool& mirrorX, bool& mirrorY); inline bool IsSingleController() const diff --git a/DeviceAdapters/Zaber/Zaber.cpp b/DeviceAdapters/Zaber/Zaber.cpp index 01652ceab..a97ba4c49 100644 --- a/DeviceAdapters/Zaber/Zaber.cpp +++ b/DeviceAdapters/Zaber/Zaber.cpp @@ -4,9 +4,9 @@ // SUBSYSTEM: DeviceAdapters //----------------------------------------------------------------------------- // DESCRIPTION: Zaber Controller Driver -// -// AUTHOR: David Goosen & Athabasca Witschi (contact@zaber.com) -// +// +// AUTHOR: David Goosen, Athabasca Witschi, Martin Zak (contact@zaber.com) +// // COPYRIGHT: Zaber Technologies Inc., 2014 // // LICENSE: This file is distributed under the BSD license. @@ -32,6 +32,7 @@ #include "FilterWheel.h" #include "FilterCubeTurret.h" #include "Illuminator.h" +#include "ObjectiveChanger.h" #include @@ -39,16 +40,15 @@ using namespace std; const char* g_Msg_PORT_CHANGE_FORBIDDEN = "The port cannot be changed once the device is initialized."; const char* g_Msg_DRIVER_DISABLED = "The driver has disabled itself due to overheating."; -const char* g_Msg_BUSY_TIMEOUT = "Timed out while waiting for device to finish executing a command."; -const char* g_Msg_AXIS_COUNT = "Dual-axis controller required."; +const char* g_Msg_MOVEMENT_FAILED = "The movement has failed."; const char* g_Msg_COMMAND_REJECTED = "The device rejected the command."; const char* g_Msg_NO_REFERENCE_POS = "The device has not had a reference position established."; const char* g_Msg_SETTING_FAILED = "The property could not be set. Is the value in the valid range?"; -const char* g_Msg_INVALID_DEVICE_NUM = "Device numbers must be in the range of 1 to 99."; -const char* g_Msg_LAMP_DISCONNECTED= "Some of the illuminator lamps are disconnected."; +const char* g_Msg_LAMP_DISCONNECTED = "Some of the illuminator lamps are disconnected."; const char* g_Msg_LAMP_OVERHEATED = "Some of the illuminator lamps are overheated."; const char* g_Msg_PERIPHERAL_DISCONNECTED = "A peripheral has been disconnected; please reconnect it or set its peripheral ID to zero and then restart the driver."; const char* g_Msg_PERIPHERAL_UNSUPPORTED = "Controller firmware does not support one of the connected peripherals; please update the firmware."; +const char* g_Msg_FIRMWARE_UNSUPPORTED = "Firmware is not suported; please update the firmware."; ////////////////////////////////////////////////////////////////////////////////// @@ -61,33 +61,38 @@ MODULE_API void InitializeModuleData() RegisterDevice(g_FilterWheelName, MM::StateDevice, g_FilterWheelDescription); RegisterDevice(g_FilterTurretName, MM::StateDevice, g_FilterTurretDescription); RegisterDevice(g_IlluminatorName, MM::ShutterDevice, g_IlluminatorDescription); -} + RegisterDevice(g_ObjectiveChangerName, MM::StateDevice, g_ObjectiveChangerDescription); +} -MODULE_API MM::Device* CreateDevice(const char* deviceName) +MODULE_API MM::Device* CreateDevice(const char* deviceName) { if (strcmp(deviceName, g_XYStageName) == 0) { return new XYStage(); } else if (strcmp(deviceName, g_StageName) == 0) - { + { return new Stage(); } else if (strcmp(deviceName, g_FilterWheelName) == 0) - { + { return new FilterWheel(); } else if (strcmp(deviceName, g_FilterTurretName) == 0) - { + { return new FilterCubeTurret(); } else if (strcmp(deviceName, g_IlluminatorName) == 0) - { + { return new Illuminator(); } + else if (strcmp(deviceName, g_ObjectiveChangerName) == 0) + { + return new ObjectiveChanger(); + } else - { + { return 0; } } @@ -103,296 +108,170 @@ MODULE_API void DeleteDevice(MM::Device* pDevice) // ZaberBase (convenience parent class) /////////////////////////////////////////////////////////////////////////////// +ConnectionManager ZaberBase::connections; + ZaberBase::ZaberBase(MM::Device *device) : initialized_(false), port_("Undefined"), device_(device), - core_(0), - cmdPrefix_("/") + core_(0) { } ZaberBase::~ZaberBase() { + resetConnection(); } -// COMMUNICATION "clear buffer" utility function: -int ZaberBase::ClearPort() const +int ZaberBase::Command(long device, long axis, const string command, zml::Response& reply) { - core_->LogMessage(device_, "ZaberBase::ClearPort\n", true); + core_->LogMessage(device_, "ZaberBase::Command\n", true); - const int bufSize = 255; - unsigned char clear[bufSize]; - unsigned long read = bufSize; - int ret; - - while ((int) read == bufSize) - { - ret = core_->ReadFromSerial(device_, port_.c_str(), clear, bufSize, read); - if (ret != DEVICE_OK) - { - return ret; - } - } - - return DEVICE_OK; + return handleException([&]() { + ensureConnected(); + reply = connection_->genericCommand(command, static_cast(device), static_cast(axis)); + }); } -// COMMUNICATION "send" utility function: -int ZaberBase::SendCommand(const string command) const -{ - core_->LogMessage(device_, "ZaberBase::SendCommand\n", true); - - const char* msgFooter = "\n"; // required by Zaber ASCII protocol - string baseCommand = ""; - baseCommand += command; - return core_->SetSerialCommand(device_, port_.c_str(), baseCommand.c_str(), msgFooter); -} - - -int ZaberBase::SendCommand(long device, long axis, const string command) const -{ - core_->LogMessage(device_, "ZaberBase::SendCommand(device,axis)\n", true); - - ostringstream cmd; - cmd << cmdPrefix_ << device << " " << axis << " " << command; - vector resp; - return QueryCommand(cmd.str().c_str(), resp); -} - - -// COMMUNICATION "send & receive" utility function: -int ZaberBase::QueryCommand(const string command, vector& reply) const -{ - core_->LogMessage(device_, "ZaberBase::QueryCommand\n", true); - - int ret = QueryCommandUnchecked(command, reply); - if (ret != DEVICE_OK) - { - return ret; - } - - ret = CheckReplyFlags(reply[4]); - if (ret != DEVICE_OK) - { - return ret; - } - - if (reply[2] != "OK") - { - return ERR_COMMAND_REJECTED; +void ZaberBase::ensureConnected() { + if (!connection_) { + core_->LogMessage(device_, "ZaberBase::ensureConnected\n", true); + connection_ = ZaberBase::connections.getConnection(port_); + connection_->enableAlerts(); + onNewConnection(); } - - return DEVICE_OK; -} - - -// Send a command and return the next reply string without any parsing or warning -// flag checking. -int ZaberBase::QueryCommandUnchecked(const string command, std::vector& reply) const -{ - core_->LogMessage(device_, "ZaberBase::QueryCommandUnchecked\n", true); - - const char* msgFooter = "\r\n"; // required by Zaber ASCII protocol - - const size_t BUFSIZE = 2048; - char buf[BUFSIZE] = {'\0'}; - - int ret = SendCommand(command); - if (ret != DEVICE_OK) - { - return ret; - } - - ret = core_->GetSerialAnswer(device_, port_.c_str(), BUFSIZE, buf, msgFooter); - if (ret != DEVICE_OK) - { - return ret; - } - - string resp = buf; - if (resp.length() < 1) - { - return DEVICE_SERIAL_INVALID_RESPONSE; - } - - // remove checksum before parsing - int thirdLast = int(resp.length() - 3); - if (resp[thirdLast] == ':') - { - resp.erase(thirdLast, string::npos); - } - - CDeviceUtils::Tokenize(resp, reply, " "); - /* reply[0] = message type and device address, reply[1] = axis number, - * reply[2] = reply flags, reply[3] = device status, reply[4] = warning flags, - * reply[5] (and possibly reply[6]) = response data, if there is data - */ - if (reply.size() < 5) - { - return DEVICE_SERIAL_INVALID_RESPONSE; - } - - return DEVICE_OK; } - -int ZaberBase::QueryCommandUnchecked(long device, long axis, const std::string command, std::vector& reply) const -{ - ostringstream cmd; - cmd << cmdPrefix_ << device << " " << axis << " " << command; - return QueryCommandUnchecked(cmd.str().c_str(), reply); +void ZaberBase::onNewConnection() { } - - -int ZaberBase::CheckReplyFlags(const std::string reply) const -{ - if (reply == "FN") +void ZaberBase::resetConnection() { + try { - return ERR_PERIPHERAL_UNSUPPORTED; + // the connection destructor can throw in the rarest occasions + connection_ = nullptr; } - else if (reply == "FZ") + catch (const zmlbase::MotionLibException e) { - return ERR_PERIPHERAL_DISCONNECTED; - } - else if (reply[0] == 'F') - { - return ERR_DRIVER_DISABLED; } +} - return DEVICE_OK; +int ZaberBase::Command(long device, long axis, const std::string command) { + zml::Response reply; + return Command(device, axis, command, reply); } -int ZaberBase::GetSetting(long device, long axis, string setting, long& data) const +template int ZaberBase::GetSetting(long device, long axis, string setting, TReturn& data) { - core_->LogMessage(device_, "ZaberBase::GetSetting(long)\n", true); - - ostringstream cmd; - cmd << cmdPrefix_ << device << " " << axis << " get " << setting; - vector resp; + core_->LogMessage(device_, "ZaberBase::GetSetting()\n", true); - int ret = QueryCommand(cmd.str().c_str(), resp); - if (ret != DEVICE_OK) + zml::Response resp; + int ret = Command(device, axis, "get " + setting, resp); + if (ret != DEVICE_OK) { return ret; } - // extract data - string dataString = resp[5]; - stringstream(dataString) >> data; + stringstream(resp.getData()) >> data; return DEVICE_OK; } - -int ZaberBase::GetSetting(long device, long axis, string setting, double& data) const +int ZaberBase::GetSettings(long device, long axis, std::string setting, std::vector& data) { - core_->LogMessage(device_, "ZaberBase::GetSetting(double)\n", true); + core_->LogMessage(device_, "ZaberBase::GetSettings()\n", true); - ostringstream cmd; - cmd << cmdPrefix_ << device << " " << axis << " get " << setting; - vector resp; - - int ret = QueryCommand(cmd.str().c_str(), resp); - if (ret != DEVICE_OK) + data.clear(); + zml::Response resp; + int ret = Command(device, axis, "get " + setting, resp); + if (ret != DEVICE_OK) { + if (ret == DEVICE_UNSUPPORTED_COMMAND) { + return DEVICE_OK; + } return ret; } - // extract data - string dataString = resp[5]; - stringstream(dataString) >> data; - return DEVICE_OK; -} - - -int ZaberBase::SetSetting(long device, long axis, string setting, long data) const -{ - core_->LogMessage(device_, "ZaberBase::SetSetting(long)\n", true); - - ostringstream cmd; - cmd << cmdPrefix_ << device << " " << axis << " set " << setting << " " << data; - vector resp; + std::vector strData; + CDeviceUtils::Tokenize(resp.getData(), strData, " "); + for (const auto& token : strData) { + if (token == "NA") { + data.push_back(NAN); + continue; + } - int ret = QueryCommand(cmd.str().c_str(), resp); - if (ret != DEVICE_OK) - { - return ERR_SETTING_FAILED; + double numData; + stringstream(token) >> numData; + data.push_back(numData); } return DEVICE_OK; } - -int ZaberBase::SetSetting(long device, long axis, string setting, double data, int decimalPlaces) const +int ZaberBase::SetSetting(long device, long axis, string setting, double data, int decimalPlaces) { core_->LogMessage(device_, "ZaberBase::SetSetting(double)\n", true); - ostringstream cmd; - cmd.precision(decimalPlaces); - cmd << cmdPrefix_ << device << " " << axis << " set " << setting << " " << fixed << data; - vector resp; + ostringstream cmd; + cmd.precision(decimalPlaces > 0 ? decimalPlaces : 0); + cmd << "set " << setting << " " << fixed << data; + zml::Response resp; - int ret = QueryCommand(cmd.str().c_str(), resp); - if (ret != DEVICE_OK) + int ret = Command(device, axis, cmd.str(), resp); + if (ret == ERR_COMMAND_REJECTED) { return ERR_SETTING_FAILED; } - - return DEVICE_OK; + return ret; } -bool ZaberBase::IsBusy(long device) const +bool ZaberBase::IsBusy(long device) { core_->LogMessage(device_, "ZaberBase::IsBusy\n", true); - ostringstream cmd; - cmd << cmdPrefix_ << device; - vector resp; + zml::Response resp; - int ret = QueryCommand(cmd.str().c_str(), resp); + int ret = Command(device, 0, "", resp); if (ret != DEVICE_OK) { ostringstream os; - os << "SendSerialCommand failed in ZaberBase::IsBusy, error code: " << ret; + os << "ZaberBase::IsBusy failed, error code: " << ret; core_->LogMessage(device_, os.str().c_str(), false); return false; } - return (resp[3] == ("BUSY")); + return resp.getStatus() != "IDLE"; } -int ZaberBase::Stop(long device, long lockstepGroup) const +int ZaberBase::Stop(long device, long lockstepGroup) { core_->LogMessage(device_, "ZaberBase::Stop\n", true); ostringstream cmd; if (lockstepGroup > 0) { - cmd << cmdPrefix_ << device << " lockstep " << lockstepGroup << " stop"; + cmd << "lockstep " << lockstepGroup << " stop"; } else { - cmd << cmdPrefix_ << device << " stop"; + cmd << "stop"; } - vector resp; - return QueryCommand(cmd.str().c_str(), resp); + return Command(device, 0, cmd.str()); } -int ZaberBase::GetLimits(long device, long axis, long& min, long& max) const +int ZaberBase::GetLimits(long device, long axis, long& min, long& max) { core_->LogMessage(device_, "ZaberBase::GetLimits\n", true); int ret = GetSetting(device, axis, "limit.min", min); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { return ret; } @@ -401,67 +280,52 @@ int ZaberBase::GetLimits(long device, long axis, long& min, long& max) const } -int ZaberBase::SendMoveCommand(long device, long axis, std::string type, long data, bool lockstep) const +int ZaberBase::SendMoveCommand(long device, long axis, std::string type, long data, bool lockstep) { core_->LogMessage(device_, "ZaberBase::SendMoveCommand\n", true); ostringstream cmd; if (lockstep) { - cmd << cmdPrefix_ << device << " lockstep " << axis << " move " << type << " " << data; + cmd << "lockstep " << axis << " move " << type << " " << data; + axis = 0; } else { - cmd << cmdPrefix_ << device << " " << axis << " move " << type << " " << data; + cmd << "move " << type << " " << data; } - vector resp; - return QueryCommand(cmd.str().c_str(), resp); + return Command(device, axis, cmd.str()); } -int ZaberBase::SendAndPollUntilIdle(long device, long axis, string command, int timeoutMs) const +int ZaberBase::SendAndPollUntilIdle(long device, long axis, string command) { core_->LogMessage(device_, "ZaberBase::SendAndPollUntilIdle\n", true); - - ostringstream cmd; - cmd << cmdPrefix_ << device << " " << axis << " " << command; - vector resp; - - int ret = QueryCommand(cmd.str().c_str(), resp); - if (ret != DEVICE_OK) - { - return ret; - } - - int numTries = 0, pollIntervalMs = 100; - do + return handleException([&]() { - numTries++; - CDeviceUtils::SleepMs(pollIntervalMs); - } - while (IsBusy(device) && (numTries*pollIntervalMs < timeoutMs)); - - if (numTries*pollIntervalMs >= timeoutMs) - { - return ERR_BUSY_TIMEOUT; - } - - ostringstream os; - os << "Completed after " << (numTries*pollIntervalMs/1000.0) << " seconds."; - core_->LogMessage(device_, os.str().c_str(), true); - return DEVICE_OK; + ensureConnected(); + connection_->genericCommand(command, static_cast(device), static_cast(axis)); + auto zmlDevice = connection_->getDevice(device); + if (axis == 0) { + zmlDevice.getAllAxes().waitUntilIdle(); + } + else + { + zmlDevice.getAxis(axis).waitUntilIdle(); + } + }); } -int ZaberBase::GetRotaryIndexedDeviceInfo(long device, long axis, long& numIndices, long& currentIndex) const +int ZaberBase::GetRotaryIndexedDeviceInfo(long device, long axis, long& numIndices, long& currentIndex) { core_->LogMessage(device_, "ZaberBase::GetRotaryIndexedDeviceInfo\n", true); // Get the size of a full circle in microsteps. long cycleSize = -1; int ret = GetSetting(device, axis, "limit.cycle.dist", cycleSize); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { core_->LogMessage(device_, "Attempt to detect rotary cycle distance failed.\n", true); return ret; @@ -477,7 +341,7 @@ int ZaberBase::GetRotaryIndexedDeviceInfo(long device, long axis, long& numIndic // Get the size of a filter increment in microsteps. long indexSize = -1; ret = GetSetting(device, axis, "motion.index.dist", indexSize); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { core_->LogMessage(device_, "Attempt to detect index spacing failed.\n", true); return ret; @@ -493,7 +357,7 @@ int ZaberBase::GetRotaryIndexedDeviceInfo(long device, long axis, long& numIndic long index = -1; ret = GetSetting(device, axis, "motion.index.num", index); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { core_->LogMessage(device_, "Attempt to detect current index position failed.\n", true); return ret; @@ -516,108 +380,97 @@ int ZaberBase::GetRotaryIndexedDeviceInfo(long device, long axis, long& numIndic // return error codes relating to the flags. If the command is rejected // or the version can otherwise not be determined, the version will // be 0.00. The build number is not included. -int ZaberBase::GetFirmwareVersion(long device, double& version) const +int ZaberBase::GetFirmwareVersion(long device, double& version) { core_->LogMessage(device_, "ZaberBase::GetFirmwareVersion\n", true); + return GetSetting(device, 0, "version", version); +} - version = 0.0; - vector reply; - ostringstream cmd; - cmd << cmdPrefix_ << device << " 0 get version"; - - int ret = QueryCommandUnchecked(cmd.str().c_str(), reply); - if ((reply.size() > 5) && (reply[2] == "OK")) - { - double data; - stringstream sstream; - sstream << reply[5]; - sstream >> data; - if (!sstream.bad() && !sstream.fail()) - { - version = data; - } - } +int ZaberBase::ActivatePeripheralsIfNeeded(long device) +{ + core_->LogMessage(device_, "ZaberBase::ActivatePeripheralsIfNeeded\n", true); + zml::Response reply; + int ret = Command(device, 0, "warnings", reply); if (ret != DEVICE_OK) { + core_->LogMessage(device_, "Could not get device warning flags.\n", true); return ret; } - ret = CheckReplyFlags(reply[4]); - if (ret != DEVICE_OK) - { - return ret; + if (reply.getData().find("FZ") == string::npos) { + return DEVICE_OK; } - - if (reply[2] != "OK") + + core_->LogMessage(device_, "An axis needs activation.\n", false); + ret = Command(device, 0, "activate"); + if (ret != DEVICE_OK) { - return ERR_COMMAND_REJECTED; + core_->LogMessage(device_, "Activating a peripheral failed.\n", false); } return DEVICE_OK; } - -int ZaberBase::ActivatePeripheralsIfNeeded(long device) const -{ - core_->LogMessage(device_, "ZaberBase::ActivatePeripheralsIfNeeded\n", true); - - vector reply; - - int ret = QueryCommandUnchecked(device, 0, "warnings", reply); - if (ret != DEVICE_OK) +int ZaberBase::handleException(std::function wrapped) { + try { - core_->LogMessage(device_, "Could not get device warning flags.\n", true); - return ret; + wrapped(); + return DEVICE_OK; } - - if (std::find(reply.begin(), reply.end(), "FZ") != reply.end()) - { - core_->LogMessage(device_, "An axis needs activation.\n", false); - - // If the activate command failed, it must mean that some axes were - // previous connected but now are unplugged or have different peripherals - // plugged in. Find them and reset their peripheral IDs. - reply.clear(); - ret = QueryCommandUnchecked(device, 0, "get system.axiscount", reply); - if (ret != DEVICE_OK) - { - core_->LogMessage(device_, "Could not get axis count.\n", true); - return ret; + catch (const zmlbase::ConnectionFailedException e) { + core_->LogMessage(device_, e.what(), true); + resetConnection(); + return DEVICE_NOT_CONNECTED; + } + catch (const zmlbase::ConnectionClosedException e) { + core_->LogMessage(device_, e.what(), true); + resetConnection(); + return DEVICE_NOT_CONNECTED; + } + catch (const zmlbase::CommandFailedException e) { + core_->LogMessage(device_, e.what(), true); + auto reason = e.getDetails().getResponseData(); + if (reason == "BADCOMMAND") { + return DEVICE_UNSUPPORTED_COMMAND; } - - int axisCount = 0; - string dataString = reply[5]; - stringstream(dataString) >> axisCount; - - for (int axis = 1; axis <= axisCount; axis++) - { - ostringstream cmd; - cmd << cmdPrefix_ << device << " " << axis << " activate"; - reply.clear(); - ret = QueryCommand(cmd.str().c_str(), reply); - if (ret != DEVICE_OK) - { - core_->LogMessage(device_, "Activating a peripheral failed; resetting peripheral ID.\n", false); - reply.clear(); - ret = QueryCommandUnchecked(device, axis, "get peripheral.id.pending", reply); - if ((reply[2] == "OK") && (reply[4] == "FZ")) - { - int id; - dataString = reply[5]; - stringstream(dataString) >> id; - ret = SetSetting(device, axis, "peripheral.id", id); - if (ret != DEVICE_OK) - { - core_->LogMessage(device_, "Failed to reset the peripheral ID of a dis/re-connected axis. Please ensure all axes are either connected or have their peripheral IDs set to zero.\n", false); - return ret; - } - } - } + else if (reason == "DRIVERDISABLED") { + return ERR_DRIVER_DISABLED; } + else if (reason == "INACTIVE") { + return ERR_PERIPHERAL_DISCONNECTED; + } + else if (reason == "NOTSUPPORTED") { + return ERR_PERIPHERAL_UNSUPPORTED; + } + return ERR_COMMAND_REJECTED; + } + catch (const zmlbase::RequestTimeoutException e) { + core_->LogMessage(device_, e.what(), true); + return DEVICE_NOT_CONNECTED; + } + catch (const zmlbase::MovementFailedException e) { + core_->LogMessage(device_, e.what(), true); + return ERR_MOVEMENT_FAILED; + } + catch (const zmlbase::MotionLibException e) { + core_->LogMessage(device_, e.what(), true); + return DEVICE_ERR; } - - return DEVICE_OK; } + +void ZaberBase::setErrorMessages(std::function setter) { + setter(ERR_PORT_CHANGE_FORBIDDEN, g_Msg_PORT_CHANGE_FORBIDDEN); + setter(ERR_DRIVER_DISABLED, g_Msg_DRIVER_DISABLED); + setter(ERR_COMMAND_REJECTED, g_Msg_COMMAND_REJECTED); + setter(ERR_MOVEMENT_FAILED, g_Msg_MOVEMENT_FAILED); + setter(ERR_NO_REFERENCE_POS, g_Msg_NO_REFERENCE_POS); + setter(ERR_SETTING_FAILED, g_Msg_SETTING_FAILED); + setter(ERR_PERIPHERAL_DISCONNECTED, g_Msg_PERIPHERAL_DISCONNECTED); + setter(ERR_PERIPHERAL_UNSUPPORTED, g_Msg_PERIPHERAL_UNSUPPORTED); + setter(ERR_LAMP_DISCONNECTED, g_Msg_LAMP_DISCONNECTED); + setter(ERR_LAMP_OVERHEATED, g_Msg_LAMP_OVERHEATED); + setter(ERR_FIRMWARE_UNSUPPORTED, g_Msg_FIRMWARE_UNSUPPORTED); +} diff --git a/DeviceAdapters/Zaber/Zaber.h b/DeviceAdapters/Zaber/Zaber.h index 64bdc2743..1fba41aa8 100644 --- a/DeviceAdapters/Zaber/Zaber.h +++ b/DeviceAdapters/Zaber/Zaber.h @@ -4,9 +4,9 @@ // SUBSYSTEM: DeviceAdapters //----------------------------------------------------------------------------- // DESCRIPTION: Zaber Controller Driver -// -// AUTHOR: David Goosen & Athabasca Witschi (contact@zaber.com) -// +// +// AUTHOR: David Goosen, Athabasca Witschi, Martin Zak (contact@zaber.com) +// // COPYRIGHT: Zaber Technologies Inc., 2014 // // LICENSE: This file is distributed under the BSD license. @@ -28,6 +28,15 @@ #include #include #include +#undef VERSION +#include +#include + +#include "ConnectionManager.h" + +namespace zmlbase = zaber::motion; +namespace zml = zaber::motion::ascii; +namespace zmlmi = zaber::motion::microscopy; ////////////////////////////////////////////////////////////////////////////// // Various constants: error codes, error messages @@ -35,32 +44,16 @@ #define ERR_PORT_CHANGE_FORBIDDEN 10002 #define ERR_DRIVER_DISABLED 10004 -#define ERR_BUSY_TIMEOUT 10008 -#define ERR_AXIS_COUNT 10016 +#define ERR_MOVEMENT_FAILED 10016 #define ERR_COMMAND_REJECTED 10032 #define ERR_NO_REFERENCE_POS 10064 #define ERR_SETTING_FAILED 10128 -#define ERR_INVALID_DEVICE_NUM 10256 #define ERR_LAMP_DISCONNECTED 10512 #define ERR_LAMP_OVERHEATED 11024 #define ERR_PERIPHERAL_DISCONNECTED 12048 #define ERR_PERIPHERAL_UNSUPPORTED 14096 #define ERR_FIRMWARE_UNSUPPORTED 18192 -extern const char* g_Msg_PORT_CHANGE_FORBIDDEN; -extern const char* g_Msg_DRIVER_DISABLED; -extern const char* g_Msg_BUSY_TIMEOUT; -extern const char* g_Msg_AXIS_COUNT; -extern const char* g_Msg_COMMAND_REJECTED; -extern const char* g_Msg_NO_REFERENCE_POS; -extern const char* g_Msg_SETTING_FAILED; -extern const char* g_Msg_INVALID_DEVICE_NUM; -extern const char* g_Msg_LAMP_DISCONNECTED; -extern const char* g_Msg_LAMP_OVERHEATED; -extern const char* g_Msg_PERIPHERAL_DISCONNECTED; -extern const char* g_Msg_PERIPHERAL_UNSUPPORTED; -extern const char* g_Msg_FIRMWARE_UNSUPPORTED; - // N.B. Concrete device classes deriving ZaberBase must set core_ in // Initialize(). class ZaberBase @@ -69,32 +62,35 @@ class ZaberBase ZaberBase(MM::Device *device); virtual ~ZaberBase(); + static void setErrorMessages(std::function setter); + protected: - int ClearPort() const; - int SendCommand(const std::string command) const; - int SendCommand(long device, long axis, const std::string command) const; - int QueryCommand(const std::string command, std::vector& reply) const; - int QueryCommandUnchecked(const std::string command, std::vector& reply) const; - int QueryCommandUnchecked(long device, long axis, const std::string command, std::vector& reply) const; - int CheckReplyFlags(const std::string reply) const; - int GetSetting(long device, long axis, std::string setting, long& data) const; - int GetSetting(long device, long axis, std::string setting, double& data) const; - int SetSetting(long device, long axis, std::string setting, long data) const; - int SetSetting(long device, long axis, std::string setting, double data, int decimalPlaces) const; - bool IsBusy(long device) const; - int Stop(long device, long lockstepGroup = 0) const; - int GetLimits(long device, long axis, long& min, long& max) const; - int SendMoveCommand(long device, long axis, std::string type, long data, bool lockstep = false) const; - int SendAndPollUntilIdle(long device, long axis, std::string command, int timeoutMs) const; - int GetRotaryIndexedDeviceInfo(long device, long axis, long& numIndices, long& currentIndex) const; - int GetFirmwareVersion(long device, double& version) const; - int ActivatePeripheralsIfNeeded(long device) const; + int Command(long device, long axis, const std::string command, zml::Response& reply); + int Command(long device, long axis, const std::string command); + template int GetSetting(long device, long axis, std::string setting, TReturn& data); + int GetSettings(long device, long axis, std::string setting, std::vector& data); + int SetSetting(long device, long axis, std::string setting, double data, int decimalPlaces = -1); + bool IsBusy(long device); + int Stop(long device, long lockstepGroup = 0); + int GetLimits(long device, long axis, long& min, long& max); + int SendMoveCommand(long device, long axis, std::string type, long data, bool lockstep = false); + int SendAndPollUntilIdle(long device, long axis, std::string command); + int GetRotaryIndexedDeviceInfo(long device, long axis, long& numIndices, long& currentIndex); + int GetFirmwareVersion(long device, double& version); + int ActivatePeripheralsIfNeeded(long device); + int handleException(std::function wrapped); + void ensureConnected(); + virtual void onNewConnection(); + void resetConnection(); bool initialized_; std::string port_; MM::Device *device_; MM::Core *core_; - std::string cmdPrefix_; + std::shared_ptr connection_; + +private: + static ConnectionManager connections; }; #endif //_ZABER_H_ diff --git a/DeviceAdapters/Zaber/Zaber.vcxproj b/DeviceAdapters/Zaber/Zaber.vcxproj index 6b4c63ebd..8adb57132 100644 --- a/DeviceAdapters/Zaber/Zaber.vcxproj +++ b/DeviceAdapters/Zaber/Zaber.vcxproj @@ -59,24 +59,32 @@ true true WIN32;MODULE_EXPORTS;_USRDLL;_WINDOWS;%(PreprocessorDefinitions) + $(MM_3RDPARTYPUBLIC)\Zaber\zaber-motion\include;%(AdditionalIncludeDirectories) + stdcpp17 true true + zml.lib;%(AdditionalDependencies) + $(MM_3RDPARTYPUBLIC)\Zaber\zaber-motion\win64\lib;%(AdditionalLibraryDirectories) + + + + @@ -89,4 +97,4 @@ - \ No newline at end of file + diff --git a/DeviceAdapters/Zaber/Zaber.vcxproj.filters b/DeviceAdapters/Zaber/Zaber.vcxproj.filters index 441365c96..192f91275 100644 --- a/DeviceAdapters/Zaber/Zaber.vcxproj.filters +++ b/DeviceAdapters/Zaber/Zaber.vcxproj.filters @@ -33,6 +33,12 @@ Source Files + + Source Files + + + Source Files + @@ -53,5 +59,11 @@ Header Files + + Header Files + + + Header Files + \ No newline at end of file diff --git a/DeviceAdapters/Zaber/ZaberTest/ZaberTest.cpp b/DeviceAdapters/Zaber/ZaberTest/ZaberTest.cpp index 4b01f3772..c73caf1a7 100644 --- a/DeviceAdapters/Zaber/ZaberTest/ZaberTest.cpp +++ b/DeviceAdapters/Zaber/ZaberTest/ZaberTest.cpp @@ -1,6 +1,4 @@ #include "../../../MMCore/MMCore.h" -#include "../../../DeviceKit/Common/PropertyTypes.h" -#include "../../../DeviceKit/Common/DeviceTypes.h" #include #include #include @@ -35,16 +33,12 @@ int main(int argc, char* argv[]) { // Initialize the device // --------------------- - cout << "Loading " << portName << " from library SerialManager..." << endl; - core.loadDevice(portName.c_str(), "SerialManager", portName.c_str()); - cout << "Done." << endl; cout << "Loading " << deviceName << " from library " << moduleName << "..." << endl; core.loadDevice(label.c_str(), moduleName.c_str(), deviceName.c_str()); cout << "Done." << endl; - core.setProperty(label.c_str(), "Port", portName.c_str()); - core.setProperty(portName.c_str(), MM::g_Keyword_BaudRate, "115200"); + core.setProperty(label.c_str(), "Zaber Serial Port", portName.c_str()); cout << "Initializing..." << endl; core.initializeAllDevices(); @@ -55,10 +49,10 @@ int main(int argc, char* argv[]) vector props(core.getDevicePropertyNames(label.c_str())); for (unsigned i=0; i < props.size(); i++) { - cout << props[i] << " (" << ::getPropertyTypeVerbose(core.getPropertyType(label.c_str(), props[i].c_str())) << ") = " + cout << props[i] << " (" << core.getPropertyType(label.c_str(), props[i].c_str()) << ") = " << core.getProperty(label.c_str(), props[i].c_str()) << endl; } - + // unload the device // ----------------- core.unloadAllDevices(); diff --git a/DeviceAdapters/Zaber/ZaberTest/ZaberTest.vcxproj b/DeviceAdapters/Zaber/ZaberTest/ZaberTest.vcxproj index 384ec8922..ab49d9f93 100644 --- a/DeviceAdapters/Zaber/ZaberTest/ZaberTest.vcxproj +++ b/DeviceAdapters/Zaber/ZaberTest/ZaberTest.vcxproj @@ -1,18 +1,10 @@ - + - - Debug - Win32 - Debug x64 - - Release - Win32 - Release x64 @@ -23,32 +15,20 @@ DeviceTest Win32Proj ZaberTest + 10.0 - - Application - Unicode - true - Windows7.1SDK - false - - - Application - Unicode - Windows7.1SDK - true - Application Unicode true - Windows7.1SDK + v142 false Application Unicode - Windows7.1SDK + v142 true @@ -146,10 +126,6 @@ - - - - {36571628-728c-4acd-a47f-503ba91c5d43} diff --git a/DeviceAdapters/configure.ac b/DeviceAdapters/configure.ac index cac90eae8..ad9cf6daa 100644 --- a/DeviceAdapters/configure.ac +++ b/DeviceAdapters/configure.ac @@ -39,11 +39,26 @@ AC_SUBST(MMDEVAPI_LDFLAGS) # Location of third party public files thirdpartypublic="${micromanager_path}/../3rdpartypublic" - +MMTHIRDPARTYPUBLIC="${thirdpartypublic}" +AC_SUBST(MMTHIRDPARTYPUBLIC) MM_INSTALL_DIRS +# Detect the target system +build_linux=no +build_osx=no + +case $host in + *linux*) build_linux=yes ;; + *apple-darwin*) build_osx=yes ;; + *) AC_MSG_ERROR(["Host $host is not supported"]) ;; +esac + +AM_CONDITIONAL([HOST_LINUX], [test "x$build_linux" = xyes]) +AM_CONDITIONAL([HOST_OSX], [test "x$build_osx" = xyes]) + + # libusb 0.1 or libusb-compat MM_ARG_WITH_OPTIONAL_LIB([libusb 0.1 or libusb-compat], [libusb-0-1], [LIBUSB_0_1]) AS_IF([test "x$want_libusb_0_1" != xno],