diff --git a/DeviceAdapters/AAAOTF/AAAOTF.cpp b/DeviceAdapters/AAAOTF/AAAOTF.cpp index 31a6491d8..5cae883fc 100644 --- a/DeviceAdapters/AAAOTF/AAAOTF.cpp +++ b/DeviceAdapters/AAAOTF/AAAOTF.cpp @@ -11,7 +11,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "AAAOTF.h" #include diff --git a/DeviceAdapters/ABS/ABSCamera.h b/DeviceAdapters/ABS/ABSCamera.h index d84b50c50..702dc0428 100644 --- a/DeviceAdapters/ABS/ABSCamera.h +++ b/DeviceAdapters/ABS/ABSCamera.h @@ -6,7 +6,6 @@ #include #pragma warning(disable : 4996) // disable warning for deperecated CRT functions on Windows #endif -#include "FixSnprintf.h" #include "MMDevice.h" #include "MMDeviceConstants.h" diff --git a/DeviceAdapters/ABS/CcmFileStd.cpp b/DeviceAdapters/ABS/CcmFileStd.cpp index a7bd4f1e8..a6ea7e4cb 100644 --- a/DeviceAdapters/ABS/CcmFileStd.cpp +++ b/DeviceAdapters/ABS/CcmFileStd.cpp @@ -26,7 +26,6 @@ #include #pragma warning(disable : 4996) // disable warning for deperecated CRT functions on Windows #endif -#include "FixSnprintf.h" #include "ccmfilestd.h" #include "memoryinifile.h" diff --git a/DeviceAdapters/ABS/TimeSpanPC.h b/DeviceAdapters/ABS/TimeSpanPC.h index 4322b736a..ed4665b49 100644 --- a/DeviceAdapters/ABS/TimeSpanPC.h +++ b/DeviceAdapters/ABS/TimeSpanPC.h @@ -5,7 +5,6 @@ #include #pragma warning(disable : 4996) // disable warning for deperecated CRT functions on Windows #endif -#include "FixSnprintf.h" class CTimeSpanPC { diff --git a/DeviceAdapters/ABS/absdelayloaddll.cpp b/DeviceAdapters/ABS/absdelayloaddll.cpp index 831d4d79c..d29e6a673 100644 --- a/DeviceAdapters/ABS/absdelayloaddll.cpp +++ b/DeviceAdapters/ABS/absdelayloaddll.cpp @@ -3,7 +3,6 @@ #include #pragma warning(disable : 4996) // disable warning for deperecated CRT functions on Windows #endif -#include "FixSnprintf.h" #include #include "delayimp.h" #include "ABSDelayLoadDll.h" diff --git a/DeviceAdapters/AOTF/AOTF.cpp b/DeviceAdapters/AOTF/AOTF.cpp index 3c8060023..a0ef4c99c 100644 --- a/DeviceAdapters/AOTF/AOTF.cpp +++ b/DeviceAdapters/AOTF/AOTF.cpp @@ -29,7 +29,6 @@ #define WIN32_LEAN_AND_MEAN #include #endif -#include "FixSnprintf.h" #include "AOTF.h" #include "ModuleInterface.h" diff --git a/DeviceAdapters/ASIWPTR/wptr.cpp b/DeviceAdapters/ASIWPTR/wptr.cpp index 956c5107e..85cbd0717 100644 --- a/DeviceAdapters/ASIWPTR/wptr.cpp +++ b/DeviceAdapters/ASIWPTR/wptr.cpp @@ -23,8 +23,6 @@ // AUTHOR: Vikram Kopuri, based on Code by Nenad Amodaj Nico Stuurman and Jizhen Zhao // -#include "FixSnprintf.h" - #include "wptr.h" #include #include diff --git a/DeviceAdapters/AgilentLaserCombiner/AgilentLaserCombiner.cpp b/DeviceAdapters/AgilentLaserCombiner/AgilentLaserCombiner.cpp index eff4f4b20..8de77634e 100644 --- a/DeviceAdapters/AgilentLaserCombiner/AgilentLaserCombiner.cpp +++ b/DeviceAdapters/AgilentLaserCombiner/AgilentLaserCombiner.cpp @@ -14,7 +14,6 @@ #define WIN32_LEAN_AND_MEAN #include #endif -#include "FixSnprintf.h" #include "AgilentLaserCombiner.h" #include "LaserCombinerSDK.h" diff --git a/DeviceAdapters/Aladdin/Aladdin.cpp b/DeviceAdapters/Aladdin/Aladdin.cpp index 8366bc7ec..a10277ede 100644 --- a/DeviceAdapters/Aladdin/Aladdin.cpp +++ b/DeviceAdapters/Aladdin/Aladdin.cpp @@ -27,7 +27,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "MMDevice.h" diff --git a/DeviceAdapters/AlliedVisionCamera/AlliedVisionCamera.cpp b/DeviceAdapters/AlliedVisionCamera/AlliedVisionCamera.cpp new file mode 100644 index 000000000..fbc80e37c --- /dev/null +++ b/DeviceAdapters/AlliedVisionCamera/AlliedVisionCamera.cpp @@ -0,0 +1,1411 @@ +/*============================================================================= + Copyright (C) 2023 Allied Vision Technologies. All Rights Reserved. + + This file is distributed under the BSD license. + License text is included with the source distribution. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF TITLE, + NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +=============================================================================*/ +#define NOMINMAX // +#include +#include +#include +#include + +#include "AlliedVisionHub.h" +#include "ModuleInterface.h" +#include "VmbC/VmbC.h" + +/////////////////////////////////////////////////////////////////////////////// +// STATIC VALUES +/////////////////////////////////////////////////////////////////////////////// + +constexpr const char *ADJUST_PACKAGE_SIZE_COMMAND = "GVSPAdjustPacketSize"; + +const std::unordered_map AlliedVisionCamera::m_featureToProperty = { { g_PixelFormatFeature, + MM::g_Keyword_PixelType } }; +const std::unordered_set AlliedVisionCamera::m_ipAddressFeatures = { + "MulticastIPAddress", + "GevCurrentSubnetMask", + "GevCurrentIPAddress", + "GevCurrentDefaultGateway", + "GevPersistentIPAddress", + "GevPersistentDefaultGateway", + "GevPersistentSubnetMask", +}; + +const std::unordered_set AlliedVisionCamera::m_macAddressFeatures = { + "GevMACAddress" +}; + +/////////////////////////////////////////////////////////////////////////////// +// Exported MMDevice API +/////////////////////////////////////////////////////////////////////////////// +MODULE_API void InitializeModuleData() +{ + RegisterDevice(g_hubName, MM::HubDevice, "Allied Vision Hub"); +} + +MODULE_API MM::Device *CreateDevice(const char *deviceName) +{ + if (deviceName == nullptr) + { + return nullptr; + } + + if (std::string(deviceName) == std::string(g_hubName)) + { + return new AlliedVisionHub(); + } + else + { + return new AlliedVisionCamera(deviceName); + } +} + +MODULE_API void DeleteDevice(MM::Device *pDevice) +{ + delete pDevice; +} + +/////////////////////////////////////////////////////////////////////////////// +// AlliedVisionCamera +/////////////////////////////////////////////////////////////////////////////// +AlliedVisionCamera::~AlliedVisionCamera() +{ + m_handle = nullptr; + + for (size_t i = 0; i < MAX_FRAMES; i++) + { + delete[] m_buffer[i]; + } +} + +AlliedVisionCamera::AlliedVisionCamera(const char *deviceName) : + m_sdk(nullptr), + m_handle{ nullptr }, + m_cameraName{ deviceName }, + m_frames{}, + m_buffer{}, + m_bufferSize{ 0 }, + m_payloadSize{ 0 }, + m_isAcquisitionRunning{ false }, + m_currentPixelFormat{}, + m_propertyToFeature{}, + m_exposureFeatureName{} +{ + CreateHubIDProperty(); + // Binning property is a Core Property, we will have a dummy one + CreateProperty(MM::g_Keyword_Binning, "N/A", MM::String, true, nullptr); + AddAllowedValue(MM::g_Keyword_Binning, "N/A"); +} + +int AlliedVisionCamera::Initialize() +{ + auto parentHub = dynamic_cast(GetParentHub()); + if (parentHub == nullptr) + { + LOG_ERROR(VmbErrorBadParameter, "Parent HUB not found!"); + return VmbErrorBadParameter; + } + m_sdk = parentHub->getSDK(); + + LogMessage("Opening camera: " + m_cameraName); + VmbError_t err = m_sdk->VmbCameraOpen_t(m_cameraName.c_str(), VmbAccessModeType::VmbAccessModeFull, &m_handle); + if (err != VmbErrorSuccess || m_handle == nullptr) + { + LOG_ERROR(err, "Error while opening camera or handle is NULL!"); + return err; + } + + // Try to execute custom command available to Allied Vision GigE Cameras to ensure the packet size is chosen well + VmbCameraInfo_t info; + err = m_sdk->VmbCameraInfoQuery_t(m_cameraName.c_str(), &info, sizeof(info)); + if (err == VmbErrorSuccess && info.streamCount > 0) + { + VmbHandle_t stream = info.streamHandles[0]; + if (m_sdk->VmbFeatureCommandRun_t(stream, ADJUST_PACKAGE_SIZE_COMMAND) == VmbErrorSuccess) + { + VmbBool_t isCommandDone = VmbBoolFalse; + do + { + if (m_sdk->VmbFeatureCommandIsDone_t(stream, ADJUST_PACKAGE_SIZE_COMMAND, &isCommandDone) != VmbErrorSuccess) + { + break; + } + } while (isCommandDone == VmbBoolFalse); + } + } + + // Ignore errors from setting up properties + (void)setupProperties(); + return resizeImageBuffer(); +} + +int AlliedVisionCamera::Shutdown() +{ + LogMessage("Shutting down camera: " + m_cameraName); + VmbError_t err = VmbErrorSuccess; + if (m_sdk != nullptr && m_sdk->isInitialized()) + { + if (m_handle != nullptr) + { + err = m_sdk->VmbCameraClose_t(m_handle); + } + } + + return err; +} + +VmbError_t AlliedVisionCamera::setupProperties() +{ + VmbUint32_t featureCount = 0; + VmbError_t err = m_sdk->VmbFeaturesList_t(m_handle, NULL, 0, &featureCount, sizeof(VmbFeatureInfo_t)); + if (err != VmbErrorSuccess) + { + LOG_ERROR(err, "Error occurred when obtaining features count!"); + return err; + } + + std::vector features{ featureCount }; + + err = m_sdk->VmbFeaturesList_t(m_handle, features.data(), featureCount, &featureCount, sizeof(VmbFeatureInfo_t)); + if (err != VmbErrorSuccess) + { + LOG_ERROR(err, "Error occurred when obtaining features!"); + return err; + } + + const auto exposureTime = + std::find_if(features.begin(), features.end(), [](const auto &feat) { return std::string(feat.name) == g_ExposureFeature; }); + + if (exposureTime != features.end()) + { + m_exposureFeatureName = std::string{ exposureTime->name }; + } + else + { + const auto exposureTimeAbs = + std::find_if(features.begin(), features.end(), [](const auto &feat) { return std::string(feat.name) == g_ExposureAbsFeature; }); + + if (exposureTimeAbs != features.end()) + { + m_exposureFeatureName = std::string{ exposureTimeAbs->name }; + } + } + + for (const auto &feature : features) + { + if (feature.visibility != VmbFeatureVisibilityInvisible) + { + err = createPropertyFromFeature(&feature); + if (err != VmbErrorSuccess) + { + LOG_ERROR(err, "Error while creating property" + std::string(feature.name)); + } + } + } + + return VmbErrorSuccess; +} + +VmbError_t AlliedVisionCamera::resizeImageBuffer() +{ + VmbError_t err = m_sdk->VmbPayloadSizeGet_t(m_handle, &m_payloadSize); + if (err != VmbErrorSuccess) + { + LOG_ERROR(err, "Error while reading payload size"); + return err; + } + + m_bufferSize = std::max(GetImageWidth() * GetImageHeight() * m_currentPixelFormat.getBytesPerPixel(), m_payloadSize); + + for (size_t i = 0; i < MAX_FRAMES; i++) + { + delete[] m_buffer[i]; + m_buffer[i] = new VmbUint8_t[m_bufferSize]; + } + + return VmbErrorSuccess; +} + +VmbError_t AlliedVisionCamera::createPropertyFromFeature(const VmbFeatureInfo_t *feature) +{ + if (feature == nullptr) + { + LogMessage("Cannot create feature. It is NULL"); + return VmbErrorInvalidValue; + } + + VmbError_t err = VmbErrorSuccess; + // Skip Event, Chunk, RAW features + std::string featureCategory = feature->category; + if (featureCategory.find(g_EventCategory) != std::string::npos || featureCategory.find(g_ChunkCategory) != std::string::npos || + feature->featureDataType == VmbFeatureDataRaw) + { + // Skip + return err; + } + + // Map feature to property name + const auto propertyName = [&] + { + const auto tempName = mapFeatureNameToPropertyName(feature->name); + + if (tempName != feature->name) + { + if (!m_propertyToFeature.count(tempName)) + { + m_propertyToFeature.emplace(tempName, feature->name); + return tempName; + } + } + + return std::string(feature->name); + }(); + + // uManager callback + CPropertyAction *uManagerCallback = new CPropertyAction(this, &AlliedVisionCamera::onProperty); + + // Vimba callback + auto vmbCallback = [](VmbHandle_t handle, const char *name, void *userContext) + { + (void)handle; + AlliedVisionCamera *camera = reinterpret_cast(userContext); + const auto propertyName = camera->mapFeatureNameToPropertyName(name); + auto err = camera->UpdateProperty(propertyName.c_str()); + if (err != VmbErrorSuccess) + { + camera->LOG_ERROR(err, "Property: " + propertyName + " update failed"); + } + }; + + // Register VMB callback for given feature + err = m_sdk->VmbFeatureInvalidationRegister_t(m_handle, feature->name, vmbCallback, this); + if (err != VmbErrorSuccess) + { + LOG_ERROR(err, "Error while registering invalidation callback for " + std::string(feature->name)); + return err; + } + + if (m_ipAddressFeatures.count(feature->name) || m_macAddressFeatures.count(feature->name)) + { + err = CreateStringProperty(propertyName.c_str(), "", true, uManagerCallback); + return err; + } + + switch (feature->featureDataType) + { + case VmbFeatureDataInt: + { + err = CreateIntegerProperty(propertyName.c_str(), 0, false, uManagerCallback); + break; + } + case VmbFeatureDataBool: + { + err = CreateStringProperty(propertyName.c_str(), g_False, false, uManagerCallback); + AddAllowedValue(propertyName.c_str(), g_False); + AddAllowedValue(propertyName.c_str(), g_True); + break; + } + case VmbFeatureDataCommand: + { + err = CreateStringProperty(propertyName.c_str(), g_Command, false, uManagerCallback); + AddAllowedValue(propertyName.c_str(), g_Command); + AddAllowedValue(propertyName.c_str(), g_Execute); + break; + } + case VmbFeatureDataEnum: + case VmbFeatureDataString: + { + err = CreateStringProperty(propertyName.c_str(), "", false, uManagerCallback); + break; + } + case VmbFeatureDataFloat: + { + err = CreateFloatProperty(propertyName.c_str(), 0.0, false, uManagerCallback); + break; + } + case VmbFeatureDataUnknown: + case VmbFeatureDataRaw: + case VmbFeatureDataNone: + default: + // nothing + break; + } + + if (err != VmbErrorSuccess) + { + LOG_ERROR(err, "Error while creating property " + std::string(feature->name)); + } + + return err; +} + +const unsigned char *AlliedVisionCamera::GetImageBuffer() +{ + return reinterpret_cast(m_buffer[0]); +} + +unsigned AlliedVisionCamera::GetImageWidth() const +{ + std::string value{}; + auto ret = getFeatureValue(g_Width, value); + if (ret != VmbErrorSuccess) + { + LOG_ERROR(ret, "Error while getting image width!"); + return 0; + } + + return atoi(value.c_str()); +} + +unsigned AlliedVisionCamera::GetImageHeight() const +{ + std::string value{}; + auto ret = getFeatureValue(g_Height, value); + if (ret != VmbErrorSuccess) + { + LOG_ERROR(ret, "Error while getting image height!"); + return 0; + } + + return atoi(value.c_str()); +} + +unsigned AlliedVisionCamera::GetImageBytesPerPixel() const +{ + return m_currentPixelFormat.getBytesPerPixel(); +} + +long AlliedVisionCamera::GetImageBufferSize() const +{ + return m_bufferSize; +} + +unsigned AlliedVisionCamera::GetBitDepth() const +{ + return m_currentPixelFormat.getBitDepth(); +} + +unsigned AlliedVisionCamera::GetNumberOfComponents() const +{ + return m_currentPixelFormat.getNumberOfComponents(); +} + +int AlliedVisionCamera::GetBinning() const +{ + // Binning not supported. We support BinningVertical/Horizontal + return 1; +} + +int AlliedVisionCamera::SetBinning(int binSize) +{ + // Binning not supported. We support BinningVertical/Horizontal + return DEVICE_ERR; +} + +double AlliedVisionCamera::GetExposure() const +{ + std::string value{}; + auto ret = getFeatureValue(m_exposureFeatureName.c_str(), value); + if (ret != VmbErrorSuccess) + { + LOG_ERROR(ret, "Error while getting exposure!"); + return 0; + } + + return std::stod(value) / MS_TO_US; +} + +void AlliedVisionCamera::SetExposure(double exp_ms) +{ + SetProperty(m_exposureFeatureName.c_str(), CDeviceUtils::ConvertToString(exp_ms * MS_TO_US)); + GetCoreCallback()->OnExposureChanged(this, exp_ms); +} + +int AlliedVisionCamera::SetROI(unsigned x, unsigned y, unsigned xSize, unsigned ySize) +{ + auto width = GetImageWidth(); + auto height = GetImageHeight(); + VmbError_t err = VmbErrorSuccess; + + std::function setOffsetXProperty = [this](long x) + { + auto err = SetProperty(g_OffsetX, CDeviceUtils::ConvertToString(x)); + if (err) + { + LOG_ERROR(err, "Error while ROI Offset X!"); + } + return err; + }; + std::function setOffsetYProperty = [this](long y) + { + auto err = SetProperty(g_OffsetY, CDeviceUtils::ConvertToString(y)); + if (err) + { + LOG_ERROR(err, "Error while ROI Offset Y!"); + } + return err; + }; + std::function setWidthProperty = [this](long xSize) + { + auto err = SetProperty(g_Width, CDeviceUtils::ConvertToString(xSize)); + if (err) + { + LOG_ERROR(err, "Error while ROI X!"); + } + return err; + }; + std::function setHeightProperty = [this](long ySize) + { + auto err = SetProperty(g_Height, CDeviceUtils::ConvertToString(ySize)); + if (err) + { + LOG_ERROR(err, "Error while ROI Y!"); + } + return err; + }; + + if (xSize > width) + { + err = setOffsetXProperty(x) || setWidthProperty(xSize); + } + else + { + err = setWidthProperty(xSize) || setOffsetXProperty(x); + } + + if (ySize > height) + { + err = setOffsetYProperty(y) || setHeightProperty(ySize); + } + else + { + err = setHeightProperty(ySize) || setOffsetYProperty(y); + } + + if (err != VmbErrorSuccess) + { + return err; + } + + return resizeImageBuffer(); +} + +int AlliedVisionCamera::GetROI(unsigned &x, unsigned &y, unsigned &xSize, unsigned &ySize) +{ + std::map fields = { { g_OffsetX, x }, { g_OffsetY, y }, { g_Width, xSize }, { g_Height, ySize } }; + + VmbError_t err = VmbErrorSuccess; + for (auto &field : fields) + { + std::string value{}; + err = getFeatureValue(field.first, value); + if (err != VmbErrorSuccess) + { + LOG_ERROR(err, "Error while getting ROI!"); + break; + } + field.second = atoi(value.data()); + } + + return err; +} + +int AlliedVisionCamera::ClearROI() +{ + std::string maxWidth, maxHeight; + VmbError_t err = getFeatureValue(g_WidthMax, maxWidth) | getFeatureValue(g_HeightMax, maxHeight); + if (VmbErrorSuccess != err) + { + LOG_ERROR(err, "Error while clearing ROI!"); + return err; + } + + // Keep the order of the fields + std::vector> fields = { + { g_OffsetX, "0" }, { g_OffsetY, "0" }, { g_Width, maxWidth }, { g_Height, maxHeight } + }; + + for (auto &field : fields) + { + err = setFeatureValue(field.first, field.second); + if (err != VmbErrorSuccess) + { + LOG_ERROR(err, "Error while clearing ROI!"); + break; + } + } + + return resizeImageBuffer(); +} + +int AlliedVisionCamera::IsExposureSequenceable(bool &isSequenceable) const +{ + // TODO implement + return VmbErrorSuccess; +} + +void AlliedVisionCamera::GetName(char *name) const +{ + CDeviceUtils::CopyLimitedString(name, m_cameraName.c_str()); +} + +bool AlliedVisionCamera::IsCapturing() +{ + return m_isAcquisitionRunning; +} + +int AlliedVisionCamera::onProperty(MM::PropertyBase *pProp, MM::ActionType eAct) +{ + // Init + VmbError_t err = VmbErrorSuccess; + + auto pChildProperty = dynamic_cast(pProp); + if (pChildProperty == nullptr) + { + err = VmbErrorBadParameter; + LOG_ERROR(err, "Could not get Property from PropertyBase object"); + return err; + } + + const auto propertyName = pProp->GetName(); + + auto valueEqual = [pProp](const std::string& newValue) -> bool { + switch (pProp->GetType()) + { + case MM::PropertyType::Float: + { + double oldValue{}; + pProp->Get(oldValue); + return oldValue == std::stod(newValue); + } + default: + { + std::string oldValue{}; + pProp->Get(oldValue); + return oldValue == newValue; + } + } + }; + + // Check property mapping + auto const featureName = mapPropertyNameToFeatureName(propertyName.c_str()); + + // Get Feature Info and Access Mode + VmbFeatureInfo_t featureInfo; + bool rMode{}, wMode{}; + err = m_sdk->VmbFeatureInfoQuery_t(m_handle, featureName.c_str(), &featureInfo, sizeof(featureInfo)) | + m_sdk->VmbFeatureAccessQuery_t(m_handle, featureName.c_str(), &rMode, &wMode); + if (VmbErrorSuccess != err) + { + LOG_ERROR(err, "Error while getting info or access query!"); + return err; + } + + if (m_ipAddressFeatures.count(featureName)) + { + wMode = false; + } + + const bool readOnly = (rMode && !wMode); + const bool featureAvailable = (rMode || wMode); + + // Get values + std::string propertyValue{}, featureValue{}; + pProp->Get(propertyValue); + + // Handle property value change + switch (eAct) + { + case MM::ActionType::BeforeGet: //!< Update property from feature + + // Update feature range + if (featureAvailable) + { + err = setAllowedValues(&featureInfo, propertyName.c_str()); + } + // Feature not available -> clear value and range + else + { + switch (pProp->GetType()) + { + case MM::Float: + case MM::Integer: + SetPropertyLimits(propertyName.c_str(), 0.0, 0.0); + pProp->Set("0"); + break; + case MM::String: + ClearAllowedValues(propertyName.c_str()); + pProp->Set(""); + break; + default: + // feature type not supported + break; + } + } + + if (rMode) + { + err = getFeatureValue(&featureInfo, featureName.c_str(), featureValue); + if (VmbErrorSuccess != err) + { + LOG_ERROR(err, "Error while getting feature value " + featureName); + return err; + } + + // Update property + if (!valueEqual(featureValue)) + { + pProp->Set(featureValue.c_str()); + err = GetCoreCallback()->OnPropertyChanged(this, propertyName.c_str(), featureValue.c_str()); + if (propertyName == MM::g_Keyword_PixelType) + { + handlePixelFormatChange(featureValue); + } + + if (VmbErrorSuccess != err) + { + LOG_ERROR(err, "Error while calling OnPropertyChanged callback for " + featureName); + return err; + } + + if (m_exposureFeatureName == featureName) + { + GetCoreCallback()->OnExposureChanged(this, std::stod(featureValue) / MS_TO_US); + } + } + } + + // Set property to readonly (grey out in GUI) if it is readonly or unavailable + pChildProperty->SetReadOnly(readOnly || !featureAvailable); + + break; + case MM::ActionType::AfterSet: //!< Update feature from property + err = setFeatureValue(&featureInfo, featureName.c_str(), propertyValue); + if (err == VmbErrorInvalidValue) + { + // Update limits first to have latest min and max + err = setAllowedValues(&featureInfo, propertyName.c_str()); + if (VmbErrorSuccess != err) + { + LOG_ERROR(err, "Error while setting allowed values for feature " + featureName); + return err; + } + + // Adjust value + double min{}, max{}; + err = GetPropertyLowerLimit(propertyName.c_str(), min) | GetPropertyUpperLimit(propertyName.c_str(), max); + if (VmbErrorSuccess != err) + { + LOG_ERROR(err, "Error while getting limits for " + propertyName); + return err; + } + std::string adjustedValue = adjustValue(featureInfo, min, max, std::stod(propertyValue)); + err = setFeatureValue(&featureInfo, featureName.c_str(), adjustedValue); + if (err == VmbErrorSuccess) + { + err = GetCoreCallback()->OnPropertyChanged(this, propertyName.c_str(), adjustedValue.c_str()); + } + } + else if (err == VmbErrorSuccess) + { + err = GetCoreCallback()->OnPropertyChanged(this, propertyName.c_str(), propertyValue.c_str()); + } + + + if (propertyName == MM::g_Keyword_PixelType) + { + handlePixelFormatChange(propertyValue); + } + break; + default: + // nothing + break; + } + + if (VmbErrorSuccess != err) + { + LOG_ERROR(err, "Error while updating property " + propertyName); + } + + return err; + +} + +void AlliedVisionCamera::handlePixelFormatChange(const std::string &pixelType) +{ + m_currentPixelFormat.setPixelType(pixelType); +} + +VmbError_t AlliedVisionCamera::getFeatureValue(VmbFeatureInfo_t *featureInfo, const char *featureName, std::string &value) const +{ + VmbError_t err = VmbErrorSuccess; + switch (featureInfo->featureDataType) + { + case VmbFeatureDataBool: + { + VmbBool_t out; + err = m_sdk->VmbFeatureBoolGet_t(m_handle, featureName, &out); + if (err != VmbErrorSuccess) + { + break; + } + value = (out ? g_True : g_False); + break; + } + case VmbFeatureDataEnum: + { + const char *out = nullptr; + err = m_sdk->VmbFeatureEnumGet_t(m_handle, featureName, &out); + if (err != VmbErrorSuccess) + { + break; + } + value = std::string(out); + break; + } + case VmbFeatureDataFloat: + { + double out; + err = m_sdk->VmbFeatureFloatGet_t(m_handle, featureName, &out); + if (err != VmbErrorSuccess) + { + break; + } + value = std::to_string(out); + break; + } + case VmbFeatureDataInt: + { + VmbInt64_t out; + err = m_sdk->VmbFeatureIntGet_t(m_handle, featureName, &out); + if (err != VmbErrorSuccess) + { + break; + } + if (m_ipAddressFeatures.count(featureName)) + { + std::stringstream ipAddressStream; + ipAddressStream << (0xFF & (out >> 24)) << "." << (0xFF & (out >> 16)) << "." << (0xFF & (out >> 8)) << "." << (0xFF & out); + + value = ipAddressStream.str(); + } + else if (m_macAddressFeatures.count(featureName)) + { + std::stringstream macAddressStream; + macAddressStream << std::hex + << std::setw(2) << std::setfill('0') << static_cast(0xFF & (out >> 40)) << ":" + << std::setw(2) << std::setfill('0') << static_cast(0xFF & (out >> 32)) << ":" + << std::setw(2) << std::setfill('0') << static_cast(0xFF & (out >> 24)) << ":" + << std::setw(2) << std::setfill('0') << static_cast(0xFF & (out >> 16)) << ":" + << std::setw(2) << std::setfill('0') << static_cast(0xFF & (out >> 8)) << ":" + << std::setw(2) << std::setfill('0') << static_cast(0xFF & out); + value = macAddressStream.str(); + } + else + { + value = std::to_string(out); + } + break; + } + case VmbFeatureDataString: + { + VmbUint32_t size = 0; + err = m_sdk->VmbFeatureStringGet_t(m_handle, featureName, nullptr, 0, &size); + if (VmbErrorSuccess == err && size > 0) + { + std::shared_ptr buff = std::shared_ptr(new char[size]); + err = m_sdk->VmbFeatureStringGet_t(m_handle, featureName, buff.get(), size, &size); + if (err != VmbErrorSuccess) + { + break; + } + value = std::string(buff.get()); + } + break; + } + case VmbFeatureDataCommand: + value = std::string(g_Command); + break; + case VmbFeatureDataUnknown: + case VmbFeatureDataRaw: + case VmbFeatureDataNone: + default: + // nothing + break; + } + + if (VmbErrorSuccess != err) + { + LOG_ERROR(err, "Error while getting feature value " + std::string(featureName)); + } + + return err; +} + +VmbError_t AlliedVisionCamera::getFeatureValue(const char *featureName, std::string &value) const +{ + VmbFeatureInfo_t featureInfo; + VmbError_t err = m_sdk->VmbFeatureInfoQuery_t(m_handle, featureName, &featureInfo, sizeof(featureInfo)); + if (VmbErrorSuccess != err) + { + LOG_ERROR(err, "Error while getting feature value " + std::string(featureName)); + return err; + } + + return getFeatureValue(&featureInfo, featureName, value); +} + +VmbError_t AlliedVisionCamera::setFeatureValue(VmbFeatureInfo_t *featureInfo, const char *featureName, std::string &value) +{ + VmbError_t err = VmbErrorSuccess; + std::stringstream ss(value); + bool isDone = false; + VmbUint32_t maxLen = 0; + + switch (featureInfo->featureDataType) + { + case VmbFeatureDataBool: + { + VmbBool_t out = (value == g_True ? true : false); + err = m_sdk->VmbFeatureBoolSet_t(m_handle, featureName, out); + break; + } + case VmbFeatureDataEnum: + { + err = m_sdk->VmbFeatureEnumSet_t(m_handle, featureName, value.c_str()); + break; + } + case VmbFeatureDataFloat: + { + double out; + ss >> out; + err = m_sdk->VmbFeatureFloatSet_t(m_handle, featureName, out); + break; + } + case VmbFeatureDataInt: + { + VmbInt64_t out; + ss >> out; + err = m_sdk->VmbFeatureIntSet_t(m_handle, featureName, out); + break; + } + case VmbFeatureDataString: + { + err = m_sdk->VmbFeatureStringMaxlengthQuery_t(m_handle, featureName, &maxLen); + if (err != VmbErrorSuccess) + { + break; + } + if (value.size() > maxLen) + { + err = VmbErrorInvalidValue; + } + else + { + err = m_sdk->VmbFeatureStringSet_t(m_handle, featureName, value.c_str()); + } + break; + } + case VmbFeatureDataCommand: + if (value == g_Execute) + { + const auto propertyName = mapFeatureNameToPropertyName(featureName); + if (!propertyName.empty()) + { + err = m_sdk->VmbFeatureCommandRun_t(m_handle, featureName); + if (err != VmbErrorSuccess) + { + break; + } + while (!isDone) + { + err = m_sdk->VmbFeatureCommandIsDone_t(m_handle, featureName, &isDone); + if (err != VmbErrorSuccess) + { + break; + } + } + // Set back property to "Command" + err = SetProperty(propertyName.c_str(), g_Command); + GetCoreCallback()->OnPropertyChanged(this, propertyName.c_str(), g_Command); + if (err != VmbErrorSuccess) + { + break; + } + } + else + { + err = VmbErrorInvalidValue; + } + } + break; + case VmbFeatureDataUnknown: + case VmbFeatureDataRaw: + case VmbFeatureDataNone: + default: + // nothing + break; + } + + if (VmbErrorSuccess != err) + { + LOG_ERROR(err, "Error while setting feature value " + std::string(featureName)); + } + + return err; +} + +VmbError_t AlliedVisionCamera::setFeatureValue(const char *featureName, std::string &value) +{ + VmbFeatureInfo_t featureInfo; + VmbError_t err = m_sdk->VmbFeatureInfoQuery_t(m_handle, featureName, &featureInfo, sizeof(featureInfo)); + if (VmbErrorSuccess != err) + { + LOG_ERROR(err, "Error while setting feature value " + std::string(featureName)); + return err; + } + + return setFeatureValue(&featureInfo, featureName, value); +} + +std::string AlliedVisionCamera::mapFeatureNameToPropertyName(const char *feature) const +{ + + auto search = m_featureToProperty.find(feature); + if (search != m_featureToProperty.end()) + { + return search->second; + } + + return feature; +} +std::string AlliedVisionCamera::mapPropertyNameToFeatureName(const char *property) const +{ + auto search = m_propertyToFeature.find(property); + if (search != m_propertyToFeature.end()) + { + return search->second; + } + + return property; +} + +std::string AlliedVisionCamera::adjustValue(VmbFeatureInfo_t &featureInfo, double min, double max, double propertyValue) const +{ + VmbError_t err = VmbErrorSuccess; + double step = 1.0; + VmbInt64_t stepI = 1; + bool isIncremental = true; + switch (featureInfo.featureDataType) + { + case VmbFeatureDataFloat: + err = m_sdk->VmbFeatureFloatIncrementQuery_t(m_handle, featureInfo.name, &isIncremental, &step); + break; + case VmbFeatureDataInt: + err = m_sdk->VmbFeatureIntIncrementQuery_t(m_handle, featureInfo.name, &stepI); + step = static_cast(stepI); + break; + default: + // nothing + break; + } + if (VmbErrorSuccess != err) + { + LOG_ERROR(err, "Error while getting increment query for feature " + std::string(featureInfo.name)); + return std::to_string(propertyValue); + } + + if (!isIncremental) + { + return std::to_string(propertyValue); + } + + if (propertyValue > max) + { + return std::to_string(max); + } + if (propertyValue < min) + { + return std::to_string(min); + } + + VmbInt64_t factor = static_cast((propertyValue - min) / step); + double prev = min + factor * step; + double next = min + (factor + 1) * step; + + double prevDiff = abs(propertyValue - prev); + double nextDiff = abs(next - propertyValue); + + return (nextDiff < prevDiff) ? std::to_string(next) : std::to_string(prev); +} + +VmbError_t AlliedVisionCamera::setAllowedValues(const VmbFeatureInfo_t *feature, const char *propertyName) +{ + if (feature == nullptr || propertyName == nullptr) + { + return VmbErrorInvalidValue; + } + + VmbError_t err = VmbErrorSuccess; + + switch (feature->featureDataType) + { + case VmbFeatureDataBool: + { + // Already set in creation, but maybe reset when become unavailable + if (GetNumberOfPropertyValues(propertyName) == 0) + { + AddAllowedValue(propertyName, g_False); + AddAllowedValue(propertyName, g_True); + } + break; + } + case VmbFeatureDataCommand: + { + // Already set in creation, but maybe reset when become unavailable + if (GetNumberOfPropertyValues(propertyName) == 0) + { + AddAllowedValue(propertyName, g_Command); + AddAllowedValue(propertyName, g_Execute); + } + break; + } + case VmbFeatureDataFloat: + { + double min, max; + err = m_sdk->VmbFeatureFloatRangeQuery_t(m_handle, feature->name, &min, &max); + if (VmbErrorSuccess != err || min == max) + { + break; + } + + err = SetPropertyLimits(propertyName, min, max); + break; + } + case VmbFeatureDataEnum: + { + std::array values; + std::vector strValues; + VmbUint32_t valuesNum = 0; + err = m_sdk->VmbFeatureEnumRangeQuery_t(m_handle, feature->name, values.data(), MM::MaxStrLength, &valuesNum); + if (VmbErrorSuccess != err) + { + break; + } + + for (size_t i = 0; i < valuesNum; i++) + { + strValues.push_back(values[i]); + } + err = SetAllowedValues(propertyName, strValues); + + break; + } + case VmbFeatureDataInt: + { + VmbInt64_t min, max; + std::vector strValues; + + err = m_sdk->VmbFeatureIntRangeQuery_t(m_handle, feature->name, &min, &max); + if (VmbErrorSuccess != err || min == max) + { + break; + } + + err = SetPropertyLimits(propertyName, static_cast(min), static_cast(max)); + break; + } + case VmbFeatureDataString: + case VmbFeatureDataRaw: + case VmbFeatureDataNone: + default: + // nothing + break; + } + + if (VmbErrorSuccess != err) + { + LOG_ERROR(err, "Error while setting allowed values for feature " + std::string(feature->name)); + } + + return err; +} + +int AlliedVisionCamera::SnapImage() +{ + if (IsCapturing()) + { + return DEVICE_CAMERA_BUSY_ACQUIRING; + } + resizeImageBuffer(); + + VmbFrame_t frame; + frame.buffer = m_buffer[0]; + frame.bufferSize = m_payloadSize; + + VmbError_t err = VmbErrorSuccess; + err = m_sdk->VmbFrameAnnounce_t(m_handle, &frame, sizeof(VmbFrame_t)); + if (err != VmbErrorSuccess) + { + LOG_ERROR(err, "Error while snapping image!"); + (void)StopSequenceAcquisition(); + return err; + } + err = m_sdk->VmbCaptureStart_t(m_handle); + if (err != VmbErrorSuccess) + { + LOG_ERROR(err, "Error while snapping image!"); + (void)StopSequenceAcquisition(); + return err; + } + err = m_sdk->VmbCaptureFrameQueue_t(m_handle, &frame, nullptr); + if (err != VmbErrorSuccess) + { + LOG_ERROR(err, "Error while snapping image!"); + (void)StopSequenceAcquisition(); + return err; + } + err = m_sdk->VmbFeatureCommandRun_t(m_handle, g_AcquisitionStart); + if (err != VmbErrorSuccess) + { + LOG_ERROR(err, "Error while snapping image!"); + (void)StopSequenceAcquisition(); + return err; + } + m_isAcquisitionRunning = true; + err = m_sdk->VmbCaptureFrameWait_t(m_handle, &frame, 3000); + if (err != VmbErrorSuccess) + { + LOG_ERROR(err, "Error while snapping image!"); + (void)StopSequenceAcquisition(); + return err; + } + + err = transformImage(&frame); + if (err != VmbErrorSuccess) + { + LOG_ERROR(err, "Error while snapping image - cannot transform image!"); + (void)StopSequenceAcquisition(); + return err; + } + + (void)StopSequenceAcquisition(); + return err; +} + +int AlliedVisionCamera::StartSequenceAcquisition(long numImages, double interval_ms, bool stopOnOverflow) +{ + (void)stopOnOverflow; + (void)interval_ms; + (void)numImages; + + if (IsCapturing()) + { + return DEVICE_CAMERA_BUSY_ACQUIRING; + } + + int err = GetCoreCallback()->PrepareForAcq(this); + if (err != VmbErrorSuccess) + { + LOG_ERROR(err, "Error while preparing for acquisition!"); + return err; + } + + err = resizeImageBuffer(); + if (err != VmbErrorSuccess) + { + LOG_ERROR(err, "Error during frame praparation for continous acquisition!"); + return err; + } + + for (size_t i = 0; i < MAX_FRAMES; i++) + { + m_frames[i].buffer = m_buffer[i]; + m_frames[i].bufferSize = m_payloadSize; + m_frames[i].context[0] = this; //(i); //(frame->context[0])->insertFrame(frame); + }; + + err = m_sdk->VmbFrameAnnounce_t(m_handle, &(m_frames[i]), sizeof(VmbFrame_t)); + if (err != VmbErrorSuccess) + { + LOG_ERROR(err, "Error during frame praparation for continous acquisition!"); + return err; + } + + err = m_sdk->VmbCaptureFrameQueue_t(m_handle, &(m_frames[i]), frameCallback); + if (err != VmbErrorSuccess) + { + LOG_ERROR(err, "Error during frame praparation for continous acquisition!"); + return err; + } + } + + err = m_sdk->VmbCaptureStart_t(m_handle); + if (err != VmbErrorSuccess) + { + LOG_ERROR(err, "Error during frame praparation for continous acquisition!"); + return err; + } + + err = m_sdk->VmbFeatureCommandRun_t(m_handle, g_AcquisitionStart); + m_isAcquisitionRunning = true; + + if (err != VmbErrorSuccess) + { + LOG_ERROR(err, "Error during start acquisition!"); + return err; + } + + GetCoreCallback()->OnPropertiesChanged(this); + + return UpdateStatus(); +} + +int AlliedVisionCamera::StartSequenceAcquisition(double interval_ms) +{ + return StartSequenceAcquisition(LONG_MAX, interval_ms, true); +} +int AlliedVisionCamera::StopSequenceAcquisition() +{ + // This method shall never return any error + VmbError_t err = VmbErrorSuccess; + if (IsCapturing()) + { + err = m_sdk->VmbFeatureCommandRun_t(m_handle, g_AcquisitionStop); + m_isAcquisitionRunning = false; + if (err != VmbErrorSuccess) + { + LOG_ERROR(err, "Error during stopping acquisition command!"); + return err; + } + } + + err = m_sdk->VmbCaptureEnd_t(m_handle); + if (err != VmbErrorSuccess) + { + LOG_ERROR(err, "Error during stop acquisition!"); + return err; + } + + err = m_sdk->VmbCaptureQueueFlush_t(m_handle); + if (err != VmbErrorSuccess) + { + LOG_ERROR(err, "Error during stop acquisition!"); + return err; + } + + err = m_sdk->VmbFrameRevokeAll_t(m_handle); + if (err != VmbErrorSuccess) + { + LOG_ERROR(err, "Error during stop acquisition!"); + return err; + } + + GetCoreCallback()->OnPropertiesChanged(this); + + return UpdateStatus(); +} + +VmbError_t AlliedVisionCamera::transformImage(VmbFrame_t *frame) +{ + VmbError_t err = VmbErrorSuccess; + VmbImage src{}, dest{}; + VmbTransformInfo info{}; + std::shared_ptr tempBuff = std::shared_ptr(new VmbUint8_t[m_bufferSize]); + auto srcBuff = reinterpret_cast(frame->buffer); + + src.Data = srcBuff; + src.Size = sizeof(src); + + dest.Data = tempBuff.get(); + dest.Size = sizeof(dest); + + err = m_sdk->VmbSetImageInfoFromPixelFormat_t(frame->pixelFormat, frame->width, frame->height, &src); + if (err != VmbErrorSuccess) + { + LOG_ERROR(err, "Cannot set image info from pixel format!"); + return err; + } + + err = m_sdk->VmbSetImageInfoFromPixelFormat_t(m_currentPixelFormat.getVmbFormat(), frame->width, frame->height, &dest); + if (err != VmbErrorSuccess) + { + LOG_ERROR(err, "Cannot set image info from pixel format!"); + return err; + } + + err = m_sdk->VmbImageTransform_t(&src, &dest, &info, 0); + if (err != VmbErrorSuccess) + { + LOG_ERROR(err, "Cannot transform image!"); + return err; + } + + memcpy(srcBuff, tempBuff.get(), m_bufferSize); + return err; +} + +void AlliedVisionCamera::insertFrame(VmbFrame_t *frame) +{ + if (frame != nullptr && frame->receiveStatus == VmbFrameStatusComplete) + { + VmbError_t err = VmbErrorSuccess; + + err = transformImage(frame); + if (err != VmbErrorSuccess) + { + // Error logged in transformImage + return; + } + + // TODO implement metadata + Metadata md; + md.put("Camera", m_cameraName); + + VmbUint8_t *buffer = reinterpret_cast(frame->buffer); + err = GetCoreCallback()->InsertImage(this, buffer, GetImageWidth(), GetImageHeight(), m_currentPixelFormat.getBytesPerPixel(), + m_currentPixelFormat.getNumberOfComponents(), md.Serialize().c_str()); + + if (err == DEVICE_BUFFER_OVERFLOW) + { + GetCoreCallback()->ClearImageBuffer(this); + err = GetCoreCallback()->InsertImage(this, buffer, GetImageWidth(), GetImageHeight(), m_currentPixelFormat.getBytesPerPixel(), + m_currentPixelFormat.getNumberOfComponents(), md.Serialize().c_str(), false); + } + + if (IsCapturing()) + { + m_sdk->VmbCaptureFrameQueue_t(m_handle, frame, + [](const VmbHandle_t cameraHandle, const VmbHandle_t streamHandle, VmbFrame_t *frame) + { + (void)cameraHandle; + (void)streamHandle; + reinterpret_cast(frame->context[0])->insertFrame(frame); + }); + } + } +} \ No newline at end of file diff --git a/DeviceAdapters/AlliedVisionCamera/AlliedVisionCamera.h b/DeviceAdapters/AlliedVisionCamera/AlliedVisionCamera.h new file mode 100644 index 000000000..ec0e2660c --- /dev/null +++ b/DeviceAdapters/AlliedVisionCamera/AlliedVisionCamera.h @@ -0,0 +1,472 @@ +/*============================================================================= + Copyright (C) 2023 Allied Vision Technologies. All Rights Reserved. + + This file is distributed under the BSD license. + License text is included with the source distribution. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF TITLE, + NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +=============================================================================*/ +#ifndef ALLIEDVISIONCAMERA_H +#define ALLIEDVISIONCAMERA_H + +#include +#include +#include +#include +#include + +#include "AlliedVisionDeviceBase.h" +#include "DeviceBase.h" +#include "Loader/LibLoader.h" + +/////////////////////////////////////////////////////////////////////////////// +// STATIC FEATURE NAMES (FROM VIMBA) +/////////////////////////////////////////////////////////////////////////////// +static constexpr const char *g_PixelFormatFeature = "PixelFormat"; +static constexpr const char *g_ExposureFeature = "ExposureTime"; +static constexpr const char *g_ExposureAbsFeature = "ExposureTimeAbs"; +static constexpr const char *g_BinningHorizontalFeature = "BinningHorizontal"; +static constexpr const char *g_BinningVerticalFeature = "BinningVertical"; +static constexpr const char *g_Width = "Width"; +static constexpr const char *g_Height = "Height"; +static constexpr const char *g_OffsetX = "OffsetX"; +static constexpr const char *g_OffsetY = "OffsetY"; +static constexpr const char *g_WidthMax = "WidthMax"; +static constexpr const char *g_HeightMax = "HeightMax"; + +/////////////////////////////////////////////////////////////////////////////// +// STATIC VARIABLES +/////////////////////////////////////////////////////////////////////////////// +static constexpr const char *g_True = "True"; +static constexpr const char *g_False = "False"; +static constexpr const char *g_Execute = "Execute"; +static constexpr const char *g_Command = "Command"; +static constexpr const char *g_ChunkCategory = "ChunkDataControl"; +static constexpr const char *g_EventCategory = "EventControl"; +static constexpr const char *g_AcquisitionStart = "AcquisitionStart"; +static constexpr const char *g_AcquisitionStop = "AcquisitionStop"; +static constexpr const char *g_AcqusitionStatus = "AcqusitionStatus"; + +static constexpr const double MS_TO_US = 1000.0; + +/** + * @brief Pixel Format class that contains VMB Pixel Format info and contains + * helper methods to convert these information to the one that uManager + * supports. + * + * [IMPORTANT] uManager supports formats: + * 8bit GRAY [no. component = 1] + * 16bit GRAY [no. component = 1] + * 32bit RGB [no. component = 4] + */ +class PixelFormatConverter +{ + /////////////////////////////////////////////////////////////////////////////// + // PUBLIC + /////////////////////////////////////////////////////////////////////////////// +public: + /** + * @brief Constructor + */ + PixelFormatConverter() : + m_pixelType{ "Mono8" }, + m_components{ 1 }, + m_bitDepth{ 8 }, + m_bytesPerPixel{ 1 }, + m_isMono{ true }, + m_vmbFormat{ VmbPixelFormatMono8 } + { + } + + /** + * @brief Destructor + */ + virtual ~PixelFormatConverter() = default; + + /** + * @brief Setter for pixel type + * @param[in] type New pixel type (string value from VMB) + */ + void setPixelType(const std::string &type) + { + m_pixelType = type; + updateFields(); + } + + /** + * @brief Getter to check if given pixel type is Mono or Color + * @return True if Mono, otherwise false + */ + bool isMono() const + { + return m_isMono; + } + + /** + * @brief Getter for number of components for given pixel type + * uManager supports only 1 or 4 components + * @return Number of components + */ + unsigned getNumberOfComponents() const + { + return m_components; + } + + /** + * @brief Getter for bit depth for given pixel type + * @return Number of bits for one pixel + */ + unsigned getBitDepth() const + { + return m_bitDepth; + } + + /** + * @brief Getter for bytes per pixel + * @return Bytes per pixel + */ + unsigned getBytesPerPixel() const + { + return m_bytesPerPixel; + } + + /** + * @brief Getter of destination VmbPixelFormat that fits into the one, that + * uManager supports. In general uManager supports three pixelFormats: + * 1. Mono8 + * 2. Mono16 + * 3. RGB32 + * These types fits into following VmbPixelTypes: + * 1. VmbPixelFormatMono8 + * 2. VmbPixelFormatMono16 + * 3. VmbPixelFormatBgra8 + * @return Destination VmbPixelFormat + */ + VmbPixelFormatType getVmbFormat() const + { + return m_vmbFormat; + } + + /////////////////////////////////////////////////////////////////////////////// + // PRIVATE + /////////////////////////////////////////////////////////////////////////////// +private: + /** + * @brief Helper method to update info if given format is Mono or Color + */ + void updateMono() + { + std::regex re("Mono(\\d+)"); + std::smatch m; + m_isMono = std::regex_search(m_pixelType, m, re); + } + + /** + * @brief Helper method to update number of components for given pixel type + * uManager supports only 1 or 4 components + */ + void updateNumberOfComponents() + { + m_components = m_isMono ? 1 : 4; + } + + /** + * @brief Helper method to update bit depth for given pixel type + */ + void updateBitDepth() + { + m_bitDepth = 8; + if (isMono()) + { + std::regex re("Mono(\\d+)"); + std::smatch m; + std::regex_search(m_pixelType, m, re); + if (m.size() > 0) + { + if (std::atoi(m[1].str().c_str()) == 16) + { + // We do transformation to Mono16 only for Mono16, otherwise + // it will always be Mono8 + m_bitDepth = 16; + } + else + { + m_bitDepth = 8; + } + } + else + { + // ERROR + } + } + else + { + m_bitDepth = 32; + } + } + + /** + * @brief Helper method to update bytes per pixel + */ + void updateBytesPerPixel() + { + m_bytesPerPixel = m_bitDepth / 8; + } + + /** + * @brief Helper method to update destination VmbPixelFormatType + */ + void updateVmbFormat() + { + switch (m_bytesPerPixel) + { + case 1: + m_vmbFormat = VmbPixelFormatMono8; + break; + case 2: + m_vmbFormat = VmbPixelFormatMono16; + break; + case 4: + m_vmbFormat = VmbPixelFormatBgra8; // TODO check if this is a valid format + break; + default: + break; + } + } + + /** + * @brief Helper method to update all required fields + */ + void updateFields() + { + // [IMPORTANT] Keep order of the calls + updateMono(); + updateNumberOfComponents(); + updateBitDepth(); + updateBytesPerPixel(); + updateVmbFormat(); + } + + std::string m_pixelType; //!< Pixel type (in string) - value from VMB + unsigned m_components; //!< Number of components + unsigned m_bitDepth; //, AlliedVisionCamera> +{ + /////////////////////////////////////////////////////////////////////////////// + // PUBLIC + /////////////////////////////////////////////////////////////////////////////// +public: + /** + * @brief Contructor of Allied Vision Camera + * @param[in] deviceName Device name + */ + AlliedVisionCamera(const char *deviceName); + /** + * @brief Allied Vision Camera destructor + */ + virtual ~AlliedVisionCamera(); + + /////////////////////////////////////////////////////////////////////////////// + // uMANAGER API METHODS + /////////////////////////////////////////////////////////////////////////////// + int Initialize() override; + int Shutdown() override; + const unsigned char *GetImageBuffer() override; + unsigned GetImageWidth() const override; + unsigned GetImageHeight() const override; + unsigned GetImageBytesPerPixel() const override; + int SnapImage() override; + long GetImageBufferSize() const override; + unsigned GetBitDepth() const override; + int GetBinning() const override; + int SetBinning(int binSize) override; + void SetExposure(double exp_ms) override; + double GetExposure() const override; + int SetROI(unsigned x, unsigned y, unsigned xSize, unsigned ySize) override; + int GetROI(unsigned &x, unsigned &y, unsigned &xSize, unsigned &ySize) override; + int ClearROI() override; + int IsExposureSequenceable(bool &isSequenceable) const override; + void GetName(char *name) const override; + int StartSequenceAcquisition(double interval_ms) override; + int StartSequenceAcquisition(long numImages, double interval_ms, bool stopOnOverflow) override; + int StopSequenceAcquisition() override; + bool IsCapturing() override; + unsigned GetNumberOfComponents() const override; + + /////////////////////////////////////////////////////////////////////////////// + // uMANAGER CALLBACKS + /////////////////////////////////////////////////////////////////////////////// + int onProperty(MM::PropertyBase *pProp, + MM::ActionType eAct); //!<< General property callback + + /////////////////////////////////////////////////////////////////////////////// + // PRIVATE + /////////////////////////////////////////////////////////////////////////////// +private: + // Static variables + static constexpr const VmbUint8_t MAX_FRAMES = 7; //!<< Max frame number in the buffer + + /** + * @brief Helper method to handle change of pixel type + * @param[in] pixelType New pixel type (as string) + */ + void handlePixelFormatChange(const std::string &pixelType); + + /** + * @brief Resize all buffers for image frames + * @return VmbError_t + */ + VmbError_t resizeImageBuffer(); + + /** + * @brief Setup uManager properties from Vimba features + * @return VmbError_t + */ + VmbError_t setupProperties(); + + /** + * @brief Helper method to create single uManager property from Vimba feature + * @param[in] feature Pointer to the Vimba feature + * @return VmbError_t + */ + VmbError_t createPropertyFromFeature(const VmbFeatureInfo_t *feature); + + /** + * @brief Helper method to set allowed values for given property, based on + * its feature type + * @param[in] feature Vimba feature name + * @param[in] propertyName uManager propery name (if differs from + * feature name) + * @return + */ + VmbError_t setAllowedValues(const VmbFeatureInfo_t *feature, const char *propertyName); + + /** + * @brief Insert ready frame to the uManager + * @param[in] frame Pointer to the frame + */ + void insertFrame(VmbFrame_t *frame); + + /** + * @brief Method to get feature value, based on its type. Feature value is + * always a string type. + * @param[in] featureInfo Feature info object + * @param[in] featureName Feature name + * @param[out] value Value of feature, read from device + * @return VmbError_t + */ + VmbError_t getFeatureValue(VmbFeatureInfo_t *featureInfo, const char *featureName, std::string &value) const; + /** + * @brief Method to get feature value, based on its type. Feature value is + * always a string type. + * @param[in] featureName Feature name + * @param[out] value Value of feature, read from device + * @return VmbError_t + */ + VmbError_t getFeatureValue(const char *featureName, std::string &value) const; + + /** + * @brief Method to set a feature value, bases on its type. Feature value is + * always a string type. + * @param[in] featureInfo Feature info object + * @param[in] featureName Feature name + * @param[in] value Value of feature to be set + * @return VmbError_t + */ + VmbError_t setFeatureValue(VmbFeatureInfo_t *featureInfo, const char *featureName, std::string &value); + + /** + * @brief Method to set a feature value, bases on its type. Feature value is + * always a string type. + * @param[in] featureName Feature name + * @param[in] value Value of feature to be set + * @return VmbError_t + */ + VmbError_t setFeatureValue(const char *featureName, std::string &value); + + /** + * @brief Helper method to map feature name into property name of uManager + * @param[in] feature Vimba Feature name + * @return uManager property name + */ + std::string mapFeatureNameToPropertyName(const char *feature) const; + + /** + * @brief Helper method to map uManager property in Vimba feature or features + * name + * @param[in] property uManager property name + * @param featureNames Vimba feature or features name + */ + std::string mapPropertyNameToFeatureName(const char *property) const; + + /** + * @brief In case trying to set invalid value, adjust it to the closest with + * inceremntal step + + * @param[in] step Incremental step + + * @return Adjusted value resresented as a string + */ + + /** + * @brief In case trying to set invalid value, adjust it to the closest with + * inceremntal step + * @param[in] featureInfo Feature info object + * @param[in] min Minimum for given property + * @param[in] max Maximum for given property + * @param[in] propertyValue Value that was tried to be set + * @return Adjusted value resresented as a string + */ + std::string adjustValue(VmbFeatureInfo_t &featureInfo, double min, double max, double propertyValue) const; + + /** + * @brief Internal method to transform image to the destination format that + * uManager supports (see \ref PixelFormatConverter) and replaces input frame + * with output (transformed) frame + * @param[in] frame Frame with image to transform from and into + * @return Error in case of failure + */ + VmbError_t transformImage(VmbFrame_t *frame); + + /////////////////////////////////////////////////////////////////////////////// + // MEMBERS + /////////////////////////////////////////////////////////////////////////////// + std::shared_ptr m_sdk; // m_frames; // m_buffer; // m_featureToProperty; //!< Map of features name into uManager properties + std::unordered_map m_propertyToFeature; //!< Map of uManager properties into Vimba features + + static const std::unordered_set m_ipAddressFeatures; + static const std::unordered_set m_macAddressFeatures; +}; + +#endif diff --git a/DeviceAdapters/AlliedVisionCamera/AlliedVisionCamera.vcxproj b/DeviceAdapters/AlliedVisionCamera/AlliedVisionCamera.vcxproj new file mode 100644 index 000000000..1aa16787b --- /dev/null +++ b/DeviceAdapters/AlliedVisionCamera/AlliedVisionCamera.vcxproj @@ -0,0 +1,110 @@ + + + + + Debug + x64 + + + Release + x64 + + + + + + + + + + + + + + + + + + {b8c95f39-54bf-40a9-807b-598df2821d55} + + + + 16.0 + Win32Proj + {12e75cb0-4b48-4d7c-bb26-d928f18488c2} + AlliedVisionCamera + 10.0 + + + + DynamicLibrary + true + v142 + Unicode + + + DynamicLibrary + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + true + C:\Program Files\Micro-Manager-2.0\ + + + false + + + + true + _DEBUG;ALLIEDVISIONCAMERA_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + + $(MM_3RDPARTYPRIVATE)\AVT\VimbaX-2023-1-Win64\api\include;%(AdditionalIncludeDirectories) + + + Windows + true + false + + + + + true + true + true + NDEBUG;ALLIEDVISIONCAMERA_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + + $(MM_3RDPARTYPRIVATE)\AVT\VimbaX-2023-1-Win64\api\include;%(AdditionalIncludeDirectories) + + + Windows + true + true + true + false + + + + + + \ No newline at end of file diff --git a/DeviceAdapters/AlliedVisionCamera/AlliedVisionCamera.vcxproj.filters b/DeviceAdapters/AlliedVisionCamera/AlliedVisionCamera.vcxproj.filters new file mode 100644 index 000000000..cbb7c0517 --- /dev/null +++ b/DeviceAdapters/AlliedVisionCamera/AlliedVisionCamera.vcxproj.filters @@ -0,0 +1,48 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/DeviceAdapters/AlliedVisionCamera/AlliedVisionDeviceBase.cpp b/DeviceAdapters/AlliedVisionCamera/AlliedVisionDeviceBase.cpp new file mode 100644 index 000000000..6e1430cb5 --- /dev/null +++ b/DeviceAdapters/AlliedVisionCamera/AlliedVisionDeviceBase.cpp @@ -0,0 +1 @@ +#include "AlliedVisionDeviceBase.h" diff --git a/DeviceAdapters/AlliedVisionCamera/AlliedVisionDeviceBase.h b/DeviceAdapters/AlliedVisionCamera/AlliedVisionDeviceBase.h new file mode 100644 index 000000000..a719d772e --- /dev/null +++ b/DeviceAdapters/AlliedVisionCamera/AlliedVisionDeviceBase.h @@ -0,0 +1,85 @@ +/*============================================================================= + Copyright (C) 2023 Allied Vision Technologies. All Rights Reserved. + + This file is distributed under the BSD license. + License text is included with the source distribution. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF TITLE, + NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +=============================================================================*/ +#ifndef ALLIEDVISIONDEVICEBASE_H +#define ALLIEDVISIONDEVICEBASE_H + +#include "DeviceBase.h" +#include "Loader/LibLoader.h" + +#define LOG_ERROR(err, message) logError(err, message, __FUNCTION__, __LINE__) + +/** + * @brief Base class for Allied Vision devices + */ +template class AlliedVisionDeviceBase : public CDeviceBase +{ + /////////////////////////////////////////////////////////////////////////////// + // PUBLIC + /////////////////////////////////////////////////////////////////////////////// +public: + /** + * @brief Constructor + */ + AlliedVisionDeviceBase() + { + CDeviceBase::InitializeDefaultErrorMessages(); + setApiErrorMessages(); + }; + + /** + * @brief Destructor + */ + virtual ~AlliedVisionDeviceBase() = default; + + void logError(int error, std::string message, std::string function = "", int line = 0) const + { + std::string prefix = "[" + function + "():" + std::to_string(line) + "] "; + CDeviceBase::LogMessage(prefix + message); + CDeviceBase::LogMessageCode(error); + } + + /////////////////////////////////////////////////////////////////////////////// + // PRIVATE + /////////////////////////////////////////////////////////////////////////////// +private: + /** + * @brief Setup error messages for Vimba API + */ + void setApiErrorMessages() + { + CDeviceBase::SetErrorText(VmbErrorApiNotStarted, "Vimba X API not started"); + CDeviceBase::SetErrorText(VmbErrorNotFound, "Device cannot be found"); + CDeviceBase::SetErrorText(VmbErrorDeviceNotOpen, "Device cannot be opened"); + CDeviceBase::SetErrorText(VmbErrorBadParameter, "Invalid parameter passed to the function"); + CDeviceBase::SetErrorText(VmbErrorNotImplemented, "Feature not implemented"); + CDeviceBase::SetErrorText(VmbErrorNotSupported, "Feature not supported"); + CDeviceBase::SetErrorText(VmbErrorUnknown, "Unknown error"); + CDeviceBase::SetErrorText(VmbErrorInvalidValue, "The value is not valid: either out of bounds or not an " + "increment of the minimum"); + CDeviceBase::SetErrorText(VmbErrorBadHandle, "Given device handle is not valid"); + CDeviceBase::SetErrorText(VmbErrorInvalidAccess, "Operation is invalid with the current access mode"); + CDeviceBase::SetErrorText(VmbErrorTimeout, "Timeout occured"); + CDeviceBase::SetErrorText(VmbErrorNotAvailable, "Something is not available"); + CDeviceBase::SetErrorText(VmbErrorNotInitialized, "Something is not initialized"); + CDeviceBase::SetErrorText(VmbErrorAlready, "The operation has been already done"); + CDeviceBase::SetErrorText(VmbErrorFeaturesUnavailable, "Feature is currently unavailable"); + } +}; + +#endif diff --git a/DeviceAdapters/AlliedVisionCamera/AlliedVisionHub.cpp b/DeviceAdapters/AlliedVisionCamera/AlliedVisionHub.cpp new file mode 100644 index 000000000..aa3147fea --- /dev/null +++ b/DeviceAdapters/AlliedVisionCamera/AlliedVisionHub.cpp @@ -0,0 +1,96 @@ +/*============================================================================= + Copyright (C) 2023 Allied Vision Technologies. All Rights Reserved. + + This file is distributed under the BSD license. + License text is included with the source distribution. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF TITLE, + NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +=============================================================================*/ +#include "AlliedVisionHub.h" + +#include "AlliedVisionCamera.h" + +AlliedVisionHub::AlliedVisionHub() : + m_sdk(std::make_shared()) +{ +} + +int AlliedVisionHub::DetectInstalledDevices() +{ + LogMessage("Detecting installed cameras..."); + VmbUint32_t camNum; + // Get the number of connected cameras first + VmbError_t err = m_sdk->VmbCamerasList_t(nullptr, 0, &camNum, 0); + if (VmbErrorSuccess == err) + { + VmbCameraInfo_t *camInfo = new VmbCameraInfo_t[camNum]; + + // Get the cameras + err = m_sdk->VmbCamerasList_t(camInfo, camNum, &camNum, sizeof *camInfo); + + if (err == VmbErrorSuccess) + { + for (VmbUint32_t i = 0; i < camNum; ++i) + { + if (camInfo[i].permittedAccess & VmbAccessModeFull) + { + MM::Device *pDev = new AlliedVisionCamera(camInfo[i].cameraIdString); + AddInstalledDevice(pDev); + } + } + } + + delete[] camInfo; + } + else + { + LOG_ERROR(err, "Cannot get installed devices!"); + } + + return err; +} + +int AlliedVisionHub::Initialize() +{ + LogMessage("Init HUB"); + if (m_sdk->isInitialized()) + { + return DEVICE_OK; + } + else + { + LOG_ERROR(VmbErrorApiNotStarted, "SDK not initialized!"); + return VmbErrorApiNotStarted; + } +} + +int AlliedVisionHub::Shutdown() +{ + LogMessage("Shutting down HUB"); + return DEVICE_OK; +} + +void AlliedVisionHub::GetName(char *name) const +{ + CDeviceUtils::CopyLimitedString(name, g_hubName); +} + +bool AlliedVisionHub::Busy() +{ + return false; +} + +std::shared_ptr &AlliedVisionHub::getSDK() +{ + return m_sdk; +} diff --git a/DeviceAdapters/AlliedVisionCamera/AlliedVisionHub.h b/DeviceAdapters/AlliedVisionCamera/AlliedVisionHub.h new file mode 100644 index 000000000..7d47f4b96 --- /dev/null +++ b/DeviceAdapters/AlliedVisionCamera/AlliedVisionHub.h @@ -0,0 +1,73 @@ +/*============================================================================= + Copyright (C) 2023 Allied Vision Technologies. All Rights Reserved. + + This file is distributed under the BSD license. + License text is included with the source distribution. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF TITLE, + NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +=============================================================================*/ +#ifndef ALLIEDVISIONHUB_H +#define ALLIEDVISIONHUB_H + +#include "AlliedVisionDeviceBase.h" +#include "DeviceBase.h" +#include "Loader/LibLoader.h" + +#include +/////////////////////////////////////////////////////////////////////////////// +// STATIC VARIABLES +/////////////////////////////////////////////////////////////////////////////// +static constexpr const char *g_hubName = "AlliedVisionHub"; + +/** + * @brief Class that represents a HUB of supported devices + */ +class AlliedVisionHub : public AlliedVisionDeviceBase, AlliedVisionHub> +{ + /////////////////////////////////////////////////////////////////////////////// + // PUBLIC + /////////////////////////////////////////////////////////////////////////////// +public: + /** + * @brief Contructor of a HUB + */ + AlliedVisionHub(); + + /** + * @brief Destructor of a HUB + */ + virtual ~AlliedVisionHub() = default; + + /** + * @brief SDK getter + * @return Pointer to SDK + */ + std::shared_ptr &getSDK(); + + /////////////////////////////////////////////////////////////////////////////// + // uMANAGER API METHODS + /////////////////////////////////////////////////////////////////////////////// + int DetectInstalledDevices() override; + int Initialize() override; + int Shutdown() override; + void GetName(char *name) const override; + bool Busy() override; + + /////////////////////////////////////////////////////////////////////////////// + // PRIVATE + /////////////////////////////////////////////////////////////////////////////// +private: + std::shared_ptr m_sdk; // +#elif _WIN32 +#include +#else +// nothing +#endif + +#include "Constants.h" + +VimbaXApi::VimbaXApi() : + m_sdk(VIMBA_X_LIB_NAME, VIMBA_X_LIB_DIR.c_str()), + m_initialized(false), + m_imageTransform(VIMBA_X_IMAGE_TRANSFORM_NAME, VIMBA_X_LIB_DIR.c_str()) +{ + if (m_sdk.isLoaded() && m_imageTransform.isLoaded()) + { + bool allResolved = true; + VmbStartup_t = m_sdk.resolveFunction("VmbStartup", allResolved); + VmbVersionQuery_t = m_sdk.resolveFunction("VmbVersionQuery", allResolved); + VmbShutdown_t = m_sdk.resolveFunction("VmbShutdown", allResolved); + VmbCamerasList_t = m_sdk.resolveFunction("VmbCamerasList", allResolved); + VmbCameraOpen_t = m_sdk.resolveFunction("VmbCameraOpen", allResolved); + VmbCameraClose_t = m_sdk.resolveFunction("VmbCameraClose", allResolved); + VmbCameraInfoQuery_t = m_sdk.resolveFunction("VmbCameraInfoQuery", allResolved); + VmbPayloadSizeGet_t = m_sdk.resolveFunction("VmbPayloadSizeGet", allResolved); + VmbFrameAnnounce_t = m_sdk.resolveFunction("VmbFrameAnnounce", allResolved); + VmbCaptureStart_t = m_sdk.resolveFunction("VmbCaptureStart", allResolved); + VmbCaptureEnd_t = m_sdk.resolveFunction("VmbCaptureEnd", allResolved); + VmbCaptureFrameQueue_t = m_sdk.resolveFunction("VmbCaptureFrameQueue", allResolved); + VmbCaptureFrameWait_t = m_sdk.resolveFunction("VmbCaptureFrameWait", allResolved); + VmbCaptureQueueFlush_t = m_sdk.resolveFunction("VmbCaptureQueueFlush", allResolved); + VmbFrameRevokeAll_t = m_sdk.resolveFunction("VmbFrameRevokeAll", allResolved); + VmbFeatureCommandRun_t = m_sdk.resolveFunction("VmbFeatureCommandRun", allResolved); + VmbFeaturesList_t = m_sdk.resolveFunction("VmbFeaturesList", allResolved); + VmbFeatureBoolGet_t = m_sdk.resolveFunction("VmbFeatureBoolGet", allResolved); + VmbFeatureBoolSet_t = m_sdk.resolveFunction("VmbFeatureBoolSet", allResolved); + VmbFeatureEnumGet_t = m_sdk.resolveFunction("VmbFeatureEnumGet", allResolved); + VmbFeatureEnumSet_t = m_sdk.resolveFunction("VmbFeatureEnumSet", allResolved); + VmbFeatureFloatGet_t = m_sdk.resolveFunction("VmbFeatureFloatGet", allResolved); + VmbFeatureFloatSet_t = m_sdk.resolveFunction("VmbFeatureFloatSet", allResolved); + VmbFeatureIntGet_t = m_sdk.resolveFunction("VmbFeatureIntGet", allResolved); + VmbFeatureIntSet_t = m_sdk.resolveFunction("VmbFeatureIntSet", allResolved); + VmbFeatureStringGet_t = m_sdk.resolveFunction("VmbFeatureStringGet", allResolved); + VmbFeatureStringSet_t = m_sdk.resolveFunction("VmbFeatureStringSet", allResolved); + VmbChunkDataAccess_t = m_sdk.resolveFunction("VmbChunkDataAccess", allResolved); + VmbFeatureEnumRangeQuery_t = m_sdk.resolveFunction("VmbFeatureEnumRangeQuery", allResolved); + VmbFeatureIntRangeQuery_t = m_sdk.resolveFunction("VmbFeatureIntRangeQuery", allResolved); + VmbFeatureStringMaxlengthQuery_t = m_sdk.resolveFunction("VmbFeatureStringMaxlengthQuery", allResolved); + VmbFeatureInfoQuery_t = m_sdk.resolveFunction("VmbFeatureInfoQuery", allResolved); + VmbFeatureFloatRangeQuery_t = m_sdk.resolveFunction("VmbFeatureFloatRangeQuery", allResolved); + VmbFeatureInvalidationRegister_t = m_sdk.resolveFunction("VmbFeatureInvalidationRegister", allResolved); + VmbFeatureAccessQuery_t = m_sdk.resolveFunction("VmbFeatureAccessQuery", allResolved); + VmbFeatureIntIncrementQuery_t = m_sdk.resolveFunction("VmbFeatureIntIncrementQuery", allResolved); + VmbFeatureFloatIncrementQuery_t = m_sdk.resolveFunction("VmbFeatureFloatIncrementQuery", allResolved); + VmbFeatureCommandIsDone_t = m_sdk.resolveFunction("VmbFeatureCommandIsDone", allResolved); + VmbSetImageInfoFromPixelFormat_t = m_imageTransform.resolveFunction("VmbSetImageInfoFromPixelFormat", allResolved); + VmbImageTransform_t = m_imageTransform.resolveFunction("VmbImageTransform", allResolved); + if (allResolved) + { + auto err = VmbStartup_t(nullptr); + if (err == VmbErrorSuccess) + { + m_initialized = true; + } + } + } +} + +bool VimbaXApi::isInitialized() const +{ + return m_initialized; +} + +VimbaXApi::~VimbaXApi() +{ + if (m_initialized) + { + m_initialized = false; + VmbShutdown_t(); + } +} + +bool LibLoader::isLoaded() const +{ + return m_loaded; +} +#ifdef __linux__ +LibLoader::LibLoader(const char *lib, const char *libPath) : + m_libName(lib), + m_libPath(libPath), + m_module(nullptr), + m_loaded(false) +{ + std::string path = std::string(m_libName); + dlerror(); + m_module = dlopen(path.c_str(), RTLD_NOW); + if (m_module != nullptr) + { + m_loaded = true; + } +} + +LibLoader::~LibLoader() +{ + if (m_loaded) + { + dlclose(m_module); + m_module = nullptr; + m_loaded = false; + m_libName = nullptr; + } +} + +SymbolWrapper LibLoader::resolveFunction(const char *functionName, bool &allResolved) const +{ + dlerror(); + void *funPtr = nullptr; + if (allResolved) + { + funPtr = dlsym(m_module, functionName); + allResolved = allResolved && (funPtr != nullptr); + } + return SymbolWrapper(funPtr); +} +#elif _WIN32 +LibLoader::LibLoader(const char *lib, const char *libPath) : + m_libName(lib), + m_libPath(libPath), + m_module(nullptr), + m_loaded(false) +{ + SetDllDirectoryA(m_libPath); + m_module = LoadLibraryA(m_libName); + if (m_module != nullptr) + { + m_loaded = true; + } +} + +LibLoader::~LibLoader() +{ + if (m_loaded) + { + FreeModule(static_cast(m_module)); + m_module = nullptr; + m_loaded = false; + m_libName = nullptr; + } +} + +SymbolWrapper LibLoader::resolveFunction(const char *functionName, bool &allResolved) const +{ + void *funPtr = nullptr; + if (allResolved) + { + funPtr = GetProcAddress(static_cast(m_module), functionName); + allResolved = allResolved && (funPtr != nullptr); + } + return SymbolWrapper(funPtr); +} +#else +// nothing +#endif diff --git a/DeviceAdapters/AlliedVisionCamera/Loader/LibLoader.h b/DeviceAdapters/AlliedVisionCamera/Loader/LibLoader.h new file mode 100644 index 000000000..fc63ea663 --- /dev/null +++ b/DeviceAdapters/AlliedVisionCamera/Loader/LibLoader.h @@ -0,0 +1,187 @@ +/*============================================================================= + Copyright (C) 2023 Allied Vision Technologies. All Rights Reserved. + + This file is distributed under the BSD license. + License text is included with the source distribution. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF TITLE, + NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +=============================================================================*/ +#ifndef LIBLOADER_H +#define LIBLOADER_H + +#include +#include + +#include "VmbC/VmbC.h" +#include "VmbImageTransform/VmbTransform.h" + +/** + * @brief Wrapper for single Windows process + */ +class SymbolWrapper +{ + /////////////////////////////////////////////////////////////////////////////// + // PUBLIC + /////////////////////////////////////////////////////////////////////////////// +public: + /** + * @brief Constructor of wrapper + * @param[in] funPtr Pointer to the resolved symbol + */ + explicit SymbolWrapper(void *funPtr) : + m_funPtr(funPtr) + { + } + + /** + * @brief Operator () overload to call function + */ + template ::value>> operator T *() const + { + return reinterpret_cast(m_funPtr); + } + /////////////////////////////////////////////////////////////////////////////// + // PRIVATE + /////////////////////////////////////////////////////////////////////////////// +private: + void *m_funPtr; //GetName(); + if (name.find("OutputB") != string::npos) index = 1; + if (name.find("OutputC") != string::npos) index = 2; + + if (eAct == MM::AfterSet) + { + bool acquiring = sequenceRunning_; + if (acquiring) + StopSequenceAcquisition(true); + + { + DriverGuard dg(this); + + if (sequenceRunning_) + return ERR_BUSY_ACQUIRING; + + //added to use RTA + if (!(iCurrentTriggerMode_ == SOFTWARE)) + SetToIdle(); + + string val; + pProp->Get(val); + for (unsigned i = 0; i < ExternalOutputStates_.size(); ++i) + { + if (ExternalOutputStates_[i].compare(val) == 0) + { + int iState = 1; + if (i == 0) + iState = 1; //Enabled + if (i == 1) + iState = 0; //Disabled + unsigned ret = SetDDGExternalOutputEnabled(index, iState); + if (DRV_SUCCESS != ret) + return (int)ret; + else + { + if (acquiring) + StartSequenceAcquisition(sequenceLength_ - imageCounter_, intervalMs_, stopOnOverflow_); + PrepareSnap(); + ExternalOutputState_[index] = val; + return DEVICE_OK; + } + } + } + assert(!"Unrecognized External Output State"); + } + } + else if (eAct == MM::BeforeGet) + { + DriverGuard dg(this); //not even sure this is needed + pProp->Set(ExternalOutputState_[index].c_str()); + } + return DEVICE_OK; + } + + + int AndorCamera::OnDDGExternalOutputTime(MM::PropertyBase* pProp, MM::ActionType eAct) + { + unsigned long index = 0; + auto name = pProp->GetName(); + if (name.find("OutputB") != string::npos) index = 1; + if (name.find("OutputC") != string::npos) index = 2; + + if (eAct == MM::AfterSet) + { + bool acquiring = sequenceRunning_; + if (acquiring) + StopSequenceAcquisition(true); + + { + DriverGuard dg(this); + + if (sequenceRunning_) + return ERR_BUSY_ACQUIRING; + + long val; + pProp->Get(val); + long delay = extOutputDelay_[index]; + long width = extOutputWidth_[index]; + if (name.find("Width") != string::npos) + if (val == extOutputWidth_[index]) + return DEVICE_OK; + else + width = val; + if (name.find("Delay") != string::npos) + if (val == extOutputDelay_[index]) + return DEVICE_OK; + else + delay = val; + pProp->Set(val); + + //added to use RTA + if (!(iCurrentTriggerMode_ == SOFTWARE)) + SetToIdle(); + + unsigned ret = SetDDGExternalOutputTime(index, delay * 1000, width * 1000); + if (DRV_SUCCESS != ret) + return (int)ret; + + if (name.find("Width") != string::npos) + extOutputWidth_[index] = val; + else + extOutputDelay_[index] = val; + + if (acquiring) + StartSequenceAcquisition(sequenceLength_ - imageCounter_, intervalMs_, stopOnOverflow_); + + PrepareSnap(); + } + } + else if (eAct == MM::BeforeGet) + { + DriverGuard dg(this); //not even sure this is needed + pProp->Set((name.find("Width") != string::npos) ? extOutputWidth_[index] : extOutputDelay_[index]); + } + return DEVICE_OK; + } + int AndorCamera::OnCountConvert(MM::PropertyBase* pProp, MM::ActionType eAct) { if (eAct == MM::AfterSet) diff --git a/DeviceAdapters/Andor/Andor.h b/DeviceAdapters/Andor/Andor.h index a298c2ed0..8d97ca506 100644 --- a/DeviceAdapters/Andor/Andor.h +++ b/DeviceAdapters/Andor/Andor.h @@ -187,6 +187,9 @@ class AndorCamera : public CCameraBase int OnDDGGateDelay(MM::PropertyBase* pProp, MM::ActionType eAct); int OnDDGGateWidth(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnDDGExternalOutputEnabled(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnDDGExternalOutputTime(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnOptAcquireMode(MM::PropertyBase* pProp, MM::ActionType eAct); int OnROI(MM::PropertyBase* pProp, MM::ActionType eAct); void UpdateOAParams(const char* OAModeName); @@ -258,6 +261,11 @@ class AndorCamera : public CCameraBase std::string IOCTrigger_; long mcpGain_; long gateDelay_, gateWidth_; + + long extOutputDelay_[3], extOutputWidth_[3]; + std::vector ExternalOutputStates_; + std::string ExternalOutputState_[3]; + double countConvertWavelength_; std::string optAcquireModeStr_; diff --git a/DeviceAdapters/AndorLaserCombiner/AndorLaserCombiner.cpp b/DeviceAdapters/AndorLaserCombiner/AndorLaserCombiner.cpp index 657a59991..8a6b9f433 100644 --- a/DeviceAdapters/AndorLaserCombiner/AndorLaserCombiner.cpp +++ b/DeviceAdapters/AndorLaserCombiner/AndorLaserCombiner.cpp @@ -30,8 +30,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" - // Declarations for the ALC library. #include "ALC_REV.h" diff --git a/DeviceAdapters/AndorLaserCombiner/build.xml b/DeviceAdapters/AndorLaserCombiner/build.xml index 4c5d324cc..8e0fe08fe 100644 --- a/DeviceAdapters/AndorLaserCombiner/build.xml +++ b/DeviceAdapters/AndorLaserCombiner/build.xml @@ -1,13 +1,10 @@ - - - - - + - + diff --git a/DeviceAdapters/AndorLaserCombiner/lib/AB_ALC_REV.dll b/DeviceAdapters/AndorLaserCombiner/lib/AB_ALC_REV.dll deleted file mode 100644 index f48fc1f93..000000000 Binary files a/DeviceAdapters/AndorLaserCombiner/lib/AB_ALC_REV.dll and /dev/null differ diff --git a/DeviceAdapters/AndorLaserCombiner/lib/AB_ALC_REV64.dll b/DeviceAdapters/AndorLaserCombiner/lib/AB_ALC_REV64.dll deleted file mode 100644 index c3dfa4dd9..000000000 Binary files a/DeviceAdapters/AndorLaserCombiner/lib/AB_ALC_REV64.dll and /dev/null differ diff --git a/DeviceAdapters/AndorLaserCombiner/lib/usbi2cio64.dll b/DeviceAdapters/AndorLaserCombiner/lib/usbi2cio64.dll deleted file mode 100644 index 2456c62cd..000000000 Binary files a/DeviceAdapters/AndorLaserCombiner/lib/usbi2cio64.dll and /dev/null differ diff --git a/DeviceAdapters/AndorSDK3/AOIProperty.cpp b/DeviceAdapters/AndorSDK3/AOIProperty.cpp index c6261984d..e7292a04c 100644 --- a/DeviceAdapters/AndorSDK3/AOIProperty.cpp +++ b/DeviceAdapters/AndorSDK3/AOIProperty.cpp @@ -82,10 +82,11 @@ void TAOIProperty::populateWidthMaps(bool fullAoiControl) if (fullAoiControl) { + auto height = sensor_height_->Get(); aoiWidthIndexMap_[2560] = 0; aoiWidthHeightMap_[2560] = 2160; aoiWidthIndexMap_[2048] = 1; - aoiWidthHeightMap_[2048] = 2048; + aoiWidthHeightMap_[2048] = (height > 2046) ? 2048 : 2046 ; aoiWidthIndexMap_[1920] = 2; aoiWidthHeightMap_[1920] = 1080; aoiWidthIndexMap_[1400] = 3; diff --git a/DeviceAdapters/AndorSDK3/AndorSDK3.cpp b/DeviceAdapters/AndorSDK3/AndorSDK3.cpp index 60f465ecb..a0a0a3221 100644 --- a/DeviceAdapters/AndorSDK3/AndorSDK3.cpp +++ b/DeviceAdapters/AndorSDK3/AndorSDK3.cpp @@ -777,8 +777,8 @@ int CAndorSDK3Camera::Initialize() DDGStepWidthMode_property = new TEnumProperty(TAndorSDK3Strings::DDG_STEP_WIDTH_MODE, cameraDevice->GetEnum(L"DDGStepWidthMode"), this, thd_, snapShotController_, false, true); - // SRRF (for Sona only) - if (0 == s_cameraName.compare(0, 4, "Sona")) + // SRRF (for Sona or Zyla) + if ((0 == s_cameraName.compare(0, 4, "Sona")) || (0 == s_cameraName.compare(0, 4, "Zyla"))) { SRRFCamera_ = new SRRFAndorSDK3Camera(this); if (SRRFCamera_) { diff --git a/DeviceAdapters/Arduino/Arduino.cpp b/DeviceAdapters/Arduino/Arduino.cpp index 71fc4c419..57d10fb35 100644 --- a/DeviceAdapters/Arduino/Arduino.cpp +++ b/DeviceAdapters/Arduino/Arduino.cpp @@ -21,7 +21,6 @@ #define WIN32_LEAN_AND_MEAN #include #endif -#include "FixSnprintf.h" const char* g_DeviceNameArduinoHub = "Arduino-Hub"; const char* g_DeviceNameArduinoSwitch = "Arduino-Switch"; diff --git a/DeviceAdapters/Arduino32bitBoards/Arduino32bitBoards.cpp b/DeviceAdapters/Arduino32bitBoards/Arduino32bitBoards.cpp index 5e59da4ee..7bf57abdd 100644 --- a/DeviceAdapters/Arduino32bitBoards/Arduino32bitBoards.cpp +++ b/DeviceAdapters/Arduino32bitBoards/Arduino32bitBoards.cpp @@ -32,7 +32,6 @@ #define WIN32_LEAN_AND_MEAN #include #endif -#include "FixSnprintf.h" const char* g_DeviceNameArduino32Hub = "Arduino32-Hub"; const char* g_DeviceNameArduino32Switch = "Arduino32-Switch"; diff --git a/DeviceAdapters/ArduinoCounter/ArduinoCounter.cpp b/DeviceAdapters/ArduinoCounter/ArduinoCounter.cpp new file mode 100644 index 000000000..53ceb5f2a --- /dev/null +++ b/DeviceAdapters/ArduinoCounter/ArduinoCounter.cpp @@ -0,0 +1,876 @@ +/////////////////////////////////////////////////////////////////////////////// +// FILE: ArduinoCounter.cpp +// PROJECT: Micro-Manager +// SUBSYSTEM: DeviceAdapters +//----------------------------------------------------------------------------- +// DESCRIPTION: Arduino adapter. Needs accompanying firmware +// COPYRIGHT: Altos Labs, 2023, based on code copyright UCSF 2008 +// LICENSE: LGPL +// +// AUTHOR: Nico Stuurman, nstuurman@altoslabs.com, 7/13/2023, 10/10/2023 +// + +#include "ArduinoCounter.h" +#include "ModuleInterface.h" +#include +#include + +#ifdef WIN32 + #define WIN32_LEAN_AND_MEAN + #include +#endif + +const char* g_DeviceNameArduinoCounterCamera = "ArduinoCounterCamera"; + + + +// Global info about the state of the Arduino. +const double g_Min_MMVersion = 2.0; +const double g_Max_MMVersion = 2.0; +const char* g_versionProp = "Version"; +const char* g_Undefined = "Undefined"; +const char* g_Logic = "Output Logic"; +const char* g_Direct = "Direct"; +const char* g_Invert = "Invert"; + + + + +/////////////////////////////////////////////////////////////////////////////// +// Exported MMDevice API +/////////////////////////////////////////////////////////////////////////////// +MODULE_API void InitializeModuleData() +{ + RegisterDevice(g_DeviceNameArduinoCounterCamera, MM::CameraDevice, "ArduinoCounterCamera"); +} + +MODULE_API MM::Device* CreateDevice(const char* deviceName) +{ + if (deviceName == 0) + return 0; + + if (strcmp(deviceName, g_DeviceNameArduinoCounterCamera) == 0) + { + return new ArduinoCounterCamera; + } + + return 0; +} + +MODULE_API void DeleteDevice(MM::Device* pDevice) +{ + delete pDevice; +} + + + +ArduinoCounterCamera::ArduinoCounterCamera() : + imageBuffer_(0), + nrCamerasInUse_(0), + initialized_(false), + invert_(false) +{ + InitializeDefaultErrorMessages(); + + SetErrorText(ERR_INVALID_DEVICE_NAME, "Please select a valid camera"); + SetErrorText(ERR_NO_PHYSICAL_CAMERA, "No physical camera assigned"); + SetErrorText(ERR_NO_EQUAL_SIZE, "Cameras differ in image size"); + SetErrorText(ERR_FIRMWARE_VERSION_TOO_NEW, "Firmware version is newer than expected"); + SetErrorText(ERR_FIRMWARE_VERSION_TOO_OLD, "Firmware version is older than expected"); + SetErrorText(ERR_BOARD_NOT_FOUND, "Board did not identify as ArduinoCounter"); + + // Name + CreateProperty(MM::g_Keyword_Name, g_DeviceNameArduinoCounterCamera, MM::String, true); + + // Description + CreateProperty(MM::g_Keyword_Description, "Combines Arduino Pulse Counter with a Camera", MM::String, true); + + for (int i = 0; i < MAX_NUMBER_PHYSICAL_CAMERAS; i++) { + usedCameras_.push_back(g_Undefined); + } + + CPropertyAction* pAct = new CPropertyAction(this, &ArduinoCounterCamera::OnPort); + CreateProperty(MM::g_Keyword_Port, "Undefined", MM::String, false, pAct, true); +} + +ArduinoCounterCamera::~ArduinoCounterCamera() +{ + if (initialized_) + Shutdown(); +} + +int ArduinoCounterCamera::Shutdown() +{ + delete imageBuffer_; + // Rely on the cameras to shut themselves down + return DEVICE_OK; +} + +int ArduinoCounterCamera::Initialize() +{ + // get list with available Cameras. + std::vector availableCameras; + availableCameras.clear(); + char deviceName[MM::MaxStrLength]; + unsigned int deviceIterator = 0; + for (;;) + { + GetLoadedDeviceOfType(MM::CameraDevice, deviceName, deviceIterator++); + if (0 < strlen(deviceName)) + { + availableCameras.push_back(std::string(deviceName)); + } + else + break; + } + + availableCameras_.push_back(g_Undefined); + std::vector::iterator iter; + for (iter = availableCameras.begin(); + iter != availableCameras.end(); + iter++) + { + MM::Device* camera = GetDevice((*iter).c_str()); + std::ostringstream os; + os << this << " " << camera; + LogMessage(os.str().c_str()); + if (camera && (this != camera)) + availableCameras_.push_back(*iter); + } + + for (long i = 0; i < MAX_NUMBER_PHYSICAL_CAMERAS; i++) + { + CPropertyActionEx* pAct = new CPropertyActionEx(this, &ArduinoCounterCamera::OnPhysicalCamera, i); + std::ostringstream os; + os << "Physical Camera " << i + 1; + CreateProperty(os.str().c_str(), availableCameras_[0].c_str(), MM::String, false, pAct, false); + SetAllowedValues(os.str().c_str(), availableCameras_); + } + + CPropertyAction* pAct = new CPropertyAction(this, &ArduinoCounterCamera::OnBinning); + CreateProperty(MM::g_Keyword_Binning, "1", MM::Integer, false, pAct, false); + + // start Arduino + // The first second or so after opening the serial port, the Arduino is waiting for firmwareupgrades. Sleep 2 seconds + CDeviceUtils::SleepMs(2000); + + + // Check that we have a controller: + PurgeComPort(port_.c_str()); + int ret = startCommunication(); + if (DEVICE_OK != ret) + return ret; + + if (version_ < g_Min_MMVersion) + return ERR_FIRMWARE_VERSION_TOO_OLD; + if (version_ > g_Max_MMVersion) + return ERR_FIRMWARE_VERSION_TOO_NEW; + + pAct = new CPropertyAction(this, &ArduinoCounterCamera::OnVersion); + std::ostringstream sversion; + sversion << version_; + CreateProperty(g_versionProp, sversion.str().c_str(), MM::Float, true, pAct); + + pAct = new CPropertyAction(this, &ArduinoCounterCamera::OnLogic); + CreateProperty(g_Logic, g_Direct, MM::String, false, pAct); + AddAllowedValue(g_Logic, g_Direct); + AddAllowedValue(g_Logic, g_Invert); + + + initialized_ = true; + + return DEVICE_OK; +} + +void ArduinoCounterCamera::GetName(char* name) const +{ + CDeviceUtils::CopyLimitedString(name, g_DeviceNameArduinoCounterCamera); +} + +int ArduinoCounterCamera::SnapImage() +{ + if (nrCamerasInUse_ < 1) + return ERR_NO_PHYSICAL_CAMERA; + + if (!ImageSizesAreEqual()) + return ERR_NO_EQUAL_SIZE; + + CameraSnapThread t[MAX_NUMBER_PHYSICAL_CAMERAS]; + for (unsigned int i = 0; i < usedCameras_.size(); i++) + { + MM::Camera* camera = (MM::Camera*)GetDevice(usedCameras_[i].c_str()); + if (camera != 0) + { + t[i].SetCamera(camera); + t[i].Start(); + } + } + // I think that the CameraSnapThread destructor waits until the SnapImage function is done + // So, we are likely to be waiting here until all cameras are done snapping + + return DEVICE_OK; +} + +/** + * return the ImageBuffer of the first physical camera + */ +const unsigned char* ArduinoCounterCamera::GetImageBuffer() +{ + if (nrCamerasInUse_ < 1) + return 0; + + return GetImageBuffer(0); +} + +const unsigned char* ArduinoCounterCamera::GetImageBuffer(unsigned channelNr) +{ + // We have a vector of physicalCameras, and a vector of Strings listing the cameras + // we actually use. + int j = -1; + unsigned height = GetImageHeight(); + unsigned width = GetImageWidth(); + unsigned pixDepth = GetImageBytesPerPixel(); + for (unsigned int i = 0; i < usedCameras_.size(); i++) + { + MM::Camera* camera = (MM::Camera*)GetDevice(usedCameras_[i].c_str()); + if (usedCameras_[i] != g_Undefined) + j++; + if (j == (int)channelNr) + { + unsigned thisHeight = camera->GetImageHeight(); + unsigned thisWidth = camera->GetImageWidth(); + if (height == thisHeight && width == thisWidth) + return camera->GetImageBuffer(); + else + { + img_.Resize(width, height, pixDepth); + img_.ResetPixels(); + if (width == thisWidth) + { + memcpy(img_.GetPixelsRW(), camera->GetImageBuffer(), thisHeight * thisWidth * pixDepth); + } + else + { + // we need to copy line by line + const unsigned char* pixels = camera->GetImageBuffer(); + for (unsigned k = 0; k < thisHeight; k++) + { + memcpy(img_.GetPixelsRW() + k * width, pixels + k * thisWidth, thisWidth); + } + } + return img_.GetPixels(); + } + } + } + return 0; +} + +bool ArduinoCounterCamera::IsCapturing() +{ + std::vector::iterator iter; + for (iter = usedCameras_.begin(); iter != usedCameras_.end(); iter++) { + MM::Camera* camera = (MM::Camera*)GetDevice((*iter).c_str()); + if ((camera != 0) && camera->IsCapturing()) + return true; + } + + return false; +} + +/** + * Returns the largest width of cameras used + */ +unsigned ArduinoCounterCamera::GetImageWidth() const +{ + // TODO: should we use cached width? + // If so, when do we cache? + // Since this function is const, we can not cache the width found + unsigned width = 0; + unsigned int j = 0; + while (j < usedCameras_.size()) + { + MM::Camera* camera = (MM::Camera*)GetDevice(usedCameras_[j].c_str()); + if (camera != 0) { + unsigned tmp = camera->GetImageWidth(); + if (tmp > width) + width = tmp; + } + j++; + } + + return width; +} + +/** + * Returns the largest height of cameras used + */ +unsigned ArduinoCounterCamera::GetImageHeight() const +{ + unsigned height = 0; + unsigned int j = 0; + while (j < usedCameras_.size()) + { + MM::Camera* camera = (MM::Camera*)GetDevice(usedCameras_[j].c_str()); + if (camera != 0) + { + unsigned tmp = camera->GetImageHeight(); + if (tmp > height) + height = tmp; + } + j++; + } + + return height; +} + + +/** + * Returns true if image sizes of all available cameras are identical + * false otherwise + * edge case: if we have no or one camera, their sizes are equal + */ +bool ArduinoCounterCamera::ImageSizesAreEqual() { + unsigned height = 0; + unsigned width = 0; + for (unsigned int i = 0; i < usedCameras_.size(); i++) { + MM::Camera* camera = (MM::Camera*)GetDevice(usedCameras_[i].c_str()); + if (camera != 0) + { + height = camera->GetImageHeight(); + width = camera->GetImageWidth(); + } + } + + for (unsigned int i = 0; i < usedCameras_.size(); i++) { + MM::Camera* camera = (MM::Camera*)GetDevice(usedCameras_[i].c_str()); + if (camera != 0) + { + if (height != camera->GetImageHeight()) + return false; + if (width != camera->GetImageWidth()) + return false; + } + } + return true; +} + +unsigned ArduinoCounterCamera::GetImageBytesPerPixel() const +{ + MM::Camera* camera0 = (MM::Camera*)GetDevice(usedCameras_[0].c_str()); + if (camera0 != 0) + { + unsigned bytes = camera0->GetImageBytesPerPixel(); + for (unsigned int i = 1; i < usedCameras_.size(); i++) + { + MM::Camera* camera = (MM::Camera*)GetDevice(usedCameras_[i].c_str()); + if (camera != 0) + if (bytes != camera->GetImageBytesPerPixel()) + return 0; + } + return bytes; + } + return 0; +} + +unsigned ArduinoCounterCamera::GetBitDepth() const +{ + // Return the maximum bit depth found in all channels. + MM::Camera* camera0 = (MM::Camera*)GetDevice(usedCameras_[0].c_str()); + if (camera0 != 0) + { + unsigned bitDepth = 0; + for (unsigned int i = 0; i < usedCameras_.size(); i++) + { + MM::Camera* camera = (MM::Camera*)GetDevice(usedCameras_[i].c_str()); + if (camera != 0) + { + unsigned nextBitDepth = camera->GetBitDepth(); + if (nextBitDepth > bitDepth) + { + bitDepth = nextBitDepth; + } + } + } + return bitDepth; + } + return 0; +} + +long ArduinoCounterCamera::GetImageBufferSize() const +{ + long maxSize = 0; + int unsigned counter = 0; + for (unsigned int i = 0; i < usedCameras_.size(); i++) + { + MM::Camera* camera = (MM::Camera*)GetDevice(usedCameras_[i].c_str()); + if (camera != 0) + { + counter++; + long tmp = camera->GetImageBufferSize(); + if (tmp > maxSize) + maxSize = tmp; + } + } + + return counter * maxSize; +} + +double ArduinoCounterCamera::GetExposure() const +{ + MM::Camera* camera0 = (MM::Camera*)GetDevice(usedCameras_[0].c_str()); + if (camera0 != 0) + { + double exposure = camera0->GetExposure(); + for (unsigned int i = 1; i < usedCameras_.size(); i++) + { + MM::Camera* camera = (MM::Camera*)GetDevice(usedCameras_[i].c_str()); + if (camera != 0) + if (exposure != camera->GetExposure()) + return 0; + } + return exposure; + } + return 0.0; +} + +void ArduinoCounterCamera::SetExposure(double exp) +{ + if (exp > 0.0) + { + for (unsigned int i = 0; i < usedCameras_.size(); i++) + { + MM::Camera* camera = (MM::Camera*)GetDevice(usedCameras_[i].c_str()); + if (camera != 0) + camera->SetExposure(exp); + } + } +} + +int ArduinoCounterCamera::SetROI(unsigned x, unsigned y, unsigned xSize, unsigned ySize) +{ + for (unsigned int i = 0; i < usedCameras_.size(); i++) + { + MM::Camera* camera = (MM::Camera*)GetDevice(usedCameras_[i].c_str()); + // TODO: deal with case when CCD size are not identical + if (camera != 0) + { + int ret = camera->SetROI(x, y, xSize, ySize); + if (ret != DEVICE_OK) + return ret; + } + } + return DEVICE_OK; +} + +int ArduinoCounterCamera::GetROI(unsigned& x, unsigned& y, unsigned& xSize, unsigned& ySize) +{ + MM::Camera* camera0 = (MM::Camera*)GetDevice(usedCameras_[0].c_str()); + // TODO: check if ROI is same on all cameras + if (camera0 != 0) + { + int ret = camera0->GetROI(x, y, xSize, ySize); + if (ret != DEVICE_OK) + return ret; + } + + return DEVICE_OK; +} + +int ArduinoCounterCamera::ClearROI() +{ + for (unsigned int i = 0; i < usedCameras_.size(); i++) + { + MM::Camera* camera = (MM::Camera*)GetDevice(usedCameras_[i].c_str()); + if (camera != 0) + { + int ret = camera->ClearROI(); + if (ret != DEVICE_OK) + return ret; + } + } + + return DEVICE_OK; +} + +int ArduinoCounterCamera::PrepareSequenceAcqusition() +{ + if (nrCamerasInUse_ < 1) + return ERR_NO_PHYSICAL_CAMERA; + + for (unsigned int i = 0; i < usedCameras_.size(); i++) + { + MM::Camera* camera = (MM::Camera*)GetDevice(usedCameras_[i].c_str()); + if (camera != 0) + { + int ret = camera->PrepareSequenceAcqusition(); + if (ret != DEVICE_OK) + return ret; + } + } + + return DEVICE_OK; +} + +int ArduinoCounterCamera::StartSequenceAcquisition(double interval) +{ + if (nrCamerasInUse_ < 1) + return ERR_NO_PHYSICAL_CAMERA; + + if (!ImageSizesAreEqual()) + return ERR_NO_EQUAL_SIZE; + + for (unsigned int i = 0; i < usedCameras_.size(); i++) + { + MM::Camera* camera = (MM::Camera*)GetDevice(usedCameras_[i].c_str()); + if (camera != 0) + { + std::ostringstream os; + os << i; + camera->AddTag(MM::g_Keyword_CameraChannelName, usedCameras_[i].c_str(), + usedCameras_[i].c_str()); + camera->AddTag(MM::g_Keyword_CameraChannelIndex, usedCameras_[i].c_str(), + os.str().c_str()); + + int ret = camera->StartSequenceAcquisition(interval); + if (ret != DEVICE_OK) + return ret; + } + } + return DEVICE_OK; +} + +int ArduinoCounterCamera::StartSequenceAcquisition(long numImages, double interval_ms, bool stopOnOverflow) +{ + if (nrCamerasInUse_ < 1) + return ERR_NO_PHYSICAL_CAMERA; + + int ret = startCounting(numImages); + if (ret != DEVICE_OK) + return ret; + + for (unsigned int i = 0; i < usedCameras_.size(); i++) + { + MM::Camera* camera = (MM::Camera*)GetDevice(usedCameras_[i].c_str()); + if (camera != 0) + { + ret = camera->StartSequenceAcquisition(numImages, interval_ms, stopOnOverflow); + if (ret != DEVICE_OK) + return ret; + } + } + return DEVICE_OK; +} + +int ArduinoCounterCamera::StopSequenceAcquisition() +{ + for (unsigned int i = 0; i < usedCameras_.size(); i++) + { + MM::Camera* camera = (MM::Camera*)GetDevice(usedCameras_[i].c_str()); + if (camera != 0) + { + int ret = camera->StopSequenceAcquisition(); + int ret2 = stopCounting(); + + if (ret != DEVICE_OK) + return ret; + if (ret2 != DEVICE_OK) + return ret2; + + std::ostringstream os; + os << i; + camera->AddTag(MM::g_Keyword_CameraChannelName, usedCameras_[i].c_str(), + ""); + camera->AddTag(MM::g_Keyword_CameraChannelIndex, usedCameras_[i].c_str(), + os.str().c_str()); + } + } + return DEVICE_OK; +} + +int ArduinoCounterCamera::GetBinning() const +{ + MM::Camera* camera0 = (MM::Camera*)GetDevice(usedCameras_[0].c_str()); + int binning = 0; + if (camera0 != 0) + binning = camera0->GetBinning(); + for (unsigned int i = 0; i < usedCameras_.size(); i++) + { + MM::Camera* camera = (MM::Camera*)GetDevice(usedCameras_[i].c_str()); + if (camera != 0) + { + if (binning != camera->GetBinning()) + return 0; + } + } + return binning; +} + +int ArduinoCounterCamera::SetBinning(int bS) +{ + for (unsigned int i = 0; i < usedCameras_.size(); i++) + { + MM::Camera* camera = (MM::Camera*)GetDevice(usedCameras_[i].c_str()); + if (camera != 0) + { + int ret = camera->SetBinning(bS); + if (ret != DEVICE_OK) + return ret; + } + } + return DEVICE_OK; +} + +int ArduinoCounterCamera::IsExposureSequenceable(bool& isSequenceable) const +{ + isSequenceable = false; + + return DEVICE_OK; +} + +unsigned ArduinoCounterCamera::GetNumberOfComponents() const +{ + return 1; +} + +unsigned ArduinoCounterCamera::GetNumberOfChannels() const +{ + return nrCamerasInUse_; +} + +int ArduinoCounterCamera::GetChannelName(unsigned channel, char* name) +{ + CDeviceUtils::CopyLimitedString(name, ""); + int ch = Logical2Physical(channel); + if (ch >= 0 && static_cast(ch) < usedCameras_.size()) + { + CDeviceUtils::CopyLimitedString(name, usedCameras_[ch].c_str()); + } + return DEVICE_OK; +} + +int ArduinoCounterCamera::Logical2Physical(int logical) +{ + int j = -1; + for (unsigned int i = 0; i < usedCameras_.size(); i++) + { + if (usedCameras_[i] != g_Undefined) + j++; + if (j == logical) + return i; + } + return -1; +} + + +int ArduinoCounterCamera::OnPhysicalCamera(MM::PropertyBase* pProp, MM::ActionType eAct, long i) +{ + if (eAct == MM::BeforeGet) + { + pProp->Set(usedCameras_[i].c_str()); + } + + else if (eAct == MM::AfterSet) + { + MM::Camera* camera = (MM::Camera*)GetDevice(usedCameras_[i].c_str()); + if (camera != 0) + { + camera->RemoveTag(MM::g_Keyword_CameraChannelName); + camera->RemoveTag(MM::g_Keyword_CameraChannelIndex); + } + + std::string cameraName; + pProp->Get(cameraName); + + if (cameraName == g_Undefined) { + usedCameras_[i] = g_Undefined; + } + else { + camera = (MM::Camera*)GetDevice(cameraName.c_str()); + if (camera != 0) { + usedCameras_[i] = cameraName; + std::ostringstream os; + os << i; + char myName[MM::MaxStrLength]; + GetLabel(myName); + camera->AddTag(MM::g_Keyword_CameraChannelName, myName, usedCameras_[i].c_str()); + camera->AddTag(MM::g_Keyword_CameraChannelIndex, myName, os.str().c_str()); + } + else + return ERR_INVALID_DEVICE_NAME; + } + nrCamerasInUse_ = 0; + for (unsigned int usedCameraCounter = 0; usedCameraCounter < usedCameras_.size(); usedCameraCounter++) + { + if (usedCameras_[usedCameraCounter] != g_Undefined) + nrCamerasInUse_++; + } + + // TODO: Set allowed binning values correctly + MM::Camera* camera0 = (MM::Camera*)GetDevice(usedCameras_[0].c_str()); + if (camera0 != 0) + { + ClearAllowedValues(MM::g_Keyword_Binning); + int nr = camera0->GetNumberOfPropertyValues(MM::g_Keyword_Binning); + for (int j = 0; j < nr; j++) + { + char value[MM::MaxStrLength]; + camera0->GetPropertyValueAt(MM::g_Keyword_Binning, j, value); + AddAllowedValue(MM::g_Keyword_Binning, value); + } + } + } + + return DEVICE_OK; +} + +int ArduinoCounterCamera::OnBinning(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::BeforeGet) + { + pProp->Set((long)GetBinning()); + } + else if (eAct == MM::AfterSet) + { + long binning; + pProp->Get(binning); + int ret = SetBinning(binning); + if (ret != DEVICE_OK) + return ret; + } + return DEVICE_OK; +} + +int ArduinoCounterCamera::OnPort(MM::PropertyBase* pProp, MM::ActionType pAct) +{ + if (pAct == MM::BeforeGet) + { + pProp->Set(port_.c_str()); + } + else if (pAct == MM::AfterSet) + { + pProp->Get(port_); + portAvailable_ = true; + } + return DEVICE_OK; +} + +int ArduinoCounterCamera::OnLogic(MM::PropertyBase* pProp, MM::ActionType pAct) +{ + int ret = DEVICE_OK; + unsigned char command[2]; + command[0] = 'p'; + std::string answer; + if (pAct == MM::BeforeGet) + { + command[1] = '?'; + ret = WriteToComPort(port_.c_str(), (const unsigned char*)command, 2); + if (ret != DEVICE_OK) + return ret; + ret = GetSerialAnswer(port_.c_str(), "\r\n", answer); + if (ret != DEVICE_OK) + return ret; + if (answer == g_Invert) + invert_ = true; + else if (answer == g_Direct) + invert_ = false; + else + return DEVICE_SERIAL_INVALID_RESPONSE; + } + else if (pAct == MM::AfterSet) + { + std::string cmd; + pProp->Get(cmd); + if (cmd == g_Invert) { + command[1] = 'i'; + ret = WriteToComPort(port_.c_str(), (const unsigned char*)command, 2); + if (ret != DEVICE_OK) + return ret; + ret = GetSerialAnswer(port_.c_str(), "\r\n", answer); + if (ret != DEVICE_OK) + return ret; + if (answer != g_Invert) + return DEVICE_SERIAL_INVALID_RESPONSE; + invert_ = true; + } + else if (cmd == g_Direct) { + command[1] = 'd'; + ret = WriteToComPort(port_.c_str(), (const unsigned char*)command, 2); + if (ret != DEVICE_OK) + return ret; + ret = GetSerialAnswer(port_.c_str(), "\r\n", answer); + if (ret != DEVICE_OK) + return ret; + if (answer != g_Direct) + return DEVICE_SERIAL_INVALID_RESPONSE; + invert_ = false; + } + } + return DEVICE_OK; +} + +int ArduinoCounterCamera::OnVersion(MM::PropertyBase* pProp, MM::ActionType pAct) +{ + if (pAct == MM::BeforeGet) + { + pProp->Set((long)version_); + } + return DEVICE_OK; +} + +int ArduinoCounterCamera::startCommunication() +{ + int ret = DEVICE_OK; + unsigned char command[1]; + command[0] = 'i'; + + ret = WriteToComPort(port_.c_str(), (const unsigned char*) command, 1); + if (ret != DEVICE_OK) + return ret; + + std::string answer; + ret = GetSerialAnswer(port_.c_str(), "\r\n", answer); + if (ret != DEVICE_OK) + return ret; + + std::string boardName = answer.substr(0, 14); + + if (boardName != "ArduinoCounter") + return ERR_BOARD_NOT_FOUND; + + std::string versionString = answer.substr(23, 3); + std::istringstream is(versionString); + is >> version_; + + return ret; +} + +int ArduinoCounterCamera::startCounting(int number) +{ + int ret = DEVICE_OK; + std::ostringstream os; + os << 'g' << number << '\n'; + std::string command = os.str(); + + ret = WriteToComPort(port_.c_str(), (const unsigned char*) command.c_str(), (unsigned int) command.length()); + if (ret != DEVICE_OK) + return ret; + + std::string answer; + ret = GetSerialAnswer(port_.c_str(), "\r\n", answer); + // for now, ignore answer + return ret; +} + +int ArduinoCounterCamera::stopCounting() +{ + unsigned char command[1]; + command[0] = 's'; + int ret = WriteToComPort(port_.c_str(), (const unsigned char*) command, 1); + if (ret != DEVICE_OK) + return ret; + + std::string answer; + ret = GetSerialAnswer(port_.c_str(), "\r\n", answer); + // for now, ignore answer + return ret; +} diff --git a/DeviceAdapters/ArduinoCounter/ArduinoCounter.h b/DeviceAdapters/ArduinoCounter/ArduinoCounter.h new file mode 100644 index 000000000..41f05f022 --- /dev/null +++ b/DeviceAdapters/ArduinoCounter/ArduinoCounter.h @@ -0,0 +1,143 @@ + ////////////////////////////////////////////////////////////////////////////// +// FILE: ArduinoCounter.h +// PROJECT: Micro-Manager +// SUBSYSTEM: DeviceAdapters +//----------------------------------------------------------------------------- +// DESCRIPTION: Adapter for Arduino board with pulse counting firmware +// +// AUTHOR: Nico Stuurman, nstuurman@altoslabs.com 2023/07/10 +// +// + +#ifndef _ArduinoCounter_H_ +#define _ArduinoCounter_H_ + +#include "MMDevice.h" +#include "DeviceBase.h" +#include "ImgBuffer.h" +#include +#include + +////////////////////////////////////////////////////////////////////////////// +// Error codes +// +#define ERR_UNKNOWN_POSITION 101 +#define ERR_INITIALIZE_FAILED 102 +#define ERR_WRITE_FAILED 103 +#define ERR_CLOSE_FAILED 104 +#define ERR_BOARD_NOT_FOUND 105 +#define ERR_PORT_OPEN_FAILED 106 +#define ERR_COMMUNICATION 107 +#define ERR_NO_PORT_SET 108 +#define ERR_VERSION_MISMATCH 109 +#define ERR_INVALID_DEVICE_NAME 10001 +#define ERR_NO_PHYSICAL_CAMERA 10010 +#define ERR_NO_EQUAL_SIZE 10011 +#define ERR_FIRMWARE_VERSION_TOO_NEW 10012 +#define ERR_FIRMWARE_VERSION_TOO_OLD 10013 + +////////////////////////////////////////////////////////////////////////////// +// Max number of physical cameras +// +#define MAX_NUMBER_PHYSICAL_CAMERAS 1 + + + +/** + * CameraSnapThread: helper thread for MultiCamera + */ +class CameraSnapThread : public MMDeviceThreadBase +{ +public: + CameraSnapThread() : + camera_(0), + started_(false) + {} + + ~CameraSnapThread() { if (started_) wait(); } + + void SetCamera(MM::Camera* camera) { camera_ = camera; } + + int svc() { camera_->SnapImage(); return 0; } + + void Start() { activate(); started_ = true; } + +private: + MM::Camera* camera_; + bool started_; +}; + + + +/* + * ArduinoCounter: + */ +class ArduinoCounterCamera : public CCameraBase +{ +public: + ArduinoCounterCamera(); + ~ArduinoCounterCamera(); + + int Initialize(); + int Shutdown(); + + void GetName(char* name) const; + + int SnapImage(); + const unsigned char* GetImageBuffer(); + const unsigned char* GetImageBuffer(unsigned channelNr); + unsigned GetImageWidth() const; + unsigned GetImageHeight() const; + unsigned GetImageBytesPerPixel() const; + unsigned GetBitDepth() const; + long GetImageBufferSize() const; + double GetExposure() const; + void SetExposure(double exp); + int SetROI(unsigned x, unsigned y, unsigned xSize, unsigned ySize); + int GetROI(unsigned& x, unsigned& y, unsigned& xSize, unsigned& ySize); + int ClearROI(); + int PrepareSequenceAcqusition(); + int StartSequenceAcquisition(double interval); + int StartSequenceAcquisition(long numImages, double interval_ms, bool stopOnOverflow); + int StopSequenceAcquisition(); + int GetBinning() const; + int SetBinning(int bS); + int IsExposureSequenceable(bool& isSequenceable) const; + unsigned GetNumberOfComponents() const; + unsigned GetNumberOfChannels() const; + int GetChannelName(unsigned channel, char* name); + bool IsCapturing(); + + // action interface + // --------------- + int OnPhysicalCamera(MM::PropertyBase* pProp, MM::ActionType eAct, long nr); + int OnBinning(MM::PropertyBase* pProp, MM::ActionType eAct); + + // property handlers + int OnPort(MM::PropertyBase* pPropt, MM::ActionType eAct); + int OnLogic(MM::PropertyBase* pPropt, MM::ActionType eAct); + int OnVersion(MM::PropertyBase* pPropt, MM::ActionType eAct); + +private: + int Logical2Physical(int logical); + bool ImageSizesAreEqual(); + unsigned char* imageBuffer_; + int startCommunication(); + int startCounting(int number); + int stopCounting(); + + std::vector availableCameras_; + std::vector usedCameras_; + std::vector cameraWidths_; + std::vector cameraHeights_; + unsigned int nrCamerasInUse_; + bool initialized_; + ImgBuffer img_; + std::string port_; + double version_; + bool portAvailable_; + bool invert_; +}; + + +#endif //_ArduinoCounter_H_ \ No newline at end of file diff --git a/DeviceAdapters/ArduinoCounter/ArduinoCounter.vcxproj b/DeviceAdapters/ArduinoCounter/ArduinoCounter.vcxproj new file mode 100644 index 000000000..7b7884e23 --- /dev/null +++ b/DeviceAdapters/ArduinoCounter/ArduinoCounter.vcxproj @@ -0,0 +1,100 @@ + + + + + Debug + x64 + + + Release + x64 + + + + {067D4A8-8C32-40D3-BA9F-6B388ECCD714} + Arduino + 10.0 + + + + DynamicLibrary + MultiByte + true + v142 + false + + + DynamicLibrary + MultiByte + v142 + true + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.40219.1 + true + false + + + + X64 + + + Disabled + %(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;_USRDLL;MODULE_EXPORTS;%(PreprocessorDefinitions) + EnableFastChecks + 4290;%(DisableSpecificWarnings) + + + %(AdditionalLibraryDirectories) + Windows + + + + + X64 + + + MaxSpeed + true + %(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;_USRDLL;MODULE_EXPORTS;%(PreprocessorDefinitions) + true + 4290;%(DisableSpecificWarnings) + + + %(AdditionalLibraryDirectories) + Windows + true + true + + + + + {b8c95f39-54bf-40a9-807b-598df2821d55} + + + + + + + + + + + + \ No newline at end of file diff --git a/DeviceAdapters/ArduinoCounter/ArduinoCounter.vcxproj.filters b/DeviceAdapters/ArduinoCounter/ArduinoCounter.vcxproj.filters new file mode 100644 index 000000000..30219ea7e --- /dev/null +++ b/DeviceAdapters/ArduinoCounter/ArduinoCounter.vcxproj.filters @@ -0,0 +1,27 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav + + + + + Header Files + + + + + Source Files + + + \ No newline at end of file diff --git a/DeviceAdapters/ArduinoCounter/ArduinoCounter/ArduinoCounter.ino b/DeviceAdapters/ArduinoCounter/ArduinoCounter/ArduinoCounter.ino new file mode 100644 index 000000000..2141a03e9 --- /dev/null +++ b/DeviceAdapters/ArduinoCounter/ArduinoCounter/ArduinoCounter.ino @@ -0,0 +1,144 @@ +// Arduino firmware that counts input pulses, relays these to +// the output, but stops doing so after a specified +// amount of pulses has been received. +// Since version 2, also can invert the output. +// Communicates through serial, commands: +// gnnn - starts pulse counting. nnn should be an +// integer number (of any size) +// s - Stops counting, and reverts to transducing +// all input to the output +// i - Info: returns identification string +// pn - Sets/Reads the flag inverting the input polarity +// n=i Invert the input to the output +// n=d Do not invert the input to the output +// n=? report output polarity + + + +unsigned int version_ = 2; + +// pin on which to receive the trigger (2 and 3 can be used with interrupts, although this code does not use interrupts) +const int inPin = 2; + + +const int outPin = 8; + +const unsigned long timeOut = 1000; + +unsigned int counter = 0; +unsigned int limit; +boolean counting = false; +boolean inputWas; +boolean invert = false; + +void setup() { + // put your setup code here, to run once: + Serial.begin(115200); + + pinMode(inPin, INPUT); + pinMode(outPin, OUTPUT); + + inputWas = digitalRead(inPin); +} + +void loop() { + while (true) { + // put your main code here, to run repeatedly: + + if (Serial.available() > 0) { + int inByte = Serial.read(); + switch (inByte) { + + // go (i.e. start) 'g' followed by max number of TTLs to pass + case 103: + if (waitForSerial(timeOut)) { + limit = Serial.parseInt(); + Serial.write("Starting with "); + Serial.println(limit, DEC); + // set limit here + counting = true; + counter = 0; + inputWas = digitalRead(inPin); + break; + } + + // stop 's'; i.e. operate in passthrough mode + case 115: + counting = false; + Serial.println("Stopping"); + break; + + // get info 'i'; what version are you? + case 105: + Serial.println("ArduinoCounter version 2.0"); + break; + + // Sets/gets output polarity + case 112: + if (waitForSerial(timeOut)) { + int command = Serial.read(); + switch (command) { + case 105: // "i" + invert = true; + Serial.println("Invert"); + break; + + case 100: // "d" + invert = false; + Serial.println("Direct"); + break; + + case 63: // "?" + if (invert) { + Serial.println("Invert"); + } else { + Serial.println("Direct"); + } + break; + } + } + } + } + + + if (counting) { + if (invert) { + if (inputWas && !(PIND & B00000100)) { + inputWas = LOW; + PORTB = 1; + } else if (!inputWas && (PIND & B00000100)) { + counter++; + inputWas = HIGH; + if (counter <= limit) { + PORTB = 0; + } + } + } else { // do not invert output + if (inputWas && !(PIND & B00000100)) { + counter++; + inputWas = LOW; + if (counter <= limit) { + PORTB = 0; + } + } else if (!inputWas && (PIND & B00000100)) { + inputWas = HIGH; + PORTB = 1; + } + } + } else { // not counting + if (invert) { + PORTB = !(PIND & B00000100); + } else { + PORTB = (PIND & B00000100) >> 2; + } + } + } +} + +bool waitForSerial(unsigned long timeOut) { + unsigned long startTime = millis(); + while (Serial.available() == 0 && (millis() - startTime < timeOut)) {} + if (Serial.available() > 0) + return true; + return false; +} diff --git a/DeviceAdapters/ArduinoCounter/Makefile.am b/DeviceAdapters/ArduinoCounter/Makefile.am new file mode 100644 index 000000000..94706190f --- /dev/null +++ b/DeviceAdapters/ArduinoCounter/Makefile.am @@ -0,0 +1,7 @@ + +AM_CXXFLAGS = $(MMDEVAPI_CXXFLAGS) +deviceadapter_LTLIBRARIES = libmmgr_dal_ArduinoCounter.la +libmmgr_dal_ArduinoCounter_la_SOURCES = ArduinoCounter.cpp ArduinoCounter.h \ + ../../MMDevice/MMDevice.h ../../MMDevice/DeviceBase.h +libmmgr_dal_ArduinoCounter_la_LIBADD = $(MMDEVAPI_LIBADD) +libmmgr_dal_ArduinoCounter_la_LDFLAGS = $(MMDEVAPI_LDFLAGS) diff --git a/DeviceAdapters/ArduinoCounter/license.txt b/DeviceAdapters/ArduinoCounter/license.txt new file mode 100644 index 000000000..52b96bc66 --- /dev/null +++ b/DeviceAdapters/ArduinoCounter/license.txt @@ -0,0 +1,142 @@ +GNU Lesser General Public License + +Preamble + +The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. + +This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. + +When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. + +To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. + +For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. + +We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. + +To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. + +Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. + +Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. + +When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. + +We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. + +For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. + +In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. + +Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. + +The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. + + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". + +A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. + +The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) + +"Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. + +Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. + +1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. + +You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. + + (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) + + These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. + + Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. + + In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. + +3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. + +Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. + +This option is useful when you wish to copy part of the code of the Library into a program that is not a library. + +4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. + +If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. + +5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. + +However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. + +When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. + +If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) + +Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. + +6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. + +You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: + + a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. + + e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. + +For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. + +It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. + +7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. + + b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. + +8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. + +9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. + +10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. + +11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. + +This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. + +12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. + +13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. + +14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. + +NO WARRANTY + +15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +END OF TERMS AND CONDITIONS diff --git a/DeviceAdapters/BlueboxOptics_niji/niji.cpp b/DeviceAdapters/BlueboxOptics_niji/niji.cpp index 91b782b5e..700998689 100644 --- a/DeviceAdapters/BlueboxOptics_niji/niji.cpp +++ b/DeviceAdapters/BlueboxOptics_niji/niji.cpp @@ -29,8 +29,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" - #include "MMDevice.h" #include "ModuleInterface.h" diff --git a/DeviceAdapters/CARVII/CARVII.cpp b/DeviceAdapters/CARVII/CARVII.cpp index 4de34e914..baf5d1254 100644 --- a/DeviceAdapters/CARVII/CARVII.cpp +++ b/DeviceAdapters/CARVII/CARVII.cpp @@ -24,7 +24,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "CARVII.h" #include "CARVIIHub.h" diff --git a/DeviceAdapters/CNCMicroscope/ArduinoNeoPixelShutter/ArduinoNeoPixelShutter.cpp b/DeviceAdapters/CNCMicroscope/ArduinoNeoPixelShutter/ArduinoNeoPixelShutter.cpp index d880e6d99..76b40e2cd 100644 --- a/DeviceAdapters/CNCMicroscope/ArduinoNeoPixelShutter/ArduinoNeoPixelShutter.cpp +++ b/DeviceAdapters/CNCMicroscope/ArduinoNeoPixelShutter/ArduinoNeoPixelShutter.cpp @@ -25,7 +25,6 @@ limitations under the License. #define WIN32_LEAN_AND_MEAN #include #endif -#include "FixSnprintf.h" const char* g_DeviceNameArduinoNeoPixelHub = "ArduinoNeoPixel-Hub"; const char* g_DeviceNameArduinoNeoPixelShutter = "ArduinoNeoPixel-Shutter"; diff --git a/DeviceAdapters/CSUW1/CSUW1.cpp b/DeviceAdapters/CSUW1/CSUW1.cpp index 653e4540f..7c72d929f 100644 --- a/DeviceAdapters/CSUW1/CSUW1.cpp +++ b/DeviceAdapters/CSUW1/CSUW1.cpp @@ -34,7 +34,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "CSUW1.h" #include "CSUW1Hub.h" diff --git a/DeviceAdapters/ChuoSeiki_MD5000/ChuoSeiki_MD_XYZ.cpp b/DeviceAdapters/ChuoSeiki_MD5000/ChuoSeiki_MD_XYZ.cpp index 12daf7bc5..5968dc0c0 100644 --- a/DeviceAdapters/ChuoSeiki_MD5000/ChuoSeiki_MD_XYZ.cpp +++ b/DeviceAdapters/ChuoSeiki_MD5000/ChuoSeiki_MD_XYZ.cpp @@ -30,7 +30,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "ChuoSeiki_MD_XYZ.h" diff --git a/DeviceAdapters/ChuoSeiki_QT/ChuoSeiki_QT_XYZ.cpp b/DeviceAdapters/ChuoSeiki_QT/ChuoSeiki_QT_XYZ.cpp index 19b02f2ef..826d0350e 100644 --- a/DeviceAdapters/ChuoSeiki_QT/ChuoSeiki_QT_XYZ.cpp +++ b/DeviceAdapters/ChuoSeiki_QT/ChuoSeiki_QT_XYZ.cpp @@ -34,7 +34,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "ChuoSeiki_QT_XYZ.h" #include "ModuleInterface.h" diff --git a/DeviceAdapters/CoherentCube/CoherentCube.cpp b/DeviceAdapters/CoherentCube/CoherentCube.cpp index 0862d650f..bb1536e34 100644 --- a/DeviceAdapters/CoherentCube/CoherentCube.cpp +++ b/DeviceAdapters/CoherentCube/CoherentCube.cpp @@ -22,13 +22,9 @@ // CVS: // - - #ifdef WIN32 #include #endif -#include "FixSnprintf.h" - #include "MMDevice.h" #include "CoherentCube.h" diff --git a/DeviceAdapters/CoherentOBIS/CoherentOBIS.cpp b/DeviceAdapters/CoherentOBIS/CoherentOBIS.cpp index 8fbd898dd..7c3353de1 100644 --- a/DeviceAdapters/CoherentOBIS/CoherentOBIS.cpp +++ b/DeviceAdapters/CoherentOBIS/CoherentOBIS.cpp @@ -25,7 +25,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "CoherentOBIS.h" diff --git a/DeviceAdapters/CoherentScientificRemote/CoherentScientificRemote.cpp b/DeviceAdapters/CoherentScientificRemote/CoherentScientificRemote.cpp index ed775bebb..d52f52fce 100644 --- a/DeviceAdapters/CoherentScientificRemote/CoherentScientificRemote.cpp +++ b/DeviceAdapters/CoherentScientificRemote/CoherentScientificRemote.cpp @@ -26,7 +26,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "CoherentScientificRemote.h" diff --git a/DeviceAdapters/Conix/Conix.cpp b/DeviceAdapters/Conix/Conix.cpp index f95896825..bd7469c57 100644 --- a/DeviceAdapters/Conix/Conix.cpp +++ b/DeviceAdapters/Conix/Conix.cpp @@ -15,7 +15,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "Conix.h" #include diff --git a/DeviceAdapters/CoolLEDpE300/pE300.cpp b/DeviceAdapters/CoolLEDpE300/pE300.cpp index 8ecdd2665..af10e36a3 100644 --- a/DeviceAdapters/CoolLEDpE300/pE300.cpp +++ b/DeviceAdapters/CoolLEDpE300/pE300.cpp @@ -21,8 +21,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" - #include "MMDevice.h" #include "pE300.h" diff --git a/DeviceAdapters/CoolLEDpE4000/pE4000.cpp b/DeviceAdapters/CoolLEDpE4000/pE4000.cpp index 7c40b1957..ec54823bb 100644 --- a/DeviceAdapters/CoolLEDpE4000/pE4000.cpp +++ b/DeviceAdapters/CoolLEDpE4000/pE4000.cpp @@ -21,8 +21,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" - #include "MMDevice.h" #include "pE4000.h" diff --git a/DeviceAdapters/Corvus/Corvus.cpp b/DeviceAdapters/Corvus/Corvus.cpp index 29f7f4311..4068cac23 100644 --- a/DeviceAdapters/Corvus/Corvus.cpp +++ b/DeviceAdapters/Corvus/Corvus.cpp @@ -37,8 +37,6 @@ // "st" gives 8bit value. bit 0 tells if ready to execute; busy if "1" -#include "FixSnprintf.h" - #include #include #include diff --git a/DeviceAdapters/DTOpenLayer/DTOpenLayer.cpp b/DeviceAdapters/DTOpenLayer/DTOpenLayer.cpp index 1f1201ac3..6fc4adbda 100644 --- a/DeviceAdapters/DTOpenLayer/DTOpenLayer.cpp +++ b/DeviceAdapters/DTOpenLayer/DTOpenLayer.cpp @@ -24,7 +24,6 @@ #define WIN32_LEAN_AND_MEAN #include #endif -#include "FixSnprintf.h" #include "DTOpenLayer.h" #include "ModuleInterface.h" diff --git a/DeviceAdapters/Diskovery/Diskovery.cpp b/DeviceAdapters/Diskovery/Diskovery.cpp index 828b66e38..1d6fe660e 100644 --- a/DeviceAdapters/Diskovery/Diskovery.cpp +++ b/DeviceAdapters/Diskovery/Diskovery.cpp @@ -25,7 +25,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "Diskovery.h" #include diff --git a/DeviceAdapters/Diskovery/DiskoveryModel.cpp b/DeviceAdapters/Diskovery/DiskoveryModel.cpp index 005e0d74d..bd1048ccb 100644 --- a/DeviceAdapters/Diskovery/DiskoveryModel.cpp +++ b/DeviceAdapters/Diskovery/DiskoveryModel.cpp @@ -21,12 +21,9 @@ // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES. - #ifdef WIN32 #include #endif -#include "FixSnprintf.h" - #include "DiskoveryModel.h" #include "Diskovery.h" diff --git a/DeviceAdapters/ESP32/ESP32.vcxproj b/DeviceAdapters/ESP32/ESP32.vcxproj new file mode 100644 index 000000000..cfbd67cdd --- /dev/null +++ b/DeviceAdapters/ESP32/ESP32.vcxproj @@ -0,0 +1,94 @@ + + + + + Debug + x64 + + + Release + x64 + + + + + + + + + + + {b8c95f39-54bf-40a9-807b-598df2821d55} + + + + 16.0 + Win32Proj + {0c72846a-bcfa-4f75-91af-d9f5ca01e6b2} + ESP32 + 10.0 + + + + DynamicLibrary + true + v142 + Unicode + + + DynamicLibrary + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + true + _DEBUG;ESP32_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + pch.h + + + Windows + true + false + + + + + true + true + true + NDEBUG;ESP32_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + pch.h + + + Windows + true + true + true + false + + + + + + \ No newline at end of file diff --git a/DeviceAdapters/ESP32/ESP32.vcxproj.filters b/DeviceAdapters/ESP32/ESP32.vcxproj.filters new file mode 100644 index 000000000..4987ad61a --- /dev/null +++ b/DeviceAdapters/ESP32/ESP32.vcxproj.filters @@ -0,0 +1,27 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + + + Source Files + + + \ No newline at end of file diff --git a/DeviceAdapters/ESP32/ESP32_Firmware/ESP32_COM_BT_WiFi/ESP32_COM_BT_WiFi.ino b/DeviceAdapters/ESP32/ESP32_Firmware/ESP32_COM_BT_WiFi/ESP32_COM_BT_WiFi.ino new file mode 100644 index 000000000..054b2ac93 --- /dev/null +++ b/DeviceAdapters/ESP32/ESP32_Firmware/ESP32_COM_BT_WiFi/ESP32_COM_BT_WiFi.ino @@ -0,0 +1,832 @@ +/* +This Arduino Sketch is based on the one written by +// Bonno Meddens, 30/07/2019 +For 32-Bit microcontroller boards. + +This Version Written by: +Dr Justin E. Molloy +Professor of Biophysics +Centre for Mechanochemical Cell Biology (L1.01) +Warwick Medical School +Coventry CV4 7AL +M: +44 (0) 7986143550 +E: justin.molloy@warwick.ac.uk + +The code interfaces with MicroManager Adapter called mmgr_dal_ESP32.dll +The C++ code for the MM adapter is also deposited on GitHUb. +ESP32.Cpp & ESP32.h + +This arduino sketch is highly modified and specifically for ESP32 series microcontrollers. + +If you want to use Bluetooth, Wifi, Serial etc.. you will need to allocate more memory on your ESP Chip. + +To compile this program for NodeMCU32s using Arduino IDE modify the memory allocation file: +c:\Users\username\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.9\tools\partitions\default.csv +You must increase App0 to at least 0x190000 +(Reduce App1 by same amnount and change memory offset start addresses) +Note: App1 memory is usually reserved for "Over the Air" updates... we don't need that! +As below: +# Name Type SubType Offset Size Flags +nvs data nvs 0x9000 0x5000 +otadata data ota 0xe000 0x2000 +app0 app ota_0 0x10000 0x190000 +app1 app ota_1 0x1A0000 0x0F0000 +spiffs data spiffs 0x290000 0x160000 +coredump data coredump 0x3F0000 0x10000 +==================================================================== + +And ..... +c:\Users\username\AppData\Local\Arduino15\packages\hardware\esp\1.0.6\boards.txt +search for....nodemcu and change "maximum_size=1638400" (=0x190000) +nodemcu-32s.upload.tool=esptool_py +nodemcu-32s.upload.maximum_size=1638400 +nodemcu-32s.upload.maximum_data_size=327680 +nodemcu-32s.upload.wait_for_upload_port=true + +// Notes on the Bonno Meddens' original Arduino code: +////////////////////////////////////////////////////// +The old MM-Device Adaptor code: "ARDUINO32bitBoards.cpp" +Used the following commands: +Command Number = Associated Method/Class + 1 = CArduino32Shutter::WriteToPort(long value) + 1 = CArduino32Switch::WriteToPort (long value) + 3 = CArduino32DA::WriteToPort(unsigned long value) + 5&6 = CArduino32Switch::LoadSequence(unsigned size, unsigned char* seq) + 8&9 = CArduino32Switch::OnState(MM::PropertyBase* pProp, MM::ActionType eAct) + 11 = CArduino32Switch::OnRepeatTimedPattern(MM::PropertyBase* pProp, MM::ActionType eAct) + 12&9 = CArduino32Switch::OnState(MM::PropertyBase* pProp, MM::ActionType eAct) + 20&21= CArduino32Switch::OnBlanking(MM::PropertyBase* pProp, MM::ActionType eAct) + 22 = CArduino32Switch::OnBlankingTriggerDirection(MM::PropertyBase* pProp, MM::ActionType eAct) + 30&31 = CArduino32Hub::GetControllerVersion(int& version) + 40 = CArduino32Input::GetDigitalInput(long* state) + 4 = CArduino32Input::OnAnalogInput(MM::PropertyBase* pProp, MM::ActionType eAct, long channel )***check*** + 42 = CArduino32Input::SetPullUp(int pin, int state) + +These old codes seemed to be unused: +2 - Query digitial outputs +7 - Skip triggers +10 - Time intervals for trigger mode +13-19 - Not defined +23-29, 32-39, 41, 43-> + +All the old commands are now replaced with ASCII command and value format: +================================================================= + Comma-delimited commands and parameters + Example command format: "P,7,123" + gives: triggerPattern[7]= 0111 1011 Binary +================================================================= +ESP32 Board pin-out.... +========================================================================================== + NodeMCU ESP32S + 3.3V o- -o GND + Reset o- -o GPIO23 * SDA - to OLED display + ADC-5 GPIO36 o- -o GPIO22 * SCL - to OLED display + ADC-4 GPIO39 o- -o Int + ADC-3 GPIO34 o- -o Int + ADC-2 GPIO35 o- -o GPIO21 + ADC-1 GPIO32 o- -o GND + ADC-0 GPIO33 o- -o GPIO19 *Shutter - to FET driver 1A max. SHUTTER + X OpAmp+LP filter *ch5 GPIO25 o- -o GPIO18 *ch3 - to FET LED driver 1A max. Blue + Y OpAmp+LP filter *ch6 GPIO26 o- -o GPIO5 *ch2 - to FET LED driver 1A max. Green + Z OpAmp+LP filter *ch7 GPIO27 o- -o GPIO17 *ch1 - to FET LED driver 1A max. Yellow + GPIO14 o- -o GPIO16 *ch0 - to FET LED driver 1A max. Red + GPIO12 o- -o GPIO4 + GND o- -o GPIO0 {O/P only - affects boot} + GPIO13 o- -o GPIO2 + Int 09 o- -o GPIO15 *inPin <= blanking deglitch with 0.1uF cap. to GND + Int 10 o- -o Int GPIO8 + Int 11 o- -o Int GPIO7 + 5V o- | | -o Int GPIO6 + |___| + USB skt +========================================================================================== +Note 8-bit res on GPIO25/26 is pretty poor so maybe set them to PWM mode at 14 bit res, (clk ~10kHz) +Need to low-pass filter to about 10Hz to remove ripple - which would be fine for most X-Y stages +Better still, use GPIO 25,26,27 as chip select lines for SPI comms to 16 or 20bit DACS. +*/ + +// BLUETOOTH +// Mobile APP for communicating with microcontrollers +// Available at: WWW.KEUWL.COM +// You can set up sliders, buttons, pad, dials etc +#include "BluetoothSerial.h" +#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED) +#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it +#endif +BluetoothSerial SerialBT; + +// WiFi Stuff +#include +// create a server and listen on port 8088 +WiFiServer server(8088); + +//local login credentials at work +//const char* ssid = "WorkRouter"; +//const char* password = "AnotherPASSWORD"; + +// local login credentials at home +const char* ssid = "HOMERouter"; +const char* password = "PASSWORD"; + +// Stuff for the I2C OLED display +//#include +#include +#define SCREEN_WIDTH 128 // OLED display width, in pixels +#define SCREEN_HEIGHT 64 // OLED display height, in pixels +#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin) +#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32 +Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); +// define GPIO pins for I2C communications +#define I2C_SDA 23 +#define I2C_SCL 22 +int linemax = 8; +#define dummy " " +// initialise nlines string values..for pointer to index! +String txtlines[] = { dummy, dummy, dummy, dummy, dummy, dummy, dummy, dummy }; + +String MMresponse = "MM-ESP32"; // Expected response from Arduino +unsigned int version = 1; // This is Version 1 <= update as necessary to allow checking by MM + +const int SEQUENCELENGTH = 12; // This should be good enough + +// Each byte in triggerPattern[] array codes the output blanking pattern for upto 5 - channels +// Each entry is bitmapped as a binary mask to turn output pins off (="0") or at Vout value (="1"). +// bits 7,6,5 are reserved... bit4=shutter, bit3=blue, bit2=green, bit1=yellow, bit0=red. +// Initially all outputs are active for every timed sequence position e.g. all "r r r 1 1 1 1 1 BIN" = 255 DEC +byte triggerPattern[SEQUENCELENGTH] = { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }; + +//Delay times in milliseconds for each sequence output +unsigned int triggerDelay[SEQUENCELENGTH] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +//We can also "sequence" the x,y,z stage positions +//Here, we store the x,y,z positions +unsigned int xPosn[SEQUENCELENGTH] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +unsigned int yPosn[SEQUENCELENGTH] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +unsigned int zPosn[SEQUENCELENGTH] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +int patternLength = 0; // Number of pattern slots that are currently in use +byte repeatPattern = 0; // Number of repeat cycles + +volatile long triggerNr; // # of triggers can start at negative value = "-skipTriggers" +volatile long sequenceNr; // current sequence number as we cycle through sequence pattern (0->patternLength) + +int skipTriggers = 0; // # of triggers to skip before starting to generate patterns (triggerNr is set to "-skipTriggers") +byte currentPattern = 255; // A global variable that keeps track of channel output logic + +bool blanking = false; +bool blankOnHigh = false; +bool triggerMode = false; +bool triggerState = false; + +// Some thoughts on using PWM for Stage control and LED control: +// NodeMCU 32S PWM clock (APB clk) runs at 80MHz (regardless of CPU) +// Max possible PWM freq = 80,000,000/(2^nbits) +// +// "bit noise" resolution Max. PWM freq +// Amax*0.015e-3 16 bit ~ 1.2 kHz (we use 1kHz) +// Amax*0.03e-3 15 bit ~ 2.4 kHz (we use 2kHz) +// Amax*0.06e-3 14 bit ~ 4.88 kHz (we use 4kHz) +// Amax*0.24e-3 12 bit ~ 19.5 kHz (we use 16kHz) +// Amax*0.98e-3 10 bit ~ 78.1 kHz (we use 50kHz) +// +// If Amax is 200um range on a piezo stage multiply by 200,000 to get nm/bit +// Now, if you use a single-pole R-C filter on the PWM output the +// PWM ripple equivalent will be very approx 200,000 * (fc/PWMfreq) nm "peak-peak" +// +// e.g. at 12-bit PWM resolution: +// The smallest stepsize = 0.24e-3*200,000 = 48nm/bit +// And using a 15 kOhm + 1 uF "R-C" filter = fc~10Hz (100ms response time). +// PWM ripple noise = (10 Hz/16,000 Hz) * 200,000 = 125nm P-P YUK!!!! +// RESULT, PWM is fine for testing but use 20-bit DACs for real applications +// +// However, 10-bit PWM control for LEDs is fine because +// if your max frame rate is 100Hz (10ms/frame) and the PWM +// is asynchronous with the camera frames you will get 500 LED flashes +/-1 +// on each frame so "flicker" (1/500) is trivial. +// +// chan 0 1 2 3 +const uint8_t DACpin[] = { 16, 17, 5, 18 }; // GPIO Pins (OUTPUT) +const uint32_t PWMfreq[] = { 50, 50, 50, 50 }; // PWM freq in kHz +const uint32_t PWMres[] = { 10, 10, 10, 10 }; // PWM resolution in bits +unsigned int Vout[] = { 0, 0, 0, 0 }; // LED output values +const int nDAC = 4; // 4 DAC channels + +// Stage Position control +const uint8_t xPin = 25; // GPIO Pin (OUTPUT) +const uint32_t xPWMfreq = 4; // PWM freq in kHz +const uint32_t xPWMres = 14; // PWM resolution in bits +unsigned int xOut = 0; // xyz Stage output values +const int xChan = 10; // PWM output channel number +int xRange = 200; // xRange (um) + +const uint8_t yPin = 26; // GPIO Pin (OUTPUT) +const uint32_t yPWMfreq = 4; // PWM freq in kHz +const uint32_t yPWMres = 14; // PWM resolution in bits +unsigned int yOut = 0; // xyz Stage output values +const int yChan = 11; // PWM output channel number +int yRange = 200; // yRange (um) + +const uint8_t zPin = 27; // GPIO Pin (OUTPUT) +const uint32_t zPWMfreq = 4; // PWM freq in kHz +const uint32_t zPWMres = 14; // PWM resolution in bits +unsigned int zOut = 0; // xyz Stage output values +const int zChan = 12; // PWM output channel number +int zRange = 100; // zRange (um) + +// Analogue input GPIO pins on the ESP32 NODEMCU +// 0 1 2 3 4 5 +const uint8_t ADCpin[] = { 33, 32, 35, 34, 39, 36 }; // set GPIO Pins (INPUT) +const int nADC = 6; // 6 ADC channels + +//"Logical" pin for blanking (I/P) and harware shutter (O/P) +const uint8_t shutterPin = 19; // set GPIO Pin for Shutter control (O/P) +const uint8_t inPin = 15; // set GPIO Pin for trigger/blanking (I/P) + +// Create some global variable so we can see the commands + params everywhere +// This saves passing values back-and-forth to functions all the time! +int const maxVal=4; +int const maxBytes=50; +char command = 0; +char inBuf[maxBytes]; +char outBuf[maxBytes]; +float val[maxVal]; +int ct=0; + +//============================= Setup =============================== +void setup() { + +Serial.begin(115200); //Baud rate + + Wire.begin(I2C_SDA, I2C_SCL); +// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally + display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS); + +// Clear the buffer + display.clearDisplay(); + display.setTextSize(1); // Normal 1:1 pixel scale + display.setTextColor(SSD1306_WHITE, SSD1306_BLACK); // Draw white text, Black background + +// Set inPin for input (pullup=normally high) + pinMode(inPin, INPUT_PULLUP); + // Set Shutter for output + pinMode(shutterPin, OUTPUT); + +// Configure the PWM ouptut channels + ledcSetup(xChan, xPWMfreq*1000, xPWMres); + ledcSetup(yChan, yPWMfreq*1000, yPWMres); + ledcSetup(zChan, zPWMfreq*1000, zPWMres); +// Assign the correct GPIO pins to the channels + +// xChan, yChan, zChan are RESERVED 10, 11, 12 + ledcAttachPin(xPin, xChan); + ledcAttachPin(yPin, yChan); + ledcAttachPin(zPin, zChan); + +// PWM setup - ESP32 has up to 16 independent PWM channels +// DACPWM channels 0->nDac (where nDAC<10 - see x,y,z chans above) + for (uint8_t i = 0; i < nDAC; i++) ledcSetup(i, PWMfreq[i]*1000, PWMres[i]); + for (uint8_t i = 0; i < nDAC; i++) ledcAttachPin(DACpin[i], i); + + String outStr = "Running:" + MMresponse + "-v" + String(version); + writeln(outStr); + + // clear the RS232 serial buffer + Serial.flush(); + +// Start Bluetooth + SerialBT.begin("Node_ESP32s"); //Bluetooth device name + writeln("Bluetooth=Node_ESP32s"); + +// connect to WiFi + outStr = "Connect:" + String(ssid); + writeln(outStr); + + WiFi.begin(ssid, password); + delay(100); + + boolean connected= false; + int ct = 0; +// wait until ESP32 connected + while ((WiFi.status() != WL_CONNECTED) && (ct<20)) { + delay(100); + ct+=1; + } + + if (ct<20) connected = true; + + IPAddress ip = WiFi.localIP(); + char buf[16]; + sprintf(buf, "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]); + outStr = "IP:" + String(buf); + writeln(outStr); + +// start Server + if (connected) server.begin(); + +} +//========================= Setup ENDS =========================== + +//=========== +void loop() { +//=========== +// Sit in this main loop and wait for incoming commands over RS232 (USB) + + WiFiClient client = server.available(); + if (client) { + while (client.connected()) { + +// check for WiFi input + if (client.available() > 0) { + delay(5); // wait for WiFi buffer to fill + int nbytes = client.available(); + if (nbytes > maxBytes) nbytes = maxBytes; + int len = client.readBytesUntil('\n', inBuf, nbytes); + inBuf[len]=0; + String str = String(inBuf); + splitStr(str); + // we now have the command character and the parameters val[0]->val[3] + bool respond = doCommand(); + // We MUST send the outBuf if respond is true + if (respond) { + client.print(outBuf); + } + if (command != 'L') writeln(String(outBuf)); + command = 0; + } + + if (Serial.available() > 0) doRS232(); // check for RS232 input + + if (SerialBT.available() > 0) doBlueTooth(); // check for bluetooth + +// check for trigger or blanking during Video recording + checkTrig(); + } // loop while WiFI client connected + } // WiFi client no longer connected... + + if (Serial.available() > 0) doRS232(); // check for RS232 input + + if (SerialBT.available() > 0) doBlueTooth(); // check for bluetooth + + checkTrig(); + +// loop and try for new WiFi client +} + +//========================= Main Loop ENDS ========================= +//============== +void doRS232() { +//============== +// new RS232 data available + delay(50); + int nbytes = Serial.available(); + if (nbytes > maxBytes) nbytes = maxBytes; + int len = Serial.readBytesUntil('\n', inBuf, nbytes); + inBuf[len]=0; + String str = String(inBuf); + splitStr(str); + bool respond = doCommand(); + // We MUST send the outBuf if respond is true + if (respond){ + Serial.print(outBuf); + } + if (command != 'L') writeln(String(outBuf)); + command=0; +} +//================== +void doBlueTooth() { +//================== +// new Bluetooth data available + delay(50); //wait for buffer to fill + int nbytes = SerialBT.available(); + if (nbytes > maxBytes) nbytes = maxBytes; + int len = SerialBT.readBytesUntil('\n', inBuf, nbytes); + inBuf[len]=0; + String str = String(inBuf); + splitStr(str); + bool respond = doCommand(); + // We MUST send the outBuf if respond is true. + if (respond){ + //Serial.print(outBuf); + } + if (command != 'L') writeln(String(outBuf)); + command=0; +} + +//================ +void checkTrig() { +//================ +// On eack loop check for a trigger signal +// In trigger mode, we will blank even if blanking is not on.. +// Only do this on first transition of the trigger signal + if (triggerMode) { + bool trigSig = digitalRead(inPin); + if ((triggerState != trigSig) && (blankOnHigh == trigSig)) { // first trigger - turn everything off + writeZeros(); + triggerState = trigSig; + } else if (blankOnHigh == trigSig) { // subsequent triggers + if (triggerNr >= 0) { // TRUE when we have skipped "n" pre-triggers + writePattern(triggerPattern[sequenceNr]); // move to next pattern + sequenceNr = (sequenceNr++) % patternLength; // modulus wrap around + } + triggerNr++; // tally number of triggers + } + } else if (blanking) { // blanking mode?? + if ((blankOnHigh == digitalRead(inPin))) writePattern(currentPattern); + else writeZeros(); + } + } + +//========================= +void splitStr(String str) { +//========================= +// This is called when a string has been received from RS232, TelNet or Bluetooth +// We leave the function having updated: +// "command" string (usually just one character) and variables "val[0], val[1], val[2]... +// writeln("Recd:" +str); + str.trim(); + int startIndex = 0; + int commaIndex = str.indexOf(',', startIndex); // Search for delimiter "," + if (commaIndex == -1) commaIndex = str.length(); // Command with no numerical parameters + String param = str.substring(startIndex, commaIndex); // e.g. "m" or "MOV" (allows long command names) + str.toUpperCase(); + command = str[0]; // e.g. "mov" => "MOV" => "M" +// work through the string and extract parameters (up to 4) "M,12.34,56.7,89.1" + int nVals = 0; + while ((commaIndex > 0) && (nVals < maxVal)) { // we have at lease one comma and presumably another variable? + startIndex = commaIndex + 1; + commaIndex = str.indexOf(',', startIndex); // get params + if (commaIndex > 0) { + param = str.substring(startIndex, commaIndex); + val[nVals] = param.toFloat(); + startIndex = commaIndex + 1; + } else { + param = str.substring(startIndex, str.length()); + val[nVals] = param.toFloat(); + } + nVals += 1; + } +} + +//================ +bool doCommand() { +//================ +// This function executes the received command +// It uses global variables "command" and "val[0],val[1]..." +// A bit messy but economical if we have different control inputs: +// RS232, TelNet, BlueTooth, and possibly user buttons etc changing things! +// "X,Y,Z" commands are for stage movements +//================ +bool resp=false; +unsigned int chan = 0; +unsigned int seqNo = 0; +float posn = 0; +unsigned int state = 0; +unsigned int tmp = 0; + + switch (command) { + +// "V" = Return Firmware ID and version number (was 30&31) +// RETURN "MM-ESP32",version#"+crlf + case 'V': + // we must always send this response - do not disable + sprintf(outBuf,"%s,%d\r\n",MMresponse,version); + resp=true; + break; + +// "H,0/1" = sHutter open/close <= this can be a mechanical device or +// as we do here we use electronic shuttering of the LED output pins + case 'H': + if (val[0] == 0){ + digitalWrite(shutterPin,LOW); // Close Shutter (assumes "normally closed") + writeZeros(); + } else if (val[0] > 0) { + currentPattern = (byte) val[0]; + writePattern(currentPattern); + digitalWrite(shutterPin,HIGH); // OPEN Shutter (assumes "normally closed") + } + sprintf(outBuf,"H,%d\r\n",(byte) val[0]); + break; + +// "B,1/0" = Start Blanking mode based on InPin gating signal (was 20 & 21) +// Outputs = 0 while "InPin" LOW +// Outputs = command "S" pattern while "InPin" HIGH + case 'B': + if (val[0]==0) blanking = false; + else blanking = true; + sprintf(outBuf,"B,%d\r\n",val[0]); + break; + +// "F,0/1" = Flip 'polarity' of input TTL for blanking mode +// Note: 1 means pin is "active High" (blankOnHigh=true) (i.e. the pin is normally low) +// 0 is "active Low" (BlankonHigh=false) (default) (i.e. pin is normally held high) +// By default we make it "active low" (so set pin Pull-up "ON") which is the norm for digital circuits. + case 'F': + if (val[0] == 1) blankOnHigh = false; + else blankOnHigh = true; + sprintf(outBuf,"F,%d\r\n",val[0]); + break; + +// "S" = Set digital output pattern (was 1) +// "S,v" v is desired digital pattern + case 'S': + currentPattern = (byte) val[0]; + if (!blanking) writePattern(currentPattern); + sprintf(outBuf,"S,%d\r\n",val[0]); + break; + +// "Q" = Query digital output pattern (was 2) +// duplex - RETURN "Q,p"+crlf Where p is digital output pattern +// should always return "p"+crlf + case 'Q': + sprintf(outBuf,"Q,%d\r\n",currentPattern); + resp=true; + break; + +// "O" = Output Vout value to channel (was 3) +// "O,chan,%OP" - ouput channel and %O/P as floating point + case 'O': + chan = (int) val[0]; + if (val[1]>100) val[1]=100; + if (val[1]<0) val[1]=0; + if (chan < nDAC) { + Vout[chan] = val[1]/100 * (1 << PWMres[chan]); + ledcWrite(chan, Vout[chan]); + } + sprintf(outBuf,"O,%d,%3.2f\r\n",val[0],val[1]); + break; + +// "P" = bit Pattern used in triggered mode (was 5) +// "P,n,d" - n is pattern index (0->12) +// - d is digital pattern + case 'P': + seqNo = (int) val[0]; + if ((seqNo >= 0) && (seqNo < SEQUENCELENGTH)) { + triggerPattern[seqNo] = (byte) val[1]; + char byte2bin[8]; +// print bit pattern to the OLED screen + for (int i = 0; i < 8; i++) { + byte2bin[i] = ((byte) triggerPattern[seqNo] & ((byte) 1 << (7-i))) ? '1' : '0'; + } + char pBuf[22]; + sprintf(pBuf, "O/P#:%d = %s", seqNo ,String(byte2bin)); + writeln(String(pBuf)); + } + sprintf(outBuf,"P,%d,%d\r\n",val[0],val[1]); + break; + +// "N" = Number of digital patterns for trigger mode (was 6) +// "Nx" - x number of digital patterns will be used (0->12 +// Note: In triggered mode controller wraps around + case 'N': + seqNo = (int) val[0]; + if ((seqNo >= 0) && (seqNo < SEQUENCELENGTH)) patternLength = seqNo; + sprintf(outBuf,"N,%d\r\n",val[0]); + break; + +// "K" = sKip triggers (was 7) +// "Kx" - x number of events to skip on input pin + case 'K': + skipTriggers = int(val[0]); + sprintf(outBuf,"K,%d\r\n",val[0]); + break; + +// "R" = Run trigger mode (was 8) +// Note: Trigger mode overrides blanking mode (if it was active)?? + case 'R': + if (patternLength > 0) { + sequenceNr = 0; + triggerNr = -skipTriggers; + triggerState = digitalRead(inPin); + writeZeros(); + triggerMode = true; + } + sprintf(outBuf,"R\r\n"); + break; + +// "E" = End trigger Mode (was 9) +// duplex - RETURN "E,x"+crlf - x is number of triggers on the last run +// Should always return "x"+crlf + case 'E': + triggerMode = false; + writeZeros(); + sprintf(outBuf,"E,%d\r\n",triggerNr); + resp=true; + break; + +// "T" = Time intervals for trigger mode (was 10) +// "Txtt" - x is interval number index (0 -> 12) +// - tt is interval (in ms) unsigned int format. + case 'T': + seqNo = (int) val[0]; + if ((seqNo >= 0) && (seqNo < SEQUENCELENGTH)) { + triggerDelay[seqNo] = (int) val[1]; + } + sprintf(outBuf,"T,%d\r\n",val[0]); + break; + +// "I" = Iterations of trigger patterns (was 11) +// "Ix" - x number of iterations in timed trigger mode + case 'I': + repeatPattern = (int) val[0]; + sprintf(outBuf,"I,%d\r\n",val[0]); + break; + +///////////////////////////////////////////////////////////////////////////// +// NOTE: if "G" command has been received we sit in this loop repeating +// patterns until we reach patternLength or "Serial.available" +// "G" = Go timed trigger mode.. (was 12) +// Vout patterns output (LEDs on and off) as below +// Pre-Sets.......: +// - Patterns are set using "T" command +// - Intervals are set with "D" command +// - Number of Patterns set with "N" command +// - Iterations are set with "I" command +// duplex - RETURN "G"+crlf + case 'G': + if (patternLength > 0) { + writeZeros(); + for (int i = 0; i < repeatPattern && (Serial.available() == 0); i++) { + for (int j = 0; j < patternLength && (Serial.available() == 0); j++) { + writePattern(triggerPattern[j]); + delay(triggerDelay[j]); + } + } + writeZeros(); + } + sprintf(outBuf,"G\r\n"); + break; +///////////////////////////////////////////////////////////////////////////// + +// "L" = logic in on Analogue Pins (was 40) +// "L,x" - x = Analogue Pin number 0-5 +// If x = 6 this means "ALL" - perhaps && channels? +// I'm just going to send back 1! - don't understand "ALL" +// RETURN "L,1/0"+crlf + case 'L': + chan = val[0]; + tmp=1; + if ((chan>= 0) && (chan < nADC)) tmp = digitalRead(ADCpin[chan]); + sprintf(outBuf,"L,%d\r\n", tmp); + resp=true; + break; + +// "A" = Analogue read pin value (was 41) +// "A,x" - x = Analogue pin number (0->5) +// Duplex - RETURN "A,v"+crlf - where v is 10-bit value on AnaloguePin(x) +// Always return "v"+crlf + case 'A': + chan = (int) val[0]; + tmp=0; + if ((chan >= 0) && (chan < nADC)) tmp = analogRead(ADCpin[chan]); + sprintf(outBuf,"A,%d\r\n", tmp); + resp=true; + break; + +// "D" set input pullup (was 42) +// "D,chan,state" if state true set "Input_PullUp" +// duplex - RETURN "D"+crlf + case 'D': + chan = val[0]; + state = (int) val[1]; + if ((chan >= 0) && (chan < nADC)) { + if (state) pinMode(ADCpin[chan], INPUT_PULLUP); + else pinMode(ADCpin[chan], INPUT); + } + sprintf(outBuf,"D,%d,%d\r\n",val[0],val[1]); + break; + +////////////////// XYZ Stage control Commands ////////////////// +// Code adapted from PIEZO CONCEPT X,Y,Z +// "U,chan" get axis range for x,y,z +// RETURN "U,100"+crlf {200um in x,y, 100um in z}? + case 'U': + chan= (int) val[0]; + switch (chan) { + case 0: + sprintf(outBuf,"U,%d\r\n", xRange); + resp=true; + break; + case 1: + sprintf(outBuf,"U,%d\r\n", yRange); + resp=true; + break; + case 2: + sprintf(outBuf,"U,%d\r\n", zRange); + resp=true; + break; + } + break; + + case 'X': + posn = val[0]; + if (posn < 0) posn = 0; + if (posn > xRange) posn = xRange; + xOut = posn/xRange * (1 << xPWMres); + ledcWrite(xChan, xOut); + sprintf(outBuf,"X\r\n"); + resp=true; + break; + + case 'Y': + posn = val[0]; + if (posn < 0) posn = 0; + if (posn > yRange) posn = yRange; + yOut = posn/yRange * (1 << yPWMres); + ledcWrite(yChan, yOut); + sprintf(outBuf,"Y\r\n"); + resp=true; + break; + + case 'Z': + posn = val[0]; + if (posn < 0) posn = 0; + if (posn > zRange) posn = zRange; + zOut = posn/zRange * (1 << zPWMres); + ledcWrite(zChan, zOut); + sprintf(outBuf,"Z\r\n"); + resp=true; + break; + +// Stage and Focus Sequencing... this is not yet coded on the MMAdapter-side +// "M, chan, seqNo, posn" + case 'M': + seqNo = (int) val[1]; + if ((seqNo >= 0) && (seqNo < SEQUENCELENGTH)) { + posn = val[2]; + if (posn < 0) posn = 0; + + chan= (int) val[0]; + switch (chan) { + case 0: + if (posn > xRange) posn = xRange; + xOut = posn/xRange * (1 << xPWMres); + xPosn[seqNo] = xOut; + break; + case 1: + if (posn > yRange) posn = yRange; + yOut = posn/yRange * (1 << yPWMres); + yPosn[seqNo] = yOut; + break; + case 2: + if (posn > zRange) posn = zRange; + zOut = posn/zRange * (1 << zPWMres); + zPosn[seqNo] = zOut; + break; + } + } + sprintf(outBuf,"M,%d,%d,%3.2f\r\n",val[0],val[1],val[2]); + break; + + } + +return (resp); +} + +// Service functions.... +// ************** +void writeZeros() { +// ************** +// self-explanatory + for (int i = 0; i < nDAC; i++) { + ledcWrite(i, 0); + } +} + +// ***************************** +void writePattern(byte pattern) { +// ***************************** +// self-explanatory + for (int i = 0; i < nDAC; i++) { + if (bitRead(pattern, i) == 0) ledcWrite(i, 0); + else ledcWrite(i, Vout[i]); + } +} + +//============================ +void writeln(String txt) { +//============================ +// Write to the OLED screen so we can watch comms traffic etc +// most of this code is just to scroll the lines up! +// There's an Adafruit command to do it..BUT +// Here's some Q&D grunt code! + + for (int i = 0; i < (linemax - 1); i++) { + txtlines[i] = txtlines[i + 1]; // shuffle the txt up! + } + + txtlines[linemax - 1] = txt; + + display.clearDisplay(); + + for (int i = 0; i < linemax; i++) { + display.setCursor(0, i * 8); // set line + txt = txtlines[i]; + if (txt.indexOf("PC:") >= 0) { + display.setTextColor(SSD1306_BLACK, SSD1306_WHITE); // Draw Black text, White background + } else { + display.setTextColor(SSD1306_WHITE, SSD1306_BLACK); // Draw White text, Black background + } + display.print(txt); + } + display.display(); // update display +} \ No newline at end of file diff --git a/DeviceAdapters/ESP32/ESP32_Firmware/ESP32_FEATHER_COM_BT_WiFi/ESP32_FEATHER_COM_BT_WiFi.ino b/DeviceAdapters/ESP32/ESP32_Firmware/ESP32_FEATHER_COM_BT_WiFi/ESP32_FEATHER_COM_BT_WiFi.ino new file mode 100644 index 000000000..406b0e3e6 --- /dev/null +++ b/DeviceAdapters/ESP32/ESP32_Firmware/ESP32_FEATHER_COM_BT_WiFi/ESP32_FEATHER_COM_BT_WiFi.ino @@ -0,0 +1,801 @@ +/* +If you want to use Bluetooth, Wifi, Serial etc.. you might need to allocate more memory on your ESP +To compile this program for Feather_Huzzah using Arduino IDE modify the memory allocation file: +c:\Users\username\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.9\tools\partitions\default.csv +You must increase App0 to at least 0x190000 +(Reduce App1 by same amnount and change memory offset start addresses) +Note: App1 memory is usually reserved for "Over the Air" updates... we don't need that +As below: +# Name Type SubType Offset Size Flags +nvs data nvs 0x9000 0x5000 +otadata data ota 0xe000 0x2000 +app0 app ota_0 0x10000 0x190000 +app1 app ota_1 0x1A0000 0x0F0000 +spiffs data spiffs 0x290000 0x160000 +coredump data coredump 0x3F0000 0x10000 +==================================================================== + +And ..... +c:\Users\username\AppData\Local\Arduino15\packages\hardware\esp\1.0.6\boards.txt +search for....Feather and change "maximum_size=1638400" (=0x190000) +featheresp32.upload.tool=esptool_py +featheresp32.upload.maximum_size=1638400 +featheresp32.upload.maximum_data_size=327680 +featheresp32.upload.wait_for_upload_port=true + + +The old MM-Device Adaptor code: "ARDUINO32bitBoards.cpp" +Used the following commands: +Command Number = Associated Method/Class + 1 = CArduino32Shutter::WriteToPort(long value) + 1 = CArduino32Switch::WriteToPort (long value) + 3 = CArduino32DA::WriteToPort(unsigned long value) + 5&6 = CArduino32Switch::LoadSequence(unsigned size, unsigned char* seq) + 8&9 = CArduino32Switch::OnState(MM::PropertyBase* pProp, MM::ActionType eAct) + 11 = CArduino32Switch::OnRepeatTimedPattern(MM::PropertyBase* pProp, MM::ActionType eAct) + 12&9 = CArduino32Switch::OnState(MM::PropertyBase* pProp, MM::ActionType eAct) + 20&21= CArduino32Switch::OnBlanking(MM::PropertyBase* pProp, MM::ActionType eAct) + 22 = CArduino32Switch::OnBlankingTriggerDirection(MM::PropertyBase* pProp, MM::ActionType eAct) + 30&31 = CArduino32Hub::GetControllerVersion(int& version) + 40 = CArduino32Input::GetDigitalInput(long* state) + 4 = CArduino32Input::OnAnalogInput(MM::PropertyBase* pProp, MM::ActionType eAct, long channel )***check*** + 42 = CArduino32Input::SetPullUp(int pin, int state) + +These old codes were unused: +2 - Query digitial outputs +7 - Skip triggers +10 - Time intervals for trigger mode +13-19 - Not defined +23-29, 32-39, 41, 43-> + +New ASCII command and value format: +================================================================= + Comma-delimited commands and parameters + Example command format: "P,7,123" + gives: triggerPattern[7]= 0111 1011 Binary +================================================================= +ESP32 FEATHER HUZZAH-32 Board pin-out.... +========================================================================================== + ESP Feather Huzzah32 + (__) (__) + OLED <- SDA GPIO23 o- -o GPIO21 *Shutter - to FET LED driver 1A max. + OLED <- SCL GPIO22 o- -o GPIO17 *ch3 - to FET LED driver 1A max. Blue + Stage-X GPIO14 o- -o GPIO16 *ch2 - to FET LED driver 1A max. Green + Stage-Y GPIO32 o- -o GPIO19 *ch1 - to FET LED driver 1A max. Yellow + Stage-Z GPIO15 o- -o GPIO18 *ch0 - to FET LED driver 1A max. Red + ADC0 GPIO33 o- -o GPIO5 *inPin <= blanking deglitch with 0.1uF cap. to GND + ADC1 GPIO27 o- -o GPIO4 + ADC2 GPIO12 o- -o GPIO36 + ADC3 GPIO13 o- -o GPIO39 + VBus o- -o GPIO34 + Z OpAmp+LP filter *ch7 EN o- -o GPIO25 + VBat o- -o GPIO26 + ====| -o GND + ====| Battery -o N/C + ====| -o 3.3V + | | -o Reset + (__) |___| (__) + USB skt +========================================================================================== +Note 8-bit res on GPIO25/26 is pretty poor so maybe set them to PWM mode at 14 bit res, (clk ~10kHz) +Need to low-pass filter to about 10Hz to remove ripple - which would be fine for most X-Y stages +*/ + +// BLUETOOTH +// Mobile APP for communicating with microcontrollers +// Available at: WWW.KEUWL.COM +// You can set up sliders, buttons, pad, dials etc +#include "BluetoothSerial.h" +#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED) +#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it +#endif +BluetoothSerial SerialBT; + +// WiFi Stuff +#include +// create a server and listen on port 8088 +WiFiServer server(8088); + +//local login credentials at work +//const char* ssid = "WorkRouter"; +//const char* password = "WorkPASSWORD"; + +// local login credentials at home +const char* ssid = "HomeRouter"; +const char* password = "HomePassword"; + +// Stuff for the I2C OLED display +//#include +#include +#define SCREEN_WIDTH 128 // OLED display width, in pixels +#define SCREEN_HEIGHT 64 // OLED display height, in pixels +#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin) +#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32 +Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); +// define GPIO pins for I2C communications +#define I2C_SDA 23 +#define I2C_SCL 22 +int linemax = (SCREEN_HEIGHT / 8 ) ; +#define dummy " " +// initialise nlines string values..for pointer to index! +String txtlines[] = { dummy, dummy, dummy, dummy, dummy, dummy, dummy, dummy}; + +String MMresponse = "MM-ESP32"; // Expected response from Arduino +unsigned int version = 1; // This version of the firmware is Version 1 <<== change if significant update + +const int SEQUENCELENGTH = 12; // This should be good enough + +// Each byte in triggerPattern[] array codes the output blanking pattern for upto 5 - channels +// Each entry is bitmapped as a binary mask to turn output pins off (="0") or at Vout value (="1"). +// bits 7,6,5 are reserved... bit4=shutter, bit3=blue, bit2=green, bit1=yellow, bit0=red. +// Initially all outputs are active for every timed sequence position e.g. all "r r r 1 1 1 1 1 BIN" = 255 DEC +byte triggerPattern[SEQUENCELENGTH] = { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }; + +//Delay times in milliseconds for each sequence output +unsigned int triggerDelay[SEQUENCELENGTH] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +//We can also "sequence" the x,y,z stage positions +//Here, we store the x,y,z positions +unsigned int xPosn[SEQUENCELENGTH] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +unsigned int yPosn[SEQUENCELENGTH] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +unsigned int zPosn[SEQUENCELENGTH] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +int patternLength = 0; // Number of pattern slots that are currently in use +byte repeatPattern = 0; // Number of repeat cycles + +volatile long triggerNr; // # of triggers can start at negative value = "-skipTriggers" +volatile long sequenceNr; // current sequence number as we cycle through sequence pattern (0->patternLength) + +int skipTriggers = 0; // # of triggers to skip before starting to generate patterns (triggerNr is set to "-skipTriggers") +byte currentPattern = 255; // A global variable that keeps track of channel output logic + +bool blanking = false; +bool blankOnHigh = false; +bool triggerMode = false; +bool triggerState = false; + +// chan 0 1 2 3 +const uint8_t DACpin[] = { 18, 19, 16, 17 }; // GPIO Pins (OUTPUT) +const uint32_t PWMfreq[] = { 50, 50, 50, 50 }; // PWM freq in kHz +const uint32_t PWMres[] = { 10, 10, 10, 10 }; // PWM resolution in bits +unsigned int Vout[] = { 0, 0, 0, 0 }; // LED output values +const int nDAC = 4; // 4 DAC channels + +// Stage Position control +const uint8_t xPin = 14; // GPIO Pin (OUTPUT) +const uint32_t xPWMfreq = 4; // PWM freq in kHz +const uint32_t xPWMres = 14; // PWM resolution in bits +unsigned int xOut = 0; // xyz Stage output values +const int xChan = 10; // PWM output channel number +int xRange = 200; // xRange (um) + +const uint8_t yPin = 32; // GPIO Pin (OUTPUT) +const uint32_t yPWMfreq = 4; // PWM freq in kHz +const uint32_t yPWMres = 14; // PWM resolution in bits +unsigned int yOut = 0; // xyz Stage output values +const int yChan = 11; // PWM output channel number +int yRange = 200; // yRange (um) + +const uint8_t zPin = 15; // GPIO Pin (OUTPUT) +const uint32_t zPWMfreq = 4; // PWM freq in kHz +const uint32_t zPWMres = 14; // PWM resolution in bits +unsigned int zOut = 0; // xyz Stage output values +const int zChan = 12; // PWM output channel number +int zRange = 100; // zRange (um) + +// Analogue input GPIO pins on the ESP32 Feather +// 0 1 2 3 +const uint8_t ADCpin[] = { 33, 27, 12, 13 }; // set GPIO Pins (INPUT) +const int nADC = 4; // 4 ADC channels + +//"Logical" pin for blanking (I/P) and harware shutter (O/P) +const uint8_t shutterPin = 21; // set GPIO Pin for Shutter control (O/P) +const uint8_t inPin = 5; // set GPIO Pin for trigger/blanking (I/P) + +// Create some global variable so we can see the commands + params everywhere +// This saves passing values back-and-forth to functions all the time! +int const maxVal=4; +int const maxBytes=50; +char command = 0; +char inBuf[maxBytes]; +char outBuf[maxBytes]; +float val[maxVal]; +int ct=0; + +//============================= Setup =============================== +void setup() { + +Serial.begin(115200); //Baud rate + + Wire.begin(I2C_SDA, I2C_SCL); +// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally + display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS); + +// Clear the buffer + display.clearDisplay(); + display.setTextSize(1); // Normal 1:1 pixel scale + display.setTextColor(SSD1306_WHITE, SSD1306_BLACK); // Draw white text, Black background + +// Set inPin for input (pullup=normally high) + pinMode(inPin, INPUT_PULLUP); + // Set Shutter for output + pinMode(shutterPin, OUTPUT); + +// Configure the PWM ouptut channels + ledcSetup(xChan, xPWMfreq*1000, xPWMres); + ledcSetup(yChan, yPWMfreq*1000, yPWMres); + ledcSetup(zChan, zPWMfreq*1000, zPWMres); + +// Assign the correct GPIO pins to the channels +// xChan, yChan, zChan are RESERVED 10, 11, 12 + ledcAttachPin(xPin, xChan); + ledcAttachPin(yPin, yChan); + ledcAttachPin(zPin, zChan); + +// PWM setup - ESP32 has up to 16 independent PWM channels +// DACPWM channels 0->nDac (where nDAC<10 - see x,y,z chans above) + for (uint8_t i = 0; i < nDAC; i++) ledcSetup(i, PWMfreq[i]*1000, PWMres[i]); + for (uint8_t i = 0; i < nDAC; i++) ledcAttachPin(DACpin[i], i); + + String outStr = "Running:" + MMresponse + String(version); + writeln(outStr); + + // clear the RS232 serial buffer + Serial.flush(); + +// Start Bluetooth + SerialBT.begin("ESP32s"); //Bluetooth device name + writeln("Bluetooth=ESP32s"); + +// connect to WiFi + outStr = "Connect:" + String(ssid); + writeln(outStr); + + WiFi.begin(ssid, password); + delay(100); + + boolean connected= false; + int ct = 0; +// wait until ESP32 connected + while ((WiFi.status() != WL_CONNECTED) && (ct<20)) { + delay(100); + ct+=1; + } + + if (ct<20) connected = true; + + IPAddress ip = WiFi.localIP(); + char buf[16]; + sprintf(buf, "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]); + outStr = "IP:" + String(buf); + writeln(outStr); + +// start Server + if (connected) server.begin(); + +} +//========================= Setup ENDS =========================== + +//=========== +void loop() { +//=========== +// Sit in this main loop and wait for incoming commands over RS232 (USB) + + WiFiClient client = server.available(); + if (client) { + while (client.connected()) { + +// check for WiFi input + if (client.available() > 0) { + delay(5); // wait for WiFi buffer to fill + int nbytes = client.available(); + if (nbytes > maxBytes) nbytes = maxBytes; + int len = client.readBytesUntil('\n', inBuf, nbytes); + inBuf[len]=0; + String str = String(inBuf); + splitStr(str); + // we now have the command character and the parameters val[0]->val[3] + bool respond = doCommand(); + // We MUST send the outBuf if respond is true + if (respond) { + client.print(outBuf); + writeln(String(outBuf)); + } + command = 0; + } + + if (Serial.available() > 0) doRS232(); // check for RS232 input + + if (SerialBT.available() > 0) doBlueTooth(); // check for bluetooth + +// check for trigger or blanking during Video recording + checkTrig(); + } // loop while WiFI client connected + + } // WiFi client no longer connected... + + if (Serial.available() > 0) doRS232(); // check for RS232 input + + if (SerialBT.available() > 0) doBlueTooth(); // check for bluetooth + + checkTrig(); + +// loop and try for new WiFi client +} + +//========================= Main Loop ENDS ========================= +//============== +void doRS232() { +//============== +// new RS232 data available + delay(50); + int nbytes = Serial.available(); + if (nbytes > maxBytes) nbytes = maxBytes; + int len = Serial.readBytesUntil('\n', inBuf, nbytes); + inBuf[len]=0; + String str = String(inBuf); + splitStr(str); + bool respond = doCommand(); + // We MUST send the outBuf if respond is true + if (respond){ + Serial.print(outBuf); + writeln(String(outBuf)); + } + command=0; +} +//================== +void doBlueTooth() { +//================== +// new Bluetooth data available + delay(50); //wait for buffer to fill + int nbytes = SerialBT.available(); + if (nbytes > maxBytes) nbytes = maxBytes; + int len = SerialBT.readBytesUntil('\n', inBuf, nbytes); + inBuf[len]=0; + String str = String(inBuf); + splitStr(str); + bool respond = doCommand(); + // We MUST send the outBuf if respond is true. + if (respond){ + //Serial.print(outBuf); + writeln(String(outBuf)); + } + command=0; +} + +//================ +void checkTrig() { +//================ +// On eack loop check for a trigger signal +// In trigger mode, we will blank even if blanking is not on.. +// Only do this on first transition of the trigger signal + if (triggerMode) { + bool trigSig = digitalRead(inPin); + if ((triggerState != trigSig) && (blankOnHigh == trigSig)) { // first trigger - turn everything off + writeZeros(); + writeln("Arse"); + triggerState = trigSig; + } else if (blankOnHigh == trigSig) { // subsequent triggers + if (triggerNr >= 0) { // TRUE when we have skipped "n" pre-triggers + writePattern(triggerPattern[sequenceNr]); // move to next pattern + sequenceNr = (sequenceNr++) % patternLength; // modulus wrap around + } + triggerNr++; // tally number of triggers + } + } else if (blanking) { // blanking mode?? + if ((blankOnHigh == digitalRead(inPin))) writePattern(currentPattern); + else writeZeros(); + } + } + +//========================= +void splitStr(String str) { +//========================= +// This is called when a string has been received from RS232, TelNet or Bluetooth +// We leave the function having updated: +// "command" string (usually just one character) and variables "val[0], val[1], val[2]... +// writeln("Recd:" +str); + str.trim(); + int startIndex = 0; + int commaIndex = str.indexOf(',', startIndex); // Search for delimiter "," + if (commaIndex == -1) commaIndex = str.length(); // Command with no numerical parameters + String param = str.substring(startIndex, commaIndex); // e.g. "m" or "MOV" (allows long command names) + str.toUpperCase(); + command = str[0]; // e.g. "mov" => "MOV" => "M" +// work through the string and extract parameters (up to 4) "M,12.34,56.7,89.1" + int nVals = 0; + while ((commaIndex > 0) && (nVals < maxVal)) { // we have at lease one comma and presumably another variable? + startIndex = commaIndex + 1; + commaIndex = str.indexOf(',', startIndex); // get params + if (commaIndex > 0) { + param = str.substring(startIndex, commaIndex); + val[nVals] = param.toFloat(); + startIndex = commaIndex + 1; + } else { + param = str.substring(startIndex, str.length()); + val[nVals] = param.toFloat(); + } + nVals += 1; + } + + char pBuf[16]; + if (command == 'L') ct+=1; + sprintf(pBuf, "%c:%.0f,%.0f,%d", command, val[0], val[1], ct); + writeln(String(pBuf)); + +} + +//================ +bool doCommand() { +//================ +// This function executes the received command +// It uses global variables "command" and "val[0],val[1]..." +// A bit messy but economical if we have different control inputs: +// RS232, TelNet, BlueTooth, and possibly user buttons etc changing things! +// "X,Y,Z" commands are for stage movements +//================ +bool resp=false; +unsigned int chan = 0; +unsigned int seqNo = 0; +float posn = 0; +unsigned int state = 0; +unsigned int tmp = 0; + + switch (command) { + +// "V" = Return Firmware ID and version number (was 30&31) +// RETURN "MM-ESP32",version#"+crlf + case 'V': + // we must always send this response - do not disable + sprintf(outBuf,"%s,%d\r\n",MMresponse,version); + resp=true; + break; + +// "H,0/1" = sHutter open/close <= this can be a mechanical device or +// as we do here we use electronic shuttering of the LED output pins + case 'H': + if (val[0] == 0){ + digitalWrite(shutterPin,LOW); // Close Shutter (assumes "normally closed") + writeZeros(); + } else if (val[0] > 0) { + currentPattern = (byte) val[0]; + writePattern(currentPattern); + digitalWrite(shutterPin,HIGH); // OPEN Shutter (assumes "normally closed") + } + sprintf(outBuf,"H\r\n"); + break; + +// "B,1/0" = Start Blanking mode based on InPin gating signal (was 20 & 21) +// Outputs = 0 while "InPin" LOW +// Outputs = command "S" pattern while "InPin" HIGH +// duplex - RETURN "B"+crlf + case 'B': + if (val[0]==0) blanking = false; + else blanking = true; + sprintf(outBuf,"B\r\n"); + break; + +// "F,0/1" = Flip 'polarity' of input TTL for blanking mode +// Note: 1 means pin is "active High" (blankOnHigh=true) (i.e. the pin is normally low) +// 0 is "active Low" (BlankonHigh=false) (default) (i.e. pin is normally held high) +// By default we make it "active low" (so set pin Pull-up "ON") which is the norm for digital circuits. +// duplex - RETURN "F"+crlf + case 'F': + if (val[0] == 1) blankOnHigh = false; + else blankOnHigh = true; + sprintf(outBuf,"F\r\n"); + break; + +// "S" = Set digital output pattern (was 1) +// "S,v" v is desired digital pattern +// duplex - RETURN "S"+crlf + case 'S': + currentPattern = (byte) val[0]; + if (!blanking) writePattern(currentPattern); + sprintf(outBuf,"S\r\n"); + break; + +// "Q" = Query digital output pattern (was 2) +// duplex - RETURN "Q,p"+crlf Where p is digital output pattern +// should always return "p"+crlf + case 'Q': + sprintf(outBuf,"Q,%d\r\n",currentPattern); + resp=true; + break; + +// "O" = Output Vout value to channel (was 3) +// "O,chan,%OP" - ouput channel and %O/P as floating point +// duplex - RETURN "O"+crlf + case 'O': + chan = (int) val[0]; + if (val[1]>100) val[1]=100; + if (val[1]<0) val[1]=0; + if (chan < nDAC) { + Vout[chan] = val[1]/100 * (1 << PWMres[chan]); + ledcWrite(chan, Vout[chan]); + } + sprintf(outBuf,"O\r\n"); + break; + +// "P" = bit Pattern used in triggered mode (was 5) +// "P,n,d" - n is pattern index (0->12) +// - d is digital pattern +// duplex - RETURN "P+crlf" + case 'P': + seqNo = (int) val[0]; + if ((seqNo >= 0) && (seqNo < SEQUENCELENGTH)) { + triggerPattern[seqNo] = (byte) val[1]; + char byte2bin[8]; +// print bit pattern to the OLED screen + for (int i = 0; i < 8; i++) { + byte2bin[i] = ((byte) triggerPattern[seqNo] & ((byte) 1 << (7-i))) ? '1' : '0'; + } + char pBuf[22]; + sprintf(pBuf, "O/P#:%d = %s", seqNo ,String(byte2bin)); + writeln(String(pBuf)); + } + sprintf(outBuf,"P\r\n"); + break; + +// "N" = Number of digital patterns for trigger mode (was 6) +// "Nx" - x number of digital patterns will be used (0->12 +// Note: In triggered mode controller wraps around +// duplex - RETURN "N"+crlf + case 'N': + seqNo = (int) val[0]; + if ((seqNo >= 0) && (seqNo < SEQUENCELENGTH)) patternLength = seqNo; + sprintf(outBuf,"N\r\n"); + break; + +// "K" = sKip triggers (was 7) +// "Kx" - x number of events to skip on input pin +// duplex - RETURN "K"+crlf + case 'K': + skipTriggers = int(val[0]); + sprintf(outBuf,"K\r\n"); + break; + +// "R" = Run trigger mode (was 8) +// Note: Trigger mode overrides blanking mode (if it was active)?? +// duplex - RETURN "R"+crlf + case 'R': + if (patternLength > 0) { + sequenceNr = 0; + triggerNr = -skipTriggers; + triggerState = digitalRead(inPin); + writeZeros(); + triggerMode = true; + } + sprintf(outBuf,"R\r\n"); + break; + +// "E" = End trigger Mode (was 9) +// duplex - RETURN "E,x"+crlf - x is number of triggers on the last run +// Should always return "x"+crlf + case 'E': + triggerMode = false; + writeZeros(); + sprintf(outBuf,"E,%d\r\n",triggerNr); + resp=true; + break; + +// "T" = Time intervals for trigger mode (was 10) +// "Txtt" - x is interval number index (0 -> 12) +// - tt is interval (in ms) unsigned int format. +// duplex - RETURN "T"+crlf + case 'T': + seqNo = (int) val[0]; + if ((seqNo >= 0) && (seqNo < SEQUENCELENGTH)) { + triggerDelay[seqNo] = (int) val[1]; + } + sprintf(outBuf,"T\r\n"); + break; + +// "I" = Iterations of trigger patterns (was 11) +// "Ix" - x number of iterations in timed trigger mode +// duplex - RETURN "I"+crlf + case 'I': + repeatPattern = (int) val[0]; + sprintf(outBuf,"I\r\n"); + break; + +///////////////////////////////////////////////////////////////////////////// +// NOTE: if "G" command has been received we sit in this loop repeating +// patterns until we reach patternLength or "Serial.available" +// "G" = Go timed trigger mode.. (was 12) +// Vout patterns output (LEDs on and off) as below +// Pre-Sets.......: +// - Patterns are set using "T" command +// - Intervals are set with "D" command +// - Number of Patterns set with "N" command +// - Iterations are set with "I" command +// duplex - RETURN "G"+crlf + case 'G': + if (patternLength > 0) { + writeZeros(); + for (int i = 0; i < repeatPattern && (Serial.available() == 0); i++) { + for (int j = 0; j < patternLength && (Serial.available() == 0); j++) { + writePattern(triggerPattern[j]); + delay(triggerDelay[j]); + } + } + writeZeros(); + } + sprintf(outBuf,"G\r\n"); + break; +///////////////////////////////////////////////////////////////////////////// + +// "L" = logic in on Analogue Pins (was 40) +// "L,x" - x = Analogue Pin number 0-5 +// If x = 6 this means "ALL" - perhaps && channels? +// I'm just going to send back 1! - don't understand "ALL" +// RETURN "L,1/0"+crlf + case 'L': + chan = val[0]; + tmp=1; + if (chan < nADC) tmp = digitalRead(ADCpin[chan]); + sprintf(outBuf,"L,%d\r\n", tmp); + resp=true; + break; + +// "A" = Analogue read pin value (was 41) +// "A,x" - x = Analogue pin number (0->5) +// Duplex - RETURN "A,v"+crlf - where v is 10-bit value on AnaloguePin(x) +// Always return "v"+crlf + case 'A': + chan = (int) val[0]; + tmp=0; + if ((chan >= 0) && (chan < nADC)) tmp = analogRead(ADCpin[chan]); + sprintf(outBuf,"A,%d\r\n", tmp); + resp=true; + break; + +// "D" set input pullup (was 42) +// "D,chan,state" if state true set "Input_PullUp" +// duplex - RETURN "D"+crlf + case 'D': + chan = val[0]; + state = (int) val[1]; + if ((chan >= 0) && (chan < nADC)) { + if (state) pinMode(ADCpin[chan], INPUT_PULLUP); + else pinMode(ADCpin[chan], INPUT); + } + sprintf(outBuf,"D\r\n"); + break; + +////////////////// XYZ Stage control Commands ////////////////// +// Code adapted from PIEZO CONCEPT X,Y,Z +// "U,chan" get axis range for x,y,z +// RETURN "U,100"+crlf {200um in x,y, 100um in z}? + case 'U': + chan= (int) val[0]; + switch (chan) { + case 0: + sprintf(outBuf,"U,%d\r\n", xRange); + resp=true; + break; + case 1: + sprintf(outBuf,"U,%d\r\n", yRange); + resp=true; + break; + case 2: + sprintf(outBuf,"U,%d\r\n", zRange); + resp=true; + break; + } + break; + + case 'X': + posn = val[0]; + if (posn < 0) posn = 0; + if (posn > xRange) posn = xRange; + xOut = posn/xRange * (1 << xPWMres); + ledcWrite(xChan, xOut); + sprintf(outBuf,"X\r\n"); + resp=true; + break; + + case 'Y': + posn = val[0]; + if (posn < 0) posn = 0; + if (posn > yRange) posn = yRange; + yOut = posn/yRange * (1 << yPWMres); + ledcWrite(yChan, yOut); + sprintf(outBuf,"Y\r\n"); + resp=true; + break; + + case 'Z': + posn = val[0]; + if (posn < 0) posn = 0; + if (posn > zRange) posn = zRange; + zOut = posn/zRange * (1 << zPWMres); + ledcWrite(zChan, zOut); + sprintf(outBuf,"Z\r\n"); + resp=true; + break; + + // Stage and Focus Sequencing... this is not yet coded on the MMAdapter-side +// "M, chan, seqNo, posn" + case 'M': + seqNo = (int) val[1]; + if ((seqNo >= 0) && (seqNo < SEQUENCELENGTH)) { + posn = val[2]; + if (posn < 0) posn = 0; + + chan= (int) val[0]; + switch (chan) { + case 0: + if (posn > xRange) posn = xRange; + xOut = posn/xRange * (1 << xPWMres); + xPosn[seqNo] = xOut; + break; + case 1: + if (posn > yRange) posn = yRange; + yOut = posn/yRange * (1 << yPWMres); + yPosn[seqNo] = yOut; + break; + case 2: + if (posn > zRange) posn = zRange; + zOut = posn/zRange * (1 << zPWMres); + zPosn[seqNo] = zOut; + break; + } + } + sprintf(outBuf,"M\r\n"); + break; + + } + +return (resp); +} + + +// Service functions.... +// ************** +void writeZeros() { +// ************** +// self-explanatory + for (int i = 0; i < nDAC; i++) { + ledcWrite(i, 0); + } +} + +// ***************************** +void writePattern(byte pattern) { +// ***************************** +// self-explanatory + for (int i = 0; i < nDAC; i++) { + if (bitRead(pattern, i) == 0) ledcWrite(i, 0); + else ledcWrite(i, Vout[i]); + } +} + +//============================ +void writeln(String txt) { +//============================ +// Write to the OLED screen so we can watch comms traffic etc +// most of this code is just to scroll the lines up! +// There's an Adafruit command to do it..BUT +// Here's some Q&D grunt code! + + for (int i = 0; i < (linemax - 1); i++) { + txtlines[i] = txtlines[i + 1]; // shuffle the txt up! + } + + txtlines[linemax - 1] = txt; + + display.clearDisplay(); + + for (int i = 0; i < linemax; i++) { + display.setCursor(0, i * 8); // set line + txt = txtlines[i]; + if (txt.indexOf("PC:") >= 0) { + display.setTextColor(SSD1306_BLACK, SSD1306_WHITE); // Draw Black text, White background + } else { + display.setTextColor(SSD1306_WHITE, SSD1306_BLACK); // Draw White text, Black background + } + display.print(txt); + } + display.display(); // update display +} \ No newline at end of file diff --git a/DeviceAdapters/ESP32/esp32.cpp b/DeviceAdapters/ESP32/esp32.cpp new file mode 100644 index 000000000..e004922b0 --- /dev/null +++ b/DeviceAdapters/ESP32/esp32.cpp @@ -0,0 +1,2410 @@ +/* + ===================================================================== + + This Adapter is modified from Arduino32.cpp & PIEZOCONCEPT.cpp: + + Original authors and copyright as below: + COPYRIGHT: University of California, San Francisco, 2008 + LICENSE: LGPL + AUTHOR: Original Author Arduino Device Adapter: + Nico Stuurman, nico@cmp.ucsf.edu, 11/09/2008 + + Automatic device detection by Karl Hoover + + Author 32-Bit-Boards adaptation: + Bonno Meddens, 30/07/2019 + + ===================================================================== + + This uManager Device Adapter is called "ESP32.cpp" + It requires associated firmware to be uploaded to an ESP family microcontroller. + The microcontroller is programmed using Arduino software. + "ESP32_COM_BT_WiFi.INO" + + AUTHORS: Justin E. Molloy & Nicholas J. Carter + University of Warwick, UK 2023 + + The firmware code is heavily commented so should be pretty easy to modify. + It has a combined interface to Bluetooth, WiFI & USB/RS232. It also supports an + attached OLED screen which helps with debugging. + + Efforts have been made to comment any new code. However, much of the original code + remains uncommented except for some guesses which may be incorrect - I apologise. + + Main changes: + 1) All commands are now sent as ASCII so we can debug/test using PuTTY and monitor command + transactions on the OLED screen. The "byte count" overhead is trivial compared to USB bus + latency times and total packet size for single COM <-> transactions - most transactions are + not time-critical + 2) Most commands are now simplex (no duplex handshaking) + 3) x,y,z stage control has been added + + Hint: to make the code easier to navigate in Visual Studio press "ctrl M" then "ctrl O" + to collapse the code. + + Things to improve: + 1) Need to fully comment the code so it is easy to modify and debug. + 2) Port communications are messy and should be conducted using a single function. + 3) String handling is inconsistent. + (Note: The "ASI-hub" has some very nice utility functions - need to port to here) + 5) In this version the "shutter" is a switch and the switch is a shutter which is confusing + 6) The input monitor thread (timed "L" calls) don't seem to work quite as I would expect + 7) Need to read back all settings especially: temperature, x,y,z stage posn, optical trap, + magnetic tweezer position and various logic state switches so they can be saved with the + movie data as Tiff Tags + 8) MDA LED switching cycle is not working exactly as it should + The first channel in the series turns on at end of cycle (during the dark phase) + +*/ + +#include "ESP32.h" +#include "ModuleInterface.h" +#include + +//string and IO handling +#include +#include +#include +using namespace std; + +// Name the Devices something short and sensible +const char* g_DeviceNameESP32Hub = "ESP32-Hub"; +const char* g_DeviceNameESP32Switch = "ESP32-Switch"; +const char* g_DeviceNameESP32Shutter = "ESP32-Shutter"; +const char* g_DeviceNameESP32PWM0 = "ESP32-PWM0"; +const char* g_DeviceNameESP32PWM1 = "ESP32-PWM1"; +const char* g_DeviceNameESP32PWM2 = "ESP32-PWM2"; +const char* g_DeviceNameESP32PWM3 = "ESP32-PWM3"; +const char* g_DeviceNameESP32PWM4 = "ESP32-PWM4"; +const char* g_DeviceNameStage = "ZStage"; +const char* g_DeviceNameXYStage = "XYStage"; +const char* g_DeviceNameESP32Input = "ESP32-Input"; + +const char* g_PropertyMinUm = "Z Stage Low Posn(um)"; +const char* g_PropertyMaxUm = "Z Stage High Posn(um)"; + +const char* g_PropertyXMinUm = "X Stage Min Posn(um)"; +const char* g_PropertyXMaxUm = "X Stage Max Posn(um)"; + +const char* g_PropertyYMinUm = "Y Stage Min Posn(um)"; +const char* g_PropertyYMaxUm = "Y Stage Max Posn(um)"; + +// Global info about the state of the ESP32. This should be folded into a class +const int g_Min_MMVersion = 1; +const int g_Max_MMVersion = 100; +const char* g_versionProp = "Version"; +const char* g_normalLogicString = "Normal"; +const char* g_invertedLogicString = "Inverted"; + +const char* g_On = "On"; +const char* g_Off = "Off"; + +// static lock +MMThreadLock CESP32Hub::lock_; + +/////////////////////////////////////////////////////////////////////////////// +// Exported MMDevice API +/////////////////////////////////////////////////////////////////////////////// +MODULE_API void InitializeModuleData() +{ + // The HUB and other devices are registered with the MMcore code + // I think there are currently ~16 recognised Device "types" including: + // Hub, StateDevice, ShutterDevice, GenericDevice, SignalIODevice, StageDevice(focus?), + // XYStageDevice, AutofocusDevice, GalvoDevice.. There seems to be some ambiguity/overlap + // in what the different Device types actually do.. and the shutter function is used to + // change the LED output pattern. + + RegisterDevice(g_DeviceNameESP32Hub, MM::HubDevice, "Hub (required)"); + + // Note: The "StateDevice" is used to "blank" the SignalIODevice by setting the output to zero. + RegisterDevice(g_DeviceNameESP32Switch, MM::StateDevice, "Switch on/off channels 0 to 10"); + RegisterDevice(g_DeviceNameESP32Shutter, MM::ShutterDevice, "Shutter"); + RegisterDevice(g_DeviceNameESP32PWM0, MM::SignalIODevice, "PWM channel 0"); + RegisterDevice(g_DeviceNameESP32PWM1, MM::SignalIODevice, "PWM channel 1"); + RegisterDevice(g_DeviceNameESP32PWM2, MM::SignalIODevice, "PWM channel 2"); + RegisterDevice(g_DeviceNameESP32PWM3, MM::SignalIODevice, "PWM channel 3"); + RegisterDevice(g_DeviceNameESP32PWM4, MM::SignalIODevice, "PWM channel 4"); + RegisterDevice(g_DeviceNameStage, MM::StageDevice, "Z Stage"); + RegisterDevice(g_DeviceNameXYStage, MM::XYStageDevice, "XY Stage"); + RegisterDevice(g_DeviceNameESP32Input, MM::GenericDevice, "ADC"); +} + +MODULE_API MM::Device* CreateDevice(const char* deviceName) +{ + // Justin's Notes: After registering the devices we now "create them" and give them another name. + // I admit I find it hard to keep track of the different names - especially when + // things get renamed again within the config file. Just keep your hair on and go + // with it! + + if ( deviceName == 0 ) return 0; + + if ( strcmp(deviceName, g_DeviceNameESP32Hub) == 0 ) return new CESP32Hub; + else if ( strcmp(deviceName, g_DeviceNameESP32Switch) == 0 ) return new CESP32Switch; + else if ( strcmp(deviceName, g_DeviceNameESP32Shutter) == 0 ) return new CESP32Shutter; + else if ( strcmp(deviceName, g_DeviceNameESP32PWM0) == 0 ) return new CESP32DA(0); // channel 0 + else if ( strcmp(deviceName, g_DeviceNameESP32PWM1) == 0 ) return new CESP32DA(1); // channel 1 + else if ( strcmp(deviceName, g_DeviceNameESP32PWM2) == 0 ) return new CESP32DA(2); // channel 2 + else if ( strcmp(deviceName, g_DeviceNameESP32PWM3) == 0 ) return new CESP32DA(3); // channel 3 + else if ( strcmp(deviceName, g_DeviceNameESP32PWM4) == 0 ) return new CESP32DA(4); // channel 4 + else if ( strcmp(deviceName, g_DeviceNameStage) == 0 ) return new CESP32Stage(); + else if ( strcmp(deviceName, g_DeviceNameXYStage) == 0 ) return new CESP32XYStage(); + else if ( strcmp(deviceName, g_DeviceNameESP32Input) == 0 ) return new CESP32Input; + return 0; +} + +MODULE_API void DeleteDevice(MM::Device* pDevice) +{ + delete pDevice; +} + + +/* CESP32HUb implementation: + The HUB is the master "Device" it gives a convenient way to communicate with several + physical or "nominal" devices connected via a single cable to the PC. The hub heirarchy makes things + a little more complicated than adapters written for single, stand-alone, physical devices. + + Although the ESP32 microcontroller is just a single physical device, here + it functions as four or five separate "MM_Devices". The firmware running on the ESP can + control LED light sources, move an XY stage, a focusing device, open/close a physical shutter, + control a laser system etc. + + The ESP firmware allows Bluetooth, Wifi, USB(RS232) control "Commanders" + We should report back to micromanager what different commanders are doing. + (I don't do that at present!). + + LED channels: + We have 10-bit res on 4 GPIOs (50kHz PWM) for LED control - easy to interface FETs + with a PWM signal which then allows control of high-current LEDs. + + Laser Control: + Suggest ESP32 firmware does all the device-specific RS232 stuff by "echoing" appropriate + commands down its secondary RxTx (pins) with splitter (e.g. MAX 3221 + Max399 to + create a 3-port RS2323 hub)? Then we can control all our devices without writing individual + device adapaters for MM. + E.G. Our Toptica laser requires only three commands via RS232 to do what we need: + "la on", "la off" and set power ("ch 1 pow "+(floating point mW value)) we can do that + over the secondary (hardware) RxTx on ESP32. + We assign it to an available DAC line (here channel "5") + E.g. MM sends: "O,5,0" to turn the laser OFF + "O,5,27.5" to turn laser on to 27.5mW +### +ARDUINO PSEUDO-CODE: +#include +void setup() { + Serial.begin(115200); // connected to computer + Serial2.begin(115200); // connected to laser +} + +void loop() { + if (Serial.available()) { + ...read and process commands sent by uManager via primary USB(RS232) + .. if it's "O"utput to "channel 5" (the laser) e.g. "O,5,27.5" + ...then echo the device-specific command via secondary RS232 [GPIO16] & [GPIO17] + Serial2.println("ch 1 pow 27.5"); + Serial2.println("la on"); + Serial.println("O"); // echo back to micromanager that command has been received and done. + } +} +### +Using the above approach the laser would just appear as another DAC channel: adjust power, turn on/off +sequence it do what you like. +*/ + +/* + HUB Summary: + ============ + The Hub code sends 2 commands or "requests" to the ESP device. + => "V" "V"ersion number & Firmware ID? - expected response is "MM-ESP32,5" or greater + => "U,0-2" "U"nderstand the XYZ stage ranges (X=0, Y=1, Z=2) + => "U,0" expects ESP32 response e.g. "U,200" (X-axis range) + => "U,2" expects ESP32 response e.g. "U,100" (Z-axis range) + => if no response or range=0.. MM thinks there's no stage + + => There are 3 or 4 functions/methods that do clever things to DetectDevices + Probably critical for the Hardware Wizard to work. + => Nice to have these functions documented here.. I have no idea of the mechanics +*/ + +//CESP32HUb implementation +CESP32Hub::CESP32Hub() : + initialized_(false), + switchState_(0), + shutterState_(0), + hasZStage_(false), + hasXYStage_(false) +{ + portAvailable_ = false; + invertedLogic_ = false; + timedOutputActive_ = false; + + +// Create Error messages + InitializeDefaultErrorMessages(); + SetErrorText(ERR_PORT_OPEN_FAILED, "Failed opening ESP32 USB device"); + SetErrorText(ERR_BOARD_NOT_FOUND, "Did not find an ESP32 board with the correct firmware. Is the ESP32 board connected to this serial port?"); + SetErrorText(ERR_NO_PORT_SET, "Hub Device not found. The ESP32 Hub device is needed to create this device"); + std::ostringstream errorText; + errorText << "The firmware version on the ESP32 is not compatible with this adapter. Please use firmware version "; + errorText << g_Min_MMVersion << " to " << g_Max_MMVersion; + SetErrorText(ERR_VERSION_MISMATCH, errorText.str().c_str()); + +// Create some pre-initialisation properties: +// Port: + CPropertyAction* pAct = new CPropertyAction(this, &CESP32Hub::OnPort); + CreateProperty(MM::g_Keyword_Port, "Undefined", MM::String, false, pAct, true); + +// Set default logic property to "inverted" (1 is 0) ? +// comments==0 + pAct = new CPropertyAction(this, &CESP32Hub::OnLogic); + CreateProperty("Logic", g_invertedLogicString, MM::String, false, pAct, true); + + AddAllowedValue("Logic", g_invertedLogicString); + AddAllowedValue("Logic", g_normalLogicString); +} + +CESP32Hub::~CESP32Hub() +{ + Shutdown(); +} + +void CESP32Hub::GetName(char* name) const +{ + CDeviceUtils::CopyLimitedString(name, g_DeviceNameESP32Hub); +} + +bool CESP32Hub::Busy() +{ + return false; +} + +// Commands: 30 & 31 - We now just send "V" (to confirm ESP firmware name and version) +int CESP32Hub::GetControllerVersion(int& version) +{ + // Here we use a char array to "send" and a string for "receive" + // Makes sense because we don't know how long the receive message will be + int ret = DEVICE_OK; + char command[50]; + string answer; + + LogMessage("Check ESP32 firmware name and version (>=1?)", false); + + // Flush the I/O buffer + PurgeComPort(port_.c_str()); + + // construct the command char array (limit to 50 characters).. here it's just one character + snprintf(( char* ) command, 50, "V"); + + ret = SendSerialCommand(port_.c_str(), command, "\r\n"); + if ( ret != DEVICE_OK ) return ret; + + // get the answer.. a String .. hence need to INCLUDE and + ret = GetSerialAnswer(port_.c_str(), "\r\n", answer); + // Flush the I/O buffer + PurgeComPort(port_.c_str()); + + if ( ret != DEVICE_OK ) return ret; + + if (answer.find("MM-ESP32") > 0 ) return ERR_BOARD_NOT_FOUND; + + // convert the "answer" string to a char array so we can parse it with sscanf + const char* anschar = answer.c_str(); + sscanf(( const char* ) anschar, "%8s,%d", command, &version); + + // version number should be => 1 + std::ostringstream os; + os << "Send/Receive:"<< port_.c_str() << " -> FirmwareQuery: " << answer; + LogMessage(os.str().c_str(), false); + + LogMessage("InitialisingStageRanges X,Y,Z", false); + + // now the XYZ stage setup + + double travelX, travelY, travelZ; + + ret = GetAxisInfo(0, travelX); + ret = GetAxisInfo(1, travelY); + if ( ( travelX > 0 ) && ( travelY > 0 ) ) hasXYStage_ = true; + + ret = GetAxisInfo(2, travelZ); + if ( travelZ > 0 ) hasZStage_ = true; + + return ret; +} + +// Command: "U" obtain stageRange for X, Y and Z axes +int CESP32Hub::GetAxisInfo(int axis, double& travel) +{ + + if ( axis < 0 || axis > 2 ) return DEVICE_ERR; + + std::stringstream cmd; + cmd.clear(); + + switch ( axis ) { + case 0: + cmd << "U,0"; // X-axis + break; + case 1: + cmd << "U,1"; // Y-axis + break; + case 2: + cmd << "U,2"; // Z-axis + break; + } + + // Flush the I/O buffer + PurgeComPort(port_.c_str()); + + int ret = SendSerialCommand(port_.c_str(), cmd.str().c_str(), "\r\n"); + if ( ret != DEVICE_OK ) return ret; + + std::string answer; + ret = GetSerialAnswer(port_.c_str(), "\r\n", answer); + if ( ret != DEVICE_OK ) return ret; + + // another way to play with string.. + std::stringstream ss(answer); + std::string type, trav; + getline(ss, type, ','); + getline(ss, trav, ','); + + if ( 0 != strcmp(type.c_str(), "U") ) return ERR_UNKNOWN_AXIS; + + std::stringstream sstravel(trav); + sstravel >> travel; + + return DEVICE_OK; +} + +bool CESP32Hub::SupportsDeviceDetection(void) +{ + return true; +} + +MM::DeviceDetectionStatus CESP32Hub::DetectDevice(void) +{ + if ( initialized_ ) return MM::CanCommunicate; + + // all conditions must be satisfied... + MM::DeviceDetectionStatus result = MM::Misconfigured; + char answerTO[MM::MaxStrLength]; + + try + { + std::string portLowerCase = port_; + for ( std::string::iterator its = portLowerCase.begin(); its != portLowerCase.end(); ++its ) + { + *its = ( char ) tolower(*its); + } + if ( 0 < portLowerCase.length() && 0 != portLowerCase.compare("undefined") && 0 != portLowerCase.compare("unknown") ) + { + result = MM::CanNotCommunicate; + // record the default answer time out + GetCoreCallback()->GetDeviceProperty(port_.c_str(), "AnswerTimeout", answerTO); + + // device specific default communication parameters + GetCoreCallback()->SetDeviceProperty(port_.c_str(), MM::g_Keyword_Handshaking, g_Off); + GetCoreCallback()->SetDeviceProperty(port_.c_str(), MM::g_Keyword_BaudRate, "115200"); + GetCoreCallback()->SetDeviceProperty(port_.c_str(), MM::g_Keyword_StopBits, "1"); + // ESP32 needs quite a long AnswerTimeout because it checks fo WiFi etc... + GetCoreCallback()->SetDeviceProperty(port_.c_str(), "AnswerTimeout", "5000.0"); + GetCoreCallback()->SetDeviceProperty(port_.c_str(), "DelayBetweenCharsMs", "0"); + MM::Device* pS = GetCoreCallback()->GetDevice(this, port_.c_str()); + + pS->Initialize(); + + // The first second or so after opening the serial port, the ESP32 is waiting for firmwareupgrades. Simply sleep 2 seconds. + CDeviceUtils::SleepMs(5000); + MMThreadGuard myLock(lock_); + PurgeComPort(port_.c_str()); + int v = 0; + int ret = GetControllerVersion(v); + + if ( DEVICE_OK != ret ) LogMessageCode(ret, false); + else result = MM::CanCommunicate; + + pS->Shutdown(); + + // always restore the AnswerTimeout to the default + GetCoreCallback()->SetDeviceProperty(port_.c_str(), "AnswerTimeout", answerTO); + + } + } + catch ( ... ) + { + LogMessage("Exception in DetectDevice!", false); + } + + return result; +} + +int CESP32Hub::Initialize() +{ + // Name + int ret = CreateProperty(MM::g_Keyword_Name, g_DeviceNameESP32Hub, MM::String, true); + if ( DEVICE_OK != ret ) + return ret; + + // Sleep for 2 seconds while ESP32 restarts. + CDeviceUtils::SleepMs(2000); + + MMThreadGuard myLock(lock_); + + // Check that we have a controller: + PurgeComPort(port_.c_str()); + ret = GetControllerVersion(version_); + if ( DEVICE_OK != ret ) + return ret; + + if ( version_ < g_Min_MMVersion ) + return ERR_VERSION_MISMATCH; + + CPropertyAction* pAct = new CPropertyAction(this, &CESP32Hub::OnVersion); + std::ostringstream sversion; + sversion << version_; + CreateProperty(g_versionProp, sversion.str().c_str(), MM::Integer, true, pAct); + + ret = UpdateStatus(); + if ( ret != DEVICE_OK ) return ret; + + // turn off verbose serial debug messages + // GetCoreCallback()->SetDeviceProperty(port_.c_str(), "Verbose", "0"); + + initialized_ = true; + return DEVICE_OK; +} + +int CESP32Hub::DetectInstalledDevices() +{ + if ( MM::CanCommunicate == DetectDevice() ) + { + std::vector peripherals; + peripherals.clear(); + peripherals.push_back(g_DeviceNameESP32Switch); + peripherals.push_back(g_DeviceNameESP32Shutter); + peripherals.push_back(g_DeviceNameESP32PWM0); + peripherals.push_back(g_DeviceNameESP32PWM1); + peripherals.push_back(g_DeviceNameESP32PWM2); + peripherals.push_back(g_DeviceNameESP32PWM3); + peripherals.push_back(g_DeviceNameESP32PWM4); + peripherals.push_back(g_DeviceNameStage); + peripherals.push_back(g_DeviceNameXYStage); + peripherals.push_back(g_DeviceNameESP32Input); + + for ( size_t i = 0; i < peripherals.size(); i++ ) + { + MM::Device* pDev = ::CreateDevice(peripherals[i].c_str()); + if ( pDev ) AddInstalledDevice(pDev); + } + } + + return DEVICE_OK; +} + +int CESP32Hub::Shutdown() +{ + initialized_ = false; + return DEVICE_OK; +} + +int CESP32Hub::OnPort(MM::PropertyBase* pProp, MM::ActionType pAct) +{ + if ( pAct == MM::BeforeGet ) + { + pProp->Set(port_.c_str()); + } + else if ( pAct == MM::AfterSet ) + { + pProp->Get(port_); + portAvailable_ = true; + } + return DEVICE_OK; +} + +int CESP32Hub::OnVersion(MM::PropertyBase* pProp, MM::ActionType pAct) +{ + if ( pAct == MM::BeforeGet ) + { + pProp->Set(( long ) version_); + } + return DEVICE_OK; +} + +int CESP32Hub::OnLogic(MM::PropertyBase* pProp, MM::ActionType pAct) +{ + if ( pAct == MM::BeforeGet ) + { + if ( invertedLogic_ ) + pProp->Set(g_invertedLogicString); + else + pProp->Set(g_normalLogicString); + } + else if ( pAct == MM::AfterSet ) + { + std::string logic; + pProp->Get(logic); + if ( logic.compare(g_invertedLogicString) == 0 ) + invertedLogic_ = true; + else invertedLogic_ = false; + } + return DEVICE_OK; +} + +/* CESP32Switch implementation: + Justin's Notes: + The "Switch Device" is registered with MM as a "State Device" + I don't know the full command set used by the MM config file which is + documented by "example" - I haven't found a detailed listing for that. + + Different switch states are attached to different "channels" - + That is done here, again in the config file, also in the ESP32 firmware + and finally the physical wiring of the GPIO pins. Very flexible but + the complexity can lead to confusion. +*/ + +// CESP32Switch implementation +CESP32Switch::CESP32Switch() : + nrPatternsUsed_(0), + currentDelay_(0), + sequenceOn_(false), + blanking_(false), + initialized_(false), + numPos_(12), + busy_(false) +{ + + + InitializeDefaultErrorMessages(); + + // add custom error messages + SetErrorText(ERR_UNKNOWN_POSITION, "Invalid position (state) specified"); + SetErrorText(ERR_INITIALIZE_FAILED, "Initialization of the device failed"); + SetErrorText(ERR_WRITE_FAILED, "Failed to write data to the device"); + SetErrorText(ERR_CLOSE_FAILED, "Failed closing the device"); + SetErrorText(ERR_COMMUNICATION, "Error in communication with ESP32 board"); + SetErrorText(ERR_NO_PORT_SET, "Hub Device not found. The ESP32 Hub device is needed to create this device"); + + for ( unsigned int i = 0; i < NUMPATTERNS; i++ ) + pattern_[i] = 0; + + // Description + int ret = CreateProperty(MM::g_Keyword_Description, "ESP32 digital output driver", MM::String, true); + assert(DEVICE_OK == ret); + + // Name + ret = CreateProperty(MM::g_Keyword_Name, g_DeviceNameESP32Switch, MM::String, true); + assert(DEVICE_OK == ret); + + // parent ID display + CreateHubIDProperty(); +} + +CESP32Switch::~CESP32Switch() +{ + Shutdown(); +} + +void CESP32Switch::GetName(char* name) const +{ + CDeviceUtils::CopyLimitedString(name, g_DeviceNameESP32Switch); +} + +int CESP32Switch::Initialize() +{ + CESP32Hub* hub = static_cast< CESP32Hub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) { + return ERR_NO_PORT_SET; + } + char hubLabel[MM::MaxStrLength]; + hub->GetLabel(hubLabel); + SetParentID(hubLabel); // for backward comp. + + // set property list + // ----------------- + + // In our ESP32 firmware we have "nDAC" switchable devices + // Currently, 4 LEDS. + // so 2^4 = 16 bit-mapped options + // We will define up to 256 char labels for 256 options ! + // 0="0", 2="2"...... 255="255" + // The ASCII labels are the decimal numbers representing each bit-mapped switch pattern + // (see below for explanation) + + //Create the text labels that we will use in the config file + const int bufSize = 4; + char buf[bufSize]; + for ( long i = 0; i < 256; i++ ) { + snprintf(( char* ) buf, bufSize, "%d", ( unsigned ) i); + SetPositionLabel(i, buf); + } + + /* E.g. if you have different LED light sources powered by the different ESP32 O/P pins + B, G, Y, R < colour of the LED + 3, 2, 1, 0 < bit position + 8, 4, 2, 1 < decimal number when set to "1" at that bit position + 1, 0, 0, 0, = "Blue only" requires this bit pattern = 16 Decimal + And if you want RGB simultaneously you send: + 1, 1, 0, 1 = 8+4+1 = 13 Decimal + To make this easier you list the options you want available in the Config file like this: + +*** example config file: The comma-delimited fields need to be documented.. add HTTP: here.. +Label,ESP32-Switch,0,All_OFF +Label,ESP32-Switch,1,Red +Label,ESP32-Switch,2,Yellow +Label,ESP32-Switch,4,Green +Label,ESP32-Switch,8,Blue +Label,ESP32-Switch,13,RedGreenBlue + +ConfigGroup,Channel,Red_LED,ESP32-Switch,Label,Red +ConfigGroup,Channel,Yellow_LED,ESP32-Switch,Label,Yellow +ConfigGroup,Channel,Green_LED,ESP32-Switch,Label,Green +ConfigGroup,Channel,Blue_LED,ESP32-Switch,Label,Blue +ConfigGroup,Channel,Red_Green_Blue_LEDs,ESP32-Switch,Label,RedGreenBlue +*** + + */ + // State + CPropertyAction* pAct = new CPropertyAction(this, &CESP32Switch::OnState); + int nRet = CreateProperty(MM::g_Keyword_State, "0", MM::Integer, false, pAct); + if ( nRet != DEVICE_OK ) return nRet; + SetPropertyLimits(MM::g_Keyword_State, 0, 255); + + // Label + pAct = new CPropertyAction(this, &CStateBase::OnLabel); + nRet = CreateProperty(MM::g_Keyword_Label, "", MM::String, false, pAct); + if ( nRet != DEVICE_OK ) + return nRet; + + pAct = new CPropertyAction(this, &CESP32Switch::OnSequence); + nRet = CreateProperty("Sequence", g_On, MM::String, false, pAct); + if ( nRet != DEVICE_OK ) + return nRet; + AddAllowedValue("Sequence", g_On); + AddAllowedValue("Sequence", g_Off); + + // Starts "blanking" mode: goal is to synchronize laser light with camera exposure + std::string blankMode = "Blanking Mode"; + pAct = new CPropertyAction(this, &CESP32Switch::OnBlanking); + nRet = CreateProperty(blankMode.c_str(), "Idle", MM::String, false, pAct); + if ( nRet != DEVICE_OK ) return nRet; + + AddAllowedValue(blankMode.c_str(), g_On); + AddAllowedValue(blankMode.c_str(), g_Off); + + // Blank on TTL high or low + pAct = new CPropertyAction(this, &CESP32Switch::OnBlankingTriggerDirection); + nRet = CreateProperty("Blank On", "Low", MM::String, false, pAct); + if ( nRet != DEVICE_OK ) + return nRet; + AddAllowedValue("Blank On", "Low"); + AddAllowedValue("Blank On", "High"); + + /* + // Some original comments: + // but SADLY, the code itself has been commented out + // In fact, looks like a useful thing to include...To Do. + // ////////////////////////////////////////////////////// + // Starts producing timed digital output patterns + // Parameters that influence the pattern are 'Repeat Timed Pattern', 'Delay', 'State' + // where the latter two are manipulated with the Get and SetPattern functions + + std::string timedOutput = "Timed Output Mode"; + pAct = new CPropertyAction(this, &CESP32Switch::OnStartTimedOutput); + nRet = CreateProperty(timedOutput.c_str(), "Idle", MM::String, false, pAct); + if (nRet != DEVICE_OK) + return nRet; + AddAllowedValue(timedOutput.c_str(), "Stop"); + AddAllowedValue(timedOutput.c_str(), "Start"); + AddAllowedValue(timedOutput.c_str(), "Running"); + AddAllowedValue(timedOutput.c_str(), "Idle"); + + // Sets a delay (in ms) to be used in timed output mode + // This delay will be transferred to the ESP32 using the Get and SetPattern commands + pAct = new CPropertyAction(this, &CESP32Switch::OnDelay); + nRet = CreateProperty("Delay (ms)", "0", MM::Integer, false, pAct); + if (nRet != DEVICE_OK) + return nRet; + SetPropertyLimits("Delay (ms)", 0, 65535); + + // Repeat the timed Pattern this many times: + pAct = new CPropertyAction(this, &CESP32Switch::OnRepeatTimedPattern); + nRet = CreateProperty("Repeat Timed Pattern", "0", MM::Integer, false, pAct); + if (nRet != DEVICE_OK) + return nRet; + SetPropertyLimits("Repeat Timed Pattern", 0, 255); + */ + + nRet = UpdateStatus(); + if ( nRet != DEVICE_OK ) return nRet; + + initialized_ = true; + + return DEVICE_OK; +} + +int CESP32Switch::Shutdown() +{ + initialized_ = false; + return DEVICE_OK; +} + +// I am assuming when this function (method) is called the passed parameter "value" +// is the bitmap pattern that is to be written to the switches (if bit is 0 the output is turned off +// (electronically gated) if the bit is 1 then the output goes to it's preset value. + +// command: 1 now "S" Set current digital output pattern - The "CESP32Shutter" NO LONGER uses this command! +int CESP32Switch::WriteToPort(long value) +{ + CESP32Hub* hub = static_cast< CESP32Hub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) return ERR_NO_PORT_SET; + + // keep the low byte only.. we might want up to 8 lines later + // Top bit (128) is the master shutter + value = 255 & value; + if ( hub->IsLogicInverted() ) value = ~value; + + MMThreadGuard myLock(hub->GetLock()); + hub->PurgeComPortH(); + int ret = DEVICE_OK; + char command[50]; + unsigned int leng; + + leng = snprintf(( char* ) command, 50, "S,%d\r\n", value); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + + // Purge the IO buffer in case ESP has sent a response! + hub->PurgeComPortH(); + hub->SetTimedOutput(false); + + std::ostringstream os; + os << "Switch::WriteToPort Command= " << command; + LogMessage(os.str().c_str(), false); + + return DEVICE_OK; +} + +// Commands: 5 & 6 now "P" and "N" = store new "P"atterns and "N"umber of patterns +int CESP32Switch::LoadSequence(unsigned size, unsigned char* seq) +{ + + std::ostringstream os; + os << "Switch::LoadSequence size= " << size <<" Seq= " << seq; + LogMessage(os.str().c_str(), false); + + CESP32Hub* hub = static_cast< CESP32Hub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) + return ERR_NO_PORT_SET; + + // preamble for all port reads and writes + MMThreadGuard myLock(hub->GetLock()); + hub->PurgeComPortH(); + int ret = DEVICE_OK; + char command[50]; + unsigned int leng; + + for ( unsigned i = 0; i < size; i++ ) + { + unsigned char value = seq[i]; + + value = 255 & value; + if ( hub->IsLogicInverted() ) value = ~value; + + leng = snprintf(( char* ) command, 50, "P,%d,%d\r\n", i, value); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + } + + leng = snprintf(( char* ) command, 50, "N,%d\r\n", size); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + + // Purge the IO buffer in case ESP has sent a response! + hub->SetTimedOutput(false); + hub->PurgeComPortH(); + + return DEVICE_OK; +} + +// Action handlers + +// Commands: 8 & 9 now "R" and "E" Run and End Trigger mode resp. +int CESP32Switch::OnState(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + + std::ostringstream os; + os << "Switch::OnState_"; + LogMessage(os.str().c_str(), false); + + CESP32Hub* hub = static_cast< CESP32Hub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) + return ERR_NO_PORT_SET; + + // Some comments here would be helpful + if ( eAct == MM::BeforeGet ) + { + // nothing to do, let the caller use cached property ??? + } + else if ( eAct == MM::AfterSet ) + { + + // **** a comment here would be very helpful... + // where are we GETting "pos" value from? + long pos; + pProp->Get(pos); + +// this is obscure - I can guess what it does.. but, I'm not going to say + hub->SetSwitchState(pos); + + if ( hub->GetShutterState() > 0 ) // I don't like this because of confusion with "shutterDevice" + os << "_Pos= " << pos <<" WriteToPort(pos)?"; + LogMessage(os.str().c_str(), false); + return WriteToPort(pos); // I understand this! + } + else if ( eAct == MM::IsSequenceable ) + { + if ( sequenceOn_ ) + pProp->SetSequenceable(NUMPATTERNS); + else + pProp->SetSequenceable(0); + } + else if ( eAct == MM::AfterLoadSequence ) + { + std::vector sequence = pProp->GetSequence(); + + if ( sequence.size() > NUMPATTERNS ) return DEVICE_SEQUENCE_TOO_LARGE; + + unsigned char* seq = new unsigned char[sequence.size()]; + for ( unsigned int i = 0; i < sequence.size(); i++ ) + { + std::istringstream ios(sequence[i]); + int val; + ios >> val; + seq[i] = ( unsigned char ) val; + } + + int ret = LoadSequence(( unsigned ) sequence.size(), seq); + if ( ret != DEVICE_OK ) + return ret; + + delete[ ] seq; + } + else if ( eAct == MM::StartSequence ) + { + MMThreadGuard myLock(hub->GetLock()); + +// preamble for all port reads and writes + hub->PurgeComPortH(); + int ret = DEVICE_OK; + char command[50]; + unsigned int leng; + + leng = snprintf(( char* ) command, 50, "R\r\n"); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + + } + else if ( eAct == MM::StopSequence ) + { + MMThreadGuard myLock(hub->GetLock()); + + // preamble for all port reads and writes + hub->PurgeComPortH(); + int ret = DEVICE_OK; + char command[50]; + unsigned int leng; + + leng = snprintf(( char* ) command, 50, "E\r\n"); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + + // This code needs to be improved! + // We expect a reply like "E,23456\r\n" + + MM::MMTime startTime = GetCurrentMMTime(); + unsigned long bytesRead = 0; + unsigned char answer[10]; + + // Now chug through the I/O buffer, byte-by-byte and stop at cr or lf.... yuk! + while ( ( bytesRead < 10 ) && ( ( GetCurrentMMTime() - startTime ).getMsec() < 250 ) ) { + unsigned long br; + ret = hub->ReadFromComPortH(( unsigned char* ) answer + bytesRead, 1, br); + if ( answer[bytesRead] == '\r' ) break; + bytesRead += br; + } + + answer[bytesRead] = 0; // string terminator + + hub->SetTimedOutput(false); + hub->PurgeComPortH(); + if ( ret != DEVICE_OK ) return ret; + + int num; + char com[1]; + sscanf(( const char* ) answer, "%1s,%d", com, &num); + // should give com[0]='E' num = 23456 (or something!) + + if ( com[0] != 'E' ) return ERR_COMMUNICATION; + + os << "Sequence_Transitions: " << num; + LogMessage(os.str().c_str(), false); + } + + return DEVICE_OK; +} +int CESP32Switch::OnSequence(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if ( eAct == MM::BeforeGet ) + { + if ( sequenceOn_ ) + pProp->Set(g_On); + else + pProp->Set(g_Off); + } + else if ( eAct == MM::AfterSet ) + { + std::string state; + pProp->Get(state); + if ( state == g_On ) + sequenceOn_ = true; + else + sequenceOn_ = false; + } + + std::ostringstream os; + os << "Switch::OnSequence_SeqOn= " << sequenceOn_; + LogMessage(os.str().c_str(), false); + + + return DEVICE_OK; +} + +// Commands: 12 & 9 now = "G" and "E" +int CESP32Switch::OnStartTimedOutput(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + + std::ostringstream os; + os << "Switch::OnStartTimedOutput "; + LogMessage(os.str().c_str(), false); + + CESP32Hub* hub = static_cast< CESP32Hub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) + return ERR_NO_PORT_SET; + + if ( eAct == MM::BeforeGet ) { + if ( hub->IsTimedOutputActive() ) pProp->Set("Running"); + else pProp->Set("Idle"); + } + else if ( eAct == MM::AfterSet ) + { + MMThreadGuard myLock(hub->GetLock()); + + std::string prop; + pProp->Get(prop); + + if ( prop == "Start" ) { + // preamble for port read and write + hub->PurgeComPortH(); + int ret = DEVICE_OK; + char command[50]; + unsigned int leng; + + leng = snprintf(( char* ) command, 50, "G\r\n"); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + + hub->SetTimedOutput(true); // Check this *********** + + } + else { + // preamble for port read and write + hub->PurgeComPortH(); + int ret = DEVICE_OK; + char command[50]; + unsigned int leng; + leng = snprintf(( char* ) command, 50, "E\r\n"); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + + hub->SetTimedOutput(false); + } + } + + return DEVICE_OK; +} + +// Commands: 20 & 21 now = "B,1" "B,0" now Blanking on(1) or Blanking off (0) +int CESP32Switch::OnBlanking(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + std::ostringstream os; + os << "Switch::OnBlanking "; + LogMessage(os.str().c_str(), false); + + CESP32Hub* hub = static_cast< CESP32Hub* >( GetParentHub() ); + + if ( !hub || !hub->IsPortAvailable() ) return ERR_NO_PORT_SET; + + if ( eAct == MM::BeforeGet ) { + if ( blanking_ ) pProp->Set(g_On); + else pProp->Set(g_Off); + } + else if ( eAct == MM::AfterSet ) + { + MMThreadGuard myLock(hub->GetLock()); + + std::string prop; + pProp->Get(prop); + + if ( prop == g_On && !blanking_ ) { + hub->PurgeComPortH(); + int ret = DEVICE_OK; + char command[50]; + unsigned int leng; + + leng = snprintf(( char* ) command, 50, "B,1\r\n"); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + + blanking_ = true; + hub->SetTimedOutput(false); + LogMessage("Switched blanking on", false); + + } + else if ( prop == g_Off && blanking_ ) { + hub->PurgeComPortH(); + int ret = DEVICE_OK; + char command[50]; + unsigned int leng; + + leng = snprintf(( char* ) command, 50, "B,0\r\n"); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + + blanking_ = false; + hub->SetTimedOutput(false); + LogMessage("Switched blanking off", false); + } + } + return DEVICE_OK; +} + +// Command: 22 now = "F,0" or "F,1" Flip polarity of trigger signal +int CESP32Switch::OnBlankingTriggerDirection(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + + std::ostringstream os; + os << "Switch::OnBlankingTriggerDirection"; + LogMessage(os.str().c_str(), false); + + CESP32Hub* hub = static_cast< CESP32Hub* >( GetParentHub() ); + + if ( !hub || !hub->IsPortAvailable() ) return ERR_NO_PORT_SET; + + // Perhaps shorten to one line and clarify logic? + // Only execute if eAct has already been got and set. + // if ((eAct != MM::BeforeGet) && (eAct == MM::AfterSet)) { + + if ( eAct == MM::BeforeGet ) { + // nothing to do, let the caller use cached property + } + else if ( eAct == MM::AfterSet ) + { + + MMThreadGuard myLock(hub->GetLock()); + + std::string direction; + pProp->Get(direction); + hub->PurgeComPortH(); + int ret = DEVICE_OK; + char command[50]; + unsigned int leng; + + int dir = 0; + if ( direction == "Low" ) dir = 1; + leng = snprintf(( char* ) command, 50, "F,%d\r\n", dir); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + + hub->SetTimedOutput(false); + } + return DEVICE_OK; +} +int CESP32Switch::OnDelay(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + std::ostringstream os; + os << "Switch::OnDelay "; + LogMessage(os.str().c_str(), false); + + if ( eAct == MM::BeforeGet ) { + pProp->Set(( long int ) currentDelay_); + } + else if ( eAct == MM::AfterSet ) + { + long prop; + pProp->Get(prop); + currentDelay_ = ( int ) prop; + } + + return DEVICE_OK; +} + +// Command: 11 now = "I" set number of "I"terations +int CESP32Switch::OnRepeatTimedPattern(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + std::ostringstream os; + os << "Switch::OnRepeatTimedPattern "; + LogMessage(os.str().c_str(), false); + + CESP32Hub* hub = static_cast< CESP32Hub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) return ERR_NO_PORT_SET; + + // Perhaps shorten and clarify logic to one line? + // if ((eAct != MM::BeforeGet) && (eAct == MM::AfterSet)) { + + if ( eAct == MM::BeforeGet ) { + } + else if ( eAct == MM::AfterSet ) + { + MMThreadGuard myLock(hub->GetLock()); + + long prop; + pProp->Get(prop); + + hub->PurgeComPortH(); + int ret = DEVICE_OK; + char command[50]; + unsigned int leng; + leng = snprintf(( char* ) command, 50, "I,%d\r\n", prop); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + + hub->SetTimedOutput(false); + } + + return DEVICE_OK; +} + +// CESP32Shutter implementation +// Justin Notes: I'm a bit mystified by the "shutter" and when it is called. Currently it issues a "Switch" +// command - I find that confusing. + +// CESP32Shutter implementation +CESP32Shutter::CESP32Shutter() : initialized_(false), name_(g_DeviceNameESP32Shutter) +{ + InitializeDefaultErrorMessages(); + EnableDelay(); + + SetErrorText(ERR_NO_PORT_SET, "Hub Device not found. The ESP32 Hub device is needed to create this device"); + + // Name + int ret = CreateProperty(MM::g_Keyword_Name, g_DeviceNameESP32Shutter, MM::String, true); + assert(DEVICE_OK == ret); + + // Description + ret = CreateProperty(MM::g_Keyword_Description, "ESP32 shutter driver", MM::String, true); + assert(DEVICE_OK == ret); + + // parent ID display + CreateHubIDProperty(); +} +CESP32Shutter::~CESP32Shutter() +{ + Shutdown(); +} +void CESP32Shutter::GetName(char* name) const +{ + CDeviceUtils::CopyLimitedString(name, g_DeviceNameESP32Shutter); +} +bool CESP32Shutter::Busy() +{ + MM::MMTime interval = GetCurrentMMTime() - changedTime_; + + return interval < MM::MMTime::fromMs(GetDelayMs()); +} +int CESP32Shutter::Initialize() +{ + CESP32Hub* hub = static_cast< CESP32Hub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) { + return ERR_NO_PORT_SET; + } + char hubLabel[MM::MaxStrLength]; + hub->GetLabel(hubLabel); + SetParentID(hubLabel); // for backward comp. + + // set property list + // ----------------- + + // removed comment from OnOff + // set shutter into the off state ???wot + // WriteToPort(0); + + // OnOff + CPropertyAction* pAct = new CPropertyAction(this, &CESP32Shutter::OnOnOff); + int ret = CreateProperty("OnOff", "0", MM::Integer, false, pAct); + if ( ret != DEVICE_OK ) return ret; + + std::vector vals; + vals.push_back("0"); + vals.push_back("1"); + ret = SetAllowedValues("OnOff", vals); + if ( ret != DEVICE_OK ) return ret; + + ret = UpdateStatus(); + if ( ret != DEVICE_OK ) return ret; + + changedTime_ = GetCurrentMMTime(); + initialized_ = true; + + return DEVICE_OK; +} +int CESP32Shutter::Shutdown() +{ + if ( initialized_ ) + { + initialized_ = false; + } + return DEVICE_OK; +} +int CESP32Shutter::SetOpen(bool open) +{ + std::ostringstream os; + os << "Shutter::SetOpen open= " << open; + LogMessage(os.str().c_str(), false); + + if ( open ) return SetProperty("OnOff", "1"); + else return SetProperty("OnOff", "0"); +} +int CESP32Shutter::GetOpen(bool& open) +{ + std::ostringstream os; + os << "Shutter::GetOpen open= " << open; + LogMessage(os.str().c_str(), false); + + char buf[MM::MaxStrLength]; + int ret = GetProperty("OnOff", buf); + if ( ret != DEVICE_OK ) return ret; + + long pos = atol(buf); + pos > 0 ? open = true : open = false; + + return DEVICE_OK; +} +int CESP32Shutter::Fire(double /*deltaT*/) +{ + return DEVICE_UNSUPPORTED_COMMAND; +} + +// e.g. "x0101100" turns off channels 0,1,5,7.. +// Note: channels 2,3,6 will switch on at their preset output level.. "shutter" is messing with the switches! +// must try to separate church and state. + +// Command: 1 Now "H,1/0" Set s"H"utter open or closed (this can be hardware or electronic shuttering) or both! +int CESP32Shutter::WriteToPort(long value) +{ + CESP32Hub* hub = static_cast< CESP32Hub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) + return ERR_NO_PORT_SET; + + MMThreadGuard myLock(hub->GetLock()); + + // keep the lowest 8 bits - + value = value & 255; + if ( hub->IsLogicInverted() ) value = ~value; + + hub->PurgeComPortH(); + int ret = DEVICE_OK; + // create command buffer + char command[50]; + unsigned int leng; + + // load command buffer with the command string "lf" terminated. + leng = snprintf(( char* ) command, 50, "H,%d\r\n", value); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + + hub->SetTimedOutput(false); + + std::ostringstream os; + os << "Shutter::WriteToPort Command= " << command; + LogMessage(os.str().c_str(), false); + + return DEVICE_OK; +} + +// I think this method should be part of the "Switch" Class and should call Switch::WritetoPort +// This method is called in an inapproriate manner during MDA.. need to sort it out +// OnOnOff should be moved to "Switch::" : it's called during MDA and operates the "switch" indirectly +// by issuing the "S" command + +// Action handlers - don't seem to work correctly at present. I've modded it and it now fails! +int CESP32Shutter::OnOnOff(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + CESP32Hub* hub = static_cast< CESP32Hub* >( GetParentHub() ); + if ( eAct == MM::BeforeGet ) + { + // use cached state + pProp->Set(( long ) hub->GetShutterState()); + } + else if ( eAct == MM::AfterSet ) + { + long pos; + pProp->Get(pos); + int ret; + if ( pos == 0 ) + ret = WriteToPort(0); // Shutter closed (and/or write zeros to all LEDs) + else + //ret = WriteToPort(1); // Shutter open (and/or restore LED O/P pattern) + // Set top bit to open shutter + // ret = WriteToPort(pos | 128); + ret = WriteToPort((hub->GetSwitchState()) | 128); // restore old setting NO - let the ESP32 do this logic! + + if ( ret != DEVICE_OK ) return ret; + + hub->SetShutterState(pos); + changedTime_ = GetCurrentMMTime(); + } + + std::ostringstream os; + os << "Shutter::OnOnOff hub->GetSwitchState()= " << hub->GetSwitchState(); + LogMessage(os.str().c_str(), false); + + return DEVICE_OK; +} + +// CESP32DA implementation: +// Justin's Notes: +// This "Device" only sends one command "O,chan,volts" +// Summary: +// => OnVolts Action Handler is called by an event handler in MM. +// It subsequently calls: +// => SetSignal which optionally "gates" the O/P to "zero Volts" +// => WriteSignal scales the value to 12 bits (but now does nothing!) +// => WritetoPort - Sends "O,(int) chan,(float) volts" to ESP32 +// => OnMaxVolts & => OnChannel just keep Volts and Channels within bounds +// +// If we need anything fast or device-specific do it on the ESP32. +// USB transmit/receive latency will dominate. + +// CESP32DA implementation +CESP32DA::CESP32DA(int channel) : + busy_(false), + minV_(0.0), + maxV_(100.0), + volts_(0.0), + gatedVolts_(0.0), + channel_(channel), + maxChannel_(7), + gateOpen_(true) +{ + InitializeDefaultErrorMessages(); + + // add custom error messages + SetErrorText(ERR_UNKNOWN_POSITION, "Invalid position (state) specified"); + SetErrorText(ERR_INITIALIZE_FAILED, "Initialization of the device failed"); + SetErrorText(ERR_WRITE_FAILED, "Failed to write data to the device"); + SetErrorText(ERR_CLOSE_FAILED, "Failed closing the device"); + SetErrorText(ERR_NO_PORT_SET, "Hub Device not found. The ESP32 Hub device is needed to create this device"); + + /* Channel property is not needed + CPropertyAction* pAct = new CPropertyAction(this, &CESP32DA::OnChannel); + CreateProperty("Channel", channel_ == 1 ? "1" : "2", MM::Integer, false, pAct, true); + for (int i=1; i<= 2; i++){ + std::ostringstream os; + os << i; + AddAllowedValue("Channel", os.str().c_str()); + } + */ + + CPropertyAction* pAct = new CPropertyAction(this, &CESP32DA::OnMaxVolt); + CreateProperty("Power %", "100", MM::Float, false, pAct, true); + + if (channel_ == 0) name_ = g_DeviceNameESP32PWM0; + else if (channel_ == 1) name_ = g_DeviceNameESP32PWM1; + else if (channel_ == 2) name_ = g_DeviceNameESP32PWM2; + else if (channel_ == 3) name_ = g_DeviceNameESP32PWM3; + else if (channel_ == 4) name_ = g_DeviceNameESP32PWM4; + + //name_ = channel_ == 1 ? g_DeviceNameESP32DA1 : g_DeviceNameESP32DA2; + + // Description + int nRet = CreateProperty(MM::g_Keyword_Description, "ESP32 DAC driver", MM::String, true); + assert(DEVICE_OK == nRet); + + // Name + nRet = CreateProperty(MM::g_Keyword_Name, name_.c_str(), MM::String, true); + assert(DEVICE_OK == nRet); + + // parent ID display + CreateHubIDProperty(); +} +CESP32DA::~CESP32DA() +{ + Shutdown(); +} +void CESP32DA::GetName(char* name) const +{ + CDeviceUtils::CopyLimitedString(name, name_.c_str()); +} +int CESP32DA::Initialize() +{ + CESP32Hub* hub = static_cast(GetParentHub()); + if (!hub || !hub->IsPortAvailable()) { + return ERR_NO_PORT_SET; + } + char hubLabel[MM::MaxStrLength]; + hub->GetLabel(hubLabel); + SetParentID(hubLabel); // for backward comp. + + // set property list + // ----------------- + + // State + // ----- + CPropertyAction* pAct = new CPropertyAction(this, &CESP32DA::OnVolts); + int nRet = CreateProperty("Volts", "0.0", MM::Float, false, pAct); + if (nRet != DEVICE_OK) + return nRet; + SetPropertyLimits("Volts", minV_, maxV_); + + nRet = UpdateStatus(); + + if (nRet != DEVICE_OK) return nRet; + + initialized_ = true; + + return DEVICE_OK; +} +int CESP32DA::Shutdown() +{ + initialized_ = false; + return DEVICE_OK; +} + +// Command: 3 now = "O,chan,value" Output DAC value +int CESP32DA::WriteToPort(double value) +{ + CESP32Hub* hub = static_cast(GetParentHub()); + if (!hub || !hub->IsPortAvailable()) + return ERR_NO_PORT_SET; + + MMThreadGuard myLock(hub->GetLock()); + + hub->PurgeComPortH(); + int ret = DEVICE_OK; + char command[50]; + unsigned int leng; + + leng = snprintf((char*)command, 50, "O,%d,%3.3f\r\n", ( int ) channel_, value); + ret = hub->WriteToComPortH((const unsigned char*)command, leng); + if (ret != DEVICE_OK) return ret; + + hub->SetTimedOutput(false); + + return DEVICE_OK; +} +int CESP32DA::WriteSignal(double volts) +{ + double value = volts; // ( (volts - minV_) / maxV_ * 4095); + + std::ostringstream os; + os << "Volts= " << volts << " MaxVoltage= " << maxV_ << " digitalValue= " << value; + LogMessage(os.str().c_str(), false); + + return WriteToPort(value); +} +int CESP32DA::SetSignal(double volts) +{ + volts_ = volts; + if (gateOpen_) { + gatedVolts_ = volts_; + return WriteSignal(volts_); + } + else { + gatedVolts_ = 0; + } + + return DEVICE_OK; +} +int CESP32DA::SetGateOpen(bool open) +{ + if (open) { + gateOpen_ = true; + gatedVolts_ = volts_; + return WriteSignal(volts_); + } + gateOpen_ = false; + gatedVolts_ = 0; + return WriteSignal(0.0); + +} + +// Action handlers +int CESP32DA::OnVolts(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::BeforeGet) + { + // nothing to do, let the caller use cached property + } + else if (eAct == MM::AfterSet) + { + double volts; + pProp->Get(volts); + return SetSignal(volts); + } + + return DEVICE_OK; +} +int CESP32DA::OnMaxVolt(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::BeforeGet) + { + pProp->Set(maxV_); + } + else if (eAct == MM::AfterSet) + { + pProp->Get(maxV_); + if (HasProperty("Volts")) + SetPropertyLimits("Volts", 0.0, maxV_); + + } + return DEVICE_OK; +} +int CESP32DA::OnChannel(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::BeforeGet) + { + pProp->Set((long int)channel_); + } + else if (eAct == MM::AfterSet) + { + long channel; + pProp->Get(channel); + if (channel >= 0 && ((unsigned)channel <= maxChannel_)) + channel_ = channel; + } + return DEVICE_OK; +} + +/* +Below, I have folded-in (and modified) the PIEZOCONCEPT XYZ stage adapter +so we can control focus and move an x-y stage connected to our ESP32 board (see firmware). +So, we have two new blocks of code: + +CESP32Stage & CESP32XYStage + +Notes: +Stage and Focus noise: +On the ESP side, I gave the XYZ channels 16-bit resolution (which allows only 1kHz PWM (Fpwm) on ESP32). +If we low-pass filter to 1 Hz (Fc) we will still get "Ao * (Fc/Fpwm)" of p-p PWM ripple noise +which is too high for most applications. +Suggest using 20-bit DACs (e.g. Analog devices: DAC1220) which can be 'chip selected' using +the "DAC" GPIO pins (see ESP32 Firmware code) and values sent using the ESP SPI interface. +Easy to "mod" the ESP32 firmware and leave this code unchanged. +*/ + +// CESP32Stage Implementation +CESP32Stage::CESP32Stage() : + stepSizeUm_(0.0001), + pos_um_(0.0), + busy_(false), + initialized_(false), + lowerLimit_(0.0), + upperLimit_(100.0) +{ + InitializeDefaultErrorMessages(); + + CreateHubIDProperty(); +} + +CESP32Stage::~CESP32Stage() +{ + Shutdown(); +} + +void CESP32Stage::GetName(char* Name) const +{ + CDeviceUtils::CopyLimitedString(Name, g_DeviceNameStage); +} + +int CESP32Stage::Initialize() +{ + CESP32Hub* hub = static_cast< CESP32Hub* >( GetParentHub() ); + + if ( !hub || !hub->IsPortAvailable() ) return ERR_NO_PORT_SET; + + char hubLabel[MM::MaxStrLength]; + hub->GetLabel(hubLabel); + SetParentID(hubLabel); // for backward comp. + + if ( initialized_ ) return DEVICE_OK; + + // Name + int ret = CreateProperty(MM::g_Keyword_Name, g_DeviceNameStage, MM::String, true); + if ( DEVICE_OK != ret ) return ret; + + // Description + ret = CreateProperty(MM::g_Keyword_Description, "ESP32 Z stage driver", MM::String, true); + if ( DEVICE_OK != ret ) return ret; + + double travel; + + ret = hub->GetAxisInfoH(2, travel); + if ( DEVICE_OK != ret ) return ret; + + upperLimit_ = travel; + //Should we initialise stepsize as we do for X- Y- axes + //Suggest e.g. perhaps 10 digital bits per mouse-wheel click? + //stepSizeUm_ = travel / 6553.6; + + ret = UpdateStatus(); + if ( ret != DEVICE_OK ) return ret; + + initialized_ = true; + + std::ostringstream os; + os << "Stage::Initialize (exit)"; + LogMessage(os.str().c_str(), false); + + return DEVICE_OK; +} + +int CESP32Stage::Shutdown() +{ + if ( initialized_ ) initialized_ = false; + return DEVICE_OK; +} + +int CESP32Stage::GetPositionUm(double& pos) +{ + pos = pos_um_; + return DEVICE_OK; +} + +int CESP32Stage::SetPositionUm(double pos) +{ + int ret = MoveZ(pos); + if ( ret != DEVICE_OK ) return ret; + return OnStagePositionChanged(pos); +} + +int CESP32Stage::GetPositionSteps(long& steps) +{ + double posUm; + int ret = GetPositionUm(posUm); + if ( ret != DEVICE_OK ) return ret; + + steps = static_cast< long >( posUm / GetStepSize() ); + return DEVICE_OK; +} + +int CESP32Stage::SetPositionSteps(long steps) +{ + return SetPositionUm(steps * GetStepSize()); +} + +// "Z" command - move to new Z-position (Focus up/down) +int CESP32Stage::MoveZ(double pos) +{ + CESP32Hub* hub = static_cast< CESP32Hub* >( GetParentHub() ); + + if ( !hub || !hub->IsPortAvailable() ) return ERR_HUB_UNAVAILABLE; + + if ( pos > upperLimit_ ) pos = upperLimit_; + if ( pos < lowerLimit_ ) pos = lowerLimit_; + + char buf[25]; + int length = sprintf(buf, "Z,%3.3f\r\n", pos); + + std::stringstream ss; + ss << "Command: " << buf << " Position set: " << pos; + LogMessage(ss.str().c_str(), false); + + MMThreadGuard myLock(hub->GetLock()); + hub->PurgeComPortH(); + + int ret = hub->WriteToComPortH(( unsigned char* ) buf, length); + if ( ret != DEVICE_OK ) return ret; + + MM::MMTime startTime = GetCurrentMMTime(); + unsigned long bytesRead = 0; + unsigned char answer[10]; + while ( ( bytesRead < 10 ) && ( ( GetCurrentMMTime() - startTime ).getMsec() < 250 ) ) { + unsigned long br; + ret = hub->ReadFromComPortH(( unsigned char* ) answer + bytesRead, 1, br); + if ( answer[bytesRead] == '\r' ) break; + bytesRead += br; + } + + answer[bytesRead] = 0; // string terminator + + hub->PurgeComPortH(); + if ( ret != DEVICE_OK ) return ret; + + if ( answer[0] != 'Z' ) return ERR_COMMUNICATION; + + hub->SetTimedOutput(false); + + std::ostringstream os; + os << "MoveZ Z," << pos; + LogMessage(os.str().c_str(), false); + + pos_um_ = pos; + + return DEVICE_OK; +} + +bool CESP32Stage::Busy() +{ + return false; +} + +CESP32XYStage::CESP32XYStage() : CXYStageBase(), +stepSize_X_um_(0.1), +stepSize_Y_um_(0.1), +posX_um_(0.0), +posY_um_(0.0), +busy_(false), +initialized_(false), +lowerLimitX_(0.0), +upperLimitX_(200.0), +lowerLimitY_(0.0), +upperLimitY_(200.0) +{ + InitializeDefaultErrorMessages(); + + // parent ID display + CreateHubIDProperty(); + + // step size + CPropertyAction* pAct = new CPropertyAction(this, &CESP32XYStage::OnXStageMinPos); + CreateProperty(g_PropertyXMinUm, "0", MM::Float, false, pAct, true); + + pAct = new CPropertyAction(this, &CESP32XYStage::OnXStageMaxPos); + CreateProperty(g_PropertyXMaxUm, "200", MM::Float, false, pAct, true); + + pAct = new CPropertyAction(this, &CESP32XYStage::OnYStageMinPos); + CreateProperty(g_PropertyYMinUm, "0", MM::Float, false, pAct, true); + + pAct = new CPropertyAction(this, &CESP32XYStage::OnYStageMaxPos); + CreateProperty(g_PropertyYMaxUm, "200", MM::Float, false, pAct, true); +} + +CESP32XYStage::~CESP32XYStage() +{ + Shutdown(); +} + +void CESP32XYStage::GetName(char* Name) const +{ + CDeviceUtils::CopyLimitedString(Name, g_DeviceNameXYStage); +} + +int CESP32XYStage::Initialize() +{ + CESP32Hub* hub = static_cast< CESP32Hub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) return ERR_NO_PORT_SET; + + char hubLabel[MM::MaxStrLength]; + hub->GetLabel(hubLabel); + SetParentID(hubLabel); // for backward comp. + + if ( initialized_ ) return DEVICE_OK; + + // Name + int ret = CreateProperty(MM::g_Keyword_Name, g_DeviceNameXYStage, MM::String, true); + if ( DEVICE_OK != ret ) return ret; + + // Description + ret = CreateProperty(MM::g_Keyword_Description, "XY stage driver", MM::String, true); + if ( DEVICE_OK != ret ) return ret; + + double travelX, travelY; + ret = hub->GetAxisInfoH(0, travelX); + if ( DEVICE_OK != ret ) return ret; + + ret = hub->GetAxisInfoH(1, travelY); + if ( DEVICE_OK != ret ) return ret; + + // here the stepsize is set to 1 digital bit ?..Cut-and-paste from PIEZOCONCEPT. + upperLimitX_ = travelX; + stepSize_X_um_ = travelX / 65535; + upperLimitY_ = travelY; + stepSize_Y_um_ = travelY / 65535; + + ret = UpdateStatus(); + if ( ret != DEVICE_OK ) return ret; + + initialized_ = true; + + return DEVICE_OK; +} + +int CESP32XYStage::Shutdown() +{ + if ( initialized_ ) initialized_ = false; + return DEVICE_OK; +} + +bool CESP32XYStage::Busy() +{ + return false; +} + +int CESP32XYStage::GetPositionSteps(long& x, long& y) +{ + x = ( long ) ( posX_um_ / stepSize_X_um_ ); + y = ( long ) ( posY_um_ / stepSize_Y_um_ ); + + std::stringstream ss; + ss << "GetPositionSteps :=" << x << "," << y; + LogMessage(ss.str(), false); + return DEVICE_OK; +} + +int CESP32XYStage::SetPositionSteps(long x, long y) +{ + double posX = x * stepSize_X_um_; + double posY = y * stepSize_Y_um_; + + std::stringstream ss; + ss << "Current position = " << posX_um_ << "," << posY_um_ << " \n Commanded position = " << posX << "," << posY; + LogMessage(ss.str(), false); + + int ret = DEVICE_OK; + + if ( posX_um_ != posX ) + { + ret = MoveX(posX); + if ( ret != DEVICE_OK ) return ret; + } + if ( posY_um_ != posY ) + { + ret = MoveY(posY); + if ( ret != DEVICE_OK ) return ret; + } + return OnXYStagePositionChanged(posX_um_, posY_um_); +} + +int CESP32XYStage::SetRelativePositionSteps(long x, long y) +{ + long curX, curY; + GetPositionSteps(curX, curY); + + return SetPositionSteps(curX + x, curY + y); +} + +// "X" command - move to new x-position (stage translate) +int CESP32XYStage::MoveX(double posUm) +{ + CESP32Hub* hub = static_cast< CESP32Hub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) return ERR_HUB_UNAVAILABLE; + + if ( posUm < lowerLimitX_ ) posUm = lowerLimitX_; + if ( posUm > upperLimitX_ ) posUm = upperLimitX_; + + char buf[25]; + int length = sprintf(buf, "X,%3.3f\r\n", posUm); + + std::stringstream ss; + ss << "Command: " << buf << " Position set: " << posUm; + LogMessage(ss.str().c_str(), false); + + MMThreadGuard myLock(hub->GetLock()); + hub->PurgeComPortH(); + + int ret = hub->WriteToComPortH(( unsigned char* ) buf, length); + if ( ret != DEVICE_OK ) return ret; + + MM::MMTime startTime = GetCurrentMMTime(); + unsigned long bytesRead = 0; + unsigned char answer[10]; + while ( ( bytesRead < 10 ) && ( ( GetCurrentMMTime() - startTime ).getMsec() < 250 ) ) { + unsigned long br; + ret = hub->ReadFromComPortH(( unsigned char* ) answer + bytesRead, 1, br); + if ( answer[bytesRead] == '\r' ) break; + bytesRead += br; + } + + answer[bytesRead] = 0; // string terminator + + hub->PurgeComPortH(); + hub->SetTimedOutput(false); + + if ( ret != DEVICE_OK ) return ret; + + if ( answer[0] != 'X' ) return ERR_COMMUNICATION; + + std::ostringstream os; + os << "X-Answer= " << answer; + LogMessage(os.str().c_str(), false); + + posX_um_ = posUm; + return DEVICE_OK; +} + +// "Y" command - move to new y-position (stage translate) +int CESP32XYStage::MoveY(double posUm) +{ + CESP32Hub* hub = static_cast< CESP32Hub* >( GetParentHub() ); + + if ( !hub || !hub->IsPortAvailable() ) return ERR_HUB_UNAVAILABLE; + + if ( posUm < lowerLimitY_ ) posUm = lowerLimitY_; + if ( posUm > upperLimitY_ ) posUm = upperLimitY_; + + char buf[25]; + int length = sprintf(buf, "Y,%3.3f\r\n", posUm); + + std::stringstream ss; + ss << "Command: " << buf << " Position set: " << posUm; + LogMessage(ss.str().c_str(), false); + + MMThreadGuard myLock(hub->GetLock()); + hub->PurgeComPortH(); + + int ret = hub->WriteToComPortH(( unsigned char* ) buf, length); + if ( ret != DEVICE_OK ) return ret; + + MM::MMTime startTime = GetCurrentMMTime(); + unsigned long bytesRead = 0; + unsigned char answer[10]; + while ( ( bytesRead < 10 ) && ( ( GetCurrentMMTime() - startTime ).getMsec() < 250 ) ) { + unsigned long br; + ret = hub->ReadFromComPortH(( unsigned char* ) answer + bytesRead, 1, br); + if ( answer[bytesRead] == '\r' ) break; + bytesRead += br; + } + + answer[bytesRead] = 0; // string terminator + + hub->PurgeComPortH(); + hub->SetTimedOutput(false); + + if ( ret != DEVICE_OK ) return ret; + + if ( answer[0] != 'Y' ) return ERR_COMMUNICATION; + + std::ostringstream os; + os << "Y-Answer= " << answer; + LogMessage(os.str().c_str(), false); + + posY_um_ = posUm; + return DEVICE_OK; +} + +int CESP32XYStage::OnXStageMinPos(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + int ret = DEVICE_ERR; + if ( eAct == MM::BeforeGet ) + { + pProp->Set(lowerLimitX_); + ret = DEVICE_OK; + } + else if ( eAct == MM::AfterSet ) + { + double limit; + pProp->Get(limit); + lowerLimitX_ = limit; + + ret = DEVICE_OK; + } + + return ret; +} + +int CESP32XYStage::OnXStageMaxPos(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + int ret = DEVICE_ERR; + if ( eAct == MM::BeforeGet ) + { + pProp->Set(upperLimitX_); + ret = DEVICE_OK; + } + else if ( eAct == MM::AfterSet ) + { + double limit; + pProp->Get(limit); + upperLimitX_ = limit; + + ret = DEVICE_OK; + } + + return ret; +} + +int CESP32XYStage::OnYStageMinPos(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + int ret = DEVICE_ERR; + if ( eAct == MM::BeforeGet ) + { + pProp->Set(lowerLimitY_); + ret = DEVICE_OK; + } + else if ( eAct == MM::AfterSet ) + { + double limit; + pProp->Get(limit); + lowerLimitY_ = limit; + + ret = DEVICE_OK; + } + + return ret; +} + +int CESP32XYStage::OnYStageMaxPos(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + int ret = DEVICE_ERR; + if ( eAct == MM::BeforeGet ) + { + pProp->Set(upperLimitY_); + ret = DEVICE_OK; + } + else if ( eAct == MM::AfterSet ) + { + double limit; + pProp->Get(limit); + upperLimitY_ = limit; + + ret = DEVICE_OK; + } + + return ret; +} + + +// The "Input" functions below need to be commented +CESP32Input::CESP32Input() : + mThread_(0), + pin_(0), + name_(g_DeviceNameESP32Input) +{ + std::string errorText = "To use the Input function you need firmware version 1 or higher"; + SetErrorText(ERR_VERSION_MISMATCH, errorText.c_str()); + + CreateProperty("Pin", "All", MM::String, false, 0, true); + AddAllowedValue("Pin", "All"); + AddAllowedValue("Pin", "0"); + AddAllowedValue("Pin", "1"); + AddAllowedValue("Pin", "2"); + AddAllowedValue("Pin", "3"); + AddAllowedValue("Pin", "4"); + AddAllowedValue("Pin", "5"); + + CreateProperty("Pull-Up-Resistor", g_On, MM::String, false, 0, true); + AddAllowedValue("Pull-Up-Resistor", g_On); + AddAllowedValue("Pull-Up-Resistor", g_Off); + + // Name + int ret = CreateProperty(MM::g_Keyword_Name, name_.c_str(), MM::String, true); + assert(DEVICE_OK == ret); + + // Description + ret = CreateProperty(MM::g_Keyword_Description, "ESP32 shutter driver", MM::String, true); + assert(DEVICE_OK == ret); + + // parent ID display + CreateHubIDProperty(); +} + +CESP32Input::~CESP32Input() +{ + Shutdown(); +} + +int CESP32Input::Shutdown() +{ + if ( initialized_ ) + delete( mThread_ ); + initialized_ = false; + return DEVICE_OK; +} + + +int CESP32Input::Initialize() +{ + CESP32Hub* hub = static_cast< CESP32Hub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) return ERR_NO_PORT_SET; + + char hubLabel[MM::MaxStrLength]; + hub->GetLabel(hubLabel); + SetParentID(hubLabel); // for backward comp. + + char ver[MM::MaxStrLength] = "0"; + hub->GetProperty(g_versionProp, ver); + + // we should get ASCII "1" (minimally) back from the ESP - convert to (int) + int version = atoi(ver); + if ( version < g_Min_MMVersion ) return ERR_VERSION_MISMATCH; + + + // This bit of code needs some comments! + // Are we going to use the GPIO lines for Analogue or Digital; I/O + // I think the idea is to request setup of the INPUT_PULLUP state + // on the GPIO pins on the microcontroller... but it is not clear + + int ret = GetProperty("Pin", pins_); + if ( ret != DEVICE_OK ) return ret; + + // pin_ = ascii to integer of pins_ (i.e. 0,1,2,3,4,5) unknown value if pins_="All" + if ( strcmp("All", pins_) != 0 ) pin_ = atoi(pins_); + + ret = GetProperty("Pull-Up-Resistor", pullUp_); + if ( ret != DEVICE_OK ) return ret; + + // Digital Input + CPropertyAction* pAct = new CPropertyAction(this, &CESP32Input::OnDigitalInput); + ret = CreateProperty("DigitalInput", "0", MM::Integer, true, pAct); + if ( ret != DEVICE_OK ) return ret; + + int start = 0; + int end = 5; + + //if pins_ != "All" then start=end=pin_ + if ( strcmp("All", pins_) != 0 ) { + start = pin_; + end = pin_; + } + + for ( long i = start; i <= end; i++ ) + { + +// This does something and it would be very helpful to know what******* + CPropertyActionEx* pExAct = new CPropertyActionEx(this, &CESP32Input::OnAnalogInput, i); + std::ostringstream os; + os << "AnalogInput= " << i; + ret = CreateProperty(os.str().c_str(), "0.0", MM::Float, true, pExAct); + if ( ret != DEVICE_OK ) return ret; + + // set pull up resistor state for this pin + if ( strcmp(g_On, pullUp_) == 0 ) SetPullUp(i, 1); + else SetPullUp(i, 0); + + } + + mThread_ = new ESP32InputMonitorThread(*this); + mThread_->Start(); + + initialized_ = true; + + return DEVICE_OK; +} + +void CESP32Input::GetName(char* name) const +{ + CDeviceUtils::CopyLimitedString(name, name_.c_str()); +} + +bool CESP32Input::Busy() +{ + return false; +} + +// Justin Notes: "GetDigitalInput" is polled for state change every 0.5sec by the thread below. +// In the original ESP code we were testing the Analogue Inputs for a digital change.. +// That's fine.. but we should set this up in a more obvious way. +// suggest we assign some of the GPIO lines as Digital I/O and some as Analogue Input. +// Currently this is unclear to me...I don't know what Analogue inputs we want to monitor. + +// Command: 40 now "L" logic in - returns "L,l\r\n" pin High/Low +int CESP32Input::GetDigitalInput(long* state) +{ + CESP32Hub* hub = static_cast< CESP32Hub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) + return ERR_NO_PORT_SET; + + MMThreadGuard myLock(hub->GetLock()); + + hub->PurgeComPortH(); + int ret = DEVICE_OK; + char command[50]; + unsigned int leng; + + int testPin = pin_; //(test pin 0->5) + if ( strcmp("All", pins_) == 0 ) testPin = 6; // (test if all pins set) + + leng = snprintf(( char* ) command, 50, "L,%d\r\n", testPin); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + + // we expect a reply like "L,1\r\n" 1 or 0 if testPin is High/Low + MM::MMTime startTime = GetCurrentMMTime(); + unsigned long bytesRead = 0; + char answer[10]; + // Chug through the I/O buffer, byte-by-byte and stop at lf! + while ( ( bytesRead < 10 ) && ( ( GetCurrentMMTime() - startTime ).getMsec() < 250 ) ) { + unsigned long br; + ret = hub->ReadFromComPortH(( unsigned char* ) answer + bytesRead, 1, br); + if ( answer[bytesRead] == '\r' ) break; + bytesRead += br; + } + + answer[bytesRead] = 0; // string terminator + + // discard anything left in the IO buffer + hub->PurgeComPortH(); + hub->SetTimedOutput(false); + + if ( ret != DEVICE_OK ) return ret; + + int num; + char com[1]; + sscanf(( const char* ) answer, "%1s,%d", com, &num); + + // should give com[0]='L' and num = 1 or 0 + + if ( com[0] != 'L' ) return ERR_COMMUNICATION; + + *state = ( long ) num; + + std::ostringstream os; + os << "GetDigitalInput_State=" << *state << " testPin = " << testPin; + LogMessage(os.str().c_str(), false); + + return DEVICE_OK; +} +int CESP32Input::ReportStateChange(long newState) +{ + std::ostringstream os; + os << newState; + return OnPropertyChanged("DigitalInput", os.str().c_str()); +} + +// Action handlers +int CESP32Input::OnDigitalInput(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + // This sets a property listed in the MMDevice Property.h file (very limited commenting) + // "Set" is an "Action Functor" (handler?) + // "eACT" seems common to all "On" functions - so probably an Event listener? + // Anyway, it wants a (long) value for "state" which becomes a virtual Boolean, apparently! + + if ( eAct == MM::BeforeGet ) + { + long state; + + // get the state of the selected pin 0-5 or all pins (which I have made to be "6" in my ESP code) + int ret = GetDigitalInput(&state); + if ( ret != DEVICE_OK ) return ret; + pProp->Set(state); + } + + return DEVICE_OK; +} + +// Commands: 41 now = "A,chan" analogue read channel +int CESP32Input::OnAnalogInput(MM::PropertyBase* pProp, MM::ActionType eAct, long channel) +{ + CESP32Hub* hub = static_cast< CESP32Hub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) return ERR_NO_PORT_SET; + + if ( eAct == MM::BeforeGet ) + { + + MMThreadGuard myLock(hub->GetLock()); + hub->PurgeComPortH(); + int ret = DEVICE_OK; + char command[50]; + unsigned int leng; + + leng = snprintf(( char* ) command, 50, "A,%d\r\n", ( int ) channel); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + + // We expect a reply like "A,597\r\n" analogue value on channel + // I think we need to use the hub to communicate via the rather "ReadFromComPortH" command?? + unsigned long bytesRead = 0; + char answer[20]; + MM::MMTime startTime = GetCurrentMMTime(); + while ( ( bytesRead < 20 ) && ( ( GetCurrentMMTime() - startTime ).getMsec() < 1000) ) { + unsigned long br; + ret = hub->ReadFromComPortH(( unsigned char* ) answer + bytesRead, 1, br); + if ( answer[bytesRead] == '\r' ) break; + bytesRead += br; + } + + answer[bytesRead] = 0; // replace \r with string terminator + + hub->PurgeComPortH(); + hub->SetTimedOutput(false); + + if ( ret != DEVICE_OK ) return ret; + + int num; + char com[1]; + sscanf(( const char* ) answer, "%1s,%d", com, &num); + // should give com[0]='A' and num = 597 or something! + + std::ostringstream os; + os << "AnswerTo A," << channel << " = " << answer; + LogMessage(os.str().c_str(), false); + + if ( com[0] != 'A' ) return ERR_COMMUNICATION; + + pProp->Set(( long ) num); + } + return DEVICE_OK; +} + +// Commands: 42 - now "D" set digital pull-up ?? needs commenting - not sure why we want to do this dynamically. +int CESP32Input::SetPullUp(int pin, int state) +{ + CESP32Hub* hub = static_cast< CESP32Hub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) return ERR_NO_PORT_SET; + + MMThreadGuard myLock(hub->GetLock()); + hub->PurgeComPortH(); + int ret = DEVICE_OK; + char command[50]; + unsigned int leng; + + leng = snprintf(( char* ) command, 50, "D,%d,%d\r\n", ( int ) pin, ( int ) state); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + + hub->PurgeComPortH(); + hub->SetTimedOutput(false); + + return DEVICE_OK; +} + +ESP32InputMonitorThread::ESP32InputMonitorThread(CESP32Input& aInput) : + state_(0), + aInput_(aInput) +{ +} +ESP32InputMonitorThread::~ESP32InputMonitorThread() +{ + Stop(); + wait(); +} + +// I think this thread is a 0.5s delay loop that polls the Digital I/O lines and reports changes +// It looks like an infinite background polling-thread...until we hit a comms error +// It has probably been messed-up by my meddling! + +int ESP32InputMonitorThread::svc() +{ + while ( !stop_ ) + { + long state; + int ret = aInput_.GetDigitalInput(&state); + if ( ret != DEVICE_OK ) + { + stop_ = true; // on communication error drop out of loop and don't retry ?? + return ret; + } + + if ( state != state_ ) + { + aInput_.ReportStateChange(state); + state_ = state; + } + CDeviceUtils::SleepMs(500); + } + return DEVICE_OK; +} +void ESP32InputMonitorThread::Start() +{ + stop_ = false; + activate(); +} \ No newline at end of file diff --git a/DeviceAdapters/ESP32/esp32.h b/DeviceAdapters/ESP32/esp32.h new file mode 100644 index 000000000..60101a955 --- /dev/null +++ b/DeviceAdapters/ESP32/esp32.h @@ -0,0 +1,421 @@ +////////////////////////////////////////////////////////////////////////////// +// FILE: ESP32.h +// PROJECT: Micro-Manager +// SUBSYSTEM: DeviceAdapters +//----------------------------------------------------------------------------- +// DESCRIPTION: Adapter for ESP32 board +// Needs accompanying firmware to be installed on the board +// COPYRIGHT: University of California, San Francisco, 2008 +// LICENSE: LGPL +// +// AUTHOR: Nico Stuurman, nico@cmp.ucsf.edu, 11/09/2008 +// automatic device detection by Karl Hoover +// +// + +#ifndef _ESP32_H_ +#define _ESP32_H_ + +#include "MMDevice.h" +#include "DeviceBase.h" +#include +#include + +////////////////////////////////////////////////////////////////////////////// +// Error codes +// +#define ERR_UNKNOWN_POSITION 101 +#define ERR_INITIALIZE_FAILED 102 +#define ERR_WRITE_FAILED 103 +#define ERR_CLOSE_FAILED 104 +#define ERR_BOARD_NOT_FOUND 105 +#define ERR_PORT_OPEN_FAILED 106 +#define ERR_COMMUNICATION 107 +#define ERR_NO_PORT_SET 108 +#define ERR_VERSION_MISMATCH 109 +#define ERR_HUB_UNAVAILABLE 110 +#define ERR_UNKNOWN_AXIS 111 + +class ESP32InputMonitorThread; + +class CESP32Hub : public HubBase +{ +public: + CESP32Hub(); + ~CESP32Hub(); + + int Initialize(); + int Shutdown(); + void GetName(char* pszName) const; + bool Busy(); + + bool SupportsDeviceDetection(void); + MM::DeviceDetectionStatus DetectDevice(void); + int DetectInstalledDevices(); + + // property handlers + int OnPort(MM::PropertyBase* pPropt, MM::ActionType eAct); + int OnLogic(MM::PropertyBase* pPropt, MM::ActionType eAct); + int OnVersion(MM::PropertyBase* pPropt, MM::ActionType eAct); + + // custom interface for child devices + bool IsPortAvailable() { return portAvailable_; } + bool IsLogicInverted() { return invertedLogic_; } + bool IsTimedOutputActive() { return timedOutputActive_; } + void SetTimedOutput(bool active) { timedOutputActive_ = active; } + + int PurgeComPortH() { return PurgeComPort(port_.c_str()); } + int WriteToComPortH(const unsigned char* command, unsigned len) { return WriteToComPort(port_.c_str(), command, len); } + int ReadFromComPortH(unsigned char* answer, unsigned maxLen, unsigned long& bytesRead) + { + return ReadFromComPort(port_.c_str(), answer, maxLen, bytesRead); + } + + + static MMThreadLock& GetLock() { return lock_; } + void SetShutterState(unsigned state) { shutterState_ = state; } + void SetSwitchState(unsigned state) { switchState_ = state; } + unsigned GetShutterState() { return shutterState_; } + unsigned GetSwitchState() { return switchState_; } + int GetAxisInfoH(int axis, double& travel) { return GetAxisInfo(axis, travel); } + + +private: + int GetControllerVersion(int&); + std::string port_; + bool initialized_; + bool portAvailable_; + bool invertedLogic_; + bool timedOutputActive_; + ; + int version_; + static MMThreadLock lock_; + unsigned switchState_; + unsigned shutterState_; + + int GetControllerInfo(); + int GetAxisInfo(int, double&); + bool hasZStage_; + bool hasXYStage_; +}; + +class CESP32Shutter : public CShutterBase +{ +public: + CESP32Shutter(); + ~CESP32Shutter(); + + // MMDevice API + // ------------ + int Initialize(); + int Shutdown(); + + void GetName(char* pszName) const; + bool Busy(); + + // Shutter API + int SetOpen(bool open = true); + int GetOpen(bool& open); + int Fire(double deltaT); + + // action interface + // ---------------- + int OnOnOff(MM::PropertyBase* pProp, MM::ActionType eAct); + +private: + int WriteToPort(long lnValue); + MM::MMTime changedTime_; + bool initialized_; + std::string name_; +}; + +class CESP32Switch : public CStateDeviceBase +{ +public: + CESP32Switch(); + ~CESP32Switch(); + + // MMDevice API + // ------------ + int Initialize(); + int Shutdown(); + + void GetName(char* pszName) const; + bool Busy() { return busy_; } + + unsigned long GetNumberOfPositions()const { return numPos_; } + + // action interface + // ---------------- + int OnState(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnDelay(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnRepeatTimedPattern(MM::PropertyBase* pProp, MM::ActionType eAct); + /* + int OnSetPattern(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnGetPattern(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnPatternsUsed(MM::PropertyBase* pProp, MM::ActionType eAct); + */ + int OnSkipTriggers(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnStartTrigger(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnStartTimedOutput(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnBlanking(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnBlankingTriggerDirection(MM::PropertyBase* pProp, MM::ActionType eAct); + + int OnSequence(MM::PropertyBase* pProp, MM::ActionType eAct); + +private: + static const unsigned int NUMPATTERNS = 12; + + int OpenPort(const char* pszName, long lnValue); + int WriteToPort(long lnValue); + int ClosePort(); + int LoadSequence(unsigned size, unsigned char* seq); + + unsigned pattern_[NUMPATTERNS]; + unsigned delay_[NUMPATTERNS]; + int nrPatternsUsed_; + unsigned currentDelay_; + bool sequenceOn_; + bool blanking_; + bool initialized_; + long numPos_; + bool busy_; +}; + +class CESP32DA : public CSignalIOBase +{ +public: + CESP32DA(int channel); + ~CESP32DA(); + + // MMDevice API + // ------------ + int Initialize(); + int Shutdown(); + + void GetName(char* pszName) const; + bool Busy() { return busy_; } + + // DA API + int SetGateOpen(bool open); + int GetGateOpen(bool& open) { open = gateOpen_; return DEVICE_OK; }; + int SetSignal(double volts); + int GetSignal(double& volts) { volts_ = volts; return DEVICE_UNSUPPORTED_COMMAND; } + int GetLimits(double& minVolts, double& maxVolts) { minVolts = minV_; maxVolts = maxV_; return DEVICE_OK; } + + int IsDASequenceable(bool& isSequenceable) const { isSequenceable = false; return DEVICE_OK; } + + // action interface + // ---------------- + int OnVolts(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnMaxVolt(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnChannel(MM::PropertyBase* pProp, MM::ActionType eAct); + +private: + int WriteToPort(double lnValue); + int WriteSignal(double volts); + + bool initialized_; + bool busy_; + double minV_; + double maxV_; + double volts_; + double gatedVolts_; + unsigned channel_; + unsigned maxChannel_; + bool gateOpen_; + std::string name_; +}; + +class CESP32Input : public CGenericBase +{ +public: + CESP32Input(); + ~CESP32Input(); + + int Initialize(); + int Shutdown(); + void GetName(char* pszName) const; + bool Busy(); + + int OnDigitalInput(MM::PropertyBase* pPropt, MM::ActionType eAct); + int OnAnalogInput(MM::PropertyBase* pProp, MM::ActionType eAct, long channel); + + int GetDigitalInput(long* state); + int ReportStateChange(long newState); + +private: + // int ReadNBytes(CESP32Hub* h, unsigned int n, unsigned char* answer); + int SetPullUp(int pin, int state); + + MMThreadLock lock_; + ESP32InputMonitorThread* mThread_; + char pins_[MM::MaxStrLength]; + char pullUp_[MM::MaxStrLength]; + int pin_; + bool initialized_; + std::string name_; +}; + +class ESP32InputMonitorThread : public MMDeviceThreadBase +{ +public: + ESP32InputMonitorThread(CESP32Input& aInput); + ~ESP32InputMonitorThread(); + int svc(); + int open(void*) { return 0; } + int close(unsigned long) { return 0; } + + void Start(); + void Stop() { stop_ = true; } + ESP32InputMonitorThread& operator=(const ESP32InputMonitorThread&) + { + return *this; + } + + +private: + long state_; + CESP32Input& aInput_; + bool stop_; +}; + +class CESP32Stage : public CStageBase +{ +public: + CESP32Stage(); + ~CESP32Stage(); + + bool Busy(); + void GetName(char* pszName) const; + + int Initialize(); + int Shutdown(); + + // Stage API + int SetPositionUm(double pos); + int GetPositionUm(double& pos); + double GetStepSize() { return stepSizeUm_; } + + int SetPositionSteps(long steps); + int GetPositionSteps(long& steps); + + int SetOrigin() { return DEVICE_UNSUPPORTED_COMMAND; } + + int GetLimits(double& lower, double& upper) + { + lower = lowerLimit_; + upper = upperLimit_; + return DEVICE_OK; + } + + int Move(double /*v*/) { return DEVICE_UNSUPPORTED_COMMAND; } + + bool IsContinuousFocusDrive() const { return false; } + + int OnStageMinPos(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnStageMaxPos(MM::PropertyBase* pProp, MM::ActionType eAct); + + int IsStageSequenceable(bool& isSequenceable) const + { + isSequenceable = false; return DEVICE_OK; + } + int GetStageSequenceMaxLength(long& nrEvents) const + { + nrEvents = 0; return DEVICE_OK; + } + +private: + double stepSizeUm_; + double pos_um_; + bool busy_; + bool initialized_; + double lowerLimit_; + double upperLimit_; + + int MoveZ(double pos); +}; + +class CESP32XYStage : public CXYStageBase +{ +public: + CESP32XYStage(); + ~CESP32XYStage(); + + bool Busy(); + void GetName(char* pszName) const; + + int Initialize(); + int Shutdown(); + + int GetPositionSteps(long& x, long& y); + int SetPositionSteps(long x, long y); + int SetRelativePositionSteps(long, long); + + virtual int Home() { return DEVICE_UNSUPPORTED_COMMAND; } + virtual int Stop() { return DEVICE_UNSUPPORTED_COMMAND; } + virtual int SetOrigin() { return DEVICE_UNSUPPORTED_COMMAND; } + + virtual int GetLimits(double& lower, double& upper) + { + lower = lowerLimitX_; + upper = upperLimitY_; + return DEVICE_OK; + } + + virtual int GetLimitsUm(double& xMin, double& xMax, double& yMin, double& yMax) + { + xMin = lowerLimitX_; xMax = upperLimitX_; + yMin = lowerLimitY_; yMax = upperLimitY_; + return DEVICE_OK; + } + + virtual int GetStepLimits(long& /*xMin*/, long& /*xMax*/, long& /*yMin*/, long& /*yMax*/) + { + return DEVICE_UNSUPPORTED_COMMAND; + } + + double GetStepSizeXUm() + { + return stepSize_X_um_; + } + + double GetStepSizeYUm() + { + return stepSize_Y_um_; + } + + int Move(double /*vx*/, double /*vy*/) { return DEVICE_OK; } + + int IsXYStageSequenceable(bool& isSequenceable) const { isSequenceable = false; return DEVICE_OK; } + + int OnXStageMinPos(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnXStageMaxPos(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnYStageMinPos(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnYStageMaxPos(MM::PropertyBase* pProp, MM::ActionType eAct); + + int OnStepsPerSecond(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnMicrostepMultiplier(MM::PropertyBase* pProp, MM::ActionType eAct); + +private: + double stepSize_X_um_; + double stepSize_Y_um_; + double posX_um_; + double posY_um_; + unsigned int driveID_X_; + unsigned int driveID_Y_; + + bool busy_; + MM::TimeoutMs* timeOutTimer_; + double velocity_; + bool initialized_; + double lowerLimitX_; + double upperLimitX_; + double lowerLimitY_; + double upperLimitY_; + + int MoveX(double); + int MoveY(double); +}; + +#endif //_ESP32_H_ diff --git a/DeviceAdapters/FLICamera/FLICamera.cpp b/DeviceAdapters/FLICamera/FLICamera.cpp index 2569943d9..65a1f3e31 100644 --- a/DeviceAdapters/FLICamera/FLICamera.cpp +++ b/DeviceAdapters/FLICamera/FLICamera.cpp @@ -25,7 +25,6 @@ #define WIN32_LEAN_AND_MEAN #include -#include "FixSnprintf.h" extern "C" { long __stdcall FLILibAttach(void); diff --git a/DeviceAdapters/FreeSerialPort/FreeSerialPort.cpp b/DeviceAdapters/FreeSerialPort/FreeSerialPort.cpp index ad737ea4a..5326f442a 100644 --- a/DeviceAdapters/FreeSerialPort/FreeSerialPort.cpp +++ b/DeviceAdapters/FreeSerialPort/FreeSerialPort.cpp @@ -25,7 +25,6 @@ #define WIN32_LEAN_AND_MEAN #include #endif -#include "FixSnprintf.h" #include "FreeSerialPort.h" #include "ModuleInterface.h" diff --git a/DeviceAdapters/HamiltonMVP/MVPCommands.h b/DeviceAdapters/HamiltonMVP/MVPCommands.h index 056a5006c..c1746e5df 100644 --- a/DeviceAdapters/HamiltonMVP/MVPCommands.h +++ b/DeviceAdapters/HamiltonMVP/MVPCommands.h @@ -41,9 +41,6 @@ #include #include -#include "FixSnprintf.h" - - const char* const MVP_TERM = "\r"; const char MVP_ACK = 6; const char MVP_NAK = 21; diff --git a/DeviceAdapters/HydraLMT200/ITKHydra.cpp b/DeviceAdapters/HydraLMT200/ITKHydra.cpp index 312ee6235..4178b449a 100644 --- a/DeviceAdapters/HydraLMT200/ITKHydra.cpp +++ b/DeviceAdapters/HydraLMT200/ITKHydra.cpp @@ -35,8 +35,6 @@ //TODO: NEED TO MAKE SURE COMMENDS SEND IN CR + LFKE SURE COMMENDS SEND IN CR + LF -#include "FixSnprintf.h" - #include #include #include diff --git a/DeviceAdapters/ITC18/MMITC18.cpp b/DeviceAdapters/ITC18/MMITC18.cpp index 8f4260880..ff8015b4b 100644 --- a/DeviceAdapters/ITC18/MMITC18.cpp +++ b/DeviceAdapters/ITC18/MMITC18.cpp @@ -15,7 +15,6 @@ #else #include #endif -#include "FixSnprintf.h" #include "MMITC18.h" #include "ModuleInterface.h" diff --git a/DeviceAdapters/IlluminateLEDArray/LEDArray.cpp b/DeviceAdapters/IlluminateLEDArray/LEDArray.cpp index de550db8e..749a403ca 100644 --- a/DeviceAdapters/IlluminateLEDArray/LEDArray.cpp +++ b/DeviceAdapters/IlluminateLEDArray/LEDArray.cpp @@ -30,8 +30,6 @@ #define WIN32_LEAN_AND_MEAN #include #endif -#include "FixSnprintf.h" - /////////////////////////////////////////////////////////////////////////////// // Exported MMDevice API diff --git a/DeviceAdapters/IntegratedLaserEngine/ILEWrapper/ILEWrapper.cpp b/DeviceAdapters/IntegratedLaserEngine/ILEWrapper/ILEWrapper.cpp index 9cd258d74..1889a27e2 100644 --- a/DeviceAdapters/IntegratedLaserEngine/ILEWrapper/ILEWrapper.cpp +++ b/DeviceAdapters/IntegratedLaserEngine/ILEWrapper/ILEWrapper.cpp @@ -118,11 +118,8 @@ CILEWrapper::CILEWrapper() : throw std::runtime_error( "GetProcAddress GetILEPowerManagementInterface_ failed\n" ); } + // HLE specific interface not required for ILE GetILEPowerManagement2Interface_ = ( TGetILEPowerManagement2Interface ) GetProcAddress( DLL_, "GetILEPowerManagement2Interface" ); - if ( GetILEPowerManagement2Interface_ == nullptr ) - { - throw std::runtime_error( "GetProcAddress GetILEPowerManagement2Interface_ failed\n" ); - } GetILEInterface2_ = (TGetILEInterface2)GetProcAddress( DLL_, "GetILEInterface2" ); if ( GetILEInterface2_ == nullptr ) @@ -323,7 +320,8 @@ IALC_REV_ILEPowerManagement2* CILEWrapper::GetILEPowerManagement2Interface( IALC IALC_REV_ILEPowerManagement2* vILEPowerManagement2 = nullptr; { CILESDKLock vSDKLock; - vILEPowerManagement2 = GetILEPowerManagement2Interface_( vILEObject ); + if ( GetILEPowerManagement2Interface_ != nullptr ) + vILEPowerManagement2 = GetILEPowerManagement2Interface_( vILEObject ); } if ( vILEPowerManagement2 != nullptr ) { diff --git a/DeviceAdapters/IntegratedLaserEngine/Lasers.cpp b/DeviceAdapters/IntegratedLaserEngine/Lasers.cpp index b7f82643d..1adf1cfe8 100644 --- a/DeviceAdapters/IntegratedLaserEngine/Lasers.cpp +++ b/DeviceAdapters/IntegratedLaserEngine/Lasers.cpp @@ -244,6 +244,32 @@ int CLasers::OnPowerSetpoint(MM::PropertyBase* Prop, MM::ActionType Act, long L return vRet; } } + else + { + // Update HW setpoint even when shutter closed + double vPercentScale = PowerSetpoint( LaserIndex ); + double vPower = ( vPercentScale / 100. ) * ( LasersState_[LaserIndex].LaserRange_.PowerMax - LasersState_[LaserIndex].LaserRange_.PowerMin ) + LasersState_[LaserIndex].LaserRange_.PowerMin; + + MMILE_->LogMMMessage( "SetLas" + std::to_string( static_cast( LaserIndex ) ) + " = " + std::to_string( static_cast( vPower ) ), true ); + + TLaserState vLaserState; + if ( LaserInterface_->GetLaserState( LaserIndex, &vLaserState ) ) + { + if ( vLaserState > ALC_NOT_AVAILABLE ) + { + MMILE_->LogMMMessage( "Setting Laser " + std::to_string( static_cast( Wavelength( LaserIndex ) ) ) + " to " + std::to_string( static_cast( vPower ) ) + "% full scale", true ); + if ( !LaserInterface_->SetLas_I( LaserIndex, vPower, false ) ) + { + MMILE_->LogMMMessage( std::string( "Setting Laser power for laser " + std::to_string( static_cast( LaserIndex ) ) + " failed with value [" ) + std::to_string( static_cast( vPower ) ) + "]" ); + return ERR_LASER_SET; + } + } + } + else + { + return ERR_LASER_STATE_READ; + } + } } //Prop->Set(achievedSetpoint); ---- for quantization.... diff --git a/DeviceAdapters/IsmatecMCP/MCPCommands.h b/DeviceAdapters/IsmatecMCP/MCPCommands.h index ee911a706..f30321e19 100644 --- a/DeviceAdapters/IsmatecMCP/MCPCommands.h +++ b/DeviceAdapters/IsmatecMCP/MCPCommands.h @@ -39,9 +39,6 @@ #include #include -#include "FixSnprintf.h" - - const char* const MCP_CMD_TERM = "\r"; const char* const MCP_RESP_TERM = "\r\n"; diff --git a/DeviceAdapters/K8055/K8055.cpp b/DeviceAdapters/K8055/K8055.cpp index 5d9b3406a..5aa67f270 100644 --- a/DeviceAdapters/K8055/K8055.cpp +++ b/DeviceAdapters/K8055/K8055.cpp @@ -18,7 +18,6 @@ #define WIN32_LEAN_AND_MEAN #include #endif -#include "FixSnprintf.h" using namespace std; diff --git a/DeviceAdapters/K8061/K8061.cpp b/DeviceAdapters/K8061/K8061.cpp index d4a648af3..bc18a7a3f 100644 --- a/DeviceAdapters/K8061/K8061.cpp +++ b/DeviceAdapters/K8061/K8061.cpp @@ -19,7 +19,6 @@ #define WIN32_LEAN_AND_MEAN #include #endif -#include "FixSnprintf.h" using namespace std; diff --git a/DeviceAdapters/LeicaDMI/LeicaDMI.cpp b/DeviceAdapters/LeicaDMI/LeicaDMI.cpp index 95016c230..bb0f50087 100644 --- a/DeviceAdapters/LeicaDMI/LeicaDMI.cpp +++ b/DeviceAdapters/LeicaDMI/LeicaDMI.cpp @@ -31,7 +31,6 @@ #else #include #endif -#include "FixSnprintf.h" #include "LeicaDMI.h" #include "LeicaDMIModel.h" diff --git a/DeviceAdapters/LeicaDMI/LeicaDMIModel.cpp b/DeviceAdapters/LeicaDMI/LeicaDMIModel.cpp index a6fd45b7d..f1a94d3ca 100644 --- a/DeviceAdapters/LeicaDMI/LeicaDMIModel.cpp +++ b/DeviceAdapters/LeicaDMI/LeicaDMIModel.cpp @@ -31,7 +31,6 @@ #else #include #endif -#include "FixSnprintf.h" #include "LeicaDMIModel.h" #include "ModuleInterface.h" diff --git a/DeviceAdapters/LeicaDMI/LeicaDMIScopeInterface.cpp b/DeviceAdapters/LeicaDMI/LeicaDMIScopeInterface.cpp index beb9e3391..89db1cbde 100644 --- a/DeviceAdapters/LeicaDMI/LeicaDMIScopeInterface.cpp +++ b/DeviceAdapters/LeicaDMI/LeicaDMIScopeInterface.cpp @@ -31,7 +31,6 @@ #else #include #endif -#include "FixSnprintf.h" #include "LeicaDMIScopeInterface.h" #include "LeicaDMIModel.h" diff --git a/DeviceAdapters/LeicaDMR/LeicaDMR.cpp b/DeviceAdapters/LeicaDMR/LeicaDMR.cpp index 19c50f46f..4356fad06 100644 --- a/DeviceAdapters/LeicaDMR/LeicaDMR.cpp +++ b/DeviceAdapters/LeicaDMR/LeicaDMR.cpp @@ -26,8 +26,6 @@ // AUTHOR: Nico Stuurman (based on code by Nenad Amodaj), nico@cmp.ucsf.edu, April 2007 // -#include "FixSnprintf.h" - #include "LeicaDMR.h" #include "LeicaDMRHub.h" #include diff --git a/DeviceAdapters/LeicaDMSTC/LeicaDMSTC.cpp b/DeviceAdapters/LeicaDMSTC/LeicaDMSTC.cpp index 14b02356b..60258a2aa 100644 --- a/DeviceAdapters/LeicaDMSTC/LeicaDMSTC.cpp +++ b/DeviceAdapters/LeicaDMSTC/LeicaDMSTC.cpp @@ -22,8 +22,6 @@ // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES. // -#include "FixSnprintf.h" - #include "LeicaDMSTC.h" #include "LeicaDMSTCHub.h" #include diff --git a/DeviceAdapters/LightSheetManager/MicroscopeGeometry.cpp b/DeviceAdapters/LightSheetManager/MicroscopeGeometry.cpp index 8e0a5d132..414d23140 100644 --- a/DeviceAdapters/LightSheetManager/MicroscopeGeometry.cpp +++ b/DeviceAdapters/LightSheetManager/MicroscopeGeometry.cpp @@ -129,7 +129,8 @@ void MicroscopeGeometry::CreateDeviceMap() {"IllumSlice", MM::GalvoDevice}, {"IllumBeam", MM::GalvoDevice}, {"ImagingFocus", MM::StageDevice}, - {"ImagingCamera", MM::CameraDevice} + {"ImagingCamera", MM::CameraDevice}, + {"PreviewCamera", MM::CameraDevice} } } }; diff --git a/DeviceAdapters/Ludl/Ludl.cpp b/DeviceAdapters/Ludl/Ludl.cpp index c93b3fd4b..959ef1d0e 100644 --- a/DeviceAdapters/Ludl/Ludl.cpp +++ b/DeviceAdapters/Ludl/Ludl.cpp @@ -30,7 +30,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "Ludl.h" #include "ModuleInterface.h" diff --git a/DeviceAdapters/LudlLow/Ludl.cpp b/DeviceAdapters/LudlLow/Ludl.cpp index e3108f442..7d29130a4 100644 --- a/DeviceAdapters/LudlLow/Ludl.cpp +++ b/DeviceAdapters/LudlLow/Ludl.cpp @@ -29,7 +29,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "Ludl.h" #include "ModuleInterface.h" diff --git a/DeviceAdapters/LumencorCIA/CIA.cpp b/DeviceAdapters/LumencorCIA/CIA.cpp index 826fff82f..e43b28b37 100644 --- a/DeviceAdapters/LumencorCIA/CIA.cpp +++ b/DeviceAdapters/LumencorCIA/CIA.cpp @@ -24,7 +24,6 @@ #include #include #endif -#include "FixSnprintf.h" #define DEBUG diff --git a/DeviceAdapters/LumencorCIA/LECtrl.cpp b/DeviceAdapters/LumencorCIA/LECtrl.cpp index 687061d88..37a576e37 100644 --- a/DeviceAdapters/LumencorCIA/LECtrl.cpp +++ b/DeviceAdapters/LumencorCIA/LECtrl.cpp @@ -24,7 +24,6 @@ #include #include #endif -#include "FixSnprintf.h" #include #include diff --git a/DeviceAdapters/MP285/MP285.cpp b/DeviceAdapters/MP285/MP285.cpp index f0cacc4eb..b618bef0a 100644 --- a/DeviceAdapters/MP285/MP285.cpp +++ b/DeviceAdapters/MP285/MP285.cpp @@ -33,7 +33,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include #include diff --git a/DeviceAdapters/MP285/MP285Ctrl.cpp b/DeviceAdapters/MP285/MP285Ctrl.cpp index 036f153c8..80ceca457 100644 --- a/DeviceAdapters/MP285/MP285Ctrl.cpp +++ b/DeviceAdapters/MP285/MP285Ctrl.cpp @@ -31,7 +31,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include #include diff --git a/DeviceAdapters/MP285/MP285Error.cpp b/DeviceAdapters/MP285/MP285Error.cpp index d853e8a72..7d66edb20 100644 --- a/DeviceAdapters/MP285/MP285Error.cpp +++ b/DeviceAdapters/MP285/MP285Error.cpp @@ -31,7 +31,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include #include diff --git a/DeviceAdapters/MP285/MP285XYStage.cpp b/DeviceAdapters/MP285/MP285XYStage.cpp index be6ef72c9..faf5901f2 100644 --- a/DeviceAdapters/MP285/MP285XYStage.cpp +++ b/DeviceAdapters/MP285/MP285XYStage.cpp @@ -31,7 +31,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include #include diff --git a/DeviceAdapters/MP285/MP285ZStage.cpp b/DeviceAdapters/MP285/MP285ZStage.cpp index cfb1d3c32..48becdf5b 100644 --- a/DeviceAdapters/MP285/MP285ZStage.cpp +++ b/DeviceAdapters/MP285/MP285ZStage.cpp @@ -31,7 +31,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include #include diff --git a/DeviceAdapters/MT20/MT20.cpp b/DeviceAdapters/MT20/MT20.cpp index e119f0f6a..5dafa964f 100755 --- a/DeviceAdapters/MT20/MT20.cpp +++ b/DeviceAdapters/MT20/MT20.cpp @@ -35,8 +35,6 @@ const char* g_MT20Shutter = "MT20-Shutter"; const char* g_MT20Filterwheel = "MT20-Filterwheel"; const char* g_MT20Attenuator = "MT20-Attenuator"; -#include "FixSnprintf.h" - // instantiate MT20hub object, which will handle communication with device MT20hub mt20; diff --git a/DeviceAdapters/MT20/MT20hub.cpp b/DeviceAdapters/MT20/MT20hub.cpp index 6cb6cd5b8..c9f871edf 100644 --- a/DeviceAdapters/MT20/MT20hub.cpp +++ b/DeviceAdapters/MT20/MT20hub.cpp @@ -33,7 +33,6 @@ #else #include #endif -#include "FixSnprintf.h" MT20hub::MT20hub() : connected_(false), diff --git a/DeviceAdapters/MaestroServo/MaestroServo.cpp b/DeviceAdapters/MaestroServo/MaestroServo.cpp index d72cec3ea..a75cf01a4 100644 --- a/DeviceAdapters/MaestroServo/MaestroServo.cpp +++ b/DeviceAdapters/MaestroServo/MaestroServo.cpp @@ -20,7 +20,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "MaestroServo.h" #include diff --git a/DeviceAdapters/Makefile.am b/DeviceAdapters/Makefile.am index 1fa43090c..ec6b8891a 100644 --- a/DeviceAdapters/Makefile.am +++ b/DeviceAdapters/Makefile.am @@ -1,6 +1,9 @@ AUTOMAKE_OPTIONS = foreign ACLOCAL_AMFLAGS = -I ../m4 +if BUILD_ALLIED_VISION_CAMERA + ALLIED_VISION_CAMERA = AlliedVisionCamera +endif if BUILD_ANDOR ANDOR = Andor endif @@ -65,9 +68,13 @@ endif if BUILD_V4L2 V4L = Video4Linux endif +if BUILD_ZABER + ZABER = Zaber +endif # Please keep these ASCII-lexically sorted (pass through sort(1)). SUBDIRS = \ + $(ALLIED_VISION_CAMERA) \ $(ANDOR) \ $(ANDORLASERCOMBINER) \ $(ANDORSDK3) \ @@ -90,6 +97,7 @@ SUBDIRS = \ $(SPOT) \ $(USBMANAGER) \ $(V4L) \ + $(ZABER) \ AAAOTF \ AOTF \ ASIFW1000 \ @@ -203,7 +211,6 @@ SUBDIRS = \ Xcite \ YodnE600 \ Yokogawa \ - Zaber \ ZeissCAN \ ZeissCAN29 \ kdv \ diff --git a/DeviceAdapters/Marzhauser-LStep/LStep.cpp b/DeviceAdapters/Marzhauser-LStep/LStep.cpp index 462db45be..c7fd4ed0a 100644 --- a/DeviceAdapters/Marzhauser-LStep/LStep.cpp +++ b/DeviceAdapters/Marzhauser-LStep/LStep.cpp @@ -33,7 +33,6 @@ #ifdef WIN32 #pragma warning(disable: 4355) #endif -#include "FixSnprintf.h" #include "LStep.h" #include diff --git a/DeviceAdapters/Marzhauser/Marzhauser.cpp b/DeviceAdapters/Marzhauser/Marzhauser.cpp index 16407f2f3..c6d4eb8ea 100644 --- a/DeviceAdapters/Marzhauser/Marzhauser.cpp +++ b/DeviceAdapters/Marzhauser/Marzhauser.cpp @@ -34,7 +34,6 @@ #ifdef WIN32 #pragma warning(disable: 4355) #endif -#include "FixSnprintf.h" #include "Marzhauser.h" #include diff --git a/DeviceAdapters/MarzhauserLStepOld/LStepOld.cpp b/DeviceAdapters/MarzhauserLStepOld/LStepOld.cpp index 339d7501d..e38eb88fc 100644 --- a/DeviceAdapters/MarzhauserLStepOld/LStepOld.cpp +++ b/DeviceAdapters/MarzhauserLStepOld/LStepOld.cpp @@ -20,7 +20,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "LStepOld.h" #include diff --git a/DeviceAdapters/MicroFPGA/MicroFPGA.cpp b/DeviceAdapters/MicroFPGA/MicroFPGA.cpp index 5847d568b..4e297e863 100644 --- a/DeviceAdapters/MicroFPGA/MicroFPGA.cpp +++ b/DeviceAdapters/MicroFPGA/MicroFPGA.cpp @@ -20,7 +20,6 @@ #ifdef WIN32 #include -#define snprintf _snprintf #endif const char* g_DeviceNameMicroFPGAHub = "MicroFPGA-Hub"; diff --git a/DeviceAdapters/MicroPoint/MicroPoint.cpp b/DeviceAdapters/MicroPoint/MicroPoint.cpp index 9c6f182e4..2981d43f0 100644 --- a/DeviceAdapters/MicroPoint/MicroPoint.cpp +++ b/DeviceAdapters/MicroPoint/MicroPoint.cpp @@ -29,7 +29,6 @@ #ifdef WIN32 #pragma warning(disable: 4355) #endif -#include "FixSnprintf.h" #include "MicroPoint.h" #include diff --git a/DeviceAdapters/NKTSuperK/SuperKModule.cpp b/DeviceAdapters/NKTSuperK/SuperKModule.cpp index 435d2bb9f..2cea36e9c 100644 --- a/DeviceAdapters/NKTSuperK/SuperKModule.cpp +++ b/DeviceAdapters/NKTSuperK/SuperKModule.cpp @@ -32,7 +32,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "SuperK.h" diff --git a/DeviceAdapters/NeoPixel/NeoPixel.cpp b/DeviceAdapters/NeoPixel/NeoPixel.cpp index 725851f58..098ab4991 100644 --- a/DeviceAdapters/NeoPixel/NeoPixel.cpp +++ b/DeviceAdapters/NeoPixel/NeoPixel.cpp @@ -20,7 +20,6 @@ #define WIN32_LEAN_AND_MEAN #include #endif -#include "FixSnprintf.h" const char* g_DeviceNameShutter = "NeoPixel-Shutter"; diff --git a/DeviceAdapters/Neos/Neos.cpp b/DeviceAdapters/Neos/Neos.cpp index 2992376f7..a30cab7a2 100644 --- a/DeviceAdapters/Neos/Neos.cpp +++ b/DeviceAdapters/Neos/Neos.cpp @@ -20,7 +20,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "Neos.h" #include diff --git a/DeviceAdapters/NewportSMC/Newport.cpp b/DeviceAdapters/NewportSMC/Newport.cpp index 768076375..2356664ad 100644 --- a/DeviceAdapters/NewportSMC/Newport.cpp +++ b/DeviceAdapters/NewportSMC/Newport.cpp @@ -26,7 +26,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "Newport.h" #include diff --git a/DeviceAdapters/Nikon/Nikon.cpp b/DeviceAdapters/Nikon/Nikon.cpp index 3ba84a493..e405bda8a 100644 --- a/DeviceAdapters/Nikon/Nikon.cpp +++ b/DeviceAdapters/Nikon/Nikon.cpp @@ -24,7 +24,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "Nikon.h" #include diff --git a/DeviceAdapters/OVP_ECS2/OVP_ECS2.cpp b/DeviceAdapters/OVP_ECS2/OVP_ECS2.cpp index 1d245a951..9f01fa25b 100644 --- a/DeviceAdapters/OVP_ECS2/OVP_ECS2.cpp +++ b/DeviceAdapters/OVP_ECS2/OVP_ECS2.cpp @@ -20,9 +20,6 @@ // AUTHOR: Jon Daniels (jon@asiimaging.com) 06/2014 // - -#include "FixSnprintf.h" - #include "OVP_ECS2.h" #include "MMDevice.h" #include "DeviceBase.h" diff --git a/DeviceAdapters/ObjectiveImaging/OasisControl.cpp b/DeviceAdapters/ObjectiveImaging/OasisControl.cpp index 5c39b1463..e68f60729 100644 --- a/DeviceAdapters/ObjectiveImaging/OasisControl.cpp +++ b/DeviceAdapters/ObjectiveImaging/OasisControl.cpp @@ -32,7 +32,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" // The main Oasis controller API #include "oasis4i.h" diff --git a/DeviceAdapters/Omicron/CoherentOBISDirect.cpp b/DeviceAdapters/Omicron/CoherentOBISDirect.cpp index 01854a379..8e484376b 100644 --- a/DeviceAdapters/Omicron/CoherentOBISDirect.cpp +++ b/DeviceAdapters/Omicron/CoherentOBISDirect.cpp @@ -29,7 +29,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "CoherentOBISDirect.h" diff --git a/DeviceAdapters/OxxiusCombiner/Cobolt08_01.cpp b/DeviceAdapters/OxxiusCombiner/Cobolt08_01.cpp new file mode 100644 index 000000000..a49336791 --- /dev/null +++ b/DeviceAdapters/OxxiusCombiner/Cobolt08_01.cpp @@ -0,0 +1,169 @@ +#include "Cobolt08_01.h" +using namespace std; + +Cobolt08_01::Cobolt08_01(const char* nameAndSlot) : initialized_(false) +{ + string tSlot = string(nameAndSlot); + + name_.assign(tSlot);// set laser name + tSlot = tSlot.substr(tSlot.length() - 1, 1); + slot_ = (unsigned int)atoi(tSlot.c_str());// set laser slot + + parentHub_; + busy_ = false; + laserOn_ = false; + + alarm_ = ""; + + InitializeDefaultErrorMessages(); + SetErrorText(ERR_NO_PORT_SET, "Hub Device not found. The Laser combiner is needed to create this device"); + + // parent ID display + CreateHubIDProperty(); +} + + +Cobolt08_01::~Cobolt08_01() +{ + Shutdown(); +} + + + +int Cobolt08_01::Initialize() +{ + + if (!initialized_) { + + parentHub_ = static_cast(GetParentHub()); + if (!parentHub_) { + return DEVICE_COMM_HUB_MISSING; + } + + + + RETURN_ON_MM_ERROR(UpdateStatus()); + + initialized_ = true; + + } + + return DEVICE_OK; + +} + + +int Cobolt08_01::Shutdown() +{ + initialized_ = false; + return DEVICE_OK; +} + + + + +int Cobolt08_01::Fire(double deltaT) +{ + string activate_query = "dl 1"; //SOUR1:AM:STAT ON + string deactivate_query = "dl 0"; + + if (laserOn_ == false) { + laserOn_ = true; + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), slot_, activate_query.c_str(), false)); + } + CDeviceUtils::SleepMs((long)(deltaT)); + laserOn_ = false; + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), slot_, deactivate_query.c_str(), false)); + //At the end, the laser is set to off + + return DEVICE_OK; +} + +//Handlers + +int Cobolt08_01::OnAlarm(MM::PropertyBase* pProp, MM::ActionType) +{ + unsigned int alarmInt = 99; + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), slot_, "?F", false)); + + parentHub_->ParseforInteger(alarmInt); + + switch (alarmInt) { + case 0: + alarm_ = "No Alarm"; + break; + case 1: + alarm_ = "Out-of-bounds diode current"; + break; + case 2: + alarm_ = "Unexpected laser power value"; + break; + case 3: + alarm_ = "Out-of-bounds supply voltage"; + break; + case 4: + alarm_ = "Out-of-bounds internal temperature"; + break; + case 5: + alarm_ = "Out-of-bounds baseplate temperature"; + break; + case 7: + alarm_ = "Interlock circuit open"; + break; + case 8: + alarm_ = "Soft reset"; + break; + default: + alarm_ = "Other alarm"; + } + + pProp->Set(alarm_.c_str()); + + return DEVICE_OK; +} + + +int Cobolt08_01::OnEmissionOnOff(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::BeforeGet) { + double status; + ostringstream query; + + query << "?l"; + + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), slot_, query.str().c_str(), false)); + parentHub_->ParseforDouble(status); + + if (status == 1) { + laserOn_ = true; + } + else { + laserOn_ = false; + } + + if (laserOn_) { + pProp->Set("ON"); + } + else { + pProp->Set("OFF"); + } + } + else if (eAct == MM::AfterSet) { + string newEmissionStatus, newCommand = ""; + + pProp->Get(newEmissionStatus); + + if (newEmissionStatus.compare("ON") == 0) { + newCommand = "dl 1"; //original: SOUR1:AM:STAT ON + laserOn_ = true; + } + else if (newEmissionStatus.compare("OFF") == 0) { + newCommand = "dl 0"; + laserOn_ = false; + } + + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), slot_, newCommand.c_str(), false)); + } + + return DEVICE_OK; +} diff --git a/DeviceAdapters/OxxiusCombiner/Cobolt08_01.h b/DeviceAdapters/OxxiusCombiner/Cobolt08_01.h new file mode 100644 index 000000000..a2001d803 --- /dev/null +++ b/DeviceAdapters/OxxiusCombiner/Cobolt08_01.h @@ -0,0 +1,41 @@ +#pragma once +#include "GenericLaser.h" +using namespace std; + +class Cobolt08_01 : public GenericLaser +{ +public: + Cobolt08_01(const char* nameAndSlot); //OK + ~Cobolt08_01(); //OK + + int Initialize(); //OK + int Shutdown(); //OK + + int Fire(double deltaT); //OK + + int OnPowerSetPoint(MM::PropertyBase* pProp, MM::ActionType eAct) { return DEVICE_OK; };//Aucun pour l'instant + int OnCurrentSetPoint(MM::PropertyBase* pProp, MM::ActionType eAct) { return DEVICE_OK; };//Aucun pour l'instant + int OnEmissionOnOff(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnControlMode(MM::PropertyBase* pProp, MM::ActionType eAct) { return DEVICE_OK; };//Aucun pour l'instant + int OnAnalogMod(MM::PropertyBase* pProp, MM::ActionType eAct) { return DEVICE_OK; };//Aucun pour l'instant + int OnDigitalMod(MM::PropertyBase* pProp, MM::ActionType eAct) { return DEVICE_OK; };//Aucun pour l'instant + int OnState(MM::PropertyBase* pProp, MM::ActionType eAct) { return DEVICE_OK; };//Aucun pour l'instant + int OnAlarm(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnFire(MM::PropertyBase* pProp, MM::ActionType eAct) { return DEVICE_OK; };//Aucun pour l'instant + + int OnPowerReadback(MM::PropertyBase* pProp, MM::ActionType eAct) { return DEVICE_OK; };//Aucun pour l'instant + int OnCurrentReadback(MM::PropertyBase* pProp, MM::ActionType eAct) { return DEVICE_OK; };//Aucun pour l'instant + int OnOperatingMode(MM::PropertyBase* pProp, MM::ActionType eAct) { return DEVICE_OK; };//Aucun pour l'instant + +private: + bool initialized_; + + //// SPECIFIC TO Cobolt08_01 + bool laserOn_; + //double maxPower_; + //double minPower_; + string alarm_; + //double wavelength_; + //string description_; + +}; \ No newline at end of file diff --git a/DeviceAdapters/OxxiusCombiner/CoherentObis.cpp b/DeviceAdapters/OxxiusCombiner/CoherentObis.cpp new file mode 100644 index 000000000..c17e358fb --- /dev/null +++ b/DeviceAdapters/OxxiusCombiner/CoherentObis.cpp @@ -0,0 +1,345 @@ +#include "CoherentObis.h" +using namespace std; + +CoherentObis::CoherentObis(const char* nameAndSlot) +{ + initialized_ = false; + + string tSlot = string(nameAndSlot); + + name_.assign(tSlot);// set laser name + tSlot = tSlot.substr(tSlot.length() - 1, 1); + slot_ = (unsigned int)atoi(tSlot.c_str());// set laser slot + + parentHub_; + busy_ = false; + laserOn_ = false; + + alarm_ = ""; + + InitializeDefaultErrorMessages(); + SetErrorText(ERR_NO_PORT_SET, "Hub Device not found. The Laser combiner is needed to create this device"); + + // parent ID display + CreateHubIDProperty(); +} + + +CoherentObis::~CoherentObis() +{ + Shutdown(); +} + + +int CoherentObis::Initialize() +{ + + if (!initialized_) { + + parentHub_ = static_cast(GetParentHub()); + if (!parentHub_) { + return DEVICE_COMM_HUB_MISSING; + } + + string cmdmaxpwr = "SOUR1:POW:LIM:HIGH?"; + string cmdminpwr = "SOUR1:POW:LIM:LOW?"; + parentHub_->QueryCommand(this, GetCoreCallback(), slot_, cmdmaxpwr.c_str(), true); + parentHub_->ParseforDouble(maxPower_); + parentHub_->QueryCommand(this, GetCoreCallback(), slot_, cmdminpwr.c_str(), true); + parentHub_->ParseforDouble(minPower_); + + + + // Set property list + // ----------------- + // Name (read only) + RETURN_ON_MM_ERROR(CreateProperty(MM::g_Keyword_Name, name_.c_str(), MM::String, true)); + + // store the device serial number + parentHub_->QueryCommand(this, GetCoreCallback(), slot_, "SYST:INF:SNUM?", false); + parentHub_->ParseforString(serialNumber_); + + // Description (read only) + ostringstream descriPt1; + parentHub_->QueryCommand(this, GetCoreCallback(), slot_, "*IDN?", false); + parentHub_->ParseforString(description_); //example: "Coherent, Inc - OBIS 405nm 50mW C - V1.0.1 - Dec 14 2010" + + //we ignore "spa" + + //define the laser's wavelength + parentHub_->QueryCommand(this, GetCoreCallback(), slot_, "SYST1:INF:WAV?", false); + parentHub_->ParseforDouble(wavelength_); + + //let's skip the nominal power retrieve + + //let's assume there are no MPA or AOMs + + std::ostringstream InfoMessage1; // Debug purposes only + InfoMessage1 << "INFOS RECUPEREES: maxpower:"<LogMessage(this, InfoMessage1.str().c_str(), false); + + descriPt1 << "OBIS source on slot " << slot_; + descriPt1 << ", " << string(serialNumber_); + + RETURN_ON_MM_ERROR(CreateProperty(MM::g_Keyword_Description, descriPt1.str().c_str(), MM::String, true)); + + // Alarm (read only) + CPropertyAction* pAct = new CPropertyAction(this, &GenericLaser::OnAlarm); + RETURN_ON_MM_ERROR(CreateProperty("Alarm", "None", MM::String, true, pAct)); + //No status + //No Control mode + + + + //Emission selector + pAct = new CPropertyAction(this, &GenericLaser::OnEmissionOnOff); + RETURN_ON_MM_ERROR(CreateProperty("Emission", "", MM::String, false, pAct)); + AddAllowedValue("Emission", "ON"); + AddAllowedValue("Emission", "OFF"); + + //No Current set point ? + maxPower_ = 100.0; + + //Set Power + pAct = new CPropertyAction(this, &GenericLaser::OnPowerSetPoint); + RETURN_ON_MM_ERROR(CreateProperty("Power set point", "0.0", MM::Float, false, pAct)); + SetPropertyLimits("Power set point", 0.0, maxPower_); + + //Read Power + pAct = new CPropertyAction(this, &GenericLaser::OnPowerReadback); + RETURN_ON_MM_ERROR(CreateProperty("Read Power point", "0.0", MM::Float, false, pAct)); + + + //Read Current + pAct = new CPropertyAction(this, &GenericLaser::OnCurrentReadback); + RETURN_ON_MM_ERROR(CreateProperty("Read Current point", "0.0", MM::Float, false, pAct)); + + //Fire + pAct = new CPropertyAction(this, &GenericLaser::OnFire); + RETURN_ON_MM_ERROR(CreateProperty("Fire", "0", MM::Float, false, pAct)); + + //Modes + pAct = new CPropertyAction(this, &GenericLaser::OnOperatingMode); + RETURN_ON_MM_ERROR(CreateProperty("Mode", "", MM::String, false, pAct)); + + + RETURN_ON_MM_ERROR(UpdateStatus()); + + initialized_ = true; + + } + + return DEVICE_OK; + +} + + +int CoherentObis::Fire(double deltaT) +{ + string activate_query = "dl 1"; //SOUR1:AM:STAT ON + string deactivate_query = "dl 0"; + + if (laserOn_ == false) { + laserOn_ = true; + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), slot_, activate_query.c_str(), false)); + } + CDeviceUtils::SleepMs((long)(deltaT)); + laserOn_ = false; + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), slot_, deactivate_query.c_str(), false)); + //At the end, the laser is set to off + + return DEVICE_OK; +} + +//Handlers + +int CoherentObis::OnAlarm(MM::PropertyBase* pProp, MM::ActionType) +{ + unsigned int alarmInt = 99; + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), slot_, "?F", false)); + + parentHub_->ParseforInteger(alarmInt); + + switch (alarmInt) { + case 0: + alarm_ = "No Alarm"; + break; + case 1: + alarm_ = "Out-of-bounds diode current"; + break; + case 2: + alarm_ = "Unexpected laser power value"; + break; + case 3: + alarm_ = "Out-of-bounds supply voltage"; + break; + case 4: + alarm_ = "Out-of-bounds internal temperature"; + break; + case 5: + alarm_ = "Out-of-bounds baseplate temperature"; + break; + case 7: + alarm_ = "Interlock circuit open"; + break; + case 8: + alarm_ = "Soft reset"; + break; + default: + alarm_ = "Other alarm"; + } + + pProp->Set(alarm_.c_str()); + + return DEVICE_OK; +} + + +int CoherentObis::OnEmissionOnOff(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::BeforeGet) { + double status; + ostringstream query; + + query << "?l"; + + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), slot_, query.str().c_str(), false)); + parentHub_->ParseforDouble(status); + + if (status == 1) { + laserOn_ = true; + } + else { + laserOn_ = false; + } + + if (laserOn_) { + pProp->Set("ON"); + } + else { + pProp->Set("OFF"); + } + } + else if (eAct == MM::AfterSet) { + string newEmissionStatus, newCommand = ""; + + pProp->Get(newEmissionStatus); + + if (newEmissionStatus.compare("ON") == 0) { + newCommand = "dl 1"; //original: SOUR1:AM:STAT ON + laserOn_ = true; + } + else if (newEmissionStatus.compare("OFF") == 0) { + newCommand = "dl 0"; + laserOn_ = false; + } + + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), slot_, newCommand.c_str(), false)); + } + + return DEVICE_OK; +} + +/* +int CoherentObis::OnPowerSetPoint(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::BeforeGet) { + float absSetPoint_; + string command = "SOUR1:POW:LEV:IMM:AMPL?"; + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), slot_, command.c_str(), false)); + parentHub_->ParseforFloat(absSetPoint_); + + pProp->Set(absSetPoint_); + } + else if (eAct == MM::AfterSet) { + double GUISetPoint = 0.0; + pProp->Get(GUISetPoint); + + if ((GUISetPoint >= 0.0) && (GUISetPoint <= maxPower_)) { //&& instead of || + string command = "SOUR1:POW:LEV:IMM:AMPL "; + command += to_string(GUISetPoint); + + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), slot_, command.c_str(), false)); + + } + else { + // If the value entered through the GUI is not valid, read the machine value + OnPowerSetPoint(pProp, MM::BeforeGet); + } + } + return DEVICE_OK; +} +*/ + +int CoherentObis::OnPowerReadback(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::BeforeGet) + { + string command = "SOUR1:POW:LEV:IMM:AMPL?"; + float power; + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), slot_, command.c_str(), false)); + parentHub_->ParseforFloat(power); + pProp->Set(power); + } + return DEVICE_OK; +} + +int CoherentObis::OnCurrentReadback(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::BeforeGet) + { + string command = "SOUR1:POW:CURR?"; + float current; + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), slot_, command.c_str(), false)); + parentHub_->ParseforFloat(current); + pProp->Set(current); + } + return DEVICE_OK; +} + +int CoherentObis::OnOperatingMode(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if(eAct==MM::BeforeGet) + { + string command = "SOUR:AM:SOUR?"; + string operating_mode; + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), slot_, command.c_str(), false)); + parentHub_->ParseforString(operating_mode); + pProp->Set(operating_mode.c_str()); + } + else if (eAct == MM::AfterSet) { + string input = ""; + string command; + pProp->Get(input); + if (input == "CWP" || input == "CWC") { + command = "SOUR:AM:INT "; + } + else if (input == "DIG" || input == "ANAL" || input == "MIX" || input == "DIGITAL" || input == "ANALOG" || input == "MIXED" || input == "DIGSO" || input == "MIXSO") { + command = "SOUR:AM:EXT "; + } + command += input; + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), slot_, command.c_str(), false)); + string rep; + parentHub_->ParseforString(rep); + + + std::ostringstream Info; /////DEBUG + Info << "DEBUG OPERATING MODE: " << rep << "avec la commande encoyée suivante: " << command; + GetCoreCallback()->LogMessage(this, Info.str().c_str(), false); + + } + + return DEVICE_OK; + +} +/* +int CoherentObis::OnFire(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::AfterSet) { + double input; + pProp->Get(input); + return Fire(input); + } + return DEVICE_OK; +} +*/ \ No newline at end of file diff --git a/DeviceAdapters/OxxiusCombiner/CoherentObis.h b/DeviceAdapters/OxxiusCombiner/CoherentObis.h new file mode 100644 index 000000000..ca9c1f5e4 --- /dev/null +++ b/DeviceAdapters/OxxiusCombiner/CoherentObis.h @@ -0,0 +1,39 @@ +#pragma once +#include "GenericLaser.h" +using namespace std; + +class CoherentObis : public GenericLaser +{ +public: + CoherentObis(const char* nameAndSlot); //OK + ~CoherentObis(); //OK + + int Initialize(); //OK + + int Fire(double deltaT); + + // int OnPowerSetPoint(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnCurrentSetPoint(MM::PropertyBase* pProp, MM::ActionType eAct) { return DEVICE_OK; };//Aucun pour l'instant + int OnEmissionOnOff(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnControlMode(MM::PropertyBase* pProp, MM::ActionType eAct) { return DEVICE_OK; };//Aucun pour l'instant + int OnAnalogMod(MM::PropertyBase* pProp, MM::ActionType eAct) { return DEVICE_OK; };//Aucun pour l'instant + int OnDigitalMod(MM::PropertyBase* pProp, MM::ActionType eAct) { return DEVICE_OK; };//Aucun pour l'instant + int OnState(MM::PropertyBase* pProp, MM::ActionType eAct) { return DEVICE_OK; };//Aucun pour l'instant + int OnAlarm(MM::PropertyBase* pProp, MM::ActionType eAct); + // int OnFire(MM::PropertyBase* pProp, MM::ActionType eAct); + + int OnPowerReadback(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnCurrentReadback(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnOperatingMode(MM::PropertyBase* pProp, MM::ActionType eAct); + +private: + bool initialized_; + + //// SPECIFIC TO CoherentObis + double maxPower_; + double minPower_; + string alarm_; + double wavelength_; + string description_; + +}; diff --git a/DeviceAdapters/OxxiusCombiner/GenericLaser.cpp b/DeviceAdapters/OxxiusCombiner/GenericLaser.cpp new file mode 100644 index 000000000..281fabc10 --- /dev/null +++ b/DeviceAdapters/OxxiusCombiner/GenericLaser.cpp @@ -0,0 +1,104 @@ +#include "GenericLaser.h" + +int GenericLaser::Shutdown() +{ + initialized_ = false; + return DEVICE_OK; +} + +void GenericLaser::GetName(char* Name) const +{ + CDeviceUtils::CopyLimitedString(Name, name_.c_str()); +} + +bool GenericLaser::Busy() +{ + return busy_; +} + +int GenericLaser::SetOpen(bool openCommand) +{ + string activate_query = "DL 1"; + string deactivate_query = "DL 0"; + + if (openCommand == false) { + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), slot_, deactivate_query.c_str(), false)); + laserOn_ = false; + } + else { + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), slot_, activate_query.c_str(), false)); + laserOn_ = true; + } + return DEVICE_OK; +} + + +int GenericLaser::GetOpen(bool& isOpen) +{ + unsigned int status = 0; + ostringstream query; + + query << "?CS " << slot_; + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), NO_SLOT, query.str().c_str(), false)); + parentHub_->ParseforInteger(status); + + switch (status) { + case 0: // Emission is OFF + laserOn_ = false; + break; + case 1: // Emission is ON + laserOn_ = true; + break; + default: // Other cases: emission is potentially ON + laserOn_ = true; + } + isOpen = laserOn_; + return DEVICE_OK; +} + + + +int GenericLaser::OnPowerSetPoint(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::BeforeGet) { + double relSetPoint_ = 0.0; + string powSetPointQuery = "?PPL"; // Generic query to read the power set point (percentage) + ostringstream powSetPointPhrase; + powSetPointPhrase << powSetPointQuery << slot_; + + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), NO_SLOT, powSetPointPhrase.str().c_str(), false)); + parentHub_->ParseforPercent(relSetPoint_); + + pProp->Set(relSetPoint_); + } + else if (eAct == MM::AfterSet) { + + double GUISetPoint = 0.0; + pProp->Get(GUISetPoint); + + if ((GUISetPoint >= 0.0) || (GUISetPoint <= POW_UPPER_LIMIT)) { + string powSetPointCommand = "PPL"; // Generic query to set the laser power (percentage) + + ostringstream powSetPointPhrase; + char* powerSPString = new char[20]; + strcpy( powerSPString, CDeviceUtils::ConvertToString(GUISetPoint) ); + + powSetPointPhrase << powSetPointCommand << slot_ << " " << powerSPString; + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), NO_SLOT, powSetPointPhrase.str().c_str(), false)); + } + else {} // Do nothing if the GUI set point is out-of-bounds + + } + return DEVICE_OK; +} + + +int GenericLaser::OnFire(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::AfterSet) { + double input; + pProp->Get(input); + return Fire(input); + } + return DEVICE_OK; +} \ No newline at end of file diff --git a/DeviceAdapters/OxxiusCombiner/GenericLaser.h b/DeviceAdapters/OxxiusCombiner/GenericLaser.h new file mode 100644 index 000000000..2a6d9e3d3 --- /dev/null +++ b/DeviceAdapters/OxxiusCombiner/GenericLaser.h @@ -0,0 +1,43 @@ +#pragma once +#include "OxxiusCombinerHub.h" +using namespace std; + +#define POW_UPPER_LIMIT 100.0 + +class GenericLaser : public CShutterBase +{ +public: + virtual int Initialize()=0; + int Shutdown(); + + virtual void GetName(char* pszName) const; + virtual bool Busy(); + virtual int SetOpen(bool openCommand = true); + virtual int GetOpen(bool& isOpen); + virtual int Fire(double deltaT) = 0; + + int OnPowerSetPoint(MM::PropertyBase* pProp, MM::ActionType eAct); + virtual int OnCurrentSetPoint(MM::PropertyBase* , MM::ActionType ) = 0; + virtual int OnEmissionOnOff(MM::PropertyBase* , MM::ActionType ) = 0; + virtual int OnControlMode(MM::PropertyBase* , MM::ActionType ) = 0; + virtual int OnAnalogMod(MM::PropertyBase* , MM::ActionType ) = 0; + virtual int OnDigitalMod(MM::PropertyBase* , MM::ActionType ) = 0; + virtual int OnState(MM::PropertyBase* , MM::ActionType ) = 0; + virtual int OnAlarm(MM::PropertyBase* , MM::ActionType ) = 0; + int OnFire(MM::PropertyBase* , MM::ActionType ); + + virtual int OnPowerReadback(MM::PropertyBase* , MM::ActionType ) = 0; + virtual int OnCurrentReadback(MM::PropertyBase* , MM::ActionType ) = 0; + virtual int OnOperatingMode(MM::PropertyBase* , MM::ActionType ) = 0; + +protected: // Common data to all lasers + string serialNumber_; + string name_; + unsigned int slot_; + OxxiusCombinerHub* parentHub_; + bool busy_; + bool laserOn_; + +private: + bool initialized_; +}; \ No newline at end of file diff --git a/DeviceAdapters/OxxiusCombiner/Makefile.am b/DeviceAdapters/OxxiusCombiner/Makefile.am index e0d93215d..0c642455c 100644 --- a/DeviceAdapters/OxxiusCombiner/Makefile.am +++ b/DeviceAdapters/OxxiusCombiner/Makefile.am @@ -1,6 +1,16 @@ AM_CXXFLAGS = $(MMDEVAPI_CXXFLAGS) deviceadapter_LTLIBRARIES = libmmgr_dal_OxxiusCombiner.la -libmmgr_dal_OxxiusCombiner_la_SOURCES = OnBoardHW.cpp OnBoardHW.h Oxxius_combiner.cpp Oxxius_combiner.h +libmmgr_dal_OxxiusCombiner_la_SOURCES = \ + Cobolt08_01.cpp \ + CoherentObis.cpp \ + GenericLaser.cpp \ + OxxiusCombinerHub.cpp \ + OxxiusFlipMirror.cpp \ + OxxiusLBX.cpp \ + OxxiusLCX.cpp \ + OxxiusMDual.cpp \ + OxxiusShutter.cpp + libmmgr_dal_OxxiusCombiner_la_LIBADD = $(MMDEVAPI_LIBADD) libmmgr_dal_OxxiusCombiner_la_LDFLAGS = $(MMDEVAPI_LDFLAGS) diff --git a/DeviceAdapters/OxxiusCombiner/OnBoardHW.cpp b/DeviceAdapters/OxxiusCombiner/OnBoardHW.cpp deleted file mode 100644 index 88596fe6e..000000000 --- a/DeviceAdapters/OxxiusCombiner/OnBoardHW.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// FILE: OnBoardHW.cpp -// PROJECT: Micro-Manager -// SUBSYSTEM: DeviceAdapters -//----------------------------------------------------------------------------- -// DESCRIPTION: Describes the contents of the devices (lasers, modulators) -// operating inside a combiner -// COPYRIGHT: Oxxius SA, 2013-2018 -// LICENSE: LGPL -// AUTHOR: Tristan Martinez -// - -#include "OnBoardHW.h" -#include -#include - -using namespace std; - -OnBoardHW::OnBoardHW(unsigned int numberOfSlots) { - AOM1Position_ = 0; - AOM2Position_ = 0; - - if (numberOfSlots <= MAX_NUMBER_OF_SLOTS) { - slotMax_ = numberOfSlots; - } else { - slotMax_ = MAX_NUMBER_OF_SLOTS; - } - - for(unsigned int i = 0; i 0) & (slot <= MAX_NUMBER_OF_SLOTS) ) { - std::string modelStr = "", - wavelengthStr = "", - nominalPowerStr = "", - newSourceDescription = string(newSourceType), - token = "-"; - std::size_t found; - - found = newSourceDescription.find(token); - if (found!=std::string::npos) { - modelStr = newSourceDescription.substr(0, found); - } - - if (modelStr.compare("LBX") == 0) { // The source is a LBX - sourceType_[slot-1] = 10; - } else if (modelStr.compare("LCX") == 0) { // The source is a LCX - if( slot == AOM1Position_ ) { - sourceType_[slot-1] = 21; - } else if( slot == AOM2Position_ ) { - sourceType_[slot-1] = 22; - } else { - sourceType_[slot-1] = 20; - } - } else { // Should not happen: unkown type - sourceType_[slot-1] = 99; - } - - newSourceDescription.erase(0, found+1); - - found = newSourceDescription.find(token); - if (found!=std::string::npos) { - wavelengthStr = newSourceDescription.substr(0, found); - sourceWavelength_[slot-1] = (unsigned int) atoi(wavelengthStr.c_str()); - } - newSourceDescription.erase(0, found+1); - - nominalPowerStr.assign(newSourceDescription); - sourceNominalPower_[slot-1] = (unsigned int) atoi(nominalPowerStr.c_str()); - } -} - - -void OnBoardHW::GetSerialNumber(unsigned int slot, char * serialN) { - strcpy(serialN, sourceSerialNumber_[slot-1].c_str()); -} - - -void OnBoardHW::SetSerialNumber(unsigned int slot, const char* newSerialN) { - if(sourceType_[slot-1] != 0) { - sourceSerialNumber_[slot-1].assign(newSerialN); - } -} - - -void OnBoardHW::SetAOMPos(unsigned int AOM1, unsigned int AOM2) { - if (AOM1>0) - AOM1Position_ = AOM1 + 1; - - if (AOM2>0) - AOM2Position_ = AOM2 + 1; -} - - -void OnBoardHW::GetNominalPower(unsigned int slot, unsigned int nomPower) { - if(sourceType_[slot-1] != 0) { - nomPower = sourceNominalPower_[slot-1]; - } -} diff --git a/DeviceAdapters/OxxiusCombiner/OnBoardHW.h b/DeviceAdapters/OxxiusCombiner/OnBoardHW.h deleted file mode 100644 index d969c5a82..000000000 --- a/DeviceAdapters/OxxiusCombiner/OnBoardHW.h +++ /dev/null @@ -1,67 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// FILE: OnBoardHW.h -// PROJECT: Micro-Manager -// SUBSYSTEM: DeviceAdapters -//----------------------------------------------------------------------------- -// DESCRIPTION: Describes the contents of the devices (lasers, modulators) -// operating inside a combiner -// COPYRIGHT: Oxxius SA, 2013-2018 -// LICENSE: LGPL -// AUTHOR: Tristan Martinez -// - - -// Laser sources exists in different models... -// SourceType[] depicts the can take the following values: -// -// -// model[0] model[1] -// (major) (minor) -// 1 0 -> standard LBX -// 2 0 -> standard LCX -// 2 1 -> LCX linked to an AOM number 1 -// 2 2 -> LCX linked to an AOM number 2 -// 2 5 -> LCX with power adjustment - -#ifndef _OXXIUS_ONBOARDHW_H_ -#define _OXXIUS_ONBOARDHW_H_ - -#define MAX_NUMBER_OF_SLOTS 6 -#define MAX_CHARACTERS 256 - -#include -#include - -class OnBoardHW -{ -public: - OnBoardHW(unsigned int numberOfSlots); - ~OnBoardHW(); - - bool IsEmpty(); - - unsigned int GetType(unsigned int slot); - void SetType(unsigned int slot, const char* newSourceType); - - void GetSerialNumber(unsigned int slot, char* serialN); - void SetSerialNumber(unsigned int slot, const char* newSerialN); - - void SetAOMPos(unsigned int AOM1, unsigned int AOM2); - - void GetNominalPower(unsigned int slot, unsigned int nomPower); - -private: - unsigned int sourceType_[MAX_NUMBER_OF_SLOTS]; - unsigned int sourceWavelength_[MAX_NUMBER_OF_SLOTS]; - unsigned int sourceNominalPower_[MAX_NUMBER_OF_SLOTS]; - std::vector sourceSerialNumber_; -// std::array sourceSerialNumber_; -// std::array sourceDescription_; - - unsigned int slotMax_; - unsigned int wavelength_; - unsigned int AOM1Position_; - unsigned int AOM2Position_; -}; - -#endif // _OXXIUS_ONBOARDHW_H_ \ No newline at end of file diff --git a/DeviceAdapters/OxxiusCombiner/OxxiusCombiner.vcxproj b/DeviceAdapters/OxxiusCombiner/OxxiusCombiner.vcxproj index 7966a9772..1e3a7d3ce 100644 --- a/DeviceAdapters/OxxiusCombiner/OxxiusCombiner.vcxproj +++ b/DeviceAdapters/OxxiusCombiner/OxxiusCombiner.vcxproj @@ -91,21 +91,31 @@ - - - - - - - + + {b8c95f39-54bf-40a9-807b-598df2821d55} + - + + + + + + + + + - - {b8c95f39-54bf-40a9-807b-598df2821d55} - + + + + + + + + + diff --git a/DeviceAdapters/OxxiusCombiner/OxxiusCombiner.vcxproj.filters b/DeviceAdapters/OxxiusCombiner/OxxiusCombiner.vcxproj.filters new file mode 100644 index 000000000..eefe3a775 --- /dev/null +++ b/DeviceAdapters/OxxiusCombiner/OxxiusCombiner.vcxproj.filters @@ -0,0 +1,69 @@ + + + + + {15751bdb-12d5-452c-8197-c6762010b6b3} + + + {d5eef910-aac2-4511-8c33-ad79c7499d4d} + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/DeviceAdapters/OxxiusCombiner/OxxiusCombinerHub.cpp b/DeviceAdapters/OxxiusCombiner/OxxiusCombinerHub.cpp new file mode 100644 index 000000000..107c38b35 --- /dev/null +++ b/DeviceAdapters/OxxiusCombiner/OxxiusCombinerHub.cpp @@ -0,0 +1,781 @@ +#include "OxxiusCombinerHub.h" +#include "GenericLaser.h" +#include "OxxiusShutter.h" +#include "OxxiusMDual.h" +#include "OxxiusFlipMirror.h" +#include "OxxiusLBX.h" +#include "OxxiusLCX.h" +#include "CoherentObis.h" +#include "Cobolt08_01.h" + +#include "ModuleInterface.h" + +using namespace std; + + +// Oxxius devices +const char* g_OxxiusCombinerDeviceName = "Combiner"; + +const char* g_LCXLaserDeviceName = "LCX laser source"; +const char* g_LCXLaser1DeviceName = "LCX laser source 1"; +const char* g_LCXLaser2DeviceName = "LCX laser source 2"; +const char* g_LCXLaser3DeviceName = "LCX laser source 3"; +const char* g_LCXLaser4DeviceName = "LCX laser source 4"; +const char* g_LCXLaser5DeviceName = "LCX laser source 5"; +const char* g_LCXLaser6DeviceName = "LCX laser source 6"; + +const char* g_LBXLaserDeviceName = "LBX laser source"; +const char* g_LBXLaser1DeviceName = "LBX laser source 1"; +const char* g_LBXLaser2DeviceName = "LBX laser source 2"; +const char* g_LBXLaser3DeviceName = "LBX laser source 3"; +const char* g_LBXLaser4DeviceName = "LBX laser source 4"; +const char* g_LBXLaser5DeviceName = "LBX laser source 5"; +const char* g_LBXLaser6DeviceName = "LBX laser source 6"; + +const char* g_OBISLaserDeviceName = "Obis laser source"; +const char* g_ObisLaser1DeviceName = "Obis laser source 1"; +const char* g_ObisLaser2DeviceName = "Obis laser source 2"; +const char* g_ObisLaser3DeviceName = "Obis laser source 3"; +const char* g_ObisLaser4DeviceName = "Obis laser source 4"; +const char* g_ObisLaser5DeviceName = "Obis laser source 5"; +const char* g_ObisLaser6DeviceName = "Obis laser source 6"; + +const char* g_OxxiusShutterDeviceName = "Shutter"; +const char* g_OxxiusShutter1DeviceName = "Shutter 1"; +const char* g_OxxiusShutter2DeviceName = "Shutter 2"; +const char* g_OxxiusMDualDeviceName = "MDual"; +const char* g_OxxiusMDualADeviceName = "MDual A"; +const char* g_OxxiusMDualBDeviceName = "MDual B"; +const char* g_OxxiusMDualCDeviceName = "MDual C"; +const char* g_OxxiusFlipMirrorDeviceName = "Flip-Mirror"; +const char* g_OxxiusFlipMirror1DeviceName = "Flip-Mirror 1"; +const char* g_OxxiusFlipMirror2DeviceName = "Flip-Mirror 2"; + + + + +const char* g_slotPrefix[7] = { "","L1 ","L2 ","L3 ","L4 ","L5 ","L6 " }; + +const char* convertable[3] = { "A", "B", "C" }; + +/////////////////////////////////////////////////////////////////////////////// +// Exported MMDevice API +/////////////////////////////////////////////////////////////////////////////// +MODULE_API void InitializeModuleData() +{ + RegisterDevice(g_OxxiusCombinerDeviceName, MM::HubDevice, "Oxxius laser combiner controlled through serial interface"); + + RegisterDevice(g_LCXLaser1DeviceName, MM::ShutterDevice, "LCX Laser on slot 1"); + RegisterDevice(g_LCXLaser2DeviceName, MM::ShutterDevice, "LCX Laser on slot 2"); + RegisterDevice(g_LCXLaser3DeviceName, MM::ShutterDevice, "LCX Laser on slot 3"); + RegisterDevice(g_LCXLaser4DeviceName, MM::ShutterDevice, "LCX Laser on slot 4"); + RegisterDevice(g_LCXLaser5DeviceName, MM::ShutterDevice, "LCX Laser on slot 5"); + RegisterDevice(g_LCXLaser6DeviceName, MM::ShutterDevice, "LCX Laser on slot 6"); + + RegisterDevice(g_LBXLaser1DeviceName, MM::ShutterDevice, "LBX Laser on slot 1"); + RegisterDevice(g_LBXLaser2DeviceName, MM::ShutterDevice, "LBX Laser on slot 2"); + RegisterDevice(g_LBXLaser3DeviceName, MM::ShutterDevice, "LBX Laser on slot 3"); + RegisterDevice(g_LBXLaser4DeviceName, MM::ShutterDevice, "LBX Laser on slot 4"); + RegisterDevice(g_LBXLaser5DeviceName, MM::ShutterDevice, "LBX Laser on slot 5"); + RegisterDevice(g_LBXLaser6DeviceName, MM::ShutterDevice, "LBX Laser on slot 6"); + + RegisterDevice(g_ObisLaser1DeviceName, MM::ShutterDevice, "Obis Laser on slot 1"); + RegisterDevice(g_ObisLaser2DeviceName, MM::ShutterDevice, "Obis Laser on slot 2"); + RegisterDevice(g_ObisLaser3DeviceName, MM::ShutterDevice, "Obis Laser on slot 3"); + RegisterDevice(g_ObisLaser4DeviceName, MM::ShutterDevice, "Obis Laser on slot 4"); + RegisterDevice(g_ObisLaser5DeviceName, MM::ShutterDevice, "Obis Laser on slot 5"); + RegisterDevice(g_ObisLaser6DeviceName, MM::ShutterDevice, "Obis Laser on slot 6"); + + RegisterDevice(g_OxxiusShutter1DeviceName, MM::ShutterDevice, "E-m shutter on channel 1"); + RegisterDevice(g_OxxiusShutter2DeviceName, MM::ShutterDevice, "E-m shutter on channel 2"); + RegisterDevice(g_OxxiusMDualADeviceName, MM::GenericDevice, "M-Dual on channel A"); + RegisterDevice(g_OxxiusMDualBDeviceName, MM::GenericDevice, "M-Dual on channel B"); + RegisterDevice(g_OxxiusMDualCDeviceName, MM::GenericDevice, "M-Dual on channel C"); + RegisterDevice(g_OxxiusFlipMirror1DeviceName, MM::StateDevice, "Flip-Mirror on slot 1"); + RegisterDevice(g_OxxiusFlipMirror2DeviceName, MM::StateDevice, "Flip-Mirror on slot 2"); + +} + +MODULE_API MM::Device* CreateDevice(const char* deviceNameChar) +{ + if (deviceNameChar == 0) + return 0; + + std::string deviceNameAndSlot = string(deviceNameChar); + + if (strcmp(deviceNameChar, g_OxxiusCombinerDeviceName) == 0) { + return new OxxiusCombinerHub(); + } + else if (deviceNameAndSlot.compare(0, strlen(g_LCXLaserDeviceName), g_LCXLaserDeviceName) == 0) { + return new OxxiusLCX(deviceNameChar); + } + else if (deviceNameAndSlot.compare(0, strlen(g_LBXLaserDeviceName), g_LBXLaserDeviceName) == 0) { + return new OxxiusLBX(deviceNameChar); + } + else if (deviceNameAndSlot.compare(0, strlen(g_OxxiusShutterDeviceName), g_OxxiusShutterDeviceName) == 0) { + return new OxxiusShutter(deviceNameChar); + } + else if (deviceNameAndSlot.compare(0, strlen(g_OxxiusMDualDeviceName), g_OxxiusMDualDeviceName) == 0) { + return new OxxiusMDual(deviceNameChar); + } + else if (deviceNameAndSlot.compare(0, strlen(g_OxxiusFlipMirrorDeviceName), g_OxxiusFlipMirrorDeviceName) == 0) { + return new OxxiusFlipMirror(deviceNameChar); + } + else if (deviceNameAndSlot.compare(0, strlen(g_OBISLaserDeviceName), g_OBISLaserDeviceName) == 0) { + return new CoherentObis(deviceNameChar); + } + return 0; +} + +MODULE_API void DeleteDevice(MM::Device* pDevice) +{ + delete pDevice; +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Oxxius combiner implementation +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +/////////////////////////////////////////////////////////////////////////////// + + +OxxiusCombinerHub::OxxiusCombinerHub() : initialized_(false) +{ + // Initializing private variables + serialNumber_ = ""; + installedDevices_ = 0; + serialAnswer_ = ""; + interlockClosed_ = false; + keyActivated_ = false; + maxtemperature_ = 20.0; + + InitializeDefaultErrorMessages(); + SetErrorText(ERR_COMBINER_NOT_FOUND, "Hub Device not found. The peer device is expected to be a Oxxius combiner"); + + + // Create pre-initialization properties + // ------------------------------------ + + // Communication port + CPropertyAction* pAct = new CPropertyAction(this, &OxxiusCombinerHub::OnPort); + CreateProperty(MM::g_Keyword_Port, "Undefined", MM::String, false, pAct, true); +} + +OxxiusCombinerHub::~OxxiusCombinerHub() +{ + Shutdown(); +} + +void OxxiusCombinerHub::GetName(char* name) const +{ + CDeviceUtils::CopyLimitedString(name, g_OxxiusCombinerDeviceName); +} + +bool OxxiusCombinerHub::Busy() +{ + return false; + +} + + +int OxxiusCombinerHub::Initialize() +{ + if (!initialized_) { + + // Set proprety list + // - - - - - - - - - + + // Name and description of the combiner: + RETURN_ON_MM_ERROR(CreateProperty(MM::g_Keyword_Name, g_OxxiusCombinerDeviceName, MM::String, true)); + RETURN_ON_MM_ERROR(CreateProperty(MM::g_Keyword_Description, "Oxxius L6Cc/L4Cc combiner", MM::String, true)); + + // Serial number of the combiner: + CPropertyAction* pAct = new CPropertyAction(this, &OxxiusCombinerHub::OnSerialNumber); + RETURN_ON_MM_ERROR(CreateProperty(MM::g_Keyword_HubID, serialNumber_.c_str(), MM::String, true, pAct)); + + // Interlock circuit: + pAct = new CPropertyAction(this, &OxxiusCombinerHub::OnInterlock); + RETURN_ON_MM_ERROR(CreateProperty("Interlock circuit", "", MM::String, true, pAct)); + + // Emission key: + pAct = new CPropertyAction(this, &OxxiusCombinerHub::OnEmissionKey); + RETURN_ON_MM_ERROR(CreateProperty("EmissionKey", "", MM::String, true, pAct)); + + // Temperature: + pAct = new CPropertyAction(this, &OxxiusCombinerHub::OnTemperature); + RETURN_ON_MM_ERROR(CreateProperty("MaxTemperature", "", MM::Float, true, pAct)); + + + if (!IsCallbackRegistered()) + return DEVICE_NO_CALLBACK_REGISTERED; + + // Enumerates the installed AOMs and their position + bool AOM1en = false, AOM2en = false; + unsigned int ver = 0; + + RETURN_ON_MM_ERROR(QueryCommand(this, GetCoreCallback(), NO_SLOT, "AOM1 EN", false)); + ParseforBoolean(AOM1en); + + RETURN_ON_MM_ERROR(QueryCommand(this, GetCoreCallback(), NO_SLOT, "AOM2 EN", false)); + ParseforBoolean(AOM2en); + + RETURN_ON_MM_ERROR(QueryCommand(this, GetCoreCallback(), NO_SLOT, "SV?", false)); + ParseforVersion(ver); + + // A position equal to "0" stands for an absence of modulator + if (AOM1en) { + bool adcom = false; + string command = ""; + + if (ver < 1016) { //version check + adcom = true; + command = "AOM1 PO"; + } + else { + adcom = false; + command = "AOM1 POS"; + } + RETURN_ON_MM_ERROR(QueryCommand(this, GetCoreCallback(), NO_SLOT, command.c_str(), adcom)); + ParseforInteger(AOM1pos_); + } + if (AOM2en) { + bool adcom = false; + string command = ""; + + if (ver < 1016) { //version check + adcom = true; + command = "AOM2 PO"; + } + else { + adcom = false; + command = "AOM2 POS"; + } + RETURN_ON_MM_ERROR(QueryCommand(this, GetCoreCallback(), NO_SLOT, command.c_str(), adcom)); + ParseforInteger(AOM1pos_); + } + + + //Mpa position retreive + for (unsigned int i = 1; i <= MAX_NUMBER_OF_SLOTS; i++) { + string command = "IP"; + std::stringstream ss; + ss << i; + command += ss.str(); + + RETURN_ON_MM_ERROR(QueryCommand(this, GetCoreCallback(), NO_SLOT, command.c_str(), true)); + if (serialAnswer_ != "????") { + mpa[i] = 1; + } + } + + + + RETURN_ON_MM_ERROR(UpdateStatus()); + + initialized_ = true; + + // RETURN_ON_MM_ERROR( DetectInstalledDevices() ); + } + return DEVICE_OK; +} + + + +int OxxiusCombinerHub::DetectInstalledDevices() +{ + if (initialized_) { + + // Enumerates the lasers (or devices) present on the combiner + unsigned int masque = 1; + unsigned int repartition = 0; + + //sending command ?CL + RETURN_ON_MM_ERROR(QueryCommand(this, GetCoreCallback(), NO_SLOT, "?CL", false)); + ParseforInteger(repartition); + + for (unsigned int querySlot = 1; querySlot <= MAX_NUMBER_OF_SLOTS; querySlot++) { + if ((repartition & masque) != 0) { + string answer; + // A laser source is listed, now querying for detailed information (model, etc) + + std::string detailedInfo, serialNumber; + + //send command to get devices information + RETURN_ON_MM_ERROR(QueryCommand(this, GetCoreCallback(), querySlot, "INF?", false)); + ParseforString(detailedInfo); + + if (detailedInfo != "timeout") { + std::ostringstream nameSlotModel; + + //get the model (LCX or LBX or third-party) + std::string model = detailedInfo.substr(0, 3); + + if (model == "LBX" || model == "LSX") { + nameSlotModel << g_LBXLaserDeviceName << " " << querySlot; + } + else if (model == "LCX" || model == "LPX") { + nameSlotModel << g_LCXLaserDeviceName << " " << querySlot; + } + else if (detailedInfo == "ERR-100") { + //Dans le OBIS + nameSlotModel << g_OBISLaserDeviceName << " " << querySlot; + } + + MM::Device* pDev = ::CreateDevice(nameSlotModel.str().c_str()); + if (pDev) { + AddInstalledDevice(pDev); + installedDevices_++; + } + } + } + masque <<= 1; // Left-shift the bit mask and repeat + } + + // Creating Devices for the two electro-mechanical shutters: + for (unsigned int channel = 1; channel <= 2; channel++) { + std::ostringstream nameModelChannel; + nameModelChannel << g_OxxiusShutterDeviceName << " " << channel; + + MM::Device* pDev = ::CreateDevice(nameModelChannel.str().c_str()); + if (pDev) { + AddInstalledDevice(pDev); + installedDevices_++; + } + } + + // Creating Devices for the "Flip mirror" or MDUAL modules: + /*unsigned int FM1type = 0, FM2type = 0; + RETURN_ON_MM_ERROR(QueryCommand(this, GetCoreCallback(), NO_SLOT, "FM1C", false)); + ParseforInteger(FM1type); + RETURN_ON_MM_ERROR(QueryCommand(this, GetCoreCallback(), NO_SLOT, "FM2C", false)); + ParseforInteger(FM2type);*/ + + + // MDUAL module detection + for (unsigned int j = 0; j <= 2; j++) { + std::string MDSlot; + std::ostringstream com; + com << "IP" << convertable[j]; + + RETURN_ON_MM_ERROR(QueryCommand(this, GetCoreCallback(), NO_SLOT, com.str().c_str(), true)); + ParseforString(MDSlot); + + com.str(""); + com.clear(); + com << g_OxxiusMDualDeviceName << " " << convertable[j]; + + if (MDSlot != "????") { + MM::Device* pDev = ::CreateDevice(com.str().c_str()); + if (pDev) { + AddInstalledDevice(pDev); + installedDevices_++; + } + + } + + } + + //Flip mirror module creation + + for (unsigned int j = 1; j <= 2; j++) { + unsigned int FMpresence; + std::ostringstream queryPhrase,FMName ; + queryPhrase << "FM" << j << "C"; + + RETURN_ON_MM_ERROR(QueryCommand(this, GetCoreCallback(), NO_SLOT, queryPhrase.str().c_str(), false)); + ParseforInteger(FMpresence); + + FMName << g_OxxiusFlipMirrorDeviceName << " " << j; + if ( (FMpresence == 1) || (FMpresence == 2) || (FMpresence == 3) ) { + MM::Device* pDev = ::CreateDevice(FMName.str().c_str()); + if (pDev) { + AddInstalledDevice(pDev); + installedDevices_++; + } + } + } + + } // if (initialized_) + + return DEVICE_OK; +} + + +int OxxiusCombinerHub::Shutdown() +{ + initialized_ = false; + return DEVICE_OK; +} + + +/////////////////////////////////////////////////////////////////////////////// +// Action handlers +/////////////////////////////////////////////////////////////////////////////// + +int OxxiusCombinerHub::OnPort(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::BeforeGet) + { + pProp->Set(port_.c_str()); + } + else if (eAct == MM::AfterSet) + { + pProp->Get(port_); + pProp->Set(port_.c_str()); + } + return DEVICE_OK; +} + + +int OxxiusCombinerHub::OnSerialNumber(MM::PropertyBase* pProp, MM::ActionType pAct) +{ + if (pAct == MM::BeforeGet) { + QueryCommand(this, GetCoreCallback(), NO_SLOT, "HID?", false); + ParseforString(serialNumber_); + pProp->Set(serialNumber_.c_str()); + } + + return DEVICE_OK; +} + + +int OxxiusCombinerHub::OnInterlock(MM::PropertyBase* pProp, MM::ActionType pAct) +{ + if (pAct == MM::BeforeGet) { + QueryCommand(this, GetCoreCallback(), NO_SLOT, "INT?", false); + ParseforBoolean(interlockClosed_); + + if (interlockClosed_) { + pProp->Set("Closed"); + } + else { + pProp->Set("Open"); + } + } + + return DEVICE_OK; +} + + +int OxxiusCombinerHub::OnEmissionKey(MM::PropertyBase* pProp, MM::ActionType pAct) +{ + if (pAct == MM::BeforeGet) { + QueryCommand(this, GetCoreCallback(), NO_SLOT, "KEY?", false); + ParseforBoolean(keyActivated_); + + if (keyActivated_) { + pProp->Set("Armed"); + } + else { + pProp->Set("Disarmed"); + } + } + + return DEVICE_OK; +} + + +int OxxiusCombinerHub::OnTemperature(MM::PropertyBase* pProp, MM::ActionType pAct) +{ + if (pAct == MM::BeforeGet) { + // create an array of the temperatures of all the lasers + std::vector temperature_array; + float temp_var; + + for (int i = 1; i <= MAX_NUMBER_OF_SLOTS; i++) { + + if (i != GetObPos()) { + QueryCommand(this, GetCoreCallback(), i, "?BT", false); //for LBX and LCX lasers + ParseforFloat(temp_var); + } + else { + QueryCommand(this, GetCoreCallback(), i, "SOUR:TEMP:BAS?", false); // For OBIS laser + ParseforTemperature(temp_var); + } + temperature_array.push_back(temp_var); + } + + float maxTemp = temperature_array[0]; + for (auto i : temperature_array) { + if (i > maxTemp) { + maxTemp = i; + } + } + maxtemperature_ = maxTemp; + + pProp->Set(static_cast(maxtemperature_)); + + } + return DEVICE_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +// Generic methods +/////////////////////////////////////////////////////////////////////////////// + +void OxxiusCombinerHub::LogError(int id, MM::Device* device, MM::Core* core, const char* functionName) //print log messages +{ + std::ostringstream os; + char deviceName[MM::MaxStrLength]; + device->GetName(deviceName); + os << "Error " << id << ", " << deviceName << ", " << functionName << endl; + core->LogMessage(device, os.str().c_str(), false); +} + + +/** + * Sends a serial command to a given slot, then stores the result in the receive buffer. + */ +int OxxiusCombinerHub::QueryCommand(MM::Device* device, MM::Core* core, const unsigned int destinationSlot, const char* command, bool adco) +{ + // First check: if the command string is empty, do nothing and return "DEVICE_OK" + if (strcmp(command, "") == 0) return DEVICE_OK; + + char rcvBuf_[RCV_BUF_LENGTH]; + // Compose the command to be sent to the combiner + std::string strCommand, strHZIn, strHZOut; + strCommand.assign(g_slotPrefix[destinationSlot]); + strCommand.append(command); + strHZIn.assign(g_slotPrefix[destinationSlot]); + strHZIn.append("HZ 9876"); + strHZOut.assign(g_slotPrefix[destinationSlot]); + strHZOut.append("HZ 0"); + + /* + std::ostringstream InfoMessage; + InfoMessage << "Now sending command :"; + InfoMessage << string(strCommand.c_str()); + LogError(DEVICE_OK, device, core, InfoMessage.str().c_str()); + */ + std::ostringstream InfoMessage2; + InfoMessage2 << "Send: " << command << " Received: "; + + // Preambule for specific commands + if (adco) { + int ret = core->SetSerialCommand(device, port_.c_str(), strHZIn.c_str(), "\r\n"); + ret = core->GetSerialAnswer(device, port_.c_str(), RCV_BUF_LENGTH, rcvBuf_, "\r\n"); + if (ret != DEVICE_OK) { + LogError(ret, device, core, "QueryCommand-SetSerialCommand - preambule"); + return ret; + } + } + + // Send command through the serial interface + int ret = core->SetSerialCommand(device, port_.c_str(), strCommand.c_str(), "\r\n"); + if (ret != DEVICE_OK) { + LogError(ret, device, core, "QueryCommand-SetSerialCommand"); + return ret; + } + + // Get a response + ret = core->GetSerialAnswer(device, port_.c_str(), RCV_BUF_LENGTH, rcvBuf_, "\r\n"); + + InfoMessage2 << rcvBuf_; + /* DEBUG ONLY */ + // LogError(DEVICE_OK, device, core, InfoMessage2.str().c_str()); + + if (ret != DEVICE_OK) { + LogError(ret, device, core, "QueryCommand-GetSerialAnswer"); + + // Keep on trying until we either get our answer, or 3 seconds have passed + int maxTimeMs = 3000; + // Wait for a (increasing) delay between each try + int delayMs = 10; + // Keep track of how often we tried + int counter = 1; + bool done = false; + MM::MMTime startTime(GetCurrentMMTime()); + + while (!done) { + counter++; + ret = core->GetSerialAnswer(device, port_.c_str(), RCV_BUF_LENGTH, rcvBuf_, "\r\n"); + if ((ret == DEVICE_OK) || ((GetCurrentMMTime() - startTime) > MM::MMTime::fromMs(maxTimeMs))) + done = true; + else { + CDeviceUtils::SleepMs(delayMs); + delayMs *= 2; + } + } + ostringstream os; + if (ret == DEVICE_OK) + os << "QueryCommand-GetSerialAnswer: Succeeded reading from serial port after trying " << counter << "times."; + else + os << "QueryCommand-GetSerialAnswer: Failed reading from serial port after trying " << counter << "times."; + + core->LogMessage(device, os.str().c_str(), true); + + serialAnswer_.assign(rcvBuf_); + return ret; + } + serialAnswer_.assign(rcvBuf_); + ret = core->PurgeSerial(device, port_.c_str()); + + /* if( strcmp(serialAnswer_, "timeout") == 0) { + std::ostringstream syntaxErrorMessage; + syntaxErrorMessage << "Time out received against sent command '"; + syntaxErrorMessage << string(strCommand.c_str()); + syntaxErrorMessage << "'"; + + LogError(DEVICE_SERIAL_TIMEOUT, device, core, syntaxErrorMessage.str().c_str()); + return DEVICE_SERIAL_TIMEOUT; + } + */ + // Epilogue for specific commands + if (adco) { + int retEpi = core->SetSerialCommand(device, port_.c_str(), strHZOut.c_str(), "\r\n"); + retEpi = core->GetSerialAnswer(device, port_.c_str(), RCV_BUF_LENGTH, rcvBuf_, "\r\n"); + if (retEpi != DEVICE_OK) { + LogError(retEpi, device, core, "QueryCommand-SetSerialCommand - Epilogue"); + return retEpi; + } + } + + return DEVICE_OK; +} + + +int OxxiusCombinerHub::ParseforBoolean(bool& Bval) +{ + unsigned int intAnswer = (unsigned int)atoi(serialAnswer_.c_str()); + Bval = (intAnswer == 1); + + serialAnswer_.clear(); + + return DEVICE_OK; +} + + +int OxxiusCombinerHub::ParseforFloat(float& Dval) +{ + Dval = (float)atof(serialAnswer_.c_str()); + + serialAnswer_.clear(); + + return DEVICE_OK; +} + +int OxxiusCombinerHub::ParseforDouble(double& Dval) +{ + Dval = (double)atof(serialAnswer_.c_str()); + + serialAnswer_.clear(); + + return DEVICE_OK; +} + + + +int OxxiusCombinerHub::ParseforInteger(unsigned int& Ival) +{ + Ival = (unsigned int)atoi(serialAnswer_.c_str()); + + serialAnswer_.clear(); + + return DEVICE_OK; +} + + +int OxxiusCombinerHub::ParseforString(std::string& Sval) +{ + Sval.assign(serialAnswer_); + + serialAnswer_.clear(); + + return DEVICE_OK; +} + + +int OxxiusCombinerHub::ParseforVersion(unsigned int& Vval) //cast the string into a comparable int +{ + std::string temp1; + std::string temp2(serialAnswer_); + + for (unsigned int i = 0; i <= (temp2.length()) - 1; i++) { + if (temp2.at(i) != '.') { + temp1 += temp2.at(i); + } + } + + stringstream s(temp1); + s >> Vval; + serialAnswer_.clear(); + + return DEVICE_OK; +} + + +int OxxiusCombinerHub::ParseforPercent(double& Pval) //cast the string into a comparable int +{ + std::string percentage; + std::size_t found; + + percentage.assign(serialAnswer_); + + found = percentage.find("%"); + if (found != std::string::npos) { + Pval = atof(percentage.substr(0, found).c_str()); + } + + return DEVICE_OK; +} + + +int OxxiusCombinerHub::ParseforTemperature(float& Tval) // cast a Celsius temperature string into a comparable float +{ + std::string temperature; + std::size_t found; + + temperature.assign(serialAnswer_); + + found = temperature.find("C"); + if (found != std::string::npos) { + Tval = atof(temperature.substr(0, found).c_str()); + } + + return DEVICE_OK; +} + +int OxxiusCombinerHub::ParseforChar(char* Nval) +{ + strcpy(Nval, serialAnswer_.c_str()); + serialAnswer_.clear(); + + return DEVICE_OK; +} + +bool OxxiusCombinerHub::GetAOMpos1(unsigned int slot) +{ + bool res = false; + + if (slot == AOM1pos_) { + res = true; + } + + return res; +} + +bool OxxiusCombinerHub::GetAOMpos2(unsigned int slot) +{ + bool res = false; + + if (slot == AOM2pos_) { + res = true; + } + + return res; +} + +bool OxxiusCombinerHub::GetMPA(unsigned int slot) { + bool res = false; + + if (mpa[slot] == 1) { + res = true; + } + + return res; +} + +int OxxiusCombinerHub::GetObPos() { + return obPos_; +} diff --git a/DeviceAdapters/OxxiusCombiner/OxxiusCombinerHub.h b/DeviceAdapters/OxxiusCombiner/OxxiusCombinerHub.h new file mode 100644 index 000000000..117001914 --- /dev/null +++ b/DeviceAdapters/OxxiusCombiner/OxxiusCombinerHub.h @@ -0,0 +1,140 @@ +#pragma once +#include "DeviceBase.h" +#include +#include +#include +#include +#include +#include +#include +#include // For system time +#include // For formatting time +#include // For string streams +using namespace std; + +//For Obis +#ifdef WIN32 + #include +#endif +#include "DeviceUtils.h" +#include +#include +// + +// +class OxxiusLBX; // advance declaration +class OxxiusLCX; // advance declaration +class CoherentObis; //advance declaration +class Cobolt08_01; //advance declaration +class OxxiusShutter; // advance declaration +class OxxiusMDual; // advance declaration +class OxxiusFlipMirror; // advance declaration + +// +#define MAX_NUMBER_OF_SLOTS 6 +#define RCV_BUF_LENGTH 256 +#define NO_SLOT 0 + +////////////////////////////////////////////////////////////////////////////// +// Error codes for LBX and LCX +// +#define ERR_PORT_CHANGE_FORBIDDEN 101 +#define ERR_NO_PORT_SET 102 +#define ERR_COMBINER_NOT_FOUND 201 +#define ERR_UNSUPPORTED_VERSION 202 + +////////////////////////////////////////////////////////////////////////////// +// Error codes for Obis +// +#define OBIS_ERR_PORT_CHANGE_FORBIDDEN 10004 +#define OBIS_ERR_DEVICE_NOT_FOUND 10005 + +#define OBIS_POWERCONVERSION 1000 //convert the power into mW from the W it wants the commands in + +////////////////////////////////////////////////////////////////////////////// +// Miscellaneous definitions +// +// Use the name 'return_value' that is unlikely to appear within 'result'. +#define RETURN_ON_MM_ERROR( result ) do { \ + int return_value = (result); \ + if (return_value != DEVICE_OK) { \ + return return_value; \ + } \ +} while (0) + + + + +////////////////////////////////////////////////////////////////////////////// +// Defining device adaptaters +// + + +class OxxiusCombinerHub : public HubBase +{ +public: + OxxiusCombinerHub(); + ~OxxiusCombinerHub(); + + // MMDevice API + // ------------ + int Initialize(); + int Shutdown(); + + void GetName(char* pszName) const; + bool Busy(); + int DetectInstalledDevices(); + unsigned int GetNumberOfInstalledDevices() { return installedDevices_; }; + // MM::DeviceDetectionStatus DetectDevice(void); + bool SupportsDeviceDetection(void) { return true; }; + + + // Property handlers + int OnPort(MM::PropertyBase* pPropt, MM::ActionType eAct); + int OnSerialNumber(MM::PropertyBase* pPropt, MM::ActionType eAct); + int OnInterlock(MM::PropertyBase* pPropt, MM::ActionType eAct); + int OnEmissionKey(MM::PropertyBase* pPropt, MM::ActionType eAct); + int OnTemperature(MM::PropertyBase* pPropt, MM::ActionType eAct); + + // Custom interface for child devices +// bool IsPortAvailable() {return portAvailable_;} + int QueryCommand(MM::Device* device, MM::Core* core, const unsigned int destinationSlot, const char* command, bool adco); + int ParseforBoolean(bool& destBoolean); + int ParseforFloat(float& destFloat); + int ParseforInteger(unsigned int& destInteger); + int ParseforString(string& destString); + int ParseforVersion(unsigned int& Vval); + int ParseforPercent(double& Pval); + int ParseforTemperature(float& Tval); + int ParseforChar(char* Nval); + + int ParseforDouble(double& destDouble); + // int TempAdminInt(const char* com); + // int TempAdminString(int com, string &res); + + bool GetAOMpos1(unsigned int slot); + bool GetAOMpos2(unsigned int slot); + bool GetMPA(unsigned int slot); + int GetObPos(); + +private: + void LogError(int id, MM::Device* device, MM::Core* core, const char* functionName); + + string port_; + bool initialized_; + unsigned int installedDevices_; + + string type_; + float maxtemperature_; + + string serialAnswer_; + + string serialNumber_; + bool interlockClosed_; + bool keyActivated_; + + unsigned int AOM1pos_; + unsigned int AOM2pos_; + unsigned int mpa[7]; + unsigned int obPos_; +}; diff --git a/DeviceAdapters/OxxiusCombiner/OxxiusFlipMirror.cpp b/DeviceAdapters/OxxiusCombiner/OxxiusFlipMirror.cpp new file mode 100644 index 000000000..1cd2a3345 --- /dev/null +++ b/DeviceAdapters/OxxiusCombiner/OxxiusFlipMirror.cpp @@ -0,0 +1,127 @@ +#include "OxxiusFlipMirror.h" + +#include +#include +#include +#include +using namespace std; + + +/////////////////////////////////////////////////////////////////////////////// +// +// Oxxius Flip-Mirror implementation +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +/////////////////////////////////////////////////////////////////////////////// + +OxxiusFlipMirror::OxxiusFlipMirror(const char* nameAndSlot) : initialized_(false) +{ + parentHub_ = 0; + core_ = GetCoreCallback(); + + std::string fSlot = string(nameAndSlot); + nameF_.assign(fSlot);// set laser name + fSlot = fSlot.substr(fSlot.length() - 1, 1); + slot_ = (unsigned int)atoi(fSlot.c_str());// set laser slot + + numPos_ = 0; + + // Set property list ///////////////////////////////////////////////////////////////////////////////////////////////////// NOT WORKING? (duplicate property name Name(4)) + // ----------------- + // Name (read only) + /*CreateProperty(MM::g_Keyword_Name, nameF_.c_str(), MM::String, true); + + CreateProperty(MM::g_Keyword_Description, "Flip-Mirror module", MM::String, true); + + InitializeDefaultErrorMessages(); + SetErrorText(ERR_NO_PORT_SET, "Hub Device not found. The Laser combiner is needed to create this device"); + + // parent ID display + CreateHubIDProperty();*/ +} + + +OxxiusFlipMirror::~OxxiusFlipMirror() +{ + Shutdown(); +} + + +int OxxiusFlipMirror::Initialize() +{ + if (!initialized_) { + parentHub_ = static_cast(GetParentHub()); + if (!parentHub_) { + return DEVICE_COMM_HUB_MISSING; + } + char hubLabel[MM::MaxStrLength]; + parentHub_->GetLabel(hubLabel); + SetParentID(hubLabel); // for backward compatibility + + CPropertyAction* pAct = new CPropertyAction(this, &OxxiusFlipMirror::OnSwitchPos);//setting the possible positions + RETURN_ON_MM_ERROR(CreateProperty("Switch Position", "0", MM::Integer, false, pAct)); + SetPropertyLimits("Switch Position", 0, 1); + + std::ostringstream descriPt2; + descriPt2 << ""; + RETURN_ON_MM_ERROR(CreateProperty(MM::g_Keyword_Description, descriPt2.str().c_str(), MM::String, true)); + + // Gate, or "closed" position +// RETURN_ON_MM_ERROR( CreateProperty(MM::g_Keyword_Closed_Position, "0", MM::String, false) ); + +// isOpen_ = false; // MDual closed posisiton is + + RETURN_ON_MM_ERROR(UpdateStatus()); + + initialized_ = true; + } + + return DEVICE_OK; +} + + +int OxxiusFlipMirror::Shutdown() +{ + initialized_ = false; + return DEVICE_OK; +} + + +void OxxiusFlipMirror::GetName(char* Name) const +{ + CDeviceUtils::CopyLimitedString(Name, nameF_.c_str()); +} + + +bool OxxiusFlipMirror::Busy() +{ + return false; +} + + +int OxxiusFlipMirror::OnSwitchPos(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::BeforeGet) { + unsigned int currentPos = 0; + std::ostringstream command; + command << "FM" << slot_; + + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), NO_SLOT, command.str().c_str(), false)); + parentHub_->ParseforInteger(currentPos); + + //SetPosition(currentPos); + pProp->Set((long)currentPos); + } + else if (eAct == MM::AfterSet) { + long newPosition = 0; + + //GetPosition(newPosition); + pProp->Get(newPosition); + + std::ostringstream newCommand; + newCommand << "FM" << slot_ << " " << newPosition; + + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), NO_SLOT, newCommand.str().c_str(), false)); + } + return DEVICE_OK; +} \ No newline at end of file diff --git a/DeviceAdapters/OxxiusCombiner/OxxiusFlipMirror.h b/DeviceAdapters/OxxiusCombiner/OxxiusFlipMirror.h new file mode 100644 index 000000000..80d781372 --- /dev/null +++ b/DeviceAdapters/OxxiusCombiner/OxxiusFlipMirror.h @@ -0,0 +1,39 @@ +#pragma once + +#include "OxxiusCombinerHub.h" + +#include "DeviceBase.h" +#include +#include +#include + +class OxxiusFlipMirror : public CStateDeviceBase +{ +public: + OxxiusFlipMirror(const char* name); + ~OxxiusFlipMirror(); + + // MMDevice API + // ------------ + int Initialize(); + int Shutdown(); + + void GetName(char* pszName) const; + bool Busy(); + unsigned long GetNumberOfPositions() const { return 2; }; + + // Action Interface + // ---------------- + int OnSwitchPos(MM::PropertyBase* pProp, MM::ActionType eAct); + + +private: + bool initialized_; + std::string nameF_; + unsigned int slot_; + MM::Core* core_; + + OxxiusCombinerHub* parentHub_; + + unsigned long numPos_; +}; diff --git a/DeviceAdapters/OxxiusCombiner/OxxiusLBX.cpp b/DeviceAdapters/OxxiusCombiner/OxxiusLBX.cpp new file mode 100644 index 000000000..114d90fe7 --- /dev/null +++ b/DeviceAdapters/OxxiusCombiner/OxxiusLBX.cpp @@ -0,0 +1,545 @@ +#include "OxxiusLBX.h" +using namespace std; + +OxxiusLBX::OxxiusLBX(const char* nameAndSlot) : initialized_(false) +{ + string tSlot = string(nameAndSlot); + + name_.assign(tSlot);// set laser name + tSlot = tSlot.substr(tSlot.length() - 1, 1); + slot_ = (unsigned int)atoi(tSlot.c_str());// set laser slot + + parentHub_; + busy_ = false; + laserOn_ = false; + alarm_ = ""; + state_ = ""; + digitalMod_ = ""; + analogMod_ = ""; + controlMode_ = ""; + + //MPA + mpa_number = -1; //probably not needed + + // powerSetPoint_ = 0.0; + maxRelPower_ = 100.0; + nominalPower_ = 100.0; + maxCurrent_ = 125.0; + + InitializeDefaultErrorMessages(); + SetErrorText(ERR_NO_PORT_SET, "Hub Device not found. The Laser combiner is needed to create this device"); + + // parent ID display + CreateHubIDProperty(); +} + + +OxxiusLBX::~OxxiusLBX() +{ + Shutdown(); +} + + + +int OxxiusLBX::Initialize() +{ + if (!initialized_) { + + size_t found; + string strSlot; + string spa; + + + parentHub_ = static_cast(GetParentHub()); + if (!parentHub_) { + return DEVICE_COMM_HUB_MISSING; + } + char hubLabel[MM::MaxStrLength]; + parentHub_->GetLabel(hubLabel); + SetParentID(hubLabel); // for backward compatibility + + // Set property list + // ----------------- + // Name (read only) + RETURN_ON_MM_ERROR(CreateProperty(MM::g_Keyword_Name, name_.c_str(), MM::String, true)); + + // Description (read only) + ostringstream descriPt1; + char sourceSerialNumber[] = "LAS-XXXXXXXXXX"; + parentHub_->QueryCommand(this, GetCoreCallback(), slot_, "HID?", false); + parentHub_->ParseforChar(sourceSerialNumber); + + parentHub_->QueryCommand(this, GetCoreCallback(), slot_, "IP", true); + parentHub_->ParseforString(spa); + + parentHub_->QueryCommand(this, GetCoreCallback(), slot_, "INF?", false); + parentHub_->ParseforString(strSlot); + + // Retrieves and delete the laser's type + found = strSlot.find("-"); + if (found != string::npos) { + strSlot.erase(0, found + 1); + } + + // Retrieves and define the laser's wavelength + found = strSlot.find("-"); + if (found != string::npos) { + waveLength = (unsigned int)atoi(strSlot.substr(0, found).c_str()); + } + + // Retrieves and define the nominal power + strSlot.erase(0, found + 1); + nominalPower_ = (float)atof(strSlot.substr(0, found).c_str()); + + + if (parentHub_->GetMPA(slot_)) { + mpa_number = slot_; + } + else { + mpa_number = -1; + } + + + // LBX model + descriPt1 << "LBX"; + descriPt1 << " source on slot " << slot_; + descriPt1 << ", " << sourceSerialNumber; + + RETURN_ON_MM_ERROR(CreateProperty(MM::g_Keyword_Description, descriPt1.str().c_str(), MM::String, true)); + + // Alarm (read only) + CPropertyAction* pAct = new CPropertyAction(this, &GenericLaser::OnAlarm); + RETURN_ON_MM_ERROR(CreateProperty("Alarm", "None", MM::String, true, pAct)); + + // Status (read only) + pAct = new CPropertyAction(this, &GenericLaser::OnState); + RETURN_ON_MM_ERROR(CreateProperty("State", "", MM::String, true, pAct)); + + // Emission selector (write/read) + pAct = new CPropertyAction(this, &GenericLaser::OnEmissionOnOff); + RETURN_ON_MM_ERROR(CreateProperty("Emission", "", MM::String, false, pAct)); + AddAllowedValue("Emission", "ON"); + AddAllowedValue("Emission", "OFF"); + + // Digital modulation selector (write/read) + pAct = new CPropertyAction(this, &GenericLaser::OnDigitalMod); + RETURN_ON_MM_ERROR(CreateProperty("Digital Modulation", "", MM::String, false, pAct)); + AddAllowedValue("Digital Modulation", "ON"); + AddAllowedValue("Digital Modulation", "OFF"); + + // Analog modulation selector (write/read) + pAct = new CPropertyAction(this, &GenericLaser::OnAnalogMod); + RETURN_ON_MM_ERROR(CreateProperty("Analog Modulation", "", MM::String, false, pAct)); + AddAllowedValue("Analog Modulation", "ON"); + AddAllowedValue("Analog Modulation", "OFF"); + + // Control mode selector (= APC or ACC) (write/read) + pAct = new CPropertyAction(this, &GenericLaser::OnControlMode); + RETURN_ON_MM_ERROR(CreateProperty("Control mode", "", MM::String, false, pAct)); + AddAllowedValue("Control mode", "ACC"); + AddAllowedValue("Control mode", "APC"); + + //Fire property + pAct = new CPropertyAction(this, &GenericLaser::OnFire); + RETURN_ON_MM_ERROR(CreateProperty("Fire", "0", MM::Float, false, pAct)); + + // Define the maximal current and power + maxRelPower_ = 100.0; + maxCurrent_ = 105.0; + + // Power set point (write/read) + pAct = new CPropertyAction(this, &GenericLaser::OnPowerSetPoint); + RETURN_ON_MM_ERROR(CreateProperty("Power set point", "0", MM::Float, false, pAct)); + SetPropertyLimits("Power set point", 0, maxRelPower_); + + // Current set point (write/read) + pAct = new CPropertyAction(this, &GenericLaser::OnCurrentSetPoint); + RETURN_ON_MM_ERROR(CreateProperty("Current set point", "0", MM::Float, false, pAct)); + SetPropertyLimits("Current set point", 0, maxCurrent_); + + RETURN_ON_MM_ERROR(UpdateStatus()); + + initialized_ = true; + } + + return DEVICE_OK; +} + + +/* +int OxxiusLBX::Shutdown() +{ + initialized_ = false; + return DEVICE_OK; +} +*/ + + +int OxxiusLBX::Fire(double deltaT) +{ + string activate_query = "DL 1"; + string deactivate_query = "DL 0"; + + if (laserOn_ == false) { + laserOn_ = true; + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), slot_, activate_query.c_str(), false)); + } + CDeviceUtils::SleepMs((long)(deltaT)); + laserOn_ = false; + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), slot_, deactivate_query.c_str(), false)); + //At the end, the laser is set to off + + return DEVICE_OK; +} + +//RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), slot_, newCommand.c_str(), false)); + +/////////////////////////////////////////////////////////////////////////////// +// Action handlers +/////////////////////////////////////////////////////////////////////////////// + +int OxxiusLBX::OnAlarm(MM::PropertyBase* pProp, MM::ActionType) +{ + unsigned int alarmInt = 99; + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), slot_, "?F", false)); + + parentHub_->ParseforInteger(alarmInt); + + switch (alarmInt) { + case 0: + alarm_ = "No Alarm"; + break; + case 1: + alarm_ = "Out-of-bounds diode current"; + break; + case 2: + alarm_ = "Unexpected laser power value"; + break; + case 3: + alarm_ = "Out-of-bounds supply voltage"; + break; + case 4: + alarm_ = "Out-of-bounds internal temperature"; + break; + case 5: + alarm_ = "Out-of-bounds baseplate temperature"; + break; + case 7: + alarm_ = "Interlock circuit open"; + break; + case 8: + alarm_ = "Soft reset"; + break; + default: + alarm_ = "Other alarm"; + } + + pProp->Set(alarm_.c_str()); + + return DEVICE_OK; +} + + +int OxxiusLBX::OnState(MM::PropertyBase* pProp, MM::ActionType) +{ + unsigned int stateInt = 99; + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), slot_, "?STA", false)); + + parentHub_->ParseforInteger(stateInt); + + switch (stateInt) { + case 1: + state_ = "Warm-up phase"; + break; + case 2: + state_ = "Stand-by state"; + break; + case 3: + state_ = "Emission on"; + break; + case 4: + state_ = "Internal error"; + break; + case 5: + state_ = "Alarm"; + break; + case 6: + state_ = "Sleep state"; + break; + default: + state_ = "Other state"; + } + + pProp->Set(alarm_.c_str()); + + return DEVICE_OK; +} + + +int OxxiusLBX::OnEmissionOnOff(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::BeforeGet) { + unsigned int status = 0; + ostringstream query; + + query << "?CS " << slot_; + + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), NO_SLOT, query.str().c_str(), false)); + parentHub_->ParseforInteger(status); + + switch (status) { + case 0: // LBX model: Emission off + laserOn_ = false; + break; + case 1: // LBX model: Emission on + laserOn_ = true; + break; + default: + laserOn_ = true; + } + + if (laserOn_) { + pProp->Set("ON"); + } + else { + pProp->Set("OFF"); + } + } + else if (eAct == MM::AfterSet) { + string newEmissionStatus, newCommand = ""; + + pProp->Get(newEmissionStatus); + + if (newEmissionStatus.compare("ON") == 0) { + newCommand = "DL 1"; + laserOn_ = true; + } + else if (newEmissionStatus.compare("OFF") == 0) { + newCommand = "DL 0"; + laserOn_ = false; + } + + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), slot_, newCommand.c_str(), false)); + } + return DEVICE_OK; +} + + +int OxxiusLBX::OnDigitalMod(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + unsigned int querySlot = NO_SLOT; + + if (eAct == MM::BeforeGet) { + string query; + + querySlot = slot_; + query = "?TTL"; + + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), querySlot, query.c_str(), false)); + + bool digiM; + parentHub_->ParseforBoolean(digiM); + + if (digiM) + digitalMod_.assign("ON"); + else + digitalMod_.assign("OFF"); + + pProp->Set(digitalMod_.c_str()); + + } + else if (eAct == MM::AfterSet) { + string newModSet, newCommand; + + pProp->Get(newModSet); + digitalMod_.assign(newModSet); + + if (digitalMod_ == "ON") { + querySlot = slot_; + newCommand.assign("TTL 1"); + } + + else if (digitalMod_ == "OFF") { + querySlot = slot_; + newCommand.assign("TTL 0"); + } + + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), querySlot, newCommand.c_str(), false)); + } + return DEVICE_OK; +} + + +int OxxiusLBX::OnAnalogMod(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::BeforeGet) { + string query; + query = "?AM"; + + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), slot_, query.c_str(), false)); + bool digiM; + parentHub_->ParseforBoolean(digiM); + + if (digiM) { + digitalMod_.assign("ON"); + } + else { + digitalMod_.assign("OFF"); + } + pProp->Set(digitalMod_.c_str()); + + } + else if (eAct == MM::AfterSet) { + ostringstream newCommand; + newCommand << "AM "; + + pProp->Get(digitalMod_); + + if (digitalMod_ == "OFF") + newCommand << "0"; + else + newCommand << "1"; + + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), slot_, newCommand.str().c_str(), false)); + } + return DEVICE_OK; +} + + +int OxxiusLBX::OnControlMode(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::BeforeGet) { + unsigned int ctrlM = 1; + string command; + + command = "?APC"; + + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), slot_, command.c_str(), false)); + parentHub_->ParseforInteger(ctrlM); + + if (ctrlM == 1) { + controlMode_.assign("APC"); + } + else if (ctrlM == 0) { + controlMode_.assign("ACC"); + } + + pProp->Set(controlMode_.c_str()); + } + else if (eAct == MM::AfterSet) { + string newCommand; + + pProp->Get(controlMode_); + + if (controlMode_ == "ACC") { + newCommand.assign("APC 0"); + } + else if (controlMode_ == "APC") { + newCommand.assign("APC 1"); + } + + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), slot_, newCommand.c_str(), false)); + } + return DEVICE_OK; +} + +/* +int OxxiusLBX::OnPowerSetPoint(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::BeforeGet) { + string command = "?SP"; + unsigned int thisSlot = slot_; + float absSetPoint_; + + + if ((0 < mpa_number) && (mpa_number < 7)) { + thisSlot = NO_SLOT; + command = "?PL"; + stringstream s; + s << mpa_number; + command += s.str(); + } + + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), thisSlot, command.c_str(), false)); + parentHub_->ParseforFloat(absSetPoint_ ); + + pProp->Set(static_cast( absSetPoint_ )); + } + else if (eAct == MM::AfterSet) { + + double GUISetPoint = 0.0; + pProp->Get(GUISetPoint); + + if ((GUISetPoint >= 0.0) || (GUISetPoint <= nominalPower_)) { + string command = "P"; + unsigned int thisSlot = slot_; + + ostringstream newCommand; + char* powerSPString = new char[20]; + strcpy(powerSPString, CDeviceUtils::ConvertToString( static_cast(GUISetPoint) ) ); + + if ((0 < mpa_number) && (mpa_number < 7)) { + thisSlot = NO_SLOT; + command = "IP"; + command += CDeviceUtils::ConvertToString((int)(mpa_number)); + + strcpy(powerSPString, CDeviceUtils::ConvertToString(GUISetPoint)); + } + + newCommand << command << " " << powerSPString; + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), thisSlot, newCommand.str().c_str(), false)); + } + else { + // If the value entered through the GUI is not valid, read the machine value + OnPowerSetPoint(pProp, MM::BeforeGet); + } + } + return DEVICE_OK; +} +*/ + + +int OxxiusLBX::OnCurrentSetPoint(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::BeforeGet) { + float machineSetPoint = 0.0; + string command = "?SC"; + + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), slot_, command.c_str(), false)); + parentHub_->ParseforFloat(machineSetPoint); + + pProp->Set(machineSetPoint); + } + + else if (eAct == MM::AfterSet) { + + double GUISetPoint = 0.0; + pProp->Get(GUISetPoint); + + if ((GUISetPoint >= 0.0) || (GUISetPoint <= maxCurrent_)) { + + ostringstream newCommand; + string command = "C"; + + char* currentSPString = new char[20]; + strcpy(currentSPString, CDeviceUtils::ConvertToString(GUISetPoint)); + + newCommand << command << " " << currentSPString; + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), slot_, newCommand.str().c_str(), false)); + + } + } + return DEVICE_OK; +} + +/* +int OxxiusLBX::OnFire(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::AfterSet) { + double input; + pProp->Get(input); + return Fire(input); + } + return DEVICE_OK; +} +*/ \ No newline at end of file diff --git a/DeviceAdapters/OxxiusCombiner/OxxiusLBX.h b/DeviceAdapters/OxxiusCombiner/OxxiusLBX.h new file mode 100644 index 000000000..e5e3034d3 --- /dev/null +++ b/DeviceAdapters/OxxiusCombiner/OxxiusLBX.h @@ -0,0 +1,46 @@ +#pragma once +#include "GenericLaser.h" +using namespace std; + +class OxxiusLBX : public GenericLaser +{ +public: + OxxiusLBX(const char* nameAndSlot); + ~OxxiusLBX(); + + int Initialize() ; + // int Shutdown() ; + + int Fire(double deltaT) ; + + // int OnPowerSetPoint(MM::PropertyBase* pProp, MM::ActionType eAct) ; + int OnCurrentSetPoint(MM::PropertyBase* pProp, MM::ActionType eAct) ; + int OnEmissionOnOff(MM::PropertyBase* pProp, MM::ActionType eAct) ; + int OnControlMode(MM::PropertyBase* pProp, MM::ActionType eAct) ; + int OnAnalogMod(MM::PropertyBase* pProp, MM::ActionType eAct) ; + int OnDigitalMod(MM::PropertyBase* pProp, MM::ActionType eAct) ; + int OnState(MM::PropertyBase* pProp, MM::ActionType eAct) ; + int OnAlarm(MM::PropertyBase* pProp, MM::ActionType eAct) ; + // int OnFire(MM::PropertyBase* pProp, MM::ActionType eAct) ; + + int OnPowerReadback(MM::PropertyBase* , MM::ActionType ) { return DEVICE_OK; }; + int OnCurrentReadback(MM::PropertyBase* , MM::ActionType ) { return DEVICE_OK; }; + int OnOperatingMode(MM::PropertyBase* , MM::ActionType ) { return DEVICE_OK; }; + + +private: + bool initialized_; + + //// SPECIFIC TO LBX + float maxRelPower_; + float nominalPower_; + float maxCurrent_; + unsigned int waveLength; + string state_; + string alarm_; + string controlMode_; + string analogMod_; + string digitalMod_; + + int mpa_number; +}; diff --git a/DeviceAdapters/OxxiusCombiner/OxxiusLCX.cpp b/DeviceAdapters/OxxiusCombiner/OxxiusLCX.cpp new file mode 100644 index 000000000..0c2e941bc --- /dev/null +++ b/DeviceAdapters/OxxiusCombiner/OxxiusLCX.cpp @@ -0,0 +1,575 @@ +#include "OxxiusLCX.h" +using namespace std; + +OxxiusLCX::OxxiusLCX(const char* nameAndSlot) +{ + initialized_ = false; + + string tSlot = string(nameAndSlot); + + name_.assign(tSlot);// set laser name + tSlot = tSlot.substr(tSlot.length() - 1, 1); + slot_ = (unsigned int)atoi(tSlot.c_str());// set laser slot + + parentHub_; + busy_ = false; + laserOn_ = false; + alarm_ = ""; + state_ = ""; + digitalMod_ = ""; + analogMod_ = ""; + controlMode_ = ""; + + //Maybe useless + link_AOM1 = false; + link_AOM2 = false; + pow_adj = false; + mpa_number = -1; + + // powerSetPoint_ = 0.0; + maxRelPower_ = 0.0; + nominalPower_ = 0.0; + maxCurrent_ = 125.0; + + InitializeDefaultErrorMessages(); + SetErrorText(ERR_NO_PORT_SET, "Hub Device not found. The Laser combiner is needed to create this device"); + + // parent ID display + CreateHubIDProperty(); +} + + +OxxiusLCX::~OxxiusLCX() +{ + Shutdown(); +} + + + +int OxxiusLCX::Initialize() +{ + if (!initialized_) { + + size_t found; + string strSlot; + string spa; + + + parentHub_ = static_cast(GetParentHub()); + if (!parentHub_) { + return DEVICE_COMM_HUB_MISSING; + } + char hubLabel[MM::MaxStrLength]; + parentHub_->GetLabel(hubLabel); + SetParentID(hubLabel); // for backward compatibility + + // Set property list + // ----------------- + // Name (read only) + RETURN_ON_MM_ERROR(CreateProperty(MM::g_Keyword_Name, name_.c_str(), MM::String, true)); + + // Description (read only) + ostringstream descriPt1; + char sourceSerialNumber[] = "LAS-XXXXXXXXXX"; + parentHub_->QueryCommand(this, GetCoreCallback(), slot_, "HID?", false); + parentHub_->ParseforChar(sourceSerialNumber); + + parentHub_->QueryCommand(this, GetCoreCallback(), slot_, "IP", true); + parentHub_->ParseforString(spa); + + parentHub_->QueryCommand(this, GetCoreCallback(), slot_, "INF?", false); + parentHub_->ParseforString(strSlot); + + // Retrieves and delete the laser's type + found = strSlot.find("-"); + if (found != string::npos) { + strSlot.erase(0, found + 1); + } + + // Retrieves and define the laser's wavelength + found = strSlot.find("-"); + if (found != string::npos) { + waveLength = (unsigned int)atoi(strSlot.substr(0, found).c_str()); + } + + // Retrieves and define the nominal power + strSlot.erase(0, found + 1); + nominalPower_ = (float)atof(strSlot.substr(0, found).c_str()); + + + + if (parentHub_->GetMPA(slot_)) { + mpa_number = slot_; + } + else { + mpa_number = -1; + } + + + if (parentHub_->GetAOMpos1(slot_)) { //laser has AMO1 + link_AOM1 = true; + } + if (parentHub_->GetAOMpos2(slot_)) { //laser has AMO2 + link_AOM2 = true; + } + else if (spa != "????") { //self modulating lcx + pow_adj = true; + } + + descriPt1 << "LCX"; + + descriPt1 << " source on slot " << slot_; + descriPt1 << ", " << sourceSerialNumber; + + RETURN_ON_MM_ERROR(CreateProperty(MM::g_Keyword_Description, descriPt1.str().c_str(), MM::String, true)); + + // Alarm (read only) + CPropertyAction* pAct = new CPropertyAction(this, &GenericLaser::OnAlarm); + RETURN_ON_MM_ERROR(CreateProperty("Alarm", "None", MM::String, true, pAct)); + + // Status (read only) + pAct = new CPropertyAction(this, &GenericLaser::OnState); + RETURN_ON_MM_ERROR(CreateProperty("State", "", MM::String, true, pAct)); + + // Emission selector (write/read) + pAct = new CPropertyAction(this, &GenericLaser::OnEmissionOnOff); + RETURN_ON_MM_ERROR(CreateProperty("Emission", "", MM::String, false, pAct)); + AddAllowedValue("Emission", "ON"); + AddAllowedValue("Emission", "OFF"); + + // Digital modulation selector (write/read) + pAct = new CPropertyAction(this, &GenericLaser::OnDigitalMod); + RETURN_ON_MM_ERROR(CreateProperty("Digital Modulation", "", MM::String, false, pAct)); + AddAllowedValue("Digital Modulation", "ON"); + AddAllowedValue("Digital Modulation", "OFF"); + + // Analog modulation selector (write/read) + pAct = new CPropertyAction(this, &GenericLaser::OnAnalogMod); + RETURN_ON_MM_ERROR(CreateProperty("Analog Modulation", "", MM::String, false, pAct)); + AddAllowedValue("Analog Modulation", "ON"); + AddAllowedValue("Analog Modulation", "OFF"); + + // Control mode selector (= APC or ACC) (write/read) + pAct = new CPropertyAction(this, &GenericLaser::OnControlMode); + RETURN_ON_MM_ERROR(CreateProperty("Control mode", "", MM::String, false, pAct)); + AddAllowedValue("Control mode", "ACC"); + AddAllowedValue("Control mode", "APC"); + + //Fire property + pAct = new CPropertyAction(this, &GenericLaser::OnFire); + RETURN_ON_MM_ERROR(CreateProperty("Fire", "0", MM::Float, false, pAct)); + + + + // Retrieves and define the laser's maximal power + string maxpowCmd = "DL SPM"; + + maxRelPower_ = 100.0; + + + // Retrieves and define the laser's maximal current + maxCurrent_ = 125.0; + + + // Power set point (write/read) + pAct = new CPropertyAction(this, &GenericLaser::OnPowerSetPoint); + RETURN_ON_MM_ERROR(CreateProperty("Power set point", "0", MM::Float, false, pAct)); + SetPropertyLimits("Power set point", 0, maxRelPower_); + + RETURN_ON_MM_ERROR(UpdateStatus()); + + initialized_ = true; + } + + return DEVICE_OK; +} + + +int OxxiusLCX::Fire(double deltaT) +{ + string activate_query = "DL 1"; + string deactivate_query = "DL 0"; + + if (laserOn_ == false) { + laserOn_ = true; + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), slot_, activate_query.c_str(), false)); + } + CDeviceUtils::SleepMs((long)(deltaT)); + laserOn_ = false; + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), slot_, deactivate_query.c_str(), false)); + //At the end, the laser is set to off + + return DEVICE_OK; +} + +//RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), slot_, newCommand.c_str(), false)); + +/////////////////////////////////////////////////////////////////////////////// +// Action handlers +/////////////////////////////////////////////////////////////////////////////// + +int OxxiusLCX::OnAlarm(MM::PropertyBase* pProp, MM::ActionType) +{ + unsigned int alarmInt = 99; + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), slot_, "?F", false)); + + parentHub_->ParseforInteger(alarmInt); + + switch (alarmInt) { + case 0: + alarm_ = "No Alarm"; + break; + case 1: + alarm_ = "Out-of-bounds diode current"; + break; + case 2: + alarm_ = "Unexpected laser power value"; + break; + case 3: + alarm_ = "Out-of-bounds supply voltage"; + break; + case 4: + alarm_ = "Out-of-bounds internal temperature"; + break; + case 5: + alarm_ = "Out-of-bounds baseplate temperature"; + break; + case 7: + alarm_ = "Interlock circuit open"; + break; + case 8: + alarm_ = "Soft reset"; + break; + default: + alarm_ = "Other alarm"; + } + + pProp->Set(alarm_.c_str()); + + return DEVICE_OK; +} + + +int OxxiusLCX::OnState(MM::PropertyBase* pProp, MM::ActionType) +{ + unsigned int stateInt = 99; + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), slot_, "?STA", false)); + + parentHub_->ParseforInteger(stateInt); + + switch (stateInt) { + case 1: + state_ = "Warm-up phase"; + break; + case 2: + state_ = "Stand-by state"; + break; + case 3: + state_ = "Emission on"; + break; + case 4: + state_ = "Internal error"; + break; + case 5: + state_ = "Alarm"; + break; + case 6: + state_ = "Sleep state"; + break; + default: + state_ = "Other state"; + } + + pProp->Set(alarm_.c_str()); + + return DEVICE_OK; +} + + + +int OxxiusLCX::OnEmissionOnOff(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::BeforeGet) { + unsigned int status = 0; + ostringstream query; + + query << "?CS " << slot_; + + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), NO_SLOT, query.str().c_str(), false)); + parentHub_->ParseforInteger(status); + + switch (status) { + case 2: // LCX model: shutter closed + laserOn_ = false; + break; + case 3: // LCX model: shutter open + laserOn_ = true; + break; + default: + laserOn_ = true; + } + + if (laserOn_) { + pProp->Set("ON"); + } + else { + pProp->Set("OFF"); + } + } + else if (eAct == MM::AfterSet) { + string newEmissionStatus, newCommand = ""; + + pProp->Get(newEmissionStatus); + + if (newEmissionStatus.compare("ON") == 0) { + newCommand = "DL 1"; + laserOn_ = true; + } + else if (newEmissionStatus.compare("OFF") == 0) { + newCommand = "DL 0"; + laserOn_ = false; + } + + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), slot_, newCommand.c_str(), false)); + } + return DEVICE_OK; +} + + +int OxxiusLCX::OnDigitalMod(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + unsigned int querySlot = NO_SLOT; + + if (eAct == MM::BeforeGet) { + string query; + + + querySlot = NO_SLOT; + if (link_AOM1 == true) { + query = "AOM1 TTL"; + } + else if (link_AOM2 == true) { + query = "AOM2 TTL"; + } + else { + query = ""; + } + + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), querySlot, query.c_str(), false)); + + bool digiM; + parentHub_->ParseforBoolean(digiM); + + if (digiM) + digitalMod_.assign("ON"); + else + digitalMod_.assign("OFF"); + + pProp->Set(digitalMod_.c_str()); + + } + else if (eAct == MM::AfterSet) { + string newModSet, newCommand; + + pProp->Get(newModSet); + digitalMod_.assign(newModSet); + + if (digitalMod_ == "ON") { + querySlot = NO_SLOT; + + if (link_AOM1 == true) { + newCommand.assign("AOM1 TTL 1"); + } + else if (link_AOM2 == true) { + newCommand.assign("AOM2 TTL 1"); + } + else { + newCommand.assign(""); + } + + } + else if (digitalMod_ == "OFF") { + querySlot = NO_SLOT; + + if (link_AOM1 == true) { + newCommand.assign("AOM1 TTL 0"); + } + else if (link_AOM2 == true) { + newCommand.assign("AOM2 TTL 0"); + } + else { + newCommand.assign(""); + } + } + + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), querySlot, newCommand.c_str(), false)); + } + return DEVICE_OK; +} + + +int OxxiusLCX::OnAnalogMod(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + unsigned int querySlot = NO_SLOT; + + if (eAct == MM::BeforeGet) { + string query; + querySlot = NO_SLOT; + + if (link_AOM1 == true) { + query = "AOM1 AM"; + } + else if (link_AOM2 == true) { + query = "AOM2 AM"; + } + else { + query = ""; + } + + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), querySlot, query.c_str(), false)); + bool digiM; + parentHub_->ParseforBoolean(digiM); + + if (digiM) { + digitalMod_.assign("ON"); + } + else { + digitalMod_.assign("OFF"); + } + pProp->Set(digitalMod_.c_str()); + + } + else if (eAct == MM::AfterSet) { + ostringstream newCommand; + + querySlot = NO_SLOT; + if (link_AOM1 == true) { + newCommand << "AOM1 AM"; + } + if (link_AOM2 == true) { + newCommand << "AOM2 AM"; + } + else { + return DEVICE_OK; + } + + pProp->Get(digitalMod_); + + if (digitalMod_ == "OFF") + newCommand << "0"; + else + newCommand << "1"; + + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), querySlot, newCommand.str().c_str(), false)); + } + return DEVICE_OK; +} + + +int OxxiusLCX::OnControlMode(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::BeforeGet) { + controlMode_.assign("APC"); + pProp->Set(controlMode_.c_str()); + } + else if (eAct == MM::AfterSet) { + string newCommand; + + pProp->Get(controlMode_); + + controlMode_ = "APC"; + pProp->Set(controlMode_.c_str()); + + } + return DEVICE_OK; +} + +/* +int OxxiusLCX::OnPowerSetPoint(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::BeforeGet) { + string command = "?SP"; + unsigned int thisSlot = slot_; + float absSetPoint_; + + if ((0 < mpa_number) && (mpa_number < 7)) { + thisSlot = NO_SLOT; + command = "?PL"; + stringstream s; + s << mpa_number; + command += s.str(); + } + else { + if (link_AOM1 == true) { + thisSlot = NO_SLOT; + command = "?SP1"; + } + else if (link_AOM2 == true) { + thisSlot = NO_SLOT; + command = "?SP2"; + } + } + + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), thisSlot, command.c_str(), false)); + parentHub_->ParseforFloat(absSetPoint_); + + pProp->Set((100 * absSetPoint_) / nominalPower_); + } + else if (eAct == MM::AfterSet) { + + double GUISetPoint = 0.0; + pProp->Get(GUISetPoint); + + if ((GUISetPoint >= 0.0) || (GUISetPoint <= maxRelPower_)) { + string command = "P"; + unsigned int thisSlot = slot_; + + ostringstream newCommand; + char* powerSPString = new char[20]; + strcpy(powerSPString, CDeviceUtils::ConvertToString((GUISetPoint * nominalPower_) / 100)); + + if ((0 < mpa_number) && (mpa_number < 7)) { + thisSlot = NO_SLOT; + command = "IP"; + command += CDeviceUtils::ConvertToString((int)(mpa_number)); + + strcpy(powerSPString, CDeviceUtils::ConvertToString(GUISetPoint)); + } + else { + if (link_AOM1 == true) { + thisSlot = NO_SLOT; + command = "P"; + } + else if (link_AOM2 == true) { + thisSlot = NO_SLOT; + command = "P2"; + } + else if (pow_adj == true) { + command = "IP"; + strcpy(powerSPString, CDeviceUtils::ConvertToString(GUISetPoint)); + } + } + + newCommand << command << " " << powerSPString; + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), thisSlot, newCommand.str().c_str(), false)); + } + else { + // If the value entered through the GUI is not valid, read the machine value + OnPowerSetPoint(pProp, MM::BeforeGet); + } + } + return DEVICE_OK; +} +*/ + +/* +int OxxiusLCX::OnFire(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::AfterSet) { + double input; + pProp->Get(input); + return Fire(input); + } + return DEVICE_OK; +} +*/ \ No newline at end of file diff --git a/DeviceAdapters/OxxiusCombiner/OxxiusLCX.h b/DeviceAdapters/OxxiusCombiner/OxxiusLCX.h new file mode 100644 index 000000000..b361a596b --- /dev/null +++ b/DeviceAdapters/OxxiusCombiner/OxxiusLCX.h @@ -0,0 +1,47 @@ +#pragma once +#include "GenericLaser.h" +using namespace std; + +class OxxiusLCX : public GenericLaser +{ +public: + OxxiusLCX(const char* nameAndSlot); + ~OxxiusLCX(); + + int Initialize(); + + int Fire(double deltaT); + + // int OnPowerSetPoint(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnCurrentSetPoint(MM::PropertyBase* pProp, MM::ActionType eAct) { return DEVICE_OK; }; //never called because it's a LCX laser + int OnEmissionOnOff(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnControlMode(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnAnalogMod(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnDigitalMod(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnState(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnAlarm(MM::PropertyBase* pProp, MM::ActionType eAct); + // int OnFire(MM::PropertyBase* pProp, MM::ActionType eAct); + + int OnPowerReadback(MM::PropertyBase* pProp, MM::ActionType eAct) { return DEVICE_OK; }; + int OnCurrentReadback(MM::PropertyBase* pProp, MM::ActionType eAct) { return DEVICE_OK; }; + int OnOperatingMode(MM::PropertyBase* pProp, MM::ActionType eAct) { return DEVICE_OK; }; + +private: + bool initialized_; + + //// SPECIFIC TO LCX + float maxRelPower_; + float nominalPower_; + float maxCurrent_; + unsigned int waveLength; + string state_; + string alarm_; + string controlMode_; + string analogMod_; + string digitalMod_; + + bool link_AOM1;//->LCX linked to a AOM number 1 + bool link_AOM2;//->LCX linked to a AOM number 2 + bool pow_adj; //-> LCX with power adjustment + int mpa_number; //-> LCX linked to mpa number n +}; \ No newline at end of file diff --git a/DeviceAdapters/OxxiusCombiner/OxxiusMDual.cpp b/DeviceAdapters/OxxiusCombiner/OxxiusMDual.cpp new file mode 100644 index 000000000..7360b3a11 --- /dev/null +++ b/DeviceAdapters/OxxiusCombiner/OxxiusMDual.cpp @@ -0,0 +1,169 @@ +#include "OxxiusMDual.h" + +#include +#include +#include +#include +using namespace std; + +/////////////////////////////////////////////////////////////////////////////// +// +// Oxxius M-Dual implementation +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +/////////////////////////////////////////////////////////////////////////////// + +OxxiusMDual::OxxiusMDual(const char* nameAndSlot) : initialized_(false) +{ + parentHub_ = 0; + core_ = GetCoreCallback(); + + std::string tSlot = string(nameAndSlot); + name_.assign(tSlot); // sets MDual name + + slot_ = tSlot.substr(tSlot.length() - 1, 1); + + // Set property list + // ----------------- + // Name (read only) + CreateProperty(MM::g_Keyword_Name, name_.c_str(), MM::String, true); + + CreateProperty(MM::g_Keyword_Description, "M-Dual module", MM::String, true); + + InitializeDefaultErrorMessages(); + SetErrorText(ERR_NO_PORT_SET, "Hub Device not found. The Laser combiner is needed to create this device"); + + // parent ID display + CreateHubIDProperty(); +} + + +OxxiusMDual::~OxxiusMDual() +{ + Shutdown(); +} + + +int OxxiusMDual::Initialize() +{ + if (!initialized_) { + parentHub_ = static_cast(GetParentHub()); + if (!parentHub_) { + return DEVICE_COMM_HUB_MISSING; + } + char hubLabel[MM::MaxStrLength]; + parentHub_->GetLabel(hubLabel); + SetParentID(hubLabel); // for backward compatibility + + + // Set property list + // ----------------- + CPropertyAction* pAct = new CPropertyAction(this, &OxxiusMDual::OnSetRatio);//setting the possible positions + RETURN_ON_MM_ERROR(CreateProperty("Split ratio", "0", MM::Float, false, pAct)); + SetPropertyLimits("Split ratio", 0.0, 100.0); + + // Set property list + // ----------------- + // State + /*CPropertyAction* pAct = new CPropertyAction (this, &OxxiusMDual::OnState); + RETURN_ON_MM_ERROR( CreateProperty(MM::g_Keyword_State, "0", MM::Integer, false, pAct) ); + SetPropertyLimits("Set Position", 0, 100);*/ + + /*char pos[3]; + for (unsigned int i=0; iQueryCommand(this, GetCoreCallback(), NO_SLOT, command.str().c_str()) ); + parentHub_->ParseforInteger(currentPos); + + //SetPosition(currentPos); + pProp->Set((long)currentPos); + } + else if (eAct == MM::AfterSet) { + long newPosition = 0; + + //GetPosition(newPosition); + pProp->Get(newPosition); + + std::ostringstream newCommand; + newCommand << "IP" << slot_ << " " << newPosition; + + RETURN_ON_MM_ERROR( parentHub_->QueryCommand(this, GetCoreCallback(), NO_SLOT, newCommand.str().c_str()) ); + } + return DEVICE_OK; +}*/ + + +int OxxiusMDual::OnSetRatio(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::BeforeGet) { + double currentRatio = 0.0; + std::ostringstream command; + command << "IP" << slot_; + + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), NO_SLOT, command.str().c_str(), true)); + parentHub_->ParseforPercent(currentRatio); + + pProp->Set(currentRatio); + } + + else if (eAct == MM::AfterSet) { + double newRatio = 0.0; + + pProp->Get(newRatio); + if ((newRatio >= 0.0) || (newRatio <= 100.0)) { + std::ostringstream newCommand; + newCommand << "IP" << slot_ << " " << newRatio; + + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), NO_SLOT, newCommand.str().c_str(), true)); + } + } + return DEVICE_OK; +} \ No newline at end of file diff --git a/DeviceAdapters/OxxiusCombiner/OxxiusMDual.h b/DeviceAdapters/OxxiusCombiner/OxxiusMDual.h new file mode 100644 index 000000000..e3a769de8 --- /dev/null +++ b/DeviceAdapters/OxxiusCombiner/OxxiusMDual.h @@ -0,0 +1,35 @@ +#pragma once + +#include "OxxiusCombinerHub.h" + +#include "DeviceBase.h" +#include +#include +#include + +class OxxiusMDual : public CGenericBase +{ +public: + OxxiusMDual(const char* name); + ~OxxiusMDual(); + + // MMDevice API + // ------------ + int Initialize(); + int Shutdown(); + + void GetName(char* pszName) const; + bool Busy(); + + // Action Interface + // ---------------- + int OnSetRatio(MM::PropertyBase* pProp, MM::ActionType eAct); + +private: + bool initialized_; + std::string name_; + std::string slot_; + MM::Core* core_; + + OxxiusCombinerHub* parentHub_; +}; diff --git a/DeviceAdapters/OxxiusCombiner/OxxiusShutter.cpp b/DeviceAdapters/OxxiusCombiner/OxxiusShutter.cpp new file mode 100644 index 000000000..b985b1db7 --- /dev/null +++ b/DeviceAdapters/OxxiusCombiner/OxxiusShutter.cpp @@ -0,0 +1,147 @@ +#include "OxxiusShutter.h" + +#include +#include +#include +#include +using namespace std; + +/////////////////////////////////////////////////////////////////////////////// +// +// Oxxius shutter implementation +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +/////////////////////////////////////////////////////////////////////////////// + +OxxiusShutter::OxxiusShutter(const char* nameAndSlot) : initialized_(false) +{ + isOpen_ = false; + parentHub_ = 0; + + name_.assign(nameAndSlot); + + std::string strChnl = string(nameAndSlot); + strChnl = strChnl.substr(strChnl.length() - 1, 1); + channel_ = (unsigned int)atoi(strChnl.c_str()); + + // Set property list + // ----------------- + // Name (read only) + CreateProperty(MM::g_Keyword_Name, name_.c_str(), MM::String, true); + + std::ostringstream shutterDesc; + shutterDesc << "Electro-mechanical shutter on channel " << channel_ << "."; + CreateProperty(MM::g_Keyword_Description, shutterDesc.str().c_str(), MM::String, true); + + InitializeDefaultErrorMessages(); + SetErrorText(ERR_NO_PORT_SET, "Hub Device not found. The Laser combiner is needed to create this device"); + + // parent ID display + CreateHubIDProperty(); +} + + +OxxiusShutter::~OxxiusShutter() +{ + Shutdown(); +} + + +int OxxiusShutter::Initialize() +{ + if (!initialized_) { + parentHub_ = static_cast(GetParentHub()); + if (!parentHub_) { + return DEVICE_COMM_HUB_MISSING; + } + char hubLabel[MM::MaxStrLength]; + parentHub_->GetLabel(hubLabel); + SetParentID(hubLabel); // for backward compatibility + + // Set property list + // ----------------- + + // Open/Close selector (write/read) + CPropertyAction* pAct = new CPropertyAction(this, &OxxiusShutter::OnState); + RETURN_ON_MM_ERROR(CreateProperty(MM::g_Keyword_State, "", MM::String, false, pAct)); + AddAllowedValue(MM::g_Keyword_State, "Open"); + AddAllowedValue(MM::g_Keyword_State, "Closed"); + + // Closing the shutter on Initialization + RETURN_ON_MM_ERROR(SetProperty(MM::g_Keyword_State, "Closed")); + + RETURN_ON_MM_ERROR(UpdateStatus()); + + initialized_ = true; + } + + return DEVICE_OK; +} + + +int OxxiusShutter::Shutdown() +{ + initialized_ = false; + return DEVICE_OK; +} + + +void OxxiusShutter::GetName(char* Name) const +{ + CDeviceUtils::CopyLimitedString(Name, name_.c_str()); +} + + +bool OxxiusShutter::Busy() +{ + return false; +} + + +int OxxiusShutter::SetOpen(bool openCommand) +{ + if (openCommand) + return SetProperty(MM::g_Keyword_State, "Open"); + else + return SetProperty(MM::g_Keyword_State, "Closed"); +} + + +int OxxiusShutter::GetOpen(bool& isOpen) +{ + isOpen = isOpen_; + return DEVICE_OK; +} + + +int OxxiusShutter::OnState(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::BeforeGet) { + if (isOpen_) { + pProp->Set("Open"); + } + else { + pProp->Set("Closed"); + } + } + else if (eAct == MM::AfterSet) { + + std::string newState = ""; + pProp->Get(newState); + + std::ostringstream newCommand; + newCommand << "SH" << channel_ << " "; + + if (newState.compare("Open") == 0) { + newCommand << "1"; + isOpen_ = true; + } + else if (newState.compare("Closed") == 0) { + newCommand << "0"; + isOpen_ = false; + } + + RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), NO_SLOT, newCommand.str().c_str(), false)); + } + return DEVICE_OK; +} \ No newline at end of file diff --git a/DeviceAdapters/OxxiusCombiner/OxxiusShutter.h b/DeviceAdapters/OxxiusCombiner/OxxiusShutter.h new file mode 100644 index 000000000..8ce63db4b --- /dev/null +++ b/DeviceAdapters/OxxiusCombiner/OxxiusShutter.h @@ -0,0 +1,48 @@ +#pragma once + +#include "OxxiusCombinerHub.h" + +#include "DeviceBase.h" +#include +#include +#include + +////////////////////////////////////////////////////////////////////////////// +// +// Device adaptaters for "shutter" source in Combiner +// +////////////////////////////////////////////////////////////////////////////// + +class OxxiusShutter : public CShutterBase +{ +public: + OxxiusShutter(const char* nameAndChannel); + ~OxxiusShutter(); + + // MMDevice API + // ------------ + int Initialize(); + int Shutdown(); + + void GetName(char* pszName) const; + bool Busy(); + + // Shutter API + int SetOpen(bool openCommand = true); + int GetOpen(bool& isOpen); + int Fire(double /*deltaT*/) { return DEVICE_UNSUPPORTED_COMMAND; } + + // Action Interface + // ---------------- + int OnState(MM::PropertyBase* pProp, MM::ActionType eAct); + +private: + bool initialized_; + std::string name_; + + OxxiusCombinerHub* parentHub_; + bool isOpen_; + unsigned int channel_; + + MM::MMTime changedTime_; +}; diff --git a/DeviceAdapters/OxxiusCombiner/Oxxius_combiner.cpp b/DeviceAdapters/OxxiusCombiner/Oxxius_combiner.cpp deleted file mode 100644 index 49e042ddd..000000000 --- a/DeviceAdapters/OxxiusCombiner/Oxxius_combiner.cpp +++ /dev/null @@ -1,1920 +0,0 @@ - -/////////////////////////////////////////////////////////////////////////////// -// FILE: OxxiusCombiner.cpp -// PROJECT: Micro-Manager -// SUBSYSTEM: DeviceAdapters -//----------------------------------------------------------------------------- -// DESCRIPTION: Controls Oxxius lasers and combiners through a serial port -// COPYRIGHT: Oxxius SA, 2013-2019 -// LICENSE: LGPL -// AUTHORS: Tristan Martinez, Pierre Bretagne -// - - -#include "Oxxius_combiner.h" -#include -#include -#include -#include -#include "ModuleInterface.h" -using namespace std; - -// -#define MAX_NUMBER_OF_SLOTS 6 -#define RCV_BUF_LENGTH 256 -#define NO_SLOT 0 - -// Oxxius devices -const char* g_OxxiusCombinerDeviceName = "Combiner"; -const char* g_OxxiusLaserBoxxDeviceName = "LaserBoxx source"; -const char* g_OxxiusLaserBoxx1DeviceName = "LaserBoxx source 1"; -const char* g_OxxiusLaserBoxx2DeviceName = "LaserBoxx source 2"; -const char* g_OxxiusLaserBoxx3DeviceName = "LaserBoxx source 3"; -const char* g_OxxiusLaserBoxx4DeviceName = "LaserBoxx source 4"; -const char* g_OxxiusLaserBoxx5DeviceName = "LaserBoxx source 5"; -const char* g_OxxiusLaserBoxx6DeviceName = "LaserBoxx source 6"; - -const char* g_ObisLaserDeviceName = "Obis laser source"; -const char* g_ObisLaser1DeviceName = "Obis laser Source 1"; -const char* g_ObisLaser2DeviceName = "Obis laser Source 2"; -const char* g_ObisLaser3DeviceName = "Obis laser Source 3"; -const char* g_ObisLaser4DeviceName = "Obis laser Source 4"; -const char* g_ObisLaser5DeviceName = "Obis laser Source 5"; -const char* g_ObisLaser6DeviceName = "Obis laser Source 6"; - -const char* g_OxxiusShutterDeviceName = "Shutter"; -const char* g_OxxiusShutter1DeviceName = "Shutter 1"; -const char* g_OxxiusShutter2DeviceName = "Shutter 2"; -const char* g_OxxiusMDualDeviceName = "MDual"; -const char* g_OxxiusMDualADeviceName = "MDual A"; -const char* g_OxxiusMDualBDeviceName = "MDual B"; -const char* g_OxxiusMDualCDeviceName = "MDual C"; -const char* g_OxxiusFlipMirrorDeviceName = "Flip-Mirror"; -const char* g_OxxiusFlipMirror1DeviceName = "Flip-Mirror 1"; -const char* g_OxxiusFlipMirror2DeviceName = "Flip-Mirror 2"; - - - - -const char* g_slotPrefix[7] = {"","L1 ","L2 ","L3 ","L4 ","L5 ","L6 "}; - -const char* convertable[3] = { "A", "B", "C" }; - -/////////////////////////////////////////////////////////////////////////////// -// Exported MMDevice API -/////////////////////////////////////////////////////////////////////////////// -MODULE_API void InitializeModuleData() -{ - RegisterDevice(g_OxxiusCombinerDeviceName, MM::HubDevice, "Oxxius laser combiner controlled through serial interface"); - RegisterDevice(g_OxxiusLaserBoxx1DeviceName, MM::ShutterDevice, "LaserBoxx on slot 1"); - RegisterDevice(g_OxxiusLaserBoxx2DeviceName, MM::ShutterDevice, "LaserBoxx on slot 2"); - RegisterDevice(g_OxxiusLaserBoxx3DeviceName, MM::ShutterDevice, "LaserBoxx on slot 3"); - RegisterDevice(g_OxxiusLaserBoxx4DeviceName, MM::ShutterDevice, "LaserBoxx on slot 4"); - RegisterDevice(g_OxxiusLaserBoxx5DeviceName, MM::ShutterDevice, "LaserBoxx on slot 5"); - RegisterDevice(g_OxxiusLaserBoxx6DeviceName, MM::ShutterDevice, "LaserBoxx on slot 6"); - - RegisterDevice(g_ObisLaser1DeviceName, MM::ShutterDevice, "Obis Laser on slot 1"); - RegisterDevice(g_ObisLaser2DeviceName, MM::ShutterDevice, "Obis Laser on slot 2"); - RegisterDevice(g_ObisLaser3DeviceName, MM::ShutterDevice, "Obis Laser on slot 3"); - RegisterDevice(g_ObisLaser4DeviceName, MM::ShutterDevice, "Obis Laser on slot 4"); - RegisterDevice(g_ObisLaser5DeviceName, MM::ShutterDevice, "Obis Laser on slot 5"); - RegisterDevice(g_ObisLaser6DeviceName, MM::ShutterDevice, "Obis Laser on slot 6"); - - RegisterDevice(g_OxxiusShutter1DeviceName, MM::ShutterDevice, "E-m shutter on channel 1"); - RegisterDevice(g_OxxiusShutter2DeviceName, MM::ShutterDevice, "E-m shutter on channel 2"); - RegisterDevice(g_OxxiusMDualADeviceName, MM::GenericDevice, "M-Dual on channel A"); - RegisterDevice(g_OxxiusMDualBDeviceName, MM::GenericDevice, "M-Dual on channel B"); - RegisterDevice(g_OxxiusMDualCDeviceName, MM::GenericDevice, "M-Dual on channel C"); - RegisterDevice(g_OxxiusFlipMirror1DeviceName, MM::GenericDevice, "Flip-Mirror on slot 1"); - RegisterDevice(g_OxxiusFlipMirror2DeviceName, MM::GenericDevice, "Flip-Mirror on slot 2"); - -} - -MODULE_API MM::Device* CreateDevice(const char* deviceNameChar) -{ - if (deviceNameChar == 0) - return 0; - - std::string deviceNameAndSlot = string(deviceNameChar); - - if (strcmp(deviceNameChar,g_OxxiusCombinerDeviceName) == 0) { - return new OxxiusCombinerHub(); - } else if ( deviceNameAndSlot.compare(0, strlen(g_OxxiusLaserBoxxDeviceName), g_OxxiusLaserBoxxDeviceName) == 0 ) { - return new OxxiusLaserBoxx(deviceNameChar); - } else if ( deviceNameAndSlot.compare(0, strlen(g_OxxiusShutterDeviceName), g_OxxiusShutterDeviceName) == 0 ) { - return new OxxiusShutter(deviceNameChar); - } else if (deviceNameAndSlot.compare(0, strlen(g_OxxiusMDualDeviceName), g_OxxiusMDualDeviceName) == 0) { - return new OxxiusMDual(deviceNameChar); - } else if (deviceNameAndSlot.compare(0, strlen(g_OxxiusFlipMirrorDeviceName), g_OxxiusFlipMirrorDeviceName) == 0) { - return new OxxiusFlipMirror(deviceNameChar); - } else if (deviceNameAndSlot.compare(0, strlen(g_ObisLaserDeviceName), g_ObisLaserDeviceName) == 0) { -// return new OxxiusObisSupport(deviceNameChar); - } - return 0; -} - -MODULE_API void DeleteDevice(MM::Device* pDevice) -{ - delete pDevice; -} - -/////////////////////////////////////////////////////////////////////////////// -// -// Oxxius combiner implementation -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// -/////////////////////////////////////////////////////////////////////////////// - - -OxxiusCombinerHub::OxxiusCombinerHub() : initialized_(false) -{ - // Initializing private variables - serialNumber_ = ""; - installedDevices_ = 0; - serialAnswer_ = ""; - interlockClosed_ = false; - keyActivated_ = false; - - InitializeDefaultErrorMessages(); - SetErrorText(ERR_COMBINER_NOT_FOUND, "Hub Device not found. The peer device is expected to be a Oxxius combiner"); - - - // Create pre-initialization properties - // ------------------------------------ - - // Communication port - CPropertyAction* pAct = new CPropertyAction(this, &OxxiusCombinerHub::OnPort); - CreateProperty(MM::g_Keyword_Port, "Undefined", MM::String, false, pAct, true); -} - -OxxiusCombinerHub::~OxxiusCombinerHub() -{ - Shutdown(); -} - -void OxxiusCombinerHub::GetName(char* name) const -{ - CDeviceUtils::CopyLimitedString(name, g_OxxiusCombinerDeviceName); -} - -bool OxxiusCombinerHub::Busy() -{ - return false; - -} - - -int OxxiusCombinerHub::Initialize() -{ - if(!initialized_) { - - // Set proprety list - // - - - - - - - - - - - // Name and description of the combiner: - RETURN_ON_MM_ERROR( CreateProperty(MM::g_Keyword_Name, g_OxxiusCombinerDeviceName, MM::String, true) ); - RETURN_ON_MM_ERROR( CreateProperty(MM::g_Keyword_Description, "Oxxius L6Cc/L4Cc combiner", MM::String, true) ); - - // Serial number of the combiner: - CPropertyAction* pAct = new CPropertyAction (this, &OxxiusCombinerHub::OnSerialNumber); - RETURN_ON_MM_ERROR( CreateProperty(MM::g_Keyword_HubID, serialNumber_.c_str(), MM::String, true, pAct) ); - - // Interlock circuit: - pAct = new CPropertyAction (this, &OxxiusCombinerHub::OnInterlock); - RETURN_ON_MM_ERROR( CreateProperty("Interlock circuit", "", MM::String, true, pAct) ); - - // Emission key: - pAct = new CPropertyAction (this, &OxxiusCombinerHub::OnEmissionKey); - RETURN_ON_MM_ERROR( CreateProperty("EmissionKey", "", MM::String, true, pAct) ); - - if (!IsCallbackRegistered()) - return DEVICE_NO_CALLBACK_REGISTERED; - - // Enumerates the installed AOMs and their position - bool AOM1en = false, AOM2en = false; - unsigned int ver = 0; - - RETURN_ON_MM_ERROR(QueryCommand(this, GetCoreCallback(), NO_SLOT, "AOM1 EN", false)); - ParseforBoolean(AOM1en); - - RETURN_ON_MM_ERROR(QueryCommand(this, GetCoreCallback(), NO_SLOT, "AOM2 EN", false)); - ParseforBoolean(AOM2en); - - RETURN_ON_MM_ERROR(QueryCommand(this, GetCoreCallback(), NO_SLOT, "SV?", false)); - ParseforVersion(ver); - - // A position equal to "0" stands for an absence of modulator - if (AOM1en) { - bool adcom = false; - string command = ""; - - if (ver < 1016) { //version check - adcom = true; - command = "AOM1 PO"; - } - else { - adcom = false; - command = "AOM1 POS"; - } - RETURN_ON_MM_ERROR(QueryCommand(this, GetCoreCallback(), NO_SLOT, command.c_str(), adcom)); - ParseforInteger(AOM1pos_); - } - if (AOM2en) { - bool adcom = false; - string command = ""; - - if (ver < 1016) { //version check - adcom = true; - command = "AOM2 PO"; - } - else { - adcom = false; - command = "AOM2 POS"; - } - RETURN_ON_MM_ERROR(QueryCommand(this, GetCoreCallback(), NO_SLOT, command.c_str(), adcom)); - ParseforInteger(AOM1pos_); - } - - - //Mpa position retreive - for (unsigned int i = 1; i <= MAX_NUMBER_OF_SLOTS; i++) { - string command = "IP"; - std::stringstream ss; - ss << i; - command += ss.str(); - - RETURN_ON_MM_ERROR(QueryCommand(this, GetCoreCallback(), NO_SLOT, command.c_str(), true)); - if (serialAnswer_ != "????") { - mpa[i] = 1; - } - } - - - - RETURN_ON_MM_ERROR( UpdateStatus() ); - - initialized_ = true; - - // RETURN_ON_MM_ERROR( DetectInstalledDevices() ); - } - return DEVICE_OK; -} - - - -int OxxiusCombinerHub::DetectInstalledDevices() -{ - if (initialized_) { - - // Enumerates the lasers (or devices) present on the combiner - unsigned int masque = 1; - unsigned int repartition = 0; - - //sending command ?CL - RETURN_ON_MM_ERROR( QueryCommand(this, GetCoreCallback(), NO_SLOT, "?CL", false) ); - ParseforInteger(repartition); - - for(unsigned int querySlot=1; querySlot<=MAX_NUMBER_OF_SLOTS; querySlot++) { - if ((repartition & masque) != 0) { - string answer; - // A laser source is listed, now querying for detailed information (model, etc) - - std::string detailedInfo, serialNumber; - - //send command to get devices information - RETURN_ON_MM_ERROR(QueryCommand(this, GetCoreCallback(), querySlot, "INF?", false)); - ParseforString(detailedInfo); - - if (detailedInfo != "timeout") { - std::ostringstream nameSlotModel; - nameSlotModel << g_OxxiusLaserBoxxDeviceName << " " << querySlot; - - MM::Device* pDev = ::CreateDevice(nameSlotModel.str().c_str()); - if (pDev) { - AddInstalledDevice(pDev); - installedDevices_++; - } - } - } - masque <<= 1; // Left-shift the bit mask and repeat - } - - // Creating Devices for the two electro-mechanical shutters: - for(unsigned int channel=1; channel<=2; channel++) { - std::ostringstream nameModelChannel; - nameModelChannel << g_OxxiusShutterDeviceName << " " << channel; - - MM::Device* pDev = ::CreateDevice(nameModelChannel.str().c_str()); - if (pDev) { - AddInstalledDevice(pDev); - installedDevices_++; - } - } - - // Creating Devices for the "Flip mirror" or MDUAL modules: - /*unsigned int FM1type = 0, FM2type = 0; - RETURN_ON_MM_ERROR(QueryCommand(this, GetCoreCallback(), NO_SLOT, "FM1C", false)); - ParseforInteger(FM1type); - RETURN_ON_MM_ERROR(QueryCommand(this, GetCoreCallback(), NO_SLOT, "FM2C", false)); - ParseforInteger(FM2type);*/ - - - //Mdual module creation - for (unsigned int j = 0; j <= 2; j++) { - std::string MDSlot; - std::ostringstream com; - com << "IP" << convertable[j]; - - std::ostringstream InfoMessage4; /////test in hub - InfoMessage4 << "test" << com.str(); - LogError(DEVICE_OK, this, GetCoreCallback(), InfoMessage4.str().c_str()); - - RETURN_ON_MM_ERROR(QueryCommand(this, GetCoreCallback(), NO_SLOT, com.str().c_str(), true)); - ParseforString(MDSlot); - - com.str(""); - com.clear(); - com << g_OxxiusMDualDeviceName << " " << convertable[j]; - - if (MDSlot != "????") { - MM::Device* pDev = ::CreateDevice(com.str().c_str()); - if (pDev) { - AddInstalledDevice(pDev); - installedDevices_++; - } - - } - - } - - //Flip mirror module creation - - for (unsigned int j = 1; j <= 4; j++) { - unsigned int FMSlot; - std::ostringstream com; - com << "FM" << j << "C"; - - RETURN_ON_MM_ERROR(QueryCommand(this, GetCoreCallback(), NO_SLOT, com.str().c_str(), false)); - ParseforInteger(FMSlot); - - com.str(""); - com.clear(); - com << g_OxxiusFlipMirrorDeviceName << " " << j; - - if (FMSlot == 1) { - - std::ostringstream InfoMessage4; /////test - InfoMessage4 << "test " << com.str().c_str(); - LogError(DEVICE_OK, this, GetCoreCallback(), InfoMessage4.str().c_str()); - - MM::Device* pDev = ::CreateDevice(com.str().c_str()); - if (pDev) { - AddInstalledDevice(pDev); - installedDevices_++; - } - - } - - } - - - //Laser OBIS creation - RETURN_ON_MM_ERROR(QueryCommand(this, GetCoreCallback(), NO_SLOT, "OB", true)); - ParseforInteger(obPos_); - - if (obPos_ != 0 && obPos_ != -1) { - std::ostringstream nameSlotModel; - nameSlotModel << g_ObisLaserDeviceName << " " << obPos_; - - MM::Device* pDev = ::CreateDevice(nameSlotModel.str().c_str()); - if (pDev) { - AddInstalledDevice(pDev); - installedDevices_++; - } - } - - /*switch (FM1type) { - case 4: { // MDual detected - MM::Device* pDev = ::CreateDevice(g_OxxiusMDualDeviceName); - if (pDev) { - AddInstalledDevice(pDev); - installedDevices_++; - } - break; - } - case 0: - default: - // nop - break; - }*/ - } - - return DEVICE_OK; -} - - -int OxxiusCombinerHub::Shutdown() -{ - initialized_ = false; - return DEVICE_OK; -} - - -/////////////////////////////////////////////////////////////////////////////// -// Action handlers -/////////////////////////////////////////////////////////////////////////////// - -int OxxiusCombinerHub::OnPort(MM::PropertyBase* pProp, MM::ActionType eAct) -{ - if (eAct == MM::BeforeGet) - { - pProp->Set(port_.c_str()); - } - else if (eAct == MM::AfterSet) - { - pProp->Get(port_); - pProp->Set(port_.c_str()); - } - return DEVICE_OK; -} - - -int OxxiusCombinerHub::OnSerialNumber(MM::PropertyBase* pProp, MM::ActionType pAct) -{ - if (pAct == MM::BeforeGet) { - QueryCommand(this, GetCoreCallback(), NO_SLOT, "HID?", false); - ParseforString(serialNumber_); - pProp->Set(serialNumber_.c_str()); - } - - return DEVICE_OK; -} - - -int OxxiusCombinerHub::OnInterlock(MM::PropertyBase* pProp, MM::ActionType pAct) -{ - if (pAct == MM::BeforeGet) { - QueryCommand(this, GetCoreCallback(), NO_SLOT, "INT?", false); - ParseforBoolean(interlockClosed_); - - if( interlockClosed_) { - pProp->Set("Closed"); - } else { - pProp->Set("Open"); - } - } - - return DEVICE_OK; -} - - -int OxxiusCombinerHub::OnEmissionKey(MM::PropertyBase* pProp, MM::ActionType pAct) -{ - if (pAct == MM::BeforeGet) { - QueryCommand(this, GetCoreCallback(), NO_SLOT, "KEY?", false); - ParseforBoolean(keyActivated_); - - if( keyActivated_) { - pProp->Set("Armed"); - } else { - pProp->Set("Disarmed"); - } - } - - return DEVICE_OK; -} - - -/////////////////////////////////////////////////////////////////////////////// -// Generic methods -/////////////////////////////////////////////////////////////////////////////// - -void OxxiusCombinerHub::LogError(int id, MM::Device* device, MM::Core* core, const char* functionName) //prinnt log messages -{ - std::ostringstream os; - char deviceName[MM::MaxStrLength]; - device->GetName(deviceName); - os << "Error " << id << ", " << deviceName << ", " << functionName << endl; - core->LogMessage(device, os.str().c_str(), false); -} - - -/** - * Sends a serial command to a given slot, then stores the result in the receive buffer. - */ -int OxxiusCombinerHub::QueryCommand(MM::Device* device, MM::Core* core, const unsigned int destinationSlot, const char* command, bool adco) -{ - // First check: if the command string is empty, do nothing and return "DEVICE_OK" - if( strcmp(command, "") == 0) return DEVICE_OK; - - char rcvBuf_[RCV_BUF_LENGTH]; - // Compose the command to be sent to the combiner - std::string strCommand, strHZIn, strHZOut; - strCommand.assign(g_slotPrefix[destinationSlot]); - strCommand.append(command); - strHZIn.assign(g_slotPrefix[destinationSlot]); - strHZIn.append("HZ 9876"); - strHZOut.assign(g_slotPrefix[destinationSlot]); - strHZOut.append("HZ 0"); - -/* - std::ostringstream InfoMessage; - InfoMessage << "Now sending command :"; - InfoMessage << string(strCommand.c_str()); - LogError(DEVICE_OK, device, core, InfoMessage.str().c_str()); -*/ - std::ostringstream InfoMessage2; - InfoMessage2 << "Send: " << command << " Received: "; - - // Preambule for specific commands - if (adco) { - int ret = core->SetSerialCommand(device, port_.c_str(), strHZIn.c_str(), "\r\n"); - ret = core->GetSerialAnswer(device, port_.c_str(), RCV_BUF_LENGTH, rcvBuf_, "\r\n"); - if (ret != DEVICE_OK) { - LogError(ret, device, core, "QueryCommand-SetSerialCommand - preambule"); - return ret; - } - } - - // Send command through the serial interface - int ret = core->SetSerialCommand(device, port_.c_str(), strCommand.c_str(), "\r\n"); - if (ret != DEVICE_OK) { - LogError(ret, device, core, "QueryCommand-SetSerialCommand"); - return ret; - } - - // Get a response - ret = core->GetSerialAnswer(device, port_.c_str(), RCV_BUF_LENGTH, rcvBuf_, "\r\n"); - - InfoMessage2 << rcvBuf_ ; - /* DEBUG ONLY */ - // LogError(DEVICE_OK, device, core, InfoMessage2.str().c_str()); - - if (ret != DEVICE_OK) { - LogError(ret, device, core, "QueryCommand-GetSerialAnswer"); - - // Keep on trying until we either get our answer, or 3 seconds have passed - int maxTimeMs = 3000; - // Wait for a (increasing) delay between each try - int delayMs = 10; - // Keep track of how often we tried - int counter = 1; - bool done = false; - MM::MMTime startTime (core->GetCurrentMMTime()); // Let's keep in mind that MMTime is counted in microseconds - - while (!done) { - counter++; - ret = core->GetSerialAnswer(device, port_.c_str(), RCV_BUF_LENGTH, rcvBuf_, "\r\n"); - if ((ret == DEVICE_OK) || - ((core->GetCurrentMMTime() - startTime) > - MM::MMTime::fromMs(maxTimeMs))) { - done = true; - } else { - CDeviceUtils::SleepMs(delayMs); - delayMs *= 2; - } - } - ostringstream os; - if (ret == DEVICE_OK) - os << "QueryCommand-GetSerialAnswer: Succeeded reading from serial port after trying " << counter << "times."; - else - os << "QueryCommand-GetSerialAnswer: Failed reading from serial port after trying " << counter << "times."; - - core->LogMessage(device, os.str().c_str(), true); - - serialAnswer_.assign(rcvBuf_); - return ret; - } - serialAnswer_.assign(rcvBuf_); - ret = core->PurgeSerial(device, port_.c_str()); - -/* if( strcmp(serialAnswer_, "timeout") == 0) { - std::ostringstream syntaxErrorMessage; - syntaxErrorMessage << "Time out received against sent command '"; - syntaxErrorMessage << string(strCommand.c_str()); - syntaxErrorMessage << "'"; - - LogError(DEVICE_SERIAL_TIMEOUT, device, core, syntaxErrorMessage.str().c_str()); - return DEVICE_SERIAL_TIMEOUT; - } -*/ - // Epilogue for specific commands - if (adco) { - int ret = core->SetSerialCommand(device, port_.c_str(), strHZOut.c_str(), "\r\n"); - ret = core->GetSerialAnswer(device, port_.c_str(), RCV_BUF_LENGTH, rcvBuf_, "\r\n"); - if (ret != DEVICE_OK) { - LogError(ret, device, core, "QueryCommand-SetSerialCommand - Epilogue"); - return ret; - } - } - - return DEVICE_OK; -} - - -int OxxiusCombinerHub::ParseforBoolean(bool &Bval) -{ - unsigned int intAnswer = (unsigned int)atoi(serialAnswer_.c_str()); - Bval = (intAnswer == 1); - - serialAnswer_.clear(); - - return DEVICE_OK; -} - - -int OxxiusCombinerHub::ParseforFloat(float &Dval) -{ - Dval = (float) atof(serialAnswer_.c_str()); - - serialAnswer_.clear(); - - return DEVICE_OK; -} - - -int OxxiusCombinerHub::ParseforInteger(unsigned int &Ival) -{ - Ival = (unsigned int)atoi(serialAnswer_.c_str()); - - serialAnswer_.clear(); - - return DEVICE_OK; -} - - -int OxxiusCombinerHub::ParseforString(std::string &Sval) -{ - Sval.assign(serialAnswer_); - - serialAnswer_.clear(); - - return DEVICE_OK; -} - - -int OxxiusCombinerHub::ParseforVersion(unsigned int &Vval) //cast the string into a comparable int -{ - std::string temp1; - std::string temp2(serialAnswer_); - - for (unsigned int i = 0; i <= (temp2.length())-1; i++) { - if (temp2.at(i) != '.') { - temp1 += temp2.at(i); - } - } - - stringstream s(temp1); - s >> Vval; - serialAnswer_.clear(); - - return DEVICE_OK; -} - - -int OxxiusCombinerHub::ParseforPercent(double &Pval) //cast the string into a comparable int -{ - std::string percentage; - std::size_t found; - - percentage.assign(serialAnswer_); - - found = percentage.find("%"); - if (found != std::string::npos) { - Pval = atof(percentage.substr(0, found).c_str()); - } - - return DEVICE_OK; -} - - - -int OxxiusCombinerHub::ParseforChar(char* Nval) -{ - strcpy(Nval,serialAnswer_.c_str()); - serialAnswer_.clear(); - - return DEVICE_OK; -} - -bool OxxiusCombinerHub::GetAOMpos1(unsigned int slot) -{ - bool res = false; - - if (slot == AOM1pos_) { - res = true; - } - - return res; -} - -bool OxxiusCombinerHub::GetAOMpos2(unsigned int slot) -{ - bool res = false; - - if (slot == AOM2pos_) { - res = true; - } - - return res; -} - -bool OxxiusCombinerHub::GetMPA(unsigned int slot) { - bool res = false; - - if (mpa[slot] == 1) { - res = true; - } - - return res; -} - -int OxxiusCombinerHub::GetObPos() { - return obPos_; -} - - -/////////////////////////////////////////////////////////////////////////////// -// -// Oxxius generic LaserBoxx implementation -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// -/////////////////////////////////////////////////////////////////////////////// - -/*std::ostringstream InfoMessageB; //////////// test out of hub - InfoMessageB << "testB" << "-" << slot_; - LogMessage(InfoMessageB.str().c_str(), false);*/ - -OxxiusLaserBoxx::OxxiusLaserBoxx(const char* nameAndSlot) : initialized_(false) -{ - std::string tSlot = string(nameAndSlot); - - name_.assign(tSlot);// set laser name - tSlot = tSlot.substr(tSlot.length()-1, 1); - slot_ = (unsigned int)atoi(tSlot.c_str());// set laser slot - - parentHub_ ; - busy_ = false; - laserOn_ = false; - alarm_ = ""; - state_ = ""; - digitalMod_ = ""; - analogMod_ = ""; - controlMode_ = ""; - - // powerSetPoint_ = 0.0; - maxRelPower_ = 0.0; - nominalPower_ = 0.0; - maxCurrent_ = 125.0; - - InitializeDefaultErrorMessages(); - SetErrorText(ERR_NO_PORT_SET, "Hub Device not found. The Laser combiner is needed to create this device"); - - // parent ID display - CreateHubIDProperty(); -} - - -OxxiusLaserBoxx::~OxxiusLaserBoxx() -{ - Shutdown(); -} - - -void OxxiusLaserBoxx::GetName(char* Name) const -{ - CDeviceUtils::CopyLimitedString(Name, name_.c_str()); -} - - -int OxxiusLaserBoxx::Initialize() -{ - if (!initialized_) { - - std::size_t found; - std::string strSlot; - std::string spa; - - - parentHub_ = static_cast(GetParentHub()); - if (!parentHub_ ) { - return DEVICE_COMM_HUB_MISSING; - } - char hubLabel[MM::MaxStrLength]; - parentHub_->GetLabel(hubLabel); - SetParentID(hubLabel); // for backward compatibility - - // Set property list - // ----------------- - // Name (read only) - RETURN_ON_MM_ERROR( CreateProperty(MM::g_Keyword_Name, name_.c_str(), MM::String, true) ); - - // Description (read only) - std::ostringstream descriPt1; - char sourceSerialNumber[] = "LAS-XXXXXXXXXX"; - parentHub_->QueryCommand(this, GetCoreCallback(), slot_, "HID?", false); - parentHub_->ParseforChar(sourceSerialNumber); - - parentHub_->QueryCommand(this, GetCoreCallback(), slot_, "IP", true); - parentHub_->ParseforString(spa); - - parentHub_->QueryCommand(this, GetCoreCallback(), slot_, "INF?", false); - parentHub_->ParseforString(strSlot); - - // Retrieves and define the laser's type - found = strSlot.find("-"); - if (found != std::string::npos) { - type = strSlot.substr(0, found); - } - strSlot.erase(0, found + 1); - - // Retrieves and define the laser's wavelength - found = strSlot.find("-"); - if (found != std::string::npos) { - waveLength = (unsigned int)atoi(strSlot.substr(0, found).c_str()); - - } - - // Retrieves and define the nominal power - strSlot.erase(0, found + 1); - nominalPower_ = (float) atof(strSlot.substr(0, found).c_str()); - - //set laser AOM if needed - // model[0] model[1] - // (major) (minor) - // 1 0 -> standard LBX - // 1 1n -> LBX linked to mpa number n - // 2 0 -> standard LCX - // 2 1 -> LCX linked to a AOM number 1 - // 2 2 -> LCX linked to a AOM number 2 - // 2 5 -> LCX with power adjustment - // 2 1n -> LCX linked to mpa number n - - - if (parentHub_->GetMPA(slot_)) { - model_[1] = 10 + slot_; - } else { - model_[1] = 0; - } - - if (type.compare("LBX") == 0) { // The source is a LBX - model_[0] = 1; - } - else if (type.compare("LCX") == 0) { // The source is a LCX - model_[0] = 2; - if (parentHub_->GetAOMpos1(slot_)) { //laser has AMO1 - model_[1] = 1; - } - if (parentHub_->GetAOMpos2(slot_)){ //laser has AMO2 - model_[1] = 2; - } - else if (spa != "????") { //self modulating lcx - model_[1] = 5; - } - } - else { // Should not happen: unkown type - model_[0] = 9; - model_[1] = 9; - } - - switch (model_[0]) { - case 1: // LBX model - descriPt1 << "LBX"; - break; - case 2: // LCX model - descriPt1 << "LCX"; - break; - default: // Should not happen - descriPt1 << "Unknown"; - break; - } - descriPt1 << " source on slot " << slot_; - descriPt1 << ", " << sourceSerialNumber; - - RETURN_ON_MM_ERROR( CreateProperty(MM::g_Keyword_Description, descriPt1.str().c_str(), MM::String, true) ); - - // Alarm (read only) - CPropertyAction* pAct = new CPropertyAction (this, &OxxiusLaserBoxx::OnAlarm); - RETURN_ON_MM_ERROR( CreateProperty("Alarm", "None", MM::String, true, pAct) ); - - // Status (read only) - pAct = new CPropertyAction (this, &OxxiusLaserBoxx::OnState); - RETURN_ON_MM_ERROR( CreateProperty("State", "", MM::String, true, pAct) ); - - // Emission selector (write/read) - pAct = new CPropertyAction (this, &OxxiusLaserBoxx::OnEmissionOnOff); - RETURN_ON_MM_ERROR( CreateProperty("Emission", "", MM::String, false, pAct) ); - AddAllowedValue("Emission", "ON"); - AddAllowedValue("Emission", "OFF"); - - // Digital modulation selector (write/read) - pAct = new CPropertyAction (this, &OxxiusLaserBoxx::OnDigitalMod); - RETURN_ON_MM_ERROR( CreateProperty("Digital Modulation", "", MM::String, false, pAct) ); - AddAllowedValue("Digital Modulation", "ON"); - AddAllowedValue("Digital Modulation", "OFF"); - - // Analog modulation selector (write/read) - pAct = new CPropertyAction (this, &OxxiusLaserBoxx::OnAnalogMod); - RETURN_ON_MM_ERROR( CreateProperty("Analog Modulation", "", MM::String, false, pAct) ); - AddAllowedValue("Analog Modulation", "ON"); - AddAllowedValue("Analog Modulation", "OFF"); - - // Control mode selector (= APC or ACC) (write/read) - pAct = new CPropertyAction (this, &OxxiusLaserBoxx::OnControlMode); - RETURN_ON_MM_ERROR( CreateProperty("Control mode", "", MM::String, false, pAct) ); - AddAllowedValue("Control mode", "ACC"); - AddAllowedValue("Control mode", "APC"); - - - - // Retrieves and define the laser's maximal power - std::string maxpowCmd = "DL SPM"; - switch (model_[0]) { - case 1: // LBX model - switch (model_[1]) { - case 0: // Standard LBX - parentHub_->QueryCommand(this, GetCoreCallback(), slot_, maxpowCmd.c_str(), true); - parentHub_->ParseforFloat(maxRelPower_); - maxRelPower_ = 100 * maxRelPower_ / nominalPower_; - break; - default: // LBX + MPA - maxRelPower_ = 100.0; - break; - } - break; - case 2: // LCX model - maxRelPower_ = 100.0; - break; - default: // Should not happen - maxRelPower_ = 0.0; - break; - } - - - // Retrieves and define the laser's maximal current - maxCurrent_ = 125.0; - - - // Power set point (write/read) - pAct = new CPropertyAction (this, &OxxiusLaserBoxx::OnPowerSetPoint); - RETURN_ON_MM_ERROR( CreateProperty("Power set point", "0", MM::Float, false, pAct) ); - SetPropertyLimits("Power set point", 0, maxRelPower_); - - // Power set point (write/read) - pAct = new CPropertyAction (this, &OxxiusLaserBoxx::OnCurrentSetPoint); - RETURN_ON_MM_ERROR( CreateProperty("Current set point", "0", MM::Float, false, pAct) ); - SetPropertyLimits("Current set point", 0, maxCurrent_); - - RETURN_ON_MM_ERROR( UpdateStatus() ); - - initialized_ = true; - } - - return DEVICE_OK; -} - - - -int OxxiusLaserBoxx::Shutdown() -{ - initialized_ = false; - return DEVICE_OK; -} - - -bool OxxiusLaserBoxx::Busy() -{ - return busy_; -} - - -int OxxiusLaserBoxx::SetOpen(bool openCommand) -{ - laserOn_ = openCommand; - return DEVICE_OK; -} - - -int OxxiusLaserBoxx::GetOpen(bool& isOpen) -{ - isOpen = laserOn_; - return DEVICE_OK; -} - - -/////////////////////////////////////////////////////////////////////////////// -// Action handlers -/////////////////////////////////////////////////////////////////////////////// - -int OxxiusLaserBoxx::OnAlarm(MM::PropertyBase* pProp, MM::ActionType) -{ - unsigned int alarmInt = 99; - RETURN_ON_MM_ERROR( parentHub_->QueryCommand(this, GetCoreCallback(), slot_, "?F", false) ); - - parentHub_->ParseforInteger(alarmInt); - - switch (alarmInt) { - case 0: - alarm_ = "No Alarm"; - break; - case 1: - alarm_ = "Out-of-bounds diode current"; - break; - case 2: - alarm_ = "Unexpected laser power value"; - break; - case 3: - alarm_ = "Out-of-bounds supply voltage"; - break; - case 4: - alarm_ = "Out-of-bounds internal temperature"; - break; - case 5: - alarm_ = "Out-of-bounds baseplate temperature"; - break; - case 7: - alarm_ = "Interlock circuit open"; - break; - case 8: - alarm_ = "Soft reset"; - break; - default: - alarm_ = "Other alarm"; - } - - pProp->Set(alarm_.c_str()); - - return DEVICE_OK; -} - - -int OxxiusLaserBoxx::OnState(MM::PropertyBase* pProp, MM::ActionType) -{ - unsigned int stateInt = 99; - RETURN_ON_MM_ERROR( parentHub_->QueryCommand(this, GetCoreCallback(), slot_, "?STA", false) ); - - parentHub_->ParseforInteger(stateInt); - - switch (stateInt) { - case 1: - state_ = "Warm-up phase"; - break; - case 2: - state_ = "Stand-by state"; - break; - case 3: - state_ = "Emission on"; - break; - case 4: - state_ = "Internal error"; - break; - case 5: - state_ = "Alarm"; - break; - case 6: - state_ = "Sleep state"; - break; - default: - state_ = "Other state"; - } - - pProp->Set(alarm_.c_str()); - - return DEVICE_OK; -} - - -int OxxiusLaserBoxx::OnEmissionOnOff(MM::PropertyBase* pProp, MM::ActionType eAct) -{ - if (eAct == MM::BeforeGet) { - unsigned int status = 0; - std::ostringstream query; - - query << "?CS " << slot_; - - RETURN_ON_MM_ERROR( parentHub_->QueryCommand(this, GetCoreCallback(), NO_SLOT, query.str().c_str(), false) ); - parentHub_->ParseforInteger(status); - - switch (status) { - case 0: // LBX model: Emission off - case 2: // LCX model: shutter closed - laserOn_ = false; - break; - case 1: // LBX model: Emission on - case 3: // LCX model: shutter open - laserOn_ = true; - break; - default: - laserOn_ = true; - } - - if (laserOn_) { - pProp->Set("ON"); - } else { - pProp->Set("OFF"); - } - } - else if (eAct == MM::AfterSet) { - std::string newEmissionStatus, newCommand = ""; - - pProp->Get(newEmissionStatus); - - if( newEmissionStatus.compare("ON") == 0 ) { - newCommand = "DL 1"; - laserOn_ = true; - } else if ( newEmissionStatus.compare("OFF") == 0 ) { - newCommand = "DL 0"; - laserOn_ = false; - } - - RETURN_ON_MM_ERROR( parentHub_->QueryCommand(this, GetCoreCallback(), slot_, newCommand.c_str(), false) ); - } - return DEVICE_OK; -} - - -int OxxiusLaserBoxx::OnDigitalMod(MM::PropertyBase* pProp, MM::ActionType eAct) -{ - unsigned int querySlot = NO_SLOT; - - if (eAct == MM::BeforeGet) { - std::string query; - - // The slot and command depend on the model - switch (model_[0]) { - case 1: // LBX model: retreiving the modulation status - querySlot = slot_; - query = "?TTL"; - break; - case 2: // LCX model: retreiving the modulation status - querySlot = NO_SLOT; - switch (model_[1]) { - case 1: - query = "AOM1 TTL"; - break; - case 2: - query = "AOM2 TTL"; - break; - default: - query = ""; - } - break; - default: // Should not happen - return DEVICE_OK; - } - - RETURN_ON_MM_ERROR( parentHub_->QueryCommand(this, GetCoreCallback(), querySlot, query.c_str(), false) ); - - bool digiM; - parentHub_->ParseforBoolean(digiM); - - if (digiM) - digitalMod_.assign("ON"); - else - digitalMod_.assign("OFF"); - - pProp->Set(digitalMod_.c_str()); - - } else if (eAct == MM::AfterSet) { - std::string newModSet, newCommand; - - pProp->Get(newModSet); - digitalMod_.assign(newModSet); - - if (digitalMod_ == "ON") { - switch (model_[0]) { - case 1: // LBX model: setting the modulation on - querySlot = slot_; - newCommand.assign("TTL 1"); - break; - case 2: // LCX model: setting the modulation on - querySlot = NO_SLOT; - - switch (model_[1]) { - case 1: - newCommand.assign("AOM1 TTL 1"); - break; - case 2: - newCommand.assign("AOM2 TTL 1"); - break; - default: - newCommand.assign(""); - } - break; - default: // Should not happen - return DEVICE_OK; - } - - } else if (digitalMod_ == "OFF") { - switch (model_[0]) { - case 1: // LBX model: setting the modulation off - querySlot = slot_; - newCommand.assign("TTL 0"); - break; - case 2: // LCX model: setting the modulation off - querySlot = NO_SLOT; - switch (model_[1]) { - case 1: - newCommand.assign("AOM1 TTL 0"); - break; - case 2: - newCommand.assign("AOM2 TTL 0"); - break; - default: - newCommand.assign(""); - } - break; - default: // Should not happen - return DEVICE_OK; - } - } - - RETURN_ON_MM_ERROR( parentHub_->QueryCommand(this, GetCoreCallback(), querySlot, newCommand.c_str(), false) ); - } - return DEVICE_OK; -} - - -int OxxiusLaserBoxx::OnAnalogMod(MM::PropertyBase* pProp, MM::ActionType eAct) -{ - unsigned int querySlot = NO_SLOT; - - if (eAct == MM::BeforeGet) { - std::string query; - - // The slot and command depend on the model - switch (model_[0]) { - case 1: // LBX model: retreiving the modulation state - querySlot = slot_; - query = "?AM"; - break; - case 2: // LCX model: retreiving the modulation state - querySlot = NO_SLOT; - switch (model_[1]) { - case 1: - query = "AOM1 AM"; - break; - case 2: - query = "AOM2 AM"; - break; - default: - query = ""; - } - break; - default: // Should not happen - return DEVICE_OK; - } - - RETURN_ON_MM_ERROR( parentHub_->QueryCommand(this, GetCoreCallback(), querySlot, query.c_str(), false) ); - bool digiM; - parentHub_->ParseforBoolean(digiM); - - if (digiM) { - digitalMod_.assign("ON"); - } else { - digitalMod_.assign("OFF"); - } - pProp->Set(digitalMod_.c_str()); - - } else if (eAct == MM::AfterSet) { - std::ostringstream newCommand; - - - - switch (model_[0]) { - case 1: // LBX model: setting the modulation on - querySlot = slot_; - newCommand << "AM "; - break; - case 2: // LCX model: setting the modulation on - querySlot = NO_SLOT; - switch (model_[1]) { - case 1: - case 2: - newCommand << "AOM" << model_[1] << " AM "; - break; - default: // No modulation without a modulator - return DEVICE_OK; - } - break; - default: // Should not happen - return DEVICE_OK; - } - - pProp->Get(digitalMod_); - - if (digitalMod_ == "OFF") - newCommand << "0"; - else - newCommand << "1"; - - RETURN_ON_MM_ERROR( parentHub_->QueryCommand(this, GetCoreCallback(), querySlot, newCommand.str().c_str(), false) ); - } - return DEVICE_OK; -} - - -int OxxiusLaserBoxx::OnControlMode(MM::PropertyBase* pProp, MM::ActionType eAct) -{ - if (eAct == MM::BeforeGet) { - unsigned int ctrlM = 1; - std::string command; - - // The slot and command depend on the model - switch (model_[0]) { - case 1: // LBX model - command = "?APC"; - - RETURN_ON_MM_ERROR( parentHub_->QueryCommand(this, GetCoreCallback(), slot_, command.c_str(), false)); - parentHub_->ParseforInteger(ctrlM); - - break; - - case 2: // LCX model -> always in APC - ctrlM = 1; - break; - - default:; // Should not happen - } - - if (ctrlM == 1) { - controlMode_.assign("APC"); - } else if (ctrlM == 0) { - controlMode_.assign("ACC"); - } - - pProp->Set(controlMode_.c_str()); - } - else if (eAct == MM::AfterSet) { - std::string newCommand; - - pProp->Get(controlMode_); - - switch (model_[0]) { - case 1: // LBX model - if (controlMode_ == "ACC") { - newCommand.assign("APC 0"); - } else if (controlMode_ == "APC") { - newCommand.assign("APC 1"); - } - - RETURN_ON_MM_ERROR( parentHub_->QueryCommand(this, GetCoreCallback(), slot_, newCommand.c_str(), false) ); - - break; - - case 2: // LCX model -> always in APC - controlMode_ = "APC"; - pProp->Set(controlMode_.c_str()); - break; - - default:; // Should not happen - } - } - return DEVICE_OK; -} - - -int OxxiusLaserBoxx::OnPowerSetPoint(MM::PropertyBase* pProp, MM::ActionType eAct) -{ - if (eAct == MM::BeforeGet) { - std::string command = "?SP"; - unsigned int thisSlot = slot_; - float absSetPoint_; - - if ((10 < model_[1]) && (model_[1] < 17)) { - thisSlot = NO_SLOT; - command = "?PL"; - stringstream s; - s << (model_[1] - 10); - command += s.str(); - } - else if (model_[0] == 2) { - switch (model_[1]) { - case 1: // LCX on AOM1 - thisSlot = NO_SLOT; - command = "?SP1"; - break; - - case 2: // LCX on AOM2 - thisSlot = NO_SLOT; - command = "?SP2"; - break; - default: // LCX without AOM: identical to LBX polling - break; - } - } - - RETURN_ON_MM_ERROR( parentHub_->QueryCommand(this, GetCoreCallback(), thisSlot, command.c_str(), false) ); - parentHub_->ParseforFloat(absSetPoint_); - - pProp->Set( (100 * absSetPoint_) / nominalPower_ ); - } - else if (eAct == MM::AfterSet) { - - double GUISetPoint = 0.0; - pProp->Get(GUISetPoint); - - if( (GUISetPoint >= 0.0)||(GUISetPoint <= maxRelPower_) ) { - std::string command = "P"; - unsigned int thisSlot = slot_; - - std::ostringstream newCommand; - char * powerSPString = new char[20]; - strcpy(powerSPString , CDeviceUtils::ConvertToString( (GUISetPoint * nominalPower_) / 100 )); - - if ((10 < model_[1]) && (model_[1] < 17)) { - thisSlot = NO_SLOT; - command = "IP"; - command += CDeviceUtils::ConvertToString((int)(model_[1] - 10)); - - strcpy(powerSPString , CDeviceUtils::ConvertToString(GUISetPoint) ); - } - else if (model_[0] == 2) { - switch (model_[1]) { - case 1: // LCX on AOM1 - thisSlot = NO_SLOT; - command = "P"; - break; - case 2: // LCX on AOM2 - thisSlot = NO_SLOT; - command = "P2"; - break; - case 5: // LCX with power adjustment - command = "IP"; - strcpy(powerSPString , CDeviceUtils::ConvertToString(GUISetPoint) ); - break; - default: - break; - } - } - newCommand << command << " " << powerSPString; - RETURN_ON_MM_ERROR( parentHub_->QueryCommand(this, GetCoreCallback(), thisSlot, newCommand.str().c_str(), false) ); - } else { - // If the value entered through the GUI is not valid, read the machine value - OnPowerSetPoint(pProp,MM::BeforeGet); - } - } - return DEVICE_OK; -} - - - -int OxxiusLaserBoxx::OnCurrentSetPoint(MM::PropertyBase* pProp, MM::ActionType eAct) -{ - if (eAct == MM::BeforeGet) { - float machineSetPoint = 0.0; - std::string command = "?SC"; - unsigned int thisSlot = slot_; - - if (model_[0] == 1) { // Current modification only allowed on LBX models - RETURN_ON_MM_ERROR( parentHub_->QueryCommand(this, GetCoreCallback(), thisSlot, command.c_str(), false) ); - parentHub_->ParseforFloat(machineSetPoint); - } - - pProp->Set( machineSetPoint ); - } - - else if (eAct == MM::AfterSet) { - - double GUISetPoint = 0.0; - pProp->Get(GUISetPoint); - - if( (GUISetPoint >= 0.0)||(GUISetPoint <= maxCurrent_) ) { - - std::ostringstream newCommand; - std::string command = "C"; - unsigned int thisSlot = slot_; - - char * currentSPString = new char[20]; - strcpy(currentSPString , CDeviceUtils::ConvertToString(GUISetPoint) ); - - if (model_[0] == 1) { // Current modification only allowed on LBX models - newCommand << command << " " << currentSPString; - RETURN_ON_MM_ERROR( parentHub_->QueryCommand(this, GetCoreCallback(), thisSlot, newCommand.str().c_str(), false) ); - } - } - } - return DEVICE_OK; -} - - - - -/////////////////////////////////////////////////////////////////////////////// -// -// Oxxius shutter implementation -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// -/////////////////////////////////////////////////////////////////////////////// - -OxxiusShutter::OxxiusShutter(const char* nameAndSlot) : initialized_(false) -{ - isOpen_ = false; - parentHub_ = 0; - - name_.assign(nameAndSlot); - - std::string strChnl = string(nameAndSlot); - strChnl = strChnl.substr(strChnl.length()-1, 1); - channel_ = (unsigned int) atoi(strChnl.c_str()); - - // Set property list - // ----------------- - // Name (read only) - CreateProperty(MM::g_Keyword_Name, name_.c_str(), MM::String, true); - - std::ostringstream shutterDesc; - shutterDesc << "Electro-mechanical shutter on channel " << channel_ << "."; - CreateProperty(MM::g_Keyword_Description, shutterDesc.str().c_str(), MM::String, true); - - InitializeDefaultErrorMessages(); - SetErrorText(ERR_NO_PORT_SET, "Hub Device not found. The Laser combiner is needed to create this device"); - - // parent ID display - CreateHubIDProperty(); -} - - -OxxiusShutter::~OxxiusShutter() -{ - Shutdown(); -} - - -int OxxiusShutter::Initialize() -{ - if (!initialized_) { - parentHub_ = static_cast(GetParentHub()); - if (!parentHub_ ) { - return DEVICE_COMM_HUB_MISSING; - } - char hubLabel[MM::MaxStrLength]; - parentHub_->GetLabel(hubLabel); - SetParentID(hubLabel); // for backward compatibility - - // Set property list - // ----------------- - - // Open/Close selector (write/read) - CPropertyAction* pAct = new CPropertyAction (this, &OxxiusShutter::OnState); - RETURN_ON_MM_ERROR( CreateProperty(MM::g_Keyword_State, "", MM::String, false, pAct) ); - AddAllowedValue(MM::g_Keyword_State, "Open"); - AddAllowedValue(MM::g_Keyword_State, "Closed"); - - // Closing the shutter on Initialization - RETURN_ON_MM_ERROR( SetProperty(MM::g_Keyword_State, "Closed") ); - - RETURN_ON_MM_ERROR( UpdateStatus() ); - - initialized_ = true; - } - - return DEVICE_OK; -} - - -int OxxiusShutter::Shutdown() -{ - initialized_ = false; - return DEVICE_OK; -} - - -void OxxiusShutter::GetName(char* Name) const -{ - CDeviceUtils::CopyLimitedString(Name, name_.c_str()); -} - - -bool OxxiusShutter::Busy() -{ - return false; -} - - -int OxxiusShutter::SetOpen(bool openCommand) -{ - if (openCommand) - return SetProperty(MM::g_Keyword_State, "Open"); - else - return SetProperty(MM::g_Keyword_State, "Closed"); -} - - -int OxxiusShutter::GetOpen(bool& isOpen) -{ - isOpen = isOpen_; - return DEVICE_OK; -} - - -int OxxiusShutter::OnState(MM::PropertyBase* pProp, MM::ActionType eAct) -{ - if (eAct == MM::BeforeGet) { - if (isOpen_) { - pProp->Set("Open"); - } else { - pProp->Set("Closed"); - } - } - else if (eAct == MM::AfterSet) { - - std::string newState = ""; - pProp->Get(newState); - - std::ostringstream newCommand; - newCommand << "SH" << channel_ << " "; - - if( newState.compare("Open") == 0 ) { - newCommand << "1"; - isOpen_ = true; - } else if ( newState.compare("Closed") == 0 ) { - newCommand << "0"; - isOpen_ = false; - } - - RETURN_ON_MM_ERROR( parentHub_->QueryCommand(this, GetCoreCallback(), NO_SLOT, newCommand.str().c_str(), false)); - } - return DEVICE_OK; -} - - - -/////////////////////////////////////////////////////////////////////////////// -// -// Oxxius M-Dual implementation -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// -/////////////////////////////////////////////////////////////////////////////// - -OxxiusMDual::OxxiusMDual(const char* nameAndSlot) : initialized_(false) -{ - parentHub_ = 0; - core_ = GetCoreCallback(); - - std::string tSlot = string(nameAndSlot); - name_.assign(tSlot); // sets MDual name - - slot_ = tSlot.substr(tSlot.length() - 1, 1); - - // Set property list - // ----------------- - // Name (read only) - CreateProperty(MM::g_Keyword_Name, name_.c_str(), MM::String, true); - - CreateProperty(MM::g_Keyword_Description, "M-Dual module", MM::String, true); - - InitializeDefaultErrorMessages(); - SetErrorText(ERR_NO_PORT_SET, "Hub Device not found. The Laser combiner is needed to create this device"); - - // parent ID display - CreateHubIDProperty(); -} - - -OxxiusMDual::~OxxiusMDual() -{ - Shutdown(); -} - - -int OxxiusMDual::Initialize() -{ - if (!initialized_) { - parentHub_ = static_cast(GetParentHub()); - if (!parentHub_ ) { - return DEVICE_COMM_HUB_MISSING; - } - char hubLabel[MM::MaxStrLength]; - parentHub_->GetLabel(hubLabel); - SetParentID(hubLabel); // for backward compatibility - - - // Set property list - // ----------------- - CPropertyAction* pAct = new CPropertyAction(this, &OxxiusMDual::OnSetRatio);//setting the possible positions - RETURN_ON_MM_ERROR(CreateProperty("Split ratio", "0", MM::Float, false, pAct)); - SetPropertyLimits("Split ratio", 0.0, 100.0); - - // Set property list - // ----------------- - // State - /*CPropertyAction* pAct = new CPropertyAction (this, &OxxiusMDual::OnState); - RETURN_ON_MM_ERROR( CreateProperty(MM::g_Keyword_State, "0", MM::Integer, false, pAct) ); - SetPropertyLimits("Set Position", 0, 100);*/ - - /*char pos[3]; - for (unsigned int i=0; iQueryCommand(this, GetCoreCallback(), NO_SLOT, command.str().c_str()) ); - parentHub_->ParseforInteger(currentPos); - - //SetPosition(currentPos); - pProp->Set((long)currentPos); - } - else if (eAct == MM::AfterSet) { - long newPosition = 0; - - //GetPosition(newPosition); - pProp->Get(newPosition); - - std::ostringstream newCommand; - newCommand << "IP" << slot_ << " " << newPosition; - - RETURN_ON_MM_ERROR( parentHub_->QueryCommand(this, GetCoreCallback(), NO_SLOT, newCommand.str().c_str()) ); - } - return DEVICE_OK; -}*/ - - -int OxxiusMDual::OnSetRatio(MM::PropertyBase* pProp, MM::ActionType eAct) -{ - if (eAct == MM::BeforeGet) { - double currentRatio = 0.0; - std::ostringstream command; - command << "IP" << slot_; - - RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), NO_SLOT, command.str().c_str(), true)); - parentHub_->ParseforPercent(currentRatio); - - pProp->Set(currentRatio); - } - - else if (eAct == MM::AfterSet) { - double newRatio = 0.0; - - pProp->Get(newRatio); - if( (newRatio >= 0.0) || (newRatio <= 100.0) ) { - std::ostringstream newCommand; - newCommand << "IP" << slot_ << " " << newRatio; - - RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), NO_SLOT, newCommand.str().c_str(), true)); - } - } - return DEVICE_OK; -} - -/////////////////////////////////////////////////////////////////////////////// -// -// Oxxius Flip-Mirror implementation -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// -/////////////////////////////////////////////////////////////////////////////// - -OxxiusFlipMirror::OxxiusFlipMirror(const char* nameAndSlot) : initialized_(false) -{ - parentHub_ = 0; - core_ = GetCoreCallback(); - - std::string fSlot = string(nameAndSlot); - nameF_.assign(fSlot);// set laser name - fSlot = fSlot.substr(fSlot.length() - 1, 1); - slot_ = (unsigned int)atoi(fSlot.c_str());// set laser slot - - // Set property list ///////////////////////////////////////////////////////////////////////////////////////////////////// NOT WORKING? (duplicate property name Name(4)) - // ----------------- - // Name (read only) - /*CreateProperty(MM::g_Keyword_Name, nameF_.c_str(), MM::String, true); - - CreateProperty(MM::g_Keyword_Description, "Flip-Mirror module", MM::String, true); - - InitializeDefaultErrorMessages(); - SetErrorText(ERR_NO_PORT_SET, "Hub Device not found. The Laser combiner is needed to create this device"); - - // parent ID display - CreateHubIDProperty();*/ -} - - -OxxiusFlipMirror::~OxxiusFlipMirror() -{ - Shutdown(); -} - - -int OxxiusFlipMirror::Initialize() -{ - if (!initialized_) { - parentHub_ = static_cast(GetParentHub()); - if (!parentHub_) { - return DEVICE_COMM_HUB_MISSING; - } - char hubLabel[MM::MaxStrLength]; - parentHub_->GetLabel(hubLabel); - SetParentID(hubLabel); // for backward compatibility - - CPropertyAction* pAct = new CPropertyAction(this, &OxxiusFlipMirror::OnSwitchPos);//setting the possible positions - RETURN_ON_MM_ERROR(CreateProperty("Switch Position", "0", MM::Integer, false, pAct)); - SetPropertyLimits("Switch Position", 0, 1); - - std::ostringstream descriPt2; - descriPt2 << ""; - RETURN_ON_MM_ERROR(CreateProperty(MM::g_Keyword_Description, descriPt2.str().c_str(), MM::String, true)); - - // Gate, or "closed" position -// RETURN_ON_MM_ERROR( CreateProperty(MM::g_Keyword_Closed_Position, "0", MM::String, false) ); - -// isOpen_ = false; // MDual closed posisiton is - - RETURN_ON_MM_ERROR(UpdateStatus()); - - initialized_ = true; - } - - return DEVICE_OK; -} - - -int OxxiusFlipMirror::Shutdown() -{ - initialized_ = false; - return DEVICE_OK; -} - - -void OxxiusFlipMirror::GetName(char* Name) const -{ - CDeviceUtils::CopyLimitedString(Name, nameF_.c_str()); -} - - -bool OxxiusFlipMirror::Busy() -{ - return false; -} - - -int OxxiusFlipMirror::OnSwitchPos(MM::PropertyBase* pProp, MM::ActionType eAct) -{ - if (eAct == MM::BeforeGet) { - unsigned int currentPos = 0; - std::ostringstream command; - command << "FM" << slot_; - - RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), NO_SLOT, command.str().c_str(), false)); - parentHub_->ParseforInteger(currentPos); - - //SetPosition(currentPos); - pProp->Set((long)currentPos); - } - else if (eAct == MM::AfterSet) { - long newPosition = 0; - - //GetPosition(newPosition); - pProp->Get(newPosition); - - std::ostringstream newCommand; - newCommand << "FM" << slot_ << " " << newPosition; - - RETURN_ON_MM_ERROR(parentHub_->QueryCommand(this, GetCoreCallback(), NO_SLOT, newCommand.str().c_str(), false)); - } - return DEVICE_OK; -} diff --git a/DeviceAdapters/OxxiusCombiner/Oxxius_combiner.h b/DeviceAdapters/OxxiusCombiner/Oxxius_combiner.h deleted file mode 100644 index e31480a1a..000000000 --- a/DeviceAdapters/OxxiusCombiner/Oxxius_combiner.h +++ /dev/null @@ -1,349 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// FILE: OxxiusCombiner.h -// PROJECT: Micro-Manager -// SUBSYSTEM: DeviceAdapters -//----------------------------------------------------------------------------- -// DESCRIPTION: Controls Oxxius combiners through a serial interface -// COPYRIGHT: Oxxius SA, 2013-2018 -// LICENSE: LGPL -// AUTHOR: Tristan Martinez -// - -#ifndef _OXXIUS_COMBINER_H_ -#define _OXXIUS_COMBINER_H_ - -#include "MMDevice.h" -#include "DeviceBase.h" -#include "ModuleInterface.h" -#include -#include -#include - - -////////////////////////////////////////////////////////////////////////////// -// Error codes -// -#define ERR_PORT_CHANGE_FORBIDDEN 101 -#define ERR_NO_PORT_SET 102 -#define ERR_COMBINER_NOT_FOUND 201 -#define ERR_UNSUPPORTED_VERSION 202 - -////////////////////////////////////////////////////////////////////////////// -// Miscellaneous definitions -// -// Use the name 'return_value' that is unlikely to appear within 'result'. -#define RETURN_ON_MM_ERROR( result ) do { \ - int return_value = (result); \ - if (return_value != DEVICE_OK) { \ - return return_value; \ - } \ -} while (0) - - -////////////////////////////////////////////////////////////////////////////// -// Defining device adaptaters -// - -class OxxiusCombinerHub: public HubBase -{ -public: - OxxiusCombinerHub(); - ~OxxiusCombinerHub(); - - // MMDevice API - // ------------ - int Initialize(); - int Shutdown(); - - void GetName(char* pszName) const; - bool Busy(); - int DetectInstalledDevices(); - unsigned int GetNumberOfInstalledDevices() {return installedDevices_;}; -// MM::DeviceDetectionStatus DetectDevice(void); - bool SupportsDeviceDetection(void) {return true;}; - - - // Property handlers - int OnPort(MM::PropertyBase* pPropt, MM::ActionType eAct); - int OnSerialNumber(MM::PropertyBase* pPropt, MM::ActionType eAct); - int OnInterlock(MM::PropertyBase* pPropt, MM::ActionType eAct); - int OnEmissionKey(MM::PropertyBase* pPropt, MM::ActionType eAct); - - // Custom interface for child devices -// bool IsPortAvailable() {return portAvailable_;} - int QueryCommand(MM::Device* device, MM::Core* core, const unsigned int destinationSlot, const char* command, bool adco); - int ParseforBoolean(bool &destBoolean); - int ParseforFloat(float &destFloat); - int ParseforInteger(unsigned int &destInteger); - int ParseforString(std::string &destString); - int ParseforVersion(unsigned int &Vval); - int ParseforPercent(double &Pval); - int ParseforChar(char* Nval); - // int TempAdminInt(const char* com); - // int TempAdminString(int com, std::string &res); - - bool GetAOMpos1(unsigned int slot); - bool GetAOMpos2(unsigned int slot); - bool GetMPA(unsigned int slot); - int GetObPos(); - -private: - void LogError(int id, MM::Device* device, MM::Core* core, const char* functionName); - - std::string port_; - bool initialized_; - unsigned int installedDevices_; - - std::string serialAnswer_; - - std::string serialNumber_; - bool interlockClosed_; - bool keyActivated_; - - unsigned int AOM1pos_; - unsigned int AOM2pos_; - unsigned int mpa[7]; - unsigned int obPos_; -}; - -////////////////////////////////////////////////////////////////////////////// -// -// Device adaptaters for "LaserBoxx" source -// -////////////////////////////////////////////////////////////////////////////// - -class OxxiusLaserBoxx: public CShutterBase -{ -public: - OxxiusLaserBoxx(const char* nameAndSlot); - ~OxxiusLaserBoxx(); - - // MMDevice API - // ------------ - int Initialize(); - int Shutdown(); - - void GetName(char* pszName) const; - bool Busy(); - - int SetOpen(bool openCommand = true); - int GetOpen(bool& isOpen); - int Fire(double /*deltaT*/) { return DEVICE_UNSUPPORTED_COMMAND; } -// int LaserOnOff(int); - - // Action interface - // ---------------- -// int OnPower(MM::PropertyBase* pProp, MM::ActionType eAct); -// int OnCurrent(MM::PropertyBase* pProp, MM::ActionType eAct); - int OnPowerSetPoint(MM::PropertyBase* pProp, MM::ActionType eAct); - int OnCurrentSetPoint(MM::PropertyBase* pProp, MM::ActionType eAct); - int OnEmissionOnOff(MM::PropertyBase* pProp, MM::ActionType eAct); - - int OnControlMode(MM::PropertyBase* pProp, MM::ActionType eAct); - int OnAnalogMod(MM::PropertyBase* pProp, MM::ActionType eAct); - int OnDigitalMod(MM::PropertyBase* pProp, MM::ActionType eAct); - - int OnState(MM::PropertyBase* pProp, MM::ActionType eAct); - int OnAlarm(MM::PropertyBase* pProp, MM::ActionType eAct); -// int OnHours(MM::PropertyBase* pProp, MM::ActionType eAct); - - unsigned int aomp1; - unsigned int aomp2; - -private: - std::string name_; - unsigned int slot_; - unsigned int model_[2]; - OxxiusCombinerHub* parentHub_; - bool initialized_; - bool busy_; - - // double powerSetPoint_; - // double currentSetPoint_; - float maxRelPower_; - float nominalPower_; - float maxCurrent_; - unsigned int waveLength; - std::string type; - - - bool laserOn_; - std::string state_; - std::string alarm_; -// std::string serialNumber_; -// std::string softVersion_; - std::string controlMode_; - std::string analogMod_; - std::string digitalMod_; -}; - -////////////////////////////////////////////////////////////////////////////// -// -// Device adaptaters for "Obis" source in Combiner -// -////////////////////////////////////////////////////////////////////////////// -/* -class OxxiusObisSupport : public CShutterBase -{ -public: - OxxiusObisSupport(const char* nameAndSlot); - ~OxxiusObisSupport(); - - // MMDevice API - // ------------ - int Initialize(); - int Shutdown(); - - void GetName(char* pszName) const; - bool Busy(); - - int SetOpen(bool openCommand = true); - int GetOpen(bool& isOpen); - int Fire(double ) { return DEVICE_UNSUPPORTED_COMMAND; } - // int LaserOnOff(int); - - // Action interface - // ---------------- - // int OnPower(MM::PropertyBase* pProp, MM::ActionType eAct); - // int OnCurrent(MM::PropertyBase* pProp, MM::ActionType eAct); - int OnPowerSetPoint(MM::PropertyBase* pProp, MM::ActionType eAct); - int OnCurrentSetPoint(MM::PropertyBase* pProp, MM::ActionType eAct); - int OnEmissionOnOff(MM::PropertyBase* pProp, MM::ActionType eAct); - - int OnControlMode(MM::PropertyBase* pProp, MM::ActionType eAct); - int OnAnalogMod(MM::PropertyBase* pProp, MM::ActionType eAct); - int OnDigitalMod(MM::PropertyBase* pProp, MM::ActionType eAct); - - int OnState(MM::PropertyBase* pProp, MM::ActionType eAct); - int OnAlarm(MM::PropertyBase* pProp, MM::ActionType eAct); - // int OnHours(MM::PropertyBase* pProp, MM::ActionType eAct); - - unsigned int aomp1; - unsigned int aomp2; - -private: - std::string name_; - unsigned int slot_; - unsigned int model_[2]; - OxxiusCombinerHub* parentHub_; - bool initialized_; - bool busy_; - - double powerSetPoint_; - double currentSetPoint_; - unsigned int maxPower_; - unsigned int waveLength; - std::string type; - // double maxCurrent_; - - - bool laserOn_; - std::string state_; - std::string alarm_; - // std::string serialNumber_; - // std::string softVersion_; - std::string controlMode_; - std::string analogMod_; - std::string digitalMod_; -}; -*/ -////////////////////////////////////////////////////////////////////////////// -// -// Device adaptaters for "shutter" source in Combiner -// -////////////////////////////////////////////////////////////////////////////// - -class OxxiusShutter: public CShutterBase -{ -public: - OxxiusShutter(const char* nameAndChannel); - ~OxxiusShutter(); - - // MMDevice API - // ------------ - int Initialize(); - int Shutdown(); - - void GetName(char* pszName) const; - bool Busy(); - - // Shutter API - int SetOpen(bool openCommand = true); - int GetOpen(bool& isOpen); - int Fire(double /*deltaT*/) { return DEVICE_UNSUPPORTED_COMMAND; } - - // Action Interface - // ---------------- - int OnState(MM::PropertyBase* pProp, MM::ActionType eAct); - -private: - bool initialized_; - std::string name_; - - OxxiusCombinerHub* parentHub_; - bool isOpen_; - unsigned int channel_; - - MM::MMTime changedTime_; -}; - - -class OxxiusMDual: public CGenericBase -{ -public: - OxxiusMDual(const char* name); - ~OxxiusMDual(); - - // MMDevice API - // ------------ - int Initialize(); - int Shutdown(); - - void GetName(char* pszName) const; - bool Busy(); - - // Action Interface - // ---------------- - int OnSetRatio(MM::PropertyBase* pProp, MM::ActionType eAct); - -private: - bool initialized_; - std::string name_; - std::string slot_; - MM::Core* core_; - - OxxiusCombinerHub* parentHub_; -}; - - -class OxxiusFlipMirror : public CGenericBase -{ -public: - OxxiusFlipMirror(const char* name); - ~OxxiusFlipMirror(); - - // MMDevice API - // ------------ - int Initialize(); - int Shutdown(); - - void GetName(char* pszName) const; - bool Busy(); - //unsigned long GetNumberOfPositions()const { return numPos_; } - - // Action Interface - // ---------------- - int OnSwitchPos(MM::PropertyBase* pProp, MM::ActionType eAct); - - -private: - bool initialized_; - std::string nameF_; - unsigned int slot_; - MM::Core* core_; - - OxxiusCombinerHub* parentHub_; - - unsigned long numPos_; -}; -#endif // _OXXIUS_COMBINER_H_ \ No newline at end of file diff --git a/DeviceAdapters/OxxiusCombiner/license.txt b/DeviceAdapters/OxxiusCombiner/license.txt new file mode 100644 index 000000000..56b20e458 --- /dev/null +++ b/DeviceAdapters/OxxiusCombiner/license.txt @@ -0,0 +1,142 @@ +GNU Lesser General Public License + +Preamble + +The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. + +This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. + +When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. + +To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. + +For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. + +We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. + +To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. + +Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. + +Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. + +When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. + +We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. + +For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. + +In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. + +Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. + +The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. + + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". + +A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. + +The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) + +"Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. + +Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. + +1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. + +You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. + + (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) + + These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. + + Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. + + In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. + +3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. + +Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. + +This option is useful when you wish to copy part of the code of the Library into a program that is not a library. + +4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. + +If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. + +5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. + +However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. + +When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. + +If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) + +Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. + +6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. + +You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: + + a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. + + e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. + +For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. + +It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. + +7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. + + b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. + +8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. + +9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. + +10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. + +11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. + +This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. + +12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. + +13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. + +14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. + +NO WARRANTY + +15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/DeviceAdapters/PI/PI.cpp b/DeviceAdapters/PI/PI.cpp index 05dd70842..e56d5a5ec 100644 --- a/DeviceAdapters/PI/PI.cpp +++ b/DeviceAdapters/PI/PI.cpp @@ -26,7 +26,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "PI.h" #include diff --git a/DeviceAdapters/PICAM/PICAMUniversal.cpp b/DeviceAdapters/PICAM/PICAMUniversal.cpp index e80af29b5..eacade6f8 100644 --- a/DeviceAdapters/PICAM/PICAMUniversal.cpp +++ b/DeviceAdapters/PICAM/PICAMUniversal.cpp @@ -70,8 +70,6 @@ using namespace std; #define START_ONPROPERTY(name,action) #endif -#include "FixSnprintf.h" - // Number of references to this class int Universal::refCount_ = 0; bool Universal::PICAM_initialized_ = false; diff --git a/DeviceAdapters/PI_GCS/PI_GCS.cpp b/DeviceAdapters/PI_GCS/PI_GCS.cpp index efd95969e..08834c813 100644 --- a/DeviceAdapters/PI_GCS/PI_GCS.cpp +++ b/DeviceAdapters/PI_GCS/PI_GCS.cpp @@ -25,7 +25,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "PI_GCS.h" #include diff --git a/DeviceAdapters/ParallelPort/ParallelPort.cpp b/DeviceAdapters/ParallelPort/ParallelPort.cpp index afa6bd5e5..405e56162 100644 --- a/DeviceAdapters/ParallelPort/ParallelPort.cpp +++ b/DeviceAdapters/ParallelPort/ParallelPort.cpp @@ -32,7 +32,6 @@ #define WIN32_LEAN_AND_MEAN #include #endif -#include "FixSnprintf.h" #include "ParallelPort.h" #include "ModuleInterface.h" diff --git a/DeviceAdapters/Pecon/Pecon.cpp b/DeviceAdapters/Pecon/Pecon.cpp index f6c5ec215..30d59a91f 100644 --- a/DeviceAdapters/Pecon/Pecon.cpp +++ b/DeviceAdapters/Pecon/Pecon.cpp @@ -11,7 +11,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "Pecon.h" #include diff --git a/DeviceAdapters/Piezosystem_30DV50/Piezosystem_30DV50.cpp b/DeviceAdapters/Piezosystem_30DV50/Piezosystem_30DV50.cpp index 1a75607e7..e6a3694ed 100644 --- a/DeviceAdapters/Piezosystem_30DV50/Piezosystem_30DV50.cpp +++ b/DeviceAdapters/Piezosystem_30DV50/Piezosystem_30DV50.cpp @@ -35,7 +35,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "Piezosystem_30DV50.h" #include "ModuleInterface.h" diff --git a/DeviceAdapters/Piezosystem_NV120_1/Piezosystem_NV120_1.cpp b/DeviceAdapters/Piezosystem_NV120_1/Piezosystem_NV120_1.cpp index 4c588e3db..29385981a 100644 --- a/DeviceAdapters/Piezosystem_NV120_1/Piezosystem_NV120_1.cpp +++ b/DeviceAdapters/Piezosystem_NV120_1/Piezosystem_NV120_1.cpp @@ -34,7 +34,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "Piezosystem_NV120_1.h" #include "ModuleInterface.h" diff --git a/DeviceAdapters/Piezosystem_NV40_1/Piezosystem_NV40_1.cpp b/DeviceAdapters/Piezosystem_NV40_1/Piezosystem_NV40_1.cpp index 4c0cb3383..97b8430be 100644 --- a/DeviceAdapters/Piezosystem_NV40_1/Piezosystem_NV40_1.cpp +++ b/DeviceAdapters/Piezosystem_NV40_1/Piezosystem_NV40_1.cpp @@ -35,7 +35,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "Piezosystem_NV40_1.h" #include "ModuleInterface.h" diff --git a/DeviceAdapters/Piezosystem_NV40_3/Piezosystem_NV40_3.cpp b/DeviceAdapters/Piezosystem_NV40_3/Piezosystem_NV40_3.cpp index 671731a8e..7bb94ad3a 100644 --- a/DeviceAdapters/Piezosystem_NV40_3/Piezosystem_NV40_3.cpp +++ b/DeviceAdapters/Piezosystem_NV40_3/Piezosystem_NV40_3.cpp @@ -35,7 +35,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "Piezosystem_NV40_3.h" #include "ModuleInterface.h" diff --git a/DeviceAdapters/Piezosystem_dDrive/Piezosystem_dDrive.cpp b/DeviceAdapters/Piezosystem_dDrive/Piezosystem_dDrive.cpp index 36bd94ec0..a1a614f91 100755 --- a/DeviceAdapters/Piezosystem_dDrive/Piezosystem_dDrive.cpp +++ b/DeviceAdapters/Piezosystem_dDrive/Piezosystem_dDrive.cpp @@ -37,7 +37,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "Piezosystem_dDrive.h" #include "devicelist.h" diff --git a/DeviceAdapters/Piper/stdafx.h b/DeviceAdapters/Piper/stdafx.h index 7d318b4d9..4a2a84d2a 100755 --- a/DeviceAdapters/Piper/stdafx.h +++ b/DeviceAdapters/Piper/stdafx.h @@ -28,8 +28,6 @@ #include -#include "FixSnprintf.h" - #define __PIPER_API_EXPORT __declspec(dllimport) #include "PiperApiErrors.h" diff --git a/DeviceAdapters/PointGrey/PointGrey.cpp b/DeviceAdapters/PointGrey/PointGrey.cpp index ce7daf511..3cce8dd1d 100644 --- a/DeviceAdapters/PointGrey/PointGrey.cpp +++ b/DeviceAdapters/PointGrey/PointGrey.cpp @@ -262,8 +262,8 @@ int PointGrey::Initialize() // BE AWARE: This version number needs to be updated if/when MM is // linked against another PGR version - if (pVersion.major != 2 || pVersion.minor != 10 || pVersion.type != 3 || pVersion.build != 266) { - SetErrorText(ALLERRORS, "Flycapture2_v100.dll is not version 2.10.3.266. Micro-Manager works correctly only with that version"); + if (pVersion.major != 2 || pVersion.minor != 13 || pVersion.type != 3 || pVersion.build != 61) { + SetErrorText(ALLERRORS, "Flycapture2_v100.dll is not version 2.13.3.61. Micro-Manager works correctly only with that version"); return ALLERRORS; } diff --git a/DeviceAdapters/PrecisExcite/PrecisExcite.cpp b/DeviceAdapters/PrecisExcite/PrecisExcite.cpp index 150f4cebd..b81d11fcb 100644 --- a/DeviceAdapters/PrecisExcite/PrecisExcite.cpp +++ b/DeviceAdapters/PrecisExcite/PrecisExcite.cpp @@ -22,13 +22,9 @@ // CVS: // - - #ifdef WIN32 #include #endif -#include "FixSnprintf.h" - #include "MMDevice.h" #include "PrecisExcite.h" diff --git a/DeviceAdapters/PrincetonInstruments/PVCAMUniversal.cpp b/DeviceAdapters/PrincetonInstruments/PVCAMUniversal.cpp index c4fdd0c24..043e4fc2c 100644 --- a/DeviceAdapters/PrincetonInstruments/PVCAMUniversal.cpp +++ b/DeviceAdapters/PrincetonInstruments/PVCAMUniversal.cpp @@ -58,8 +58,6 @@ #include #include -#include "FixSnprintf.h" - using namespace std; unsigned Universal::refCount_ = 0; bool Universal::PVCAM_initialized_ = false; diff --git a/DeviceAdapters/Prior/Prior.cpp b/DeviceAdapters/Prior/Prior.cpp index d39998c19..7d2dd4042 100644 --- a/DeviceAdapters/Prior/Prior.cpp +++ b/DeviceAdapters/Prior/Prior.cpp @@ -25,7 +25,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "prior.h" #include diff --git a/DeviceAdapters/PriorLegacy/PriorLegacy.cpp b/DeviceAdapters/PriorLegacy/PriorLegacy.cpp index 34a623350..f36c725d0 100644 --- a/DeviceAdapters/PriorLegacy/PriorLegacy.cpp +++ b/DeviceAdapters/PriorLegacy/PriorLegacy.cpp @@ -30,7 +30,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "PriorLegacy.h" #include diff --git a/DeviceAdapters/PrizmatixDevice/PrizmatixMain.cpp b/DeviceAdapters/PrizmatixDevice/PrizmatixMain.cpp index d589e3dde..7edf3bcfe 100644 --- a/DeviceAdapters/PrizmatixDevice/PrizmatixMain.cpp +++ b/DeviceAdapters/PrizmatixDevice/PrizmatixMain.cpp @@ -3,8 +3,6 @@ #define WIN32_LEAN_AND_MEAN #include #endif -#include "FixSnprintf.h" - const char* g_DeviceNameHub = "prizmatix-Hub"; const char* g_DeviceOneLED = "Prizmatix Ctrl"; diff --git a/DeviceAdapters/Rapp/Rapp.cpp b/DeviceAdapters/Rapp/Rapp.cpp index ffe14d228..053237f09 100644 --- a/DeviceAdapters/Rapp/Rapp.cpp +++ b/DeviceAdapters/Rapp/Rapp.cpp @@ -29,7 +29,6 @@ #ifdef WIN32 #pragma warning(disable: 4355) #endif -#include "FixSnprintf.h" #include "Rapp.h" #include diff --git a/DeviceAdapters/Sapphire/Sapphire.cpp b/DeviceAdapters/Sapphire/Sapphire.cpp index f4c6acff7..91e27e903 100644 --- a/DeviceAdapters/Sapphire/Sapphire.cpp +++ b/DeviceAdapters/Sapphire/Sapphire.cpp @@ -22,13 +22,9 @@ // CVS: // - - #ifdef WIN32 #include #endif -#include "FixSnprintf.h" - #include "MMDevice.h" #include "Sapphire.h" diff --git a/DeviceAdapters/Scientifica/Scientifica.cpp b/DeviceAdapters/Scientifica/Scientifica.cpp index 0d86ca44d..504056e97 100644 --- a/DeviceAdapters/Scientifica/Scientifica.cpp +++ b/DeviceAdapters/Scientifica/Scientifica.cpp @@ -25,7 +25,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "Scientifica.h" #include diff --git a/DeviceAdapters/ScionCam/ScionCamera.cpp b/DeviceAdapters/ScionCam/ScionCamera.cpp index 5a70f2e2a..3befd8828 100644 --- a/DeviceAdapters/ScionCam/ScionCamera.cpp +++ b/DeviceAdapters/ScionCam/ScionCamera.cpp @@ -37,12 +37,10 @@ // CVS: $Id: ScionCamera.c,v 1.33 2009/08/19 22:40:57 nenad Exp $ // - #ifdef WIN32 #define WIN32_LEAN_AND_MEAN #include #endif -#include "FixSnprintf.h" #include "ScionCamera.h" #include diff --git a/DeviceAdapters/ScionCam/capture.cpp b/DeviceAdapters/ScionCam/capture.cpp index a48f088bf..24288a10e 100644 --- a/DeviceAdapters/ScionCam/capture.cpp +++ b/DeviceAdapters/ScionCam/capture.cpp @@ -41,7 +41,6 @@ #define WIN32_LEAN_AND_MEAN #include #endif -#include "FixSnprintf.h" #include "ScionCamera.h" #include diff --git a/DeviceAdapters/ScionCam/utilities.cpp b/DeviceAdapters/ScionCam/utilities.cpp index fdd4d7735..49c1c37ba 100644 --- a/DeviceAdapters/ScionCam/utilities.cpp +++ b/DeviceAdapters/ScionCam/utilities.cpp @@ -41,7 +41,6 @@ #define WIN32_LEAN_AND_MEAN #include #endif -#include "FixSnprintf.h" #include "ScionCamera.h" diff --git a/DeviceAdapters/Sensicam/Sensicam.cpp b/DeviceAdapters/Sensicam/Sensicam.cpp index 23d5c2840..9cb3a61f0 100644 --- a/DeviceAdapters/Sensicam/Sensicam.cpp +++ b/DeviceAdapters/Sensicam/Sensicam.cpp @@ -40,13 +40,9 @@ #include #include // Liisa: for ceil - - // temp #include "stdio.h" -#include "FixSnprintf.h" - using namespace std; CSensicam* CSensicam::m_pInstance = 0; diff --git a/DeviceAdapters/SmarActHCU-3D/SmarActHCU-3D.cpp b/DeviceAdapters/SmarActHCU-3D/SmarActHCU-3D.cpp index eb3c42bbf..1591601ec 100644 --- a/DeviceAdapters/SmarActHCU-3D/SmarActHCU-3D.cpp +++ b/DeviceAdapters/SmarActHCU-3D/SmarActHCU-3D.cpp @@ -14,7 +14,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "SmarActHCU-3D.h" #include diff --git a/DeviceAdapters/Spot/SpotCamera.cpp b/DeviceAdapters/Spot/SpotCamera.cpp index 4fa97f73d..31189a8f7 100644 --- a/DeviceAdapters/Spot/SpotCamera.cpp +++ b/DeviceAdapters/Spot/SpotCamera.cpp @@ -26,7 +26,6 @@ #define WIN32_LEAN_AND_MEAN #include #endif -#include "FixSnprintf.h" #include #include diff --git a/DeviceAdapters/Standa/Standa.cpp b/DeviceAdapters/Standa/Standa.cpp index 7e8945dcd..dd03ce114 100644 --- a/DeviceAdapters/Standa/Standa.cpp +++ b/DeviceAdapters/Standa/Standa.cpp @@ -23,7 +23,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "Standa.h" diff --git a/DeviceAdapters/SutterLambda/SutterLambda.cpp b/DeviceAdapters/SutterLambda/SutterLambda.cpp index 10d642495..e9bf2f1ca 100644 --- a/DeviceAdapters/SutterLambda/SutterLambda.cpp +++ b/DeviceAdapters/SutterLambda/SutterLambda.cpp @@ -22,7 +22,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "SutterLambda.h" #include diff --git a/DeviceAdapters/SutterLambda2/SutterLambda2.cpp b/DeviceAdapters/SutterLambda2/SutterLambda2.cpp index 0bd122f13..db91adb1f 100644 --- a/DeviceAdapters/SutterLambda2/SutterLambda2.cpp +++ b/DeviceAdapters/SutterLambda2/SutterLambda2.cpp @@ -24,7 +24,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "SutterHub.h" #include "SutterWheel.h" diff --git a/DeviceAdapters/SutterStage/SutterStage.cpp b/DeviceAdapters/SutterStage/SutterStage.cpp index fa92008f1..8a1c6fdfd 100644 --- a/DeviceAdapters/SutterStage/SutterStage.cpp +++ b/DeviceAdapters/SutterStage/SutterStage.cpp @@ -30,7 +30,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "SutterStage.h" #include "ModuleInterface.h" diff --git a/DeviceAdapters/TeesnySLM/TeensySLM.cpp b/DeviceAdapters/TeesnySLM/TeensySLM.cpp index 570843ea8..b7a7cbcf2 100644 --- a/DeviceAdapters/TeesnySLM/TeensySLM.cpp +++ b/DeviceAdapters/TeesnySLM/TeensySLM.cpp @@ -20,7 +20,6 @@ #ifdef WIN32 #define WIN32_LEAN_AND_MEAN #include - #define snprintf _snprintf #endif diff --git a/DeviceAdapters/Thorlabs/IntegratedFilterWheel.cpp b/DeviceAdapters/Thorlabs/IntegratedFilterWheel.cpp index e27793cc5..b8f604420 100644 --- a/DeviceAdapters/Thorlabs/IntegratedFilterWheel.cpp +++ b/DeviceAdapters/Thorlabs/IntegratedFilterWheel.cpp @@ -20,12 +20,10 @@ // AUTHOR: Nenad Amodaj, http://nenad.amodaj.com, 2011 // - #ifdef WIN32 #define WIN32_LEAN_AND_MEAN #include #endif -#include "FixSnprintf.h" // Prevent "unused variable" warnings. #ifdef __GNUC__ diff --git a/DeviceAdapters/Thorlabs/MotorZStage.cpp b/DeviceAdapters/Thorlabs/MotorZStage.cpp index ca507820c..ae75d702d 100644 --- a/DeviceAdapters/Thorlabs/MotorZStage.cpp +++ b/DeviceAdapters/Thorlabs/MotorZStage.cpp @@ -24,7 +24,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "MotorZStage.h" #include "Thorlabs.h" diff --git a/DeviceAdapters/Thorlabs/PiezoZStage.cpp b/DeviceAdapters/Thorlabs/PiezoZStage.cpp index 7b03103bb..ce476a33f 100644 --- a/DeviceAdapters/Thorlabs/PiezoZStage.cpp +++ b/DeviceAdapters/Thorlabs/PiezoZStage.cpp @@ -25,7 +25,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "PiezoZStage.h" #include "Thorlabs.h" diff --git a/DeviceAdapters/Thorlabs/Thorlabs.cpp b/DeviceAdapters/Thorlabs/Thorlabs.cpp index 2a25afa96..e6954e462 100644 --- a/DeviceAdapters/Thorlabs/Thorlabs.cpp +++ b/DeviceAdapters/Thorlabs/Thorlabs.cpp @@ -23,7 +23,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "Thorlabs.h" #include "XYStage.h" diff --git a/DeviceAdapters/ThorlabsAPTStage/ThorlabsAPTStage.cpp b/DeviceAdapters/ThorlabsAPTStage/ThorlabsAPTStage.cpp index bd0e7524d..9c0bea387 100644 --- a/DeviceAdapters/ThorlabsAPTStage/ThorlabsAPTStage.cpp +++ b/DeviceAdapters/ThorlabsAPTStage/ThorlabsAPTStage.cpp @@ -42,7 +42,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "ThorlabsAPTStage.h" #include "APTAPI.h" diff --git a/DeviceAdapters/ThorlabsDCxxxx/DCxxxx_Plugin.cpp b/DeviceAdapters/ThorlabsDCxxxx/DCxxxx_Plugin.cpp index e677fe2a9..7e7cbc66c 100644 --- a/DeviceAdapters/ThorlabsDCxxxx/DCxxxx_Plugin.cpp +++ b/DeviceAdapters/ThorlabsDCxxxx/DCxxxx_Plugin.cpp @@ -21,11 +21,9 @@ // AUTHOR: Olaf Wohlmann, owohlmann@thorlabs.com // - #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include #include #include "DCxxxx_Plugin.h" diff --git a/DeviceAdapters/ThorlabsFilterWheel/FilterWheel.cpp b/DeviceAdapters/ThorlabsFilterWheel/FilterWheel.cpp index bb169962a..8653ffa78 100644 --- a/DeviceAdapters/ThorlabsFilterWheel/FilterWheel.cpp +++ b/DeviceAdapters/ThorlabsFilterWheel/FilterWheel.cpp @@ -23,12 +23,10 @@ // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES. - #ifdef WIN32 #define WIN32_LEAN_AND_MEAN #include #endif -#include "FixSnprintf.h" #include "FilterWheel.h" #include diff --git a/DeviceAdapters/ThorlabsSC10/SC10.cpp b/DeviceAdapters/ThorlabsSC10/SC10.cpp index d1d7d44e7..88fb660eb 100644 --- a/DeviceAdapters/ThorlabsSC10/SC10.cpp +++ b/DeviceAdapters/ThorlabsSC10/SC10.cpp @@ -13,7 +13,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "SC10.h" #include diff --git a/DeviceAdapters/Thorlabs_ELL14/ELL14.cpp b/DeviceAdapters/Thorlabs_ELL14/ELL14.cpp index b100264c6..d105b98f4 100644 --- a/DeviceAdapters/Thorlabs_ELL14/ELL14.cpp +++ b/DeviceAdapters/Thorlabs_ELL14/ELL14.cpp @@ -13,7 +13,6 @@ #ifdef WIN32 #include -#define snprintf _snprintf #endif #include "ELL14.h" diff --git a/DeviceAdapters/Tofra/Tofra.cpp b/DeviceAdapters/Tofra/Tofra.cpp index 4a695d6ae..dadb39699 100644 --- a/DeviceAdapters/Tofra/Tofra.cpp +++ b/DeviceAdapters/Tofra/Tofra.cpp @@ -25,7 +25,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "Tofra.h" #include diff --git a/DeviceAdapters/TriggerScopeMM/TriggerScopeMM.cpp b/DeviceAdapters/TriggerScopeMM/TriggerScopeMM.cpp index d6159a141..0a9d5ba15 100644 --- a/DeviceAdapters/TriggerScopeMM/TriggerScopeMM.cpp +++ b/DeviceAdapters/TriggerScopeMM/TriggerScopeMM.cpp @@ -256,39 +256,39 @@ MM::DeviceDetectionStatus CTriggerScopeMMHub::DetectDevice(void) MMThreadGuard myLock(lock_); PurgeComPort(port_.c_str()); - std::string answer; - int ret = SendAndReceiveNoCheck("*", answer); - - if(answer.length() > 0) - { - size_t idx = answer.find("ARC TRIGGERSCOPE 16"); - if(idx!=string::npos) - { - if (answer.substr(answer.length() - 2, 2) == "MM") - { - result = MM::CanCommunicate; - } - } - } - - if( DEVICE_OK != ret ) - { - LogMessageCode(ret,true); - } - else - { - // to succeed must reach here.... - result = MM::CanCommunicate; - } - pS->Shutdown(); - // always restore the AnswerTimeout to the default - GetCoreCallback()->SetDeviceProperty(port_.c_str(), "AnswerTimeout", answerTO); +std::string answer; +int ret = SendAndReceiveNoCheck("*", answer); +if (answer.length() > 0) +{ + size_t idx = answer.find("ARC TRIGGERSCOPE 16"); + if (idx != string::npos) + { + if (answer.substr(answer.length() - 2, 2) == "MM") + { + result = MM::CanCommunicate; } } - catch(...) +} + +if (DEVICE_OK != ret) +{ + LogMessageCode(ret, true); +} +else +{ + // to succeed must reach here.... + result = MM::CanCommunicate; +} +pS->Shutdown(); +// always restore the AnswerTimeout to the default +GetCoreCallback()->SetDeviceProperty(port_.c_str(), "AnswerTimeout", answerTO); + + } + } + catch (...) { - LogMessage("Exception in DetectDevice!",false); + LogMessage("Exception in DetectDevice!", false); } return result; @@ -303,7 +303,7 @@ MM::DeviceDetectionStatus CTriggerScopeMMHub::DetectDevice(void) * Shutdown() was not called. */ CTriggerScopeMMHub::~CTriggerScopeMMHub(void) -{ +{ Shutdown(); delete pResourceLock_; } @@ -338,7 +338,7 @@ int CTriggerScopeMMHub::Initialize() ret = Purge(); if (ret != DEVICE_OK) return ret; - firmwareVer_ = 0.0; + firmwareVer_ = 0.0; std::string answer = ""; ret = SendAndReceiveNoCheck("*", answer); int attempts = 1; @@ -351,30 +351,42 @@ int CTriggerScopeMMHub::Initialize() if (ret != DEVICE_OK) return ret; - if(answer.length()>0) + if (answer.length() > 0) { + // first check if the device returned an error. This can happen + // when something else send bytes to the port before we did + size_t idx = answer.find("ERROR_UNKNOWN_COMMAND"); + if (idx != string::npos) { + ret = SendAndReceiveNoCheck("*", answer); + if (ret != DEVICE_OK) { + return ret; + } + } + } + + if (answer.length() > 0) { size_t idx = answer.find("ARC TRIGGERSCOPE 16"); - if(idx!=string::npos) + if (idx != string::npos) bTS16_ = true; idx = answer.find("ARC_LED 16"); - if (idx!=string::npos) + if (idx != string::npos) bTS16_ = true; idx = answer.find("ARC TRIGGERSCOPE"); - if (idx==string::npos) + if (idx == string::npos) idx = answer.find("ARC_LED"); - if (idx!=string::npos) + if (idx != string::npos) { idx = answer.find("v."); - if (idx!=string::npos) + if (idx != string::npos) firmwareVer_ = atof(&(answer.c_str()[idx+2])); else { idx = answer.find("v"); - if (idx!=string::npos) + if (idx != string::npos) firmwareVer_ = atof(&(answer.c_str()[idx+1])); } if (answer.substr(answer.length() - 2, 2) != "MM") diff --git a/DeviceAdapters/TriggerScopeMM/TriggerScopeMMTTL.cpp b/DeviceAdapters/TriggerScopeMM/TriggerScopeMMTTL.cpp index 2892be7ee..0942d47ea 100644 --- a/DeviceAdapters/TriggerScopeMM/TriggerScopeMMTTL.cpp +++ b/DeviceAdapters/TriggerScopeMM/TriggerScopeMMTTL.cpp @@ -58,7 +58,7 @@ CTriggerScopeMMTTL::CTriggerScopeMMTTL(uint8_t pinGroup) : void CTriggerScopeMMTTL::GetName(char* name) const { - if(pinGroup_ == 1) + if(pinGroup_ == 0) { CDeviceUtils::CopyLimitedString(name, g_TriggerScopeMMTTLDeviceName1); } @@ -136,12 +136,12 @@ int CTriggerScopeMMTTL::Initialize() for (long ttlNr = 1; ttlNr <= 8; ttlNr ++) { long pinNr = ttlNr + (pinGroup_ * 8l); - std::ostringstream os; + std::ostringstream oss; if (pinNr == 9) - os << "TTL-" << std::setfill('0') << std::setw(2) << pinNr; + oss << "TTL-" << std::setfill('0') << std::setw(2) << pinNr; else - os << "TTL-" << pinNr; - std::string propName = os.str(); + oss << "TTL-" << pinNr; + std::string propName = oss.str(); CPropertyActionEx* pActEx = new CPropertyActionEx(this, &CTriggerScopeMMTTL::OnTTL, ttlNr - 1); nRet = CreateProperty(propName.c_str(), "0", MM::Integer, false, pActEx, false); if (nRet != DEVICE_OK) @@ -309,8 +309,8 @@ int CTriggerScopeMMTTL::OnState(MM::PropertyBase* pProp, MM::ActionType eAct) int val = -1; for (unsigned int i=0; i < sequence.size(); i++) { - std::istringstream os (sequence[i]); - os >> val; + std::istringstream oss (sequence[i]); + oss >> val; if (val < 0 || val > 255) return ERR_INVALID_VALUE; } @@ -323,30 +323,30 @@ int CTriggerScopeMMTTL::OnState(MM::PropertyBase* pProp, MM::ActionType eAct) return ret; // then send the sequence - std::ostringstream cout; - cout << "PDO" << (int) pinGroup_ << "-0"; + std::ostringstream cmd; + cmd << "PDO" << (int) pinGroup_ << "-0"; for(std::vector::iterator it = sequence.begin(); it != sequence.end(); ++it) { - cout << "-" << *it; + cmd << "-" << *it; } - return pHub_->SendAndReceive(cout.str().c_str()); + return pHub_->SendAndReceive(cmd.str().c_str()); } else if (eAct == MM::StartSequence) { - std::ostringstream cout; - cout << "PDS" << (int) pinGroup_ << "-1-" << (int) sequenceTransitionOnRising_; + std::ostringstream cmd; + cmd << "PDS" << (int) pinGroup_ << "-1-" << (int) sequenceTransitionOnRising_; - return pHub_->SendAndReceive(cout.str().c_str()); + return pHub_->SendAndReceive(cmd.str().c_str()); } else if (eAct == MM::StopSequence) { - std::ostringstream cout; - cout << "PDS" << (int) pinGroup_ << "-0-" << (int) sequenceTransitionOnRising_; + std::ostringstream cmd; + cmd << "PDS" << (int) pinGroup_ << "-0-" << (int) sequenceTransitionOnRising_; - return pHub_->SendAndReceive(cout.str().c_str()); + return pHub_->SendAndReceive(cmd.str().c_str()); } return DEVICE_OK; diff --git a/DeviceAdapters/Vincent/Vincent.cpp b/DeviceAdapters/Vincent/Vincent.cpp index a50097208..e98732f84 100644 --- a/DeviceAdapters/Vincent/Vincent.cpp +++ b/DeviceAdapters/Vincent/Vincent.cpp @@ -23,7 +23,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "Vincent.h" #include diff --git a/DeviceAdapters/Vortran/Stradus.cpp b/DeviceAdapters/Vortran/Stradus.cpp index d468562ea..ede5292e5 100644 --- a/DeviceAdapters/Vortran/Stradus.cpp +++ b/DeviceAdapters/Vortran/Stradus.cpp @@ -24,7 +24,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "MMDevice.h" #include diff --git a/DeviceAdapters/Vortran/VersaLase.cpp b/DeviceAdapters/Vortran/VersaLase.cpp index c0ad29dd4..397af8c5a 100644 --- a/DeviceAdapters/Vortran/VersaLase.cpp +++ b/DeviceAdapters/Vortran/VersaLase.cpp @@ -51,7 +51,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "MMDevice.h" #include "DeviceBase.h" diff --git a/DeviceAdapters/WOSM/WOSM.cpp b/DeviceAdapters/WOSM/WOSM.cpp new file mode 100644 index 000000000..28d5e3548 --- /dev/null +++ b/DeviceAdapters/WOSM/WOSM.cpp @@ -0,0 +1,2548 @@ +/////////////////////////////////////////////////////////////////////////////// +// FILE: WOSM.cpp +// PROJECT: Micro-Manager +// SUBSYSTEM: DeviceAdapters +//----------------------------------------------------------------------------- +// DESCRIPTION: Adapter for Warwick Open-Source Microscope "WOSM" +// HTTPS://www.wosmic.org +// This device Adapter is a modded Arduino Device Adapter +// +// COPYRIGHT: University of Warwick, UK 2023 +// LICENSE: LGPL +// AUTHORS: Justin E. Molloy & Nicholas J. Carter +// +// +// +// Software is modified from Arduino Adapter written by: +// Copyright : University of California, San Francisco, 2008 +// Nico Stuurman, nico@cmp.ucsf.edu, 11/09/2008 +// automatic device detection by Karl Hoover +// +// This device Adapter also includes code modified from the PiezoConcept XYZ. +// +/////////////////////////////////////////////////////////////////////////////// +// +// The IP address of the WOSM controller is available on the WOSM LCD screen. +// e.g. "192.168.10.100" use port 23 or 1023 +// Upon connection via TCP/IP the WOSM reports its model ID and networking information +// and requests the user to enter an "admin password: " (default is "wosm") +// It then enters "command mode" signified by the prompt: +// +// W> +// +// ============================ +// WOSM Command-Set over-view: +// ============================ +// Here, we use a small sub-set of commands to control the WOSM +// The full command-set for the WOSM is at: http://wosmic.org/mcu/commands.php +// +// Hint: You can enter and test commands using PuTTy. +// +// System Commands: +// sys_ commands= time, date, usec, uptime etc.... +// example: W>sys_time ->returns system time in UTC format. +// +// Temperature Sensors: +// temp_ commands= ser, ref, val etc... +// example: W>temp_val stage ->temp (oC*256) of sensor called "stage" +// +// Addressable Serial LEDs: +// led_ commands= conf, buff, mask, bright, on +// example: W>led_buff 0xFF00FF 0x000000010 ->make LED number 9 purple +// +// Counters (2 * 16-bit counters on digital lines "q" & "r"): +// cnt_ commands= en, clr, val +// example: W>cnt_clr q ->clear counter on digtal line "q" +// +// KeyPad, Rotary encoder (monitor remote controller box - send messages to its screen): +// kyp_ commands= val, rot, lcd_txt +// examples: W>kyp_rot ->read rotary encoder clicks +// W>kyp_lcd_screen t=6s \"\n Micromanager\n Connected.\" +// +// Digital I/O lines (shutters, triggers, interlocks etc - 26 lines available ("a-z"): +// - 4 lines ("s,t,u,v") are used to gate the High-Current LED drivers (below) +// dig_ commands= in, out, mode, hilo, lohi etc.... +// example: W>dig_in f ->read T/F on digital line "f" (there are 26 lines "a-z") +// +// Analogue outputs 8 * 16-bit DAC lines (these lines are prefixed with "p" on the "P"ower daughter-board) +// - 4 lines ("ps, pt, pu, pv") have high-current MOSFET drivers (for e.g. LED lamps) +// - (Note: these outputs are gated by the dig lines "s,t,u,v") +// - 4 lines ("pw, px, py, pz") have low-current 16-bit DACs (for e.g. PZT stage control) +// dac_ commands= ref, out, rate, mode +// Note: The output lines are prefixed with "p" for power board and "m" for mother +// examples: W>dac_mode ps 3 ->line "s" as LED driver mode (3 = current mode) +// W>dac_out ps 12.5 ->set line s to 12.5mA output +// +// Motors, Steppers, Servos: +// (not clear how these motors interact with "Stage" and "Analogue" x,y,z +// mot_ commands= accl, bckl, dest, pos, out.. etc +// example: W>mot_out m3 27.5 ->moves motor to 27.5um absolute +// +// Stage control: +// (not clear how this works/interacts with pw,px,py,pz or with motor controls) +// stg_ command= out, val, min, max +// examples: W>stg_out_x r+22.345 ->move stage x-axis relative by 22.345um +// +// Serial Config (configure the serial ports SPI and RS232): +// ser_ commands: config, mode, send +// example: W>ser_config d on pdsel=2 stsel=0 ->enable serial bus "d", 8 data, even parity, 1 stop +// +// Networking: +// lan_ commands: host, config, ip, mac, subnet etc... +// examples: W>lan_mac ->get the lan MAC address. +// +// Macro commands: +// wml_ commands: run, stop, pause, file_new +// examples: W>wml_file_new col3z4 ->load a new macro command file +// W>wml_run col3z4 ->run the loaded macro command file +// User Management: +// usr_ commands: set, del, name +// example: W>usr_name 10 ->returns authenticated user name index 10 +// +// Note: all commands are "cr+lf" terminated +////////////////////////////////////////////////////////////////////////////////////// + +#include "WOSM.h" +#include "ModuleInterface.h" +#include + +//$$ string and IO handling +#include +#include +#include +using namespace std; +//$$ + +// Name the Devices something short and sensible +const char* g_DeviceNameWOSMHub = "WOSM-Hub"; +const char* g_DeviceNameWOSMSwitch = "WOSM-Switch"; +const char* g_DeviceNameWOSMShutter = "WOSM-Shutter"; +const char* g_DeviceNameWOSMDAC0 = "WOSM-DAC0"; +const char* g_DeviceNameWOSMDAC1 = "WOSM-DAC1"; +const char* g_DeviceNameWOSMDAC2 = "WOSM-DAC2"; +const char* g_DeviceNameWOSMDAC3 = "WOSM-DAC3"; +const char* g_DeviceNameWOSMDAC4 = "WOSM-DAC4"; +const char* g_DeviceNameStage = "ZStage"; +const char* g_DeviceNameXYStage = "XYStage"; +const char* g_DeviceNameWOSMInput = "WOSM-Input"; + +const char* g_PropertyMinUm = "Z Stage Low Posn(um)"; +const char* g_PropertyMaxUm = "Z Stage High Posn(um)"; +const char* g_PropertyXMinUm = "X Stage Min Posn(um)"; +const char* g_PropertyXMaxUm = "X Stage Max Posn(um)"; +const char* g_PropertyYMinUm = "Y Stage Min Posn(um)"; +const char* g_PropertyYMaxUm = "Y Stage Max Posn(um)"; + +// Global info about the state of the WOSM. This should be folded into a class +const int g_Min_MMVersion = 5; +const int g_Max_MMVersion = 100; +const char* g_versionProp = "Version"; +const char* g_normalLogicString = "Normal"; +const char* g_invertedLogicString = "Inverted"; + +const char* g_On = "On"; +const char* g_Off = "Off"; + +// static lock +MMThreadLock CWOSMHub::lock_; + +/////////////////////////////////////////////////////////////////////////////// +// Exported MMDevice API +/////////////////////////////////////////////////////////////////////////////// +MODULE_API void InitializeModuleData() +{ + // Justin's Notes: I think this is where the HUb and other devices are registered with the MMcore code + // I think there are currently ~16 recognised Device "types" including: + // Hub, StateDevice, ShutterDevice, GenericDevice, SignalIODevice, StageDevice(focus?), XYStageDevice, AutofocusDevice, GalvoDevice + // There seems to be some ambiguity/overlap in what the different Device types actually do.. and the shutter function is used to change + // the LED output pattern - which is not logical to me. + // Anyway, seems we call 'em what we like! + + RegisterDevice(g_DeviceNameWOSMHub, MM::HubDevice, "Hub (required)"); + + // Note: The "StateDevice" is used to "blank" the SignalIODevice by setting the output to zero. + RegisterDevice(g_DeviceNameWOSMSwitch, MM::StateDevice, "Switch on/off channels 0 to 10"); + RegisterDevice(g_DeviceNameWOSMShutter, MM::ShutterDevice, "Shutter"); + RegisterDevice(g_DeviceNameWOSMDAC0, MM::SignalIODevice, "DAC channel 0"); + RegisterDevice(g_DeviceNameWOSMDAC1, MM::SignalIODevice, "DAC channel 1"); + RegisterDevice(g_DeviceNameWOSMDAC2, MM::SignalIODevice, "DAC channel 2"); + RegisterDevice(g_DeviceNameWOSMDAC3, MM::SignalIODevice, "DAC channel 3"); + RegisterDevice(g_DeviceNameWOSMDAC4, MM::SignalIODevice, "DAC channel 4"); + RegisterDevice(g_DeviceNameStage, MM::StageDevice, "Z Stage"); + RegisterDevice(g_DeviceNameXYStage, MM::XYStageDevice, "XY Stage"); + RegisterDevice(g_DeviceNameWOSMInput, MM::GenericDevice, "ADC"); +} + +MODULE_API MM::Device* CreateDevice(const char* deviceName) +{ + // Justin's Notes: After registering the devices we now "create them" and give them another name. + // I admit I find it hard to keep track of the different names - especially when + // things get renamed again within the config file. Just keep your hair on and go + // with it! + + if ( deviceName == 0 ) + return 0; + + if ( strcmp(deviceName, g_DeviceNameWOSMHub) == 0 ) + { + return new CWOSMHub; + } + else if ( strcmp(deviceName, g_DeviceNameWOSMSwitch) == 0 ) + { + return new CWOSMSwitch; + } + else if ( strcmp(deviceName, g_DeviceNameWOSMShutter) == 0 ) + { + return new CWOSMShutter; + } + else if ( strcmp(deviceName, g_DeviceNameWOSMDAC0) == 0 ) + { + return new CWOSMDA(0); // channel 0 + } + else if ( strcmp(deviceName, g_DeviceNameWOSMDAC1) == 0 ) + { + return new CWOSMDA(1); // channel 1 + } + else if ( strcmp(deviceName, g_DeviceNameWOSMDAC2) == 0 ) + { + return new CWOSMDA(2); // channel 2 + } + else if ( strcmp(deviceName, g_DeviceNameWOSMDAC3) == 0 ) + { + return new CWOSMDA(3); // channel 3 + } + else if ( strcmp(deviceName, g_DeviceNameWOSMDAC4) == 0 ) + { + return new CWOSMDA(4); // channel 4 + } + else if ( strcmp(deviceName, g_DeviceNameStage) == 0 ) + { + return new CWOSMStage(); + } + else if ( strcmp(deviceName, g_DeviceNameXYStage) == 0 ) + { + return new CWOSMXYStage(); + } + else if ( strcmp(deviceName, g_DeviceNameWOSMInput) == 0 ) + { + return new CWOSMInput; + } + return 0; +} + +MODULE_API void DeleteDevice(MM::Device* pDevice) +{ + delete pDevice; +} + +/* CWOSMHUb implementation: + Justin's Notes: The WOSM HUB is the master "Device" it gives a convenient way to communicate with several + physical or "nominal" devices connected via a single cable to the PC. The hub heirarchy makes things + a little more complicated than adapters written for single, stand-alone, physical devices. + The WOSM controller runs four or five separate "MM_Devices". The firmware running on the WOSM can + control LED light sources, move an XY stage, a focusing device, open/close a physical shutter, + control a laser system etc. +*/ + +//CWOSMHUb implementation +CWOSMHub::CWOSMHub() : + initialized_(false), + switchState_(0), + shutterState_(0), + hasZStage_(false), + hasXYStage_(false) +{ + portAvailable_ = false; + invertedLogic_ = false; + timedOutputActive_ = false; + + + // we can guess what this does... + InitializeDefaultErrorMessages(); + SetErrorText(ERR_PORT_OPEN_FAILED, "Failed opening WOSM TCP/IP device"); + SetErrorText(ERR_WOSM_NOT_FOUND, "Did not find a WOSM with the correct firmware. Is the WOSM connected to this IP address?"); + SetErrorText(ERR_NO_PORT_SET, "Hub Device not found. The WOSM Hub device is needed to create this device"); + std::ostringstream errorText; + errorText << "The firmware version on the WOSM is not compatible with this adapter. Please use firmware version "; + errorText << g_Min_MMVersion << " to " << g_Max_MMVersion; + SetErrorText(ERR_VERSION_MISMATCH, errorText.str().c_str()); + + // It would be very helpful to know exactly what this does... + CPropertyAction* pAct = new CPropertyAction(this, &CWOSMHub::OnPort); + CreateProperty(MM::g_Keyword_Port, "Undefined", MM::String, false, pAct, true); + + // and this! + pAct = new CPropertyAction(this, &CWOSMHub::OnLogic); + CreateProperty("Logic", g_invertedLogicString, MM::String, false, pAct, true); + + AddAllowedValue("Logic", g_invertedLogicString); + AddAllowedValue("Logic", g_normalLogicString); +} + +CWOSMHub::~CWOSMHub() +{ + Shutdown(); +} + +void CWOSMHub::GetName(char* name) const +{ + CDeviceUtils::CopyLimitedString(name, g_DeviceNameWOSMHub); +} + +bool CWOSMHub::Busy() +{ + return false; +} + +/* The Hub sends 2 "commands" / "requests" to the WOSM controller. += > upon TCP / IP connection we must send the login password += > "wosm" Expected response is command prompt "W>" += > We confirm connection with "kyp_lcd_screen t=6s \"\n Micromanager\n connected.\"", crlf + Once connected...we get the XYZ stage ranges += > "U,0-2" "U"nderstand the XYZ stage ranges(X = 0, Y = 1, Z = 2) += > "U,0" expects WOSM response e.g. "U,200" ( X - axis range ) += > "U,2" expects WOSM response e.g. "U,100" ( Z - axis range ) += > if no response or range = 0..MM thinks there's no stage +*/ + +// Commands: "wosm" login, LCD screen message, set xyz modes & get xyz ranges. +int CWOSMHub::GetControllerVersion(int& version) +{ + + int ret = DEVICE_OK; + + LogMessage("Login to WOSM", false); + + // Flush the I/O buffer + PurgeComPort(port_.c_str()); + + ret = SendSerialCommand(port_.c_str(), " ", "\r\n"); + if ( ret != DEVICE_OK ) return ret; + LogMessage("SENT space", false); + + // wait for Password request.... + string answer; + ret = GetSerialAnswer(port_.c_str(), "admin password:", answer); + if ( ret != DEVICE_OK ) return ERR_WOSM_NOT_FOUND; +; + ret = SendSerialCommand(port_.c_str(), "wosm", "\r\n"); + ret = GetSerialAnswer(port_.c_str(), "W>", answer); + if ( ret != DEVICE_OK ) return ERR_WOSM_NOT_FOUND; + + ret = SendSerialCommand(port_.c_str(), "kyp_lcd_screen t = 5s \"\n MicroManager\n connected!\"", "\r\n"); + ret = GetSerialAnswer(port_.c_str(), "W>", answer); + if ( ret != DEVICE_OK ) return ret; + + //completed initialisation okay...from now on.. don't bother with comms error checking! + version = 99; + + // Below is what happens when you steal code from differnt places and can't be bothered to + // have a unified approach! + + // XYZ stage setup set DAC mode to voltage output (mode 2) (0-10V) + // Obtain the Stage range (should be 16 bits = 65535) + double travelX, travelY, travelZ; + ret = SetAxisMode(0); + ret = GetAxisInfo(0, travelX); + + ret = SetAxisMode(1); + ret = GetAxisInfo(1, travelY); + + ret = SetAxisMode(2); + ret = GetAxisInfo(2, travelZ); + + if ( ( travelX > 0 ) && ( travelY > 0 ) ) hasXYStage_ = true; + if ( travelZ > 0 ) hasZStage_ = true; + + // now initialise the LED DAC lines (ps->pv) to constant current mode (mode 3) + ret = SendSerialCommand(port_.c_str(), "dac_mode ps 3", "\r\n"); ret = GetSerialAnswer(port_.c_str(), "W>", answer); + ret = SendSerialCommand(port_.c_str(), "dac_mode pt 3", "\r\n"); ret = GetSerialAnswer(port_.c_str(), "W>", answer); + ret = SendSerialCommand(port_.c_str(), "dac_mode pu 3", "\r\n"); ret = GetSerialAnswer(port_.c_str(), "W>", answer); + ret = SendSerialCommand(port_.c_str(), "dac_mode pv 3", "\r\n"); ret = GetSerialAnswer(port_.c_str(), "W>", answer); + + // now initialise the LED DIG lines (s->v) for gating (mode 12) + ret = SendSerialCommand(port_.c_str(), "dig_mode s 12", "\r\n"); ret = GetSerialAnswer(port_.c_str(), "W>", answer); + ret = SendSerialCommand(port_.c_str(), "dig_mode t 12", "\r\n"); ret = GetSerialAnswer(port_.c_str(), "W>", answer); + ret = SendSerialCommand(port_.c_str(), "dig_mode u 12", "\r\n"); ret = GetSerialAnswer(port_.c_str(), "W>", answer); + ret = SendSerialCommand(port_.c_str(), "dig_mode v 12", "\r\n"); ret = GetSerialAnswer(port_.c_str(), "W>", answer); + + return ret; +} + +// Command: "dac_max" obtain stageRange for X, Y and Z axes +int CWOSMHub::GetAxisInfo(int axis, double& travel) +{ + if ( axis < 0 || axis > 2 ) return DEVICE_ERR; + + std::stringstream cmd; + cmd.clear(); + + switch ( axis ) { + case 0: + cmd << "dac_max px"; // X-axis + break; + case 1: + cmd << "dac_max py"; // Y-axis + break; + case 2: + cmd << "dac_max pz"; // Z-axis + break; + } + + // Flush the I/O buffer then send command + PurgeComPort(port_.c_str()); + int ret = SendSerialCommand(port_.c_str(), cmd.str().c_str(), "\r\n"); + if ( ret != DEVICE_OK ) return ret; + + std::string answer; + ret = GetSerialAnswer(port_.c_str(), "W>", answer); + if ( ret != DEVICE_OK ) return ERR_UNKNOWN_AXIS; + + std::stringstream ss(answer); + std::string trav; + getline(ss, trav, '\r'); + + std::stringstream sstravel(trav); + sstravel >> travel; + + travel = travel / 65535 * 100; // all rather pointless! we know its 16 bits and 100um! + + return DEVICE_OK; +} + +// Command: "dac_mode" make X, Y and Z axes all voltage out +int CWOSMHub::SetAxisMode(int axis) +{ + if ( axis < 0 || axis > 2 ) return DEVICE_ERR; + + std::stringstream cmd; + cmd.clear(); + + switch ( axis ) { + case 0: + cmd << "dac_mode px 2"; // X-axis + break; + case 1: + cmd << "dac_mode py 2"; // Y-axis + break; + case 2: + cmd << "dac_mode pz 2"; // Z-axis + break; + } + + // Flush the I/O buffer then send command + PurgeComPort(port_.c_str()); + int ret = SendSerialCommand(port_.c_str(), cmd.str().c_str(), "\r\n"); + if ( ret != DEVICE_OK ) return ret; + + std::string answer; + ret = GetSerialAnswer(port_.c_str(), "W>", answer); + if ( ret != DEVICE_OK ) return ERR_UNKNOWN_AXIS; + + return DEVICE_OK; +} +bool CWOSMHub::SupportsDeviceDetection(void) +{ + return true; +} + +MM::DeviceDetectionStatus CWOSMHub::DetectDevice(void) +{ + if ( initialized_ ) + return MM::CanCommunicate; + + // all conditions must be satisfied... + MM::DeviceDetectionStatus result = MM::Misconfigured; + char answerTO[MM::MaxStrLength]; + + try + { + std::string portLowerCase = port_; + for ( std::string::iterator its = portLowerCase.begin(); its != portLowerCase.end(); ++its ) + { + *its = ( char ) tolower(*its); + } + if ( 0 < portLowerCase.length() && 0 != portLowerCase.compare("undefined") && 0 != portLowerCase.compare("unknown") ) + { + result = MM::CanNotCommunicate; + + // record the default answer time out + GetCoreCallback()->GetDeviceProperty(port_.c_str(), "AnswerTimeout", answerTO); + + MM::Device* pS = GetCoreCallback()->GetDevice(this, port_.c_str()); + + pS->Initialize(); + + MMThreadGuard myLock(lock_); + PurgeComPort(port_.c_str()); + int v = 0; + int ret = GetControllerVersion(v); + + if ( DEVICE_OK != ret ) LogMessageCode(ret, false); + else result = MM::CanCommunicate; + + pS->Shutdown(); + + // always restore the AnswerTimeout to the default + GetCoreCallback()->SetDeviceProperty(port_.c_str(), "AnswerTimeout", answerTO); + + } + } + catch ( ... ) + { + LogMessage("Exception in DetectDevice!", false); + } + + return result; +} + +int CWOSMHub::Initialize() +{ + // Name + int ret = CreateProperty(MM::g_Keyword_Name, g_DeviceNameWOSMHub, MM::String, true); + if ( DEVICE_OK != ret ) + return ret; + + MMThreadGuard myLock(lock_); + + // Check that we have a controller: + PurgeComPort(port_.c_str()); + ret = GetControllerVersion(version_); + if ( DEVICE_OK != ret ) + return ret; + + if ( version_ < g_Min_MMVersion ) + return ERR_VERSION_MISMATCH; + + CPropertyAction* pAct = new CPropertyAction(this, &CWOSMHub::OnVersion); + std::ostringstream sversion; + sversion << version_; + CreateProperty(g_versionProp, sversion.str().c_str(), MM::Integer, true, pAct); + + ret = UpdateStatus(); + if ( ret != DEVICE_OK ) return ret; + + // turn off verbose serial debug messages + // GetCoreCallback()->SetDeviceProperty(port_.c_str(), "Verbose", "0"); + + initialized_ = true; + return DEVICE_OK; +} + +int CWOSMHub::DetectInstalledDevices() +{ + if ( MM::CanCommunicate == DetectDevice() ) + { + std::vector peripherals; + peripherals.clear(); + peripherals.push_back(g_DeviceNameWOSMSwitch); + peripherals.push_back(g_DeviceNameWOSMShutter); + peripherals.push_back(g_DeviceNameWOSMDAC0); + peripherals.push_back(g_DeviceNameWOSMDAC1); + peripherals.push_back(g_DeviceNameWOSMDAC2); + peripherals.push_back(g_DeviceNameWOSMDAC3); + peripherals.push_back(g_DeviceNameWOSMDAC4); + peripherals.push_back(g_DeviceNameStage); + peripherals.push_back(g_DeviceNameXYStage); + peripherals.push_back(g_DeviceNameWOSMInput); + + for ( size_t i = 0; i < peripherals.size(); i++ ) + { + MM::Device* pDev = ::CreateDevice(peripherals[i].c_str()); + if ( pDev ) + { + AddInstalledDevice(pDev); + } + } + } + + return DEVICE_OK; +} + +int CWOSMHub::Shutdown() +{ + initialized_ = false; + return DEVICE_OK; +} + +int CWOSMHub::OnPort(MM::PropertyBase* pProp, MM::ActionType pAct) +{ + if ( pAct == MM::BeforeGet ) + { + pProp->Set(port_.c_str()); + } + else if ( pAct == MM::AfterSet ) + { + pProp->Get(port_); + portAvailable_ = true; + } + return DEVICE_OK; +} + +int CWOSMHub::OnVersion(MM::PropertyBase* pProp, MM::ActionType pAct) +{ + if ( pAct == MM::BeforeGet ) + { + pProp->Set(( long ) version_); + } + return DEVICE_OK; +} + +int CWOSMHub::OnLogic(MM::PropertyBase* pProp, MM::ActionType pAct) +{ + if ( pAct == MM::BeforeGet ) + { + if ( invertedLogic_ ) + pProp->Set(g_invertedLogicString); + else + pProp->Set(g_normalLogicString); + } + else if ( pAct == MM::AfterSet ) + { + std::string logic; + pProp->Get(logic); + if ( logic.compare(g_invertedLogicString) == 0 ) + invertedLogic_ = true; + else invertedLogic_ = false; + } + return DEVICE_OK; +} + +/* CWOSMSwitch implementation: + Justin's Notes: + The "Switch Device" is registered with MM as a "State Device" + I don't know the full command set used by the config file it seems to + be documented by "example". + + Different switch states are attached to different "channels" - + That is done here, again in the config file, also in the WOSM firmware + and finally the physical wiring of the GPIO pins. +*/ + +// CWOSMSwitch implementation +CWOSMSwitch::CWOSMSwitch() : + nrPatternsUsed_(0), + currentDelay_(0), + sequenceOn_(false), + blanking_(false), + initialized_(false), + numPos_(12), + busy_(false) +{ + + InitializeDefaultErrorMessages(); + + // add custom error messages + SetErrorText(ERR_UNKNOWN_POSITION, "Invalid position (state) specified"); + SetErrorText(ERR_INITIALIZE_FAILED, "Initialization of the device failed"); + SetErrorText(ERR_WRITE_FAILED, "Failed to write data to the device"); + SetErrorText(ERR_CLOSE_FAILED, "Failed closing the device"); + SetErrorText(ERR_COMMUNICATION, "Error in communication with WOSM board"); + SetErrorText(ERR_NO_PORT_SET, "Hub Device not found. The WOSM Hub device is needed to create this device"); + + for ( unsigned int i = 0; i < NUMPATTERNS; i++ ) + pattern_[i] = 0; + + // Description + int ret = CreateProperty(MM::g_Keyword_Description, "WOSM digital output driver", MM::String, true); + assert(DEVICE_OK == ret); + + // Name + ret = CreateProperty(MM::g_Keyword_Name, g_DeviceNameWOSMSwitch, MM::String, true); + assert(DEVICE_OK == ret); + + // parent ID display + CreateHubIDProperty(); +} + +CWOSMSwitch::~CWOSMSwitch() +{ + Shutdown(); +} + +void CWOSMSwitch::GetName(char* name) const +{ + CDeviceUtils::CopyLimitedString(name, g_DeviceNameWOSMSwitch); +} + +int CWOSMSwitch::Initialize() +{ + CWOSMHub* hub = static_cast< CWOSMHub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) { + return ERR_NO_PORT_SET; + } + char hubLabel[MM::MaxStrLength]; + hub->GetLabel(hubLabel); + SetParentID(hubLabel); // for backward comp. + + // set property list + // ----------------- + + // In this version of the adpater we will control up to 8 switchable devices + // Using WOSM dig_out lines s,t,u,v,w,x,y,z (in this adapter we use x,y,z for stage control) + // .. so we might "blank" those lines (x,y,z) to prevent accidentally switching to zero! + // Anyway.... We will define up to 256 char labels for 256 options ! + // 0="0", 2="2"...... 255="255" + // you will see later "WriteToPort" method that we need to do some bit shifting to get the + // correct control-byte pattern (since in principle we have up to 26 control bits!! + // The ASCII labels are just the decimal numbers representing each bit-mapped switch pattern + // (see below for explanation) + + //Create the text labels that we will use in the config file + const int bufSize = 4; + char buf[bufSize]; + for ( long i = 0; i < 256; i++ ) { + snprintf(( char* ) buf, bufSize, "%d", ( unsigned ) i); + SetPositionLabel(i, buf); + } + + /* E.g. if you have different LED light sources powered by the different WOSM O/P pins + B, G, Y, R < colour of the LED + 3, 2, 1, 0 < bit position + 8, 4, 2, 1 < decimal number when set to "1" at that bit position + 1, 0, 0, 0, = "Blue only" requires this bit pattern = 16 Decimal + And if you want RGB simultaneously you send: + 1, 1, 0, 1 = 8+4+1 = 13 Decimal + To make this easier you list the options you want available in the Config file like this: + +*** config file: +Label,WOSM-Switch,0,All_OFF +Label,WOSM-Switch,1,Red +Label,WOSM-Switch,2,Yellow +Label,WOSM-Switch,4,Red +Label,WOSM-Switch,8,Blue +Label,WOSM-Switch,13,RedGreenBlue +ConfigGroup,Channel,Red_LED,WOSM-Switch,Label,Red +ConfigGroup,Channel,Yellow_LED,WOSM-Switch,Label,Yellow +ConfigGroup,Channel,Green_LED,WOSM-Switch,Label,Green +ConfigGroup,Channel,Blue_LED,WOSM-Switch,Label,Blue +ConfigGroup,Channel,Red_Yell_UV,WOSM-Switch,Label,RedGreenBlue +*** + + */ + + // State + CPropertyAction* pAct = new CPropertyAction(this, &CWOSMSwitch::OnState); + int nRet = CreateProperty(MM::g_Keyword_State, "0", MM::Integer, false, pAct); + if ( nRet != DEVICE_OK ) + return nRet; + SetPropertyLimits(MM::g_Keyword_State, 0, 256 - 1); + + // Label + pAct = new CPropertyAction(this, &CStateBase::OnLabel); + nRet = CreateProperty(MM::g_Keyword_Label, "", MM::String, false, pAct); + if ( nRet != DEVICE_OK ) + return nRet; + + pAct = new CPropertyAction(this, &CWOSMSwitch::OnSequence); + nRet = CreateProperty("Sequence", g_On, MM::String, false, pAct); + if ( nRet != DEVICE_OK ) + return nRet; + AddAllowedValue("Sequence", g_On); + AddAllowedValue("Sequence", g_Off); + + // Starts "blanking" mode: goal is to synchronize laser light with camera exposure + std::string blankMode = "Blanking Mode"; + pAct = new CPropertyAction(this, &CWOSMSwitch::OnBlanking); + nRet = CreateProperty(blankMode.c_str(), "Idle", MM::String, false, pAct); + if ( nRet != DEVICE_OK ) return nRet; + + AddAllowedValue(blankMode.c_str(), g_On); + AddAllowedValue(blankMode.c_str(), g_Off); + + // Blank on TTL high or low + pAct = new CPropertyAction(this, &CWOSMSwitch::OnBlankingTriggerDirection); + nRet = CreateProperty("Blank On", "Low", MM::String, false, pAct); + if ( nRet != DEVICE_OK ) + return nRet; + AddAllowedValue("Blank On", "Low"); + AddAllowedValue("Blank On", "High"); + + /* + // Some original comments: + // but SADLY, the code itself is commented out + // In fact, looks like a useful thing to include...To Do. + // //////////////////////////////////////////////////////////////// + // Starts producing timed digital output patterns + // Parameters that influence the pattern are 'Repeat Timed Pattern', 'Delay', 'State' + // where the latter two are manipulated with the Get and SetPattern functions + + std::string timedOutput = "Timed Output Mode"; + pAct = new CPropertyAction(this, &CWOSMSwitch::OnStartTimedOutput); + nRet = CreateProperty(timedOutput.c_str(), "Idle", MM::String, false, pAct); + if (nRet != DEVICE_OK) + return nRet; + AddAllowedValue(timedOutput.c_str(), "Stop"); + AddAllowedValue(timedOutput.c_str(), "Start"); + AddAllowedValue(timedOutput.c_str(), "Running"); + AddAllowedValue(timedOutput.c_str(), "Idle"); + + // Sets a delay (in ms) to be used in timed output mode + // This delay will be transferred to the WOSM using the Get and SetPattern commands + pAct = new CPropertyAction(this, &CWOSMSwitch::OnDelay); + nRet = CreateProperty("Delay (ms)", "0", MM::Integer, false, pAct); + if (nRet != DEVICE_OK) + return nRet; + SetPropertyLimits("Delay (ms)", 0, 65535); + + // Repeat the timed Pattern this many times: + pAct = new CPropertyAction(this, &CWOSMSwitch::OnRepeatTimedPattern); + nRet = CreateProperty("Repeat Timed Pattern", "0", MM::Integer, false, pAct); + if (nRet != DEVICE_OK) + return nRet; + SetPropertyLimits("Repeat Timed Pattern", 0, 255); + */ + + nRet = UpdateStatus(); + if ( nRet != DEVICE_OK ) return nRet; + + initialized_ = true; + + return DEVICE_OK; +} + +int CWOSMSwitch::Shutdown() +{ + initialized_ = false; + return DEVICE_OK; +} +// I am assuming when this function (method) is called the passed parameter "value" +// is the bitmap pattern that is to be written to the switches (if bit is 0 the output is turned off +// (electronically gated) if the bit is 1 then the output goes to it's preset value. + +// command: 1 now "dig_out" Set current digital output pattern - The "CWOSMShutter" NO LONGER uses this command! +int CWOSMSwitch::WriteToPort(long value) +{ + CWOSMHub* hub = static_cast< CWOSMHub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) return ERR_NO_PORT_SET; + + // Masking pattern for the WOSM dig_out lines + // ----------- H I G H W O R D ----------- ----------- L O W W O R D ----------- + // 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00 :: 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00 + // * * * * * * z y x w v u t s r q :: p o n m l k j i h g f e d c b a + // ^ ^ ^ ^ ^ ^ ^ ^ + // useful control lines + // ...... Mask bits ..... + // 0 0 0 1 1 0 1 0 = 26 Decimal (= turn on lines w,v & t, all others off!) + // Keep the low byte and shift up to the High Word position as shown above + // Note: to make double sure we don't interfere with other lines we mask our mask with 03FC::0000 + // (i.e. only modify digital lines 's->z') + + value = 255 & value; + value = value << 18; + + if ( hub->IsLogicInverted() ) value = ~value; + + MMThreadGuard myLock(hub->GetLock()); + hub->PurgeComPortH(); + int ret = DEVICE_OK; + char command[50]; + unsigned int leng; + + leng = snprintf(( char* ) command, 50, "dig_out %d 0x03FC0000\r\n", value); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + + // Purge the IO buffer in case WOSM has sent a response! + hub->PurgeComPortH(); + hub->SetTimedOutput(false); + + std::ostringstream os; + os << "Switch::WriteToPort Command= " << command; + LogMessage(os.str().c_str(), false); + + // wait for usual response from WOSM ("W>") + + MM::MMTime startTime = GetCurrentMMTime(); + unsigned long bytesRead = 0; + unsigned char answer[50]; + while ( ( bytesRead < 50 ) && ( ( GetCurrentMMTime() - startTime ).getMsec() < 250 ) ) { + unsigned long br = 0; + ret = hub->ReadFromComPortH(( unsigned char* ) answer + bytesRead, 1, br); + if ( answer[bytesRead] == '>' ) break; + bytesRead += br; + } + + answer[bytesRead] = 0; // string terminator + + hub->PurgeComPortH(); + if ( ret != DEVICE_OK ) return ret; + + //if ( answer[0] != 'W' ) return ERR_COMMUNICATION; + + return DEVICE_OK; +} + +// Commands: 5 & 6 now "P" and "N" = store new "P"atterns and "N"umber of patterns +int CWOSMSwitch::LoadSequence(unsigned size, unsigned char* seq) +{ + + std::ostringstream os; + os << "Switch::LoadSequence size= " << size << " Seq= " << seq; + LogMessage(os.str().c_str(), false); + + CWOSMHub* hub = static_cast< CWOSMHub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) + return ERR_NO_PORT_SET; + + // preamble for all port reads and writes + MMThreadGuard myLock(hub->GetLock()); + hub->PurgeComPortH(); + int ret = DEVICE_OK; + char command[50]; + unsigned int leng; + + for ( unsigned i = 0; i < size; i++ ) + { + unsigned char value = seq[i]; + + value = 255 & value; + if ( hub->IsLogicInverted() ) value = ~value; + + leng = snprintf(( char* ) command, 50, "P,%d,%d\r\n", i, value); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + } + + leng = snprintf(( char* ) command, 50, "N,%d\r\n", size); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + + // Purge the IO buffer in case WOSM has sent a response! + hub->SetTimedOutput(false); + hub->PurgeComPortH(); + + return DEVICE_OK; +} + +// Action handlers + +// Commands: 8 & 9 now "R" and "E" Run and End Trigger mode resp. +int CWOSMSwitch::OnState(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + + std::ostringstream os; + os << "Switch::OnState_"; + LogMessage(os.str().c_str(), false); + + CWOSMHub* hub = static_cast< CWOSMHub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) + return ERR_NO_PORT_SET; + + // Some comments here would be helpful + if ( eAct == MM::BeforeGet ) + { + // nothing to do, let the caller use cached property ??? + } + else if ( eAct == MM::AfterSet ) + { + // **** a comment here is essential !.. + // where are we getting the "pos" value from? + long pos; + pProp->Get(pos); + + // this is obscure - I can guess what it does.. but, I'm not going to say + hub->SetSwitchState(pos); + + if ( hub->GetShutterState() > 0 ) // I don't like this because of confusion with "shutterDevice" + os << "_Pos= " << pos << " WriteToPort(pos)?"; + LogMessage(os.str().c_str(), false); + return WriteToPort(pos); // I understand this! + } + else if ( eAct == MM::IsSequenceable ) + { + if ( sequenceOn_ ) + pProp->SetSequenceable(NUMPATTERNS); + else + pProp->SetSequenceable(0); + } + else if ( eAct == MM::AfterLoadSequence ) + { + std::vector sequence = pProp->GetSequence(); + + if ( sequence.size() > NUMPATTERNS ) return DEVICE_SEQUENCE_TOO_LARGE; + + unsigned char* seq = new unsigned char[sequence.size()]; + for ( unsigned int i = 0; i < sequence.size(); i++ ) + { + std::istringstream ios(sequence[i]); + int val; + ios >> val; + seq[i] = ( unsigned char ) val; + } + + int ret = LoadSequence(( unsigned ) sequence.size(), seq); + if ( ret != DEVICE_OK ) + return ret; + + delete[ ] seq; + } + else if ( eAct == MM::StartSequence ) + { + MMThreadGuard myLock(hub->GetLock()); + + // preamble for all port reads and writes + hub->PurgeComPortH(); + int ret = DEVICE_OK; + char command[50]; + unsigned int leng; + + leng = snprintf(( char* ) command, 50, "R\r\n"); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + + } + else if ( eAct == MM::StopSequence ) + { + MMThreadGuard myLock(hub->GetLock()); + + // preamble for all port reads and writes + hub->PurgeComPortH(); + int ret = DEVICE_OK; + char command[50]; + unsigned int leng; + + leng = snprintf(( char* ) command, 50, "E\r\n"); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + + // This code needs to be improved! + // We expect a reply like "E,23456\r\n" + + MM::MMTime startTime = GetCurrentMMTime(); + unsigned long bytesRead = 0; + unsigned char answer[50]; + + // Now chug through the I/O buffer, byte-by-byte and stop at cr or lf.... yuk! + while ( ( bytesRead < 50 ) && ( ( GetCurrentMMTime() - startTime ).getMsec() < 250 ) ) { + unsigned long br; + ret = hub->ReadFromComPortH(( unsigned char* ) answer + bytesRead, 1, br); + if ( answer[bytesRead] == '\r' ) break; + bytesRead += br; + } + + answer[bytesRead] = 0; // string terminator + + hub->SetTimedOutput(false); + hub->PurgeComPortH(); + if ( ret != DEVICE_OK ) return ret; + + int num; + char com[1]; + sscanf(( const char* ) answer, "%1s,%d", com, &num); + // should give com[0]='E' num = 23456 (or something!) + + if ( com[0] != 'E' ) return ERR_COMMUNICATION; + + os << "Sequence_Transitions: " << num; + LogMessage(os.str().c_str(), false); + } + + return DEVICE_OK; +} +int CWOSMSwitch::OnSequence(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if ( eAct == MM::BeforeGet ) + { + if ( sequenceOn_ ) + pProp->Set(g_On); + else + pProp->Set(g_Off); + } + else if ( eAct == MM::AfterSet ) + { + std::string state; + pProp->Get(state); + if ( state == g_On ) + sequenceOn_ = true; + else + sequenceOn_ = false; + } + + std::ostringstream os; + os << "Switch::OnSequence_SeqOn= " << sequenceOn_; + LogMessage(os.str().c_str(), false); + + + return DEVICE_OK; +} + +// Commands: 12 & 9 now = "G" and "E" +int CWOSMSwitch::OnStartTimedOutput(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + + std::ostringstream os; + os << "Switch::OnStartTimedOutput "; + LogMessage(os.str().c_str(), false); + + CWOSMHub* hub = static_cast< CWOSMHub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) + return ERR_NO_PORT_SET; + + if ( eAct == MM::BeforeGet ) { + if ( hub->IsTimedOutputActive() ) pProp->Set("Running"); + else pProp->Set("Idle"); + } + else if ( eAct == MM::AfterSet ) + { + MMThreadGuard myLock(hub->GetLock()); + + std::string prop; + pProp->Get(prop); + + if ( prop == "Start" ) { + // preamble for port read and write + hub->PurgeComPortH(); + int ret = DEVICE_OK; + char command[50]; + unsigned int leng; + + leng = snprintf(( char* ) command, 50, "G\r\n"); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + + hub->SetTimedOutput(true); // Check this *********** + + } + else { + // preamble for port read and write + hub->PurgeComPortH(); + int ret = DEVICE_OK; + char command[50]; + unsigned int leng; + leng = snprintf(( char* ) command, 50, "E\r\n"); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + + hub->SetTimedOutput(false); + } + } + + return DEVICE_OK; +} + +// Commands: 20 & 21 now = "B,1" "B,0" now Blanking on(1) or Blanking off (0) +int CWOSMSwitch::OnBlanking(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + std::ostringstream os; + os << "Switch::OnBlanking "; + LogMessage(os.str().c_str(), false); + + CWOSMHub* hub = static_cast< CWOSMHub* >( GetParentHub() ); + + if ( !hub || !hub->IsPortAvailable() ) return ERR_NO_PORT_SET; + + if ( eAct == MM::BeforeGet ) { + if ( blanking_ ) pProp->Set(g_On); + else pProp->Set(g_Off); + } + else if ( eAct == MM::AfterSet ) + { + MMThreadGuard myLock(hub->GetLock()); + + std::string prop; + pProp->Get(prop); + + if ( prop == g_On && !blanking_ ) { + hub->PurgeComPortH(); + int ret = DEVICE_OK; + char command[50]; + unsigned int leng; + + leng = snprintf(( char* ) command, 50, "B,1\r\n"); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + + blanking_ = true; + hub->SetTimedOutput(false); + LogMessage("Switched blanking on", false); + + } + else if ( prop == g_Off && blanking_ ) { + hub->PurgeComPortH(); + int ret = DEVICE_OK; + char command[50]; + unsigned int leng; + + leng = snprintf(( char* ) command, 50, "B,0\r\n"); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + + blanking_ = false; + hub->SetTimedOutput(false); + LogMessage("Switched blanking off", false); + } + } + return DEVICE_OK; +} + +// Command: 22 now = "F,0" or "F,1" Flip polarity of trigger signal +int CWOSMSwitch::OnBlankingTriggerDirection(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + + std::ostringstream os; + os << "Switch::OnBlankingTriggerDirection"; + LogMessage(os.str().c_str(), false); + + CWOSMHub* hub = static_cast< CWOSMHub* >( GetParentHub() ); + + if ( !hub || !hub->IsPortAvailable() ) return ERR_NO_PORT_SET; + + // Perhaps shorten to one line and clarify logic? + // Only execute if eAct has already been got and set. + + // if ((eAct != MM::BeforeGet) && (eAct == MM::AfterSet)) { + + if ( eAct == MM::BeforeGet ) { + // nothing to do, let the caller use cached property + } + else if ( eAct == MM::AfterSet ) + { + + MMThreadGuard myLock(hub->GetLock()); + + std::string direction; + pProp->Get(direction); + hub->PurgeComPortH(); + int ret = DEVICE_OK; + char command[50]; + unsigned int leng; + + int dir = 0; + if ( direction == "Low" ) dir = 1; + leng = snprintf(( char* ) command, 50, "F,%d\r\n", dir); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + + hub->SetTimedOutput(false); + } + return DEVICE_OK; +} +int CWOSMSwitch::OnDelay(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + std::ostringstream os; + os << "Switch::OnDelay "; + LogMessage(os.str().c_str(), false); + + if ( eAct == MM::BeforeGet ) { + pProp->Set(( long int ) currentDelay_); + } + else if ( eAct == MM::AfterSet ) + { + long prop; + pProp->Get(prop); + currentDelay_ = ( int ) prop; + } + + return DEVICE_OK; +} + +// Command: 11 now = "I" set number of "I"terations +int CWOSMSwitch::OnRepeatTimedPattern(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + std::ostringstream os; + os << "Switch::OnRepeatTimedPattern "; + LogMessage(os.str().c_str(), false); + + CWOSMHub* hub = static_cast< CWOSMHub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) return ERR_NO_PORT_SET; + + // Perhaps shorten and clarify logic to one line? + // if ((eAct != MM::BeforeGet) && (eAct == MM::AfterSet)) { + + if ( eAct == MM::BeforeGet ) { + } + else if ( eAct == MM::AfterSet ) + { + MMThreadGuard myLock(hub->GetLock()); + + long prop; + pProp->Get(prop); + + hub->PurgeComPortH(); + int ret = DEVICE_OK; + char command[50]; + unsigned int leng; + leng = snprintf(( char* ) command, 50, "I,%d\r\n", prop); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + + hub->SetTimedOutput(false); + } + + return DEVICE_OK; +} + +// CWOSMShutter implementation +// Justin Notes: I'm a bit mystified by the "shutter" and when it is called. Currently it issues a "Switch" +// command - I find that confusing. + +// CWOSMShutter implementation +CWOSMShutter::CWOSMShutter() : initialized_(false), name_(g_DeviceNameWOSMShutter) +{ + InitializeDefaultErrorMessages(); + EnableDelay(); + + SetErrorText(ERR_NO_PORT_SET, "Hub Device not found. The WOSM Hub device is needed to create this device"); + + // Name + int ret = CreateProperty(MM::g_Keyword_Name, g_DeviceNameWOSMShutter, MM::String, true); + assert(DEVICE_OK == ret); + + // Description + ret = CreateProperty(MM::g_Keyword_Description, "WOSM shutter driver", MM::String, true); + assert(DEVICE_OK == ret); + + // parent ID display + CreateHubIDProperty(); +} +CWOSMShutter::~CWOSMShutter() +{ + Shutdown(); +} +void CWOSMShutter::GetName(char* name) const +{ + CDeviceUtils::CopyLimitedString(name, g_DeviceNameWOSMShutter); +} +bool CWOSMShutter::Busy() +{ + MM::MMTime interval = GetCurrentMMTime() - changedTime_; + + return interval < MM::MMTime::fromMs(GetDelayMs()); +} +int CWOSMShutter::Initialize() +{ + CWOSMHub* hub = static_cast< CWOSMHub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) { + return ERR_NO_PORT_SET; + } + char hubLabel[MM::MaxStrLength]; + hub->GetLabel(hubLabel); + SetParentID(hubLabel); // for backward comp. + + // set property list + // ----------------- + + // removed comment from OnOff + // set shutter into the off state ???wot + // WriteToPort(0); + + // OnOff + CPropertyAction* pAct = new CPropertyAction(this, &CWOSMShutter::OnOnOff); + int ret = CreateProperty("OnOff", "0", MM::Integer, false, pAct); + if ( ret != DEVICE_OK ) return ret; + + std::vector vals; + vals.push_back("0"); + vals.push_back("1"); + ret = SetAllowedValues("OnOff", vals); + if ( ret != DEVICE_OK ) return ret; + + ret = UpdateStatus(); + if ( ret != DEVICE_OK ) return ret; + + changedTime_ = GetCurrentMMTime(); + initialized_ = true; + + return DEVICE_OK; +} +int CWOSMShutter::Shutdown() +{ + if ( initialized_ ) + { + initialized_ = false; + } + return DEVICE_OK; +} +int CWOSMShutter::SetOpen(bool open) +{ + std::ostringstream os; + os << "Shutter::SetOpen open= " << open; + LogMessage(os.str().c_str(), false); + + if ( open ) return SetProperty("OnOff", "1"); + else return SetProperty("OnOff", "0"); +} +int CWOSMShutter::GetOpen(bool& open) +{ + std::ostringstream os; + os << "Shutter::GetOpen open= " << open; + LogMessage(os.str().c_str(), false); + + char buf[MM::MaxStrLength]; + int ret = GetProperty("OnOff", buf); + if ( ret != DEVICE_OK ) return ret; + + long pos = atol(buf); + pos > 0 ? open = true : open = false; + + return DEVICE_OK; +} +int CWOSMShutter::Fire(double /*deltaT*/) +{ + return DEVICE_UNSUPPORTED_COMMAND; +} + +// e.g. "x0101100" turns off channels 0,1,5,7.. +// Note: channels 2,3,6 will switch on at their preset output level.. "shutter" is messing with the switches! +// must try to separate church and state. + +// Command: "dig_out r 0/1" Shutter open or closed on channel "r" +int CWOSMShutter::WriteToPort(long value) +{ + CWOSMHub* hub = static_cast< CWOSMHub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) + return ERR_NO_PORT_SET; + + MMThreadGuard myLock(hub->GetLock()); + + // Just keep the lowest bit - a single shutter it's either on or off (open or closed) + value = 255 & value; + value = value << 18; + + if ( hub->IsLogicInverted() ) value = ~value; + + hub->PurgeComPortH(); + int ret = DEVICE_OK; + // create command buffer + char command[50]; + unsigned int leng; + + leng = snprintf(( char* ) command, 50, "dig_out %d 0x3FC0000\r\n", value); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + + MM::MMTime startTime = GetCurrentMMTime(); + unsigned long bytesRead = 0; + unsigned char answer[50]; + while ( ( bytesRead < 50 ) && ( ( GetCurrentMMTime() - startTime ).getMsec() < 250 ) ) { + unsigned long br = 0; + ret = hub->ReadFromComPortH(( unsigned char* ) answer + bytesRead, 1, br); + if ( answer[bytesRead] == '>' ) break; + bytesRead += br; + } + answer[bytesRead] = 0; // string terminator + + hub->PurgeComPortH(); + if ( ret != DEVICE_OK ) return ret; + + //if ( answer[bytesRead-1] != 'W' ) return ERR_COMMUNICATION; + + hub->SetTimedOutput(false); + + std::ostringstream os; + os << "Shutter::WriteToPort Command= " << command; + LogMessage(os.str().c_str(), false); + + return DEVICE_OK; +} + +// I think this method should be part of the "Switch" Class and should call Switch::WritetoPort +// This method is called in an inapproriate manner during MDA.. need to sort it out +// OnOnOff should be moved to "Switch::" : it's called during MDA and operates the "switch" indirectly +// by issuing the "S" command + +// Action handlers - don't seem to work correctly at present. I've modded it so, it now fails! +int CWOSMShutter::OnOnOff(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + CWOSMHub* hub = static_cast< CWOSMHub* >( GetParentHub() ); + if ( eAct == MM::BeforeGet ) + { + // use cached state + pProp->Set(( long ) hub->GetShutterState()); + } + else if ( eAct == MM::AfterSet ) + { + long pos; + pProp->Get(pos); + int ret; + if ( pos == 0 ) + ret = WriteToPort(0); // Shutter closed (write zeros to all LEDs) + else + ret = WriteToPort(hub->GetSwitchState()); // restore old setting + + if ( ret != DEVICE_OK ) return ret; + + hub->SetShutterState(pos); + changedTime_ = GetCurrentMMTime(); + } + + std::ostringstream os; + os << "Shutter::OnOnOff hub->GetSwitchState()= " << hub->GetSwitchState(); + LogMessage(os.str().c_str(), false); + + return DEVICE_OK; +} + +// CWOSMDA implementation: +// Justin's Notes: +// This "Device" only sends one command "O,chan,volts" +// Summary: +// => OnVolts Action Handler is called by an event handler in MM. +// It subsequently calls: +// => SetSignal which optionally "gates" the O/P to "zero Volts" +// => WriteSignal scales the value to 12 bits (but now does nothing!) +// => WritetoPort - Sends "O,(int) chan,(float) volts" to WOSM +// => OnMaxVolts & => OnChannel just keep Volts and Channels within bounds +// +// If we need anything fast or device-specific do it on the WOSM. +// USB transmit/receive latency will dominate. + +// CWOSMDA implementation +CWOSMDA::CWOSMDA(int channel) : + busy_(false), + minV_(0.0), + maxV_(100.0), + volts_(0.0), + gatedVolts_(0.0), + channel_(channel), + maxChannel_(7), + gateOpen_(true) +{ + InitializeDefaultErrorMessages(); + + // add custom error messages + SetErrorText(ERR_UNKNOWN_POSITION, "Invalid position (state) specified"); + SetErrorText(ERR_INITIALIZE_FAILED, "Initialization of the device failed"); + SetErrorText(ERR_WRITE_FAILED, "Failed to write data to the device"); + SetErrorText(ERR_CLOSE_FAILED, "Failed closing the device"); + SetErrorText(ERR_NO_PORT_SET, "Hub Device not found. The WOSM Hub device is needed to create this device"); + + /* Channel property is not needed + CPropertyAction* pAct = new CPropertyAction(this, &CWOSMDA::OnChannel); + CreateProperty("Channel", channel_ == 1 ? "1" : "2", MM::Integer, false, pAct, true); + for (int i=1; i<= 2; i++){ + std::ostringstream os; + os << i; + AddAllowedValue("Channel", os.str().c_str()); + } + */ + + CPropertyAction* pAct = new CPropertyAction(this, &CWOSMDA::OnMaxVolt); + CreateProperty("Power %", "100", MM::Float, false, pAct, true); + + if ( channel_ == 0 ) + { + name_ = g_DeviceNameWOSMDAC0; + } + else if ( channel_ == 1 ) + { + name_ = g_DeviceNameWOSMDAC1; + } + else if ( channel_ == 2 ) + { + name_ = g_DeviceNameWOSMDAC2; + } + else if ( channel_ == 3 ) + { + name_ = g_DeviceNameWOSMDAC3; + } + else if ( channel_ == 4 ) + { + name_ = g_DeviceNameWOSMDAC4; + } + + //name_ = channel_ == 1 ? g_DeviceNameWOSMDA1 : g_DeviceNameWOSMDA2; + + // Description + int nRet = CreateProperty(MM::g_Keyword_Description, "WOSM DAC driver", MM::String, true); + assert(DEVICE_OK == nRet); + + // Name + nRet = CreateProperty(MM::g_Keyword_Name, name_.c_str(), MM::String, true); + assert(DEVICE_OK == nRet); + + // parent ID display + CreateHubIDProperty(); +} +CWOSMDA::~CWOSMDA() +{ + Shutdown(); +} +void CWOSMDA::GetName(char* name) const +{ + CDeviceUtils::CopyLimitedString(name, name_.c_str()); +} +int CWOSMDA::Initialize() +{ + CWOSMHub* hub = static_cast< CWOSMHub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) { + return ERR_NO_PORT_SET; + } + char hubLabel[MM::MaxStrLength]; + hub->GetLabel(hubLabel); + SetParentID(hubLabel); // for backward comp. + + // set property list + // ----------------- + + // State + // ----- + CPropertyAction* pAct = new CPropertyAction(this, &CWOSMDA::OnVolts); + int nRet = CreateProperty("Volts", "0.0", MM::Float, false, pAct); + if ( nRet != DEVICE_OK ) + return nRet; + SetPropertyLimits("Volts", minV_, maxV_); + + nRet = UpdateStatus(); + + if ( nRet != DEVICE_OK ) return nRet; + + initialized_ = true; + + return DEVICE_OK; +} +int CWOSMDA::Shutdown() +{ + initialized_ = false; + return DEVICE_OK; +} + +// Command: "dac_out ps 24.5" +int CWOSMDA::WriteToPort(double value) +{ + CWOSMHub* hub = static_cast< CWOSMHub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) + return ERR_NO_PORT_SET; + + LogMessage("Into write dac_out", false); + + MMThreadGuard myLock(hub->GetLock()); + + hub->PurgeComPortH(); + int ret = DEVICE_OK; + char command[50]; + unsigned int leng; + char chan[2]; + if ( ( channel_ >= 0 ) && ( channel_ < 4 ) ) { + chan[0] = 's' + static_cast(channel_) ; chan[1] = 0; // set s,t,u or v lines on WOSM + + leng = snprintf(( char* ) command, 50, "dac_out p%s %3.3f\r\n", ( char* ) chan, value); + + std::stringstream ss; + ss << "Sending...Command: " << command ; + LogMessage(ss.str().c_str(), false); + + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + + MM::MMTime startTime = GetCurrentMMTime(); + unsigned long bytesRead = 0; + unsigned char answer[50]; + while ( ( bytesRead < 50 ) && ( ( GetCurrentMMTime() - startTime ).getMsec() < 250 ) ) { + unsigned long br=0; + ret = hub->ReadFromComPortH(( unsigned char* ) answer + bytesRead, 1, br); + if ( answer[bytesRead] == '>' ) break; + bytesRead += br; + } + + answer[bytesRead] = 0; // string terminator + + hub->PurgeComPortH(); + if ( ret != DEVICE_OK ) return ret; + + //if ( answer[0] != 'W' ) return ERR_COMMUNICATION; + } + hub->SetTimedOutput(false); + + return DEVICE_OK; +} +int CWOSMDA::WriteSignal(double volts) +{ + double value = ( double ) volts; // ( (volts - minV_) / maxV_ * 4095); + + std::ostringstream os; + os << "Volts= " << volts << " MaxVoltage= " << maxV_ << " digitalValue= " << value; + LogMessage(os.str().c_str(), false); + + return WriteToPort(value); +} +int CWOSMDA::SetSignal(double volts) +{ + volts_ = volts; + if ( gateOpen_ ) { + gatedVolts_ = volts_; + return WriteSignal(volts_); + } + else { + gatedVolts_ = 0; + } + + return DEVICE_OK; +} +int CWOSMDA::SetGateOpen(bool open) +{ + if ( open ) { + gateOpen_ = true; + gatedVolts_ = volts_; + return WriteSignal(volts_); + } + gateOpen_ = false; + gatedVolts_ = 0; + return WriteSignal(0.0); + +} + +// Action handlers +int CWOSMDA::OnVolts(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if ( eAct == MM::BeforeGet ) + { + // nothing to do, let the caller use cached property + } + else if ( eAct == MM::AfterSet ) + { + double volts; + pProp->Get(volts); + return SetSignal(volts); + } + + return DEVICE_OK; +} +int CWOSMDA::OnMaxVolt(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if ( eAct == MM::BeforeGet ) + { + pProp->Set(maxV_); + } + else if ( eAct == MM::AfterSet ) + { + pProp->Get(maxV_); + if ( HasProperty("Volts") ) + SetPropertyLimits("Volts", 0.0, maxV_); + + } + return DEVICE_OK; +} +int CWOSMDA::OnChannel(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if ( eAct == MM::BeforeGet ) + { + pProp->Set(( long int ) channel_); + } + else if ( eAct == MM::AfterSet ) + { + long channel; + pProp->Get(channel); + if ( channel >= 0 && ( ( unsigned ) channel <= maxChannel_ ) ) + channel_ = channel; + } + return DEVICE_OK; +} + +/* +Below, I have folded-in (and modified) the PIEZOCONCEPT XYZ stage adapter +so we can control focus and move an x-y stage connected to our WOSM board (see firmware). +So, we have two new blocks of code: + +CWOSMStage & CWOSMXYStage + +Notes: +Stage and Focus noise: +On the WOSM side, the XYZ channels have 16-bit resolution + +*/ + +// CWOSMStage Implementation +CWOSMStage::CWOSMStage() : + stepSizeUm_(0.0001), + pos_um_(0.0), + busy_(false), + initialized_(false), + lowerLimit_(0.0), + upperLimit_(100.0) +{ + InitializeDefaultErrorMessages(); + + CreateHubIDProperty(); +} + +CWOSMStage::~CWOSMStage() +{ + Shutdown(); +} + +void CWOSMStage::GetName(char* Name) const +{ + CDeviceUtils::CopyLimitedString(Name, g_DeviceNameStage); +} + +int CWOSMStage::Initialize() +{ + CWOSMHub* hub = static_cast< CWOSMHub* >( GetParentHub() ); + + if ( !hub || !hub->IsPortAvailable() ) return ERR_NO_PORT_SET; + + char hubLabel[MM::MaxStrLength]; + hub->GetLabel(hubLabel); + SetParentID(hubLabel); // for backward comp. + + if ( initialized_ ) return DEVICE_OK; + + // Name + int ret = CreateProperty(MM::g_Keyword_Name, g_DeviceNameStage, MM::String, true); + if ( DEVICE_OK != ret ) return ret; + + // Description + ret = CreateProperty(MM::g_Keyword_Description, "WOSM Z stage driver", MM::String, true); + if ( DEVICE_OK != ret ) return ret; + + double travel; + + ret = hub->GetAxisInfoH(2, travel); + if ( DEVICE_OK != ret ) return ret; + + upperLimit_ = travel; + //Should we initialise stepsize as we do for X- Y- axes + //Suggest e.g. perhaps 10 digital bits per mouse-wheel click? + //stepSizeUm_ = travel / 6553.6; + + ret = UpdateStatus(); + if ( ret != DEVICE_OK ) return ret; + + initialized_ = true; + + std::ostringstream os; + os << "Stage::Initialize (exit)"; + LogMessage(os.str().c_str(), false); + + return DEVICE_OK; +} + +int CWOSMStage::Shutdown() +{ + if ( initialized_ ) initialized_ = false; + return DEVICE_OK; +} + +int CWOSMStage::GetPositionUm(double& pos) +{ + pos = pos_um_; + return DEVICE_OK; +} + +int CWOSMStage::SetPositionUm(double pos) +{ + int ret = MoveZ(pos); + if ( ret != DEVICE_OK ) return ret; + return OnStagePositionChanged(pos); +} + +int CWOSMStage::GetPositionSteps(long& steps) +{ + double posUm; + int ret = GetPositionUm(posUm); + if ( ret != DEVICE_OK ) return ret; + + steps = static_cast< long >( posUm / GetStepSize() ); + return DEVICE_OK; +} + +int CWOSMStage::SetPositionSteps(long steps) +{ + return SetPositionUm(steps * GetStepSize()); +} + +// "Z" command - move to new Z-position (Focus up/down) +int CWOSMStage::MoveZ(double pos) +{ + CWOSMHub* hub = static_cast< CWOSMHub* >( GetParentHub() ); + + LogMessage("Into moveZ" , false); + + if ( !hub || !hub->IsPortAvailable() ) return ERR_HUB_UNAVAILABLE; + + if ( pos > upperLimit_ ) pos = upperLimit_; + if ( pos < lowerLimit_ ) pos = lowerLimit_; + + LogMessage("after limits", false); + + char buf[50]; + int length = sprintf(buf, "dac_out pz %3.3f\r\n", pos); + + std::stringstream ss; + ss << "Command: " << buf << " Position set: " << pos; + LogMessage(ss.str().c_str(), false); + + MMThreadGuard myLock(hub->GetLock()); + hub->PurgeComPortH(); + + int ret = hub->WriteToComPortH(( unsigned char* ) buf, length); + if ( ret != DEVICE_OK ) return ret; + + MM::MMTime startTime = GetCurrentMMTime(); + unsigned long bytesRead = 0; + unsigned char answer[50]; + while ( ( bytesRead < 50 ) && ( ( GetCurrentMMTime() - startTime ).getMsec() < 250 ) ) { + unsigned long br; + ret = hub->ReadFromComPortH(( unsigned char* ) answer + bytesRead, 1, br); + if ( answer[bytesRead] == '>' ) break; + bytesRead += br; + } + + answer[bytesRead] = 0; // string terminator + + hub->PurgeComPortH(); + if ( ret != DEVICE_OK ) return ret; + + //if ( answer[0] != 'W' ) return ERR_COMMUNICATION; + + hub->SetTimedOutput(false); + + std::ostringstream os; + os << "MoveZ Z," << pos; + LogMessage(os.str().c_str(), false); + + pos_um_ = pos; + + return DEVICE_OK; +} + +bool CWOSMStage::Busy() +{ + return false; +} + +CWOSMXYStage::CWOSMXYStage() : CXYStageBase(), +stepSize_X_um_(0.1), +stepSize_Y_um_(0.1), +posX_um_(0.0), +posY_um_(0.0), +busy_(false), +initialized_(false), +lowerLimitX_(0.0), +upperLimitX_(100.0), +lowerLimitY_(0.0), +upperLimitY_(100.0) +{ + InitializeDefaultErrorMessages(); + + // parent ID display + CreateHubIDProperty(); + + // step size + CPropertyAction* pAct = new CPropertyAction(this, &CWOSMXYStage::OnXStageMinPos); + CreateProperty(g_PropertyXMinUm, "0", MM::Float, false, pAct, true); + + pAct = new CPropertyAction(this, &CWOSMXYStage::OnXStageMaxPos); + CreateProperty(g_PropertyXMaxUm, "100", MM::Float, false, pAct, true); + + pAct = new CPropertyAction(this, &CWOSMXYStage::OnYStageMinPos); + CreateProperty(g_PropertyYMinUm, "0", MM::Float, false, pAct, true); + + pAct = new CPropertyAction(this, &CWOSMXYStage::OnYStageMaxPos); + CreateProperty(g_PropertyYMaxUm, "100", MM::Float, false, pAct, true); +} + +CWOSMXYStage::~CWOSMXYStage() +{ + Shutdown(); +} + +void CWOSMXYStage::GetName(char* Name) const +{ + CDeviceUtils::CopyLimitedString(Name, g_DeviceNameXYStage); +} + +int CWOSMXYStage::Initialize() +{ + CWOSMHub* hub = static_cast< CWOSMHub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) return ERR_NO_PORT_SET; + + char hubLabel[MM::MaxStrLength]; + hub->GetLabel(hubLabel); + SetParentID(hubLabel); // for backward comp. + + if ( initialized_ ) return DEVICE_OK; + + // Name + int ret = CreateProperty(MM::g_Keyword_Name, g_DeviceNameXYStage, MM::String, true); + if ( DEVICE_OK != ret ) return ret; + + // Description + ret = CreateProperty(MM::g_Keyword_Description, "XY stage driver", MM::String, true); + if ( DEVICE_OK != ret ) return ret; + + double travelX, travelY; + ret = hub->GetAxisInfoH(0, travelX); + if ( DEVICE_OK != ret ) return ret; + + ret = hub->GetAxisInfoH(1, travelY); + if ( DEVICE_OK != ret ) return ret; + + // here the stepsize is set to 1 digital bit ?..Cut-and-paste from PIEZOCONCEPT. + upperLimitX_ = travelX; + stepSize_X_um_ = travelX / 65535; + upperLimitY_ = travelY; + stepSize_Y_um_ = travelY / 65535; + + ret = UpdateStatus(); + if ( ret != DEVICE_OK ) return ret; + + initialized_ = true; + + return DEVICE_OK; +} + +int CWOSMXYStage::Shutdown() +{ + if ( initialized_ ) initialized_ = false; + return DEVICE_OK; +} + +bool CWOSMXYStage::Busy() +{ + return false; +} + +int CWOSMXYStage::GetPositionSteps(long& x, long& y) +{ + x = ( long ) ( posX_um_ / stepSize_X_um_ ); + y = ( long ) ( posY_um_ / stepSize_Y_um_ ); + + std::stringstream ss; + ss << "GetPositionSteps :=" << x << "," << y; + LogMessage(ss.str(), false); + return DEVICE_OK; +} + +int CWOSMXYStage::SetPositionSteps(long x, long y) +{ + double posX = x * stepSize_X_um_; + double posY = y * stepSize_Y_um_; + + std::stringstream ss; + ss << "Current position = " << posX_um_ << "," << posY_um_ << " \n Commanded position = " << posX << "," << posY; + LogMessage(ss.str(), false); + + int ret = DEVICE_OK; + + if ( posX_um_ != posX ) + { + ret = MoveX(posX); + if ( ret != DEVICE_OK ) return ret; + } + if ( posY_um_ != posY ) + { + ret = MoveY(posY); + if ( ret != DEVICE_OK ) return ret; + } + return OnXYStagePositionChanged(posX_um_, posY_um_); +} + +int CWOSMXYStage::SetRelativePositionSteps(long x, long y) +{ + long curX, curY; + GetPositionSteps(curX, curY); + + return SetPositionSteps(curX + x, curY + y); +} + +// "X" command - move to new x-position (stage translate) +int CWOSMXYStage::MoveX(double posUm) +{ + CWOSMHub* hub = static_cast< CWOSMHub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) return ERR_HUB_UNAVAILABLE; + + if ( posUm < lowerLimitX_ ) posUm = lowerLimitX_; + if ( posUm > upperLimitX_ ) posUm = upperLimitX_; + + char buf[25]; + int length = sprintf(buf, "dac_out px %1.3f\r\n", posUm); + + std::stringstream ss; + ss << "Command: " << buf << " Position set: " << posUm; + LogMessage(ss.str().c_str(), false); + + MMThreadGuard myLock(hub->GetLock()); + hub->PurgeComPortH(); + + int ret = hub->WriteToComPortH(( unsigned char* ) buf, length); + if ( ret != DEVICE_OK ) return ret; + + MM::MMTime startTime = GetCurrentMMTime(); + unsigned long bytesRead = 0; + unsigned char answer[50]; + while ( ( bytesRead < 50 ) && ( ( GetCurrentMMTime() - startTime ).getMsec() < 250 ) ) { + unsigned long br; + ret = hub->ReadFromComPortH(( unsigned char* ) answer + bytesRead, 1, br); + if ( answer[bytesRead] == '>' ) break; + bytesRead += br; + } + + answer[bytesRead] = 0; // string terminator + + hub->PurgeComPortH(); + hub->SetTimedOutput(false); + + if ( ret != DEVICE_OK ) return ret; + + //if ( answer[0] != 'W' ) return ERR_COMMUNICATION; + + std::ostringstream os; + os << "X-Answer= " << answer; + LogMessage(os.str().c_str(), false); + + posX_um_ = posUm; + return DEVICE_OK; +} + +// "Y" command - move to new y-position (stage translate) +int CWOSMXYStage::MoveY(double posUm) +{ + CWOSMHub* hub = static_cast< CWOSMHub* >( GetParentHub() ); + + if ( !hub || !hub->IsPortAvailable() ) return ERR_HUB_UNAVAILABLE; + + if ( posUm < lowerLimitY_ ) posUm = lowerLimitY_; + if ( posUm > upperLimitY_ ) posUm = upperLimitY_; + + char buf[25]; + int length = sprintf(buf, "dac_out py %1.3f\r\n", posUm); + + std::stringstream ss; + ss << "Command: " << buf << " Position set: " << posUm; + LogMessage(ss.str().c_str(), true); + + MMThreadGuard myLock(hub->GetLock()); + hub->PurgeComPortH(); + + int ret = hub->WriteToComPortH(( unsigned char* ) buf, length); + if ( ret != DEVICE_OK ) return ret; + + MM::MMTime startTime = GetCurrentMMTime(); + unsigned long bytesRead = 0; + unsigned char answer[50]; + while ( ( bytesRead < 50 ) && ( ( GetCurrentMMTime() - startTime ).getMsec() < 250 ) ) { + unsigned long br; + ret = hub->ReadFromComPortH(( unsigned char* ) answer + bytesRead, 1, br); + if ( answer[bytesRead] == '>' ) break; + bytesRead += br; + } + + answer[bytesRead] = 0; // string terminator + + hub->PurgeComPortH(); + hub->SetTimedOutput(false); + + if ( ret != DEVICE_OK ) return ret; + + //if ( answer[0] != 'W' ) return ERR_COMMUNICATION; + + std::ostringstream os; + os << "Y-Answer= " << answer; + LogMessage(os.str().c_str(), false); + + posY_um_ = posUm; + return DEVICE_OK; +} + +int CWOSMXYStage::OnXStageMinPos(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + int ret = DEVICE_ERR; + if ( eAct == MM::BeforeGet ) + { + pProp->Set(lowerLimitX_); + ret = DEVICE_OK; + } + else if ( eAct == MM::AfterSet ) + { + double limit; + pProp->Get(limit); + lowerLimitX_ = limit; + + ret = DEVICE_OK; + } + + return ret; +} + +int CWOSMXYStage::OnXStageMaxPos(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + int ret = DEVICE_ERR; + if ( eAct == MM::BeforeGet ) + { + pProp->Set(upperLimitX_); + ret = DEVICE_OK; + } + else if ( eAct == MM::AfterSet ) + { + double limit; + pProp->Get(limit); + upperLimitX_ = limit; + + ret = DEVICE_OK; + } + + return ret; +} + +int CWOSMXYStage::OnYStageMinPos(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + int ret = DEVICE_ERR; + if ( eAct == MM::BeforeGet ) + { + pProp->Set(lowerLimitY_); + ret = DEVICE_OK; + } + else if ( eAct == MM::AfterSet ) + { + double limit; + pProp->Get(limit); + lowerLimitY_ = limit; + + ret = DEVICE_OK; + } + + return ret; +} + +int CWOSMXYStage::OnYStageMaxPos(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + int ret = DEVICE_ERR; + if ( eAct == MM::BeforeGet ) + { + pProp->Set(upperLimitY_); + ret = DEVICE_OK; + } + else if ( eAct == MM::AfterSet ) + { + double limit; + pProp->Get(limit); + upperLimitY_ = limit; + + ret = DEVICE_OK; + } + + return ret; +} + + +// The "Input" functions below need to be commented +CWOSMInput::CWOSMInput() : + mThread_(0), + pin_(0), + name_(g_DeviceNameWOSMInput) +{ + std::string errorText = "To use the Input function you need firmware version 5 or higher"; + SetErrorText(ERR_VERSION_MISMATCH, errorText.c_str()); + + + + CreateProperty("Pin", "All", MM::String, false, 0, true); + AddAllowedValue("Pin", "All"); + AddAllowedValue("Pin", "0"); + AddAllowedValue("Pin", "1"); + AddAllowedValue("Pin", "2"); + AddAllowedValue("Pin", "3"); + AddAllowedValue("Pin", "4"); + AddAllowedValue("Pin", "5"); + + CreateProperty("Pull-Up-Resistor", g_On, MM::String, false, 0, true); + AddAllowedValue("Pull-Up-Resistor", g_On); + AddAllowedValue("Pull-Up-Resistor", g_Off); + + // Name + int ret = CreateProperty(MM::g_Keyword_Name, name_.c_str(), MM::String, true); + assert(DEVICE_OK == ret); + + // Description + ret = CreateProperty(MM::g_Keyword_Description, "WOSM shutter driver", MM::String, true); + assert(DEVICE_OK == ret); + + // parent ID display + CreateHubIDProperty(); +} + +CWOSMInput::~CWOSMInput() +{ + Shutdown(); +} + +int CWOSMInput::Shutdown() +{ + if ( initialized_ ) + delete( mThread_ ); + initialized_ = false; + return DEVICE_OK; +} + +int CWOSMInput::Initialize() +{ + CWOSMHub* hub = static_cast< CWOSMHub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) return ERR_NO_PORT_SET; + + char hubLabel[MM::MaxStrLength]; + hub->GetLabel(hubLabel); + SetParentID(hubLabel); // for backward comp. + + char ver[MM::MaxStrLength] = "0"; + hub->GetProperty(g_versionProp, ver); + + // we should get ASCII "5" back from the WOSM - convert to (int) + int version = atoi(ver); + if ( version < g_Min_MMVersion ) return ERR_VERSION_MISMATCH; + + // I think the idea is to request setup of the INPUT_PULLUP state + // on the GPIO pins on the microcontroller... but it is not clear + + int ret = GetProperty("Pin", pins_); + if ( ret != DEVICE_OK ) return ret; + + // pin_ = ascii to integer of pins_ (i.e. 0,1,2,3,4,5) unknown value if pins_="All" + if ( strcmp("All", pins_) != 0 ) pin_ = atoi(pins_); + + ret = GetProperty("Pull-Up-Resistor", pullUp_); + if ( ret != DEVICE_OK ) return ret; + + // Digital Input + CPropertyAction* pAct = new CPropertyAction(this, &CWOSMInput::OnDigitalInput); + ret = CreateProperty("DigitalInput", "0", MM::Integer, true, pAct); + if ( ret != DEVICE_OK ) return ret; + + int start = 0; + int end = 5; + if ( strcmp("All", pins_) != 0 ) { + start = pin_; + end = pin_; + } + + for ( long i = start; i <= end; i++ ) + { + CPropertyActionEx* pExAct = new CPropertyActionEx(this, &CWOSMInput::OnAnalogInput, i); + std::ostringstream os; + os << "AnalogInput= " << i; + + ret = CreateProperty(os.str().c_str(), "0.0", MM::Float, true, pExAct); + if ( ret != DEVICE_OK ) return ret; + + // set pull up resistor state for this pin + if ( strcmp(g_On, pullUp_) == 0 ) SetPullUp(i, 1); + else SetPullUp(i, 0); + + } + + mThread_ = new WOSMInputMonitorThread(*this); + mThread_->Start(); + + initialized_ = true; + + return DEVICE_OK; +} + +void CWOSMInput::GetName(char* name) const +{ + CDeviceUtils::CopyLimitedString(name, name_.c_str()); +} + +bool CWOSMInput::Busy() +{ + return false; +} + +// Justin Notes: "GetDigitalInput" is polled every 0.5sec by the timed thread see below. +// In the original WOSM code we were testing the Analogue Inputs for a digital change.. +// That's fine.. but we should set this up in a more obvious way. +// suggest we assign a some of the GPIO lines as Digital I/O and some as Analogue Input. +// Currently this is unclear to me...I don't know what Analogue inputs we want to monitor. + +// Command: 40 now "L" logic in - returns "L,l\r\n" pin High/Low +int CWOSMInput::GetDigitalInput(long* state) +{ + CWOSMHub* hub = static_cast< CWOSMHub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) + return ERR_NO_PORT_SET; + + MMThreadGuard myLock(hub->GetLock()); + + hub->PurgeComPortH(); + int ret = DEVICE_OK; + char command[50]; + unsigned int leng; + + int testPin = pin_; //(test pin 0->5) + if ( strcmp("All", pins_) == 0 ) testPin = 6; // (test if all pins set) + + leng = snprintf(( char* ) command, 50, "L,%d\r\n", testPin); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + + // we expect a reply like "L,1\r\n" 1 or 0 if testPin is High/Low + MM::MMTime startTime = GetCurrentMMTime(); + unsigned long bytesRead = 0; + char answer[50]; + // Chug through the I/O buffer, byte-by-byte and stop at lf! + while ( ( bytesRead < 50 ) && ( ( GetCurrentMMTime() - startTime ).getMsec() < 250 ) ) { + unsigned long br=0; + ret = hub->ReadFromComPortH(( unsigned char* ) answer + bytesRead, 1, br); + if ( answer[bytesRead] == '\r' ) break; + bytesRead += br; + } + + answer[bytesRead] = 0; // string terminator + + // discard anything left in the IO buffer + hub->PurgeComPortH(); + hub->SetTimedOutput(false); + + if ( ret != DEVICE_OK ) return ret; + + int num; + char com[1]; + // sscanf(( const char* ) answer, "%1s,%d", com, &num); + // EDITED HERE************************************************** + num = 1; + // should give com[0]='L' and num = 1 or 0 + + if ( com[0] != 'L' ) return ERR_COMMUNICATION; + + *state = ( long ) num; + + return DEVICE_OK; +} +int CWOSMInput::ReportStateChange(long newState) +{ + std::ostringstream os; + os << newState; + return OnPropertyChanged("DigitalInput", os.str().c_str()); +} + +// Action handlers +int CWOSMInput::OnDigitalInput(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + // This sets a property listed in the MMDevice Property.h file (very limited commenting) + // "Set" is an "Action Functor" (handler?) + // "eACT" seems common to all "On" functions - so probably an Event listener? + // Anyway, it wants a (long) value for "state" which becomes a virtual Boolean, apparently! + + if ( eAct == MM::BeforeGet ) + { + long state; + + // get the state of the selected pin 0-5 or all pins (which I have made to be "6" in my WOSM code) + int ret = GetDigitalInput(&state); + if ( ret != DEVICE_OK ) return ret; + pProp->Set(state); + } + + return DEVICE_OK; +} + +// Commands: 41 now = "A,chan" analogue read channel +int CWOSMInput::OnAnalogInput(MM::PropertyBase* pProp, MM::ActionType eAct, long channel) +{ + CWOSMHub* hub = static_cast< CWOSMHub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) return ERR_NO_PORT_SET; + + if ( eAct == MM::BeforeGet ) + { + + MMThreadGuard myLock(hub->GetLock()); + hub->PurgeComPortH(); + int ret = DEVICE_OK; + char command[50]; + unsigned int leng; + + leng = snprintf(( char* ) command, 50, "A,%d\r\n", ( int ) channel); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + + // We expect a reply like "A,597\r\n" analogue value on channel + // I think we need to use the hub to communicate via the rather "ReadFromComPortH" command?? + unsigned long bytesRead = 0; + char answer[50]; + MM::MMTime startTime = GetCurrentMMTime(); + while ( ( bytesRead < 50 ) && ( ( GetCurrentMMTime() - startTime ).getMsec() < 1000 ) ) { + unsigned long br=0; + ret = hub->ReadFromComPortH(( unsigned char* ) answer + bytesRead, 1, br); + if ( answer[bytesRead] == '\r' ) break; + bytesRead += br; + } + + answer[bytesRead] = 0; // replace \r with string terminator + + hub->PurgeComPortH(); + hub->SetTimedOutput(false); + + if ( ret != DEVICE_OK ) return ret; + + int num; + char com[1]; + //sscanf(( const char* ) answer, "%1s,%d", com, &num); + // should give com[0]='A' and num = 597 or something! + //EDITED HERE ********************************** + num = 1; + std::ostringstream os; + os << "AnswerToA= " << answer << " bytesRead= " << bytesRead << " com= " << com << " num= " << num; + LogMessage(os.str().c_str(), false); + + //if ( com[0] != 'A' ) return ERR_COMMUNICATION; + + pProp->Set(( long ) num); + } + return DEVICE_OK; +} + +// Commands: 42 - now "D" set digital pull-up ?? needs commenting - not sure why we want to do this dynamically. +int CWOSMInput::SetPullUp(int pin, int state) +{ + CWOSMHub* hub = static_cast< CWOSMHub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) return ERR_NO_PORT_SET; + + MMThreadGuard myLock(hub->GetLock()); + hub->PurgeComPortH(); + int ret = DEVICE_OK; + char command[50]; + unsigned int leng; + + leng = snprintf(( char* ) command, 50, "D,%d,%d\r\n", ( int ) pin, ( int ) state); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + + hub->PurgeComPortH(); + hub->SetTimedOutput(false); + + return DEVICE_OK; +} + +WOSMInputMonitorThread::WOSMInputMonitorThread(CWOSMInput& aInput) : + state_(0), + aInput_(aInput) +{ +} +WOSMInputMonitorThread::~WOSMInputMonitorThread() +{ + Stop(); + wait(); +} + +// I think this "free-running" thread is a 0.5s delay loop that polls the Digital I/O lines and reports changes +// It is perhaps not "thread safe" because the "L" and "A" commands seems to interfere... I haven't debugged this. +// It has probably been messed-up by my meddling! + +int WOSMInputMonitorThread::svc() +{ + while ( !stop_ ) + { + long state; + int ret = aInput_.GetDigitalInput(&state); + if ( ret != DEVICE_OK ) + { + stop_ = true; + return ret; + } + + if ( state != state_ ) + { + aInput_.ReportStateChange(state); + state_ = state; + } + CDeviceUtils::SleepMs(500); + } + return DEVICE_OK; +} +void WOSMInputMonitorThread::Start() +{ + stop_ = false; + activate(); +} diff --git a/DeviceAdapters/WOSM/WOSM.h b/DeviceAdapters/WOSM/WOSM.h new file mode 100644 index 000000000..542475bf3 --- /dev/null +++ b/DeviceAdapters/WOSM/WOSM.h @@ -0,0 +1,427 @@ +////////////////////////////////////////////////////////////////////////////// +// FILE: WOSM.h +// PROJECT: Micro-Manager +// SUBSYSTEM: DeviceAdapters +//----------------------------------------------------------------------------- +// DESCRIPTION: Adapter for Warwick Open-Source Microscope "WOSM" +// +// COPYRIGHT: Justin Molloy, Warwick University 2023 +// LICENSE: LGPL +// +// AUTHOR: Justin E. Molloy +// +// Software is modified from Arduino Adapter written by: +// Nico Stuurman, nico@cmp.ucsf.edu, 11/09/2008 +// automatic device detection by Karl Hoover +// +// + +#ifndef _WOSM_H_ +#define _WOSM_H_ + +#include "MMDevice.h" +#include "DeviceBase.h" +#include +#include + +////////////////////////////////////////////////////////////////////////////// +// Error codes +// +#define ERR_UNKNOWN_POSITION 101 +#define ERR_INITIALIZE_FAILED 102 +#define ERR_WRITE_FAILED 103 +#define ERR_CLOSE_FAILED 104 +#define ERR_WOSM_NOT_FOUND 105 +#define ERR_PORT_OPEN_FAILED 106 +#define ERR_COMMUNICATION 107 +#define ERR_NO_PORT_SET 108 +#define ERR_VERSION_MISMATCH 109 +#define ERR_HUB_UNAVAILABLE 110 +#define ERR_UNKNOWN_AXIS 111 + +class WOSMInputMonitorThread; + +class CWOSMHub : public HubBase +{ +public: + CWOSMHub(); + ~CWOSMHub(); + + int Initialize(); + int Shutdown(); + void GetName(char* pszName) const; + bool Busy(); + + bool SupportsDeviceDetection(void); + MM::DeviceDetectionStatus DetectDevice(void); + int DetectInstalledDevices(); + + // property handlers + int OnPort(MM::PropertyBase* pPropt, MM::ActionType eAct); + int OnLogic(MM::PropertyBase* pPropt, MM::ActionType eAct); + int OnVersion(MM::PropertyBase* pPropt, MM::ActionType eAct); + + // custom interface for child devices + bool IsPortAvailable() { return portAvailable_; } + bool IsLogicInverted() { return invertedLogic_; } + bool IsTimedOutputActive() { return timedOutputActive_; } + void SetTimedOutput(bool active) { timedOutputActive_ = active; } + + int PurgeComPortH() { return PurgeComPort(port_.c_str()); } + int WriteToComPortH(const unsigned char* command, unsigned len) { return WriteToComPort(port_.c_str(), command, len); } + int ReadFromComPortH(unsigned char* answer, unsigned maxLen, unsigned long& bytesRead) + { + return ReadFromComPort(port_.c_str(), answer, maxLen, bytesRead); + } + + + static MMThreadLock& GetLock() { return lock_; } + void SetShutterState(unsigned state) { shutterState_ = state; } + void SetSwitchState(unsigned state) { switchState_ = state; } + unsigned GetShutterState() { return shutterState_; } + unsigned GetSwitchState() { return switchState_; } + int GetAxisInfoH(int axis, double& travel) { return GetAxisInfo(axis, travel); } + int SetAxisModeH(int axis) { return SetAxisMode(axis); } + + +private: + int GetControllerVersion(int&); + std::string port_; + bool initialized_; + bool portAvailable_; + bool invertedLogic_; + bool timedOutputActive_; + ; + int version_; + static MMThreadLock lock_; + unsigned switchState_; + unsigned shutterState_; + + int GetControllerInfo(); + int GetAxisInfo(int, double&); + int SetAxisMode(int); + bool hasZStage_; + bool hasXYStage_; +}; + +class CWOSMShutter : public CShutterBase +{ +public: + CWOSMShutter(); + ~CWOSMShutter(); + + // MMDevice API + // ------------ + int Initialize(); + int Shutdown(); + + void GetName(char* pszName) const; + bool Busy(); + + // Shutter API + int SetOpen(bool open = true); + int GetOpen(bool& open); + int Fire(double deltaT); + + // action interface + // ---------------- + int OnOnOff(MM::PropertyBase* pProp, MM::ActionType eAct); + +private: + int WriteToPort(long lnValue); + MM::MMTime changedTime_; + bool initialized_; + std::string name_; +}; + +class CWOSMSwitch : public CStateDeviceBase +{ +public: + CWOSMSwitch(); + ~CWOSMSwitch(); + + // MMDevice API + // ------------ + int Initialize(); + int Shutdown(); + + void GetName(char* pszName) const; + bool Busy() { return busy_; } + + unsigned long GetNumberOfPositions()const { return numPos_; } + + // action interface + // ---------------- + int OnState(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnDelay(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnRepeatTimedPattern(MM::PropertyBase* pProp, MM::ActionType eAct); + /* + int OnSetPattern(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnGetPattern(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnPatternsUsed(MM::PropertyBase* pProp, MM::ActionType eAct); + */ + int OnSkipTriggers(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnStartTrigger(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnStartTimedOutput(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnBlanking(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnBlankingTriggerDirection(MM::PropertyBase* pProp, MM::ActionType eAct); + + int OnSequence(MM::PropertyBase* pProp, MM::ActionType eAct); + +private: + static const unsigned int NUMPATTERNS = 12; + + int OpenPort(const char* pszName, long lnValue); + int WriteToPort(long lnValue); + int ClosePort(); + int LoadSequence(unsigned size, unsigned char* seq); + + unsigned pattern_[NUMPATTERNS]; + unsigned delay_[NUMPATTERNS]; + int nrPatternsUsed_; + unsigned currentDelay_; + bool sequenceOn_; + bool blanking_; + bool initialized_; + long numPos_; + bool busy_; +}; + +class CWOSMDA : public CSignalIOBase +{ +public: + CWOSMDA(int channel); + ~CWOSMDA(); + + // MMDevice API + // ------------ + int Initialize(); + int Shutdown(); + + void GetName(char* pszName) const; + bool Busy() { return busy_; } + + // DA API + int SetGateOpen(bool open); + int GetGateOpen(bool& open) { open = gateOpen_; return DEVICE_OK; }; + int SetSignal(double volts); + int GetSignal(double& volts) { volts_ = volts; return DEVICE_UNSUPPORTED_COMMAND; } + int GetLimits(double& minVolts, double& maxVolts) { minVolts = minV_; maxVolts = maxV_; return DEVICE_OK; } + + int IsDASequenceable(bool& isSequenceable) const { isSequenceable = false; return DEVICE_OK; } + + // action interface + // ---------------- + int OnVolts(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnMaxVolt(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnChannel(MM::PropertyBase* pProp, MM::ActionType eAct); + +private: + int WriteToPort(double lnValue); + int WriteSignal(double volts); + + bool initialized_; + bool busy_; + double minV_; + double maxV_; + double volts_; + double gatedVolts_; + unsigned channel_; + unsigned maxChannel_; + bool gateOpen_; + std::string name_; +}; + +class CWOSMInput : public CGenericBase +{ +public: + CWOSMInput(); + ~CWOSMInput(); + + int Initialize(); + int Shutdown(); + void GetName(char* pszName) const; + bool Busy(); + + int OnDigitalInput(MM::PropertyBase* pPropt, MM::ActionType eAct); + int OnAnalogInput(MM::PropertyBase* pProp, MM::ActionType eAct, long channel); + + int GetDigitalInput(long* state); + int ReportStateChange(long newState); + +private: + // int ReadNBytes(CWOSMHub* h, unsigned int n, unsigned char* answer); + int SetPullUp(int pin, int state); + + MMThreadLock lock_; + WOSMInputMonitorThread* mThread_; + char pins_[MM::MaxStrLength]; + char pullUp_[MM::MaxStrLength]; + int pin_; + bool initialized_; + std::string name_; +}; + +class WOSMInputMonitorThread : public MMDeviceThreadBase +{ +public: + WOSMInputMonitorThread(CWOSMInput& aInput); + ~WOSMInputMonitorThread(); + int svc(); + int open(void*) { return 0; } + int close(unsigned long) { return 0; } + + void Start(); + void Stop() { stop_ = true; } + WOSMInputMonitorThread& operator=(const WOSMInputMonitorThread&) + { + return *this; + } + + +private: + long state_; + CWOSMInput& aInput_; + bool stop_; +}; + +class CWOSMStage : public CStageBase +{ +public: + CWOSMStage(); + ~CWOSMStage(); + + bool Busy(); + void GetName(char* pszName) const; + + int Initialize(); + int Shutdown(); + + // Stage API + int SetPositionUm(double pos); + int GetPositionUm(double& pos); + double GetStepSize() { return stepSizeUm_; } + + int SetPositionSteps(long steps); + int GetPositionSteps(long& steps); + + int SetOrigin() { return DEVICE_UNSUPPORTED_COMMAND; } + + int GetLimits(double& lower, double& upper) + { + lower = lowerLimit_; + upper = upperLimit_; + return DEVICE_OK; + } + + int Move(double /*v*/) { return DEVICE_UNSUPPORTED_COMMAND; } + + bool IsContinuousFocusDrive() const { return false; } + + int OnStageMinPos(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnStageMaxPos(MM::PropertyBase* pProp, MM::ActionType eAct); + + int IsStageSequenceable(bool& isSequenceable) const + { + isSequenceable = false; return DEVICE_OK; + } + int GetStageSequenceMaxLength(long& nrEvents) const + { + nrEvents = 0; return DEVICE_OK; + } + +private: + double stepSizeUm_; + double pos_um_; + bool busy_; + bool initialized_; + double lowerLimit_; + double upperLimit_; + + int MoveZ(double pos); +}; + +class CWOSMXYStage : public CXYStageBase +{ +public: + CWOSMXYStage(); + ~CWOSMXYStage(); + + bool Busy(); + void GetName(char* pszName) const; + + int Initialize(); + int Shutdown(); + + int GetPositionSteps(long& x, long& y); + int SetPositionSteps(long x, long y); + int SetRelativePositionSteps(long, long); + + virtual int Home() { return DEVICE_UNSUPPORTED_COMMAND; } + virtual int Stop() { return DEVICE_UNSUPPORTED_COMMAND; } + virtual int SetOrigin() { return DEVICE_UNSUPPORTED_COMMAND; } + + virtual int GetLimits(double& lower, double& upper) + { + lower = lowerLimitX_; + upper = upperLimitY_; + return DEVICE_OK; + } + + virtual int GetLimitsUm(double& xMin, double& xMax, double& yMin, double& yMax) + { + xMin = lowerLimitX_; xMax = upperLimitX_; + yMin = lowerLimitY_; yMax = upperLimitY_; + return DEVICE_OK; + } + + virtual int GetStepLimits(long& /*xMin*/, long& /*xMax*/, long& /*yMin*/, long& /*yMax*/) + { + return DEVICE_UNSUPPORTED_COMMAND; + } + + double GetStepSizeXUm() + { + return stepSize_X_um_; + } + + double GetStepSizeYUm() + { + return stepSize_Y_um_; + } + + int Move(double /*vx*/, double /*vy*/) { return DEVICE_OK; } + + int IsXYStageSequenceable(bool& isSequenceable) const { isSequenceable = false; return DEVICE_OK; } + + int OnXStageMinPos(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnXStageMaxPos(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnYStageMinPos(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnYStageMaxPos(MM::PropertyBase* pProp, MM::ActionType eAct); + + int OnStepsPerSecond(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnMicrostepMultiplier(MM::PropertyBase* pProp, MM::ActionType eAct); + +private: + double stepSize_X_um_; + double stepSize_Y_um_; + double posX_um_; + double posY_um_; + unsigned int driveID_X_; + unsigned int driveID_Y_; + + bool busy_; + MM::TimeoutMs* timeOutTimer_; + double velocity_; + bool initialized_; + double lowerLimitX_; + double upperLimitX_; + double lowerLimitY_; + double upperLimitY_; + + int MoveX(double); + int MoveY(double); +}; + +#endif //_WOSM_H_ + diff --git a/DeviceAdapters/WOSM/WOSM.vcxproj b/DeviceAdapters/WOSM/WOSM.vcxproj new file mode 100644 index 000000000..937832a2f --- /dev/null +++ b/DeviceAdapters/WOSM/WOSM.vcxproj @@ -0,0 +1,94 @@ + + + + + Debug + x64 + + + Release + x64 + + + + + + + + + + + {b8c95f39-54bf-40a9-807b-598df2821d55} + + + + 16.0 + Win32Proj + {64e94a9e-b3ae-47ef-8f5a-0356b0e6b9da} + WOSM + 10.0 + + + + DynamicLibrary + true + v142 + Unicode + + + DynamicLibrary + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + true + _DEBUG;WOSM_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + pch.h + + + Windows + true + false + + + + + true + true + true + NDEBUG;WOSM_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + pch.h + + + Windows + true + true + true + false + + + + + + \ No newline at end of file diff --git a/DeviceAdapters/WOSM/WOSM.vcxproj.filters b/DeviceAdapters/WOSM/WOSM.vcxproj.filters new file mode 100644 index 000000000..46a66a42b --- /dev/null +++ b/DeviceAdapters/WOSM/WOSM.vcxproj.filters @@ -0,0 +1,27 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + + + Source Files + + + \ No newline at end of file diff --git a/DeviceAdapters/WOSM/WOSM_rev.cpp b/DeviceAdapters/WOSM/WOSM_rev.cpp new file mode 100644 index 000000000..c3749d10a --- /dev/null +++ b/DeviceAdapters/WOSM/WOSM_rev.cpp @@ -0,0 +1,2578 @@ +/////////////////////////////////////////////////////////////////////////////// +// FILE: WOSM.cpp +// PROJECT: Micro-Manager +// SUBSYSTEM: DeviceAdapters +//----------------------------------------------------------------------------- +// DESCRIPTION: Adapter for Warwick Open-Source Microscope "WOSM" +// HTTPS://www.wosmic.org +// This device Adapter is a modded Arduino Device Adapter +// +// COPYRIGHT: University of Warwick, UK 2023 +// LICENSE: LGPL +// AUTHORS: Justin E. Molloy & Nicholas J. Carter +// +// +// +// Software is modified from Arduino Adapter written by: +// Copyright : University of California, San Francisco, 2008 +// Nico Stuurman, nico@cmp.ucsf.edu, 11/09/2008 +// automatic device detection by Karl Hoover +// +// This device Adapter also includes code modified from the PiezoConcept XYZ. +// +/////////////////////////////////////////////////////////////////////////////// +// +// The IP address of the WOSM controller is available on the WOSM LCD screen. +// e.g. "192.168.10.100" use port 23 or 1023 +// Upon connection via TCP/IP the WOSM reports its model ID and networking information +// and requests the user to enter an "admin password: " (default is "wosm") +// It then enters "command mode" signified by the prompt: +// +// W> +// +// ============================ +// WOSM Command-Set over-view: +// ============================ +// Here, we use a small sub-set of commands to control the WOSM +// The full command-set for the WOSM is at: http://wosmic.org/mcu/commands.php +// +// Hint: You can enter and test commands using PuTTy. +// +// System Commands: +// sys_ commands= time, date, usec, uptime etc.... +// example: W>sys_time ->returns system time in UTC format. +// +// Temperature Sensors: +// temp_ commands= ser, ref, val etc... +// example: W>temp_val stage ->temp (oC*256) of sensor called "stage" +// +// Addressable Serial LEDs: +// led_ commands= conf, buff, mask, bright, on +// example: W>led_buff 0xFF00FF 0x000000010 ->make LED number 9 purple +// +// Counters (2 * 16-bit counters on digital lines "q" & "r"): +// cnt_ commands= en, clr, val +// example: W>cnt_clr q ->clear counter on digtal line "q" +// +// KeyPad, Rotary encoder (monitor remote controller box - send messages to its screen): +// kyp_ commands= val, rot, lcd_txt +// examples: W>kyp_rot ->read rotary encoder clicks +// W>kyp_lcd_screen t=6s \"\n Micromanager\n Connected.\" +// +// Digital I/O lines (shutters, triggers, interlocks etc - 26 lines available ("a-z"): +// - 4 lines ("s,t,u,v") are used to gate the High-Current LED drivers (below) +// dig_ commands= in, out, mode, hilo, lohi etc.... +// example: W>dig_in f ->read T/F on digital line "f" (there are 26 lines "a-z") +// +// Analogue outputs 8 * 16-bit DAC lines (these lines are prefixed with "p" on the "P"ower daughter-board) +// - 4 lines ("ps, pt, pu, pv") have high-current MOSFET drivers (for e.g. LED lamps) +// - (Note: these outputs are gated by the dig lines "s,t,u,v") +// - 4 lines ("pw, px, py, pz") have low-current 16-bit DACs (for e.g. PZT stage control) +// dac_ commands= ref, out, rate, mode +// Note: The output lines are prefixed with "p" for power board and "m" for mother +// examples: W>dac_mode ps 3 ->line "s" as LED driver mode (3 = current mode) +// W>dac_out ps 12.5 ->set line s to 12.5mA output +// +// Motors, Steppers, Servos: +// (not clear how these motors interact with "Stage" and "Analogue" x,y,z +// mot_ commands= accl, bckl, dest, pos, out.. etc +// example: W>mot_out m3 27.5 ->moves motor to 27.5um absolute +// +// Stage control: +// (not clear how this works/interacts with pw,px,py,pz or with motor controls) +// stg_ command= out, val, min, max +// examples: W>stg_out_x r+22.345 ->move stage x-axis relative by 22.345um +// +// Serial Config (configure the serial ports SPI and RS232): +// ser_ commands: config, mode, send +// example: W>ser_config d on pdsel=2 stsel=0 ->enable serial bus "d", 8 data, even parity, 1 stop +// +// Networking: +// lan_ commands: host, config, ip, mac, subnet etc... +// examples: W>lan_mac ->get the lan MAC address. +// +// Macro commands: +// wml_ commands: run, stop, pause, file_new +// examples: W>wml_file_new col3z4 ->load a new macro command file +// W>wml_run col3z4 ->run the loaded macro command file +// User Management: +// usr_ commands: set, del, name +// example: W>usr_name 10 ->returns authenticated user name index 10 +// +// Note: all commands are "cr+lf" terminated +////////////////////////////////////////////////////////////////////////////////////// + +#include "WOSM.h" +#include "ModuleInterface.h" +#include + +//$$ string and IO handling +#include +#include +#include +using namespace std; +//$$ + +#ifdef WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif + +// Name the Devices something short and sensible +const char* g_DeviceNameWOSMHub = "WOSM-Hub"; +const char* g_DeviceNameWOSMSwitch = "WOSM-Switch"; +const char* g_DeviceNameWOSMShutter = "WOSM-Shutter"; +const char* g_DeviceNameWOSMDAC0 = "WOSM-DAC0"; +const char* g_DeviceNameWOSMDAC1 = "WOSM-DAC1"; +const char* g_DeviceNameWOSMDAC2 = "WOSM-DAC2"; +const char* g_DeviceNameWOSMDAC3 = "WOSM-DAC3"; +const char* g_DeviceNameWOSMDAC4 = "WOSM-DAC4"; +const char* g_DeviceNameStage = "ZStage"; +const char* g_DeviceNameXYStage = "XYStage"; +const char* g_DeviceNameWOSMInput = "WOSM-Input"; + +const char* g_PropertyMinUm = "Z Stage Low Posn(um)"; +const char* g_PropertyMaxUm = "Z Stage High Posn(um)"; +const char* g_PropertyXMinUm = "X Stage Min Posn(um)"; +const char* g_PropertyXMaxUm = "X Stage Max Posn(um)"; +const char* g_PropertyYMinUm = "Y Stage Min Posn(um)"; +const char* g_PropertyYMaxUm = "Y Stage Max Posn(um)"; + +// Global info about the state of the WOSM. This should be folded into a class +const int g_Min_MMVersion = 5; +const int g_Max_MMVersion = 100; +const char* g_versionProp = "Version"; +const char* g_normalLogicString = "Normal"; +const char* g_invertedLogicString = "Inverted"; + +const char* g_On = "On"; +const char* g_Off = "Off"; + +// static lock +MMThreadLock CWOSMHub::lock_; + +/////////////////////////////////////////////////////////////////////////////// +// Exported MMDevice API +/////////////////////////////////////////////////////////////////////////////// +MODULE_API void InitializeModuleData() +{ + // Justin's Notes: I think this is where the HUb and other devices are registered with the MMcore code + // I think there are currently ~16 recognised Device "types" including: + // Hub, StateDevice, ShutterDevice, GenericDevice, SignalIODevice, StageDevice(focus?), XYStageDevice, AutofocusDevice, GalvoDevice + // There seems to be some ambiguity/overlap in what the different Device types actually do.. and the shutter function is used to change + // the LED output pattern - which is not logical to me. + // Anyway, seems we call 'em what we like! + + RegisterDevice(g_DeviceNameWOSMHub, MM::HubDevice, "Hub (required)"); + + // Note: The "StateDevice" is used to "blank" the SignalIODevice by setting the output to zero. + RegisterDevice(g_DeviceNameWOSMSwitch, MM::StateDevice, "Switch on/off channels 0 to 10"); + RegisterDevice(g_DeviceNameWOSMShutter, MM::ShutterDevice, "Shutter"); + RegisterDevice(g_DeviceNameWOSMDAC0, MM::SignalIODevice, "DAC channel 0"); + RegisterDevice(g_DeviceNameWOSMDAC1, MM::SignalIODevice, "DAC channel 1"); + RegisterDevice(g_DeviceNameWOSMDAC2, MM::SignalIODevice, "DAC channel 2"); + RegisterDevice(g_DeviceNameWOSMDAC3, MM::SignalIODevice, "DAC channel 3"); + RegisterDevice(g_DeviceNameWOSMDAC4, MM::SignalIODevice, "DAC channel 4"); + RegisterDevice(g_DeviceNameStage, MM::StageDevice, "Z Stage"); + RegisterDevice(g_DeviceNameXYStage, MM::XYStageDevice, "XY Stage"); + RegisterDevice(g_DeviceNameWOSMInput, MM::GenericDevice, "ADC"); +} + +MODULE_API MM::Device* CreateDevice(const char* deviceName) +{ + // Justin's Notes: After registering the devices we now "create them" and give them another name. + // I admit I find it hard to keep track of the different names - especially when + // things get renamed again within the config file. Just keep your hair on and go + // with it! + + if ( deviceName == 0 ) + return 0; + + if ( strcmp(deviceName, g_DeviceNameWOSMHub) == 0 ) + { + return new CWOSMHub; + } + else if ( strcmp(deviceName, g_DeviceNameWOSMSwitch) == 0 ) + { + return new CWOSMSwitch; + } + else if ( strcmp(deviceName, g_DeviceNameWOSMShutter) == 0 ) + { + return new CWOSMShutter; + } + else if ( strcmp(deviceName, g_DeviceNameWOSMDAC0) == 0 ) + { + return new CWOSMDA(0); // channel 0 + } + else if ( strcmp(deviceName, g_DeviceNameWOSMDAC1) == 0 ) + { + return new CWOSMDA(1); // channel 1 + } + else if ( strcmp(deviceName, g_DeviceNameWOSMDAC2) == 0 ) + { + return new CWOSMDA(2); // channel 2 + } + else if ( strcmp(deviceName, g_DeviceNameWOSMDAC3) == 0 ) + { + return new CWOSMDA(3); // channel 3 + } + else if ( strcmp(deviceName, g_DeviceNameWOSMDAC4) == 0 ) + { + return new CWOSMDA(4); // channel 4 + } + else if ( strcmp(deviceName, g_DeviceNameStage) == 0 ) + { + return new CWOSMStage(); + } + else if ( strcmp(deviceName, g_DeviceNameXYStage) == 0 ) + { + return new CWOSMXYStage(); + } + else if ( strcmp(deviceName, g_DeviceNameWOSMInput) == 0 ) + { + return new CWOSMInput; + } + return 0; +} + +MODULE_API void DeleteDevice(MM::Device* pDevice) +{ + delete pDevice; +} + +/* CWOSMHUb implementation: + Justin's Notes: The WOSM HUB is the master "Device" it gives a convenient way to communicate with several + physical or "nominal" devices connected via a single cable to the PC. The hub heirarchy makes things + a little more complicated than adapters written for single, stand-alone, physical devices. + The WOSM controller runs four or five separate "MM_Devices". The firmware running on the WOSM can + control LED light sources, move an XY stage, a focusing device, open/close a physical shutter, + control a laser system etc. +*/ + +//CWOSMHUb implementation +CWOSMHub::CWOSMHub() : + initialized_(false), + switchState_(0), + shutterState_(0), + hasZStage_(false), + hasXYStage_(false) +{ + portAvailable_ = false; + invertedLogic_ = false; + timedOutputActive_ = false; + + // we can guess what this does... + InitializeDefaultErrorMessages(); + SetErrorText(ERR_PORT_OPEN_FAILED, "Failed opening WOSM TCP/IP device"); + SetErrorText(ERR_WOSM_NOT_FOUND, "Did not find a WOSM with the correct firmware. Is the WOSM connected to this IP address?"); + SetErrorText(ERR_NO_PORT_SET, "Hub Device not found. The WOSM Hub device is needed to create this device"); + std::ostringstream errorText; + errorText << "The firmware version on the WOSM is not compatible with this adapter. Please use firmware version "; + errorText << g_Min_MMVersion << " to " << g_Max_MMVersion; + SetErrorText(ERR_VERSION_MISMATCH, errorText.str().c_str()); + + // It would be very helpful to know exactly what this does... + CPropertyAction* pAct = new CPropertyAction(this, &CWOSMHub::OnPort); + CreateProperty(MM::g_Keyword_Port, "Undefined", MM::String, false, pAct, true); + + // and this! + pAct = new CPropertyAction(this, &CWOSMHub::OnLogic); + CreateProperty("Logic", g_invertedLogicString, MM::String, false, pAct, true); + + AddAllowedValue("Logic", g_invertedLogicString); + AddAllowedValue("Logic", g_normalLogicString); +} + +CWOSMHub::~CWOSMHub() +{ + Shutdown(); +} + +void CWOSMHub::GetName(char* name) const +{ + CDeviceUtils::CopyLimitedString(name, g_DeviceNameWOSMHub); +} + +bool CWOSMHub::Busy() +{ + return false; +} + +/* The Hub sends 2 "commands" / "requests" to the WOSM controller. += > upon TCP / IP connection we must send the login password += > "wosm" Expected response is command prompt "W>" += > We confirm connection with "kyp_lcd_screen t=6s \"\n Micromanager\n connected.\"", crlf + Once connected...we set the xyz stage control to "voltage mode" = 2 + and get the XYZ stage ranges += > if no response or range = 0..MM thinks there's no stage += > we set the stuv power board DAC lines to "current mode" = 3 + +*/ + +// Commands: "wosm" login, LCD screen message, set xyz modes & get xyz ranges. +int CWOSMHub::GetControllerVersion(int& version) +{ + + int ret = DEVICE_OK; + + LogMessage("Login to WOSM", false); + + // Flush the I/O buffer + PurgeComPort(port_.c_str()); + + ret = SendSerialCommand(port_.c_str(), " ", "\r\n"); + if ( ret != DEVICE_OK ) return ret; + LogMessage("SENT space", false); + + // wait for Password request.... + string answer; + ret = GetSerialAnswer(port_.c_str(), "admin password:", answer); + if ( ret != DEVICE_OK ) return ERR_WOSM_NOT_FOUND; +; + ret = SendSerialCommand(port_.c_str(), "wosm", "\r\n"); + if ( ret != DEVICE_OK ) return ret; + + ret = GetSerialAnswer(port_.c_str(), "W>", answer); + if ( ret != DEVICE_OK ) return ERR_WOSM_NOT_FOUND; + + ret = SendSerialCommand(port_.c_str(), "kyp_lcd_screen t = 5s \"\n MicroManager\n connected!\"", "\r\n"); + if (ret != DEVICE_OK) return ret; + + //CDeviceUtils::SleepMs(5000); + LogMessage("Sent splash Screen and didn't wait for 5 seconds!!", false); + + ret = GetSerialAnswer(port_.c_str(), "W>", answer); + if ( ret != DEVICE_OK ) return ret; + + //completed initialisation okay... + version = 99; + + LogMessage("InitialisingStageRanges X,Y,Z", false); + + // Below is what happens when you steal code from differnt places and can't be bothered to + // have a unified approach! + // XYZ stage setup set DAC mode to voltage output (mode 2) (0-10V) + // Obtain the Stage range (should be 16 bits = 65535) + double travelX, travelY, travelZ; + ret = SetAxisMode(0); + if ( ret != DEVICE_OK ) return ret; + ret = GetAxisInfo(0, travelX); + if ( ret != DEVICE_OK ) return ret; + + ret = SetAxisMode(1); + if ( ret != DEVICE_OK ) return ret; + ret = GetAxisInfo(1, travelY); + if ( ret != DEVICE_OK ) return ret; + + ret = SetAxisMode(2); + if ( ret != DEVICE_OK ) return ret; + ret = GetAxisInfo(2, travelZ); + if ( ret != DEVICE_OK ) return ret; + + if (( travelX > 0) && (travelY > 0 )) hasXYStage_ = true; + if (travelZ > 0) hasZStage_ = true; + + // now initialise the LED DAC lines (ps->pv) to constant current mode (mode 3) + ret = SendSerialCommand(port_.c_str(), "dac_mode ps 3", "\r\n"); + if ( ret != DEVICE_OK ) return ret; + ret = GetSerialAnswer(port_.c_str(), "W>", answer); + if ( ret != DEVICE_OK ) return ret; + ret = SendSerialCommand(port_.c_str(), "dac_mode pt 3", "\r\n"); + if ( ret != DEVICE_OK ) return ret; + ret = GetSerialAnswer(port_.c_str(), "W>", answer); + if ( ret != DEVICE_OK ) return ret; + ret = SendSerialCommand(port_.c_str(), "dac_mode pu 3", "\r\n"); + if ( ret != DEVICE_OK ) return ret; + ret = GetSerialAnswer(port_.c_str(), "W>", answer); + if ( ret != DEVICE_OK ) return ret; + ret = SendSerialCommand(port_.c_str(), "dac_mode pv 3", "\r\n"); + if ( ret != DEVICE_OK ) return ret; + ret = GetSerialAnswer(port_.c_str(), "W>", answer); + if ( ret != DEVICE_OK ) return ret; + return ret; +} + +// Command: "dac_max" obtain stageRange for X, Y and Z axes +int CWOSMHub::GetAxisInfo(int axis, double& travel) +{ + if ( axis < 0 || axis > 2 ) return DEVICE_ERR; + + std::stringstream cmd; + cmd.clear(); + + switch ( axis ) { + case 0: + cmd << "dac_max px"; // X-axis + break; + case 1: + cmd << "dac_max py"; // Y-axis + break; + case 2: + cmd << "dac_max pz"; // Z-axis + break; + } + + // Flush the I/O buffer then send command + PurgeComPort(port_.c_str()); + int ret = SendSerialCommand(port_.c_str(), cmd.str().c_str(), "\r\n"); + if ( ret != DEVICE_OK ) return ret; + + std::string answer; + ret = GetSerialAnswer(port_.c_str(), "W>", answer); + if ( ret != DEVICE_OK ) return ERR_UNKNOWN_AXIS; + + std::stringstream ss(answer); + std::string trav; + getline(ss, trav, '\r'); + + std::stringstream sstravel(trav); + sstravel >> travel; + + travel = travel / 65535 * 100; // all rather pointless! we know its 16 bits and 100um! + + return DEVICE_OK; +} + +// Command: "dac_mode" make X, Y and Z axes all voltage out +int CWOSMHub::SetAxisMode(int axis) +{ + if ( axis < 0 || axis > 2 ) return DEVICE_ERR; + + std::stringstream cmd; + cmd.clear(); + + switch ( axis ) { + case 0: + cmd << "dac_mode px 2"; // X-axis + break; + case 1: + cmd << "dac_mode py 2"; // Y-axis + break; + case 2: + cmd << "dac_mode pz 2"; // Z-axis + break; + } + + // Flush the I/O buffer then send command + PurgeComPort(port_.c_str()); + int ret = SendSerialCommand(port_.c_str(), cmd.str().c_str(), "\r\n"); + if ( ret != DEVICE_OK ) return ret; + + std::string answer; + ret = GetSerialAnswer(port_.c_str(), "W>", answer); + if ( ret != DEVICE_OK ) return ERR_UNKNOWN_AXIS; + + return DEVICE_OK; +} + +bool CWOSMHub::SupportsDeviceDetection(void) +{ + return true; +} + +MM::DeviceDetectionStatus CWOSMHub::DetectDevice(void) +{ + if ( initialized_ ) + return MM::CanCommunicate; + + // all conditions must be satisfied... + MM::DeviceDetectionStatus result = MM::Misconfigured; + char answerTO[MM::MaxStrLength]; + + try + { + std::string portLowerCase = port_; + for ( std::string::iterator its = portLowerCase.begin(); its != portLowerCase.end(); ++its ) + { + *its = ( char ) tolower(*its); + } + if ( 0 < portLowerCase.length() && 0 != portLowerCase.compare("undefined") && 0 != portLowerCase.compare("unknown") ) + { + result = MM::CanNotCommunicate; + + // record the default answer time out + GetCoreCallback()->GetDeviceProperty(port_.c_str(), "AnswerTimeout", answerTO); + + MM::Device* pS = GetCoreCallback()->GetDevice(this, port_.c_str()); + + pS->Initialize(); + + MMThreadGuard myLock(lock_); + PurgeComPort(port_.c_str()); + int v = 0; + int ret = GetControllerVersion(v); + + if ( DEVICE_OK != ret ) LogMessageCode(ret, false); + else result = MM::CanCommunicate; + + pS->Shutdown(); + + // always restore the AnswerTimeout to the default + GetCoreCallback()->SetDeviceProperty(port_.c_str(), "AnswerTimeout", answerTO); + + } + } + catch ( ... ) + { + LogMessage("Exception in DetectDevice!", false); + } + + return result; +} + +int CWOSMHub::Initialize() +{ + // Name + int ret = CreateProperty(MM::g_Keyword_Name, g_DeviceNameWOSMHub, MM::String, true); + if ( DEVICE_OK != ret ) + return ret; + + MMThreadGuard myLock(lock_); + + // Check that we have a controller: + PurgeComPort(port_.c_str()); + ret = GetControllerVersion(version_); + if ( DEVICE_OK != ret ) + return ret; + + if ( version_ < g_Min_MMVersion ) + return ERR_VERSION_MISMATCH; + + CPropertyAction* pAct = new CPropertyAction(this, &CWOSMHub::OnVersion); + std::ostringstream sversion; + sversion << version_; + CreateProperty(g_versionProp, sversion.str().c_str(), MM::Integer, true, pAct); + + ret = UpdateStatus(); + if ( ret != DEVICE_OK ) return ret; + + // turn off verbose serial debug messages + // GetCoreCallback()->SetDeviceProperty(port_.c_str(), "Verbose", "0"); + + initialized_ = true; + return DEVICE_OK; +} + +int CWOSMHub::DetectInstalledDevices() +{ + if ( MM::CanCommunicate == DetectDevice() ) + { + std::vector peripherals; + peripherals.clear(); + peripherals.push_back(g_DeviceNameWOSMSwitch); + peripherals.push_back(g_DeviceNameWOSMShutter); + peripherals.push_back(g_DeviceNameWOSMDAC0); + peripherals.push_back(g_DeviceNameWOSMDAC1); + peripherals.push_back(g_DeviceNameWOSMDAC2); + peripherals.push_back(g_DeviceNameWOSMDAC3); + peripherals.push_back(g_DeviceNameWOSMDAC4); + peripherals.push_back(g_DeviceNameStage); + peripherals.push_back(g_DeviceNameXYStage); + peripherals.push_back(g_DeviceNameWOSMInput); + + for ( size_t i = 0; i < peripherals.size(); i++ ) + { + MM::Device* pDev = ::CreateDevice(peripherals[i].c_str()); + if ( pDev ) + { + AddInstalledDevice(pDev); + } + } + } + + return DEVICE_OK; +} + +int CWOSMHub::Shutdown() +{ + initialized_ = false; + return DEVICE_OK; +} + +int CWOSMHub::OnPort(MM::PropertyBase* pProp, MM::ActionType pAct) +{ + if ( pAct == MM::BeforeGet ) + { + pProp->Set(port_.c_str()); + } + else if ( pAct == MM::AfterSet ) + { + pProp->Get(port_); + portAvailable_ = true; + } + return DEVICE_OK; +} + +int CWOSMHub::OnVersion(MM::PropertyBase* pProp, MM::ActionType pAct) +{ + if ( pAct == MM::BeforeGet ) + { + pProp->Set(( long ) version_); + } + return DEVICE_OK; +} + +int CWOSMHub::OnLogic(MM::PropertyBase* pProp, MM::ActionType pAct) +{ + if ( pAct == MM::BeforeGet ) + { + if ( invertedLogic_ ) + pProp->Set(g_invertedLogicString); + else + pProp->Set(g_normalLogicString); + } + else if ( pAct == MM::AfterSet ) + { + std::string logic; + pProp->Get(logic); + if ( logic.compare(g_invertedLogicString) == 0 ) + invertedLogic_ = true; + else invertedLogic_ = false; + } + return DEVICE_OK; +} + +/* CWOSMSwitch implementation: + Justin's Notes: + The "Switch Device" is registered with MM as a "State Device" + I don't know the full command set used by the config file it seems to + be documented by "example". + + Different switch states are attached to different "channels" - + That is done here, again in the config file, also in the WOSM firmware + and finally the physical wiring of the GPIO pins. +*/ + +// CWOSMSwitch implementation +CWOSMSwitch::CWOSMSwitch() : + nrPatternsUsed_(0), + currentDelay_(0), + sequenceOn_(false), + blanking_(false), + initialized_(false), + numPos_(12), + busy_(false) +{ + + InitializeDefaultErrorMessages(); + + // add custom error messages + SetErrorText(ERR_UNKNOWN_POSITION, "Invalid position (state) specified"); + SetErrorText(ERR_INITIALIZE_FAILED, "Initialization of the device failed"); + SetErrorText(ERR_WRITE_FAILED, "Failed to write data to the device"); + SetErrorText(ERR_CLOSE_FAILED, "Failed closing the device"); + SetErrorText(ERR_COMMUNICATION, "Error in communication with WOSM board"); + SetErrorText(ERR_NO_PORT_SET, "Hub Device not found. The WOSM Hub device is needed to create this device"); + + for ( unsigned int i = 0; i < NUMPATTERNS; i++ ) + pattern_[i] = 0; + + // Description + int ret = CreateProperty(MM::g_Keyword_Description, "WOSM digital output driver", MM::String, true); + assert(DEVICE_OK == ret); + + // Name + ret = CreateProperty(MM::g_Keyword_Name, g_DeviceNameWOSMSwitch, MM::String, true); + assert(DEVICE_OK == ret); + + // parent ID display + CreateHubIDProperty(); +} + +CWOSMSwitch::~CWOSMSwitch() +{ + Shutdown(); +} + +void CWOSMSwitch::GetName(char* name) const +{ + CDeviceUtils::CopyLimitedString(name, g_DeviceNameWOSMSwitch); +} + +int CWOSMSwitch::Initialize() +{ + CWOSMHub* hub = static_cast< CWOSMHub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) { + return ERR_NO_PORT_SET; + } + char hubLabel[MM::MaxStrLength]; + hub->GetLabel(hubLabel); + SetParentID(hubLabel); // for backward comp. + + // set property list + // ----------------- + + // In this version of the adpater we will control up to 8 switchable devices + // Using WOSM dig_out lines s,t,u,v,w,x,y,z (in this adapter we use x,y,z for stage control) + // .. so we might "blank" those lines (x,y,z) to prevent accidentally switching to zero! + // Anyway.... We will define up to 256 char labels for 256 options ! + // 0="0", 2="2"...... 255="255" + // you will see later "WriteToPort" method that we need to do some bit shifting to get the + // correct control-byte pattern (since in principle we have up to 26 control bits!! + // The ASCII labels are just the decimal numbers representing each bit-mapped switch pattern + // (see below for explanation) + + //Create the text labels that we will use in the config file + const int bufSize = 4; + char buf[bufSize]; + for ( long i = 0; i < 256; i++ ) { + snprintf(( char* ) buf, bufSize, "%d", ( unsigned ) i); + SetPositionLabel(i, buf); + } + + /* E.g. if you have different LED light sources powered by the different WOSM O/P pins + B, G, Y, R < colour of the LED + 3, 2, 1, 0 < bit position + 8, 4, 2, 1 < decimal number when set to "1" at that bit position + 1, 0, 0, 0, = "Blue only" requires this bit pattern = 16 Decimal + And if you want RGB simultaneously you send: + 1, 1, 0, 1 = 8+4+1 = 13 Decimal + To make this easier you list the options you want available in the Config file like this: + +*** config file: +Label,WOSM-Switch,0,All_OFF +Label,WOSM-Switch,1,Red +Label,WOSM-Switch,2,Yellow +Label,WOSM-Switch,4,Red +Label,WOSM-Switch,8,Blue +Label,WOSM-Switch,13,RedGreenBlue +ConfigGroup,Channel,Red_LED,WOSM-Switch,Label,Red +ConfigGroup,Channel,Yellow_LED,WOSM-Switch,Label,Yellow +ConfigGroup,Channel,Green_LED,WOSM-Switch,Label,Green +ConfigGroup,Channel,Blue_LED,WOSM-Switch,Label,Blue +ConfigGroup,Channel,Red_Yell_UV,WOSM-Switch,Label,RedGreenBlue +*** + + */ + + // State + CPropertyAction* pAct = new CPropertyAction(this, &CWOSMSwitch::OnState); + int nRet = CreateProperty(MM::g_Keyword_State, "0", MM::Integer, false, pAct); + if ( nRet != DEVICE_OK ) + return nRet; + SetPropertyLimits(MM::g_Keyword_State, 0, 256 - 1); + + // Label + pAct = new CPropertyAction(this, &CStateBase::OnLabel); + nRet = CreateProperty(MM::g_Keyword_Label, "", MM::String, false, pAct); + if ( nRet != DEVICE_OK ) + return nRet; + + pAct = new CPropertyAction(this, &CWOSMSwitch::OnSequence); + nRet = CreateProperty("Sequence", g_On, MM::String, false, pAct); + if ( nRet != DEVICE_OK ) + return nRet; + AddAllowedValue("Sequence", g_On); + AddAllowedValue("Sequence", g_Off); + + // Starts "blanking" mode: goal is to synchronize laser light with camera exposure + std::string blankMode = "Blanking Mode"; + pAct = new CPropertyAction(this, &CWOSMSwitch::OnBlanking); + nRet = CreateProperty(blankMode.c_str(), "Idle", MM::String, false, pAct); + if ( nRet != DEVICE_OK ) return nRet; + + AddAllowedValue(blankMode.c_str(), g_On); + AddAllowedValue(blankMode.c_str(), g_Off); + + // Blank on TTL high or low + pAct = new CPropertyAction(this, &CWOSMSwitch::OnBlankingTriggerDirection); + nRet = CreateProperty("Blank On", "Low", MM::String, false, pAct); + if ( nRet != DEVICE_OK ) + return nRet; + AddAllowedValue("Blank On", "Low"); + AddAllowedValue("Blank On", "High"); + + /* + // Some original comments: + // but SADLY, the code itself is commented out + // In fact, looks like a useful thing to include...To Do. + // //////////////////////////////////////////////////////////////// + // Starts producing timed digital output patterns + // Parameters that influence the pattern are 'Repeat Timed Pattern', 'Delay', 'State' + // where the latter two are manipulated with the Get and SetPattern functions + + std::string timedOutput = "Timed Output Mode"; + pAct = new CPropertyAction(this, &CWOSMSwitch::OnStartTimedOutput); + nRet = CreateProperty(timedOutput.c_str(), "Idle", MM::String, false, pAct); + if (nRet != DEVICE_OK) + return nRet; + AddAllowedValue(timedOutput.c_str(), "Stop"); + AddAllowedValue(timedOutput.c_str(), "Start"); + AddAllowedValue(timedOutput.c_str(), "Running"); + AddAllowedValue(timedOutput.c_str(), "Idle"); + + // Sets a delay (in ms) to be used in timed output mode + // This delay will be transferred to the WOSM using the Get and SetPattern commands + pAct = new CPropertyAction(this, &CWOSMSwitch::OnDelay); + nRet = CreateProperty("Delay (ms)", "0", MM::Integer, false, pAct); + if (nRet != DEVICE_OK) + return nRet; + SetPropertyLimits("Delay (ms)", 0, 65535); + + // Repeat the timed Pattern this many times: + pAct = new CPropertyAction(this, &CWOSMSwitch::OnRepeatTimedPattern); + nRet = CreateProperty("Repeat Timed Pattern", "0", MM::Integer, false, pAct); + if (nRet != DEVICE_OK) + return nRet; + SetPropertyLimits("Repeat Timed Pattern", 0, 255); + */ + + nRet = UpdateStatus(); + if ( nRet != DEVICE_OK ) return nRet; + + initialized_ = true; + + return DEVICE_OK; +} + +int CWOSMSwitch::Shutdown() +{ + initialized_ = false; + return DEVICE_OK; +} +// I am assuming that when this function (method) is called the passed parameter "value" +// is the bitmap pattern that is to be written to the switches (if bit is 0 the output is turned off +// (electronically gated) if the bit is 1 then the output goes to it's preset value. + +// command: 1 now "dig_out" Set current digital output pattern - The "CWOSMShutter" NO LONGER uses this command! +int CWOSMSwitch::WriteToPort(long value) +{ + CWOSMHub* hub = static_cast< CWOSMHub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) return ERR_NO_PORT_SET; + + // Masking pattern for the WOSM dig_out lines + // ----------- H I G H W O R D ----------- ----------- L O W W O R D ----------- + // 16 15 14 13 12 11 10 09 08 07 06 05 04 02 01 :: 16 15 14 13 12 11 10 09 08 07 06 05 04 02 01 + // * * * * z y x w v u t s r q p :: o n m l k j i h g f e d c b a + // ^ ^ ^ ^ ^ ^ ^ ^ + // useful control lines + // 8 7 6 5 4 3 2 1 + // ...... Mask bits ..... + // 0 0 0 1 1 0 1 0 = 26 Decimal (= turn on lines w,v & t, all others off!) + // Keep the low byte and shift up to the High Word position as shown above + // e.g. a starting value of "90" would have the bit pattern + + value = 255 & value; + value = value << 19; + + if ( hub->IsLogicInverted() ) value = ~value; + + MMThreadGuard myLock(hub->GetLock()); + hub->PurgeComPortH(); + int ret = DEVICE_OK; + char command[50]; + unsigned int leng; + + leng = snprintf(( char* ) command, 50, "dig_out %d\r\n", value); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + + // Purge the IO buffer in case WOSM has sent a response! + hub->PurgeComPortH(); + hub->SetTimedOutput(false); + + std::ostringstream os; + os << "Switch::WriteToPort Command= " << command; + LogMessage(os.str().c_str(), false); + + // wait for usual response from WOSM ("W>") + + MM::MMTime startTime = GetCurrentMMTime(); + unsigned long bytesRead = 0; + unsigned char answer[50]; + while ( ( bytesRead < 50 ) && ( ( GetCurrentMMTime() - startTime ).getMsec() < 250 ) ) { + unsigned long br = 0; + ret = hub->ReadFromComPortH(( unsigned char* ) answer + bytesRead, 1, br); + if ( answer[bytesRead] == '>' ) break; + bytesRead += br; + } + + answer[bytesRead] = 0; // string terminator + + hub->PurgeComPortH(); + if ( ret != DEVICE_OK ) return ret; + + //if ( answer[0] != 'W' ) return ERR_COMMUNICATION; + + return DEVICE_OK; +} + +// Commands: 5 & 6 now "P" and "N" = store new "P"atterns and "N"umber of patterns +int CWOSMSwitch::LoadSequence(unsigned size, unsigned char* seq) +{ + + std::ostringstream os; + os << "Switch::LoadSequence size= " << size << " Seq= " << seq; + LogMessage(os.str().c_str(), false); + + CWOSMHub* hub = static_cast< CWOSMHub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) + return ERR_NO_PORT_SET; + + // preamble for all port reads and writes + MMThreadGuard myLock(hub->GetLock()); + hub->PurgeComPortH(); + int ret = DEVICE_OK; + char command[50]; + unsigned int leng; + + for ( unsigned i = 0; i < size; i++ ) + { + unsigned char value = seq[i]; + + value = 255 & value; + if ( hub->IsLogicInverted() ) value = ~value; + + leng = snprintf(( char* ) command, 50, "P,%d,%d\r\n", i, value); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + } + + leng = snprintf(( char* ) command, 50, "N,%d\r\n", size); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + + // Purge the IO buffer in case WOSM has sent a response! + hub->SetTimedOutput(false); + hub->PurgeComPortH(); + + return DEVICE_OK; +} + +// Action handlers + +// Commands: 8 & 9 now "R" and "E" Run and End Trigger mode resp. +int CWOSMSwitch::OnState(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + + std::ostringstream os; + os << "Switch::OnState_"; + LogMessage(os.str().c_str(), false); + + CWOSMHub* hub = static_cast< CWOSMHub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) + return ERR_NO_PORT_SET; + + // Some comments here would be helpful + if ( eAct == MM::BeforeGet ) + { + // nothing to do, let the caller use cached property ??? + } + else if ( eAct == MM::AfterSet ) + { + // **** a comment here is essential !.. + // where are we getting the "pos" value from? + long pos; + pProp->Get(pos); + + // this is obscure - I can guess what it does.. but, I'm not going to say + hub->SetSwitchState(pos); + + if ( hub->GetShutterState() > 0 ) // I don't like this because of confusion with "shutterDevice" + os << "_Pos= " << pos << " WriteToPort(pos)?"; + LogMessage(os.str().c_str(), false); + return WriteToPort(pos); // I understand this! + } + else if ( eAct == MM::IsSequenceable ) + { + if ( sequenceOn_ ) + pProp->SetSequenceable(NUMPATTERNS); + else + pProp->SetSequenceable(0); + } + else if ( eAct == MM::AfterLoadSequence ) + { + std::vector sequence = pProp->GetSequence(); + + if ( sequence.size() > NUMPATTERNS ) return DEVICE_SEQUENCE_TOO_LARGE; + + unsigned char* seq = new unsigned char[sequence.size()]; + for ( unsigned int i = 0; i < sequence.size(); i++ ) + { + std::istringstream ios(sequence[i]); + int val; + ios >> val; + seq[i] = ( unsigned char ) val; + } + + int ret = LoadSequence(( unsigned ) sequence.size(), seq); + if ( ret != DEVICE_OK ) + return ret; + + delete[ ] seq; + } + else if ( eAct == MM::StartSequence ) + { + MMThreadGuard myLock(hub->GetLock()); + + // preamble for all port reads and writes + hub->PurgeComPortH(); + int ret = DEVICE_OK; + char command[50]; + unsigned int leng; + + leng = snprintf(( char* ) command, 50, "R\r\n"); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + + } + else if ( eAct == MM::StopSequence ) + { + MMThreadGuard myLock(hub->GetLock()); + + // preamble for all port reads and writes + hub->PurgeComPortH(); + int ret = DEVICE_OK; + char command[50]; + unsigned int leng; + + leng = snprintf(( char* ) command, 50, "E\r\n"); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + + // This code needs to be improved! + // We expect a reply like "E,23456\r\n" + + MM::MMTime startTime = GetCurrentMMTime(); + unsigned long bytesRead = 0; + unsigned char answer[50]; + + // Now chug through the I/O buffer, byte-by-byte and stop at cr or lf.... yuk! + while ( ( bytesRead < 50 ) && ( ( GetCurrentMMTime() - startTime ).getMsec() < 250 ) ) { + unsigned long br; + ret = hub->ReadFromComPortH(( unsigned char* ) answer + bytesRead, 1, br); + if ( answer[bytesRead] == '\r' ) break; + bytesRead += br; + } + + answer[bytesRead] = 0; // string terminator + + hub->SetTimedOutput(false); + hub->PurgeComPortH(); + if ( ret != DEVICE_OK ) return ret; + + int num; + char com[1]; + sscanf(( const char* ) answer, "%1s,%d", com, &num); + // should give com[0]='E' num = 23456 (or something!) + + if ( com[0] != 'E' ) return ERR_COMMUNICATION; + + os << "Sequence_Transitions: " << num; + LogMessage(os.str().c_str(), false); + } + + return DEVICE_OK; +} +int CWOSMSwitch::OnSequence(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if ( eAct == MM::BeforeGet ) + { + if ( sequenceOn_ ) + pProp->Set(g_On); + else + pProp->Set(g_Off); + } + else if ( eAct == MM::AfterSet ) + { + std::string state; + pProp->Get(state); + if ( state == g_On ) + sequenceOn_ = true; + else + sequenceOn_ = false; + } + + std::ostringstream os; + os << "Switch::OnSequence_SeqOn= " << sequenceOn_; + LogMessage(os.str().c_str(), false); + + + return DEVICE_OK; +} + +// Commands: 12 & 9 now = "G" and "E" +int CWOSMSwitch::OnStartTimedOutput(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + + std::ostringstream os; + os << "Switch::OnStartTimedOutput "; + LogMessage(os.str().c_str(), false); + + CWOSMHub* hub = static_cast< CWOSMHub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) + return ERR_NO_PORT_SET; + + if ( eAct == MM::BeforeGet ) { + if ( hub->IsTimedOutputActive() ) pProp->Set("Running"); + else pProp->Set("Idle"); + } + else if ( eAct == MM::AfterSet ) + { + MMThreadGuard myLock(hub->GetLock()); + + std::string prop; + pProp->Get(prop); + + if ( prop == "Start" ) { + // preamble for port read and write + hub->PurgeComPortH(); + int ret = DEVICE_OK; + char command[50]; + unsigned int leng; + + leng = snprintf(( char* ) command, 50, "G\r\n"); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + + hub->SetTimedOutput(true); // Check this *********** + + } + else { + // preamble for port read and write + hub->PurgeComPortH(); + int ret = DEVICE_OK; + char command[50]; + unsigned int leng; + leng = snprintf(( char* ) command, 50, "E\r\n"); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + + hub->SetTimedOutput(false); + } + } + + return DEVICE_OK; +} + +// Commands: 20 & 21 now = "B,1" "B,0" now Blanking on(1) or Blanking off (0) +int CWOSMSwitch::OnBlanking(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + std::ostringstream os; + os << "Switch::OnBlanking "; + LogMessage(os.str().c_str(), false); + + CWOSMHub* hub = static_cast< CWOSMHub* >( GetParentHub() ); + + if ( !hub || !hub->IsPortAvailable() ) return ERR_NO_PORT_SET; + + if ( eAct == MM::BeforeGet ) { + if ( blanking_ ) pProp->Set(g_On); + else pProp->Set(g_Off); + } + else if ( eAct == MM::AfterSet ) + { + MMThreadGuard myLock(hub->GetLock()); + + std::string prop; + pProp->Get(prop); + + if ( prop == g_On && !blanking_ ) { + hub->PurgeComPortH(); + int ret = DEVICE_OK; + char command[50]; + unsigned int leng; + + leng = snprintf(( char* ) command, 50, "B,1\r\n"); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + + blanking_ = true; + hub->SetTimedOutput(false); + LogMessage("Switched blanking on", false); + + } + else if ( prop == g_Off && blanking_ ) { + hub->PurgeComPortH(); + int ret = DEVICE_OK; + char command[50]; + unsigned int leng; + + leng = snprintf(( char* ) command, 50, "B,0\r\n"); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + + blanking_ = false; + hub->SetTimedOutput(false); + LogMessage("Switched blanking off", false); + } + } + return DEVICE_OK; +} + +// Command: 22 now = "F,0" or "F,1" Flip polarity of trigger signal +int CWOSMSwitch::OnBlankingTriggerDirection(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + + std::ostringstream os; + os << "Switch::OnBlankingTriggerDirection"; + LogMessage(os.str().c_str(), false); + + CWOSMHub* hub = static_cast< CWOSMHub* >( GetParentHub() ); + + if ( !hub || !hub->IsPortAvailable() ) return ERR_NO_PORT_SET; + + // Perhaps shorten to one line and clarify logic? + // Only execute if eAct has already been got and set. + + // if ((eAct != MM::BeforeGet) && (eAct == MM::AfterSet)) { + + if ( eAct == MM::BeforeGet ) { + // nothing to do, let the caller use cached property + } + else if ( eAct == MM::AfterSet ) + { + + MMThreadGuard myLock(hub->GetLock()); + + std::string direction; + pProp->Get(direction); + hub->PurgeComPortH(); + int ret = DEVICE_OK; + char command[50]; + unsigned int leng; + + int dir = 0; + if ( direction == "Low" ) dir = 1; + leng = snprintf(( char* ) command, 50, "F,%d\r\n", dir); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + + hub->SetTimedOutput(false); + } + return DEVICE_OK; +} +int CWOSMSwitch::OnDelay(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + std::ostringstream os; + os << "Switch::OnDelay "; + LogMessage(os.str().c_str(), false); + + if ( eAct == MM::BeforeGet ) { + pProp->Set(( long int ) currentDelay_); + } + else if ( eAct == MM::AfterSet ) + { + long prop; + pProp->Get(prop); + currentDelay_ = ( int ) prop; + } + + return DEVICE_OK; +} + +// Command: 11 now = "I" set number of "I"terations +int CWOSMSwitch::OnRepeatTimedPattern(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + std::ostringstream os; + os << "Switch::OnRepeatTimedPattern "; + LogMessage(os.str().c_str(), false); + + CWOSMHub* hub = static_cast< CWOSMHub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) return ERR_NO_PORT_SET; + + // Perhaps shorten and clarify logic to one line? + // if ((eAct != MM::BeforeGet) && (eAct == MM::AfterSet)) { + + if ( eAct == MM::BeforeGet ) { + } + else if ( eAct == MM::AfterSet ) + { + MMThreadGuard myLock(hub->GetLock()); + + long prop; + pProp->Get(prop); + + hub->PurgeComPortH(); + int ret = DEVICE_OK; + char command[50]; + unsigned int leng; + leng = snprintf(( char* ) command, 50, "I,%d\r\n", prop); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + + hub->SetTimedOutput(false); + } + + return DEVICE_OK; +} + +// CWOSMShutter implementation +// Justin Notes: I'm a bit mystified by the "shutter" and when it is called. Currently it issues a "Switch" +// command - I find that confusing. + +// CWOSMShutter implementation +CWOSMShutter::CWOSMShutter() : initialized_(false), name_(g_DeviceNameWOSMShutter) +{ + InitializeDefaultErrorMessages(); + EnableDelay(); + + SetErrorText(ERR_NO_PORT_SET, "Hub Device not found. The WOSM Hub device is needed to create this device"); + + // Name + int ret = CreateProperty(MM::g_Keyword_Name, g_DeviceNameWOSMShutter, MM::String, true); + assert(DEVICE_OK == ret); + + // Description + ret = CreateProperty(MM::g_Keyword_Description, "WOSM shutter driver", MM::String, true); + assert(DEVICE_OK == ret); + + // parent ID display + CreateHubIDProperty(); +} +CWOSMShutter::~CWOSMShutter() +{ + Shutdown(); +} +void CWOSMShutter::GetName(char* name) const +{ + CDeviceUtils::CopyLimitedString(name, g_DeviceNameWOSMShutter); +} +bool CWOSMShutter::Busy() +{ + MM::MMTime interval = GetCurrentMMTime() - changedTime_; + + return interval < MM::MMTime::fromMs(GetDelayMs()); +} +int CWOSMShutter::Initialize() +{ + CWOSMHub* hub = static_cast< CWOSMHub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) { + return ERR_NO_PORT_SET; + } + char hubLabel[MM::MaxStrLength]; + hub->GetLabel(hubLabel); + SetParentID(hubLabel); // for backward comp. + + // set property list + // ----------------- + + // removed comment from OnOff + // set shutter into the off state ???wot + // WriteToPort(0); + + // OnOff + CPropertyAction* pAct = new CPropertyAction(this, &CWOSMShutter::OnOnOff); + int ret = CreateProperty("OnOff", "0", MM::Integer, false, pAct); + if ( ret != DEVICE_OK ) return ret; + + std::vector vals; + vals.push_back("0"); + vals.push_back("1"); + ret = SetAllowedValues("OnOff", vals); + if ( ret != DEVICE_OK ) return ret; + + ret = UpdateStatus(); + if ( ret != DEVICE_OK ) return ret; + + changedTime_ = GetCurrentMMTime(); + initialized_ = true; + + return DEVICE_OK; +} +int CWOSMShutter::Shutdown() +{ + if ( initialized_ ) + { + initialized_ = false; + } + return DEVICE_OK; +} +int CWOSMShutter::SetOpen(bool open) +{ + std::ostringstream os; + os << "Shutter::SetOpen open= " << open; + LogMessage(os.str().c_str(), false); + + if ( open ) return SetProperty("OnOff", "1"); + else return SetProperty("OnOff", "0"); +} +int CWOSMShutter::GetOpen(bool& open) +{ + std::ostringstream os; + os << "Shutter::GetOpen open= " << open; + LogMessage(os.str().c_str(), false); + + char buf[MM::MaxStrLength]; + int ret = GetProperty("OnOff", buf); + if ( ret != DEVICE_OK ) return ret; + + long pos = atol(buf); + pos > 0 ? open = true : open = false; + + return DEVICE_OK; +} +int CWOSMShutter::Fire(double /*deltaT*/) +{ + return DEVICE_UNSUPPORTED_COMMAND; +} + +// e.g. "x0101100" turns off channels 0,1,5,7.. +// Note: channels 2,3,6 will switch on at their preset output level.. "shutter" is messing with the switches! +// must try to separate church and state. + +// Command: "dig_out r 0/1" Shutter open or closed on channel "r" +int CWOSMShutter::WriteToPort(long value) +{ + CWOSMHub* hub = static_cast< CWOSMHub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) + return ERR_NO_PORT_SET; + + MMThreadGuard myLock(hub->GetLock()); + + // Just keep the lowest bit - a single shutter it's either on or off (open or closed) + value = 1 & value; + if ( hub->IsLogicInverted() ) value = ~value; + + hub->PurgeComPortH(); + int ret = DEVICE_OK; + // create command buffer + char command[50]; + unsigned int leng; + char chan[2]; + + // iterate through shutter control lines just do line 'w' = ('s' + 4) + for ( int i = 4; i < 5; i++ ) { + chan[0] = 's' + i; chan[1] = 0; // iterate through s, t, u, v, w lines on WOSM + + leng = snprintf(( char* ) command, 50, "dig_out %s %d\r\n", (char*) chan, value); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + + MM::MMTime startTime = GetCurrentMMTime(); + unsigned long bytesRead = 0; + unsigned char answer[50]; + while ( ( bytesRead < 50 ) && ( ( GetCurrentMMTime() - startTime ).getMsec() < 250 ) ) { + unsigned long br = 0; + ret = hub->ReadFromComPortH(( unsigned char* ) answer + bytesRead, 1, br); + if ( answer[bytesRead] == '>' ) break; + bytesRead += br; + } + + answer[bytesRead] = 0; // string terminator + + hub->PurgeComPortH(); + if ( ret != DEVICE_OK ) return ret; + + //if ( answer[bytesRead-1] != 'W' ) return ERR_COMMUNICATION; + + } + hub->SetTimedOutput(false); + + std::ostringstream os; + os << "Shutter::WriteToPort Command= " << command; + LogMessage(os.str().c_str(), false); + + return DEVICE_OK; +} + +// I think this method should be part of the "Switch" Class and should call Switch::WritetoPort +// This method is called in an inapproriate manner during MDA.. need to sort it out +// OnOnOff should be moved to "Switch::" : it's called during MDA and operates the "switch" indirectly +// by issuing the "S" command + +// Action handlers - don't seem to work correctly at present. I've modded it so, it now fails! +int CWOSMShutter::OnOnOff(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + CWOSMHub* hub = static_cast< CWOSMHub* >( GetParentHub() ); + if ( eAct == MM::BeforeGet ) + { + // use cached state + pProp->Set(( long ) hub->GetShutterState()); + } + else if ( eAct == MM::AfterSet ) + { + long pos; + pProp->Get(pos); + int ret; + if ( pos == 0 ) + ret = WriteToPort(0); // Shutter closed (and/or write zeros to all LEDs) + else + ret = WriteToPort(1); // Shutter open (and/or restore LED O/P pattern) <- I don't want "Shutter" to mess with switches + // ret = WriteToPort(hub->GetSwitchState()); // restore old setting NO - let the WOSM do this logic! + + if ( ret != DEVICE_OK ) return ret; + + hub->SetShutterState(pos); + changedTime_ = GetCurrentMMTime(); + } + + std::ostringstream os; + os << "Shutter::OnOnOff hub->GetSwitchState()= " << hub->GetSwitchState(); + LogMessage(os.str().c_str(), false); + + return DEVICE_OK; +} + +// CWOSMDA implementation: +// Justin's Notes: +// This "Device" only sends one command "O,chan,volts" +// Summary: +// => OnVolts Action Handler is called by an event handler in MM. +// It subsequently calls: +// => SetSignal which optionally "gates" the O/P to "zero Volts" +// => WriteSignal scales the value to 12 bits (but now does nothing!) +// => WritetoPort - Sends "O,(int) chan,(float) volts" to WOSM +// => OnMaxVolts & => OnChannel just keep Volts and Channels within bounds +// +// If we need anything fast or device-specific do it on the WOSM. +// USB transmit/receive latency will dominate. + +// CWOSMDA implementation +CWOSMDA::CWOSMDA(int channel) : + busy_(false), + minV_(0.0), + maxV_(100.0), + volts_(0.0), + gatedVolts_(0.0), + channel_(channel), + maxChannel_(7), + gateOpen_(true) +{ + InitializeDefaultErrorMessages(); + + // add custom error messages + SetErrorText(ERR_UNKNOWN_POSITION, "Invalid position (state) specified"); + SetErrorText(ERR_INITIALIZE_FAILED, "Initialization of the device failed"); + SetErrorText(ERR_WRITE_FAILED, "Failed to write data to the device"); + SetErrorText(ERR_CLOSE_FAILED, "Failed closing the device"); + SetErrorText(ERR_NO_PORT_SET, "Hub Device not found. The WOSM Hub device is needed to create this device"); + + /* Channel property is not needed + CPropertyAction* pAct = new CPropertyAction(this, &CWOSMDA::OnChannel); + CreateProperty("Channel", channel_ == 1 ? "1" : "2", MM::Integer, false, pAct, true); + for (int i=1; i<= 2; i++){ + std::ostringstream os; + os << i; + AddAllowedValue("Channel", os.str().c_str()); + } + */ + + CPropertyAction* pAct = new CPropertyAction(this, &CWOSMDA::OnMaxVolt); + CreateProperty("Power %", "100", MM::Float, false, pAct, true); + + if ( channel_ == 0 ) + { + name_ = g_DeviceNameWOSMDAC0; + } + else if ( channel_ == 1 ) + { + name_ = g_DeviceNameWOSMDAC1; + } + else if ( channel_ == 2 ) + { + name_ = g_DeviceNameWOSMDAC2; + } + else if ( channel_ == 3 ) + { + name_ = g_DeviceNameWOSMDAC3; + } + else if ( channel_ == 4 ) + { + name_ = g_DeviceNameWOSMDAC4; + } + + //name_ = channel_ == 1 ? g_DeviceNameWOSMDA1 : g_DeviceNameWOSMDA2; + + // Description + int nRet = CreateProperty(MM::g_Keyword_Description, "WOSM DAC driver", MM::String, true); + assert(DEVICE_OK == nRet); + + // Name + nRet = CreateProperty(MM::g_Keyword_Name, name_.c_str(), MM::String, true); + assert(DEVICE_OK == nRet); + + // parent ID display + CreateHubIDProperty(); +} +CWOSMDA::~CWOSMDA() +{ + Shutdown(); +} +void CWOSMDA::GetName(char* name) const +{ + CDeviceUtils::CopyLimitedString(name, name_.c_str()); +} +int CWOSMDA::Initialize() +{ + CWOSMHub* hub = static_cast< CWOSMHub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) { + return ERR_NO_PORT_SET; + } + char hubLabel[MM::MaxStrLength]; + hub->GetLabel(hubLabel); + SetParentID(hubLabel); // for backward comp. + + // set property list + // ----------------- + + // State + // ----- + CPropertyAction* pAct = new CPropertyAction(this, &CWOSMDA::OnVolts); + int nRet = CreateProperty("Volts", "0.0", MM::Float, false, pAct); + if ( nRet != DEVICE_OK ) + return nRet; + SetPropertyLimits("Volts", minV_, maxV_); + + nRet = UpdateStatus(); + + if ( nRet != DEVICE_OK ) return nRet; + + initialized_ = true; + + return DEVICE_OK; +} +int CWOSMDA::Shutdown() +{ + initialized_ = false; + return DEVICE_OK; +} + +// Command: "dac_out ps 24.5" +int CWOSMDA::WriteToPort(double value) +{ + CWOSMHub* hub = static_cast< CWOSMHub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) + return ERR_NO_PORT_SET; + + LogMessage("Into write dac_out", false); + + MMThreadGuard myLock(hub->GetLock()); + + hub->PurgeComPortH(); + int ret = DEVICE_OK; + char command[50]; + unsigned int leng; + char chan[2]; + if ( ( channel_ >= 0 ) && ( channel_ < 4 ) ) { + chan[0] = 's'+channel_ ; chan[1] = 0; // set s,t,u or v lines on WOSM + + leng = snprintf(( char* ) command, 50, "dac_out p%s %3.3f\r\n", ( char* ) chan, value); + + std::stringstream ss; + ss << "Sending...Command: " << command ; + LogMessage(ss.str().c_str(), false); + + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + + MM::MMTime startTime = GetCurrentMMTime(); + unsigned long bytesRead = 0; + unsigned char answer[50]; + while ( ( bytesRead < 50 ) && ( ( GetCurrentMMTime() - startTime ).getMsec() < 250 ) ) { + unsigned long br=0; + ret = hub->ReadFromComPortH(( unsigned char* ) answer + bytesRead, 1, br); + if ( answer[bytesRead] == '>' ) break; + bytesRead += br; + } + + answer[bytesRead] = 0; // string terminator + + hub->PurgeComPortH(); + if ( ret != DEVICE_OK ) return ret; + + //if ( answer[0] != 'W' ) return ERR_COMMUNICATION; + } + hub->SetTimedOutput(false); + + return DEVICE_OK; +} +int CWOSMDA::WriteSignal(double volts) +{ + double value = ( double ) volts; // ( (volts - minV_) / maxV_ * 4095); + + std::ostringstream os; + os << "Volts= " << volts << " MaxVoltage= " << maxV_ << " digitalValue= " << value; + LogMessage(os.str().c_str(), false); + + return WriteToPort(value); +} +int CWOSMDA::SetSignal(double volts) +{ + volts_ = volts; + if ( gateOpen_ ) { + gatedVolts_ = volts_; + return WriteSignal(volts_); + } + else { + gatedVolts_ = 0; + } + + return DEVICE_OK; +} +int CWOSMDA::SetGateOpen(bool open) +{ + if ( open ) { + gateOpen_ = true; + gatedVolts_ = volts_; + return WriteSignal(volts_); + } + gateOpen_ = false; + gatedVolts_ = 0; + return WriteSignal(0.0); + +} + +// Action handlers +int CWOSMDA::OnVolts(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if ( eAct == MM::BeforeGet ) + { + // nothing to do, let the caller use cached property + } + else if ( eAct == MM::AfterSet ) + { + double volts; + pProp->Get(volts); + return SetSignal(volts); + } + + return DEVICE_OK; +} +int CWOSMDA::OnMaxVolt(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if ( eAct == MM::BeforeGet ) + { + pProp->Set(maxV_); + } + else if ( eAct == MM::AfterSet ) + { + pProp->Get(maxV_); + if ( HasProperty("Volts") ) + SetPropertyLimits("Volts", 0.0, maxV_); + + } + return DEVICE_OK; +} +int CWOSMDA::OnChannel(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if ( eAct == MM::BeforeGet ) + { + pProp->Set(( long int ) channel_); + } + else if ( eAct == MM::AfterSet ) + { + long channel; + pProp->Get(channel); + if ( channel >= 0 && ( ( unsigned ) channel <= maxChannel_ ) ) + channel_ = channel; + } + return DEVICE_OK; +} + +/* +Below, I have folded-in (and modified) the PIEZOCONCEPT XYZ stage adapter +so we can control focus and move an x-y stage connected to our WOSM board (see firmware). +So, we have two new blocks of code: + +CWOSMStage & CWOSMXYStage + +Notes: +Stage and Focus noise: +On the WOSM side, the XYZ channels have 16-bit resolution + +*/ + +// CWOSMStage Implementation +CWOSMStage::CWOSMStage() : + stepSizeUm_(0.0001), + pos_um_(0.0), + busy_(false), + initialized_(false), + lowerLimit_(0.0), + upperLimit_(100.0) +{ + InitializeDefaultErrorMessages(); + + CreateHubIDProperty(); +} + +CWOSMStage::~CWOSMStage() +{ + Shutdown(); +} + +void CWOSMStage::GetName(char* Name) const +{ + CDeviceUtils::CopyLimitedString(Name, g_DeviceNameStage); +} + +int CWOSMStage::Initialize() +{ + CWOSMHub* hub = static_cast< CWOSMHub* >( GetParentHub() ); + + if ( !hub || !hub->IsPortAvailable() ) return ERR_NO_PORT_SET; + + char hubLabel[MM::MaxStrLength]; + hub->GetLabel(hubLabel); + SetParentID(hubLabel); // for backward comp. + + if ( initialized_ ) return DEVICE_OK; + + // Name + int ret = CreateProperty(MM::g_Keyword_Name, g_DeviceNameStage, MM::String, true); + if ( DEVICE_OK != ret ) return ret; + + // Description + ret = CreateProperty(MM::g_Keyword_Description, "WOSM Z stage driver", MM::String, true); + if ( DEVICE_OK != ret ) return ret; + + double travel; + + ret = hub->GetAxisInfoH(2, travel); + if ( DEVICE_OK != ret ) return ret; + + upperLimit_ = travel; + //Should we initialise stepsize as we do for X- Y- axes + //Suggest e.g. perhaps 10 digital bits per mouse-wheel click? + //stepSizeUm_ = travel / 6553.6; + + ret = UpdateStatus(); + if ( ret != DEVICE_OK ) return ret; + + initialized_ = true; + + std::ostringstream os; + os << "Stage::Initialize (exit)"; + LogMessage(os.str().c_str(), false); + + return DEVICE_OK; +} + +int CWOSMStage::Shutdown() +{ + if ( initialized_ ) initialized_ = false; + return DEVICE_OK; +} + +int CWOSMStage::GetPositionUm(double& pos) +{ + pos = pos_um_; + return DEVICE_OK; +} + +int CWOSMStage::SetPositionUm(double pos) +{ + int ret = MoveZ(pos); + if ( ret != DEVICE_OK ) return ret; + return OnStagePositionChanged(pos); +} + +int CWOSMStage::GetPositionSteps(long& steps) +{ + double posUm; + int ret = GetPositionUm(posUm); + if ( ret != DEVICE_OK ) return ret; + + steps = static_cast< long >( posUm / GetStepSize() ); + return DEVICE_OK; +} + +int CWOSMStage::SetPositionSteps(long steps) +{ + return SetPositionUm(steps * GetStepSize()); +} + +// "Z" command - move to new Z-position (Focus up/down) +int CWOSMStage::MoveZ(double pos) +{ + CWOSMHub* hub = static_cast< CWOSMHub* >( GetParentHub() ); + + LogMessage("Into moveZ" , false); + + if ( !hub || !hub->IsPortAvailable() ) return ERR_HUB_UNAVAILABLE; + + if ( pos > upperLimit_ ) pos = upperLimit_; + if ( pos < lowerLimit_ ) pos = lowerLimit_; + + LogMessage("after limits", false); + + char buf[50]; + int length = sprintf(buf, "dac_out pz %3.3f\r\n", pos); + + std::stringstream ss; + ss << "Command: " << buf << " Position set: " << pos; + LogMessage(ss.str().c_str(), false); + + MMThreadGuard myLock(hub->GetLock()); + hub->PurgeComPortH(); + + int ret = hub->WriteToComPortH(( unsigned char* ) buf, length); + if ( ret != DEVICE_OK ) return ret; + + MM::MMTime startTime = GetCurrentMMTime(); + unsigned long bytesRead = 0; + unsigned char answer[50]; + while ( ( bytesRead < 50 ) && ( ( GetCurrentMMTime() - startTime ).getMsec() < 250 ) ) { + unsigned long br; + ret = hub->ReadFromComPortH(( unsigned char* ) answer + bytesRead, 1, br); + if ( answer[bytesRead] == '>' ) break; + bytesRead += br; + } + + answer[bytesRead] = 0; // string terminator + + hub->PurgeComPortH(); + if ( ret != DEVICE_OK ) return ret; + + //if ( answer[0] != 'W' ) return ERR_COMMUNICATION; + + hub->SetTimedOutput(false); + + std::ostringstream os; + os << "MoveZ Z," << pos; + LogMessage(os.str().c_str(), false); + + pos_um_ = pos; + + return DEVICE_OK; +} + +bool CWOSMStage::Busy() +{ + return false; +} + +CWOSMXYStage::CWOSMXYStage() : CXYStageBase(), +stepSize_X_um_(0.1), +stepSize_Y_um_(0.1), +posX_um_(0.0), +posY_um_(0.0), +busy_(false), +initialized_(false), +lowerLimitX_(0.0), +upperLimitX_(100.0), +lowerLimitY_(0.0), +upperLimitY_(100.0) +{ + InitializeDefaultErrorMessages(); + + // parent ID display + CreateHubIDProperty(); + + // step size + CPropertyAction* pAct = new CPropertyAction(this, &CWOSMXYStage::OnXStageMinPos); + CreateProperty(g_PropertyXMinUm, "0", MM::Float, false, pAct, true); + + pAct = new CPropertyAction(this, &CWOSMXYStage::OnXStageMaxPos); + CreateProperty(g_PropertyXMaxUm, "100", MM::Float, false, pAct, true); + + pAct = new CPropertyAction(this, &CWOSMXYStage::OnYStageMinPos); + CreateProperty(g_PropertyYMinUm, "0", MM::Float, false, pAct, true); + + pAct = new CPropertyAction(this, &CWOSMXYStage::OnYStageMaxPos); + CreateProperty(g_PropertyYMaxUm, "100", MM::Float, false, pAct, true); +} + +CWOSMXYStage::~CWOSMXYStage() +{ + Shutdown(); +} + +void CWOSMXYStage::GetName(char* Name) const +{ + CDeviceUtils::CopyLimitedString(Name, g_DeviceNameXYStage); +} + +int CWOSMXYStage::Initialize() +{ + CWOSMHub* hub = static_cast< CWOSMHub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) return ERR_NO_PORT_SET; + + char hubLabel[MM::MaxStrLength]; + hub->GetLabel(hubLabel); + SetParentID(hubLabel); // for backward comp. + + if ( initialized_ ) return DEVICE_OK; + + // Name + int ret = CreateProperty(MM::g_Keyword_Name, g_DeviceNameXYStage, MM::String, true); + if ( DEVICE_OK != ret ) return ret; + + // Description + ret = CreateProperty(MM::g_Keyword_Description, "XY stage driver", MM::String, true); + if ( DEVICE_OK != ret ) return ret; + + double travelX, travelY; + ret = hub->GetAxisInfoH(0, travelX); + if ( DEVICE_OK != ret ) return ret; + + ret = hub->GetAxisInfoH(1, travelY); + if ( DEVICE_OK != ret ) return ret; + + // here the stepsize is set to 1 digital bit ?..Cut-and-paste from PIEZOCONCEPT. + upperLimitX_ = travelX; + stepSize_X_um_ = travelX / 65535; + upperLimitY_ = travelY; + stepSize_Y_um_ = travelY / 65535; + + ret = UpdateStatus(); + if ( ret != DEVICE_OK ) return ret; + + initialized_ = true; + + return DEVICE_OK; +} + +int CWOSMXYStage::Shutdown() +{ + if ( initialized_ ) initialized_ = false; + return DEVICE_OK; +} + +bool CWOSMXYStage::Busy() +{ + return false; +} + +int CWOSMXYStage::GetPositionSteps(long& x, long& y) +{ + x = ( long ) ( posX_um_ / stepSize_X_um_ ); + y = ( long ) ( posY_um_ / stepSize_Y_um_ ); + + std::stringstream ss; + ss << "GetPositionSteps :=" << x << "," << y; + LogMessage(ss.str(), false); + return DEVICE_OK; +} + +int CWOSMXYStage::SetPositionSteps(long x, long y) +{ + double posX = x * stepSize_X_um_; + double posY = y * stepSize_Y_um_; + + std::stringstream ss; + ss << "Current position = " << posX_um_ << "," << posY_um_ << " \n Commanded position = " << posX << "," << posY; + LogMessage(ss.str(), false); + + int ret = DEVICE_OK; + + if ( posX_um_ != posX ) + { + ret = MoveX(posX); + if ( ret != DEVICE_OK ) return ret; + } + if ( posY_um_ != posY ) + { + ret = MoveY(posY); + if ( ret != DEVICE_OK ) return ret; + } + return OnXYStagePositionChanged(posX_um_, posY_um_); +} + +int CWOSMXYStage::SetRelativePositionSteps(long x, long y) +{ + long curX, curY; + GetPositionSteps(curX, curY); + + return SetPositionSteps(curX + x, curY + y); +} + +// "X" command - move to new x-position (stage translate) +int CWOSMXYStage::MoveX(double posUm) +{ + CWOSMHub* hub = static_cast< CWOSMHub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) return ERR_HUB_UNAVAILABLE; + + if ( posUm < lowerLimitX_ ) posUm = lowerLimitX_; + if ( posUm > upperLimitX_ ) posUm = upperLimitX_; + + char buf[25]; + int length = sprintf(buf, "dac_out px %1.3f\r\n", posUm); + + std::stringstream ss; + ss << "Command: " << buf << " Position set: " << posUm; + LogMessage(ss.str().c_str(), false); + + MMThreadGuard myLock(hub->GetLock()); + hub->PurgeComPortH(); + + int ret = hub->WriteToComPortH(( unsigned char* ) buf, length); + if ( ret != DEVICE_OK ) return ret; + + MM::MMTime startTime = GetCurrentMMTime(); + unsigned long bytesRead = 0; + unsigned char answer[50]; + while ( ( bytesRead < 50 ) && ( ( GetCurrentMMTime() - startTime ).getMsec() < 250 ) ) { + unsigned long br; + ret = hub->ReadFromComPortH(( unsigned char* ) answer + bytesRead, 1, br); + if ( answer[bytesRead] == '>' ) break; + bytesRead += br; + } + + answer[bytesRead] = 0; // string terminator + + hub->PurgeComPortH(); + hub->SetTimedOutput(false); + + if ( ret != DEVICE_OK ) return ret; + + //if ( answer[0] != 'W' ) return ERR_COMMUNICATION; + + std::ostringstream os; + os << "X-Answer= " << answer; + LogMessage(os.str().c_str(), false); + + posX_um_ = posUm; + return DEVICE_OK; +} + +// "Y" command - move to new y-position (stage translate) +int CWOSMXYStage::MoveY(double posUm) +{ + CWOSMHub* hub = static_cast< CWOSMHub* >( GetParentHub() ); + + if ( !hub || !hub->IsPortAvailable() ) return ERR_HUB_UNAVAILABLE; + + if ( posUm < lowerLimitY_ ) posUm = lowerLimitY_; + if ( posUm > upperLimitY_ ) posUm = upperLimitY_; + + char buf[25]; + int length = sprintf(buf, "dac_out py %1.3f\r\n", posUm); + + std::stringstream ss; + ss << "Command: " << buf << " Position set: " << posUm; + LogMessage(ss.str().c_str(), true); + + MMThreadGuard myLock(hub->GetLock()); + hub->PurgeComPortH(); + + int ret = hub->WriteToComPortH(( unsigned char* ) buf, length); + if ( ret != DEVICE_OK ) return ret; + + MM::MMTime startTime = GetCurrentMMTime(); + unsigned long bytesRead = 0; + unsigned char answer[50]; + while ( ( bytesRead < 50 ) && ( ( GetCurrentMMTime() - startTime ).getMsec() < 250 ) ) { + unsigned long br; + ret = hub->ReadFromComPortH(( unsigned char* ) answer + bytesRead, 1, br); + if ( answer[bytesRead] == '>' ) break; + bytesRead += br; + } + + answer[bytesRead] = 0; // string terminator + + hub->PurgeComPortH(); + hub->SetTimedOutput(false); + + if ( ret != DEVICE_OK ) return ret; + + //if ( answer[0] != 'W' ) return ERR_COMMUNICATION; + + std::ostringstream os; + os << "Y-Answer= " << answer; + LogMessage(os.str().c_str(), false); + + posY_um_ = posUm; + return DEVICE_OK; +} + +int CWOSMXYStage::OnXStageMinPos(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + int ret = DEVICE_ERR; + if ( eAct == MM::BeforeGet ) + { + pProp->Set(lowerLimitX_); + ret = DEVICE_OK; + } + else if ( eAct == MM::AfterSet ) + { + double limit; + pProp->Get(limit); + lowerLimitX_ = limit; + + ret = DEVICE_OK; + } + + return ret; +} + +int CWOSMXYStage::OnXStageMaxPos(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + int ret = DEVICE_ERR; + if ( eAct == MM::BeforeGet ) + { + pProp->Set(upperLimitX_); + ret = DEVICE_OK; + } + else if ( eAct == MM::AfterSet ) + { + double limit; + pProp->Get(limit); + upperLimitX_ = limit; + + ret = DEVICE_OK; + } + + return ret; +} + +int CWOSMXYStage::OnYStageMinPos(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + int ret = DEVICE_ERR; + if ( eAct == MM::BeforeGet ) + { + pProp->Set(lowerLimitY_); + ret = DEVICE_OK; + } + else if ( eAct == MM::AfterSet ) + { + double limit; + pProp->Get(limit); + lowerLimitY_ = limit; + + ret = DEVICE_OK; + } + + return ret; +} + +int CWOSMXYStage::OnYStageMaxPos(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + int ret = DEVICE_ERR; + if ( eAct == MM::BeforeGet ) + { + pProp->Set(upperLimitY_); + ret = DEVICE_OK; + } + else if ( eAct == MM::AfterSet ) + { + double limit; + pProp->Get(limit); + upperLimitY_ = limit; + + ret = DEVICE_OK; + } + + return ret; +} + + +// The "Input" functions below need to be commented +CWOSMInput::CWOSMInput() : + mThread_(0), + pin_(0), + name_(g_DeviceNameWOSMInput) +{ + std::string errorText = "To use the Input function you need firmware version 5 or higher"; + SetErrorText(ERR_VERSION_MISMATCH, errorText.c_str()); + + + + CreateProperty("Pin", "All", MM::String, false, 0, true); + AddAllowedValue("Pin", "All"); + AddAllowedValue("Pin", "0"); + AddAllowedValue("Pin", "1"); + AddAllowedValue("Pin", "2"); + AddAllowedValue("Pin", "3"); + AddAllowedValue("Pin", "4"); + AddAllowedValue("Pin", "5"); + + CreateProperty("Pull-Up-Resistor", g_On, MM::String, false, 0, true); + AddAllowedValue("Pull-Up-Resistor", g_On); + AddAllowedValue("Pull-Up-Resistor", g_Off); + + // Name + int ret = CreateProperty(MM::g_Keyword_Name, name_.c_str(), MM::String, true); + assert(DEVICE_OK == ret); + + // Description + ret = CreateProperty(MM::g_Keyword_Description, "WOSM shutter driver", MM::String, true); + assert(DEVICE_OK == ret); + + // parent ID display + CreateHubIDProperty(); +} + +CWOSMInput::~CWOSMInput() +{ + Shutdown(); +} + +int CWOSMInput::Shutdown() +{ + if ( initialized_ ) + delete( mThread_ ); + initialized_ = false; + return DEVICE_OK; +} + +int CWOSMInput::Initialize() +{ + CWOSMHub* hub = static_cast< CWOSMHub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) return ERR_NO_PORT_SET; + + char hubLabel[MM::MaxStrLength]; + hub->GetLabel(hubLabel); + SetParentID(hubLabel); // for backward comp. + + char ver[MM::MaxStrLength] = "0"; + hub->GetProperty(g_versionProp, ver); + + // we should get ASCII "5" back from the WOSM - convert to (int) + int version = atoi(ver); + if ( version < g_Min_MMVersion ) return ERR_VERSION_MISMATCH; + + // I think the idea is to request setup of the INPUT_PULLUP state + // on the GPIO pins on the microcontroller... but it is not clear + + int ret = GetProperty("Pin", pins_); + if ( ret != DEVICE_OK ) return ret; + + // pin_ = ascii to integer of pins_ (i.e. 0,1,2,3,4,5) unknown value if pins_="All" + if ( strcmp("All", pins_) != 0 ) pin_ = atoi(pins_); + + ret = GetProperty("Pull-Up-Resistor", pullUp_); + if ( ret != DEVICE_OK ) return ret; + + // Digital Input + CPropertyAction* pAct = new CPropertyAction(this, &CWOSMInput::OnDigitalInput); + ret = CreateProperty("DigitalInput", "0", MM::Integer, true, pAct); + if ( ret != DEVICE_OK ) return ret; + + int start = 0; + int end = 5; + if ( strcmp("All", pins_) != 0 ) { + start = pin_; + end = pin_; + } + + for ( long i = start; i <= end; i++ ) + { + CPropertyActionEx* pExAct = new CPropertyActionEx(this, &CWOSMInput::OnAnalogInput, i); + std::ostringstream os; + os << "AnalogInput= " << i; + + ret = CreateProperty(os.str().c_str(), "0.0", MM::Float, true, pExAct); + if ( ret != DEVICE_OK ) return ret; + + // set pull up resistor state for this pin + if ( strcmp(g_On, pullUp_) == 0 ) SetPullUp(i, 1); + else SetPullUp(i, 0); + + } + + mThread_ = new WOSMInputMonitorThread(*this); + mThread_->Start(); + + initialized_ = true; + + return DEVICE_OK; +} + +void CWOSMInput::GetName(char* name) const +{ + CDeviceUtils::CopyLimitedString(name, name_.c_str()); +} + +bool CWOSMInput::Busy() +{ + return false; +} + +// Justin Notes: "GetDigitalInput" is polled every 0.5sec by the timed thread see below. +// In the original WOSM code we were testing the Analogue Inputs for a digital change.. +// That's fine.. but we should set this up in a more obvious way. +// suggest we assign a some of the GPIO lines as Digital I/O and some as Analogue Input. +// Currently this is unclear to me...I don't know what Analogue inputs we want to monitor. + +// Command: 40 now "L" logic in - returns "L,l\r\n" pin High/Low +int CWOSMInput::GetDigitalInput(long* state) +{ + CWOSMHub* hub = static_cast< CWOSMHub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) + return ERR_NO_PORT_SET; + + MMThreadGuard myLock(hub->GetLock()); + + hub->PurgeComPortH(); + int ret = DEVICE_OK; + char command[50]; + unsigned int leng; + + int testPin = pin_; //(test pin 0->5) + if ( strcmp("All", pins_) == 0 ) testPin = 6; // (test if all pins set) + + leng = snprintf(( char* ) command, 50, "L,%d\r\n", testPin); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + + // we expect a reply like "L,1\r\n" 1 or 0 if testPin is High/Low + MM::MMTime startTime = GetCurrentMMTime(); + unsigned long bytesRead = 0; + char answer[50]; + // Chug through the I/O buffer, byte-by-byte and stop at lf! + while ( ( bytesRead < 50 ) && ( ( GetCurrentMMTime() - startTime ).getMsec() < 250 ) ) { + unsigned long br=0; + ret = hub->ReadFromComPortH(( unsigned char* ) answer + bytesRead, 1, br); + if ( answer[bytesRead] == '\r' ) break; + bytesRead += br; + } + + answer[bytesRead] = 0; // string terminator + + // discard anything left in the IO buffer + hub->PurgeComPortH(); + hub->SetTimedOutput(false); + + if ( ret != DEVICE_OK ) return ret; + + int num; + char com[1]; + // sscanf(( const char* ) answer, "%1s,%d", com, &num); + // EDITED HERE************************************************** + num = 1; + // should give com[0]='L' and num = 1 or 0 + + if ( com[0] != 'L' ) return ERR_COMMUNICATION; + + *state = ( long ) num; + + return DEVICE_OK; +} +int CWOSMInput::ReportStateChange(long newState) +{ + std::ostringstream os; + os << newState; + return OnPropertyChanged("DigitalInput", os.str().c_str()); +} + +// Action handlers +int CWOSMInput::OnDigitalInput(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + // This sets a property listed in the MMDevice Property.h file (very limited commenting) + // "Set" is an "Action Functor" (handler?) + // "eACT" seems common to all "On" functions - so probably an Event listener? + // Anyway, it wants a (long) value for "state" which becomes a virtual Boolean, apparently! + + if ( eAct == MM::BeforeGet ) + { + long state; + + // get the state of the selected pin 0-5 or all pins (which I have made to be "6" in my WOSM code) + int ret = GetDigitalInput(&state); + if ( ret != DEVICE_OK ) return ret; + pProp->Set(state); + } + + return DEVICE_OK; +} + +// Commands: 41 now = "A,chan" analogue read channel +int CWOSMInput::OnAnalogInput(MM::PropertyBase* pProp, MM::ActionType eAct, long channel) +{ + CWOSMHub* hub = static_cast< CWOSMHub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) return ERR_NO_PORT_SET; + + if ( eAct == MM::BeforeGet ) + { + + MMThreadGuard myLock(hub->GetLock()); + hub->PurgeComPortH(); + int ret = DEVICE_OK; + char command[50]; + unsigned int leng; + + leng = snprintf(( char* ) command, 50, "A,%d\r\n", ( int ) channel); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + + // We expect a reply like "A,597\r\n" analogue value on channel + // I think we need to use the hub to communicate via the rather "ReadFromComPortH" command?? + unsigned long bytesRead = 0; + char answer[50]; + MM::MMTime startTime = GetCurrentMMTime(); + while ( ( bytesRead < 50 ) && ( ( GetCurrentMMTime() - startTime ).getMsec() < 1000 ) ) { + unsigned long br=0; + ret = hub->ReadFromComPortH(( unsigned char* ) answer + bytesRead, 1, br); + if ( answer[bytesRead] == '\r' ) break; + bytesRead += br; + } + + answer[bytesRead] = 0; // replace \r with string terminator + + hub->PurgeComPortH(); + hub->SetTimedOutput(false); + + if ( ret != DEVICE_OK ) return ret; + + int num; + char com[1]; + //sscanf(( const char* ) answer, "%1s,%d", com, &num); + // should give com[0]='A' and num = 597 or something! + //EDITED HERE ********************************** + num = 1; + std::ostringstream os; + os << "AnswerToA= " << answer << " bytesRead= " << bytesRead << " com= " << com << " num= " << num; + LogMessage(os.str().c_str(), false); + + //if ( com[0] != 'A' ) return ERR_COMMUNICATION; + + pProp->Set(( long ) num); + } + return DEVICE_OK; +} + +// Commands: 42 - now "D" set digital pull-up ?? needs commenting - not sure why we want to do this dynamically. +int CWOSMInput::SetPullUp(int pin, int state) +{ + CWOSMHub* hub = static_cast< CWOSMHub* >( GetParentHub() ); + if ( !hub || !hub->IsPortAvailable() ) return ERR_NO_PORT_SET; + + MMThreadGuard myLock(hub->GetLock()); + hub->PurgeComPortH(); + int ret = DEVICE_OK; + char command[50]; + unsigned int leng; + + leng = snprintf(( char* ) command, 50, "D,%d,%d\r\n", ( int ) pin, ( int ) state); + ret = hub->WriteToComPortH(( const unsigned char* ) command, leng); + if ( ret != DEVICE_OK ) return ret; + + hub->PurgeComPortH(); + hub->SetTimedOutput(false); + + return DEVICE_OK; +} + +WOSMInputMonitorThread::WOSMInputMonitorThread(CWOSMInput& aInput) : + state_(0), + aInput_(aInput) +{ +} +WOSMInputMonitorThread::~WOSMInputMonitorThread() +{ + Stop(); + wait(); +} + +// I think this "free-running" thread is a 0.5s delay loop that polls the Digital I/O lines and reports changes +// It is perhaps not "thread safe" because the "L" and "A" commands seems to interfere... I haven't debugged this. +// It has probably been messed-up by my meddling! + +int WOSMInputMonitorThread::svc() +{ + while ( !stop_ ) + { + long state; + int ret = aInput_.GetDigitalInput(&state); + if ( ret != DEVICE_OK ) + { + stop_ = true; + return ret; + } + + if ( state != state_ ) + { + aInput_.ReportStateChange(state); + state_ = state; + } + CDeviceUtils::SleepMs(500); + } + return DEVICE_OK; +} +void WOSMInputMonitorThread::Start() +{ + stop_ = false; + activate(); +} diff --git a/DeviceAdapters/WieneckeSinske/CAN29.cpp b/DeviceAdapters/WieneckeSinske/CAN29.cpp index fd0e9e051..a3e4ffa21 100644 --- a/DeviceAdapters/WieneckeSinske/CAN29.cpp +++ b/DeviceAdapters/WieneckeSinske/CAN29.cpp @@ -31,7 +31,6 @@ #else #include #endif -#include "FixSnprintf.h" #include "CAN29.h" #include diff --git a/DeviceAdapters/WieneckeSinske/CAN29Axis.cpp b/DeviceAdapters/WieneckeSinske/CAN29Axis.cpp index 8710d7a84..2b5aafc23 100644 --- a/DeviceAdapters/WieneckeSinske/CAN29Axis.cpp +++ b/DeviceAdapters/WieneckeSinske/CAN29Axis.cpp @@ -31,7 +31,6 @@ #else #include #endif -#include "FixSnprintf.h" #include #include diff --git a/DeviceAdapters/WieneckeSinske/WS.cpp b/DeviceAdapters/WieneckeSinske/WS.cpp index 4ad50afdf..05b7a1f43 100644 --- a/DeviceAdapters/WieneckeSinske/WS.cpp +++ b/DeviceAdapters/WieneckeSinske/WS.cpp @@ -31,7 +31,6 @@ #else #include #endif -#include "FixSnprintf.h" #include "WS.h" #include diff --git a/DeviceAdapters/WieneckeSinske/WSAxis.cpp b/DeviceAdapters/WieneckeSinske/WSAxis.cpp index a71a6124f..29dc35741 100644 --- a/DeviceAdapters/WieneckeSinske/WSAxis.cpp +++ b/DeviceAdapters/WieneckeSinske/WSAxis.cpp @@ -31,7 +31,6 @@ #else #include #endif -#include "FixSnprintf.h" #include #include diff --git a/DeviceAdapters/WieneckeSinske/WieneckeSinske.cpp b/DeviceAdapters/WieneckeSinske/WieneckeSinske.cpp index 8f53344ef..a57647b32 100644 --- a/DeviceAdapters/WieneckeSinske/WieneckeSinske.cpp +++ b/DeviceAdapters/WieneckeSinske/WieneckeSinske.cpp @@ -32,7 +32,6 @@ #else #include #endif -#include "FixSnprintf.h" #include "WieneckeSinske.h" #include diff --git a/DeviceAdapters/WieneckeSinske/XYStageDevice.cpp b/DeviceAdapters/WieneckeSinske/XYStageDevice.cpp index e27576985..5163daead 100644 --- a/DeviceAdapters/WieneckeSinske/XYStageDevice.cpp +++ b/DeviceAdapters/WieneckeSinske/XYStageDevice.cpp @@ -32,7 +32,6 @@ #else #include #endif -#include "FixSnprintf.h" #include "XYStageDevice.h" #include diff --git a/DeviceAdapters/WieneckeSinske/ZPiezoCanDevice.cpp b/DeviceAdapters/WieneckeSinske/ZPiezoCanDevice.cpp index d87b7790c..149edbb2e 100644 --- a/DeviceAdapters/WieneckeSinske/ZPiezoCanDevice.cpp +++ b/DeviceAdapters/WieneckeSinske/ZPiezoCanDevice.cpp @@ -31,7 +31,6 @@ #else #include #endif -#include "FixSnprintf.h" #include "WieneckeSinske.h" #include diff --git a/DeviceAdapters/WieneckeSinske/ZPiezoWSDevice.cpp b/DeviceAdapters/WieneckeSinske/ZPiezoWSDevice.cpp index 6c13f8d3e..7f969f875 100644 --- a/DeviceAdapters/WieneckeSinske/ZPiezoWSDevice.cpp +++ b/DeviceAdapters/WieneckeSinske/ZPiezoWSDevice.cpp @@ -31,7 +31,6 @@ #else #include #endif -#include "FixSnprintf.h" #include "WieneckeSinske.h" #include diff --git a/DeviceAdapters/XCite120PC_Exacte/XCite.cpp b/DeviceAdapters/XCite120PC_Exacte/XCite.cpp index a276591ac..dc326ce8a 100644 --- a/DeviceAdapters/XCite120PC_Exacte/XCite.cpp +++ b/DeviceAdapters/XCite120PC_Exacte/XCite.cpp @@ -24,7 +24,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "XCite120PC.h" #include "XCiteExacte.h" diff --git a/DeviceAdapters/XCite120PC_Exacte/XCite120PC.cpp b/DeviceAdapters/XCite120PC_Exacte/XCite120PC.cpp index 5bd4f6621..ae703b643 100644 --- a/DeviceAdapters/XCite120PC_Exacte/XCite120PC.cpp +++ b/DeviceAdapters/XCite120PC_Exacte/XCite120PC.cpp @@ -28,7 +28,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "XCite120PC.h" #include "ModuleInterface.h" diff --git a/DeviceAdapters/XCite120PC_Exacte/XCiteExacte.cpp b/DeviceAdapters/XCite120PC_Exacte/XCiteExacte.cpp index 15f82e0aa..3abacd6a9 100644 --- a/DeviceAdapters/XCite120PC_Exacte/XCiteExacte.cpp +++ b/DeviceAdapters/XCite120PC_Exacte/XCiteExacte.cpp @@ -29,7 +29,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "XCiteExacte.h" #include "ModuleInterface.h" diff --git a/DeviceAdapters/XCiteLed/XLed.cpp b/DeviceAdapters/XCiteLed/XLed.cpp index 5a18565aa..2b09bfbd0 100644 --- a/DeviceAdapters/XCiteLed/XLed.cpp +++ b/DeviceAdapters/XCiteLed/XLed.cpp @@ -31,7 +31,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include #include diff --git a/DeviceAdapters/XCiteLed/XLedCtrl.cpp b/DeviceAdapters/XCiteLed/XLedCtrl.cpp index fb24d4607..02ec361b5 100644 --- a/DeviceAdapters/XCiteLed/XLedCtrl.cpp +++ b/DeviceAdapters/XCiteLed/XLedCtrl.cpp @@ -31,7 +31,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include #include diff --git a/DeviceAdapters/XCiteLed/XLedDev.cpp b/DeviceAdapters/XCiteLed/XLedDev.cpp index b7426e910..164ced580 100644 --- a/DeviceAdapters/XCiteLed/XLedDev.cpp +++ b/DeviceAdapters/XCiteLed/XLedDev.cpp @@ -31,7 +31,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include #include diff --git a/DeviceAdapters/XCiteXT600/XT600.cpp b/DeviceAdapters/XCiteXT600/XT600.cpp index c136c1b34..9823e6b43 100644 --- a/DeviceAdapters/XCiteXT600/XT600.cpp +++ b/DeviceAdapters/XCiteXT600/XT600.cpp @@ -33,7 +33,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include #include diff --git a/DeviceAdapters/XCiteXT600/XT600Ctrl.cpp b/DeviceAdapters/XCiteXT600/XT600Ctrl.cpp index e0dc59a4f..217e32751 100644 --- a/DeviceAdapters/XCiteXT600/XT600Ctrl.cpp +++ b/DeviceAdapters/XCiteXT600/XT600Ctrl.cpp @@ -32,7 +32,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include #include diff --git a/DeviceAdapters/XCiteXT600/XT600Dev.cpp b/DeviceAdapters/XCiteXT600/XT600Dev.cpp index c91607629..4053cc2b2 100644 --- a/DeviceAdapters/XCiteXT600/XT600Dev.cpp +++ b/DeviceAdapters/XCiteXT600/XT600Dev.cpp @@ -33,7 +33,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include #include diff --git a/DeviceAdapters/XLight/XLight.cpp b/DeviceAdapters/XLight/XLight.cpp index 787dfc3b9..b5c24b1ef 100644 --- a/DeviceAdapters/XLight/XLight.cpp +++ b/DeviceAdapters/XLight/XLight.cpp @@ -24,7 +24,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "XLight.h" #include "XLIGHTHub.h" diff --git a/DeviceAdapters/Xcite/Xcite.cpp b/DeviceAdapters/Xcite/Xcite.cpp index 7b2540674..eef154ea6 100755 --- a/DeviceAdapters/Xcite/Xcite.cpp +++ b/DeviceAdapters/Xcite/Xcite.cpp @@ -20,11 +20,9 @@ // This code is based on the Vincent Uniblitz controller adapter // by Nico Stuurman, 2006 - #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "Xcite.h" #include diff --git a/DeviceAdapters/Yokogawa/CSU22.cpp b/DeviceAdapters/Yokogawa/CSU22.cpp index 0ea842749..4c36631c5 100644 --- a/DeviceAdapters/Yokogawa/CSU22.cpp +++ b/DeviceAdapters/Yokogawa/CSU22.cpp @@ -34,7 +34,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "CSU22.h" #include "CSU22Hub.h" diff --git a/DeviceAdapters/Yokogawa/CSUX.cpp b/DeviceAdapters/Yokogawa/CSUX.cpp index 6795fcc17..194823687 100644 --- a/DeviceAdapters/Yokogawa/CSUX.cpp +++ b/DeviceAdapters/Yokogawa/CSUX.cpp @@ -34,7 +34,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "CSUX.h" #include "CSUXHub.h" 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..3044c6fc8 100644 --- a/DeviceAdapters/Zaber/FilterCubeTurret.cpp +++ b/DeviceAdapters/Zaber/FilterCubeTurret.cpp @@ -24,7 +24,6 @@ #ifdef WIN32 #pragma warning(disable: 4355) #endif -#include "FixSnprintf.h" #include "FilterCubeTurret.h" @@ -42,11 +41,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 +51,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 +78,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 +97,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 +123,7 @@ int FilterCubeTurret::Initialize() } ret = UpdateStatus(); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { return ret; } @@ -197,7 +177,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 +261,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 +281,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 +297,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..2466cdf0a 100644 --- a/DeviceAdapters/Zaber/FilterWheel.cpp +++ b/DeviceAdapters/Zaber/FilterWheel.cpp @@ -23,7 +23,6 @@ #ifdef WIN32 #pragma warning(disable: 4355) #endif -#include "FixSnprintf.h" #include "FilterWheel.h" @@ -41,11 +40,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 +50,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 +77,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 +97,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 +123,7 @@ int FilterWheel::Initialize() } ret = UpdateStatus(); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { return ret; } @@ -193,7 +173,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 +256,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 +276,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 +292,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..cbf4fbce2 100644 --- a/DeviceAdapters/Zaber/Illuminator.cpp +++ b/DeviceAdapters/Zaber/Illuminator.cpp @@ -23,7 +23,6 @@ #ifdef WIN32 #pragma warning(disable: 4355) #endif -#include "FixSnprintf.h" #include "Illuminator.h" @@ -32,8 +31,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 +45,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 +53,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 +80,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 +105,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 +122,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 +158,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 +172,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 +187,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 +199,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 +215,7 @@ int Illuminator::Initialize() } ret = UpdateStatus(); - if (ret != DEVICE_OK) + if (ret != DEVICE_OK) { return ret; } @@ -313,7 +284,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 +302,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 +321,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 +412,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 +431,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 +441,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 +460,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..4941c2be6 100644 --- a/DeviceAdapters/Zaber/Makefile.am +++ b/DeviceAdapters/Zaber/Makefile.am @@ -1,20 +1,26 @@ +AM_CXXFLAGS = $(MMDEVAPI_CXXFLAGS) $(ZML_CPPFLAGS) -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 -libmmgr_dal_Zaber_la_LIBADD = $(MMDEVAPI_LIBADD) -libmmgr_dal_Zaber_la_LDFLAGS = $(MMDEVAPI_LDFLAGS) + 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) $(ZML_LIBS) +libmmgr_dal_Zaber_la_LDFLAGS = $(MMDEVAPI_LDFLAGS) $(ZML_LDFLAGS) + +dist_deviceadapter_DATA = $(ZML_LIBS_TO_COPY) 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..92e13cb82 --- /dev/null +++ b/DeviceAdapters/Zaber/ObjectiveChanger.cpp @@ -0,0 +1,368 @@ +/////////////////////////////////////////////////////////////////////////////// +// 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 "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 Stage Device Number", xLdaAddress_, false, pAct, true); + SetPropertyLimits("Focus Stage 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; + labelStr << "Objective " << 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_ && (indexToSet + 1) != currentObjective_) + { + if (indexToSet < 0 || indexToSet >= numPositions_) + { + this->LogMessage("Requested position is outside the legal range.\n", true); + return DEVICE_UNKNOWN_POSITION; + } + + return setObjective(indexToSet + 1, false); + } + } + + return DEVICE_OK; +} + +int ObjectiveChanger::FocusOffsetGetSet(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + this->LogMessage("ObjectiveChanger::FocusOffsetGetSet\n", true); + + if (eAct == MM::AfterSet) + { + double newOffset; + pProp->Get(newOffset); + auto update = focusOffset_ != newOffset; + focusOffset_ = newOffset; + if (update) { + return setObjective(currentObjective_, true); + } + } + else if (eAct == MM::BeforeGet) + { + pProp->Set(focusOffset_); + } + + 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..8dfe20b97 100644 --- a/DeviceAdapters/Zaber/Stage.cpp +++ b/DeviceAdapters/Zaber/Stage.cpp @@ -23,7 +23,6 @@ #ifdef WIN32 #pragma warning(disable: 4355) #endif -#include "FixSnprintf.h" #include "Stage.h" @@ -37,7 +36,6 @@ Stage::Stage() : deviceAddress_(1), axisNumber_(1), lockstepGroup_(0), - homingTimeoutMs_(20000), stepSizeUm_(0.15625), convFactor_(1.6384), // not very informative name cmdPrefix_("/"), @@ -48,13 +46,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 +54,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 +95,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 +118,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 +164,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 +240,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 +361,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 +380,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 +396,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 +415,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..b16a2ffe9 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. @@ -24,7 +24,6 @@ #ifdef WIN32 #pragma warning(disable: 4355) #endif -#include "FixSnprintf.h" #include "XYStage.h" @@ -62,28 +61,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 +123,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 +155,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 +186,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 +256,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 +273,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 +316,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 +338,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 +360,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 +392,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 +405,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 +423,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 +437,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 +450,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 +475,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 +493,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 +503,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 +522,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 +532,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 +542,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 +561,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 +571,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 +593,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 +619,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 +829,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..b242ffb1e 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. @@ -24,7 +24,6 @@ #ifdef WIN32 #pragma warning(disable: 4355) #endif -#include "FixSnprintf.h" #include "Zaber.h" #include "XYStage.h" @@ -32,6 +31,7 @@ #include "FilterWheel.h" #include "FilterCubeTurret.h" #include "Illuminator.h" +#include "ObjectiveChanger.h" #include @@ -39,16 +39,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 +60,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 +107,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 +279,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 +340,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 +356,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 +379,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..9fcad38a5 100644 --- a/DeviceAdapters/Zaber/Zaber.vcxproj +++ b/DeviceAdapters/Zaber/Zaber.vcxproj @@ -59,24 +59,31 @@ true true WIN32;MODULE_EXPORTS;_USRDLL;_WINDOWS;%(PreprocessorDefinitions) + $(MM_3RDPARTYPUBLIC)\Zaber\zaber-motion\include;%(AdditionalIncludeDirectories) true true + zml.lib;%(AdditionalDependencies) + $(MM_3RDPARTYPUBLIC)\Zaber\zaber-motion\win64\lib;%(AdditionalLibraryDirectories) + + + + 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/ZeissAxioZoom/ZeissAxioZoom.h b/DeviceAdapters/ZeissAxioZoom/ZeissAxioZoom.h index 1385657b3..1323e3db8 100644 --- a/DeviceAdapters/ZeissAxioZoom/ZeissAxioZoom.h +++ b/DeviceAdapters/ZeissAxioZoom/ZeissAxioZoom.h @@ -44,7 +44,6 @@ // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES. - #ifndef _ZEISSCAN29_H_ #define _ZEISSCAN29_H_ @@ -54,7 +53,6 @@ #else #include #endif -#include "FixSnprintf.h" #include "MMDevice.h" #include "DeviceBase.h" diff --git a/DeviceAdapters/ZeissCAN/PhotoModule.cpp b/DeviceAdapters/ZeissCAN/PhotoModule.cpp index bd3124fe4..d46b3d893 100644 --- a/DeviceAdapters/ZeissCAN/PhotoModule.cpp +++ b/DeviceAdapters/ZeissCAN/PhotoModule.cpp @@ -2,7 +2,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "ZeissCAN.h" using namespace std; diff --git a/DeviceAdapters/ZeissCAN/ZStage.cpp b/DeviceAdapters/ZeissCAN/ZStage.cpp index eaa2bf500..07a945728 100644 --- a/DeviceAdapters/ZeissCAN/ZStage.cpp +++ b/DeviceAdapters/ZeissCAN/ZStage.cpp @@ -7,11 +7,9 @@ // Zeiss CAN bus controller for Axioscop 2 MOT, Z-stage // - #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "ZeissCAN.h" using namespace std; diff --git a/DeviceAdapters/ZeissCAN/ZeissCAN.cpp b/DeviceAdapters/ZeissCAN/ZeissCAN.cpp index 9c78d1ebf..d37419365 100644 --- a/DeviceAdapters/ZeissCAN/ZeissCAN.cpp +++ b/DeviceAdapters/ZeissCAN/ZeissCAN.cpp @@ -27,11 +27,9 @@ // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES. - #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "ZeissCAN.h" #include diff --git a/DeviceAdapters/ZeissCAN/mcu28.cpp b/DeviceAdapters/ZeissCAN/mcu28.cpp index ea220134b..68e87fe6f 100644 --- a/DeviceAdapters/ZeissCAN/mcu28.cpp +++ b/DeviceAdapters/ZeissCAN/mcu28.cpp @@ -28,11 +28,9 @@ // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES. - #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "ZeissCAN.h" #include diff --git a/DeviceAdapters/ZeissCAN29/ZeissCAN29.h b/DeviceAdapters/ZeissCAN29/ZeissCAN29.h index 2e9d328a4..f7fee7a13 100644 --- a/DeviceAdapters/ZeissCAN29/ZeissCAN29.h +++ b/DeviceAdapters/ZeissCAN29/ZeissCAN29.h @@ -38,7 +38,6 @@ // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES. - #ifndef _ZEISSCAN29_H_ #define _ZEISSCAN29_H_ @@ -48,7 +47,6 @@ #else #include #endif -#include "FixSnprintf.h" #include "MMDevice.h" #include "DeviceBase.h" diff --git a/DeviceAdapters/configure.ac b/DeviceAdapters/configure.ac index cac90eae8..7424deba3 100644 --- a/DeviceAdapters/configure.ac +++ b/DeviceAdapters/configure.ac @@ -40,10 +40,23 @@ AC_SUBST(MMDEVAPI_LDFLAGS) # Location of third party public files thirdpartypublic="${micromanager_path}/../3rdpartypublic" - 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], @@ -381,6 +394,52 @@ AS_IF([test "x$want_opencv" != xno], AM_CONDITIONAL([BUILD_OPENCV], [test "x$use_opencv" = xyes]) +# Vimba X (Allied Vision) SDK +MM_ARG_WITH_OPTIONAL_LIB([Vimba X], [vimba-x], [VIMBA_X]) +AS_IF([test "x$want_vimba_x" != xno], +[ + MM_LIB_VIMBA_X([$VIMBA_X_PREFIX], + [use_vimba_x=yes], + [ + use_vimba_x=no + AS_IF([test "x$want_vimba_x" = xyes], + [MM_MSG_OPTIONAL_LIB_FAILURE([Vimba X], [vimba-x])]) + ]) +], +[use_vimba_x=no]) + +AM_CONDITIONAL([BUILD_ALLIED_VISION_CAMERA], [test "x$use_vimba_x" = xyes]) + +# Zaber Motion Library (hack: only support 3rdpartypublic copy currently) +AC_MSG_CHECKING([for Zaber Motion Library in 3rdpartypublic]) +zml_header_to_check="${thirdpartypublic}/Zaber/zaber-motion/include/zaber/motion/library.h" +AM_CONDITIONAL([BUILD_ZABER], [test -f $zml_header_to_check]) +if test -f "$zml_header_to_check"; then + AC_MSG_RESULT([found]) + ZML_CPPFLAGS="-I${thirdpartypublic}/Zaber/zaber-motion/include" + ZML_LIBS="-lzml" + case $host in + *linux*) + # Linux builds by users should install ZML libs + zml_linux_libdir="${thirdpartypublic}/Zaber/zaber-motion/linux-amd64/lib" + ZML_LDFLAGS="-L${zml_linux_libdir} -Wl,-rpath,"'\$$ORIGIN' + ZML_LIBS_TO_COPY="${zml_linux_libdir}/libzml.so.3.4 ${zml_linux_libdir}/libzaber-motion-lib-linux-amd64.so.3.4.3" + ;; + + *apple-darwin*) + # macOS build for packaging does not ship ZML + ZML_LDFLAGS="-L${thirdpartypublic}/Zaber/zaber-motion/darwin/lib" + ZML_LIBS_TO_COPY="" + ;; + esac +else + AC_MSG_RESULT([not found]) +fi +AC_SUBST(ZML_CPPFLAGS) +AC_SUBST(ZML_LIBS) +AC_SUBST(ZML_LDFLAGS) +AC_SUBST(ZML_LIBS_TO_COPY) + # Only build ... when the code is there AM_CONDITIONAL([BUILD_TEST],[test -f "Test/Test.h"]) @@ -463,6 +522,7 @@ m4_define([device_adapter_dirs], [m4_strip([ ASITiger ASIWPTR Aladdin + AlliedVisionCamera Andor AndorLaserCombiner AndorSDK3 @@ -633,4 +693,7 @@ echo "m4_text_wrap([$use_libusb_0_1], echo "m4_text_wrap([$use_opencv], [ ], [ Build with OpenCV: ])" +echo "m4_text_wrap([$use_vimba_x], + [ ], + [ Build with Vimba X: ])" echo "" diff --git a/DeviceAdapters/kdv/KDV.cpp b/DeviceAdapters/kdv/KDV.cpp index 038078091..b656722e5 100644 --- a/DeviceAdapters/kdv/KDV.cpp +++ b/DeviceAdapters/kdv/KDV.cpp @@ -32,7 +32,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include "KDV.h" #include diff --git a/DeviceAdapters/nPoint/nPC400.cpp b/DeviceAdapters/nPoint/nPC400.cpp index 3438ad871..7dd294d4f 100755 --- a/DeviceAdapters/nPoint/nPC400.cpp +++ b/DeviceAdapters/nPoint/nPC400.cpp @@ -31,7 +31,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include #include diff --git a/DeviceAdapters/nPoint/nPC400Channel.cpp b/DeviceAdapters/nPoint/nPC400Channel.cpp index ab509fd0e..c5c4af549 100755 --- a/DeviceAdapters/nPoint/nPC400Channel.cpp +++ b/DeviceAdapters/nPoint/nPC400Channel.cpp @@ -31,7 +31,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include #include diff --git a/DeviceAdapters/nPoint/nPC400Ctrl.cpp b/DeviceAdapters/nPoint/nPC400Ctrl.cpp index 2cc9a26b6..f87316ffe 100755 --- a/DeviceAdapters/nPoint/nPC400Ctrl.cpp +++ b/DeviceAdapters/nPoint/nPC400Ctrl.cpp @@ -31,7 +31,6 @@ #ifdef WIN32 #include #endif -#include "FixSnprintf.h" #include #include diff --git a/DeviceAdapters/pgFocus/pgFocus.cpp b/DeviceAdapters/pgFocus/pgFocus.cpp index 4bf381e2c..f3bea3673 100644 --- a/DeviceAdapters/pgFocus/pgFocus.cpp +++ b/DeviceAdapters/pgFocus/pgFocus.cpp @@ -18,7 +18,6 @@ #ifdef WIN32 #pragma warning(disable: 4355) #endif -#include "FixSnprintf.h" #include "pgFocus.h" #include diff --git a/DeviceAdapters/pgFocus/pgFocus.h b/DeviceAdapters/pgFocus/pgFocus.h index 91d1a08d3..4623cd4eb 100644 --- a/DeviceAdapters/pgFocus/pgFocus.h +++ b/DeviceAdapters/pgFocus/pgFocus.h @@ -23,8 +23,6 @@ #else #include #endif -#include "FixSnprintf.h" - #include "MMDevice.h" #include "DeviceBase.h" diff --git a/MMCore/AppleHost.h b/MMCore/AppleHost.h deleted file mode 100644 index 9511e54d1..000000000 --- a/MMCore/AppleHost.h +++ /dev/null @@ -1,204 +0,0 @@ -//#ifdef APPLEHOSTIMPL - -/* - File: GetPrimaryMACAddress.c - - Description: This sample application demonstrates how to do retrieve the Ethernet MAC - address of the built-in Ethernet interface from the I/O Registry on Mac OS X. - Techniques shown include finding the primary (built-in) Ethernet interface, - finding the parent Ethernet controller, and retrieving properties from the - controller's I/O Registry entry. - - Copyright: © Copyright 2001-2005 Apple Computer, Inc. All rights reserved. - - Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. - ("Apple") in consideration of your agreement to the following terms, and your - use, installation, modification or redistribution of this Apple software - constitutes acceptance of these terms. If you do not agree with these terms, - please do not use, install, modify or redistribute this Apple software. - - In consideration of your agreement to abide by the following terms, and subject - to these terms, Apple grants you a personal, non-exclusive license, under Apple’s - copyrights in this original Apple software (the "Apple Software"), to use, - reproduce, modify and redistribute the Apple Software, with or without - modifications, in source and/or binary forms; provided that if you redistribute - the Apple Software in its entirety and without modifications, you must retain - this notice and the following text and disclaimers in all such redistributions of - the Apple Software. Neither the name, trademarks, service marks or logos of - Apple Computer, Inc. may be used to endorse or promote products derived from the - Apple Software without specific prior written permission from Apple. Except as - expressly stated in this notice, no other rights or licenses, express or implied, - are granted by Apple herein, including but not limited to any patent rights that - may be infringed by your derivative works or by other works in which the Apple - Software may be incorporated. - - The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO - WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED - WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR - PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN - COMBINATION WITH YOUR PRODUCTS. - - IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE - GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION - OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT - (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN - ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - Change History (most recent first): - - <3> 09/15/05 Updated to produce a universal binary. Use kIOMasterPortDefault - instead of older IOMasterPort function. Print the MAC address - to stdout in response to . - <2> 04/30/02 Fix bug in creating the matching dictionary that caused the - kIOPrimaryInterface property to be ignored. Clean up comments and add - additional comments about how IOServiceGetMatchingServices operates. - <1> 06/07/01 New sample. - -*/ - -#include - -#include - -#include -#include -#include -#include - -static kern_return_t FindEthernetInterfaces(io_iterator_t *matchingServices); -static kern_return_t GetMACAddress(io_iterator_t intfIterator, UInt8 *MACAddress, UInt8 bufferSize); - -// Returns an iterator containing the primary (built-in) Ethernet interface. The caller is responsible for -// releasing the iterator after the caller is done with it. -static kern_return_t FindEthernetInterfaces(io_iterator_t *matchingServices) -{ - kern_return_t kernResult; - CFMutableDictionaryRef matchingDict; - CFMutableDictionaryRef propertyMatchDict; - - // Ethernet interfaces are instances of class kIOEthernetInterfaceClass. - // IOServiceMatching is a convenience function to create a dictionary with the key kIOProviderClassKey and - // the specified value. - matchingDict = IOServiceMatching(kIOEthernetInterfaceClass); - - // Note that another option here would be: - // matchingDict = IOBSDMatching("en0"); - - if (NULL == matchingDict) { - printf("IOServiceMatching returned a NULL dictionary.\n"); - } - else { - // Each IONetworkInterface object has a Boolean property with the key kIOPrimaryInterface. Only the - // primary (built-in) interface has this property set to TRUE. - - // IOServiceGetMatchingServices uses the default matching criteria defined by IOService. This considers - // only the following properties plus any family-specific matching in this order of precedence - // (see IOService::passiveMatch): - // - // kIOProviderClassKey (IOServiceMatching) - // kIONameMatchKey (IOServiceNameMatching) - // kIOPropertyMatchKey - // kIOPathMatchKey - // kIOMatchedServiceCountKey - // family-specific matching - // kIOBSDNameKey (IOBSDNameMatching) - // kIOLocationMatchKey - - // The IONetworkingFamily does not define any family-specific matching. This means that in - // order to have IOServiceGetMatchingServices consider the kIOPrimaryInterface property, we must - // add that property to a separate dictionary and then add that to our matching dictionary - // specifying kIOPropertyMatchKey. - - propertyMatchDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - - if (NULL == propertyMatchDict) { - printf("CFDictionaryCreateMutable returned a NULL dictionary.\n"); - } - else { - // Set the value in the dictionary of the property with the given key, or add the key - // to the dictionary if it doesn't exist. This call retains the value object passed in. - CFDictionarySetValue(propertyMatchDict, CFSTR(kIOPrimaryInterface), kCFBooleanTrue); - - // Now add the dictionary containing the matching value for kIOPrimaryInterface to our main - // matching dictionary. This call will retain propertyMatchDict, so we can release our reference - // on propertyMatchDict after adding it to matchingDict. - CFDictionarySetValue(matchingDict, CFSTR(kIOPropertyMatchKey), propertyMatchDict); - CFRelease(propertyMatchDict); - } - } - - // IOServiceGetMatchingServices retains the returned iterator, so release the iterator when we're done with it. - // IOServiceGetMatchingServices also consumes a reference on the matching dictionary so we don't need to release - // the dictionary explicitly. - kernResult = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, matchingServices); - if (KERN_SUCCESS != kernResult) { - printf("IOServiceGetMatchingServices returned 0x%08x\n", kernResult); - } - - return kernResult; -} - -// Given an iterator across a set of Ethernet interfaces, return the MAC address of the last one. -// If no interfaces are found the MAC address is set to an empty string. -// In this sample the iterator should contain just the primary interface. -static kern_return_t GetMACAddress(io_iterator_t intfIterator, UInt8 *MACAddress, UInt8 bufferSize) -{ - kern_return_t kernResult = KERN_FAILURE; - - // Make sure the caller provided enough buffer space. Protect against buffer overflow problems. - if (bufferSize < kIOEthernetAddressSize) { - return kernResult; - } - - // Initialize the returned address - bzero(MACAddress, bufferSize); - - // IOIteratorNext retains the returned object, so release it when we're done with it. - while (io_object_t intfService = IOIteratorNext(intfIterator)) - { - io_object_t controllerService; - CFTypeRef MACAddressAsCFData; - - // IONetworkControllers can't be found directly by the IOServiceGetMatchingServices call, - // since they are hardware nubs and do not participate in driver matching. In other words, - // registerService() is never called on them. So we've found the IONetworkInterface and will - // get its parent controller by asking for it specifically. - - // IORegistryEntryGetParentEntry retains the returned object, so release it when we're done with it. - kernResult = IORegistryEntryGetParentEntry(intfService, - kIOServicePlane, - &controllerService); - - if (KERN_SUCCESS != kernResult) { - printf("IORegistryEntryGetParentEntry returned 0x%08x\n", kernResult); - } - else { - // Retrieve the MAC address property from the I/O Registry in the form of a CFData - MACAddressAsCFData = IORegistryEntryCreateCFProperty(controllerService, - CFSTR(kIOMACAddress), - kCFAllocatorDefault, - 0); - if (MACAddressAsCFData) { - - // Get the raw bytes of the MAC address from the CFData - CFDataGetBytes((const __CFData*)MACAddressAsCFData, CFRangeMake(0, kIOEthernetAddressSize), MACAddress); - CFRelease(MACAddressAsCFData); - } - - // Done with the parent Ethernet controller object so we release it. - (void) IOObjectRelease(controllerService); - } - - // Done with the Ethernet interface object so we release it. - (void) IOObjectRelease(intfService); - } - - return kernResult; -} - -//#endif // APPLEHOSTIMPL - diff --git a/MMCore/ConfigGroup.h b/MMCore/ConfigGroup.h index 6c87857a9..0a8d705ed 100644 --- a/MMCore/ConfigGroup.h +++ b/MMCore/ConfigGroup.h @@ -397,7 +397,7 @@ class PixelSizeConfiguration : public Configuration { if (affineMatrix.size() != 6) { - throw new CMMError("PixelConfig affineMatrix must have 6 elements"); + throw CMMError("PixelConfig affineMatrix must have 6 elements"); } for (unsigned int i=0; i < affineMatrix.size(); i++) { diff --git a/MMCore/Configuration.cpp b/MMCore/Configuration.cpp index b40a4f41d..87ae0b1ee 100644 --- a/MMCore/Configuration.cpp +++ b/MMCore/Configuration.cpp @@ -192,46 +192,4 @@ void Configuration::deleteSetting(const char* device, const char* prop) index_[settings_[i].getKey()] = i; } -} - - -/** - * Returns the property pair with specified index. - */ -PropertyPair PropertyBlock::getPair(size_t index) const throw (CMMError) -{ - std::map::const_iterator it = pairs_.begin(); - if (index >= pairs_.size()) - { - std::ostringstream errTxt; - errTxt << (unsigned int)index << " - invalid property pair index"; - throw CMMError(errTxt.str().c_str(), MMERR_DEVICE_GENERIC); - } - - for (size_t i=0; isecond; -} - -/** - * Adds a new pair to the current contents. - */ -void PropertyBlock::addPair(const PropertyPair& pair) -{ - pairs_[pair.getPropertyName()] = pair; -} - -/** - * Get value of the specified key (property). - */ -std::string PropertyBlock::getValue(const char* key) const throw (CMMError) -{ - std::map::const_iterator it; - it = pairs_.find(key); - if (it != pairs_.end()) - return it->second.getPropertyValue(); - - std::ostringstream errTxt; - errTxt << key << " - invalid property name"; - throw CMMError(errTxt.str().c_str(), MMERR_DEVICE_GENERIC); -} - +} \ No newline at end of file diff --git a/MMCore/Configuration.h b/MMCore/Configuration.h index 51d2727d3..133321259 100644 --- a/MMCore/Configuration.h +++ b/MMCore/Configuration.h @@ -86,36 +86,6 @@ struct PropertySetting bool readOnly_; }; -/** - * Property pair defined as doublet: - * property - value. - */ -struct PropertyPair -{ - /** - * Constructor for the struct specifying the entire contents. - * @param prop - * @param value - */ - PropertyPair(const char* prop, const char* value) : - propertyName_(prop), value_(value) {} - - PropertyPair() {} - ~PropertyPair() {} - /** - * Returns the property name. - */ - std::string getPropertyName() const {return propertyName_;} - /** - * Returns the property value. - */ - std::string getPropertyValue() const {return value_;} - -private: - std::string propertyName_; - std::string value_; -}; - /** * Encapsulation of the configuration information. Designed to be wrapped * by SWIG. A collection of configuration settings. @@ -151,27 +121,4 @@ class Configuration std::map index_; }; -/** - * Encapsulation of the property collection. Designed to be wrapped - * by SWIG. A collection of property pairs. - */ -class PropertyBlock -{ -public: - - PropertyBlock() {} - ~PropertyBlock() {} - - void addPair(const PropertyPair& pair); - PropertyPair getPair(size_t index) const throw (CMMError); - /** - * Returns the number of contained property parts. - */ - size_t size() const {return pairs_.size();} - std::string getValue(const char* key) const throw (CMMError); - -private: - std::map pairs_; -}; - #endif //_CONFIGURATION_H_ diff --git a/MMCore/Devices/AutoFocusInstance.cpp b/MMCore/Devices/AutoFocusInstance.cpp index 7a7ec8817..d82b5f146 100644 --- a/MMCore/Devices/AutoFocusInstance.cpp +++ b/MMCore/Devices/AutoFocusInstance.cpp @@ -22,13 +22,13 @@ #include "AutoFocusInstance.h" -int AutoFocusInstance::SetContinuousFocusing(bool state) { return GetImpl()->SetContinuousFocusing(state); } -int AutoFocusInstance::GetContinuousFocusing(bool& state) { return GetImpl()->GetContinuousFocusing(state); } -bool AutoFocusInstance::IsContinuousFocusLocked() { return GetImpl()->IsContinuousFocusLocked(); } -int AutoFocusInstance::FullFocus() { return GetImpl()->FullFocus(); } -int AutoFocusInstance::IncrementalFocus() { return GetImpl()->IncrementalFocus(); } -int AutoFocusInstance::GetLastFocusScore(double& score) { return GetImpl()->GetLastFocusScore(score); } -int AutoFocusInstance::GetCurrentFocusScore(double& score) { return GetImpl()->GetCurrentFocusScore(score); } -int AutoFocusInstance::AutoSetParameters() { return GetImpl()->AutoSetParameters(); } -int AutoFocusInstance::GetOffset(double &offset) { return GetImpl()->GetOffset(offset); } -int AutoFocusInstance::SetOffset(double offset) { return GetImpl()->SetOffset(offset); } +int AutoFocusInstance::SetContinuousFocusing(bool state) { RequireInitialized(__func__); return GetImpl()->SetContinuousFocusing(state); } +int AutoFocusInstance::GetContinuousFocusing(bool& state) { RequireInitialized(__func__); return GetImpl()->GetContinuousFocusing(state); } +bool AutoFocusInstance::IsContinuousFocusLocked() { RequireInitialized(__func__); return GetImpl()->IsContinuousFocusLocked(); } +int AutoFocusInstance::FullFocus() { RequireInitialized(__func__); return GetImpl()->FullFocus(); } +int AutoFocusInstance::IncrementalFocus() { RequireInitialized(__func__); return GetImpl()->IncrementalFocus(); } +int AutoFocusInstance::GetLastFocusScore(double& score) { RequireInitialized(__func__); return GetImpl()->GetLastFocusScore(score); } +int AutoFocusInstance::GetCurrentFocusScore(double& score) { RequireInitialized(__func__); return GetImpl()->GetCurrentFocusScore(score); } +int AutoFocusInstance::AutoSetParameters() { RequireInitialized(__func__); return GetImpl()->AutoSetParameters(); } +int AutoFocusInstance::GetOffset(double &offset) { RequireInitialized(__func__); return GetImpl()->GetOffset(offset); } +int AutoFocusInstance::SetOffset(double offset) { RequireInitialized(__func__); return GetImpl()->SetOffset(offset); } diff --git a/MMCore/Devices/CameraInstance.cpp b/MMCore/Devices/CameraInstance.cpp index 8b9285de2..8633d1b5d 100644 --- a/MMCore/Devices/CameraInstance.cpp +++ b/MMCore/Devices/CameraInstance.cpp @@ -22,14 +22,15 @@ #include "CameraInstance.h" -int CameraInstance::SnapImage() { return GetImpl()->SnapImage(); } -const unsigned char* CameraInstance::GetImageBuffer() { return GetImpl()->GetImageBuffer(); } -const unsigned char* CameraInstance::GetImageBuffer(unsigned channelNr) { return GetImpl()->GetImageBuffer(channelNr); } -const unsigned int* CameraInstance::GetImageBufferAsRGB32() { return GetImpl()->GetImageBufferAsRGB32(); } -unsigned CameraInstance::GetNumberOfComponents() const { return GetImpl()->GetNumberOfComponents(); } +int CameraInstance::SnapImage() { RequireInitialized(__func__); return GetImpl()->SnapImage(); } +const unsigned char* CameraInstance::GetImageBuffer() { RequireInitialized(__func__); return GetImpl()->GetImageBuffer(); } +const unsigned char* CameraInstance::GetImageBuffer(unsigned channelNr) { RequireInitialized(__func__); return GetImpl()->GetImageBuffer(channelNr); } +const unsigned int* CameraInstance::GetImageBufferAsRGB32() { RequireInitialized(__func__); return GetImpl()->GetImageBufferAsRGB32(); } +unsigned CameraInstance::GetNumberOfComponents() const { RequireInitialized(__func__); return GetImpl()->GetNumberOfComponents(); } std::string CameraInstance::GetComponentName(unsigned component) { + RequireInitialized(__func__); DeviceStringBuffer nameBuf(this, "GetComponentName"); int err = GetImpl()->GetComponentName(component, nameBuf.GetBuffer()); ThrowIfError(err, "Cannot get component name at index " + @@ -37,35 +38,37 @@ std::string CameraInstance::GetComponentName(unsigned component) return nameBuf.Get(); } -int unsigned CameraInstance::GetNumberOfChannels() const { return GetImpl()->GetNumberOfChannels(); } +int unsigned CameraInstance::GetNumberOfChannels() const { RequireInitialized(__func__); return GetImpl()->GetNumberOfChannels(); } std::string CameraInstance::GetChannelName(unsigned channel) { + RequireInitialized(__func__); DeviceStringBuffer nameBuf(this, "GetChannelName"); int err = GetImpl()->GetChannelName(channel, nameBuf.GetBuffer()); ThrowIfError(err, "Cannot get channel name at index " + ToString(channel)); return nameBuf.Get(); } -long CameraInstance::GetImageBufferSize()const { return GetImpl()->GetImageBufferSize(); } -unsigned CameraInstance::GetImageWidth() const { return GetImpl()->GetImageWidth(); } -unsigned CameraInstance::GetImageHeight() const { return GetImpl()->GetImageHeight(); } -unsigned CameraInstance::GetImageBytesPerPixel() const { return GetImpl()->GetImageBytesPerPixel(); } -unsigned CameraInstance::GetBitDepth() const { return GetImpl()->GetBitDepth(); } -double CameraInstance::GetPixelSizeUm() const { return GetImpl()->GetPixelSizeUm(); } -int CameraInstance::GetBinning() const { return GetImpl()->GetBinning(); } -int CameraInstance::SetBinning(int binSize) { return GetImpl()->SetBinning(binSize); } -void CameraInstance::SetExposure(double exp_ms) { return GetImpl()->SetExposure(exp_ms); } -double CameraInstance::GetExposure() const { return GetImpl()->GetExposure(); } -int CameraInstance::SetROI(unsigned x, unsigned y, unsigned xSize, unsigned ySize) { return GetImpl()->SetROI(x, y, xSize, ySize); } -int CameraInstance::GetROI(unsigned& x, unsigned& y, unsigned& xSize, unsigned& ySize) { return GetImpl()->GetROI(x, y, xSize, ySize); } -int CameraInstance::ClearROI() { return GetImpl()->ClearROI(); } +long CameraInstance::GetImageBufferSize() const { RequireInitialized(__func__); return GetImpl()->GetImageBufferSize(); } +unsigned CameraInstance::GetImageWidth() const { RequireInitialized(__func__); return GetImpl()->GetImageWidth(); } +unsigned CameraInstance::GetImageHeight() const { RequireInitialized(__func__); return GetImpl()->GetImageHeight(); } +unsigned CameraInstance::GetImageBytesPerPixel() const { RequireInitialized(__func__); return GetImpl()->GetImageBytesPerPixel(); } +unsigned CameraInstance::GetBitDepth() const { RequireInitialized(__func__); return GetImpl()->GetBitDepth(); } +double CameraInstance::GetPixelSizeUm() const { RequireInitialized(__func__); return GetImpl()->GetPixelSizeUm(); } +int CameraInstance::GetBinning() const { RequireInitialized(__func__); return GetImpl()->GetBinning(); } +int CameraInstance::SetBinning(int binSize) { RequireInitialized(__func__); return GetImpl()->SetBinning(binSize); } +void CameraInstance::SetExposure(double exp_ms) { RequireInitialized(__func__); return GetImpl()->SetExposure(exp_ms); } +double CameraInstance::GetExposure() const { RequireInitialized(__func__); return GetImpl()->GetExposure(); } +int CameraInstance::SetROI(unsigned x, unsigned y, unsigned xSize, unsigned ySize) { RequireInitialized(__func__); return GetImpl()->SetROI(x, y, xSize, ySize); } +int CameraInstance::GetROI(unsigned& x, unsigned& y, unsigned& xSize, unsigned& ySize) { RequireInitialized(__func__); return GetImpl()->GetROI(x, y, xSize, ySize); } +int CameraInstance::ClearROI() { RequireInitialized(__func__); return GetImpl()->ClearROI(); } /** * Queries if the camera supports multiple simultaneous ROIs. */ bool CameraInstance::SupportsMultiROI() { + RequireInitialized(__func__); return GetImpl()->SupportsMultiROI(); } @@ -76,6 +79,7 @@ bool CameraInstance::SupportsMultiROI() */ bool CameraInstance::IsMultiROISet() { + RequireInitialized(__func__); return GetImpl()->IsMultiROISet(); } @@ -85,6 +89,7 @@ bool CameraInstance::IsMultiROISet() */ int CameraInstance::GetMultiROICount(unsigned int& count) { + RequireInitialized(__func__); return GetImpl()->GetMultiROICount(count); } @@ -101,6 +106,7 @@ int CameraInstance::SetMultiROI(const unsigned int* xs, const unsigned int* ys, const unsigned* widths, const unsigned int* heights, unsigned numROIs) { + RequireInitialized(__func__); return GetImpl()->SetMultiROI(xs, ys, widths, heights, numROIs); } @@ -117,17 +123,19 @@ int CameraInstance::SetMultiROI(const unsigned int* xs, const unsigned int* ys, int CameraInstance::GetMultiROI(unsigned* xs, unsigned* ys, unsigned* widths, unsigned* heights, unsigned* length) { + RequireInitialized(__func__); return GetImpl()->GetMultiROI(xs, ys, widths, heights, length); } -int CameraInstance::StartSequenceAcquisition(long numImages, double interval_ms, bool stopOnOverflow) { return GetImpl()->StartSequenceAcquisition(numImages, interval_ms, stopOnOverflow); } -int CameraInstance::StartSequenceAcquisition(double interval_ms) { return GetImpl()->StartSequenceAcquisition(interval_ms); } -int CameraInstance::StopSequenceAcquisition() { return GetImpl()->StopSequenceAcquisition(); } -int CameraInstance::PrepareSequenceAcqusition() { return GetImpl()->PrepareSequenceAcqusition(); } -bool CameraInstance::IsCapturing() { return GetImpl()->IsCapturing(); } +int CameraInstance::StartSequenceAcquisition(long numImages, double interval_ms, bool stopOnOverflow) { RequireInitialized(__func__); return GetImpl()->StartSequenceAcquisition(numImages, interval_ms, stopOnOverflow); } +int CameraInstance::StartSequenceAcquisition(double interval_ms) { RequireInitialized(__func__); return GetImpl()->StartSequenceAcquisition(interval_ms); } +int CameraInstance::StopSequenceAcquisition() { RequireInitialized(__func__); return GetImpl()->StopSequenceAcquisition(); } +int CameraInstance::PrepareSequenceAcqusition() { RequireInitialized(__func__); return GetImpl()->PrepareSequenceAcqusition(); } +bool CameraInstance::IsCapturing() { RequireInitialized(__func__); return GetImpl()->IsCapturing(); } std::string CameraInstance::GetTags() { + RequireInitialized(__func__); // TODO Probably makes sense to deserialize here. // Also note the danger of limiting serialized metadata to MM::MaxStrLength // (CCameraBase takes no precaution to limit string length; it is an @@ -137,12 +145,12 @@ std::string CameraInstance::GetTags() return serializedMetadataBuf.Get(); } -void CameraInstance::AddTag(const char* key, const char* deviceLabel, const char* value) { return GetImpl()->AddTag(key, deviceLabel, value); } -void CameraInstance::RemoveTag(const char* key) { return GetImpl()->RemoveTag(key); } -int CameraInstance::IsExposureSequenceable(bool& isSequenceable) const { return GetImpl()->IsExposureSequenceable(isSequenceable); } -int CameraInstance::GetExposureSequenceMaxLength(long& nrEvents) const { return GetImpl()->GetExposureSequenceMaxLength(nrEvents); } -int CameraInstance::StartExposureSequence() { return GetImpl()->StartExposureSequence(); } -int CameraInstance::StopExposureSequence() { return GetImpl()->StopExposureSequence(); } -int CameraInstance::ClearExposureSequence() { return GetImpl()->ClearExposureSequence(); } -int CameraInstance::AddToExposureSequence(double exposureTime_ms) { return GetImpl()->AddToExposureSequence(exposureTime_ms); } -int CameraInstance::SendExposureSequence() const { return GetImpl()->SendExposureSequence(); } +void CameraInstance::AddTag(const char* key, const char* deviceLabel, const char* value) { RequireInitialized(__func__); return GetImpl()->AddTag(key, deviceLabel, value); } +void CameraInstance::RemoveTag(const char* key) { RequireInitialized(__func__); return GetImpl()->RemoveTag(key); } +int CameraInstance::IsExposureSequenceable(bool& isSequenceable) const { RequireInitialized(__func__); return GetImpl()->IsExposureSequenceable(isSequenceable); } +int CameraInstance::GetExposureSequenceMaxLength(long& nrEvents) const { RequireInitialized(__func__); return GetImpl()->GetExposureSequenceMaxLength(nrEvents); } +int CameraInstance::StartExposureSequence() { RequireInitialized(__func__); return GetImpl()->StartExposureSequence(); } +int CameraInstance::StopExposureSequence() { RequireInitialized(__func__); return GetImpl()->StopExposureSequence(); } +int CameraInstance::ClearExposureSequence() { RequireInitialized(__func__); return GetImpl()->ClearExposureSequence(); } +int CameraInstance::AddToExposureSequence(double exposureTime_ms) { RequireInitialized(__func__); return GetImpl()->AddToExposureSequence(exposureTime_ms); } +int CameraInstance::SendExposureSequence() const { RequireInitialized(__func__); return GetImpl()->SendExposureSequence(); } diff --git a/MMCore/Devices/DeviceInstance.cpp b/MMCore/Devices/DeviceInstance.cpp index 41a8a5e58..caa6d0e31 100644 --- a/MMCore/Devices/DeviceInstance.cpp +++ b/MMCore/Devices/DeviceInstance.cpp @@ -121,6 +121,20 @@ DeviceInstance::ThrowIfError(int code, const std::string& message) const throw e; } +void +DeviceInstance::RequireInitialized(const char *operation) const +{ + if (!initialized_) { + // This is an error, but existing application code (in particular, + // the Hardware Configuration Wizard) breaks if we enforce it strictly. + // Until such code is fixed, we only log. + LOG_WARNING(Logger()) << "Operation (" << operation << + ") not permitted on uninitialized device (this will be an error in a future version of MMCore; for now we continue with the operation anyway, even though it might not be safe)"; + // Eventually to be replaced with: + // ThrowError("Operation not permitted on uninitialized device"); + } +} + void DeviceInstance::DeviceStringBuffer::ThrowBufferOverflowError() const { @@ -159,6 +173,9 @@ void DeviceInstance::SetProperty(const std::string& name, const std::string& value) const { + if (initialized_ && GetPropertyInitStatus(name.c_str())) + ThrowError("Cannot set pre-init property after initialization"); + LOG_DEBUG(Logger()) << "Will set property \"" << name << "\" to \"" << value << "\""; @@ -315,7 +332,10 @@ DeviceInstance::GetErrorText(int code) const bool DeviceInstance::Busy() -{ return pImpl_->Busy(); } +{ + RequireInitialized(__func__); + return pImpl_->Busy(); +} double DeviceInstance::GetDelayMs() const @@ -332,12 +352,19 @@ DeviceInstance::UsesDelay() void DeviceInstance::Initialize() { + // Device initialization can only be attempted once per instance lifetime. + if (initializeCalled_) + ThrowError("Device already initialized (or initialization already attempted)"); + initializeCalled_ = true; ThrowIfError(pImpl_->Initialize()); + initialized_ = true; } void DeviceInstance::Shutdown() { + // Note we do not require device to be initialized before calling Shutdown(). + initialized_ = false; ThrowIfError(pImpl_->Shutdown()); } @@ -358,7 +385,6 @@ DeviceInstance::SetCallback(MM::Core* callback) { pImpl_->SetCallback(callback); } - bool DeviceInstance::SupportsDeviceDetection() { diff --git a/MMCore/Devices/DeviceInstance.h b/MMCore/Devices/DeviceInstance.h index 02bc813a5..0919ca1a4 100644 --- a/MMCore/Devices/DeviceInstance.h +++ b/MMCore/Devices/DeviceInstance.h @@ -71,6 +71,8 @@ class DeviceInstance DeleteDeviceFunction deleteFunction_; mm::logging::Logger deviceLogger_; mm::logging::Logger coreLogger_; + bool initializeCalled_ = false; + bool initialized_ = false; public: DeviceInstance(const DeviceInstance&) = delete; @@ -88,6 +90,9 @@ class DeviceInstance // Callback API int LogMessage(const char* msg, bool debugOnly); + bool IsInitialized() const { return initialized_; } + bool HasInitializationBeenAttempted() const { return initializeCalled_; } + protected: // The DeviceInstance object owns the raw device pointer (pDevice) as soon // as the constructor is called, even if the constructor throws. @@ -112,6 +117,7 @@ class DeviceInstance void ThrowError(const std::string& message) const; void ThrowIfError(int code) const; void ThrowIfError(int code, const std::string& message) const; + void RequireInitialized(const char *) const; /// Utility class for getting fixed-length strings from the device interface. /** diff --git a/MMCore/Devices/GalvoInstance.cpp b/MMCore/Devices/GalvoInstance.cpp index 3b252192b..98a3e314d 100644 --- a/MMCore/Devices/GalvoInstance.cpp +++ b/MMCore/Devices/GalvoInstance.cpp @@ -22,25 +22,26 @@ #include "GalvoInstance.h" -int GalvoInstance::PointAndFire(double x, double y, double time_us) { return GetImpl()->PointAndFire(x, y, time_us); } -int GalvoInstance::SetSpotInterval(double pulseInterval_us) { return GetImpl()->SetSpotInterval(pulseInterval_us); } -int GalvoInstance::SetPosition(double x, double y) { return GetImpl()->SetPosition(x, y); } -int GalvoInstance::GetPosition(double& x, double& y) { return GetImpl()->GetPosition(x, y); } -int GalvoInstance::SetIlluminationState(bool on) { return GetImpl()->SetIlluminationState(on); } -double GalvoInstance::GetXRange() { return GetImpl()->GetXRange(); } -double GalvoInstance::GetXMinimum() { return GetImpl()->GetXMinimum(); } -double GalvoInstance::GetYRange() { return GetImpl()->GetYRange(); } -double GalvoInstance::GetYMinimum() { return GetImpl()->GetYMinimum(); } -int GalvoInstance::AddPolygonVertex(int polygonIndex, double x, double y) { return GetImpl()->AddPolygonVertex(polygonIndex, x, y); } -int GalvoInstance::DeletePolygons() { return GetImpl()->DeletePolygons(); } -int GalvoInstance::RunSequence() { return GetImpl()->RunSequence(); } -int GalvoInstance::LoadPolygons() { return GetImpl()->LoadPolygons(); } -int GalvoInstance::SetPolygonRepetitions(int repetitions) { return GetImpl()->SetPolygonRepetitions(repetitions); } -int GalvoInstance::RunPolygons() { return GetImpl()->RunPolygons(); } -int GalvoInstance::StopSequence() { return GetImpl()->StopSequence(); } +int GalvoInstance::PointAndFire(double x, double y, double time_us) { RequireInitialized(__func__); return GetImpl()->PointAndFire(x, y, time_us); } +int GalvoInstance::SetSpotInterval(double pulseInterval_us) { RequireInitialized(__func__); return GetImpl()->SetSpotInterval(pulseInterval_us); } +int GalvoInstance::SetPosition(double x, double y) { RequireInitialized(__func__); return GetImpl()->SetPosition(x, y); } +int GalvoInstance::GetPosition(double& x, double& y) { RequireInitialized(__func__); return GetImpl()->GetPosition(x, y); } +int GalvoInstance::SetIlluminationState(bool on) { RequireInitialized(__func__); return GetImpl()->SetIlluminationState(on); } +double GalvoInstance::GetXRange() { RequireInitialized(__func__); return GetImpl()->GetXRange(); } +double GalvoInstance::GetXMinimum() { RequireInitialized(__func__); return GetImpl()->GetXMinimum(); } +double GalvoInstance::GetYRange() { RequireInitialized(__func__); return GetImpl()->GetYRange(); } +double GalvoInstance::GetYMinimum() { RequireInitialized(__func__); return GetImpl()->GetYMinimum(); } +int GalvoInstance::AddPolygonVertex(int polygonIndex, double x, double y) { RequireInitialized(__func__); return GetImpl()->AddPolygonVertex(polygonIndex, x, y); } +int GalvoInstance::DeletePolygons() { RequireInitialized(__func__); return GetImpl()->DeletePolygons(); } +int GalvoInstance::RunSequence() { RequireInitialized(__func__); return GetImpl()->RunSequence(); } +int GalvoInstance::LoadPolygons() { RequireInitialized(__func__); return GetImpl()->LoadPolygons(); } +int GalvoInstance::SetPolygonRepetitions(int repetitions) { RequireInitialized(__func__); return GetImpl()->SetPolygonRepetitions(repetitions); } +int GalvoInstance::RunPolygons() { RequireInitialized(__func__); return GetImpl()->RunPolygons(); } +int GalvoInstance::StopSequence() { RequireInitialized(__func__); return GetImpl()->StopSequence(); } std::string GalvoInstance::GetChannel() { + RequireInitialized(__func__); DeviceStringBuffer nameBuf(this, "GetChannel"); int err = GetImpl()->GetChannel(nameBuf.GetBuffer()); ThrowIfError(err, "Cannot get current channel name"); diff --git a/MMCore/Devices/HubInstance.cpp b/MMCore/Devices/HubInstance.cpp index 141faf606..7800828e8 100644 --- a/MMCore/Devices/HubInstance.cpp +++ b/MMCore/Devices/HubInstance.cpp @@ -33,6 +33,8 @@ std::vector HubInstance::GetInstalledPeripheralNames() { + RequireInitialized(__func__); + std::vector peripherals = GetInstalledPeripherals(); std::vector names; @@ -59,6 +61,8 @@ HubInstance::GetInstalledPeripheralNames() std::string HubInstance::GetInstalledPeripheralDescription(const std::string& peripheralName) { + RequireInitialized(__func__); + std::vector peripherals = GetInstalledPeripherals(); for (std::vector::iterator it = peripherals.begin(), end = peripherals.end(); it != end; ++it) diff --git a/MMCore/Devices/ImageProcessorInstance.cpp b/MMCore/Devices/ImageProcessorInstance.cpp index 318083430..dd23c4d9d 100644 --- a/MMCore/Devices/ImageProcessorInstance.cpp +++ b/MMCore/Devices/ImageProcessorInstance.cpp @@ -22,4 +22,4 @@ #include "ImageProcessorInstance.h" -int ImageProcessorInstance::Process(unsigned char* buffer, unsigned width, unsigned height, unsigned byteDepth) { return GetImpl()->Process(buffer, width, height, byteDepth); } +int ImageProcessorInstance::Process(unsigned char* buffer, unsigned width, unsigned height, unsigned byteDepth) { RequireInitialized(__func__); return GetImpl()->Process(buffer, width, height, byteDepth); } diff --git a/MMCore/Devices/MagnifierInstance.cpp b/MMCore/Devices/MagnifierInstance.cpp index e0fb5eb7f..06b406b1d 100644 --- a/MMCore/Devices/MagnifierInstance.cpp +++ b/MMCore/Devices/MagnifierInstance.cpp @@ -22,4 +22,4 @@ #include "MagnifierInstance.h" -double MagnifierInstance::GetMagnification() { return GetImpl()->GetMagnification(); } +double MagnifierInstance::GetMagnification() { RequireInitialized(__func__); return GetImpl()->GetMagnification(); } diff --git a/MMCore/Devices/SLMInstance.cpp b/MMCore/Devices/SLMInstance.cpp index c0078efdd..19f7b75bf 100644 --- a/MMCore/Devices/SLMInstance.cpp +++ b/MMCore/Devices/SLMInstance.cpp @@ -22,26 +22,26 @@ #include "SLMInstance.h" -int SLMInstance::SetImage(unsigned char* pixels) { return GetImpl()->SetImage(pixels); } -int SLMInstance::SetImage(unsigned int* pixels) { return GetImpl()->SetImage(pixels); } -int SLMInstance::DisplayImage() { return GetImpl()->DisplayImage(); } -int SLMInstance::SetPixelsTo(unsigned char intensity) { return GetImpl()->SetPixelsTo(intensity); } -int SLMInstance::SetPixelsTo(unsigned char red, unsigned char green, unsigned char blue) { return GetImpl()->SetPixelsTo(red, green, blue); } -int SLMInstance::SetExposure(double interval_ms) { return GetImpl()->SetExposure(interval_ms); } -double SLMInstance::GetExposure() { return GetImpl()->GetExposure(); } -unsigned SLMInstance::GetWidth() { return GetImpl()->GetWidth(); } -unsigned SLMInstance::GetHeight() { return GetImpl()->GetHeight(); } -unsigned SLMInstance::GetNumberOfComponents() { return GetImpl()->GetNumberOfComponents(); } -unsigned SLMInstance::GetBytesPerPixel() { return GetImpl()->GetBytesPerPixel(); } +int SLMInstance::SetImage(unsigned char* pixels) { RequireInitialized(__func__); return GetImpl()->SetImage(pixels); } +int SLMInstance::SetImage(unsigned int* pixels) { RequireInitialized(__func__); return GetImpl()->SetImage(pixels); } +int SLMInstance::DisplayImage() { RequireInitialized(__func__); return GetImpl()->DisplayImage(); } +int SLMInstance::SetPixelsTo(unsigned char intensity) { RequireInitialized(__func__); return GetImpl()->SetPixelsTo(intensity); } +int SLMInstance::SetPixelsTo(unsigned char red, unsigned char green, unsigned char blue) { RequireInitialized(__func__); return GetImpl()->SetPixelsTo(red, green, blue); } +int SLMInstance::SetExposure(double interval_ms) { RequireInitialized(__func__); return GetImpl()->SetExposure(interval_ms); } +double SLMInstance::GetExposure() { RequireInitialized(__func__); return GetImpl()->GetExposure(); } +unsigned SLMInstance::GetWidth() { RequireInitialized(__func__); return GetImpl()->GetWidth(); } +unsigned SLMInstance::GetHeight() { RequireInitialized(__func__); return GetImpl()->GetHeight(); } +unsigned SLMInstance::GetNumberOfComponents() { RequireInitialized(__func__); return GetImpl()->GetNumberOfComponents(); } +unsigned SLMInstance::GetBytesPerPixel() { RequireInitialized(__func__); return GetImpl()->GetBytesPerPixel(); } int SLMInstance::IsSLMSequenceable(bool& isSequenceable) -{ return GetImpl()->IsSLMSequenceable(isSequenceable); } +{ RequireInitialized(__func__); return GetImpl()->IsSLMSequenceable(isSequenceable); } int SLMInstance::GetSLMSequenceMaxLength(long& nrEvents) -{ return GetImpl()->GetSLMSequenceMaxLength(nrEvents); } -int SLMInstance::StartSLMSequence() { return GetImpl()->StartSLMSequence(); } -int SLMInstance::StopSLMSequence() { return GetImpl()->StopSLMSequence(); } -int SLMInstance::ClearSLMSequence() { return GetImpl()->ClearSLMSequence(); } +{ RequireInitialized(__func__); return GetImpl()->GetSLMSequenceMaxLength(nrEvents); } +int SLMInstance::StartSLMSequence() { RequireInitialized(__func__); return GetImpl()->StartSLMSequence(); } +int SLMInstance::StopSLMSequence() { RequireInitialized(__func__); return GetImpl()->StopSLMSequence(); } +int SLMInstance::ClearSLMSequence() { RequireInitialized(__func__); return GetImpl()->ClearSLMSequence(); } int SLMInstance::AddToSLMSequence(const unsigned char * pixels) -{ return GetImpl()->AddToSLMSequence(pixels); } +{ RequireInitialized(__func__); return GetImpl()->AddToSLMSequence(pixels); } int SLMInstance::AddToSLMSequence(const unsigned int * pixels) -{ return GetImpl()->AddToSLMSequence(pixels); } -int SLMInstance::SendSLMSequence() { return GetImpl()->SendSLMSequence(); } +{ RequireInitialized(__func__); return GetImpl()->AddToSLMSequence(pixels); } +int SLMInstance::SendSLMSequence() { RequireInitialized(__func__); return GetImpl()->SendSLMSequence(); } diff --git a/MMCore/Devices/SerialInstance.cpp b/MMCore/Devices/SerialInstance.cpp index 63bde6c24..ae43cbd98 100644 --- a/MMCore/Devices/SerialInstance.cpp +++ b/MMCore/Devices/SerialInstance.cpp @@ -22,9 +22,9 @@ #include "SerialInstance.h" -MM::PortType SerialInstance::GetPortType() const { return GetImpl()->GetPortType(); } -int SerialInstance::SetCommand(const char* command, const char* term) { return GetImpl()->SetCommand(command, term); } -int SerialInstance::GetAnswer(char* txt, unsigned maxChars, const char* term) { return GetImpl()->GetAnswer(txt, maxChars, term); } -int SerialInstance::Write(const unsigned char* buf, unsigned long bufLen) { return GetImpl()->Write(buf, bufLen); } -int SerialInstance::Read(unsigned char* buf, unsigned long bufLen, unsigned long& charsRead) { return GetImpl()->Read(buf, bufLen, charsRead); } -int SerialInstance::Purge() { return GetImpl()->Purge(); } +MM::PortType SerialInstance::GetPortType() const { RequireInitialized(__func__); return GetImpl()->GetPortType(); } +int SerialInstance::SetCommand(const char* command, const char* term) { RequireInitialized(__func__); return GetImpl()->SetCommand(command, term); } +int SerialInstance::GetAnswer(char* txt, unsigned maxChars, const char* term) { RequireInitialized(__func__); return GetImpl()->GetAnswer(txt, maxChars, term); } +int SerialInstance::Write(const unsigned char* buf, unsigned long bufLen) { RequireInitialized(__func__); return GetImpl()->Write(buf, bufLen); } +int SerialInstance::Read(unsigned char* buf, unsigned long bufLen, unsigned long& charsRead) { RequireInitialized(__func__); return GetImpl()->Read(buf, bufLen, charsRead); } +int SerialInstance::Purge() { RequireInitialized(__func__); return GetImpl()->Purge(); } diff --git a/MMCore/Devices/ShutterInstance.cpp b/MMCore/Devices/ShutterInstance.cpp index 55cd6da74..eca0b2986 100644 --- a/MMCore/Devices/ShutterInstance.cpp +++ b/MMCore/Devices/ShutterInstance.cpp @@ -22,6 +22,6 @@ #include "ShutterInstance.h" -int ShutterInstance::SetOpen(bool open) { return GetImpl()->SetOpen(open); } -int ShutterInstance::GetOpen(bool& open) { return GetImpl()->GetOpen(open); } -int ShutterInstance::Fire(double deltaT) { return GetImpl()->Fire(deltaT); } +int ShutterInstance::SetOpen(bool open) { RequireInitialized(__func__); return GetImpl()->SetOpen(open); } +int ShutterInstance::GetOpen(bool& open) { RequireInitialized(__func__); return GetImpl()->GetOpen(open); } +int ShutterInstance::Fire(double deltaT) { RequireInitialized(__func__); return GetImpl()->Fire(deltaT); } diff --git a/MMCore/Devices/SignalIOInstance.cpp b/MMCore/Devices/SignalIOInstance.cpp index b189f2391..ebe84f3bf 100644 --- a/MMCore/Devices/SignalIOInstance.cpp +++ b/MMCore/Devices/SignalIOInstance.cpp @@ -22,15 +22,15 @@ #include "SignalIOInstance.h" -int SignalIOInstance::SetGateOpen(bool open) { return GetImpl()->SetGateOpen(open); } -int SignalIOInstance::GetGateOpen(bool& open) { return GetImpl()->GetGateOpen(open); } -int SignalIOInstance::SetSignal(double volts) { return GetImpl()->SetSignal(volts); } -int SignalIOInstance::GetSignal(double& volts) { return GetImpl()->GetSignal(volts); } -int SignalIOInstance::GetLimits(double& minVolts, double& maxVolts) { return GetImpl()->GetLimits(minVolts, maxVolts); } -int SignalIOInstance::IsDASequenceable(bool& isSequenceable) const { return GetImpl()->IsDASequenceable(isSequenceable); } -int SignalIOInstance::GetDASequenceMaxLength(long& nrEvents) const { return GetImpl()->GetDASequenceMaxLength(nrEvents); } -int SignalIOInstance::StartDASequence() { return GetImpl()->StartDASequence(); } -int SignalIOInstance::StopDASequence() { return GetImpl()->StopDASequence(); } -int SignalIOInstance::ClearDASequence() { return GetImpl()->ClearDASequence(); } -int SignalIOInstance::AddToDASequence(double voltage) { return GetImpl()->AddToDASequence(voltage); } -int SignalIOInstance::SendDASequence() { return GetImpl()->SendDASequence(); } +int SignalIOInstance::SetGateOpen(bool open) { RequireInitialized(__func__); return GetImpl()->SetGateOpen(open); } +int SignalIOInstance::GetGateOpen(bool& open) { RequireInitialized(__func__); return GetImpl()->GetGateOpen(open); } +int SignalIOInstance::SetSignal(double volts) { RequireInitialized(__func__); return GetImpl()->SetSignal(volts); } +int SignalIOInstance::GetSignal(double& volts) { RequireInitialized(__func__); return GetImpl()->GetSignal(volts); } +int SignalIOInstance::GetLimits(double& minVolts, double& maxVolts) { RequireInitialized(__func__); return GetImpl()->GetLimits(minVolts, maxVolts); } +int SignalIOInstance::IsDASequenceable(bool& isSequenceable) const { RequireInitialized(__func__); return GetImpl()->IsDASequenceable(isSequenceable); } +int SignalIOInstance::GetDASequenceMaxLength(long& nrEvents) const { RequireInitialized(__func__); return GetImpl()->GetDASequenceMaxLength(nrEvents); } +int SignalIOInstance::StartDASequence() { RequireInitialized(__func__); return GetImpl()->StartDASequence(); } +int SignalIOInstance::StopDASequence() { RequireInitialized(__func__); return GetImpl()->StopDASequence(); } +int SignalIOInstance::ClearDASequence() { RequireInitialized(__func__); return GetImpl()->ClearDASequence(); } +int SignalIOInstance::AddToDASequence(double voltage) { RequireInitialized(__func__); return GetImpl()->AddToDASequence(voltage); } +int SignalIOInstance::SendDASequence() { RequireInitialized(__func__); return GetImpl()->SendDASequence(); } diff --git a/MMCore/Devices/StageInstance.cpp b/MMCore/Devices/StageInstance.cpp index 0d21e85fe..f8408eb09 100644 --- a/MMCore/Devices/StageInstance.cpp +++ b/MMCore/Devices/StageInstance.cpp @@ -22,17 +22,17 @@ #include "StageInstance.h" -int StageInstance::SetPositionUm(double pos) { return GetImpl()->SetPositionUm(pos); } -int StageInstance::SetRelativePositionUm(double d) { return GetImpl()->SetRelativePositionUm(d); } -int StageInstance::Move(double velocity) { return GetImpl()->Move(velocity); } -int StageInstance::Stop() { return GetImpl()->Stop(); } -int StageInstance::Home() { return GetImpl()->Home(); } -int StageInstance::SetAdapterOriginUm(double d) { return GetImpl()->SetAdapterOriginUm(d); } -int StageInstance::GetPositionUm(double& pos) { return GetImpl()->GetPositionUm(pos); } -int StageInstance::SetPositionSteps(long steps) { return GetImpl()->SetPositionSteps(steps); } -int StageInstance::GetPositionSteps(long& steps) { return GetImpl()->GetPositionSteps(steps); } -int StageInstance::SetOrigin() { return GetImpl()->SetOrigin(); } -int StageInstance::GetLimits(double& lower, double& upper) { return GetImpl()->GetLimits(lower, upper); } +int StageInstance::SetPositionUm(double pos) { RequireInitialized(__func__); return GetImpl()->SetPositionUm(pos); } +int StageInstance::SetRelativePositionUm(double d) { RequireInitialized(__func__); return GetImpl()->SetRelativePositionUm(d); } +int StageInstance::Move(double velocity) { RequireInitialized(__func__); return GetImpl()->Move(velocity); } +int StageInstance::Stop() { RequireInitialized(__func__); return GetImpl()->Stop(); } +int StageInstance::Home() { RequireInitialized(__func__); return GetImpl()->Home(); } +int StageInstance::SetAdapterOriginUm(double d) { RequireInitialized(__func__); return GetImpl()->SetAdapterOriginUm(d); } +int StageInstance::GetPositionUm(double& pos) { RequireInitialized(__func__); return GetImpl()->GetPositionUm(pos); } +int StageInstance::SetPositionSteps(long steps) { RequireInitialized(__func__); return GetImpl()->SetPositionSteps(steps); } +int StageInstance::GetPositionSteps(long& steps) { RequireInitialized(__func__); return GetImpl()->GetPositionSteps(steps); } +int StageInstance::SetOrigin() { RequireInitialized(__func__); return GetImpl()->SetOrigin(); } +int StageInstance::GetLimits(double& lower, double& upper) { RequireInitialized(__func__); return GetImpl()->GetLimits(lower, upper); } MM::FocusDirection StageInstance::GetFocusDirection() @@ -57,14 +57,14 @@ StageInstance::SetFocusDirection(MM::FocusDirection direction) focusDirectionHasBeenSet_ = true; } -int StageInstance::IsStageSequenceable(bool& isSequenceable) const { return GetImpl()->IsStageSequenceable(isSequenceable); } -int StageInstance::IsStageLinearSequenceable(bool& isSequenceable) const { return GetImpl()->IsStageLinearSequenceable(isSequenceable); } -bool StageInstance::IsContinuousFocusDrive() const { return GetImpl()->IsContinuousFocusDrive(); } -int StageInstance::GetStageSequenceMaxLength(long& nrEvents) const { return GetImpl()->GetStageSequenceMaxLength(nrEvents); } -int StageInstance::StartStageSequence() { return GetImpl()->StartStageSequence(); } -int StageInstance::StopStageSequence() { return GetImpl()->StopStageSequence(); } -int StageInstance::ClearStageSequence() { return GetImpl()->ClearStageSequence(); } -int StageInstance::AddToStageSequence(double position) { return GetImpl()->AddToStageSequence(position); } -int StageInstance::SendStageSequence() { return GetImpl()->SendStageSequence(); } +int StageInstance::IsStageSequenceable(bool& isSequenceable) const { RequireInitialized(__func__); return GetImpl()->IsStageSequenceable(isSequenceable); } +int StageInstance::IsStageLinearSequenceable(bool& isSequenceable) const { RequireInitialized(__func__); return GetImpl()->IsStageLinearSequenceable(isSequenceable); } +bool StageInstance::IsContinuousFocusDrive() const { RequireInitialized(__func__); return GetImpl()->IsContinuousFocusDrive(); } +int StageInstance::GetStageSequenceMaxLength(long& nrEvents) const { RequireInitialized(__func__); return GetImpl()->GetStageSequenceMaxLength(nrEvents); } +int StageInstance::StartStageSequence() { RequireInitialized(__func__); return GetImpl()->StartStageSequence(); } +int StageInstance::StopStageSequence() { RequireInitialized(__func__); return GetImpl()->StopStageSequence(); } +int StageInstance::ClearStageSequence() { RequireInitialized(__func__); return GetImpl()->ClearStageSequence(); } +int StageInstance::AddToStageSequence(double position) { RequireInitialized(__func__); return GetImpl()->AddToStageSequence(position); } +int StageInstance::SendStageSequence() { RequireInitialized(__func__); return GetImpl()->SendStageSequence(); } int StageInstance::SetStageLinearSequence(double dZ_um, long nSlices) -{ return GetImpl()->SetStageLinearSequence(dZ_um, nSlices); } \ No newline at end of file +{ RequireInitialized(__func__); return GetImpl()->SetStageLinearSequence(dZ_um, nSlices); } \ No newline at end of file diff --git a/MMCore/Devices/StateInstance.cpp b/MMCore/Devices/StateInstance.cpp index a0125b098..7939aa43e 100644 --- a/MMCore/Devices/StateInstance.cpp +++ b/MMCore/Devices/StateInstance.cpp @@ -22,12 +22,13 @@ #include "StateInstance.h" -int StateInstance::SetPosition(long pos) { return GetImpl()->SetPosition(pos); } -int StateInstance::SetPosition(const char* label) { return GetImpl()->SetPosition(label); } -int StateInstance::GetPosition(long& pos) const { return GetImpl()->GetPosition(pos); } +int StateInstance::SetPosition(long pos) { RequireInitialized(__func__); return GetImpl()->SetPosition(pos); } +int StateInstance::SetPosition(const char* label) { RequireInitialized(__func__); return GetImpl()->SetPosition(label); } +int StateInstance::GetPosition(long& pos) const { RequireInitialized(__func__); return GetImpl()->GetPosition(pos); } std::string StateInstance::GetPositionLabel() const { + RequireInitialized(__func__); DeviceStringBuffer labelBuf(this, "GetPosition"); int err = GetImpl()->GetPosition(labelBuf.GetBuffer()); ThrowIfError(err, "Cannot get current position label"); @@ -36,14 +37,15 @@ std::string StateInstance::GetPositionLabel() const std::string StateInstance::GetPositionLabel(long pos) const { + RequireInitialized(__func__); DeviceStringBuffer labelBuf(this, "GetPositionLabel"); int err = GetImpl()->GetPositionLabel(pos, labelBuf.GetBuffer()); ThrowIfError(err, "Cannot get position label at index " + ToString(pos)); return labelBuf.Get(); } -int StateInstance::GetLabelPosition(const char* label, long& pos) const { return GetImpl()->GetLabelPosition(label, pos); } -int StateInstance::SetPositionLabel(long pos, const char* label) { return GetImpl()->SetPositionLabel(pos, label); } -unsigned long StateInstance::GetNumberOfPositions() const { return GetImpl()->GetNumberOfPositions(); } -int StateInstance::SetGateOpen(bool open) { return GetImpl()->SetGateOpen(open); } -int StateInstance::GetGateOpen(bool& open) { return GetImpl()->GetGateOpen(open); } +int StateInstance::GetLabelPosition(const char* label, long& pos) const { RequireInitialized(__func__); return GetImpl()->GetLabelPosition(label, pos); } +int StateInstance::SetPositionLabel(long pos, const char* label) { RequireInitialized(__func__); return GetImpl()->SetPositionLabel(pos, label); } +unsigned long StateInstance::GetNumberOfPositions() const { RequireInitialized(__func__); return GetImpl()->GetNumberOfPositions(); } +int StateInstance::SetGateOpen(bool open) { RequireInitialized(__func__); return GetImpl()->SetGateOpen(open); } +int StateInstance::GetGateOpen(bool& open) { RequireInitialized(__func__); return GetImpl()->GetGateOpen(open); } diff --git a/MMCore/Devices/XYStageInstance.cpp b/MMCore/Devices/XYStageInstance.cpp index e3abb766f..7b7c2908c 100644 --- a/MMCore/Devices/XYStageInstance.cpp +++ b/MMCore/Devices/XYStageInstance.cpp @@ -22,27 +22,27 @@ #include "XYStageInstance.h" -int XYStageInstance::SetPositionUm(double x, double y) { return GetImpl()->SetPositionUm(x, y); } -int XYStageInstance::SetRelativePositionUm(double dx, double dy) { return GetImpl()->SetRelativePositionUm(dx, dy); } -int XYStageInstance::SetAdapterOriginUm(double x, double y) { return GetImpl()->SetAdapterOriginUm(x, y); } -int XYStageInstance::GetPositionUm(double& x, double& y) { return GetImpl()->GetPositionUm(x, y); } -int XYStageInstance::GetLimitsUm(double& xMin, double& xMax, double& yMin, double& yMax) { return GetImpl()->GetLimitsUm(xMin, xMax, yMin, yMax); } -int XYStageInstance::Move(double vx, double vy) { return GetImpl()->Move(vx, vy); } -int XYStageInstance::SetPositionSteps(long x, long y) { return GetImpl()->SetPositionSteps(x, y); } -int XYStageInstance::GetPositionSteps(long& x, long& y) { return GetImpl()->GetPositionSteps(x, y); } -int XYStageInstance::SetRelativePositionSteps(long x, long y) { return GetImpl()->SetRelativePositionSteps(x, y); } -int XYStageInstance::Home() { return GetImpl()->Home(); } -int XYStageInstance::Stop() { return GetImpl()->Stop(); } -int XYStageInstance::SetOrigin() { return GetImpl()->SetOrigin(); } -int XYStageInstance::SetXOrigin() { return GetImpl()->SetXOrigin(); } -int XYStageInstance::SetYOrigin() { return GetImpl()->SetYOrigin(); } -int XYStageInstance::GetStepLimits(long& xMin, long& xMax, long& yMin, long& yMax) { return GetImpl()->GetStepLimits(xMin, xMax, yMin, yMax); } -double XYStageInstance::GetStepSizeXUm() { return GetImpl()->GetStepSizeXUm(); } -double XYStageInstance::GetStepSizeYUm() { return GetImpl()->GetStepSizeYUm(); } -int XYStageInstance::IsXYStageSequenceable(bool& isSequenceable) const { return GetImpl()->IsXYStageSequenceable(isSequenceable); } -int XYStageInstance::GetXYStageSequenceMaxLength(long& nrEvents) const { return GetImpl()->GetXYStageSequenceMaxLength(nrEvents); } -int XYStageInstance::StartXYStageSequence() { return GetImpl()->StartXYStageSequence(); } -int XYStageInstance::StopXYStageSequence() { return GetImpl()->StopXYStageSequence(); } -int XYStageInstance::ClearXYStageSequence() { return GetImpl()->ClearXYStageSequence(); } -int XYStageInstance::AddToXYStageSequence(double positionX, double positionY) { return GetImpl()->AddToXYStageSequence(positionX, positionY); } -int XYStageInstance::SendXYStageSequence() { return GetImpl()->SendXYStageSequence(); } +int XYStageInstance::SetPositionUm(double x, double y) { RequireInitialized(__func__); return GetImpl()->SetPositionUm(x, y); } +int XYStageInstance::SetRelativePositionUm(double dx, double dy) { RequireInitialized(__func__); return GetImpl()->SetRelativePositionUm(dx, dy); } +int XYStageInstance::SetAdapterOriginUm(double x, double y) { RequireInitialized(__func__); return GetImpl()->SetAdapterOriginUm(x, y); } +int XYStageInstance::GetPositionUm(double& x, double& y) { RequireInitialized(__func__); return GetImpl()->GetPositionUm(x, y); } +int XYStageInstance::GetLimitsUm(double& xMin, double& xMax, double& yMin, double& yMax) { RequireInitialized(__func__); return GetImpl()->GetLimitsUm(xMin, xMax, yMin, yMax); } +int XYStageInstance::Move(double vx, double vy) { RequireInitialized(__func__); return GetImpl()->Move(vx, vy); } +int XYStageInstance::SetPositionSteps(long x, long y) { RequireInitialized(__func__); return GetImpl()->SetPositionSteps(x, y); } +int XYStageInstance::GetPositionSteps(long& x, long& y) { RequireInitialized(__func__); return GetImpl()->GetPositionSteps(x, y); } +int XYStageInstance::SetRelativePositionSteps(long x, long y) { RequireInitialized(__func__); return GetImpl()->SetRelativePositionSteps(x, y); } +int XYStageInstance::Home() { RequireInitialized(__func__); return GetImpl()->Home(); } +int XYStageInstance::Stop() { RequireInitialized(__func__); return GetImpl()->Stop(); } +int XYStageInstance::SetOrigin() { RequireInitialized(__func__); return GetImpl()->SetOrigin(); } +int XYStageInstance::SetXOrigin() { RequireInitialized(__func__); return GetImpl()->SetXOrigin(); } +int XYStageInstance::SetYOrigin() { RequireInitialized(__func__); return GetImpl()->SetYOrigin(); } +int XYStageInstance::GetStepLimits(long& xMin, long& xMax, long& yMin, long& yMax) { RequireInitialized(__func__); return GetImpl()->GetStepLimits(xMin, xMax, yMin, yMax); } +double XYStageInstance::GetStepSizeXUm() { RequireInitialized(__func__); return GetImpl()->GetStepSizeXUm(); } +double XYStageInstance::GetStepSizeYUm() { RequireInitialized(__func__); return GetImpl()->GetStepSizeYUm(); } +int XYStageInstance::IsXYStageSequenceable(bool& isSequenceable) const { RequireInitialized(__func__); return GetImpl()->IsXYStageSequenceable(isSequenceable); } +int XYStageInstance::GetXYStageSequenceMaxLength(long& nrEvents) const { RequireInitialized(__func__); return GetImpl()->GetXYStageSequenceMaxLength(nrEvents); } +int XYStageInstance::StartXYStageSequence() { RequireInitialized(__func__); return GetImpl()->StartXYStageSequence(); } +int XYStageInstance::StopXYStageSequence() { RequireInitialized(__func__); return GetImpl()->StopXYStageSequence(); } +int XYStageInstance::ClearXYStageSequence() { RequireInitialized(__func__); return GetImpl()->ClearXYStageSequence(); } +int XYStageInstance::AddToXYStageSequence(double positionX, double positionY) { RequireInitialized(__func__); return GetImpl()->AddToXYStageSequence(positionX, positionY); } +int XYStageInstance::SendXYStageSequence() { RequireInitialized(__func__); return GetImpl()->SendXYStageSequence(); } diff --git a/MMCore/ErrorCodes.h b/MMCore/ErrorCodes.h index 9e54a7aeb..b63a2e918 100644 --- a/MMCore/ErrorCodes.h +++ b/MMCore/ErrorCodes.h @@ -48,7 +48,7 @@ #define MMERR_NoConfiguration 20 #define MMERR_InvalidConfigurationIndex 21 #define MMERR_DEVICE_GENERIC 22 -#define MMERR_InvalidPropertyBlock 23 +#define MMERR_InvalidPropertyBlock 23 // No longer used #define MMERR_UnhandledException 24 #define MMERR_DevicePollingTimeout 25 #define MMERR_InvalidShutterDevice 26 diff --git a/MMCore/Host.cpp b/MMCore/Host.cpp deleted file mode 100644 index d87c0de7b..000000000 --- a/MMCore/Host.cpp +++ /dev/null @@ -1,226 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// FILE: Host.cpp -// PROJECT: Micro-Manager -// SUBSYSTEM: MMCore -//----------------------------------------------------------------------------- -// DESCRIPTION: Multi-platform implementation of some simple network facilities -// -// COPYRIGHT: University of California, San Francisco, 2011, -// -// 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: Karl Hoover karl.hoover@gmail.com 2011 - - -#include "Host.h" - -#ifdef _WINDOWS -#include -#include "Iphlpapi.h" -#include -#endif //_WINDOWS - -#include "../MMDevice/FixSnprintf.h" - -#ifdef __APPLE__ - -#include "AppleHost.h" - -#endif //__APPLE__ - -#ifdef __linux__ -#include -#include -#include -#include -#include -#include -#include -#include -#endif // __linux__ - - -Host::Host(void) -{ -} - -Host::~Host(void) -{ -} - - - -// a function to call into the OS stuff - -std::vector Host::getMACAddresses(long& status) -{ - // so far no problem... - status = 0; - std::vector retval; - -#ifdef _WINDOWS - - // Get the buffer length required for IP_ADAPTER_INFO. - ULONG BufferLength = 0; - BYTE* pBuffer = 0; - long osstatus; - osstatus = GetAdaptersInfo( 0, &BufferLength ); - if( ERROR_BUFFER_OVERFLOW == osstatus) - { - // Now the BufferLength contain the required buffer length. - // Allocate necessary buffer. - pBuffer = new BYTE[ BufferLength ]; - } - else - { - status = (0==osstatus?-1:osstatus); - } - - if( 0 == status) - { - // Get the Adapter Information. - PIP_ADAPTER_INFO pAdapterInfo = reinterpret_cast(pBuffer); - GetAdaptersInfo( pAdapterInfo, &BufferLength ); - - // Iterate the network adapters and print their MAC address. - while( pAdapterInfo ) - { - MACValue v; // it's really a long long - // Assuming pAdapterInfo->AddressLength is 6. - memcpy(&v, pAdapterInfo->Address, 6); - retval.push_back(v); - - // Get next adapter info. - pAdapterInfo = pAdapterInfo->Next; - } - - // deallocate the buffer. - delete[] pBuffer; - } - -#endif //_WINDOWS - - -#ifdef __APPLE__ - - io_iterator_t intfIterator; - UInt8 MACAddress[kIOEthernetAddressSize]; - - int kernResult = FindEthernetInterfaces(&intfIterator); - - if (KERN_SUCCESS != kernResult) { - status = (long)(0==kernResult?-1:kernResult); - } - else { - kernResult = GetMACAddress(intfIterator, MACAddress, sizeof(MACAddress)); - - if (KERN_SUCCESS != kernResult) { - status = (long)(0==kernResult?-1:kernResult); - } - else { - MACValue v; - memcpy(&v, MACAddress, sizeof(v)); - - - retval.push_back(v); - } - } - - (void) IOObjectRelease(intfIterator); // Release the iterator. - //#endif //APPLEHOSTIMPL - - - -#endif // __APPLE__ - -#ifdef __linux__ - - int sock; - sock = socket(PF_INET, SOCK_DGRAM, 0); - if ( -1 == sock ) { - status = static_cast(errno); - } - else { - char buf[2048]; - ifconf ifc; - ifc.ifc_buf = buf; - ifc.ifc_len = sizeof(buf); - int osstatus = ioctl(sock, SIOCGIFCONF, &ifc); - if ( -1 == osstatus ) { - status = static_cast(errno); - } - else { - MACValue macaddr; - for ( ifreq *ifr(ifc.ifc_req), *ifrEnd(ifc.ifc_req + ifc.ifc_len / sizeof(ifreq)); - ifr != ifrEnd; - ++ifr ) - { - osstatus = ioctl(sock, SIOCGIFHWADDR, ifr); - if ( osstatus == 0 ) { - macaddr = 0; - memcpy(&macaddr, ifr->ifr_hwaddr.sa_data, 6); - if(macaddr != 0) { - retval.push_back(macaddr); - } - } - } - // Raise error flag if no mac address could be found for *any* interface. The loopback and some tunnel - // interfaces, for example, may have an IP address but only one peer and therefore no need for a media layer - // address. So, some mac addr retrieval failures and null mac addrs are to be expected. - if ( retval.empty() ) { - status = static_cast(errno); - } - } - close(sock); - } - - -#endif // __linux__ - - - - return retval; -} - - - -std::vector Host::MACAddresses(long& status) -{ - - std::vector retval; - - std::vector values = getMACAddresses(status); - - if( 0 == status) - { - - std::vector::iterator j; - for( j = values.begin(); j!=values.end(); ++j) - { - unsigned char ctmp[6]; - memcpy( ctmp, &(*j), 6); - - char address[19]; - snprintf(address, 19, "%02x-%02x-%02x-%02x-%02x-%02x", - ctmp[0], - ctmp[1], - ctmp[2], - ctmp[3], - ctmp[4], - ctmp[5]); - retval.push_back(std::string(address)); - } - } - - return retval; - -} diff --git a/MMCore/Host.h b/MMCore/Host.h deleted file mode 100644 index 54e8dc5b2..000000000 --- a/MMCore/Host.h +++ /dev/null @@ -1,41 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// FILE: Host.cpp -// PROJECT: Micro-Manager -// SUBSYSTEM: MMCore -//----------------------------------------------------------------------------- -// DESCRIPTION: Multi-platform declaration of some simple network facilities -// -// COPYRIGHT: University of California, San Francisco, 2011, -// -// 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: Karl Hoover karl.hoover@gmail.com 2011 -#ifndef HOST_H -#define HOST_H -#include -#include - -typedef long long MACValue; - - -class Host -{ -public: - Host(void); - ~Host(void); - std::vector MACAddresses(long& status); - std::vector getMACAddresses(long& status); - - -}; - -#endif \ No newline at end of file diff --git a/MMCore/MMCore.cpp b/MMCore/MMCore.cpp index eb55d5391..a04aaa6ef 100644 --- a/MMCore/MMCore.cpp +++ b/MMCore/MMCore.cpp @@ -50,7 +50,6 @@ #include "CoreUtils.h" #include "DeviceManager.h" #include "Devices/DeviceInstances.h" -#include "Host.h" #include "LogManager.h" #include "MMCore.h" #include "MMEventCallback.h" @@ -65,17 +64,6 @@ #include #include - -#ifndef _WINDOWS -// Needed on Unix for getcwd() and gethostname() -#include -#include -#include -#else -// for _getcwd -#include -#endif - using namespace std; /* @@ -113,7 +101,7 @@ using namespace std; * (Keep the 3 numbers on one line to make it easier to look at diffs when * merging/rebasing.) */ -const int MMCore_versionMajor = 10, MMCore_versionMinor = 4, MMCore_versionPatch = 0; +const int MMCore_versionMajor = 11, MMCore_versionMinor = 0, MMCore_versionPatch = 0; /////////////////////////////////////////////////////////////////////////////// @@ -309,40 +297,6 @@ void CMMCore::stopSecondaryLogFile(int handle) throw (CMMError) logManager_->RemoveSecondaryLogFile(h); } - -/*! - Displays current user name. - */ -string CMMCore::getUserId() const -{ - char buf[8192]; -#ifndef _WINDOWS - struct passwd* ppw = getpwuid(geteuid()); - strcpy( buf, ppw->pw_name); -#else - DWORD bufCharCount = 8192; - if( !GetUserName( buf, &bufCharCount ) ) - buf[0] = 0; -#endif - return string(buf); -} - -/** - * return current computer name. - */ -string CMMCore::getHostName() const -{ - char buf[8192]; -#ifndef _WINDOWS - gethostname(buf, 8192); -#else - DWORD bufCharCount = 8192; - if( !GetComputerName( buf, &bufCharCount ) ) - buf[0] = 0; -#endif - return string(buf); -} - /** * Displays core version. */ @@ -646,43 +600,6 @@ std::vector CMMCore::getDeviceAdapterNames() throw (CMMError) return pluginManager_->GetAvailableDeviceAdapters(); } -/** - * Add a list of paths to the legacy device adapter search path list. - * - * Do not use in new code. This adds to a global (static) fallback list that is - * only searched when a device adapter is not located in any of the directories - * set by setDeviceAdapterSearchPaths(). The list is initially empty. - * - * @deprecated Use the non-static setDeviceAdapterSearchPaths() instead. - * - * @param path a list of search paths in a single string - */ -void CMMCore::addSearchPath(const char *path) -{ - if (!path) - return; - - CPluginManager::AddLegacyFallbackSearchPath(path); -} - -/** - * Returns a list of library names available in the search path. - * - * Do not use in new code. For backward compatibility, this method returns the - * list of device adapters available in the default search path(s) and the - * paths added via addSearchPath(). For obvious reasons (since this method is - * static), it will not return device adapters found in the search paths set by - * setDeviceAdapterSearchPaths(). Thus, this method will only work as expected - * when called from legacy code that does not make use of - * setDeviceAdapterSearchPaths(). - * - * @deprecated Use the non-static getDeviceAdapterNames() instead. - */ -vector CMMCore::getDeviceLibraries() throw (CMMError) -{ - return CPluginManager::GetModulesInLegacyFallbackSearchPaths(); -} - /** * Loads a device from the plugin library. * @param label assigned name for the device during the core session @@ -828,9 +745,6 @@ void CMMCore::unloadAllDevices() throw (CMMError) LOG_INFO(coreLogger_) << "Did unload all devices"; properties_->Refresh(); - - // TODO - // clear equipment definitions ??? } catch (CMMError& err) { logError("MMCore::unloadAllDevices", err.getMsg().c_str()); @@ -839,7 +753,7 @@ void CMMCore::unloadAllDevices() throw (CMMError) } /** - * Unloads all devices from the core, clears all configuration data and property blocks. + * Unloads all devices from the core, clears all configuration data. */ void CMMCore::reset() throw (CMMError) { @@ -866,12 +780,6 @@ void CMMCore::reset() throw (CMMError) // unload devices unloadAllDevices(); - // clear property blocks - CPropBlockMap::const_iterator i; - for (i = propBlocks_.begin(); i != propBlocks_.end(); i++) - delete i->second; - propBlocks_.clear(); - properties_->Refresh(); LOG_INFO(coreLogger_) << "System reset"; @@ -963,6 +871,29 @@ void CMMCore::initializeDevice(const char* label ///< the device to initialize } +/** + * Queries the initialization state of the given device. + * + * @param label the device label + */ +DeviceInitializationState +CMMCore::getDeviceInitializationState(const char* label) const throw (CMMError) +{ + std::shared_ptr pDevice = deviceManager_->GetDevice(label); + + mm::DeviceModuleLockGuard guard(pDevice); + if (pDevice->IsInitialized()) + { + return DeviceInitializationState::InitializedSuccessfully; + } + if (pDevice->HasInitializationBeenAttempted()) + { + return DeviceInitializationState::InitializationFailed; + } + return DeviceInitializationState::Uninitialized; +} + + /** * Updates the state of the entire hardware. @@ -1316,25 +1247,6 @@ void CMMCore::waitForConfig(const char* group, const char* configName) throw (CM } } -/** - * Wait for the slowest device in the ImageSynchro list. - * - * @deprecated ImageSynchro will not be supported in the future. - */ -void CMMCore::waitForImageSynchro() throw (CMMError) -{ - for (std::vector< std::weak_ptr >::iterator - it = imageSynchroDevices_.begin(), end = imageSynchroDevices_.end(); - it != end; ++it) - { - std::shared_ptr device = it->lock(); - if (device) - { - waitForDevice(device); - } - } -} - /** * Sets the position of the stage in microns. * @param label the stage device label @@ -2386,10 +2298,6 @@ void CMMCore::snapImage() throw (CMMError) int ret = DEVICE_OK; try { - - // wait for all synchronized devices to stop before taking an image - waitForImageSynchro(); - // open the shutter std::shared_ptr shutter = currentShutterDevice_.lock(); @@ -2448,72 +2356,6 @@ void CMMCore::snapImage() throw (CMMError) } } - -// Predicate used by assignImageSynchro() and removeImageSynchro() -namespace -{ - class DeviceWeakPtrInvalidOrMatches - { - std::shared_ptr theDevice_; - public: - explicit DeviceWeakPtrInvalidOrMatches( - std::shared_ptr theDevice) : - theDevice_(theDevice) - {} - - bool operator()(const std::weak_ptr& ptr) - { - std::shared_ptr aDevice = ptr.lock(); - return (!aDevice || aDevice == theDevice_); - } - }; -} // anonymous namespace - -/** - * Add device to the image-synchro list. Image acquisition waits for all devices - * in this list. - * @param label the device label - * - * @deprecated ImageSynchro will not be supported in the future. - */ -void CMMCore::assignImageSynchro(const char* label) throw (CMMError) -{ - std::shared_ptr device = deviceManager_->GetDevice(label); - - imageSynchroDevices_.erase(std::remove_if(imageSynchroDevices_.begin(), - imageSynchroDevices_.end(), DeviceWeakPtrInvalidOrMatches(device)), - imageSynchroDevices_.end()); - - imageSynchroDevices_.push_back(device); - LOG_DEBUG(coreLogger_) << "Added " << label << " to image-synchro list"; -} - -/** - * Removes device from the image-synchro list. - * @param label the device label - * - * @deprecated ImageSynchro will not be supported in the future. - */ -void CMMCore::removeImageSynchro(const char* label) throw (CMMError) -{ - std::shared_ptr device = deviceManager_->GetDevice(label); - imageSynchroDevices_.erase(std::remove_if(imageSynchroDevices_.begin(), - imageSynchroDevices_.end(), DeviceWeakPtrInvalidOrMatches(device)), - imageSynchroDevices_.end()); - LOG_DEBUG(coreLogger_) << "Removed " << label << " from image-synchro list"; -} - -/** - * Clears the image synchro device list. - * - * @deprecated ImageSynchro will not be supported in the future. - */ -void CMMCore::removeImageSynchroAll() -{ - imageSynchroDevices_.clear(); - LOG_DEBUG(coreLogger_) << "Cleared image-synchro list"; -} - /** * If this option is enabled Shutter automatically opens and closes when the image * is acquired. @@ -2740,13 +2582,17 @@ long CMMCore::getImageBufferSize() { std::shared_ptr camera = currentCameraDevice_.lock(); if (camera) { - mm::DeviceModuleLockGuard guard(camera); - return camera->GetImageBufferSize(); - } - else - { - return 0; + try + { + mm::DeviceModuleLockGuard guard(camera); + return camera->GetImageBufferSize(); + } + catch (const CMMError&) // Possibly uninitialized camera + { + // Fall through + } } + return 0; } /** @@ -2821,6 +2667,13 @@ void CMMCore::startSequenceAcquisition(const char* label, long numImages, double throw CMMError(getCoreErrorText(MMERR_NotAllowedDuringSequenceAcquisition).c_str(), MMERR_NotAllowedDuringSequenceAcquisition); + if (!cbuf_->Initialize(pCam->GetNumberOfChannels(), pCam->GetImageWidth(), pCam->GetImageHeight(), pCam->GetImageBytesPerPixel())) + { + logError(getDeviceName(pCam).c_str(), getCoreErrorText(MMERR_CircularBufferFailedToInitialize).c_str()); + throw CMMError(getCoreErrorText(MMERR_CircularBufferFailedToInitialize).c_str(), MMERR_CircularBufferFailedToInitialize); + } + cbuf_->Clear(); + LOG_DEBUG(coreLogger_) << "Will start sequence acquisition from camera " << label; int nRet = pCam->StartSequenceAcquisition(numImages, intervalMs, stopOnOverflow); @@ -2971,13 +2824,17 @@ bool CMMCore::isSequenceRunning() throw () std::shared_ptr camera = currentCameraDevice_.lock(); if (camera) { - mm::DeviceModuleLockGuard guard(camera); - return camera->IsCapturing(); - } - else - { - return false; + try + { + mm::DeviceModuleLockGuard guard(camera); + return camera->IsCapturing(); + } + catch (const CMMError&) // Possibly uninitialized camera + { + // Fall through + } } + return false; }; /** @@ -4077,13 +3934,19 @@ MM::PropertyType CMMCore::getPropertyType(const char* label, const char* propNam unsigned CMMCore::getImageWidth() { std::shared_ptr camera = currentCameraDevice_.lock(); - if (!camera) + if (camera) { - return 0; + try + { + mm::DeviceModuleLockGuard guard(camera); + return camera->GetImageWidth(); + } + catch (const CMMError&) // Possibly uninitialized camera + { + // Fall through + } } - - mm::DeviceModuleLockGuard guard(camera); - return camera->GetImageWidth(); + return 0; } /** @@ -4093,13 +3956,19 @@ unsigned CMMCore::getImageWidth() unsigned CMMCore::getImageHeight() { std::shared_ptr camera = currentCameraDevice_.lock(); - if (!camera) + if (camera) { - return 0; + try + { + mm::DeviceModuleLockGuard guard(camera); + return camera->GetImageHeight(); + } + catch (const CMMError&) // Possibly uninitialized camera + { + // Fall through + } } - - mm::DeviceModuleLockGuard guard(camera); - return camera->GetImageHeight(); + return 0; } /** @@ -4110,13 +3979,19 @@ unsigned CMMCore::getImageHeight() unsigned CMMCore::getBytesPerPixel() { std::shared_ptr camera = currentCameraDevice_.lock(); - if (!camera) + if (camera) { - return 0; + try + { + mm::DeviceModuleLockGuard guard(camera); + return camera->GetImageBytesPerPixel(); + } + catch (const CMMError&) // Possibly uninitialized camera + { + // Fall through + } } - - mm::DeviceModuleLockGuard guard(camera); - return camera->GetImageBytesPerPixel(); + return 0; } /** @@ -4129,13 +4004,19 @@ unsigned CMMCore::getBytesPerPixel() unsigned CMMCore::getImageBitDepth() { std::shared_ptr camera = currentCameraDevice_.lock(); - if (!camera) + if (camera) { - return 0; + try + { + mm::DeviceModuleLockGuard guard(camera); + return camera->GetBitDepth(); + } + catch (const CMMError&) // Possibly uninitialized camera + { + // Fall through + } } - - mm::DeviceModuleLockGuard guard(camera); - return camera->GetBitDepth(); + return 0; } /** @@ -4145,12 +4026,19 @@ unsigned CMMCore::getImageBitDepth() unsigned CMMCore::getNumberOfComponents() { std::shared_ptr camera = currentCameraDevice_.lock(); - if (!camera) + if (camera) { - return 0; + try + { + mm::DeviceModuleLockGuard guard(camera); + return camera->GetNumberOfComponents(); + } + catch (const CMMError&) // Possibly uninitialized camera + { + // Fall through + } } - mm::DeviceModuleLockGuard guard(camera); - return camera->GetNumberOfComponents(); + return 0; } /** @@ -4159,13 +4047,19 @@ unsigned CMMCore::getNumberOfComponents() unsigned CMMCore::getNumberOfCameraChannels() { std::shared_ptr camera = currentCameraDevice_.lock(); - if (!camera) + if (camera) { - return 0; + try + { + mm::DeviceModuleLockGuard guard(camera); + return camera->GetNumberOfChannels(); + } + catch (const CMMError&) // Possibly uninitialized camera + { + // Fall through + } } - - mm::DeviceModuleLockGuard guard(camera); - return camera->GetNumberOfChannels(); + return 0; } /** @@ -4174,13 +4068,19 @@ unsigned CMMCore::getNumberOfCameraChannels() string CMMCore::getCameraChannelName(unsigned int channelNr) { std::shared_ptr camera = currentCameraDevice_.lock(); - if (!camera) + if (camera) { - return std::string(); + try + { + mm::DeviceModuleLockGuard guard(camera); + return camera->GetChannelName(channelNr); + } + catch (const CMMError&) // Possibly uninitialized camera + { + // Fall through + } } - - mm::DeviceModuleLockGuard guard(camera); - return camera->GetChannelName(channelNr); + return std::string(); } /** @@ -5472,8 +5372,15 @@ double CMMCore::getPixelSizeUm(bool cached) std::shared_ptr camera = currentCameraDevice_.lock(); if (camera) { - mm::DeviceModuleLockGuard guard(camera); - pixSize *= camera->GetBinning(); + try + { + mm::DeviceModuleLockGuard guard(camera); + pixSize *= camera->GetBinning(); + } + catch (const CMMError&) // Possibly uninitialized camera + { + // Assume no binning + } } pixSize /= getMagnificationFactor(); @@ -5598,17 +5505,18 @@ double CMMCore::getMagnificationFactor() const vector magnifiers = getLoadedDevicesOfType(MM::MagnifierDevice); for (size_t i=0; i magnifier = + deviceManager_->GetDeviceOfType(magnifiers[i]); + try { - std::shared_ptr magnifier = - deviceManager_->GetDeviceOfType(magnifiers[i]); - mm::DeviceModuleLockGuard guard(magnifier); magnification *= magnifier->GetMagnification(); } catch (const CMMError&) { - assert(!"Internal error in generating a list of specific devices"); + // Most likely the magnifier was not initialized. + // Ignore it: only initialized magnifiers count. } } return magnification; @@ -5640,136 +5548,6 @@ bool CMMCore::isGroupDefined(const char* groupName) return configGroups_->isDefined(groupName); } -/** - * Defines a reference for the collection of property-value pairs. - * This construct is useful for defining - * interchangeable equipment features, such as objective magnifications, filter wavelengths, etc. - * - * @deprecated Property blocks will not be supported in the future. - */ -void CMMCore::definePropertyBlock(const char* blockName, const char* propertyName, const char* propertyValue) -{ - CheckPropertyBlockName(blockName); - CheckPropertyName(propertyName); - CheckPropertyValue(propertyValue); - - // check if the block already exists - CPropBlockMap::const_iterator it = propBlocks_.find(blockName); - PropertyBlock* pBlock; - if (it == propBlocks_.end()) - { - pBlock = new PropertyBlock(); - propBlocks_[blockName] = pBlock; // add new block - } - else - pBlock = it->second; - - // add the pair - PropertyPair pair(propertyName, propertyValue); - pBlock->addPair(pair); - - LOG_DEBUG(coreLogger_) << "Property block " << blockName << - ": added setting " << propertyName << " = " << propertyValue; -} - -/** - * Returns all defined property block identifiers. - * - * @deprecated Property blocks will not be supported in the future. - */ -std::vector CMMCore::getAvailablePropertyBlocks() const -{ - vector blkList; - CPropBlockMap::const_iterator it = propBlocks_.begin(); - while(it != propBlocks_.end()) - blkList.push_back(it++->first); - - return blkList; -} - -/** - * Returns the collection of property-value pairs defined in this block. - * - * @deprecated Property blocks will not be supported in the future. - */ -PropertyBlock CMMCore::getPropertyBlockData(const char* blockName) -{ - CheckPropertyBlockName(blockName); - - CPropBlockMap::const_iterator it = propBlocks_.find(blockName); - if (it == propBlocks_.end()) - { - logError(blockName, getCoreErrorText(MMERR_InvalidPropertyBlock).c_str()); - throw CMMError(ToQuotedString(blockName) + ": " + getCoreErrorText(MMERR_InvalidPropertyBlock), - MMERR_InvalidPropertyBlock); - } - return *it->second; -} - -/** - * Returns the collection of property-value pairs defined for the specific device and state label. - * - * @deprecated Property blocks will not be supported in the future. - */ -PropertyBlock CMMCore::getStateLabelData(const char* deviceLabel, const char* stateLabel) -{ - std::shared_ptr pStateDev = - deviceManager_->GetDeviceOfType(deviceLabel); - CheckStateLabel(stateLabel); - - mm::DeviceModuleLockGuard guard(pStateDev); - - // check if corresponding label exists - long pos; - int nRet = pStateDev->GetLabelPosition(stateLabel, pos); - if (nRet != DEVICE_OK) - { - logError(deviceLabel, getDeviceErrorText(nRet, pStateDev).c_str()); - throw CMMError(getDeviceErrorText(nRet, pStateDev)); - } - - PropertyBlock blk; - try { - blk = getPropertyBlockData(stateLabel); - } catch (...) { - ; - // if getting data did not succeed for any reason we will assume - // that there is no connection between state label and property block. - // In this context it is not an error, we'll just say there is no data. - } - return blk; -} - -/** - * Returns the collection of property-value pairs defined for the current state. - * - * @deprecated Property blocks will not be supported in the future. - */ -PropertyBlock CMMCore::getData(const char* deviceLabel) -{ - std::shared_ptr pStateDev = - deviceManager_->GetDeviceOfType(deviceLabel); - - // here we could have written simply: - // return getStateLabelData(deviceLabel, getStateLabel(deviceLabel).c_str()); - // but that would be inefficient because of the multiple index lookup, so we'll - // do it explicitly: - - mm::DeviceModuleLockGuard guard(pStateDev); - - // obtain the current state label - std::string pos = pStateDev->GetPositionLabel(); - - PropertyBlock blk; - try { - blk = getPropertyBlockData(pos.c_str()); - } catch (...) { - ; - // not an error here - there is just no data for this entry. - } - return blk; -} - /** * Sets all com port properties in a single call */ @@ -6520,7 +6298,7 @@ void CMMCore::loadSystemState(const char* fileName) throw (CMMError) /** * Saves the current system configuration to a text file of the MM specific format. * The configuration file records only the information essential to the hardware - * setup: devices, labels, equipment pre-initialization properties, and configurations. + * setup: devices, labels, pre-initialization properties, and configurations. * The file format is the same as for the system state. */ void CMMCore::saveSystemConfiguration(const char* fileName) throw (CMMError) @@ -6553,20 +6331,6 @@ void CMMCore::saveSystemConfiguration(const char* fileName) throw (CMMError) os << MM::g_CFGCommand_Device << "," << *it << "," << pDev->GetAdapterModule()->GetName() << "," << pDev->GetName() << endl; } - // save equipment - os << "# Equipment attributes" << endl; - vector propBlocks = getAvailablePropertyBlocks(); - for (size_t i=0; iGetLastFocusScore(score); - if (ret != DEVICE_OK) - return 0.0; - return score; + try + { + mm::DeviceModuleLockGuard guard(autofocus); + double score; + int ret = autofocus->GetLastFocusScore(score); + if (ret == DEVICE_OK) + return score; + } + catch (const CMMError&) // Probably uninitialized device + { + // Fall through + } } - else - return 0.0; + return 0.0; } /** @@ -7042,15 +6804,20 @@ double CMMCore::getCurrentFocusScore() currentAutofocusDevice_.lock(); if (autofocus) { - mm::DeviceModuleLockGuard guard(autofocus); - double score; - int ret = autofocus->GetCurrentFocusScore(score); - if (ret != DEVICE_OK) - return 0.0; - return score; + try + { + mm::DeviceModuleLockGuard guard(autofocus); + double score; + int ret = autofocus->GetCurrentFocusScore(score); + if (ret == DEVICE_OK) + return score; + } + catch (const CMMError&) // Probably uninitialized device + { + // Fall through + } } - else - return 0.0; + return 0.0; } @@ -7264,7 +7031,7 @@ void CMMCore::InitializeErrorMessages() errorText_[MMERR_CameraNotAvailable] = "Camera not loaded or initialized."; errorText_[MMERR_InvalidStateDevice] = "Unsupported API. This device is not a state device"; errorText_[MMERR_NoConfiguration] = "Configuration not defined"; - errorText_[MMERR_InvalidPropertyBlock] = "Property block not defined"; + errorText_[MMERR_InvalidPropertyBlock] = "Property block not defined"; // No longer used errorText_[MMERR_UnhandledException] = "Internal inconsistency: unknown system exception encountered"; errorText_[MMERR_DevicePollingTimeout] = "Device timed out"; @@ -7424,15 +7191,6 @@ void CMMCore::CheckConfigPresetName(const char* presetName) throw (CMMError) MMERR_BadConfigName); } -void CMMCore::CheckPropertyBlockName(const char* blockName) throw (CMMError) -{ - if (!blockName) - throw CMMError("Null property block name", MMERR_NullPointerException); - if (ContainsForbiddenCharacters(blockName)) - throw CMMError("Property block name " + ToQuotedString(blockName) + " contains reserved characters", - MMERR_InvalidContents); -} - bool CMMCore::IsCoreDeviceLabel(const char* label) const throw (CMMError) { if (!label) @@ -7771,39 +7529,3 @@ std::string CMMCore::getInstalledDeviceDescription(const char* hubLabel, const c } return description.empty() ? "N/A" : description; } - -// at least on OS X, there is a 'primary' MAC address, so we'll -// assume that is the first one. -/** -* Retrieve vector of MAC addresses for the Ethernet cards in the current computer -* formatted xx-xx-xx-xx-xx-xx -* -*/ -std::vector CMMCore::getMACAddresses(void) -{ - std::vector retv; - try - { - - Host* pHost = new Host(); - if(NULL != pHost) - { - long status; - retv = pHost->MACAddresses(status); - - if( 0 != status) - { - LOG_ERROR(coreLogger_) << "Error " << status << - " while getting MAC address"; - } - delete pHost; - } - } - catch(...) - { - - } - return retv; -} - - diff --git a/MMCore/MMCore.h b/MMCore/MMCore.h index 71addcb64..aa355a9b8 100644 --- a/MMCore/MMCore.h +++ b/MMCore/MMCore.h @@ -91,7 +91,6 @@ class CorePropertyCollection; class MMEventCallback; class Metadata; class PixelSizeConfigGroup; -class PropertyBlock; class AutoFocusInstance; class CameraInstance; @@ -112,6 +111,12 @@ namespace mm { typedef unsigned int* imgRGB32; +enum DeviceInitializationState { + Uninitialized, + InitializedSuccessfully, + InitializationFailed, +}; + /// The Micro-Manager Core. /** @@ -147,6 +152,7 @@ class CMMCore void unloadAllDevices() throw (CMMError); void initializeAllDevices() throw (CMMError); void initializeDevice(const char* label) throw (CMMError); + DeviceInitializationState getDeviceInitializationState(const char* label) const throw (CMMError); void reset() throw (CMMError); void unloadLibrary(const char* moduleName) throw (CMMError); @@ -190,10 +196,8 @@ class CMMCore ///@{ std::vector getDeviceAdapterSearchPaths(); void setDeviceAdapterSearchPaths(const std::vector& paths); - MMCORE_DEPRECATED(static void addSearchPath(const char *path)); std::vector getDeviceAdapterNames() throw (CMMError); - MMCORE_DEPRECATED(static std::vector getDeviceLibraries() throw (CMMError)); std::vector getAvailableDevices(const char* library) throw (CMMError); std::vector getAvailableDeviceDescriptions(const char* library) throw (CMMError); @@ -240,7 +244,6 @@ class CMMCore void waitForConfig(const char* group, const char* configName) throw (CMMError); bool systemBusy() throw (CMMError); void waitForSystem() throw (CMMError); - MMCORE_DEPRECATED(void waitForImageSynchro() throw (CMMError)); bool deviceTypeBusy(MM::DeviceType devType) throw (CMMError); void waitForDeviceType(MM::DeviceType devType) throw (CMMError); @@ -341,14 +344,6 @@ class CMMCore Configuration getPixelSizeConfigData(const char* configName) throw (CMMError); ///@} - /** \name Property blocks. */ - ///@{ - MMCORE_DEPRECATED(void definePropertyBlock(const char* blockName, const char* propertyName, - const char* propertyValue)); - MMCORE_DEPRECATED(std::vector getAvailablePropertyBlocks() const); - MMCORE_DEPRECATED(PropertyBlock getPropertyBlockData(const char* blockName)); - ///@} - /** \name Image acquisition. */ ///@{ void setROI(int x, int y, int xSize, int ySize) throw (CMMError); @@ -384,10 +379,6 @@ class CMMCore std::string getCameraChannelName(unsigned int channelNr); long getImageBufferSize(); - MMCORE_DEPRECATED(void assignImageSynchro(const char* deviceLabel) throw (CMMError)); - MMCORE_DEPRECATED(void removeImageSynchro(const char* deviceLabel) throw (CMMError)); - MMCORE_DEPRECATED(void removeImageSynchroAll()); - void setAutoShutter(bool state); bool getAutoShutter(); void setShutterOpen(bool state) throw (CMMError); @@ -462,9 +453,6 @@ class CMMCore throw (CMMError); long getStateFromLabel(const char* stateDeviceLabel, const char* stateLabel) throw (CMMError); - MMCORE_DEPRECATED(PropertyBlock getStateLabelData(const char* stateDeviceLabel, - const char* stateLabel)); - MMCORE_DEPRECATED(PropertyBlock getData(const char* stateDeviceLabel)); ///@} /** \name Focus (Z) stage control. */ @@ -627,20 +615,11 @@ class CMMCore std::vector getLoadedPeripheralDevices(const char* hubLabel) throw (CMMError); ///@} - /** \name Miscellaneous. */ - ///@{ - MMCORE_DEPRECATED(std::string getUserId() const); - MMCORE_DEPRECATED(std::string getHostName() const); - MMCORE_DEPRECATED(std::vector getMACAddresses(void)); - ///@} - private: // make object non-copyable CMMCore(const CMMCore&); CMMCore& operator=(const CMMCore&); - typedef std::map CPropBlockMap; - private: // LogManager should be the first data member, so that it is available for // as long as possible during construction and (especially) destruction. @@ -671,11 +650,9 @@ class CMMCore PixelSizeConfigGroup* pixelSizeGroup_; CircularBuffer* cbuf_; - std::vector< std::weak_ptr > imageSynchroDevices_; std::shared_ptr pluginManager_; std::shared_ptr deviceManager_; std::map errorText_; - CPropBlockMap propBlocks_; // Must be unlocked when calling MMEventCallback or calling device methods // or acquiring a module lock @@ -696,7 +673,6 @@ class CMMCore static void CheckStateLabel(const char* stateLabel) throw (CMMError); static void CheckConfigGroupName(const char* groupName) throw (CMMError); static void CheckConfigPresetName(const char* presetName) throw (CMMError); - static void CheckPropertyBlockName(const char* blockName) throw (CMMError); bool IsCoreDeviceLabel(const char* label) const throw (CMMError); void applyConfiguration(const Configuration& config) throw (CMMError); diff --git a/MMCore/MMCore.vcxproj b/MMCore/MMCore.vcxproj index 0b2428216..e639480d5 100644 --- a/MMCore/MMCore.vcxproj +++ b/MMCore/MMCore.vcxproj @@ -59,7 +59,6 @@ $(OutDir)MMCore.lib - Iphlpapi.lib @@ -75,7 +74,6 @@ $(OutDir)MMCore.lib - Iphlpapi.lib @@ -100,7 +98,6 @@ - @@ -143,7 +140,6 @@ - @@ -185,4 +181,4 @@ - \ No newline at end of file + diff --git a/MMCore/MMCore.vcxproj.filters b/MMCore/MMCore.vcxproj.filters index a01ede1fd..d3009c657 100644 --- a/MMCore/MMCore.vcxproj.filters +++ b/MMCore/MMCore.vcxproj.filters @@ -45,9 +45,6 @@ Source Files - - Source Files - Source Files @@ -167,9 +164,6 @@ Header Files - - Header Files - Header Files diff --git a/MMCore/Makefile.am b/MMCore/Makefile.am index 8babcab15..1a2975a6d 100644 --- a/MMCore/Makefile.am +++ b/MMCore/Makefile.am @@ -1,17 +1,13 @@ AUTOMAKE_OPTIONS = foreign subdir-objects -AM_LDFLAGS = $(MMCORE_APPLEHOST_LDFLAGS) - noinst_LTLIBRARIES = libMMCore.la libMMCore_la_LIBADD = ../MMDevice/libMMDevice.la libMMCore_la_SOURCES = \ - ../MMDevice/FixSnprintf.h \ ../MMDevice/MMDevice.h \ ../MMDevice/MMDeviceConstants.h \ ../MMDevice/ModuleInterface.h \ - AppleHost.h \ CircularBuffer.cpp \ CircularBuffer.h \ ConfigGroup.h \ @@ -60,8 +56,6 @@ libMMCore_la_SOURCES = \ ErrorCodes.h \ FrameBuffer.cpp \ FrameBuffer.h \ - Host.cpp \ - Host.h \ LibraryInfo/LibraryPaths.h \ LibraryInfo/LibraryPathsUnix.cpp \ LoadableModules/LoadedDeviceAdapter.cpp \ diff --git a/MMCore/PluginManager.cpp b/MMCore/PluginManager.cpp index 2813b9ea5..125057e07 100644 --- a/MMCore/PluginManager.cpp +++ b/MMCore/PluginManager.cpp @@ -63,8 +63,6 @@ const char* const LIB_NAME_SUFFIX = ""; // CPluginManager class // -------------------- -std::vector CPluginManager::fallbackSearchPaths_; - CPluginManager::CPluginManager() { const std::vector paths = GetDefaultSearchPaths(); @@ -86,15 +84,8 @@ CPluginManager::~CPluginManager() std::string CPluginManager::FindInSearchPath(std::string filename) { - std::vector searchPaths = GetActualSearchPaths(); - - // look in search paths, if there are any - if (searchPaths.size() == 0) - return filename; - - std::vector::const_iterator it; - for (it = searchPaths.begin(); it != searchPaths.end(); it++) { - std::string path(*it); + for (const auto& p : searchPaths_) { + std::string path = p; #ifdef WIN32 path += "\\" + filename + ".dll"; #else @@ -106,11 +97,8 @@ CPluginManager::FindInSearchPath(std::string filename) in.close(); if (!in.fail()) - // we found it! return path; } - - // not found! return filename; } @@ -188,29 +176,6 @@ CPluginManager::UnloadPluginLibrary(const char* moduleName) } -void -CPluginManager::AddLegacyFallbackSearchPath(const std::string& path) -{ - // TODO Should normalize slashes and cases (depending on platform) before - // comparing. - - // When this function is used, the instance search path - // (preferredSearchPaths_) remains equal to the default. Do not add - // duplicate paths. - std::vector defaultPaths(GetDefaultSearchPaths()); - if (std::find(defaultPaths.begin(), defaultPaths.end(), path) != - defaultPaths.end()) - return; - - // Again, do not add duplicate paths. - if (std::find(fallbackSearchPaths_.begin(), fallbackSearchPaths_.end(), path) != - fallbackSearchPaths_.end()) - return; - - fallbackSearchPaths_.push_back(path); -} - - // TODO Use std::filesystem instead of this. // This stop-gap implementation makes the assumption that the argument is in // the format that could be returned from MMCorePrivate::GetPathOfThisModule() @@ -260,15 +225,6 @@ CPluginManager::GetDefaultSearchPaths() } -std::vector -CPluginManager::GetActualSearchPaths() const -{ - std::vector paths(preferredSearchPaths_); - paths.insert(paths.end(), fallbackSearchPaths_.begin(), fallbackSearchPaths_.end()); - return paths; -} - - /** * List all modules (device libraries) at a given path. */ @@ -330,46 +286,9 @@ CPluginManager::GetModules(std::vector &modules, const char* search std::vector CPluginManager::GetAvailableDeviceAdapters() { - std::vector searchPaths = GetActualSearchPaths(); - - std::vector modules; - - for (std::vector::const_iterator it = searchPaths.begin(), end = searchPaths.end(); it != end; ++it) - GetModules(modules, it->c_str()); - - // Check for duplicates - // XXX Is this the right place to be doing this checking? Shouldn't it be an - // error to have duplicates even if we're not listing all libraries? - std::set moduleSet; - for (std::vector::const_iterator it = modules.begin(), end = modules.end(); it != end; ++it) { - if (moduleSet.count(*it)) { - std::string msg("Duplicate libraries found with name \"" + *it + "\""); - throw CMMError(msg.c_str(), DEVICE_DUPLICATE_LIBRARY); - } - } - - return modules; -} - - -std::vector -CPluginManager::GetModulesInLegacyFallbackSearchPaths() -{ - // Search in default search paths and any that were added to the legacy path - // list. - std::vector paths(GetDefaultSearchPaths()); - for (std::vector::const_iterator it = fallbackSearchPaths_.begin(), - end = fallbackSearchPaths_.end(); - it != end; ++it) - { - if (std::find(paths.begin(), paths.end(), *it) == paths.end()) - paths.push_back(*it); - } - std::vector modules; - for (std::vector::const_iterator it = paths.begin(), end = paths.end(); - it != end; ++it) - GetModules(modules, it->c_str()); + for (const auto& path : searchPaths_) + GetModules(modules, path.c_str()); // Check for duplicates // XXX Is this the right place to be doing this checking? Shouldn't it be an @@ -383,4 +302,4 @@ CPluginManager::GetModulesInLegacyFallbackSearchPaths() } return modules; -} +} \ No newline at end of file diff --git a/MMCore/PluginManager.h b/MMCore/PluginManager.h index b6b6c497f..aa3b1aa45 100644 --- a/MMCore/PluginManager.h +++ b/MMCore/PluginManager.h @@ -42,18 +42,13 @@ class CPluginManager /* final */ void UnloadPluginLibrary(const char* moduleName); - // Device adapter search paths (there are two sets of search paths; see - // CMMCore method documentation) + // Device adapter search paths template void SetSearchPaths(TStringIter begin, TStringIter end) - { preferredSearchPaths_.assign(begin, end); } - std::vector GetSearchPaths() const { return preferredSearchPaths_; } + { searchPaths_.assign(begin, end); } + std::vector GetSearchPaths() const { return searchPaths_; } std::vector GetAvailableDeviceAdapters(); - // Legacy search path support - static void AddLegacyFallbackSearchPath(const std::string& path); - static std::vector GetModulesInLegacyFallbackSearchPaths(); - /** * Return a device adapter module, loading it if necessary */ @@ -64,12 +59,10 @@ class CPluginManager /* final */ private: static std::vector GetDefaultSearchPaths(); - std::vector GetActualSearchPaths() const; static void GetModules(std::vector &modules, const char *path); std::string FindInSearchPath(std::string filename); - std::vector preferredSearchPaths_; - static std::vector fallbackSearchPaths_; + std::vector searchPaths_; std::map< std::string, std::shared_ptr > moduleMap_; }; diff --git a/MMCoreJ_wrap/Makefile.am b/MMCoreJ_wrap/Makefile.am index f408cc480..6e8d61d6d 100644 --- a/MMCoreJ_wrap/Makefile.am +++ b/MMCoreJ_wrap/Makefile.am @@ -27,11 +27,9 @@ swig_sources = MMCoreJ.i \ ../MMCore/CoreUtils.h \ ../MMCore/Error.h \ ../MMCore/ErrorCodes.h \ - ../MMCore/Host.h \ ../MMCore/MMCore.h \ ../MMCore/MMEventCallback.h \ ../MMCore/PluginManager.h \ - ../MMDevice/FixSnprintf.h \ ../MMDevice/ImageMetadata.h \ ../MMDevice/MMDevice.h \ ../MMDevice/MMDeviceConstants.h diff --git a/MMCoreJ_wrap/pom.xml b/MMCoreJ_wrap/pom.xml index c309e7b44..a7f089a36 100644 --- a/MMCoreJ_wrap/pom.xml +++ b/MMCoreJ_wrap/pom.xml @@ -3,7 +3,7 @@ org.micro-manager.mmcorej MMCoreJ jar - 10.4.0 + 11.0.0 Micro-Manager Java Interface to MMCore Micro-Manager is open source software for control of automated/motorized microscopes. This specific packages provides the Java interface to the device abstractino layer (MMCore) that is written in C++ with a C-interface http://micro-manager.org diff --git a/MMDevice/DeviceUtils.cpp b/MMDevice/DeviceUtils.cpp index 7f0df5262..fe5162a8d 100644 --- a/MMDevice/DeviceUtils.cpp +++ b/MMDevice/DeviceUtils.cpp @@ -31,8 +31,6 @@ #include #endif -#include "FixSnprintf.h" - char CDeviceUtils::m_pszBuffer[MM::MaxStrLength]={""}; /** diff --git a/MMDevice/FixSnprintf.h b/MMDevice/FixSnprintf.h deleted file mode 100644 index d30b5d692..000000000 --- a/MMDevice/FixSnprintf.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Provide snprintf() for MSVC prior to VS2015 - * This file is part of MMDevice - * - * Author: Mark Tsuchida - * - * (C)2020 Board of Regents of the University of Wisconsin System - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holder nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -// VC++ added a proper snprintf() in VS2015. Prior to that, both _snprintf() -// and _snprintf_s() were incompatible with the C standard snprintf(). -// This file serves as a single location where this issue is dealt with. -// -// This file can be included before or after stdio.h, but should be included -// outside of any conditional compilation block. - -#if defined(_MSC_VER) && _MSC_VER < 1900 // VS2015 - -// Unlike snprintf(), _snprintf() does not null-terminate the result if it is -// longer than the buffer. However, Micro-Manager code has customarily defined -// snprintf to be _snprintf. Since we will be moving to a newer compiler in the -// near future (at which point this file can be removed), I'm just keeping that -// method rather than trying to emulate correct behavior (it's trickier than it -// initially appears). - -#define snprintf _snprintf - -#endif diff --git a/MMDevice/MMDevice-SharedRuntime.vcxproj b/MMDevice/MMDevice-SharedRuntime.vcxproj index 68a792d41..d1e1f8636 100644 --- a/MMDevice/MMDevice-SharedRuntime.vcxproj +++ b/MMDevice/MMDevice-SharedRuntime.vcxproj @@ -23,7 +23,6 @@ - diff --git a/MMDevice/MMDevice-SharedRuntime.vcxproj.filters b/MMDevice/MMDevice-SharedRuntime.vcxproj.filters index 069df08d2..2f38adf00 100644 --- a/MMDevice/MMDevice-SharedRuntime.vcxproj.filters +++ b/MMDevice/MMDevice-SharedRuntime.vcxproj.filters @@ -43,9 +43,6 @@ Header Files - - Header Files - Header Files diff --git a/MMDevice/MMDevice-StaticRuntime.vcxproj b/MMDevice/MMDevice-StaticRuntime.vcxproj index 99d845cac..be67f1eed 100644 --- a/MMDevice/MMDevice-StaticRuntime.vcxproj +++ b/MMDevice/MMDevice-StaticRuntime.vcxproj @@ -23,7 +23,6 @@ - diff --git a/MMDevice/MMDevice-StaticRuntime.vcxproj.filters b/MMDevice/MMDevice-StaticRuntime.vcxproj.filters index 069df08d2..2f38adf00 100644 --- a/MMDevice/MMDevice-StaticRuntime.vcxproj.filters +++ b/MMDevice/MMDevice-StaticRuntime.vcxproj.filters @@ -43,9 +43,6 @@ Header Files - - Header Files - Header Files diff --git a/MMDevice/MMDevice.h b/MMDevice/MMDevice.h index cd070d6ff..6b53c047b 100644 --- a/MMDevice/MMDevice.h +++ b/MMDevice/MMDevice.h @@ -70,7 +70,6 @@ # define MM_DEPRECATED(prototype) prototype #endif - #ifdef WIN32 #define WIN32_LEAN_AND_MEAN #include @@ -80,13 +79,8 @@ typedef void* HDEVMODULE; #endif -#include "FixSnprintf.h" - - class ImgBuffer; - - namespace MM { // forward declaration for the MMCore callback class diff --git a/MMDevice/Makefile.am b/MMDevice/Makefile.am index 951766123..33444b2e0 100644 --- a/MMDevice/Makefile.am +++ b/MMDevice/Makefile.am @@ -5,7 +5,6 @@ noinst_HEADERS = \ DeviceBase.h \ DeviceThreads.h \ DeviceUtils.h \ - FixSnprintf.h \ ImageMetadata.h \ ImgBuffer.h \ MMDevice.h \ diff --git a/MMDevice/Property.cpp b/MMDevice/Property.cpp index 8d1b55d66..1e7a3b5a1 100644 --- a/MMDevice/Property.cpp +++ b/MMDevice/Property.cpp @@ -26,8 +26,6 @@ using namespace std; -#include "FixSnprintf.h" - const int BUFSIZE = 60; // For number-to-string conversion diff --git a/camera_triggering_API_v2.md b/camera_triggering_API_v2.md index d0b2dde42..9baecbfa1 100644 --- a/camera_triggering_API_v2.md +++ b/camera_triggering_API_v2.md @@ -25,7 +25,7 @@ Based on the [GenICam specification](https://www.emva.org/wp-content/uploads/Gen //////////////////////////////////////////// -bool isNewAPIImplemented(); +bool isTriggerAPIImplemented(); ////////////////////////////// diff --git a/m4/mm_lib_ifelse.m4 b/m4/mm_lib_ifelse.m4 index 171f2db00..74b8ec6a3 100644 --- a/m4/mm_lib_ifelse.m4 +++ b/m4/mm_lib_ifelse.m4 @@ -64,7 +64,7 @@ AC_DEFUN([MM_LIB_IFELSE], [ ]) ], [ - mm_lib_ifelse_have_$1=no + mm_lib_ifelse_have_$1=yes ]) ]) diff --git a/m4/mm_libs.m4 b/m4/mm_libs.m4 index 4f6296471..603a2aa14 100644 --- a/m4/mm_libs.m4 +++ b/m4/mm_libs.m4 @@ -105,3 +105,14 @@ AC_DEFUN([MM_LIB_USB_0_1], [ [$1], [-lusb], [usb.h], [usb_init], [$2], [$3]) ]) + + +# Check for Allied Vision Vimba X SDK +# +# MM_LIB_VIMBA_X([Vimba X api prefix], [action-if-found], [action-if-not-found]) +# +# Defines variable VIMBA_X_CPPFLAGS. +# +AC_DEFUN([MM_LIB_VIMBA_X], [ + MM_LIB_SIMPLE([VIMBA_X], [Vimba X], [$1], [], [VmbC/VmbC.h], [], [$2], [$3]) +]) diff --git a/micromanager.sln b/micromanager.sln index e8393aa93..a5776d644 100644 --- a/micromanager.sln +++ b/micromanager.sln @@ -468,8 +468,16 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AtikCamera", "DeviceAdapter EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MPBLaser", "DeviceAdapters\MPBLaser\MPBLaser.vcxproj", "{5E193242-B386-49EF-B592-9ED3552D273D}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AlliedVisionCamera", "DeviceAdapters\AlliedVisionCamera\AlliedVisionCamera.vcxproj", "{12E75CB0-4B48-4D7C-BB26-D928F18488C2}" +EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BH_DCC_DCU", "DeviceAdapters\BH_DCC_DCU\BH_DCC_DCU.vcxproj", "{9AE989AF-F6B7-45D7-983B-A7629FBF8F1B}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ArduinoCounter", "DeviceAdapters\ArduinoCounter\ArduinoCounter.vcxproj", "{0067D4A8-0000-0000-0000-000000000000}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ESP32", "DeviceAdapters\ESP32\ESP32.vcxproj", "{0C72846A-BCFA-4F75-91AF-D9F5CA01E6B2}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WOSM", "DeviceAdapters\WOSM\WOSM.vcxproj", "{64E94A9E-B3AE-47EF-8F5A-0356B0E6B9DA}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -1408,10 +1416,26 @@ Global {5E193242-B386-49EF-B592-9ED3552D273D}.Debug|x64.Build.0 = Debug|x64 {5E193242-B386-49EF-B592-9ED3552D273D}.Release|x64.ActiveCfg = Release|x64 {5E193242-B386-49EF-B592-9ED3552D273D}.Release|x64.Build.0 = Release|x64 + {12E75CB0-4B48-4D7C-BB26-D928F18488C2}.Debug|x64.ActiveCfg = Debug|x64 + {12E75CB0-4B48-4D7C-BB26-D928F18488C2}.Debug|x64.Build.0 = Debug|x64 + {12E75CB0-4B48-4D7C-BB26-D928F18488C2}.Release|x64.ActiveCfg = Release|x64 + {12E75CB0-4B48-4D7C-BB26-D928F18488C2}.Release|x64.Build.0 = Release|x64 {9AE989AF-F6B7-45D7-983B-A7629FBF8F1B}.Debug|x64.ActiveCfg = Debug|x64 {9AE989AF-F6B7-45D7-983B-A7629FBF8F1B}.Debug|x64.Build.0 = Debug|x64 {9AE989AF-F6B7-45D7-983B-A7629FBF8F1B}.Release|x64.ActiveCfg = Release|x64 {9AE989AF-F6B7-45D7-983B-A7629FBF8F1B}.Release|x64.Build.0 = Release|x64 + {0067D4A8-0000-0000-0000-000000000000}.Debug|x64.ActiveCfg = Debug|x64 + {0067D4A8-0000-0000-0000-000000000000}.Debug|x64.Build.0 = Debug|x64 + {0067D4A8-0000-0000-0000-000000000000}.Release|x64.ActiveCfg = Release|x64 + {0067D4A8-0000-0000-0000-000000000000}.Release|x64.Build.0 = Release|x64 + {0C72846A-BCFA-4F75-91AF-D9F5CA01E6B2}.Debug|x64.ActiveCfg = Debug|x64 + {0C72846A-BCFA-4F75-91AF-D9F5CA01E6B2}.Debug|x64.Build.0 = Debug|x64 + {0C72846A-BCFA-4F75-91AF-D9F5CA01E6B2}.Release|x64.ActiveCfg = Release|x64 + {0C72846A-BCFA-4F75-91AF-D9F5CA01E6B2}.Release|x64.Build.0 = Release|x64 + {64E94A9E-B3AE-47EF-8F5A-0356B0E6B9DA}.Debug|x64.ActiveCfg = Debug|x64 + {64E94A9E-B3AE-47EF-8F5A-0356B0E6B9DA}.Debug|x64.Build.0 = Debug|x64 + {64E94A9E-B3AE-47EF-8F5A-0356B0E6B9DA}.Release|x64.ActiveCfg = Release|x64 + {64E94A9E-B3AE-47EF-8F5A-0356B0E6B9DA}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/secret-device-adapters-commit b/secret-device-adapters-commit index a6ec0e86f..b48fc70eb 100644 --- a/secret-device-adapters-commit +++ b/secret-device-adapters-commit @@ -1 +1 @@ -bd9ad88799e2d744a1801ef6731a38afde0ad87e +f5e40313f919bd58bd53e587d1790b6508933a79