working server for real mock data

This commit is contained in:
Raika Furude 2025-10-18 16:48:05 -04:00
parent e178eba0af
commit aa789b3431
2 changed files with 138 additions and 91 deletions

View File

@ -8,11 +8,35 @@ from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from typing import List, Dict, Optional
from datetime import datetime, timedelta
from contextlib import asynccontextmanager
import asyncio
import json
from collections import defaultdict
app = FastAPI(title="VitalLink API", version="1.0.0")
# ============================================================================
# LIFESPAN MANAGEMENT (Modern FastAPI Way)
# ============================================================================
@asynccontextmanager
async def lifespan(app: FastAPI):
# Startup
print("=" * 80)
print("VitalLink Backend API Started")
print("=" * 80)
print("API Documentation: http://localhost:8000/docs")
print("WebSocket Endpoint: ws://localhost:8000/ws")
print("=" * 80)
yield
# Shutdown
print("\nVitalLink Backend API Shutting Down")
# ============================================================================
# APP INITIALIZATION
# ============================================================================
app = FastAPI(title="VitalLink API", version="1.0.0", lifespan=lifespan)
# CORS middleware for frontend
app.add_middleware(
@ -151,6 +175,17 @@ def calculate_priority_score(patient: Patient) -> float:
# ============================================================================
@app.get("/")
async def root():
"""Root endpoint"""
return {
"message": "VitalLink Backend API",
"version": "1.0.0",
"docs": "/docs",
"status": "running",
}
@app.post("/api/checkin")
async def check_in_patient(data: PatientCheckIn):
"""Register a new patient and assign wristband"""
@ -362,34 +397,10 @@ async def broadcast_update(message: dict):
# ============================================================================
# STARTUP / SHUTDOWN
# RUN SERVER
# ============================================================================
if __name__ == "__main__":
import uvicorn
@app.on_event("startup")
async def startup_event():
print("=" * 80)
print("VitalLink Backend API Started")
print("=" * 80)
print("API Documentation: http://localhost:8000/docs")
print("WebSocket Endpoint: ws://localhost:8000/ws")
print("=" * 80)
# ============================================================================
# INTEGRATION WITH SIMULATOR
# ============================================================================
async def simulator_integration_task():
"""
Background task to integrate with wristband simulator
In production, this receives data from actual base station
"""
# This would be replaced with actual base station connection
# For now, shows how to integrate the simulator
pass
# Run with: uvicorn vitalink_backend:app --reload
# Then access at http://localhost:8000
uvicorn.run(app, host="0.0.0.0", port=8000)

View File

@ -7,6 +7,7 @@ import asyncio
import struct
import random
import time
import aiohttp
from dataclasses import dataclass
from typing import Dict, List, Optional
from enum import IntFlag
@ -347,6 +348,17 @@ class BaseStationSimulator:
"checksum": f"0x{checksum:02X}",
}
async def send_to_backend(self, decoded_data: dict):
"""Send vitals data to backend API"""
try:
async with aiohttp.ClientSession() as session:
await session.post(
"http://localhost:8000/api/vitals", json=decoded_data
)
except Exception as e:
# Silently fail if backend not available
pass
async def simulate_band_transmission(self, band_id: str):
"""Simulate continuous transmission from one wristband"""
band = self.wristbands[band_id]
@ -369,6 +381,9 @@ class BaseStationSimulator:
# Log packet
self.packet_log.append(decoded)
# Send to backend API
await self.send_to_backend(decoded)
# Print to console
tier_symbol = (
"🔴"
@ -384,9 +399,6 @@ class BaseStationSimulator:
f"Seq={decoded['seq']}"
)
# Send to backend API (in production)
# await self.send_to_api(decoded)
await asyncio.sleep(interval)
async def run(self):
@ -402,7 +414,7 @@ class BaseStationSimulator:
]
# Run until stopped
await asyncio.gather(*tasks)
await asyncio.gather(*tasks, return_exceptions=True)
def stop(self):
"""Stop the simulation"""
@ -428,68 +440,101 @@ class BaseStationSimulator:
# ============================================================================
# DEMO / TEST SCENARIOS
# AUTO CHECK-IN FUNCTION
# ============================================================================
async def demo_scenario_1():
"""Demo: Mix of stable and deteriorating patients"""
print("\n" + "=" * 80)
print("DEMO SCENARIO 1: Mixed Patient Population")
print("=" * 80 + "\n")
async def auto_checkin_patients():
"""Automatically check in patients via API"""
patients = [
{
"firstName": "John",
"lastName": "Smith",
"dob": "1985-03-15",
"symptoms": ["Chest Pain"],
"severity": "mild",
},
{
"firstName": "Sarah",
"lastName": "Johnson",
"dob": "1990-07-22",
"symptoms": ["Fever", "Difficulty Breathing"],
"severity": "moderate",
},
{
"firstName": "Michael",
"lastName": "Chen",
"dob": "1978-11-05",
"symptoms": ["Severe Headache"],
"severity": "severe",
},
]
base = BaseStationSimulator()
# Add various patients
base.add_wristband("VitalLink-A1B2", "stable", "P100001")
base.add_wristband("VitalLink-C3D4", "mild_anxiety", "P100002")
base.add_wristband("VitalLink-E5F6", "deteriorating", "P100003")
# Run for 30 seconds
assigned = []
try:
await asyncio.wait_for(base.run(), timeout=30.0)
except asyncio.TimeoutError:
base.stop()
async with aiohttp.ClientSession() as session:
for patient_data in patients:
async with session.post(
"http://localhost:8000/api/checkin", json=patient_data
) as resp:
if resp.status == 200:
data = await resp.json()
assigned.append(data)
print(
f"[CHECKIN] {patient_data['firstName']} {patient_data['lastName']}{data['patient_id']} / {data['band_id']}"
)
except Exception as e:
print(f"[ERROR] Could not check in patients: {e}")
print("[INFO] Make sure backend is running at http://localhost:8000")
return []
return assigned
# ============================================================================
# MAIN - CONTINUOUS MODE
# ============================================================================
async def continuous_mode():
"""Run simulator continuously, sending data to backend"""
print("\n" + "=" * 80)
print("SUMMARY:")
print(base.get_summary())
print("=" * 80)
async def demo_scenario_2():
"""Demo: Critical patient arrival"""
print("\n" + "=" * 80)
print("DEMO SCENARIO 2: Critical Patient Emergency")
print("CONTINUOUS MODE: Sending data to backend at http://localhost:8000")
print("=" * 80 + "\n")
# Auto check-in patients first
print("Checking in patients...")
assigned = await auto_checkin_patients()
if not assigned:
print("\n[ERROR] No patients checked in. Is the backend running?")
print("[INFO] Start backend with: python backend/server.py")
return
print()
base = BaseStationSimulator()
# Start with stable patients
base.add_wristband("VitalLink-1111", "stable", "P200001")
base.add_wristband("VitalLink-2222", "stable", "P200002")
# Add wristbands using the assigned IDs
profiles = ["stable", "mild_anxiety", "deteriorating"]
for i, assignment in enumerate(assigned):
profile = profiles[i] if i < len(profiles) else "stable"
base.add_wristband(assignment["band_id"], profile, assignment["patient_id"])
# Simulate for 10 seconds
async def add_critical_patient():
await asyncio.sleep(10)
print("\n⚠️ CRITICAL PATIENT ARRIVED ⚠️\n")
base.add_wristband("VitalLink-9999", "critical", "P200999")
# Run both tasks
await asyncio.gather(
add_critical_patient(), asyncio.wait_for(base.run(), timeout=25.0)
)
base.stop()
print("\n" + "=" * 80)
print("SUMMARY:")
print(base.get_summary())
print("\nPress Ctrl+C to stop\n")
print("=" * 80)
# Run indefinitely
try:
await base.run()
except KeyboardInterrupt:
base.stop()
print("\n" + "=" * 80)
print("SUMMARY:")
print(base.get_summary())
print("=" * 80)
print("\n[BASE] Simulator stopped by user")
# ============================================================================
# MAIN
# ============================================================================
if __name__ == "__main__":
print("""
@ -504,16 +549,7 @@ Available Patient Profiles:
- deteriorating: Gradually worsening condition
- critical: Severe vitals, triggers emergency tier
- sepsis: Rapid deterioration pattern
Usage Examples:
1. Run demo scenarios (below)
2. Create custom scenarios using BaseStationSimulator
3. Integrate with FastAPI backend for web portal
""")
# Choose a demo to run
print("\nRunning Demo Scenario 1...\n")
asyncio.run(demo_scenario_1())
# Uncomment to run other scenarios:
# asyncio.run(demo_scenario_2())
# Run continuous mode
asyncio.run(continuous_mode())