Anfängerfrage zur Struktur

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
Thomas_Kaki
User
Beiträge: 8
Registriert: Sonntag 7. Juni 2015, 20:50

Moin!

Die folgenden Aufgabe beziehen sich auf einen Minicomputer Raspberry Pi B2.

An dem Pi sind Temperatursensoren über das 1-wire-Protokoll angeschlossen. Derzeit habe ich 30 Sensoren dran, es werden noch ca. 20 mehr. Die Sensoren identifizieren sich dem Pi gegenüber mit ihrer Seriennummer.

Zur Auswertung kommt ein Programm (oder sagt man hier Script?) das ich im Anhang mal mitliefer. Die Ausgabe ist so:
Sensor ID | Temperatur
-----------------------------
28-000006375540 | 21.2 °C
28-000006377b60 | 21.1 °C
28-000006376a30 | 21.2 °C



Nun sagt die Seriennummer nicht viel aus, ich hätte gerne einen Klartextnamen. Ich könnte nun per If-Abfrage im # print temperature Block jeden Wert (Seriennummer) abfragen und in der Ausgabe durch einen Klarnamen (z.B. "Dachboden") ersetzten. Nur würde dann die Schleife nicht nur (max) 50 mal durchlaufen, sondern bei jedem Durchlauf würden noch mal 50 If-Abfragen dazukommen. Gibt es da eine bessere Lösung?

Thomas

Code: Alles auswählen

# Copyright (c) 2013-2015 by Georg Kainzbauer <http://www.gtkdb.de>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#

# import sys module
import sys
import time


# open 1-wire slaves list for reading
file = open('/sys/devices/w1_bus_master1/w1_master_slaves')

# read 1-wire slaves list
w1_slaves = file.readlines()

# close 1-wire slaves list
file.close()

# print header for results table
now = time.localtime()
print(time.strftime("%d.%m.%Y %H.%M"))

print("Sensor ID       | Temperatur")
print("-----------------------------")

# repeat following steps with each 1-wire slave
for line in w1_slaves:
  
  # extract 1-wire slave
  w1_slave = line.split("\n")[0]

  # open 1-wire slave file
  file = open('/sys/bus/w1/devices/' + str(w1_slave) + '/w1_slave')

  # read content from 1-wire slave file
  filecontent = file.read()

  # close 1-wire slave file
  file.close()

  # extract temperature string
  stringvalue = filecontent.split("\n")[1].split(" ")[9]

  # convert temperature value
  temperature = float(stringvalue[2:]) / 1000

  # print temperature
  fd=open('Messwerte.txt', "a")
  print(str(w1_slave) + " | %5.1f °C" % temperature)
  fd.write(time.strftime("%d.%m.%Y %H.%M") +"; "+ str(w1_slave) + " ; %5.1f °C" % temperature)
  fd.write('\n')
  fd.close

# quit python script
sys.exit(0)
AxXel001
User
Beiträge: 29
Registriert: Sonntag 7. Juni 2015, 22:22

Wenn die Seriennummern alle bekannt sind, kannst du dazu ein Dictionary nutzen. Das ist eine Datenstruktur, mit der du eine Zuordnung realisieren kannst. In deinem Fall möchtest du ja jeder Seriennummer eine Klartextbezeichnung zuordnen.

Genutzt wird ein Dictionary folgendermaßen:

Code: Alles auswählen

# Ordne jeder Telefonnummer einen Namen zu
nummern = {
    "0173123456" : "Peter Lustig",
    "0154654321" : "Max Mustermann"
}
# Wem gehört noch mal diese Nummer?
name = nummern["0154654321"]
print name, "hat die Nummer 0154654321"
# Max Mustermann hat die Nummer 0154654321
Für eine genauere Beschreibung einfach nach "python dictionary" googeln, da wirst du schnell fündig ;)

LG
Alex
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

@Thomas_Kaki: dafür hat man in Python Wörterbücher (dict) erfunden:

Code: Alles auswählen

SERIAL2NAME = {
    "28-000006377b60": "Badezimmer",
    "28-000006376a30": "Garage",
}
Zu Deinem Programm: außer Konstanten und Funktionen sollte nichts auf oberster Ebene stehen. Das Programm ist auch so lang, dass es sinnvoll wäre, es in mehrer Funktionen aufzuteilen. Eine davon heißt typischerweise main und wird über

Code: Alles auswählen

if __name__ == '__main__':
    main()
am Schluß der Datei aufgerufen.
"file" ist ein schlechter Variablenname, weil er die Klasse file überschreibt. Dateien sollten auch mit dem with-Statement geöffnet werden. Statt das low-level time-Modul zu verwenden, empfiehlt sich datetime. Statt dem seltsamen split in Zeile 34 gibt es strip, bzw. rstrip. Statt Pfade mit + zusammenzustückeln gibt es os.path.join. Das str ist unnötig. "Messwerte.txt" sollte nur einmal vor der Schleife geöffnet werden und nicht für jeden Sensor einmal. Hier auch mit with öffnen, dann wird die Datei auch wieder geschlossen und nicht nur die close-Methode referenziert und gleich wieder verworfen. Du kennst doch schon Stringformatierung, warum stückelst Du dann die Seriennummer noch mit + zusammen? Die .format-Methode bietet Dir zudem mit dem Datetime-Modul das Datum schön im Format-String zu formatieren. Zeile 59 ist überflüssig.
bords0
User
Beiträge: 234
Registriert: Mittwoch 4. Juli 2007, 20:40

AxXel001 hat geschrieben:"file" ist ein schlechter Variablenname, weil er die Klasse file überschreibt.
Der OP scheint Python 3 zu benutzen (print als function), und da gilt das nicht:

Code: Alles auswählen

>>> file
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
NameError: name 'file' is not defined
Thomas_Kaki
User
Beiträge: 8
Registriert: Sonntag 7. Juni 2015, 20:50

AxXel001 hat geschrieben:Wenn die Seriennummern alle bekannt sind, kannst du dazu ein Dictionary nutzen. Das ist eine Datenstruktur, mit der du eine Zuordnung realisieren kannst. In deinem Fall möchtest du ja jeder Seriennummer eine Klartextbezeichnung zuordnen.
...
Werde ich ausprobieren.

Thomas
Thomas_Kaki
User
Beiträge: 8
Registriert: Sonntag 7. Juni 2015, 20:50

Sirius3 hat geschrieben:@Thomas_Kaki: dafür hat man in Python Wörterbücher (dict) erfunden:

Code: Alles auswählen

SERIAL2NAME = {
    "28-000006377b60": "Badezimmer",
    "28-000006376a30": "Garage",
}
Zu Deinem Programm: außer Konstanten und Funktionen sollte nichts auf oberster Ebene stehen. Das Programm ist auch so lang, dass es sinnvoll wäre, es in mehrer Funktionen aufzuteilen. Eine davon heißt typischerweise main und wird über

Code: Alles auswählen

if __name__ == '__main__':
    main()
am Schluß der Datei aufgerufen.
Also, rund 60 Zeilen sind wirklich nicht lang. Vor ca. 30 Jahren habe ich eine Warenwirtschaft in knapp 3000 Zeilen GFA-Basic geschrieben. Das war lang... Trotzdem: Was für einen Sinn oder Vorteil hat es, dort mit main zu arbeiten?

Außerdem habe ich das Programm selbst nicht geschrieben, sondern nur in letztem Block die Dateiausgabe eingebunden.

Thomas
Benutzeravatar
bwbg
User
Beiträge: 407
Registriert: Mittwoch 23. Januar 2008, 13:35

Also, rund 60 Zeilen sind wirklich nicht lang.
60 Zeilen in einer Funktion - gut hier wird keine einzige Funktion verwendet - ist lang. Eine gute Übung wären ca. 5 Zeilen je Funktion. Hier könnte man zum Beispiel mit einer main-Funktion beginnen und dann sukzessive das Programm in Funktionen aufteilen.
"Du bist der Messias! Und ich muss es wissen, denn ich bin schon einigen gefolgt!"
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

@Thomas_Kaki: es macht immer Sinn, eine main-Funktion zu haben, weil sonst das Script nicht importierbar und damit nicht testbar ist. Ein eindeutiges Zeichen, dass der Code ohne Funktionen zu lang ist, ist, dass man versucht mit Kommentaren eine Struktur hinein zu bringen.
Thomas_Kaki
User
Beiträge: 8
Registriert: Sonntag 7. Juni 2015, 20:50

AxXel001 hat geschrieben:Wenn die Seriennummern alle bekannt sind, kannst du dazu ein Dictionary nutzen. Das ist eine Datenstruktur, mit der du eine Zuordnung realisieren kannst. In deinem Fall möchtest du ja jeder Seriennummer eine Klartextbezeichnung zuordnen.

Genutzt wird ein Dictionary folgendermaßen:

Code: Alles auswählen

# Ordne jeder Telefonnummer einen Namen zu
nummern = {
    "0173123456" : "Peter Lustig",
    "0154654321" : "Max Mustermann"
}
# Wem gehört noch mal diese Nummer?
name = nummern["0154654321"]
print name, "hat die Nummer 0154654321"
# Max Mustermann hat die Nummer 0154654321
Moin,

ich nochmal. Das mit der Liste habe ich jetzt auch hinbekommen (ohne Google, allein durch überlegen und probieren).

Nächste Frage - wie läßt sich die Liste in der Ausgabe logisch sortieren? Das ist sicher nicht trivial, denn ich möchte folgende Reihenfolge in der Ausgabe:

Dach oben
Dach mitte
WP Eingang
Speicher
WP Ausgang
<div. andere>

Die Liste wird aber standartmäßig nach Reihenfolge der Dateinamen abgearbeitet. Die Dateinamen ändern sich von Zeit zu Zeit, und auch in der Anzahl.
Die Ausgabe sieht so aus:

Code: Alles auswählen

Sensor ID       | Temperatur
-----------------------------
28-000006375540  Rueckl  05  19.5 °C
28-000006377b60  Ohne N. 02  19.4 °C
28-000006376a30  Vorlauf 05  19.5 °C
28-00000636ebf0  Vorlauf 04  19.6 °C
28-000006372544  Vorlauf 10  19.4 °C
28-0000063769e4  Rueckl  02  19.5 °C
28-00000636f8f4  Rueckl  10  19.5 °C
28-00000636dfec  Rueckl  06  19.4 °C
28-000006375e02  Rueckl  11  19.3 °C
28-000006370d4a  Ohne N. 04  19.4 °C
28-00000637151a  Vorl    08  19.5 °C
28-00000636a00e  Vorlauf 11  19.4 °C
28-000006377c2e  Vorlauf 05  19.5 °C
28-00000636c71e  Rueckl  12  19.4 °C
28-000006372441  WP Ausgang  22.5 °C
28-000006370cf1  SpeicherWW  36.8 °C
28-000006376e09  Rueckl  08  19.5 °C
28-00000636ff89  Vorlauf 01  19.5 °C
28-000006377a15  Dach oben   24.1 °C
28-000006370b75  Rueckl  09  19.6 °C
28-00000636c5ed  Rueckl  01  19.4 °C
28-000006370b1d  Vorlauf 07  19.5 °C
28-0000064ee0dd  Vorlauf 09  19.5 °C
28-00000637187d  Vorlauf 12  19.4 °C
28-0000063770fd  Ohne N.  5  22.6 °C
28-0000063769fd  Ohne Name   19.7 °C
28-000006377fab  Rueckl  07  19.5 °C
28-000006377267  Vorlauf 02  19.5 °C
28-000006375e17  Rueckl  03  19.5 °C
28-000006374ccf  Raumtemp    22.3 °C
Vorschläge?

Thomas
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

@Thomas_Kaki: statt eines Wörterbuchs hast Du halt eine Liste NAMES_AND_SERIALS, in der Du die Namen und Seriennummern in der Reihenfolge ablegst, die Du gerne hättest;

Code: Alles auswählen

NAMES_AND_SERIALS = [
    ('Dach oben', '28-00000636dfec'),
    ('Dach mitte', '28-000006376a30'),
   ...
]
und statt die Daten sofort rauszuschreiben, sammelst Du sie in einem Wörterbuch serialnumber -> temperatur.
Zur Ausgabe gehst Du dann die Liste NAMES_AND_SERIALS der Reihe nach durch und suchst zu jeder Seriennummer die passende Temperatur.
Thomas_Kaki
User
Beiträge: 8
Registriert: Sonntag 7. Juni 2015, 20:50

Sirius3 hat geschrieben:@Thomas_Kaki: statt eines Wörterbuchs hast Du halt eine Liste NAMES_AND_SERIALS, in der Du die Namen und Seriennummern in der Reihenfolge ablegst, die Du gerne hättest;

Code: Alles auswählen

NAMES_AND_SERIALS = [
    ('Dach oben', '28-00000636dfec'),
    ('Dach mitte', '28-000006376a30'),
   ...
]
und statt die Daten sofort rauszuschreiben, sammelst Du sie in einem Wörterbuch serialnumber -> temperatur.
Zur Ausgabe gehst Du dann die Liste NAMES_AND_SERIALS der Reihe nach durch und suchst zu jeder Seriennummer die passende Temperatur.
Das Wörterbuch habe ich doch in Benutzung, rein die Sortierung der Liste weiss ich noch nicht zu bewerkstelligen.

Thomas

Code: Alles auswählen

Bezeichner = {
        "28-00000636a00e" : "Vorlauf 11",
        "28-00000636ff89" : "Vorlauf 01",
        "28-00000637187d" : "Vorlauf 12",
        "28-000006375e17" : "Rueckl  03",
        "28-000006377267" : "Vorlauf 02",
        "28-00000636c5ed" : "Rueckl  01",
        "28-000006370b1d" : "Vorlauf 07",
        "28-000006372441" : "WP Ausgang",
        "28-0000063769e4" : "Rueckl  02",
        "28-000006377a15" : "Dach oben ",
        "28-00000636c71e" : "Rueckl  12",
        "28-000006370b75" : "Rueckl  09",
        "28-000006372544" : "Vorlauf 10",
usw.
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

@Thomas_Kaki: ein Wörterbuch hat keine Sortierung. Du willst ja die Sortierung umstellen, von "nach Dateiname sortiert" zu "nach Ort sortiert". Das bedingt halt eine interne Reorganisation.
Thomas_Kaki
User
Beiträge: 8
Registriert: Sonntag 7. Juni 2015, 20:50

Sirius3 hat geschrieben:@Thomas_Kaki: ein Wörterbuch hat keine Sortierung. Du willst ja die Sortierung umstellen, von "nach Dateiname sortiert" zu "nach Ort sortiert". Das bedingt halt eine interne Reorganisation.
Ich bin mir jetzt nicht sicher, ob wir uns verstehen. Also ich versuche es mal zu erklären:

Die Sensoren werden im Dateisystem als Dateinamen abgebildet. Diese Dateinamen sind "hardverdrahtete" Seriennummern. Die Schleife liest den Ordner ein und findet Dateinamen. Diese werden als Liste (for-Schleife) abgearbeitet und die Werte ausgegeben - in der Reihenfolge, wie sie im Dateisystem vorgefunden werden.

Ich hätte aber gerne das die AUSGABEREIHENFOLGE nach meinen Gegebenheiten geschieht, also erst Dachboden, dann Wärmepumpe, dann Raumtemperaturen, dann Heizungskreisläufe.

Wenn ich die Reihenfolge ändern möchte, dann müßte ich (nach meinem Wissensstand) erst alle Messwerte (Dateinamen) einlesen (Array in BASIC), dann sortieren, dann ausgeben. Ob das besser geht weiss ich nicht. Wie das in Python geht auch nicht - in Basic oder Pascal würde ich das alleine hinbekommen.

Problem ist halt dabei, das die Menge der Sensoren variabel ist, je nachdem was gerade am Kabel hängt.

Thomas
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

@Thomas_Kaki: nichts anderes sag ich doch. Erst alle Dateien einlesen und die Werte in einem Wörterbuch seriennummer -> temperatur abspeichern. Danach alles in der gewünschten Reihenfolge ausgeben.
Thomas_Kaki
User
Beiträge: 8
Registriert: Sonntag 7. Juni 2015, 20:50

Sirius3 hat geschrieben:@Thomas_Kaki: nichts anderes sag ich doch. Erst alle Dateien einlesen und die Werte in einem Wörterbuch seriennummer -> temperatur abspeichern. Danach alles in der gewünschten Reihenfolge ausgeben.
Hmmm ...

Muß ich das Array "Bezeichner" vorher groß genug definieren?

Vermutung:
Bezeichner = {
"28-00000636a00e" : "Vorlauf 11", " ",
<weitere Elemente>}

Wie komme ich an ein drittes Element in "Bezeichner" ran?
In Basic würde ich sowas wie

Print Bezeichner[0,2]

schreiben.

Thomas
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

@Thomas_Kaki: in Python muß man gar keine Größen definieren. Wenn Du für einen Key mehrere Values hast, nimm ein Tuple.

"Bezeichner" hält sich nicht an die PEP8-Namenskonvention und ist auch ein völlig nichtssagender Name. Was soll das sein? Und was sind die zwei Werte, die Du Deiner Seriennummer zuordnen willst?
Thomas_Kaki
User
Beiträge: 8
Registriert: Sonntag 7. Juni 2015, 20:50

Sirius3 hat geschrieben:@Thomas_Kaki: in Python muß man gar keine Größen definieren. Wenn Du für einen Key mehrere Values hast, nimm ein Tuple.

"Bezeichner" hält sich nicht an die PEP8-Namenskonvention und ist auch ein völlig nichtssagender Name. Was soll das sein? Und was sind die zwei Werte, die Du Deiner Seriennummer zuordnen willst?
Tuple kenne ich noch nicht. "Bezeichner" ist mein selbst bestimmer Name für das Array. (Jedenfalls heisst das so in Basic).

Bezeichnber ist eben das Array, wo an erste Stelle die Seriennummer steht (ASCII-String), an zweiter Stelle der Ort des Messsensors (ASCII-String) und an dritter Stelle soll eine Real (Fliesskomma)-Zahl (einfache Genauigkeit) stehen, die den Wert in Grad Celsius angibt.

Thomas
BlackJack

@Thomas_Kaki: Es ist schon klar was `Bezeichner` *technisch* ist, die Frage ist warum das Wort "Bezeichner" gewählt wurde und nicht etwas was dem Leser besser vermittelt was der Wert der sich dahinter verbirgt *bedeutet*.

Das ist übrigens kein Array sondern ein Wörterbuch (Typ `dict`). Selbst wenn es eine Liste gewesen wäre, würde man das in Python nicht Array nennen, sondern eben Liste, denn wenn man in Python einen Array-Typ hat, dann ist das (sehr selten) der aus dem `array`-Modul in der Standardbibliothek, oder (in den meisten Fällen) ein Array-Typ aus dem `numpy`-Modul oder im Zusammenhang mit einem Modul das auf `numpy` aufbaut.
Benutzeravatar
Kebap
User
Beiträge: 686
Registriert: Dienstag 15. November 2011, 14:20
Wohnort: Dortmund

Thomas_Kaki hat geschrieben: Tuple kenne ich noch nicht.
Tupel, Liste und Wörterbuch sind grundlegende Python Datenstrukturen, die man sich schon mal anschauen sollte. Sie helfen bei vielen Dingen. Listen sind so ähnlich wie deine Arrays aus Basic, aber viel flexibler. Sicherlich stehen auch in deinem Tutorial einige Beispiele und Übungen, die beim Verständnis helfen können. :mrgreen:
MorgenGrauen: 1 Welt, 8 Rassen, 13 Gilden, >250 Abenteuer, >5000 Waffen & Rüstungen,
>7000 NPC, >16000 Räume, >200 freiwillige Programmierer, nur Text, viel Spaß, seit 1992.
Antworten