Files
RFP_Infinity-Vis/Infinity_Vis_1/infinity_vis_1/live.py

95 lines
4.4 KiB
Python

from __future__ import annotations
import argparse
from pathlib import Path
import time
from .engine import OutputSettings, RealtimeEngine
from .mapping_xml import load_mapping
from .models import PatternParams, SceneState
def main() -> None:
parser = argparse.ArgumentParser(description="Run the Infinity_Vis_1 live DDP sender.")
parser.add_argument(
"--mapping",
type=Path,
default=Path(__file__).resolve().parents[2] / "sample_data" / "infinity_mirror_mapping_clean.xml",
help="Path to the XML mapping file.",
)
parser.add_argument("--pattern", type=str, default="solid", help="Pattern id.")
parser.add_argument("--tempo", type=float, default=120.0, help="Tempo in BPM.")
parser.add_argument("--fps", type=float, default=40.0, help="Target output FPS.")
parser.add_argument("--preview-fps", type=float, default=0.0, help="Preview cadence for snapshot generation.")
parser.add_argument("--duration", type=float, default=0.0, help="Optional runtime limit in seconds. 0 means until Ctrl+C.")
parser.add_argument("--primary-color", type=str, default="#4D7CFF", help="Primary color in #RRGGBB.")
parser.add_argument("--secondary-color", type=str, default="#0E1630", help="Secondary color in #RRGGBB.")
parser.add_argument("--palette", type=str, default="Laser Club", help="Palette name.")
parser.add_argument("--brightness", type=float, default=1.0, help="Global pattern brightness 0..1.")
parser.add_argument("--tempo-multiplier", type=float, default=1.0, help="Pattern tempo multiplier.")
parser.add_argument("--color-mode", type=str, default="dual", help="Color mode, for example dual or palette.")
parser.add_argument("--strobe-mode", type=str, default="global", help="Strobe mode.")
parser.add_argument("--keepalive", type=float, default=0.35, help="Keepalive interval for unchanged payloads.")
parser.add_argument("--no-delta", action="store_true", help="Always send every controller every frame.")
args = parser.parse_args()
mapping = load_mapping(args.mapping)
scene = SceneState(
pattern_id=args.pattern,
tempo_bpm=args.tempo,
params=PatternParams(
brightness=max(0.0, min(1.0, args.brightness)),
tempo_multiplier=args.tempo_multiplier,
color_mode=args.color_mode,
primary_color=args.primary_color,
secondary_color=args.secondary_color,
palette=args.palette,
strobe_mode=args.strobe_mode,
),
)
settings = OutputSettings(
output_fps=max(1.0, args.fps),
preview_fps=max(0.0, args.preview_fps),
keepalive_seconds=max(0.0, args.keepalive),
delta_sending=not args.no_delta,
preview_enabled=args.preview_fps > 0.0,
)
engine = RealtimeEngine(mapping, scene=scene, settings=settings)
deadline = time.perf_counter() + args.duration if args.duration > 0 else None
last_report = time.perf_counter()
print(f"Infinity_Vis_1 live sender started")
print(f"Mapping: {mapping.name}")
print(f"Pattern: {scene.pattern_id} | Tempo: {scene.tempo_bpm:.1f} BPM | Output FPS: {settings.output_fps:.1f}")
print(f"Controllers: {engine.diagnostics.controller_count} | Tiles: {engine.diagnostics.tile_count} | LEDs: {engine.diagnostics.total_leds}")
print("Press Ctrl+C to stop.")
try:
while True:
now = time.perf_counter()
if deadline is not None and now >= deadline:
break
engine.send_due(now=now)
if now - last_report >= 1.0:
diagnostics = engine.diagnostics
print(
"output={output_frames} preview={preview_frames} packets={packets_sent} "
"changed={changed} keepalive={keepalive} stale={stale} render={render_ms:.2f} ms".format(
output_frames=diagnostics.output_frames,
preview_frames=diagnostics.preview_frames,
packets_sent=diagnostics.packets_sent,
changed=diagnostics.changed_controller_frames,
keepalive=diagnostics.keepalive_controller_frames,
stale=diagnostics.stale_output_frames,
render_ms=diagnostics.last_render_ms,
)
)
last_report = now
time.sleep(0.001)
except KeyboardInterrupt:
print("Stopped by user.")
if __name__ == "__main__":
main()