-
Notifications
You must be signed in to change notification settings - Fork 114
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
Refactor BLE connecting to allow scanning for vehicle presence #353
Conversation
pkg/connector/ble/ble.go
Outdated
go func() { | ||
select { | ||
case <-ctx.Done(): | ||
cancel() |
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.
Since ctx2 is created from ctx, ctx2 will be terminated automatically when ctx is done.
pkg/connector/ble/ble.go
Outdated
ch := make(chan Advertisement) | ||
fn := func(a Advertisement) { | ||
ln := a.LocalName() | ||
if ln != localName { |
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 know this this is copy/pasted, but we should just if a.LocalName() != localName
.
pkg/connector/ble/ble.go
Outdated
return | ||
} | ||
cancel() | ||
ch <- a |
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.
A bit worried about creating something like go-ble/ble#118 here. I don't believe the interface defines whether or not blocking behavior in fn
is allowed. If device.Scan
blocks on fn
, then there's a deadlock because ch
is unbuffered and nothing reads from it until device.Scan
finishes. On the other hand, if fn
runs asynchronously then we could leak a go routine if two BLE devices with localName
respond with advertisements, because the second will block indefinitely while waiting for ch
to clear.
Edit: For context, go-ble/ble@c1142dc changed the behavior of Scan
on MacOS from non-blocking to blocking, so being agnostic to this requirement would be great, and would probably allow us to uprev the ble package.
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 would need help with this change 😅. I'm new to Go and also don't have a Mac to test.
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 based this on your changes: Lenart12#1
Tested on Linux and MacOS.
pkg/connector/ble/ble.go
Outdated
var lastError error | ||
for { | ||
conn, err := tryToConnect(ctx, vin) | ||
conn, err := tryToConnect(ctx, vin, target) |
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.
Results in redundant calls to VehicleLocalName
. Call it once in NewConnectionToBleTarget
and pass the result to tryToConnect
instead of the VIN.
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 the only way its called twice is when user is calling first ScanVehicleBeacon
and then NewConnectionToBleTarget
. If calling NewConnection
(or NewConnectionToBleTarget(ctx, vin, nil)
) it is only generated once in tryToConnect
and passed to scanVehicleBeacon
. I think avoiding double generation can be solved only by requiring the user to generate the local name and passing it to both the calls, replacing the vin argument in both ScanVehicleBeacon
and TryToConnectToBleTarget
. Did you mean this?
return nil, ErrMaxConnectionsExceeded | ||
} | ||
|
||
log.Debug("Connecting to BLE beacon %s...", client.Addr()) | ||
log.Debug("Dialing to %s (%s)...", target.Addr(), localName) |
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.
Is connecting with target.Addr()
significantly faster than scanning? If so, then it might be worth modifying the API to accept target.Addr()
instead of target
. This could allow clients to cache target.Addr()
to use for future connections.
We'd need to consider being able to recover in cases where target.Addr()
changes due to a VCSEC replacement, as well as detecting ErrMaxConnectionsExceeded
. But optmizing for the common case could be useful.
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.
Good idea, but If we would count on "longer term" address caching it would make sense to also seperate this part of the tryToConnect
method to its own function, since it adds back the same pain point this PR is trying to address. Thoughts?
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 fine punting that improvement to a different PR. The reason I proposed it because implementing it as part of this one would help limit API churn.
pkg/connector/ble/ble.go
Outdated
var lastError error | ||
for { | ||
conn, err := tryToConnect(ctx, vin) | ||
conn, err := tryToConnect(ctx, vin, target) |
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.
If target.Connectable() is false, we're just going to fail in a tight loop. Should return immediately.
pkg/connector/ble/ble.go
Outdated
@@ -146,61 +230,51 @@ func NewConnection(ctx context.Context, vin string) (*Connection, error) { | |||
} | |||
} | |||
|
|||
func tryToConnect(ctx context.Context, vin string) (*Connection, error) { | |||
func tryToConnect(ctx context.Context, vin string, target Advertisement) (*Connection, error, bool) { |
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 function fails the recently merged linter checks because error
isn't the final return value. Please fix and rebase off of the latest version.
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.
Fixed linters
This likely has the side effect of fixing #272 by removing the direct call to |
The MacOS BLE Scan() method blocks until all queued instances of the callback function finish. This created a deadlock, since the callback function would try to write a matching advertisement to an unbuffered channel, and the unbuffered channel would not be read until Scan() completed. To fix the problem, this commit makes the channel buffered, allowing the callback function to succeed on the first match. The callback handles subsequent matches by detecting that the scan context has been canceled and returning. This commit also uprevs the BLE package. This was previously impossible because we relied on the device.Connect method, which is broken upstream on MacOS.
Thanks, @Lenart12 ! I think we ended up killing a couple of very annoying birds in the process with this one. |
Is updating the tesla-control CLI docs planned? I am not sure how I should check for the presence of the car. Also, how to check change the timeout and what is the default timeout? |
Description
This PR splits the
ble.tryToConnect
method in to two parts, by factoring out its first step in which we scan for a suitable tesla BLE advertisement (beacon). This is achieved by putting the code in a newble.scanVehicleBeacon
method along with other smaller refactorings. This allows us to expose the new method withble.ScanVehicleBeacon
.The end user can then use a different (shorter) timeout to check for the presence of the vehicle before deciding if it wants to try to connect, since if a beacon got detected it would make sense to use a longer timeout to connect, or even do multiple retries, and if it wasn't detected allow for faster error handling.
Example
Type of change
Please select all options that apply to this change:
Checklist:
Confirm you have completed the following steps: