-
Notifications
You must be signed in to change notification settings - Fork 27
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
Improve characteristic interactions #151
Improve characteristic interactions #151
Conversation
I have another approach which would be to implement the characteristic functions on the server directly rather than on the services. This circumvents all of the issues with referencing and ambiguous types, but has its own set of compromises:
|
Might be worth change the controller-specific error into a different type based on ErrorType::kind() (impemented by Controller traits), so that you can avoid the generics? In practice, what benefits does this approach give vs. the existing? From the example, it seem like it would change
to
Personally I'm not sure why this is better, but maybe there are better examples to illustrate the advantage of this change. I would also like to say, that if you need to make significant changes to make the API better, please do not hesitate to do that. |
I'm inclined to agree with you. There is still the benefit that these helper functions use the characteristic's type rather than requiring the user to convert to and from byte slices.
I appreciate the endorsement! I'm certainly willing to do this, but I think at present I'm not sure I have a clear enough vision of how it should look. |
Yeah, regarding the use of singletons, I think it should be possible to avoid that, but it does indeed require some experimentation to learn what the ideal structure is. I like the structure of the stuff below GATT at the moment, but maybe for GATT it's worth putting some additional constraints in order to provide a better API. So maybe the best course of action is to define a few directions for experimenting. |
I agree that singletons tend not to work well in Rust so should be avoided if possible. It seems like the main virtue of this API is to allow you to use the internal type of the attribute rather than a byte array. Perhaps an alternative like: trait AttributeHandle {
type Value: ToGatt;
fn raw_handle(&self) -> u16;
}
trait CharacteristicHandle: AttributeHandle {}
struct GattServer {
...
async fn notify<T: CharacteristicHandle>(&self, handle: &T, value: &T::Value) -> Result<...> {
...
}
}
What if instead the struct generated by the |
I like both of these suggestions, I think I'll use this PR to bring in the GattValue traits and extend the existing |
This reverts commit 3bd5d74.
The main difference between the callbacks and server.next() is that the callbacks trigger before the event is processed. Hopefully the new signature of the write callback allowing the application code a say in accepting / rejecting the request helps to differentiate it and give it more of a unique purpose too. I did wonder whether there were any other parameters we might want to provide to the callbacks to increase their usefulness. If anyone has any ideas I'd be keen to hear them. I did originally try to implement them as async, but I can't use the |
Just a quick update to say that I tried this but |
/test |
Agent is back up now. |
/test |
…dysonltd/trouble into consolidate-characteristic-interactions
/test |
/test |
…echanism - happy path
/test |
host/src/attribute.rs
Outdated
self.iterate(|mut it| { | ||
while let Some(att) = it.next() { | ||
if att.handle == handle.handle { | ||
if let AttributeData::Data { props, value } = &mut att.data { | ||
assert_eq!(value.len(), input.len()); | ||
value.copy_from_slice(input); | ||
assert_eq!(value.len(), gatt_value.len()); |
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 assert should probably be removed now we're returning a result. We can bubble this error check up.
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.
host/src/attribute.rs
Outdated
self.iterate(|mut it| { | ||
while let Some(att) = it.next() { | ||
if att.handle == handle.handle { | ||
if let AttributeData::Data { props, value } = &mut att.data { | ||
let v = f(value); | ||
// Type is inferred from the Characteristic so Result can be unwrapped here | ||
let v = <T as GattValue>::from_gatt(value).unwrap(); |
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 unwrap can be handled
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.
/test |
@petekubiak I just switched my application to your branch! One thing I noticed is that |
Thanks for this, real-world testing is always appreciated! Yes, seems like that change can be made without changing anything else in the code - good spot! |
/test |
I do think that being able to use async in the callbacks would provide huge benefit. I was talking to @jamessizeland about this yesterday and we realised that |
If you require async, you could also use the server.next() and handle it there. I think with Box you'd anyway end up needing to store the value first for the future to reach it. I think some kind of async iterator API is more powerful than the callbacks and more natural to async, so if we can get some way to poll characteristics for events or the like, maybe that could be an alternative. |
This work has evolved considerably from the initial proposal I gave in #136 as my understanding of the code base has grown.
The scope of this PR is to make use of the new macro-style declaration of services and characteristics to improve the server's API. This has led to the following changes:
on_read
andon_write
callbacks have been implemented for attributes. These will be automatically called when a read or write event occurs on the attribute and have the following signatures: