UC2-REST 0.2.0.42__tar.gz → 0.2.0.44__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.
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/PKG-INFO +1 -1
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/UC2_REST.egg-info/PKG-INFO +1 -1
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/UC2_REST.egg-info/SOURCES.txt +2 -0
- uc2_rest-0.2.0.44/tests/test_motor_axes.py +114 -0
- uc2_rest-0.2.0.44/tests/test_motor_get_reply.py +92 -0
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/UC2Client.py +5 -2
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/__version__.py +1 -1
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/home.py +27 -99
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/motor.py +266 -332
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/mserial.py +20 -5
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/LICENSE +0 -0
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/README.md +0 -0
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/UC2_REST.egg-info/dependency_links.txt +0 -0
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/UC2_REST.egg-info/not-zip-safe +0 -0
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/UC2_REST.egg-info/requires.txt +0 -0
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/UC2_REST.egg-info/top_level.txt +0 -0
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/setup.cfg +0 -0
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/setup.py +0 -0
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/tests/test_canota_reconnect.py +0 -0
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/tests/test_stage_scan.py +0 -0
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/MockSerial.py +0 -0
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/__init__.py +0 -0
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/aio.py +0 -0
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/analog.py +0 -0
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/camera.py +0 -0
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/camera_trigger.py +0 -0
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/can.py +0 -0
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/canota.py +0 -0
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/cmdrecorder.py +0 -0
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/digitalin.py +0 -0
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/digitalout.py +0 -0
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/fan.py +0 -0
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/galvo.py +0 -0
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/gpio.py +0 -0
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/gripper.py +0 -0
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/i2c.py +0 -0
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/laser.py +0 -0
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/lcddisplay.py +0 -0
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/ledmatrix.py +0 -0
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/logger.py +0 -0
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/message.py +0 -0
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/modules.py +0 -0
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/motor_config.py +0 -0
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/objective.py +0 -0
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/pid.py +0 -0
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/ptz.py +0 -0
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/rotator.py +0 -0
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/slm.py +0 -0
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/state.py +0 -0
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/temperature.py +0 -0
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/utils.py +0 -0
- {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/wifi.py +0 -0
|
@@ -8,6 +8,8 @@ UC2_REST.egg-info/not-zip-safe
|
|
|
8
8
|
UC2_REST.egg-info/requires.txt
|
|
9
9
|
UC2_REST.egg-info/top_level.txt
|
|
10
10
|
tests/test_canota_reconnect.py
|
|
11
|
+
tests/test_motor_axes.py
|
|
12
|
+
tests/test_motor_get_reply.py
|
|
11
13
|
tests/test_stage_scan.py
|
|
12
14
|
uc2rest/MockSerial.py
|
|
13
15
|
uc2rest/UC2Client.py
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"""Generic motor axes (stepperid 0-9, named axes), without hardware.
|
|
2
|
+
|
|
3
|
+
Run: python -m unittest tests.test_motor_axes (from the repo root)
|
|
4
|
+
"""
|
|
5
|
+
import unittest
|
|
6
|
+
from unittest import mock
|
|
7
|
+
|
|
8
|
+
from uc2rest.home import Home
|
|
9
|
+
from uc2rest.motor import Motor
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class _Parent:
|
|
13
|
+
"""Just enough of UC2Client: a serial with register_callback, post_json and a motor."""
|
|
14
|
+
|
|
15
|
+
def __init__(self):
|
|
16
|
+
self.serial = mock.Mock()
|
|
17
|
+
self.logger = mock.Mock()
|
|
18
|
+
self.sent = []
|
|
19
|
+
self.reply = {"qid": 1, "return": 1}
|
|
20
|
+
|
|
21
|
+
def post_json(self, path, payload, **kwargs):
|
|
22
|
+
self.sent.append((path, payload))
|
|
23
|
+
return self.reply
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class MotorAxesTest(unittest.TestCase):
|
|
27
|
+
def setUp(self):
|
|
28
|
+
self.parent = _Parent()
|
|
29
|
+
self.motor = self.parent.motor = Motor(parent=self.parent)
|
|
30
|
+
|
|
31
|
+
def steppers(self):
|
|
32
|
+
return self.parent.sent[-1][1]["motor"]["steppers"]
|
|
33
|
+
|
|
34
|
+
def test_named_axis_beyond_xyza(self):
|
|
35
|
+
self.motor.setup_motor("C", minPos=0, maxPos=10, stepSize=0.5, backlash=0)
|
|
36
|
+
self.motor.move_axis_by_name("C", 10, speed=1000)
|
|
37
|
+
self.assertEqual([(s["stepperid"], s["position"]) for s in self.steppers()], [(5, 20)])
|
|
38
|
+
self.motor.move_axis_by_name(7, 3, speed=1000) # by stepperid
|
|
39
|
+
self.assertEqual(self.steppers()[0]["stepperid"], 7)
|
|
40
|
+
|
|
41
|
+
def test_move_axes_mixed_names(self):
|
|
42
|
+
self.motor.move_axes({"X": 100, "B": -50}, speed={"X": 2000, "B": 3000})
|
|
43
|
+
got = {s["stepperid"]: (s["position"], s["speed"]) for s in self.steppers()}
|
|
44
|
+
self.assertEqual(got, {1: (100, 2000), 4: (-50, 3000)})
|
|
45
|
+
|
|
46
|
+
def test_relative_zero_axes_are_not_sent(self):
|
|
47
|
+
self.motor.move_xyz((0, 5, 0), speed=1000)
|
|
48
|
+
self.assertEqual([s["stepperid"] for s in self.steppers()], [2])
|
|
49
|
+
|
|
50
|
+
def test_remapped_and_custom_names(self):
|
|
51
|
+
self.motor.setMotorAxisOrder([0, 2, 1, 3]) # legacy: X and Y swapped
|
|
52
|
+
self.motor.move_x(10, speed=1000)
|
|
53
|
+
self.assertEqual(self.steppers()[0]["stepperid"], 2)
|
|
54
|
+
self.motor.set_axis_ids({"T": 6})
|
|
55
|
+
self.motor.move_axis_by_name("t", 1, speed=1000)
|
|
56
|
+
self.assertEqual(self.steppers()[0]["stepperid"], 6)
|
|
57
|
+
with self.assertRaises(ValueError):
|
|
58
|
+
self.motor.axis_id("Q")
|
|
59
|
+
with self.assertRaises(ValueError):
|
|
60
|
+
self.motor.axis_id(10)
|
|
61
|
+
|
|
62
|
+
def test_feedback_reaches_every_callback(self):
|
|
63
|
+
seen = {}
|
|
64
|
+
self.motor.register_callback("stage1", lambda p: seen.setdefault("a", p[6]))
|
|
65
|
+
self.motor.register_callback("stage2", lambda p: seen.setdefault("b", p[6]))
|
|
66
|
+
self.motor.setup_motor("D", 0, 0, stepSize=2, backlash=0, direction=-1)
|
|
67
|
+
self.motor._callback_motor_status(
|
|
68
|
+
{"steppers": [{"stepperid": 6, "position": 10}, {"stepperid": 42, "position": 1}]})
|
|
69
|
+
self.assertEqual(seen, {"a": -20, "b": -20})
|
|
70
|
+
|
|
71
|
+
def test_stop_all_sends_only_known_ids(self):
|
|
72
|
+
self.motor.move_axis_by_name("E", 5, speed=1000)
|
|
73
|
+
self.motor.stop()
|
|
74
|
+
self.assertEqual([s["stepperid"] for s in self.steppers()], [0, 1, 2, 3, 7])
|
|
75
|
+
|
|
76
|
+
def test_move_forever_only_given_axes(self):
|
|
77
|
+
self.motor.move_forever({"C": 500})
|
|
78
|
+
self.assertEqual(self.steppers(), [{"stepperid": 5, "isforever": 1, "speed": 500}])
|
|
79
|
+
|
|
80
|
+
def test_get_position_filtered(self):
|
|
81
|
+
self.parent.reply = [{"steppers": [{"stepperid": 4, "position": 8}, {"stepperid": 5, "isDone": -1}]}]
|
|
82
|
+
self.motor.setup_motor("B", 0, 0, stepSize=0.25, backlash=0)
|
|
83
|
+
pos = self.motor.get_position(axis=["B", "C"])
|
|
84
|
+
self.assertEqual(self.parent.sent[-1][1]["motor"], {"steppers": [{"stepperid": 4}, {"stepperid": 5}]})
|
|
85
|
+
self.assertEqual((len(pos), pos[4], pos[5]), (10, 2, 0))
|
|
86
|
+
|
|
87
|
+
def test_legacy_step_size_attributes(self):
|
|
88
|
+
self.motor.stepSizeX = 2.0
|
|
89
|
+
self.assertEqual((self.motor.stepSize[1], self.motor.stepSizeX), (2.0, 2.0))
|
|
90
|
+
|
|
91
|
+
def test_backlash_only_on_reversal(self):
|
|
92
|
+
self.motor.set_backlash("X", 10)
|
|
93
|
+
self.motor.move_x(100, speed=1000) # first move counts as a reversal
|
|
94
|
+
self.assertEqual(self.steppers()[0]["position"], 110)
|
|
95
|
+
self.motor.move_y(5, speed=1000) # another axis must not reset X's direction
|
|
96
|
+
self.motor.move_x(100, speed=1000)
|
|
97
|
+
self.assertEqual(self.steppers()[0]["position"], 100)
|
|
98
|
+
self.motor.move_x(-100, speed=1000)
|
|
99
|
+
self.assertEqual(self.steppers()[0]["position"], -110)
|
|
100
|
+
self.motor.move_x(0, speed=1000, is_absolute=True) # still moving down
|
|
101
|
+
self.assertEqual(self.steppers()[0]["position"], 0)
|
|
102
|
+
self.motor.move_x(50, speed=1000, is_absolute=True) # up again: overshoot upwards
|
|
103
|
+
self.assertEqual(self.steppers()[0]["position"], 60)
|
|
104
|
+
|
|
105
|
+
def test_home_named_axis(self):
|
|
106
|
+
home = Home(self.parent)
|
|
107
|
+
home.home(axis="C", speed=1000)
|
|
108
|
+
self.assertEqual(self.parent.sent[-1][1]["home"]["steppers"][0]["stepperid"], 5)
|
|
109
|
+
home.home_x()
|
|
110
|
+
self.assertEqual(self.parent.sent[-1][1]["home"]["steppers"][0]["stepperid"], 1)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
if __name__ == "__main__":
|
|
114
|
+
unittest.main()
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""/motor_get answers vs. position pushes that share their qid (no hardware).
|
|
2
|
+
|
|
3
|
+
The firmware tags a motor's position push ({"steppers":[{.., "isDone": 1}]}) with the qid of
|
|
4
|
+
that motor's last move; uc2rest's qids wrap after 254 commands, so such a frame can carry the
|
|
5
|
+
qid of a later /motor_get. Taken as the answer, every other axis read 0 (ImSwitch FRAME
|
|
6
|
+
calibration, 2026-11-12: "X reads 728.2 after a move to 19394.5" = device X 0).
|
|
7
|
+
|
|
8
|
+
Run: python -m unittest tests.test_motor_get_reply (from the repo root)
|
|
9
|
+
"""
|
|
10
|
+
import threading
|
|
11
|
+
import time
|
|
12
|
+
import unittest
|
|
13
|
+
from unittest import mock
|
|
14
|
+
|
|
15
|
+
import numpy as np
|
|
16
|
+
|
|
17
|
+
from uc2rest.motor import Motor
|
|
18
|
+
from uc2rest.mserial import Serial
|
|
19
|
+
|
|
20
|
+
PUSH = {"steppers": [{"stepperid": 0, "position": -29000, "isDone": 1}], "qid": 7}
|
|
21
|
+
REPLY = {"motor": {"steppers": [{"stepperid": i, "position": p} for i, p in enumerate((-29000, 19000, 60000, 5900))]},
|
|
22
|
+
"qid": 7}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class _Parent:
|
|
26
|
+
def __init__(self, frames):
|
|
27
|
+
self.serial = mock.Mock()
|
|
28
|
+
self.logger = mock.Mock()
|
|
29
|
+
self.frames = frames
|
|
30
|
+
|
|
31
|
+
def post_json(self, path, payload, accept=None, **kwargs):
|
|
32
|
+
# like Serial.sendMessage: with accept, the matching frame first
|
|
33
|
+
if accept is not None:
|
|
34
|
+
hit = next((f for f in self.frames if accept(f)), None)
|
|
35
|
+
return "communication interrupted by timeout" if hit is None else [hit] + [f for f in self.frames if f is not hit]
|
|
36
|
+
return list(self.frames)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class MotorGetReplyTest(unittest.TestCase):
|
|
40
|
+
def motor(self, frames):
|
|
41
|
+
parent = _Parent(frames)
|
|
42
|
+
m = parent.motor = Motor(parent=parent)
|
|
43
|
+
for ax in "AXYZ":
|
|
44
|
+
m.setup_motor(ax, minPos=-1e9, maxPos=1e9, stepSize=1, backlash=0)
|
|
45
|
+
return m
|
|
46
|
+
|
|
47
|
+
def test_push_with_our_qid_is_not_the_answer(self):
|
|
48
|
+
p = self.motor([PUSH, REPLY]).get_position()
|
|
49
|
+
self.assertEqual(list(p[:4]), [-29000, 19000, 60000, 5900])
|
|
50
|
+
|
|
51
|
+
def test_no_answer_reports_missing(self):
|
|
52
|
+
m = self.motor([PUSH])
|
|
53
|
+
self.assertTrue(np.isnan(m.get_position(missing=np.nan)[:4]).all())
|
|
54
|
+
self.assertEqual(list(m.get_position()[:4]), [0, 0, 0, 0]) # default: as before
|
|
55
|
+
self.assertEqual(m._read_firmware_positions(), {})
|
|
56
|
+
|
|
57
|
+
def test_filtered_can_reply_is_an_answer(self):
|
|
58
|
+
can = {"steppers": [{"stepperid": 4, "position": 3}, {"stepperid": 5, "isDone": -1}], "qid": 2}
|
|
59
|
+
self.assertTrue(Motor._is_motor_get_reply(can, ids=[4, 5]))
|
|
60
|
+
self.assertFalse(Motor._is_motor_get_reply(can)) # a bare request wants {"motor": ...}
|
|
61
|
+
self.assertFalse(Motor._is_motor_get_reply(PUSH, ids=[0, 1]))
|
|
62
|
+
self.assertFalse(Motor._is_motor_get_reply(PUSH))
|
|
63
|
+
self.assertFalse(Motor._is_motor_get_reply("communication interrupted"))
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class SendMessageAcceptTest(unittest.TestCase):
|
|
67
|
+
"""Serial.sendMessage waits past frames that share the qid but fail accept."""
|
|
68
|
+
|
|
69
|
+
def test_waits_for_the_accepted_frame(self):
|
|
70
|
+
s = Serial.__new__(Serial)
|
|
71
|
+
s.lock, s.responses, s.commands = threading.Lock(), {}, {}
|
|
72
|
+
s.identifier_counter, s.running, s.is_connected = 0, True, True
|
|
73
|
+
s.resetLastCommand, s.use_qid_done, s.DEBUG, s.manufacturer = False, False, False, "test"
|
|
74
|
+
s.timeReturnReceived, s._logger, s.cmdWriteCallBackFct = 5, mock.Mock(), None
|
|
75
|
+
|
|
76
|
+
def write(dev, payload):
|
|
77
|
+
qid = s.identifier_counter
|
|
78
|
+
def arrive():
|
|
79
|
+
for f in ({**PUSH, "qid": qid}, {**REPLY, "qid": qid}):
|
|
80
|
+
time.sleep(0.05)
|
|
81
|
+
with s.lock:
|
|
82
|
+
s.responses.setdefault(qid, []).append(f)
|
|
83
|
+
threading.Thread(target=arrive, daemon=True).start()
|
|
84
|
+
s._write, s.serialdevice = write, None
|
|
85
|
+
r = s.sendMessage({"task": "/motor_get", "position": True}, nResponses=1, timeout=2,
|
|
86
|
+
accept=Motor._is_motor_get_reply)
|
|
87
|
+
self.assertIn("motor", r[0])
|
|
88
|
+
self.assertEqual(len(r), 2)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
if __name__ == "__main__":
|
|
92
|
+
unittest.main()
|
|
@@ -177,10 +177,13 @@ class UC2Client(object):
|
|
|
177
177
|
# initialize module controller
|
|
178
178
|
self.modules = Modules(parent=self)
|
|
179
179
|
|
|
180
|
-
def post_json(self, path, payload, getReturn=True, nResponses=1, timeout=1):
|
|
180
|
+
def post_json(self, path, payload, getReturn=True, nResponses=1, timeout=1, accept=None):
|
|
181
181
|
if timeout <=0:
|
|
182
182
|
getReturn = False
|
|
183
|
-
|
|
183
|
+
if accept is None: # serial implementations without the accept argument keep working
|
|
184
|
+
return self.serial.post_json(path, payload, getReturn=getReturn, nResponses=nResponses, timeout=timeout)
|
|
185
|
+
return self.serial.post_json(path, payload, getReturn=getReturn, nResponses=nResponses, timeout=timeout,
|
|
186
|
+
accept=accept)
|
|
184
187
|
|
|
185
188
|
def get_json(self, path, getReturn=True, timeout=1):
|
|
186
189
|
if self.is_serial or self.isPyScript:
|
|
@@ -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.
|
|
9
|
+
__version__ = "v0.2.0.44"
|
|
10
10
|
__author__ = 'Benedict Diederich'
|
|
11
11
|
__author_email__ = 'benedictdied@gmail.com'
|
|
12
12
|
__license__ = 'GPL v3'
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import time
|
|
2
2
|
import json
|
|
3
3
|
import numpy as np
|
|
4
|
+
from .motor import AXIS_NAMES
|
|
4
5
|
|
|
5
6
|
class Home(object):
|
|
6
7
|
## Laser
|
|
@@ -12,8 +13,8 @@ class Home(object):
|
|
|
12
13
|
self.timeout = 20000
|
|
13
14
|
self.endstoppolarity = 1
|
|
14
15
|
|
|
15
|
-
self.nMotors =
|
|
16
|
-
self.isHomed = np.zeros((self.nMotors))
|
|
16
|
+
self.nMotors = len(AXIS_NAMES)
|
|
17
|
+
self.isHomed = np.zeros((self.nMotors)) # indexed by stepperid
|
|
17
18
|
|
|
18
19
|
|
|
19
20
|
# register a callback function for the motor status on the serial loop
|
|
@@ -43,15 +44,10 @@ class Home(object):
|
|
|
43
44
|
into the position array of the motors '''
|
|
44
45
|
try:
|
|
45
46
|
steppers = data["home"]["steppers"]
|
|
46
|
-
#
|
|
47
|
-
if isinstance(steppers, dict):
|
|
48
|
-
|
|
49
|
-
stepperID
|
|
50
|
-
self.isHomed[stepperID] = steppers["isDone"]
|
|
51
|
-
elif isinstance(steppers, list):
|
|
52
|
-
# Multiple steppers format: [{"axis":1,"pos":0,"isDone":1}, ...]
|
|
53
|
-
for stepper in steppers:
|
|
54
|
-
stepperID = stepper["axis"]
|
|
47
|
+
# Single stepper {"axis":1,"pos":0,"isDone":1} or a list of them
|
|
48
|
+
for stepper in ([steppers] if isinstance(steppers, dict) else steppers):
|
|
49
|
+
stepperID = stepper["axis"]
|
|
50
|
+
if 0 <= stepperID < self.nMotors:
|
|
55
51
|
self.isHomed[stepperID] = stepper["isDone"]
|
|
56
52
|
|
|
57
53
|
if callable(self._callbackPerKey[0]):
|
|
@@ -65,49 +61,21 @@ class Home(object):
|
|
|
65
61
|
|
|
66
62
|
|
|
67
63
|
def home_x(self, speed = None, direction = None, endposrelease = None, endstoppolarity=None, timeout=None, isBlocking=False):
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
self.home(axis=axis,
|
|
71
|
-
endstoptimeout=timeout,
|
|
72
|
-
speed = speed,
|
|
73
|
-
direction = direction,
|
|
74
|
-
endposrelease=endposrelease,
|
|
75
|
-
endstoppolarity=endstoppolarity,
|
|
76
|
-
isBlocking=isBlocking)
|
|
64
|
+
return self.home(axis="X", endstoptimeout=timeout, speed=speed, direction=direction,
|
|
65
|
+
endposrelease=endposrelease, endstoppolarity=endstoppolarity, isBlocking=isBlocking)
|
|
77
66
|
|
|
78
67
|
def home_y(self, speed = None, direction = None, endposrelease = None, endstoppolarity=None, timeout=None, isBlocking=False):
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
endstoptimeout=timeout,
|
|
83
|
-
speed = speed,
|
|
84
|
-
direction = direction,
|
|
85
|
-
endposrelease=endposrelease,
|
|
86
|
-
endstoppolarity=endstoppolarity,
|
|
87
|
-
isBlocking=isBlocking)
|
|
88
|
-
|
|
68
|
+
return self.home(axis="Y", endstoptimeout=timeout, speed=speed, direction=direction,
|
|
69
|
+
endposrelease=endposrelease, endstoppolarity=endstoppolarity, isBlocking=isBlocking)
|
|
70
|
+
|
|
89
71
|
def home_z(self, speed = None, direction = None, endposrelease = None, endstoppolarity=None, timeout=None, isBlocking=False, hardhome=False):
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
speed = speed,
|
|
95
|
-
direction = direction,
|
|
96
|
-
endposrelease=endposrelease,
|
|
97
|
-
endstoppolarity=endstoppolarity,
|
|
98
|
-
isBlocking=isBlocking,
|
|
99
|
-
hardhome=hardhome)
|
|
100
|
-
|
|
72
|
+
return self.home(axis="Z", endstoptimeout=timeout, speed=speed, direction=direction,
|
|
73
|
+
endposrelease=endposrelease, endstoppolarity=endstoppolarity, isBlocking=isBlocking,
|
|
74
|
+
hardhome=hardhome)
|
|
75
|
+
|
|
101
76
|
def home_a(self, speed = None, direction = None, endposrelease = None, endstoppolarity=None, timeout=None, isBlocking=False):
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
self.home(axis=axis,
|
|
105
|
-
endstoptimeout=timeout,
|
|
106
|
-
speed = speed,
|
|
107
|
-
direction = direction,
|
|
108
|
-
endposrelease=endposrelease,
|
|
109
|
-
endstoppolarity=endstoppolarity,
|
|
110
|
-
isBlocking=isBlocking)
|
|
77
|
+
return self.home(axis="A", endstoptimeout=timeout, speed=speed, direction=direction,
|
|
78
|
+
endposrelease=endposrelease, endstoppolarity=endstoppolarity, isBlocking=isBlocking)
|
|
111
79
|
|
|
112
80
|
def home_xy(self, axes=["X", "Y"], speeds=None, directions=None, endposreleases=None,
|
|
113
81
|
endstoppolarities=None, timeouts=None, isBlocking=False, preMove=True):
|
|
@@ -133,18 +101,8 @@ class Home(object):
|
|
|
133
101
|
timeouts=[400, 200]
|
|
134
102
|
)
|
|
135
103
|
"""
|
|
136
|
-
#
|
|
137
|
-
|
|
138
|
-
stepper_ids = []
|
|
139
|
-
motorAxisOrder = self._parent.motor.motorAxisOrder if hasattr(self._parent.motor , 'motorAxisOrder') else [0, 1, 2, 3]
|
|
140
|
-
for axis in axes:
|
|
141
|
-
if isinstance(axis, str):
|
|
142
|
-
logical_motor_index = self.xyztTo1230(axis)
|
|
143
|
-
else:
|
|
144
|
-
logical_motor_index = axis
|
|
145
|
-
# Map logical motor index to physical stepper ID
|
|
146
|
-
stepper_id = motorAxisOrder[logical_motor_index]
|
|
147
|
-
stepper_ids.append(stepper_id)
|
|
104
|
+
# axis names or stepperids -> stepperids
|
|
105
|
+
stepper_ids = [self.xyztTo1230(axis) for axis in axes]
|
|
148
106
|
|
|
149
107
|
num_motors = len(stepper_ids)
|
|
150
108
|
|
|
@@ -180,18 +138,8 @@ class Home(object):
|
|
|
180
138
|
preMoveDistanceSteps = 100 # steps to move away from endstop
|
|
181
139
|
|
|
182
140
|
# Move away from endstop using the motor controller
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
speed=speeds[i], is_blocking=True, is_absolute=False, is_enabled=True)
|
|
186
|
-
elif stepper_id == 2: # Y
|
|
187
|
-
self._parent.motor.move_y(steps=preMoveDirection*preMoveDistanceSteps,
|
|
188
|
-
speed=speeds[i], is_blocking=True, is_absolute=False, is_enabled=True)
|
|
189
|
-
elif stepper_id == 3: # Z
|
|
190
|
-
self._parent.motor.move_z(steps=preMoveDirection*preMoveDistanceSteps,
|
|
191
|
-
speed=speeds[i], is_blocking=True, is_absolute=False, is_enabled=True)
|
|
192
|
-
elif stepper_id == 0: # A
|
|
193
|
-
self._parent.motor.move_a(steps=preMoveDirection*preMoveDistanceSteps,
|
|
194
|
-
speed=speeds[i], is_blocking=True, is_absolute=False, is_enabled=True)
|
|
141
|
+
self._parent.motor.move_axis_by_name(axis=stepper_id, steps=preMoveDirection*preMoveDistanceSteps,
|
|
142
|
+
speed=speeds[i], is_blocking=True, is_absolute=False, is_enabled=True)
|
|
195
143
|
time.sleep(0.5)
|
|
196
144
|
|
|
197
145
|
# Build the steppers list for the JSON payload
|
|
@@ -227,22 +175,12 @@ class Home(object):
|
|
|
227
175
|
return r
|
|
228
176
|
|
|
229
177
|
def xyztTo1230(self, axis):
|
|
230
|
-
"""
|
|
231
|
-
|
|
232
|
-
axis = axis.upper()
|
|
233
|
-
if axis == "X":
|
|
234
|
-
return 1
|
|
235
|
-
elif axis == "Y":
|
|
236
|
-
return 2
|
|
237
|
-
elif axis == "Z":
|
|
238
|
-
return 3
|
|
239
|
-
elif axis == "A":
|
|
240
|
-
return 0
|
|
241
|
-
return axis
|
|
178
|
+
"""Axis name ("X", "B", ...) or stepperid -> stepperid, see Motor.axis_id."""
|
|
179
|
+
return self._parent.motor.axis_id(axis)
|
|
242
180
|
|
|
243
181
|
def home(self, axis=None, timeout=None, speed=None, direction=None, endposrelease=None, endstoppolarity=None, endstoptimeout=10000, isBlocking=False, preMove=False, hardhome=False):
|
|
244
182
|
'''
|
|
245
|
-
axis = 0,1,2,3 or
|
|
183
|
+
axis = stepperid (A=0, X=1, Y=2, Z=3, B..G=4..9) or axis name ('X', 'B', ...)
|
|
246
184
|
timeout => when to stop homing (it's a while loop on the MCU)
|
|
247
185
|
speed => speed of homing (0...15000)
|
|
248
186
|
direction => 1,-1 (left/right)
|
|
@@ -275,20 +213,10 @@ class Home(object):
|
|
|
275
213
|
preMoveDistanceSteps = 100 # steps to move away from endstop
|
|
276
214
|
|
|
277
215
|
# move away from endstop
|
|
278
|
-
|
|
279
|
-
self._parent.motor.move_x(steps=preMoveDirection*preMoveDistanceSteps, speed=self.speed, is_blocking=True, is_absolute=False, is_enabled=True)
|
|
280
|
-
elif axis == 2 or axis == "Y":
|
|
281
|
-
self._parent.motor.move_y(steps=preMoveDirection*preMoveDistanceSteps, speed=self.speed, is_blocking=True, is_absolute=False, is_enabled=True)
|
|
282
|
-
elif axis == 3 or axis == "Z":
|
|
283
|
-
self._parent.motor.move_z(steps=preMoveDirection*preMoveDistanceSteps, speed=self.speed, is_blocking=True, is_absolute=False, is_enabled=True)
|
|
284
|
-
elif axis == 0 or axis == "A":
|
|
285
|
-
self._parent.motor.move_a(steps=preMoveDirection*preMoveDistanceSteps, speed=self.speed, is_blocking=True, is_absolute=False, is_enabled=True)
|
|
286
|
-
else:
|
|
287
|
-
raise ValueError("Invalid axis. Use 'X', 'Y', 'Z', or 'A'.")
|
|
216
|
+
self._parent.motor.move_axis_by_name(axis=axis, steps=preMoveDirection*preMoveDistanceSteps, speed=self.speed, is_blocking=True, is_absolute=False, is_enabled=True)
|
|
288
217
|
time.sleep(0.5)
|
|
289
218
|
|
|
290
|
-
|
|
291
|
-
stepper_id = self._parent.motor.motorAxisOrder[axis]
|
|
219
|
+
stepper_id = self.xyztTo1230(axis)
|
|
292
220
|
|
|
293
221
|
# construct json string
|
|
294
222
|
path = "/home_act"
|