Alarmlog mit Zeitstempel und Prio. 1&2

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.
Benutzeravatar
DeaD_EyE
User
Beiträge: 1240
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Code: Alles auswählen

# leere Alarm-Liste erzeugen
alarmmeldungen = list()

# Alarme anhängen
alarmmeldungen.append(meldung)

# Liste leeren
alarmmeldungen.clear()

# Eine bestimmte Meldung aus der Liste löschen
alarmmeldungen.remove(meldung)
# wenn meldung nicht in der Liste ist, wird ein ValueError ausgegeben.

Das gehört aber alles zu den Basics. Die Datentypen sollte man alle kennen.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Da ist vieles im Argen. Die Liste "anweisung" ist mehr oder minder eine globale Liste. Instanz-Variablen werden in __init__ angelegt, sonst teilen die sich alle Instanzen der Klasse, und man braucht gar keine verwenden. Die ganzen Klammern ala "(pin)==.." sind ueberfluessig. print (len(anweisung)) wird nur EINMAL ausgefuehrt, und hat daher auch immer die gleiche Ausgabe - 0.

Last but not least: das ist doch nicht der komplette Code, oder?
Lacsap93
User
Beiträge: 23
Registriert: Mittwoch 20. November 2019, 08:50

Hallo Dead_EyE
Danke für die Antwort.
Wie das funktioniert weiss ich auch. Meine Problembeschreibung ist weiter oben => Meine Nachricht von 15:03.
Oder verstehe ich dich falsch?

Hallo __deets__
auch danke für deine Antwort.
Ich weiss. Ich dachte mir nur zum Testen ginge es so einfacher. Ach so ja die Klammern sind klar das kommt vom kopieren. Die sind schon gelöscht.
Das mit dem print (len(anweisung))-Befehl habe ich auch gemerkt, daher habe ich weiter unten das selbe noch einmal gemacht, aber habe es oben noch drinn gelassen.
Nein wie weiter oben geschrieben ist der ganze Code sehr gross, daher habe ich nur die betreffende Klasse eingefügt. Wenn es gewünscht wird kann ich gerne den ganzen Code einfügen.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Wenn du Texte anpassen willst, dann geht das mittels String-Formatierung. Also zb

Code: Alles auswählen

name = "Petra"
"hallo {}".format(name)
Warum da da mit einer Liste arbeitest, und vor allem warum diese Liste dann auch noch eine Instanzvariable ist, erschliesst sich aber nicht, und ich wuerde das erstmal als falsch ansehen.
Sirius3
User
Beiträge: 18272
Registriert: Sonntag 21. Oktober 2012, 17:20

›Test‹ ist ein schlechter Klassenname. ›anweisung‹ ist eine globale Variable, die auch gar nicht gebraucht wird. Dazu später mehr. A0, A1 und A2 benutzen \". Da es aber vier verschiedene Arten von Anführungszeichen gibt, ist es eigentlich nie nötig, Anführungszeichen zu escapen.
In ›handle_on_up‹ wird ein einzelnes Element an eine Lsite gehängt, indem eine einelementige Liste erzeugt wird. Statt dessen benutzt man ›append‹. ›befehl‹ wird nicht gebraucht und ist sowieso immer `None`. Die drei if-Anweisungen schließen sich gegenseitig aus, also benutzt man elif.
`fuenf`, `neun` und `zehn` tun quasi das selbe, können also in einer Funktion zusammengefasst werden. Die Liste `anweisung` ist nur eine umständliche Art, einen Parameter zu übergeben, `k` ist nirgends definiert und auch ein schlechter Variablenname. `port` ist auch ein globaler Name.

Code: Alles auswählen

class Test(ReadHandler):        
    def __init__(self, serial_out, load_pin, clock_enable, clock_pin, warnings=False, bitcount=16):
        ReadHandler.__init__(self, serial_out, load_pin, clock_enable, clock_pin, warnings, bitcount)
    
    def handle_on_down(self, pin):
        print('Pin {} has been changed to DOWN'.format(pin))

    def handle_on_up(self, pin):
        print('Pin {} has been changed to UP'.format(pin))
        if pin == 5:
            self.send_command(0, "Störung Heizung")
        elif pin == 9:
            self.send_command(1, "Stoerung Grundwasserpumpe 1")
        elif pin == 10:
            self.send_command(2, "Stoerung Grundwasserpumpe 2")
            
    def send_command(self, number, text):
        port.write(f't{number}.txt="{text}"'.encode('utf8'))
        port.write(k*3)

with Test(26, 4, 6, 5) as handler:
    try:
        handler.watch_inputs() 
    except KeyboardInterrupt:
        handler.loop_breaker = True
        print('\nBroke Loop')
Benutzeravatar
DeaD_EyE
User
Beiträge: 1240
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Lacsap93 hat geschrieben: Montag 6. Januar 2020, 16:09 Hallo Dead_EyE
Danke für die Antwort.
Wie das funktioniert weiss ich auch. Meine Problembeschreibung ist weiter oben => Meine Nachricht von 15:03.
Oder verstehe ich dich falsch?
Ich bezog mich hier drauf: viewtopic.php?f=1&t=47007#p357829
Ich vermute mal, dass die Fehlermeldungen dauerhaft anliegen und erst wieder nach dem Quittieren der Fehler wieder verschwinden.
Ansonsten müsstest du von jedem Eingang die Flanke beobachten, was dann ein callback auslöst, welcher dann die Info aktualisiert.
So wie ich das aber verstanden habe, hast du ganz viele Eingänge und jeder davon ist ein einzelner Fehler für sich.


Die Alarm-Meldungen könnte man z.B. so verwalten:

Code: Alles auswählen

import time
import datetime


class Alarms:
    def __init__(self, alarm_mapping):
        self.active_alarms = {} # Pin : {"ts": Zeitstempel, "msg": "..."}
        self.alarm_mapping = alarm_mapping

    def set_alarms(self, alarms):
        """
        Alle pins, die auf High sind,
        müssen sich in der liste alarms befinden.
        """

        # Nicht mehr vorhandene Fehler entfernen
        for removed_alarm in self.active_alarms.keys() - alarms:
            del self.active_alarms[removed_alarm]

        # Die neuen Fehler hinzufügen
        for pin in alarms - self.active_alarms.keys():
            self.active_alarms[pin] = {
                "ts": datetime.datetime.utcnow(),
                "msg": self.alarm_mapping[pin],
            }
            
    @property
    def alarm_messages(self):
        return [alarm["msg"] for alarm in self.active_alarms.values()]


alarm_mapping = {0: "Fehler 0", 1: "Fehler 1", 2: "Fehler 2", 3: "Fehler 3"}
a = Alarms(alarm_mapping)

# jetzt liest du alle Eingänge ein, die einer Fehlermeldung zugeordnet und auch in alarm_mapping definiert sind.
# in der Liste müssen dann alle Eingänge als integer vorhanden sein, die High sind, also True.
print('Pin 0 und 3 sind High')
inputs = [0, 3]
a.set_alarms(inputs)
# setzt die Alarmmeldungen und fügt automatisch einen Zeitstempel hinzu (UTC0)
print(a.active_alarms)
print(a.alarm_messages)
print()
time.sleep(2)

# ein Fehler wurde quittiert, der andere liegt noch an:
print('Pin 3 ist High')
inputs = [3]
a.set_alarms(inputs)
print(a.active_alarms)
print(a.alarm_messages)
print()
time.sleep(2)

print('Pin 0, 1, 2 und 3 sind High')
inputs = [0, 1, 2, 3]
a.set_alarms(inputs)
print(a.active_alarms)
print(a.alarm_messages)

Das Einlesen der Eingänge habe ich in der Klasse absichtlich nicht gemacht.
Die keys der dicts sind ein set. Deswegen kann man auch das machen:

Code: Alles auswählen

my_dict = {0: 'Foo', 1: 'Bar', 3: 'FooBar'}
my_list = [0]
print(my_dict.keys() - my_list)
# {1, 3}
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Lacsap93
User
Beiträge: 23
Registriert: Mittwoch 20. November 2019, 08:50

Hallo Leute
Danke für eure Antworten!
Ich werde eure Lösungen nun austesten.
@Sirius3
Wegen den \ Zeichen: Ich muss das leider so machen, denn der Text muss so aussehen, damit das Nextion den Text auch erkennt und übernimmt.
Aber ich habe dein Programm teilweise übernehmen können. Ich dachte, dass die drei IF-Anweisungen einzeln für sich sind. ich wusste nicht, dass das dasselbe ist wie mit if und elif.
Den Rest habe ich so wie von dir vorgeschlagen übernommen.
@DeaD_EyE
Ach so da habe ich dich falsch verstanden. Es kann sein, dass sich die Fehler nur kurzzeitig anstehen und sich selber quittieren. Diese möchte ich aber trotzdem angezeigt haben. Ich werde mir dein Programm morgen noch genauer anschauen.

LG Lacsap
Benutzeravatar
__blackjack__
User
Beiträge: 14051
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Lacsap93: Woher soll denn das Nextion wissen wie Du das " in eine Zeichenkette bekommen hast? Du musst kein \ schreiben, darfst aber natürlich nicht nur das \ weglassen sondern musst auch andere Begrenzer für die Zeichenkette verwenden. Also eine der anderen drei Möglichkeiten als einfache doppelte Anführungszeichen.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Benutzeravatar
DeaD_EyE
User
Beiträge: 1240
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Lacsap93 hat geschrieben: Dienstag 7. Januar 2020, 16:45 Ach so da habe ich dich falsch verstanden. Es kann sein, dass sich die Fehler nur kurzzeitig anstehen und sich selber quittieren. Diese möchte ich aber trotzdem angezeigt haben. Ich werde mir dein Programm morgen noch genauer anschauen.
Ok, in dem Fall solltest du mit Callbacks arbeiten.
Je nach verwendeter Bibliothek für die GPIOs kann man einen Eingang eine Ereigniserkennung zuweisen.
D.h. Klasse Alarms müsste dann die Callbacks registrieren.
Mit der steigenden Flanke Fehler setzen und mit der fallenden Flanke um Fehler wieder löschen.

Code: Alles auswählen

import time
import datetime

import RPi.GPIO as gpio


class Alarms:
    def __init__(self, alarm_mapping):
        self.active_alarms = {}
        self.alarm_mapping = alarm_mapping

    def setup(self):
        for pin in self.alarm_mapping:
            gpio.setup(pin, gpio.IN, gpio.PUD_DOWN)
            gpio.add_event_detect(pin, gpio.BOTH, self.callback, bouncetime=200)

    def callback(self, pin):
        state = gpio.input(pin)
        if state:
            self.set_alarm(pin)
        elif pin in self.active_alarms:
            del self.active_alarms[pin]
        # Fehlermeldungen ausgeben
        self.log_alarm_messages()
        # Kann auch später eine andere Funktion abrufen, um irgendwas auszulösen

    def set_alarm(self, pin):
        self.active_alarms[pin] = {
            "ts": datetime.datetime.utcnow(),
            "msg": self.alarm_mapping[pin],
        }

    @property
    def alarm_messages(self):
        return [alarm["msg"] for alarm in self.active_alarms.values()]

    def log_alarm_messages(self):
        now = datetime.datetime.utcnow().isoformat()
        print(f'{now}: {self.alarm_messages}')


gpio.setmode(gpio.BCM)
alarm_mapping = {17: "Fehler 0", 27: "Fehler 1", 22: "Fehler 2", 22: "Fehler 3"}
a = Alarms(alarm_mapping)
a.setup()


# später im programm
# last_alarms = a.alarm_messages
# while True:
#    time.sleep(1)
#    current_alarms = a.alarm_messages
#    if last_alarms != current_alarms:
#        last_alarms = current_alarms
#        print(current_alarms)
Ich habe das mal mit den 3 Eingängen auf einem Raspberry Pi 4 getestet. Bei einer Bouncetime von 200ms, kann man die Eingänge auch mit einer Drahtbrücke testen.
(Auf den GND aufpassen und nur die 3.3V, PIN 1verwenden.)
Wählt man die Bouncetime zu kurz, kann fallende Flanke oder steigende Flanke mehrmals ausgelöst werden (prellen).
Wählt man die Bouncetime zu lang und ein Signal liegt kürzer an, als die Bouncetimne, kommt es zu keinem Ereignis.

Wenn du das Programm ausführst, beendet es sich gleich wieder.
Zum Testen kannst du das ja im interaktiven Modus starten:

Code: Alles auswählen

python3 -i programm.py
Das lädt dann das Programm und öffnet die REPL.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Er kann nicht mir callbacks arbeiten, weil er so viele Alarme hat, dass ein serielles schieberegister (bzw. mehrere) zum Einsatz kommen. Stand hier oder in einem anderen Thread zu Beginn des Projektes.
Lacsap93
User
Beiträge: 23
Registriert: Mittwoch 20. November 2019, 08:50

Hallo zusammen ich habe das Programm mit euren Tipps nun so hingekriegt, dass ich die Alarme einer nach dem Anderen in das Nextion schreibe, ich hoffe nun, dass das dann mit den insgesamt ca. 120 Alarmen auch noch reibungslos klappt, ohne dass ich zu grossen Zeitverlust habe. Aber das wird sich zeigen.
Ich danke euch und Wünsche ein schönes Wochenende :-)
Benutzeravatar
DeaD_EyE
User
Beiträge: 1240
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

__deets__ hat geschrieben: Donnerstag 9. Januar 2020, 12:15 Er kann nicht mir callbacks arbeiten, weil er so viele Alarme hat, dass ein serielles schieberegister (bzw. mehrere) zum Einsatz kommen. Stand hier oder in einem anderen Thread zu Beginn des Projektes.
Dann müsste ja die erste vorgeschlagene Lösung funktionieren, wenn man das Schieberegister richtig auswertet.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Antworten