| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285 |
- //
- // PairPodSetupViewController.swift
- // OmniKitUI
- //
- // Created by Pete Schwamb on 9/18/18.
- // Copyright © 2018 Pete Schwamb. All rights reserved.
- //
- import UIKit
- import LoopKit
- import LoopKitUI
- import RileyLinkKit
- import OmniKit
- import os.log
- class PairPodSetupViewController: SetupTableViewController {
-
- var rileyLinkPumpManager: RileyLinkPumpManager!
-
- var previouslyEncounteredWeakComms: Bool = false
-
- var pumpManager: OmnipodPumpManager! {
- didSet {
- if oldValue == nil && pumpManager != nil {
- pumpManagerWasSet()
- }
- }
- }
- private let log = OSLog(category: "PairPodSetupViewController")
- // MARK: -
-
- @IBOutlet weak var activityIndicator: SetupIndicatorView!
-
- @IBOutlet weak var loadingLabel: UILabel!
-
- private var loadingText: String? {
- didSet {
- tableView.beginUpdates()
- loadingLabel.text = loadingText
-
- let isHidden = (loadingText == nil)
- loadingLabel.isHidden = isHidden
- tableView.endUpdates()
- }
- }
-
- override func viewDidLoad() {
- super.viewDidLoad()
- continueState = .initial
- }
-
- private func pumpManagerWasSet() {
- // Still priming?
- let primeFinishesAt = pumpManager.state.podState?.primeFinishTime
- let currentTime = Date()
- if let finishTime = primeFinishesAt, finishTime > currentTime {
- self.continueState = .pairing
- let delay = finishTime.timeIntervalSince(currentTime)
- DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
- self.continueState = .ready
- }
- }
- }
-
- // MARK: - UITableViewDelegate
-
- override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
- if case .pairing = continueState {
- return
- }
-
- tableView.deselectRow(at: indexPath, animated: true)
- }
-
- // MARK: - State
-
- private enum State {
- case initial
- case pairing
- case priming(finishTime: TimeInterval)
- case fault
- case ready
- }
-
- private var continueState: State = .initial {
- didSet {
- log.default("Changed continueState from %{public}@ to %{public}@", String(describing: oldValue), String(describing: continueState))
- switch continueState {
- case .initial:
- activityIndicator.state = .hidden
- footerView.primaryButton.isEnabled = true
- footerView.primaryButton.setPairTitle()
- case .pairing:
- activityIndicator.state = .indeterminantProgress
- footerView.primaryButton.isEnabled = false
- footerView.primaryButton.setPairTitle()
- lastError = nil
- loadingText = LocalizedString("Pairing…", comment: "The text of the loading label when pairing")
- case .priming(let finishTime):
- activityIndicator.state = .timedProgress(finishTime: CACurrentMediaTime() + finishTime)
- footerView.primaryButton.isEnabled = false
- footerView.primaryButton.setPairTitle()
- lastError = nil
- loadingText = LocalizedString("Priming…", comment: "The text of the loading label when priming")
- case .fault:
- activityIndicator.state = .hidden
- footerView.primaryButton.isEnabled = true
- footerView.primaryButton.setDeactivateTitle()
- case .ready:
- activityIndicator.state = .completed
- footerView.primaryButton.isEnabled = true
- footerView.primaryButton.resetTitle()
- lastError = nil
- loadingText = LocalizedString("Primed", comment: "The text of the loading label when pod is primed")
- }
- }
- }
-
- private var lastError: Error? {
- didSet {
- guard oldValue != nil || lastError != nil else {
- return
- }
-
- var errorStrings: [String]
- var errorText: String
-
- if let error = lastError as? LocalizedError {
- errorStrings = [error.errorDescription, error.failureReason, error.recoverySuggestion].compactMap { $0 }
- } else {
- errorStrings = [lastError?.localizedDescription].compactMap { $0 }
- }
-
- let podCommsError: PodCommsError?
- if let pumpManagerError = lastError as? PumpManagerError {
- switch pumpManagerError {
- // Check for a wrapped PodCommsError in the possible PumpManagerError types
- case .communication(let error), .configuration(let error), .connection(let error), .deviceState(let error):
- podCommsError = error as? PodCommsError
- default:
- podCommsError = nil
- break
- }
- } else {
- // Check for a non PumpManagerError PodCommsError
- podCommsError = lastError as? PodCommsError
- }
- if let podCommsError = podCommsError, podCommsError.possibleWeakCommsCause {
- if previouslyEncounteredWeakComms {
- errorStrings.append(LocalizedString("If the problem persists, move to a new area and try again", comment: "Additional pairing recovery suggestion on multiple pairing failures"))
- } else {
- previouslyEncounteredWeakComms = true
- }
- }
- errorText = errorStrings.joined(separator: ". ")
-
- if !errorText.isEmpty {
- errorText += "."
- } else if let error = lastError {
- // We have an error but no error text, generate a string to describe the error
- errorText = String(describing: error)
- }
- loadingText = errorText
-
- // If we have an error, update the continue state appropriately
- if let podCommsError = podCommsError {
- if podCommsError.isFaulted {
- continueState = .fault
- } else {
- continueState = .initial
- }
- } else if lastError != nil {
- continueState = .initial
- }
- }
- }
-
- // MARK: - Navigation
-
- private func navigateToReplacePod() {
- log.default("Navigating to ReplacePod screen")
- performSegue(withIdentifier: "ReplacePod", sender: nil)
- }
- override func continueButtonPressed(_ sender: Any) {
- switch continueState {
- case .initial:
- pair()
- case .ready:
- super.continueButtonPressed(sender)
- case .fault:
- navigateToReplacePod()
- default:
- break
- }
- }
-
- override func cancelButtonPressed(_ sender: Any) {
- let podState = pumpManager.state.podState
- if podState != nil {
- let confirmVC = UIAlertController(pumpDeletionHandler: {
- self.navigateToReplacePod()
- })
- self.present(confirmVC, animated: true) {}
- } else {
- super.cancelButtonPressed(sender)
- }
- }
-
- // MARK: -
-
- private func pair() {
- self.continueState = .pairing
- pumpManager.pairAndPrime() { (result) in
- DispatchQueue.main.async {
- switch result {
- case .success(let finishTime):
- self.log.default("Pairing succeeded, finishing in %{public}@ sec", String(describing: finishTime))
- if finishTime > 0 {
- self.continueState = .priming(finishTime: finishTime)
- DispatchQueue.main.asyncAfter(deadline: .now() + finishTime) {
- self.continueState = .ready
- }
- } else {
- self.continueState = .ready
- }
- case .failure(let error):
- self.log.default("Pairing failed with error: %{public}@", String(describing: error))
- self.lastError = error
- }
- }
- }
- }
- }
- private extension PodCommsError {
- var possibleWeakCommsCause: Bool {
- switch self {
- case .invalidData, .noResponse, .invalidAddress, .rssiTooLow, .rssiTooHigh, .unexpectedPacketType:
- return true
- default:
- return false
- }
- }
- }
- private extension SetupButton {
- func setPairTitle() {
- setTitle(LocalizedString("Pair", comment: "Button title to pair with pod during setup"), for: .normal)
- }
-
- func setDeactivateTitle() {
- setTitle(LocalizedString("Deactivate", comment: "Button title to deactivate pod because of fault during setup"), for: .normal)
- }
- }
- private extension UIAlertController {
- convenience init(pumpDeletionHandler handler: @escaping () -> Void) {
- self.init(
- title: nil,
- message: LocalizedString("Are you sure you want to shutdown this pod?", comment: "Confirmation message for shutting down a pod"),
- preferredStyle: .actionSheet
- )
-
- addAction(UIAlertAction(
- title: LocalizedString("Deactivate Pod", comment: "Button title to deactivate pod"),
- style: .destructive,
- handler: { (_) in
- handler()
- }
- ))
-
- let exit = LocalizedString("Continue", comment: "The title of the continue action in an action sheet")
- addAction(UIAlertAction(title: exit, style: .default, handler: nil))
- }
- }
|