TrioRemoteControl+Bolus.swift 3.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. import Foundation
  2. extension TrioRemoteControl {
  3. internal func handleBolusCommand(_ pushMessage: PushMessage) async throws {
  4. guard let bolusAmount = pushMessage.bolusAmount else {
  5. await logError("Command rejected: bolus amount is missing or invalid.", pushMessage: pushMessage)
  6. return
  7. }
  8. let maxBolus = await TrioApp.resolver.resolve(SettingsManager.self)?.pumpSettings.maxBolus ?? Decimal(0)
  9. if bolusAmount > maxBolus {
  10. await logError(
  11. "Command rejected: bolus amount (\(bolusAmount) units) exceeds the maximum allowed (\(maxBolus) units).",
  12. pushMessage: pushMessage
  13. )
  14. return
  15. }
  16. let maxIOB = settings.preferences.maxIOB
  17. guard let currentIOB = iobService.currentIOB else {
  18. throw CoreDataError.fetchError(function: #function, file: #file)
  19. }
  20. if (currentIOB + bolusAmount) > maxIOB {
  21. await logError(
  22. "Command rejected: bolus amount (\(bolusAmount) units) would exceed max IOB (\(maxIOB) units). Current IOB: \(currentIOB) units.",
  23. pushMessage: pushMessage
  24. )
  25. return
  26. }
  27. let totalRecentBolusAmount =
  28. try await fetchTotalRecentBolusAmount(since: Date(timeIntervalSince1970: pushMessage.timestamp))
  29. if totalRecentBolusAmount >= bolusAmount * 0.2 {
  30. await logError(
  31. "Command rejected: boluses totaling more than 20% of the requested amount have been delivered since the command was sent.",
  32. pushMessage: pushMessage
  33. )
  34. return
  35. }
  36. debug(.remoteControl, "Enacting bolus command with amount: \(bolusAmount) units.")
  37. guard let apsManager = await TrioApp.resolver.resolve(APSManager.self) else {
  38. await logError(
  39. "Error: unable to process bolus command because the APS Manager is not available.",
  40. pushMessage: pushMessage
  41. )
  42. return
  43. }
  44. await apsManager.enactBolus(amount: Double(truncating: bolusAmount as NSNumber), isSMB: false, callback: nil)
  45. debug(
  46. .remoteControl,
  47. "Remote command processed successfully. \(pushMessage.humanReadableDescription())"
  48. )
  49. }
  50. private func fetchTotalRecentBolusAmount(since date: Date) async throws -> Decimal {
  51. let predicate = NSPredicate(
  52. format: "type == %@ AND timestamp > %@",
  53. PumpEventStored.EventType.bolus.rawValue,
  54. date as NSDate
  55. )
  56. let results: Any = try await CoreDataStack.shared.fetchEntitiesAsync(
  57. ofType: PumpEventStored.self,
  58. onContext: pumpHistoryFetchContext,
  59. predicate: predicate,
  60. key: "timestamp",
  61. ascending: true,
  62. fetchLimit: nil,
  63. propertiesToFetch: ["bolus.amount"]
  64. )
  65. guard let bolusDictionaries = results as? [[String: Any]] else {
  66. await logError("Failed to cast fetched bolus events. Fetched entities type: \(type(of: results))")
  67. throw CoreDataError.fetchError(function: #function, file: #file)
  68. }
  69. let totalAmount = bolusDictionaries.compactMap { ($0["bolus.amount"] as? NSNumber)?.decimalValue }.reduce(0, +)
  70. return totalAmount
  71. }
  72. }