Salut, j’ai demandé à ChatGPT, mais il semble un peu perdu dans ce genre de problème. Le problème n’est pas spécifique au Raspberry Pi, en fait c’est même pour Windows, mais vu que le Python est fortement utilisé et que c’est le seul forum que je suis actif, je me suis dit que je pourrais trouver un début de solution.
Mon projet en gros est de pouvoir gérer des étiquette pour imprimante Zebra et les imprimer, toute cette facette marche, je me suis basé sur mon code pour les imprimante Brother QL que j’avais fait.
Le problème est avec le module Web, qui offre l’interface pour gérer l’étiquette et l’envoyer en impression. Ce script marche parfaitement si je le lance directement avec Python ou si je le compile avec une console visible. Mais l’objectif c’est de ne pas avoir de console pour le lancer.
Je peux cacher la console avec un script en .VBS (Visual Basic Script), mais je préfère éviter.
Alors il faut savoir que j’ai bien sur des logs pour moi, soit avec print()
ou avec showMessage()
, mais eux ne semble pas poser problème. Dans mes tests, je sais que dans tout les cas le script marche et traite les demande correctement.
Le problème est le suivant; en version compilé sans console, toutes demandes Web par un client est accepté mais retourne aucune réponse, pas de donnée, pas d’entête, c’est une fin de connexion sans réponse.
Je pense que le problème vient du module socket, ce code est pris d’un code public et donc je ne comprend pas nécessairement le fonctionnement (comme la partie pour créer la « class »), mais j’ai fais le restant du code en me basant sur l’exemple et l’ajustant avec mes observations. Ce n’est peut-être pas optimal, mais ça marche.
Mais quand le socket reçois une demande, il retourne dans la console une information, soit l’entête HTTP du client (du même style qu’ont retrouve dans les logs de Apache), ce qui est normal. Je sais qu’il apparait APRÈS mes print()
qui commence par « ** ». Alors il doit être généré lors du « return », ou dans les environ. J’ai tenté une recherche pour voir si je peux arrêter ce genre de log (même avant le problème de la version compilé) mais je n’ai rien trouvé.
ChatGPT ma donné quelques lignes pour orienter les logs ailleurs, dans un fichier, dans un Null, … mais mis a part mes propre print()
, les logs du module Socket ne change pas de place. J’ai même tenté de démarrer le serveur en sous processus, j’ai toujours des réponse vide si je n’ai pas de console.
Au final j’aimerais que la version compilé puisse répondre, et je crois que si j’arrive à éliminer les sortie du Socket je réglerais mon problème de la version compilé. Si vous connaissez directement la solution pour la compilation, tant mieu, mais vu que RPi est linux, si vous avez au moins la solution pour « fermer la gueule » du Socket, ce sera déjà ça de gagné et testé.
#!/usr/bin/python3
import urllib.parse as urlparse
import http.server
import socketserver
import os
import subprocess
import configparser
import json
import datetime
import time
from threading import Timer
from urllib.parse import parse_qs
from os import path
from datetime import datetime as dt
from os import listdir
from os.path import isfile, join, isdir
from subprocess import Popen
def is_compiled():
if getattr(sys, 'frozen', False):
return True
else:
return hasattr(sys, '_MEIPASS')
print("Starting Web Server script")
if is_compiled():
scriptpath=os.path.dirname(sys.executable)+"/"
else:
scriptpath=os.path.dirname(os.path.realpath(__file__))+"/"
print(" * Loading configuration file: "+scriptpath+"config.ini")
config = configparser.ConfigParser()
config.read("config.ini", encoding='utf-8')
print("Setting web directory")
WEBPATH=scriptpath + config["web"]["path"]
def is_compiled():
if getattr(sys, 'frozen', False):
return True
else:
return hasattr(sys, '_MEIPASS')
if is_compiled():
import wx
class appMessage:
msg=""
def __init__(self, msg):
self.msg=""
class appDialog(wx.Dialog):
def __init__(self, *args, **kwds):
kwds["style"] = kwds.get("style", 0) | wx.CAPTION | wx.STAY_ON_TOP
wx.Dialog.__init__(self, *args, **kwds)
self.SetTitle("Zebra Label Printer Server - Message")
sizer_1 = wx.BoxSizer(wx.VERTICAL)
self.dialog_text = wx.StaticText(self, wx.ID_ANY, diamsg.msg)
sizer_1.Add(self.dialog_text, 0, wx.ALL | wx.EXPAND, 10)
sizer_2 = wx.StdDialogButtonSizer()
sizer_1.Add(sizer_2, 0, wx.ALIGN_RIGHT | wx.ALL, 4)
self.button_CLOSE = wx.Button(self, wx.ID_CLOSE, "")
sizer_2.AddButton(self.button_CLOSE)
sizer_2.Realize()
self.SetSizer(sizer_1)
sizer_1.Fit(self)
self.SetEscapeId(self.button_CLOSE.GetId())
self.Layout()
self.Centre()
def showMessage(txt):
diamsg.msg=txt
dlg = appDialog(None)
dlg.ShowModal()
app = wx.App(False)
diamsg=appMessage(0)
class MyHttpRequestHandler(http.server.SimpleHTTPRequestHandler):
update = False
def do_GET(self):
if self.path == '/':
self.path = "/index.html"
pathSplit = self.path.split("?")
try:
self.runPage(pathSplit[0],pathSplit[1])
except:
self.runPage(pathSplit[0])
pass
def do_POST(self):
content_length = int(self.headers['Content-Length'])
post_data = self.rfile.read(content_length)
postDatas = post_data.decode("utf-8")
if self.path == '/':
self.path = "/index.html"
pathSplit = self.path.split("?")
try:
self.runPage(pathSplit[0],pathSplit[1],postDatas)
except:
self.runPage(pathSplit[0],"",postDatas)
pass
def runPage(self,page,getDatas="",postDatas=""):
pathSection = page.split("/")
print("** URL: "+page)
print("** _GET: "+getDatas)
print("** _POST: "+postDatas)
try:
getDatas = urlparse.parse_qs(getDatas.replace('"',"''"))
except:
getDatas=""
pass
try:
postDatas = urlparse.parse_qs(postDatas.replace('"',"''"))
except:
postDatas=""
pass
if path.exists(WEBPATH+page) is True:
self.path = page
try:
f = open(WEBPATH+self.path, 'rb')
ctype = self.guess_type(self.path)
fs = os.fstat(f.fileno())
self.send_response(200)
self.send_header("Access-Control-Allow-Origin", "*")
self.send_header("Content-type", ctype)
self.send_header("Content-Length", str(fs[6]))
self.send_header("Last-Modified",
self.date_time_string(fs.st_mtime))
self.end_headers()
try:
self.copyfile(f, self.wfile)
finally:
f.close()
#return
#return http.server.SimpleHTTPRequestHandler.do_GET(self)
except OSError:
self.send_response(404)
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write(bytes('Document requested is not found.', "utf-8"))
return None
elif pathSection[1] == "api":
outputJson={"result":"error","reason":"Invalid action"}
values=""
if pathSection[2] == "images":
imgs=[]
for x in os.listdir("images"):
if x.endswith(".jpg") or x.endswith(".png") or x.endswith(".ico"):
imgs.append(x)
outputJson={"result":"success","reason":"Get images","images":imgs}
elif pathSection[2] == "image":
try:
f = open("images/"+pathSection[3], 'rb')
ctype = self.guess_type("images/"+pathSection[3])
fs = os.fstat(f.fileno())
self.send_response(200)
self.send_header("Access-Control-Allow-Origin", "*")
self.send_header("Content-type", ctype)
self.send_header("Content-Length", str(fs[6]))
self.send_header("Last-Modified",
self.date_time_string(fs.st_mtime))
self.end_headers()
try:
self.copyfile(f, self.wfile)
finally:
f.close()
return
except OSError:
self.send_response(404)
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write(bytes('Document requested is not found.', "utf-8"))
return None
elif pathSection[2] == "templates":
templates=[]
for x in os.listdir("templates"):
if x.endswith(".zpt"):
templates.append(x)
outputJson={"result":"success","reason":"Get templates","templates":templates}
elif pathSection[2] == "template":
result="{"
template = configparser.ConfigParser()
template.read("templates/" + pathSection[3] + ".zpt", encoding='utf-8')
for s in template.sections():
result=result+'"'+str(s)+'":{'
for o,v in template.items(s):
result=result+'"'+str(o)+'":"'+str(v)+'",'
result=result+'},'
result=result+"}"
result=result.replace(",}","}")
outputJson={"result":"success","reason":"Reading template.","datas":json.loads(result)}
self.send_response(200)
self.send_header("Access-Control-Allow-Origin", "*")
self.send_header("Content-type", "application/json")
self.end_headers()
return self.wfile.write(bytes(json.dumps(outputJson), "utf-8"))
elif pathSection[1] == "print":
self.send_response(200)
self.send_header("Access-Control-Allow-Origin", "*")
self.send_header("Content-type", "application/json")
self.end_headers()
outputJson={"result":"error","reason":"Invalid action"}
values=""
try:
if(int(postDatas['copie'])<=0):
copie=1
else:
copie=int(postDatas['copie'])
except:
copie=1
try:
if pathSection[2] == "exemple":
if(postDatas.get('text') is not None):
values=values+' -a text -k "'+str(copie)+'" -t "'+str(postDatas['text'])+'"'
outputJson={"result":"success","reason":"Printing text label."}
self.goPrint(str(values))
else:
outputJson={"result":"error","reason":"Text is missing."}
elif pathSection[2] == "cli":
print("CLI")
if(postDatas.get('data') is not None):
params=str(postDatas['data']).replace('["',"").replace('"]',"").replace("''",'"')
outputJson={"result":"success","reason":"Send to printer script the parameters."}
self.goPrint(" "+params)
else:
print("NO PARAMS")
outputJson={"result":"error","reason":"No parameter to send to printer script."}
except:
outputJson={"result":"error","reason":"Error when processing the request."}
pass
return self.wfile.write(bytes(json.dumps(outputJson), "utf-8"))
elif pathSection[1] == "manage":
print(" * Loading configuration file: "+scriptpath+"config.ini")
config = configparser.ConfigParser()
config.read(scriptpath+"config.ini", encoding='utf-8')
outputJson={"result":"error","reason":"Invalid action"}
if(postDatas.get('password') is not None):
if(str(postDatas.get('password')).replace("['","").replace("']","")==str(config['web']['adminpass'])):
try:
if pathSection[2] == "config":
if pathSection[3] == "load":
result="{"
readconfig = configparser.ConfigParser()
readconfig.read('config.ini', encoding='utf-8')
for s in readconfig.sections():
result=result+'"'+str(s)+'":{'
for o,v in readconfig.items(s):
if(str(o)!="adminpass"):
result=result+'"'+str(o)+'":"'+str(v)+'",'
result=result+'},'
result=result+"}"
result=result.replace(",}","}")
outputJson={"result":"success","reason":"Reading configuration.","datas":json.loads(result)}
elif pathSection[3] == "save":
saveconfig = configparser.ConfigParser()
saveconfig.read('config.ini', encoding='utf-8')
if(postDatas.get('printername') is not None):
saveconfig['printer']['printername']=str(postDatas['printername']).replace("['","").replace("']","")
with open('config.ini', 'w', encoding='utf-8') as configfile: # save
saveconfig.write(configfile)
result="{"
saveconfig = configparser.ConfigParser()
saveconfig.read('config.ini', encoding='utf-8')
for s in saveconfig.sections():
result=result+'"'+str(s)+'":{'
for o,v in saveconfig.items(s):
if(str(o)!="adminpass"):
result=result+'"'+str(o)+'":"'+str(v)+'",'
result=result+'},'
result=result+"}"
result=result.replace(",}","}")
outputJson={"result":"success","reason":"Saving configuration.","datas":json.loads(result)}
else:
print("Error in config")
outputJson={"result":"error","reason":"This action is not valid."}
else:
outputJson={"result":"error","reason":"Request is not valid."}
except Exception as e:
print(f"{type(e).__name__} at line {e.__traceback__.tb_lineno} of {__file__}: {e}")
outputJson={"result":"error","reason":"Error when processing the request."}
pass
else:
outputJson={"result":"error","reason":"Password is incorrect."}
else:
outputJson={"result":"error","reason":"No password or not in POST method."}
self.send_response(200)
self.send_header("Content-type", "application/json")
self.end_headers()
return self.wfile.write(bytes(json.dumps(outputJson), "utf-8"))
else:
self.send_response(404)
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write(bytes('Document requested is not found.', "utf-8"))
return
def goPrint(self,values):
strclean=str(values).replace("['","").replace("']","")
print("Send to printer : "+strclean)
try:
os.system('./zp.exe '+strclean)
#os.system('python ./zp.py '+strclean)
except:
if is_compiled():
showMessage("Can't run the print command.")
else:
print("Can't run the print command.")
def start():
zpWeb = MyHttpRequestHandler
zpWebserver = socketserver.TCPServer(("0.0.0.0", int(config['web']['port'])), zpWeb)
if is_compiled():
showMessage("Starting Web Server at port "+str(config['web']['port'])+".\n\nWeb path : "+WEBPATH)
else:
print("*** RUNNING WEB SERVER AT PORT "+str(config['web']['port'])+" ***")
zpWebserver.serve_forever()
start()