Personal warehouse management system
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

211 lines
4.9 KiB

  1. package ql
  2. import (
  3. "errors"
  4. "image"
  5. "io"
  6. "os"
  7. "path/filepath"
  8. "syscall"
  9. "time"
  10. "unsafe"
  11. )
  12. // #include <linux/ioctl.h>
  13. import "C"
  14. // -----------------------------------------------------------------------------
  15. func _IOC(dir, typ, nr, size int) uintptr {
  16. return (uintptr(dir) << C._IOC_DIRSHIFT) |
  17. (uintptr(typ) << C._IOC_TYPESHIFT) |
  18. (uintptr(nr) << C._IOC_NRSHIFT) |
  19. (uintptr(size) << C._IOC_SIZESHIFT)
  20. }
  21. const (
  22. iocnrGetDeviceID = 1
  23. )
  24. // lpiocGetDeviceID reads the IEEE-1284 Device ID string of a printer.
  25. func lpiocGetDeviceID(fd uintptr) ([]byte, error) {
  26. var buf [1024]byte
  27. if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd,
  28. _IOC(C._IOC_READ, 'P', iocnrGetDeviceID, len(buf)),
  29. uintptr(unsafe.Pointer(&buf))); err != 0 {
  30. return nil, err
  31. }
  32. // In theory it might get trimmed along the way.
  33. length := int(buf[0])<<8 | int(buf[1])
  34. if 2+length > len(buf) {
  35. return buf[2:], errors.New("the device ID string got trimmed")
  36. }
  37. return buf[2 : 2+length], nil
  38. }
  39. // -----------------------------------------------------------------------------
  40. type Printer struct {
  41. File *os.File
  42. Manufacturer string
  43. Model string
  44. LastStatus *Status
  45. MediaInfo *MediaInfo
  46. // StatusNotify is called whenever we receive a status packet.
  47. StatusNotify func(*Status)
  48. }
  49. // Open finds and initializes the first USB printer found supporting
  50. // the appropriate protocol. Returns nil if no printer could be found.
  51. func Open() (*Printer, error) {
  52. // Linux usblp module, located in /drivers/usb/class/usblp.c
  53. paths, err := filepath.Glob("/dev/usb/lp[0-9]*")
  54. if err != nil {
  55. return nil, err
  56. }
  57. for _, candidate := range paths {
  58. f, err := os.OpenFile(candidate, os.O_RDWR, 0)
  59. if err != nil {
  60. continue
  61. }
  62. // Filter out obvious non-printers.
  63. deviceID, err := lpiocGetDeviceID(f.Fd())
  64. if err != nil {
  65. f.Close()
  66. continue
  67. }
  68. parsedID := parseIEEE1284DeviceID(deviceID)
  69. // Filter out printers that wouldn't understand the protocol.
  70. if !compatible(parsedID) {
  71. f.Close()
  72. continue
  73. }
  74. return &Printer{
  75. File: f,
  76. Manufacturer: parsedID.FindFirst("MANUFACTURER", "MFG"),
  77. Model: parsedID.FindFirst("MODEL", "MDL"),
  78. }, nil
  79. }
  80. return nil, nil
  81. }
  82. // Initialize initializes the printer for further operations.
  83. func (p *Printer) Initialize() error {
  84. // Clear the print buffer.
  85. invalidate := make([]byte, 400)
  86. if _, err := p.File.Write(invalidate); err != nil {
  87. return err
  88. }
  89. // Initialize.
  90. if _, err := p.File.WriteString("\x1b\x40"); err != nil {
  91. return err
  92. }
  93. // Flush any former responses in the printer's queue.
  94. //
  95. // I haven't checked if this is the kernel driver or the printer doing
  96. // the buffering that causes data to be returned at this point.
  97. var dummy [32]byte
  98. for {
  99. if _, err := p.File.Read(dummy[:]); err == io.EOF {
  100. break
  101. }
  102. }
  103. return nil
  104. }
  105. var errTimeout = errors.New("timeout")
  106. var errInvalidRead = errors.New("invalid read")
  107. func (p *Printer) updateStatus(status Status) {
  108. p.LastStatus = &status
  109. if p.StatusNotify != nil {
  110. p.StatusNotify(p.LastStatus)
  111. }
  112. }
  113. // pollStatusBytes waits for the printer to send a status packet and returns
  114. // it as raw data.
  115. func (p *Printer) pollStatusBytes(
  116. timeout time.Duration) (*Status, error) {
  117. start, buf := time.Now(), [32]byte{}
  118. for {
  119. if n, err := p.File.Read(buf[:]); err == io.EOF {
  120. time.Sleep(10 * time.Millisecond)
  121. } else if err != nil {
  122. return nil, err
  123. } else if n < 32 {
  124. return nil, errInvalidRead
  125. } else {
  126. p.updateStatus(Status(buf))
  127. return p.LastStatus, nil
  128. }
  129. if time.Now().Sub(start) > timeout {
  130. return nil, errTimeout
  131. }
  132. }
  133. }
  134. // Request new status information from the printer. The printer
  135. // must be in an appropriate mode, i.e. on-line and not currently printing.
  136. func (p *Printer) UpdateStatus() error {
  137. // Request status information.
  138. if _, err := p.File.WriteString("\x1b\x69\x53"); err != nil {
  139. return err
  140. }
  141. // Retrieve status information.
  142. if _, err := p.pollStatusBytes(time.Second); err != nil {
  143. p.LastStatus = nil
  144. return err
  145. }
  146. return nil
  147. }
  148. var errErrorOccurred = errors.New("error occurred")
  149. var errUnexpectedStatus = errors.New("unexpected status")
  150. var errUnknownMedia = errors.New("unknown media")
  151. func (p *Printer) Print(image image.Image) error {
  152. data := makePrintData(p.LastStatus, image)
  153. if data == nil {
  154. return errUnknownMedia
  155. }
  156. if _, err := p.File.Write(data); err != nil {
  157. return err
  158. }
  159. // See diagrams: we may receive an error status instead of the transition
  160. // to the printing state. Or even after it.
  161. //
  162. // Not sure how exactly cooling behaves and I don't want to test it.
  163. for {
  164. status, err := p.pollStatusBytes(10 * time.Second)
  165. if err != nil {
  166. return err
  167. }
  168. switch status.Type() {
  169. case StatusTypePhaseChange:
  170. // Nothing to do.
  171. case StatusTypePrintingCompleted:
  172. return nil
  173. case StatusTypeErrorOccurred:
  174. return errErrorOccurred
  175. default:
  176. return errUnexpectedStatus
  177. }
  178. }
  179. }
  180. // Close closes the underlying file.
  181. func (p *Printer) Close() error {
  182. return p.File.Close()
  183. }