Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions Model/Helper/PumpEvent+helper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,35 @@ enum PumpEventDTO: Encodable {
try prime.encode(to: encoder)
}
}

var isResume: Bool {
if case .resume = self { return true }
return false
}

var isSuspend: Bool {
if case .suspend = self { return true }
return false
}

var timestampDate: Date? {
switch self {
case let .bolus(dto):
return PumpEventStored.dateFormatter.date(from: dto.timestamp)
case let .tempBasal(dto):
return PumpEventStored.dateFormatter.date(from: dto.timestamp)
case let .tempBasalDuration(dto):
return PumpEventStored.dateFormatter.date(from: dto.timestamp)
case let .suspend(dto):
return PumpEventStored.dateFormatter.date(from: dto.timestamp)
case let .resume(dto):
return PumpEventStored.dateFormatter.date(from: dto.timestamp)
case let .rewind(dto):
return PumpEventStored.dateFormatter.date(from: dto.timestamp)
case let .prime(dto):
return PumpEventStored.dateFormatter.date(from: dto.timestamp)
}
}
}

// Extension with helper functions to map pump events to DTO objects via uniform masking enum
Expand Down
2 changes: 1 addition & 1 deletion Trio/Resources/javascript/prepare/profile.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//для pumpprofile.json параметры: settings/settings.json settings/bg_targets.json settings/insulin_sensitivities.json settings/basal_profile.json preferences.json settings/carb_ratios.json settings/temptargets.json settings/model.json
cd//для pumpprofile.json параметры: settings/settings.json settings/bg_targets.json settings/insulin_sensitivities.json settings/basal_profile.json preferences.json settings/carb_ratios.json settings/temptargets.json settings/model.json
//для profile.json параметры: settings/settings.json settings/bg_targets.json settings/insulin_sensitivities.json settings/basal_profile.json preferences.json settings/carb_ratios.json settings/temptargets.json settings/model.json settings/autotune.json

function generate(pumpsettings_data, bgtargets_data, isf_data, basalprofile_data, preferences_input = false, carbratio_input = false, temptargets_input = false, model_input = false, autotune_input = false, trio_data) {
Expand Down
62 changes: 62 additions & 0 deletions Trio/Sources/APS/OpenAPS/OpenAPS.swift
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,24 @@ final class OpenAPS {
dtos.insert(simulatedBolusDTO, at: 0)
}

// Addresses https://github.com/nightscout/Trio/issues/898
//
// On a cold start (new user, fresh onboarding, or pump disconnected > 24h),
// the oldest event in pump history can be a resume with no preceding pump
// activity. oref interprets this as the end of a suspend that never started,
// which drives negative IOB and can cause excessive insulin delivery.
//
// Detection: if the chronologically oldest DTO is a resume AND there are zero
// pump events in the 24h before it, this is a cold-start orphaned resume.
// Fix: inject a simulated suspend 1 second before the resume so oref sees a
// balanced suspend/resume pair that is effectively a no-op.
// Check whether the oldest event is an orphaned resume (cold start detection).
if let orphanedResumeDate = self.detectOrphanedResume(pumpHistoryObjectIDs) {
let suspendDTO = self.createSimulatedSuspendDTO(at: orphanedResumeDate.addingTimeInterval(-1))
// Insert at the end since this is chronologically the oldest event
dtos.append(suspendDTO)
}

// Convert the DTOs to JSON
return self.jsonConverter.convertToJSON(dtos)
}
Expand Down Expand Up @@ -276,6 +294,50 @@ final class OpenAPS {
return .bolus(bolusDTO)
}

private func createSimulatedSuspendDTO(at date: Date) -> PumpEventDTO {
let dateFormatted = PumpEventStored.dateFormatter.string(from: date)

let suspendDTO = SuspendDTO(
id: UUID().uuidString,
timestamp: dateFormatted
)
return .suspend(suspendDTO)
}

/// Detects a cold-start orphaned resume: returns the resume's date if the chronologically
/// oldest event in pump history is a resume AND there are no pump events in the 24h before it.
private func detectOrphanedResume(
_ pumpHistoryObjectIDs: [NSManagedObjectID]
) -> Date? {
// Map object IDs to (type, timestamp) pairs and find the chronologically oldest
let events: [(type: String, timestamp: Date)] = pumpHistoryObjectIDs.compactMap { objectID in
guard let event = self.context.object(with: objectID) as? PumpEventStored,
let type = event.type,
let timestamp = event.timestamp
else { return nil }
return (type: type, timestamp: timestamp)
}

guard let oldest = events.min(by: { $0.timestamp < $1.timestamp }) else { return nil }

// Only proceed if the oldest event is a resume
guard oldest.type == PumpEventStored.EventType.pumpResume.rawValue else { return nil }

// Check whether any pump event exists in the 24h before this resume
let lookbackDate = oldest.timestamp.addingTimeInterval(-24 * 60 * 60)

let request: NSFetchRequest<PumpEventStored> = PumpEventStored.fetchRequest()
request.predicate = NSPredicate(
format: "timestamp >= %@ AND timestamp < %@",
lookbackDate as NSDate,
oldest.timestamp as NSDate
)
request.fetchLimit = 1

let count = (try? context.count(for: request)) ?? 0
return count == 0 ? oldest.timestamp : nil
}

func determineBasal(
currentTemp: TempBasal,
clock: Date = Date(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ extension Onboarding {

// MARK: - Determine Initial Build State

/// Determines whether the app is in a fresh install state for Trio v0.3.0.
/// Determines whether the app is in a fresh install state for Trio (new vs. returning/updating user).
///
/// This check is based on the assumption that a truly clean install will only contain
/// the `logs/` directory and the `preferences.json` file in the app's Documents directory.
Expand Down
Loading