config.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. from __future__ import annotations
  2. from dataclasses import dataclass
  3. import os
  4. from typing import Any, Dict
  5. def _env(name: str, default: str) -> str:
  6. v = os.getenv(name, "").strip()
  7. return v or default
  8. def _env_float(name: str, default: float) -> float:
  9. v = os.getenv(name, "").strip()
  10. if not v:
  11. return float(default)
  12. try:
  13. return float(v)
  14. except Exception:
  15. return float(default)
  16. @dataclass(frozen=True)
  17. class ServiceConfig:
  18. hardware: str
  19. interp: str
  20. analysis: str
  21. SERVICES = ServiceConfig(
  22. hardware=_env("HARDWARE_BASE_URL", "http://127.0.0.1:8000"),
  23. interp=_env("INTERP_BASE_URL", "http://127.0.0.1:7475"),
  24. analysis=_env("ANALYSIS_BASE_URL", "http://127.0.0.1:1717"),
  25. )
  26. HARDWARE_BASE_URL: str = SERVICES.hardware
  27. INTERP_BASE_URL: str = SERVICES.interp
  28. ANALYSIS_BASE_URL: str = SERVICES.analysis
  29. BASE_URL: str = HARDWARE_BASE_URL
  30. HTTP_TIMEOUT_SEC: float = _env_float("HTTP_TIMEOUT_SEC", 5.0)
  31. ENDPOINTS: Dict[str, Any] = {
  32. # -----------------------------
  33. # scanner / systems / sequences
  34. # -----------------------------
  35. "sequence_params": "/sequence/params", # POST sequence params JSON
  36. "sequence_interpret": {
  37. "interpret": "/interpret/", # POST multipart file (.seq)
  38. "status": "/status/", # GET status of interpretation tasks
  39. },
  40. "systems": {
  41. "gradient": {
  42. "health": "/systems/gradient/health", # GET
  43. "upload_config": "/systems/gradient/config", # POST multipart file
  44. "start": "/systems/gradient/start", # POST
  45. },
  46. "rf": {
  47. "health": "/systems/rf/health",
  48. "upload_config": "/systems/rf/config",
  49. "start": "/systems/rf/start",
  50. },
  51. "adc": {
  52. "health": "/systems/adc/health",
  53. "upload_config": "/systems/adc/config",
  54. "start": "/systems/adc/start",
  55. },
  56. },
  57. "scan": {
  58. # Real spectrometer service API (measurement-based)
  59. "start": "/api/measurement", # POST (or GET fallback) -> measurement id
  60. "start_fallback_get": "/api/measurement", # GET starts measurement on some deployments
  61. "state_by_id": "/api/measurement/{scan_id}/state", # GET
  62. "data_by_id": "/api/measurement/{scan_id}/data", # GET
  63. "state": "/api/mstate", # GET optional legacy state endpoint
  64. # Backward compatibility for older backend shape
  65. "progress": "/scan/progress", # GET params: scan_id=...
  66. "stop": "/scan/stop", # POST optional
  67. },
  68. # -----------------------------
  69. # ANALYSIS
  70. # -----------------------------
  71. "analysis": {
  72. "upload": "/upload/", # POST multipart
  73. "filter": "/filter/", # POST form
  74. "fft": "/fft/", # POST form
  75. "result": "/result/", # GET params session_id
  76. "export": "/export/", # POST form
  77. "plot_raw": "/plot-raw/", # POST form
  78. # readme_spectrum endpoints
  79. "export_raw_data": "/export-raw-data/", # POST form
  80. "export_filter_data": "/export-filter-data/", # POST form
  81. "export_decdem_data": "/export-decdem-data/", # POST form
  82. "export_position_freq": "/export-position-freq/", # POST form
  83. "export_fwhm": "/export-FWHM/", # POST form
  84. "export_max_amplitude_freq": "/export-max-amplitude-freq/",# POST form
  85. },
  86. }
  87. def join_url(base_url: str, path: str) -> str:
  88. b = (base_url or "").rstrip("/")
  89. p = (path or "")
  90. if not p.startswith("/"):
  91. p = "/" + p
  92. return b + p
  93. def build_url(service: str, path: str) -> str:
  94. base = getattr(SERVICES, service)
  95. return join_url(base, path)
  96. def get_endpoint(*keys: str) -> str:
  97. """Resolve endpoint path from ENDPOINTS by keys.
  98. Examples:
  99. get_endpoint("sequence_params")
  100. get_endpoint("systems", "gradient", "health")
  101. get_endpoint("analysis", "upload")
  102. """
  103. cur: Any = ENDPOINTS
  104. for k in keys:
  105. cur = cur[k]
  106. if not isinstance(cur, str):
  107. raise TypeError(f"Endpoint {keys} resolved to non-string: {type(cur)}")
  108. return cur