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 pydantic import BaseModel
from typing import List, Dict, Optional from typing import List, Dict, Optional
from datetime import datetime, timedelta from datetime import datetime, timedelta
from contextlib import asynccontextmanager
import asyncio import asyncio
import json import json
from collections import defaultdict 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 # CORS middleware for frontend
app.add_middleware( 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") @app.post("/api/checkin")
async def check_in_patient(data: PatientCheckIn): async def check_in_patient(data: PatientCheckIn):
"""Register a new patient and assign wristband""" """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") uvicorn.run(app, host="0.0.0.0", port=8000)
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

View File

@ -7,6 +7,7 @@ import asyncio
import struct import struct
import random import random
import time import time
import aiohttp
from dataclasses import dataclass from dataclasses import dataclass
from typing import Dict, List, Optional from typing import Dict, List, Optional
from enum import IntFlag from enum import IntFlag
@ -347,6 +348,17 @@ class BaseStationSimulator:
"checksum": f"0x{checksum:02X}", "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): async def simulate_band_transmission(self, band_id: str):
"""Simulate continuous transmission from one wristband""" """Simulate continuous transmission from one wristband"""
band = self.wristbands[band_id] band = self.wristbands[band_id]
@ -369,6 +381,9 @@ class BaseStationSimulator:
# Log packet # Log packet
self.packet_log.append(decoded) self.packet_log.append(decoded)
# Send to backend API
await self.send_to_backend(decoded)
# Print to console # Print to console
tier_symbol = ( tier_symbol = (
"🔴" "🔴"
@ -384,9 +399,6 @@ class BaseStationSimulator:
f"Seq={decoded['seq']}" f"Seq={decoded['seq']}"
) )
# Send to backend API (in production)
# await self.send_to_api(decoded)
await asyncio.sleep(interval) await asyncio.sleep(interval)
async def run(self): async def run(self):
@ -402,7 +414,7 @@ class BaseStationSimulator:
] ]
# Run until stopped # Run until stopped
await asyncio.gather(*tasks) await asyncio.gather(*tasks, return_exceptions=True)
def stop(self): def stop(self):
"""Stop the simulation""" """Stop the simulation"""
@ -428,68 +440,101 @@ class BaseStationSimulator:
# ============================================================================ # ============================================================================
# DEMO / TEST SCENARIOS # AUTO CHECK-IN FUNCTION
# ============================================================================ # ============================================================================
async def demo_scenario_1(): async def auto_checkin_patients():
"""Demo: Mix of stable and deteriorating patients""" """Automatically check in patients via API"""
print("\n" + "=" * 80) patients = [
print("DEMO SCENARIO 1: Mixed Patient Population") {
print("=" * 80 + "\n") "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() assigned = []
# 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
try: try:
await asyncio.wait_for(base.run(), timeout=30.0) async with aiohttp.ClientSession() as session:
except asyncio.TimeoutError: for patient_data in patients:
base.stop() 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("\n" + "=" * 80)
print("SUMMARY:") print("CONTINUOUS MODE: Sending data to backend at http://localhost:8000")
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("=" * 80 + "\n") 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() base = BaseStationSimulator()
# Start with stable patients # Add wristbands using the assigned IDs
base.add_wristband("VitalLink-1111", "stable", "P200001") profiles = ["stable", "mild_anxiety", "deteriorating"]
base.add_wristband("VitalLink-2222", "stable", "P200002") 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 print("\nPress Ctrl+C to stop\n")
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("=" * 80) 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__": if __name__ == "__main__":
print(""" print("""
@ -504,16 +549,7 @@ Available Patient Profiles:
- deteriorating: Gradually worsening condition - deteriorating: Gradually worsening condition
- critical: Severe vitals, triggers emergency tier - critical: Severe vitals, triggers emergency tier
- sepsis: Rapid deterioration pattern - 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 # Run continuous mode
print("\nRunning Demo Scenario 1...\n") asyncio.run(continuous_mode())
asyncio.run(demo_scenario_1())
# Uncomment to run other scenarios:
# asyncio.run(demo_scenario_2())