Metadata-Version: 2.4
Name: hpgl-buddy
Version: 1.0.0
Summary: Carefree, observable plotting of HP-GL files on HP pen plotters over RS-232.
Author-email: Pavel Kim <hello@pavelkim.com>
License: MIT
Project-URL: Homepage, https://github.com/hpgl-buddy/hpgl-buddy
Project-URL: Repository, https://github.com/hpgl-buddy/hpgl-buddy
Project-URL: Issues, https://github.com/hpgl-buddy/hpgl-buddy/issues
Keywords: hpgl,plotter,hp7475a,rs232,pen-plotter
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: End Users/Desktop
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Printing
Classifier: Topic :: System :: Hardware :: Hardware Drivers
Requires-Python: >=3.13
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: pyserial>=3.5
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == "dev"
Dynamic: license-file

# hpgl-buddy

Carefree, observable plotting of HP-GL files on HP pen plotters over RS-232.

It does not just shove a file at the plotter: it validates the file, splits it to fit
the device buffer, feeds it so the buffer never overflows and an inked pen never stalls
mid-stroke, watches the device for faults the whole time, and logs every exchange so a
run can be understood and troubleshooted from the log alone.

**Supported device:** HP 7475A (RS-232). New devices are added as declarative profiles
(see [Extending](#extending)); HP-IB is planned.

---

## Install

Requires Python 3.13 and a USB-serial adapter (macOS: use the `/dev/cu.*` device).

```bash
pip install -e .          # editable, for development
# or build a wheel:
python -m build --wheel   # -> dist/hpgl_buddy-*.whl  (or: tox -e build)
```

`pyserial` is the only runtime dependency.

---

## Quick start

```bash
# 1. Validate a file offline (no plotter needed)
hpgl-buddy check drawing.hpgl

# 2. Check the plotter is alive and interpret its status
hpgl-buddy status --port /dev/cu.usbserial-XXXX

# 3. Plot it (verbose shows every byte/ESC exchange)
hpgl-buddy -v plot drawing.hpgl --port /dev/cu.usbserial-XXXX

# 4. Generate and plot a built-in demo
hpgl-buddy demo --pens 6 --out demo.hpgl
hpgl-buddy plot demo.hpgl --port /dev/cu.usbserial-XXXX
```

Serial defaults: **9600 8N1, no flow control** (configurable). Flow control is off by
default because XON/XOFF corrupted the exchange on the on-site adapter; enable it with
`--xonxoff` if your cabling needs it.

---

## Commands

| Command | What it does |
|---|---|
| `check FILE` | Offline HP-GL syntax check. No device. Exit non-zero on errors. |
| `status` | Ad-hoc healthcheck: identification, buffer, status byte, errors, limits - all interpreted. |
| `plot FILE` | Safe, buffer-aware plotting with progress + end-of-run report. |
| `monitor on\|off\|watch` | Switch monitor mode (computer port) or stream the echoed bytes (terminal port). |
| `demo` | Generate demo HP-GL (`--scene card` shapes/fills/labels/colours, or `--scene house` a continuous one-line drawing). |

Global: `-v/--verbose` (DEBUG, incl. raw ASCII+hex wire dumps), `--version`.

Serial options (on `status`/`plot`/`monitor`): `--port`, `--model` (default `hp7475a`),
`--baud`, `--framing` (e.g. `8N1`), `--timeout`, `--xonxoff`, `--rtscts`.

### plot options

- `--on-error abort|prompt|continue` - on a reported error: stop and park the pen
  (default), ask interactively, or auto-recover (`ESC.K` discard -> `IN` -> replay the
  tracked state preamble -> carry on).
- `--live-hpgl-verify off|chunk|pu` - optional on-device HP-GL error checking (see
  [Verification](#verification)). Default `off`.
- `--ignore-syntax-errors` - plot even if the offline check found errors.
- `--stats-json PATH` - write run statistics as JSON (`-` for stdout).

### monitor (two ports)

The 7475A enables monitor mode via an ESC sequence on the **computer** (data) port, then
echoes received bytes out a separate **terminal** port. So:

```bash
# stream the echo on the terminal port, enabling monitor on the computer port first
hpgl-buddy monitor watch --port /dev/cu.TERMINAL --command-port /dev/cu.COMPUTER --enable
```

`watch` prints every byte as binary, hex, decimal, and ASCII/control-name.

---

## How plotting works

```
file -> parse -> offline syntax check -> plan into chunks -> stream to device -> report
```

1. **Parse** the bytes into instructions, each tagged with its source line and sequence
   index (so any error names the exact command).
2. **Syntax check** offline; `plot` refuses a file with errors unless `--ignore-syntax-errors`.
3. **Plan** into chunks of <=256 bytes, split only at instruction boundaries (never inside
   a command), each tagged whether it ends with the pen up.
4. **Stream**: a chunk is sent only when `ESC.B` reports enough free buffer space. This one
   gate both prevents overflow and keeps the buffer fed, so a long pen-down stroke spanning
   several chunks never underruns (an underrun would park an inked pen and blot).
5. **Watch** (always on, after each chunk): `ESC.E` for I/O faults (overflow / framing /
   data loss) and `ESC.O` for environmental faults (paper lever or pinch wheels raised ->
   abort; VIEW pressed -> warn). Both are immediate and never stall the pen.
6. **Confirm**: a final `OS;OE;OI;` tailgate waits for the pen to physically finish and
   reports the end status / any HP-GL error.
7. **Report** instructions/chunks/bytes sent, elapsed time, recovered errors, and warnings
   (and the same as JSON via `--stats-json`).

### Verification

HP-GL/syntax errors are a property of the *file* (already validated offline), so on-device
HP-GL error checking is **optional** and off by default. When enabled it never stalls the
pen - it uses a *one-deep* tailgate: the `OS;OE;OI;` query is prefixed to the chunk after a
pen-up, so its verdict reports the *previous* chunk while the current one is already drawing.

- `chunk` - check at pen-up chunk boundaries.
- `pu` - break a chunk at every pen-up so each completed stroke is checked (more, smaller
  chunks).

Either way you learn a chunk is clean within ~1 chunk, and a reported error names the span
of chunks it could belong to with all candidate instructions.

### Limits to know

- **No pen sensing on the 7475A**: a missing or fallen pen plots dry and is *not* detectable
  by any status query. Pre-load the pens your file uses. (`plot` warns about this.)

---

## Architecture

Thoroughly separated layers, each replaceable on its own:

```
hpgl/       parse HP-GL bytes into a Program; offline syntax check
devices/    declarative TOML profiles + abstract Device base (registry)
interface/  Transport abstraction + pyserial RS-232 implementation
status/     ESC + HP-GL command builders, response parsers, status interpretation, monitor
execution/  planner (Program -> chunks) + flow control + executor + progress/report
demo/       demo HP-GL generators
```

The authoritative design rationale (with HP manual citations) lives in `DESIGN.md`; the
running decision log is the "Follow-up steering" section of `TASK-1-BASIC-IMPLEMENTATION.md`.

### Extending

Add a simple device by dropping a `<model>.toml` into `devices/profiles/` (buffer size,
pen count, serial defaults, capabilities, `pen_sensing`). A device with unusual behavior
can subclass `Device` and register it. The interface layer is transport-agnostic, so HP-IB
can be added as another `Transport` without touching the rest.

---

## Development

```bash
tox            # run the test suite (Python 3.13)
tox -e build   # build the wheel
```

Dependencies are managed two-file style: `requirements-rough.txt` (hand-maintained,
loose top-level) is frozen into a fully-pinned `requirements.txt` by the
`dependencies_update` GitHub Actions workflow. A `Dockerfile` provides a reproducible
build/test image (not for serial I/O - hardware is not reachable from a container).

Conventions: extensive logging (no `print`), ASCII-only output, descriptive names, and
errors that state what happened, where, and why.
