52 lines
1.8 KiB
Python
52 lines
1.8 KiB
Python
"""Which propagator answers, and one instance of it per process.
|
|
|
|
``get_backend()`` with no argument returns the deployment's configured default;
|
|
with a name it returns that one specifically. Both matter. The default is what
|
|
the request path should use, and the explicit name is what a caller needing
|
|
physics the default cannot do -- space weather, drag fitting, decay -- must pass.
|
|
|
|
That second case is not hypothetical. The default is ``sgp4``, which raises on
|
|
any of those; a view that forgot to name ``"orekit"`` and swallowed the error
|
|
rendered an empty chart for weeks without anything logging a complaint. If your
|
|
call needs the sidecar, say so.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from django.conf import settings
|
|
|
|
from odm import PropagatorBackend
|
|
|
|
from .backends.orekit import OrekitBackend
|
|
from .backends.sgp4 import Sgp4Backend
|
|
|
|
BACKENDS: dict[str, type[PropagatorBackend]] = {
|
|
"sgp4": Sgp4Backend,
|
|
"orekit": OrekitBackend,
|
|
}
|
|
|
|
#: One instance per name. Backends are stateless apart from an HTTP client, so
|
|
#: sharing them saves a connection pool rather than risking shared state.
|
|
_INSTANCES: dict[str, PropagatorBackend] = {}
|
|
|
|
DEFAULT_BACKEND = "sgp4"
|
|
|
|
|
|
def register(name: str, backend: type[PropagatorBackend]) -> None:
|
|
"""Add a backend under ``name``. For a service with its own propagator."""
|
|
BACKENDS[name] = backend
|
|
_INSTANCES.pop(name, None)
|
|
|
|
|
|
def get_backend(name: str | None = None) -> PropagatorBackend:
|
|
key = name or getattr(settings, "ORBITAL_PROPAGATOR_BACKEND", DEFAULT_BACKEND)
|
|
try:
|
|
cls = BACKENDS[key]
|
|
except KeyError as exc:
|
|
raise ValueError(
|
|
f"unknown propagation backend {key!r}; "
|
|
f"expected one of {', '.join(sorted(BACKENDS))}"
|
|
) from exc
|
|
if key not in _INSTANCES:
|
|
_INSTANCES[key] = cls()
|
|
return _INSTANCES[key]
|