Code-Durchsicht
Verfasst: Freitag 5. März 2021, 09:55
Guten Morgen zusammen,
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:
und TimeRecording.py :
Wenn ich mit einem Auftrag fertig bin, nehme ich den USB-Stick und stecke ihn Daheim in einen Pi-zero. Dieser erkennt das ein USB-Stick hinzugefügt wurde, liest die Dateien aus und fügt die Arbeitszeiten einer Excel-Datei hinzu. Die Excel-Datei liegt auf unseren privaten Cloud (nicht im öffentlichen Netz erreichbar) und von dort aus, kann meine Freundin mit den Stunden weiter arbeiten. Da der Pi-zero kein Display hat, habe ich eine LED angeschlossen, die mir signalisieren soll, wann ich den Stick wieder abziehen kann.
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: obwohl ich keinen 'print'-Befehl im Code habe. Die Kopiervorgänge funktionieren aber, also der Stick war wohl gemountet.
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.
Die EditTimes.py:
Ich bin gespannt, was ihr davon haltet.
Vielen Dank und Grüße
Dennis
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