1112 lines
53 KiB
Python
1112 lines
53 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
# Form implementation generated from reading ui file 'test.ui'
|
|
#
|
|
# Created by: PyQt5 UI code generator 5.15.9
|
|
#
|
|
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
|
# run again. Do not edit this file unless you know what you are doing.
|
|
|
|
|
|
########## beim Static workflow funktionieren die Threads ab dem zweiten nicht mehr
|
|
|
|
#from PyQt5 import QApplication, QMainWindow, QPushButton, QVBoxLayout, QWidget, QProgressBar
|
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
|
from PyQt5.QtWidgets import QApplication, QComboBox
|
|
from PyQt5.QtCore import QTimer, QThread, pyqtSignal, QObject, Qt
|
|
from PyQt5.QtGui import QColor
|
|
|
|
import serial
|
|
import binascii
|
|
import math
|
|
import mariadb
|
|
import time
|
|
import threading
|
|
|
|
from lib.CameraStream import YOLOv8CameraStream # Import the YOLOv8CameraStream class
|
|
import cv2
|
|
|
|
import os
|
|
|
|
import lib.sainsmartrelay as sainsmartrelay
|
|
|
|
|
|
db_config = {
|
|
'user': 'dbUser',
|
|
'password': 'dbPassword',
|
|
'host': '127.0.0.1', # 'host': 'localhost', changed because more compatible
|
|
'database': 'projectGeislinger',
|
|
'port': 3306 # Standard port for MariaDB
|
|
}
|
|
|
|
|
|
# Establishing the connection
|
|
conn = mariadb.connect(**db_config)
|
|
# Create a cursor to execute queries
|
|
cursor = conn.cursor()
|
|
|
|
# Configuration of the serial port
|
|
try:
|
|
ser = serial.Serial('/dev/serial/by-path/pci-0000:00:14.0-usbv2-0:3:1.0-port0', 9600)
|
|
print("Serial port connected successfully.")
|
|
except serial.SerialException:
|
|
ser = None
|
|
print("Warning: Serial port not found. Continuing without serial connection. Only working for demo-purposes.")
|
|
#ser = serial.Serial('/dev/serial/by-id/usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port0', 9600) #dadurch garantiert immer die gleiche Schnittstelle verwendet
|
|
# und nicht die Schnittstelle, welche die Bezeichnung ttyUSB0 verwendet (welche sich ändern könnte)
|
|
# um die ID der USB-Schnittstelle heraus zu finden im Terminal folgendes eingeben: ls -l /dev/serial/by-id/
|
|
# addition: now 2 ESP32 are used: 1 for reading the scale and 1 for controlling the led light. Problem is they both use the same id so
|
|
# physical port is selected instead.
|
|
|
|
|
|
waageEingeschwungen = False
|
|
|
|
|
|
def wahrscheinlichkeitsDichte(x,mue, var):
|
|
# in der Funktion wird der Wahrscheinlichkeitsdichtenwert der Variable x für eine bestimmte Normalverteilung berechnet
|
|
standardabweichung = var**0.5
|
|
|
|
result = 1/(standardabweichung * (2*math.pi)**0.5 ) * math.exp(-0.5 * ((x-mue)/standardabweichung)**2)
|
|
|
|
return result
|
|
|
|
def calcWahrscheinlichkeitFromDichte(x,mue, var):
|
|
# in der Funktion wird die Wahrscheinlichkeit via der Wahrscheinlichkeitsdichte berechnet, indem das Verhältnis aus der dem Bauteil zugehörigen Wahrscheinlichkeitsdichte zu der maximalen Wahrscheinlichkeitsdichte berechnet wird
|
|
p1 = wahrscheinlichkeitsDichte(x,mue, var)
|
|
p_max = wahrscheinlichkeitsDichte(mue,mue, var)
|
|
|
|
return p1/p_max
|
|
|
|
|
|
|
|
#class Worker(QThread):
|
|
class Worker(QObject):
|
|
#progress = pyqtSignal(int)
|
|
objectDetectionStartSignal = pyqtSignal(str)
|
|
objectDetectionFinishedSignal = pyqtSignal(int, object)
|
|
|
|
checkWaageStartSignal = pyqtSignal(int, int, str)
|
|
checkWaageUpdateSignal = pyqtSignal(int, int)
|
|
checkWaageFinishedSignal = pyqtSignal(int)
|
|
|
|
waageStoppedSignal = pyqtSignal(int)
|
|
stopLoopSignal = pyqtSignal(bool) #das Signal wird verwendet um direkt den stopLoop-Wert zu ändern (also kein Funktionsaufruf)
|
|
|
|
stopLoop = False
|
|
btTypeIsSet = False
|
|
correctBtNr = False
|
|
|
|
|
|
def __init__(self):
|
|
super(Worker, self).__init__()
|
|
stopLoop = False
|
|
|
|
|
|
def getDataOfArticleType(self, allArticles, articleType):
|
|
# die Funktion geht die Liste mit allen Artikeln durch und gibt jenen Eintrag, welcher mit dem "articleType" übereinstimmt zurück
|
|
for i in allArticles:
|
|
if i[1] == articleType:
|
|
return i
|
|
|
|
return -1
|
|
|
|
def waageNichtEingeschwungenOutput(self):
|
|
print("Die Waage ist noch nicht eingeschwungen - Ergebnisse sind dadurch noch fehlerhaft.")
|
|
|
|
def readWaage(self):
|
|
# in folgender Funktion wird die Waage ausgelesen
|
|
|
|
#print("connection is open: ", ser.is_open) #Debuggingausgabe
|
|
#print("port to which it is connected: ", ser.portstr) #Debuggingausgabe
|
|
|
|
|
|
if ser.is_open == False:
|
|
ser.open()
|
|
|
|
|
|
#an die Waage den Befehl senden, dass sie ausgelesen werden soll
|
|
ser.write(b'getWeight\n')
|
|
#ser.write(b'tare\n')
|
|
serialString = ser.readline().decode('utf-8').rstrip() #Auslesen des Serial-Strings/der Messung der Waage
|
|
|
|
# wenn am Ende des Strings kg steht, dann ist die Waage eingeschwungen - das wird hiermit überprüft
|
|
lenString = len(serialString)-1
|
|
if serialString[lenString] == "g" and serialString[lenString-1] == "k":
|
|
#print("ist eingeschwungen") #Debuggingausgabe
|
|
waageEingeschwungen = True
|
|
else:
|
|
print("die Waage ist noch nicht eingeschwungen")
|
|
waageEingeschwungen = False
|
|
|
|
#aus dem String werden alle Zeichen, welche nicht zur Darstellung der Zahl benötigt werden entfernt
|
|
intString = ""
|
|
for i in serialString:
|
|
if i=="-" or i=="0" or i=="." or i=="1" or i=="2" or i=="3" or i=="4" or i=="5" or i=="6" or i=="7" or i=="8" or i=="9":
|
|
intString = intString + i
|
|
print("Wert, welcher von der Waage ausgelesen wurde: " + intString + "kg")
|
|
## Waage auslesen - ENDE
|
|
|
|
ser.close()
|
|
#print("connection is open: ", ser.is_open) #Debuggingausgabe
|
|
|
|
return waageEingeschwungen, intString
|
|
|
|
def objectTypeDetectionThread(self, auftragsnummer):
|
|
# in dieser Funktion wird der Typ des Bauteils automatisch erkannt
|
|
|
|
#print("objectTypeDetectionThread - Running in thread:", threading.current_thread().name) #Debuggausgabe
|
|
|
|
# Parameterdefinition
|
|
propDensVect = []
|
|
|
|
self.btTypeIsSet = False
|
|
|
|
while(self.btTypeIsSet == False and self.stopLoop == False):
|
|
|
|
# Auslesen der Waage
|
|
waageEingeschwungen, intString = self.readWaage()
|
|
if waageEingeschwungen == False:
|
|
self.waageNichtEingeschwungenOutput()
|
|
else:
|
|
|
|
# Datenbankabfrage
|
|
sql_query = "SELECT Auftraege.id, EinzelteilID, Auftragsnummer, Anzahl, Einzelteile.id, Bezeichnung, CAST(projectGeislinger.Einzelteile.GewichtMittelwert AS CHAR), CAST(projectGeislinger.Einzelteile.GewichtVarianz AS CHAR) FROM projectGeislinger.Auftraege, projectGeislinger.Einzelteile where projectGeislinger.Auftraege.EinzelteilID = projectGeislinger.Einzelteile.id AND projectGeislinger.Auftraege.Auftragsnummer = " + auftragsnummer
|
|
cursor.execute(sql_query)
|
|
auftragEinzelteilDaten = cursor.fetchall()
|
|
|
|
'''
|
|
# Display data #Debugausgabe
|
|
print("Ausgabe der Auftragsdetails des obigen Auftrags, inklusive Einzelteildetails:")
|
|
for row in auftragEinzelteilDaten:
|
|
print(row)
|
|
'''
|
|
|
|
# in der Folge werden alle Wahrscheinlichkeitsdichten der Auftragsbauteile berechnet und in dem Vektor gesammelt
|
|
for row in auftragEinzelteilDaten:
|
|
propDensVect.append([wahrscheinlichkeitsDichte(float(intString),float(row[6]), float(row[7])), row[1], row[5]])
|
|
|
|
# Jenen Eintrag des propDensVect raussuchen, welcher die größte Wahrscheinlichkeitsdichte beinhaltet
|
|
maxpropDens = 0
|
|
einzelteilID = 0
|
|
rowData = None
|
|
for row in propDensVect:
|
|
if row[0] > maxpropDens:
|
|
maxpropDens = row[0]
|
|
einzelteilID = row[1]
|
|
rowData = row
|
|
|
|
# überprüfen, ob das aufliegende Bauteil in der Auftragstabelle vorhanden ist
|
|
if(einzelteilID == 0):
|
|
print("Es wurde kein Bauteil aus der Auftragsliste (Tabelle) erkannt.")
|
|
else:
|
|
print("Bei dem Bauteil" , einzelteilID , "wurde die höchste Wahrscheinlichkeitsdichte berechnet.")
|
|
|
|
# Wahrscheinlichkeit berechnen, dass das angegebene Bauteil auch wirklich diesem entspricht
|
|
prop = 0
|
|
for row in auftragEinzelteilDaten:
|
|
if row[1] == einzelteilID:
|
|
prop = calcWahrscheinlichkeitFromDichte(float(intString),float(row[6]), float(row[7]))
|
|
break
|
|
print("Die Wahrscheinlichkeit, dass es das Bauteil ist, beträgt: ", prop)
|
|
|
|
# den Bool auf true setzen, damit die Schleife beendet wird - dieser wird auf True gesetzt, wenn ein Bauteiltyp erkannt wird
|
|
self.btTypeIsSet = True
|
|
|
|
if(self.stopLoop == False):
|
|
# ein Signal zurück an den MainThreat senden, mit den Infos/Ergebnissen, der Typenbestimmung
|
|
self.objectDetectionFinishedSignal.emit(einzelteilID, rowData)
|
|
else:
|
|
self.waageStoppedSignal.emit(einzelteilID)
|
|
|
|
def checkWaageThread(self, einzelteilID, teileZuViel, auftragsnummer):
|
|
|
|
self.correctBtNr = False
|
|
prevAnzahl = 0
|
|
anzahl = 0
|
|
einzelteilID = einzelteilID
|
|
|
|
while (self.correctBtNr == False and self.stopLoop == False):
|
|
|
|
#überprüfen, ob die Waage eingeschwungen ist (und damit korrekte Ergebnisse liefert)
|
|
waageEingeschwungen, intString = self.readWaage()
|
|
if waageEingeschwungen == False:
|
|
self.waageNichtEingeschwungenOutput()
|
|
else:
|
|
|
|
# Datenbankabfrage - holen der Auftrags- und Bauteilinfos
|
|
sql_query = "SELECT Auftraege.id, EinzelteilID, Auftragsnummer, Anzahl, Einzelteile.id, Bezeichnung, CAST(projectGeislinger.Einzelteile.GewichtMittelwert AS CHAR), CAST(projectGeislinger.Einzelteile.GewichtVarianz AS CHAR) FROM projectGeislinger.Auftraege, projectGeislinger.Einzelteile where projectGeislinger.Auftraege.EinzelteilID = projectGeislinger.Einzelteile.id AND projectGeislinger.Auftraege.Auftragsnummer = " + auftragsnummer
|
|
cursor.execute(sql_query)
|
|
auftragDaten = cursor.fetchall()
|
|
|
|
'''
|
|
# Display data - zum Debuggen
|
|
print("Ausgabe der Auftragsdetails:")
|
|
print("id|EinzelteilID|Auftragsnummer|Anzahl")
|
|
for row in auftragDaten:
|
|
print(row)
|
|
'''
|
|
|
|
#auslesen, wie viele Bauteile des Types laut Auftrag vorhanden sein sollen
|
|
anzBauteile_soll = 0
|
|
idVorhanden = False
|
|
|
|
for row in auftragDaten:
|
|
if row[1] == einzelteilID:
|
|
anzBauteile_soll = row[3]
|
|
idVorhanden = True
|
|
|
|
if(idVorhanden == False):
|
|
print("Die gewählte Pos.Nr ist nicht in der Auftragsliste (Tabelle) vorhanden.")
|
|
return
|
|
|
|
# Berechnen der Wahrscheinlichkeitsdichten - der Betrag aller Wahrscheinlichkeitsdichten werden im propDensVect gespeichert
|
|
propDensVect = []
|
|
articleData = self.getDataOfArticleType(auftragDaten, einzelteilID)
|
|
for i in range(1,anzBauteile_soll+teileZuViel+1):
|
|
# Mathematische Sätze, auf welchen die Berechnung basiert:
|
|
# seien X1,..., Xn unabhängige Zufallsvariablen die N(mue_i, sigma_i^2) verteilt sind, dann ist X = X1+...+Xn - N(mue, sigma^2) verteilt mit mue=mue1+...+mue_n, sigma^2 = sigma_1^2+...+sigma_n^2
|
|
mueGes = float(articleData[6])*i # Berechnen des äquivalenten Mittelwert
|
|
varGes = float(articleData[7])*i # Berechnen der äquivalenten Varianz
|
|
propDensVect.append([wahrscheinlichkeitsDichte(float(intString),mueGes, varGes), i])
|
|
|
|
# durch den propDensVect iterieren und jenen Eintrag mit der höchsten Wahrscheinlichkeitsdichte raussuchen
|
|
maxpropDens = 0
|
|
for row in propDensVect:
|
|
if row[0] > maxpropDens:
|
|
maxpropDens = row[0]
|
|
anzahl = row[1]
|
|
|
|
# damit nur etwas gemacht wird (zb ein Updatesignal an den Hauptthread zurücksenden), wenn sich die berechnete Anzahl ändert
|
|
if prevAnzahl != anzahl:
|
|
prevAnzahl = anzahl
|
|
|
|
# Die Wahrscheinlichkeit berechnen, dass das obige Ergebnis auch dem Bauteil entspricht
|
|
if anzahl > 0:
|
|
for row in auftragDaten:
|
|
if row[1] == einzelteilID:
|
|
prop = calcWahrscheinlichkeitFromDichte(float(intString),float(row[6])*anzahl, float(row[7])*anzahl)
|
|
break
|
|
print("Die Wahrscheinlichkeit, dass es das Bauteil ist, beträgt: ", prop)
|
|
|
|
# wenn genug Bauteile vorhanden sind, dann soll die Schleife beendet werden
|
|
if (anzahl == anzBauteile_soll):
|
|
self.correctBtNr = True
|
|
|
|
# Konsolenausgabe
|
|
if maxpropDens > 0:
|
|
print("Bei der Anzahl" , anzahl , "des Bauteiltypes", einzelteilID, ",wurde die höchste Wahrscheinlichkeitsdichte berechnet.")
|
|
self.checkWaageUpdateSignal.emit(einzelteilID, anzahl)
|
|
|
|
else:
|
|
print("Von der ausgewählten Bauteiltype liegt die berechnete Wahrscheinlichkeit bei 0, dass zwischen 0 und", anzBauteile_soll+teileZuViel, "Bauteilen auf der Waage liegen.")
|
|
print("Falls die korrekte Anzhl an Bauteilen auf der Waage liegt, könnte der Fehler in einer falschen Kalibrierung der Waage liegen (TARE).")
|
|
|
|
if(self.stopLoop == False):
|
|
self.checkWaageFinishedSignal.emit(einzelteilID)
|
|
else:
|
|
self.waageStoppedSignal.emit(einzelteilID)
|
|
|
|
|
|
|
|
|
|
class Ui_MainWindow(object):
|
|
def setupUi(self, MainWindow):
|
|
self.auftragsnummer = ""
|
|
self.teileZuViel = 100 # Anzahl an Bauteilen, welche zu viel drinnen sein können - der Parameter kann selbst gesetzt werden #####
|
|
self.correctBtNr = False
|
|
self.btTypeIsSet = False
|
|
|
|
|
|
MainWindow.setObjectName("MainWindow")
|
|
MainWindow.resize(1400, 675)
|
|
self.centralwidget = QtWidgets.QWidget(MainWindow)
|
|
self.centralwidget.setObjectName("centralwidget")
|
|
self.btn1 = QtWidgets.QPushButton(self.centralwidget)
|
|
self.btn1.setGeometry(QtCore.QRect(700, 45, 111, 25))
|
|
self.btn1.setObjectName("btn1")
|
|
# Tabelle zur Darstellung der Auftragsdetails
|
|
self.AuftragsdetailsTable = QtWidgets.QTableWidget(self.centralwidget)
|
|
self.AuftragsdetailsTable.setGeometry(QtCore.QRect(10, 100, 661, 400))
|
|
self.AuftragsdetailsTable.setObjectName("AuftragsdetailsTable")
|
|
self.AuftragsdetailsTable.setColumnCount(7)
|
|
self.AuftragsdetailsTable.setRowCount(0)
|
|
item = QtWidgets.QTableWidgetItem()
|
|
font = QtGui.QFont()
|
|
font.setPointSize(8)
|
|
item.setFont(font)
|
|
self.AuftragsdetailsTable.setHorizontalHeaderItem(0, item)
|
|
item = QtWidgets.QTableWidgetItem()
|
|
font = QtGui.QFont()
|
|
font.setPointSize(8)
|
|
item.setFont(font)
|
|
self.AuftragsdetailsTable.setHorizontalHeaderItem(1, item)
|
|
item = QtWidgets.QTableWidgetItem()
|
|
font = QtGui.QFont()
|
|
font.setPointSize(8)
|
|
item.setFont(font)
|
|
self.AuftragsdetailsTable.setHorizontalHeaderItem(2, item)
|
|
item = QtWidgets.QTableWidgetItem()
|
|
font = QtGui.QFont()
|
|
font.setPointSize(8)
|
|
item.setFont(font)
|
|
self.AuftragsdetailsTable.setHorizontalHeaderItem(3, item)
|
|
item = QtWidgets.QTableWidgetItem()
|
|
font = QtGui.QFont()
|
|
font.setPointSize(8)
|
|
item.setFont(font)
|
|
self.AuftragsdetailsTable.setHorizontalHeaderItem(5, item)
|
|
item = QtWidgets.QTableWidgetItem()
|
|
font = QtGui.QFont()
|
|
font.setPointSize(8)
|
|
item.setFont(font)
|
|
self.AuftragsdetailsTable.setHorizontalHeaderItem(4, item)
|
|
item = QtWidgets.QTableWidgetItem()
|
|
font = QtGui.QFont()
|
|
font.setPointSize(8)
|
|
item.setFont(font)
|
|
self.AuftragsdetailsTable.setHorizontalHeaderItem(6, item)
|
|
|
|
# Tabelle zur Darstellung der Arbeitsschritte
|
|
self.ArbeitsschrittTable = QtWidgets.QTableWidget(self.centralwidget)
|
|
self.ArbeitsschrittTable.setGeometry(QtCore.QRect(1000, 50, 395, 400))
|
|
self.ArbeitsschrittTable.setObjectName("AuftragsdetailsTable")
|
|
self.ArbeitsschrittTable.setColumnCount(2)
|
|
self.ArbeitsschrittTable.setRowCount(4)
|
|
self.ArbeitsschrittTable.setColumnWidth(0, 100)
|
|
self.ArbeitsschrittTable.setColumnWidth(1, 280)
|
|
self.ArbeitsschrittTable.setRowHeight(0, 80)
|
|
self.ArbeitsschrittTable.setRowHeight(1, 180)
|
|
self.ArbeitsschrittTable.setRowHeight(2, 50)
|
|
self.ArbeitsschrittTable.setRowHeight(3, 50)
|
|
item = QtWidgets.QTableWidgetItem()
|
|
font = QtGui.QFont()
|
|
font.setPointSize(8)
|
|
item.setFont(font)
|
|
self.ArbeitsschrittTable.setHorizontalHeaderItem(0, item)
|
|
item = QtWidgets.QTableWidgetItem()
|
|
font = QtGui.QFont()
|
|
font.setPointSize(8)
|
|
item.setFont(font)
|
|
self.ArbeitsschrittTable.setHorizontalHeaderItem(1, item)
|
|
|
|
self.label = QtWidgets.QLabel(self.centralwidget)
|
|
self.label.setGeometry(QtCore.QRect(500, 20, 121, 17))
|
|
self.label.setObjectName("label")
|
|
self.textEdit = QtWidgets.QTextEdit(self.centralwidget)
|
|
self.textEdit.setGeometry(QtCore.QRect(450, 40, 221, 31))
|
|
self.textEdit.setObjectName("textEdit")
|
|
self.bauteilTypBtn = QtWidgets.QPushButton(self.centralwidget)
|
|
self.bauteilTypBtn.setGeometry(QtCore.QRect(700, 100, 161, 25))
|
|
self.bauteilTypBtn.setObjectName("bauteilTypBtn")
|
|
self.checkWaageBtn = QtWidgets.QPushButton(self.centralwidget)
|
|
self.checkWaageBtn.setGeometry(QtCore.QRect(700, 150, 161, 25))
|
|
self.checkWaageBtn.setObjectName("checkWaageBtn")
|
|
self.stopLoopBtn = QtWidgets.QPushButton(self.centralwidget)
|
|
self.stopLoopBtn.setGeometry(QtCore.QRect(700, 350, 161, 25))
|
|
self.stopLoopBtn.setObjectName("stopLoopBtn")
|
|
self.waageTareBtn = QtWidgets.QPushButton(self.centralwidget)
|
|
self.waageTareBtn.setGeometry(QtCore.QRect(700, 400, 161, 25))
|
|
self.waageTareBtn.setObjectName("waageTareBtn")
|
|
self.bauteiltypTextbox = QtWidgets.QTextEdit(self.centralwidget)
|
|
self.bauteiltypTextbox.setGeometry(QtCore.QRect(700, 290, 221, 31))
|
|
self.bauteiltypTextbox.setObjectName("bauteiltypTextbox")
|
|
self.BezeichnungLabel = QtWidgets.QLabel(self.centralwidget)
|
|
self.BezeichnungLabel.setGeometry(QtCore.QRect(700, 270, 181, 20))
|
|
self.BezeichnungLabel.setObjectName("BezeichnungLabel")
|
|
self.PosNrLabel = QtWidgets.QLabel(self.centralwidget)
|
|
self.PosNrLabel.setGeometry(QtCore.QRect(700, 200, 67, 17))
|
|
self.PosNrLabel.setObjectName("PosNrLabel")
|
|
self.PosNrTxtFeld = QtWidgets.QTextEdit(self.centralwidget)
|
|
self.PosNrTxtFeld.setGeometry(QtCore.QRect(700, 220, 191, 31))
|
|
self.PosNrTxtFeld.setObjectName("PosNrTxtFeld")
|
|
self.teileZuVielLabel = QtWidgets.QLabel(self.centralwidget)
|
|
self.teileZuVielLabel.setGeometry(QtCore.QRect(1000, 560, 350, 17))
|
|
self.teileZuVielLabel.setObjectName("teileZuVielLabel")
|
|
self.teileZuVielTxtFeld = QtWidgets.QTextEdit(self.centralwidget)
|
|
self.teileZuVielTxtFeld.setGeometry(QtCore.QRect(1000, 580, 191, 31))
|
|
MainWindow.setCentralWidget(self.centralwidget)
|
|
self.menubar = QtWidgets.QMenuBar(MainWindow)
|
|
self.menubar.setGeometry(QtCore.QRect(0, 0, 1090, 22))
|
|
self.menubar.setObjectName("menubar")
|
|
MainWindow.setMenuBar(self.menubar)
|
|
self.statusbar = QtWidgets.QStatusBar(MainWindow)
|
|
self.statusbar.setObjectName("statusbar")
|
|
MainWindow.setStatusBar(self.statusbar)
|
|
self.checkBox = QtWidgets.QCheckBox(self.centralwidget)
|
|
self.checkBox.setGeometry(QtCore.QRect(1000, 480, 151, 23))
|
|
self.checkBox.setObjectName("checkBox")
|
|
self.checkBoxAutoTare = QtWidgets.QCheckBox(self.centralwidget)
|
|
self.checkBoxAutoTare.setGeometry(QtCore.QRect(1000, 520, 180, 23))
|
|
self.checkBoxAutoTare.setObjectName("checkBoxAutoTare")
|
|
|
|
# button and checkbox for camera workflow + graphicsview widget
|
|
self.camWorkFlowcheckBox = QtWidgets.QCheckBox(self.centralwidget)
|
|
self.camWorkFlowcheckBox.setGeometry(QtCore.QRect(700, int(1080/2), 151, 23))
|
|
self.camWorkFlowcheckBox.setObjectName("camWorkFlowcheckBox")
|
|
self.modelComboBox = QtWidgets.QComboBox(self.centralwidget)
|
|
self.modelComboBox.setGeometry(QtCore.QRect(700, int(1080/2)+50, 161, 25))
|
|
self.modelComboBox.setObjectName("modelComboBox")
|
|
self.startCamBtn = QtWidgets.QPushButton(self.centralwidget)
|
|
self.startCamBtn.setGeometry(QtCore.QRect(700, int(1080/2)+100, 161, 25)) #int(1080/2)+100
|
|
self.startCamBtn.setObjectName("startCamBtn")
|
|
self.stopCamBtn = QtWidgets.QPushButton(self.centralwidget)
|
|
self.stopCamBtn.setGeometry(QtCore.QRect(700, int(1080/2)+150, 161, 25))
|
|
self.stopCamBtn.setObjectName("stopCamBtn")
|
|
self.graphicsView = QtWidgets.QGraphicsView(self.centralwidget)
|
|
self.graphicsView.setGeometry(QtCore.QRect(10, int(1080/2), 661, int(480*1.05))) # position and size of camera frame # int(640*1.05)
|
|
self.graphicsView.setObjectName("graphicsView")
|
|
|
|
# relay control
|
|
self.startSpotlightBtn = QtWidgets.QPushButton(self.centralwidget)
|
|
self.startSpotlightBtn.setGeometry(QtCore.QRect(700+200, int(1080/2)+100, 161, 25)) #int(1080/2)+100
|
|
self.startSpotlightBtn.setObjectName("startSpotlightBtn")
|
|
self.stopSpotlightBtn = QtWidgets.QPushButton(self.centralwidget)
|
|
self.stopSpotlightBtn.setGeometry(QtCore.QRect(700+200, int(1080/2)+150, 161, 25))
|
|
self.stopSpotlightBtn.setObjectName("stopSpotlightBtn")
|
|
|
|
#self.myTestLambda = lambda: self.worker.checkWaageStartSignal.emit(einzelteilID, self.teileZuViel, self.auftragsnummer)
|
|
|
|
'''
|
|
item = QtWidgets.QTableWidgetItem()
|
|
font = QtGui.QFont()
|
|
font.setPointSize(8)
|
|
item.setFont(font)
|
|
self.AuftragsdetailsTable.setVerticalHeaderItem(0,item)
|
|
'''
|
|
|
|
# Maximize the window on startup
|
|
MainWindow.showMaximized()
|
|
|
|
self.retranslateUi(MainWindow)
|
|
QtCore.QMetaObject.connectSlotsByName(MainWindow)
|
|
|
|
def retranslateUi(self, MainWindow):
|
|
_translate = QtCore.QCoreApplication.translate
|
|
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
|
|
self.btn1.setText(_translate("MainWindow", "load Auftrag"))
|
|
self.btn1.clicked.connect(self.auftragsBtnClicked)
|
|
|
|
# Tabelle zur Darstellung der Auftragsdetails
|
|
item = self.AuftragsdetailsTable.horizontalHeaderItem(0)
|
|
item.setText(_translate("MainWindow", "Pos.-Nr"))
|
|
item = self.AuftragsdetailsTable.horizontalHeaderItem(1)
|
|
item.setText(_translate("MainWindow", "Matnr mit hoechster Version"))
|
|
item = self.AuftragsdetailsTable.horizontalHeaderItem(2)
|
|
item.setText(_translate("MainWindow", "Bezeichnung + Werkstoff"))
|
|
item = self.AuftragsdetailsTable.horizontalHeaderItem(3)
|
|
item.setText(_translate("MainWindow", "Soll-Menge"))
|
|
item = self.AuftragsdetailsTable.horizontalHeaderItem(4)
|
|
item.setText(_translate("MainWindow", "Ist-Menge"))
|
|
item = self.AuftragsdetailsTable.horizontalHeaderItem(5)
|
|
item.setText(_translate("MainWindow", "Lgort"))
|
|
item = self.AuftragsdetailsTable.horizontalHeaderItem(6)
|
|
item.setText(_translate("MainWindow", "set Farbe"))
|
|
|
|
# Tabelle zur Darstellung der Arbeitsschritte
|
|
item = self.ArbeitsschrittTable.horizontalHeaderItem(0)
|
|
item.setText(_translate("MainWindow", "Arbeitsschritt"))
|
|
item = self.ArbeitsschrittTable.horizontalHeaderItem(1)
|
|
item.setText(_translate("MainWindow", "ToDo:"))
|
|
|
|
# Befüllen der Arbeisschritttabelle mit Text
|
|
#item = self.ArbeitsschrittTable.
|
|
# Ein neues QTableWidgetItem erstellen und den Text setzen
|
|
item = QtWidgets.QTableWidgetItem("Auftrag laden")
|
|
self.ArbeitsschrittTable.setItem(0, 0, item)
|
|
item = QtWidgets.QTableWidgetItem("--> Auftragsnummer eingeben \n--> \"load Auftrag\" Buttonclick")
|
|
self.ArbeitsschrittTable.setItem(0, 1, item)
|
|
item = QtWidgets.QTableWidgetItem("Bauteiltyp erkennen")
|
|
self.ArbeitsschrittTable.setItem(1, 0, item)
|
|
item = QtWidgets.QTableWidgetItem("visuell: \n--> ein Bauteil in die Kamera halten\n\nWaage: \n--> ein Bauteil auf die Waage legen\n--> \"Bauteiltyp erkennen\" Buttonclick\n\nmanuell:\n--> Bauteiltypen auswählen\n-->\"ckeck Waage\" Button click")
|
|
self.ArbeitsschrittTable.setItem(1, 1, item)
|
|
item = QtWidgets.QTableWidgetItem("Bauteilanzahl erkennen")
|
|
self.ArbeitsschrittTable.setItem(2, 0, item)
|
|
item = QtWidgets.QTableWidgetItem("--> Bauteile auflegen")
|
|
self.ArbeitsschrittTable.setItem(2, 1, item)
|
|
item = QtWidgets.QTableWidgetItem("Prozess beendet")
|
|
self.ArbeitsschrittTable.setItem(3, 0, item)
|
|
self.ArbeitsschrittTable.setEditTriggers(QtWidgets.QTableWidget.NoEditTriggers)
|
|
|
|
#self.ArbeitsschrittTable.item(0,0).setText("test")
|
|
|
|
#einzelteilID = int(self.AuftragsdetailsTable.item(i,0).text())
|
|
#self.PosNrTxtFeld.setText(str(self.AuftragsdetailsTable.item(i,0).text()))
|
|
#self.bauteiltypTextbox.setText(str(self.AuftragsdetailsTable.item(i,2).text()))
|
|
|
|
self.label.setText(_translate("MainWindow", "Auftragsnummer:"))
|
|
self.bauteilTypBtn.setText(_translate("MainWindow", "Bauteiltyp erkennen"))
|
|
self.bauteilTypBtn.clicked.connect(self.objectTypeDetection)
|
|
self.AuftragsdetailsTable.cellClicked.connect(self.onTableCellClick)
|
|
#self.AuftragsdetailsTable.setEditTriggers(QtWidgets.QTableWidget.NoEditTriggers)
|
|
#item = self.AuftragsdetailsTable.verticalHeaderItem(0)
|
|
#item.setText(_translate("MainWindow", "test"))
|
|
self.BezeichnungLabel.setText(_translate("MainWindow", "Bezeichnung"))
|
|
self.PosNrLabel.setText(_translate("MainWindow", "Pos.-Nr:"))
|
|
self.teileZuVielLabel.setText(_translate("MainWindow", "Zu prüfende Teileanzahl über Auftragsanzahl:"))
|
|
self.teileZuVielTxtFeld.setText(str(self.teileZuViel))
|
|
self.checkWaageBtn.setText(_translate("MainWindow", "check Waage"))
|
|
self.checkWaageBtn.clicked.connect(self.checkWaage)
|
|
self.stopLoopBtn.setText(_translate("MainWindow", "stop loop"))
|
|
self.stopLoopBtn.clicked.connect(self.stopLoopClicked)
|
|
self.waageTareBtn.setText(_translate("MainWindow", "Waage tarieren"))
|
|
self.waageTareBtn.clicked.connect(self.onTareClick)
|
|
self.checkBox.setText(_translate("MainWindow", "static workflow"))
|
|
self.checkBox.clicked.connect(self.onCheckboxCheck)
|
|
self.checkBoxAutoTare.setText(_translate("MainWindow", "automatisches Tarieren"))
|
|
self.checkBoxAutoTare.clicked.connect(self.autoTareCheck)
|
|
|
|
self.setRowColor(self.ArbeitsschrittTable, 0,255,165,0) # zum setzen der ersten Spalte der Arbeitsschrittetabelle auf orange
|
|
self.checkBoxAutoTare.setCheckState(Qt.Checked)
|
|
self.checkBox.setCheckState(Qt.Unchecked)
|
|
|
|
# new camera workflow
|
|
self.startCamBtn.setText(_translate("MainWindow", "Start Camera"))
|
|
# self.startCamBtn.clicked.connect(self.startCamBtnClicked)
|
|
self.stopCamBtn.setText(_translate("MainWindow", "Stop Camera"))
|
|
self.camWorkFlowcheckBox.setText(_translate("MainWindow", "Camera Workflow"))
|
|
# self.camWorkFlowcheckBox.clicked.connect(self.onCheckboxCheck)
|
|
self.modelComboBox
|
|
|
|
# relay control
|
|
self.startSpotlightBtn.setText(_translate("MainWindow", "Turn on light"))
|
|
self.stopSpotlightBtn.setText(_translate("MainWindow", "Turn off light"))
|
|
|
|
|
|
|
|
def mousePressEvent(self, event):
|
|
print("Das MainWindow wurde angeklickt.")
|
|
self.setFocus()
|
|
super().mousePressEvent(event)
|
|
|
|
|
|
def onTareClick(self):
|
|
#Tarieren der Waage
|
|
if ser.is_open == False:
|
|
ser.open()
|
|
ser.write(b'tare\n')
|
|
ser.close()
|
|
|
|
|
|
def onCheckboxCheck(self):
|
|
if self.checkBox.isChecked() == True:
|
|
print("static workflow activated")
|
|
else:
|
|
print("static workflow deactivated")
|
|
|
|
def autoTareCheck(self):
|
|
if self.checkBoxAutoTare.isChecked() == True:
|
|
print("automatisches Tarieren ist aktiviert")
|
|
else:
|
|
print("automatisches Tarieren ist deaktiviert")
|
|
|
|
def getRowNr(self, posNr):
|
|
for i in range(0,self.AuftragsdetailsTable.rowCount()):
|
|
if(self.AuftragsdetailsTable.item(i,0).text() == str(posNr)):
|
|
return i
|
|
# brauche ich hier vermutlich nicht mehr - ist in den Worker Thread kopiert worden
|
|
def waageNichtEingeschwungenOutput(self):
|
|
print("Die Waage ist noch nicht eingeschwungen - Ergebnisse sind dadurch noch fehlerhaft.")
|
|
|
|
def onTableCellClick(self):
|
|
self.PosNrTxtFeld.setText(self.AuftragsdetailsTable.item(self.AuftragsdetailsTable.currentRow(),0).text())
|
|
self.bauteiltypTextbox.setText(self.AuftragsdetailsTable.item(self.AuftragsdetailsTable.currentRow(),2).text())
|
|
|
|
def setAuftragsnummer(self):
|
|
self.auftragsnummer = self.textEdit.toPlainText()
|
|
if(not self.auftragsnummer):
|
|
self.auftragsnummer = ""
|
|
self.textEdit.setText("")
|
|
|
|
def checkAuftragsnummerEmpty(self):
|
|
if self.auftragsnummer == "":
|
|
print("Das Auftragsnummernfeld ist leer.")
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
def checkPosNrEmpty(self):
|
|
if self.PosNrTxtFeld.toPlainText() == "":
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
|
|
# eventuell benötigt man die Funktion hier nicht mehr, da sie zu den Threads kopiert wurde
|
|
def getDataOfArticleType(self, allArticles, articleType):
|
|
# die Funktion geht die Liste mit allen Artikeln durch und gibt jenen Eintrag, welcher mit dem "articleType" übereinstimmt zurück
|
|
for i in allArticles:
|
|
if i[1] == articleType:
|
|
return i
|
|
return -1
|
|
|
|
def updateGUI(self):
|
|
self.PosNrLabel.repaint() #GUI aktualisieren
|
|
QApplication.processEvents() #GUI aktualisieren
|
|
|
|
|
|
def setRowColor(self, tableObject, rowID,r,g,b):
|
|
for col in range(tableObject.columnCount()-1):
|
|
tableObject.item(rowID, col).setBackground(QtGui.QColor(r,g,b))
|
|
'''
|
|
die alte Version der Programmierung
|
|
tableObject.item(rowID, 0).setBackground(QtGui.QColor(r,g,b))
|
|
tableObject.item(rowID, 1).setBackground(QtGui.QColor(r,g,b))
|
|
tableObject.item(rowID, 2).setBackground(QtGui.QColor(r,g,b))
|
|
tableObject.item(rowID, 3).setBackground(QtGui.QColor(r,g,b))
|
|
tableObject.item(rowID, 4).setBackground(QtGui.QColor(r,g,b))
|
|
tableObject.item(rowID, 5).setBackground(QtGui.QColor(r,g,b))
|
|
'''
|
|
|
|
#wird hier vermutlich nicht mehr benötigt - wurde in die Workerklasse kopiert
|
|
def readWaage(self):
|
|
# in folgender Funktion wird die Waage ausgelesen
|
|
|
|
#print("connection is open: ", ser.is_open) #Debuggingausgabe
|
|
#print("port to which it is connected: ", ser.portstr) #Debuggingausgabe
|
|
self.checkPosNrEmpty()
|
|
|
|
if ser.is_open == False:
|
|
ser.open()
|
|
|
|
|
|
#an die Waage den Befehl senden, dass sie ausgelesen werden soll
|
|
ser.write(b'getWeight\n')
|
|
#ser.write(b'tare\n')
|
|
serialString = ser.readline().decode('utf-8').rstrip() #Auslesen des Serial-Strings/der Messung der Waage
|
|
|
|
# wenn am Ende des Strings kg steht, dann ist die Waage eingeschwungen - das wird hiermit überprüft
|
|
lenString = len(serialString)-1
|
|
if serialString[lenString] == "g" and serialString[lenString-1] == "k":
|
|
#print("ist eingeschwungen") #Debuggingausgabe
|
|
waageEingeschwungen = True
|
|
else:
|
|
print("die Waage ist noch nicht eingeschwungen")
|
|
waageEingeschwungen = False
|
|
|
|
#aus dem String werden alle Zeichen, welche nicht zur Darstellung der Zahl benötigt werden entfernt
|
|
intString = ""
|
|
for i in serialString:
|
|
if i=="-" or i=="0" or i=="." or i=="1" or i=="2" or i=="3" or i=="4" or i=="5" or i=="6" or i=="7" or i=="8" or i=="9":
|
|
intString = intString + i
|
|
print("Wert, welcher von der Waage ausgelesen wurde: " + intString + "kg")
|
|
## Waage auslesen - ENDE
|
|
|
|
ser.close()
|
|
#print("connection is open: ", ser.is_open) #Debuggingausgabe
|
|
|
|
return waageEingeschwungen, intString
|
|
|
|
def auftragsBtnClicked(self):
|
|
databaseQueryWorking = False #wird für die Überprüfung, ob die Datenbankabfrage fehlerhaft ist, verwendet
|
|
auftragEinzelteilDaten = []
|
|
|
|
self.setAuftragsnummer()
|
|
if(not self.checkAuftragsnummerEmpty()):
|
|
|
|
sql_query = "SELECT Auftraege.id, EinzelteilID, Auftragsnummer, Anzahl, Einzelteile.id, Bezeichnung, CAST(projectGeislinger.Einzelteile.GewichtMittelwert AS CHAR), CAST(projectGeislinger.Einzelteile.GewichtVarianz AS CHAR) FROM projectGeislinger.Auftraege, projectGeislinger.Einzelteile where projectGeislinger.Auftraege.EinzelteilID = projectGeislinger.Einzelteile.id AND projectGeislinger.Auftraege.Auftragsnummer = " + self.auftragsnummer
|
|
try:
|
|
cursor.execute(sql_query)
|
|
# Fetch results
|
|
auftragEinzelteilDaten = cursor.fetchall()
|
|
databaseQueryWorking = True
|
|
except:
|
|
print("Fehler in der Datenbankabfrage.")
|
|
|
|
|
|
|
|
if databaseQueryWorking==True and len(auftragEinzelteilDaten)>0:
|
|
'''
|
|
# Display data
|
|
print("Ausgabe der Auftragsdetails des obigen Auftrags, inklusive Einzelteildetails:")
|
|
for row in auftragEinzelteilDaten:
|
|
if str(row[2]) == self.auftragsnummer:
|
|
print("passt")
|
|
print(row)
|
|
'''
|
|
|
|
### die Auftragsdaten in die Tabelle laden
|
|
self.AuftragsdetailsTable.setRowCount(len(auftragEinzelteilDaten))
|
|
self.item = QtWidgets.QTableWidgetItem()
|
|
counter = 0
|
|
for row in auftragEinzelteilDaten:
|
|
self.item = QtWidgets.QTableWidgetItem()
|
|
self.AuftragsdetailsTable.setItem(counter, 0, self.item)
|
|
self.item.setText(str(row[1]))
|
|
self.item.setFlags(self.item.flags() & ~QtCore.Qt.ItemIsEditable)
|
|
self.item = QtWidgets.QTableWidgetItem()
|
|
self.AuftragsdetailsTable.setItem(counter, 1, self.item)
|
|
self.item.setFlags(self.item.flags() & ~QtCore.Qt.ItemIsEditable)
|
|
self.item = QtWidgets.QTableWidgetItem()
|
|
self.AuftragsdetailsTable.setItem(counter, 2, self.item)
|
|
self.item.setText(str(row[5]))
|
|
self.item.setFlags(self.item.flags() & ~QtCore.Qt.ItemIsEditable)
|
|
self.item = QtWidgets.QTableWidgetItem()
|
|
self.AuftragsdetailsTable.setItem(counter, 3, self.item)
|
|
self.item.setText(str(row[3]))
|
|
self.item.setFlags(self.item.flags() & ~QtCore.Qt.ItemIsEditable)
|
|
self.item = QtWidgets.QTableWidgetItem()
|
|
self.AuftragsdetailsTable.setItem(counter, 4, self.item)
|
|
self.item.setText(str("0"))
|
|
self.item = QtWidgets.QTableWidgetItem()
|
|
self.AuftragsdetailsTable.setItem(counter, 5, self.item)
|
|
self.item.setFlags(self.item.flags() & ~QtCore.Qt.ItemIsEditable)
|
|
|
|
# Dropdown für Farben in die 6. Spalte einfügen
|
|
#self.item = QtWidgets.QTableWidgetItem()
|
|
#self.AuftragsdetailsTable.setItem(counter, 6, self.item)
|
|
combo = QComboBox()
|
|
combo.addItems(["Farbe", "Weiß", "Orange", "Grün"])
|
|
combo.currentIndexChanged.connect(lambda index, rowId =counter: self.change_row_color(rowId, index))
|
|
self.AuftragsdetailsTable.setCellWidget(counter, 6, combo)
|
|
counter = counter +1
|
|
|
|
self.setRowColor(self.ArbeitsschrittTable, 0,0,255,0) #zum setzen der ersten Zeile der Auftragstabelle auf grün
|
|
self.setRowColor(self.ArbeitsschrittTable, 1,255,165,0) #zum setzen der zweiten Zeile der Auftragstabelle auf orange
|
|
self.setRowColor(self.ArbeitsschrittTable, 2,255,255,255) #zum setzen der dritten Zeile der Auftragstabelle auf weiß
|
|
self.setRowColor(self.ArbeitsschrittTable, 3,255,255,255) #zum setzen der vierten Zeile der Auftragstabelle auf weiß
|
|
|
|
# setzen der beiden Felder auf einen leeren String, um Fehler zu Vermeiden, wenn ein neuer Auftrag geladen wird
|
|
self.bauteiltypTextbox.setText("")
|
|
self.PosNrTxtFeld.setText("")
|
|
|
|
|
|
elif(databaseQueryWorking==True and len(auftragEinzelteilDaten)==0):
|
|
print("Es wurde in der Datenbank kein Auftrag mit dieser Auftragsnummer gefunden.")
|
|
|
|
def change_row_color(self, row, color_index):
|
|
if(color_index == 1):
|
|
self.setRowColor(self.AuftragsdetailsTable, row,255,255,255)
|
|
if(color_index == 2):
|
|
self.setRowColor(self.AuftragsdetailsTable, row,255,165,0)
|
|
if(color_index == 3):
|
|
self.setRowColor(self.AuftragsdetailsTable, row,0,255,0)
|
|
|
|
def stopLoopClicked(self):
|
|
# damit wird beim Klick auf den Stopbutton der stopLoop-boolWert in der Workerklasse auf true gesetzt -> der Stop des Threads wird initiiert
|
|
if hasattr(self, 'objectDetectionWorker'):
|
|
self.objectDetectionWorker.stopLoopSignal.emit(True)
|
|
|
|
if hasattr(self, 'checkWaageWorker'):
|
|
self.checkWaageWorker.stopLoopSignal.emit(True)
|
|
|
|
def checkFinished(self):
|
|
#die Funktion geht alle Zeilen der Auftragsliste durch und schaut, ob die richtige Anzahl an Teilen vorhanden sind
|
|
for i in range(0,self.AuftragsdetailsTable.rowCount()):
|
|
if self.AuftragsdetailsTable.item(i,3).text() != self.AuftragsdetailsTable.item(i,4).text():
|
|
return False
|
|
return True
|
|
|
|
|
|
def checkWaage(self):
|
|
#print("Running in thread:", threading.current_thread().name) # Debuggingausgabe
|
|
QApplication.processEvents()
|
|
|
|
# die Loopvariable des Workers auf False setzten, damit die Schleife durchgelaufen wird (diese wird zum Abbruch der Schleife benötigt -> siehe stopLoop)
|
|
#self.checkWaageWorker.stopLoopSignal.emit(False)
|
|
|
|
# überprüfen, ob der Auftrag geladen wurde
|
|
if(self.AuftragsdetailsTable.item(0,0) == None):
|
|
print("Der Auftrag muss zuerst geladen werden.")
|
|
return
|
|
|
|
# überprüfen, ob ein Bauteiltyp gesetzt wurde
|
|
if(self.checkPosNrEmpty()==True and self.checkBox.isChecked() == False):
|
|
print("Das Pos.-Nr.-Feld ist leer.")
|
|
return
|
|
|
|
# den GUI Wert von Textfeld TeileZuViel in die Variable schreiben
|
|
self.teileZuViel = int(float(self.teileZuVielTxtFeld.toPlainText()))
|
|
self.teileZuVielTxtFeld.setText(str(self.teileZuViel))
|
|
|
|
einzelteilID = -1 # Initialisieren der Variable mit einem Defaultwert, welcher nie eingenommen werden können sollte
|
|
if (self.checkBox.isChecked() == False):
|
|
#übernehmen der BauteilID aus dem Feld Pos.-Nr - wenn der Workflow dynamisch gewählt wurde
|
|
einzelteilID = int(self.PosNrTxtFeld.toPlainText())
|
|
else:
|
|
# wenn der Workflow statisch gesetzt wurde, dann soll das erste Item aus der Auftragsliste geladen werden, von welchem noch nicht genug Bauteile auf der Waage liegen
|
|
for i in range(0,self.AuftragsdetailsTable.rowCount()):
|
|
if self.AuftragsdetailsTable.item(i,3).text() != self.AuftragsdetailsTable.item(i,4).text():
|
|
einzelteilID = int(self.AuftragsdetailsTable.item(i,0).text())
|
|
self.PosNrTxtFeld.setText(str(self.AuftragsdetailsTable.item(i,0).text()))
|
|
self.bauteiltypTextbox.setText(str(self.AuftragsdetailsTable.item(i,2).text()))
|
|
break
|
|
|
|
if einzelteilID == -1:
|
|
# den Thread beenden
|
|
self.checkWaageThread.quit()
|
|
self.checkWaageThread.wait()
|
|
try:
|
|
self.checkWaageThread.started.disconnect() # Trenne das Signal, damit es beim nächsten Start keine Konflikte gibt
|
|
print("Der Thread wurde beendet.")
|
|
except:
|
|
print("Disconnecting the thread did not work.")
|
|
return
|
|
|
|
|
|
#zum setzen den Farbe der gesamten Reihe auf Orange
|
|
self.setRowColor(self.AuftragsdetailsTable, self.getRowNr(einzelteilID),255,165,0)
|
|
self.setRowColor(self.ArbeitsschrittTable, 1,0,255,0) # zum setzen der zweiten Spalte der Arbeitsschrittetabelle auf grün
|
|
self.setRowColor(self.ArbeitsschrittTable, 2,255,165,0) # zum setzen der dritten Spalte der Arbeitsschrittetabelle auf orange
|
|
|
|
# den Thread starten, welcher die Bauteilanzahl überprüft
|
|
self.checkWaageWorker = Worker()
|
|
self.checkWaageThread = QThread()
|
|
|
|
self.checkWaageWorker.moveToThread(self.checkWaageThread)
|
|
|
|
self.checkWaageWorker.checkWaageStartSignal.connect(self.checkWaageWorker.checkWaageThread)
|
|
self.checkWaageWorker.checkWaageUpdateSignal.connect(self.checkWaageUpdate)
|
|
self.checkWaageWorker.checkWaageFinishedSignal.connect(self.checkWaageFinished)
|
|
self.checkWaageWorker.waageStoppedSignal.connect(self.threadStopped)
|
|
self.checkWaageWorker.stopLoopSignal.connect(lambda status: setattr(self.checkWaageWorker, 'stopLoop', status)) #wird fürs aktive Stoppen des Threads benötigt
|
|
|
|
self.checkWaageThread.started.connect(lambda: self.checkWaageWorker.checkWaageStartSignal.emit(einzelteilID, self.teileZuViel, self.auftragsnummer))
|
|
self.checkWaageThread.start()
|
|
|
|
def checkWaageUpdate(self, einzelteilID, anzahl):
|
|
# die Funktion updated die (vom Thread) berechnete Bauteilanzahl in der GUI
|
|
self.AuftragsdetailsTable.item(self.getRowNr(einzelteilID), 4).setText(str(anzahl)) # in die Tabelle die Anzahl an berechneten Bauteilen reinschreiben
|
|
|
|
def checkWaageFinished(self, einzelteilID):
|
|
self.setRowColor(self.AuftragsdetailsTable, self.getRowNr(einzelteilID),0,255,0) #zum setzen den Farbe der gesamten Reihe auf Grün
|
|
|
|
if self.checkFinished(): # wenn alle Bauteile auf der Waage liegen, soll folgender Code ausgeführt werden
|
|
self.setRowColor(self.ArbeitsschrittTable, 2,0,255,0) # zum setzen der zweiten Spalte der Arbeitsschrittetabelle auf orange
|
|
self.setRowColor(self.ArbeitsschrittTable, 3,0,255,0) # zum setzen der dritten Spalte der Arbeitsschrittetabelle auf weiß
|
|
else: # wenn noch nicht alle Bauteile des Auftrags auf der Waage liegen, soll folgernder Code ausgeführt werden
|
|
self.setRowColor(self.ArbeitsschrittTable, 1,255,165,0) # zum setzen der zweiten Spalte der Arbeitsschrittetabelle auf orange
|
|
self.setRowColor(self.ArbeitsschrittTable, 2,255,255,255) # zum setzen der dritten Spalte der Arbeitsschrittetabelle auf weiß
|
|
if self.checkBoxAutoTare.isChecked():
|
|
self.onTareClick()
|
|
|
|
# den Thread beenden
|
|
self.checkWaageThread.quit()
|
|
self.checkWaageThread.wait()
|
|
|
|
# auch wenn die Länge der Liste überschritten wurde
|
|
# wenn der statische Workflow aktiviert ist, dann soll die checkwaage-funktion erneut aufgerufen werden
|
|
if(self.checkBox.isChecked() == True):
|
|
self.checkWaage()
|
|
|
|
# GUI updaten
|
|
#self.updateGUI()
|
|
|
|
def objectTypeDetection(self):
|
|
# in dieser Funktion wird der Typ des Bauteils automatisch erkannt
|
|
|
|
# wenn der statische Workflow ausgewählt wurde, dann soll die checkWaage Funktion aufgerufen werden, auch wenn die detectBauteiltyp-Funkion aufgerufen wurde
|
|
if self.checkBox.isChecked() == True:
|
|
self.checkWaage()
|
|
return
|
|
|
|
'''
|
|
if(self.checkPosNrEmpty()==True):
|
|
print("Das Pos.-Nr.-Feld ist leer.")
|
|
return
|
|
'''
|
|
|
|
# die Loopvariable des Workers auf False setzten, damit die Schleife durchgelaufen wird (diese wird zum Abbruch der Schleife benötigt -> siehe stopLoop)
|
|
#self.objectDetectionWorker.stopLoopSignal.emit(False)
|
|
|
|
# überprüfen, ob der Auftrag in die Tabelle geladen wurde
|
|
if(self.AuftragsdetailsTable.item(0,0) == None):
|
|
print("Der Auftrag muss zuerst geladen werden.")
|
|
return
|
|
|
|
# speichert den Wert, welcher im Textfeld steht in Variablen im Code
|
|
self.setAuftragsnummer()
|
|
|
|
# starten des Threats, welcher den Bauteiltyp zurück gibt
|
|
self.objectDetectionWorker = Worker()
|
|
self.objectDetectionThread = QThread()
|
|
|
|
self.objectDetectionWorker.moveToThread(self.objectDetectionThread)
|
|
|
|
self.objectDetectionWorker.objectDetectionStartSignal.connect(self.objectDetectionWorker.objectTypeDetectionThread)
|
|
self.objectDetectionWorker.objectDetectionFinishedSignal.connect(self.objectTypeDetectionFinished)
|
|
self.objectDetectionWorker.waageStoppedSignal.connect(self.threadStopped)
|
|
self.objectDetectionWorker.stopLoopSignal.connect(lambda status: setattr(self.objectDetectionWorker, 'stopLoop', status)) #wird fürs aktive Stoppen des Threads benötigt
|
|
|
|
self.objectDetectionThread.started.connect(lambda: self.objectDetectionWorker.objectDetectionStartSignal.emit(self.auftragsnummer))
|
|
self.objectDetectionThread.start()
|
|
|
|
def objectTypeDetectionFinished(self, einzelteilID, rowData):
|
|
# wenn der Typ des Objektes erkannt wurde, dann soll die Funktion aufgerufen werden
|
|
|
|
# Schreiben der Bauteiltype und Pos.-Nr in die jeweiligen Felder
|
|
self.PosNrTxtFeld.setText(str(rowData[1]))
|
|
self.bauteiltypTextbox.setText(rowData[2])
|
|
|
|
# setzt den Boolean, um aus der Schleife raus zu gehen
|
|
self.btTypeIsSet = True
|
|
|
|
#zum setzen den Farbe der gesamten Reihe auf Orange
|
|
self.setRowColor(self.AuftragsdetailsTable, self.getRowNr(einzelteilID),255,165,0)
|
|
|
|
# den Thread beenden
|
|
self.objectDetectionThread.quit()
|
|
self.objectDetectionThread.wait()
|
|
|
|
'''
|
|
try:
|
|
self.objectDetectionThread.started.disconnect() # Trenne das Signal, damit es beim nächsten Start keine Konflikte gibt
|
|
except:
|
|
print(f"Error while disconnecting: {e}")
|
|
print("Disconnecting the thread did not work.")
|
|
'''
|
|
|
|
# wenn der Bauteiltyp erkannt wurde, dann soll die GUI aktualisiert werden und anschließend die CheckWaage-Funktion aufgerufen werden
|
|
self.updateGUI()
|
|
self.checkWaage()
|
|
|
|
def threadStopped(self, einzelteilID):
|
|
# wenn kein Bauteil erkannt wurde, dann ist die EinzenteilID = 0
|
|
if self.checkFinished() == False:
|
|
if(einzelteilID > 0):
|
|
self.setRowColor(self.AuftragsdetailsTable, self.getRowNr(einzelteilID),255,255,255) #zum setzen den Farbe der gesamten Reihe auf Weiß
|
|
self.setRowColor(self.ArbeitsschrittTable, 2,255,255,255)
|
|
self.setRowColor(self.ArbeitsschrittTable, 1,255,165,0)
|
|
|
|
# den Thread beenden
|
|
if hasattr(self, 'objectDetectionThread'):
|
|
self.objectDetectionThread.quit()
|
|
self.objectDetectionThread.wait()
|
|
print("Der ObjektDetection-Thread wurde beendet.")
|
|
|
|
if hasattr(self, 'checkWaageThread'):
|
|
self.checkWaageThread.quit()
|
|
self.checkWaageThread.wait()
|
|
print("Der CheckWaage-Thread wurde beendet.")
|
|
|
|
|
|
# new class for Camera Object detection with YOLOv8
|
|
class CameraStreamApp(QtWidgets.QMainWindow):
|
|
def __init__(self, ui):
|
|
super().__init__()
|
|
self.ui = ui
|
|
self.yolo_stream = None # Initialize YOLOv8CameraStream as None
|
|
self.timer = QtCore.QTimer(self)
|
|
self.timer.timeout.connect(self.update_frame)
|
|
|
|
# Populate the model dropdown menu
|
|
self.populate_model_dropdown()
|
|
|
|
self.ui.startCamBtn.clicked.connect(self.start_camera) # start camera if button LoadAuftrag is clicked
|
|
self.ui.stopCamBtn.clicked.connect(self.stop_camera) # start camera if button LoadAuftrag is clicked
|
|
self.scene = QtWidgets.QGraphicsScene(self)
|
|
self.ui.graphicsView.setScene(self.scene)
|
|
|
|
def populate_model_dropdown(self):
|
|
"""Populate the dropdown menu with model files from the models directory."""
|
|
models_dir = "models"
|
|
model_files = [f for f in os.listdir(models_dir) if f.endswith(".pt")]
|
|
self.ui.modelComboBox.clear()
|
|
self.ui.modelComboBox.addItems(model_files)
|
|
|
|
def start_camera(self):
|
|
# Start the YOLOv8 camera stream (only if not already started)
|
|
if self.yolo_stream is None:
|
|
self.yolo_stream = YOLOv8CameraStream(model_path="models/yolov8m_seg_e300.pt", logging_level="high")
|
|
# self.yolo_stream.start() # Start the YOLOv8 stream
|
|
self.timer.start(30) # Start the timer to update the frame every 30ms (about 33 FPS)
|
|
|
|
def stop_camera(self):
|
|
# Stop the camera stream and processing
|
|
if self.yolo_stream is not None:
|
|
self.timer.stop() # Stop the timer
|
|
self.yolo_stream.cap.release() # Release the camera resource
|
|
self.yolo_stream = None # Reset the YOLOv8 stream object
|
|
self.scene.clear() # Clear the displayed frame from the graphicsView
|
|
print("Camera stream stopped and resources released.")
|
|
|
|
def update_frame(self):
|
|
# Update the frame from YOLOv8 stream
|
|
if self.yolo_stream:
|
|
ret, frame = self.yolo_stream.cap.read() # Capture frame from YOLOv8 stream
|
|
|
|
if ret:
|
|
# new part including processing via yolo model
|
|
processed_frame = self.yolo_stream.process_frame(frame)
|
|
|
|
# Convert the frame from BGR (OpenCV format) to RGB
|
|
# frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
|
frame = cv2.cvtColor(processed_frame, cv2.COLOR_BGR2RGB) # might change nomenclature later?
|
|
|
|
# Convert the frame to QImage
|
|
h, w, ch = frame.shape
|
|
bytes_per_line = ch * w
|
|
qt_image = QtGui.QImage(frame.data, w, h, bytes_per_line, QtGui.QImage.Format_RGB888)
|
|
|
|
# Add the QImage to a QPixmap
|
|
pixmap = QtGui.QPixmap.fromImage(qt_image)
|
|
|
|
# Get the size of the graphicsView and scale the pixmap to fit
|
|
view_size = self.ui.graphicsView.size()
|
|
scaled_pixmap = pixmap.scaled(view_size, QtCore.Qt.KeepAspectRatio)
|
|
|
|
# Update the scene with the scaled pixmap
|
|
self.scene.clear()
|
|
self.scene.addPixmap(scaled_pixmap)
|
|
|
|
def closeEvent(self, event):
|
|
# Release the camera when the application is closed
|
|
if self.yolo_stream is not None:
|
|
self.yolo_stream.cap.release()
|
|
event.accept()
|
|
|
|
# new class for relay control
|
|
class RelayControl(QtWidgets.QMainWindow):
|
|
def __init__(self, ui):
|
|
super().__init__()
|
|
self.ui = ui
|
|
|
|
self.ui.startSpotlightBtn.clicked.connect(self.spot_on)
|
|
self.ui.stopSpotlightBtn.clicked.connect(self.spot_off)
|
|
self.r = sainsmartrelay.SainsmartRelay()
|
|
|
|
def spot_on(self):
|
|
print("Turn on light clicked")
|
|
self.r.turn_on(1)
|
|
|
|
def spot_off(self):
|
|
print("Turn off light clicked")
|
|
self.r.turn_off(1)
|
|
|
|
def red_on(self):
|
|
print("Red light is on")
|
|
self.r.turn_on(2)
|
|
|
|
def red_off(self):
|
|
print("Red light is off")
|
|
self.r.turn_off(2)
|
|
|
|
def yellow_on(self):
|
|
print("Yellow light is on")
|
|
self.r.turn_on(3)
|
|
|
|
def yellow_off(self):
|
|
print("Yellow light is off")
|
|
self.r.turn_off(3)
|
|
|
|
def green_on(self):
|
|
print("Green light is on")
|
|
self.r.turn_on(4)
|
|
|
|
def green_off(self):
|
|
print("Green light is off")
|
|
self.r.turn_off(4)
|
|
|
|
if __name__ == "__main__":
|
|
import sys
|
|
app = QtWidgets.QApplication(sys.argv)
|
|
MainWindow = QtWidgets.QMainWindow()
|
|
ui = Ui_MainWindow()
|
|
ui.setupUi(MainWindow)
|
|
|
|
# Initialize the CameraStreamApp with the UI
|
|
camera_app = CameraStreamApp(ui)
|
|
# initialize relay app
|
|
relay_app = RelayControl(ui)
|
|
|
|
MainWindow.show()
|
|
sys.exit(app.exec_()) |