UC2-REST 0.2.0.17__tar.gz → 0.2.0.19__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 (38) hide show
  1. {UC2-REST-0.2.0.17 → uc2_rest-0.2.0.19}/PKG-INFO +1 -1
  2. {UC2-REST-0.2.0.17 → uc2_rest-0.2.0.19}/UC2_REST.egg-info/PKG-INFO +1 -1
  3. {UC2-REST-0.2.0.17 → uc2_rest-0.2.0.19}/UC2_REST.egg-info/SOURCES.txt +1 -0
  4. {UC2-REST-0.2.0.17 → uc2_rest-0.2.0.19}/uc2rest/UC2Client.py +6 -2
  5. {UC2-REST-0.2.0.17 → uc2_rest-0.2.0.19}/uc2rest/__version__.py +1 -1
  6. {UC2-REST-0.2.0.17 → uc2_rest-0.2.0.19}/uc2rest/home.py +2 -2
  7. {UC2-REST-0.2.0.17 → uc2_rest-0.2.0.19}/uc2rest/laser.py +2 -32
  8. uc2_rest-0.2.0.19/uc2rest/message.py +85 -0
  9. {UC2-REST-0.2.0.17 → uc2_rest-0.2.0.19}/uc2rest/motor.py +51 -17
  10. {UC2-REST-0.2.0.17 → uc2_rest-0.2.0.19}/uc2rest/mserial.py +91 -33
  11. uc2_rest-0.2.0.19/uc2rest/temperature.py +93 -0
  12. UC2-REST-0.2.0.17/uc2rest/temperature.py +0 -56
  13. {UC2-REST-0.2.0.17 → uc2_rest-0.2.0.19}/LICENSE +0 -0
  14. {UC2-REST-0.2.0.17 → uc2_rest-0.2.0.19}/README.md +0 -0
  15. {UC2-REST-0.2.0.17 → uc2_rest-0.2.0.19}/UC2_REST.egg-info/dependency_links.txt +0 -0
  16. {UC2-REST-0.2.0.17 → uc2_rest-0.2.0.19}/UC2_REST.egg-info/not-zip-safe +0 -0
  17. {UC2-REST-0.2.0.17 → uc2_rest-0.2.0.19}/UC2_REST.egg-info/requires.txt +0 -0
  18. {UC2-REST-0.2.0.17 → uc2_rest-0.2.0.19}/UC2_REST.egg-info/top_level.txt +0 -0
  19. {UC2-REST-0.2.0.17 → uc2_rest-0.2.0.19}/setup.cfg +0 -0
  20. {UC2-REST-0.2.0.17 → uc2_rest-0.2.0.19}/setup.py +0 -0
  21. {UC2-REST-0.2.0.17 → uc2_rest-0.2.0.19}/uc2rest/__init__.py +0 -0
  22. {UC2-REST-0.2.0.17 → uc2_rest-0.2.0.19}/uc2rest/analog.py +0 -0
  23. {UC2-REST-0.2.0.17 → uc2_rest-0.2.0.19}/uc2rest/camera.py +0 -0
  24. {UC2-REST-0.2.0.17 → uc2_rest-0.2.0.19}/uc2rest/cmdrecorder.py +0 -0
  25. {UC2-REST-0.2.0.17 → uc2_rest-0.2.0.19}/uc2rest/config.py +0 -0
  26. {UC2-REST-0.2.0.17 → uc2_rest-0.2.0.19}/uc2rest/config_.py +0 -0
  27. {UC2-REST-0.2.0.17 → uc2_rest-0.2.0.19}/uc2rest/digitalout.py +0 -0
  28. {UC2-REST-0.2.0.17 → uc2_rest-0.2.0.19}/uc2rest/galvo.py +0 -0
  29. {UC2-REST-0.2.0.17 → uc2_rest-0.2.0.19}/uc2rest/ledmatrix.py +0 -0
  30. {UC2-REST-0.2.0.17 → uc2_rest-0.2.0.19}/uc2rest/logger.py +0 -0
  31. {UC2-REST-0.2.0.17 → uc2_rest-0.2.0.19}/uc2rest/modules.py +0 -0
  32. {UC2-REST-0.2.0.17 → uc2_rest-0.2.0.19}/uc2rest/pid.py +0 -0
  33. {UC2-REST-0.2.0.17 → uc2_rest-0.2.0.19}/uc2rest/rotator.py +0 -0
  34. {UC2-REST-0.2.0.17 → uc2_rest-0.2.0.19}/uc2rest/slm.py +0 -0
  35. {UC2-REST-0.2.0.17 → uc2_rest-0.2.0.19}/uc2rest/state.py +0 -0
  36. {UC2-REST-0.2.0.17 → uc2_rest-0.2.0.19}/uc2rest/updater.py +0 -0
  37. {UC2-REST-0.2.0.17 → uc2_rest-0.2.0.19}/uc2rest/utils.py +0 -0
  38. {UC2-REST-0.2.0.17 → uc2_rest-0.2.0.19}/uc2rest/wifi.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: UC2-REST
3
- Version: 0.2.0.17
3
+ Version: 0.2.0.19
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.1
2
2
  Name: UC2-REST
3
- Version: 0.2.0.17
3
+ Version: 0.2.0.19
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
@@ -21,6 +21,7 @@ uc2rest/home.py
21
21
  uc2rest/laser.py
22
22
  uc2rest/ledmatrix.py
23
23
  uc2rest/logger.py
24
+ uc2rest/message.py
24
25
  uc2rest/modules.py
25
26
  uc2rest/motor.py
26
27
  uc2rest/mserial.py
@@ -23,7 +23,7 @@ from .rotator import Rotator
23
23
  from .logger import Logger
24
24
  from .cmdrecorder import cmdRecorder
25
25
  from .temperature import Temperature
26
-
26
+ from .message import Message
27
27
  try:
28
28
  import requests
29
29
  except:
@@ -141,6 +141,9 @@ class UC2Client(object):
141
141
  # initialize digital out
142
142
  self.digitalout = DigitalOut(self)
143
143
 
144
+ # initialize messaging
145
+ self.message = Message(self)
146
+
144
147
  # initialize config
145
148
  if False: # not self.isPyScript:
146
149
  self.config = config(self)
@@ -160,6 +163,7 @@ class UC2Client(object):
160
163
  # FIXME: this is not working
161
164
  url = f"http://{self.host}:{self.port}{path}"
162
165
  try:
166
+ if timeout==0: timeout=.2
163
167
  r = requests.post(url, json=payload, headers=self.headers, timeout=timeout)
164
168
  returnMessage = r.json()
165
169
  returnMessage["success"] = r.status_code==200
@@ -172,7 +176,7 @@ class UC2Client(object):
172
176
  elif self.is_serial or self.isPyScript:
173
177
  if timeout <=0:
174
178
  getReturn = False
175
- return self.serial.post_json(path, payload, getReturn=getReturn, nResponses=nResponses)
179
+ return self.serial.post_json(path, payload, getReturn=getReturn, nResponses=nResponses, timeout=timeout)
176
180
  else:
177
181
  self.logger.error("No ESP32 device is connected - check IP or Serial port!")
178
182
  return None
@@ -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.17"
9
+ __version__ = "v0.2.0.19"
10
10
  __author__ = 'Benedict Diederich'
11
11
  __author_email__ = 'benedictdied@gmail.com'
12
12
  __license__ = 'GPL v3'
@@ -78,9 +78,9 @@ class Home(object):
78
78
  {
79
79
  "stepperid": axis,
80
80
  "timeout":timeout*1000,
81
- "speed":speed,
81
+ "speed":direction*abs(speed),
82
82
  "direction":direction,
83
- "endposrelease":endposrelease,
83
+ "endposrelease":endposrelease,
84
84
  "endstoppolarity":endstoppolarity
85
85
  }]
86
86
  }}
@@ -11,7 +11,7 @@ class Laser(object):
11
11
  def set_laser(self, channel=1, value=0, auto_filterswitch=False,
12
12
  filter_axis=-1, filter_position = None,
13
13
  despeckleAmplitude = 0.,
14
- despecklePeriod=10, timeout=20, is_blocking = True):
14
+ despecklePeriod=10, timeout=20, is_blocking = False):
15
15
  if channel not in (0,1,2,3):
16
16
  if channel=="R":
17
17
  channel = 1
@@ -46,7 +46,7 @@ class Laser(object):
46
46
 
47
47
  }
48
48
  #self._parent.logger.debug("Setting Laser "+str(channel)+", value: "+str(value))
49
- r = self._parent.post_json(path, payload, getReturn=is_blocking)
49
+ r = self._parent.post_json(path, payload, getReturn=is_blocking, timeout=.5)
50
50
  return r
51
51
 
52
52
  def set_laserpin(self, laserid=1, laserpin=0):
@@ -61,33 +61,3 @@ class Laser(object):
61
61
  r = self._parent.post_json(path, payload)
62
62
  return r
63
63
 
64
- def get_laserpins(self):
65
- path = '/laser_get'
66
-
67
- r = self._parent.get_json(path)
68
-
69
- if type(r) is dict:
70
- # cast laser pins
71
- if "LASER1pin" in r: r["LASER1pin"] = int(r["LASER1pin"])
72
- else: r["LASER1pin"] = 0
73
- if r.__contains__("LASER2pin"): r["LASER2pin"] = int(r["LASER2pin"])
74
- else: r["LASER2pin"] = 0
75
- if r.__contains__("LASER3pin"): r["LASER3pin"] = int(r["LASER3pin"])
76
- else: r["LASER3pin"] = 0
77
- else:
78
- r={}
79
- r["LASER1pin"] = 0
80
- r["LASER2pin"] = 0
81
- r["LASER3pin"] = 0
82
-
83
- return r
84
-
85
- def get_laserpin(self, laserid=1):
86
- path = '/laser_get'
87
-
88
- payload = {
89
- "task": path,
90
- }
91
-
92
- r = self._parent.post_json(path, payload,timeout=2)
93
- return r["LASER"+str(laserid)+"pin"]
@@ -0,0 +1,85 @@
1
+ import numpy as np
2
+ import time
3
+ import json
4
+
5
+
6
+ gTIMEOUT = 1 # seconds to wait for a response from the ESP32
7
+ class Message(object):
8
+ '''
9
+ This class only parses incoming messages from the ESP32 that can be used e.g. for triggering events such as taking an image
10
+ or converting hardware inputs such as button presses to software events
11
+ '''
12
+ def __init__(self, parent=None):
13
+ self._parent = parent
14
+
15
+ # create a dictionary of callback functions that can be triggered by incoming messages
16
+ self._callbackPerKey = {}
17
+ self.NCallBacks = 10
18
+ for i in range(self.NCallBacks):
19
+ self._callbackPerKey[i] = None
20
+
21
+ # register a callback function for the motor status on the serial loop
22
+ if hasattr(self._parent, "serial"):
23
+ self._parent.serial.register_callback(self._callback_message, pattern="message")
24
+
25
+ def _callback_message(self, data):
26
+ ''' cast the json in the form:
27
+ {
28
+ "message": {
29
+ "key": 1,
30
+ "data": 1
31
+ }
32
+ }
33
+ into seperated actions that follow this pattern:
34
+ data lookup:
35
+ {
36
+ key | meaning | value
37
+ --------------------------------
38
+ 1 | snap image | 0
39
+ 2 | exposure time | 0....1000000
40
+ 3 | gain | 0...100
41
+ ...
42
+
43
+ Example:
44
+ #%%
45
+ import uc2rest
46
+ import time
47
+
48
+ port = "unknown"
49
+ port = "/dev/cu.SLAB_USBtoUART"
50
+ ESP32 = uc2rest.UC2Client(serialport=port, baudrate=500000, DEBUG=True)
51
+
52
+ # register callback function for key/value pair
53
+ def my_callback_key1(value):
54
+ print("Callback: ", value)
55
+ ESP32.message.register_callback(1, my_callback_key1)
56
+
57
+ while True:
58
+ time.sleep(.1)
59
+ */
60
+
61
+ '''
62
+ try:
63
+ # parse the message
64
+ key = data["message"]["key"]
65
+ value = data["message"]["data"]
66
+ # trigger the action
67
+ if self._callbackPerKey[key] is not None:
68
+ self._callbackPerKey[key](value) # we call the function with the value
69
+ except Exception as e:
70
+ print("Error in _callback_message: ", e)
71
+
72
+ def register_callback(self, key, callback):
73
+ ''' register a callback function for a specific key '''
74
+ self._callbackPerKey[key] = callback
75
+
76
+ def trigger_message(self, key:int=1, value:int=1):
77
+ # {"task": "/message_act", "message": 1, "key":1, "value":1}
78
+ path = "/heat_act"
79
+ payload = {
80
+ "task": path,
81
+ "key": key,
82
+ "value": value
83
+ }
84
+ r = self._parent.post_json(path, payload, getReturn=False)
85
+ return r
@@ -9,16 +9,6 @@ class Motor(object):
9
9
  # indicate if there is any motion happening
10
10
  isRunning = False
11
11
 
12
- # a dictionary that stores all motor parameters for each dxis
13
- settingsdict = {"motor": {"steppers":
14
- [{"stepperid":0,"dir":0,"step":0,"enable":0,"dir_inverted":False,"step_inverted":False,"enable_inverted":False,"speed":0,"speedmax":200000,"max_pos":100000,"min_pos":-100000},
15
- {"stepperid":1,"dir":0,"step":0,"enable":0,"dir_inverted":False,"step_inverted":False,"enable_inverted":False,"speed":0,"speedmax":200000,"max_pos":100000,"min_pos":-100000},
16
- {"stepperid":2,"dir":0,"step":0,"enable":0,"dir_inverted":False,"step_inverted":False,"enable_inverted":False,"speed":0,"speedmax":200000,"max_pos":100000,"min_pos":-100000},
17
- {"stepperid":3,"dir":0,"step":0,"enable":0,"dir_inverted":False,"step_inverted":False,"enable_inverted":False,"speed":0,"speedmax":200000,"max_pos":100000,"min_pos":-100000}]
18
- }}
19
-
20
-
21
-
22
12
  def __init__(self, parent=None):
23
13
  self._parent = parent
24
14
 
@@ -48,14 +38,17 @@ class Motor(object):
48
38
  self.stepSizeZ = 1
49
39
  self.stepSizeA = 1
50
40
 
41
+ self.DEFAULT_ACCELERATION = 10000
42
+
51
43
  self.motorAxisOrder = [0,1,2,3] # motor axis is 1,2,3,0 => X,Y,Z,T # FIXME: Hardcoded
52
44
 
53
45
  # register a callback function for the motor status on the serial loop
54
- self._parent.serial.register_callback(self._callback_motor_status, pattern="steppers")
46
+ if hasattr(self._parent, "serial"):
47
+ self._parent.serial.register_callback(self._callback_motor_status, pattern="steppers")
55
48
 
56
49
  # move motor to wake them up #FIXME: Should not be necessary!
57
- self.move_stepper(steps=(1,1,1,1), speed=(1000,1000,1000,1000), is_absolute=(False,False,False,False))
58
- self.move_stepper(steps=(-1,-1,-1,-1), speed=(1000,1000,1000,1000), is_absolute=(False,False,False,False))
50
+ #self.move_stepper(steps=(1,1,1,1), speed=(1000,1000,1000,1000), is_absolute=(False,False,False,False))
51
+ #self.move_stepper(steps=(-1,-1,-1,-1), speed=(1000,1000,1000,1000), is_absolute=(False,False,False,False))
59
52
 
60
53
  def _callback_motor_status(self, data):
61
54
  ''' cast the json in the form:
@@ -77,7 +70,46 @@ class Motor(object):
77
70
  except Exception as e:
78
71
  print("Error in _callback_motor_status: ", e)
79
72
 
73
+ def setTrigger(self, axis="X", pin=1, offset=0, period=1):
74
+ # {"task": "/motor_act", "setTrig": {"steppers": [{"stepperid": 1, "trigPin": 1, "trigOff":0, "trigPer":1}]}}
75
+ if type(axis) is not int:
76
+ axis = self.xyztTo1230(axis)
77
+ path = "/motor_act"
78
+ payload = {
79
+ "task": path,
80
+ "setTrig":{
81
+ "steppers": [
82
+ {
83
+ "stepperid": axis,
84
+ "trigPin": pin,
85
+ "trigOff": offset,
86
+ "trigPer": period
87
+ }]
88
+ }}
89
+ r = self._parent.post_json(path, payload)
90
+ return r
91
+
92
+ # {"task": "/motor_act", "stagescan": {"nStepsLine": 50, "dStepsLine": 1, "nTriggerLine": 1, "nStepsPixel": 50, "dStepsPixel": 1, "nTriggerPixel": 1, "delayTimeStep": 10, "stopped": 0, "nFrames": 50}}"}}
93
+ def startStageScanning(self, nStepsLine=100, dStepsLine=1, nTriggerLine=1, nStepsPixel=100, dStepsPixel=1, nTriggerPixel=1, delayTimeStep=10, nFrames=5, isBlocking = False):
94
+ path = "/motor_act"
95
+ payload = {
96
+ "task": path,
97
+ "stagescan":{
98
+ "nStepsLine": nStepsLine,
99
+ "dStepsLine": dStepsLine,
100
+ "nTriggerLine": nTriggerLine,
101
+ "nStepsPixel": nStepsPixel,
102
+ "dStepsPixel": dStepsPixel,
103
+ "nTriggerPixel": nTriggerPixel,
104
+ "delayTimeStep": delayTimeStep,
105
+ "stopped": 0,
106
+ "nFrames": nFrames
107
+ }}
108
+ r = self._parent.post_json(path, payload, getReturn=isBlocking)
109
+ return r
80
110
 
111
+ def stopStageScanning(self):
112
+ self.startStageScanning(stopped=1)
81
113
 
82
114
  def setIsCoreXY(self, isCoreXY = False):
83
115
  self.isCoreXY = isCoreXY
@@ -164,7 +196,7 @@ class Motor(object):
164
196
  # have to move only one motor to move in XY direction
165
197
  return self.move_xyza(steps=(0,steps[0], steps[1], 0), speed=(0,speed[0],speed[1],0), is_blocking=is_blocking, is_absolute=is_absolute, is_enabled=is_enabled, timeout=timeout)
166
198
  else:
167
- if len(speed)!= 2:
199
+ if type(speed)==int or len(speed)!= 2:
168
200
  speed = (speed,speed)
169
201
 
170
202
  if len(acceleration)!= 2:
@@ -339,10 +371,12 @@ class Motor(object):
339
371
  "position": int(steps[iMotor]),
340
372
  "speed": int(speed[iMotor]),
341
373
  "isabs": isAbsoluteArray[iMotor],
342
- "isaccel":0,
374
+ "isaccel":1,
343
375
  "isen": is_enabled}
344
376
  if acceleration[iMotor] is not None:
345
377
  motorProp["accel"] = int(acceleration[iMotor])
378
+ else:
379
+ motorProp["accel"] = self.DEFAULT_ACCELERATION
346
380
  motorPropList.append(motorProp)
347
381
 
348
382
  path = "/motor_act"
@@ -478,7 +512,7 @@ class Motor(object):
478
512
  r = self._parent.post_json(path, payload)
479
513
  return r
480
514
 
481
- def get_position(self, axis=None, timeout=1):
515
+ def get_position(self, axis=None, timeout=.2):
482
516
  # pulls all current positions from the stepper controller
483
517
  path = "/motor_get"
484
518
  payload = {
@@ -489,7 +523,7 @@ class Motor(object):
489
523
  _physicalStepSizes = np.array((self.stepSizeA, self.stepSizeX, self.stepSizeY, self.stepSizeZ))
490
524
 
491
525
  # this may be an asynchronous call.. #FIXME!
492
- r = self._parent.post_json(path, payload, getReturn = True, nResponses=1)
526
+ r = self._parent.post_json(path, payload, getReturn = True, nResponses=1, timeout=timeout)
493
527
  if "motor" in r:
494
528
  for index, istepper in enumerate(r["motor"]["steppers"]):
495
529
  _position[istepper["stepperid"]]=istepper["position"]*_physicalStepSizes[self.motorAxisOrder[index]]
@@ -9,12 +9,12 @@ class Serial:
9
9
  def __init__(self, port, baudrate=115200, timeout=5,
10
10
  identity="UC2_Feather", parent=None, DEBUG=False):
11
11
 
12
- self.ser = None
12
+ self.serialdevice = None
13
13
  self.serialport = port
14
14
  self.baudrate = baudrate
15
15
  self.timeout = timeout
16
16
  self._parent = parent
17
-
17
+ self.manufacturer = ""
18
18
  if self._parent is None:
19
19
  import logging
20
20
  self._logger = logging.getLogger(__name__)
@@ -26,23 +26,24 @@ class Serial:
26
26
  self.identity = identity
27
27
  self.DEBUG = DEBUG
28
28
  self.is_connected = False
29
- self.write_timeout = 0.02
29
+ self.write_timeout = 0.05
30
30
  self.read_timeout = 0.02
31
31
 
32
32
  self.cmdCallBackFct = None
33
33
 
34
34
  # setup command queue
35
35
  self.resetLastCommand = False
36
- self.command_queue = queue.Queue()
36
+ self.sender_queue = queue.Queue()
37
37
  self.responses = {}
38
38
  self.commands = {}
39
39
  self.lock = threading.Lock()
40
-
40
+
41
+ # setup callback list for parent modules
41
42
  self.callBackList = []
42
43
 
43
44
  # initialize serial connection
44
45
  self.thread = None
45
- self.ser = self.openDevice(port, baudrate)
46
+ self.serialdevice= self.openDevice(port, baudrate)
46
47
 
47
48
  def breakCurrentCommunication(self):
48
49
  self.resetLastCommand = True
@@ -66,7 +67,7 @@ class Serial:
66
67
  def openDevice(self, port=None, baud_rate=115200):
67
68
  try: # try to close an eventually open serial connection
68
69
  if str(type(self.ser)) != "<class 'uc2rest.mserial.MockSerial'>":
69
- self.ser.close()
70
+ self.serialdevice.close()
70
71
  except: pass
71
72
 
72
73
  try:
@@ -101,9 +102,20 @@ class Serial:
101
102
  self.identifier_counter = 0 # Counter for generating unique identifiers
102
103
  self.thread = threading.Thread(target=self._process_commands)
103
104
  self.thread.start()
105
+
106
+ # setup sender queue
107
+ self.sender_worker_thread = threading.Thread(target=self._sending_commands)
108
+ self.sender_worker_thread.daemon = True # Ensure the thread exits when the main program does
109
+ self.sender_worker_thread.start()
104
110
  return ser
105
111
 
106
112
  def findCorrectSerialDevice(self):
113
+ '''
114
+ This function tries to find the correct serial device from the list of available ports
115
+ It also checks if the firmware is correct by sending a command and checking for the correct response
116
+ It may be that - depending on the OS - the response may be corrupted
117
+ If this is the case try to hard-code the COM port into the config JSON file
118
+ '''
107
119
  _available_ports = serial.tools.list_ports.comports(include_links=False)
108
120
  ports_to_check = ["COM", "/dev/tt", "/dev/a", "/dev/cu.SLA", "/dev/cu.wchusb"]
109
121
  descriptions_to_check = ["CH340", "CP2102"]
@@ -113,12 +125,14 @@ class Serial:
113
125
  any(port.description.startswith(allowed_description) for allowed_description in descriptions_to_check):
114
126
  if self.tryToConnect(port.device):
115
127
  self.is_connected = True
128
+ self.manufacturer = port.manufacturer
116
129
  return self.serialdevice
117
130
 
118
131
  self.is_connected = False
119
132
  self.serialport = "NotConnected"
120
133
  self.serialdevice = None
121
134
  self._logger.debug("No USB device connected! Using DUMMY!")
135
+ self.manufacturer = "UC2Mock"
122
136
  return None
123
137
 
124
138
  def tryToConnect(self, port):
@@ -148,7 +162,7 @@ class Serial:
148
162
  ser.write(json.dumps(payload).encode('utf-8'))
149
163
  ser.write(b'\n')
150
164
  # iterate a few times in case the debug mode on the ESP32 is turned on and it sends additional lines
151
- for i in range(100):
165
+ for i in range(500):
152
166
  # if we just want to send but not even wait for a response
153
167
  mReadline = ser.readline()
154
168
  if self.DEBUG: self._logger.debug("[checkFirmware]: "+str(mReadline))
@@ -162,6 +176,29 @@ class Serial:
162
176
  self.identifier_counter += 1
163
177
  return self.identifier_counter
164
178
 
179
+ def _enqueue_command(self, json_command):
180
+ """Add a command to the queue."""
181
+ self.sender_queue.put(json_command)
182
+
183
+ def _sending_commands(self):
184
+ """Sending commands from the queue with a 1s delay between them."""
185
+ while self.running:
186
+
187
+ # Wait for the next command
188
+ json_command = self.sender_queue.get()
189
+
190
+ # Process the command
191
+ try:
192
+ self.serialdevice.write(json_command.encode('utf-8'))
193
+ except Exception as e:
194
+ self._logger.error("Failed to write the line in serial: "+str(e))
195
+
196
+ # Signal that the command has been processed
197
+ self.sender_queue.task_done()
198
+
199
+ # Wait for .05 second before processing the next command
200
+ time.sleep(0.05)
201
+
165
202
  def _process_commands(self):
166
203
  buffer = ""
167
204
  reading_json = False
@@ -170,24 +207,23 @@ class Serial:
170
207
  lineCounter = 0
171
208
  while self.running:
172
209
 
173
- # Check if the last command went through successfully
174
- #if not self.command_queue.empty() and not reading_json:
175
- # currentIdentifier, command = self.command_queue.get()
176
-
210
+ if self.manufacturer == "UC2Mock":
211
+ self.running = False
212
+ return
213
+
177
214
  # device not ready yet
178
- if self.ser is None:
215
+ if self.serialdevice is None:
179
216
  self.is_connected = False
180
217
  continue
181
218
  else:
182
219
  self.is_connected = True
183
220
 
184
221
  # if we just want to send but not even wait for a response
222
+
185
223
  try:
186
- mReadline = self.ser.readline()
224
+ mReadline = self.serialdevice.readline()
187
225
  except Exception as e:
188
226
  self._logger.error("Failed to read the line in serial: "+str(e))
189
- self.is_connected = False
190
- break
191
227
  try:
192
228
  line = mReadline.decode('utf-8').strip()
193
229
  if self.DEBUG and line!="": self._logger.debug("[ProcessLines]:"+str(line))
@@ -212,18 +248,29 @@ class Serial:
212
248
 
213
249
  except Exception as e:
214
250
  self._logger.error("Failed to load the json from serial %s" % buffer)
215
- self._logger.error("Error: %s", e)
251
+ self._logger.error("Error: %s" % str(e))
216
252
  json_response = {}
217
253
 
254
+ try: currentIdentifier = json_response["qid"]
255
+ except: pass
256
+
218
257
  with self.lock:
219
- try: currentIdentifier = json_response["qid"]
220
- except: pass
221
258
  try:
222
259
  self.responses[currentIdentifier].append(json_response.copy())
223
260
  except:
224
261
  self.responses[currentIdentifier] = list()
225
262
  self.responses[currentIdentifier].append(json_response.copy())
226
- buffer = "" # reset buffer
263
+ buffer = "" # reset buffer
264
+
265
+ # detect a reboot of the device and return the current QIDs
266
+ elif line == "reboot":
267
+ self._logger.warning("Device rebooted")
268
+ self.resetLastCommand = True
269
+ buffer = ""
270
+ lineCounter = 0
271
+ reading_json = False
272
+ self.responses[currentIdentifier].append({"reboot": 1})
273
+ self.responses[currentIdentifier].append({"qid": currentIdentifier})
227
274
 
228
275
  if reading_json:
229
276
  buffer += line
@@ -235,7 +282,7 @@ class Serial:
235
282
  message = json.dumps(message)
236
283
  return self.sendMessage(message, nResponses=0, timeout=timeout)
237
284
 
238
- def post_json(self, path, payload, getReturn=True, nResponses=1, timeout=1):
285
+ def post_json(self, path, payload, getReturn=True, nResponses=1, timeout=100):
239
286
  """Make an HTTP POST request and return the JSON response"""
240
287
  if payload is None:
241
288
  payload = {}
@@ -281,26 +328,25 @@ class Serial:
281
328
  If nResponses is 1, then the command is sent and the response is returned.
282
329
  If nResponses is >1, then the command is sent and a list of responses is returned.
283
330
  '''
284
- t0 = time.time()
285
331
  if type(command) == str:
286
332
  command = json.loads(command)
287
333
  identifier = self._generate_identifier()
288
334
  command["qid"] = identifier
289
- self.command_queue.put((identifier, command))
290
335
  try:
291
336
  json_command = json.dumps(command)+"\n"
292
- with self.lock:
293
- t0 = time.time()
294
- self.ser.flush()
295
- self._logger.debug("[SendMessage]: "+str(json_command))
296
- self.ser.write(json_command.encode('utf-8') )
297
- self._logger.debug("[SendMessage] took: "+str(time.time()-t0))
337
+ self.serialdevice.flush()
338
+ if self.DEBUG: self._logger.debug("[SendMessage]: "+str(json_command))
339
+ # we have to queue the commands and give it some time to process
340
+ self._enqueue_command(json_command)
341
+
298
342
  except Exception as e:
299
343
  if self.DEBUG: self._logger.error(e)
300
344
  return "Failed to Send"
301
- self.commands[identifier]=command
302
- if nResponses <= 0 or not self.is_connected or not type(self.ser.BAUDRATES) is tuple:
345
+ self.commands[identifier]=command # FIXME: Need to clear this after the response is received
346
+ if nResponses <= 0 or not self.is_connected or self.manufacturer=="UC2Mock":
303
347
  return identifier
348
+ t0 = time.time()
349
+ timeReturnReceived = 0.3
304
350
  while self.running:
305
351
  time.sleep(0.002)
306
352
  if self.resetLastCommand or time.time()-t0>timeout or not self.is_connected:
@@ -313,6 +359,15 @@ class Serial:
313
359
  if -identifier in self.responses:
314
360
  self._logger.debug("You have sent the wrong command!")
315
361
  return "Wrong Command"
362
+ if time.time()-t0>timeReturnReceived and not (identifier in self.responses and len(self.responses[identifier]) > 0):
363
+ self._logger.debug("It takes too long to get a response, we will resend the last command")
364
+ try:
365
+ self.DEBUG=1
366
+ self.serialdevice.write(json.dumps(self.commands[identifier]).encode('utf-8'))
367
+ time.sleep(0.1)
368
+ except Exception as e:
369
+ self._logger.error("Failed to write the line in serial: "+str(e))
370
+
316
371
 
317
372
  def interruptCurrentSerialCommunication(self):
318
373
  self.resetLastCommand = True
@@ -320,7 +375,7 @@ class Serial:
320
375
  def stop(self):
321
376
  self.running = False
322
377
  self.thread.join()
323
- self.ser.close()
378
+ self.serialdevice.close()
324
379
 
325
380
  def closeSerial(self):
326
381
  self.stop()
@@ -329,7 +384,7 @@ class Serial:
329
384
  def reconnect(self):
330
385
  self.running = False
331
386
  try:
332
- self.ser.close()
387
+ self.serialdevice.close()
333
388
  except:
334
389
  pass
335
390
  self.serialdevice = self.openDevice(port = self.serialport, baud_rate = self.baudrate)
@@ -388,6 +443,9 @@ class MockSerial:
388
443
  self.manufacturer = "UC2Mock"
389
444
  self.BAUDRATES = -1
390
445
 
446
+ def flush(self):
447
+ pass
448
+
391
449
  def isOpen(self):
392
450
  return self.is_open
393
451
 
@@ -0,0 +1,93 @@
1
+ import time
2
+ import threading
3
+ class Temperature(object):
4
+
5
+ def __init__(self, parent):
6
+ self._parent = parent
7
+ self._temperature = -273.15
8
+
9
+ self.is_temperature_polling = False
10
+
11
+ # register a callback function for the motor status on the serial loop
12
+ if hasattr(self._parent, "serial"):
13
+ self._parent.serial.register_callback(self._callback_temperature_status, pattern="heat")
14
+
15
+
16
+ '''
17
+ ##############################################################################################################################
18
+ Temperature Controllers
19
+ ##############################################################################################################################
20
+ '''
21
+
22
+ def start_temperature_polling(self, period=5):
23
+ self.is_temperature_polling = True
24
+ def _poll_temperature():
25
+ while self.is_temperature_polling:
26
+ path = "/heat_get"
27
+ r = self._parent.get_json(path, timeout=0)
28
+ time.sleep(period)
29
+ threading.Thread(target=_poll_temperature).start()
30
+
31
+ def stop_temperature_polling(self):
32
+ self.is_temperature_polling = False
33
+
34
+
35
+ def _callback_temperature_status(self, data):
36
+ ''' cast the json in the form:
37
+ {
38
+ "qid": 0,
39
+ "heat": 37.0
40
+ }
41
+ into the position array of the motors '''
42
+ try:
43
+ self._temperature = data["heat"]
44
+ except Exception as e:
45
+ print("Error in _callback_temperature_status: ", e)
46
+
47
+
48
+ def stop_heating(self):
49
+ r = self.set_temperature(active=0)
50
+ return r
51
+
52
+ def set_temperature(self, active=1, Kp=1000, Ki=0.1, Kd=0.1, target=37, timeout=600000000, updaterate=1000):
53
+ # {"task": "/heat_act", "active":1, "Kp":1000, "Ki":0.1, "Kd":0.1, "target":37, "timeout":60000000, "updaterate":1000}
54
+ path = "/heat_act"
55
+ payload = {
56
+ "task": path,
57
+ "active": int(active),
58
+ "Kp": Kp,
59
+ "Ki": Ki,
60
+ "Kd": Kd,
61
+ "target": target,
62
+ "timeout": timeout,
63
+ "updaterate": updaterate
64
+ }
65
+ r = self._parent.post_json(path, payload, getReturn=False)
66
+ return r
67
+
68
+ def get_temperature(self, timeout=0.5, isBlocking=False):
69
+ # {"task": "/heat_get"}
70
+ if isBlocking:
71
+ path = "/heat_get"
72
+ r = self._parent.get_json(path, timeout=timeout)
73
+ try:
74
+ r = r["heat"]
75
+ except:
76
+ r = -273.15
77
+ return r
78
+ else:
79
+ return self._temperature
80
+
81
+ if __name__ == "__main__":
82
+
83
+ #%%
84
+ import uc2rest
85
+ import time
86
+ port = "/dev/cu.SLAB_USBtoUART"
87
+ ESP32 = uc2rest.UC2Client(serialport=port, DEBUG=True)
88
+ ESP32.temperature.set_temperature(active=1, Kp=1000, Ki=0.1, Kd=0.1, target=37, timeout=600000, updaterate=1000)
89
+ for i in range(10):
90
+ print(ESP32.temperature.get_temperature())
91
+ time.sleep(1)
92
+ ESP32.temperature.stop_heating()
93
+ ESP32.close()
@@ -1,56 +0,0 @@
1
- class Temperature(object):
2
-
3
- def __init__(self, parent):
4
- self._parent = parent
5
-
6
- '''
7
- ##############################################################################################################################
8
- Temperature Controllers
9
- ##############################################################################################################################
10
- '''
11
-
12
- def stop_heating(self):
13
- r = self.set_temperature(active=0)
14
- return r
15
-
16
- def set_temperature(self, active=1, Kp=1000, Ki=0.1, Kd=0.1, target=37, timeout=600000, updaterate=1000):
17
- # {"task": "/heat_act", "active":1, "Kp":1000, "Ki":0.1, "Kd":0.1, "target":37, "timeout":600000, "updaterate":1000}
18
- path = "/heat_act"
19
- payload = {
20
- "task": path,
21
- "active": int(active),
22
- "Kp": Kp,
23
- "Ki": Ki,
24
- "Kd": Kd,
25
- "target": target,
26
- "timeout": timeout,
27
- "updaterate": updaterate
28
- }
29
- r = self._parent.post_json(path, payload, getReturn=False)
30
- return r
31
-
32
- def get_temperature(self, timeout=0.5):
33
- path = "/heat_get"
34
- r = self._parent.get_json(path, timeout=timeout)
35
- try:
36
- r = r["heat"]
37
- except:
38
- r = 9999
39
- return r
40
-
41
-
42
- if __name__ == "__main__":
43
-
44
- #%%
45
- import uc2rest
46
- import time
47
- port = "/dev/cu.SLAB_USBtoUART"
48
- ESP32 = uc2rest.UC2Client(serialport=port, DEBUG=True)
49
- ESP32.temperature.set_temperature(active=1, Kp=1000, Ki=0.1, Kd=0.1, target=37, timeout=600000, updaterate=1000)
50
- for i in range(10):
51
- print(ESP32.temperature.get_temperature())
52
- time.sleep(1)
53
- ESP32.temperature.stop_heating()
54
- ESP32.close()
55
-
56
-
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes