[IMP] hw_scanner: remove useless controller
[odoo/odoo.git] / addons / hw_scanner / controllers / main.py
1 # -*- coding: utf-8 -*-
2 import logging
3 import os
4 import time
5 from os import listdir
6 from os.path import join
7 from threading import Thread, Lock
8 from select import select
9 from Queue import Queue, Empty
10
11 import openerp
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 _
16
17 _logger = logging.getLogger(__name__)
18
19 try:
20     import evdev
21 except ImportError:
22     _logger.error('OpenERP module hw_scanner depends on the evdev python module')
23     evdev = None
24
25
26 class Scanner(Thread):
27     def __init__(self):
28         Thread.__init__(self)
29         self.lock = Lock()
30         self.status = {'status':'connecting', 'messages':[]}
31         self.input_dir = '/dev/input/by-id/'
32         self.barcodes = Queue()
33         self.keymap = {
34             2: ("1","!"),
35             3: ("2","@"),
36             4: ("3","#"),
37             5: ("4","$"),
38             6: ("5","%"),
39             7: ("6","^"),
40             8: ("7","&"),
41             9: ("8","*"),
42             10:("9","("), 
43             11:("0",")"), 
44             12:("-","_"), 
45             13:("=","+"), 
46             # 14 BACKSPACE
47             # 15 TAB 
48             16:("q","Q"), 
49             17:("w","W"),
50             18:("e","E"),
51             19:("r","R"),
52             20:("t","T"),
53             21:("y","Y"),
54             22:("u","U"),
55             23:("i","I"),
56             24:("o","O"),
57             25:("p","P"),
58             26:("[","{"),
59             27:("]","}"),
60             # 28 ENTER
61             # 29 LEFT_CTRL
62             30:("a","A"),
63             31:("s","S"),
64             32:("d","D"),
65             33:("f","F"),
66             34:("g","G"),
67             35:("h","H"),
68             36:("j","J"),
69             37:("k","K"),
70             38:("l","L"),
71             39:(";",":"),
72             40:("'","\""),
73             41:("`","~"),
74             # 42 LEFT SHIFT
75             43:("\\","|"),
76             44:("z","Z"),
77             45:("x","X"),
78             46:("c","C"),
79             47:("v","V"),
80             48:("b","B"),
81             49:("n","N"),
82             50:("m","M"),
83             51:(",","<"),
84             52:(".",">"),
85             53:("/","?"),
86             # 54 RIGHT SHIFT
87             57:(" "," "),
88         }
89
90     def lockedstart(self):
91         self.lock.acquire()
92         if not self.isAlive():
93             self.start()
94         self.lock.release()
95
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)
100         else:
101             self.status['status'] = status
102             if message:
103                 self.status['messages'] = [message]
104             else:
105                 self.status['messages'] = []
106
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)
111
112     def get_device(self):
113         try:
114             if not evdev:
115                 return None
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]))
125             else:
126                 self.set_status('disconnected','Barcode Scanner Not Found')
127                 return None
128         except Exception as e:
129             self.set_status('error',str(e))
130             return None
131
132     def get_barcode(self):
133         """ Returns a scanned barcode. Will wait at most 5 seconds to get a barcode, and will
134             return barcode scanned in the past if they are not older than 5 seconds and have not
135             been returned before. This is necessary to catch barcodes scanned while the POS is
136             busy reading another barcode
137         """
138
139         self.lockedstart()
140
141         while True:
142             try:
143                 timestamp, barcode = self.barcodes.get(True, 5)
144                 if timestamp > time.time() - 5: 
145                     return barcode
146             except Empty:
147                 return ''
148     
149     def get_status(self):
150         self.lockedstart()
151         return self.status
152
153     def run(self):
154         """ This will start a loop that catches all keyboard events, parse barcode
155             sequences and put them on a timestamped queue that can be consumed by
156             the point of sale's requests for barcode events 
157         """
158         
159         self.barcodes = Queue()
160         
161         barcode  = []
162         shift    = False
163         device   = None
164
165         while True: # barcodes loop
166             if device:  # ungrab device between barcodes and timeouts for plug & play
167                 try:
168                     device.ungrab() 
169                 except Exception as e:
170                     self.set_status('error',str(e))
171             device = self.get_device()
172             if not device:
173                 time.sleep(5)   # wait until a suitable device is plugged
174             else:
175                 try:
176                     device.grab()
177                     shift = False
178                     barcode = []
179
180                     while True: # keycode loop
181                         r,w,x = select([device],[],[],5)
182                         if len(r) == 0: # timeout
183                             break
184                         events = device.read()
185
186                         for event in events:
187                             if event.type == evdev.ecodes.EV_KEY:
188                                 #_logger.debug('Evdev Keyboard event %s',evdev.categorize(event))
189                                 if event.value == 1: # keydown events
190                                     if event.code in self.keymap: 
191                                         if shift:
192                                             barcode.append(self.keymap[event.code][1])
193                                         else:
194                                             barcode.append(self.keymap[event.code][0])
195                                     elif event.code == 42 or event.code == 54: # SHIFT
196                                         shift = True
197                                     elif event.code == 28: # ENTER, end of barcode
198                                         self.barcodes.put( (time.time(),''.join(barcode)) )
199                                         barcode = []
200                                 elif event.value == 0: #keyup events
201                                     if event.code == 42 or event.code == 54: # LEFT SHIFT
202                                         shift = False
203
204                 except Exception as e:
205                     self.set_status('error',str(e))
206
207 s = Scanner()
208
209 hw_proxy.drivers['scanner'] = s
210
211 class ScannerDriver(hw_proxy.Proxy):
212     @http.route('/hw_proxy/scanner', type='json', auth='none', cors='*')
213     def scanner(self):
214         return s.get_barcode()
215         
216