working server for real mock data
This commit is contained in:
parent
e178eba0af
commit
aa789b3431
@ -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)
|
||||
|
||||
@ -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())
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user