From f1fa4fa2305159d15f5c41ff8b83ae1d19b6b077 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20van=20der=20Essen?= Date: Sun, 26 Jan 2014 19:35:00 +0100 Subject: [PATCH] [IMP] hw_proxy: better driver status reporting with hw_proxy/status, improved printing reliability bzr revid: fva@openerp.com-20140126183500-ejbm9d4jafl817tq --- addons/hw_escpos/controllers/main.py | 123 ++++++++++++++++++++------ addons/hw_proxy/controllers/main.py | 53 ++++++++++- addons/hw_scanner/controllers/main.py | 41 ++++++--- 3 files changed, 174 insertions(+), 43 deletions(-) diff --git a/addons/hw_escpos/controllers/main.py b/addons/hw_escpos/controllers/main.py index 4fde44de4b3..5d5808208df 100644 --- a/addons/hw_escpos/controllers/main.py +++ b/addons/hw_escpos/controllers/main.py @@ -10,6 +10,8 @@ import random import math import openerp.addons.hw_proxy.controllers.main as hw_proxy import subprocess +from threading import Thread +from Queue import Queue, Empty try: import usb.core @@ -27,11 +29,16 @@ from openerp.addons.web.controllers.main import manifest_list, module_boot, html _logger = logging.getLogger(__name__) -class EscposDriver(hw_proxy.Proxy): - - supported_printers = [ - { 'vendor' : 0x04b8, 'product' : 0x0e03, 'name' : 'Epson TM-T20' } - ] +class EscposDriver(Thread): + def __init__(self): + Thread.__init__(self) + self.queue = None + self.status = {'status':'connecting', 'messages':[]} + + self.supported_printers = [ + { 'vendor' : 0x04b8, 'product' : 0x0e03, 'name' : 'Epson TM-T20' }, + { 'vendor' : 0x04b8, 'product' : 0x0202, 'name' : 'Epson TM-T70' }, + ] def connected_usb_devices(self,devices): connected = [] @@ -41,32 +48,74 @@ class EscposDriver(hw_proxy.Proxy): return connected def get_escpos_printer(self): - printers = self.connected_usb_devices(self.supported_printers) - if len(printers) > 0: - return escpos.printer.Usb(printers[0]['vendor'], printers[0]['product']) - else: + try: + printers = self.connected_usb_devices(self.supported_printers) + 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)) return None - - @http.route('/hw_proxy/open_cashbox', type='json', auth='admin') - def open_cashbox(self): - _logger.info('ESC/POS: OPEN CASHBOX') - eprint = self.get_escpos_printer() - if eprint != None: - eprint.cashdraw(2) - eprint.cashdraw(5) + + def get_status(self): + self.push_task('status') + return self.status + + def open_cashbox(printer): + printer.cashdraw(2) + printer.cashdraw(5) + + def set_status(self, status, message = None): + if status == self.status['status']: + if message != None and message != self.status['messages'][-1]: + self.status['messages'].append(message) else: - _logger.error('ESC/POS: OPEN CASHBOX: could not find printer') - - @http.route('/hw_proxy/print_receipt', type='json', auth='admin') - def print_receipt(self, receipt): - _logger.info('ESC/POS: PRINT RECEIPT') - eprint = self.get_escpos_printer() - if eprint != None: - self.print_receipt_body(eprint,receipt) - eprint.cut() - else: - _logger.error('ESC/POS: PRINT RECEIPT: could not find printer') - + self.status['status'] = status + if message: + self.status['messages'] = [message] + else: + self.status['messages'] = [] + + if status == 'error' and message: + _logger.error('ESC/POS Error: '+message) + elif status == 'disconnected' and message: + _logger.warning('ESC/POS Device Disconnected: '+message) + + def run(self): + self.queue = Queue() + while True: + try: + timestamp, task, data = self.queue.get(True) + + printer = self.get_escpos_printer() + + if printer == None: + if task != 'status': + self.queue.put((timestamp,task,data)) + time.sleep(5) + continue + elif task == 'receipt': + if timestamp >= time.time() - 1 * 60 * 60: + self.print_receipt_body(printer,data) + printer.cut() + elif task == 'cashbox': + if timestamp >= time.time() * 12: + self.open_cashbox(printer) + elif task == 'status': + pass + + except Exception as e: + self.set_status('error', str(e)) + _logger.error(e); + + def push_task(self,task, data = None): + if not self.isAlive(): + self.start() + self.queue.put((time.time(),task,data)) + def print_receipt_body(self,eprint,receipt): def check(string): @@ -207,4 +256,20 @@ class EscposDriver(hw_proxy.Proxy): +'/'+ str(receipt['date']['year']).zfill(4) +' '+ str(receipt['date']['hour']).zfill(2) +':'+ str(receipt['date']['minute']).zfill(2) ) + +driver = EscposDriver() + +hw_proxy.drivers['escpos'] = driver +class EscposProxy(hw_proxy.Proxy): + + @http.route('/hw_proxy/open_cashbox', type='json', auth='admin') + def open_cashbox(self): + _logger.info('ESC/POS: OPEN CASHBOX') + driver.push_task('cashbox') + + @http.route('/hw_proxy/print_receipt', type='json', auth='admin') + def print_receipt(self, receipt): + _logger.info('ESC/POS: PRINT RECEIPT') + driver.push_task('receipt',receipt) + diff --git a/addons/hw_proxy/controllers/main.py b/addons/hw_proxy/controllers/main.py index 6ddf94843fd..1888c2304d1 100644 --- a/addons/hw_proxy/controllers/main.py +++ b/addons/hw_proxy/controllers/main.py @@ -7,6 +7,7 @@ import openerp import time import random import subprocess +import simplejson import werkzeug import werkzeug.wrappers _logger = logging.getLogger(__name__) @@ -17,13 +18,23 @@ from openerp.http import request from openerp.addons.web.controllers.main import manifest_list, module_boot, html_template +# drivers modules must add to drivers an object with a get_status() method +# so that 'status' can return the status of all active drivers +drivers = {} + class Proxy(http.Controller): def __init__(self): self.scale = 'closed' self.scale_weight = 0.0 + def get_status(self): + statuses = {} + for driver in drivers: + statuses[driver] = drivers[driver].get_status() + return statuses + @http.route('/hw_proxy/hello', type='http', auth='admin') - def helloajx(self): + def hello(self): return request.make_response('ping', { 'Cache-Control': 'no-cache', 'Content-Type': 'text/html; charset=utf-8', @@ -31,9 +42,43 @@ class Proxy(http.Controller): 'Access-Control-Allow-Methods': 'GET', }) - @http.route('/hw_proxy/handshake', type='json', auth='admin') - def handshake(self): - return True + @http.route('/hw_proxy/status', type='http', auth='admin') + def status_http(self): + resp = '\n\n

Hardware Proxy Status

\n' + statuses = self.get_status() + for driver in statuses: + + status = statuses[driver] + + if status['status'] == 'connecting': + color = 'black' + elif status['status'] == 'connected': + color = 'green' + else: + color = 'red' + + resp += "

"+driver+' : '+status['status']+"

\n" + resp += "\n" + resp += "\n\n\n\n" + + return request.make_response(resp,{ + 'Cache-Control': 'no-cache', + 'Content-Type': 'text/html; charset=utf-8', + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Methods': 'GET', + }) + + @http.route('/hw_proxy/status', type='json', auth='admin') + def status_json(self): + return request.make_response(simplejson.dumps(self.get_status()),{ + 'Cache-Control': 'no-cache', + 'Content-Type': 'text/html; charset=utf-8', + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Methods': 'GET', + }) @http.route('/hw_proxy/scan_item_success', type='json', auth='admin') def scan_item_success(self, ean): diff --git a/addons/hw_scanner/controllers/main.py b/addons/hw_scanner/controllers/main.py index cd70a353372..3e487cab404 100644 --- a/addons/hw_scanner/controllers/main.py +++ b/addons/hw_scanner/controllers/main.py @@ -26,7 +26,9 @@ except ImportError: class Scanner(Thread): def __init__(self): Thread.__init__(self) + self.status = {'status':'connecting', 'messages':[]} self.input_dir = '/dev/input/by-id/' + self.barcodes = Queue() self.keymap = { 2: ("1","!"), 3: ("2","@"), @@ -84,6 +86,24 @@ class Scanner(Thread): 57:(" "," "), } + def set_status(self, status, message = None): + if status == self.status['status']: + if message != None and message != self.status['messages'][-1]: + self.status['messages'].append(message) + else: + self.status['status'] = status + if message: + self.status['messages'] = [message] + else: + self.status['messages'] = [] + + if status == 'error' and message: + _logger.error('Barcode Scanner Error: '+message) + elif status == 'disconnected' and message: + _logger.warning('Disconnected Barcode Scanner: '+message) + + + def get_device(self): try: if not evdev: @@ -92,14 +112,16 @@ class Scanner(Thread): keyboards = [ device for device in devices if 'kbd' in device ] scanners = [ device for device in devices if ('barcode' in device.lower()) or ('scanner' in device.lower()) ] if len(scanners) > 0: + self.set_status('connected','Connected to '+scanners[0]) return evdev.InputDevice(join(self.input_dir,scanners[0])) elif len(keyboards) > 0: + self.set_status('connected','Connected to '+keyboards[0]) return evdev.InputDevice(join(self.input_dir,keyboards[0])) else: - _logger.error('Could not find the Barcode Scanner') + self.set_status('disconnected','Barcode Scanner Not Found') return None except Exception as e: - _logger.error('Found the Barcode Scanner, but unable to access it\n Exception: ' + str(e)) + self.set_status('error',str(e)) return None @http.route('/hw_proxy/Vis_scanner_connected', type='json', auth='admin') @@ -120,6 +142,9 @@ class Scanner(Thread): return barcode except Empty: return '' + + def get_status(self): + return self.status def run(self): """ This will start a loop that catches all keyboard events, parse barcode @@ -138,7 +163,7 @@ class Scanner(Thread): try: device.ungrab() except Exception as e: - _logger.error('Unable to release barcode scanner\n Exception:'+str(e)) + self.set_status('error',str(e)) device = self.get_device() if not device: time.sleep(5) # wait until a suitable device is plugged @@ -173,17 +198,13 @@ class Scanner(Thread): shift = False except Exception as e: - _logger.error('Could not read Barcode Scanner Events:\n Exception: '+str(e)) + self.set_status('error',str(e)) s = Scanner() +hw_proxy.drivers['scanner'] = s + class ScannerDriver(hw_proxy.Proxy): - @http.route('/hw_proxy/is_scanner_connected', type='json', auth='admin') - def is_scanner_connected(self): - if not s.isAlive(): - s.start() - return s.get_device() != None - @http.route('/hw_proxy/scanner', type='json', auth='admin') def scanner(self): if not s.isAlive():