# 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)
11. [MIMIC-IV Real Patient Data](#11-mimic-iv-real-patient-data)

---

## 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 six 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
```

### mimic-list -- Browse available MIMIC-IV patients and ICU stays

```bash
dotnet run --project VigilCare.Simulator -- mimic-list <mimic-data-dir> [options]
```

Displays a table of all ICU stays in the MIMIC-IV dataset with patient demographics, care unit, length of stay, and outcome. Does **not** contact the API.

**Options:**

| Option | Description |
|--------|-------------|
| `--subject-id <int>` | Filter to a specific patient |
| `--stay-id <int>` | Filter to a specific ICU stay |

**Example:**

```bash
dotnet run --project VigilCare.Simulator -- mimic-list docs/MIMIC-IV/
```

### mimic-generate -- Generate a scenario from real MIMIC-IV data

```bash
dotnet run --project VigilCare.Simulator -- mimic-generate <mimic-data-dir> --stay-id <int> [options]
```

Reads MIMIC-IV CSV files and generates a VigilCare scenario JSON from a specific ICU stay. The generated file is fully compatible with `replay`, `validate`, and `dry-run`. Does **not** contact the API.

**Options:**

| Option | Default | Description |
|--------|---------|-------------|
| `--stay-id <int>` | (required) | ICU stay ID to generate scenario for |
| `--max-hours <int>` | full stay | Limit scenario duration (real ICU stays can be days/weeks) |
| `--no-medications` | off | Exclude medication events |
| `--no-labs` | off | Exclude lab observations |
| `--output <path>` | `Scenarios/List/mimic-s{stayId}.json` | Custom output file path |
| `--validate` | off | Run scenario validation after generation |

**Example -- generate a 24-hour CVICU scenario:**

```bash
dotnet run --project VigilCare.Simulator -- mimic-generate docs/MIMIC-IV/ \
  --stay-id 32604416 --max-hours 24 --validate
```

Then replay it:

```bash
dotnet run --project VigilCare.Simulator -- replay \
  VigilCare.Simulator/Scenarios/List/mimic-s32604416.json --speed 0 --poll
```

See [MIMIC-IV Real Patient Data](#11-mimic-iv-real-patient-data) for the full guide.

---

## 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

---

## 11. MIMIC-IV Real Patient Data

The simulator can generate scenarios from **real de-identified ICU data** from MIT's MIMIC-IV dataset (PhysioNet). This bridges the gap from synthetic scenarios to actual clinical records — a critical validation step for scoring engines and alert logic.

The MIMIC-IV CSV files in `docs/MIMIC-IV/` contain 100 patients, 140 ICU stays, 668K chart events, and 107K lab events. The generator reads these files and produces standard scenario JSON files that replay through VigilCare's full scoring pipeline (NEWS2, SOFA, GCS, qSOFA, trend detection, alerting).

### Quick start

```bash
# 1. Browse available ICU stays
dotnet run --project VigilCare.Simulator -- mimic-list docs/MIMIC-IV/

# 2. Generate a 24-hour scenario from a CVICU patient
dotnet run --project VigilCare.Simulator -- mimic-generate docs/MIMIC-IV/ \
  --stay-id 32604416 --max-hours 24 --validate

# 3. Preview the timeline
dotnet run --project VigilCare.Simulator -- dry-run \
  VigilCare.Simulator/Scenarios/List/mimic-s32604416.json

# 4. Replay against the live API
dotnet run --project VigilCare.Simulator -- replay \
  VigilCare.Simulator/Scenarios/List/mimic-s32604416.json --speed 0 --poll
```

### What gets generated

The generator maps MIMIC-IV data to VigilCare observations:

| MIMIC Source | VigilCare Codes |
|---|---|
| **Vital signs** (chartevents) | HEART_RATE, RESP_RATE, SYSTOLIC_BP, DIASTOLIC_BP, TEMP_C, SPO2, FIO2_PCT |
| **GCS** (chartevents, text labels) | GCS_EYE, GCS_VERBAL, GCS_MOTOR |
| **Labs** (labevents) | CREATININE_MG_DL, PLATELET_K_UL, BILIRUBIN_MG_DL, LACTATE_MMOL_L, WBC_K_UL, POTASSIUM_MEQ_L, GLUCOSE_MG_DL, PAO2_MMHG |
| **Prescriptions** | Medication events (drug name, dose, route) |

The generated scenario has no `expectedOutcomes` — these are exploratory replays. The alerts and scores that fire are the real output, driven by actual patient trajectories.

### Useful options

| Option | Effect |
|--------|--------|
| `--max-hours 24` | Cap the scenario at 24 hours (real ICU stays can be weeks) |
| `--no-medications` | Exclude medication events for a cleaner vitals-only replay |
| `--no-labs` | Exclude lab observations (vitals + GCS only) |
| `--validate` | Run `ScenarioValidator` after generation and print results |
| `--output my-file.json` | Write to a custom path instead of `Scenarios/List/` |

### Data handling notes

- **GCS:** MIMIC stores GCS as text labels ("Obeys Commands", "To Speech", etc.). The generator reads the numeric `valuenum` column; if missing, falls back to a text-to-numeric lookup dictionary.
- **Temperature:** Fahrenheit readings (item 223761) are converted to Celsius. Celsius readings (item 223762) pass through.
- **Blood pressure:** When both non-invasive (NBP) and arterial (ABP) readings exist at the same time, the generator keeps non-invasive and discards arterial. ABP is used only when no NBP is available.
- **Cluster limits:** The VigilCare API accepts at most 10 observations per batch. When a MIMIC timestamp has more than 10 readings, the generator keeps vital signs first and spills labs to the next minute offset.
- **Patient identity:** De-identified names (`MIMIC-{subjectId}` / `S{hadmId}`). Date of birth approximated from `anchor_age` and `anchor_year`.

### Finding interesting patients

Use `mimic-list` with filters to find specific cases:

```bash
# Find all stays for a specific patient
dotnet run --project VigilCare.Simulator -- mimic-list docs/MIMIC-IV/ --subject-id 10005817

# Look for a specific stay
dotnet run --project VigilCare.Simulator -- mimic-list docs/MIMIC-IV/ --stay-id 32604416
```

The table shows care unit, length of stay, and whether the patient expired -- useful for finding clinically interesting trajectories to replay.
