deapi 5.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. deapi/__init__.py +33 -0
  2. deapi/buffer_protocols/__init__.py +29 -0
  3. deapi/buffer_protocols/pb_2_3_0.py +666 -0
  4. deapi/buffer_protocols/pb_3_11_4.py +741 -0
  5. deapi/buffer_protocols/pb_3_19_3.py +114 -0
  6. deapi/buffer_protocols/pb_3_23_3.py +41 -0
  7. deapi/buffer_protocols/pb_3_6_1.py +745 -0
  8. deapi/client.py +2159 -0
  9. deapi/conf.py +184 -0
  10. deapi/data_types.py +586 -0
  11. deapi/fake_data/__init__.py +7 -0
  12. deapi/fake_data/base_fake_data.py +113 -0
  13. deapi/fake_data/grains.py +87 -0
  14. deapi/index.rst +10 -0
  15. deapi/prop_dump.json +450 -0
  16. deapi/python_3_instruction.txt +31 -0
  17. deapi/release_notes.txt +34 -0
  18. deapi/simulated_server/__init__.py +0 -0
  19. deapi/simulated_server/fake_server.py +721 -0
  20. deapi/simulated_server/initialize_server.py +66 -0
  21. deapi/tests/__init__.py +0 -0
  22. deapi/tests/conftest.py +95 -0
  23. deapi/tests/original_tests/01_fps.py +102 -0
  24. deapi/tests/original_tests/02_hwRoisize.py +155 -0
  25. deapi/tests/original_tests/03_hwBinning.py +117 -0
  26. deapi/tests/original_tests/04_swBinning.py +141 -0
  27. deapi/tests/original_tests/05_swhwBinning.py +113 -0
  28. deapi/tests/original_tests/06_patternPixel.py +139 -0
  29. deapi/tests/original_tests/07_reference.py +92 -0
  30. deapi/tests/original_tests/08_virtmask.py +114 -0
  31. deapi/tests/original_tests/09_scanRoi.py +104 -0
  32. deapi/tests/original_tests/10_imageStatistics.py +247 -0
  33. deapi/tests/original_tests/__init__.py +0 -0
  34. deapi/tests/original_tests/func.py +76 -0
  35. deapi/tests/original_tests/propertyName.py +725 -0
  36. deapi/tests/original_tests/test_legacy.py +132 -0
  37. deapi/tests/speed_tests/__init__.py +0 -0
  38. deapi/tests/speed_tests/test_internal_file_saving.py +54 -0
  39. deapi/tests/speed_tests/test_movie_buffer_transfer.py +54 -0
  40. deapi/tests/test_client.py +270 -0
  41. deapi/tests/test_fake_server/__init__.py +0 -0
  42. deapi/tests/test_fake_server/test_server.py +35 -0
  43. deapi/tests/test_file_saving/__init__.py +0 -0
  44. deapi/tests/test_file_saving/test_file_loading_libertem.py +49 -0
  45. deapi/tests/test_file_saving/test_file_loading_rsciio.py +53 -0
  46. deapi/tests/test_file_saving/test_scan_pattern_saving.py +68 -0
  47. deapi/version.py +3 -0
  48. deapi-5.2.0.dist-info/METADATA +44 -0
  49. deapi-5.2.0.dist-info/RECORD +52 -0
  50. deapi-5.2.0.dist-info/WHEEL +5 -0
  51. deapi-5.2.0.dist-info/entry_points.txt +2 -0
  52. deapi-5.2.0.dist-info/top_level.txt +1 -0
deapi/client.py ADDED
@@ -0,0 +1,2159 @@
1
+ # File containing the Client for connecting to the DE-Server
2
+ #
3
+ # Last update: 2024-08-07
4
+ # cfrancis@directelectron.com
5
+
6
+
7
+ # Python System imports
8
+ import socket
9
+ import sys
10
+ import struct
11
+ import time
12
+ import os
13
+ import logging
14
+ import mmap
15
+ from datetime import datetime
16
+ from time import sleep
17
+ import re
18
+ from typing import List
19
+ from enum import Enum
20
+
21
+ import numpy as np
22
+
23
+ # External package imports
24
+ from PIL import Image
25
+ import numpy
26
+
27
+
28
+ # Internal package imports
29
+ from deapi.data_types import (
30
+ FrameType,
31
+ PixelFormat,
32
+ Attributes,
33
+ Histogram,
34
+ PropertySpec,
35
+ MovieBufferStatus,
36
+ MovieBufferInfo,
37
+ DataType,
38
+ PropertyCollection,
39
+ VirtualMask,
40
+ )
41
+
42
+
43
+ from deapi.buffer_protocols import pb
44
+ from deapi.version import version, commandVersion
45
+ from deapi.version import commandVersion as cVersion
46
+ import functools
47
+
48
+
49
+ ## the commandInfo contains [VERSION_MAJOR.VERSION_MINOR.VERSION_PATCH.VERSION_REVISION]
50
+
51
+
52
+ logLevel = logging.INFO
53
+ logging.basicConfig(format="%(asctime)s DE %(levelname)-8s %(message)s", level=logLevel)
54
+ log = logging.getLogger("DECameraClientLib")
55
+ log.info("Python : " + sys.version.split("(")[0])
56
+ log.info("DEClient : " + version)
57
+ log.info("CommandVer: " + str(commandVersion))
58
+ log.info("logLevel : " + str(logging.getLevelName(logLevel)))
59
+
60
+
61
+ def write_only(func):
62
+ def wrapper(*args, **kwargs):
63
+ if args[0].read_only:
64
+ log.error("Client is read-only. Cannot set property.")
65
+ return
66
+ else:
67
+ return func(*args, **kwargs)
68
+
69
+ return wrapper
70
+
71
+
72
+ def disable_scan(func):
73
+ def wrapper(*args, **kwargs):
74
+ print("Disabling scan")
75
+ initial_scan = args[0]["Scan - Enable"]
76
+ args[0].set_property("Scan - Enable", False)
77
+ ans = func(*args, **kwargs)
78
+ args[0].set_property("Scan - Enable", initial_scan)
79
+ return ans
80
+
81
+ return wrapper
82
+
83
+
84
+ class Client:
85
+ """A class for connecting to the DE-Server
86
+
87
+ Examples
88
+ --------
89
+ >>> client = Client()
90
+ >>> client.connect()
91
+ >>> client["Exposure Time (seconds)"]
92
+ """
93
+
94
+ def __init__(self):
95
+ pass
96
+
97
+ def set_log_level(self, level):
98
+ log = logging.getLogger("DECameraClientLib")
99
+ log.setLevel(level)
100
+ log.info("Log level set to %s", level)
101
+ return
102
+
103
+ def __str__(self):
104
+ return f"Client(host={self.host}, port={self.port}, camera={self.get_current_camera()})"
105
+
106
+ def _ipython_key_completions_(self):
107
+ return self.list_properties()
108
+
109
+ def _repr_html_(self):
110
+ table = f"""
111
+ <table>
112
+ <tr>
113
+ <th>Host</th>
114
+ <th>Port</th>
115
+ <th>Current Camera</th>
116
+ </tr>
117
+ <tr>
118
+ <td>{self.host}</td>
119
+ <td>{self.port}</td>
120
+ <td>{self.currCamera}</td>
121
+ </tr>
122
+ </table>
123
+ <details>
124
+ <summary>Current Info</summary>
125
+ <pre>
126
+ {self.get_current_info()}
127
+ </pre>
128
+ </details>
129
+ """
130
+ return table
131
+
132
+ def gui(self):
133
+ from IPython.display import display
134
+
135
+ display(self)
136
+
137
+ def __setitem__(self, key, value):
138
+ self.set_property(key, value)
139
+
140
+ def __getitem__(self, key):
141
+ return self.get_property(key)
142
+
143
+ def get_current_info(self):
144
+ prop_list = self.list_properties()
145
+ values = self.get_properties(prop_list)
146
+ text = ""
147
+ for p, v in zip(prop_list, values):
148
+ text += f"{p}: {v} \n"
149
+ return text
150
+
151
+ def _initialize_attributes(self):
152
+ all_properties = self.list_properties()
153
+ collections = [p.split(" - ")[0] for p in all_properties if " - " in p]
154
+ unique_collections = np.unique(collections)
155
+ for collection in unique_collections:
156
+ stripped = collection.lower().strip().replace(" ", "_")
157
+ props = [p for p in all_properties if collection + " -" in p]
158
+ setattr(
159
+ self,
160
+ stripped,
161
+ PropertyCollection(client=self, name=collection, properties=props),
162
+ )
163
+
164
+ def connect(self, host: str = "127.0.0.1", port: int = 13240, read_only=False):
165
+ """Connect to DE-Server
166
+
167
+ Parameters
168
+ ----------
169
+ host : str, optional
170
+ The host to connect to, by default "127.0.0.1" for local connection
171
+ port : int, optional
172
+ The port to connect to, by default 13240
173
+ """
174
+ if host == "localhost" or host == "127.0.0.1":
175
+ tcpNoDelay = 0 # on loopback interface, nodelay causes delay
176
+
177
+ if self.usingMmf:
178
+ self.mmf = mmap.mmap(0, MMF_DATA_BUFFER_SIZE, "ImageFileMappingObject")
179
+ self.mmf[0] = True
180
+ else:
181
+ self.usingMmf = False # Disabled MMF if connected remotely
182
+ tcpNoDelay = 1
183
+
184
+ if logLevel == logging.DEBUG:
185
+ log.debug("Connecting to server: %s", host)
186
+
187
+ self.socket = socket.socket(
188
+ socket.AF_INET, socket.SOCK_STREAM
189
+ ) # Create a socket (SOCK_STREAM means a TCP socket)
190
+ self.socket.connect(
191
+ (host, port)
192
+ ) # Connect to server reading port for sending data
193
+ self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, tcpNoDelay)
194
+ self.socket.setblocking(False)
195
+ self.socket.settimeout(2)
196
+
197
+ self.cameras = self.__getStrings(self.LIST_CAMERAS)
198
+ if logLevel == logging.DEBUG:
199
+ log.debug("Available cameras: %s", self.cameras)
200
+
201
+ self.currCamera = self.cameras[0]
202
+ if logLevel == logging.DEBUG:
203
+ log.debug("Current camera: %s", self.currCamera)
204
+
205
+ self.connected = True
206
+ self.host = host
207
+ self.port = port
208
+ log.info("Connected to server: %s, port: %d", host, port)
209
+
210
+ serverVersion = self.GetProperty("Server Software Version")
211
+ serverVersion = re.findall(r"\d+", serverVersion)
212
+
213
+ version = [int(part) for part in serverVersion[:4]]
214
+ temp = version[2] + version[1] * 1000 + version[0] * 1000000
215
+
216
+ if cVersion >= 12:
217
+ self.set_client_read_only(read_only)
218
+
219
+ if temp >= 2007004:
220
+ ## version after 2.7.4
221
+ commandVersion = 12
222
+ elif temp >= 2007003:
223
+ ## version after 2.7.3
224
+ commandVersion = 11
225
+ elif temp >= 2007002:
226
+ ## version after 2.7.2
227
+ commandVersion = 10
228
+ elif temp >= 2005025:
229
+ ##version after 2.5.25
230
+ commandVersion = 4
231
+ elif temp >= 2001017:
232
+ ## version after 2.1.17
233
+ commandVersion = 3
234
+ self._initialize_attributes()
235
+ self.update_scan_size()
236
+ self.update_image_size()
237
+ self.virtual_masks = []
238
+ for i in range(4):
239
+ self.virtual_masks.append(VirtualMask(client=self, index=i))
240
+
241
+ def set_client_read_only(self, read_only):
242
+ self.read_only = read_only
243
+ command = self._addSingleCommand(self.SET_CLIENT_READ_ONLY, None, [read_only])
244
+ response = self._sendCommand(command)
245
+ return response
246
+
247
+ def update_scan_size(self):
248
+ self.scan_sizex = self["Scan - Size X"]
249
+ self.scan_sizey = self["Scan - Size Y"]
250
+
251
+ def update_image_size(self):
252
+ self.image_sizex = self["Image Size X (pixels)"]
253
+ self.image_sizey = self["Image Size Y (pixels)"]
254
+
255
+ def disconnect(self):
256
+ """
257
+ Disconnects from the server.
258
+ Closes the memory-mapped file (mmf) if it is open.
259
+ Closes the socket connection if it is open.
260
+ Sets the 'connected' flag to False.
261
+ """
262
+ if self.mmf != 0:
263
+ self.mmf.close()
264
+
265
+ if self.connected:
266
+ self.socket.close()
267
+ self.socket.close()
268
+ self.connected = False
269
+ log.info("Disconnected.")
270
+
271
+ def list_cameras(self) -> List[str]:
272
+ """
273
+ List the available cameras on the server.
274
+ """
275
+ return self.cameras
276
+
277
+ def get_virtual_mask(self, index):
278
+ mask_name = f"virtual_mask{index}"
279
+ a = Attributes()
280
+ a.windowWidth = self["Image Size X (pixels)"]
281
+ a.windowHeight = self["Image Size Y (pixels)"]
282
+ (
283
+ res,
284
+ _,
285
+ _,
286
+ _,
287
+ ) = self.get_result(mask_name, DataType.DE8u, attributes=a)
288
+ return res
289
+
290
+ def get_current_camera(self) -> str:
291
+ """
292
+ Get the current camera on the server.
293
+ """
294
+ if self.currCamera is None:
295
+ return "No current camera"
296
+ else:
297
+ return self.currCamera
298
+
299
+ @write_only
300
+ def set_current_camera(self, camera_name: str = None):
301
+ """
302
+ Set the current camera on the server.
303
+ """
304
+ if camera_name is None:
305
+ return False
306
+
307
+ self.currCamera = camera_name
308
+
309
+ if logLevel == logging.DEBUG:
310
+ log.debug("current camera: %s", camera_name)
311
+
312
+ self.refreshProperties = True
313
+ return True
314
+
315
+ def list_properties(self, options=None, search=None):
316
+ """
317
+ Get a list of property names from the current camera on DE-Server
318
+
319
+ Parameters
320
+ ----------
321
+ options : list, optional
322
+ Options to pass to the server, by default None
323
+ """
324
+ available_properties = self.__getStrings(self.LIST_PROPERTIES, options)
325
+ if available_properties != False:
326
+ self.available_properties = available_properties
327
+
328
+ if logLevel == logging.DEBUG:
329
+ log.debug("Available camera properties: %s", available_properties)
330
+ if search is not None:
331
+ available_properties = [p for p in available_properties if search in p]
332
+ return available_properties
333
+
334
+ def get_property_spec(self, propertyName: str):
335
+ """
336
+ Get a list of allowed values for a property of the current camera on DE-Server
337
+
338
+ Parameters
339
+ ----------
340
+ propertyName : str
341
+ The name of the property to get the allowed values for
342
+ """
343
+ t0 = self.GetTime()
344
+ values = False
345
+ command = self._addSingleCommand(self.LIST_ALLOWED_VALUES, propertyName)
346
+ response = self._sendCommand(command)
347
+ if response == False:
348
+ return None
349
+
350
+ values = self.__getParameters(response.acknowledge[0])
351
+
352
+ propSpec = PropertySpec()
353
+ propSpec.dataType = values[0]
354
+ propSpec.valueType = values[1]
355
+ propSpec.category = values[len(values) - 3]
356
+ propSpec.options = list(values[2 : len(values) - 3])
357
+ propSpec.defaultValue = str(values[len(values) - 2])
358
+ propSpec.currentValue = str(values[len(values) - 1])
359
+
360
+ optionsLength = len(propSpec.options)
361
+
362
+ if propSpec.valueType == "Range":
363
+ if optionsLength == 2:
364
+ rangeString = ""
365
+ for i in range(optionsLength):
366
+ if propSpec.dataType == "Integer":
367
+ rangeString += str(int(propSpec.options[i]))
368
+ else:
369
+ rangeString += str(propSpec.options[i])
370
+ if i == 0:
371
+ rangeString += str(" - ")
372
+
373
+ propSpec.options.append(rangeString)
374
+
375
+ if propSpec.valueType == "Set":
376
+ for i in range(optionsLength):
377
+ if propSpec.defaultValue == propSpec.options[i]:
378
+ if propSpec.defaultValue != "":
379
+ propSpec.options[i] = propSpec.defaultValue + str("*")
380
+ else:
381
+ emptyStringIndex = i
382
+ if propSpec.defaultValue == "":
383
+ propSpec.options.pop(emptyStringIndex)
384
+
385
+ if "allow_all" in propSpec.valueType:
386
+ propSpec.options = ""
387
+ elif propSpec.dataType == "String":
388
+ propSpec.options = str(list(map(lambda a: str(a), propSpec.options)))[1:-1]
389
+ else:
390
+ propSpec.options = str(propSpec.options)[1:-1]
391
+
392
+ return propSpec
393
+
394
+ def property_valid_values(self, propertyName: str):
395
+ """
396
+ Get a list of allowed values for a property of the current camera on DE-Server
397
+ """
398
+ t0 = self.GetTime()
399
+ values = False
400
+ command = self._addSingleCommand(self.LIST_ALLOWED_VALUES, propertyName)
401
+ response = self._sendCommand(command)
402
+ if response != False:
403
+ values = self.__getParameters(response.acknowledge[0])
404
+
405
+ if logLevel == logging.DEBUG:
406
+ log.debug(
407
+ "get allowed property values: %s = %s, completed in %.1f ms",
408
+ propertyName,
409
+ values,
410
+ (self.GetTime() - t0) * 1000,
411
+ )
412
+
413
+ return values
414
+
415
+ def get_property(self, propertyName: str):
416
+ """
417
+ Get the value of a property of the current camera on DE-Server
418
+
419
+ Parameters
420
+ ----------
421
+ propertyName : str
422
+ The name of the property to get the value of
423
+ """
424
+ t0 = self.GetTime()
425
+ ret = False
426
+
427
+ if propertyName is not None:
428
+ command = self._addSingleCommand(self.GET_PROPERTY, propertyName)
429
+ response = self._sendCommand(command)
430
+ if response != False:
431
+ values = self.__getParameters(response.acknowledge[0])
432
+ if type(values) is list:
433
+ if len(values) > 0:
434
+ ret = values[0] # always return the first value
435
+ else:
436
+ ret = values
437
+
438
+ if logLevel == logging.DEBUG:
439
+ log.debug(
440
+ "GetProperty: %s = %s, completed in %.1f ms",
441
+ propertyName,
442
+ values,
443
+ (self.GetTime() - t0) * 1000,
444
+ )
445
+
446
+ return ret
447
+
448
+ def get_server_version(self):
449
+ """
450
+ Get the server software version
451
+ """
452
+ server_version = self.GetProperty("Server Software Version")
453
+ server_version = re.findall(r"\d+", server_version)
454
+
455
+ ver = [int(part) for part in server_version[:4]]
456
+ res = ver[2] + ver[1] * 1000 + ver[0] * 1000000
457
+ return res
458
+
459
+ def get_properties(self, names=None):
460
+ if names is None:
461
+ names = self.list_properties()
462
+ return [self.get_property(p) for p in names]
463
+
464
+ @property
465
+ def acquiring(self):
466
+ """Check if the camera is currently acquiring images. (bool)"""
467
+ return self.get_property("Acquisition Status") == "Acquiring"
468
+
469
+ @write_only
470
+ def set_property(self, name: str, value):
471
+ """
472
+ Set the value of a property of the current camera on DE-Server
473
+
474
+ Parameters
475
+ ----------
476
+ name : str
477
+ The name of the property to set the value of
478
+ value : any
479
+ The value to set the property to
480
+ """
481
+ t0 = self.GetTime()
482
+ ret = False
483
+
484
+ if name is not None and value is not None:
485
+ command = self._addSingleCommand(self.SET_PROPERTY, name, [value])
486
+ response = self._sendCommand(command)
487
+ if response != False:
488
+ ret = response.acknowledge[0].error != True
489
+ self.refreshProperties = True
490
+
491
+ if logLevel == logging.DEBUG:
492
+ log.debug(
493
+ "SetProperty: %s = %s, completed in %.1f ms",
494
+ name,
495
+ value,
496
+ (self.GetTime() - t0) * 1000,
497
+ )
498
+
499
+ return ret
500
+
501
+ @write_only
502
+ def set_property_and_get_changed_properties(self, name, value, changedProperties):
503
+ """
504
+ Set the value of a property of the current camera on DE-Server and get all of
505
+ the changed properties. This is useful for testing and determining how certain
506
+ properties affect others.
507
+
508
+ Parameters
509
+ ----------
510
+ name : str
511
+ The name of the property to set the value of
512
+ value : any
513
+ The value to set the property to
514
+ changedProperties : list
515
+ List of properties that have changed
516
+ """
517
+ t0 = self.GetTime()
518
+ ret = False
519
+
520
+ if name is not None and value is not None:
521
+ command = self._addSingleCommand(
522
+ self.SET_PROPERTY_AND_GET_CHANGED_PROPERTIES, name, [value]
523
+ )
524
+ response = self._sendCommand(command)
525
+ if response != False:
526
+ ret = response.acknowledge[0].error != True
527
+ self.refreshProperties = True
528
+
529
+ if ret:
530
+ ret = self.ParseChangedProperties(changedProperties, response)
531
+ if logLevel == logging.DEBUG:
532
+ log.debug(
533
+ "SetProperty: %s = %s, completed in %.1f ms",
534
+ name,
535
+ value,
536
+ (self.GetTime() - t0) * 1000,
537
+ )
538
+
539
+ return ret
540
+
541
+ @write_only
542
+ def set_engineering_mode(self, enable, password):
543
+ """
544
+ Set the engineering mode of the current camera on DE-Server. Mostly for internal testing.
545
+
546
+ Parameters
547
+ ----------
548
+ enable : bool
549
+ Enable or disable engineering mode
550
+ password : str
551
+ The password to enable engineering mode
552
+ """
553
+ ret = False
554
+
555
+ command = self._addSingleCommand(self.SET_ENG_MODE, None, [enable, password])
556
+ response = self._sendCommand(command)
557
+ if response != False:
558
+ ret = response.acknowledge[0].error != True
559
+ self.refreshProperties = True
560
+ return ret
561
+
562
+ @write_only
563
+ def setEngModeAndGetChangedProperties(self, enable, password, changedProperties):
564
+
565
+ ret = False
566
+
567
+ command = self.__addSingleCommand(
568
+ self.SET_ENG_MODE_GET_CHANGED_PROPERTIES, None, [enable, password]
569
+ )
570
+ response = self.__sendCommand(command)
571
+ if response != False:
572
+ ret = response.acknowledge[0].error != True
573
+ self.refreshProperties = True
574
+
575
+ if ret:
576
+ ret = self.ParseChangedProperties(changedProperties, response)
577
+
578
+ return ret
579
+
580
+ @write_only
581
+ def set_hw_roi(self, offsetX: int, offsetY: int, sizeX: int, sizeY: int):
582
+ """
583
+ Set the hardware region of interest (ROI) of the current camera on DE-Server.
584
+
585
+ Parameters
586
+ ----------
587
+ offsetX : int
588
+ The x offset of the ROI
589
+ offsetY : int
590
+ The y offset of the ROI
591
+ sizeX : int
592
+ The width of the ROI
593
+ sizeY : int
594
+ The height of the ROI
595
+ """
596
+ t0 = self.GetTime()
597
+ ret = False
598
+
599
+ command = self._addSingleCommand(
600
+ self.SET_HW_ROI, None, [offsetX, offsetY, sizeX, sizeY]
601
+ )
602
+ response = self._sendCommand(command)
603
+ if response != False:
604
+ ret = response.acknowledge[0].error != True
605
+ self.refreshProperties = True
606
+
607
+ if logLevel == logging.DEBUG:
608
+ log.debug(
609
+ "SetHwRoi: (%i,%i,%i,%i) , completed in %.1f ms",
610
+ offsetX,
611
+ offsetY,
612
+ sizeX,
613
+ sizeY,
614
+ (self.GetTime() - t0) * 1000,
615
+ )
616
+
617
+ return ret
618
+
619
+ @write_only
620
+ def SetScanSize(self, sizeX, sizeY):
621
+
622
+ t0 = self.GetTime()
623
+ ret = False
624
+
625
+ command = self.__addSingleCommand(self.SET_SCAN_SIZE, None, [sizeX, sizeY])
626
+ response = self.__sendCommand(command)
627
+ if response != False:
628
+ ret = response.acknowledge[0].error != True
629
+ self.refreshProperties = True
630
+
631
+ if logLevel == logging.DEBUG:
632
+ log.debug(
633
+ "SetScanSize: (%i,%i) , completed in %.1f ms",
634
+ sizeX,
635
+ sizeY,
636
+ (self.GetTime() - t0) * 1000,
637
+ )
638
+
639
+ return ret
640
+
641
+ @write_only
642
+ def SetScanSizeAndGetChangedProperties(self, sizeX, sizeY, changedProperties):
643
+ t0 = self.GetTime()
644
+ ret = False
645
+
646
+ command = self.__addSingleCommand(
647
+ self.SET_SCAN_SIZE_AND_GET_CHANGED_PROPERTIES, None, [sizeX, sizeY]
648
+ )
649
+ response = self.__sendCommand(command)
650
+ if response != False:
651
+ ret = response.acknowledge[0].error != True
652
+ self.refreshProperties = True
653
+
654
+ if ret:
655
+ ret = self.ParseChangedProperties(changedProperties, response)
656
+
657
+ if logLevel == logging.DEBUG:
658
+ log.debug(
659
+ "SetScanSize: (%i,%i) , completed in %.1f ms",
660
+ sizeX,
661
+ sizeY,
662
+ (self.GetTime() - t0) * 1000,
663
+ )
664
+
665
+ return ret
666
+
667
+ @write_only
668
+ def SetScanROI(self, enable, offsetX, offsetY, sizeX, sizeY):
669
+
670
+ t0 = self.GetTime()
671
+ ret = False
672
+
673
+ command = self.__addSingleCommand(
674
+ self.SET_SCAN_SIZE, None, [enable, offsetX, offsetY, sizeX, sizeY]
675
+ )
676
+ response = self.__sendCommand(command)
677
+ if response != False:
678
+ ret = response.acknowledge[0].error != True
679
+ self.refreshProperties = True
680
+
681
+ if logLevel == logging.DEBUG:
682
+ log.debug(
683
+ "SetScanROI: (%i,%i,%i,%i) , completed in %.1f ms",
684
+ offsetX,
685
+ offsetY,
686
+ sizeX,
687
+ sizeY,
688
+ (self.GetTime() - t0) * 1000,
689
+ )
690
+
691
+ return ret
692
+
693
+ @write_only
694
+ def SetScanROI(self, enable, offsetX, offsetY, sizeX, sizeY, changedProperties):
695
+ t0 = self.GetTime()
696
+ ret = False
697
+
698
+ command = self.__addSingleCommand(
699
+ self.SET_SCAN_ROI__AND_GET_CHANGED_PROPERTIES,
700
+ None,
701
+ [enable, offsetX, offsetY, sizeX, sizeY],
702
+ )
703
+ response = self.__sendCommand(command)
704
+ if response != False:
705
+ ret = response.acknowledge[0].error != True
706
+ self.refreshProperties = True
707
+
708
+ if ret:
709
+ ret = self.ParseChangedProperties(changedProperties, response)
710
+
711
+ if logLevel == logging.DEBUG:
712
+ log.debug(
713
+ "SetScanROI: (%i,%i,%i,%i) , completed in %.1f ms",
714
+ offsetX,
715
+ offsetY,
716
+ sizeX,
717
+ sizeY,
718
+ (self.GetTime() - t0) * 1000,
719
+ )
720
+
721
+ return ret
722
+
723
+ @write_only
724
+ def set_hw_roi_and_get_changed_properties(
725
+ self, offsetX: int, offsetY: int, sizeX: int, sizeY: int, changedProperties
726
+ ):
727
+ """
728
+ Set the hardware region of interest (ROI) of the current camera on DE-Server and get all of
729
+ the changed properties. This is useful for testing and determining how certain
730
+ properties affect others.
731
+
732
+ Parameters
733
+ ----------
734
+ offsetX : int
735
+ The x offset of the ROI
736
+ offsetY : int
737
+ The y offset of the ROI
738
+ sizeX : int
739
+ The width of the ROI
740
+ sizeY : int
741
+ The height of the ROI
742
+ changedProperties : list
743
+ List of properties that have changed
744
+ """
745
+ t0 = self.GetTime()
746
+ ret = False
747
+
748
+ command = self._addSingleCommand(
749
+ self.SET_HW_ROI_AND_GET_CHANGED_PROPERTIES,
750
+ None,
751
+ [offsetX, offsetY, sizeX, sizeY],
752
+ )
753
+ response = self._sendCommand(command)
754
+ if response != False:
755
+ ret = response.acknowledge[0].error != True
756
+ self.refreshProperties = True
757
+
758
+ if ret:
759
+ ret = self.ParseChangedProperties(changedProperties, response)
760
+
761
+ if logLevel == logging.DEBUG:
762
+ log.debug(
763
+ "SetHwRoi: (%i,%i,%i,%i) , completed in %.1f ms",
764
+ offsetX,
765
+ offsetY,
766
+ sizeX,
767
+ sizeY,
768
+ (self.GetTime() - t0) * 1000,
769
+ )
770
+
771
+ return ret
772
+
773
+ @write_only
774
+ def set_sw_roi(self, offsetX: int, offsetY: int, sizeX: int, sizeY: int):
775
+ """
776
+ Set the software region of interest (ROI) of the current camera on DE-Server.
777
+
778
+ Parameters
779
+ ----------
780
+ offsetX : int
781
+ The x offset of the ROI
782
+ offsetY : int
783
+ The y offset of the ROI
784
+ sizeX : int
785
+ The width of the ROI
786
+ sizeY : int
787
+ The height of the ROI
788
+ """
789
+ t0 = self.GetTime()
790
+ ret = False
791
+
792
+ command = self._addSingleCommand(
793
+ self.SET_SW_ROI, None, [offsetX, offsetY, sizeX, sizeY]
794
+ )
795
+ response = self._sendCommand(command)
796
+ if response != False:
797
+ ret = response.acknowledge[0].error != True
798
+ self.refreshProperties = True
799
+
800
+ if logLevel == logging.DEBUG:
801
+ log.debug(
802
+ "SetSwRoi: (%i,%i,%i,%i) , completed in %.1f ms",
803
+ offsetX,
804
+ offsetY,
805
+ sizeX,
806
+ sizeY,
807
+ (self.GetTime() - t0) * 1000,
808
+ )
809
+
810
+ return ret
811
+
812
+ @write_only
813
+ def set_sw_roi_and_get_changed_properties(
814
+ self, offsetX, offsetY, sizeX, sizeY, changedProperties
815
+ ):
816
+ """
817
+ Set the software region of interest (ROI) of the current camera on DE-Server and get all of
818
+ the changed properties. This is useful for testing and determining how certain
819
+ properties affect others.
820
+
821
+ Parameters
822
+ ----------
823
+ offsetX : int
824
+ The x offset of the ROI
825
+ offsetY : int
826
+ The y offset of the ROI
827
+ sizeX : int
828
+ The width of the ROI
829
+ sizeY : int
830
+ The height of the ROI
831
+ changedProperties : list
832
+ List of properties that have changed
833
+ """
834
+ t0 = self.GetTime()
835
+ ret = False
836
+
837
+ command = self._addSingleCommand(
838
+ self.SET_SW_ROI_AND_GET_CHANGED_PROPERTIES,
839
+ None,
840
+ [offsetX, offsetY, sizeX, sizeY],
841
+ )
842
+ response = self._sendCommand(command)
843
+ if response != False:
844
+ ret = response.acknowledge[0].error != True
845
+ self.refreshProperties = True
846
+
847
+ if ret:
848
+ ret = self.ParseChangedProperties(changedProperties, response)
849
+
850
+ if logLevel == logging.DEBUG:
851
+ log.debug(
852
+ "SetSWRoi: (%i,%i,%i,%i) , completed in %.1f ms",
853
+ offsetX,
854
+ offsetY,
855
+ sizeX,
856
+ sizeY,
857
+ (self.GetTime() - t0) * 1000,
858
+ )
859
+
860
+ return ret
861
+
862
+ def current_movie_buffer(self):
863
+ movieBufferInfo = self.GetMovieBufferInfo()
864
+ if movieBufferInfo.imageDataType == DataType.DE8u:
865
+ imageType = numpy.uint8
866
+ elif movieBufferInfo.imageDataType == DataType.DE16u:
867
+ imageType = numpy.uint16
868
+ elif movieBufferInfo.imageDataType == DataType.DE32f:
869
+ imageType = numpy.float32
870
+
871
+ ## Allocate movie buffers
872
+ totalBytes = movieBufferInfo.headerBytes + movieBufferInfo.imageBufferBytes
873
+ buffer = bytearray(totalBytes)
874
+ return movieBufferInfo, buffer, totalBytes, imageType
875
+
876
+ @write_only
877
+ def start_acquisition(
878
+ self,
879
+ numberOfAcquisitions: int = 1,
880
+ requestMovieBuffer=False,
881
+ update=True,
882
+ ):
883
+ """
884
+ Start acquiring images. Make sure all of the properties are set to the desired values.
885
+
886
+ Parameters
887
+ ----------
888
+ numberOfAcquisitions : int, optional
889
+ The number of acquisitions to repeat, by default 1
890
+ requestMovieBuffer : bool, optional
891
+ Request a movie buffer, by default False. If True, the movie buffer will be returned
892
+ with all of the frames.
893
+
894
+ """
895
+ start_time = self.GetTime()
896
+ step_time = self.GetTime()
897
+
898
+ if update:
899
+ self.update_scan_size()
900
+ self.update_image_size()
901
+
902
+ if self.refreshProperties:
903
+ self.roi_x = self.GetProperty("Crop Size X")
904
+ self.roi_y = self.GetProperty("Crop Size Y")
905
+ self.binning_x = self.GetProperty("Binning X")
906
+ self.binning_y = self.GetProperty("Binning Y")
907
+ self.width = self.GetProperty("Image Size X (pixels)")
908
+ self.height = self.GetProperty("Image Size Y (pixels)")
909
+ self.exposureTime = self.GetProperty("Exposure Time (seconds)")
910
+ self.refreshProperties = False
911
+
912
+ if logLevel == logging.DEBUG:
913
+ lapsed = (self.GetTime() - step_time) * 1000
914
+ log.debug(" Prepare Time: %.1f ms", lapsed)
915
+ step_time = self.GetTime()
916
+
917
+ if self.width * self.height == 0:
918
+ log.error(" Image size is 0! ")
919
+ else:
920
+ bytesize = 0
921
+ command = self._addSingleCommand(
922
+ self.START_ACQUISITION, None, [numberOfAcquisitions, requestMovieBuffer]
923
+ )
924
+
925
+ if logLevel == logging.DEBUG:
926
+ lapsed = (self.GetTime() - step_time) * 1000
927
+ log.debug(" Build Time: %.1f ms", lapsed)
928
+ step_time = self.GetTime()
929
+
930
+ response = self._sendCommand(command)
931
+ if logLevel == logging.DEBUG:
932
+ lapsed = (self.GetTime() - step_time) * 1000
933
+ log.debug(" Command Time: %.1f ms", lapsed)
934
+ step_time = self.GetTime()
935
+
936
+ if response != False:
937
+ ret = response.acknowledge[0].error != True
938
+ self.refreshProperties = True
939
+
940
+ if logLevel == logging.DEBUG:
941
+ lapsed = (self.GetTime() - step_time) * 1000
942
+ log.debug(" Typing Time: %.1f ms", lapsed)
943
+ step_time = self.GetTime()
944
+
945
+ if logLevel <= logging.DEBUG:
946
+ lapsed = (self.GetTime() - start_time) * 1000
947
+ log.debug(
948
+ " Start Time: %.1f ms, ROI:[%d, %d], Binning:[%d, %d], Image size:[%d, %d]",
949
+ lapsed,
950
+ self.roi_x,
951
+ self.roi_y,
952
+ self.binning_x,
953
+ self.binning_y,
954
+ self.width,
955
+ self.height,
956
+ )
957
+
958
+ @write_only
959
+ def stop_acquisition(self):
960
+ """
961
+ Stop acquiring images.
962
+
963
+ This can be called in the same thread or another thread to stop the current acquisitions.
964
+ This will cause `get_result` calls to return immediately.
965
+ """
966
+ start_time = self.GetTime()
967
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP
968
+ sock.sendto(b"PyClientStopAcq", (self.host, self.port))
969
+ respond = sock.recv(32)
970
+ if logLevel == logging.INFO:
971
+ log.info(f"{self.host} {self.port} {respond}")
972
+ if logLevel <= logging.DEBUG:
973
+ lapsed = (self.GetTime() - start_time) * 1000
974
+ log.debug(" Stop Time: %.1f ms", lapsed)
975
+
976
+ return b"Stopped" in respond
977
+
978
+ def get_result(
979
+ self,
980
+ frameType="singleframe_integrated",
981
+ pixelFormat="UINT16",
982
+ attributes="auto",
983
+ histogram=None,
984
+ ):
985
+ """
986
+ Get the specified type of frames in the desired pixel format and associated information.
987
+
988
+ Parameters
989
+ ----------
990
+ frameType: FrameType
991
+ The type of frame to get. Use the FrameType enum or a string.
992
+ Most common:
993
+ - virtual_image0 (or 1, 2, 3, 4)
994
+ - external_image1 (or 2, 3, 4)
995
+ - sumtotal
996
+ - singleframe_integrated
997
+ pixelFormat: PixelFormat
998
+ The pixel format to get. Use the PixelFormat enum or a string.
999
+ One of the following:
1000
+ - DE8u
1001
+ - DE16u
1002
+ - DE32f
1003
+ - DE64f
1004
+ attributes: Attributes
1005
+ Defines the image to be returned, some members can be updated.
1006
+ Some members of this parameter are input only, some are input/output.
1007
+ histogram: Histogram
1008
+ Returns the histogram if desired.
1009
+ Some members of this parameter are input only, some are input/output.
1010
+
1011
+ Note
1012
+ ----
1013
+ During acquisition, live frames will be returned; after acquisition, the last image will be returned.
1014
+ """
1015
+ if isinstance(frameType, str):
1016
+ frameType = getattr(FrameType, frameType.upper())
1017
+ if isinstance(pixelFormat, str):
1018
+ pixelFormat = getattr(PixelFormat, pixelFormat)
1019
+
1020
+ if attributes == "auto":
1021
+ attributes = Attributes()
1022
+ scan_images = [17, 18, 19, 20, 21, 22, 23, 24, 25]
1023
+ if frameType.value in scan_images:
1024
+ attributes.windowWidth = self.scan_sizex
1025
+ attributes.windowHeight = self.scan_sizey
1026
+ else:
1027
+ attributes.windowWidth = self.image_sizex
1028
+ attributes.windowHeight = self.image_sizey
1029
+
1030
+ log.debug("GetResult frameType:%s, pixelFormat:%s", frameType, pixelFormat)
1031
+ start_time = self.GetTime()
1032
+ step_time = self.GetTime()
1033
+
1034
+ if attributes == None:
1035
+ attributes = Attributes()
1036
+
1037
+ if histogram == None:
1038
+ histogram = Histogram()
1039
+
1040
+ if attributes.windowWidth > 0:
1041
+ self.width = attributes.windowWidth
1042
+
1043
+ if attributes.windowHeight > 0:
1044
+ self.height = attributes.windowHeight
1045
+
1046
+ image = None
1047
+ imageDataType = numpy.uint16
1048
+
1049
+ if logLevel == logging.DEBUG:
1050
+ lapsed = (self.GetTime() - step_time) * 1000
1051
+ log.debug(" Prepare Time: %.1f ms", lapsed)
1052
+ step_time = self.GetTime()
1053
+
1054
+ if histogram != None:
1055
+ histoMin = histogram.min
1056
+ histoMax = histogram.max
1057
+ histoBins = histogram.bins
1058
+ else:
1059
+ histoMin = 0
1060
+ histoMax = 0
1061
+ histoBins = 0
1062
+
1063
+ if self.width * self.height == 0:
1064
+ log.error(" Image size is 0! ")
1065
+ else:
1066
+ bytesize = 0
1067
+ command = self._addSingleCommand(
1068
+ self.GET_RESULT,
1069
+ None,
1070
+ [
1071
+ frameType.value,
1072
+ pixelFormat.value,
1073
+ attributes.centerX,
1074
+ attributes.centerY,
1075
+ attributes.zoom,
1076
+ attributes.windowWidth,
1077
+ attributes.windowHeight,
1078
+ attributes.fft,
1079
+ attributes.stretchType,
1080
+ attributes.manualStretchMin,
1081
+ attributes.manualStretchMax,
1082
+ attributes.manualStretchGamma,
1083
+ attributes.outlierPercentage,
1084
+ attributes.timeoutMsec,
1085
+ histoMin,
1086
+ histoMax,
1087
+ histoBins,
1088
+ ],
1089
+ )
1090
+
1091
+ if logLevel == logging.DEBUG:
1092
+ lapsed = (self.GetTime() - step_time) * 1000
1093
+ log.debug(" Build Time: %.1f ms", lapsed)
1094
+ step_time = self.GetTime()
1095
+ response = self._sendCommand(command)
1096
+ if logLevel == logging.DEBUG:
1097
+ lapsed = (self.GetTime() - step_time) * 1000
1098
+ log.debug(" Command Time: %.1f ms", lapsed)
1099
+ step_time = self.GetTime()
1100
+
1101
+ if response != False:
1102
+ values = self.__getParameters(response.acknowledge[0])
1103
+ if (
1104
+ type(values) is list and len(values) >= 20
1105
+ ): # This should be majorly simplified
1106
+ i = 0
1107
+ pixelFormat = PixelFormat(values[i])
1108
+ i += 1
1109
+ attributes.frameWidth = values[i]
1110
+ i += 1
1111
+ attributes.frameHeight = values[i]
1112
+ i += 1
1113
+ attributes.datasetName = values[i]
1114
+ i += 1
1115
+ attributes.acqIndex = values[i]
1116
+ i += 1
1117
+ attributes.acqFinished = values[i]
1118
+ i += 1
1119
+ attributes.imageIndex = values[i]
1120
+ i += 1
1121
+ attributes.frameCount = values[i]
1122
+ i += 1
1123
+ attributes.imageMin = values[i]
1124
+ i += 1
1125
+ attributes.imageMax = values[i]
1126
+ i += 1
1127
+ attributes.imageMean = values[i]
1128
+ i += 1
1129
+ attributes.imageStd = values[i]
1130
+ i += 1
1131
+ attributes.eppix = values[i]
1132
+ i += 1
1133
+ attributes.eps = values[i]
1134
+ i += 1
1135
+ attributes.eppixps = values[i]
1136
+ i += 1
1137
+ attributes.epa2 = values[i]
1138
+ i += 1
1139
+ attributes.eppixpf = values[i]
1140
+ i += 1
1141
+ if commandVersion >= 12:
1142
+ attributes.eppix_incident = values[i]
1143
+ i += 1
1144
+ attributes.eps_incident = values[i]
1145
+ i += 1
1146
+ attributes.eppixps_incident = values[i]
1147
+ i += 1
1148
+ attributes.epa2_incident = values[i]
1149
+ i += 1
1150
+ attributes.eppixpf_incident = values[i]
1151
+ i += 1
1152
+ if commandVersion >= 11:
1153
+ attributes.saturation = values[i]
1154
+ i += 1
1155
+ if commandVersion < 10:
1156
+ attributes.underExposureRate = values[i]
1157
+ i += 1
1158
+ attributes.overExposureRate = values[i]
1159
+ i += 1
1160
+ attributes.timestamp = float(values[i])
1161
+ i += 1
1162
+ if commandVersion >= 10:
1163
+ attributes.autoStretchMin = values[i]
1164
+ i += 1
1165
+ attributes.autoStretchMax = values[i]
1166
+ i += 1
1167
+ attributes.autoStretchGamma = values[i]
1168
+ i += 1
1169
+
1170
+ if (
1171
+ histogram != None
1172
+ and histoBins > 0
1173
+ and len(values) >= i + histogram.bins
1174
+ ):
1175
+ histogram.data = [0] * histogram.bins
1176
+ histogram.min = values[i]
1177
+ i += 1
1178
+ histogram.max = values[i]
1179
+ i += 1
1180
+ if commandVersion >= 11:
1181
+ histogram.upperMostLocalMaxima = values[i]
1182
+ i += 1
1183
+ for j in range(histogram.bins):
1184
+ log.debug("Hist %d: %d" % (j, values[i + j]))
1185
+ histogram.data[j] = values[i + j]
1186
+
1187
+ if pixelFormat == PixelFormat.FLOAT32:
1188
+ imageDataType = numpy.float32
1189
+ elif pixelFormat == PixelFormat.UINT16:
1190
+ imageDataType = numpy.uint16
1191
+ else:
1192
+ imageDataType = numpy.uint8
1193
+
1194
+ self.width = attributes.frameWidth
1195
+ self.height = attributes.frameHeight
1196
+
1197
+ recvbyteSizeString = self._recvFromSocket(
1198
+ self.socket, 4
1199
+ ) # get the first 4 bytes
1200
+ if len(recvbyteSizeString) == 4:
1201
+ recvbyteSize = struct.unpack(
1202
+ "I", recvbyteSizeString
1203
+ ) # interpret as size
1204
+ received_string = self._recvFromSocket(
1205
+ self.socket, recvbyteSize[0]
1206
+ ) # get the rest
1207
+ data_header = pb.DEPacket()
1208
+ data_header.ParseFromString(received_string)
1209
+ bytesize = data_header.data_header.bytesize
1210
+
1211
+ if self.usingMmf:
1212
+ image = numpy.frombuffer(
1213
+ self.mmf,
1214
+ offset=MMF_DATA_HEADER_SIZE,
1215
+ dtype=imageDataType,
1216
+ count=self.width * self.height,
1217
+ )
1218
+ image.shape = [self.height, self.width]
1219
+ bytesize = self.width * self.height * 2
1220
+ elif bytesize > 0:
1221
+ packet = self._recvFromSocket(self.socket, bytesize)
1222
+ if len(packet) == bytesize:
1223
+ image = numpy.frombuffer(packet, imageDataType)
1224
+ bytesize = self.height * self.width * 2
1225
+ image.shape = [self.height, self.width]
1226
+ else:
1227
+ log.error(
1228
+ "The size of the image does not match the expected size from "
1229
+ "The header. Expected: %d, Received: %d",
1230
+ bytesize,
1231
+ len(packet),
1232
+ )
1233
+
1234
+ if logLevel == logging.DEBUG:
1235
+ elapsed = self.GetTime() - step_time
1236
+ log.debug(
1237
+ "Transfer time: %.1f ms, %d bytes, %d mbps",
1238
+ elapsed * 1000,
1239
+ bytesize,
1240
+ bytesize * 8 / elapsed / 1024 / 1024,
1241
+ )
1242
+ step_time = self.GetTime()
1243
+
1244
+ if bytesize <= 0:
1245
+ log.error(" GetResut failed! An empty image will be returned.")
1246
+ image = None
1247
+
1248
+ if logLevel == logging.DEBUG:
1249
+ lapsed = (self.GetTime() - step_time) * 1000
1250
+ log.debug(" Saving Time: %.1f ms", lapsed)
1251
+ step_time = self.GetTime()
1252
+
1253
+ if image is None:
1254
+ log.error(" GetResut failed!")
1255
+ else:
1256
+ image = image.astype(imageDataType)
1257
+
1258
+ if logLevel == logging.DEBUG:
1259
+ lapsed = (self.GetTime() - step_time) * 1000
1260
+ log.debug(" Typing Time: %.1f ms", lapsed)
1261
+ step_time = self.GetTime()
1262
+
1263
+ if logLevel <= logging.DEBUG:
1264
+ lapsed = (self.GetTime() - start_time) * 1000
1265
+ log.debug(
1266
+ "GetResult frameType:%s, pixelFormat:%s ROI:[%d, %d] Binning:[%d, %d], Return size:[%d, %d], datasetName:%s acqCount:%d, frameCount:%d min:%.1f max:%.1f mean:%.1f std:%.1f %.1f ms",
1267
+ frameType,
1268
+ pixelFormat,
1269
+ attributes.datasetName,
1270
+ attributes.acqIndex,
1271
+ attributes.frameCount,
1272
+ attributes.imageMin,
1273
+ attributes.imageMax,
1274
+ attributes.imageMean,
1275
+ attributes.imageStd,
1276
+ lapsed,
1277
+ )
1278
+
1279
+ return image, pixelFormat, attributes, histogram
1280
+
1281
+ @write_only
1282
+ def set_virtual_mask(self, id, w, h, mask):
1283
+ """
1284
+ Set the virtual mask of the current camera on DE-Server.
1285
+
1286
+ Parameters
1287
+ ----------
1288
+ id : int
1289
+ The id of the mask. 0-3
1290
+ w : int
1291
+ The width of the mask
1292
+ h : int
1293
+ The height of the mask
1294
+ mask : np.ndarray
1295
+ The mask to set
1296
+ """
1297
+ if id < 1 or id > 4:
1298
+ log.error(
1299
+ " SetVirtualMask The virtual mask id must be selected between 1-4"
1300
+ )
1301
+ ret = False
1302
+ elif w < 0 or h < 0:
1303
+ log.error(
1304
+ " SetVirtualMask The virtual mask width and height must greater than 0"
1305
+ )
1306
+ ret = False
1307
+ else:
1308
+ command = self._addSingleCommand(self.SET_VIRTUAL_MASK, None, [id, w, h])
1309
+ ret = True
1310
+ try:
1311
+ packet = (
1312
+ struct.pack("I", command.ByteSize()) + command.SerializeToString()
1313
+ )
1314
+ self.socket.send(packet)
1315
+ except socket.error as e:
1316
+ ret = False
1317
+
1318
+ if ret:
1319
+ mask_bytes = mask.tobytes()
1320
+ self.__sendToSocket(self.socket, mask_bytes, len(mask_bytes))
1321
+
1322
+ ret = self.__ReceiveResponseForCommand(command) != False
1323
+
1324
+ return ret
1325
+
1326
+ def get_movie_buffer_info(self, movieBufferInfo=None, timeoutMsec=5000):
1327
+ """
1328
+ Get the movie buffer information of the current camera on DE-Server.
1329
+
1330
+ Parameters
1331
+ ----------
1332
+ movieBufferInfo : MovieBufferInfo, optional
1333
+ The movie buffer information to get, by default None. If None
1334
+ a new MovieBufferInfo object will be created.
1335
+ timeoutMsec : int, optional
1336
+ The timeout in milliseconds, by default 5000"""
1337
+
1338
+ if movieBufferInfo == None:
1339
+ movieBufferInfo = MovieBufferInfo()
1340
+ command = self._addSingleCommand(self.GET_MOVIE_BUFFER_INFO, None, None)
1341
+
1342
+ response = self._sendCommand(command)
1343
+
1344
+ if response != False:
1345
+ values = self.__getParameters(response.acknowledge[0])
1346
+ if type(values) is list:
1347
+ movieBufferInfo.headerBytes = values[0]
1348
+ movieBufferInfo.imageBufferBytes = values[1]
1349
+ movieBufferInfo.frameIndexStartPos = values[2]
1350
+ movieBufferInfo.imageStartPos = values[3]
1351
+ movieBufferInfo.imageW = values[4]
1352
+ movieBufferInfo.imageH = values[5]
1353
+ movieBufferInfo.framesInBuffer = values[6]
1354
+ dataType = values[7]
1355
+ movieBufferInfo.imageDataType = DataType(dataType)
1356
+
1357
+ return movieBufferInfo
1358
+
1359
+ def get_movie_buffer(
1360
+ self, movieBuffer, movieBufferSize, numFrames, timeoutMsec=5000
1361
+ ):
1362
+ """
1363
+ Get the movie buffer of the current camera on DE-Server. The movie buffer
1364
+ is a series of frames that are stored in memory and can be retrieved as
1365
+ a single buffer for faster processing.
1366
+
1367
+ """
1368
+
1369
+ movieBufferStatus = MovieBufferStatus.UNKNOWN
1370
+ retval = True
1371
+
1372
+ command = self._addSingleCommand(self.GET_MOVIE_BUFFER, None, [timeoutMsec])
1373
+ response = self._sendCommand(command)
1374
+
1375
+ if response != False:
1376
+ totalBytes = 0
1377
+ status = 0
1378
+ values = self.__getParameters(response.acknowledge[0])
1379
+ if type(values) is list:
1380
+ status = values[0]
1381
+ totalBytes = values[1]
1382
+ numFrames = values[2]
1383
+ movieBufferStatus = MovieBufferStatus(status)
1384
+ if movieBufferStatus == MovieBufferStatus.OK:
1385
+ if totalBytes == 0 or movieBufferSize < totalBytes:
1386
+ retval = False
1387
+ log.error(
1388
+ f"Image received did not have the expected size."
1389
+ f"expected: {totalBytes}, received: {movieBufferSize}"
1390
+ )
1391
+ else:
1392
+ print("reading movie buffer", totalBytes)
1393
+ movieBuffer = self._recvFromSocket(self.socket, totalBytes)
1394
+ print("Done reading movie buffer")
1395
+ else:
1396
+ retval = False
1397
+
1398
+ if not retval:
1399
+ movieBufferStatus = MovieBufferStatus.FAILED
1400
+
1401
+ return movieBufferStatus, totalBytes, numFrames, movieBuffer
1402
+
1403
+ def save_image(self, image, fileName, textSize=0):
1404
+ t0 = self.GetTime()
1405
+ filePath = self.debugImagesFolder + fileName + ".tif"
1406
+ try:
1407
+ if not os.path.exists(self.debugImagesFolder):
1408
+ os.makedirs(self.debugImagesFolder)
1409
+
1410
+ tiff = Image.fromarray(image)
1411
+ tiff.save(filePath)
1412
+ log.info("Saved %s" % filePath)
1413
+
1414
+ # if textSize > 0:
1415
+ # self.__saveText(image, fileName, textSize)
1416
+
1417
+ except OSError:
1418
+ log.error("Failed to save file")
1419
+
1420
+ if logLevel == logging.DEBUG or True:
1421
+ log.debug("Save time: %.1f ms", (self.GetTime() - t0) * 1000)
1422
+
1423
+ return filePath
1424
+
1425
+ def print_server_info(self, camera=None):
1426
+ """
1427
+ Print out the server information
1428
+ """
1429
+ if camera is None:
1430
+ camera = self["Camera Name"]
1431
+ print("Time : " + datetime.now().strftime("%m/%d/%Y, %H:%M:%S"))
1432
+ print("Computer : " + socket.gethostname())
1433
+ print("DE-Server : " + self.GetProperty("Server Software Version"))
1434
+ print("CPU : " + self.GetProperty("Computer CPU Info"))
1435
+ print("GPU : " + self.GetProperty("Computer GPU Info"))
1436
+ print("Memory : " + self.GetProperty("Computer Memory Info"))
1437
+ print("Camera Name : " + camera)
1438
+ print("Camera S/N : " + str(self.GetProperty("Camera SN")))
1439
+ print("Sensor S/N : " + str(self.GetProperty("Sensor Module SN")))
1440
+ print("Firmware : " + str(self.GetProperty("Firmware Version")))
1441
+ print("Python : " + sys.version.split("(")[0])
1442
+ print("Client : " + version)
1443
+ print("Interrupt : " + str(self.GetProperty("Interrupt Status")))
1444
+
1445
+ def print_acquisition_info(self):
1446
+ """
1447
+ Print out the acquisition information
1448
+ """
1449
+ hwW = self.GetProperty("Hardware ROI - Size X")
1450
+ hwH = self.GetProperty("Hardware ROI - Size Y")
1451
+ hwX = self.GetProperty("Hardware ROI Offset X")
1452
+ hwY = self.GetProperty("Hardware ROI Offset Y")
1453
+ hwBinX = self.GetProperty("Hardware Binning X")
1454
+ hwBinY = self.GetProperty("Hardware Binning Y")
1455
+
1456
+ swW = self.GetProperty("ROI Size X")
1457
+ swH = self.GetProperty("ROI Size Y")
1458
+ swX = self.GetProperty("ROI Offset X")
1459
+ swY = self.GetProperty("ROI Offset Y")
1460
+ swBinX = self.GetProperty("Binning X")
1461
+ swBinY = self.GetProperty("Binning Y")
1462
+
1463
+ print("Test Pattern: " + self.GetProperty("Test Pattern"))
1464
+ print("Log Level : " + self.GetProperty("Log Level"))
1465
+ print(
1466
+ "Sensor size : "
1467
+ + str(self.GetProperty("Sensor Size X (pixels)"))
1468
+ + " x "
1469
+ + str(self.GetProperty("Sensor Size Y (pixels)"))
1470
+ )
1471
+ print("HW ROI : " + str("%d x %d at (%d, %d)" % (hwW, hwH, hwX, hwY)))
1472
+ print("HW Binning : " + "%d x %d" % (hwBinX, hwBinY))
1473
+ print("SW ROI : " + str("%d x %d at (%d, %d)" % (swW, swH, swX, swY)))
1474
+ print("SW Binning : " + "%d x %d" % (swBinX, swBinY))
1475
+ print(
1476
+ "Image size : "
1477
+ + str(self.GetProperty("Image Size X (pixels)"))
1478
+ + " x "
1479
+ + str(self.GetProperty("Image Size Y (pixels)"))
1480
+ )
1481
+ print("Max FPS : " + str(self.GetProperty("Frames Per Second (Max)")))
1482
+ print("FPS : " + str(self.GetProperty("Frames Per Second")))
1483
+ print("Grab Buffers: " + str(self.GetProperty("Grab Buffer Size")))
1484
+
1485
+ def print_saving_info(self):
1486
+ """
1487
+ Print out the saving information
1488
+ """
1489
+ sys.stdout.write("Saving : ")
1490
+
1491
+ if self.GetProperty("Autosave Crude Frames") == "On":
1492
+ sys.stdout.write("crude ")
1493
+ if self.GetProperty("Autosave Raw Frames") == "On":
1494
+ sys.stdout.write("raw ")
1495
+ if self.GetProperty("Autosave Single Frames") == "On":
1496
+ sys.stdout.write("frames ")
1497
+ if self.GetProperty("Autosave Integrating Frames") == "On":
1498
+ sys.stdout.write("integrating frames ")
1499
+ if self.GetProperty("Autosave Movie") == "On":
1500
+ sys.stdout.write("movie(")
1501
+ sys.stdout.write(str(self.GetProperty("Autosave Movie - Sum Count")))
1502
+ sys.stdout.write(") ")
1503
+ if self.GetProperty("Autosave Final Image") == "On":
1504
+ sys.stdout.write("final ")
1505
+
1506
+ print("")
1507
+
1508
+ def grab(self, frames=1, dataSetName="", fileName=None):
1509
+ """
1510
+ Grab specified number of frames and print out stats. Mostly used for testing purposes
1511
+
1512
+ Parameters
1513
+ ----------
1514
+ frames : int, optional
1515
+ The number of frames requested, by default 1
1516
+ dataSetName : str, optional
1517
+ Data set name to be used, by default ""
1518
+ fileName : str, optional
1519
+ Save the returned image as a file if provided, by default None
1520
+ """
1521
+ imageW = self.GetProperty("Image Size X (pixels)")
1522
+ imageH = self.GetProperty("Image Size Y (pixels)")
1523
+ fps = self.GetProperty("Frames Per Second")
1524
+
1525
+ self.SetProperty("Exposure Time (seconds)", frames / fps)
1526
+ expoSec = self.GetProperty("Exposure Time (seconds)")
1527
+ maxExpoSec = self.GetProperty("Exposure Time Max (seconds)")
1528
+ prevSuffix = self.GetProperty("Autosave Filename Suffix")
1529
+
1530
+ frames = round(expoSec * fps)
1531
+ # frames = expoSec * fps
1532
+
1533
+ if dataSetName != "" and dataSetName != None:
1534
+ self.SetProperty("Autosave Filename Suffix", dataSetName)
1535
+ dataSetName = dataSetName + " "
1536
+
1537
+ if dataSetName != None:
1538
+ sys.stdout.write("%s%dx%d fps:%.3f " % (dataSetName, imageW, imageH, fps))
1539
+ sys.stdout.flush()
1540
+
1541
+ t0 = self.GetTime()
1542
+ self.StartAcquisition(1)
1543
+
1544
+ frameType = FrameType.SUMTOTAL
1545
+
1546
+ pixelFormat = PixelFormat.AUTO
1547
+ attributes = Attributes()
1548
+ histogram = Histogram()
1549
+ image = self.GetResult(frameType, pixelFormat, attributes, histogram)[0]
1550
+
1551
+ duration = self.GetTime() - t0
1552
+ measuredFps = self.GetProperty("Measured Frame Rate")
1553
+ missedFrames = self.GetProperty("Missed Frames")
1554
+
1555
+ frameCount = self.GetProperty("Number of Frames Processed")
1556
+ sys.stdout.write(
1557
+ "mfps:%.3f et:%.2fs dur:%.2fs frames:%d/%d %s %s, min:%4.1f max:%4.0f mean:%8.3f std:%8.3f timestamp:%10.6f"
1558
+ % (
1559
+ measuredFps,
1560
+ expoSec,
1561
+ duration,
1562
+ frameCount,
1563
+ frames,
1564
+ frameType,
1565
+ pixelFormat,
1566
+ attributes.imageMin,
1567
+ attributes.imageMax,
1568
+ attributes.imageMean,
1569
+ attributes.imageStd,
1570
+ attributes.timestamp,
1571
+ )
1572
+ )
1573
+ sys.stdout.flush()
1574
+
1575
+ if max == 0:
1576
+ sys.stdout.write(", empty")
1577
+
1578
+ if missedFrames > 0:
1579
+ sys.stdout.write(", missed:%d " % (missedFrames))
1580
+
1581
+ sys.stdout.flush()
1582
+
1583
+ self.WaitForSavingFiles(dataSetName == None)
1584
+ self.SetProperty("Autosave Filename Suffix", prevSuffix)
1585
+
1586
+ if fileName and len(fileName) > 0:
1587
+ self.SaveImage(image, fileName)
1588
+
1589
+ return image
1590
+
1591
+ def wait_for_saving_files(self, quiet=True):
1592
+ """
1593
+ Wait for the saving files to complete
1594
+ """
1595
+
1596
+ t0 = self.GetTime()
1597
+ saveCrude = True if self.GetProperty("Autosave Crude Frames") == "On" else False
1598
+ saveRaw = True if self.GetProperty("Autosave Raw Frames") == "On" else False
1599
+ saveFrame = (
1600
+ True if self.GetProperty("Autosave Single Frames") == "On" else False
1601
+ )
1602
+ saveMovie = True if self.GetProperty("Autosave Movie") == "On" else False
1603
+ counting = (
1604
+ True
1605
+ if self.GetProperty("Image Processing - Mode") != "Integrating"
1606
+ else False
1607
+ )
1608
+
1609
+ saveIntegratingFrames = saveFrame and (
1610
+ not counting or self.GetProperty("Autosave Integrating Frames") == "On"
1611
+ )
1612
+ saveCountingFrames = saveFrame and counting
1613
+ saveIntegratingMovie = saveMovie and not counting
1614
+ saveCountingMovie = saveMovie and counting
1615
+
1616
+ saveFinal = True if self.GetProperty("Autosave Final Image") == "On" else False
1617
+ expMode = self.GetProperty("Exposure Mode")
1618
+
1619
+ if expMode == "Dark" or expMode == "Gain":
1620
+ repeats = self.GetProperty("Remaining Number of Acquisitions")
1621
+ remaining = self.GetProperty("Remaining Number of Acquisitions")
1622
+ if remaining > 0:
1623
+ for i in range(repeats * 10):
1624
+ remaining = self.GetProperty("Remaining Number of Acquisitions")
1625
+ if remaining > 0:
1626
+ sleep(1)
1627
+ else:
1628
+ break
1629
+ elif (
1630
+ saveCrude
1631
+ or saveRaw
1632
+ or saveIntegratingFrames
1633
+ or saveCountingFrames
1634
+ or saveIntegratingMovie
1635
+ or saveCountingMovie
1636
+ or saveFinal
1637
+ ):
1638
+ for i in range(1000):
1639
+ if self.GetProperty("Autosave Status") in ["Starting", "In Progress"]:
1640
+ if not quiet:
1641
+ sys.stdout.write(".")
1642
+ sys.stdout.flush()
1643
+ sleep(1)
1644
+ else:
1645
+ if not quiet:
1646
+ sys.stdout.write(
1647
+ " \tgrab:%4.0fMB/s"
1648
+ % self.GetProperty("Speed - Grabbing (MB/s)")
1649
+ )
1650
+ sys.stdout.write(
1651
+ " proc:%4.0fMB/s"
1652
+ % self.GetProperty("Speed - Processing (MB/s)")
1653
+ )
1654
+ sys.stdout.write(
1655
+ " save:%4.0fMB/s"
1656
+ % self.GetProperty("Speed - Writing (MB/s)")
1657
+ )
1658
+
1659
+ if saveCrude:
1660
+ sys.stdout.write(
1661
+ " crude:%d"
1662
+ % self.GetProperty("Autosave Crude Frames Written")
1663
+ )
1664
+ if saveRaw:
1665
+ sys.stdout.write(
1666
+ " raw:%d"
1667
+ % self.GetProperty("Autosave Raw Frames Written")
1668
+ )
1669
+ if saveIntegratingFrames:
1670
+ # sys.stdout.write(" frames:%d" % self.GetProperty("Autosave Single Frames - Frames Written") )
1671
+ sys.stdout.write(
1672
+ " integrating frames:%d"
1673
+ % self.GetProperty(
1674
+ "Autosave Integrated Single Frames Written"
1675
+ )
1676
+ )
1677
+ if saveCountingFrames:
1678
+ sys.stdout.write(
1679
+ " counting frames:%d"
1680
+ % self.GetProperty(
1681
+ "Autosave Counted Single Frames Written"
1682
+ )
1683
+ )
1684
+ if saveIntegratingMovie:
1685
+ sys.stdout.write(
1686
+ " integrating movie:%d"
1687
+ % self.GetProperty(
1688
+ "Autosave Integrated Movie Frames Written"
1689
+ )
1690
+ )
1691
+ if saveCountingMovie:
1692
+ sys.stdout.write(
1693
+ " counting movie:%d"
1694
+ % self.GetProperty(
1695
+ "Autosave Counted Movie Frames Written"
1696
+ )
1697
+ )
1698
+ if saveFinal:
1699
+ sys.stdout.write(
1700
+ " final sum count:%d"
1701
+ % self.GetProperty("Autosave Final Image - Sum Count")
1702
+ )
1703
+
1704
+ break
1705
+
1706
+ duration = self.GetTime() - t0
1707
+ if not quiet:
1708
+ print(" %.1fs" % duration)
1709
+ sys.stdout.flush()
1710
+
1711
+ # Start acquisition and get a single image
1712
+ # fileName: the image will be saved to disk if a file name is provided
1713
+ # textSize: a text file with pixel values will be saved if textSize is given, for debugging/test
1714
+ def get_image(self, pixelFormat=PixelFormat.AUTO, fileName=None, textSize=0):
1715
+ """
1716
+ Get a single image and save it to disk if a file name is provided
1717
+
1718
+ Parameters
1719
+ ----------
1720
+ pixelFormat : PixelFormat, optional
1721
+ The pixel format of the image, by default PixelFormat.AUTO
1722
+ fileName : str, optional
1723
+ The file name to save the image, by default None
1724
+ textSize : int, optional
1725
+ The text size, by default 0
1726
+ """
1727
+ self.StartAcquisition(1)
1728
+ frameType = FrameType.SUMTOTAL
1729
+
1730
+ if pixelFormat == "float32":
1731
+ pixelFormat = PixelFormat.FLOAT32
1732
+
1733
+ elif pixelFormat == "uint16":
1734
+ pixelFormat = PixelFormat.UINT16
1735
+
1736
+ attributes = Attributes()
1737
+ histogram = Histogram()
1738
+ image = self.GetResult(frameType, pixelFormat, attributes, histogram)[0]
1739
+
1740
+ if fileName and len(fileName) > 0:
1741
+ self.SaveImage(image, fileName, textSize)
1742
+
1743
+ return image
1744
+
1745
+ @disable_scan
1746
+ def take_dark_reference(self, frameRate: float = 20):
1747
+ """
1748
+ Take dark reference images
1749
+
1750
+ Parameters
1751
+ ----------
1752
+ frameRate : float, optional
1753
+ The frame rate, by default 20 frames per second
1754
+ """
1755
+ sys.stdout.write("Taking dark references: ")
1756
+ sys.stdout.flush()
1757
+
1758
+ prevExposureMode = self.GetProperty("Exposure Mode")
1759
+ prevExposureTime = self.GetProperty("Exposure Time (seconds)")
1760
+
1761
+ acquisitions = 20
1762
+ self.SetProperty("Exposure Mode", "Dark")
1763
+ self.SetProperty("Frames Per Second", frameRate)
1764
+ self.SetProperty("Exposure Time (seconds)", 1)
1765
+ self.StartAcquisition(acquisitions)
1766
+
1767
+ while True:
1768
+ attributes = Attributes()
1769
+ histogram = Histogram()
1770
+ image = self.GetResult(
1771
+ FrameType.SUMTOTAL, PixelFormat.FLOAT32, attributes, histogram
1772
+ )
1773
+
1774
+ sys.stdout.write(str(attributes.acqIndex) + " ")
1775
+ sys.stdout.flush()
1776
+ remaining = self.GetProperty("Remaining Number of Acquisitions")
1777
+
1778
+ if remaining == 0:
1779
+ print("done.")
1780
+ break
1781
+
1782
+ self.SetProperty("Exposure Mode", prevExposureMode)
1783
+ self.SetProperty("Exposure Time (seconds)", prevExposureTime)
1784
+
1785
+ def get_time(self):
1786
+ """
1787
+ Get the current time from the system clock
1788
+ """
1789
+ if sys.version_info[0] < 3:
1790
+ return time.clock()
1791
+ else:
1792
+ return time.perf_counter()
1793
+
1794
+ # private methods
1795
+
1796
+ def __del__(self):
1797
+ if self.connected:
1798
+ self.disconnect()
1799
+
1800
+ # get multiple parameters from a single acknowledge packet
1801
+ def __getParameters(self, single_acknowledge=None):
1802
+ output = []
1803
+ if single_acknowledge is None:
1804
+ return output
1805
+ if single_acknowledge.error == True:
1806
+ return output
1807
+ for one_parameter in single_acknowledge.parameter:
1808
+ if one_parameter.type == pb.AnyParameter.P_BOOL:
1809
+ output.append(one_parameter.p_bool)
1810
+ elif one_parameter.type == pb.AnyParameter.P_STRING:
1811
+ output.append(one_parameter.p_string)
1812
+ elif one_parameter.type == pb.AnyParameter.P_INT:
1813
+ output.append(one_parameter.p_int)
1814
+ elif one_parameter.type == pb.AnyParameter.P_FLOAT:
1815
+ output.append(one_parameter.p_float)
1816
+ return output
1817
+
1818
+ # get strings from a single command response
1819
+ def __getStrings(self, command_id=None, param=None):
1820
+ if command_id is None:
1821
+ return False
1822
+ command = self._addSingleCommand(command_id, param)
1823
+ response = self._sendCommand(command)
1824
+ if response != False:
1825
+ return self.__getParameters(response.acknowledge[0])
1826
+ else:
1827
+ return False
1828
+
1829
+ # add a new command (with optional label and parameter)
1830
+ def _addSingleCommand(self, command_id=None, label=None, params=None):
1831
+ if command_id is None:
1832
+ return False
1833
+ command = pb.DEPacket() # create the command packet
1834
+ command.type = pb.DEPacket.P_COMMAND
1835
+ singlecommand1 = command.command.add() # add the first single command
1836
+ singlecommand1.command_id = command_id + commandVersion * 100
1837
+ if not label is None:
1838
+ str_param = command.command[0].parameter.add()
1839
+ str_param.type = pb.AnyParameter.P_STRING
1840
+ str_param.p_string = label
1841
+ str_param.name = "label"
1842
+
1843
+ if not params is None:
1844
+ for param in params:
1845
+ if isinstance(param, bool):
1846
+ bool_param = command.command[0].parameter.add()
1847
+ bool_param.type = pb.AnyParameter.P_BOOL
1848
+ bool_param.p_bool = bool(param)
1849
+ bool_param.name = "val"
1850
+ elif isinstance(param, int) or isinstance(param, np.int32):
1851
+ int_param = command.command[0].parameter.add()
1852
+ int_param.type = pb.AnyParameter.P_INT
1853
+ int_param.p_int = int(param)
1854
+ int_param.name = "val"
1855
+ elif isinstance(param, float):
1856
+ float_param = command.command[0].parameter.add()
1857
+ float_param.type = pb.AnyParameter.P_FLOAT
1858
+ float_param.p_float = param
1859
+ float_param.name = "val"
1860
+ else:
1861
+ str_param = command.command[0].parameter.add()
1862
+ str_param.type = pb.AnyParameter.P_STRING
1863
+ str_param.p_string = str(param)
1864
+ str_param.name = "val"
1865
+ return command
1866
+
1867
+ # send single command and get a response, if error occurred, return False
1868
+ def _sendCommand(self, command=None):
1869
+ step_time = self.GetTime()
1870
+
1871
+ if command is None:
1872
+ return False
1873
+
1874
+ if len(command.camera_name) == 0:
1875
+ command.camera_name = (
1876
+ self.currCamera
1877
+ ) # append the current camera name if necessary
1878
+
1879
+ try:
1880
+ packet = struct.pack("I", command.ByteSize()) + command.SerializeToString()
1881
+ res = self.socket.send(packet)
1882
+ # packet.PrintDebugString()
1883
+ # log.debug("sent result = %d\n", res)
1884
+ except:
1885
+ log.error("Error sending %s\n", command)
1886
+
1887
+ if logLevel == logging.DEBUG:
1888
+ lapsed = (self.GetTime() - step_time) * 1000
1889
+ log.debug(" Send Time: %.1f ms", lapsed)
1890
+ step_time = self.GetTime()
1891
+
1892
+ return self.__ReceiveResponseForCommand(command)
1893
+
1894
+ def __ReceiveResponseForCommand(self, command):
1895
+ step_time = self.GetTime()
1896
+
1897
+ recvbyteSizeString = self._recvFromSocket(
1898
+ self.socket, 4
1899
+ ) # get the first 4 byte
1900
+
1901
+ if len(recvbyteSizeString) == 4:
1902
+ recvbyteSize = struct.unpack("I", recvbyteSizeString) # interpret as size
1903
+ log.debug("-- recvbyteSize: " + str(recvbyteSize))
1904
+ received_string = self._recvFromSocket(
1905
+ self.socket, recvbyteSize[0]
1906
+ ) # get the rest
1907
+ if logLevel == logging.DEBUG:
1908
+ lapsed = (self.GetTime() - step_time) * 1000
1909
+ log.debug(" Recv Time: %.1f ms, %d bytes", lapsed, recvbyteSize[0])
1910
+ step_time = self.GetTime()
1911
+
1912
+ Acknowledge_return = pb.DEPacket()
1913
+ Acknowledge_return.ParseFromString(received_string) # parse the byte string
1914
+ if logLevel == logging.DEBUG:
1915
+ lapsed = (self.GetTime() - step_time) * 1000
1916
+ log.debug("Parse Time: %.1f ms", lapsed)
1917
+ step_time = self.GetTime()
1918
+
1919
+ if (
1920
+ Acknowledge_return.type == pb.DEPacket.P_ACKNOWLEDGE
1921
+ ): # has to be an acknowledge packet
1922
+ if len(command.command) <= len(Acknowledge_return.acknowledge):
1923
+ error = False
1924
+ for one_ack in Acknowledge_return.acknowledge:
1925
+ error = error or one_ack.error
1926
+ if error:
1927
+ message = Acknowledge_return.acknowledge[0].error_message
1928
+ if logLevel == logging.DEBUG:
1929
+ log.error(
1930
+ "Server returned error for request :\n"
1931
+ + str(command)
1932
+ + "\n"
1933
+ + "Response :\n"
1934
+ + str(message)
1935
+ )
1936
+ elif not message.startswith("Unknown property"):
1937
+ log.error(message)
1938
+ else:
1939
+ if logLevel == logging.DEBUG:
1940
+ lapsed = (self.GetTime() - step_time) * 1000
1941
+ log.debug(" Ack Time: %.1f ms", lapsed)
1942
+ step_time = self.GetTime()
1943
+ return Acknowledge_return
1944
+ else:
1945
+ log.error(
1946
+ "len(command.command):%d != len(Acknowledge_return.acknowledge):%d",
1947
+ len(command.command),
1948
+ len(Acknowledge_return.acknowledge),
1949
+ )
1950
+ else:
1951
+ log.error("Response from server is not ACK")
1952
+ else:
1953
+ log.error(
1954
+ "Server response is %d bytes, shorter than mimumum of 4 bytes",
1955
+ len(recvbyteSizeString),
1956
+ )
1957
+
1958
+ return False
1959
+
1960
+ def _recvFromSocket(self, sock, bytes):
1961
+ timeout = self.exposureTime * 10 + 30
1962
+ startTime = self.GetTime()
1963
+ self.socket.settimeout(timeout)
1964
+
1965
+ buffer = b""
1966
+
1967
+ total_len = len(buffer)
1968
+ upper_lim = 4096 * 4096 * 12 # 4096 #1024*256
1969
+ while total_len < bytes:
1970
+ bytes_left = bytes - total_len
1971
+ if bytes_left < upper_lim:
1972
+ packet_size = bytes_left
1973
+ else:
1974
+ packet_size = upper_lim
1975
+ loopTime = self.GetTime()
1976
+ try:
1977
+ buffer += sock.recv(packet_size)
1978
+
1979
+ except socket.timeout:
1980
+ log.debug(
1981
+ " __recvFromSocket : timeout in trying to receive %d bytes in %.1f ms",
1982
+ bytes,
1983
+ (self.GetTime() - loopTime) * 1000,
1984
+ )
1985
+ if self.GetTime() - startTime > timeout:
1986
+ log.error(" __recvFromSocket: max timeout %d seconds", timeout)
1987
+ break
1988
+ else:
1989
+ pass # continue further
1990
+ except:
1991
+ log.error(
1992
+ "Unknown exception occurred. Current Length: %d in %.1f ms",
1993
+ len(buffer),
1994
+ (self.GetTime() - loopTime) * 1000,
1995
+ )
1996
+ break
1997
+ total_len = len(buffer)
1998
+
1999
+ totalTimeMs = (self.GetTime() - startTime) * 1000
2000
+ Gbps = total_len * 8 / (totalTimeMs / 1000) / 1024 / 1024 / 1024
2001
+ log.debug(
2002
+ " __recvFromSocket :received %d of %d bytes in total in %.1f ms, %.1f Gbps",
2003
+ total_len,
2004
+ bytes,
2005
+ totalTimeMs,
2006
+ Gbps,
2007
+ )
2008
+
2009
+ return buffer
2010
+
2011
+ def __sendToSocket(self, sock, buffer, bytes):
2012
+ timeout = self.exposureTime * 10 + 30
2013
+ startTime = self.GetTime()
2014
+ self.socket.settimeout(timeout)
2015
+
2016
+ retval = True
2017
+ chunkSize = 4096
2018
+ for i in range(0, len(buffer), chunkSize):
2019
+ try:
2020
+ sock.send(buffer[i : min(len(buffer), i + chunkSize)])
2021
+ except socket.timeout:
2022
+
2023
+ log.debug(
2024
+ " __sendToSocket : timeout in trying to send %d bytes in %.1f ms",
2025
+ bytes,
2026
+ (self.GetTime() - loopTime) * 1000,
2027
+ )
2028
+ if self.GetTime() - startTime > timeout:
2029
+ log.error(" __recvFromSocket: max timeout %d seconds", timeout)
2030
+ retval = False
2031
+ break
2032
+ else:
2033
+ pass # continue further
2034
+ except socket.error as e:
2035
+ log.error(f"Error during send: {e}")
2036
+ # Handle the error as needed, e.g., close the connection
2037
+ retval = False
2038
+ break
2039
+ return buffer
2040
+
2041
+ def __saveText(self, image, fileName, textSize):
2042
+ text = open(self.debugImagesFolder + fileName + ".txt", "w+")
2043
+ line = "%s: [%d x %d]\n" % (fileName, image.shape[1], image.shape[0])
2044
+ text.write(line)
2045
+ for i in range(min(image.shape[0], textSize)):
2046
+ for j in range(min(image.shape[1], textSize)):
2047
+ text.write("%8.3f\t" % image[i][j])
2048
+
2049
+ if image.shape[1] > textSize:
2050
+ text.write(" ...\n")
2051
+ else:
2052
+ text.write("\n")
2053
+
2054
+ if image.shape[0] > textSize:
2055
+ text.write("...\n")
2056
+ else:
2057
+ text.write("\n")
2058
+
2059
+ def ParseChangedProperties(self, changedProperties, response):
2060
+ value = self.__getParameters(response.acknowledge[0])[0]
2061
+
2062
+ props = value.split("|")
2063
+
2064
+ try:
2065
+ for prop in props:
2066
+ p = prop.split(":")
2067
+
2068
+ if len(p) == 2:
2069
+ changedProperties[p[0]] = p[1]
2070
+ except Exception as e:
2071
+ log.error("Parse changed properties failed." + e.Message)
2072
+
2073
+ return changedProperties
2074
+
2075
+ # renamed methods to follow python standards
2076
+ GetServerVersion = get_server_version
2077
+ Connect = connect
2078
+ Disconnect = disconnect
2079
+ ListCameras = list_cameras
2080
+ GetCurrentCamera = get_current_camera
2081
+ SetCurrentCamera = set_current_camera
2082
+ ListProperties = list_properties
2083
+ GetPropertySpec = get_property_spec
2084
+ PropertyValidValues = property_valid_values
2085
+ GetProperty = get_property
2086
+ SetProperty = set_property
2087
+ SetPropertyAndGetChangedProperties = set_property_and_get_changed_properties
2088
+ setEngMode = set_engineering_mode
2089
+ SetHWROI = set_hw_roi
2090
+ SetHWROIAndGetChangedProperties = set_hw_roi_and_get_changed_properties
2091
+ SetSWROI = set_sw_roi
2092
+ SetSWROIAndGetChangedProperties = set_sw_roi_and_get_changed_properties
2093
+ StartAcquisition = start_acquisition
2094
+ StopAcquisition = stop_acquisition
2095
+ GetResult = get_result
2096
+ SetVirtualMask = set_virtual_mask
2097
+ GetMovieBufferInfo = get_movie_buffer_info
2098
+ GetMovieBuffer = get_movie_buffer
2099
+ SaveImage = save_image
2100
+ PrintServerInfo = print_server_info
2101
+ PrintAcqInfo = print_acquisition_info
2102
+ PrintSavingInfo = print_saving_info
2103
+ Grab = grab
2104
+ WaitForSavingFiles = wait_for_saving_files
2105
+ GetImage = get_image
2106
+ TakeDarkReference = take_dark_reference
2107
+ GetTime = get_time
2108
+
2109
+ # method setProperty was renamed to SetProperty. please use SetProperty
2110
+ setProperty = SetProperty
2111
+ getProperty = GetProperty
2112
+
2113
+ # private members
2114
+ width = 0
2115
+ height = 0
2116
+ mmf = 0
2117
+ usingMmf = True
2118
+ debugImagesFolder = "D:\\DebugImages\\"
2119
+ connected = False
2120
+ cameras = None
2121
+ currCamera = ""
2122
+ refreshProperties = True
2123
+ exposureTime = 1
2124
+ host = 0
2125
+ port = 0
2126
+ read_only = False
2127
+
2128
+ # command lists
2129
+ LIST_CAMERAS = 0
2130
+ LIST_PROPERTIES = 1
2131
+ LIST_ALLOWED_VALUES = 2
2132
+ GET_PROPERTY = 3
2133
+ SET_PROPERTY = 4
2134
+ GET_IMAGE_16U = 5
2135
+ GET_IMAGE_32F = 10
2136
+ STOP_ACQUISITION = 11
2137
+ GET_RESULT = 14
2138
+ START_ACQUISITION = 15
2139
+ SET_HW_ROI = 16
2140
+ SET_SW_ROI = 17
2141
+ GET_MOVIE_BUFFER_INFO = 18
2142
+ GET_MOVIE_BUFFER = 19
2143
+ SET_PROPERTY_AND_GET_CHANGED_PROPERTIES = 20
2144
+ SET_HW_ROI_AND_GET_CHANGED_PROPERTIES = 21
2145
+ SET_SW_ROI_AND_GET_CHANGED_PROPERTIES = 22
2146
+ SET_VIRTUAL_MASK = 23
2147
+ SAVE_FINAL_AFTER_ACQ = 24
2148
+ SET_ENG_MODE = 25
2149
+ SET_ENG_MODE_GET_CHANGED_PROPERTIES = 26
2150
+ SET_SCAN_SIZE = 27
2151
+ SET_SCAN_ROI = 28
2152
+ SET_SCAN_SIZE_AND_GET_CHANGED_PROPERTIES = 29
2153
+ SET_SCAN_ROI__AND_GET_CHANGED_PROPERTIES = 30
2154
+ SET_CLIENT_READ_ONLY = 31
2155
+
2156
+
2157
+ MMF_DATA_HEADER_SIZE = 24
2158
+ MMF_IMAGE_BUFFER_SIZE = 8192 * 16384 * 4
2159
+ MMF_DATA_BUFFER_SIZE = MMF_IMAGE_BUFFER_SIZE + MMF_DATA_HEADER_SIZE