# Printer installer map and install endpoints How the shop-floor fleet installs network printers from shopdb-flask, replacing the classic ASP `apiprinters.asp` / `apipcdefaultprinter.asp` / `installprinter.asp` contract. Shopfloor 2.0 PCs cannot run unsigned `.bat` maps, so a signed installer EXE (and the public web map page) drives installs from three endpoints in the printers plugin. - Server code: `plugins/printers/api/asset_routes.py` (`printer_install_list`, `pc_default_printer`, `printer_install_batch`) - Consumed as a fleet manifest entry: the `common` scope's `printer map` entry (see `GE-ENFORCE-DISPLAY.md`). All three endpoints are `@jwt_required(optional=True)`: an anonymous fleet client works, and a logged-in browser (the public map page) works too. --- ## 1. The public map page `PrinterInstallerMap` is a public (no-login) frontend page: the floor map with printer hotspots positioned at each printer's `mapx` / `mapy`. The user clicks the printers they want, and the page requests an install batch. The PC's default printer is preselected via `pc-default`. --- ## 2. `GET /api/printers/install-list` Flat, unpaginated list of active NETWORK printers. A printer counts as network only if it has a hostname or a non-USB IP; USB-only printers are excluded. Fields per row: | Field | Notes | |---|---| | `printerid` | Printer id (the token `install-batch` takes). | | `name` | Asset name, else asset number. | | `machinenumber` | The asset number. | | `windowsname` | Standardized Windows printer name. | | `sharename` | Share / CSF name. | | `hostname` | Print-queue host. | | `ipaddress` | Primary IP (falls back to any communication row). | | `vendorname` | Direct vendor, else the model's vendor. | | `modelnumber` | Model name. | | `installpath` | Installer path for this printer (see install-batch). | | `iscsf` | CSF flag. | | `locationname` | Location name, if the asset has one. | | `mapx` / `mapy` | Floor-map hotspot position, in the native pixels of `levelid`. | | `levelid` | Which level (drawing) those pixels belong to. Null when unplaced. | `?format=text` returns a pipe-delimited line per printer, one printer per line, with a fixed field order so the Inno / Pascal installer does a `split()` instead of parsing JSON: ``` printerid|windowsname|vendorname|modelnumber|hostname|ipaddress|mapx|mapy|levelid ``` Any pipe or newline inside a value is neutralized to a space so the field count stays fixed. The web map uses the default JSON. **Fields 0-7 are frozen.** The shipped installer reads them positionally (`GetField(Line, 6)` is mapx), so a new field goes on the END and nowhere else - inserting one shifts every later field and the installer keeps running while reading a model number as a coordinate. `levelid` is field 8 for that reason, and installers built before levels existed ignore it. An installer that ignores `levelid` draws every printer on whichever single blueprint it ships, which is correct only while a site has one level. See ADR-017 and section 6. --- ## 3. `GET /api/printers/pc-default?machine=NNNN` The PC's default printer, by machine (asset) number persisted at PXE enrollment. Parity with classic `apipcdefaultprinter.asp`: the installer preselects a PC's default-printer hotspot on the site-map wizard. The link is resolved through the `defaultprinter` asset relationship (PC asset -> printer asset), so it stays inside the contract surface (no cross-plugin model import). Returns `{printerid, windowsname}`, or `{}` when the machine is unknown or has no active default printer set. `?format=text` returns one pipe-delimited line (`printerid|windowsname`), or an EMPTY body when there is no default (so the installer's split yields nothing). --- ## 4. `GET /api/printers/install-batch?printerids=1,2,3` Returns a self-deleting Windows `.bat` that installs the selected printers, grouped the same way classic `installprinter.asp` grouped them: - HP / Xerox: one universal `PrinterInstaller.exe /PRINTER="a,b,c"` call. - Printers with a `.exe` `installpath`: a PowerShell `Invoke-WebRequest` download (using the caller's Windows credentials, against the site base URL + the IIS-served `/installers` folder) followed by running it `/SILENT`. - No `installpath`, or a non-`.exe` payload (e.g. a `.zip`): listed as a manual install rather than run blindly. `printerids` is required, comma-separated; non-numeric tokens are ignored. An empty / missing list is a validation error. The install name preference is `windowsname`, else `sharename`, else the asset name / number. --- ## 5. Fleet wiring The `common` scope's `printer map` manifest entry (see `GE-ENFORCE-DISPLAY.md`) lays down the signed installer that consumes these endpoints. The web map page covers the same install flow for a human at a browser. --- ## 6. The shipped installer predates levels (known gap) The Pascal installer in the `inno` repo (`PrinterInstallerMap`) still assumes one drawing per site, in two places that must change together: - `MAP_SOURCE_W = 3300` / `MAP_SOURCE_H = 2550` are compiled-in constants, and every hotspot is scaled by them. They are the dimensions of ONE level. - A downsized copy of that level's blueprint is bundled into the installer (`880x680`), so the picture is fixed at build time. Two consequences, neither of which the installer can detect: 1. It ignores field 8, so printers on any level are drawn on the bundled image. Coordinates from a different drawing land somewhere plausible and wrong. 2. When a level's blueprint is replaced with one of different dimensions, the constants and the bundled image are both stale and every hotspot shifts. The API side is ready: `GET /api/maplevels` is deliberately unauthenticated so the installer can fetch level dimensions and blueprint URLs at runtime rather than compiling them in, which is what fixes both. Until the installer is rebuilt against it, treat its map as accurate for the default level only.