Ein Feld aus einer Funktion übernehmen

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
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

@LotharK: Weil ich mich gerade mit FRP und DDD beschäftige ;): http://www.python-forum.de/pastebin.php?mode=view&s=425

Läuft nur unter Python 3.4 auf unixoiden Betriebssystemen (wg. SIGALRM). Du willst ja sowieso auf andere Signale reagieren, also einfach die einsetzen. Man kann es auch leicht nach Python 2.7 portieren, Enums zB. gibt es als Backport. Die DB ist ein Dummy und muss durch eine richtige ersetzt werden, ebenso alles, was ich zu Demo-Zwecken mittels random simuliert habe.

Wenn man es laufen lässt, kommt ungefähr sowas als Ausgabe:

Code: Alles auswählen

Door(1) access for [Staffer(5), Staffer(1), Staffer(2)]
Door(2) access for [Staffer(2), Staffer(5), Staffer(1), Staffer(3)]
Door(3) access for [Staffer(4), Staffer(2)]
NOT opening Door(3) for Staffer(3)
opening Door(2) for Staffer(2)
NOT opening Door(1) for Staffer(3)
Door(1) access for [Staffer(1)]
Door(2) access for [Staffer(5), Staffer(1), Staffer(4)]
Door(3) access for [Staffer(3), Staffer(5)]
NOT opening Door(2) for Staffer(2)
opening Door(1) for Staffer(1)
opening Door(2) for Staffer(4)
Door(1) access for [Staffer(2), Staffer(4)]
Door(2) access for [Staffer(2)]
Door(3) access for [Staffer(1), Staffer(5), Staffer(2), Staffer(4)]
NOT opening Door(3) for Staffer(3)
opening Door(2) for Staffer(2)
NOT opening Door(1) for Staffer(3)
Door(1) access for [Staffer(3), Staffer(4)]
Door(2) access for [Staffer(1)]
Door(3) access for [Staffer(1)]
opening Door(3) for Staffer(1)
NOT opening Door(2) for Staffer(5)
NOT opening Door(3) for Staffer(2)
...
Und ein *beep* für jedes Nicht-Öffnen einer Tür.
In specifications, Murphy's Law supersedes Ohm's.
LotharK
User
Beiträge: 51
Registriert: Sonntag 22. März 2015, 10:02

Hallo BlackJack,

@BlackJack
Ich habe hier in der Fa. mehrere Türen( insgesamt 22 mit Lager) welche alle mit Transponder geöffnet werden. Ist ein Transponder verloren, oder es kommt ein neuer Mitarbeiter, muss eine Fa. jedes Mal indie neuen Daten in jede Steuerung eintragen. Das ist sehr kostspielig.
Frage nicht, was ein vernetztes System kostet.

Ich ersetze diese Steuerungen durch Raspi.

Das System macht also nichts anderes, als die ganze Zeit zu lauern, ob jemand durch die Tür will. Kommt jemand, wird die Transponder-Nr eingelesen. Das System prüft, ob der betreffende TP im System ist. Wenn ja, wird geprüft, für welche Türen er Rechte hat.
Hat er, wird die Tür geöffnet. Das Ganze wird mitgelockt und steht im System zur Verfügung.
Die Vorteile liegen klar auf der Hand. Die Personalabteilung kann nun bequem Transponder und Rechte verwalten. Ebenso kann nachvollzogen werden, wann und wo jemand gekommen ist.
Das System funktioniert schon jetzt so, wie beschrieben. Nebeneffekt - ich gebe auf LCD den Benutzer aus und Zutritt erlaubt oder nicht. Das ganze mit einem Signalton und 2 Status-LEDs aufgemotzt, ist es besser als das System was jetzt gar nichts zeigt und nur durch leises Klicken signalisiert, ob die Tür offen ist.

Das Ganze wird am Notstrom hängen und zusätzlich mit 12V Bleigel gepuffert.
Wenn unser Netzwerk nicht läuft, sammelt es die Daten und gibt sie bei Erreichen wieder aus.

Soweit das Programm.
Ich hatte so ein System schon mit ATMega18 gemacht. Nur mit dem Raspi ist es einfach einfach. :)

Jetzt fülle ich das Feld Transponder_Liste (welches ich in meinem Test oben einfach fld nannte) einfach im Hauptprogramm mit einer IF-Anweisung. Wobei ein Flag geprüft wird, was ich mittels Interrupt setze.
Soweit so gut. Damit kann ich leben. Ich hätte es eben nur gern in einer Function erledigt. Wo es meiner Meinung nach auch hin gehört. Nun habe ich in dem ganzen Tread schon so viel gelesen, dass ich eigentlich alles falsch mache und dass ich keine Ahnung habe...
Ich schrieb ja auch anfangs, dass ich mich mit Python noch nicht gut auskenne.
Die Standard-Tipps, "lies erst mal ein Buch", befolgte ich auch. Nun setze ich das um und es ist auch falsch.

Leider weiß ich immer noch nicht, wie ich die Liste im Hauptprogramm prüfen, und ggf in der Function neu füllen kann.

MfG LotharK
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@LotharK: bei Deinem Problem böte sich ja ein minimalistisches Event-System an, das über eine Queue sowohl Türöffner-Interrupt als auch neue Daten an das Hauptprogramm meldet, das dann die entsprechenden Aktionen auslöst, hier mal als Template-Code:

Code: Alles auswählen

import numbers
import os
from Queue import Queue
from time import sleep

def read_rights_mapping(filename):
    pass #TODO

def get_transponder_id():
    pass #TODO

def process_door_open_request(rights, transponder):
    pass #TODO

def rights_mapping_watcher(queue):
    last_modification_time = None
    while True:
        try:
            if os.path.getmtime(RIGHTS_FILENAME) != last_modification_time:
                last_modification_time = os.path.getmtime(RIGHTS_FILENAME)
                queue.put(read_rights_mapping(RIGHTS_FILENAME))
        except Exception:
            # ignore network errors or related stuff
            logging.exception("error reading rights file")
        sleep(CHECK_INTERVAL)

def transponder_trigger(queue):
    queue.put(get_transponder_id())

def main():
    queue = Queue()
    register_trigger(transponder_trigger, args=(queue,))
    rights_watcher = threading.Thread(target=rights_mapping_watcher, args=(queue,))
    rights_watcher.deamon = True
    rights_watcher.start()
    rights = {}
    while True:
        event = queue.get()
        if isinstance(event, numbers.Integral):
            process_door_open_request(rights, event)
        else:
            rights = event

if __name__ == '__main__':
    main()
Die Funktionalität fehlt da natürlich noch, aber ich hoffe, aus den Namen wird deutlich was zu tun ist. Der Vorteil ist, dass man immer mit einer vollständigen Rechte-Tabelle arbeitet, auch wenn das Netzwerk vielleicht gerade ausgefallen ist, es gibt keine globalen Zustände und das Lesen über das Netzwerk blockiert die Tür nicht.
LotharK
User
Beiträge: 51
Registriert: Sonntag 22. März 2015, 10:02

Hallo sirius3

vielen Dank für Deine Hilfe. Natürlich habe ich den Code noch nicht vollständig verstanden. Er sieht aber vielversprechend aus. Jetzt muss ich mich erst mal an die Arbeit machen und analysieren, was die einzelnen Abschnitte tun. Z.B Import Queue und Import OS. Das finde ich aber allein raus.

Jetzt, wo ich Code habe, wird das Ganze für mich nachvollziebar.
Ich werde mich heute Abend, wenn ich Ruhe habe, damit beschäftigen.

Letztendlich ist das sowieso nur ein vorläufiges Programm. Am Ende wird das Programm auf einen MS-SQL Server 2008 R2 zugreifen. Soweit bin ich aber noch lange nicht. :?
Ich habe es jetzt aber schon mal geschafft, dass das Programm vom Netzwerk AD/Domain gestartet wird und Daten auf dem Server holt und hinterlegt.
Zwar meldet sich der Raspi an die Domäne an und hat nur Rechte auf 1 Verzeichnis und sonst nichts, trotzdem erscheint mir das Mapping mit hinterlegtem Passwort, IP und User im Klartext doch etwas unsicher. Ich habe zwar gelesen, dass man das über eine versteckte Datei handeln kann, scheint mir aber auch nicht sehr sicher. Wie das am Ende richtig geht, bekomme ich aber allein raus.

Ich möchte mich bei allen bedanken, die sich die Mühe gemacht haben, mir bei meinem Projekt weiter zu helfen.

Ciao LotharK
:lol:
PS - Nicht explodieren - Kann man nun eine Liste irgend wie als global definieren und in einer Funktion verändern? Würde mich nur mal so nebenbei noch interessieren!)
BlackJack

@LotharK: Das sind zwei Fragen. Man kann in Funktionen Listen auch verändern die als Argument übergeben werden, dazu muss die Liste nicht global sein. Nicht explodieren, aber beides sollte man eher nicht tun. :-P
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

LotharK hat geschrieben:Kann man nun eine Liste irgend wie als global definieren und in einer Funktion verändern?
Wenn man im Code die betreffende Liste referenziert, dann ist es völlig egal, ob sie global oder lokal ist. Die Antwort lautet also: Ja, aber tu es besser trotzdem nicht. ;)
BlackJack

@LotharK: Noch ein Grund warum man die Liste in der Funktion nicht verändern sollte: Wenn man die Liste verändert während jemand versucht sich anzumelden, wirds interessant. ;-)
LotharK
User
Beiträge: 51
Registriert: Sonntag 22. März 2015, 10:02

@snafu
Ja ist gut, ich bekomme sie aber trotzdem nicht von einem Modul ins andere - Siehe Soucecode ganz am Anfang.

@BlackJack

Überlege mal, was passiert, wenn ein Mitarbeiter sich anmeldet. Die Anmeldedaten stehen in der Liste. Die 'Liste wird durchlaufen - ( 0.1 Sek für 2000 Transponder). In der Zeit wurde die Liste verändert.
Die Datei wird im Interrupt kontrolliert, ob sie geändert wurde. Wurde sie geändert, wird sie neu eingelesen.

Die Liste wird in das Feld neu eingelesen und danach der Rest der Liste im Hauptprogramm weiter abgearbeitet.
Der Nutzer hat nun, entweder das Recht bekommen, oder entzogen bekommen. Oh, diese Entscheidung innerhalb einer 1/10 Sekunde ist schlimm!
Der andere Fall, es käme zu einem Laufzeitfehler (warum eigentlich?). Durch das Error-Konstrukt würde das Programm nicht abstürzen, sondern vorzeitig beenden. Der Mitarbeiter müsste den Transponder erneut ans Gerät halten. Wie oft kommt das vor?
Die Daten werden irgend wann mal geändert. Manchmal vergehen Monate. Sollte dieser Zufall wirklich auftreten - und was tut's????
Man sollte vielleicht nicht zu viel hineininterpretieren. Die ganze Sache ist viel zu simpel dafür.

:)
MfG LotharK
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

LotharK hat geschrieben:Ja ist gut, ich bekomme sie aber trotzdem nicht von einem Modul ins andere - Siehe Soucecode ganz am Anfang.
Wo ist da ein Modul? Ich sehe da nur Code auf Modulebene fürs Hauptprogramm und eine ominöse Funktion, die mit jedem Aufruf weitere Daten an ein global vorliegendes Objekt anhängt.

Hast du möglicherweise eine andere Vorstellung von Modul? Ich denke gerade eher an Python-Module - also einzeln vorliegende "*.py"-Dateien.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@LotharK: Wenn Du Dich irgendwo anmelden willst, mußt Du das Passwort, einen Hash des Passworts oder einen Private Key irgendwo hinterlegen, also irgendetwas was Dich als rechtmäßigen Zugreifer ausweist. Egal wie, wenn jemand die Zugangsdaten kopieren kann, hat er Zugriff. Deshalb ja der Vorschlag mit einem eigenen WebService, wo zu einer Transponder-ID nur abgefragt werden kann, ob der Zugang erlaubt wird oder nicht. Das Schadenspotential wird dadurch minimiert.

PS: je nach Design der Datei, der Art der Änderung wäre der schlimmste Anzunehmende Fall, dass die Zuordnung von Transponder-ID zu Rechten durcheinander gerät und jemand Zugang erhält, der das eigentlich nicht dürfte. Gegen diesen Fall mußt Du Deine Implementierung prüfen. Die Prüfung kann in bestimmten Fällen nicht trivial sein, also warum nicht gleich das ganze Problem umgehen, indem nie gleichzeitig auf gerade einlesende Daten zugegriffen werden kann.
BlackJack

@LotharK: Die Liste in das ”Feld” neu einlesen bedeutet doch aber das man erst den alten Inhalt löscht und dann mit den Daten vom Netzlaufwerk neu füllt. Das braucht Zeit. In dieser Zeit gibt es Leute die sich nicht anmelden können, weil ihre Daten noch nicht (wieder) in der Liste sind. Und wenn die Netzverbindung hängt oder langsam ist, wird's richtig lustig. Code der die globale, geteilte Liste aktualisiert müsste also deutlich komplexer sein um so etwas zu vermeiden, oder man sollte halt ganz einfach auf die selbe Liste nicht nebenläufig zugreifen und spart sich das somit. Und man kann dann auch eine saubere, vernünftige Funktion schreiben welche die Datei ausliest und eine *neue* Liste mit den Daten liefert. Beziehungsweise eine Datenstruktur die a) den Namen verdient und b) ein einfaches Testen auf das Recht erlaubt statt linear eine Liste mit Benutzerdaten durchlaufen zu müssen und dann vielleicht auch noch linear die Rechte.
LotharK
User
Beiträge: 51
Registriert: Sonntag 22. März 2015, 10:02

@BlackJack, glaub es bitte - die Liste einlesen passiert unter 1/10 sek.

Ich möchte das Thema auch nicht mehr weiter ausweiten. Das System steht hier und funktioniert. Auch ohne ständigen Netzzugriff. Zur Zeit zwar nur 3 Raspis, aber es macht genau das, was es soll.

Die ursprüngliche Frage war - wie ich auf eine Liste, welche in einer Function gefüllt wird, in einer anderen Routine lesen kann. Ich vermute mal, dass das nicht geht, da ja niemand eine Antwort hat.
Ich danke trotzdem noch mal allen und denke, das Thema ist erledigt. Zwar ohne Antwort auf die Frage, aber dafür habe ich ja ein brauchbares Script von Sirius3 erhalten

Ciao LotharK
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@LotharK: Genau, wenn Listen irgendwo geändert werden und das Auswirkungen auf eine andere Stelle im Code hat, wo diese Liste gelesen wird, dann ist es nicht einfach möglich, das fehlerfrei zu programmieren. Die Lösung ist ganz einfach, und wurde hier auch schon mehrfach genannt: man macht es anders.
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Weil Das Wort Event-System gefallen ist, hier noch eine Version, mit ebensolchem: http://www.python-forum.de/pastebin.php ... &mode=view

Dabei gibt es zu Simulationszwecken für jede Tür einen eigenen Thread.

Die Anforderung, dass auf lokale Daten zugegriffen werden soll, wenn und solange ein Raspi keine Netzverbindung hat, kann man über das Proxy/State-Pattern lösen. Dazu hat man einen DB-Proxy, der alle Anfragen an die Remote-DB delegiert, und sollte das scheitern, wechselt man zu einer lokalen DB. Letztere braucht bloß dasselbe Interface zu besitzen wie die Remote-DB, braucht aber keine wirkliche DB mit allem Drum und Dran zu sein, sondern kann einfach mit Dictionaries implementiert sein, wie in meinem Beispiel. Man braucht bloß regelmäßig zu schauen, wann die Netzverbindung wieder steht und kann dann zurückschalten und die lokal angesamelten Daten flushen. Der Rest des Programms kann davon unbehelligt weiterlaufen, so wie es sein sollte. Und immer noch braucht es keine globale Liste.
In specifications, Murphy's Law supersedes Ohm's.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

pillmuncher hat geschrieben:Weil Das Wort Event-System gefallen ist, hier noch eine Version, mit ebensolchem: http://www.python-forum.de/pastebin.php ... &mode=view
nice! :)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
BlackJack

@LotharK: Glaub es mir, Netzlaufwerke können auch mal hängen. Und selbst wenn es das nie tun wird, wäre das trotzdem eine fehlerhafte Lösung. Warum sollte man etwas mit einem bekannten Fehler programmieren wollen wenn das sowieso keine saubere Lösung wäre, es aber durchaus fehlerfreie Lösungen gibt, wenn man sich denn nicht auf diesen „unbedingt globale Datenstrukturen haben wollen”-Ansatz versteifen würde?
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

@BlackJack: manche wollen halt die einfachst-mögliche Lösung, egal, wie kompliziert sie ist.
In specifications, Murphy's Law supersedes Ohm's.
Benutzeravatar
darktrym
User
Beiträge: 784
Registriert: Freitag 24. April 2009, 09:26

Einfach-kompliziert klingt nach Max-Power-Art.
„gcc finds bugs in Linux, NetBSD finds bugs in gcc.“[Michael Dexter, Systems 2008]
Bitbucket, Github
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

pillmuncher hat geschrieben:manche wollen halt die einfachst-mögliche Lösung, egal, wie kompliziert sie ist.
Cooler Spruch. :mrgreen:
LotharK
User
Beiträge: 51
Registriert: Sonntag 22. März 2015, 10:02

@BlackJack

Langsam verstehe ich die Welt nicht mehr. Am Anfang hast Du mir diesen Code gepostet. Dieser Code macht haargenau das, was ich wollte. Jedenfalls im Grundgerüst.

Code: Alles auswählen


#!/usr/bin/env python
2.# coding: utf8
3.from __future__ import absolute_import, division, print_function
4. 
5.TRANSPONDER_FILENAME = '/mnt/netz/Python/Transponder.txt'
6. 
7. 
8.def load_transponders(filename):
9.    with open(filename, 'r') as lines:
10.        return [line.split(';') for line in lines if len(line) > 2]
11. 
12. 
13.def main():
14.    transponders = load_transponders(TRANSPONDER_FILENAME)
15.    for row in transponders:
16.        print(row[1])
17. 
18. 
19.if __name__ == '__main__':
20.    main()
Nun auf einmal ist alles falsch? Ich beobachte Foren, seit es sie gibt. Es ist egal welche. Aus einer simplen Frage werden dann Diskusionen, die die Welt nicht braucht. Simpelste Programmfunktionen werden in Klassen gepackt, etc.

Ich habe kürzlich im Microcontroller-Forum eine SMD-Leiterplatte vorgestellt, und gefragt, warum die Schaltung nicht funktioniert. Das Ergebnis: 3 Seiten Posting, was alles falsch wäre. Dass die Schaltung sowieso nicht funktionieren kann und das es die bei Ebay sowieso schon gibt.
Die Schaltung funktionierte übrigens vom ersten Tag an, bis heute perfekt. :D
Seit Mitte der 80er habe ich mit Computern zu tun. die Dinger werden immer schneller in der Prozessor-Leistung. Warum warte ich dann 5 Minuten auf einen Start? Und das bei einem Neugerät ohne schon etwas zu installieren?
Wenn ich sehe, wie aufgebläht viele Programme sind, kommt mir das K. Wieso passen manche Druckerinstallationen nicht mehr auf eine CD? Der Treiber ist höchstens 5MB. 5MB waren mal 5000000 Schreibmaschinenseite Soucecode.
:D
Tschüss
Antworten