各包职责: - config: 分类 YAML 配置 - model: Device/IP/MAC 公共类型 - discovery: 扫描 + ARP MAC 解析 + Devices 变量 - sshclient: SSH 连接 - stress: stress-ng/stressapptest 压测 + Results 变量 - report: 80mm 热敏模板/sparkline/打印 + Printed 变量 - workflow: 永久循环调度 + Start/Stop workflow 已精简为 100 行纯编排,所有业务逻辑下沉各包
258 lines
5.9 KiB
Go
258 lines
5.9 KiB
Go
package report
|
||
|
||
import (
|
||
"bufio"
|
||
"fmt"
|
||
"io"
|
||
"net"
|
||
"os"
|
||
"os/exec"
|
||
"runtime"
|
||
"strings"
|
||
"time"
|
||
|
||
"auto-check/pkg/config"
|
||
)
|
||
|
||
// Printed 打印状态表(IP → 上次打印的状态),包级公开
|
||
// 用于"状态变化才重新打印"策略
|
||
var Printed = make(map[string]string)
|
||
|
||
// ============================
|
||
// Printer 接口 — 打印后端抽象
|
||
// ============================
|
||
|
||
// Printer 报告打印机
|
||
type Printer interface {
|
||
Print(text string) error
|
||
Close() error
|
||
}
|
||
|
||
// ============================
|
||
// 打印配置
|
||
// ============================
|
||
|
||
// DefaultPrintConfig 默认打印配置(80mm 热敏)
|
||
func DefaultPrintConfig() config.PrintConfig {
|
||
return config.PrintConfig{
|
||
Backend: "system",
|
||
Width: 42,
|
||
Charset: "utf-8",
|
||
}
|
||
}
|
||
|
||
// ============================
|
||
// ESC/POS 指令生成(USB 与网络后端共用)
|
||
// ============================
|
||
|
||
// writeEscpos 将文本转为 ESC/POS 指令流写入 writer
|
||
func writeEscpos(w io.Writer, text string) error {
|
||
bw := bufio.NewWriter(w)
|
||
|
||
// 初始化打印机
|
||
bw.WriteString("\x1b@") // ESC @ 复位
|
||
bw.WriteString("\x1b3\x18") // 行距 24点
|
||
bw.WriteString("\x1bM\x00") // 标准字体
|
||
bw.WriteString("\x1ba\x00") // 左对齐
|
||
|
||
// 文本内容(UTF-8 直发,打印机需支持)
|
||
for _, line := range strings.Split(text, "\n") {
|
||
bw.WriteString(line)
|
||
bw.WriteString("\n")
|
||
}
|
||
|
||
// 走纸 + 切纸
|
||
bw.WriteString("\n\n") // 尾部空行
|
||
bw.WriteString("\x1di") // 切纸(部分机型)
|
||
return bw.Flush()
|
||
}
|
||
|
||
// ============================
|
||
// 后端实现
|
||
// ============================
|
||
|
||
// SystemPrinter 系统打印后端(lp/lpr/print)
|
||
type SystemPrinter struct {
|
||
printerName string
|
||
command string
|
||
}
|
||
|
||
// NewSystemPrinter 创建系统打印机
|
||
// printerName 为空使用系统默认打印机
|
||
func NewSystemPrinter(printerName string) *SystemPrinter {
|
||
command := "lpr"
|
||
if runtime.GOOS == "windows" {
|
||
command = "print"
|
||
}
|
||
return &SystemPrinter{printerName: printerName, command: command}
|
||
}
|
||
|
||
// Print 发送文本到系统打印机
|
||
func (p *SystemPrinter) Print(text string) error {
|
||
var cmd *exec.Cmd
|
||
switch runtime.GOOS {
|
||
case "windows":
|
||
// 临时文件方式(print 命令需要文件)
|
||
tmp, err := os.CreateTemp("", "auto-check-*.txt")
|
||
if err != nil {
|
||
return err
|
||
}
|
||
name := tmp.Name()
|
||
if _, err := tmp.WriteString(text); err != nil {
|
||
tmp.Close()
|
||
return err
|
||
}
|
||
tmp.Close()
|
||
defer os.Remove(name)
|
||
|
||
if p.printerName != "" {
|
||
cmd = exec.Command("cmd", "/c", "print", "/D:"+p.printerName, name)
|
||
} else {
|
||
cmd = exec.Command("cmd", "/c", "print", name)
|
||
}
|
||
default:
|
||
args := []string{}
|
||
if p.printerName != "" {
|
||
args = append(args, "-P", p.printerName)
|
||
}
|
||
cmd = exec.Command(p.command, append(args, "-")...)
|
||
cmd.Stdin = strings.NewReader(text)
|
||
}
|
||
return cmd.Run()
|
||
}
|
||
|
||
// Close 系统打印无需关闭
|
||
func (p *SystemPrinter) Close() error { return nil }
|
||
|
||
// ============================
|
||
// ESCPOSPrinter — 网络热敏直连(9100)
|
||
// ============================
|
||
|
||
// ESCPOSPrinter 网络 ESC/POS 打印机
|
||
type ESCPOSPrinter struct {
|
||
conn net.Conn
|
||
}
|
||
|
||
// NewESCPOSPrinter 连接网络 ESC/POS 打印机
|
||
// address 格式: 192.168.1.100:9100
|
||
func NewESCPOSPrinter(address string) (*ESCPOSPrinter, error) {
|
||
conn, err := net.DialTimeout("tcp", address, 5*time.Second)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("连接打印机 %s 失败: %w", address, err)
|
||
}
|
||
return &ESCPOSPrinter{conn: conn}, nil
|
||
}
|
||
|
||
// Print 发送文本(自动转 ESC/POS 指令)
|
||
func (p *ESCPOSPrinter) Print(text string) error {
|
||
return writeEscpos(p.conn, text)
|
||
}
|
||
|
||
// Close 关闭连接
|
||
func (p *ESCPOSPrinter) Close() error {
|
||
if p.conn != nil {
|
||
return p.conn.Close()
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// ============================
|
||
// USBPrinter — USB 热敏直连
|
||
// ============================
|
||
|
||
// USBPrinter USB ESC/POS 打印机
|
||
// Windows: \\.\usb001(或打印机端口名)
|
||
// Linux: /dev/usb/lp0(或 lp1...)
|
||
type USBPrinter struct {
|
||
dev *os.File
|
||
}
|
||
|
||
// defaultUSBDevice 默认 USB 设备路径
|
||
func defaultUSBDevice() string {
|
||
if runtime.GOOS == "windows" {
|
||
return `\\.\usb001`
|
||
}
|
||
return "/dev/usb/lp0"
|
||
}
|
||
|
||
// NewUSBPrinter 打开 USB 打印机设备
|
||
// device 为空时自动探测(Linux /dev/usb/lp0,Windows \\.\usb001)
|
||
func NewUSBPrinter(device string) (*USBPrinter, error) {
|
||
if device == "" || device == "auto" {
|
||
device = defaultUSBDevice()
|
||
}
|
||
|
||
f, err := os.OpenFile(device, os.O_WRONLY, 0)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("打开 USB 打印机 %s 失败: %w", device, err)
|
||
}
|
||
return &USBPrinter{dev: f}, nil
|
||
}
|
||
|
||
// Print 发送文本(自动转 ESC/POS 指令)
|
||
func (p *USBPrinter) Print(text string) error {
|
||
return writeEscpos(p.dev, text)
|
||
}
|
||
|
||
// Close 关闭设备
|
||
func (p *USBPrinter) Close() error {
|
||
if p.dev != nil {
|
||
return p.dev.Close()
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// ============================
|
||
// FilePrinter — 文件后端(调试/重定向)
|
||
// ============================
|
||
|
||
// FilePrinter 文件打印后端
|
||
type FilePrinter struct {
|
||
path string
|
||
f *os.File
|
||
}
|
||
|
||
// NewFilePrinter 创建文件打印机
|
||
func NewFilePrinter(path string) (*FilePrinter, error) {
|
||
f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return &FilePrinter{path: path, f: f}, nil
|
||
}
|
||
|
||
// Print 追加写入文件
|
||
func (p *FilePrinter) Print(text string) error {
|
||
_, err := p.f.WriteString(text + "\n\n")
|
||
return err
|
||
}
|
||
|
||
// Close 关闭文件
|
||
func (p *FilePrinter) Close() error {
|
||
return p.f.Close()
|
||
}
|
||
|
||
// ============================
|
||
// 工厂
|
||
// ============================
|
||
|
||
// NewPrinter 按配置创建打印机
|
||
func NewPrinter(cfg config.PrintConfig) (Printer, error) {
|
||
if !cfg.Enabled {
|
||
return nil, nil
|
||
}
|
||
|
||
switch cfg.Backend {
|
||
case "system":
|
||
return NewSystemPrinter(cfg.Device), nil
|
||
case "escpos":
|
||
return NewESCPOSPrinter(cfg.Device)
|
||
case "usb":
|
||
return NewUSBPrinter(cfg.Device)
|
||
case "file":
|
||
return NewFilePrinter(cfg.Device)
|
||
default:
|
||
return nil, fmt.Errorf("未知打印后端: %s", cfg.Backend)
|
||
}
|
||
}
|