"""AR-Autopilot Studio application entry point. Usage: python studio_main.py # launch the GUI python -m arautopilot.studio.app # same python -m arautopilot.studio.app --seed-demo # populate the local user store # with one of each role + default # PINs (1111 SA, 2222 Eng, # 3333 Owner, 4444 User) The Studio runs locally on the integrator's workstation. The dedicated bridge display is a separate Flutter app (Sprint 4+); the two share the same data model (``arautopilot.core``) and Modbus register map (``arautopilot.shared.modbus_register_map``). """ from __future__ import annotations import argparse import sys from pathlib import Path REPO_ROOT = Path(__file__).resolve().parents[2] from arautopilot.core.audit import AuditLog from arautopilot.core.user_store import UserStore, seed_demo_users from arautopilot.studio.session import studio_data_dir def run(argv: list[str] | None = None) -> int: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument( "--seed-demo", action="store_true", help="Populate the local user store with one of each role + default " "PINs (1111/2222/3333/4444). Then exit.", ) parser.add_argument( "--data-dir", type=Path, default=None, help="Override the per-user data directory. Defaults to " "~/.ar-autopilot/studio/.", ) args = parser.parse_args(argv) data_dir = args.data_dir or studio_data_dir() data_dir.mkdir(parents=True, exist_ok=True) user_store = UserStore(data_dir / "users.json") audit_log = AuditLog(data_dir / "audit.jsonl") if args.seed_demo: before = len(user_store) seed_demo_users(user_store) after = len(user_store) print( f"User store at {user_store.path}: {before} -> {after} users.\n" "Demo PINs: 1111 (Super Admin), 2222 (Engineer), 3333 (Owner), " "4444 (User).\n" "Change them with the Studio's user manager (Sprint 4+) before " "shipping to a customer." ) return 0 # Lazy-import the Qt machinery so that --seed-demo (and pytest collection # of this module) does not depend on a working display server. try: from PySide6.QtWidgets import QApplication, QMessageBox except ImportError: sys.stderr.write( "PySide6 is not installed. Run:\n\n" " pip install -e \".[studio]\"\n\n" "or:\n\n" " pip install PySide6 pyserial\n" ) return 2 from arautopilot.studio.login_window import LoginDialog from arautopilot.studio.main_window import StudioMainWindow app = QApplication(sys.argv) app.setApplicationName("AR-Autopilot Studio") # Apply AR Electronics brand theme from arautopilot.studio.ar_style import apply_ar_style # noqa: PLC0415 apply_ar_style(app) # Window icon (logo) from PySide6.QtGui import QIcon # noqa: PLC0415 from pathlib import Path as _Path # noqa: PLC0415 _logo = REPO_ROOT / "display" / "assets" / "images" / "ar_logo_full.png" if not _logo.exists(): _logo = _Path(__file__).resolve().parents[2] / "display" / "assets" / "images" / "ar_logo_full.png" if _logo.exists(): app.setWindowIcon(QIcon(str(_logo))) if len(user_store) == 0: QMessageBox.information( None, "No users", "The local user store is empty. The Studio will seed it with " "demo users (PINs 1111/2222/3333/4444). Change them immediately " "in production.", ) seed_demo_users(user_store) login = LoginDialog(store=user_store, audit=audit_log) win_holder: list[StudioMainWindow] = [] def on_logged_in(_session: object) -> None: from arautopilot.studio.session import SessionHolder session = SessionHolder.require() win = StudioMainWindow(session) win.show() win_holder.append(win) login.logged_in.connect(on_logged_in) if login.exec() == 0 and not win_holder: return 0 return app.exec() if __name__ == "__main__": sys.exit(run())