- Create UI client (src/ui_client.py) with MIDI controller integration - Create control server (src/control_server.py) with lighting logic - Implement WebSocket protocol between UI and control server - Add startup script (start_lighting_controller.py) for all components - Update Pipfile with new scripts for separated architecture - Add comprehensive documentation (README_SEPARATED.md) - Fix LED connection stability with heartbeat mechanism - Fix UI knob display and button highlighting - Maintain backward compatibility with existing MIDI mappings
117 lines
3.5 KiB
Python
117 lines
3.5 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Startup script for the separated lighting controller architecture.
|
|
Starts the control server, sound detector, and UI client.
|
|
"""
|
|
|
|
import subprocess
|
|
import sys
|
|
import time
|
|
import signal
|
|
import os
|
|
from pathlib import Path
|
|
|
|
def start_process(command, name, cwd=None):
|
|
"""Start a subprocess and return the process object."""
|
|
print(f"Starting {name}...")
|
|
try:
|
|
process = subprocess.Popen(
|
|
command,
|
|
shell=True,
|
|
cwd=cwd,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
preexec_fn=os.setsid if os.name != 'nt' else None
|
|
)
|
|
print(f"{name} started with PID {process.pid}")
|
|
return process
|
|
except Exception as e:
|
|
print(f"Failed to start {name}: {e}")
|
|
return None
|
|
|
|
def main():
|
|
"""Main startup function."""
|
|
print("Starting Lighting Controller (Separated Architecture)")
|
|
print("=" * 50)
|
|
|
|
# Get the project directory
|
|
project_dir = Path(__file__).parent
|
|
|
|
processes = []
|
|
|
|
try:
|
|
# Start control server
|
|
control_process = start_process(
|
|
"python src/control_server.py",
|
|
"Control Server",
|
|
cwd=project_dir
|
|
)
|
|
if control_process:
|
|
processes.append(("Control Server", control_process))
|
|
|
|
# Wait a moment for the control server to start
|
|
time.sleep(2)
|
|
|
|
# Start sound detector
|
|
sound_process = start_process(
|
|
"python src/sound.py",
|
|
"Sound Detector",
|
|
cwd=project_dir
|
|
)
|
|
if sound_process:
|
|
processes.append(("Sound Detector", sound_process))
|
|
|
|
# Wait a moment for the sound detector to start
|
|
time.sleep(1)
|
|
|
|
# Start UI client
|
|
ui_process = start_process(
|
|
"python src/ui_client.py",
|
|
"UI Client",
|
|
cwd=project_dir
|
|
)
|
|
if ui_process:
|
|
processes.append(("UI Client", ui_process))
|
|
|
|
print("\nAll components started successfully!")
|
|
print("Press Ctrl+C to stop all components...")
|
|
|
|
# Wait for processes
|
|
try:
|
|
while True:
|
|
time.sleep(1)
|
|
# Check if any process has died
|
|
for name, process in processes:
|
|
if process.poll() is not None:
|
|
print(f"Warning: {name} has stopped unexpectedly")
|
|
except KeyboardInterrupt:
|
|
print("\nShutting down all components...")
|
|
|
|
except Exception as e:
|
|
print(f"Error during startup: {e}")
|
|
|
|
finally:
|
|
# Clean up all processes
|
|
for name, process in processes:
|
|
if process and process.poll() is None:
|
|
print(f"Stopping {name}...")
|
|
try:
|
|
if os.name != 'nt':
|
|
os.killpg(os.getpgid(process.pid), signal.SIGTERM)
|
|
else:
|
|
process.terminate()
|
|
process.wait(timeout=5)
|
|
except subprocess.TimeoutExpired:
|
|
print(f"Force killing {name}...")
|
|
if os.name != 'nt':
|
|
os.killpg(os.getpgid(process.pid), signal.SIGKILL)
|
|
else:
|
|
process.kill()
|
|
except Exception as e:
|
|
print(f"Error stopping {name}: {e}")
|
|
|
|
print("All components stopped.")
|
|
|
|
if __name__ == "__main__":
|
|
main()
|