[FIX] hw_escpos: Backport driver robustness
This commit is contained in:
parent
2067a206ec
commit
3cf45e1111
|
@ -16,6 +16,14 @@ import pickle
|
|||
import re
|
||||
import subprocess
|
||||
import traceback
|
||||
|
||||
try:
|
||||
from .. escpos import *
|
||||
from .. escpos.exceptions import *
|
||||
from .. escpos.printer import Usb
|
||||
except ImportError:
|
||||
escpos = printer = None
|
||||
|
||||
from threading import Thread, Lock
|
||||
from Queue import Queue, Empty
|
||||
|
||||
|
@ -24,13 +32,6 @@ try:
|
|||
except ImportError:
|
||||
usb = None
|
||||
|
||||
try:
|
||||
from .. import escpos
|
||||
from ..escpos import printer
|
||||
from ..escpos import supported_devices
|
||||
except ImportError:
|
||||
escpos = printer = None
|
||||
|
||||
from PIL import Image
|
||||
|
||||
from openerp import http
|
||||
|
@ -110,24 +111,19 @@ class EscposDriver(Thread):
|
|||
self.start()
|
||||
|
||||
def get_escpos_printer(self):
|
||||
try:
|
||||
printers = self.connected_usb_devices()
|
||||
if len(printers) > 0:
|
||||
self.set_status('connected','Connected to '+printers[0]['name'])
|
||||
return escpos.printer.Usb(printers[0]['vendor'], printers[0]['product'])
|
||||
else:
|
||||
self.set_status('disconnected','Printer Not Found')
|
||||
return None
|
||||
except Exception as e:
|
||||
self.set_status('error',str(e))
|
||||
|
||||
printers = self.connected_usb_devices()
|
||||
if len(printers) > 0:
|
||||
self.set_status('connected','Connected to '+printers[0]['name'])
|
||||
return Usb(printers[0]['vendor'], printers[0]['product'])
|
||||
else:
|
||||
self.set_status('disconnected','Printer Not Found')
|
||||
return None
|
||||
|
||||
def get_status(self):
|
||||
self.push_task('status')
|
||||
return self.status
|
||||
|
||||
|
||||
|
||||
def open_cashbox(self,printer):
|
||||
printer.cashdraw(2)
|
||||
printer.cashdraw(5)
|
||||
|
@ -150,11 +146,13 @@ class EscposDriver(Thread):
|
|||
_logger.warning('ESC/POS Device Disconnected: '+message)
|
||||
|
||||
def run(self):
|
||||
|
||||
if not escpos:
|
||||
_logger.error('ESC/POS cannot initialize, please verify system dependencies.')
|
||||
return
|
||||
while True:
|
||||
try:
|
||||
error = True
|
||||
timestamp, task, data = self.queue.get(True)
|
||||
|
||||
printer = self.get_escpos_printer()
|
||||
|
@ -162,6 +160,7 @@ class EscposDriver(Thread):
|
|||
if printer == None:
|
||||
if task != 'status':
|
||||
self.queue.put((timestamp,task,data))
|
||||
error = False
|
||||
time.sleep(5)
|
||||
continue
|
||||
elif task == 'receipt':
|
||||
|
@ -178,11 +177,25 @@ class EscposDriver(Thread):
|
|||
self.print_status(printer)
|
||||
elif task == 'status':
|
||||
pass
|
||||
error = False
|
||||
|
||||
except NoDeviceError as e:
|
||||
print "No device found %s" %str(e)
|
||||
except HandleDeviceError as e:
|
||||
print "Impossible to handle the device due to previous error %s" % str(e)
|
||||
except TicketNotPrinted as e:
|
||||
print "The ticket does not seems to have been fully printed %s" % str(e)
|
||||
except NoStatusError as e:
|
||||
print "Impossible to get the status of the printer %s" % str(e)
|
||||
except Exception as e:
|
||||
self.set_status('error', str(e))
|
||||
errmsg = str(e) + '\n' + '-'*60+'\n' + traceback.format_exc() + '-'*60 + '\n'
|
||||
_logger.error(errmsg);
|
||||
finally:
|
||||
if error:
|
||||
self.queue.put((timestamp, task, data))
|
||||
if printer:
|
||||
printer.close()
|
||||
|
||||
def push_task(self,task, data = None):
|
||||
self.lockedstart()
|
||||
|
|
|
@ -8,6 +8,13 @@ CTL_FF = '\x0c' # Form feed
|
|||
CTL_CR = '\x0d' # Carriage return
|
||||
CTL_HT = '\x09' # Horizontal tab
|
||||
CTL_VT = '\x0b' # Vertical tab
|
||||
|
||||
# RT Status commands
|
||||
DLE_EOT_PRINTER = '\x10\x04\x01' # Transmit printer status
|
||||
DLE_EOT_OFFLINE = '\x10\x04\x02'
|
||||
DLE_EOT_ERROR = '\x10\x04\x03'
|
||||
DLE_EOT_PAPER = '\x10\x04\x04'
|
||||
|
||||
# Printer hardware
|
||||
HW_INIT = '\x1b\x40' # Clear data in buffer and reset modes
|
||||
HW_SELECT = '\x1b\x3d\x01' # Printer select
|
||||
|
|
|
@ -1,13 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
@author: Manuel F Martinez <manpaz@bashlinux.com>
|
||||
@organization: Bashlinux
|
||||
@copyright: Copyright (c) 2012 Bashlinux
|
||||
@license: GPL
|
||||
'''
|
||||
|
||||
|
||||
import logging
|
||||
import time
|
||||
import copy
|
||||
import io
|
||||
|
@ -21,19 +13,15 @@ import xml.dom.minidom as minidom
|
|||
|
||||
from PIL import Image
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
import jcconv
|
||||
except ImportError:
|
||||
jcconv = None
|
||||
_logger.warning('ESC/POS: please install jcconv for improved Japanese receipt printing:\n # pip install jcconv')
|
||||
|
||||
try:
|
||||
import qrcode
|
||||
except ImportError:
|
||||
qrcode = None
|
||||
_logger.warning('ESC/POS: please install the qrcode python module for qrcode printing in point of sale receipts:\n # pip install qrcode')
|
||||
|
||||
from constants import *
|
||||
from exceptions import *
|
||||
|
@ -107,14 +95,14 @@ class StyleStack:
|
|||
'off': TXT_BOLD_OFF,
|
||||
'on': TXT_BOLD_ON,
|
||||
# must be issued after 'size' command
|
||||
# because ESC ! resets ESC E
|
||||
# because ESC ! resets ESC -
|
||||
'_order': 10,
|
||||
},
|
||||
'font': {
|
||||
'a': TXT_FONT_A,
|
||||
'b': TXT_FONT_B,
|
||||
# must be issued after 'size' command
|
||||
# because ESC ! resets ESC M
|
||||
# because ESC ! resets ESC -
|
||||
'_order': 10,
|
||||
},
|
||||
'size': {
|
||||
|
@ -122,7 +110,7 @@ class StyleStack:
|
|||
'double-height': TXT_2HEIGHT,
|
||||
'double-width': TXT_2WIDTH,
|
||||
'double': TXT_DOUBLE,
|
||||
'_order': 1,
|
||||
'_order': 1,
|
||||
},
|
||||
'color': {
|
||||
'black': TXT_COLOR_BLACK,
|
||||
|
@ -181,9 +169,8 @@ class StyleStack:
|
|||
def to_escpos(self):
|
||||
""" converts the current style to an escpos command string """
|
||||
cmd = ''
|
||||
# Sort commands because some commands affect others (see _order attributes above)
|
||||
ordered_cmds = self.cmds.keys()
|
||||
ordered_cmds.sort(lambda x, y: cmp(self.cmds[x]['_order'], self.cmds[y]['_order']))
|
||||
ordered_cmds.sort(lambda x,y: cmp(self.cmds[x]['_order'], self.cmds[y]['_order']))
|
||||
for style in ordered_cmds:
|
||||
cmd += self.cmds[style][self.get(style)]
|
||||
return cmd
|
||||
|
|
|
@ -78,3 +78,39 @@ class CashDrawerError(Error):
|
|||
|
||||
def __str__(self):
|
||||
return "Valid pin must be set to send pulse"
|
||||
|
||||
class NoStatusError(Error):
|
||||
def __init__(self, msg=""):
|
||||
Error.__init__(self, msg)
|
||||
self.msg = msg
|
||||
self.resultcode = 70
|
||||
|
||||
def __str__(self):
|
||||
return "Impossible to get status from the printer"
|
||||
|
||||
class TicketNotPrinted(Error):
|
||||
def __init__(self, msg=""):
|
||||
Error.__init__(self, msg)
|
||||
self.msg = msg
|
||||
self.resultcode = 80
|
||||
|
||||
def __str__(self):
|
||||
return "A part of the ticket was not been printed"
|
||||
|
||||
class NoDeviceError(Error):
|
||||
def __init__(self, msg=""):
|
||||
Error.__init__(self, msg)
|
||||
self.msg = msg
|
||||
self.resultcode = 90
|
||||
|
||||
def __str__(self):
|
||||
return "Impossible to find the printer Device"
|
||||
|
||||
class HandleDeviceError(Error):
|
||||
def __init__(self, msg=""):
|
||||
Error.__init__(self, msg)
|
||||
self.msg = msg
|
||||
self.resultcode = 100
|
||||
|
||||
def __str__(self):
|
||||
return "Impossible to handle device"
|
||||
|
|
|
@ -1,10 +1,4 @@
|
|||
#!/usr/bin/python
|
||||
'''
|
||||
@author: Manuel F Martinez <manpaz@bashlinux.com>
|
||||
@organization: Bashlinux
|
||||
@copyright: Copyright (c) 2012 Bashlinux
|
||||
@license: GPL
|
||||
'''
|
||||
|
||||
import usb.core
|
||||
import usb.util
|
||||
|
@ -14,6 +8,7 @@ import socket
|
|||
from escpos import *
|
||||
from constants import *
|
||||
from exceptions import *
|
||||
from time import sleep
|
||||
|
||||
class Usb(Escpos):
|
||||
""" Define USB printer """
|
||||
|
@ -26,6 +21,9 @@ class Usb(Escpos):
|
|||
@param in_ep : Input end point
|
||||
@param out_ep : Output end point
|
||||
"""
|
||||
|
||||
self.errorText = "ERROR PRINTER\n\n\n\n\n\n"+PAPER_FULL_CUT
|
||||
|
||||
self.idVendor = idVendor
|
||||
self.idProduct = idProduct
|
||||
self.interface = interface
|
||||
|
@ -33,35 +31,102 @@ class Usb(Escpos):
|
|||
self.out_ep = out_ep
|
||||
self.open()
|
||||
|
||||
|
||||
def open(self):
|
||||
""" Search device on USB tree and set is as escpos device """
|
||||
|
||||
self.device = usb.core.find(idVendor=self.idVendor, idProduct=self.idProduct)
|
||||
if self.device is None:
|
||||
print "Cable isn't plugged in"
|
||||
|
||||
if self.device.is_kernel_driver_active(0):
|
||||
try:
|
||||
self.device.detach_kernel_driver(0)
|
||||
except usb.core.USBError as e:
|
||||
print "Could not detatch kernel driver: %s" % str(e)
|
||||
|
||||
raise NoDeviceError()
|
||||
try:
|
||||
if self.device.is_kernel_driver_active(self.interface):
|
||||
self.device.detach_kernel_driver(self.interface)
|
||||
self.device.set_configuration()
|
||||
self.device.reset()
|
||||
usb.util.claim_interface(self.device, self.interface)
|
||||
except usb.core.USBError as e:
|
||||
print "Could not set configuration: %s" % str(e)
|
||||
raise HandleDeviceError(e)
|
||||
|
||||
def close(self):
|
||||
i = 0
|
||||
while True:
|
||||
try:
|
||||
if not self.device.is_kernel_driver_active(self.interface):
|
||||
usb.util.release_interface(self.device, self.interface)
|
||||
self.device.attach_kernel_driver(self.interface)
|
||||
usb.util.dispose_resources(self.device)
|
||||
else:
|
||||
self.device = None
|
||||
return True
|
||||
except usb.core.USBError as e:
|
||||
i += 1
|
||||
if i > 100:
|
||||
return False
|
||||
|
||||
sleep(0.1)
|
||||
|
||||
def _raw(self, msg):
|
||||
""" Print any command sent in raw format """
|
||||
self.device.write(self.out_ep, msg, self.interface)
|
||||
if len(msg) != self.device.write(self.out_ep, msg, self.interface):
|
||||
self.device.write(self.out_ep, self.errorText, self.interface)
|
||||
raise TicketNotPrinted()
|
||||
|
||||
def __extract_status(self):
|
||||
maxiterate = 0
|
||||
rep = None
|
||||
while rep == None:
|
||||
maxiterate += 1
|
||||
if maxiterate > 10000:
|
||||
raise NoStatusError()
|
||||
r = self.device.read(self.in_ep, 20, self.interface).tolist()
|
||||
while len(r):
|
||||
rep = r.pop()
|
||||
return rep
|
||||
|
||||
def get_printer_status(self):
|
||||
status = {
|
||||
'printer': {},
|
||||
'offline': {},
|
||||
'error' : {},
|
||||
'paper' : {},
|
||||
}
|
||||
|
||||
self.device.write(self.out_ep, DLE_EOT_PRINTER, self.interface)
|
||||
printer = self.__extract_status()
|
||||
self.device.write(self.out_ep, DLE_EOT_OFFLINE, self.interface)
|
||||
offline = self.__extract_status()
|
||||
self.device.write(self.out_ep, DLE_EOT_ERROR, self.interface)
|
||||
error = self.__extract_status()
|
||||
self.device.write(self.out_ep, DLE_EOT_PAPER, self.interface)
|
||||
paper = self.__extract_status()
|
||||
|
||||
status['printer']['status_code'] = printer
|
||||
status['printer']['status_error'] = not ((printer & 147) == 18)
|
||||
status['printer']['online'] = not bool(printer & 8)
|
||||
status['printer']['recovery'] = bool(printer & 32)
|
||||
status['printer']['paper_feed_on'] = bool(printer & 64)
|
||||
status['printer']['drawer_pin_high'] = bool(printer & 4)
|
||||
status['offline']['status_code'] = offline
|
||||
status['offline']['status_error'] = not ((offline & 147) == 18)
|
||||
status['offline']['cover_open'] = bool(offline & 4)
|
||||
status['offline']['paper_feed_on'] = bool(offline & 8)
|
||||
status['offline']['paper'] = not bool(offline & 32)
|
||||
status['offline']['error'] = bool(offline & 64)
|
||||
status['error']['status_code'] = error
|
||||
status['error']['status_error'] = not ((error & 147) == 18)
|
||||
status['error']['recoverable'] = bool(error & 4)
|
||||
status['error']['autocutter'] = bool(error & 8)
|
||||
status['error']['unrecoverable'] = bool(error & 32)
|
||||
status['error']['auto_recoverable'] = not bool(error & 64)
|
||||
status['paper']['status_code'] = paper
|
||||
status['paper']['status_error'] = not ((paper & 147) == 18)
|
||||
status['paper']['near_end'] = bool(paper & 12)
|
||||
status['paper']['present'] = not bool(paper & 96)
|
||||
|
||||
return status
|
||||
|
||||
def __del__(self):
|
||||
""" Release USB interface """
|
||||
if self.device:
|
||||
usb.util.dispose_resources(self.device)
|
||||
self.close()
|
||||
self.device = None
|
||||
|
||||
|
||||
|
@ -134,3 +199,4 @@ class Network(Escpos):
|
|||
def __del__(self):
|
||||
""" Close TCP connection """
|
||||
self.device.close()
|
||||
|
||||
|
|
|
@ -107,7 +107,7 @@ class Scanner(Thread):
|
|||
if status == 'error' and message:
|
||||
_logger.error('Barcode Scanner Error: '+message)
|
||||
elif status == 'disconnected' and message:
|
||||
_logger.warning('Disconnected Barcode Scanner: '+message)
|
||||
_logger.info('Disconnected Barcode Scanner: %s', message)
|
||||
|
||||
def get_device(self):
|
||||
try:
|
||||
|
@ -168,41 +168,41 @@ class Scanner(Thread):
|
|||
device.ungrab()
|
||||
except Exception as e:
|
||||
self.set_status('error',str(e))
|
||||
device = self.get_device()
|
||||
if not device:
|
||||
time.sleep(5) # wait until a suitable device is plugged
|
||||
else:
|
||||
try:
|
||||
device.grab()
|
||||
shift = False
|
||||
barcode = []
|
||||
time.sleep(5) # wait until a suitable device is plugged
|
||||
device = self.get_device()
|
||||
|
||||
while True: # keycode loop
|
||||
r,w,x = select([device],[],[],5)
|
||||
if len(r) == 0: # timeout
|
||||
break
|
||||
events = device.read()
|
||||
try:
|
||||
device.grab()
|
||||
shift = False
|
||||
barcode = []
|
||||
|
||||
for event in events:
|
||||
if event.type == evdev.ecodes.EV_KEY:
|
||||
#_logger.debug('Evdev Keyboard event %s',evdev.categorize(event))
|
||||
if event.value == 1: # keydown events
|
||||
if event.code in self.keymap:
|
||||
if shift:
|
||||
barcode.append(self.keymap[event.code][1])
|
||||
else:
|
||||
barcode.append(self.keymap[event.code][0])
|
||||
elif event.code == 42 or event.code == 54: # SHIFT
|
||||
shift = True
|
||||
elif event.code == 28: # ENTER, end of barcode
|
||||
self.barcodes.put( (time.time(),''.join(barcode)) )
|
||||
barcode = []
|
||||
elif event.value == 0: #keyup events
|
||||
if event.code == 42 or event.code == 54: # LEFT SHIFT
|
||||
shift = False
|
||||
while True: # keycode loop
|
||||
r,w,x = select([device],[],[],5)
|
||||
if len(r) == 0: # timeout
|
||||
break
|
||||
events = device.read()
|
||||
|
||||
except Exception as e:
|
||||
self.set_status('error',str(e))
|
||||
for event in events:
|
||||
if event.type == evdev.ecodes.EV_KEY:
|
||||
#_logger.debug('Evdev Keyboard event %s',evdev.categorize(event))
|
||||
if event.value == 1: # keydown events
|
||||
if event.code in self.keymap:
|
||||
if shift:
|
||||
barcode.append(self.keymap[event.code][1])
|
||||
else:
|
||||
barcode.append(self.keymap[event.code][0])
|
||||
elif event.code == 42 or event.code == 54: # SHIFT
|
||||
shift = True
|
||||
elif event.code == 28: # ENTER, end of barcode
|
||||
self.barcodes.put( (time.time(),''.join(barcode)) )
|
||||
barcode = []
|
||||
elif event.value == 0: #keyup events
|
||||
if event.code == 42 or event.code == 54: # LEFT SHIFT
|
||||
shift = False
|
||||
|
||||
except Exception as e:
|
||||
self.set_status('error',str(e))
|
||||
|
||||
s = Scanner()
|
||||
|
||||
|
@ -212,5 +212,4 @@ class ScannerDriver(hw_proxy.Proxy):
|
|||
@http.route('/hw_proxy/scanner', type='json', auth='none', cors='*')
|
||||
def scanner(self):
|
||||
return s.get_barcode()
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue