UC2-REST 0.2.0.28__tar.gz → 0.2.0.29__tar.gz

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 (39) hide show
  1. {uc2_rest-0.2.0.28 → uc2_rest-0.2.0.29}/PKG-INFO +1 -1
  2. {uc2_rest-0.2.0.28 → uc2_rest-0.2.0.29}/UC2_REST.egg-info/PKG-INFO +1 -1
  3. {uc2_rest-0.2.0.28 → uc2_rest-0.2.0.29}/UC2_REST.egg-info/SOURCES.txt +2 -0
  4. {uc2_rest-0.2.0.28 → uc2_rest-0.2.0.29}/uc2rest/UC2Client.py +9 -1
  5. {uc2_rest-0.2.0.28 → uc2_rest-0.2.0.29}/uc2rest/__version__.py +1 -1
  6. uc2_rest-0.2.0.29/uc2rest/can.py +33 -0
  7. uc2_rest-0.2.0.29/uc2rest/lcddisplay.py +59 -0
  8. {uc2_rest-0.2.0.28 → uc2_rest-0.2.0.29}/uc2rest/motor.py +79 -3
  9. {uc2_rest-0.2.0.28 → uc2_rest-0.2.0.29}/uc2rest/mserial.py +15 -13
  10. {uc2_rest-0.2.0.28 → uc2_rest-0.2.0.29}/uc2rest/objective.py +1 -0
  11. {uc2_rest-0.2.0.28 → uc2_rest-0.2.0.29}/LICENSE +0 -0
  12. {uc2_rest-0.2.0.28 → uc2_rest-0.2.0.29}/README.md +0 -0
  13. {uc2_rest-0.2.0.28 → uc2_rest-0.2.0.29}/UC2_REST.egg-info/dependency_links.txt +0 -0
  14. {uc2_rest-0.2.0.28 → uc2_rest-0.2.0.29}/UC2_REST.egg-info/not-zip-safe +0 -0
  15. {uc2_rest-0.2.0.28 → uc2_rest-0.2.0.29}/UC2_REST.egg-info/requires.txt +0 -0
  16. {uc2_rest-0.2.0.28 → uc2_rest-0.2.0.29}/UC2_REST.egg-info/top_level.txt +0 -0
  17. {uc2_rest-0.2.0.28 → uc2_rest-0.2.0.29}/setup.cfg +0 -0
  18. {uc2_rest-0.2.0.28 → uc2_rest-0.2.0.29}/setup.py +0 -0
  19. {uc2_rest-0.2.0.28 → uc2_rest-0.2.0.29}/uc2rest/MockSerial.py +0 -0
  20. {uc2_rest-0.2.0.28 → uc2_rest-0.2.0.29}/uc2rest/__init__.py +0 -0
  21. {uc2_rest-0.2.0.28 → uc2_rest-0.2.0.29}/uc2rest/analog.py +0 -0
  22. {uc2_rest-0.2.0.28 → uc2_rest-0.2.0.29}/uc2rest/camera.py +0 -0
  23. {uc2_rest-0.2.0.28 → uc2_rest-0.2.0.29}/uc2rest/cmdrecorder.py +0 -0
  24. {uc2_rest-0.2.0.28 → uc2_rest-0.2.0.29}/uc2rest/digitalout.py +0 -0
  25. {uc2_rest-0.2.0.28 → uc2_rest-0.2.0.29}/uc2rest/galvo.py +0 -0
  26. {uc2_rest-0.2.0.28 → uc2_rest-0.2.0.29}/uc2rest/gripper.py +0 -0
  27. {uc2_rest-0.2.0.28 → uc2_rest-0.2.0.29}/uc2rest/home.py +0 -0
  28. {uc2_rest-0.2.0.28 → uc2_rest-0.2.0.29}/uc2rest/laser.py +0 -0
  29. {uc2_rest-0.2.0.28 → uc2_rest-0.2.0.29}/uc2rest/ledmatrix.py +0 -0
  30. {uc2_rest-0.2.0.28 → uc2_rest-0.2.0.29}/uc2rest/logger.py +0 -0
  31. {uc2_rest-0.2.0.28 → uc2_rest-0.2.0.29}/uc2rest/message.py +0 -0
  32. {uc2_rest-0.2.0.28 → uc2_rest-0.2.0.29}/uc2rest/modules.py +0 -0
  33. {uc2_rest-0.2.0.28 → uc2_rest-0.2.0.29}/uc2rest/pid.py +0 -0
  34. {uc2_rest-0.2.0.28 → uc2_rest-0.2.0.29}/uc2rest/rotator.py +0 -0
  35. {uc2_rest-0.2.0.28 → uc2_rest-0.2.0.29}/uc2rest/slm.py +0 -0
  36. {uc2_rest-0.2.0.28 → uc2_rest-0.2.0.29}/uc2rest/state.py +0 -0
  37. {uc2_rest-0.2.0.28 → uc2_rest-0.2.0.29}/uc2rest/temperature.py +0 -0
  38. {uc2_rest-0.2.0.28 → uc2_rest-0.2.0.29}/uc2rest/utils.py +0 -0
  39. {uc2_rest-0.2.0.28 → uc2_rest-0.2.0.29}/uc2rest/wifi.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: UC2-REST
3
- Version: 0.2.0.28
3
+ Version: 0.2.0.29
4
4
  Summary: This pacage will help you to drive the ESP32-driven microscopy control modules from UC2
5
5
  Home-page: https://github.com/openUC2/UC2-REST
6
6
  Author: Benedict Diederich
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: UC2-REST
3
- Version: 0.2.0.28
3
+ Version: 0.2.0.29
4
4
  Summary: This pacage will help you to drive the ESP32-driven microscopy control modules from UC2
5
5
  Home-page: https://github.com/openUC2/UC2-REST
6
6
  Author: Benedict Diederich
@@ -13,12 +13,14 @@ uc2rest/__init__.py
13
13
  uc2rest/__version__.py
14
14
  uc2rest/analog.py
15
15
  uc2rest/camera.py
16
+ uc2rest/can.py
16
17
  uc2rest/cmdrecorder.py
17
18
  uc2rest/digitalout.py
18
19
  uc2rest/galvo.py
19
20
  uc2rest/gripper.py
20
21
  uc2rest/home.py
21
22
  uc2rest/laser.py
23
+ uc2rest/lcddisplay.py
22
24
  uc2rest/ledmatrix.py
23
25
  uc2rest/logger.py
24
26
  uc2rest/message.py
@@ -9,6 +9,7 @@ from .mserial import Serial
9
9
  from .mserial import SerialManagerWrapper
10
10
  from .galvo import Galvo
11
11
  from .ledmatrix import LedMatrix
12
+ from .lcddisplay import LCDDisplay
12
13
  from .motor import Motor
13
14
  from .gripper import Gripper
14
15
  from .home import Home
@@ -25,6 +26,7 @@ from .logger import Logger
25
26
  from .cmdrecorder import cmdRecorder
26
27
  from .temperature import Temperature
27
28
  from .message import Message
29
+ from .can import CAN
28
30
  try:
29
31
  import requests
30
32
  except:
@@ -88,7 +90,7 @@ class UC2Client(object):
88
90
 
89
91
  # import libraries depending on API version
90
92
  self.logger.debug("Using API version 2")
91
-
93
+
92
94
  # initialize state
93
95
  self.state = State(self)
94
96
 
@@ -98,9 +100,15 @@ class UC2Client(object):
98
100
  # initialize LED matrix
99
101
  self.led = LedMatrix(self, NLeds=NLeds)
100
102
 
103
+ # initialize LCD display
104
+ self.lcd = LCDDisplay(self)
105
+
101
106
  # initilize motor
102
107
  self.motor = Motor(self)
103
108
 
109
+ # initialize CAN
110
+ self.can = CAN(self)
111
+
104
112
  # initialize gripper
105
113
  self.gripper = Gripper(self)
106
114
 
@@ -6,7 +6,7 @@ __version__.py
6
6
 
7
7
  __title__ = 'UC2-REST'
8
8
  __description__ = 'This pacage will help you to drive the ESP32-driven microscopy control modules from UC2'
9
- __version__ = "v0.2.0.28"
9
+ __version__ = "v0.2.0.29"
10
10
  __author__ = 'Benedict Diederich'
11
11
  __author_email__ = 'benedictdied@gmail.com'
12
12
  __license__ = 'GPL v3'
@@ -0,0 +1,33 @@
1
+ class CAN(object):
2
+ def __init__(self, parent):
3
+ """
4
+ CANController handles sending commands to a remote CAN device via the parent post_json interface.
5
+
6
+ :param parent: Parent object with post_json(path, payload, getReturn, timeout, nResponses)
7
+ """
8
+ self._parent = parent
9
+
10
+ def reboot_remote(self, qid=1, can_address=0, isBlocking=False, timeout=2):
11
+ """
12
+ Send a reboot signal to the remote CAN device.
13
+
14
+ :param qid: Query ID for the CAN command (default: 1)
15
+ :param isBlocking: If True, wait for response
16
+ :param timeout: Timeout for the command in seconds
17
+ :param can_address: Address of the CAN device to reboot (0 is master)
18
+ :return: Response from the device
19
+ """
20
+ path = "/can_act"
21
+ payload = {
22
+ "task": path,
23
+ "restart": int(can_address)
24
+ }
25
+ nResponses = 1 if isBlocking else 0
26
+ # Send the payload to the parent, which handles the actual communication
27
+ return self._parent.post_json(
28
+ path,
29
+ payload,
30
+ getReturn=isBlocking,
31
+ timeout=timeout if isBlocking else 0,
32
+ nResponses=nResponses
33
+ )
@@ -0,0 +1,59 @@
1
+ class LCDDisplay:
2
+ def __init__(self, parent):
3
+ """
4
+ LCD controller interface.
5
+ Sends JSON commands to draw on the LCD via the parent object.
6
+ """
7
+ self._parent = parent
8
+ self.path = "/lcd_act"
9
+ self.timeout = 10
10
+ self.color = (255, 255, 255) # Default color
11
+
12
+ def _send(self, action, isBlocking=True, **kwargs):
13
+ payload = {
14
+ "task": self.path,
15
+ "action": action,
16
+ **kwargs
17
+ }
18
+ return self._parent.post_json(self.path, payload, getReturn=isBlocking, nResponses=1, timeout=self.timeout)
19
+
20
+ def set_grating(self, perdiod, r=255, g=255, b=255, horizontal=True):
21
+ # {"task":"/lcd_act", "action":"gratingh","period":3,"r":255,"g":255,"b":255}
22
+ if horizontal:
23
+ return self._send("gratingh", period=perdiod, r=r, g=g, b=b)
24
+ else:
25
+ return self._send("gratingv", period=perdiod, r=r, g=g, b=b)
26
+
27
+ def set_timeout(self, timeout=5):
28
+ """
29
+ Set the timeout for the LCD display.
30
+ :param timeout: Timeout in seconds.
31
+ """
32
+ self.timeout = timeout
33
+
34
+ def set_color(self, r=255, g=255, b=255):
35
+ self.color = (r, g, b)
36
+
37
+ def clear(self, r=0, g=0, b=0):
38
+ return self._send("clear", r=r, g=g, b=b)
39
+
40
+ def hline(self, x, y, length, width=1):
41
+ r, g, b = self.color
42
+ # {"task":"/lcd_act","action":"hline","x":0,"y":10,"len":800,"width":1,"r":255,"g":255,"b":0}
43
+ return self._send("hline", x=x, y=y, len=length, width=width, r=r, g=g, b=b)
44
+
45
+ def vline(self, x, y, length, width=1):
46
+ r, g, b = self.color
47
+ return self._send("vline", x=x, y=y, len=length, width=width, r=r, g=g, b=b)
48
+
49
+ def point(self, x, y, diam=1):
50
+ r, g, b = self.color
51
+ return self._send("point", x=x, y=y, diam=diam, r=r, g=g, b=b)
52
+
53
+ def rect(self, x, y, len, width):
54
+ r, g, b = self.color
55
+ return self._send("rect", x=x, y=y, len=len, width=width, r=r, g=g, b=b)
56
+
57
+ def fill(self, x, y, len, width):
58
+ r, g, b = self.color
59
+ return self._send("fill", x=x, y=y, len=len, width=width, r=r, g=g, b=b)
@@ -42,7 +42,7 @@ class Motor(object):
42
42
  self.offsetY = 0
43
43
  self.offsetZ = 0
44
44
 
45
- self.DEFAULT_ACCELERATION = 10000
45
+ self.DEFAULT_ACCELERATION = 1000000
46
46
 
47
47
  self.motorAxisOrder = [0,1,2,3] # motor axis is 1,2,3,0 => X,Y,Z,T # FIXME: Hardcoded
48
48
 
@@ -142,6 +142,38 @@ class Motor(object):
142
142
  def setMotorAxisOrder(self, order=[0,1,2,3]):
143
143
  self.motorAxisOrder = order
144
144
 
145
+ # { "task": "/motor_act", "focusscan": { "zStart": 0, "zStep": 50, "nZ": 20, "tPre": 80, "tTrig": 20, "tPost": 0, "led": 0, "illumination": [0, 255, 0, 0], "speed": 20000, "acceleration": 1000000, "qid": 42 }}
146
+ def startFocusScanning(self, zStart=0, zStep=50, nZ=20, tPre=80, tTrig=20, tPost=0, led=0, illumination=[0, 255, 0, 0], speed=20000, acceleration=1000000, qid=42):
147
+ path = "/motor_act"
148
+ payload = {
149
+ "task": path,
150
+ "focusscan": {
151
+ "zStart": zStart,
152
+ "zStep": zStep,
153
+ "nZ": nZ,
154
+ "tPre": tPre,
155
+ "tTrig": tTrig,
156
+ "tPost": tPost,
157
+ "led": led,
158
+ "illumination": illumination,
159
+ "speed": speed,
160
+ "acceleration": acceleration,
161
+ "qid": qid
162
+ }
163
+ }
164
+ r = self._parent.post_json(path, payload)
165
+ return r
166
+
167
+ def stopFocusScanning(self):
168
+ path = "/motor_act"
169
+ payload = {
170
+ "task": path,
171
+ "focusscan": {
172
+ "stopped": 1
173
+ }
174
+ }
175
+ r = self._parent.post_json(path, payload)
176
+ return r
145
177
  '''################################################################################################################################################
146
178
  HIGH-LEVEL Functions that rely on basic REST-API functions
147
179
  ################################################################################################################################################'''
@@ -423,7 +455,7 @@ class Motor(object):
423
455
  self.currentDirection[iMotor] = np.sign(steps[iMotor])
424
456
  if self.lastDirection[iMotor] != self.currentDirection[iMotor]:
425
457
  # we want to overshoot a bit
426
- steps[iMotor] = steps[iMotor] + self.currentDirection[iMotor]*self.backlash[iMotor]
458
+ steps[iMotor] = steps[iMotor] + self.currentDirection[iMotor]*self.backlash[iMotor]
427
459
 
428
460
 
429
461
  if isAbsoluteArray[iMotor]:
@@ -459,6 +491,10 @@ class Motor(object):
459
491
  motorPropList = []
460
492
  for iMotor in range(4):
461
493
  if isAbsoluteArray[iMotor] or abs(steps[iMotor])>0:
494
+ # if we are absolute and the last target position is the same as the current one, we don't need to move
495
+ if isAbsoluteArray[iMotor] and abs(steps[iMotor] - self.currentPosition[iMotor])<1:
496
+ if self._parent.serial.DEBUG: self._parent.logger.debug(f"Motor {iMotor} is already at target position {steps[iMotor]}")
497
+ continue
462
498
  motorProp = { "stepperid": int(self.motorAxisOrder[iMotor]),
463
499
  "position": int(steps[iMotor]),
464
500
  "speed": int(speed[iMotor]),
@@ -471,7 +507,8 @@ class Motor(object):
471
507
  else:
472
508
  motorProp["accel"] = self.DEFAULT_ACCELERATION
473
509
  motorPropList.append(motorProp)
474
-
510
+ if len(motorPropList)==0:
511
+ return "{'return':-1}"
475
512
  path = "/motor_act"
476
513
  payload = {
477
514
  "task":path,
@@ -698,7 +735,46 @@ class Motor(object):
698
735
 
699
736
  def set_direction(self, axis=1, sign=1, timeout=1):
700
737
  return False
738
+
739
+ def stop_stage_scanning(self):
740
+ # {"task":"/motor_act", "stagescan":{ "stopped":1 }}
741
+ path = "/motor_act"
742
+ payload = {
743
+ "task": path,
744
+ "stagescan": {
745
+ "stopped": 1
746
+ }
747
+ }
748
+ r = self._parent.post_json(path, payload)
749
+ return r
750
+
751
+
701
752
 
753
+ def start_stage_scanning(self, xstart=0, xstep=1000, nx=20, ystart=0, ystep=1000, ny=10, tsettle=5, tExposure=50, illumination=(0,0,0,0), led=0, speed=20000, acceleration=None):
754
+ # {"task": "/motor_act", "stagescan": {"xStart": 0, "yStart": 0, "xStep": 500, "yStep": 500, "nX": 10, "nY": 10, "tPre": 50, "tPost": 50, "illumination": [0, 1, 0, 0], "led": 255}}
755
+ if acceleration is None:
756
+ acceleration = self.DEFAULT_ACCELERATION
757
+ path = "/motor_act"
758
+ payload = {
759
+ "task": path,
760
+ "stagescan": {
761
+ "xStart": xstart / self.stepSizeX,
762
+ "xStep": xstep / self.stepSizeX,
763
+ "nX": nx,
764
+ "yStart": ystart / self.stepSizeY,
765
+ "yStep": ystep / self.stepSizeY,
766
+ "nY": ny,
767
+ "tPre": tsettle,
768
+ "tPost": tExposure,
769
+ "illumination": illumination,
770
+ "led": led,
771
+ "accel": self.DEFAULT_ACCELERATION, # default acceleration
772
+ "speed": speed, # default speed
773
+ }
774
+ }
775
+ r = self._parent.post_json(path, payload)
776
+ return r
777
+
702
778
  def set_tmc_parameters(self, axis=0, msteps=None, rms_current=None, stall_value=None, sgthrs=None, semin=None, semax=None, blank_time=None, toff=None, timeout=1):
703
779
  ''' set the TMC parameters for a specific axis
704
780
  msteps: microsteps
@@ -33,7 +33,7 @@ class Serial:
33
33
  self._logger.addHandler(logging.StreamHandler())
34
34
  else:
35
35
  self._logger = self._parent.logger
36
-
36
+ self.identifier_counter = 0
37
37
  self.identity = identity
38
38
  self.DEBUG = DEBUG
39
39
  self.is_connected = False
@@ -228,7 +228,8 @@ class Serial:
228
228
  try:
229
229
  mLine = serialdevice.readline()
230
230
  if self.cmdReadCallBackFct is not None and mLine!=b'' and mLine != b'\n' :
231
- self.cmdReadCallBackFct(mLine)
231
+ try:self.cmdReadCallBackFct(mLine)
232
+ except:pass
232
233
  return mLine
233
234
  except SerialException as e:
234
235
  self._logger.error("Failed to read the line in serial: "+str(e))
@@ -305,7 +306,7 @@ class Serial:
305
306
  def _process_commands(self):
306
307
  buffer = ""
307
308
  reading_json = False
308
- currentIdentifier = None
309
+ currentIdentifier = 0
309
310
  nLineCountTimeout = 50 # maximum number of lines read before timeout
310
311
  lineCounter = 0
311
312
  nFailedCommands = 0
@@ -339,19 +340,20 @@ class Serial:
339
340
  line = ""
340
341
 
341
342
  # if we have a problem with the serial connection, we need to reconnect
342
- for i in range(4):
343
- nFailedCommands=0
344
- if self.reconnect():
345
- self._logger.debug("Reconnected to the serial device")
346
- break
347
- else:
348
- self._logger.debug("Failed to reconnect to the serial device")
349
- time.sleep(1)
343
+ if nFailedCommands>5:
344
+ for i in range(4):
345
+ nFailedCommands=0
346
+ if self.reconnect():
347
+ self._logger.debug("Reconnected to the serial device")
348
+ break
349
+ else:
350
+ self._logger.debug("Failed to reconnect to the serial device")
351
+ time.sleep(1)
350
352
 
351
353
  if line == "++":
352
354
  reading_json = True
353
355
  continue
354
- elif line.find("error") != -1:
356
+ elif line.find("error") != -1 and currentIdentifier is not None:
355
357
  # if we have an error, we need to reset the last command
356
358
  self._logger.debug("Error - last command did not match the firmware: "+str(self.commands[currentIdentifier]))
357
359
  self.resetLastCommand = True
@@ -365,7 +367,7 @@ class Serial:
365
367
  reading_json = False
366
368
  try:
367
369
  json_response = json.loads(buffer)
368
- self._logger.debug("[ProcessCommands]: "+str(json_response))
370
+ if self.DEBUG: self._logger.debug("[ProcessCommands]: "+str(json_response))
369
371
  if len(self.callBackList) > 0:
370
372
  for callback in self.callBackList:
371
373
  # check if json has key
@@ -1,5 +1,6 @@
1
1
  import time
2
2
  import json
3
+ import numpy as np
3
4
 
4
5
  class Objective(object):
5
6
  def __init__(self, parent):
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes