diff --git a/.github/workflows/ci-misc.yml b/.github/workflows/ci-misc.yml new file mode 100644 index 000000000..1a5d7741b --- /dev/null +++ b/.github/workflows/ci-misc.yml @@ -0,0 +1,14 @@ +name: Misc checks + +on: + pull_request: + push: + branches: + - main + +jobs: + check-utf8-encoding: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - run: ./tools/check-utf8.sh diff --git a/.github/workflows/ci-mmdevice-mmcore.yml b/.github/workflows/ci-mmdevice-mmcore.yml index bcc16bb02..807f54d53 100644 --- a/.github/workflows/ci-mmdevice-mmcore.yml +++ b/.github/workflows/ci-mmdevice-mmcore.yml @@ -30,7 +30,7 @@ jobs: runner: windows-2019 cxx: cl - os: macos - runner: macos-11 + runner: macos-12 cxx: clang++ - os: ubuntu runner: ubuntu-22.04 diff --git a/DeviceAdapters/ABS/ABSCamera.cpp b/DeviceAdapters/ABS/ABSCamera.cpp index e95d618ac..f1199c9d9 100644 --- a/DeviceAdapters/ABS/ABSCamera.cpp +++ b/DeviceAdapters/ABS/ABSCamera.cpp @@ -704,7 +704,7 @@ int CABSCamera::Initialize() temperatureIndex_ = 0; pAct = new CPropertyAction (this, &CABSCamera::OnTemperature); - nRet = CreateProperty(MM::g_Keyword_CCDTemperature, "20.0 °C", MM::String, true, pAct); + nRet = CreateProperty(MM::g_Keyword_CCDTemperature, "20.0 °C", MM::String, true, pAct); assert(nRet == DEVICE_OK); } } @@ -1258,7 +1258,7 @@ int CABSCamera::InsertImage() // Important: metadata about the image are generated here: Metadata md; - md.put("Camera", label); + md.put(MM::g_Keyword_Metadata_CameraLabel, label); md.put(MM::g_Keyword_Elapsed_Time_ms, CDeviceUtils::ConvertToString((timeStamp - sequenceStartTime_).getMsec())); md.put(MM::g_Keyword_Metadata_ImageNumber, CDeviceUtils::ConvertToString( imageCounter_ )); md.put(MM::g_Keyword_Metadata_ROI_X, CDeviceUtils::ConvertToString( (long) GetImageWidth())); @@ -2956,7 +2956,7 @@ int CABSCamera::OnTemperature(MM::PropertyBase* pProp, MM::ActionType eAct) if ( IsNoError( rc ) ) { string temperature; - str::sprintf( temperature, "%3.2f °C \0\0", sTR.wSensorValue / temperatureUnit_ ); + str::sprintf( temperature, "%3.2f °C \0\0", sTR.wSensorValue / temperatureUnit_ ); pProp->Set( temperature.c_str() ); } return convertApiErrorCode( rc, __FUNCTION__ ); diff --git a/DeviceAdapters/ABS/abscommontools.cpp b/DeviceAdapters/ABS/abscommontools.cpp index 82be422de..5ec0ecc15 100644 --- a/DeviceAdapters/ABS/abscommontools.cpp +++ b/DeviceAdapters/ABS/abscommontools.cpp @@ -260,7 +260,7 @@ bool ABSTools::GetOsVersion(OSVERSIONINFOEX &sOSVerEx) if ( false == bOsVersionInfoEx ) { - // OSVersionEx - Struct wird nicht unterstützt! + // OSVersionEx - Struct wird nicht unterstützt! // lese die default Struct... sOSVerEx.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); if (!GetVersionEx ( (OSVERSIONINFO *) &sOSVerEx) ) diff --git a/DeviceAdapters/ABS/dllmain.cpp b/DeviceAdapters/ABS/dllmain.cpp index c3a1e12e4..e7f95d70b 100644 --- a/DeviceAdapters/ABS/dllmain.cpp +++ b/DeviceAdapters/ABS/dllmain.cpp @@ -1,4 +1,4 @@ -// dllmain.cpp : Definiert den Einstiegspunkt für die DLL-Anwendung. +// dllmain.cpp : Definiert den Einstiegspunkt für die DLL-Anwendung. #include "MMDevice.h" #include "ModuleInterface.h" diff --git a/DeviceAdapters/ABS/include/camusb_api_ext.h b/DeviceAdapters/ABS/include/camusb_api_ext.h index e5d629f5d..2f5517b55 100644 --- a/DeviceAdapters/ABS/include/camusb_api_ext.h +++ b/DeviceAdapters/ABS/include/camusb_api_ext.h @@ -147,7 +147,7 @@ USBAPI BOOL CCONV CamUSB_GetCameraResolutionInfo ( S_RESOLUTION_INFO* pResInfo, * * Valid from the next image on. * - * \param pdwExposure_us Exposure / Integration time value in µs (us). + * \param pdwExposure_us Exposure / Integration time value in µs (us). * \param nDevNr Camera index number, that identifies the * camera device which should be used with this * function @@ -176,7 +176,7 @@ USBAPI BOOL CCONV CamUSB_GetCameraResolutionInfo ( S_RESOLUTION_INFO* pResInfo, // nExposure contains now actual exposure time } - nExposure = 20000; // 20000µs = 20ms + nExposure = 20000; // 20000µs = 20ms if (CamUSB_SetExposureTime( &nExposure ) != TRUE) { // error see CamUSB_GetLastError @@ -197,7 +197,7 @@ USBAPI BOOL CCONV CamUSB_SetExposureTime( LPDWORD pdwExposure_us, // CamUSB_GetExposureTime //! \brief returns the active Exposure Time. //! -//! \param pdwExposure_us Exposure / Integration time value in µs (us). +//! \param pdwExposure_us Exposure / Integration time value in µs (us). //! \param nDevNr Camera index number, that identifies the //! camera device which should be used with this //! function diff --git a/DeviceAdapters/ABS/include/common_constants_exp.h b/DeviceAdapters/ABS/include/common_constants_exp.h index 2307008bf..51ed700cb 100644 --- a/DeviceAdapters/ABS/include/common_constants_exp.h +++ b/DeviceAdapters/ABS/include/common_constants_exp.h @@ -135,7 +135,7 @@ #define ST_FPA640x512 0x0010 //!< InGaAs-Sensor 640x512(ShortWave-IR VGA) #define ST_FPA320x256 0x0011 //!< InGaAs-Sensor 320x256(ShortWave-IR QVGA) #define ST_FPA320x256_K 0x0012 //!< InGaAs-Sensor 320x256TEC (ShortWave-IR QVGA) -#define ST_FPA320x256_K22 0x0013 //!< InGaAs-Sensor 320x256TEC-2,2µm (ShortWave-IR QVGA) +#define ST_FPA320x256_K22 0x0013 //!< InGaAs-Sensor 320x256TEC-2,2µm (ShortWave-IR QVGA) #define ST_VIIMAGIC_9221M 0x0014 //!< viimagic 9221 (Full-HD, Mono) #define ST_VIIMAGIC_9221C 0x4014 //!< viimagic 9221 (Full-HD, Color) diff --git a/DeviceAdapters/ABS/include/common_structs_exp.h b/DeviceAdapters/ABS/include/common_structs_exp.h index 0bcd1acac..8046cec6e 100644 --- a/DeviceAdapters/ABS/include/common_structs_exp.h +++ b/DeviceAdapters/ABS/include/common_structs_exp.h @@ -476,7 +476,7 @@ typedef struct { //! \brief Camera Exposure Data\n //! Returned in "pData" by #CamUSB_GetFunctionCaps => functionID => #FUNC_EXPOSURE \n -//! The exposure unit is in micro seconds (µs). +//! The exposure unit is in micro seconds (µs). typedef struct { u32 dwCountRanges; //!< count exposure ranges @@ -493,7 +493,7 @@ typedef struct //! => functionID => #FUNC_EXPOSURE typedef struct { - u32 dwExposure_us; //!< exposure time in µs + u32 dwExposure_us; //!< exposure time in µs } STRUCT_PACKED S_EXPOSURE_PARAMS, S_EXPOSURE_RETVALS; @@ -695,7 +695,7 @@ typedef struct u16 wPortType; //!< port type setting (e.g. #PORT_TYPE_OUTPUT) u16 wPortFeatures; //!< port features setting (e.g. #PORT_FEATURE_POL_ACTHIGH) u16 wPortState; //!< port state setting (e.g. #PORT_STATE_SET) - u32 dwDelay; //!< port delay setting (µs) + u32 dwDelay; //!< port delay setting (µs) } STRUCT_PACKED S_IO_PORT_PARAMS, S_IO_PORT_RETVALS; @@ -983,9 +983,9 @@ typedef struct u08 bHysterese; //!< (ignored) 0 => use internal default; use 1..100% u08 bSpeed; //!< (ignored) 0 => use internal default; use 1..100% of max speed u16 wOptions; //!< see #AEXP_OPTION_BRIGHNESSOFFSET - u32 dwMinExposure; //!< min. allowed exposure value for exposure control in µs (0 => default value) + u32 dwMinExposure; //!< min. allowed exposure value for exposure control in µs (0 => default value) - //! \brief max. allowed exposure value for exposure control in µs (0 => default value)\n + //! \brief max. allowed exposure value for exposure control in µs (0 => default value)\n //! if value != 0 the current framerate settings may be affected, if you switch AutoExposure == off //! it is recommended to read out the currently used framerate\n u32 dwMaxExposure; @@ -1081,7 +1081,7 @@ typedef struct i32 dwOffset_y; //!< ROI vertical (y) offset u08 bBitShift; //!< bitshift setting u08 bTempSensorIndex; //!< index of sensor to get (see #S_TEMPERATURE_CAPS::dwSensors) - i16 wTemperature; //!< temperature sensor value in 1/100°C + i16 wTemperature; //!< temperature sensor value in 1/100°C f32 fGain; //!< green or global gain value (during capture) u32 dwReserved[9]; //!< reserved @@ -1147,9 +1147,9 @@ typedef struct //! returned by #CamUSB_GetFunctionCaps => functionID => #FUNC_HUE_SATURATION typedef struct { - i16 wHueMin; //!< minimum value (-180° default) - i16 wHueMax; //!< maximum value (+180° default) - i16 wHueStep; //!< modify at count of dwStep (1° default) + i16 wHueMin; //!< minimum value (-180° default) + i16 wHueMax; //!< maximum value (+180° default) + i16 wHueStep; //!< modify at count of dwStep (1° default) i16 wSatMin; //!< minimum value (-100 default) i16 wSatMax; //!< maximum value (+100 default) i16 wSatStep; //!< modify at count of dwStep (1 default) @@ -1256,7 +1256,7 @@ typedef struct { u32 dwTriggerOptions; //!< bitfield of available trigger options, see #TRIG_OPTION_NONE u32 dwMaxCycleTime_ms; //!< max. trigger cycle time (in ms), used for timed CCD clear - u32 dwMaxMinPulseWidth_us;//!< max. value for minimum trigger pulse width settings (in µs) + u32 dwMaxMinPulseWidth_us;//!< max. value for minimum trigger pulse width settings (in µs) u32 dwReserved[5]; //!< reserved } STRUCT_PACKED S_TRIGGER_OPTION_CAPS; @@ -1269,7 +1269,7 @@ typedef struct { u32 dwTriggerOptions; //!< bitfield of used trigger options, see #TRIG_OPTION_NONE u32 dwCycleTime_ms; //!< trigger cycle time (in ms), used for timed CCD clear - u32 dwMinPulseWidth_us; //!< minimum trigger pulse width settings (in µs) + u32 dwMinPulseWidth_us; //!< minimum trigger pulse width settings (in µs) u32 dwReserved[5]; //!< reserved } STRUCT_PACKED S_TRIGGER_OPTION_PARAMS, S_TRIGGER_OPTION_RETVALS; @@ -1627,7 +1627,7 @@ typedef struct typedef struct { u64 qwFunctionMask; - u32 dwExposure_us; //!< exposure time in µs + u32 dwExposure_us; //!< exposure time in µs u16 wGainChannel; //!< gain channel to set, more than one channel possible (e.g.#GAIN_RED | #GAIN_BLUE) u08 bReserved[2]; //!< reserved u32 dwGain[MAX_GAIN_CHANNELS_ONCE]; //!< gain values @@ -1839,7 +1839,7 @@ typedef struct u08 bCoolingLevelStep; //!< cooling level step size in percent, e.g. 1 corresponds 1% and 100 => 100% (only for #CM_TEC_AUTOMATIC) i16 wTargetTempMin; //!< minimum supported target temperature value for automatic cooling i16 wTargetTempMax; //!< maximum supported target temperature value for automatic cooling - u16 wTargetTempStep; //!< possible temperature step size (e.g. 10 corresponds 1.0°C and 5 => 0.5°C and -124 = -12.4°C) + u16 wTargetTempStep; //!< possible temperature step size (e.g. 10 corresponds 1.0°C and 5 => 0.5°C and -124 = -12.4°C) u16 wMinPeltierVoltage; //!< obsolate don't use (minimum peltier voltage e.g. 600 mean 0.6 V) u16 wMaxPeltierVoltage; //!< obsolate don't use (maximum peltier voltage 9400 mean 9.4 V) @@ -1847,9 +1847,9 @@ typedef struct u08 bFanLevelMin; //!< minimum supported Fan level in percent (only for #CM_FAN_MANUAL) u08 bFanLevelMax; //!< maximum supported Fan level in percent (only for #CM_FAN_MANUAL) u08 bFanLevelStep; //!< Fan level step size in percent, e.g. 1 means 1% and 100 => 100% (only for #CM_FAN_MANUAL) - i16 wFanThreshTempMin; //!< minimum supported threshold temperature in 0.1°C, e.g. 10 => 1.0°C (only for #CM_FAN_AUTOMATIC) - i16 wFanThreshTempMax; //!< maximum supported threshold temperature in 0.1°C (only for #CM_FAN_AUTOMATIC) - i16 wFanThreshTempStep; //!< minimum supported threshold temperature in 0.1°C(only for #CM_FAN_AUTOMATIC) + i16 wFanThreshTempMin; //!< minimum supported threshold temperature in 0.1°C, e.g. 10 => 1.0°C (only for #CM_FAN_AUTOMATIC) + i16 wFanThreshTempMax; //!< maximum supported threshold temperature in 0.1°C (only for #CM_FAN_AUTOMATIC) + i16 wFanThreshTempStep; //!< minimum supported threshold temperature in 0.1°C(only for #CM_FAN_AUTOMATIC) u08 bReserved[40]; //!< reserved } STRUCT_PACKED S_COOLING_CAPS; @@ -1861,14 +1861,14 @@ typedef struct { u08 bCoolingMode; //!< #CM_TEC_OFF, #CM_TEC_AUTOMATIC or #CM_TEC_MANUAL u08 bReserved1[3]; //!< reserved - i16 wTargetTemp; //!< in 1/10 °C (10 => 1.0°C) only used with cooling mode #CM_TEC_AUTOMATIC + i16 wTargetTemp; //!< in 1/10 °C (10 => 1.0°C) only used with cooling mode #CM_TEC_AUTOMATIC u08 bReserved2[2]; //!< reserved u08 bCoolingLevel; //!< in percent (%) (15 => 15 %) only used with cooling mode #CM_TEC_MANUAL u08 bReserved3[3]; //!< reserved u08 bFanMode; //!< current Fan mode (#CM_FAN_OFF, #CM_FAN_AUTOMATIC or #CM_FAN_MANUAL), must be #CM_FAN_AUTOMATIC if #CM_TEC_AUTOMATIC is active) u08 bReserved4[3]; //!< reserved - i16 wFanThreshTemp; //!< current threshold temperature in 0.1°C (only used with fan mode #CM_FAN_AUTOMATIC) + i16 wFanThreshTemp; //!< current threshold temperature in 0.1°C (only used with fan mode #CM_FAN_AUTOMATIC) u08 bReserved5[2]; //!< reserved u08 bFanLevel; //!< current Fan level in percent (only used with fan mode #CM_FAN_MANUAL) u08 bReserved[43]; //!< reserved diff --git a/DeviceAdapters/ASIStage/ASICRISP.cpp b/DeviceAdapters/ASIStage/ASICRISP.cpp index 8b2333ab8..4e2e0eb0b 100644 --- a/DeviceAdapters/ASIStage/ASICRISP.cpp +++ b/DeviceAdapters/ASIStage/ASICRISP.cpp @@ -651,7 +651,7 @@ int CRISP::OnNA(MM::PropertyBase* pProp, MM::ActionType eAct) } else if (eAct == MM::AfterSet) { - long na; + double na; pProp->Get(na); std::ostringstream command; command << std::fixed << "LR Y=" << na; diff --git a/DeviceAdapters/ASITiger/ASIPLogic.cpp b/DeviceAdapters/ASITiger/ASIPLogic.cpp index e77912264..d8d761a49 100644 --- a/DeviceAdapters/ASITiger/ASIPLogic.cpp +++ b/DeviceAdapters/ASITiger/ASIPLogic.cpp @@ -36,23 +36,19 @@ #include #include -#define PLOGIC_NUM_ADDRESSES 128 -#define PLOGIC_INVERT_ADDRESS 64 -#define PLOGIC_FRONTPANEL_START_ADDRESS 33 -#define PLOGIC_FRONTPANEL_END_ADDRESS 40 -#define PLOGIC_FRONTPANEL_NUM (PLOGIC_FRONTPANEL_END_ADDRESS - PLOGIC_FRONTPANEL_START_ADDRESS + 1) -#define PLOGIC_BACKPLANE_START_ADDRESS 41 -#define PLOGIC_BACKPLANE_END_ADDRESS 48 -#define PLOGIC_BACKPLANE_NUM (PLOGIC_BACKPLANE_END_ADDRESS - PLOGIC_BACKPLANE_START_ADDRESS + 1) -#define PLOGIC_PHYSICAL_IO_START_ADDRESS PLOGIC_FRONTPANEL_START_ADDRESS -#define PLOGIC_PHYSICAL_IO_END_ADDRESS PLOGIC_BACKPLANE_END_ADDRESS -#define PLOGIC_PHYSICAL_IO_NUM (PLOGIC_PHYSICAL_IO_END_ADDRESS - PLOGIC_PHYSICAL_IO_START_ADDRESS + 1) - -using namespace std; - -/////////////////////////////////////////////////////////////////////////////// -// CPLogic -// +constexpr unsigned int PLOGIC_NUM_ADDRESSES = 128; +constexpr unsigned int PLOGIC_INVERT_ADDRESS = 64; +constexpr unsigned int PLOGIC_FRONTPANEL_START_ADDRESS = 33; +constexpr unsigned int PLOGIC_FRONTPANEL_END_ADDRESS = 40; +constexpr unsigned int PLOGIC_FRONTPANEL_NUM = PLOGIC_FRONTPANEL_END_ADDRESS - PLOGIC_FRONTPANEL_START_ADDRESS + 1; +constexpr unsigned int PLOGIC_BACKPLANE_START_ADDRESS = 41; +constexpr unsigned int PLOGIC_BACKPLANE_END_ADDRESS = 48; +constexpr unsigned int PLOGIC_BACKPLANE_NUM = PLOGIC_BACKPLANE_END_ADDRESS - PLOGIC_BACKPLANE_START_ADDRESS + 1; +constexpr unsigned int PLOGIC_PHYSICAL_IO_START_ADDRESS = PLOGIC_FRONTPANEL_START_ADDRESS; +constexpr unsigned int PLOGIC_PHYSICAL_IO_END_ADDRESS = PLOGIC_BACKPLANE_END_ADDRESS; +constexpr unsigned int PLOGIC_PHYSICAL_IO_NUM = PLOGIC_PHYSICAL_IO_END_ADDRESS - PLOGIC_PHYSICAL_IO_START_ADDRESS + 1; + +// ASI Tiger Programmable Logic Card CPLogic::CPLogic(const char* name) : ASIPeripheralBase< ::CShutterBase, CPLogic >(name), axisLetter_(g_EmptyAxisLetterStr), // value determined by extended name @@ -74,14 +70,16 @@ CPLogic::CPLogic(const char* name) : CPropertyAction* pAct; // pre-init property to say how PLogic card is used - // original option is have shutter functionality for diSPIM (laser controls on BNCs 5-8) - // second option to have similar function with laser controls on BNCs 5-8 but not use diSPIM beam enable + // diSPIM shutter has shutter functionality for diSPIM (laser controls on BNCs 5-8) + // 7-channel shutter has laser controls on BNCs 1-7 + // 7-channel TTL shutter has laser controls on BNCs 1-7 and single camera control on BNC 8 pAct = new CPropertyAction (this, &CPLogic::OnPLogicMode); CreateProperty(g_PLogicModePropertyName, g_PLogicModeNone, MM::String, false, pAct, true); AddAllowedValue(g_PLogicModePropertyName, g_PLogicModeNone); AddAllowedValue(g_PLogicModePropertyName, g_PLogicModediSPIMShutter); AddAllowedValue(g_PLogicModePropertyName, g_PLogicMode4ChShutter); AddAllowedValue(g_PLogicModePropertyName, g_PLogicMode7ChShutter); + AddAllowedValue(g_PLogicModePropertyName, g_PLogicMode7ChTTLShutter); } int CPLogic::Initialize() @@ -90,7 +88,7 @@ int CPLogic::Initialize() RETURN_ON_MM_ERROR( PeripheralInitialize() ); // create MM description; this doesn't work during hardware configuration wizard but will work afterwards - ostringstream command; + std::ostringstream command; command.str(""); command << g_PLogicDeviceDescription << " HexAddr=" << addressString_; CreateProperty(MM::g_Keyword_Description, command.str().c_str(), MM::String, true); @@ -101,7 +99,7 @@ int CPLogic::Initialize() char buildName[MM::MaxStrLength]; unsigned int tmp; GetProperty(g_FirmwareBuildPropertyName, buildName); - string s = buildName; + std::string s = buildName; hub_->SetLastSerialAnswer(s); int ret = hub_->ParseAnswerAfterUnderscore(tmp); if (!ret) { @@ -157,7 +155,7 @@ int CPLogic::Initialize() AddAllowedValue(g_SetCardPresetPropertyName, g_PresetCode2, 2); AddAllowedValue(g_SetCardPresetPropertyName, g_PresetCode3, 3); AddAllowedValue(g_SetCardPresetPropertyName, g_PresetCode4, 4); - if (useAs4ChShutter_) // includes useAsdiSPIMShutter_ + if (useAs4ChShutter_) { AddAllowedValue(g_SetCardPresetPropertyName, g_PresetCode5, 5); AddAllowedValue(g_SetCardPresetPropertyName, g_PresetCode6, 6); @@ -268,7 +266,8 @@ int CPLogic::Initialize() AddAllowedValue(g_SaveSettingsPropertyName, g_SaveSettingsOrig); AddAllowedValue(g_SaveSettingsPropertyName, g_SaveSettingsDone); - // edits cell wherever current pointer position is + // edits cell type wherever current pointer position is + // Note: also add new cell types to OnAdvancedProperties pAct = new CPropertyAction (this, &CPLogic::OnEditCellType); CreateProperty(g_EditCellTypePropertyName, g_CellTypeCode0, MM::String, false, pAct); AddAllowedValue(g_EditCellTypePropertyName, g_CellTypeCode0, 0); @@ -287,6 +286,10 @@ int CPLogic::Initialize() AddAllowedValue(g_EditCellTypePropertyName, g_CellTypeCode13, 13); AddAllowedValue(g_EditCellTypePropertyName, g_CellTypeCode14, 14); AddAllowedValue(g_EditCellTypePropertyName, g_CellTypeCode15, 15); + if (FirmwareVersionAtLeast(3.50)) { + AddAllowedValue(g_EditCellTypePropertyName, g_CellTypeCode16, 16); + AddAllowedValue(g_EditCellTypePropertyName, g_CellTypeCode17, 17); + } AddAllowedValue(g_EditCellTypePropertyName, g_IOTypeCode0, 100); AddAllowedValue(g_EditCellTypePropertyName, g_IOTypeCode1, 101); AddAllowedValue(g_EditCellTypePropertyName, g_IOTypeCode2, 102); @@ -351,61 +354,71 @@ int CPLogic::Initialize() // special masked preset selector for shutter channel pAct = new CPropertyAction (this, &CPLogic::OnSetShutterChannel); CreateProperty(g_SetChannelPropertyName, g_7ChannelNone, MM::String, false, pAct); - // use (CCA X) card presets here, just under a different name - AddAllowedValue(g_SetChannelPropertyName, g_ChannelOnly1, 37); - AddAllowedValue(g_SetChannelPropertyName, g_ChannelOnly2, 38); - AddAllowedValue(g_SetChannelPropertyName, g_ChannelOnly3, 39); - AddAllowedValue(g_SetChannelPropertyName, g_ChannelOnly4, 40); - AddAllowedValue(g_SetChannelPropertyName, g_ChannelOnly5, 41); - AddAllowedValue(g_SetChannelPropertyName, g_ChannelOnly6, 42); - AddAllowedValue(g_SetChannelPropertyName, g_ChannelOnly7, 43); - AddAllowedValue(g_SetChannelPropertyName, g_Channel2And4, 44); - AddAllowedValue(g_SetChannelPropertyName, g_Channel3And5, 45); - AddAllowedValue(g_SetChannelPropertyName, g_Channel4And6, 46); - AddAllowedValue(g_SetChannelPropertyName, g_Channel5And7, 47); - AddAllowedValue(g_SetChannelPropertyName, g_Channel1And3And5, 48); - AddAllowedValue(g_SetChannelPropertyName, g_Channel2And4And6, 49); - AddAllowedValue(g_SetChannelPropertyName, g_7ChannelNone, 50); - if (FirmwareVersionAtLeast(3.35)) { - AddAllowedValue(g_SetChannelPropertyName, g_Channel1And6, 53); - AddAllowedValue(g_SetChannelPropertyName, g_Channel1And4And6, 54); - } - if (FirmwareVersionAtLeast(3.37)) { - AddAllowedValue(g_SetChannelPropertyName, g_Channel1And4, 55); - AddAllowedValue(g_SetChannelPropertyName, g_Channel2And5, 56); - AddAllowedValue(g_SetChannelPropertyName, g_Channel3And6, 57); - AddAllowedValue(g_SetChannelPropertyName, g_Channel1And5, 58); - AddAllowedValue(g_SetChannelPropertyName, g_Channel2And6, 59); + if (FirmwareVersionAtLeast(3.29)) { + // use (CCA X) card presets here, just under a different name + AddAllowedValue(g_SetChannelPropertyName, g_ChannelOnly1, 37); + AddAllowedValue(g_SetChannelPropertyName, g_ChannelOnly2, 38); + AddAllowedValue(g_SetChannelPropertyName, g_ChannelOnly3, 39); + AddAllowedValue(g_SetChannelPropertyName, g_ChannelOnly4, 40); + AddAllowedValue(g_SetChannelPropertyName, g_ChannelOnly5, 41); + AddAllowedValue(g_SetChannelPropertyName, g_ChannelOnly6, 42); + AddAllowedValue(g_SetChannelPropertyName, g_ChannelOnly7, 43); + AddAllowedValue(g_SetChannelPropertyName, g_Channel2And4, 44); + AddAllowedValue(g_SetChannelPropertyName, g_Channel3And5, 45); + AddAllowedValue(g_SetChannelPropertyName, g_Channel4And6, 46); + AddAllowedValue(g_SetChannelPropertyName, g_Channel5And7, 47); + AddAllowedValue(g_SetChannelPropertyName, g_Channel1And3And5, 48); + AddAllowedValue(g_SetChannelPropertyName, g_Channel2And4And6, 49); + AddAllowedValue(g_SetChannelPropertyName, g_7ChannelNone, 50); + if (FirmwareVersionAtLeast(3.35)) { + AddAllowedValue(g_SetChannelPropertyName, g_Channel1And6, 53); + AddAllowedValue(g_SetChannelPropertyName, g_Channel1And4And6, 54); + } + if (FirmwareVersionAtLeast(3.37)) { + AddAllowedValue(g_SetChannelPropertyName, g_Channel1And4, 55); + AddAllowedValue(g_SetChannelPropertyName, g_Channel2And5, 56); + AddAllowedValue(g_SetChannelPropertyName, g_Channel3And6, 57); + AddAllowedValue(g_SetChannelPropertyName, g_Channel1And5, 58); + AddAllowedValue(g_SetChannelPropertyName, g_Channel2And6, 59); + } } UpdateProperty(g_SetChannelPropertyName); // doesn't do anything right now SetProperty(g_SetChannelPropertyName, g_7ChannelNone); // makes sure card actually gets initialized SetOpen(false); // always start shutter in closed state } - if (useAsdiSPIMShutter_) { - // set up card up for diSPIM shutter - // this sets up all 8 BNC outputs including 4 lasers - SetProperty(g_SetCardPresetPropertyName, g_PresetCode14); - - // set to be triggered by micro-mirror card for diSPIM case + if (useAsdiSPIMShutter_) { // true if it is triggered by backplane TTL1 like in diSPIM + // set PLC clock/trigger source to be micro-mirror card SetProperty(g_TriggerSourcePropertyName, g_TriggerSourceCode1); - } - // things for shutter when not a diSPIM - if ((useAs4ChShutter_ || useAs7ChShutter_) && !useAsdiSPIMShutter_) { - // sets up 4 lasers triggered by cell 10 - SetProperty(g_SetCardPresetPropertyName, g_PresetCode12); + if (useAs4ChShutter_) { // original diSPIM use case + // set up card up for diSPIM shutter + // this sets up all 8 BNC outputs including 4 lasers, sets cell 10 as the "hardware shutter open" indicator, etc. + // NB this sets up camera and laser triggers too + SetProperty(g_SetCardPresetPropertyName, g_PresetCode14); + } + + if (useAs7ChShutter_) { + // sets cell 10 as "hardware shutter open" indicator combining the TTL1 line and cell 8 which is "software shutter open" + SetProperty(g_SetCardPresetPropertyName, g_PresetCode12); - // make it ignore the TTL backplane signal usually from the micro-mirror card - if (FirmwareVersionAtLeast(3.27)) { - SetProperty(g_SetCardPresetPropertyName, g_PresetCode36); - } else { - // have to replicate preset behavior ourselves: cell 10 will reflect cell 8 - SetProperty(g_PointerPositionPropertyName, "10"); - SetProperty(g_EditCellTypePropertyName, g_CellTypeCode5); - SetProperty(g_EditCellInput1PropertyName, "64"); - SetProperty(g_EditCellInput2PropertyName, "8"); + // set output #8 to be Camera0 trigger which is on the internal TTL0 line + SetProperty(g_PointerPositionPropertyName, "40"); + SetProperty(g_EditCellTypePropertyName, g_IOTypeCode2); + SetProperty(g_EditCellConfigPropertyName, "41"); } + + } else if (useAs4ChShutter_ || useAs7ChShutter_) { // things for shutter when a shutter but not TTL1-triggered + // make it ignore the TTL backplane signal usually from the micro-mirror card + if (FirmwareVersionAtLeast(3.27)) { + SetProperty(g_SetCardPresetPropertyName, g_PresetCode36); + } else { + // have to replicate preset behavior ourselves: cell 10 will reflect cell 8 + SetProperty(g_PointerPositionPropertyName, "10"); + SetProperty(g_EditCellTypePropertyName, g_CellTypeCode5); + SetProperty(g_EditCellInput1PropertyName, "64"); + SetProperty(g_EditCellInput2PropertyName, "8"); + } } @@ -417,8 +430,9 @@ int CPLogic::SetOpen(bool open) { if (useAs4ChShutter_ || useAs7ChShutter_) { - ostringstream command; command.str(""); + std::ostringstream command; command.str(""); shutterOpen_ = open; + // sets cell 8 which is "software shutter open" indicator via preset 11 (sets high) or preset 10 (sets low) if (open) { SetProperty(g_SetCardPresetPropertyName, g_PresetCode11); } else { @@ -437,37 +451,44 @@ int CPLogic::GetOpen(bool& open) //////////////// // action handlers +// Modify the "PLogic Mode Table" in the header if you change this function. int CPLogic::OnPLogicMode(MM::PropertyBase* pProp, MM::ActionType eAct) { if (eAct == MM::BeforeGet) { // do nothing for now } else if (eAct == MM::AfterSet) { - string tmpstr; - pProp->Get(tmpstr); - if (tmpstr.compare(g_PLogicModediSPIMShutter) == 0) - { - useAsdiSPIMShutter_ = true; - useAs4ChShutter_ = true; - useAs7ChShutter_ = false; - } - else if (tmpstr.compare(g_PLogicMode4ChShutter) == 0) - { - useAsdiSPIMShutter_ = false; - useAs4ChShutter_ = true; - useAs7ChShutter_ = false; - } - else if (tmpstr.compare(g_PLogicMode7ChShutter) == 0) - { - useAsdiSPIMShutter_ = false; - useAs4ChShutter_ = false; - useAs7ChShutter_ = true; - } - else - { - useAsdiSPIMShutter_ = false; - useAs4ChShutter_ = false; - useAs7ChShutter_ = false; - } + std::string tmpstr; + pProp->Get(tmpstr); + if (tmpstr.compare(g_PLogicModediSPIMShutter) == 0) + { + useAsdiSPIMShutter_ = true; + useAs4ChShutter_ = true; + useAs7ChShutter_ = false; + } + else if (tmpstr.compare(g_PLogicMode4ChShutter) == 0) + { + useAsdiSPIMShutter_ = false; + useAs4ChShutter_ = true; + useAs7ChShutter_ = false; + } + else if (tmpstr.compare(g_PLogicMode7ChShutter) == 0) + { + useAsdiSPIMShutter_ = false; + useAs4ChShutter_ = false; + useAs7ChShutter_ = true; + } + else if (tmpstr.compare(g_PLogicMode7ChTTLShutter) == 0) + { + useAsdiSPIMShutter_ = true; + useAs4ChShutter_ = false; + useAs7ChShutter_ = true; + } + else + { + useAsdiSPIMShutter_ = false; + useAs4ChShutter_ = false; + useAs7ChShutter_ = false; + } } return DEVICE_OK; } @@ -485,9 +506,9 @@ int CPLogic::OnSetShutterChannel(MM::PropertyBase* pProp, MM::ActionType eAct) } } } else if (eAct == MM::AfterSet) { - ostringstream command; command.str(""); + std::ostringstream command; command.str(""); long tmp; - string tmpstr; + std::string tmpstr; RETURN_ON_MM_ERROR ( GetCurrentPropertyData(pProp->GetName().c_str(), tmp) ); if (tmp < 0) return DEVICE_OK; // no preset and other "signaling" preset codes are negative command << addressChar_ << "CCA X=" << tmp; @@ -502,11 +523,11 @@ int CPLogic::OnSetShutterChannel(MM::PropertyBase* pProp, MM::ActionType eAct) int CPLogic::OnPLogicOutputState(MM::PropertyBase* pProp, MM::ActionType eAct) { unsigned int val; - ostringstream command; command.str(""); + std::ostringstream command; command.str(""); if (eAct == MM::BeforeGet || eAct == MM::AfterSet) { // always read - command << addressChar_ << "RDADC Z?"; + command << addressChar_ << "RA Z?"; RETURN_ON_MM_ERROR ( hub_->QueryCommandVerify(command.str(), ":A") ); RETURN_ON_MM_ERROR ( hub_->ParseAnswerAfterPosition2(val) ); if (!pProp->Set((long)val)) @@ -518,11 +539,11 @@ int CPLogic::OnPLogicOutputState(MM::PropertyBase* pProp, MM::ActionType eAct) int CPLogic::OnPLogicOutputStateUpper(MM::PropertyBase* pProp, MM::ActionType eAct) { unsigned int val; - ostringstream command; command.str(""); + std::ostringstream command; command.str(""); if (eAct == MM::BeforeGet || eAct == MM::AfterSet) { // always read - command << addressChar_ << "RDADC F?"; + command << addressChar_ << "RA F?"; RETURN_ON_MM_ERROR ( hub_->QueryCommandVerify(command.str(), ":A") ); RETURN_ON_MM_ERROR ( hub_->ParseAnswerAfterPosition2(val) ); if (!pProp->Set((long)val)) @@ -534,11 +555,11 @@ int CPLogic::OnPLogicOutputStateUpper(MM::PropertyBase* pProp, MM::ActionType eA int CPLogic::OnFrontpanelOutputState(MM::PropertyBase* pProp, MM::ActionType eAct) { unsigned int val; - ostringstream command; command.str(""); + std::ostringstream command; command.str(""); if (eAct == MM::BeforeGet || eAct == MM::AfterSet) { // always read - command << addressChar_ << "RDADC X?"; + command << addressChar_ << "RA X?"; RETURN_ON_MM_ERROR ( hub_->QueryCommandVerify(command.str(), ":A") ); RETURN_ON_MM_ERROR ( hub_->ParseAnswerAfterPosition2(val) ); if (!pProp->Set((long)val)) @@ -550,11 +571,11 @@ int CPLogic::OnFrontpanelOutputState(MM::PropertyBase* pProp, MM::ActionType eAc int CPLogic::OnBackplaneOutputState(MM::PropertyBase* pProp, MM::ActionType eAct) { unsigned int val; - ostringstream command; command.str(""); + std::ostringstream command; command.str(""); if (eAct == MM::BeforeGet || eAct == MM::AfterSet) { // always read - command << addressChar_ << "RDADC Y?"; + command << addressChar_ << "RA Y?"; RETURN_ON_MM_ERROR ( hub_->QueryCommandVerify(command.str(), ":A") ); RETURN_ON_MM_ERROR ( hub_->ParseAnswerAfterPosition2(val) ); if (!pProp->Set((long)val)) @@ -565,23 +586,23 @@ int CPLogic::OnBackplaneOutputState(MM::PropertyBase* pProp, MM::ActionType eAct int CPLogic::OnTriggerSource(MM::PropertyBase* pProp, MM::ActionType eAct) { - ostringstream command; command.str(""); + std::ostringstream command; command.str(""); long tmp; - string tmpstr; + std::string tmpstr; if (eAct == MM::BeforeGet) { if (!refreshProps_ && initialized_) return DEVICE_OK; command << "PM " << axisLetter_ << "?"; RETURN_ON_MM_ERROR ( hub_->QueryCommandVerify(command.str(), axisLetter_) ); RETURN_ON_MM_ERROR ( hub_->ParseAnswerAfterEquals(tmp) ); - bool success = 0; + bool success = false; switch (tmp) { case 0: success = pProp->Set(g_TriggerSourceCode0); break; case 1: success = pProp->Set(g_TriggerSourceCode1); break; case 2: success = pProp->Set(g_TriggerSourceCode2); break; case 3: success = pProp->Set(g_TriggerSourceCode3); break; case 4: success = pProp->Set(g_TriggerSourceCode4); break; - default: success=0; + default: break; } if (!success) return DEVICE_INVALID_PROPERTY_VALUE; @@ -596,7 +617,7 @@ int CPLogic::OnTriggerSource(MM::PropertyBase* pProp, MM::ActionType eAct) int CPLogic::OnPointerPosition(MM::PropertyBase* pProp, MM::ActionType eAct) { static bool justSet = false; - ostringstream command; command.str(""); + std::ostringstream command; command.str(""); if (eAct == MM::BeforeGet) { if (!refreshProps_ && initialized_ && !justSet) @@ -621,8 +642,8 @@ int CPLogic::OnPointerPosition(MM::PropertyBase* pProp, MM::ActionType eAct) int CPLogic::OnSaveCardSettings(MM::PropertyBase* pProp, MM::ActionType eAct) { - string tmpstr; - ostringstream command; command.str(""); + std::string tmpstr; + std::ostringstream command; command.str(""); if (eAct == MM::AfterSet) { command << addressChar_ << "SS "; pProp->Get(tmpstr); @@ -644,7 +665,7 @@ int CPLogic::OnSaveCardSettings(MM::PropertyBase* pProp, MM::ActionType eAct) int CPLogic::OnRefreshProperties(MM::PropertyBase* pProp, MM::ActionType eAct) { - string tmpstr; + std::string tmpstr; if (eAct == MM::AfterSet) { pProp->Get(tmpstr); if (tmpstr.compare(g_YesState) == 0) @@ -691,7 +712,7 @@ int CPLogic::RefreshEditCellPropertyValues() int CPLogic::OnEditCellType(MM::PropertyBase* pProp, MM::ActionType eAct) { // NB: works for I/O addresses but will have incorrect strings - ostringstream command; command.str(""); + std::ostringstream command; command.str(""); if(currentPosition_ <= numCells_ // normal cells || (currentPosition_ >= PLOGIC_FRONTPANEL_START_ADDRESS && currentPosition_ <= PLOGIC_BACKPLANE_END_ADDRESS)) { // or I/O addresses if (eAct == MM::BeforeGet) { @@ -759,7 +780,7 @@ int CPLogic::OnEditCellInput4(MM::PropertyBase* pProp, MM::ActionType eAct) int CPLogic::OnEditCellUpdates(MM::PropertyBase* pProp, MM::ActionType eAct) { - string tmpstr; + std::string tmpstr; if (eAct == MM::AfterSet) { pProp->Get(tmpstr); if (tmpstr.compare(g_YesState) == 0) @@ -770,17 +791,17 @@ int CPLogic::OnEditCellUpdates(MM::PropertyBase* pProp, MM::ActionType eAct) return DEVICE_OK; } -int CPLogic::GetCellPropertyName(long index, string suffix, char* name) +int CPLogic::GetCellPropertyName(long index, const std::string& suffix, char* name) { - ostringstream os; - os << "PCell_" << setw(2) << setfill('0') << index << suffix; + std::ostringstream os; + os << "PCell_" << std::setw(2) << std::setfill('0') << index << suffix; CDeviceUtils::CopyLimitedString(name, os.str().c_str()); return DEVICE_OK; } -int CPLogic::GetIOPropertyName(long index, string suffix, char* name) +int CPLogic::GetIOPropertyName(long index, const std::string& suffix, char* name) { - ostringstream os; + std::ostringstream os; if (index < PLOGIC_BACKPLANE_START_ADDRESS) { // front panel os << "IOFrontpanel_" << index - PLOGIC_FRONTPANEL_START_ADDRESS + 1; } else { // backplane @@ -798,7 +819,7 @@ int CPLogic::OnAdvancedProperties(MM::PropertyBase* pProp, MM::ActionType eAct) return DEVICE_OK; // do nothing } else if (eAct == MM::AfterSet) { - string tmpstr; + std::string tmpstr; pProp->Get(tmpstr); if ((tmpstr.compare(g_YesState) == 0) && !advancedPropsEnabled_) // after creating advanced properties once no need to repeat { @@ -807,13 +828,15 @@ int CPLogic::OnAdvancedProperties(MM::PropertyBase* pProp, MM::ActionType eAct) advancedPropsEnabled_ = true; + // make sure that the new properties are initialized, set to true at the end of creating them + initialized_ = false; + // force-on refresh char refreshPropValsStr[MM::MaxStrLength]; GetProperty(g_RefreshPropValsPropertyName, refreshPropValsStr); SetProperty(g_RefreshPropValsPropertyName, g_YesState); - for (long i=1; i<=(long)numCells_; i++) { - + for (long i = 1; i <= (long)numCells_; i++) { // logic cell type GetCellPropertyName(i, "_CellType", propName); pActEx = new CPropertyActionEx (this, &CPLogic::OnCellType, i); @@ -834,6 +857,10 @@ int CPLogic::OnAdvancedProperties(MM::PropertyBase* pProp, MM::ActionType eAct) AddAllowedValue(propName, g_CellTypeCode13, 13); AddAllowedValue(propName, g_CellTypeCode14, 14); AddAllowedValue(propName, g_CellTypeCode15, 15); + if (FirmwareVersionAtLeast(3.50)) { + AddAllowedValue(propName, g_CellTypeCode16, 16); + AddAllowedValue(propName, g_CellTypeCode17, 17); + } UpdateProperty(propName); // logic cell CCA Z code @@ -865,10 +892,9 @@ int CPLogic::OnAdvancedProperties(MM::PropertyBase* pProp, MM::ActionType eAct) pActEx = new CPropertyActionEx (this, &CPLogic::OnInput4, i); CreateProperty(propName, "0", MM::Integer, false, pActEx); UpdateProperty(propName); - } - for (long i=PLOGIC_FRONTPANEL_START_ADDRESS; i<=PLOGIC_BACKPLANE_END_ADDRESS; i++) { + for (long i = PLOGIC_FRONTPANEL_START_ADDRESS; i <= (long)PLOGIC_BACKPLANE_END_ADDRESS; i++) { GetIOPropertyName(i, "_IOType", propName); pActEx = new CPropertyActionEx (this, &CPLogic::OnIOType, i); CreateProperty(propName, "0", MM::String, false, pActEx); @@ -885,8 +911,11 @@ int CPLogic::OnAdvancedProperties(MM::PropertyBase* pProp, MM::ActionType eAct) // restore refresh setting SetProperty(g_RefreshPropValsPropertyName, refreshPropValsStr); + + initialized_ = true; } } + return DEVICE_OK; } @@ -920,7 +949,7 @@ int CPLogic::RefreshAdvancedCellPropertyValues(long index) // don't bother updating things like if the user changes position from the property int CPLogic::SetPositionDirectly(unsigned int position) { - ostringstream command; command.str(""); + std::ostringstream command; command.str(""); if (position == currentPosition_) return DEVICE_OK; command << "M " << axisLetter_ << "=" << position; @@ -931,7 +960,7 @@ int CPLogic::SetPositionDirectly(unsigned int position) int CPLogic::RefreshCurrentPosition() { - ostringstream command; command.str(""); + std::ostringstream command; command.str(""); unsigned int tmp; command << "W " << axisLetter_; RETURN_ON_MM_ERROR ( hub_->QueryCommandVerify(command.str(),":A") ); @@ -942,9 +971,9 @@ int CPLogic::RefreshCurrentPosition() int CPLogic::OnCellType(MM::PropertyBase* pProp, MM::ActionType eAct, long index) { - ostringstream command; command.str(""); + std::ostringstream command; command.str(""); long tmp; - string tmpstr; + std::string tmpstr; if (eAct == MM::BeforeGet) { if (!refreshProps_ && initialized_) return DEVICE_OK; @@ -952,12 +981,13 @@ int CPLogic::OnCellType(MM::PropertyBase* pProp, MM::ActionType eAct, long index command << addressChar_ << "CCA Y?"; RETURN_ON_MM_ERROR ( hub_->QueryCommandVerify(command.str(),":A") ); RETURN_ON_MM_ERROR ( hub_->ParseAnswerAfterEquals(tmp) ); - bool success = 0; + bool success = false; if (currentPosition_ > numCells_) { switch (tmp) { case 0: success = pProp->Set(g_IOTypeCode0); break; case 1: success = pProp->Set(g_IOTypeCode1); break; case 2: success = pProp->Set(g_IOTypeCode2); break; + default: break; } } else { switch (tmp) { @@ -977,7 +1007,9 @@ int CPLogic::OnCellType(MM::PropertyBase* pProp, MM::ActionType eAct, long index case 13:success = pProp->Set(g_CellTypeCode13); break; case 14:success = pProp->Set(g_CellTypeCode14); break; case 15:success = pProp->Set(g_CellTypeCode15); break; - default: success=0; + case 16:success = pProp->Set(g_CellTypeCode16); break; + case 17:success = pProp->Set(g_CellTypeCode17); break; + default: break; } } if (!success) @@ -996,7 +1028,7 @@ int CPLogic::OnCellType(MM::PropertyBase* pProp, MM::ActionType eAct, long index int CPLogic::OnCellConfig(MM::PropertyBase* pProp, MM::ActionType eAct, long index) { - ostringstream command; command.str(""); + std::ostringstream command; command.str(""); long tmp; if (eAct == MM::BeforeGet) { if (!refreshProps_ && initialized_) @@ -1018,7 +1050,7 @@ int CPLogic::OnCellConfig(MM::PropertyBase* pProp, MM::ActionType eAct, long ind int CPLogic::OnInput1(MM::PropertyBase* pProp, MM::ActionType eAct, long index) { - ostringstream command; command.str(""); + std::ostringstream command; command.str(""); long tmp; if (eAct == MM::BeforeGet) { if (!refreshProps_ && initialized_) @@ -1040,7 +1072,7 @@ int CPLogic::OnInput1(MM::PropertyBase* pProp, MM::ActionType eAct, long index) int CPLogic::OnInput2(MM::PropertyBase* pProp, MM::ActionType eAct, long index) { - ostringstream command; command.str(""); + std::ostringstream command; command.str(""); long tmp; if (eAct == MM::BeforeGet) { if (!refreshProps_ && initialized_) @@ -1062,7 +1094,7 @@ int CPLogic::OnInput2(MM::PropertyBase* pProp, MM::ActionType eAct, long index) int CPLogic::OnInput3(MM::PropertyBase* pProp, MM::ActionType eAct, long index) { - ostringstream command; command.str(""); + std::ostringstream command; command.str(""); long tmp; if (eAct == MM::BeforeGet) { if (!refreshProps_ && initialized_) @@ -1084,7 +1116,7 @@ int CPLogic::OnInput3(MM::PropertyBase* pProp, MM::ActionType eAct, long index) int CPLogic::OnInput4(MM::PropertyBase* pProp, MM::ActionType eAct, long index) { - ostringstream command; command.str(""); + std::ostringstream command; command.str(""); long tmp; if (eAct == MM::BeforeGet) { if (!refreshProps_ && initialized_) @@ -1106,7 +1138,7 @@ int CPLogic::OnInput4(MM::PropertyBase* pProp, MM::ActionType eAct, long index) int CPLogic::OnIOType(MM::PropertyBase* pProp, MM::ActionType eAct, long index) { - ostringstream command; command.str(""); + std::ostringstream command; command.str(""); long tmp; if (eAct == MM::BeforeGet) { if (!refreshProps_ && initialized_) @@ -1115,12 +1147,12 @@ int CPLogic::OnIOType(MM::PropertyBase* pProp, MM::ActionType eAct, long index) command << addressChar_ << "CCA Y?"; RETURN_ON_MM_ERROR ( hub_->QueryCommandVerify(command.str(),":A") ); RETURN_ON_MM_ERROR ( hub_->ParseAnswerAfterEquals(tmp) ); - bool success = 0; + bool success = false; switch (tmp) { case 0: success = pProp->Set(g_IOTypeCode0); break; case 1: success = pProp->Set(g_IOTypeCode1); break; case 2: success = pProp->Set(g_IOTypeCode2); break; - default: success=0; + default: break; } if (!success) return DEVICE_INVALID_PROPERTY_VALUE; @@ -1137,7 +1169,7 @@ int CPLogic::OnIOType(MM::PropertyBase* pProp, MM::ActionType eAct, long index) int CPLogic::OnIOSourceAddress(MM::PropertyBase* pProp, MM::ActionType eAct, long index) { - ostringstream command; command.str(""); + std::ostringstream command; command.str(""); long tmp; if (eAct == MM::BeforeGet) { if (!refreshProps_ && initialized_) @@ -1159,12 +1191,12 @@ int CPLogic::OnIOSourceAddress(MM::PropertyBase* pProp, MM::ActionType eAct, lon int CPLogic::OnClearAllCellStates(MM::PropertyBase* pProp, MM::ActionType eAct) { - ostringstream command; command.str(""); + std::ostringstream command; command.str(""); if (eAct == MM::BeforeGet) { pProp->Set(g_IdleState); } else if (eAct == MM::AfterSet) { - string tmpstr; + std::string tmpstr; pProp->Get(tmpstr); if (tmpstr.compare(g_DoItState) == 0) { @@ -1178,9 +1210,9 @@ int CPLogic::OnClearAllCellStates(MM::PropertyBase* pProp, MM::ActionType eAct) int CPLogic::OnSetCardPreset(MM::PropertyBase* pProp, MM::ActionType eAct) { - ostringstream command; command.str(""); + std::ostringstream command; command.str(""); long tmp; - string tmpstr; + std::string tmpstr; if (eAct == MM::BeforeGet) { // can't do anything of real value here if (!initialized_) diff --git a/DeviceAdapters/ASITiger/ASIPLogic.h b/DeviceAdapters/ASITiger/ASIPLogic.h index 4f8125929..e3dd0bec6 100644 --- a/DeviceAdapters/ASITiger/ASIPLogic.h +++ b/DeviceAdapters/ASITiger/ASIPLogic.h @@ -22,8 +22,8 @@ // BASED ON: ASIStage.h and others // -#ifndef _ASIPLOGIC_H_ -#define _ASIPLOGIC_H_ +#ifndef ASIPLOGIC_H +#define ASIPLOGIC_H #include "ASIPeripheralBase.h" #include "MMDevice.h" @@ -36,7 +36,6 @@ class CPLogic : public ASIPeripheralBase ~CPLogic() { } // Device API - // ---------- int Initialize(); bool Busy() { return false; } @@ -46,7 +45,6 @@ class CPLogic : public ASIPeripheralBase int Fire(double /*deltaT*/) { return DEVICE_UNSUPPORTED_COMMAND; } // action interface - // ---------------- int OnPLogicMode (MM::PropertyBase* pProp, MM::ActionType eAct); int OnSetShutterChannel (MM::PropertyBase* pProp, MM::ActionType eAct); int OnPLogicOutputState (MM::PropertyBase* pProp, MM::ActionType eAct); @@ -78,24 +76,35 @@ class CPLogic : public ASIPeripheralBase private: - string axisLetter_; + std::string axisLetter_; unsigned int numCells_; unsigned int currentPosition_; // cached value of current position -// static const int NUM_CELLS = 16; - bool useAsdiSPIMShutter_; // super-set of useAs4ChShutter_ + + // PLogic Mode Table + // -------------------- + // diSPIM | 4ch | 7ch | (useAsdiSPIMShutter_, useAs4ChShutter_, useAs7ChShutter_) + // -------------------- + // 0 | 0 | 0 | "None" + // 1 | 1 | 0 | "diSPIM Shutter" + // 0 | 1 | 0 | "Four-channel shutter" + // 0 | 0 | 1 | "Seven-channel shutter" + // 1 | 0 | 1 | "Seven-channel TTL shutter" + bool useAsdiSPIMShutter_; bool useAs4ChShutter_; bool useAs7ChShutter_; + // used together with either useAs4ChShutter_ or useAs7ChShutter_, + // takes into account address 41 backplane (TTL0 for CameraA) + bool shutterOpen_; - bool advancedPropsEnabled_; bool editCellUpdates_; + bool advancedPropsEnabled_; // flag to only create advanced properties once - int SetShutterChannel(); int SetPositionDirectly(unsigned int position); - int GetCellPropertyName(long index, string suffix, char* name); - int GetIOPropertyName(long index, string suffix, char* name); + int GetCellPropertyName(long index, const std::string& suffix, char* name); + int GetIOPropertyName(long index, const std::string& suffix, char* name); int RefreshAdvancedCellPropertyValues(long index); int RefreshCurrentPosition(); int RefreshEditCellPropertyValues(); }; -#endif //_ASIPLOGIC_H_ +#endif // ASIPLOGIC_H diff --git a/DeviceAdapters/ASITiger/ASIScanner.cpp b/DeviceAdapters/ASITiger/ASIScanner.cpp index 52f2b09f4..300c35ae9 100644 --- a/DeviceAdapters/ASITiger/ASIScanner.cpp +++ b/DeviceAdapters/ASITiger/ASIScanner.cpp @@ -547,6 +547,15 @@ int CScanner::Initialize() UpdateProperty(g_SPIMScanDurationPropertyName); } + if (FirmwareVersionAtLeast(3.14)) { + // in 3.50 added bit 7 of SPIM mode for smooth slice (e.g. constant galvo scan for scope) + pAct = new CPropertyAction (this, &CScanner::OnSPIMSmoothSliceEnable); + CreateProperty(g_SPIMSmoothSliceEnable, g_NoState, MM::String, false, pAct); + AddAllowedValue(g_SPIMSmoothSliceEnable, g_YesState); + AddAllowedValue(g_SPIMSmoothSliceEnable, g_NoState); + UpdateProperty(g_SPIMSmoothSliceEnable); + } + } // adding SPIM properties // add ring buffer properties if supported (starting 2.81) @@ -3251,7 +3260,7 @@ int CScanner::OnSPIMAlternateDirectionsEnable(MM::PropertyBase* pProp, MM::Actio RETURN_ON_MM_ERROR( hub_->QueryCommandVerify(command.str(), ":A Z=")); RETURN_ON_MM_ERROR ( hub_->ParseAnswerAfterEquals(tmp) ); tmp = tmp >> 5; // shift left to get bits 5 in position of LSB - tmp &= (0x01); // mask off all but what used to be bit 4 + tmp &= (0x01); // mask off all but what used to be bit 5 switch (tmp) { case 0: success = pProp->Set(g_NoState); break; @@ -3287,6 +3296,55 @@ int CScanner::OnSPIMAlternateDirectionsEnable(MM::PropertyBase* pProp, MM::Actio return DEVICE_OK; } +int CScanner::OnSPIMSmoothSliceEnable(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + ostringstream command; command.str(""); + long tmp = 0; + bool success; + if (eAct == MM::BeforeGet) + { + if (!refreshProps_ && initialized_) + return DEVICE_OK; + command << addressChar_ << "NR Z?"; + RETURN_ON_MM_ERROR( hub_->QueryCommandVerify(command.str(), ":A Z=")); + RETURN_ON_MM_ERROR ( hub_->ParseAnswerAfterEquals(tmp) ); + tmp = tmp >> 7; // shift left to get bits 7 in position of LSB + tmp &= (0x01); // mask off all but what used to be bit 7 + switch (tmp) + { + case 0: success = pProp->Set(g_NoState); break; + case 1: success = pProp->Set(g_YesState); break; + default: success = 0; + } + if (!success) + return DEVICE_INVALID_PROPERTY_VALUE; + } + else if (eAct == MM::AfterSet) { + if (hub_->UpdatingSharedProperties()) + return DEVICE_OK; + string tmpstr; + pProp->Get(tmpstr); + if (tmpstr.compare(g_NoState) == 0) + tmp = 0; + else if (tmpstr.compare(g_YesState) == 0) + tmp = 1; + else + return DEVICE_INVALID_PROPERTY_VALUE; + tmp = tmp << 7; // right shift to get the value to bit 7 + command << addressChar_ << "NR Z?"; + long tmp2; + RETURN_ON_MM_ERROR( hub_->QueryCommandVerify(command.str(), ":A Z=")); + RETURN_ON_MM_ERROR ( hub_->ParseAnswerAfterEquals(tmp2) ); + tmp += (tmp2 & (0x7F)); // keep bit 7 from tmp, all others use current setting + if (tmp == tmp2) + return DEVICE_OK; // don't need to set value if it's already correct + command.str(""); command << addressChar_ << "NR Z=" << tmp; + RETURN_ON_MM_ERROR ( hub_->QueryCommandVerify(command.str(), ":A") ); + RETURN_ON_MM_ERROR ( hub_->UpdateSharedProperties(addressChar_, pProp->GetName(), tmpstr.c_str()) ); + } + return DEVICE_OK; +} + int CScanner::OnSPIMModeByte(MM::PropertyBase* pProp, MM::ActionType eAct) { ostringstream command; command.str(""); diff --git a/DeviceAdapters/ASITiger/ASIScanner.h b/DeviceAdapters/ASITiger/ASIScanner.h index 8885b23d4..1e1ce7db8 100644 --- a/DeviceAdapters/ASITiger/ASIScanner.h +++ b/DeviceAdapters/ASITiger/ASIScanner.h @@ -122,6 +122,7 @@ class CScanner : public ASIPeripheralBase int OnSPIMPiezoHomeDisable (MM::PropertyBase* pProp, MM::ActionType eAct); int OnSPIMInterleaveSidesEnable(MM::PropertyBase* pProp, MM::ActionType eAct); int OnSPIMAlternateDirectionsEnable(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnSPIMSmoothSliceEnable(MM::PropertyBase* pProp, MM::ActionType eAct); int OnSPIMModeByte (MM::PropertyBase* pProp, MM::ActionType eAct); int OnSPIMNumRepeats (MM::PropertyBase* pProp, MM::ActionType eAct); int OnSPIMState (MM::PropertyBase* pProp, MM::ActionType eAct); diff --git a/DeviceAdapters/ASITiger/ASITiger.h b/DeviceAdapters/ASITiger/ASITiger.h index 591adad76..c466a8519 100644 --- a/DeviceAdapters/ASITiger/ASITiger.h +++ b/DeviceAdapters/ASITiger/ASITiger.h @@ -401,6 +401,7 @@ const char* const g_SPIMPiezoHomeDisable = "SPIMPiezoHomeDisable"; const char* const g_SPIMScannerHomeDisable = "SPIMScannerHomeDisable"; const char* const g_SPIMInterleaveSidesEnable = "SPIMInterleaveSidesEnable"; const char* const g_SPIMAlternateDirectionsEnable = "SPIMAlternateDirectionsEnable"; +const char* const g_SPIMSmoothSliceEnable = "SPIMSmoothSliceEnable"; const char* const g_SPIMNumRepeatsPropertyName = "SPIMNumRepeats"; const char* const g_SPIMArmForTTLPropertyName = "SPIMArm"; const char* const g_SPIMStatePropertyName = "SPIMState"; @@ -477,6 +478,8 @@ const char* const g_CellTypeCode12 = "12 - D flop (sync)"; const char* const g_CellTypeCode13 = "13 - JK flop"; const char* const g_CellTypeCode14 = "14 - one shot (NRT)"; const char* const g_CellTypeCode15 = "15 - delay (NRT)"; +const char* const g_CellTypeCode16 = "16 - one shot OR2 (NRT)"; +const char* const g_CellTypeCode17 = "17 - delay OR2 (NRT)"; const char* const g_IOTypeCode0 = "0 - input"; const char* const g_IOTypeCode1 = "1 - output (open-drain)"; const char* const g_IOTypeCode2 = "2 - output (push-pull)"; @@ -551,6 +554,7 @@ const char* const g_PLogicModeNone = "None"; const char* const g_PLogicModediSPIMShutter = "diSPIM Shutter"; const char* const g_PLogicMode4ChShutter = "Four-channel shutter"; const char* const g_PLogicMode7ChShutter = "Seven-channel shutter"; +const char* const g_PLogicMode7ChTTLShutter = "Seven-channel TTL shutter"; const char* const g_4ChannelNone = "none of outputs 5-8"; const char* const g_7ChannelNone = "none of outputs 1-7"; const char* const g_ChannelOnly1 = "output 1 only"; diff --git a/DeviceAdapters/ASITiger/ASIXYStage.cpp b/DeviceAdapters/ASITiger/ASIXYStage.cpp index caeda2338..37d53360c 100644 --- a/DeviceAdapters/ASITiger/ASIXYStage.cpp +++ b/DeviceAdapters/ASITiger/ASIXYStage.cpp @@ -857,6 +857,9 @@ int CXYStage::OnAdvancedProperties(MM::PropertyBase* pProp, MM::ActionType eAct) CPropertyAction* pAct; advancedPropsEnabled_ = true; + // make sure that the new properties are initialized, set to true at the end of creating them + initialized_ = false; + // overshoot (OS) pAct = new CPropertyAction (this, &CXYStage::OnOvershoot); CreateProperty(g_OvershootPropertyName, "0", MM::Float, false, pAct); @@ -904,6 +907,8 @@ int CXYStage::OnAdvancedProperties(MM::PropertyBase* pProp, MM::ActionType eAct) CreateProperty(g_NrExtraMoveRepsPropertyName, "0", MM::Integer, false, pAct); SetPropertyLimits(g_NrExtraMoveRepsPropertyName, 0, 3); // don't let the user set too high, though there is no actual limit UpdateProperty(g_NrExtraMoveRepsPropertyName); + + initialized_ = true; } } return DEVICE_OK; diff --git a/DeviceAdapters/ASITiger/ASIZStage.cpp b/DeviceAdapters/ASITiger/ASIZStage.cpp index 59b83734e..952308240 100644 --- a/DeviceAdapters/ASITiger/ASIZStage.cpp +++ b/DeviceAdapters/ASITiger/ASIZStage.cpp @@ -715,6 +715,9 @@ int CZStage::OnAdvancedProperties(MM::PropertyBase* pProp, MM::ActionType eAct) CPropertyAction* pAct; advancedPropsEnabled_ = true; + // make sure that the new properties are initialized, set to true at the end of creating them + initialized_ = false; + // overshoot (OS) pAct = new CPropertyAction (this, &CZStage::OnOvershoot); CreateProperty(g_OvershootPropertyName, "0", MM::Float, false, pAct); @@ -754,6 +757,8 @@ int CZStage::OnAdvancedProperties(MM::PropertyBase* pProp, MM::ActionType eAct) pAct = new CPropertyAction (this, &CZStage::OnAZero); CreateProperty(g_AZeroXPropertyName, "0", MM::String, false, pAct); UpdateProperty(g_AZeroXPropertyName); + + initialized_ = true; } } return DEVICE_OK; diff --git a/DeviceAdapters/AlliedVisionCamera/AlliedVisionCamera.cpp b/DeviceAdapters/AlliedVisionCamera/AlliedVisionCamera.cpp index fbc80e37c..d33914664 100644 --- a/DeviceAdapters/AlliedVisionCamera/AlliedVisionCamera.cpp +++ b/DeviceAdapters/AlliedVisionCamera/AlliedVisionCamera.cpp @@ -1384,7 +1384,7 @@ void AlliedVisionCamera::insertFrame(VmbFrame_t *frame) // TODO implement metadata Metadata md; - md.put("Camera", m_cameraName); + md.put(MM::g_Keyword_Metadata_CameraLabel, m_cameraName); VmbUint8_t *buffer = reinterpret_cast(frame->buffer); err = GetCoreCallback()->InsertImage(this, buffer, GetImageWidth(), GetImageHeight(), m_currentPixelFormat.getBytesPerPixel(), diff --git a/DeviceAdapters/AmScope/AmScope.cpp b/DeviceAdapters/AmScope/AmScope.cpp index cecaf886d..77a043190 100644 --- a/DeviceAdapters/AmScope/AmScope.cpp +++ b/DeviceAdapters/AmScope/AmScope.cpp @@ -769,7 +769,7 @@ int AmScope::InsertImage() MMThreadGuard g(imgPixelsLock_); // Important: metadata about the image are generated here: Metadata md; - md.put("Camera", label); + md.put(MM::g_Keyword_Metadata_CameraLabel, label); md.put(MM::g_Keyword_Elapsed_Time_ms, CDeviceUtils::ConvertToString((timeStamp - sequenceStartTime_).getMsec())); md.put(MM::g_Keyword_Metadata_ImageNumber, CDeviceUtils::ConvertToString( imageCounter_ )); md.put(MM::g_Keyword_Metadata_ROI_X, CDeviceUtils::ConvertToString( (long) roiX_)); diff --git a/DeviceAdapters/Andor/Andor.cpp b/DeviceAdapters/Andor/Andor.cpp index c2dd44805..ef4cbbe2b 100644 --- a/DeviceAdapters/Andor/Andor.cpp +++ b/DeviceAdapters/Andor/Andor.cpp @@ -5780,7 +5780,7 @@ unsigned int AndorCamera::PopulateROIDropdownFVB() // remove first .put deprecated and check if any changes. Perhaps take 5seq MDA first using current build, then rebuild and check difference // Check both MM Metadata, and .text file generated by stack images. // add SRRF - may need better timings, perhaps a new m_var_ but can decide during impl. - //md.put("Camera", label); + //md.put(MM::g_Keyword_Metadata_CameraLabel, label); //md.put(MM::g_Keyword_Elapsed_Time_ms, CDeviceUtils::ConvertToString((timestamp - startTime_).getMsec())); //md.put(MM::g_Keyword_Metadata_ImageNumber, CDeviceUtils::ConvertToString(imageCounter_)); //md.put(MM::g_Keyword_Binning, binSize_); diff --git a/DeviceAdapters/Aravis/AravisCamera.cpp b/DeviceAdapters/Aravis/AravisCamera.cpp new file mode 100644 index 000000000..7c740b3d3 --- /dev/null +++ b/DeviceAdapters/Aravis/AravisCamera.cpp @@ -0,0 +1,1209 @@ +/* + Copyright 2024 Hazen Babcock + + 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. +*/ + +#include "AravisCamera.h" + +#include +#include +#include + + +std::vector supportedPixelFormats = { + "Mono8", + "Mono10", + "Mono12", + "Mono14", + "Mono16", + "BayerRG8", + "BayerRG10", + "BayerRG12", + "BayerRG16", + "RGB8", + "BGR8" +}; + + +/* + * Module functions. + */ +MODULE_API void InitializeModuleData() +{ + uint64_t nDevices=0; + + // Debugging. + //arv_debug_enable("all:1,device"); + + // Update and get number of aravis compatible cameras. + arv_update_device_list(); + nDevices = arv_get_n_devices(); + + for (int i = 0; i < nDevices; i++) + { + RegisterDevice(arv_get_device_id(i), MM::CameraDevice, "Aravis Camera"); + } +} + + +MODULE_API MM::Device* CreateDevice(const char* deviceName) +{ + return new AravisCamera(deviceName); +} + + +MODULE_API void DeleteDevice(MM::Device* pDevice) +{ + delete pDevice; +} + + +// RGB unpacker. +void rgb_to_rgba(unsigned char *dest, unsigned char *source, size_t size) +{ + size_t i; + size_t dOffset = 0; + size_t sOffset = 0; + + for (i = 0; i < size; i++){ + memcpy(dest + dOffset, source + sOffset, 3); + //dest[dOffset + 3] = 0; + sOffset += 3; + dOffset += 4; + } +} + + +// Sequence acquisition callback. +static void +stream_callback (void *user_data, ArvStreamCallbackType type, ArvBuffer *arv_buffer) +{ + AravisCamera *camera = (AravisCamera *) user_data; + + camera->AcquisitionCallback(type, arv_buffer); +} + + +/* + * Camera class and methods. + */ +AravisCamera::AravisCamera(const char *name) : + CCameraBase(), + capturing(false), + counter(0), + exposure_time(0.0), + img_buffer_bit_depth(0), + img_buffer_bytes_per_pixel(0), + img_buffer_height(0), + img_buffer_number_components(0), + img_buffer_number_pixels(0), + img_buffer_size(0), + img_buffer_width(0), + initialized(false), + arv_buffer(nullptr), + arv_cam(nullptr), + arv_cam_name(nullptr), + arv_stream(nullptr), + img_buffer(nullptr), + pixel_type(nullptr) +{ + arv_cam_name = (char *)malloc(sizeof(char) * strlen(name)); + CDeviceUtils::CopyLimitedString(arv_cam_name, name); +} + + +AravisCamera::~AravisCamera() +{ + g_clear_object(&arv_cam); +} + + +// These are in alphabetical order. +void AravisCamera::AcquisitionCallback(ArvStreamCallbackType type, ArvBuffer *cb_arv_buffer) +{ + size_t size; + unsigned char *cb_arv_buffer_data; + + Metadata md; + + if (!capturing){ + return; + } + + switch (type) { + /* Do we need this? IDK. */ + case ARV_STREAM_CALLBACK_TYPE_INIT: + arv_make_thread_realtime (10); + arv_make_thread_high_priority(-10); + break; + case ARV_STREAM_CALLBACK_TYPE_BUFFER_DONE: + + // Copy buffer data. + g_assert(cb_arv_buffer == arv_stream_pop_buffer(arv_stream)); + g_assert(cb_arv_buffer != NULL); + ArvBufferUpdate(cb_arv_buffer); + + // Image metadata. + md.put(MM::g_Keyword_Metadata_CameraLabel, ""); + md.put(MM::g_Keyword_Metadata_ROI_X, CDeviceUtils::ConvertToString((long)img_buffer_width)); + md.put(MM::g_Keyword_Metadata_ROI_Y, CDeviceUtils::ConvertToString((long)img_buffer_height)); + md.put(MM::g_Keyword_Metadata_ImageNumber, CDeviceUtils::ConvertToString(counter)); + md.put(MM::g_Keyword_Meatdata_Exposure, exposure_time); + md.put(MM::g_Keyword_PixelType, pixel_type); + + // Pass data to MM. + int ret = GetCoreCallback()->InsertImage(this, + img_buffer, + img_buffer_width, + img_buffer_height, + img_buffer_bytes_per_pixel, + 1, + md.Serialize().c_str(), + FALSE); + if (ret == DEVICE_BUFFER_OVERFLOW) { + GetCoreCallback()->ClearImageBuffer(this); + } + + arv_stream_push_buffer(arv_stream, cb_arv_buffer); + counter += 1; + break; + } +} + + +void AravisCamera::ArvBufferUpdate(ArvBuffer *aBuffer) +{ + int status; + size_t arvSize, size; + guint32 arvPixelFormat; + unsigned char *arvBufferData; + + status = arv_buffer_get_status(aBuffer); + if (status != 0){ + printf("Error, Aravis buffer status is %d\n", status); + return; + } + + // Pixel format updates. + arvPixelFormat = arv_buffer_get_image_pixel_format(aBuffer); + ArvPixelFormatUpdate(arvPixelFormat); + + // Image size updates. + img_buffer_width = (int)arv_buffer_get_image_width(aBuffer); + img_buffer_height = (int)arv_buffer_get_image_height(aBuffer); + img_buffer_number_pixels = img_buffer_width * img_buffer_height; + + // Copy buffer to MM. + arvBufferData = (unsigned char *)arv_buffer_get_data(aBuffer, &arvSize); + size = img_buffer_width * img_buffer_height * img_buffer_bytes_per_pixel; + + if (img_buffer_size != size){ + if (img_buffer != nullptr){ + free(img_buffer); + } + img_buffer = (unsigned char *)malloc(size); + img_buffer_size = size; + } + if (img_buffer_number_components == 1){ + memcpy(img_buffer, arvBufferData, size); + } + else{ + rgb_to_rgba(img_buffer, arvBufferData, img_buffer_number_pixels); + } +} + + +int AravisCamera::ArvCheckError(GError *gerror) const +{ + if (gerror != NULL) { + std::stringstream msg; + msg << "Aravis Error: " << gerror->message; + LogMessage(msg.str(), false); + g_clear_error(&gerror); + return 1; + } + return 0; +} + + +// Call the Aravis library to check exposure time only as needed. +void AravisCamera::ArvGetExposure() +{ + double expTimeUs; + GError *gerror = nullptr; + + expTimeUs = arv_camera_get_exposure_time(arv_cam, &gerror); + if(!ArvCheckError(gerror)){ + exposure_time = expTimeUs * 1.0e-3; + } +} + + +// Update MM image values based on pixel format. +void AravisCamera::ArvPixelFormatUpdate(guint32 arvPixelFormat) +{ + switch (arvPixelFormat){ + case ARV_PIXEL_FORMAT_MONO_8: + img_buffer_bit_depth = 8; + img_buffer_bytes_per_pixel = 1; + img_buffer_number_components = 1; + pixel_type = "8bit mono"; + break; + case ARV_PIXEL_FORMAT_MONO_10: + img_buffer_bit_depth = 10; + img_buffer_bytes_per_pixel = 2; + img_buffer_number_components = 1; + pixel_type = "10bit mono"; + break; + case ARV_PIXEL_FORMAT_MONO_12: + img_buffer_bit_depth = 10; + img_buffer_bytes_per_pixel = 2; + img_buffer_number_components = 1; + pixel_type = "12bit mono"; + break; + case ARV_PIXEL_FORMAT_MONO_14: + img_buffer_bit_depth = 14; + img_buffer_bytes_per_pixel = 2; + img_buffer_number_components = 1; + pixel_type = "14bit mono"; + break; + case ARV_PIXEL_FORMAT_MONO_16: + img_buffer_bit_depth = 16; + img_buffer_bytes_per_pixel = 2; + img_buffer_number_components = 1; + pixel_type = "16bit mono"; + break; + + case ARV_PIXEL_FORMAT_BAYER_RG_8: + img_buffer_bit_depth = 8; + img_buffer_bytes_per_pixel = 1; + img_buffer_number_components = 1; + pixel_type = "8bit mono"; + break; + case ARV_PIXEL_FORMAT_BAYER_RG_10: + img_buffer_bit_depth = 10; + img_buffer_bytes_per_pixel = 2; + img_buffer_number_components = 1; + pixel_type = "10bit mono"; + break; + case ARV_PIXEL_FORMAT_BAYER_RG_12: + img_buffer_bit_depth = 12; + img_buffer_bytes_per_pixel = 2; + img_buffer_number_components = 1; + pixel_type = "12bit mono"; + break; + case ARV_PIXEL_FORMAT_BAYER_RG_16: + img_buffer_bit_depth = 16; + img_buffer_bytes_per_pixel = 2; + img_buffer_number_components = 1; + pixel_type = "16bit mono"; + break; + + case ARV_PIXEL_FORMAT_RGB_8_PACKED: + img_buffer_bit_depth = 8; + img_buffer_bytes_per_pixel = 4; + img_buffer_number_components = 4; + pixel_type = "8bitRGB"; + break; + case ARV_PIXEL_FORMAT_BGR_8_PACKED: + img_buffer_bit_depth = 8; + img_buffer_bytes_per_pixel = 4; + img_buffer_number_components = 4; + pixel_type = "8bitBGR"; + break; + + default: + printf ("Aravis Error: Pixel Format %d is not implemented\n", (int)arvPixelFormat); + break; + } +} + + +int AravisCamera::ArvStartSequenceAcquisition() +{ + int i; + size_t payload; + GError *gerror = nullptr; + + counter = 0; + + arv_camera_set_acquisition_mode(arv_cam, ARV_ACQUISITION_MODE_CONTINUOUS, &gerror); + if (!ArvCheckError(gerror)){ + arv_stream = arv_camera_create_stream(arv_cam, stream_callback, this, &gerror); + if (ArvCheckError(gerror)){ + return 1; + } + } + else{ + return 1; + } + + if (ARV_IS_STREAM(arv_stream)){ + payload = arv_camera_get_payload(arv_cam, &gerror); + if (!ArvCheckError(gerror)){ + for (i = 0; i < 20; i++) + arv_stream_push_buffer(arv_stream, arv_buffer_new(payload, NULL)); + } + arv_camera_start_acquisition(arv_cam, &gerror); + if (ArvCheckError(gerror)){ + return 1; + } + } + else{ + return 1; + } + capturing = true; + return 0; +} + + +int AravisCamera::ClearROI() +{ + gint h,tmp,w; + GError *gerror = nullptr; + + arv_camera_set_region(arv_cam, 0, 0, 64, 64, &gerror); + ArvCheckError(gerror); + + arv_camera_get_height_bounds(arv_cam, &tmp, &h, &gerror); + ArvCheckError(gerror); + + arv_camera_get_width_bounds(arv_cam, &tmp, &w, &gerror); + ArvCheckError(gerror); + + arv_camera_set_region(arv_cam, 0, 0, w, h, &gerror); + ArvCheckError(gerror); + + return DEVICE_OK; +} + + +int AravisCamera::GetBinning() const +{ + gint dx; + gint dy; + GError *gerror = nullptr; + + arv_camera_get_binning(arv_cam, &dx, &dy, &gerror); + ArvCheckError(gerror); + + // dx is always dy for MM? Add check? + return (int)dx; +} + + +unsigned AravisCamera::GetBitDepth() const +{ + return img_buffer_bit_depth; +} + + +double AravisCamera::GetExposure() const +{ + return exposure_time; +} + + +const unsigned char* AravisCamera::GetImageBuffer() +{ + int status; + size_t arv_size, size; + gboolean chunks; + unsigned char *arv_buffer_data; + + if (ARV_IS_BUFFER (arv_buffer)) { + ArvBufferUpdate(arv_buffer); + g_clear_object(&arv_buffer); + SetProperty(MM::g_Keyword_PixelType, pixel_type); + return img_buffer; + } + return NULL; +} + + +long AravisCamera::GetImageBufferSize() const +{ + return img_buffer_number_pixels * img_buffer_bytes_per_pixel; +} + + +unsigned AravisCamera::GetImageBytesPerPixel() const +{ + return img_buffer_bytes_per_pixel; +} + + +unsigned AravisCamera::GetImageWidth() const +{ + return (unsigned)img_buffer_width; +} + + +unsigned AravisCamera::GetImageHeight() const +{ + return (unsigned)img_buffer_height; +} + + +void AravisCamera::GetName(char *name) const +{ + CDeviceUtils::CopyLimitedString(name, arv_cam_name); +} + + +unsigned AravisCamera::GetNumberOfComponents() const +{ + return img_buffer_number_components; +} + + +int AravisCamera::GetROI(unsigned& x, unsigned& y, unsigned& xSize, unsigned& ySize) +{ + gint gx,gy,gwidth,gheight; + GError *gerror = nullptr; + + arv_camera_get_region(arv_cam, &gx, &gy, &gwidth, &gheight, &gerror); + ArvCheckError(gerror); + + x = (unsigned)gx; + y = (unsigned)gx; + xSize = (unsigned)xSize; + ySize = (unsigned)ySize; + + return DEVICE_OK; +} + + +int AravisCamera::Initialize() +{ + int i,ret; + gint tmp; + GError *gerror = nullptr; + + if(initialized){ + return DEVICE_OK; + } + + arv_cam = arv_camera_new(arv_cam_name, &gerror); + if (ArvCheckError(gerror)) return ARV_ERROR; + + arv_device = arv_camera_get_device(arv_cam); + + // Clear ROI settings that may still be present from a previous session. + ClearROI(); + + // Get starting image size. + gint h,w; + arv_camera_get_height_bounds(arv_cam, &tmp, &h, &gerror); + ArvCheckError(gerror); + + arv_camera_get_width_bounds(arv_cam, &tmp, &w, &gerror); + ArvCheckError(gerror); + + img_buffer_height = (int)h; + img_buffer_width = (int)w; + + // Set image properties based on current pixel type. + guint32 arvPixelFormat; + arvPixelFormat = arv_camera_get_pixel_format(arv_cam, &gerror); + ArvCheckError(gerror); + ArvPixelFormatUpdate(arvPixelFormat); + + // Turn off auto exposure. + arv_camera_set_exposure_time_auto(arv_cam, ARV_AUTO_OFF, &gerror); + ArvCheckError(gerror); + + // Get current exposure time. + ArvGetExposure(); + + // Pixel formats. + // FIXME: Camera might start with a format that is not supported. + const char *pixel_format; + pixel_format = arv_camera_get_pixel_format_as_string (arv_cam, &gerror); + ArvCheckError(gerror); + + CPropertyAction* pAct = new CPropertyAction(this, &AravisCamera::OnPixelType); + ret = CreateProperty(MM::g_Keyword_PixelType, pixel_format, MM::String, false, pAct); + assert(ret == DEVICE_OK); + + guint nPixelFormats; + std::vector pixelTypeValues; + const char **pixelFormats; + + pixelFormats = arv_camera_dup_available_pixel_formats_as_strings(arv_cam, &nPixelFormats, &gerror); + ArvCheckError(gerror); + for(i=0;i autoGainValues = {"AUTO_OFF", "AUTO_ONCE", "AUTO_CONTINUOUS"}; + SetAllowedValues("GainAuto", autoGainValues); + } + + // Gain. + gboolean hasGain; + hasGain = arv_camera_is_gain_available(arv_cam, &gerror); + ArvCheckError(gerror); + + if (hasGain){ + double gmin,gmax; + + arv_camera_get_gain_bounds(arv_cam, &gmin, &gmax, &gerror); + ArvCheckError(gerror); + + pAct = new CPropertyAction(this, &AravisCamera::OnGain); + ret = CreateProperty(MM::g_Keyword_Gain, "1.0", MM::Float, false, pAct); + SetPropertyLimits(MM::g_Keyword_Gain, gmin, gmax); + } + + // Auto black level. + gboolean hasAutoBlackLevel; + hasAutoBlackLevel = arv_camera_is_black_level_auto_available(arv_cam, &gerror); + ArvCheckError(gerror); + + if (hasAutoBlackLevel){ + pAct = new CPropertyAction(this, &AravisCamera::OnAutoBlackLevel); + ret = CreateProperty("BlackLevelAuto", "NA", MM::String, false, pAct); + assert(ret == DEVICE_OK); + std::vector autoBlackLevelValues = {"AUTO_OFF", "AUTO_ONCE", "AUTO_CONTINUOUS"}; + SetAllowedValues("BlackLevelAuto", autoBlackLevelValues); + } + + // Black level. + gboolean hasBlackLevel; + hasBlackLevel = arv_camera_is_black_level_available(arv_cam, &gerror); + ArvCheckError(gerror); + + if (hasBlackLevel){ + double bmin,bmax; + + arv_camera_get_black_level_bounds(arv_cam, &bmin, &bmax, &gerror); + ArvCheckError(gerror); + + pAct = new CPropertyAction(this, &AravisCamera::OnBlackLevel); + ret = CreateProperty(MM::g_Keyword_Offset, "1.0", MM::Float, false, pAct); + assert(ret == DEVICE_OK); + SetPropertyLimits(MM::g_Keyword_Offset, bmin, bmax); + } + + // Gamma. + // + // Check by getting the feature because if "GammaEnable" is turned off the + // feature won't appear as available with arv_device_is_feature_avaialable(). + // + ArvGcNode *hasGamma; + hasGamma = arv_device_get_feature(arv_device, "Gamma"); + if (hasGamma != NULL){ + double gmin,gmax; + + arv_device_get_float_feature_bounds(arv_device, "Gamma", &gmin, &gmax, &gerror); + ArvCheckError(gerror); + + pAct = new CPropertyAction(this, &AravisCamera::OnGamma); + ret = CreateProperty("Gamma", "1.0", MM::Float, false, pAct); + assert(ret == DEVICE_OK); + SetPropertyLimits("Gamma", gmin, gmax); + } + + // Gamma enable. + gboolean hasGammaEnable; + hasGammaEnable = arv_device_is_feature_available(arv_device, "GammaEnable", &gerror); + ArvCheckError(gerror); + + if (hasGammaEnable){ + pAct = new CPropertyAction(this, &AravisCamera::OnGammaEnable); + ret = CreateProperty("GammaEnable", "0", MM::String, false, pAct); + assert(ret == DEVICE_OK); + std::vector gammaEnableValues = {"0", "1"}; + SetAllowedValues("GammaEnable", gammaEnableValues); + } + + // Trigger mode. + guint nTriggerModes = 0; + const char **triggerModes; + triggerModes = arv_device_dup_available_enumeration_feature_values_as_strings(arv_device, "TriggerMode", &nTriggerModes, &gerror); + ArvCheckError(gerror); + + if (nTriggerModes > 1){ + CPropertyAction* pAct = new CPropertyAction(this, &AravisCamera::OnTriggerMode); + ret = CreateProperty("TriggerMode", "NA", MM::String, false, pAct); + assert(ret == DEVICE_OK); + + std::vector triggerModeValues; + for(i=0;i 1){ + CPropertyAction* pAct = new CPropertyAction(this, &AravisCamera::OnTriggerSelector); + ret = CreateProperty("TriggerSelector", "NA", MM::String, false, pAct); + assert(ret == DEVICE_OK); + + std::vector triggerSelectorValues; + for(i=0;i 1){ + CPropertyAction* pAct = new CPropertyAction(this, &AravisCamera::OnTriggerSource); + ret = CreateProperty("TriggerSource", "NA", MM::String, false, pAct); + assert(ret == DEVICE_OK); + + std::vector triggerSourceValues; + for(i=0;iGet(autoBlackLevelMode); + + if (!autoBlackLevelMode.compare("AUTO_OFF")){ + arv_camera_set_black_level_auto(arv_cam, ARV_AUTO_OFF, &gerror); + } + else if (!autoBlackLevelMode.compare("AUTO_ONCE")){ + arv_camera_set_black_level_auto(arv_cam, ARV_AUTO_ONCE, &gerror); + } + else if (!autoBlackLevelMode.compare("AUTO_CONTINUOUS")){ + arv_camera_set_black_level_auto(arv_cam, ARV_AUTO_CONTINUOUS, &gerror); + } + else{ + printf("Unrecognized auto black level mode %s", autoBlackLevelMode.c_str()); + } + ArvCheckError(gerror); + } + } + else if (eAct == MM::BeforeGet) { + int mode; + mode = arv_camera_get_black_level_auto(arv_cam, &gerror); + ArvCheckError(gerror); + + if (mode == ARV_AUTO_OFF){ + pProp->Set("AUTO_OFF"); + } + else if (mode == ARV_AUTO_ONCE){ + pProp->Set("AUTO_ONCE"); + } + else if (mode == ARV_AUTO_CONTINUOUS){ + pProp->Set("AUTO_CONTINUOUS"); + } + } + + return DEVICE_OK; +} + + +int AravisCamera::OnAutoGain(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + GError *gerror = nullptr; + + if (eAct == MM::AfterSet){ + if (!capturing){ + std::string autoGainMode; + pProp->Get(autoGainMode); + + if (!autoGainMode.compare("AUTO_OFF")){ + arv_camera_set_gain_auto(arv_cam, ARV_AUTO_OFF, &gerror); + } + else if (!autoGainMode.compare("AUTO_ONCE")){ + arv_camera_set_gain_auto(arv_cam, ARV_AUTO_ONCE, &gerror); + } + else if (!autoGainMode.compare("AUTO_CONTINUOUS")){ + arv_camera_set_gain_auto(arv_cam, ARV_AUTO_CONTINUOUS, &gerror); + } + else{ + printf("Unrecognized auto gain mode %s", autoGainMode.c_str()); + } + ArvCheckError(gerror); + } + } + else if (eAct == MM::BeforeGet) { + int mode; + mode = arv_camera_get_gain_auto(arv_cam, &gerror); + ArvCheckError(gerror); + + if (mode == ARV_AUTO_OFF){ + pProp->Set("AUTO_OFF"); + } + else if (mode == ARV_AUTO_ONCE){ + pProp->Set("AUTO_ONCE"); + } + else if (mode == ARV_AUTO_CONTINUOUS){ + pProp->Set("AUTO_CONTINUOUS"); + } + } + + return DEVICE_OK; +} + + +int AravisCamera::OnBinning(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + gint bx,by; + std::string binning; + GError *gerror = nullptr; + + if (eAct == MM::AfterSet){ + if (!capturing){ + pProp->Get(binning); + bx = std::stoi(binning); + + arv_camera_set_binning(arv_cam, bx, bx, &gerror); + ArvCheckError(gerror); + + // This restores the image size when we decrease the binning. + ClearROI(); + } + } + else if (eAct == MM::BeforeGet) { + arv_camera_get_binning(arv_cam, &bx, &by, &gerror); + ArvCheckError(gerror); + + std::string bxs = std::to_string(bx); + pProp->Set(bxs.c_str()); + } + + return DEVICE_OK; +} + + +int AravisCamera::OnBlackLevel(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + double blackLevel; + GError *gerror = nullptr; + + if (eAct == MM::AfterSet){ + int mode; + mode = arv_camera_get_black_level_auto(arv_cam, &gerror); + ArvCheckError(gerror); + + if (mode == ARV_AUTO_OFF){ + pProp->Get(blackLevel); + arv_camera_set_black_level(arv_cam, blackLevel, &gerror); + ArvCheckError(gerror); + } + } + else if (eAct == MM::BeforeGet){ + blackLevel = arv_camera_get_black_level(arv_cam, &gerror); + ArvCheckError(gerror); + + pProp->Set(blackLevel); + } + return DEVICE_OK; +} + + +int AravisCamera::OnGain(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + double gain; + GError *gerror = nullptr; + + if (eAct == MM::AfterSet){ + int mode; + mode = arv_camera_get_gain_auto(arv_cam, &gerror); + ArvCheckError(gerror); + + if (mode == ARV_AUTO_OFF){ + pProp->Get(gain); + arv_camera_set_gain(arv_cam, gain, &gerror); + ArvCheckError(gerror); + } + } + else if (eAct == MM::BeforeGet) { + gain = arv_camera_get_gain(arv_cam, &gerror); + ArvCheckError(gerror); + + pProp->Set(gain); + } + return DEVICE_OK; +} + + +int AravisCamera::OnGamma(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + double gamma; + GError *gerror = nullptr; + + if (eAct == MM::AfterSet){ + pProp->Get(gamma); + arv_device_set_float_feature_value(arv_device, "Gamma", gamma, &gerror); + ArvCheckError(gerror); + } + else if (eAct == MM::BeforeGet){ + gamma = arv_device_get_float_feature_value(arv_device, "Gamma", &gerror); + ArvCheckError(gerror); + pProp->Set(gamma); + } + return DEVICE_OK; +} + + +int AravisCamera::OnGammaEnable(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + gboolean ge; + std::string gammaEnable; + GError *gerror = nullptr; + + if (eAct == MM::AfterSet){ + pProp->Get(gammaEnable); + ge = std::stoi(gammaEnable); + arv_device_set_boolean_feature_value(arv_device, "GammaEnable", ge, &gerror); + ArvCheckError(gerror); + } + else if (eAct == MM::BeforeGet){ + ge = arv_device_get_boolean_feature_value(arv_device, "GammaEnable", &gerror); + ArvCheckError(gerror); + gammaEnable = std::to_string(ge); + pProp->Set(gammaEnable.c_str()); + } + return DEVICE_OK; +} + + +int AravisCamera::OnPixelType(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + GError *gerror = nullptr; + + if (eAct == MM::AfterSet){ + if (!capturing){ + guint32 arvPixelFormat; + std::string pixelType; + pProp->Get(pixelType); + + arv_camera_set_pixel_format_from_string(arv_cam, pixelType.c_str(), &gerror); + ArvCheckError(gerror); + + arvPixelFormat = arv_camera_get_pixel_format(arv_cam, &gerror); + ArvCheckError(gerror); + ArvPixelFormatUpdate(arvPixelFormat); + } + } + else if (eAct == MM::BeforeGet) { + const char *pixelFormat; + pixelFormat = arv_camera_get_pixel_format_as_string(arv_cam, &gerror); + ArvCheckError(gerror); + + pProp->Set(pixelFormat); + } + + return DEVICE_OK; +} + + +int AravisCamera::OnTriggerMode(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + GError *gerror = nullptr; + + if (eAct == MM::AfterSet){ + if (!capturing){ + std::string mode; + pProp->Get(mode); + + arv_device_set_string_feature_value(arv_device, "TriggerMode", mode.c_str(), &gerror); + ArvCheckError(gerror); + } + } + else if (eAct == MM::BeforeGet) { + const char *mode; + mode = arv_device_get_string_feature_value(arv_device, "TriggerMode", &gerror); + ArvCheckError(gerror); + + pProp->Set(mode); + } + + return DEVICE_OK; +} + + +int AravisCamera::OnTriggerSelector(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + GError *gerror = nullptr; + + if (eAct == MM::AfterSet){ + if (!capturing){ + std::string trigger; + pProp->Get(trigger); + + arv_device_set_string_feature_value(arv_device, "TriggerSelector", trigger.c_str(), &gerror); + //arv_camera_set_trigger(arv_cam, trigger.c_str(), &gerror); + ArvCheckError(gerror); + } + } + else if (eAct == MM::BeforeGet) { + const char *trigger; + trigger = arv_device_get_string_feature_value(arv_device, "TriggerSelector", &gerror); + ArvCheckError(gerror); + + pProp->Set(trigger); + } + + return DEVICE_OK; +} + + +int AravisCamera::OnTriggerSource(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + GError *gerror = nullptr; + + if (eAct == MM::AfterSet){ + if (!capturing){ + std::string triggerSource; + pProp->Get(triggerSource); + + arv_camera_set_trigger_source(arv_cam, triggerSource.c_str(), &gerror); + ArvCheckError(gerror); + } + } + else if (eAct == MM::BeforeGet) { + const char *triggerSource; + triggerSource = arv_camera_get_trigger_source(arv_cam, &gerror); + ArvCheckError(gerror); + + pProp->Set(triggerSource); + } + + return DEVICE_OK; +} + + +int AravisCamera::PrepareSequenceAcqusition() +{ + return DEVICE_OK; +} + + +int AravisCamera::SetBinning(int binSize) +{ + GError *gerror = nullptr; + + arv_camera_set_binning(arv_cam, (gint)binSize, (gint)binSize, &gerror); + ArvCheckError(gerror); + + return DEVICE_OK; +} + + +void AravisCamera::SetExposure(double expMs) +{ + double expUs = 1000.0*expMs; + double min, max; + GError *gerror = nullptr; + + arv_camera_get_exposure_time_bounds(arv_cam, &min, &max, &gerror); + ArvCheckError(gerror); + + if (expUs < min){ expUs = min; } + if (expUs > max){ expUs = max; } + + arv_camera_set_exposure_time(arv_cam, expUs, &gerror); + ArvCheckError(gerror); + + // This always returns the same bounds, independent of exposure time.. + arv_camera_get_frame_rate_bounds(arv_cam, &min, &max, &gerror); + ArvCheckError(gerror); + + // arv_camera_set_frame_rate(arv_cam, max, &gerror); + // This is supposed to disable the frame rate, which will then + // presumably be set by the exposure time. + arv_camera_set_frame_rate(arv_cam, -1.0, &gerror); + ArvCheckError(gerror); + + ArvGetExposure(); +} + + +int AravisCamera::SetROI(unsigned x, unsigned y, unsigned xSize, unsigned ySize) +{ + gint inc, ix, iy, ixs, iys; + GError *gerror = nullptr; + + inc = arv_camera_get_x_offset_increment(arv_cam, &gerror); + ArvCheckError(gerror); + ix = ((gint)x/inc)*inc; + + inc = arv_camera_get_y_offset_increment(arv_cam, &gerror); + ArvCheckError(gerror); + iy = ((gint)y/inc)*inc; + + inc = arv_camera_get_width_increment(arv_cam, &gerror); + ArvCheckError(gerror); + ixs = ((gint)xSize/inc)*inc; + + inc = arv_camera_get_height_increment(arv_cam, &gerror); + ArvCheckError(gerror); + iys = ((gint)ySize/inc)*inc; + + arv_camera_set_region(arv_cam, ix, iy, ixs, iys, &gerror); + ArvCheckError(gerror); + + return DEVICE_OK; +} + + +int AravisCamera::Shutdown() +{ + return DEVICE_OK; +} + + +// This should wait until the image is acquired? Maybe it does? +int AravisCamera::SnapImage() +{ + GError *gerror = nullptr; + + arv_buffer = arv_camera_acquisition(arv_cam, 0, &gerror); + if (ArvCheckError(gerror)) return ARV_ERROR; + + return DEVICE_OK; +} + + +int AravisCamera::StartSequenceAcquisition(long numImages, double interval_ms, bool stopOnOverflow) +{ + if (!ArvStartSequenceAcquisition()){ + int ret = GetCoreCallback()->PrepareForAcq(this); + if (ret != DEVICE_OK) { + return ret; + } + return DEVICE_OK; + } + return ARV_ERROR; +} + + +int AravisCamera::StartSequenceAcquisition(double interval_ms) { + if (!ArvStartSequenceAcquisition()){ + int ret = GetCoreCallback()->PrepareForAcq(this); + if (ret != DEVICE_OK) { + return ret; + } + return DEVICE_OK; + } + return ARV_ERROR; +} + + +int AravisCamera::StopSequenceAcquisition() +{ + GError *gerror = nullptr; + + if (capturing){ + capturing = false; + arv_camera_stop_acquisition(arv_cam, &gerror); + ArvCheckError(gerror); + g_clear_object(&arv_stream); + + GetCoreCallback()->AcqFinished(this, 0); + } + return DEVICE_OK; +} diff --git a/DeviceAdapters/Aravis/AravisCamera.h b/DeviceAdapters/Aravis/AravisCamera.h new file mode 100644 index 000000000..5f62e9d9f --- /dev/null +++ b/DeviceAdapters/Aravis/AravisCamera.h @@ -0,0 +1,131 @@ +/* + Copyright 2024 Hazen Babcock + + 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. +*/ + +#ifndef _ARAVIS_CAMERA_H_ +#define _ARAVIS_CAMERA_H_ + +/* +#include +#include +#include +*/ +#include "DeviceBase.h" +#include "arv.h" +#include "glib.h" + + +#define ARV_ERROR 3141 // Should this be something specific? + + +class AravisAcquisitionThread; + + +class AravisCamera : public CCameraBase +{ +public: + AravisCamera(const char *); + ~AravisCamera(); + + // MMDevice API. + void GetName(char* name) const; + int Initialize(); + int Shutdown(); + + // MMCamera API. + int ClearROI(); + int GetBinning() const; + unsigned GetBitDepth() const; + double GetExposure() const; + const unsigned char* GetImageBuffer(); + long GetImageBufferSize() const; + unsigned GetImageBytesPerPixel() const; + unsigned GetImageWidth() const; + unsigned GetImageHeight() const; + unsigned GetNumberOfComponents() const; + int GetROI(unsigned& x, unsigned& y, unsigned& xSize, unsigned& ySize); + int IsExposureSequenceable(bool& isSequenceable) const; + int SetBinning(int binSize); + void SetExposure(double exp); + int SetROI(unsigned x, unsigned y, unsigned xSize, unsigned ySize); + int SnapImage(); + + // Continuous acquisition. + bool IsCapturing(); + int PrepareSequenceAcqusition(); + int StartSequenceAcquisition(long numImages, double interval_ms, bool stopOnOverflow); + int StartSequenceAcquisition(double interval_ms); + int StopSequenceAcquisition(); + + // Properties. + int OnAutoBlackLevel(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnAutoGain(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnBinning(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnBlackLevel(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnGain(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnGamma(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnGammaEnable(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnPixelType(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnTriggerMode(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnTriggerSelector(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnTriggerSource(MM::PropertyBase* pProp, MM::ActionType eAct); + + // Internal. + void AcquisitionCallback(ArvStreamCallbackType, ArvBuffer *); + void ArvBufferUpdate(ArvBuffer *aBuffer); + int ArvCheckError(GError *gerror) const; + void ArvGetExposure(); + void ArvPixelFormatUpdate(guint32 arvPixelFormat); + int ArvStartSequenceAcquisition(); + + +private: + bool capturing; + long counter; + double exposure_time; + unsigned img_buffer_bit_depth; + int img_buffer_bytes_per_pixel; + int img_buffer_height; + unsigned img_buffer_number_components; + size_t img_buffer_number_pixels; + size_t img_buffer_size; + int img_buffer_width; + bool initialized; + + ArvBuffer *arv_buffer; + ArvCamera *arv_cam; + char *arv_cam_name; + ArvDevice *arv_device; + ArvStream *arv_stream; + unsigned char *img_buffer; + const char *pixel_type; + const char *trigger; +}; + +#endif // !_ARAVIS_CAMERA_H_ + diff --git a/DeviceAdapters/Aravis/Makefile.am b/DeviceAdapters/Aravis/Makefile.am new file mode 100644 index 000000000..e722b1372 --- /dev/null +++ b/DeviceAdapters/Aravis/Makefile.am @@ -0,0 +1,14 @@ + +AM_CXXFLAGS=$(MMDEVAPI_CXXFLAGS) + +# Linux, use output from from pkg-config (aravis-0.10). +ARAVISCPPFLAGS = -I/usr/local/include/aravis-0.10 -I/usr/include/libxml2 -I/usr/include/libusb-1.0 -I/usr/include/libmount -I/usr/include/blkid -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include +ARAVISLDFLAGS = -Wl,--enable-new-dtags -Wl,-rpath,/usr/local/lib/x86_64-linux-gnu,-L/usr/local/lib/x86_64-linux-gnu +ARAVISLDLIBS = -laravis-0.10 -lgio-2.0 -lgobject-2.0 -lglib-2.0 + +deviceadapter_LTLIBRARIES=libmmgr_dal_AravisCamera.la +libmmgr_dal_AravisCamera_la_SOURCES=AravisCamera.cpp AravisCamera.h +libmmgr_dal_AravisCamera_la_CPPFLAGS=$(ARAVISCPPFLAGS) +libmmgr_dal_AravisCamera_la_LIBADD=$(MMDEVAPI_LIBADD) $(ARAVISLDLIBS) +libmmgr_dal_AravisCamera_la_LDFLAGS=$(MMDEVAPI_LDFLAGS) $(ARAVISLDLIBS) $(ARAVISLDFLAGS) + diff --git a/DeviceAdapters/Aravis/README.md b/DeviceAdapters/Aravis/README.md new file mode 100644 index 000000000..c12088a24 --- /dev/null +++ b/DeviceAdapters/Aravis/README.md @@ -0,0 +1,7 @@ +## About + +These device adapters add support for the [Aravis Project](https://github.com/AravisProject). The adapters have been developed and tested on 64-bit Linux with Aravis-0.10. + +### Note + +For the cameras used to test this driver there were two choices for the same camera at hardware configuration, and only one of the two choices worked as expected. \ No newline at end of file diff --git a/DeviceAdapters/Arduino/AOTFcontroller/AOTFcontroller.ino b/DeviceAdapters/Arduino/AOTFcontroller/AOTFcontroller.ino index 013ae9749..a85418b4c 100644 --- a/DeviceAdapters/Arduino/AOTFcontroller/AOTFcontroller.ino +++ b/DeviceAdapters/Arduino/AOTFcontroller/AOTFcontroller.ino @@ -26,7 +26,7 @@ * * * Set digital patten for triggered mode: 5xd - * Where x is the number of the pattern (currently, 12 patterns can be stored). + * Where x is the number of the pattern (Quesry the max number of patterns with 32, default is 12, but number can be changed in the firmware). * and d is the digital pattern to be stored at that position. Note that x should * be the real number (i.e., not ASCI encoded) * Controller will return 5xd @@ -90,6 +90,11 @@ * Get Version: 31 * Returns: version number (as ASCI string) \r\n * + * Get Max number of patterns that can be uploaded: 32 + * Returns: Max number of patterns as an unsigned int, 2 bytes, highbyte first + * Available as of version 3 + * + * * Read digital state of analogue input pins 0-5: 40 * Returns raw value of PINC (two high bits are not used) * @@ -104,7 +109,7 @@ * Get Number of digital patterns */ - unsigned int version_ = 2; + unsigned int version_ = 3; // pin on which to receive the trigger (2 and 3 can be used with interrupts, although this code does not use interrupts) int inPin_ = 2; @@ -118,9 +123,9 @@ // pin connected to CS of TLV5618 int latchPin = 5; - const int SEQUENCELENGTH = 12; // this should be good enough for everybody;) - byte triggerPattern_[SEQUENCELENGTH] = {0,0,0,0,0,0,0,0,0,0,0,0}; - unsigned int triggerDelay_[SEQUENCELENGTH] = {0,0,0,0,0,0,0,0,0,0,0,0}; + const uint16_t SEQUENCELENGTH = 256; // Can be increased, but pay attention that there is significant memory left for local variables + byte triggerPattern_[SEQUENCELENGTH]; + unsigned int triggerDelay_[SEQUENCELENGTH]; int patternLength_ = 0; byte repeatPattern_ = 0; volatile long triggerNr_; // total # of triggers in this run (0-based) @@ -154,6 +159,11 @@ PORTC = PORTC | B00111111; digitalWrite(latchPin, HIGH); + + for (unsigned int i = 0; i < SEQUENCELENGTH; i++) { + triggerPattern_[i] = 0; + triggerDelay_[i] = 0; + } } void loop() { @@ -337,6 +347,13 @@ Serial.println(version_); break; + // returns Maximum number of patterns for sequencing + case 32: + Serial.write( byte(32)); + Serial.write(highByte(SEQUENCELENGTH)); + Serial.write(lowByte(SEQUENCELENGTH)); + break; + case 40: Serial.write( byte(40)); Serial.write( PINC); diff --git a/DeviceAdapters/Arduino/Arduino.cpp b/DeviceAdapters/Arduino/Arduino.cpp index 57d10fb35..05f9aa582 100644 --- a/DeviceAdapters/Arduino/Arduino.cpp +++ b/DeviceAdapters/Arduino/Arduino.cpp @@ -28,11 +28,12 @@ const char* g_DeviceNameArduinoShutter = "Arduino-Shutter"; const char* g_DeviceNameArduinoDA1 = "Arduino-DAC1"; const char* g_DeviceNameArduinoDA2 = "Arduino-DAC2"; const char* g_DeviceNameArduinoInput = "Arduino-Input"; +const char* g_DeviceNameArduinoMagnifier = "Arduino-Magnifier"; // Global info about the state of the Arduino. This should be folded into a class const int g_Min_MMVersion = 1; -const int g_Max_MMVersion = 2; +const int g_Max_MMVersion = 3; const char* g_versionProp = "Version"; const char* g_normalLogicString = "Normal"; const char* g_invertedLogicString = "Inverted"; @@ -40,8 +41,6 @@ const char* g_invertedLogicString = "Inverted"; const char* g_On = "On"; const char* g_Off = "Off"; -// static lock -MMThreadLock CArduinoHub::lock_; /////////////////////////////////////////////////////////////////////////////// // Exported MMDevice API @@ -54,6 +53,7 @@ MODULE_API void InitializeModuleData() RegisterDevice(g_DeviceNameArduinoDA1, MM::SignalIODevice, "DAC channel 1"); RegisterDevice(g_DeviceNameArduinoDA2, MM::SignalIODevice, "DAC channel 2"); RegisterDevice(g_DeviceNameArduinoInput, MM::GenericDevice, "ADC"); + RegisterDevice(g_DeviceNameArduinoMagnifier, MM::MagnifierDevice, "Magnifier(needs ADC)"); } MODULE_API MM::Device* CreateDevice(const char* deviceName) @@ -85,6 +85,10 @@ MODULE_API MM::Device* CreateDevice(const char* deviceName) { return new CArduinoInput; } + else if (strcmp(deviceName, g_DeviceNameArduinoMagnifier) == 0) + { + return new CArduinoMagnifier; + } return 0; } @@ -100,6 +104,9 @@ MODULE_API void DeleteDevice(MM::Device* pDevice) // CArduinoHub::CArduinoHub() : initialized_ (false), + maxNumPatterns_(12), + version_(0), + magnifier_(0), switchState_ (0), shutterState_ (0) { @@ -127,21 +134,25 @@ CArduinoHub::CArduinoHub() : AddAllowedValue("Logic", g_normalLogicString); } + CArduinoHub::~CArduinoHub() { Shutdown(); } + void CArduinoHub::GetName(char* name) const { CDeviceUtils::CopyLimitedString(name, g_DeviceNameArduinoHub); } + bool CArduinoHub::Busy() { return false; } + // private and expects caller to: // 1. guard the port // 2. purge the port @@ -182,11 +193,13 @@ int CArduinoHub::GetControllerVersion(int& version) } + bool CArduinoHub::SupportsDeviceDetection(void) { return true; } + MM::DeviceDetectionStatus CArduinoHub::DetectDevice(void) { if (initialized_) @@ -221,7 +234,7 @@ MM::DeviceDetectionStatus CArduinoHub::DetectDevice(void) pS->Initialize(); // The first second or so after opening the serial port, the Arduino is waiting for firmwareupgrades. Simply sleep 2 seconds. CDeviceUtils::SleepMs(2000); - MMThreadGuard myLock(lock_); + const std::lock_guard lock(mutex_); PurgeComPort(port_.c_str()); int v = 0; int ret = GetControllerVersion(v); @@ -260,7 +273,7 @@ int CArduinoHub::Initialize() // The first second or so after opening the serial port, the Arduino is waiting for firmwareupgrades. Simply sleep 1 second. CDeviceUtils::SleepMs(2000); - MMThreadGuard myLock(lock_); + const std::lock_guard lock(mutex_); // Check that we have a controller: PurgeComPort(port_.c_str()); @@ -276,6 +289,36 @@ int CArduinoHub::Initialize() sversion << version_; CreateProperty(g_versionProp, sversion.str().c_str(), MM::Integer, true, pAct); + if (version_ >= 3) + { + unsigned char command[1]; + command[0] = 32; + + ret = WriteToComPortH((const unsigned char*) command, 1); + if (ret != DEVICE_OK) + return ret; + + MM::MMTime startTime = GetCurrentMMTime(); + const unsigned int nrBytes = 3; + unsigned long bytesRead = 0; + unsigned char answer[nrBytes] = { 0, 0, 0}; + while ((bytesRead < nrBytes) && ( (GetCurrentMMTime() - startTime).getMsec() < 250)) { + unsigned long br; + ret = ReadFromComPortH(answer + bytesRead, nrBytes - bytesRead, br); + if (ret != DEVICE_OK) + return ret; + bytesRead += br; + } + + if (answer[0] != 32) + return ERR_COMMUNICATION; + + unsigned int tmp = answer[1]; + tmp = tmp << 8; + tmp = tmp | answer[2]; + maxNumPatterns_ = tmp; + } + ret = UpdateStatus(); if (ret != DEVICE_OK) return ret; @@ -287,6 +330,7 @@ int CArduinoHub::Initialize() return DEVICE_OK; } + int CArduinoHub::DetectInstalledDevices() { if (MM::CanCommunicate == DetectDevice()) @@ -296,6 +340,7 @@ int CArduinoHub::DetectInstalledDevices() peripherals.push_back(g_DeviceNameArduinoSwitch); peripherals.push_back(g_DeviceNameArduinoShutter); peripherals.push_back(g_DeviceNameArduinoInput); + peripherals.push_back(g_DeviceNameArduinoMagnifier); peripherals.push_back(g_DeviceNameArduinoDA1); peripherals.push_back(g_DeviceNameArduinoDA2); for (size_t i=0; i < peripherals.size(); i++) @@ -319,6 +364,7 @@ int CArduinoHub::Shutdown() return DEVICE_OK; } + int CArduinoHub::OnPort(MM::PropertyBase* pProp, MM::ActionType pAct) { if (pAct == MM::BeforeGet) @@ -333,6 +379,7 @@ int CArduinoHub::OnPort(MM::PropertyBase* pProp, MM::ActionType pAct) return DEVICE_OK; } + int CArduinoHub::OnVersion(MM::PropertyBase* pProp, MM::ActionType pAct) { if (pAct == MM::BeforeGet) @@ -342,6 +389,7 @@ int CArduinoHub::OnVersion(MM::PropertyBase* pProp, MM::ActionType pAct) return DEVICE_OK; } + int CArduinoHub::OnLogic(MM::PropertyBase* pProp, MM::ActionType pAct) { if (pAct == MM::BeforeGet) @@ -361,11 +409,31 @@ int CArduinoHub::OnLogic(MM::PropertyBase* pProp, MM::ActionType pAct) return DEVICE_OK; } + +int CArduinoHub::ReportNewInputState(long newState) +{ + if (magnifier_ != 0) + { + magnifier_->UpdateState(newState); + } + return DEVICE_OK; +} + + +int CArduinoHub::RegisterMagnifier(CArduinoMagnifier* magnifier) +{ + magnifier_ = magnifier; + return DEVICE_OK; +} + + /////////////////////////////////////////////////////////////////////////////// // CArduinoSwitch implementation // ~~~~~~~~~~~~~~~~~~~~~~~~~~ CArduinoSwitch::CArduinoSwitch() : + numPatterns_(12), + hub_(0), nrPatternsUsed_(0), currentDelay_(0), sequenceOn_(false), @@ -384,8 +452,6 @@ CArduinoSwitch::CArduinoSwitch() : SetErrorText(ERR_COMMUNICATION, "Error in communication with Arduino board"); SetErrorText(ERR_NO_PORT_SET, "Hub Device not found. The Arduino 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, "Arduino digital output driver", MM::String, true); @@ -413,12 +479,12 @@ void CArduinoSwitch::GetName(char* name) const int CArduinoSwitch::Initialize() { - CArduinoHub* hub = static_cast(GetParentHub()); - if (!hub || !hub->IsPortAvailable()) { + hub_ = static_cast(GetParentHub()); + if (!hub_ || !hub_->IsPortAvailable()) { return ERR_NO_PORT_SET; } char hubLabel[MM::MaxStrLength]; - hub->GetLabel(hubLabel); + hub_->GetLabel(hubLabel); SetParentID(hubLabel); // for backward comp. // set property list @@ -501,6 +567,9 @@ int CArduinoSwitch::Initialize() SetPropertyLimits("Repeat Timed Pattern", 0, 255); */ + // ask the hub for numPatterns_ + numPatterns_ = hub_->GetMaxNumPatterns(); + nRet = UpdateStatus(); if (nRet != DEVICE_OK) return nRet; @@ -510,103 +579,100 @@ int CArduinoSwitch::Initialize() return DEVICE_OK; } + int CArduinoSwitch::Shutdown() { initialized_ = false; return DEVICE_OK; } + int CArduinoSwitch::WriteToPort(long value) { - CArduinoHub* hub = static_cast(GetParentHub()); - if (!hub || !hub->IsPortAvailable()) { + if (!hub_ || !hub_->IsPortAvailable()) { return ERR_NO_PORT_SET; } - MMThreadGuard myLock(hub->GetLock()); + const std::lock_guard lock(hub_->GetLock()); value = 63 & value; - if (hub->IsLogicInverted()) + if (hub_->IsLogicInverted()) value = ~value; - hub->PurgeComPortH(); + hub_->PurgeComPortH(); - unsigned char command[2]; + unsigned char command[2] = { 0, 0 }; command[0] = 1; command[1] = (unsigned char) value; - int ret = hub->WriteToComPortH((const unsigned char*) command, 2); + int ret = hub_->WriteToComPortH((const unsigned char*) command, 2); if (ret != DEVICE_OK) return ret; MM::MMTime startTime = GetCurrentMMTime(); unsigned long bytesRead = 0; - unsigned char answer[1]; + unsigned char answer[1] = { 0 }; while ((bytesRead < 1) && ( (GetCurrentMMTime() - startTime).getMsec() < 250)) { - ret = hub->ReadFromComPortH(answer, 1, bytesRead); + ret = hub_->ReadFromComPortH(answer, 1, bytesRead); if (ret != DEVICE_OK) return ret; } if (answer[0] != 1) return ERR_COMMUNICATION; - hub->SetTimedOutput(false); + hub_->SetTimedOutput(false); return DEVICE_OK; } int CArduinoSwitch::LoadSequence(unsigned size, unsigned char* seq) { - CArduinoHub* hub = static_cast(GetParentHub()); - if (!hub || !hub->IsPortAvailable()) + if (!hub_ || !hub_->IsPortAvailable()) return ERR_NO_PORT_SET; - hub->PurgeComPortH(); + const std::lock_guard lock(hub_->GetLock()); + + hub_->PurgeComPortH(); for (unsigned i=0; i < size; i++) { unsigned char value = seq[i]; value = 63 & value; - if (hub->IsLogicInverted()) + if (hub_->IsLogicInverted()) value = ~value; - unsigned char command[3]; - command[0] = 5; - command[1] = (unsigned char) i; - command[2] = value; - int ret = hub->WriteToComPortH((const unsigned char*) command, 3); + unsigned char command[3] = { 5, (unsigned char)i, value }; + int ret = hub_->WriteToComPortH((const unsigned char*) command, 3); if (ret != DEVICE_OK) return ret; - MM::MMTime startTime = GetCurrentMMTime(); + const unsigned int nrBytes = 3; unsigned long bytesRead = 0; - unsigned char answer[3]; - while ((bytesRead < 3) && ( (GetCurrentMMTime() - startTime).getMsec() < 250)) { + unsigned char answer[nrBytes] = { 0, 0, 0}; + while ((bytesRead < nrBytes) && ( (GetCurrentMMTime() - startTime).getMsec() < 250)) { unsigned long br; - ret = hub->ReadFromComPortH(answer + bytesRead, 3, br); - if (ret != DEVICE_OK) - return ret; - bytesRead += br; + ret = hub_->ReadFromComPortH(answer + bytesRead, nrBytes - bytesRead, br); + if (ret != DEVICE_OK) + return ret; + bytesRead += br; } if (answer[0] != 5) return ERR_COMMUNICATION; - } - unsigned char command[2]; - command[0] = 6; - command[1] = (unsigned char) size; - int ret = hub->WriteToComPortH((const unsigned char*) command, 2); + unsigned char command[2] = { 6, (unsigned char) size }; + int ret = hub_->WriteToComPortH((const unsigned char*) command, 2); if (ret != DEVICE_OK) return ret; MM::MMTime startTime = GetCurrentMMTime(); unsigned long bytesRead = 0; - unsigned char answer[2]; - while ((bytesRead < 2) && ( (GetCurrentMMTime() - startTime).getMsec() < 250)) { + const unsigned int nrBytes = 2; + unsigned char answer[nrBytes] = { 0, 0 };; + while ((bytesRead < nrBytes) && ( (GetCurrentMMTime() - startTime).getMsec() < 250)) { unsigned long br; - ret = hub->ReadFromComPortH(answer + bytesRead, 2, br); + ret = hub_->ReadFromComPortH(answer + bytesRead, nrBytes - bytesRead, br); if (ret != DEVICE_OK) return ret; bytesRead += br; @@ -623,10 +689,6 @@ int CArduinoSwitch::LoadSequence(unsigned size, unsigned char* seq) int CArduinoSwitch::OnState(MM::PropertyBase* pProp, MM::ActionType eAct) { - CArduinoHub* hub = static_cast(GetParentHub()); - if (!hub || !hub->IsPortAvailable()) - return ERR_NO_PORT_SET; - if (eAct == MM::BeforeGet) { // nothing to do, let the caller use cached property @@ -635,31 +697,31 @@ int CArduinoSwitch::OnState(MM::PropertyBase* pProp, MM::ActionType eAct) { long pos; pProp->Get(pos); - hub->SetSwitchState(pos); - if (hub->GetShutterState() > 0) + hub_->SetSwitchState(pos); + if (hub_->GetShutterState() > 0) return WriteToPort(pos); } else if (eAct == MM::IsSequenceable) { if (sequenceOn_) - pProp->SetSequenceable(NUMPATTERNS); + pProp->SetSequenceable(numPatterns_); else pProp->SetSequenceable(0); } else if (eAct == MM::AfterLoadSequence) { std::vector sequence = pProp->GetSequence(); - std::ostringstream os; - if (sequence.size() > NUMPATTERNS) + 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 os (sequence[i]); - int val; - os >> val; - seq[i] = (unsigned char) val; + std::istringstream is (sequence[i]); + unsigned char val; + is >> val; + seq[i] = val; } + // Note: LoadSequence calls hub_->GetLock() int ret = LoadSequence((unsigned) sequence.size(), seq); if (ret != DEVICE_OK) return ret; @@ -668,21 +730,22 @@ int CArduinoSwitch::OnState(MM::PropertyBase* pProp, MM::ActionType eAct) } else if (eAct == MM::StartSequence) { - MMThreadGuard myLock(hub->GetLock()); + const std::lock_guard lock(hub_->GetLock()); - hub->PurgeComPortH(); + hub_->PurgeComPortH(); unsigned char command[1]; command[0] = 8; - int ret = hub->WriteToComPortH((const unsigned char*) command, 1); + int ret = hub_->WriteToComPortH((const unsigned char*) command, 1); if (ret != DEVICE_OK) return ret; MM::MMTime startTime = GetCurrentMMTime(); + const unsigned int nrBytes = 1; unsigned long bytesRead = 0; - unsigned char answer[1]; - while ((bytesRead < 1) && ( (GetCurrentMMTime() - startTime).getMsec() < 250)) { + unsigned char answer[1] = { 0 }; + while ((bytesRead < nrBytes) && ( (GetCurrentMMTime() - startTime).getMsec() < 250)) { unsigned long br; - ret = hub->ReadFromComPortH(answer + bytesRead, 1, br); + ret = hub_->ReadFromComPortH(answer + bytesRead, nrBytes - bytesRead, br); if (ret != DEVICE_OK) return ret; bytesRead += br; @@ -692,20 +755,21 @@ int CArduinoSwitch::OnState(MM::PropertyBase* pProp, MM::ActionType eAct) } else if (eAct == MM::StopSequence) { - MMThreadGuard myLock(hub->GetLock()); + const std::lock_guard lock(hub_->GetLock()); unsigned char command[1]; command[0] = 9; - int ret = hub->WriteToComPortH((const unsigned char*) command, 1); + int ret = hub_->WriteToComPortH((const unsigned char*) command, 1); if (ret != DEVICE_OK) return ret; MM::MMTime startTime = GetCurrentMMTime(); + const unsigned int nrBytes = 2; unsigned long bytesRead = 0; - unsigned char answer[2]; - while ((bytesRead < 2) && ( (GetCurrentMMTime() - startTime).getMsec() < 250)) { + unsigned char answer[nrBytes] = { 0, 0 }; + while ((bytesRead < nrBytes) && ( (GetCurrentMMTime() - startTime).getMsec() < 250)) { unsigned long br; - ret = hub->ReadFromComPortH(answer + bytesRead, 2, br); + ret = hub_->ReadFromComPortH(answer + bytesRead, nrBytes - bytesRead, br); if (ret != DEVICE_OK) return ret; bytesRead += br; @@ -722,6 +786,7 @@ int CArduinoSwitch::OnState(MM::PropertyBase* pProp, MM::ActionType eAct) return DEVICE_OK; } + int CArduinoSwitch::OnSequence(MM::PropertyBase* pProp, MM::ActionType eAct) { if (eAct == MM::BeforeGet) @@ -743,78 +808,73 @@ int CArduinoSwitch::OnSequence(MM::PropertyBase* pProp, MM::ActionType eAct) return DEVICE_OK; } + int CArduinoSwitch::OnStartTimedOutput(MM::PropertyBase* pProp, MM::ActionType eAct) { - CArduinoHub* hub = static_cast(GetParentHub()); - if (!hub || !hub->IsPortAvailable()) - return ERR_NO_PORT_SET; - if (eAct == MM::BeforeGet) { - if (hub->IsTimedOutputActive()) + if (hub_->IsTimedOutputActive()) pProp->Set("Running"); else pProp->Set("Idle"); } else if (eAct == MM::AfterSet) { - MMThreadGuard myLock(hub->GetLock()); + const std::lock_guard lock(hub_->GetLock()); std::string prop; pProp->Get(prop); if (prop =="Start") { - hub->PurgeComPortH(); + hub_->PurgeComPortH(); unsigned char command[1]; command[0] = 12; - int ret = hub->WriteToComPortH((const unsigned char*) command, 1); + int ret = hub_->WriteToComPortH((const unsigned char*) command, 1); if (ret != DEVICE_OK) return ret; MM::MMTime startTime = GetCurrentMMTime(); unsigned long bytesRead = 0; - unsigned char answer[1]; + unsigned char answer[1] = { 0 }; while ((bytesRead < 1) && ( (GetCurrentMMTime() - startTime).getMsec() < 250)) { unsigned long br; - ret = hub->ReadFromComPortH(answer + bytesRead, 1, br); + ret = hub_->ReadFromComPortH(answer, 1, br); if (ret != DEVICE_OK) return ret; bytesRead += br; } if (answer[0] != 12) return ERR_COMMUNICATION; - hub->SetTimedOutput(true); + hub_->SetTimedOutput(true); } else { unsigned char command[1]; command[0] = 9; - int ret = hub->WriteToComPortH((const unsigned char*) command, 1); + int ret = hub_->WriteToComPortH((const unsigned char*) command, 1); if (ret != DEVICE_OK) return ret; MM::MMTime startTime = GetCurrentMMTime(); + const unsigned int nrBytes = 2; unsigned long bytesRead = 0; - unsigned char answer[2]; - while ((bytesRead < 2) && ( (GetCurrentMMTime() - startTime).getMsec() < 250)) { + unsigned char answer[nrBytes] = { 0, 0 }; + while ((bytesRead < nrBytes) && ( (GetCurrentMMTime() - startTime).getMsec() < 250)) { unsigned long br; - ret = hub->ReadFromComPortH(answer + bytesRead, 2, br); + ret = hub_->ReadFromComPortH(answer + bytesRead, nrBytes - bytesRead, br); if (ret != DEVICE_OK) return ret; bytesRead += br; } if (answer[0] != 9) return ERR_COMMUNICATION; - hub->SetTimedOutput(false); + hub_->SetTimedOutput(false); } } return DEVICE_OK; } + int CArduinoSwitch::OnBlanking(MM::PropertyBase* pProp, MM::ActionType eAct) { - CArduinoHub* hub = static_cast(GetParentHub()); - if (!hub || !hub->IsPortAvailable()) - return ERR_NO_PORT_SET; - if (eAct == MM::BeforeGet) { if (blanking_) pProp->Set(g_On); @@ -823,25 +883,25 @@ int CArduinoSwitch::OnBlanking(MM::PropertyBase* pProp, MM::ActionType eAct) } else if (eAct == MM::AfterSet) { - MMThreadGuard myLock(hub->GetLock()); + const std::lock_guard lock(hub_->GetLock()); std::string prop; pProp->Get(prop); if (prop == g_On && !blanking_) { - hub->PurgeComPortH(); + hub_->PurgeComPortH(); unsigned char command[1]; command[0] = 20; - int ret = hub->WriteToComPortH((const unsigned char*) command, 1); + int ret = hub_->WriteToComPortH((const unsigned char*) command, 1); if (ret != DEVICE_OK) return ret; MM::MMTime startTime = GetCurrentMMTime(); unsigned long bytesRead = 0; - unsigned char answer[1]; + unsigned char answer[1] = { 0 }; while ((bytesRead < 1) && ( (GetCurrentMMTime() - startTime).getMsec() < 250)) { unsigned long br; - ret = hub->ReadFromComPortH(answer + bytesRead, 1, br); + ret = hub_->ReadFromComPortH(answer, 1, br); if (ret != DEVICE_OK) return ret; bytesRead += br; @@ -849,22 +909,23 @@ int CArduinoSwitch::OnBlanking(MM::PropertyBase* pProp, MM::ActionType eAct) if (answer[0] != 20) return ERR_COMMUNICATION; blanking_ = true; - hub->SetTimedOutput(false); + hub_->SetTimedOutput(false); LogMessage("Switched blanking on", true); } else if (prop == g_Off && blanking_){ unsigned char command[1]; command[0] = 21; - int ret = hub->WriteToComPortH((const unsigned char*) command, 1); + int ret = hub_->WriteToComPortH((const unsigned char*) command, 1); if (ret != DEVICE_OK) return ret; MM::MMTime startTime = GetCurrentMMTime(); + const unsigned int nrBytes = 2; unsigned long bytesRead = 0; - unsigned char answer[2]; - while ((bytesRead < 2) && ( (GetCurrentMMTime() - startTime).getMsec() < 250)) { + unsigned char answer[nrBytes] = { 0, 0 }; + while ((bytesRead < nrBytes) && ( (GetCurrentMMTime() - startTime).getMsec() < 250)) { unsigned long br; - ret = hub->ReadFromComPortH(answer + bytesRead, 2, br); + ret = hub_->ReadFromComPortH(answer + bytesRead, nrBytes - bytesRead, br); if (ret != DEVICE_OK) return ret; bytesRead += br; @@ -872,7 +933,7 @@ int CArduinoSwitch::OnBlanking(MM::PropertyBase* pProp, MM::ActionType eAct) if (answer[0] != 21) return ERR_COMMUNICATION; blanking_ = false; - hub->SetTimedOutput(false); + hub_->SetTimedOutput(false); LogMessage("Switched blanking off", true); } } @@ -882,21 +943,17 @@ int CArduinoSwitch::OnBlanking(MM::PropertyBase* pProp, MM::ActionType eAct) int CArduinoSwitch::OnBlankingTriggerDirection(MM::PropertyBase* pProp, MM::ActionType eAct) { - CArduinoHub* hub = static_cast(GetParentHub()); - if (!hub || !hub->IsPortAvailable()) - return ERR_NO_PORT_SET; - if (eAct == MM::BeforeGet) { // nothing to do, let the caller use cached property } else if (eAct == MM::AfterSet) { - MMThreadGuard myLock(hub->GetLock()); + const std::lock_guard lock(hub_->GetLock()); std::string direction; pProp->Get(direction); - hub->PurgeComPortH(); + hub_->PurgeComPortH(); unsigned char command[2]; command[0] = 22; if (direction == "Low") @@ -904,16 +961,16 @@ int CArduinoSwitch::OnBlankingTriggerDirection(MM::PropertyBase* pProp, MM::Acti else command[1] = 0; - int ret = hub->WriteToComPortH((const unsigned char*) command, 2); + int ret = hub_->WriteToComPortH((const unsigned char*) command, 2); if (ret != DEVICE_OK) return ret; MM::MMTime startTime = GetCurrentMMTime(); unsigned long bytesRead = 0; - unsigned char answer[1]; + unsigned char answer[1] = { 0 }; while ((bytesRead < 1) && ( (GetCurrentMMTime() - startTime).getMsec() < 250)) { unsigned long br; - ret = hub->ReadFromComPortH(answer + bytesRead, 1, br); + ret = hub_->ReadFromComPortH(answer, 1, br); if (ret != DEVICE_OK) return ret; bytesRead += br; @@ -926,6 +983,7 @@ int CArduinoSwitch::OnBlankingTriggerDirection(MM::PropertyBase* pProp, MM::Acti return DEVICE_OK; } + int CArduinoSwitch::OnDelay(MM::PropertyBase* pProp, MM::ActionType eAct) { if (eAct == MM::BeforeGet) { @@ -941,36 +999,34 @@ int CArduinoSwitch::OnDelay(MM::PropertyBase* pProp, MM::ActionType eAct) return DEVICE_OK; } + int CArduinoSwitch::OnRepeatTimedPattern(MM::PropertyBase* pProp, MM::ActionType eAct) { - CArduinoHub* hub = static_cast(GetParentHub()); - if (!hub || !hub->IsPortAvailable()) - return ERR_NO_PORT_SET; - if (eAct == MM::BeforeGet) { } else if (eAct == MM::AfterSet) { - MMThreadGuard myLock(hub->GetLock()); + const std::lock_guard lock(hub_->GetLock()); long prop; pProp->Get(prop); - hub->PurgeComPortH(); + hub_->PurgeComPortH(); unsigned char command[2]; command[0] = 11; command[1] = (unsigned char) prop; - int ret = hub->WriteToComPortH((const unsigned char*) command, 2); + int ret = hub_->WriteToComPortH((const unsigned char*) command, 2); if (ret != DEVICE_OK) return ret; MM::MMTime startTime = GetCurrentMMTime(); + const unsigned int nrBytes = 2; unsigned long bytesRead = 0; - unsigned char answer[2]; - while ((bytesRead < 2) && ( (GetCurrentMMTime() - startTime).getMsec() < 250)) { + unsigned char answer[nrBytes] = { 0, 0 }; + while ((bytesRead < nrBytes) && ( (GetCurrentMMTime() - startTime).getMsec() < 250)) { unsigned long br; - ret = hub->ReadFromComPortH(answer + bytesRead, 2, br); + ret = hub_->ReadFromComPortH(answer + bytesRead, nrBytes - bytesRead, br); if (ret != DEVICE_OK) return ret; bytesRead += br; @@ -978,7 +1034,7 @@ int CArduinoSwitch::OnRepeatTimedPattern(MM::PropertyBase* pProp, MM::ActionType if (answer[0] != 11) return ERR_COMMUNICATION; - hub->SetTimedOutput(false); + hub_->SetTimedOutput(false); } return DEVICE_OK; @@ -1087,7 +1143,7 @@ int CArduinoDA::WriteToPort(unsigned long value) if (!hub || !hub->IsPortAvailable()) return ERR_NO_PORT_SET; - MMThreadGuard myLock(hub->GetLock()); + const std::lock_guard lock(hub->GetLock()); hub->PurgeComPortH(); @@ -1101,11 +1157,12 @@ int CArduinoDA::WriteToPort(unsigned long value) return ret; MM::MMTime startTime = GetCurrentMMTime(); + const unsigned int nrBytes = 4; unsigned long bytesRead = 0; - unsigned char answer[4]; - while ((bytesRead < 4) && ( (GetCurrentMMTime() - startTime).getMsec() < 2500)) { + unsigned char answer[nrBytes] = {0, 0, 0, 0}; + while ((bytesRead < nrBytes) && ( (GetCurrentMMTime() - startTime).getMsec() < 2500)) { unsigned long bR; - ret = hub->ReadFromComPortH(answer + bytesRead, 4 - bytesRead, bR); + ret = hub->ReadFromComPortH(answer + bytesRead, nrBytes - bytesRead, bR); if (ret != DEVICE_OK) return ret; bytesRead += bR; @@ -1213,7 +1270,10 @@ int CArduinoDA::OnChannel(MM::PropertyBase* pProp, MM::ActionType eAct) // CArduinoShutter implementation // ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -CArduinoShutter::CArduinoShutter() : initialized_(false), name_(g_DeviceNameArduinoShutter) +CArduinoShutter::CArduinoShutter() : + hub_(0), + initialized_(false), + name_(g_DeviceNameArduinoShutter) { InitializeDefaultErrorMessages(); EnableDelay(); @@ -1251,12 +1311,12 @@ bool CArduinoShutter::Busy() int CArduinoShutter::Initialize() { - CArduinoHub* hub = static_cast(GetParentHub()); - if (!hub || !hub->IsPortAvailable()) { + hub_ = static_cast(GetParentHub()); + if (!hub_ || !hub_->IsPortAvailable()) { return ERR_NO_PORT_SET; } char hubLabel[MM::MaxStrLength]; - hub->GetLabel(hubLabel); + hub_->GetLabel(hubLabel); SetParentID(hubLabel); // for backward comp. // set property list @@ -1329,37 +1389,33 @@ int CArduinoShutter::Fire(double /*deltaT*/) int CArduinoShutter::WriteToPort(long value) { - CArduinoHub* hub = static_cast(GetParentHub()); - if (!hub || !hub->IsPortAvailable()) - return ERR_NO_PORT_SET; - - MMThreadGuard myLock(hub->GetLock()); + const std::lock_guard lock(hub_->GetLock()); value = 63 & value; - if (hub->IsLogicInverted()) + if (hub_->IsLogicInverted()) value = ~value; - hub->PurgeComPortH(); + hub_->PurgeComPortH(); unsigned char command[2]; command[0] = 1; command[1] = (unsigned char) value; - int ret = hub->WriteToComPortH((const unsigned char*) command, 2); + int ret = hub_->WriteToComPortH((const unsigned char*) command, 2); if (ret != DEVICE_OK) return ret; MM::MMTime startTime = GetCurrentMMTime(); unsigned long bytesRead = 0; - unsigned char answer[1]; + unsigned char answer[1] = { 0 }; while ((bytesRead < 1) && ( (GetCurrentMMTime() - startTime).getMsec() < 250)) { - ret = hub->ReadFromComPortH(answer, 1, bytesRead); + ret = hub_->ReadFromComPortH(answer, 1, bytesRead); if (ret != DEVICE_OK) return ret; } if (answer[0] != 1) return ERR_COMMUNICATION; - hub->SetTimedOutput(false); + hub_->SetTimedOutput(false); return DEVICE_OK; } @@ -1370,11 +1426,10 @@ int CArduinoShutter::WriteToPort(long value) int CArduinoShutter::OnOnOff(MM::PropertyBase* pProp, MM::ActionType eAct) { - CArduinoHub* hub = static_cast(GetParentHub()); if (eAct == MM::BeforeGet) { // use cached state - pProp->Set((long)hub->GetShutterState()); + pProp->Set((long)hub_->GetShutterState()); } else if (eAct == MM::AfterSet) { @@ -1384,10 +1439,10 @@ int CArduinoShutter::OnOnOff(MM::PropertyBase* pProp, MM::ActionType eAct) if (pos == 0) ret = WriteToPort(0); // turn everything off else - ret = WriteToPort(hub->GetSwitchState()); // restore old setting + ret = WriteToPort(hub_->GetSwitchState()); // restore old setting if (ret != DEVICE_OK) return ret; - hub->SetShutterState(pos); + hub_->SetShutterState(pos); changedTime_ = GetCurrentMMTime(); } @@ -1400,7 +1455,13 @@ int CArduinoShutter::OnOnOff(MM::PropertyBase* pProp, MM::ActionType eAct) * or for an individual pin only */ +/////////////////////////////////////////////////////////////////////////////// +// CArduinoInput implementation +// Arduino input. Can either be for all pins (0-6) +// or for an individual pin only +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ CArduinoInput::CArduinoInput() : + hub_(0), mThread_(0), pin_(0), name_(g_DeviceNameArduinoInput) @@ -1448,16 +1509,16 @@ int CArduinoInput::Shutdown() int CArduinoInput::Initialize() { - CArduinoHub* hub = static_cast(GetParentHub()); - if (!hub || !hub->IsPortAvailable()) { + hub_ = static_cast(GetParentHub()); + if (!hub_ || !hub_->IsPortAvailable()) { return ERR_NO_PORT_SET; } char hubLabel[MM::MaxStrLength]; - hub->GetLabel(hubLabel); + hub_->GetLabel(hubLabel); SetParentID(hubLabel); // for backward comp. char ver[MM::MaxStrLength] = "0"; - hub->GetProperty(g_versionProp, ver); + hub_->GetProperty(g_versionProp, ver); int version = atoi(ver); if (version < 2) return ERR_VERSION_MISMATCH; @@ -1520,23 +1581,20 @@ bool CArduinoInput::Busy() return false; } + int CArduinoInput::GetDigitalInput(long* state) { - CArduinoHub* hub = static_cast(GetParentHub()); - if (!hub || !hub->IsPortAvailable()) - return ERR_NO_PORT_SET; - - MMThreadGuard myLock(hub->GetLock()); + const std::lock_guard lock(hub_->GetLock()); unsigned char command[1]; command[0] = 40; - int ret = hub->WriteToComPortH((const unsigned char*) command, 1); + int ret = hub_->WriteToComPortH((const unsigned char*) command, 1); if (ret != DEVICE_OK) return ret; unsigned char answer[2]; - ret = ReadNBytes(hub, 2, answer); + ret = ReadNBytes(hub_, 2, answer); if (ret != DEVICE_OK) return ret; @@ -1555,6 +1613,7 @@ int CArduinoInput::GetDigitalInput(long* state) int CArduinoInput::ReportStateChange(long newState) { + hub_->ReportNewInputState(newState); std::ostringstream os; os << newState; return OnPropertyChanged("DigitalInput", os.str().c_str()); @@ -1582,24 +1641,22 @@ int CArduinoInput::OnDigitalInput(MM::PropertyBase* pProp, MM::ActionType eAct) int CArduinoInput::OnAnalogInput(MM::PropertyBase* pProp, MM::ActionType eAct, long channel ) { - CArduinoHub* hub = static_cast(GetParentHub()); - if (!hub || !hub->IsPortAvailable()) + hub_ = static_cast(GetParentHub()); + if (!hub_ || !hub_->IsPortAvailable()) return ERR_NO_PORT_SET; if (eAct == MM::BeforeGet) { - MMThreadGuard myLock(hub->GetLock()); + const std::lock_guard lock(hub_->GetLock()); - unsigned char command[2]; - command[0] = 41; - command[1] = (unsigned char) channel; + unsigned char command[2] = { 41, (unsigned char) channel }; - int ret = hub->WriteToComPortH((const unsigned char*) command, 2); + int ret = hub_->WriteToComPortH((const unsigned char*) command, 2); if (ret != DEVICE_OK) return ret; unsigned char answer[4]; - ret = ReadNBytes(hub, 4, answer); + ret = ReadNBytes(hub_, 4, answer); if (ret != DEVICE_OK) return ret; @@ -1619,11 +1676,7 @@ int CArduinoInput::OnAnalogInput(MM::PropertyBase* pProp, MM::ActionType eAct, l int CArduinoInput::SetPullUp(int pin, int state) { - CArduinoHub* hub = static_cast(GetParentHub()); - if (!hub || !hub->IsPortAvailable()) - return ERR_NO_PORT_SET; - - MMThreadGuard myLock(hub->GetLock()); + const std::lock_guard lock(hub_->GetLock()); const int nrChrs = 3; unsigned char command[nrChrs]; @@ -1631,12 +1684,12 @@ int CArduinoInput::SetPullUp(int pin, int state) command[1] = (unsigned char) pin; command[2] = (unsigned char) state; - int ret = hub->WriteToComPortH((const unsigned char*) command, nrChrs); + int ret = hub_->WriteToComPortH((const unsigned char*) command, nrChrs); if (ret != DEVICE_OK) return ret; - unsigned char answer[3]; - ret = ReadNBytes(hub, 3, answer); + unsigned char answer[3] = { 0, 0, 0 }; + ret = ReadNBytes(hub_, 3, answer); if (ret != DEVICE_OK) return ret; @@ -1664,7 +1717,140 @@ int CArduinoInput::ReadNBytes(CArduinoHub* hub, unsigned int n, unsigned char* a return DEVICE_OK; } + + +/////////////////////////////////////////////////////////////////////////////// +// CArduinoMagnifier implementation +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +CArduinoMagnifier::CArduinoMagnifier() : + state_(0), + initialized_(false) +{ + CPropertyAction* pAct = new CPropertyAction(this, &CArduinoMagnifier::OnNumberOfMagnifications); + std::string nrMags = "Number of Magnifications"; + CreateIntegerProperty(nrMags.c_str(), 2, false, pAct, true); + SetPropertyLimits(nrMags.c_str(), 1, 16); +} + + +CArduinoMagnifier::~CArduinoMagnifier() +{ + if (initialized_) + Shutdown(); +} + + +int CArduinoMagnifier::Shutdown() +{ + if (hub_ != 0) + hub_->RegisterMagnifier(0); + initialized_ = false; + return DEVICE_OK; +} + + +int CArduinoMagnifier::Initialize() +{ + hub_ = static_cast(GetParentHub()); + if (!hub_ || !hub_->IsPortAvailable()) { + return ERR_NO_PORT_SET; + } + + std::string userString = "Magnification At State: "; + for (long state = 0; state < magnifications_.size(); state++) + { + CPropertyActionEx* pActEx = new CPropertyActionEx(this, &CArduinoMagnifier::OnSetMagnification, state); + std::ostringstream propName; + propName << userString << state; + CreateFloatProperty(propName.str().c_str(), 1.0, false, pActEx); + } + + hub_->RegisterMagnifier(this); + + return DEVICE_OK; +} + + +void CArduinoMagnifier::GetName(char* name) const +{ + CDeviceUtils::CopyLimitedString(name, g_DeviceNameArduinoMagnifier); +} + + +double CArduinoMagnifier::GetMagnification() +{ + double mag = 1.0; + auto it = magnifications_.find(state_); + if (it != magnifications_.end()) { + mag = it->second; + } + return mag; +} + + +int CArduinoMagnifier::UpdateState(long state) +{ + state_ = state; + GetCoreCallback()->OnMagnifierChanged(this); + + return DEVICE_OK; +} + + +int CArduinoMagnifier::OnNumberOfMagnifications(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::BeforeGet) + { + pProp->Set( (long) magnifications_.size()); + } + else if (eAct == MM::AfterSet) + { + long nrMagnifications; + pProp->Get(nrMagnifications); + if (nrMagnifications > 0) { + magnifications_.clear(); + for (long i = 0; i < nrMagnifications; i++) { + magnifications_.insert(std::pair(i, 1.0)); + } + } + } + return DEVICE_OK; +} + + +int CArduinoMagnifier::OnSetMagnification(MM::PropertyBase* pProp, MM::ActionType eAct, long state) +{ + if (eAct == MM::BeforeGet) + { + double mag = 1.0; + auto it = magnifications_.find(state); + if (it != magnifications_.end()) { + mag = it->second; + } + pProp->Set(mag); + } + else if (eAct == MM::AfterSet) + { + double mag; + pProp->Get(mag); + auto it = magnifications_.find(state); + if (it != magnifications_.end()) { + it->second = mag; + } + else { + return DEVICE_UNKNOWN_POSITION; + } + } + return DEVICE_OK; +} + + + +/////////////////////////////////////////////////////////////////////////////// +// ArduinoInputMonitorThread implementation +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ ArduinoInputMonitorThread::ArduinoInputMonitorThread(CArduinoInput& aInput) : + stop_(false), state_(0), aInput_(aInput) { diff --git a/DeviceAdapters/Arduino/Arduino.h b/DeviceAdapters/Arduino/Arduino.h index 45fba24bd..690808851 100644 --- a/DeviceAdapters/Arduino/Arduino.h +++ b/DeviceAdapters/Arduino/Arduino.h @@ -20,6 +20,7 @@ #include "DeviceBase.h" #include #include +#include ////////////////////////////////////////////////////////////////////////////// // Error codes @@ -35,6 +36,7 @@ #define ERR_VERSION_MISMATCH 109 class ArduinoInputMonitorThread; +class CArduinoMagnifier; class CArduinoHub : public HubBase { @@ -55,6 +57,9 @@ class CArduinoHub : public HubBase int OnPort(MM::PropertyBase* pPropt, MM::ActionType eAct); int OnLogic(MM::PropertyBase* pPropt, MM::ActionType eAct); int OnVersion(MM::PropertyBase* pPropt, MM::ActionType eAct); + unsigned int GetMaxNumPatterns() { + return maxNumPatterns_; + }; // custom interface for child devices bool IsPortAvailable() {return portAvailable_;} @@ -68,11 +73,13 @@ class CArduinoHub : public HubBase { return ReadFromComPort(port_.c_str(), answer, maxLen, bytesRead); } - static MMThreadLock& GetLock() {return lock_;} + int ReportNewInputState(long newState); + int RegisterMagnifier(CArduinoMagnifier* magnifier); + std::mutex& GetLock() {return mutex_;} void SetShutterState(unsigned state) {shutterState_ = state;} void SetSwitchState(unsigned state) {switchState_ = state;} - unsigned GetShutterState() {return shutterState_;} - unsigned GetSwitchState() {return switchState_;} + const unsigned GetShutterState() {return shutterState_;} + const unsigned GetSwitchState() {return switchState_;} private: int GetControllerVersion(int&); @@ -82,7 +89,9 @@ class CArduinoHub : public HubBase bool invertedLogic_; bool timedOutputActive_; int version_; - static MMThreadLock lock_; + unsigned int maxNumPatterns_; + CArduinoMagnifier* magnifier_; + std::mutex mutex_; unsigned switchState_; unsigned shutterState_; }; @@ -111,6 +120,7 @@ class CArduinoShutter : public CShutterBase int OnOnOff(MM::PropertyBase* pProp, MM::ActionType eAct); private: + CArduinoHub* hub_; int WriteToPort(long lnValue); MM::MMTime changedTime_; bool initialized_; @@ -143,8 +153,8 @@ class CArduinoSwitch : public CStateDeviceBase 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 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); @@ -152,15 +162,14 @@ class CArduinoSwitch : public CStateDeviceBase int OnSequence(MM::PropertyBase* pProp, MM::ActionType eAct); private: - static const unsigned int NUMPATTERNS = 12; + unsigned int numPatterns_; - int OpenPort(const char* pszName, long lnValue); + CArduinoHub* hub_; + //int OpenPort(const char* pszName, long lnValue); int WriteToPort(long lnValue); - int ClosePort(); + //int ClosePort(); int LoadSequence(unsigned size, unsigned char* seq); - unsigned pattern_[NUMPATTERNS]; - unsigned delay_[NUMPATTERNS]; int nrPatternsUsed_; unsigned currentDelay_; bool sequenceOn_; @@ -236,7 +245,7 @@ class CArduinoInput : public CGenericBase int ReadNBytes(CArduinoHub* h, unsigned int n, unsigned char* answer); int SetPullUp(int pin, int state); - MMThreadLock lock_; + CArduinoHub* hub_; ArduinoInputMonitorThread* mThread_; char pins_[MM::MaxStrLength]; char pullUp_[MM::MaxStrLength]; @@ -245,6 +254,30 @@ class CArduinoInput : public CGenericBase std::string name_; }; +class CArduinoMagnifier : public CMagnifierBase +{ +public: + CArduinoMagnifier(); + ~CArduinoMagnifier(); + + int Initialize(); + int Shutdown(); + void GetName(char* pszName) const; + bool Busy() { return false; }; + + double GetMagnification(); + + int UpdateState(long state); + int OnNumberOfMagnifications(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnSetMagnification(MM::PropertyBase* pProp, MM::ActionType eAct, long state); + +private: + CArduinoHub* hub_; + std::map magnifications_; + long state_; + bool initialized_; +}; + class ArduinoInputMonitorThread : public MMDeviceThreadBase { public: @@ -265,6 +298,7 @@ class ArduinoInputMonitorThread : public MMDeviceThreadBase private: long state_; CArduinoInput& aInput_; + std::mutex mutex_; bool stop_; }; diff --git a/DeviceAdapters/ArduinoCounter/ArduinoCounter.vcxproj b/DeviceAdapters/ArduinoCounter/ArduinoCounter.vcxproj index 7c73b01d5..0e723d7e4 100644 --- a/DeviceAdapters/ArduinoCounter/ArduinoCounter.vcxproj +++ b/DeviceAdapters/ArduinoCounter/ArduinoCounter.vcxproj @@ -11,7 +11,7 @@ - {067D4A8-8C32-40D3-BA9F-6B388ECCD714} + {8331c4e1-6c17-481e-9b4f-232db77d95cb} Arduino 10.0 @@ -97,4 +97,4 @@ - \ No newline at end of file + diff --git a/DeviceAdapters/Atik/Atik.cpp b/DeviceAdapters/Atik/Atik.cpp index d5444c358..5165f8385 100644 --- a/DeviceAdapters/Atik/Atik.cpp +++ b/DeviceAdapters/Atik/Atik.cpp @@ -880,7 +880,7 @@ int Atik::InsertImage() // Important: metadata about the image are generated here: Metadata md; - md.put("Camera", "Atik SDK Camera"); + md.put(MM::g_Keyword_Metadata_CameraLabel, "Atik SDK Camera"); string serialised = md.Serialize(); diff --git a/DeviceAdapters/Basler/BaslerPylonCamera.cpp b/DeviceAdapters/Basler/BaslerPylonCamera.cpp index 3e98fe9ba..c6403320e 100644 --- a/DeviceAdapters/Basler/BaslerPylonCamera.cpp +++ b/DeviceAdapters/Basler/BaslerPylonCamera.cpp @@ -2127,7 +2127,7 @@ void CircularBufferInserter::OnImageGrabbed(CInstantCamera& /* camera */, const // Important: meta data about the image are generated here: Metadata md; - md.put("Camera", ""); + md.put(MM::g_Keyword_Metadata_CameraLabel, ""); md.put(MM::g_Keyword_Metadata_ROI_X, CDeviceUtils::ConvertToString((long)ptrGrabResult->GetWidth())); md.put(MM::g_Keyword_Metadata_ROI_Y, CDeviceUtils::ConvertToString((long)ptrGrabResult->GetHeight())); md.put(MM::g_Keyword_Metadata_ImageNumber, CDeviceUtils::ConvertToString((long)ptrGrabResult->GetImageNumber())); diff --git a/DeviceAdapters/BaumerOptronic/BaumerOptronic.cpp b/DeviceAdapters/BaumerOptronic/BaumerOptronic.cpp index 40cb85a65..6fc0276e4 100644 --- a/DeviceAdapters/BaumerOptronic/BaumerOptronic.cpp +++ b/DeviceAdapters/BaumerOptronic/BaumerOptronic.cpp @@ -2273,7 +2273,7 @@ int CBaumerOptronic::SendImageToCore() char label[MM::MaxStrLength]; this->GetLabel(label); Metadata md; - md.put("Camera", label); + md.put(MM::g_Keyword_Metadata_CameraLabel, label); int err = WaitForImageAndCopyToBuffer(); if (err != DEVICE_OK) diff --git a/DeviceAdapters/CARVII/license.txt b/DeviceAdapters/CARVII/license.txt index 69098da00..3a30f6d87 100644 --- a/DeviceAdapters/CARVII/license.txt +++ b/DeviceAdapters/CARVII/license.txt @@ -1,17 +1,17 @@ -Copyright (c) 2011, Children’s Hospital Los Angeles +Copyright (c) 2011, Children’s Hospital Los Angeles All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: -•Redistributions of source code must retain the above copyright notice, this +•Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -•Redistributions in binary form must reproduce the above copyright notice, +•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. -•Neither the name of Children’s Hospital Los Angeles nor the names of its +•Neither the name of Children’s Hospital Los Angeles nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/DeviceAdapters/Cobolt/Cobolt.h b/DeviceAdapters/Cobolt/Cobolt.h index 4de297f75..07dfb176c 100644 --- a/DeviceAdapters/Cobolt/Cobolt.h +++ b/DeviceAdapters/Cobolt/Cobolt.h @@ -56,9 +56,9 @@ // Strings // -const char * g_DeviceVendorName = "HÜBNER Photonics"; +const char * g_DeviceVendorName = "HÃœBNER Photonics"; const char * g_DeviceCoboltName = "Cobolt"; -const char * g_DeviceCoboltDescription = "Cobolt Controller by Karl Bellvé with contribution from Alexis Maizel"; +const char * g_DeviceCoboltDescription = "Cobolt Controller by Karl Bellvé with contribution from Alexis Maizel"; const char * g_SendTerm = "\r"; diff --git a/DeviceAdapters/CoboltOfficial/CoboltOfficial.cpp b/DeviceAdapters/CoboltOfficial/CoboltOfficial.cpp index 367903d53..54835d91a 100644 --- a/DeviceAdapters/CoboltOfficial/CoboltOfficial.cpp +++ b/DeviceAdapters/CoboltOfficial/CoboltOfficial.cpp @@ -41,7 +41,7 @@ using namespace cobolt; const char * g_DeviceName = "Cobolt Laser"; const char * g_DeviceDescription = "Official device adapter for Cobolt lasers."; -const char * g_DeviceVendorName = "Cobolt - a HÜBNER Group company"; +const char * g_DeviceVendorName = "Cobolt - a HÃœBNER Group company"; const char* const g_Property_Port_None = "None"; diff --git a/DeviceAdapters/CoboltOfficial/LaserFactory.cpp b/DeviceAdapters/CoboltOfficial/LaserFactory.cpp index e3da7695f..d3e7e0965 100644 --- a/DeviceAdapters/CoboltOfficial/LaserFactory.cpp +++ b/DeviceAdapters/CoboltOfficial/LaserFactory.cpp @@ -81,7 +81,12 @@ Laser* LaserFactory::Create( LaserDriver* driver ) Laser* laser; - if ( modelString.find( "-06-91-" ) != std::string::npos ) { + if (modelString.find("-06-51-") != std::string::npos || + modelString.find("-06-53-") != std::string::npos || + modelString.find("-06-57-") != std::string::npos || + modelString.find("-06-91-") != std::string::npos || + modelString.find("-06-93-") != std::string::npos || + modelString.find("-06-97-") != std::string::npos) { laser = new Dpl06Laser( wavelength, driver ); diff --git a/DeviceAdapters/Corvus/Corvus.cpp b/DeviceAdapters/Corvus/Corvus.cpp index 4068cac23..6d225a7a1 100644 --- a/DeviceAdapters/Corvus/Corvus.cpp +++ b/DeviceAdapters/Corvus/Corvus.cpp @@ -7,7 +7,7 @@ // XY Stage // Z Stage // -// AUTHOR: Johan Henriksson, mahogny@areta.org, derived from Märzhauser adapter +// AUTHOR: Johan Henriksson, mahogny@areta.org, derived from Märzhauser adapter // COPYRIGHT: Johan Henriksson, 2010 // LICENSE: This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public @@ -345,7 +345,7 @@ int XYStage::Initialize() return ret; SetPropertyLimits("Speed [mm/s]", 0.001, 100.0); // mm/s - // Accel (Acceleration (in m/s²) + // Accel (Acceleration (in m/s²) // ----- pAct = new CPropertyAction (this, &XYStage::OnAccel); // TODO: get current Acceleration from the controller @@ -406,7 +406,7 @@ bool XYStage::Busy() /** - * Returns current position in µm. + * Returns current position in µm. */ int XYStage::GetPositionUm(double& x, double& y) { @@ -439,7 +439,7 @@ int ret; /** - * Sets position in µm + * Sets position in µm */ int XYStage::SetPositionUm(double x, double y) { diff --git a/DeviceAdapters/DahengGalaxy/ClassGalaxy.cpp b/DeviceAdapters/DahengGalaxy/ClassGalaxy.cpp index 0c4dc32f9..5649ba40f 100644 --- a/DeviceAdapters/DahengGalaxy/ClassGalaxy.cpp +++ b/DeviceAdapters/DahengGalaxy/ClassGalaxy.cpp @@ -28,7 +28,7 @@ MODULE_API MM::Device* CreateDevice(const char* deviceName) if (deviceName == 0) return 0; - // decide which device class to create based on the deviceName parameter ±È½Ï½á¹û + // decide which device class to create based on the deviceName parameter 比较结果 if (strcmp(deviceName, g_CameraDeviceName) == 0) { // create camera return new ClassGalaxy(); @@ -68,6 +68,7 @@ ClassGalaxy::ClassGalaxy() : sensorReadoutMode_("Undefined"), shutterMode_("None"), imgBufferSize_(0), + sequenceRunning_(false), initialized_(false) { // call the base class method to set-up default error codes/messages @@ -144,19 +145,19 @@ int ClassGalaxy::Initialize() vectorDeviceInfo.clear(); - //ö¾ÙÉ豸 + //枚举设备 IGXFactory::GetInstance().UpdateDeviceList(1000, vectorDeviceInfo); - //ÅжÏö¾Ùµ½µÄÉ豸ÊÇ·ñ´óÓÚÁ㣬Èç¹û²»ÊÇÔòµ¯¿òÌáʾ + //判断枚举到的设备是å¦å¤§äºŽé›¶ï¼Œå¦‚æžœä¸æ˜¯åˆ™å¼¹æ¡†æ示 if (vectorDeviceInfo.size() <= 0) { return DEVICE_NOT_CONNECTED; } - //»ñÈ¡¿ÉÖ´ÐгÌÐòµÄµ±Ç°Â·¾¶,ĬÈÏ¿ªÆôµÚÒ»¸ö + //获å–å¯æ‰§è¡Œç¨‹åºçš„当å‰è·¯å¾„,默认开å¯ç¬¬ä¸€ä¸ª initialized_ = false; // This checks, among other things, that the camera is not already in use. // Without that check, the following CreateDevice() may crash on duplicate - // serial number. Unfortunately, this call is slow. ĬÈÏ´ò¿ªµÚÒ»¸öÉ豸 + // serial number. Unfortunately, this call is slow. 默认打开第一个设备 int index = 0; string serialNumberstr = vectorDeviceInfo[index].GetSN().c_str(); @@ -166,13 +167,13 @@ int ClassGalaxy::Initialize() if (strlen(serialNumber) == 0 || strcmp(serialNumber, "Undefined") == 0) return 0; SetProperty("SerialNumber", serialNumber); - //´ò¿ªÉ豸 + //打开设备 m_objDevicePtr = IGXFactory::GetInstance().OpenDeviceBySN(vectorDeviceInfo[index].GetSN(), GX_ACCESS_MODE::GX_ACCESS_EXCLUSIVE); m_objFeatureControlPtr = m_objDevicePtr->GetRemoteFeatureControl(); //m_objFeatureControlPtr->GetEnumFeature("StreamBufferHandlingMode")->SetValue("NewestOnly"); - //ÅжÏÉ豸Á÷ÊÇ·ñ´óÓÚÁ㣬Èç¹û´óÓÚÁãÔò´ò¿ªÁ÷ + //判断设备æµæ˜¯å¦å¤§äºŽé›¶ï¼Œå¦‚æžœå¤§äºŽé›¶åˆ™æ‰“å¼€æµ int nStreamCount = m_objDevicePtr->GetStreamCount(); //CPropertyAction* pAct; @@ -187,19 +188,19 @@ int ClassGalaxy::Initialize() } else { - throw exception("δ·¢ÏÖÉ豸Á÷!"); + throw exception("未å‘现设备æµ!"); } GX_DEVICE_CLASS_LIST objDeviceClass = m_objDevicePtr->GetDeviceInfo().GetDeviceClass(); if (GX_DEVICE_CLASS_GEV == objDeviceClass) { - // ÅжÏÉ豸ÊÇ·ñÖ§³ÖÁ÷ͨµÀÊý¾Ý°ü¹¦ÄÜ + // 判断设备是å¦æ”¯æŒæµé€šé“æ•°æ®åŒ…功能 if (true == m_objFeatureControlPtr->IsImplemented("GevSCPSPacketSize")) { - // »ñÈ¡µ±Ç°ÍøÂç»·¾³µÄ×îÓÅ°ü³¤Öµ + // 获å–当å‰ç½‘络环境的最优包长值 int nPacketSize = m_objStreamPtr->GetOptimalPacketSize(); - // ½«×îÓÅ°ü³¤ÖµÉèÖÃΪµ±Ç°É豸µÄÁ÷ͨµÀ°ü³¤Öµ + // 将最优包长值设置为当å‰è®¾å¤‡çš„æµé€šé“包长值 CIntFeaturePointer GevSCPD = m_objFeatureControlPtr->GetIntFeature("GevSCPSPacketSize"); m_objFeatureControlPtr->GetIntFeature("GevSCPSPacketSize")->SetValue(nPacketSize); m_objFeatureControlPtr->GetIntFeature("GevHeartbeatTimeout")->SetValue(300000); @@ -209,7 +210,7 @@ int ClassGalaxy::Initialize() SetPropertyLimits("InterPacketDelay", (double)GevSCPD->GetMin(), (double)GevSCPD->GetMax()); assert(ret == DEVICE_OK); } - //µÚ¶þ¸ö²ÎÊýΪÓû§Ë½ÓвÎÊý£¬Óû§¿ÉÒÔÔڻص÷º¯ÊýÄÚ²¿½«Æ仹ԭȻʹÓã¬Èç¹û²»ÐèÒªÔò¿É´«Èë NULL ¼´¿É + //第二个å‚数为用户ç§æœ‰å‚数,用户å¯ä»¥åœ¨å›žè°ƒå‡½æ•°å†…部将其还原然使用,如果ä¸éœ€è¦åˆ™å¯ä¼ å…¥ NULL å³å¯ //hDeviceOffline = m_objDevicePtr->RegisterDeviceOfflineCallback(pDeviceOfflineEventHandler, this); } else if (GX_DEVICE_CLASS_U3V == objDeviceClass) @@ -226,7 +227,7 @@ int ClassGalaxy::Initialize() } } - //ÑÕÉ«ÅÐ¶Ï + //颜色判断 gxstring strValue = ""; if (m_objDevicePtr->GetRemoteFeatureControl()->IsImplemented("PixelColorFilter")) { @@ -243,7 +244,7 @@ int ClassGalaxy::Initialize() //AddToLog(msg.str()); msg << "using camera " << m_objFeatureControlPtr->GetStringFeature("DeviceUserID")->GetValue(); AddToLog(msg.str()); - // initialize the pylon image formatter. ÅжÏÏà»úͼÏñÊä³ö¸ñʽ-ÕÔΰ¸¦ + // initialize the pylon image formatter. 判断相机图åƒè¾“出格å¼-赵伟甫 // // Name int ret = CreateProperty(MM::g_Keyword_Name, g_CameraDeviceName, MM::String, true); @@ -262,12 +263,12 @@ int ClassGalaxy::Initialize() //Get information about camera (e.g. height, width, byte depth) //check if given Camera support event. //Register Camera events - //ÕÔΰ¸¦£º×¢²áÏà»úʼþ£¬×¢²á²É¼¯»Øµ÷-δ¼Ó ζÈʼþ + //赵伟甫:注册相机事件,注册采集回调-未加 温度事件 CIntFeaturePointer width = m_objFeatureControlPtr->GetIntFeature("Width"); CIntFeaturePointer height = m_objFeatureControlPtr->GetIntFeature("Height"); - //×ÜÊôÐÔÆ÷ + //总属性器 if (1) { CPropertyAction* pAct = new CPropertyAction(this, &ClassGalaxy::OnWidth); @@ -290,7 +291,7 @@ int ClassGalaxy::Initialize() //end of Sensor size long bytes = (long)(height->GetValue() * width->GetValue() * 4); - //20221020ÕÔΰ¸¦ + //20221020赵伟甫 //Buffer4ContinuesShot = malloc(bytes); @@ -309,7 +310,7 @@ int ClassGalaxy::Initialize() vector pixelTypeValues; CEnumFeaturePointer PixelFormatList = m_objFeatureControlPtr->GetEnumFeature("PixelFormat"); gxstring_vector LisePixelFormat = PixelFormatList->GetEnumEntryList(); - //ΪÁ˸³ÖµÓà + //为了赋值用 for (size_t i = 0; i < LisePixelFormat.size(); i++) { string strValue(LisePixelFormat[i]); @@ -388,7 +389,7 @@ int ClassGalaxy::Initialize() } SetAllowedValues("TriggerSource", LSPVals); } - //20230217ÉèÖÃÆÚÍûÖ¡ÂÊʹÄÜ + //20230217设置期望帧率使能 if (m_objDevicePtr->GetRemoteFeatureControl()->IsImplemented("AcquisitionFrameRateMode")) { CEnumFeaturePointer AdjFrameRateMode = m_objFeatureControlPtr->GetEnumFeature("AcquisitionFrameRateMode"); @@ -409,7 +410,7 @@ int ClassGalaxy::Initialize() CFloatFeaturePointer AdjFrameRate = m_objFeatureControlPtr->GetFloatFeature("AcquisitionFrameRate"); pAct = new CPropertyAction(this, &ClassGalaxy::OnAcquisitionFrameRate); ret = CreateProperty("AcquisitionFrameRate", CDeviceUtils::ConvertToString((float)0), MM::Float, false, pAct); - //µ±Ç°²É¼¯Ö¡ÂÊÐèÖØмÆËã + //当å‰é‡‡é›†å¸§çŽ‡éœ€é‡æ–°è®¡ç®— SetPropertyLimits("AcquisitionFrameRate", (double)AdjFrameRate->GetMin(), (double)AdjFrameRate->GetMax()); assert(ret == DEVICE_OK); } @@ -455,7 +456,7 @@ int ClassGalaxy::Initialize() assert(ret == DEVICE_OK); } - //ÔöÒæ + //增益 m_objFeatureControlPtr->GetEnumFeature("GainSelector")->SetValue("AnalogAll"); m_objFeatureControlPtr->GetFloatFeature("Gain")->SetValue(0.0000); double d = m_objFeatureControlPtr->GetFloatFeature("Gain")->GetValue(); @@ -468,7 +469,7 @@ int ClassGalaxy::Initialize() SetPropertyLimits("Gain", (double)Gain->GetMin(), (double)Gain->GetMax()); assert(ret == DEVICE_OK); } - //20230220ÉèÖÃͼÏñת»»RGBA8 + //20230220设置图åƒè½¬æ¢RGBA8 ret = UpdateStatus(); if (ret != DEVICE_OK) @@ -500,7 +501,7 @@ void ClassGalaxy::CoverToRGB(GX_PIXEL_FORMAT_ENTRY emDstFormat,void* DstBuffer, TestFormatConvertPtr->SetAlphaValue(255); uint64_t Size = TestFormatConvertPtr->GetBufferSizeForConversion(pObjSrcImageData); TestFormatConvertPtr->Convert(pObjSrcImageData, DstBuffer, Size, false); //modify by LXM in 20240305 - //×¢ÒⶼÄÜÏÔʾ16λͼÏñRGB + //注æ„都能显示16ä½å›¾åƒRGB } catch (CGalaxyException& e) { @@ -524,7 +525,7 @@ int ClassGalaxy::CheckForBinningMode(CPropertyAction* pAct) vector LSPVals; gxstring_vector LiseBinningHorizontalMode = BinningHorizontalMode->GetEnumEntryList(); - //ΪÁ˸³ÖµÓà + //为了赋值用 for (size_t i = 0; i < LiseBinningHorizontalMode.size(); i++) { string strValue(LiseBinningHorizontalMode[i]); @@ -574,17 +575,17 @@ int ClassGalaxy::OnBinningMode(MM::PropertyBase* pProp, MM::ActionType eAct) } int ClassGalaxy::Shutdown() { - //¹Ø±ÕÏà»ú + //关闭相机 try { - //ÅжÏÊÇ·ñÍ£Ö¹²É¼¯ + //判断是å¦åœæ­¢é‡‡é›† if (m_objStreamFeatureControlPtr->GetBoolFeature("StreamIsGrabbing")->GetValue()) { - //·¢ËÍÍ£²ÉÃüÁî + //å‘é€åœé‡‡å‘½ä»¤ m_objFeatureControlPtr->GetCommandFeature("AcquisitionStop")->Execute(); - //¹Ø±ÕÁ÷²ã²É¼¯ + //关闭æµå±‚采集 m_objStreamPtr->StopGrab(); - //×¢Ïú²É¼¯»Øµ÷º¯Êý + //注销采集回调函数 m_objStreamPtr->UnregisterCaptureCallback(); } @@ -595,10 +596,10 @@ int ClassGalaxy::Shutdown() } try { - //¹Ø±ÕÁ÷¶ÔÏó + //关闭æµå¯¹è±¡ m_objStreamPtr->Close(); - //¹Ø±ÕÉ豸 + //关闭设备 m_objDevicePtr->Close(); } catch (CGalaxyException& e) @@ -625,7 +626,7 @@ int ClassGalaxy::SnapImage() // modify by LXM in 20240305 //if (m_bIsOpen) { - //AddToLog("---------------ÅжÏÊÇ·ñ¿ª²É"); + //AddToLog("---------------判断是å¦å¼€é‡‡"); m_objFeatureControlPtr->GetCommandFeature("AcquisitionStop")->Execute(); m_objStreamPtr->StopGrab(); //camera_->DeregisterImageEventHandler(ImageHandler_); @@ -640,13 +641,13 @@ int ClassGalaxy::SnapImage() //end modify int timeout_ms = 5000; - //¿ªÆôÁ÷²ã²É¼¯ + //å¼€å¯æµå±‚采集 m_objStreamPtr->StartGrab(); - //·¢ËÍ¿ª²ÉÃüÁî + //å‘é€å¼€é‡‡å‘½ä»¤ m_objFeatureControlPtr->GetCommandFeature("AcquisitionStart")->Execute(); m_bIsOpen = true;//modify by LXM m_objStreamPtr->FlushQueue(); - //¿ÉÒÔʹÓòɵ¥Ö¡À´»ñÈ¡ + //å¯ä»¥ä½¿ç”¨é‡‡å•å¸§æ¥èŽ·å– CImageDataPointer ptrGrabResult = m_objStreamPtr->GetImage(timeout_ms); uint64_t length = ptrGrabResult->GetPayloadSize(); @@ -663,7 +664,7 @@ int ClassGalaxy::SnapImage() string a = e.what(); AddToLog(e.what()); m_objFeatureControlPtr->GetCommandFeature("AcquisitionStop")->Execute(); - //¹Ø±ÕÁ÷²ã²É¼¯ + //关闭æµå±‚采集 m_objStreamPtr->StopGrab(); return DEVICE_ERR; } @@ -671,7 +672,7 @@ int ClassGalaxy::SnapImage() { AddToLog(e.what()); m_objFeatureControlPtr->GetCommandFeature("AcquisitionStop")->Execute(); - //¹Ø±ÕÁ÷²ã²É¼¯ + //关闭æµå±‚采集 m_objStreamPtr->StopGrab(); return DEVICE_ERR; } @@ -690,9 +691,9 @@ void ClassGalaxy::CopyToImageBuffer(CImageDataPointer& objImageDataPointer) std::size_t found = pixelType_.find(subject); //pixelType_.assign(pixelFormat_gx); - //Ïà»úÀàÐÍ-Ã÷È·Ïà»úµÄÊä³öÐÎʽ--´ýÈ·ÈÏ + //相机类型-明确相机的输出形å¼--待确认 GX_VALID_BIT_LIST emValidBits = GX_BIT_0_7; - //Ã÷È·Ïà»úµÄ²Éͼ¸ñʽ + //æ˜Žç¡®ç›¸æœºçš„é‡‡å›¾æ ¼å¼ emValidBits = GetBestValudBit(objImageDataPointer->GetPixelFormat()); if (found != std::string::npos) @@ -707,10 +708,10 @@ void ClassGalaxy::CopyToImageBuffer(CImageDataPointer& objImageDataPointer) memcpy(imgBuffer_, buffer, GetImageBufferSize()); SetProperty(MM::g_Keyword_PixelType, g_PixelType_8bit); } - //20221025´ý¶¨ÆäËûÑÕÉ«¸ñʽ + //20221025å¾…å®šå…¶ä»–é¢œè‰²æ ¼å¼ else if (pixelType_.compare("Mono16") == 0 || pixelType_.compare("Mono12") == 0 || pixelType_.compare("Mono10") == 0) { - //ºÚ°×8-16λ + //黑白8-16ä½ //copy image buffer to a snap buffer allocated by device adapter void* buffer = objImageDataPointer->GetBuffer(); memcpy(imgBuffer_, buffer, GetImageBufferSize()); @@ -751,23 +752,25 @@ void ClassGalaxy::CopyToImageBuffer(CImageDataPointer& objImageDataPointer) } } -int ClassGalaxy::StartSequenceAcquisition(long /* numImages */, double /* interval_ms */, bool /* stopOnOverflow */) { +int ClassGalaxy::StartSequenceAcquisition(long numImages, double interval_ms, bool stopOnOverflow) { try { - AddToLog("ReadyMuiltySequenceAcquisition"); - ImageHandler_ = new CircularBufferInserter(this); + AddToLog("ReadyMultySequenceAcquisition"); + ImageHandler_ = new CircularBufferInserter(this, numImages, stopOnOverflow); //camera_->RegisterImageEventHandler(ImageHandler_, RegistrationMode_Append, Cleanup_Delete); m_objStreamPtr->RegisterCaptureCallback(ImageHandler_, this); - //camera_->StartGrabbing(numImages, GrabStrategy_OneByOne, GrabLoop_ProvidedByInstantCamera); - m_objFeatureControlPtr->GetCommandFeature("AcquisitionStart")->Execute(); - //¿ªÆôÁ÷²ã²É¼¯ - m_objStreamPtr->StartGrab(); int ret = GetCoreCallback()->PrepareForAcq(this); if (ret != DEVICE_OK) { return ret; } + sequenceRunning_ = true; + + //camera_->StartGrabbing(numImages, GrabStrategy_OneByOne, GrabLoop_ProvidedByInstantCamera); + m_objFeatureControlPtr->GetCommandFeature("AcquisitionStart")->Execute(); + //å¼€å¯æµå±‚采集 + m_objStreamPtr->StartGrab(); AddToLog("StartSequenceAcquisition"); } @@ -776,20 +779,24 @@ int ClassGalaxy::StartSequenceAcquisition(long /* numImages */, double /* interv string a = e.what(); AddToLog(e.what()); m_objFeatureControlPtr->GetCommandFeature("AcquisitionStop")->Execute(); - //¹Ø±ÕÁ÷²ã²É¼¯ + //关闭æµå±‚采集 m_objStreamPtr->StopGrab(); + sequenceRunning_ = false; return DEVICE_ERR; } catch (const std::exception& e) { AddToLog(e.what()); m_objFeatureControlPtr->GetCommandFeature("AcquisitionStop")->Execute(); - //¹Ø±ÕÁ÷²ã²É¼¯ + //关闭æµå±‚采集 m_objStreamPtr->StopGrab(); + sequenceRunning_ = false; return DEVICE_ERR; } return DEVICE_OK; } + + int ClassGalaxy::StartSequenceAcquisition(double /* interval_ms */) { try { @@ -797,7 +804,7 @@ int ClassGalaxy::StartSequenceAcquisition(double /* interval_ms */) { //modify by LXM in 20240306 //if (m_bIsOpen) { - //AddToLog("---------------ÅжÏÊÇ·ñ¿ª²É"); + //AddToLog("---------------判断是å¦å¼€é‡‡"); m_objFeatureControlPtr->GetCommandFeature("AcquisitionStop")->Execute(); m_objStreamPtr->StopGrab(); } @@ -811,9 +818,10 @@ int ClassGalaxy::StartSequenceAcquisition(double /* interval_ms */) { if (ret != DEVICE_OK) { return ret; } + sequenceRunning_ = true; //camera_->StartGrabbing(numImages, GrabStrategy_OneByOne, GrabLoop_ProvidedByInstantCamera); m_objFeatureControlPtr->GetCommandFeature("AcquisitionStart")->Execute(); - //¿ªÆôÁ÷²ã²É¼¯ + //å¼€å¯æµå±‚采集 m_objStreamPtr->StartGrab(); AddToLog("StartSequenceAcquisition"); } @@ -822,24 +830,26 @@ int ClassGalaxy::StartSequenceAcquisition(double /* interval_ms */) { string a = e.what(); AddToLog(e.what()); m_objFeatureControlPtr->GetCommandFeature("AcquisitionStop")->Execute(); - //¹Ø±ÕÁ÷²ã²É¼¯ + //关闭æµå±‚采集 m_objStreamPtr->StopGrab(); + sequenceRunning_ = false; return DEVICE_ERR; } catch (const std::exception& e) { AddToLog(e.what()); m_objFeatureControlPtr->GetCommandFeature("AcquisitionStop")->Execute(); - //¹Ø±ÕÁ÷²ã²É¼¯ + //关闭æµå±‚采集 m_objStreamPtr->StopGrab(); + sequenceRunning_ = false; return DEVICE_ERR; } return DEVICE_OK; } + + int ClassGalaxy::StopSequenceAcquisition() { - - if (m_objStreamFeatureControlPtr->GetBoolFeature("StreamIsGrabbing")->GetValue()) { m_objFeatureControlPtr->GetCommandFeature("AcquisitionStop")->Execute(); @@ -848,10 +858,17 @@ int ClassGalaxy::StopSequenceAcquisition() //camera_->DeregisterImageEventHandler(ImageHandler_); m_objStreamPtr->UnregisterCaptureCallback(); } + sequenceRunning_ = false; AddToLog("StopSequenceAcquisition"); return DEVICE_OK; } + +bool ClassGalaxy::IsCapturing() +{ + return sequenceRunning_; +} + int ClassGalaxy::PrepareSequenceAcqusition() { AddToLog("PrepareSequenceAcqusition"); @@ -863,14 +880,14 @@ void ClassGalaxy::ResizeSnapBuffer() { free(imgBuffer_); GetImageSize(); - imageBufferSize_ = Width_ * Height_ * GetImageBytesPerPixel();//Ô­ÏÈÊÇbuffersize + imageBufferSize_ = Width_ * Height_ * GetImageBytesPerPixel();//原先是buffersize imgBuffer_ = malloc(imageBufferSize_); } bool ClassGalaxy::__IsPixelFormat8(GX_PIXEL_FORMAT_ENTRY emPixelFormatEntry) { bool bIsPixelFormat8 = false; - const unsigned PIXEL_FORMATE_BIT = 0x00FF0000; ///<ÓÃÓÚÓ뵱ǰµÄÊý¾Ý¸ñʽ½øÐÐÓëÔËËãµÃµ½µ±Ç°µÄÊý¾ÝλÊý + const unsigned PIXEL_FORMATE_BIT = 0x00FF0000; ///<用于与当å‰çš„æ•°æ®æ ¼å¼è¿›è¡Œä¸Žè¿ç®—得到当å‰çš„æ•°æ®ä½æ•° unsigned uiPixelFormatEntry = (unsigned)emPixelFormatEntry; if ((uiPixelFormatEntry & PIXEL_FORMATE_BIT) == GX_PIXEL_8BIT) { @@ -886,9 +903,9 @@ unsigned char* ClassGalaxy::GetImageBufferFromCallBack(CImageDataPointer& objIma INT64 Width_ = m_objFeatureControlPtr->GetIntFeature("Width")->GetValue(); INT64 Height_ = m_objFeatureControlPtr->GetIntFeature("Height")->GetValue(); - //Ïà»úÀàÐÍ-Ã÷È·Ïà»úµÄÊä³öÐÎʽ--´ýÈ·ÈÏ + //相机类型-明确相机的输出形å¼--待确认 GX_VALID_BIT_LIST emValidBits = GX_BIT_0_7; - //Ã÷È·Ïà»úµÄ²Éͼ¸ñʽ + //æ˜Žç¡®ç›¸æœºçš„é‡‡å›¾æ ¼å¼ emValidBits = GetBestValudBit(objImageDataPointer->GetPixelFormat()); if (colorCamera_) @@ -906,22 +923,22 @@ unsigned char* ClassGalaxy::GetImageBufferFromCallBack(CImageDataPointer& objIma imgBuffer_2 = (BYTE*)objImageDataPointer->ConvertToRaw8(emValidBits); } - // ºÚ°×Ïà»úÐèÒª·­×ªÊý¾ÝºóÏÔʾ + // 黑白相机需è¦ç¿»è½¬æ•°æ®åŽæ˜¾ç¤º for (int i = 0; i < Height_; i++) { - //º¬Òå + //å«ä¹‰ memcpy(m_pImageBuffer + i * Width_, imgBuffer_2 + (Height_ - i - 1) * Width_, (size_t)Width_); return (unsigned char*)imgBuffer_; } } - //»ñȡͼÏñbuffer + //获å–图åƒbuffer return (unsigned char*)imgBuffer_; } const unsigned char* ClassGalaxy::GetImageBuffer() { - //°´ÕÕºÚ°×ÏÔʾ + //按照黑白显示 return (unsigned char*)imgBuffer_; } @@ -980,7 +997,7 @@ int ClassGalaxy::OnBinning(MM::PropertyBase* pProp, MM::ActionType eAct) if (Isgrabbing) { m_objFeatureControlPtr->GetCommandFeature("AcquisitionStop")->Execute(); - //¹Ø±ÕÁ÷²ã²É¼¯ + //关闭æµå±‚采集 m_objStreamPtr->StopGrab(); } pProp->Get(binningFactor_); @@ -989,7 +1006,7 @@ int ClassGalaxy::OnBinning(MM::PropertyBase* pProp, MM::ActionType eAct) BinningVertical->SetValue(val); if (Isgrabbing) { - //ÖØ¿ª + //é‡å¼€ m_objFeatureControlPtr->GetCommandFeature("AcquisitionStart")->Execute(); m_objStreamPtr->StartGrab(); @@ -1123,7 +1140,7 @@ int ClassGalaxy::OnHeight(MM::PropertyBase* pProp, MM::ActionType eAct) if (Isgrabbing) { m_objFeatureControlPtr->GetCommandFeature("AcquisitionStop")->Execute(); - //¹Ø±ÕÁ÷²ã²É¼¯ + //关闭æµå±‚采集 m_objStreamPtr->StopGrab(); //camera_->StopGrabbing(); } @@ -1181,7 +1198,7 @@ int ClassGalaxy::OnPixelType(MM::PropertyBase* pProp, MM::ActionType eAct) if (m_objStreamFeatureControlPtr->GetBoolFeature("StreamIsGrabbing")->GetValue()) { m_objFeatureControlPtr->GetCommandFeature("AcquisitionStop")->Execute(); - //¹Ø±ÕÁ÷²ã²É¼¯ + //关闭æµå±‚采集 m_objStreamPtr->StopGrab(); } //CEnumerationPtr pixelFormat(nodeMap_->GetNode("PixelFormat")); @@ -1192,9 +1209,9 @@ int ClassGalaxy::OnPixelType(MM::PropertyBase* pProp, MM::ActionType eAct) pProp->Get(pixelType_); try { - //´úÂ뱨´í + //代ç æŠ¥é”™ - //ÉèÖÃÐÂÖµ + //设置新值 pixelFormat_->SetValue(pixelType_.c_str()); const char* subject("Bayer"); std::size_t found = pixelType_.find(subject); @@ -1262,7 +1279,7 @@ int ClassGalaxy::OnPixelType(MM::PropertyBase* pProp, MM::ActionType eAct) if (m_objStreamFeatureControlPtr->GetBoolFeature("StreamIsGrabbing")->GetValue()) { - //ÖØ¿ª + //é‡å¼€ m_objFeatureControlPtr->GetCommandFeature("AcquisitionStart")->Execute(); m_objStreamPtr->StartGrab(); } @@ -1413,7 +1430,7 @@ int ClassGalaxy::OnWidth(MM::PropertyBase* pProp, MM::ActionType eAct) if (m_objStreamFeatureControlPtr->GetBoolFeature("StreamIsGrabbing")->GetValue()) { m_objFeatureControlPtr->GetCommandFeature("AcquisitionStop")->Execute(); - //¹Ø±ÕÁ÷²ã²É¼¯ + //关闭æµå±‚采集 m_objStreamPtr->StopGrab(); //camera_->StopGrabbing(); } @@ -1641,7 +1658,7 @@ int ClassGalaxy::ClearROI() void ClassGalaxy::ReduceImageSize(int64_t Width, int64_t Height) { - //´ý¶¨ + //待定 return ; } @@ -1666,7 +1683,7 @@ void ClassGalaxy::RGB24PackedToRGBA(void* destbuffer, void* srcbuffer, CImageDat unsigned int dstOffset = 0; GX_VALID_BIT_LIST emValidBits = GX_BIT_0_7; uint64_t Payloadsize=objImageDataPointer->GetPayloadSize(); - //Ã÷È·Ïà»úµÄ²Éͼ¸ñʽ + //æ˜Žç¡®ç›¸æœºçš„é‡‡å›¾æ ¼å¼ emValidBits = GetBestValudBit(objImageDataPointer->GetPixelFormat()); if (emValidBits!= GX_BIT_0_7) { @@ -1694,19 +1711,19 @@ void ClassGalaxy::RG8ToRGB24Packed(void* destbuffer,CImageDataPointer& objImageD return;*/ //end modify - //RG8תRGB24 + //RG8转RGB24 try { GX_VALID_BIT_LIST emValidBits = GX_BIT_0_7; - //Ã÷È·Ïà»úµÄ²Éͼ¸ñʽ + //æ˜Žç¡®ç›¸æœºçš„é‡‡å›¾æ ¼å¼ emValidBits = GetBestValudBit(objImageDataPointer->GetPixelFormat()); - //ΪÁËÏÔʾ£¬ÐèÒª¶¼×ª³ÉRaw8λ + //为了显示,需è¦éƒ½è½¬æˆRaw8ä½ if (emValidBits!= GX_BIT_0_7) { return; } - //RGB24Packed-´óС³ËÒÔ3 + //RGB24Packed-大å°ä¹˜ä»¥3 void* buffer = objImageDataPointer->ConvertToRGB24(emValidBits, GX_RAW2RGB_NEIGHBOUR, false); RGB24PackedToRGBA(destbuffer, buffer, objImageDataPointer); AddToLog("RG8ToRGB24Packed"); @@ -1740,7 +1757,7 @@ void ClassGalaxy::RG10ToRGB24Packed(void* pRGB24Bufdest, CImageDataPointer& objI { if (0) { - //ת³ÉRGB8*2£¬ÔÚת³ÉRGBA8*2*4 + //转æˆRGB8*2,在转æˆRGBA8*2*4 size_t BufferSize = GetImageSizeLarge() * 3 * sizeof(unsigned short int); void* RGB16 = malloc(BufferSize); @@ -1761,7 +1778,7 @@ void ClassGalaxy::RG10ToRGB24Packed(void* pRGB24Bufdest, CImageDataPointer& objI GX_VALID_BIT_LIST emValidBits = GX_BIT_0_7; - //Ã÷È·Ïà»úµÄ²Éͼ¸ñʽ + //æ˜Žç¡®ç›¸æœºçš„é‡‡å›¾æ ¼å¼ emValidBits = GetBestValudBit(objImageDataPointer->GetPixelFormat()); BYTE* pRGB24Buf2 = (BYTE*)objImageDataPointer->ConvertToRGB24(emValidBits, GX_RAW2RGB_NEIGHBOUR, false); @@ -1820,7 +1837,7 @@ GX_VALID_BIT_LIST ClassGalaxy::GetBestValudBit(GX_PIXEL_FORMAT_ENTRY emPixelForm } case GX_PIXEL_FORMAT_MONO14: { - //ÔÝʱûÓÐÕâÑùµÄÊý¾Ý¸ñʽ´ýÉý¼¶ + //暂时没有这样的数æ®æ ¼å¼å¾…å‡çº§ break; } case GX_PIXEL_FORMAT_MONO16: @@ -1829,7 +1846,7 @@ GX_VALID_BIT_LIST ClassGalaxy::GetBestValudBit(GX_PIXEL_FORMAT_ENTRY emPixelForm case GX_PIXEL_FORMAT_BAYER_GB16: case GX_PIXEL_FORMAT_BAYER_BG16: { - //ÔÝʱûÓÐÕâÑùµÄÊý¾Ý¸ñʽ´ýÉý¼¶ + //暂时没有这样的数æ®æ ¼å¼å¾…å‡çº§ if (emPixelFormatEntry != GX_PIXEL_FORMAT_MONO16) { IsByerFormat = true; @@ -1843,14 +1860,25 @@ GX_VALID_BIT_LIST ClassGalaxy::GetBestValudBit(GX_PIXEL_FORMAT_ENTRY emPixelForm } CircularBufferInserter::CircularBufferInserter(ClassGalaxy* dev) : - dev_(dev) + dev_(dev), + numImages_(-1), + imgCounter_(0), + stopOnOverflow_(false) +{} + +CircularBufferInserter::CircularBufferInserter(ClassGalaxy* dev, long numImages, bool stopOnOverflow) : + dev_(dev), + numImages_(numImages), + imgCounter_(0), + stopOnOverflow_(stopOnOverflow) {} + //--------------------------------------------------------------------------------- /** - \brief ²É¼¯»Øµ÷º¯Êý - \param objImageDataPointer ͼÏñ´¦Àí²ÎÊý - \param pFrame Óû§²ÎÊý - \return ÎÞ + \brief 采集回调函数 + \param objImageDataPointer 图åƒå¤„ç†å‚æ•° + \param pFrame 用户å‚æ•° + \return æ—  */ //---------------------------------------------------------------------------------- void CircularBufferInserter::DoOnImageCaptured(CImageDataPointer& objImageDataPointer, void* pUserParam) @@ -1859,7 +1887,7 @@ void CircularBufferInserter::DoOnImageCaptured(CImageDataPointer& objImageDataPo //dev_->AddToLog("OnImageGrabbed"); // Important: meta data about the image are generated here: Metadata md; - md.put("Camera", ""); + md.put(MM::g_Keyword_Metadata_CameraLabel, ""); md.put(MM::g_Keyword_Metadata_ROI_X, CDeviceUtils::ConvertToString((long)objImageDataPointer->GetWidth())); md.put(MM::g_Keyword_Metadata_ROI_Y, CDeviceUtils::ConvertToString((long)objImageDataPointer->GetHeight())); md.put(MM::g_Keyword_Metadata_ImageNumber, CDeviceUtils::ConvertToString((long)objImageDataPointer->GetFrameID())); @@ -1867,11 +1895,11 @@ void CircularBufferInserter::DoOnImageCaptured(CImageDataPointer& objImageDataPo // Image grabbed successfully ? if (objImageDataPointer->GetStatus()== GX_FRAME_STATUS_SUCCESS) { - //²éѯͼÏñ¸ñʽ + //查询图åƒæ ¼å¼ GX_PIXEL_FORMAT_ENTRY pixelFormat_gx = objImageDataPointer->GetPixelFormat(); dev_->ResizeSnapBuffer(); - //ºÚ°× + //黑白 if (!dev_->colorCamera_) { //copy to intermediate buffer @@ -1880,12 +1908,20 @@ void CircularBufferInserter::DoOnImageCaptured(CImageDataPointer& objImageDataPo (unsigned)dev_->GetImageBytesPerPixel(), 1, md.Serialize().c_str(), FALSE); if (ret == DEVICE_BUFFER_OVERFLOW) { //if circular buffer overflows, just clear it and keep putting stuff in so live mode can continue - dev_->GetCoreCallback()->ClearImageBuffer(dev_); + if (stopOnOverflow_) + { + dev_->StopSequenceAcquisition(); + dev_->LogMessage("Error inserting image into sequence buffer", false); + } + else + { + dev_->GetCoreCallback()->ClearImageBuffer(dev_); + } } } else if (dev_->colorCamera_) { - //²ÊÉ«£¬×¢ÒâÕâÀïÈ«²¿×ª³É8λRGB + //彩色,注æ„这里全部转æˆ8ä½RGB if (dev_->__IsPixelFormat8(pixelFormat_gx)) { dev_->RG8ToRGB24Packed(dev_->imgBuffer_, objImageDataPointer); @@ -1902,22 +1938,25 @@ void CircularBufferInserter::DoOnImageCaptured(CImageDataPointer& objImageDataPo dev_->GetCoreCallback()->ClearImageBuffer(dev_); } } + imgCounter_++; + if (imgCounter_ == numImages_) + { + dev_->StopSequenceAcquisition(); + } } else { - dev_->AddToLog("²ÐÖ¡"); + dev_->AddToLog("残帧"); } +} - - - -} int64_t ClassGalaxy::__GetStride(int64_t nWidth, bool bIsColor) { return bIsColor ? nWidth * 3 : nWidth; } + bool ClassGalaxy::__IsCompatible(BITMAPINFO* pBmpInfo, uint64_t nWidth, uint64_t nHeight) { if (pBmpInfo == NULL @@ -1932,7 +1971,7 @@ bool ClassGalaxy::__IsCompatible(BITMAPINFO* pBmpInfo, uint64_t nWidth, uint64_t void ClassGalaxy::__ColorPrepareForShowImg() { //-------------------------------------------------------------------- - //---------------------------³õʼ»¯bitmapÍ·--------------------------- + //---------------------------åˆå§‹åŒ–bitmap头--------------------------- m_pBmpInfo = (BITMAPINFO*)m_chBmpBuf; m_pBmpInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); m_pBmpInfo->bmiHeader.biWidth = (LONG)Width_; @@ -1972,7 +2011,7 @@ void ClassGalaxy::SaveBmp(CImageDataPointer& objCImageDataPointer, const std::st throw std::runtime_error("Argument is error"); } - //¼ì²éͼÏñÊÇ·ñ¸Ä±ä²¢¸üÐÂBuffer + //检查图åƒæ˜¯å¦æ”¹å˜å¹¶æ›´æ–°Buffer __UpdateBitmap(objCImageDataPointer); emValidBits = GetBestValudBit(objCImageDataPointer->GetPixelFormat()); @@ -1993,7 +2032,7 @@ void ClassGalaxy::SaveBmp(CImageDataPointer& objCImageDataPointer, const std::st { pBuffer = (BYTE*)objCImageDataPointer->ConvertToRaw8(emValidBits); } - // ºÚ°×Ïà»úÐèÒª·­×ªÊý¾ÝºóÏÔʾ + // 黑白相机需è¦ç¿»è½¬æ•°æ®åŽæ˜¾ç¤º for (unsigned int i = 0; i < Height_; i++) { memcpy(m_pImageBuffer + i * Width_, pBuffer + (Height_ - i - 1) * Width_, (size_t)Width_); @@ -2005,23 +2044,23 @@ void ClassGalaxy::SaveBmp(CImageDataPointer& objCImageDataPointer, const std::st BITMAPFILEHEADER stBfh = { 0 }; DWORD dwBytesRead = 0; - stBfh.bfType = (WORD)'M' << 8 | 'B'; //¶¨ÒåÎļþÀàÐÍ + stBfh.bfType = (WORD)'M' << 8 | 'B'; //定义文件类型 stBfh.bfOffBits = colorCamera_ ? sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) - : sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (256 * 4); //¶¨ÒåÎļþÍ·´óСtrueΪ²ÊÉ«,falseΪºÚ°× - stBfh.bfSize = stBfh.bfOffBits + dwImageSize; //Îļþ´óС + : sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (256 * 4); //定义文件头大å°true为彩色,false为黑白 + stBfh.bfSize = stBfh.bfOffBits + dwImageSize; //æ–‡ä»¶å¤§å° DWORD dwBitmapInfoHeader = colorCamera_ ? sizeof(BITMAPINFOHEADER) - : sizeof(BITMAPINFOHEADER) + (256 * 4); //¶¨ÒåBitmapInfoHeader´óСtrueΪ²ÊÉ«,falseΪºÚ°× + : sizeof(BITMAPINFOHEADER) + (256 * 4); //定义BitmapInfoHeader大å°true为彩色,false为黑白 const char* strEn = strFilePath.c_str(); - //½«const char*ת»¯ÎªLPCTSTR + //å°†const char*转化为LPCTSTR size_t length = sizeof(TCHAR) * (strlen(strEn) + 1); LPTSTR tcBuffer = new TCHAR[length]; memset(tcBuffer, 0, length); MultiByteToWideChar(CP_ACP, 0, strEn, (int) strlen(strEn), tcBuffer, (int) length); LPCTSTR pDest = (LPCTSTR)tcBuffer; - //´´½¨Îļþ + //创建文件 HANDLE hFile = ::CreateFile(pDest, GENERIC_WRITE, 0, @@ -2035,7 +2074,7 @@ void ClassGalaxy::SaveBmp(CImageDataPointer& objCImageDataPointer, const std::st throw std::runtime_error("Handle is invalid"); } ::WriteFile(hFile, &stBfh, sizeof(BITMAPFILEHEADER), &dwBytesRead, NULL); - ::WriteFile(hFile, m_pBmpInfo, dwBitmapInfoHeader, &dwBytesRead, NULL); //ºÚ°×ºÍ²ÊÉ«×ÔÊÊÓ¦ + ::WriteFile(hFile, m_pBmpInfo, dwBitmapInfoHeader, &dwBytesRead, NULL); //黑白和彩色自适应 ::WriteFile(hFile, pBuffer, dwImageSize, &dwBytesRead, NULL); CloseHandle(hFile); } @@ -2049,7 +2088,7 @@ void ClassGalaxy::SaveBmp(CImageDataPointer& objCImageDataPointer,void* buffer,c throw std::runtime_error("Argument is error"); } - //¼ì²éͼÏñÊÇ·ñ¸Ä±ä²¢¸üÐÂBuffer + //检查图åƒæ˜¯å¦æ”¹å˜å¹¶æ›´æ–°Buffer __UpdateBitmap(objCImageDataPointer); emValidBits = GetBestValudBit(objCImageDataPointer->GetPixelFormat()); @@ -2070,7 +2109,7 @@ void ClassGalaxy::SaveBmp(CImageDataPointer& objCImageDataPointer,void* buffer,c { pBuffer = (BYTE*)objCImageDataPointer->ConvertToRaw8(emValidBits); } - // ºÚ°×Ïà»úÐèÒª·­×ªÊý¾ÝºóÏÔʾ + // 黑白相机需è¦ç¿»è½¬æ•°æ®åŽæ˜¾ç¤º for (unsigned int i = 0; i < Height_; i++) { memcpy(m_pImageBuffer + i * Width_, pBuffer + (Height_ - i - 1) * Width_, (size_t)Width_); @@ -2082,23 +2121,23 @@ void ClassGalaxy::SaveBmp(CImageDataPointer& objCImageDataPointer,void* buffer,c BITMAPFILEHEADER stBfh = { 0 }; DWORD dwBytesRead = 0; - stBfh.bfType = (WORD)'M' << 8 | 'B'; //¶¨ÒåÎļþÀàÐÍ + stBfh.bfType = (WORD)'M' << 8 | 'B'; //定义文件类型 stBfh.bfOffBits = colorCamera_ ? sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) - : sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (256 * 4); //¶¨ÒåÎļþÍ·´óСtrueΪ²ÊÉ«,falseΪºÚ°× - stBfh.bfSize = stBfh.bfOffBits + dwImageSize; //Îļþ´óС + : sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (256 * 4); //定义文件头大å°true为彩色,false为黑白 + stBfh.bfSize = stBfh.bfOffBits + dwImageSize; //æ–‡ä»¶å¤§å° DWORD dwBitmapInfoHeader = colorCamera_ ? sizeof(BITMAPINFOHEADER) - : sizeof(BITMAPINFOHEADER) + (256 * 4); //¶¨ÒåBitmapInfoHeader´óСtrueΪ²ÊÉ«,falseΪºÚ°× + : sizeof(BITMAPINFOHEADER) + (256 * 4); //定义BitmapInfoHeader大å°true为彩色,false为黑白 const char* strEn = strFilePath.c_str(); - //½«const char*ת»¯ÎªLPCTSTR + //å°†const char*转化为LPCTSTR size_t length = sizeof(TCHAR) * (strlen(strEn) + 1); LPTSTR tcBuffer = new TCHAR[length]; memset(tcBuffer, 0, length); MultiByteToWideChar(CP_ACP, 0, strEn, (int) strlen(strEn), tcBuffer, (int) length); LPCTSTR pDest = (LPCTSTR)tcBuffer; - //´´½¨Îļþ + //创建文件 HANDLE hFile = ::CreateFile(pDest, GENERIC_WRITE, 0, @@ -2112,7 +2151,7 @@ void ClassGalaxy::SaveBmp(CImageDataPointer& objCImageDataPointer,void* buffer,c throw std::runtime_error("Handle is invalid"); } ::WriteFile(hFile, &stBfh, sizeof(BITMAPFILEHEADER), &dwBytesRead, NULL); - ::WriteFile(hFile, m_pBmpInfo, dwBitmapInfoHeader, &dwBytesRead, NULL); //ºÚ°×ºÍ²ÊÉ«×ÔÊÊÓ¦ + ::WriteFile(hFile, m_pBmpInfo, dwBitmapInfoHeader, &dwBytesRead, NULL); //黑白和彩色自适应 //::WriteFile(hFile, pBuffer, dwImageSize, &dwBytesRead, NULL); ::WriteFile(hFile, buffer, dwImageSize, &dwBytesRead, NULL); @@ -2126,23 +2165,23 @@ void ClassGalaxy::SaveRaw(CImageDataPointer& objCImageDataPointer, const std::st throw std::runtime_error("Argument is error"); } - //¼ì²éͼÏñÊÇ·ñ¸Ä±ä²¢¸üÐÂBuffer + //检查图åƒæ˜¯å¦æ”¹å˜å¹¶æ›´æ–°Buffer __UpdateBitmap(objCImageDataPointer); - DWORD dwImageSize = (DWORD)objCImageDataPointer->GetPayloadSize(); // дÈëÎļþµÄ³¤¶È - DWORD dwBytesRead = 0; // Îļþ¶ÁÈ¡µÄ³¤¶È + DWORD dwImageSize = (DWORD)objCImageDataPointer->GetPayloadSize(); // 写入文件的长度 + DWORD dwBytesRead = 0; // 文件读å–的长度 BYTE* pbuffer = (BYTE*)objCImageDataPointer->GetBuffer(); const char* strEn = strFilePath.c_str(); - //½«const char*ת»¯ÎªLPCTSTR + //å°†const char*转化为LPCTSTR size_t length = sizeof(TCHAR) * (strlen(strEn) + 1); LPTSTR tcBuffer = new TCHAR[length]; memset(tcBuffer, 0, length); MultiByteToWideChar(CP_ACP, 0, strEn, (int) strlen(strEn), tcBuffer, (int) length); LPCTSTR pDest = (LPCTSTR)tcBuffer; - // ´´½¨Îļþ + // 创建文件 HANDLE hFile = ::CreateFile(pDest, GENERIC_WRITE, FILE_SHARE_READ, @@ -2151,11 +2190,11 @@ void ClassGalaxy::SaveRaw(CImageDataPointer& objCImageDataPointer, const std::st FILE_ATTRIBUTE_NORMAL, NULL); - if (hFile == INVALID_HANDLE_VALUE) // ´´½¨Ê§°ÜÔò·µ»Ø + if (hFile == INVALID_HANDLE_VALUE) // 创建失败则返回 { throw std::runtime_error("Handle is invalid"); } - else // ±£´æRawͼÏñ + else // ä¿å­˜Rawå›¾åƒ { ::WriteFile(hFile, pbuffer, dwImageSize, &dwBytesRead, NULL); CloseHandle(hFile); diff --git a/DeviceAdapters/DahengGalaxy/ClassGalaxy.h b/DeviceAdapters/DahengGalaxy/ClassGalaxy.h index 10510616c..c19fda432 100644 --- a/DeviceAdapters/DahengGalaxy/ClassGalaxy.h +++ b/DeviceAdapters/DahengGalaxy/ClassGalaxy.h @@ -10,7 +10,7 @@ //#include "DxImageProc.h" //--------------------------------------------------------------------------------- /** -\brief Óû§¼Ì³Ð²É¼¯Ê¼þ´¦ÀíÀ࣬»Øµ÷º¯Êý£¬Öصã¹Ø×¢ ÀàÖÐÓлص÷À࣬¹©²É¼¯Í¼Ïñ +\brief 用户继承采集事件处ç†ç±»ï¼Œå›žè°ƒå‡½æ•°ï¼Œé‡ç‚¹å…³æ³¨ ç±»ä¸­æœ‰å›žè°ƒç±»ï¼Œä¾›é‡‡é›†å›¾åƒ */ //---------------------------------------------------------------------------------- #include @@ -37,7 +37,7 @@ class MODULE_API ClassGalaxy : public CCameraBase public: ClassGalaxy(); - //º¯ÊýµÄÒâÒå-Îö¹¹º¯Êý deleteʱ£¬ÆôÓÃ; + //函数的æ„义-æžæž„函数 delete时,å¯ç”¨; ~ClassGalaxy(void); // MMDevice API @@ -93,7 +93,7 @@ class MODULE_API ClassGalaxy : public CCameraBase //void UpdateTemperature(); /** - * Starts continuous acquisition. liveģʽ£¬µ«ÊÇÒÔϺ¯ÊýûÓÐдҲ»á¿ªÆôliveģʽ£¬¿ªÆô¸Ãº¯ÊýÖ®ºó£¬Èí¼þ»á¿¨ËÀ£¬ÄÚ²¿Óк¯Êý + * Starts continuous acquisition. live模å¼ï¼Œä½†æ˜¯ä»¥ä¸‹å‡½æ•°æ²¡æœ‰å†™ä¹Ÿä¼šå¼€å¯live模å¼ï¼Œå¼€å¯è¯¥å‡½æ•°ä¹‹åŽï¼Œè½¯ä»¶ä¼šå¡æ­»ï¼Œå†…部有函数 */ int StartSequenceAcquisition(long numImages, double interval_ms, bool stopOnOverflow) final ; int StartSequenceAcquisition(double interval_ms) final; @@ -104,7 +104,7 @@ class MODULE_API ClassGalaxy : public CCameraBase //* Flag to indicate whether Sequence Acquisition is currently running. //* Return true when Sequence acquisition is active, false otherwise //*/ - //bool IsCapturing(); + bool IsCapturing(); ////Genicam Callback ////void ResultingFramerateCallback(GenApi::INode* pNode); @@ -143,10 +143,10 @@ class MODULE_API ClassGalaxy : public CCameraBase int OnTriggerDelay(MM::PropertyBase* pProp, MM::ActionType eAct); int OnTriggerFilterRaisingEdge(MM::PropertyBase* pProp, MM::ActionType eAct); - //ΪÁËʵÏÖÔڲɼ¯ÀàÖÐʹÓà + //为了实现在采集类中使用 bool colorCamera_; - CGXFeatureControlPointer m_objFeatureControlPtr; ///< ÊôÐÔ¿ØÖÆÆ÷ - //¸ñʽת»»º¯Êý + CGXFeatureControlPointer m_objFeatureControlPtr; ///< 属性控制器 + //æ ¼å¼è½¬æ¢å‡½æ•° void RG8ToRGB24Packed(void* destbuffer, CImageDataPointer& objImageDataPointer); void CoverRGB16ToRGBA16(unsigned short int* Desbuffer, unsigned short int* Srcbuffer); void RG10ToRGB24Packed(void* destbuffer, CImageDataPointer& objImageDataPointer); @@ -190,17 +190,18 @@ class MODULE_API ClassGalaxy : public CCameraBase std::string TriggerDelay_; std::string TriggerFilterRaisingEdge_; - BITMAPINFO* m_pBmpInfo; /// unsigned char* imgBuffer_2; unsigned char* m_pImageBuffer; - CGXDevicePointer m_objDevicePtr; ///< É豸¾ä±ú - CGXStreamPointer m_objStreamPtr; ///< É豸Á÷ - CGXFeatureControlPointer m_objStreamFeatureControlPtr; ///< Á÷²ã¿ØÖÆÆ÷¶ÔÏó + CGXDevicePointer m_objDevicePtr; ///< 设备å¥æŸ„ + CGXStreamPointer m_objStreamPtr; ///< è®¾å¤‡æµ + CGXFeatureControlPointer m_objStreamFeatureControlPtr; ///< æµå±‚控制器对象 bool m_bIsOpen = false; @@ -224,9 +225,13 @@ class MODULE_API ClassGalaxy : public CCameraBase class CircularBufferInserter : public ICaptureEventHandler { private: ClassGalaxy* dev_; + long numImages_; + long imgCounter_; + bool stopOnOverflow_; public: CircularBufferInserter(ClassGalaxy* dev); + CircularBufferInserter(ClassGalaxy* dev, long numImages, bool stoponOverflow); virtual void DoOnImageCaptured(CImageDataPointer& objImageDataPointer, void* pUserParam); }; \ No newline at end of file diff --git a/DeviceAdapters/DemoCamera/DemoCamera.cpp b/DeviceAdapters/DemoCamera/DemoCamera.cpp index c0444e2e0..cde5082f7 100644 --- a/DeviceAdapters/DemoCamera/DemoCamera.cpp +++ b/DeviceAdapters/DemoCamera/DemoCamera.cpp @@ -1092,7 +1092,7 @@ int CDemoCamera::InsertImage() // Important: metadata about the image are generated here: Metadata md; - md.put("Camera", label); + md.put(MM::g_Keyword_Metadata_CameraLabel, label); md.put(MM::g_Keyword_Elapsed_Time_ms, CDeviceUtils::ConvertToString((timeStamp - sequenceStartTime_).getMsec())); md.put(MM::g_Keyword_Metadata_ROI_X, CDeviceUtils::ConvertToString( (long) roiX_)); md.put(MM::g_Keyword_Metadata_ROI_Y, CDeviceUtils::ConvertToString( (long) roiY_)); diff --git a/DeviceAdapters/ETL/myETL.cpp b/DeviceAdapters/ETL/myETL.cpp index e68208a73..944db6ea7 100644 --- a/DeviceAdapters/ETL/myETL.cpp +++ b/DeviceAdapters/ETL/myETL.cpp @@ -238,7 +238,7 @@ void myETL::DecToHexa(int n, char* output) char hexaDeciNum[100]; // counter for hexadecimal number array - uint32_t num = n; // format très important pour les valeurs négatives + uint32_t num = n; // format très important pour les valeurs négatives int i = 0; while (num != 0) { @@ -265,7 +265,7 @@ void myETL::DecToHexa(int n, char* output) int k = 0; - // on s'attend à un hexa en 2 octets + // on s'attend à un hexa en 2 octets if (i < 4) { output[0] = '0'; diff --git a/DeviceAdapters/ETL/myETL.h b/DeviceAdapters/ETL/myETL.h index 2c8c5aeb7..c6be8b650 100644 --- a/DeviceAdapters/ETL/myETL.h +++ b/DeviceAdapters/ETL/myETL.h @@ -33,7 +33,7 @@ class myETL : public CGenericBase unsigned char* CreateAndSendCurrentCommand(double current); /*int ClearPort(void); - int CheckDeviceStatus(void); // vient de LSTEP, ne correspond pas à ETL + int CheckDeviceStatus(void); // vient de LSTEP, ne correspond pas à ETL int SendCommand(const char* command) const; int QueryCommand(const char* command, std::string& answer) const;*/ diff --git a/DeviceAdapters/Fli/FirstLightImagingCameras.cpp b/DeviceAdapters/Fli/FirstLightImagingCameras.cpp index b85ab904c..48ad3912d 100644 --- a/DeviceAdapters/Fli/FirstLightImagingCameras.cpp +++ b/DeviceAdapters/Fli/FirstLightImagingCameras.cpp @@ -217,7 +217,7 @@ void FirstLightImagingCameras::imageReceived(const uint8_t* image) Metadata md; char label[MM::MaxStrLength]; GetLabel(label); - md.put("Camera", label); + md.put(MM::g_Keyword_Metadata_CameraLabel, label); md.put(MM::g_Keyword_Metadata_ROI_X, CDeviceUtils::ConvertToString((long)w)); md.put(MM::g_Keyword_Metadata_ROI_Y, CDeviceUtils::ConvertToString((long)h)); diff --git a/DeviceAdapters/Hikrobot/Hikrobot.vcxproj b/DeviceAdapters/Hikrobot/Hikrobot.vcxproj new file mode 100644 index 000000000..cb98f9888 --- /dev/null +++ b/DeviceAdapters/Hikrobot/Hikrobot.vcxproj @@ -0,0 +1,115 @@ + + + + + Debug + x64 + + + Release + x64 + + + + {38DCD378-83FE-4C42-8916-1C477A35F65F} + Hikrobot + Win32Proj + Hikrobot + 10.0 + + + + DynamicLibrary + Unicode + true + v142 + false + + + DynamicLibrary + Unicode + v142 + true + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.40219.1 + true + false + + + + X64 + + + Disabled + $(MM_3RDPARTYPUBLIC)\Hikrobot\Windows\Include;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;_USRDLL;MODULE_EXPORTS;%(PreprocessorDefinitions) + EnableFastChecks + + + 4482;4127;4290;4610;4510;4512;%(DisableSpecificWarnings) + /property:MM_3RDPARTYPRIVATE=D:\3rdparty %(AdditionalOptions) + + + %(AdditionalDependencies);MvCameraControl.lib + $(MM_3RDPARTYPUBLIC)\Hikrobot\Windows\Libs\win64;%(AdditionalLibraryDirectories) + Windows + $(OutDir)$(TargetName)$(TargetExt) + + + + + + X64 + + + Disabled + true + Speed + $(MM_3RDPARTYPUBLIC)\Hikrobot\Windows\Include;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;_USRDLL;MODULE_EXPORTS;%(PreprocessorDefinitions) + true + + + 4127;4290;4610;4510;4512;%(DisableSpecificWarnings) + + + %(AdditionalDependencies);MvCameraControl.lib + $(MM_3RDPARTYPUBLIC)\Hikrobot\Windows\Libs\win64;%(AdditionalLibraryDirectories) + Windows + true + true + + + + + + + + + + + + + + {b8c95f39-54bf-40a9-807b-598df2821d55} + + + + + + \ No newline at end of file diff --git a/DeviceAdapters/Hikrobot/HikrobotCamera.cpp b/DeviceAdapters/Hikrobot/HikrobotCamera.cpp new file mode 100644 index 000000000..353c4f78e --- /dev/null +++ b/DeviceAdapters/Hikrobot/HikrobotCamera.cpp @@ -0,0 +1,2548 @@ + +#include +#include +#include "ModuleInterface.h" +#include "DeviceUtils.h" +#include +#include "HikrobotCamera.h" +#include "DeviceBase.h" +#include +#include //std::sort +using namespace std; + +const char* g_HikrobotCameraDeviceName = "HikrobotCamera"; + +static const char* g_PropertyChannel = "PropertyNAme"; +static const char* g_PixelType_8bit = "8bit mono"; +static const char* g_PixelType_10bit = "10bit mono"; +static const char* g_PixelType_12bit = "12bit mono"; +static const char* g_PixelType_16bit = "16bit mono"; +static const char* g_PixelType_10packedbit = "10bit mono"; +static const char* g_PixelType_12packedbit = "12bit mono"; +static const char* g_PixelType_8bitRGBA = "8bitBGRA"; +static const char* g_PixelType_8bitRGB = "8bitRGB"; +static const char* g_PixelType_8bitBGR = "8bitBGR"; + + + +#define MONO_COMPONENTS 1 //mono8 å ç”¨1个组件,æ¯ä¸ªé€šé“是1个组件 +#define MONO_CONVERTED_DEPTH 8 // mono8å ç”¨8字节 +#define MONO_IMAGE_BYTES_PERPIXEL 1 // 使用mono8的字节数 +#define MONO_CONVERTED_FORMAT PixelType_Gvsp_Mono8 + + +#define COLOR_COMPONENTS 4 //RGBA å ç”¨4个组件,æ¯ä¸ªé€šé“是1个组件 +#define COLOR_CONVERTED_DEPTH 32 //RGBA çš„å­—èŠ‚æ•°é‡ +#define COLOR_IMAGE_BYTES_PERPIXEL 4 // 使用RGBA8的字节数 +#define COLOR_CONVERTED_FORMAT PixelType_Gvsp_RGBA8_Packed //32ä½ï¼Œ å¯èƒ½åº•å±‚ CircularBuffer::InsertMultiChannel é™åˆ¶ 或者其他原因,导致 转æ¢æˆRGB32å¼‚å¸¸ï¼Œå¾…åˆ†æž + + +#if 0 +/* 下é¢é…ç½® pok */ +#define COLOR_COMPONENTS 3 //RGB æ¯ä¸ªé€šé“是1个组件 +#define COLOR_CONVERTED_DEPTH 24 +#define COLOR_IMAGE_BYTES_PERPIXEL 3 +#define COLOR_CONVERTED_FORMAT PixelType_Gvsp_RGB8_Packed +#endif + + + + +/////////////////////////////////////////////////////////////////////////////// +// Exported MMDevice API +/////////////////////////////////////////////////////////////////////////////// + +MODULE_API void InitializeModuleData() +{ + RegisterDevice(g_HikrobotCameraDeviceName, MM::CameraDevice, "Hikrobot Camera"); +} + +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_HikrobotCameraDeviceName) == 0) { + // create camera + return new HikrobotCamera(); + } + // ...supplied name not recognized + return 0; +} + +MODULE_API void DeleteDevice(MM::Device* pDevice) +{ + delete pDevice; +} + +/////////////////////////////////////////////////////////////////////////////// +// HikrobotCamera implementation +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +/** +* Constructor. +*/ +HikrobotCamera::HikrobotCamera() : + CCameraBase(), + maxWidth_(0), + maxHeight_(0), + exposure_us_(0), + exposureMax_(0), + exposureMin_(0), + gainMax_(0), + gainMin_(0), + m_nbitDepth(MONO_CONVERTED_DEPTH), + temperatureState_("Undefined"), + reverseX_("0"), + reverseY_("0"), + imgBuffer_(NULL), + pixelType_("Undefined"), + sensorReadoutMode_("Undefined"), + shutterMode_("None"), + m_bInitialized(false), + m_pCamera(new CMvCamera()), + m_bGrabbing(false), + m_hImageRecvThreadHandle(NULL), + m_bRecvRuning(false), + m_pConvertData(NULL), + m_nConvertDataLen(0), + m_nComponents(MONO_COMPONENTS) +{ + // call the base class method to set-up default error codes/messages + InitializeDefaultErrorMessages(); + SetErrorText(ERR_SERIAL_NUMBER_REQUIRED, "Serial number is required"); + SetErrorText(ERR_SERIAL_NUMBER_NOT_FOUND, "No camera with the given serial number was found"); + SetErrorText(ERR_CANNOT_CONNECT, "Cannot connect to camera; it may be in use"); + + CreateStringProperty("SerialNumber", "Undefined", false, 0, true); + + EnumDevice(); +} + +HikrobotCamera::~HikrobotCamera() +{ + + m_bRecvRuning = false; + if (m_hImageRecvThreadHandle) + { + WaitForSingleObject(m_hImageRecvThreadHandle, INFINITE); + CloseHandle(m_hImageRecvThreadHandle); + m_hImageRecvThreadHandle = NULL; + } + + if (NULL != m_pCamera) + { + delete m_pCamera; + m_pCamera = NULL; + } + + if (NULL != imgBuffer_) + { + free(imgBuffer_); + imgBuffer_ = NULL; + } + + if (NULL != m_pConvertData) + { + free(m_pConvertData); + m_pConvertData = NULL; + } + +} + +int HikrobotCamera::EnumDevice() +{ + memset(&m_stDevList, 0, sizeof(MV_CC_DEVICE_INFO_LIST)); + CMvCamera::EnumDevices(MV_GIGE_DEVICE|MV_USB_DEVICE, &m_stDevList); + bool bFirst = true; + if (0 == m_stDevList.nDeviceNum) + { + MvWriteLog(__FILE__, __LINE__, m_chDevID, "No camera present."); + } + + for (unsigned int i = 0; i < m_stDevList.nDeviceNum; i++) + { + MV_CC_DEVICE_INFO* pDeviceInfo = m_stDevList.pDeviceInfo[i]; + if (NULL == pDeviceInfo) + { + continue; + } + + string chSerialNumber; + + if (MV_GIGE_DEVICE == pDeviceInfo->nTLayerType) + { + chSerialNumber = (char*)m_stDevList.pDeviceInfo[i]->SpecialInfo.stGigEInfo.chSerialNumber; + } + else if (MV_USB_DEVICE == pDeviceInfo->nTLayerType) + { + chSerialNumber = (char*)m_stDevList.pDeviceInfo[i]->SpecialInfo.stUsb3VInfo.chSerialNumber; + } + else + { + continue; // not support. + } + + AddAllowedValue("SerialNumber", chSerialNumber.c_str()); + if (bFirst) + { + SetProperty("SerialNumber", chSerialNumber.c_str()); + bFirst = false; + } + } + + return DEVICE_OK; +} + + +/** +* Obtains device name. +*/ +void HikrobotCamera::GetName(char* name) const +{ + CDeviceUtils::CopyLimitedString(name, g_HikrobotCameraDeviceName); //增加说明: 此处是æ’件的å字,具体相机åºåˆ—å·æ˜¯éœ€è¦ (GetProperty("SerialNumber", serialNumber);) 中选择的。 (basler也是åŒæ ·çš„逻辑) +} + +/** +* Initializes the hardware. +*/ +int HikrobotCamera::Initialize() +{ + if (m_bInitialized) + { + MvWriteLog(__FILE__, __LINE__, m_chDevID, "Initialize has already m_bInitialized"); + return DEVICE_OK; + } + + char serialNumber[MM::MaxStrLength] = {0}; + GetProperty("SerialNumber", serialNumber); + if (strlen(serialNumber) == 0 || strcmp(serialNumber, "Undefined") == 0) + return ERR_SERIAL_NUMBER_REQUIRED; + + int nIdx = 0; + + string chCurrentSerialNumber; + + for (unsigned int i = 0; i < m_stDevList.nDeviceNum; i++) + { + chCurrentSerialNumber = ""; + + + if (MV_GIGE_DEVICE == m_stDevList.pDeviceInfo[i]->nTLayerType) + { + chCurrentSerialNumber = (char*)m_stDevList.pDeviceInfo[i]->SpecialInfo.stGigEInfo.chSerialNumber; + } + else + { + chCurrentSerialNumber = (char*)m_stDevList.pDeviceInfo[i]->SpecialInfo.stUsb3VInfo.chSerialNumber; + } + + if (strcmp(chCurrentSerialNumber.c_str(), serialNumber) == 0) + { + nIdx = i; + break; + } + } + + if (!m_pCamera->IsDeviceAccessible(m_stDevList.pDeviceInfo[nIdx], MV_ACCESS_Exclusive)) + { + MvWriteLog(__FILE__, __LINE__, m_chDevID, "Device is not Accessible"); + return DEVICE_ERR; + } + + stringstream msg; + string strUsrDefName; + string strManufactureName; + string strModeName; + string strSerialNumber; + + if (MV_GIGE_DEVICE == m_stDevList.pDeviceInfo[nIdx]->nTLayerType) + { + strUsrDefName = (char*)m_stDevList.pDeviceInfo[nIdx]->SpecialInfo.stGigEInfo.chUserDefinedName; + strManufactureName = (char*)m_stDevList.pDeviceInfo[nIdx]->SpecialInfo.stGigEInfo.chManufacturerName; + strModeName = (char*)m_stDevList.pDeviceInfo[nIdx]->SpecialInfo.stGigEInfo.chModelName; + strSerialNumber = (char*)m_stDevList.pDeviceInfo[nIdx]->SpecialInfo.stGigEInfo.chSerialNumber; + } + else + { + strUsrDefName = (char*)m_stDevList.pDeviceInfo[nIdx]->SpecialInfo.stUsb3VInfo.chUserDefinedName; + strManufactureName = (char*)m_stDevList.pDeviceInfo[nIdx]->SpecialInfo.stUsb3VInfo.chManufacturerName; + strModeName = (char*)m_stDevList.pDeviceInfo[nIdx]->SpecialInfo.stUsb3VInfo.chModelName; + strSerialNumber = (char*)m_stDevList.pDeviceInfo[nIdx]->SpecialInfo.stUsb3VInfo.chSerialNumber; + } + + + MvWriteLog(__FILE__, __LINE__, m_chDevID,"Begin Connect Camera UsrName[%s] ManufactureName[%s] strModeName[%s] strSerialNumber[%s]", strUsrDefName, strManufactureName, strModeName, strSerialNumber); + + // Name + int ret = CreateProperty(MM::g_Keyword_Name, g_HikrobotCameraDeviceName, MM::String, true); + if (DEVICE_OK != ret) + { + return ret; + } + + + // Description + ret = CreateProperty(MM::g_Keyword_Description, "Hikrobot Camera device adapter", MM::String, true); + if (DEVICE_OK != ret) + { + return ret; + } + + // Serial Number + ret = CreateProperty(MM::g_Keyword_CameraID, strSerialNumber.c_str(), MM::String, true); + if (DEVICE_OK != ret) + { + return ret; + } + + assert(nIdx < m_stDevList.nDeviceNum); + int nRet = m_pCamera->Open(m_stDevList.pDeviceInfo[nIdx]); + if (MV_OK != nRet) + { + return DEVICE_ERR; + } + + string strBasiceLog = " [ "+ strModeName + " " + strSerialNumber + " ] "; + SetLogBasicInfo(strBasiceLog); + + sprintf_s(m_chDevID, sizeof(m_chDevID), " %s(%s) ", strModeName.c_str(), strSerialNumber.c_str()); //ä¿å­˜ç›¸æœºçš„åºåˆ—å·ï¼Œåž‹å·ä¿¡æ¯ + + + //Sensor size + MVCC_INTVALUE_EX WidthMax = { 0 }; + m_pCamera->GetIntValue("WidthMax", &WidthMax); + maxWidth_ = WidthMax.nCurValue; + + MVCC_INTVALUE_EX stParam = { 0 }; + m_pCamera->GetIntValue("Width", &stParam); + + if (IsAvailable("Width")) + { + CPropertyAction* pAct = new CPropertyAction(this, &HikrobotCamera::OnWidth); + ret = CreateProperty("SensorWidth", CDeviceUtils::ConvertToString((int)stParam.nCurValue), MM::Integer, false, pAct); + SetPropertyLimits("SensorWidth", (double)stParam.nMin, (double)stParam.nMax); + assert(ret == DEVICE_OK); + } + + + MVCC_INTVALUE_EX HeightMax = {0}; + m_pCamera->GetIntValue("HeightMax", &HeightMax); + maxHeight_ = HeightMax.nCurValue; + + memset(&stParam, 0, sizeof(MVCC_INTVALUE_EX)); + m_pCamera->GetIntValue("Height", &stParam); + if (IsAvailable("Height")) + { + CPropertyAction* pAct = new CPropertyAction(this, &HikrobotCamera::OnHeight); + ret = CreateProperty("SensorHeight", CDeviceUtils::ConvertToString((int)stParam.nCurValue), MM::Integer, false, pAct); + SetPropertyLimits("SensorHeight", (double)stParam.nMin, (double)stParam.nMax); + assert(ret == DEVICE_OK); + } + //end of Sensor size + + MVCC_FLOATVALUE stFloatValue = { 0.0 }; + m_pCamera->GetFloatValue("ExposureTime", &stFloatValue); + exposureMin_ = stFloatValue.fMin; + exposureMax_ = stFloatValue.fMax; + exposure_us_ = stFloatValue.fCurValue; + + //Pixel type + CPropertyAction* pAct = new CPropertyAction(this, &HikrobotCamera::OnPixelType); + ret = CreateProperty(MM::g_Keyword_PixelType, "NA", MM::String, false, pAct); + assert(ret == DEVICE_OK); + + vector pixelTypeValues; + + MVCC_ENUMVALUE stEnumValue = { 0 }; + MVCC_ENUMENTRY stPixelFormatInfo = { 0 }; + + + m_pCamera->GetEnumValue("PixelFormat", &stEnumValue); + for (int i = stEnumValue.nSupportedNum - 1; i >= 0; i--) + //for (int i = 0; i < stEnumValue.nSupportedNum; i++) + { + stPixelFormatInfo.nValue = stEnumValue.nSupportValue[i]; + m_pCamera->GetEnumEntrySymbolic("PixelFormat", &stPixelFormatInfo); + string strPixelFormatInfo = (char*)stPixelFormatInfo.chSymbolic; + pixelTypeValues.push_back(strPixelFormatInfo); + + if (stEnumValue.nCurValue == stEnumValue.nSupportValue[i]) + { + pixelType_ = strPixelFormatInfo; + MvWriteLog(__FILE__, __LINE__, m_chDevID, "Camera Default pixelType_ %s", strPixelFormatInfo.c_str()); + } + + MvWriteLog(__FILE__, __LINE__, m_chDevID, "GetEnumEntrySymbolic and pixelType_ %s nCurValue[%d] nSupportValue [%d]", strPixelFormatInfo.c_str(), stEnumValue.nCurValue, stEnumValue.nSupportValue[i]); + } + + + SetAllowedValues(MM::g_Keyword_PixelType, pixelTypeValues); + + /////TestPattern////// + MVCC_ENUMVALUE stTestPattern = { 0 }; + m_pCamera->GetEnumValue("TestPattern", &stTestPattern); + if (IsWritable("TestPattern")) + { + if (IsAvailable("TestPattern")) + { + pAct = new CPropertyAction(this, &HikrobotCamera::OnTestPattern); + ret = CreateProperty("TestPattern", "NA", MM::String, false, pAct); + vector TestPatternVals; + TestPatternVals.push_back("Off"); //å‚考basler,先把off放入vectorï¼› 在循环中放入off,应该也OKï¼› + MVCC_ENUMENTRY Entry = { 0 }; + + for (unsigned int i = 0; i < stTestPattern.nSupportedNum; i++) + { + Entry.nValue = stTestPattern.nSupportValue[i]; + m_pCamera->GetEnumEntrySymbolic("TestPattern", &Entry); + string strValue = Entry.chSymbolic; + if (IsAvailable(strValue.c_str()) && strValue != "Off") + { + TestPatternVals.push_back(strValue); + } + } + SetAllowedValues("TestPattern", TestPatternVals); + } + } + + + /////AutoGain////// + MVCC_ENUMVALUE gainAuto = {0}; + m_pCamera->GetEnumValue("GainAuto", &gainAuto); + if (IsWritable("GainAuto")) + { + + if (/*gainAuto != NULL && */IsAvailable("GainAuto")) + { + pAct = new CPropertyAction(this, &HikrobotCamera::OnAutoGain); + ret = CreateProperty("GainAuto", "NA", MM::String, false, pAct); + vector LSPVals; + LSPVals.push_back("Off"); + //gainAuto->GetEntries(entries); + MVCC_ENUMENTRY gainEntry = { 0 }; + + for (unsigned int i = 0; i < gainAuto.nSupportedNum; i++) + { + gainEntry.nValue = gainAuto.nSupportValue[i]; + m_pCamera->GetEnumEntrySymbolic("GainAuto", &gainEntry); + string strValue = gainEntry.chSymbolic; + if (IsAvailable(strValue.c_str()) && strValue != "Off") + { + LSPVals.push_back(strValue); + } + } + SetAllowedValues("GainAuto", LSPVals); + } + } + + + /////AutoExposure////// + MVCC_ENUMVALUE ExposureAuto = { 0 }; + m_pCamera->GetEnumValue("ExposureAuto", &ExposureAuto); + if (IsWritable("ExposureAuto")) + { + + if (/*ExposureAuto != NULL && */IsAvailable("ExposureAuto")) + { + pAct = new CPropertyAction(this, &HikrobotCamera::OnAutoExpore); + ret = CreateProperty("ExposureAuto", "NA", MM::String, false, pAct); + vector LSPVals; + LSPVals.push_back("Off"); + + MVCC_ENUMENTRY entry; + + for (unsigned int i = 0;i < ExposureAuto.nSupportedNum; i++) + { + entry.nValue = ExposureAuto.nSupportValue[i]; + m_pCamera->GetEnumEntrySymbolic("ExposureAuto", &entry); + string strValue = entry.chSymbolic; + if (IsAvailable(strValue.c_str()) && strValue != "Off") + { + LSPVals.push_back(strValue); + } + } + SetAllowedValues("ExposureAuto", LSPVals); + } + } + + //get gain limits and value + if (IsAvailable("Gain")) + { + MVCC_FLOATVALUE gain; + m_pCamera->GetFloatValue("Gain", &gain); + + gainMax_ = gain.fMax; + gainMin_ = gain.fMin; + gain_ = gain.fCurValue; + } + else if (IsAvailable("GainRaw")) + { + MVCC_FLOATVALUE GainRaw; + m_pCamera->GetFloatValue("GainRaw", &GainRaw); + gainMax_ = (double)GainRaw.fMax; + gainMin_ = (double)GainRaw.fMin; + gain_ = (double)GainRaw.fCurValue; + } + + //make property + pAct = new CPropertyAction(this, &HikrobotCamera::OnGain); + ret = CreateProperty(MM::g_Keyword_Gain, "1.0", MM::Float, false, pAct); + SetPropertyLimits(MM::g_Keyword_Gain, gainMin_, gainMax_); + + /////Offset////// + MVCC_FLOATVALUE BlackLevel = {0}; + m_pCamera->GetFloatValue("BlackLevel", &BlackLevel); + MVCC_FLOATVALUE BlackLevelRaw = { 0 }; + m_pCamera->GetFloatValue("BlackLevelRaw", &BlackLevelRaw); + + + if (IsAvailable("BlackLevel")) + { + offsetMax_ = BlackLevel.fMax; + offsetMin_ = BlackLevel.fMin; + offset_ = BlackLevel.fCurValue; + + } + else if (IsAvailable("BlackLevelRaw")) + { + offsetMax_ = (double)BlackLevelRaw.fMax; + offsetMin_ = (double)BlackLevelRaw.fMin; + offset_ = (double)BlackLevelRaw.fCurValue; + } + + //make property + pAct = new CPropertyAction(this, &HikrobotCamera::OnOffset); + ret = CreateProperty(MM::g_Keyword_Offset, "1.0", MM::Float, false, pAct); + SetPropertyLimits(MM::g_Keyword_Offset, offsetMin_, offsetMax_); + + + ////Sensor readout////// + if (IsAvailable("SensorReadoutMode")) + { + pAct = new CPropertyAction(this, &HikrobotCamera::OnSensorReadoutMode); + ret = CreateProperty("SensorReadoutMode", "NA", MM::String, false, pAct); + vector vals; + // vals.push_back("Off"); // 海康相机无这个节点,ä¸ä¼šèµ°åˆ°è¿™ä¸ªåˆ†æ”¯ä¸­; å‚考basler分支,basler无添加off节点 + MVCC_ENUMVALUE SensorReadoutMode = { 0 }; + m_pCamera->GetEnumValue("SensorReadoutMode", &SensorReadoutMode); + + for (unsigned int i = 0; i < SensorReadoutMode.nSupportedNum; i++) + { + MVCC_ENUMENTRY entry; + entry.nValue = SensorReadoutMode.nSupportValue[i]; + m_pCamera->GetEnumEntrySymbolic("SensorReadoutMode", &entry); + string strValue = entry.chSymbolic; + if (IsAvailable(strValue.c_str()) && strValue != "Off") + { + vals.push_back(strValue); + } + } + SetAllowedValues("SensorReadoutMode", vals); + } + + MVCC_ENUMVALUE LightSourcePreset = { 0 }; + m_pCamera->GetEnumValue("LightSourcePreset", &LightSourcePreset); + if (/*LightSourcePreset != NULL && */IsAvailable("LightSourcePreset")) + { + pAct = new CPropertyAction(this, &HikrobotCamera::OnLightSourcePreset); + ret = CreateProperty("LightSourcePreset", "NA", MM::String, false, pAct); + vector LSPVals; + LSPVals.push_back("Off"); + MVCC_ENUMENTRY entry; + for (unsigned int i = 0;i < LightSourcePreset.nSupportedNum; i++) + { + entry.nValue = LightSourcePreset.nSupportValue[i]; + m_pCamera->GetEnumEntrySymbolic("LightSourcePreset", &entry); + string strValue = entry.chSymbolic; + if (IsAvailable(strValue.c_str()) && strValue != "Off") + { + LSPVals.push_back(strValue); + } + } + SetAllowedValues("LightSourcePreset", LSPVals); + } + + + /////Trigger Mode////// + MVCC_ENUMVALUE TriggerMode = {0}; + m_pCamera->GetEnumValue("TriggerMode", &TriggerMode); + if (IsAvailable("TriggerMode")) + { + pAct = new CPropertyAction(this, &HikrobotCamera::OnTriggerMode); + ret = CreateProperty("TriggerMode", "Off", MM::String, false, pAct); + vector LSPVals; + LSPVals.push_back("Off"); + LSPVals.push_back("On"); + SetAllowedValues("TriggerMode", LSPVals); + } + + /////Trigger Source////// + MVCC_ENUMVALUE triggersource = {0}; + m_pCamera->GetEnumValue("TriggerSource", &triggersource); + MVCC_ENUMENTRY triggersourceEntry = { 0 }; + + if (IsWritable("TriggerSource")) + { + if (IsAvailable("TriggerSource")) + { + pAct = new CPropertyAction(this, &HikrobotCamera::OnTriggerSource); + ret = CreateProperty("TriggerSource", "NA", MM::String, false, pAct); + vector LSPVals; + for (unsigned int i = 0; i < triggersource.nSupportedNum; i++) + { + triggersourceEntry.nValue = triggersource.nSupportValue[i]; + m_pCamera->GetEnumEntrySymbolic("TriggerSource", &triggersourceEntry); + string strEntry = triggersourceEntry.chSymbolic; + + if (IsAvailable(strEntry.c_str()) /*&& strEntry.find("Software") == std::string::npos*/ ) + { + MvWriteLog(__FILE__, __LINE__, m_chDevID, "Init find OnTriggerSource: %s ", strEntry.c_str()); + + LSPVals.push_back(strEntry); + } + } + SetAllowedValues("TriggerSource", LSPVals); + } + } + + ////Shutter mode////// + MVCC_ENUMVALUE shutterMode = {0}; + m_pCamera->GetEnumValue("ShutterMode",&shutterMode); + if (IsAvailable("ShutterMode")) + { + pAct = new CPropertyAction(this, &HikrobotCamera::OnShutterMode); + ret = CreateProperty("ShutterMode", "NA", MM::String, false, pAct); + vector shutterVals; + + MVCC_ENUMENTRY entry = { 0 }; + for (unsigned int i = 0; i < shutterMode.nSupportedNum; i++) + { + entry.nValue = shutterMode.nSupportValue[i]; + m_pCamera->GetEnumEntrySymbolic("ShutterMode", &entry); + } + + std::string strValue = entry.chSymbolic; + if ( strValue.compare("Global") && IsAvailable("Global")) + { + shutterVals.push_back("Global"); + } + if (strValue.compare("Rolling") && IsAvailable("Rolling")) + { + shutterVals.push_back("Rolling"); + } + if (strValue.compare("GlobalResetRelease") && IsAvailable("GlobalResetRelease")) + { + shutterVals.push_back("GlobalResetRelease"); + } + SetAllowedValues("ShutterMode", shutterVals); + } + + /////Reverse X////// + if (IsAvailable("ReverseX")) + { + pAct = new CPropertyAction(this, &HikrobotCamera::OnReverseX); + ret = CreateProperty("ReverseX", "0", MM::String, false, pAct); + vector reverseXVals; + reverseXVals.push_back("0"); + reverseXVals.push_back("1"); + SetAllowedValues("ReverseX", reverseXVals); + } + + /////Reverse Y////// + if (IsAvailable("ReverseY")) + { + pAct = new CPropertyAction(this, &HikrobotCamera::OnReverseY); + ret = CreateProperty("ReverseY", "0", MM::String, false, pAct); + vector reverseYVals; + reverseYVals.push_back("0"); + reverseYVals.push_back("1"); + SetAllowedValues("ReverseY", reverseYVals); + } + + //////ResultingFramerate + if (IsAvailable("ResultingFrameRateAbs")) + { + + MVCC_INTVALUE_EX ResultingFrameRatePrevious = {0}; + m_pCamera->GetIntValue("ResultingFrameRateAbs", &ResultingFrameRatePrevious); + + std::ostringstream oss; + oss << ResultingFrameRatePrevious.nCurValue; + pAct = new CPropertyAction(this, &HikrobotCamera::OnResultingFramerate); + ret = CreateProperty("ResultingFrameRateAbs", oss.str().c_str(), MM::String, true, pAct); + if (DEVICE_OK != ret) + { + return ret; + } + + } + + //////ResultingFramerate + if (IsAvailable("ResultingFrameRate")) + { + + MVCC_INTVALUE_EX ResultingFrameRatePrevious = { 0 }; + m_pCamera->GetIntValue("ResultingFrameRate", &ResultingFrameRatePrevious); + + std::ostringstream oss; + oss << ResultingFrameRatePrevious.nCurValue; + pAct = new CPropertyAction(this, &HikrobotCamera::OnResultingFramerate); + ret = CreateProperty("ResultingFrameRate", oss.str().c_str(), MM::String, true, pAct); + if (DEVICE_OK != ret) + { + return ret; + } + + } + + /////Set Acquisition AcquisitionFrameRateEnable////// + if (IsAvailable("AcquisitionFrameRateEnable")) + { + pAct = new CPropertyAction(this, &HikrobotCamera::OnAcqFramerateEnable); + ret = CreateProperty("AcquisitionFramerateEnable", "0", MM::String, false, pAct); + vector setAcqFrmVals; + setAcqFrmVals.push_back("0"); + setAcqFrmVals.push_back("1"); + SetAllowedValues("AcquisitionFramerateEnable", setAcqFrmVals); + } + + /////Acquisition Frame rate////// + { + if (IsAvailable("AcquisitionFrameRate")) + { + MVCC_INTVALUE_EX AcquisitionFrameRate = { 0 }; + m_pCamera->GetIntValue("AcquisitionFrameRate", &AcquisitionFrameRate); + // it is not necessary to use full range to + acqFramerateMax_ = AcquisitionFrameRate.nMax; + acqFramerateMin_ = AcquisitionFrameRate.nMin; + acqFramerate_ = AcquisitionFrameRate.nCurValue; + + } + else if (IsAvailable("AcquisitionFrameRateAbs")) + { + MVCC_INTVALUE_EX AcquisitionFrameRateAbs = { 0 }; + m_pCamera->GetIntValue("AcquisitionFrameRateAbs", &AcquisitionFrameRateAbs); + acqFramerateMax_ = AcquisitionFrameRateAbs.nMax; + acqFramerateMin_ = AcquisitionFrameRateAbs.nMin; + acqFramerate_ = AcquisitionFrameRateAbs.nCurValue; + + } + pAct = new CPropertyAction(this, &HikrobotCamera::OnAcqFramerate); + ret = CreateProperty("AcquisitionFramerate", "100", MM::String, false, pAct); + //SetPropertyLimits("AcquisitionFramerate", acqFramerateMin_, acqFramerateMax_); + assert(ret == DEVICE_OK); + } + + //// binning + pAct = new CPropertyAction(this, &HikrobotCamera::OnBinning); + ret = CreateProperty(MM::g_Keyword_Binning, "1", MM::Integer, false, pAct); + SetPropertyLimits(MM::g_Keyword_Binning, 1, 1); + assert(ret == DEVICE_OK); + vector binValues; + MVCC_ENUMVALUE BinningHorizontal = { 0 }; + m_pCamera->GetEnumValue("BinningHorizontal", &BinningHorizontal); + + MVCC_ENUMVALUE BinningVertical = { 0 }; + m_pCamera->GetEnumValue("BinningVertical", &BinningVertical); + + if (IsAvailable("BinningHorizontal") && IsAvailable("BinningVertical")) + { + + //assumed that BinningHorizontal and BinningVertical allow same steps + int64_t min = 0;// = BinningHorizontal->GetMin(); + int64_t max = 0;// = BinningHorizontal->GetMax(); + const int num = BinningHorizontal.nSupportedNum; + std::vector vec; + for (unsigned int i = 0; i < BinningHorizontal.nSupportedNum; i++) + { + vec.push_back(BinningHorizontal.nSupportValue[i]); + } + std::sort(vec.begin(), vec.end()); + min = vec[0]; + max = vec[BinningHorizontal.nSupportedNum - 1]; + + MvWriteLog(__FILE__, __LINE__, m_chDevID, "binning range: %lld - %lld", min, max); + + SetPropertyLimits(MM::g_Keyword_Binning, (double)min, (double)max); + + for (int x = 1; x <= max; x++) + { + std::ostringstream oss; + oss << x; + binValues.push_back(oss.str()); + AddAllowedValue(MM::g_Keyword_Binning, oss.str().c_str()); + } + binningFactor_.assign(CDeviceUtils::ConvertToString((long)BinningHorizontal.nCurValue)); + CheckForBinningMode(pAct); + } + else + { + binValues.push_back("1"); + binningFactor_.assign("1"); + } + + if (m_pCamera->EnumerateTls() & MV_GIGE_DEVICE) + { + MVCC_INTVALUE_EX GevSCPD = { 0 }; + m_pCamera->GetIntValue("GevSCPD", &GevSCPD); + if (IsAvailable("GevSCPD")) + { + pAct = new CPropertyAction(this, &HikrobotCamera::OnInterPacketDelay); + ret = CreateProperty("InterPacketDelay", CDeviceUtils::ConvertToString((long)GevSCPD.nCurValue), MM::Integer, false, pAct); + SetPropertyLimits("InterPacketDelay", (double)GevSCPD.nMin, (double)GevSCPD.nMax); + assert(ret == DEVICE_OK); + } + } + + // synchronize all properties + // -------------------------- + ret = UpdateStatus(); + if (DEVICE_OK != ret) + { + return ret; + } + + //preparation for snaps + ResizeSnapBuffer(); + m_bInitialized = true; + + MvWriteLog(__FILE__, __LINE__, m_chDevID, "HikrobotCamera::Initialize"); + + return DEVICE_OK; +} + + +int HikrobotCamera::CheckForBinningMode(CPropertyAction* pAct) +{ + // Binning Mode + MVCC_ENUMVALUE BinningModeHorizontal = { 0 }; + m_pCamera->GetEnumValue("BinningModeHorizontal", &BinningModeHorizontal); + + MVCC_ENUMVALUE BinningModeVertical = { 0 }; + m_pCamera->GetEnumValue("BinningModeVertical", &BinningModeVertical); + + if (IsAvailable("BinningModeVertical") && IsAvailable("BinningModeHorizontal")) + { + pAct = new CPropertyAction(this, &HikrobotCamera::OnBinningMode); + + vector LSPVals; + // assumed BinningHorizontalMode & BinningVerticalMode same entries + for (unsigned int i = 0;i < BinningModeVertical.nSupportedNum;i ++) + { + MVCC_ENUMENTRY EnumEntry; + EnumEntry.nValue = BinningModeVertical.nSupportValue[i]; + if (i == 0) + { + CreateProperty("BinningMode", EnumEntry.chSymbolic, MM::String, false, pAct); + } + + LSPVals.push_back(EnumEntry.chSymbolic); + } + SetAllowedValues("BinningMode", LSPVals); + return DEVICE_OK; + } + return DEVICE_CAN_NOT_SET_PROPERTY; +} + + +int HikrobotCamera::SetProperty(const char* name, const char* value) +{ + int nRet = __super::SetProperty( name, value ); + return nRet; +} + +/** +* Shuts down (unloads) the device. +*/ +int HikrobotCamera::Shutdown() +{ + if (!m_pCamera) + { + m_pCamera->Close(); + delete m_pCamera; + } + m_bInitialized = false; + MvWriteLog(__FILE__, __LINE__, m_chDevID, "Shutdown set m_bInitialized false"); + + + return DEVICE_OK; +} + +int HikrobotCamera::SnapImage() +{ + + /* + basler在snapimage中调用的是 virtual void StartGrabbing( size_t maxImages, EGrabStrategy strategy = GrabStrategy_OneByOne, EGrabLoop grabLoopType = GrabLoop_ProvidedByUser ); + 这个接å£çš„æ述是:â€Extends the StartGrabbing(EStrategy, EGrabLoop) by a number of images to grab. If the passed count of images has been reached, StopGrabbing is called + automatically. The images are counted according to the grab strategy. Skipped images are not taken into account.“; 就是说获å–图åƒä¸ªæ•°æ»¡è¶³åŽï¼ŒåŽå°ä¼šè‡ªåŠ¨åœæ­¢å–æµï¼› + 海康SDK无此类接å£ï¼Œæ‰€ä»¥éœ€è¦ start ,获å–图åƒï¼Œ stop + */ + + MvWriteLog(__FILE__, __LINE__, m_chDevID, "SnapImage Begin"); + + m_pCamera->SetGrabStrategy(MV_GrabStrategy_OneByOne); + m_pCamera->StartGrabbing(); + + m_bGrabbing = true; + int nRet = MV_E_UNKNOW; + MV_FRAME_OUT stOutFrame = { 0 }; + + do + { + //此处暂时设定1s, 若相机帧率过低,则å¯èƒ½å¼‚常,需è¦è°ƒæ•´; + // 超时时间ä¸èƒ½å¤ªé•¿ï¼Œå®¹æ˜“导致接å£å¡æ­»å¼‚常; + nRet = m_pCamera->GetImageBuffer(&stOutFrame, 1000); + if (nRet == MV_OK) + { + MvWriteLog( __FILE__, __LINE__, m_chDevID, "Get One Frame: Width[%d], Height[%d], FrameNum[%d]", + stOutFrame.stFrameInfo.nWidth, stOutFrame.stFrameInfo.nHeight, stOutFrame.stFrameInfo.nFrameNum); + } + else + { + MvWriteLog(__FILE__, __LINE__, m_chDevID, "Get Image fail!"); + break; + } + + ResizeSnapBuffer(); //分é…内存空间 + CopyToImageBuffer(&stOutFrame); + + nRet = m_pCamera->FreeImageBuffer(&stOutFrame); + if (nRet != MV_OK) + { + MvWriteLog(__FILE__, __LINE__, m_chDevID, "Free Image Buffer fail [%#x]!", nRet); + } + + + break; // 获å–一张图åƒç»“æŸ. + } while (0); + + m_pCamera->StopGrabbing(); + m_bGrabbing = false; + + MvWriteLog(__FILE__, __LINE__, m_chDevID, "SnapImage End"); + return DEVICE_OK; +} + + +void HikrobotCamera::CopyToImageBuffer(MV_FRAME_OUT* pstFrameOut) +{ + if (NULL == pstFrameOut || NULL == pstFrameOut->pBufAddr || NULL == imgBuffer_) + { + MvWriteLog(__FILE__, __LINE__, m_chDevID, "CopyToImageBuffer param invalid."); + return; + } + + if (pstFrameOut->stFrameInfo.enPixelType == PixelType_Gvsp_Mono8) + { + // Workaround : OnPixelType call back will not be fired always. + m_nComponents = MONO_COMPONENTS; + m_nbitDepth = MONO_CONVERTED_DEPTH; + SetProperty(MM::g_Keyword_PixelType, g_PixelType_8bit); + + memcpy(imgBuffer_, pstFrameOut->pBufAddr, pstFrameOut->stFrameInfo.nFrameLen); + } + else + { + int nRet = MV_OK; + unsigned char* pConvertData = NULL; + unsigned int nConvertDataSize = 0; + MvGvspPixelType enDstPixelType = PixelType_Gvsp_Undefined; + unsigned int nChannelNum = 0; + + + nRet = PixTypeProc(pstFrameOut->stFrameInfo.enPixelType, nChannelNum, enDstPixelType); + if (MV_OK != nRet) + { + MvWriteLog(__FILE__, __LINE__, m_chDevID, "PixTypeProc Failed,errcode [%#x]!", nRet); + return; + } + + int nNeedSize = pstFrameOut->stFrameInfo.nWidth * pstFrameOut->stFrameInfo.nHeight * nChannelNum; + + if (m_nConvertDataLen < nNeedSize || (NULL == m_pConvertData)) + { + if (m_pConvertData) + { + free(m_pConvertData); + m_pConvertData = NULL; + } + + m_pConvertData = (unsigned char*)malloc(nNeedSize); + if (NULL == m_pConvertData) + { + MvWriteLog(__FILE__, __LINE__, m_chDevID, "malloc pConvertData len [%d] fail!",nNeedSize); + nRet = MV_E_RESOURCE; + return; + } + m_nConvertDataLen = nNeedSize; + } + + // ch:åƒç´ æ ¼å¼è½¬æ¢ | en:Convert pixel format + MV_CC_PIXEL_CONVERT_PARAM stConvertParam = { 0 }; + + stConvertParam.nWidth = pstFrameOut->stFrameInfo.nWidth; //ch:图åƒå®½ | en:image width + stConvertParam.nHeight = pstFrameOut->stFrameInfo.nHeight; //ch:图åƒé«˜ | en:image height + stConvertParam.pSrcData = pstFrameOut->pBufAddr; //ch:输入数æ®ç¼“å­˜ | en:input data buffer + stConvertParam.nSrcDataLen = pstFrameOut->stFrameInfo.nFrameLen; //ch:输入数æ®å¤§å° | en:input data size + stConvertParam.enSrcPixelType = pstFrameOut->stFrameInfo.enPixelType; //ch:输入åƒç´ æ ¼å¼ | en:input pixel format + stConvertParam.enDstPixelType = enDstPixelType; //ch:输出åƒç´ æ ¼å¼ | en:output pixel format + stConvertParam.pDstBuffer = m_pConvertData; //ch:输出数æ®ç¼“å­˜ | en:output data buffer + stConvertParam.nDstBufferSize = nNeedSize; //ch:è¾“å‡ºç¼“å­˜å¤§å° | en:output buffer size + nRet = GetCamera()->ConvertPixelType(&stConvertParam); + if (MV_OK != nRet) + { + MvWriteLog(__FILE__, __LINE__, m_chDevID, "Convert Pixel Type fail, errcode [%#x]!", nRet); + } + + memcpy(imgBuffer_, m_pConvertData, stConvertParam.nDstLen); + + } +} + + +unsigned HikrobotCamera::PixTypeProc(MvGvspPixelType enPixelType, unsigned int & nChannelNum, MvGvspPixelType & enDstPixelType) +{ + int nRet = MV_OK; + + //如果是彩色则转æˆRGB8 + if (IsColor(enPixelType)) + { + nChannelNum = COLOR_CONVERTED_DEPTH / 8; + enDstPixelType = COLOR_CONVERTED_FORMAT;; + SetProperty(MM::g_Keyword_PixelType, g_PixelType_8bitRGBA); + + m_nComponents = COLOR_COMPONENTS; + m_nbitDepth = COLOR_CONVERTED_DEPTH; + + } + //如果是黑白则转æ¢æˆMono8 + else if (IsMono(enPixelType)) + { + nChannelNum = 1; + enDstPixelType = PixelType_Gvsp_Mono8; + SetProperty(MM::g_Keyword_PixelType, g_PixelType_8bit); + + m_nComponents = MONO_COMPONENTS; + m_nbitDepth = MONO_CONVERTED_DEPTH; + } + else + { + MvWriteLog(__FILE__, __LINE__, m_chDevID, "[%d] Don't support to convert.", enPixelType); + + return MV_E_PARAMETER; + } + + return nRet; + +} + + +/** +* Returns pixel data. +*/ +const unsigned char* HikrobotCamera::GetImageBuffer() +{ + return (unsigned char*)imgBuffer_; +} + +unsigned HikrobotCamera::GetImageWidth() const +{ + + MVCC_INTVALUE_EX stParam = { 0 }; + m_pCamera->GetIntValue("Width", &stParam); + return stParam.nCurValue; +} + +unsigned HikrobotCamera::GetImageHeight() const +{ + MVCC_INTVALUE_EX stParam = { 0 }; + m_pCamera->GetIntValue("Height", &stParam); + return stParam.nCurValue; +} + + +/** +* Returns image buffer pixel depth in bytes. +*/ +unsigned HikrobotCamera::GetImageBytesPerPixel() const +{ + const char* subject("Bayer"); + std::size_t found = pixelType_.find(subject); + unsigned int ret = 0; + + //mono统一转æ¢æˆmon8,其他类型转æ¢ä¸ºRGBA32 + if (pixelType_ == "Mono8" || pixelType_ == "Mono10" || pixelType_ == "Mono12" || pixelType_ == "Mono10Packed" || pixelType_ == "Mono12Packed" || pixelType_ == "Mono16") + { + ret = MONO_IMAGE_BYTES_PERPIXEL; + + } + else if (pixelType_ == "BayerGB8" || pixelType_ == "BayerGB12Packed" || pixelType_ == "BayerGB12" || pixelType_ == "BayerGB8" || pixelType_ == "RGB8Packed" || + pixelType_ == "YUV422_8_UYVY" || pixelType_ == "YUV422_8") + { + ret = COLOR_IMAGE_BYTES_PERPIXEL; + } + else + { + ret = COLOR_IMAGE_BYTES_PERPIXEL; + } + + + MvWriteLog(__FILE__, __LINE__, (char *)m_chDevID, "pixelType_ [%s] GetImageBytesPerPixel [%d].", pixelType_.c_str(), ret); + + return ret; +} + +/** +* Returns the bit depth (dynamic range) of the pixel. +*/ +unsigned int HikrobotCamera::GetBitDepth() const +{ + const char* subject("Bayer"); + std::size_t found = pixelType_.find(subject); + unsigned int ret = 0; + //mono统一转æ¢æˆmon8,其他类型转æ¢ä¸ºRGBA32 + if (pixelType_ == "Mono8" || pixelType_ == "Mono10" || pixelType_ == "Mono12" || pixelType_ == "Mono10Packed" || pixelType_ == "Mono12Packed" || pixelType_ == "Mono16") + { + ret = MONO_CONVERTED_DEPTH; + + } + else if (pixelType_ == "BayerGB8" || pixelType_ == "BayerGB12Packed" || pixelType_ == "BayerGB12" || pixelType_ == "BayerGB8" || pixelType_ == "RGB8Packed" || + pixelType_ == "YUV422_8_UYVY" || pixelType_ == "YUV422_8") + { + ret = COLOR_CONVERTED_DEPTH; + } + else + { + ret = COLOR_CONVERTED_DEPTH; + } + + + MvWriteLog(__FILE__, __LINE__, (char*)m_chDevID, "pixelType_ [%s] GetBitDepth [%d].", pixelType_.c_str(), ret); + return ret; +} + + +int HikrobotCamera::OnPixelType(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + MVCC_ENUMVALUE pixelFormat = { 0 }; + MVCC_ENUMENTRY entry = { 0 }; + + if (eAct == MM::AfterSet) { + + if (m_bGrabbing) + { + MvWriteLog(__FILE__, __LINE__, m_chDevID, "OnPixelType Already start, StopGrab first. "); + m_pCamera->StopGrabbing(); + } + + pProp->Get(pixelType_); + m_pCamera->SetEnumValueByString("PixelFormat", pixelType_.c_str()); + + + MvWriteLog(__FILE__, __LINE__, m_chDevID, "OnPixelType Set PixelFormat value [%s] ", pixelType_.c_str()); + + MVCC_FLOATVALUE offset = { 0 }; + m_pCamera->GetFloatValue("BlackLevel", &offset); + offsetMax_ = offset.fMax; + offsetMin_ = offset.fMin; + SetPropertyLimits(MM::g_Keyword_Offset, offsetMin_, offsetMax_); + + + if (m_bGrabbing) + { + MvWriteLog(__FILE__, __LINE__, m_chDevID, "OnPixelType Already start, Recovery StartGrabing. "); + m_pCamera->StartGrabbing(); + } + + } + else if (eAct == MM::BeforeGet) { + + m_pCamera->GetEnumValue("PixelFormat", &pixelFormat); + entry.nValue = pixelFormat.nCurValue; + m_pCamera->GetEnumEntrySymbolic("PixelFormat", &entry); + + MvWriteLog(__FILE__, __LINE__, m_chDevID, "OnPixelType Get PixelFormat value [%s]", entry.chSymbolic); + + pixelType_.assign(entry.chSymbolic); + pProp->Set(pixelType_.c_str()); + } + + + m_pCamera->GetEnumValue("PixelFormat", &pixelFormat); + entry.nValue = pixelFormat.nCurValue; + m_pCamera->GetEnumEntrySymbolic("PixelFormat", &entry); + std::string strPixelFormatInfo(entry.chSymbolic); + const char* subject("Bayer"); + std::size_t found = strPixelFormatInfo.find(subject); + + if (strPixelFormatInfo.compare("Mono8") == 0 + || strPixelFormatInfo.compare("Mono10") == 0 || strPixelFormatInfo.compare("Mono10Packed") == 0 + || strPixelFormatInfo.compare("Mono12") == 0 || strPixelFormatInfo.compare("Mono12Packed") == 0 + || strPixelFormatInfo.compare("Mono16") == 0) + { + m_nComponents = MONO_COMPONENTS; + m_nbitDepth = MONO_CONVERTED_DEPTH; + SetProperty(MM::g_Keyword_PixelType, g_PixelType_8bit); + + + } + + else if (strPixelFormatInfo.compare("BayerGB12Packed") == 0 || strPixelFormatInfo.compare("BayerGB12") == 0 || + strPixelFormatInfo.compare("BGR8") == 0 || strPixelFormatInfo.compare("RGB8") == 0) + { + m_nComponents = COLOR_COMPONENTS; + m_nbitDepth = COLOR_CONVERTED_DEPTH; + SetProperty(MM::g_Keyword_PixelType, g_PixelType_8bitRGBA); + } + else + { + m_nComponents = COLOR_COMPONENTS; + m_nbitDepth = COLOR_CONVERTED_DEPTH; + SetProperty(MM::g_Keyword_PixelType, g_PixelType_8bitRGBA); + } + + + MvWriteLog(__FILE__, __LINE__, m_chDevID, "OnPixelType [%s] nComponents_ [%d]", entry.chSymbolic, m_nComponents); + + return DEVICE_OK; +} +/** +* Returns the size in bytes of the image buffer. +*/ +long HikrobotCamera::GetImageBufferSize() const +{ + return GetImageWidth() * GetImageHeight() * GetImageBytesPerPixel(); +} + +/** +* Sets the camera Region Of Interest. +* @param x - top-left corner coordinate +* @param y - top-left corner coordinate +* @param xSize - width +* @param ySize - height +*/ +int HikrobotCamera::SetROI(unsigned x, unsigned y, unsigned xSize, unsigned ySize) +{ + + MVCC_INTVALUE_EX width = { 0 }; + m_pCamera->GetIntValue("Width", &width); + MVCC_INTVALUE_EX height = { 0 }; + m_pCamera->GetIntValue("Height", &height); + MVCC_INTVALUE_EX offsetX = { 0 }; + m_pCamera->GetIntValue("OffsetX", &offsetX); + MVCC_INTVALUE_EX offsetY = { 0 }; + m_pCamera->GetIntValue("OffsetY", &offsetY); + + + x -= (x % offsetX.nInc); + y -= (y % offsetY.nInc); + xSize -= (xSize % width.nInc); + ySize -= (ySize % height.nInc); + if (xSize < width.nMin) { + xSize = (unsigned int)width.nMin; + } + if (ySize < height.nMin) { + ySize = (unsigned int)height.nMin; + } + if (x < offsetX.nMin) { + x = (unsigned int)offsetX.nMin; + } + if (y < offsetY.nMin) { + y = (unsigned int)offsetY.nMin; + } + + m_pCamera->SetIntValue("Width", xSize); + m_pCamera->SetIntValue("Height", ySize); + m_pCamera->SetIntValue("OffsetX", x); + m_pCamera->SetIntValue("OffsetY", y); + + + MvWriteLog(__FILE__, __LINE__, m_chDevID, "Set roi Width %d, Height %d, OffsetX %d, OffsetY %d", xSize, ySize, x, y); + + return DEVICE_OK; +} + +/** +* Returns the actual dimensions of the current ROI. +*/ +int HikrobotCamera::GetROI(unsigned& x, unsigned& y, unsigned& xSize, unsigned& ySize) +{ + MVCC_INTVALUE_EX width = { 0 }; + m_pCamera->GetIntValue("Width", &width); + MVCC_INTVALUE_EX height = { 0 }; + m_pCamera->GetIntValue("Height", &height); + MVCC_INTVALUE_EX offsetX = { 0 }; + m_pCamera->GetIntValue("OffsetX", &offsetX); + MVCC_INTVALUE_EX offsetY = { 0 }; + m_pCamera->GetIntValue("OffsetY", &offsetY); + + x = (unsigned int)offsetX.nCurValue; + y = (unsigned int)offsetY.nCurValue; + xSize = (unsigned int)width.nCurValue; + ySize = (unsigned int)height.nCurValue; + +#if 0 + MvWriteLog(__FILE__, __LINE__, m_chDevID, "Get roi Width %d, Height %d, OffsetX %d, OffsetY %d\n", xSize, ySize, x, y); +#endif + + return DEVICE_OK; +} + +/** +* Resets the Region of Interest to full frame. +*/ +int HikrobotCamera::ClearROI() +{ + m_pCamera->SetIntValue("OffsetX", 0); + m_pCamera->SetIntValue("OffsetY", 0); + m_pCamera->SetIntValue("Width", maxWidth_); + m_pCamera->SetIntValue("Height", maxHeight_); + + MvWriteLog(__FILE__, __LINE__, m_chDevID, "Clear roi Width %d, Height %d, OffsetX %d, OffsetY %d\n", maxWidth_, maxHeight_, 0, 0); + + return DEVICE_OK; +} + +/** +* Returns the current exposure setting in milliseconds. +* Required by the MM::Camera API. +*/ +double HikrobotCamera::GetExposure() const +{ + MVCC_FLOATVALUE stFloatValue = { 0.0 }; + m_pCamera->GetFloatValue("ExposureTime", &stFloatValue); + return stFloatValue.fCurValue / 1000.0; +} + +/** +* Sets exposure in milliseconds. +* Required by the MM::Camera API. +*/ +void HikrobotCamera::SetExposure(double exp) +{ + exp *= 1000; //convert to us + if (exp > exposureMax_) { + exp = exposureMax_; + } + else if (exp < exposureMin_) { + exp = exposureMin_; + } + + m_pCamera->SetEnumValue("ExposureAuto", MV_EXPOSURE_AUTO_MODE_OFF); + m_pCamera->SetFloatValue("ExposureTime", exp); + exposure_us_ = exp; + +} + +/** +* Returns the current binning factor. +*/ +int HikrobotCamera::GetBinning() const +{ + return std::atoi(binningFactor_.c_str()); +} + +int HikrobotCamera::SetBinning(int binFactor) +{ + cout << "SetBinning called\n"; + if (binFactor > 1 && binFactor < 4) { + return DEVICE_OK; + } + return DEVICE_OK; +} + +bool HikrobotCamera::IsColor(MvGvspPixelType enType) +{ + switch (enType) + { + case PixelType_Gvsp_RGB8_Packed: + case PixelType_Gvsp_BGR8_Packed: + case PixelType_Gvsp_YUV422_Packed: + case PixelType_Gvsp_YUV422_YUYV_Packed: + case PixelType_Gvsp_BayerGR8: + case PixelType_Gvsp_BayerRG8: + case PixelType_Gvsp_BayerGB8: + case PixelType_Gvsp_BayerBG8: + case PixelType_Gvsp_BayerGB10: + case PixelType_Gvsp_BayerGB10_Packed: + case PixelType_Gvsp_BayerBG10: + case PixelType_Gvsp_BayerBG10_Packed: + case PixelType_Gvsp_BayerRG10: + case PixelType_Gvsp_BayerRG10_Packed: + case PixelType_Gvsp_BayerGR10: + case PixelType_Gvsp_BayerGR10_Packed: + case PixelType_Gvsp_BayerGB12: + case PixelType_Gvsp_BayerGB12_Packed: + case PixelType_Gvsp_BayerBG12: + case PixelType_Gvsp_BayerBG12_Packed: + case PixelType_Gvsp_BayerRG12: + case PixelType_Gvsp_BayerRG12_Packed: + case PixelType_Gvsp_BayerGR12: + case PixelType_Gvsp_BayerGR12_Packed: + return true; + default: + return false; + } +} + +bool HikrobotCamera::IsMono(MvGvspPixelType enType) +{ + switch (enType) + { + case PixelType_Gvsp_Mono8: + case PixelType_Gvsp_Mono8_Signed: + case PixelType_Gvsp_Mono10: + case PixelType_Gvsp_Mono10_Packed: + case PixelType_Gvsp_Mono12: + case PixelType_Gvsp_Mono12_Packed: + case PixelType_Gvsp_Mono14: + case PixelType_Gvsp_Mono16: + return true; + default: + return false; + } +} + + +int HikrobotCamera::StartSequenceAcquisition(long numImages, double /* interval_ms */, bool /* stopOnOverflow */) { + + + UNREFERENCED_PARAMETER(numImages); + MvWriteLog(__FILE__, __LINE__, m_chDevID, "GStartSequenceAcquisition , not support ,just return."); + + return DEVICE_OK; + +} + +int HikrobotCamera::StartSequenceAcquisition(double /* interval_ms */) { + + if (m_bGrabbing) + { + MvWriteLog(__FILE__, __LINE__, m_chDevID, "StartSequenceAcquisition Begin, but Already Start."); + return DEVICE_NOT_SUPPORTED; //设备已ç»start,ä¸èƒ½å†æ¬¡start; ImageJ 中截图和å–æµä¸èƒ½åŒæ—¶ä½¿ç”¨ [截图åŽï¼Œå¿«é€Ÿstartå¯èƒ½ä¼šæŠ¥é”™] + } + + MvWriteLog(__FILE__, __LINE__, m_chDevID, "StartSequenceAcquisition Begin"); + + StopSequenceAcquisition(); + m_pCamera->SetGrabStrategy(MV_GrabStrategy_OneByOne); + m_pCamera->StartGrabbing(); + + + m_bRecvRuning = true; //å–æµçº¿ç¨‹å·¥ä½œ + unsigned int nThreadID = 0; + if (NULL == m_hImageRecvThreadHandle) + { + m_hImageRecvThreadHandle = (void*)_beginthreadex(NULL, 0, ImageRecvThread, this, 0, &nThreadID); + if (NULL == m_hImageRecvThreadHandle) + { + MvWriteLog(__FILE__, __LINE__, m_chDevID, "Create ImageRecvThread failed."); + return DEVICE_ERR; + } + } + + m_bGrabbing = true; //å–æµçŠ¶æ€ + MvWriteLog(__FILE__, __LINE__, m_chDevID, "StartSequenceAcquisition End"); + + return DEVICE_OK; +} + +// å–æµå¤„ç†çº¿ç¨‹ +unsigned int __stdcall HikrobotCamera::ImageRecvThread(void* pUser) +{ + if (NULL == pUser) + { + return 0; + } + HikrobotCamera* pThis = (HikrobotCamera*)pUser; + + pThis->MvWriteLog(__FILE__, __LINE__, pThis->m_chDevID, "ImageRecvThreadProc Start."); + pThis->ImageRecvThreadProc(); + pThis->MvWriteLog(__FILE__, __LINE__, pThis->m_chDevID, "ImageRecvThreadProc End."); + +} + + +void HikrobotCamera::ImageRecvThreadProc() +{ + int nRet = MV_OK; + MV_FRAME_OUT stOutFrame = { 0 }; + + MvWriteLog(__FILE__, __LINE__, m_chDevID,"ImageRecvThreadProc Begin"); + + + + while (m_bRecvRuning) + { + nRet = GetCamera()->GetImageBuffer(&stOutFrame, 1000); + if (MV_OK == nRet) + { + MvWriteLog(__FILE__, __LINE__, m_chDevID, "Get One Frame: Width[%d], Height[%d], FrameNum[%d] FrameLen[%d] enPixelType[%lld]", + stOutFrame.stFrameInfo.nWidth, stOutFrame.stFrameInfo.nHeight, stOutFrame.stFrameInfo.nFrameNum, stOutFrame.stFrameInfo.nFrameLen, stOutFrame.stFrameInfo.enPixelType); + + + MvGvspPixelType enDstPixelType = PixelType_Gvsp_Undefined; + unsigned int nChannelNum = 0; + + nRet = PixTypeProc(stOutFrame.stFrameInfo.enPixelType, nChannelNum, enDstPixelType); + if (MV_OK != nRet) + { + MvWriteLog(__FILE__, __LINE__, m_chDevID, "PixTypeProc Failed,errcode [%#x]!", nRet); + return; + } + + int nNeedSize = stOutFrame.stFrameInfo.nWidth * stOutFrame.stFrameInfo.nHeight * nChannelNum; + if (m_nConvertDataLen < nNeedSize || (NULL == m_pConvertData)) + { + if (m_pConvertData) + { + free(m_pConvertData); + m_pConvertData = NULL; + } + + m_pConvertData = (unsigned char*)malloc(nNeedSize); + if (NULL == m_pConvertData) + { + nRet = GetCamera()->FreeImageBuffer(&stOutFrame); + if (MV_OK != nRet) + { + MvWriteLog(__FILE__, __LINE__, m_chDevID, "FreeImageBuffer failed [%#x]", nRet); + } + + MvWriteLog(__FILE__, __LINE__, m_chDevID, "Malloc pConvertData Len %d fail!", nNeedSize); + nRet = MV_E_RESOURCE; + + break; + } + m_nConvertDataLen = nNeedSize; + + MvWriteLog(__FILE__, __LINE__, m_chDevID, "Malloc pConvertData len [%d].", nNeedSize); + } + + + // ch:åƒç´ æ ¼å¼è½¬æ¢ | en:Convert pixel format + MV_CC_PIXEL_CONVERT_PARAM stConvertParam = { 0 }; + stConvertParam.nWidth = stOutFrame.stFrameInfo.nWidth; //ch:图åƒå®½ | en:image width + stConvertParam.nHeight = stOutFrame.stFrameInfo.nHeight; //ch:图åƒé«˜ | en:image height + stConvertParam.pSrcData = stOutFrame.pBufAddr; //ch:输入数æ®ç¼“å­˜ | en:input data buffer + stConvertParam.nSrcDataLen = stOutFrame.stFrameInfo.nFrameLen; //ch:输入数æ®å¤§å° | en:input data size + stConvertParam.enSrcPixelType = stOutFrame.stFrameInfo.enPixelType; //ch:输入åƒç´ æ ¼å¼ | en:input pixel format + stConvertParam.enDstPixelType = enDstPixelType; //ch:输出åƒç´ æ ¼å¼ | en:output pixel format + stConvertParam.pDstBuffer = m_pConvertData; //ch:输出数æ®ç¼“å­˜ | en:output data buffer + stConvertParam.nDstBufferSize = m_nConvertDataLen; //ch:è¾“å‡ºç¼“å­˜å¤§å° | en:output buffer size + nRet = GetCamera()->ConvertPixelType(&stConvertParam); + if (MV_OK != nRet) + { + MvWriteLog(__FILE__, __LINE__, m_chDevID, "Convert Pixel Type fail, Errcode [%#x]!", nRet); + break; + } + + //!fix , md must assign something + Metadata md; + md.put(MM::g_Keyword_Metadata_CameraLabel, ""); + + nRet = GetCoreCallback()->InsertImage(this, (const unsigned char*)stConvertParam.pDstBuffer, + stOutFrame.stFrameInfo.nWidth, + stOutFrame.stFrameInfo.nHeight, GetImageBytesPerPixel(), 1, md.Serialize().c_str(), FALSE); + if (nRet == DEVICE_BUFFER_OVERFLOW) + { + //if circular buffer overflows, just clear it and keep putting stuff in so live mode can continue + GetCoreCallback()->ClearImageBuffer(this); + MvWriteLog(__FILE__, __LINE__, m_chDevID, "InsertImage clear!"); + + + } + if (nRet == DEVICE_OK) + { + MvWriteLog(__FILE__, __LINE__, m_chDevID, "Success InsertImage Width[%d], Height[%d], FrameNum[%d] FrameLen[%d] enPixelType[%lld]", + stOutFrame.stFrameInfo.nWidth, stOutFrame.stFrameInfo.nHeight, stOutFrame.stFrameInfo.nFrameNum, stOutFrame.stFrameInfo.nFrameLen, stOutFrame.stFrameInfo.enPixelType); + } + + + nRet = GetCamera()->FreeImageBuffer(&stOutFrame); + if (nRet != MV_OK) + { + MvWriteLog(__FILE__, __LINE__, m_chDevID, "FreeImageBuffer failed!"); + } + + } + else + { + //DebugInfo("HikrobotCamera::Get Image fail! nRet [0x%x]\n", nRet); + } + + } + + + GetCoreCallback()->ClearImageBuffer(this); + + + + MvWriteLog(__FILE__, __LINE__, m_chDevID, "ImageRecvThreadProc End!"); + return; +} + + + +bool HikrobotCamera::IsCapturing() +{ + return m_bGrabbing; +} + +int HikrobotCamera::StopSequenceAcquisition() +{ + MvWriteLog(__FILE__, __LINE__, m_chDevID, "StopSequenceAcquisition Begin"); + + + m_pCamera->StopGrabbing(); + + m_bRecvRuning = false; + if (m_hImageRecvThreadHandle) + { + WaitForSingleObject(m_hImageRecvThreadHandle, INFINITE); + CloseHandle(m_hImageRecvThreadHandle); + m_hImageRecvThreadHandle = NULL; + } + m_bGrabbing = false; + MvWriteLog(__FILE__, __LINE__, m_chDevID, "StopSequenceAcquisition End"); + + return DEVICE_OK; +} + +int HikrobotCamera::PrepareSequenceAcqusition() +{ + // nothing to prepare + return DEVICE_OK; +} + +void HikrobotCamera::ResizeSnapBuffer() { + + long bytes = GetImageBufferSize(); + + if (bytes > imgBufferSize_) + { + if (imgBuffer_) + { + free(imgBuffer_); + imgBuffer_ = NULL; + } + + imgBuffer_ = malloc(bytes); + imgBufferSize_ = bytes; + + MvWriteLog(__FILE__, __LINE__, m_chDevID, "imgBufferSize_ %d ", imgBufferSize_); + } +} + + +////// +// Action handlers +/////////////////////////////////////////////////////////////////////////////// + +int HikrobotCamera::OnTriggerSource(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + string TriggerSource_; + if (eAct == MM::AfterSet) { + pProp->Get(TriggerSource_); + m_pCamera->SetEnumValueByString("TriggerSource", TriggerSource_.c_str()); + + MvWriteLog(__FILE__, __LINE__, m_chDevID, "OnTriggerSource MM::AfterSet %s ", TriggerSource_.c_str()); + } + else if (eAct == MM::BeforeGet) { + + MVCC_ENUMVALUE stEnumValue = { 0 }; + m_pCamera->GetEnumValue("TriggerSource", &stEnumValue); + if(MV_TRIGGER_SOURCE_SOFTWARE == stEnumValue.nCurValue) + { + MvWriteLog(__FILE__, __LINE__, m_chDevID, "OnTriggerSource MM::BeforeGet Software"); + + const char* s = "Software"; + pProp->Set(s); + } + } + return DEVICE_OK; +} + +int HikrobotCamera::OnBinningMode(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + MVCC_ENUMVALUE BinningHorizontal = {0}; + m_pCamera->GetEnumValue("BinningHorizontal", &BinningHorizontal); + MVCC_ENUMVALUE BinningVertical = {0}; + m_pCamera->GetEnumValue("BinningHorizontal", &BinningVertical); + + if (eAct == MM::AfterSet) + { + if (IsAvailable("BinningModeVertical") && IsAvailable("BinningModeVertical")) + { + + string binningMode; + pProp->Get(binningMode); + m_pCamera->SetStringValue("BinningModeHorizontal", binningMode.c_str()); + m_pCamera->SetStringValue("BinningModeVertical", binningMode.c_str()); + + + } + } + else if (eAct == MM::BeforeGet) + { + if (IsAvailable("BinningModeVertical") && IsAvailable("BinningModeVertical")) + { + std::ostringstream strValue; + strValue << BinningHorizontal.nCurValue; + pProp->Set(strValue.str().c_str()); + } + + } + return DEVICE_OK; +} + +int HikrobotCamera::OnHeight(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + MVCC_INTVALUE_EX Height = { 0 }; + m_pCamera->GetIntValue("Height", &Height); + + std::string strval; + if (eAct == MM::AfterSet) + { + + if (IsAvailable("Height")) + { + try + { + if (m_bGrabbing) + { + MvWriteLog(__FILE__, __LINE__, m_chDevID, "OnHeight Already start, StopGrab first. "); + m_pCamera->StopGrabbing(); + } + pProp->Get(strval); + int64_t val = std::atoi(strval.c_str()); + int64_t inc = Height.nInc; + m_pCamera->SetIntValue("Height", val - (val % inc)); + + if (m_bGrabbing) + { + MvWriteLog(__FILE__, __LINE__, m_chDevID, "OnHeight Already start, Recovery StartGrabing. "); + m_pCamera->StartGrabbing(); + } + //pProp->Set(Width->GetValue()); + + MvWriteLog(__FILE__, __LINE__, m_chDevID, "OnHeight AfterSet: %ld ", long(val - (val % inc))); + } + catch (...) + { + // Error handling. + MvWriteLog(__FILE__, __LINE__, m_chDevID, "error handle"); + + } + } + } + else if (eAct == MM::BeforeGet) { + try { + if (IsAvailable("Height")) + { + pProp->Set((long)Height.nCurValue); + MvWriteLog(__FILE__, __LINE__, m_chDevID, "OnHeight BeforeGet: %d", Height.nCurValue); + } + } + catch (...) + { + // Error handling. + MvWriteLog(__FILE__, __LINE__, m_chDevID, "Set Height, Error."); + } + } + return DEVICE_OK; +} + +int HikrobotCamera::OnWidth(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + MVCC_INTVALUE_EX Width = { 0 }; + m_pCamera->GetIntValue("Width", &Width); + + std::string strval; + if (eAct == MM::AfterSet) + { + bool Isgrabbing = m_bGrabbing; + + if (IsAvailable("Width")) + { + try + { + if (Isgrabbing) + { + m_pCamera->StopGrabbing(); + } + pProp->Get(strval); + int64_t val = std::atoi(strval.c_str()); + int64_t inc = Width.nInc; + m_pCamera->SetIntValue("Width", val - (val % inc)); + if (Isgrabbing) + { + m_pCamera->StartGrabbing(); + } + //pProp->Set(Width->GetValue()); + + MvWriteLog(__FILE__, __LINE__, m_chDevID, "OnWidth AfterSet get: %ld ", long(val - (val % inc))); + + } + catch (...) + { + // Error handling. + MvWriteLog(__FILE__, __LINE__, m_chDevID, "An exception occurred when set width"); + + } + } + } + else if (eAct == MM::BeforeGet) { + try { + if (IsAvailable("Width")) + { + pProp->Set((long)Width.nCurValue); + + MvWriteLog(__FILE__, __LINE__, m_chDevID, "OnWidth before get: %ld ", long(Width.nCurValue)); + } + } + catch (...) + { + // Error handling. + MvWriteLog(__FILE__, __LINE__, m_chDevID, "An exception occurred when get width"); + } + } + return DEVICE_OK; +} + +int HikrobotCamera::OnExposure(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::AfterSet) + { + if (IsWritable("ExposureTime") || IsWritable("ExposureTimeAbs")) + { + try + { + pProp->Get(exposure_us_); + + exposure_us_ = m_pCamera->SetFloatValue("ExposureTime", exposure_us_); + exposure_us_ = m_pCamera->SetFloatValue("ExposureTimeAbs", exposure_us_); + } + catch (...) + { + // Error handling. + MvWriteLog(__FILE__, __LINE__, m_chDevID, "An exception occurred when set ExposureTime or ExposureTimeAbs"); + } + } + } + else if (eAct == MM::BeforeGet) { + + try { + if (IsAvailable("ExposureTime") && IsAvailable("ExposureTimeAbs")) + { + MVCC_FLOATVALUE stValue; + m_pCamera->GetFloatValue("ExposureTime", &stValue); + exposure_us_ = stValue.fCurValue; + + m_pCamera->GetFloatValue("ExposureTimeAbs", &stValue); + exposure_us_ = stValue.fCurValue; + + pProp->Set(exposure_us_); + } + } + catch (...) + { + // Error handling. + MvWriteLog(__FILE__, __LINE__, m_chDevID, "An exception occurred when get ExposureTime or ExposureTimeAbs"); + } + } + + return DEVICE_OK; +} + +int HikrobotCamera::OnBinning(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + + MVCC_ENUMVALUE BinningHorizontal = {0}; + m_pCamera->GetEnumValue("BinningHorizontal", &BinningHorizontal); + MVCC_ENUMVALUE BinningVertical = { 0 }; + m_pCamera->GetEnumValue("BinningHorizontal", &BinningVertical); + + if (eAct == MM::AfterSet) + { + bool Isgrabbing = m_bGrabbing; + + if (IsAvailable("BinningHorizontal") && IsAvailable("BinningHorizontal")) + { + try + { + if (Isgrabbing) + { + m_pCamera->StopGrabbing(); + } + pProp->Get(binningFactor_); + int64_t val = std::atoi(binningFactor_.c_str()); + m_pCamera->SetIntValue("BinningHorizontal", val); + m_pCamera->SetIntValue("BinningVertical", val); + if (Isgrabbing) + { + m_pCamera->StartGrabbing(); + } + pProp->Set(binningFactor_.c_str()); + } + catch (...) + { + MvWriteLog(__FILE__, __LINE__, m_chDevID, "An exception occurred when set BinningHorizontal or BinningVertical"); + + } + } + } + else if (eAct == MM::BeforeGet) { + + try { + if (IsAvailable("BinningHorizontal") && IsAvailable("BinningHorizontal") ) + { + binningFactor_ = CDeviceUtils::ConvertToString((long)BinningHorizontal.nCurValue); + pProp->Set((long)BinningHorizontal.nCurValue); + } + else + { + pProp->Set("1"); + } + } + catch (...) + { + MvWriteLog(__FILE__, __LINE__, m_chDevID, "An exception occurred when get BinningHorizontal or BinningVertical"); + } + } + + return DEVICE_OK; +} + +unsigned HikrobotCamera::GetNumberOfComponents() const +{ + std::string s = CDeviceUtils::ConvertToString(long(m_nComponents)); + MvWriteLog(__FILE__, __LINE__, (char *)m_chDevID, "GetNumberOfComponents: %ld" , m_nComponents); + + return m_nComponents; +}; + + +int HikrobotCamera::OnSensorReadoutMode(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (IsAvailable("SensorReadoutMode")) + { + string Sensormode = ""; + if (eAct == MM::AfterSet) { + pProp->Get(Sensormode); + + + m_pCamera->SetStringValue("SensorReadoutMode", Sensormode.c_str()); + + MVCC_STRINGVALUE strMode; + m_pCamera->GetStringValue("SensorReadoutMode", &strMode); + pProp->Set(strMode.chCurValue); + } + else if (eAct == MM::BeforeGet) { + MVCC_STRINGVALUE strMode; + m_pCamera->GetStringValue("SensorReadoutMode", &strMode); + pProp->Set(strMode.chCurValue); + } + } + return DEVICE_OK; +} + +int HikrobotCamera::OnTriggerMode(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + MVCC_ENUMVALUE TriggerMode = { 0 }; + string TriggerMode_; + + m_pCamera->GetEnumValue("TriggerMode", &TriggerMode); + MVCC_ENUMENTRY entry; + entry.nValue = TriggerMode.nCurValue; + m_pCamera->GetEnumEntrySymbolic("TriggerMode", &entry); + + if (IsAvailable("TriggerMode")) + { + if (eAct == MM::AfterSet) + { + pProp->Get(TriggerMode_); + m_pCamera->SetEnumValueByString("TriggerMode", TriggerMode_.c_str()); + + pProp->Set(TriggerMode_.c_str()); + + MvWriteLog(__FILE__, __LINE__, m_chDevID, "OnTriggerMode MM::AfterSet %s", TriggerMode_.c_str()); + } + else if (eAct == MM::BeforeGet) + { + + MvWriteLog(__FILE__, __LINE__, m_chDevID, "OnTriggerMode MM::BeforeGet %s", entry.chSymbolic); + + pProp->Set(entry.chSymbolic); + } + } + + return DEVICE_OK; +} + +int HikrobotCamera::OnTemperature(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + UNREFERENCED_PARAMETER(pProp); + UNREFERENCED_PARAMETER(eAct); + return DEVICE_NOT_SUPPORTED; +} + +int HikrobotCamera::OnTemperatureState(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + //FIX me + if (eAct == MM::BeforeGet) { + MVCC_ENUMENTRY ptrtemperatureState_; + m_pCamera->GetEnumEntrySymbolic("TemperatureState", &ptrtemperatureState_); + + temperatureState_.assign(ptrtemperatureState_.chSymbolic); + pProp->Set(temperatureState_.c_str()); + } + return DEVICE_OK; +} + +int HikrobotCamera::OnReverseX(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::AfterSet) { + pProp->Get(reverseX_); + + bool reverseX = false; + m_pCamera->GetBoolValue("ReverseX", &reverseX); + //reverseX->FromString(reverseX_.c_str()); + istringstream(reverseX_) >> boolalpha >> reverseX;//boolalpha>>å¿…é¡»è¦åŠ  + m_pCamera->SetBoolValue("ReverseX", &reverseX); + } + else if (eAct == MM::BeforeGet) { + //CBooleanPtr reverseX(nodeMap_->GetNode("ReverseX")); + + bool reverseX = false; + m_pCamera->GetBoolValue("ReverseX", &reverseX); + //reverseX_.assign(reverseX); + pProp->Set(reverseX_.c_str()); + } + + return DEVICE_OK; +} + +int HikrobotCamera::OnReverseY(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::AfterSet) { + pProp->Get(reverseY_); + //CBooleanPtr reverseY(nodeMap_->GetNode("ReverseY")); + //reverseY->FromString(reverseY_.c_str()); + bool ReverseY = false; + m_pCamera->GetBoolValue("ReverseY", &ReverseY); + istringstream(reverseX_) >> boolalpha >> ReverseY;//boolalpha>>å¿…é¡»è¦åŠ  + m_pCamera->SetBoolValue("ReverseX", &ReverseY); + } + else if (eAct == MM::BeforeGet) { + //CBooleanPtr reverseY(nodeMap_->GetNode("ReverseY")); + //reverseY_.assign(reverseY->ToString().c_str()); + bool reverseX = false; + m_pCamera->GetBoolValue("ReverseX", &reverseX); + pProp->Set(reverseY_.c_str()); + } + return DEVICE_OK; +} + +int HikrobotCamera::OnAcqFramerateEnable(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::AfterSet) { + pProp->Get(setAcqFrm_); + + + //CBooleanPtr setAcqFrm(nodeMap_->GetNode("AcquisitionFrameRateEnable")); + //setAcqFrm->FromString(setAcqFrm_.c_str()); + + bool setAcqFrm = false; + m_pCamera->GetBoolValue("AcquisitionFrameRateEnable", &setAcqFrm); + istringstream(setAcqFrm_) >> boolalpha >> setAcqFrm;//boolalpha>>å¿…é¡»è¦åŠ  + m_pCamera->SetBoolValue("AcquisitionFrameRateEnable", &setAcqFrm); + + } + else if (eAct == MM::BeforeGet) { + //CBooleanPtr setAcqFrm(nodeMap_->GetNode("AcquisitionFrameRateEnable")); + //setAcqFrm_.assign(setAcqFrm->ToString().c_str()); + bool setAcqFrm = false; + m_pCamera->SetBoolValue("AcquisitionFrameRateEnable", &setAcqFrm); + setAcqFrm_ = std::to_string(setAcqFrm); + + pProp->Set(setAcqFrm_.c_str()); + } + return DEVICE_OK; +} + +int HikrobotCamera::OnAcqFramerate(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::AfterSet) { + pProp->Get(acqFramerate_); + //m_pCamera->AcquisitionFrameRateAbs.TrySetValue(acqFramerate_); + //m_pCamera->AcquisitionFrameRate.TrySetValue(acqFramerate_); + + m_pCamera->SetFloatValue("AcquisitionFrameRateAbs", acqFramerate_); + m_pCamera->SetFloatValue("AcquisitionFrameRate", acqFramerate_); + } + else if (eAct == MM::BeforeGet) { + if (IsAvailable("AcquisitionFrameRate")) + { + MVCC_FLOATVALUE value; + m_pCamera->GetFloatValue("AcquisitionFrameRate",&value); + acqFramerate_ = value.fCurValue; + } + else if (IsAvailable("AcquisitionFrameRateAbs")) + { + MVCC_FLOATVALUE value; + m_pCamera->GetFloatValue("AcquisitionFrameRateAbs", &value); + acqFramerate_ = value.fCurValue; + //acqFramerate_ = m_pCamera->AcquisitionFrameRateAbs.GetValue(); + } + std::ostringstream oss; + //oss << std::fixed << std::setfill('0') << std::setprecision(2) << acqFramerate_; + oss << acqFramerate_; + pProp->Set(oss.str().c_str()); + } + return DEVICE_OK; +} + +int HikrobotCamera::OnTestPattern(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + string TestPattern_; + MVCC_ENUMVALUE stEnumValue = { 0 }; + MVCC_ENUMENTRY entry = { 0 }; + + if (eAct == MM::AfterSet) + { + pProp->Get(TestPattern_); + + m_pCamera->SetEnumValueByString("TestPattern", TestPattern_.c_str()); + + MvWriteLog(__FILE__, __LINE__, m_chDevID, "OnTestPattern MM::AfterSet %s", TestPattern_.c_str()); + } + else if (eAct == MM::BeforeGet) + { + m_pCamera->GetEnumValue("TestPattern", &stEnumValue); + entry.nValue = stEnumValue.nCurValue; + m_pCamera->GetEnumEntrySymbolic("TestPattern", &entry); + + MvWriteLog(__FILE__, __LINE__, m_chDevID, "OnTestPattern MM::BeforeGet %s", entry.chSymbolic); + + TestPattern_.assign(entry.chSymbolic); + pProp->Set(TestPattern_.c_str()); + } + + return DEVICE_OK; +} + +int HikrobotCamera::OnAutoGain(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + string GainAuto_; + if (eAct == MM::AfterSet) { + pProp->Get(GainAuto_); + + MVCC_ENUMVALUE GainAuto = { 0 }; + m_pCamera->GetEnumValue("GainAuto", &GainAuto); + + pProp->Set(CDeviceUtils::ConvertToString((long)GainAuto.nCurValue)); + } + else if (eAct == MM::BeforeGet) { + + MVCC_ENUMVALUE GainAuto = { 0 }; + m_pCamera->GetEnumValue("GainAuto", &GainAuto); + pProp->Set(CDeviceUtils::ConvertToString((long)GainAuto.nCurValue)); + + } + return DEVICE_OK; +} + + + + +int HikrobotCamera::OnResultingFramerate(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::BeforeGet) { + if (IsAvailable("ResultingFrameRateAbs")) + { + MVCC_STRINGVALUE value; + m_pCamera->GetStringValue("ResultingFrameRateAbs", &value); + pProp->Set(value.chCurValue); + } + else if (IsAvailable("ResultingFrameRate")) + { + MVCC_STRINGVALUE value; + m_pCamera->GetStringValue("ResultingFrameRate", &value); + pProp->Set(value.chCurValue); + } + } + return DEVICE_OK; +} +int HikrobotCamera::OnAutoExpore(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + + string ExposureAuto_; + if (eAct == MM::AfterSet) { + pProp->Get(ExposureAuto_); + + + MVCC_ENUMVALUE ExposureAuto = { 0 }; + m_pCamera->GetEnumValue("ExposureAuto", &ExposureAuto); + pProp->Set(CDeviceUtils::ConvertToString((long)ExposureAuto.nCurValue)); + + } + else if (eAct == MM::BeforeGet) { + + MVCC_ENUMVALUE ExposureAuto = { 0 }; + m_pCamera->GetEnumValue("ExposureAuto", &ExposureAuto); + pProp->Set(CDeviceUtils::ConvertToString((long)ExposureAuto.nCurValue)); + } + + return DEVICE_OK; +} + + + +int HikrobotCamera::OnLightSourcePreset(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + + string LightSourcePreset_; + if (eAct == MM::AfterSet) { + pProp->Get(LightSourcePreset_); + + MVCC_ENUMVALUE LightSourcePreset = { 0 }; + m_pCamera->GetEnumValue("LightSourcePreset", &LightSourcePreset); + pProp->Set(CDeviceUtils::ConvertToString((long)LightSourcePreset.nCurValue)); + } + else if (eAct == MM::BeforeGet) { + MVCC_ENUMVALUE LightSourcePreset = { 0 }; + m_pCamera->GetEnumValue("LightSourcePreset", &LightSourcePreset); + pProp->Set(CDeviceUtils::ConvertToString((long)LightSourcePreset.nCurValue)); + } + return DEVICE_OK; +} + +int HikrobotCamera::OnShutterMode(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + + if (eAct == MM::AfterSet) { + pProp->Get(shutterMode_); + + MVCC_ENUMVALUE shutterMode = { 0 }; + m_pCamera->GetEnumValue("ShutterMode", &shutterMode); + pProp->Set(CDeviceUtils::ConvertToString((long)shutterMode.nCurValue)); + } + else if (eAct == MM::BeforeGet) { + + MVCC_ENUMVALUE shutterMode = { 0 }; + m_pCamera->GetEnumValue("ShutterMode", &shutterMode); + pProp->Set(CDeviceUtils::ConvertToString((long)shutterMode.nCurValue)); + } + return DEVICE_OK; +} + +int HikrobotCamera::OnDeviceLinkThroughputLimit(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + + MVCC_INTVALUE_EX DeviceLinkThroughputLimit = {0}; + m_pCamera->GetIntValue("DeviceLinkThroughputLimit", &DeviceLinkThroughputLimit); + if (IsAvailable("DeviceLinkThroughputLimit")) + { + if (eAct == MM::AfterSet && IsWritable("DeviceLinkThroughputLimit")) + { + long val; + pProp->Get(val); + m_pCamera->SetIntValue("DeviceLinkThroughputLimit", val); + DeviceLinkThroughputLimit_ = DeviceLinkThroughputLimit.nCurValue; + } + else if (eAct == MM::BeforeGet) + { + DeviceLinkThroughputLimit_ = DeviceLinkThroughputLimit.nCurValue; + pProp->Set(CDeviceUtils::ConvertToString((long)DeviceLinkThroughputLimit_)); + } + } + return DEVICE_OK; +} + +int HikrobotCamera::OnInterPacketDelay(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + + MVCC_INTVALUE_EX GevSCPD = {0}; + m_pCamera->GetIntValue("GevSCPD", &GevSCPD); + if (IsAvailable("GevSCPD")) + { + if (eAct == MM::AfterSet && IsWritable("GevSCPD")) + { + long val; + pProp->Get(val); + m_pCamera->SetIntValue("GevSCPD", val); + + InterPacketDelay_ = GevSCPD.nCurValue; + } + else if (eAct == MM::BeforeGet) + { + InterPacketDelay_ = GevSCPD.nCurValue; + pProp->Set(CDeviceUtils::ConvertToString((long)InterPacketDelay_)); + } + } + return DEVICE_OK; +} + +int HikrobotCamera::OnGain(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + try + { + + MVCC_FLOATVALUE gain; + m_pCamera->GetFloatValue("Gain", &gain); + + MVCC_INTVALUE_EX GainRaw = {0}; + m_pCamera->GetIntValue("GainRaw", &GainRaw); + + if (eAct == MM::AfterSet) { + pProp->Get(gain_); + if (gain_ > gainMax_) { + gain_ = gainMax_; + } + if (gain_ < gainMin_) { + gain_ = gainMin_; + } + if (IsAvailable("Gain")) + { + // the range gain depends on Pixel format sometimes. + if (gain.fMin <= gain_ && gain.fMax >= gain_) + { + m_pCamera->SetFloatValue("Gain", gain_); + + } + else + { + MvWriteLog(__FILE__, __LINE__, m_chDevID, "gain value out of range"); + gainMax_ = gain.fMax; + gainMin_ = gain.fMin; + gain_ = gain.fCurValue; + SetPropertyLimits(MM::g_Keyword_Gain, gainMin_, gainMax_); + pProp->Set(gain_); + } + } + else if (IsAvailable("GainRaw")) + { + // the range gain depends on Pixel format sometimes. + if (GainRaw.nMin <= gain_ && GainRaw.nMax >= gain_) + { + m_pCamera->SetFloatValue("GainRaw", (int64_t)(gain_)); + } + else + { + MvWriteLog(__FILE__, __LINE__, m_chDevID, "gain value out of range"); + gainMax_ = gain.fMax; + gainMin_ = gain.fMin; + gain_ = gain.fCurValue; + SetPropertyLimits(MM::g_Keyword_Gain, gainMin_, gainMax_); + pProp->Set(gain_); + } + } + } + else if (eAct == MM::BeforeGet) { + + if (IsAvailable("Gain")) + { + gain_ = gain.fCurValue; + pProp->Set(gain_); + } + else if (IsAvailable("GainRaw")) + { + gain_ = (double)GainRaw.nCurValue; + pProp->Set(gain_); + cout << "Gain Raw set successfully" << gain_ << endl; + } + } + } + catch (...) + { + // Error handling. + MvWriteLog(__FILE__, __LINE__, m_chDevID, "OnGain unkonwn error"); + + return DEVICE_ERR; + } + return DEVICE_OK; +} + +int HikrobotCamera::OnOffset(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + MVCC_FLOATVALUE offset; + m_pCamera->GetFloatValue("BlackLevel", &offset); + MVCC_FLOATVALUE offsetRaw; + m_pCamera->GetFloatValue("BlackLevelRaw", &offsetRaw); + + if (eAct == MM::AfterSet) { + pProp->Get(offset_); + if (offset_ > offsetMax_) { + offset_ = offsetMax_; + } + if (offset_ < offsetMin_) { + offset_ = offsetMin_; + } + if (IsAvailable("BlackLevel")) + { + m_pCamera->SetFloatValue("BlackLevel", offset_); + } + else if (IsAvailable("BlackLevelRaw")) + { + m_pCamera->SetFloatValue("BlackLevelRaw", offset_); + + } + } + else if (eAct == MM::BeforeGet) { + if (IsAvailable("BlackLevel")) + { + offset_ = offset.fCurValue; + pProp->Set(offset_); + } + else if (IsAvailable("BlackLevelRaw")) + { + offset_ = offsetRaw.fCurValue; + pProp->Set(offset_); + } + } + return DEVICE_OK; +} + +void HikrobotCamera::ReduceImageSize(int64_t Width, int64_t Height) +{ + // This function is just for debug purpose + /*if (!m_pCamera->IsOpen()) + { + m_pCamera->Open(); + }*/ + + + MvWriteLog(__FILE__, __LINE__, m_chDevID, "HikrobotCamera::ReduceImageSize width %lld, height %lld \r\n", Width, Height); + + int64_t inc = 1; + MVCC_INTVALUE_EX width = { 0 }; + m_pCamera->GetIntValue("Width", &width); + if (width.nMax >= Width) + { + inc = width.nInc; + m_pCamera->SetIntValue("Width",(Width - (Width % inc))); + } + + MVCC_INTVALUE_EX height = { 0 }; + m_pCamera->GetIntValue("Width", &height); + if (height.nMax >= Height) + { + inc = height.nInc; + m_pCamera->SetIntValue("Width", (Height - (Height % inc))); + } +} + + +void HikrobotCamera::SetLogBasicInfo(std::string msg) +{ + // 记录型å·ï¼Œåºåˆ—å·; + m_strBasiceLog = msg; +} + + + +void HikrobotCamera::AddToLog(std::string msg) const +{ + // 增加下型å·ï¼Œåºåˆ—å·; + LogMessage(m_strBasiceLog + msg, false); +} + +char* GetFileName(char* strPath) +{ + char* endPos = NULL; + if (strPath) + { +#ifdef WIN32 + if ((endPos = const_cast(strrchr(strPath, '\\'))) != NULL) +#else + if ((endPos = const_cast(strrchr(strPath, '/'))) != NULL) +#endif + { + endPos += 1; + } + } + + return endPos; +} + + +void HikrobotCamera::MvWriteLog(char* file, int line, char* pDevID, const char* fmt, ...) const +{ + va_list args; + char szInfo[1024]; + + sprintf_s(szInfo, 1024, "DevID:%s File:%s Line:-L%04d Description:", pDevID, GetFileName(file), line); + + + va_start(args, fmt); + unsigned int nLen = strlen(szInfo); + + if ((nLen > 0) && (nLen < 1024)) + { + vsnprintf_s(szInfo + nLen, 1024 - nLen, 1024 - nLen, fmt, args); + va_end(args); + } + + + LogMessage(szInfo, false); +} + + + + +void HikrobotCamera::SetPixConfig(bool bMono) +{ + if (bMono) + { + SetProperty(MM::g_Keyword_PixelType, g_PixelType_8bit); + m_nComponents = MONO_COMPONENTS; + m_nbitDepth = MONO_CONVERTED_DEPTH; + } + else + { + SetProperty(MM::g_Keyword_PixelType, g_PixelType_8bitRGBA); + + m_nComponents = COLOR_COMPONENTS; + m_nbitDepth = COLOR_CONVERTED_DEPTH; + } +} + + +void HikrobotCamera::UpdateTemperature() +{ + return; +} + +bool HikrobotCamera::IsAvailable(const char* strName) { + //! Tests if available, FIX! + MV_XML_AccessMode AccessMode = AM_Undefined; + m_pCamera->GetNodeAccessMode(strName, &AccessMode); + bool bRet = !(AccessMode == AM_NA || AccessMode == AM_NI); + + +#ifdef _DEBUG + MvWriteLog(__FILE__, __LINE__, m_chDevID, "Node name [%s] IsWritable [%d], accessmode [%d]\n", strName, bRet, AccessMode); +#endif + + return bRet; +} + +bool HikrobotCamera::IsWritable(const char* strName) +{ + MV_XML_AccessMode AccessMode = AM_Undefined; + m_pCamera->GetNodeAccessMode(strName, &AccessMode); + bool bRet = (AccessMode == AM_WO || AccessMode == AM_RW); + +#ifdef _DEBUG + + MvWriteLog(__FILE__, __LINE__, m_chDevID, "Node name [%s] IsWritable [%d], accessmode [%d]", strName, bRet, AccessMode); + +#endif + + return bRet; +} + + diff --git a/DeviceAdapters/Hikrobot/HikrobotCamera.h b/DeviceAdapters/Hikrobot/HikrobotCamera.h new file mode 100644 index 000000000..162168e6e --- /dev/null +++ b/DeviceAdapters/Hikrobot/HikrobotCamera.h @@ -0,0 +1,271 @@ +/////////////////////////////////////////////////////////////////////////////// +// FILE: HikrobotCamera.h +// SUBSYSTEM: DeviceAdapters +//----------------------------------------------------------------------------- +// DESCRIPTION: Adapter for hikrobot Cameras +// +// Copyright 2023 andy.xin +// +// 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 + +#include "DeviceBase.h" +#include "DeviceThreads.h" +#include +#include +#include +#include "ImageMetadata.h" +#include "ImgBuffer.h" +#include +#include "MvCamera.h" + + +////////////////////////////////////////////////////////////////////////////// +// Error codes +// +//#define ERR_UNKNOWN_BINNING_MODE 410 +enum +{ + ERR_SERIAL_NUMBER_REQUIRED = 20001, + ERR_SERIAL_NUMBER_NOT_FOUND, + ERR_CANNOT_CONNECT, +}; + +////////////////////////////////////////////////////////////////////////////// +// hikrobot camera class +////////////////////////////////////////////////////////////////////////////// +//Callback class for putting frames in circular buffer as they arrive + +//class CTempCameraEventHandler; +//class CircularBufferInserter; +class HikrobotCamera : public CCameraBase { +public: + HikrobotCamera(); + ~HikrobotCamera(); + + // MMDevice API + // ------------ + int Initialize(); + int Shutdown(); + + void GetName(char* name) const; + bool Busy() {return false;} + + + // MMCamera API + // ------------ + int SnapImage(); + const unsigned char* GetImageBuffer(); + void* Buffer4ContinuesShot; + + unsigned GetNumberOfComponents() const; + 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(); + void ReduceImageSize(int64_t Width, int64_t Height); + int GetBinning() const; + int SetBinning(int binSize); + int IsExposureSequenceable(bool& seq) const {seq = false; return DEVICE_OK;} + //void RGBPackedtoRGB(void* destbuffer, const CGrabResultPtr& ptrGrabResult); + int SetProperty(const char* name, const char* value); + int CheckForBinningMode(CPropertyAction *pAct); + + void AddToLog(std::string msg) const; //ä¸æŽ¨è使用 + void MvWriteLog(char* file, int line, char* pDevID, const char* fmt, ...) const ; //推è使用 + + + void CopyToImageBuffer(MV_FRAME_OUT* pstFrameOut); + //CImageFormatConverter *converter; + //CircularBufferInserter *ImageHandler_; + //std::string EnumToString(EDeviceAccessiblityInfo DeviceAccessiblityInfo); + void UpdateTemperature(); + + /** + * Starts continuous acquisition. + */ + int StartSequenceAcquisition(long numImages, double interval_ms, bool stopOnOverflow); + int StartSequenceAcquisition(double interval_ms); + int StopSequenceAcquisition(); + int PrepareSequenceAcqusition(); + + /** + * Flag to indicate whether Sequence Acquisition is currently running. + * Return true when Sequence acquisition is active, false otherwise + */ + bool IsCapturing(); + + //Genicam Callback + //void ResultingFramerateCallback(GenApi::INode* pNode); + + + // action interface + // ---------------- + int OnAcqFramerate(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnAcqFramerateEnable(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnAutoExpore(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnAutoGain(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnTestPattern(MM::PropertyBase* pProp, MM::ActionType eAct); + + int OnBinning(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnBinningMode(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnDeviceLinkThroughputLimit(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnExposure(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnGain(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnHeight(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnInterPacketDelay(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnLightSourcePreset(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnOffset(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnPixelType(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnResultingFramerate(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnReverseX(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnReverseY(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnSensorReadoutMode(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnShutterMode(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnTemperature(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnTemperatureState(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnTriggerMode(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnTriggerSource(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnWidth(MM::PropertyBase* pProp, MM::ActionType eAct); +public: + CMvCamera* GetCamera() { + return m_pCamera; + } + + + bool IsColor(MvGvspPixelType enType); + bool IsMono(MvGvspPixelType enType); + + void SetPixConfig(bool bMono); + + +private: + bool IsAvailable(const char* strName); + bool IsWritable(const char* strName); + + int EnumDevice(); + + void SetLogBasicInfo(std::string msg); + + + static unsigned int __stdcall ImageRecvThread(void* pUser); + void ImageRecvThreadProc(); + + + unsigned PixTypeProc(MvGvspPixelType enPixelType, unsigned int& nChannelNum, MvGvspPixelType& enDstPixelType); +private: + + // åŸºç¡€æ—¥å¿—è®°å½•ä¿¡æ¯ + std::string m_strBasiceLog; + char m_chDevID[128];// 设备ID + + MV_CC_DEVICE_INFO_LIST m_stDevList; + CMvCamera * m_pCamera; + + + int m_nComponents; //组件个数(通é“个数) + unsigned m_nbitDepth; //å›¾åƒ å çš„字节个数 + + unsigned maxWidth_, maxHeight_; + int64_t DeviceLinkThroughputLimit_; + int64_t InterPacketDelay_; + double ResultingFrameRatePrevious; + double acqFramerate_, acqFramerateMax_, acqFramerateMin_; + double exposure_us_, exposureMax_, exposureMin_; + double gain_, gainMax_, gainMin_; + double offset_, offsetMin_, offsetMax_; + + + std::string binningFactor_; + std::string pixelType_; + std::string reverseX_, reverseY_; + std::string sensorReadoutMode_; + std::string setAcqFrm_; + std::string shutterMode_; + std::string temperature_; + std::string temperatureState_; + + + void* imgBuffer_; + long imgBufferSize_; + ImgBuffer img_; + + + unsigned char* m_pConvertData; + long m_nConvertDataLen; + + + bool m_bInitialized; + + void ResizeSnapBuffer(); + bool m_bGrabbing; //å–æµå·¥ä½œçŠ¶æ€ + + bool m_bRecvRuning; //å–æµçº¿ç¨‹çŠ¶æ€ + HANDLE m_hImageRecvThreadHandle; //å–æµçº¿ç¨‹å¥æŸ„ + +}; + +//Enumeration used for distinguishing different events. +enum TemperatureEvents +{ + TempCritical = 100, + TempOverTemp = 200 +}; + +// Number of images to be grabbed. +static const uint32_t c_countOfImagesToGrab = 5; + + +// Example handler for camera events. +// class CTempCameraEventHandler : public CBaslerUniversalCameraEventHandler +// { +// private: +// HikrobotCamera* dev_; +// public: +// CTempCameraEventHandler(HikrobotCamera* dev); +// virtual void OnCameraEvent(CBaslerUniversalInstantCamera& camera, intptr_t userProvidedId, GenApi::INode* pNode); +// }; +// +// +// class CircularBufferInserter : public CImageEventHandler { +// private: +// HikrobotCamera* dev_; +// +// public: +// CircularBufferInserter(HikrobotCamera* dev); +// +// virtual void OnImageGrabbed( CInstantCamera& camera, const CGrabResultPtr& ptrGrabResult); +// }; + diff --git a/DeviceAdapters/Hikrobot/MvCamera.cpp b/DeviceAdapters/Hikrobot/MvCamera.cpp new file mode 100644 index 000000000..910894ac3 --- /dev/null +++ b/DeviceAdapters/Hikrobot/MvCamera.cpp @@ -0,0 +1,348 @@ +#include "MvCamera.h" + +CMvCamera::CMvCamera() +{ + m_hDevHandle = MV_NULL; +} + +CMvCamera::~CMvCamera() +{ + if (m_hDevHandle) + { + MV_CC_DestroyHandle(m_hDevHandle); + m_hDevHandle = MV_NULL; + } +} + +// ch:获å–SDKç‰ˆæœ¬å· | en:Get SDK Version +int CMvCamera::GetSDKVersion() +{ + return MV_CC_GetSDKVersion(); +} + +// ch:枚举设备 | en:Enumerate Device +int CMvCamera::EnumDevices(unsigned int nTLayerType, MV_CC_DEVICE_INFO_LIST* pstDevList) +{ + return MV_CC_EnumDevices(nTLayerType, pstDevList); +} + +// ch:判断设备是å¦å¯è¾¾ | en:Is the device accessible +bool CMvCamera::IsDeviceAccessible(MV_CC_DEVICE_INFO* pstDevInfo, unsigned int nAccessMode) +{ + return MV_CC_IsDeviceAccessible(pstDevInfo, nAccessMode); +} + +// ch:打开设备 | en:Open Device +int CMvCamera::Open(MV_CC_DEVICE_INFO* pstDeviceInfo) +{ + if (MV_NULL == pstDeviceInfo) + { + return MV_E_PARAMETER; + } + + if (m_hDevHandle) + { + return MV_E_CALLORDER; + } + + int nRet = MV_CC_CreateHandle(&m_hDevHandle, pstDeviceInfo); + if (MV_OK != nRet) + { + return nRet; + } + + nRet = MV_CC_OpenDevice(m_hDevHandle); + if (MV_OK != nRet) + { + MV_CC_DestroyHandle(m_hDevHandle); + m_hDevHandle = MV_NULL; + } + + return nRet; +} + +// ch:关闭设备 | en:Close Device +int CMvCamera::Close() +{ + if (MV_NULL == m_hDevHandle) + { + return MV_E_HANDLE; + } + + MV_CC_CloseDevice(m_hDevHandle); + + int nRet = MV_CC_DestroyHandle(m_hDevHandle); + m_hDevHandle = MV_NULL; + + return nRet; +} + +// ch:判断相机是å¦å¤„äºŽè¿žæŽ¥çŠ¶æ€ | en:Is The Device Connected +bool CMvCamera::IsDeviceConnected() +{ + return MV_CC_IsDeviceConnected(m_hDevHandle); +} + +// ch:注册图åƒæ•°æ®å›žè°ƒ | en:Register Image Data CallBack +int CMvCamera::RegisterImageCallBack(void(__stdcall* cbOutput)(unsigned char * pData, MV_FRAME_OUT_INFO_EX* pFrameInfo, void* pUser), void* pUser) +{ + return MV_CC_RegisterImageCallBackEx(m_hDevHandle, cbOutput, pUser); +} + +// ch:å¼€å¯æŠ“图 | en:Start Grabbing +int CMvCamera::StartGrabbing() +{ + return MV_CC_StartGrabbing(m_hDevHandle); +} + +// ch:åœæ­¢æŠ“图 | en:Stop Grabbing +int CMvCamera::StopGrabbing() +{ + return MV_CC_StopGrabbing(m_hDevHandle); +} + +// ch:主动获å–一帧图åƒæ•°æ® | en:Get one frame initiatively +int CMvCamera::GetImageBuffer(MV_FRAME_OUT* pFrame, int nMsec) +{ + return MV_CC_GetImageBuffer(m_hDevHandle, pFrame, nMsec); +} + +// ch:释放图åƒç¼“å­˜ | en:Free image buffer +int CMvCamera::FreeImageBuffer(MV_FRAME_OUT* pFrame) +{ + return MV_CC_FreeImageBuffer(m_hDevHandle, pFrame); +} + +// ch:设置显示窗å£å¥æŸ„ | en:Set Display Window Handle +int CMvCamera::DisplayOneFrame(MV_DISPLAY_FRAME_INFO* pDisplayInfo) +{ + return MV_CC_DisplayOneFrame(m_hDevHandle, pDisplayInfo); +} + +// ch:设置SDK内部图åƒç¼“存节点个数 | en:Set the number of the internal image cache nodes in SDK +int CMvCamera::SetImageNodeNum(unsigned int nNum) +{ + return MV_CC_SetImageNodeNum(m_hDevHandle, nNum); +} + +// ch:获å–è®¾å¤‡ä¿¡æ¯ | en:Get device information +int CMvCamera::GetDeviceInfo(MV_CC_DEVICE_INFO* pstDevInfo) +{ + return MV_CC_GetDeviceInfo(m_hDevHandle, pstDevInfo); +} + +// ch:获å–GEVç›¸æœºçš„ç»Ÿè®¡ä¿¡æ¯ | en:Get detect info of GEV camera +int CMvCamera::GetGevAllMatchInfo(MV_MATCH_INFO_NET_DETECT* pMatchInfoNetDetect) +{ + if (MV_NULL == pMatchInfoNetDetect) + { + return MV_E_PARAMETER; + } + + MV_CC_DEVICE_INFO stDevInfo = {0}; + GetDeviceInfo(&stDevInfo); + if (stDevInfo.nTLayerType != MV_GIGE_DEVICE) + { + return MV_E_SUPPORT; + } + + MV_ALL_MATCH_INFO struMatchInfo = {0}; + + struMatchInfo.nType = MV_MATCH_TYPE_NET_DETECT; + struMatchInfo.pInfo = pMatchInfoNetDetect; + struMatchInfo.nInfoSize = sizeof(MV_MATCH_INFO_NET_DETECT); + memset(struMatchInfo.pInfo, 0, sizeof(MV_MATCH_INFO_NET_DETECT)); + + return MV_CC_GetAllMatchInfo(m_hDevHandle, &struMatchInfo); +} + +// ch:获å–U3Vç›¸æœºçš„ç»Ÿè®¡ä¿¡æ¯ | en:Get detect info of U3V camera +int CMvCamera::GetU3VAllMatchInfo(MV_MATCH_INFO_USB_DETECT* pMatchInfoUSBDetect) +{ + if (MV_NULL == pMatchInfoUSBDetect) + { + return MV_E_PARAMETER; + } + + MV_CC_DEVICE_INFO stDevInfo = {0}; + GetDeviceInfo(&stDevInfo); + if (stDevInfo.nTLayerType != MV_USB_DEVICE) + { + return MV_E_SUPPORT; + } + + MV_ALL_MATCH_INFO struMatchInfo = {0}; + + struMatchInfo.nType = MV_MATCH_TYPE_USB_DETECT; + struMatchInfo.pInfo = pMatchInfoUSBDetect; + struMatchInfo.nInfoSize = sizeof(MV_MATCH_INFO_USB_DETECT); + memset(struMatchInfo.pInfo, 0, sizeof(MV_MATCH_INFO_USB_DETECT)); + + return MV_CC_GetAllMatchInfo(m_hDevHandle, &struMatchInfo); +} + +// ch:获å–和设置Intåž‹å‚数,如 Widthå’ŒHeight,详细内容å‚考SDK安装目录下的 MvCameraNode.xlsx 文件 +// en:Get Int type parameters, such as Width and Height, for details please refer to MvCameraNode.xlsx file under SDK installation directory +int CMvCamera::GetIntValue(IN const char* strKey, OUT MVCC_INTVALUE_EX *pIntValue) +{ + return MV_CC_GetIntValueEx(m_hDevHandle, strKey, pIntValue); +} + +int CMvCamera::SetIntValue(IN const char* strKey, IN int64_t nValue) +{ + return MV_CC_SetIntValueEx(m_hDevHandle, strKey, nValue); +} + +// ch:获å–和设置Enumåž‹å‚数,如 PixelFormat,详细内容å‚考SDK安装目录下的 MvCameraNode.xlsx 文件 +// en:Get Enum type parameters, such as PixelFormat, for details please refer to MvCameraNode.xlsx file under SDK installation directory +int CMvCamera::GetEnumValue(IN const char* strKey, OUT MVCC_ENUMVALUE *pEnumValue) +{ + return MV_CC_GetEnumValue(m_hDevHandle, strKey, pEnumValue); +} + +int CMvCamera::SetEnumValue(IN const char* strKey, IN unsigned int nValue) +{ + return MV_CC_SetEnumValue(m_hDevHandle, strKey, nValue); +} + +int CMvCamera::SetEnumValueByString(IN const char* strKey, IN const char* sValue) +{ + return MV_CC_SetEnumValueByString(m_hDevHandle, strKey, sValue); +} + +int CMvCamera::GetEnumEntrySymbolic(IN const char* strKey, IN MVCC_ENUMENTRY* pstEnumEntry) +{ + return MV_CC_GetEnumEntrySymbolic(m_hDevHandle, strKey, pstEnumEntry); +} + +// ch:获å–和设置Floatåž‹å‚数,如 ExposureTimeå’ŒGain,详细内容å‚考SDK安装目录下的 MvCameraNode.xlsx 文件 +// en:Get Float type parameters, such as ExposureTime and Gain, for details please refer to MvCameraNode.xlsx file under SDK installation directory +int CMvCamera::GetFloatValue(IN const char* strKey, OUT MVCC_FLOATVALUE *pFloatValue) +{ + return MV_CC_GetFloatValue(m_hDevHandle, strKey, pFloatValue); +} + +int CMvCamera::SetFloatValue(IN const char* strKey, IN float fValue) +{ + return MV_CC_SetFloatValue(m_hDevHandle, strKey, fValue); +} + +// ch:获å–和设置Boolåž‹å‚数,如 ReverseX,详细内容å‚考SDK安装目录下的 MvCameraNode.xlsx 文件 +// en:Get Bool type parameters, such as ReverseX, for details please refer to MvCameraNode.xlsx file under SDK installation directory +int CMvCamera::GetBoolValue(IN const char* strKey, OUT bool *pbValue) +{ + return MV_CC_GetBoolValue(m_hDevHandle, strKey, pbValue); +} + +int CMvCamera::SetBoolValue(IN const char* strKey, IN bool bValue) +{ + return MV_CC_SetBoolValue(m_hDevHandle, strKey, bValue); +} + +// ch:获å–和设置Stringåž‹å‚数,如 DeviceUserID,详细内容å‚考SDK安装目录下的 MvCameraNode.xlsx 文件UserSetSave +// en:Get String type parameters, such as DeviceUserID, for details please refer to MvCameraNode.xlsx file under SDK installation directory +int CMvCamera::GetStringValue(IN const char* strKey, MVCC_STRINGVALUE *pStringValue) +{ + return MV_CC_GetStringValue(m_hDevHandle, strKey, pStringValue); +} + +int CMvCamera::SetStringValue(IN const char* strKey, IN const char* strValue) +{ + return MV_CC_SetStringValue(m_hDevHandle, strKey, strValue); +} + +// ch:执行一次Command型命令,如 UserSetSave,详细内容å‚考SDK安装目录下的 MvCameraNode.xlsx 文件 +// en:Execute Command once, such as UserSetSave, for details please refer to MvCameraNode.xlsx file under SDK installation directory +int CMvCamera::CommandExecute(IN const char* strKey) +{ + return MV_CC_SetCommandValue(m_hDevHandle, strKey); +} + +// ch:探测网络最佳包大å°(åªå¯¹GigE相机有效) | en:Detection network optimal package size(It only works for the GigE camera) +int CMvCamera::GetOptimalPacketSize(unsigned int* pOptimalPacketSize) +{ + if (MV_NULL == pOptimalPacketSize) + { + return MV_E_PARAMETER; + } + + int nRet = MV_CC_GetOptimalPacketSize(m_hDevHandle); + if (nRet < MV_OK) + { + return nRet; + } + + *pOptimalPacketSize = (unsigned int)nRet; + + return MV_OK; +} + +// ch:注册消æ¯å¼‚常回调 | en:Register Message Exception CallBack +int CMvCamera::RegisterExceptionCallBack(void(__stdcall* cbException)(unsigned int nMsgType, void* pUser),void* pUser) +{ + return MV_CC_RegisterExceptionCallBack(m_hDevHandle, cbException, pUser); +} + +// ch:注册å•ä¸ªäº‹ä»¶å›žè°ƒ | en:Register Event CallBack +int CMvCamera::RegisterEventCallBack(const char* pEventName, void(__stdcall* cbEvent)(MV_EVENT_OUT_INFO * pEventInfo, void* pUser), void* pUser) +{ + return MV_CC_RegisterEventCallBackEx(m_hDevHandle, pEventName, cbEvent, pUser); +} + +// ch:强制IP | en:Force IP +int CMvCamera::ForceIp(unsigned int nIP, unsigned int nSubNetMask, unsigned int nDefaultGateWay) +{ + return MV_GIGE_ForceIpEx(m_hDevHandle, nIP, nSubNetMask, nDefaultGateWay); +} + +// ch:é…ç½®IPæ–¹å¼ | en:IP configuration method +int CMvCamera::SetIpConfig(unsigned int nType) +{ + return MV_GIGE_SetIpConfig(m_hDevHandle, nType); +} + +// ch:è®¾ç½®ç½‘ç»œä¼ è¾“æ¨¡å¼ | en:Set Net Transfer Mode +int CMvCamera::SetNetTransMode(unsigned int nType) +{ + return MV_GIGE_SetNetTransMode(m_hDevHandle, nType); +} + +// ch:åƒç´ æ ¼å¼è½¬æ¢ | en:Pixel format conversion +int CMvCamera::ConvertPixelType(MV_CC_PIXEL_CONVERT_PARAM* pstCvtParam) +{ + return MV_CC_ConvertPixelType(m_hDevHandle, pstCvtParam); +} + +// ch:ä¿å­˜å›¾ç‰‡ | en:save image +int CMvCamera::SaveImage(MV_SAVE_IMAGE_PARAM_EX* pstParam) +{ + return MV_CC_SaveImageEx2(m_hDevHandle, pstParam); +} + +// ch:ä¿å­˜å›¾ç‰‡ä¸ºæ–‡ä»¶ | en:Save the image as a file +int CMvCamera::SaveImageToFile(MV_SAVE_IMG_TO_FILE_PARAM* pstSaveFileParam) +{ + return MV_CC_SaveImageToFile(m_hDevHandle, pstSaveFileParam); +} + +// ch:绘制圆形辅助线 | en:Draw circle auxiliary line +int CMvCamera::DrawCircle(MVCC_CIRCLE_INFO* pCircleInfo) +{ + return MV_CC_DrawCircle(m_hDevHandle, pCircleInfo); +} + +// ch:绘制线形辅助线 | en:Draw lines auxiliary line +int CMvCamera::DrawLines(MVCC_LINES_INFO* pLinesInfo) +{ + return MV_CC_DrawLines(m_hDevHandle, pLinesInfo); +} + +int CMvCamera::SetGrabStrategy(MV_GRAB_STRATEGY enGrabStrategy) +{ + return MV_CC_SetGrabStrategy(m_hDevHandle, enGrabStrategy); +} + +int CMvCamera::GetNodeAccessMode(const char* strName, MV_XML_AccessMode* penAccessMode) +{ + return MV_XML_GetNodeAccessMode(m_hDevHandle, strName, penAccessMode); +} diff --git a/DeviceAdapters/Hikrobot/MvCamera.h b/DeviceAdapters/Hikrobot/MvCamera.h new file mode 100644 index 000000000..cfeae9333 --- /dev/null +++ b/DeviceAdapters/Hikrobot/MvCamera.h @@ -0,0 +1,146 @@ +/************************************************************************/ +/* 以C++接å£ä¸ºåŸºç¡€ï¼Œå¯¹å¸¸ç”¨å‡½æ•°è¿›è¡ŒäºŒæ¬¡å°è£…,方便用户使用 */ +/************************************************************************/ + +#ifndef _MV_CAMERA_H_ +#define _MV_CAMERA_H_ + +#include "MvCameraControl.h" +#include + +#ifndef MV_NULL +#define MV_NULL 0 +#endif + +class CMvCamera +{ +public: + CMvCamera(); + ~CMvCamera(); + + // ch:获å–SDKç‰ˆæœ¬å· | en:Get SDK Version + static int GetSDKVersion(); + + // ch:枚举设备 | en:Enumerate Device + static int EnumDevices(unsigned int nTLayerType, MV_CC_DEVICE_INFO_LIST* pstDevList); + + // ch:判断设备是å¦å¯è¾¾ | en:Is the device accessible + static bool IsDeviceAccessible(MV_CC_DEVICE_INFO* pstDevInfo, unsigned int nAccessMode); + + // ch:打开设备 | en:Open Device + int Open(MV_CC_DEVICE_INFO* pstDeviceInfo); + + // ch:关闭设备 | en:Close Device + int Close(); + + // ch:判断相机是å¦å¤„äºŽè¿žæŽ¥çŠ¶æ€ | en:Is The Device Connected + bool IsDeviceConnected(); + + // ch:注册图åƒæ•°æ®å›žè°ƒ | en:Register Image Data CallBack + int RegisterImageCallBack(void(__stdcall* cbOutput)(unsigned char * pData, MV_FRAME_OUT_INFO_EX* pFrameInfo, void* pUser), void* pUser); + + // ch:å¼€å¯æŠ“图 | en:Start Grabbing + int StartGrabbing(); + + // ch:åœæ­¢æŠ“图 | en:Stop Grabbing + int StopGrabbing(); + + // ch:主动获å–一帧图åƒæ•°æ® | en:Get one frame initiatively + int GetImageBuffer(MV_FRAME_OUT* pFrame, int nMsec); + + // ch:释放图åƒç¼“å­˜ | en:Free image buffer + int FreeImageBuffer(MV_FRAME_OUT* pFrame); + + // ch:æ˜¾ç¤ºä¸€å¸§å›¾åƒ | en:Display one frame image + int DisplayOneFrame(MV_DISPLAY_FRAME_INFO* pDisplayInfo); + + // ch:设置SDK内部图åƒç¼“存节点个数 | en:Set the number of the internal image cache nodes in SDK + int SetImageNodeNum(unsigned int nNum); + + // ch:获å–è®¾å¤‡ä¿¡æ¯ | en:Get device information + int GetDeviceInfo(MV_CC_DEVICE_INFO* pstDevInfo); + + // ch:获å–GEVç›¸æœºçš„ç»Ÿè®¡ä¿¡æ¯ | en:Get detect info of GEV camera + int GetGevAllMatchInfo(MV_MATCH_INFO_NET_DETECT* pMatchInfoNetDetect); + + // ch:获å–U3Vç›¸æœºçš„ç»Ÿè®¡ä¿¡æ¯ | en:Get detect info of U3V camera + int GetU3VAllMatchInfo(MV_MATCH_INFO_USB_DETECT* pMatchInfoUSBDetect); + + // ch:获å–和设置Intåž‹å‚数,如 Widthå’ŒHeight,详细内容å‚考SDK安装目录下的 MvCameraNode.xlsx 文件 + // en:Get Int type parameters, such as Width and Height, for details please refer to MvCameraNode.xlsx file under SDK installation directory + int GetIntValue(IN const char* strKey, OUT MVCC_INTVALUE_EX *pIntValue); + int SetIntValue(IN const char* strKey, IN int64_t nValue); + + // ch:获å–和设置Enumåž‹å‚数,如 PixelFormat,详细内容å‚考SDK安装目录下的 MvCameraNode.xlsx 文件 + // en:Get Enum type parameters, such as PixelFormat, for details please refer to MvCameraNode.xlsx file under SDK installation directory + int GetEnumValue(IN const char* strKey, OUT MVCC_ENUMVALUE *pEnumValue); + int SetEnumValue(IN const char* strKey, IN unsigned int nValue); + int SetEnumValueByString(IN const char* strKey, IN const char* sValue); + int GetEnumEntrySymbolic(IN const char* strKey, IN MVCC_ENUMENTRY* pstEnumEntry); + + // ch:获å–和设置Floatåž‹å‚数,如 ExposureTimeå’ŒGain,详细内容å‚考SDK安装目录下的 MvCameraNode.xlsx 文件 + // en:Get Float type parameters, such as ExposureTime and Gain, for details please refer to MvCameraNode.xlsx file under SDK installation directory + int GetFloatValue(IN const char* strKey, OUT MVCC_FLOATVALUE *pFloatValue); + int SetFloatValue(IN const char* strKey, IN float fValue); + + // ch:获å–和设置Boolåž‹å‚数,如 ReverseX,详细内容å‚考SDK安装目录下的 MvCameraNode.xlsx 文件 + // en:Get Bool type parameters, such as ReverseX, for details please refer to MvCameraNode.xlsx file under SDK installation directory + int GetBoolValue(IN const char* strKey, OUT bool *pbValue); + int SetBoolValue(IN const char* strKey, IN bool bValue); + + // ch:获å–和设置Stringåž‹å‚数,如 DeviceUserID,详细内容å‚考SDK安装目录下的 MvCameraNode.xlsx 文件UserSetSave + // en:Get String type parameters, such as DeviceUserID, for details please refer to MvCameraNode.xlsx file under SDK installation directory + int GetStringValue(IN const char* strKey, MVCC_STRINGVALUE *pStringValue); + int SetStringValue(IN const char* strKey, IN const char * strValue); + + // ch:执行一次Command型命令,如 UserSetSave,详细内容å‚考SDK安装目录下的 MvCameraNode.xlsx 文件 + // en:Execute Command once, such as UserSetSave, for details please refer to MvCameraNode.xlsx file under SDK installation directory + int CommandExecute(IN const char* strKey); + + // ch:探测网络最佳包大å°(åªå¯¹GigE相机有效) | en:Detection network optimal package size(It only works for the GigE camera) + int GetOptimalPacketSize(unsigned int* pOptimalPacketSize); + + // ch:注册消æ¯å¼‚常回调 | en:Register Message Exception CallBack + int RegisterExceptionCallBack(void(__stdcall* cbException)(unsigned int nMsgType, void* pUser), void* pUser); + + // ch:注册å•ä¸ªäº‹ä»¶å›žè°ƒ | en:Register Event CallBack + int RegisterEventCallBack(const char* pEventName, void(__stdcall* cbEvent)(MV_EVENT_OUT_INFO * pEventInfo, void* pUser), void* pUser); + + // ch:强制IP | en:Force IP + int ForceIp(unsigned int nIP, unsigned int nSubNetMask, unsigned int nDefaultGateWay); + + // ch:é…ç½®IPæ–¹å¼ | en:IP configuration method + int SetIpConfig(unsigned int nType); + + // ch:è®¾ç½®ç½‘ç»œä¼ è¾“æ¨¡å¼ | en:Set Net Transfer Mode + int SetNetTransMode(unsigned int nType); + + // ch:åƒç´ æ ¼å¼è½¬æ¢ | en:Pixel format conversion + int ConvertPixelType(MV_CC_PIXEL_CONVERT_PARAM* pstCvtParam); + + // ch:ä¿å­˜å›¾ç‰‡ | en:save image + int SaveImage(MV_SAVE_IMAGE_PARAM_EX* pstParam); + + // ch:ä¿å­˜å›¾ç‰‡ä¸ºæ–‡ä»¶ | en:Save the image as a file + int SaveImageToFile(MV_SAVE_IMG_TO_FILE_PARAM* pstParam); + + // ch:绘制圆形辅助线 | en:Draw circle auxiliary line + int DrawCircle(MVCC_CIRCLE_INFO* pCircleInfo); + + // ch:绘制线形辅助线 | en:Draw lines auxiliary line + int DrawLines(MVCC_LINES_INFO* pLinesInfo); + + int SetGrabStrategy(MV_GRAB_STRATEGY enGrabStrategy); + + int GetNodeAccessMode(const char* strName, MV_XML_AccessMode* penAccessMode); + + int EnumerateTls() { + return MV_CC_EnumerateTls(); + } +private: + + void* m_hDevHandle; + +}; + +#endif//_MV_CAMERA_H_ diff --git a/DeviceAdapters/HydraLMT200/ITKHydra.h b/DeviceAdapters/HydraLMT200/ITKHydra.h index 1370303ae..022e66277 100644 --- a/DeviceAdapters/HydraLMT200/ITKHydra.h +++ b/DeviceAdapters/HydraLMT200/ITKHydra.h @@ -6,7 +6,7 @@ // DESCRIPTION: ITK Hydra Controller Driver // XY Stage // -// AUTHOR: Steven Fletcher, derived from Corvus adapter written by Johan Henriksson, mahogny@areta.org, derived from Märzhauser adapter +// AUTHOR: Steven Fletcher, derived from Corvus adapter written by Johan Henriksson, mahogny@areta.org, derived from Märzhauser adapter // COPYRIGHT: Steven Fletcher 2017 // LICENSE: This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public diff --git a/DeviceAdapters/IDSPeak/IDSPeak.cpp b/DeviceAdapters/IDSPeak/IDSPeak.cpp index 310ade461..89c6dad8d 100644 --- a/DeviceAdapters/IDSPeak/IDSPeak.cpp +++ b/DeviceAdapters/IDSPeak/IDSPeak.cpp @@ -1178,7 +1178,7 @@ int CIDSPeak::InsertImage() // Important: metadata about the image are generated here: Metadata md; - md.put("Camera", label); + md.put(MM::g_Keyword_Metadata_CameraLabel, label); md.put(MM::g_Keyword_Elapsed_Time_ms, CDeviceUtils::ConvertToString((timeStamp - sequenceStartTime_).getMsec())); md.put(MM::g_Keyword_Metadata_ROI_X, CDeviceUtils::ConvertToString((long)roiX_)); md.put(MM::g_Keyword_Metadata_ROI_Y, CDeviceUtils::ConvertToString((long)roiY_)); diff --git a/DeviceAdapters/IDS_uEye/IDS_uEye.cpp b/DeviceAdapters/IDS_uEye/IDS_uEye.cpp index 750d239c2..d88cda6b8 100644 --- a/DeviceAdapters/IDS_uEye/IDS_uEye.cpp +++ b/DeviceAdapters/IDS_uEye/IDS_uEye.cpp @@ -1311,7 +1311,7 @@ int CIDS_uEye::InsertImage() */ // Add our own metadata - md.put("Camera", label); + md.put(MM::g_Keyword_Metadata_CameraLabel, label); md.put(MM::g_Keyword_Elapsed_Time_ms, CDeviceUtils::ConvertToString((timeStamp - sequenceStartTime_).getMsec())); md.put(MM::g_Keyword_Metadata_ImageNumber, CDeviceUtils::ConvertToString(imageCounter_)); md.put(MM::g_Keyword_Metadata_ROI_X, CDeviceUtils::ConvertToString( (long) roiX_)); diff --git a/DeviceAdapters/IIDC/MMIIDCCamera.cpp b/DeviceAdapters/IIDC/MMIIDCCamera.cpp index abd4a9444..c9eafc165 100644 --- a/DeviceAdapters/IIDC/MMIIDCCamera.cpp +++ b/DeviceAdapters/IIDC/MMIIDCCamera.cpp @@ -2071,7 +2071,7 @@ MMIIDCCamera::ProcessedSequenceCallback(const void* pixels, char label[MM::MaxStrLength]; GetLabel(label); - md.put("Camera", label); + md.put(MM::g_Keyword_Metadata_CameraLabel, label); #ifndef _WIN32 // The Windows CMU backend does not provide a valid timestamp (the field diff --git a/DeviceAdapters/MCL_MicroDrive/MicroDriveZStage.cpp b/DeviceAdapters/MCL_MicroDrive/MicroDriveZStage.cpp index 893109d66..85434d76e 100644 --- a/DeviceAdapters/MCL_MicroDrive/MicroDriveZStage.cpp +++ b/DeviceAdapters/MCL_MicroDrive/MicroDriveZStage.cpp @@ -370,7 +370,7 @@ int MCL_MicroDrive_ZStage::OnPositionMm(MM::PropertyBase* pProp, MM::ActionType err = GetPositionMm(z); if(err != MCL_SUCCESS) return err; - err = BeginMovementThread(STANDARD_MOVE_TYPE, z); + err = BeginMovementThread(STANDARD_MOVE_TYPE, pos); if (err != DEVICE_OK) return err; } diff --git a/DeviceAdapters/MP285/MP285XYStage.cpp b/DeviceAdapters/MP285/MP285XYStage.cpp index faf5901f2..964a1b886 100644 --- a/DeviceAdapters/MP285/MP285XYStage.cpp +++ b/DeviceAdapters/MP285/MP285XYStage.cpp @@ -258,7 +258,7 @@ int XYStage::SetMotionMode(long lMotionMode) } // -// Returns current X-Y position in µm. +// Returns current X-Y position in µm. // int XYStage::GetPositionUm(double& dXPosUm, double& dYPosUm) { @@ -317,7 +317,7 @@ int XYStage::GetPositionUm(double& dXPosUm, double& dYPosUm) } // -// Move x-y stage to a relative distance from current position in µm +// Move x-y stage to a relative distance from current position in µm // int XYStage::SetRelativePositionUm(double dXPosUm, double dYPosUm) { @@ -368,7 +368,7 @@ int XYStage::SetRelativePositionUm(double dXPosUm, double dYPosUm) } // -// Move 2 x-y position in µm +// Move 2 x-y position in µm // int XYStage::SetPositionUm(double dXPosUm, double dYPosUm) { diff --git a/DeviceAdapters/MT20/tinystr.cpp b/DeviceAdapters/MT20/tinystr.cpp old mode 100755 new mode 100644 index 41ab27661..eddcaa861 --- a/DeviceAdapters/MT20/tinystr.cpp +++ b/DeviceAdapters/MT20/tinystr.cpp @@ -24,7 +24,7 @@ distribution. */ /* - * THIS FILE WAS ALTERED BY Tyge Løvset, 7. April 2005. + * THIS FILE WAS ALTERED BY Tyge Løvset, 7. April 2005. */ diff --git a/DeviceAdapters/Makefile.am b/DeviceAdapters/Makefile.am index 5325c1738..78b13951b 100644 --- a/DeviceAdapters/Makefile.am +++ b/DeviceAdapters/Makefile.am @@ -13,6 +13,9 @@ endif if BUILD_ANDORSDK3 ANDORSDK3 = AndorSDK3 endif +if BUILD_ARAVIS_LINUX + ARAVIS = Aravis +endif if BUILD_BASLER_LINUX BASLER = Basler endif @@ -81,6 +84,7 @@ SUBDIRS = \ $(ANDOR) \ $(ANDORLASERCOMBINER) \ $(ANDORSDK3) \ + $(ARAVIS) \ $(BASLER) \ $(DC1394) \ $(FAKECAMERA) \ diff --git a/DeviceAdapters/Marzhauser-LStep/LStep.cpp b/DeviceAdapters/Marzhauser-LStep/LStep.cpp index c7fd4ed0a..8b2e692ad 100644 --- a/DeviceAdapters/Marzhauser-LStep/LStep.cpp +++ b/DeviceAdapters/Marzhauser-LStep/LStep.cpp @@ -192,7 +192,7 @@ XYStage::XYStage() : speedX_(20.0), //[mm/s] speedY_(20.0), //[mm/s] - accelX_(0.2), //[m/s²] + accelX_(0.2), //[m/s²] accelY_(0.2), originX_(0), originY_(0), @@ -316,12 +316,12 @@ int XYStage::Initialize() SetPropertyLimits("SpeedY [mm/s]", 0.01, 20*pitch); // mm/s //****************************************************************************************************************************************** - // Accel (Acceleration (in m/s²) + // Accel (Acceleration (in m/s²) // ----- pAct = new CPropertyAction (this, &XYStage::OnAccelX); - ret = QueryCommand("?accel x", resp); //Lstep : 0,01 to 20,00 m/s² + ret = QueryCommand("?accel x", resp); //Lstep : 0,01 to 20,00 m/s² if (ret != DEVICE_OK) return DEVICE_UNSUPPORTED_COMMAND; ret = CreateProperty("Acceleration X [m/s^2]", resp.c_str(), MM::Float, false, pAct); @@ -378,12 +378,12 @@ bool XYStage::Busy() /** - * Returns current position in µm. + * Returns current position in µm. */ int XYStage::GetPositionUm(double& x, double& y) { int ret; - // switch to µm + // switch to µm ret = SendCommand("!dim 1 1"); if (ret != DEVICE_OK) return ret; @@ -404,7 +404,7 @@ int ret; /** - * Sets position in µm + * Sets position in µm */ int XYStage::SetPositionUm(double x, double y) { @@ -412,7 +412,7 @@ int XYStage::SetPositionUm(double x, double y) os << "XYStage::SetPositionUm() " << x << " " << y; this->LogMessage(os.str().c_str()); - // switch to µm + // switch to µm int ret = SendCommand("!dim 1 1"); if (ret != DEVICE_OK) return ret; @@ -438,7 +438,7 @@ int XYStage::SetPositionUm(double x, double y) /** - * Sets relative position in µm + * Sets relative position in µm */ int XYStage::SetRelativePositionUm(double dx, double dy) { @@ -446,7 +446,7 @@ int XYStage::SetRelativePositionUm(double dx, double dy) os << "XYStage::SetPositionUm() " << dx << " " << dy; this->LogMessage(os.str().c_str()); - // switch to µm + // switch to µm int ret = SendCommand("!dim 1 1"); if (ret != DEVICE_OK) return ret; @@ -693,7 +693,7 @@ int XYStage::GetLimitsUm(double& xMin, double& xMax, double& yMin, double& yMax) { if (!range_measured_) return DEVICE_UNKNOWN_POSITION; - // switch to µm + // switch to µm int ret = SendCommand("!dim 1 1"); if (ret != DEVICE_OK) return ret; @@ -1094,7 +1094,7 @@ ZStage::ZStage() : range_measured_(false), stepSizeUm_(0.1), speedZ_(20.0), //[mm/s] - accelZ_(0.2), //[m/s²] + accelZ_(0.2), //[m/s²] originZ_(0), pitchZ_(1) @@ -1183,7 +1183,7 @@ int ZStage::Initialize() SetPropertyLimits("SpeedZ [mm/s]", 0.001, 20*pitchZ_); // mm/s - // Accel (Acceleration (in m/s²) + // Accel (Acceleration (in m/s²) // ----- ret = QueryCommand("?accel z", resp); if (ret != DEVICE_OK) return DEVICE_UNSUPPORTED_COMMAND; @@ -1241,7 +1241,7 @@ int ZStage::SetPositionUm(double pos) os << "ZStage::SetPositionUm() " << pos; this->LogMessage(os.str().c_str()); - // switch to µm + // switch to µm int ret = SendCommand("!dim z 1"); if (ret != DEVICE_OK) return ret; @@ -1271,7 +1271,7 @@ int ZStage::SetPositionUm(double pos) int ZStage::SetRelativePositionUm(double d) { - // switch to µm + // switch to µm int ret = SendCommand("!dim z 1"); if (ret != DEVICE_OK) return ret; @@ -1300,7 +1300,7 @@ int ZStage::SetRelativePositionUm(double d) int ZStage::GetPositionUm(double& pos) { int ret; - // switch to µm + // switch to µm ret = SendCommand("!dim z 1"); if (ret != DEVICE_OK) return ret; @@ -1463,7 +1463,7 @@ int ZStage::GetLimits(double& min, double& max) { if (!range_measured_) return DEVICE_UNKNOWN_POSITION; - // switch to µm + // switch to µm int ret = SendCommand("!dim z 1"); if (ret != DEVICE_OK) return ret; diff --git a/DeviceAdapters/Marzhauser/Marzhauser.cpp b/DeviceAdapters/Marzhauser/Marzhauser.cpp index c6d4eb8ea..9d2868ceb 100644 --- a/DeviceAdapters/Marzhauser/Marzhauser.cpp +++ b/DeviceAdapters/Marzhauser/Marzhauser.cpp @@ -217,7 +217,7 @@ XYStage::XYStage() : stepSizeYUm_(0.0012), speedX_(20.0), //[mm/s] speedY_(20.0), //[mm/s] - accelX_(0.2), //[m/s²] + accelX_(0.2), //[m/s²] accelY_(0.2), originX_(0), originY_(0) @@ -338,7 +338,7 @@ int XYStage::Initialize() SetPropertyLimits("SpeedY [mm/s]", 0.001, 100.0); // mm/s - // Accel (Acceleration (in m/s²) + // Accel (Acceleration (in m/s²) // ----- pAct = new CPropertyAction (this, &XYStage::OnAccelX); @@ -359,7 +359,7 @@ int XYStage::Initialize() SetPropertyLimits("Acceleration Y [m/s^2]", 0.01, 2.0); - // Backlash (in µm) + // Backlash (in µm) ret = QueryCommand("?backlash x", resp); if (ret == DEVICE_OK) { @@ -416,12 +416,12 @@ bool XYStage::Busy() /** - * Returns current position in µm. + * Returns current position in µm. */ int XYStage::GetPositionUm(double& x, double& y) { int ret; - // switch to µm + // switch to µm ret = SendCommand("!dim 1 1"); if (ret != DEVICE_OK) return ret; @@ -442,7 +442,7 @@ int ret; /** - * Sets position in µm + * Sets position in µm */ int XYStage::SetPositionUm(double x, double y) { @@ -450,7 +450,7 @@ int XYStage::SetPositionUm(double x, double y) os << "XYStage::SetPositionUm() " << x << " " << y; this->LogMessage(os.str().c_str()); - // switch to µm + // switch to µm int ret = SendCommand("!dim 1 1"); if (ret != DEVICE_OK) return ret; @@ -476,7 +476,7 @@ int XYStage::SetPositionUm(double x, double y) /** - * Sets relative position in µm + * Sets relative position in µm */ int XYStage::SetRelativePositionUm(double dx, double dy) { @@ -484,7 +484,7 @@ int XYStage::SetRelativePositionUm(double dx, double dy) os << "XYStage::SetPositionUm() " << dx << " " << dy; this->LogMessage(os.str().c_str()); - // switch to µm + // switch to µm int ret = SendCommand("!dim 1 1"); if (ret != DEVICE_OK) return ret; @@ -729,7 +729,7 @@ int XYStage::GetLimitsUm(double& xMin, double& xMax, double& yMin, double& yMax) { if (!range_measured_) return DEVICE_UNKNOWN_POSITION; - // switch to µm + // switch to µm int ret = SendCommand("!dim 1 1"); if (ret != DEVICE_OK) return ret; @@ -1214,7 +1214,7 @@ ZStage::ZStage() : range_measured_(false), stepSizeUm_(0.1), speedZ_(20.0), //[mm/s] - accelZ_(0.2), //[m/s²] + accelZ_(0.2), //[m/s²] originZ_(0), sequenceable_(false), nrEvents_(1024) @@ -1310,7 +1310,7 @@ int ZStage::Initialize() SetPropertyLimits("SpeedZ [mm/s]", 0.001, 100.0); // mm/s - // Accel (Acceleration (in m/s²) + // Accel (Acceleration (in m/s²) // ----- ret = QueryCommand("?accel z", resp); if (ret != DEVICE_OK) return DEVICE_UNSUPPORTED_COMMAND; @@ -1320,7 +1320,7 @@ int ZStage::Initialize() if (ret != DEVICE_OK) return ret; SetPropertyLimits("Acceleration Z [m/s^2]", 0.01, 2.0); - // Backlash (in µm) + // Backlash (in µm) // get current Backlash from the controller ret = QueryCommand("?backlash z", resp); if (ret == DEVICE_OK) @@ -1391,7 +1391,7 @@ int ZStage::SetPositionUm(double pos) os << "ZStage::SetPositionUm() " << pos; this->LogMessage(os.str().c_str()); - // switch to µm + // switch to µm int ret = SendCommand("!dim z 1"); if (ret != DEVICE_OK) return ret; @@ -1421,7 +1421,7 @@ int ZStage::SetPositionUm(double pos) int ZStage::SetRelativePositionUm(double d) { - // switch to µm + // switch to µm int ret = SendCommand("!dim z 1"); if (ret != DEVICE_OK) return ret; @@ -1450,7 +1450,7 @@ int ZStage::SetRelativePositionUm(double d) int ZStage::GetPositionUm(double& pos) { int ret; - // switch to µm + // switch to µm ret = SendCommand("!dim z 1"); if (ret != DEVICE_OK) return ret; @@ -1613,7 +1613,7 @@ int ZStage::GetLimits(double& min, double& max) { if (!range_measured_) return DEVICE_UNKNOWN_POSITION; - // switch to µm + // switch to µm int ret = SendCommand("!dim z 1"); if (ret != DEVICE_OK) return ret; @@ -1753,7 +1753,7 @@ int ZStage::SendStageSequence() is2 >> err; if (err != 0) return DEVICE_SERIAL_INVALID_RESPONSE; - // switch to µm + // switch to µm ret = SendCommand("!dim z 1"); if (ret != DEVICE_OK) return ret; @@ -2048,7 +2048,7 @@ AStage::AStage() : TangoBase(this), range_measured_(false), speed_(20.0), //[mm/s] - accel_(0.2), //[m/s²] + accel_(0.2), //[m/s²] origin_(0), stepSizeUm_(0.1) { @@ -2132,7 +2132,7 @@ int AStage::Initialize() SetPropertyLimits("SpeedA [mm/s]", 0.001, 100.0); // mm/s - // Accel (Acceleration (in m/s²) + // Accel (Acceleration (in m/s²) // ----- ret = QueryCommand("?accel a", resp); if (ret != DEVICE_OK) return DEVICE_UNSUPPORTED_COMMAND; @@ -2142,7 +2142,7 @@ int AStage::Initialize() if (ret != DEVICE_OK) return ret; SetPropertyLimits("Acceleration A [m/s^2]", 0.01, 2.0); - // Backlash (in µm) + // Backlash (in µm) // get current Backlash from the controller ret = QueryCommand("?backlash a", resp); if (ret == DEVICE_OK) @@ -2200,7 +2200,7 @@ int AStage::SetPositionUm(double pos) os << "AStage::SetPositionUm() " << pos; this->LogMessage(os.str().c_str()); - // switch to µm + // switch to µm int ret = SendCommand("!dim a 1"); if (ret != DEVICE_OK) return ret; @@ -2230,7 +2230,7 @@ int AStage::SetPositionUm(double pos) int AStage::SetRelativePositionUm(double d) { - // switch to µm + // switch to µm int ret = SendCommand("!dim a 1"); if (ret != DEVICE_OK) return ret; @@ -2261,7 +2261,7 @@ int AStage::SetRelativePositionUm(double d) int AStage::GetPositionUm(double& pos) { int ret; - // switch to µm + // switch to µm ret = SendCommand("!dim a 1"); if (ret != DEVICE_OK) return ret; @@ -2428,7 +2428,7 @@ int AStage::GetLimits(double& min, double& max) { if (!range_measured_) return DEVICE_UNKNOWN_POSITION; - // switch to µm + // switch to µm int ret = SendCommand("!dim a 1"); if (ret != DEVICE_OK) return ret; @@ -2998,7 +2998,7 @@ int LED100::Initialize() // Fire // create property Fire if Tango command flash is possible with this version ostringstream cmd; - cmd << "!flash -0.01"; //example 10µs pulse + cmd << "!flash -0.01"; //example 10µs pulse ret = SendCommand(cmd.str().c_str()); if (ret !=DEVICE_OK) return ret; diff --git a/DeviceAdapters/MatrixVision/mvIMPACT_Acquire_Device.cpp b/DeviceAdapters/MatrixVision/mvIMPACT_Acquire_Device.cpp index b5d2a488e..9150fd222 100644 --- a/DeviceAdapters/MatrixVision/mvIMPACT_Acquire_Device.cpp +++ b/DeviceAdapters/MatrixVision/mvIMPACT_Acquire_Device.cpp @@ -967,7 +967,7 @@ int mvIMPACT_Acquire_Device::InsertImage( void ) Metadata md; char label[MM::MaxStrLength]; GetLabel( label ); - md.put( "Camera", label ); + md.put(MM::g_Keyword_Metadata_CameraLabel, label ); MM::MMTime timeStamp = readoutStartTime_; md.put( MM::g_Keyword_Elapsed_Time_ms, CDeviceUtils::ConvertToString( ( timeStamp - sequenceStartTime_ ).getMsec() ) ); md.put( MM::g_Keyword_Metadata_ImageNumber, CDeviceUtils::ConvertToString( static_cast( pCurrentRequest_->infoFrameID.read() ) ) ); diff --git a/DeviceAdapters/Mightex_BLS/Mightex_BLSDriver_SDK.h b/DeviceAdapters/Mightex_BLS/Mightex_BLSDriver_SDK.h old mode 100755 new mode 100644 index 120ce032b..bf68adba9 --- a/DeviceAdapters/Mightex_BLS/Mightex_BLSDriver_SDK.h +++ b/DeviceAdapters/Mightex_BLS/Mightex_BLSDriver_SDK.h @@ -1,5 +1,5 @@ -#define MAX_PULSE_COUNT 21 // For SX Modules, it¡¯s 3 instead of 128. +#define MAX_PULSE_COUNT 21 // For SX Modules, it’s 3 instead of 128. #define MAX_PROFILE_ITEM 128 #define DISABLE_MODE 0 diff --git a/DeviceAdapters/Mightex_C_Cam/Mightex_USBCamera.cpp b/DeviceAdapters/Mightex_C_Cam/Mightex_USBCamera.cpp index 852ea683f..6e47d7168 100755 --- a/DeviceAdapters/Mightex_C_Cam/Mightex_USBCamera.cpp +++ b/DeviceAdapters/Mightex_C_Cam/Mightex_USBCamera.cpp @@ -1553,7 +1553,7 @@ int CMightex_BUF_USBCCDCamera::InsertImage() // Important: metadata about the image are generated here: Metadata md; - md.put("Camera", label); + md.put(MM::g_Keyword_Metadata_CameraLabel, label); md.put(MM::g_Keyword_Elapsed_Time_ms, CDeviceUtils::ConvertToString((timeStamp - sequenceStartTime_).getMsec())); md.put(MM::g_Keyword_Metadata_ROI_X, CDeviceUtils::ConvertToString( (long) roiX_)); md.put(MM::g_Keyword_Metadata_ROI_Y, CDeviceUtils::ConvertToString( (long) roiY_)); diff --git a/DeviceAdapters/NIDAQ/NIAnalogOutputPort.cpp b/DeviceAdapters/NIDAQ/NIAnalogOutputPort.cpp index fec0c8d64..f62812321 100644 --- a/DeviceAdapters/NIDAQ/NIAnalogOutputPort.cpp +++ b/DeviceAdapters/NIDAQ/NIAnalogOutputPort.cpp @@ -199,6 +199,12 @@ int NIAnalogOutputPort::StartDASequence() if (task_) StopTask(); + // not checkint the size of the array will lead to a crash in the next line + if (sentSequence_.size() < 1) { + // Do we neet to return an error code? Probably... + return DEVICE_OK; + } + // probably beneficial in all cases to move to the first position, // essential if we are transitioning post-exposure double volt0 = sentSequence_[0]; diff --git a/DeviceAdapters/NIDAQ/NIDAQ.cpp b/DeviceAdapters/NIDAQ/NIDAQ.cpp index 67ed339f8..01f812d2e 100644 --- a/DeviceAdapters/NIDAQ/NIDAQ.cpp +++ b/DeviceAdapters/NIDAQ/NIDAQ.cpp @@ -529,9 +529,7 @@ std::string NIDAQHub::GetPhysicalChannelListForSequencing(std::vector inline int NIDAQHub::GetLCMSamplesPerChannel(size_t& seqLen, std::vector> channelSequences) const { - // Use an arbitrary but reasonable limit to prevent - // overflow or excessive memory consumption. - const uint64_t factorLimit = 2 << 14; + const uint64_t factorLimit = channelSequences.size() * maxSequenceLength_; uint64_t len = 1; for (unsigned int i = 0; i < channelSequences.size(); ++i) diff --git a/DeviceAdapters/NIDAQ/NIDigitalOutputPort.cpp b/DeviceAdapters/NIDAQ/NIDigitalOutputPort.cpp index 06f52de3c..4f8e74379 100644 --- a/DeviceAdapters/NIDAQ/NIDigitalOutputPort.cpp +++ b/DeviceAdapters/NIDAQ/NIDigitalOutputPort.cpp @@ -98,6 +98,22 @@ int DigitalOutputPort::Initialize() supportsBlankingAndSequencing_ = true; GetHub()->StopDOBlankingAndSequence(portWidth_); + // Some cards lie about their portwidth, if blanking does not work, try 32 bits + // Workaround for possible DAQmx bug on USB-6341; see + // https://forums.ni.com/t5/Multifunction-DAQ/problem-with-correlated-DIO-on-USB-6341/td-p/3344066 + if (!supportsBlankingAndSequencing_) + { + uInt32 oldPortWidth = portWidth_; + portWidth_ = 32; + if (GetHub()->StartDOBlankingAndOrSequence(tmpNiPort, portWidth_, true, false, 0, false, tmpTriggerTerminal) == DEVICE_OK) + supportsBlankingAndSequencing_ = true; + GetHub()->StopDOBlankingAndSequence(portWidth_); + if (!supportsBlankingAndSequencing_) + { + portWidth_ = oldPortWidth; + } + } + CPropertyAction* pAct; if (supportsBlankingAndSequencing_) { diff --git a/DeviceAdapters/Omicron/OmicronDeviceDriver.cpp b/DeviceAdapters/Omicron/OmicronDeviceDriver.cpp index e192d3154..4e70eea57 100644 --- a/DeviceAdapters/Omicron/OmicronDeviceDriver.cpp +++ b/DeviceAdapters/Omicron/OmicronDeviceDriver.cpp @@ -13,7 +13,7 @@ const char* OMIDLLname = "OmicronxXDevices64.dll"; #else const char* OMIDLLname = "OmicronxXDevices.dll"; #endif -//[DllImport(“msvcrt.dll”, CallingConvention = CallingConvention.Cdecl)] +//[DllImport(“msvcrt.dllâ€, CallingConvention = CallingConvention.Cdecl)] TxXGetDLLVersion ExXGetDLLVersion = NULL; diff --git a/DeviceAdapters/OpenCVgrabber/OpenCVgrabber.cpp b/DeviceAdapters/OpenCVgrabber/OpenCVgrabber.cpp index 42d027286..acdaf1299 100644 --- a/DeviceAdapters/OpenCVgrabber/OpenCVgrabber.cpp +++ b/DeviceAdapters/OpenCVgrabber/OpenCVgrabber.cpp @@ -809,7 +809,7 @@ int COpenCVgrabber::InsertImage() // Important: metadata about the image are generated here: Metadata md; - md.put("Camera", label); + md.put(MM::g_Keyword_Metadata_CameraLabel, label); md.put(MM::g_Keyword_Elapsed_Time_ms, CDeviceUtils::ConvertToString((timeStamp - sequenceStartTime_).getMsec())); md.put(MM::g_Keyword_Metadata_ImageNumber, CDeviceUtils::ConvertToString(imageCounter_)); md.put(MM::g_Keyword_Metadata_ROI_X, CDeviceUtils::ConvertToString( (long) roiX_)); diff --git a/DeviceAdapters/OpenFlexure/OpenFlexure.cpp b/DeviceAdapters/OpenFlexure/OpenFlexure.cpp new file mode 100644 index 000000000..ab36480df --- /dev/null +++ b/DeviceAdapters/OpenFlexure/OpenFlexure.cpp @@ -0,0 +1,966 @@ +/////////////////////////////////////////////////////////////////////////////// +// FILE: OpenFlexure.cpp +// PROJECT: Micro-Manager +// SUBSYSTEM: DeviceAdapters +//----------------------------------------------------------------------------- +// DESCRIPTION: Adapter for the OpenFlexure Microscope. This adapter is used on the v5 Sangaboard. +// - Tested with Sangaboard Firmware v1.0.1-dev || Sangaboard v0.5.x +// +// AUTHOR: Samdrea Hsu, samdreahsu@gmail.com, 09/23/2024 +// +// COPYRIGHT: Samdrea Hsu +// +// 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 "OpenFlexure.h" +#include "ModuleInterface.h" +#include +#include +#include +#include +#include +#include + +#ifdef WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Exported MMDevice API +/////////////////////////////////////////////////////////////////////////////// +MODULE_API void InitializeModuleData() +{ + RegisterDevice(g_XYStageDeviceName, MM::XYStageDevice, "XY Stage"); + RegisterDevice(g_HubDeviceName, MM::HubDevice, "Sangaboard Hub"); + RegisterDevice(g_ZStageDeviceName, MM::StageDevice, "Z Stage"); + RegisterDevice(g_ShutterDeviceName, MM::ShutterDevice, "LED Illumination"); +} + +MODULE_API MM::Device* CreateDevice(const char* deviceName) +{ + if (deviceName == 0) + return 0; + + if (strcmp(deviceName, g_XYStageDeviceName) == 0) + return new XYStage; // Create XY stage + else if (strcmp(deviceName, g_HubDeviceName) == 0) + return new SangaBoardHub; // Create hub + else if (strcmp(deviceName, g_ZStageDeviceName) == 0) + return new ZStage; // Create Z stage + else if (strcmp(deviceName, g_ShutterDeviceName) == 0) + return new LEDIllumination; // Create LED shutter device + + return 0; // Device name not recognized +} + +MODULE_API void DeleteDevice(MM::Device* pDevice) +{ + delete pDevice; +} + +/////////////////////////////////////////////////////////////////////////////// +// Sangaboard hub implementation +/////////////////////////////////////////////////////////////////////////////// + +SangaBoardHub::SangaBoardHub() : initialized_(false), port_("Undefined"), portAvailable_(false), busy_(false) +{ + // Initialize default error messages + InitializeDefaultErrorMessages(); + + // Initialize custom error messages + SetErrorText(DEVICE_STAGE_STILL_MOVING, g_Msg_DEVICE_STAGE_STILL_MOVING); + + // Pre-initialization property: port name + CPropertyAction* pAct = new CPropertyAction(this, &SangaBoardHub::OnPort); + int ret = CreateProperty(MM::g_Keyword_Port, "Undefined", MM::String, false, pAct, true); + +} + +SangaBoardHub::~SangaBoardHub() +{ + Shutdown(); +} + +int SangaBoardHub::Initialize() +{ + // Send a command to check if Sangaboard is connected + SendCommand("version", _serial_answer); // Example response is "Sangaboard v0.5.x" + + // Check if the response contains "Sangaboard" + if (_serial_answer.find("Sangaboard") == std::string::npos) { + // If not found, return an error code + return DEVICE_NOT_CONNECTED; + } + + initialized_ = true; + + // Set up Manual Command Interface to send command directly to SangaBoard + CPropertyAction* pCommand = new CPropertyAction(this, &SangaBoardHub::OnManualCommand); + int ret = CreateProperty(g_Keyword_Command, "", MM::String, false, pCommand); + if (DEVICE_OK != ret) return DEVICE_CAN_NOT_SET_PROPERTY; + + // Set up property to display most recent serial response (read-only) + ret = CreateProperty(g_Keyword_Response, "", MM::String, false); + if (DEVICE_OK != ret) return DEVICE_CAN_NOT_SET_PROPERTY; + + // Initialize the cache values to the current values stored on hardware + this->SyncState(); + + // Set up Step Delay property for changing velocity + CPropertyAction* pStepDel = new CPropertyAction(this, &SangaBoardHub::OnStepDelay); + ret = CreateIntegerProperty(g_Keyword_StepDelay, step_delay_, false, pStepDel); + if (DEVICE_OK != ret) return DEVICE_CAN_NOT_SET_PROPERTY; + AddAllowedValue(g_Keyword_StepDelay, "1000"); + AddAllowedValue(g_Keyword_StepDelay, "2000"); + AddAllowedValue(g_Keyword_StepDelay, "3000"); + AddAllowedValue(g_Keyword_StepDelay, "4000"); + AddAllowedValue(g_Keyword_StepDelay, "5000"); + + // Set up Ramp Time property for changing acceleration + CPropertyAction* pRampTime = new CPropertyAction(this, &SangaBoardHub::OnRampTime); + ret = CreateIntegerProperty(g_Keyword_RampTime, ramp_time_, false, pRampTime); + if (DEVICE_OK != ret) return DEVICE_CAN_NOT_SET_PROPERTY; + AddAllowedValue(g_Keyword_RampTime, "0", 0); + AddAllowedValue(g_Keyword_RampTime, "100000"); // Not really sure if these values really do much for acceleration... + AddAllowedValue(g_Keyword_RampTime, "200000"); + AddAllowedValue(g_Keyword_RampTime, "300000"); + + // Set up extra functions drop down menu + CPropertyAction* pExtras= new CPropertyAction(this, &SangaBoardHub::OnExtraCommands); + ret = CreateStringProperty(g_Keyword_Extras, g_Keyword_None, false, pExtras); + if (DEVICE_OK != ret) return DEVICE_CAN_NOT_SET_PROPERTY; + AddAllowedValue(g_Keyword_Extras, g_ExtraCommand_Stop); + AddAllowedValue(g_Keyword_Extras, g_ExtraCommand_Zero); + AddAllowedValue(g_Keyword_Extras, g_ExtraCommand_Release); + AddAllowedValue(g_Keyword_Extras, g_ExtraCommand_Version); + + return DEVICE_OK; +} + +int SangaBoardHub::Shutdown() +{ + initialized_ = false; + return DEVICE_OK; +} + +int SangaBoardHub::DetectInstalledDevices() +{ + ClearInstalledDevices(); + + char hubName[MM::MaxStrLength]; + GetName(hubName); // this device name + for (unsigned i = 0; i < GetNumberOfDevices(); i++) + { + char deviceName[MM::MaxStrLength]; + bool success = GetDeviceName(i, deviceName, MM::MaxStrLength); + if (success && (strcmp(hubName, deviceName) != 0)) + { + MM::Device* pDev = CreateDevice(deviceName); + AddInstalledDevice(pDev); + } + } + return DEVICE_OK; +} + +void SangaBoardHub::GetName(char* name) const +{ + CDeviceUtils::CopyLimitedString(name, g_HubDeviceName); +} + +/* +* Make sure no moves are in progress +*/ +bool SangaBoardHub::Busy() +{ + //MM::MMTime timeout(0, 500000); // wait for 0.5 sec + + PurgeComPort(port_.c_str()); + + // Send a query to check if stage is moving + int ret = SendSerialCommand(port_.c_str(), "moving?", "\n"); + + // Check response + GetSerialAnswer(port_.c_str(), "\r", _serial_answer); // Should return "\ntrue" or "\nfalse" + + return _serial_answer.find("true") != std::string::npos; +} + +/////////////////////////////////////////////////////////////////////////////// +// SangaboardHub Action handlers +/////////////////////////////////////////////////////////////////////////////// + +int SangaBoardHub::OnPort(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::BeforeGet) + { + pProp->Set(port_.c_str()); + } + else if (eAct == MM::AfterSet) + { + pProp->Get(port_); + portAvailable_ = true; + } + + return DEVICE_OK; +} + +int SangaBoardHub::OnManualCommand(MM::PropertyBase* pProp, MM::ActionType pAct) +{ + if (pAct == MM::BeforeGet) + { + pProp->Set(_command.c_str()); + } + else if (pAct == MM::AfterSet) + { + // Get and send the command typed into property + pProp->Get(_command); + PurgeComPort(port_.c_str()); + SendCommand(_command, _serial_answer); + + // Remember the response + std::string ans = _serial_answer; + + // Sync the hub itself + this->SyncState(); + + // Sync the peripherals xy, z, and LED to possible changes made through serial call + this->SyncPeripherals(); + + // Display the response to the command + SetProperty(g_Keyword_Response, ans.c_str()); + } + + // Search for error + return _serial_answer.find("ERROR") != std::string::npos ? DEVICE_ERR : DEVICE_OK; +} + +int SangaBoardHub::OnStepDelay(MM::PropertyBase* pPropt, MM::ActionType eAct) +{ + if (eAct == MM::BeforeGet) + { + pPropt->Set(step_delay_); + } + else if (eAct == MM::AfterSet) + { + pPropt->Get(step_delay_); + std::string cmd = "dt " + std::to_string(step_delay_); + this->SendCommand(cmd, _serial_answer); + } + + return DEVICE_OK; +} + +int SangaBoardHub::OnRampTime(MM::PropertyBase* pPropt, MM::ActionType eAct) +{ + if (eAct == MM::BeforeGet) + { + pPropt->Set(ramp_time_); + } + else if (eAct == MM::AfterSet) + { + pPropt->Get(ramp_time_); + std::string cmd = "ramp_time " + std::to_string(ramp_time_); + this->SendCommand(cmd, _serial_answer); + } + + return DEVICE_OK; + +} + +int SangaBoardHub::OnExtraCommands(MM::PropertyBase* pPropt, MM::ActionType eAct) +{ + if (eAct == MM::BeforeGet) + { + pPropt->Set(g_Keyword_None); + } + else if (eAct == MM::AfterSet) + { + std::string cmd; + pPropt->Get(cmd); + this->SendCommand(cmd, _serial_answer); + + // Remember the response + std::string ans = _serial_answer; + + // Sync the stages, because the extra commands mostly change the state of the stage + this->SyncPeripherals(); + + // Display the response to the command + SetProperty(g_Keyword_Response, ans.c_str()); + } + + return DEVICE_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +// SangaboardHub Helper Functions +/////////////////////////////////////////////////////////////////////////////// +/** +* Return port name +*/ +void SangaBoardHub::GetPort(std::string& port) +{ + port = this->port_; +} + +/** +* Manages serial port calls by peripheral devices +* Locks thread to use serial port so only one serial call is done at a time +*/ +int SangaBoardHub::SendCommand(std::string cmd, std::string& ans) +{ + // Lock the serial port with threadguard + MMThreadGuard g(serial_lock_); + + // Allow previous move to finish before sending new command through serial port + if (Busy() && cmd.find("mr") != -1) { + return DEVICE_STAGE_STILL_MOVING; + } + + // Send command and receive response + int ret = SendSerialCommand(port_.c_str(), cmd.c_str(), "\n"); + + if (ret != DEVICE_OK) { + return ret; + } + + ret = GetSerialAnswer(port_.c_str(), "\r", ans); + + if (ret != DEVICE_OK) { + return ret; + } + + // Reflect the command in the serial response property + ret = SetProperty(g_Keyword_Response, ans.c_str()); + if (DEVICE_OK != ret) return DEVICE_CAN_NOT_SET_PROPERTY; + + return DEVICE_OK; + +} // End of function automatically unlocks the lock + +/** +* Prompt device to validate MM cached values for the hub +*/ +int SangaBoardHub::SyncState() +{ + // Update minimum step delay + std::string cmd = "dt?"; + this->SendCommand(cmd, _serial_answer); // Output is something like "minimum step delay 1000" + + // Get the step delay from response + step_delay_ = ExtractNumber(_serial_answer); + + // Update ramp time + cmd = "ramp_time?"; + this->SendCommand(cmd, _serial_answer); // Output is something like "ramp_time 0†+ + // Get the ramp time from response + ramp_time_ = ExtractNumber(_serial_answer); + + return DEVICE_OK; +} + +/** +* Sync the peripherals if they exist +*/ +int SangaBoardHub::SyncPeripherals() +{ + // Look for peripherals that need syncing + for (unsigned i = 0; i < GetNumberOfDevices(); i++) { + + char deviceName[MM::MaxStrLength]; + bool success = GetDeviceName(i, deviceName, MM::MaxStrLength); + + // Sync xy stage + if (success && (strcmp(g_XYStageDeviceName, deviceName) == 0)) + { + XYStage* xyStage = dynamic_cast(GetDevice(deviceName)); + xyStage->SyncState(); + } + + // Sync z stage + if (success && (strcmp(g_ZStageDeviceName, deviceName) == 0)) + { + ZStage* zStage = dynamic_cast(GetDevice(deviceName)); + zStage->SyncState(); + } + + // Sync illumination + if (success && (strcmp(g_ShutterDeviceName, deviceName) == 0)) + { + LEDIllumination* light = dynamic_cast(GetDevice(deviceName)); + light->SyncState(); + } + } + + return DEVICE_OK; +} + +/** +* Parse a sentence for numbers +*/ +long SangaBoardHub::ExtractNumber(std::string str) { + std::stringstream ss(str); + std::string temp; + long found; + + // Running loop till the end of the stream + while (ss >> temp) { + // Checking if the given word is a long or not + if (std::stringstream(temp) >> found) { + return found; // Return the first long found + } + } + return 0; // Return 0 if no long is found +} + +/////////////////////////////////////////////////////////////////////////////// +// XYStage implementation +/////////////////////////////////////////////////////////////////////////////// + +XYStage::XYStage() : initialized_(false), portAvailable_(false), stepSizeUm_(0.07), stepsX_(0), stepsY_(0), pHub(NULL) +{ + // Parent ID display + //CreateHubIDProperty(); + + // Initialize default error messages + InitializeDefaultErrorMessages(); +} + +XYStage::~XYStage() +{ + Shutdown(); +} + +int XYStage::Initialize() +{ + if (initialized_) + return DEVICE_OK; + + // Get hub instance + pHub = static_cast(GetParentHub()); + if (pHub) + { + char hubLabel[MM::MaxStrLength]; + pHub->GetLabel(hubLabel); + SetParentID(hubLabel); // for backward comp. + } + else + LogMessage(NoHubError); + + // Check status to see if device is ready to start + int status = UpdateStatus(); + if (status != DEVICE_OK) { + return status; + } + + // Use non-blocking moves + std::string cmd = "blocking_moves false"; + pHub->SendCommand(cmd, _serial_answer); + if (_serial_answer.find("done") == -1) { + return DEVICE_SERIAL_INVALID_RESPONSE; + } + + // Set the current stage position + SyncState(); + + // Device is now initialized + initialized_ = true; + + return DEVICE_OK; +} + +int XYStage::Shutdown() +{ + //De-energize the motors + std::string cmd = "release"; + pHub->SendCommand(cmd, _serial_answer); + initialized_ = false; + return DEVICE_OK; +} + +int XYStage::SetPositionSteps(long x, long y) +{ + // Probably best to call SetRelativePosition() to complete this function + return DEVICE_OK; +} + +int XYStage::SetPositionUm(double posX, double posY) +{ + // Manual changing position + return DEVICE_OK; +} + +int XYStage::GetPositionUm(double& posX, double& posY) +{ + posX = stepsX_ * stepSizeUm_; + posY = stepsY_ * stepSizeUm_; + + return DEVICE_OK; +} + +/** +* Should be called by GetPositionUm(), but probably redundant, because I'm keeping stepsX_ and stepsY_ global variables +*/ +int XYStage::GetPositionSteps(long& x, long& y) +{ + x = stepsX_; + y = stepsY_; + + return DEVICE_OK; +} + +int XYStage::SetRelativePositionUm(double dx, double dy) +{ + long dxSteps = nint(dx / stepSizeUm_); + long dySteps = nint(dy / stepSizeUm_); + int ret = SetRelativePositionSteps(dxSteps, dySteps); // Stage starts moving after this step + + if (ret == DEVICE_OK) { + stepsX_ += dxSteps; + stepsY_ += dySteps; + this->OnXYStagePositionChanged(stepsX_ * stepSizeUm_, stepsY_ * stepSizeUm_); + } + + return DEVICE_OK; +} + +int XYStage::SetRelativePositionSteps(long x, long y) +{ + + // Sending two commands sequentially + std::ostringstream cmd; + cmd << "mrx " << x << "\nmry " << y; // move in x first then y (arbitrary choice) + + int ret = pHub->SendCommand(cmd.str(), _serial_answer); + + return ret; +} + +int XYStage::SetOrigin() +{ + + // Set current position as origin (all motor positions set to 0) + std::string cmd = "zero"; + int ret = pHub->SendCommand(cmd, _serial_answer); + + return ret; +} + +int XYStage::SetAdapterOrigin() +{ + //Could be the function to sync adapter to the stage's actual positions... + return DEVICE_OK; +} + +/** +* Return the device to the origin +*/ +int XYStage::Home() +{ + //TODO: Query the position steps and set the number of steps to opposite that + return DEVICE_OK; +} + +int XYStage::Stop() +{ + // send the stop command to the stage + std::string cmd = "stop"; + pHub->SendCommand(cmd, _serial_answer); + + // Make sure current position is synched + SyncState(); + + return DEVICE_OK; + +} + +int XYStage::GetStepLimits(long& xMin, long& xMax, long& yMin, long& yMax) +{ + return DEVICE_OK; +} + +int XYStage::GetLimitsUm(double& xMin, double& xMax, double& yMin, double& yMax) +{ + return DEVICE_OK; +} + +void XYStage::GetName(char* name) const +{ + CDeviceUtils::CopyLimitedString(name, g_XYStageDeviceName); +} + +/////////////////////////////////////////////////////////////////////////////// +// XYStage Helper Functions +/////////////////////////////////////////////////////////////////////////////// + +/** +* Sync the starting position of the stage to the cached values in the adapter +*/ +int XYStage::SyncState() +{ + // Query for the current position [x y z] of the stage + std::string cmd = "p"; + pHub->SendCommand(cmd, _serial_answer); + + // Parse the position of the stage into the x and y componennts + std::istringstream iss(_serial_answer); + + iss >> stepsX_; + + iss >> stepsY_; + + // Reflect the synch-ed state in display + int ret = OnXYStagePositionChanged(stepsX_ * stepSizeUm_, stepsY_ * stepSizeUm_); + + if (ret != DEVICE_OK) { + return ret; + } + + return DEVICE_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +// ZsStage implementation +/////////////////////////////////////////////////////////////////////////////// + +ZStage::ZStage() : initialized_(false), stepSizeUm_(0.05), stepsZ_(0), pHub(NULL) +{ + // Parent ID display + //CreateHubIDProperty(); + + // Initialize default error messages + InitializeDefaultErrorMessages(); +} + +ZStage::~ZStage() +{ + Shutdown(); +} + +int ZStage::Initialize() +{ + if (initialized_) + return DEVICE_OK; + + // Create pointer to parent hub (sangaboard) + pHub = static_cast(GetParentHub()); + if (pHub) + { + char hubLabel[MM::MaxStrLength]; + pHub->GetLabel(hubLabel); + SetParentID(hubLabel); // for backward comp. + } + else + LogMessage(NoHubError); + + // Check status to see if device is ready to start + int status = UpdateStatus(); + if (status != DEVICE_OK) { + return status; + } + + // Use non-blocking moves + std::string cmd = "blocking_moves false"; + pHub->SendCommand(cmd, _serial_answer); + if (_serial_answer.find("done") == -1) { + return DEVICE_SERIAL_INVALID_RESPONSE; + } + + // Set the current stagePosition + SyncState(); + + // Device is now initialized + initialized_ = true; + + return DEVICE_OK; +} + +int ZStage::Shutdown() +{ + // De-energize the motors + std::string cmd = "release"; + pHub->SendCommand(cmd, _serial_answer); + initialized_ = false; + return DEVICE_OK; +} + +int ZStage::GetPositionUm(double& pos) +{ + pos = stepsZ_ * stepSizeUm_; + return DEVICE_OK; +} + +int ZStage::GetPositionSteps(long& steps) +{ + steps = stepsZ_; + return DEVICE_OK; +} + +int ZStage::SetRelativePositionUm(double d) +{ + long dSteps = nint(d / stepSizeUm_); + int ret = SetRelativePositionSteps(dSteps); // Stage starts moving after this step + + if (ret == DEVICE_OK) { + stepsZ_ += dSteps; + this->OnStagePositionChanged(stepsZ_ * stepSizeUm_); + } + + return DEVICE_OK; +} + + +int ZStage::SetRelativePositionSteps(long z) +{ + // Concatenate the command and number of steps + std::ostringstream cmd; + cmd << "mrz " << z; + + // Send command through hub + int ret = pHub->SendCommand(cmd.str(), _serial_answer); + + return ret; +} + + +int ZStage::SetOrigin() +{ + // Set current position as origin (all motor positions set to 0) + std::string cmd = "zero"; + int ret = pHub->SendCommand(cmd, _serial_answer); + + return ret; +} + +void ZStage::GetName(char* name) const +{ + CDeviceUtils::CopyLimitedString(name, g_ZStageDeviceName); +} + +/////////////////////////////////////////////////////////////////////////////// +// ZStage Helper functions +/////////////////////////////////////////////////////////////////////////////// + +/** +* Sync the starting position of the stage to the cached values in the adapter +*/ +int ZStage::SyncState() +{ + // Query for the current position [x y z] of the stage + std::string cmd = "p"; + pHub->SendCommand(cmd, _serial_answer); + + // Parse the position of the stage to just get the z position + std::istringstream iss(_serial_answer); + + iss >> stepsZ_ >> stepsZ_ >> stepsZ_; + + // Reflect the synch-ed state in display + int ret = OnStagePositionChanged(stepsZ_ * stepSizeUm_); + + if (ret != DEVICE_OK) { + return ret; + } + + return DEVICE_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +// LED Illumination implementation +/////////////////////////////////////////////////////////////////////////////// + +LEDIllumination::~LEDIllumination() +{ + Shutdown(); +} + +int LEDIllumination::Initialize() +{ + pHub = static_cast(GetParentHub()); + if (pHub) + { + char hubLabel[MM::MaxStrLength]; + pHub->GetLabel(hubLabel); + SetParentID(hubLabel); // for backward comp. + } + else + LogMessage(NoHubError); + + if (initialized_) + return DEVICE_OK; + + // Sync current device values to cached values + SyncState(); + + // Set property list + // ------------------ + // state + CPropertyAction* pAct = new CPropertyAction(this, &LEDIllumination::OnState); + int ret = CreateIntegerProperty(MM::g_Keyword_State, state_, false, pAct); + if (ret != DEVICE_OK) + return ret; + + AddAllowedValue(MM::g_Keyword_State, "0"); // Closed + AddAllowedValue(MM::g_Keyword_State, "1"); // Open + + // Brightness property is a slider + CPropertyAction* pActbr = new CPropertyAction(this, &LEDIllumination::OnBrightness); + CreateProperty(g_Keyword_Brightness, std::to_string(brightness_).c_str(), MM::Float, false, pActbr); + SetPropertyLimits(g_Keyword_Brightness, 0, 1.0); + + ret = UpdateStatus(); + if (ret != DEVICE_OK) + return ret; + + initialized_ = true; + + return DEVICE_OK; +} + +int LEDIllumination::Shutdown() +{ + // Turn LED off + SetOpen(false); + initialized_ = false; + return DEVICE_OK; +} + +int LEDIllumination::SetOpen(bool open)//bool open = true) +{ + state_ = open; + changedTime_ = GetCurrentMMTime(); + + if (state_ == true) { + SetBrightness(); + } + else { + std::string cmd = "led_cc 0"; + pHub->SendCommand(cmd, _serial_answer); + } + + return DEVICE_OK; +} + +int LEDIllumination::GetOpen(bool& open) +{ + open = state_; + return DEVICE_OK; +} + +void LEDIllumination::GetName(char* name) const +{ + CDeviceUtils::CopyLimitedString(name, g_ShutterDeviceName); +} + +/////////////////////////////////////////////////////////////////////////////// +// LED Illumination Action handlers +/////////////////////////////////////////////////////////////////////////////// + +int LEDIllumination::OnState(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::BeforeGet) + { + if (state_) + pProp->Set(1L); + else + pProp->Set(0L); + } + else if (eAct == MM::AfterSet) + { + // Set timer for the Busy signal + changedTime_ = GetCurrentMMTime(); + + long pos; + pProp->Get(pos); + + // apply the value + state_ = pos == 0 ? false : true; + SetOpen(state_); + } + + return DEVICE_OK; +} + +int LEDIllumination::OnBrightness(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::BeforeGet) + { + pProp->Set(brightness_); // set the property display in mm to the brightness variable stored on cache + } + else if (eAct == MM::AfterSet) + { + // get value from mm property display and store as brightness variable on cache + pProp->Get(brightness_); + + if (state_) { + // actually send command to set brightness of the LedArray + SetBrightness(); + } + } + return DEVICE_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +// LED Illumination Helper Functions +/////////////////////////////////////////////////////////////////////////////// + +/** +* Queries actual hardware for current values to sync the MM cached values +*/ +int LEDIllumination::SyncState() +{ + if (!initialized_) { + return DEVICE_OK; // Keep device at default brightness + } + + // Query for the brightness of the LED + std::string cmd = "led_cc?"; + pHub->SendCommand(cmd, _serial_answer); // Output is something like CC LED:1.00, so i can't use hub's ExtractNumber() + + // Find the position of the numeric value in the response + size_t colonPos = _serial_answer.find(':'); + if (colonPos != std::string::npos) { + std::string valueStr = _serial_answer.substr(colonPos + 1); // Extract the substring after the colon + + double LEDVal = std::stod(valueStr); + + // If only sync if light was manually turned on to a certain brightness + if (LEDVal != 0.0) { + state_ = true; + brightness_ = LEDVal; + } + else { + state_ = false; + // Note: brightness_ is not updated when LEDVal is 0.0, so it retains its last value. + } + } + else { + return DEVICE_ERR; + } + return DEVICE_OK; +} + +/** +* Turn LED on to a certain brightness- +*/ +int LEDIllumination::SetBrightness() +{ + // actually send command to set brightness of the LedArray + std::string cmd = "led_cc " + std::to_string(brightness_); + pHub->SendCommand(cmd, _serial_answer); + + return DEVICE_OK; +} diff --git a/DeviceAdapters/OpenFlexure/OpenFlexure.h b/DeviceAdapters/OpenFlexure/OpenFlexure.h new file mode 100644 index 000000000..c72e8bd81 --- /dev/null +++ b/DeviceAdapters/OpenFlexure/OpenFlexure.h @@ -0,0 +1,218 @@ +/////////////////////////////////////////////////////////////////////////////// +// FILE: OpenFlexure.h +// PROJECT: Micro-Manager +// SUBSYSTEM: DeviceAdapters +//----------------------------------------------------------------------------- +// DESCRIPTION: Adapter for the OpenFlexure Microscope. This adapter is used on the v5 Sangaboard. +// - Tested with Sangaboard Firmware v1.0.1-dev || Sangaboard v0.5.x +// +// AUTHOR: Samdrea Hsu, samdreahsu@gmail.com, 09/23/2024 +// +// COPYRIGHT: Samdrea Hsu +// +// 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 _OpenFlexure_H_ +#define _OpenFlexure_H_ + +#include "MMDevice.h" +#include "DeviceBase.h" +#include +#include + +// Global keywords +const char* g_XYStageDeviceName = "XY Stage"; +const char* g_HubDeviceName = "SangaboardHub"; +const char* g_ZStageDeviceName = "Z Stage"; +const char* g_ShutterDeviceName = "LED illumination"; +const char* g_Keyword_Response = "SerialResponse"; +const char* g_Keyword_Command = "SerialCommand"; +const char* g_Keyword_Brightness = "LED Brightness"; +const char* g_Keyword_StepDelay = "Stage Step Delay (us)"; +const char* g_Keyword_RampTime = "Stage Ramp Time (us)"; +const char* g_Keyword_Extras = "Xtra Stage Commands"; +const char* g_Keyword_None = "None"; +const char* g_ExtraCommand_Stop = "stop"; +const char* g_ExtraCommand_Zero = "zero"; +const char* g_ExtraCommand_Release = "release"; +const char* g_ExtraCommand_Version = "version"; +const char* NoHubError = "Parent Hub not defined."; +const char* const g_Msg_DEVICE_STAGE_STILL_MOVING = "Stage is still moving. Current move aborted."; + +// Custom Error texts +#define DEVICE_STAGE_STILL_MOVING 42 + +class SangaBoardHub : public HubBase +{ +public: + SangaBoardHub(); + ~SangaBoardHub(); + + // MMDevice API + int Initialize(); + int Shutdown(); + void GetName(char* pszName) const; + bool Busy(); + + // Hub API + int DetectInstalledDevices(); + + // Action Handlers + int OnPort(MM::PropertyBase* pPropt, MM::ActionType eAct); + int OnManualCommand(MM::PropertyBase* pPropt, MM::ActionType eAct); + int OnStepDelay(MM::PropertyBase* pPropt, MM::ActionType eAct); + int OnRampTime(MM::PropertyBase* pPropt, MM::ActionType eAct); + int OnExtraCommands(MM::PropertyBase* pPropt, MM::ActionType eAct); + + // Helper Functions + void GetPort(std::string& port); + int SendCommand(std::string cmd, std::string& res); + int SyncState(); + long ExtractNumber(std::string serial_output); + int SyncPeripherals(); + +private: + bool initialized_; + bool busy_; + bool portAvailable_; + long step_delay_; + long ramp_time_; + std::string port_; + std::string _command; + std::string _serial_answer; + MMThreadLock serial_lock_; + + bool IsPortAvailable() { return portAvailable_; } +}; + +class XYStage : public CXYStageBase +{ +public: + XYStage(); + ~XYStage(); + + // MMDevice API + int Initialize(); + int Shutdown(); + + // XYStage API + int SetPositionSteps(long x, long y); + int GetPositionSteps(long& x, long& y); + int SetRelativePositionSteps(long x, long y); + int GetPositionUm(double& posX, double& posY); + int SetPositionUm(double posX, double posY); + int SetRelativePositionUm(double posX, double posY); + int SetOrigin(); + int SetAdapterOrigin(); + int Home(); + int Stop(); + double GetStepSizeXUm() { return stepSizeUm_; } + double GetStepSizeYUm() { return stepSizeUm_; } + int GetStepLimits(long& xMin, long& xMax, long& yMin, long& yMax); + int GetLimitsUm(double& xMin, double& xMax, double& yMin, double& yMax); + int IsXYStageSequenceable(bool& isSequenceable) const { isSequenceable = false; return DEVICE_OK; } + + bool Busy() { return false; } + void GetName(char*) const; + + // Helper functions + int SyncState(); + +private: + long stepsX_; + long stepsY_; + bool initialized_; + bool portAvailable_; + double stepSizeUm_; + std::string port_; + std::string _serial_answer; + SangaBoardHub* pHub; + + bool IsPortAvailable() { return portAvailable_; } +}; + +class ZStage : public CStageBase +{ +public: + ZStage(); + ~ZStage(); + + // MMDevice API + int Initialize(); + int Shutdown(); + void GetName(char* name) const; + + // ZStage API + int SetPositionUm(double pos) { return DEVICE_UNSUPPORTED_COMMAND;} + int SetPositionSteps(long steps) { return DEVICE_UNSUPPORTED_COMMAND;} + int SetRelativePositionUm(double d); + int SetRelativePositionSteps(long z); + int Stop() {return DEVICE_UNSUPPORTED_COMMAND;} + int GetPositionUm(double& pos); + int GetPositionSteps(long& steps); + int SetOrigin(); + int GetLimits(double& lower, double& upper) { return DEVICE_UNSUPPORTED_COMMAND;} // nah + int IsStageSequenceable(bool& isSequenceable) const { isSequenceable = false; return DEVICE_OK;} + bool IsContinuousFocusDrive() const { return false; } + bool Busy() { return false; } + + // Helper functions + int SyncState(); + +private: + long stepsZ_; + bool initialized_; + double stepSizeUm_; + std::string _serial_answer; + SangaBoardHub* pHub; +}; + +class LEDIllumination : public CShutterBase +{ +public: + LEDIllumination() : state_(false), initialized_(false), changedTime_(0.0), brightness_(1.0), pHub(NULL){} + ~LEDIllumination(); + + // MMDevice API + int Initialize(); + int Shutdown(); + void GetName(char* name) const; + + + // Shutter API + int SetOpen(bool open); + int GetOpen(bool& open); + int Fire(double deltaT) { return DEVICE_UNSUPPORTED_COMMAND;} + bool Busy() { return false; } + + // Action Handlers + int OnState(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnBrightness(MM::PropertyBase* pPropt, MM::ActionType eAct); + + // Helper functions + int SyncState(); + int SetBrightness(); + +private: + bool state_; + bool initialized_; + double brightness_; + MM::MMTime changedTime_; + std::string _serial_answer; + SangaBoardHub* pHub; +}; + +#endif + + diff --git a/DeviceAdapters/OpenFlexure/OpenFlexure.vcxproj b/DeviceAdapters/OpenFlexure/OpenFlexure.vcxproj new file mode 100644 index 000000000..1ebc89733 --- /dev/null +++ b/DeviceAdapters/OpenFlexure/OpenFlexure.vcxproj @@ -0,0 +1,169 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + + + + + + + {b8c95f39-54bf-40a9-807b-598df2821d55} + + + + 16.0 + Win32Proj + {149b6a9d-40fb-4fdf-bc0c-6b68e3bc4938} + OpenFlexure + 10.0 + + + + DynamicLibrary + true + v142 + Unicode + + + DynamicLibrary + false + v142 + true + Unicode + + + DynamicLibrary + true + v142 + Unicode + + + DynamicLibrary + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + false + + + true + + + false + + + + Level3 + true + WIN32;_DEBUG;OPENFLEXURE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + + + Windows + true + false + + + + + Level3 + true + true + true + WIN32;NDEBUG;OPENFLEXURE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + + + Windows + true + true + true + false + + + + + true + _DEBUG;OPENFLEXURE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + + + Windows + true + false + + + + + true + true + true + NDEBUG;OPENFLEXURE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + + + Windows + true + true + true + false + + + + + + diff --git a/DeviceAdapters/OpenFlexure/OpenFlexure.vcxproj.filters b/DeviceAdapters/OpenFlexure/OpenFlexure.vcxproj.filters new file mode 100644 index 000000000..f64bb9f77 --- /dev/null +++ b/DeviceAdapters/OpenFlexure/OpenFlexure.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/OxxiusCombiner/CoherentObis.cpp b/DeviceAdapters/OxxiusCombiner/CoherentObis.cpp index c17e358fb..464f01ddf 100644 --- a/DeviceAdapters/OxxiusCombiner/CoherentObis.cpp +++ b/DeviceAdapters/OxxiusCombiner/CoherentObis.cpp @@ -324,7 +324,7 @@ int CoherentObis::OnOperatingMode(MM::PropertyBase* pProp, MM::ActionType eAct) std::ostringstream Info; /////DEBUG - Info << "DEBUG OPERATING MODE: " << rep << "avec la commande encoyée suivante: " << command; + Info << "DEBUG OPERATING MODE: " << rep << "avec la commande encoyée suivante: " << command; GetCoreCallback()->LogMessage(this, Info.str().c_str(), false); } diff --git a/DeviceAdapters/PCO_Generic/MicroManager.cpp b/DeviceAdapters/PCO_Generic/MicroManager.cpp index 38eb58c65..57ca48314 100644 --- a/DeviceAdapters/PCO_Generic/MicroManager.cpp +++ b/DeviceAdapters/PCO_Generic/MicroManager.cpp @@ -45,6 +45,7 @@ const char* g_CameraDeviceName = "pco_camera"; const char* g_PixelType_8bit = "8bit"; const char* g_PixelType_16bit = "16bit"; const char* g_PixelType_RGB32bit = "RGB 32bit"; +const char* g_PixelType_RGB48bit = "RGB 48bit"; const char* g_TimeStamp_No = "No stamp"; const char* g_TimeStamp_B = "Binary"; @@ -1008,8 +1009,10 @@ int CPCOCam::OnPixelType(MM::PropertyBase* pProp, MM::ActionType eAct) pixelDepth_ = 2; else if(pixType.compare(g_PixelType_8bit) == 0) pixelDepth_ = 1; - else if(pixType.compare(g_PixelType_RGB32bit) == 0) + else if (pixType.compare(g_PixelType_RGB32bit) == 0) pixelDepth_ = 4; + else if (pixType.compare(g_PixelType_RGB48bit) == 0) + pixelDepth_ = 6; else { return DEVICE_INTERNAL_INCONSISTENCY; @@ -1022,8 +1025,10 @@ int CPCOCam::OnPixelType(MM::PropertyBase* pProp, MM::ActionType eAct) pProp->Set(g_PixelType_8bit); else if(pixelDepth_ == 2) pProp->Set(g_PixelType_16bit); - else if(pixelDepth_ == 4) + else if (pixelDepth_ == 4) pProp->Set(g_PixelType_RGB32bit); + else if (pixelDepth_ == 6) + pProp->Set(g_PixelType_RGB48bit); else { return DEVICE_INTERNAL_INCONSISTENCY; @@ -1380,8 +1385,12 @@ int CPCOCam::Initialize() pixTypes.push_back(g_PixelType_16bit); pixTypes.push_back(g_PixelType_8bit); - if(m_pCamera->GetCCDCol(0)) + if (m_pCamera->GetCCDCol(0)) + { pixTypes.push_back(g_PixelType_RGB32bit); + //pixTypes.push_back(g_PixelType_RGB48bit); // Not possible up to now 07/22 + // Also GetImage does not convert to 48bit rgb, just 32bit rgb + } nRet = SetAllowedValues(MM::g_Keyword_PixelType, pixTypes); if(nRet != DEVICE_OK) @@ -1663,10 +1672,14 @@ unsigned CPCOCam::GetBitDepth() const { return m_pCamera->GetBitsPerPixel(); } - else if(img_.Depth() == 4) + else if (img_.Depth() == 4) { return 8; } + else if (img_.Depth() == 6) + { + return 16; + } else { return 0; // should not happen @@ -1746,9 +1759,9 @@ const unsigned char* CPCOCam::GetBuffer(int ibufnum) ppic8 += iadd; } } - else if(img_.Depth() == 4) + else if (img_.Depth() == 4) { - if(m_bDoAutoBalance) + if (m_bDoAutoBalance) { m_pCamera->SetLutMinMax(TRUE, TRUE); m_pCamera->AutoBalance(0, 0, 0, 0, 0); @@ -1757,28 +1770,60 @@ const unsigned char* CPCOCam::GetBuffer(int ibufnum) m_pCamera->Convert(ibufnum); iw = img_.Width(); ih = img_.Height(); - unsigned char *pchar; - unsigned char *ppic8, *ph; + unsigned char* pchar; + unsigned char* ppic8, * ph; int iadd; ppic8 = m_pCamera->GetPic8c(); iadd = (iw * 3) % 4; - if(iadd != 0) + if (iadd != 0) iadd = 4 - iadd; pchar = const_cast(img_.GetPixelsRW()); - for(int y = 0; y < ih; y++) + for (int y = 0; y < ih; y++) { - ph = &ppic8[y*(iw * 3 + iadd)]; - for(int x = 0; x < iw; x++) + ph = &ppic8[y * (iw * 3 + iadd)]; + for (int x = 0; x < iw; x++) { - *pchar++ = (unsigned char) *ph++; - *pchar++ = (unsigned char) *ph++; - *pchar++ = (unsigned char) *ph++; + *pchar++ = (unsigned char)*ph++; + *pchar++ = (unsigned char)*ph++; + *pchar++ = (unsigned char)*ph++; *pchar++ = 0; } } } + else if (img_.Depth() == 6) + { + if (m_bDoAutoBalance) + { + m_pCamera->SetLutMinMax(TRUE, TRUE); + m_pCamera->AutoBalance(0, 0, 0, 0, 0); + m_bDoAutoBalance = FALSE; + } + m_pCamera->Convert(ibufnum); + iw = img_.Width(); + ih = img_.Height(); + unsigned short* pchar; + unsigned char* ppic8, * ph; + int iadd; + + ppic8 = m_pCamera->GetPic8c(); + + iadd = (iw * 3) % 4; + if (iadd != 0) + iadd = 4 - iadd; + pchar = (unsigned short*)(img_.GetPixelsRW()); + for (int y = 0; y < ih; y++) + { + ph = &ppic8[y * (iw * 3 + iadd)]; + for (int x = 0; x < iw; x++) + { + *pchar++ = (unsigned char)*ph++; + *pchar++ = (unsigned char)*ph++; + *pchar++ = (unsigned char)*ph++; + } + } + } if(nErr != 0) return 0; @@ -2165,7 +2210,7 @@ int CPCOCam::ResizeImageBuffer() m_iHeight = nHeight; } - if(!(pixelDepth_ == 1 || pixelDepth_ == 2 || pixelDepth_ == 4)) + if(!(pixelDepth_ == 1 || pixelDepth_ == 2 || pixelDepth_ == 4 || pixelDepth_ == 6)) return -1; img_.Resize(nWidth, nHeight, pixelDepth_); SetSizes(nWidth, nHeight, pixelDepth_); diff --git a/DeviceAdapters/PCO_Generic/lib/pco_generic/sc2_SDKStructures.h b/DeviceAdapters/PCO_Generic/lib/pco_generic/sc2_SDKStructures.h index 8e7c4aa5d..086d4c5ee 100644 --- a/DeviceAdapters/PCO_Generic/lib/pco_generic/sc2_SDKStructures.h +++ b/DeviceAdapters/PCO_Generic/lib/pco_generic/sc2_SDKStructures.h @@ -411,7 +411,7 @@ typedef struct #define NUM_SIGNAL_NAMES 4 typedef struct { - WORD wSize; // Sizeof ‘this’ (for future enhancements) + WORD wSize; // Sizeof ‘this’ (for future enhancements) WORD ZZwAlignDummy1; char strSignalName[NUM_SIGNAL_NAMES][25];// Name of signal 104 // Specifies NUM_SIGNAL_NAMES functionalities (1-4) @@ -446,7 +446,7 @@ typedef struct typedef struct { - WORD wSize; // Sizeof ‘this’ (for future enhancements) + WORD wSize; // Sizeof ‘this’ (for future enhancements) WORD wNumOfSignals; // Parameter to fetch the num. of descr. from the camera PCO_Single_Signal_Desc strSingeSignalDesc[NUM_MAX_SIGNALS];// Array of singel signal descriptors // 4004 DWORD dwDummy[524]; // reserved for future use. // 6100 @@ -522,8 +522,8 @@ typedef struct typedef struct { WORD wSize; // Sizeof this struct - WORD wTimeBaseDelay; // Timebase delay 0:ns, 1:µs, 2:ms - WORD wTimeBaseExposure; // Timebase expos 0:ns, 1:µs, 2:ms + WORD wTimeBaseDelay; // Timebase delay 0:ns, 1:µs, 2:ms + WORD wTimeBaseExposure; // Timebase expos 0:ns, 1:µs, 2:ms WORD wCMOSParameter; // Line Time mode: 0: off 1: on // 8 DWORD dwCMOSDelayLines; // See next line DWORD dwCMOSExposureLines; // Delay and Exposure lines for lightsheet // 16 @@ -551,7 +551,7 @@ typedef struct WORD wModulationMode; // Mode for modulation (0 = modulation off, 1 = modulation on) // 1070 WORD wCameraSynchMode; // Camera synchronization mode (0 = off, 1 = master, 2 = slave) DWORD dwPeriodicalTime; // Periodical time (unit depending on timebase) for modulation // 1076 - WORD wTimeBasePeriodical; // timebase for periodical time for modulation 0 -> ns, 1 -> µs, 2 -> ms + WORD wTimeBasePeriodical; // timebase for periodical time for modulation 0 -> ns, 1 -> µs, 2 -> ms WORD ZZwDummy3; DWORD dwNumberOfExposures; // Number of exposures during modulation // 1084 LONG lMonitorOffset; // Monitor offset value in ns // 1088 diff --git a/DeviceAdapters/PICAM/PICAMUniversal.cpp b/DeviceAdapters/PICAM/PICAMUniversal.cpp index eacade6f8..f2d0489d1 100644 --- a/DeviceAdapters/PICAM/PICAMUniversal.cpp +++ b/DeviceAdapters/PICAM/PICAMUniversal.cpp @@ -2525,7 +2525,7 @@ int Universal::BuildMetadata( Metadata& md ) MM::MMTime timestamp = GetCurrentMMTime(); md.Clear(); - md.put("Camera", label); + md.put(MM::g_Keyword_Metadata_CameraLabel, label); #ifdef PICAM_FRAME_INFO_SUPPORTED md.PutImageTag("PICAM-FrameNr", pFrameInfo_->FrameNr); diff --git a/DeviceAdapters/PVCAM/PVCAMUniversal.cpp b/DeviceAdapters/PVCAM/PVCAMUniversal.cpp index 87a2ced4e..a87a7a9b3 100644 --- a/DeviceAdapters/PVCAM/PVCAMUniversal.cpp +++ b/DeviceAdapters/PVCAM/PVCAMUniversal.cpp @@ -4205,7 +4205,7 @@ int Universal::ProcessNotification( const NotificationEntry& entry ) { // Build the metadata Metadata md; - md.PutImageTag("Camera", deviceLabel_); + md.PutImageTag(MM::g_Keyword_Metadata_CameraLabel, deviceLabel_); md.PutImageTag("TimeStampMsec", CDeviceUtils::ConvertToString(frameNfo.TimeStampMsec())); #ifdef PVCAM_FRAME_INFO_SUPPORTED diff --git a/DeviceAdapters/PeCon2000/PeCon2000.cpp b/DeviceAdapters/PeCon2000/PeCon2000.cpp index 771b77e23..9d1fd90ec 100644 --- a/DeviceAdapters/PeCon2000/PeCon2000.cpp +++ b/DeviceAdapters/PeCon2000/PeCon2000.cpp @@ -7,7 +7,7 @@ // // AUTHOR: Christian Sachs // -// COPYRIGHT: Forschungszentrum Jülich +// COPYRIGHT: Forschungszentrum Jülich // LICENSE: BSD (2-clause/FreeBSD license) // THANKS: Dr. Oliver Merk (of PeCon GmbH) for Support and Testing @@ -163,7 +163,7 @@ CPeCon2000Device::CPeCon2000Device(string name) : ready(false), name(name), retr ADD_MAP(ChannelStatusMapper, PdlChannelStatusOk, "OK. No channel error"); ADD_MAP(ChannelStatusMapper, PdlChannelStatusNoSensor, "NO SENSOR. No valid sensor signal detected"); - ADD_MAP(ChannelStatusMapper, PdlChannelStatusOvertemp, "OVERTEMP. Actual Temperature > Max Setpoint + 5°C"); + ADD_MAP(ChannelStatusMapper, PdlChannelStatusOvertemp, "OVERTEMP. Actual Temperature > Max Setpoint + 5°C"); ADD_MAP(ChannelStatusMapper, PdlChannelStatusOvercurrent, "OVERCURRENT. Actual Current > 4A"); ADD_MAP(ChannelStatusMapper, PdlChannelStatusSumOvercurrent, "SUM OVERCURRENT. Current CH1 + CH2 > 4.2A"); ADD_MAP(ChannelStatusMapper, PdlChannelStatusIdDetectError, "ID DETECT ERROR. ID number mismatch after 5x ID request over serial interface (3 identical ID's could not be found in the received 5 ID's) Auto-Detect will be repeated after a wait time, until 3 identical ID's are received."); @@ -173,15 +173,15 @@ CPeCon2000Device::CPeCon2000Device(string name) : ready(false), name(name), retr ADD_MAP(ChannelStatusMapper, PdlChannelStatusMBTempTooHi, "MB TEMP TOO HI. Mainboard temperature on connected component is too high"); ADD_MAP(ChannelStatusMapper, PdlChannelStatusChangeFilter, "CHANGE FILTER. Filters on/in connected component needs replacement"); - ADD_MAP(ChannelStatusMapper, PdlChannelStatusHeatingSensor, "HEATING SENSOR. CO2: The sensor temperature is not +/- 5°C of the calibration temperature. If 60 min. after device start/reset the sensor status bit is still active, the channel status changes to \"FALSE SENS TEMP\", because the sensor heater normally heats up the sensor within 20-30 min. (CO2-Sensor 2000: Status Bit 6 is set) O2: The sensor heating voltage is slowly increased and has not reached yet final value."); + ADD_MAP(ChannelStatusMapper, PdlChannelStatusHeatingSensor, "HEATING SENSOR. CO2: The sensor temperature is not +/- 5°C of the calibration temperature. If 60 min. after device start/reset the sensor status bit is still active, the channel status changes to \"FALSE SENS TEMP\", because the sensor heater normally heats up the sensor within 20-30 min. (CO2-Sensor 2000: Status Bit 6 is set) O2: The sensor heating voltage is slowly increased and has not reached yet final value."); ADD_MAP(ChannelStatusMapper, PdlChannelStatusWaitForSensor, "WAIT FOR SENSOR. CO2: The sensor is not ready yet to give a value (CO2-Sensor 2000: Status Bit 0 is set)"); ADD_MAP(ChannelStatusMapper, PdlChannelStatusAutoCompensate, "AUTO COMPENSATE. CO2: The lamp intensity of the CO2-Sensor 2000 is automatically adjusted to give a specified CO2-concentration (e.g. 100,00% with pure CO2) (CO2-Sensor 2000: Status Bit 1 is set)"); - ADD_MAP(ChannelStatusMapper, PdlChannelStatusFalseSensTemp, "FALSE SENS TEMP. CO2: The sensor temperature is not +/- 6°C of the calibration temperature after it was once inside the +/- 5°C tolerance band. Or the sensor status bit was still set 60min. after device start/reset. (CO2-Sensor 2000: Status Bit 6 is set)"); + ADD_MAP(ChannelStatusMapper, PdlChannelStatusFalseSensTemp, "FALSE SENS TEMP. CO2: The sensor temperature is not +/- 6°C of the calibration temperature after it was once inside the +/- 5°C tolerance band. Or the sensor status bit was still set 60min. after device start/reset. (CO2-Sensor 2000: Status Bit 6 is set)"); ADD_MAP(ChannelStatusMapper, PdlChannelStatusLampOverLimit, "LAMP OVER LIMIT. CO2: The lamp PWM limit of the connected sensor has been exceeded (<=450 / >=950). (CO2-Sensor 2000: Status Bit 2 is set)"); ADD_MAP(ChannelStatusMapper, PdlChannelStatusNearADLimit, "NEAR AD LIMIT. CO2: One of the internal data points for CO2 measurement is near (or above) the limit of the AD converter. CO2-concentratrion of this time cycle is therefore invalid. (CO2-Sensor 2000: Status Bit 3 is set)"); ADD_MAP(ChannelStatusMapper, PdlChannelStatusFalseCalibVal, "FALSE CALIB VAL. CO2: The calibration values of the connected CO2-sensor 2000 head are invalid (10%/100% value=0) -> Factory recalibration with 0/10/100% (CO2-Sensor 2000: Status Bit 5 is set)"); ADD_MAP(ChannelStatusMapper, PdlChannelStatusNegativeValue, "NEGATIVE VALUE. CO2: The internal raw value is negative because of inverted detector signal. (CO2-Sensor 2000: Status Bit 4 is set)"); - ADD_MAP(ChannelStatusMapper, PdlChannelStatusHeatingVErr, "HEATING V ERR. O2: The heating voltage feedback control algorithm has reached the upper/lower DA voltage output limit, while not have reached the set heating voltage read back by AD-measure­ment."); + ADD_MAP(ChannelStatusMapper, PdlChannelStatusHeatingVErr, "HEATING V ERR. O2: The heating voltage feedback control algorithm has reached the upper/lower DA voltage output limit, while not have reached the set heating voltage read back by AD-measure­ment."); ADD_MAP(ChannelStatusMapper, PdlChannelStatusReferenceGas, "REFERENCE GAS. CO2: The sensor chamber is flooded with CO2 reference gas. O2: The sensor chamber is flooded with N2 reference gas."); ADD_MAP(ChannelStatusMapper, PdlChannelStatusAmbientAir, "AMBIENT AIR. CO2: The sensor chamber is flooded with ambient air. O2: The sensor chamber is flooded with ambient air."); ADD_MAP(ChannelStatusMapper, PdlChannelStatusAdjOtherSens, "ADJ OTHER SENS. CO2: The O2 sensor is automatically adjusted. O2: The CO2 sensor is automatically aging compensated (Auto Compensate). This error is set to each sensor, that is within the same volume as the other sensor, whose value is currently corrected/calibrated."); diff --git a/DeviceAdapters/Piezosystem_dDrive/devicelist.cpp b/DeviceAdapters/Piezosystem_dDrive/devicelist.cpp old mode 100755 new mode 100644 index 7ea1aa65f..980fe9a75 --- a/DeviceAdapters/Piezosystem_dDrive/devicelist.cpp +++ b/DeviceAdapters/Piezosystem_dDrive/devicelist.cpp @@ -20,7 +20,7 @@ bool devicelist::isStage(std::string name){ name == "NANOX400" || name == "NANOX400SG" || name == "NANOX400CAP" || name == "NANOSX400" || name == "NANOSX400CAP" || name == "NANOSX400CAPDIG" || name == "NANOSX800" || name == "NANOSX800CAP" || name == "NANOSX800CAPDIG" || - name == "NANOX240SG45" || name == "NANOX240SG45°"|| + name == "NANOX240SG45" || name == "NANOX240SG45°"|| */ //X positionierer diff --git a/DeviceAdapters/PlayerOne/POACamera.cpp b/DeviceAdapters/PlayerOne/POACamera.cpp new file mode 100644 index 000000000..91894d391 --- /dev/null +++ b/DeviceAdapters/PlayerOne/POACamera.cpp @@ -0,0 +1,3356 @@ +/////////////////////////////////////////////////////////////////////////////// +// FILE: POACamera.cpp +// PROJECT: Micro-Manager +// SUBSYSTEM: DeviceAdapters +//----------------------------------------------------------------------------- +// DESCRIPTION: This is a device adapter of Micro-Manager for Player One cameras. +// This is modified based on DemoCamera project. +// +// AUTHOR: Lei Zhang, lei.zhang@player-one-astronomy.com, Feb 2024 +// +// COPYRIGHT: Player One Astronomy, SUZHOU, 2024 +// +// 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 "POACamera.h" +#include "ConvFuncs.h" + + +#include +#include +#include +#include "ModuleInterface.h" +#include +#include +//#include "WriteCompactTiffRGB.h" +#include +#include + +double g_IntensityFactor_ = 1.0; + +// External names used used by the rest of the system +// to load particular device from the "DemoCamera.dll" library +const char* g_CameraDeviceName = "POA_Camera"; +const char* g_WheelDeviceName = "POA_FilterWheel"; +//const char* g_AutoFocusDeviceName = "DAutoFocus"; // possible use in future + +// constants for naming pixel types (allowed values of the "PixelType" property) +const char* g_PixelType_8bit = "RAW8"; +const char* g_PixelType_16bit = "RAW16"; +const char* g_PixelType_32bitRGB = "RGB32"; +const char* g_PixelType_MONO8bit = "MONO8"; +//const char* g_PixelType_64bitRGB = "64bitRGB"; +//const char* g_PixelType_32bit = "32bit"; // floating point greyscale + +const char* g_HubDeviceName = "DHub"; +const char* g_SelectCamera = "Select Camera"; +const char* g_SelectFilterWheel = "Select Filter Wheel"; + +//camera properties +const char* g_PropName_USBHost = "USB Host"; +const char* g_PropName_USB_BW_Limit = "USB Bandwidth Limit"; +const char* g_PropName_FrameRate_Limit = "Frame Rate Limit"; +const char* g_PropName_Flip = "Image Flip"; +const char* g_PropName_PixelBinSum = "Pixel Bin Sum"; +const char* g_PropName_HardwareBin = "Hardware Bin"; + +const char* g_PropName_GainPreset = "Gain Preset"; +const char* g_PropName_OffsetPreset = "Offset Preset"; + +const char* g_PropName_WB_R = "WB_Red"; +const char* g_PropName_WB_G = "WB_Green"; +const char* g_PropName_WB_B = "WB_Blue"; +const char* g_PropName_AutoWB = "WB Auto"; +const char* g_PropName_MonoBin = "Mono Bin"; + +const char* g_PropName_Gamma = "Gamma"; +const char* g_PropName_AutoExp = "Exp Auto"; +const char* g_PropName_AutoGain = "Gain Auto"; +const char* g_PropName_AutoExpBrightness = "Auto Exp Brightness"; + + +const char* g_PropName_TargetTemp = "Target Temperature"; +const char* g_PropName_IsCoolerOn = "Cooler On"; +const char* g_PropName_CoolPower = "Cooler Power(%)"; +const char* g_PropName_FanPower = "Fan Power(%)"; +const char* g_PropName_IsHeaterOn = "Anti-dew(Heater) On"; +const char* g_PropName_HeaterPower = "Anti-dew(Heater) Power(%)"; + +const char* g_PropName_ON = "ON"; +const char* g_PropName_OFF = "OFF"; + +// flip values +const std::string g_FlipHori("Hori"); +const std::string g_FlipVert("Vert"); +const std::string g_FlipBoth("Both"); +const std::string g_FlipNone("None"); + +//gain preset +const std::string g_gainHighestDR("HighestDR Gain"); +const std::string g_HCGain("HCGain"); +const std::string g_unityGain("Unity Gain"); +const std::string g_gainLowestRN("LowestRN Gain"); + +//offset preset +const std::string g_offsetHighestDR("HighestDR Offset"); +const std::string g_offsetHCGain("HCGain Offset"); +const std::string g_offsetUnityGain("UnityGain Offset"); +const std::string g_offsetLowestRN("LowestRN Offset"); + +//gamma values +const double g_gamma_min = 0.01; +const double g_gamma_max = 7.99; +const double g_gamma_def = 1.0; + +enum { MODE_ARTIFICIAL_WAVES, MODE_NOISE, MODE_COLOR_TEST }; + +//#define DEBUG_METHOD_NAMES + +#ifdef DEBUG_METHOD_NAMES +#define LOG(name) this->LogMessage(name); +#define LOG_ONPROPERTY(name,action) this->LogMessage(string(name)+(action==MM::AfterSet?"(AfterSet)":"(BeforeGet)")); +#else +#define LOG(name) +#define LOG_ONPROPERTY(name,action) +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// Exported MMDevice API +/////////////////////////////////////////////////////////////////////////////// + +MODULE_API void InitializeModuleData() +{ + RegisterDevice(g_CameraDeviceName, MM::CameraDevice, "POA camera"); + RegisterDevice(g_WheelDeviceName, MM::StateDevice, "POA filter wheel"); + + /*RegisterDevice("TransposeProcessor", MM::ImageProcessorDevice, "TransposeProcessor"); + RegisterDevice("ImageFlipX", MM::ImageProcessorDevice, "ImageFlipX"); + RegisterDevice("ImageFlipY", MM::ImageProcessorDevice, "ImageFlipY"); + RegisterDevice("MedianFilter", MM::ImageProcessorDevice, "MedianFilter");*/ +} + +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_CameraDeviceName) == 0) + { + // create camera + return new POACamera(); + } + else if (strcmp(deviceName, g_WheelDeviceName) == 0) + { + // create filter wheel + return new POAFilterWheel(); + } + // ...supplied name not recognized + return 0; +} + +MODULE_API void DeleteDevice(MM::Device* pDevice) +{ + delete pDevice; +} + +/////////////////////////////////////////////////////////////////////////////// +// POACamera implementation +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ + +/** +* POACamera 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. +*/ +POACamera::POACamera() : + CCameraBase(), + exposureMaximum_(2000000.0), + initialized_(false), + roiX_(0), + roiY_(0), + cameraCCDXSize_(512), + cameraCCDYSize_(512), + imgFmt_(POA_RAW8), + bitDepth_(8), + binSize_(1), + ccdT_(0.0), + gammaValue_(g_gamma_def), + p8bitGammaTable(nullptr), + p16bitGammaTable(nullptr), + nominalPixelSizeUm_(1.0), + pRGB24(nullptr), + RGB24BufSize_(0), + readoutUs_(0.0), + sequenceStartTime_(0), + isSequenceable_(false), + sequenceMaxLength_(100), + sequenceRunning_(false), + sequenceIndex_(0), + stopOnOverflow_(false), + supportsMultiROI_(false), + nComponents_(1), + gainHighestDR_(0), + HCGain_(0), + unityGain_(0), + gainLowestRN_(0), + offsetHighestDR_(0), + offsetHCGain_(0), + offsetUnityGain_(0), + offsetLowestRN_(0) +{ + + // call the base class method to set-up default error codes/messages + InitializeDefaultErrorMessages(); + + // parent ID display + CreateHubIDProperty(); + + camProp_.cameraID = -1; + //scan all POA cameras + POACameraProperties camProp; + connectCamerasName_.clear(); + int connnectCamNums = POAGetCameraCount(); + for (int i = 0; i < connnectCamNums; ++i) + { + camProp.cameraID = -1; + POAGetCameraProperties(i, &camProp); //get camera properties + if(camProp.cameraID == -1) + { + continue; + } + + connectCamerasName_.push_back(std::string(camProp.cameraModelName)); + } + + CPropertyAction* pAct = new CPropertyAction(this, &POACamera::OnSelectCamIndex); + if (connnectCamNums > 0) + { + selectCamIndex_ = 0; + selectCamName_ = connectCamerasName_.at(selectCamIndex_); //default camera is first one + POAGetCameraProperties(selectCamIndex_, &camProp_); + } + else + { + selectCamIndex_ = -1; + selectCamName_ = "No POA camera found"; + } + + CreateStringProperty(g_SelectCamera, selectCamName_.c_str(), false, pAct, true); + SetAllowedValues(g_SelectCamera, connectCamerasName_); + + CreateFloatProperty("MaximumExposureMs", exposureMaximum_, false, + new CPropertyAction(this, &POACamera::OnMaxExposure), + true); + + readoutStartTime_ = GetCurrentMMTime(); + thd_ = new MySequenceThread(this); +} + +/** +* POACamera 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. +*/ +POACamera::~POACamera() +{ + if (camProp_.cameraID >= 0) + { + POACameraState camState; + POAGetCameraState(camProp_.cameraID, &camState); + + if (camState != STATE_CLOSED) + { + Shutdown(); + } + } + + if (pRGB24) + { + delete[] pRGB24; + pRGB24 = nullptr; + } + + if (p8bitGammaTable) + { + delete[] p8bitGammaTable; + p8bitGammaTable = nullptr; + } + + if (p16bitGammaTable) + { + delete[] p16bitGammaTable; + p16bitGammaTable = nullptr; + } + + delete thd_; +} + +/** +* Obtains device name. +* Required by the MM::Device API. +*/ +void POACamera::GetName(char* name) const +{ + // Return the name used to referr to this device adapte + CDeviceUtils::CopyLimitedString(name, g_CameraDeviceName); +} + +/** +* Intializes the hardware. +* Required by the MM::Device API. +* Typically we access and initialize hardware at this point. +* Device properties are typically created here as well, except +* the ones we need to use for defining initialization parameters. +* Such pre-initialization properties are created in the constructor. +* (This device does not have any pre-initialization properties) +*/ +int POACamera::Initialize() +{ + char cameraIDStr[128]; + + LOG("POACamera::Initialize"); + + if (initialized_) + return DEVICE_OK; + + DemoHub* pHub = static_cast(GetParentHub()); + if (pHub) + { + char hubLabel[MM::MaxStrLength]; + pHub->GetLabel(hubLabel); + SetParentID(hubLabel); // for backward comp. + } + else + LogMessage(NoHubError); + + + // set property list + // ----------------- + std::vector boolStrVec; + boolStrVec.push_back(g_PropName_ON); + boolStrVec.push_back(g_PropName_OFF); + + // Name + int nRet = CreateStringProperty(MM::g_Keyword_Name, g_CameraDeviceName, true); + if (DEVICE_OK != nRet) + return nRet; + + // Description + nRet = CreateStringProperty(MM::g_Keyword_Description, "Player One Astronomy Camera Adapter", true); + if (DEVICE_OK != nRet) + return nRet; + + //operate camera + if (camProp_.cameraID < 0) + { + return DEVICE_NOT_CONNECTED; + } + + if (POAOpenCamera(camProp_.cameraID) != POA_OK) //open camera + { + return DEVICE_NOT_CONNECTED; + } + + if (POAInitCamera(camProp_.cameraID) != POA_OK) // init camera + { + return DEVICE_NOT_CONNECTED; + } + + //init gamma table + p8bitGammaTable = new unsigned char[256]; + p16bitGammaTable = new unsigned short[65536]; + ResetGammaTable(); + + nominalPixelSizeUm_ = camProp_.pixelSize; + + char* pCameraName = camProp_.cameraModelName; + + // CameraName + nRet = CreateStringProperty(MM::g_Keyword_CameraName, pCameraName, true); + assert(nRet == DEVICE_OK); + + // CameraID(SN) + sprintf(cameraIDStr, "Serial number %s", camProp_.SN); + nRet = CreateStringProperty(MM::g_Keyword_CameraID, cameraIDStr, true); + assert(nRet == DEVICE_OK); + + //SDK version + const char *pVer = POAGetSDKVersion(); + nRet = CreateStringProperty("Cam SDK Ver", pVer, true); + assert(nRet == DEVICE_OK); + + //get camera current image parameters + binSize_ = 1; + POAGetImageBin(camProp_.cameraID, &binSize_); + roiX_ = 0, roiY_ = 0; + POAGetImageStartPos(camProp_.cameraID, &roiX_, &roiY_); + cameraCCDXSize_ = camProp_.maxWidth; + cameraCCDYSize_ = camProp_.maxHeight; + POAGetImageSize(camProp_.cameraID, &cameraCCDXSize_, &cameraCCDYSize_); + imgFmt_ = POA_RAW8; + POAGetImageFormat(camProp_.cameraID, &imgFmt_); + + //get gain and offset preset values + POAGetGainsAndOffsets(camProp_.cameraID, &gainHighestDR_, &HCGain_, &unityGain_, &gainLowestRN_, + &offsetHighestDR_, &offsetHCGain_, &offsetUnityGain_, &offsetLowestRN_); + + // get current camera temperature + POABool isAuto; + POAGetConfig(camProp_.cameraID, POA_TEMPERATURE, &ccdT_, &isAuto); + + // Host (PC) USB connenction Type + std::string strUSBType = (camProp_.isUSB3Speed == POA_TRUE) ? "USB3.0" : "USB2.0"; + nRet = CreateStringProperty(g_PropName_USBHost, strUSBType.c_str(), true); + assert(nRet == DEVICE_OK); + + // binning + CPropertyAction* pAct = new CPropertyAction(this, &POACamera::OnBinning); + nRet = CreateIntegerProperty(MM::g_Keyword_Binning, binSize_, false, pAct); + assert(nRet == DEVICE_OK); + + std::vector binningStrVec; + char binStr[2]; + for (int i = 0; i < 8; i++) //int bins[8]; in camera property + { + if (camProp_.bins[i] == 0) + { + break; + } + memset(binStr, 0, 2); + sprintf(binStr, "%d", camProp_.bins[i]); + binningStrVec.push_back(binStr); + } + + nRet = SetAllowedValues(MM::g_Keyword_Binning, binningStrVec); + if (nRet != DEVICE_OK) + return nRet; + + // pixel type + pAct = new CPropertyAction(this, &POACamera::OnPixelType); + nRet = CreateStringProperty(MM::g_Keyword_PixelType, ImgFmtToString(imgFmt_), false, pAct); + assert(nRet == DEVICE_OK); + + std::vector pixelTypeValues; + for (int i = 0; i < 8; i++) //POAImgFormat imgFormats[8]; in camera property + { + if (camProp_.imgFormats[i] == POA_RAW8) + { + pixelTypeValues.push_back(g_PixelType_8bit); + } + else if (camProp_.imgFormats[i] == POA_RAW16) + { + pixelTypeValues.push_back(g_PixelType_16bit); + } + else if (camProp_.imgFormats[i] == POA_RGB24) + { + pixelTypeValues.push_back(g_PixelType_32bitRGB); + } + else if (camProp_.imgFormats[i] == POA_MONO8) + { + pixelTypeValues.push_back(g_PixelType_MONO8bit); + } + else + { + break; + } + } + + nRet = SetAllowedValues(MM::g_Keyword_PixelType, pixelTypeValues); + if (nRet != DEVICE_OK) + return nRet; + + long minVal = 0, maxVal = 0, defVal = 0; + + // exposure + double defExp; + if (GetConfigRange(camProp_.cameraID, POA_EXPOSURE, &maxVal, &minVal, &defVal) != POA_OK) + { + exposureMaximum_ = 2000000.0; + defExp = 10.0; + } + else + { + exposureMaximum_ = maxVal / 1000.0; + defExp = defVal / 1000.0; + } + pAct = new CPropertyAction(this, &POACamera::OnExposure); + nRet = CreateFloatProperty(MM::g_Keyword_Exposure, defExp, false, pAct); + assert(nRet == DEVICE_OK); + SetPropertyLimits(MM::g_Keyword_Exposure, 0.0, exposureMaximum_); + + //Exp auto + pAct = new CPropertyAction(this, &POACamera::OnExpAuto); + nRet = CreateStringProperty(g_PropName_AutoExp, g_PropName_OFF, false, pAct); + assert(nRet == DEVICE_OK); + SetAllowedValues(g_PropName_AutoExp, boolStrVec); + + // camera gain + minVal = 0; + maxVal = 500; + defVal = 0; + GetConfigRange(camProp_.cameraID, POA_GAIN, &maxVal, &minVal, &defVal); + pAct = new CPropertyAction(this, &POACamera::OnGain); + nRet = CreateIntegerProperty(MM::g_Keyword_Gain, defVal, false, pAct); + assert(nRet == DEVICE_OK); + SetPropertyLimits(MM::g_Keyword_Gain, minVal, maxVal); + + //auto gain + pAct = new CPropertyAction(this, &POACamera::OnGainAuto); + nRet = CreateStringProperty(g_PropName_AutoGain, g_PropName_OFF, false, pAct); + assert(nRet == DEVICE_OK); + SetAllowedValues(g_PropName_AutoGain, boolStrVec); + + //gain preset + std::vector gainPresetStrVec; + gainPresetStrVec.push_back(g_gainHighestDR); + gainPresetStrVec.push_back(g_HCGain); + gainPresetStrVec.push_back(g_unityGain); + gainPresetStrVec.push_back(g_gainLowestRN); + //gainPresetStrVec.push_back(g_manually); + pAct = new CPropertyAction(this, &POACamera::OnGainPreset); + nRet = CreateStringProperty(g_PropName_GainPreset, "", false, pAct); + assert(nRet == DEVICE_OK); + SetAllowedValues(g_PropName_GainPreset, gainPresetStrVec); + + //offset preset + std::vector offsetPresetStrVec; + offsetPresetStrVec.push_back(g_offsetHighestDR); + offsetPresetStrVec.push_back(g_offsetHCGain); + offsetPresetStrVec.push_back(g_offsetUnityGain); + offsetPresetStrVec.push_back(g_offsetLowestRN); + //offsetPresetStrVec.push_back(g_manually); + pAct = new CPropertyAction(this, &POACamera::OnOffsetPreset); + nRet = CreateStringProperty(g_PropName_OffsetPreset, "", false, pAct); + assert(nRet == DEVICE_OK); + SetAllowedValues(g_PropName_OffsetPreset, offsetPresetStrVec); + + //auto Exp brightness + minVal = 50; + maxVal = 200; + defVal = 100; + GetConfigRange(camProp_.cameraID, POA_AUTOEXPO_BRIGHTNESS, &maxVal, &minVal, &defVal); + pAct = new CPropertyAction(this, &POACamera::OnAutoExpBrightness); + nRet = CreateIntegerProperty(g_PropName_AutoExpBrightness, defVal, false, pAct); + assert(nRet == DEVICE_OK); + SetPropertyLimits(g_PropName_AutoExpBrightness, minVal, maxVal); + + // camera offset + minVal = 0; + maxVal = 200; + defVal = 0; + GetConfigRange(camProp_.cameraID, POA_OFFSET, &maxVal, &minVal, &defVal); + pAct = new CPropertyAction(this, &POACamera::OnOffset); + nRet = CreateIntegerProperty(MM::g_Keyword_Offset, defVal, false, pAct); + assert(nRet == DEVICE_OK); + SetPropertyLimits(MM::g_Keyword_Offset, minVal, maxVal); + + //gamma + pAct = new CPropertyAction(this, &POACamera::OnGamma); + nRet = CreateFloatProperty(g_PropName_Gamma, g_gamma_def, false, pAct); + assert(nRet == DEVICE_OK); + SetPropertyLimits(g_PropName_Gamma, g_gamma_min, g_gamma_max); + + // camera temperature + pAct = new CPropertyAction(this, &POACamera::OnCCDTemp); + nRet = CreateFloatProperty(MM::g_Keyword_CCDTemperature, 0, true, pAct);//read only + assert(nRet == DEVICE_OK); + SetPropertyLimits(MM::g_Keyword_CCDTemperature, -100, 100); + + //camera USB bandwidth limit + minVal = 35; + maxVal = 100; + defVal = 100; + GetConfigRange(camProp_.cameraID, POA_USB_BANDWIDTH_LIMIT, &maxVal, &minVal, &defVal); + pAct = new CPropertyAction(this, &POACamera::OnUSBBandwidthLimit); + nRet = CreateIntegerProperty(g_PropName_USB_BW_Limit, defVal, false, pAct); + assert(nRet == DEVICE_OK); + SetPropertyLimits(g_PropName_USB_BW_Limit, minVal, maxVal); + + //camera frame rate limit + minVal = 0; + maxVal = 2000; + defVal = 0; + GetConfigRange(camProp_.cameraID, POA_FRAME_LIMIT, &maxVal, &minVal, &defVal); + pAct = new CPropertyAction(this, &POACamera::OnFrameRateLimit); + nRet = CreateIntegerProperty(g_PropName_FrameRate_Limit, defVal, false, pAct); + assert(nRet == DEVICE_OK); + SetPropertyLimits(g_PropName_FrameRate_Limit, minVal, maxVal); + + // camera image flip + std::vector imgFlipStrVec; + imgFlipStrVec.push_back(g_FlipHori); + imgFlipStrVec.push_back(g_FlipVert); + imgFlipStrVec.push_back(g_FlipBoth); + imgFlipStrVec.push_back(g_FlipNone); + pAct = new CPropertyAction(this, &POACamera::OnFlip); + nRet = CreateStringProperty(g_PropName_Flip, g_FlipNone.c_str(), false, pAct); + assert(nRet == DEVICE_OK); + SetAllowedValues(g_PropName_Flip, imgFlipStrVec); + + //camera hardware bin + if (camProp_.isSupportHardBin == POA_TRUE) + { + pAct = new CPropertyAction(this, &POACamera::OnHardBin); + nRet = CreateStringProperty(g_PropName_HardwareBin, g_PropName_OFF, false, pAct); + assert(nRet == DEVICE_OK); + SetAllowedValues(g_PropName_HardwareBin, boolStrVec); + } + + //Pixel Bin Sum + pAct = new CPropertyAction(this, &POACamera::OnPixelBinSum); + nRet = CreateStringProperty(g_PropName_PixelBinSum, g_PropName_OFF, false, pAct); + assert(nRet == DEVICE_OK); + SetAllowedValues(g_PropName_PixelBinSum, boolStrVec); + + //camera white balance + if (camProp_.isColorCamera == POA_TRUE) + { + minVal = -1200; + maxVal = 1200; + defVal = 0; + + //Red + GetConfigRange(camProp_.cameraID, POA_WB_R, &maxVal, &minVal, &defVal); + pAct = new CPropertyAction(this, &POACamera::OnWB_Red); + nRet = CreateIntegerProperty(g_PropName_WB_R, defVal, false, pAct); + assert(nRet == DEVICE_OK); + SetPropertyLimits(g_PropName_WB_R, minVal, maxVal); + + //Green + GetConfigRange(camProp_.cameraID, POA_WB_G, &maxVal, &minVal, &defVal); + pAct = new CPropertyAction(this, &POACamera::OnWB_Green); + nRet = CreateIntegerProperty(g_PropName_WB_G, defVal, false, pAct); + assert(nRet == DEVICE_OK); + SetPropertyLimits(g_PropName_WB_G, minVal, maxVal); + + //Blue + GetConfigRange(camProp_.cameraID, POA_WB_B, &maxVal, &minVal, &defVal); + pAct = new CPropertyAction(this, &POACamera::OnWB_Blue); + nRet = CreateIntegerProperty(g_PropName_WB_B, defVal, false, pAct); + assert(nRet == DEVICE_OK); + SetPropertyLimits(g_PropName_WB_B, minVal, maxVal); + + //Auto + pAct = new CPropertyAction(this, &POACamera::OnAutoWB); + nRet = CreateStringProperty(g_PropName_AutoWB, g_PropName_OFF, false, pAct); + assert(nRet == DEVICE_OK); + SetAllowedValues(g_PropName_AutoWB, boolStrVec); + + // MONO bin + pAct = new CPropertyAction(this, &POACamera::OnMonoBin); + nRet = CreateStringProperty(g_PropName_MonoBin, g_PropName_OFF, false, pAct); + assert(nRet == DEVICE_OK); + SetAllowedValues(g_PropName_MonoBin, boolStrVec); + } + + // cooled camera settings + if (camProp_.isHasCooler == POA_TRUE) + { + //Target Temperature + minVal = -50; + maxVal = 50; + defVal = 0; + GetConfigRange(camProp_.cameraID, POA_TARGET_TEMP, &maxVal, &minVal, &defVal); + pAct = new CPropertyAction(this, &POACamera::OnTargetTEMP); + nRet = CreateIntegerProperty(g_PropName_TargetTemp, defVal, false, pAct); + assert(nRet == DEVICE_OK); + SetPropertyLimits(g_PropName_TargetTemp, minVal, maxVal); + + // Cooler On + pAct = new CPropertyAction(this, &POACamera::OnCoolerOn); + nRet = CreateStringProperty(g_PropName_IsCoolerOn, g_PropName_OFF, false, pAct); + assert(nRet == DEVICE_OK); + SetAllowedValues(g_PropName_IsCoolerOn, boolStrVec); + + //Cooler Power + minVal = 0; + maxVal = 100; + defVal = 0; + GetConfigRange(camProp_.cameraID, POA_COOLER_POWER, &maxVal, &minVal, &defVal); + pAct = new CPropertyAction(this, &POACamera::OnCoolerPower); + nRet = CreateIntegerProperty(g_PropName_CoolPower, defVal, true, pAct); //read only + assert(nRet == DEVICE_OK); + SetPropertyLimits(g_PropName_CoolPower, minVal, maxVal); + + //Fan Power + minVal = 0; + maxVal = 100; + defVal = 70; + GetConfigRange(camProp_.cameraID, POA_FAN_POWER, &maxVal, &minVal, &defVal); + pAct = new CPropertyAction(this, &POACamera::OnFanPower); + nRet = CreateIntegerProperty(g_PropName_FanPower, defVal, false, pAct); + assert(nRet == DEVICE_OK); + SetPropertyLimits(g_PropName_FanPower, minVal, maxVal); + + // Anti-dew(Heater) //deprecated, set Cooler On / Off , then Heater On / Off + /*pAct = new CPropertyAction(this, &POACamera::OnHeaterOn); + nRet = CreateStringProperty(g_PropName_IsHeaterOn, g_PropName_OFF, false, pAct); + assert(nRet == DEVICE_OK); + SetAllowedValues(g_PropName_IsHeaterOn, boolStrVec);*/ + + // Heater Power + minVal = 0; + maxVal = 100; + defVal = 10; + GetConfigRange(camProp_.cameraID, POA_HEATER_POWER, &maxVal, &minVal, &defVal); + pAct = new CPropertyAction(this, &POACamera::OnHeaterPower); + nRet = CreateIntegerProperty(g_PropName_HeaterPower, defVal, false, pAct); + assert(nRet == DEVICE_OK); + SetPropertyLimits(g_PropName_HeaterPower, minVal, maxVal); + } + + // synchronize all properties + // -------------------------- + nRet = UpdateStatus(); + if (nRet != DEVICE_OK) + return nRet; + + + // setup the buffer + // ---------------- + nRet = ResizeImageBuffer(); + if (nRet != DEVICE_OK) + return nRet; + +#ifdef TESTRESOURCELOCKING + TestResourceLocking(true); + LogMessage("TestResourceLocking OK", true); +#endif + + initialized_ = true; + + // initialize image buffer + GenerateEmptyImage(img_); + + LOG("Init POACamera OK!"); + return DEVICE_OK; +} + +/** +* Shuts down (unloads) the device. +* Required by the MM::Device API. +* Ideally this method will completely unload the device and release all resources. +* Shutdown() may be called multiple times in a row. +* After Shutdown() we should be allowed to call Initialize() again to load the device +* without causing problems. +*/ +int POACamera::Shutdown() +{ + LOG("Shut down camera!"); + initialized_ = false; + StopSequenceAcquisition(); + + POACameraState camState = STATE_CLOSED; + POAGetCameraState(camProp_.cameraID, &camState); + if (camState == STATE_EXPOSING) + { + POAStopExposure(camProp_.cameraID); + } + POACloseCamera(camProp_.cameraID); + + if (pRGB24) + { + delete[] pRGB24; + pRGB24 = nullptr; + } + + if (p8bitGammaTable) + { + delete[] p8bitGammaTable; + p8bitGammaTable = nullptr; + } + + if (p16bitGammaTable) + { + delete[] p16bitGammaTable; + p16bitGammaTable = nullptr; + } + + return DEVICE_OK; +} + +/** +* Performs exposure and grabs a single image. +* This function should block during the actual exposure and return immediately afterwards +* (i.e., before readout). This behavior is needed for proper synchronization with the shutter. +* Required by the MM::Camera API. +*/ +int POACamera::SnapImage() +{ + LOG("Snap Image"); + + if (camProp_.cameraID < 0) + return DEVICE_NOT_CONNECTED; + + double exp = GetExposure(); + + POACameraState camState = STATE_CLOSED; + POAGetCameraState(camProp_.cameraID, &camState); + if (camState == STATE_EXPOSING) + { + return DEVICE_OK; + } + + if (POAStartExposure(camProp_.cameraID, POA_TRUE) != POA_OK) + { + return DEVICE_SNAP_IMAGE_FAILED; + } + + m_bIsToStopExposure = false; + + do + { + if (m_bIsToStopExposure) + { + break; + } + + POAGetCameraState(camProp_.cameraID, &camState); + } while (camState == STATE_EXPOSING); + + POABool pIsReady = POA_FALSE; + + POAImageReady(camProp_.cameraID, &pIsReady); + + if (m_bIsToStopExposure) + { + return DEVICE_OK; + } + + if (pIsReady == POA_TRUE) + { + MMThreadGuard g(imgPixelsLock_); + if (img_.Height() == 0 || img_.Width() == 0 || img_.Depth() == 0) + return DEVICE_OUT_OF_MEMORY; + + unsigned char* pImgBuffer = img_.GetPixelsRW(); + long lBufSize = GetImageBufferSize(); + POAErrors error = POAGetImageData(camProp_.cameraID, pImgBuffer, lBufSize, (int)exp+500); + + if (error != POA_OK) + { + return DEVICE_SNAP_IMAGE_FAILED; + } + + bool isEnableGamma = (gammaValue_ != g_gamma_def); + + if (imgFmt_ == POA_RGB24) + { + if (!pRGB24) // if RGB24 buffer is nullptr + { + return DEVICE_SNAP_IMAGE_FAILED; + } + std::memcpy(pRGB24, pImgBuffer, RGB24BufSize_); + BGR888ToRGB32(pRGB24, pImgBuffer, (int) RGB24BufSize_, isEnableGamma); + } + else + { + if (isEnableGamma) + { + unsigned int pixNum = img_.Width() * img_.Height(); + + if (imgFmt_ == POA_RAW16) + { + unsigned short* pU16DataBuffer = reinterpret_cast(pImgBuffer); + for (unsigned int i = 0; i < pixNum; i++) + { + pU16DataBuffer[i] = p16bitGammaTable[ pU16DataBuffer[i] ]; + } + } + else //raw8 and mono8 + { + for (unsigned int i = 0; i < pixNum; i++) + { + pImgBuffer[i] = p8bitGammaTable[pImgBuffer[i]]; + } + } + } + } + } + else + { + return DEVICE_SNAP_IMAGE_FAILED; + } + + return DEVICE_OK; + + //------------------------------------------------------ + //static int callCounter = 0; + //++callCounter; + + //MM::MMTime startTime = GetCurrentMMTime(); + // + //if (sequenceRunning_ && IsCapturing()) + //{ + // exp = GetSequenceExposure(); + //} + + //if (!fastImage_) + //{ + // //GenerateSyntheticImage(img_, exp); + //} + + //MM::MMTime s0(0, 0); + //if (s0 < startTime) + //{ + // while (exp > (GetCurrentMMTime() - startTime).getMsec()) + // { + // CDeviceUtils::SleepMs(1); + // } + //} + //else + //{ + // std::cerr << "You are operating this device adapter without setting the core callback, timing functions aren't yet available" << std::endl; + // // called without the core callback probably in off line test program + // // need way to build the core in the test program + + //} + //readoutStartTime_ = GetCurrentMMTime(); +} + + +/** +* 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* POACamera::GetImageBuffer() +{ + MMThreadGuard g(imgPixelsLock_); + MM::MMTime readoutTime(readoutUs_); + while (readoutTime > (GetCurrentMMTime() - readoutStartTime_)) {} + unsigned char* pB = (unsigned char*)(img_.GetPixels()); + return pB; +} + +/** +* Returns image buffer X-size in pixels. +* Required by the MM::Camera API. +*/ +unsigned POACamera::GetImageWidth() const +{ + return img_.Width(); +} + +/** +* Returns image buffer Y-size in pixels. +* Required by the MM::Camera API. +*/ +unsigned POACamera::GetImageHeight() const +{ + return img_.Height(); +} + +/** +* Returns image buffer pixel depth in bytes. +* Required by the MM::Camera API. +*/ +unsigned POACamera::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 POACamera::GetBitDepth() const +{ + return bitDepth_; +} + +/** +* Returns the size in bytes of the image buffer. +* Required by the MM::Camera API. +*/ +long POACamera::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. +* If multiple ROIs are currently set, then this method clears them in favor of +* the new ROI. +* @param x - top-left corner coordinate +* @param y - top-left corner coordinate +* @param xSize - width +* @param ySize - height +*/ +int POACamera::SetROI(unsigned x, unsigned y, unsigned xSize, unsigned ySize) +{ + if (camProp_.cameraID < 0) + return DEVICE_NOT_CONNECTED; + + if (xSize == 0 && ySize == 0) + { + // effectively clear ROI + ClearROI(); + } + else + { + POABool isFlipHori = POA_FALSE, isFlipVert = POA_FALSE; + POAGetFlip(camProp_.cameraID, &isFlipHori, &isFlipVert); + + int curBinMaxWidth = camProp_.maxWidth / binSize_ / 4 * 4; + int curBinMaxHeight = camProp_.maxHeight / binSize_ / 2 * 2; + + if ((isFlipHori == POA_TRUE) && (isFlipVert == POA_TRUE)) + { + x = curBinMaxWidth - x - xSize; + y = curBinMaxHeight - y - ySize; + } + else if (isFlipHori == POA_TRUE) + { + x = curBinMaxWidth - x - xSize; + } + else if (isFlipVert == POA_TRUE) + { + y = curBinMaxHeight - y - ySize; + } + + POAErrors error = POASetImageSize(camProp_.cameraID, xSize, ySize); + if (error != POA_OK) + { + return DEVICE_ERR; + } + + error = POASetImageStartPos(camProp_.cameraID, x, y); + if (error != POA_OK) + { + return DEVICE_ERR; + } + + RefreshCurrROIParas(); + + // apply ROI + img_.Resize(cameraCCDXSize_, cameraCCDYSize_); + ManageRGB24Memory(); + } + return DEVICE_OK; +} + +/** +* Returns the actual dimensions of the current ROI. +* If multiple ROIs are set, then the returned ROI should encompass all of them. +* Required by the MM::Camera API. +*/ +int POACamera::GetROI(unsigned& x, unsigned& y, unsigned& xSize, unsigned& ySize) +{ + x = roiX_; + y = roiY_; + + xSize = img_.Width(); + ySize = img_.Height(); + + POABool isFlipHori = POA_FALSE, isFlipVert = POA_FALSE; + POAGetFlip(camProp_.cameraID, &isFlipHori, &isFlipVert); + + int curBinMaxWidth = camProp_.maxWidth / binSize_ / 4 * 4; + int curBinMaxHeight = camProp_.maxHeight / binSize_ / 2 * 2; + + if ((isFlipHori == POA_TRUE) && (isFlipVert == POA_TRUE)) + { + x = curBinMaxWidth - roiX_ - xSize; + y = curBinMaxHeight - roiY_ - ySize; + } + else if (isFlipHori == POA_TRUE) + { + x = curBinMaxWidth - roiX_ - xSize; + } + else if (isFlipVert == POA_TRUE) + { + y = curBinMaxHeight - roiY_ - ySize; + } + + return DEVICE_OK; +} + +/** +* Resets the Region of Interest to full frame. +* Required by the MM::Camera API. +*/ +int POACamera::ClearROI() +{ + if (camProp_.cameraID < 0) + return DEVICE_NOT_CONNECTED; + + roiX_ = 0; + roiY_ = 0; + + cameraCCDXSize_ = camProp_.maxWidth / binSize_ / 4 * 4; + cameraCCDYSize_ = camProp_.maxHeight / binSize_ / 2 * 2; + + POASetImageStartPos(camProp_.cameraID, roiX_, roiY_); //move (0, 0) fisrt + + POAErrors error = POASetImageSize(camProp_.cameraID, cameraCCDXSize_, cameraCCDYSize_); + if (error != POA_OK) + { + return DEVICE_ERR; + } + + error = POASetImageStartPos(camProp_.cameraID, roiX_, roiY_); + if (error != POA_OK) + { + return DEVICE_ERR; + } + + ResizeImageBuffer(); + + + return DEVICE_OK; +} + +/** + * Queries if the camera supports multiple simultaneous ROIs. + * Optional method in the MM::Camera API; by default cameras do not support + * multiple ROIs. + */ +bool POACamera::SupportsMultiROI() +{ + return supportsMultiROI_; +} + + +/** +* Returns the current exposure setting in milliseconds. +* Required by the MM::Camera API. +*/ +double POACamera::GetExposure() const +{ + long lExp = 0; + POABool isAuto = POA_FALSE; + POAGetConfig(camProp_.cameraID, POA_EXPOSURE, &lExp, &isAuto); + return lExp / 1000.0; +} + +/** + * Returns the current exposure from a sequence and increases the sequence counter + * Used for exposure sequences + */ +double POACamera::GetSequenceExposure() +{ + if (exposureSequence_.size() == 0) + return this->GetExposure(); + + double exposure = exposureSequence_[sequenceIndex_]; + + sequenceIndex_++; + if (sequenceIndex_ >= exposureSequence_.size()) + sequenceIndex_ = 0; + + return exposure; +} + +/** +* Sets exposure in milliseconds. +* Required by the MM::Camera API. +*/ +void POACamera::SetExposure(double exp) +{ + if (exp < 0.0) + { + exp = 0.0; + } + else if (exp > exposureMaximum_) { + exp = exposureMaximum_; + } + SetProperty(MM::g_Keyword_Exposure, CDeviceUtils::ConvertToString(exp)); + GetCoreCallback()->OnExposureChanged(this, exp); +} + +/** +* Returns the current binning factor. +* Required by the MM::Camera API. +*/ +int POACamera::GetBinning() const +{ + //POAGetImageBin(camProp_.cameraID, &binSize_); + return binSize_; +} + +/** +* Sets binning factor. +* Required by the MM::Camera API. +*/ +int POACamera::SetBinning(int binF) +{ + return SetProperty(MM::g_Keyword_Binning, CDeviceUtils::ConvertToString(binF)); +} + +int POACamera::IsExposureSequenceable(bool& isSequenceable) const +{ + isSequenceable = isSequenceable_; + return DEVICE_OK; +} + +int POACamera::GetExposureSequenceMaxLength(long& nrEvents) const +{ + if (!isSequenceable_) { + return DEVICE_UNSUPPORTED_COMMAND; + } + + nrEvents = sequenceMaxLength_; + return DEVICE_OK; +} + +int POACamera::StartExposureSequence() +{ + if (!isSequenceable_) { + return DEVICE_UNSUPPORTED_COMMAND; + } + + // may need thread lock + sequenceRunning_ = true; + return DEVICE_OK; +} + +int POACamera::StopExposureSequence() +{ + if (!isSequenceable_) { + return DEVICE_UNSUPPORTED_COMMAND; + } + + // may need thread lock + sequenceRunning_ = false; + sequenceIndex_ = 0; + return DEVICE_OK; +} + +/** + * Clears the list of exposures used in sequences + */ +int POACamera::ClearExposureSequence() +{ + if (!isSequenceable_) { + return DEVICE_UNSUPPORTED_COMMAND; + } + + exposureSequence_.clear(); + return DEVICE_OK; +} + +/** + * Adds an exposure to a list of exposures used in sequences + */ +int POACamera::AddToExposureSequence(double exposureTime_ms) +{ + if (!isSequenceable_) { + return DEVICE_UNSUPPORTED_COMMAND; + } + + exposureSequence_.push_back(exposureTime_ms); + return DEVICE_OK; +} + +int POACamera::SendExposureSequence() const { + if (!isSequenceable_) { + return DEVICE_UNSUPPORTED_COMMAND; + } + + return DEVICE_OK; +} + + +int POACamera::PrepareSequenceAcqusition() +{ + LOG("PrepareSequenceAcqusition") + if (IsCapturing()) + return DEVICE_CAMERA_BUSY_ACQUIRING; + + return DEVICE_OK; +} + +/** + * 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 POACamera::StartSequenceAcquisition(double interval) +{ + return StartSequenceAcquisition(LONG_MAX, interval, false); +} + +/** +* Stop and wait for the Sequence thread finished +*/ +int POACamera::StopSequenceAcquisition() +{ + LOG("StopSequenceAcquisition") + + m_bIsToStopExposure = true; + if (!thd_->IsStopped()) + { + thd_->Stop(); + thd_->wait(); + } + + POAStopExposure(camProp_.cameraID); + + 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 POACamera::StartSequenceAcquisition(long numImages, double interval_ms, bool stopOnOverflow) +{ + LOG("StartSequenceAcquisition") + + if (IsCapturing()) + return DEVICE_CAMERA_BUSY_ACQUIRING; + + m_bIsToStopExposure = false; + POAErrors error = POAStartExposure(camProp_.cameraID, POA_FALSE); // video mode + if (error != POA_OK) + return DEVICE_ERR; + + int ret = GetCoreCallback()->PrepareForAcq(this); + if (ret != DEVICE_OK) + return ret; + sequenceStartTime_ = GetCurrentMMTime(); + imageCounter_ = 0; + thd_->Start(numImages, interval_ms); + stopOnOverflow_ = stopOnOverflow; + return DEVICE_OK; +} + + +bool POACamera::IsCapturing() +{ + POACameraState camState = STATE_CLOSED; + POAGetCameraState(camProp_.cameraID, &camState); + + return camState == STATE_EXPOSING; + + //return !thd_->IsStopped(); +} + +/* + * Inserts Image and MetaData into MMCore circular Buffer + */ +int POACamera::InsertImage() +{ + MM::MMTime timeStamp = this->GetCurrentMMTime(); + char label[MM::MaxStrLength]; + this->GetLabel(label); + + // Important: metadata about the image are generated here: + Metadata md; + md.put(MM::g_Keyword_Metadata_CameraLabel, label); + md.put(MM::g_Keyword_Elapsed_Time_ms, CDeviceUtils::ConvertToString((timeStamp - sequenceStartTime_).getMsec())); + md.put(MM::g_Keyword_Metadata_ROI_X, CDeviceUtils::ConvertToString((long)roiX_)); + md.put(MM::g_Keyword_Metadata_ROI_Y, CDeviceUtils::ConvertToString((long)roiY_)); + + imageCounter_++; + + char buf[MM::MaxStrLength]; + GetProperty(MM::g_Keyword_Binning, buf); + md.put(MM::g_Keyword_Binning, buf); + + + MMThreadGuard g(imgPixelsLock_); + + const unsigned char* pI; + pI = GetImageBuffer(); + + unsigned int w = GetImageWidth(); + unsigned int h = GetImageHeight(); + unsigned int b = GetImageBytesPerPixel(); + + int ret = GetCoreCallback()->InsertImage(this, pI, w, h, b, md.Serialize().c_str()); + + if (!stopOnOverflow_ && ret == DEVICE_BUFFER_OVERFLOW) + { + // do not stop on overflow - just reset the buffer + GetCoreCallback()->ClearImageBuffer(this); + // don't process this same image again... + return GetCoreCallback()->InsertImage(this, pI, w, h, b, md.Serialize().c_str(), false); + } + else + return ret; +} + +/* + * Do actual capturing + * Called from inside the thread + */ +int POACamera::RunSequenceOnThread(MM::MMTime /* startTime */) +{ + LOG("RunSequenceOnThread") + + int ret = DEVICE_ERR; + + double exp = GetExposure(); + unsigned char* pImgBuffer = img_.GetPixelsRW(); + long lBufSize = GetImageBufferSize(); + + POABool pIsReady = POA_FALSE; + while (pIsReady == POA_FALSE) + { + if (m_bIsToStopExposure) + break; + + POAImageReady(camProp_.cameraID, &pIsReady); + } + + if (m_bIsToStopExposure) + return ret; + + POAErrors error = POAGetImageData(camProp_.cameraID, pImgBuffer, lBufSize, (int)exp + 500); + if (error == POA_OK) + { + bool isEnableGamma = (gammaValue_ != g_gamma_def); + + if (imgFmt_ == POA_RGB24) + { + if (!pRGB24) // if RGB24 buffer is nullptr + { + return DEVICE_ERR; + } + std::memcpy(pRGB24, pImgBuffer, RGB24BufSize_); + BGR888ToRGB32(pRGB24, pImgBuffer, (int) RGB24BufSize_, isEnableGamma); + } + else + { + if (isEnableGamma) + { + unsigned int pixNum = img_.Width() * img_.Height(); + + if (imgFmt_ == POA_RAW16) + { + unsigned short* pU16DataBuffer = reinterpret_cast(pImgBuffer); + for (unsigned int i = 0; i < pixNum; i++) + { + pU16DataBuffer[i] = p16bitGammaTable[pU16DataBuffer[i]]; + } + } + else //raw8 and mono8 + { + for (unsigned int i = 0; i < pixNum; i++) + { + pImgBuffer[i] = p8bitGammaTable[pImgBuffer[i]]; + } + } + } + } + + ret = InsertImage(); + } + + return ret; +} + +/* + * called from the thread function before exit + */ +void POACamera::OnThreadExiting() throw() +{ + try + { + LogMessage(g_Msg_SEQUENCE_ACQUISITION_THREAD_EXITING); + GetCoreCallback() ? GetCoreCallback()->AcqFinished(this, 0) : DEVICE_OK; + } + catch (...) + { + LogMessage(g_Msg_EXCEPTION_IN_ON_THREAD_EXITING, false); + } +} + +MySequenceThread::MySequenceThread(POACamera* pCam) + :intervalMs_(default_intervalMS) + , numImages_(default_numImages) + , imageCounter_(0) + , stop_(true) + , suspend_(false) + , camera_(pCam) + , startTime_(0) + , actualDuration_(0) + , lastFrameTime_(0) +{}; + +MySequenceThread::~MySequenceThread() {}; + +void MySequenceThread::Stop() { + MMThreadGuard g(this->stopLock_); + stop_ = true; +} + +void MySequenceThread::Start(long numImages, double intervalMs) +{ + MMThreadGuard g1(this->stopLock_); + MMThreadGuard g2(this->suspendLock_); + numImages_ = numImages; + intervalMs_ = intervalMs; + imageCounter_ = 0; + stop_ = false; + suspend_ = false; + activate(); + actualDuration_ = MM::MMTime{}; + startTime_ = camera_->GetCurrentMMTime(); + lastFrameTime_ = MM::MMTime{}; +} + +bool MySequenceThread::IsStopped() { + MMThreadGuard g(this->stopLock_); + return stop_; +} + +void MySequenceThread::Suspend() { + MMThreadGuard g(this->suspendLock_); + suspend_ = true; +} + +bool MySequenceThread::IsSuspended() { + MMThreadGuard g(this->suspendLock_); + return suspend_; +} + +void MySequenceThread::Resume() { + MMThreadGuard g(this->suspendLock_); + suspend_ = false; +} + +int MySequenceThread::svc(void) throw() +{ + int ret = DEVICE_ERR; + try + { + do + { + ret = camera_->RunSequenceOnThread(startTime_); + } while (DEVICE_OK == ret && !IsStopped() && imageCounter_++ < numImages_ - 1); + if (IsStopped()) + camera_->LogMessage("SeqAcquisition interrupted by the user\n"); + } + catch (...) { + camera_->LogMessage(g_Msg_EXCEPTION_IN_THREAD, false); + } + stop_ = true; + actualDuration_ = camera_->GetCurrentMMTime() - startTime_; + camera_->OnThreadExiting(); + return ret; +} + + +/////////////////////////////////////////////////////////////////////////////// +// POACamera Action handlers +/////////////////////////////////////////////////////////////////////////////// + +/** +* Handles "CamIndex" property. +*/ +int POACamera::OnSelectCamIndex(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + std::string str; + if (eAct == MM::AfterSet) + { + pProp->Get(str); + for (int i = 0; i < connectCamerasName_.size(); i++) + { + if (str == connectCamerasName_.at(i)) + { + POAGetCameraProperties(i, &camProp_); + selectCamIndex_ = i; + selectCamName_ = connectCamerasName_.at(i); + break; + } + } + } + else if (eAct == MM::BeforeGet) + { + pProp->Set(selectCamName_.c_str()); + } + + return DEVICE_OK; +} + +int POACamera::OnMaxExposure(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::BeforeGet) + { + pProp->Set(exposureMaximum_); + } + else if (eAct == MM::AfterSet) + { + pProp->Get(exposureMaximum_); + } + return DEVICE_OK; +} + + +void POACamera::SlowPropUpdate(std::string leaderValue) +{ + // wait in order to simulate a device doing something slowly + // in a thread + long delay; GetProperty("AsyncPropertyDelayMS", delay); + CDeviceUtils::SleepMs(delay); + { + MMThreadGuard g(asyncFollowerLock_); + asyncFollower_ = leaderValue; + } + OnPropertyChanged("AsyncPropertyFollower", leaderValue.c_str()); +} + +int POACamera::OnAsyncFollower(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::BeforeGet) { + MMThreadGuard g(asyncFollowerLock_); + pProp->Set(asyncFollower_.c_str()); + } + // no AfterSet as this is a readonly property + return DEVICE_OK; +} + +int POACamera::OnAsyncLeader(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::BeforeGet) { + pProp->Set(asyncLeader_.c_str()); + } + if (eAct == MM::AfterSet) + { + pProp->Get(asyncLeader_); + fut_ = std::async(std::launch::async, &POACamera::SlowPropUpdate, this, asyncLeader_); + } + return DEVICE_OK; +} + +/** +* Handles "Binning" property. +*/ +int POACamera::OnBinning(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (camProp_.cameraID < 0) + return DEVICE_NOT_CONNECTED; + + int ret = DEVICE_ERR; + switch (eAct) + { + case MM::AfterSet: + { + if (IsCapturing()) + return DEVICE_CAMERA_BUSY_ACQUIRING; + + long binFactor; + pProp->Get(binFactor); + + POAErrors error = POASetImageBin(camProp_.cameraID, binFactor); + + if (error == POA_OK) + { + RefreshCurrROIParas(); + + img_.Resize(cameraCCDXSize_, cameraCCDYSize_); + ManageRGB24Memory(); + + std::ostringstream os; + os << binSize_; + OnPropertyChanged(MM::g_Keyword_Binning, os.str().c_str()); + ret = DEVICE_OK; + } + } + break; + case MM::BeforeGet: + { + POAGetImageBin(camProp_.cameraID, &binSize_); + pProp->Set((long)binSize_); + ret = DEVICE_OK; + } + break; + default: + break; + } + return ret; +} + +/** +* Handles "PixelType" property. +*/ +int POACamera::OnPixelType(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (camProp_.cameraID < 0) + return DEVICE_NOT_CONNECTED; + + int ret = DEVICE_ERR; + switch (eAct) + { + case MM::AfterSet: + { + if (IsCapturing()) + return DEVICE_CAMERA_BUSY_ACQUIRING; + + std::string pixelType; + pProp->Get(pixelType); + POAImgFormat imgFmt; + + if (pixelType.compare(g_PixelType_8bit) == 0) + { + imgFmt = POA_RAW8; + } + else if (pixelType.compare(g_PixelType_16bit) == 0) + { + imgFmt = POA_RAW16; + } + else if (pixelType.compare(g_PixelType_32bitRGB) == 0) + { + imgFmt = POA_RGB24; + } + else if (pixelType.compare(g_PixelType_MONO8bit) == 0) + { + imgFmt = POA_MONO8; + } + else + { + imgFmt = POA_END; + } + + if (imgFmt == POA_END) + { + ret = ERR_UNKNOWN_MODE; + } + else + { + POAErrors error = POASetImageFormat(camProp_.cameraID, imgFmt); + if (error == POA_OK) + { + imgFmt_ = imgFmt; + + if (imgFmt == POA_RAW16) + { + nComponents_ = 1; + img_.Resize(img_.Width(), img_.Height(), 2); + bitDepth_ = 16; + } + else if (imgFmt == POA_RGB24) + { + nComponents_ = 4; + img_.Resize(img_.Width(), img_.Height(), 4); + bitDepth_ = 8; + } + else + { + nComponents_ = 1; + img_.Resize(img_.Width(), img_.Height(), 1); + bitDepth_ = 8; + } + ret = DEVICE_OK; + } + + ManageRGB24Memory(); + } + + } + break; + case MM::BeforeGet: + { + POAGetImageFormat(camProp_.cameraID, &imgFmt_); + pProp->Set(ImgFmtToString(imgFmt_)); + + ret = DEVICE_OK; + } break; + default: + break; + } + return ret; +} + +/** +* Handles "Exposure" property. +*/ +int POACamera::OnExposure(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (camProp_.cameraID < 0) + return DEVICE_NOT_CONNECTED; + + int ret = DEVICE_ERR; + switch (eAct) + { + case MM::AfterSet: + { + double dblExp; + pProp->Get(dblExp); + + if (dblExp < 0.0) + { + dblExp = 0.0; + } + else if (dblExp > exposureMaximum_) { + dblExp = exposureMaximum_; + } + + long lExp = (long) (dblExp * 1000.0); + POASetConfig(camProp_.cameraID, POA_EXPOSURE, lExp, POA_FALSE); + + ret = DEVICE_OK; + } + break; + case MM::BeforeGet: + { + long lExp = 0; + POABool isAuto = POA_FALSE; + POAGetConfig(camProp_.cameraID, POA_EXPOSURE, &lExp, &isAuto); + + pProp->Set(lExp / 1000.0); + + ret = DEVICE_OK; + } + break; + default: + break; + } + + return ret; +} + +int POACamera::OnExpAuto(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (camProp_.cameraID < 0) + return DEVICE_NOT_CONNECTED; + + int ret = DEVICE_ERR; + switch (eAct) + { + case MM::AfterSet: + { + std::string strExpAuto; + pProp->Get(strExpAuto); + long lExp = 0; + POABool isExpAuto = POA_FALSE; + POAGetConfig(camProp_.cameraID, POA_EXPOSURE, &lExp, &isExpAuto); // get current exp value first + + isExpAuto = (strExpAuto == g_PropName_ON) ? POA_TRUE : POA_FALSE; + POASetConfig(camProp_.cameraID, POA_EXPOSURE, lExp, isExpAuto); + ret = DEVICE_OK; + } + break; + case MM::BeforeGet: + { + long lExp = 0; + POABool isExpAuto = POA_FALSE; + POAGetConfig(camProp_.cameraID, POA_EXPOSURE, &lExp, &isExpAuto); + + pProp->Set((isExpAuto == POA_TRUE) ? g_PropName_ON : g_PropName_OFF); + + ret = DEVICE_OK; + } + break; + default: + break; + } + + return ret; +} + +/** +* Handles "Gain" property. +*/ +int POACamera::OnGain(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (camProp_.cameraID < 0) + return DEVICE_NOT_CONNECTED; + + int ret = DEVICE_ERR; + switch (eAct) + { + case MM::AfterSet: + { + long lGain; + pProp->Get(lGain); + + POASetConfig(camProp_.cameraID, POA_GAIN, lGain, POA_FALSE); + + ret = DEVICE_OK; + } + break; + case MM::BeforeGet: + { + long lGain = 0; + POABool isAuto = POA_FALSE; + POAGetConfig(camProp_.cameraID, POA_GAIN, &lGain, &isAuto); + + pProp->Set(lGain); + + ret = DEVICE_OK; + } + break; + default: + break; + } + + return ret; +} + +int POACamera::OnGainAuto(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (camProp_.cameraID < 0) + return DEVICE_NOT_CONNECTED; + + int ret = DEVICE_ERR; + switch (eAct) + { + case MM::AfterSet: + { + std::string strGainAuto; + pProp->Get(strGainAuto); + long lGain = 0; + POABool isGainAuto = POA_FALSE; + POAGetConfig(camProp_.cameraID, POA_GAIN, &lGain, &isGainAuto); // get current gain value first + + isGainAuto = (strGainAuto == g_PropName_ON) ? POA_TRUE : POA_FALSE; + POASetConfig(camProp_.cameraID, POA_GAIN, lGain, isGainAuto); + ret = DEVICE_OK; + } + break; + case MM::BeforeGet: + { + long lGain = 0; + POABool isGainAuto = POA_FALSE; + POAGetConfig(camProp_.cameraID, POA_GAIN, &lGain, &isGainAuto); + + pProp->Set((isGainAuto == POA_TRUE) ? g_PropName_ON : g_PropName_OFF); + + ret = DEVICE_OK; + } + break; + default: + break; + } + + return ret; +} + +int POACamera::OnAutoExpBrightness(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (camProp_.cameraID < 0) + return DEVICE_NOT_CONNECTED; + + int ret = DEVICE_ERR; + switch (eAct) + { + case MM::AfterSet: + { + long lBrighntness; + pProp->Get(lBrighntness); + + POASetConfig(camProp_.cameraID, POA_AUTOEXPO_BRIGHTNESS, lBrighntness, POA_FALSE); + + ret = DEVICE_OK; + } + break; + case MM::BeforeGet: + { + long lBrighntness = 0; + POABool isAuto = POA_FALSE; + POAGetConfig(camProp_.cameraID, POA_AUTOEXPO_BRIGHTNESS, &lBrighntness, &isAuto); + + pProp->Set(lBrighntness); + + ret = DEVICE_OK; + } + break; + default: + break; + } + + return ret; +} + +/** +* Handles "Offset" property. +*/ +int POACamera::OnOffset(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (camProp_.cameraID < 0) + return DEVICE_NOT_CONNECTED; + + int ret = DEVICE_ERR; + switch (eAct) + { + case MM::AfterSet: + { + long lOffset; + pProp->Get(lOffset); + + POASetConfig(camProp_.cameraID, POA_OFFSET, lOffset, POA_FALSE); + + ret = DEVICE_OK; + } + break; + case MM::BeforeGet: + { + long lOffset = 0; + POABool isAuto = POA_FALSE; + POAGetConfig(camProp_.cameraID, POA_OFFSET, &lOffset, &isAuto); + + pProp->Set(lOffset); + + ret = DEVICE_OK; + } + break; + default: + break; + } + + return ret; +} + +int POACamera::OnGamma(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (camProp_.cameraID < 0) + return DEVICE_NOT_CONNECTED; + + int ret = DEVICE_ERR; + switch (eAct) + { + case MM::AfterSet: + { + pProp->Get(gammaValue_); + + //reset gamma table + ResetGammaTable(); + + ret = DEVICE_OK; + } + break; + case MM::BeforeGet: + { + pProp->Set(gammaValue_); + + ret = DEVICE_OK; + } + break; + default: + break; + } + + return ret; +} + +/** +* Handles "USB Bandwidth Limit" property. +*/ +int POACamera::OnUSBBandwidthLimit(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (camProp_.cameraID < 0) + return DEVICE_NOT_CONNECTED; + + int ret = DEVICE_ERR; + switch (eAct) + { + case MM::AfterSet: + { + long lUSB_BW; + pProp->Get(lUSB_BW); + + POASetConfig(camProp_.cameraID, POA_USB_BANDWIDTH_LIMIT, lUSB_BW, POA_FALSE); + + ret = DEVICE_OK; + } + break; + case MM::BeforeGet: + { + long lUSB_BW = 0; + POABool isAuto = POA_FALSE; + POAGetConfig(camProp_.cameraID, POA_USB_BANDWIDTH_LIMIT, &lUSB_BW, &isAuto); + + pProp->Set(lUSB_BW); + + ret = DEVICE_OK; + } + break; + default: + break; + } + + return ret; +} + +/** +* Handles "Frame Rate Limit" property. +*/ +int POACamera::OnFrameRateLimit(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (camProp_.cameraID < 0) + return DEVICE_NOT_CONNECTED; + + int ret = DEVICE_ERR; + switch (eAct) + { + case MM::AfterSet: + { + long lFrmRateLimit; + pProp->Get(lFrmRateLimit); + + POASetConfig(camProp_.cameraID, POA_FRAME_LIMIT, lFrmRateLimit, POA_FALSE); + + ret = DEVICE_OK; + } + break; + case MM::BeforeGet: + { + long lFrmRateLimit = 0; + POABool isAuto = POA_FALSE; + POAGetConfig(camProp_.cameraID, POA_FRAME_LIMIT, &lFrmRateLimit, &isAuto); + + pProp->Set(lFrmRateLimit); + + ret = DEVICE_OK; + } + break; + default: + break; + } + + return ret; +} + +/** +* Handles "Image Flip" property. +*/ +int POACamera::OnFlip(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (camProp_.cameraID < 0) + return DEVICE_NOT_CONNECTED; + + int ret = DEVICE_ERR; + switch (eAct) + { + case MM::AfterSet: + { + std::string strFlip; + pProp->Get(strFlip); + + POAConfig flipCfg = POA_FLIP_NONE; + if (strFlip == g_FlipHori) + { + flipCfg = POA_FLIP_HORI; + } + else if (strFlip == g_FlipVert) + { + flipCfg = POA_FLIP_VERT; + } + else if (strFlip == g_FlipBoth) + { + flipCfg = POA_FLIP_BOTH; + } + + POASetConfig(camProp_.cameraID, flipCfg, POA_TRUE); + + ret = DEVICE_OK; + } + break; + case MM::BeforeGet: + { + std::string strFlip = g_FlipNone; + + POABool isEnableBoth = POA_FALSE, isEnableHori = POA_FALSE, isEnableVert = POA_FALSE; + + POAGetConfig(camProp_.cameraID, POA_FLIP_BOTH, &isEnableBoth); + POAGetConfig(camProp_.cameraID, POA_FLIP_HORI, &isEnableHori); + POAGetConfig(camProp_.cameraID, POA_FLIP_VERT, &isEnableVert); + + if (isEnableBoth == POA_TRUE) + { + strFlip = g_FlipBoth; + } + else if(isEnableHori == POA_TRUE) + { + strFlip = g_FlipHori; + } + else if (isEnableVert == POA_TRUE) + { + strFlip = g_FlipVert; + } + + pProp->Set(strFlip.c_str()); + + ret = DEVICE_OK; + } + break; + default: + break; + } + + return ret; +} + +int POACamera::OnCCDTemp(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (camProp_.cameraID < 0) + return DEVICE_NOT_CONNECTED; + + if (eAct == MM::BeforeGet) + { + POABool isAuto; + POAGetConfig(camProp_.cameraID, POA_TEMPERATURE, &ccdT_, &isAuto); + pProp->Set(ccdT_); + } + // no AfterSet as this is a readonly property + return DEVICE_OK; +} + + +int POACamera::OnHardBin(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (camProp_.cameraID < 0) + return DEVICE_NOT_CONNECTED; + + int ret = DEVICE_ERR; + switch (eAct) + { + case MM::AfterSet: + { + std::string strHardBin; + pProp->Get(strHardBin); + + POABool isHardBin = (strHardBin == g_PropName_ON) ? POA_TRUE : POA_FALSE; + POASetConfig(camProp_.cameraID, POA_HARDWARE_BIN, isHardBin); + ret = DEVICE_OK; + } + break; + case MM::BeforeGet: + { + POABool isHardBin; + POAGetConfig(camProp_.cameraID, POA_HARDWARE_BIN, &isHardBin); + + pProp->Set((isHardBin == POA_TRUE) ? g_PropName_ON : g_PropName_OFF); + + ret = DEVICE_OK; + } + break; + default: + break; + } + + return ret; +} + +int POACamera::OnPixelBinSum(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (camProp_.cameraID < 0) + return DEVICE_NOT_CONNECTED; + + int ret = DEVICE_ERR; + switch (eAct) + { + case MM::AfterSet: + { + std::string strPixelBinSum; + pProp->Get(strPixelBinSum); + + POABool isPixBinSum = (strPixelBinSum == g_PropName_ON) ? POA_TRUE : POA_FALSE; + POASetConfig(camProp_.cameraID, POA_PIXEL_BIN_SUM, isPixBinSum); + ret = DEVICE_OK; + } + break; + case MM::BeforeGet: + { + POABool isPixBinSum; + POAGetConfig(camProp_.cameraID, POA_PIXEL_BIN_SUM, &isPixBinSum); + + pProp->Set((isPixBinSum == POA_TRUE) ? g_PropName_ON : g_PropName_OFF); + + ret = DEVICE_OK; + } + break; + default: + break; + } + + return ret; +} + +int POACamera::OnWB_Red(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (camProp_.cameraID < 0) + return DEVICE_NOT_CONNECTED; + + int ret = DEVICE_ERR; + switch (eAct) + { + case MM::AfterSet: + { + long lWB_R; + pProp->Get(lWB_R); + + POASetConfig(camProp_.cameraID, POA_WB_R, lWB_R, POA_FALSE); + + ret = DEVICE_OK; + } + break; + case MM::BeforeGet: + { + long lWB_R = 0; + POABool isAuto = POA_FALSE; + POAGetConfig(camProp_.cameraID, POA_WB_R, &lWB_R, &isAuto); + + pProp->Set(lWB_R); + + ret = DEVICE_OK; + } + break; + default: + break; + } + + return ret; +} + +int POACamera::OnWB_Green(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (camProp_.cameraID < 0) + return DEVICE_NOT_CONNECTED; + + int ret = DEVICE_ERR; + switch (eAct) + { + case MM::AfterSet: + { + long lWB_G; + pProp->Get(lWB_G); + + POASetConfig(camProp_.cameraID, POA_WB_G, lWB_G, POA_FALSE); + + ret = DEVICE_OK; + } + break; + case MM::BeforeGet: + { + long lWB_G = 0; + POABool isAuto = POA_FALSE; + POAGetConfig(camProp_.cameraID, POA_WB_G, &lWB_G, &isAuto); + + pProp->Set(lWB_G); + + ret = DEVICE_OK; + } + break; + default: + break; + } + + return ret; +} + +int POACamera::OnWB_Blue(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (camProp_.cameraID < 0) + return DEVICE_NOT_CONNECTED; + + int ret = DEVICE_ERR; + switch (eAct) + { + case MM::AfterSet: + { + long lWB_B; + pProp->Get(lWB_B); + + POASetConfig(camProp_.cameraID, POA_WB_B, lWB_B, POA_FALSE); + + ret = DEVICE_OK; + } + break; + case MM::BeforeGet: + { + long lWB_B = 0; + POABool isAuto = POA_FALSE; + POAGetConfig(camProp_.cameraID, POA_WB_B, &lWB_B, &isAuto); + + pProp->Set(lWB_B); + + ret = DEVICE_OK; + } + break; + default: + break; + } + + return ret; +} + +int POACamera::OnAutoWB(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (camProp_.cameraID < 0) + return DEVICE_NOT_CONNECTED; + + int ret = DEVICE_ERR; + switch (eAct) + { + case MM::AfterSet: + { + std::string strAutoWB; + pProp->Get(strAutoWB); + + long lWB_R = 0; + POABool isAuto = POA_FALSE; + POAGetConfig(camProp_.cameraID, POA_WB_R, &lWB_R, &isAuto); + + isAuto = (strAutoWB == g_PropName_ON) ? POA_TRUE : POA_FALSE; + POASetConfig(camProp_.cameraID, POA_WB_R, lWB_R, isAuto); // just set R is OK + ret = DEVICE_OK; + } + break; + case MM::BeforeGet: + { + long lWB_R = 0; + POABool isAuto = POA_FALSE; + POAGetConfig(camProp_.cameraID, POA_WB_R, &lWB_R, &isAuto); + + pProp->Set((isAuto == POA_TRUE) ? g_PropName_ON : g_PropName_OFF); + + ret = DEVICE_OK; + } + break; + default: + break; + } + + return ret; +} + +int POACamera::OnMonoBin(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (camProp_.cameraID < 0) + return DEVICE_NOT_CONNECTED; + + int ret = DEVICE_ERR; + switch (eAct) + { + case MM::AfterSet: + { + std::string strMonoBin; + pProp->Get(strMonoBin); + + POABool isMonoBin = (strMonoBin == g_PropName_ON) ? POA_TRUE : POA_FALSE; + POASetConfig(camProp_.cameraID, POA_MONO_BIN, isMonoBin); + ret = DEVICE_OK; + } + break; + case MM::BeforeGet: + { + POABool isMonoBin; + POAGetConfig(camProp_.cameraID, POA_MONO_BIN, &isMonoBin); + + pProp->Set((isMonoBin == POA_TRUE) ? g_PropName_ON : g_PropName_OFF); + + ret = DEVICE_OK; + } + break; + default: + break; + } + + return ret; +} + +int POACamera::OnTargetTEMP(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (camProp_.cameraID < 0) + return DEVICE_NOT_CONNECTED; + + int ret = DEVICE_ERR; + switch (eAct) + { + case MM::AfterSet: + { + long lTargetTemp; + pProp->Get(lTargetTemp); + + POASetConfig(camProp_.cameraID, POA_TARGET_TEMP, lTargetTemp, POA_FALSE); + + ret = DEVICE_OK; + } + break; + case MM::BeforeGet: + { + long lTargetTemp = 0; + POABool isAuto = POA_FALSE; + POAGetConfig(camProp_.cameraID, POA_TARGET_TEMP, &lTargetTemp, &isAuto); + + pProp->Set(lTargetTemp); + + ret = DEVICE_OK; + } + break; + default: + break; + } + + return ret; +} + +int POACamera::OnCoolerOn(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (camProp_.cameraID < 0) + return DEVICE_NOT_CONNECTED; + + int ret = DEVICE_ERR; + switch (eAct) + { + case MM::AfterSet: + { + std::string strCoolerOn; + pProp->Get(strCoolerOn); + + POABool isCoolerOn = (strCoolerOn == g_PropName_ON) ? POA_TRUE : POA_FALSE; + POASetConfig(camProp_.cameraID, POA_COOLER, isCoolerOn); + ret = DEVICE_OK; + } + break; + case MM::BeforeGet: + { + POABool isCoolerOn; + POAGetConfig(camProp_.cameraID, POA_COOLER, &isCoolerOn); + + pProp->Set((isCoolerOn == POA_TRUE) ? g_PropName_ON : g_PropName_OFF); + + ret = DEVICE_OK; + } + break; + default: + break; + } + + return ret; +} + +int POACamera::OnCoolerPower(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (camProp_.cameraID < 0) + return DEVICE_NOT_CONNECTED; + + if (eAct == MM::BeforeGet) + { + long lCoolerPower = 0; + POABool isAuto = POA_FALSE; + POAGetConfig(camProp_.cameraID, POA_COOLER_POWER, &lCoolerPower, &isAuto); + pProp->Set(lCoolerPower); + } + // no AfterSet as this is a readonly property + return DEVICE_OK; +} + +int POACamera::OnFanPower(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (camProp_.cameraID < 0) + return DEVICE_NOT_CONNECTED; + + int ret = DEVICE_ERR; + switch (eAct) + { + case MM::AfterSet: + { + long lFanPower; + pProp->Get(lFanPower); + + POASetConfig(camProp_.cameraID, POA_FAN_POWER, lFanPower, POA_FALSE); + + ret = DEVICE_OK; + } + break; + case MM::BeforeGet: + { + long lFanPower = 0; + POABool isAuto = POA_FALSE; + POAGetConfig(camProp_.cameraID, POA_FAN_POWER, &lFanPower, &isAuto); + + pProp->Set(lFanPower); + + ret = DEVICE_OK; + } + break; + default: + break; + } + + return ret; +} + +int POACamera::OnHeaterPower(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (camProp_.cameraID < 0) + return DEVICE_NOT_CONNECTED; + + int ret = DEVICE_ERR; + switch (eAct) + { + case MM::AfterSet: + { + long lHeaterPower; + pProp->Get(lHeaterPower); + + POASetConfig(camProp_.cameraID, POA_HEATER_POWER, lHeaterPower, POA_FALSE); + + ret = DEVICE_OK; + } + break; + case MM::BeforeGet: + { + long lHeaterPower = 0; + POABool isAuto = POA_FALSE; + POAGetConfig(camProp_.cameraID, POA_HEATER_POWER, &lHeaterPower, &isAuto); + + pProp->Set(lHeaterPower); + + ret = DEVICE_OK; + } + break; + default: + break; + } + + return ret; +} + +int POACamera::OnGainPreset(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (camProp_.cameraID < 0) + return DEVICE_NOT_CONNECTED; + + int ret = DEVICE_ERR; + switch (eAct) + { + case MM::AfterSet: + { + std::string strGainPreset; + pProp->Get(strGainPreset); + + long lGain; + if (strGainPreset == g_gainHighestDR) + { + lGain = gainHighestDR_; + } + else if (strGainPreset == g_HCGain) + { + lGain = HCGain_; + } + else if (strGainPreset == g_unityGain) + { + lGain = unityGain_; + } + else if (strGainPreset == g_gainLowestRN) + { + lGain = gainLowestRN_; + } + else + { + POABool isAuto = POA_FALSE; + POAGetConfig(camProp_.cameraID, POA_GAIN, &lGain, &isAuto); + } + + SetProperty(MM::g_Keyword_Gain, CDeviceUtils::ConvertToString(lGain)); + //GetCoreCallback()->OnPropertyChanged(this, MM::g_Keyword_Gain, CDeviceUtils::ConvertToString(lGain)); + + ret = DEVICE_OK; + } + break; + case MM::BeforeGet: + { + std::string strGainPreset; + long lGain; + POABool isAuto = POA_FALSE; + POAGetConfig(camProp_.cameraID, POA_GAIN, &lGain, &isAuto); + + if (lGain == gainHighestDR_) + { + strGainPreset = g_gainHighestDR; + } + else if (lGain == HCGain_) + { + strGainPreset = g_HCGain; + } + else if (lGain == unityGain_) + { + strGainPreset = g_unityGain; + } + else if (lGain == gainLowestRN_) + { + strGainPreset = g_gainLowestRN; + } + else + { + strGainPreset = ""; + } + + + pProp->Set(strGainPreset.c_str()); + + ret = DEVICE_OK; + } + break; + default: + break; + } + + return ret; +} +int POACamera::OnOffsetPreset(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (camProp_.cameraID < 0) + return DEVICE_NOT_CONNECTED; + + int ret = DEVICE_ERR; + switch (eAct) + { + case MM::AfterSet: + { + std::string strOffsetPreset; + pProp->Get(strOffsetPreset); + + long lOffset; + if (strOffsetPreset == g_offsetHighestDR) + { + lOffset = offsetHighestDR_; + } + else if (strOffsetPreset == g_offsetHCGain) + { + lOffset = offsetHCGain_; + } + else if (strOffsetPreset == g_offsetUnityGain) + { + lOffset = offsetUnityGain_; + } + else if (strOffsetPreset == g_offsetLowestRN) + { + lOffset = offsetLowestRN_; + } + else + { + POABool isAuto = POA_FALSE; + POAGetConfig(camProp_.cameraID, POA_OFFSET, &lOffset, &isAuto); + } + + SetProperty(MM::g_Keyword_Offset, CDeviceUtils::ConvertToString(lOffset)); + //GetCoreCallback()->OnPropertyChanged(this, MM::g_Keyword_Offset, CDeviceUtils::ConvertToString(lOffset)); + + ret = DEVICE_OK; + } + break; + case MM::BeforeGet: + { + std::string strOffsetPreset; + long lOffset; + POABool isAuto = POA_FALSE; + POAGetConfig(camProp_.cameraID, POA_OFFSET, &lOffset, &isAuto); + + if (lOffset == offsetHighestDR_) + { + strOffsetPreset = g_offsetHighestDR; + } + else if (lOffset == offsetHCGain_) + { + strOffsetPreset = g_offsetHCGain; + } + else if (lOffset == offsetUnityGain_) + { + strOffsetPreset = g_offsetUnityGain; + } + else if (lOffset == offsetLowestRN_) + { + strOffsetPreset = g_offsetLowestRN; + } + else + { + strOffsetPreset = ""; + } + + + pProp->Set(strOffsetPreset.c_str()); + + ret = DEVICE_OK; + } + break; + default: + break; + } + + return ret; +} + +int POACamera::OnIsSequenceable(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + std::string val = "Yes"; + if (eAct == MM::BeforeGet) + { + if (!isSequenceable_) + { + val = "No"; + } + pProp->Set(val.c_str()); + } + else if (eAct == MM::AfterSet) + { + isSequenceable_ = false; + pProp->Get(val); + if (val == "Yes") + { + isSequenceable_ = true; + } + } + + return DEVICE_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +// Private POACamera methods +/////////////////////////////////////////////////////////////////////////////// + +/** +* Sync internal image buffer size to the chosen property values. +*/ +int POACamera::ResizeImageBuffer() +{ + char buf[MM::MaxStrLength]; + //int ret = GetProperty(MM::g_Keyword_Binning, buf); + //if (ret != DEVICE_OK) + // return ret; + //binSize_ = atol(buf); + + int ret = GetProperty(MM::g_Keyword_PixelType, buf); + if (ret != DEVICE_OK) + return ret; + + std::string pixelType(buf); + int byteDepth = 0; + + if (pixelType.compare(g_PixelType_8bit) == 0) + { + byteDepth = 1; + } + else if (pixelType.compare(g_PixelType_16bit) == 0) + { + byteDepth = 2; + } + else if (pixelType.compare(g_PixelType_32bitRGB) == 0) + { + byteDepth = 4; + } + else if (pixelType.compare(g_PixelType_MONO8bit) == 0) + { + byteDepth = 1; + } + + img_.Resize(cameraCCDXSize_, cameraCCDYSize_, byteDepth); + ManageRGB24Memory(); + + return DEVICE_OK; +} + +void POACamera::GenerateEmptyImage(ImgBuffer& img) +{ + MMThreadGuard g(imgPixelsLock_); + if (img.Height() == 0 || img.Width() == 0 || img.Depth() == 0) + return; + unsigned char* pBuf = const_cast(img.GetPixels()); + memset(pBuf, 0, img.Height() * img.Width() * img.Depth()); +} + +bool POACamera::GenerateColorTestPattern(ImgBuffer& img) +{ + unsigned width = img.Width(), height = img.Height(); + switch (img.Depth()) + { + case 1: + { + const unsigned char maxVal = 255; + unsigned char* rawBytes = img.GetPixelsRW(); + for (unsigned y = 0; y < height; ++y) + { + for (unsigned x = 0; x < width; ++x) + { + if (y == 0) + { + rawBytes[x] = (unsigned char)(maxVal * (x + 1) / (width - 1)); + } + else { + rawBytes[x + y * width] = rawBytes[x]; + } + } + } + return true; + } + case 2: + { + const unsigned short maxVal = 65535; + unsigned short* rawShorts = + reinterpret_cast(img.GetPixelsRW()); + for (unsigned y = 0; y < height; ++y) + { + for (unsigned x = 0; x < width; ++x) + { + if (y == 0) + { + rawShorts[x] = (unsigned short)(maxVal * (x + 1) / (width - 1)); + } + else { + rawShorts[x + y * width] = rawShorts[x]; + } + } + } + return true; + } + case 4: + { + const unsigned long maxVal = 255; + unsigned* rawPixels = reinterpret_cast(img.GetPixelsRW()); + for (unsigned section = 0; section < 8; ++section) + { + unsigned ystart = section * (height / 8); + unsigned ystop = section == 7 ? height : ystart + (height / 8); + for (unsigned y = ystart; y < ystop; ++y) + { + for (unsigned x = 0; x < width; ++x) + { + rawPixels[x + y * width] = 0; + for (unsigned component = 0; component < 4; ++component) + { + unsigned sample = 0; + if (component == section || + (section >= 4 && section - 4 != component)) + { + sample = maxVal * (x + 1) / (width - 1); + } + sample &= 0xff; // Just in case + rawPixels[x + y * width] |= sample << (8 * component); + } + } + } + } + return true; + } + } + return false; +} + + +void POACamera::TestResourceLocking(const bool recurse) +{ + if (recurse) + TestResourceLocking(false); +} + + +const char* POACamera::ImgFmtToString(const POAImgFormat& imgFmt) +{ + switch (imgFmt) + { + case POA_RAW8: + return g_PixelType_8bit; + case POA_RAW16: + return g_PixelType_16bit; + case POA_RGB24: + return g_PixelType_32bitRGB; + case POA_MONO8: + return g_PixelType_MONO8bit; + default: + return ""; + } +} + +void POACamera::RefreshCurrROIParas() +{ + POAGetImageBin(camProp_.cameraID, &binSize_); + + POAGetImageStartPos(camProp_.cameraID, &roiX_, &roiY_); + + POAGetImageSize(camProp_.cameraID, &cameraCCDXSize_, &cameraCCDYSize_); + + POAGetImageFormat(camProp_.cameraID, &imgFmt_); +} + +void POACamera::ManageRGB24Memory() +{ + //malloc RGB24 memory + if (imgFmt_ == POA_RGB24) + { + std::size_t size = (std::size_t)img_.Width() * img_.Height() * 3; + if (!pRGB24) + { + pRGB24 = new unsigned char[size]; + RGB24BufSize_ = size; + } + else + { + if (size != RGB24BufSize_) + { + delete[] pRGB24; + pRGB24 = nullptr; + + pRGB24 = new unsigned char[size]; + RGB24BufSize_ = size; + } + } + } + else + { + if (pRGB24) + { + delete[] pRGB24; + pRGB24 = nullptr; + } + } +} + +void POACamera::ResetGammaTable() +{ + if (!p8bitGammaTable || !p16bitGammaTable) + { + return; + } + + for (int i = 0; i < 256; i++) + { + double gamma_f = 1 / gammaValue_; + double f = std::pow((i + 0.5) / 255.0, gamma_f); + p8bitGammaTable[i] = (unsigned char)std::min(255.0, f * 255.0 - 0.5); + } + + for (int i = 0; i < 65536; i++) + { + double gamma_f = 1 / gammaValue_; + double f = std::pow((i + 0.5) / 65535.0, gamma_f); + p16bitGammaTable[i] = (unsigned short)std::min(65535.0, f * 65535.0 - 0.5); + } +} + +void POACamera::BGR888ToRGB32(unsigned char* pBGR888Buf, unsigned char* pRGB32Buf, int bgr888Len, bool isEnableGamma) +{ + int pixNum = bgr888Len / 3; + for (int i = 0; i < pixNum; i++) + { + unsigned char value_B = pBGR888Buf[i * 3]; + unsigned char value_G = pBGR888Buf[i * 3 + 1]; + unsigned char value_R = pBGR888Buf[i * 3 + 2]; + + if (isEnableGamma) + { + value_B = p8bitGammaTable[value_B]; + value_G = p8bitGammaTable[value_G]; + value_R = p8bitGammaTable[value_R]; + } + + pRGB32Buf[i * 4] = value_B; + pRGB32Buf[i * 4 + 1] = value_G; + pRGB32Buf[i * 4 + 2] = value_R; + pRGB32Buf[i * 4 + 3] = 255; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// POAFilterWheel implementation +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +POAFilterWheel::POAFilterWheel() : + initialized_(false), + changedTime_(0.0), + position_(0), + isBusyWait(false) +{ + InitializeDefaultErrorMessages(); + SetErrorText(ERR_UNKNOWN_POSITION, "Requested position not available in this device"); + EnableDelay(); // signals that the delay setting will be used + // parent ID display + CreateHubIDProperty(); + + PWProp_.Handle = -1; + + //scan all POA wheels + int PW_count = POAGetPWCount(); + PWProperties PWProp; + connectPWsName_.clear(); + for (int i = 0; i < PW_count; i++) + { + PWProp.Handle = -1; + POAGetPWProperties(i, &PWProp); + if (PWProp.Handle == -1) + { + continue; + } + connectPWsName_.push_back(std::string(PWProp.Name)); + } + + CPropertyAction* pAct = new CPropertyAction(this, &POAFilterWheel::OnSelectPWIndex); + + if (PW_count > 0) + { + selectPWIndex_ = 0; + selectPWName_ = connectPWsName_.at(selectPWIndex_); //default PW is first one + POAGetPWProperties(selectPWIndex_, &PWProp_); + } + else + { + selectPWIndex_ = -1; + selectPWName_ = "No POA filter wheel found"; + } + + CreateStringProperty(g_SelectFilterWheel, selectPWName_.c_str(), false, pAct, true); + SetAllowedValues(g_SelectFilterWheel, connectPWsName_); +} + +POAFilterWheel::~POAFilterWheel() +{ + Shutdown(); +} + +void POAFilterWheel::GetName(char* Name) const +{ + CDeviceUtils::CopyLimitedString(Name, g_WheelDeviceName); +} + + +int POAFilterWheel::Initialize() +{ + DemoHub* pHub = static_cast(GetParentHub()); + if (pHub) + { + char hubLabel[MM::MaxStrLength]; + pHub->GetLabel(hubLabel); + SetParentID(hubLabel); // for backward comp. + } + else + LogMessage(NoHubError); + + if (initialized_) + return DEVICE_OK; + + // set property list + // ----------------- + + // Name + int ret = CreateStringProperty(MM::g_Keyword_Name, g_WheelDeviceName, true); + if (DEVICE_OK != ret) + return ret; + + // Description + ret = CreateStringProperty(MM::g_Keyword_Description, "Player One Astronomy Filter Wheel Driver", true); + if (DEVICE_OK != ret) + return ret; + + // Set timer for the Busy signal, or we'll get a time-out the first time we check the state of the shutter, for good measure, go back 'delay' time into the past + changedTime_ = GetCurrentMMTime(); + + // Gate Closed Position + //ret = CreateIntegerProperty(MM::g_Keyword_Closed_Position, 0, false); + //if (ret != DEVICE_OK) + //return ret; + + if (PWProp_.Handle < 0) + return DEVICE_NOT_CONNECTED; + + if (POAOpenPW(PWProp_.Handle) != PW_OK) + { + return DEVICE_NOT_CONNECTED; + } + + // create default positions and labels + const int bufSize = 128; + char buf[bufSize]; + for (int i = 0; i < PWProp_.PositionCount; i++) + { + snprintf(buf, bufSize, "position-%d", i); + SetPositionLabel(i, buf); + //snprintf(buf, bufSize, "%ld", i); + //AddAllowedValue(MM::g_Keyword_Closed_Position, buf); + } + + // State + // ----- + POAGetCurrentPosition(PWProp_.Handle, &position_); + + CPropertyAction* pAct = new CPropertyAction(this, &POAFilterWheel::OnState); + ret = CreateIntegerProperty(MM::g_Keyword_State, position_, false, pAct); + SetPropertyLimits(MM::g_Keyword_State, 0, (PWProp_.PositionCount - 1)); + if (ret != DEVICE_OK) + return ret; + + // Label + // ----- + pAct = new CPropertyAction(this, &CStateBase::OnLabel); + ret = CreateStringProperty(MM::g_Keyword_Label, "", false, pAct); + if (ret != DEVICE_OK) + return ret; + + ret = UpdateStatus(); + if (ret != DEVICE_OK) + return ret; + + initialized_ = true; + + return DEVICE_OK; +} + +bool POAFilterWheel::Busy() +{ + if (isBusyWait) //Prevent frequent queries + { + MM::MMTime interval = GetCurrentMMTime() - changedTime_; + if (interval < MM::MMTime::fromMs(200)) + return true; + } + + PWState pw_state; + POAGetPWState(PWProp_.Handle, &pw_state); + if (pw_state == PW_STATE_MOVING) + { + changedTime_ = GetCurrentMMTime(); + isBusyWait = true; + return true; + } + else + { + isBusyWait = false; + return false; + } +} + + +int POAFilterWheel::Shutdown() +{ + if (initialized_) + { + initialized_ = false; + } + + POAClosePW(PWProp_.Handle); + return DEVICE_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +// Action handlers +/////////////////////////////////////////////////////////////////////////////// + +int POAFilterWheel::OnState(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::BeforeGet) + { + POAGetCurrentPosition(PWProp_.Handle, &position_); + pProp->Set((long)position_); + // nothing to do, let the caller to use cached property + } + else if (eAct == MM::AfterSet) + { + // Set timer for the Busy signal + changedTime_ = GetCurrentMMTime(); + + long pos; + pProp->Get(pos); + if (pos >= PWProp_.PositionCount || pos < 0) + { + return ERR_UNKNOWN_POSITION; + } + else + { + PWErrors err = POAGotoPosition(PWProp_.Handle, (int)pos); + if (err != PW_OK) + { + return DEVICE_ERR; + } + } + + position_ = pos; + } + + return DEVICE_OK; +} + +int POAFilterWheel::OnSelectPWIndex(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + std::string str; + if (eAct == MM::AfterSet) + { + pProp->Get(str); + for (int i = 0; i < connectPWsName_.size(); i++) + { + if (str == connectPWsName_.at(i)) + { + POAGetPWProperties(i, &PWProp_); + selectPWIndex_ = i; + selectPWName_ = connectPWsName_.at(i); + break; + } + } + } + else if (eAct == MM::BeforeGet) + { + pProp->Set(selectPWName_.c_str()); + } + + return DEVICE_OK; +} + +unsigned long POAFilterWheel::GetNumberOfPositions() const +{ + return PWProp_.PositionCount; +} + + +/////////////////////////////////////////////////////////////////////////////// +// Demo hub +/////////////////////////////////////////////////////////////////////////////// +int DemoHub::Initialize() +{ + initialized_ = true; + + return DEVICE_OK; +} + +int DemoHub::DetectInstalledDevices() +{ + ClearInstalledDevices(); + + // make sure this method is called before we look for available devices + InitializeModuleData(); + + char hubName[MM::MaxStrLength]; + GetName(hubName); // this device name + for (unsigned i = 0; i < GetNumberOfDevices(); i++) + { + char deviceName[MM::MaxStrLength]; + bool success = GetDeviceName(i, deviceName, MM::MaxStrLength); + if (success && (strcmp(hubName, deviceName) != 0)) + { + MM::Device* pDev = CreateDevice(deviceName); + AddInstalledDevice(pDev); + } + } + return DEVICE_OK; +} + +void DemoHub::GetName(char* pName) const +{ + CDeviceUtils::CopyLimitedString(pName, g_HubDeviceName); +} \ No newline at end of file diff --git a/DeviceAdapters/PlayerOne/POACamera.h b/DeviceAdapters/PlayerOne/POACamera.h new file mode 100644 index 000000000..6651a0de4 --- /dev/null +++ b/DeviceAdapters/PlayerOne/POACamera.h @@ -0,0 +1,323 @@ +/////////////////////////////////////////////////////////////////////////////// +// FILE: POACamera.h +// PROJECT: Micro-Manager +// SUBSYSTEM: DeviceAdapters +//----------------------------------------------------------------------------- +// DESCRIPTION: This is a device adapter of Micro-Manager for Player One cameras. +// This is modified based on DemoCamera project. +// +// AUTHOR: Lei Zhang, lei.zhang@player-one-astronomy.com, Feb 2024 +// +// COPYRIGHT: Player One Astronomy, SUZHOU, 2024 +// +// 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. + +#pragma once + +#include "DeviceBase.h" +#include "ImgBuffer.h" +#include "DeviceThreads.h" +#include "PlayerOneCamera.h" +#include "PlayerOnePW.h" + +#include +#include +#include +#include +#include + +////////////////////////////////////////////////////////////////////////////// +// Error codes +// +#define ERR_UNKNOWN_MODE 102 +#define ERR_UNKNOWN_POSITION 103 +#define ERR_IN_SEQUENCE 104 +#define ERR_SEQUENCE_INACTIVE 105 +#define ERR_STAGE_MOVING 106 +#define HUB_NOT_AVAILABLE 107 + +const char* NoHubError = "Parent Hub not defined."; + +//////////////////////// +// DemoHub +////////////////////// + +class DemoHub : public HubBase +{ +public: + DemoHub() : + initialized_(false), + busy_(false) + {} + ~DemoHub() {} + + // Device API + // --------- + int Initialize(); + int Shutdown() { return DEVICE_OK; }; + void GetName(char* pName) const; + bool Busy() { return busy_; }; + + // HUB api + int DetectInstalledDevices(); + +private: + void GetPeripheralInventory(); + + std::vector peripherals_; + bool initialized_; + bool busy_; +}; + +////////////////////////////////////////////////////////////////////////////// +// POACamera class +////////////////////////////////////////////////////////////////////////////// + +class MySequenceThread; + +class POACamera : public CCameraBase +{ +public: + POACamera(); + ~POACamera(); + + // MMDevice API + // ------------ + int Initialize(); + int Shutdown(); + + void GetName(char* name) const; + + // MMCamera 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(); + double GetNominalPixelSizeUm() const { return nominalPixelSizeUm_; } + double GetPixelSizeUm() const { return nominalPixelSizeUm_ * GetBinning(); } + int GetBinning() const; + int SetBinning(int bS); + + bool SupportsMultiROI(); + + int PrepareSequenceAcqusition(); + int StartSequenceAcquisition(double interval); + int StartSequenceAcquisition(long numImages, double interval_ms, bool stopOnOverflow); + int StopSequenceAcquisition(); + bool IsCapturing(); + + int InsertImage(); + int RunSequenceOnThread(MM::MMTime startTime); + void OnThreadExiting() throw(); + + int IsExposureSequenceable(bool& isSequenceable) const; + int GetExposureSequenceMaxLength(long& nrEvents) const; + int StartExposureSequence(); + int StopExposureSequence(); + int ClearExposureSequence(); + int AddToExposureSequence(double exposureTime_ms); + int SendExposureSequence() const; + + unsigned GetNumberOfComponents() const { return nComponents_; }; + + // action interface + // ---------------- + int OnSelectCamIndex(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnMaxExposure(MM::PropertyBase* pProp, MM::ActionType eAct); + + int OnAsyncFollower(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnAsyncLeader(MM::PropertyBase* pProp, MM::ActionType eAct); + void SlowPropUpdate(std::string leaderValue); + + int OnExposure(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnExpAuto(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnGain(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnGainAuto(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnAutoExpBrightness(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnBinning(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnPixelType(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnOffset(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnGamma(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnUSBBandwidthLimit(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnFrameRateLimit(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnFlip(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnCCDTemp(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnHardBin(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnPixelBinSum(MM::PropertyBase* pProp, MM::ActionType eAct); + + int OnWB_Red(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnWB_Green(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnWB_Blue(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnAutoWB(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnMonoBin(MM::PropertyBase* pProp, MM::ActionType eAct); + + int OnTargetTEMP(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnCoolerOn(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnCoolerPower(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnFanPower(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnHeaterPower(MM::PropertyBase* pProp, MM::ActionType eAct); + + int OnGainPreset(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnOffsetPreset(MM::PropertyBase* pProp, MM::ActionType eAct); + + int OnIsSequenceable(MM::PropertyBase* pProp, MM::ActionType eAct); + + long GetCCDXSize() { return cameraCCDXSize_; } + long GetCCDYSize() { return cameraCCDYSize_; } + + +private: + void TestResourceLocking(const bool); + void GenerateEmptyImage(ImgBuffer& img); + bool GenerateColorTestPattern(ImgBuffer& img); + int ResizeImageBuffer(); + const char* ImgFmtToString(const POAImgFormat &imgFmt); + // Refresh the current ROI parameters + void RefreshCurrROIParas(); + void ManageRGB24Memory(); + void ResetGammaTable(); + void BGR888ToRGB32(unsigned char* pBGR888Buf, unsigned char* pRGB32Buf, int bgr888Len, bool isEnableGamma = false); + + bool initialized_; + unsigned char* pRGB24; + std::size_t RGB24BufSize_; + + std::vector connectCamerasName_; + std::string selectCamName_; + int selectCamIndex_; + POACameraProperties camProp_; + int roiX_; + int roiY_; + int cameraCCDXSize_; + int cameraCCDYSize_; + int binSize_; + POAImgFormat imgFmt_; + int bitDepth_; + volatile bool m_bIsToStopExposure = false; + double gammaValue_; + unsigned char *p8bitGammaTable; + unsigned short *p16bitGammaTable; + double nominalPixelSizeUm_; + double exposureMaximum_; + ImgBuffer img_; + double ccdT_; + + int gainHighestDR_, HCGain_, unityGain_, gainLowestRN_; + int offsetHighestDR_, offsetHCGain_, offsetUnityGain_, offsetLowestRN_; + + double readoutUs_; + MM::MMTime readoutStartTime_; + + MM::MMTime sequenceStartTime_; + bool isSequenceable_; + long sequenceMaxLength_; + bool sequenceRunning_; + unsigned long sequenceIndex_; + double GetSequenceExposure(); + std::vector exposureSequence_; + long imageCounter_; + + bool stopOnOverflow_; + + bool supportsMultiROI_; + + std::string asyncLeader_; + std::string asyncFollower_; + MMThreadLock imgPixelsLock_; + MMThreadLock asyncFollowerLock_; + friend class MySequenceThread; + int nComponents_; + MySequenceThread* thd_; + std::future fut_; +}; + +class MySequenceThread : public MMDeviceThreadBase +{ + friend class POACamera; + enum { default_numImages = 1, default_intervalMS = 100 }; +public: + MySequenceThread(POACamera* pCam); + ~MySequenceThread(); + void Stop(); + void Start(long numImages, double intervalMs); + bool IsStopped(); + void Suspend(); + bool IsSuspended(); + void Resume(); + double GetIntervalMs() { return intervalMs_; } + void SetLength(long images) { numImages_ = images; } + long GetLength() const { return numImages_; } + long GetImageCounter() { return imageCounter_; } + MM::MMTime GetStartTime() { return startTime_; } + MM::MMTime GetActualDuration() { return actualDuration_; } +private: + int svc(void) throw(); + double intervalMs_; + long numImages_; + long imageCounter_; + bool stop_; + bool suspend_; + POACamera* camera_; + MM::MMTime startTime_; + MM::MMTime actualDuration_; + MM::MMTime lastFrameTime_; + MMThreadLock stopLock_; + MMThreadLock suspendLock_; +}; + +////////////////////////////////////////////////////////////////////////////// +// POAFilterWheel class +////////////////////////////////////////////////////////////////////////////// + +class POAFilterWheel : public CStateDeviceBase +{ +public: + POAFilterWheel(); + ~POAFilterWheel(); + + // MMDevice API + // ------------ + int Initialize(); + int Shutdown(); + + void GetName(char* pszName) const; + bool Busy(); + unsigned long GetNumberOfPositions()const; + + // action interface + // ---------------- + int OnState(MM::PropertyBase* pProp, MM::ActionType eAct); + + int OnSelectPWIndex(MM::PropertyBase* pProp, MM::ActionType eAct); + +private: + //long numPos_; + bool initialized_; + MM::MMTime changedTime_; + int position_; + + std::vector connectPWsName_; + std::string selectPWName_; + int selectPWIndex_; + PWProperties PWProp_; + bool isBusyWait; +}; + diff --git a/DeviceAdapters/PlayerOne/PlayerOne.vcxproj b/DeviceAdapters/PlayerOne/PlayerOne.vcxproj new file mode 100644 index 000000000..2290da107 --- /dev/null +++ b/DeviceAdapters/PlayerOne/PlayerOne.vcxproj @@ -0,0 +1,108 @@ + + + + + Debug + x64 + + + Release + x64 + + + + + + + + + + + {b8c95f39-54bf-40a9-807b-598df2821d55} + + + + 16.0 + Win32Proj + {46f4ffaf-79fd-45d9-a2c5-9e16b46787c5} + PlayerOne + 10.0 + + + + DynamicLibrary + true + v142 + Unicode + + + DynamicLibrary + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + true + + + false + + + + true + NOMINMAX +;WIN32;_DEBUG;PLAYERONE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + pch.h + $(MM_3RDPARTYPUBLIC)\PlayerOne\FilterWheelSDK\include;$(MM_3RDPARTYPUBLIC)\PlayerOne\CameraSDK\include;%(AdditionalIncludeDirectories) + + + Windows + true + false + $(MM_3RDPARTYPUBLIC)\PlayerOne\FilterWheelSDK\lib\Win\x64;$(MM_3RDPARTYPRIVATE)\PlayerOne\CameraSDK\lib\Win\x64;%(AdditionalLibraryDirectories) + PlayerOneCamera.lib;PlayerOnePW.lib;%(AdditionalDependencies) + + + + + true + true + true + NOMINMAX; +WIN32;NDEBUG;PLAYERONE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + pch.h + $(MM_3RDPARTYPUBLIC)\PlayerOne\FilterWheelSDK\include;$(MM_3RDPARTYPUBLIC)\PlayerOne\CameraSDK\include;%(AdditionalIncludeDirectories) + + + Windows + true + true + true + false + $(MM_3RDPARTYPUBLIC)\PlayerOne\FilterWheelSDK\lib\Win\x64;$(MM_3RDPARTYPUBLIC)\PlayerOne\CameraSDK\lib\Win\x64;%(AdditionalLibraryDirectories) + PlayerOneCamera.lib;PlayerOnePW.lib;%(AdditionalDependencies) + + + + + + \ No newline at end of file diff --git a/DeviceAdapters/PlayerOne/PlayerOne.vcxproj.filters b/DeviceAdapters/PlayerOne/PlayerOne.vcxproj.filters new file mode 100644 index 000000000..b8489bff6 --- /dev/null +++ b/DeviceAdapters/PlayerOne/PlayerOne.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 + + + + + 头文件 + + + + + æºæ–‡ä»¶ + + + \ No newline at end of file diff --git a/DeviceAdapters/PlayerOne/license.txt b/DeviceAdapters/PlayerOne/license.txt new file mode 100644 index 000000000..2adfa7398 --- /dev/null +++ b/DeviceAdapters/PlayerOne/license.txt @@ -0,0 +1,22 @@ +Copyright (c) 2024, Player One Astronomy Co., Ltd. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of +conditions and the following disclaimer. + +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 OWNER 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. diff --git a/DeviceAdapters/PointGrey/PointGrey.cpp b/DeviceAdapters/PointGrey/PointGrey.cpp index c6a4d3b91..cae282436 100644 --- a/DeviceAdapters/PointGrey/PointGrey.cpp +++ b/DeviceAdapters/PointGrey/PointGrey.cpp @@ -59,6 +59,9 @@ const char* g_InternalTrigger = "Internal"; const char* g_ExternalTrigger = "External"; const char* g_SoftwareTrigger = "Software"; const char* g_CameraTime = "CameraTime"; +const char* g_PolarityLow = "Low"; +const char* g_PolarityHigh = "High"; + ///////////////////////////////////////////////////// @@ -190,6 +193,7 @@ PointGrey::PointGrey(const char* deviceName) : isCapturing_(false), f7InUse_(false), triggerMode_(TRIGGER_INTERNAL), + triggerPolarity_(TRIGGER_POLARITY_LOW), externalTriggerGrabTimeout_(60000), bytesPerPixel_(1), imgBuf_(0), @@ -686,6 +690,14 @@ int PointGrey::Initialize() } } + if (triggerModeInfo.polaritySupported == true) + { + CPropertyAction* pAct = new CPropertyAction(this, &PointGrey::OnTriggerPolarity); + CreateProperty("TriggerPolarity", g_PolarityLow, MM::String, false, pAct, false); + AddAllowedValue("TriggerPolarity", g_PolarityLow); + AddAllowedValue("TriggerPolarity", g_PolarityHigh); + } + // We most likely want little endian bit order ret = SetEndianess(true); if (ret != DEVICE_OK) @@ -1823,6 +1835,65 @@ int PointGrey::OnTriggerMode(MM::PropertyBase* pProp, MM::ActionType eAct) return DEVICE_OK; } +/*************************************************************** +* Handles Trigger Polarity +* +* This property determines whether an external trigger must be +* in the high or low state to start an exposure.. +*/ +int PointGrey::OnTriggerPolarity(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::AfterSet) + { + Error error = cam_.StopCapture(); + // if camera is not capturing, StopCapture will return an error. + // probably safe to ignore, it is bad to return + if (error != PGRERROR_OK && error != PGRERROR_ISOCH_NOT_STARTED) + { + SetErrorText(ALLERRORS, error.GetDescription()); + return ALLERRORS; + } + + std::string polarity; + pProp->Get(polarity); + + int ret = DEVICE_OK; + unsigned short newPolarity; + if ( polarity == g_PolarityLow ) { + newPolarity = 0; + ret = SetTriggerPolarity(newPolarity); + } + else { + newPolarity = 1; + ret = SetTriggerPolarity(newPolarity); + } + if ( ret != DEVICE_OK ) + { + return ret; + } + error = cam_.StartCapture(); + if ( error != PGRERROR_OK ) + { + SetErrorText(ALLERRORS, error.GetDescription()); + return ALLERRORS; + } + + triggerPolarity_ = newPolarity; + } + else if ( eAct == MM::BeforeGet ) + { + if (triggerPolarity_ == TRIGGER_POLARITY_LOW) + { + pProp->Set(g_PolarityLow); + } + else { + pProp->Set(g_PolarityHigh); + } + } + + return DEVICE_OK; +} + /*********************************************************************** * Sets a register in the camera to indicate the endianness of the * output. It seems that MM wants little endian, most likely since @@ -2264,6 +2335,47 @@ int PointGrey::SetTriggerMode(const unsigned short newMode) return DEVICE_OK; } +int PointGrey::SetTriggerPolarity(const unsigned short newPolarity) +{ + if ((newPolarity != 0) && (newPolarity != 1)) + { + return ERR_INVALID_TRIGGER_POLARITY; + } + + if (triggerPolarity_ != newPolarity) + { + // Get current trigger settings + TriggerMode triggerMode; + Error error = cam_.GetTriggerMode(&triggerMode); + if (error != PGRERROR_OK) { + } + else + { + triggerMode.polarity = newPolarity; + error = cam_.SetTriggerMode(&triggerMode); + if (error != PGRERROR_OK) + { + SetErrorText(ALLERRORS, error.GetDescription()); + return ALLERRORS; + } + + if (triggerMode.source == 7) // 7 for software trigger + { + // Poll to ensure camera is ready + int ret = PollForTriggerReady(2000); + if (ret != DEVICE_OK) + { + return ret; + } + } + + triggerPolarity_ = newPolarity; + } + } + + return DEVICE_OK; +} + int PointGrey::SetGrabTimeout(const unsigned long timeoutMs) { FC2Config config; diff --git a/DeviceAdapters/PointGrey/PointGrey.h b/DeviceAdapters/PointGrey/PointGrey.h index 68be193aa..802157910 100644 --- a/DeviceAdapters/PointGrey/PointGrey.h +++ b/DeviceAdapters/PointGrey/PointGrey.h @@ -35,12 +35,17 @@ #define ERR_NOT_READY_FOR_SOFTWARE_TRIGGER 12301 #define ERR_UNAVAILABLE_TRIGGER_MODE_REQUESTED 12302 #define ERR_UNKNOWN_TRIGGER_MODE_STRING 12303 +#define ERR_INVALID_TRIGGER_POLARITY 12304 // Trigger modes #define TRIGGER_INTERNAL 0 #define TRIGGER_EXTERNAL 1 #define TRIGGER_SOFTWARE 2 +// Trigger polarities +#define TRIGGER_POLARITY_LOW 0 +#define TRIGGER_POLARITY_HIGH 1 + using namespace FlyCapture2; ////////////////////////////////////////////////////////////////////////////// @@ -112,6 +117,7 @@ class PointGrey : public CCameraBase int OnPixelType(MM::PropertyBase* pProp, MM::ActionType eAct); int OnFormat7Mode(MM::PropertyBase* pProp, MM::ActionType eAct); int OnTriggerMode(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnTriggerPolarity(MM::PropertyBase* pProp, MM::ActionType eAct); private: void updatePixelFormats(unsigned int pixelFormatBitField); @@ -121,6 +127,7 @@ class PointGrey : public CCameraBase int PollForTriggerReady(const unsigned long timeoutMs); bool FireSoftwareTrigger(); int SetTriggerMode(const unsigned short newMode); + int SetTriggerPolarity(const unsigned short newPolarity); int SetGrabTimeout(const unsigned long timeoutMs); int PowerCameraOn(const unsigned int timeoutMs); int TriggerModeFromString(std::string mode, unsigned short& tMode); @@ -148,6 +155,7 @@ class PointGrey : public CCameraBase bool f7InUse_; double exposureTimeMs_; unsigned short triggerMode_; + unsigned short triggerPolarity_; unsigned short snapTriggerMode_; unsigned long externalTriggerGrabTimeout_; unsigned short bytesPerPixel_; diff --git a/DeviceAdapters/PyDevice/.gitignore b/DeviceAdapters/PyDevice/.gitignore new file mode 100644 index 000000000..d723bc533 --- /dev/null +++ b/DeviceAdapters/PyDevice/.gitignore @@ -0,0 +1,11 @@ +AutoConfig.props +CoreLogs +*.log +*.idea +__pycache__ +build +.vs +PyDevice.sln +AutoConfig.props +x64 +*.dll diff --git a/DeviceAdapters/PyDevice/Actions.cpp b/DeviceAdapters/PyDevice/Actions.cpp new file mode 100644 index 000000000..4c9746622 --- /dev/null +++ b/DeviceAdapters/PyDevice/Actions.cpp @@ -0,0 +1,82 @@ +#include "pch.h" +#include "Actions.h" +#include "PyDevice.h" + +/** +* Callback that is called when a property value is read or written +* @return MM result code +*/ +int PyAction::Execute(MM::PropertyBase* pProp, MM::ActionType eAct) { + PyLock lock; + if (eAct != MM::BeforeGet && eAct != MM::AfterSet) + return DEVICE_OK; // nothing to do. + + if (eAct == MM::BeforeGet) { + auto value = getter_.Call(); + set(pProp, value); + } + else + setter_.Call(get(pProp)); + + return check_errors_(); +} + +void PyBoolAction::set(MM::PropertyBase* pProp, const PyObj& value) const noexcept { + pProp->Set(value.as()); +} + +PyObj PyBoolAction::get(MM::PropertyBase* pProp) const noexcept { + long value; + pProp->Get(value); + return PyObj(value); +} + +void PyFloatAction::set(MM::PropertyBase* pProp, const PyObj& value) const noexcept { + pProp->Set(value.as()); +} + +PyObj PyFloatAction::get(MM::PropertyBase* pProp) const noexcept { + double value; + pProp->Get(value); + return PyObj(value); +} + +void PyIntAction::set(MM::PropertyBase* pProp, const PyObj& value) const noexcept { + pProp->Set(value.as()); +} + +PyObj PyIntAction::get(MM::PropertyBase* pProp) const noexcept { + long value; + pProp->Get(value); + return PyObj(value); +} + +void PyStringAction::set(MM::PropertyBase* pProp, const PyObj& value) const noexcept { + pProp->Set(value.as().c_str()); +} + +PyObj PyStringAction::get(MM::PropertyBase* pProp) const noexcept { + string value; + pProp->Get(value); + return PyObj(value); +} + +void PyEnumAction::set(MM::PropertyBase* pProp, const PyObj& value) const noexcept { + for (size_t i = 0; i < enum_values.size(); i++) { + if (PyObject_RichCompareBool(enum_values[i], value, Py_EQ)) { + pProp->Set(enum_keys[i].c_str()); + return; + } + } + // value not found, do nothing +} + +PyObj PyEnumAction::get(MM::PropertyBase* pProp) const noexcept { + string value; + pProp->Get(value); + for (size_t i = 0; i < enum_keys.size(); i++) { + if (enum_keys[i] == value) + return enum_values[i]; + } + return PyObj(); // value not found +} diff --git a/DeviceAdapters/PyDevice/Actions.h b/DeviceAdapters/PyDevice/Actions.h new file mode 100644 index 000000000..b4a886b0b --- /dev/null +++ b/DeviceAdapters/PyDevice/Actions.h @@ -0,0 +1,64 @@ +#pragma once +#include "pch.h" +#include "PyObj.h" + +class PyAction : public MM::ActionFunctor { + PyObj getter_; + PyObj setter_; + ErrorCallback check_errors_; +public: + const string name; // Name of MM property + const MM::PropertyType type; + const bool readonly; + double min = 0; + double max = 0; + bool has_limits = false; + vector enum_keys; + vector enum_values; +public: + PyAction(const PyObj& getter, const PyObj& setter, const string& name, MM::PropertyType type, const ErrorCallback& callback) : getter_(getter), setter_(setter), check_errors_(callback), name(name), type(type), readonly(!setter_) {} + int Execute(MM::PropertyBase* pProp, MM::ActionType eAct) override; + virtual void set(MM::PropertyBase* pProp, const PyObj& value) const noexcept = 0; + virtual PyObj get(MM::PropertyBase* pProp) const noexcept = 0; +}; + +class PyIntAction : public PyAction { +public: + PyIntAction(const PyObj& getter, const PyObj& setter, const string& name, const ErrorCallback& callback) : PyAction(getter, setter, name, MM::Integer, callback) {} + void set(MM::PropertyBase* pProp, const PyObj& value) const noexcept override; + PyObj get(MM::PropertyBase* pProp) const noexcept override; +}; + +class PyBoolAction : public PyAction { +public: + PyBoolAction(const PyObj& getter, const PyObj& setter, const string& name, const ErrorCallback& callback) : PyAction(getter, setter, name, MM::Integer, callback) { + enum_keys.emplace_back("0"); + enum_values.emplace_back(false); + enum_keys.emplace_back("1"); + enum_values.emplace_back(true); + } + void set(MM::PropertyBase* pProp, const PyObj& value) const noexcept override; + PyObj get(MM::PropertyBase* pProp) const noexcept override; +}; + +class PyFloatAction : public PyAction { +public: + PyFloatAction(const PyObj& getter, const PyObj& setter, const string& name, const ErrorCallback& callback) : PyAction(getter, setter, name, MM::Float, callback) {} + void set(MM::PropertyBase* pProp, const PyObj& value) const noexcept override; + PyObj get(MM::PropertyBase* pProp) const noexcept override; +}; + +class PyStringAction : public PyAction { +public: + PyStringAction(const PyObj& getter, const PyObj& setter, const string& name, const ErrorCallback& callback) : PyAction(getter, setter, name, MM::String, callback) {} + void set(MM::PropertyBase* pProp, const PyObj& value) const noexcept override; + PyObj get(MM::PropertyBase* pProp) const noexcept override; +}; + +class PyEnumAction : public PyAction { +public: + PyEnumAction(const PyObj& getter, const PyObj& setter, const string& name, const ErrorCallback& callback) : PyAction(getter, setter, name, MM::String, callback) {} + void set(MM::PropertyBase* pProp, const PyObj& value) const noexcept override; + PyObj get(MM::PropertyBase* pProp) const noexcept override; +}; + diff --git a/DeviceAdapters/PyDevice/LICENCE.txt b/DeviceAdapters/PyDevice/LICENCE.txt new file mode 100644 index 000000000..dda005e36 --- /dev/null +++ b/DeviceAdapters/PyDevice/LICENCE.txt @@ -0,0 +1,32 @@ +BSD 3-Clause License + +Copyright (c) 2023, Ivo Vellekoop, Jeroen Doornbos, University of Twente +All rights reserved. + +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. \ No newline at end of file diff --git a/DeviceAdapters/PyDevice/Module.cpp b/DeviceAdapters/PyDevice/Module.cpp new file mode 100644 index 000000000..399a529d4 --- /dev/null +++ b/DeviceAdapters/PyDevice/Module.cpp @@ -0,0 +1,50 @@ +#include "pch.h" +#include "PyDevice.h" +#include "PyCamera.h" +#include "PyStage.h" + +// Entry points for loading the dll and creating devices. +MODULE_API void InitializeModuleData() +{ + RegisterDevice(CPyHub::g_adapterName, MM::HubDevice, "Runs a Python script that constructs one or more device objects"); +} + + +/** + * @brief Creates a hub device of MM device wrapper for a Python object + * @param id "PyHub" or "{devicetype}:{objectname}" + * @return newly created device, or nullptr if device name is not found +*/ +MODULE_API MM::Device* CreateDevice(const char* id) +{ + if (!id) + return nullptr; + + auto path = string(id); + + if (path == CPyHub::g_adapterName) + return new CPyHub(); + + // else, we are (re)creating an MM Device wrapper for an existing Python object. To locate the object, we must first extract the object type + + string deviceType; + string deviceName; + if (!CPyHub::SplitId(id, deviceType, deviceName)) + return nullptr; // invalid id + + if (deviceType == "Device") + return new CPyGenericDevice(id); + if (deviceType == "Camera") + return new CPyCamera(id); + if (deviceType == "Stage") + return new CPyStage(id); + if (deviceType == "XYStage") + return new CPyXYStage(id); + return nullptr; +} + + +MODULE_API void DeleteDevice(MM::Device* pDevice) +{ + delete pDevice; +} diff --git a/DeviceAdapters/PyDevice/PyCamera.cpp b/DeviceAdapters/PyDevice/PyCamera.cpp new file mode 100644 index 000000000..776eb74f8 --- /dev/null +++ b/DeviceAdapters/PyDevice/PyCamera.cpp @@ -0,0 +1,252 @@ +#include "pch.h" +#include "PyCamera.h" + +#include "buffer.h" + +const char* g_Keyword_Width = "Width"; +const char* g_Keyword_Height = "Height"; +const char* g_Keyword_Top = "Top"; +const char* g_Keyword_Left = "Left"; +const char* g_Keyword_Exposure = "Exposure-ms"; +const char* g_Keyword_Binning = "Binning"; +const char* g_Method_Read = "read"; + +/** +* Performs exposure and grabs a single image. +* This function should block during the actual exposure and return immediately afterwards +* (i.e., before readout). This behavior is needed for proper synchronization with the shutter. +* Required by the MM::Camera API. +*/ + +int CPyCamera::ConnectMethods(const PyObj& methods) +{ + _check_(PyCameraClass::ConnectMethods(methods)); + read_ = methods.GetDictItem("read"); + return CheckError(); +} + +int CPyCamera::SnapImage() +{ + auto frame = read_.Call(); + ReleaseBuffer(); + if (PyObject_GetBuffer(frame, &lastFrame_, PyBUF_C_CONTIGUOUS) == -1) + this->LogMessage("Error, 'image' property should return a numpy array"); + return CheckError(); +} + +int CPyCamera::Shutdown() +{ + StopSequenceAcquisition(); + ReleaseBuffer(); + return PyCameraClass::Shutdown(); +} + +/** +* 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(), GetImageHeight() and GetImageBytesPerPixel(). +* The calling program also 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* CPyCamera::GetImageBuffer() +{ + PyLock lock; + if (CheckError() != DEVICE_OK) + return nullptr; + + if (lastFrame_.buf == nullptr || lastFrame_.ndim != 2 || lastFrame_.itemsize != 2) { + this->LogMessage( + "Error, 'image' property should be a 2-dimensional numpy array that is c-contiguous in memory and contains 16 bit unsigned integers"); + return nullptr; + } + + // check if the array has the correct size + auto w = GetImageWidth(); + auto h = GetImageHeight(); + auto nw = lastFrame_.shape[1]; + auto nh = lastFrame_.shape[0]; + if (nw != w || nh != h) + { + auto msg = "Error, 'image' dimensions should be (" + std::to_string(w) + ", " + std::to_string(h) + + ") pixels, but were found to be (" + std::to_string(nw) + ", " + std::to_string(nh) + ") pixels"; + this->LogMessage(msg.c_str()); + return nullptr; + } + + return static_cast(lastFrame_.buf); +} + +/** +* Returns image buffer X-size in pixels. +* Required by the MM::Camera API. +*/ +unsigned CPyCamera::GetImageWidth() const +{ + return GetLongProperty(g_Keyword_Width); +} + +/** +* Returns image buffer Y-size in pixels. +* Required by the MM::Camera API. +*/ +unsigned CPyCamera::GetImageHeight() const +{ + return GetLongProperty(g_Keyword_Height); +} + +/** +* Returns image buffer pixel depth in bytes. +* Required by the MM::Camera API. +*/ +unsigned CPyCamera::GetImageBytesPerPixel() const +{ + return (GetBitDepth() + 7) / 8; +} + +/** +* Returns the bit depth (dynamic range) of the pixel. Fixed at 16 bit per pixel +* Required by the MM::Camera API. +*/ +unsigned CPyCamera::GetBitDepth() const +{ + return 16; +} + +/** +* Returns the size in bytes of the image buffer. +* Required by the MM::Camera API. +*/ +long CPyCamera::GetImageBufferSize() const +{ + return GetImageWidth() * GetImageHeight() * 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. +*/ +int CPyCamera::SetROI(unsigned x, unsigned y, unsigned xSize, unsigned ySize) +{ + if (xSize == 0 && ySize == 0) // special case: reset ROI + return ClearROI(); + + // apply ROI + PyLock lock; // make sure all four elements of the ROI are set without any other thread having access in between + SetLongProperty(g_Keyword_Left, x); + SetLongProperty(g_Keyword_Top, y); + SetLongProperty(g_Keyword_Width, xSize); + SetLongProperty(g_Keyword_Height, ySize); + return DEVICE_OK; +} + +/** +* Returns the actual dimensions of the current ROI. +* If multiple ROIs are set, then the returned ROI should encompass all of them. +* Required by the MM::Camera API. +*/ +int CPyCamera::GetROI(unsigned& x, unsigned& y, unsigned& xSize, unsigned& ySize) +{ + PyLock lock; // make sure all four elements of the ROI are read without any other thread having access + x = GetLongProperty(g_Keyword_Left); + y = GetLongProperty(g_Keyword_Top); + xSize = GetLongProperty(g_Keyword_Width); + ySize = GetLongProperty(g_Keyword_Height); + return DEVICE_OK; +} + +/** +* Resets the Region of Interest to full frame. +* Required by the MM::Camera API. +*/ +int CPyCamera::ClearROI() +{ + PyLock lock; // make sure all four elements of the ROI are set without any other thread having access in between + double width, height, top, left; + GetPropertyLowerLimit(g_Keyword_Top, top); + GetPropertyLowerLimit(g_Keyword_Left, left); + GetPropertyUpperLimit(g_Keyword_Width, width); + GetPropertyUpperLimit(g_Keyword_Height, height); + SetLongProperty(g_Keyword_Top, static_cast(top)); + SetLongProperty(g_Keyword_Left, static_cast(left)); + SetLongProperty(g_Keyword_Width, static_cast(width)); + SetLongProperty(g_Keyword_Height, static_cast(height)); + return DEVICE_OK; +} + + +/** +* Returns the current exposure setting in milliseconds. +* Required by the MM::Camera API. +*/ +double CPyCamera::GetExposure() const +{ + return GetFloatProperty(g_Keyword_Exposure); +} + +/** +* Sets exposure in milliseconds. +* Required by the MM::Camera API. +*/ +void CPyCamera::SetExposure(double exp_ms) +{ + if (SetFloatProperty(g_Keyword_Exposure, exp_ms) == DEVICE_OK) + GetCoreCallback()->OnExposureChanged(this, exp_ms); +} + +/** +* Returns the current binning factor. Currently only a binning of 1 (no binning) is supported +* Required by the MM::Camera API. +*/ +int CPyCamera::GetBinning() const +{ + return GetLongProperty(g_Keyword_Binning); +} + +/** +* Sets binning factor. +* Required by the MM::Camera API. +*/ +int CPyCamera::SetBinning(int binF) +{ + return SetLongProperty(g_Keyword_Binning, binF); +} + +int CPyCamera::IsExposureSequenceable(bool& isSequenceable) const +{ + isSequenceable = false; + return DEVICE_OK; +} + +// overriding default implementation which is broken (does not check for nullptr return from buffer) +int CPyCamera::InsertImage() +{ + char label[MM::MaxStrLength]; + this->GetLabel(label); + Metadata md; + md.put(MM::g_Keyword_Metadata_CameraLabel, label); + auto buffer = GetImageBuffer(); + if (!buffer) + return DEVICE_ERR; + + int ret = GetCoreCallback()->InsertImage(this, buffer, GetImageWidth(), + GetImageHeight(), GetImageBytesPerPixel(), + md.Serialize().c_str()); + if (!isStopOnOverflow() && ret == DEVICE_BUFFER_OVERFLOW) + { + // do not stop on overflow - just reset the buffer + GetCoreCallback()->ClearImageBuffer(this); + return GetCoreCallback()->InsertImage(this, buffer, GetImageWidth(), + GetImageHeight(), GetImageBytesPerPixel(), + md.Serialize().c_str()); + } + return ret; +} diff --git a/DeviceAdapters/PyDevice/PyCamera.h b/DeviceAdapters/PyDevice/PyCamera.h new file mode 100644 index 000000000..0fcb01de7 --- /dev/null +++ b/DeviceAdapters/PyDevice/PyCamera.h @@ -0,0 +1,43 @@ +#pragma once +#include "PyDevice.h" +#include "buffer.h" + +using PyCameraClass = CPyDeviceTemplate>; +class CPyCamera : public PyCameraClass { + Py_buffer lastFrame_; + PyObj read_; // the read() method of the camera object + +public: + CPyCamera(const string& id) : PyCameraClass(id) + { + lastFrame_.obj = nullptr; + lastFrame_.buf = nullptr; + } + const unsigned char* GetImageBuffer() override; + unsigned GetImageWidth() const override; + unsigned GetImageHeight() const override; + unsigned GetImageBytesPerPixel() const override; + unsigned GetBitDepth() const override; + long GetImageBufferSize() 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; + double GetExposure() const override; + void SetExposure(double exp_ms) override; + int GetBinning() const override; + int SetBinning(int binF) override; + int IsExposureSequenceable(bool& isSequenceable) const override; + int SnapImage() override; + int Shutdown() override; + int InsertImage() override; + int ConnectMethods(const PyObj& methods) override; + +private: + void ReleaseBuffer() + { + PyLock lock; + if (lastFrame_.obj) + PyBuffer_Release(&lastFrame_); + lastFrame_.buf = nullptr; + } +}; \ No newline at end of file diff --git a/DeviceAdapters/PyDevice/PyDevice.cpp b/DeviceAdapters/PyDevice/PyDevice.cpp new file mode 100644 index 000000000..70fb93838 --- /dev/null +++ b/DeviceAdapters/PyDevice/PyDevice.cpp @@ -0,0 +1,272 @@ +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// FILE: Pydevice.cpp +// PROJECT: Micro-Manager +// SUBSYSTEM: DeviceAdapters +//----------------------------------------------------------------------------- +// DESCRIPTION: The implementation of the Python camera. Adapted from the Democamera in the +// Micromanager repository. +// +// AUTHOR: Jeroen Doornbos +// Ivo Vellekoop +// COPYRIGHT: +// LICENSE: ? +#include "pch.h" +#include "PyDevice.h" + +CPyHub* CPyHub::g_the_hub = nullptr; + +CPyHub::CPyHub() : PyHubClass(g_adapterName) +{ + SetErrorText(ERR_PYTHON_SCRIPT_NOT_FOUND, "Could not find the Python script."); + SetErrorText(ERR_PYTHON_NO_DEVICE_DICT, "Script did not generate a global `device` variable holding a dictionary."); + SetErrorText( + ERR_PYTHON_ONLY_ONE_HUB_ALLOWED, + "Only one PyHub device may be active at a time. To combine multiple Python devices, write a script that combines them in a single `devices` dictionary."); + SetErrorText(ERR_PYTHON_RUNTIME_NOT_FOUND, "Could not locate the Python runtime. Make sure Python is installed and that the Python path is set to a virtual environment folder a with 'pyvenv.cfg' in it, or use the value '(auto)'."); + + CreateStringProperty(p_PythonScriptPath, "", false, nullptr, true); + CreateStringProperty(p_PythonPath, "(auto)", false, nullptr, true); + id_ = "PyHub"; +} + + +/** + * Destroys all Python objects by releasing the references we currently have + * + * @return +*/ +int CPyHub::Shutdown() +{ + if (initialized_) + { + PyLock lock; + devices_.clear(); + g_the_hub = nullptr; + PyObj::DeinitializeInterpreter(); + } + return PyHubClass::Shutdown(); +} + +int CPyHub::DetectInstalledDevices() +{ + ClearInstalledDevices(); + for (const auto& key_value : devices_) + { + auto mm_device = new CPyGenericDevice(key_value.first); + AddInstalledDevice(mm_device); + } + return CheckError(); +} + +/** + * @brief Loads the Python script specified in ScriptPath + * If the script is not found, a dialog box file browser is shown so that the user can select a file (Windows only). + * @return Script text string read from the file, or an empty string if the user cancelled the load. +*/ +string CPyHub::LoadScript() noexcept +{ + char scriptPathString[MM::MaxStrLength] = {0}; + if (GetProperty(p_PythonScriptPath, scriptPathString) != DEVICE_OK) + return string(); + + script_path_ = scriptPathString; +#ifdef _WIN32 + if (!FileExists(script_path_)) + { + // file not found, let the user select one + OPENFILENAMEW options = {0}; + wchar_t file_name[MAX_PATH] = {0}; + wcsncpy(file_name, script_path_.filename().generic_wstring().c_str(), MAX_PATH - 1); + options.lStructSize = sizeof(OPENFILENAMEW); + options.lpstrFilter = L"Python scripts\0*.py\0\0"; + options.lpstrFile = file_name; + options.lpstrTitle = L"Select Python file that includes the `devices` dictionary"; + options.nMaxFile = MAX_PATH; + + if (GetOpenFileName(&options)) + { + script_path_ = options.lpstrFile; + if (SetProperty(p_PythonScriptPath, script_path_.generic_u8string().c_str()) != DEVICE_OK) + return string(); + } + } +#endif + + // load the python script from disk + auto stream = std::ifstream(script_path_); + if (!stream) + return string(); // file not found + + auto code = std::string(); + char buffer[1024]; + while (stream.read(buffer, sizeof(buffer))) + code.append(buffer, 0, stream.gcount()); + + code.append(buffer, 0, stream.gcount()); + return code; +} + + + + +/** + * @brief Initialize the Python interpreter, run the script, and convert the 'devices' dictionary into a c++ map +*/ +int CPyHub::Initialize() +{ + if (!initialized_) + { + if (g_the_hub) + return ERR_PYTHON_ONLY_ONE_HUB_ALLOWED; + + auto script = LoadScript(); + if (script.empty()) + return ERR_PYTHON_SCRIPT_NOT_FOUND; + + char pythonPathStr[MM::MaxStrLength] = { 0 }; + if (GetProperty(p_PythonPath, pythonPathStr) != DEVICE_OK) + return {}; // could not read path property, this is an error! + + bool search = strcmp(pythonPathStr, "(auto)") == 0; + auto venvPath = search ? script_path_.parent_path() : fs::path(pythonPathStr); + auto pythonDllPath = InitializePython(venvPath, search); + if (pythonDllPath.empty()) + return ERR_PYTHON_RUNTIME_NOT_FOUND; + CreateStringProperty(p_DllPath, pythonDllPath.generic_u8string().c_str(), true, nullptr, false); + CreateStringProperty(p_VirtualEnvironment, venvPath.generic_u8string().c_str(), true, nullptr, false); + + + if (!PyObj::Bootstrap()) + return CheckError(); // initializing the interpreter failed, abort initialization and report the error + + PyLock lock; + // execute the Python script, and read the 'devices' field, + auto deviceDict = PyObj::g_load_devices.Call(PyObj(""), + PyObj(script_path_.parent_path().generic_u8string()), + PyObj(script_path_.stem().generic_u8string())); + if (!deviceDict) + return CheckError(); + + // process device list and add metadata + auto deviceList = PyObj(PyDict_Items(deviceDict)); + auto device_count = PyList_Size(deviceList); // todo: move to PyObj? to assert lock? + for (Py_ssize_t i = 0; i < device_count; i++) + { + auto key_value = deviceList.GetListItem(i); + auto name = key_value.GetTupleItem(0).as(); + auto obj = key_value.GetTupleItem(1); + auto type = obj.Get("device_type").as(); + auto id = ComposeId(type, name); + devices_[id] = obj; + } + + initialized_ = true; + g_the_hub = this; + } + return CheckError(); +} + +tuple, PyObj> EnumerateProperties(const PyObj& deviceInfo, const ErrorCallback& callback) noexcept +{ + PyLock lock; + + // Loop over all properties in the PyDevice Python object, and convert the properties to Action objects + // These objects can be used as callbacks for the MM property system, and used directly to get/set property values. + // + auto propertyDescriptors = vector(); + auto properties = deviceInfo.Get("properties"); + auto property_count = PyList_Size(properties); + for (Py_ssize_t i = 0; i < property_count; i++) + { + PyAction* descriptor; + auto pinfo = properties.GetListItem(i); + auto mmName = pinfo.Get("mm_name").as(); + auto getter = pinfo.Get("get"); + auto setter = pinfo.Get("set"); + auto type = pinfo.Get("data_type").as(); + + if (type == "int") + descriptor = new PyIntAction(getter, setter, mmName, callback); + else if (type == "float") + descriptor = new PyFloatAction(getter, setter, mmName, callback); + else if (type == "str") + descriptor = new PyStringAction(getter, setter, mmName, callback); + else if (type == "bool") + descriptor = new PyBoolAction(getter, setter, mmName, callback); + else if (type == "enum") + { + descriptor = new PyEnumAction(getter, setter, mmName, callback); + auto options = PyObj(PyDict_Items(pinfo.Get("options"))); + auto option_count = PyList_Size(options); + for (Py_ssize_t j = 0; j < option_count; j++) + { + auto key_value = options.GetListItem(j); + descriptor->enum_keys.push_back(key_value.GetTupleItem(0).as()); + descriptor->enum_values.push_back(key_value.GetTupleItem(1)); + } + } + else // other property type, skip + continue; + + if (descriptor->type == MM::Integer || descriptor->type == MM::Float) + { + auto lower = pinfo.Get("min"); + auto upper = pinfo.Get("max"); + if (lower != Py_None && upper != Py_None) + { + descriptor->min = lower.as(); + descriptor->max = upper.as(); + descriptor->has_limits = true; + } + } + + propertyDescriptors.push_back(descriptor); + } + + auto methods = deviceInfo.Get("methods"); + + return tuple(propertyDescriptors, methods); +} + + +/** +* Locates a Python object by device id +*/ +PyObj CPyHub::GetDeviceInfo(const string& device_id) noexcept +{ + // split device id + string deviceType; + string deviceName; + SplitId(device_id, deviceType, deviceName); + if (!g_the_hub) + return PyObj(); // no hub initialized + + auto device_idx = g_the_hub->devices_.find(device_id); + if (device_idx == g_the_hub->devices_.end()) + return PyObj(); // device not found + + return device_idx->second; +} + +/* + * A device id is of the form DeviceType:name, where : + * DeviceType is the device type : "Device", "Camera", etc. + * name is the key of the 'devices' dictionary that contains the object + */ +bool CPyHub::SplitId(const string& id, string& deviceType, string& deviceName) noexcept +{ + auto colon1 = id.find(':'); + if (colon1 != string::npos) + { + deviceType = id.substr(0, colon1); + deviceName = id.substr(colon1 + 1); + return true; + } + return false; +}; + +string CPyHub::ComposeId(const string& deviceType, const string& deviceName) noexcept +{ + return deviceType + ":" + deviceName; +} diff --git a/DeviceAdapters/PyDevice/PyDevice.h b/DeviceAdapters/PyDevice/PyDevice.h new file mode 100644 index 000000000..827ee0b33 --- /dev/null +++ b/DeviceAdapters/PyDevice/PyDevice.h @@ -0,0 +1,261 @@ +/////////////////////////////////////////////////////////////////////////////// +// FILE: Pydevice.h +// PROJECT: Micro-Manager +// SUBSYSTEM: DeviceAdapters +//----------------------------------------------------------------------------- +// DESCRIPTION: Generic device adapter that runs a Python script. Serves as base class for PyCamera, etc. +// +// AUTHOR: Ivo Vellekoop +// Jeroen Doornbos +// +// COPYRIGHT: University of Twente, Enschede, The Netherlands. +// +// 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. +#pragma once +#include "pch.h" +#include "PyObj.h" +#include "Actions.h" + +const char* const g_Method_Busy = "busy"; + +class CPyHub; +tuple, PyObj> EnumerateProperties(const PyObj& deviceInfo, const ErrorCallback& callback) noexcept; + +/** + * Base class for device adapters that are implement by a Python script. + * @tparam BaseType Base type to implement. Should be CCameraBase, CGenericDevice, etc. +*/ +template +class CPyDeviceTemplate : public BaseType +{ +protected: + bool initialized_ = false; + PyObj busy_; // busy() method + string id_; + +public: + /** + * Constructs a new device + * The device is not initialized, and no Python calls are made. This only sets up error messages, the error handler, and three 'pre-init' properties that hold the path of the Python libraries, the path of the Python script, and the name of the Python class that implements the device. + * @param id device type, e.g. "Camera:cam". + */ + CPyDeviceTemplate(const string& id) : BaseType(), id_(id) + { + this->SetErrorText( + ERR_PYTHON_EXCEPTION, "The Python code threw an exception, check the CoreLog error log for details"); + this->SetErrorText(ERR_PYTHON_RUNTIME_NOT_FOUND, ""); + } + + int CreateProperties(const vector& propertyDescriptors) noexcept + { + for (auto property : propertyDescriptors) + { + this->CreateProperty(property->name.c_str(), "", property->type, property->readonly, property, false); + + // Set limits. Only supported by MM if both upper and lower limit are present. + if (property->has_limits) + this->SetPropertyLimits(property->name.c_str(), property->min, property->max); + + + // For enum-type objects (may be string, int or float), notify MM about the allowed values + if (!property->enum_keys.empty()) + this->SetAllowedValues(property->name.c_str(), property->enum_keys); + } + /* + + propertyDescriptors.clear(); // remove the pointers from the vector because we transfered ownership of the Action objects in CreateProperty + + if (deviceInfo.HasAttribute("__doc__")) { + auto doc = deviceInfo.Get("__doc__"); + if (doc != Py_None) + this->SetDescription(doc.as().c_str()); + }*/ + return DEVICE_OK; + } + + /** + * Checks if a Python error has occurred since the last call to CheckError + * @return DEVICE_OK or ERR_PYTHON_EXCEPTION + */ + int CheckError() noexcept + { + PyLock lock; + PyObj::ReportError(); // check if any new errors happened + if (!PyObj::g_errorMessage.empty()) + { + // note: thread safety of this part relies on the PyLock + auto& err = PyObj::g_errorMessage; + this->SetErrorText(ERR_PYTHON_EXCEPTION, err.c_str()); + this->LogMessage(err.c_str()); //note: is this function thread safe?? + PyObj::g_errorMessage.clear(); + return ERR_PYTHON_EXCEPTION; + } + return DEVICE_OK; + } + + /** + * Executes the Python script and creates a Python object corresponding to the device + * Initializes the Python interpreter (if needed). + * The Python class may perform hardware initialization in its __init__ function. After creating the Python object and initializing it, the function 'InitializeDevice' is called, which may be overridden e.g. to check if all required properties are present on the Python object (see PyCamera for an example). + * @return MM error code + */ + int Initialize() override + { + if (!initialized_) + { + auto deviceInfo = CPyHub::GetDeviceInfo(id_); + if (!deviceInfo) + { + string deviceType, deviceName; + CPyHub::SplitId(id_, deviceType, deviceName); + auto altId = CPyHub::ComposeId("Device",deviceName); + deviceInfo = CPyHub::GetDeviceInfo(altId); + if (!deviceInfo) { + this->SetErrorText( + ERR_PYTHON_RUNTIME_NOT_FOUND, + ("Could not find the Python device id " + id_ + + ". It may be that the Python script or the device object within it was renamed.").c_str()); + return ERR_PYTHON_RUNTIME_NOT_FOUND; + } else + { + auto msg = "Did not recognize device type " + deviceType; + this->CreateProperty("WARNING", msg.c_str(), MM::String, true, nullptr, false); + } + } + auto [properties, methods] = EnumerateProperties(deviceInfo, [this]() { return this->CheckError(); }); + _check_(CheckError()); + _check_(CreateProperties(properties)); + _check_(ConnectMethods(methods)); + _check_(this->UpdateStatus()); // load value of all properties from the Python object + initialized_ = true; + } + return DEVICE_OK; + } + + long GetLongProperty(const char* property) const + { + long value = 0; + // Unfortunately, GetProperty is 'const' for some (historical?) reason. + // Therefore, we need to manually remove the const qualifier from 'this' + const_cast(static_cast(this))->GetProperty(property, value); + return value; + } + + int SetLongProperty(const char* property, long value) + { + return this->SetProperty(property, std::to_string(value).c_str()); + } + + double GetFloatProperty(const char* property) const + { + double value = 0.0; + const_cast(static_cast(this))->GetProperty(property, value); + return value; + } + + int SetFloatProperty(const char* property, double value) + { + return this->SetProperty(property, std::to_string(value).c_str()); + } + + virtual int ConnectMethods(const PyObj& methods) + { + busy_ = methods.GetDictItem("busy"); + return CheckError(); + } + + /** + * Destroys the Python object + * @todo Currently, the Python interpreter is never de-initialized, even if all devices have been destroyed. + */ + int Shutdown() override + { + initialized_ = false; + return DEVICE_OK; + } + + void GetName(char* name) const override + { + CDeviceUtils::CopyLimitedString(name, id_.c_str()); + } + + bool Busy() override + { + if (!busy_) + return false; // device does not have a busy() method + + auto retval = busy_.Call().as(); + CheckError(); + return retval; + } + + CPyDeviceTemplate(CPyDeviceTemplate& other) = delete; // disable copy +}; + +/** + * Class representing a generic device that is implemented by Python code + * @todo add buttons to the GUI so that we can activate the device so that it actually does something +*/ +using PyGenericClass = CPyDeviceTemplate>; + +class CPyGenericDevice : public PyGenericClass +{ +public: + CPyGenericDevice(const string& id) : PyGenericClass(id) + { + } +}; + + +using PyHubClass = CPyDeviceTemplate>; + +/** + @brief Entry point for pydevice. This is the device that is listed in the hardware configuration manager. + + Only a single hub can be active at a time. Therefore, all devices that will be used in the configuration need to be constructed and + initialized by a single Python script. + + Shutting down and initializing the Python runtime again is undefined behavior by the Python C API documentation. + Therefore, the runtime is only initialized the first time a Hub is initialized, and never de-initialized (see PyObj). +*/ +class CPyHub : public PyHubClass +{ + static constexpr const char* p_PythonScriptPath = "ScriptPath"; + static constexpr const char* p_PythonPath = "PythonEnvironment"; + static constexpr const char* p_DllPath = "Python executable"; + static constexpr const char* p_VirtualEnvironment = "Virtual environment"; + +public: + static constexpr const char* g_adapterName = "PyHub"; + CPyHub(); + int Initialize() override; + int Shutdown() override; + + static PyObj GetDeviceInfo(const string& device_id) noexcept; + static bool SplitId(const string& id, string& deviceType, string& deviceName) noexcept; + static string ComposeId(const string& deviceType, const string& deviceName) noexcept; + +protected: + int DetectInstalledDevices() override; + +private: + string LoadScript() noexcept; + + // list of devices read from the `devices` dictionary that the Python script returns. + std::map devices_; + + // Location of the currently loaded script + fs::path script_path_; + + // Pointer to the current (only) active Hub, or nullptr if no Hub is active. + static CPyHub* g_the_hub; +}; diff --git a/DeviceAdapters/PyDevice/PyDevice.sln b/DeviceAdapters/PyDevice/PyDevice.sln new file mode 100644 index 000000000..f9b866f42 --- /dev/null +++ b/DeviceAdapters/PyDevice/PyDevice.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.33424.131 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PyDevice", "PyDevice.vcxproj", "{36CF524A-8214-404C-8E6B-B5DEC1FDADF9}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {36CF524A-8214-404C-8E6B-B5DEC1FDADF9}.Debug|x64.ActiveCfg = Debug|x64 + {36CF524A-8214-404C-8E6B-B5DEC1FDADF9}.Debug|x64.Build.0 = Debug|x64 + {36CF524A-8214-404C-8E6B-B5DEC1FDADF9}.Debug|x86.ActiveCfg = Debug|x64 + {36CF524A-8214-404C-8E6B-B5DEC1FDADF9}.Debug|x86.Build.0 = Debug|x64 + {36CF524A-8214-404C-8E6B-B5DEC1FDADF9}.Release|x64.ActiveCfg = Release|x64 + {36CF524A-8214-404C-8E6B-B5DEC1FDADF9}.Release|x64.Build.0 = Release|x64 + {36CF524A-8214-404C-8E6B-B5DEC1FDADF9}.Release|x86.ActiveCfg = Release|x64 + {36CF524A-8214-404C-8E6B-B5DEC1FDADF9}.Release|x86.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {BB42987E-363A-4CB5-ADD7-F4FF09489E94} + EndGlobalSection +EndGlobal diff --git a/DeviceAdapters/PyDevice/PyDevice.vcxproj b/DeviceAdapters/PyDevice/PyDevice.vcxproj new file mode 100644 index 000000000..6bc5c7fa0 --- /dev/null +++ b/DeviceAdapters/PyDevice/PyDevice.vcxproj @@ -0,0 +1,153 @@ + + + + + Debug + x64 + + + Release + x64 + + + + + + + + + + + + CppHeader + + + + + + + + Create + Create + + + + + + + + + + + + + + {b8c95f39-54bf-40a9-807b-598df2821d55} + + + + 16.0 + Win32Proj + {36cf524a-8214-404c-8e6b-b5dec1fdadf9} + Pydevice + 10.0 + PyDevice + + + + DynamicLibrary + true + v142 + Unicode + + + DynamicLibrary + false + v142 + true + Unicode + + + + + + + + + + + + + + + $(VC_IncludePath);$(WindowsSDK_IncludePath);$(IncludePath) + $(MM_3RDPARTYPUBLIC)\Python\libs;$(LibraryPath) + + + + true + + + $(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64) + + + false + $(LibraryPath) + + + + Level4 + true + _DEBUG;PYDEVICE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + pch.h + %(AdditionalIncludeDirectories) + %(AdditionalOptions) + stdcpp17 + Use + + + Windows + true + false + $(MM_3RDPARTYPUBLIC)\Python\cp39-win_amd64\libs;%(AdditionalLibraryDirectories) + Comdlg32.lib + python39.dll + + + + + + + + + + + + + Level4 + true + true + true + NDEBUG;PYDEVICE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + pch.h + %(AdditionalIncludeDirectories) + stdcpp17 + Use + + + Windows + true + true + true + false + $(MM_3RDPARTYPUBLIC)\Python\cp39-win_amd64\libs;%(AdditionalLibraryDirectories) + Comdlg32.lib + python39.dll + + + + + + \ No newline at end of file diff --git a/DeviceAdapters/PyDevice/PyDevice.vcxproj.filters b/DeviceAdapters/PyDevice/PyDevice.vcxproj.filters new file mode 100644 index 000000000..9cbff42ac --- /dev/null +++ b/DeviceAdapters/PyDevice/PyDevice.vcxproj.filters @@ -0,0 +1,80 @@ + + + + + {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 + + + 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 + + + Resource Files + + + \ No newline at end of file diff --git a/DeviceAdapters/PyDevice/PyDevice.vcxproj.user b/DeviceAdapters/PyDevice/PyDevice.vcxproj.user new file mode 100644 index 000000000..dde8ca4cf --- /dev/null +++ b/DeviceAdapters/PyDevice/PyDevice.vcxproj.user @@ -0,0 +1,17 @@ + + + + $(ProjectDir)/debug_helper.bat + WindowsLocalDebugger + $(TargetPath) + MM_EXECUTABLE_PATH="C:\Program Files\Micro-Manager-2.0" +$(LocalDebuggerEnvironment) + + + $(ProjectDir)/debug_helper.bat + WindowsLocalDebugger + $(TargetPath) + MM_EXECUTABLE_PATH="C:\Program Files\Micro-Manager-2.0" +$(LocalDebuggerEnvironment) + + \ No newline at end of file diff --git a/DeviceAdapters/PyDevice/PyObj.cpp b/DeviceAdapters/PyDevice/PyObj.cpp new file mode 100644 index 000000000..c94744183 --- /dev/null +++ b/DeviceAdapters/PyDevice/PyObj.cpp @@ -0,0 +1,127 @@ +#include "pch.h" +#include "PyObj.h" + +PyObj PyObj::g_traceback_to_string; +PyObj PyObj::g_load_devices; +PyObj PyObj::g_main_module; +PyObj PyObj::g_global_scope; + +/** +* Takes a new reference and wraps it into a PyObj smart pointer. +* +* This function does not increase the reference count of the object (also see Borrow). The reference count is decreased when the PyObj smart pointer is destroyed (e.g. when it goes out of scope). +* +*/ +PyObj::PyObj(PyObject* obj) : p_(obj) { + if (!obj) + ReportError(); +} + +string PyObj::g_errorMessage; + + +/** +@brief Initializes the Python interpreter. +@return true on success, false on failure (the g_errorMessage field will be set). +*/ +bool PyObj::Bootstrap() noexcept +{ + // run the bootstrapping script + const char* bootstrap; + #include "bootstrap.py" + + PyLock lock; + auto module = PyObj(PyModule_New("pydevice")); + // Set properties on the new module object + if (PyModule_AddStringConstant(module, "__file__", "pydevice") != 0) + return ReportError(); + + g_global_scope = PyObj::Borrow(PyModule_GetDict(module)); // Returns a borrowed reference: no need to Py_DECREF() it once we are done + auto builtins = PyObj::Borrow(PyEval_GetBuiltins()); // Returns a borrowed reference: no need to Py_DECREF() it once we are done + if (PyDict_SetItemString(g_global_scope, "__builtins__", builtins) != 0) + return ReportError(); + + auto bootstrap_code = PyObj(Py_CompileString(&bootstrap[1], "bootstrap.py", Py_file_input)); + if (!bootstrap_code) + return ReportError(); + + if (PyEval_EvalCode(bootstrap_code, g_global_scope, g_global_scope) == nullptr) + return ReportError(); + + // get the um unit for use with stages + g_traceback_to_string = g_global_scope.GetDictItem("traceback_to_string"); + g_load_devices = g_global_scope.GetDictItem("load_devices"); + return ReportError(); +} + +/** + * @brief Clears all referencences to Python objects. + * Note, this does _not_ call Py_Finalize() because deinitializing/initializing Python multiple times is undefined behavior. + * Instead, we clean up as much as we can, making sure that this dll does not hold any refcount anymore. +*/ +void PyObj::DeinitializeInterpreter() noexcept +{ + PyLock lock; + g_traceback_to_string.Clear(); + g_load_devices.Clear(); + g_main_module.Clear(); + g_global_scope.Clear(); +} + + +/** + * @brief Compiles and executes the Python code + * @param code Python source code + * @param file_name Value of __file__. Also used in tracebacks + * @param locals Dictionary object that holds the local variables of the script. Can be used to 'return' values from the script + * @return true on success, false on failure (g_errorMessage will be set) +*/ +bool PyObj::RunScript(const string& code, const string& file_name, const PyObj& locals) noexcept { + PyLock lock; + auto bootstrap_code = PyObj(Py_CompileString(code.c_str(), file_name.c_str(), Py_file_input)); + if (!bootstrap_code) + return false; + return PyObj(PyEval_EvalCode(bootstrap_code, g_global_scope, locals)); // Py_None on success (->true), NULL on failure (->false) +} + + +/** + * Queries the Python error state to get additional information about the error, and resets the error state. + * Returns false if an error occurred + * +*/ +bool PyObj::ReportError() { + PyLock lock; // todo: should not be needed. If we don't hold the GIL, ReportError is undefined behavior in a multi-threaded context? + if (!PyErr_Occurred()) + return true; + + // prevent infinite recursion if an error happens in the CheckError function itself + static bool reentrant = false; + if (reentrant) { + PyErr_Clear(); + return false; + } + reentrant = true; + + auto msg = string("Python error."); + PyObject* type = nullptr; + PyObject* value = nullptr; + PyObject* traceback = nullptr; + PyErr_Fetch(&type, &value, &traceback); + if (type) { + msg += Borrow(type).as(); + msg += " : "; + } + if (value) + msg += Borrow(value).as(); + + if (traceback) + msg += "\n" + g_traceback_to_string.Call(Borrow(traceback)).as(); + + PyErr_Restore(type, value, traceback); + PyErr_Clear(); + g_errorMessage += msg + '\n'; + reentrant = false; + return false; +} + diff --git a/DeviceAdapters/PyDevice/PyObj.h b/DeviceAdapters/PyDevice/PyObj.h new file mode 100644 index 000000000..b9b196758 --- /dev/null +++ b/DeviceAdapters/PyDevice/PyObj.h @@ -0,0 +1,240 @@ +#pragma once +#include "pch.h" + +/** + * Helper class to automatically lock and unlock the global interpreter lock (GIL) + * This is needed because Python is single threaded (!) while MM is not. + * Note that the GIL should be locked for any Python call, including Py_INCREF and Py_DECREF +*/ +class PyLock { + PyGILState_STATE gstate_; +public: + PyLock() { + gstate_ = PyGILState_Ensure(); + } + ~PyLock() { + PyGILState_Release(gstate_); + } + + PyLock(const PyLock&) = delete; // cannot copy this object + PyLock& operator=(const PyLock&) = delete; +}; + +/** + * @brief Signature of a function that checks for Python errors and reports them to micro-manager. + * @returns MM error code or Device_OK +*/ +using ErrorCallback = function; + + +/** +@brief A smart pointer to a Python object + +A PyObj wraps a PyObject* pointer in a smart pointer, taking care of automatic reference counting, GIL locking, and type conversions. +Access to the attributes of the Python object is provided through Get, Set, and HasAttribute. +Member functions can be called using CallMethod, and callable Python objects can be invoked using Call. + +Because the common way of the Python API to report errors is to return a null pointer, the PyObj constructor is the main point where errors raised by the Python code are captured. + +PyObj has static functions to initialize the Python interpreter and run scripts. +*/ +class PyObj { + PyObject* p_; +public: + PyObj() : p_(nullptr) { + } + PyObj(PyObj&& other) noexcept : p_(other.p_) { + other.p_ = nullptr; + } + + explicit PyObj(PyObject* obj); + PyObj(const PyObj& other) : p_(other ? other : nullptr) { + if (p_) { + PyLock lock; + Py_INCREF(p_); + } + } + + // utility functions to construct new Python object from primitive values + explicit PyObj(double value) { + PyLock lock; + p_ = PyFloat_FromDouble(value); + if (!p_) + ReportError(); + } + explicit PyObj(const char* value) { + PyLock lock; + p_ = PyUnicode_FromString(value); + if (!p_) + ReportError(); + } + + explicit PyObj(long value) { + PyLock lock; + p_ = PyLong_FromLong(value); + if (!p_) + ReportError(); + } + + explicit PyObj(bool value) : PyObj(Borrow(value ? Py_True : Py_False)) {} + explicit PyObj(const string& value) : PyObj(value.c_str()) {} + + // utility functions to convert to primitive types + // note: if an error occurred during these functions, it will be logged in the g_errorMessage (also see CheckErrors) check for python + // note: the current thread must hold the GIL (see PyLock) + template T as() const; + template <> long as() const { + PyLock lock; + auto retval = PyLong_AsLong(*this); + if (retval == -1) // may be an error + ReportError(); + return retval; + } + template <> bool as() const { + return p_ == Py_True; + } + template <> double as() const { + if (p_ == Py_None) + return NAN; + PyLock lock; + auto retval = PyFloat_AsDouble(*this); + if (retval == -1.0) // may be an error + ReportError(); + return retval; + } + template <> string as() const { + PyLock lock; + if (auto as_str = PyObj(PyObject_Str(*this))) { // convert any object to a Python string by calling the str() function + if (auto as_bytes = PyObj(PyUnicode_AsUTF8String(as_str))) { + if (auto string_bytes = PyBytes_AsString(as_bytes)) { + auto retval = string(string_bytes); // copies the string (before releasing lock) + return retval; + } + else + ReportError(); + } + } + return string(); + } + template <> PyObj as() const { + return *this; + } + template PyObj CallMember(const char* function, Arguments... arguments) const noexcept { + PyLock lock; + return Get(function).Call(arguments...); + } + template PyObj Call(Arguments... arguments) const noexcept { + PyLock lock; + return PyObj(PyObject_CallFunctionObjArgs(*this, static_cast(arguments)..., NULL)); + } + PyObj Get(const char* attribute) const noexcept { + PyLock lock; + return PyObj(PyObject_GetAttrString(*this, attribute)); + } + /*PyObj Get(const string& attribute) const noexcept { + return Get(attribute.c_str()); + }*/ + /* + template void SetDictItem(const string& key, T value) { + SetDictItem(key.c_str(), value); + } + template void SetDictItem(const char* key, T value) { + PyLock lock; + if (PyDict_SetItemString(*this, key, PyObj(value)) != 0) + ReportError(); + }*/ + // Returns the dictionary item indicated by 'key' + // returns an empty PyObj if the key is missing + PyObj GetDictItem(const char* key) const noexcept { + PyLock lock; + return Borrow(PyDict_GetItemString(*this, key)); + } + /* + PyObj GetDictItem(const string& key) const noexcept { + return GetDictItem(key.c_str()); + }*/ + PyObj GetListItem(Py_ssize_t index) const noexcept { + return Borrow(PyList_GetItem(*this, index)); + } + PyObj GetTupleItem(Py_ssize_t index) const noexcept { + return Borrow(PyTuple_GetItem(*this, index)); + } + /* + bool HasAttribute(const string& attribute) const noexcept { + return HasAttribute(attribute.c_str()); + } + bool HasAttribute(const char* attribute) const noexcept { + PyLock lock; + return PyObject_HasAttrString(*this, attribute); + }*/ + + /** + * @brief Clear the reference (setting it to nullptr). If this is the last reference to the object, the object is destroyed. + */ + void Clear() { + if (p_) { + PyLock lock; + Py_DECREF(p_); + p_ = nullptr; + } + } + ~PyObj() { + Clear(); + } + + /** + * @brief Used to access the contained PyObject pointer + */ + operator PyObject* () const { + return p_ ? p_ : Py_None; + } + operator bool() const { + return p_ != nullptr; + } + PyObj& operator = (PyObj&& other) noexcept { + Clear(); + p_ = other.p_; + other.p_ = nullptr; + return *this; + } + PyObj& operator = (const PyObj& other) { + if ((p_ || other.p_) && p_ != other.p_) { + PyLock lock; + Py_XDECREF(p_); + p_ = other; + Py_XINCREF(p_); + } + return *this; + } + + static bool Bootstrap() noexcept; + static void DeinitializeInterpreter() noexcept; + static bool RunScript(const string& code, const string& file_name, const PyObj& locals) noexcept; + + /** + * Checks if a Python error has occurred. If so, logs the error and resets the error state. + * Note: Python error handling is very fragile, and it is essential to check for errors after every call to a Python API function. This usually happens automatically by converting the result to a PyObj (see PyObj constructor). Failure to reset the error state after a Python exception has occurred results in very strange behavior (unrelated fake errors popping up in unrelated parts of the program) or a complete crash of the program (this happens in some cases when throwing an exception without resetting the error state first). + + The errors are all concatenated as a single string. Also see PythonBridge::CheckError, since this is the place where the error list is copied to the MM CoreDebug log and reported to the end user. + */ + static bool ReportError(); + static string g_errorMessage; + static PyObj g_traceback_to_string; + static PyObj g_load_devices; + static PyObj g_main_module; + static PyObj g_global_scope; + private: + /** + * Takes a borrowed reference and wraps it in a PyObj smart pointer + * This increases the reference count of the object. + * The reference count is decreased when the PyObj smart pointer is destroyed (or goes out of scope). + */ + static PyObj Borrow(PyObject* obj) { + if (obj) { + PyLock lock; + Py_INCREF(obj); + } + return PyObj(obj); + } +}; + diff --git a/DeviceAdapters/PyDevice/PyStage.cpp b/DeviceAdapters/PyDevice/PyStage.cpp new file mode 100644 index 000000000..4bedc23de --- /dev/null +++ b/DeviceAdapters/PyDevice/PyStage.cpp @@ -0,0 +1,177 @@ +#include "pch.h" +#include "PyStage.h" + +const char* g_Keyword_Position = "Position-um"; +const char* g_Keyword_X = "X-um"; +const char* g_Keyword_Y = "Y-um"; +const char* g_Keyword_StepSize = "StepSize-um"; +const char* g_Keyword_StepSizeX = "StepSizeX-um"; +const char* g_Keyword_StepSizeY = "StepSizeY-um"; + +/** + * Home the stage + * + * For stages that support homing, the stage is moved to some fixed position (e.g. using a homing switch). This position is defined as position 0.0. + * This function does _not_ wait for homing to complete, but does update the position known to MM ('OnStagePositionChanged'). Also see `Busy()`. + * +*/ +int CPyStage::Home() +{ + _check_(home_.Call()); + origin_ = 0.0; + set_pos_ = 0.0; + OnStagePositionChanged(0.0); + return CheckError(); +} + +int CPyXYStage::Home() +{ + _check_(home_.Call()); + origin_x_ = 0.0; + origin_y_ = 0.0; + set_pos_x_ = 0.0; + set_pos_y_ = 0.0; + OnXYStagePositionChanged(0.0, 0.0); + return CheckError(); +} + + +/** + * Returns the current position of the stage in micrometers. + * This function does _not_ wait for the stage to reach a stable position, so it may still be moving at this point. +*/ +int CPyStage::GetPositionUm(double& pos) { + pos = GetFloatProperty(g_Keyword_Position) - origin_; + return CheckError(); +} + +int CPyXYStage::GetPositionUm(double& x, double& y) { + PyLock lock; + x = GetFloatProperty(g_Keyword_X) - origin_x_; + y = GetFloatProperty(g_Keyword_Y) - origin_y_; + return CheckError(); +} + +/** + * Sets the desired absolute position of the stage in micrometers. + * This function does _not_ wait for the stage to reach this position. +*/ +int CPyStage::SetPositionUm(double pos) { + PyLock lock; + SetFloatProperty(g_Keyword_Position, pos + origin_); + set_pos_ = pos; + OnStagePositionChanged(pos); + return CheckError(); +} + +int CPyXYStage::SetPositionUm(double x, double y) { + PyLock lock; + SetFloatProperty(g_Keyword_X, x + origin_x_); + SetFloatProperty(g_Keyword_Y, y + origin_y_); + set_pos_x_ = x; + set_pos_y_ = y; + OnXYStagePositionChanged(x, y); + return CheckError(); +} + +/** + * Sets the position relative to the currently _set_ position (note, this may not be where the stage is at the moment if the stage is still moving) +*/ +int CPyStage::SetRelativePositionUm(double dpos) { + if (isnan(set_pos_)) + _check_(GetPositionUm(set_pos_)); + + return SetPositionUm(set_pos_ + dpos); +} + +int CPyXYStage::SetRelativePositionUm(double dx, double dy) { + double dummy; + if (isnan(set_pos_x_)) + _check_(GetPositionUm(set_pos_x_, dummy)); + if (isnan(set_pos_y_)) + _check_(GetPositionUm(dummy, set_pos_y_)); + + return SetPositionUm(set_pos_x_ + dx, set_pos_y_ + dy); +} + + +/** + * Stops the stage by ordering it to move to the current position + * Does _not_ wait for the stage to stop. (see Busy()) +*/ +int CPyStage::Stop() { + PyLock lock; + double pos; + _check_(GetPositionUm(pos)); + return SetPositionUm(pos); +} + +int CPyXYStage::Stop() { + PyLock lock; + double x, y; + _check_(GetPositionUm(x, y)); + return SetPositionUm(x, y); +} + +double CPyStage::StepSizeUm() const { + PyLock lock; + return GetFloatProperty(g_Keyword_StepSize); +} + +double CPyXYStage::GetStepSizeXUm() { + PyLock lock; + return GetFloatProperty(g_Keyword_StepSizeX); +} + +double CPyXYStage::GetStepSizeYUm() { + PyLock lock; + return GetFloatProperty(g_Keyword_StepSizeY); +} + +// Sets current position as home. Returns DEVICE_UNKNOWN_POSITION if the device is still moving +int CPyStage::SetOrigin() { + if (Busy()) + return DEVICE_UNKNOWN_POSITION; + return GetPositionUm(origin_); +} + +// Sets current position as home +int CPyXYStage::SetXOrigin() { + if (Busy()) + return DEVICE_UNKNOWN_POSITION; + double dummy; + return GetPositionUm(origin_x_, dummy); +} + +int CPyXYStage::SetYOrigin() { + if (Busy()) + return DEVICE_UNKNOWN_POSITION; + double dummy; + return GetPositionUm(dummy, origin_y_); +} + +int CPyXYStage::SetOrigin() { + if (Busy()) + return DEVICE_UNKNOWN_POSITION; + return GetPositionUm(origin_x_, origin_y_); +} + +int CPyStage::GetLimits(double& lower, double& upper) { + _check_(GetPropertyLowerLimit(g_Keyword_Position, lower)); + _check_(GetPropertyUpperLimit(g_Keyword_Position, upper)); + lower -= origin_; + upper -= origin_; + return upper == lower ? DEVICE_UNSUPPORTED_COMMAND : DEVICE_OK; +} + +int CPyXYStage::GetLimitsUm(double& x_lower, double& x_upper, double& y_lower, double& y_upper) { + _check_(GetPropertyLowerLimit(g_Keyword_X, x_lower)); + _check_(GetPropertyUpperLimit(g_Keyword_X, x_upper)); + _check_(GetPropertyLowerLimit(g_Keyword_Y, y_lower)); + _check_(GetPropertyUpperLimit(g_Keyword_Y, y_upper)); + x_lower -= origin_x_; + x_upper -= origin_x_; + y_lower -= origin_y_; + y_upper -= origin_y_; + return x_upper == x_lower ? DEVICE_UNSUPPORTED_COMMAND : DEVICE_OK; +} diff --git a/DeviceAdapters/PyDevice/PyStage.h b/DeviceAdapters/PyDevice/PyStage.h new file mode 100644 index 000000000..dc0fa9430 --- /dev/null +++ b/DeviceAdapters/PyDevice/PyStage.h @@ -0,0 +1,127 @@ +#pragma once +#include "PyDevice.h" + +using PyStageClass = CPyDeviceTemplate>; +class CPyStage : public PyStageClass { +public: + CPyStage(const string& id) : PyStageClass(id) {} + int Home() override; + int GetPositionUm(double& pos) override; + int SetPositionUm(double pos) override; + int SetPositionSteps(long steps) override { + return SetPositionUm(steps * StepSizeUm()); + } + int SetRelativePositionUm(double dpos) override; + int GetPositionSteps(long& steps) override { + double pos; + _check_(GetPositionUm(pos)); + steps = std::lround((pos + origin_) / StepSizeUm()); + return CheckError(); + } + int Stop() override; + int SetOrigin() override; + int SetAdapterOriginUm(double value) override { + origin_ = value; + return DEVICE_OK; + } + int GetLimits(double& lower, double& upper) override; + int IsStageSequenceable(bool& value) const override { + value = false; + return DEVICE_OK; + } + bool IsContinuousFocusDrive() const override { + return true; + } +protected: + double StepSizeUm() const; + double origin_ = 0.0; + double set_pos_ = NAN; + PyObj home_; +}; + + + +// note: we don't derive from CXYStageBase, because we don't use that functionality and prefer to keep track of position in micrometers. +using PyXYStageClass = CPyDeviceTemplate>; +class CPyXYStage : public PyXYStageClass { +public: + CPyXYStage(const string& id) : PyXYStageClass(id) {} + int Home() override; + int GetPositionUm(double& x, double& y) override; + int SetPositionUm(double x, double y) override; + int SetPositionSteps(long x, long y) override { + return SetPositionUm((x + origin_x_) * GetStepSizeXUm(), (y + origin_y_) * GetStepSizeYUm()); + } + int SetRelativePositionUm(double dx, double dy) override; + int SetRelativePositionSteps(long dx, long dy) override { + return SetRelativePositionUm(dx * GetStepSizeXUm(), dy * GetStepSizeYUm()); + } + int SetOrigin() override; + int SetXOrigin() override; + int SetYOrigin() override; + int SetAdapterOriginUm(double x, double y) override { + origin_x_ = x; + origin_y_ = y; + return DEVICE_OK; + } + int GetLimitsUm(double& x_lower, double& x_upper, double& y_lower, double& y_upper) override; + int GetStepLimits(long& xMin, long& xMax, long& yMin, long& yMax) override { + double x_lower, x_upper, y_lower, y_upper; + _check_(GetLimitsUm(x_lower, x_upper, y_lower, y_upper)); + double x_step = GetStepSizeXUm(); + double y_step = GetStepSizeXUm(); + xMin = std::lround((x_lower + origin_x_) / x_step); + xMax = std::lround((x_lower + origin_x_) / x_step); + yMin = std::lround((y_lower + origin_y_) / y_step); + yMax = std::lround((y_lower + origin_y_) / y_step); + return DEVICE_OK; + } + int GetPositionSteps(long& x_steps, long& y_steps) override { + double x, y; + _check_(GetPositionUm(x, y)); + x_steps = std::lround((x + origin_x_) / GetStepSizeXUm()); + y_steps = std::lround((y + origin_y_) / GetStepSizeYUm()); + return CheckError(); + } + int Stop() override; + int IsXYStageSequenceable(bool& value) const override { + value = false; + return DEVICE_OK; + } + double GetStepSizeXUm() override; + double GetStepSizeYUm() override; + int Move(double /*x_velocity*/, double /*y_velocity*/) override { + return DEVICE_UNSUPPORTED_COMMAND; + } + + int GetXYStageSequenceMaxLength(long& /*nrEvents*/) const override { + return DEVICE_UNSUPPORTED_COMMAND; + } + + int StartXYStageSequence() override { + return DEVICE_UNSUPPORTED_COMMAND; + } + + int StopXYStageSequence() override { + return DEVICE_UNSUPPORTED_COMMAND; + } + + int ClearXYStageSequence() override { + return DEVICE_UNSUPPORTED_COMMAND; + } + + int AddToXYStageSequence(double /*positionX*/, double /*positionY*/) override { + return DEVICE_UNSUPPORTED_COMMAND; + } + + int SendXYStageSequence() override { + return DEVICE_UNSUPPORTED_COMMAND; + } + +protected: + double origin_x_ = 0.0; + double origin_y_ = 0.0; + double set_pos_x_ = NAN; + double set_pos_y_ = NAN; + PyObj home_; +}; \ No newline at end of file diff --git a/DeviceAdapters/PyDevice/README.md b/DeviceAdapters/PyDevice/README.md new file mode 100644 index 000000000..98d48702d --- /dev/null +++ b/DeviceAdapters/PyDevice/README.md @@ -0,0 +1,327 @@ + +| | | +|:------------|:--------------------------------------------------------------------| +| **Summary** | Device adapter for the integration of Python objects in MicroManager | +| **Author** | Ivo Vellekoop and Jeroen Doornbos | +| **License** | BSD-3 | +| **Devices** | Stage, XYStage, Camera, Generic device | + + +# PyDevice +PyDevice is a Micro-Manager device adapter that imports objects from a Python script, and integrates them into MicroManager as devices (e.g. a camera or a stage). This integration enables the use of Python scripts to control microscope hardware, without requiring any programming experience from the end user. Essentially, PyDevice acts as a translator, allowing Python-developed objects to be used in MicroManager with almost no interfacing code required. + +PyDevice is currently **experimental**. It may still change significantly, and it is not fully tested under all circumstances. Please feel free to try it out and provide feedback. + +## Requirements +To use PyDevice, the following software needs to be installed: + +* **Windows 10 or 11**. PyDevice was developed and tested in Windows. Although it _should_ work on Linux, this is not tested. Please contact the developers if you are interested in testing and using PyDevice in Linux. + +* **Python 3.9 or higher**. + +* **Micro-Manager 2.0** with the PyDevice device adapter installed. Until PyDevice is included in the nightly build (https://micro-manager.org/Micro-Manager_Nightly_Builds), it is still required to build the device adapter from source, as described below. + +## Getting started +1. PyDevice needs to be able to locate the Python runtime. It is recommended to set up a virtual environment for your project, as described in [Virtual environments](#virtual-environments). Alternatively, if PyDevice does not find a virtual environment, it will try to locate the global Python installation, which should be included in the system path. To verify that a global Python installation is configured correctly, type `python --version` in a command prompt. + +2. Create a script that defines a class with properties and methods that you want to use in Micro-Manager. For example, create a file `hello_device.py` with the following content: + +```python +class HelloDevice: + def __init__(self): + self._message = "Hello, World!" + + @property + def message(self) -> str: + return self._message + + @message.setter + def message(self, value): + self._message = str(value) + + +devices = {'hello': HelloDevice()} +``` + +3. In Micro-Manager, create a new hardware configuration. In the Hardware Configuration Wizard select `PyDevice->PyHub` from the list of devices and `Add` the device. + +4. There is no need to set the `PythonEnvironment` or `ScriptPath` properties. Instead, just press `Ok` to get a file browser dialog. Select the `hello_device.py` script you just created. This will execute the script and import all objects in the `devices` dictionary into Micro-Manager. You will now see a list of the objects that were successfully recognized, similar to the following screen: + +![camera_selection_screen](docs/camera_selection_screen.png) + +5. Select the device to add it to the configuration. Optionally, change the label from `Device:hello` to anything you like, and press `Ok` to close the dialog. + +6. Complete the rest of the wizard, add other devices if you want. Note, however, that there can only be a single PyHub device, and thus a single Python script, in a configuration. This script, however, can define as many devices as needed by adding them to the `devices` dictionary. + +If all went well, you should now see the device in the Device Property Browser, with one property `Message`, that you can modify. + +## How does it work? + +The PyDevice device adapter runs a Python interpreter in which the Python script is executed. It then looks for a dictionary called `devices`, and adds the objects in this dictionary as devices to Micro-Manager. + +To make a property available in Micro-Manager, it should be public (i. e., not `_`-prefixed), and declared with the `@property` decorator, as shown in the example. Moreover, the property getter should have a type annotation for the return value (`-> str`), so that PyDevice can determine the type of the property. PyDevice supports `str`, `int`, `float`, `bool`, as well as `Enum` types and floats with units (see [Advanced use](#advanced-use)). If you want to make the property writable, you should also define a setter method (`@message.setter` in the example). + +Note that the property name in the example was converted from `message` to `Message` in Micro-Manager to comply with the naming conventions in both Python and Micro-Manager. Also note that, except for the construction of the dictionary object, there is no special code to interact with Micro-Manager. The Python script can be used and tested independently of Micro-Manager. + + +## Other device types +It is also possible to define other device types. PyDevice will automatically detect the device type by examining the properties and methods present on the object. Currently, the following device types are supported: + +- `Camera`: requires the following properties and methods: + - `exposure_ms` (float): the exposure time in milliseconds + - `top` (int): the top coordinate of the region of interest + - `left` (int): the left coordinate of the region of interest + - `width` (int): the width of the region of interest + - `height` (int): the height of the region of interest + - `binning` (int): the binning factor. This property is optional, and defaults to 1 + - `read()` (method): acquire an image and return it as a numpy array, or as any object that implements the Python buffer protocol (such as a pytoch object). + - `busy()` (method): return `True` if the camera is busy acquiring an image + +- `Stage`: requires the following properties and methods: + - `position_um` (float): position of the stage in micrometer + - `step_size_um` (float): step size of the stage in micrometer + - `home()` (method): move the stage to the home position and reset the position to (0) + - `busy()` (method): return `True` if the stage is moving + +- `XYStage`: requires the following properties and methods: + - `x_um` (float): position of the stage in micrometer + - `y_um` (float): position of the stage in micrometer + - `step_size_x_um` (float): step size of the stage in micrometer + - `step_size_y_um` (float): step size of the stage in micrometer + - `home()` (method): move the stage to the home position and reset the position to (0, 0) + - `busy()` (method): return `True` if the stage is moving + +For examples of how to define devices, see the `examples` directory in the GitHub repository. + +## Virtual environments +It is considered good practice to use virtual environments to manage Python packages. A virtual environment acts as a stand-alone Python installation, with its own packages and dependencies. This way, you can have different versions of packages for different projects, without interfering with each other. There are several different tools to set up a virtual environment, such as [`venv`](https://docs.python.org/3/tutorial/venv.html), `poetry`, and `conda`. + +When creating a PyDevice object, the `PythonEnvironment` variable may be used to specify the path to the virtual environment that should be used. It should point to a folder that has a `pyvenv.cfg` file, which is the configuration file that is part of a Python virtual environment. For example, Poetry stores the virtual environments in `C:\Users\{username}\AppData\Local\pypoetry\Cache\virtualenvs\{virtual-environment-name}\Lib\site-packages`. + +To facilitate the use of virtual environments, the `PythonEnvironment` property is set to `(auto)` by default. In this case, PyDevice will look in the parent folders of the loaded script for a `venv` or `.venv` directory. If this directory is found and contains a `pyvenv.cfg` file, this virtual environment is used for running the Python code in the device script. + +If no virtual environment is found, the base Python installation of the system is used. To locate this install, first the `PYTHONHOME` environment variable is checked, and if that is not set, the system path is searched for the `python3.dll` file. Although using the system-wide Python installation can be convenient, it may lead to conflicts with other packages that are installed in the base Python installation. Therefore, it is recommended to use a virtual environment. + +## Use with `openwfs` +PyDevice was developed to integrate seamlessly with our [OpenWFS](https://openwfs.readthedocs.io) package for controlling wavefront shaping experiments. This package provides a high-level interface to control spatial light modulators, cameras, and other hardware. For example, it includes code to control a **_scanning multiphoton microscope_** through a NI-daq data acquisition card. This code integrates directly as a `Camera` device in Micro-Manager. In addition, it includes code to **_simulate a motorized microscope_**, which allows you to try out Micro-Manager and PyDevice without any hardware. The code for these devices is available in the `openwfs` package, which can be installed with `pip install openwfs`. + + +## Use in `pymmcore` +PyDevice can be used in [pymmcore](https://github.com/micro-manager/pymmcore) just as any other device adapter. For example, to load a configuration file `camera.cfg` that contains the definition of a camera device, and acquire an image, you can use the following code: + +```python +import pymmcore +mmc = pymmcore.CMMCore() +mmc.setDeviceAdapterSearchPaths(["C:/Program Files/Micro-Manager-2.0/"]) +mmc.loadSystemConfiguration("camera.cfg") +mmc.setProperty("cam", "Width", 121) +mmc.setProperty("cam", "Height", 333) +mmc.snapImage() +frame = mmc.getImage() +``` + +Using `pymmcore`, you can even debug the Python code, by simply setting a breakpoint in the code implementing the Device (tested in PyCharm 2024 with Conda 22.9 (Python 3.9)). This is actually quite remarkable as the device is running in a separate Python interpreter, and the debugger is attached to the main Python interpreter. + + +## Advanced use +### astropy.units +Some of the properties that PyDevice expects are specified in milliseconds or micrometers (indicated by the `_ms` or `_um` suffix). As an alternative to using plain floats, you can use the `astropy.units` package to specify the units of the property. For example, the `exposure_ms` property in the `Camera` device can be defined as follows: + +```python +from astropy import units as u + +class Camera: + def __init__(self): + self._exposure = 10 * u.ms + + @property + def exposure(self) -> u.Quantity[u.ms]: + return self._exposure * u.ms + + @exposure.setter + def exposure(self, value): + self._exposure = value.to(u.ms) + + ... +``` +PyDevice detects properties of type `astropy.units.Quantity` and automatically postfixes the unit in Micro-Manager (e. g., `Exposure-ms`). Currently, the following units are recognized: s, ms, us, ns, m, cm, mm, um, nm, A, mA, uA, V, mV, uV, Hz, kHz, MHz, GHz. One of the benefit of using this approach is that the user can specify the exposure time in the units they prefer (e.g. `camera.exposure = 1 * u.s`), with astropy taking care of the unit conversion. + +### Enum types +PyDevice supports properties with Enum types. For example, the following code defines a property `color` with an Enum type: + +```python +from enum import Enum +class Color(Enum): + Orange = 1 + Red = 2 + Blue = 3 + +class DeviceWithColor: + + def __init__(self, color): + self._color = color + + @property + def color(self) -> Color: + return self._color + + @color.setter + def options(self, value: Color): + self._color = Color(value) + + ... +``` +PyDevice will detect that the `color` property returns an Enum, and create a property `Color` in Micro-Manager with the options `Orange`, `Red`, and `Blue` shown in a drop-down box as shown below + + ![example device full](docs/example_device_full.png) + + +### Actions +`Camera` objects define a method to read a frame, and `Stage` and `XYStage` devices define methods to home the stage. These actions are started by Micro-Manager. Currently, there is no direct support for user-defined actions that can be started from the GUI. As a work-around, one can define a property that triggers an action when set. For example, the following code defines a property `capture` that triggers the `read` method of the `Camera` object: + +```python +from enum import Enum +class Action(Enum): + Idle = 0 + Do_A = 1 + Do_B = 2 + Do_C = 3 + +class DeviceWithActions: + @property + def action(self) -> Action: + return Action.Idle + + @action.setter + def options(self, value): + if value == Action.Do_A: + self.do_A() + elif value == Action.Do_B: + self.do_B() + elif value == Action.Do_C: + self.do_C() + + ... +``` +The user can now select an action from a drop-down box in Micro-Manager to activate the device. + + + +### Specifying ranges (experimental) +PyDevice supports specifying a range for integer and float properties using the `Annotated` type hint and the `Ge`, `Le` and `Interval` classes defined in the `annotated_types` package. For example, the type hint + `Annotated[int, Ge(1), Le(42)}]` specifies that the property holds an integer between 1 and 42 inclusive. If both upper and lower limits are set, the Micro-Manager GUI displays a slider that the user can adjust the value with. + +```python +from typing import Annotated +from annotated_types import Interval + +class DeviceWithInteger: + def __init__(self): + self._integer = 0 + + @property + def integer(self) -> Annotated[int, Interval(ge=0,le=0)]: + return self._integer + + @integer.setter + def integer(self, value): + self._integer = int(value) +``` + +### Using attributes instead of properties (experimental) +It is now possible to use attributes instead of properties. This can be useful when the property does not require any additional logic in the getter or setter. For example, the following code defines a device with an attribute `value`: + +```python +class DeviceDirect: + value: float + def __init__(self): + self.value = 0.0 +``` +Just as when using properties, the attribute should be public and have an appropriate type hint. + +## Known limitations +* PyDevice was developed and tested on Windows. If you are interested in porting the plugin to Linux, please contact the developers. +* It is not yet possible to link an action to a push button in the GUI. +* Only a single PyHub device can be active at a time. If you want to combine multiple Python devices, just create a single Python scripts that collects all devices in a single `devices` dictionary. +* Inheriting property definitions from a base class may work, but this aspect is not fully tested yet. + +## Troubleshooting +* **The plugin does not show up in Micro-Manager.** This may be because you have a version of Micro-Manager that does not have PyDevice included yet. + +* **The PyDevice plugin is shown in the list of device adapters, but it grayed out and cannot be loaded**. If you built PyDevice yourself, this problem can be caused by a version difference between MicroManager executable and the source code used to build PyDevice. + +* **The plugin crashes when it tries to load a script.** To find out what is the exact problem, enable logging in Micro-Manager and examine the core log file. The most likely cause is a problem in initializing the Python runtime. Unfortunately, in the case of an initialization problem, the Python runtime terminates the program instead of reporting an error. One of the causes may be an incorrect configuration of the system path or the `PYTHONHOME` environment variable. Try setting up a virtual environment as described above. + +* **My device script is recognized as `Device` instead of a `Camera`, `Stage`, etc.**. PyDevice determines the device type by examining the properties and methods of the object. If the object has all the required properties and methods of a `Camera`, it will be recognized as such. If the object is recognized as a `Device`, it means that one or more of the required properties or methods are missing. Check the spelling of the property names, and make sure that the property getters have the correct type annotation for the return value. For troubleshooting, you can construct a `PyDevice` wrapper object in Python, using the definition in `bootstrap.py`, which is included in the GitHub repository. See `tests_reflection.py` for an example. + +* **A property does not show up in MicroManager**. This is typically caused by a missing type annotation. Also note that properties should have an explicit 'getter', in the form of a `@property` decorator, see examples above. + +* **An error occurs when reading a property set to None**. Currently, MicroManager does not support missing values for properties. Instead, if `None` is found in a float property, it is silently converted to `nan`. For other property types, if the getter returns `None`, an error is given. + +* * ****. Python does not enforce correct data types. This means that a property that is declaredw with a `-> int` type hint, may just as well return a `float`, `None`, or any other object. PyDevice will try to convert the returned value to the expected type, but if this fails, a `TypeError` is raised. To prevent this, make sure that the property holds a value of the correct type, which can be done by explictly converting a value to the correct type using the property setters, (e.g. `self._value = int(value)`, see examples above). + + + + +## Building from source code +First, install the following prerequisites + +* **Visual Studio with C++ build tools**. + You can download the free Visual Studio Community edition + here . Make sure to include the c++ build tools during + installation. +* **Python 3**. + To build PyDevice, the file `python39.lib` is needed. This file should be located in the folder `3rdpartypublic/Python/cp39-win_amd64libs/libs`, where `3rdpartypublic` is located in the parent folder of the parent folder of `mmCoreAndDevices`. For example, if the repository is located in `c:\git\mmCoreAndDevices`, we expect to find the file at `c:\3rdpartypublic\Python\cp39-win_amd64libs\libs\python39.lib`. This file can be copied from the installation directory of a Python 3.9 installation (e. g. ). + +* **Micro-Manager 2.0** + You can download the latest version (nightly build) here: https://micro-manager.org/Micro-Manager_Nightly_Builds. Alternatively, you can build the micro-manager application from source, or use an older, stable, version. Note that Micro-Manager only recognizes plugins with the correct internal version number, so if the Mirco-Manager version is too old, it will not recognize the plugin. + +### Building the plugin + +1. Open the solution file `mmCoreAndDevices/Micro-Manager.sln` in Visual Studio + If asked to convert to a newer version, press cancel. +2. Not all plugins will build correctly. To build just the PyDevice plugin, right-click the PyDevice in the Solution + Explorer and select `build`. + +### Implementation of loading the Python runtime +By far the hardest part of developing PyDevice was starting the Python runtime. Starting a virtual environment from c++ code is not trivial, and the process is poorly documented and extremely complex (a partial documentation of how the Python runtime locates dependencies can be found [here](https://github.com/python/cpython/blob/main/Modules/getpath.py)). The complication is, in part, caused by the following: + +* c++ code that interfaces with Python should load the Python runtime. The _standard_ way to do this is to link the code with an import library, which causes the Python runtime to be loaded when the c++ code is loaded. The caveats here are: + 1) the exact version of Python needs to be known at compile time. This means that if the plugin is built using Python 3.10, it will not work with Python 3.9, or Python 3.10 or any other Python version on the system of the user. + 2) The OS should be able to locate the runtime dll for exactly that version, or it should be distributed along with the device adapter. + 3) If the Python runtime cannot be found, the plugin fails to load altogether, without any error message, making it hard for the user to find out what is going on. + +* In an attempt to mitigate these issues, Python defines a 'stable' API that allows linking against a generic `python3.dll` file, which then loads the runtime that is available on the system. This approach, however, has many problems on its own: + 1) With this approach, the plugin always loads the Python version that is present on the system path. There is no way to configure it to use a different Python version. So, a global Python installation must be available and there is no option to choose a different Python version (e.g. when using a virtual environment). + 2) The 'stable' API is not very stable. In particular, the way the Python runtime is initialized is now deprecated, with no stable alternative provided. Therefore, like most projects, PyDevice uses the deprecated functions, which still work. The drawback of these functions is that the Python runtime does not report an error when initialization fails. Instead, it terminates (crashes) the program. + 3) The 'stable' API does not support the buffer protocol for all Python versions, meaning that it is not possible to pass arrays to Python code except when explicitly relying on the `numpy` C API, which is very sensitive to a correct configuration of the Python paths and Python version, and version of the C-API (see [here](https://numpy.org/devdocs/user/troubleshooting-importerror.html)). + +To work around all the issues above, PyDevice imports the python runtime dynamically (known as delay loading). This way, the Python runtime is loaded only when the plugin is loaded, and the user can specify which version of Python to use exactly. Here are the steps that PyDevice takes to load the runtime: + +1) First, the `PythonEnvironment` is used to locate the correct virtual environment or global installation. +2) The `home` entry in the `pyvenv.cfg` configuration file in that virtual environment is then used to locate the Python runtime, allowing the correct version of Python to be used. +3) The `pythonXX.dll` runtime from that location is loaded dynamically using `LoadLibrary`. + +To facilitate delay-loading the dll, the `DELAYLOAD` support of Visual Studio is used. This way, there is almost no overhead in the c++ code. Instead of using `Python.h`, a list of function signatures for the stable API is included in `stable.h`, and functions for the buffer protocol are included in `buffer.h`. Have a look at `stable.cpp` to see how the Python runtime is loaded. For this automatic delay loading to work, the linker needs access to the file `python39.lib`. Note that this file is not actually used for linking anything, only to provide the linker with the information about which functions are present in the delay-loaded Python dll. Therefore, the result will not depend on the version of Python that was used to build the plugin. + +### Debugging + +To debug the plugin in Micro-Manager, select a `Debug` build configuration in Visual Studio. In addition, right-click +PyDevice in the solution explorer, and `Set as Startup Project`. Right-click again and edit the PyDevice project +Properties, under Debugging, fill in the following settings: + +| | | +|-------------------|---------------------------------------------------------| +| Command | $(ProjectDir)/debug_helper.bat | +| Command Arguments | $(TargetPath) | +| Environment | MM_EXECUTABLE_PATH="C:\Program Files\Micro-Manager-2.0" | + +Here, adjust the `MM_EXECUTABLE_PATH` if you installed Micro-Manager somewhere else. Finally, in the menu select `Debug->Other Debug Targets->Child Process Debug Settings...` and tick `Enable child process debugging`. +You can now press F5 to build and install the plugin, and start Micro-Manager with the debugger attached. Note: you can safely ignore the segmentation faults occurring in the Java virtual machine. + +For device scripts, we recommend debugging and testing the scripts outside Micro-Manager first. The scripts should simply as standalone Python scripts, since they do not contain any Micro-Manager specific code. This way, you can test the scripts in an environment that is easier to debug and test. After testing the devices, you can also load them in `pymmcore` to test the interaction with Micro-Manager while maintaining the ability to debug the Python code. + +Finally, it is also possible to debug the c++ code while running pymmcore. To do so, attach the debugger to your Python IDE (e.g. PyCharm). This way, you can set breakpoints in the c++ code and inspect the state of the program while running pymmcore. + +When debugging PyDevice, be aware that any changes to the c++ code, or to `bootstrap.py`, require the code to be re-compiled and the device adapter to be installed again (which is done automatically by the debug helper). At the moment, Visual Studio does not detect changes in `bootstrap.py` and does not recompile the plugin when this file is changed. Therefore, you need to manually rebuild the device adapter in Visual Studio to see the changes. + diff --git a/DeviceAdapters/PyDevice/bootstrap.py b/DeviceAdapters/PyDevice/bootstrap.py new file mode 100644 index 000000000..5f50a0259 --- /dev/null +++ b/DeviceAdapters/PyDevice/bootstrap.py @@ -0,0 +1,241 @@ +bootstrap = R"raw(" +# This file is formatted so that it can be run by the Python interpreter directly, and that it can also be included +# in the c++ code. +# See RunScript() in PyDevice.cpp for the code that loads this script as a c++ string. +import sys +import importlib +from typing import get_origin, Annotated +from types import MethodType +import traceback +import inspect +from enum import Enum + + +# the presence of the astropy.units and annotated_types modules is optional. +class Dummy: + pass + + +try: + from astropy.units import UnitBase + + has_astropy = True +except ImportError: + UnitBase = Dummy + has_astropy = False + +try: + from annotated_types import Ge, Le, Gt, Lt, Interval, Unit as AnnotatedUnit + + has_annotated_types = True +except ImportError: + Ge = Le = Gt = Lt = Interval = AnnotatedUnit = Dummy + has_annotated_types = False + + +def traceback_to_string(tb): + """Helper function to convert a traceback object to a string. This function is called from the C++ code.""" + return ''.join(traceback.format_tb(tb)) + + +def load_devices(module_path, script_path, script_name) -> dict: + """Helper function to load the Python script that defines the devices. This function is called from the C++ code.""" + _original_path = sys.path.copy() + try: + sys.path = [*_original_path, *module_path.split(';'), script_path] + script = importlib.import_module(script_name) + return {k: PyDevice(v) for k, v in script.devices.items()} + finally: + sys.path = _original_path + + +def _to_title_case(name: str) -> str: + for suffix in ['_s', '_ms', '_us', '_ns', '_m', '_cm', '_mm', '_um', '_nm', '_A', '_mA', '_uA', '_V', '_mV', '_uV', + '_Hz', '_kHz', '_MHz', '_GHz']: + if name.endswith(suffix): + return name[:-len(suffix)].title().replace('_', '') + '-' + suffix[1:] + + return name.title().replace('_', '') # convert to TitleCase + + +class PyProperty: + def __init__(self, obj, name: str, p: property): + """Extracts metadata from a property object and stores it in a format accessible to the C++ code. + + Recognizes properties, as well as attributes. + """ + self.python_name = name + self.mm_name = _to_title_case(name) + self.min = None + self.max = None + self.options = None + self.unit = None + + if isinstance(p, property): # property + fset = getattr(p, 'fset', None) + self.get = lambda: p.fget(obj) + self.set = (lambda value: fset(obj, value)) if fset is not None else None + # get the return type from the annotations. + # Note: this will raise an error if p is not gettable or has no return type annotation + return_type = p.fget.__annotations__['return'] # noqa: ok to raise an error here + else: # attribute + self.get = lambda: getattr(obj, name) + self.set = lambda value: setattr(obj, name, value) + return_type = p + + self.data_type = self._process_return_type(return_type) + + def _process_return_type(self, return_type) -> str: + if get_origin(return_type) is Annotated: + return_type = self._process_annotations(return_type) + + if not inspect.isclass(return_type): # for example, Optional[...] + raise ValueError(f"Unsupported property type: {return_type}") + elif return_type == bool: + return 'bool' + elif issubclass(return_type, int): + return 'int' + elif issubclass(return_type, float): + return 'float' + elif issubclass(return_type, str): + return 'str' + elif issubclass(return_type, Enum): # Enum + self.options = {k.lower().capitalize(): v for (k, v) in return_type.__members__.items()} + return 'enum' + else: + raise ValueError(return_type) # unsupported property type + + def _process_annotations(self, return_type): + # Process annotations for range and unit + annotations = return_type.__metadata__ + return_type = return_type.__origin__ # remove the Annotated wrapper + + for meta in annotations: + if has_annotated_types: + if isinstance(meta, Ge): + self.min = meta.ge + continue + elif isinstance(meta, Le): + self.max = meta.le + continue + elif isinstance(meta, Interval): + self.min = meta.ge + self.max = meta.le + continue + elif isinstance(meta, AnnotatedUnit): + self.unit = meta.unit + self.mm_name = self.mm_name + '-' + self.unit + continue + if has_astropy and isinstance(meta, UnitBase): + # for astropy units, create wrapper functions to convert between float and quantity + self.unit = str(meta) + self.mm_name = self.mm_name + '-' + self.unit + unit = meta # note: this copy is needed so that the lambda's capture the current value of meta + if self.set is not None: + set_original = self.set + self.set = lambda value: set_original(value * unit) + get_original = self.get + self.get = lambda: get_original().to_value(unit) + return_type = float + continue + return return_type # updated return type + + def __str__(self): + return f"{self.mm_name}({self.data_type}{', readonly' if self.set is None else ''})" + + +class PyDevice: + """Wrapper class for a device that provides metadata about the device's properties + and methods in a format accessible to the C++ code.""" + + def __init__(self, device): + # get a list of all properties and methods, including the ones in base classes + # Also process class annotations, for attributes that are not properties + class_hierarchy = inspect.getmro(device.__class__) + all_dict = {} + for c in class_hierarchy[::-1]: + all_dict.update(c.__dict__) + annotations = c.__dict__.get('__annotations__', {}) + all_dict.update(annotations) + + # extract metadata for each property and method + self.properties = [] + self.methods = {} + for name, p in all_dict.items(): + if not name.startswith('_'): + if inspect.isfunction(p): + self.methods[name] = MethodType(p, device) + else: + try: + self.properties.append(PyProperty(device, name, p)) + except (AttributeError, KeyError, ValueError): + pass # property does not have a getter or annotations, or is of an incorrect type + + # detect the device type + if self._init_camera(): + self.device_type = 'Camera' + elif self._init_xy_stage(): + self.device_type = 'XYStage' + elif self._init_stage(): + self.device_type = 'Stage' + else: + self.device_type = 'Device' + + # The 'busy' method is optional + + def _init_camera(self) -> bool: + """Checks if the device corresponds to a Camera, and prepares the camera object if it does""" + + # check required properties and methods + if not self._has_properties(('Exposure-ms', 'float'), ('Top', 'int'), ('Left', 'int'), ('Height', 'int'), + ('Width', 'int')) or not self._has_methods('read'): + return False + + # add a read-only binning property if it does not exist + if not self._has_properties(('Binning', 'int')): + @property + def binning(obj) -> int: + return 1 + + self.properties.append(PyProperty(self, 'binning', binning)) + + return True + + def _init_xy_stage(self) -> bool: + """Checks if the device corresponds to an XYStage, and prepares the stage object if it does""" + return self._has_properties(('X-um', 'float'), ('Y-um', 'float'), ('StepSizeX-um', 'float'), + ('StepSizeY-um', 'float')) and self._has_methods('home') + + def _init_stage(self) -> bool: + """Checks if the device corresponds to an XYStage, and prepares the stage object if it does""" + return self._has_properties(('Position-um', 'float'), ('StepSize-um', 'float')) and self._has_methods('home') + + def _has_properties(self, *properties) -> bool: + """Checks if the device has the specified properties of the specified types + + Returns: + bool: True if the device has the specified properties, False otherwise + + Raises: + ValueError: if the device has one of the specified properties, but with a different type + """ + for name, data_type in properties: + # find the property with the specified name + p = next((p for p in self.properties if p.mm_name == name), None) + if p is None: + return False + if p.data_type != data_type: + raise ValueError(f"Property '{name}' is of type {p.data_type}, expected {data_type}") + return True + + def _has_methods(self, *required_methods) -> bool: + """Checks if the device has the specified methods + + Returns: + bool: True if the device has the specified methods, False otherwise + """ + return all(m in self.methods.keys() for m in required_methods) + + def __str__(self): + return f"{self.device_type}({', '.join(str(p) for p in self.properties)})" +# )raw"; diff --git a/DeviceAdapters/PyDevice/buffer.h b/DeviceAdapters/PyDevice/buffer.h new file mode 100644 index 000000000..bb2236b71 --- /dev/null +++ b/DeviceAdapters/PyDevice/buffer.h @@ -0,0 +1,70 @@ +#pragma once +#include "stable.h" + +struct Py_buffer { + void* buf; + PyObject* obj; /* owned reference */ + Py_ssize_t len; + Py_ssize_t itemsize; /* This is Py_ssize_t so it can be + pointed to by strides in simple case.*/ + int readonly; + int ndim; + char* format; + Py_ssize_t* shape; + Py_ssize_t* strides; + Py_ssize_t* suboffsets; + void* internal; +}; + +extern "C" { + int PyObject_CheckBuffer(PyObject* obj); + int PyObject_GetBuffer(PyObject* exporter, Py_buffer* view, int flags); + void PyBuffer_Release(Py_buffer* view); + Py_ssize_t PyBuffer_SizeFromFormat(const char* format); + int PyBuffer_IsContiguous(const Py_buffer* view, char order); + void* PyBuffer_GetPointer(const Py_buffer* view, const Py_ssize_t* indices); + int PyBuffer_FromContiguous(const Py_buffer* view, const void* buf, Py_ssize_t len, char fort); + int PyBuffer_ToContiguous(void* buf, const Py_buffer* src, Py_ssize_t len, char order); + int PyObject_CopyData(PyObject* dest, PyObject* src); + void PyBuffer_FillContiguousStrides(int ndims, Py_ssize_t* shape, Py_ssize_t* strides, int itemsize, char order); + int PyBuffer_FillInfo(Py_buffer* view, PyObject* exporter, void* buf, Py_ssize_t len, int readonly, int flags); +} + + +typedef int (*getbufferproc)(PyObject*, Py_buffer*, int); +typedef void (*releasebufferproc)(PyObject*, Py_buffer*); + +typedef PyObject* (*vectorcallfunc)(PyObject* callable, PyObject* const* args, + size_t nargsf, PyObject* kwnames); + +/* Maximum number of dimensions */ +#define PyBUF_MAX_NDIM 64 + +/* Flags for getting buffers */ +#define PyBUF_SIMPLE 0 +#define PyBUF_WRITABLE 0x0001 +/* we used to include an E, backwards compatible alias */ +#define PyBUF_WRITEABLE PyBUF_WRITABLE +#define PyBUF_FORMAT 0x0004 +#define PyBUF_ND 0x0008 +#define PyBUF_STRIDES (0x0010 | PyBUF_ND) +#define PyBUF_C_CONTIGUOUS (0x0020 | PyBUF_STRIDES) +#define PyBUF_F_CONTIGUOUS (0x0040 | PyBUF_STRIDES) +#define PyBUF_ANY_CONTIGUOUS (0x0080 | PyBUF_STRIDES) +#define PyBUF_INDIRECT (0x0100 | PyBUF_STRIDES) + +#define PyBUF_CONTIG (PyBUF_ND | PyBUF_WRITABLE) +#define PyBUF_CONTIG_RO (PyBUF_ND) + +#define PyBUF_STRIDED (PyBUF_STRIDES | PyBUF_WRITABLE) +#define PyBUF_STRIDED_RO (PyBUF_STRIDES) + +#define PyBUF_RECORDS (PyBUF_STRIDES | PyBUF_WRITABLE | PyBUF_FORMAT) +#define PyBUF_RECORDS_RO (PyBUF_STRIDES | PyBUF_FORMAT) + +#define PyBUF_FULL (PyBUF_INDIRECT | PyBUF_WRITABLE | PyBUF_FORMAT) +#define PyBUF_FULL_RO (PyBUF_INDIRECT | PyBUF_FORMAT) + + +#define PyBUF_READ 0x100 +#define PyBUF_WRITE 0x200 diff --git a/DeviceAdapters/PyDevice/debug_helper.bat b/DeviceAdapters/PyDevice/debug_helper.bat new file mode 100644 index 000000000..89beb4d54 --- /dev/null +++ b/DeviceAdapters/PyDevice/debug_helper.bat @@ -0,0 +1,3 @@ +copy %1 %MM_EXECUTABLE_PATH% +cd %MM_EXECUTABLE_PATH% +start ImageJ.exe diff --git a/DeviceAdapters/PyDevice/docs/camera_selection_screen.png b/DeviceAdapters/PyDevice/docs/camera_selection_screen.png new file mode 100644 index 000000000..6736afacc Binary files /dev/null and b/DeviceAdapters/PyDevice/docs/camera_selection_screen.png differ diff --git a/DeviceAdapters/PyDevice/docs/config_manager_screen.png b/DeviceAdapters/PyDevice/docs/config_manager_screen.png new file mode 100644 index 000000000..6c68a1b75 Binary files /dev/null and b/DeviceAdapters/PyDevice/docs/config_manager_screen.png differ diff --git a/DeviceAdapters/PyDevice/docs/example_device_full.png b/DeviceAdapters/PyDevice/docs/example_device_full.png new file mode 100644 index 000000000..73ec606f8 Binary files /dev/null and b/DeviceAdapters/PyDevice/docs/example_device_full.png differ diff --git a/DeviceAdapters/PyDevice/examples/camera.py b/DeviceAdapters/PyDevice/examples/camera.py new file mode 100644 index 000000000..335409fa7 --- /dev/null +++ b/DeviceAdapters/PyDevice/examples/camera.py @@ -0,0 +1,119 @@ +import numpy as np +from enum import Enum + + +# Note: this example requires numpy to be installed + +class NoiseType(Enum): + UNIFORM = 1 + EXPONENTIAL = 2 + GAUSSIAN = 3 + + +class Camera: + """Demo camera implementation that returns noise images. To test building device graphs, the random number + generator is implemented as a separate object with its own properties.""" + + def __init__(self, left=0, top=0, width=100, height=100): + self._rng = np.random.default_rng() + self._low = 1.0 + self._high = 1000.0 + self._left = left + self._top = top + self._width = width + self._height = height + self._exposure_ms = 1 + self._noise_type = NoiseType.UNIFORM + + def read(self): + size = (self._height, self._width) + if self._noise_type == NoiseType.UNIFORM: + image = self._rng.uniform(self._low, self._high, size) + elif self._noise_type == NoiseType.EXPONENTIAL: + image = self._rng.exponential(self._high - self._low, size) + self._low + else: + mean = 0.5 * (self._high + self._low) + std = 0.5 * (self._high - self._low) + image = self._rng.normal(mean, std, size) + return image.astype(np.uint16) + + def busy(self): + return False + + @property + def left(self) -> int: + return self._left + + @left.setter + def left(self, value: int): + self._left = value + + @property + def top(self) -> int: + return self._top + + @top.setter + def top(self, value: int): + self._top = value + + @property + def width(self) -> int: + return self._width + + @width.setter + def width(self, value: int): + self._width = value + + @property + def height(self) -> int: + return self._height + + @height.setter + def height(self, value: int): + self._height = value + + @property + def exposure_ms(self) -> float: + return self._exposure_ms + + @exposure_ms.setter + def exposure_ms(self, value): + self._exposure_ms = float(value) + + @property + def noise_type(self) -> NoiseType: + return self._noise_type + + @noise_type.setter + def noise_type(self, value: NoiseType): + self._noise_type = value + + @property + def low(self) -> float: + return self._low + + @low.setter + def low(self, value: float): + self._low = value + + @property + def high(self) -> float: + return self._high + + @high.setter + def high(self, value: float): + self._high = value + + +devices = {'cam': Camera()} + +if __name__ == "__main__": + import sys + import os + + sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + from bootstrap import PyDevice + + device = PyDevice(devices['cam']) + print(device) + assert device.device_type == 'Camera' diff --git a/DeviceAdapters/PyDevice/examples/device.py b/DeviceAdapters/PyDevice/examples/device.py new file mode 100644 index 000000000..af3152500 --- /dev/null +++ b/DeviceAdapters/PyDevice/examples/device.py @@ -0,0 +1,86 @@ +from enum import Enum +from typing import Annotated + +from annotated_types import Ge, Le + + +class Color(Enum): + Orange = 1 + Red = 2 + Blue = 3 + + +class GenericDevice: + + def __init__(self, color, floating_point, distance, boolean, integer, command): + self._color = color + self._floating_point = floating_point + self._distance_mm = distance + self._boolean = boolean + self._integer = integer + self._command = command + + @property + def color(self) -> Color: + return self._color + + @color.setter + def color(self, value): + self._color = value + + @property + def floating_point(self) -> Annotated[float, Ge(0.0), Le(1.0)]: + # setting a range, also sets this range in MicroManager + return self._floating_point + + @floating_point.setter + def floating_point(self, value): + self._floating_point = value + + @property + def distance_mm(self) -> float: + return self._distance_mm + + @distance_mm.setter + def distance_mm(self, value): + self._distance_mm = float(value) + + @property + def boolean(self) -> bool: + return self._boolean + + @boolean.setter + # setting value:bool forces the users to input the correct type. Optional. + def boolean(self, value: bool): + self._boolean = value + + @property + def integer(self) -> int: + return self._integer + + @integer.setter + def integer(self, value): + self._integer = value + + @property + def command(self) -> str: + return self._command + + @command.setter + def command(self, value): + self._command = str(value) + + +device = GenericDevice(Color.Blue, 23.7, 0.039, True, 4, 'Something') +devices = {'some_device': device} + +if __name__ == "__main__": + import sys + import os + + sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + from bootstrap import PyDevice + + device = PyDevice(devices['some_device']) + print(device) + assert device.device_type == 'Device' diff --git a/DeviceAdapters/PyDevice/examples/hello_device.py b/DeviceAdapters/PyDevice/examples/hello_device.py new file mode 100644 index 000000000..70d5bf69b --- /dev/null +++ b/DeviceAdapters/PyDevice/examples/hello_device.py @@ -0,0 +1,14 @@ +class HelloDevice: + def __init__(self): + self._message = "Hello, World!" + + @property + def message(self) -> str: + return self._message + + @message.setter + def message(self, value): + self._message = value + + +devices = {'hello': HelloDevice()} diff --git a/DeviceAdapters/PyDevice/examples/microscope.py b/DeviceAdapters/PyDevice/examples/microscope.py new file mode 100644 index 000000000..074ae3b18 --- /dev/null +++ b/DeviceAdapters/PyDevice/examples/microscope.py @@ -0,0 +1,72 @@ +""" Sample microscope +======================= +This script simulates a microscopic imaging system, generating a random noise image as a mock source and capturing it +through a microscope with adjustable magnification, numerical aperture, and wavelength. It visualizes the original and +processed images dynamically, demonstrating how changes in optical parameters affect image quality and resolution. +""" +import astropy.units as u +import numpy as np +from openwfs.plot_utilities import grab_and_show, imshow +from openwfs.simulation import Microscope, StaticSource, ADCProcessor +from openwfs.utilities import set_pixel_size + +# Parameters that can be altered +img_size_x = 1024 +# Determines how wide the image is. + +img_size_y = 1024 +# Determines how high the image is. + +magnification = 40 +# magnification from object plane to camera. + +numerical_aperture = 0.85 +# numerical aperture of the microscope objective + +wavelength = 532.8 * u.nm +# wavelength of the light, different wavelengths are possible, units can be adjusted accordingly. + +pixel_size = 6.45 * u.um +# Size of the pixels on the camera + +camera_resolution = (256, 256) +# number of pixels on the camera + +p_limit = 100 +# Number of iterations. Influences how quick the 'animation' is complete. + +# Code +img = set_pixel_size( + np.maximum(np.random.randint(-10000, 100, (img_size_y, img_size_x), dtype=np.int16), 0), + 60 * u.nm) +src = StaticSource(img) +mic = Microscope(src, magnification=magnification, numerical_aperture=numerical_aperture, wavelength=wavelength) + +# simulate shot noise in an 8-bit camera with auto-exposure: +cam = mic.get_camera(shot_noise=True, digital_max=255, data_shape=camera_resolution, pixel_size=pixel_size) +stage = mic.xy_stage + +# todo: remove when fixed in openwfs +cam.__class__.exposure = cam.__class__.duration # old version of openwfs does not yet have exposure property. +cam.gaussian_noise_std = 0 # old version of openwfs has bug where gaussian noise was specified as integer. Make sure it actually _is_ an integer. +stage.__class__.step_size_x_um = float # old versiom of openwfs does not yet have step size properties. +stage.step_size_x_um = 0.001 +stage.__class__.step_size_y_um = float +stage.step_size_y_um = 0.001 + +# construct dictionary of objects to expose to Micro-Manager +devices = {'camera': cam, 'stage': stage} + +if __name__ == "__main__": + import sys + import os + + sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + from bootstrap import PyDevice + + c_device = PyDevice(devices['camera']) + print(c_device) + assert c_device.device_type == 'Camera' + s_device = PyDevice(devices['stage']) + print(s_device) + assert s_device.device_type == 'XYStage' diff --git a/DeviceAdapters/PyDevice/examples/units.py b/DeviceAdapters/PyDevice/examples/units.py new file mode 100644 index 000000000..f56773ac2 --- /dev/null +++ b/DeviceAdapters/PyDevice/examples/units.py @@ -0,0 +1,109 @@ +from typing import Annotated +from enum import Enum +import astropy.units as u +from annotated_types import Ge, Le, Gt, Lt, Interval + + +# Note: this example requires astropy and annotatedtypes to be installed + + +class Color(Enum): + Orange = 1 + Red = 2 + Blue = 3 + + +class DeviceWithUnits: + + def __init__(self, color, floating_point, distance, boolean, integer, integer2, voltage1, voltage2): + self._color = color + self._floating_point = floating_point + self._distance = distance + self._boolean = boolean + self._integer = integer + self._integer2 = integer2 + self._voltage1 = voltage1 + self._voltage2 = voltage2 + + @property + def color(self) -> Color: + return self._color + + @color.setter + def color(self, value): + self._color = value + + @property + def floating_point(self) -> float: + return self._floating_point + + @floating_point.setter + def floating_point(self, value): + self._floating_point = value + + @property + def distance(self) -> u.Quantity[u.mm]: + return self._distance + + @distance.setter + def distance(self, value): + self._distance = value.to(u.mm) + + @property + def boolean(self) -> bool: + return self._boolean + + @boolean.setter + # setting value:bool forces the users to input the correct type. Optional. + def boolean(self, value: bool): + self._boolean = value + + @property + # setting a range, also sets this range in MicroManager, also optional. + def integer(self) -> Annotated[int, Ge(1), Le(42)]: + return self._integer + + @integer.setter + def integer(self, value): + self._integer = value + + @property + # setting a range, also sets this range in MicroManager, also optional. + def integer2(self) -> Annotated[int, Gt(0), Lt(43)]: + return self._integer2 + + @integer2.setter + def integer2(self, value): + self._integer2 = value + + @property + def voltage1(self) -> u.Quantity[u.V]: + return self._voltage1 + + @voltage1.setter + def voltage1(self, value: u.Quantity[u.V]): + self._voltage1 = value.to(u.V) + + @property + def voltage2(self) -> Annotated[u.Quantity[u.V], Interval(ge=0 * u.V, le=0.9 * u.V)]: + return self._voltage2 + + @voltage2.setter + def voltage2(self, value: u.Quantity[u.V]): + self._voltage2 = value.to(u.V) + + +device = DeviceWithUnits(Color.Blue, 23.7, 0.039 * u.m, True, 4, 5, 0.5 * u.V, 0.6 * u.V) +devices = {'some_device': device} + +if __name__ == "__main__": + import sys + import os + + sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + from bootstrap import PyDevice + + device = PyDevice(devices['some_device']) + device.properties[-1].get() + print(device) + assert device.device_type == 'Device' diff --git a/DeviceAdapters/PyDevice/examples/xystage.py b/DeviceAdapters/PyDevice/examples/xystage.py new file mode 100644 index 000000000..56b7a300c --- /dev/null +++ b/DeviceAdapters/PyDevice/examples/xystage.py @@ -0,0 +1,66 @@ +from astropy.units import Quantity +import astropy.units as u + + +class GenericXYStage: + def __init__(self, step_size_x: Quantity[u.um], step_size_y: Quantity[u.um]): + super().__init__() + self._step_size_x = step_size_x.to(u.um) + self._step_size_y = step_size_y.to(u.um) + self._y = 0.0 * u.um + self._x = 0.0 * u.um + + def home(self): + self._x = 0.0 * u.um + self._y = 0.0 * u.um + + def busy(self): + return False + + @property + def x(self) -> Quantity[u.um]: + return self._x + + @x.setter + def x(self, value: Quantity[u.um]): + former = self._x + self._x = value.to(u.um) + former + + @property + def y(self) -> Quantity[u.um]: + return self._y + + @y.setter + def y(self, value: Quantity[u.um]): + former = self._y + self._y = value.to(u.um) + former + + @property + def step_size_x(self) -> Quantity[u.um]: + return self._step_size_x + + @step_size_x.setter + def step_size_x(self, value: Quantity[u.um]): + self._step_size_x = value.to(u.um) + + @property + def step_size_y(self) -> Quantity[u.um]: + return self._step_size_y + + @step_size_y.setter + def step_size_y(self, value: Quantity[u.um]): + self._step_size_y = value.to(u.um) + + +devices = {'stage': GenericXYStage(1 * u.um, 1 * u.um)} + +if __name__ == "__main__": + import sys + import os + + sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + from bootstrap import PyDevice + + device = PyDevice(devices['stage']) + print(device) + assert device.device_type == 'XYStage' diff --git a/DeviceAdapters/PyDevice/examples/zstage.py b/DeviceAdapters/PyDevice/examples/zstage.py new file mode 100644 index 000000000..1ca3c15eb --- /dev/null +++ b/DeviceAdapters/PyDevice/examples/zstage.py @@ -0,0 +1,47 @@ +from astropy.units import Quantity +import astropy.units as u + + +class GenericZStage: + def __init__(self, step_size: Quantity[u.um]): + super().__init__() + self._step_size = step_size.to(u.um) + + self._position = 0.0 * u.um + + def home(self): + self._position = 0.0 * u.um + + def busy(self): + return False + + @property + def position(self) -> Quantity[u.um]: + return self._position + + @position.setter + def position(self, value: Quantity[u.um]): + former = self._position + self._position = value.to(u.um) + former + + @property + def step_size(self) -> Quantity[u.um]: + return self._step_size + + @step_size.setter + def step_size(self, value: Quantity[u.um]): + self._step_size = value.to(u.um) + + +devices = {'stage': GenericZStage(1 * u.um)} + +if __name__ == "__main__": + import sys + import os + + sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + from bootstrap import PyDevice + + device = PyDevice(devices['stage']) + print(device) + assert device.device_type == 'Stage' diff --git a/DeviceAdapters/PyDevice/pch.cpp b/DeviceAdapters/PyDevice/pch.cpp new file mode 100644 index 000000000..17305716a --- /dev/null +++ b/DeviceAdapters/PyDevice/pch.cpp @@ -0,0 +1 @@ +#include "pch.h" \ No newline at end of file diff --git a/DeviceAdapters/PyDevice/pch.h b/DeviceAdapters/PyDevice/pch.h new file mode 100644 index 000000000..8c6c512ba --- /dev/null +++ b/DeviceAdapters/PyDevice/pch.h @@ -0,0 +1,47 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#pragma warning(disable: 5040) // disable warning we get because we are using C++17 for compilation. +#include // all MM includes +namespace fs = std::filesystem; +using std::string; +using std::function; +using std::vector; +using std::tuple; +using std::map; +#ifdef _WIN32 +#include +#include +#include +#endif +#include "stable.h" // stable Python C API + +/// Helper function for checking if a file exists +inline bool FileExists(const fs::path& path) noexcept { + std::ifstream test(path); + return test.good(); +} + +#define ERR_PYTHON_SCRIPT_NOT_FOUND 101 +#define ERR_PYTHON_NO_DEVICE_DICT 102 +#define ERR_PYTHON_RUNTIME_NOT_FOUND 103 +#define ERR_PYTHON_ONLY_ONE_HUB_ALLOWED 104 +#define ERR_PYTHON_EXCEPTION 105 +#define _check_(expression) if (auto result=(expression); result != DEVICE_OK) return result + +#include + +// note: we are not actually using python39.dll, only linking against its import library +// when executing, the delay load mechanism loads the correct dll (e.g. python311.dll) +// +#pragma comment(lib, "python39") +#pragma comment(lib, "delayimp") +#pragma comment(lib, "user32") diff --git a/DeviceAdapters/PyDevice/stable.cpp b/DeviceAdapters/PyDevice/stable.cpp new file mode 100644 index 000000000..3d086b624 --- /dev/null +++ b/DeviceAdapters/PyDevice/stable.cpp @@ -0,0 +1,188 @@ +#include "pch.h" + +HMODULE pythonDll = nullptr; // handle to loaded python3xx.dll +PyThreadState* g_threadState = nullptr; // pointer to the thread state + +const std::regex filenamePattern(R"(python3[0-9]+\.dll)", std::regex_constants::icase); +std::regex key_value(R"(home\s*=\s*(.+?)\s*$)"); + +PyObject* Py_None = nullptr; +PyObject* Py_True = nullptr; +PyObject* Py_False = nullptr; + + +/** + @brief Tries to open and parse pyvenv.cfg + + + When running in a virtual environment, we need to read the 'home' entry of the pyvenv.cfg configuration file, + since it points to the location of the pythonxx.dll that we need to load. + We have to load this dll before calling any Python API function, and we must load the correct one (there may be multiple python dlls, + and we don't know the version number in advance). +*/ +fs::path TryParsePyvenv(const fs::path& venv) noexcept +{ + auto configFile = std::ifstream(venv / "pyvenv.cfg"); + if (!configFile.is_open()) + return {}; // file not found or could not be opened + + // locate the `home = ...` value + string line; + while (std::getline(configFile, line)) { + std::smatch matches; + if (std::regex_search(line, matches, key_value)) { + return matches.str(1); + } + } + return {}; // pyvenv.cfg did not contain a 'home', we cannot use it +} + +// Hook for the delay-loading the python dll. Instead of loading the dll, we return a handle to the pre-loaded dll +// which may be different from python39.dll (e.g. python311.dll) +FARPROC WINAPI delayHook(unsigned dliNotify, PDelayLoadInfo pdli) { + if (dliNotify == dliNotePreLoadLibrary && strcmp(pdli->szDll, "python39.dll") == 0) + return reinterpret_cast(pythonDll); + return nullptr; +} + +ExternC const PfnDliHook __pfnDliNotifyHook2 = delayHook; + + +/** + * \brief Loads the python3xx.dll runtime library using the specified folder to locate it + * + * Updates the venv path, and returns the home path + * + * If search is false, `venv` should be the root folder of a Python virtual environment. It should hold an `pyvenv.cfg` file + * with a `home` variable that points to the folder holding python3xx.dll (i.e. PYTHONHOME). + * + * If search is true, and the folder does not hold `pyvenv.cfg`: + * - recursively look in parent folders if they have a `venv` or `.venv` subfolder. If they do, check if they contain a `pyvenv.cfg` file and use that. + * - if no `pyvenv.cfg` was found, use the $PYTHONHOME$ environment variable if present. + * - otherwise, try to load `python3.dll` through the OS, and use the path of that folder as PYTHONHOME. + * + * + * \param venv + * \param search + */ +fs::path SetupPaths(fs::path& venv, bool search) +{ + // option 1: load the specified virtual environment + auto pythonHome = TryParsePyvenv(venv); + if (!pythonHome.empty()) + return pythonHome; + + if (!search) + return {}; + + // option 2: look in parent folders for venv or .venv and load that virtual environment + fs::path searchPath = venv; + for (int depth = 0; depth < 10; depth++) + { + if (searchPath.empty()) + break; + + for (auto dir : { "venv", ".venv" }) + { + venv = searchPath / dir; + pythonHome = TryParsePyvenv(venv); + if (!pythonHome.empty()) + return pythonHome; + } + searchPath = searchPath.parent_path(); + } + + venv.clear(); // we could not locate a virtual environment + + // option 3: check the PYTHONHOME environmental variable + // in this case, there is no virtual environment + if (auto pythonHomeVariable = _wgetenv(L"PYTHONHOME")) + { + pythonHome = pythonHomeVariable; + if (!pythonHome.empty()) + return pythonHome; + } + + // option 4: check if the OS can find python3.dll. If so, use that folder + // in this case, there is no virtual environment + if (auto handle = LoadLibrary(L"python3.dll")) + { + wchar_t dllPath[MAX_PATH]; + if (GetModuleFileName(handle, dllPath, MAX_PATH)) + pythonHome = fs::path(dllPath).parent_path(); + FreeLibrary(handle); + return pythonHome; + } + + return {}; +} + + + +/** + * \brief Locates and loads the python3xx.dll runtime library + * \param venv location of the virtual environment to use. When 'search' = true, 'venv' may be updated to the location the virtual environment was found. + * \returns The path of the python3x.dll that was loaded, or an empty path if no python3x.dll was found or if it could not be loaded + */ +fs::path InitializePython(fs::path& venv, bool search) noexcept +{ + fs::path dllPath; + if (pythonDll) + { + FreeLibrary(pythonDll); + pythonDll = nullptr; + } + + try { + // Locate python home and python path + auto pythonHome = SetupPaths(venv, search); + if (pythonHome.empty()) + return {}; + + // Search the home folder for a file of the name python3xx.dll + // When found, load the dll + for (const auto& entry : fs::directory_iterator(pythonHome)) + if (entry.is_regular_file() && std::regex_match(entry.path().filename().string(), filenamePattern)) + { + dllPath = entry.path(); + pythonDll = LoadLibrary(dllPath.generic_wstring().c_str()); + if (!pythonDll) + return {}; // could not load the python dll! + + // Load the data members from the DLL + Py_None = reinterpret_cast(GetProcAddress(pythonDll, "_Py_NoneStruct")); + Py_True = reinterpret_cast(GetProcAddress(pythonDll, "_Py_TrueStruct")); + Py_False = reinterpret_cast(GetProcAddress(pythonDll, "_Py_FalseStruct")); + if (Py_None == nullptr || Py_False == nullptr || Py_True == nullptr) + return {}; // we loaded the dll, but it does not hold the none true and false data members!? + break; + } + if (!pythonDll) + return {}; // there was no python dll in the home folder + } + catch (const fs::filesystem_error&) + { + return {}; // there was an error accessing one of the files or iterating one of the folders + } + + // Start the Python interpreter if it is not running yet + // all paths are set up. When using the first function from the python dll (Py_IsInitialized below), + // the delay-load mechanism will now load the correct dll + if (!Py_IsInitialized()) { + if (!venv.empty()) + { + auto program = (venv / "Scripts" / "python.exe").make_preferred(); + Py_SetProgramName(program.c_str()); + } + // else: not using a virtual environment. Let Python figure out the paths itself. + + Py_InitializeEx(0); // note: some Python distributions just crash here if there is a configuration error + + // enable multi-threading + if (!g_threadState) + g_threadState = PyEval_SaveThread(); + } + + return dllPath; +} + diff --git a/DeviceAdapters/PyDevice/stable.h b/DeviceAdapters/PyDevice/stable.h new file mode 100644 index 000000000..74cf2cb2c --- /dev/null +++ b/DeviceAdapters/PyDevice/stable.h @@ -0,0 +1,1044 @@ +#pragma once +#include +#include +namespace fs = std::filesystem; +fs::path InitializePython(fs::path& venv, bool search) noexcept; + +typedef uintptr_t Py_uintptr_t; +typedef intptr_t Py_intptr_t; +typedef intptr_t Py_ssize_t; +typedef uint32_t Py_UCS4; +typedef uint16_t Py_UCS2; +typedef uint8_t Py_UCS1; +typedef Py_ssize_t Py_hash_t; +typedef size_t Py_uhash_t; +typedef int Py_ssize_clean_t; +typedef struct _Py_tss_t Py_tss_t; +struct PyTypeObject; +struct PyObject { + Py_ssize_t ob_refcnt; + PyTypeObject* ob_type; +}; +struct PyVarObject { + PyObject ob_base; + Py_ssize_t ob_size; /* Number of items in variable part */ +}; +#define Py_INCREF(obj) Py_IncRef(obj); +#define Py_DECREF(obj) Py_DecRef(obj); +#define Py_XINCREF(obj) Py_IncRef(obj); +#define Py_XDECREF(obj) Py_DecRef(obj); + +#define Py_single_input 256 +#define Py_file_input 257 +#define Py_eval_input 258 +#define Py_func_type_input 345 +#define Py_fstring_input 800 + +#define Py_LT 0 +#define Py_LE 1 +#define Py_EQ 2 +#define Py_NE 3 +#define Py_GT 4 +#define Py_GE 5 + +#define PyBUF_READ 0x100 +#define PyBUF_WRITE 0x200 + +extern PyObject* Py_None; +extern PyObject* Py_True; +extern PyObject* Py_False; +bool LoadPythonData(); + +struct PyLongObject; +struct PyMemberDef; +struct PyFrameObject; + +typedef PyObject* (*unaryfunc)(PyObject*); +typedef PyObject* (*binaryfunc)(PyObject*, PyObject*); +typedef PyObject* (*ternaryfunc)(PyObject*, PyObject*, PyObject*); +typedef int (*inquiry)(PyObject*); +typedef Py_ssize_t(*lenfunc)(PyObject*); +typedef PyObject* (*ssizeargfunc)(PyObject*, Py_ssize_t); +typedef PyObject* (*ssizessizeargfunc)(PyObject*, Py_ssize_t, Py_ssize_t); +typedef int(*ssizeobjargproc)(PyObject*, Py_ssize_t, PyObject*); +typedef int(*ssizessizeobjargproc)(PyObject*, Py_ssize_t, Py_ssize_t, PyObject*); +typedef int(*objobjargproc)(PyObject*, PyObject*, PyObject*); +typedef int (*objobjproc)(PyObject*, PyObject*); +typedef int (*visitproc)(PyObject*, void*); +typedef int (*traverseproc)(PyObject*, visitproc, void*); +typedef void (*freefunc)(void*); +typedef void (*destructor)(PyObject*); +typedef PyObject* (*getattrfunc)(PyObject*, char*); +typedef PyObject* (*getattrofunc)(PyObject*, PyObject*); +typedef int (*setattrfunc)(PyObject*, char*, PyObject*); +typedef int (*setattrofunc)(PyObject*, PyObject*, PyObject*); +typedef PyObject* (*reprfunc)(PyObject*); +typedef Py_hash_t(*hashfunc)(PyObject*); +typedef PyObject* (*richcmpfunc) (PyObject*, PyObject*, int); +typedef PyObject* (*getiterfunc) (PyObject*); +typedef PyObject* (*iternextfunc) (PyObject*); +typedef PyObject* (*descrgetfunc) (PyObject*, PyObject*, PyObject*); +typedef int (*descrsetfunc) (PyObject*, PyObject*, PyObject*); +typedef int (*initproc)(PyObject*, PyObject*, PyObject*); +typedef PyObject* (*newfunc)(PyTypeObject*, PyObject*, PyObject*); +typedef PyObject* (*allocfunc)(PyTypeObject*, Py_ssize_t); + +typedef enum { PyGILState_LOCKED, PyGILState_UNLOCKED } PyGILState_STATE; +typedef struct PyCodeObject PyCodeObject; +typedef PyObject* (*getter)(PyObject*, void*); +typedef int (*setter)(PyObject*, PyObject*, void*); +typedef struct PyGetSetDef { + const char* name; + getter get; + setter set; + const char* doc; + void* closure; +} PyGetSetDef; +typedef PyObject* (*PyCFunction)(PyObject*, PyObject*); +typedef PyObject* (*PyCFunctionWithKeywords)(PyObject*, PyObject*, PyObject*); +typedef PyObject* (*PyCMethod)(PyObject*, PyTypeObject*, PyObject* const*, size_t, PyObject*); +typedef struct PyMethodDef PyMethodDef; +typedef void (*PyCapsule_Destructor)(PyObject*); +typedef struct PyModuleDef_Base { + PyObject ob_base; + PyObject* (*m_init)(void); + Py_ssize_t m_index; + PyObject* m_copy; +} PyModuleDef_Base; +struct PyModuleDef_Slot; +typedef struct PyModuleDef_Slot { + int slot; + void* value; +} PyModuleDef_Slot; +typedef struct PyModuleDef { + PyModuleDef_Base m_base; + const char* m_name; + const char* m_doc; + Py_ssize_t m_size; + PyMethodDef* m_methods; + PyModuleDef_Slot* m_slots; + traverseproc m_traverse; + inquiry m_clear; + freefunc m_free; +} PyModuleDef; +typedef void (*PyOS_sighandler_t)(int); +typedef struct PyStructSequence_Field { + const char* name; + const char* doc; +} PyStructSequence_Field; +typedef struct PyStructSequence_Desc { + const char* name; + const char* doc; + struct PyStructSequence_Field* fields; + int n_in_sequence; +} PyStructSequence_Desc; +typedef void* PyThread_type_lock; +typedef enum PyLockStatus { + PY_LOCK_FAILURE = 0, + PY_LOCK_ACQUIRED = 1, + PY_LOCK_INTR +} PyLockStatus; +struct _ts; +struct _is; +typedef struct _ts PyThreadState; +typedef struct _is PyInterpreterState; + +struct PyMethodDef { + const char* ml_name; + PyCFunction ml_meth; + int ml_flags; + const char* ml_doc; +}; +typedef struct { + int slot; + void* pfunc; +} PyType_Slot; + +struct PyType_Spec { + const char* name; + int basicsize; + int itemsize; + unsigned int flags; + PyType_Slot* slots; +}; + + +extern "C" { + __declspec(dllimport) void* PyMem_Malloc(size_t size); + __declspec(dllimport) void* PyMem_Realloc(void* ptr, size_t new_size); + __declspec(dllimport) void PyMem_Free(void* ptr); + + __declspec(dllimport) unsigned int PyType_ClearCache(void); + __declspec(dllimport) PyObject* PyType_FromModuleAndSpec(PyObject*, PyType_Spec*, PyObject*); + __declspec(dllimport) PyObject* PyType_FromSpec(PyType_Spec*); + __declspec(dllimport) PyObject* PyType_FromSpecWithBases(PyType_Spec*, PyObject*); + __declspec(dllimport) PyObject* PyType_GenericAlloc(PyTypeObject*, Py_ssize_t); + __declspec(dllimport) PyObject* PyType_GenericNew(PyTypeObject*, PyObject*, PyObject*); + __declspec(dllimport) unsigned long PyType_GetFlags(PyTypeObject*); + __declspec(dllimport) PyObject* PyType_GetModule(struct _typeobject*); + __declspec(dllimport) void* PyType_GetModuleState(struct _typeobject*); + __declspec(dllimport) void* PyType_GetSlot(PyTypeObject*, int); + __declspec(dllimport) int PyType_IsSubtype(PyTypeObject*, PyTypeObject*); + __declspec(dllimport) void PyType_Modified(PyTypeObject*); + __declspec(dllimport) int PyType_Ready(PyTypeObject*); + + __declspec(dllimport) PyObject* PyObject_ASCII(PyObject*); + __declspec(dllimport) PyObject* PyObject_Repr(PyObject*); + __declspec(dllimport) PyObject* PyObject_Str(PyObject*); + __declspec(dllimport) PyObject* PyObject_Bytes(PyObject*); + __declspec(dllimport) PyObject* PyObject_RichCompare(PyObject*, PyObject*, int); + __declspec(dllimport) int PyObject_RichCompareBool(PyObject*, PyObject*, int); + __declspec(dllimport) PyObject* PyObject_GetAttrString(PyObject*, const char*); + __declspec(dllimport) int PyObject_SetAttrString(PyObject*, const char*, PyObject*); + __declspec(dllimport) int PyObject_HasAttrString(PyObject*, const char*); + __declspec(dllimport) PyObject* PyObject_GetAttr(PyObject*, PyObject*); + __declspec(dllimport) int PyObject_SetAttr(PyObject*, PyObject*, PyObject*); + __declspec(dllimport) int PyObject_HasAttr(PyObject*, PyObject*); + __declspec(dllimport) PyObject* PyObject_SelfIter(PyObject*); + __declspec(dllimport) PyObject* PyObject_GenericGetAttr(PyObject*, PyObject*); + __declspec(dllimport) int PyObject_GenericSetAttr(PyObject*, PyObject*, PyObject*); + __declspec(dllimport) int PyObject_GenericSetDict(PyObject*, PyObject*, void*); + __declspec(dllimport) Py_hash_t PyObject_Hash(PyObject*); + __declspec(dllimport) Py_hash_t PyObject_HashNotImplemented(PyObject*); + __declspec(dllimport) int PyObject_IsTrue(PyObject*); + __declspec(dllimport) int PyObject_Not(PyObject*); + __declspec(dllimport) void PyObject_ClearWeakRefs(PyObject*); + __declspec(dllimport) PyObject* PyObject_Dir(PyObject*); + __declspec(dllimport) void* PyObject_Malloc(size_t size); + __declspec(dllimport) void* PyObject_Calloc(size_t nelem, size_t elsize); + __declspec(dllimport) void* PyObject_Realloc(void* ptr, size_t new_size); + __declspec(dllimport) void PyObject_Free(void* ptr); + __declspec(dllimport) PyObject* PyObject_Init(PyObject*, PyTypeObject*); + __declspec(dllimport) PyVarObject* PyObject_InitVar(PyVarObject*, PyTypeObject*, Py_ssize_t); + + __declspec(dllimport) void PyObject_GC_Track(void*); + __declspec(dllimport) void PyObject_GC_UnTrack(void*); + __declspec(dllimport) void PyObject_GC_Del(void*); + __declspec(dllimport) int PyObject_GC_IsTracked(PyObject*); + __declspec(dllimport) int PyObject_GC_IsFinalized(PyObject*); + __declspec(dllimport) Py_ssize_t PyGC_Collect(void); + + __declspec(dllimport) PyObject* PySeqIter_New(PyObject*); + __declspec(dllimport) PyObject* PyCallIter_New(PyObject*, PyObject*); + __declspec(dllimport) int PyCallable_Check(PyObject*); + __declspec(dllimport) int Py_ReprEnter(PyObject*); + __declspec(dllimport) void Py_ReprLeave(PyObject*); + __declspec(dllimport) void Py_IncRef(PyObject*); + __declspec(dllimport) void Py_DecRef(PyObject*); + + __declspec(dllimport) PyObject* PyByteArray_FromObject(PyObject*); + __declspec(dllimport) PyObject* PyByteArray_Concat(PyObject*, PyObject*); + __declspec(dllimport) PyObject* PyByteArray_FromStringAndSize(const char*, Py_ssize_t); + __declspec(dllimport) Py_ssize_t PyByteArray_Size(PyObject*); + __declspec(dllimport) char* PyByteArray_AsString(PyObject*); + __declspec(dllimport) int PyByteArray_Resize(PyObject*, Py_ssize_t); + + __declspec(dllimport) PyObject* PyBytes_FromStringAndSize(const char*, Py_ssize_t); + __declspec(dllimport) PyObject* PyBytes_FromString(const char*); + __declspec(dllimport) PyObject* PyBytes_FromObject(PyObject*); + __declspec(dllimport) PyObject* PyBytes_FromFormatV(const char*, va_list); + __declspec(dllimport) PyObject* PyBytes_FromFormat(const char*, ...); + + __declspec(dllimport) Py_ssize_t PyBytes_Size(PyObject*); + __declspec(dllimport) char* PyBytes_AsString(PyObject*); + __declspec(dllimport) PyObject* PyBytes_Repr(PyObject*, int); + __declspec(dllimport) void PyBytes_Concat(PyObject**, PyObject*); + __declspec(dllimport) void PyBytes_ConcatAndDel(PyObject**, PyObject*); + __declspec(dllimport) PyObject* PyBytes_DecodeEscape(const char*, Py_ssize_t, const char*, Py_ssize_t, const char*); + __declspec(dllimport) int PyBytes_AsStringAndSize(PyObject* obj, char** s, Py_ssize_t* len); + + __declspec(dllimport) PyObject* PyUnicode_FromStringAndSize(const char* u, Py_ssize_t size); + __declspec(dllimport) PyObject* PyUnicode_FromString(const char* u); + __declspec(dllimport) PyObject* PyUnicode_Substring(PyObject* str, Py_ssize_t start, Py_ssize_t end); + __declspec(dllimport) Py_UCS4* PyUnicode_AsUCS4(PyObject* unicode, Py_UCS4* buffer, Py_ssize_t buflen, int copy_null); + __declspec(dllimport) Py_UCS4* PyUnicode_AsUCS4Copy(PyObject* unicode); + __declspec(dllimport) Py_ssize_t PyUnicode_GetLength(PyObject* unicode); + __declspec(dllimport) Py_UCS4 PyUnicode_ReadChar(PyObject* unicode, Py_ssize_t index); + __declspec(dllimport) int PyUnicode_WriteChar(PyObject* unicode, Py_ssize_t index, Py_UCS4 character); + __declspec(dllimport) int PyUnicode_Resize(PyObject** unicode, Py_ssize_t length); + __declspec(dllimport) PyObject* PyUnicode_FromEncodedObject(PyObject* obj, const char* encoding, const char* errors); + __declspec(dllimport) PyObject* PyUnicode_FromObject(PyObject* obj); + __declspec(dllimport) PyObject* PyUnicode_FromFormatV(const char* format, va_list vargs); + __declspec(dllimport) PyObject* PyUnicode_FromFormat(const char* format, ...); + __declspec(dllimport) void PyUnicode_InternInPlace(PyObject**); + __declspec(dllimport) void PyUnicode_InternImmortal(PyObject**); + __declspec(dllimport) PyObject* PyUnicode_InternFromString(const char* u); + __declspec(dllimport) PyObject* PyUnicode_FromWideChar(const wchar_t* w, Py_ssize_t size); + __declspec(dllimport) Py_ssize_t PyUnicode_AsWideChar(PyObject* unicode, wchar_t* w, Py_ssize_t size); + __declspec(dllimport) wchar_t* PyUnicode_AsWideCharString(PyObject* unicode, Py_ssize_t* size); + __declspec(dllimport) PyObject* PyUnicode_FromOrdinal(int ordinal); + __declspec(dllimport) const char* PyUnicode_GetDefaultEncoding(void); + __declspec(dllimport) PyObject* PyUnicode_Decode(const char* s, Py_ssize_t size, const char* encoding, const char* errors); + __declspec(dllimport) PyObject* PyUnicode_AsEncodedString(PyObject* unicode, const char* encoding, const char* errors); + __declspec(dllimport) PyObject* PyUnicode_BuildEncodingMap(PyObject* string); + __declspec(dllimport) PyObject* PyUnicode_DecodeUTF7(const char* string, Py_ssize_t length, const char* errors); + __declspec(dllimport) PyObject* PyUnicode_DecodeUTF7Stateful(const char* string, Py_ssize_t length, const char* errors, Py_ssize_t* consumed); + __declspec(dllimport) PyObject* PyUnicode_DecodeUTF8(const char* string, Py_ssize_t length, const char* errors); + __declspec(dllimport) PyObject* PyUnicode_DecodeUTF8Stateful(const char* string, Py_ssize_t length, const char* errors, Py_ssize_t* consumed); + __declspec(dllimport) PyObject* PyUnicode_AsUTF8String(PyObject* unicode); + __declspec(dllimport) PyObject* PyUnicode_DecodeUTF32( + const char* string, + Py_ssize_t length, + const char* errors, + int* byteorder + ); + __declspec(dllimport) PyObject* PyUnicode_DecodeUTF32Stateful( + const char* string, + Py_ssize_t length, + const char* errors, + int* byteorder, + Py_ssize_t* consumed + ); + __declspec(dllimport) PyObject* PyUnicode_AsUTF32String( + PyObject* unicode + ); + __declspec(dllimport) PyObject* PyUnicode_DecodeUTF16( + const char* string, + Py_ssize_t length, + const char* errors, + int* byteorder + ); + __declspec(dllimport) PyObject* PyUnicode_DecodeUTF16Stateful( + const char* string, + Py_ssize_t length, + const char* errors, + int* byteorder, + Py_ssize_t* consumed + ); + __declspec(dllimport) PyObject* PyUnicode_AsUTF16String( + PyObject* unicode + ); + __declspec(dllimport) PyObject* PyUnicode_DecodeUnicodeEscape( + const char* string, + Py_ssize_t length, + const char* errors + ); + __declspec(dllimport) PyObject* PyUnicode_AsUnicodeEscapeString( + PyObject* unicode + ); + __declspec(dllimport) PyObject* PyUnicode_DecodeRawUnicodeEscape( + const char* string, + Py_ssize_t length, + const char* errors + ); + __declspec(dllimport) PyObject* PyUnicode_AsRawUnicodeEscapeString( + PyObject* unicode + ); + __declspec(dllimport) PyObject* PyUnicode_DecodeLatin1( + const char* string, + Py_ssize_t length, + const char* errors + ); + __declspec(dllimport) PyObject* PyUnicode_AsLatin1String( + PyObject* unicode + ); + __declspec(dllimport) PyObject* PyUnicode_DecodeASCII( + const char* string, + Py_ssize_t length, + const char* errors + ); + __declspec(dllimport) PyObject* PyUnicode_AsASCIIString( + PyObject* unicode + ); + __declspec(dllimport) PyObject* PyUnicode_DecodeCharmap( + const char* string, + Py_ssize_t length, + PyObject* mapping, + const char* errors + ); + __declspec(dllimport) PyObject* PyUnicode_AsCharmapString( + PyObject* unicode, + PyObject* mapping + ); + __declspec(dllimport) PyObject* PyUnicode_DecodeMBCS( + const char* string, + Py_ssize_t length, + const char* errors + ); + __declspec(dllimport) PyObject* PyUnicode_DecodeMBCSStateful( + const char* string, + Py_ssize_t length, + const char* errors, + Py_ssize_t* consumed + ); + __declspec(dllimport) PyObject* PyUnicode_DecodeCodePageStateful( + int code_page, + const char* string, + Py_ssize_t length, + const char* errors, + Py_ssize_t* consumed + ); + __declspec(dllimport) PyObject* PyUnicode_AsMBCSString( + PyObject* unicode + ); + __declspec(dllimport) PyObject* PyUnicode_EncodeCodePage( + int code_page, + PyObject* unicode, + const char* errors + ); + __declspec(dllimport) PyObject* PyUnicode_DecodeLocaleAndSize( + const char* str, + Py_ssize_t len, + const char* errors); + __declspec(dllimport) PyObject* PyUnicode_DecodeLocale( + const char* str, + const char* errors); + __declspec(dllimport) PyObject* PyUnicode_EncodeLocale( + PyObject* unicode, + const char* errors + ); + __declspec(dllimport) int PyUnicode_FSConverter(PyObject*, void*); + __declspec(dllimport) int PyUnicode_FSDecoder(PyObject*, void*); + __declspec(dllimport) PyObject* PyUnicode_DecodeFSDefault( + const char* s + ); + __declspec(dllimport) PyObject* PyUnicode_DecodeFSDefaultAndSize( + const char* s, + Py_ssize_t size + ); + __declspec(dllimport) PyObject* PyUnicode_EncodeFSDefault( + PyObject* unicode + ); + __declspec(dllimport) PyObject* PyUnicode_Concat( + PyObject* left, + PyObject* right + ); + __declspec(dllimport) void PyUnicode_Append( + PyObject** pleft, + PyObject* right + ); + __declspec(dllimport) void PyUnicode_AppendAndDel( + PyObject** pleft, + PyObject* right + ); + __declspec(dllimport) PyObject* PyUnicode_Split( + PyObject* s, + PyObject* sep, + Py_ssize_t maxsplit + ); + __declspec(dllimport) PyObject* PyUnicode_Splitlines( + PyObject* s, + int keepends + ); + __declspec(dllimport) PyObject* PyUnicode_Partition( + PyObject* s, + PyObject* sep + ); + __declspec(dllimport) PyObject* PyUnicode_RPartition( + PyObject* s, + PyObject* sep + ); + __declspec(dllimport) PyObject* PyUnicode_RSplit( + PyObject* s, + PyObject* sep, + Py_ssize_t maxsplit + ); + __declspec(dllimport) PyObject* PyUnicode_Translate( + PyObject* str, + PyObject* table, + const char* errors + ); + __declspec(dllimport) PyObject* PyUnicode_Join( + PyObject* separator, + PyObject* seq + ); + __declspec(dllimport) Py_ssize_t PyUnicode_Tailmatch( + PyObject* str, + PyObject* substr, + Py_ssize_t start, + Py_ssize_t end, + int direction + ); + __declspec(dllimport) Py_ssize_t PyUnicode_Find( + PyObject* str, + PyObject* substr, + Py_ssize_t start, + Py_ssize_t end, + int direction + ); + __declspec(dllimport) Py_ssize_t PyUnicode_FindChar( + PyObject* str, + Py_UCS4 ch, + Py_ssize_t start, + Py_ssize_t end, + int direction + ); + __declspec(dllimport) Py_ssize_t PyUnicode_Count( + PyObject* str, + PyObject* substr, + Py_ssize_t start, + Py_ssize_t end + ); + __declspec(dllimport) PyObject* PyUnicode_Replace( + PyObject* str, + PyObject* substr, + PyObject* replstr, + Py_ssize_t maxcount + ); + __declspec(dllimport) int PyUnicode_Compare( + PyObject* left, + PyObject* right + ); + __declspec(dllimport) int PyUnicode_CompareWithASCIIString( + PyObject* left, + const char* right + ); + __declspec(dllimport) PyObject* PyUnicode_RichCompare( + PyObject* left, + PyObject* right, + int op + ); + __declspec(dllimport) PyObject* PyUnicode_Format( + PyObject* format, + PyObject* args + ); + __declspec(dllimport) int PyUnicode_Contains( + PyObject* container, + PyObject* element + ); + __declspec(dllimport) int PyUnicode_IsIdentifier(PyObject* s); + + __declspec(dllimport) PyObject* PyLong_FromLong(long); + __declspec(dllimport) PyObject* PyLong_FromUnsignedLong(unsigned long); + __declspec(dllimport) PyObject* PyLong_FromSize_t(size_t); + __declspec(dllimport) PyObject* PyLong_FromSsize_t(Py_ssize_t); + __declspec(dllimport) PyObject* PyLong_FromDouble(double); + __declspec(dllimport) long PyLong_AsLong(PyObject*); + __declspec(dllimport) long PyLong_AsLongAndOverflow(PyObject*, int*); + __declspec(dllimport) Py_ssize_t PyLong_AsSsize_t(PyObject*); + __declspec(dllimport) size_t PyLong_AsSize_t(PyObject*); + __declspec(dllimport) unsigned long PyLong_AsUnsignedLong(PyObject*); + __declspec(dllimport) unsigned long PyLong_AsUnsignedLongMask(PyObject*); + __declspec(dllimport) PyObject* PyLong_GetInfo(void); + __declspec(dllimport) double PyLong_AsDouble(PyObject*); + __declspec(dllimport) PyObject* PyLong_FromVoidPtr(void*); + __declspec(dllimport) void* PyLong_AsVoidPtr(PyObject*); + __declspec(dllimport) PyObject* PyLong_FromLongLong(long long); + __declspec(dllimport) PyObject* PyLong_FromUnsignedLongLong(unsigned long long); + __declspec(dllimport) long long PyLong_AsLongLong(PyObject*); + __declspec(dllimport) unsigned long long PyLong_AsUnsignedLongLong(PyObject*); + __declspec(dllimport) unsigned long long PyLong_AsUnsignedLongLongMask(PyObject*); + __declspec(dllimport) long long PyLong_AsLongLongAndOverflow(PyObject*, int*); + __declspec(dllimport) PyObject* PyLong_FromString(const char*, char**, int); + __declspec(dllimport) unsigned long PyOS_strtoul(const char*, char**, int); + __declspec(dllimport) long PyOS_strtol(const char*, char**, int); + + __declspec(dllimport) PyObject* PyBool_FromLong(long); + + __declspec(dllimport) double PyFloat_GetMax(void); + __declspec(dllimport) double PyFloat_GetMin(void); + __declspec(dllimport) PyObject* PyFloat_GetInfo(void); + __declspec(dllimport) PyObject* PyFloat_FromString(PyObject*); + __declspec(dllimport) PyObject* PyFloat_FromDouble(double); + __declspec(dllimport) double PyFloat_AsDouble(PyObject*); + + __declspec(dllimport) PyObject* PyComplex_FromDoubles(double real, double imag); + __declspec(dllimport) double PyComplex_RealAsDouble(PyObject* op); + __declspec(dllimport) double PyComplex_ImagAsDouble(PyObject* op); + + __declspec(dllimport) PyObject* PyMemoryView_FromObject(PyObject* base); + __declspec(dllimport) PyObject* PyMemoryView_FromMemory(char* mem, Py_ssize_t size, int flags); + __declspec(dllimport) PyObject* PyMemoryView_GetContiguous(PyObject* base, int buffertype, char order); + + __declspec(dllimport) PyObject* PyTuple_New(Py_ssize_t size); + __declspec(dllimport) Py_ssize_t PyTuple_Size(PyObject*); + __declspec(dllimport) PyObject* PyTuple_GetItem(PyObject*, Py_ssize_t); + __declspec(dllimport) int PyTuple_SetItem(PyObject*, Py_ssize_t, PyObject*); + __declspec(dllimport) PyObject* PyTuple_GetSlice(PyObject*, Py_ssize_t, Py_ssize_t); + __declspec(dllimport) PyObject* PyTuple_Pack(Py_ssize_t, ...); + + __declspec(dllimport) PyObject* PyList_New(Py_ssize_t size); + __declspec(dllimport) Py_ssize_t PyList_Size(PyObject*); + __declspec(dllimport) PyObject* PyList_GetItem(PyObject*, Py_ssize_t); + __declspec(dllimport) int PyList_SetItem(PyObject*, Py_ssize_t, PyObject*); + __declspec(dllimport) int PyList_Insert(PyObject*, Py_ssize_t, PyObject*); + __declspec(dllimport) int PyList_Append(PyObject*, PyObject*); + __declspec(dllimport) PyObject* PyList_GetSlice(PyObject*, Py_ssize_t, Py_ssize_t); + __declspec(dllimport) int PyList_SetSlice(PyObject*, Py_ssize_t, Py_ssize_t, PyObject*); + __declspec(dllimport) int PyList_Sort(PyObject*); + __declspec(dllimport) int PyList_Reverse(PyObject*); + __declspec(dllimport) PyObject* PyList_AsTuple(PyObject*); + + __declspec(dllimport) PyObject* PyDict_New(void); + __declspec(dllimport) PyObject* PyDict_GetItem(PyObject* mp, PyObject* key); + __declspec(dllimport) PyObject* PyDict_GetItemWithError(PyObject* mp, PyObject* key); + __declspec(dllimport) int PyDict_SetItem(PyObject* mp, PyObject* key, PyObject* item); + __declspec(dllimport) int PyDict_DelItem(PyObject* mp, PyObject* key); + __declspec(dllimport) void PyDict_Clear(PyObject* mp); + __declspec(dllimport) int PyDict_Next(PyObject* mp, Py_ssize_t* pos, PyObject** key, PyObject** value); + __declspec(dllimport) PyObject* PyDict_Keys(PyObject* mp); + __declspec(dllimport) PyObject* PyDict_Values(PyObject* mp); + __declspec(dllimport) PyObject* PyDict_Items(PyObject* mp); + __declspec(dllimport) Py_ssize_t PyDict_Size(PyObject* mp); + __declspec(dllimport) PyObject* PyDict_Copy(PyObject* mp); + __declspec(dllimport) int PyDict_Contains(PyObject* mp, PyObject* key); + __declspec(dllimport) int PyDict_Update(PyObject* mp, PyObject* other); + __declspec(dllimport) int PyDict_Merge(PyObject* mp, PyObject* other, int override); + __declspec(dllimport) int PyDict_MergeFromSeq2(PyObject* d, PyObject* seq2, int override); + __declspec(dllimport) PyObject* PyDict_GetItemString(PyObject* dp, const char* key); + __declspec(dllimport) int PyDict_SetItemString(PyObject* dp, const char* key, PyObject* item); + __declspec(dllimport) int PyDict_DelItemString(PyObject* dp, const char* key); + + __declspec(dllimport) PyObject* PySet_New(PyObject*); + __declspec(dllimport) PyObject* PyFrozenSet_New(PyObject*); + __declspec(dllimport) int PySet_Add(PyObject* set, PyObject* key); + __declspec(dllimport) int PySet_Clear(PyObject* set); + __declspec(dllimport) int PySet_Contains(PyObject* anyset, PyObject* key); + __declspec(dllimport) int PySet_Discard(PyObject* set, PyObject* key); + __declspec(dllimport) PyObject* PySet_Pop(PyObject* set); + __declspec(dllimport) Py_ssize_t PySet_Size(PyObject* anyset); + + __declspec(dllimport) PyCFunction PyCFunction_GetFunction(PyObject*); + __declspec(dllimport) PyObject* PyCFunction_GetSelf(PyObject*); + __declspec(dllimport) int PyCFunction_GetFlags(PyObject*); + __declspec(dllimport) PyObject* PyCFunction_NewEx(PyMethodDef*, PyObject*, PyObject*); + __declspec(dllimport) PyObject* PyCMethod_New(PyMethodDef*, PyObject*, PyObject*, PyTypeObject*); + + __declspec(dllimport) PyObject* PyModule_NewObject(PyObject* name); + __declspec(dllimport) PyObject* PyModule_New(const char* name); + __declspec(dllimport) PyObject* PyModule_GetDict(PyObject*); + __declspec(dllimport) PyObject* PyModule_GetNameObject(PyObject*); + __declspec(dllimport) const char* PyModule_GetName(PyObject*); + __declspec(dllimport) PyObject* PyModule_GetFilenameObject(PyObject*); + __declspec(dllimport) PyModuleDef* PyModule_GetDef(PyObject*); + __declspec(dllimport) void* PyModule_GetState(PyObject*); + __declspec(dllimport) PyObject* PyModuleDef_Init(PyModuleDef*); + __declspec(dllimport) PyObject* PyFile_FromFd(int, const char*, const char*, int, const char*, const char*, const char*, int); + __declspec(dllimport) PyObject* PyFile_GetLine(PyObject*, int); + __declspec(dllimport) int PyFile_WriteObject(PyObject*, PyObject*, int); + __declspec(dllimport) int PyFile_WriteString(const char*, PyObject*); + __declspec(dllimport) int PyObject_AsFileDescriptor(PyObject*); + + + __declspec(dllimport) PyObject* PyCapsule_New(void* pointer, const char* name, PyCapsule_Destructor destructor); + __declspec(dllimport) void* PyCapsule_GetPointer(PyObject* capsule, const char* name); + __declspec(dllimport) PyCapsule_Destructor PyCapsule_GetDestructor(PyObject* capsule); + __declspec(dllimport) const char* PyCapsule_GetName(PyObject* capsule); + __declspec(dllimport) void* PyCapsule_GetContext(PyObject* capsule); + __declspec(dllimport) int PyCapsule_IsValid(PyObject* capsule, const char* name); + __declspec(dllimport) int PyCapsule_SetPointer(PyObject* capsule, void* pointer); + __declspec(dllimport) int PyCapsule_SetDestructor(PyObject* capsule, PyCapsule_Destructor destructor); + __declspec(dllimport) int PyCapsule_SetName(PyObject* capsule, const char* name); + __declspec(dllimport) int PyCapsule_SetContext(PyObject* capsule, void* context); + __declspec(dllimport) void* PyCapsule_Import(const char* name, int no_block); + __declspec(dllimport) int PyFrame_GetLineNumber(PyFrameObject*); + __declspec(dllimport) PyCodeObject* PyFrame_GetCode(PyFrameObject* frame); + __declspec(dllimport) int PyTraceBack_Here(PyFrameObject*); + __declspec(dllimport) int PyTraceBack_Print(PyObject*, PyObject*); + + __declspec(dllimport) PyObject* PySlice_New(PyObject* start, PyObject* stop, PyObject* step); + __declspec(dllimport) int PySlice_GetIndices(PyObject* r, Py_ssize_t length, Py_ssize_t* start, Py_ssize_t* stop, Py_ssize_t* step); + __declspec(dllimport) int PySlice_Unpack(PyObject* slice, Py_ssize_t* start, Py_ssize_t* stop, Py_ssize_t* step); + __declspec(dllimport) Py_ssize_t PySlice_AdjustIndices(Py_ssize_t length, Py_ssize_t* start, Py_ssize_t* stop, Py_ssize_t step); + __declspec(dllimport) PyObject* PyDescr_NewMethod(PyTypeObject*, PyMethodDef*); + __declspec(dllimport) PyObject* PyDescr_NewClassMethod(PyTypeObject*, PyMethodDef*); + __declspec(dllimport) PyObject* PyDescr_NewMember(PyTypeObject*, PyMemberDef*); + __declspec(dllimport) PyObject* PyDescr_NewGetSet(PyTypeObject*, PyGetSetDef*); + __declspec(dllimport) PyObject* PyDictProxy_New(PyObject*); + __declspec(dllimport) PyObject* PyWrapper_New(PyObject*, PyObject*); + __declspec(dllimport) PyObject* Py_GenericAlias(PyObject*, PyObject*); + __declspec(dllimport) int PyErr_WarnEx( + PyObject* category, + const char* message, + Py_ssize_t stack_level); + __declspec(dllimport) int PyErr_WarnFormat( + PyObject* category, + Py_ssize_t stack_level, + const char* format, + ...); + __declspec(dllimport) int PyErr_ResourceWarning( + PyObject* source, + Py_ssize_t stack_level, + const char* format, + ...); + __declspec(dllimport) int PyErr_WarnExplicit( + PyObject* category, + const char* message, + const char* filename, + int lineno, + const char* module, + PyObject* registry); + __declspec(dllimport) PyObject* PyWeakref_NewRef(PyObject* ob, PyObject* callback); + __declspec(dllimport) PyObject* PyWeakref_NewProxy(PyObject* ob, PyObject* callback); + __declspec(dllimport) PyObject* PyWeakref_GetObject(PyObject* ref); + __declspec(dllimport) PyTypeObject* PyStructSequence_NewType(PyStructSequence_Desc* desc); + __declspec(dllimport) PyObject* PyStructSequence_New(PyTypeObject* type); + __declspec(dllimport) void PyStructSequence_SetItem(PyObject*, Py_ssize_t, PyObject*); + __declspec(dllimport) PyObject* PyStructSequence_GetItem(PyObject*, Py_ssize_t); + __declspec(dllimport) int PyCodec_Register(PyObject* search_function); + __declspec(dllimport) int PyCodec_KnownEncoding(const char* encoding); + __declspec(dllimport) PyObject* PyCodec_Encode(PyObject* object, const char* encoding, const char* errors); + __declspec(dllimport) PyObject* PyCodec_Decode(PyObject* object, const char* encoding, const char* errors); + __declspec(dllimport) PyObject* PyCodec_Encoder( + const char* encoding + ); + __declspec(dllimport) PyObject* PyCodec_Decoder( + const char* encoding + ); + __declspec(dllimport) PyObject* PyCodec_IncrementalEncoder( + const char* encoding, + const char* errors + ); + __declspec(dllimport) PyObject* PyCodec_IncrementalDecoder( + const char* encoding, + const char* errors + ); + __declspec(dllimport) PyObject* PyCodec_StreamReader( + const char* encoding, + PyObject* stream, + const char* errors + ); + __declspec(dllimport) PyObject* PyCodec_StreamWriter( + const char* encoding, + PyObject* stream, + const char* errors + ); + __declspec(dllimport) int PyCodec_RegisterError(const char* name, PyObject* error); + __declspec(dllimport) PyObject* PyCodec_LookupError(const char* name); + __declspec(dllimport) PyObject* PyCodec_StrictErrors(PyObject* exc); + __declspec(dllimport) PyObject* PyCodec_IgnoreErrors(PyObject* exc); + __declspec(dllimport) PyObject* PyCodec_ReplaceErrors(PyObject* exc); + __declspec(dllimport) PyObject* PyCodec_XMLCharRefReplaceErrors(PyObject* exc); + __declspec(dllimport) PyObject* PyCodec_BackslashReplaceErrors(PyObject* exc); + __declspec(dllimport) PyObject* PyCodec_NameReplaceErrors(PyObject* exc); + + __declspec(dllimport) void PyErr_SetNone(PyObject*); + __declspec(dllimport) void PyErr_SetObject(PyObject*, PyObject*); + __declspec(dllimport) void PyErr_SetString(PyObject* exception, const char* string); + __declspec(dllimport) PyObject* PyErr_Occurred(void); + __declspec(dllimport) void PyErr_Clear(void); + __declspec(dllimport) void PyErr_Fetch(PyObject**, PyObject**, PyObject**); + __declspec(dllimport) void PyErr_Restore(PyObject*, PyObject*, PyObject*); + __declspec(dllimport) void PyErr_GetExcInfo(PyObject**, PyObject**, PyObject**); + __declspec(dllimport) void PyErr_SetExcInfo(PyObject*, PyObject*, PyObject*); + __declspec(dllimport) void __declspec(noreturn) Py_FatalError(const char* message); + __declspec(dllimport) int PyErr_GivenExceptionMatches(PyObject*, PyObject*); + __declspec(dllimport) int PyErr_ExceptionMatches(PyObject*); + __declspec(dllimport) void PyErr_NormalizeException(PyObject**, PyObject**, PyObject**); + __declspec(dllimport) int PyException_SetTraceback(PyObject*, PyObject*); + __declspec(dllimport) PyObject* PyException_GetTraceback(PyObject*); + __declspec(dllimport) PyObject* PyException_GetCause(PyObject*); + __declspec(dllimport) void PyException_SetCause(PyObject*, PyObject*); + __declspec(dllimport) PyObject* PyException_GetContext(PyObject*); + __declspec(dllimport) void PyException_SetContext(PyObject*, PyObject*); + __declspec(dllimport) const char* PyExceptionClass_Name(PyObject*); + __declspec(dllimport) int PyErr_BadArgument(void); + __declspec(dllimport) PyObject* PyErr_NoMemory(void); + __declspec(dllimport) PyObject* PyErr_SetFromErrno(PyObject*); + __declspec(dllimport) PyObject* PyErr_SetFromErrnoWithFilenameObject(PyObject*, PyObject*); + __declspec(dllimport) PyObject* PyErr_SetFromErrnoWithFilenameObjects(PyObject*, PyObject*, PyObject*); + __declspec(dllimport) PyObject* PyErr_SetFromErrnoWithFilename(PyObject* exc, const char* filename); + __declspec(dllimport) PyObject* PyErr_Format(PyObject* exception, const char* format, ...); + __declspec(dllimport) PyObject* PyErr_FormatV(PyObject* exception, const char* format, va_list vargs); + __declspec(dllimport) PyObject* PyErr_SetFromWindowsErrWithFilename(int ierr, const char* filename); + __declspec(dllimport) PyObject* PyErr_SetFromWindowsErr(int); + __declspec(dllimport) PyObject* PyErr_SetExcFromWindowsErrWithFilenameObject(PyObject*, int, PyObject*); + __declspec(dllimport) PyObject* PyErr_SetExcFromWindowsErrWithFilenameObjects(PyObject*, int, PyObject*, PyObject*); + __declspec(dllimport) PyObject* PyErr_SetExcFromWindowsErrWithFilename(PyObject* exc, int ierr, const char* filename); + __declspec(dllimport) PyObject* PyErr_SetExcFromWindowsErr(PyObject*, int); + __declspec(dllimport) PyObject* PyErr_SetImportErrorSubclass(PyObject*, PyObject*, PyObject*, PyObject*); + __declspec(dllimport) PyObject* PyErr_SetImportError(PyObject*, PyObject*, PyObject*); + __declspec(dllimport) void PyErr_BadInternalCall(void); + __declspec(dllimport) PyObject* PyErr_NewException(const char* name, PyObject* base, PyObject* dict); + __declspec(dllimport) PyObject* PyErr_NewExceptionWithDoc(const char* name, const char* doc, PyObject* base, PyObject* dict); + __declspec(dllimport) void PyErr_WriteUnraisable(PyObject*); + __declspec(dllimport) int PyErr_CheckSignals(void); + __declspec(dllimport) void PyErr_SetInterrupt(void); + __declspec(dllimport) void PyErr_SyntaxLocation(const char* filename, int lineno); + __declspec(dllimport) void PyErr_SyntaxLocationEx(const char* filename, int lineno, int col_offset); + __declspec(dllimport) PyObject* PyErr_ProgramText(const char* filename, int lineno); + __declspec(dllimport) PyObject* PyUnicodeDecodeError_Create(const char* encoding, const char* object, Py_ssize_t length, Py_ssize_t start, Py_ssize_t end, const char* reason); + __declspec(dllimport) PyObject* PyUnicodeEncodeError_GetEncoding(PyObject*); + __declspec(dllimport) PyObject* PyUnicodeDecodeError_GetEncoding(PyObject*); + __declspec(dllimport) PyObject* PyUnicodeEncodeError_GetObject(PyObject*); + __declspec(dllimport) PyObject* PyUnicodeDecodeError_GetObject(PyObject*); + __declspec(dllimport) PyObject* PyUnicodeTranslateError_GetObject(PyObject*); + __declspec(dllimport) int PyUnicodeEncodeError_GetStart(PyObject*, Py_ssize_t*); + __declspec(dllimport) int PyUnicodeDecodeError_GetStart(PyObject*, Py_ssize_t*); + __declspec(dllimport) int PyUnicodeTranslateError_GetStart(PyObject*, Py_ssize_t*); + __declspec(dllimport) int PyUnicodeEncodeError_SetStart(PyObject*, Py_ssize_t); + __declspec(dllimport) int PyUnicodeDecodeError_SetStart(PyObject*, Py_ssize_t); + __declspec(dllimport) int PyUnicodeTranslateError_SetStart(PyObject*, Py_ssize_t); + __declspec(dllimport) int PyUnicodeEncodeError_GetEnd(PyObject*, Py_ssize_t*); + __declspec(dllimport) int PyUnicodeDecodeError_GetEnd(PyObject*, Py_ssize_t*); + __declspec(dllimport) int PyUnicodeTranslateError_GetEnd(PyObject*, Py_ssize_t*); + __declspec(dllimport) int PyUnicodeEncodeError_SetEnd(PyObject*, Py_ssize_t); + __declspec(dllimport) int PyUnicodeDecodeError_SetEnd(PyObject*, Py_ssize_t); + __declspec(dllimport) int PyUnicodeTranslateError_SetEnd(PyObject*, Py_ssize_t); + __declspec(dllimport) PyObject* PyUnicodeEncodeError_GetReason(PyObject*); + __declspec(dllimport) PyObject* PyUnicodeDecodeError_GetReason(PyObject*); + __declspec(dllimport) PyObject* PyUnicodeTranslateError_GetReason(PyObject*); + __declspec(dllimport) int PyUnicodeEncodeError_SetReason(PyObject* exc, const char* reason); + __declspec(dllimport) int PyUnicodeDecodeError_SetReason(PyObject* exc, const char* reason); + __declspec(dllimport) int PyUnicodeTranslateError_SetReason(PyObject* exc, const char* reason); + __declspec(dllimport) int PyOS_snprintf(char* str, size_t size, const char* format, ...); + __declspec(dllimport) int PyOS_vsnprintf(char* str, size_t size, const char* format, va_list va); + + __declspec(dllimport) void PyThread_init_thread(void); + __declspec(dllimport) unsigned long PyThread_start_new_thread(void (*)(void*), void*); + __declspec(dllimport) void __declspec(noreturn) PyThread_exit_thread(void); + __declspec(dllimport) unsigned long PyThread_get_thread_ident(void); + __declspec(dllimport) unsigned long PyThread_get_thread_native_id(void); + __declspec(dllimport) PyThread_type_lock PyThread_allocate_lock(void); + __declspec(dllimport) void PyThread_free_lock(PyThread_type_lock); + __declspec(dllimport) int PyThread_acquire_lock(PyThread_type_lock, int); + __declspec(dllimport) PyLockStatus PyThread_acquire_lock_timed(PyThread_type_lock, long long microseconds, int intr_flag); + __declspec(dllimport) void PyThread_release_lock(PyThread_type_lock); + __declspec(dllimport) size_t PyThread_get_stacksize(void); + __declspec(dllimport) int PyThread_set_stacksize(size_t); + __declspec(dllimport) PyObject* PyThread_GetInfo(void); + __declspec(dllimport) Py_tss_t* PyThread_tss_alloc(void); + __declspec(dllimport) void PyThread_tss_free(Py_tss_t* key); + __declspec(dllimport) int PyThread_tss_is_created(Py_tss_t* key); + __declspec(dllimport) int PyThread_tss_create(Py_tss_t* key); + __declspec(dllimport) void PyThread_tss_delete(Py_tss_t* key); + __declspec(dllimport) int PyThread_tss_set(Py_tss_t* key, void* value); + __declspec(dllimport) void* PyThread_tss_get(Py_tss_t* key); + __declspec(dllimport) PyInterpreterState* PyInterpreterState_New(void); + __declspec(dllimport) void PyInterpreterState_Clear(PyInterpreterState*); + __declspec(dllimport) void PyInterpreterState_Delete(PyInterpreterState*); + __declspec(dllimport) PyInterpreterState* PyInterpreterState_Get(void); + __declspec(dllimport) PyObject* PyInterpreterState_GetDict(PyInterpreterState*); + __declspec(dllimport) int64_t PyInterpreterState_GetID(PyInterpreterState*); + __declspec(dllimport) int PyState_AddModule(PyObject*, struct PyModuleDef*); + __declspec(dllimport) int PyState_RemoveModule(struct PyModuleDef*); + __declspec(dllimport) PyObject* PyState_FindModule(struct PyModuleDef*); + __declspec(dllimport) PyThreadState* PyThreadState_New(PyInterpreterState*); + __declspec(dllimport) void PyThreadState_Clear(PyThreadState*); + __declspec(dllimport) void PyThreadState_Delete(PyThreadState*); + __declspec(dllimport) PyThreadState* PyThreadState_Get(void); + __declspec(dllimport) PyThreadState* PyThreadState_Swap(PyThreadState*); + __declspec(dllimport) PyObject* PyThreadState_GetDict(void); + __declspec(dllimport) int PyThreadState_SetAsyncExc(unsigned long, PyObject*); + __declspec(dllimport) PyInterpreterState* PyThreadState_GetInterpreter(PyThreadState* tstate); + __declspec(dllimport) PyFrameObject* PyThreadState_GetFrame(PyThreadState* tstate); + __declspec(dllimport) uint64_t PyThreadState_GetID(PyThreadState* tstate); + __declspec(dllimport) PyGILState_STATE PyGILState_Ensure(void); + __declspec(dllimport) void PyGILState_Release(PyGILState_STATE); + __declspec(dllimport) PyThreadState* PyGILState_GetThisThreadState(void); + __declspec(dllimport) int PyArg_Parse(PyObject*, const char*, ...); + __declspec(dllimport) int PyArg_ParseTuple(PyObject*, const char*, ...); + __declspec(dllimport) int PyArg_ParseTupleAndKeywords(PyObject*, PyObject*, const char*, char**, ...); + __declspec(dllimport) int PyArg_VaParse(PyObject*, const char*, va_list); + __declspec(dllimport) int PyArg_VaParseTupleAndKeywords(PyObject*, PyObject*, const char*, char**, va_list); + __declspec(dllimport) int PyArg_ValidateKeywordArguments(PyObject*); + __declspec(dllimport) int PyArg_UnpackTuple(PyObject*, const char*, Py_ssize_t, Py_ssize_t, ...); + __declspec(dllimport) PyObject* Py_BuildValue(const char*, ...); + __declspec(dllimport) PyObject* Py_VaBuildValue(const char*, va_list); + __declspec(dllimport) int PyModule_AddObject(PyObject*, const char*, PyObject*); + __declspec(dllimport) int PyModule_AddIntConstant(PyObject*, const char*, long); + __declspec(dllimport) int PyModule_AddStringConstant(PyObject*, const char*, const char*); + __declspec(dllimport) int PyModule_AddType(PyObject* module, PyTypeObject* type); + __declspec(dllimport) int PyModule_SetDocString(PyObject*, const char*); + __declspec(dllimport) int PyModule_AddFunctions(PyObject*, PyMethodDef*); + __declspec(dllimport) int PyModule_ExecDef(PyObject* module, PyModuleDef* def); + __declspec(dllimport) PyObject* PyModule_Create2(struct PyModuleDef*, int apiver); + __declspec(dllimport) PyObject* PyModule_FromDefAndSpec2(PyModuleDef* def, PyObject* spec, int module_api_version); + __declspec(dllimport) PyObject* Py_CompileString(const char*, const char*, int); + __declspec(dllimport) struct symtable* Py_SymtableString(const char* str, const char* filename, int start); + __declspec(dllimport) void PyErr_Print(void); + __declspec(dllimport) void PyErr_PrintEx(int); + __declspec(dllimport) void PyErr_Display(PyObject*, PyObject*, PyObject*); + __declspec(dllimport) void Py_Initialize(void); + __declspec(dllimport) void Py_InitializeEx(int); + __declspec(dllimport) void Py_Finalize(void); + __declspec(dllimport) int Py_FinalizeEx(void); + __declspec(dllimport) int Py_IsInitialized(void); + __declspec(dllimport) PyThreadState* Py_NewInterpreter(void); + __declspec(dllimport) void Py_EndInterpreter(PyThreadState*); + __declspec(dllimport) int Py_AtExit(void (*func)(void)); + __declspec(dllimport) void __declspec(noreturn) Py_Exit(int); + __declspec(dllimport) int Py_Main(int argc, wchar_t** argv); + __declspec(dllimport) int Py_BytesMain(int argc, char** argv); + __declspec(dllimport) void Py_SetProgramName(const wchar_t*); + __declspec(dllimport) wchar_t* Py_GetProgramName(void); + __declspec(dllimport) void Py_SetPythonHome(const wchar_t*); + __declspec(dllimport) wchar_t* Py_GetPythonHome(void); + __declspec(dllimport) wchar_t* Py_GetProgramFullPath(void); + __declspec(dllimport) wchar_t* Py_GetPrefix(void); + __declspec(dllimport) wchar_t* Py_GetExecPrefix(void); + __declspec(dllimport) wchar_t* Py_GetPath(void); + __declspec(dllimport) void Py_SetPath(const wchar_t*); + __declspec(dllimport) const char* Py_GetVersion(void); + __declspec(dllimport) const char* Py_GetPlatform(void); + __declspec(dllimport) const char* Py_GetCopyright(void); + __declspec(dllimport) const char* Py_GetCompiler(void); + __declspec(dllimport) const char* Py_GetBuildInfo(void); + __declspec(dllimport) PyOS_sighandler_t PyOS_getsig(int); + __declspec(dllimport) PyOS_sighandler_t PyOS_setsig(int, PyOS_sighandler_t); + __declspec(dllimport) PyObject* PyEval_GetBuiltins(void); + __declspec(dllimport) PyObject* PyEval_GetGlobals(void); + __declspec(dllimport) PyObject* PyEval_GetLocals(void); + __declspec(dllimport) PyFrameObject* PyEval_GetFrame(void); + __declspec(dllimport) int Py_AddPendingCall(int (*func)(void*), void* arg); + __declspec(dllimport) int Py_MakePendingCalls(void); + __declspec(dllimport) void Py_SetRecursionLimit(int); + __declspec(dllimport) int Py_GetRecursionLimit(void); + __declspec(dllimport) int Py_EnterRecursiveCall(const char* where); + __declspec(dllimport) void Py_LeaveRecursiveCall(void); + __declspec(dllimport) const char* PyEval_GetFuncName(PyObject*); + __declspec(dllimport) const char* PyEval_GetFuncDesc(PyObject*); + __declspec(dllimport) PyObject* PyEval_EvalFrame(PyFrameObject*); + __declspec(dllimport) PyObject* PyEval_EvalFrameEx(PyFrameObject* f, int exc); + __declspec(dllimport) PyThreadState* PyEval_SaveThread(void); + __declspec(dllimport) void PyEval_RestoreThread(PyThreadState*); + __declspec(dllimport) void PyEval_ReleaseLock(void); + __declspec(dllimport) void PyEval_AcquireThread(PyThreadState* tstate); + __declspec(dllimport) void PyEval_ReleaseThread(PyThreadState* tstate); + __declspec(dllimport) PyObject* PySys_GetObject(const char*); + __declspec(dllimport) int PySys_SetObject(const char*, PyObject*); + __declspec(dllimport) void PySys_SetArgv(int, wchar_t**); + __declspec(dllimport) void PySys_SetArgvEx(int, wchar_t**, int); + __declspec(dllimport) void PySys_SetPath(const wchar_t*); + __declspec(dllimport) void PySys_WriteStdout(const char* format, ...); + __declspec(dllimport) void PySys_WriteStderr(const char* format, ...); + __declspec(dllimport) void PySys_FormatStdout(const char* format, ...); + __declspec(dllimport) void PySys_FormatStderr(const char* format, ...); + __declspec(dllimport) void PySys_ResetWarnOptions(void); + __declspec(dllimport) void PySys_AddWarnOption(const wchar_t*); + __declspec(dllimport) void PySys_AddWarnOptionUnicode(PyObject*); + __declspec(dllimport) int PySys_HasWarnOptions(void); + __declspec(dllimport) void PySys_AddXOption(const wchar_t*); + __declspec(dllimport) PyObject* PySys_GetXOptions(void); + __declspec(dllimport) PyObject* PyOS_FSPath(PyObject* path); + __declspec(dllimport) int PyOS_InterruptOccurred(void); + __declspec(dllimport) void PyOS_InitInterrupts(void); + __declspec(dllimport) long PyImport_GetMagicNumber(void); + __declspec(dllimport) const char* PyImport_GetMagicTag(void); + __declspec(dllimport) PyObject* PyImport_ExecCodeModule(const char* name, PyObject* co); + __declspec(dllimport) PyObject* PyImport_ExecCodeModuleEx(const char* name, PyObject* co, const char* pathname); + __declspec(dllimport) PyObject* PyImport_ExecCodeModuleWithPathnames(const char* name, PyObject* co, const char* pathname, const char* cpathname); + __declspec(dllimport) PyObject* PyImport_ExecCodeModuleObject(PyObject* name, PyObject* co, PyObject* pathname, PyObject* cpathname); + __declspec(dllimport) PyObject* PyImport_GetModuleDict(void); + __declspec(dllimport) PyObject* PyImport_GetModule(PyObject* name); + __declspec(dllimport) PyObject* PyImport_AddModuleObject(PyObject* name); + __declspec(dllimport) PyObject* PyImport_AddModule(const char* name); + __declspec(dllimport) PyObject* PyImport_ImportModule(const char* name); + __declspec(dllimport) PyObject* PyImport_ImportModuleNoBlock(const char* name); + __declspec(dllimport) PyObject* PyImport_ImportModuleLevel(const char* name, PyObject* globals, PyObject* locals, PyObject* fromlist, int level); + __declspec(dllimport) PyObject* PyImport_ImportModuleLevelObject( + PyObject* name, + PyObject* globals, + PyObject* locals, + PyObject* fromlist, + int level + ); + __declspec(dllimport) PyObject* PyImport_GetImporter(PyObject* path); + __declspec(dllimport) PyObject* PyImport_Import(PyObject* name); + __declspec(dllimport) PyObject* PyImport_ReloadModule(PyObject* m); + __declspec(dllimport) int PyImport_ImportFrozenModuleObject( + PyObject* name + ); + __declspec(dllimport) int PyImport_ImportFrozenModule( + const char* name + ); + __declspec(dllimport) int PyImport_AppendInittab( + const char* name, + PyObject* (*initfunc)(void) + ); + __declspec(dllimport) PyObject* PyObject_CallNoArgs(PyObject* func); + __declspec(dllimport) PyObject* PyObject_Call(PyObject* callable, + PyObject* args, PyObject* kwargs); + __declspec(dllimport) PyObject* PyObject_CallObject(PyObject* callable, + PyObject* args); + __declspec(dllimport) PyObject* PyObject_CallFunction(PyObject* callable, + const char* format, ...); + __declspec(dllimport) PyObject* PyObject_CallMethod(PyObject* obj, + const char* name, + const char* format, ...); + __declspec(dllimport) PyObject* PyObject_CallFunctionObjArgs(PyObject* callable, + ...); + __declspec(dllimport) PyObject* PyObject_CallMethodObjArgs( + PyObject* obj, + PyObject* name, + ...); + __declspec(dllimport) PyObject* PyObject_Type(PyObject* o); + __declspec(dllimport) Py_ssize_t PyObject_Size(PyObject* o); + __declspec(dllimport) Py_ssize_t PyObject_Length(PyObject* o); + __declspec(dllimport) PyObject* PyObject_GetItem(PyObject* o, PyObject* key); + __declspec(dllimport) int PyObject_SetItem(PyObject* o, PyObject* key, PyObject* v); + __declspec(dllimport) int PyObject_DelItemString(PyObject* o, const char* key); + __declspec(dllimport) int PyObject_DelItem(PyObject* o, PyObject* key); + __declspec(dllimport) PyObject* PyObject_Format(PyObject* obj, PyObject* format_spec); + __declspec(dllimport) PyObject* PyObject_GetIter(PyObject*); + __declspec(dllimport) int PyIter_Check(PyObject*); + __declspec(dllimport) PyObject* PyIter_Next(PyObject*); + __declspec(dllimport) int PyNumber_Check(PyObject* o); + __declspec(dllimport) PyObject* PyNumber_Add(PyObject* o1, PyObject* o2); + __declspec(dllimport) PyObject* PyNumber_Subtract(PyObject* o1, PyObject* o2); + __declspec(dllimport) PyObject* PyNumber_Multiply(PyObject* o1, PyObject* o2); + __declspec(dllimport) PyObject* PyNumber_MatrixMultiply(PyObject* o1, PyObject* o2); + __declspec(dllimport) PyObject* PyNumber_FloorDivide(PyObject* o1, PyObject* o2); + __declspec(dllimport) PyObject* PyNumber_TrueDivide(PyObject* o1, PyObject* o2); + __declspec(dllimport) PyObject* PyNumber_Remainder(PyObject* o1, PyObject* o2); + __declspec(dllimport) PyObject* PyNumber_Divmod(PyObject* o1, PyObject* o2); + __declspec(dllimport) PyObject* PyNumber_Power(PyObject* o1, PyObject* o2, PyObject* o3); + __declspec(dllimport) PyObject* PyNumber_Negative(PyObject* o); + __declspec(dllimport) PyObject* PyNumber_Positive(PyObject* o); + __declspec(dllimport) PyObject* PyNumber_Absolute(PyObject* o); + __declspec(dllimport) PyObject* PyNumber_Invert(PyObject* o); + __declspec(dllimport) PyObject* PyNumber_Lshift(PyObject* o1, PyObject* o2); + __declspec(dllimport) PyObject* PyNumber_Rshift(PyObject* o1, PyObject* o2); + __declspec(dllimport) PyObject* PyNumber_And(PyObject* o1, PyObject* o2); + __declspec(dllimport) PyObject* PyNumber_Xor(PyObject* o1, PyObject* o2); + __declspec(dllimport) PyObject* PyNumber_Or(PyObject* o1, PyObject* o2); + __declspec(dllimport) int PyIndex_Check(PyObject*); + __declspec(dllimport) PyObject* PyNumber_Index(PyObject* o); + __declspec(dllimport) Py_ssize_t PyNumber_AsSsize_t(PyObject* o, PyObject* exc); + __declspec(dllimport) PyObject* PyNumber_Long(PyObject* o); + __declspec(dllimport) PyObject* PyNumber_Float(PyObject* o); + __declspec(dllimport) PyObject* PyNumber_InPlaceAdd(PyObject* o1, PyObject* o2); + __declspec(dllimport) PyObject* PyNumber_InPlaceSubtract(PyObject* o1, PyObject* o2); + __declspec(dllimport) PyObject* PyNumber_InPlaceMultiply(PyObject* o1, PyObject* o2); + __declspec(dllimport) PyObject* PyNumber_InPlaceMatrixMultiply(PyObject* o1, PyObject* o2); + __declspec(dllimport) PyObject* PyNumber_InPlaceFloorDivide(PyObject* o1, PyObject* o2); + __declspec(dllimport) PyObject* PyNumber_InPlaceTrueDivide(PyObject* o1, PyObject* o2); + __declspec(dllimport) PyObject* PyNumber_InPlaceRemainder(PyObject* o1, PyObject* o2); + __declspec(dllimport) PyObject* PyNumber_InPlacePower(PyObject* o1, PyObject* o2, PyObject* o3); + __declspec(dllimport) PyObject* PyNumber_InPlaceLshift(PyObject* o1, PyObject* o2); + __declspec(dllimport) PyObject* PyNumber_InPlaceRshift(PyObject* o1, PyObject* o2); + __declspec(dllimport) PyObject* PyNumber_InPlaceAnd(PyObject* o1, PyObject* o2); + __declspec(dllimport) PyObject* PyNumber_InPlaceXor(PyObject* o1, PyObject* o2); + __declspec(dllimport) PyObject* PyNumber_InPlaceOr(PyObject* o1, PyObject* o2); + __declspec(dllimport) PyObject* PyNumber_ToBase(PyObject* n, int base); + __declspec(dllimport) int PySequence_Check(PyObject* o); + __declspec(dllimport) Py_ssize_t PySequence_Size(PyObject* o); + __declspec(dllimport) Py_ssize_t PySequence_Length(PyObject* o); + __declspec(dllimport) PyObject* PySequence_Concat(PyObject* o1, PyObject* o2); + __declspec(dllimport) PyObject* PySequence_Repeat(PyObject* o, Py_ssize_t count); + __declspec(dllimport) PyObject* PySequence_GetItem(PyObject* o, Py_ssize_t i); + __declspec(dllimport) PyObject* PySequence_GetSlice(PyObject* o, Py_ssize_t i1, Py_ssize_t i2); + __declspec(dllimport) int PySequence_SetItem(PyObject* o, Py_ssize_t i, PyObject* v); + __declspec(dllimport) int PySequence_DelItem(PyObject* o, Py_ssize_t i); + __declspec(dllimport) int PySequence_SetSlice(PyObject* o, Py_ssize_t i1, Py_ssize_t i2, PyObject* v); + __declspec(dllimport) int PySequence_DelSlice(PyObject* o, Py_ssize_t i1, Py_ssize_t i2); + __declspec(dllimport) PyObject* PySequence_Tuple(PyObject* o); + __declspec(dllimport) PyObject* PySequence_List(PyObject* o); + __declspec(dllimport) PyObject* PySequence_Fast(PyObject* o, const char* m); + __declspec(dllimport) Py_ssize_t PySequence_Count(PyObject* o, PyObject* value); + __declspec(dllimport) int PySequence_Contains(PyObject* seq, PyObject* ob); + __declspec(dllimport) int PySequence_In(PyObject* o, PyObject* value); + __declspec(dllimport) Py_ssize_t PySequence_Index(PyObject* o, PyObject* value); + __declspec(dllimport) PyObject* PySequence_InPlaceConcat(PyObject* o1, PyObject* o2); + __declspec(dllimport) PyObject* PySequence_InPlaceRepeat(PyObject* o, Py_ssize_t count); + __declspec(dllimport) int PyMapping_Check(PyObject* o); + __declspec(dllimport) Py_ssize_t PyMapping_Size(PyObject* o); + __declspec(dllimport) Py_ssize_t PyMapping_Length(PyObject* o); + __declspec(dllimport) int PyMapping_HasKeyString(PyObject* o, const char* key); + __declspec(dllimport) int PyMapping_HasKey(PyObject* o, PyObject* key); + __declspec(dllimport) PyObject* PyMapping_Keys(PyObject* o); + __declspec(dllimport) PyObject* PyMapping_Values(PyObject* o); + __declspec(dllimport) PyObject* PyMapping_Items(PyObject* o); + __declspec(dllimport) PyObject* PyMapping_GetItemString(PyObject* o, const char* key); + __declspec(dllimport) int PyMapping_SetItemString(PyObject* o, const char* key, PyObject* value); + __declspec(dllimport) int PyObject_IsInstance(PyObject* object, PyObject* typeorclass); + __declspec(dllimport) int PyObject_IsSubclass(PyObject* object, PyObject* typeorclass); + __declspec(dllimport) PyObject* PyEval_EvalCode(PyObject*, PyObject*, PyObject*); + __declspec(dllimport) PyObject* PyEval_EvalCodeEx(PyObject* co, PyObject* globals, PyObject* locals, PyObject* const* args, int argc, PyObject* const* kwds, int kwdc, PyObject* const* defs, int defc, PyObject* kwdefs, PyObject* closure); + __declspec(dllimport) double PyOS_string_to_double(const char* str, char** endptr, PyObject* overflow_exception); + __declspec(dllimport) char* PyOS_double_to_string(double val, char format_code, int precision, int flags, int* type); + __declspec(dllimport) int PyOS_mystrnicmp(const char*, const char*, Py_ssize_t); + __declspec(dllimport) int PyOS_mystricmp(const char*, const char*); + __declspec(dllimport) wchar_t* Py_DecodeLocale(const char* arg, size_t* size); + __declspec(dllimport) char* Py_EncodeLocale(const wchar_t* text, size_t* error_pos); +} diff --git a/DeviceAdapters/PyDevice/tests/camera.cfg b/DeviceAdapters/PyDevice/tests/camera.cfg new file mode 100644 index 000000000..b807a8083 --- /dev/null +++ b/DeviceAdapters/PyDevice/tests/camera.cfg @@ -0,0 +1,41 @@ +# Generated by Configurator on Fri Dec 15 14:02:01 CET 2023 + +# Reset +Property,Core,Initialize,0 + +# Devices +Device,PyHub,PyDevice,PyHub +Device,cam,PyDevice,Camera:cam + +# Pre-init settings for devices +Property,PyHub,PythonEnvironment,(auto) +Property,PyHub,ScriptPath,../examples/camera.py +# Pre-init settings for COM ports + +# Hub (parent) references +Parent,cam,PyHub + +# Initialize +Property,Core,Initialize,1 + +# Delays + +# Focus directions + +# Roles +Property,Core,Camera,cam +Property,Core,AutoShutter,1 +# Camera-synchronized devices + +# Labels + +# Configuration presets +# Group: Channel + +# Group: System +# Preset: Startup + + + +# PixelSize settings + diff --git a/DeviceAdapters/PyDevice/tests/device.cfg b/DeviceAdapters/PyDevice/tests/device.cfg new file mode 100644 index 000000000..5e756b18a --- /dev/null +++ b/DeviceAdapters/PyDevice/tests/device.cfg @@ -0,0 +1,42 @@ +# Generated by Configurator on Mon Apr 08 10:19:31 CEST 2024 + +# Reset +Property,Core,Initialize,0 + +# Devices +Device,PyHub,PyDevice,PyHub +Device,some_device,PyDevice,Device:some_device + +# Pre-init settings for devices +Property,PyHub,PythonEnvironment,(auto) +Property,PyHub,ScriptPath,../examples/device.py + +# Pre-init settings for COM ports + +# Hub (parent) references +Parent,some_device,PyHub + +# Initialize +Property,Core,Initialize,1 + +# Delays + +# Focus directions + +# Roles +Property,Core,AutoShutter,1 + +# Camera-synchronized devices + +# Labels + +# Configuration presets +# Group: Channel + +# Group: System +# Preset: Startup + + + +# PixelSize settings + diff --git a/DeviceAdapters/PyDevice/tests/fixture.py b/DeviceAdapters/PyDevice/tests/fixture.py new file mode 100644 index 000000000..626a2f798 --- /dev/null +++ b/DeviceAdapters/PyDevice/tests/fixture.py @@ -0,0 +1,210 @@ +"""Collection of device classes for testing""" +from enum import Enum +from typing import Annotated + +import annotated_types +import astropy.units as u +import os +import sys + +import numpy as np + +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + + +class Options(Enum): + A = 'option a' + B = 'option b' + C = 'option c' + + +class GenericDevice: + def __init__(self): + self._float_value = 0.0 + self._int_value = 0 + self._string_value = "" + self._bool_value = False + self._enum_value = Options.A + self._meters = 0.0 * u.m + self._millimeters = 0.0 * u.mm + self._annotated_unit = 0.0 + self._ranged_int = 0 + + @property + def read_only_float(self) -> float: + return 1.1 + + @property + def read_only_int(self) -> int: + return 2 + + @property + def read_only_string(self) -> str: + return 'str' + + @property + def read_only_enum(self) -> Options: + return Options.B + + @property + def read_only_bool(self) -> bool: + return True + + @property + def read_write_float(self) -> float: + return self._float_value + + @read_write_float.setter + def read_write_float(self, value): + self._float_value = value + + @property + def read_write_int(self) -> int: + return self._int_value + + @read_write_int.setter + def read_write_int(self, value): + self._int_value = value + + @property + def read_write_string(self) -> str: + return self._string_value + + @read_write_string.setter + def read_write_string(self, value): + self._string_value = value + + @property + def read_write_enum(self) -> Options: + return self._enum_value + + @read_write_enum.setter + def read_write_enum(self, value): + self._enum_value = value + + @property + def read_write_bool(self) -> bool: + return self._bool_value + + @read_write_bool.setter + def read_write_bool(self, value): + self._bool_value = value + + @property + def meters(self) -> u.Quantity[u.m]: + return self._meters + + @meters.setter + def meters(self, value: u.Quantity[u.m]): + self._meters = value.to(u.m) + + @property + def millimeters(self) -> u.Quantity[u.mm]: + return self._millimeters + + @millimeters.setter + def millimeters(self, value: u.Quantity[u.mm]): + self._millimeters = value.to(u.mm) + + @property + def annotated_unit(self) -> Annotated[int, annotated_types.Unit("s")]: + return self._annotated_unit + + @annotated_unit.setter + def annotated_unit(self, value): + self._annotated_unit = int(value) + + @property + def ranged_int(self) -> Annotated[int, annotated_types.Ge(1), annotated_types.Le(42)]: + return self._ranged_int + + @ranged_int.setter + def ranged_int(self, value): + self._ranged_int = int(value) + + @property + def not_detected(self): + """This property should not be detected because it has no type annotation""" + return 0 + + @not_detected.setter + def not_detected(self, value): + pass + + def add_one(self, a: int) -> int: + return a + 1 + + +class Camera1: + def __init__(self): + self._shutter_ms = 0.0 + self._top = 0 + self._left = 0 + self._height = 0 + self._width = 0 + + @property + def exposure_ms(self) -> float: + return self._shutter_ms + + @exposure_ms.setter + def exposure_ms(self, value): + self._shutter_ms = value + + @property + def top(self) -> int: + return self._top + + @top.setter + def top(self, value): + self._top = value + + @property + def left(self) -> int: + return self._left + + @left.setter + def left(self, value): + self._left = value + + @property + def height(self) -> int: + return self._height + + @height.setter + def height(self, value): + self._height = value + + @property + def width(self) -> int: + return self._width + + @width.setter + def width(self, value): + self._width = value + + def read(self) -> np.ndarray: + return np.zeros((self._height, self._width), dtype=np.uint16) + + def busy(self) -> bool: + return False + + +class GenericDeviceDirect: + float_value: float + int_value: int + string_value: str + bool_value: bool + enum_value: Options + meters: u.Quantity[u.m] + millimeters: u.Quantity[u.mm] + not_detected = 0 # This property should not be detected because it has no type annotation + + def __init__(self): + self._float_value = 0.0 + self._int_value = 0 + self._string_value = "" + self._bool_value = False + self._enum_value = Options.A + self._meters = 0.0 * u.m + self._millimeters = 0.0 * u.mm diff --git a/DeviceAdapters/PyDevice/tests/microscope.cfg b/DeviceAdapters/PyDevice/tests/microscope.cfg new file mode 100644 index 000000000..bdd4671b5 --- /dev/null +++ b/DeviceAdapters/PyDevice/tests/microscope.cfg @@ -0,0 +1,23 @@ +# Reset +Property,Core,Initialize,0 + +# Devices +Device,PyHub,PyDevice,PyHub +Device,camera,PyDevice,Camera:camera +Device,stage,PyDevice,XYStage:stage + +# Pre-init settings for devices +Property,PyHub,PythonEnvironment,(auto) +Property,PyHub,ScriptPath,../examples/microscope.py + +# Hub (parent) references +Parent,camera,PyHub +Parent,stage,PyHub + +# Initialize +Property,Core,Initialize,1 + +# Roles +Property,Core,Camera,camera +Property,Core,XYStage,stage +Property,Core,AutoShutter,1 diff --git a/DeviceAdapters/PyDevice/tests/test_pymmcore.py b/DeviceAdapters/PyDevice/tests/test_pymmcore.py new file mode 100644 index 000000000..b181d5013 --- /dev/null +++ b/DeviceAdapters/PyDevice/tests/test_pymmcore.py @@ -0,0 +1,38 @@ +import pymmcore +import os + +"""Tests if PyDevice can be used from pymmcore correctly. +Note that these tests use the currently installed device adapter, which has `bootstrap.py` inlined. +Therefore, if `bootstrap.py` is changed, the device adapter should be recompiled. +""" + +mm_dir = "C:/Program Files/Micro-Manager-2.0/" +os.environ["PATH"] += os.pathsep.join(["", mm_dir]) # advisable on Windows + + +def test_generic_device(): + mmc = pymmcore.CMMCore() + mmc.setDeviceAdapterSearchPaths([mm_dir]) + mmc.loadSystemConfiguration("device.cfg") + assert mmc.getProperty("some_device", "Integer") == '4' + assert mmc.getPropertyLowerLimit("some_device", "FloatingPoint") == 0.0 + assert mmc.getPropertyUpperLimit("some_device", "FloatingPoint") == 1.0 + + +def test_camera(): + mmc = pymmcore.CMMCore() + mmc.setDeviceAdapterSearchPaths([mm_dir]) + mmc.loadSystemConfiguration("camera.cfg") + mmc.setProperty("cam", "Width", 121) + mmc.setProperty("cam", "Height", 333) + mmc.snapImage() + frame = mmc.getImage() + assert frame.shape == (333, 121) + + +def test_microscope(): + mmc = pymmcore.CMMCore() + mmc.setDeviceAdapterSearchPaths([mm_dir]) + mmc.loadSystemConfiguration("microscope.cfg") + mmc.snapImage() + frame = mmc.getImage() diff --git a/DeviceAdapters/PyDevice/tests/test_reflection.py b/DeviceAdapters/PyDevice/tests/test_reflection.py new file mode 100644 index 000000000..f955dfdb2 --- /dev/null +++ b/DeviceAdapters/PyDevice/tests/test_reflection.py @@ -0,0 +1,121 @@ +"""Tests if `bootstrap.py` recognizes device types correctly and extracts all properties""" +from fixture import * +from bootstrap import PyDevice + + +def test_properties(): + device = GenericDevice() + pydevice = PyDevice(device) + properties = pydevice.properties + + assert pydevice.device_type == 'Device' + + assert properties[0].python_name == 'read_only_float' + assert properties[0].mm_name == 'ReadOnlyFloat' + assert properties[0].data_type == 'float' + assert properties[0].set is None + assert properties[0].min is None + assert properties[0].max is None + assert properties[0].options is None + assert properties[0].unit is None + + assert properties[1].python_name == 'read_only_int' + assert properties[1].mm_name == 'ReadOnlyInt' + assert properties[1].data_type == 'int' + + assert properties[2].python_name == 'read_only_string' + assert properties[2].mm_name == 'ReadOnlyString' + assert properties[2].data_type == 'str' + + assert properties[3].python_name == 'read_only_enum' + assert properties[3].mm_name == 'ReadOnlyEnum' + assert properties[3].data_type == 'enum' + assert properties[3].options == {'A': Options.A, 'B': Options.B, 'C': Options.C} + + assert properties[4].python_name == 'read_only_bool' + assert properties[4].mm_name == 'ReadOnlyBool' + assert properties[4].data_type == 'bool' + + assert properties[5].python_name == 'read_write_float' + assert properties[5].mm_name == 'ReadWriteFloat' + assert properties[5].data_type == 'float' + assert properties[5].set is not None + device.read_write_float = 0.8 + assert properties[5].get() == 0.8 + properties[5].set(1.1) + assert properties[5].get() == 1.1 + assert device.read_write_float == 1.1 + + # if a property has a unit attached, a _X suffix is added to the name + # using the getter and setter from the PyProperty object, we can set + # the value as a float, implicitly converting it to the specified unit. + # we can also still use the properties on the object directly from Python, + # where the value is set and returned with the specified astropy unit attached. + # + p_meters = properties[10] + assert p_meters.python_name == 'meters' + assert p_meters.mm_name == 'Meters-m' + assert p_meters.data_type == 'float' + assert p_meters.unit == u.m + p_meters.set(9.0) + assert p_meters.get() == 9.0 + assert device.meters == 9 * u.m + device.meters = 100 * u.mm + assert p_meters.get() == 0.1 + + p_millimeters = properties[11] + assert p_millimeters.python_name == 'millimeters' + assert p_millimeters.mm_name == 'Millimeters-mm' + assert p_millimeters.data_type == 'float' + assert p_millimeters.unit == 'mm' + p_millimeters.set(12) + assert p_millimeters.get() == 12 + assert device.millimeters == 12 * u.mm + + p_annotated_unit = properties[12] + assert p_annotated_unit.python_name == 'annotated_unit' + assert p_annotated_unit.mm_name == 'AnnotatedUnit-s' + assert p_annotated_unit.data_type == 'int' + assert p_annotated_unit.unit == 's' + p_annotated_unit.set(1.5) + assert p_annotated_unit.get() == 1 # converted to int + + p_ranged_int = properties[13] + assert p_ranged_int.python_name == 'ranged_int' + assert p_ranged_int.mm_name == 'RangedInt' + assert p_ranged_int.data_type == 'int' + assert p_ranged_int.min == 1 + assert p_ranged_int.max == 42 + + assert len(properties) == 14 # don't detect anything else + + assert pydevice.methods['add_one'](5) == 6 + + +def test_camera(): + """Checks if a camera object is detected correctly""" + cam = Camera1() + pydevice = PyDevice(cam) + properties = pydevice.properties + + assert pydevice.device_type == 'Camera' + assert properties[-1].python_name == 'binning' + assert properties[-1].mm_name == 'Binning' + assert properties[0].mm_name == 'Exposure-ms' + + # test if the binning property works + assert properties[-1].get() == 1 + + +def test_direct(): + device = GenericDeviceDirect() + pydevice = PyDevice(device) + properties = pydevice.properties + assert properties[0].python_name == 'float_value' + assert properties[1].python_name == 'int_value' + assert properties[2].python_name == 'string_value' + assert properties[3].python_name == 'bool_value' + assert properties[4].python_name == 'enum_value' + assert properties[5].python_name == 'meters' + assert properties[6].python_name == 'millimeters' + assert len(properties) == 7 diff --git a/DeviceAdapters/QSI/QSICameraAdapter.cpp b/DeviceAdapters/QSI/QSICameraAdapter.cpp index c52873ff5..0ac9ff97b 100644 --- a/DeviceAdapters/QSI/QSICameraAdapter.cpp +++ b/DeviceAdapters/QSI/QSICameraAdapter.cpp @@ -480,7 +480,7 @@ int QSICameraAdapter::InsertImage() // Assemble metadata this->GetLabel( label ); - metadata.put( "Camera", label ); + metadata.put(MM::g_Keyword_Metadata_CameraLabel, label ); pSerializedMetadata = metadata.Serialize().c_str(); diff --git a/DeviceAdapters/RaptorEPIX/RaptorEPIX.cpp b/DeviceAdapters/RaptorEPIX/RaptorEPIX.cpp index 267d57358..571859adb 100644 --- a/DeviceAdapters/RaptorEPIX/RaptorEPIX.cpp +++ b/DeviceAdapters/RaptorEPIX/RaptorEPIX.cpp @@ -2524,7 +2524,7 @@ int CRaptorEPIX::Initialize() case PIXCI_D32: osModel << "PIXCI(R) D32 Imaging Board"; break ; case PIXCI_D2X: osModel << "PIXCI(R) D2X Imaging Board"; break ; case PIXCI_D3X: osModel << "PIXCI(R) D3X Imaging Board"; break ; - case PIXCI_D3XE: osModel << "PIXCI® D3XE Frame Grabber"; break ; + case PIXCI_D3XE: osModel << "PIXCI® D3XE Frame Grabber"; break ; case PIXCI_E1: osModel << "PIXCI(R) E1 Imaging Board"; break ; case PIXCI_E1DB: osModel << "PIXCI(R) E1DB Imaging Board"; break ; case PIXCI_E4: osModel << "PIXCI(R) E4 Imaging Board"; break ; @@ -5885,7 +5885,7 @@ int CRaptorEPIX::InsertImage() md.PutTag(mst.GetName(), mst.GetDevice(), mst.GetValue()); } */ - md.put("Camera", label); + md.put(MM::g_Keyword_Metadata_CameraLabel, label); md.put(MM::g_Keyword_Elapsed_Time_ms, CDeviceUtils::ConvertToString((timeStamp - sequenceStartTime_).getMsec())); //md.put(MM::g_Keyword_Elapsed_Time_ms, CDeviceUtils::ConvertToString(fieldCount_)); md.put(MM::g_Keyword_Metadata_ImageNumber, CDeviceUtils::ConvertToString(imageCounter_)); diff --git a/DeviceAdapters/Scientifica/Scientifica.cpp b/DeviceAdapters/Scientifica/Scientifica.cpp index 504056e97..d6603cdf4 100644 --- a/DeviceAdapters/Scientifica/Scientifica.cpp +++ b/DeviceAdapters/Scientifica/Scientifica.cpp @@ -141,6 +141,7 @@ int XYStage::Initialize() if (baud == 9600) { stepSizeXUm_ = 0.1; + stepSizeYUm_ = 0.1; // Step size pAct = new CPropertyAction(this, &XYStage::OnStepSizeX); CreateProperty("StepSizeX_um", "0.1", MM::Float, true, pAct); @@ -151,6 +152,7 @@ int XYStage::Initialize() else { stepSizeXUm_ = 0.01; + stepSizeYUm_ = 0.01; // Step size pAct = new CPropertyAction(this, &XYStage::OnStepSizeX); CreateProperty("StepSizeX_um", "0.01", MM::Float, true, pAct); diff --git a/DeviceAdapters/ScientificaMotion8/ScientificaMotion8.cpp b/DeviceAdapters/ScientificaMotion8/ScientificaMotion8.cpp new file mode 100644 index 000000000..627ac0bc9 --- /dev/null +++ b/DeviceAdapters/ScientificaMotion8/ScientificaMotion8.cpp @@ -0,0 +1,1104 @@ +/////////////////////////////////////////////////////////////////////////////// +// FILE: ScientificaMotion8.h +// PROJECT: Micro-Manager +// SUBSYSTEM: DeviceAdapters +//----------------------------------------------------------------------------- +// DESCRIPTION: Scientifica Motion 8 rack adapter +// COPYRIGHT: University of California, San Francisco, 2006 +// 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. +// +// AUTHOR: Nenad Amodaj, nenad@amodaj.com, 06/01/2006 +// +// Scientifica Specific Parts +// AUTHOR: Matthew Player (ElecSoft Solutions) + +#include "ScientificaMotion8.h" +#include "ScientificaRxPacket.h" + +#include "ModuleInterface.h" +#include +#include +#include + +#ifdef WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif + +#define AXIS_X 0 +#define AXIS_Y 1 +#define AXIS_Z 2 +#define AXIS_F 6 + +const char* g_DeviceNameM8Hub = "Scientifica-Motion8-Hub"; +const char* g_DeviceNameM8XY_Device1 = "Scientifica-Motion8-XY_Device_1"; +const char* g_DeviceNameM8Z_Device1 = "Scientifica-Motion8-Z_Device_1"; +const char* g_DeviceNameM8XY_Device2 = "Scientifica-Moiton8-XY_Device_2"; +const char* g_DeviceNameM8Z_Device2 = "Scientifica-Moition8-Z_Device_2"; +const char* g_DeviceNameFilter_Device1 = "Scientifica-Motion8-Filter_Device_1"; +const char* g_DeviceNameFilter_Device2 = "Scientifica-Motion8-Filter_Device_2"; + +// static lock +MMThreadLock ScientificaMotion8Hub::lock_; + +/////////////////////////////////////////////////////////////////////////////// +// Exported MMDevice API +/////////////////////////////////////////////////////////////////////////////// +MODULE_API void InitializeModuleData() +{ + RegisterDevice(g_DeviceNameM8Hub, MM::HubDevice, "Hub (required)"); + RegisterDevice(g_DeviceNameM8XY_Device1, MM::XYStageDevice, "XY Stage (Device 1)"); + RegisterDevice(g_DeviceNameM8Z_Device1, MM::StageDevice, "Z Stage (Device 1)"); + RegisterDevice(g_DeviceNameM8XY_Device2, MM::XYStageDevice, "XY Stage (Device 2)"); + RegisterDevice(g_DeviceNameM8Z_Device2, MM::StageDevice, "Z Stage (Device 2)"); + RegisterDevice(g_DeviceNameFilter_Device1, MM::StateDevice, "Filter Wheel (Device 1)"); + RegisterDevice(g_DeviceNameFilter_Device2, MM::StateDevice, "Filter Wheel (Device 2)"); +} + +MODULE_API MM::Device* CreateDevice(const char* deviceName) +{ + if (deviceName == 0) + return 0; + + if (strcmp(deviceName, g_DeviceNameM8Hub) == 0) + { + return new ScientificaMotion8Hub; + } + else if (strcmp(deviceName, g_DeviceNameM8XY_Device1) == 0) + { + return new M8XYStage(0); + } + else if (strcmp(deviceName, g_DeviceNameM8Z_Device1) == 0) + { + return new M8ZStage(0); + } + else if (strcmp(deviceName, g_DeviceNameM8XY_Device2) == 0) + { + return new M8XYStage(1); + } + else if (strcmp(deviceName, g_DeviceNameM8Z_Device2) == 0) + { + return new M8ZStage(1); + } + else if (strcmp(deviceName, g_DeviceNameFilter_Device1) == 0) + { + return new M8FilterCubeTurret(0); + } + else if (strcmp(deviceName, g_DeviceNameFilter_Device2) == 0) + { + return new M8FilterCubeTurret(1); + } + + return 0; +} + +MODULE_API void DeleteDevice(MM::Device* pDevice) +{ + delete pDevice; +} + +/////////////////////////////////////////////////////////////////////////////// +// M8XYStage implementation +/////////////////////////////////////////////////////////////////////////////// + +ScientificaMotion8Hub::ScientificaMotion8Hub() : + initialized_ (false) +{ + InitializeDefaultErrorMessages(); + + // Parent ID display + CreateHubIDProperty(); + + CPropertyAction* pAct = new CPropertyAction(this, &ScientificaMotion8Hub::OnPort); + CreateProperty(MM::g_Keyword_Port, "Undefined", MM::String, false, pAct, true); + + device_1_x_channel_ = 0xFF; + device_1_y_channel_ = 0xFF; + device_1_z_channel_ = 0xFF; + device_1_f_channel_ = 0xFF; + + device_2_x_channel_ = 0xFF; + device_2_y_channel_ = 0xFF; + device_2_z_channel_ = 0xFF; + device_2_f_channel_ = 0xFF; +} + +ScientificaMotion8Hub::~ScientificaMotion8Hub() +{ + Shutdown(); +} + +void ScientificaMotion8Hub::GetName(char* name) const +{ + CDeviceUtils::CopyLimitedString(name, g_DeviceNameM8Hub); +} + +bool ScientificaMotion8Hub::Busy() +{ + return false; +} + +//Is DetectDevice() implemented +bool ScientificaMotion8Hub::SupportsDeviceDetection(void) +{ + return true; +} + +//Used to automate discovery of correct serial port +MM::DeviceDetectionStatus ScientificaMotion8Hub::DetectDevice(void) +{ + if (initialized_) + return MM::CanCommunicate; + + 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; + + GetCoreCallback()->GetDeviceProperty(port_.c_str(), "AnswerTimeout", answerTO); + + GetCoreCallback()->SetDeviceProperty(port_.c_str(), MM::g_Keyword_BaudRate, "115200"); + GetCoreCallback()->SetDeviceProperty(port_.c_str(), MM::g_Keyword_StopBits, "1"); + + GetCoreCallback()->SetDeviceProperty(port_.c_str(), "AnswerTimeout", "300.0"); + GetCoreCallback()->SetDeviceProperty(port_.c_str(), "DelayBetweenCharsMs", "0"); + + MM::Device* pS = GetCoreCallback()->GetDevice(this, port_.c_str()); + pS->Initialize(); + + CDeviceUtils::SleepMs(1000); + MMThreadGuard myLock(lock_); + PurgeComPort(port_.c_str()); + + std::string version; + bool supportedVersion = CheckControllerVersion(); + if (!supportedVersion) + { + LogMessage("Controller needs updating to 0.9.27 or above. Please use LinLab 3 0.5.16 or above to update the controller."); + result = MM::Misconfigured; + } + else + { + int ret = ReadControllerMap(); + if (ret == DEVICE_OK && + ((device_1_x_channel_ != 0xFF) || (device_1_y_channel_ != 0xFF) || (device_1_z_channel_ != 0xFF) || (device_2_x_channel_ != 0xFF) || (device_2_y_channel_ != 0xFF) || (device_2_z_channel_ != 0xFF))) + { + result = MM::CanCommunicate; + } + } + + pS->Shutdown(); + + GetCoreCallback()->SetDeviceProperty(port_.c_str(), "AnswerTimeout", answerTO); + } + } + catch (...) + { + LogMessage("Exception in DetectDevice!", false); + } + + return result; +} + +ScientificaRxPacket* ScientificaMotion8Hub::WriteRead(ScientificaTxPacket* tx, int expected_length) +{ + ScientificaRxPacket* rx_packet = NULL; + + MMThreadGuard myLock(lock_); + + PurgeComPort(port_.c_str()); + + const unsigned char* data = tx->GetPacketToSend(); + int len = tx->GetEncodedLength(); + + int return_status = WriteToComPort(port_.c_str(), data, len); + + if (return_status != DEVICE_OK) + { + return NULL; + } + + unsigned char rxBuffer[256]; //256 is the maximum size of a packet + int bufferIndex = 0; + unsigned long actualBytesRead = 0; + + MM::MMTime startTime = GetCurrentMMTime(); + bool readZero = false; + + while (!readZero && ((GetCurrentMMTime() - startTime).getMsec() < 250)) + { + return_status = ReadFromComPort(port_.c_str(), &rxBuffer[bufferIndex], 256 - bufferIndex, actualBytesRead); + if (return_status != DEVICE_OK) + break; + + for (unsigned int i = bufferIndex; i < (bufferIndex + actualBytesRead); i++) + { + if (rxBuffer[i] == 0) + { + readZero = true; + break; + } + } + + bufferIndex += actualBytesRead; + } + + if (!readZero) + { + rx_packet = NULL; + } + + if (bufferIndex > 0) + { + rx_packet = new ScientificaRxPacket(rxBuffer, bufferIndex - 1); //rxBuffer is decoded into to rx_packet + + if (rx_packet->RemainingBytes() < expected_length) + { + delete rx_packet; + rx_packet = NULL; + } + } + + return rx_packet; +} + + +bool ScientificaMotion8Hub::CheckControllerVersion() +{ + bool supportedVersion = false; + std::string version; + ScientificaTxPacket* txPacket = new ScientificaTxPacket(0xBB, 0, 1); + ScientificaRxPacket* rxPacket = NULL; + + rxPacket = WriteRead(txPacket, 6); + + if (rxPacket != NULL) + { + uint16_t major; + uint16_t minor; + uint16_t patch; + + rxPacket->GetUInt16(&major); + rxPacket->GetUInt16(&minor); + rxPacket->GetUInt16(&patch); + + if (minor >= 9 || (minor == 9 && patch > 27)) + { + supportedVersion = true; + } + + std::ostringstream oss; + oss << major << "." << minor << "." << patch; + version = oss.str(); + + LogMessage("Controller version: " + version, false); + + delete rxPacket; + } + + return supportedVersion; +} + +int ScientificaMotion8Hub::Stop(uint8_t device) +{ + ScientificaTxPacket* txPacket = new ScientificaTxPacket(0xBB, 2, 7); + ScientificaRxPacket* rxPacket = NULL; + + int ret = DEVICE_OK; + + txPacket->AddUInt8(device); + rxPacket = WriteRead(txPacket, 0); + + if (rxPacket == NULL) + { + ret = DEVICE_SERIAL_TIMEOUT; + } + else + { + delete rxPacket; + } + + return ret; +} + +int ScientificaMotion8Hub::SetPosition(uint8_t device, uint8_t axis, long steps) +{ + ScientificaTxPacket* txPacket = new ScientificaTxPacket(0xBB, 2, 0xC); + txPacket->AddUInt8(device); + txPacket->AddUInt8(axis); + txPacket->AddInt32(steps); + ScientificaRxPacket* rxPacket = NULL; + + int ret = DEVICE_OK; + + rxPacket = WriteRead(txPacket, 0); + + if (rxPacket == NULL) + { + ret = DEVICE_SERIAL_TIMEOUT; + } + else + { + delete rxPacket; + } + + return ret; +} + +int ScientificaMotion8Hub::IsMoving(uint8_t device, bool* is_moving) +{ + if(is_moving == NULL) + return DEVICE_ERR; + + ScientificaTxPacket* txPacket = new ScientificaTxPacket(0xBB, 2, 0xF); + txPacket->AddUInt8(device); + ScientificaRxPacket* rxPacket = NULL; + + int ret = DEVICE_OK; + rxPacket = WriteRead(txPacket, 1); + + if (rxPacket == NULL) + { + ret = DEVICE_SERIAL_TIMEOUT; + } + else + { + uint8_t moving; + rxPacket->GetByte(&moving); + *is_moving = moving != 0; + delete rxPacket; + } + + return ret; +} + +int ScientificaMotion8Hub::ReadControllerMap(void) +{ + unsigned char tx[3]; + tx[0] = 0xBB; + tx[1] = 0; + tx[2] = 6; + + ScientificaTxPacket* txPacket = new ScientificaTxPacket(0xBB, 0, 6); + ScientificaRxPacket* rxPacket = NULL; + + device_1_x_channel_ = 0xFF; + device_1_y_channel_ = 0xFF; + device_1_z_channel_ = 0xFF; + device_1_f_channel_ = 0xFF; + + device_2_x_channel_ = 0xFF; + device_2_y_channel_ = 0xFF; + device_2_z_channel_ = 0xFF; + device_2_f_channel_ = 0xFF; + + + int ret = DEVICE_OK; + rxPacket = WriteRead(txPacket, 16); + + if (rxPacket == NULL) + { + ret = DEVICE_SERIAL_TIMEOUT; + } + else + { + for (uint8_t ch = 0; ch < 8; ch++) + { + uint8_t device; + uint8_t axis; + + rxPacket->GetByte(&device); + rxPacket->GetByte(&axis); + + if (device == 0) + { + if (axis == AXIS_X) + device_1_x_channel_ = ch; + else if (axis == AXIS_Y) + device_1_y_channel_ = ch; + else if (axis == AXIS_Z) + device_1_z_channel_ = ch; + else if (axis == AXIS_F) + device_1_f_channel_ = ch; + + } + else if (device == 1) + { + if (axis == AXIS_X) + device_2_x_channel_ = ch; + else if (axis == AXIS_Y) + device_2_y_channel_ = ch; + else if (axis == AXIS_Z) + device_2_z_channel_ = ch; + else if (axis == AXIS_F) + device_2_f_channel_ = ch; + } + } + + delete rxPacket; + } + + return ret; +} + + +int ScientificaMotion8Hub::Initialize() +{ + // Name + int ret = CreateProperty(MM::g_Keyword_Name, g_DeviceNameM8Hub, MM::String, true); + if (DEVICE_OK != ret) + return ret; + + CDeviceUtils::SleepMs(2000); + + MMThreadGuard myLock(lock_); + + initialized_ = true; + + return DEVICE_OK; +} + + +//Detext and instantiate all avalible chile peripherals +int ScientificaMotion8Hub::DetectInstalledDevices() +{ + if (MM::CanCommunicate == DetectDevice()) + { + ReadControllerMap(); + + std::vector peripherals; + peripherals.clear(); + + if((device_1_x_channel_ != 0xFF) || (device_1_y_channel_ != 0xFF)) + { + peripherals.push_back(g_DeviceNameM8XY_Device1); + } + if (device_1_z_channel_ != 0xFF) + { + peripherals.push_back(g_DeviceNameM8Z_Device1); + } + if (device_1_f_channel_ != 0xFF) + { + peripherals.push_back(g_DeviceNameFilter_Device1); + } + if ((device_2_x_channel_ != 0xFF) || (device_2_y_channel_ != 0xFF)) + { + peripherals.push_back(g_DeviceNameM8XY_Device2); + } + if (device_2_z_channel_ != 0xFF) + { + peripherals.push_back(g_DeviceNameM8Z_Device2); + } + if (device_2_f_channel_ != 0xFF) + { + peripherals.push_back(g_DeviceNameFilter_Device2); + } + + 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 ScientificaMotion8Hub::Shutdown() +{ + initialized_ = false; + return DEVICE_OK; +} + +int ScientificaMotion8Hub::OnPort(MM::PropertyBase* pProp, MM::ActionType pAct) +{ + if (pAct == MM::BeforeGet) + { + pProp->Set(port_.c_str()); + } + else if (pAct == MM::AfterSet) + { + pProp->Get(port_); + } + return DEVICE_OK; +} + +M8XYStage::M8XYStage(uint8_t device) +{ + CreateProperty(MM::g_Keyword_Name, "Scientifica-Motion8-XYStage", MM::String, true); + device_ = device; + + name_ = device == 0 ? g_DeviceNameM8XY_Device1 : g_DeviceNameM8XY_Device2; +} + +M8XYStage::~M8XYStage() +{ + +} + +bool M8XYStage::Busy() +{ + bool is_moving = false; + + int ret = DEVICE_OK; + + MM::Hub* hub = GetParentHub(); + if (!hub) + return true; + + ScientificaMotion8Hub* parentHub = dynamic_cast(hub); + if (!parentHub) + return true; + + ret = parentHub->IsMoving(device_, &is_moving); + + return ret; +} + +void M8XYStage::GetName(char* name) const +{ + CDeviceUtils::CopyLimitedString(name, name_.c_str()); +} + +int M8XYStage::Initialize() +{ + return DEVICE_OK; +} + +int M8XYStage::Shutdown() +{ + return DEVICE_OK; +} + +int M8XYStage::SetPositionSteps(long x, long y) +{ + MM::Hub* hub = GetParentHub(); + if (!hub) + return DEVICE_INTERNAL_INCONSISTENCY; + + ScientificaMotion8Hub* parentHub = dynamic_cast(hub); + if (!parentHub) + return DEVICE_INTERNAL_INCONSISTENCY; + + ScientificaTxPacket* txPacket = new ScientificaTxPacket(0xBB, 2, 3); + txPacket->AddUInt8(device_); + txPacket->AddInt32(x); + txPacket->AddInt32(y); + ScientificaRxPacket* rxPacket = NULL; + + int ret = DEVICE_OK; + + rxPacket = parentHub->WriteRead(txPacket, 0); + + if (rxPacket == NULL) + { + ret = DEVICE_SERIAL_TIMEOUT; + } + else + { + delete rxPacket; + } + + return ret; +} + +int M8XYStage::GetPositionSteps(long& x, long& y) +{ + MM::Hub* hub = GetParentHub(); + if (!hub) + return DEVICE_INTERNAL_INCONSISTENCY; + + ScientificaMotion8Hub* parentHub = dynamic_cast(hub); + if (!parentHub) + return DEVICE_INTERNAL_INCONSISTENCY; + + ScientificaTxPacket* txPacket = new ScientificaTxPacket(0xBB, 0, 0x14); + txPacket->AddUInt8(device_); + ScientificaRxPacket* rxPacket = NULL; + + int ret = DEVICE_OK; + + rxPacket = parentHub->WriteRead(txPacket, 29); + + if (rxPacket == NULL) + { + ret = DEVICE_SERIAL_TIMEOUT; + } + else + { + uint8_t device; + rxPacket->GetByte(&device); //Called to skip the device byte + int32_t x_from_device; + int32_t y_from_device; + rxPacket->GetInt32(&x_from_device); + rxPacket->GetInt32(&y_from_device); + x = x_from_device; + y = y_from_device; + delete rxPacket; + } + + return ret; +} + +int M8XYStage::Home() +{ + return DEVICE_OK; +} + +int M8XYStage::Stop() +{ + int ret = DEVICE_OK; + + MM::Hub* hub = GetParentHub(); + if (!hub) + return DEVICE_INTERNAL_INCONSISTENCY; + + ScientificaMotion8Hub* parentHub = dynamic_cast(hub); + if (!parentHub) + return DEVICE_INTERNAL_INCONSISTENCY; + + ret = parentHub->Stop(device_); + + return ret; +} + +int M8XYStage::SetOrigin() +{ + int ret; + ret = SetXOrigin(); + if (ret == DEVICE_OK) + ret = SetYOrigin(); + + return ret; +} + +int M8XYStage::SetXOrigin() +{ + int ret = DEVICE_OK; + + MM::Hub* hub = GetParentHub(); + if (!hub) + return DEVICE_INTERNAL_INCONSISTENCY; + + ScientificaMotion8Hub* parentHub = dynamic_cast(hub); + if (!parentHub) + return DEVICE_INTERNAL_INCONSISTENCY; + + ret = parentHub->SetPosition(device_, AXIS_X, 0); + + return ret; +} + +int M8XYStage::SetYOrigin() +{ + int ret = DEVICE_OK; + + MM::Hub* hub = GetParentHub(); + if (!hub) + return DEVICE_INTERNAL_INCONSISTENCY; + + ScientificaMotion8Hub* parentHub = dynamic_cast(hub); + if (!parentHub) + return DEVICE_INTERNAL_INCONSISTENCY; + + ret = parentHub->SetPosition(device_, AXIS_Y, 0); + return ret; +} + +int M8XYStage::GetLimitsUm(double& xMin, double& xMax, double& yMin, double& yMax) +{ + (void)xMin; + (void)xMax; + (void)yMin; + (void)yMax; + + return DEVICE_UNSUPPORTED_COMMAND; +} + +int M8XYStage::GetStepLimits(long& xMin, long& xMax, long& yMin, long& yMax) +{ + (void)xMin; + (void)xMax; + (void)yMin; + (void)yMax; + + return DEVICE_UNSUPPORTED_COMMAND; +} + +M8ZStage::M8ZStage(uint8_t device) +{ + CreateProperty(MM::g_Keyword_Name, "Scientifica-Motion8-ZStage", MM::String, true); + device_ = device; + + + name_ = device == 0 ? g_DeviceNameM8Z_Device1 : g_DeviceNameM8Z_Device2; +} + +M8ZStage::~M8ZStage() +{ + +} + +bool M8ZStage::Busy() +{ + bool is_moving = false; + + int ret = DEVICE_OK; + + MM::Hub* hub = GetParentHub(); + if (!hub) + return true; + + ScientificaMotion8Hub* parentHub = dynamic_cast(hub); + if (!parentHub) + return true; + + ret = parentHub->IsMoving(device_, &is_moving); + + return ret; +} + +void M8ZStage::GetName(char* name) const +{ + CDeviceUtils::CopyLimitedString(name, name_.c_str()); +} + +int M8ZStage::Initialize() +{ + return DEVICE_OK; +} + +int M8ZStage::Shutdown() +{ + return DEVICE_OK; +} + +int M8ZStage::SetPositionUm(double pos) +{ + long steps = (long)round(pos / 0.01); + return SetPositionSteps(steps); + +} +int M8ZStage::GetPositionUm(double& pos) +{ + long steps; + int ret = GetPositionSteps(steps); + if (ret != DEVICE_OK) + return ret; + + pos = steps * 0.01; + + return DEVICE_OK; +} + +int M8ZStage::SetPositionSteps(long z) +{ + MM::Hub* hub = GetParentHub(); + if (!hub) + return DEVICE_INTERNAL_INCONSISTENCY; + + ScientificaMotion8Hub* parentHub = dynamic_cast(hub); + if (!parentHub) + return DEVICE_INTERNAL_INCONSISTENCY; + + ScientificaTxPacket* txPacket = new ScientificaTxPacket(0xBB, 2, 3); + txPacket->AddUInt8(device_); + txPacket->AddUInt8(AXIS_Z); + txPacket->AddInt32(z); + ScientificaRxPacket* rxPacket = NULL; + + int ret = DEVICE_OK; + + rxPacket = parentHub->WriteRead(txPacket, 0); + + if (rxPacket == NULL) + { + ret = DEVICE_SERIAL_TIMEOUT; + } + else + { + delete rxPacket; + } + + return ret; +} + +int M8ZStage::GetPositionSteps(long& z) +{ + MM::Hub* hub = GetParentHub(); + if (!hub) + return DEVICE_INTERNAL_INCONSISTENCY; + + ScientificaMotion8Hub* parentHub = dynamic_cast(hub); + if (!parentHub) + return DEVICE_INTERNAL_INCONSISTENCY; + + ScientificaTxPacket* txPacket = new ScientificaTxPacket(0xBB, 0, 0x14); + txPacket->AddUInt8(device_); + ScientificaRxPacket* rxPacket = NULL; + + int ret = DEVICE_OK; + + rxPacket = parentHub->WriteRead(txPacket, 29); + + if (rxPacket == NULL) + { + ret = DEVICE_SERIAL_TIMEOUT; + } + else + { + + uint8_t device; + + rxPacket->GetByte(&device); //Called to skip the device byte + + int32_t ignore_from_device; + rxPacket->GetInt32(&ignore_from_device); //Called to skip the x value + rxPacket->GetInt32(&ignore_from_device); //Called to skip the y value + + int32_t z_from_device; + rxPacket->GetInt32(&z_from_device); + z = z_from_device; + + delete rxPacket; + } + + + return ret; +} + +int M8ZStage::Home() +{ + return DEVICE_OK; +} + +int M8ZStage::Stop() +{ + int ret = DEVICE_OK; + + MM::Hub* hub = GetParentHub(); + if (!hub) + return DEVICE_INTERNAL_INCONSISTENCY; + + ScientificaMotion8Hub* parentHub = dynamic_cast(hub); + if (!parentHub) + return DEVICE_INTERNAL_INCONSISTENCY; + + ret = parentHub->Stop(device_); + + return ret; +} + +int M8ZStage::SetOrigin() +{ + int ret = DEVICE_OK; + + MM::Hub* hub = GetParentHub(); + if (!hub) + return DEVICE_INTERNAL_INCONSISTENCY; + + ScientificaMotion8Hub* parentHub = dynamic_cast(hub); + if (!parentHub) + return DEVICE_INTERNAL_INCONSISTENCY; + + ret = parentHub->SetPosition(device_, AXIS_Z, 0); + + return ret; +} + +int M8ZStage::GetLimits(double& min, double& max) +{ + (void)min; + (void)max; + return DEVICE_UNSUPPORTED_COMMAND; +} + +M8FilterCubeTurret::M8FilterCubeTurret(uint8_t device) +{ + // CreateProperty(MM::g_Keyword_Name, "Scientifica-Motion8-ZStage", MM::String, true); + device_ = device; + numPositions_ = 3; + + name_ = device == 0 ? g_DeviceNameFilter_Device1 : g_DeviceNameFilter_Device2; + +} + +M8FilterCubeTurret::~M8FilterCubeTurret() +{ + +} + +int M8FilterCubeTurret::Initialize() +{ + return DEVICE_OK; +} + +int M8FilterCubeTurret::Shutdown() +{ + return DEVICE_OK; +} + +void M8FilterCubeTurret::GetName(char* name) const +{ + CDeviceUtils::CopyLimitedString(name, name_.c_str()); +} + +bool M8FilterCubeTurret::Busy() +{ + bool is_moving = false; + + MM::Hub* hub = GetParentHub(); + if (!hub) + return is_moving; + + ScientificaMotion8Hub* parentHub = dynamic_cast(hub); + if (!parentHub) + return is_moving; + + ScientificaTxPacket* txPacket = new ScientificaTxPacket(0xBB, 0, 0x11); + ScientificaRxPacket* rxPacket = NULL; + + rxPacket = parentHub->WriteRead(txPacket, 18); + + if (rxPacket != NULL) + { + + if (device_ == 0) + { + rxPacket->Skip(5); //Skip to Device 1 filter state + } + else + { + rxPacket->Skip(14); //Skip to Device 2 filter state + } + + uint8_t filterState; + rxPacket->GetByte(&filterState); + + is_moving = filterState != 0; + + delete rxPacket; + } + + return is_moving; +} + +int M8FilterCubeTurret::OnState(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + LogMessage("M8FilterCubeTurret::PositionGetSet\n", true); + + if (eAct == MM::BeforeGet) + { + int filterIndex; + int ret = GetFilter(filterIndex); + if (ret != DEVICE_OK) + return ret; + pProp->Set((long)filterIndex); + } + else if (eAct == MM::AfterSet) + { + long filterIndex; + pProp->Get(filterIndex); + + if ((filterIndex > 0) && (filterIndex <= numPositions_)) + return SetFilter(filterIndex); + else + return DEVICE_UNKNOWN_POSITION; + } + + return DEVICE_OK; +} + +int M8FilterCubeTurret::SetFilter(int filterIndex) +{ + MM::Hub* hub = GetParentHub(); + if (!hub) + return DEVICE_INTERNAL_INCONSISTENCY; + + ScientificaMotion8Hub* parentHub = dynamic_cast(hub); + if (!parentHub) + return DEVICE_INTERNAL_INCONSISTENCY; + + ScientificaTxPacket* txPacket = new ScientificaTxPacket(0xBB, 2, 0x0E); + txPacket->AddUInt8(device_); + txPacket->AddUInt8((uint8_t)filterIndex); + + ScientificaRxPacket* rxPacket = NULL; + + int ret = DEVICE_OK; + rxPacket = parentHub->WriteRead(txPacket, 18); + + if (rxPacket == NULL) + { + ret = DEVICE_SERIAL_TIMEOUT; + } + else + { + delete rxPacket; + } + + return ret; +} + +int M8FilterCubeTurret::GetFilter(int& filterIndex) +{ + MM::Hub* hub = GetParentHub(); + if (!hub) + return DEVICE_INTERNAL_INCONSISTENCY; + + ScientificaMotion8Hub* parentHub = dynamic_cast(hub); + if (!parentHub) + return DEVICE_INTERNAL_INCONSISTENCY; + + ScientificaTxPacket* txPacket = new ScientificaTxPacket(0xBB, 2, 0x0E); + ScientificaRxPacket* rxPacket = NULL; + + int ret = DEVICE_OK; + + txPacket->AddUInt8((uint8_t)filterIndex); + rxPacket = parentHub->WriteRead(txPacket, 2); + + if (rxPacket == NULL) + { + ret = DEVICE_SERIAL_TIMEOUT; + } + else + { + + rxPacket->Skip(1); //Skip device index + uint8_t filter; + rxPacket->GetByte(&filter); + + if (filter < numPositions_) + filter = filter; + else + ret = DEVICE_UNKNOWN_POSITION; + + + delete rxPacket; + } + + return ret; +} \ No newline at end of file diff --git a/DeviceAdapters/ScientificaMotion8/ScientificaMotion8.h b/DeviceAdapters/ScientificaMotion8/ScientificaMotion8.h new file mode 100644 index 000000000..00b098e41 --- /dev/null +++ b/DeviceAdapters/ScientificaMotion8/ScientificaMotion8.h @@ -0,0 +1,159 @@ +/////////////////////////////////////////////////////////////////////////////// +// FILE: ScientificaMotion8.h +// PROJECT: Micro-Manager +// SUBSYSTEM: DeviceAdapters +//----------------------------------------------------------------------------- +// DESCRIPTION: Scientifica Motion 8 rack adapter +// COPYRIGHT: University of California, San Francisco, 2006 +// 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. +// +// AUTHOR: Nenad Amodaj, nenad@amodaj.com, 06/01/2006 +// +// Scientifica Specific Parts +// AUTHOR: Matthew Player (ElecSoft Solutions) + +#ifndef _SCIENTIFICA_MOTION_8_H_ +#define _SCIENTIFICA_MOTION_8_H_ + +#include "MMDevice.h" +#include "DeviceBase.h" +#include +#include +#include "ScientificaTxPacket.h" +#include "ScientificaRxPacket.h" + +class ScientificaMotion8Hub : public HubBase +{ +public: + ScientificaMotion8Hub(); + ~ScientificaMotion8Hub(); + + int Initialize(); + int Shutdown(); + void GetName(char* pName) const; + bool Busy(); + + bool SupportsDeviceDetection(void); + MM::DeviceDetectionStatus DetectDevice(void); + int DetectInstalledDevices(); + + int OnPort(MM::PropertyBase* pPropt, MM::ActionType eAct); + + ScientificaRxPacket* WriteRead(ScientificaTxPacket* tx, int expected_length); + bool CheckControllerVersion(); + int Stop(uint8_t device); + int SetPosition(uint8_t device, uint8_t axis, long steps); + int IsMoving(uint8_t device, bool* moving); + bool initialized_; +private: + std::string port_; + static MMThreadLock lock_; + int ReadControllerMap(void); + + + uint8_t device_1_x_channel_; + uint8_t device_1_y_channel_; + uint8_t device_1_z_channel_; + uint8_t device_1_f_channel_; + + uint8_t device_2_x_channel_; + uint8_t device_2_y_channel_; + uint8_t device_2_z_channel_; + uint8_t device_2_f_channel_; +}; + + +class M8XYStage : public CXYStageBase +{ +public: + M8XYStage(uint8_t device); + ~M8XYStage(); + + bool Busy(); + void GetName(char* pName) const; + + int Initialize(); + int Shutdown(); + + // XYStage API + int SetPositionSteps(long x, long y); + int GetPositionSteps(long& x, long& y); + int Home(); + int Stop(); + int SetOrigin(); + int SetXOrigin(); + int SetYOrigin(); + int GetLimitsUm(double& xMin, double& xMax, double& yMin, double& yMax); + int GetStepLimits(long& xMin, long& xMax, long& yMin, long& yMax); + double GetStepSizeXUm() { return 0.01; } + double GetStepSizeYUm() { return 0.01; } + int IsXYStageSequenceable(bool& isSequenceable) const { isSequenceable = false; return DEVICE_OK; } +private: + uint8_t device_; + std::string name_; +}; + +class M8ZStage : public CStageBase +{ +public: + M8ZStage(uint8_t device); + ~M8ZStage(); + + //Device API + int Initialize(); + int Shutdown(); + void GetName(char* pName) const; + bool Busy(); + + // Stage API + int GetPositionUm(double& pos); + int SetPositionUm(double pos); + int SetPositionSteps(long steps); + int GetPositionSteps(long& steps); + int Home(); + int Stop(); + int SetOrigin(); + int GetLimits(double& min, double& max); + int IsStageSequenceable(bool& isSequenceable) const { isSequenceable = false; return DEVICE_OK; } + bool IsContinuousFocusDrive() const { return false; } +private: + uint8_t device_; + std::string name_; +}; + +class M8FilterCubeTurret : public CStateDeviceBase +{ +public: + M8FilterCubeTurret(uint8_t device); + ~M8FilterCubeTurret(); + + + //Device API + int Initialize(); + int Shutdown(); + void GetName(char* pName) const; + bool Busy(); + + unsigned long GetNumberOfPositions()const { return numPositions_; } + + int OnState(MM::PropertyBase* pProp, MM::ActionType eAct); +private: + uint8_t device_; + std::string name_; + + int SetFilter(int filterIndex); + int GetFilter(int& filterIndex); + + int numPositions_; +}; + +#endif diff --git a/DeviceAdapters/ScientificaMotion8/ScientificaMotion8.vcxproj b/DeviceAdapters/ScientificaMotion8/ScientificaMotion8.vcxproj new file mode 100644 index 000000000..0a54c5289 --- /dev/null +++ b/DeviceAdapters/ScientificaMotion8/ScientificaMotion8.vcxproj @@ -0,0 +1,108 @@ + + + + + Debug + x64 + + + Release + x64 + + + + {E4E9FA4F-E9DD-4483-9140-2FD472C28F1D} + Scientifica + Win32Proj + 10.0 + + + + DynamicLibrary + MultiByte + v142 + false + + + DynamicLibrary + MultiByte + v142 + true + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.40219.1 + false + false + + + + X64 + + + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + EnableFastChecks + true + + + 4290;%(DisableSpecificWarnings) + + + true + Windows + + + + + + + X64 + + + WIN32;NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + + + 4290;%(DisableSpecificWarnings) + + + Windows + true + true + + + + + + + + + + + + + + + + + {b8c95f39-54bf-40a9-807b-598df2821d55} + + + + + + \ No newline at end of file diff --git a/DeviceAdapters/ScientificaMotion8/ScientificaMotion8.vcxproj.filters b/DeviceAdapters/ScientificaMotion8/ScientificaMotion8.vcxproj.filters new file mode 100644 index 000000000..97d6f0163 --- /dev/null +++ b/DeviceAdapters/ScientificaMotion8/ScientificaMotion8.vcxproj.filters @@ -0,0 +1,39 @@ + + + + + {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 + + + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/DeviceAdapters/ScientificaMotion8/ScientificaRxPacket.cpp b/DeviceAdapters/ScientificaMotion8/ScientificaRxPacket.cpp new file mode 100644 index 000000000..0760c0a60 --- /dev/null +++ b/DeviceAdapters/ScientificaMotion8/ScientificaRxPacket.cpp @@ -0,0 +1,120 @@ +/////////////////////////////////////////////////////////////////////////////// +// FILE: ScientificaRxPacket.h +// PROJECT: Micro-Manager +// SUBSYSTEM: DeviceAdapters +//----------------------------------------------------------------------------- +// DESCRIPTION: Helper class to parse packets from Scientifica motion 8 racks +// +// 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. +// +// AUTHOR: Matthew Player (ElecSoft Solutions) + +#include "ScientificaRxPacket.h" + +#define MAX_PACKET_SIZE 256 + +ScientificaRxPacket::ScientificaRxPacket(unsigned char* data, int data_length) +{ + index_ = 0; + data_ = new unsigned char[MAX_PACKET_SIZE]; + + //COBS Decoding + int block_remaining = 0; + int block_length = 0xFF; + int write = 0; + + for (int i = 0; i < MAX_PACKET_SIZE; i++) + { + data_[i] = 0; + } + + for (int i = 0; i < data_length; i++) + { + if (block_remaining != 0) + { + if (data[i] != 0) + { + data_[write] = data[i]; + write++; + } + } + else + { + if (block_length != 0xFF) + { + data_[write] = 0; + write++; + } + block_remaining = data[i]; + block_length = data[i]; + } + + block_remaining--; + } + + data_length_ = write; + + if (data_length_ < 4) + { + status = 0; + echo = 0; + command1 = 0; + command2 = 0; + } + else + { + status = data_[0]; + echo = data_[1]; + command1 = data_[2]; + command2 = data_[3]; + index_ = 4; + } +} + +ScientificaRxPacket::~ScientificaRxPacket() +{ + delete[] data_; +} + +bool ScientificaRxPacket::GetByte(uint8_t* value) +{ + if (index_ + 1 > data_length_) + return false; + + *value = data_[index_]; + index_++; + return true; +} + +bool ScientificaRxPacket::GetUInt16(uint16_t* value) +{ + if (index_ + 2 > data_length_) + return false; + + *value = data_[index_]; + *value |= data_[index_ + 1] << 8; + index_ += 2; + return true; +} + +bool ScientificaRxPacket::GetInt32(int32_t* value) +{ + if (index_ + 4 > data_length_) + return false; + + *value = data_[index_]; + *value |= data_[index_ + 1] << 8; + *value |= data_[index_ + 2] << 16; + *value |= data_[index_ + 3] << 24; + index_ += 4; + return true; +} \ No newline at end of file diff --git a/DeviceAdapters/ScientificaMotion8/ScientificaRxPacket.h b/DeviceAdapters/ScientificaMotion8/ScientificaRxPacket.h new file mode 100644 index 000000000..47e94a895 --- /dev/null +++ b/DeviceAdapters/ScientificaMotion8/ScientificaRxPacket.h @@ -0,0 +1,92 @@ +/////////////////////////////////////////////////////////////////////////////// +// FILE: ScientificaRxPacket.h +// PROJECT: Micro-Manager +// SUBSYSTEM: DeviceAdapters +//----------------------------------------------------------------------------- +// DESCRIPTION: Helper class to parse packets from Scientifica motion 8 racks +// +// 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. +// +// AUTHOR: Matthew Player (ElecSoft Solutions) + +#pragma once +#include +class ScientificaRxPacket +{ +public: + /** + * @brief Constructor + */ + ScientificaRxPacket(unsigned char* buffer, int buffer_size); + + /** + * @brief Destructor + */ + ~ScientificaRxPacket(); + + /** + * @brief Get next unsigned 8 bit integer from packet + * @param[in] value Destination for value + * @return true if successful, false if not enough data + */ + bool GetByte(uint8_t* value); + + /** + * @brief Get next unsigned 16 bit integer from packet + * @param[in] value Destination for value + * @return true if successful, false if not enough data + */ + bool GetUInt16(uint16_t* value); + + /** + * @brief Get next signed 32 bit integer from packet + * @param[in] value Destination for value + * @return true if successful, false if not enough data + */ + bool GetInt32(int32_t* value); + + /** + * @brief Get the remaining number of bytes in the packet + * @return The number of bytes remaining in the packet + */ + int RemainingBytes() { return data_length_ - index_; } + + /** + * @brief Get the length of the packet + * @return The length of the packet + */ + int Length() { return data_length_; } + + /** + * @brief Get the data buffer + * @return The data buffer + */ + unsigned char* GetData() { return data_; } + + /** + * @brief Skip the next given number of bytes in the packet + * @param[in] count The number of bytes to skip + */ + void Skip(int count) { index_ += count; } + + +private: + unsigned char* data_; + int data_length_; + int index_; + + uint8_t status; + uint8_t echo; + uint8_t command1; + uint8_t command2; +}; + diff --git a/DeviceAdapters/ScientificaMotion8/ScientificaTxPacket.cpp b/DeviceAdapters/ScientificaMotion8/ScientificaTxPacket.cpp new file mode 100644 index 000000000..e45683836 --- /dev/null +++ b/DeviceAdapters/ScientificaMotion8/ScientificaTxPacket.cpp @@ -0,0 +1,110 @@ +/////////////////////////////////////////////////////////////////////////////// +// FILE: ScientificaTxPacket.h +// PROJECT: Micro-Manager +// SUBSYSTEM: DeviceAdapters +//----------------------------------------------------------------------------- +// DESCRIPTION: Helper class to build packets to send to Scientifica motion 8 racks +// +// 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. +// +// AUTHOR: Matthew Player (ElecSoft Solutions) + +#include "ScientificaTxPacket.h" + +#define MAX_PACKET_SIZE 256 + +ScientificaTxPacket::ScientificaTxPacket(uint8_t echo, uint8_t command1, uint8_t command2) +{ + packet_ = new uint8_t[MAX_PACKET_SIZE]; + encoded_ = new uint8_t[MAX_PACKET_SIZE]; + write_index_ = 0; + encoded_length_ = 0; + + packet_[0] = echo; + packet_[1] = command1; + packet_[2] = command2; + write_index_ = 3; +} +ScientificaTxPacket::~ScientificaTxPacket() +{ + delete[] packet_; + delete[] encoded_; +} + +unsigned char* ScientificaTxPacket::GetPacketToSend() +{ + int block_start = 0; + int block_length = 1; + int write = 1; + + for (int i = 0; i < MAX_PACKET_SIZE; i++) + { + encoded_[i] = 0; + } + //COBS Encoding + for (int i = 0; i < write_index_; i++) + { + if (packet_[i] != 0) + { + encoded_[write] = packet_[i]; + block_length++; + write++; + } + + if ((block_length == 0xFF) || (packet_[i] == 0)) + { + encoded_[block_start] = (uint8_t)block_length; + block_start = write; + encoded_[write] = 0; + write++; + block_length = 1; + } + } + encoded_[block_start] = (uint8_t)block_length; + encoded_[write] = 0x0; //Add packet deliminator + write++; + + encoded_length_ = write; + return (unsigned char*)encoded_; +} + +void ScientificaTxPacket::Clear() +{ + write_index_ = 0; +} + +void ScientificaTxPacket::AddUInt8(uint8_t byte) +{ + packet_[write_index_++] = byte; +} + +void ScientificaTxPacket::AddUInt16(uint16_t word) +{ + packet_[write_index_++] = word & 0xff; + packet_[write_index_++] = (word >> 8) & 0xff; +} + +void ScientificaTxPacket::AddUInt32(uint32_t dword) +{ + packet_[write_index_++] = dword & 0xff; + packet_[write_index_++] = (dword >> 8) & 0xff; + packet_[write_index_++] = (dword >> 16) & 0xff; + packet_[write_index_++] = (dword >> 24) & 0xff; +} + +void ScientificaTxPacket::AddInt32(int32_t dword) +{ + packet_[write_index_++] = dword & 0xff; + packet_[write_index_++] = (dword >> 8) & 0xff; + packet_[write_index_++] = (dword >> 16) & 0xff; + packet_[write_index_++] = (dword >> 24) & 0xff; +} diff --git a/DeviceAdapters/ScientificaMotion8/ScientificaTxPacket.h b/DeviceAdapters/ScientificaMotion8/ScientificaTxPacket.h new file mode 100644 index 000000000..dc304b88f --- /dev/null +++ b/DeviceAdapters/ScientificaMotion8/ScientificaTxPacket.h @@ -0,0 +1,84 @@ +/////////////////////////////////////////////////////////////////////////////// +// FILE: ScientificaTxPacket.h +// PROJECT: Micro-Manager +// SUBSYSTEM: DeviceAdapters +//----------------------------------------------------------------------------- +// DESCRIPTION: Helper class to build packets to send to Scientifica motion 8 racks +// +// 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. +// +// AUTHOR: Matthew Player (ElecSoft Solutions) + +#pragma once +#include + +class ScientificaTxPacket +{ +public: + /** + * @brief Constructor + */ + ScientificaTxPacket(uint8_t echo, uint8_t command1, uint8_t command2); + + /** + * @brief Destructor + */ + ~ScientificaTxPacket(); + + /** + * @brief Get the encoded packet ready to send + * @return encoded packet to send + */ + unsigned char* GetPacketToSend(); + + /** + * @brief Get the length of the encoded packet + * @return The length of the encoded packet + */ + int GetEncodedLength() { return encoded_length_; } + + /** + * @brief Clear all data from the packet + */ + void Clear(); + + /** + * @brief Add an unsigned 8 bit integer to packet + * @param[in] byte value to add + */ + void AddUInt8(uint8_t byte); + + /** + * @brief Add an unsigned 16 bit integer to packet + * @param[in] word value to add + */ + void AddUInt16(uint16_t word); + + /** + * @brief Add an unsigned 32 bit integer to packet + * @param[in] dword value to add + */ + void AddUInt32(uint32_t dword); + + /** + * @brief Add an signed 32 bit integer to packet + * @param[in] dword value to add + */ + void AddInt32(int32_t dword); + +private: + uint8_t* packet_; + uint8_t* encoded_; + int write_index_; + int encoded_length_; +}; + diff --git a/DeviceAdapters/SequenceTester/SequenceTester.cpp b/DeviceAdapters/SequenceTester/SequenceTester.cpp index 2cc35b4cf..87d638318 100644 --- a/DeviceAdapters/SequenceTester/SequenceTester.cpp +++ b/DeviceAdapters/SequenceTester/SequenceTester.cpp @@ -531,7 +531,7 @@ TesterCamera::SendSequence(bool finite, long count, bool stopOnOverflow) char label[MM::MaxStrLength]; GetLabel(label); Metadata md; - md.put("Camera", label); + md.put(MM::g_Keyword_Metadata_CameraLabel, label); std::string serializedMD(md.Serialize()); const unsigned char* bytes = 0; diff --git a/DeviceAdapters/SigmaKoki/Camera.cpp b/DeviceAdapters/SigmaKoki/Camera.cpp index fb2013c3c..8f485a75c 100644 --- a/DeviceAdapters/SigmaKoki/Camera.cpp +++ b/DeviceAdapters/SigmaKoki/Camera.cpp @@ -953,7 +953,7 @@ int Camera::InsertImage() // Important: metadata about the image are generated here: Metadata md; - md.put("Camera", label); + md.put(MM::g_Keyword_Metadata_CameraLabel, label); md.put(MM::g_Keyword_Elapsed_Time_ms, CDeviceUtils::ConvertToString((timeStamp - sequenceStartTime_).getMsec())); md.put(MM::g_Keyword_Metadata_ImageNumber, CDeviceUtils::ConvertToString(imageCounter_)); diff --git a/DeviceAdapters/SigmaKoki/XYStage.cpp b/DeviceAdapters/SigmaKoki/XYStage.cpp index 5601c49fe..49f064ed1 100644 --- a/DeviceAdapters/SigmaKoki/XYStage.cpp +++ b/DeviceAdapters/SigmaKoki/XYStage.cpp @@ -490,7 +490,7 @@ int XYStage::SetDivision(int channel, int division) if (ret != DEVICE_OK) return ret; - // Send command c the division number + // Send command … the division number if (model_ == SHRC3) // hit mode SHRC controller { if (channel == 1) { ret = SendCheckRecievedOK("S:" + to_string(division) + ",,"); } diff --git a/DeviceAdapters/SigmaKoki/XYStage.h b/DeviceAdapters/SigmaKoki/XYStage.h index a25e3dd2c..7fdd87ee0 100644 --- a/DeviceAdapters/SigmaKoki/XYStage.h +++ b/DeviceAdapters/SigmaKoki/XYStage.h @@ -75,7 +75,7 @@ class XYStage : public CXYStageBase, public SigmaBase void CreateDivisionPropXY(); void CreateFullStepPropXY(); - void AddAllowedDivisionPropXY(const char* div); // Added 5ŒŽ12“ú2022 t.abed + void AddAllowedDivisionPropXY(const char* div); // Added 5月12æ—¥2022 t.abed int SetDeviceModel(); // change from [ SetDeviceModel(XYStageModel& model)] no needed int SetDivision(int channel, int division); int SetSpeedXY(int vxy); diff --git a/DeviceAdapters/SigmaKoki/ZStage.cpp b/DeviceAdapters/SigmaKoki/ZStage.cpp index 36be97756..119bd650b 100644 --- a/DeviceAdapters/SigmaKoki/ZStage.cpp +++ b/DeviceAdapters/SigmaKoki/ZStage.cpp @@ -717,7 +717,7 @@ void ZStage::CreateDivisionProp() { // Clear all allowed value declared before.-------------------------------------------------------------------------------------------- ClearAllowedValues(g_ZStageDivision); - AddAllowedDivisionPropXY(g_ZStageDivision); // Added 9ŒŽ12“ú2022 t.abed + AddAllowedDivisionPropXY(g_ZStageDivision); // Added 9月12æ—¥2022 t.abed } /// diff --git a/DeviceAdapters/SigmaKoki/ZStage.h b/DeviceAdapters/SigmaKoki/ZStage.h index 544763649..6b976469b 100644 --- a/DeviceAdapters/SigmaKoki/ZStage.h +++ b/DeviceAdapters/SigmaKoki/ZStage.h @@ -66,19 +66,19 @@ class ZStage : public CStageBase, public SigmaBase int SetDeviceModel(); // change from [ SetDeviceModel(XYStageModel& model)] no needed void CreateDivisionProp(); - void AddAllowedDivisionPropXY(const char* div); // Added 5ŒŽ12“ú2022 t.abed + void AddAllowedDivisionPropXY(const char* div); // Added 5月12æ—¥2022 t.abed void CreateChanelProp(); int SetDivision(int division); int SetSpeed(int val); - int UpdatePositionZ(); // Added 4ŒŽ7“ú2022@@@t.abed - int DriveCommadProcessZ(double position);// Added 4ŒŽ7“ú2022 t.abed + int UpdatePositionZ(); // Added 4月7æ—¥2022   t.abed + int DriveCommadProcessZ(double position);// Added 4月7æ—¥2022 t.abed long GetSlowSpeedPulse(long pls); ZStageModel model_; std::string controlMode_; std::string channel_; int speed_; - double stepsZ_; // Z stage current position Added 4ŒŽ7“ú2022 t.abed + double stepsZ_; // Z stage current position Added 4月7æ—¥2022 t.abed double fullstepSizeZum_; double stepSizeZum_; std::string divisionZ_; diff --git a/DeviceAdapters/Skyra/Skyra.h b/DeviceAdapters/Skyra/Skyra.h index f796c1978..bef78eb8d 100644 --- a/DeviceAdapters/Skyra/Skyra.h +++ b/DeviceAdapters/Skyra/Skyra.h @@ -78,9 +78,9 @@ // Strings // -const char * g_DeviceVendorName = "HÜBNER Photonics"; +const char * g_DeviceVendorName = "HÃœBNER Photonics"; const char * g_DeviceSkyraName = "Skyra"; -const char * g_DeviceSkyraDescription = "Skyra/Cobolt Controller by Karl Bellvé"; +const char * g_DeviceSkyraDescription = "Skyra/Cobolt Controller by Karl Bellvé"; const char * g_SendTerm = "\r"; diff --git a/DeviceAdapters/Spinnaker/SpinnakerCamera.cpp b/DeviceAdapters/Spinnaker/SpinnakerCamera.cpp index 1386a89c2..2cae743bf 100644 --- a/DeviceAdapters/Spinnaker/SpinnakerCamera.cpp +++ b/DeviceAdapters/Spinnaker/SpinnakerCamera.cpp @@ -1608,7 +1608,7 @@ int SpinnakerCamera::MoveImageToCircularBuffer() this->GetLabel(label); Metadata md; - md.put("Camera", label); + md.put(MM::g_Keyword_Metadata_CameraLabel, label); md.put(MM::g_Keyword_Elapsed_Time_ms, CDeviceUtils::ConvertToString((timeStamp - m_aqThread->GetStartTime()).getMsec())); md.put(MM::g_Keyword_Metadata_ROI_X, CDeviceUtils::ConvertToString((long)m_cam->Width.GetValue())); md.put(MM::g_Keyword_Metadata_ROI_Y, CDeviceUtils::ConvertToString((long)m_cam->Height.GetValue())); diff --git a/DeviceAdapters/TISCam/TIScamera.cpp b/DeviceAdapters/TISCam/TIScamera.cpp index 3d98e22ac..f1ce5e0ee 100644 --- a/DeviceAdapters/TISCam/TIScamera.cpp +++ b/DeviceAdapters/TISCam/TIScamera.cpp @@ -45,7 +45,7 @@ You may extract these files from TIS development environment "IC Imaging Control IMPORTANT: Use build 3.3.0.1796 or later System: -Use the VC runtime installations from Microsoft™. +Use the VC runtime installations from Microsoftâ„¢. The files VC100*.* shall be reachable from the path specifier. */ @@ -2161,7 +2161,7 @@ int CTIScamera::PushImage() // Important: metadata about the image are generated here: Metadata md; - md.put("Camera", label); + md.put(MM::g_Keyword_Metadata_CameraLabel, label); md.put(MM::g_Keyword_Elapsed_Time_ms, CDeviceUtils::ConvertToString((timeStamp - sequenceStartTime_).getMsec())); md.put(MM::g_Keyword_Metadata_ImageNumber, CDeviceUtils::ConvertToString(imageCounter_)); md.put(MM::g_Keyword_Binning, binSize_); diff --git a/DeviceAdapters/TUCam/MMTUCam.cpp b/DeviceAdapters/TUCam/MMTUCam.cpp index 403834f64..43b7f0676 100755 --- a/DeviceAdapters/TUCam/MMTUCam.cpp +++ b/DeviceAdapters/TUCam/MMTUCam.cpp @@ -100,6 +100,7 @@ const char* g_PropNameFrameRate = "Frame Rate"; const char* g_PropNameTestImg = "Test Image"; +const char* g_PropNameWaitTimeOut= "GetFrame TimeOut"; const char* g_PropNameBrightness = "Targeting Level"; //"Brightness"; const char* g_PropNamePixelRatio = "Metering Level"; //"Pixel Ratio"; const char* g_PropNameImgMetadata= "Image Metadata"; @@ -140,16 +141,16 @@ const char* g_FAN_OFF = "Off"; const char* g_OT_ON = "On"; const char* g_OT_OFF = "Off"; -const char* g_CMSBIT_ON = "CMS"; -const char* g_HDRBIT_ON = "HDR"; -const char* g_HIGHBIT_ON = "HIGH"; -const char* g_LOWBIT_ON = "LOW"; -const char* g_GRHIGH_ON = "GLOBALRESETHIGH"; -const char* g_GRLOW_ON = "GLOBALRESETLOW"; -const char* g_HSHIGH_ON = "HIGHSPEEDHG"; -const char* g_HSLOW_ON = "HIGHSPEEDLG"; -const char* g_STDHIGH_ON = "STDHIGH"; -const char* g_STDLOW_ON = "STDLOW"; +const char* g_CMSBIT_ON = "High Sensitivity"; /// CMS +const char* g_HDRBIT_ON = "High Dynamic"; +const char* g_HIGHBIT_ON = "High Gain"; +const char* g_LOWBIT_ON = "Low Gain"; +const char* g_GRHIGH_ON = "Global Reset(High)"; +const char* g_GRLOW_ON = "Global Reset(Low)"; +const char* g_HSHIGH_ON = "High Speed(High)"; +const char* g_HSLOW_ON = "High Speed(Low)"; +const char* g_STDHIGH_ON = "High Gain_HS"; +const char* g_STDLOW_ON = "Low Gain_HS"; const char* g_HIGHDYNAMIC_ON = "High Dynamic"; /// HDR const char* g_HIGHSPEED_ON = "High Speed"; /// HighSpeedHg @@ -342,6 +343,7 @@ CMMTUCam::CMMTUCam() : m_rsPara.nSlitHeightMin = 1; m_rsPara.nSlitHeightStep = 1; m_rsPara.dbLineInvalTm = 1.0; + m_nWaitForFrameTimeOut = 10000; m_nDriverType = 0; m_bCC1Support = false; m_nTriType = TRITYPE_SMA; @@ -480,6 +482,7 @@ int CMMTUCam::Initialize() if (TUCAMRET_SUCCESS == TUCAM_Dev_GetInfo(m_opCam.hIdxTUCam, &valInfo)) { m_nPID = valInfo.nValue; +// LoadProfile(); } if (DHYANA_201D == m_nPID || DHYANA_401D == m_nPID) @@ -527,8 +530,8 @@ int CMMTUCam::Initialize() if (TRITYPE_HR == m_nTriType) { - m_tgrOutPara.TgrPort1.nTgrOutMode = 3; - m_tgrOutPara.TgrPort2.nTgrOutMode = 5; + m_tgrOutPara.TgrPort1.nTgrOutMode = 5; ///3 + m_tgrOutPara.TgrPort2.nTgrOutMode = 3; ///5 } // binning @@ -565,13 +568,13 @@ int CMMTUCam::Initialize() TUCAM_Capa_SetValue(m_opCam.hIdxTUCam, TUIDC_BITOFDEPTH, 8); } - pAct = new CPropertyAction (this, &CMMTUCam::OnBitDepth); - nRet = CreateProperty(g_PropNameBODP, "8", MM::String, false, pAct); - assert(nRet == DEVICE_OK); + pAct = new CPropertyAction(this, &CMMTUCam::OnBitDepth); + nRet = CreateProperty(g_PropNameBODP, "8", MM::String, false, pAct); + assert(nRet == DEVICE_OK); - vector bitDepths; - bitDepths.push_back("8"); - bitDepths.push_back("16"); + vector bitDepths; + bitDepths.push_back("8"); + bitDepths.push_back("16"); nRet = SetAllowedValues(g_PropNameBODP, bitDepths); if (nRet != DEVICE_OK) @@ -633,10 +636,10 @@ int CMMTUCam::Initialize() if (TUCAMRET_SUCCESS == TUCAM_Prop_GetAttr(m_opCam.hIdxTUCam, &propAttr)) { pAct = new CPropertyAction(this, &CMMTUCam::OnPixelRatio); - nRet = CreateProperty(g_PropNamePixelRatio, "0", MM::Integer, false, pAct); + nRet = CreateProperty(g_PropNamePixelRatio, "0", MM::Float, false, pAct); assert(nRet == DEVICE_OK); - SetPropertyLimits(g_PropNamePixelRatio, propAttr.dbValMin, propAttr.dbValMax); + SetPropertyLimits(g_PropNamePixelRatio, propAttr.dbValMin / 100, propAttr.dbValMax / 100); } // Global Gain @@ -703,7 +706,7 @@ int CMMTUCam::Initialize() capaAttr.idCapa = TUIDC_ENABLETIMESTAMP; if (TUCAMRET_SUCCESS == TUCAM_Capa_GetAttr(m_opCam.hIdxTUCam, &capaAttr)) { - if (IsSupport401DNew() || IsSupport95V2New() || IsSupport400BSIV3New()) + if (IsSupport401DNew() || IsSupport201DNew() || IsSupport95V2New() || IsSupport400BSIV3New()) { pAct = new CPropertyAction(this, &CMMTUCam::OnTimeStamp); nRet = CreateProperty(g_PropNameImgMetadata, "FALSE", MM::String, false, pAct); @@ -792,7 +795,8 @@ int CMMTUCam::Initialize() } // Shutter - if (PID_FL_26BW == m_nPID) + capaAttr.idCapa = TUIDC_SHUTTER; + if (TUCAMRET_SUCCESS == TUCAM_Capa_GetAttr(m_opCam.hIdxTUCam, &capaAttr)) // if (PID_FL_26BW == m_nPID) { pAct = new CPropertyAction(this, &CMMTUCam::OnShutterMode); nRet = CreateProperty(g_PropNameShutter, "", MM::String, false, pAct); @@ -1047,12 +1051,12 @@ int CMMTUCam::Initialize() { pAct = new CPropertyAction(this, &CMMTUCam::OnCMSMode); - nRet = CreateProperty(g_PropNameCMS, g_CMS_ON, MM::String, false, pAct); - assert(nRet == DEVICE_OK); - vectorCMSValues; + nRet = CreateProperty(g_PropNameCMS, g_CMS_ON, MM::String, false, pAct); + assert(nRet == DEVICE_OK); + vectorCMSValues; - CMSValues.push_back(g_CMS_OFF); - CMSValues.push_back(g_CMS_ON); + CMSValues.push_back(g_CMS_OFF); + CMSValues.push_back(g_CMS_ON); nRet = SetAllowedValues(g_PropNameCMS, CMSValues); @@ -1224,7 +1228,7 @@ int CMMTUCam::Initialize() vectorModTgrValues; ModTgrValues.push_back(g_TRIGGER_OFF); - if (IsSupport95V2New() || IsSupport401DNew() || IsSupport400BSIV3New()) + if (IsSupport95V2New() || IsSupport401DNew() || IsSupport201DNew() || IsSupport400BSIV3New()) { ModTgrValues.push_back(g_TRIGGER_STDOVERLAP); ModTgrValues.push_back(g_TRIGGER_STDNONOVERLAP); @@ -1311,6 +1315,7 @@ int CMMTUCam::Initialize() pAct = new CPropertyAction (this, &CMMTUCam::OnTriggerDelay); nRet = CreateProperty(g_PropNameMdDly, "0", MM::Integer, false, pAct); assert(nRet == DEVICE_OK); + SetPropertyLimits(g_PropNameMdDly, 0, 10000000); // Trigger Filter capaAttr.idCapa = TUIDC_SIGNALFILTER; @@ -1319,7 +1324,7 @@ int CMMTUCam::Initialize() pAct = new CPropertyAction(this, &CMMTUCam::OnTriggerFilter); nRet = CreateProperty(g_PropNameFilter, "0", MM::Integer, false, pAct); assert(nRet == DEVICE_OK); - SetPropertyLimits(g_PropNameFilter, 1, 1000000); + SetPropertyLimits(g_PropNameFilter, 0, 1000000); } if (PID_FL_9BW == m_nPID || PID_FL_9BW_LT == m_nPID || PID_FL_26BW == m_nPID) @@ -1332,7 +1337,8 @@ int CMMTUCam::Initialize() SetPropertyLimits(g_PropNameMdTFrames, 1, 0xFFFF); - if (TUCAMRET_SUCCESS == TUCAM_Capa_GetValue(m_opCam.hIdxTUCam, TUIDC_ROLLINGSCANMODE, &m_rsPara.nMode)) + double dbPixelRatio = 0; + if (TUCAMRET_SUCCESS == TUCAM_Capa_GetValue(m_opCam.hIdxTUCam, TUIDC_ROLLINGSCANMODE, &m_rsPara.nMode) || TUCAMRET_SUCCESS == TUCAM_Prop_GetValue(m_opCam.hIdxTUCam, TUIDP_PIXELRATIO, &dbPixelRatio)) { // Trigger frames pAct = new CPropertyAction(this, &CMMTUCam::OnTriggerFrames); @@ -1502,19 +1508,20 @@ int CMMTUCam::Initialize() vectorModPortValues; ModPortValues.push_back(g_TRIGGER_PORT1); - ModPortValues.push_back(g_TRIGGER_PORT2); - if (TRITYPE_SMA == m_nTriType) - { - ModPortValues.push_back(g_TRIGGER_PORT3); - } + ModPortValues.push_back(g_TRIGGER_PORT2); + + if (TRITYPE_SMA == m_nTriType) + { + ModPortValues.push_back(g_TRIGGER_PORT3); + } nRet = SetAllowedValues(g_PropNamePort, ModPortValues); if (nRet != DEVICE_OK) return nRet; // OutPutTrigger Kind Mode - if (TRITYPE_SMA == m_nTriType) + if (TRITYPE_SMA == m_nTriType || IsSupport401DNew() || IsSupport201DNew()) { pAct = new CPropertyAction (this, &CMMTUCam::OnTrgOutKindMode); nRet = CreateProperty(g_PropNameKind, g_TRIGGER_READEND, MM::String, false, pAct); @@ -1524,7 +1531,7 @@ int CMMTUCam::Initialize() ModKindValues.push_back(g_TRIGGER_EXPSTART); ModKindValues.push_back(g_TRIGGER_READEND); ModKindValues.push_back(g_TRIGGER_GLBEXP); - if (IsSupport95V2New() || IsSupport401DNew() || IsSupport400BSIV3New()) + if (IsSupport95V2New() || IsSupport401DNew() || IsSupport201DNew() || IsSupport400BSIV3New()) { ModKindValues.push_back(g_TRIGGER_TRIREADY); } @@ -1562,9 +1569,13 @@ int CMMTUCam::Initialize() assert(nRet == DEVICE_OK); SetPropertyLimits(g_PropNameWidth, 1, 10000000); - } + // WaitForFrame TimeOut + pAct = new CPropertyAction(this, &CMMTUCam::OnWaitForTimeOut); + nRet = CreateProperty(g_PropNameWaitTimeOut, "10000", MM::Integer, false, pAct); + assert(nRet == DEVICE_OK); + // initialize image buffer nRet = StartCapture(); if (nRet != DEVICE_OK) @@ -1693,6 +1704,8 @@ int CMMTUCam::Shutdown() StopSequenceAcquisition(); +// SaveProfile(); + UninitTUCamApi(); initialized_ = false; return DEVICE_OK; @@ -1899,10 +1912,11 @@ int CMMTUCam::SetROI(unsigned x, unsigned y, unsigned xSize, unsigned ySize) if (xSize == 0 && ySize == 0) { // effectively clear ROI - ResizeImageBuffer(); roiX_ = 0; roiY_ = 0; m_bROI = false; + + ResizeImageBuffer(); } else { @@ -1918,7 +1932,7 @@ int CMMTUCam::SetROI(unsigned x, unsigned y, unsigned xSize, unsigned ySize) roiAttr.bEnable = TRUE; roiAttr.nHOffset= ((x >> 2) << 2); roiAttr.nVOffset= ((y >> 2) << 2); -// roiAttr.nVOffset= (((m_nMaxHeight/*img_.Height()*/ - y - ySize) >> 2) << 2); +// roiAttr.nVOffset= (((m_nMaxHeight/*img_.Height()*/ - y - ySize) >> 2) << 2); roiAttr.nWidth = (xSize >> 3) << 3; //// roiAttr.nWidth = (xSize >> 2) << 2; roiAttr.nHeight = (ySize >> 3) << 3; //// roiAttr.nHeight = (ySize >> 2) << 2; @@ -2031,14 +2045,17 @@ double CMMTUCam::GetSequenceExposure() */ void CMMTUCam::SetExposure(double exp) { + double dbVal = 0; if (exp < exposureMinimum_) { exp = exposureMinimum_; } else if (exp > exposureMaximum_) { exp = exposureMaximum_; } - SetProperty(MM::g_Keyword_Exposure, CDeviceUtils::ConvertToString(exp)); - GetCoreCallback()->OnExposureChanged(this, exp); + TUCAM_Prop_SetValue(m_opCam.hIdxTUCam, TUIDP_EXPOSURETM, exp); + TUCAM_Prop_GetValue(m_opCam.hIdxTUCam, TUIDP_EXPOSURETM, &dbVal); + SetProperty(MM::g_Keyword_Exposure, CDeviceUtils::ConvertToString(dbVal)); + GetCoreCallback()->OnExposureChanged(this, dbVal); } /** @@ -2497,7 +2514,7 @@ int CMMTUCam::SetAllowedGainMode() else { pAct = new CPropertyAction(this, &CMMTUCam::OnImageMode); - nRet = CreateProperty(g_PropNameGain, "HDR", MM::String, false, pAct); + nRet = CreateProperty(g_PropNameGain, g_HDRBIT_ON, MM::String, false, pAct); assert(nRet == DEVICE_OK); nRet = SetAllowedImageMode(); } @@ -2518,22 +2535,13 @@ int CMMTUCam::SetAllowedImageMode() return DEVICE_NOT_SUPPORTED; } - char szBuf[64] = {0}; - TUCAM_VALUE_TEXT valText; - valText.nID = TUIDP_GLOBALGAIN; - valText.nTextSize = 64; - valText.pText = &szBuf[0]; - - vector modValues; - int nCnt = 2/*(int)propAttr.dbValMax*/ - (int)propAttr.dbValMin + 1; + vector modValues; - for (int i=0; iStart(numImages,interval_ms); stopOnOverflow_ = stopOnOverflow; m_bAcquisition = true; + return DEVICE_OK; } @@ -2850,7 +2859,7 @@ int CMMTUCam::InsertImage() // Important: metadata about the image are generated here: Metadata md; - md.put("Camera", label); + md.put(MM::g_Keyword_Metadata_CameraLabel, label); md.put(MM::g_Keyword_Elapsed_Time_ms, CDeviceUtils::ConvertToString((timeStamp - sequenceStartTime_).getMsec())); md.put(MM::g_Keyword_Metadata_ROI_X, CDeviceUtils::ConvertToString( (long) roiX_)); md.put(MM::g_Keyword_Metadata_ROI_Y, CDeviceUtils::ConvertToString( (long) roiY_)); @@ -2864,7 +2873,7 @@ int CMMTUCam::InsertImage() char szTemp[256] = {0}; sprintf(szTemp, "%.3f", m_fCurTemp); md.put("Temperature", szTemp); - + MMThreadGuard g(imgPixelsLock_); const unsigned char* pI; @@ -2875,7 +2884,7 @@ int CMMTUCam::InsertImage() unsigned int b = GetImageBytesPerPixel(); int ret = GetCoreCallback()->InsertImage(this, pI, w, h, b, md.Serialize().c_str()); - + if (!stopOnOverflow_ && ret == DEVICE_BUFFER_OVERFLOW) { // do not stop on overflow - just reset the buffer @@ -2905,8 +2914,7 @@ int CMMTUCam::RunSequenceOnThread(MM::MMTime startTime) } } - ret = WaitForFrame(img_); - + ret = WaitForFrame(img_, INFINITE); /* if (!fastImage_) { @@ -2983,9 +2991,9 @@ void CTUCamThread::Start(long numImages, double intervalMs) stop_ = false; suspend_=false; activate(); - actualDuration_ = MM::MMTime{}; + actualDuration_ = MM::MMTime{}; startTime_= camera_->GetCurrentMMTime(); - lastFrameTime_ = MM::MMTime{}; + lastFrameTime_ = MM::MMTime{}; } bool CTUCamThread::IsStopped() @@ -3308,7 +3316,8 @@ int CMMTUCam::OnBinningSum(MM::PropertyBase* pProp, MM::ActionType eAct) default: break; } - return ret; + + return ret; } /** @@ -3413,9 +3422,11 @@ int CMMTUCam::OnExposure(MM::PropertyBase* pProp, MM::ActionType eAct) { dblExp = exposureMinimum_; } - else if (dblExp > exposureMaximum_) { + else if (dblExp > exposureMaximum_) + { dblExp = exposureMaximum_; } + TUCAM_Prop_SetValue(m_opCam.hIdxTUCam, TUIDP_EXPOSURETM, dblExp); ret = DEVICE_OK; @@ -3493,7 +3504,7 @@ int CMMTUCam::OnPixelRatio(MM::PropertyBase* pProp, MM::ActionType eAct) double dbVal = 0.0f; pProp->Get(dbVal); - TUCAM_Prop_SetValue(m_opCam.hIdxTUCam, TUIDP_PIXELRATIO, dbVal); + TUCAM_Prop_SetValue(m_opCam.hIdxTUCam, TUIDP_PIXELRATIO, dbVal * 100); ret = DEVICE_OK; } @@ -3504,7 +3515,7 @@ int CMMTUCam::OnPixelRatio(MM::PropertyBase* pProp, MM::ActionType eAct) TUCAM_Prop_GetValue(m_opCam.hIdxTUCam, TUIDP_PIXELRATIO, &dbVal); - pProp->Set(dbVal); + pProp->Set(dbVal / 100); ret = DEVICE_OK; } @@ -4600,61 +4611,63 @@ int CMMTUCam::OnGAINMode(MM::PropertyBase* pProp, MM::ActionType eAct) nImgMode = 2; //STDL } - if (nImgMode != nVal) - { - if (bLiving){ StopCapture(); } - TUCAM_Capa_SetValue(m_opCam.hIdxTUCam, TUIDC_IMGMODESELECT, nImgMode); - if (bLiving){ StartCapture(); } - } - TUCAM_Prop_SetValue(m_opCam.hIdxTUCam, TUIDP_GLOBALGAIN, nGain); - - } - else - { - TUCAM_Prop_GetValue(m_opCam.hIdxTUCam, TUIDP_EXPOSURETM, &dblExp); + if (nImgMode != nVal) + { + if (bLiving){ StopCapture(); } + TUCAM_Capa_SetValue(m_opCam.hIdxTUCam, TUIDC_IMGMODESELECT, nImgMode); + if (bLiving){ StartCapture(); } + } + TUCAM_Prop_SetValue(m_opCam.hIdxTUCam, TUIDP_GLOBALGAIN, nGain); - if (0 == val.compare(g_CMSBIT_ON)){ - nImgMode = 1; //CMS - nGain = 0; - } - else if(0 == val.compare(g_HDRBIT_ON)){ - nImgMode = 2; //HDR - nGain = 0; - } - else if(0 == val.compare(g_HIGHBIT_ON)){ - nImgMode = 2; //High Gain - nGain = 1; - } - else if (0 == val.compare(g_LOWBIT_ON)){ - nImgMode = 2; //High Gain - nGain = 2; - } - else if(0 == val.compare(g_GRHIGH_ON)){ - nImgMode = 3; //Global Reset High Gain - nGain = 1; - } - else if(0 == val.compare(g_GRLOW_ON)){ - nImgMode = 3; //Global Reset Low Gain - nGain = 2; - } - else if (0 == val.compare(g_HSHIGH_ON)){ - nImgMode = 3; //HighSpeed High Gain - nGain = 1; - } - else if (0 == val.compare(g_HSLOW_ON)){ - nImgMode = 4; //HighSpeed Low Gain - nGain = 2; - } + if (IsSupport95V2New()) + UpdateLevelsRange(); + } + else + { + TUCAM_Prop_GetValue(m_opCam.hIdxTUCam, TUIDP_EXPOSURETM, &dblExp); - if (nImgMode != nVal) - { - TUCAM_Capa_SetValue(m_opCam.hIdxTUCam, TUIDC_IMGMODESELECT, nImgMode); - } - TUCAM_Prop_SetValue(m_opCam.hIdxTUCam, TUIDP_GLOBALGAIN, nGain); + if (0 == val.compare(g_CMSBIT_ON)){ + nImgMode = 1; //CMS + nGain = 0; + } + else if (0 == val.compare(g_HDRBIT_ON)){ + nImgMode = 2; //HDR + nGain = 0; + } + else if (0 == val.compare(g_HIGHBIT_ON)){ + nImgMode = 2; //High Gain + nGain = 1; + } + else if (0 == val.compare(g_LOWBIT_ON)){ + nImgMode = 2; //High Gain + nGain = 2; + } + else if (0 == val.compare(g_GRHIGH_ON)){ + nImgMode = 3; //Global Reset High Gain + nGain = 1; + } + else if (0 == val.compare(g_GRLOW_ON)){ + nImgMode = 3; //Global Reset Low Gain + nGain = 2; + } + else if (0 == val.compare(g_HSHIGH_ON)){ + nImgMode = 3; //HighSpeed High Gain + nGain = 1; + } + else if (0 == val.compare(g_HSLOW_ON)){ + nImgMode = 4; //HighSpeed Low Gain + nGain = 2; + } - TUCAM_Prop_SetValue(m_opCam.hIdxTUCam, TUIDP_EXPOSURETM, dblExp); + if (nImgMode != nVal) + { + TUCAM_Capa_SetValue(m_opCam.hIdxTUCam, TUIDC_IMGMODESELECT, nImgMode); + } + TUCAM_Prop_SetValue(m_opCam.hIdxTUCam, TUIDP_GLOBALGAIN, nGain); - UpdateExpRange(); + TUCAM_Prop_SetValue(m_opCam.hIdxTUCam, TUIDP_EXPOSURETM, dblExp); + + UpdateExpRange(); if ((DHYANA_400BSIV2 == m_nPID && m_nBCD > 0x04)) { @@ -4869,8 +4882,8 @@ int CMMTUCam::OnModeSelect(MM::PropertyBase* pProp, MM::ActionType eAct) nGain = 0; } else if (0 == val.compare(g_GLOBALRESET_ON)){ - nImgMode = 5; //GlobalReset Hg - nGain = 1; + nImgMode = 5; //GlobalReset HDR or HG + nGain = (m_nBCD >= 0x2002) ? 0x00 : 0x01; m_rsPara.nMode = 0x00; TUCAM_Capa_SetValue(m_opCam.hIdxTUCam, TUIDC_ROLLINGSCANMODE, m_rsPara.nMode); } @@ -4884,6 +4897,9 @@ int CMMTUCam::OnModeSelect(MM::PropertyBase* pProp, MM::ActionType eAct) UpdateExpRange(); + if (IsSupport400BSIV3New()) + UpdateLevelsRange(); + TUCAM_CAPA_ATTR capaAttr; capaAttr.idCapa = TUIDC_ROLLINGSCANLTD; TUCAM_Capa_GetAttr(m_opCam.hIdxTUCam, &capaAttr); @@ -4896,15 +4912,22 @@ int CMMTUCam::OnModeSelect(MM::PropertyBase* pProp, MM::ActionType eAct) vectorModTgrValues; ModTgrValues.push_back(g_TRIGGER_OFF); - if (0 == val.compare(g_GLOBALRESET_ON)) + + if (IsSupport400BSIV3New()) { - ModTgrValues.push_back(g_TRIGGER_STD); + ModTgrValues.push_back(g_TRIGGER_STDOVERLAP); + ModTgrValues.push_back(g_TRIGGER_STDNONOVERLAP); } else { ModTgrValues.push_back(g_TRIGGER_STD); + } + + if (0 != val.compare(g_GLOBALRESET_ON)) + { ModTgrValues.push_back(g_TRIGGER_SYN); } + ModTgrValues.push_back(g_TRIGGER_SWF); ClearAllowedValues(g_PropNameMdTgr); SetAllowedValues(g_PropNameMdTgr, ModTgrValues); @@ -4917,7 +4940,9 @@ int CMMTUCam::OnModeSelect(MM::PropertyBase* pProp, MM::ActionType eAct) case MM::BeforeGet: { int nVal = 0; + double dbValue = 0; TUCAM_Capa_GetValue(m_opCam.hIdxTUCam, TUIDC_IMGMODESELECT, &nVal); + TUCAM_Prop_GetValue(m_opCam.hIdxTUCam, TUIDP_GLOBALGAIN, &dbValue); m_rsPara.nSlitHeightMin = 1; m_rsPara.nSlitHeightStep = 1; if (1 == nVal) // CMS @@ -4930,7 +4955,7 @@ int CMMTUCam::OnModeSelect(MM::PropertyBase* pProp, MM::ActionType eAct) m_rsPara.nSlitHeightMin = 2; m_rsPara.nSlitHeightStep = 2; } - else if (5 == nVal){ // GlobaelReset HG + else if (5 == nVal){ // GlobaelReset pProp->Set(g_GLOBALRESET_ON); m_rsPara.nMode = 0x00; } @@ -4957,81 +4982,65 @@ int CMMTUCam::OnImageMode(MM::PropertyBase* pProp, MM::ActionType eAct) return DEVICE_NOT_CONNECTED; int ret = DEVICE_ERR; - switch(eAct) - { - case MM::AfterSet: - { - string val; - pProp->Get(val); - - if (val.length() != 0) - { - TUCAM_PROP_ATTR propAttr; - propAttr.nIdxChn= 0; - propAttr.idProp = TUIDP_GLOBALGAIN; - - if (TUCAMRET_SUCCESS == TUCAM_Prop_GetAttr(m_opCam.hIdxTUCam, &propAttr)) - { - char szBuf[64] = {0}; - TUCAM_VALUE_TEXT valText; - valText.nID = TUIDP_GLOBALGAIN; - valText.nTextSize = 64; - valText.pText = &szBuf[0]; + switch (eAct) + { + case MM::AfterSet: + { + string val; + pProp->Get(val); - int nCnt = 2/*(int)propAttr.dbValMax*/ - (int)propAttr.dbValMin + 1; + if (val.length() != 0) + { + if (0 == val.compare(g_HDRBIT_ON)) + { + m_nIdxGain = 0; + } - for (int i=0; iSet(valText.pText); + if (0 == m_nIdxGain) + { + pProp->Set(g_HDRBIT_ON); + } + + if (1 == m_nIdxGain) + { + pProp->Set(g_HIGHBIT_ON); + } + if (2 == m_nIdxGain) + { + pProp->Set(g_LOWBIT_ON); + } ret = DEVICE_OK; } break; @@ -5183,7 +5192,8 @@ int CMMTUCam::OnBitDepth(MM::PropertyBase* pProp, MM::ActionType eAct) TUCAM_Capa_SetValue(m_opCam.hIdxTUCam, TUIDC_BITOFDEPTH, 16); SetPropertyLimits(g_PropNameLLev, 0, 65534); SetPropertyLimits(g_PropNameRLev, 1, 65535); - SetProperty(MM::g_Keyword_PixelType, g_PixelType_16bit); + SetProperty(MM::g_Keyword_PixelType, g_PixelType_16bit); + m_frame.ucElemBytes = 2; } else { @@ -5191,6 +5201,7 @@ int CMMTUCam::OnBitDepth(MM::PropertyBase* pProp, MM::ActionType eAct) SetPropertyLimits(g_PropNameLLev, 0, 254); SetPropertyLimits(g_PropNameRLev, 1, 255); SetProperty(MM::g_Keyword_PixelType, g_PixelType_8bit); + m_frame.ucElemBytes = 1; } if (m_nPID == PID_FL_9BW || PID_FL_9BW_LT == m_nPID || m_nPID == PID_FL_20BW || m_nPID == PID_FL_26BW) @@ -5283,6 +5294,8 @@ int CMMTUCam::OnBitDepthEum(MM::PropertyBase* pProp, MM::ActionType eAct) if (0 == val.compare(valText.pText)) { TUCAM_Capa_SetValue(m_opCam.hIdxTUCam, TUIDC_BITOFDEPTH, i); + m_frame.ucElemBytes = (0 == val.compare(g_PixelType_8bit)) ? 1 : 2; + SetProperty(MM::g_Keyword_PixelType, (0 == val.compare(g_PixelType_8bit)) ? g_PixelType_8bit : g_PixelType_16bit); break; } } @@ -5316,14 +5329,14 @@ int CMMTUCam::OnBitDepthEum(MM::PropertyBase* pProp, MM::ActionType eAct) TUCAM_Capa_GetValueText(m_opCam.hIdxTUCam, &valText); pProp->Set(valText.pText); - ret = DEVICE_OK; - } - break; - default: - break; - } + ret = DEVICE_OK; + } + break; + default: + break; + } - return ret; + return ret; } /** @@ -6377,7 +6390,7 @@ int CMMTUCam::OnTriggerMode(MM::PropertyBase* pProp, MM::ActionType eAct) m_tgrAttr.nTgrMode = TUCCM_TRIGGER_SOFTWARE; } - if (TUCCM_TRIGGER_STANDARD != m_tgrAttr.nTgrMode || TUCTE_EXPTM != m_tgrAttr.nExpMode) + if ((TUCCM_TRIGGER_STANDARD != m_tgrAttr.nTgrMode && TUCCM_TRIGGER_STANDARD_NONOVERLAP != m_tgrAttr.nTgrMode) || TUCTE_EXPTM != m_tgrAttr.nExpMode) m_tgrAttr.nFrames = 1; if (m_bLiving) @@ -6406,7 +6419,7 @@ int CMMTUCam::OnTriggerMode(MM::PropertyBase* pProp, MM::ActionType eAct) } else if (TUCCM_TRIGGER_STANDARD == m_tgrAttr.nTgrMode) { - if (IsSupport95V2New() || IsSupport401DNew() || IsSupport400BSIV3New()) + if (IsSupport95V2New() || IsSupport401DNew() || IsSupport201DNew() || IsSupport400BSIV3New()) { pProp->Set(g_TRIGGER_STDOVERLAP); } @@ -6478,7 +6491,7 @@ int CMMTUCam::OnTriggerExpMode(MM::PropertyBase* pProp, MM::ActionType eAct) if(m_tgrAttr.nTgrMode == TUCCM_TRIGGER_SYNCHRONOUS) m_tgrAttr.nExpMode = TUCTE_WIDTH; - if (TUCCM_TRIGGER_STANDARD != m_tgrAttr.nTgrMode || TUCTE_EXPTM != m_tgrAttr.nExpMode) + if ((TUCCM_TRIGGER_STANDARD != m_tgrAttr.nTgrMode && TUCCM_TRIGGER_STANDARD_NONOVERLAP != m_tgrAttr.nTgrMode) || TUCTE_EXPTM != m_tgrAttr.nExpMode) m_tgrAttr.nFrames = 1; TUCAM_Cap_SetTrigger(m_opCam.hIdxTUCam, m_tgrAttr); @@ -6658,7 +6671,7 @@ int CMMTUCam::OnTriggerFrames(MM::PropertyBase* pProp, MM::ActionType eAct) long lVal = 0; pProp->Get(lVal); - if (TUCCM_TRIGGER_STANDARD == m_tgrAttr.nTgrMode && TUCTE_EXPTM == m_tgrAttr.nExpMode) + if ((TUCCM_TRIGGER_STANDARD == m_tgrAttr.nTgrMode || TUCCM_TRIGGER_STANDARD_NONOVERLAP == m_tgrAttr.nTgrMode) && TUCTE_EXPTM == m_tgrAttr.nExpMode) m_tgrAttr.nFrames = lVal; else m_tgrAttr.nFrames = 1; @@ -6702,25 +6715,25 @@ int CMMTUCam::OnTriggerTotalFrames(MM::PropertyBase* pProp, MM::ActionType eAct) m_tgrAttr.nFrames = lVal; } - TUCAM_Cap_SetTrigger(m_opCam.hIdxTUCam, m_tgrAttr); + TUCAM_Cap_SetTrigger(m_opCam.hIdxTUCam, m_tgrAttr); - ret = DEVICE_OK; - } - break; - case MM::BeforeGet: - { - TUCAM_Cap_GetTrigger(m_opCam.hIdxTUCam, &m_tgrAttr); + ret = DEVICE_OK; + } + break; + case MM::BeforeGet: + { + TUCAM_Cap_GetTrigger(m_opCam.hIdxTUCam, &m_tgrAttr); - pProp->Set((long)(m_tgrAttr.nFrames)); + pProp->Set((long)(m_tgrAttr.nFrames)); - ret = DEVICE_OK; - } - break; - default: - break; - } + ret = DEVICE_OK; + } + break; + default: + break; + } - return ret; + return ret; } int CMMTUCam::OnTriggerDoSoftware(MM::PropertyBase* pProp, MM::ActionType eAct) @@ -7393,6 +7406,41 @@ int CMMTUCam::OnTrgOutWidth(MM::PropertyBase* pProp, MM::ActionType eAct) return ret; } +/** +* Handles "OnWaitForTimeOut" property. +*/ +int CMMTUCam::OnWaitForTimeOut(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (NULL == m_opCam.hIdxTUCam) + return DEVICE_NOT_CONNECTED; + + int ret = DEVICE_ERR; + switch (eAct) + { + case MM::AfterSet: + { + long lVal = 0; + pProp->Get(lVal); + m_nWaitForFrameTimeOut = max(lVal, 10000); + + ret = DEVICE_OK; + } + break; + case MM::BeforeGet: + { + + pProp->Set((long)(m_nWaitForFrameTimeOut)); + + ret = DEVICE_OK; + } + break; + default: + break; + } + + return ret; +} + /** * Handles "ReadoutTime" property. */ @@ -7719,7 +7767,7 @@ int CMMTUCam::ResizeImageBuffer() } char sz[256] = {0}; - sprintf(sz, "[ResizeImageBuffer]:Width:%d, Height:%d, BytesPerPixel:%d\n", valWidth.nValue, valHeight.nValue, m_frame.ucElemBytes * nChnnels); + sprintf(sz, "[ResizeImageBuffer]:Width:%d, Height:%d, BytesPerPixel:%d, %d\n", valWidth.nValue, valHeight.nValue, byteDepth, m_frame.ucElemBytes * nChnnels); OutputDebugString(sz); if (!m_bROI) @@ -7728,7 +7776,7 @@ int CMMTUCam::ResizeImageBuffer() } #ifdef _WIN64 - img_.Resize(valWidth.nValue, valHeight.nValue, (m_frame.ucElemBytes * nChnnels)); + img_.Resize(valWidth.nValue, valHeight.nValue, m_frame.ucElemBytes * nChnnels); #else img_.Resize(valWidth.nValue, valHeight.nValue, (4 == nChnnels ? 4 : (m_frame.ucElemBytes * nChnnels))); #endif @@ -8165,7 +8213,7 @@ void CMMTUCam::RunTemperature() dw = GetTickCount(); - if (isSupportSoftProtect()) // 400BSIV2 BCD = 0x05, 0x07, 0x09 ²»¿Éµ÷·çÉÈÃà»ú + if (isSupportSoftProtect()) // 400BSIV2 BCD = 0x05, 0x07, 0x09 ä¸å¯è°ƒé£Žæ‰‡ç›¸æœº { int nFan = 0; TUCAM_Capa_GetValue(m_opCam.hIdxTUCam, TUIDC_FAN_GEAR, &nFan); @@ -8360,12 +8408,21 @@ int CMMTUCam::StartCapture() } -int CMMTUCam::WaitForFrame(ImgBuffer& img) +int CMMTUCam::WaitForFrame(ImgBuffer& img, int timeOut) { MMThreadGuard g(imgPixelsLock_); + ///double exp = GetExposure(); + int time = timeOut; + + if (INFINITE != timeOut) + { + time = max(m_nWaitForFrameTimeOut, timeOut);///10000 + (int)exp; + } + + ///TUDBG_PRINTF("timeout = %d, m_nWaitForFrameTimeOut = %d", timeout, m_nWaitForFrameTimeOut); m_frame.ucFormatGet = TUFRM_FMT_USUAl; // Set usual format - if (TUCAMRET_SUCCESS == TUCAM_Buf_WaitForFrame(m_opCam.hIdxTUCam, &m_frame, 1000)) + if (TUCAMRET_SUCCESS == TUCAM_Buf_WaitForFrame(m_opCam.hIdxTUCam, &m_frame, time)) { if (img.Height() == 0 || img.Width() == 0 || img.Depth() == 0) return DEVICE_OUT_OF_MEMORY; @@ -8524,7 +8581,7 @@ int CMMTUCam::WaitForFrame(ImgBuffer& img) return DEVICE_OK; } - return DEVICE_NATIVE_MODULE_FAILED; + return DEVICE_NATIVE_MODULE_FAILED; } bool CMMTUCam::SaveRaw(char *pfileName, unsigned char *pData, unsigned long ulSize) @@ -8633,7 +8690,7 @@ void CMMTUCam::UpdateExpRange() propAttr.nIdxChn = 0; propAttr.idProp = TUIDP_EXPOSURETM; TUCAM_Prop_GetAttr(m_opCam.hIdxTUCam, &propAttr); - exposureMinimum_ = propAttr.dbValMin; + exposureMinimum_ = (propAttr.dbValMin - 0.0001); exposureMaximum_ = propAttr.dbValMax; if (PID_FL_9BW == m_nPID || PID_FL_9BW_LT == m_nPID || PID_FL_20BW == m_nPID || PID_FL_26BW == m_nPID) diff --git a/DeviceAdapters/TUCam/MMTUCam.h b/DeviceAdapters/TUCam/MMTUCam.h index 662a53ecc..03bfa01a6 100755 --- a/DeviceAdapters/TUCam/MMTUCam.h +++ b/DeviceAdapters/TUCam/MMTUCam.h @@ -324,6 +324,16 @@ class CMMTUCam : public CCameraBase int OnTrgOutEdgeMode(MM::PropertyBase* pProp, MM::ActionType eAct); int OnTrgOutDelay(MM::PropertyBase* pProp, MM::ActionType eAct); int OnTrgOutWidth(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnWaitForTimeOut(MM::PropertyBase* pProp, MM::ActionType eAct); +/* + int OnMaxExposure(MM::PropertyBase* pProp, MM::ActionType eAct); // 设置æ›å…‰æœ€å¤§å€¼ä¸Šé™ + + int OnMono(MM::PropertyBase* pProp, MM::ActionType eAct); // å½©è‰²æ¨¡å¼ + + int OnTemperatureState(MM::PropertyBase* pProp, MM::ActionType eAct); // 温控开关 + int OnTemperatureCurrent(MM::PropertyBase* pProp, MM::ActionType eAct); // 当å‰æ¸©åº¦ + int OnTemperatureCooling(MM::PropertyBase* pProp, MM::ActionType eAct); // 目标温度 +*/ int OnReadoutTime(MM::PropertyBase* pProp, MM::ActionType eAct); int OnScanMode(MM::PropertyBase* pProp, MM::ActionType eAct); @@ -368,6 +378,7 @@ class CMMTUCam : public CCameraBase bool IsSupport95V2New() { return DHYANA_D95_V2 == m_nPID && m_nBCD >= 0x2000; } bool IsSupport401DNew() { return DHYANA_401D == m_nPID && m_nBCD >= 0x2000; } + bool IsSupport201DNew() { return DHYANA_201D == m_nPID && m_nBCD >= 0x2000; } bool IsSupport400BSIV3New() { return DHYANA_400BSIV3 == m_nPID && m_nBCD >= 0x2000; } bool IsSupportAries16() { return 0xE424 == m_nPID || 0xE425 == m_nPID; } @@ -434,7 +445,7 @@ class CMMTUCam : public CCameraBase int StopCapture(); int StartCapture(); - int WaitForFrame(ImgBuffer& img); + int WaitForFrame(ImgBuffer& img, int timeOut = 10000); bool SaveRaw(char *pfileName, unsigned char *pData, unsigned long ulSize); bool isSupportFanCool(); @@ -451,6 +462,7 @@ class CMMTUCam : public CCameraBase static int s_nNumCam; // The number of cameras static int s_nCntCam; // The count of camera + int m_nWaitForFrameTimeOut; // The WaitForFrameTimeOut int m_nDriverType; // The Driver Type int m_nPID; // The PID int m_nBCD; // The BCD @@ -518,7 +530,7 @@ class CTUCamThread : public MMDeviceThreadBase MM::MMTime lastFrameTime_; MMThreadLock stopLock_; MMThreadLock suspendLock_; -}; +}; #endif //_MMTUCAM_H_ diff --git a/DeviceAdapters/ThorlabsDCxxxx/DC2200.cpp b/DeviceAdapters/ThorlabsDCxxxx/DC2200.cpp index 46eabb179..b1157e0c9 100644 --- a/DeviceAdapters/ThorlabsDCxxxx/DC2200.cpp +++ b/DeviceAdapters/ThorlabsDCxxxx/DC2200.cpp @@ -701,7 +701,7 @@ bool DC2200::dynErrlist_add(int err, std::string descr) dynError->descr = descr; m_dynErrlist.push_back(dynError); - // publish the new error to µManager + // publish the new error to µManager SetErrorText((int)(dynError->err), descr.c_str()); return true; diff --git a/DeviceAdapters/ThorlabsDCxxxx/DC2XXX.cpp b/DeviceAdapters/ThorlabsDCxxxx/DC2XXX.cpp index 6257c02f8..220013fc5 100644 --- a/DeviceAdapters/ThorlabsDCxxxx/DC2XXX.cpp +++ b/DeviceAdapters/ThorlabsDCxxxx/DC2XXX.cpp @@ -751,7 +751,7 @@ bool DC2xxx::dynErrlist_add(int err, std::string descr) dynError->descr = descr; m_dynErrlist.push_back(dynError); - // publish the new error to µManager + // publish the new error to µManager SetErrorText((int)(dynError->err), descr.c_str()); return true; diff --git a/DeviceAdapters/ThorlabsDCxxxx/DC3100.cpp b/DeviceAdapters/ThorlabsDCxxxx/DC3100.cpp index 5e2931d13..785e39479 100644 --- a/DeviceAdapters/ThorlabsDCxxxx/DC3100.cpp +++ b/DeviceAdapters/ThorlabsDCxxxx/DC3100.cpp @@ -707,7 +707,7 @@ bool DC3100::dynErrlist_add(int err, std::string descr) dynError->descr = descr; m_dynErrlist.push_back(dynError); - // publish the new error to µManager + // publish the new error to µManager SetErrorText((int)(dynError->err), descr.c_str()); return true; diff --git a/DeviceAdapters/ThorlabsDCxxxx/DC4100.cpp b/DeviceAdapters/ThorlabsDCxxxx/DC4100.cpp index 9493b9b62..0e85414de 100644 --- a/DeviceAdapters/ThorlabsDCxxxx/DC4100.cpp +++ b/DeviceAdapters/ThorlabsDCxxxx/DC4100.cpp @@ -384,7 +384,7 @@ bool DC4100::dynErrlist_add(int err, std::string descr) dynError->descr = descr; m_dynErrlist.push_back(dynError); - // publish the new error to µManager + // publish the new error to µManager SetErrorText((int)(dynError->err), descr.c_str()); return true; diff --git a/DeviceAdapters/ThorlabsDCxxxx/DCxxxx_Plugin.cpp b/DeviceAdapters/ThorlabsDCxxxx/DCxxxx_Plugin.cpp index 7e7cbc66c..c253ef94f 100644 --- a/DeviceAdapters/ThorlabsDCxxxx/DCxxxx_Plugin.cpp +++ b/DeviceAdapters/ThorlabsDCxxxx/DCxxxx_Plugin.cpp @@ -46,7 +46,7 @@ const char* g_DeviceDC2xxxName = "Thorlabs DC2010/DC2100"; // /////////////////////////////////////////////////////////////////////////////// /*--------------------------------------------------------------------------- - Initialize module data. It publishes the DCxxxx series names to µManager. + Initialize module data. It publishes the DCxxxx series names to µManager. ---------------------------------------------------------------------------*/ MODULE_API void InitializeModuleData() { diff --git a/DeviceAdapters/ThorlabsUSBCamera/ThorlabsUSBCamera.cpp b/DeviceAdapters/ThorlabsUSBCamera/ThorlabsUSBCamera.cpp index 879a7604c..6e64abd4f 100644 --- a/DeviceAdapters/ThorlabsUSBCamera/ThorlabsUSBCamera.cpp +++ b/DeviceAdapters/ThorlabsUSBCamera/ThorlabsUSBCamera.cpp @@ -612,7 +612,7 @@ int ThorlabsUSBCam::InsertImage() Metadata md; char label[MM::MaxStrLength]; this->GetLabel(label); - md.put("Camera", label); + md.put(MM::g_Keyword_Metadata_CameraLabel, label); md.put(MM::g_Keyword_Elapsed_Time_ms, CDeviceUtils::ConvertToString((GetCurrentMMTime() - sequenceStartTime_).getMsec())); md.put(MM::g_Keyword_Metadata_ImageNumber, CDeviceUtils::ConvertToString(imageCounter_)); diff --git a/DeviceAdapters/Thorlabs_ELL14/ELL14.cpp b/DeviceAdapters/Thorlabs_ELL14/ELL14.cpp index d105b98f4..74b59e7fe 100644 --- a/DeviceAdapters/Thorlabs_ELL14/ELL14.cpp +++ b/DeviceAdapters/Thorlabs_ELL14/ELL14.cpp @@ -7,7 +7,7 @@ //// https://www.thorlabs.de/newgrouppage9.cfm?objectgroup_ID=12829 //// //// AUTHOR: Manon Paillat, 2022 -//// developped under the supervision of Florian Ströhl +//// developped under the supervision of Florian Ströhl //// Contact: florian.strohl@uit.no // diff --git a/DeviceAdapters/Thorlabs_ELL14/ELL14.h b/DeviceAdapters/Thorlabs_ELL14/ELL14.h index 5c6f9a7b4..24aa2d4f5 100644 --- a/DeviceAdapters/Thorlabs_ELL14/ELL14.h +++ b/DeviceAdapters/Thorlabs_ELL14/ELL14.h @@ -7,7 +7,7 @@ //// https://www.thorlabs.de/newgrouppage9.cfm?objectgroup_ID=12829 //// //// AUTHOR: Manon Paillat, 2022 -//// developped under the supervision of Florian Ströhl +//// developped under the supervision of Florian Ströhl //// Contact: florian.strohl@uit.no // @@ -102,7 +102,7 @@ class ELL14 : public CGenericBase< ELL14> double pulsesPerRev_; double maxReplyTimeMs_; - double pos_; // in ° + double pos_; // in ° double offset_; // " double jogStep_; // " double relativeMove_; // " diff --git a/DeviceAdapters/TwainCamera/CommonTWAIN.cpp b/DeviceAdapters/TwainCamera/CommonTWAIN.cpp index 590db35b4..92e614686 100644 --- a/DeviceAdapters/TwainCamera/CommonTWAIN.cpp +++ b/DeviceAdapters/TwainCamera/CommonTWAIN.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -* Copyright © 2007 TWAIN Working Group: +* Copyright © 2007 TWAIN Working Group: * Adobe Systems Incorporated, AnyDoc Software Inc., Eastman Kodak Company, * Fujitsu Computer Products of America, JFL Peripheral Solutions Inc., * Ricoh Corporation, and Xerox Corporation. diff --git a/DeviceAdapters/TwoPhoton/BFTest.cpp b/DeviceAdapters/TwoPhoton/BFTest.cpp index f210c03c6..0a99b44cb 100644 Binary files a/DeviceAdapters/TwoPhoton/BFTest.cpp and b/DeviceAdapters/TwoPhoton/BFTest.cpp differ diff --git a/DeviceAdapters/TwoPhoton/TwoPhoton.cpp b/DeviceAdapters/TwoPhoton/TwoPhoton.cpp index 15a046bbd..62df9a22e 100644 --- a/DeviceAdapters/TwoPhoton/TwoPhoton.cpp +++ b/DeviceAdapters/TwoPhoton/TwoPhoton.cpp @@ -1194,8 +1194,8 @@ void BitFlowCamera::GetCosineWarpLUT(vector &new_pixel, int image_width, in /* Again this code can be replaced by simpler code - The correction factor = ø /sin ø where ø = pixel number (from the correct center pixel) * ?ø - ?ø = 2?/freq*127. The corretion factor is calculated for each pixel and applied to the pixel + The correction factor = ψ /sin ψ where ψ = pixel number (from the correct center pixel) * ?ψ + ?ψ = 2?/freq*127. The corretion factor is calculated for each pixel and applied to the pixel */ /*9 Loop to shift pixels to new image LUT*/ diff --git a/DeviceAdapters/UniversalMMHubUsb/ummhUsb.cpp b/DeviceAdapters/UniversalMMHubUsb/ummhUsb.cpp index d9b983ed6..d69ae4f6c 100644 --- a/DeviceAdapters/UniversalMMHubUsb/ummhUsb.cpp +++ b/DeviceAdapters/UniversalMMHubUsb/ummhUsb.cpp @@ -3744,7 +3744,7 @@ int UmmhCamera::InsertImage() // Important: metadata about the image are generated here: Metadata md; - md.put("Camera", label); + md.put(MM::g_Keyword_Metadata_CameraLabel, label); md.put(MM::g_Keyword_Elapsed_Time_ms, CDeviceUtils::ConvertToString((timeStamp - sequenceStartTime_).getMsec())); md.put(MM::g_Keyword_Metadata_ROI_X, CDeviceUtils::ConvertToString( (long) roiX_)); md.put(MM::g_Keyword_Metadata_ROI_Y, CDeviceUtils::ConvertToString( (long) roiY_)); diff --git a/DeviceAdapters/Utilities/DATTLStateDevice.cpp b/DeviceAdapters/Utilities/DATTLStateDevice.cpp index ba3ac1890..a6f7e5020 100644 --- a/DeviceAdapters/Utilities/DATTLStateDevice.cpp +++ b/DeviceAdapters/Utilities/DATTLStateDevice.cpp @@ -37,6 +37,10 @@ extern const char* g_DeviceNameDATTLStateDevice; extern const char* g_normalLogicString; extern const char* g_invertedLogicString; +extern const char* g_InvertLogic; +extern const char* g_TTLVoltage; +extern const char* g_3_3; +extern const char* g_5_0; DATTLStateDevice::DATTLStateDevice() : @@ -124,18 +128,18 @@ int DATTLStateDevice::Initialize() SetPropertyLimits(MM::g_Keyword_State, 0, numPos - 1); pAct = new CPropertyAction(this, &DATTLStateDevice::OnInvert); - ret = CreateStringProperty("Invert Logic", g_normalLogicString, false, pAct); + ret = CreateStringProperty(g_InvertLogic, g_normalLogicString, false, pAct); if (ret != DEVICE_OK) return ret; - AddAllowedValue("Invert Logic", g_normalLogicString); - AddAllowedValue("Invert Logic", g_invertedLogicString); + AddAllowedValue(g_InvertLogic, g_normalLogicString); + AddAllowedValue(g_InvertLogic, g_invertedLogicString); pAct = new CPropertyAction(this, &DATTLStateDevice::OnTTLLevel); - ret = CreateStringProperty("TTL Voltage", "3.3", false, pAct); + ret = CreateStringProperty(g_TTLVoltage, g_3_3, false, pAct); if (ret != DEVICE_OK) return ret; - AddAllowedValue("TTL Voltage", "3.3"); - AddAllowedValue("TTL Voltage", "5.0"); + AddAllowedValue(g_TTLVoltage, g_3_3); + AddAllowedValue(g_TTLVoltage, g_5_0); pAct = new CPropertyAction(this, &DATTLStateDevice::OnLabel); ret = CreateStringProperty(MM::g_Keyword_Label, "0", false, pAct); @@ -387,8 +391,9 @@ int DATTLStateDevice::OnTTLLevel(MM::PropertyBase* pProp, MM::ActionType eAct) { if (eAct == MM::BeforeGet) { - - pProp->Set(CDeviceUtils::ConvertToString(ttlVoltage_)); + char buffer[8]; + snprintf(buffer, sizeof(buffer), "%.1f", ttlVoltage_); + pProp->Set(buffer); } else if (eAct == MM::AfterSet) { diff --git a/DeviceAdapters/Utilities/Utilities.cpp b/DeviceAdapters/Utilities/Utilities.cpp index 59ccd66b4..66698521f 100644 --- a/DeviceAdapters/Utilities/Utilities.cpp +++ b/DeviceAdapters/Utilities/Utilities.cpp @@ -63,6 +63,10 @@ const char* g_SyncNow = "Sync positions now"; const char* g_normalLogicString = "Normal"; const char* g_invertedLogicString = "Inverted"; +const char* g_InvertLogic = "Invert Logic"; +const char* g_TTLVoltage = "TTL Voltage"; +const char* g_3_3 = "3.3"; +const char* g_5_0 = "5.0"; diff --git a/DeviceAdapters/XCiteLed/XLed.cpp b/DeviceAdapters/XCiteLed/XLed.cpp index 2b09bfbd0..d3a3363dd 100644 --- a/DeviceAdapters/XCiteLed/XLed.cpp +++ b/DeviceAdapters/XCiteLed/XLed.cpp @@ -403,9 +403,9 @@ XLed::XLed() XLed::m_sXLedStr[XLed::XL_TriggerDelayTimeLabel] = "L.16 Trigger Delay Time (0-65535)"; // 47: Led device trigger delay time label XLed::m_sXLedStr[XLed::XL_PWMUnitsLabel] = "L.17 IPG Units (0:uS/1:mS/2:S)"; // 48: Led device PWM units label XLed::m_sXLedStr[XLed::XL_LedTempLabel] = "L.18 Current Temperature (Deg.C)"; // 49: Led device temperature label - XLed::m_sXLedStr[XLed::XL_LedMaxTempLabel] = "L.19 Max Allowed Temperature (Deg.C)"; // 50: Led device max allowed temperature label [°C] - XLed::m_sXLedStr[XLed::XL_LedMinTempLabel] = "L.20 Min Allowed Temperature (Deg.C)"; // 51: Led device min allowed temperature label [°C] - XLed::m_sXLedStr[XLed::XL_LedTempHystLabel] = "L.21 Temperature Hysteresis (Deg.C)"; // 52: Led device temperature hysteresis label [°C] + XLed::m_sXLedStr[XLed::XL_LedMaxTempLabel] = "L.19 Max Allowed Temperature (Deg.C)"; // 50: Led device max allowed temperature label [°C] + XLed::m_sXLedStr[XLed::XL_LedMinTempLabel] = "L.20 Min Allowed Temperature (Deg.C)"; // 51: Led device min allowed temperature label [°C] + XLed::m_sXLedStr[XLed::XL_LedTempHystLabel] = "L.21 Temperature Hysteresis (Deg.C)"; // 52: Led device temperature hysteresis label [°C] XLed::m_sXLedStr[XLed::XL_Reserved] = "Reserved"; // 53: Led device software version label } diff --git a/DeviceAdapters/XCiteXT600/XT600.cpp b/DeviceAdapters/XCiteXT600/XT600.cpp index 9823e6b43..3b9000ed1 100644 --- a/DeviceAdapters/XCiteXT600/XT600.cpp +++ b/DeviceAdapters/XCiteXT600/XT600.cpp @@ -233,9 +233,9 @@ XLed::XLed() //XLed::m_sXLedStr[XLed::XL_TriggerDelayTimeLabel] = "L.16 Trigger Delay Time (0-65535)"; // 47: Led device trigger delay time label //XLed::m_sXLedStr[XLed::XL_PWMUnitsLabel] = "L.17 IPG Units (0:uS/1:mS/2:S)"; // 48: Led device PWM units label XLed::m_sXLedStr[XLed::XL_LedTempLabel] = "L.09 Current Temperature (Deg.C)"; // 49: Led device temperature label - XLed::m_sXLedStr[XLed::XL_LedMaxTempLabel] = "L.10 Max Allowed Temperature (Deg.C)"; // 42: Led device max allowed temperature label [°C] - XLed::m_sXLedStr[XLed::XL_LedMinTempLabel] = "L.11 Min Allowed Temperature (Deg.C)"; // 43: Led device min allowed temperature label [°C] - XLed::m_sXLedStr[XLed::XL_LedTempHystLabel] = "L.12 Temperature Hysteresis (Deg.C)"; // 44: Led device temperature hysteresis label [°C] + XLed::m_sXLedStr[XLed::XL_LedMaxTempLabel] = "L.10 Max Allowed Temperature (Deg.C)"; // 42: Led device max allowed temperature label [°C] + XLed::m_sXLedStr[XLed::XL_LedMinTempLabel] = "L.11 Min Allowed Temperature (Deg.C)"; // 43: Led device min allowed temperature label [°C] + XLed::m_sXLedStr[XLed::XL_LedTempHystLabel] = "L.12 Temperature Hysteresis (Deg.C)"; // 44: Led device temperature hysteresis label [°C] XLed::m_sXLedStr[XLed::XL_LedTriggerSequenceLabel] = "L.13 Trigger Sequence"; // 13: TTL Trigger sequence XLed::m_sXLedStr[XLed::XL_LedOnOffStateLabel] = "L.14 On/Off State (1=On 0=Off)"; // 40: Led device ON/OFF state label XLed::m_sXLedStr[XLed::XL_LedIntensityLabel] = "L.15 Intensity (0.0 or ";//5.0 - 100.0)%"; // 41: Led device intensity label diff --git a/DeviceAdapters/XLightV3/XLight_MM.cpp b/DeviceAdapters/XLightV3/XLight_MM.cpp index 006c20d7e..30c3fdc34 100644 --- a/DeviceAdapters/XLightV3/XLight_MM.cpp +++ b/DeviceAdapters/XLightV3/XLight_MM.cpp @@ -369,7 +369,7 @@ std::string XLightHub::BuilCommand (TCmdType eCmdType, TDeviceInfo* pDeviceInfo // ---------------------------------------------------------------------------------------------- int XLightHub::ParseAnswer(std::string pCmd, std::string pAnsw, TCmdType eCmdType , TDeviceInfo* pDeviceInfo){ - // creo il comando che è stato inviato + // creo il comando che è stato inviato std::string StrCmdBase=BuilCommandBase(eCmdType, pDeviceInfo); std::string StrAnsw= pAnsw; std::string StrCmd= pCmd; @@ -379,7 +379,7 @@ int XLightHub::ParseAnswer(std::string pCmd, std::string pAnsw, TCmdType eCmdTyp if (StrAnsw.find(StrCmdBase)!=0){ return ERR_COMMAND_EXECUTION_ERROR; } - // se arrivo qui il comando è presente nella risposta, quindi estraggo quello che rimane + // se arrivo qui il comando è presente nella risposta, quindi estraggo quello che rimane StrAnsw=StrAnsw.substr(StrCmdBase.length()); try { @@ -568,7 +568,7 @@ int XLightHub::IsOnline(TDevicelType DeviceType){ if (ret!=DEVICE_OK) return ret; - // se è un device con base 1 riduco il tutto + // se è un device con base 1 riduco il tutto if (DeviceType>=EMISSION_FT && DeviceType<=EXCITATION_FT){ if (RetValue==0){ InitialPositions[DeviceType-1]=1; diff --git a/DeviceAdapters/Ximea/XIMEACamera.h b/DeviceAdapters/Ximea/XIMEACamera.h index 80fc4c141..bd4c76bd3 100644 --- a/DeviceAdapters/Ximea/XIMEACamera.h +++ b/DeviceAdapters/Ximea/XIMEACamera.h @@ -6,7 +6,7 @@ // DESCRIPTION: XIMEA camera module. // // AUTHOR: Marian Zajko, -// COPYRIGHT: Marian Zajko and XIMEA GmbH, Münster, 2011 +// COPYRIGHT: Marian Zajko and XIMEA GmbH, Münster, 2011 // // LICENSE: This file is distributed under the BSD license. // License text is included with the source distribution. diff --git a/DeviceAdapters/ZWO/MyASICam2.cpp b/DeviceAdapters/ZWO/MyASICam2.cpp index 50b1c077e..86a9b25c3 100644 --- a/DeviceAdapters/ZWO/MyASICam2.cpp +++ b/DeviceAdapters/ZWO/MyASICam2.cpp @@ -93,9 +93,9 @@ inline static void OutputDbgPrint(const char* strOutPutString, ...) MODULE_API void InitializeModuleData() { #ifdef _VEROPTICS - RegisterDevice(g_CameraName, MM::CameraDevice, "Micro-manager Veroptics camera");//³öÏÖÔÚdevice listÀï + RegisterDevice(g_CameraName, MM::CameraDevice, "Micro-manager Veroptics camera");//出现在device list里 #else - RegisterDevice(g_CameraName, MM::CameraDevice, "ZWO ASI camera");//³öÏÖÔÚdevice listÀï + RegisterDevice(g_CameraName, MM::CameraDevice, "ZWO ASI camera");//出现在device list里 #endif RegisterDevice(g_StateDeviceName, MM::StateDevice, "ZWO EFW filter wheel"); } @@ -218,19 +218,19 @@ CMyASICam::CMyASICam() : { ASIGetCameraProperty(&ASICameraInfo, i); #ifdef _VEROPTICS - StrReplace(ASICameraInfo.Name, ASICameraInfo.Name, "ZWO", "Veroptics");//Çø·Ö´óСд - StrReplace(ASICameraInfo.Name, ASICameraInfo.Name, "ASI", "VER");//Çø·Ö´óСд - StrReplace(ASICameraInfo.Name, ASICameraInfo.Name, "mc", "C", false);//²»Çø·Ö´óСд - StrReplace(ASICameraInfo.Name, ASICameraInfo.Name, "mm", "M", false);//²»Çø·Ö´óСд + StrReplace(ASICameraInfo.Name, ASICameraInfo.Name, "ZWO", "Veroptics");//区分大å°å†™ + StrReplace(ASICameraInfo.Name, ASICameraInfo.Name, "ASI", "VER");//区分大å°å†™ + StrReplace(ASICameraInfo.Name, ASICameraInfo.Name, "mc", "C", false);//ä¸åŒºåˆ†å¤§å°å†™ + StrReplace(ASICameraInfo.Name, ASICameraInfo.Name, "mm", "M", false);//ä¸åŒºåˆ†å¤§å°å†™ #endif - strcpy(ConnectedCamName[i], ASICameraInfo.Name);//±£´æÁ¬½ÓµÄÉãÏñÍ·Ãû×Ö + strcpy(ConnectedCamName[i], ASICameraInfo.Name);//ä¿å­˜è¿žæŽ¥çš„æ‘„åƒå¤´åå­— CamIndexValues.push_back(ConnectedCamName[i]); } - CPropertyAction *pAct = new CPropertyAction (this, &CMyASICam::OnSelectCamIndex);//ͨ¹ýÃû×ÖÑ¡Ôñ´ò¿ªµÄÐòºÅ + CPropertyAction *pAct = new CPropertyAction (this, &CMyASICam::OnSelectCamIndex);//通过å字选择打开的åºå· if(iConnectedCamNum > 0) { - strcpy(sz_ModelIndex, ConnectedCamName[0]);//ĬÈÏ´ò¿ªµÚÒ»¸öcamera + strcpy(sz_ModelIndex, ConnectedCamName[0]);//默认打开第一个camera //iCamIndex = 0; ASIGetCameraProperty(&ASICameraInfo, 0); } @@ -244,7 +244,7 @@ CMyASICam::CMyASICam() : } // strcpy(sz_ModelIndex, "DropDown"); - ret = CreateProperty(g_DeviceIndex, sz_ModelIndex, MM::String, false, pAct, true); //Ñ¡ÔñÉãÏñÍ·ÐòºÅ + ret = CreateProperty(g_DeviceIndex, sz_ModelIndex, MM::String, false, pAct, true); //选择摄åƒå¤´åºå· SetAllowedValues(g_DeviceIndex, CamIndexValues); assert(ret == DEVICE_OK); @@ -310,10 +310,10 @@ int CMyASICam::Initialize() // ASIGetCameraProperty(&ASICameraInfo, iCamIndex); #ifdef _VEROPTICS - StrReplace(ASICameraInfo.Name, ASICameraInfo.Name, "ZWO", "Veroptics");//Çø·Ö´óСд - StrReplace(ASICameraInfo.Name, ASICameraInfo.Name, "ASI", "VER");//Çø·Ö´óСд - StrReplace(ASICameraInfo.Name, ASICameraInfo.Name, "mc", "C", false);//²»Çø·Ö´óСд - StrReplace(ASICameraInfo.Name, ASICameraInfo.Name, "mm", "M", false);//²»Çø·Ö´óСд + StrReplace(ASICameraInfo.Name, ASICameraInfo.Name, "ZWO", "Veroptics");//区分大å°å†™ + StrReplace(ASICameraInfo.Name, ASICameraInfo.Name, "ASI", "VER");//区分大å°å†™ + StrReplace(ASICameraInfo.Name, ASICameraInfo.Name, "mc", "C", false);//ä¸åŒºåˆ†å¤§å°å†™ + StrReplace(ASICameraInfo.Name, ASICameraInfo.Name, "mm", "M", false);//ä¸åŒºåˆ†å¤§å°å†™ #endif char *sz_Name = ASICameraInfo.Name; int nRet = CreateStringProperty(MM::g_Keyword_CameraName, sz_Name, true); @@ -666,7 +666,7 @@ int CMyASICam::Shutdown() * (i.e., before readout). This behavior is needed for proper synchronization with the shutter. * Required by the MM::Camera API. */ -int CMyASICam::SnapImage()//ÆعâÆÚ¼äÒª×èÈû +int CMyASICam::SnapImage()//æ›å…‰æœŸé—´è¦é˜»å¡ž { // GenerateImage(); // ASIGetStartPos(iCamIndex, &iStartXImg, &iStartYImg); @@ -830,7 +830,7 @@ unsigned CMyASICam::GetImageHeight() const * Returns image buffer pixel depth in bytes. * Required by the MM::Camera API. */ -unsigned CMyASICam::GetImageBytesPerPixel() const //ÿ¸öÏñËصÄ×Ö½ÚÊý +unsigned CMyASICam::GetImageBytesPerPixel() const //æ¯ä¸ªåƒç´ çš„字节数 { return iPixBytes; } @@ -841,7 +841,7 @@ unsigned CMyASICam::GetImageHeight() const * a guideline on how to interpret pixel values. * Required by the MM::Camera API. */ -unsigned CMyASICam::GetBitDepth() const//ÑÕÉ«µÄ·¶Î§ 8bit »ò 16bit +unsigned CMyASICam::GetBitDepth() const//颜色的范围 8bit 或 16bit { if(ImgType == ASI_IMG_RAW16) { @@ -931,16 +931,16 @@ void CMyASICam::DeleteImgBuf() pRGB64 = 0; } } -int CMyASICam::SetROI(unsigned x, unsigned y, unsigned xSize, unsigned ySize)//bin2ʱµÄÖµÊÇÏà¶ÔÓÚbin2ºóͼÏñÉϵÄ, ¶øASISetStartPos¶¼ÊÇÏà¶ÔÓÚbin1µÄ +int CMyASICam::SetROI(unsigned x, unsigned y, unsigned xSize, unsigned ySize)//bin2时的值是相对于bin2åŽå›¾åƒä¸Šçš„, 而ASISetStartPos都是相对于bin1çš„ { if (xSize == 0 && ySize == 0) ; else { /*20160107 - ÉèÖÃROIÊÇÒÔÏÔʾͼƬΪ²ÎÕÕµÄ,³ÌÐò´«½øÀ´µÄÆðʼµã(x, y)ÊÇÓÃÒÑÏÔʾͼƬµÄÆðʼµã(µ÷ÓÃGetROI()µÃµ½)¼ÓÉÏÊó±êÑ¡ÔñµÄÆ«ÒÆ£¬ - ³ß´çºÍÆðʼµã¶¼ÒÔImgBin/iSetBinËõ·Å, iSetBinÊÇÒªÉèÖõÄbinÖµ - Èç¹ûÓз­×ª, ÔòÒª»»Ëã»ØÕý³£µÄÆðµã×ø±ê + 设置ROI是以显示图片为å‚照的,程åºä¼ è¿›æ¥çš„起始点(x, y)是用已显示图片的起始点(调用GetROI()得到)加上鼠标选择的å移, + 尺寸和起始点都以ImgBin/iSetBin缩放, iSetBin是è¦è®¾ç½®çš„bin值 + 如果有翻转, 则è¦æ¢ç®—回正常的起点åæ ‡ */ switch(ImgFlip) { @@ -964,15 +964,15 @@ void CMyASICam::DeleteImgBuf() iSetHei = iSetHei/2*2; - iSetX = x*ImgBin/iSetBin;//bin¸Ä±äºó, startposÊÇÏà¶ÔÓÚbinºóµÄ»­ÃæµÄ£¬Ò²Òª°´ÕÕ±ÈÀý¸Ä±ä + iSetX = x*ImgBin/iSetBin;//bin改å˜åŽ, startpos是相对于binåŽçš„ç”»é¢çš„,也è¦æŒ‰ç…§æ¯”ä¾‹æ”¹å˜ iSetY = y*ImgBin/iSetBin; iSetX = iSetX/4*4; iSetY = iSetY/2*2; - if(ASISetROIFormat(ASICameraInfo.CameraID, iSetWid, iSetHei, iSetBin, ImgType) == ASI_SUCCESS)//Èç¹ûÉèÖóɹ¦ + if(ASISetROIFormat(ASICameraInfo.CameraID, iSetWid, iSetHei, iSetBin, ImgType) == ASI_SUCCESS)//如果设置æˆåŠŸ { OutputDbgPrint("wid:%d hei:%d bin:%d\n", xSize, ySize, iBin); - DeleteImgBuf();//buff´óС¸Ä±ä + DeleteImgBuf();//buff大å°æ”¹å˜ ASISetStartPos(ASICameraInfo.CameraID, iSetX, iSetY); } ASIGetROIFormat(ASICameraInfo.CameraID, &iROIWidth, &iROIHeight, &iBin, &ImgType); @@ -984,11 +984,11 @@ void CMyASICam::DeleteImgBuf() * Returns the actual dimensions of the current ROI. * Required by the MM::Camera API. */ -int CMyASICam::GetROI(unsigned& x, unsigned& y, unsigned& xSize, unsigned& ySize)//³ÌÐòµ÷ÓÃÕâÀïµÃµ½µ±Ç°ROIÆðµã£¬¼ÓÉÏROIÀïµÄ¾ØÐÎÆðµã£¬µÃµ½ROIÔÙROIµÄÆðµã +int CMyASICam::GetROI(unsigned& x, unsigned& y, unsigned& xSize, unsigned& ySize)//程åºè°ƒç”¨è¿™é‡Œå¾—到当å‰ROI起点,加上ROI里的矩形起点,得到ROIå†ROI的起点 { /* 20160107 - µÃµ½ÏÔʾͼÏñµÄROIÐÅÏ¢ - Èç¹ûÓз­×ª, Òª»»Ëã³É·´·½Ïò±ßµÄ×ø±ê, ·½±ã³ÌÐòÏà¼ÓµÃµ½ÐÂROI,ÔÙ»»Ëã»ØÕý³£·½ÏòµÄ×ø±ê*/ + 得到显示图åƒçš„ROIä¿¡æ¯ + 如果有翻转, è¦æ¢ç®—æˆåæ–¹å‘边的åæ ‡, 方便程åºç›¸åŠ å¾—到新ROI,å†æ¢ç®—回正常方å‘çš„åæ ‡*/ x = ImgStartX; y = ImgStartY; @@ -1070,7 +1070,7 @@ int CMyASICam::GetBinning() const */ int CMyASICam::SetBinning(int binF) { - return SetProperty(MM::g_Keyword_Binning, CDeviceUtils::ConvertToString(binF));//¾ÍÊÇonBinning(, afterSet) + return SetProperty(MM::g_Keyword_Binning, CDeviceUtils::ConvertToString(binF));//就是onBinning(, afterSet) } int CMyASICam::PrepareSequenceAcqusition() @@ -1108,7 +1108,7 @@ int CMyASICam::StartSequenceAcquisition(long numImages, double interval_ms, bool Status = capturing; OutputDbgPrint("StartSeqAcq\n"); - thd_->Start(numImages,interval_ms);//¿ªÊ¼Ïß³Ì + thd_->Start(numImages,interval_ms);//开始线程 return DEVICE_OK; } @@ -1147,7 +1147,7 @@ int CMyASICam::InsertImage() // Important: metadata about the image are generated here: Metadata md; - md.put("Camera", label); + md.put(MM::g_Keyword_Metadata_CameraLabel, label); char buf[MM::MaxStrLength]; GetProperty(MM::g_Keyword_Binning, buf); @@ -1159,7 +1159,7 @@ int CMyASICam::InsertImage() pI = GetImageBuffer(); int ret = 0; ret = GetCoreCallback()->InsertImage(this, pI, iROIWidth, iROIHeight, iPixBytes, md.Serialize().c_str()); - if (ret == DEVICE_BUFFER_OVERFLOW)//»º³åÇøÂúÁËÒªÇå¿Õ, ·ñÔò²»ÄܼÌÐø²åÈëͼÏñ¶ø¿¨×¡ + if (ret == DEVICE_BUFFER_OVERFLOW)//缓冲区满了è¦æ¸…空, å¦åˆ™ä¸èƒ½ç»§ç»­æ’入图åƒè€Œå¡ä½ { // do not stop on overflow - just reset the buffer GetCoreCallback()->ClearImageBuffer(this); @@ -1177,10 +1177,10 @@ int CMyASICam::StopSequenceAcquisition() { if (!thd_->IsStopped()) { - thd_->Stop();//Í£Ö¹Ïß³Ì + thd_->Stop();//åœæ­¢çº¿ç¨‹ OutputDbgPrint("StopSeqAcq bf wait\n"); // if(!thd_->IsStopped()) - thd_->wait();//µÈ´ýÏß³ÌÍ˳ö + thd_->wait();//等待线程退出 OutputDbgPrint("StopSeqAcq af wait\n"); } // if(Status == capturing) @@ -1220,21 +1220,21 @@ int CMyASICam::OnBinning(MM::PropertyBase* pProp, MM::ActionType eAct) char binF; binF = binSize; - if( !thd_->IsStopped() )//micro managerÖ÷Ãæ°åÀïbinʱ»áÏÈÓɳÌÐòÍ£Ö¹ÔÙÉèÖ㬶øÔÚpropertyÀïÉèÖÃbin²»»áÍ£Ö¹£¬µ¼Ö´íÎó£¬ËùÒÔ²»Í£Ö¹Ê±²»ÄÜÉèÖà + if( !thd_->IsStopped() )//micro manager主é¢æ¿é‡Œbin时会先由程åºåœæ­¢å†è®¾ç½®ï¼Œè€Œåœ¨property里设置binä¸ä¼šåœæ­¢ï¼Œå¯¼è‡´é”™è¯¯ï¼Œæ‰€ä»¥ä¸åœæ­¢æ—¶ä¸èƒ½è®¾ç½® return DEVICE_CAMERA_BUSY_ACQUIRING; - /* binºóµÄ ÆðʼµãºÍ³ß´çÊÇ °ÑÉèÖÃÖµ°´ÕÕ old Bin/new Bin Ëõ·ÅµÄ*/ + /* binåŽçš„ 起始点和尺寸是 把设置值按照 old Bin/new Bin 缩放的*/ iSetWid = iSetWid*iSetBin/binF;// 2->1, *2 iSetHei = iSetHei*iSetBin/binF;//1->2. *0.5 iSetWid = iSetWid/8*8; iSetHei = iSetHei/2*2; - iSetX = iSetX*iSetBin/binF;//bin¸Ä±äºó, startposÊÇÏà¶ÔÓÚbinºóµÄ»­ÃæµÄ£¬Ò²Òª°´ÕÕ±ÈÀý¸Ä±ä + iSetX = iSetX*iSetBin/binF;//bin改å˜åŽ, startpos是相对于binåŽçš„ç”»é¢çš„,也è¦æŒ‰ç…§æ¯”ä¾‹æ”¹å˜ iSetY = iSetY*iSetBin/binF; if(ASISetROIFormat(ASICameraInfo.CameraID, iSetWid, iSetHei, binF, ImgType) == ASI_SUCCESS) { DeleteImgBuf(); - ASISetStartPos(ASICameraInfo.CameraID, iSetX, iSetY);//»áÖØмÆËãstartx ºÍstarty£¬ºÍËùÑ¡ÇøÓò²»Í¬£¬Òò´ËÒªÖØÐÂÉèÖà + ASISetStartPos(ASICameraInfo.CameraID, iSetX, iSetY);//会é‡æ–°è®¡ç®—startx å’Œstarty,和所选区域ä¸åŒï¼Œå› æ­¤è¦é‡æ–°è®¾ç½® } ASIGetROIFormat(ASICameraInfo.CameraID, &iROIWidth, &iROIHeight, &iBin, &ImgType); iSetBin = binF; @@ -1253,7 +1253,7 @@ int CMyASICam::OnBinning(MM::PropertyBase* pProp, MM::ActionType eAct) */ int CMyASICam::OnPixelType(MM::PropertyBase* pProp, MM::ActionType eAct) { - if (eAct == MM::AfterSet)//´Ó¿Ø¼þµÃµ½Ñ¡¶¨µÄÖµ + if (eAct == MM::AfterSet)//从控件得到选定的值 { string val; pProp->Get(val); @@ -1295,7 +1295,7 @@ int CMyASICam::OnPixelType(MM::PropertyBase* pProp, MM::ActionType eAct) } - else if (eAct == MM::BeforeGet)//Öµ¸ø¿Ø¼þÏÔʾ + else if (eAct == MM::BeforeGet)//值给控件显示 { ASIGetROIFormat(ASICameraInfo.CameraID, &iROIWidth, &iROIHeight, &iBin, &ImgType); @@ -1354,7 +1354,7 @@ int CMyASICam::OnGain(MM::PropertyBase* pProp, MM::ActionType eAct) int CMyASICam::OnSelectCamIndex(MM::PropertyBase* pProp, MM::ActionType eAct) { string str; - if (eAct == MM::AfterSet)//´Ó¿Ø¼þµÃµ½Ñ¡¶¨µÄÖµ + if (eAct == MM::AfterSet)//从控件得到选定的值 { pProp->Get(str); for(int i = 0; i < iConnectedCamNum; i++) @@ -1368,7 +1368,7 @@ int CMyASICam::OnSelectCamIndex(MM::PropertyBase* pProp, MM::ActionType eAct) } } } - else if (eAct == MM::BeforeGet)//Öµ¸ø¿Ø¼þÏÔʾ + else if (eAct == MM::BeforeGet)//值给控件显示 { pProp->Set(sz_ModelIndex); } @@ -1402,12 +1402,12 @@ int CMyASICam::OnBrightness(MM::PropertyBase* pProp,MM::ActionType eAct) { long lVal; ASI_BOOL bAuto; - if (eAct == MM::AfterSet)//´Ó¿Ø¼þµÃµ½Ñ¡¶¨µÄÖµ + if (eAct == MM::AfterSet)//从控件得到选定的值 { pProp->Get(lVal); ASISetControlValue(ASICameraInfo.CameraID,ASI_BRIGHTNESS, lVal, ASI_FALSE); } - else if (eAct == MM::BeforeGet)//Öµ¸ø¿Ø¼þÏÔʾ + else if (eAct == MM::BeforeGet)//值给控件显示 { ASIGetControlValue(ASICameraInfo.CameraID, ASI_BRIGHTNESS, &lVal, &bAuto); pProp->Set(lVal); @@ -1423,12 +1423,12 @@ int CMyASICam::OnUSBTraffic(MM::PropertyBase* pProp, MM::ActionType eAct) { long lVal; ASI_BOOL bAuto; - if (eAct == MM::AfterSet)//´Ó¿Ø¼þµÃµ½Ñ¡¶¨µÄÖµ + if (eAct == MM::AfterSet)//从控件得到选定的值 { pProp->Get(lVal); ASISetControlValue(ASICameraInfo.CameraID,ASI_BANDWIDTHOVERLOAD, lVal, ASI_FALSE); } - else if (eAct == MM::BeforeGet)//Öµ¸ø¿Ø¼þÏÔʾ + else if (eAct == MM::BeforeGet)//值给控件显示 { ASIGetControlValue(ASICameraInfo.CameraID,ASI_BANDWIDTHOVERLOAD, &lVal, &bAuto); pProp->Set(lVal); @@ -1444,7 +1444,7 @@ int CMyASICam::OnUSB_Auto(MM::PropertyBase* pProp, MM::ActionType eAct) { long lVal; ASI_BOOL bAuto; - if (eAct == MM::AfterSet)//´Ó¿Ø¼þµÃµ½Ñ¡¶¨µÄÖµ + if (eAct == MM::AfterSet)//从控件得到选定的值 { ASIGetControlValue(ASICameraInfo.CameraID,ASI_BANDWIDTHOVERLOAD, &lVal, &bAuto); string strVal; @@ -1453,7 +1453,7 @@ int CMyASICam::OnUSB_Auto(MM::PropertyBase* pProp, MM::ActionType eAct) ASISetControlValue(ASICameraInfo.CameraID,ASI_BANDWIDTHOVERLOAD, lVal, bAuto); // SetPropertyReadOnly(g_Keyword_USBTraffic, bAuto); } - else if (eAct == MM::BeforeGet)//Öµ¸ø¿Ø¼þÏÔʾ + else if (eAct == MM::BeforeGet)//值给控件显示 { ASIGetControlValue(ASICameraInfo.CameraID,ASI_BANDWIDTHOVERLOAD, &lVal, &bAuto); pProp->Set(bAuto==ASI_TRUE?g_Keyword_on:g_Keyword_off); @@ -1473,11 +1473,11 @@ int CMyASICam::OnCoolerOn(MM::PropertyBase* pProp, MM::ActionType eAct) { // ASIGetControlValue(iCamIndex, ASI_TARGET_TEMP, &lVal, &bAuto); string strVal; - pProp->Get(strVal);//´Ó¿Ø¼þµÃµ½Ñ¡¶¨µÄÖµ + pProp->Get(strVal);//从控件得到选定的值 lVal = !strVal.compare(g_Keyword_on); ASISetControlValue(ASICameraInfo.CameraID, ASI_COOLER_ON, lVal, ASI_FALSE); } - else if (eAct == MM::BeforeGet)//Öµ¸ø¿Ø¼þÏÔʾ + else if (eAct == MM::BeforeGet)//值给控件显示 { ASIGetControlValue(ASICameraInfo.CameraID, ASI_COOLER_ON, &lVal, &bAuto); pProp->Set(lVal > 0?g_Keyword_on:g_Keyword_off); @@ -1495,11 +1495,11 @@ int CMyASICam::OnHeater(MM::PropertyBase* pProp, MM::ActionType eAct) { // ASIGetControlValue(iCamIndex, ASI_TARGET_TEMP, &lVal, &bAuto); string strVal; - pProp->Get(strVal);//´Ó¿Ø¼þµÃµ½Ñ¡¶¨µÄÖµ + pProp->Get(strVal);//从控件得到选定的值 lVal = !strVal.compare(g_Keyword_on); ASISetControlValue(ASICameraInfo.CameraID, ASI_ANTI_DEW_HEATER, lVal, ASI_FALSE); } - else if (eAct == MM::BeforeGet)//Öµ¸ø¿Ø¼þÏÔʾ + else if (eAct == MM::BeforeGet)//值给控件显示 { ASIGetControlValue(ASICameraInfo.CameraID, ASI_ANTI_DEW_HEATER, &lVal, &bAuto); pProp->Set(lVal > 0?g_Keyword_on:g_Keyword_off); @@ -1516,10 +1516,10 @@ int CMyASICam::OnTargetTemp(MM::PropertyBase* pProp, MM::ActionType eAct) if (eAct == MM::AfterSet) { ASIGetControlValue(ASICameraInfo.CameraID,ASI_TARGET_TEMP, &lVal, &bAuto); - pProp->Get(lVal);//´Ó¿Ø¼þµÃµ½Ñ¡¶¨µÄÖµ->±äÁ¿ + pProp->Get(lVal);//从控件得到选定的值->å˜é‡ ASISetControlValue(ASICameraInfo.CameraID,ASI_TARGET_TEMP, lVal, bAuto); } - else if (eAct == MM::BeforeGet)//±äÁ¿Öµ->¿Ø¼þÏÔʾ + else if (eAct == MM::BeforeGet)//å˜é‡å€¼->控件显示 { ASIGetControlValue(ASICameraInfo.CameraID,ASI_TARGET_TEMP, &lVal, &bAuto); pProp->Set(lVal); @@ -1535,10 +1535,10 @@ int CMyASICam::OnCoolerPowerPerc(MM::PropertyBase* pProp, MM::ActionType eAct) ASI_BOOL bAuto; if (eAct == MM::AfterSet) { - pProp->Get(lVal);//´Ó¿Ø¼þµÃµ½Ñ¡¶¨µÄÖµ->±äÁ¿ + pProp->Get(lVal);//从控件得到选定的值->å˜é‡ } - else if (eAct == MM::BeforeGet)//±äÁ¿Öµ->¿Ø¼þÏÔʾ + else if (eAct == MM::BeforeGet)//å˜é‡å€¼->控件显示 { ASIGetControlValue(ASICameraInfo.CameraID,ASI_COOLER_POWER_PERC, &lVal, &bAuto); pProp->Set(lVal); @@ -1554,10 +1554,10 @@ int CMyASICam::OnWB_R(MM::PropertyBase* pProp, MM::ActionType eAct) ASI_BOOL bAuto; if (eAct == MM::AfterSet) { - pProp->Get(lVal);//´Ó¿Ø¼þµÃµ½Ñ¡¶¨µÄÖµ->±äÁ¿ + pProp->Get(lVal);//从控件得到选定的值->å˜é‡ ASISetControlValue(ASICameraInfo.CameraID,ASI_WB_R, lVal, ASI_FALSE); } - else if (eAct == MM::BeforeGet)//±äÁ¿Öµ->¿Ø¼þÏÔʾ + else if (eAct == MM::BeforeGet)//å˜é‡å€¼->控件显示 { ASIGetControlValue(ASICameraInfo.CameraID,ASI_WB_R, &lVal, &bAuto); pProp->Set(lVal); @@ -1573,10 +1573,10 @@ int CMyASICam::OnWB_B(MM::PropertyBase* pProp, MM::ActionType eAct) ASI_BOOL bAuto; if (eAct == MM::AfterSet) { - pProp->Get(lVal);//´Ó¿Ø¼þµÃµ½Ñ¡¶¨µÄÖµ->±äÁ¿ + pProp->Get(lVal);//从控件得到选定的值->å˜é‡ ASISetControlValue(ASICameraInfo.CameraID,ASI_WB_B, lVal, ASI_FALSE); } - else if (eAct == MM::BeforeGet)//±äÁ¿Öµ->¿Ø¼þÏÔʾ + else if (eAct == MM::BeforeGet)//å˜é‡å€¼->控件显示 { ASIGetControlValue(ASICameraInfo.CameraID,ASI_WB_B, &lVal, &bAuto); pProp->Set(lVal); @@ -1594,14 +1594,14 @@ int CMyASICam::OnAutoWB(MM::PropertyBase* pProp, MM::ActionType eAct) if (eAct == MM::AfterSet) { ASIGetControlValue(ASICameraInfo.CameraID,ASI_WB_B, &lVal, &bAuto); - pProp->Get(strVal);//´Ó¿Ø¼þµÃµ½Ñ¡¶¨µÄÖµ->±äÁ¿ + pProp->Get(strVal);//从控件得到选定的值->å˜é‡ bAuto = strVal.compare(g_Keyword_on)?ASI_FALSE:ASI_TRUE; ASISetControlValue(ASICameraInfo.CameraID,ASI_WB_B, lVal, bAuto); // SetPropertyReadOnly(g_Keyword_WB_R,bAuto ); // SetPropertyReadOnly(g_Keyword_WB_B,bAuto ); } - else if (eAct == MM::BeforeGet)//±äÁ¿Öµ->¿Ø¼þÏÔʾ + else if (eAct == MM::BeforeGet)//å˜é‡å€¼->控件显示 { ASIGetControlValue(ASICameraInfo.CameraID,ASI_WB_B, &lVal, &bAuto); pProp->Set(bAuto?g_Keyword_on:g_Keyword_off); @@ -1617,13 +1617,13 @@ int CMyASICam::OnGamma(MM::PropertyBase* pProp, MM::ActionType eAct) { long lVal; ASI_BOOL bAuto; - if (eAct == MM::AfterSet)//´Ó¿Ø¼þµÃµ½Ñ¡¶¨µÄÖµ->±äÁ¿ + if (eAct == MM::AfterSet)//从控件得到选定的值->å˜é‡ { pProp->Get(lVal); ASISetControlValue(ASICameraInfo.CameraID,ASI_GAMMA, lVal, ASI_FALSE); } - else if(eAct == MM::BeforeGet)//±äÁ¿Öµ->¿Ø¼þÏÔʾ + else if(eAct == MM::BeforeGet)//å˜é‡å€¼->控件显示 { ASIGetControlValue(ASICameraInfo.CameraID,ASI_GAMMA, &lVal, &bAuto); pProp->Set(lVal); @@ -1637,7 +1637,7 @@ int CMyASICam::OnAutoExp(MM::PropertyBase* pProp, MM::ActionType eAct) { long lVal; ASI_BOOL bAuto; - if (eAct == MM::AfterSet)//´Ó¿Ø¼þµÃµ½Ñ¡¶¨µÄÖµ->±äÁ¿ + if (eAct == MM::AfterSet)//从控件得到选定的值->å˜é‡ { ASIGetControlValue(ASICameraInfo.CameraID,ASI_EXPOSURE, &lVal, &bAuto); string strVal; @@ -1645,7 +1645,7 @@ int CMyASICam::OnAutoExp(MM::PropertyBase* pProp, MM::ActionType eAct) bAuto = strVal.compare(g_Keyword_on)?ASI_FALSE:ASI_TRUE; ASISetControlValue(ASICameraInfo.CameraID,ASI_EXPOSURE, lVal, bAuto); } - else if(eAct == MM::BeforeGet)//±äÁ¿Öµ->¿Ø¼þÏÔʾ + else if(eAct == MM::BeforeGet)//å˜é‡å€¼->控件显示 { ASIGetControlValue(ASICameraInfo.CameraID,ASI_EXPOSURE, &lVal, &bAuto); pProp->Set(bAuto?g_Keyword_on:g_Keyword_off); @@ -1660,7 +1660,7 @@ int CMyASICam::OnAutoGain(MM::PropertyBase* pProp, MM::ActionType eAct) { long lVal; ASI_BOOL bAuto; - if (eAct == MM::AfterSet)//´Ó¿Ø¼þµÃµ½Ñ¡¶¨µÄÖµ->±äÁ¿ + if (eAct == MM::AfterSet)//从控件得到选定的值->å˜é‡ { ASIGetControlValue(ASICameraInfo.CameraID,ASI_GAIN, &lVal, &bAuto); string strVal; @@ -1668,7 +1668,7 @@ int CMyASICam::OnAutoGain(MM::PropertyBase* pProp, MM::ActionType eAct) bAuto = strVal.compare(g_Keyword_on)?ASI_FALSE:ASI_TRUE; ASISetControlValue(ASICameraInfo.CameraID,ASI_GAIN, lVal, bAuto); } - else if(eAct == MM::BeforeGet)//±äÁ¿Öµ->¿Ø¼þÏÔʾ + else if(eAct == MM::BeforeGet)//å˜é‡å€¼->控件显示 { ASIGetControlValue(ASICameraInfo.CameraID,ASI_GAIN, &lVal, &bAuto); pProp->Set(bAuto?g_Keyword_on:g_Keyword_off); @@ -1683,7 +1683,7 @@ int CMyASICam::OnFlip(MM::PropertyBase* pProp, MM::ActionType eAct) { long lVal; ASI_BOOL bAuto; - if (eAct == MM::AfterSet)//´Ó¿Ø¼þµÃµ½Ñ¡¶¨µÄÖµ->±äÁ¿ + if (eAct == MM::AfterSet)//从控件得到选定的值->å˜é‡ { ASIGetControlValue(ASICameraInfo.CameraID, ASI_FLIP, &lVal, &bAuto); string strVal; @@ -1698,7 +1698,7 @@ int CMyASICam::OnFlip(MM::PropertyBase* pProp, MM::ActionType eAct) } } - else if(eAct == MM::BeforeGet)//±äÁ¿Öµ->¿Ø¼þÏÔʾ + else if(eAct == MM::BeforeGet)//å˜é‡å€¼->控件显示 { ASIGetControlValue(ASICameraInfo.CameraID,ASI_FLIP, &lVal, &bAuto); pProp->Set(FlipArr[lVal]); @@ -1712,14 +1712,14 @@ int CMyASICam::OnHighSpeedMod(MM::PropertyBase* pProp, MM::ActionType eAct) { long lVal; ASI_BOOL bAuto; - if (eAct == MM::AfterSet)//´Ó¿Ø¼þµÃµ½Ñ¡¶¨µÄÖµ->±äÁ¿ + if (eAct == MM::AfterSet)//从控件得到选定的值->å˜é‡ { string strVal; pProp->Get(strVal); lVal = strVal.compare(g_Keyword_on)?0:1; ASISetControlValue(ASICameraInfo.CameraID,ASI_HIGH_SPEED_MODE, lVal, ASI_FALSE); } - else if(eAct == MM::BeforeGet)//±äÁ¿Öµ->¿Ø¼þÏÔʾ + else if(eAct == MM::BeforeGet)//å˜é‡å€¼->控件显示 { ASIGetControlValue(ASICameraInfo.CameraID,ASI_HIGH_SPEED_MODE, &lVal, &bAuto); pProp->Set(lVal?g_Keyword_on:g_Keyword_off); @@ -1733,14 +1733,14 @@ int CMyASICam::OnHardwareBin(MM::PropertyBase* pProp, MM::ActionType eAct) { long lVal; ASI_BOOL bAuto; - if (eAct == MM::AfterSet)//´Ó¿Ø¼þµÃµ½Ñ¡¶¨µÄÖµ->±äÁ¿ + if (eAct == MM::AfterSet)//从控件得到选定的值->å˜é‡ { string strVal; pProp->Get(strVal); lVal = strVal.compare(g_Keyword_on)?0:1; ASISetControlValue(ASICameraInfo.CameraID,ASI_HARDWARE_BIN, lVal, ASI_FALSE); } - else if(eAct == MM::BeforeGet)//±äÁ¿Öµ->¿Ø¼þÏÔʾ + else if(eAct == MM::BeforeGet)//å˜é‡å€¼->控件显示 { ASIGetControlValue(ASICameraInfo.CameraID,ASI_HARDWARE_BIN, &lVal, &bAuto); pProp->Set(lVal?g_Keyword_on:g_Keyword_off); @@ -1850,14 +1850,14 @@ CMyEFW::CMyEFW() : for(int i = 0; i < iConnectedEFWNum; i++) { EFWGetID(i, &EFWInfo.ID); - sprintf(ConnectedEFWName[i], "EFW (ID %d)", EFWInfo.ID);//±£´æÃû×Ö + sprintf(ConnectedEFWName[i], "EFW (ID %d)", EFWInfo.ID);//ä¿å­˜åå­— EFWIndexValues.push_back(ConnectedEFWName[i]); } - CPropertyAction *pAct = new CPropertyAction (this, &CMyEFW::OnSelectEFWIndex);//ͨ¹ýÃû×ÖÑ¡Ôñ´ò¿ªµÄÐòºÅ + CPropertyAction *pAct = new CPropertyAction (this, &CMyEFW::OnSelectEFWIndex);//通过å字选择打开的åºå· if(iConnectedEFWNum > 0) { - strcpy(sz_ModelIndex, ConnectedEFWName[0]);//ĬÈÏ´ò¿ªµÚÒ»¸ö + strcpy(sz_ModelIndex, ConnectedEFWName[0]);//默认打开第一个 //iCamIndex = 0; EFWGetID(0, &EFWInfo.ID); } @@ -1866,7 +1866,7 @@ CMyEFW::CMyEFW() : strcpy(sz_ModelIndex,"no EFW connected"); } // strcpy(sz_ModelIndex, "DropDown"); - ret = CreateProperty(g_DeviceIndex, sz_ModelIndex, MM::String, false, pAct, true); //Ñ¡ÔñÉãÏñÍ·ÐòºÅ + ret = CreateProperty(g_DeviceIndex, sz_ModelIndex, MM::String, false, pAct, true); //选择摄åƒå¤´åºå· SetAllowedValues(g_DeviceIndex, EFWIndexValues); assert(ret == DEVICE_OK); } @@ -1942,7 +1942,7 @@ int CMyEFW::Initialize() return DEVICE_OK; } -bool CMyEFW::Busy()//·µ»Øtrueʱ²»Ë¢ÐÂlabelºÍstate +bool CMyEFW::Busy()//返回trueæ—¶ä¸åˆ·æ–°labelå’Œstate { if(bPosWait)// { @@ -1983,9 +1983,9 @@ int CMyEFW::Shutdown() // Action handlers /////////////////////////////////////////////////////////////////////////////// -int CMyEFW::OnState(MM::PropertyBase* pProp, MM::ActionType eAct)//CStateDeviceBase::OnLabel »áµ÷ÓÃÕâÀï +int CMyEFW::OnState(MM::PropertyBase* pProp, MM::ActionType eAct)//CStateDeviceBase::OnLabel 会调用这里 { - if (eAct == MM::BeforeGet)//Öµ¸ø¿Ø¼þÏÔʾ + if (eAct == MM::BeforeGet)//值给控件显示 { int pos; EFWGetPosition(EFWInfo.ID, &pos); @@ -1998,7 +1998,7 @@ int CMyEFW::Shutdown() } // nothing to do, let the caller to use cached property } - else if (eAct == MM::AfterSet)//´Ó¿Ø¼þµÃµ½Ñ¡¶¨µÄÖµ->±äÁ¿ + else if (eAct == MM::AfterSet)//从控件得到选定的值->å˜é‡ { // Set timer for the Busy signal // changedTime_ = GetCurrentMMTime(); @@ -2024,7 +2024,7 @@ int CMyEFW::Shutdown() int CMyEFW::OnSelectEFWIndex(MM::PropertyBase* pProp, MM::ActionType eAct) { string str; - if (eAct == MM::AfterSet)//´Ó¿Ø¼þµÃµ½Ñ¡¶¨µÄÖµ + if (eAct == MM::AfterSet)//从控件得到选定的值 { pProp->Get(str); for(int i = 0; i < iConnectedEFWNum; i++) @@ -2038,7 +2038,7 @@ int CMyEFW::OnSelectEFWIndex(MM::PropertyBase* pProp, MM::ActionType eAct) } } } - else if (eAct == MM::BeforeGet)//Öµ¸ø¿Ø¼þÏÔʾ + else if (eAct == MM::BeforeGet)//值给控件显示 { pProp->Set(sz_ModelIndex); } diff --git a/DeviceAdapters/ZWO/MyASICam2.h b/DeviceAdapters/ZWO/MyASICam2.h index b0b466df4..6710bf6b1 100644 --- a/DeviceAdapters/ZWO/MyASICam2.h +++ b/DeviceAdapters/ZWO/MyASICam2.h @@ -99,10 +99,10 @@ class CMyASICam:public CCameraBase unsigned long iBufSize; - int iPixBytes;//ÿ¸öÏñËØ×Ö½ÚÊý + int iPixBytes;//æ¯ä¸ªåƒç´ å­—节数 int iComponents; - int iROIWidth, iROIHeight, iBin;//sensorµÄ×ø±ê³ß´çÐÅÏ¢ - int iSetWid, iSetHei, iSetBin, iSetX, iSetY; //ÒªÉèÖõÄ×ø±ê³ß´çÐÅÏ¢ + int iROIWidth, iROIHeight, iBin;//sensorçš„åæ ‡å°ºå¯¸ä¿¡æ¯ + int iSetWid, iSetHei, iSetBin, iSetX, iSetY; //è¦è®¾ç½®çš„åæ ‡å°ºå¯¸ä¿¡æ¯ ASI_IMG_TYPE ImgType; friend class SequenceThread; @@ -111,7 +111,7 @@ class CMyASICam:public CCameraBase ASI_CAMERA_INFO ASICameraInfo; int iCtrlNum; ASI_FLIP_STATUS ImgFlip; - int ImgStartX, ImgStartY, ImgBin, ImgWid, ImgHei;//ËùÏÔʾͼÏñµÄ×ø±ê³ß´çÐÅÏ¢ + int ImgStartX, ImgStartY, ImgBin, ImgWid, ImgHei;//所显示图åƒçš„åæ ‡å°ºå¯¸ä¿¡æ¯ // int iCamIndex; @@ -190,4 +190,4 @@ class CMyEFW : public CStateDeviceBase bool bPosWait; // long position_; }; -//2.0.0.0->20170113:Ôö¼Óanti-dewºÍEFW \ No newline at end of file +//2.0.0.0->20170113:增加anti-dewå’ŒEFW \ No newline at end of file diff --git a/DeviceAdapters/ZWO/SequenceThread.cpp b/DeviceAdapters/ZWO/SequenceThread.cpp index 37378f202..ecda11ab2 100644 --- a/DeviceAdapters/ZWO/SequenceThread.cpp +++ b/DeviceAdapters/ZWO/SequenceThread.cpp @@ -64,7 +64,7 @@ void SequenceThread::Start(long numImages, double intervalMs) imageCounter_=0; stop_ = false; OutputDbgPrint("bf act\n"); - activate();//¿ªÊ¼Ïß³Ì + activate();//开始线程 OutputDbgPrint("af act\n"); } diff --git a/DeviceAdapters/Zaber/ConnectionManager.cpp b/DeviceAdapters/Zaber/ConnectionManager.cpp index b99add048..df9824fc6 100644 --- a/DeviceAdapters/Zaber/ConnectionManager.cpp +++ b/DeviceAdapters/Zaber/ConnectionManager.cpp @@ -1,7 +1,10 @@ #include "ConnectionManager.h" +#include +#include + 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()) { @@ -9,9 +12,32 @@ std::shared_ptr ConnectionManager::getConnection(std::string po } } - auto connection = std::make_shared(zml::Connection::openSerialPort(port)); + std::shared_ptr connection; + if (port.find("share://") == 0) { + // share://:/ + std::regex parser("^share:\\/\\/([^:\\/]+)(:\\d+)?(\\/.*)?$", std::regex_constants::ECMAScript); + std::smatch partMatch; + if (!std::regex_match(port, partMatch, parser)) { + throw zmlbase::InvalidArgumentException("Invalid network share connection string: " + port); + } + + std::string host = partMatch[1].str(); + int sharePort = 11421; + std::string connectionName; + if (partMatch[2].matched) { + sharePort = std::stoi(partMatch[2].str().substr(1)); + } + if (partMatch[3].matched) { + connectionName = partMatch[3].str().substr(1); + } + + connection = std::make_shared(zml::Connection::openNetworkShare(host, sharePort, connectionName)); + } else { + connection = std::make_shared(zml::Connection::openSerialPort(port)); + } + auto id = connection->getInterfaceId(); - connection->getDisconnected().subscribe([=](std::shared_ptr) { + connection->getDisconnected().subscribe([=, this](std::shared_ptr) { removeConnection(port, id); }); connections_[port] = connection; @@ -22,18 +48,19 @@ std::shared_ptr ConnectionManager::getConnection(std::string po bool ConnectionManager::removeConnection(std::string port, int interfaceId) { std::lock_guard lockGuard(lock_); - if (connections_.count(port) == 0) { + auto it = connections_.find(port); + if (it == connections_.end()) { return false; - } + } if (interfaceId != -1) { - if (auto connection = connections_.at(port).lock()) { + if (auto connection = it->second.lock()) { if (connection->getInterfaceId() != interfaceId) { return false; } } } - connections_.erase(port); + connections_.erase(it); return true; -} \ No newline at end of file +} diff --git a/DeviceAdapters/Zaber/FilterCubeTurret.cpp b/DeviceAdapters/Zaber/FilterCubeTurret.cpp index 3044c6fc8..4fb837c8f 100644 --- a/DeviceAdapters/Zaber/FilterCubeTurret.cpp +++ b/DeviceAdapters/Zaber/FilterCubeTurret.cpp @@ -222,9 +222,7 @@ int FilterCubeTurret::PortGetSet(MM::PropertyBase* pProp, MM::ActionType eAct) { if (initialized_) { - // revert - pProp->Set(port_.c_str()); - return ERR_PORT_CHANGE_FORBIDDEN; + resetConnection(); } pProp->Get(port_); diff --git a/DeviceAdapters/Zaber/FilterCubeTurret.h b/DeviceAdapters/Zaber/FilterCubeTurret.h index 07988a00e..8cdaf53fb 100644 --- a/DeviceAdapters/Zaber/FilterCubeTurret.h +++ b/DeviceAdapters/Zaber/FilterCubeTurret.h @@ -38,21 +38,21 @@ class FilterCubeTurret : public CStateDeviceBase, public Zaber // Device API // ---------- - int Initialize(); - int Shutdown(); - void GetName(char* name) const; - bool Busy(); + int Initialize() override; + int Shutdown() override; + void GetName(char* name) const override; + bool Busy() override; // Stage API // --------- - unsigned long GetNumberOfPositions() const + unsigned long GetNumberOfPositions() const override { return numPositions_; } // Base class overrides // ---------------- - virtual int GetPositionLabel(long pos, char* label) const; + int GetPositionLabel(long pos, char* label) const override; // Properties // ---------------- diff --git a/DeviceAdapters/Zaber/FilterWheel.cpp b/DeviceAdapters/Zaber/FilterWheel.cpp index 2466cdf0a..4fbcccfbb 100644 --- a/DeviceAdapters/Zaber/FilterWheel.cpp +++ b/DeviceAdapters/Zaber/FilterWheel.cpp @@ -217,9 +217,7 @@ int FilterWheel::PortGetSet(MM::PropertyBase* pProp, MM::ActionType eAct) { if (initialized_) { - // revert - pProp->Set(port_.c_str()); - return ERR_PORT_CHANGE_FORBIDDEN; + resetConnection(); } pProp->Get(port_); diff --git a/DeviceAdapters/Zaber/FilterWheel.h b/DeviceAdapters/Zaber/FilterWheel.h index 31e2cd2bc..3f2587ec5 100644 --- a/DeviceAdapters/Zaber/FilterWheel.h +++ b/DeviceAdapters/Zaber/FilterWheel.h @@ -37,21 +37,21 @@ class FilterWheel : public CStateDeviceBase, public ZaberBase // Device API // ---------- - int Initialize(); - int Shutdown(); - void GetName(char* name) const; - bool Busy(); + int Initialize() override; + int Shutdown() override; + void GetName(char* name) const override; + bool Busy() override; // Stage API // --------- - unsigned long GetNumberOfPositions() const + unsigned long GetNumberOfPositions() const override { return numPositions_; } // Base class overrides // ---------------- - virtual int GetPositionLabel(long pos, char* label) const; + int GetPositionLabel(long pos, char* label) const override; // Properties // ---------------- diff --git a/DeviceAdapters/Zaber/Illuminator.cpp b/DeviceAdapters/Zaber/Illuminator.cpp index cbf4fbce2..1a340072a 100644 --- a/DeviceAdapters/Zaber/Illuminator.cpp +++ b/DeviceAdapters/Zaber/Illuminator.cpp @@ -373,9 +373,7 @@ int Illuminator::PortGetSet(MM::PropertyBase* pProp, MM::ActionType eAct) { if (initialized_) { - // revert - pProp->Set(port_.c_str()); - return ERR_PORT_CHANGE_FORBIDDEN; + resetConnection(); } pProp->Get(port_); diff --git a/DeviceAdapters/Zaber/Illuminator.h b/DeviceAdapters/Zaber/Illuminator.h index a95d198c2..5ff54e64e 100644 --- a/DeviceAdapters/Zaber/Illuminator.h +++ b/DeviceAdapters/Zaber/Illuminator.h @@ -37,15 +37,15 @@ class Illuminator : public CShutterBase, public ZaberBase // Device API // ---------- - int Initialize(); - int Shutdown(); + int Initialize() override; + int Shutdown() override; - void GetName(char* pszName) const; - bool Busy(); + void GetName(char* pszName) const override; + bool Busy() override; - int SetOpen(bool open = true); - int GetOpen(bool& open); - int Fire(double deltaT); + int SetOpen(bool open = true) override; + int GetOpen(bool& open) override; + int Fire(double deltaT) override; // Properties // ---------------- @@ -66,7 +66,7 @@ class Illuminator : public CShutterBase, public ZaberBase bool *lampExists_; // Enables the optimization of using the device-scope lamp on command. - bool canUseDeviceLampOnCommand_; + bool canUseDeviceLampOnCommand_; // Enables the optimization of only turning on individual axes that // have nonzero flux when the shutter opens. @@ -76,9 +76,9 @@ class Illuminator : public CShutterBase, public ZaberBase double* maxFlux_; // These variables exist only to enable good UX in the absence of - // a device-scope lamp on command. The specific behavior these are + // a device-scope lamp on command. The specific behavior these are // for is when you are tuning presets, you leave the shutter open - // and adjust intensities. If you change a zero intensity to + // and adjust intensities. If you change a zero intensity to // nonzero, it must turn that axis on, only if the shutter is open. bool *lampIsOn_; bool isOpen_; diff --git a/DeviceAdapters/Zaber/Makefile.am b/DeviceAdapters/Zaber/Makefile.am index 4941c2be6..7a5d659ee 100644 --- a/DeviceAdapters/Zaber/Makefile.am +++ b/DeviceAdapters/Zaber/Makefile.am @@ -2,6 +2,8 @@ AM_CXXFLAGS = $(MMDEVAPI_CXXFLAGS) $(ZML_CPPFLAGS) deviceadapter_LTLIBRARIES = libmmgr_dal_Zaber.la libmmgr_dal_Zaber_la_SOURCES = \ + WdiAutofocus.cpp \ + WdiAutofocus.h \ ObjectiveChanger.cpp \ ObjectiveChanger.h \ Illuminator.cpp \ diff --git a/DeviceAdapters/Zaber/ObjectiveChanger.cpp b/DeviceAdapters/Zaber/ObjectiveChanger.cpp index 92e13cb82..ea5a59438 100644 --- a/DeviceAdapters/Zaber/ObjectiveChanger.cpp +++ b/DeviceAdapters/Zaber/ObjectiveChanger.cpp @@ -93,7 +93,7 @@ int ObjectiveChanger::Initialize() this->LogMessage("ObjectiveChanger::Initialize\n", true); - auto ret = handleException([=]() { + auto ret = handleException([&]() { ensureConnected(); if (!this->changer_.getFocusAxis().isHomed()) { this->changer_.change(1); @@ -232,9 +232,7 @@ int ObjectiveChanger::PortGetSet(MM::PropertyBase* pProp, MM::ActionType eAct) { if (initialized_) { - // revert - pProp->Set(port_.c_str()); - return ERR_PORT_CHANGE_FORBIDDEN; + resetConnection(); } pProp->Get(port_); @@ -350,7 +348,7 @@ int ObjectiveChanger::FocusOffsetGetSet(MM::PropertyBase* pProp, MM::ActionType } int ObjectiveChanger::setObjective(long objective, bool applyOffset) { - return handleException([=]() { + return handleException([&]() { ensureConnected(); zmlbase::Measurement offset; if (applyOffset) { diff --git a/DeviceAdapters/Zaber/ObjectiveChanger.h b/DeviceAdapters/Zaber/ObjectiveChanger.h index 05c8b15f8..cce17d40e 100644 --- a/DeviceAdapters/Zaber/ObjectiveChanger.h +++ b/DeviceAdapters/Zaber/ObjectiveChanger.h @@ -38,25 +38,25 @@ class ObjectiveChanger : public CStateDeviceBase, public Zaber // Device API // ---------- - int Initialize(); - int Shutdown(); - void GetName(char* name) const; - bool Busy(); + int Initialize() override; + int Shutdown() override; + void GetName(char* name) const override; + bool Busy() override; // Stage API // --------- - unsigned long GetNumberOfPositions() const + unsigned long GetNumberOfPositions() const override { return numPositions_; } // Base class overrides // ---------------- - virtual int GetPositionLabel(long pos, char* label) const; + int GetPositionLabel(long pos, char* label) const override; // ZaverBase class overrides // ---------------- - virtual void onNewConnection(); + void onNewConnection() override; // Properties // ---------------- @@ -66,9 +66,9 @@ class ObjectiveChanger : public CStateDeviceBase, public Zaber 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: + int setObjective(long objective, bool applyOffset); + long xMorAddress_; long xLdaAddress_; long numPositions_; diff --git a/DeviceAdapters/Zaber/Stage.cpp b/DeviceAdapters/Zaber/Stage.cpp index 8dfe20b97..158b9c2c3 100644 --- a/DeviceAdapters/Zaber/Stage.cpp +++ b/DeviceAdapters/Zaber/Stage.cpp @@ -294,9 +294,7 @@ int Stage::OnPort (MM::PropertyBase* pProp, MM::ActionType eAct) { if (initialized_) { - // revert - pProp->Set(port_.c_str()); - return ERR_PORT_CHANGE_FORBIDDEN; + resetConnection(); } pProp->Get(port_); diff --git a/DeviceAdapters/Zaber/Stage.h b/DeviceAdapters/Zaber/Stage.h index ca9b7f06c..6f9be2722 100644 --- a/DeviceAdapters/Zaber/Stage.h +++ b/DeviceAdapters/Zaber/Stage.h @@ -36,28 +36,28 @@ class Stage : public CStageBase, public ZaberBase // Device API // ---------- - int Initialize(); - int Shutdown(); - void GetName(char* name) const; - bool Busy(); + int Initialize() override; + int Shutdown() override; + void GetName(char* name) const override; + bool Busy() override; // Stage API // --------- - int GetPositionUm(double& pos); - int GetPositionSteps(long& steps); - int SetPositionUm(double pos); - int SetRelativePositionUm(double d); - int SetPositionSteps(long steps); - int SetRelativePositionSteps(long steps); - int Move(double velocity); - int Stop(); - int Home(); - int SetAdapterOriginUm(double d); - int SetOrigin(); - int GetLimits(double& lower, double& upper); + int GetPositionUm(double& pos) override; + int GetPositionSteps(long& steps) override; + int SetPositionUm(double pos) override; + int SetRelativePositionUm(double d) override; + int SetPositionSteps(long steps) override; + int SetRelativePositionSteps(long steps); // not in the base class + int Move(double velocity) override; + int Stop() override; + int Home() override; + int SetAdapterOriginUm(double d) override; + int SetOrigin() override; + int GetLimits(double& lower, double& upper) override; - int IsStageSequenceable(bool& isSequenceable) const {isSequenceable = false; return DEVICE_OK;} - bool IsContinuousFocusDrive() const {return false;} + int IsStageSequenceable(bool& isSequenceable) const override { isSequenceable = false; return DEVICE_OK; } + bool IsContinuousFocusDrive() const override { return false; } // action interface // ---------------- diff --git a/DeviceAdapters/Zaber/WdiAutofocus.cpp b/DeviceAdapters/Zaber/WdiAutofocus.cpp new file mode 100644 index 000000000..50fde9275 --- /dev/null +++ b/DeviceAdapters/Zaber/WdiAutofocus.cpp @@ -0,0 +1,320 @@ +/////////////////////////////////////////////////////////////////////////////// +// FILE: WdiAutofocus.cpp +// PROJECT: Micro-Manager +// SUBSYSTEM: DeviceAdapters +//----------------------------------------------------------------------------- +// DESCRIPTION: Device adapter for WDI autofocus. +// +// AUTHOR: Martin Zak (contact@zaber.com) + +// COPYRIGHT: Zaber Technologies, 2024 + +// 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 "WdiAutofocus.h" + +const char* g_WdiAutofocusName = "WdiAutofocus"; +const char* g_WdiAutofocusDescription = "Zaber WDI Autofocus device adapter"; +const char* g_Msg_AF_MOVEMENT_FAILED = "The movement has failed. Ensure that the autofocus is in range and the focus axis is within the limits."; + +using namespace std; + +WdiAutofocus::WdiAutofocus() : + ZaberBase(this), + focusAddress_(1), + focusAxis_(1), + limitMin_(0.0), + limitMax_(0.0) +{ + this->LogMessage("WdiAutofocus::WdiAutofocus\n", true); + + InitializeDefaultErrorMessages(); + ZaberBase::setErrorMessages([&](auto code, auto message) { this->SetErrorText(code, message); }); + this->SetErrorText(ERR_MOVEMENT_FAILED, g_Msg_AF_MOVEMENT_FAILED); + + CreateProperty(MM::g_Keyword_Name, g_WdiAutofocusName, MM::String, true); + CreateProperty(MM::g_Keyword_Description, g_WdiAutofocusDescription, MM::String, true); + + CPropertyAction* pAct = new CPropertyAction(this, &WdiAutofocus::PortGetSet); + CreateProperty("Zaber Serial Port", port_.c_str(), MM::String, false, pAct, true); + + pAct = new CPropertyAction(this, &WdiAutofocus::FocusAddressGetSet); + CreateIntegerProperty("Focus Stage Device Number", focusAddress_, false, pAct, true); + SetPropertyLimits("Focus Stage Device Number", 1, 99); + + pAct = new CPropertyAction(this, &WdiAutofocus::FocusAxisGetSet); + CreateIntegerProperty("Focus Stage Axis Number", focusAxis_, false, pAct, true); + SetPropertyLimits("Focus Stage Axis Number", 1, 99); +} + +WdiAutofocus::~WdiAutofocus() +{ + this->LogMessage("WdiAutofocus::~WdiAutofocus\n", true); + Shutdown(); +} + +void WdiAutofocus::GetName(char* name) const +{ + CDeviceUtils::CopyLimitedString(name, g_WdiAutofocusDescription); +} + +int WdiAutofocus::Initialize() +{ + if (initialized_) + { + return DEVICE_OK; + } + + core_ = GetCoreCallback(); + + this->LogMessage("WdiAutofocus::Initialize\n", true); + + auto pAct = new CPropertyAction(this, &WdiAutofocus::LimitMinGetSet); + CreateFloatProperty("Limit Min [mm]", limitMin_, false, pAct); + pAct = new CPropertyAction(this, &WdiAutofocus::LimitMaxGetSet); + CreateFloatProperty("Limit Max [mm]", limitMax_, false, pAct); + + auto ret = UpdateStatus(); + if (ret == DEVICE_OK) + { + initialized_ = true; + } + + return ret; +} + +int WdiAutofocus::Shutdown() +{ + this->LogMessage("WdiAutofocus::Shutdown\n", true); + + if (initialized_) + { + initialized_ = false; + } + + return DEVICE_OK; +} + +bool WdiAutofocus::Busy() +{ + this->LogMessage("WdiAutofocus::Busy\n", true); + + bool busy = false; + auto ret = handleException([&]() { + ensureConnected(); + busy = axis_.isBusy(); + }); + return ret == DEVICE_OK && busy; +} + +int WdiAutofocus::PortGetSet(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + ostringstream os; + os << "WdiAutofocus::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_) + { + resetConnection(); + } + + pProp->Get(port_); + } + + return DEVICE_OK; +} + +int WdiAutofocus::FocusAddressGetSet(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + this->LogMessage("WdiAutofocus::FocusAddressGetSet\n", true); + + if (eAct == MM::AfterSet) + { + if (initialized_) + { + resetConnection(); + } + + pProp->Get(focusAddress_); + } + else if (eAct == MM::BeforeGet) + { + pProp->Set(focusAddress_); + } + + return DEVICE_OK; +} + +int WdiAutofocus::FocusAxisGetSet(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + this->LogMessage("WdiAutofocus::FocusAxisGetSet\n", true); + + if (eAct == MM::AfterSet) + { + if (initialized_) + { + resetConnection(); + } + + pProp->Get(focusAxis_); + } + else if (eAct == MM::BeforeGet) + { + pProp->Set(focusAxis_); + } + + return DEVICE_OK; +} + +int WdiAutofocus::LimitMinGetSet(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + this->LogMessage("ObjectiveChanger::LimitMinGetSet\n", true); + return LimitGetSet(pProp, eAct, limitMin_, "motion.tracking.limit.min"); +} + +int WdiAutofocus::LimitMaxGetSet(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + this->LogMessage("ObjectiveChanger::LimitMaxGetSet\n", true); + return LimitGetSet(pProp, eAct, limitMax_, "motion.tracking.limit.max"); +} + +int WdiAutofocus::LimitGetSet(MM::PropertyBase* pProp, MM::ActionType eAct, double& limit, const char* setting) +{ + if (eAct == MM::AfterSet) + { + double newLimit; + pProp->Get(newLimit); + bool update = limit != newLimit; + limit = newLimit; + + if (update) { + return handleException([&]() { + ensureConnected(); + axis_.getSettings().set(setting, limit * WdiAutofocus_::xLdaNativePerMm); + }); + } + } + else if (eAct == MM::BeforeGet) + { + if (initialized_) + { + int ret = handleException([&]() { + ensureConnected(); + limit = axis_.getSettings().get(setting) / WdiAutofocus_::xLdaNativePerMm; + }); + if (ret != DEVICE_OK) + { + return ret; + } + } + + pProp->Set(limit); + } + + return DEVICE_OK; +} + +int WdiAutofocus::FullFocus() { + this->LogMessage("WdiAutofocus::FullFocus\n", true); + + return handleException([&]() { + ensureConnected(); + + axis_.genericCommand("move track once"); + axis_.waitUntilIdle(); + }); +} + +int WdiAutofocus::IncrementalFocus() { + this->LogMessage("WdiAutofocus::IncrementalFocus\n", true); + + return FullFocus(); +} + +int WdiAutofocus::GetLastFocusScore(double& score) { + this->LogMessage("WdiAutofocus::GetLastFocusScore\n", true); + + return GetCurrentFocusScore(score); +} + +int WdiAutofocus::GetCurrentFocusScore(double& score) { + this->LogMessage("WdiAutofocus::GetCurrentFocusScore\n", true); + + score = 0.0; + return handleException([&]() { + ensureConnected(); + + auto reply = axis_.getDevice().genericCommand("io get ai 1"); + score = stod(reply.getData()); + }); +} + +int WdiAutofocus::GetOffset(double& offset) { + this->LogMessage("WdiAutofocus::GetOffset\n", true); + offset = 0.0; + return DEVICE_OK; +} +int WdiAutofocus::SetOffset(double offset) { + this->LogMessage("WdiAutofocus::SetOffset\n", true); + if (offset != 0.0) { + return DEVICE_UNSUPPORTED_COMMAND; + } + return DEVICE_OK; +} + +int WdiAutofocus::SetContinuousFocusing(bool state) { + this->LogMessage("WdiAutofocus::SetContinuousFocusing\n", true); + + return handleException([&]() { + ensureConnected(); + + if (state) { + axis_.genericCommand("move track"); + } else { + axis_.stop(); + } + }); +} + +int WdiAutofocus::GetContinuousFocusing(bool& state) { + this->LogMessage("WdiAutofocus::GetContinuousFocusing\n", true); + return handleException([&]() { + ensureConnected(); + state = axis_.isBusy(); + }); +} + +bool WdiAutofocus::IsContinuousFocusLocked() { + this->LogMessage("WdiAutofocus::IsContinuousFocusLocked\n", true); + + bool locked; + auto ret = handleException([&]() { + ensureConnected(); + locked = axis_.getSettings().get("motion.tracking.settle.tolerance.met") > 0; + }); + return ret == DEVICE_OK && locked; +} + +void WdiAutofocus::onNewConnection() { + ZaberBase::onNewConnection(); + axis_ = connection_->getDevice(focusAddress_).getAxis(focusAxis_); +} diff --git a/DeviceAdapters/Zaber/WdiAutofocus.h b/DeviceAdapters/Zaber/WdiAutofocus.h new file mode 100644 index 000000000..3e32ac9ca --- /dev/null +++ b/DeviceAdapters/Zaber/WdiAutofocus.h @@ -0,0 +1,78 @@ +#pragma once +/////////////////////////////////////////////////////////////////////////////// +// FILE: WdiAutofocus.h +// PROJECT: Micro-Manager +// SUBSYSTEM: DeviceAdapters +//----------------------------------------------------------------------------- +// DESCRIPTION: Device adapter for WDI autofocus. +// +// AUTHOR: Martin Zak (contact@zaber.com) + +// COPYRIGHT: Zaber Technologies, 2024 + +// 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_WdiAutofocusName; +extern const char* g_WdiAutofocusDescription; + +namespace WdiAutofocus_ { + const double xLdaNativePerMm = 1000000.0; +} + +class WdiAutofocus : public CAutoFocusBase, public ZaberBase +{ +public: + WdiAutofocus(); + ~WdiAutofocus(); + + // Device API + // ---------- + int Initialize() override; + int Shutdown() override; + void GetName(char* name) const override; + bool Busy() override; + + // AutoFocus API + // ---------- + bool IsContinuousFocusLocked() override; + int FullFocus() override; + int IncrementalFocus() override; + int GetLastFocusScore(double& score) override; + int GetCurrentFocusScore(double& score) override; + int GetOffset(double& offset) override; + int SetOffset(double offset) override; + int SetContinuousFocusing(bool state) override; + int GetContinuousFocusing(bool& state) override; + + // ZaberBase class overrides + // ---------------- + void onNewConnection() override; + + // Properties + // ---------------- + int PortGetSet(MM::PropertyBase* pProp, MM::ActionType eAct); + int FocusAddressGetSet(MM::PropertyBase* pProp, MM::ActionType eAct); + int FocusAxisGetSet(MM::PropertyBase* pProp, MM::ActionType eAct); + int LimitMinGetSet(MM::PropertyBase* pProp, MM::ActionType eAct); + int LimitMaxGetSet(MM::PropertyBase* pProp, MM::ActionType eAct); + int LimitGetSet(MM::PropertyBase* pProp, MM::ActionType eAct, double& limit, const char* setting); + +private: + long focusAddress_; + long focusAxis_; + zml::Axis axis_; + + double limitMin_; + double limitMax_; +}; diff --git a/DeviceAdapters/Zaber/XYStage.cpp b/DeviceAdapters/Zaber/XYStage.cpp index b16a2ffe9..117e7f211 100644 --- a/DeviceAdapters/Zaber/XYStage.cpp +++ b/DeviceAdapters/Zaber/XYStage.cpp @@ -607,9 +607,7 @@ int XYStage::OnPort(MM::PropertyBase* pProp, MM::ActionType eAct) { if (initialized_) { - // revert - pProp->Set(port_.c_str()); - return ERR_PORT_CHANGE_FORBIDDEN; + resetConnection(); } pProp->Get(port_); } diff --git a/DeviceAdapters/Zaber/XYStage.h b/DeviceAdapters/Zaber/XYStage.h index b4048b958..6c6000833 100644 --- a/DeviceAdapters/Zaber/XYStage.h +++ b/DeviceAdapters/Zaber/XYStage.h @@ -4,9 +4,9 @@ // SUBSYSTEM: DeviceAdapters //----------------------------------------------------------------------------- // DESCRIPTION: XYStage Device Adapter -// +// // AUTHOR: David Goosen & Athabasca Witschi (contact@zaber.com) -// +// // COPYRIGHT: Zaber Technologies Inc., 2017 // // LICENSE: This file is distributed under the BSD license. @@ -36,27 +36,26 @@ class XYStage : public CXYStageBase, public ZaberBase // Device API // ---------- - int Initialize(); - int Shutdown(); - void GetName(char* name) const; - bool Busy(); + int Initialize() override; + int Shutdown() override; + void GetName(char* name) const override; + bool Busy() override; // XYStage API // ----------- - int GetLimitsUm(double& xMin, double& xMax, double& yMin, double& yMax); - int Move(double vx, double vy); - int SetPositionSteps(long x, long y); - int GetPositionSteps(long& x, long& y); - int SetRelativePositionSteps(long x, long y); - int Home(); - int Stop(); - int SetOrigin(); - int SetAdapterOrigin(); - int GetStepLimits(long& xMin, long& xMax, long& yMin, long& yMax); - double GetStepSizeXUm() {return stepSizeXUm_;} - double GetStepSizeYUm() {return stepSizeYUm_;} + int GetLimitsUm(double& xMin, double& xMax, double& yMin, double& yMax) override; + int Move(double vx, double vy) override; + int SetPositionSteps(long x, long y) override; + int GetPositionSteps(long& x, long& y) override; + int SetRelativePositionSteps(long x, long y) override; + int Home() override; + int Stop() override; + int SetOrigin() override; + int GetStepLimits(long& xMin, long& xMax, long& yMin, long& yMax) override; + double GetStepSizeXUm() override { return stepSizeXUm_; } + double GetStepSizeYUm() override { return stepSizeYUm_; } - int IsXYStageSequenceable(bool& isSequenceable) const {isSequenceable = false; return DEVICE_OK;} + int IsXYStageSequenceable(bool& isSequenceable) const override { isSequenceable = false; return DEVICE_OK; } // action interface // ---------------- diff --git a/DeviceAdapters/Zaber/Zaber.cpp b/DeviceAdapters/Zaber/Zaber.cpp index b242ffb1e..4cf5183ce 100644 --- a/DeviceAdapters/Zaber/Zaber.cpp +++ b/DeviceAdapters/Zaber/Zaber.cpp @@ -32,6 +32,7 @@ #include "FilterCubeTurret.h" #include "Illuminator.h" #include "ObjectiveChanger.h" +#include "WdiAutofocus.h" #include @@ -48,6 +49,7 @@ const char* g_Msg_LAMP_OVERHEATED = "Some of the illuminator lamps are overheate 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."; +const char* g_Msg_ERR_INVALID_OPERATION = "Operation cannot be performed at this time."; ////////////////////////////////////////////////////////////////////////////////// @@ -61,6 +63,7 @@ MODULE_API void InitializeModuleData() RegisterDevice(g_FilterTurretName, MM::StateDevice, g_FilterTurretDescription); RegisterDevice(g_IlluminatorName, MM::ShutterDevice, g_IlluminatorDescription); RegisterDevice(g_ObjectiveChangerName, MM::StateDevice, g_ObjectiveChangerDescription); + RegisterDevice(g_WdiAutofocusName, MM::AutoFocusDevice, g_WdiAutofocusDescription); } @@ -90,6 +93,10 @@ MODULE_API MM::Device* CreateDevice(const char* deviceName) { return new ObjectiveChanger(); } + else if (strcmp(deviceName, g_WdiAutofocusName) == 0) + { + return new WdiAutofocus(); + } else { return 0; @@ -153,7 +160,7 @@ void ZaberBase::resetConnection() { // the connection destructor can throw in the rarest occasions connection_ = nullptr; } - catch (const zmlbase::MotionLibException e) + catch (const zmlbase::MotionLibException e) { } } @@ -228,13 +235,13 @@ int ZaberBase::SetSetting(long device, long axis, string setting, double data, i } -bool ZaberBase::IsBusy(long device) +bool ZaberBase::IsBusy(long device, long axis) { core_->LogMessage(device_, "ZaberBase::IsBusy\n", true); zml::Response resp; - int ret = Command(device, 0, "", resp); + int ret = Command(device, axis, "", resp); if (ret != DEVICE_OK) { ostringstream os; @@ -453,6 +460,10 @@ int ZaberBase::handleException(std::function wrapped) { core_->LogMessage(device_, e.what(), true); return ERR_MOVEMENT_FAILED; } + catch (const zmlbase::InvalidOperationException e) { + core_->LogMessage(device_, e.what(), true); + return ERR_INVALID_OPERATION; + } catch (const zmlbase::MotionLibException e) { core_->LogMessage(device_, e.what(), true); return DEVICE_ERR; @@ -472,4 +483,5 @@ void ZaberBase::setErrorMessages(std::function setter) { setter(ERR_LAMP_DISCONNECTED, g_Msg_LAMP_DISCONNECTED); setter(ERR_LAMP_OVERHEATED, g_Msg_LAMP_OVERHEATED); setter(ERR_FIRMWARE_UNSUPPORTED, g_Msg_FIRMWARE_UNSUPPORTED); + setter(ERR_INVALID_OPERATION, g_Msg_ERR_INVALID_OPERATION); } diff --git a/DeviceAdapters/Zaber/Zaber.h b/DeviceAdapters/Zaber/Zaber.h index 1fba41aa8..d1c5e59d4 100644 --- a/DeviceAdapters/Zaber/Zaber.h +++ b/DeviceAdapters/Zaber/Zaber.h @@ -44,6 +44,7 @@ namespace zmlmi = zaber::motion::microscopy; #define ERR_PORT_CHANGE_FORBIDDEN 10002 #define ERR_DRIVER_DISABLED 10004 +#define ERR_INVALID_OPERATION 10008 #define ERR_MOVEMENT_FAILED 10016 #define ERR_COMMAND_REJECTED 10032 #define ERR_NO_REFERENCE_POS 10064 @@ -70,7 +71,7 @@ class ZaberBase 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); + bool IsBusy(long device, long axis = 0); 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); diff --git a/DeviceAdapters/Zaber/Zaber.vcxproj b/DeviceAdapters/Zaber/Zaber.vcxproj index a25dd995a..b485aec5c 100644 --- a/DeviceAdapters/Zaber/Zaber.vcxproj +++ b/DeviceAdapters/Zaber/Zaber.vcxproj @@ -59,13 +59,13 @@ true true WIN32;_USRDLL;_WINDOWS;%(PreprocessorDefinitions) - $(MM_3RDPARTYPUBLIC)\Zaber\zaber-motion\include;%(AdditionalIncludeDirectories) + $(MM_3RDPARTYPUBLIC)\Zaber\zaber-motion-3.4.4\include;%(AdditionalIncludeDirectories) true true zml.lib;%(AdditionalDependencies) - $(MM_3RDPARTYPUBLIC)\Zaber\zaber-motion\win64\lib;%(AdditionalLibraryDirectories) + $(MM_3RDPARTYPUBLIC)\Zaber\zaber-motion-3.4.4\win64\lib;%(AdditionalLibraryDirectories) @@ -75,6 +75,7 @@ + @@ -85,6 +86,7 @@ + diff --git a/DeviceAdapters/Zaber/Zaber.vcxproj.filters b/DeviceAdapters/Zaber/Zaber.vcxproj.filters index 192f91275..884105b05 100644 --- a/DeviceAdapters/Zaber/Zaber.vcxproj.filters +++ b/DeviceAdapters/Zaber/Zaber.vcxproj.filters @@ -39,6 +39,9 @@ Source Files + + Source Files + @@ -65,5 +68,8 @@ Header Files + + Header Files + \ No newline at end of file diff --git a/DeviceAdapters/ZeissCAN/ZeissCAN.cpp b/DeviceAdapters/ZeissCAN/ZeissCAN.cpp index d37419365..c323e7d19 100644 --- a/DeviceAdapters/ZeissCAN/ZeissCAN.cpp +++ b/DeviceAdapters/ZeissCAN/ZeissCAN.cpp @@ -613,7 +613,7 @@ MM::DeviceDetectionStatus ZeissScope::DetectDevice(void) { *its = (char)tolower(*its); } - // ensure we’ve been provided with a valid serial port device name + // ensure we’ve been provided with a valid serial port device name if( 0< transformed.length() && 0 != transformed.compare("undefined") && 0 != transformed.compare("unknown") ) { // the port property seems correct, so give it a try diff --git a/DeviceAdapters/configure.ac b/DeviceAdapters/configure.ac index 1759beff5..af0e239db 100644 --- a/DeviceAdapters/configure.ac +++ b/DeviceAdapters/configure.ac @@ -306,6 +306,15 @@ else AC_MSG_RESULT([not found]) fi +# Aravis (v0.10). +AC_MSG_CHECKING(for Aravis_Linux) +AM_CONDITIONAL([BUILD_ARAVIS_LINUX],[test -f "/usr/local/include/aravis-0.10/arv.h"]) +if test -f "/usr/local/include/aravis-0.10/arv.h"; then + AC_MSG_RESULT([found]) +else + AC_MSG_RESULT([not found]) +fi + # BaslerPylon AC_MSG_CHECKING(for Basler_Linux) AM_CONDITIONAL([BUILD_BASLER_LINUX],[test -f "/opt/pylon/include/pylon/PylonIncludes.h"]) @@ -422,24 +431,26 @@ AS_IF([test "x$want_vimba_x" != xno], AM_CONDITIONAL([BUILD_ALLIED_VISION_CAMERA], [test "x$use_vimba_x" = xyes]) # Zaber Motion Library (hack: only support 3rdpartypublic copy currently) +zml_version="3.4.4" +zml_so_version="3.4" AC_MSG_CHECKING([for Zaber Motion Library in 3rdpartypublic]) -zml_header_to_check="${thirdpartypublic}/Zaber/zaber-motion/include/zaber/motion/library.h" +zml_header_to_check="${thirdpartypublic}/Zaber/zaber-motion-${zml_version}/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_CPPFLAGS="-I${thirdpartypublic}/Zaber/zaber-motion-${zml_version}/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_linux_libdir="${thirdpartypublic}/Zaber/zaber-motion-${zml_version}/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" + ZML_LIBS_TO_COPY="${zml_linux_libdir}/libzml.so.${zml_so_version} ${zml_linux_libdir}/libzaber-motion-lib-linux-amd64.so.${zml_version}" ;; *apple-darwin*) # macOS build for packaging does not ship ZML - ZML_LDFLAGS="-L${thirdpartypublic}/Zaber/zaber-motion/darwin/lib" + ZML_LDFLAGS="-L${thirdpartypublic}/Zaber/zaber-motion-${zml_version}/darwin/lib" ZML_LIBS_TO_COPY="" ;; esac @@ -538,6 +549,7 @@ m4_define([device_adapter_dirs], [m4_strip([ AndorLaserCombiner AndorSDK3 Aquinas + Aravis Arduino Arduino32bitBoards Basler diff --git a/MMCore/CircularBuffer.cpp b/MMCore/CircularBuffer.cpp index 933142e9b..e23ea89b3 100644 --- a/MMCore/CircularBuffer.cpp +++ b/MMCore/CircularBuffer.cpp @@ -256,7 +256,7 @@ bool CircularBuffer::InsertMultiChannel(const unsigned char* pixArray, unsigned md = *pMd; } - std::string cameraName = md.GetSingleTag("Camera").GetValue(); + std::string cameraName = md.GetSingleTag(MM::g_Keyword_Metadata_CameraLabel).GetValue(); if (imageNumbers_.end() == imageNumbers_.find(cameraName)) { imageNumbers_[cameraName] = 0; diff --git a/MMCore/CoreCallback.cpp b/MMCore/CoreCallback.cpp index 7cd1b0e5c..572f9a0ce 100644 --- a/MMCore/CoreCallback.cpp +++ b/MMCore/CoreCallback.cpp @@ -223,7 +223,7 @@ CoreCallback::AddCameraMetadata(const MM::Device* caller, const Metadata* pMd) core_->deviceManager_->GetDevice(caller)); std::string label = camera->GetLabel(); - newMD.put("Camera", label); + newMD.put(MM::g_Keyword_Metadata_CameraLabel, label); std::string serializedMD; try diff --git a/MMDevice/DeviceBase.h b/MMDevice/DeviceBase.h index fc3d80c68..84cef7177 100644 --- a/MMDevice/DeviceBase.h +++ b/MMDevice/DeviceBase.h @@ -1542,7 +1542,7 @@ class CCameraBase : public CDeviceBase char label[MM::MaxStrLength]; this->GetLabel(label); Metadata md; - md.put("Camera", label); + md.put(MM::g_Keyword_Metadata_CameraLabel, label); int ret = GetCoreCallback()->InsertImage(this, GetImageBuffer(), GetImageWidth(), GetImageHeight(), GetImageBytesPerPixel(), md.Serialize().c_str()); @@ -1562,7 +1562,7 @@ class CCameraBase : public CDeviceBase virtual long GetNumberOfImages() {return thd_->GetNumberOfImages();} // called from the thread function before exit - virtual void OnThreadExiting() throw() + virtual void OnThreadExiting() { try { @@ -1668,7 +1668,7 @@ class CCameraBase : public CDeviceBase void UpdateActualDuration() {actualDuration_ = camera_->GetCurrentMMTime() - startTime_;} private: - virtual int svc(void) throw() + virtual int svc() { int ret=DEVICE_ERR; try diff --git a/MMDevice/ImageMetadata.h b/MMDevice/ImageMetadata.h index 98f5c8a64..212d7b084 100644 --- a/MMDevice/ImageMetadata.h +++ b/MMDevice/ImageMetadata.h @@ -22,17 +22,6 @@ #pragma once -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable: 4290) // 'C++ exception specification ignored' -#endif - -#if defined(__GNUC__) && !defined(__clang__) -#pragma GCC diagnostic push -// 'dynamic exception specifications are deprecated in C++11 [-Wdeprecated]' -#pragma GCC diagnostic ignored "-Wdeprecated" -#endif - #include "MMDeviceConstants.h" #include @@ -42,6 +31,12 @@ #include #include +#ifdef SWIG +#define MMDEVICE_LEGACY_THROW(ex) throw (ex) +#else +#define MMDEVICE_LEGACY_THROW(ex) +#endif + /////////////////////////////////////////////////////////////////////////////// // MetadataError // ------------- @@ -326,14 +321,14 @@ class Metadata return false; } - MetadataSingleTag GetSingleTag(const char* key) const throw (MetadataKeyError) + MetadataSingleTag GetSingleTag(const char* key) const MMDEVICE_LEGACY_THROW(MetadataKeyError) { MetadataTag* tag = FindTag(key); const MetadataSingleTag* stag = tag->ToSingleTag(); return *stag; } - MetadataArrayTag GetArrayTag(const char* key) const throw (MetadataKeyError) + MetadataArrayTag GetArrayTag(const char* key) const MMDEVICE_LEGACY_THROW(MetadataKeyError) { MetadataTag* tag = FindTag(key); const MetadataArrayTag* atag = tag->ToArrayTag(); @@ -499,11 +494,3 @@ class Metadata typedef std::map::iterator TagIter; typedef std::map::const_iterator TagConstIter; }; - -#if defined(__GNUC__) && !defined(__clang__) -#pragma GCC diagnostic pop -#endif - -#ifdef _MSC_VER -#pragma warning(pop) -#endif diff --git a/MMDevice/MMDeviceConstants.h b/MMDevice/MMDeviceConstants.h index 956b05474..62aa53ce6 100644 --- a/MMDevice/MMDeviceConstants.h +++ b/MMDevice/MMDeviceConstants.h @@ -143,6 +143,7 @@ namespace MM { // image annotations + const char* const g_Keyword_Metadata_CameraLabel = "Camera"; const char* const g_Keyword_Meatdata_Exposure = "Exposure-ms"; const char* const g_Keyword_Metadata_Score = "Score"; const char* const g_Keyword_Metadata_ImageNumber = "ImageNumber"; diff --git a/buildscripts/VisualStudio/MMCommon.props b/buildscripts/VisualStudio/MMCommon.props index 8c42b9a2a..8cdc0883d 100644 --- a/buildscripts/VisualStudio/MMCommon.props +++ b/buildscripts/VisualStudio/MMCommon.props @@ -23,10 +23,11 @@ Level4 - 4127;4290;%(DisableSpecificWarnings) + 4127;4290;4828;%(DisableSpecificWarnings) Sync _CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true + /utf-8 %(AdditionalOptions) true diff --git a/micromanager.sln b/micromanager.sln index 021928e29..c2e37be46 100644 --- a/micromanager.sln +++ b/micromanager.sln @@ -472,7 +472,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AlliedVisionCamera", "Devic 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}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ArduinoCounter", "DeviceAdapters\ArduinoCounter\ArduinoCounter.vcxproj", "{8331c4e1-6c17-481e-9b4f-232db77d95cb}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ESP32", "DeviceAdapters\ESP32\ESP32.vcxproj", "{0C72846A-BCFA-4F75-91AF-D9F5CA01E6B2}" EndProject @@ -481,996 +481,1528 @@ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ThorlabsCHROLIS", "DeviceAdapters\ThorlabsCHROLIS\ThorlabsCHROLIS.vcxproj", "{F93128A5-B344-4662-BFB8-B4CAECF1FE52}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "IDSPeak", "DeviceAdapters\IDSPeak\IDSPeak.vcxproj", "{823CF77E-8120-41B1-9B25-823A79C93198}" +EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "QSI", "DeviceAdapters\QSI\QSI.vcxproj", "{320AF64E-C4FC-4A97-A7D1-F8233A8A44C7}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NotificationTester", "DeviceAdapters\NotificationTester\NotificationTester.vcxproj", "{7C8C60FA-92E3-4102-80AB-A4468C4FCD2F}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PlayerOne", "DeviceAdapters\PlayerOne\PlayerOne.vcxproj", "{46F4FFAF-79FD-45D9-A2C5-9E16B46787C5}" +EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DahengGalaxy", "DeviceAdapters\DahengGalaxy\DahengGalaxy.vcxproj", "{DD3A2820-F54C-42F3-AA0E-DC95D57481B7}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Octopi-Research", "DeviceAdapters\Octopi-Research\Octopi-research.vcxproj", "{FE305987-2B15-4EDA-9FC8-32D81AAE1543}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PyDevice", "DeviceAdapters\PyDevice\PyDevice.vcxproj", "{36CF524A-8214-404C-8E6B-B5DEC1FDADF9}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Hikrobot", "DeviceAdapters\HikRobot\HikRobot.vcxproj", "{38DCD378-83FE-4C42-8916-1C477A35F65F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OpenFlexure", "DeviceAdapters\OpenFlexure\OpenFlexure.vcxproj", "{149B6A9D-40FB-4FDF-BC0C-6B68E3BC4938}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ScientificaMotion8", "DeviceAdapters\ScientificaMotion8\ScientificaMotion8.vcxproj", "{E4E9FA4F-E9DD-4483-9140-2FD472C28F1D}" +>>>>>>> main EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {36571628-728C-4ACD-A47F-503BA91C5D43}.Debug|x64.ActiveCfg = Debug|x64 {36571628-728C-4ACD-A47F-503BA91C5D43}.Debug|x64.Build.0 = Debug|x64 + {36571628-728C-4ACD-A47F-503BA91C5D43}.Debug|x86.ActiveCfg = Debug|x64 {36571628-728C-4ACD-A47F-503BA91C5D43}.Release|x64.ActiveCfg = Release|x64 {36571628-728C-4ACD-A47F-503BA91C5D43}.Release|x64.Build.0 = Release|x64 + {36571628-728C-4ACD-A47F-503BA91C5D43}.Release|x86.ActiveCfg = Release|x64 {EFA68887-8A97-49D7-BAB4-768E15A4E597}.Debug|x64.ActiveCfg = Debug|x64 {EFA68887-8A97-49D7-BAB4-768E15A4E597}.Debug|x64.Build.0 = Debug|x64 + {EFA68887-8A97-49D7-BAB4-768E15A4E597}.Debug|x86.ActiveCfg = Debug|x64 {EFA68887-8A97-49D7-BAB4-768E15A4E597}.Release|x64.ActiveCfg = Release|x64 {EFA68887-8A97-49D7-BAB4-768E15A4E597}.Release|x64.Build.0 = Release|x64 + {EFA68887-8A97-49D7-BAB4-768E15A4E597}.Release|x86.ActiveCfg = Release|x64 {806A629B-7EAA-4265-A4C0-8A0EC03B104F}.Debug|x64.ActiveCfg = Debug|x64 {806A629B-7EAA-4265-A4C0-8A0EC03B104F}.Debug|x64.Build.0 = Debug|x64 + {806A629B-7EAA-4265-A4C0-8A0EC03B104F}.Debug|x86.ActiveCfg = Debug|x64 {806A629B-7EAA-4265-A4C0-8A0EC03B104F}.Release|x64.ActiveCfg = Release|x64 {806A629B-7EAA-4265-A4C0-8A0EC03B104F}.Release|x64.Build.0 = Release|x64 + {806A629B-7EAA-4265-A4C0-8A0EC03B104F}.Release|x86.ActiveCfg = Release|x64 {1FCA5DD9-57F4-45A4-9141-8AE392E2AFE8}.Debug|x64.ActiveCfg = Debug|x64 {1FCA5DD9-57F4-45A4-9141-8AE392E2AFE8}.Debug|x64.Build.0 = Debug|x64 + {1FCA5DD9-57F4-45A4-9141-8AE392E2AFE8}.Debug|x86.ActiveCfg = Debug|x64 {1FCA5DD9-57F4-45A4-9141-8AE392E2AFE8}.Release|x64.ActiveCfg = Release|x64 {1FCA5DD9-57F4-45A4-9141-8AE392E2AFE8}.Release|x64.Build.0 = Release|x64 + {1FCA5DD9-57F4-45A4-9141-8AE392E2AFE8}.Release|x86.ActiveCfg = Release|x64 {A28DEB4E-E645-4990-A91D-4A7FF6762239}.Debug|x64.ActiveCfg = Debug|x64 {A28DEB4E-E645-4990-A91D-4A7FF6762239}.Debug|x64.Build.0 = Debug|x64 + {A28DEB4E-E645-4990-A91D-4A7FF6762239}.Debug|x86.ActiveCfg = Debug|x64 {A28DEB4E-E645-4990-A91D-4A7FF6762239}.Release|x64.ActiveCfg = Release|x64 {A28DEB4E-E645-4990-A91D-4A7FF6762239}.Release|x64.Build.0 = Release|x64 + {A28DEB4E-E645-4990-A91D-4A7FF6762239}.Release|x86.ActiveCfg = Release|x64 {3645AD5B-44B8-4121-81FE-808067EFEA2A}.Debug|x64.ActiveCfg = Debug|x64 {3645AD5B-44B8-4121-81FE-808067EFEA2A}.Debug|x64.Build.0 = Debug|x64 + {3645AD5B-44B8-4121-81FE-808067EFEA2A}.Debug|x86.ActiveCfg = Debug|x64 {3645AD5B-44B8-4121-81FE-808067EFEA2A}.Release|x64.ActiveCfg = Release|x64 {3645AD5B-44B8-4121-81FE-808067EFEA2A}.Release|x64.Build.0 = Release|x64 + {3645AD5B-44B8-4121-81FE-808067EFEA2A}.Release|x86.ActiveCfg = Release|x64 {8A82B822-DA7A-464B-AE6F-8EFB5EDA2C19}.Debug|x64.ActiveCfg = Debug|x64 {8A82B822-DA7A-464B-AE6F-8EFB5EDA2C19}.Debug|x64.Build.0 = Debug|x64 + {8A82B822-DA7A-464B-AE6F-8EFB5EDA2C19}.Debug|x86.ActiveCfg = Debug|x64 {8A82B822-DA7A-464B-AE6F-8EFB5EDA2C19}.Release|x64.ActiveCfg = Release|x64 {8A82B822-DA7A-464B-AE6F-8EFB5EDA2C19}.Release|x64.Build.0 = Release|x64 + {8A82B822-DA7A-464B-AE6F-8EFB5EDA2C19}.Release|x86.ActiveCfg = Release|x64 {19B83E51-446A-41DC-8899-ED2D59675933}.Debug|x64.ActiveCfg = Debug|x64 {19B83E51-446A-41DC-8899-ED2D59675933}.Debug|x64.Build.0 = Debug|x64 + {19B83E51-446A-41DC-8899-ED2D59675933}.Debug|x86.ActiveCfg = Debug|x64 {19B83E51-446A-41DC-8899-ED2D59675933}.Release|x64.ActiveCfg = Release|x64 {19B83E51-446A-41DC-8899-ED2D59675933}.Release|x64.Build.0 = Release|x64 + {19B83E51-446A-41DC-8899-ED2D59675933}.Release|x86.ActiveCfg = Release|x64 {6626896D-CA91-457B-9B78-242797B88FCF}.Debug|x64.ActiveCfg = Debug|x64 {6626896D-CA91-457B-9B78-242797B88FCF}.Debug|x64.Build.0 = Debug|x64 + {6626896D-CA91-457B-9B78-242797B88FCF}.Debug|x86.ActiveCfg = Debug|x64 {6626896D-CA91-457B-9B78-242797B88FCF}.Release|x64.ActiveCfg = Release|x64 {6626896D-CA91-457B-9B78-242797B88FCF}.Release|x64.Build.0 = Release|x64 + {6626896D-CA91-457B-9B78-242797B88FCF}.Release|x86.ActiveCfg = Release|x64 {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}.Debug|x86.ActiveCfg = Debug|x64 {AC358657-0C2B-41B6-97C1-1EFB8481D82A}.Release|x64.ActiveCfg = Release|x64 {AC358657-0C2B-41B6-97C1-1EFB8481D82A}.Release|x64.Build.0 = Release|x64 + {AC358657-0C2B-41B6-97C1-1EFB8481D82A}.Release|x86.ActiveCfg = Release|x64 {A7148F42-8EE8-48B4-BFC9-F61AB8A3800D}.Debug|x64.ActiveCfg = Debug|x64 {A7148F42-8EE8-48B4-BFC9-F61AB8A3800D}.Debug|x64.Build.0 = Debug|x64 + {A7148F42-8EE8-48B4-BFC9-F61AB8A3800D}.Debug|x86.ActiveCfg = Debug|x64 {A7148F42-8EE8-48B4-BFC9-F61AB8A3800D}.Release|x64.ActiveCfg = Release|x64 {A7148F42-8EE8-48B4-BFC9-F61AB8A3800D}.Release|x64.Build.0 = Release|x64 + {A7148F42-8EE8-48B4-BFC9-F61AB8A3800D}.Release|x86.ActiveCfg = Release|x64 {D29861C1-D0A2-44EB-BAD9-3808165609D8}.Debug|x64.ActiveCfg = Debug|x64 {D29861C1-D0A2-44EB-BAD9-3808165609D8}.Debug|x64.Build.0 = Debug|x64 + {D29861C1-D0A2-44EB-BAD9-3808165609D8}.Debug|x86.ActiveCfg = Debug|x64 {D29861C1-D0A2-44EB-BAD9-3808165609D8}.Release|x64.ActiveCfg = Release|x64 {D29861C1-D0A2-44EB-BAD9-3808165609D8}.Release|x64.Build.0 = Release|x64 + {D29861C1-D0A2-44EB-BAD9-3808165609D8}.Release|x86.ActiveCfg = Release|x64 {91CCB72B-852D-47D8-82FA-0E19A300A025}.Debug|x64.ActiveCfg = Debug|x64 {91CCB72B-852D-47D8-82FA-0E19A300A025}.Debug|x64.Build.0 = Debug|x64 + {91CCB72B-852D-47D8-82FA-0E19A300A025}.Debug|x86.ActiveCfg = Debug|x64 {91CCB72B-852D-47D8-82FA-0E19A300A025}.Release|x64.ActiveCfg = Release|x64 {91CCB72B-852D-47D8-82FA-0E19A300A025}.Release|x64.Build.0 = Release|x64 + {91CCB72B-852D-47D8-82FA-0E19A300A025}.Release|x86.ActiveCfg = Release|x64 {85D14FAC-12F1-42BF-8E32-DD2DD62A6AC9}.Debug|x64.ActiveCfg = Debug|x64 {85D14FAC-12F1-42BF-8E32-DD2DD62A6AC9}.Debug|x64.Build.0 = Debug|x64 + {85D14FAC-12F1-42BF-8E32-DD2DD62A6AC9}.Debug|x86.ActiveCfg = Debug|x64 {85D14FAC-12F1-42BF-8E32-DD2DD62A6AC9}.Release|x64.ActiveCfg = Release|x64 {85D14FAC-12F1-42BF-8E32-DD2DD62A6AC9}.Release|x64.Build.0 = Release|x64 + {85D14FAC-12F1-42BF-8E32-DD2DD62A6AC9}.Release|x86.ActiveCfg = Release|x64 {E69C9F0E-2608-4EAD-9AB1-75E8DEF7E053}.Debug|x64.ActiveCfg = Debug|x64 {E69C9F0E-2608-4EAD-9AB1-75E8DEF7E053}.Debug|x64.Build.0 = Debug|x64 + {E69C9F0E-2608-4EAD-9AB1-75E8DEF7E053}.Debug|x86.ActiveCfg = Debug|x64 {E69C9F0E-2608-4EAD-9AB1-75E8DEF7E053}.Release|x64.ActiveCfg = Release|x64 {E69C9F0E-2608-4EAD-9AB1-75E8DEF7E053}.Release|x64.Build.0 = Release|x64 + {E69C9F0E-2608-4EAD-9AB1-75E8DEF7E053}.Release|x86.ActiveCfg = Release|x64 {5D1758C4-147E-4E06-9BF3-F4DDF23B5B97}.Debug|x64.ActiveCfg = Debug|x64 {5D1758C4-147E-4E06-9BF3-F4DDF23B5B97}.Debug|x64.Build.0 = Debug|x64 + {5D1758C4-147E-4E06-9BF3-F4DDF23B5B97}.Debug|x86.ActiveCfg = Debug|x64 {5D1758C4-147E-4E06-9BF3-F4DDF23B5B97}.Release|x64.ActiveCfg = Release|x64 {5D1758C4-147E-4E06-9BF3-F4DDF23B5B97}.Release|x64.Build.0 = Release|x64 + {5D1758C4-147E-4E06-9BF3-F4DDF23B5B97}.Release|x86.ActiveCfg = Release|x64 {ED4BAD61-62D4-4407-9A40-E0DE686F5419}.Debug|x64.ActiveCfg = Debug|x64 {ED4BAD61-62D4-4407-9A40-E0DE686F5419}.Debug|x64.Build.0 = Debug|x64 + {ED4BAD61-62D4-4407-9A40-E0DE686F5419}.Debug|x86.ActiveCfg = Debug|x64 {ED4BAD61-62D4-4407-9A40-E0DE686F5419}.Release|x64.ActiveCfg = Release|x64 {ED4BAD61-62D4-4407-9A40-E0DE686F5419}.Release|x64.Build.0 = Release|x64 + {ED4BAD61-62D4-4407-9A40-E0DE686F5419}.Release|x86.ActiveCfg = Release|x64 {E2900E6C-549B-46D2-AB40-8656280F7C53}.Debug|x64.ActiveCfg = Debug|x64 {E2900E6C-549B-46D2-AB40-8656280F7C53}.Debug|x64.Build.0 = Debug|x64 + {E2900E6C-549B-46D2-AB40-8656280F7C53}.Debug|x86.ActiveCfg = Debug|x64 {E2900E6C-549B-46D2-AB40-8656280F7C53}.Release|x64.ActiveCfg = Release|x64 {E2900E6C-549B-46D2-AB40-8656280F7C53}.Release|x64.Build.0 = Release|x64 + {E2900E6C-549B-46D2-AB40-8656280F7C53}.Release|x86.ActiveCfg = Release|x64 {2F7AA48D-2A51-436E-A6F2-6C36EDD1A1A1}.Debug|x64.ActiveCfg = Debug|x64 {2F7AA48D-2A51-436E-A6F2-6C36EDD1A1A1}.Debug|x64.Build.0 = Debug|x64 + {2F7AA48D-2A51-436E-A6F2-6C36EDD1A1A1}.Debug|x86.ActiveCfg = Debug|x64 {2F7AA48D-2A51-436E-A6F2-6C36EDD1A1A1}.Release|x64.ActiveCfg = Release|x64 {2F7AA48D-2A51-436E-A6F2-6C36EDD1A1A1}.Release|x64.Build.0 = Release|x64 + {2F7AA48D-2A51-436E-A6F2-6C36EDD1A1A1}.Release|x86.ActiveCfg = Release|x64 {5E687DA5-2842-4661-AAB4-FEE4D192EBB9}.Debug|x64.ActiveCfg = Debug|x64 {5E687DA5-2842-4661-AAB4-FEE4D192EBB9}.Debug|x64.Build.0 = Debug|x64 + {5E687DA5-2842-4661-AAB4-FEE4D192EBB9}.Debug|x86.ActiveCfg = Debug|x64 {5E687DA5-2842-4661-AAB4-FEE4D192EBB9}.Release|x64.ActiveCfg = Release|x64 {5E687DA5-2842-4661-AAB4-FEE4D192EBB9}.Release|x64.Build.0 = Release|x64 + {5E687DA5-2842-4661-AAB4-FEE4D192EBB9}.Release|x86.ActiveCfg = Release|x64 {AEA99C48-1A7A-4265-BAF6-28D734EE61E8}.Debug|x64.ActiveCfg = Debug|x64 {AEA99C48-1A7A-4265-BAF6-28D734EE61E8}.Debug|x64.Build.0 = Debug|x64 + {AEA99C48-1A7A-4265-BAF6-28D734EE61E8}.Debug|x86.ActiveCfg = Debug|x64 {AEA99C48-1A7A-4265-BAF6-28D734EE61E8}.Release|x64.ActiveCfg = Release|x64 {AEA99C48-1A7A-4265-BAF6-28D734EE61E8}.Release|x64.Build.0 = Release|x64 + {AEA99C48-1A7A-4265-BAF6-28D734EE61E8}.Release|x86.ActiveCfg = Release|x64 {FB00F98E-4A2C-4888-A11F-29E5A1CA3E10}.Debug|x64.ActiveCfg = Debug|x64 {FB00F98E-4A2C-4888-A11F-29E5A1CA3E10}.Debug|x64.Build.0 = Debug|x64 + {FB00F98E-4A2C-4888-A11F-29E5A1CA3E10}.Debug|x86.ActiveCfg = Debug|x64 {FB00F98E-4A2C-4888-A11F-29E5A1CA3E10}.Release|x64.ActiveCfg = Release|x64 {FB00F98E-4A2C-4888-A11F-29E5A1CA3E10}.Release|x64.Build.0 = Release|x64 + {FB00F98E-4A2C-4888-A11F-29E5A1CA3E10}.Release|x86.ActiveCfg = Release|x64 {CF60E682-EF19-4FDD-8060-DE80188C827C}.Debug|x64.ActiveCfg = Debug|x64 {CF60E682-EF19-4FDD-8060-DE80188C827C}.Debug|x64.Build.0 = Debug|x64 + {CF60E682-EF19-4FDD-8060-DE80188C827C}.Debug|x86.ActiveCfg = Debug|x64 {CF60E682-EF19-4FDD-8060-DE80188C827C}.Release|x64.ActiveCfg = Release|x64 {CF60E682-EF19-4FDD-8060-DE80188C827C}.Release|x64.Build.0 = Release|x64 + {CF60E682-EF19-4FDD-8060-DE80188C827C}.Release|x86.ActiveCfg = Release|x64 {3FCCE846-C82F-437A-A9B9-3CCEC02250EA}.Debug|x64.ActiveCfg = Debug|x64 {3FCCE846-C82F-437A-A9B9-3CCEC02250EA}.Debug|x64.Build.0 = Debug|x64 + {3FCCE846-C82F-437A-A9B9-3CCEC02250EA}.Debug|x86.ActiveCfg = Debug|x64 {3FCCE846-C82F-437A-A9B9-3CCEC02250EA}.Release|x64.ActiveCfg = Release|x64 {3FCCE846-C82F-437A-A9B9-3CCEC02250EA}.Release|x64.Build.0 = Release|x64 + {3FCCE846-C82F-437A-A9B9-3CCEC02250EA}.Release|x86.ActiveCfg = Release|x64 {3FCCE846-C82F-437A-A9B9-6ABDA34403AE}.Debug|x64.ActiveCfg = Debug|x64 {3FCCE846-C82F-437A-A9B9-6ABDA34403AE}.Debug|x64.Build.0 = Debug|x64 + {3FCCE846-C82F-437A-A9B9-6ABDA34403AE}.Debug|x86.ActiveCfg = Debug|x64 {3FCCE846-C82F-437A-A9B9-6ABDA34403AE}.Release|x64.ActiveCfg = Release|x64 {3FCCE846-C82F-437A-A9B9-6ABDA34403AE}.Release|x64.Build.0 = Release|x64 + {3FCCE846-C82F-437A-A9B9-6ABDA34403AE}.Release|x86.ActiveCfg = Release|x64 {0E350120-8D1B-4E44-8B21-DADAEBC02217}.Debug|x64.ActiveCfg = Debug|x64 {0E350120-8D1B-4E44-8B21-DADAEBC02217}.Debug|x64.Build.0 = Debug|x64 + {0E350120-8D1B-4E44-8B21-DADAEBC02217}.Debug|x86.ActiveCfg = Debug|x64 {0E350120-8D1B-4E44-8B21-DADAEBC02217}.Release|x64.ActiveCfg = Release|x64 {0E350120-8D1B-4E44-8B21-DADAEBC02217}.Release|x64.Build.0 = Release|x64 + {0E350120-8D1B-4E44-8B21-DADAEBC02217}.Release|x86.ActiveCfg = Release|x64 {24FF0DE6-E201-46C1-ACFB-E4B6B4527C84}.Debug|x64.ActiveCfg = Debug|x64 {24FF0DE6-E201-46C1-ACFB-E4B6B4527C84}.Debug|x64.Build.0 = Debug|x64 + {24FF0DE6-E201-46C1-ACFB-E4B6B4527C84}.Debug|x86.ActiveCfg = Debug|x64 {24FF0DE6-E201-46C1-ACFB-E4B6B4527C84}.Release|x64.ActiveCfg = Release|x64 {24FF0DE6-E201-46C1-ACFB-E4B6B4527C84}.Release|x64.Build.0 = Release|x64 + {24FF0DE6-E201-46C1-ACFB-E4B6B4527C84}.Release|x86.ActiveCfg = Release|x64 {A9146B8A-A700-40BB-B018-4FAD6F8E79EC}.Debug|x64.ActiveCfg = Debug|x64 {A9146B8A-A700-40BB-B018-4FAD6F8E79EC}.Debug|x64.Build.0 = Debug|x64 + {A9146B8A-A700-40BB-B018-4FAD6F8E79EC}.Debug|x86.ActiveCfg = Debug|x64 {A9146B8A-A700-40BB-B018-4FAD6F8E79EC}.Release|x64.ActiveCfg = Release|x64 {A9146B8A-A700-40BB-B018-4FAD6F8E79EC}.Release|x64.Build.0 = Release|x64 + {A9146B8A-A700-40BB-B018-4FAD6F8E79EC}.Release|x86.ActiveCfg = Release|x64 {B6FEC6DD-A94F-435C-B8BD-BEE8931F0E57}.Debug|x64.ActiveCfg = Debug|x64 {B6FEC6DD-A94F-435C-B8BD-BEE8931F0E57}.Debug|x64.Build.0 = Debug|x64 + {B6FEC6DD-A94F-435C-B8BD-BEE8931F0E57}.Debug|x86.ActiveCfg = Debug|x64 {B6FEC6DD-A94F-435C-B8BD-BEE8931F0E57}.Release|x64.ActiveCfg = Release|x64 {B6FEC6DD-A94F-435C-B8BD-BEE8931F0E57}.Release|x64.Build.0 = Release|x64 + {B6FEC6DD-A94F-435C-B8BD-BEE8931F0E57}.Release|x86.ActiveCfg = Release|x64 {E68C2689-7D9F-4B7B-9100-FAB4CF5B08FD}.Debug|x64.ActiveCfg = Debug|x64 {E68C2689-7D9F-4B7B-9100-FAB4CF5B08FD}.Debug|x64.Build.0 = Debug|x64 + {E68C2689-7D9F-4B7B-9100-FAB4CF5B08FD}.Debug|x86.ActiveCfg = Debug|x64 {E68C2689-7D9F-4B7B-9100-FAB4CF5B08FD}.Release|x64.ActiveCfg = Release|x64 {E68C2689-7D9F-4B7B-9100-FAB4CF5B08FD}.Release|x64.Build.0 = Release|x64 + {E68C2689-7D9F-4B7B-9100-FAB4CF5B08FD}.Release|x86.ActiveCfg = Release|x64 {2067D4A8-8C32-40D3-BA9F-6B388ECCD714}.Debug|x64.ActiveCfg = Debug|x64 {2067D4A8-8C32-40D3-BA9F-6B388ECCD714}.Debug|x64.Build.0 = Debug|x64 + {2067D4A8-8C32-40D3-BA9F-6B388ECCD714}.Debug|x86.ActiveCfg = Debug|x64 {2067D4A8-8C32-40D3-BA9F-6B388ECCD714}.Release|x64.ActiveCfg = Release|x64 {2067D4A8-8C32-40D3-BA9F-6B388ECCD714}.Release|x64.Build.0 = Release|x64 + {2067D4A8-8C32-40D3-BA9F-6B388ECCD714}.Release|x86.ActiveCfg = Release|x64 {52D8BB90-2859-4086-A310-C72721BEF446}.Debug|x64.ActiveCfg = Debug|x64 {52D8BB90-2859-4086-A310-C72721BEF446}.Debug|x64.Build.0 = Debug|x64 + {52D8BB90-2859-4086-A310-C72721BEF446}.Debug|x86.ActiveCfg = Debug|x64 {52D8BB90-2859-4086-A310-C72721BEF446}.Release|x64.ActiveCfg = Release|x64 {52D8BB90-2859-4086-A310-C72721BEF446}.Release|x64.Build.0 = Release|x64 + {52D8BB90-2859-4086-A310-C72721BEF446}.Release|x86.ActiveCfg = Release|x64 {3BFC5BBD-6C7D-44A7-8361-F9D6E03C98AB}.Debug|x64.ActiveCfg = Debug|x64 {3BFC5BBD-6C7D-44A7-8361-F9D6E03C98AB}.Debug|x64.Build.0 = Debug|x64 + {3BFC5BBD-6C7D-44A7-8361-F9D6E03C98AB}.Debug|x86.ActiveCfg = Debug|x64 {3BFC5BBD-6C7D-44A7-8361-F9D6E03C98AB}.Release|x64.ActiveCfg = Release|x64 {3BFC5BBD-6C7D-44A7-8361-F9D6E03C98AB}.Release|x64.Build.0 = Release|x64 + {3BFC5BBD-6C7D-44A7-8361-F9D6E03C98AB}.Release|x86.ActiveCfg = Release|x64 {31FE7D1C-8B0F-4E6C-9A50-F63596606764}.Debug|x64.ActiveCfg = Debug|x64 {31FE7D1C-8B0F-4E6C-9A50-F63596606764}.Debug|x64.Build.0 = Debug|x64 + {31FE7D1C-8B0F-4E6C-9A50-F63596606764}.Debug|x86.ActiveCfg = Debug|x64 {31FE7D1C-8B0F-4E6C-9A50-F63596606764}.Release|x64.ActiveCfg = Release|x64 {31FE7D1C-8B0F-4E6C-9A50-F63596606764}.Release|x64.Build.0 = Release|x64 + {31FE7D1C-8B0F-4E6C-9A50-F63596606764}.Release|x86.ActiveCfg = Release|x64 {DCAF2299-3620-40FB-9E55-62A860FD67F2}.Debug|x64.ActiveCfg = Debug|x64 {DCAF2299-3620-40FB-9E55-62A860FD67F2}.Debug|x64.Build.0 = Debug|x64 + {DCAF2299-3620-40FB-9E55-62A860FD67F2}.Debug|x86.ActiveCfg = Debug|x64 {DCAF2299-3620-40FB-9E55-62A860FD67F2}.Release|x64.ActiveCfg = Release|x64 {DCAF2299-3620-40FB-9E55-62A860FD67F2}.Release|x64.Build.0 = Release|x64 + {DCAF2299-3620-40FB-9E55-62A860FD67F2}.Release|x86.ActiveCfg = Release|x64 {399E0E5D-DF7F-4747-A448-5116E7534955}.Debug|x64.ActiveCfg = Debug|x64 {399E0E5D-DF7F-4747-A448-5116E7534955}.Debug|x64.Build.0 = Debug|x64 + {399E0E5D-DF7F-4747-A448-5116E7534955}.Debug|x86.ActiveCfg = Debug|x64 {399E0E5D-DF7F-4747-A448-5116E7534955}.Release|x64.ActiveCfg = Release|x64 {399E0E5D-DF7F-4747-A448-5116E7534955}.Release|x64.Build.0 = Release|x64 + {399E0E5D-DF7F-4747-A448-5116E7534955}.Release|x86.ActiveCfg = Release|x64 {DEE8AF34-2F99-4AA8-AFDA-848DE44F1DB2}.Debug|x64.ActiveCfg = Debug|x64 {DEE8AF34-2F99-4AA8-AFDA-848DE44F1DB2}.Debug|x64.Build.0 = Debug|x64 + {DEE8AF34-2F99-4AA8-AFDA-848DE44F1DB2}.Debug|x86.ActiveCfg = Debug|x64 {DEE8AF34-2F99-4AA8-AFDA-848DE44F1DB2}.Release|x64.ActiveCfg = Release|x64 {DEE8AF34-2F99-4AA8-AFDA-848DE44F1DB2}.Release|x64.Build.0 = Release|x64 + {DEE8AF34-2F99-4AA8-AFDA-848DE44F1DB2}.Release|x86.ActiveCfg = Release|x64 {A6FA70EC-5358-4837-A8E0-EB02B702F6A8}.Debug|x64.ActiveCfg = Debug|x64 {A6FA70EC-5358-4837-A8E0-EB02B702F6A8}.Debug|x64.Build.0 = Debug|x64 + {A6FA70EC-5358-4837-A8E0-EB02B702F6A8}.Debug|x86.ActiveCfg = Debug|x64 {A6FA70EC-5358-4837-A8E0-EB02B702F6A8}.Release|x64.ActiveCfg = Release|x64 {A6FA70EC-5358-4837-A8E0-EB02B702F6A8}.Release|x64.Build.0 = Release|x64 + {A6FA70EC-5358-4837-A8E0-EB02B702F6A8}.Release|x86.ActiveCfg = Release|x64 {06A4A3EA-9461-4780-B6EA-81004B3F3D56}.Debug|x64.ActiveCfg = Debug|x64 {06A4A3EA-9461-4780-B6EA-81004B3F3D56}.Debug|x64.Build.0 = Debug|x64 + {06A4A3EA-9461-4780-B6EA-81004B3F3D56}.Debug|x86.ActiveCfg = Debug|x64 {06A4A3EA-9461-4780-B6EA-81004B3F3D56}.Release|x64.ActiveCfg = Release|x64 {06A4A3EA-9461-4780-B6EA-81004B3F3D56}.Release|x64.Build.0 = Release|x64 + {06A4A3EA-9461-4780-B6EA-81004B3F3D56}.Release|x86.ActiveCfg = Release|x64 {391502E7-BAAF-42DF-9327-2114B73FF8DF}.Debug|x64.ActiveCfg = Debug|x64 {391502E7-BAAF-42DF-9327-2114B73FF8DF}.Debug|x64.Build.0 = Debug|x64 + {391502E7-BAAF-42DF-9327-2114B73FF8DF}.Debug|x86.ActiveCfg = Debug|x64 {391502E7-BAAF-42DF-9327-2114B73FF8DF}.Release|x64.ActiveCfg = Release|x64 {391502E7-BAAF-42DF-9327-2114B73FF8DF}.Release|x64.Build.0 = Release|x64 + {391502E7-BAAF-42DF-9327-2114B73FF8DF}.Release|x86.ActiveCfg = Release|x64 {DEE8AF34-2F99-4AA8-AFDA-848DE44F1DB3}.Debug|x64.ActiveCfg = Debug|x64 {DEE8AF34-2F99-4AA8-AFDA-848DE44F1DB3}.Debug|x64.Build.0 = Debug|x64 + {DEE8AF34-2F99-4AA8-AFDA-848DE44F1DB3}.Debug|x86.ActiveCfg = Debug|x64 {DEE8AF34-2F99-4AA8-AFDA-848DE44F1DB3}.Release|x64.ActiveCfg = Release|x64 {DEE8AF34-2F99-4AA8-AFDA-848DE44F1DB3}.Release|x64.Build.0 = Release|x64 + {DEE8AF34-2F99-4AA8-AFDA-848DE44F1DB3}.Release|x86.ActiveCfg = Release|x64 {EB02D0C4-F80E-4C5A-9B8E-C5760E02F8BE}.Debug|x64.ActiveCfg = Debug|x64 {EB02D0C4-F80E-4C5A-9B8E-C5760E02F8BE}.Debug|x64.Build.0 = Debug|x64 + {EB02D0C4-F80E-4C5A-9B8E-C5760E02F8BE}.Debug|x86.ActiveCfg = Debug|x64 {EB02D0C4-F80E-4C5A-9B8E-C5760E02F8BE}.Release|x64.ActiveCfg = Release|x64 {EB02D0C4-F80E-4C5A-9B8E-C5760E02F8BE}.Release|x64.Build.0 = Release|x64 + {EB02D0C4-F80E-4C5A-9B8E-C5760E02F8BE}.Release|x86.ActiveCfg = Release|x64 {CFD4836D-3BA0-4CFE-B986-A4B34D23CDA6}.Debug|x64.ActiveCfg = Debug|x64 {CFD4836D-3BA0-4CFE-B986-A4B34D23CDA6}.Debug|x64.Build.0 = Debug|x64 + {CFD4836D-3BA0-4CFE-B986-A4B34D23CDA6}.Debug|x86.ActiveCfg = Debug|x64 {CFD4836D-3BA0-4CFE-B986-A4B34D23CDA6}.Release|x64.ActiveCfg = Release|x64 {CFD4836D-3BA0-4CFE-B986-A4B34D23CDA6}.Release|x64.Build.0 = Release|x64 + {CFD4836D-3BA0-4CFE-B986-A4B34D23CDA6}.Release|x86.ActiveCfg = Release|x64 {AD2E2778-836C-11DE-9828-78A156D89593}.Debug|x64.ActiveCfg = Debug|x64 {AD2E2778-836C-11DE-9828-78A156D89593}.Debug|x64.Build.0 = Debug|x64 + {AD2E2778-836C-11DE-9828-78A156D89593}.Debug|x86.ActiveCfg = Debug|x64 {AD2E2778-836C-11DE-9828-78A156D89593}.Release|x64.ActiveCfg = Release|x64 {AD2E2778-836C-11DE-9828-78A156D89593}.Release|x64.Build.0 = Release|x64 + {AD2E2778-836C-11DE-9828-78A156D89593}.Release|x86.ActiveCfg = Release|x64 {3A83E7A0-34A5-4304-8D88-148D933EACCD}.Debug|x64.ActiveCfg = Debug|x64 {3A83E7A0-34A5-4304-8D88-148D933EACCD}.Debug|x64.Build.0 = Debug|x64 + {3A83E7A0-34A5-4304-8D88-148D933EACCD}.Debug|x86.ActiveCfg = Debug|x64 {3A83E7A0-34A5-4304-8D88-148D933EACCD}.Release|x64.ActiveCfg = Release|x64 {3A83E7A0-34A5-4304-8D88-148D933EACCD}.Release|x64.Build.0 = Release|x64 + {3A83E7A0-34A5-4304-8D88-148D933EACCD}.Release|x86.ActiveCfg = Release|x64 {19B83E51-446A-41DC-8899-ED2D59679999}.Debug|x64.ActiveCfg = Debug|x64 {19B83E51-446A-41DC-8899-ED2D59679999}.Debug|x64.Build.0 = Debug|x64 + {19B83E51-446A-41DC-8899-ED2D59679999}.Debug|x86.ActiveCfg = Debug|x64 {19B83E51-446A-41DC-8899-ED2D59679999}.Release|x64.ActiveCfg = Release|x64 {19B83E51-446A-41DC-8899-ED2D59679999}.Release|x64.Build.0 = Release|x64 + {19B83E51-446A-41DC-8899-ED2D59679999}.Release|x86.ActiveCfg = Release|x64 {43250DC7-A5AB-4795-9A54-7BF048465D00}.Debug|x64.ActiveCfg = Debug|x64 {43250DC7-A5AB-4795-9A54-7BF048465D00}.Debug|x64.Build.0 = Debug|x64 + {43250DC7-A5AB-4795-9A54-7BF048465D00}.Debug|x86.ActiveCfg = Debug|x64 {43250DC7-A5AB-4795-9A54-7BF048465D00}.Release|x64.ActiveCfg = Release|x64 {43250DC7-A5AB-4795-9A54-7BF048465D00}.Release|x64.Build.0 = Release|x64 + {43250DC7-A5AB-4795-9A54-7BF048465D00}.Release|x86.ActiveCfg = Release|x64 {E3DA287B-6861-49E2-8484-B08DBA020F7D}.Debug|x64.ActiveCfg = Debug|x64 {E3DA287B-6861-49E2-8484-B08DBA020F7D}.Debug|x64.Build.0 = Debug|x64 + {E3DA287B-6861-49E2-8484-B08DBA020F7D}.Debug|x86.ActiveCfg = Debug|x64 {E3DA287B-6861-49E2-8484-B08DBA020F7D}.Release|x64.ActiveCfg = Release|x64 {E3DA287B-6861-49E2-8484-B08DBA020F7D}.Release|x64.Build.0 = Release|x64 + {E3DA287B-6861-49E2-8484-B08DBA020F7D}.Release|x86.ActiveCfg = Release|x64 {489AF9AB-7A0E-43DE-8483-3E4B8CCCBFCD}.Debug|x64.ActiveCfg = Debug|x64 {489AF9AB-7A0E-43DE-8483-3E4B8CCCBFCD}.Debug|x64.Build.0 = Debug|x64 + {489AF9AB-7A0E-43DE-8483-3E4B8CCCBFCD}.Debug|x86.ActiveCfg = Debug|x64 {489AF9AB-7A0E-43DE-8483-3E4B8CCCBFCD}.Release|x64.ActiveCfg = Release|x64 {489AF9AB-7A0E-43DE-8483-3E4B8CCCBFCD}.Release|x64.Build.0 = Release|x64 + {489AF9AB-7A0E-43DE-8483-3E4B8CCCBFCD}.Release|x86.ActiveCfg = Release|x64 {2B522528-9660-4B78-8E2A-BC296C9B9937}.Debug|x64.ActiveCfg = Debug|x64 {2B522528-9660-4B78-8E2A-BC296C9B9937}.Debug|x64.Build.0 = Debug|x64 + {2B522528-9660-4B78-8E2A-BC296C9B9937}.Debug|x86.ActiveCfg = Debug|x64 {2B522528-9660-4B78-8E2A-BC296C9B9937}.Release|x64.ActiveCfg = Release|x64 {2B522528-9660-4B78-8E2A-BC296C9B9937}.Release|x64.Build.0 = Release|x64 + {2B522528-9660-4B78-8E2A-BC296C9B9937}.Release|x86.ActiveCfg = Release|x64 {2B522528-9660-4B78-8E2A-BC296C9BC0C0}.Debug|x64.ActiveCfg = Debug|x64 {2B522528-9660-4B78-8E2A-BC296C9BC0C0}.Debug|x64.Build.0 = Debug|x64 + {2B522528-9660-4B78-8E2A-BC296C9BC0C0}.Debug|x86.ActiveCfg = Debug|x64 {2B522528-9660-4B78-8E2A-BC296C9BC0C0}.Release|x64.ActiveCfg = Release|x64 {2B522528-9660-4B78-8E2A-BC296C9BC0C0}.Release|x64.Build.0 = Release|x64 + {2B522528-9660-4B78-8E2A-BC296C9BC0C0}.Release|x86.ActiveCfg = Release|x64 {2EF7ACDC-F742-4CB6-B301-A242F52BE5C3}.Debug|x64.ActiveCfg = Debug|x64 {2EF7ACDC-F742-4CB6-B301-A242F52BE5C3}.Debug|x64.Build.0 = Debug|x64 + {2EF7ACDC-F742-4CB6-B301-A242F52BE5C3}.Debug|x86.ActiveCfg = Debug|x64 {2EF7ACDC-F742-4CB6-B301-A242F52BE5C3}.Release|x64.ActiveCfg = Release|x64 {2EF7ACDC-F742-4CB6-B301-A242F52BE5C3}.Release|x64.Build.0 = Release|x64 + {2EF7ACDC-F742-4CB6-B301-A242F52BE5C3}.Release|x86.ActiveCfg = Release|x64 {205D835F-D939-4E0D-9516-061AB141480F}.Debug|x64.ActiveCfg = Debug|x64 {205D835F-D939-4E0D-9516-061AB141480F}.Debug|x64.Build.0 = Debug|x64 + {205D835F-D939-4E0D-9516-061AB141480F}.Debug|x86.ActiveCfg = Debug|x64 {205D835F-D939-4E0D-9516-061AB141480F}.Release|x64.ActiveCfg = Release|x64 {205D835F-D939-4E0D-9516-061AB141480F}.Release|x64.Build.0 = Release|x64 + {205D835F-D939-4E0D-9516-061AB141480F}.Release|x86.ActiveCfg = Release|x64 {CF60E682-EF19-4FDD-8060-DE80256C827C}.Debug|x64.ActiveCfg = Debug|x64 {CF60E682-EF19-4FDD-8060-DE80256C827C}.Debug|x64.Build.0 = Debug|x64 + {CF60E682-EF19-4FDD-8060-DE80256C827C}.Debug|x86.ActiveCfg = Debug|x64 {CF60E682-EF19-4FDD-8060-DE80256C827C}.Release|x64.ActiveCfg = Release|x64 {CF60E682-EF19-4FDD-8060-DE80256C827C}.Release|x64.Build.0 = Release|x64 + {CF60E682-EF19-4FDD-8060-DE80256C827C}.Release|x86.ActiveCfg = Release|x64 {2B522528-9660-4B78-8E2A-000000AAA03F}.Debug|x64.ActiveCfg = Debug|x64 {2B522528-9660-4B78-8E2A-000000AAA03F}.Debug|x64.Build.0 = Debug|x64 + {2B522528-9660-4B78-8E2A-000000AAA03F}.Debug|x86.ActiveCfg = Debug|x64 {2B522528-9660-4B78-8E2A-000000AAA03F}.Release|x64.ActiveCfg = Release|x64 {2B522528-9660-4B78-8E2A-000000AAA03F}.Release|x64.Build.0 = Release|x64 + {2B522528-9660-4B78-8E2A-000000AAA03F}.Release|x86.ActiveCfg = Release|x64 {19B83E51-446A-41DC-8899-ED2D59680000}.Debug|x64.ActiveCfg = Debug|x64 {19B83E51-446A-41DC-8899-ED2D59680000}.Debug|x64.Build.0 = Debug|x64 + {19B83E51-446A-41DC-8899-ED2D59680000}.Debug|x86.ActiveCfg = Debug|x64 {19B83E51-446A-41DC-8899-ED2D59680000}.Release|x64.ActiveCfg = Release|x64 {19B83E51-446A-41DC-8899-ED2D59680000}.Release|x64.Build.0 = Release|x64 + {19B83E51-446A-41DC-8899-ED2D59680000}.Release|x86.ActiveCfg = Release|x64 {F7EE5E71-A1B0-7DF7-EE5E-71A1B07DF7EE}.Debug|x64.ActiveCfg = Debug|x64 {F7EE5E71-A1B0-7DF7-EE5E-71A1B07DF7EE}.Debug|x64.Build.0 = Debug|x64 + {F7EE5E71-A1B0-7DF7-EE5E-71A1B07DF7EE}.Debug|x86.ActiveCfg = Debug|x64 {F7EE5E71-A1B0-7DF7-EE5E-71A1B07DF7EE}.Release|x64.ActiveCfg = Release|x64 {F7EE5E71-A1B0-7DF7-EE5E-71A1B07DF7EE}.Release|x64.Build.0 = Release|x64 + {F7EE5E71-A1B0-7DF7-EE5E-71A1B07DF7EE}.Release|x86.ActiveCfg = Release|x64 {4B9E78C0-C365-11DF-9539-E01BDFD72085}.Debug|x64.ActiveCfg = Debug|x64 {4B9E78C0-C365-11DF-9539-E01BDFD72085}.Debug|x64.Build.0 = Debug|x64 + {4B9E78C0-C365-11DF-9539-E01BDFD72085}.Debug|x86.ActiveCfg = Debug|x64 {4B9E78C0-C365-11DF-9539-E01BDFD72085}.Release|x64.ActiveCfg = Release|x64 {4B9E78C0-C365-11DF-9539-E01BDFD72085}.Release|x64.Build.0 = Release|x64 + {4B9E78C0-C365-11DF-9539-E01BDFD72085}.Release|x86.ActiveCfg = Release|x64 {95CE7E3D-1E0D-4C5B-AF9B-B8343A8724FB}.Debug|x64.ActiveCfg = Debug|x64 {95CE7E3D-1E0D-4C5B-AF9B-B8343A8724FB}.Debug|x64.Build.0 = Debug|x64 + {95CE7E3D-1E0D-4C5B-AF9B-B8343A8724FB}.Debug|x86.ActiveCfg = Debug|x64 {95CE7E3D-1E0D-4C5B-AF9B-B8343A8724FB}.Release|x64.ActiveCfg = Release|x64 {95CE7E3D-1E0D-4C5B-AF9B-B8343A8724FB}.Release|x64.Build.0 = Release|x64 + {95CE7E3D-1E0D-4C5B-AF9B-B8343A8724FB}.Release|x86.ActiveCfg = Release|x64 {E5ABCDE7-03F1-4683-98C3-8F5F79E1A473}.Debug|x64.ActiveCfg = Debug|x64 {E5ABCDE7-03F1-4683-98C3-8F5F79E1A473}.Debug|x64.Build.0 = Debug|x64 + {E5ABCDE7-03F1-4683-98C3-8F5F79E1A473}.Debug|x86.ActiveCfg = Debug|x64 {E5ABCDE7-03F1-4683-98C3-8F5F79E1A473}.Release|x64.ActiveCfg = Release|x64 {E5ABCDE7-03F1-4683-98C3-8F5F79E1A473}.Release|x64.Build.0 = Release|x64 + {E5ABCDE7-03F1-4683-98C3-8F5F79E1A473}.Release|x86.ActiveCfg = Release|x64 {7DFCF21B-1F90-4444-B2AF-27212A52BA44}.Debug|x64.ActiveCfg = Debug|x64 {7DFCF21B-1F90-4444-B2AF-27212A52BA44}.Debug|x64.Build.0 = Debug|x64 + {7DFCF21B-1F90-4444-B2AF-27212A52BA44}.Debug|x86.ActiveCfg = Debug|x64 {7DFCF21B-1F90-4444-B2AF-27212A52BA44}.Release|x64.ActiveCfg = Release|x64 {7DFCF21B-1F90-4444-B2AF-27212A52BA44}.Release|x64.Build.0 = Release|x64 + {7DFCF21B-1F90-4444-B2AF-27212A52BA44}.Release|x86.ActiveCfg = Release|x64 {3A83E7A0-34A5-4304-8D88-00005A99712E}.Debug|x64.ActiveCfg = Debug|x64 {3A83E7A0-34A5-4304-8D88-00005A99712E}.Debug|x64.Build.0 = Debug|x64 + {3A83E7A0-34A5-4304-8D88-00005A99712E}.Debug|x86.ActiveCfg = Debug|x64 {3A83E7A0-34A5-4304-8D88-00005A99712E}.Release|x64.ActiveCfg = Release|x64 {3A83E7A0-34A5-4304-8D88-00005A99712E}.Release|x64.Build.0 = Release|x64 + {3A83E7A0-34A5-4304-8D88-00005A99712E}.Release|x86.ActiveCfg = Release|x64 {806A629B-7EAA-4265-A4C0-000000000000}.Debug|x64.ActiveCfg = Debug|x64 {806A629B-7EAA-4265-A4C0-000000000000}.Debug|x64.Build.0 = Debug|x64 + {806A629B-7EAA-4265-A4C0-000000000000}.Debug|x86.ActiveCfg = Debug|x64 {806A629B-7EAA-4265-A4C0-000000000000}.Release|x64.ActiveCfg = Release|x64 {806A629B-7EAA-4265-A4C0-000000000000}.Release|x64.Build.0 = Release|x64 + {806A629B-7EAA-4265-A4C0-000000000000}.Release|x86.ActiveCfg = Release|x64 {3BFC5BBD-6C7D-44A7-8381-F9D6E03C98AB}.Debug|x64.ActiveCfg = Debug|x64 {3BFC5BBD-6C7D-44A7-8381-F9D6E03C98AB}.Debug|x64.Build.0 = Debug|x64 + {3BFC5BBD-6C7D-44A7-8381-F9D6E03C98AB}.Debug|x86.ActiveCfg = Debug|x64 {3BFC5BBD-6C7D-44A7-8381-F9D6E03C98AB}.Release|x64.ActiveCfg = Release|x64 {3BFC5BBD-6C7D-44A7-8381-F9D6E03C98AB}.Release|x64.Build.0 = Release|x64 + {3BFC5BBD-6C7D-44A7-8381-F9D6E03C98AB}.Release|x86.ActiveCfg = Release|x64 {308AA3EE-4053-47E0-9004-A57E9572C7A6}.Debug|x64.ActiveCfg = Debug|x64 {308AA3EE-4053-47E0-9004-A57E9572C7A6}.Debug|x64.Build.0 = Debug|x64 + {308AA3EE-4053-47E0-9004-A57E9572C7A6}.Debug|x86.ActiveCfg = Debug|x64 {308AA3EE-4053-47E0-9004-A57E9572C7A6}.Release|x64.ActiveCfg = Release|x64 {308AA3EE-4053-47E0-9004-A57E9572C7A6}.Release|x64.Build.0 = Release|x64 + {308AA3EE-4053-47E0-9004-A57E9572C7A6}.Release|x86.ActiveCfg = Release|x64 {1B4CC156-6C0B-4571-AC6D-E6173512684F}.Debug|x64.ActiveCfg = Debug|x64 {1B4CC156-6C0B-4571-AC6D-E6173512684F}.Debug|x64.Build.0 = Debug|x64 + {1B4CC156-6C0B-4571-AC6D-E6173512684F}.Debug|x86.ActiveCfg = Debug|x64 {1B4CC156-6C0B-4571-AC6D-E6173512684F}.Release|x64.ActiveCfg = Release|x64 {1B4CC156-6C0B-4571-AC6D-E6173512684F}.Release|x64.Build.0 = Release|x64 + {1B4CC156-6C0B-4571-AC6D-E6173512684F}.Release|x86.ActiveCfg = Release|x64 {3A83E7A0-34A5-4304-8D88-00000572AD05}.Debug|x64.ActiveCfg = Debug|x64 {3A83E7A0-34A5-4304-8D88-00000572AD05}.Debug|x64.Build.0 = Debug|x64 + {3A83E7A0-34A5-4304-8D88-00000572AD05}.Debug|x86.ActiveCfg = Debug|x64 {3A83E7A0-34A5-4304-8D88-00000572AD05}.Release|x64.ActiveCfg = Release|x64 {3A83E7A0-34A5-4304-8D88-00000572AD05}.Release|x64.Build.0 = Release|x64 + {3A83E7A0-34A5-4304-8D88-00000572AD05}.Release|x86.ActiveCfg = Release|x64 {DEE8AF34-2F99-4AA8-AFDA-948DE44F1DB2}.Debug|x64.ActiveCfg = Debug|x64 {DEE8AF34-2F99-4AA8-AFDA-948DE44F1DB2}.Debug|x64.Build.0 = Debug|x64 + {DEE8AF34-2F99-4AA8-AFDA-948DE44F1DB2}.Debug|x86.ActiveCfg = Debug|x64 {DEE8AF34-2F99-4AA8-AFDA-948DE44F1DB2}.Release|x64.ActiveCfg = Release|x64 {DEE8AF34-2F99-4AA8-AFDA-948DE44F1DB2}.Release|x64.Build.0 = Release|x64 + {DEE8AF34-2F99-4AA8-AFDA-948DE44F1DB2}.Release|x86.ActiveCfg = Release|x64 {028283B8-4E29-457E-9A78-AB6154A0F9D4}.Debug|x64.ActiveCfg = Debug|x64 {028283B8-4E29-457E-9A78-AB6154A0F9D4}.Debug|x64.Build.0 = Debug|x64 + {028283B8-4E29-457E-9A78-AB6154A0F9D4}.Debug|x86.ActiveCfg = Debug|x64 {028283B8-4E29-457E-9A78-AB6154A0F9D4}.Release|x64.ActiveCfg = Release|x64 {028283B8-4E29-457E-9A78-AB6154A0F9D4}.Release|x64.Build.0 = Release|x64 + {028283B8-4E29-457E-9A78-AB6154A0F9D4}.Release|x86.ActiveCfg = Release|x64 {A9E3F2DD-8B71-4FA3-9AFE-DC2040D2CDDD}.Debug|x64.ActiveCfg = Debug|x64 {A9E3F2DD-8B71-4FA3-9AFE-DC2040D2CDDD}.Debug|x64.Build.0 = Debug|x64 + {A9E3F2DD-8B71-4FA3-9AFE-DC2040D2CDDD}.Debug|x86.ActiveCfg = Debug|x64 {A9E3F2DD-8B71-4FA3-9AFE-DC2040D2CDDD}.Release|x64.ActiveCfg = Release|x64 {A9E3F2DD-8B71-4FA3-9AFE-DC2040D2CDDD}.Release|x64.Build.0 = Release|x64 + {A9E3F2DD-8B71-4FA3-9AFE-DC2040D2CDDD}.Release|x86.ActiveCfg = Release|x64 {AA9B1613-8359-4944-B08D-B96639D0998E}.Debug|x64.ActiveCfg = Debug|x64 {AA9B1613-8359-4944-B08D-B96639D0998E}.Debug|x64.Build.0 = Debug|x64 + {AA9B1613-8359-4944-B08D-B96639D0998E}.Debug|x86.ActiveCfg = Debug|x64 {AA9B1613-8359-4944-B08D-B96639D0998E}.Release|x64.ActiveCfg = Release|x64 {AA9B1613-8359-4944-B08D-B96639D0998E}.Release|x64.Build.0 = Release|x64 + {AA9B1613-8359-4944-B08D-B96639D0998E}.Release|x86.ActiveCfg = Release|x64 {20B38F93-934F-4022-9507-72693315AF6E}.Debug|x64.ActiveCfg = Debug|x64 {20B38F93-934F-4022-9507-72693315AF6E}.Debug|x64.Build.0 = Debug|x64 + {20B38F93-934F-4022-9507-72693315AF6E}.Debug|x86.ActiveCfg = Debug|x64 {20B38F93-934F-4022-9507-72693315AF6E}.Release|x64.ActiveCfg = Release|x64 {20B38F93-934F-4022-9507-72693315AF6E}.Release|x64.Build.0 = Release|x64 + {20B38F93-934F-4022-9507-72693315AF6E}.Release|x86.ActiveCfg = Release|x64 {35FD34A9-3076-4DD9-8B51-174F94FA2248}.Debug|x64.ActiveCfg = Debug|x64 {35FD34A9-3076-4DD9-8B51-174F94FA2248}.Debug|x64.Build.0 = Debug|x64 + {35FD34A9-3076-4DD9-8B51-174F94FA2248}.Debug|x86.ActiveCfg = Debug|x64 {35FD34A9-3076-4DD9-8B51-174F94FA2248}.Release|x64.ActiveCfg = Release|x64 {35FD34A9-3076-4DD9-8B51-174F94FA2248}.Release|x64.Build.0 = Release|x64 + {35FD34A9-3076-4DD9-8B51-174F94FA2248}.Release|x86.ActiveCfg = Release|x64 {00B6552D-65F7-4412-BF78-E72F34152AB9}.Debug|x64.ActiveCfg = Debug|x64 {00B6552D-65F7-4412-BF78-E72F34152AB9}.Debug|x64.Build.0 = Debug|x64 + {00B6552D-65F7-4412-BF78-E72F34152AB9}.Debug|x86.ActiveCfg = Debug|x64 {00B6552D-65F7-4412-BF78-E72F34152AB9}.Release|x64.ActiveCfg = Release|x64 {00B6552D-65F7-4412-BF78-E72F34152AB9}.Release|x64.Build.0 = Release|x64 + {00B6552D-65F7-4412-BF78-E72F34152AB9}.Release|x86.ActiveCfg = Release|x64 {AA828D40-0B3D-484C-BAB9-C72018D7760E}.Debug|x64.ActiveCfg = Debug|x64 {AA828D40-0B3D-484C-BAB9-C72018D7760E}.Debug|x64.Build.0 = Debug|x64 + {AA828D40-0B3D-484C-BAB9-C72018D7760E}.Debug|x86.ActiveCfg = Debug|x64 {AA828D40-0B3D-484C-BAB9-C72018D7760E}.Release|x64.ActiveCfg = Release|x64 {AA828D40-0B3D-484C-BAB9-C72018D7760E}.Release|x64.Build.0 = Release|x64 + {AA828D40-0B3D-484C-BAB9-C72018D7760E}.Release|x86.ActiveCfg = Release|x64 {5A692627-8FBA-4DAB-911D-75E88B96F99F}.Debug|x64.ActiveCfg = Debug|x64 {5A692627-8FBA-4DAB-911D-75E88B96F99F}.Debug|x64.Build.0 = Debug|x64 + {5A692627-8FBA-4DAB-911D-75E88B96F99F}.Debug|x86.ActiveCfg = Debug|x64 {5A692627-8FBA-4DAB-911D-75E88B96F99F}.Release|x64.ActiveCfg = Release|x64 {5A692627-8FBA-4DAB-911D-75E88B96F99F}.Release|x64.Build.0 = Release|x64 + {5A692627-8FBA-4DAB-911D-75E88B96F99F}.Release|x86.ActiveCfg = Release|x64 {52A0BB2A-0A16-40F8-86D4-99E0379F6E55}.Debug|x64.ActiveCfg = Debug|x64 {52A0BB2A-0A16-40F8-86D4-99E0379F6E55}.Debug|x64.Build.0 = Debug|x64 + {52A0BB2A-0A16-40F8-86D4-99E0379F6E55}.Debug|x86.ActiveCfg = Debug|x64 {52A0BB2A-0A16-40F8-86D4-99E0379F6E55}.Release|x64.ActiveCfg = Release|x64 {52A0BB2A-0A16-40F8-86D4-99E0379F6E55}.Release|x64.Build.0 = Release|x64 + {52A0BB2A-0A16-40F8-86D4-99E0379F6E55}.Release|x86.ActiveCfg = Release|x64 {2B522528-9660-4B78-8E2A-000000000000}.Debug|x64.ActiveCfg = Debug|x64 {2B522528-9660-4B78-8E2A-000000000000}.Debug|x64.Build.0 = Debug|x64 + {2B522528-9660-4B78-8E2A-000000000000}.Debug|x86.ActiveCfg = Debug|x64 {2B522528-9660-4B78-8E2A-000000000000}.Release|x64.ActiveCfg = Release|x64 {2B522528-9660-4B78-8E2A-000000000000}.Release|x64.Build.0 = Release|x64 + {2B522528-9660-4B78-8E2A-000000000000}.Release|x86.ActiveCfg = Release|x64 {8BAA30B8-F062-42F3-A89C-D9E5A0912957}.Debug|x64.ActiveCfg = Debug|x64 {8BAA30B8-F062-42F3-A89C-D9E5A0912957}.Debug|x64.Build.0 = Debug|x64 + {8BAA30B8-F062-42F3-A89C-D9E5A0912957}.Debug|x86.ActiveCfg = Debug|x64 {8BAA30B8-F062-42F3-A89C-D9E5A0912957}.Release|x64.ActiveCfg = Release|x64 {8BAA30B8-F062-42F3-A89C-D9E5A0912957}.Release|x64.Build.0 = Release|x64 + {8BAA30B8-F062-42F3-A89C-D9E5A0912957}.Release|x86.ActiveCfg = Release|x64 {6C5DBBEC-8688-4B68-9928-118BB3891FD5}.Debug|x64.ActiveCfg = Debug|x64 {6C5DBBEC-8688-4B68-9928-118BB3891FD5}.Debug|x64.Build.0 = Debug|x64 + {6C5DBBEC-8688-4B68-9928-118BB3891FD5}.Debug|x86.ActiveCfg = Debug|x64 {6C5DBBEC-8688-4B68-9928-118BB3891FD5}.Release|x64.ActiveCfg = Release|x64 {6C5DBBEC-8688-4B68-9928-118BB3891FD5}.Release|x64.Build.0 = Release|x64 + {6C5DBBEC-8688-4B68-9928-118BB3891FD5}.Release|x86.ActiveCfg = Release|x64 {7EEA4183-1A52-4DC7-8839-9BEF31B59F98}.Debug|x64.ActiveCfg = Debug|x64 {7EEA4183-1A52-4DC7-8839-9BEF31B59F98}.Debug|x64.Build.0 = Debug|x64 + {7EEA4183-1A52-4DC7-8839-9BEF31B59F98}.Debug|x86.ActiveCfg = Debug|x64 {7EEA4183-1A52-4DC7-8839-9BEF31B59F98}.Release|x64.ActiveCfg = Release|x64 {7EEA4183-1A52-4DC7-8839-9BEF31B59F98}.Release|x64.Build.0 = Release|x64 + {7EEA4183-1A52-4DC7-8839-9BEF31B59F98}.Release|x86.ActiveCfg = Release|x64 {7AF13550-B80D-11E2-9E96-0800200C9A66}.Debug|x64.ActiveCfg = Debug|x64 {7AF13550-B80D-11E2-9E96-0800200C9A66}.Debug|x64.Build.0 = Debug|x64 + {7AF13550-B80D-11E2-9E96-0800200C9A66}.Debug|x86.ActiveCfg = Debug|x64 {7AF13550-B80D-11E2-9E96-0800200C9A66}.Release|x64.ActiveCfg = Release|x64 {7AF13550-B80D-11E2-9E96-0800200C9A66}.Release|x64.Build.0 = Release|x64 + {7AF13550-B80D-11E2-9E96-0800200C9A66}.Release|x86.ActiveCfg = Release|x64 {B1058311-C336-4961-8F0D-9702B809FCF7}.Debug|x64.ActiveCfg = Debug|x64 {B1058311-C336-4961-8F0D-9702B809FCF7}.Debug|x64.Build.0 = Debug|x64 + {B1058311-C336-4961-8F0D-9702B809FCF7}.Debug|x86.ActiveCfg = Debug|x64 {B1058311-C336-4961-8F0D-9702B809FCF7}.Release|x64.ActiveCfg = Release|x64 {B1058311-C336-4961-8F0D-9702B809FCF7}.Release|x64.Build.0 = Release|x64 + {B1058311-C336-4961-8F0D-9702B809FCF7}.Release|x86.ActiveCfg = Release|x64 {C621764B-8634-4EE9-BC2F-436EE81202F0}.Debug|x64.ActiveCfg = Debug|x64 {C621764B-8634-4EE9-BC2F-436EE81202F0}.Debug|x64.Build.0 = Debug|x64 + {C621764B-8634-4EE9-BC2F-436EE81202F0}.Debug|x86.ActiveCfg = Debug|x64 {C621764B-8634-4EE9-BC2F-436EE81202F0}.Release|x64.ActiveCfg = Release|x64 {C621764B-8634-4EE9-BC2F-436EE81202F0}.Release|x64.Build.0 = Release|x64 + {C621764B-8634-4EE9-BC2F-436EE81202F0}.Release|x86.ActiveCfg = Release|x64 {4BB973FE-D080-4B86-A453-61CB32673787}.Debug|x64.ActiveCfg = Debug|x64 {4BB973FE-D080-4B86-A453-61CB32673787}.Debug|x64.Build.0 = Debug|x64 + {4BB973FE-D080-4B86-A453-61CB32673787}.Debug|x86.ActiveCfg = Debug|x64 {4BB973FE-D080-4B86-A453-61CB32673787}.Release|x64.ActiveCfg = Release|x64 {4BB973FE-D080-4B86-A453-61CB32673787}.Release|x64.Build.0 = Release|x64 + {4BB973FE-D080-4B86-A453-61CB32673787}.Release|x86.ActiveCfg = Release|x64 {A35BDDC1-60C9-4F86-9658-7D28928D4866}.Debug|x64.ActiveCfg = Debug|x64 {A35BDDC1-60C9-4F86-9658-7D28928D4866}.Debug|x64.Build.0 = Debug|x64 + {A35BDDC1-60C9-4F86-9658-7D28928D4866}.Debug|x86.ActiveCfg = Debug|x64 {A35BDDC1-60C9-4F86-9658-7D28928D4866}.Release|x64.ActiveCfg = Release|x64 {A35BDDC1-60C9-4F86-9658-7D28928D4866}.Release|x64.Build.0 = Release|x64 + {A35BDDC1-60C9-4F86-9658-7D28928D4866}.Release|x86.ActiveCfg = Release|x64 {22E07B2E-036A-46B8-BAF9-2C8F956A500E}.Debug|x64.ActiveCfg = Debug|x64 {22E07B2E-036A-46B8-BAF9-2C8F956A500E}.Debug|x64.Build.0 = Debug|x64 + {22E07B2E-036A-46B8-BAF9-2C8F956A500E}.Debug|x86.ActiveCfg = Debug|x64 {22E07B2E-036A-46B8-BAF9-2C8F956A500E}.Release|x64.ActiveCfg = Release|x64 {22E07B2E-036A-46B8-BAF9-2C8F956A500E}.Release|x64.Build.0 = Release|x64 + {22E07B2E-036A-46B8-BAF9-2C8F956A500E}.Release|x86.ActiveCfg = Release|x64 {E206DB83-E348-4E32-AA0F-B7E05E3D8F51}.Debug|x64.ActiveCfg = Debug|x64 {E206DB83-E348-4E32-AA0F-B7E05E3D8F51}.Debug|x64.Build.0 = Debug|x64 + {E206DB83-E348-4E32-AA0F-B7E05E3D8F51}.Debug|x86.ActiveCfg = Debug|x64 {E206DB83-E348-4E32-AA0F-B7E05E3D8F51}.Release|x64.ActiveCfg = Release|x64 {E206DB83-E348-4E32-AA0F-B7E05E3D8F51}.Release|x64.Build.0 = Release|x64 + {E206DB83-E348-4E32-AA0F-B7E05E3D8F51}.Release|x86.ActiveCfg = Release|x64 {0BD4B2B9-5FDA-4D1A-A7F8-B66C7C49FFF7}.Debug|x64.ActiveCfg = Debug|x64 {0BD4B2B9-5FDA-4D1A-A7F8-B66C7C49FFF7}.Debug|x64.Build.0 = Debug|x64 + {0BD4B2B9-5FDA-4D1A-A7F8-B66C7C49FFF7}.Debug|x86.ActiveCfg = Debug|x64 {0BD4B2B9-5FDA-4D1A-A7F8-B66C7C49FFF7}.Release|x64.ActiveCfg = Release|x64 {0BD4B2B9-5FDA-4D1A-A7F8-B66C7C49FFF7}.Release|x64.Build.0 = Release|x64 + {0BD4B2B9-5FDA-4D1A-A7F8-B66C7C49FFF7}.Release|x86.ActiveCfg = Release|x64 {823879D7-A915-4CEA-8365-09BA70FDAF2C}.Debug|x64.ActiveCfg = Debug|x64 {823879D7-A915-4CEA-8365-09BA70FDAF2C}.Debug|x64.Build.0 = Debug|x64 + {823879D7-A915-4CEA-8365-09BA70FDAF2C}.Debug|x86.ActiveCfg = Debug|x64 {823879D7-A915-4CEA-8365-09BA70FDAF2C}.Release|x64.ActiveCfg = Release|x64 {823879D7-A915-4CEA-8365-09BA70FDAF2C}.Release|x64.Build.0 = Release|x64 + {823879D7-A915-4CEA-8365-09BA70FDAF2C}.Release|x86.ActiveCfg = Release|x64 {E8FCC396-C0C5-4D1A-B6A7-A570FB948D24}.Debug|x64.ActiveCfg = Debug|x64 {E8FCC396-C0C5-4D1A-B6A7-A570FB948D24}.Debug|x64.Build.0 = Debug|x64 + {E8FCC396-C0C5-4D1A-B6A7-A570FB948D24}.Debug|x86.ActiveCfg = Debug|x64 {E8FCC396-C0C5-4D1A-B6A7-A570FB948D24}.Release|x64.ActiveCfg = Release|x64 {E8FCC396-C0C5-4D1A-B6A7-A570FB948D24}.Release|x64.Build.0 = Release|x64 + {E8FCC396-C0C5-4D1A-B6A7-A570FB948D24}.Release|x86.ActiveCfg = Release|x64 {5772F2FA-DBB1-4ABD-A94D-A320CB2E8FC9}.Debug|x64.ActiveCfg = Debug|x64 {5772F2FA-DBB1-4ABD-A94D-A320CB2E8FC9}.Debug|x64.Build.0 = Debug|x64 + {5772F2FA-DBB1-4ABD-A94D-A320CB2E8FC9}.Debug|x86.ActiveCfg = Debug|x64 {5772F2FA-DBB1-4ABD-A94D-A320CB2E8FC9}.Release|x64.ActiveCfg = Release|x64 {5772F2FA-DBB1-4ABD-A94D-A320CB2E8FC9}.Release|x64.Build.0 = Release|x64 + {5772F2FA-DBB1-4ABD-A94D-A320CB2E8FC9}.Release|x86.ActiveCfg = Release|x64 {FFF4637C-DD40-4E3F-BDAF-6D0C102CBC7D}.Debug|x64.ActiveCfg = Debug|x64 {FFF4637C-DD40-4E3F-BDAF-6D0C102CBC7D}.Debug|x64.Build.0 = Debug|x64 + {FFF4637C-DD40-4E3F-BDAF-6D0C102CBC7D}.Debug|x86.ActiveCfg = Debug|x64 {FFF4637C-DD40-4E3F-BDAF-6D0C102CBC7D}.Release|x64.ActiveCfg = Release|x64 {FFF4637C-DD40-4E3F-BDAF-6D0C102CBC7D}.Release|x64.Build.0 = Release|x64 + {FFF4637C-DD40-4E3F-BDAF-6D0C102CBC7D}.Release|x86.ActiveCfg = Release|x64 {FEC6A7E8-6DB2-4DB0-9DB4-B568D0C1E521}.Debug|x64.ActiveCfg = Debug|x64 {FEC6A7E8-6DB2-4DB0-9DB4-B568D0C1E521}.Debug|x64.Build.0 = Debug|x64 + {FEC6A7E8-6DB2-4DB0-9DB4-B568D0C1E521}.Debug|x86.ActiveCfg = Debug|x64 {FEC6A7E8-6DB2-4DB0-9DB4-B568D0C1E521}.Release|x64.ActiveCfg = Release|x64 {FEC6A7E8-6DB2-4DB0-9DB4-B568D0C1E521}.Release|x64.Build.0 = Release|x64 + {FEC6A7E8-6DB2-4DB0-9DB4-B568D0C1E521}.Release|x86.ActiveCfg = Release|x64 {22B180B5-9E3C-455A-80D8-00387EE0A274}.Debug|x64.ActiveCfg = Debug|x64 {22B180B5-9E3C-455A-80D8-00387EE0A274}.Debug|x64.Build.0 = Debug|x64 + {22B180B5-9E3C-455A-80D8-00387EE0A274}.Debug|x86.ActiveCfg = Debug|x64 {22B180B5-9E3C-455A-80D8-00387EE0A274}.Release|x64.ActiveCfg = Release|x64 {22B180B5-9E3C-455A-80D8-00387EE0A274}.Release|x64.Build.0 = Release|x64 + {22B180B5-9E3C-455A-80D8-00387EE0A274}.Release|x86.ActiveCfg = Release|x64 {C613608D-C4D0-4FC2-AA44-9D0098AAFDD8}.Debug|x64.ActiveCfg = Debug|x64 {C613608D-C4D0-4FC2-AA44-9D0098AAFDD8}.Debug|x64.Build.0 = Debug|x64 + {C613608D-C4D0-4FC2-AA44-9D0098AAFDD8}.Debug|x86.ActiveCfg = Debug|x64 {C613608D-C4D0-4FC2-AA44-9D0098AAFDD8}.Release|x64.ActiveCfg = Release|x64 {C613608D-C4D0-4FC2-AA44-9D0098AAFDD8}.Release|x64.Build.0 = Release|x64 + {C613608D-C4D0-4FC2-AA44-9D0098AAFDD8}.Release|x86.ActiveCfg = Release|x64 {5D42D76C-B076-4045-A03E-1DF101583C39}.Debug|x64.ActiveCfg = Debug|x64 {5D42D76C-B076-4045-A03E-1DF101583C39}.Debug|x64.Build.0 = Debug|x64 + {5D42D76C-B076-4045-A03E-1DF101583C39}.Debug|x86.ActiveCfg = Debug|x64 {5D42D76C-B076-4045-A03E-1DF101583C39}.Release|x64.ActiveCfg = Release|x64 {5D42D76C-B076-4045-A03E-1DF101583C39}.Release|x64.Build.0 = Release|x64 + {5D42D76C-B076-4045-A03E-1DF101583C39}.Release|x86.ActiveCfg = Release|x64 {992E35D6-47AF-44DC-8A59-1F369BF1836C}.Debug|x64.ActiveCfg = Debug|x64 {992E35D6-47AF-44DC-8A59-1F369BF1836C}.Debug|x64.Build.0 = Debug|x64 + {992E35D6-47AF-44DC-8A59-1F369BF1836C}.Debug|x86.ActiveCfg = Debug|x64 {992E35D6-47AF-44DC-8A59-1F369BF1836C}.Release|x64.ActiveCfg = Release|x64 {992E35D6-47AF-44DC-8A59-1F369BF1836C}.Release|x64.Build.0 = Release|x64 + {992E35D6-47AF-44DC-8A59-1F369BF1836C}.Release|x86.ActiveCfg = Release|x64 {3366ED2E-3283-4F30-9F77-55391129489C}.Debug|x64.ActiveCfg = Debug|x64 {3366ED2E-3283-4F30-9F77-55391129489C}.Debug|x64.Build.0 = Debug|x64 + {3366ED2E-3283-4F30-9F77-55391129489C}.Debug|x86.ActiveCfg = Debug|x64 {3366ED2E-3283-4F30-9F77-55391129489C}.Release|x64.ActiveCfg = Release|x64 {3366ED2E-3283-4F30-9F77-55391129489C}.Release|x64.Build.0 = Release|x64 + {3366ED2E-3283-4F30-9F77-55391129489C}.Release|x86.ActiveCfg = Release|x64 {FDEFF75C-41F4-45CA-A704-05A46D9A2FAB}.Debug|x64.ActiveCfg = Debug|x64 {FDEFF75C-41F4-45CA-A704-05A46D9A2FAB}.Debug|x64.Build.0 = Debug|x64 + {FDEFF75C-41F4-45CA-A704-05A46D9A2FAB}.Debug|x86.ActiveCfg = Debug|x64 {FDEFF75C-41F4-45CA-A704-05A46D9A2FAB}.Release|x64.ActiveCfg = Release|x64 {FDEFF75C-41F4-45CA-A704-05A46D9A2FAB}.Release|x64.Build.0 = Release|x64 + {FDEFF75C-41F4-45CA-A704-05A46D9A2FAB}.Release|x86.ActiveCfg = Release|x64 {A25A294D-A3AC-4349-8EA6-24A83C5CD0B7}.Debug|x64.ActiveCfg = Debug|x64 {A25A294D-A3AC-4349-8EA6-24A83C5CD0B7}.Debug|x64.Build.0 = Debug|x64 + {A25A294D-A3AC-4349-8EA6-24A83C5CD0B7}.Debug|x86.ActiveCfg = Debug|x64 {A25A294D-A3AC-4349-8EA6-24A83C5CD0B7}.Release|x64.ActiveCfg = Release|x64 {A25A294D-A3AC-4349-8EA6-24A83C5CD0B7}.Release|x64.Build.0 = Release|x64 + {A25A294D-A3AC-4349-8EA6-24A83C5CD0B7}.Release|x86.ActiveCfg = Release|x64 {8D1E58FA-AD04-43A1-8BDF-0457103F8DE1}.Debug|x64.ActiveCfg = Debug|x64 {8D1E58FA-AD04-43A1-8BDF-0457103F8DE1}.Debug|x64.Build.0 = Debug|x64 + {8D1E58FA-AD04-43A1-8BDF-0457103F8DE1}.Debug|x86.ActiveCfg = Debug|x64 {8D1E58FA-AD04-43A1-8BDF-0457103F8DE1}.Release|x64.ActiveCfg = Release|x64 {8D1E58FA-AD04-43A1-8BDF-0457103F8DE1}.Release|x64.Build.0 = Release|x64 + {8D1E58FA-AD04-43A1-8BDF-0457103F8DE1}.Release|x86.ActiveCfg = Release|x64 {B06A629B-7EAA-4265-A4C0-8A0EC03B00B0}.Debug|x64.ActiveCfg = Debug|x64 {B06A629B-7EAA-4265-A4C0-8A0EC03B00B0}.Debug|x64.Build.0 = Debug|x64 + {B06A629B-7EAA-4265-A4C0-8A0EC03B00B0}.Debug|x86.ActiveCfg = Debug|x64 {B06A629B-7EAA-4265-A4C0-8A0EC03B00B0}.Release|x64.ActiveCfg = Release|x64 {B06A629B-7EAA-4265-A4C0-8A0EC03B00B0}.Release|x64.Build.0 = Release|x64 + {B06A629B-7EAA-4265-A4C0-8A0EC03B00B0}.Release|x86.ActiveCfg = Release|x64 {A41C689F-E343-4BDA-8132-FE2ADB166249}.Debug|x64.ActiveCfg = Debug|x64 {A41C689F-E343-4BDA-8132-FE2ADB166249}.Debug|x64.Build.0 = Debug|x64 + {A41C689F-E343-4BDA-8132-FE2ADB166249}.Debug|x86.ActiveCfg = Debug|x64 {A41C689F-E343-4BDA-8132-FE2ADB166249}.Release|x64.ActiveCfg = Release|x64 {A41C689F-E343-4BDA-8132-FE2ADB166249}.Release|x64.Build.0 = Release|x64 + {A41C689F-E343-4BDA-8132-FE2ADB166249}.Release|x86.ActiveCfg = Release|x64 {2C362B1C-80C9-4867-AD96-2482535190D1}.Debug|x64.ActiveCfg = Debug|x64 {2C362B1C-80C9-4867-AD96-2482535190D1}.Debug|x64.Build.0 = Debug|x64 + {2C362B1C-80C9-4867-AD96-2482535190D1}.Debug|x86.ActiveCfg = Debug|x64 {2C362B1C-80C9-4867-AD96-2482535190D1}.Release|x64.ActiveCfg = Release|x64 {2C362B1C-80C9-4867-AD96-2482535190D1}.Release|x64.Build.0 = Release|x64 + {2C362B1C-80C9-4867-AD96-2482535190D1}.Release|x86.ActiveCfg = Release|x64 {7C767DFF-87E6-4FE0-9557-2FF656ED1E57}.Debug|x64.ActiveCfg = Debug|x64 {7C767DFF-87E6-4FE0-9557-2FF656ED1E57}.Debug|x64.Build.0 = Debug|x64 + {7C767DFF-87E6-4FE0-9557-2FF656ED1E57}.Debug|x86.ActiveCfg = Debug|x64 {7C767DFF-87E6-4FE0-9557-2FF656ED1E57}.Release|x64.ActiveCfg = Release|x64 {7C767DFF-87E6-4FE0-9557-2FF656ED1E57}.Release|x64.Build.0 = Release|x64 + {7C767DFF-87E6-4FE0-9557-2FF656ED1E57}.Release|x86.ActiveCfg = Release|x64 {98510FB2-2BD3-4A6D-9BB0-48EB5FD4C445}.Debug|x64.ActiveCfg = Debug|x64 {98510FB2-2BD3-4A6D-9BB0-48EB5FD4C445}.Debug|x64.Build.0 = Debug|x64 + {98510FB2-2BD3-4A6D-9BB0-48EB5FD4C445}.Debug|x86.ActiveCfg = Debug|x64 {98510FB2-2BD3-4A6D-9BB0-48EB5FD4C445}.Release|x64.ActiveCfg = Release|x64 {98510FB2-2BD3-4A6D-9BB0-48EB5FD4C445}.Release|x64.Build.0 = Release|x64 + {98510FB2-2BD3-4A6D-9BB0-48EB5FD4C445}.Release|x86.ActiveCfg = Release|x64 {F61EE788-E229-43F1-9E05-DAE2AB60B882}.Debug|x64.ActiveCfg = Debug|x64 {F61EE788-E229-43F1-9E05-DAE2AB60B882}.Debug|x64.Build.0 = Debug|x64 + {F61EE788-E229-43F1-9E05-DAE2AB60B882}.Debug|x86.ActiveCfg = Debug|x64 {F61EE788-E229-43F1-9E05-DAE2AB60B882}.Release|x64.ActiveCfg = Release|x64 {F61EE788-E229-43F1-9E05-DAE2AB60B882}.Release|x64.Build.0 = Release|x64 + {F61EE788-E229-43F1-9E05-DAE2AB60B882}.Release|x86.ActiveCfg = Release|x64 {41F88027-BC30-4949-B407-42C2BCB34C60}.Debug|x64.ActiveCfg = Debug|x64 {41F88027-BC30-4949-B407-42C2BCB34C60}.Debug|x64.Build.0 = Debug|x64 + {41F88027-BC30-4949-B407-42C2BCB34C60}.Debug|x86.ActiveCfg = Debug|x64 {41F88027-BC30-4949-B407-42C2BCB34C60}.Release|x64.ActiveCfg = Release|x64 {41F88027-BC30-4949-B407-42C2BCB34C60}.Release|x64.Build.0 = Release|x64 + {41F88027-BC30-4949-B407-42C2BCB34C60}.Release|x86.ActiveCfg = Release|x64 {D557C94D-4D26-48D0-B522-B314DC28800D}.Debug|x64.ActiveCfg = Debug|x64 {D557C94D-4D26-48D0-B522-B314DC28800D}.Debug|x64.Build.0 = Debug|x64 + {D557C94D-4D26-48D0-B522-B314DC28800D}.Debug|x86.ActiveCfg = Debug|x64 {D557C94D-4D26-48D0-B522-B314DC28800D}.Release|x64.ActiveCfg = Release|x64 {D557C94D-4D26-48D0-B522-B314DC28800D}.Release|x64.Build.0 = Release|x64 + {D557C94D-4D26-48D0-B522-B314DC28800D}.Release|x86.ActiveCfg = Release|x64 {36651F00-C01D-461B-A8BA-BE4826D7130E}.Debug|x64.ActiveCfg = Debug|x64 {36651F00-C01D-461B-A8BA-BE4826D7130E}.Debug|x64.Build.0 = Debug|x64 + {36651F00-C01D-461B-A8BA-BE4826D7130E}.Debug|x86.ActiveCfg = Debug|x64 {36651F00-C01D-461B-A8BA-BE4826D7130E}.Release|x64.ActiveCfg = Release|x64 {36651F00-C01D-461B-A8BA-BE4826D7130E}.Release|x64.Build.0 = Release|x64 + {36651F00-C01D-461B-A8BA-BE4826D7130E}.Release|x86.ActiveCfg = Release|x64 {A356B8C0-0897-4BAA-9DD1-8D210988155C}.Debug|x64.ActiveCfg = Debug|x64 {A356B8C0-0897-4BAA-9DD1-8D210988155C}.Debug|x64.Build.0 = Debug|x64 + {A356B8C0-0897-4BAA-9DD1-8D210988155C}.Debug|x86.ActiveCfg = Debug|x64 {A356B8C0-0897-4BAA-9DD1-8D210988155C}.Release|x64.ActiveCfg = Release|x64 {A356B8C0-0897-4BAA-9DD1-8D210988155C}.Release|x64.Build.0 = Release|x64 + {A356B8C0-0897-4BAA-9DD1-8D210988155C}.Release|x86.ActiveCfg = Release|x64 {86BB3621-D40C-4731-8838-842A925E0615}.Debug|x64.ActiveCfg = Debug|x64 {86BB3621-D40C-4731-8838-842A925E0615}.Debug|x64.Build.0 = Debug|x64 + {86BB3621-D40C-4731-8838-842A925E0615}.Debug|x86.ActiveCfg = Debug|x64 {86BB3621-D40C-4731-8838-842A925E0615}.Release|x64.ActiveCfg = Release|x64 {86BB3621-D40C-4731-8838-842A925E0615}.Release|x64.Build.0 = Release|x64 + {86BB3621-D40C-4731-8838-842A925E0615}.Release|x86.ActiveCfg = Release|x64 {2A68B908-9C46-4D8D-A5AA-662787137BB0}.Debug|x64.ActiveCfg = Debug|x64 {2A68B908-9C46-4D8D-A5AA-662787137BB0}.Debug|x64.Build.0 = Debug|x64 + {2A68B908-9C46-4D8D-A5AA-662787137BB0}.Debug|x86.ActiveCfg = Debug|x64 {2A68B908-9C46-4D8D-A5AA-662787137BB0}.Release|x64.ActiveCfg = Release|x64 {2A68B908-9C46-4D8D-A5AA-662787137BB0}.Release|x64.Build.0 = Release|x64 + {2A68B908-9C46-4D8D-A5AA-662787137BB0}.Release|x86.ActiveCfg = Release|x64 {91CCB72B-852D-47D8-82FA-0E19A300A125}.Debug|x64.ActiveCfg = Debug|x64 {91CCB72B-852D-47D8-82FA-0E19A300A125}.Debug|x64.Build.0 = Debug|x64 + {91CCB72B-852D-47D8-82FA-0E19A300A125}.Debug|x86.ActiveCfg = Debug|x64 {91CCB72B-852D-47D8-82FA-0E19A300A125}.Release|x64.ActiveCfg = Release|x64 {91CCB72B-852D-47D8-82FA-0E19A300A125}.Release|x64.Build.0 = Release|x64 + {91CCB72B-852D-47D8-82FA-0E19A300A125}.Release|x86.ActiveCfg = Release|x64 {1995D553-BABB-4987-A0AC-84496297DDE5}.Debug|x64.ActiveCfg = Debug|x64 {1995D553-BABB-4987-A0AC-84496297DDE5}.Debug|x64.Build.0 = Debug|x64 + {1995D553-BABB-4987-A0AC-84496297DDE5}.Debug|x86.ActiveCfg = Debug|x64 {1995D553-BABB-4987-A0AC-84496297DDE5}.Release|x64.ActiveCfg = Release|x64 {1995D553-BABB-4987-A0AC-84496297DDE5}.Release|x64.Build.0 = Release|x64 + {1995D553-BABB-4987-A0AC-84496297DDE5}.Release|x86.ActiveCfg = Release|x64 {4FFC5AC3-7A59-40C2-B155-AA3EA5D391AE}.Debug|x64.ActiveCfg = Debug|x64 {4FFC5AC3-7A59-40C2-B155-AA3EA5D391AE}.Debug|x64.Build.0 = Debug|x64 + {4FFC5AC3-7A59-40C2-B155-AA3EA5D391AE}.Debug|x86.ActiveCfg = Debug|x64 {4FFC5AC3-7A59-40C2-B155-AA3EA5D391AE}.Release|x64.ActiveCfg = Release|x64 {4FFC5AC3-7A59-40C2-B155-AA3EA5D391AE}.Release|x64.Build.0 = Release|x64 + {4FFC5AC3-7A59-40C2-B155-AA3EA5D391AE}.Release|x86.ActiveCfg = Release|x64 {AFC831A1-1946-43A5-BCA9-1B4618703C20}.Debug|x64.ActiveCfg = Debug|x64 {AFC831A1-1946-43A5-BCA9-1B4618703C20}.Debug|x64.Build.0 = Debug|x64 + {AFC831A1-1946-43A5-BCA9-1B4618703C20}.Debug|x86.ActiveCfg = Debug|x64 {AFC831A1-1946-43A5-BCA9-1B4618703C20}.Release|x64.ActiveCfg = Release|x64 {AFC831A1-1946-43A5-BCA9-1B4618703C20}.Release|x64.Build.0 = Release|x64 + {AFC831A1-1946-43A5-BCA9-1B4618703C20}.Release|x86.ActiveCfg = Release|x64 {70ADDFBB-1216-412E-B326-51A146C3B8A4}.Debug|x64.ActiveCfg = Debug|x64 {70ADDFBB-1216-412E-B326-51A146C3B8A4}.Debug|x64.Build.0 = Debug|x64 + {70ADDFBB-1216-412E-B326-51A146C3B8A4}.Debug|x86.ActiveCfg = Debug|x64 {70ADDFBB-1216-412E-B326-51A146C3B8A4}.Release|x64.ActiveCfg = Release|x64 {70ADDFBB-1216-412E-B326-51A146C3B8A4}.Release|x64.Build.0 = Release|x64 + {70ADDFBB-1216-412E-B326-51A146C3B8A4}.Release|x86.ActiveCfg = Release|x64 {349B37AE-AB50-410E-A445-6C4A29522434}.Debug|x64.ActiveCfg = Debug|x64 {349B37AE-AB50-410E-A445-6C4A29522434}.Debug|x64.Build.0 = Debug|x64 + {349B37AE-AB50-410E-A445-6C4A29522434}.Debug|x86.ActiveCfg = Debug|x64 {349B37AE-AB50-410E-A445-6C4A29522434}.Release|x64.ActiveCfg = Release|x64 {349B37AE-AB50-410E-A445-6C4A29522434}.Release|x64.Build.0 = Release|x64 + {349B37AE-AB50-410E-A445-6C4A29522434}.Release|x86.ActiveCfg = Release|x64 {6D793C18-411A-4D31-B186-636817767C86}.Debug|x64.ActiveCfg = Debug|x64 {6D793C18-411A-4D31-B186-636817767C86}.Debug|x64.Build.0 = Debug|x64 + {6D793C18-411A-4D31-B186-636817767C86}.Debug|x86.ActiveCfg = Debug|x64 {6D793C18-411A-4D31-B186-636817767C86}.Release|x64.ActiveCfg = Release|x64 {6D793C18-411A-4D31-B186-636817767C86}.Release|x64.Build.0 = Release|x64 + {6D793C18-411A-4D31-B186-636817767C86}.Release|x86.ActiveCfg = Release|x64 {849D7396-9AD9-4C0E-A0FD-F6126FBA2CAB}.Debug|x64.ActiveCfg = Debug|x64 {849D7396-9AD9-4C0E-A0FD-F6126FBA2CAB}.Debug|x64.Build.0 = Debug|x64 + {849D7396-9AD9-4C0E-A0FD-F6126FBA2CAB}.Debug|x86.ActiveCfg = Debug|x64 {849D7396-9AD9-4C0E-A0FD-F6126FBA2CAB}.Release|x64.ActiveCfg = Release|x64 {849D7396-9AD9-4C0E-A0FD-F6126FBA2CAB}.Release|x64.Build.0 = Release|x64 + {849D7396-9AD9-4C0E-A0FD-F6126FBA2CAB}.Release|x86.ActiveCfg = Release|x64 {2D7CE355-8831-40EE-9B43-E60866A45D7D}.Debug|x64.ActiveCfg = Debug|x64 {2D7CE355-8831-40EE-9B43-E60866A45D7D}.Debug|x64.Build.0 = Debug|x64 + {2D7CE355-8831-40EE-9B43-E60866A45D7D}.Debug|x86.ActiveCfg = Debug|x64 {2D7CE355-8831-40EE-9B43-E60866A45D7D}.Release|x64.ActiveCfg = Release|x64 {2D7CE355-8831-40EE-9B43-E60866A45D7D}.Release|x64.Build.0 = Release|x64 + {2D7CE355-8831-40EE-9B43-E60866A45D7D}.Release|x86.ActiveCfg = Release|x64 {8F66F808-6E8C-46D5-B9D1-DE50811030D4}.Debug|x64.ActiveCfg = Debug|x64 {8F66F808-6E8C-46D5-B9D1-DE50811030D4}.Debug|x64.Build.0 = Debug|x64 + {8F66F808-6E8C-46D5-B9D1-DE50811030D4}.Debug|x86.ActiveCfg = Debug|x64 {8F66F808-6E8C-46D5-B9D1-DE50811030D4}.Release|x64.ActiveCfg = Release|x64 {8F66F808-6E8C-46D5-B9D1-DE50811030D4}.Release|x64.Build.0 = Release|x64 + {8F66F808-6E8C-46D5-B9D1-DE50811030D4}.Release|x86.ActiveCfg = Release|x64 {49E0167C-FF14-42F2-BEB4-EDBA568640A5}.Debug|x64.ActiveCfg = Debug|x64 {49E0167C-FF14-42F2-BEB4-EDBA568640A5}.Debug|x64.Build.0 = Debug|x64 + {49E0167C-FF14-42F2-BEB4-EDBA568640A5}.Debug|x86.ActiveCfg = Debug|x64 {49E0167C-FF14-42F2-BEB4-EDBA568640A5}.Release|x64.ActiveCfg = Release|x64 {49E0167C-FF14-42F2-BEB4-EDBA568640A5}.Release|x64.Build.0 = Release|x64 + {49E0167C-FF14-42F2-BEB4-EDBA568640A5}.Release|x86.ActiveCfg = Release|x64 {23F9BA69-3FBF-419F-AD60-F06E1AC0706B}.Debug|x64.ActiveCfg = Debug|x64 {23F9BA69-3FBF-419F-AD60-F06E1AC0706B}.Debug|x64.Build.0 = Debug|x64 + {23F9BA69-3FBF-419F-AD60-F06E1AC0706B}.Debug|x86.ActiveCfg = Debug|x64 {23F9BA69-3FBF-419F-AD60-F06E1AC0706B}.Release|x64.ActiveCfg = Release|x64 {23F9BA69-3FBF-419F-AD60-F06E1AC0706B}.Release|x64.Build.0 = Release|x64 + {23F9BA69-3FBF-419F-AD60-F06E1AC0706B}.Release|x86.ActiveCfg = Release|x64 {7BAC0C19-0280-4CB2-A8FA-A6BF7C2A025E}.Debug|x64.ActiveCfg = Debug|x64 {7BAC0C19-0280-4CB2-A8FA-A6BF7C2A025E}.Debug|x64.Build.0 = Debug|x64 + {7BAC0C19-0280-4CB2-A8FA-A6BF7C2A025E}.Debug|x86.ActiveCfg = Debug|x64 {7BAC0C19-0280-4CB2-A8FA-A6BF7C2A025E}.Release|x64.ActiveCfg = Release|x64 {7BAC0C19-0280-4CB2-A8FA-A6BF7C2A025E}.Release|x64.Build.0 = Release|x64 + {7BAC0C19-0280-4CB2-A8FA-A6BF7C2A025E}.Release|x86.ActiveCfg = Release|x64 {537D636C-29BB-4B81-B020-D8314ECA88CC}.Debug|x64.ActiveCfg = Debug|x64 {537D636C-29BB-4B81-B020-D8314ECA88CC}.Debug|x64.Build.0 = Debug|x64 + {537D636C-29BB-4B81-B020-D8314ECA88CC}.Debug|x86.ActiveCfg = Debug|x64 {537D636C-29BB-4B81-B020-D8314ECA88CC}.Release|x64.ActiveCfg = Release|x64 {537D636C-29BB-4B81-B020-D8314ECA88CC}.Release|x64.Build.0 = Release|x64 + {537D636C-29BB-4B81-B020-D8314ECA88CC}.Release|x86.ActiveCfg = Release|x64 {971C3730-9CE9-453B-BB79-F792B2E2C017}.Debug|x64.ActiveCfg = Debug|x64 {971C3730-9CE9-453B-BB79-F792B2E2C017}.Debug|x64.Build.0 = Debug|x64 + {971C3730-9CE9-453B-BB79-F792B2E2C017}.Debug|x86.ActiveCfg = Debug|x64 {971C3730-9CE9-453B-BB79-F792B2E2C017}.Release|x64.ActiveCfg = Release|x64 {971C3730-9CE9-453B-BB79-F792B2E2C017}.Release|x64.Build.0 = Release|x64 + {971C3730-9CE9-453B-BB79-F792B2E2C017}.Release|x86.ActiveCfg = Release|x64 {32B89825-3A2D-47D6-A2FC-5792A25E5420}.Debug|x64.ActiveCfg = Debug|x64 {32B89825-3A2D-47D6-A2FC-5792A25E5420}.Debug|x64.Build.0 = Debug|x64 + {32B89825-3A2D-47D6-A2FC-5792A25E5420}.Debug|x86.ActiveCfg = Debug|x64 {32B89825-3A2D-47D6-A2FC-5792A25E5420}.Release|x64.ActiveCfg = Release|x64 {32B89825-3A2D-47D6-A2FC-5792A25E5420}.Release|x64.Build.0 = Release|x64 + {32B89825-3A2D-47D6-A2FC-5792A25E5420}.Release|x86.ActiveCfg = Release|x64 {B305430A-2CB0-4057-AB7C-F90F52973BC1}.Debug|x64.ActiveCfg = Debug|x64 {B305430A-2CB0-4057-AB7C-F90F52973BC1}.Debug|x64.Build.0 = Debug|x64 + {B305430A-2CB0-4057-AB7C-F90F52973BC1}.Debug|x86.ActiveCfg = Debug|x64 {B305430A-2CB0-4057-AB7C-F90F52973BC1}.Release|x64.ActiveCfg = Release|x64 {B305430A-2CB0-4057-AB7C-F90F52973BC1}.Release|x64.Build.0 = Release|x64 + {B305430A-2CB0-4057-AB7C-F90F52973BC1}.Release|x86.ActiveCfg = Release|x64 {16385D50-05B1-454D-9F0B-AC2C9D6BD9AB}.Debug|x64.ActiveCfg = Debug|x64 {16385D50-05B1-454D-9F0B-AC2C9D6BD9AB}.Debug|x64.Build.0 = Debug|x64 + {16385D50-05B1-454D-9F0B-AC2C9D6BD9AB}.Debug|x86.ActiveCfg = Debug|x64 {16385D50-05B1-454D-9F0B-AC2C9D6BD9AB}.Release|x64.ActiveCfg = Release|x64 {16385D50-05B1-454D-9F0B-AC2C9D6BD9AB}.Release|x64.Build.0 = Release|x64 + {16385D50-05B1-454D-9F0B-AC2C9D6BD9AB}.Release|x86.ActiveCfg = Release|x64 {B8C95F39-54BF-40A9-807B-598DF2821D55}.Debug|x64.ActiveCfg = Debug|x64 {B8C95F39-54BF-40A9-807B-598DF2821D55}.Debug|x64.Build.0 = Debug|x64 + {B8C95F39-54BF-40A9-807B-598DF2821D55}.Debug|x86.ActiveCfg = Debug|x64 {B8C95F39-54BF-40A9-807B-598DF2821D55}.Release|x64.ActiveCfg = Release|x64 {B8C95F39-54BF-40A9-807B-598DF2821D55}.Release|x64.Build.0 = Release|x64 + {B8C95F39-54BF-40A9-807B-598DF2821D55}.Release|x86.ActiveCfg = Release|x64 {AF3143A4-5529-4C78-A01A-9F2A8977ED64}.Debug|x64.ActiveCfg = Debug|x64 {AF3143A4-5529-4C78-A01A-9F2A8977ED64}.Debug|x64.Build.0 = Debug|x64 + {AF3143A4-5529-4C78-A01A-9F2A8977ED64}.Debug|x86.ActiveCfg = Debug|x64 {AF3143A4-5529-4C78-A01A-9F2A8977ED64}.Release|x64.ActiveCfg = Release|x64 {AF3143A4-5529-4C78-A01A-9F2A8977ED64}.Release|x64.Build.0 = Release|x64 + {AF3143A4-5529-4C78-A01A-9F2A8977ED64}.Release|x86.ActiveCfg = Release|x64 {E1EEF63F-5DE9-46DD-9291-9D356F02CFD1}.Debug|x64.ActiveCfg = Debug|x64 {E1EEF63F-5DE9-46DD-9291-9D356F02CFD1}.Debug|x64.Build.0 = Debug|x64 + {E1EEF63F-5DE9-46DD-9291-9D356F02CFD1}.Debug|x86.ActiveCfg = Debug|x64 {E1EEF63F-5DE9-46DD-9291-9D356F02CFD1}.Release|x64.ActiveCfg = Release|x64 {E1EEF63F-5DE9-46DD-9291-9D356F02CFD1}.Release|x64.Build.0 = Release|x64 + {E1EEF63F-5DE9-46DD-9291-9D356F02CFD1}.Release|x86.ActiveCfg = Release|x64 {2AC2C860-BA44-452A-A801-BAAB7A51C0AC}.Debug|x64.ActiveCfg = Debug|x64 {2AC2C860-BA44-452A-A801-BAAB7A51C0AC}.Debug|x64.Build.0 = Debug|x64 + {2AC2C860-BA44-452A-A801-BAAB7A51C0AC}.Debug|x86.ActiveCfg = Debug|x64 {2AC2C860-BA44-452A-A801-BAAB7A51C0AC}.Release|x64.ActiveCfg = Release|x64 {2AC2C860-BA44-452A-A801-BAAB7A51C0AC}.Release|x64.Build.0 = Release|x64 + {2AC2C860-BA44-452A-A801-BAAB7A51C0AC}.Release|x86.ActiveCfg = Release|x64 {8FDA6D04-7C5D-4061-8467-AE0C104A2C83}.Debug|x64.ActiveCfg = Debug|x64 {8FDA6D04-7C5D-4061-8467-AE0C104A2C83}.Debug|x64.Build.0 = Debug|x64 + {8FDA6D04-7C5D-4061-8467-AE0C104A2C83}.Debug|x86.ActiveCfg = Debug|x64 {8FDA6D04-7C5D-4061-8467-AE0C104A2C83}.Release|x64.ActiveCfg = Release|x64 {8FDA6D04-7C5D-4061-8467-AE0C104A2C83}.Release|x64.Build.0 = Release|x64 + {8FDA6D04-7C5D-4061-8467-AE0C104A2C83}.Release|x86.ActiveCfg = Release|x64 {3AD2E831-7A16-4097-ACE1-4210A7F08751}.Debug|x64.ActiveCfg = Debug|x64 {3AD2E831-7A16-4097-ACE1-4210A7F08751}.Debug|x64.Build.0 = Debug|x64 + {3AD2E831-7A16-4097-ACE1-4210A7F08751}.Debug|x86.ActiveCfg = Debug|x64 {3AD2E831-7A16-4097-ACE1-4210A7F08751}.Release|x64.ActiveCfg = Release|x64 {3AD2E831-7A16-4097-ACE1-4210A7F08751}.Release|x64.Build.0 = Release|x64 + {3AD2E831-7A16-4097-ACE1-4210A7F08751}.Release|x86.ActiveCfg = Release|x64 {DC09C991-D279-4A37-9332-5F7767621D5C}.Debug|x64.ActiveCfg = Debug|x64 {DC09C991-D279-4A37-9332-5F7767621D5C}.Debug|x64.Build.0 = Debug|x64 + {DC09C991-D279-4A37-9332-5F7767621D5C}.Debug|x86.ActiveCfg = Debug|x64 {DC09C991-D279-4A37-9332-5F7767621D5C}.Release|x64.ActiveCfg = Release|x64 {DC09C991-D279-4A37-9332-5F7767621D5C}.Release|x64.Build.0 = Release|x64 + {DC09C991-D279-4A37-9332-5F7767621D5C}.Release|x86.ActiveCfg = Release|x64 {9953A884-2927-4E91-B414-FC773102FE6D}.Debug|x64.ActiveCfg = Debug|x64 {9953A884-2927-4E91-B414-FC773102FE6D}.Debug|x64.Build.0 = Debug|x64 + {9953A884-2927-4E91-B414-FC773102FE6D}.Debug|x86.ActiveCfg = Debug|x64 {9953A884-2927-4E91-B414-FC773102FE6D}.Release|x64.ActiveCfg = Release|x64 {9953A884-2927-4E91-B414-FC773102FE6D}.Release|x64.Build.0 = Release|x64 + {9953A884-2927-4E91-B414-FC773102FE6D}.Release|x86.ActiveCfg = Release|x64 {36B74175-48F3-4157-BE5D-84C88DAF40D9}.Debug|x64.ActiveCfg = Debug|x64 {36B74175-48F3-4157-BE5D-84C88DAF40D9}.Debug|x64.Build.0 = Debug|x64 + {36B74175-48F3-4157-BE5D-84C88DAF40D9}.Debug|x86.ActiveCfg = Debug|x64 {36B74175-48F3-4157-BE5D-84C88DAF40D9}.Release|x64.ActiveCfg = Release|x64 {36B74175-48F3-4157-BE5D-84C88DAF40D9}.Release|x64.Build.0 = Release|x64 + {36B74175-48F3-4157-BE5D-84C88DAF40D9}.Release|x86.ActiveCfg = Release|x64 {788C63FE-C4C5-442C-9F56-A9326118D9BA}.Debug|x64.ActiveCfg = Debug|x64 {788C63FE-C4C5-442C-9F56-A9326118D9BA}.Debug|x64.Build.0 = Debug|x64 + {788C63FE-C4C5-442C-9F56-A9326118D9BA}.Debug|x86.ActiveCfg = Debug|x64 {788C63FE-C4C5-442C-9F56-A9326118D9BA}.Release|x64.ActiveCfg = Release|x64 {788C63FE-C4C5-442C-9F56-A9326118D9BA}.Release|x64.Build.0 = Release|x64 + {788C63FE-C4C5-442C-9F56-A9326118D9BA}.Release|x86.ActiveCfg = Release|x64 {FB87D337-AFA5-4DA8-8D99-84461B363108}.Debug|x64.ActiveCfg = Debug|x64 {FB87D337-AFA5-4DA8-8D99-84461B363108}.Debug|x64.Build.0 = Debug|x64 + {FB87D337-AFA5-4DA8-8D99-84461B363108}.Debug|x86.ActiveCfg = Debug|x64 {FB87D337-AFA5-4DA8-8D99-84461B363108}.Release|x64.ActiveCfg = Release|x64 {FB87D337-AFA5-4DA8-8D99-84461B363108}.Release|x64.Build.0 = Release|x64 + {FB87D337-AFA5-4DA8-8D99-84461B363108}.Release|x86.ActiveCfg = Release|x64 {94D2DA0B-304D-4B92-AE12-1749EBD6F5F0}.Debug|x64.ActiveCfg = Debug|x64 {94D2DA0B-304D-4B92-AE12-1749EBD6F5F0}.Debug|x64.Build.0 = Debug|x64 + {94D2DA0B-304D-4B92-AE12-1749EBD6F5F0}.Debug|x86.ActiveCfg = Debug|x64 {94D2DA0B-304D-4B92-AE12-1749EBD6F5F0}.Release|x64.ActiveCfg = Release|x64 {94D2DA0B-304D-4B92-AE12-1749EBD6F5F0}.Release|x64.Build.0 = Release|x64 + {94D2DA0B-304D-4B92-AE12-1749EBD6F5F0}.Release|x86.ActiveCfg = Release|x64 {BECF37AD-77C3-4BD4-83EF-E5B0A2DC4188}.Debug|x64.ActiveCfg = Debug|x64 {BECF37AD-77C3-4BD4-83EF-E5B0A2DC4188}.Debug|x64.Build.0 = Debug|x64 + {BECF37AD-77C3-4BD4-83EF-E5B0A2DC4188}.Debug|x86.ActiveCfg = Debug|x64 {BECF37AD-77C3-4BD4-83EF-E5B0A2DC4188}.Release|x64.ActiveCfg = Release|x64 {BECF37AD-77C3-4BD4-83EF-E5B0A2DC4188}.Release|x64.Build.0 = Release|x64 + {BECF37AD-77C3-4BD4-83EF-E5B0A2DC4188}.Release|x86.ActiveCfg = Release|x64 {D8296B7C-9580-4AD6-A79C-A5660680A9FF}.Debug|x64.ActiveCfg = Debug|x64 {D8296B7C-9580-4AD6-A79C-A5660680A9FF}.Debug|x64.Build.0 = Debug|x64 + {D8296B7C-9580-4AD6-A79C-A5660680A9FF}.Debug|x86.ActiveCfg = Debug|x64 {D8296B7C-9580-4AD6-A79C-A5660680A9FF}.Release|x64.ActiveCfg = Release|x64 {D8296B7C-9580-4AD6-A79C-A5660680A9FF}.Release|x64.Build.0 = Release|x64 + {D8296B7C-9580-4AD6-A79C-A5660680A9FF}.Release|x86.ActiveCfg = Release|x64 {7724C191-A199-426A-9CF8-FCBF12377B71}.Debug|x64.ActiveCfg = Debug|x64 {7724C191-A199-426A-9CF8-FCBF12377B71}.Debug|x64.Build.0 = Debug|x64 + {7724C191-A199-426A-9CF8-FCBF12377B71}.Debug|x86.ActiveCfg = Debug|x64 {7724C191-A199-426A-9CF8-FCBF12377B71}.Release|x64.ActiveCfg = Release|x64 {7724C191-A199-426A-9CF8-FCBF12377B71}.Release|x64.Build.0 = Release|x64 + {7724C191-A199-426A-9CF8-FCBF12377B71}.Release|x86.ActiveCfg = Release|x64 {B51FE0EB-A748-4414-B180-48F9CFEC7BE4}.Debug|x64.ActiveCfg = Debug|x64 {B51FE0EB-A748-4414-B180-48F9CFEC7BE4}.Debug|x64.Build.0 = Debug|x64 + {B51FE0EB-A748-4414-B180-48F9CFEC7BE4}.Debug|x86.ActiveCfg = Debug|x64 {B51FE0EB-A748-4414-B180-48F9CFEC7BE4}.Release|x64.ActiveCfg = Release|x64 {B51FE0EB-A748-4414-B180-48F9CFEC7BE4}.Release|x64.Build.0 = Release|x64 + {B51FE0EB-A748-4414-B180-48F9CFEC7BE4}.Release|x86.ActiveCfg = Release|x64 {5A3274BE-645D-4EFC-B51E-621367E0F7D9}.Debug|x64.ActiveCfg = Debug|x64 {5A3274BE-645D-4EFC-B51E-621367E0F7D9}.Debug|x64.Build.0 = Debug|x64 + {5A3274BE-645D-4EFC-B51E-621367E0F7D9}.Debug|x86.ActiveCfg = Debug|x64 {5A3274BE-645D-4EFC-B51E-621367E0F7D9}.Release|x64.ActiveCfg = Release|x64 {5A3274BE-645D-4EFC-B51E-621367E0F7D9}.Release|x64.Build.0 = Release|x64 + {5A3274BE-645D-4EFC-B51E-621367E0F7D9}.Release|x86.ActiveCfg = Release|x64 {1890CE4F-B07D-43F8-B78E-E3995E582A88}.Debug|x64.ActiveCfg = Debug|x64 {1890CE4F-B07D-43F8-B78E-E3995E582A88}.Debug|x64.Build.0 = Debug|x64 + {1890CE4F-B07D-43F8-B78E-E3995E582A88}.Debug|x86.ActiveCfg = Debug|x64 {1890CE4F-B07D-43F8-B78E-E3995E582A88}.Release|x64.ActiveCfg = Release|x64 {1890CE4F-B07D-43F8-B78E-E3995E582A88}.Release|x64.Build.0 = Release|x64 + {1890CE4F-B07D-43F8-B78E-E3995E582A88}.Release|x86.ActiveCfg = Release|x64 {B2C75B14-27EA-4729-91D4-1C907CDDC8B1}.Debug|x64.ActiveCfg = Debug|x64 {B2C75B14-27EA-4729-91D4-1C907CDDC8B1}.Debug|x64.Build.0 = Debug|x64 + {B2C75B14-27EA-4729-91D4-1C907CDDC8B1}.Debug|x86.ActiveCfg = Debug|x64 {B2C75B14-27EA-4729-91D4-1C907CDDC8B1}.Release|x64.ActiveCfg = Release|x64 {B2C75B14-27EA-4729-91D4-1C907CDDC8B1}.Release|x64.Build.0 = Release|x64 + {B2C75B14-27EA-4729-91D4-1C907CDDC8B1}.Release|x86.ActiveCfg = Release|x64 {8F3B9231-0B58-49F4-9FB0-BCAF8F24771D}.Debug|x64.ActiveCfg = Debug|x64 {8F3B9231-0B58-49F4-9FB0-BCAF8F24771D}.Debug|x64.Build.0 = Debug|x64 + {8F3B9231-0B58-49F4-9FB0-BCAF8F24771D}.Debug|x86.ActiveCfg = Debug|x64 {8F3B9231-0B58-49F4-9FB0-BCAF8F24771D}.Release|x64.ActiveCfg = Release|x64 {8F3B9231-0B58-49F4-9FB0-BCAF8F24771D}.Release|x64.Build.0 = Release|x64 + {8F3B9231-0B58-49F4-9FB0-BCAF8F24771D}.Release|x86.ActiveCfg = Release|x64 {11F219E1-907F-44C3-B817-A595C6BDFC6B}.Debug|x64.ActiveCfg = Debug|x64 {11F219E1-907F-44C3-B817-A595C6BDFC6B}.Debug|x64.Build.0 = Debug|x64 + {11F219E1-907F-44C3-B817-A595C6BDFC6B}.Debug|x86.ActiveCfg = Debug|x64 {11F219E1-907F-44C3-B817-A595C6BDFC6B}.Release|x64.ActiveCfg = Release|x64 {11F219E1-907F-44C3-B817-A595C6BDFC6B}.Release|x64.Build.0 = Release|x64 + {11F219E1-907F-44C3-B817-A595C6BDFC6B}.Release|x86.ActiveCfg = Release|x64 {977C1D7D-5CCC-466A-9FEF-3F7471032FE6}.Debug|x64.ActiveCfg = Debug|x64 {977C1D7D-5CCC-466A-9FEF-3F7471032FE6}.Debug|x64.Build.0 = Debug|x64 + {977C1D7D-5CCC-466A-9FEF-3F7471032FE6}.Debug|x86.ActiveCfg = Debug|x64 {977C1D7D-5CCC-466A-9FEF-3F7471032FE6}.Release|x64.ActiveCfg = Release|x64 {977C1D7D-5CCC-466A-9FEF-3F7471032FE6}.Release|x64.Build.0 = Release|x64 + {977C1D7D-5CCC-466A-9FEF-3F7471032FE6}.Release|x86.ActiveCfg = Release|x64 {504F5562-47FD-4D46-8F18-0F5D078212D2}.Debug|x64.ActiveCfg = Debug|x64 {504F5562-47FD-4D46-8F18-0F5D078212D2}.Debug|x64.Build.0 = Debug|x64 + {504F5562-47FD-4D46-8F18-0F5D078212D2}.Debug|x86.ActiveCfg = Debug|x64 {504F5562-47FD-4D46-8F18-0F5D078212D2}.Release|x64.ActiveCfg = Release|x64 {504F5562-47FD-4D46-8F18-0F5D078212D2}.Release|x64.Build.0 = Release|x64 + {504F5562-47FD-4D46-8F18-0F5D078212D2}.Release|x86.ActiveCfg = Release|x64 {512D6313-BBE7-4ED0-92FF-A2B39817D059}.Debug|x64.ActiveCfg = Debug|x64 {512D6313-BBE7-4ED0-92FF-A2B39817D059}.Debug|x64.Build.0 = Debug|x64 + {512D6313-BBE7-4ED0-92FF-A2B39817D059}.Debug|x86.ActiveCfg = Debug|x64 {512D6313-BBE7-4ED0-92FF-A2B39817D059}.Release|x64.ActiveCfg = Release|x64 {512D6313-BBE7-4ED0-92FF-A2B39817D059}.Release|x64.Build.0 = Release|x64 + {512D6313-BBE7-4ED0-92FF-A2B39817D059}.Release|x86.ActiveCfg = Release|x64 {3B7D8A46-01A9-47E3-A431-F7DD7B5C7DD3}.Debug|x64.ActiveCfg = Debug|x64 {3B7D8A46-01A9-47E3-A431-F7DD7B5C7DD3}.Debug|x64.Build.0 = Debug|x64 + {3B7D8A46-01A9-47E3-A431-F7DD7B5C7DD3}.Debug|x86.ActiveCfg = Debug|x64 {3B7D8A46-01A9-47E3-A431-F7DD7B5C7DD3}.Release|x64.ActiveCfg = Release|x64 {3B7D8A46-01A9-47E3-A431-F7DD7B5C7DD3}.Release|x64.Build.0 = Release|x64 + {3B7D8A46-01A9-47E3-A431-F7DD7B5C7DD3}.Release|x86.ActiveCfg = Release|x64 {3049CF3F-97D8-4E88-AF5A-8C0F4D52A688}.Debug|x64.ActiveCfg = Debug|x64 {3049CF3F-97D8-4E88-AF5A-8C0F4D52A688}.Debug|x64.Build.0 = Debug|x64 + {3049CF3F-97D8-4E88-AF5A-8C0F4D52A688}.Debug|x86.ActiveCfg = Debug|x64 {3049CF3F-97D8-4E88-AF5A-8C0F4D52A688}.Release|x64.ActiveCfg = Release|x64 {3049CF3F-97D8-4E88-AF5A-8C0F4D52A688}.Release|x64.Build.0 = Release|x64 + {3049CF3F-97D8-4E88-AF5A-8C0F4D52A688}.Release|x86.ActiveCfg = Release|x64 {94157F30-3A38-4F0C-8C7C-7E8AA0560EBD}.Debug|x64.ActiveCfg = Debug|x64 {94157F30-3A38-4F0C-8C7C-7E8AA0560EBD}.Debug|x64.Build.0 = Debug|x64 + {94157F30-3A38-4F0C-8C7C-7E8AA0560EBD}.Debug|x86.ActiveCfg = Debug|x64 {94157F30-3A38-4F0C-8C7C-7E8AA0560EBD}.Release|x64.ActiveCfg = Release|x64 {94157F30-3A38-4F0C-8C7C-7E8AA0560EBD}.Release|x64.Build.0 = Release|x64 + {94157F30-3A38-4F0C-8C7C-7E8AA0560EBD}.Release|x86.ActiveCfg = Release|x64 {0484B8F5-F399-4A09-B604-CE5C981F3FDF}.Debug|x64.ActiveCfg = Debug|x64 {0484B8F5-F399-4A09-B604-CE5C981F3FDF}.Debug|x64.Build.0 = Debug|x64 + {0484B8F5-F399-4A09-B604-CE5C981F3FDF}.Debug|x86.ActiveCfg = Debug|x64 {0484B8F5-F399-4A09-B604-CE5C981F3FDF}.Release|x64.ActiveCfg = Release|x64 {0484B8F5-F399-4A09-B604-CE5C981F3FDF}.Release|x64.Build.0 = Release|x64 + {0484B8F5-F399-4A09-B604-CE5C981F3FDF}.Release|x86.ActiveCfg = Release|x64 {6F16F25A-6B06-4AB7-9B87-95311D8A4E2E}.Debug|x64.ActiveCfg = Debug|x64 {6F16F25A-6B06-4AB7-9B87-95311D8A4E2E}.Debug|x64.Build.0 = Debug|x64 + {6F16F25A-6B06-4AB7-9B87-95311D8A4E2E}.Debug|x86.ActiveCfg = Debug|x64 {6F16F25A-6B06-4AB7-9B87-95311D8A4E2E}.Release|x64.ActiveCfg = Release|x64 {6F16F25A-6B06-4AB7-9B87-95311D8A4E2E}.Release|x64.Build.0 = Release|x64 + {6F16F25A-6B06-4AB7-9B87-95311D8A4E2E}.Release|x86.ActiveCfg = Release|x64 {6D3C96B1-886E-4415-88BF-F5376A17222A}.Debug|x64.ActiveCfg = Debug|x64 {6D3C96B1-886E-4415-88BF-F5376A17222A}.Debug|x64.Build.0 = Debug|x64 + {6D3C96B1-886E-4415-88BF-F5376A17222A}.Debug|x86.ActiveCfg = Debug|x64 {6D3C96B1-886E-4415-88BF-F5376A17222A}.Release|x64.ActiveCfg = Release|x64 {6D3C96B1-886E-4415-88BF-F5376A17222A}.Release|x64.Build.0 = Release|x64 + {6D3C96B1-886E-4415-88BF-F5376A17222A}.Release|x86.ActiveCfg = Release|x64 {63DEA291-B595-447A-8C7F-D32AFDA11DFD}.Debug|x64.ActiveCfg = Debug|x64 {63DEA291-B595-447A-8C7F-D32AFDA11DFD}.Debug|x64.Build.0 = Debug|x64 + {63DEA291-B595-447A-8C7F-D32AFDA11DFD}.Debug|x86.ActiveCfg = Debug|x64 {63DEA291-B595-447A-8C7F-D32AFDA11DFD}.Release|x64.ActiveCfg = Release|x64 {63DEA291-B595-447A-8C7F-D32AFDA11DFD}.Release|x64.Build.0 = Release|x64 + {63DEA291-B595-447A-8C7F-D32AFDA11DFD}.Release|x86.ActiveCfg = Release|x64 {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}.Debug|x86.ActiveCfg = Debug|x64 {805A53D5-1E00-42D7-96F0-661AC7D042A6}.Release|x64.ActiveCfg = Release|x64 {805A53D5-1E00-42D7-96F0-661AC7D042A6}.Release|x64.Build.0 = Release|x64 + {805A53D5-1E00-42D7-96F0-661AC7D042A6}.Release|x86.ActiveCfg = Release|x64 {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}.Debug|x86.ActiveCfg = Debug|x64 {ED3E2C21-ADA8-446D-8951-26F1E6E9F4DC}.Release|x64.ActiveCfg = Release|x64 {ED3E2C21-ADA8-446D-8951-26F1E6E9F4DC}.Release|x64.Build.0 = Release|x64 + {ED3E2C21-ADA8-446D-8951-26F1E6E9F4DC}.Release|x86.ActiveCfg = Release|x64 {97768FFD-1616-4CA5-902A-2F128118CC39}.Debug|x64.ActiveCfg = Debug|x64 {97768FFD-1616-4CA5-902A-2F128118CC39}.Debug|x64.Build.0 = Debug|x64 + {97768FFD-1616-4CA5-902A-2F128118CC39}.Debug|x86.ActiveCfg = Debug|x64 {97768FFD-1616-4CA5-902A-2F128118CC39}.Release|x64.ActiveCfg = Release|x64 {97768FFD-1616-4CA5-902A-2F128118CC39}.Release|x64.Build.0 = Release|x64 + {97768FFD-1616-4CA5-902A-2F128118CC39}.Release|x86.ActiveCfg = Release|x64 {00E2B1E5-E1AA-4422-882C-A1DC0FB3C296}.Debug|x64.ActiveCfg = Debug|x64 {00E2B1E5-E1AA-4422-882C-A1DC0FB3C296}.Debug|x64.Build.0 = Debug|x64 + {00E2B1E5-E1AA-4422-882C-A1DC0FB3C296}.Debug|x86.ActiveCfg = Debug|x64 {00E2B1E5-E1AA-4422-882C-A1DC0FB3C296}.Release|x64.ActiveCfg = Release|x64 {00E2B1E5-E1AA-4422-882C-A1DC0FB3C296}.Release|x64.Build.0 = Release|x64 + {00E2B1E5-E1AA-4422-882C-A1DC0FB3C296}.Release|x86.ActiveCfg = Release|x64 {BC02359B-2485-413C-ABAF-54CA71E650E5}.Debug|x64.ActiveCfg = Debug|x64 {BC02359B-2485-413C-ABAF-54CA71E650E5}.Debug|x64.Build.0 = Debug|x64 + {BC02359B-2485-413C-ABAF-54CA71E650E5}.Debug|x86.ActiveCfg = Debug|x64 {BC02359B-2485-413C-ABAF-54CA71E650E5}.Release|x64.ActiveCfg = Release|x64 {BC02359B-2485-413C-ABAF-54CA71E650E5}.Release|x64.Build.0 = Release|x64 + {BC02359B-2485-413C-ABAF-54CA71E650E5}.Release|x86.ActiveCfg = Release|x64 {9EFC3E5C-9172-4AA9-A258-787E9A432F9D}.Debug|x64.ActiveCfg = Debug|x64 {9EFC3E5C-9172-4AA9-A258-787E9A432F9D}.Debug|x64.Build.0 = Debug|x64 + {9EFC3E5C-9172-4AA9-A258-787E9A432F9D}.Debug|x86.ActiveCfg = Debug|x64 {9EFC3E5C-9172-4AA9-A258-787E9A432F9D}.Release|x64.ActiveCfg = Release|x64 {9EFC3E5C-9172-4AA9-A258-787E9A432F9D}.Release|x64.Build.0 = Release|x64 + {9EFC3E5C-9172-4AA9-A258-787E9A432F9D}.Release|x86.ActiveCfg = Release|x64 {641D9549-0A15-4A98-9895-FDD19B380376}.Debug|x64.ActiveCfg = Debug|x64 {641D9549-0A15-4A98-9895-FDD19B380376}.Debug|x64.Build.0 = Debug|x64 + {641D9549-0A15-4A98-9895-FDD19B380376}.Debug|x86.ActiveCfg = Debug|x64 {641D9549-0A15-4A98-9895-FDD19B380376}.Release|x64.ActiveCfg = Release|x64 {641D9549-0A15-4A98-9895-FDD19B380376}.Release|x64.Build.0 = Release|x64 + {641D9549-0A15-4A98-9895-FDD19B380376}.Release|x86.ActiveCfg = Release|x64 {A982F985-EE25-434F-B9AF-E33C8A3AAC58}.Debug|x64.ActiveCfg = Debug|x64 {A982F985-EE25-434F-B9AF-E33C8A3AAC58}.Debug|x64.Build.0 = Debug|x64 + {A982F985-EE25-434F-B9AF-E33C8A3AAC58}.Debug|x86.ActiveCfg = Debug|x64 {A982F985-EE25-434F-B9AF-E33C8A3AAC58}.Release|x64.ActiveCfg = Release|x64 {A982F985-EE25-434F-B9AF-E33C8A3AAC58}.Release|x64.Build.0 = Release|x64 + {A982F985-EE25-434F-B9AF-E33C8A3AAC58}.Release|x86.ActiveCfg = Release|x64 {47C2BC19-A92B-4B1A-A0ED-E01D5DF50BDF}.Debug|x64.ActiveCfg = Debug|x64 {47C2BC19-A92B-4B1A-A0ED-E01D5DF50BDF}.Debug|x64.Build.0 = Debug|x64 + {47C2BC19-A92B-4B1A-A0ED-E01D5DF50BDF}.Debug|x86.ActiveCfg = Debug|x64 {47C2BC19-A92B-4B1A-A0ED-E01D5DF50BDF}.Release|x64.ActiveCfg = Release|x64 {47C2BC19-A92B-4B1A-A0ED-E01D5DF50BDF}.Release|x64.Build.0 = Release|x64 + {47C2BC19-A92B-4B1A-A0ED-E01D5DF50BDF}.Release|x86.ActiveCfg = Release|x64 {CF768AA5-5FC1-4657-8717-5AB4C0B71E9F}.Debug|x64.ActiveCfg = Debug|x64 {CF768AA5-5FC1-4657-8717-5AB4C0B71E9F}.Debug|x64.Build.0 = Debug|x64 + {CF768AA5-5FC1-4657-8717-5AB4C0B71E9F}.Debug|x86.ActiveCfg = Debug|x64 {CF768AA5-5FC1-4657-8717-5AB4C0B71E9F}.Release|x64.ActiveCfg = Release|x64 {CF768AA5-5FC1-4657-8717-5AB4C0B71E9F}.Release|x64.Build.0 = Release|x64 + {CF768AA5-5FC1-4657-8717-5AB4C0B71E9F}.Release|x86.ActiveCfg = Release|x64 {93B23968-DB73-4209-9D19-613A00BFE6B0}.Debug|x64.ActiveCfg = Debug|x64 {93B23968-DB73-4209-9D19-613A00BFE6B0}.Debug|x64.Build.0 = Debug|x64 + {93B23968-DB73-4209-9D19-613A00BFE6B0}.Debug|x86.ActiveCfg = Debug|x64 {93B23968-DB73-4209-9D19-613A00BFE6B0}.Release|x64.ActiveCfg = Release|x64 {93B23968-DB73-4209-9D19-613A00BFE6B0}.Release|x64.Build.0 = Release|x64 + {93B23968-DB73-4209-9D19-613A00BFE6B0}.Release|x86.ActiveCfg = Release|x64 {A6E4F003-EC8E-4868-9ABC-78D7C743BFB8}.Debug|x64.ActiveCfg = Debug|x64 {A6E4F003-EC8E-4868-9ABC-78D7C743BFB8}.Debug|x64.Build.0 = Debug|x64 + {A6E4F003-EC8E-4868-9ABC-78D7C743BFB8}.Debug|x86.ActiveCfg = Debug|x64 {A6E4F003-EC8E-4868-9ABC-78D7C743BFB8}.Release|x64.ActiveCfg = Release|x64 {A6E4F003-EC8E-4868-9ABC-78D7C743BFB8}.Release|x64.Build.0 = Release|x64 + {A6E4F003-EC8E-4868-9ABC-78D7C743BFB8}.Release|x86.ActiveCfg = Release|x64 {89E3B550-695B-48CA-95CB-514AD36A216D}.Debug|x64.ActiveCfg = Debug|x64 {89E3B550-695B-48CA-95CB-514AD36A216D}.Debug|x64.Build.0 = Debug|x64 + {89E3B550-695B-48CA-95CB-514AD36A216D}.Debug|x86.ActiveCfg = Debug|x64 {89E3B550-695B-48CA-95CB-514AD36A216D}.Release|x64.ActiveCfg = Release|x64 {89E3B550-695B-48CA-95CB-514AD36A216D}.Release|x64.Build.0 = Release|x64 + {89E3B550-695B-48CA-95CB-514AD36A216D}.Release|x86.ActiveCfg = Release|x64 {736FBB21-A1CB-47AB-A511-8C4D4C6C4CCA}.Debug|x64.ActiveCfg = Debug|x64 {736FBB21-A1CB-47AB-A511-8C4D4C6C4CCA}.Debug|x64.Build.0 = Debug|x64 + {736FBB21-A1CB-47AB-A511-8C4D4C6C4CCA}.Debug|x86.ActiveCfg = Debug|x64 {736FBB21-A1CB-47AB-A511-8C4D4C6C4CCA}.Release|x64.ActiveCfg = Release|x64 {736FBB21-A1CB-47AB-A511-8C4D4C6C4CCA}.Release|x64.Build.0 = Release|x64 + {736FBB21-A1CB-47AB-A511-8C4D4C6C4CCA}.Release|x86.ActiveCfg = Release|x64 {F4370C29-1428-41CC-A52D-EB1EE711DCBC}.Debug|x64.ActiveCfg = Debug|x64 {F4370C29-1428-41CC-A52D-EB1EE711DCBC}.Debug|x64.Build.0 = Debug|x64 + {F4370C29-1428-41CC-A52D-EB1EE711DCBC}.Debug|x86.ActiveCfg = Debug|x64 {F4370C29-1428-41CC-A52D-EB1EE711DCBC}.Release|x64.ActiveCfg = Release|x64 {F4370C29-1428-41CC-A52D-EB1EE711DCBC}.Release|x64.Build.0 = Release|x64 + {F4370C29-1428-41CC-A52D-EB1EE711DCBC}.Release|x86.ActiveCfg = Release|x64 {7BC77845-C12B-443E-800F-ABA28C25BCD3}.Debug|x64.ActiveCfg = Debug|x64 {7BC77845-C12B-443E-800F-ABA28C25BCD3}.Debug|x64.Build.0 = Debug|x64 + {7BC77845-C12B-443E-800F-ABA28C25BCD3}.Debug|x86.ActiveCfg = Debug|x64 {7BC77845-C12B-443E-800F-ABA28C25BCD3}.Release|x64.ActiveCfg = Release|x64 {7BC77845-C12B-443E-800F-ABA28C25BCD3}.Release|x64.Build.0 = Release|x64 + {7BC77845-C12B-443E-800F-ABA28C25BCD3}.Release|x86.ActiveCfg = Release|x64 {B3649820-C484-41C1-ABA1-38958EA7C2F6}.Debug|x64.ActiveCfg = Debug|x64 {B3649820-C484-41C1-ABA1-38958EA7C2F6}.Debug|x64.Build.0 = Debug|x64 + {B3649820-C484-41C1-ABA1-38958EA7C2F6}.Debug|x86.ActiveCfg = Debug|x64 {B3649820-C484-41C1-ABA1-38958EA7C2F6}.Release|x64.ActiveCfg = Release|x64 {B3649820-C484-41C1-ABA1-38958EA7C2F6}.Release|x64.Build.0 = Release|x64 + {B3649820-C484-41C1-ABA1-38958EA7C2F6}.Release|x86.ActiveCfg = Release|x64 {21977D8F-A672-4087-8A2D-B6CD64AC9355}.Debug|x64.ActiveCfg = Debug|x64 {21977D8F-A672-4087-8A2D-B6CD64AC9355}.Debug|x64.Build.0 = Debug|x64 + {21977D8F-A672-4087-8A2D-B6CD64AC9355}.Debug|x86.ActiveCfg = Debug|x64 {21977D8F-A672-4087-8A2D-B6CD64AC9355}.Release|x64.ActiveCfg = Release|x64 {21977D8F-A672-4087-8A2D-B6CD64AC9355}.Release|x64.Build.0 = Release|x64 + {21977D8F-A672-4087-8A2D-B6CD64AC9355}.Release|x86.ActiveCfg = Release|x64 {BAD7F927-3E16-4E48-BC4D-BD44EE743D31}.Debug|x64.ActiveCfg = Debug|x64 {BAD7F927-3E16-4E48-BC4D-BD44EE743D31}.Debug|x64.Build.0 = Debug|x64 + {BAD7F927-3E16-4E48-BC4D-BD44EE743D31}.Debug|x86.ActiveCfg = Debug|x64 {BAD7F927-3E16-4E48-BC4D-BD44EE743D31}.Release|x64.ActiveCfg = Release|x64 {BAD7F927-3E16-4E48-BC4D-BD44EE743D31}.Release|x64.Build.0 = Release|x64 + {BAD7F927-3E16-4E48-BC4D-BD44EE743D31}.Release|x86.ActiveCfg = Release|x64 {F6797E85-2A45-41E0-A066-85C78D7E3CD2}.Debug|x64.ActiveCfg = Debug|x64 {F6797E85-2A45-41E0-A066-85C78D7E3CD2}.Debug|x64.Build.0 = Debug|x64 + {F6797E85-2A45-41E0-A066-85C78D7E3CD2}.Debug|x86.ActiveCfg = Debug|x64 {F6797E85-2A45-41E0-A066-85C78D7E3CD2}.Release|x64.ActiveCfg = Release|x64 {F6797E85-2A45-41E0-A066-85C78D7E3CD2}.Release|x64.Build.0 = Release|x64 + {F6797E85-2A45-41E0-A066-85C78D7E3CD2}.Release|x86.ActiveCfg = Release|x64 {CE9218C0-5508-413D-8D9F-A022D61C4C10}.Debug|x64.ActiveCfg = Debug|x64 {CE9218C0-5508-413D-8D9F-A022D61C4C10}.Debug|x64.Build.0 = Debug|x64 + {CE9218C0-5508-413D-8D9F-A022D61C4C10}.Debug|x86.ActiveCfg = Debug|x64 {CE9218C0-5508-413D-8D9F-A022D61C4C10}.Release|x64.ActiveCfg = Release|x64 {CE9218C0-5508-413D-8D9F-A022D61C4C10}.Release|x64.Build.0 = Release|x64 + {CE9218C0-5508-413D-8D9F-A022D61C4C10}.Release|x86.ActiveCfg = Release|x64 {A0676B13-4850-4BF4-88BC-F099BEE5B762}.Debug|x64.ActiveCfg = Debug|x64 {A0676B13-4850-4BF4-88BC-F099BEE5B762}.Debug|x64.Build.0 = Debug|x64 + {A0676B13-4850-4BF4-88BC-F099BEE5B762}.Debug|x86.ActiveCfg = Debug|x64 {A0676B13-4850-4BF4-88BC-F099BEE5B762}.Release|x64.ActiveCfg = Release|x64 {A0676B13-4850-4BF4-88BC-F099BEE5B762}.Release|x64.Build.0 = Release|x64 + {A0676B13-4850-4BF4-88BC-F099BEE5B762}.Release|x86.ActiveCfg = Release|x64 {EAF38044-90A7-4062-B4B6-C0F6357E9EB9}.Debug|x64.ActiveCfg = Debug|x64 {EAF38044-90A7-4062-B4B6-C0F6357E9EB9}.Debug|x64.Build.0 = Debug|x64 + {EAF38044-90A7-4062-B4B6-C0F6357E9EB9}.Debug|x86.ActiveCfg = Debug|x64 {EAF38044-90A7-4062-B4B6-C0F6357E9EB9}.Release|x64.ActiveCfg = Release|x64 {EAF38044-90A7-4062-B4B6-C0F6357E9EB9}.Release|x64.Build.0 = Release|x64 + {EAF38044-90A7-4062-B4B6-C0F6357E9EB9}.Release|x86.ActiveCfg = Release|x64 {43564119-6C0F-4589-9BF5-0C6C653FF9F1}.Debug|x64.ActiveCfg = Debug|x64 {43564119-6C0F-4589-9BF5-0C6C653FF9F1}.Debug|x64.Build.0 = Debug|x64 + {43564119-6C0F-4589-9BF5-0C6C653FF9F1}.Debug|x86.ActiveCfg = Debug|x64 {43564119-6C0F-4589-9BF5-0C6C653FF9F1}.Release|x64.ActiveCfg = Release|x64 {43564119-6C0F-4589-9BF5-0C6C653FF9F1}.Release|x64.Build.0 = Release|x64 + {43564119-6C0F-4589-9BF5-0C6C653FF9F1}.Release|x86.ActiveCfg = Release|x64 {C374EE7D-242E-483E-AB16-C455EA9AF9E3}.Debug|x64.ActiveCfg = Debug|x64 {C374EE7D-242E-483E-AB16-C455EA9AF9E3}.Debug|x64.Build.0 = Debug|x64 + {C374EE7D-242E-483E-AB16-C455EA9AF9E3}.Debug|x86.ActiveCfg = Debug|x64 {C374EE7D-242E-483E-AB16-C455EA9AF9E3}.Release|x64.ActiveCfg = Release|x64 {C374EE7D-242E-483E-AB16-C455EA9AF9E3}.Release|x64.Build.0 = Release|x64 + {C374EE7D-242E-483E-AB16-C455EA9AF9E3}.Release|x86.ActiveCfg = Release|x64 {71ADDFBC-1246-482E-A326-51A156C3B8A7}.Debug|x64.ActiveCfg = Debug|x64 {71ADDFBC-1246-482E-A326-51A156C3B8A7}.Debug|x64.Build.0 = Debug|x64 + {71ADDFBC-1246-482E-A326-51A156C3B8A7}.Debug|x86.ActiveCfg = Debug|x64 {71ADDFBC-1246-482E-A326-51A156C3B8A7}.Release|x64.ActiveCfg = Release|x64 {71ADDFBC-1246-482E-A326-51A156C3B8A7}.Release|x64.Build.0 = Release|x64 + {71ADDFBC-1246-482E-A326-51A156C3B8A7}.Release|x86.ActiveCfg = Release|x64 {C1769CC1-A247-432C-9575-3A5A3C7402C7}.Debug|x64.ActiveCfg = Debug|x64 {C1769CC1-A247-432C-9575-3A5A3C7402C7}.Debug|x64.Build.0 = Debug|x64 + {C1769CC1-A247-432C-9575-3A5A3C7402C7}.Debug|x86.ActiveCfg = Debug|x64 {C1769CC1-A247-432C-9575-3A5A3C7402C7}.Release|x64.ActiveCfg = Release|x64 {C1769CC1-A247-432C-9575-3A5A3C7402C7}.Release|x64.Build.0 = Release|x64 + {C1769CC1-A247-432C-9575-3A5A3C7402C7}.Release|x86.ActiveCfg = Release|x64 {0C72F391-6E64-4C65-9029-CD20C3B9CE10}.Debug|x64.ActiveCfg = Debug|x64 {0C72F391-6E64-4C65-9029-CD20C3B9CE10}.Debug|x64.Build.0 = Debug|x64 + {0C72F391-6E64-4C65-9029-CD20C3B9CE10}.Debug|x86.ActiveCfg = Debug|x64 {0C72F391-6E64-4C65-9029-CD20C3B9CE10}.Release|x64.ActiveCfg = Release|x64 {0C72F391-6E64-4C65-9029-CD20C3B9CE10}.Release|x64.Build.0 = Release|x64 + {0C72F391-6E64-4C65-9029-CD20C3B9CE10}.Release|x86.ActiveCfg = Release|x64 {474D32D8-5BEC-453B-ADEA-ACF6F742DE0D}.Debug|x64.ActiveCfg = Debug|x64 {474D32D8-5BEC-453B-ADEA-ACF6F742DE0D}.Debug|x64.Build.0 = Debug|x64 + {474D32D8-5BEC-453B-ADEA-ACF6F742DE0D}.Debug|x86.ActiveCfg = Debug|x64 {474D32D8-5BEC-453B-ADEA-ACF6F742DE0D}.Release|x64.ActiveCfg = Release|x64 {474D32D8-5BEC-453B-ADEA-ACF6F742DE0D}.Release|x64.Build.0 = Release|x64 + {474D32D8-5BEC-453B-ADEA-ACF6F742DE0D}.Release|x86.ActiveCfg = Release|x64 {BD5DD57F-A092-4441-BA35-5B0701C7CCE5}.Debug|x64.ActiveCfg = Debug|x64 {BD5DD57F-A092-4441-BA35-5B0701C7CCE5}.Debug|x64.Build.0 = Debug|x64 + {BD5DD57F-A092-4441-BA35-5B0701C7CCE5}.Debug|x86.ActiveCfg = Debug|x64 {BD5DD57F-A092-4441-BA35-5B0701C7CCE5}.Release|x64.ActiveCfg = Release|x64 {BD5DD57F-A092-4441-BA35-5B0701C7CCE5}.Release|x64.Build.0 = Release|x64 + {BD5DD57F-A092-4441-BA35-5B0701C7CCE5}.Release|x86.ActiveCfg = Release|x64 {DFA5AB7B-A917-44BB-AB76-6AB1AECFADC8}.Debug|x64.ActiveCfg = Debug|x64 {DFA5AB7B-A917-44BB-AB76-6AB1AECFADC8}.Debug|x64.Build.0 = Debug|x64 + {DFA5AB7B-A917-44BB-AB76-6AB1AECFADC8}.Debug|x86.ActiveCfg = Debug|x64 {DFA5AB7B-A917-44BB-AB76-6AB1AECFADC8}.Release|x64.ActiveCfg = Release|x64 {DFA5AB7B-A917-44BB-AB76-6AB1AECFADC8}.Release|x64.Build.0 = Release|x64 + {DFA5AB7B-A917-44BB-AB76-6AB1AECFADC8}.Release|x86.ActiveCfg = Release|x64 {715C6F65-57D3-462B-92BC-C4E99B2FFC2B}.Debug|x64.ActiveCfg = Debug|x64 {715C6F65-57D3-462B-92BC-C4E99B2FFC2B}.Debug|x64.Build.0 = Debug|x64 + {715C6F65-57D3-462B-92BC-C4E99B2FFC2B}.Debug|x86.ActiveCfg = Debug|x64 {715C6F65-57D3-462B-92BC-C4E99B2FFC2B}.Release|x64.ActiveCfg = Release|x64 {715C6F65-57D3-462B-92BC-C4E99B2FFC2B}.Release|x64.Build.0 = Release|x64 + {715C6F65-57D3-462B-92BC-C4E99B2FFC2B}.Release|x86.ActiveCfg = Release|x64 {27A3BEE1-1852-44CD-A271-46D667378699}.Debug|x64.ActiveCfg = Debug|x64 {27A3BEE1-1852-44CD-A271-46D667378699}.Debug|x64.Build.0 = Debug|x64 + {27A3BEE1-1852-44CD-A271-46D667378699}.Debug|x86.ActiveCfg = Debug|x64 {27A3BEE1-1852-44CD-A271-46D667378699}.Release|x64.ActiveCfg = Release|x64 {27A3BEE1-1852-44CD-A271-46D667378699}.Release|x64.Build.0 = Release|x64 + {27A3BEE1-1852-44CD-A271-46D667378699}.Release|x86.ActiveCfg = Release|x64 {66015666-4FA4-4313-86A1-A4B44B608FD8}.Debug|x64.ActiveCfg = Debug|x64 {66015666-4FA4-4313-86A1-A4B44B608FD8}.Debug|x64.Build.0 = Debug|x64 + {66015666-4FA4-4313-86A1-A4B44B608FD8}.Debug|x86.ActiveCfg = Debug|x64 {66015666-4FA4-4313-86A1-A4B44B608FD8}.Release|x64.ActiveCfg = Release|x64 {66015666-4FA4-4313-86A1-A4B44B608FD8}.Release|x64.Build.0 = Release|x64 + {66015666-4FA4-4313-86A1-A4B44B608FD8}.Release|x86.ActiveCfg = Release|x64 {1D7C5EE9-360A-4ECD-A732-16A57D05CA2C}.Debug|x64.ActiveCfg = Debug|x64 {1D7C5EE9-360A-4ECD-A732-16A57D05CA2C}.Debug|x64.Build.0 = Debug|x64 + {1D7C5EE9-360A-4ECD-A732-16A57D05CA2C}.Debug|x86.ActiveCfg = Debug|x64 {1D7C5EE9-360A-4ECD-A732-16A57D05CA2C}.Release|x64.ActiveCfg = Release|x64 {1D7C5EE9-360A-4ECD-A732-16A57D05CA2C}.Release|x64.Build.0 = Release|x64 + {1D7C5EE9-360A-4ECD-A732-16A57D05CA2C}.Release|x86.ActiveCfg = Release|x64 {FE81C0DA-B042-4ACA-B0CF-B9ABEB687FB8}.Debug|x64.ActiveCfg = Debug|x64 {FE81C0DA-B042-4ACA-B0CF-B9ABEB687FB8}.Debug|x64.Build.0 = Debug|x64 + {FE81C0DA-B042-4ACA-B0CF-B9ABEB687FB8}.Debug|x86.ActiveCfg = Debug|x64 {FE81C0DA-B042-4ACA-B0CF-B9ABEB687FB8}.Release|x64.ActiveCfg = Release|x64 {FE81C0DA-B042-4ACA-B0CF-B9ABEB687FB8}.Release|x64.Build.0 = Release|x64 + {FE81C0DA-B042-4ACA-B0CF-B9ABEB687FB8}.Release|x86.ActiveCfg = Release|x64 {1CB0DE2A-5A06-4F58-9EB1-8D3B289EAC7B}.Debug|x64.ActiveCfg = Debug|x64 {1CB0DE2A-5A06-4F58-9EB1-8D3B289EAC7B}.Debug|x64.Build.0 = Debug|x64 + {1CB0DE2A-5A06-4F58-9EB1-8D3B289EAC7B}.Debug|x86.ActiveCfg = Debug|x64 {1CB0DE2A-5A06-4F58-9EB1-8D3B289EAC7B}.Release|x64.ActiveCfg = Release|x64 {1CB0DE2A-5A06-4F58-9EB1-8D3B289EAC7B}.Release|x64.Build.0 = Release|x64 + {1CB0DE2A-5A06-4F58-9EB1-8D3B289EAC7B}.Release|x86.ActiveCfg = Release|x64 {F0E26FF0-5A6E-4B20-8467-2EA2A7E128E3}.Debug|x64.ActiveCfg = Debug|x64 {F0E26FF0-5A6E-4B20-8467-2EA2A7E128E3}.Debug|x64.Build.0 = Debug|x64 + {F0E26FF0-5A6E-4B20-8467-2EA2A7E128E3}.Debug|x86.ActiveCfg = Debug|x64 {F0E26FF0-5A6E-4B20-8467-2EA2A7E128E3}.Release|x64.ActiveCfg = Release|x64 {F0E26FF0-5A6E-4B20-8467-2EA2A7E128E3}.Release|x64.Build.0 = Release|x64 + {F0E26FF0-5A6E-4B20-8467-2EA2A7E128E3}.Release|x86.ActiveCfg = Release|x64 {986A17F0-C03C-41E5-B27F-5D46CC3361A2}.Debug|x64.ActiveCfg = Debug|x64 {986A17F0-C03C-41E5-B27F-5D46CC3361A2}.Debug|x64.Build.0 = Debug|x64 + {986A17F0-C03C-41E5-B27F-5D46CC3361A2}.Debug|x86.ActiveCfg = Debug|x64 {986A17F0-C03C-41E5-B27F-5D46CC3361A2}.Release|x64.ActiveCfg = Release|x64 {986A17F0-C03C-41E5-B27F-5D46CC3361A2}.Release|x64.Build.0 = Release|x64 + {986A17F0-C03C-41E5-B27F-5D46CC3361A2}.Release|x86.ActiveCfg = Release|x64 {A0A13E6E-0A7F-42F9-9695-68900BB2455D}.Debug|x64.ActiveCfg = Debug|x64 {A0A13E6E-0A7F-42F9-9695-68900BB2455D}.Debug|x64.Build.0 = Debug|x64 + {A0A13E6E-0A7F-42F9-9695-68900BB2455D}.Debug|x86.ActiveCfg = Debug|x64 {A0A13E6E-0A7F-42F9-9695-68900BB2455D}.Release|x64.ActiveCfg = Release|x64 {A0A13E6E-0A7F-42F9-9695-68900BB2455D}.Release|x64.Build.0 = Release|x64 + {A0A13E6E-0A7F-42F9-9695-68900BB2455D}.Release|x86.ActiveCfg = Release|x64 {CCF70670-40F9-4F6C-B202-0D1A2E0F21BD}.Debug|x64.ActiveCfg = Debug|x64 {CCF70670-40F9-4F6C-B202-0D1A2E0F21BD}.Debug|x64.Build.0 = Debug|x64 + {CCF70670-40F9-4F6C-B202-0D1A2E0F21BD}.Debug|x86.ActiveCfg = Debug|x64 {CCF70670-40F9-4F6C-B202-0D1A2E0F21BD}.Release|x64.ActiveCfg = Release|x64 {CCF70670-40F9-4F6C-B202-0D1A2E0F21BD}.Release|x64.Build.0 = Release|x64 + {CCF70670-40F9-4F6C-B202-0D1A2E0F21BD}.Release|x86.ActiveCfg = Release|x64 {45AC6AA4-D094-4248-98E1-53403895BEE8}.Debug|x64.ActiveCfg = Debug|x64 {45AC6AA4-D094-4248-98E1-53403895BEE8}.Debug|x64.Build.0 = Debug|x64 + {45AC6AA4-D094-4248-98E1-53403895BEE8}.Debug|x86.ActiveCfg = Debug|x64 {45AC6AA4-D094-4248-98E1-53403895BEE8}.Release|x64.ActiveCfg = Release|x64 {45AC6AA4-D094-4248-98E1-53403895BEE8}.Release|x64.Build.0 = Release|x64 + {45AC6AA4-D094-4248-98E1-53403895BEE8}.Release|x86.ActiveCfg = Release|x64 {E4867599-5514-46D9-9200-AD2BC4674015}.Debug|x64.ActiveCfg = Debug|x64 {E4867599-5514-46D9-9200-AD2BC4674015}.Debug|x64.Build.0 = Debug|x64 + {E4867599-5514-46D9-9200-AD2BC4674015}.Debug|x86.ActiveCfg = Debug|x64 {E4867599-5514-46D9-9200-AD2BC4674015}.Release|x64.ActiveCfg = Release|x64 {E4867599-5514-46D9-9200-AD2BC4674015}.Release|x64.Build.0 = Release|x64 + {E4867599-5514-46D9-9200-AD2BC4674015}.Release|x86.ActiveCfg = Release|x64 {D098C9DC-D6DB-4E88-851C-A997D0F87C9D}.Debug|x64.ActiveCfg = Debug|x64 {D098C9DC-D6DB-4E88-851C-A997D0F87C9D}.Debug|x64.Build.0 = Debug|x64 + {D098C9DC-D6DB-4E88-851C-A997D0F87C9D}.Debug|x86.ActiveCfg = Debug|x64 {D098C9DC-D6DB-4E88-851C-A997D0F87C9D}.Release|x64.ActiveCfg = Release|x64 {D098C9DC-D6DB-4E88-851C-A997D0F87C9D}.Release|x64.Build.0 = Release|x64 + {D098C9DC-D6DB-4E88-851C-A997D0F87C9D}.Release|x86.ActiveCfg = Release|x64 {3941E397-C74E-4AC8-8A01-741DB3AD1624}.Debug|x64.ActiveCfg = Debug|x64 {3941E397-C74E-4AC8-8A01-741DB3AD1624}.Debug|x64.Build.0 = Debug|x64 + {3941E397-C74E-4AC8-8A01-741DB3AD1624}.Debug|x86.ActiveCfg = Debug|x64 {3941E397-C74E-4AC8-8A01-741DB3AD1624}.Release|x64.ActiveCfg = Release|x64 {3941E397-C74E-4AC8-8A01-741DB3AD1624}.Release|x64.Build.0 = Release|x64 + {3941E397-C74E-4AC8-8A01-741DB3AD1624}.Release|x86.ActiveCfg = Release|x64 {7074BB23-A32B-4CD5-B77E-9EF4E24416BD}.Debug|x64.ActiveCfg = Debug|x64 {7074BB23-A32B-4CD5-B77E-9EF4E24416BD}.Debug|x64.Build.0 = Debug|x64 + {7074BB23-A32B-4CD5-B77E-9EF4E24416BD}.Debug|x86.ActiveCfg = Debug|x64 {7074BB23-A32B-4CD5-B77E-9EF4E24416BD}.Release|x64.ActiveCfg = Release|x64 {7074BB23-A32B-4CD5-B77E-9EF4E24416BD}.Release|x64.Build.0 = Release|x64 + {7074BB23-A32B-4CD5-B77E-9EF4E24416BD}.Release|x86.ActiveCfg = Release|x64 {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}.Debug|x86.ActiveCfg = Debug|x64 {E376977B-2B54-4745-9FE1-DDF726AD376F}.Release|x64.ActiveCfg = Release|x64 {E376977B-2B54-4745-9FE1-DDF726AD376F}.Release|x64.Build.0 = Release|x64 + {E376977B-2B54-4745-9FE1-DDF726AD376F}.Release|x86.ActiveCfg = Release|x64 {9B71AD68-C43A-42C2-96E7-4113975D1C2A}.Debug|x64.ActiveCfg = Debug|x64 {9B71AD68-C43A-42C2-96E7-4113975D1C2A}.Debug|x64.Build.0 = Debug|x64 + {9B71AD68-C43A-42C2-96E7-4113975D1C2A}.Debug|x86.ActiveCfg = Debug|x64 {9B71AD68-C43A-42C2-96E7-4113975D1C2A}.Release|x64.ActiveCfg = Release|x64 {9B71AD68-C43A-42C2-96E7-4113975D1C2A}.Release|x64.Build.0 = Release|x64 + {9B71AD68-C43A-42C2-96E7-4113975D1C2A}.Release|x86.ActiveCfg = Release|x64 {2289E020-8729-4785-8EEA-168303BF6A35}.Debug|x64.ActiveCfg = Debug|x64 {2289E020-8729-4785-8EEA-168303BF6A35}.Debug|x64.Build.0 = Debug|x64 + {2289E020-8729-4785-8EEA-168303BF6A35}.Debug|x86.ActiveCfg = Debug|x64 {2289E020-8729-4785-8EEA-168303BF6A35}.Release|x64.ActiveCfg = Release|x64 {2289E020-8729-4785-8EEA-168303BF6A35}.Release|x64.Build.0 = Release|x64 + {2289E020-8729-4785-8EEA-168303BF6A35}.Release|x86.ActiveCfg = Release|x64 {A8FBEF79-3324-4ABF-B494-1A47C604F5B5}.Debug|x64.ActiveCfg = Debug|x64 {A8FBEF79-3324-4ABF-B494-1A47C604F5B5}.Debug|x64.Build.0 = Debug|x64 + {A8FBEF79-3324-4ABF-B494-1A47C604F5B5}.Debug|x86.ActiveCfg = Debug|x64 {A8FBEF79-3324-4ABF-B494-1A47C604F5B5}.Release|x64.ActiveCfg = Release|x64 {A8FBEF79-3324-4ABF-B494-1A47C604F5B5}.Release|x64.Build.0 = Release|x64 + {A8FBEF79-3324-4ABF-B494-1A47C604F5B5}.Release|x86.ActiveCfg = Release|x64 {17A48943-0897-4DA3-81D3-CDF2BA6028FD}.Debug|x64.ActiveCfg = Debug|x64 {17A48943-0897-4DA3-81D3-CDF2BA6028FD}.Debug|x64.Build.0 = Debug|x64 + {17A48943-0897-4DA3-81D3-CDF2BA6028FD}.Debug|x86.ActiveCfg = Debug|x64 {17A48943-0897-4DA3-81D3-CDF2BA6028FD}.Release|x64.ActiveCfg = Release|x64 {17A48943-0897-4DA3-81D3-CDF2BA6028FD}.Release|x64.Build.0 = Release|x64 + {17A48943-0897-4DA3-81D3-CDF2BA6028FD}.Release|x86.ActiveCfg = Release|x64 {51AC9E8C-8BD5-4EFE-8571-6EE74D0BC22B}.Debug|x64.ActiveCfg = Debug|x64 {51AC9E8C-8BD5-4EFE-8571-6EE74D0BC22B}.Debug|x64.Build.0 = Debug|x64 + {51AC9E8C-8BD5-4EFE-8571-6EE74D0BC22B}.Debug|x86.ActiveCfg = Debug|x64 {51AC9E8C-8BD5-4EFE-8571-6EE74D0BC22B}.Release|x64.ActiveCfg = Release|x64 {51AC9E8C-8BD5-4EFE-8571-6EE74D0BC22B}.Release|x64.Build.0 = Release|x64 + {51AC9E8C-8BD5-4EFE-8571-6EE74D0BC22B}.Release|x86.ActiveCfg = Release|x64 {5583AC3C-328B-4969-978A-63AED0DD2258}.Debug|x64.ActiveCfg = Debug|x64 {5583AC3C-328B-4969-978A-63AED0DD2258}.Debug|x64.Build.0 = Debug|x64 + {5583AC3C-328B-4969-978A-63AED0DD2258}.Debug|x86.ActiveCfg = Debug|x64 {5583AC3C-328B-4969-978A-63AED0DD2258}.Release|x64.ActiveCfg = Release|x64 {5583AC3C-328B-4969-978A-63AED0DD2258}.Release|x64.Build.0 = Release|x64 + {5583AC3C-328B-4969-978A-63AED0DD2258}.Release|x86.ActiveCfg = Release|x64 {870B45D3-5EA6-47C3-9A86-3372CE0A0436}.Debug|x64.ActiveCfg = Debug|x64 {870B45D3-5EA6-47C3-9A86-3372CE0A0436}.Debug|x64.Build.0 = Debug|x64 + {870B45D3-5EA6-47C3-9A86-3372CE0A0436}.Debug|x86.ActiveCfg = Debug|x64 {870B45D3-5EA6-47C3-9A86-3372CE0A0436}.Release|x64.ActiveCfg = Release|x64 {870B45D3-5EA6-47C3-9A86-3372CE0A0436}.Release|x64.Build.0 = Release|x64 + {870B45D3-5EA6-47C3-9A86-3372CE0A0436}.Release|x86.ActiveCfg = Release|x64 {9A33047E-4E1F-4C96-8BBA-FB9610D75373}.Debug|x64.ActiveCfg = Debug|x64 {9A33047E-4E1F-4C96-8BBA-FB9610D75373}.Debug|x64.Build.0 = Debug|x64 + {9A33047E-4E1F-4C96-8BBA-FB9610D75373}.Debug|x86.ActiveCfg = Debug|x64 {9A33047E-4E1F-4C96-8BBA-FB9610D75373}.Release|x64.ActiveCfg = Release|x64 {9A33047E-4E1F-4C96-8BBA-FB9610D75373}.Release|x64.Build.0 = Release|x64 + {9A33047E-4E1F-4C96-8BBA-FB9610D75373}.Release|x86.ActiveCfg = Release|x64 {BD7B178B-B026-48FC-B7BD-C5D5257B3A20}.Debug|x64.ActiveCfg = Debug|x64 {BD7B178B-B026-48FC-B7BD-C5D5257B3A20}.Debug|x64.Build.0 = Debug|x64 + {BD7B178B-B026-48FC-B7BD-C5D5257B3A20}.Debug|x86.ActiveCfg = Debug|x64 {BD7B178B-B026-48FC-B7BD-C5D5257B3A20}.Release|x64.ActiveCfg = Release|x64 {BD7B178B-B026-48FC-B7BD-C5D5257B3A20}.Release|x64.Build.0 = Release|x64 + {BD7B178B-B026-48FC-B7BD-C5D5257B3A20}.Release|x86.ActiveCfg = Release|x64 {B301A051-2FA3-439F-BBC1-9D6FBBEDD2F2}.Debug|x64.ActiveCfg = Debug|x64 {B301A051-2FA3-439F-BBC1-9D6FBBEDD2F2}.Debug|x64.Build.0 = Debug|x64 + {B301A051-2FA3-439F-BBC1-9D6FBBEDD2F2}.Debug|x86.ActiveCfg = Debug|x64 {B301A051-2FA3-439F-BBC1-9D6FBBEDD2F2}.Release|x64.ActiveCfg = Release|x64 {B301A051-2FA3-439F-BBC1-9D6FBBEDD2F2}.Release|x64.Build.0 = Release|x64 + {B301A051-2FA3-439F-BBC1-9D6FBBEDD2F2}.Release|x86.ActiveCfg = Release|x64 {5BCA72C1-C342-4272-A037-A94C73E4ACE8}.Debug|x64.ActiveCfg = Debug|x64 {5BCA72C1-C342-4272-A037-A94C73E4ACE8}.Debug|x64.Build.0 = Debug|x64 + {5BCA72C1-C342-4272-A037-A94C73E4ACE8}.Debug|x86.ActiveCfg = Debug|x64 {5BCA72C1-C342-4272-A037-A94C73E4ACE8}.Release|x64.ActiveCfg = Release|x64 {5BCA72C1-C342-4272-A037-A94C73E4ACE8}.Release|x64.Build.0 = Release|x64 + {5BCA72C1-C342-4272-A037-A94C73E4ACE8}.Release|x86.ActiveCfg = Release|x64 {03EEA700-F668-4F91-873F-E69A482A07E1}.Debug|x64.ActiveCfg = Debug|x64 {03EEA700-F668-4F91-873F-E69A482A07E1}.Debug|x64.Build.0 = Debug|x64 + {03EEA700-F668-4F91-873F-E69A482A07E1}.Debug|x86.ActiveCfg = Debug|x64 {03EEA700-F668-4F91-873F-E69A482A07E1}.Release|x64.ActiveCfg = Release|x64 {03EEA700-F668-4F91-873F-E69A482A07E1}.Release|x64.Build.0 = Release|x64 + {03EEA700-F668-4F91-873F-E69A482A07E1}.Release|x86.ActiveCfg = Release|x64 {F6AA40F0-4825-447A-8DE4-8704B6F7A3DB}.Debug|x64.ActiveCfg = Debug|x64 {F6AA40F0-4825-447A-8DE4-8704B6F7A3DB}.Debug|x64.Build.0 = Debug|x64 + {F6AA40F0-4825-447A-8DE4-8704B6F7A3DB}.Debug|x86.ActiveCfg = Debug|x64 {F6AA40F0-4825-447A-8DE4-8704B6F7A3DB}.Release|x64.ActiveCfg = Release|x64 {F6AA40F0-4825-447A-8DE4-8704B6F7A3DB}.Release|x64.Build.0 = Release|x64 + {F6AA40F0-4825-447A-8DE4-8704B6F7A3DB}.Release|x86.ActiveCfg = Release|x64 {698AC26E-0AA3-4BD5-8E29-11BC931A88CF}.Debug|x64.ActiveCfg = Debug|x64 {698AC26E-0AA3-4BD5-8E29-11BC931A88CF}.Debug|x64.Build.0 = Debug|x64 + {698AC26E-0AA3-4BD5-8E29-11BC931A88CF}.Debug|x86.ActiveCfg = Debug|x64 {698AC26E-0AA3-4BD5-8E29-11BC931A88CF}.Release|x64.ActiveCfg = Release|x64 {698AC26E-0AA3-4BD5-8E29-11BC931A88CF}.Release|x64.Build.0 = Release|x64 + {698AC26E-0AA3-4BD5-8E29-11BC931A88CF}.Release|x86.ActiveCfg = Release|x64 {3FD8CD0C-55F8-4292-99B3-3C6B5F1DE55E}.Debug|x64.ActiveCfg = Debug|x64 {3FD8CD0C-55F8-4292-99B3-3C6B5F1DE55E}.Debug|x64.Build.0 = Debug|x64 + {3FD8CD0C-55F8-4292-99B3-3C6B5F1DE55E}.Debug|x86.ActiveCfg = Debug|x64 {3FD8CD0C-55F8-4292-99B3-3C6B5F1DE55E}.Release|x64.ActiveCfg = Release|x64 {3FD8CD0C-55F8-4292-99B3-3C6B5F1DE55E}.Release|x64.Build.0 = Release|x64 + {3FD8CD0C-55F8-4292-99B3-3C6B5F1DE55E}.Release|x86.ActiveCfg = Release|x64 {ABE0F7CB-AEE4-4CD4-9447-9378E585B2F1}.Debug|x64.ActiveCfg = Debug|x64 {ABE0F7CB-AEE4-4CD4-9447-9378E585B2F1}.Debug|x64.Build.0 = Debug|x64 + {ABE0F7CB-AEE4-4CD4-9447-9378E585B2F1}.Debug|x86.ActiveCfg = Debug|x64 {ABE0F7CB-AEE4-4CD4-9447-9378E585B2F1}.Release|x64.ActiveCfg = Release|x64 {ABE0F7CB-AEE4-4CD4-9447-9378E585B2F1}.Release|x64.Build.0 = Release|x64 + {ABE0F7CB-AEE4-4CD4-9447-9378E585B2F1}.Release|x86.ActiveCfg = Release|x64 {7ECDC9D5-626B-4F5B-AD3D-E6EF249BAA6E}.Debug|x64.ActiveCfg = Debug|x64 {7ECDC9D5-626B-4F5B-AD3D-E6EF249BAA6E}.Debug|x64.Build.0 = Debug|x64 + {7ECDC9D5-626B-4F5B-AD3D-E6EF249BAA6E}.Debug|x86.ActiveCfg = Debug|x64 {7ECDC9D5-626B-4F5B-AD3D-E6EF249BAA6E}.Release|x64.ActiveCfg = Release|x64 {7ECDC9D5-626B-4F5B-AD3D-E6EF249BAA6E}.Release|x64.Build.0 = Release|x64 + {7ECDC9D5-626B-4F5B-AD3D-E6EF249BAA6E}.Release|x86.ActiveCfg = Release|x64 {39655236-2634-4ED3-AACD-0C9B032F805C}.Debug|x64.ActiveCfg = Debug|x64 {39655236-2634-4ED3-AACD-0C9B032F805C}.Debug|x64.Build.0 = Debug|x64 + {39655236-2634-4ED3-AACD-0C9B032F805C}.Debug|x86.ActiveCfg = Debug|x64 {39655236-2634-4ED3-AACD-0C9B032F805C}.Release|x64.ActiveCfg = Release|x64 {39655236-2634-4ED3-AACD-0C9B032F805C}.Release|x64.Build.0 = Release|x64 + {39655236-2634-4ED3-AACD-0C9B032F805C}.Release|x86.ActiveCfg = Release|x64 {899D6AEF-6BC3-4203-B566-4EA4B52D1E80}.Debug|x64.ActiveCfg = Debug|x64 {899D6AEF-6BC3-4203-B566-4EA4B52D1E80}.Debug|x64.Build.0 = Debug|x64 + {899D6AEF-6BC3-4203-B566-4EA4B52D1E80}.Debug|x86.ActiveCfg = Debug|x64 {899D6AEF-6BC3-4203-B566-4EA4B52D1E80}.Release|x64.ActiveCfg = Release|x64 {899D6AEF-6BC3-4203-B566-4EA4B52D1E80}.Release|x64.Build.0 = Release|x64 + {899D6AEF-6BC3-4203-B566-4EA4B52D1E80}.Release|x86.ActiveCfg = Release|x64 {A52B96CD-B86B-4963-BED4-CA3F5C515327}.Debug|x64.ActiveCfg = Debug|x64 {A52B96CD-B86B-4963-BED4-CA3F5C515327}.Debug|x64.Build.0 = Debug|x64 + {A52B96CD-B86B-4963-BED4-CA3F5C515327}.Debug|x86.ActiveCfg = Debug|x64 {A52B96CD-B86B-4963-BED4-CA3F5C515327}.Release|x64.ActiveCfg = Release|x64 {A52B96CD-B86B-4963-BED4-CA3F5C515327}.Release|x64.Build.0 = Release|x64 + {A52B96CD-B86B-4963-BED4-CA3F5C515327}.Release|x86.ActiveCfg = Release|x64 {3B7DC5D6-2FAB-4987-9274-EA5884414862}.Debug|x64.ActiveCfg = Debug|x64 {3B7DC5D6-2FAB-4987-9274-EA5884414862}.Debug|x64.Build.0 = Debug|x64 + {3B7DC5D6-2FAB-4987-9274-EA5884414862}.Debug|x86.ActiveCfg = Debug|x64 {3B7DC5D6-2FAB-4987-9274-EA5884414862}.Release|x64.ActiveCfg = Release|x64 {3B7DC5D6-2FAB-4987-9274-EA5884414862}.Release|x64.Build.0 = Release|x64 + {3B7DC5D6-2FAB-4987-9274-EA5884414862}.Release|x86.ActiveCfg = Release|x64 {06301824-70DB-47F1-80AC-DBECCEE2EA99}.Debug|x64.ActiveCfg = Debug|x64 {06301824-70DB-47F1-80AC-DBECCEE2EA99}.Debug|x64.Build.0 = Debug|x64 + {06301824-70DB-47F1-80AC-DBECCEE2EA99}.Debug|x86.ActiveCfg = Debug|x64 {06301824-70DB-47F1-80AC-DBECCEE2EA99}.Release|x64.ActiveCfg = Release|x64 {06301824-70DB-47F1-80AC-DBECCEE2EA99}.Release|x64.Build.0 = Release|x64 + {06301824-70DB-47F1-80AC-DBECCEE2EA99}.Release|x86.ActiveCfg = Release|x64 {5E193242-B386-49EF-B592-9ED3552D273D}.Debug|x64.ActiveCfg = Debug|x64 {5E193242-B386-49EF-B592-9ED3552D273D}.Debug|x64.Build.0 = Debug|x64 + {5E193242-B386-49EF-B592-9ED3552D273D}.Debug|x86.ActiveCfg = Debug|x64 {5E193242-B386-49EF-B592-9ED3552D273D}.Release|x64.ActiveCfg = Release|x64 {5E193242-B386-49EF-B592-9ED3552D273D}.Release|x64.Build.0 = Release|x64 + {5E193242-B386-49EF-B592-9ED3552D273D}.Release|x86.ActiveCfg = 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}.Debug|x86.ActiveCfg = Debug|x64 {12E75CB0-4B48-4D7C-BB26-D928F18488C2}.Release|x64.ActiveCfg = Release|x64 {12E75CB0-4B48-4D7C-BB26-D928F18488C2}.Release|x64.Build.0 = Release|x64 + {12E75CB0-4B48-4D7C-BB26-D928F18488C2}.Release|x86.ActiveCfg = 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}.Debug|x86.ActiveCfg = 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 + {9AE989AF-F6B7-45D7-983B-A7629FBF8F1B}.Release|x86.ActiveCfg = Release|x64 + {8331c4e1-6c17-481e-9b4f-232db77d95cb}.Debug|x64.ActiveCfg = Debug|x64 + {8331c4e1-6c17-481e-9b4f-232db77d95cb}.Debug|x64.Build.0 = Debug|x64 + {8331c4e1-6c17-481e-9b4f-232db77d95cb}.Debug|x86.ActiveCfg = Debug|x64 + {8331c4e1-6c17-481e-9b4f-232db77d95cb}.Release|x64.ActiveCfg = Release|x64 + {8331c4e1-6c17-481e-9b4f-232db77d95cb}.Release|x64.Build.0 = Release|x64 + {8331c4e1-6c17-481e-9b4f-232db77d95cb}.Release|x86.ActiveCfg = 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}.Debug|x86.ActiveCfg = Debug|x64 {0C72846A-BCFA-4F75-91AF-D9F5CA01E6B2}.Release|x64.ActiveCfg = Release|x64 {0C72846A-BCFA-4F75-91AF-D9F5CA01E6B2}.Release|x64.Build.0 = Release|x64 + {0C72846A-BCFA-4F75-91AF-D9F5CA01E6B2}.Release|x86.ActiveCfg = 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}.Debug|x86.ActiveCfg = Debug|x64 {64E94A9E-B3AE-47EF-8F5A-0356B0E6B9DA}.Release|x64.ActiveCfg = Release|x64 {64E94A9E-B3AE-47EF-8F5A-0356B0E6B9DA}.Release|x64.Build.0 = Release|x64 + {64E94A9E-B3AE-47EF-8F5A-0356B0E6B9DA}.Release|x86.ActiveCfg = Release|x64 {F93128A5-B344-4662-BFB8-B4CAECF1FE52}.Debug|x64.ActiveCfg = Debug|x64 {F93128A5-B344-4662-BFB8-B4CAECF1FE52}.Debug|x64.Build.0 = Debug|x64 + {F93128A5-B344-4662-BFB8-B4CAECF1FE52}.Debug|x86.ActiveCfg = Debug|x64 {F93128A5-B344-4662-BFB8-B4CAECF1FE52}.Release|x64.ActiveCfg = Release|x64 {F93128A5-B344-4662-BFB8-B4CAECF1FE52}.Release|x64.Build.0 = Release|x64 + {F93128A5-B344-4662-BFB8-B4CAECF1FE52}.Release|x86.ActiveCfg = Release|x64 {823CF77E-8120-41B1-9B25-823A79C93198}.Debug|x64.ActiveCfg = Debug|x64 {823CF77E-8120-41B1-9B25-823A79C93198}.Debug|x64.Build.0 = Debug|x64 + {823CF77E-8120-41B1-9B25-823A79C93198}.Debug|x86.ActiveCfg = Debug|x64 {823CF77E-8120-41B1-9B25-823A79C93198}.Release|x64.ActiveCfg = Release|x64 {823CF77E-8120-41B1-9B25-823A79C93198}.Release|x64.Build.0 = Release|x64 + {823CF77E-8120-41B1-9B25-823A79C93198}.Release|x86.ActiveCfg = Release|x64 {320AF64E-C4FC-4A97-A7D1-F8233A8A44C7}.Debug|x64.ActiveCfg = Debug|x64 {320AF64E-C4FC-4A97-A7D1-F8233A8A44C7}.Debug|x64.Build.0 = Debug|x64 + {320AF64E-C4FC-4A97-A7D1-F8233A8A44C7}.Debug|x86.ActiveCfg = Debug|x64 {320AF64E-C4FC-4A97-A7D1-F8233A8A44C7}.Release|x64.ActiveCfg = Release|x64 {320AF64E-C4FC-4A97-A7D1-F8233A8A44C7}.Release|x64.Build.0 = Release|x64 + {320AF64E-C4FC-4A97-A7D1-F8233A8A44C7}.Release|x86.ActiveCfg = Release|x64 {7C8C60FA-92E3-4102-80AB-A4468C4FCD2F}.Debug|x64.ActiveCfg = Debug|x64 {7C8C60FA-92E3-4102-80AB-A4468C4FCD2F}.Debug|x64.Build.0 = Debug|x64 + {7C8C60FA-92E3-4102-80AB-A4468C4FCD2F}.Debug|x86.ActiveCfg = Debug|x64 {7C8C60FA-92E3-4102-80AB-A4468C4FCD2F}.Release|x64.ActiveCfg = Release|x64 {7C8C60FA-92E3-4102-80AB-A4468C4FCD2F}.Release|x64.Build.0 = Release|x64 + {7C8C60FA-92E3-4102-80AB-A4468C4FCD2F}.Release|x86.ActiveCfg = Release|x64 + {46F4FFAF-79FD-45D9-A2C5-9E16B46787C5}.Debug|x64.ActiveCfg = Debug|x64 + {46F4FFAF-79FD-45D9-A2C5-9E16B46787C5}.Debug|x64.Build.0 = Debug|x64 + {46F4FFAF-79FD-45D9-A2C5-9E16B46787C5}.Debug|x86.ActiveCfg = Debug|x64 + {46F4FFAF-79FD-45D9-A2C5-9E16B46787C5}.Release|x64.ActiveCfg = Release|x64 + {46F4FFAF-79FD-45D9-A2C5-9E16B46787C5}.Release|x64.Build.0 = Release|x64 + {46F4FFAF-79FD-45D9-A2C5-9E16B46787C5}.Release|x86.ActiveCfg = Release|x64 {DD3A2820-F54C-42F3-AA0E-DC95D57481B7}.Debug|x64.ActiveCfg = Debug|x64 {DD3A2820-F54C-42F3-AA0E-DC95D57481B7}.Debug|x64.Build.0 = Debug|x64 + {DD3A2820-F54C-42F3-AA0E-DC95D57481B7}.Debug|x86.ActiveCfg = Debug|x64 {DD3A2820-F54C-42F3-AA0E-DC95D57481B7}.Release|x64.ActiveCfg = Release|x64 {DD3A2820-F54C-42F3-AA0E-DC95D57481B7}.Release|x64.Build.0 = Release|x64 {FE305987-2B15-4EDA-9FC8-32D81AAE1543}.Debug|x64.ActiveCfg = Debug|x64 {FE305987-2B15-4EDA-9FC8-32D81AAE1543}.Debug|x64.Build.0 = Debug|x64 {FE305987-2B15-4EDA-9FC8-32D81AAE1543}.Release|x64.ActiveCfg = Release|x64 {FE305987-2B15-4EDA-9FC8-32D81AAE1543}.Release|x64.Build.0 = Release|x64 + {DD3A2820-F54C-42F3-AA0E-DC95D57481B7}.Release|x86.ActiveCfg = Release|x64 + {36CF524A-8214-404C-8E6B-B5DEC1FDADF9}.Debug|x64.ActiveCfg = Debug|x64 + {36CF524A-8214-404C-8E6B-B5DEC1FDADF9}.Debug|x64.Build.0 = Debug|x64 + {36CF524A-8214-404C-8E6B-B5DEC1FDADF9}.Debug|x86.ActiveCfg = Debug|x64 + {36CF524A-8214-404C-8E6B-B5DEC1FDADF9}.Release|x64.ActiveCfg = Release|x64 + {36CF524A-8214-404C-8E6B-B5DEC1FDADF9}.Release|x64.Build.0 = Release|x64 + {36CF524A-8214-404C-8E6B-B5DEC1FDADF9}.Release|x86.ActiveCfg = Release|x64 + {38DCD378-83FE-4C42-8916-1C477A35F65F}.Debug|x64.ActiveCfg = Debug|x64 + {38DCD378-83FE-4C42-8916-1C477A35F65F}.Debug|x64.Build.0 = Debug|x64 + {38DCD378-83FE-4C42-8916-1C477A35F65F}.Debug|x86.ActiveCfg = Debug|x64 + {38DCD378-83FE-4C42-8916-1C477A35F65F}.Release|x64.ActiveCfg = Release|x64 + {38DCD378-83FE-4C42-8916-1C477A35F65F}.Release|x64.Build.0 = Release|x64 + {38DCD378-83FE-4C42-8916-1C477A35F65F}.Release|x86.ActiveCfg = Release|x64 + {149B6A9D-40FB-4FDF-BC0C-6B68E3BC4938}.Debug|x64.ActiveCfg = Debug|x64 + {149B6A9D-40FB-4FDF-BC0C-6B68E3BC4938}.Debug|x64.Build.0 = Debug|x64 + {149B6A9D-40FB-4FDF-BC0C-6B68E3BC4938}.Debug|x86.ActiveCfg = Debug|Win32 + {149B6A9D-40FB-4FDF-BC0C-6B68E3BC4938}.Debug|x86.Build.0 = Debug|Win32 + {149B6A9D-40FB-4FDF-BC0C-6B68E3BC4938}.Release|x64.ActiveCfg = Release|x64 + {149B6A9D-40FB-4FDF-BC0C-6B68E3BC4938}.Release|x64.Build.0 = Release|x64 + {149B6A9D-40FB-4FDF-BC0C-6B68E3BC4938}.Release|x86.ActiveCfg = Release|Win32 + {149B6A9D-40FB-4FDF-BC0C-6B68E3BC4938}.Release|x86.Build.0 = Release|Win32 + {E4E9FA4F-E9DD-4483-9140-2FD472C28F1D}.Debug|x64.ActiveCfg = Debug|x64 + {E4E9FA4F-E9DD-4483-9140-2FD472C28F1D}.Debug|x64.Build.0 = Debug|x64 + {E4E9FA4F-E9DD-4483-9140-2FD472C28F1D}.Debug|x86.ActiveCfg = Debug|x64 + {E4E9FA4F-E9DD-4483-9140-2FD472C28F1D}.Release|x64.ActiveCfg = Release|x64 + {E4E9FA4F-E9DD-4483-9140-2FD472C28F1D}.Release|x64.Build.0 = Release|x64 + {E4E9FA4F-E9DD-4483-9140-2FD472C28F1D}.Release|x86.ActiveCfg = Release|x64 +>>>>>>> main EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/secret-device-adapters-commit b/secret-device-adapters-commit index 068175143..add1c4707 100644 --- a/secret-device-adapters-commit +++ b/secret-device-adapters-commit @@ -1 +1 @@ -e413f51e93b71d5513dc4febc950266c054832f0 +574f3c801e6df2b0fa761298ddc32ac64dce58ce diff --git a/tools/check-utf8.sh b/tools/check-utf8.sh new file mode 100755 index 000000000..e6253057f --- /dev/null +++ b/tools/check-utf8.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +invalid_found=0 + +# We would like to redirect iconv output to /dev/null, but the macOS version of +# iconv has problems with this ("iconv: iconv(): Inappropriate ioctl for +# device", sporadically for particular inputs). So use a regular file instead. +tmpfile=/tmp/check-utf8-temp +trap "rm -f $tmpfile" EXIT + +for file in $(git ls-files | grep -E '\.(cpp|h|txt)$'); do + if ! iconv -f UTF-8 "$file" >$tmpfile; then + echo "Not valid UTF-8: $file" >&2 + invalid_found=1 + fi +done + +[ $invalid_found = 0 ] && echo "All checked files are valid UTF-8" >&2 + +exit $invalid_found