# Slice 6 — a hardware-integrated module (xlyPlc) *(optional)* This slice is for readers who'll touch the printing-press shop floor. If you only ever read or write metadata, skip it. xly is a printing-industry ERP. On the shop floor, printing presses run under PLC control (programmable logic controllers — industrial automation boxes). Each press exposes a stream of state — running / stopped / fault, current speed, current sheet count, current job number — that the ERP wants to record on the right work order in real time. xly's **`xlyPlc`** Spring Boot module is the bridge that does this. This slice covers the bridge's architecture, the per-press profile mechanism, and the boundary where hardware-specific code stops and the generic framework picks up. ## What we're documenting | | | |---|---| | **Module** | `xlyPlc` (a sibling Spring Boot service in the codebase) | | **Direction** | One-way: PLC → ERP DB (no commands sent back to the press) | | **Cadence** | Polled on a schedule (Quartz `PlcScheduledTasks`) | | **Per-press differentiation** | Spring profile (`-S10`, `-T0`, `-T1`, `-15S`, `-CT`, `-yt`, `-pro`) | | **Database tables it writes to** | `mftProduceReportMachineState` (~205k rows in dev), and related machine-state slaves | ## How small the module is `xlyPlc/src/main/java/com/xly/` has just five files: | File | Role | |---|---| | `PlcApplicationBoot.java` | Spring Boot entry point | | `web/scheduler/PlcScheduledTasks.java` | Quartz-driven poll loop | | `web/scheduler/PlcRunStatus.java` | In-memory state for the current poll cycle | | `web/scheduler/service/PlcToErpService.java` | Interface | | `web/scheduler/service/impl/PlcToErpServiceImpl.java` | The implementation: read PLC, write DB | That is the entire bridge. The protocol-handling for each press model is done by libraries pulled in via the per-profile `application-*.yml` and optional native serial-port code (`xlyRxtx`, currently disabled in the build — see [Maintainer Reference: deployment](../reference/maintainer/deployment.md)). ## Per-press profile Seven YAML profiles ship in `xlyPlc/src/main/resources/`: ``` application.yml (default — chooses a profile) application-dev.yml (developer config) application-S10.yml (S10-model presses) application-T0.yml (T0-model) application-T1.yml (T1-model) application-15S.yml (15S-model) application-CT.yml (CT-model) application-yt.yml (one customer's named profile) application-pro.yml (production) ``` A given xlyPlc instance is started with one profile active (e.g. `-Dspring.profiles.active=S10`). Each profile carries the press-model's serial-port settings, polling cadence, byte-protocol parameters, and the target ERP DB connection. *One xlyPlc deployment per press, or one per press-model per machine.* This is operational, not customer-facing. ## What it writes The poll loop reads PLC registers, transforms them into ERP-domain rows, and writes: - `mftProduceReportMachineState` — the time-series of machine status (running/stopped/fault), 204,678 rows in this dev DB. - Related `mftProductionReport*` slaves where applicable. Once the rows land, the rest of the ERP picks them up exactly like any other data: - `viw_*` views aggregate them into shop-floor dashboards. - Standard Slice-1-style modules render those views. - The metadata layer (Slice 4 customization) lets a customer add fields to the dashboards without touching the bridge. ## The framework / hardware boundary This is the cleanest story xly tells about an awkward problem: - **Above the line (xlyEntry, xlyApi, all the metadata machinery): generic framework.** No knowledge of presses, PLCs, byte protocols. - **Below the line (xlyPlc): hardware-specific.** Knows how to talk to a press. The two communicate only through the database. The bridge writes rows; the framework reads rows. There's no RPC, no shared in-process state, no callback. This makes xlyPlc: - Independently deployable (and several customers run it on a machine next to the press, separate from the central ERP server). - Independently failable: if the bridge crashes, the framework keeps running on stale machine-state data. If the framework is down, the bridge keeps writing — when the framework comes back, it sees the buffered rows. - Hard to test end-to-end without an actual press. Most CI tests stub the PLC reads. ## Concepts this slice introduces This slice is intentionally a *boundary case* and doesn't introduce new framework concepts. It exists to show readers that the data-driven runtime is *only* the framework's problem; once a row has been written to the database, the framework treats it the same regardless of whether a PM typed it into BACK, a customer submitted it from FROUNT, or a press's PLC pushed it through xlyPlc. ## Reference entries this slice exercises - [Maintainer: multi-service deployment](../reference/maintainer/deployment.md) — `xlyPlc` is one of the runnable services. The deployment chapter needs a section on shop-floor deployment (one bridge per press model). ## Open verification items 1. **The wire protocol.** Each press model has a different byte protocol; each `application-.yml` carries the parameters. Documenting the protocol per model is a separate, niche chapter that this wiki may or may not need. 2. **Bridge → ERP-DB latency.** What's the polling interval per profile, and how does that interact with shop-floor dashboard refresh? Operational concern, worth a paragraph. 3. **Why `xlyRxtx` is disabled in `settings.gradle`.** RXTX is the native serial-port library; the build excludes it. Understanding whether xlyPlc currently runs without serial support, or whether it expects serial only on certain deployments, is worth confirming.