drivers = {}
class Proxy(http.Controller):
- def __init__(self):
- self.scale = 'closed'
- self.scale_weight = 0.0
def get_status(self):
statuses = {}
"""
print "help_canceled"
- @http.route('/hw_proxy/weighting_start', type='json', auth='none', cors='*')
- def weighting_start(self):
- if self.scale == 'closed':
- print "Opening (Fake) Connection to Scale..."
- self.scale = 'open'
- self.scale_weight = 0.0
- time.sleep(0.1)
- print "... Scale Open."
- else:
- print "WARNING: Scale already Connected !!!"
-
- @http.route('/hw_proxy/weighting_read_kg', type='json', auth='none', cors='*')
- def weighting_read_kg(self):
- if self.scale == 'open':
- print "Reading Scale..."
- time.sleep(0.025)
- self.scale_weight += 0.01
- print "... Done."
- return self.scale_weight
- else:
- print "WARNING: Reading closed scale !!!"
- return 0.0
-
- @http.route('/hw_proxy/weighting_end', type='json', auth='none', cors='*')
- def weighting_end(self):
- if self.scale == 'open':
- print "Closing Connection to Scale ..."
- self.scale = 'closed'
- self.scale_weight = 0.0
- time.sleep(0.1)
- print "... Scale Closed."
- else:
- print "WARNING: Scale already Closed !!!"
-
@http.route('/hw_proxy/payment_request', type='json', auth='none', cors='*')
def payment_request(self, price):
"""
--- /dev/null
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+import controllers
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+
--- /dev/null
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+
+{
+ 'name': 'Weighting Scale Hardware Driver',
+ 'version': '1.0',
+ 'category': 'Hardware Drivers',
+ 'sequence': 6,
+ 'summary': 'Hardware Driver for Weighting Scales',
+ 'description': """
+Barcode Scanner Hardware Driver
+================================
+
+This module allows the point of sale to connect to a scale using a USB HSM Serial Scale Interface,
+such as the Mettler Toledo Ariva.
+
+""",
+ 'author': 'OpenERP SA',
+ 'depends': ['hw_proxy'],
+ 'test': [
+ ],
+ 'installable': True,
+ 'auto_install': False,
+}
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
--- /dev/null
+import main
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+
--- /dev/null
+# -*- coding: utf-8 -*-
+import logging
+import os
+import time
+from os import listdir
+from os.path import join
+from threading import Thread, Lock
+from select import select
+from Queue import Queue, Empty
+
+import openerp
+import openerp.addons.hw_proxy.controllers.main as hw_proxy
+from openerp import http
+from openerp.http import request
+from openerp.tools.translate import _
+
+_logger = logging.getLogger(__name__)
+
+try:
+ import serial
+except ImportError:
+ _logger.error('OpenERP module hw_scale depends on the pyserial python module')
+ serial = None
+
+
+class Scale(Thread):
+ def __init__(self):
+ Thread.__init__(self)
+ self.lock = Lock()
+ self.scalelock = Lock()
+ self.status = {'status':'connecting', 'messages':[]}
+ self.input_dir = '/dev/serial/by-id/'
+ self.weight = 0
+ self.weight_info = 'ok'
+ self.device = None
+
+ def lockedstart(self):
+ with self.lock:
+ if not self.isAlive():
+ self.daemon = True
+ self.start()
+
+ 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('Scale Error: '+message)
+ elif status == 'disconnected' and message:
+ _logger.warning('Disconnected Scale: '+message)
+
+ def get_device(self):
+ try:
+ devices = [ device for device in listdir(self.input_dir)]
+ scales = [ device for device in devices if ('mettler' in device.lower()) or ('toledo' in device.lower()) ]
+ if len(scales) > 0:
+ print join(self.input_dir,scales[0])
+ self.set_status('connected','Connected to '+scales[0])
+ return serial.Serial(join(self.input_dir,scales[0]),
+ baudrate = 9600,
+ bytesize = serial.SEVENBITS,
+ stopbits = serial.STOPBITS_ONE,
+ parity = serial.PARITY_EVEN,
+ #xonxoff = serial.XON,
+ timeout = 0.01,
+ writeTimeout= 0.01)
+ else:
+ self.set_status('disconnected','Scale Not Found')
+ return None
+ except Exception as e:
+ self.set_status('error',str(e))
+ return None
+
+ def get_weight(self):
+ self.lockedstart()
+ return self.weight
+
+ def get_weight_info(self):
+ self.lockedstart()
+ return self.weight_info
+
+ def get_status(self):
+ self.lockedstart()
+ return self.status
+
+ def read_weight(self):
+ with self.scalelock:
+ if self.device:
+ try:
+ self.device.write('W')
+ time.sleep(0.1)
+ answer = []
+
+ while True:
+ char = self.device.read(1)
+ if not char:
+ break
+ else:
+ answer.append(char)
+
+ if '?' in answer:
+ stat = ord(answer[answer.index('?')+1])
+ if stat == 0:
+ self.weight_info = 'ok'
+ else:
+ self.weight_info = []
+ if stat & 1 :
+ self.weight_info.append('moving')
+ if stat & 1 << 1:
+ self.weight_info.append('over_capacity')
+ if stat & 1 << 2:
+ self.weight_info.append('negative')
+ self.weight = 0.0
+ if stat & 1 << 3:
+ self.weight_info.append('outside_zero_capture_range')
+ if stat & 1 << 4:
+ self.weight_info.append('center_of_zero')
+ if stat & 1 << 5:
+ self.weight_info.append('net_weight')
+ else:
+ answer = answer[1:-1]
+ if 'N' in answer:
+ answer = answer[0:-1]
+ try:
+ self.weight = float(''.join(answer))
+ except ValueError as v:
+ self.set_status('error','No data Received, please power-cycle the scale');
+ self.device = None
+
+ except Exception as e:
+ self.set_status('error',str(e))
+ self.device = None
+
+ def set_zero(self):
+ with self.scalelock:
+ if self.device:
+ try:
+ self.device.write('Z')
+ except Exception as e:
+ self.set_status('error',str(e))
+ self.device = None
+
+ def set_tare(self):
+ with self.scalelock:
+ if self.device:
+ try:
+ self.device.write('T')
+ except Exception as e:
+ self.set_status('error',str(e))
+ self.device = None
+
+ def clear_tare(self):
+ with self.scalelock:
+ if self.device:
+ try:
+ self.device.write('C')
+ except Exception as e:
+ self.set_status('error',str(e))
+ self.device = None
+
+ def run(self):
+ self.device = None
+
+ while True:
+ if self.device:
+ self.read_weight()
+ time.sleep(0.05)
+ else:
+ with self.scalelock:
+ self.device = self.get_device()
+ if not self.device:
+ time.sleep(5)
+
+s = Scale()
+
+hw_proxy.drivers['scale'] = s
+
+class ScaleDriver(hw_proxy.Proxy):
+ @http.route('/hw_proxy/scale_read/', type='json', auth='none', cors='*')
+ def scale_read(self):
+ return {'weight':s.get_weight(), 'unit':'kg', 'info':s.get_weight_info()}
+
+ @http.route('/hw_proxy/scale_zero/', type='json', auth='none', cors='*')
+ def scale_zero(self):
+ s.set_zero()
+ return True
+
+ @http.route('/hw_proxy/scale_tare/', type='json', auth='none', cors='*')
+ def scale_tare(self):
+ s.set_tare()
+ return True
+
+ @http.route('/hw_proxy/scale_clear_tare/', type='json', auth='none', cors='*')
+ def scale_clear_tare(self):
+ s.clear_tare()
+ return True
+
+
</field>
<group string="Features" >
<group>
- <field name="iface_cashdrawer" />
+ <field name="iface_vkeyboard" />
<field name="iface_invoicing" />
- <field name="iface_electronic_scale" />
</group>
<group>
- <field name="iface_vkeyboard" />
<field name="iface_big_scrollbars" />
</group>
</group>
<field name="proxy_ip" />
<field name="iface_print_via_proxy" />
<field name="iface_scan_via_proxy" />
+ <field name="iface_electronic_scale" />
+ <field name="iface_cashdrawer" />
</group>
<group string="Receipt" >
<field name="receipt_header" placeholder="A custom receipt header message"/>
return this.message('help_canceled');
},
- //the client is starting to weight
- weighting_start: function(){
- var ret = new $.Deferred();
- if(!this.weighting){
- this.weighting = true;
- this.message('weighting_start').always(function(){
- ret.resolve();
- });
- }else{
- console.error('Weighting already started!!!');
- ret.resolve();
- }
- return ret;
- },
-
- // the client has finished weighting products
- weighting_end: function(){
- var ret = new $.Deferred();
- if(this.weighting){
- this.weighting = false;
- this.message('weighting_end').always(function(){
- ret.resolve();
- });
- }else{
- console.error('Weighting already ended !!!');
- ret.resolve();
- }
- return ret;
- },
-
- //returns the weight on the scale.
- // is called at regular interval (up to 10x/sec) between a weighting_start()
- // and a weighting_end()
- weighting_read_kg: function(){
+ // returns the weight on the scale.
+ scale_read: function(){
var self = this;
var ret = new $.Deferred();
- this.message('weighting_read_kg',{})
+ console.log('scale_read');
+ this.message('scale_read',{})
.then(function(weight){
+ console.log(weight)
ret.resolve(self.use_debug_weight ? self.debug_weight : weight);
}, function(){ //failed to read weight
- ret.resolve(self.use_debug_weight ? self.debug_weight : 0.0);
+ ret.resolve(self.use_debug_weight ? self.debug_weight : {weight:0.0, unit:'Kg', info:'ok'});
});
return ret;
},
});
queue.schedule(function(){
- return self.pos.proxy.weighting_start()
- },{ important: true });
-
- queue.schedule(function(){
- return self.pos.proxy.weighting_read_kg().then(function(weight){
- self.set_weight(weight);
+ return self.pos.proxy.scale_read().then(function(weight){
+ self.set_weight(weight.weight);
});
},{duration:50, repeat: true});
$('body').off('keyup',this.hotkey_handler);
this.pos.proxy_queue.clear();
- this.pos.proxy_queue.schedule(function(){
- self.pos.proxy.weighting_end();
- },{ important: true });
},
});
'open_cashbox',
'print_receipt',
'print_pdf_invoice',
- 'weighting_read_kg',
+ 'scale_read',
'payment_status',
],
minimized: false,
self.pos.proxy.add_notification('transaction_end',function(){
self.$('.status.transaction').removeClass('on');
});
- self.pos.proxy.add_notification('weighting_start',function(){
- self.$('.status.weighting').addClass('on');
- });
- self.pos.proxy.add_notification('weighting_end',function(){
- self.$('.status.weighting').removeClass('on');
- });
},
});
msg += _t('Printer');
}
}
+ if( this.pos.config.iface_electronic_scale ){
+ var scale = status.drivers.scale ? status.drivers.scale.status : false;
+ if( scale != 'connected' && scale != 'connecting' ){
+ warning = true;
+ msg = msg ? msg + ' & ' : msg;
+ msg += _t('Scale');
+ }
+ }
msg = msg ? msg + ' ' + _t('Offline') : msg;
this.set_status(warning ? 'warning' : 'connected', msg);
}else{
<li class="event open_cashbox">Open Cashbox</li>
<li class="event print_receipt">Print Receipt</li>
<li class="event print_pdf_invoice">Print Invoice</li>
- <li class="event weighting_read_kg">Read Weighting Scale</li>
+ <li class="event scale_read">Read Weighting Scale</li>
</ul>
</div>
</div>