# gRPC 控制流协议摘要(ControlService) ## 接口 - 服务:`control.v1.Control` - RPC:`ControlStream(stream ClientMessage) returns (stream ServerMessage)` - 双向长连接,客户端与控制端可任意时序发消息。 ## 消息结构(摘自 proto/control.proto) - `ClientMessage` oneof: - `Hello`:client_id、version、hostname、printers[] - `PrintResult`:request_id、ok、printer、message、retries_used - `Pong`:nonce(对应 Ping) - `PrinterUpdate`:printers[] - `ServerMessage` oneof: - `PrintInstruction`:request_id、pdf_data(bytes)、PrintParams - `Ping`:nonce - `PrintParams`:copies、duplex(one_sided/long_edge/short_edge)、color(auto/color/monochrome)、media、quality(draft/normal/high)、orientation(portrait/landscape)、job_name、printer(optional 指定目标打印机) - `PrinterInfo`:id、name、host、uri、state、accepting_jobs、color_supported、active_jobs、ppm、ppm_color、reasons[] ## 客户端行为(当前实现) - 建立流后立即发送 `Hello`,携带当前打印机快照。 - 后台按配置刷新(默认 10s,最小 5s)检测变更,发送 `PrinterUpdate`。 - 收到 `Ping{nonce}` 立即回 `Pong{nonce}`。 - 收到 `PrintInstruction`: - 将 pdf_data 落盘临时文件。 - 按调度策略(默认 least_queued,支持指定 printer;尊重彩色需求和可用性)分发到 CUPS。 - 成功/失败后返回 `PrintResult{ok, printer, message, retries_used}`。 - 连接出错自动指数退避重连(1s 起,最大 10s),控制端无需主动重连。 ## 字段语义补充 - `color_supported`:客户端基于 CUPS 选项判定,保守为 false 以避免误判。 - `state`:CUPS 状态字符串(Idle/Processing/Stopped 等)。 - `reasons`:CUPS 状态原因列表(offline/jam/paper-out 等)。 - `ppm`/`ppm_color`:若 CUPS 未提供则为缺省/0。 ## 控制端最小交互建议 1) 等待首条 `Hello`,登记客户端与打印机列表。 2) 定期/按需发送 `Ping` 保活。 3) 下发打印用 `PrintInstruction`(带 request_id 以关联结果)。 4) 接收 `PrintResult` 做业务回执;可订阅 `PrinterUpdate` 更新状态。 ## 开发/测试命令 - 生成/查看 proto:文件位于 `proto/control.proto`,由 `tonic-build` 在 `build.rs` 生成绑定。 - 本地 smoke(无控制端):`LIBCLANG_PATH=/usr/lib CONFIG_PATH=config/app.yaml cargo run --bin cups_smoke -- --list` / `--print -p `。 ## 原始 proto 文件 路径:`proto/control.proto` ``` syntax = "proto3"; package control.v1; service Control { rpc ControlStream(stream ClientMessage) returns (stream ServerMessage); } message ClientMessage { oneof msg { Hello hello = 1; PrintResult result = 2; Pong pong = 3; PrinterUpdate printers = 4; } } message ServerMessage { oneof msg { PrintInstruction print = 1; Ping ping = 2; } } message Hello { string client_id = 1; string version = 2; string hostname = 3; repeated PrinterInfo printers = 4; } message Ping { string nonce = 1; } message Pong { string nonce = 1; } message PrintInstruction { string request_id = 1; bytes pdf_data = 2; PrintParams params = 3; } message PrintParams { uint32 copies = 1; string duplex = 2; // one_sided, long_edge, short_edge string color = 3; // auto, color, monochrome string media = 4; string quality = 5; // draft, normal, high string orientation = 6; // portrait, landscape string job_name = 7; string printer = 8; } message PrintResult { string request_id = 1; bool ok = 2; string printer = 3; string message = 4; uint32 retries_used = 5; } message PrinterUpdate { repeated PrinterInfo printers = 1; } message PrinterInfo { string id = 1; string name = 2; string host = 3; string uri = 4; string state = 5; bool accepting_jobs = 6; bool color_supported = 7; uint32 active_jobs = 8; uint32 ppm = 9; uint32 ppm_color = 10; repeated string reasons = 11; } ```