# 切片 6 — 硬件集成模块（xlyPlc）*（可选）*

本切片面向会接触印刷车间的人。如果你只读写元数据，可以跳过。

xly 是印刷行业 ERP。在车间，印刷机由 PLC（programmable logic controller，工业自动化控制器）控制。每台机器暴露一串状态：运行 / 停止 / 故障、当前速度、当前张数、当前工单号等。ERP 希望实时把这些记录到正确工单上。xly 的 **`xlyPlc`** Spring Boot 模块就是做这件事的桥。

本切片覆盖桥的架构、每机型 profile 机制，以及硬件专用代码与通用框架之间的边界。

## 记录对象

| | |
|---|---|
| **模块** | `xlyPlc`（代码库中的兄弟 Spring Boot 服务） |
| **方向** | 单向：PLC → ERP DB（不向印刷机回发命令） |
| **节奏** | 定时轮询（`PlcScheduledTasks` 中的 Spring `@Scheduled` cron，例如 `0/30 * * * * ?` 和 `0/1 * * * * ?`） |
| **每机型区分** | Spring profile（`-S10`、`-T0`、`-T1`、`-15S`、`-CT`、`-yt`、`-pro`） |
| **写入表** | `mftProduceReportMachineState`（dev 中约 207k 行）及相关机器状态从表 |

## 模块有多小

`xlyPlc/src/main/java/com/xly/` 只有五个文件：

| 文件 | 角色 |
|---|---|
| `PlcApplicationBoot.java` | Spring Boot 入口 |
| `web/scheduler/PlcScheduledTasks.java` | `@Component`，包含两个 `@Scheduled` cron 方法（每 30 秒和每 1 秒）驱动轮询循环 |
| `web/scheduler/PlcRunStatus.java` | 当前轮询周期的内存状态 |
| `web/scheduler/service/PlcToErpService.java` | 接口 |
| `web/scheduler/service/impl/PlcToErpServiceImpl.java` | 实现：读 PLC、写 DB |

这就是整个桥。每个机型的协议处理通过每 profile 的 `application-*.yml` 和可选原生串口代码（`xlyRxtx`，当前在 build 中禁用，见[维护人员参考：部署](../reference/maintainer/deployment.md)）引入的库完成。

## 每机型 profile

`xlyPlc/src/main/resources/` 中有 9 个 YAML 文件：默认配置加 8 个命名 profile：

```text
application.yml          （默认，选择 profile）
application-dev.yml      （开发配置）
application-S10.yml      （S10 机型）
application-T0.yml       （T0 机型）
application-T1.yml       （T1 机型）
application-15S.yml      （15S 机型）
application-CT.yml       （CT 机型）
application-yt.yml       （某客户命名 profile）
application-pro.yml      （生产）
```

某个 xlyPlc 实例启动时只激活一个 profile，例如 `-Dspring.profiles.active=S10`。profile 携带该机型的串口设置、轮询频率、字节协议参数和目标 ERP DB 连接。通常是每台印刷机一个 xlyPlc 部署，或每个机型 / 机器一个部署。这是运维问题，不是客户可见功能。

## 写入什么

轮询循环读取 PLC 寄存器，把它们转成 ERP 领域行，并写入：

- `mftProduceReportMachineState`：机器状态时间序列（运行 / 停止 / 故障），此 dev DB 中 207,112 行。
- 适用时写入相关 `mftProductionReport*` 从表。

行落库后，ERP 其余部分像处理普通数据一样处理：

- `viw_*` 视图把它们聚合进车间看板。
- 标准切片 1 风格模块渲染这些视图。
- 元数据层（切片 4 定制）允许客户给看板加字段，而不碰桥接代码。

## 框架 / 硬件边界

这是 xly 对复杂问题给出的最干净故事：

- **线以上（xlyEntry、xlyApi、所有元数据机制）：通用框架。** 不知道印刷机、PLC 或字节协议。
- **线以下（xlyPlc）：硬件专用。** 知道如何与机器通信。

二者只通过数据库通信。桥写行，框架读行。没有 RPC、没有共享进程内状态、没有回调。这使 xlyPlc：

- 可独立部署；多个客户把它运行在靠近印刷机的机器上，与中心 ERP 服务器分离。
- 可独立失败：桥崩溃时，框架继续在旧机器状态上运行；框架宕机时，桥继续写入，框架恢复后看到缓冲行。
- 难以在没有真实印刷机的情况下端到端测试。多数 CI 测试只能模拟 PLC 读取。

## 本切片引入的概念

本切片刻意作为*边界案例*，不引入新的框架概念。它用于说明：数据驱动运行时只处理框架问题；一旦行写入数据库，框架不关心它来自 PM 在**后台**手输、客户在**前台**提交，还是印刷机 PLC 通过 xlyPlc 推送。

## 本切片使用的参考

- [维护人员：多服务部署](../reference/maintainer/deployment.md)：`xlyPlc` 是可运行服务之一。部署章节需要补充车间部署说明（每机型一个桥或每机器一个桥）。

## 待验证项

> **Item 1 — 暂缓（超出仓库可验证范围）。** 字节协议本身来自各印刷机型厂商文档，不在 xly 源码树中。每个 `xlyPlc/src/main/resources/application-<model>.yml` 只携带**参数**（波特率、帧格式、寄存器地址、轮询调优项）；**协议语义**属于印刷机厂商知识。完整记录这些内容是部署运维工作，不是针对 src / DB / web 的 wiki 审计能完成的。

1. **线缆协议。** 每个机型有不同字节协议；每个 `application-<model>.yml` 携带参数。按机型记录协议是独立小众章节。
2. ~~**桥 → ERP DB 延迟 / 轮询间隔。**~~ **已关闭。** `PlcScheduledTasks.java` 中有两个 Spring `@Scheduled` cron 方法（未观察到 cron 字符串按 profile 变化）：`0/30 * * * * ?`（每 30 秒，74 行）和 `0/1 * * * * ?`（每 1 秒，105 行）。125 行还有一个已注释的第三个 cron（`0 */2 * * * ?`），当前休眠。每 profile 参数调优发生在轮询代码读取 `application-<model>.yml` 时，不在 cron 表达式本身。车间看板刷新独立于桥：它的 `viw_*` 聚合会在每次 FROUNT 请求时重读 `mftProduceReportMachineState`，所以印刷机发出状态后，看板最多约 30 秒能看到新行。
3. ~~**为什么 `xlyRxtx` 在 `settings.gradle` 中禁用。**~~ **已关闭。** `xly-src/settings.gradle` 的 git history 显示，`xlyRxtx` 最早在 commit `daf581311`（“1、添加串口功能 …”）中加入，用于串口能力。cleanup 分支把它注释掉，是源码裁剪的一部分：排除当前 dev DB 尚未覆盖的硬件模块。需要直接串口访问印刷机的部署可以重新启用 `settings.gradle` 中的 include。xlyPlc 本身不依赖 RXTX 也能运行；这里记录的机型依赖 TCP / Ethernet，只有串口型机型需要重新启用 RXTX。
