Skip to content
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

Weather service improvements and improve memory usage #1860

Closed
wants to merge 6 commits into from

Conversation

JF002
Copy link
Collaborator

@JF002 JF002 commented Sep 17, 2023

This branch implements the suggestion I did in this comment in #1822:

  • Disable all weather events that are not processed by InfiniTime
  • Ensure that a single version of each event is stored in memory
  • Set the maximum number of events in the timeline to 10

Additionally, it also improves PTS : it now refreshes the weather info even if it didn't receive any precipitation event (assume no precipitation in that case).

I'm not satisfied with the policy that decides which "version" of the event will be stored in the timeline. The policy I implemented here

  • adds the event if no event of the same type already exists
  • remove the old one and add the new one if the new one has a lower expiry time
  • otherwise, keep the one from the timeline

I'm open to suggestions for a better policy :)

Enable C++ 17 so we can use new functionalities like "if constexpr".
Refactor WeatherService :
 - Add a sane max number of elements in the timeline
 - Create events in their own factory methods (CreateXXXEvent())
 - Encapsulate QCBOR deserialization in template functions Get<>().
Disable the weather events that are not processed by InfiniTime right now using "if constexpr". We'll enable them when we need them.
Fix Temperature handling (uint16 -> int16) + use of optional<>.
Ensure that we keep only 1 version of each weather event in the timeline.
Ensure that PTS will update the weather info if temperature and cloud info were received. Assume 0 precipitation if no precipitation event were received.
@github-actions
Copy link

Build size and comparison to main:

Section Size Difference
text 375188B -1596B
data 940B 0B
bss 63420B 0B

@Avamander
Copy link
Collaborator

Avamander commented Sep 17, 2023

I really don't think the "one of the same type" approach is a correct way to invalidate events.

I'd rather have bad companions wipe the timeline, than take that ability to display more than one number away from all of the companions.

@JF002
Copy link
Collaborator Author

JF002 commented Sep 17, 2023

I'd rather have bad companions wipe the timeline, than take that ability to display more than one number away from all of the companions.

I don't think this is a good idea. I mean : how would companion apps know if they can push more data or not. How would they know how many data points InfiniTime can process, and how much will overflow the memory?

I also don't think there are "bad" companion app : they simply use our API. The current API allows companion apps to let InfiniTime know that updated weather info are available. If they are able to provide updated info very often, all the better, the user will benefit from accurate weather info.

On the other hand, I want InfiniTime to be as stable as possible, it must not crash. In this case, the crashes occur because the weather service stores all data coming from the companion app. Even if the older version of the data won't be used anymore. Even if the new data is exactly the same as the previous one. There must be a better way to handle this.

In this PR, I tried to adopt a pragmatic approach based on our current use-cases : InfiniTime currently only processes temperature, cloud and precipitation. And the only thing it does with those data is to display a single data point. So there's no need to process and store other events, and multiple data points, since a single one can be displayed. Companion apps can send more events more often, that's fine, but the current version of InfiniTime will simply ignore those it cannot process.

And in the future, when we have actual use-cases for those additional data, we'll be able to extend the current implementation to process them accordingly. And this will require no change on the companion app side!

@Avamander
Copy link
Collaborator

How would they know how many data points InfiniTime can process, and how much will overflow the memory?

That's up to us to properly report. If the companion really operates on the principle of "we only know what's current, the past stuff doesn't matter and we don't know what's coming" then it would be better to start afresh.

I also don't think there are "bad" companion app [...] Even if the older version of the data won't be used anymore.

If it's not supposed to be used any more it's supposed to be pushed with appropriate expiration. A companion that "lies" is, to us, a bad companion.

Even if the new data is exactly the same as the previous one.

Fundamentally InfiniTime as a companion can't tell the difference between "the weather has remained unchanged" and "the data is the same". Information about validity/relevance has to come from the companion that actually has that data.

And the only thing it does with those data is to display a single data point.

The plan was that PTS would display min-max, so already having just a single event would make that not possible.

It's crippling the functionality before we actually get a chance to use it because a companion is sending junk and it can't be fixed.

And this will require no change on the companion app side!

It most likely would require changes, because they'd have no way to actually test out all the event types (and their validation). It's not a 100% guaranteed, but with stub functionality it's possible.

And in the future, when we have actual use-cases for those additional data, we'll be able to extend the current implementation to process them accordingly.

The other thing is exactly this. Next up would be calendar events. Sane limits would have to be figured out anyways, we can't just keep one event in the Calendar, that'd be Event :D

@FintasticMan FintasticMan added enhancement Enhancement to an existing app/feature weather Bugs and PRs related to Weather labels Sep 18, 2023
@JF002
Copy link
Collaborator Author

JF002 commented Sep 18, 2023

That's up to us to properly report. If the companion really operates on the principle of "we only know what's current, the past stuff doesn't matter and we don't know what's coming" then it would be better to start afresh.

As I said : we currently have no use for the past stuff, but this past stuff fills the memory and crashes the system. And since I cannot figure any use-case for those data, I thought it would be a good idea to drop them until we know how to use them. I'm just trying to provide a fix so that the feature we delivered actually works. We'll have the time to figure how to make future functionalities work... in the future.

Could you explain why the past stuff is so important?

If it's not supposed to be used any more it's supposed to be pushed with appropriate expiration. A companion that "lies" is, to us, a bad companion.

Please, let's try to remain courteous to companion apps and their developers. They do an amazing job integrating their apps with InfiniTime, and I don't think this is a great way to thank them for their work. As far as I know, all companion apps that integrate the weather functionality will cause a crash at one time or another. What should they have done to prevent that?

I honestly do not understand your point here : they do not lie. They just say that at a specific moment in time, the temperature forecast for the next 6h is this value. Then, say 1 hours later, they send a new data saying that the new forecast for the next 6 hours is that other value. Is this a lie? No! It's an updated information. Should InfiniTime keep all the obsolete forecasts? I don't know, but we currently have no need for them right now.

Next up would be calendar events. Sane limits would have to be figured out anyways, we can't just keep one event in the Calendar, that'd be Event :D

The calendar use-case is an interesting example.
Will we be able to store an unlimited number of events for an unlimited number of days, months and years? No! We have to set a maximum number of events. Of course, it wouldn't make sense to store a single event in the calendar.
But it doesn't make any sense to keep multiple versions of the same event. If the companion app sends the same event twice, we should just keep a single copy of it. If the companion app updates the event, we should keep only the latest version.

I think your vision of the weather service and mine are quite different, and I have a hard time trying to understand how to fix the current issue with your explanations. You seem to have a good idea about how this should work, and I would be very interested in knowing more how we should improve the API, the weather service and the companion app to fix the issue and extend the functionalities.

@Avamander
Copy link
Collaborator

Could you explain why the past stuff is so important?

Expired events are not important. But that's the thing, a companion pushing 6h every 1h will cause a bunch of useless overlap. The events are technically still valid. InfiniTime doesn't know which one to purge practically. "Let's take the oldest and always wipe that" will result in "we can't even display current min-max". Which would be a pity.

As far as I know, all companion apps that integrate the weather functionality will cause a crash at one time or another. What should they have done to prevent that?

It's not a guarantee, they might though. If they keep pushing at a higher rate than gets invalidated.

They just say that at a specific moment in time, the temperature forecast for the next 6h is this value. Then, say 1 hours later, they send a new data saying that the new forecast for the next 6 hours is that other value. Is this a lie? No! It's an updated information.

But they could know their refresh interval and just say that this forecast is valid until the next refresh (+ grace period). It Would prevent a 5x-10x resource consumption.

I think from InfiniTime's perspective it is a lie if an event always becomes invalid 5h early.

If the companion app sends the same event twice, we should just keep a single copy of it.

That's fairly easy, there's likely some kind of unique identifier per calendar event. It's possible to do a targeted replacement. We don't have that for forecasts. Especially if we already can't get accurate expiration it'd be a bit much to ask for.

We have to set a maximum number of events.

Which applies here too, instead of just 1.

They do an amazing job integrating their apps with InfiniTime, and I don't think this is a great way to thank them for their work. [...] I would be very interested in knowing more how we should improve the API, the weather service and the companion app to fix the issue and extend the functionalities.

Fundamentally InfiniTime shouldn't take on this task of forecasting how long a forecast will last. It's one of the basic requirements we have to have from the companions, to get valid data. Right now it's expiricy, next it's stable unique identifiers. I can probably submit a hotfix to Gadgetbridge to restrict all events to 1h until WeatherSpec is amended to contain the information needed.

Secondly we shouldn't limit it to just one event of the same type. Rather set a limit to the total, to an amount we can actually (likely) store.

Thirdly, we can finalize the timeline control API, let them purge the timeline and start afresh if it's too much for a companion to track. That way we don't have to significantly restrict most use-cases.

@minacode
Copy link
Contributor

It's not a guarantee, they might though. If they keep pushing at a higher rate than gets invalidated.

Does this mean that they lock themselves out of pushing new values for the time they promised their data to be valid (if they are being nice)?
Especially with weather I would like the ability to update previous data when the weather suddenly changes.
Might not be relevant now, but in the future.

One middle ground between you two seems to be to tag the data with a category id and allow at most one datum per category. This ensures flushing updated values, but would give you multiple temperatures for different purposes for example.

@Avamander
Copy link
Collaborator

Avamander commented Sep 19, 2023

Does this mean that they lock themselves out of pushing new values for the time they promised their data to be valid (if they are being nice)?

No. But they might if a timeline total size is enforced. (A fresher event with the same expiration should override when selected. We would just like it not to happen 5x-10x with each event.)

@sonnatager
Copy link

sonnatager commented Sep 20, 2023

I tested this branch and it seems, that the weather is not updated if newer or data from other location data is available and send to the watch. Just the very first time (if you restart the watch) or if the data expires, it works.

@sonnatager
Copy link

Another thing, maybe out of scope:

I am using Gadgetbridge and BreezyWeather as weather app.

The cached weather data in Gadgetbridge looks like following:

2023-09-22_08-53

You can see, it is raining.
This is the watch displaying no rain:
image

My question: why is not the "Condition Code" used to decide which weather icon is displayed?
All conditions: https://openweathermap.org/weather-conditions

@Avamander
Copy link
Collaborator

My question: why is not the "Condition Code" used to decide which weather icon is displayed?

Sorry, that's not the topic of this PR. Rather a separate issue. Without GB log it's also not possible to say if precipitation got pushed to the timeline or not.

@sonnatager
Copy link

Ok, i will create a new issue for that

@JF002
Copy link
Collaborator Author

JF002 commented Sep 24, 2023

I do not use the weather feature, and I have no need for it, so I might do some incorrect assumptions when I try to understand the use-cases of this functionality.

In my mind, the most basic use-case (the one that is currently integrated in InfiniTime), consists in displaying the current weather (temperature, cloud, precipitation). The "value" displayed in InfiniTime should basically be the same than the one displayed in the companion app/weather app. If the companion app refreshes the data, I expect to see this updated data on my PineTime. Not the older value, not a processed value from the previous ones and the new one, I expect my PineTime to reflect exactly the data displayed in the companion app. If I connect my PineTime to another companion app that uses another weather data source, the forecast might be different than the first one, but I still expect my PineTime to show the same info than the companion app it's connected to.
Am I wrong here?

From what I can tell, this simple use-case does not work : the data displayed is not always the same than the one from the companion app and InfiniTime will eventually crash because of memory overflow. I've already explained why in this post and in #1822.

Before even thinking about more advanced use-cases, I would like to ensure that our design can at least support this basic use-case.

I don't like to say this, but I would like to make it clear that the InfiniTime 1.14 will integrate a weather service that is correct (regarding memory management and data displayed) by design, or no weather service at all.

I think this PR could be a good temporary workaround until we improve the implementation of the weather service. The only thing that's missing is to define how to select which single data point will be stored and displayed in the watchface.

I'm however willing to improve this PR, implement another solution or even rewrite the weather service from scratch, but I'll need much more precise use-cases and information on how we want the data to be processed by InfiniTime.

@Avamander
Copy link
Collaborator

If the companion app refreshes the data, I expect to see this updated data on my PineTime. Not the older value, not a processed value from the previous ones and the new one, I expect my PineTime to reflect exactly the data displayed in the companion app. If I connect my PineTime to another companion app that uses another weather data source, the forecast might be different than the first one, but I still expect my PineTime to show the same info than the companion app it's connected to.

This would require implementing the control endpoint (that's currently just a stub) to provide a method for wiping cached data.

Always wiping the previous data or the previous element of the same type will not work. As described previously.

Before even thinking about more advanced use-cases, I would like to ensure that our design can at least support this basic use-case.

Absolutely can support, if not for buggy companions.

I think this PR could be a good temporary workaround until we improve the implementation of the weather service. The only thing that's missing is to define how to select which single data point will be stored and displayed in the watchface.

It would cripple it when there would be better approaches.

@kieranc
Copy link
Contributor

kieranc commented Sep 27, 2023

I'm in favour of the watch managing the data stored on the timeline and pruning it in a (hopefully) intelligent way. Making changes to companion apps is vastly more complex than to InfiniTime, and unless we have a clear spec of exactly what we want to recieve, we can't reasonably expect companion apps to deliver only what we want.

In terms of min/max data, I think this is non-viable purely from the timeline for a few reasons, given expired events are purged, the best we can offer is min/max temp for some period of time, which is dependant on the data provided by the companion app, it could be 1h or 6 or 24, and if it's not clear what period the min/max values cover, it's not useful data. An option might be to store the highest and lowest values seen on the timeline over a 24h period, but if we can't store 24h of data, we can't count on the timeline to provide realistic min/max values.

Static expiry times are already less than ideal, if we could get valid expiry times via GB we would be in a much better situation. IMO, GB also needs not to push the 24h temperature data point, it serves no purpose and only confuses things.

As it stands we are only aiming to display the current temperature, cloud and precipitation data. The watch dropping all other data types seems like an obvious step, even if it is less than ideal from the viewpoint of being able to extend the weather system in the future, but if we're doing the dropping, we can also change that easily later.

I vote to make it work with what we want to do now, without making companion apps modify their behaviour which may need reverting in the future.

@Avamander
Copy link
Collaborator

Static expiry times are already less than ideal, if we could get valid expiry times via GB we would be in a much better situation.

Fixing the expiration time needs WeatherSpec changes, but we didn't get those right now. Which is very unfortunate. This is not a InfiniTime's problem to solve.

Making changes to companion apps is vastly more complex than to InfiniTime
without making companion apps modify their behaviour which may need reverting in the future.

I disagree, having written the current support in GB.

The control endpoint was also specifically defined to accommodate functionality mentioned in my previous comment, it has not just been implemented because we couldn't predict what features would be useful. Now we do.

IMO, GB also needs not to push the 24h temperature data point, it serves no purpose and only confuses things.

It's only a guess that it's 24h, to be fair. But incorrect data being pushed is indeed confusing. I will prepare a PR to slash the expiration times and modify that one in GB so that they won't cause any issues.

we can't reasonably expect companion apps to deliver only what we want.

I did intend and hopefully document it well enough that events should expire when they're no longer valid. Knowing that the refresh period is smaller than the set expiration time means that a companion is essentially sending incorrect data. We shouldn't expect pre-filtering of event types, but we should be able to expect simply correct data.

The watch dropping all other data types seems like an obvious step

I'm fine with dropping, just to be clear, it just has to be after validation and not return an error.

@JF002
Copy link
Collaborator Author

JF002 commented Nov 11, 2023

I'm closing this PR since I don't think we'll be able to fix all the issues that were mentioned.

I'm currently in the process of re-designing the service and the weather functionality and I hope to come up with a solution soon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Enhancement to an existing app/feature weather Bugs and PRs related to Weather
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants