Procházet zdrojové kódy

working on PIU application

* experimenting with Python standalone application
clementinecomputing před 4 roky
rodič
revize
fa654be6a2
1 změnil soubory, kde provedl 256 přidání a 0 odebrání
  1. 256 0
      busunit-PIU/scripts/piu_app

+ 256 - 0
busunit-PIU/scripts/piu_app

@@ -0,0 +1,256 @@
+#!/usr/bin/python3
+#
+# Copyright (c) 2021 Clementine Computing LLC.
+#
+# This file is part of PopuFare.
+#
+# PopuFare 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.
+#
+# PopuFare 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 PopuFare.  If not, see <https://www.gnu.org/licenses/>.
+#
+
+# This is the front end interface to the PIU that will run as
+# a Python application.
+# It displays the video stream and overlays the barcode, if it finds one.
+# It scans in the directory:
+#
+#   /home/bus/queue
+#
+# for messages. If found, it then moves them to:
+#
+#  /home/bus/log/messages
+#
+# Only the last message (after filename sorting) is chosen.
+# The first line in the message file is the status message.
+# The second line is the error message.
+# Either or both lines can be blank without consequence.
+#
+# QR Codes are logged in:
+#
+#  /home/bus/log/qrcode.log
+#
+
+
+import sys
+import os
+
+import tkinter as tk
+from PIL import Image, ImageTk
+import cv2
+import signal
+import tkinter.font as tkfont
+
+import numpy as np
+from pyzbar import pyzbar
+import datetime
+import imutils
+import time
+
+from imutils.video.pivideostream import PiVideoStream
+from imutils.video import FPS
+from picamera.array import PiRGBArray
+from picamera import PiCamera
+
+def handler(ev):
+  root.destroy()
+
+def check():
+  root.after(500, check)
+
+class MainWindow():
+  def __init__(self, window, pistream):
+
+    self.display_msg = ""
+    self.display_msg_error = ""
+    self.display_msg_idle = "READY"
+    self.display_msg_t = 10000
+    self.display_msg_start = 0
+    self.display_msg_end = 0
+
+    self.message_dir = "/home/bus/queue"
+    self.processed_message_dir = "/home/bus/log/message"
+    self.log_dir = "/home/bus/log"
+    self.barcode_ofn = os.path.join(self.log_dir, "qrcode.log")
+
+    self.pistream = pistream
+
+    pad = 0
+    self.disp_pad = pad
+    self._orig_geom = "{0}x{1}+0+0".format( window.winfo_screenwidth()-pad, window.winfo_screenheight()-pad )
+
+    self.screen_width = window.winfo_screenwidth()
+    self.screen_height = window.winfo_screenheight()
+
+    window.attributes("-fullscreen", True)
+
+    self.cap_width = 400
+    self.cap_height = 300
+
+    self.window = window
+
+    self.interval = 30
+
+    self.canvas = tk.Canvas(self.window, width=self.screen_width, height=self.screen_height, bg='#000', bd=0, highlightthickness=0, relief='ridge')
+    self.canvas.grid(row=0, column=0)
+
+    self.RATE_LIMIT_MS = 1500.0
+    self.LAST_TOK = ""
+    #self.barcode_ofp = open("/tmp/qrcode.log", "a")
+    #self.barcode_ofp = open("/home/bus/log/qrcode.log", "a")
+
+    self.t_prv = time.time()*1000.0
+    self.t_now = self.t_prv
+
+    self.update_loop()
+
+  def log_barcode(self, data):
+    with open( self.barcode_ofn, "a" ) as ofp:
+      ofp.write( data  + "\n")
+      ofp.flush()
+
+  def message_scan(self):
+    msg_fns = []
+    for rootdir, dirs, fns in os.walk( self.message_dir ):
+      for fn in fns:
+        msg_fns.append(fn)
+
+    msg_fns.sort()
+    for msg_fn in msg_fns:
+      print(">>>", msg_fn)
+      src_fq_msg_fn = os.path.join(self.message_dir, msg_fn)
+      dst_fq_msg_fn = os.path.join(self.processed_message_dir, msg_fn)
+
+      with open(src_fq_msg_fn) as fp:
+        msg_a = fp.read().split("\n")
+        self.display_msg = msg_a[0][0:10]
+        if len(msg_a) > 1:
+          self.display_msg_error = msg_a[1][0:10]
+        self.display_msg_start = time.time() * 1000.0
+        self.display_msg_end = self.display_msg_start + self.display_msg_t
+
+      os.replace(src_fq_msg_fn, dst_fq_msg_fn)
+
+  def message_update(self):
+    t = time.time() * 1000.0
+
+    if t > self.display_msg_end:
+      self.display_msg = ""
+      self.display_msg_error = ""
+
+      self.display_msg = self.display_msg_idle
+
+
+  def update_loop(self):
+
+    self.message_scan()
+    self.message_update()
+
+    img_cv2 = None
+    img_cv2 = self.pistream.read()
+    img_cv2 = imutils.resize(img_cv2, width=self.cap_width)
+
+    ## overlay barcode rectanges and text on cv2 image
+    ##
+    barcodes = pyzbar.decode(img_cv2)
+    for barcode in barcodes:
+      (x,y,w,h) = barcode.rect
+      cv2.rectangle(img_cv2, (x,y), (x+w, y+h), (0, 0, 255), 2)
+
+      barcode_data = barcode.data.decode("utf-8")
+      barcode_type = barcode.type
+
+      show_text = "{} ({})".format(barcode_data, barcode_type)
+
+      cv2.putText(img_cv2, show_text, (x, y - 10),
+        cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)
+
+      self.t_now = time.time()*1000.0
+
+      if self.LAST_TOK != barcode_data:
+        #self.barcode_ofp.write( str(int(time.time())) + ": " + barcode_data + "\n")
+        #self.barcode_ofp.flush()
+        self.log_barcode( str(int(time.time())) + ": " + barcode_data )
+        self.t_prv = self.t_now
+        self.LAST_TOK = barcode_data
+      elif (self.t_now - self.t_prv) >= self.RATE_LIMIT_MS:
+        #self.barcode_ofp.write( str(int(time.time())) + ": " + barcode_data + "\n")
+        #self.barcode_ofp.flush()
+        self.log_barcode( str(int(time.time())) + ": " + barcode_data )
+        self.t_prv = self.t_now
+        self.LAST_TOK = barcode_data
+      else:
+        pass
+
+
+    # convert cv2 image to something that tkinter can display
+    #
+       
+    #self.image = cv2.cvtColor(self.cap.read()[1], cv2.COLOR_BGR2RGB)
+    self.image = cv2.cvtColor(img_cv2, cv2.COLOR_BGR2RGB)
+    self.image = cv2.flip(self.image, 1)
+    self.image = Image.fromarray(self.image)
+    self.image = ImageTk.PhotoImage(self.image)
+
+    dy = int(self.screen_height - self.cap_height)
+    dx = int((self.screen_width - self.cap_width)/2)
+    dx -= 1
+    if dx<0: dx=0
+
+    midx = int(self.screen_width/2)
+
+    _now = datetime.datetime.now()
+    yyyymmdd = _now.strftime("%Y-%m-%d")
+    time_str = _now.strftime("%a %I") + [":", " "][int(_now.strftime("%S"))%2] + _now.strftime("%M%p")
+
+    char_width = 10
+    #text_msg = [ "2021-06-01", "Tue 10:53AM", "See Driver", "No Passes", "on Card" ]
+    text_msg = [ yyyymmdd, time_str, self.display_msg, self.display_msg_error ]
+    text_msg_dy = [ 60, 80, 80, 60 , 0 ]
+
+    self.canvas.delete("all")
+
+    txt_dy = 80
+    txt_x = midx
+    txt_y = int(txt_dy/2)
+    for idx,msg in enumerate(text_msg):
+      font = None
+      if idx==0:
+        font = tkfont.Font(family="monospace", weight="bold", size=60)
+      else:
+        font = tkfont.Font(family="monospace", weight="bold", size=50)
+
+
+      color = "#aaa"
+      if idx == 2:
+        color = "#6194fa"
+      elif idx > 2:
+        color = "#fa6171"
+
+      self.canvas.create_text(txt_x, txt_y, anchor=tk.CENTER, text=msg, font=font, fill=color)
+      #txt_y += txt_dy
+      txt_y += text_msg_dy[idx]
+
+
+    self.canvas.create_image(dx, dy, anchor=tk.NW, image=self.image)
+    self.window.after(self.interval, self.update_loop)
+
+if __name__ == "__main__":
+    root = tk.Tk()
+    pistream = PiVideoStream().start()
+    time.sleep(2.0)
+    MainWindow(root, pistream)
+
+    signal.signal(signal.SIGINT, lambda x,y : print('terminal ^C') or handler(None))
+    root.after(500, check)
+    root.bind_all('<Control-c>', handler)
+    root.configure(bg='#000')
+    root.mainloop()