Code verhält sich anders bei Autostart mit rc.local

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
Ls342
User
Beiträge: 5
Registriert: Dienstag 28. März 2017, 05:53

Hallo,
wenn ich den Code test.py in der Konsole mit dem Befehl "python app/test.py" starte läuft der Code einwandfrei. Wenn jedoch die Datei test.py über rc.local automatisch gestartet wird, springt das Display in den Startbildschirm und mehr nicht.

Datei rc.local Inhalt:

Code: Alles auswählen

python root/app/test.py &
exit 0
Das Starten Funktioniert, da das Display den Startbildschirm anzeigt.
Nur bekomme ich danach keine weiteren Daten ans Display und das Programm reagiert nicht auf Daten vom Display.
Somit vermute ich, dass zwischen

Code: Alles auswählen

u.writeStr("page Home_Screen")
und der Whileschleife ein Problem gibt.
Gleicher Fehler zeigt sich, wenn ich über init.d das Programm automatisch starten lasse.

Datei test.py Inhalt:

Code: Alles auswählen

import mraa
import os
import sys
import subprocess
import signal
import time
from subprocess import Popen, PIPE, STDOUT


print "Programm start"

# Uart konfigurieren
u=mraa.Uart(0)
u.setBaudRate(9600)
u.setMode(8, mraa.UART_PARITY_NONE, 1)
u.setFlowcontrol(False, False)


# Deklaration Varriablen
mode=0
i=0
j=1
vol=50
playPause=0
fav_adress = []

#Relais konfigurieren
relais=mraa.Gpio(46)
relais.dir(mraa.DIR_OUT)
relais.write(0)

senden=bytearray(3)
senden[0]=255
senden[1]=255
senden[2]=255
u.write(senden)
u.writeStr("page Home_Screen")      #Funktioniert mit Autostart noch(Display springt in Startbildschirm)
u.write(senden)
print "Bereitschaft gesendet!"

#Radioadressen aus txt datei einlesen
radio_adress = open("radio_adress.txt")
fav_adress = []
for line in radio_adress:
	fav_adress.append(line.rstrip())
	print line.rstrip()
radio_adress.close()

while(True):
	if u.dataAvailable():
	
		data_byte= u.readStr(1)
		print ord(data_byte)
		if((ord(data_byte)==10) and (mode==0)):
			print "Output Select Internetradio"
			mode=10
			u.write(senden)
			u.writeStr("fav1.txt=\"" + fav_adress[0] + "\"")		#Funktioniert nicht mehr (Daten werden nicht ans Display gesendet)
			u.write(senden)
			u.writeStr("fav2.txt=\"" + fav_adress[1] + "\"")
			u.write(senden)
			u.writeStr("fav3.txt=\"" + fav_adress[2] + "\"")
			u.write(senden)
			u.writeStr("vol.txt=\"Volume: "+ str(vol) +"\"")
			u.write(senden)
			
		...(weitere elif abfragen)...
		
time.sleep(0.1)
Zuletzt geändert von Anonymous am Freitag 30. Juni 2017, 15:27, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
BlackJack

@Ls342: Ich vermute mal ganz stark das lesen der Datei wird nicht funktionieren weil das aktuelle Arbeitsverzeichnis ein anderes ist. Gib da mal den absoluten Pfad beim öffnen an.
Ls342
User
Beiträge: 5
Registriert: Dienstag 28. März 2017, 05:53

Vielen Dank, dass scheint das Problem gewesen zu sein. Nun funktioniert alles bestens.
BlackJack

@Ls342: Noch ein paar Anmerkungen zum Quelltext:

Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst. So wie es aussieht scheint das ganze Programm aus *einem* grossen Brocken Code zu bestehen, den man besser auf Funktionen oder Methoden verteilen sollte.

Namen sollten nicht abgekürzt werden. Einbuchstabige Namen sind nur in wenigen Ausnahmefällen sinnvoll, beispielsweise bei ganzzahligen Laufvariablen in Schleifen die aus der Mathematik üblichen `i`, `j`, `k`, oder `x`, `y`, `z` für Koordinaten. Ebenfalls nicht allzu schädlich sind einbuchstabige Namen die auf einen einzigen Ausdruck begrenzt sind („list comprehension“, Generatorausdruck, ``lambda``-Ausdruck). Aber `u` für eine serielle Verbindung oder `i` und `j` wo man bei der Definition nicht sieht was die eigentlich bedeuten sollen, gehen nicht.

`uart` wäre für `u` ein besserer Namen, aber das verrät IMHO nicht genug, denn das steht ja nicht für eine beliebige UART-Verbindung, sondern für eine zu einem Display.

Namen für alles ausser Konstanten und Klassen werden in Python konventionell `klein_mit_unterstrichen` geschrieben. Also `play_pause` statt `playPause`. Siehe auch den Style Guide for Python Code.

`senden` wäre ein passender Name für eine Funktion oder Methode, weil die etwas tun und `senden` eine Tätigkeit beschreibt. Ein `bytearray` würde niemand hinter dem Namen vermuten. `display_send_buffer` wäre vielleicht ein passender Name. Allerdings frage ich mich gerade ob sich die Werte in dem Array während des Programmablaufs jemals ändern‽ Falls nicht, braucht man kein veränderliches `bytearray`, sondern hätte eine Konstante die man als `bytes()`-Objekt anlegen könnte. Und dann wäre der Name eher so etwas wie `FRAME_MARKER` oder etwas in der Art, wenn ich mir die Verwendung anschaue.  Und es würde sich dann vielleicht auch lohnen die Verbindung zum Display in einer Klasse zu kapseln, damit man das Senden der dieser drei Bytes nicht unzählige male über den gesamten Quelltext verstreut zu stehen hat.

`fav_adress` wird unnötigerweise zweimal an eine leere Liste gebunden. Der Namen enthält eine Abkürzung und spiegelt auch nicht wieder das ein Container mit mehr als einer Adresse an den Namen gebunden wird. Namen für Listen haben üblicherweise die Mehrzahl des Bezeichners der für ein Element Sinn machen würde. Also beispielsweise `favourite_addresses` in diesem Fall.

Wenn man Dateien mit der ``with``-Anweisung zusammen öffnet, dann ist sichergestellt, dass sie am Ende auch wieder geschlossen werden, egal aus welchem Grund der ``with``-Block verlassen wird.

Der Name der Datei wäre ein sinnvoller Kandidat für eine Konstante, damit man den einfach am Programmanfang ändern/anpassen kann.

Um Bedingungen bei ``if`` und ``while`` gehören keine Klammern und man sollte das auch nicht so schreiben das man ``if`` und ``while`` mit Funktionsaufrufen verwechseln kann. Bei dem ``if`` sind auch in der Bedingung noch überflüssige Klammern.

Das gelesene Byte könnte man gleich nach dem Lesen schon in eine Zahl umwandeln, dann muss man das später nicht immer wieder aufs Neue machen.

Dieser Zahlwert wird im Programm dann mit ”magischen Zahlen” verglichen und `mode` ist auch ein Name der an solche ”magischen Zahlen” gebunden wird. So wie es jetzt dort steht, weiss man als Leser nicht was 0 oder 10 eigentlich bedeutet. Dafür führt man üblicherweise Konstanten ein, entweder unzusammenhängende, oder mittels `enum`-Modul. Das ist zwar erst mit Python 3 gekommen, aber es gibt für Python 2 einen „backport“ im Python Package Index. So etwas wie ``mode = RADIO_MODE`` oder ``mode = Mode.radio`` ist doch viel verständlicher als ``mode = 10``. (Sofern ich jetzt die Bedeutung der 10 denn richtig geraten habe, aber genau das ist ja das Problem mit ”magischen Zahlen”.)

Man kann literale Zeichenketten in ' oder " einfassen und das ist grösstenteils Geschmackssache welche man nimmt, aber wenn die Zeichenkette selbst dann eines dieser beiden Zeichen enthält, ist es sehr sinnvoll zum einfassen das *andere* zu nehmen, weil man dann nichts mit \ „escapen“ muss. Also "spam=\"Hallo\";" vs. 'spam="Hallo";', oder auch "O'Reilly" vs. 'O\'Reilly'.

Das Zusammensetzen von literalen Zeichenketten und Werten mittels ``+`` und `str()` ist eher BASIC als Python. In Python gibt es dafür Zeichenkettenformatierung.

Die ersten drei Sendevorgänge würden sich auch prima in einer Schleife machen, weil die fast identisch sind.

Kommentare sollten dem Leser einen Mehrwert über den Code geben. Wenn im Kommentar nur das steht was man als Programmierer im Quelltext oder in der Dokumentation nachlesen kann, dann braucht man den Kommentar nicht. Faustregel: Nicht kommentieren *was* der Code macht, denn das steht da schon als Code, sondern *warum* er das so macht. Sofern das überhaupt erklärungsbedürftig ist. Under dem Kriterium sind die ganzen Kommentare im Code überflüssig. ``# Deklaration Variablen`` ist zu dem falsch, weil das keine Deklaration ist sondern ”nur” eine Definition. Deklarationen gibt es in Python auch, wenn auch nur sehr wenige, darum sollte man diese beiden Begriffe nicht durcheinander bringen.

Aus den `print()`-Ausgaben würde ich `logging`-Ausgaben machen, insbesondere da das ja sowieso als Dienst im Hintergrund laufen zu scheint.

Ich lande dann ungefähr hier als Zwischenschritt (ungetestet):

Code: Alles auswählen

#!/usr/bin/python
# coding: utf8
from __future__ import absolute_import, division, print_function
import time
import mraa

RADIO_ADDRESSES_FILENAME = '/root/app/radio_adress.txt'

# TODO Oder als `enum.Enum` definieren.
I_HAVE_NO_IDEA_MODE = 0  # FIXME Namenswahl.
RADIO_MODE = 10


def main():
    # 
    # TODO Sinnvoll auf Funktionen oder Methoden verteilen.
    # 
    print('Programm start')
     
    display = mraa.Uart(0)
    display.setBaudRate(9600)
    display.setMode(8, mraa.UART_PARITY_NONE, 1)
    display.setFlowcontrol(False, False)
    # 
    # TODO Falls das hier eine Konstante ist, dann wirklich eine Konstante
    #      daraus machen und die Verbindung zum Display in einer Klasse kapseln
    #      um die ganzen Sendeoperationen dieser drei Bytes nicht überall im
    #      Quelltext stehen haben zu müssen.
    # 
    display_send_buffer = bytearray(b'\xff' * 3)
     
    relais = mraa.Gpio(46)
    relais.dir(mraa.DIR_OUT)
    relais.write(0)

    mode = I_HAVE_NO_IDEA_MODE
    i = 0  # FIXME Namenswahl.
    j = 1  # FIXME Namenswahl.
    volume = 50
    play_pause = 0
     
    display.write(display_send_buffer)
    display.writeStr('page Home_Screen')
    display.write(display_send_buffer)
    print('Bereitschaft gesendet!')
     
    favourite_addresses = []
    with open(RADIO_ADDRESSES_FILENAME) as lines:
        for line in lines:
            line = line.rstrip()
            favourite_addresses.append(line)
            print(line)
     
    while True:
        if display.dataAvailable():
            byte_value = ord(display.readStr(1))
            print(byte_value)
            if byte_value == 10 and mode == I_HAVE_NO_IDEA_MODE:
                print('Output Select Internetradio')
                mode = RADIO_MODE
                display.write(display_send_buffer)
                # 
                # FIXME Hier ist ein Fehler der aber weiter oben behoben werden
                #       sollte, oder durch auslagern in eine Funktion oder
                #       Methode.
                # 
                for i, address in enumerate(favourite_addresses, 1):
                    display.writeStr('fav{0}.txt="{1}"'.format(i, address))
                    display.write(display_send_buffer)
                display.writeStr('vol.txt="Volume: {}"'.format(volume))
                display.write(display_send_buffer)
               
            # ...(weitere elif abfragen)...
           
        time.sleep(0.1)


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