pydobotlab 0.1.0__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 (79) hide show
  1. pydobotlab-0.1.0/CONTRIBUTING.md +46 -0
  2. pydobotlab-0.1.0/LICENSE +21 -0
  3. pydobotlab-0.1.0/MANIFEST.in +14 -0
  4. pydobotlab-0.1.0/PKG-INFO +143 -0
  5. pydobotlab-0.1.0/README.md +105 -0
  6. pydobotlab-0.1.0/docs/README.md +54 -0
  7. pydobotlab-0.1.0/docs/SUMMARY.md +45 -0
  8. pydobotlab-0.1.0/docs/api/alarms.md +125 -0
  9. pydobotlab-0.1.0/docs/api/connection.md +91 -0
  10. pydobotlab-0.1.0/docs/api/discovery.md +159 -0
  11. pydobotlab-0.1.0/docs/api/end-effectors.md +84 -0
  12. pydobotlab-0.1.0/docs/api/errors.md +126 -0
  13. pydobotlab-0.1.0/docs/api/extras.md +101 -0
  14. pydobotlab-0.1.0/docs/api/io.md +128 -0
  15. pydobotlab-0.1.0/docs/api/jog.md +90 -0
  16. pydobotlab-0.1.0/docs/api/motion.md +207 -0
  17. pydobotlab-0.1.0/docs/api/overview.md +62 -0
  18. pydobotlab-0.1.0/docs/api/pose-stream.md +68 -0
  19. pydobotlab-0.1.0/docs/api/queue.md +128 -0
  20. pydobotlab-0.1.0/docs/api/speed.md +123 -0
  21. pydobotlab-0.1.0/docs/getting-started/connect.md +91 -0
  22. pydobotlab-0.1.0/docs/getting-started/first-script.md +72 -0
  23. pydobotlab-0.1.0/docs/getting-started/install.md +65 -0
  24. pydobotlab-0.1.0/docs/getting-started/linux.md +219 -0
  25. pydobotlab-0.1.0/docs/getting-started/simulator.md +68 -0
  26. pydobotlab-0.1.0/docs/panel/broker.md +75 -0
  27. pydobotlab-0.1.0/docs/panel/overview.md +52 -0
  28. pydobotlab-0.1.0/docs/panel/running.md +88 -0
  29. pydobotlab-0.1.0/docs/protocol/alarms.md +62 -0
  30. pydobotlab-0.1.0/docs/protocol/command-ids.md +186 -0
  31. pydobotlab-0.1.0/docs/protocol/ctrl-byte.md +62 -0
  32. pydobotlab-0.1.0/docs/protocol/frame-format.md +96 -0
  33. pydobotlab-0.1.0/docs/protocol/method-to-cmd.md +74 -0
  34. pydobotlab-0.1.0/docs/publishing.md +106 -0
  35. pydobotlab-0.1.0/examples/draw_smiley.py +434 -0
  36. pydobotlab-0.1.0/examples/multi_dobot.py +73 -0
  37. pydobotlab-0.1.0/examples/pick_and_place.py +59 -0
  38. pydobotlab-0.1.0/examples/smoke_test.py +114 -0
  39. pydobotlab-0.1.0/examples/speed_test.py +78 -0
  40. pydobotlab-0.1.0/examples/two_dobots_with_panels.py +95 -0
  41. pydobotlab-0.1.0/examples/unreachable_target.py +66 -0
  42. pydobotlab-0.1.0/mkdocs.yml +88 -0
  43. pydobotlab-0.1.0/pydobotlab/__init__.py +70 -0
  44. pydobotlab-0.1.0/pydobotlab/alarms.py +240 -0
  45. pydobotlab-0.1.0/pydobotlab/broker.py +355 -0
  46. pydobotlab-0.1.0/pydobotlab/broker_protocol.py +35 -0
  47. pydobotlab-0.1.0/pydobotlab/commands.py +126 -0
  48. pydobotlab-0.1.0/pydobotlab/device.py +1021 -0
  49. pydobotlab-0.1.0/pydobotlab/discovery.py +299 -0
  50. pydobotlab-0.1.0/pydobotlab/errors.py +57 -0
  51. pydobotlab-0.1.0/pydobotlab/panel/__init__.py +9 -0
  52. pydobotlab-0.1.0/pydobotlab/panel/__main__.py +10 -0
  53. pydobotlab-0.1.0/pydobotlab/panel/app.py +120 -0
  54. pydobotlab-0.1.0/pydobotlab/panel/hub.py +204 -0
  55. pydobotlab-0.1.0/pydobotlab/panel/panel.py +648 -0
  56. pydobotlab-0.1.0/pydobotlab/panel/style.qss +224 -0
  57. pydobotlab-0.1.0/pydobotlab/panel/widgets.py +206 -0
  58. pydobotlab-0.1.0/pydobotlab/protocol.py +248 -0
  59. pydobotlab-0.1.0/pydobotlab/queue.py +63 -0
  60. pydobotlab-0.1.0/pydobotlab/simulator.py +650 -0
  61. pydobotlab-0.1.0/pydobotlab/transport.py +303 -0
  62. pydobotlab-0.1.0/pydobotlab.egg-info/PKG-INFO +143 -0
  63. pydobotlab-0.1.0/pydobotlab.egg-info/SOURCES.txt +77 -0
  64. pydobotlab-0.1.0/pydobotlab.egg-info/dependency_links.txt +1 -0
  65. pydobotlab-0.1.0/pydobotlab.egg-info/entry_points.txt +2 -0
  66. pydobotlab-0.1.0/pydobotlab.egg-info/requires.txt +14 -0
  67. pydobotlab-0.1.0/pydobotlab.egg-info/top_level.txt +1 -0
  68. pydobotlab-0.1.0/pyproject.toml +81 -0
  69. pydobotlab-0.1.0/scripts/check_installed_package.py +25 -0
  70. pydobotlab-0.1.0/setup.cfg +4 -0
  71. pydobotlab-0.1.0/tests/__init__.py +0 -0
  72. pydobotlab-0.1.0/tests/test_alignment.py +182 -0
  73. pydobotlab-0.1.0/tests/test_broker.py +157 -0
  74. pydobotlab-0.1.0/tests/test_panel_imports.py +51 -0
  75. pydobotlab-0.1.0/tests/test_panel_widgets.py +82 -0
  76. pydobotlab-0.1.0/tests/test_protocol.py +188 -0
  77. pydobotlab-0.1.0/tests/test_registry.py +195 -0
  78. pydobotlab-0.1.0/tests/test_simulator.py +174 -0
  79. pydobotlab-0.1.0/tests/test_transport_regressions.py +136 -0
@@ -0,0 +1,46 @@
1
+ # Contributing
2
+
3
+ Use Python 3.10 or newer and a virtual environment:
4
+
5
+ ```bash
6
+ python -m venv .venv
7
+ source .venv/bin/activate # Windows: .venv\Scripts\Activate.ps1
8
+ python -m pip install -e ".[dev,gui]"
9
+ python -m pytest --timeout=30
10
+ ruff check .
11
+ ruff format --check .
12
+ ```
13
+
14
+ Tests use simulated devices; they do not move connected hardware. The GUI
15
+ can be exercised without a display with `QT_QPA_PLATFORM=offscreen`.
16
+
17
+ ## Python style
18
+
19
+ - Use descriptive verbs for functions and descriptive nouns for variables.
20
+ Keep established coordinate names (`x`, `y`, `z`, `r`) and the documented
21
+ DobotLab method signatures so existing scripts remain compatible.
22
+ - Prefer direct code to trivial forwarding helpers. Use public methods for
23
+ supported operations and GUI callbacks; reserve leading underscores for
24
+ state and implementation details that require internal synchronization.
25
+ - Use `pathlib.Path` for filesystem paths, explicit UTF-8 for text files,
26
+ built-in generic types, and `Type | None` for optional values.
27
+ - Keep imports at module scope unless an optional dependency or circular
28
+ dependency requires delaying them. Do not use dynamic `__import__` calls.
29
+ - Run Ruff and the hardware-free tests before submitting changes. Add
30
+ regression tests when fixing behavior, especially transport and queue bugs.
31
+
32
+ ## Package boundaries
33
+
34
+ `protocol.py` encodes frames, `transport.py` owns serial/TCP connections,
35
+ `device.py` exposes the robot API, and `discovery.py` finds devices. The
36
+ optional `panel` package contains the Qt GUI. Keep Qt imports out of the
37
+ base library. `simulator.py` supplies a software serial backend for testing.
38
+
39
+ The release process is described in [docs/publishing.md](docs/publishing.md).
40
+
41
+ ## Documentation
42
+
43
+ Install `python -m pip install -e ".[docs]"`, then use `mkdocs serve` to
44
+ preview or `mkdocs build --strict` to check links and build the site.
45
+ Navigation is configured in `mkdocs.yml`; retain `docs/SUMMARY.md` as the
46
+ Markdown table of contents.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Tal Eylon
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,14 @@
1
+ include LICENSE README.md CONTRIBUTING.md
2
+ recursive-include pydobotlab *.py *.qss
3
+ recursive-include tests *.py
4
+ recursive-include examples *.py
5
+ recursive-include docs *.md
6
+ recursive-include scripts *.py
7
+ prune .github
8
+ prune .venv
9
+ prune build
10
+ prune dist
11
+ global-exclude __pycache__ *.py[cod]
12
+ include mkdocs.yml
13
+ prune site
14
+ global-exclude PyPI-Recovery-Codes-*.txt
@@ -0,0 +1,143 @@
1
+ Metadata-Version: 2.4
2
+ Name: pydobotlab
3
+ Version: 0.1.0
4
+ Summary: Pure-Python control library for the Dobot Magician robot arm. A lightweight, cross-platform replacement for DobotStudio / DobotLab. Tel Aviv University CIM Lab
5
+ Author-email: Tal Eylon <taleylon1@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://taleylon.github.io/pydobotlab/
8
+ Project-URL: Documentation, https://taleylon.github.io/pydobotlab/
9
+ Project-URL: Repository, https://github.com/taleylon/pydobotlab
10
+ Project-URL: Issues, https://github.com/taleylon/pydobotlab/issues
11
+ Keywords: dobot,magician,robotics,serial,education
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Education
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: Operating System :: POSIX :: Linux
16
+ Classifier: Operating System :: Microsoft :: Windows
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
22
+ Classifier: Topic :: Scientific/Engineering
23
+ Requires-Python: >=3.10
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Requires-Dist: pyserial>=3.5
27
+ Provides-Extra: gui
28
+ Requires-Dist: PySide6>=6.5; extra == "gui"
29
+ Provides-Extra: dev
30
+ Requires-Dist: pytest>=8.0; extra == "dev"
31
+ Requires-Dist: pytest-timeout>=2.3; extra == "dev"
32
+ Requires-Dist: ruff>=0.6; extra == "dev"
33
+ Requires-Dist: build>=1.2; extra == "dev"
34
+ Requires-Dist: twine>=6.1; extra == "dev"
35
+ Provides-Extra: docs
36
+ Requires-Dist: mkdocs-material<10,>=9.6; extra == "docs"
37
+ Dynamic: license-file
38
+
39
+ # pydobotlab
40
+
41
+ Python control for the **Dobot Magician** robot arm, with an optional desktop
42
+ control panel and a simulator for working without hardware. Communicates
43
+ over serial using `pyserial`; no vendor DLL or DobotStudio installation is required.
44
+
45
+ **[Documentation & homepage](https://taleylon.github.io/pydobotlab/)** ·
46
+ [API reference](https://taleylon.github.io/pydobotlab/api/overview/) ·
47
+ [GitHub](https://github.com/taleylon/pydobotlab) ·
48
+ [Report an issue](https://github.com/taleylon/pydobotlab/issues)
49
+
50
+ ## Installation
51
+
52
+ Requires **Python 3.10 or newer**. Install in a virtual environment:
53
+
54
+ ```bash
55
+ python -m pip install pydobotlab
56
+ ```
57
+
58
+ For the optional PySide6 desktop panel:
59
+
60
+ ```bash
61
+ python -m pip install "pydobotlab[gui]"
62
+ pydobotlab-panel
63
+ ```
64
+
65
+ The base library requires only `pyserial`; Qt is installed only with the
66
+ `gui` extra. See the [installation guide](https://taleylon.github.io/pydobotlab/getting-started/install/)
67
+ for virtual environments, Windows drivers, and Linux serial-port permissions.
68
+ The project has been used on Ubuntu and Windows; macOS hardware has not been verified.
69
+
70
+ ## Quick start
71
+
72
+ With the arm connected and its workspace clear:
73
+
74
+ ```python
75
+ from pydobotlab import Magician, PTPMode
76
+
77
+ with Magician() as robot: # auto-discover; or specify "/dev/ttyUSB0" / "COM3"
78
+ robot.clear_alarm()
79
+ robot.set_home()
80
+ robot.move_to(220, 0, 50, mode=PTPMode.MOVJ_XYZ)
81
+ pose = robot.get_pose()
82
+ print(pose.x, pose.y, pose.z, pose.joints)
83
+ ```
84
+
85
+ `Dobot` is also available as an alias for `Magician`. The documented DobotLab
86
+ method names and parameters are preserved for existing scripts and teaching material.
87
+
88
+ ## Try it without hardware
89
+
90
+ ```bash
91
+ pydobotlab-panel --simulator
92
+ ```
93
+
94
+ Or use the simulator from Python:
95
+
96
+ ```python
97
+ from pydobotlab import Magician
98
+ from pydobotlab.simulator import install_simulator, stop_all
99
+
100
+ install_simulator(arms=1)
101
+ try:
102
+ with Magician("/dev/sim0", via_broker=False) as robot:
103
+ robot.move_to(220, 0, 50)
104
+ print(robot.get_pose())
105
+ finally:
106
+ stop_all()
107
+ ```
108
+
109
+ The simulator supports offline examples and tests. It approximates motion
110
+ and firmware behavior; it does not validate a real robot's trajectory.
111
+
112
+ ## Features and examples
113
+
114
+ - Cartesian and joint motion, jogging, homing, speed settings, and continuous paths.
115
+ - Suction cup, gripper, laser, digital I/O, sensors, and conveyor commands.
116
+ - Structured alarms, motion-failure exceptions, and live pose streaming.
117
+ - Firmware queue control and `with robot.batch()` for accumulating commands.
118
+ - Multiple robot connections and a local broker for sharing a port between the GUI and scripts.
119
+
120
+ Start with [pick and place](https://github.com/taleylon/pydobotlab/blob/main/examples/pick_and_place.py),
121
+ [smiley drawing](https://github.com/taleylon/pydobotlab/blob/main/examples/draw_smiley.py),
122
+ or [two arms with live panels](https://github.com/taleylon/pydobotlab/blob/main/examples/two_dobots_with_panels.py).
123
+ The [user guide](https://taleylon.github.io/pydobotlab/) covers the API,
124
+ control panel, broker, and wire protocol.
125
+
126
+ ## Development
127
+
128
+ From a local checkout, including before the first PyPI release:
129
+
130
+ ```bash
131
+ python -m pip install -e ".[dev,gui,docs]"
132
+ python -m pytest --timeout=30
133
+ ruff check .
134
+ ruff format --check .
135
+ mkdocs serve
136
+ ```
137
+
138
+ See [contributor guidance](https://github.com/taleylon/pydobotlab/blob/main/CONTRIBUTING.md)
139
+ and the [publishing guide](https://taleylon.github.io/pydobotlab/publishing/).
140
+
141
+ ## License
142
+
143
+ MIT. Maintained by Tal Eylon, <taleylon1@gmail.com>.
@@ -0,0 +1,105 @@
1
+ # pydobotlab
2
+
3
+ Python control for the **Dobot Magician** robot arm, with an optional desktop
4
+ control panel and a simulator for working without hardware. Communicates
5
+ over serial using `pyserial`; no vendor DLL or DobotStudio installation is required.
6
+
7
+ **[Documentation & homepage](https://taleylon.github.io/pydobotlab/)** ·
8
+ [API reference](https://taleylon.github.io/pydobotlab/api/overview/) ·
9
+ [GitHub](https://github.com/taleylon/pydobotlab) ·
10
+ [Report an issue](https://github.com/taleylon/pydobotlab/issues)
11
+
12
+ ## Installation
13
+
14
+ Requires **Python 3.10 or newer**. Install in a virtual environment:
15
+
16
+ ```bash
17
+ python -m pip install pydobotlab
18
+ ```
19
+
20
+ For the optional PySide6 desktop panel:
21
+
22
+ ```bash
23
+ python -m pip install "pydobotlab[gui]"
24
+ pydobotlab-panel
25
+ ```
26
+
27
+ The base library requires only `pyserial`; Qt is installed only with the
28
+ `gui` extra. See the [installation guide](https://taleylon.github.io/pydobotlab/getting-started/install/)
29
+ for virtual environments, Windows drivers, and Linux serial-port permissions.
30
+ The project has been used on Ubuntu and Windows; macOS hardware has not been verified.
31
+
32
+ ## Quick start
33
+
34
+ With the arm connected and its workspace clear:
35
+
36
+ ```python
37
+ from pydobotlab import Magician, PTPMode
38
+
39
+ with Magician() as robot: # auto-discover; or specify "/dev/ttyUSB0" / "COM3"
40
+ robot.clear_alarm()
41
+ robot.set_home()
42
+ robot.move_to(220, 0, 50, mode=PTPMode.MOVJ_XYZ)
43
+ pose = robot.get_pose()
44
+ print(pose.x, pose.y, pose.z, pose.joints)
45
+ ```
46
+
47
+ `Dobot` is also available as an alias for `Magician`. The documented DobotLab
48
+ method names and parameters are preserved for existing scripts and teaching material.
49
+
50
+ ## Try it without hardware
51
+
52
+ ```bash
53
+ pydobotlab-panel --simulator
54
+ ```
55
+
56
+ Or use the simulator from Python:
57
+
58
+ ```python
59
+ from pydobotlab import Magician
60
+ from pydobotlab.simulator import install_simulator, stop_all
61
+
62
+ install_simulator(arms=1)
63
+ try:
64
+ with Magician("/dev/sim0", via_broker=False) as robot:
65
+ robot.move_to(220, 0, 50)
66
+ print(robot.get_pose())
67
+ finally:
68
+ stop_all()
69
+ ```
70
+
71
+ The simulator supports offline examples and tests. It approximates motion
72
+ and firmware behavior; it does not validate a real robot's trajectory.
73
+
74
+ ## Features and examples
75
+
76
+ - Cartesian and joint motion, jogging, homing, speed settings, and continuous paths.
77
+ - Suction cup, gripper, laser, digital I/O, sensors, and conveyor commands.
78
+ - Structured alarms, motion-failure exceptions, and live pose streaming.
79
+ - Firmware queue control and `with robot.batch()` for accumulating commands.
80
+ - Multiple robot connections and a local broker for sharing a port between the GUI and scripts.
81
+
82
+ Start with [pick and place](https://github.com/taleylon/pydobotlab/blob/main/examples/pick_and_place.py),
83
+ [smiley drawing](https://github.com/taleylon/pydobotlab/blob/main/examples/draw_smiley.py),
84
+ or [two arms with live panels](https://github.com/taleylon/pydobotlab/blob/main/examples/two_dobots_with_panels.py).
85
+ The [user guide](https://taleylon.github.io/pydobotlab/) covers the API,
86
+ control panel, broker, and wire protocol.
87
+
88
+ ## Development
89
+
90
+ From a local checkout, including before the first PyPI release:
91
+
92
+ ```bash
93
+ python -m pip install -e ".[dev,gui,docs]"
94
+ python -m pytest --timeout=30
95
+ ruff check .
96
+ ruff format --check .
97
+ mkdocs serve
98
+ ```
99
+
100
+ See [contributor guidance](https://github.com/taleylon/pydobotlab/blob/main/CONTRIBUTING.md)
101
+ and the [publishing guide](https://taleylon.github.io/pydobotlab/publishing/).
102
+
103
+ ## License
104
+
105
+ MIT. Maintained by Tal Eylon, <taleylon1@gmail.com>.
@@ -0,0 +1,54 @@
1
+ # pydobotlab — User Guide
2
+
3
+ `pydobotlab` is a pure-Python control library and GUI for the **Dobot Magician** robot arm. It replaces the official DobotStudio / DobotLab desktop app with something students can drive from a Python script *and* a control panel at the same time.
4
+
5
+ Browse the guide using the sidebar, search for an API method, or use the [table of contents](SUMMARY.md). The Markdown source lives in the [GitHub repository](https://github.com/taleylon/pydobotlab/tree/main/docs). If you're new, start with [Getting Started](getting-started/install.md). If you already know the SDK and just want to look up a command, jump to the [API Reference](api/overview.md).
6
+
7
+ ```bash
8
+ python -m pip install pydobotlab
9
+ # Optional desktop control panel:
10
+ python -m pip install "pydobotlab[gui]"
11
+ ```
12
+
13
+ [Get started](getting-started/install.md){ .md-button .md-button--primary }
14
+ [View on PyPI](https://pypi.org/project/pydobotlab/){ .md-button }
15
+
16
+ ## What's in this guide
17
+
18
+ The guide is split into four parts:
19
+
20
+ 1. **[Getting Started](getting-started/install.md)** — installing the package, connecting to an arm, running your first program, and the simulator.
21
+ 2. **[API Reference](api/overview.md)** — every public function on `Magician`, grouped by what it does (motion, alarms, queue, end-effectors, I/O, ...). Each entry says what it does, what it returns, and which Dobot protocol command it sends.
22
+ 3. **[Control Panel & Broker](panel/overview.md)** — the PySide6 GUI, the multi-arm hub, and the TCP broker that lets the panel and a script share one arm.
23
+ 4. **[Protocol Reference](protocol/frame-format.md)** — the wire-level Dobot protocol: frame format, control byte, the full `CommandID` table, and how each ID maps back to a `Magician` method.
24
+
25
+ ## How the layers fit together
26
+
27
+ ```
28
+ ┌───────────────────────────────┐ ┌────────────────────────────┐
29
+ │ your script (Magician API) │ │ ControlPanel (PySide6) │
30
+ └───────────────┬───────────────┘ └──────────────┬─────────────┘
31
+ │ │
32
+ └──────────────┬────────────────────┘
33
+
34
+ ┌─────────────────────┐
35
+ │ DobotBroker │ TCP 127.0.0.1:8765
36
+ │ (per-port refcnt) │ (auto-spawned by panel)
37
+ └──────────┬──────────┘
38
+
39
+ ┌─────────────────────┐
40
+ │ SerialTransport │ 115200 8N1
41
+ └──────────┬──────────┘
42
+
43
+ ┌─────────────────────┐
44
+ │ Dobot V1.1.5 wire │ AA AA len id ctrl … cks
45
+ └─────────────────────┘
46
+ ```
47
+
48
+ The script-side and panel-side both speak through the same `Magician` class. When the panel is open the broker mediates so both can drive one arm; when there's no panel, `Magician` opens the serial port directly.
49
+
50
+ ## Quick links
51
+
52
+ - Source code on disk: `pydobotlab/` (library), `pydobotlab/panel/` (GUI), `examples/`.
53
+ - Protocol PDF reference: [Dobot Communication Protocol V1.1.5](https://www.alcom.no/wp-content/uploads/2019/11/Dobot-Communication-Protocol-V1.1.5-1.pdf) — the canonical source for command IDs and parameter layouts. Every page in the [Protocol Reference](protocol/frame-format.md) section cites it.
54
+ - Magician feature page: [Dobot Magician — features](https://www.dobot-robots.com/products/education/magician.html).
@@ -0,0 +1,45 @@
1
+ # Summary
2
+
3
+ * [Introduction](README.md)
4
+
5
+ ## Getting Started
6
+
7
+ * [Installation](getting-started/install.md)
8
+ * [Connecting to an arm](getting-started/connect.md)
9
+ * [Hello, Magician](getting-started/first-script.md)
10
+ * [Running without hardware (simulator)](getting-started/simulator.md)
11
+ * [Linux setup (Ubuntu / Debian)](getting-started/linux.md)
12
+
13
+ ## API Reference
14
+
15
+ * [Overview](api/overview.md)
16
+ * [Connection lifecycle](api/connection.md)
17
+ * [Pose & motion](api/motion.md)
18
+ * [JOG (live, non-trajectory motion)](api/jog.md)
19
+ * [Speed & motion parameters](api/speed.md)
20
+ * [Alarms](api/alarms.md)
21
+ * [Queue control](api/queue.md)
22
+ * [End-effectors](api/end-effectors.md)
23
+ * [Digital I/O & sensors](api/io.md)
24
+ * [Conveyor & extras](api/extras.md)
25
+ * [Real-time pose stream](api/pose-stream.md)
26
+ * [Discovery (finding arms)](api/discovery.md)
27
+ * [Errors & exceptions](api/errors.md)
28
+
29
+ ## Control Panel & Broker
30
+
31
+ * [Overview](panel/overview.md)
32
+ * [Running the panel](panel/running.md)
33
+ * [The DobotBroker (multi-client)](panel/broker.md)
34
+
35
+ ## Protocol Reference
36
+
37
+ * [Frame format](protocol/frame-format.md)
38
+ * [Control byte (rw / isQueued)](protocol/ctrl-byte.md)
39
+ * [Command ID table](protocol/command-ids.md)
40
+ * [Method ↔ command mapping](protocol/method-to-cmd.md)
41
+ * [Alarm bitmask](protocol/alarms.md)
42
+
43
+ ## Project
44
+
45
+ * [Publishing and maintaining the project](publishing.md)
@@ -0,0 +1,125 @@
1
+ # Alarms
2
+
3
+ The arm reports faults as a 16-byte (128-bit) bitmask. Each bit is a named alarm code (`0x00..0x7F`); `byte_index = code // 8`, `bit_index = code % 8`. pydobotlab decodes that mask into a friendly [`AlarmSet`](#alarmset) with names *and* per-alarm troubleshoot lines.
4
+
5
+ When motion alarms fire, pydobotlab surfaces them as [`DobotKinematicError`](errors.md#dobotkinematicerror-extends-dobotalarmerror) from the waiters in [`move_to`](motion.md#move-to) / [`set_home`](motion.md#set_home-waittrue-timeout600-raise_on_alarmtrue-int) / [`wait_for`](queue.md#wait_forindex-timeoutnone-poll002-raise_on_alarmtrue-none) / [`wait_idle`](queue.md#wait_idle-timeoutnone-poll002-raise_on_alarmtrue-none), so you don't sit on a `wait_for` forever for a command the firmware has refused.
6
+
7
+ ---
8
+
9
+ ## `get_alarms() -> AlarmSet`
10
+
11
+ **Purpose.** Read and decode the firmware's 16-byte alarm bitmask.
12
+
13
+ **Inputs.** None.
14
+
15
+ **Returns.** [`AlarmSet`](#alarmset).
16
+
17
+ **Protocol.** [`GET_ALARMS_STATE`](../protocol/command-ids.md) (20), read + immediate. Response: 16 raw bytes (the mask).
18
+
19
+ **Example.**
20
+
21
+ ```python
22
+ alarms = bot.get_alarms()
23
+ if alarms:
24
+ for line in alarms.format():
25
+ print(" •", line)
26
+ ```
27
+
28
+ ---
29
+
30
+ ## `clear_alarm(*, verify=False, settle=0.15) -> None` {#clear-alarm}
31
+
32
+ **Purpose.** Clear all active alarms.
33
+
34
+ **Inputs.**
35
+
36
+ | Arg | Type | Default | Meaning |
37
+ |-----|------|---------|---------|
38
+ | `verify` | `bool` | `False` | After clearing, sleep `settle` seconds and re-poll. If any *motion-related* alarm bit is still set, the firmware honoured the clear but the **physical condition** that triggered it (e.g. a joint mashed against a limit) is still active — raise [`DobotAlarmError`](errors.md#dobotalarmerror) so the student knows to fix the physical state, not just the bitmask. |
39
+ | `settle` | `float` | `0.15` | Verification settle delay in seconds. Only relevant when `verify=True`. |
40
+
41
+ **Returns.** `None`.
42
+
43
+ **Raises.** [`DobotAlarmError`](errors.md#dobotalarmerror) when `verify=True` and an alarm reasserts itself.
44
+
45
+ **Protocol.** [`CLEAR_ALL_ALARMS_STATE`](../protocol/command-ids.md) (20), **write** + immediate. Param: empty. Response: ack.
46
+
47
+ > **Note** — the protocol uses the same command ID (`20`) for read (get) and write (clear); the [`rw` bit](../protocol/ctrl-byte.md) of the control byte is what distinguishes them.
48
+
49
+ **Example — verify-on-clear.**
50
+
51
+ ```python
52
+ from pydobotlab.errors import DobotAlarmError
53
+
54
+ try:
55
+ bot.clear_alarm(verify=True)
56
+ except DobotAlarmError as e:
57
+ print("alarm reasserted — fix the physical condition first:")
58
+ for name in e.alarms:
59
+ print(" •", name)
60
+ ```
61
+
62
+ `clear_alarms()` (plural) is kept as a deprecated alias.
63
+
64
+ ---
65
+
66
+ ## `ensure_no_alarms() -> None`
67
+
68
+ **Purpose.** Guard call — raise [`DobotAlarmError`](errors.md#dobotalarmerror) if any alarm is currently active.
69
+
70
+ **Returns.** `None`.
71
+
72
+ **Protocol.** Sends `GET_ALARMS_STATE` (20) and inspects the result.
73
+
74
+ ---
75
+
76
+ ## `AlarmSet`
77
+
78
+ `from pydobotlab import AlarmSet`. The decoded result of `get_alarms()`.
79
+
80
+ | Attribute / Method | Returns | What |
81
+ |-------------------------------|---------|------|
82
+ | `raw` | `bytes` | The original 16-byte mask, untouched. |
83
+ | `alarms` | `tuple[Alarm \| int, ...]` | Active alarm codes, named when known. |
84
+ | `bool(alarmset)` | `bool` | `True` iff any bit is set. |
85
+ | `iter(alarmset)` | iter | Iterates the active alarm codes. |
86
+ | `name in alarmset` | `bool` | Membership test. |
87
+ | `.names()` | `list[str]` | Friendly names, including categorised labels for unmapped codes (e.g. `JOINT_LIMIT_POS_0x44`). |
88
+ | `.format()` | `list[str]` | One `"{TAG}: {explanation + how to fix}"` line per active alarm — what the panel banner and `DobotKinematicError.message` use. |
89
+ | `.describe()` | `list[str]` | Backwards-compat alias for `.format()`. |
90
+
91
+ ---
92
+
93
+ ## `Alarm` codes
94
+
95
+ `from pydobotlab import Alarm`. The named bit positions in the mask. Categorised by byte:
96
+
97
+ | Byte | Range | Members |
98
+ |------|-------|---------|
99
+ | 0 | `0x00..0x07` | `PUBLIC_RESET`, `PUBLIC_UNDEFINED_INSTRUCTION`, `PUBLIC_FILE_SYSTEM`, `PUBLIC_MCU_COMM`, `PUBLIC_ANGLE_SENSOR_READ` |
100
+ | 2 | `0x10..0x17` | `PLAN_INVERSE_RESOLVE`, `PLAN_INVERSE_LIMIT`, `PLAN_DATA_REPEAT`, `PLAN_CURRENT_JOINT_OUT_OF_RANGE`, `PLAN_MOTION_TARGET_OUT_OF_WORKSPACE`, `PLAN_IN_SINGULARITY_ZONE` |
101
+ | 4 | `0x20..0x27` | `KINEMATIC_SINGULARITY`, `KINEMATIC_TARGET_OUT_OF_WORKSPACE`, `KINEMATIC_INVERSE_LIMIT` |
102
+ | 6 | `0x30..0x33` | `OVERSPEED_J1..J4` |
103
+ | 8 | `0x40..0x43` | `LIMIT_POS_J1..J4` (positive joint limit) |
104
+ | 10 | `0x50..0x53` | `LIMIT_NEG_J1..J4` (negative joint limit) |
105
+ | 12 | `0x60..0x63` | `LOST_STEP_J1..J4` |
106
+ | 14 | `0x70` | `OTHER_LIMIT_TRIGGERED_J1_J2` (auto-leveling switch) |
107
+
108
+ Each named alarm has a one-line troubleshoot tip surfaced via `AlarmSet.format()`. For example, `LIMIT_POS_J3` reads:
109
+
110
+ > `joint 3 (forearm) hit positive limit — jog Z down (or J3 negative), or call set_home()`
111
+
112
+ Unmapped bits are surfaced as `JOINT_LIMIT_POS_0x44` / `OVERSPEED_0x35` / `UNKNOWN_0x90` etc. so they're never silently dropped.
113
+
114
+ ---
115
+
116
+ ## Recovery patterns
117
+
118
+ | Situation | What to do |
119
+ |-----------|-----------|
120
+ | Workspace / IK alarm (`PLAN_INVERSE_RESOLVE`, `PLAN_MOTION_TARGET_OUT_OF_WORKSPACE`, `KINEMATIC_*`) | The physical state is fine; the *target* was the problem. Call [`bot.clear_alarm()`](#clear-alarm), then retry with a reachable target. |
121
+ | Joint limit hit during JOG (`LIMIT_POS_J*` / `LIMIT_NEG_J*`) | The joint is *physically* against the stop. Jog away from the limit first, then `clear_alarm()`. Or just call [`bot.set_home()`](motion.md#set_home-waittrue-timeout600-raise_on_alarmtrue-int) — homing re-zeroes joints and clears the alarm in one step. |
122
+ | Lost step (`LOST_STEP_J*`) | Re-home to recalibrate. |
123
+ | Public / system fault (`PUBLIC_*`) | Power-cycle the arm (sometimes a re-home is enough for `PUBLIC_RESET`). |
124
+
125
+ The control panel's "Clear Alarm" button now calls [`clear_alarm()`](#clear-alarm) and re-polls 200 ms later; if the alarm comes back, it pops up an "Alarm persists" warning with the exact troubleshoot lines.
@@ -0,0 +1,91 @@
1
+ # Connection lifecycle
2
+
3
+ Methods that open and close the serial / broker channel to the arm. The `Magician` constructor doesn't talk to hardware; that happens in `connect()`.
4
+
5
+ ## `Magician(port=None, baudrate=115200, timeout=1.0, *, auto_start_queue=True, only_known_adapters=False, via_broker="auto", broker_host="127.0.0.1", broker_port=8765)`
6
+
7
+ **Purpose.** Construct a Magician driver. No I/O happens here.
8
+
9
+ See [Connecting to an arm](../getting-started/connect.md) for the full table of constructor options.
10
+
11
+ ---
12
+
13
+ ## `connect() -> None`
14
+
15
+ **Purpose.** Resolve the port, open the line, and prepare the firmware queue.
16
+
17
+ **Inputs.** None.
18
+
19
+ **Returns.** `None`.
20
+
21
+ **Raises.**
22
+ * [`DobotConnectionError`](errors.md#dobotconnectionerror) — port not found or open failed.
23
+ * [`DobotPortInUseError`](errors.md#dobotportinuseerror-extends-dobotconnectionerror) — a second Magician in this process already owns this port, or the OS refused exclusive access.
24
+
25
+ **Protocol.** No single command — at the wire level this is the OS-side `serial.Serial.open()` (or a TCP `ATTACH` to the broker), followed by:
26
+ * [`SET_QUEUED_CMD_CLEAR`](../protocol/command-ids.md) (245)
27
+ * [`SET_QUEUED_CMD_START_EXEC`](../protocol/command-ids.md) (240)
28
+
29
+ The two queue commands are skipped if you constructed with `auto_start_queue=False`.
30
+
31
+ **Example.**
32
+
33
+ ```python
34
+ bot = Magician(port="/dev/ttyUSB0")
35
+ bot.connect()
36
+ # … work …
37
+ bot.disconnect()
38
+ ```
39
+
40
+ ---
41
+
42
+ ## `disconnect() -> None`
43
+
44
+ **Purpose.** Stop any running pose stream, stop the firmware queue, and close the transport.
45
+
46
+ **Inputs.** None.
47
+
48
+ **Returns.** `None`.
49
+
50
+ **Raises.** Doesn't raise — the close path swallows transport errors so you can call it from `finally:` blocks safely.
51
+
52
+ **Protocol.** Sends [`SET_QUEUED_CMD_STOP_EXEC`](../protocol/command-ids.md) (241) on the way out, then closes the OS handle (or sends a TCP close to the broker).
53
+
54
+ ---
55
+
56
+ ## `__enter__() / __exit__()` — context-manager support
57
+
58
+ **Purpose.** Use `Magician` with `with`, which calls `connect()` on entry and `disconnect()` on exit even if an exception is raised inside the block.
59
+
60
+ **Example.**
61
+
62
+ ```python
63
+ with Magician() as bot:
64
+ bot.move_to(200, 0, 50, 0)
65
+ # Disconnected here — even if the move raised.
66
+ ```
67
+
68
+ ---
69
+
70
+ ## `port` *(read-only property)*
71
+
72
+ **Purpose.** The OS-resolved port name once `connect()` has run. `None` before connection.
73
+
74
+ **Returns.** `str | None`.
75
+
76
+ ---
77
+
78
+ ## `is_open` *(read-only property)*
79
+
80
+ **Purpose.** `True` while the underlying transport is open.
81
+
82
+ **Returns.** `bool`.
83
+
84
+ ---
85
+
86
+ ## Process-level helpers
87
+
88
+ These live in `pydobotlab.discovery` and are documented on the [Discovery](discovery.md) page, but you'll see them often when wrangling multi-arm setups:
89
+
90
+ * `is_port_in_use(port)` — `True` if some `Magician` in *this process* currently owns the port.
91
+ * `find_free_port(only_known_adapters=False)` — return the first port that answers like a Dobot and isn't already claimed.