> ## Documentation Index
> Fetch the complete documentation index at: https://docs.plato.ae/llms.txt
> Use this file to discover all available pages before exploring further.

# Print Flow

> How jobs move from Plato POS to the thermal printer.

# Print Flow

The PC printer driver intentionally returns quickly from `/print`. Actual printer I/O happens in the background worker for the target printer.

## End-to-End Flow

1. Plato Console renders a receipt, ticket, or report to a PNG blob.
2. The Console `WIN_DRIVER` adapter converts the image to black/white raster rows.
3. Raster rows are split into chunks of 128 rows.
4. Each chunk is encoded as an ESC/POS raster command and base64 encoded.
5. Console sends `POST /print` with `ip_address`, `port`, and `chunks`.
6. The driver wraps chunks with small delays, appends feed/cut, and optionally adds beep commands.
7. The job is added to that printer's BullMQ queue.
8. The worker opens a TCP socket to the printer.
9. The worker sends ESC/POS initialize, then each chunk.
10. The worker closes the printer connection after the job.

## Console Chunking

The Console adapter uses:

| Setting               | Value        |
| --------------------- | ------------ |
| Driver type           | `WIN_DRIVER` |
| Raster chunk height   | `128` rows   |
| Status timeout        | `3000ms`     |
| Print request timeout | `5000ms`     |
| Cash drawer timeout   | `3000ms`     |

The browser sends already-encoded raster chunks. The driver does not render HTML.

## Driver Chunk Wrapping

`receipt-encoder.ts` turns incoming chunks into job chunks:

* Each incoming chunk is decoded from base64.
* Each print chunk gets `delayAfterMs: 25`.
* A feed and partial cut command is appended.
* If `beep` is true, 18 beep commands are appended.
* Each beep chunk has `delayAfterMs: 150`.

## ESC/POS Commands

| Command      | Bytes             | Purpose                              |
| ------------ | ----------------- | ------------------------------------ |
| Initialize   | `1B 40`           | Reset printer before each job.       |
| Raster image | `1D 76 30 00 ...` | Sent by Console chunks.              |
| Feed         | `0A 0A 0A 0A`     | Feed before cut.                     |
| Partial cut  | Encoder-generated | Partial paper cut.                   |
| Beep         | `1B 42 01 01`     | Beep command repeated by the driver. |
| Cash drawer  | `1B 70 00 19 FA`  | Kick drawer pulse.                   |

## Socket Lifecycle

The current worker opens and closes the printer connection for each job.

| Constant                  | Value  | Purpose                                        |
| ------------------------- | ------ | ---------------------------------------------- |
| `OPEN_ATTEMPT_TIMEOUT_MS` | `2000` | Max time to open TCP socket.                   |
| `CONNECTION_COOLDOWN_MS`  | `250`  | Minimum delay after close before another open. |
| `CLOSE_TIMEOUT_MS`        | `250`  | Max wait for graceful close before destroy.    |
| `WRITE_TIMEOUT_MS`        | `6000` | Max time for one socket write.                 |

There is also an idle-release timer of `3000ms`, but the current process path closes the connection immediately after each successful job.

## Failure Path

If opening or writing fails:

1. The printer is marked `offline`.
2. The socket is released/destroyed.
3. The BullMQ job fails.
4. BullMQ retries after `queue.retryDelayMs`.
5. When the printer is reached again, delayed retry jobs are promoted unless they are intentionally scheduled with a future `runAt`.

## Status Probing

`POST /status` can probe the printer only when queue work is not active. This avoids opening a probe socket while the same printer is busy printing.
