| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198 |
- # ==============================================================================
- # lf_mri_platform -- Full launch: install (if needed) -> containers -> GUI
- # Usage:
- # .\launch.ps1 -- install if needed, start services + GUI
- # .\launch.ps1 -Mode real -- hardware mode
- # .\launch.ps1 -SkipInstall -- skip dependency check
- # .\launch.ps1 -ServicesOnly -- do not open GUI
- # ==============================================================================
- param(
- [ValidateSet("plug", "real")]
- [string]$Mode = "plug",
- [switch]$SkipInstall,
- [switch]$ServicesOnly
- )
- $ErrorActionPreference = "Stop"
- $Root = $PSScriptRoot
- $GuiDir = Join-Path $Root "apps\gui"
- $LibsDir = Join-Path $Root "libs\lf-scanner"
- $VenvPython = Join-Path $GuiDir ".venv\Scripts\python.exe"
- $VenvPip = Join-Path $GuiDir ".venv\Scripts\pip.exe"
- $AppScript = Join-Path $GuiDir "app.py"
- $EnvFile = Join-Path $Root ".env"
- $EnvExample = Join-Path $Root ".env.example"
- function Write-Step($msg) { Write-Host "`n==> $msg" -ForegroundColor Cyan }
- function Write-OK($msg) { Write-Host " [OK] $msg" -ForegroundColor Green }
- function Write-Warn($msg) { Write-Host " [!!] $msg" -ForegroundColor Yellow }
- function Write-Fail($msg) { Write-Host "`n[FAIL] $msg" -ForegroundColor Red; exit 1 }
- # -- 1. Install dependencies if venv is missing --------------------------------
- if (-not $SkipInstall) {
- Write-Step "Checking GUI environment"
- $needsInstall = $false
- if (-not (Test-Path $VenvPython)) {
- Write-Warn "Virtual environment not found -- running install"
- $needsInstall = $true
- } else {
- # Check that key packages are present
- $checkPkg = & $VenvPython -c "import PyQt6" 2>&1
- if ($LASTEXITCODE -ne 0) {
- Write-Warn "GUI packages missing -- running install"
- $needsInstall = $true
- } else {
- Write-OK "Virtual environment ready"
- }
- }
- if ($needsInstall) {
- $installScript = Join-Path $Root "install.ps1"
- if (-not (Test-Path $installScript)) {
- Write-Fail "install.ps1 not found at $installScript"
- }
- & powershell.exe -ExecutionPolicy Bypass -File $installScript
- if ($LASTEXITCODE -ne 0) { Write-Fail "install.ps1 failed" }
- }
- }
- # -- 2. Ensure Docker is running -----------------------------------------------
- Write-Step "Checking Docker"
- $dockerOk = $false
- try { & docker info *>$null; $dockerOk = $true } catch {}
- if (-not $dockerOk) {
- Write-Warn "Docker not responding -- attempting to start Docker Desktop..."
- $dockerExe = "C:\Program Files\Docker\Docker\Docker Desktop.exe"
- if (Test-Path $dockerExe) {
- Start-Process $dockerExe
- } else {
- Write-Fail "Docker Desktop not found. Install it from https://www.docker.com/products/docker-desktop/"
- }
- Write-Host " Waiting up to 60 s for Docker to start..." -ForegroundColor DarkGray
- $waited = 0
- while ($waited -lt 60) {
- Start-Sleep 5
- $waited += 5
- try { & docker info *>$null; $dockerOk = $true; break } catch {}
- Write-Host " ... $waited/60 s" -ForegroundColor DarkGray
- }
- if (-not $dockerOk) { Write-Fail "Docker did not start in time. Start Docker Desktop manually and re-run." }
- }
- Write-OK "Docker is running"
- # -- 3. Prepare .env -----------------------------------------------------------
- if (-not (Test-Path $EnvFile)) {
- if (Test-Path $EnvExample) {
- Copy-Item $EnvExample $EnvFile
- Write-Warn ".env not found -- created from .env.example"
- } else {
- Write-Warn ".env and .env.example both missing -- using Docker Compose defaults"
- }
- }
- # -- 4. Build and start containers ---------------------------------------------
- Write-Step "Starting Docker services (mode: $Mode)"
- $env:ORCHESTRATOR_MODE = $Mode
- $envArgs = if (Test-Path $EnvFile) { @("--env-file", $EnvFile) } else { @() }
- & docker compose @envArgs up --build -d
- if ($LASTEXITCODE -ne 0) { Write-Fail "docker compose up failed" }
- Write-OK "Containers started"
- # -- 5. Wait for all services to become healthy --------------------------------
- Write-Step "Waiting for services to become healthy"
- # Read ports from .env if available; fall back to compose defaults
- function Get-EnvPort($key, $default) {
- if (Test-Path $EnvFile) {
- $line = Get-Content $EnvFile | Select-String "^$key=(\d+)"
- if ($line) { return $line.Matches[0].Groups[1].Value }
- }
- return $default
- }
- $services = @(
- @{ Name = "Orchestrator"; Url = "http://localhost:$(Get-EnvPort 'ORCHESTRATOR_PORT' '1717')/health" },
- @{ Name = "Seq-Interp"; Url = "http://localhost:$(Get-EnvPort 'SEQ_INTERP_PORT' '7475')/health" },
- @{ Name = "Reconstructor"; Url = "http://localhost:$(Get-EnvPort 'RECONSTRUCTOR_PORT' '8081')/health" },
- @{ Name = "Spectroscopy"; Url = "http://localhost:$(Get-EnvPort 'SPECTROSCOPY_PORT' '8002')/health" },
- @{ Name = "Spectrometer"; Url = "http://localhost:$(Get-EnvPort 'SPECTROMETER_PORT' '8000')/api/" }
- )
- $maxWait = 120
- $interval = 3
- $elapsed = 0
- while ($elapsed -lt $maxWait) {
- $pending = @()
- foreach ($svc in $services) {
- try {
- $r = Invoke-WebRequest $svc.Url -UseBasicParsing -TimeoutSec 2 -ErrorAction Stop
- if ($r.StatusCode -lt 400) { continue }
- } catch {}
- $pending += $svc.Name
- }
- if ($pending.Count -eq 0) {
- Write-OK "All services healthy"
- break
- }
- Write-Host (" ... {0}/{1} s waiting: {2}" -f $elapsed, $maxWait, ($pending -join ", ")) -ForegroundColor DarkGray
- Start-Sleep $interval
- $elapsed += $interval
- }
- if ($elapsed -ge $maxWait) {
- Write-Warn "Some services did not respond within ${maxWait}s -- GUI will start anyway"
- foreach ($svc in $services) {
- try {
- $r = Invoke-WebRequest $svc.Url -UseBasicParsing -TimeoutSec 2 -ErrorAction Stop
- $icon = if ($r.StatusCode -lt 400) { "[OK]" } else { "[--]" }
- } catch {
- $icon = "[--]"
- }
- Write-Host (" {0,-6} {1,-15} {2}" -f $icon, $svc.Name, $svc.Url) -ForegroundColor $(if ($icon -eq "[OK]") {"Green"} else {"Yellow"})
- }
- }
- # -- 6. Launch GUI in a separate window ----------------------------------------
- if (-not $ServicesOnly) {
- Write-Step "Launching GUI"
- if (-not (Test-Path $VenvPython)) {
- Write-Warn "Venv not found -- falling back to system Python"
- $VenvPython = "python"
- }
- if (-not (Test-Path $AppScript)) {
- Write-Fail "GUI entry point not found: $AppScript"
- }
- Start-Process $VenvPython `
- -ArgumentList "`"$AppScript`"" `
- -WorkingDirectory $Root `
- -WindowStyle Normal
- Write-OK "GUI launched"
- }
- # -- 7. Summary ----------------------------------------------------------------
- Write-Host ""
- Write-Host "============================================================" -ForegroundColor Green
- Write-Host " LF-MRI platform is running" -ForegroundColor Green
- Write-Host "============================================================" -ForegroundColor Green
- Write-Host ""
- Write-Host " Service endpoints:"
- foreach ($svc in $services) {
- Write-Host (" {0,-15} {1}" -f $svc.Name, $svc.Url)
- }
- Write-Host ""
- Write-Host " Stop services: .\stop.ps1"
- Write-Host ""
|