0922cc1d68
- Struktur gemäß §8 (Eigentumsgrenzen), PLAN.md als normative Basis - Pflichtdokumente: STATUS.md, ERRORS.md, TEST_REPORT.md, CHANGELOG.md, ADRs - ADR-0001 Python 3.13-Pin, ADR-0002 GStreamer 1.28.6-Pin (Windows), ADR-0003 IPC TCP+MessagePack v1 - Kernpakete: hms_protocol, hms_domain, hms_parameter, hms_artnet, hms_adaptive, hms_capabilities, hms_plugin_sdk - Renderer-Spike: D3D11-Primärpfad + Dev-GL-Pfad (§36 Nr. 4-5) - Control Core: FastAPI REST + WebSocket (§36 Nr. 9) - Beispielplugins: Passthrough + Gaussian Blur (3 Adaptive-Quality- Varianten, HLSL/GLSL/GLES) - Tools: Art-Net-Emulator, Fixture-Generator (Master32/Layer64-CSV), Capability-Probe - JSON-Schemas: IPC, Plugin, Projekt, Cluster - 121 Unit-/Integrationstests grün, Ruff grün Gate 0 bleibt offen: Hardwaremessungen nur auf echter Windows-Referenz- hardware gültig (§29.7, §33).
149 lines
6.0 KiB
Python
149 lines
6.0 KiB
Python
"""Fixture-Generator (PLAN.md §16.3, §16.4, §16.7).
|
|
|
|
Erzeugt menschenlesbare CSV-Kanallisten für die V1-Personalities:
|
|
- HMS MediaEngine Master 32ch
|
|
- HMS MediaEngine Layer 64ch
|
|
|
|
GDTF-/pultspezifische Personality-Dateien folgen in Phase 4; die hier
|
|
erzeugten Listen tragen bereits dieselbe Fixture-Schema-Version (§16.7).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import csv
|
|
from pathlib import Path
|
|
|
|
FIXTURE_SCHEMA_VERSION = 1
|
|
|
|
MASTER32: list[tuple[int, str, str]] = [
|
|
(1, "Master Intensity (MSB)", "16 Bit (mit Kanal 2)"),
|
|
(2, "Master Intensity (LSB)", "16 Bit"),
|
|
(3, "Blackout", "Trigger/Schalter, höchste Priorität"),
|
|
(4, "Freeze Output", "Schalter"),
|
|
(5, "Preset Bank", "8 Bit"),
|
|
(6, "Preset Index (MSB)", "16 Bit (mit Kanal 7)"),
|
|
(7, "Preset Index (LSB)", "16 Bit"),
|
|
(8, "Preset Recall", "steigende Flanke, direkter Abruf ohne Cue-GO-Logik"),
|
|
(9, "Transition Type", "Enum"),
|
|
(10, "Transition Duration (MSB)", "16 Bit, konfigurierter Maximalwert"),
|
|
(11, "Transition Duration (LSB)", "16 Bit"),
|
|
(12, "Global Speed (MSB)", "16 Bit"),
|
|
(13, "Global Speed (LSB)", "16 Bit"),
|
|
(14, "BPM (MSB)", "16 Bit"),
|
|
(15, "BPM (LSB)", "16 Bit"),
|
|
(16, "Tap Tempo", "steigende Flanke"),
|
|
(17, "reserviert (Cue/Timeline-Erweiterung)", "im MVP neutral ignorieren"),
|
|
(18, "reserviert (Cue/Timeline-Erweiterung)", "im MVP neutral ignorieren"),
|
|
(19, "reserviert (Cue/Timeline-Erweiterung)", "im MVP neutral ignorieren"),
|
|
(20, "reserviert (Cue/Timeline-Erweiterung)", "im MVP neutral ignorieren"),
|
|
(21, "reserviert (Cue/Timeline-Erweiterung)", "im MVP neutral ignorieren"),
|
|
(22, "Audio Reactive Enable", "Schalter"),
|
|
(23, "Audio Master Gain", "8 Bit"),
|
|
(24, "Automation/AI Enable", "nur Freigabe, keine Sicherheitsumgehung"),
|
|
(25, "Output Test Pattern", "Enum"),
|
|
(26, "Preview Enable", "Schalter"),
|
|
(27, "Global Hue", "8 Bit"),
|
|
(28, "Global Saturation", "8 Bit"),
|
|
(29, "Fallback Preset", "8 Bit"),
|
|
(30, "Release Manual Overrides", "Trigger mit Schutzlogik"),
|
|
(31, "reserviert", "muss neutral ignoriert werden"),
|
|
(32, "reserviert", "muss neutral ignoriert werden"),
|
|
]
|
|
|
|
|
|
def _layer64() -> list[tuple[int, str, str]]:
|
|
rows: list[tuple[int, str, str]] = [
|
|
(1, "Layer Enable", "Schalter"),
|
|
(2, "Opacity (MSB)", "16 Bit"),
|
|
(3, "Opacity (LSB)", "16 Bit"),
|
|
(4, "Source Type", "Enum: Media/Generator/Live/Solid"),
|
|
(5, "Media Bank", "8 Bit"),
|
|
(6, "Media Folder", "8 Bit"),
|
|
(7, "Media/Plugin Index (MSB)", "16 Bit"),
|
|
(8, "Media/Plugin Index (LSB)", "16 Bit"),
|
|
(9, "Load/Commit Selection", "steigende Flanke"),
|
|
(10, "Transport", "Enum: Stop/Play/Pause/Retrigger"),
|
|
(11, "Loop Mode", "Enum"),
|
|
(12, "Playback Direction/Mode", "Enum"),
|
|
(13, "Playback Speed (MSB)", "16 Bit, signed Mapping"),
|
|
(14, "Playback Speed (LSB)", "16 Bit, signed Mapping"),
|
|
(15, "Playback Position (MSB)", "16 Bit, normalisiert"),
|
|
(16, "Playback Position (LSB)", "16 Bit, normalisiert"),
|
|
(17, "In Point (MSB)", "16 Bit, normalisiert"),
|
|
(18, "In Point (LSB)", "16 Bit, normalisiert"),
|
|
(19, "Out Point (MSB)", "16 Bit, normalisiert"),
|
|
(20, "Out Point (LSB)", "16 Bit, normalisiert"),
|
|
(21, "Blend Mode", "Enum"),
|
|
(22, "Transform Mode/Anchor", "Enum"),
|
|
(23, "Position X (MSB)", "16 Bit, signed"),
|
|
(24, "Position X (LSB)", "16 Bit, signed"),
|
|
(25, "Position Y (MSB)", "16 Bit, signed"),
|
|
(26, "Position Y (LSB)", "16 Bit, signed"),
|
|
(27, "Scale X (MSB)", "16 Bit"),
|
|
(28, "Scale X (LSB)", "16 Bit"),
|
|
(29, "Scale Y (MSB)", "16 Bit"),
|
|
(30, "Scale Y (LSB)", "16 Bit"),
|
|
(31, "Rotation (MSB)", "16 Bit"),
|
|
(32, "Rotation (LSB)", "16 Bit"),
|
|
(33, "Crop Left", "8 Bit"),
|
|
(34, "Crop Right", "8 Bit"),
|
|
(35, "Crop Top", "8 Bit"),
|
|
(36, "Crop Bottom", "8 Bit"),
|
|
(37, "Hue", "8 Bit"),
|
|
(38, "Saturation", "8 Bit"),
|
|
(39, "Brightness", "8 Bit"),
|
|
(40, "Contrast", "8 Bit"),
|
|
(41, "FX1 Enable", "Schalter"),
|
|
(42, "FX1 Plugin Select", "8 Bit, Show-Registry"),
|
|
(43, "FX1 Mix", "8 Bit"),
|
|
]
|
|
for slot in range(1, 9):
|
|
rows.append((43 + slot, f"FX1 Parameter P{slot}", "8 Bit oder manifestgebundene Paare"))
|
|
rows.extend(
|
|
[
|
|
(52, "FX2 Enable", "Schalter"),
|
|
(53, "FX2 Plugin Select", "8 Bit, Show-Registry"),
|
|
(54, "FX2 Mix", "8 Bit"),
|
|
]
|
|
)
|
|
for slot in range(1, 9):
|
|
rows.append((54 + slot, f"FX2 Parameter P{slot}", "8 Bit oder manifestgebundene Paare"))
|
|
rows.append((63, "Layer Retrigger/Reset", "steigende Flanke"))
|
|
rows.append((64, "reserviert", "muss neutral ignoriert werden"))
|
|
return rows
|
|
|
|
|
|
LAYER64: list[tuple[int, str, str]] = _layer64()
|
|
|
|
_HEADER = ["channel", "parameter", "resolution_behavior"]
|
|
|
|
|
|
def write_csv(rows: list[tuple[int, str, str]], path: Path) -> Path:
|
|
"""Schreibt eine Kanalliste als CSV; Elternordner werden angelegt."""
|
|
if [r[0] for r in rows] != list(range(1, len(rows) + 1)):
|
|
raise ValueError("channel numbers must be contiguous starting at 1")
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
with open(path, "w", newline="", encoding="utf-8") as fh:
|
|
writer = csv.writer(fh)
|
|
writer.writerow(_HEADER)
|
|
writer.writerows(rows)
|
|
return path
|
|
|
|
|
|
def main(argv: list[str] | None = None) -> int:
|
|
parser = argparse.ArgumentParser(prog="fixture_generator")
|
|
parser.add_argument("--out-dir", default="fixture_profiles", help="Zielordner")
|
|
args = parser.parse_args(argv)
|
|
out = Path(args.out_dir)
|
|
master = write_csv(MASTER32, out / "master32" / "master32.csv")
|
|
layer = write_csv(LAYER64, out / "layer64" / "layer64.csv")
|
|
print(f"master32: {master} ({len(MASTER32)} Kanäle)")
|
|
print(f"layer64: {layer} ({len(LAYER64)} Kanäle)")
|
|
print(f"fixture_schema_version: {FIXTURE_SCHEMA_VERSION}")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|