492 lines
17 KiB
Markdown
492 lines
17 KiB
Markdown
# VigilCare Clinical Simulator -- User Guide
|
|
|
|
Welcome! The VigilCare Simulator lets you replay realistic hospital patient scenarios against the VigilCare Clinical API. You can watch a patient's vitals change over time, see alerts fire (NEWS2, SIRS/qSOFA, sepsis bundles), and observe how the system detects clinical deterioration -- all without real patients.
|
|
|
|
Think of it as a flight simulator, but for clinical decision support.
|
|
|
|
---
|
|
|
|
## Table of Contents
|
|
|
|
1. [Prerequisites](#1-prerequisites)
|
|
2. [Quick Start](#2-quick-start)
|
|
3. [Available Commands](#3-available-commands)
|
|
4. [Understanding Scenarios](#4-understanding-scenarios)
|
|
5. [Available Scenarios](#5-available-scenarios)
|
|
6. [Controlling Replay Speed](#6-controlling-replay-speed)
|
|
7. [Reading the Output](#7-reading-the-output)
|
|
8. [Creating Your Own Scenarios](#8-creating-your-own-scenarios)
|
|
9. [Troubleshooting](#9-troubleshooting)
|
|
10. [Ward Outage Scenario (Climate Resilience)](#10-ward-outage-scenario-climate-resilience)
|
|
|
|
---
|
|
|
|
## 1. Prerequisites
|
|
|
|
You need two things installed:
|
|
|
|
- **.NET 8 SDK** -- Download from [dotnet.microsoft.com](https://dotnet.microsoft.com/download/dotnet/8.0)
|
|
- **The VigilCare API running locally** -- The simulator sends data to the API, so it must be up first
|
|
|
|
### Starting the API
|
|
|
|
From the project root directory, run:
|
|
|
|
```bash
|
|
docker compose up -d
|
|
dotnet run --project VigilCare.Api
|
|
```
|
|
|
|
The API starts on `http://localhost:5270` by default.
|
|
|
|
> See [docker-compose-usage-and-troubleshooting.md](docker-compose-usage-and-troubleshooting.md) if you have trouble with Docker.
|
|
|
|
---
|
|
|
|
## 2. Quick Start
|
|
|
|
Open a terminal in the project root and run:
|
|
|
|
```bash
|
|
dotnet run --project VigilCare.Simulator -- replay \
|
|
VigilCare.Simulator/Scenarios/List/uti-sepsis-elderly-01.json \
|
|
--speed 60 --poll
|
|
```
|
|
|
|
This replays a 4-hour UTI-to-sepsis progression in about 4 seconds, showing alerts as they fire.
|
|
|
|
That's it! Read on for more detail.
|
|
|
|
---
|
|
|
|
## 3. Available Commands
|
|
|
|
The simulator has four commands. All are run with `dotnet run --project VigilCare.Simulator -- <command>`.
|
|
|
|
### replay -- Run a scenario against the API
|
|
|
|
```bash
|
|
dotnet run --project VigilCare.Simulator -- replay <scenario-file> [options]
|
|
```
|
|
|
|
**Options:**
|
|
|
|
| Option | Default | Description |
|
|
|--------|---------|-------------|
|
|
| `--speed <number>` | 60 | How fast to run (see [Speed](#6-controlling-replay-speed)) |
|
|
| `--base-url <url>` | `http://localhost:5270` | API address (change if your API runs elsewhere) |
|
|
| `--poll` | off | Show alerts and scores after each set of vitals |
|
|
| `--poll-interval <seconds>` | 5 | How often to check for alerts when polling |
|
|
| `--username <name>` | `physician.demo` | API login username |
|
|
| `--password <secret>` | (demo default) | API login password |
|
|
| `--gateway` | off | Target the ward gateway API at `http://localhost:5081` |
|
|
| `--encounter-id <guid>` | — | Use an existing encounter (required with `--gateway`; also used with `--skip-setup`) |
|
|
| `--skip-setup` | off | Skip patient/encounter registration — requires `--encounter-id` |
|
|
| `--gateway-token <jwt>` | `$GATEWAY_JWT` | Bearer token for gateway replay (required with `--gateway`; use `./scripts/mint-gateway-jwt.sh`) |
|
|
|
|
**Example -- run the stable baseline scenario in real-time with polling:**
|
|
|
|
```bash
|
|
dotnet run --project VigilCare.Simulator -- replay \
|
|
VigilCare.Simulator/Scenarios/List/stable-baseline-01.json \
|
|
--speed 1 --poll
|
|
```
|
|
|
|
### replay-all -- Run every scenario in a folder
|
|
|
|
```bash
|
|
dotnet run --project VigilCare.Simulator -- replay-all <directory> [options]
|
|
```
|
|
|
|
Runs all `.json` scenario files in the given directory, one after another. Accepts `--speed` and `--base-url`.
|
|
|
|
**Example:**
|
|
|
|
```bash
|
|
dotnet run --project VigilCare.Simulator -- replay-all \
|
|
VigilCare.Simulator/Scenarios/List --speed 60
|
|
```
|
|
|
|
### validate -- Check a scenario file for errors
|
|
|
|
```bash
|
|
dotnet run --project VigilCare.Simulator -- validate <scenario-file>
|
|
```
|
|
|
|
Checks that the JSON is well-formed and all fields are valid. Does **not** contact the API.
|
|
|
|
**Example:**
|
|
|
|
```bash
|
|
dotnet run --project VigilCare.Simulator -- validate \
|
|
VigilCare.Simulator/Scenarios/List/stable-baseline-01.json
|
|
```
|
|
|
|
### dry-run -- Preview the timeline without touching the API
|
|
|
|
```bash
|
|
dotnet run --project VigilCare.Simulator -- dry-run <scenario-file>
|
|
```
|
|
|
|
Prints exactly what *would* happen (every vital sign, medication, and order) without sending anything.
|
|
|
|
**Example:**
|
|
|
|
```bash
|
|
dotnet run --project VigilCare.Simulator -- dry-run \
|
|
VigilCare.Simulator/Scenarios/List/cardiac-arrest-post-mi-01.json
|
|
```
|
|
|
|
---
|
|
|
|
## 4. Understanding Scenarios
|
|
|
|
Each scenario is a JSON file that tells a clinical story. It contains:
|
|
|
|
- **Patient** -- Name, date of birth, gender
|
|
- **Encounter** -- Department, encounter type, attending physician, room/bed
|
|
- **Events** -- A timeline of observations (vitals, labs), medications, and orders
|
|
|
|
Here is what a simplified scenario looks like:
|
|
|
|
```json
|
|
{
|
|
"scenario": {
|
|
"id": "stable-baseline-01",
|
|
"name": "Stable Baseline -- Routine Inpatient Monitoring",
|
|
"description": "52-year-old female admitted for elective cholecystectomy...",
|
|
"durationMinutes": 480,
|
|
"tags": ["stable", "baseline", "control", "surgery"]
|
|
},
|
|
"patient": {
|
|
"firstName": "Linda",
|
|
"lastName": "Weston",
|
|
"dateOfBirth": "1974-02-18",
|
|
"gender": "Female"
|
|
},
|
|
"encounter": {
|
|
"department": "Surgery",
|
|
"encounterType": "Inpatient",
|
|
"attendingPhysician": "Dr. James Nakamura",
|
|
"roomBed": "SURG-204B",
|
|
"admissionReason": "Elective laparoscopic cholecystectomy"
|
|
},
|
|
"events": [
|
|
{
|
|
"offsetMinutes": 0,
|
|
"type": "observation",
|
|
"data": {
|
|
"code": "HEART_RATE",
|
|
"value": 72,
|
|
"unit": "bpm"
|
|
},
|
|
"note": "Post-op arrival, patient alert and comfortable"
|
|
},
|
|
{
|
|
"offsetMinutes": 60,
|
|
"type": "medication",
|
|
"data": {
|
|
"drugName": "acetaminophen",
|
|
"dose": 1000,
|
|
"doseUnit": "mg",
|
|
"route": "PO",
|
|
"administeredBy": "RN Davis"
|
|
}
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
The key concept is **offsetMinutes** -- each event happens at a certain number of minutes after the scenario starts. The simulator waits the appropriate amount of time (adjusted by your speed setting) before sending each event.
|
|
|
|
### Event Types
|
|
|
|
| Type | What It Represents | Example |
|
|
|------|--------------------|---------|
|
|
| `observation` | A vital sign or lab result | Heart rate 110 bpm, Temperature 39.2 C |
|
|
| `medication` | A drug being administered | Ceftriaxone 1g IV |
|
|
| `order` | A clinical order being placed | "Blood cultures", "Chest X-ray" |
|
|
| `order_result` | Result of a prior order | "Positive for E. coli" |
|
|
| `alert_ack` | Acknowledgement of an open alert | RN acknowledges critical potassium alert |
|
|
|
|
`alert_ack` events require `alertType` and `clinicianId` in `data`; optional `note`. On **gateway** replay (`--gateway`), the simulator sends `clinicianId` in the acknowledge request so the ward records the bedside nurse label (e.g. `RN-Wu`) and syncs it to central. On **central** replay, attribution comes from the logged-in API user.
|
|
|
|
### Vital Sign Codes
|
|
|
|
These are the observation codes used in scenarios:
|
|
|
|
| Code | What It Measures | Unit | Normal Range |
|
|
|------|-----------------|------|-------------|
|
|
| `HEART_RATE` | Heart rate | bpm | 51--90 |
|
|
| `RESP_RATE` | Respiratory rate | /min | 12--20 |
|
|
| `SYSTOLIC_BP` | Systolic blood pressure | mmHg | 111--219 |
|
|
| `DIASTOLIC_BP` | Diastolic blood pressure | mmHg | 60--90 |
|
|
| `TEMP_C` | Temperature | C | 36.1--38.0 |
|
|
| `SPO2` | Oxygen saturation | % | 96--100 |
|
|
| `AVPU` | Consciousness level | score | 0 = Alert |
|
|
| `SUPPLEMENTAL_O2` | On supplemental oxygen? | flag | 0 = No |
|
|
| `WBC_K_UL` | White blood cell count | x10^3/uL | 4.5--11.0 |
|
|
| `LACTATE_MMOL_L` | Serum lactate | mmol/L | 0.5--1.5 |
|
|
| `POTASSIUM_MEQ_L` | Potassium | mEq/L | 3.5--5.0 |
|
|
| `GLUCOSE_MG_DL` | Blood glucose | mg/dL | 70--140 |
|
|
|
|
---
|
|
|
|
## 5. Available Scenarios
|
|
|
|
The simulator ships with 8 scenarios covering different clinical situations:
|
|
|
|
| Scenario | Clinical Story | Duration |
|
|
|----------|---------------|----------|
|
|
| **stable-baseline-01** | Post-op cholecystectomy, all vitals normal. Control case -- no alerts should fire. | 8 hours |
|
|
| **uti-sepsis-elderly-01** | 78-year-old with UTI progressing to sepsis. SIRS criteria met, sepsis bundle triggered. | 4 hours |
|
|
| **cardiac-arrest-post-mi-01** | Post-MI patient deteriorating into cardiogenic shock. Rapid HR/BP changes. | Varies |
|
|
| **post-op-hemorrhage-01** | Surgical patient with internal bleeding. Rising HR, falling BP and SpO2. | Varies |
|
|
| **respiratory-failure-asthma-01** | Asthma exacerbation progressing to respiratory failure. Falling SpO2, rising RR. | Varies |
|
|
| **dka-electrolyte-01** | Diabetic ketoacidosis with potassium and glucose derangement. | Varies |
|
|
| **hypothermia-elderly-01** | Elderly patient with severe hypothermia. Slow HR, dropping temperature. | Varies |
|
|
| **medication-false-alarm-01** | Beta-blocker causing bradycardia. Tests whether the system correctly handles medication-induced vital changes. | 3 hours |
|
|
| **ward-outage-reconnect-01** | ICU patient with critical hyperkalemia during simulated central outage. Validates gateway-local alerting and alert acknowledgement sync. | 90 minutes |
|
|
|
|
All scenario files are in: `VigilCare.Simulator/Scenarios/List/`
|
|
|
|
---
|
|
|
|
## 6. Controlling Replay Speed
|
|
|
|
The `--speed` option controls how fast simulated time passes:
|
|
|
|
| Speed | Meaning | A 4-hour scenario takes... |
|
|
|-------|---------|---------------------------|
|
|
| `0` | Instant -- no waiting, all events fire immediately | < 1 second |
|
|
| `1` | Real-time -- 1 simulated minute = 1 real minute | 4 hours |
|
|
| `10` | 10x -- 1 simulated minute = 6 real seconds | 24 minutes |
|
|
| `60` | 60x (default) -- 1 simulated minute = 1 real second | 4 minutes |
|
|
| `120` | 120x -- 1 simulated minute = 0.5 real seconds | 2 minutes |
|
|
|
|
**Recommendations:**
|
|
- **For demos / presentations:** Use `--speed 10` with `--poll` so people can follow along
|
|
- **For quick testing:** Use `--speed 60` or `--speed 0`
|
|
- **For the most realistic experience:** Use `--speed 1` (real-time, a 4-hour scenario takes 4 hours)
|
|
|
|
---
|
|
|
|
## 7. Reading the Output
|
|
|
|
When you run a `replay`, the simulator prints a colored timeline. Here's what to look for:
|
|
|
|
```
|
|
────────────────── Stable Baseline -- Routine Inpatient Monitoring ──────────────────
|
|
52-year-old female admitted for elective cholecystectomy...
|
|
|
|
Patient registered: 3fa85f64-... (MRN-12345)
|
|
Encounter opened: 7c9e6679-... (active)
|
|
|
|
[00:00] HEART_RATE 72 bpm
|
|
[00:00] RESP_RATE 14 /min
|
|
[00:00] SYSTOLIC_BP 124 mmHg
|
|
[00:00] SPO2 98 %
|
|
|
|
... waiting 60m simulated (1.0s real) ...
|
|
|
|
[01:00] HEART_RATE 74 bpm
|
|
[01:00] medication: acetaminophen 1000 mg PO
|
|
```
|
|
|
|
**With `--poll` enabled, you also see clinical scoring:**
|
|
|
|
```
|
|
[01:30] NEWS2 = 5 (Medium)
|
|
[01:30] ALERT SEPSIS_WARNING (Critical)
|
|
[01:30] SEPSIS BUNDLE InProgress (2/4 completed)
|
|
```
|
|
|
|
**At the end, a summary table appears:**
|
|
|
|
```
|
|
Metric Value
|
|
-----------------------------------
|
|
Scenario uti-sepsis-elderly-01
|
|
Observations sent 42
|
|
Medications sent 2
|
|
Orders placed 3
|
|
Wall-clock time 12.3s
|
|
```
|
|
|
|
### What the Alerts Mean
|
|
|
|
| Alert | Meaning |
|
|
|-------|---------|
|
|
| `NEWS2_LOW` | NEWS2 score 1--4: Low risk, routine monitoring |
|
|
| `NEWS2_MEDIUM` | NEWS2 score 5--6 or single parameter score of 3: Urgent review needed |
|
|
| `NEWS2_HIGH` | NEWS2 score 7+: Emergency response needed |
|
|
| `SEPSIS_WARNING` | SIRS criteria met (2+ of: temp, HR, RR, WBC abnormal) |
|
|
| `QSOFA_WARNING` | qSOFA criteria met (2+ of: altered mentation, RR >= 22, SBP <= 100) |
|
|
| `RAPID_DETERIORATION` | Sudden significant change in vital signs |
|
|
|
|
---
|
|
|
|
## 8. Creating Your Own Scenarios
|
|
|
|
You can create new clinical scenarios by writing a JSON file. There are two ways:
|
|
|
|
### Option A: Write it manually
|
|
|
|
1. Copy an existing scenario from `VigilCare.Simulator/Scenarios/List/` as a template
|
|
2. Modify the patient, encounter, and events to match your clinical story
|
|
3. Make sure `offsetMinutes` values are in ascending order
|
|
4. Validate it:
|
|
|
|
```bash
|
|
dotnet run --project VigilCare.Simulator -- validate ./my-scenario.json
|
|
```
|
|
|
|
### Option B: Use AI to generate it
|
|
|
|
There is a generation prompt file at `VigilCare.Simulator/Scenarios/GENERATE_PROMPT.md`. You can paste its contents into ChatGPT, Claude, or another AI assistant, describe the clinical case you want, and it will generate a valid scenario JSON file for you.
|
|
|
|
### Validation Rules
|
|
|
|
The simulator enforces these rules on scenario files:
|
|
|
|
- **scenario.id** and **scenario.name** are required
|
|
- **encounter.department** must be one of: `Icu`, `GeneralMedicine`, `Emergency`, `Cardiology`, `Surgery`, `Pediatrics`
|
|
- **encounter.encounterType** must be one of: `Inpatient`, `Outpatient`, `Emergency`
|
|
- **events** must be non-empty, with `offsetMinutes` in ascending order
|
|
- **observation codes** must match one of the 12 valid codes listed above
|
|
- Maximum **10 observations per time point** (API batch limit)
|
|
- **order_result** events must reference a matching prior order (or a sepsis bundle auto-order)
|
|
|
|
### Sepsis Bundle Auto-Orders
|
|
|
|
When the system detects sepsis (SIRS or qSOFA criteria met), it automatically creates four orders:
|
|
- `SEP-1: Blood cultures`
|
|
- `SEP-1: Serum lactate`
|
|
- `SEP-1: Broad-spectrum antibiotics`
|
|
- `SEP-1: IV fluid bolus`
|
|
|
|
You do **not** need to create `order` events for these. Just add `order_result` events referencing them to simulate bundle completion.
|
|
|
|
---
|
|
|
|
## 9. Troubleshooting
|
|
|
|
### "Connection refused" error
|
|
|
|
The API is not running. Start it first:
|
|
|
|
```bash
|
|
docker compose up -d
|
|
dotnet run --project VigilCare.Api
|
|
```
|
|
|
|
### "scenario.json has N error(s)"
|
|
|
|
Run `validate` to see what's wrong:
|
|
|
|
```bash
|
|
dotnet run --project VigilCare.Simulator -- validate ./your-scenario.json
|
|
```
|
|
|
|
Common issues:
|
|
- Invalid department name (must match exact casing: `Icu`, not `ICU`)
|
|
- `offsetMinutes` out of order
|
|
- Missing required fields (`scenario.id`, `scenario.name`, `encounter.attendingPhysician`)
|
|
|
|
### Medications show "skipped"
|
|
|
|
The medication tracking endpoint may not be available. This is non-fatal -- the scenario continues.
|
|
|
|
### "dotnet" command not found
|
|
|
|
Install .NET 8 SDK from [dotnet.microsoft.com](https://dotnet.microsoft.com/download/dotnet/8.0).
|
|
|
|
### I want to point the simulator at a different API
|
|
|
|
Use `--base-url`:
|
|
|
|
```bash
|
|
dotnet run --project VigilCare.Simulator -- replay scenario.json \
|
|
--base-url http://192.168.1.50:5270
|
|
```
|
|
|
|
---
|
|
|
|
## 10. Ward Outage Scenario (Climate Resilience)
|
|
|
|
The `ward-outage-reconnect-01` scenario validates Tier 1 safety during a central API outage. Observations and alerts continue on the ward gateway; acknowledgements are recorded locally and synced when the uplink returns.
|
|
|
|
### Automated verification (recommended)
|
|
|
|
From the repo root, with Docker running:
|
|
|
|
```bash
|
|
./scripts/run-phase24-verification.sh
|
|
```
|
|
|
|
This script runs all three phases automatically: central baseline replay, gateway replay during simulated central outage, and post-reconnect sync checks. Logs go to `/tmp/vigilcare-phase24-*`.
|
|
|
|
Useful flags:
|
|
|
|
| Env var | Effect |
|
|
|---------|--------|
|
|
| `SKIP_DOCKER=1` | Assume `docker compose` stack is already up |
|
|
| `SKIP_PHASE_A=1` | Skip central replay; use an existing ICU encounter on the gateway |
|
|
|
|
Helper for manual gateway API calls (ward gateway has no login endpoint):
|
|
|
|
```bash
|
|
export GATEWAY_JWT=$(./scripts/mint-gateway-jwt.sh nurse.demo NURSE)
|
|
```
|
|
|
|
### Manual procedure
|
|
|
|
1. Full stack running with the ward gateway profile:
|
|
|
|
```bash
|
|
docker compose --profile ward-gateway up -d
|
|
```
|
|
|
|
2. Central API running and the gateway encounter replica synced
|
|
3. Note an active ICU encounter id from the gateway:
|
|
|
|
```bash
|
|
curl "http://localhost:5081/api/v1/encounters?status=ACTIVE&department=ICU" \
|
|
-H "Authorization: Bearer $JWT"
|
|
```
|
|
|
|
### Procedure
|
|
|
|
**Phase A — Baseline on central (optional):**
|
|
|
|
```bash
|
|
dotnet run --project VigilCare.Simulator -- replay \
|
|
VigilCare.Simulator/Scenarios/List/ward-outage-reconnect-01.json \
|
|
--speed 0 --base-url http://localhost:5270
|
|
```
|
|
|
|
**Phase B — Stop central, replay against gateway:**
|
|
|
|
```bash
|
|
# Stop central API process/container
|
|
dotnet run --project VigilCare.Simulator -- replay \
|
|
VigilCare.Simulator/Scenarios/List/ward-outage-reconnect-01.json \
|
|
--gateway --encounter-id <ENCOUNTER-GUID> --speed 60 --poll
|
|
```
|
|
|
|
The `--gateway` flag targets `http://localhost:5081` automatically. `--encounter-id` must reference an encounter already replicated on the gateway. The `alert_ack` event at T+50 min sends `clinicianId: "RN-Wu"` from the scenario; the gateway honors this in `acknowledged_by` and in the sync buffer (login user is only used when `clinicianId` is omitted).
|
|
|
|
**Phase A note:** Central replay attributes acks to the JWT user (`physician.demo` by default). Use `--username nurse.demo` if you want a nurse role on central; gateway Phase B is the authoritative climate-resilience path for scenario attribution.
|
|
|
|
**Phase C — Restart central, verify sync:**
|
|
|
|
- Wait for `SyncUploaderService` to drain the buffer
|
|
- Poll `GET /api/v1/operations/gateways` — buffer depth should reach 0
|
|
- Confirm observations on central with preserved `recorded_at` timestamps
|
|
|
|
### Success criteria
|
|
|
|
- Critical potassium alert created on gateway at T+45 min while central is down
|
|
- Ack recorded locally at T+50 min with `acknowledged_by` = `RN-Wu`
|
|
- After reconnect: central has observations, alert, and ack; no duplicate paging logs
|