launch.ps1 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. # ==============================================================================
  2. # lf_mri_platform -- Full launch: install (if needed) -> containers -> GUI
  3. # Usage:
  4. # .\launch.ps1 -- install if needed, start services + GUI
  5. # .\launch.ps1 -Mode real -- hardware mode
  6. # .\launch.ps1 -SkipInstall -- skip dependency check
  7. # .\launch.ps1 -ServicesOnly -- do not open GUI
  8. # ==============================================================================
  9. param(
  10. [ValidateSet("plug", "real")]
  11. [string]$Mode = "plug",
  12. [switch]$SkipInstall,
  13. [switch]$ServicesOnly
  14. )
  15. $ErrorActionPreference = "Stop"
  16. $Root = $PSScriptRoot
  17. $GuiDir = Join-Path $Root "apps\gui"
  18. $LibsDir = Join-Path $Root "libs\lf-scanner"
  19. $VenvPython = Join-Path $GuiDir ".venv\Scripts\python.exe"
  20. $VenvPip = Join-Path $GuiDir ".venv\Scripts\pip.exe"
  21. $AppScript = Join-Path $GuiDir "app.py"
  22. $EnvFile = Join-Path $Root ".env"
  23. $EnvExample = Join-Path $Root ".env.example"
  24. function Write-Step($msg) { Write-Host "`n==> $msg" -ForegroundColor Cyan }
  25. function Write-OK($msg) { Write-Host " [OK] $msg" -ForegroundColor Green }
  26. function Write-Warn($msg) { Write-Host " [!!] $msg" -ForegroundColor Yellow }
  27. function Write-Fail($msg) { Write-Host "`n[FAIL] $msg" -ForegroundColor Red; exit 1 }
  28. # -- 1. Install dependencies if venv is missing --------------------------------
  29. if (-not $SkipInstall) {
  30. Write-Step "Checking GUI environment"
  31. $needsInstall = $false
  32. if (-not (Test-Path $VenvPython)) {
  33. Write-Warn "Virtual environment not found -- running install"
  34. $needsInstall = $true
  35. } else {
  36. # Check that key packages are present
  37. $checkPkg = & $VenvPython -c "import PyQt6" 2>&1
  38. if ($LASTEXITCODE -ne 0) {
  39. Write-Warn "GUI packages missing -- running install"
  40. $needsInstall = $true
  41. } else {
  42. Write-OK "Virtual environment ready"
  43. }
  44. }
  45. if ($needsInstall) {
  46. $installScript = Join-Path $Root "install.ps1"
  47. if (-not (Test-Path $installScript)) {
  48. Write-Fail "install.ps1 not found at $installScript"
  49. }
  50. & powershell.exe -ExecutionPolicy Bypass -File $installScript
  51. if ($LASTEXITCODE -ne 0) { Write-Fail "install.ps1 failed" }
  52. }
  53. }
  54. # -- 2. Ensure Docker is running -----------------------------------------------
  55. Write-Step "Checking Docker"
  56. $dockerOk = $false
  57. try { & docker info *>$null; $dockerOk = $true } catch {}
  58. if (-not $dockerOk) {
  59. Write-Warn "Docker not responding -- attempting to start Docker Desktop..."
  60. $dockerExe = "C:\Program Files\Docker\Docker\Docker Desktop.exe"
  61. if (Test-Path $dockerExe) {
  62. Start-Process $dockerExe
  63. } else {
  64. Write-Fail "Docker Desktop not found. Install it from https://www.docker.com/products/docker-desktop/"
  65. }
  66. Write-Host " Waiting up to 60 s for Docker to start..." -ForegroundColor DarkGray
  67. $waited = 0
  68. while ($waited -lt 60) {
  69. Start-Sleep 5
  70. $waited += 5
  71. try { & docker info *>$null; $dockerOk = $true; break } catch {}
  72. Write-Host " ... $waited/60 s" -ForegroundColor DarkGray
  73. }
  74. if (-not $dockerOk) { Write-Fail "Docker did not start in time. Start Docker Desktop manually and re-run." }
  75. }
  76. Write-OK "Docker is running"
  77. # -- 3. Prepare .env -----------------------------------------------------------
  78. if (-not (Test-Path $EnvFile)) {
  79. if (Test-Path $EnvExample) {
  80. Copy-Item $EnvExample $EnvFile
  81. Write-Warn ".env not found -- created from .env.example"
  82. } else {
  83. Write-Warn ".env and .env.example both missing -- using Docker Compose defaults"
  84. }
  85. }
  86. # -- 4. Build and start containers ---------------------------------------------
  87. Write-Step "Starting Docker services (mode: $Mode)"
  88. $env:ORCHESTRATOR_MODE = $Mode
  89. $envArgs = if (Test-Path $EnvFile) { @("--env-file", $EnvFile) } else { @() }
  90. & docker compose @envArgs up --build -d
  91. if ($LASTEXITCODE -ne 0) { Write-Fail "docker compose up failed" }
  92. Write-OK "Containers started"
  93. # -- 5. Wait for all services to become healthy --------------------------------
  94. Write-Step "Waiting for services to become healthy"
  95. # Read ports from .env if available; fall back to compose defaults
  96. function Get-EnvPort($key, $default) {
  97. if (Test-Path $EnvFile) {
  98. $line = Get-Content $EnvFile | Select-String "^$key=(\d+)"
  99. if ($line) { return $line.Matches[0].Groups[1].Value }
  100. }
  101. return $default
  102. }
  103. $services = @(
  104. @{ Name = "Orchestrator"; Url = "http://localhost:$(Get-EnvPort 'ORCHESTRATOR_PORT' '1717')/health" },
  105. @{ Name = "Seq-Interp"; Url = "http://localhost:$(Get-EnvPort 'SEQ_INTERP_PORT' '7475')/health" },
  106. @{ Name = "Reconstructor"; Url = "http://localhost:$(Get-EnvPort 'RECONSTRUCTOR_PORT' '8081')/health" },
  107. @{ Name = "Spectroscopy"; Url = "http://localhost:$(Get-EnvPort 'SPECTROSCOPY_PORT' '8002')/health" },
  108. @{ Name = "Spectrometer"; Url = "http://localhost:$(Get-EnvPort 'SPECTROMETER_PORT' '8000')/api/" }
  109. )
  110. $maxWait = 120
  111. $interval = 3
  112. $elapsed = 0
  113. while ($elapsed -lt $maxWait) {
  114. $pending = @()
  115. foreach ($svc in $services) {
  116. try {
  117. $r = Invoke-WebRequest $svc.Url -UseBasicParsing -TimeoutSec 2 -ErrorAction Stop
  118. if ($r.StatusCode -lt 400) { continue }
  119. } catch {}
  120. $pending += $svc.Name
  121. }
  122. if ($pending.Count -eq 0) {
  123. Write-OK "All services healthy"
  124. break
  125. }
  126. Write-Host (" ... {0}/{1} s waiting: {2}" -f $elapsed, $maxWait, ($pending -join ", ")) -ForegroundColor DarkGray
  127. Start-Sleep $interval
  128. $elapsed += $interval
  129. }
  130. if ($elapsed -ge $maxWait) {
  131. Write-Warn "Some services did not respond within ${maxWait}s -- GUI will start anyway"
  132. foreach ($svc in $services) {
  133. try {
  134. $r = Invoke-WebRequest $svc.Url -UseBasicParsing -TimeoutSec 2 -ErrorAction Stop
  135. $icon = if ($r.StatusCode -lt 400) { "[OK]" } else { "[--]" }
  136. } catch {
  137. $icon = "[--]"
  138. }
  139. Write-Host (" {0,-6} {1,-15} {2}" -f $icon, $svc.Name, $svc.Url) -ForegroundColor $(if ($icon -eq "[OK]") {"Green"} else {"Yellow"})
  140. }
  141. }
  142. # -- 6. Launch GUI in a separate window ----------------------------------------
  143. if (-not $ServicesOnly) {
  144. Write-Step "Launching GUI"
  145. if (-not (Test-Path $VenvPython)) {
  146. Write-Warn "Venv not found -- falling back to system Python"
  147. $VenvPython = "python"
  148. }
  149. if (-not (Test-Path $AppScript)) {
  150. Write-Fail "GUI entry point not found: $AppScript"
  151. }
  152. Start-Process $VenvPython `
  153. -ArgumentList "`"$AppScript`"" `
  154. -WorkingDirectory $Root `
  155. -WindowStyle Normal
  156. Write-OK "GUI launched"
  157. }
  158. # -- 7. Summary ----------------------------------------------------------------
  159. Write-Host ""
  160. Write-Host "============================================================" -ForegroundColor Green
  161. Write-Host " LF-MRI platform is running" -ForegroundColor Green
  162. Write-Host "============================================================" -ForegroundColor Green
  163. Write-Host ""
  164. Write-Host " Service endpoints:"
  165. foreach ($svc in $services) {
  166. Write-Host (" {0,-15} {1}" -f $svc.Name, $svc.Url)
  167. }
  168. Write-Host ""
  169. Write-Host " Stop services: .\stop.ps1"
  170. Write-Host ""