Python Script über Webbrowser

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Juzu
User
Beiträge: 18
Registriert: Samstag 2. Juni 2018, 13:39

Sirius3 hat geschrieben: Samstag 2. Juni 2018, 15:11 Niemand muß etwas über PHP-Skripte machen.
Welche möglichkeit gibts denn ein Python Script mit einem Button zu starten?
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du kannst in deinem Python Skript einen Webserver starten, und den mit dem Browser ansteuern.

Beispiele gibt es da viele, zb mit bottle oder flask als webframework.
Juzu
User
Beiträge: 18
Registriert: Samstag 2. Juni 2018, 13:39

Gibt es auch eine andere Möglichketi? Ich habe nämlich noch ein paar andere Seiten auf dem Server auf denen man die Parameter eingibt usw. und ich würde das jetzt ungern alles nochmal abändern, es sei denn es gibt keine andere Methode.
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Prinzipiell muss das gehen über PHP. Warum das jetzt nicht klappt kann ich so auch nicht sagen - ich bin nicht auf dem laufenden, was zB der Apache für Limitierungen an seine Prozesse anlegt. Und da muss es ja irgendwie dran liegen. Ich würde da nochmal tiefer in die Logs starren.

Das ganz generell ist dein Ansatz, per System zu arbeiten ziemlicher Mist. Denn damit bist du IMMER darauf angewiesen, dass der Browser rechtzeitig was macht. Wenn der Benutzer den Browser schließt, und dein Skript gerade die Turbine auf Vollgas gedreht hat, dann bleibt das so lange so, bis es kracht.

Darum ist es eh besser, das Skript dauerhaft laufen zu lassen, und damit zu kommunizieren. ZB per socket. Oder eben sogar HTTP. Es läuft also endlos, und bekommt per HTTP Befehle, was es tun soll. Ob die direkt aus dem Browser kommen, oder zb via PHP und CURL ist egal.

Aber so kannst du dann zb Timeouts ablaufen lassen, und die Turbine wieder abschalten. Oder auch nur die Plumpsklobeleuchtung. Eigentlich kommt fast nichts ohne Timer aus.
Juzu
User
Beiträge: 18
Registriert: Samstag 2. Juni 2018, 13:39

Ist es wirklich sinnvoll bzw. notwendig städig zu kommunizieren? Der Webserver soll nur die Daten in eine Datei speichern (klappt) und dann das script starten wenn ich den Knopf drück. Dann soll nur noch eine Seite mit script läuft angezigt werden was ich mit einer php abfrage in der index mache, indem ich eine Datei lese in der entweder running oder nix steht. Dann hat sich alles gegessen. Der Raspberry pi steht für ein paar Stunden irgendwo rum und macht eine Timelapse mit dem script. Fertig. "Mehr" solls nicht werden.
Juzu
User
Beiträge: 18
Registriert: Samstag 2. Juni 2018, 13:39

Sprich wenn das Script einmal läuft dann läuft das mit einer for x in range schleife bis es fertig ist.
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du beantwortest doch gerade deine eigene Frage. Das Skript läuft in einer endlossschleife. Also dauerhaft. Scheint also nötig, oder? Von dauerhaft kommunizieren habe ich übrigens nicht geredet.

Das du ducrh das anlegen von Dateien mit dem Skript kommunizierst Bzw aus dem heraus ist auch nix anderes als eben Kommunikation. Sehr krude, aber trotzdem. Und wenn dein Skript dauerhaft läuft, aber dann durch Anlage einer Datei loslegt, dann hast du dein Problem doch schon gelöst. Statt es starten zu müssen, legst du eine Datei an und es geht los. Oder eben besser du schickst ein Kommando über einen socket.
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Nachtrag: for ist natürlich üblicherweise keine Endlosschleife. Aber wenn du eine drumrum packst, in der auf das Start kommando gewartet wird, dann ist das ja schon genug.
Juzu
User
Beiträge: 18
Registriert: Samstag 2. Juni 2018, 13:39

Ich blick da gerade nicht durch.
Als erstes sollen Daten via Website gespeichert werden
Dann via Button script starten
Script ließt die Daten einmal aus und hat danach nichts mehr mit dem Webserver am Hut. Es geht nur um das aktivieren des Scriptes. Das script ist selbständig und unabhängig.
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Und alles was ich sage ist: starte dein Skript nicht mit PHP. Sondern zb im autostart. Und dann WARTE im Skript, bis durch den Button im PHP Skript eine Datei angelegt wurde. Das geht ja schon. Dann tut das Skript, was es tut, und wenn es fertig ist, wartet es wieder auf das nächste Kommando.
Juzu
User
Beiträge: 18
Registriert: Samstag 2. Juni 2018, 13:39

Wie baue ich das denn am besten ein? Hier der Code meines Haupt-Scripts:

Code: Alles auswählen

import time, threading, os, RPi.GPIO as GPIO, signal, subprocess
from datetime import datetime
from time import sleep
from sh import gphoto2 as gp

f = open("/var/www/html/data/saves.txt", "r")
lines = f.readlines()
length = int(lines[0])
time_between = float(lines[1])
shutter_speed = float(lines[2])
images = int(lines[3])
direction = lines[4]
f.close()

triggerCMD = ["--trigger-capture"]
downloadCMD = ["--get-all-files"]
deleteCMD = ["--folder", "/store_00020001/DCIM/100CANON", "-R", "--delete-all-files"]
save_location = "/media/pi/SSD/" + datetime.now().strftime("%Y-%m-%d_") + datetime.now().strftime("%H:%M")
count = (int(length / float(images) * 800))

image_taken = False
ID = 0
delay = 0.0004
DIR = 20
STEP = 21

GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(STEP, GPIO.OUT)
GPIO.setup(DIR, GPIO.OUT)

def killgphoto2Process():
    p = subprocess.Popen(['ps', '-A'], stdout=subprocess.PIPE)
    out, err = p.communicate()

    for line in out.splitlines():
        if b'gvfsd-gphoto2' in line:
	    pid = int(line.split(None,1)[0])
	    os.kill(pid, signal.SIGKILL)

def createSaveFolder():
	try:
		os.makedirs(save_location)
	except:
		os.chdir(save_location)

def captureImages():
	global ID
	global image_taken
	for x in range(images):
		end_time = time.time() + shutter_speed + time_between
		gp(triggerCMD)
		sleep(shutter_speed)
		image_taken = True
		sleep(1)
		os.chdir(save_location)
		gp(downloadCMD)
		gp(deleteCMD)
		ID += 1
		os.chdir(save_location)
		for filename in os.listdir("."):
			if len(filename) > 10:
				if filename.endswith(".CR2"):
					os.rename(filename, (str(ID) + ".CR2"))
		while time.time() < end_time:
			pass

def saveImages():
	global image_taken
	while True:
		if image_taken == True:
			sleep(1)
			image_taken = False
			step_count = count
			if direction == "left":
				GPIO.output(DIR, GPIO.LOW)
			elif direction == "right":
				GPIO.output(DIR, GPIO.HIGH)
			for x in range(step_count):
				GPIO.output(STEP, GPIO.HIGH)
				sleep(delay)
				GPIO.output(STEP, GPIO.LOW)
				sleep(delay)
				step_count -= 1

killgphoto2Process()
createSaveFolder()

captureImages = threading.Thread(target = captureImages)
saveImages = threading.Thread(target = saveImages)

captureImages.start()
saveImages.start()
captureImages.join()
saveImages.join()
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Pro Zeile nur ein Modul importieren, sonst verliert man schnell die Übersicht. Im ›/var/www/html‹ sollten eigentlich nur von aussen zugängliche unveränderliche Daten liegen, weil sonst das Risiko, dass irgendwer Zugriff auf Dein System erlangen kann, zu groß ist. Statt Dir ein eigenes Konfigurationsformat zu überlegen, benutz ein fertiges, und das passende Modul zum Lesen dazu (JSON, YAML, ConfigParser).
Warum rufst Du zweimal datetime.now auf um save_location zu erzeugen? Das führt dazu, dass Du unter umständen 24h zu früh dran bist.

GPIO-Warnungen sind dazu da, dass man die Ursache behebt, nicht dass man sie ignoriert.
Was ist der Sinn, irgendwelche Prozesse zu killen? Entweder das Programm läuft aus einem guten Grund, oder man sollte auf anderem Weg dafür sorgen, dass es nicht läuft.

Immer mit 4 Leerzeichen pro Ebene einrücken. Bei Tabs führt das je nach Editor zu unlesbarem Code. Zur Schreibweise: Variablen und Funktionen werden klein_mit_unterstrich geschreiben, Konstanten KOMPLETT_GROSS. Abkürzungen vermeiden,

createSaveFolder ist eine seltsame Funktion, entweder wird ein Verzeichnis erstellt, oder man wechselt in dieses Verzeichnis? In jedem Fall ist der Zustand danach unklar. Niemals nackte excepts benutzen und niemals chdir, denn das ändert einen globalen Zustand, was an anderer Stelle vielleicht nicht erwartet wird.

Vergiß dass es ›global‹ überhaupt gibt. Bei Dir richtet es das größte Chaos an, weil Du lesend und schreibend auf Variablen in zwei Threads gleichzeitig zugreifst. Threadprogrammierung ist kompliziert, und so wie Du das machst führt das zu nicht reproduzierbaren Fehlern. Wenn Threads und wenn es nötig ist, Daten dazwischen auszutauschen, dann über die bewährten Mechanismen (hier wohl ein Event). Deine Busy-Loop ersetzt Du besser durch sleep.

Einer Deiner Threads läuft endlos, somit auch das Programm endlos.

Der Umweg über PHP-Skripte, die Konfigurationen schreiben und Programme starten und dann keine Kontrolle über das ganze haben, ist nicht nur komplizierter, sondern auch fehleranfälliger.
Wenn jemand mehrmals den Startknopf drückt, kommen sich die ganzen Photoskripte in die Quere.

Viel einfacher ist es, alles unter einer Haube zu haben. Durch einen kleines Flask-Server, der eine Seite zum Eingeben der Parameter hat und eine zum Starten und die Befehle in eine Queue steckt, um die Photos in dem schon vorhandenen Thread zu machen.
Juzu
User
Beiträge: 18
Registriert: Samstag 2. Juni 2018, 13:39

Ich habe jetzt mal einen Flask Server erstellt.
Ich habe eine Seite wo ich etwas eingebe und per Button bestätige.
Auf einer zweiten seite wird das eingegebene dargestellt.
Wie kann ich die Werte in Variablen speichern und wie kann ich mit den Werten rechnen und sie auf der Seite ausgeben?
Juzu
User
Beiträge: 18
Registriert: Samstag 2. Juni 2018, 13:39

Ok durch rumprobieren hat sich die Frage mit dem rechnen erledigt, die mit den Variablen aber nicht
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

So lange es sich um wenige Daten handelt, würde man sie im Client speichern, z.B. per Cookie dann in jedem Request lesen. Bei größeren Datenmengen, oder wenn man nicht will, dass der Client direkten Zugriff hat, nimmt man eine Datenbank und überträgt nur einen Session-Cookie.
Juzu
User
Beiträge: 18
Registriert: Samstag 2. Juni 2018, 13:39

Es soll so einfach wie möglich sein. Der Pi soll nicht so viel zu tun haben da er nur über eine Powerbank begrenzt Strom bekommen wird. Bei jeden Request auslesen geht ja schlecht oder? Die Seite wo die Daten eingegeben werden besteht ja nicht zur ganzen Zeit. Es sollen Daten eingegeben werden, diese bestätigt werden und auf der nächsten Seite wird die benötigte Zeit gezeigt und per Button soll das Script gestartet werden. Dann soll nur noch eine Seite mit der Info, Script aktiv zu sehen sein.
Wie löse ich das am besten? Datenbank ist meiner Meinung nach zu "umständlich" bzw zu aufwändig.
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Datenbank muss nicht aufwändig sein. Bei so einem Projekt kann man ja auch SQLite verwenden. Datenbank ist nun mal das übliche wenn man in einer Webanwendung persistent Daten speichern will. Klar kann man sich da selbst etwas anderes basteln, wenn man dann aber die üblichen Mechanismen wie Transaktionen selber programmieren will, *dann* wird es aufwändig. Warum das Rad neu erfinden.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Antworten