iints-sdk-python35 0.0.18__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- iints/__init__.py +183 -0
- iints/analysis/__init__.py +12 -0
- iints/analysis/algorithm_xray.py +387 -0
- iints/analysis/baseline.py +92 -0
- iints/analysis/clinical_benchmark.py +198 -0
- iints/analysis/clinical_metrics.py +551 -0
- iints/analysis/clinical_tir_analyzer.py +136 -0
- iints/analysis/diabetes_metrics.py +43 -0
- iints/analysis/edge_efficiency.py +33 -0
- iints/analysis/edge_performance_monitor.py +315 -0
- iints/analysis/explainability.py +94 -0
- iints/analysis/explainable_ai.py +232 -0
- iints/analysis/hardware_benchmark.py +221 -0
- iints/analysis/metrics.py +117 -0
- iints/analysis/population_report.py +188 -0
- iints/analysis/reporting.py +345 -0
- iints/analysis/safety_index.py +311 -0
- iints/analysis/sensor_filtering.py +54 -0
- iints/analysis/validator.py +273 -0
- iints/api/__init__.py +0 -0
- iints/api/base_algorithm.py +307 -0
- iints/api/registry.py +103 -0
- iints/api/template_algorithm.py +195 -0
- iints/assets/iints_logo.png +0 -0
- iints/cli/__init__.py +0 -0
- iints/cli/cli.py +2598 -0
- iints/core/__init__.py +1 -0
- iints/core/algorithms/__init__.py +0 -0
- iints/core/algorithms/battle_runner.py +138 -0
- iints/core/algorithms/correction_bolus.py +95 -0
- iints/core/algorithms/discovery.py +92 -0
- iints/core/algorithms/fixed_basal_bolus.py +58 -0
- iints/core/algorithms/hybrid_algorithm.py +92 -0
- iints/core/algorithms/lstm_algorithm.py +138 -0
- iints/core/algorithms/mock_algorithms.py +162 -0
- iints/core/algorithms/pid_controller.py +88 -0
- iints/core/algorithms/standard_pump_algo.py +64 -0
- iints/core/device.py +0 -0
- iints/core/device_manager.py +64 -0
- iints/core/devices/__init__.py +3 -0
- iints/core/devices/models.py +160 -0
- iints/core/patient/__init__.py +9 -0
- iints/core/patient/bergman_model.py +341 -0
- iints/core/patient/models.py +285 -0
- iints/core/patient/patient_factory.py +117 -0
- iints/core/patient/profile.py +41 -0
- iints/core/safety/__init__.py +12 -0
- iints/core/safety/config.py +37 -0
- iints/core/safety/input_validator.py +95 -0
- iints/core/safety/supervisor.py +39 -0
- iints/core/simulation/__init__.py +0 -0
- iints/core/simulation/scenario_parser.py +61 -0
- iints/core/simulator.py +874 -0
- iints/core/supervisor.py +367 -0
- iints/data/__init__.py +53 -0
- iints/data/adapter.py +142 -0
- iints/data/column_mapper.py +398 -0
- iints/data/datasets.json +132 -0
- iints/data/demo/__init__.py +1 -0
- iints/data/demo/demo_cgm.csv +289 -0
- iints/data/importer.py +275 -0
- iints/data/ingestor.py +162 -0
- iints/data/nightscout.py +128 -0
- iints/data/quality_checker.py +550 -0
- iints/data/registry.py +166 -0
- iints/data/tidepool.py +38 -0
- iints/data/universal_parser.py +813 -0
- iints/data/virtual_patients/clinic_safe_baseline.yaml +9 -0
- iints/data/virtual_patients/clinic_safe_hyper_challenge.yaml +9 -0
- iints/data/virtual_patients/clinic_safe_hypo_prone.yaml +9 -0
- iints/data/virtual_patients/clinic_safe_midnight.yaml +9 -0
- iints/data/virtual_patients/clinic_safe_pizza.yaml +9 -0
- iints/data/virtual_patients/clinic_safe_stress_meal.yaml +9 -0
- iints/data/virtual_patients/default_patient.yaml +11 -0
- iints/data/virtual_patients/patient_559_config.yaml +11 -0
- iints/emulation/__init__.py +80 -0
- iints/emulation/legacy_base.py +414 -0
- iints/emulation/medtronic_780g.py +337 -0
- iints/emulation/omnipod_5.py +367 -0
- iints/emulation/tandem_controliq.py +393 -0
- iints/highlevel.py +451 -0
- iints/learning/__init__.py +3 -0
- iints/learning/autonomous_optimizer.py +194 -0
- iints/learning/learning_system.py +122 -0
- iints/metrics.py +34 -0
- iints/population/__init__.py +11 -0
- iints/population/generator.py +131 -0
- iints/population/runner.py +327 -0
- iints/presets/__init__.py +28 -0
- iints/presets/presets.json +114 -0
- iints/research/__init__.py +30 -0
- iints/research/config.py +68 -0
- iints/research/dataset.py +319 -0
- iints/research/losses.py +73 -0
- iints/research/predictor.py +329 -0
- iints/scenarios/__init__.py +3 -0
- iints/scenarios/generator.py +92 -0
- iints/templates/__init__.py +0 -0
- iints/templates/default_algorithm.py +91 -0
- iints/templates/scenarios/__init__.py +0 -0
- iints/templates/scenarios/chaos_insulin_stacking.json +29 -0
- iints/templates/scenarios/chaos_runaway_ai.json +25 -0
- iints/templates/scenarios/example_scenario.json +35 -0
- iints/templates/scenarios/exercise_stress.json +30 -0
- iints/utils/__init__.py +3 -0
- iints/utils/plotting.py +50 -0
- iints/utils/run_io.py +152 -0
- iints/validation/__init__.py +133 -0
- iints/validation/schemas.py +94 -0
- iints/visualization/__init__.py +34 -0
- iints/visualization/cockpit.py +691 -0
- iints/visualization/uncertainty_cloud.py +612 -0
- iints_sdk_python35-0.0.18.dist-info/METADATA +225 -0
- iints_sdk_python35-0.0.18.dist-info/RECORD +118 -0
- iints_sdk_python35-0.0.18.dist-info/WHEEL +5 -0
- iints_sdk_python35-0.0.18.dist-info/entry_points.txt +10 -0
- iints_sdk_python35-0.0.18.dist-info/licenses/LICENSE +28 -0
- iints_sdk_python35-0.0.18.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Medtronic 780G Emulator - IINTS-AF
|
|
4
|
+
Emulates the Medtronic MiniMed 780G with SmartGuard algorithm.
|
|
5
|
+
|
|
6
|
+
Based on:
|
|
7
|
+
- FDA 510(k) clearance documentation
|
|
8
|
+
- Clinical studies (Bergenstal et al.)
|
|
9
|
+
- User manual and technical specifications
|
|
10
|
+
|
|
11
|
+
Part of the #WeAreNotWaiting movement for transparent diabetes tech.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import numpy as np
|
|
15
|
+
from typing import Dict, List, Optional, Any
|
|
16
|
+
from dataclasses import dataclass
|
|
17
|
+
|
|
18
|
+
from .legacy_base import (
|
|
19
|
+
LegacyEmulator, PumpBehavior, PIDParameters, SafetyLimits,
|
|
20
|
+
SafetyLevel, EmulatorDecision
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class Medtronic780GBehavior(PumpBehavior):
|
|
26
|
+
"""Medtronic 780G specific behavior profile"""
|
|
27
|
+
# Additional 780G-specific parameters
|
|
28
|
+
auto_basal_enabled: bool = True
|
|
29
|
+
target_range_low: float = 100.0 # mg/dL (configurable 100-120)
|
|
30
|
+
target_range_high: float = 120.0 # mg/dL
|
|
31
|
+
max_auto_basal: float = 6.0 # units/hour
|
|
32
|
+
predictive_low_suspend: bool = True
|
|
33
|
+
plsg_window: int = 30 # minutes
|
|
34
|
+
plsg_threshold: float = 70.0 # mg/dL
|
|
35
|
+
time_step_minutes: float = 5.0 # Explicitly define the decision interval
|
|
36
|
+
|
|
37
|
+
def __init__(self):
|
|
38
|
+
super().__init__(
|
|
39
|
+
pump_name="Medtronic MiniMed 780G",
|
|
40
|
+
manufacturer="Medtronic",
|
|
41
|
+
safety_limits=SafetyLimits(
|
|
42
|
+
low_suspend_threshold=70.0,
|
|
43
|
+
high_suspend_threshold=250.0,
|
|
44
|
+
max_bolus=10.0,
|
|
45
|
+
max_basal_rate=6.0,
|
|
46
|
+
max_daily_total=80.0,
|
|
47
|
+
auto_off_duration=60.0,
|
|
48
|
+
low_suspend_duration=5.0,
|
|
49
|
+
target_glucose=120.0
|
|
50
|
+
),
|
|
51
|
+
pid_parameters=PIDParameters(
|
|
52
|
+
kp=0.02, # Proportional gain (tuned for 780G)
|
|
53
|
+
ki=0.005, # Integral gain
|
|
54
|
+
kd=0.03, # Derivative gain
|
|
55
|
+
target_glucose=120.0,
|
|
56
|
+
integral_limit=5.0
|
|
57
|
+
),
|
|
58
|
+
correction_factor=50.0,
|
|
59
|
+
carb_ratio=10.0,
|
|
60
|
+
insulin_sensitivity_factor=50.0,
|
|
61
|
+
active_insulin_duration=4.0,
|
|
62
|
+
safety_level=SafetyLevel.CONSERVATIVE
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class Medtronic780GEmulator(LegacyEmulator):
|
|
67
|
+
"""
|
|
68
|
+
Emulates Medtronic 780G SmartGuard algorithm.
|
|
69
|
+
|
|
70
|
+
Based on Medtronic 780G Clinical User Guide, Section 4.2 (SmartGuard).
|
|
71
|
+
|
|
72
|
+
The 780G uses a hybrid closed-loop system with:
|
|
73
|
+
- Automatic basal delivery (auto-basal)
|
|
74
|
+
- Automatic correction boluses
|
|
75
|
+
- Predictive Low Glucose Suspend (PLGS)
|
|
76
|
+
- Target glucose range (configurable 100-120 mg/dL)
|
|
77
|
+
|
|
78
|
+
Key characteristics:
|
|
79
|
+
- Conservative approach to avoid hypoglycemia
|
|
80
|
+
- Frequent insulin delivery (every 5 minutes)
|
|
81
|
+
- Automatic correction for elevated glucose
|
|
82
|
+
- Meal detection triggers additional insulin
|
|
83
|
+
|
|
84
|
+
Sources:
|
|
85
|
+
- Bergenstal et al. "New Closed-Loop Insulin Delivery System"
|
|
86
|
+
- FDA 510(k) K193510
|
|
87
|
+
- Medtronic 780G User Guide
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
def __init__(self):
|
|
91
|
+
"""Initialize the 780G emulator"""
|
|
92
|
+
super().__init__()
|
|
93
|
+
self.behavior = Medtronic780GBehavior()
|
|
94
|
+
|
|
95
|
+
def _get_default_behavior(self) -> Medtronic780GBehavior:
|
|
96
|
+
"""Get 780G default behavior"""
|
|
97
|
+
return Medtronic780GBehavior()
|
|
98
|
+
|
|
99
|
+
def get_sources(self) -> List[Dict[str, str]]:
|
|
100
|
+
"""Get sources for 780G emulation logic"""
|
|
101
|
+
return [
|
|
102
|
+
{
|
|
103
|
+
'title': 'Bergenstal et al. - Hybrid Closed-Loop Therapy',
|
|
104
|
+
'type': 'clinical_study',
|
|
105
|
+
'year': '2020',
|
|
106
|
+
'url': 'https://doi.org/10.1056/NEJMoa2003479'
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
'title': 'FDA 510(k) K193510 - MiniMed 780G System',
|
|
110
|
+
'type': 'regulatory',
|
|
111
|
+
'year': '2020',
|
|
112
|
+
'url': 'https://www.accessdata.fda.gov/'
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
'title': 'Medtronic 780G User Guide',
|
|
116
|
+
'type': 'technical_manual',
|
|
117
|
+
'year': '2020',
|
|
118
|
+
'url': 'https://www.medtronicdiabetes.com/'
|
|
119
|
+
}
|
|
120
|
+
]
|
|
121
|
+
|
|
122
|
+
def emulate_decision(self,
|
|
123
|
+
glucose: float,
|
|
124
|
+
velocity: float,
|
|
125
|
+
insulin_on_board: float,
|
|
126
|
+
carbs: float,
|
|
127
|
+
current_time: float = 0) -> EmulatorDecision:
|
|
128
|
+
"""
|
|
129
|
+
Emulate 780G decision-making for current conditions.
|
|
130
|
+
|
|
131
|
+
The 780G makes decisions every 5 minutes:
|
|
132
|
+
1. Check for automatic correction bolus (micro-bolus)
|
|
133
|
+
2. Check for auto-basal adjustment
|
|
134
|
+
3. Check for PLGS (Predictive Low Glucose Suspend)
|
|
135
|
+
4. Apply safety constraints
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
glucose: Current glucose (mg/dL)
|
|
139
|
+
velocity: Rate of change (mg/dL/min)
|
|
140
|
+
insulin_on_board: Current IOB (units)
|
|
141
|
+
carbs: Carbs consumed (grams)
|
|
142
|
+
current_time: Simulation time (minutes)
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
EmulatorDecision with insulin delivery details
|
|
146
|
+
"""
|
|
147
|
+
reasoning = []
|
|
148
|
+
safety_overrides = []
|
|
149
|
+
action = 'deliver'
|
|
150
|
+
insulin_delivered = 0.0
|
|
151
|
+
pid = self.behavior.pid_parameters
|
|
152
|
+
|
|
153
|
+
# --- Update internal state ---
|
|
154
|
+
time_step_minutes = self.behavior.time_step_minutes
|
|
155
|
+
|
|
156
|
+
if self.state['previous_glucose'] is not None:
|
|
157
|
+
# Re-calculate velocity based on actual time step
|
|
158
|
+
self.state['velocity'] = (glucose - self.state['previous_glucose']) / time_step_minutes
|
|
159
|
+
else:
|
|
160
|
+
self.state['velocity'] = velocity # Use provided velocity if no previous data
|
|
161
|
+
self.state['previous_glucose'] = glucose
|
|
162
|
+
|
|
163
|
+
# --- 1. Initial Safety Check & PLGS (Predictive Low Glucose Suspend) ---
|
|
164
|
+
should_suspend, suspend_reasons, safety_adjustment_factor = self._check_safety_constraints(
|
|
165
|
+
glucose, self.state['velocity'], insulin_on_board, carbs, current_time
|
|
166
|
+
)
|
|
167
|
+
reasoning.extend(suspend_reasons)
|
|
168
|
+
|
|
169
|
+
if self.behavior.predictive_low_suspend and self.state['velocity'] < 0: # Only suspend if glucose is falling
|
|
170
|
+
predicted_glucose = glucose + self.state['velocity'] * self.behavior.plsg_window
|
|
171
|
+
if predicted_glucose < self.behavior.plsg_threshold:
|
|
172
|
+
action = 'suspend_insulin'
|
|
173
|
+
insulin_delivered = 0.0
|
|
174
|
+
safety_overrides.append(
|
|
175
|
+
f"PLGS activated: predicted glucose {predicted_glucose:.0f} mg/dL in "
|
|
176
|
+
f"{self.behavior.plsg_window} min. Insulin suspended."
|
|
177
|
+
)
|
|
178
|
+
reasoning.append(safety_overrides[-1])
|
|
179
|
+
|
|
180
|
+
if action != 'suspend_insulin': # If not suspended by PLGS or initial safety check
|
|
181
|
+
|
|
182
|
+
# --- 2. Calculate PID correction (Auto-Correction Bolus) ---
|
|
183
|
+
error = glucose - pid.target_glucose
|
|
184
|
+
|
|
185
|
+
# Update integral term (cumulative error over time)
|
|
186
|
+
self.state['integral_term'] += error * time_step_minutes
|
|
187
|
+
self.state['integral_term'] = max(-pid.integral_limit,
|
|
188
|
+
min(pid.integral_limit,
|
|
189
|
+
self.state['integral_term']))
|
|
190
|
+
|
|
191
|
+
# Derivative term is based on glucose velocity (trend)
|
|
192
|
+
derivative_term = self.state['velocity']
|
|
193
|
+
|
|
194
|
+
# Calculate total PID output (insulin units per time_step)
|
|
195
|
+
pid_insulin_correction = (
|
|
196
|
+
pid.kp * error +
|
|
197
|
+
pid.ki * self.state['integral_term'] +
|
|
198
|
+
pid.kd * derivative_term
|
|
199
|
+
) / self.behavior.correction_factor
|
|
200
|
+
|
|
201
|
+
# --- 3. Add Meal Bolus ---
|
|
202
|
+
meal_bolus = 0.0
|
|
203
|
+
if carbs > 0:
|
|
204
|
+
meal_bolus = carbs / self.behavior.carb_ratio
|
|
205
|
+
reasoning.append(
|
|
206
|
+
f"Meal detected: {carbs:.0f}g -> {meal_bolus:.2f} U meal bolus"
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
# --- 4. Combine and apply internal limits ---
|
|
210
|
+
total_calculated_insulin = max(0, pid_insulin_correction + meal_bolus)
|
|
211
|
+
|
|
212
|
+
# Apply max bolus limit
|
|
213
|
+
total_calculated_insulin = min(total_calculated_insulin, self.behavior.safety_limits.max_bolus)
|
|
214
|
+
|
|
215
|
+
# Apply individual safety adjustment factor from _check_safety_constraints
|
|
216
|
+
insulin_delivered = total_calculated_insulin * safety_adjustment_factor
|
|
217
|
+
|
|
218
|
+
# The 780G delivers frequent small automatic correction boluses.
|
|
219
|
+
# This is primarily the pid_insulin_correction.
|
|
220
|
+
if insulin_delivered > 0:
|
|
221
|
+
if meal_bolus > 0:
|
|
222
|
+
reasoning.append(f"Insulin delivered for meal and auto-correction: {insulin_delivered:.2f} U")
|
|
223
|
+
elif pid_insulin_correction > 0:
|
|
224
|
+
reasoning.append(f"Auto-correction micro-bolus: {insulin_delivered:.2f} U (PID output based on glucose error and trend).")
|
|
225
|
+
else:
|
|
226
|
+
reasoning.append("No insulin delivered. Glucose at or near target, or PLGS active.")
|
|
227
|
+
|
|
228
|
+
# Create decision
|
|
229
|
+
decision = EmulatorDecision(
|
|
230
|
+
insulin_delivered=insulin_delivered,
|
|
231
|
+
action=action,
|
|
232
|
+
reasoning=reasoning,
|
|
233
|
+
safety_overrides=safety_overrides,
|
|
234
|
+
predicted_glucose=glucose + self.state['velocity'] * 30, # 30-min prediction (simplified)
|
|
235
|
+
confidence=0.90 # Higher confidence for a well-established pump
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
self._decision_history.append(decision)
|
|
239
|
+
|
|
240
|
+
return decision
|
|
241
|
+
|
|
242
|
+
def get_algorithm_personality(self) -> Dict:
|
|
243
|
+
"""
|
|
244
|
+
Get the algorithm's "personality" characteristics.
|
|
245
|
+
|
|
246
|
+
Returns:
|
|
247
|
+
Dictionary with personality traits
|
|
248
|
+
"""
|
|
249
|
+
return {
|
|
250
|
+
'name': 'Medtronic 780G SmartGuard',
|
|
251
|
+
'type': 'Hybrid Closed-Loop',
|
|
252
|
+
'personality': {
|
|
253
|
+
'aggressiveness': 'Conservative',
|
|
254
|
+
'hypo_aversion': 'High',
|
|
255
|
+
'response_speed': 'Moderate',
|
|
256
|
+
'correction_aggressiveness': 'Moderate',
|
|
257
|
+
'meal_handling': 'Automatic correction bolus',
|
|
258
|
+
'predictive_features': 'PLGS, meal detection'
|
|
259
|
+
},
|
|
260
|
+
'key_differences': [
|
|
261
|
+
'Target glucose 100-120 mg/dL (configurable)',
|
|
262
|
+
'Automatic correction boluses every 5 minutes',
|
|
263
|
+
'Conservative approach to avoid hypoglycemia',
|
|
264
|
+
'Strong predictive low glucose suspend'
|
|
265
|
+
],
|
|
266
|
+
'limitations': [
|
|
267
|
+
'Requires meal announcements for best results',
|
|
268
|
+
'Conservative tuning may lead to higher glucose',
|
|
269
|
+
'No exercise mode integration'
|
|
270
|
+
]
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def demo_medtronic_780g():
|
|
275
|
+
"""Demonstrate Medtronic 780G emulator"""
|
|
276
|
+
print("=" * 70)
|
|
277
|
+
print("MEDTRONIC 780G EMULATOR DEMONSTRATION")
|
|
278
|
+
print("=" * 70)
|
|
279
|
+
|
|
280
|
+
emulator = Medtronic780GEmulator()
|
|
281
|
+
|
|
282
|
+
# Print behavior profile
|
|
283
|
+
print("\nAlgorithm Personality:")
|
|
284
|
+
personality = emulator.get_algorithm_personality()
|
|
285
|
+
print(f" Name: {personality['name']}")
|
|
286
|
+
print(f" Type: {personality['type']}")
|
|
287
|
+
print(f" Aggressiveness: {personality['personality']['aggressiveness']}")
|
|
288
|
+
print(f" Hypo Aversion: {personality['personality']['hypo_aversion']}")
|
|
289
|
+
|
|
290
|
+
# Print sources
|
|
291
|
+
print("\nSources:")
|
|
292
|
+
for source in emulator.get_sources():
|
|
293
|
+
print(f" - [{source['type']}] {source['title']} ({source['year']})")
|
|
294
|
+
|
|
295
|
+
# Simulate scenarios
|
|
296
|
+
print("\nScenario Simulation:")
|
|
297
|
+
print("-" * 50)
|
|
298
|
+
|
|
299
|
+
scenarios = [
|
|
300
|
+
{'glucose': 180, 'velocity': 1.0, 'iob': 1.0, 'carbs': 0, 'desc': 'High glucose rising'},
|
|
301
|
+
{'glucose': 120, 'velocity': 0.5, 'iob': 2.0, 'carbs': 0, 'desc': 'At target, moderate IOB'},
|
|
302
|
+
{'glucose': 70, 'velocity': -1.5, 'iob': 1.0, 'carbs': 0, 'desc': 'Falling toward low'},
|
|
303
|
+
{'glucose': 250, 'velocity': 2.0, 'iob': 0.5, 'carbs': 30, 'desc': 'High with meal'},
|
|
304
|
+
{'glucose': 60, 'velocity': -2.5, 'iob': 0.5, 'carbs': 0, 'desc': 'Low with rapid fall'},
|
|
305
|
+
]
|
|
306
|
+
|
|
307
|
+
for i, scenario in enumerate(scenarios, 1):
|
|
308
|
+
decision = emulator.emulate_decision(
|
|
309
|
+
glucose=scenario['glucose'],
|
|
310
|
+
velocity=scenario['velocity'],
|
|
311
|
+
insulin_on_board=scenario['iob'],
|
|
312
|
+
carbs=scenario['carbs'],
|
|
313
|
+
current_time=i * 5
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
print(f"\n Scenario {i}: {scenario['desc']}")
|
|
317
|
+
print(f" Glucose: {scenario['glucose']} mg/dL, Velocity: {scenario['velocity']} mg/dL/min")
|
|
318
|
+
print(f" IOB: {scenario['iob']} U, Carbs: {scenario['carbs']}g")
|
|
319
|
+
print(f" -> Action: {decision.action}, Insulin: {decision.insulin_delivered:.2f} U")
|
|
320
|
+
print(f" Reasoning: {', '.join(decision.reasoning[:2])}")
|
|
321
|
+
|
|
322
|
+
if decision.safety_overrides:
|
|
323
|
+
print(f" [WARN] Safety: {', '.join(decision.safety_overrides)}")
|
|
324
|
+
|
|
325
|
+
# Export behavior report
|
|
326
|
+
print("\nBehavior Report:")
|
|
327
|
+
report = emulator.export_behavior_report()
|
|
328
|
+
print(f" Pump: {report['pump_name']}")
|
|
329
|
+
print(f" Decisions: {report['decision_count']}")
|
|
330
|
+
|
|
331
|
+
print("\n" + "=" * 70)
|
|
332
|
+
print("MEDTRONIC 780G EMULATOR DEMONSTRATION COMPLETE")
|
|
333
|
+
print("=" * 70)
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
if __name__ == "__main__":
|
|
337
|
+
demo_medtronic_780g()
|