Updates
This commit is contained in:
@@ -13,6 +13,7 @@ import re
|
|||||||
import socket
|
import socket
|
||||||
import subprocess
|
import subprocess
|
||||||
import threading
|
import threading
|
||||||
|
import time
|
||||||
|
|
||||||
from flask import Flask, jsonify, request
|
from flask import Flask, jsonify, request
|
||||||
|
|
||||||
@@ -25,6 +26,7 @@ KIOSK_SCRIPT = "/root/display_test_nexio.py"
|
|||||||
|
|
||||||
_video_proc: subprocess.Popen | None = None
|
_video_proc: subprocess.Popen | None = None
|
||||||
_video_lock = threading.Lock()
|
_video_lock = threading.Lock()
|
||||||
|
_kiosk_args: list[str] = ["python3", KIOSK_SCRIPT] # updated when kiosk is started
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Register commands to execute on each GET /registers request.
|
# Register commands to execute on each GET /registers request.
|
||||||
@@ -89,16 +91,38 @@ def _parse_memtool_output(raw: str) -> list:
|
|||||||
|
|
||||||
@app.route("/display", methods=["PUT"])
|
@app.route("/display", methods=["PUT"])
|
||||||
def control_display():
|
def control_display():
|
||||||
|
global _video_proc
|
||||||
data = request.get_json(force=True) or {}
|
data = request.get_json(force=True) or {}
|
||||||
state = data.get("state", "").lower()
|
state = data.get("state", "").lower()
|
||||||
if state == "on":
|
if state == "on":
|
||||||
with _video_lock:
|
with _video_lock:
|
||||||
|
if "--static-pink" in _kiosk_args:
|
||||||
|
# Full kill+restart so the DSI controller goes through LP-11 → HS
|
||||||
|
# startup, giving the scope a clean LP trigger on Pass 1.
|
||||||
if _video_proc is not None and _video_proc.poll() is None:
|
if _video_proc is not None and _video_proc.poll() is None:
|
||||||
|
_video_proc.terminate()
|
||||||
|
try:
|
||||||
|
_video_proc.wait(timeout=3)
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
_video_proc.kill()
|
||||||
|
_video_proc.wait()
|
||||||
|
time.sleep(0.15) # let DSI reach LP-11
|
||||||
|
try:
|
||||||
|
log = open("/tmp/kiosk.log", "w")
|
||||||
|
_video_proc = subprocess.Popen(
|
||||||
|
_kiosk_args, stdout=log,
|
||||||
|
stderr=subprocess.STDOUT, env=os.environ.copy(),
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({"error": f"restart failed: {e}"}), 500
|
||||||
|
return jsonify({"status": "static-pink restarted"}), 200
|
||||||
|
elif _video_proc is not None and _video_proc.poll() is None:
|
||||||
|
# Video mode: UDP trigger switches clip and reloads pipeline
|
||||||
_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
_sock.sendto(b'switch', ('127.0.0.1', 5001))
|
_sock.sendto(b'switch', ('127.0.0.1', 5001))
|
||||||
_sock.close()
|
_sock.close()
|
||||||
return jsonify({"status": "video switched"}), 200
|
return jsonify({"status": "video switched"}), 200
|
||||||
# fallback when video is not running
|
# fallback when no kiosk is running
|
||||||
os.system("echo 0 > /sys/class/graphics/fb0/blank")
|
os.system("echo 0 > /sys/class/graphics/fb0/blank")
|
||||||
return jsonify({"status": "Display ON"}), 200
|
return jsonify({"status": "Display ON"}), 200
|
||||||
elif state == "off":
|
elif state == "off":
|
||||||
@@ -216,6 +240,7 @@ def control_video():
|
|||||||
mode = data.get("mode", "")
|
mode = data.get("mode", "")
|
||||||
if mode == "static-pink":
|
if mode == "static-pink":
|
||||||
cmd.append("--static-pink")
|
cmd.append("--static-pink")
|
||||||
|
_kiosk_args[:] = cmd # persist so control_display knows the mode
|
||||||
log = open("/tmp/kiosk.log", "w")
|
log = open("/tmp/kiosk.log", "w")
|
||||||
_video_proc = subprocess.Popen(
|
_video_proc = subprocess.Popen(
|
||||||
cmd,
|
cmd,
|
||||||
|
|||||||
@@ -185,6 +185,10 @@ def play_static_color(r: int, g: int, b: int):
|
|||||||
|
|
||||||
Uses videotestsrc pattern=solid-color so every DSI line carries the same
|
Uses videotestsrc pattern=solid-color so every DSI line carries the same
|
||||||
repeating RGB triplet — any deviation in the proto_decoder output is a DSI fault.
|
repeating RGB triplet — any deviation in the proto_decoder output is a DSI fault.
|
||||||
|
|
||||||
|
Listens on UDP port 5001 for a trigger packet (same as play_kiosk), which
|
||||||
|
briefly cycles the pipeline through READY→PLAYING to generate the LP→HS
|
||||||
|
startup sequence that the scope captures on Pass 1.
|
||||||
"""
|
"""
|
||||||
Gst.init(None)
|
Gst.init(None)
|
||||||
|
|
||||||
@@ -214,6 +218,22 @@ def play_static_color(r: int, g: int, b: int):
|
|||||||
old, new, _ = msg.parse_state_changed()
|
old, new, _ = msg.parse_state_changed()
|
||||||
print(f"Pipeline: {old.value_nick} -> {new.value_nick}", flush=True)
|
print(f"Pipeline: {old.value_nick} -> {new.value_nick}", flush=True)
|
||||||
|
|
||||||
|
def _restart_pipeline():
|
||||||
|
"""Cycle READY→PLAYING to produce the LP→HS startup the scope triggers on."""
|
||||||
|
print("UDP trigger: restarting pipeline (READY → PLAYING)", flush=True)
|
||||||
|
pipeline.set_state(Gst.State.READY)
|
||||||
|
pipeline.set_state(Gst.State.PLAYING)
|
||||||
|
return False # GLib.idle_add one-shot
|
||||||
|
|
||||||
|
def _udp_listener():
|
||||||
|
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
sock.bind(('127.0.0.1', 5001))
|
||||||
|
while True:
|
||||||
|
sock.recv(64)
|
||||||
|
GLib.idle_add(_restart_pipeline)
|
||||||
|
|
||||||
|
threading.Thread(target=_udp_listener, daemon=True).start()
|
||||||
|
|
||||||
bus.connect("message", on_message)
|
bus.connect("message", on_message)
|
||||||
pipeline.set_state(Gst.State.PLAYING)
|
pipeline.set_state(Gst.State.PLAYING)
|
||||||
print(f"Static colour R:{r} G:{g} B:{b} (0x{argb:08X}) — running", flush=True)
|
print(f"Static colour R:{r} G:{g} B:{b} (0x{argb:08X}) — running", flush=True)
|
||||||
|
|||||||
Reference in New Issue
Block a user