hpgl-buddy 1.0.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.
- hpgl_buddy-1.0.0/LICENSE +21 -0
- hpgl_buddy-1.0.0/PKG-INFO +200 -0
- hpgl_buddy-1.0.0/README.md +172 -0
- hpgl_buddy-1.0.0/pyproject.toml +49 -0
- hpgl_buddy-1.0.0/setup.cfg +4 -0
- hpgl_buddy-1.0.0/src/hpgl_buddy/__init__.py +17 -0
- hpgl_buddy-1.0.0/src/hpgl_buddy/cli.py +325 -0
- hpgl_buddy-1.0.0/src/hpgl_buddy/demo/__init__.py +6 -0
- hpgl_buddy-1.0.0/src/hpgl_buddy/demo/generator.py +256 -0
- hpgl_buddy-1.0.0/src/hpgl_buddy/demo/scene.py +162 -0
- hpgl_buddy-1.0.0/src/hpgl_buddy/devices/__init__.py +19 -0
- hpgl_buddy-1.0.0/src/hpgl_buddy/devices/base.py +122 -0
- hpgl_buddy-1.0.0/src/hpgl_buddy/devices/profiles/hp7475a.toml +50 -0
- hpgl_buddy-1.0.0/src/hpgl_buddy/devices/registry.py +73 -0
- hpgl_buddy-1.0.0/src/hpgl_buddy/errors.py +70 -0
- hpgl_buddy-1.0.0/src/hpgl_buddy/execution/__init__.py +19 -0
- hpgl_buddy-1.0.0/src/hpgl_buddy/execution/executor.py +368 -0
- hpgl_buddy-1.0.0/src/hpgl_buddy/execution/flow_control.py +117 -0
- hpgl_buddy-1.0.0/src/hpgl_buddy/execution/planner.py +125 -0
- hpgl_buddy-1.0.0/src/hpgl_buddy/execution/progress.py +107 -0
- hpgl_buddy-1.0.0/src/hpgl_buddy/hpgl/__init__.py +19 -0
- hpgl_buddy-1.0.0/src/hpgl_buddy/hpgl/instruction.py +71 -0
- hpgl_buddy-1.0.0/src/hpgl_buddy/hpgl/parser.py +158 -0
- hpgl_buddy-1.0.0/src/hpgl_buddy/hpgl/syntax_check.py +180 -0
- hpgl_buddy-1.0.0/src/hpgl_buddy/hpgl/tokens.py +107 -0
- hpgl_buddy-1.0.0/src/hpgl_buddy/interface/__init__.py +11 -0
- hpgl_buddy-1.0.0/src/hpgl_buddy/interface/base.py +99 -0
- hpgl_buddy-1.0.0/src/hpgl_buddy/interface/serial_rs232.py +147 -0
- hpgl_buddy-1.0.0/src/hpgl_buddy/logging_setup.py +88 -0
- hpgl_buddy-1.0.0/src/hpgl_buddy/status/__init__.py +19 -0
- hpgl_buddy-1.0.0/src/hpgl_buddy/status/adhoc.py +167 -0
- hpgl_buddy-1.0.0/src/hpgl_buddy/status/escape.py +151 -0
- hpgl_buddy-1.0.0/src/hpgl_buddy/status/exchange.py +122 -0
- hpgl_buddy-1.0.0/src/hpgl_buddy/status/monitor.py +57 -0
- hpgl_buddy-1.0.0/src/hpgl_buddy/status/status_codes.py +136 -0
- hpgl_buddy-1.0.0/src/hpgl_buddy/version.py +9 -0
- hpgl_buddy-1.0.0/src/hpgl_buddy.egg-info/PKG-INFO +200 -0
- hpgl_buddy-1.0.0/src/hpgl_buddy.egg-info/SOURCES.txt +42 -0
- hpgl_buddy-1.0.0/src/hpgl_buddy.egg-info/dependency_links.txt +1 -0
- hpgl_buddy-1.0.0/src/hpgl_buddy.egg-info/entry_points.txt +2 -0
- hpgl_buddy-1.0.0/src/hpgl_buddy.egg-info/requires.txt +4 -0
- hpgl_buddy-1.0.0/src/hpgl_buddy.egg-info/top_level.txt +1 -0
- hpgl_buddy-1.0.0/tests/test_basic.py +128 -0
- hpgl_buddy-1.0.0/tests/test_execution.py +325 -0
hpgl_buddy-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Pavel Kim
|
|
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,200 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hpgl-buddy
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Carefree, observable plotting of HP-GL files on HP pen plotters over RS-232.
|
|
5
|
+
Author-email: Pavel Kim <hello@pavelkim.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/hpgl-buddy/hpgl-buddy
|
|
8
|
+
Project-URL: Repository, https://github.com/hpgl-buddy/hpgl-buddy
|
|
9
|
+
Project-URL: Issues, https://github.com/hpgl-buddy/hpgl-buddy/issues
|
|
10
|
+
Keywords: hpgl,plotter,hp7475a,rs232,pen-plotter
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Printing
|
|
20
|
+
Classifier: Topic :: System :: Hardware :: Hardware Drivers
|
|
21
|
+
Requires-Python: >=3.13
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: pyserial>=3.5
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
27
|
+
Dynamic: license-file
|
|
28
|
+
|
|
29
|
+
# hpgl-buddy
|
|
30
|
+
|
|
31
|
+
Carefree, observable plotting of HP-GL files on HP pen plotters over RS-232.
|
|
32
|
+
|
|
33
|
+
It does not just shove a file at the plotter: it validates the file, splits it to fit
|
|
34
|
+
the device buffer, feeds it so the buffer never overflows and an inked pen never stalls
|
|
35
|
+
mid-stroke, watches the device for faults the whole time, and logs every exchange so a
|
|
36
|
+
run can be understood and troubleshooted from the log alone.
|
|
37
|
+
|
|
38
|
+
**Supported device:** HP 7475A (RS-232). New devices are added as declarative profiles
|
|
39
|
+
(see [Extending](#extending)); HP-IB is planned.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Install
|
|
44
|
+
|
|
45
|
+
Requires Python 3.13 and a USB-serial adapter (macOS: use the `/dev/cu.*` device).
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install -e . # editable, for development
|
|
49
|
+
# or build a wheel:
|
|
50
|
+
python -m build --wheel # -> dist/hpgl_buddy-*.whl (or: tox -e build)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`pyserial` is the only runtime dependency.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Quick start
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# 1. Validate a file offline (no plotter needed)
|
|
61
|
+
hpgl-buddy check drawing.hpgl
|
|
62
|
+
|
|
63
|
+
# 2. Check the plotter is alive and interpret its status
|
|
64
|
+
hpgl-buddy status --port /dev/cu.usbserial-XXXX
|
|
65
|
+
|
|
66
|
+
# 3. Plot it (verbose shows every byte/ESC exchange)
|
|
67
|
+
hpgl-buddy -v plot drawing.hpgl --port /dev/cu.usbserial-XXXX
|
|
68
|
+
|
|
69
|
+
# 4. Generate and plot a built-in demo
|
|
70
|
+
hpgl-buddy demo --pens 6 --out demo.hpgl
|
|
71
|
+
hpgl-buddy plot demo.hpgl --port /dev/cu.usbserial-XXXX
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Serial defaults: **9600 8N1, no flow control** (configurable). Flow control is off by
|
|
75
|
+
default because XON/XOFF corrupted the exchange on the on-site adapter; enable it with
|
|
76
|
+
`--xonxoff` if your cabling needs it.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Commands
|
|
81
|
+
|
|
82
|
+
| Command | What it does |
|
|
83
|
+
|---|---|
|
|
84
|
+
| `check FILE` | Offline HP-GL syntax check. No device. Exit non-zero on errors. |
|
|
85
|
+
| `status` | Ad-hoc healthcheck: identification, buffer, status byte, errors, limits - all interpreted. |
|
|
86
|
+
| `plot FILE` | Safe, buffer-aware plotting with progress + end-of-run report. |
|
|
87
|
+
| `monitor on\|off\|watch` | Switch monitor mode (computer port) or stream the echoed bytes (terminal port). |
|
|
88
|
+
| `demo` | Generate demo HP-GL (`--scene card` shapes/fills/labels/colours, or `--scene house` a continuous one-line drawing). |
|
|
89
|
+
|
|
90
|
+
Global: `-v/--verbose` (DEBUG, incl. raw ASCII+hex wire dumps), `--version`.
|
|
91
|
+
|
|
92
|
+
Serial options (on `status`/`plot`/`monitor`): `--port`, `--model` (default `hp7475a`),
|
|
93
|
+
`--baud`, `--framing` (e.g. `8N1`), `--timeout`, `--xonxoff`, `--rtscts`.
|
|
94
|
+
|
|
95
|
+
### plot options
|
|
96
|
+
|
|
97
|
+
- `--on-error abort|prompt|continue` - on a reported error: stop and park the pen
|
|
98
|
+
(default), ask interactively, or auto-recover (`ESC.K` discard -> `IN` -> replay the
|
|
99
|
+
tracked state preamble -> carry on).
|
|
100
|
+
- `--live-hpgl-verify off|chunk|pu` - optional on-device HP-GL error checking (see
|
|
101
|
+
[Verification](#verification)). Default `off`.
|
|
102
|
+
- `--ignore-syntax-errors` - plot even if the offline check found errors.
|
|
103
|
+
- `--stats-json PATH` - write run statistics as JSON (`-` for stdout).
|
|
104
|
+
|
|
105
|
+
### monitor (two ports)
|
|
106
|
+
|
|
107
|
+
The 7475A enables monitor mode via an ESC sequence on the **computer** (data) port, then
|
|
108
|
+
echoes received bytes out a separate **terminal** port. So:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
# stream the echo on the terminal port, enabling monitor on the computer port first
|
|
112
|
+
hpgl-buddy monitor watch --port /dev/cu.TERMINAL --command-port /dev/cu.COMPUTER --enable
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
`watch` prints every byte as binary, hex, decimal, and ASCII/control-name.
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## How plotting works
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
file -> parse -> offline syntax check -> plan into chunks -> stream to device -> report
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
1. **Parse** the bytes into instructions, each tagged with its source line and sequence
|
|
126
|
+
index (so any error names the exact command).
|
|
127
|
+
2. **Syntax check** offline; `plot` refuses a file with errors unless `--ignore-syntax-errors`.
|
|
128
|
+
3. **Plan** into chunks of <=256 bytes, split only at instruction boundaries (never inside
|
|
129
|
+
a command), each tagged whether it ends with the pen up.
|
|
130
|
+
4. **Stream**: a chunk is sent only when `ESC.B` reports enough free buffer space. This one
|
|
131
|
+
gate both prevents overflow and keeps the buffer fed, so a long pen-down stroke spanning
|
|
132
|
+
several chunks never underruns (an underrun would park an inked pen and blot).
|
|
133
|
+
5. **Watch** (always on, after each chunk): `ESC.E` for I/O faults (overflow / framing /
|
|
134
|
+
data loss) and `ESC.O` for environmental faults (paper lever or pinch wheels raised ->
|
|
135
|
+
abort; VIEW pressed -> warn). Both are immediate and never stall the pen.
|
|
136
|
+
6. **Confirm**: a final `OS;OE;OI;` tailgate waits for the pen to physically finish and
|
|
137
|
+
reports the end status / any HP-GL error.
|
|
138
|
+
7. **Report** instructions/chunks/bytes sent, elapsed time, recovered errors, and warnings
|
|
139
|
+
(and the same as JSON via `--stats-json`).
|
|
140
|
+
|
|
141
|
+
### Verification
|
|
142
|
+
|
|
143
|
+
HP-GL/syntax errors are a property of the *file* (already validated offline), so on-device
|
|
144
|
+
HP-GL error checking is **optional** and off by default. When enabled it never stalls the
|
|
145
|
+
pen - it uses a *one-deep* tailgate: the `OS;OE;OI;` query is prefixed to the chunk after a
|
|
146
|
+
pen-up, so its verdict reports the *previous* chunk while the current one is already drawing.
|
|
147
|
+
|
|
148
|
+
- `chunk` - check at pen-up chunk boundaries.
|
|
149
|
+
- `pu` - break a chunk at every pen-up so each completed stroke is checked (more, smaller
|
|
150
|
+
chunks).
|
|
151
|
+
|
|
152
|
+
Either way you learn a chunk is clean within ~1 chunk, and a reported error names the span
|
|
153
|
+
of chunks it could belong to with all candidate instructions.
|
|
154
|
+
|
|
155
|
+
### Limits to know
|
|
156
|
+
|
|
157
|
+
- **No pen sensing on the 7475A**: a missing or fallen pen plots dry and is *not* detectable
|
|
158
|
+
by any status query. Pre-load the pens your file uses. (`plot` warns about this.)
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Architecture
|
|
163
|
+
|
|
164
|
+
Thoroughly separated layers, each replaceable on its own:
|
|
165
|
+
|
|
166
|
+
```
|
|
167
|
+
hpgl/ parse HP-GL bytes into a Program; offline syntax check
|
|
168
|
+
devices/ declarative TOML profiles + abstract Device base (registry)
|
|
169
|
+
interface/ Transport abstraction + pyserial RS-232 implementation
|
|
170
|
+
status/ ESC + HP-GL command builders, response parsers, status interpretation, monitor
|
|
171
|
+
execution/ planner (Program -> chunks) + flow control + executor + progress/report
|
|
172
|
+
demo/ demo HP-GL generators
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
The authoritative design rationale (with HP manual citations) lives in `DESIGN.md`; the
|
|
176
|
+
running decision log is the "Follow-up steering" section of `TASK-1-BASIC-IMPLEMENTATION.md`.
|
|
177
|
+
|
|
178
|
+
### Extending
|
|
179
|
+
|
|
180
|
+
Add a simple device by dropping a `<model>.toml` into `devices/profiles/` (buffer size,
|
|
181
|
+
pen count, serial defaults, capabilities, `pen_sensing`). A device with unusual behavior
|
|
182
|
+
can subclass `Device` and register it. The interface layer is transport-agnostic, so HP-IB
|
|
183
|
+
can be added as another `Transport` without touching the rest.
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Development
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
tox # run the test suite (Python 3.13)
|
|
191
|
+
tox -e build # build the wheel
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Dependencies are managed two-file style: `requirements-rough.txt` (hand-maintained,
|
|
195
|
+
loose top-level) is frozen into a fully-pinned `requirements.txt` by the
|
|
196
|
+
`dependencies_update` GitHub Actions workflow. A `Dockerfile` provides a reproducible
|
|
197
|
+
build/test image (not for serial I/O - hardware is not reachable from a container).
|
|
198
|
+
|
|
199
|
+
Conventions: extensive logging (no `print`), ASCII-only output, descriptive names, and
|
|
200
|
+
errors that state what happened, where, and why.
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# hpgl-buddy
|
|
2
|
+
|
|
3
|
+
Carefree, observable plotting of HP-GL files on HP pen plotters over RS-232.
|
|
4
|
+
|
|
5
|
+
It does not just shove a file at the plotter: it validates the file, splits it to fit
|
|
6
|
+
the device buffer, feeds it so the buffer never overflows and an inked pen never stalls
|
|
7
|
+
mid-stroke, watches the device for faults the whole time, and logs every exchange so a
|
|
8
|
+
run can be understood and troubleshooted from the log alone.
|
|
9
|
+
|
|
10
|
+
**Supported device:** HP 7475A (RS-232). New devices are added as declarative profiles
|
|
11
|
+
(see [Extending](#extending)); HP-IB is planned.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
Requires Python 3.13 and a USB-serial adapter (macOS: use the `/dev/cu.*` device).
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install -e . # editable, for development
|
|
21
|
+
# or build a wheel:
|
|
22
|
+
python -m build --wheel # -> dist/hpgl_buddy-*.whl (or: tox -e build)
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
`pyserial` is the only runtime dependency.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Quick start
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# 1. Validate a file offline (no plotter needed)
|
|
33
|
+
hpgl-buddy check drawing.hpgl
|
|
34
|
+
|
|
35
|
+
# 2. Check the plotter is alive and interpret its status
|
|
36
|
+
hpgl-buddy status --port /dev/cu.usbserial-XXXX
|
|
37
|
+
|
|
38
|
+
# 3. Plot it (verbose shows every byte/ESC exchange)
|
|
39
|
+
hpgl-buddy -v plot drawing.hpgl --port /dev/cu.usbserial-XXXX
|
|
40
|
+
|
|
41
|
+
# 4. Generate and plot a built-in demo
|
|
42
|
+
hpgl-buddy demo --pens 6 --out demo.hpgl
|
|
43
|
+
hpgl-buddy plot demo.hpgl --port /dev/cu.usbserial-XXXX
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Serial defaults: **9600 8N1, no flow control** (configurable). Flow control is off by
|
|
47
|
+
default because XON/XOFF corrupted the exchange on the on-site adapter; enable it with
|
|
48
|
+
`--xonxoff` if your cabling needs it.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Commands
|
|
53
|
+
|
|
54
|
+
| Command | What it does |
|
|
55
|
+
|---|---|
|
|
56
|
+
| `check FILE` | Offline HP-GL syntax check. No device. Exit non-zero on errors. |
|
|
57
|
+
| `status` | Ad-hoc healthcheck: identification, buffer, status byte, errors, limits - all interpreted. |
|
|
58
|
+
| `plot FILE` | Safe, buffer-aware plotting with progress + end-of-run report. |
|
|
59
|
+
| `monitor on\|off\|watch` | Switch monitor mode (computer port) or stream the echoed bytes (terminal port). |
|
|
60
|
+
| `demo` | Generate demo HP-GL (`--scene card` shapes/fills/labels/colours, or `--scene house` a continuous one-line drawing). |
|
|
61
|
+
|
|
62
|
+
Global: `-v/--verbose` (DEBUG, incl. raw ASCII+hex wire dumps), `--version`.
|
|
63
|
+
|
|
64
|
+
Serial options (on `status`/`plot`/`monitor`): `--port`, `--model` (default `hp7475a`),
|
|
65
|
+
`--baud`, `--framing` (e.g. `8N1`), `--timeout`, `--xonxoff`, `--rtscts`.
|
|
66
|
+
|
|
67
|
+
### plot options
|
|
68
|
+
|
|
69
|
+
- `--on-error abort|prompt|continue` - on a reported error: stop and park the pen
|
|
70
|
+
(default), ask interactively, or auto-recover (`ESC.K` discard -> `IN` -> replay the
|
|
71
|
+
tracked state preamble -> carry on).
|
|
72
|
+
- `--live-hpgl-verify off|chunk|pu` - optional on-device HP-GL error checking (see
|
|
73
|
+
[Verification](#verification)). Default `off`.
|
|
74
|
+
- `--ignore-syntax-errors` - plot even if the offline check found errors.
|
|
75
|
+
- `--stats-json PATH` - write run statistics as JSON (`-` for stdout).
|
|
76
|
+
|
|
77
|
+
### monitor (two ports)
|
|
78
|
+
|
|
79
|
+
The 7475A enables monitor mode via an ESC sequence on the **computer** (data) port, then
|
|
80
|
+
echoes received bytes out a separate **terminal** port. So:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
# stream the echo on the terminal port, enabling monitor on the computer port first
|
|
84
|
+
hpgl-buddy monitor watch --port /dev/cu.TERMINAL --command-port /dev/cu.COMPUTER --enable
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`watch` prints every byte as binary, hex, decimal, and ASCII/control-name.
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## How plotting works
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
file -> parse -> offline syntax check -> plan into chunks -> stream to device -> report
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
1. **Parse** the bytes into instructions, each tagged with its source line and sequence
|
|
98
|
+
index (so any error names the exact command).
|
|
99
|
+
2. **Syntax check** offline; `plot` refuses a file with errors unless `--ignore-syntax-errors`.
|
|
100
|
+
3. **Plan** into chunks of <=256 bytes, split only at instruction boundaries (never inside
|
|
101
|
+
a command), each tagged whether it ends with the pen up.
|
|
102
|
+
4. **Stream**: a chunk is sent only when `ESC.B` reports enough free buffer space. This one
|
|
103
|
+
gate both prevents overflow and keeps the buffer fed, so a long pen-down stroke spanning
|
|
104
|
+
several chunks never underruns (an underrun would park an inked pen and blot).
|
|
105
|
+
5. **Watch** (always on, after each chunk): `ESC.E` for I/O faults (overflow / framing /
|
|
106
|
+
data loss) and `ESC.O` for environmental faults (paper lever or pinch wheels raised ->
|
|
107
|
+
abort; VIEW pressed -> warn). Both are immediate and never stall the pen.
|
|
108
|
+
6. **Confirm**: a final `OS;OE;OI;` tailgate waits for the pen to physically finish and
|
|
109
|
+
reports the end status / any HP-GL error.
|
|
110
|
+
7. **Report** instructions/chunks/bytes sent, elapsed time, recovered errors, and warnings
|
|
111
|
+
(and the same as JSON via `--stats-json`).
|
|
112
|
+
|
|
113
|
+
### Verification
|
|
114
|
+
|
|
115
|
+
HP-GL/syntax errors are a property of the *file* (already validated offline), so on-device
|
|
116
|
+
HP-GL error checking is **optional** and off by default. When enabled it never stalls the
|
|
117
|
+
pen - it uses a *one-deep* tailgate: the `OS;OE;OI;` query is prefixed to the chunk after a
|
|
118
|
+
pen-up, so its verdict reports the *previous* chunk while the current one is already drawing.
|
|
119
|
+
|
|
120
|
+
- `chunk` - check at pen-up chunk boundaries.
|
|
121
|
+
- `pu` - break a chunk at every pen-up so each completed stroke is checked (more, smaller
|
|
122
|
+
chunks).
|
|
123
|
+
|
|
124
|
+
Either way you learn a chunk is clean within ~1 chunk, and a reported error names the span
|
|
125
|
+
of chunks it could belong to with all candidate instructions.
|
|
126
|
+
|
|
127
|
+
### Limits to know
|
|
128
|
+
|
|
129
|
+
- **No pen sensing on the 7475A**: a missing or fallen pen plots dry and is *not* detectable
|
|
130
|
+
by any status query. Pre-load the pens your file uses. (`plot` warns about this.)
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## Architecture
|
|
135
|
+
|
|
136
|
+
Thoroughly separated layers, each replaceable on its own:
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
hpgl/ parse HP-GL bytes into a Program; offline syntax check
|
|
140
|
+
devices/ declarative TOML profiles + abstract Device base (registry)
|
|
141
|
+
interface/ Transport abstraction + pyserial RS-232 implementation
|
|
142
|
+
status/ ESC + HP-GL command builders, response parsers, status interpretation, monitor
|
|
143
|
+
execution/ planner (Program -> chunks) + flow control + executor + progress/report
|
|
144
|
+
demo/ demo HP-GL generators
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
The authoritative design rationale (with HP manual citations) lives in `DESIGN.md`; the
|
|
148
|
+
running decision log is the "Follow-up steering" section of `TASK-1-BASIC-IMPLEMENTATION.md`.
|
|
149
|
+
|
|
150
|
+
### Extending
|
|
151
|
+
|
|
152
|
+
Add a simple device by dropping a `<model>.toml` into `devices/profiles/` (buffer size,
|
|
153
|
+
pen count, serial defaults, capabilities, `pen_sensing`). A device with unusual behavior
|
|
154
|
+
can subclass `Device` and register it. The interface layer is transport-agnostic, so HP-IB
|
|
155
|
+
can be added as another `Transport` without touching the rest.
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Development
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
tox # run the test suite (Python 3.13)
|
|
163
|
+
tox -e build # build the wheel
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Dependencies are managed two-file style: `requirements-rough.txt` (hand-maintained,
|
|
167
|
+
loose top-level) is frozen into a fully-pinned `requirements.txt` by the
|
|
168
|
+
`dependencies_update` GitHub Actions workflow. A `Dockerfile` provides a reproducible
|
|
169
|
+
build/test image (not for serial I/O - hardware is not reachable from a container).
|
|
170
|
+
|
|
171
|
+
Conventions: extensive logging (no `print`), ASCII-only output, descriptive names, and
|
|
172
|
+
errors that state what happened, where, and why.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "hpgl-buddy"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Carefree, observable plotting of HP-GL files on HP pen plotters over RS-232."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.13"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [{ name = "Pavel Kim", email = "hello@pavelkim.com" }]
|
|
13
|
+
keywords = ["hpgl", "plotter", "hp7475a", "rs232", "pen-plotter"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 5 - Production/Stable",
|
|
16
|
+
"Environment :: Console",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"Intended Audience :: End Users/Desktop",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Operating System :: OS Independent",
|
|
21
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
22
|
+
"Programming Language :: Python :: 3.13",
|
|
23
|
+
"Topic :: Printing",
|
|
24
|
+
"Topic :: System :: Hardware :: Hardware Drivers",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
# pyserial is the sole runtime dependency. TOML profiles are read with the
|
|
28
|
+
# standard-library tomllib (Python 3.11+), so no extra parser is needed.
|
|
29
|
+
dependencies = ["pyserial>=3.5"]
|
|
30
|
+
|
|
31
|
+
[project.optional-dependencies]
|
|
32
|
+
dev = ["pytest>=8"]
|
|
33
|
+
|
|
34
|
+
[project.urls]
|
|
35
|
+
Homepage = "https://github.com/hpgl-buddy/hpgl-buddy"
|
|
36
|
+
Repository = "https://github.com/hpgl-buddy/hpgl-buddy"
|
|
37
|
+
Issues = "https://github.com/hpgl-buddy/hpgl-buddy/issues"
|
|
38
|
+
|
|
39
|
+
[project.scripts]
|
|
40
|
+
hpgl-buddy = "hpgl_buddy.cli:main"
|
|
41
|
+
|
|
42
|
+
[tool.setuptools.dynamic]
|
|
43
|
+
version = { attr = "hpgl_buddy.version.__version__" }
|
|
44
|
+
|
|
45
|
+
[tool.setuptools.packages.find]
|
|
46
|
+
where = ["src"]
|
|
47
|
+
|
|
48
|
+
[tool.setuptools.package-data]
|
|
49
|
+
hpgl_buddy = ["devices/profiles/*.toml"]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""hpgl-buddy - carefree, observable plotting of HP-GL files on HP pen plotters.
|
|
2
|
+
|
|
3
|
+
The package is split into thoroughly isolated layers so that new devices,
|
|
4
|
+
interfaces, or protocols can be added without disturbing the others:
|
|
5
|
+
|
|
6
|
+
hpgl - parse HP-GL bytes into a Program and validate it offline.
|
|
7
|
+
devices - declarative device profiles plus the abstract Device base.
|
|
8
|
+
interface - the Transport abstraction and its RS-232 implementation.
|
|
9
|
+
status - ESC command builders, response parsers, status interpretation.
|
|
10
|
+
execution - planning a Program into safe chunks and feeding the device.
|
|
11
|
+
|
|
12
|
+
See DESIGN.md at the repository root for the rationale behind each layer.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from .version import __version__
|
|
16
|
+
|
|
17
|
+
__all__ = ["__version__"]
|