From 996c86fdeec60364441e991215c20de5135724ba Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Mon, 20 Jul 2020 15:56:38 -0500 Subject: [PATCH 1/2] Add original version of Teledyne Dalsa GigE driver Author: Robert Frazee --- .../TeledyneDalsaGigE/GigENano.vcxproj | 165 ++++ .../GigENano.vcxproj.filters | 30 + .../TeledyneDalsaGigE/TestCamera.cpp | 859 ++++++++++++++++++ DeviceAdapters/TeledyneDalsaGigE/TestCamera.h | 154 ++++ 4 files changed, 1208 insertions(+) create mode 100644 DeviceAdapters/TeledyneDalsaGigE/GigENano.vcxproj create mode 100644 DeviceAdapters/TeledyneDalsaGigE/GigENano.vcxproj.filters create mode 100644 DeviceAdapters/TeledyneDalsaGigE/TestCamera.cpp create mode 100644 DeviceAdapters/TeledyneDalsaGigE/TestCamera.h diff --git a/DeviceAdapters/TeledyneDalsaGigE/GigENano.vcxproj b/DeviceAdapters/TeledyneDalsaGigE/GigENano.vcxproj new file mode 100644 index 0000000000..b9293c17ad --- /dev/null +++ b/DeviceAdapters/TeledyneDalsaGigE/GigENano.vcxproj @@ -0,0 +1,165 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {DE209272-1DA7-4551-9D34-3ECE99DDE827} + Win32Proj + GigENano + + + + DynamicLibrary + true + Unicode + + + DynamicLibrary + true + Unicode + Windows7.1SDK + + + DynamicLibrary + false + true + Unicode + + + DynamicLibrary + false + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;GIGENANO_EXPORTS;%(PreprocessorDefinitions) + $(SAPERADIR)\Include;$(SAPERADIR)\Classes\Basic;%(AdditionalIncludeDirectories) + + + Windows + true + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;GIGENANO_EXPORTS;%(PreprocessorDefinitions) + $(SAPERADIR)\Include;$(SAPERADIR)\Classes\Basic;%(AdditionalIncludeDirectories) + + + Windows + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;GIGENANO_EXPORTS;%(PreprocessorDefinitions) + + + Windows + true + true + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;GIGENANO_EXPORTS;%(PreprocessorDefinitions) + + + Windows + true + true + true + + + + + + + + + + + + + + {b8c95f39-54bf-40a9-807b-598df2821d55} + + + + + + \ No newline at end of file diff --git a/DeviceAdapters/TeledyneDalsaGigE/GigENano.vcxproj.filters b/DeviceAdapters/TeledyneDalsaGigE/GigENano.vcxproj.filters new file mode 100644 index 0000000000..65fa98af31 --- /dev/null +++ b/DeviceAdapters/TeledyneDalsaGigE/GigENano.vcxproj.filters @@ -0,0 +1,30 @@ + + + + + {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;mfcribbon-ms + + + + + Header Files + + + + + Source Files + + + + + + \ No newline at end of file diff --git a/DeviceAdapters/TeledyneDalsaGigE/TestCamera.cpp b/DeviceAdapters/TeledyneDalsaGigE/TestCamera.cpp new file mode 100644 index 0000000000..a8282e4fec --- /dev/null +++ b/DeviceAdapters/TeledyneDalsaGigE/TestCamera.cpp @@ -0,0 +1,859 @@ +///////////////////////////////////////////////////////// +// FILE: TestCamera.cpp +// PROJECT: Teledyne DALSA Micro-Manager Glue Library +//------------------------------------------------------- +// AUTHOR: Robert Frazee, rfraze1@lsu.edu + +#include "TestCamera.h" +#include "../MMDevice/ModuleInterface.h" +#include "stdio.h" +#include "conio.h" +#include "math.h" +#include "sapclassbasic.h" +#include + +using namespace std; + +const char* g_CameraName = "GigE Nano"; +const char* g_PixelType_8bit = "8bit"; +const char* g_PixelType_10bit = "10bit"; +const char* g_PixelType_12bit = "12bit"; + +const char* g_CameraModelProperty = "Model"; +const char* g_CameraModel_A = "Nano-M1930-NIR"; + +// g_CameraAcqDeviceNumberProperty +// g_CameraServerNameProperty +// g_CameraConfigFilenameProperty +const char* g_CameraAcqDeviceNumberProperty = "Acquisition Device Number"; +const char* g_CameraAcqDeviceNumber_Def = "0"; +const char* g_CameraServerNameProperty = "Server Name"; +const char* g_CameraServerName_Def = "Nano-M1930-NIR_1"; +const char* g_CameraConfigFilenameProperty = "Config Filename"; +const char* g_CameraConfigFilename_Def = "NoFile"; + + +/////////////////////////////////////////////////////////////////////////////// +// Exported MMDevice API +/////////////////////////////////////////////////////////////////////////////// + +/** + * List all supported hardware devices here + */ +MODULE_API void InitializeModuleData() +{ + RegisterDevice(g_CameraName, MM::CameraDevice, "GigE Nano Camera Device"); +} + +MODULE_API MM::Device* CreateDevice(const char* deviceName) +{ + if (deviceName == 0) + return 0; + + // decide which device class to create based on the deviceName parameter + if (strcmp(deviceName, g_CameraName) == 0) + { + // create camera + return new TestCamera(); + } + + // ...supplied name not recognized + // to heck with it, return a device anyway + return new TestCamera(); +} + +MODULE_API void DeleteDevice(MM::Device* pDevice) +{ + delete pDevice; +} + +/////////////////////////////////////////////////////////////////////////////// +// TestCamera implementation +// ~~~~~~~~~~~~~~~~~~~~~~~ + +/** +* TestCamera constructor. +* Setup default all variables and create device properties required to exist +* before intialization. In this case, no such properties were required. All +* properties will be created in the Initialize() method. +* +* As a general guideline Micro-Manager devices do not access hardware in the +* the constructor. We should do as little as possible in the constructor and +* perform most of the initialization in the Initialize() method. +*/ +TestCamera::TestCamera() : + binning_ (1), + gain_(1), + bytesPerPixel_(1), + bitsPerPixel_(8), + initialized_(false), + roiX_(0), + roiY_(0), + thd_(0), + sequenceRunning_(false), + SapFormatBytes_(1) +{ + // call the base class method to set-up default error codes/messages + InitializeDefaultErrorMessages(); + + // Description property + int ret = CreateProperty(MM::g_Keyword_Description, "GigE Nano Camera Adapter", MM::String, true); + assert(ret == DEVICE_OK); + + // camera type pre-initialization property + ret = CreateProperty(g_CameraModelProperty, g_CameraModel_A, MM::String, false, 0, true); + assert(ret == DEVICE_OK); + + vector modelValues; + modelValues.push_back(g_CameraModel_A); + modelValues.push_back(g_CameraModel_A); + + ret = SetAllowedValues(g_CameraModelProperty, modelValues); + assert(ret == DEVICE_OK); + + // Sapera++ library stuff + + int serverCount = SapManager::GetServerCount(); + if(serverCount == 0) + { + ErrorBox((LPCWSTR)L"Initialization Error", (LPCWSTR)L"No servers!"); + } + + acqServerName_ = new char[CORSERVER_MAX_STRLEN]; + configFilename_ = new char[MAX_PATH]; + + ret = CreateProperty(g_CameraAcqDeviceNumberProperty, g_CameraAcqDeviceNumber_Def, MM::Integer, false, 0, true); + assert(ret == DEVICE_OK); + + ret = CreateProperty(g_CameraServerNameProperty, g_CameraServerName_Def, MM::String, false, 0, true); + assert(ret == DEVICE_OK); + + ret = CreateProperty(g_CameraConfigFilenameProperty, g_CameraConfigFilename_Def, MM::String, false, 0, true); + assert(ret == DEVICE_OK); + + // create live video thread + thd_ = new SequenceThread(this); +} + +/** +* TestCamera destructor. +* If this device used as intended within the Micro-Manager system, +* Shutdown() will be always called before the destructor. But in any case +* we need to make sure that all resources are properly released even if +* Shutdown() was not called. +*/ +TestCamera::~TestCamera() +{ + if (initialized_) + Shutdown(); +} + +/** +* Obtains device name. +* Required by the MM::Device API. +*/ +void TestCamera::GetName(char* name) const +{ + // We just return the name we use for referring to this + // device adapter. + CDeviceUtils::CopyLimitedString(name, g_CameraName); +} + +/** +* Intializes the hardware. +* Typically we access and initialize hardware at this point. +* Device properties are typically created here as well. +* Required by the MM::Device API. +*/ +int TestCamera::Initialize() +{ + if (initialized_) + return DEVICE_OK; + + //SapManager::DisplayMessage("This plugin logs debug messages. Press no so they don't all pop up like this."); + // set property list + // ----------------- + + // binning + CPropertyAction *pAct = new CPropertyAction (this, &TestCamera::OnBinning); + //@TODO: Check what the actual binning value is and set that + // For now, set binning to 1 for MM and set that on the camera later + int ret = CreateProperty(MM::g_Keyword_Binning, "1", MM::Integer, false, pAct); + assert(ret == DEVICE_OK); + + vector binningValues; + binningValues.push_back("1"); + binningValues.push_back("2"); + binningValues.push_back("4"); + + ret = SetAllowedValues(MM::g_Keyword_Binning, binningValues); + assert(ret == DEVICE_OK); + + //Sapera stuff + // g_CameraAcqDeviceNumberProperty + // g_CameraServerNameProperty + // g_CameraConfigFilenameProperty + long tmpDeviceNumber; + if(GetProperty(g_CameraAcqDeviceNumberProperty, tmpDeviceNumber) != DEVICE_OK) + { + //SapManager::DisplayMessage("Failed to retrieve AcqDeviceNumberProperty"); + return DEVICE_ERR; + } + acqDeviceNumber_ = (UINT32)tmpDeviceNumber; + + if(false)//GetProperty(g_CameraServerNameProperty, acqServerName_) != DEVICE_OK) + { + //SapManager::DisplayMessage("Failed to retrieve ServerNameProperty"); + return DEVICE_ERR; + } + acqServerName_ = (char *)g_CameraServerName_Def; + + if(false)//GetProperty(g_CameraConfigFilenameProperty, configFilename_) != DEVICE_OK) + { + //SapManager::DisplayMessage("Failed to retrieve ConfigFilenameProperty"); + return DEVICE_ERR; + } + configFilename_ = "NoFile"; + + //SapManager::DisplayMessage("(Sapera app)Creating loc_ object"); + SapLocation loc_(acqServerName_, acqDeviceNumber_); + //SapManager::DisplayMessage("(Sapera app)Created loc_ object"); + //SapManager::DisplayMessage("(Sapera app)GetResourceCount for ResourceAcqDevice starting"); + if(SapManager::GetResourceCount(acqServerName_, SapManager::ResourceAcqDevice) > 0) + { + //SapManager::DisplayMessage("(Sapera app)GetResourceCount for ResourceAcqDevice found something"); + if(strcmp(configFilename_, "NoFile") == 0) + AcqDevice_ = SapAcqDevice(loc_, false); + else + AcqDevice_ = SapAcqDevice(loc_, configFilename_); + + Buffers_ = SapBufferWithTrash(2, &AcqDevice_); + AcqDeviceToBuf_ = SapAcqDeviceToBuf(&AcqDevice_, &Buffers_); + Xfer_ = &AcqDeviceToBuf_; + + if(!AcqDevice_.Create()) + { + ret = FreeHandles(); + if (ret != DEVICE_OK) + { + //SapManager::DisplayMessage("Failed to FreeHandles during Acq_.Create() for ResourceAcqDevice"); + return ret; + } + //SapManager::DisplayMessage("Failed to create Acq_ for ResourceAcqDevice"); + return DEVICE_INVALID_INPUT_PARAM; + } + } + //SapManager::DisplayMessage("(Sapera app)GetResourceCount for ResourceAcqDevice done"); + //SapManager::DisplayMessage("(Sapera app)Creating Buffers_"); + if(!Buffers_.Create()) + { + ret = FreeHandles(); + if (ret != DEVICE_OK) + return ret; + return DEVICE_NATIVE_MODULE_FAILED; + } + //SapManager::DisplayMessage("(Sapera app)Creating Xfer_"); + if(Xfer_ && !Xfer_->Create()) + { + //SapManager::DisplayMessage("Xfer_ creation failed"); + ret = FreeHandles(); + if (ret != DEVICE_OK) + return ret; + return DEVICE_NATIVE_MODULE_FAILED; + } + //SapManager::DisplayMessage("(Sapera app)Starting Xfer"); + //Start continuous grab + //Xfer_->Grab(); + //SapManager::DisplayMessage("(Sapera app)Sapera Initialization for TestCamera complete"); + + if(!AcqDevice_.GetFeatureValue("ExposureTime", &exposureMs_)) + return DEVICE_ERR; + exposureMs_ = exposureMs_ / 1000; + + // synchronize bit depth with camera + + char acqFormat[10]; + AcqDevice_.GetFeatureValue("PixelFormat", acqFormat, 10); + if(strcmp(acqFormat, "Mono8") == 0) + { + // Setup Micro-Manager for 8bit pixels + SapFormatBytes_ = 1; + bitsPerPixel_ = 8; + bytesPerPixel_ = 1; + //resize the SapBuffer + int ret = SapBufferReformat(SapFormatMono8, "Mono8"); + if(ret != DEVICE_OK) + { + return ret; + } + ResizeImageBuffer(); + pAct = new CPropertyAction (this, &TestCamera::OnPixelType); + ret = CreateProperty(MM::g_Keyword_PixelType, g_PixelType_8bit, MM::String, false, pAct); + assert(ret == DEVICE_OK); + } + if(strcmp(acqFormat, "Mono10") == 0) + { + // Setup Micro-Manager for 8bit pixels + SapFormatBytes_ = 2; + bitsPerPixel_ = 10; + bytesPerPixel_ = 2; + //resize the SapBuffer + int ret = SapBufferReformat(SapFormatMono10, "Mono10"); + if(ret != DEVICE_OK) + { + return ret; + } + ResizeImageBuffer(); + pAct = new CPropertyAction (this, &TestCamera::OnPixelType); + ret = CreateProperty(MM::g_Keyword_PixelType, g_PixelType_10bit, MM::String, false, pAct); + assert(ret == DEVICE_OK); + } + + + // pixel type + + + vector pixelTypeValues; + pixelTypeValues.push_back(g_PixelType_8bit); + pixelTypeValues.push_back(g_PixelType_10bit); + + + ret = SetAllowedValues(MM::g_Keyword_PixelType, pixelTypeValues); + assert(ret == DEVICE_OK); + + // Set Binning to 1 + if(!AcqDevice_.SetFeatureValue("BinningVertical", 1)) + return DEVICE_ERR; + if(!AcqDevice_.SetFeatureValue("BinningHorizontal", 1)) + return DEVICE_ERR; + + + // Setup gain + pAct = new CPropertyAction(this, &TestCamera::OnGain); + ret = CreateProperty(MM::g_Keyword_Gain, "1.0", MM::Float, false, pAct); + assert(ret == DEVICE_OK); + if(!AcqDevice_.SetFeatureValue("Gain", 1.0)) + return DEVICE_ERR; + SapFeature SapGain_(loc_); + if(!SapGain_.Create()) + return DEVICE_ERR; + AcqDevice_.GetFeatureInfo("Gain", &SapGain_); + double g_low = 0.0; + double g_high = 0.0; + SapGain_.GetMax(&g_high); + SapGain_.GetMin(&g_low); + SetPropertyLimits(MM::g_Keyword_Gain, g_low, g_high); + + + + // synchronize all properties + // -------------------------- + ret = UpdateStatus(); + if (ret != DEVICE_OK) + return ret; + + // setup the buffer + // ---------------- + ret = ResizeImageBuffer(); + if (ret != DEVICE_OK) + return ret; + + initialized_ = true; + return DEVICE_OK; +} + +/** +* Shuts down (unloads) the device. +* Ideally this method will completely unload the device and release all resources. +* Shutdown() may be called multiple times in a row. +* Required by the MM::Device API. +*/ +int TestCamera::Shutdown() +{ + if(!initialized_) + return DEVICE_OK; + initialized_ = false; + Xfer_->Freeze(); + if(!Xfer_->Wait(5000)) + return DEVICE_NATIVE_MODULE_FAILED; + int ret; + ret = FreeHandles(); + if(ret != DEVICE_OK) + return ret; + return DEVICE_OK; +} + +/** +* Frees Sapera buffers and such +*/ +int TestCamera::FreeHandles() +{ + if(Xfer_ && *Xfer_ && !Xfer_->Destroy()) return DEVICE_ERR; + if(!Buffers_.Destroy()) return DEVICE_ERR; + if(!Acq_.Destroy()) return DEVICE_ERR; + if(!AcqDevice_.Destroy()) return DEVICE_ERR; + return DEVICE_OK; +} + +int TestCamera::ErrorBox(LPCWSTR text, LPCWSTR caption) +{ + return MessageBox(NULL, caption, text, (MB_ICONERROR | MB_OK)); +} + +/** +* Performs exposure and grabs a single image. +* This function blocks during the actual exposure and returns immediately afterwards +* Required by the MM::Camera API. +*/ +int TestCamera::SnapImage() +{ + // This will always be false, as no sequences will ever run + if(sequenceRunning_) + return DEVICE_CAMERA_BUSY_ACQUIRING; + // Start image capture + if(!Xfer_->Snap(1)) + { + return DEVICE_ERR; + } + // Wait for either the capture to finish or 2.5 seconds, whichever is first + if(!Xfer_->Wait(2500)) + { + return DEVICE_ERR; + } + return DEVICE_OK; +} + +/** +* Returns pixel data. +* Required by the MM::Camera API. +* The calling program will assume the size of the buffer based on the values +* obtained from GetImageBufferSize(), which in turn should be consistent with +* values returned by GetImageWidth(), GetImageHight() and GetImageBytesPerPixel(). +* The calling program allso assumes that camera never changes the size of +* the pixel buffer on its own. In other words, the buffer can change only if +* appropriate properties are set (such as binning, pixel type, etc.) +*/ +const unsigned char* TestCamera::GetImageBuffer() +{ + // Put Sapera buffer into Micro-Manager Buffer + Buffers_.ReadRect(roiX_, roiY_, img_.Width(), img_.Height(), const_cast(img_.GetPixels())); + // Return location of the Micro-Manager Buffer + return const_cast(img_.GetPixels()); +} + +/** +* Returns image buffer X-size in pixels. +* Required by the MM::Camera API. +*/ +unsigned TestCamera::GetImageWidth() const +{ + return img_.Width(); +} + +/** +* Returns image buffer Y-size in pixels. +* Required by the MM::Camera API. +*/ +unsigned TestCamera::GetImageHeight() const +{ + return img_.Height(); +} + +/** +* Returns image buffer pixel depth in bytes. +* Required by the MM::Camera API. +*/ +unsigned TestCamera::GetImageBytesPerPixel() const +{ + return img_.Depth(); +} + +/** +* Returns the bit depth (dynamic range) of the pixel. +* This does not affect the buffer size, it just gives the client application +* a guideline on how to interpret pixel values. +* Required by the MM::Camera API. +*/ +unsigned TestCamera::GetBitDepth() const +{ + return bitsPerPixel_; +} + +/** +* Returns the size in bytes of the image buffer. +* Required by the MM::Camera API. +*/ +long TestCamera::GetImageBufferSize() const +{ + return img_.Width() * img_.Height() * GetImageBytesPerPixel(); +} + +/** +* Sets the camera Region Of Interest. +* Required by the MM::Camera API. +* This command will change the dimensions of the image. +* Depending on the hardware capabilities the camera may not be able to configure the +* exact dimensions requested - but should try do as close as possible. +* If the hardware does not have this capability the software should simulate the ROI by +* appropriately cropping each frame. +* This demo implementation ignores the position coordinates and just crops the buffer. +* @param x - top-left corner coordinate +* @param y - top-left corner coordinate +* @param xSize - width +* @param ySize - height +*/ +int TestCamera::SetROI(unsigned x, unsigned y, unsigned xSize, unsigned ySize) +{ + if (xSize == 0 && ySize == 0) + { + // effectively clear ROI + ResizeImageBuffer(); + roiX_ = 0; + roiY_ = 0; + } + else + { + // apply ROI + img_.Resize(xSize, ySize); + roiX_ = x; + roiY_ = y; + } + return DEVICE_OK; +} + +/** +* Returns the actual dimensions of the current ROI. +* Required by the MM::Camera API. +*/ +int TestCamera::GetROI(unsigned& x, unsigned& y, unsigned& xSize, unsigned& ySize) +{ + x = roiX_; + y = roiY_; + + xSize = img_.Width(); + ySize = img_.Height(); + + return DEVICE_OK; +} + +/** +* Resets the Region of Interest to full frame. +* Required by the MM::Camera API. +*/ +int TestCamera::ClearROI() +{ + ResizeImageBuffer(); + roiX_ = 0; + roiY_ = 0; + + return DEVICE_OK; +} + +/** +* Returns the current exposure setting in milliseconds. +* Required by the MM::Camera API. +*/ +double TestCamera::GetExposure() const +{ + return exposureMs_; +} + +/** +* Sets exposure in milliseconds. +* Required by the MM::Camera API. +*/ +void TestCamera::SetExposure(double exp) +{ + exposureMs_ = exp; + // Micromanager deals with exposure time in ms + // Sapera deals with exposure time in us + // As such, we convert between the two + AcqDevice_.SetFeatureValue("ExposureTime", (exposureMs_ * 1000)); +} + +/** +* Returns the current binning factor. +* Required by the MM::Camera API. +*/ +int TestCamera::GetBinning() const +{ + return binning_; +} + +/** +* Sets binning factor. +* Required by the MM::Camera API. +*/ +int TestCamera::SetBinning(int binF) +{ + return SetProperty(MM::g_Keyword_Binning, CDeviceUtils::ConvertToString(binF)); +} + +int TestCamera::PrepareSequenceAcqusition() +{ + return DEVICE_ERR; +} + + +/** + * Required by the MM::Camera API + * Please implement this yourself and do not rely on the base class implementation + * The Base class implementation is deprecated and will be removed shortly + */ +int TestCamera::StartSequenceAcquisition(double interval_ms) +{ + //@TODO: Implement Sequence Acquisition + return DEVICE_ERR; + //int ret = StartSequenceAcquisition((long)(interval_ms/exposureMs_), interval_ms, true); + //return ret; +} + +/** +* Stop and wait for the Sequence thread finished +*/ +int TestCamera::StopSequenceAcquisition() +{ + //@TODO: Implement Sequence Acquisition + return DEVICE_ERR; + /*thd_->Stop(); + thd_->wait(); + sequenceRunning_ = false; + return DEVICE_OK;*/ +} + +/** +* Simple implementation of Sequence Acquisition +* A sequence acquisition should run on its own thread and transport new images +* coming of the camera into the MMCore circular buffer. +*/ +int TestCamera::StartSequenceAcquisition(long numImages, double interval_ms, bool stopOnOverflow) +{ + //@TODO: Implement Sequence Acquisition + return DEVICE_ERR; + /*if (sequenceRunning_) + { + return DEVICE_CAMERA_BUSY_ACQUIRING; + } + int ret = GetCoreCallback()->PrepareForAcq(this); + if (ret != DEVICE_OK) + { + return ret; + } + sequenceRunning_ = true; + thd_->SetLength(10); + thd_->Start(); + return DEVICE_OK; */ +} + +/* + * Inserts Image and MetaData into MMCore circular Buffer + */ +int TestCamera::InsertImage() +{ + //@TODO: Implement Sequence Acquisition + return GetCoreCallback()->InsertImage(this, const_cast(img_.GetPixels()), GetImageWidth(), GetImageHeight(), GetImageBytesPerPixel()); +} + + +bool TestCamera::IsCapturing() { + //@TODO: Implement Sequence Acquisition + return sequenceRunning_; +} + + +/////////////////////////////////////////////////////////////////////////////// +// TestCamera Action handlers +/////////////////////////////////////////////////////////////////////////////// + +/** +* Handles "Binning" property. +*/ +int TestCamera::OnBinning(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::AfterSet) + { + long binSize; + pProp->Get(binSize); + binning_ = (int)binSize; + if(!AcqDevice_.SetFeatureValue("BinningVertical", binning_)) + return DEVICE_ERR; + if(!AcqDevice_.SetFeatureValue("BinningHorizontal", binning_)) + return DEVICE_ERR; + return ResizeImageBuffer(); + } + else if (eAct == MM::BeforeGet) + { + pProp->Set((long)binning_); + } + + return DEVICE_OK; +} + +/** +* Handles "PixelType" property. +*/ +int TestCamera::OnPixelType(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + //bytesPerPixel_ = 1; + //ResizeImageBuffer(); + //return DEVICE_OK; + if (eAct == MM::AfterSet) + { + //SapManager::DisplayMessage("(Sapera app)OnPixelType MM:AfterSet"); + string val; + pProp->Get(val); + if (val.compare(g_PixelType_8bit) == 0) + { + if(SapFormatBytes_ != 1) + { + SapFormatBytes_ = 1; + bitsPerPixel_ = 8; + //resize the SapBuffer + int ret = SapBufferReformat(SapFormatMono8, "Mono8"); + if(ret != DEVICE_OK) + { + return ret; + } + } + bytesPerPixel_ = 1; + } + else if (val.compare(g_PixelType_10bit) == 0) + { + if(SapFormatBytes_ != 2) + { + SapFormatBytes_ = 2; + bitsPerPixel_ = 10; + //resize the SapBuffer + int ret = SapBufferReformat(SapFormatMono16, "Mono10"); + if(ret != DEVICE_OK) + { + return ret; + } + } + bytesPerPixel_ = 2; + } + else + assert(false); + + ResizeImageBuffer(); + } + else if (eAct == MM::BeforeGet) + { + if (bytesPerPixel_ == 1) + pProp->Set(g_PixelType_8bit); + else if (bytesPerPixel_ == 2) + pProp->Set(g_PixelType_10bit); + else + assert(false); // this should never happen + } + + return DEVICE_OK; +} + +/** +* Handles "Gain" property. +*/ +int TestCamera::OnGain(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::AfterSet) + { + //SapManager::DisplayMessage("(Sapera app)OnGain MM:AfterSet"); + pProp->Get(gain_); + AcqDevice_.SetFeatureValue("Gain", gain_); + } + else if (eAct == MM::BeforeGet) + { + pProp->Set(gain_); + } + + return DEVICE_OK; +} + + +/////////////////////////////////////////////////////////////////////////////// +// Private TestCamera methods +/////////////////////////////////////////////////////////////////////////////// + +/** +* Sync internal image buffer size to the chosen property values. +*/ +int TestCamera::ResizeImageBuffer() +{ + img_.Resize(IMAGE_WIDTH/binning_, IMAGE_HEIGHT/binning_, bytesPerPixel_); + + return DEVICE_OK; +} + +/** + * Generate an image with fixed value for all pixels + */ +void TestCamera::GenerateImage() +{ + const int maxValue = (1 << MAX_BIT_DEPTH) - 1; // max for the 12 bit camera + const double maxExp = 1000; + double step = maxValue/maxExp; + unsigned char* pBuf = const_cast(img_.GetPixels()); + memset(pBuf, (int) (step * max(exposureMs_, maxExp)), img_.Height()*img_.Width()*img_.Depth()); +} + +/* + * Reformat Sapera Buffer Object + */ +int TestCamera::SapBufferReformat(SapFormat format, const char * acqFormat) +{ + Xfer_->Destroy(); + AcqDevice_.SetFeatureValue("PixelFormat", acqFormat); + Buffers_.Destroy(); + Buffers_ = SapBufferWithTrash(2, &AcqDevice_); + Buffers_.SetFormat(format); + AcqDeviceToBuf_ = SapAcqDeviceToBuf(&AcqDevice_, &Buffers_); + Xfer_ = &AcqDeviceToBuf_; + if(!Buffers_.Create()) + { + //SapManager::DisplayMessage("Failed to recreate Buffer - SapBufferReformat"); + int ret = FreeHandles(); + if (ret != DEVICE_OK) + return ret; + return DEVICE_NATIVE_MODULE_FAILED; + } + if(Xfer_ && !Xfer_->Create()) + { + //SapManager::DisplayMessage("Xfer_ recreation failed - SapBufferReformat"); + int ret = FreeHandles(); + if (ret != DEVICE_OK) + return ret; + return DEVICE_NATIVE_MODULE_FAILED; + } + return DEVICE_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +// Threading methods +/////////////////////////////////////////////////////////////////////////////// + +int SequenceThread::svc() +{ + //SapManager::DisplayMessage("SequenceThread Start"); + long count(0); + while (!stop_ )//&& count < numImages_) + { + /*int ret = camera_->SnapImage(); + if (ret != DEVICE_OK) + { + //SapManager::DisplayMessage("SequenceThread Snap failed"); + camera_->StopSequenceAcquisition(); + return 1; + }*/ + + int ret = camera_->InsertImage(); + if (ret != DEVICE_OK) + { + //SapManager::DisplayMessage("SequenceThread InsertFailed"); + camera_->StopSequenceAcquisition(); + return 1; + } + //count++; + } + //SapManager::DisplayMessage("SequenceThread End"); + return 0; +} \ No newline at end of file diff --git a/DeviceAdapters/TeledyneDalsaGigE/TestCamera.h b/DeviceAdapters/TeledyneDalsaGigE/TestCamera.h new file mode 100644 index 0000000000..b3808676e6 --- /dev/null +++ b/DeviceAdapters/TeledyneDalsaGigE/TestCamera.h @@ -0,0 +1,154 @@ +/////////////////////////////////////////////////////////////////////////////// +// FILE: TestCamera.h +// PROJECT: Micro-Manager +// SUBSYSTEM: DeviceAdapters +//----------------------------------------------------------------------------- +// DESCRIPTION: Skeleton code for the micro-manager camera adapter. Use it as +// starting point for writing custom device adapters +// +// AUTHOR: Nenad Amodaj, http://nenad.amodaj.com +// +// COPYRIGHT: University of California, San Francisco, 2011 +// +// 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. +// + +#ifndef _TestCamera_H_ +#define _TestCamera_H_ + +#include "DeviceBase.h" +#include "ImgBuffer.h" +#include "DeviceThreads.h" +#include "ImgBuffer.h" +#include "stdio.h" +#include "conio.h" +#include "math.h" +#include "sapclassbasic.h" +#include + +////////////////////////////////////////////////////////////////////////////// +// Error codes +// +#define ERR_UNKNOWN_MODE 102 + +class SequenceThread; + +class TestCamera : public CCameraBase +{ +public: + TestCamera(); + ~TestCamera(); + + // MMDevice API + // ------------ + int Initialize(); + int Shutdown(); + + void GetName(char* name) const; + + // TestCamera API + // ------------ + int SnapImage(); + const unsigned char* GetImageBuffer(); + 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(); + bool IsCapturing(); + int GetBinning() const; + int SetBinning(int binSize); + int IsExposureSequenceable(bool& seq) const {seq = false; return DEVICE_OK;} + + // action interface + // ---------------- + int OnBinning(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnPixelType(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnGain(MM::PropertyBase* pProp, MM::ActionType eAct); + +private: + friend class SequenceThread; + static const int IMAGE_WIDTH = 1920; + static const int IMAGE_HEIGHT = 1200; + static const int MAX_BIT_DEPTH = 12; + + SequenceThread* thd_; + int binning_; + int bytesPerPixel_; + int bitsPerPixel_; + double gain_; + double exposureMs_; + bool initialized_; + ImgBuffer img_; + int roiX_, roiY_; + bool sequenceRunning_; + + int ResizeImageBuffer(); + void GenerateImage(); + int InsertImage(); + + UINT32 acqDeviceNumber_; + char* acqServerName_; + char* configFilename_; + SapAcquisition Acq_; + SapAcqDevice AcqDevice_; + SapBufferWithTrash Buffers_; + SapTransfer AcqToBuf_; + SapTransfer AcqDeviceToBuf_; + SapTransfer* Xfer_; + SapLocation loc_; + SapFeature SapGain_; + int SapFormatBytes_; + + int FreeHandles(); + int ErrorBox(LPCWSTR text, LPCWSTR caption); + LPCWSTR TestCamera::string2winstring(const std::string& s); + int SapBufferReformat(SapFormat format, const char * acqFormat); +}; + + +//threading stuff. Tread lightly +class SequenceThread : public MMDeviceThreadBase +{ + public: + SequenceThread(TestCamera* pCam) : stop_(false), numImages_(0) {camera_ = pCam;} + ~SequenceThread() {} + + int svc (void); + + void Stop() {stop_ = true;} + + void Start() + { + stop_ = false; + activate(); + } + + void SetLength(long images) {numImages_ = images;} + long GetLength(void) {return numImages_;}; + + private: + TestCamera* camera_; + bool stop_; + long numImages_; +}; + +#endif //_TestCamera_H_ From 0d36945e7710fb141f714f621e2d4801f70691ad Mon Sep 17 00:00:00 2001 From: Robert Frazee Date: Thu, 17 Sep 2020 14:09:00 -0500 Subject: [PATCH 2/2] GigENano now builds under MicroManager solution --- .../TeledyneDalsaGigE/GigENano.vcxproj | 28 +++++---- .../GigENano.vcxproj.filters | 2 +- MMDevice/MMDevice-SharedRuntime.vcxproj | 11 ++-- micromanager.sln | 57 ++++++++++++++----- 4 files changed, 66 insertions(+), 32 deletions(-) diff --git a/DeviceAdapters/TeledyneDalsaGigE/GigENano.vcxproj b/DeviceAdapters/TeledyneDalsaGigE/GigENano.vcxproj index b9293c17ad..ad03281691 100644 --- a/DeviceAdapters/TeledyneDalsaGigE/GigENano.vcxproj +++ b/DeviceAdapters/TeledyneDalsaGigE/GigENano.vcxproj @@ -22,53 +22,57 @@ {DE209272-1DA7-4551-9D34-3ECE99DDE827} Win32Proj GigENano + 10.0 DynamicLibrary true Unicode + v142 DynamicLibrary true Unicode - Windows7.1SDK + v142 DynamicLibrary false true Unicode + v142 DynamicLibrary false true Unicode + v142 - - + + - - + + - - + + - - + + @@ -120,6 +124,7 @@ true true WIN32;NDEBUG;_WINDOWS;_USRDLL;GIGENANO_EXPORTS;%(PreprocessorDefinitions) + $(SAPERADIR)\Include;$(SAPERADIR)\Classes\Basic;%(AdditionalIncludeDirectories) Windows @@ -137,6 +142,7 @@ true true WIN32;NDEBUG;_WINDOWS;_USRDLL;GIGENANO_EXPORTS;%(PreprocessorDefinitions) + $(SAPERADIR)\Include;$(SAPERADIR)\Classes\Basic;%(AdditionalIncludeDirectories) Windows @@ -152,10 +158,10 @@ - + - + {b8c95f39-54bf-40a9-807b-598df2821d55} diff --git a/DeviceAdapters/TeledyneDalsaGigE/GigENano.vcxproj.filters b/DeviceAdapters/TeledyneDalsaGigE/GigENano.vcxproj.filters index 65fa98af31..c65818c50e 100644 --- a/DeviceAdapters/TeledyneDalsaGigE/GigENano.vcxproj.filters +++ b/DeviceAdapters/TeledyneDalsaGigE/GigENano.vcxproj.filters @@ -25,6 +25,6 @@ - + \ No newline at end of file diff --git a/MMDevice/MMDevice-SharedRuntime.vcxproj b/MMDevice/MMDevice-SharedRuntime.vcxproj index 4c6560ae50..6a0d69fba2 100644 --- a/MMDevice/MMDevice-SharedRuntime.vcxproj +++ b/MMDevice/MMDevice-SharedRuntime.vcxproj @@ -43,31 +43,32 @@ {B8C95F39-54BF-40A9-807B-598DF2821D55} Win32Proj MMDeviceSharedRuntime + 10.0 StaticLibrary true MultiByte - Windows7.1SDK + v142 StaticLibrary true MultiByte - Windows7.1SDK + v142 StaticLibrary false MultiByte - Windows7.1SDK + v142 StaticLibrary false MultiByte - Windows7.1SDK + v142 @@ -141,4 +142,4 @@ - + \ No newline at end of file diff --git a/micromanager.sln b/micromanager.sln index 33156f5241..314d3ef05c 100644 --- a/micromanager.sln +++ b/micromanager.sln @@ -1,5 +1,7 @@ -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual C++ Express 2010 +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30309.148 +MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MMCore", "MMCore\MMCore.vcxproj", "{36571628-728C-4ACD-A47F-503BA91C5D43}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MMCoreJ_wrap", "MMCoreJ_wrap\MMCoreJ_wrap.vcxproj", "{EFA68887-8A97-49D7-BAB4-768E15A4E597}" @@ -454,7 +456,9 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Skyra", "DeviceAdapters\Sky EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CoboltOfficial", "DeviceAdapters\CoboltOfficial\CoboltOfficial.vcxproj", "{7074BB23-A32B-4CD5-B77E-9EF4E24416BD}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BDPathway", "DeviceAdapters\BDPathway\BDPathway.vcxproj", "{AC358657-0C2B-41B6-97C1-1EFB8481D82A}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BDPathway", "DeviceAdapters\BDPathway\BDPathway.vcxproj", "{E376977B-2B54-4745-9FE1-DDF726AD376F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GigENano", "DeviceAdapters\TeledyneDalsaGigE\GigENano.vcxproj", "{DE209272-1DA7-4551-9D34-3ECE99DDE827}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -2607,18 +2611,26 @@ Global {63DEA291-B595-447A-8C7F-D32AFDA11DFD}.Release|x64.Build.0 = Release|x64 {805A53D5-1E00-42D7-96F0-661AC7D042A6}.Debug|Mixed Platforms.ActiveCfg = Debug|x64 {805A53D5-1E00-42D7-96F0-661AC7D042A6}.Debug|Mixed Platforms.Build.0 = Debug|x64 + {805A53D5-1E00-42D7-96F0-661AC7D042A6}.Debug|Win32.ActiveCfg = Debug|Win32 + {805A53D5-1E00-42D7-96F0-661AC7D042A6}.Debug|Win32.Build.0 = Debug|Win32 {805A53D5-1E00-42D7-96F0-661AC7D042A6}.Debug|x64.ActiveCfg = Debug|x64 {805A53D5-1E00-42D7-96F0-661AC7D042A6}.Debug|x64.Build.0 = Debug|x64 {805A53D5-1E00-42D7-96F0-661AC7D042A6}.Release|Mixed Platforms.ActiveCfg = Release|x64 {805A53D5-1E00-42D7-96F0-661AC7D042A6}.Release|Mixed Platforms.Build.0 = Release|x64 + {805A53D5-1E00-42D7-96F0-661AC7D042A6}.Release|Win32.ActiveCfg = Release|Win32 + {805A53D5-1E00-42D7-96F0-661AC7D042A6}.Release|Win32.Build.0 = Release|Win32 {805A53D5-1E00-42D7-96F0-661AC7D042A6}.Release|x64.ActiveCfg = Release|x64 {805A53D5-1E00-42D7-96F0-661AC7D042A6}.Release|x64.Build.0 = Release|x64 {ED3E2C21-ADA8-446D-8951-26F1E6E9F4DC}.Debug|Mixed Platforms.ActiveCfg = Debug|x64 {ED3E2C21-ADA8-446D-8951-26F1E6E9F4DC}.Debug|Mixed Platforms.Build.0 = Debug|x64 + {ED3E2C21-ADA8-446D-8951-26F1E6E9F4DC}.Debug|Win32.ActiveCfg = Debug|Win32 + {ED3E2C21-ADA8-446D-8951-26F1E6E9F4DC}.Debug|Win32.Build.0 = Debug|Win32 {ED3E2C21-ADA8-446D-8951-26F1E6E9F4DC}.Debug|x64.ActiveCfg = Debug|x64 {ED3E2C21-ADA8-446D-8951-26F1E6E9F4DC}.Debug|x64.Build.0 = Debug|x64 {ED3E2C21-ADA8-446D-8951-26F1E6E9F4DC}.Release|Mixed Platforms.ActiveCfg = Release|x64 {ED3E2C21-ADA8-446D-8951-26F1E6E9F4DC}.Release|Mixed Platforms.Build.0 = Release|x64 + {ED3E2C21-ADA8-446D-8951-26F1E6E9F4DC}.Release|Win32.ActiveCfg = Release|Win32 + {ED3E2C21-ADA8-446D-8951-26F1E6E9F4DC}.Release|Win32.Build.0 = Release|Win32 {ED3E2C21-ADA8-446D-8951-26F1E6E9F4DC}.Release|x64.ActiveCfg = Release|x64 {ED3E2C21-ADA8-446D-8951-26F1E6E9F4DC}.Release|x64.Build.0 = Release|x64 {97768FFD-1616-4CA5-902A-2F128118CC39}.Debug|Mixed Platforms.ActiveCfg = Debug|x64 @@ -3143,20 +3155,35 @@ Global {7074BB23-A32B-4CD5-B77E-9EF4E24416BD}.Release|Win32.Build.0 = Release|Win32 {7074BB23-A32B-4CD5-B77E-9EF4E24416BD}.Release|x64.ActiveCfg = Release|x64 {7074BB23-A32B-4CD5-B77E-9EF4E24416BD}.Release|x64.Build.0 = Release|x64 - {AC358657-0C2B-41B6-97C1-1EFB8481D82A}.Debug|Mixed Platforms.ActiveCfg = Debug|x64 - {AC358657-0C2B-41B6-97C1-1EFB8481D82A}.Debug|Mixed Platforms.Build.0 = Debug|x64 - {AC358657-0C2B-41B6-97C1-1EFB8481D82A}.Debug|Win32.ActiveCfg = Debug|Win32 - {AC358657-0C2B-41B6-97C1-1EFB8481D82A}.Debug|Win32.Build.0 = Debug|Win32 - {AC358657-0C2B-41B6-97C1-1EFB8481D82A}.Debug|x64.ActiveCfg = Debug|x64 - {AC358657-0C2B-41B6-97C1-1EFB8481D82A}.Debug|x64.Build.0 = Debug|x64 - {AC358657-0C2B-41B6-97C1-1EFB8481D82A}.Release|Mixed Platforms.ActiveCfg = Release|x64 - {AC358657-0C2B-41B6-97C1-1EFB8481D82A}.Release|Mixed Platforms.Build.0 = Release|x64 - {AC358657-0C2B-41B6-97C1-1EFB8481D82A}.Release|Win32.ActiveCfg = Release|Win32 - {AC358657-0C2B-41B6-97C1-1EFB8481D82A}.Release|Win32.Build.0 = Release|Win32 - {AC358657-0C2B-41B6-97C1-1EFB8481D82A}.Release|x64.ActiveCfg = Release|x64 - {AC358657-0C2B-41B6-97C1-1EFB8481D82A}.Release|x64.Build.0 = Release|x64 + {E376977B-2B54-4745-9FE1-DDF726AD376F}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 + {E376977B-2B54-4745-9FE1-DDF726AD376F}.Debug|Mixed Platforms.Build.0 = Debug|Win32 + {E376977B-2B54-4745-9FE1-DDF726AD376F}.Debug|Win32.ActiveCfg = Debug|Win32 + {E376977B-2B54-4745-9FE1-DDF726AD376F}.Debug|Win32.Build.0 = Debug|Win32 + {E376977B-2B54-4745-9FE1-DDF726AD376F}.Debug|x64.ActiveCfg = Debug|x64 + {E376977B-2B54-4745-9FE1-DDF726AD376F}.Debug|x64.Build.0 = Debug|x64 + {E376977B-2B54-4745-9FE1-DDF726AD376F}.Release|Mixed Platforms.ActiveCfg = Release|Win32 + {E376977B-2B54-4745-9FE1-DDF726AD376F}.Release|Mixed Platforms.Build.0 = Release|Win32 + {E376977B-2B54-4745-9FE1-DDF726AD376F}.Release|Win32.ActiveCfg = Release|Win32 + {E376977B-2B54-4745-9FE1-DDF726AD376F}.Release|Win32.Build.0 = Release|Win32 + {E376977B-2B54-4745-9FE1-DDF726AD376F}.Release|x64.ActiveCfg = Release|x64 + {E376977B-2B54-4745-9FE1-DDF726AD376F}.Release|x64.Build.0 = Release|x64 + {DE209272-1DA7-4551-9D34-3ECE99DDE827}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 + {DE209272-1DA7-4551-9D34-3ECE99DDE827}.Debug|Mixed Platforms.Build.0 = Debug|Win32 + {DE209272-1DA7-4551-9D34-3ECE99DDE827}.Debug|Win32.ActiveCfg = Debug|Win32 + {DE209272-1DA7-4551-9D34-3ECE99DDE827}.Debug|Win32.Build.0 = Debug|Win32 + {DE209272-1DA7-4551-9D34-3ECE99DDE827}.Debug|x64.ActiveCfg = Debug|x64 + {DE209272-1DA7-4551-9D34-3ECE99DDE827}.Debug|x64.Build.0 = Debug|x64 + {DE209272-1DA7-4551-9D34-3ECE99DDE827}.Release|Mixed Platforms.ActiveCfg = Release|Win32 + {DE209272-1DA7-4551-9D34-3ECE99DDE827}.Release|Mixed Platforms.Build.0 = Release|Win32 + {DE209272-1DA7-4551-9D34-3ECE99DDE827}.Release|Win32.ActiveCfg = Release|Win32 + {DE209272-1DA7-4551-9D34-3ECE99DDE827}.Release|Win32.Build.0 = Release|Win32 + {DE209272-1DA7-4551-9D34-3ECE99DDE827}.Release|x64.ActiveCfg = Release|x64 + {DE209272-1DA7-4551-9D34-3ECE99DDE827}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {375A63CA-A41F-40F1-B15E-C67A6A1A5DA2} + EndGlobalSection EndGlobal