[IMP] pos_epson_printer: Remove ePOS library
Remove minified ePOS library and replace it with direct calls to the web server of the printer. closes odoo/odoo#63403 Taskid: 2417429 Signed-off-by: pimodoo <pimodoo@users.noreply.github.com>
This commit is contained in:
@@ -17,6 +17,9 @@ Use Epson ePOS Printers without the IoT Box in the Point of Sale
|
||||
'views/point_of_sale_assets.xml',
|
||||
'views/pos_config_views.xml',
|
||||
],
|
||||
'qweb': [
|
||||
'static/src/xml/epos_templates.xml'
|
||||
],
|
||||
'installable': True,
|
||||
'auto_install': True,
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -2,98 +2,166 @@
|
||||
odoo.define('pos_epson_printer.Printer', function (require) {
|
||||
"use strict";
|
||||
|
||||
const { Gui } = require('point_of_sale.Gui');
|
||||
var core = require('web.core');
|
||||
var PrinterMixin = require('point_of_sale.Printer').PrinterMixin;
|
||||
var { PrinterMixin, PrintResult, PrintResultGenerator } = require('point_of_sale.Printer');
|
||||
|
||||
var QWeb = core.qweb;
|
||||
var _t = core._t;
|
||||
|
||||
var EpsonPrinter = core.Class.extend(PrinterMixin, {
|
||||
init: function (ip, pos) {
|
||||
PrinterMixin.init.call(this, arguments);
|
||||
this.pos = pos;
|
||||
this.ePOSDevice = new epson.ePOSDevice();
|
||||
var port = window.location.protocol === 'http:' ? '8008' : '8043';
|
||||
this.ePOSDevice.connect(ip, port, this.callback_connect.bind(this), {eposprint: true});
|
||||
},
|
||||
class EpsonPrintResultGenerator extends PrintResultGenerator {
|
||||
constructor(address) {
|
||||
super();
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
callback_connect: function (resultConnect) {
|
||||
var deviceId = 'local_printer';
|
||||
var options = {'crypto' : false, 'buffer' : false};
|
||||
if ((resultConnect == 'OK') || (resultConnect == 'SSL_CONNECT_OK')) {
|
||||
this.ePOSDevice.createDevice(deviceId, this.ePOSDevice.DEVICE_TYPE_PRINTER, options, this.callback_createDevice.bind(this));
|
||||
} else {
|
||||
Gui.showPopup('ErrorPopup', {
|
||||
'title': _t('Connection to the printer failed'),
|
||||
'body': _t('Please check if the printer is still connected, if the configured IP address is correct and if your printer supports the ePOS protocol.'),
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
callback_createDevice: function (deviceObj, errorCode) {
|
||||
if (deviceObj === null) {
|
||||
Gui.showPopup('ErrorPopup', {
|
||||
'title': _t('Connection to the printer failed'),
|
||||
'body': _t('Please check if the printer is still connected. Error code: ') + errorCode,
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.printer = deviceObj;
|
||||
this.printer.onreceive = function(response){
|
||||
if (!response.success) {
|
||||
Gui.showPopup('ErrorPopup', {
|
||||
'title': _t('Epson ePOS Error'),
|
||||
'body': _t('An error happened while sending data to the printer. Error code: ') + response.code,
|
||||
});
|
||||
IoTActionError() {
|
||||
var printRes = new PrintResult({
|
||||
successful: false,
|
||||
message: {
|
||||
title: _t('Connection to the printer failed'),
|
||||
body: _t('Please check if the printer is still connected.'),
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
if (window.location.protocol === 'https:') {
|
||||
printRes.message.body += _.str.sprintf(
|
||||
_t('If you are on a secure server (HTTPS) please make sure you manually accepted the certificate by accessing %s'),
|
||||
this.address
|
||||
);
|
||||
}
|
||||
|
||||
return printRes;
|
||||
}
|
||||
|
||||
IoTResultError() {
|
||||
return new PrintResult({
|
||||
successful: false,
|
||||
message: {
|
||||
title: _t('Printing failed'),
|
||||
body: _t('Please check if the printer has enough paper and is ready to print.'),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var EpsonPrinter = core.Class.extend(PrinterMixin, {
|
||||
init(ip) {
|
||||
PrinterMixin.init.call(this, arguments);
|
||||
var url = window.location.protocol + '//' + ip;
|
||||
this.address = url + '/cgi-bin/epos/service.cgi?devid=local_printer';
|
||||
this.printResultGenerator = new EpsonPrintResultGenerator(url);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Transform a (potentially colored) canvas into a monochrome raster image.
|
||||
* We will use Floyd-Steinberg dithering.
|
||||
*/
|
||||
_canvasToRaster(canvas) {
|
||||
var imageData = canvas.getContext('2d').getImageData(0, 0, canvas.width, canvas.height);
|
||||
var pixels = imageData.data;
|
||||
var width = imageData.width;
|
||||
var height = imageData.height;
|
||||
var errors = Array.from(Array(width), _ => Array(height).fill(0));
|
||||
var rasterData = new Array(width * height).fill(0);
|
||||
|
||||
for (var y = 0; y < height; y++) {
|
||||
for (var x = 0; x < width; x++) {
|
||||
var idx, oldColor, newColor;
|
||||
|
||||
// Compute grayscale level. Those coefficients were found online
|
||||
// as R, G and B have different impacts on the darkness
|
||||
// perception (e.g. pure blue is darker than red or green).
|
||||
idx = (y * width + x) * 4;
|
||||
oldColor = pixels[idx] * 0.299 + pixels[idx+1] * 0.587 + pixels[idx+2] * 0.114;
|
||||
|
||||
// Propagate the error from neighbor pixels
|
||||
oldColor += errors[x][y];
|
||||
oldColor = Math.min(255, Math.max(0, oldColor));
|
||||
|
||||
if (oldColor < 128) {
|
||||
// This pixel should be black
|
||||
newColor = 0;
|
||||
rasterData[y * width + x] = 1;
|
||||
} else {
|
||||
// This pixel should be white
|
||||
newColor = 255;
|
||||
rasterData[y * width + x] = 0;
|
||||
}
|
||||
|
||||
// Propagate the error to the following pixels, based on
|
||||
// Floyd-Steinberg dithering.
|
||||
var error = oldColor - newColor;
|
||||
if (error) {
|
||||
if (x < width - 1) {
|
||||
// Pixel on the right
|
||||
errors[x + 1][y] += 7/16 * error;
|
||||
}
|
||||
if (x > 0 && y < height - 1) {
|
||||
// Pixel on the bottom left
|
||||
errors[x - 1][y + 1] += 3/16 * error;
|
||||
}
|
||||
if (y < height - 1) {
|
||||
// Pixel below
|
||||
errors[x][y + 1] += 5/16 * error;
|
||||
}
|
||||
if (x < width - 1 && y < height - 1) {
|
||||
// Pixel on the bottom right
|
||||
errors[x + 1][y + 1] += 1/16 * error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rasterData.join('');
|
||||
},
|
||||
|
||||
/**
|
||||
* Create the print request for webPRNT from a canvas
|
||||
* Base 64 encode a raster image
|
||||
*/
|
||||
_encodeRaster(rasterData) {
|
||||
var encodedData = '';
|
||||
for(var i = 0; i < rasterData.length; i+=8){
|
||||
var sub = rasterData.substr(i, 8);
|
||||
encodedData += String.fromCharCode(parseInt(sub, 2));
|
||||
}
|
||||
return btoa(encodedData);
|
||||
},
|
||||
|
||||
/**
|
||||
* Create the raster data from a canvas
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
process_canvas: function (canvas) {
|
||||
if (this.printer) {
|
||||
this.printer.addTextAlign(this.printer.ALIGN_CENTER);
|
||||
this.printer.addImage(canvas.getContext('2d'), 0, 0, canvas.width, canvas.height);
|
||||
this.printer.addCut();
|
||||
}
|
||||
process_canvas(canvas) {
|
||||
var rasterData = this._canvasToRaster(canvas);
|
||||
var encodedData = this._encodeRaster(rasterData);
|
||||
return QWeb.render('ePOSPrintImage', {
|
||||
image: encodedData,
|
||||
width: canvas.width,
|
||||
height: canvas.height,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
open_cashbox: function () {
|
||||
if (this.printer) {
|
||||
this.printer.addPulse();
|
||||
this.printer.send();
|
||||
}
|
||||
open_cashbox() {
|
||||
var pulse = QWeb.render('ePOSDrawer');
|
||||
this.send_printing_job(pulse);
|
||||
},
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
send_printing_job: function () {
|
||||
if (this.printer) {
|
||||
this.printer.send();
|
||||
return {
|
||||
result: true
|
||||
};
|
||||
}
|
||||
async send_printing_job(img) {
|
||||
const res = await $.ajax({
|
||||
url: this.address,
|
||||
method: 'POST',
|
||||
data: img,
|
||||
});
|
||||
return $(res).find('response').attr('success') === 'true';
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Handlers
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Not applicable to Epson ePOS
|
||||
* @override
|
||||
*/
|
||||
_onIoTActionFail: function () {},
|
||||
_onIoTActionResult: function () {},
|
||||
});
|
||||
|
||||
return EpsonPrinter;
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
<t t-name="ePOSTemplate">
|
||||
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
|
||||
<s:Body>
|
||||
<epos-print xmlns="http://www.epson-pos.com/schemas/2011/03/epos-print">
|
||||
<t t-raw="0"/>
|
||||
</epos-print>
|
||||
</s:Body>
|
||||
</s:Envelope>
|
||||
</t>
|
||||
|
||||
<t t-name="ePOSPrintImage">
|
||||
<t t-call="ePOSTemplate">
|
||||
<image t-att-width="width" t-att-height="height" align="center" t-esc="image"/>
|
||||
<cut type="feed"/>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
<t t-name="ePOSDrawer">
|
||||
<t t-call="ePOSTemplate">
|
||||
<pulse/>
|
||||
</t>
|
||||
</t>
|
||||
</templates>
|
||||
@@ -4,7 +4,6 @@
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript" src="/pos_epson_printer/static/src/js/pos_epson_printer.js"></script>
|
||||
<script type="text/javascript" src="/pos_epson_printer/static/src/js/printers.js"></script>
|
||||
<script type="text/javascript" src="/pos_epson_printer/static/lib/epos-2.12.0.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
</odoo>
|
||||
|
||||
@@ -16,7 +16,7 @@ var _super_posmodel = models.PosModel.prototype;
|
||||
models.PosModel = models.PosModel.extend({
|
||||
create_printer: function (config) {
|
||||
if (config.printer_type === "epson_epos") {
|
||||
return new EpsonPrinter(config.epson_printer_ip , posmodel);
|
||||
return new EpsonPrinter(config.epson_printer_ip);
|
||||
} else {
|
||||
return _super_posmodel.create_printer.apply(this, arguments);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user