Probleme mit Threads und laufender Funktion

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Hakan
User
Beiträge: 38
Registriert: Mittwoch 8. Februar 2017, 12:13

Hallo liebes Forum,

so langsam habe ich den Überblick verloren :oops: .. Mein Ziel war folgender. Ich habe im Hintergrund eine Funktion laufen die jederzeit von einem Sensor ein Wert abgreifen kann also konstant laufen muss. In meinem Fall ist das die Funktion rotary Deal. Das Problem ist aber, dass meine Threads nicht mehr berücksichtigt bzw gestartet werden sobald die rotaryDeal funktion läuft
:K

Code: Alles auswählen

def main():
        global globalCounter
        tmp = 0	# Rotary Temperary
        queue = Queue()
        queue2 = Queue()
        mess_thread = threading.Thread(target=mess_loop_RE, args=(queue,queue2, ))
        schreib_thread = threading.Thread(target=write_to_file, args=(queue,))
        senden_thread = threading.Thread(target=send_to_platform, args=(queue2,))
        mess_thread.start()
        schreib_thread.start()
        senden_thread.start()
        while True:
                rotaryDeal()
                if tmp != globalCounter:
                    print ('globalCounter = %d' % globalCounter)
                    print(GPIO.input(16))
                    print(GPIO.input(18))
                    print(GPIO.input(22))
                    tmp = globalCounter
                    
Hier nochmal die Funktion:

Code: Alles auswählen

def rotaryDeal():
    global flag
    global Last_RoB_Status
    global Current_RoB_Status
    global globalCounter
    Last_RoB_Status = GPIO.input(RoBPin)
    while(not GPIO.input(RoAPin)):
        Current_RoB_Status = GPIO.input(RoBPin)
        flag = 1
    if flag == 1:
        flag = 0
        if (Last_RoB_Status == 0) and (Current_RoB_Status == 1):
            globalCounter = globalCounter - 1
        if (Last_RoB_Status == 1) and (Current_RoB_Status == 0):
            globalCounter = globalCounter + 1
Ich hab schon mal versucht die Funktion ebenfalls als Thread zu deklarieren und zu starten aber leider funktioniert es so auch nicht! Bitte um Rat
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Hakan: was heißt "meine Threads nicht mehr berücksichtigt"? Was hast Du gemacht? Was erwartest Du, was beobachtest Du?
Aus Deinem Text wird nicht wirklich klar, was Du eigentlich machen willst.
Bevor Du weitermachst, solltest Du erstmal alle "global" loswerden und die unnötigen Klammern um die Bedingungen löschen.
Hakan
User
Beiträge: 38
Registriert: Mittwoch 8. Februar 2017, 12:13

Threads nicht berücksichtigt heißt es werden die Threads zwar gestartet...siehe Main!! Aber danach wird ausschließlich die Funktion rotaryDeal ausgeführt. Ich wollte aber das die Threads der reihe nach laufen und die Funktion rotaryDeal konstant im Hintergrund läuft. Ist doch einfach zu verstehen..wie ich das umsetzen soll ist hier die Frage :K
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Hakan: Du startest die Threads, also laufen sie auch. Daher die Frage, an was Du glaubst zu erkennen, dass sie doch nicht laufen. Hilfreich wäre auch, den Code zu zeigen, der nicht läuft. Und so lange Du "global" verwendest, ist es kein Wunder dass Du den Überblick verlierst.
Hakan
User
Beiträge: 38
Registriert: Mittwoch 8. Februar 2017, 12:13

@Sirius3: Wenn die Threads tatsächlich laufen würden, dann würden sie auch die Messwerte in eine Textdatei abspeichern, was leider nicht passiert. Ich werde morgen einen zweiten Anlauf nehmen und den Skript ohne global variablen schreiben. Wenn wir uns allein die Main anschauen Sirius, dann sehen wir ja, dass ich einige threads starte und nach while True: eine Funktion namens rotaryDeal aufrufe richtig? So und ich vermute dass die Funktion rotaryDeal irgendwie die Threads blockiert, da sie ja endlos ist bzw es gewartet wird bis die Funktion zu ende ist. Sie soll aber auch endlos laufen. Sinn und Zweck ist nähmlich, dass die Funktion Veränderungen eines Rotary Encoder wahrnimmt.
Benutzeravatar
noisefloor
User
Beiträge: 3856
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

du solltest uns auch mal die Funktione zeigen, die in den Threads ausgeführt werden. Die fehlen im Eingangspost.

Gruß, noisefloor
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

@Hakan: die Funktion rotaryDeal blockiert keine Threads. Dass Deine zuvor gestarteten Threads nicht das richtige tun, mag an dem Code liegen, den Du nicht gepostet hast.
Hakan
User
Beiträge: 38
Registriert: Mittwoch 8. Februar 2017, 12:13

Hallo liebe Forummitglieder,

hier ist der komplette Skript!!

Code: Alles auswählen

#!/usr/bin/env python
from datetime import datetime as DateTime
import json
import time
import RPi.GPIO as GPIO
import pytz
import socket
REMOTE_SERVER = "www.google.com"
from Queue import Queue
import requests
import threading
from pprint import pprint
RoAPin = 16    # pin11
RoBPin = 18    # pin12
BtnPin = 22    # Button Pin

globalCounter = 0

flag = 0
Last_RoB_Status = 0
Current_RoB_Status = 0

def setup():
	GPIO.setmode(GPIO.BOARD)       # Numbers GPIOs by physical location
	GPIO.setup(RoAPin, GPIO.IN)    # input mode
	GPIO.setup(RoBPin, GPIO.IN)
	GPIO.setup(BtnPin, GPIO.IN, pull_up_down=GPIO.PUD_UP)

def rotaryDeal():
    global flag
    global Last_RoB_Status
    global Current_RoB_Status
    global globalCounter
    Last_RoB_Status = GPIO.input(RoBPin)
    while(not GPIO.input(RoAPin)):
        Current_RoB_Status = GPIO.input(RoBPin)
        flag = 1
    if flag == 1:
        flag = 0
        if (Last_RoB_Status == 0) and (Current_RoB_Status == 1):
            globalCounter = globalCounter - 1
            print(globalCounter)
        if (Last_RoB_Status == 1) and (Current_RoB_Status == 0):
            globalCounter = globalCounter + 1
            print(globalCounter)

def value():
    global globalCounter
    value1 = globalCounter
    timestamp = DateTime.isoformat(DateTime.now(pytz.timezone('Europe/Berlin')))
    return value1,  timestamp
def Text_logfile_RE(value1,  timestamp):
    text = ("Zeitstempel:"+str(timestamp)+" globalcounter"+str(globalCounter))
    return text
def Messung_Dictionary(value1, timestamp):
    measurement = {
                    'time': timestamp,
                    'type':"c8y_demo",
					'source':{"id":"4420437"} ,
                    'Rotary-Encoder': {"Count":{
                    "value": value, "unit":"ME"}},
    }
    return measurement
    
def erstelle_Liste(measurement):
    jsonData = []
    jsonData.append(measurement)
    pprint(jsonData)
    print("---------------------------------")
    return jsonData

def schreibe_textdatei2(text):
    with open('Sensoren_logfile', 'a') as log_file:
        log_file.write(text)
        log_file.write('\n')
def is_connected():
  try:
    host = socket.gethostbyname(REMOTE_SERVER)
    s = socket.create_connection((host, 80), 2)
    return True
  except:
    pass
  return False
def senden(measurement):
        items = json.dumps(measurement)
        url = "xxxxx"
        payload = items
        headers = {
            'authorization': "xxxx",
            'content-type': "application/json",
            'accept': "application/vnd.com.nsn.cumulocity.measurement+json",
            'cache-control': "no-cache",
        }
        response = requests.request("POST", url, data=payload, headers=headers)
        print(response.text)
        print(str(response.status_code))
def send_again(queue2):
    while True:
        if is_connected() is True:
            senden(queue2.get())
            print("Erfolgreich versendet")
            break
        else: 
            a =str((DateTime.now().strftime("%Y-%m-%d %H:%M:%S")))
            print("Keine Internetverbindung vorhanden! "+a)
            time.sleep( 5 )
def send_to_platform(queue2):
    while True:
        try:
            senden(queue2.get())
        except requests.ConnectionError:
            send_again(queue2)
def write_to_file(queue):
    while True:
        schreibe_textdatei2(queue.get())

def mess_loop_RE(queue, queue2):
    while True:
        value1,  timestamp = value()
        Messwert = Messung_Dictionary(value1, timestamp)
        Schreibwert = Text_logfile_RE(timestamp,  value1)
        queue2.put(Messwert)
        queue.put(Schreibwert)
        time.sleep( 10 )
def main():
        global globalCounter
        tmp = 0   # Rotary Temperary
        queue = Queue()
        queue2 = Queue()
        mess_thread = threading.Thread(target=mess_loop_RE, args=(queue,queue2, ))
        schreib_thread = threading.Thread(target=write_to_file, args=(queue,))
        senden_thread = threading.Thread(target=send_to_platform, args=(queue2,))
        mess_thread.start()
        schreib_thread.start()
        senden_thread.start()
        while True:
                rotaryDeal()

setup()
main()
Ich habe bei der Funktion value() zwischendurch print befehle eingefügt um zu schauen in wie fern die threads laufen und komischer weise hört es vor timestamp auf... :K Dann können die anderen Threads ja auch nicht starten. HILFE!!
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

@Hakan: Also, das sieht ja übel aus. Kein Wunder, dass Du den Durchblick verloren hast. Ohne Dein Programm ausprobiert zu haben: zumindest in Zeile 61 hast Du ein Typo, das verhindert, dass der Thread läuft. Klar, dass dann weiter nichts geschieht.
Globals solltest Du meiden und pep8 beherzigen.
Am besten Du schreibst Dein Programm zunächst ohne Threads, bis Du eine lauffähige Version hast. Und zwar ohne globals. Dann kannst Du immer noch entscheiden, ob es erforderlich ist, dass einzelne Programmteile nebenläufig sind.
Zum Programmierstil selbst wirst Du wahrscheinlich noch eine Vielzahl von Hinweisen erhalten.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Hakan: ohne dass das ordentlich geschrieben ist, mit den richtigen Einrückungen und ein paar Leerzeilen und lesbaren Namen, will sich das glaube ich niemand anschauen.
Du bekommst einen Traceback, wo einer der Fehler liegt. Den zu beheben, dürfte kein Problem sein.

@kbr: ich würd ja eher sagen, Zeile 55 ist der Schreibfehler.
Hakan
User
Beiträge: 38
Registriert: Mittwoch 8. Februar 2017, 12:13

@kbr: Danke für dein Beitrag..Das einzige Problem ist folgendes: Die rotaryDeal() Funktion nimmt ja konstant Veränderungen vom Rotary Encoder wahr und gibt den wert an die globale Variable globalCounter über. Nun das ist auch gut so denn ich will ich im 10 Sekunden Intervall den aktuellen Messwert(globalCounter) abgreifen und leite diese dann an eine lokale variable(value1) in der Funktion value(). Sollte es keine globale Variable in der Funktion rotaryDeal() geben, bin ich gezwungen jeden Messwert zurückzugeben. Das will ich aber nicht... und wie soll das ohne global funktionieren? Das sieht übel aus..wie meinst du das ?? Was ich bisher gemacht habe ist doch eine Funktion nach der anderen deklariert und hinterher die main Funktion gestartet. Wie sollte es denn aussehen :K ich kann dir nicht ganz folgen?

@Sirius3, bei mir kommt kein Tracebackfehler wenn ich das Skript starte.

Also so komm ich auch nicht weiter in dem ich die Funktionsnamen verändere und paar Leerzeilen einbau... ich würde sagen das optimieren kommt eh immer zum Schluss. Dennoch wenn ihr meint es ist eine Anmaßung änder ich das eben um.
Hakan
User
Beiträge: 38
Registriert: Mittwoch 8. Februar 2017, 12:13

Jetzt nochmal etwas optimierter:

Code: Alles auswählen

#!/usr/bin/env python
from datetime import datetime as DateTime
import json
import time
import RPi.GPIO as GPIO
import pytz
import socket
REMOTE_SERVER = "www.google.com"
from Queue import Queue
import requests
import threading

RoAPin = 16    # pin11
RoBPin = 18    # pin12
BtnPin = 22    # Button Pin
globalCounter = 0
flag = 0
Last_RoB_Status = 0
Current_RoB_Status = 0

def setup_GPIO():
    GPIO.setmode(GPIO.BOARD) 
    GPIO.setup(RoAPin, GPIO.IN)
    GPIO.setup(RoBPin, GPIO.IN)
    GPIO.setup(BtnPin, GPIO.IN, pull_up_down=GPIO.PUD_UP)

def measurement_RotaryEncoder():
    global flag
    global Last_RoB_Status
    global Current_RoB_Status
    global globalCounter
    Last_RoB_Status = GPIO.input(RoBPin)
    while(not GPIO.input(RoAPin)):
        Current_RoB_Status = GPIO.input(RoBPin)
        flag = 1
    if flag == 1:
        flag = 0
        if (Last_RoB_Status == 0) and (Current_RoB_Status == 1):
            globalCounter = globalCounter - 1
            print(globalCounter)
        if (Last_RoB_Status == 1) and (Current_RoB_Status == 0):
            globalCounter = globalCounter + 1
            print(globalCounter)

def getvalue():
    global globalCounter
    value1 = globalCounter
    timestamp = DateTime.isoformat(DateTime.now(pytz.timezone('Europe/Berlin')))
    return value1,  timestamp

def getText(value1,  timestamp):
    text = ("Zeitstempel:"+str(timestamp)+" globalcounter"+str(globalCounter))
    return text

def measurement_dictionary(value1, timestamp):
    measurement = {
                    'time': timestamp,
                    'type':"c8y_demo",
					'source':{"id":"4420437"} ,
                    'Rotary-Encoder': {"Count":{
                    "value": value1, "unit":"ME"}},
    }
    return measurement

def write_logfile(text):
    with open('Sensoren_logfile', 'a') as log_file:
        log_file.write(text)
        log_file.write('\n')
        
def is_connected():
  try:
    host = socket.gethostbyname(REMOTE_SERVER)
    s = socket.create_connection((host, 80), 2)
    return True
  except:
    pass
  return False
  
def send_to_platform(measurement):
        items = json.dumps(measurement)
        url = "xxxx"
        payload = items
        headers = {
            'authorization': "xxxxxx",
            'content-type': "application/json",
            'accept': "application/vnd.com.nsn.cumulocity.measurement+json",
            'cache-control': "no-cache",
        }
        response = requests.request("POST", url, data=payload, headers=headers)
        print(response.text)
        print(str(response.status_code))

def send_again(queue2):
    while True:
        if is_connected() is True:
            send_to_platform(queue2.get())
            print("Erfolgreich versendet")
            break
        else: 
            a =str((DateTime.now().strftime("%Y-%m-%d %H:%M:%S")))
            print("Keine Internetverbindung vorhanden! "+a)
            time.sleep( 5 )

def send_to_platform_loop(queue2):
    while True:
        try:
            send_to_platform(queue2.get())
        except requests.ConnectionError:
            send_again(queue2)

def write_to_file_loop(queue):
    while True:
        write_logfile(queue.get())

def measurement_loop(queue, queue2):
    while True:
        value1,  timestamp = getvalue()
        Messwert = measurement_dictionary(value1, timestamp)
        Schreibwert = getText(timestamp,  value1)
        queue2.put(Messwert)
        queue.put(Schreibwert)
        time.sleep( 10 )

def main():
    global globalCounter
    queue = Queue()
    queue2 = Queue()
    mess_thread = threading.Thread(target=measurement_loop, args=(queue,queue2, ))
    schreib_thread = threading.Thread(target=write_to_file_loop, args=(queue,))
    senden_thread = threading.Thread(target=send_to_platform_loop, args=(queue2,))
    mess_thread.start()
    schreib_thread.start()
    senden_thread.start()
    while True:
        measurement_RotaryEncoder()

setup_GPIO()
main()
Benutzeravatar
noisefloor
User
Beiträge: 3856
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,
nimmt ja konstant Veränderungen vom Rotary Encoder wahr und gibt den wert an die globale Variable globalCounter über. Nun das ist auch gut so denn ich will ich im 10 Sekunden Intervall den aktuellen Messwert(globalCounter) abgreifen
Das ist doch schon ein Widerspruch in sich. Warum musst du den Wert des Encoders ununterbrochen erfassen, wenn du am Ende aber nur alle 10 Sekunden einen Wert speicherst? Was macht du mit den anderen X Messwerten? Nichts, oder? Von daher kannst du genau so gut auch alle 10 Sekunden eine Funtkion aufrufen, die dir genau einen Messwert zurück liefert.
Das sieht übel aus..wie meinst du das ??
Ein paar Punkte:
* uneindeutigen Funktions- und Variablennamen
* Zusammenbau von Strings mit + statt mit der format()-Methode von Strings
* teilweise triviale Funktionen, die keine Funktion sein müssten
* nackte `try... except` - man möchte normalerweise gezielt bestimmte Fehler abfangen und nicht global alle.
* globale Variablen, was in 99% der Fälle falsch / nicht nötig ist

Gruß, noisefloor
Hakan
User
Beiträge: 38
Registriert: Mittwoch 8. Februar 2017, 12:13

Denkt doch mal etwas mit. Der Raspberry pi kommt doch gar nicht mehr hinterher wenn er jeden Messwert verarbeiten muss und an die Platform senden muss usw. Nachher hab ich dann pro minute 1000 Posts an die Platform und der Server geht dann auch unter :lol: . Wenn das ganze nicht ununterbrochen läuft, dann sind die Messungen ja verfälscht dann kann ich auch gleich gar nicht erst messen und den Sensor aus dem Fenster werfen..
Was ich machen muss ist folgendes: Messwert des RotaryEncoder ununterbrochen erfassen. Dann alle 10 Sekunden den aktuellen Wert abfangen und diesen dann verarbeiten. Ich glaube so langsam habe ich euch mit der Aufgabe überfordert...
Benutzeravatar
noisefloor
User
Beiträge: 3856
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

[codebox=pycon file=Unbenannt.txt][/code]Hallo,
Nachher hab ich dann pro minute 1000 Posts an die Platform und der Server geht dann auch unter
Sagt ja auch keiner, dass du das machen sollst...
Wenn das ganze nicht ununterbrochen läuft, dann sind die Messungen ja verfälscht dann kann ich auch gleich gar nicht erst messen und den Sensor aus dem Fenster werfen..
Weil...? Nach deiner Aussage speicherst du einen Wert alle 10 Sekunden und machst nichts mit den restlichen Messwerten. Was willst du denn da verfälschen? Oder machst du doch was in den restlichen 9,9 Sekunden mit den Messwerten?

Mal abgesehen davon: der Drehgeber liefert doch so oder so das Signal - was ja nicht zwingend heißt, dass du was damit machen musst.

Dein Denkfehler ist immer noch, dass du meinst, dass `global` die Lösung ist. Ist es nicht. Wie schon geschrieben: `global` ist in 99% der Fälle falsch / schlechter Stil und die Aufgabe gehört nicht zu den verbleiben den 1%.

Eine mögliche Lösung wäre IMHO ein `deque()` mit Länge 1 zum Datenaustausch, wenn man denn #ausgründen nicht den Wert nur 1x alle 10 Sekunden abfragen will...

Code: Alles auswählen

from threading import Thread
from collections import deque
from time import sleep
from random import randint

queue = deque(maxlen=1)

def producer(queue):
    print('producer started...')
    while True:
        queue.append(randint(0,10))
        sleep(1)

def consumer(queue):
    print('consumer started...')
    while True:
        number = queue.pop()
        print('Got: {}'.format(number))
        sleep(5.5)
    
t1 = Thread(target=producer, args=(queue,))
t2 = Thread(target=consumer, args=(queue,))
t1.start()
t2.start()
Ich glaube so langsam habe ich euch mit der Aufgabe überfordert...
Na ja, da du ja hier die Frage gestellt hast und dein Skript nicht das macht, was du möchtest, liegt die Überforderung dann tendenziell doch eher auf deiner Seite ;-)

Gruß, noisefloor
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

@Hakan überfordert ist hier nienmand. Wenn du ein bisschen mehr Energie in eine vernünftige Beschreibung deines Programms investieren würdest, dann kann man da auch besser helfen.

Ich würde dir zum Beispiel für einen solchen Encoder gleich die pigpio Bibliothek nehmen. Die ist robuster, bringt das was du brauchst schon mit, und erlöst dich damit von der notwendigkeit, dich mit so vielen Threads selbst zu verwirren.

http://abyz.co.uk/rpi/pigpio/ex_rotary_encoder.html

Wenn du auf der RPI.GPIO bestehst, solltest du mit "interrupts" arbeiten, statt dem polling bei dem du dir viel Last und trotzdem auch noch weniger Zuverlässigkeit einfängst.
Hakan
User
Beiträge: 38
Registriert: Mittwoch 8. Februar 2017, 12:13

@noisefloor: Danke für dein Beitrag... Ja ist ja gut ich entschuldige mich euch zu unterstellen ihr seit überfordert. Ich bin manchmal emotional dabei wenn ich seit längerer Zeit nicht das hin bekomme was ich anstrebe. Ihr habt damit ja rein gar nichts am Hut..

@__deets__: Danke für dein Beitrag.. in Zukunft werde ich mich bemühen die wichtigen Merkmale für einen sauberen Programmierstil einzuhalten! Das sollte dann auch schon die halbe Miete sein :D
Antworten