-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Remote PR Set #3 #2009
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Remote PR Set #3 #2009
Conversation
@@ -409,7 +408,8 @@ final class DeviceDataManager { | |||
alertManager: alertManager, | |||
analyticsServicesManager: analyticsServicesManager, | |||
loggingServicesManager: loggingServicesManager, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This allows the DeviceDataManager
to handle actions for the ServicesManager
-- via a new RemoteCommandHandler
protocol. When a plugin sends an action to the ServicesManager, it needs to get that action to the DeviceDataManager.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DeviceDataManager already exposes methods for dosing. Does it need separate methods for "remote" dosing? If so, why?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm wondering how ServicesManager
can communicate with DeviceDataManager
, assuming DeviceDataManager
is still where dosing/settings things will live. Right now there is not a reference I see going that direction. So this delegate provides a bridge for the ServicesManagerManager
to talk to the DeviceDataManager
to enact doses, change settings, etc.
Is it the delegate name of RemoteActionDelegate
that I should change? Like DeviceDataManagerDelegate
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or were you thinking these remote things be relocated to RemoteDataServicesManager
? (dosing, settings)? (That's another place where the reference goes from DeviceDataManager
-> RemoteDataServicesManager
but not the other way around. )
We also need to think about where incoming push notifications are handled as well, if we are moving all these, as those need to be propogated back to the ServicesManager
too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ServicesManager
will still need to communicate with DeviceDataManager
for dosing. But it should only be for dosing. I don't think DeviceDataManager needs to know about anything "remote".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok I'll look at keeping the delegate, as we still need a bridge between these classes, but I'll rename it -- maybe ServicesManagerDelegate
(I had the delegation relationship backwards in my prior comment).
Then I'll remove as much of the remote things from DeviceDataManager into the ServicesManager
or RemoteDataServicesManager
.
Do you want these changes as part of this PR set or a future one? This change seems big enough that it may be easier to test/review separately from all this. Whatever you prefer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I documented the relationships between these managers to get the big picture of how this update will fit. A few notes on the plans for this update:
- The
RemoteActionDelegate
, implemented byDeviceDataManager
in this PR, will be renamed toServicesManagerDelegate
(red line in diagram). That will be for dosing/settings updates. I'll rename the delegate methods to drophandleRemote
from prefix. - Update my dosing/settings error messages in
DeviceDataManager
to remove any 'Remote' phraseology to be consistent with the rest of this rearchitecture. I plan to not change anything else in those methods, including remote trigger uploads and analytics uploads (will stay inDeviceDataManager
) - Move the background task code, all in support of remote, from
DeviceDataManager
toServicesManager
. - Move
handleRemoteNotification
fromDeviceDataManager
toServicesManager
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ps2 I've made the changes that I describe in the last comment and pushed to this branch. Let me know if this is what you are looking for.
Note there is a still the RemoteActionDelegate
(not represented in this diagram) that lives between the RemoteDataServices
and ServicesManager
. Then there is the new delegate between ServicesManager
and DeviceDataManager
which has no references to "Remote" like you mentioned.
I have not adjusted the naming in RemoteActionDelegate
per your comments in the LoopKit PR. I'll look at that separately. I think that is the last of your feedback thus far.
} | ||
|
||
func handleRemoteCommand(_ command: RemoteCommand) async { | ||
//Remote Overrides | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With the removal of the OverrideAction
type, this validation logic needed moved into here.
@@ -1443,30 +1442,140 @@ extension DeviceDataManager { | |||
await remoteDataServicesManager.triggerUpload(for: .overrides) | |||
} | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved from OverrideAction
} | ||
} | ||
} | ||
|
||
//Remote Bolus | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved from BolusAction
case .exceedsMaxBolus: | ||
return NSLocalizedString("Exceeds maximum allowed bolus in settings", comment: "Remote command error description: bolus exceeds maximum bolus in settings.") | ||
} | ||
} | ||
} | ||
|
||
//Remote Carb Entry | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move from CarbAction
Loop/Managers/LoopDataManager.swift
Outdated
@@ -903,6 +903,8 @@ extension LoopDataManager { | |||
} | |||
|
|||
updateRemoteRecommendation() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
REMOTE 2.0: WILL REMOVE PRE-MERGE
@@ -79,27 +79,6 @@ extension NotificationManager { | |||
|
|||
// MARK: - Notifications | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Getting rid of the these parsing/invalid error notifications for now as I think is desired -- this logic is now removed from the Loop domain with this rearchicture so it is tricky to present anyways. All of these will show in Nightscout as notes though.
let service = try serviceForPushNotification(notification) | ||
return try await service.handleRemoteNotification(notification) | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
REMOTE 2.0: WILL REMOVE PRE-MERGE
Referring to just processPendingRemoteCommands
let serviceIdentifier = notification["serviceIdentifier"] as? String ?? defaultServiceIdentifier | ||
return remoteDataServices.first(where: {$0.serviceIdentifier == serviceIdentifier}) | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method is now removed as we will not fetch a command from the plugin now with the new delegation flow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are not removing push notifications for commands, right? Just adding periodic fetch as a backup, for when push does not work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Push is still the most timely way to wake Loop to handle a remote command, but it may be not 100% reliable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah push notifications are not being removed with these updates or Remote 2.0.
@@ -201,6 +205,40 @@ extension ServicesManager: ServiceDelegate { | |||
log.default("Service with identifier '%{public}@' deleted", service.serviceIdentifier) | |||
removeActiveService(service) | |||
} | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The ServiceDelegate
protocol conforms now to the RemoteCommandHandler
and these methods are required.
func handleRemoteOverrideCancel() async throws { | ||
try await remoteCommandHandler?.handleRemoteOverrideCancel() | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are the 2 notifications we still show for Loop domain issues/success -- Carbs an Bolus. The error is propogated back to the plugin so it can still do what it needs to (right now log as NS note)
@@ -405,8 +405,8 @@ final class CarbEntryViewController: LoopChartsTableViewController, Identifiable | |||
cell.datePicker.preferredDatePickerStyle = .wheels | |||
} | |||
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use new constants for these
b1597ca
to
1bbf0a1
Compare
Loop/Managers/LoopDataManager.swift
Outdated
@@ -2204,6 +2206,14 @@ protocol LoopDataManagerDelegate: AnyObject { | |||
/// - units: The recommended bolus in U | |||
/// - Returns: a supported bolus volume in U. The volume returned should be the nearest deliverable volume. | |||
func roundBolusVolume(units: Double) -> Double |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
REMOTE 2.0: WILL REMOVE PRE-MERGE
@@ -161,11 +140,11 @@ extension NotificationManager { | |||
} | |||
|
|||
@MainActor |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are the only in-app failure notifications I left behind -- for bolus/carb specific issues (i.e. not parsing errors or OTP erorrs -- things like "The bolus is too big").
We may decide we don't want these either but I left them for now as they were pretty specific errors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Things like "the bolus is too big" is something the caregiver ideally should be told at the time of entering the command.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah the caregiver app does prevent really large values from being entered (not sure about Nightscout) but doesn't yet have support for honoring the Looper's dosing maximums.
} | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This here should really be the same - it just got moved around I believe.
throw error | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
REMOTE 2.0: WILL REMOVE PRE-MERGE
@@ -27,6 +27,8 @@ class MockDelegate: LoopDataManagerDelegate { | |||
self.recommendation = automaticDose.recommendation | |||
completion(error) | |||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
REMOTE 2.0: WILL REMOVE PRE-MERGE
@@ -1,101 +0,0 @@ | |||
// |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately these tests I recently added are not easily supported now with these changes. I may try to add these back with some architecture improvements later.
} catch { | ||
log.error("Remote Notification: Parse Error: %{public}@", String(describing: error)) | ||
return | ||
func processPendingRemoteCommands() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As much as we can, "remote" handling responsibility/concerns should be isolated to RemoteDataServicesManager/ServicesManager. I'm not sure DeviceDataManager needs to change at all.
@@ -1357,84 +1366,74 @@ extension Notification.Name { | |||
} | |||
|
|||
// MARK: - Remote Notification Handling | |||
extension DeviceDataManager { | |||
|
|||
extension DeviceDataManager: RemoteActionDelegate { | |||
|
|||
func handleRemoteNotification(_ notification: [String: AnyObject]) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should probably be moved to ServicesManager.
1bbf0a1
to
10e58b7
Compare
Use notification to detect loop completion. Move handleRemoteNotification to ServicesManager
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is closer to landing, but if possible, I still think some further re-arranging of responsibilities would keep things more organized. See the other comments for details. If you really want to land this now, we could, and tackle my comments as a later refactoring. Let me know.
@@ -416,7 +415,8 @@ final class DeviceDataManager { | |||
alertManager: alertManager, | |||
analyticsServicesManager: analyticsServicesManager, | |||
loggingServicesManager: loggingServicesManager, | |||
remoteDataServicesManager: remoteDataServicesManager | |||
remoteDataServicesManager: remoteDataServicesManager, | |||
servicesManagerDelegate: self |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dosing (eventually) needs to be delegated to DeviceDataManager, but overrides, carb, settings, are more the domain of LoopDataManager. And we may want to even run dosing through LoopDataManager, as a bolus command received mid-loop is something to consider.
func deliverCarbs(amountInGrams: Double, absorptionTime: TimeInterval?, foodType: String?, startDate: Date?) async throws { | ||
|
||
let absorptionTime = absorptionTime ?? carbStore.defaultAbsorptionTimes.medium | ||
if absorptionTime < LoopConstants.minCarbAbsorptionTime || absorptionTime > LoopConstants.maxCarbAbsorptionTime { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think validation and error reporting for remote commands should remain in ServiceManager. Local commands tend to be validated directly in the local UIs before being commanded.
Move ServiceManager delegation to LoopDataManager
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Just a note with one further small change if you want to tackle it now. Otherwise, I'll plan on merging this soon.
|
||
defer { | ||
log.default("Remote Notification: Finished handling") | ||
guard amountInUnits > 0 else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This validation could go into remote services manager, along with the error definitions. These are errors to report to the remote services, I think. Won't block on this, though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will put a dependency in ServicesManger on LoopSettings which sounds like you are ok with. I'll go ahead and make that change pre-merge. More contextual notes on the last changes are here https://loop.zulipchat.com/#narrow/stream/358458-Loop-Caregiver-App/topic/Merge.20Strategies/near/369827210
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've made these changes here and don't have any other planned changes. Let me know if anything is needed or merge if you are ready.
This is part of a collection of foundational PRs that need to merge at the same time.
Loop
LoopKit
NightscoutService
TidepoolService
Remote Delegation Update
The remote command flow has been refactored to use delegation from Loop to enact commands, rather than the recently merged type system.
Remote Errors in Nightscout
Support is added for uploading remote errors from Loop to Nightscout as a note. This was discussed in Zulip several months ago. This is not the same as our future remote 2.0 work that will track command status in Mongo. It is instead a stopgap to provide remote feedback to caregivers when commands are rejected, until we can roll out Remote 2.0. In addition, the rearchitecture mentioned above made it tricky to maintain the current level of in-app error feedback, so this comes at a convenient time.
Remote 2.0 Preview
For terminology, let's refer to the pre-work we've been merging as "Foundational" and the future implementation of remote 2.0 just as "Remote 2.0".
This set of PRs is different from the prior, as it includes the Remote 2.0 features in addition to the foundational. But I plan to remove these Remote 2.0 parts just prior to merge. PR comments point out what parts are Remote 2.0 so it should be clear. You could either ignore these sections or view them for context on what is coming later. Note these Remote 2.0 changes are not ready for merge so you will see some TODOs in these sections.
Once you approve of the parts that are intended for merge, I'll remove the Remote 2.0 parts.
So why did I leave the Remote 2.0 features for the initial review? It can be time consuming to tease apart the Foundational parts from Remote 2.0 for a PR as they are very connected. If there were significant change requests once you reviewed this, that could lead to a lot of rework to Remote 2.0 things. So it is more time efficient to keep these components together until you get through your first pass.