feat: implement Contec CMS7000PLUS TCP server and dashboard routing with deployment configuration
This commit is contained in:
@@ -185,3 +185,87 @@ def get_network_info():
|
||||
f"6. The dashboard will show data automatically"
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
# === Push Vitals Endpoint (for local-to-cloud sync) ===
|
||||
from schemas import NormalizedVitals
|
||||
from models import Patient
|
||||
|
||||
|
||||
@router.post("/push-vitals")
|
||||
async def push_vitals(vitals: NormalizedVitals, api_key: str = None, db: Session = Depends(get_db)):
|
||||
"""
|
||||
Receive vitals pushed from a local instance and store + broadcast them.
|
||||
|
||||
This enables the 'split architecture' where:
|
||||
- Local instance receives data from physical monitors via TCP
|
||||
- Local instance POSTs vitals here for cloud storage & dashboard access
|
||||
|
||||
Optional: pass ?api_key=YOUR_KEY for basic authentication.
|
||||
"""
|
||||
from config import settings
|
||||
|
||||
# Optional API key check
|
||||
if settings.api_token and api_key != settings.api_token:
|
||||
if settings.api_token: # Only enforce if a token is configured
|
||||
raise HTTPException(status_code=401, detail="Invalid or missing api_key")
|
||||
|
||||
try:
|
||||
# 1. Device tracking
|
||||
device = db.query(Device).filter(Device.device_id == vitals.device_id).first()
|
||||
if not device:
|
||||
device = Device(
|
||||
device_id=vitals.device_id,
|
||||
ip_address=vitals.ip_address or "remote-push",
|
||||
status="active"
|
||||
)
|
||||
db.add(device)
|
||||
else:
|
||||
if vitals.ip_address:
|
||||
device.ip_address = vitals.ip_address
|
||||
device.status = "active"
|
||||
device.last_seen = datetime.now(timezone.utc)
|
||||
|
||||
# 2. Patient tracking
|
||||
patient = db.query(Patient).filter(Patient.patient_id == vitals.patient_id).first()
|
||||
if not patient:
|
||||
patient = Patient(patient_id=vitals.patient_id, name="Unknown")
|
||||
db.add(patient)
|
||||
|
||||
# 3. Create vital reading
|
||||
from models import VitalReading as VR
|
||||
reading = VR(
|
||||
device_id=vitals.device_id,
|
||||
patient_id=vitals.patient_id,
|
||||
timestamp=vitals.timestamp,
|
||||
heart_rate=vitals.heart_rate,
|
||||
spo2=vitals.spo2,
|
||||
systolic_bp=vitals.systolic_bp,
|
||||
diastolic_bp=vitals.diastolic_bp,
|
||||
map_bp=vitals.map_bp,
|
||||
respiratory_rate=vitals.respiratory_rate,
|
||||
temperature=vitals.temperature,
|
||||
transmitted=True # Already received = transmitted
|
||||
)
|
||||
db.add(reading)
|
||||
db.commit()
|
||||
db.refresh(reading)
|
||||
|
||||
# 4. Broadcast to WebSocket clients
|
||||
await ws_manager.broadcast_vitals(vitals.model_dump(mode="json"))
|
||||
|
||||
logger.info(f"Push-vitals received for device {vitals.device_id}")
|
||||
|
||||
return {
|
||||
"status": "ok",
|
||||
"reading_id": reading.id,
|
||||
"device_id": vitals.device_id,
|
||||
"patient_id": vitals.patient_id
|
||||
}
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Push-vitals error: {e}")
|
||||
db.rollback()
|
||||
raise HTTPException(status_code=500, detail=f"Failed to store vitals: {str(e)}")
|
||||
|
||||
Reference in New Issue
Block a user