|
|
|
@ -1,4 +1,5 @@
|
|
|
|
|
# bureau
|
|
|
|
|
import configparser
|
|
|
|
|
import functools
|
|
|
|
|
import glob
|
|
|
|
|
import inspect
|
|
|
|
@ -12,13 +13,11 @@ import string
|
|
|
|
|
import subprocess
|
|
|
|
|
import tempfile
|
|
|
|
|
import textwrap
|
|
|
|
|
#import traceback
|
|
|
|
|
import threading
|
|
|
|
|
|
|
|
|
|
import lmdb
|
|
|
|
|
import PIL
|
|
|
|
|
import weasyprint
|
|
|
|
|
from weasyprint.fonts import FontConfiguration
|
|
|
|
|
import zmq
|
|
|
|
|
from escpos import printer
|
|
|
|
|
from mako.template import Template
|
|
|
|
@ -63,30 +62,24 @@ def add_api(apistr, name=""):
|
|
|
|
|
return decorator
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#def log_traceback(func):
|
|
|
|
|
# """ this is a decorator that catches tracebacks for logging"""
|
|
|
|
|
# def wrapper(*args):
|
|
|
|
|
# my_bureau = args[0]
|
|
|
|
|
# try:
|
|
|
|
|
# func(*args)
|
|
|
|
|
# except Exception as e:
|
|
|
|
|
# my_bureau.log.error("CRASH TRACE: {0}".format(my_bureau.name),
|
|
|
|
|
# exc_info=e)
|
|
|
|
|
# raise
|
|
|
|
|
# return wrapper
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class LogPrinter(logging.Handler):
|
|
|
|
|
"""
|
|
|
|
|
LogPrinter prints logs on a receipt printer for screenless debugging.
|
|
|
|
|
"""
|
|
|
|
|
def __init__(self):
|
|
|
|
|
def __init__(self, log_printer):
|
|
|
|
|
self.printer = log_printer
|
|
|
|
|
logging.Handler.__init__(self)
|
|
|
|
|
|
|
|
|
|
def emit(self, record):
|
|
|
|
|
prn = printer.Usb(0x416, 0x5011, in_ep=0x81, out_ep=0x03)
|
|
|
|
|
if (self.printer["inep"] is None) and (self.printer["outep"] is None):
|
|
|
|
|
prn = printer.Usb(self.printer["vendorid"], self.printer["productid"])
|
|
|
|
|
else:
|
|
|
|
|
prn = printer.Usb(self.printer["vendorid"],
|
|
|
|
|
self.printer["productid"],
|
|
|
|
|
in_ep=self.printer["inep"],
|
|
|
|
|
out_ep=self.printer["outep"])
|
|
|
|
|
msg = self.format(record)
|
|
|
|
|
text = textwrap.fill(msg, width=48)
|
|
|
|
|
text = textwrap.fill(msg, width=self.printer["textwidth"])
|
|
|
|
|
text += "\r\n" * 4
|
|
|
|
|
prn.text(text)
|
|
|
|
|
prn.cut()
|
|
|
|
@ -162,7 +155,7 @@ class Bureau(object):
|
|
|
|
|
default_config = {}
|
|
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
|
""" set up ZeroMQ connections and register commands"""
|
|
|
|
|
""" set up ZeroMQ connections, printers, fonts and register commands"""
|
|
|
|
|
self.commands = {}
|
|
|
|
|
self.api = {}
|
|
|
|
|
|
|
|
|
@ -187,6 +180,36 @@ class Bureau(object):
|
|
|
|
|
|
|
|
|
|
self.load_config()
|
|
|
|
|
|
|
|
|
|
# load printer configs
|
|
|
|
|
printcfg = configparser.ConfigParser()
|
|
|
|
|
try:
|
|
|
|
|
printcfg.read("printers.cfg")
|
|
|
|
|
self.smprint = object()
|
|
|
|
|
self.smprint["vendorid"] = printcfg["smallprinter"]["vendorid"]
|
|
|
|
|
self.smprint["prodid"] = printcfg["smallprinter"]["productid"]
|
|
|
|
|
self.smprint["in_ep"] = printcfg["smallprinter"]["inep"]
|
|
|
|
|
self.smprint["out_ep"] = printcfg["smallprinter"]["outep"]
|
|
|
|
|
self.smprint["width"] = printcfg["smallprinter"]["width"]
|
|
|
|
|
self.smprint["textwidth"] = printcfg["smallprinter"]["textwidth"]
|
|
|
|
|
|
|
|
|
|
self.lp["name"] = printcfg["largeprinter"]["name"]
|
|
|
|
|
self.lp["papersize"] = printcfg["largeprinter"]["papersize"]
|
|
|
|
|
self.lp["duplex"] = printcfg["largeprinter"]["duplex"]
|
|
|
|
|
except KeyError:
|
|
|
|
|
# TODO: eventually refactor this since it could overwrite a half-broken config
|
|
|
|
|
sp_dict = {"vendorid": None, "productid": None,
|
|
|
|
|
"inep": None, "outep": None, "width": 384,
|
|
|
|
|
"textwidth": 32}
|
|
|
|
|
lp_dict = {"name": None, "papersize": "A4",
|
|
|
|
|
"duplex": False}
|
|
|
|
|
printcfg["smallprinter"] = sp_dict
|
|
|
|
|
printcfg["largeprinter"] = lp_dict
|
|
|
|
|
with open("printers.cfg", "w") as print_conf_file:
|
|
|
|
|
printcfg.write(print_conf_file)
|
|
|
|
|
self.smprint = sp_dict
|
|
|
|
|
self.lp = lp_dict
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# setup log file
|
|
|
|
|
if "debug" in self.config:
|
|
|
|
|
if self.config["debug"]:
|
|
|
|
@ -198,7 +221,7 @@ class Bureau(object):
|
|
|
|
|
logfile = os.path.join(basepath, self.prefix + ".log")
|
|
|
|
|
logging.basicConfig(filename=logfile, level=log_level)
|
|
|
|
|
self.log = logging.getLogger(self.prefix)
|
|
|
|
|
log_printer = LogPrinter()
|
|
|
|
|
log_printer = LogPrinter(self.smprint)
|
|
|
|
|
log_format = logging.Formatter('LOG ${levelname} $name: $message', style='$')
|
|
|
|
|
log_printer.setFormatter(log_format)
|
|
|
|
|
self.log.addHandler(log_printer)
|
|
|
|
@ -352,9 +375,14 @@ class Bureau(object):
|
|
|
|
|
"""
|
|
|
|
|
print on Thermal Line printer.
|
|
|
|
|
"""
|
|
|
|
|
# TODO: look up device and width in config
|
|
|
|
|
prn = printer.Usb(0x416, 0x5011, in_ep=0x81, out_ep=0x03)
|
|
|
|
|
text = textwrap.fill(text, width=48)
|
|
|
|
|
if (self.smprint["inep"] is None) and (self.smprint["outep"] is None):
|
|
|
|
|
prn = printer.Usb(self.smprint["vendorid"], self.smprint["productid"])
|
|
|
|
|
else:
|
|
|
|
|
prn = printer.Usb(self.smprint["vendorid"],
|
|
|
|
|
self.smprint["productid"],
|
|
|
|
|
in_ep=self.smprint["inep"],
|
|
|
|
|
out_ep=self.smprint["outep"])
|
|
|
|
|
text = textwrap.fill(text, width=self.smprint["textwidth"])
|
|
|
|
|
text += "\r\n" * 2
|
|
|
|
|
prn.text(text + "\r\n\r\n")
|
|
|
|
|
if cut:
|
|
|
|
@ -364,8 +392,13 @@ class Bureau(object):
|
|
|
|
|
"""
|
|
|
|
|
print an image on the mini thermal printer.
|
|
|
|
|
"""
|
|
|
|
|
# TODO: make printer id/width configured and easy
|
|
|
|
|
prn = printer.Usb(0x416, 0x5011, in_ep=0x81, out_ep=0x03)
|
|
|
|
|
if (self.smprint["inep"] is None) and (self.smprint["outep"] is None):
|
|
|
|
|
prn = printer.Usb(self.smprint["vendorid"], self.smprint["productid"])
|
|
|
|
|
else:
|
|
|
|
|
prn = printer.Usb(self.smprint["vendorid"],
|
|
|
|
|
self.smprint["productid"],
|
|
|
|
|
in_ep=self.smprint["inep"],
|
|
|
|
|
out_ep=self.smprint["outep"])
|
|
|
|
|
|
|
|
|
|
if type(img) is PIL.Image.Image:
|
|
|
|
|
im = img
|
|
|
|
@ -374,8 +407,10 @@ class Bureau(object):
|
|
|
|
|
|
|
|
|
|
# NOTE: might be worth tring to push up brightness
|
|
|
|
|
im = PIL.ImageOps.equalize(im) # stretch histogram for nicer dither
|
|
|
|
|
im.thumbnail((576, 1024), PIL.Image.ANTIALIAS) # resize to fit printer
|
|
|
|
|
prn.image(im, impl="bitImageColumn") # not using this impl crashes ??
|
|
|
|
|
# resize to fit printer
|
|
|
|
|
im.thumbnail((self.smprint["width"], 1024), PIL.Image.ANTIALIAS)
|
|
|
|
|
# not using this bitImageColumn crashes some printers, sigh
|
|
|
|
|
prn.image(im, impl="bitImageColumn")
|
|
|
|
|
|
|
|
|
|
@add_command("test")
|
|
|
|
|
def test(self, data=None):
|
|
|
|
|