wie gestern angekündigt würde ich euch gerne mein kleines Projekt zeigen. Falls jemand sich die Zeit vertreiben will, würde ich mich über Kritik am Code freuen.
Es geht dabei um folgendes. Ein Raspberry Pi ist an einen Monitor angeschlossen und hat über die GPIO's einen RFID Reader angeschlossen. Damit möchte ich in meiner Werkstatt die Arbeitszeit pro Auftrag dokumentieren. Ich habe in der Werkstatt kein Internet, deswegen zähle ich einfach die Sekunden und berechne daraus die Stunden und Minuten. Pro Auftrag wird eine *.csv-Datei auf einem USB-Stick angelegt und darin werden die Zeiten dokumentiert.
Das machen die folgenden zwei Codes:
Code: Alles auswählen
#!/usr/bin/env python3
from RPi import GPIO
import tkinter as tk
from mfrc522 import SimpleMFRC522
from TimeRecording import TimeRecording
from pathlib import Path, PurePosixPath
import csv
READER = SimpleMFRC522()
ID = 1070737171454
ORDER_PATH = Path('/media/pi/')
ORDERS = ['Auftrag auswählen']
class App(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
self.status = None
self.check_time = TimeRecording()
self.menu_choice = tk.StringVar(self)
self.orders = self.search_orders(ORDERS)
self.menu_choice.set(self.orders[0])
self.order_menu = tk.OptionMenu(self, self.menu_choice, *self.orders)
self.order_menu.grid(row=0, column=0)
self.menu_choice.trace('w', self.read_menu)
self.information = tk.Label(self, text='Hey, mit was gehts los?')
self.information.grid(row=0, column=1, rowspan=2)
tk.Button(self, text='Kommen', command=self.start_time_recording).grid(row=2, column=0)
tk.Button(self, text='Gehen', command=self.stop_time_recording).grid(row=2, column=1)
tk.Button(self, text='Neuer Auftrag erstellen', command=self.creat_order).grid(row=2, column=2)
self.input_ordernumber = tk.Entry(self)
self.input_ordernumber.grid(row=2, column=3)
self.change_year = tk.Entry(self)
self.change_year.grid(row=0, column=3)
self.change_year.insert(0, '21')
def search_orders(self, orders):
try:
for folder in ORDER_PATH.iterdir():
order_path = Path(f'{folder})
for order in order_path.iterdir():
orders.append(PurePosixPath(order).stem)
except Exception as e:
logging.basicConfig(filename='TimeClock.log')
logging.error(e)
orders = ['USB-Stick fehlt']
return orders
def read_menu(self, *args):
self.information.configure(text=self.menu_choice.get())
def creat_order(self):
year = self.change_year.get()
new_ordernumber = self.input_ordernumber.get()
if new_ordernumber == '':
self.information.configure(text='Neue Auftragsnummer eingeben')
elif f'VG-{new_ordernumber}' in self.orders:
self.information.configure(text='Sorry, Auftragsnummer ist schon vorhanden')
else:
new_order_name = self.input_ordernumber.get()
with open(f'{ORDER_PATH}/VG-{year}{new_order_name}.csv', 'w', newline='') as csvfile:
csvwriter = csv.writer(csvfile, delimiter=',', quoting=csv.QUOTE_MINIMAL)
csvwriter.writerow(['hour, minute'])
self.order_menu['menu'].delete(0, 'end')
new_order = f'VG-{year}{new_order_name}'
self.orders.append(new_order)
self.order_menu['menu'].delete(0, 'end')
for order in self.orders:
self.order_menu['menu'].add_command(label=order, command=tk._setit(self.menu_choice, order))
self.information.configure(text=f'Auftrag {new_order} erstellt.')
def check_id(self):
id, text = READER.read()
if id == ID:
return True
else:
self.information.configure(text='Falscher Chip')
return False
def start_time_recording(self):
if self.menu_choice.get() == 'Auftrag auswählen' or self.menu_choice.get() == 'USB-Stick fehlt':
self.information.configure(text='Erst einen Auftrag auswäheln')
else:
self.check_time = TimeRecording()
if self.check_id() == True:
self.check_time.start_time()
self.status = 1
self.information.configure(text='Hey Dennis, schaffs gut!')
def stop_time_recording(self):
if self.check_id() == True:
if self.status == None:
pass
else:
self.check_time.total_time()
hour, minute = self.check_time.convert_time()
self.information.configure(text=f'Anwesend: {hour} Stunden und {minute} Minuten \n Ciao!')
with open(f'{ORDER_PATH}/{self.menu_choice.get()}.csv', 'a', newline='') as csvfile:
csvwriter = csv.writer(csvfile, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL)
csvwriter.writerow([hour, minute])
self.status = None
def main():
try:
root = tk.Tk()
root.title('Zeiterfassung')
app = App(root)
app.pack()
app.mainloop()
finally:
GPIO.cleanup()
if __name__ == "__main__":
main()
Code: Alles auswählen
#!/usr/bin/env python3
from time import time
class TimeRecording():
def start_time(self):
self.start = time()
def total_time(self):
self.end = time()
self.total_time = (self.end - self.start) / 3600
def convert_time(self):
self.total_time = str(round(self.total_time, 2)).split('.')
minute = round(float(self.total_time[1]) * 0.6)
hour = self.total_time[0]
return hour, minute
Zum Code: @Sirius3 hat mir gesagt bei subprocess.run benutzt man kein 'shell=True' gilt das allgemein? Wenn ich das weglasse bekomme ich eine Meldung, dass der Befehl nicht ausgeführt werden kann. (Sorry die genau Fehlermeldung habe ich gerade nicht mehr im Kopf, kann ich aber später nochmal reproduzieren und nachreichen) Mit 'shell=True' funktioniert es.
Der Code an sich funktioniert auch. Zur Zeit starte ich ihn noch per SSH und erhalte diese Ausgabe:
Code: Alles auswählen
umount: /media/USB: not mounted.
Eigentlich steuert man ein Python-Programm in der 'main'-Funktion, das habe ich hier nicht hinbekommen, wegen der ständigen Kontrolle, ob ein Stick an- oder abgesteckt wird.
Code: Alles auswählen
#!/usr/bin/env python3
import pyudev
import csv
import logging
from subprocess import run
from pathlib import Path
from EditTimes import EditTimes
from gpiozero import LED
MOUNT_PATH = Path('/media/USB/')
LED = LED(7)
def device_action():
context = pyudev.Context()
monitor = pyudev.Monitor.from_netlink(context)
for device in iter(monitor.poll, None):
if 'ID_FS_TYPE' in device:
if '{0}'.format(device.action) == 'add':
new_device = '{0}'.format(device.get('ID_FS_UUID'))
mount_device(new_device)
if '{0}'.format(device.action) == 'remove':
LED.off()
def mount_device(new_device):
context = pyudev.Context()
monitor = pyudev.Monitor.from_netlink(context)
for device in iter(monitor.poll, None):
if 'ID_FS_TYPE' in device:
if '{0}'.format(device.action) == 'add':
new_device = '{0}'.format(device.get('ID_FS_UUID'))
mount_device(new_device)
if '{0}'.format(device.action) == 'remove':
LED.off()
def mount_device(new_device):
try:
run([f'mount -t ntfs -o umask=007,gid=046 UUID={new_device} {MOUNT_PATH}'], check=True, shell=True)
except Exception as e:
logging.basicConfig(filename='CheckDevice.log')
logging.error(e)
start_edit = EditTimes()
csv_files = start_edit.search_files()
for csv_file in csv_files:
total_minutes = start_edit.read_time(csv_file)
working_time = start_edit.calculate_minutes(total_minutes)
start_edit.write_in_excel(csv_file.name, working_time)
try:
run([f'umount UUID={new_device} {MOUNT_PATH}'], check=True, shell=True)
except Exception as e:
logging.basicConfig(filename='CheckDevice.log')
logging.error(e)
LED.on()
def main():
device_action()
if __name__ == '__main__':
main()
Code: Alles auswählen
#!/usr/bin/env python3
import csv
from openpyxl import load_workbook
from pathlib import Path
CSV_PATH = Path('/media/USB/')
EXCEL_PATH = Path('/media/Dennis/Arbeitszeiten/Arbeitszeit.xlsx')
class EditTimes():
def read_time(self, csv_file):
with open(csv_file, newline='') as csvfile:
csvreader = csv.DictReader(csvfile)
total_minutes = sum(
int(row['hour']) * 60 + int(row['minute'])
for row in csvreader)
return total_minutes
def calculate_minutes(self, minutes):
return divmod(minutes, 60)
def search_files(self):
files = []
for file in CSV_PATH.iterdir():
files.append(file)
return files
def write_in_excel(self, csv_file, working_time):
order = Path(csv_file).stem
working_time = f'{working_time[0]}:{working_time[1]}'
book = load_workbook(EXCEL_PATH)
sheet = book['2021']
for index, row in enumerate(sheet.values, 1):
if row[0] == order:
if row[1] == working_time and row[2] == 'X':
(CSV_PATH / csv_file).unlink()
else:
sheet[f'B{index}'] = working_time
break
else:
sheet.append([order, working_time])
book.save(EXCEL_PATH)
def delete_csv(self, csv_file):
run([f'rm {CSV_PATH}/{csv_file}'], shell=True, check=True)
Vielen Dank und Grüße
Dennis