ReadPodStatusView.swift 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. //
  2. // ReadPodStatusView.swift
  3. // OmniKit
  4. //
  5. // Created by Joe Moran on 8/15/23.
  6. // Copyright © 2023 LoopKit Authors. All rights reserved.
  7. //
  8. import SwiftUI
  9. import LoopKit
  10. import OmniKit
  11. private func podStatusString(status: DetailedStatus) -> String {
  12. var result, str: String
  13. let formatter = DateComponentsFormatter()
  14. formatter.unitsStyle = .full
  15. formatter.allowedUnits = [.hour, .minute]
  16. formatter.unitsStyle = .short
  17. if let timeStr = formatter.string(from: status.timeActive) {
  18. str = timeStr
  19. } else {
  20. str = String(format: LocalizedString("%1$@ minutes", comment: "The format string for minutes (1: number of minutes string)"), String(describing: Int(status.timeActive / 60)))
  21. }
  22. result = String(format: LocalizedString("Pod Active: %1$@", comment: "The format string for Pod Active: (1: formatted time)"), str)
  23. result += String(format: LocalizedString("\nPod Progress: %1$@", comment: "The format string for Pod Progress: (1: pod progress string)"), String(describing: status.podProgressStatus))
  24. result += String(format: LocalizedString("\nDelivery Status: %1$@", comment: "The format string for Delivery Status: (1: delivery status string)"), String(describing: status.deliveryStatus))
  25. result += String(format: LocalizedString("\nLast Programming Seq Num: %1$@", comment: "The format string for last programming sequence number: (1: last programming sequence number)"), String(describing: status.lastProgrammingMessageSeqNum))
  26. result += String(format: LocalizedString("\nBolus Not Delivered: %1$@ U", comment: "The format string for Bolus Not Delivered: (1: bolus not delivered string)"), status.bolusNotDelivered.twoDecimals)
  27. result += String(format: LocalizedString("\nPulse Count: %1$d", comment: "The format string for Pulse Count (1: pulse count)"), Int(round(status.totalInsulinDelivered / Pod.pulseSize)))
  28. result += String(format: LocalizedString("\nReservoir Level: %1$@ U", comment: "The format string for Reservoir Level: (1: reservoir level string)"), status.reservoirLevel == Pod.reservoirLevelAboveThresholdMagicNumber ? "50+" : status.reservoirLevel.twoDecimals)
  29. result += String(format: LocalizedString("\nAlerts: %1$@", comment: "The format string for Alerts: (1: the alerts string)"), alertSetString(alertSet: status.unacknowledgedAlerts))
  30. if status.radioRSSI != 0 {
  31. result += String(format: LocalizedString("\nRSSI: %1$@", comment: "The format string for RSSI: (1: RSSI value)"), String(describing: status.radioRSSI))
  32. result += String(format: LocalizedString("\nReceiver Low Gain: %1$@", comment: "The format string for receiverLowGain: (1: receiverLowGain)"), String(describing: status.receiverLowGain))
  33. }
  34. if status.faultEventCode.faultType != .noFaults {
  35. // report the additional fault related information in a separate section
  36. result += String(format: LocalizedString("\n\n⚠️ Critical Pod Fault %1$03d (0x%2$02X)", comment: "The format string for fault code in decimal and hex: (1: fault code for decimal display) (2: fault code for hex display)"), status.faultEventCode.rawValue, status.faultEventCode.rawValue)
  37. result += String(format: "\n%1$@", status.faultEventCode.faultDescription)
  38. if let faultEventTimeSinceActivation = status.faultEventTimeSinceActivation,
  39. let faultTimeStr = formatter.string(from: faultEventTimeSinceActivation)
  40. {
  41. result += String(format: LocalizedString("\nFault Time: %1$@", comment: "The format string for fault time: (1: fault time string)"), faultTimeStr)
  42. }
  43. if let errorEventInfo = status.errorEventInfo {
  44. result += String(format: LocalizedString("\nFault Event Info: %1$03d (0x%2$02X),", comment: "The format string for fault event info: (1: fault event info)"), errorEventInfo.rawValue, errorEventInfo.rawValue)
  45. result += String(format: LocalizedString("\n Insulin State Table Corrupted: %@", comment: "The format string for insulin state table corrupted: (1: insulin state corrupted)"), String(describing: errorEventInfo.insulinStateTableCorruption))
  46. result += String(format: LocalizedString("\n Occlusion Type: %1$@", comment: "The format string for occlusion type: (1: occlusion type)"), String(describing: errorEventInfo.occlusionType))
  47. result += String(format: LocalizedString("\n Immediate Bolus In Progress: %1$@", comment: "The format string for immediate bolus in progress: (1: immediate bolus in progress)"), String(describing: errorEventInfo.immediateBolusInProgress))
  48. result += String(format: LocalizedString("\n Previous Pod Progress: %1$@", comment: "The format string for previous pod progress: (1: previous pod progress string)"), String(describing: errorEventInfo.podProgressStatus))
  49. }
  50. if let pdmRef = status.pdmRef {
  51. result += String(format: LocalizedString("\nRef: %@", comment: "The Ref format string (1: pdm ref string)"), pdmRef)
  52. }
  53. }
  54. return result
  55. }
  56. struct ReadPodStatusView: View {
  57. @Environment(\.horizontalSizeClass) var horizontalSizeClass
  58. @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
  59. private var toRun: ((_ completion: @escaping (_ result: PumpManagerResult<DetailedStatus>) -> Void) -> Void)?
  60. @State private var alertIsPresented: Bool = false
  61. @State private var displayString: String = ""
  62. @State private var error: LocalizedError? = nil
  63. @State private var executing: Bool = false
  64. @State private var showActivityView: Bool = false
  65. init(toRun: @escaping (_ completion: @escaping (_ result: PumpManagerResult<DetailedStatus>) -> Void) -> Void) {
  66. self.toRun = toRun
  67. }
  68. var body: some View {
  69. VStack {
  70. List {
  71. Section {
  72. Text(self.displayString).fixedSize(horizontal: false, vertical: true)
  73. }
  74. }
  75. .toolbar {
  76. ToolbarItem(placement: .navigationBarTrailing) {
  77. Button(action: {
  78. self.showActivityView = true
  79. }) {
  80. Image(systemName: "square.and.arrow.up")
  81. }
  82. }
  83. }.sheet(isPresented: $showActivityView) {
  84. ActivityView(isPresented: $showActivityView, activityItems: [self.displayString])
  85. }
  86. VStack {
  87. Button(action: {
  88. asyncAction()
  89. }) {
  90. Text(buttonText)
  91. .actionButtonStyle(.primary)
  92. }
  93. .padding()
  94. .disabled(executing)
  95. }
  96. .padding(self.horizontalSizeClass == .regular ? .bottom : [])
  97. .background(Color(UIColor.secondarySystemGroupedBackground).shadow(radius: 5))
  98. }
  99. .insetGroupedListStyle()
  100. .navigationTitle(LocalizedString("Read Pod Status", comment: "navigation title for read pod status"))
  101. .navigationBarTitleDisplayMode(.inline)
  102. .alert(isPresented: $alertIsPresented, content: { alert(error: error) })
  103. .onFirstAppear {
  104. asyncAction()
  105. }
  106. }
  107. private func asyncAction () {
  108. DispatchQueue.global(qos: .utility).async {
  109. executing = true
  110. self.displayString = ""
  111. toRun?() { (result) in
  112. switch result {
  113. case .success(let detailedStatus):
  114. self.displayString = podStatusString(status: detailedStatus)
  115. case .failure(let error):
  116. self.error = error
  117. self.alertIsPresented = true
  118. }
  119. executing = false
  120. }
  121. }
  122. }
  123. private var buttonText: String {
  124. if executing {
  125. return LocalizedString("Reading Pod Status...", comment: "button title when executing read pod status")
  126. } else {
  127. return LocalizedString("Read Pod Status", comment: "button title to read pod status")
  128. }
  129. }
  130. private func alert(error: Error?) -> SwiftUI.Alert {
  131. return SwiftUI.Alert(
  132. title: Text(LocalizedString("Failed to read pod status.", comment: "Alert title for error when reading pod status")),
  133. message: Text(error?.localizedDescription ?? "No Error")
  134. )
  135. }
  136. }
  137. struct ReadPodStatusView_Previews: PreviewProvider {
  138. static var previews: some View {
  139. NavigationView {
  140. let detailedStatus = try! DetailedStatus(encodedData: Data([0x02, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0xc3, 0x6a, 0x02, 0x07, 0x03, 0xff, 0x02, 0x09, 0x20, 0x00, 0x28, 0x99, 0x08, 0x00, 0x82]))
  141. ReadPodStatusView() { completion in
  142. completion(.success(detailedStatus))
  143. }
  144. }
  145. }
  146. }