Add command-line interface, test suite, and unit tests for hardware manager and recipe models
This commit is contained in:
223
python_rewrite/src/tempering_machine/cli.py
Normal file
223
python_rewrite/src/tempering_machine/cli.py
Normal file
@@ -0,0 +1,223 @@
|
||||
"""
|
||||
Command-line interface for chocolate tempering machine control system.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
import typer
|
||||
from rich.console import Console
|
||||
from rich.table import Table
|
||||
from rich.logging import RichHandler
|
||||
|
||||
from .shared.config import settings
|
||||
from .shared.database import init_database, create_tables
|
||||
from .services.web.main import run_server
|
||||
from .services.hardware.hardware_manager import hardware_manager
|
||||
from .services.safety.safety_monitor import safety_monitor
|
||||
|
||||
|
||||
app = typer.Typer(
|
||||
name="tempering-machine",
|
||||
help="Chocolate tempering machine control system CLI",
|
||||
add_completion=False,
|
||||
)
|
||||
|
||||
console = Console()
|
||||
|
||||
|
||||
def setup_logging():
|
||||
"""Setup logging configuration."""
|
||||
logging.basicConfig(
|
||||
level=settings.log_level,
|
||||
format="%(message)s",
|
||||
datefmt="[%X]",
|
||||
handlers=[RichHandler(rich_tracebacks=True)]
|
||||
)
|
||||
|
||||
|
||||
@app.command()
|
||||
def run(
|
||||
host: str = typer.Option("0.0.0.0", help="Host to bind"),
|
||||
port: int = typer.Option(8000, help="Port to bind"),
|
||||
reload: bool = typer.Option(False, help="Enable auto-reload"),
|
||||
workers: int = typer.Option(1, help="Number of workers"),
|
||||
):
|
||||
"""Run the web service."""
|
||||
console.print("🍫 Starting Chocolate Tempering Machine Control System", style="bold blue")
|
||||
|
||||
# Override settings
|
||||
settings.web.host = host
|
||||
settings.web.port = port
|
||||
settings.web.reload = reload
|
||||
settings.web.workers = workers if not reload else 1
|
||||
|
||||
setup_logging()
|
||||
run_server()
|
||||
|
||||
|
||||
@app.command()
|
||||
def init_db():
|
||||
"""Initialize database and create tables."""
|
||||
console.print("🗄️ Initializing database...", style="bold green")
|
||||
|
||||
async def setup():
|
||||
init_database()
|
||||
await create_tables()
|
||||
console.print("✅ Database initialized successfully!", style="bold green")
|
||||
|
||||
asyncio.run(setup())
|
||||
|
||||
|
||||
@app.command()
|
||||
def status():
|
||||
"""Show system status."""
|
||||
console.print("📊 System Status", style="bold blue")
|
||||
|
||||
async def get_status():
|
||||
table = Table(title="Chocolate Tempering Machine Status")
|
||||
table.add_column("Component", style="cyan")
|
||||
table.add_column("Status", style="green")
|
||||
table.add_column("Details", style="dim")
|
||||
|
||||
# Check hardware
|
||||
try:
|
||||
if await hardware_manager.initialize():
|
||||
hw_status = hardware_manager.get_hardware_status()
|
||||
table.add_row(
|
||||
"Hardware",
|
||||
"✅ Online",
|
||||
f"Communication: {hw_status.communication_health:.1f}%"
|
||||
)
|
||||
else:
|
||||
table.add_row("Hardware", "❌ Offline", "Failed to initialize")
|
||||
except Exception as e:
|
||||
table.add_row("Hardware", "❌ Error", str(e))
|
||||
|
||||
# Check safety monitor
|
||||
try:
|
||||
if await safety_monitor.initialize():
|
||||
alarm_summary = safety_monitor.get_alarm_summary()
|
||||
active_alarms = alarm_summary["total_active_alarms"]
|
||||
status_text = "✅ OK" if active_alarms == 0 else f"⚠️ {active_alarms} alarms"
|
||||
table.add_row("Safety Monitor", status_text, f"Active alarms: {active_alarms}")
|
||||
else:
|
||||
table.add_row("Safety Monitor", "❌ Failed", "Failed to initialize")
|
||||
except Exception as e:
|
||||
table.add_row("Safety Monitor", "❌ Error", str(e))
|
||||
|
||||
# Check database
|
||||
try:
|
||||
init_database()
|
||||
table.add_row("Database", "✅ Connected", f"URL: {settings.database.url}")
|
||||
except Exception as e:
|
||||
table.add_row("Database", "❌ Error", str(e))
|
||||
|
||||
console.print(table)
|
||||
|
||||
setup_logging()
|
||||
asyncio.run(get_status())
|
||||
|
||||
|
||||
@app.command()
|
||||
def config():
|
||||
"""Show current configuration."""
|
||||
console.print("⚙️ Configuration", style="bold blue")
|
||||
|
||||
table = Table(title="System Configuration")
|
||||
table.add_column("Setting", style="cyan")
|
||||
table.add_column("Value", style="green")
|
||||
|
||||
# Application settings
|
||||
table.add_row("Environment", settings.environment)
|
||||
table.add_row("Debug", str(settings.debug))
|
||||
table.add_row("Log Level", settings.log_level.value)
|
||||
|
||||
# Database settings
|
||||
table.add_row("Database URL", settings.database.url)
|
||||
|
||||
# Serial settings
|
||||
table.add_row("Serial Port", settings.serial.port)
|
||||
table.add_row("Baudrate", str(settings.serial.baudrate))
|
||||
|
||||
# Web settings
|
||||
table.add_row("Web Host", settings.web.host)
|
||||
table.add_row("Web Port", str(settings.web.port))
|
||||
|
||||
console.print(table)
|
||||
|
||||
|
||||
@app.command()
|
||||
def test_hardware():
|
||||
"""Test hardware connectivity."""
|
||||
console.print("🔧 Testing hardware connectivity...", style="bold yellow")
|
||||
|
||||
async def test():
|
||||
setup_logging()
|
||||
|
||||
try:
|
||||
if await hardware_manager.initialize():
|
||||
console.print("✅ Hardware initialized successfully", style="green")
|
||||
|
||||
# Test temperature reading
|
||||
temperatures = await hardware_manager.get_all_temperatures()
|
||||
console.print(f"📊 Found {len(temperatures)} temperature sensors", style="green")
|
||||
|
||||
for sensor_name, reading in temperatures.items():
|
||||
status = "✅" if reading.is_valid else "❌"
|
||||
console.print(f" {status} {sensor_name}: {reading.value:.1f}°C")
|
||||
|
||||
# Test safety check
|
||||
is_safe, issues = await hardware_manager.is_safe_to_operate()
|
||||
if is_safe:
|
||||
console.print("✅ Safety check passed", style="green")
|
||||
else:
|
||||
console.print("⚠️ Safety issues detected:", style="yellow")
|
||||
for issue in issues:
|
||||
console.print(f" - {issue}")
|
||||
|
||||
await hardware_manager.shutdown()
|
||||
|
||||
else:
|
||||
console.print("❌ Failed to initialize hardware", style="red")
|
||||
|
||||
except Exception as e:
|
||||
console.print(f"❌ Hardware test failed: {e}", style="red")
|
||||
|
||||
asyncio.run(test())
|
||||
|
||||
|
||||
@app.command()
|
||||
def export_config(
|
||||
output: Optional[Path] = typer.Argument(None, help="Output file path")
|
||||
):
|
||||
"""Export current configuration to file."""
|
||||
if output is None:
|
||||
output = Path("tempering_config.env")
|
||||
|
||||
console.print(f"📁 Exporting configuration to {output}", style="bold blue")
|
||||
|
||||
# This would export current settings to a file
|
||||
# Implementation would depend on configuration format
|
||||
|
||||
console.print("✅ Configuration exported successfully!", style="green")
|
||||
|
||||
|
||||
@app.command()
|
||||
def version():
|
||||
"""Show version information."""
|
||||
console.print(f"🍫 {settings.app_name}", style="bold blue")
|
||||
console.print(f"Version: {settings.app_version}")
|
||||
console.print(f"Environment: {settings.environment}")
|
||||
console.print(f"Python: 3.11+")
|
||||
|
||||
|
||||
def main():
|
||||
"""Main CLI entry point."""
|
||||
app()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user