3c4aa12a1b9752fe126452626881be1befdcfccc
[odoo/odoo.git] / addons / hw_scale / 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 from bitstring import BitArray
11
12 import openerp
13 import openerp.addons.hw_proxy.controllers.main as hw_proxy
14 from openerp import http
15 from openerp.http import request
16 from openerp.tools.translate import _
17
18 _logger = logging.getLogger(__name__)
19
20 try:
21     import serial
22 except ImportError:
23     _logger.error('OpenERP module hw_scale depends on the pyserial python module')
24     serial = None
25
26
27 class Scale(Thread):
28     def __init__(self):
29         Thread.__init__(self)
30         self.lock = Lock()
31         self.scalelock = Lock()
32         self.status = {'status':'connecting', 'messages':[]}
33         self.input_dir = '/dev/serial/by-id/'
34         self.weight = 0
35         self.weight_info = 'ok'
36         self.device = None
37
38     def lockedstart(self):
39         with self.lock:
40             if not self.isAlive():
41                 self.daemon = True
42                 self.start()
43
44     def set_status(self, status, message = None):
45         if status == self.status['status']:
46             if message != None and message != self.status['messages'][-1]:
47                 self.status['messages'].append(message)
48         else:
49             self.status['status'] = status
50             if message:
51                 self.status['messages'] = [message]
52             else:
53                 self.status['messages'] = []
54
55         if status == 'error' and message:
56             _logger.error('Scale Error: '+message)
57         elif status == 'disconnected' and message:
58             _logger.warning('Disconnected Scale: '+message)
59
60     def get_device(self):
61         try:
62             devices = [ device for device in listdir(self.input_dir)]
63             scales  = [ device for device in devices if ('mettler' in device.lower()) or ('toledo' in device.lower()) ]
64             if len(scales) > 0:
65                 print join(self.input_dir,scales[0])
66                 self.set_status('connected','Connected to '+scales[0])
67                 return serial.Serial(join(self.input_dir,scales[0]), 
68                         baudrate = 9600, 
69                         bytesize = serial.SEVENBITS, 
70                         stopbits = serial.STOPBITS_ONE, 
71                         parity   = serial.PARITY_EVEN, 
72                         #xonxoff  = serial.XON,
73                         timeout  = 0.01, 
74                         writeTimeout= 0.01)
75             else:
76                 self.set_status('disconnected','Scale Not Found')
77                 return None
78         except Exception as e:
79             self.set_status('error',str(e))
80             return None
81
82     def get_weight(self):
83         self.lockedstart()
84         return self.weight
85
86     def get_weight_info(self):
87         self.lockedstart()
88         return self.weight_info
89     
90     def get_status(self):
91         self.lockedstart()
92         return self.status
93
94     def read_weight(self):
95         with self.scalelock:
96             if self.device:
97                 try:
98                     self.device.write('W')
99                     time.sleep(0.1)
100                     answer = []
101
102                     while True:
103                         char = self.device.read(1)
104                         if not char: 
105                             break
106                         else:
107                             answer.append(char)
108
109                     if '?' in answer:
110                         stat = ord(answer[answer.index('?')+1])
111                         if stat == 0: 
112                             self.weight_info = 'ok'
113                         else:
114                             self.weight_info = []
115                             if stat & 1 :
116                                 self.weight_info.append('moving')
117                             if stat & 1 << 1:
118                                 self.weight_info.append('over_capacity')
119                             if stat & 1 << 2:
120                                 self.weight_info.append('negative')
121                                 self.weight = 0.0
122                             if stat & 1 << 3:
123                                 self.weight_info.append('outside_zero_capture_range')
124                             if stat & 1 << 4:
125                                 self.weight_info.append('center_of_zero')
126                             if stat & 1 << 5:
127                                 self.weight_info.append('net_weight')
128                     else:
129                         answer = answer[1:-1]
130                         if 'N' in answer:
131                             answer = answer[0:-1]
132                         self.weight = float(''.join(answer))
133                         
134                 except Exception as e:
135                     self.set_status('error',str(e))
136                     self.device = None
137
138     def set_zero(self):
139         with self.scalelock:
140             if self.device:
141                 try: 
142                     self.device.write('Z')
143                 except Exception as e:
144                     self.set_status('error',str(e))
145                     self.device = None
146
147     def set_tare(self):
148         with self.scalelock:
149             if self.device:
150                 try: 
151                     self.device.write('T')
152                 except Exception as e:
153                     self.set_status('error',str(e))
154                     self.device = None
155
156     def clear_tare(self):
157         with self.scalelock:
158             if self.device:
159                 try: 
160                     self.device.write('C')
161                 except Exception as e:
162                     self.set_status('error',str(e))
163                     self.device = None
164
165     def run(self):
166         self.device   = None
167
168         while True: 
169             if self.device:
170                 self.read_weight()
171                 time.sleep(0.05)
172             else:
173                 with self.scalelock:
174                     self.device = self.get_device()
175                 if not self.device:
176                     time.sleep(5)
177
178 s = Scale()
179
180 hw_proxy.drivers['scale'] = s
181
182 class ScaleDriver(hw_proxy.Proxy):
183     @http.route('/hw_proxy/scale_read/', type='json', auth='none', cors='*')
184     def scale_read(self):
185         return {'weight':s.get_weight(), 'unit':'kg', 'info':s.get_weight_info()}
186
187     @http.route('/hw_proxy/scale_zero/', type='json', auth='none', cors='*')
188     def scale_zero(self):
189         s.set_zero()
190         return True
191
192     @http.route('/hw_proxy/scale_tare/', type='json', auth='none', cors='*')
193     def scale_tare(self):
194         s.set_tare()
195         return True
196
197     @http.route('/hw_proxy/scale_clear_tare/', type='json', auth='none', cors='*')
198     def scale_clear_tare(self):
199         s.clear_tare()
200         return True
201         
202