# eams-print-server **Repository Path**: lssjzmn/eams-print-server ## Basic Information - **Project Name**: eams-print-server - **Description**: 企业资产全生命周期管理系统标签打印桌面服务工具 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-10-03 - **Last Updated**: 2026-11-01 ## Categories & Tags **Categories**: Uncategorized **Tags**: eams ## README # EAMS 打印工具(eams-print-server) 标签打印**客户端**(Windows 桌面程序,PySide6)。现场只需**输入资产编码 → 查询 → 预览 → 打印**:标签内容由 EAMS 后端下发,工具据此构造与 Web 端**逐字一致**的 TSC 指令,交给**本机 TSC PrintServer** 落地打印。 - 角色定位(2026-10-13 起):从「替代 TSC PrintServer 的服务端」改为「**打印客户端**」——**不再监听任何端口、不再接收 Web 端指令**,并让出 8888,因此可与官方 PrintServer **同时运行**。 - 内容来源(2026-11-01 起):**不再手输内容**。标题、右侧 4 项、二维码 9 项全部由 `POST /eams/open/print/asset` 下发; 右侧 4 项保留**可编辑**(现场临时改值),二维码 9 项**只读**(随二维码形态自动生成)。 - 依赖声明:**必须先安装并启动本机 TSC PrintServer**(官方 `TSC PrintServer`,默认 `ws://127.0.0.1:8888`)。没装/没启动时打印会失败(界面状态灯变灰、日志给出可操作提示);应急可切「直连打印机」通道(不依赖打印服务)。 - 版本:`app/__init__.py` 的 `__version__`(当前 **0.9.3**)。 --- ## 一、架构 ``` 界面 (PySide6) ← 只做:查编码取数 / 微调右侧 / 预览 / 探活 / 选择通道 │ LabelJob(标题 + 右侧 4 项 + 二维码 9 项 + 份数) ▼ app/webcompat.py ★ 唯一内容源:与 EAMS Web 端逐字一致的字段、版面算法、指令序列 │ WebBuild(commands = functions_inorder 片段 + WebLayout 最终坐标) ▼ app/jobprint.py ★ 流水线:内容 → 指令 → 通道(份数按"重复指令块"处理) ├── print_server(默认):app/print_client.py(WebSocket 客户端) │ └──► 本机 TSC PrintServer :8888 ──► 打印机(USB / 网络) └── direct(应急):指令 → app/backends/tspl.py 翻译成 TSPL 字节 └──► app/printer.py(Windows 驱动 RAW / 网络 9100)──► 打印机 ``` 设计要点: - **一个内容源、两条通道** ⇒ 经打印服务与直连印出来的内容是同一份;直连只是应急,不是另一套版面。 - **不碰打印机 I/O(默认通道)**:只有直连通道才用 `app/printer.py`。 - 版面坐标与界面预览、导出文件、干跑、自检**全部同源**(都用 `webcompat.build_label`)。 - 失败信息**可操作**:打印服务只回 `Function_Failed:函数名`,由 `print_client.function_failure_hint()` 翻成中文原因。 - **左右两栏各占 50%、不可拖动**(2026-11-01):分栏宽度只由拉伸比决定,与内容长短无关; 探活失败的长文案会换行显示(完整原文进 tooltip),不会把左栏挤没。 - **预览自适应**:位图按 1:1 生成后交给控件,`resizeEvent` 里等比缩放(上限 1:1),**任何窗口宽度下都不裁切**。 ## 二、核心功能 - **一键取数**:输入资产编码(回车或点「查询」)→ 调 `POST /eams/open/print/asset` → 自动填入标题、右侧 4 项、二维码 9 项; 流水号每次查询重新生成(换号请重新查询),日期优先取后端 `generatedDate`。 - **内容只读/可编辑的边界**:右侧 4 项(资产编码 / 资产名称 / 所属部门 / 使用人)**可编辑**; 二维码 9 项(编码/序列/名称/类型/部门/人员/原值/日期/流水)**只读**,随二维码形态自动生成。 右侧**前 2 项打「字段名 + 值」,后 2 项只打值**(即第 3 行是部门名、第 4 行是使用人)。 - **标题与网页同源**:取数响应带 `labelTitle`(后端参数 `label.title`),不再用工具本地默认标题。 - **打印通道**:经本地 TSC 打印服务(默认)/ 直连打印机(应急)。连接方式**只有两项**:**USB(免驱动,默认)** 与 **打印机列表(走 Windows 驱动)**。 - **USB 连接**:设备取自打印服务的 `usb_list`,下发 `openport_usb` + `USBPath` **原值**(Base64,空 = 由打印服务自动选第一台)——与 Web 端 `label.printer_mode=usb` 一致。 - **两级探活**:① 打印服务(`about`)② 设备列表(`usb_list`/`driver_list`)③ 打印机就绪(`openport` + 状态/名称/序列号 + `closeport`);启动即自动探活,探活顺带刷新 USB 设备下拉。 - **预览**:按 Web 版面渲染(1:1 坐标)后等比缩放显示;版面告警(静区不足、二维码被封顶等)写进日志区,不占界面。 - **空内容拦截**:内容全空时点「打印」只提示「请先输入资产编码并点『查询』」,不发起打印(避免白标签)。 - **直连应急通道**:指令翻译成 TSPL 后自己开打印机(**不支持 USB**,选 USB 会明确失败而不是偷偷走驱动)。 - **离线自检与干跑**:`tools/selftest_client.py`(8 项,含手算黄金样本逐字比对)、`tools/dryrun.py`(2 dpi × 2 换行矩阵)、`tools/fake_print_server.py`(本地假打印服务,只回包不打印)。 - **打包**:单文件 exe(`build.bat` → `dist/EamsPrintServer.exe`,无控制台、带图标)。 ## 三、与前端、后端的关系与约束 | 关系 | 事实 | 约束 | |---|---|---| | 工具 → **本机 TSC PrintServer** | WebSocket **客户端**,默认 `ws://127.0.0.1:8888`(`config.json → print.server_url`,界面可改) | 报文是**函数调用列表** `{"functions_inorder":[…]}`,收尾**纯文本** `Finished`;失败 `{"Function_Failed":fn}`;`openport_*` 可省略、`closeport` 恒在末尾;`USBPath` 原值回传 | | 工具 ⇄ 前端 | **零通信**(工具不再监听端口、不接 Web 端指令;「导入指令…」入口已于 0.6.0 移除) | 仅共享**指令契约**(`docs/PROTOCOL.md`),运行期互不影响 | | 工具 ⇄ 后端 | **只取数**:`POST /eams/open/print/asset` 按资产编码查询打印字段(**免鉴权 + 固定口令**),响应同时下发 `labelTitle / qrMode / qrBaseUrl / qrPassword`;打印仍不经后端 | 后端依旧不连打印服务、不碰打印机 IO;口令在 `config.json → api.password`,**必须**与后端 `eams.print.api-password` 一致 | | 内容口径同步 | 本工具 `app/webcompat.py` 是后端 `LabelPrintService.buildItem()` 的**逐条移植** | **改版面/内容/指令序列必须两侧同步**(对照文档:本目录 `docs/PROTOCOL.md` §3.1/§5);取数字段口径随 `AssetQrcodeVO` 走,与 Web 端同源 | | 内容口径 | 与 Web 端一致:右侧 **4 项 / 6 行**(前 2 项键值分行,后 2 项只打值:部门名 + 使用人)、二维码 **9 项**(「人员」= 使用人)、正文 `字段名:值`(**半角冒号**)、空值 `-`、金额两位小数、日期 `yyyy-MM-dd` | 口径源:`eams-ui/src/utils/qrcodeContent.ts` 与后端 `LINE_LABELS`;改一处要三处同步 | | 二维码形态(2026-10-31) | 由后端参数 `label.qr_mode` 决定,随取数接口下发:`simple`=写 9 项文字(默认)/ `api`=写 `/open/qr/text/{编码}?pwd=` 查询 URL / `html`=写 `/open/qr/html/{编码}?pwd=`(扫码得 HTML 详情页,含图片与最近 20 条履历) | 工具**不读参数表**、只认接口下发的 `qrMode/qrBaseUrl/qrPassword`;取不到/`baseUrl` 为空就回落 simple,保证离线也能打印 | | 标题口径(2026-11-01) | 标题 = 后端参数 `label.title`(随取数下发 `labelTitle`) | 工具本地默认值仅在离线/旧后端时兜底,正常应与网页标签标题一致 | | 两处**刻意**不同(用户口径) | 标题按**粗体**下发(`windowsfont` 的 `style=2`,注意 `1` 是斜体);`DIRECTION` **默认 1** 且只允许 `0/1` | 不要"对齐回 Web";已在 PROTOCOL §3.1 标注 | | 参数 | 工具用**自己的** `config.json`:`print` 段(通道/服务地址/超时/连接方式/`usb_path`)、`api` 段(后端 IP/端口/口令/超时)+ 每打印机 `profiles`(dpi/尺寸/浓度/速度/**方向**/字号/行距/二维码…) | 默认值与 Web 对齐但**独立维护**;现场改参数互不影响 | | 与官方服务共存 | 本工具**不占用** 8888 | 因此可与官方 `TSC PrintServer` 同时运行;Web 端的 `label.ws_url` 默认值也正好是它 | ## 四、开发与构建 ```bat python -m venv .venv .venv\Scripts\python.exe -m pip install -r requirements.txt run.bat :: 启动界面 .venv\Scripts\python.exe main.py --probe :: 只探活(打印服务 + 打印机) .venv\Scripts\python.exe main.py --print-once :: 按 config.json 打一张(无界面) .venv\Scripts\python.exe main.py --dry-run :: 干跑:导出 .json/.prn/.png .venv\Scripts\python.exe tools\selftest_client.py :: 客户端自检(8 项,离线) .venv\Scripts\python.exe tools\dryrun.py :: 干跑矩阵(离线) .venv\Scripts\python.exe tools\fake_print_server.py --port 8899 :: 假打印服务(联调) build.bat :: 打包 → dist\EamsPrintServer.exe ``` - Python **3.11**(64 位);依赖:`PySide6`、`Pillow`、`qrcode`、`websockets`、`pywin32`(仅直连通道需要)。 - 关键 CLI:`--server-url` 临时覆盖打印服务地址、`--print-once --direct` 走直连、`-v` 调试日志。 - 日志:程序同目录 `logs\console.log`(10MB 滚动、保留 5 个);窗口版 exe 无控制台时 stdout/stderr 也进这里。 ## 五、目录速查 ``` main.py 入口:依赖自检 / 界面 / --probe / --print-once / --dry-run app/ ├── webcompat.py ★ 唯一内容源:字段口径、版面算法、指令构造(移植自后端 buildItem) ├── print_client.py ★ 本机 TSC PrintServer 的 WebSocket 客户端(探活/发送/失败原因) ├── api_client.py ★ EAMS 后端取数客户端(`POST /open/print/asset`,标准库 urllib) ├── jobprint.py ★ 流水线:内容 → 指令 → 通道(两条通道共用一份指令) ├── config.py 配置读写(print 段、每打印机参数档、内容) ├── model.py 字段常量、格式化、参数档(LEGACY 键:label_mode/qr_style/command_set) ├── render.py Pillow 渲染:文本/二维码位图、Web 版面预览 ├── backends/tspl.py TSPL 后端:直连通道与导出用的翻译层(windowsfont→BITMAP 等) ├── printer.py 设备层(仅直连):驱动 RAW 直写 / 网络 9100 ├── protocol.py 指令契约常量 + 标签内容反解(界面入口已移除,保留作排查/回放) ├── ui.py / theme.py PySide6 界面与 QSS 主题 └── assets/icon.ico|png 默认图标(窗口/托盘/exe) tools/ dryrun / selftest_client / fake_print_server / ui_screenshot / make_icon docs/PROTOCOL.md ★ 指令契约(冻结版,改动前必读) ``` ## 六、文档索引 | 文档 | 内容 | |---|---| | `docs/PROTOCOL.md` | **指令契约冻结版**:传输与消息契约、函数映射、指令序列、版面算法、字段口径、文件索引 | | `CHANGELOG.md` | 变更记录(含角色反转、USB、粗体标题、DIRECTION 等口径变更与回退历史) | | `../eams-control/README.md` | 后端架构与标签指令产出口 | | `../eams-ui/README.md` | 前端架构与打印链路发起端 | | `../标签打印方案_TSC-PrintServer.md` | 标签打印方案(跨三者的总体设计) |