Steuerungs-Skript

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
Benutzeravatar
mobby
User
Beiträge: 76
Registriert: Donnerstag 17. April 2014, 09:43

Hey liebe Python Community,

folgendes Problem bzw. Situation, ich möchte mit einem Steuerungs-Skript einige Aufgaben erledigen. Im Folgenden die Aufgaben:

1. Das Skript startet und geht davon aus, dass noch kein Zähler läuft (das ist gewollt so), also haben wir eine Liste, in der die Stati der Zähler enthalten sind, 8 Zähler gesamt.
2. Der Initiations-Prozess beginnt, bedeutet, dass als erstes eine Konfigurationsdatei gelesen wird, in der drin steht, ob ein Zähler aktiviert wurde oder nicht.
3. Erste Abfrage besteht daraus, zu überprüfen ob Zähler x schon aktivert wurde, ist dies der Fall (kann beim ersten Start nicht sein), wird der aktuelle For-Schleifen Durchlauf übersprungen.
4. Als nächstes wird überprüft, ob der Zähler x aktiviert wurde. Ist dies der Fall, soll ein Thread mit dem entsprechenden Skript starten ( dazu später mehr). Und anschließend der Status des Zählers x auf "1" also ON gestellt werden.
5. Als nächste Abfrage wird überprüft, ob der Zähler deaktiviert wurde, aber der Thread noch läuft. Ist dies der Fall, soll der Thread gestoppt werden und der Status wieder auf "0" OFF gestellt werden.
6. Falls nichts der Fall sein sollte, wird der nächste Zähler überprüft.
7. Am ende soll die Status-Liste der Zähler übergeben werden, also im nächsten Durchlauf den gleichen Inhalt haben, dabei bin ich mir nicht sicher, ob ich das so richtig gemacht habe.

Zum Thema Threads ... keine Ahnung ob das so richtig ist wie ich den Thread zu stoppen versuche, ich gehe mal davon aus, dass es nicht stimmt. Bin aber irgendwie net so ganz schlau aus der Doku geworden, da bräuchte ich noch etwas Hilfe.

Neben den Thread Themen eine allgemeine Frage an alle, ob das sonst passt, also die Vorgehensweise, oder ob man was ganz anders gestalten sollte? Danke für Feedback! :)

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*- 


import read_ec as ec
import read_s0 as s0
import config_datei as cod
from time import sleep
from threading import Lock
from threading import Thread


def read_config_status():
    if is_changed(config_datei):
        config_datei = reload(config_datei)
    return cod.config["counterStates"]


def start_s0(x):
    number = x + 1
    intervall = 900
    frequency = 100
    digital_input = x
    led_output = x
    table_name = "S0_Zaehler_0" + number
    s0.main(intervall, frequency, table_name, digital_input, led_output)


def set_on(counter_status, x):
    counter_status[x] = 1
    return counter_status
    
    
def set_off(counter_status, x):
    counter_status[x] = 0
    return counter_status


def initiation_process(counter_status):
    config_status = read_config_status()
    for x in range(8):
        if counter_status[x] = 1:
            break
        elif config_status[x] = "active":
            start_s0_thread(x)
            set_on(counter_status, x)
            break
        elif config_status[x] = "inactive" and counter_status[x] = 1:
            stop_s0_thread(x) #der jeweilige thread soll beendet werden
            set_off(counter_status, x)
            break
        else:
            continue
    return counter_status #soll die Liste mit den Stati weiterschleifen


def start_s0_thread(x):
    thread = Thread(target=start_s0, name=x args=(number))
    thread.setDaemon(True)
    thread.start


def stop_s0_thread(x): #keine Ahnung ob das so richtig ist ... 
    thread = Thread(name=x) #soll den Thread mit dem namen x stoppen ... ?!
    thread.stop


def main():
    counter_status = [
            "0",
            "0",
            "0",
            "0",
            "0",
            "0",
            "0",
            "0",
    ]
    while True:
        initiation_process(counter_status)
        sleep(60)
        

if __name__ == '__main__':
    main()
BlackJack

@mobby: Das ist eine Menge Code dafür das der *einige* Syntaxfehler enthält. Das ist also nichts was Du mal laufen lassen hast. Vergleiche werden mit ``==`` und nicht mit ``=`` gemacht, und Argumente werden durch ``,`` und nicht durch Leerzeichen getrennt.

`config_datei` wird benutzt bevor es definiert wird, letztendlich ist der Name ja aber sowieso `cod`, wobei `config_datei` wesentlich deutlicher ist. Abkürzungen sollte man vermeiden wenn sie das Programm schwerer zu verstehen machen. In einem Modul das als Konfiguration verwendet wird, würde ich die Daten auf Modulebene erwarten und nicht noch einmal eine Ebene weiter in ein Wörterbuch mit dem Namen `config` verschoben.

Die `is_changed()`-Funktion ist nicht definiert.

Der Code ist sich nicht darüber einig was für Werte `counter_status` haben kann. 0, 1, '0'? Vielleicht auch '1'? Die Zahlen 0 und 1 sind nicht das gleiche wie Zeichenketten mit der Ziffer 0 oder 1. Und an der Stelle hätte man vielleicht auch `True` und `False` gewählt um klar zu machen, dass es keine 2 geben kann.

`set_on()` und `set_off()` sind fast identisch, da würde sich wohl eher eine `set_counter_state()`-Funktion anbieten die den Status als Argument bekommt. Da der Rückgabewert nicht nur unnötig, sondern auch sehr unschön ist, fragt sich ob man hier überhaupt eine Funktion für braucht. Die API ist unschön weil das übergebene Argument verändert *und* zurückgegeben wird. Der Aufrufer hat diesen Wert doch aber schon, sonst hätte er ihn nicht übergeben können.

Das gleiche gilt für `initiation_process()`. Das was der Kommentar am Ende suggeriert ist nicht schön. Was man machen kann, und was in Python auch üblicher wäre, ist hier eine *neue* Liste zu erstellen und die dann zurück zu geben. Dann wird man auch den `x`-Index los. Der zusätzlich noch das unschönde Problem hat, dass dort die 8 hart ins Programm kodiert ist, statt sich an der Länge der Konfiguration für die Counter zu orientieren.

Die Logik in der Schleife funktioniert nicht. Man kann einen Counter nicht stoppen, denn wen er läuft, dann wird nichts weiter geprüft. Ich denke auch das die ganzen ``break``\s da falsch sind. Die verlassen die *Schleife*.

Threads werden nicht gestartet, weil die `start()`-Methode nicht *aufgerufen* wird.

Und nein, so kann man Threads nicht stoppen. Du merkst Dir die gestarteten Thread-Objekte nirgends, also kannst Du darauf keinen Einfluss mehr nehmen. Man kann über eine ensprechende Funktion zwar über alle Threads iterieren, aber eigentlich möchte man sie sich selbst in einer geeigneten Datenstruktur merken. Also zum Beispiel das der `counter_status` nicht 0 oder 1 enthält sondern `None` oder ein `Thread`-Objekt. Das nächste ist, dass man Threads nicht von aussen stoppen kann. Die müssen eine Möglichkeit bieten das man ihnen mitteilt, dass sie sich selber beenden sollen, und das müssen sie dann auch tun.
Benutzeravatar
mobby
User
Beiträge: 76
Registriert: Donnerstag 17. April 2014, 09:43

@BlackJack: Vielen lieben Dank für das ausführliche Feedback. Ich muss gestehen, dass ich den Code wirklich kein einziges Mal ausprobiert hatte, wird nicht mehr vorkommen. Ich versuche die angesprochenen Punkte umzusetzen und werde mich zurückmelden, sobald ich weiter bin. :)
Benutzeravatar
mobby
User
Beiträge: 76
Registriert: Donnerstag 17. April 2014, 09:43

Die `is_changed()`-Funktion ist nicht definiert.
Habe ich *vercheckt*, Modul wird jetzt einfach vor jedem Lesen neu geladen. Außerdem habe ich das mit den Bibliotheken bzw. Listen auf Moduleben erst mal noch belassen. Funktioniert soweit ja auf jeden Fall und es gibt Dinge mit höherer Priorität in diesem Projekt.

Code: Alles auswählen

def read_config_status():
    config_datei = reload(config_datei)
    return config_datei.counterStates
Der Code ist sich nicht darüber einig was für Werte `counter_status` haben kann. 0, 1, '0'? Vielleicht auch '1'? Die Zahlen 0 und 1 sind nicht das gleiche wie Zeichenketten mit der Ziffer 0 oder 1. Und an der Stelle hätte man vielleicht auch `True` und `False` gewählt um klar zu machen, dass es keine 2 geben kann.
Hab ich abgeändert, klingt sehr sinnvoll :).
`set_on()` und `set_off()` sind fast identisch, da würde sich wohl eher eine `set_counter_state()`-Funktion anbieten die den Status als Argument bekommt. Da der Rückgabewert nicht nur unnötig, sondern auch sehr unschön ist, fragt sich ob man hier überhaupt eine Funktion für braucht. Die API ist unschön weil das übergebene Argument verändert *und* zurückgegeben wird. Der Aufrufer hat diesen Wert doch aber schon, sonst hätte er ihn nicht übergeben können.
Habe ich ganz rausgenommen, Status wird jetzt direkt über die Liste ersetzt.
Das gleiche gilt für `initiation_process()`. Das was der Kommentar am Ende suggeriert ist nicht schön. Was man machen kann, und was in Python auch üblicher wäre, ist hier eine *neue* Liste zu erstellen und die dann zurück zu geben. Dann wird man auch den `x`-Index los. Der zusätzlich noch das unschönde Problem hat, dass dort die 8 hart ins Programm kodiert ist, statt sich an der Länge der Konfiguration für die Counter zu orientieren.
Das mit der Länge der Konfiguration kann man einfügen, habe ich jetzt aber erst mal gelassen. Das mit der neuen Liste verstehe ich nicht ganz, wie soll ich das verstehen?
Die Logik in der Schleife funktioniert nicht. Man kann einen Counter nicht stoppen, denn wen er läuft, dann wird nichts weiter geprüft. Ich denke auch das die ganzen ``break``\s da falsch sind. Die verlassen die *Schleife*.
Breaks sind raus und auch die Logik etwas angepasst. Allerdings wie oben schon angedeutet, bin ich mir mit der Liste noch nicht ganz sicher, ob das so passt. Auch ist mir nicht klar, ob wenn ich mit return die Liste, unabhängig davon ob sie jetzt neu erstellt wurde oder die bestehende ist, zurückgegebe, die zurückgegeben beim erneuten while-Schleifen Durchlauf erkannt wird?

Code: Alles auswählen

def initiation_process(counter_status):
    config_status = read_config_status()
    for x in range(8):
        if config_status[x] == "active" and counter_status[x] == True:
            continue
        elif config_status[x] == "active" and counter_status[x] == False:
            start_s0_thread(x)
            counter_status[x] = True
        elif config_status[x] == "inactive" and counter_status[x] == True:
            stop_s0_thread(x) 
            counter_status[x] = False
        else:
            continue
    return counter_status

Code: Alles auswählen

def main():
    counter_status = [
            False,
            False,
            False,
            False,
            False,
            False,
            False,
            False,
    ]
    while True:
        initiation_process(counter_status)
        sleep(60)
Threads werden nicht gestartet, weil die `start()`-Methode nicht *aufgerufen* wird.

Und nein, so kann man Threads nicht stoppen. Du merkst Dir die gestarteten Thread-Objekte nirgends, also kannst Du darauf keinen Einfluss mehr nehmen. Man kann über eine ensprechende Funktion zwar über alle Threads iterieren, aber eigentlich möchte man sie sich selbst in einer geeigneten Datenstruktur merken. Also zum Beispiel das der `counter_status` nicht 0 oder 1 enthält sondern `None` oder ein `Thread`-Objekt. Das nächste ist, dass man Threads nicht von aussen stoppen kann. Die müssen eine Möglichkeit bieten das man ihnen mitteilt, dass sie sich selber beenden sollen, und das müssen sie dann auch tun.
Ich denk, dass das fast ein eigenes Thema ist, ich versuche erst mal alle anderen Punkte abzuarbeiten, um das als letztes zu realisieren.
BlackJack

@mobby: Was meinst Du mit „erkannt”? Es wird eine Liste übergeben, das ist die *selbe* Liste die beim Aufrufer auch an den Namen `counter_status` gebunden ist. Wenn man da in der Liste Veränderungen vornimmt, dann ändert man *die* Liste.

Die Zweige mit dem ``continue`` sind überflüssig, denn das würde ohne diese Anweisung ja sowieso passieren.

Wenn man nicht diesen Status als indirektion verwenden würde sondern dort direkt Objekte speichert, die einen Thread/Counter repräsentieren oder `None` wenn der entsprechende Counter nicht läuft, dann liesse sich das ungefähr so schreiben:

Code: Alles auswählen

from itertools import izip, repeat
# ...


def initiation_process(counters):
    result = list()
    for counter_status, counter in izip(read_config_status(), counters):
        if counters_status == 'active' and counter is None:
            counter = start_s0_thread()
        elif counter_status == 'inactive' and counter is not None:
            stop_s0_thread(counter)
            counter = None
        result.append(counter)
    return result


def main():
    counters = repeat(None)
    while True:
        counters = initiation_process(counters)
        sleep(60)
Wobei es vielleicht mehr Sinn machen würde mit Objekten zu starten welche die Zähler repräsentieren und die jeweils eine `start()`- und eine `stop()`-Methode besitzen und eine Möglichkeit zum testen ob sie gerade laufen. Also ein Attribut, ein Property, oder eine Methode.
Zuletzt geändert von BlackJack am Montag 21. Juli 2014, 10:59, insgesamt 1-mal geändert.
Grund: Fehler in Quelltext beseitigt
Benutzeravatar
mobby
User
Beiträge: 76
Registriert: Donnerstag 17. April 2014, 09:43

@BlackJack: Oha, jetzt wirds echt tricky ^^ Also ich glaube mit deinem Ansatz funktioniert meine Idee nicht, weil ich nicht nur eine absolute Anzahl an Zählern habe, sondern feste Nummern, also es kann die Situtaion auftreten, dass Zähler 1 und Zähler 5 gestartet sind und nicht nur absolut 2 Stück. Es muss also die Position in der Liste berücksichtigt werden. Ich habe mal einige Ansätze von dir übernommen und mit meinem Modell kombiniert. Diesmal auch getestet und funktioniert auch so, wie geplant:

Code: Alles auswählen

import config_datei
from time import sleep


def read_config_status():
    reload(config_datei)
    return config_datei.counterStates


def initiation_process(counters_status):
    config_status = read_config_status()
    for x in range(len(counters_status)):
        if config_status[x] == "active" and counters_status[x] == False:
            print "Nr " + str(x + 1) + " wird gestartet"
            counters_status[x] = True
        elif config_status[x] == "inactive" and counters_status[x] == True:
            print "Nr " + str(x + 1) + " wird gestoppt"
            counters_status[x] = False
    return counters_status


def main():
    counters_status = []
    for x in range(len(read_config_status())):
        counters_status.append(False)
    while True:
        counters_status = initiation_process(counters_status)
        sleep(60)
        

if __name__ == '__main__':
    main()
Die Print Befehle ersetzen das Starten der Threads, um erstmal die Funktion der Schleife zu prüfen. Jetzt muss ich nur ein Objekt für das Starten meiner Threads bauen, bei dem die absolute Nummer des Zählers mit übergeben wird, sowie dessen Status, um dem Thread intern zu übergeben, dass wenn sein Status auf False springt, er sich beenden soll. Ein Ansatz dafür wäre sehr hilfreich, allerdings versuch ichs auch gerne erst mal selbst. ;)
BlackJack

mobby: Ja ich hatte da einen dummen Fehler in der Funktion. Jetzt sollte es funktionieren, weil die Liste mit immer für jeden Zähler einen Eintrag enthält.
Benutzeravatar
mobby
User
Beiträge: 76
Registriert: Donnerstag 17. April 2014, 09:43

@BlackJack: Hm, ne irgendwie nicht. Bei deinem Code stelle ich fest, dass bei jedem Durchlauf die Zähler neu gestartet werden, aber das ist ja genau das, was ich verhindern will. Ist der Zähler einmal gestartet muss er nicht neu gestartet werden, sondern es soll nichts pasieren. Erst wenn der Status aus der config_datei sich ändert, soll etwas passieren, nämlich der Stopp des Zählers, bzw. eine Parameter-Übergabe an den jeweiligen Thread, dass er sich von selbst schließt. Außerdem habe ich so auch keine eindeutige Identifizierung des jeweiligen Zählers, also die Geschichte mit x passt in mein Konzept schon ganz gut, weil in dem zu startenden Thread brauch ich diesen Wert des Zählers.

Das versteh ich auch einfach nicht. Mir ist klar, dass ich beim Starten des Threads Argumente in Form eines Status übergeben kann. Intern im Thread kann ich dieses Argument überprüfen und sobald es sich ändert die Funktion, bzw. den Thread schließen. Nur jetzt die kritische Frage: Wie kann ich während der Thread bereits gestartet wurde, also im Nachhinein, den Parameter neu übergeben bzw. ihn ändern?
BlackJack

@mobby: Das kann eigentlich nur passieren wenn `start_s0_thread()` den Wert `None` zurück gibt, und nicht irgendwas was den laufenden Zähler repräsentiert. Genau das wäre dann ja auch die eindeutige Identifikationsmöglichkeit.

Du musst Dir den Wert halt irgendwie merken. In meinem Ansatz wäre dass das was die `start_s0_thread()`-Funktion zurück gibt. Das muss ein Objekt sein, über das man mit dem Thread kommunizieren kann.

Ab ”schönsten” wäre es an der Stelle vielleicht wenn man einfach für jeden Zähler an der Stelle ein Objekt mit `start()`, `stop()` und `is_running` hat, über das man einen Zähler starten, stoppen, und den Zustand prüfen kann.
Benutzeravatar
mobby
User
Beiträge: 76
Registriert: Donnerstag 17. April 2014, 09:43

@BlackJack: Okay, also ich habe jetzt mal was versucht ... ich bin mir zwar relativ sicher, dass es aus professioneller Sicht ziemlich katastrophal ist, aber für den Fall, dass es sich nur um einen Zähler handeln würde, funktioniert es. Jetzt meine Herausforderung, wie projeziere ich das mit dem Objekt auf mehrere Zähler?

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*- 


import config_datei
from time import sleep
from threading import Lock
from threading import Thread


def read_config_status():
    reload(config_datei)
    return config_datei.counterStates


def start_s0(x, status):
    print "S0_Zaehler_0" + str(x + 1)
    print status.get_status()
    while True:
        if status.get_status() == False:
            break
        sleep(5)
   
   
def start_s0_thread(x, status):
    thread = Thread(target=start_s0, args=(x, status))
    thread.setDaemon(True)
    thread.start()


class Status(object):
    def __init__(self, status=True):
        self.status = status
        self.lock = Lock()

    def get_status(self):
        with self.lock:
            result = self.status
        return result

    def set_status_OFF(self):
        with self.lock:
            self.status = False


def initiation_process(counters_status):
    status = Status()
    config_status = read_config_status()
    for x in range(len(counters_status)):
        if config_status[x] == "active" and counters_status[x] == False:
            print "Nr " + str(x + 1) + " wird gestartet"
            counters_status[x] = True
            start_s0_thread(x, status)
        elif config_status[x] == "inactive" and counters_status[x] == True:
            print "Nr " + str(x + 1) + " wird gestoppt"
            counters_status[x] = False
            status.set_status_OFF()
    return counters_status


def main():
    counters_status = list()
    for x in range(len(read_config_status())):
        counters_status.append(False)
    while True:
        counters_status = initiation_process(counters_status)
        sleep(10)
        

if __name__ == '__main__':
    main()
BlackJack

@mobby: Wenn Du für jeden Zähler einen Status haben möchtest, dann musst Du halt genau das machen: Für jeden Zähler einen Status erstellen und nicht einen für alle. Damit wird die `counter_status`-Liste in der Form unnötig, weil das ja genau das selbe `True` oder `False` drin stehen würde wie in den `Status`-Exemplaren.

Zu dem hatte ich ja schon mal gesagt, dass die Klasse so wie sie jetzt ist, unnötig und überkompliziert ist. Stattdessen sollten man, und auch hier wiederhole ich mich, eher eine Klasse verwenden über die man den Zähler starten, stoppen, und den Status abfragen kann.
Benutzeravatar
mobby
User
Beiträge: 76
Registriert: Donnerstag 17. April 2014, 09:43

@BlackJack: Jetzt funktionierts !!! :)

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*- 


import config_datei
from time import sleep
from threading import Thread


def read_config_status():
    reload(config_datei)
    return config_datei.counterStates


def start_s0(x, status):
    print "S0_Zaehler_0" + str(x + 1)
    while True:
        if status.get_status(x) == False:
            print "Thread wird beendet"
            break
        sleep(5)
   
   
def start_s0_thread(x, status):
    thread = Thread(target=start_s0, args=(x, status))
    thread.setDaemon(True)
    thread.start()


class Status(object):
    def __init__(self, status=list()):
        self.status = status
    
    def add_counters(self):
        self.status.append(False)
        
    def get_status(self, x):
        return self.status[x]

    def set_status_ON(self, x):
        self.status[x] = True

    def set_status_OFF(self, x):
        self.status[x] = False


def initiation_process(status):
    config_status = read_config_status()
    for x in range(len(config_status)):
        if config_status[x] == "active" and status.get_status(x) == False:
            print "Nr " + str(x + 1) + " wird gestartet"
            status.set_status_ON(x)
            start_s0_thread(x, status)
        elif config_status[x] == "inactive" and status.get_status(x) == True:
            print "Nr " + str(x + 1) + " wird gestoppt"
            status.set_status_OFF(x)


def main():
    status = Status()
    for x in range(len(read_config_status())):
        status.add_counters()
    while True:
        initiation_process(status)
        sleep(10)
        

if __name__ == '__main__':
    main()
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

mobby hat geschrieben:Jetzt funktionierts !!!
Nicht so ganz:

Code: Alles auswählen

>>> class Status(object):
...     def __init__(self, status=list()):
...         self.status = status
...     def add_counters(self):
...         self.status.append(False)
...     def get_status(self, x):
...         return self.status[x]
...     def set_status_ON(self, x):
...         self.status[x] = True
...     def set_status_OFF(self, x):
...         self.status[x] = False
...
>>> s1 = Status()
>>> s2 = Status()
>>> s1.add_counters()
>>> s1.set_status_ON(0)  # <-- s1!!
>>> s2.get_status(0)     # <-- s2!!
True
Überraschung!
In specifications, Murphy's Law supersedes Ohm's.
Benutzeravatar
mobby
User
Beiträge: 76
Registriert: Donnerstag 17. April 2014, 09:43

@pillmuncher: Was willst du mir damit sagen? Dass wenn ich zwei Objekte erstelle, aber nur Objekt 1 bearbeite der Wert trotzdem in Objekt 2 geändert wird? Auf jeden Fall nichts, was wünschenswert ist, aber ehrlich gesagt is das in meinem Beispiel eigentlich egal, da eh nur ein Objekt erstellt wird. Aber unabhängig davon, wie vermeide ich das und woher kommt dieser Effekt?
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

@mobby: Der Überltäter ist diese Zeile hier:

Code: Alles auswählen

    def __init__(self, status=list()):
Default-Parameter werden nur ein einziges mal erzeugt, nämlich wenn die Funktion, zu der sie gehören, angelegt wird. Danach gelten sie für jeden Aufruf der Funktion. Wenn man da ein veränderbares Objekt (zB. eine Liste) erzeugt, gelten alle Änderungen für dieses eine Objekt. Das in Python übliche Idiom, mit dem man das umgehen kann, ist dies hier:

Code: Alles auswählen

    def __init__(self, status=None):
        if status is None:
            self.status = []
        else:
            self.status = status
In deinem Fall ist aber die ganze Weise, wie das Status-Objekt angelegt wird, ungut. Nach dem Erzeugen sollte ein Objekt normalerweise sofort verwendbar sein. Bei dir muss man so oft add_counters() aufrufen, wie man Felder in einer Liste benötigt. Warum gibst du nicht die Länge der gewünschten Liste als Argument beim Erzeugen deines Status-Objektes an?

Code: Alles auswählen

    def __init__(self, length):
        self.status = [False] * length
und dann:

Code: Alles auswählen

def main():
    status = Status(len(read_config_status()))
dann brauchst du auch kein add_counters() mehr.
In specifications, Murphy's Law supersedes Ohm's.
Benutzeravatar
mobby
User
Beiträge: 76
Registriert: Donnerstag 17. April 2014, 09:43

@pillmuncher: Super, vielen Dank für die ausführliche Erklärung. Den Vorschlag habe ich so eingebaut :) Tip Top
Benutzeravatar
mobby
User
Beiträge: 76
Registriert: Donnerstag 17. April 2014, 09:43

Hey at all,

Update: Habe mein Steuerungsskript inzwischen so weit verbessert, dass sich alle Threads von alleine beenden, sobald dies im Steuerungsskript angewiesen wird. Dies wird über das in diesem Thema erarbeitete Objekt gelöst. Das funktioniert, weil ich in den Threads Aufgaben verteile die über eine While Schleife immer alle x Sekunden abgerufen werden. Bevor die Aufgabe ausgeführt wird, wird der status überprüft und falls er False sein sollte, mittels break die Schleife verlassen. So schließt sich auch automatisch der Thread. Nur jetzt habe ich ein Problem, sollte ich meinen Zähler entfernen und der Status springt auf False, wird mein einer Sub-Thread sofort beendet, da der sleep Timer im Millisekunden Bereich liegt, nur die Aufgabe in meinem Sub-Thread zum Schreiben in die Datenbank wird nur alle 5 / 10 / 15 Minuten abgerufen. Das bedeutet, dass der Sub-Thread und somit der komplette Thread für den Zähler nur bei Eintritt dieses Intervalls beendet wird. Will ich also Zähler 1 neu anlegen, muss ich warten bis das Intervall einmal abgelaufen ist, sonst ist der Thread noch aktiv und macht weiter, weil er wieder den Status auf True erhält.

Ich weiß ist alles etwas kompliziert erklärt, aber im Grunde geht es darum: Wie kann ich meinen Thread beenden, obwohl die Hauptaufgabe nur alle 5 Minuten ausgeführt wird? Hier mal meine bisherige Vorgehensweise. Dabei handelt es sich um einen Sub-Thread der sich selbst beenden soll, sobald status.get_status(x) auf False springt:

Code: Alles auswählen

def write_value(status, x, count, intervall, table_name):
    timestamp = int(time.time() / intervall) * intervall
    while True:
        timestamp += intervall
        time.sleep(max(0, timestamp-time.time()))
        s0_value = count.get_and_reset()
        datum = str(time.strftime("%d-%m-%Y"))
        uhrzeit = str(time.strftime("%H:%M:%S"))
        if status.get_status(x) == False:
            break
        else:
            db.insert_into_database(status, x, table_name, timestamp, datum, 
                uhrzeit, s0_value
            )
Vielen Dank für Hile !!!

Gruß
mobby
Sirius3
User
Beiträge: 17753
Registriert: Sonntag 21. Oktober 2012, 17:20

@mobby: statt selbst was zu erfinden, nimm doch das, was es schon gibt und was dafür gemacht wurde. In Deinem Fall 'threading.Event':

Code: Alles auswählen

def write_value(event, x, count, intervall, table_name):
    timestamp = int(time.time() / intervall) * intervall
    while not event.wait(max(0, timestamp-time.time())):
        timestamp += intervall
        s0_value = count.get_and_reset()
        datum = str(time.strftime("%d-%m-%Y"))
        uhrzeit = str(time.strftime("%H:%M:%S"))
        db.insert_into_database(True, x, table_name, timestamp, datum, uhrzeit, s0_value)
und irgendwoanders:

Code: Alles auswählen

event.set() # beendet Thread quasi sofort
Benutzeravatar
mobby
User
Beiträge: 76
Registriert: Donnerstag 17. April 2014, 09:43

@Sirius3: Perfekt, nach genau sowas habe ich gesucht. Vielen Dank für den Hinweis, jetzt läuft alles tadellos! :)
Antworten