20"""A microscope interface to Linkam stages.
22This module requires the LinkamSDK library and a license file, available
23from Linkam Scientific Instruments.
25Currently, this module supports on the the correlative microscopy stage,
26but should be readily extensible to support other Linkam stages.
30 This module does not run correctly
with python optimisations
in
31 use. When invoked
with `python -O`, there seem to be issues
with
32 accessing ctypes objects.
34* `get_status()` throws `AttributeError`
"c_ulonglong has no attribute 'flags'";
35* `get_id` returns an empty string,
not the device serial number.
44from ctypes import POINTER, byref
45from enum import Enum, IntEnum
51_max_version_length = 20
53# Typedefs from C headers
54_int8_t = ctypes.c_int8
55_uint8_t = ctypes.c_uint8
56_int16_t = ctypes.c_int16
57_uint16_t = ctypes.c_uint16
58_int32_t = ctypes.c_int32
59_uint32_t = ctypes.c_uint32
60_int64_t = ctypes.c_int64
61_uint64_t = ctypes.c_uint64
62_float64_t = ctypes.c_double
63_float32_t = ctypes.c_float
64_float64_t = ctypes.c_double
65_float32_t = ctypes.c_float
66_CommsHandle = ctypes.c_uint64
69class _CommsInfo(ctypes.Structure):
70 """CommsInfo struct from C headers"""
72 _fields_ = [(
"type", ctypes.c_uint), (
"info", ctypes.c_char * 124)]
76 """Provide a view of the info field so that its subfields can be accessed"""
80 ptype = POINTER(_SerialCommsInfo)
82 ptype = POINTER(_USBCommsInfo)
87 offset = getattr(_CommsInfo,
"info").offset
88 return _USBCommsInfo.from_buffer(self, offset)
92 """SerialCommsInfo struct from C headers"""
95 (
"port", ctypes.c_char * 64),
96 (
"baudrate", ctypes.c_uint32),
97 (
"bytesize", ctypes.c_uint),
98 (
"parity", ctypes.c_uint),
99 (
"stopbits", ctypes.c_uint),
100 (
"flowcontrol", ctypes.c_uint),
101 (
"timeout", ctypes.c_uint32),
102 (
"padding", ctypes.c_uint8 * 36),
107 """USBCommsInfo struct from C headers"""
110 (
"vendorID", ctypes.c_uint16),
111 (
"productID", ctypes.c_uint16),
112 (
"serialNumber", ctypes.c_char * 17),
113 (
"padding", ctypes.c_uint8 * 83),
118 """StageGroup enum from C headers"""
124 DifferentialScanningCalorimetry = 0x0003
129 CambridgeShearingSystem = 0x0008
130 TemperatureControlled = 0x0009
132 CorrelativeMicroscopy = 0x000B
133 IndiumTinOxideWarm = 0x000C
134 TemperatureControlledVacuum = 0x000D
135 TensileTestV2 = 0x000E
136 DifferentialScanningCalorimetryV2 = 0x000F
137 FreezeDryingVialSystem = 0x0010
142 """StageConfig.flags struct from C headers"""
145 (
"standardStage", ctypes.c_uint, 1),
146 (
"highTempStage", ctypes.c_uint, 1),
147 (
"peltierStage", ctypes.c_uint, 1),
148 (
"gradedStage", ctypes.c_uint, 1),
149 (
"tensileStage", ctypes.c_uint, 1),
150 (
"dscStage", ctypes.c_uint, 1),
151 (
"warmStage", ctypes.c_uint, 1),
152 (
"itoStage", ctypes.c_uint, 1),
153 (
"css450Stage", ctypes.c_uint, 1),
154 (
"correlativeStage", ctypes.c_uint, 1),
155 (
"unused10", ctypes.c_uint, 1),
156 (
"unused11", ctypes.c_uint, 1),
157 (
"unused12", ctypes.c_uint, 1),
158 (
"unused13", ctypes.c_uint, 1),
159 (
"unused14", ctypes.c_uint, 1),
160 (
"unused15", ctypes.c_uint, 1),
161 (
"unused16", ctypes.c_uint, 1),
162 (
"unused17", ctypes.c_uint, 1),
163 (
"unused18", ctypes.c_uint, 1),
164 (
"unused19", ctypes.c_uint, 1),
165 (
"unused20", ctypes.c_uint, 1),
166 (
"coolingManual", ctypes.c_uint, 1),
167 (
"coolingAutomatic", ctypes.c_uint, 1),
168 (
"coolingDual", ctypes.c_uint, 1),
169 (
"coolingDualSpeedIndependent", ctypes.c_uint, 1),
170 (
"unused25", ctypes.c_uint, 1),
171 (
"heater1", ctypes.c_uint, 1),
172 (
"heater1TempCtrl", ctypes.c_uint, 1),
173 (
"heater1TempCtrlProbe", ctypes.c_uint, 1),
174 (
"unused29", ctypes.c_uint, 1),
175 (
"unused30", ctypes.c_uint, 1),
176 (
"unused31", ctypes.c_uint, 1),
177 (
"unused32", ctypes.c_uint, 1),
178 (
"unused33", ctypes.c_uint, 1),
179 (
"unused34", ctypes.c_uint, 1),
180 (
"unused35", ctypes.c_uint, 1),
181 (
"heater2", ctypes.c_uint, 1),
182 (
"heater12IndependentLimits", ctypes.c_uint, 1),
183 (
"unused38", ctypes.c_uint, 1),
184 (
"unused39", ctypes.c_uint, 1),
185 (
"unused40", ctypes.c_uint, 1),
186 (
"unused41", ctypes.c_uint, 1),
187 (
"unused42", ctypes.c_uint, 1),
188 (
"unused43", ctypes.c_uint, 1),
189 (
"unused44", ctypes.c_uint, 1),
190 (
"unused45", ctypes.c_uint, 1),
191 (
"waterCoolingSensorFitted", ctypes.c_uint, 1),
192 (
"home", ctypes.c_uint, 1),
193 (
"supportsVacuum", ctypes.c_uint, 1),
194 (
"motorX", ctypes.c_uint, 1),
195 (
"motorY", ctypes.c_uint, 1),
196 (
"motorZ", ctypes.c_uint, 1),
197 (
"supportsHumidity", ctypes.c_uint, 1),
198 (
"unused53", ctypes.c_uint, 1),
199 (
"unused54", ctypes.c_uint, 1),
200 (
"unused55", ctypes.c_uint, 1),
201 (
"unused56", ctypes.c_uint, 1),
202 (
"unused57", ctypes.c_uint, 1),
203 (
"unused58", ctypes.c_uint, 1),
204 (
"unused59", ctypes.c_uint, 1),
205 (
"unused60", ctypes.c_uint, 1),
206 (
"unused61", ctypes.c_uint, 1),
207 (
"unused62", ctypes.c_uint, 1),
208 (
"unused63", ctypes.c_uint, 1),
213 """StageConfig union from C headers."""
215 _fields_ = [(
"flags", _StageConfigFlags), (
"value", _uint64_t)]
219 """CMSStatus.flags struct from C headers"""
222 (
"on", ctypes.c_uint, 1),
223 (
"onNoLN2", ctypes.c_uint, 1),
224 (
"prime", ctypes.c_uint, 1),
225 (
"autoTopUp", ctypes.c_uint, 1),
226 (
"warmingUp", ctypes.c_uint, 1),
227 (
"WarmingUpFromCupboard", ctypes.c_uint, 1),
228 (
"unused6", ctypes.c_uint, 1),
229 (
"unused7", ctypes.c_uint, 1),
230 (
"light", ctypes.c_uint, 1),
231 (
"sampleDewarFillSignal", ctypes.c_uint, 1),
232 (
"mainDewarFillSignal", ctypes.c_uint, 1),
233 (
"unused11", ctypes.c_uint, 1),
234 (
"unused12", ctypes.c_uint, 1),
235 (
"unused13", ctypes.c_uint, 1),
236 (
"unused14", ctypes.c_uint, 1),
237 (
"unused15", ctypes.c_uint, 1),
238 (
"unused16", ctypes.c_uint, 1),
239 (
"unused17", ctypes.c_uint, 1),
240 (
"unused18", ctypes.c_uint, 1),
241 (
"unused19", ctypes.c_uint, 1),
242 (
"unused20", ctypes.c_uint, 1),
243 (
"unused21", ctypes.c_uint, 1),
244 (
"unused22", ctypes.c_uint, 1),
245 (
"unused23", ctypes.c_uint, 1),
246 (
"unused24", ctypes.c_uint, 1),
247 (
"unused25", ctypes.c_uint, 1),
248 (
"unused26", ctypes.c_uint, 1),
249 (
"unused27", ctypes.c_uint, 1),
250 (
"unused28", ctypes.c_uint, 1),
251 (
"unused29", ctypes.c_uint, 1),
252 (
"unused30", ctypes.c_uint, 1),
253 (
"unused31", ctypes.c_uint, 1),
258 """CMSStatus union from C headers"""
260 _fields_ = [(
"flags", _CMSStatusFlags), (
"value", _uint32_t)]
264 """CMSError.flags struct from C headers"""
267 (
"mainSensorOC", ctypes.c_uint, 1),
268 (
"mainSensorOver", ctypes.c_uint, 1),
269 (
"ln2SwitchSensorOC", ctypes.c_uint, 1),
270 (
"ln2SwitchSensorOver", ctypes.c_uint, 1),
271 (
"dewarSensorOC", ctypes.c_uint, 1),
272 (
"dewarSensorOver", ctypes.c_uint, 1),
273 (
"baseSensorOC", ctypes.c_uint, 1),
274 (
"baseSensorOver", ctypes.c_uint, 1),
275 (
"dewarEmpty", ctypes.c_uint, 1),
276 (
"motorPosnError", ctypes.c_uint, 1),
277 (
"unused10", ctypes.c_uint, 1),
278 (
"unused11", ctypes.c_uint, 1),
279 (
"unused12", ctypes.c_uint, 1),
280 (
"unused13", ctypes.c_uint, 1),
281 (
"unused14", ctypes.c_uint, 1),
282 (
"unused15", ctypes.c_uint, 1),
283 (
"unused16", ctypes.c_uint, 1),
284 (
"unused17", ctypes.c_uint, 1),
285 (
"unused18", ctypes.c_uint, 1),
286 (
"unused19", ctypes.c_uint, 1),
287 (
"unused20", ctypes.c_uint, 1),
288 (
"unused21", ctypes.c_uint, 1),
289 (
"unused22", ctypes.c_uint, 1),
290 (
"unused23", ctypes.c_uint, 1),
291 (
"unused24", ctypes.c_uint, 1),
292 (
"unused25", ctypes.c_uint, 1),
293 (
"unused26", ctypes.c_uint, 1),
294 (
"unused27", ctypes.c_uint, 1),
295 (
"unused28", ctypes.c_uint, 1),
296 (
"unused29", ctypes.c_uint, 1),
297 (
"unused30", ctypes.c_uint, 1),
298 (
"unused31", ctypes.c_uint, 1),
303 """CMSError union from C headers"""
305 _fields_ = [(
"flags", _CMSErrorFlags), (
"value", _uint32_t)]
309 """ConnectionStatus.flags structure from C headers"""
312 (
"connected", ctypes.c_uint, 1),
313 (
"errorNoDeviceFound", ctypes.c_uint, 1),
314 (
"errorMultipleDevicesFound", ctypes.c_uint, 1),
315 (
"errorTimeout", ctypes.c_uint, 1),
316 (
"errorHandleRegistrationFailed", ctypes.c_uint, 1),
317 (
"errorAllocationFailed", ctypes.c_uint, 1),
318 (
"errorSerialNumberRequired", ctypes.c_uint, 1),
319 (
"errorAlreadyOpen", ctypes.c_uint, 1),
320 (
"errorPropertiesIncorrect", ctypes.c_uint, 1),
321 (
"errorPortConfig", ctypes.c_uint, 1),
322 (
"errorCommsStreams", ctypes.c_uint, 1),
323 (
"errorUnhandled", ctypes.c_uint, 1),
324 (
"unused12", ctypes.c_uint, 1),
325 (
"unused13", ctypes.c_uint, 1),
326 (
"unused14", ctypes.c_uint, 1),
327 (
"unused15", ctypes.c_uint, 1),
328 (
"unused16", ctypes.c_uint, 1),
329 (
"unused17", ctypes.c_uint, 1),
330 (
"unused18", ctypes.c_uint, 1),
331 (
"unused19", ctypes.c_uint, 1),
332 (
"unused20", ctypes.c_uint, 1),
333 (
"unused21", ctypes.c_uint, 1),
334 (
"unused22", ctypes.c_uint, 1),
335 (
"unused23", ctypes.c_uint, 1),
336 (
"unused24", ctypes.c_uint, 1),
337 (
"unused25", ctypes.c_uint, 1),
338 (
"unused26", ctypes.c_uint, 1),
339 (
"unused27", ctypes.c_uint, 1),
340 (
"unused28", ctypes.c_uint, 1),
341 (
"unused29", ctypes.c_uint, 1),
342 (
"unused30", ctypes.c_uint, 1),
343 (
"unused31", ctypes.c_uint, 1),
348 """ConnectionStatus union from C headers"""
350 _fields_ = [(
"flags", _ConnectionStatusFlags), (
"value", _uint32_t)]
354 """ControllerStatus.flags struct from C headers"""
357 (
"controllerError", ctypes.c_uint, 1),
358 (
"heater1RampSetPoint", ctypes.c_uint, 1),
359 (
"heater1Started", ctypes.c_uint, 1),
360 (
"heater2RampSetPoint", ctypes.c_uint, 1),
361 (
"heater2Started", ctypes.c_uint, 1),
362 (
"vacuumRampSetPoint", ctypes.c_uint, 1),
363 (
"vacuumCtrlStarted", ctypes.c_uint, 1),
364 (
"vacuumValveClosed", ctypes.c_uint, 1),
365 (
"vacuumValveOpen", ctypes.c_uint, 1),
366 (
"humidityRampSetPoint", ctypes.c_uint, 1),
367 (
"humidityCtrlStarted", ctypes.c_uint, 1),
368 (
"lnpCoolingPumpOn", ctypes.c_uint, 1),
369 (
"lnpCoolingPumpAuto", ctypes.c_uint, 1),
370 (
"unused13", ctypes.c_uint, 1),
371 (
"HumidityDesiccantConditioning", ctypes.c_uint, 1),
372 (
"unused15", ctypes.c_uint, 1),
373 (
"unused16", ctypes.c_uint, 1),
374 (
"unused17", ctypes.c_uint, 1),
375 (
"unused18", ctypes.c_uint, 1),
376 (
"unused19", ctypes.c_uint, 1),
377 (
"unused20", ctypes.c_uint, 1),
378 (
"unused21", ctypes.c_uint, 1),
379 (
"unused22", ctypes.c_uint, 1),
380 (
"unused23", ctypes.c_uint, 1),
381 (
"unused24", ctypes.c_uint, 1),
382 (
"unused25", ctypes.c_uint, 1),
383 (
"unused26", ctypes.c_uint, 1),
384 (
"unused27", ctypes.c_uint, 1),
385 (
"unused28", ctypes.c_uint, 1),
386 (
"unused29", ctypes.c_uint, 1),
387 (
"unused30", ctypes.c_uint, 1),
388 (
"unused31", ctypes.c_uint, 1),
389 (
"unused32", ctypes.c_uint, 1),
390 (
"unused33", ctypes.c_uint, 1),
391 (
"unused34", ctypes.c_uint, 1),
392 (
"unused35", ctypes.c_uint, 1),
393 (
"unused36", ctypes.c_uint, 1),
394 (
"unused37", ctypes.c_uint, 1),
395 (
"unused38", ctypes.c_uint, 1),
396 (
"unused39", ctypes.c_uint, 1),
397 (
"unused40", ctypes.c_uint, 1),
398 (
"motorTravelMinX", ctypes.c_uint, 1),
399 (
"motorTravelMaxX", ctypes.c_uint, 1),
400 (
"motorStoppedX", ctypes.c_uint, 1),
401 (
"motorTravelMinY", ctypes.c_uint, 1),
402 (
"motorTravelMaxY", ctypes.c_uint, 1),
403 (
"motorStoppedY", ctypes.c_uint, 1),
404 (
"motorTravelMinZ", ctypes.c_uint, 1),
405 (
"motorTravelMaxZ", ctypes.c_uint, 1),
406 (
"motorStoppedZ", ctypes.c_uint, 1),
407 (
"sampleCal", ctypes.c_uint, 1),
408 (
"motorDistanceCalTST", ctypes.c_uint, 1),
409 (
"cssRotMotorStopped", ctypes.c_uint, 1),
410 (
"cssGapMotorStopped", ctypes.c_uint, 1),
411 (
"cssLidOn", ctypes.c_uint, 1),
412 (
"cssRefLimit", ctypes.c_uint, 1),
413 (
"cssZeroLimit", ctypes.c_uint, 1),
414 (
"unused57", ctypes.c_uint, 1),
415 (
"unused58", ctypes.c_uint, 1),
416 (
"unused59", ctypes.c_uint, 1),
417 (
"unused60", ctypes.c_uint, 1),
418 (
"unused61", ctypes.c_uint, 1),
419 (
"unused62", ctypes.c_uint, 1),
420 (
"unused63", ctypes.c_uint, 1),
425 """ControllerStatus union from C headers"""
427 _fields_ = [(
"flags", _ControllerStatusFlags), (
"value", _uint64_t)]
431 """MDSStatus.flags struct from C headers"""
434 (
"xMinLimit", ctypes.c_uint, 1),
435 (
"xMaxLimit", ctypes.c_uint, 1),
436 (
"xMoveDone", ctypes.c_uint, 1),
437 (
"yMinLimit", ctypes.c_uint, 1),
438 (
"yMaxLimit", ctypes.c_uint, 1),
439 (
"yMoveDone", ctypes.c_uint, 1),
440 (
"unused6", ctypes.c_uint, 1),
441 (
"unused7", ctypes.c_uint, 1),
442 (
"unused8", ctypes.c_uint, 1),
443 (
"unused9", ctypes.c_uint, 1),
444 (
"unused10", ctypes.c_uint, 1),
445 (
"unused11", ctypes.c_uint, 1),
446 (
"unused12", ctypes.c_uint, 1),
447 (
"unused13", ctypes.c_uint, 1),
448 (
"unused14", ctypes.c_uint, 1),
449 (
"unused15", ctypes.c_uint, 1),
450 (
"unused16", ctypes.c_uint, 1),
451 (
"unused17", ctypes.c_uint, 1),
452 (
"unused18", ctypes.c_uint, 1),
453 (
"unused19", ctypes.c_uint, 1),
454 (
"unused20", ctypes.c_uint, 1),
455 (
"unused21", ctypes.c_uint, 1),
456 (
"unused22", ctypes.c_uint, 1),
457 (
"unused23", ctypes.c_uint, 1),
458 (
"unused24", ctypes.c_uint, 1),
459 (
"unused25", ctypes.c_uint, 1),
460 (
"unused26", ctypes.c_uint, 1),
461 (
"unused27", ctypes.c_uint, 1),
462 (
"unused28", ctypes.c_uint, 1),
463 (
"unused29", ctypes.c_uint, 1),
464 (
"unused30", ctypes.c_uint, 1),
465 (
"unused31", ctypes.c_uint, 1),
470 """MDSStatus union from C headers"""
472 _fields_ = [(
"flags", _MDSStatusFlags), (
"value", _uint32_t)]
476 """ControllerError enum from C headers"""
479 StageCableDisconnected = 1
481 StageTempSensorOpenOverrange = 3
482 LoadPowerOutputVoltageWrong = 4
484 T95OptionBoardWongConfig = 6
485 OptionBoardCableDisconnect = 7
486 LoadPowerIncorrectForStage = 8
487 OptionBoardIncorrectCable = 9
488 OptionBoardSensorOenOverrange = 10
489 T95FanNotWorking = 11
492 CoolingWaterTooWarmNotFlowing = 14
493 CSS450MotorDriveOverTemp = 15
494 CSS450MotorWindingError1 = 16
495 CSS450MotorWindingError2 = 17
499 CMS196ChamberSensorOpen = 21
500 CMS196ChamberSensorOverrange = 22
501 CMS196LN2SwitchSensorOpen = 23
502 CMS196LN2SwitchSensorOverrange = 24
503 CMS196DewarSensorOpen = 25
504 CMS196DewarSensorOverrange = 26
505 CMS196DewarEmpty = 27
506 CMS196BaseSensorOpen = 28
507 CMS196BaseSensorOverrange = 29
508 CMS196MotorPosnError = 30
513 OpenComms = 0x00000001
515 CloseComms = 0x00000002
516 GetControllerConfig = 0x00000003
517 GetControllerError = 0x00000004
518 GetControllerName = 0x00000005
519 GetControllerSerial = 0x00000006
520 GetStatus = 0x00000007
521 GetStageConfig = 0x00000008
522 GetStageSerial = 0x00000009
523 GetStageName = 0x0000000A
524 GetMaxValue = 0x0000000B
525 GetMinValue = 0x0000000C
526 GetResolution = 0x0000000D
527 ApplySampleCals = 0x0000000E
528 SaveSampleCals = 0x0000000F
529 StartHeating = 0x00000010
530 StartVacuum = 0x00000011
531 StartHumidity = 0x00000012
532 StartHumidityDesiccantConditioning = 0x00000013
533 StartMotors = 0x00000014
534 GetValue = 0x00000015
535 SetValue = 0x00000016
536 TstCalibrateDistance = 0x00000017
537 TstSetMode = 0x00000018
538 TstZeroForce = 0x00000019
539 TstZeroPosition = 0x0000001A
540 LnpSetMode = 0x0000001B
541 LnpSetSpeed = 0x0000001C
542 CssApplyValues = 0x0000001D
543 CssCheckValues = 0x0000001E
544 CssGotoReference = 0x0000001F
545 CssSensorCal = 0x00000020
546 CssStartJogGap = 0x00000021
547 CssStartJogRot = 0x00000022
548 EnableLogging = 0x00000023
549 DisableLogging = 0x00000024
550 GetControllerFirmwareVersion = 0x00000025
551 GetControllerHardwareVersion = 0x00000026
552 GetStageFirmwareVersion = 0x00000027
553 GetStageHardwareVersion = 0x00000028
554 GetDataRate = 0x00000029
555 SetDataRate = 0x0000002A
556 GetStageCableLimits = 0x0000002B
557 SendDscGainValues = 0x0000002C
558 SendDscPowerValue = 0x0000002D
559 SendDscBaselinePowerValues = 0x0000002E
560 SendDscTuaConstants = 0x0000002F
561 SetDSCModulationData = 0x00000030
562 GetOptionCardType = 0x00000031
563 GetOptionCardSlot = 0x00000032
564 GetOptionCardName = 0x00000033
565 GetOptionCardSerial = 0x00000034
566 GetOptionCardHardwareVersion = 0x00000035
567 DoesOptionCardSupportSensors = 0x00000036
569 GetOptionCardSensorName = 0x00000037
571 GetOptionCardSensorSerial = 0x00000038
573 GetOptionCardSensorHardwareVersion = 0x00000039
574 GetStageGroup = 0x0000003A
575 HaveInstrumentBusDeviceType = 0x0000003B
576 GetInstrumentBusDeviceName = 0x0000003C
577 GetInstrumentBusDeviceSerial = 0x0000003D
578 GetInstrumentBusDeviceFirmwareVersion = 0x0000003E
579 GetInstrumentBusDeviceHardwareVersion = 0x0000003F
580 GetHumidityControllerSensorName = 0x00000040
581 GetHumidityControllerSensorSerial = 0x00000041
582 GetHumidityControllerSensorHardwareVersion = 0x00000042
583 IsControllerType = 0x00000043
584 GetControllerPSUDetails = 0x00000044
585 SetControllerTriggerSignalEnable = 0x00000045
586 SetControllerTriggerSignalDisable = 0x00000046
587 SetControllerMainsFrequency = 0x00000047
588 InitialiseTriggerSignalPulse = 0x00000048
589 SetTriggerSignalPulse = 0x00000049
590 SetTriggerSignalPluseWidth = 0x0000004A
591 GetProgramState = 0x0000004B
592 GetStageConfiguration = 0x0000004C
593 GetControllerHeaterDetails = 0x0000004D
594 CssSendGapVelocity = 0x0000004E
595 CssSendGapOverride = 0x0000004F
596 CssSendGap = 0x00000050
597 CssSendVelocity = 0x00000051
598 CssSendRate = 0x00000052
599 CssSendFrequency = 0x00000053
600 CssSendStrain = 0x00000054
601 CssSendDirection = 0x00000055
602 CssSendForceStop = 0x00000056
603 CssSendTorque = 0x00000057
604 TstSetCalibrationForce = 0x00000058
605 ForceHeating = 0x00000059
606 ForceCooling = 0x0000005A
607 ForceHold = 0x0000005B
608 GetInstrumentBusDeviceIdent = 0x0000005C
609 GetStageIdent = 0x0000005D
610 GetControllerIdent = 0x0000005F
611 GetConnectionInformation = 0x00000060
612 GetStageHeaterIdent = 0x00000061
617 """ErrorCode enum from C headers"""
620 LibraryNotInitialised = 0xECF00001
621 NoConnectionInfo = 0xECF00002
622 DeviceRegistrationFailed = 0xECF00003
623 DeviceCreationFailure = 0xECF00004
624 SerialCommsInitialisationFailure = 0xECF00005
625 SerialCommsHandshakeFailure = 0xECF00006
626 SerialPortSocketCreationFailure = 0xECF00007
627 SerialPortSocketConfigurationFailure = 0xECF00008
628 SerialCommsRxError = 0xECF00009
629 SerialCommsUnknownRxError = 0xECF0000A
630 CommandBufferLimitReached = 0xECF0000B
631 USBCommsInitialisationFailure = 0xECF0000C
632 USBCommsHandshakeFailure = 0xECF0000D
633 USBPortSocketCreationFailure = 0xECF0000E
634 USBCommsRxError = 0xECF0000F
635 USBCommsUnknownRxError = 0xECF00010
636 USBCommsTxError = 0xECF00011
637 USBCommsUnknownTxError = 0xECF00012
638 SerialCommsTxError = 0xECF00013
639 SerialCommsUnknownTxError = 0xECF00014
644 """StageValueType enum from C headers"""
654 WaterCoolingTemp = 10
659 HumiditySetpoint = 15
669 MotorDrivenStageStatus = 25
670 VacuumBoardUnitOfMeasure = 26
671 VacMotorValveStatus = 27
672 VacMotorValvePos = 28
673 VacMotorValveVel = 29
674 VacMotorValveSetpoint = 30
677 GradedMotorDistanceSetpoint = 33
690 TriggerSignalBlue = 46
691 TriggerSignalGreen = 47
692 TriggerSignalPink = 48
693 TriggerSignalsEnabled = 49
694 TemperatureResolution = 50
697 CmsWarmingHeater = 53
698 CmsSolenoidRefill = 54
699 CmsSampleDewarFillSig = 55
703 RampHoldRemaining = 59
704 CmsMainDewarFillSig = 60
705 CmsCondenserLEDLevel = 61
710 TstMotorDistanceSetpoint = 66
712 TstForceSetpoint = 68
720 CssStrainSetpoint = 76
726 CssDefaultGapRefVel = 82
727 CssDefaultRotRefVel = 83
733 CssRotMotorVelocitySetpoint = 89
734 CssGapMotorVelocitySetpoint = 90
735 CssOptionBoardSensorData = 91
736 RS232OptionBoardSensorEnabled = 92
737 VacuumOptionBoardSensor1Data = 93
738 VacuumOptionBoardSensor1Enabled = 94
739 VacuumOptionBoardSensor2Data = 95
740 VacuumOptionBoardSensor2Enabled = 96
741 VtoOptionBoardEnabled = 97
742 CmsDewarTopTemperature = 98
743 CmsAutoDewarFill = 99
748 DscConstantTerm = 104
755 DscBaselineConstTerm = 111
756 DscBaselinePowerTerm1 = 112
757 DscBaselinePowerTerm2 = 113
758 DscBaselinePowerTerm3 = 114
759 DscBaselinePowerTerm4 = 115
762 DscOptionBoardSensorEnabled = 118
763 TstJawToJawSize = 119
764 TstTableDirection = 120
766 TstStrainEngineeringUnits = 122
767 TstStrainPercentage = 123
768 TstShowAsForceDistance = 124
769 TstCalForceValue = 125
770 TstOptionBoardSensorEnabled = 126
771 TstShowCalbData = 127
777 StageHumidityUnitData = 133
779 MotorXOptionBoardSensorEnabled = 135
780 MotorYOptionBoardSensorEnabled = 136
781 MotorZOptionBoardSensorEnabled = 137
782 MotorVacuumOptionBoardSensorEnabled = 138
783 MotorFDVacuumOptionBoardSensorEnabled = 139
784 MotorTstOptionBoardSensorEnabled = 140
785 MotorGradientOptionBoardSensorEnabled = 141
786 MotorXOptionBoardSensorData = 142
787 MotorYOptionBoardSensorData = 143
788 MotorZOptionBoardSensorData = 144
789 MotorVacuumOptionBoardSensorData = 145
790 MotorFDVacuumOptionBoardSensorData = 146
791 MotorTstOptionBoardSensorData = 147
792 MotorGradientOptionBoardSensorData = 148
793 TtcOptionBoardEnabled = 149
794 TtcOptionBoardSensor1Enabled = 150
795 TtcOptionBoardSensor2Enabled = 151
796 TtcOptionBoardSensor3Enabled = 152
797 DtcOptionBoardSensor1Enabled = 153
798 DtcOptionBoardSensor2Enabled = 154
799 MotorXDefaultSpeed = 155
800 MotorYDefaultSpeed = 156
801 MotorZDefaultSpeed = 157
802 MotorTstDefaultSpeed = 158
803 MotorGsDefaultSpeed = 159
804 MotorVacDefaultSpeed = 160
805 MotorFDVacDefaultSpeed = 161
806 HumidityDryingTimeSetpoint = 162
807 HumiditySwapTimeSetpoint = 163
808 HumidityPipeTempSetpoint = 164
809 HumidityWaterTempSetpoint = 165
810 HumidityDryingTimeLeft = 166
811 HumiditySwapTimeLeft = 167
812 HumidityWaterTemp = 168
813 VtoVideoStandard = 169
814 TriggerSignalPulseWidth = 170
816 VacuumSimulatorPlug = 172
817 PressureSimulatorPlug = 173
822 UsingXenocsStageTestCables = 178
823 UsingXenocsStageTestCableType1 = 179
824 UsingXenocsStageTestCableType2 = 180
825 UsingCalibrationPlug = 181
826 UsingCalibrationCableB = 182
827 UsingCalibrationCableC = 183
828 UsingCalibrationCableA = 184
833 CmsXaxisGridCentre = 189
834 CmsYaxisGridCentre = 190
836 CmsBaseHeaterLimit = 192
838 ManualHumiditySetpoint = 194
839 FDVSColdTrapPumpSpeed = 195
840 FDVSScanMotorPosition = 196
841 ImagingStationBrightness = 197
843 DisableJoyStick = 199
844 InvertJoyStickAxisX = 200
845 InvertJoyStickAxisY = 201
847 FDVSMotorDistanceSetpoint = 203
848 CssDefaultGapChangeVel = 204
853 """Variant union from C headers"""
856 (
"vChar", ctypes.c_char),
857 (
"vUint8", _uint8_t),
858 (
"vUint16", _uint16_t),
859 (
"vUint32", _uint32_t),
860 (
"vUint64", _uint64_t),
862 (
"vInt16", _int16_t),
863 (
"vInt32", _int32_t),
864 (
"vInt64", _int64_t),
865 (
"vFloat32", ctypes.c_float),
866 (
"vFloat64", ctypes.c_double),
867 (
"vPtr", ctypes.c_void_p),
868 (
"vBoolean", ctypes.c_bool),
870 (
"vControllerError", ctypes.c_uint),
871 (
"vControllerStatus", _ControllerStatus),
872 (
"vConnectionStatus", _ConnectionStatus),
875 (
"vStageConfig", _StageConfig),
876 (
"vStageGroup", ctypes.c_uint),
882 (
"vCMSStatus", _CMSStatus),
883 (
"vCMSError", _CMSError),
891 (
"vMDSStatus", _MDSStatus),
896 """Wrap enum variants with their python Enum for convenience"""
898 if name ==
"vStageGroup":
900 elif name ==
"vControllerError":
909_StageValueTypeToVariant = {
910 _StageValueType.MotorDrivenStageStatus:
"vMDSStatus",
911 _StageValueType.VacMotorValveStatus:
"vUint32",
912 _StageValueType.TriggerSignalsEnabled:
"vBoolean",
913 _StageValueType.CmsLight:
"vBoolean",
914 _StageValueType.CmsSampleDewarFillSig:
"vBoolean",
915 _StageValueType.CmsStatus:
"vCMSStatus",
916 _StageValueType.CmsError:
"vCMSError",
917 _StageValueType.CmsMainDewarFillSig:
"vBoolean",
918 _StageValueType.TestMotion:
"vUint16",
919 _StageValueType.MotorFeedbackYX:
"vUint16",
920 _StageValueType.CssMode:
"vUint32",
921 _StageValueType.CssDirn:
"vBoolean",
922 _StageValueType.CssStepDone:
"vBoolean",
923 _StageValueType.CssStepSuccess:
"vBoolean",
924 _StageValueType.CssStatus:
"vUint32",
925 _StageValueType.RS232OptionBoardSensorEnabled:
"vBoolean",
926 _StageValueType.VacuumOptionBoardSensor1Enabled:
"vBoolean",
927 _StageValueType.VacuumOptionBoardSensor2Enabled:
"vBoolean",
928 _StageValueType.VtoOptionBoardEnabled:
"vBoolean",
929 _StageValueType.DscOptionBoardSensorEnabled:
"vBoolean",
930 _StageValueType.TstTableDirection:
"vBoolean",
932 _StageValueType.TstStrainEngineeringUnits:
"vBoolean",
933 _StageValueType.TstShowAsForceDistance:
"vBoolean",
934 _StageValueType.TstOptionBoardSensorEnabled:
"vBoolean",
935 _StageValueType.TstStatus:
"vUint32",
936 _StageValueType.TstTableMode:
"vBoolean",
937 _StageValueType.MotorXOptionBoardSensorEnabled:
"vBoolean",
938 _StageValueType.MotorYOptionBoardSensorEnabled:
"vBoolean",
939 _StageValueType.MotorVacuumOptionBoardSensorEnabled:
"vBoolean",
940 _StageValueType.MotorFDVacuumOptionBoardSensorEnabled:
"vBoolean",
941 _StageValueType.MotorFDVacuumOptionBoardSensorEnabled:
"vBoolean",
942 _StageValueType.MotorGradientOptionBoardSensorEnabled:
"vBoolean",
947 """Base class for connecting to Linkam SDK devices.
949 This class deals with SDK initialisation and setting callbacks to
950 handle SDK events. It maintains a map of SDK handle to device instance
951 so that SDK events result
in updates to the correct instance.
966 """Fetch the SDK version."""
967 b = ctypes.create_string_buffer(_max_version_length)
968 __class__._lib.linkamGetVersion(b, _max_version_length)
973 """Initialise the SDK and set up event callbacks"""
975 __class__._lib = ctypes.WinDLL(
"LinkamSDK.dll")
978 __class__._lib = ctypes.CDLL(
"libLinkamSDK.so")
979 _lib = __class__._lib
980 """Initialise the SDK, and create and set the callbacks."""
988 os.path.dirname(microscope.abc.__file__),
989 os.path.dirname(__file__),
993 lskpath = os.path.join(p,
"Linkam.lsk")
994 if _lib.linkamInitialiseSDK(sdk_log, lskpath.encode(),
True) == 1:
998 "No linkam license file (Linkam.lsk) found in %s." % lpaths
1002 cfunc = ctypes.CFUNCTYPE(_uint32_t, _CommsHandle, _ControllerStatus)(
1003 __class__._on_new_value
1005 _lib.linkamSetCallbackNewValue(cfunc)
1006 __class__._callbacks[__class__._on_new_value] = cfunc
1008 cfunc = ctypes.CFUNCTYPE(
None, _CommsHandle)(__class__._on_connect)
1009 _lib.linkamSetCallbackControllerConnected(cfunc)
1010 __class__._callbacks[__class__._on_connect] = cfunc
1012 cfunc = ctypes.CFUNCTYPE(
None, _CommsHandle)(__class__._on_disconnect)
1013 _lib.linkamSetCallbackControllerDisconnected(cfunc)
1014 __class__._callbacks[__class__._on_disconnect] = cfunc
1016 cfunc = ctypes.CFUNCTYPE(
None, _CommsHandle, _uint32_t)(
1019 _lib.linkamSetCallbackError(cfunc)
1020 __class__._callbacks[__class__._on_error] = cfunc
1023 def _on_new_value(cls, h: _CommsHandle, status: _ControllerStatus):
1024 """NewValue callback"""
1028 stage._update_status(status)
1032 def _on_error(cls, h: _CommsHandle, errcode: _uint32_t):
1033 """Error event callback"""
1039 ErrorCode.USBCommsTxError,
1040 ErrorCode.USBCommsRxError,
1041 ErrorCode.SerialCommsTxError,
1042 ErrorCode.SerialCommsRxError,
1045 stage._reopen_comms()
1049 def _on_connect(cls, h: _CommsHandle):
1050 """Connection event callback
1052 Connection event only seems to be generated by processing an
1053 OpenComms message - USB connection is not autodetected.
"""
1057 stage._post_connect()
1061 def _on_disconnect(cls, h: _CommsHandle):
1062 """Disconnection event callback
1064 Discconneciton event only seems to be generated by processing a
1065 CloseComms message."""
1069 stage._connectionstatus.flags.connected = 0
1073 """Initalise the device and, if necessary, the SDK."""
1077 self.
_h = _CommsHandle()
1082 if __class__._lib
is None:
1085 except Exception
as e:
1089 def _do_shutdown(self) -> None:
1093 """Close comms on object deletion"""
1094 self._process_msg(Msg.CloseComms)
1096 def _post_connect(self):
1097 """Mixins should implement this method to do post-connection config."""
1101 self, msg, param1=None, param2=None, param3=None, result=None
1103 """As the SDK to process a message."""
1106 if not self._lib.linkamProcessMessage(
1107 ctypes.c_uint(msg), self._h, byref(result), param1, param2, param3
1113 """Raise an exception if the connection is down."""
1118 """Return the status update period in seconds."""
1119 return self.
_process_msg(Msg.GetDataRate).vUint32 / 1000
1122 """Fetch the controller error."""
1123 return self.
_process_msg(Msg.GetControllerError).vControllerError
1126 """Fetch the device's serial number"""
1127 buf = ctypes.create_string_buffer(18)
1128 self._process_msg(Msg.GetControllerSerial, byref(buf), 18)
1130 return buf.value.decode().rstrip()
1133 """Fetch a value from the device.
1136 svt: a StageValueType
1137 result: an existing Variant to use to return a result,
or None.
1141 if isinstance(svt, str):
1143 svt = getattr(_StageValueType, svt)
1147 vtype = _StageValueTypeToVariant.get(svt,
"vFloat32")
1148 variant = self.
_process_msg(Msg.GetValue, svt.value, result=result)
1149 if result
is not None:
1152 return getattr(variant, vtype)
1155 """Returns the bounds for a StageValueType"""
1156 if isinstance(svt, str):
1158 svt = getattr(_StageValueType, svt)
1161 vtype = _StageValueTypeToVariant.get(svt,
"vFloat32")
1164 return tuple(getattr(v, vtype)
for v
in (vmin, vmax))
1167 """Set value identified by svt to val"""
1169 if isinstance(svt, str):
1171 svt = getattr(_StageValueType, svt)
1174 vtype = _StageValueTypeToVariant.get(svt,
"vFloat32")
1180 """Returns True if the stage is moving, False if stopped
1182 This method isn't on the LinkamMDSMixin because the StageStatus motor
1183 stopped flags appear to be more reliable than the MDSStatus MoveDone
1185 if axis
is not None and axis.upper()
in "XYZ":
1186 has_motor = getattr(
1190 self.
_status.flags,
"motorStopped" + axis.upper()
1192 return has_motor
and not stopped
1194 return any(self.
is_moving(ax)
for ax
in "XYZ")
1197 """Close the comms link"""
1201 """Open the comms link and store the comms handle."""
1208 if self.
_h.value != 0:
1209 __class__._connectionMap[self.
_h.value] = self
1214 def _reopen_comms(self):
1215 """Reopen communications.
1217 This is called by the error event callback, which runs
in some thread
in
1218 the library. If comms_close
is called
in that thread, the connection
1219 status isn
't correctly updated, so reconnection must happen in another
1233 def _reopen_loop(self):
1234 """Attempt to reopen comms."""
1245 def _update_status(self, status):
1246 """Update status structures."""
1250 """Populate commsinfo struct with default USBCommsInfo"""
1257 elif not isinstance(uid, bytes):
1259 self.
_lib.linkamInitialiseUSBCommsInfo(
1264 """Populate commsinfo struct with default SerialCommsInfo for given port"""
1265 self.
_lib.linkamInitialiseSerialCommsInfo(byref(self.
_commsinfo), port)
1268 """Called by a client to fetch status in a dict.
1270 Derived classes and mixins should implement this to add their own status.
1272 status = super().
get_status(*args, status_structure, ...)
in derived classes.
1280 lambda n:
not n.startswith(
"unused"),
1281 (f[0]
for f
in s.flags._fields_),
1284 dict(map(
lambda n: (n, bool(getattr(s.flags, n))), names))
1290 """A mixin for motor-driven stages"""
1292 def __init__(self, **kwargs):
1293 super().__init__(**kwargs)
1296 def _post_connect(self):
1297 """Set up motors: set velocities and add velocity settings."""
1298 for name, flag, svt
in (
1301 self._stageconfig.flags.motorX,
1302 _StageValueType.MotorVelX,
1306 self._stageconfig.flags.motorY,
1307 _StageValueType.MotorVelY,
1311 self._stageconfig.flags.motorZ,
1312 _StageValueType.MotorVelZ,
1319 self.set_value(svt, self.get_value(svt))
1324 lambda svt=svt: self.get_value(svt),
1325 lambda val, svt=svt, s=self: self.set_value(svt, val),
1326 lambda svt=svt: self.get_value_limits(svt),
1329 super()._post_connect()
1331 def _update_status(self, status):
1332 """Call parent class update_status, then update MDS status structure."""
1333 super()._update_status(status)
1335 _StageValueType.MotorDrivenStageStatus, result=self.
_mdsstatus
1339 """Move to co-ordinates given by x and y"""
1344 self.set_value(_StageValueType.MotorSetpointX, x)
1345 self._process_msg(Msg.StartMotors,
True, 0)
1347 self.set_value(_StageValueType.MotorSetpointY, y)
1348 self._process_msg(Msg.StartMotors,
True, 1)
1350 self.set_value(_StageValueType.MotorSetpointZ, z)
1351 self._process_msg(Msg.StartMotors,
True, 2)
1353 time.sleep(5 * self.get_data_rate())
1356 """Includes MDSStatus in the get_status call."""
1360 """Return the stage's position."""
1363 if getattr(self._stageconfig.flags,
"motor" + axis):
1364 pos[axis] = self.get_value(
1365 getattr(_StageValueType,
"MotorPos" + axis)
1368 pos[axis] = float(
"nan")
1373 """Linkam correlative-microscopy stage."""
1376 "sample":
"sampleDewarFillSignal",
1377 "external":
"mainDewarFillSignal",
1380 "t_bridge": _StageValueType.Heater1Temp,
1381 "t_chamber": _StageValueType.Heater2Temp,
1382 "t_dewar": _StageValueType.Heater3Temp,
1383 "t_base": _StageValueType.Heater4Temp,
1390 dt = datetime.timedelta(0)
1395 """Start a refill: update status flag and last cycle time."""
1397 if self.
t is not None:
1398 self.
dt = datetime.datetime.now() - self.
t
1401 """End a refill: update status flag and last refill time."""
1403 self.
t = datetime.datetime.now()
1406 """Represent this object as a dict for status queries."""
1412 """Display tracker properties in representation."""
1413 return "refilling: %s, t: %s, dt: %s" % (
1447 def _update_status(self, status):
1448 """Update status structures."""
1449 super()._update_status(status)
1455 is_refilling = getattr(self.
_cmsstatus.flags, flagname)
1456 if is_refilling
and not tracker.refilling:
1457 tracker.start_refill()
1458 elif self.
_refills[key].refilling
and not is_refilling:
1459 tracker.end_refill()
1462 """Return a dict of temperature sensor readings."""
1468 """Set the state of the chamber light."""
1469 self.
set_value(_StageValueType.CmsLight, state)
1472 """Report the state of the chamber light."""
1473 return self.
get_value(_StageValueType.CmsLight)
1476 """Turn the condensor LED on or off."""
1481 self.
set_value(_StageValueType.CmsCondenserLEDLevel, level)
1484 """Set the condensor LED level"""
1486 if self.
get_value(_StageValueType.CmsCondenserLEDLevel) > 0:
1491 """Return the condensor level"""
1495 """Return the position, set point and stopped status of available motors."""
1501 getattr(_StageValueType,
"MotorPos" + axis)
1504 getattr(_StageValueType,
"MotorSetpoint" + axis)
1506 getattr(self.
_status.flags,
"motorStopped" + axis),
1513 """Start a refill of the internal dewar from an external reservoir"""
1514 return self.
set_value(_StageValueType.CmsMainDewarFillSig,
True)
1517 """Start a refill of the sample chamber from the internal dewar"""
1518 return self.
set_value(_StageValueType.CmsSampleDewarFillSig,
True)
1521 """Return information about refill times and cycle lengths."""
1522 return dict([(k, v.as_dict())
for k, v
in self.
_refills.items()])
1525 """Return a dict containing aggregated stage status."""
None add_setting(self, name, dtype, get_func, set_func, values, typing.Optional[typing.Callable[[], bool]] readonly=None)
def get_value_limits(self, svt)
def _process_msg(self, msg, param1=None, param2=None, param3=None, result=None)
def get_value(self, svt, result=None)
def __init__(self, **kwargs)
def init_serial(self, port)
dictionary _connectionMap
def get_status(self, *args)
def check_connection(self)
def is_moving(self, axis=None)
def set_value(self, svt, val)
def move_to(self, x=None, y=None, z=None)
def get_status(self, *args)
def __getattribute__(self, name)
def set_condensor_level(self, level)
def set_condensor(self, state)
def get_condensor_level(self)
def get_status(self, *args)
def refill_chamber(self, state=True)
def refill_dewar(self, state=True)
def set_light(self, state)
def __init__(self, uid="", **kwargs)