serial.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. package service
  2. import (
  3. "fmt"
  4. "os"
  5. "os/exec"
  6. "syscall"
  7. "unsafe"
  8. "github.com/tarm/serial"
  9. )
  10. // SerialPrinter 串口打印机
  11. type SerialPrinter struct {
  12. Port *serial.Port
  13. }
  14. func NewSerialPrinter(portName string, baudRate int) (*SerialPrinter, error) {
  15. if baudRate <= 0 {
  16. baudRate = 9600
  17. }
  18. if len(portName) < 3 || portName[:3] != "COM" {
  19. return nil, fmt.Errorf("非串口设备: %s", portName)
  20. }
  21. c := &serial.Config{Name: portName, Baud: baudRate}
  22. port, err := serial.OpenPort(c)
  23. if err != nil {
  24. return nil, fmt.Errorf("打开串口失败 %s: %w", portName, err)
  25. }
  26. return &SerialPrinter{Port: port}, nil
  27. }
  28. func (p *SerialPrinter) Write(data []byte) error {
  29. _, err := p.Port.Write(data)
  30. return err
  31. }
  32. func (p *SerialPrinter) Close() error {
  33. return p.Port.Close()
  34. }
  35. // === Windows RAW 打印机(WritePrinter API)===
  36. var (
  37. winspool = syscall.NewLazyDLL("winspool.drv")
  38. procOpenPrinter = winspool.NewProc("OpenPrinterW")
  39. procClosePrinter = winspool.NewProc("ClosePrinter")
  40. procStartDoc = winspool.NewProc("StartDocPrinterW")
  41. procStartPage = winspool.NewProc("StartPagePrinter")
  42. procWritePrinter = winspool.NewProc("WritePrinter")
  43. procEndPage = winspool.NewProc("EndPagePrinter")
  44. procEndDoc = winspool.NewProc("EndDocPrinter")
  45. )
  46. type PRINTER_DEFAULTS struct {
  47. pDatatype *uint16
  48. pDevMode uintptr
  49. DesiredAccess uint32
  50. }
  51. type DOC_INFO_1 struct {
  52. pDocName *uint16
  53. pOutputFile *uint16
  54. pDataType *uint16
  55. }
  56. func PrintRaw(printerName string, data []byte) error {
  57. namePtr, _ := syscall.UTF16PtrFromString(printerName)
  58. rawType, _ := syscall.UTF16PtrFromString("RAW")
  59. pd := PRINTER_DEFAULTS{
  60. pDatatype: rawType,
  61. pDevMode: 0,
  62. DesiredAccess: 0x000F0000 | 0x00000008, // PRINTER_ACCESS_USE | PRINTER_ACCESS_ADMINISTER
  63. }
  64. var handle syscall.Handle
  65. ret, _, _ := procOpenPrinter.Call(uintptr(unsafe.Pointer(namePtr)),
  66. uintptr(unsafe.Pointer(&handle)), uintptr(unsafe.Pointer(&pd)))
  67. if ret == 0 {
  68. return fmt.Errorf("OpenPrinter 失败: %s", printerName)
  69. }
  70. defer procClosePrinter.Call(uintptr(handle))
  71. docName, _ := syscall.UTF16PtrFromString("Parking Ticket")
  72. di := DOC_INFO_1{pDocName: docName, pDataType: rawType}
  73. procStartDoc.Call(uintptr(handle), 1, uintptr(unsafe.Pointer(&di)))
  74. procStartPage.Call(uintptr(handle))
  75. var written uint32
  76. procWritePrinter.Call(uintptr(handle), uintptr(unsafe.Pointer(&data[0])),
  77. uintptr(len(data)), uintptr(unsafe.Pointer(&written)))
  78. procEndPage.Call(uintptr(handle))
  79. procEndDoc.Call(uintptr(handle))
  80. return nil
  81. }
  82. // copyToPrinter 通过临时文件 + COPY /B 到打印机共享发送 RAW 数据
  83. func copyToPrinter(printerName string, data []byte) error {
  84. tmpFile, err := os.CreateTemp("", "tkt_*.bin")
  85. if err != nil {
  86. return err
  87. }
  88. tmpPath := tmpFile.Name()
  89. defer os.Remove(tmpPath)
  90. if _, err := tmpFile.Write(data); err != nil {
  91. tmpFile.Close()
  92. return err
  93. }
  94. tmpFile.Close()
  95. // 尝试 COPY /B file \\127.0.0.1\share_name
  96. // 先查共享名
  97. shareName := printerName
  98. for _, path := range []string{
  99. fmt.Sprintf("\\\\127.0.0.1\\%s", shareName),
  100. fmt.Sprintf("\\\\localhost\\%s", shareName),
  101. } {
  102. cmd := exec.Command("cmd", "/C", "copy", "/B", tmpPath, path)
  103. output, err := cmd.CombinedOutput()
  104. if err == nil {
  105. return nil
  106. }
  107. // 打印失败,尝试下一个
  108. _ = output
  109. }
  110. // 最后回退 WritePrinter API
  111. return PrintRaw(printerName, data)
  112. }
  113. var _ = os.Stderr