feat: first try at building waveform binary files.
This commit is contained in:
@@ -61,6 +61,7 @@ from minimateplus import MiniMateClient
|
||||
from minimateplus.protocol import ProtocolError
|
||||
from minimateplus.models import CallHomeConfig, ComplianceConfig, DeviceInfo, Event, PeakValues, ProjectInfo, Timestamp
|
||||
from minimateplus.transport import TcpTransport, DEFAULT_TCP_PORT
|
||||
from minimateplus.blastware_file import write_n00, blastware_filename
|
||||
from sfm.cache import SFMCache, get_cache
|
||||
from sfm.database import SeismoDb
|
||||
|
||||
@@ -848,6 +849,82 @@ def device_event_waveform(
|
||||
return result
|
||||
|
||||
|
||||
@app.get("/device/event/{index}/blastware_file")
|
||||
def device_event_blastware_file(
|
||||
index: int,
|
||||
port: Optional[str] = Query(None, description="Serial port (e.g. COM5)"),
|
||||
baud: int = Query(38400, description="Serial baud rate"),
|
||||
host: Optional[str] = Query(None, description="TCP host — modem IP or ACH relay"),
|
||||
tcp_port: int = Query(DEFAULT_TCP_PORT, description=f"TCP port (default {DEFAULT_TCP_PORT})"),
|
||||
) -> FileResponse:
|
||||
"""
|
||||
Download the full waveform for a single event (0-based index) and return it
|
||||
as a Blastware-compatible binary file (.N00 for single-shot, .9T0 for continuous).
|
||||
|
||||
Supply either *port* (serial) or *host* (TCP/modem).
|
||||
|
||||
The file is written to a temporary path under /tmp and streamed back as a
|
||||
file download. Blastware can open it directly.
|
||||
|
||||
Performs: POLL startup → get_events(full_waveform=True, stop_after_index=index)
|
||||
→ write_n00() → FileResponse.
|
||||
"""
|
||||
log.info(
|
||||
"GET /device/event/%d/blastware_file port=%s host=%s",
|
||||
index, port, host,
|
||||
)
|
||||
|
||||
try:
|
||||
def _do():
|
||||
with _build_client(port, baud, host, tcp_port, timeout=120.0) as client:
|
||||
info = client.connect()
|
||||
events = client.get_events(full_waveform=True, stop_after_index=index)
|
||||
matching = [ev for ev in events if ev.index == index]
|
||||
return matching[0] if matching else None, info
|
||||
ev, info = _run_with_retry(_do, is_tcp=_is_tcp(host))
|
||||
except HTTPException:
|
||||
raise
|
||||
except ProtocolError as exc:
|
||||
raise HTTPException(status_code=502, detail=f"Protocol error: {exc}") from exc
|
||||
except OSError as exc:
|
||||
raise HTTPException(status_code=502, detail=f"Connection error: {exc}") from exc
|
||||
except Exception as exc:
|
||||
raise HTTPException(status_code=500, detail=f"Device error: {exc}") from exc
|
||||
|
||||
if ev is None:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail=f"Event index {index} not found on device",
|
||||
)
|
||||
|
||||
a5_frames = getattr(ev, "_a5_frames", None)
|
||||
if not a5_frames:
|
||||
raise HTTPException(
|
||||
status_code=502,
|
||||
detail=f"No waveform data received for event index {index} — 5A download failed",
|
||||
)
|
||||
|
||||
# Determine serial number from device info
|
||||
serial = getattr(info, "serial", None) or "UNKNOWN"
|
||||
|
||||
# Build filename using the same algorithm Blastware uses
|
||||
filename = blastware_filename(ev, serial)
|
||||
|
||||
# Write to /tmp so FastAPI can stream it back
|
||||
out_path = Path("/tmp") / filename
|
||||
write_n00(ev, a5_frames, out_path)
|
||||
log.info(
|
||||
"blastware_file: wrote %s (%d A5 frames, serial=%s)",
|
||||
out_path, len(a5_frames), serial,
|
||||
)
|
||||
|
||||
return FileResponse(
|
||||
path=str(out_path),
|
||||
filename=filename,
|
||||
media_type="application/octet-stream",
|
||||
)
|
||||
|
||||
|
||||
# ── Write endpoints ───────────────────────────────────────────────────────────
|
||||
|
||||
class DeviceConfigBody(BaseModel):
|
||||
|
||||
Reference in New Issue
Block a user