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/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 untersttzt! + // 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 fr 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; // 20000s = 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,2m (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/100C + 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.0C and 5 => 0.5C and -124 = -12.4C) + 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.1C, e.g. 10 => 1.0C (only for #CM_FAN_AUTOMATIC) - i16 wFanThreshTempMax; //!< maximum supported threshold temperature in 0.1C (only for #CM_FAN_AUTOMATIC) - i16 wFanThreshTempStep; //!< minimum supported threshold temperature in 0.1C(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.0C) 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.1C (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/ASITiger/ASIPLogic.cpp b/DeviceAdapters/ASITiger/ASIPLogic.cpp index e77912264..502f1628b 100644 --- a/DeviceAdapters/ASITiger/ASIPLogic.cpp +++ b/DeviceAdapters/ASITiger/ASIPLogic.cpp @@ -74,14 +74,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() @@ -157,7 +159,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); @@ -351,61 +353,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"); + } } @@ -419,6 +431,7 @@ int CPLogic::SetOpen(bool open) { 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 { @@ -442,32 +455,38 @@ 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; - } + 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; } @@ -807,6 +826,9 @@ 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); @@ -885,8 +907,11 @@ int CPLogic::OnAdvancedProperties(MM::PropertyBase* pProp, MM::ActionType eAct) // restore refresh setting SetProperty(g_RefreshPropValsPropertyName, refreshPropValsStr); + + initialized_ = true; } } + return DEVICE_OK; } diff --git a/DeviceAdapters/ASITiger/ASIPLogic.h b/DeviceAdapters/ASITiger/ASIPLogic.h index 4f8125929..6a9a18507 100644 --- a/DeviceAdapters/ASITiger/ASIPLogic.h +++ b/DeviceAdapters/ASITiger/ASIPLogic.h @@ -82,7 +82,7 @@ class CPLogic : public ASIPeripheralBase unsigned int numCells_; unsigned int currentPosition_; // cached value of current position // static const int NUM_CELLS = 16; - bool useAsdiSPIMShutter_; // super-set of useAs4ChShutter_ + bool useAsdiSPIMShutter_; // used together with either useAs4ChShutter_ or useAs7ChShutter_, takes into account address 41 backplane (TTL0 for CameraA) bool useAs4ChShutter_; bool useAs7ChShutter_; bool shutterOpen_; diff --git a/DeviceAdapters/ASITiger/ASITiger.h b/DeviceAdapters/ASITiger/ASITiger.h index 591adad76..c37b23c27 100644 --- a/DeviceAdapters/ASITiger/ASITiger.h +++ b/DeviceAdapters/ASITiger/ASITiger.h @@ -551,6 +551,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/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, Childrens 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 Childrens 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 = "HBNER 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 HBNER Group company"; +const char * g_DeviceVendorName = "Cobolt - a HÜBNER Group company"; const char* const g_Property_Port_None = "None"; 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 Mrzhauser 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..55414a544 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(); @@ -144,19 +144,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 +166,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 +187,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 +209,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 +226,7 @@ int ClassGalaxy::Initialize() } } - //ɫж + //颜色判断 gxstring strValue = ""; if (m_objDevicePtr->GetRemoteFeatureControl()->IsImplemented("PixelColorFilter")) { @@ -243,7 +243,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 +262,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 +290,7 @@ int ClassGalaxy::Initialize() //end of Sensor size long bytes = (long)(height->GetValue() * width->GetValue() * 4); - //20221020ΰ + //20221020赵伟甫 //Buffer4ContinuesShot = malloc(bytes); @@ -309,7 +309,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 +388,7 @@ int ClassGalaxy::Initialize() } SetAllowedValues("TriggerSource", LSPVals); } - //20230217֡ʹ + //20230217设置期望帧率使能 if (m_objDevicePtr->GetRemoteFeatureControl()->IsImplemented("AcquisitionFrameRateMode")) { CEnumFeaturePointer AdjFrameRateMode = m_objFeatureControlPtr->GetEnumFeature("AcquisitionFrameRateMode"); @@ -409,7 +409,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 +455,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 +468,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 +500,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 +524,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 +574,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 +595,10 @@ int ClassGalaxy::Shutdown() } try { - //ر + //关闭流对象 m_objStreamPtr->Close(); - //ر豸 + //关闭设备 m_objDevicePtr->Close(); } catch (CGalaxyException& e) @@ -625,7 +625,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 +640,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 +663,7 @@ int ClassGalaxy::SnapImage() string a = e.what(); AddToLog(e.what()); m_objFeatureControlPtr->GetCommandFeature("AcquisitionStop")->Execute(); - //رɼ + //关闭流层采集 m_objStreamPtr->StopGrab(); return DEVICE_ERR; } @@ -671,7 +671,7 @@ int ClassGalaxy::SnapImage() { AddToLog(e.what()); m_objFeatureControlPtr->GetCommandFeature("AcquisitionStop")->Execute(); - //رɼ + //关闭流层采集 m_objStreamPtr->StopGrab(); return DEVICE_ERR; } @@ -690,9 +690,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; - //ȷIJͼʽ + //明确相机的采图格式 emValidBits = GetBestValudBit(objImageDataPointer->GetPixelFormat()); if (found != std::string::npos) @@ -707,10 +707,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()); @@ -761,7 +761,7 @@ int ClassGalaxy::StartSequenceAcquisition(long /* numImages */, double /* interv //camera_->StartGrabbing(numImages, GrabStrategy_OneByOne, GrabLoop_ProvidedByInstantCamera); m_objFeatureControlPtr->GetCommandFeature("AcquisitionStart")->Execute(); - //ɼ + //开启流层采集 m_objStreamPtr->StartGrab(); int ret = GetCoreCallback()->PrepareForAcq(this); @@ -776,7 +776,7 @@ int ClassGalaxy::StartSequenceAcquisition(long /* numImages */, double /* interv string a = e.what(); AddToLog(e.what()); m_objFeatureControlPtr->GetCommandFeature("AcquisitionStop")->Execute(); - //رɼ + //关闭流层采集 m_objStreamPtr->StopGrab(); return DEVICE_ERR; } @@ -784,7 +784,7 @@ int ClassGalaxy::StartSequenceAcquisition(long /* numImages */, double /* interv { AddToLog(e.what()); m_objFeatureControlPtr->GetCommandFeature("AcquisitionStop")->Execute(); - //رɼ + //关闭流层采集 m_objStreamPtr->StopGrab(); return DEVICE_ERR; } @@ -797,7 +797,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(); } @@ -813,7 +813,7 @@ int ClassGalaxy::StartSequenceAcquisition(double /* interval_ms */) { } //camera_->StartGrabbing(numImages, GrabStrategy_OneByOne, GrabLoop_ProvidedByInstantCamera); m_objFeatureControlPtr->GetCommandFeature("AcquisitionStart")->Execute(); - //ɼ + //开启流层采集 m_objStreamPtr->StartGrab(); AddToLog("StartSequenceAcquisition"); } @@ -822,7 +822,7 @@ int ClassGalaxy::StartSequenceAcquisition(double /* interval_ms */) { string a = e.what(); AddToLog(e.what()); m_objFeatureControlPtr->GetCommandFeature("AcquisitionStop")->Execute(); - //رɼ + //关闭流层采集 m_objStreamPtr->StopGrab(); return DEVICE_ERR; } @@ -830,7 +830,7 @@ int ClassGalaxy::StartSequenceAcquisition(double /* interval_ms */) { { AddToLog(e.what()); m_objFeatureControlPtr->GetCommandFeature("AcquisitionStop")->Execute(); - //رɼ + //关闭流层采集 m_objStreamPtr->StopGrab(); return DEVICE_ERR; } @@ -863,14 +863,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 +886,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; - //ȷIJͼʽ + //明确相机的采图格式 emValidBits = GetBestValudBit(objImageDataPointer->GetPixelFormat()); if (colorCamera_) @@ -906,22 +906,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 +980,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 +989,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 +1123,7 @@ int ClassGalaxy::OnHeight(MM::PropertyBase* pProp, MM::ActionType eAct) if (Isgrabbing) { m_objFeatureControlPtr->GetCommandFeature("AcquisitionStop")->Execute(); - //رɼ + //关闭流层采集 m_objStreamPtr->StopGrab(); //camera_->StopGrabbing(); } @@ -1181,7 +1181,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 +1192,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 +1262,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 +1413,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 +1641,7 @@ int ClassGalaxy::ClearROI() void ClassGalaxy::ReduceImageSize(int64_t Width, int64_t Height) { - // + //待定 return ; } @@ -1666,7 +1666,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(); - //ȷIJͼʽ + //明确相机的采图格式 emValidBits = GetBestValudBit(objImageDataPointer->GetPixelFormat()); if (emValidBits!= GX_BIT_0_7) { @@ -1694,19 +1694,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; - //ȷIJͼʽ + //明确相机的采图格式 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 +1740,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 +1761,7 @@ void ClassGalaxy::RG10ToRGB24Packed(void* pRGB24Bufdest, CImageDataPointer& objI GX_VALID_BIT_LIST emValidBits = GX_BIT_0_7; - //ȷIJͼʽ + //明确相机的采图格式 emValidBits = GetBestValudBit(objImageDataPointer->GetPixelFormat()); BYTE* pRGB24Buf2 = (BYTE*)objImageDataPointer->ConvertToRGB24(emValidBits, GX_RAW2RGB_NEIGHBOUR, false); @@ -1820,7 +1820,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 +1829,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; @@ -1847,10 +1847,10 @@ CircularBufferInserter::CircularBufferInserter(ClassGalaxy* dev) : {} //--------------------------------------------------------------------------------- /** - \brief ɼص - \param objImageDataPointer ͼ - \param pFrame û - \return + \brief 采集回调函数 + \param objImageDataPointer 图像处理参数 + \param pFrame 用户参数 + \return 无 */ //---------------------------------------------------------------------------------- void CircularBufferInserter::DoOnImageCaptured(CImageDataPointer& objImageDataPointer, void* pUserParam) @@ -1859,7 +1859,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 +1867,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 @@ -1885,7 +1885,7 @@ void CircularBufferInserter::DoOnImageCaptured(CImageDataPointer& objImageDataPo } else if (dev_->colorCamera_) { - //ɫעȫת8λRGB + //彩色,注意这里全部转成8位RGB if (dev_->__IsPixelFormat8(pixelFormat_gx)) { dev_->RG8ToRGB24Packed(dev_->imgBuffer_, objImageDataPointer); @@ -1905,7 +1905,7 @@ void CircularBufferInserter::DoOnImageCaptured(CImageDataPointer& objImageDataPo } else { - dev_->AddToLog("֡"); + dev_->AddToLog("残帧"); } @@ -1932,7 +1932,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 +1972,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 +1993,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 +2005,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 +2035,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 +2049,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 +2070,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 +2082,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 +2112,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 +2126,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(); // дļij - DWORD dwBytesRead = 0; // ļȡij + 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 +2151,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..d9e614883 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; @@ -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,8 +190,8 @@ class MODULE_API ClassGalaxy : public CCameraBase std::string TriggerDelay_; std::string TriggerFilterRaisingEdge_; - BITMAPINFO* m_pBmpInfo; /// ImgBuffer img_; bool initialized_; - //ͼת + //图像转换 CGXImageFormatConvertPointer TestFormatConvertPtr; int64_t __GetStride(int64_t nWidth, bool bIsColor); @@ -210,9 +210,9 @@ class MODULE_API ClassGalaxy : public CCameraBase 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; 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 trs important pour les valeurs ngatives + 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 Mrzhauser 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/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 Lvset, 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 10s 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, its 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/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/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 encoye 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/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 Jlich +// 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 + 5C"); + 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 +/- 5C 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 +/- 6C of the calibration temperature after it was once inside the +/- 5C 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-measurement."); + 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/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/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 5122022 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 9122022 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 5122022 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 472022@@@t.abed - int DriveCommadProcessZ(double position);// Added 472022 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 472022 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 = "HBNER 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..f2fa6dcad 100755 --- a/DeviceAdapters/TUCam/MMTUCam.cpp +++ b/DeviceAdapters/TUCam/MMTUCam.cpp @@ -2850,7 +2850,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_)); 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 Strhl +//// 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 Strhl +//// 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/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, Mnster, 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ͼϵ, ASISetStartPosbin1 +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ı, startposbinĻģҲҪձı + 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ľ㣬õROIROI +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 managerbinʱɳֹͣãpropertybinֹͣ´Բֹͣʱ + 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ı, startposbinĻģҲҪձı + 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ʱˢlabelstate +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-dewEFW \ 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/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 weve 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..5e898c7e4 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"]) @@ -538,6 +547,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 6470da93d..effe1da55 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()); diff --git a/MMDevice/MMDeviceConstants.h b/MMDevice/MMDeviceConstants.h index e131c35f9..4bfb5244d 100644 --- a/MMDevice/MMDeviceConstants.h +++ b/MMDevice/MMDeviceConstants.h @@ -150,6 +150,7 @@ namespace MM { const char* const g_Keyword_Pressure_Measured = "Pressure Measured"; // 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 da0440ec5..d69eacc52 100644 --- a/micromanager.sln +++ b/micromanager.sln @@ -481,12 +481,19 @@ 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}") = "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 Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -1461,10 +1468,22 @@ Global {7C8C60FA-92E3-4102-80AB-A4468C4FCD2F}.Debug|x64.Build.0 = Debug|x64 {7C8C60FA-92E3-4102-80AB-A4468C4FCD2F}.Release|x64.ActiveCfg = Release|x64 {7C8C60FA-92E3-4102-80AB-A4468C4FCD2F}.Release|x64.Build.0 = 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}.Release|x64.ActiveCfg = Release|x64 + {46F4FFAF-79FD-45D9-A2C5-9E16B46787C5}.Release|x64.Build.0 = 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}.Release|x64.ActiveCfg = Release|x64 {DD3A2820-F54C-42F3-AA0E-DC95D57481B7}.Release|x64.Build.0 = 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}.Release|x64.ActiveCfg = Release|x64 + {38DCD378-83FE-4C42-8916-1C477A35F65F}.Release|x64.Build.0 = 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}.Release|x64.ActiveCfg = Release|x64 + {36CF524A-8214-404C-8E6B-B5DEC1FDADF9}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/secret-device-adapters-commit b/secret-device-adapters-commit index 068175143..3eecc584d 100644 --- a/secret-device-adapters-commit +++ b/secret-device-adapters-commit @@ -1 +1 @@ -e413f51e93b71d5513dc4febc950266c054832f0 +fa7a9069a0a99f32f283de8767b452339afb7775 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