1 # -*- coding: utf-8 -*-
6 from os.path import join
7 from threading import Thread, Lock
8 from select import select
9 from Queue import Queue, Empty
12 import openerp.addons.hw_proxy.controllers.main as hw_proxy
13 from openerp import http
14 from openerp.http import request
15 from openerp.tools.translate import _
17 _logger = logging.getLogger(__name__)
22 _logger.error('OpenERP module hw_scanner depends on the evdev python module')
26 class Scanner(Thread):
30 self.status = {'status':'connecting', 'messages':[]}
31 self.input_dir = '/dev/input/by-id/'
32 self.barcodes = Queue()
90 def lockedstart(self):
92 if not self.isAlive():
96 def set_status(self, status, message = None):
97 if status == self.status['status']:
98 if message != None and message != self.status['messages'][-1]:
99 self.status['messages'].append(message)
101 self.status['status'] = status
103 self.status['messages'] = [message]
105 self.status['messages'] = []
107 if status == 'error' and message:
108 _logger.error('Barcode Scanner Error: '+message)
109 elif status == 'disconnected' and message:
110 _logger.warning('Disconnected Barcode Scanner: '+message)
112 def get_device(self):
116 devices = [ device for device in listdir(self.input_dir)]
117 keyboards = [ device for device in devices if ('kbd' in device) and ('keyboard' not in device.lower())]
118 scanners = [ device for device in devices if ('barcode' in device.lower()) or ('scanner' in device.lower())]
119 if len(scanners) > 0:
120 self.set_status('connected','Connected to '+scanners[0])
121 return evdev.InputDevice(join(self.input_dir,scanners[0]))
122 elif len(keyboards) > 0:
123 self.set_status('connected','Connected to '+keyboards[0])
124 return evdev.InputDevice(join(self.input_dir,keyboards[0]))
126 self.set_status('disconnected','Barcode Scanner Not Found')
128 except Exception as e:
129 self.set_status('error',str(e))
132 @http.route('/hw_proxy/Vis_scanner_connected', type='json', auth='none', cors='*')
133 def is_scanner_connected(self):
134 return self.get_device() != None
136 def get_barcode(self):
137 """ Returns a scanned barcode. Will wait at most 5 seconds to get a barcode, and will
138 return barcode scanned in the past if they are not older than 5 seconds and have not
139 been returned before. This is necessary to catch barcodes scanned while the POS is
140 busy reading another barcode
147 timestamp, barcode = self.barcodes.get(True, 5)
148 if timestamp > time.time() - 5:
153 def get_status(self):
158 """ This will start a loop that catches all keyboard events, parse barcode
159 sequences and put them on a timestamped queue that can be consumed by
160 the point of sale's requests for barcode events
163 self.barcodes = Queue()
169 while True: # barcodes loop
170 if device: # ungrab device between barcodes and timeouts for plug & play
173 except Exception as e:
174 self.set_status('error',str(e))
175 device = self.get_device()
177 time.sleep(5) # wait until a suitable device is plugged
184 while True: # keycode loop
185 r,w,x = select([device],[],[],5)
186 if len(r) == 0: # timeout
188 events = device.read()
191 if event.type == evdev.ecodes.EV_KEY:
192 #_logger.debug('Evdev Keyboard event %s',evdev.categorize(event))
193 if event.value == 1: # keydown events
194 if event.code in self.keymap:
196 barcode.append(self.keymap[event.code][1])
198 barcode.append(self.keymap[event.code][0])
199 elif event.code == 42 or event.code == 54: # SHIFT
201 elif event.code == 28: # ENTER, end of barcode
202 self.barcodes.put( (time.time(),''.join(barcode)) )
204 elif event.value == 0: #keyup events
205 if event.code == 42 or event.code == 54: # LEFT SHIFT
208 except Exception as e:
209 self.set_status('error',str(e))
213 hw_proxy.drivers['scanner'] = s
215 class ScannerDriver(hw_proxy.Proxy):
216 @http.route('/hw_proxy/scanner', type='json', auth='none', cors='*')
218 return s.get_barcode()