Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
raspytime/uhr-multithreading.py
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
executable file
404 lines (361 sloc)
12.9 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
# -*- coding: utf-8 -*- | |
# neuer Versuch | |
# cleaner | |
# anzeige MUSS mit lock gemacht werden da sonst evtl idle anzeige unterbrochen wird im schreibvorgang, | |
# und dann die auswertung mit ihrer anzeige nicht mehr auf /ram/temp.bmp zugreifen kann -> aufgehängt | |
# Python Imports | |
import os | |
import sys | |
import subprocess | |
import threading | |
import pygame | |
import time | |
import math | |
import socket | |
import MySQLdb | |
import serial | |
import signal | |
import locale | |
import RPi.GPIO as GPIO | |
import ConfigParser | |
# log program launch | |
print "-------START--------" | |
print "started at" + time.strftime("%D -- %H:%M:%S", time.localtime()) | |
# GPIO Setup | |
GPIO.setmode(GPIO.BCM) | |
GPIO.setup(19, GPIO.OUT) | |
GPIO.setup(20, GPIO.IN) | |
GPIO.setup(21, GPIO.IN) | |
GPIO.output(19, GPIO.LOW) | |
# Wochentag in Deutsch | |
locale.setlocale(locale.LC_ALL, 'de_DE.UTF-8') | |
# Datenbank Verbindung | |
#get from ini file | |
config = ConfigParser.ConfigParser() | |
config.read('db.ini') | |
database = dict(config.items('db')) | |
print "loaded database connection parameters from file" | |
# init vars | |
lock = threading.Lock() | |
texttime = None | |
dienstgangstatus = False | |
saldostatus = False | |
queue = [] | |
network = False | |
offline = False | |
stuck = False | |
DEVNULL = open(os.devnull, "wb") | |
# setup pygame | |
pygame.init() | |
# setup display resolution should match | |
# dont forget to adjust layout aswell | |
# if running in an X environment, fullscreen and no cursor should be enabled | |
window = pygame.display.set_mode((320,240)) | |
#pygame.display.toggle_fullscreen() | |
#pygame.mouse.set_visible(0) | |
minerva = pygame.image.load("minerva.png") | |
font0 = pygame.font.SysFont("droidsans", 21) | |
font1 = pygame.font.SysFont("droidsans", 83) | |
font2 = pygame.font.SysFont("droidsans", 33) | |
font3 = pygame.font.SysFont("droidsans", 30) | |
font2.set_italic(1) | |
font2.set_bold(1) | |
label0 = font0.render("Fritz-Haber-Institut Berlin", 1, (255,255,255)) | |
offlinelabel = font0.render("offline", 1, (232,12,122)) | |
label3 = font0.render(socket.gethostname(), 1, (255,255,255)) | |
label4 = font3.render("Dienstgang >>", 1, (255,255,255)) | |
label5 = font3.render("Saldo abrufen >>", 1, (255,255,255)) | |
rect2 = pygame.Rect(5, 125, 310, 5) | |
def anzeige(name, status, banner): | |
window.fill([0,0,0]) | |
window.blit(minerva, (0,0)) | |
label4_hpos = (315 - label4.get_width()) | |
label5_hpos = (315 - label5.get_width()) | |
window.blit(label4, (label4_hpos,30)) | |
window.blit(label5, (label5_hpos,180)) | |
if banner == "kommt": | |
color = (161,255,24) | |
if banner == "geht": | |
color = (255,0,0) | |
if banner == "minerva": | |
color = (45,232,225) | |
label1 = font1.render(status, 1, (255,255,255)) | |
label2 = font2.render(name, 1, (255,255,255)) | |
label1_hpos = (320 - label1.get_width()) / 2 | |
label2_hpos = (320 - label2.get_width()) / 2 | |
pygame.draw.rect(window, color, rect2) | |
window.blit(label0, (0,225)) | |
window.blit(label1, (label1_hpos,65)) | |
window.blit(label2, (label2_hpos,135)) | |
window.blit(label3, (260,225)) | |
global offline | |
try: | |
subprocess.check_call(['timeout', '0.2s', 'ping', '-c1', database['host']], stdout=DEVNULL, stderr=DEVNULL) | |
if offline: | |
print status + ": back online!" | |
offline = False | |
except: | |
window.blit(offlinelabel, (0,0)) | |
if not offline: | |
print status + ": went offline!" | |
offline = True | |
# this is for the C-Berry Display | |
pygame.image.save(window, "/ram/temp.bmp") | |
os.system("./tft_bmp /ram/temp.bmp") | |
# this is for X environment | |
#pygame.display.flip() | |
def idle(): | |
global texttime | |
while True: | |
while not lock.locked(): | |
oldtime = texttime | |
lt = time.localtime() | |
texttime = time.strftime("%H:%M:%S", lt) | |
if dienstgangstatus and not saldostatus: | |
name = "Dienstgang?" | |
elif saldostatus and not dienstgangstatus: | |
name = "Saldo?" | |
elif not saldostatus and not dienstgangstatus: | |
name = time.strftime("%a %d.%m.%Y -- KW: %V", lt) | |
if oldtime != texttime or GPIO.input(20) == 0 or GPIO.input(21) == 0 : | |
lock.acquire() | |
anzeige(name, texttime, "minerva") | |
lock.release() | |
time.sleep(0.01) | |
time.sleep(0.01) | |
def serialRead(): | |
serialData = serial.Serial(port='/dev/ttyAMA0',baudrate=9600) | |
ID = '' | |
ID2 = '' | |
while True: | |
while not lock.locked(): | |
if serialData.read() == '\x02' and ID == '': | |
ID = serialData.read(12) | |
serialData.flushInput() | |
if serialData.read() == '\x02' and ID != '': | |
ID2 = serialData.read(12) | |
print "ID: " + ID + " -- ID2: " + ID2 | |
if ID == ID2: | |
ID = ID.replace('\x00', '0') | |
auswertung(ID, None, False) | |
serialData.flushInput() | |
ID = '' | |
ID2 = '' | |
time.sleep(0.01) | |
time.sleep(0.01) | |
def knopf(): | |
global dienstgangstatus | |
global saldostatus | |
while True: | |
while not lock.locked(): | |
if GPIO.input(20) == 0 and not saldostatus: | |
print "pressed dienstgang button" | |
dienstgangstatus = not dienstgangstatus | |
time.sleep(0.3) | |
if GPIO.input(21) == 0 and not dienstgangstatus: | |
print "pressed saldo button" | |
saldostatus = not saldostatus | |
time.sleep(0.3) | |
time.sleep(0.01) | |
time.sleep(0.01) | |
def auswertung(ID, zeit, silent): | |
global db | |
global dienstgangstatus | |
global saldostatus | |
global queue | |
lock.acquire() | |
lt = time.localtime() | |
print "auswertung started!" | |
if not zeit: | |
jetzt = time.strftime("%Y-%m-%d %H:%M:%S", lt) | |
else: | |
print "custom time!" | |
jetzt = zeit | |
print(jetzt) | |
try: | |
subprocess.check_call(['timeout', '0.2s', 'ping', '-c1', database['host']], stdout=DEVNULL, stderr=DEVNULL) | |
network = True | |
except: | |
network = False | |
if network: | |
db = MySQLdb.connect(host=database["host"], | |
user=database["user"], | |
passwd=database["passwd"], | |
db=database["db"]) | |
dbc = db.cursor() | |
#alt: | |
#dbc.execute("select rfid, t.maID, concat(nachname, ', ', vorname) as name from Transponder t join Mitarbeiter m on t.maID = m.maID where t.rfid = %s;", [ID]) | |
dbc.execute("select rfid, t.maID, concat(nachname, ', ', vorname) as name from transponder t join mitarbeiter m on t.maID = m.maID where t.rfid = %s;", [ID]) | |
ma = dbc.fetchone() | |
else: | |
ma = None | |
if ma != None and network: | |
maID = ma[1] | |
name = ma[2] | |
print "MA is: " + name | |
print "ID is: " + ID | |
if saldostatus: | |
# neue DB: saldo direkt aus DB lesen | |
dbc.execute("select sum(saldomin) from saldo where maID = %s;", [maID]) | |
saldo = dbc.fetchone() | |
#alt: | |
#sshline = "ssh saldoread@chronos.rz-berlin.mpg.de /var/www/timerec/saldo_akt.php " + str(maID) | |
#status = subprocess.check_output(["ssh", "saldoread@chronos.rz-berlin.mpg.de", "/var/www/timerec/saldo_akt.php", str(maID)])[:-1] | |
if saldo[0] < 0: | |
banner = "geht" | |
vorzeichen = "-" | |
else: | |
banner = "kommt" | |
vorzeichen = "+" | |
saldostd = int(math.floor(abs(saldo[0]/60))) | |
saldomin = abs(saldo[0]%60) | |
if saldostd < 10: | |
saldostd = "0" + str(saldostd) | |
if saldomin < 10: | |
saldomin = "0" + str(saldomin) | |
status = vorzeichen + str(saldostd) + ":" + str(saldomin) | |
print "saldo is: " + status | |
saldostatus = False | |
else: | |
#alt: | |
#dbc.execute("select status from MA_Zeit where maID = %s and datumzeit <= %s order by datumzeit desc, mz_ID desc limit 1;", (maID, jetzt)) | |
dbc.execute("select status from zeit where maID = %s and datumzeit <= %s and date(datumzeit) = %s and status != 'Dienst' order by datumzeit desc, zeit_ID desc limit 1;", (maID, jetzt, jetzt[:-9])) | |
status = dbc.fetchone() | |
print "status is: " | |
print(status) | |
if status != None: | |
status = status[0] | |
if status == "geht" or status == None: | |
status = "kommt" | |
anwesend = 1 | |
elif status == "kommt": | |
status = "geht" | |
anwesend = 0 | |
print "status after if is: " + status | |
if dienstgangstatus: | |
# Dienstgang wird statt "kommt" oder "geht" eingesetzt. so nimmt dies keinen Einfluss auf den Saldo oder andere Aktivitäten, der Dienstgang wird aber dennoch registriert. | |
status = 'Dienst' | |
dienstgangstatus = False | |
banner = "minerva" | |
else: | |
banner = status | |
#alt: | |
#dbc.execute("update Mitarbeiter set anwesend=%s, statusa=%s where maID = %s;", (anwesend, status, maID)) # für alte uhren und alte DB | |
#dbc.execute("insert into MA_Zeit (maID, datumzeit, status, zt_ID, userid) values (%s, %s, %s, %s, %s);", (maID, jetzt, status, "0", socket.gethostname())) | |
dbc.execute("insert into zeit (maID, datumzeit, status, userid) values (%s, %s, %s, %s);", (maID, jetzt, status, socket.gethostname())) | |
#alt: | |
#if dienstgangstatus: | |
# dbc.execute("select last_insert_id();") | |
# last_insert = dbc.fetchone()[0] | |
# dbc.execute("update MA_Zeit set mz_bem = '#A' where mz_ID = %s", [last_insert]) | |
# dienstgangstatus = False | |
#banner = status | |
db.commit() | |
dbc.close() | |
db.close() | |
elif ma == None and network: | |
name = ID | |
status = "N/A" | |
banner = "minerva" | |
print "mailing unknown ID " + ID | |
sendmail = "./mailscript.sh "+ID | |
sendmail = sendmail.replace('\x00', '') | |
os.system(sendmail) | |
else: | |
name = "wird nachgereicht" | |
status = "OK" | |
banner = "minerva" | |
print "currently offline. saving results." | |
queue.append((jetzt, ID)) | |
print "now in queue:" | |
print(queue) | |
if not silent: | |
print "display data!" | |
anzeige(name, status.upper(), banner) | |
GPIO.output(19, GPIO.HIGH) | |
time.sleep(0.3) | |
GPIO.output(19, GPIO.LOW) | |
time.sleep(1.2) | |
print "auswertung finished!" | |
lock.release() | |
def offlinesubmit(): | |
global queue | |
while True: | |
try: | |
subprocess.check_call(['timeout', '0.2s', 'ping', '-c1', database['host']], stdout=DEVNULL, stderr=DEVNULL) | |
if len(queue) >= 1: | |
print "submitting offline counts..." | |
for zeit in queue: | |
print "current item: " + zeit[1] + " @ " + zeit[0] | |
auswertung(zeit[1], zeit[0], True) | |
queue = [] | |
except: | |
continue | |
time.sleep(1) | |
def unstuck(): | |
global stuck | |
oldtime = None | |
while True: | |
if oldtime == texttime and not oldtime == None: | |
print "we're stuck!" | |
stuck = True | |
oldtime = texttime | |
time.sleep(3) | |
def timeout(): | |
global saldostatus | |
global dienstgangstatus | |
while True: | |
if saldostatus or dienstgangstatus: | |
time.sleep(10) | |
if saldostatus or dienstgangstatus: | |
saldostatus = False | |
dienstgangstatus = False | |
print "10 secs passed, resetting buttons to none..." | |
time.sleep(0.1) | |
def signalhandler(signum, frame): | |
global stuck | |
stuck = True | |
signal.signal(signal.SIGTERM, signalhandler) | |
try: | |
# C-Berry Display init | |
os.system("./tft_init") | |
os.system("./tft_clear") | |
os.system("./tft_pwm 255") | |
print "init display done ..." | |
# init functions as threads | |
idlethread = threading.Thread(target=idle) | |
serialthread = threading.Thread(target=serialRead) | |
knopfthread = threading.Thread(target=knopf) | |
offlinethread = threading.Thread(target=offlinesubmit) | |
unstuckthread = threading.Thread(target=unstuck) | |
timeoutthread = threading.Thread(target=timeout) | |
# make them independent | |
idlethread.daemon = True | |
serialthread.daemon = True | |
knopfthread.daemon = True | |
offlinethread.daemon = True | |
unstuckthread.daemon = True | |
timeoutthread.daemon = True | |
# finally launch threads | |
idlethread.start() | |
serialthread.start() | |
knopfthread.start() | |
offlinethread.start() | |
unstuckthread.start() | |
timeoutthread.start() | |
print "started all threads" | |
# catch exit in main thread | |
while not stuck: | |
try: | |
time.sleep(0.2) | |
except: | |
print "we appear to be stuck, quitting application." | |
pygame.quit() | |
sys.exit(0) | |
except: | |
print "something bad happend, quitting!" | |
GPIO.cleanup() | |
db.close() | |
pygame.quit() | |
sys.exit(0) |