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.
Files changed (52) hide show
  1. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/PKG-INFO +1 -1
  2. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/UC2_REST.egg-info/PKG-INFO +1 -1
  3. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/UC2_REST.egg-info/SOURCES.txt +2 -0
  4. uc2_rest-0.2.0.44/tests/test_motor_axes.py +114 -0
  5. uc2_rest-0.2.0.44/tests/test_motor_get_reply.py +92 -0
  6. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/UC2Client.py +5 -2
  7. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/__version__.py +1 -1
  8. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/home.py +27 -99
  9. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/motor.py +266 -332
  10. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/mserial.py +20 -5
  11. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/LICENSE +0 -0
  12. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/README.md +0 -0
  13. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/UC2_REST.egg-info/dependency_links.txt +0 -0
  14. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/UC2_REST.egg-info/not-zip-safe +0 -0
  15. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/UC2_REST.egg-info/requires.txt +0 -0
  16. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/UC2_REST.egg-info/top_level.txt +0 -0
  17. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/setup.cfg +0 -0
  18. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/setup.py +0 -0
  19. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/tests/test_canota_reconnect.py +0 -0
  20. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/tests/test_stage_scan.py +0 -0
  21. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/MockSerial.py +0 -0
  22. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/__init__.py +0 -0
  23. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/aio.py +0 -0
  24. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/analog.py +0 -0
  25. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/camera.py +0 -0
  26. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/camera_trigger.py +0 -0
  27. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/can.py +0 -0
  28. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/canota.py +0 -0
  29. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/cmdrecorder.py +0 -0
  30. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/digitalin.py +0 -0
  31. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/digitalout.py +0 -0
  32. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/fan.py +0 -0
  33. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/galvo.py +0 -0
  34. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/gpio.py +0 -0
  35. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/gripper.py +0 -0
  36. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/i2c.py +0 -0
  37. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/laser.py +0 -0
  38. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/lcddisplay.py +0 -0
  39. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/ledmatrix.py +0 -0
  40. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/logger.py +0 -0
  41. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/message.py +0 -0
  42. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/modules.py +0 -0
  43. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/motor_config.py +0 -0
  44. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/objective.py +0 -0
  45. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/pid.py +0 -0
  46. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/ptz.py +0 -0
  47. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/rotator.py +0 -0
  48. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/slm.py +0 -0
  49. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/state.py +0 -0
  50. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/temperature.py +0 -0
  51. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/uc2rest/utils.py +0 -0
  52. {uc2_rest-0.2.0.42 → uc2_rest-0.2.0.44}/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.42
3
+ Version: 0.2.0.44
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.42
3
+ Version: 0.2.0.44
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
@@ -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
- return self.serial.post_json(path, payload, getReturn=getReturn, nResponses=nResponses, timeout=timeout)
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.42"
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 = 4
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
- # Handle both single stepper and multiple steppers
47
- if isinstance(steppers, dict):
48
- # Single stepper format: {"axis":1,"pos":0,"isDone":1}
49
- stepperID = steppers["axis"]
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
- # axis = 1 corresponds to 'X'
69
- axis = 1
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
- # axis = 2 corresponds to 'Y'
80
- axis = 2
81
- self.home(axis=axis,
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
- # axisa = 3 corresponds to 'Z'
91
- axis = 3
92
- self.home(axis=axis,
93
- endstoptimeout=timeout,
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
- # axis = 0 corresponds to 'A'
103
- axis = 0
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
- # Convert axes to stepper IDs
137
- # First get logical motor index, then map through motorAxisOrder to get physical stepper ID
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
- if stepper_id == 1: # X
184
- self._parent.motor.move_x(steps=preMoveDirection*preMoveDistanceSteps,
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
- """Convert axis string to stepper ID number."""
231
- if isinstance(axis, str):
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 'A, 'X','Y','Z'
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
- if axis == 1 or axis == "X":
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
- # Map logical motor index to physical stepper ID through motorAxisOrder
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"