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

Fix polar plugin #461

Merged
merged 7 commits into from
Sep 9, 2024
Merged

Fix polar plugin #461

merged 7 commits into from
Sep 9, 2024

Conversation

yatharthranjan
Copy link
Member

@yatharthranjan yatharthranjan commented Sep 5, 2024

Copy link
Member

@this-Aditya this-Aditya left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @yatharthranjan, this PR looks good, but I have identified some areas for improvement in the code merged from PR #448 that could optimize the plugin for better performance and usability.

private val ppIntervalTopic: DataCache<ObservationKey, PolarPpInterval> =
createCache("android_polar_pulse_to_pulse_interval", PolarPpInterval())
private val ppgTopic: DataCache<ObservationKey, PolarPpg> =
createCache("android_polar_ppg", PolarPpg())

private val mHandler = SafeHandler.getInstance("Polar sensors", THREAD_PRIORITY_BACKGROUND)
private var wakeLock: PowerManager.WakeLock? = null
Copy link
Member

@this-Aditya this-Aditya Sep 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need a wakelock here? We are using it with the PARTIAL_WAKE_LOCK flag, which ensures that the CPU remains active while allowing the screen to turn off. We already have this functionality with RadarService and SourceService, which always run in the background. If we still need it, the wakelock should also be released after it has been acquired.

Copy link
Member Author

@yatharthranjan yatharthranjan Sep 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have left it in for now as the app did lose connection with the device without this after a while of screen being off.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, sorry I skipped it. I’ve recommented.

Comment on lines 42 to 43

private val mHandler = SafeHandler.getInstance("Polar sensors", THREAD_PRIORITY_BACKGROUND)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please include handler.close() in the onClose method.

Comment on lines 17 to 18
override val defaultState: PolarState
get() = PolarState()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not see any use for PolarState, it could be removed and replaced with BaseSourceState instead.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have left it for now as it does set hasAcceleration to true, we can remove it in future if not required (I don't think it matters that much TBH)

manager as PolarManager
}

fun savePolarDevice(deviceId: String) {
applicationContext.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE)
Copy link
Member

@this-Aditya this-Aditya Sep 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can declare sharedPreferences as a variable to avoid repeatedly calling applicationContext.getSharedPreferences.

Suggested change
applicationContext.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE)
class PolarService : SourceService<BaseSourceState>() {
private lateinit var sharedPreferences: SharedPreferences
override fun onCreate() {
super.onCreate()
// Instantiating sharedPreferences inside onCreate to make sure that service context is available
sharedPreferences = getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE)
}
}

Comment on lines 22 to 23
handler = SafeHandler.getInstance("Polar", Process.THREAD_PRIORITY_FOREGROUND)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The handler instance is not being utilized here, we could remove it.

Comment on lines 128 to 129
status = SourceStatusListener.Status.DISCONNECTED // red circle

Copy link
Member

@this-Aditya this-Aditya Sep 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Directly setting the source status to DISCONNECTED could disrupt the logic in AbstractSourceManager and SourceService. Therefore, none of the plugins set the DISCONNECTED status directly. Instead, we might consider using AbstractSourceManager::disconnect. However, this approach could introduce issues, potentially causing a continuous loop of connecting and disconnecting the plugin.

The connection and disconnection logic can be inspired by the implementations in Empatica and Faros. For example, they override superclass methods to fit their needs, like here in E4Service.

The exact implementation logic needs to be tested on the device to ensure its effectiveness.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes this is something that needs to be worked on and tested. There are already issues related to it #453 and #454. I am going to leave this for separate PR in the future. But please feel free to add a new issue to track this.

Comment on lines 202 to 203
autoConnectDisposable = Flowable.interval(0, 5, TimeUnit.SECONDS)
.observeOn(AndroidSchedulers.mainThread())
Copy link
Member

@this-Aditya this-Aditya Sep 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not very familiar with RxJava, but we are using observeOn(AndroidSchedulers.mainThread()) in every method. It seems that this forces the code inside these methods to always execute on the main thread, which could potentially freeze the UI

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that is a good spot; I am also not very familiar with RxJava but this was provided in Polar documentation, hence being used here. I will try to remove it and test it. However, at least here it does not freeze the UI, as it only runs initially until the device is paired. Once there is a paired device, it will always connect to that and will not need scanning of devices. But I agree that sending the data could be an issue.

Copy link
Member

@this-Aditya this-Aditya Sep 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively, we can use observerOn with the main thread and simply wrap the code inside the methods using a handler if there are no UI updates.

Comment on lines 170 to 173
send(
batteryLevelTopic,
PolarBatteryLevel(name, currentTime, currentTime, batteryLevel)
)
Copy link
Member

@this-Aditya this-Aditya Sep 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Safehandler instance should be used to send data in background.

@yatharthranjan
Copy link
Member Author

Hi @this-Aditya, thanks for the comments. I also recognised various issues with the existing polar impl. but only made changes in this PR to fix the auto-connect, as there are other issues in this repo for those things that can be handled in specific PRs (and also wanted to test this in isolation). However, I will incorporate your suggestions (and delegate to separate PR/issue where suitable). Thanks

@this-Aditya
Copy link
Member

Oh, okay. I thought this was the finalized PR for the Polar plugin, which is why I mentioned these here 😅

this-Aditya
this-Aditya previously approved these changes Sep 9, 2024
Copy link
Member

@this-Aditya this-Aditya left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks.

@yatharthranjan
Copy link
Member Author

Oh, okay. I thought this was the finalized PR for the Polar plugin, which is why I mentioned these here 😅

No worries at all, very valuable feedback. I have tried to incorporate it where possible and added some other changes. This now closes #450, #454, #453.

@yatharthranjan yatharthranjan changed the title Fix polar auto-connect Fix polar plugin Sep 9, 2024
Copy link
Member

@this-Aditya this-Aditya left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @yatharthranjan for updating this PR. It looks great so far. Could you please review these additional comments and include them if necessary?

Thanks

@@ -125,8 +123,7 @@ class PolarManager(
override fun deviceDisconnected(polarDeviceInfo: PolarDeviceInfo) {
logger.debug("Device disconnected ${polarDeviceInfo.deviceId}")
isDeviceConnected = false
status = SourceStatusListener.Status.DISCONNECTED // red circle

disconnect()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not entirely sure, but does it not loop between connecting and disconnecting the manager when this callback is received? If so, we could add logic specific to the needs of this plugin ( such as handling the merging of ::disconnect with the UNAVAILABLE status or similar to this) to prevent the loop and starting from READY again during reconnections.

Please disregard this review if the behavior is functioning correctly with ::disconnect as well.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think I completely understand; when the device is disconnected, this callback is called, which sets the plugin state to disconnect. The recording is started again, and it becomes ready, looking for the device to connect again.

Here are logs showing the process

2024-09-09 16:01:27.858 19539-19539 pRMT:PolarManager       org.radarcns.detail                  D  Device disconnected CF27042A
2024-09-09 16:01:27.859 19539-19539 pRMT:SourceServiceConn* org.radarcns.detail                  I  AppSource status changed of source Polar Vantage V3 CF27042A
2024-09-09 16:01:27.859 19539-19539 pRMT:SourceServiceConn* org.radarcns.detail                  I  Updated source status to DISCONNECTING
2024-09-09 16:01:27.859 19539-19539 pRMT:RadarService       org.radarcns.detail                  I  Source of ServiceConnection<org.radarbase.passive.polar.PolarService> was updated to DISCONNECTING
2024-09-09 16:01:27.874 19539-19539 pRMT:SourceServiceConn* org.radarcns.detail                  I  AppSource status changed of source null
2024-09-09 16:01:27.874 19539-19539 pRMT:SourceServiceConn* org.radarcns.detail                  I  Updated source status to DISCONNECTED
2024-09-09 16:01:27.874 19539-19539 pRMT:RadarService       org.radarcns.detail                  I  Source of ServiceConnection<org.radarbase.passive.polar.PolarService> was updated to DISCONNECTED
2024-09-09 16:01:27.875 19539-19665 pRMT:RadarService       org.radarcns.detail                  I  Starting recording on connection ServiceConnection<org.radarbase.passive.polar.PolarService>
2024-09-09 16:01:27.880 19539-11102 pRMT:SourceService      org.radarcns.detail                  I  Starting recording now for PolarService
2024-09-09 16:01:27.883 19539-11102 pRMT:PolarManager       org.radarcns.detail                  D  Connecting to Polar API
2024-09-09 16:01:27.883 19539-19539 pRMT:SourceServiceConn* org.radarcns.detail                  I  AppSource status changed of source Polar
2024-09-09 16:01:27.883 19539-19539 pRMT:SourceServiceConn* org.radarcns.detail                  I  Updated source status to READY
2024-09-09 16:01:27.884 19539-19539 pRMT:RadarService       org.radarcns.detail                  I  Source of ServiceConnection<org.radarbase.passive.polar.PolarService> was updated to READY

I did not notice any looping in my testing.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, that’s great. I have observed this looping behavior in other plugins, but I have not worked with any wearable devices for pRMT, so I was not sure about the behavior for wearables on ::disconnect

Thanks for the logs.

@this-Aditya this-Aditya dismissed their stale review September 9, 2024 15:15

There is new work after this pr is accepted

@afolarin
Copy link
Member

afolarin commented Sep 9, 2024

Nice work 👍🏽 @this-Aditya @yatharthranjan briefed me on some of the caveats, but this will still definitely be very useful, particularly as we push for good gold standards for benchmarking studies.

@this-Aditya
Copy link
Member

Thanks @afolarin, glad to hear it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants