283 lines
7.2 KiB
Python
Executable File
283 lines
7.2 KiB
Python
Executable File
#!/usr/bin/python
|
|
# -*- coding: utf-8 -*-
|
|
#
|
|
# Copyright (C) 2011 Sebastian Lohff <seba@seba-geek.de>
|
|
# Licensed under GPL v3 or later
|
|
|
|
import base64
|
|
import gobject
|
|
import gtk
|
|
import mutex
|
|
import opencv
|
|
from opencv import highgui, adaptors
|
|
import os
|
|
from PIL import Image
|
|
import qrencode
|
|
import Queue
|
|
import re
|
|
import socket
|
|
import struct
|
|
import StringIO
|
|
import subprocess
|
|
import sys
|
|
import threading
|
|
import time
|
|
import urllib
|
|
import zbar
|
|
|
|
sys.path.append("../../../")
|
|
from ether2any import Ether2Any
|
|
from conf import Conf
|
|
|
|
mqueueMutex = threading.Lock()
|
|
squeueMutex = threading.Lock()
|
|
exqueue = Queue.Queue()
|
|
|
|
class QrDisplay(gtk.Window):
|
|
def __init__(self, mqueue, squeue, displog, timeout=250):
|
|
super(QrDisplay, self).__init__()
|
|
|
|
self.timeout = timeout
|
|
self.mqueue = mqueue
|
|
self.squeue = squeue
|
|
self.displog = displog
|
|
|
|
self.fullscreen()
|
|
self.qrimgPadding = 20
|
|
(wx, wy) = self.getHackySize()
|
|
|
|
# build up gui
|
|
self.vbox = gtk.VBox(False, 0)
|
|
self.qrimg = gtk.Image()
|
|
self.qrimg.show()
|
|
self.vbox.pack_start(self.qrimg, True, True, self.qrimgPadding)
|
|
self.vbox.show()
|
|
|
|
self.pbar = gtk.ProgressBar()
|
|
self.pbar.show()
|
|
self.vbox.pack_start(self.pbar, False, False, 0)
|
|
|
|
self.add(self.vbox)
|
|
|
|
# connect signals
|
|
self.connect("destroy", self.quitGui)
|
|
self.set_size_request(wx, wy)
|
|
self.maximize()
|
|
self.set_position(gtk.WIN_POS_CENTER)
|
|
self.show()
|
|
self.qrSet("")
|
|
gobject.timeout_add(self.timeout, self.checkQueue)
|
|
|
|
def quitGui(self, *args, **kwargs):
|
|
exqueue.put(KeyboardInterrupt())
|
|
|
|
def checkQueue(self):
|
|
#print "Check queue...",
|
|
mqueueMutex.acquire()
|
|
if not self.mqueue.empty():
|
|
print "new data available"
|
|
bmsg = self.mqueue.get()
|
|
self.qrSet(bmsg)
|
|
mqueueMutex.release()
|
|
#else:
|
|
# print "no new data available"
|
|
|
|
# progress
|
|
squeueMutex.acquire()
|
|
if not self.squeue.empty():
|
|
(frames, success) = self.squeue.get()
|
|
if frames > 0:
|
|
self.pbar.set_fraction(success/float(frames))
|
|
self.pbar.set_text("%02d / %02d (%03.02f)" % (success, frames, success / float(frames) * 100.0))
|
|
print frames, success
|
|
squeueMutex.release()
|
|
return True
|
|
|
|
def getHackySize(self):
|
|
""" Obtain current size of screen via xrandr. """
|
|
#p = subprocess.Popen(["bash", "-c", 'xrandr|egrep "\*+"|egrep -o "[0-9]+x[0-9]+"'], stdout=subprocess.PIPE)
|
|
p = subprocess.Popen('xrandr|egrep "\*+"|egrep -o "[0-9]+x[0-9]+"', stdout=subprocess.PIPE, shell=True)
|
|
p.wait()
|
|
(myout, myin) = p.communicate()
|
|
return map(lambda x: int(x)-self.qrimgPadding, myout.strip().split("x"))
|
|
|
|
def image2pixbuf(self, im):
|
|
(a, b, c, d) = self.qrimg.get_allocation()
|
|
# correct size, must be 1:1 (so select the smaller one)
|
|
x = min(c-a, d-b)
|
|
if(x < 10):
|
|
x = 100
|
|
imgSize = (x, x)
|
|
im = im.resize(imgSize)
|
|
file1 = StringIO.StringIO()
|
|
im.save(file1, "ppm")
|
|
contents = file1.getvalue()
|
|
file1.close()
|
|
loader = gtk.gdk.PixbufLoader("pnm")
|
|
loader.write(contents, len(contents))
|
|
pixbuf = loader.get_pixbuf()
|
|
loader.close()
|
|
return pixbuf
|
|
|
|
def qrSet(self, msg):
|
|
""" Set content of displayed qr image. """
|
|
if msg == None or msg == "":
|
|
msg = "Katze"
|
|
(qrVersion, qrSize, qrImg) = qrencode.encode(msg, 0)
|
|
self.qrimg.set_from_pixbuf(self.image2pixbuf(qrImg))
|
|
|
|
|
|
class DisplayThread(threading.Thread):
|
|
""" Thread that runs the GTK-GUI to display outgoing network qr codes. """
|
|
def __init__(self, dev, mqueue, squeue, displog):
|
|
threading.Thread.__init__(self)
|
|
self.dev = dev
|
|
self.quit = False
|
|
self.qrdisplay = QrDisplay(mqueue, squeue, displog)
|
|
self.mqueue = mqueue
|
|
self.squeue = squeue
|
|
self.displog = displog
|
|
|
|
def run(self):
|
|
self.displog.info("Display GTK Gui is up and running.")
|
|
try:
|
|
gtk.main()
|
|
except Exception, e:
|
|
exqueue.push(e)
|
|
|
|
class CamThread(threading.Thread):
|
|
""" Captures images from a webcam and decodes them.
|
|
|
|
Captures images from the first webcam it can find, decodes them
|
|
and writes them to the interface. """
|
|
def __init__(self, dev, squeue, camlog):
|
|
threading.Thread.__init__(self)
|
|
|
|
self.dev = dev
|
|
self.squeue = squeue
|
|
self.camlog = camlog
|
|
|
|
self.frame = 0
|
|
self.reportAfter = 20 # frames
|
|
self.quit = False
|
|
self.success = 0
|
|
self.lastPacket = ""
|
|
|
|
self.reader = highgui.cvCreateCameraCapture(Conf.get("camnum", 0))
|
|
self.scanner = zbar.ImageScanner()
|
|
self.scanner.parse_config('enable')
|
|
|
|
def run(self):
|
|
try:
|
|
while not self.quit:
|
|
frame = highgui.cvQueryFrame(self.reader)
|
|
self.frame += 1
|
|
|
|
frame = opencv.cvGetMat(frame)
|
|
img = adaptors.Ipl2PIL(frame)
|
|
width, height = img.size
|
|
zimg = zbar.Image(width, height, 'Y800', img.convert("L").tostring())
|
|
self.scanner.scan(zimg)
|
|
data = None
|
|
for symbol in zimg:
|
|
data = symbol.data
|
|
self.camlog.debug("Data is: %s" % data)
|
|
self.success += 1
|
|
# handle data
|
|
if not self.lastPacket == data:
|
|
self.lastPacket = data
|
|
try:
|
|
msg = base64.b64decode(data)
|
|
(rawtime, packet) = (msg[0:8], msg[8:])
|
|
ptime = struct.unpack("<d", rawtime)
|
|
self.camlog.debug("Network packet! Heade (time) is %s" % (ptime,))
|
|
self.dev.write(packet)
|
|
except (base64.binascii.Error, TypeError):
|
|
self.camlog.error("Base64 error - could not decode packet")
|
|
except struct.error:
|
|
self.camlog.error("Header error - could not extract header information")
|
|
else:
|
|
# packet is already known, discard
|
|
pass
|
|
|
|
# status report to gui
|
|
if self.frame % self.reportAfter == 0:
|
|
self.frame = self.success = 0
|
|
|
|
squeueMutex.acquire()
|
|
if self.squeue.qsize() > self.reportAfter/2:
|
|
while not self.squeue.empty():
|
|
self.squeue.get()
|
|
# add new status code
|
|
self.squeue.put((self.frame, self.success))
|
|
squeueMutex.release()
|
|
except Exception, e:
|
|
exqueue.put(e)
|
|
raise e
|
|
|
|
|
|
class QrNet(Ether2Any):
|
|
pidlen = 16
|
|
def __init__(self):
|
|
# device
|
|
Ether2Any.__init__(self, tap=True)
|
|
self.qrlog = self.setupLogging("QrNet")
|
|
self.mqueue = Queue.Queue()
|
|
self.squeue = Queue.Queue()
|
|
self.setTimeout(1)
|
|
|
|
network = Conf.get("network", {'mtu': 400})
|
|
self.packetDrop = Conf.get("packetDrop", 20)
|
|
|
|
self.dev.ifconfig(**network)
|
|
self.dev.up()
|
|
|
|
# thread starting...
|
|
gtk.gdk.threads_init()
|
|
|
|
self.cam = CamThread(self.dev, self.squeue, self.setupLogging("CamThread"))
|
|
self.cam.start()
|
|
self.display = DisplayThread(self.dev, self.mqueue, self.squeue, self.setupLogging("DisplayThread"))
|
|
self.display.start()
|
|
|
|
def sendToNet(self, msg):
|
|
# prepare data for queue && display
|
|
self.qrlog.debug("Data from the device")
|
|
|
|
# add acttime to generate "unique" images
|
|
acttime = struct.pack("<d", time.time())
|
|
bmsg = base64.b64encode(acttime + msg)
|
|
self.qrlog.debug("==>" + bmsg)
|
|
|
|
# add packet to queue, maybe drop packet
|
|
mqueueMutex.acquire()
|
|
if self.mqueue.qsize() < self.packetDrop:
|
|
self.mqueue.put(bmsg)
|
|
else:
|
|
self.qrlog.debug("Dropping packet!")
|
|
mqueueMutex.release()
|
|
|
|
def callAfterSelect(self):
|
|
# check queue for excetions
|
|
if exqueue.qsize() > 0:
|
|
ex = exqueue.get()
|
|
raise ex
|
|
|
|
def quit(self):
|
|
self.display.quit = True
|
|
self.cam.quit = True
|
|
gtk.main_quit()
|
|
|
|
if __name__ == '__main__':
|
|
try:
|
|
qrnet = QrNet()
|
|
qrnet.run()
|
|
except KeyboardInterrupt:
|
|
try:
|
|
qrnet.quit()
|
|
except NameError:
|
|
pass
|
|
sys.exit(0)
|
|
except Exception, e:
|
|
raise e
|