-
Notifications
You must be signed in to change notification settings - Fork 6
Add Larod #120
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
base: main
Are you sure you want to change the base?
Add Larod #120
Conversation
Just adding a comment that I am still relatively new to Rust, so suggestions to improve my code is very welcome. I will also be learning about functionality of the larod library during the process of creating this wrapper. So, guidance on interfacing with larod is also very welcome. |
Looking at the larodError documentation a bit more closely, I realize what I have so far won't work, and maybe larod cannot be wrapped? Notably larodError, which is passed to nearly every function, must be uninitialized. It might be impossible for rust to produce such a pointer https://doc.rust-lang.org/std/mem/union.MaybeUninit.html. |
Since you say the code is not ready I will focus on answering your questions and assist you when you ask for it for now. Consequently I haven't looked much at the code but I address your comments below.
Diving in at the deep end with unsafe. A word of warning; In my opinion unsafe combines the worst of C(++) and Rust, and writing safe wrappers is especially frustrating since many of the safety aspects that we need to guarantee are not documented - sometimes not even considered - by the library authors. To make matters worse, we often cannot even test empirically if our code is correct because UB does not mean that it will segfault or do anything weird right now, it means it could do it now or at any point in the future. So, know know what you are getting yourself into, that it is ok to quit and don't let this experience discourage you from using Rust because there's another side to the language that is carefree and fun 🌞 🌈 What is your programming background? I will try to adjust my communication accordingly.
That makes two of us.
I have found that sometimes the documentation should be taken with a grain of salt (I have found errors) and other times it is important to read it carefully. In any case the examples can be a good supporting resource. in larodError* error = NULL;
// ...
if (!larodConnect(&conn, &error)) { We clearly don't pass an uninitialized variable but a pointer to null. I think this is what the documentation means when it says "An uninitialized handle to an error". Compare this to GError* error = NULL;
// ...
if (!ax_event_handler_declare(event_handler,
// ...
&error)) { Footnotes |
Thanks for the feedback. My background is varied engineering (R&D, so jack of all trades master of none sort of thing). I've written production C++ for embedded microcontrollers, C++ for AI using OpenCV and libtorch, lots of Python, and a few web backend type services using Rust. Again, I'm no expert, but I've done a lot of dabbling in various things. I'm ok diving into the deep end. Just more to learn hopefully :). One piece from the larod docs that I worry a bit about is this quote, "error can also be NULL if one does not want any error information." I worry that if I initialize the pointer to Null, we'll just never get any error information back. |
I'm comparing to axevent again because I recently worked on those bindings. I think this is what passing a null pointer would look like: https://github.com/AxisCommunications/acap-native-sdk-examples/blob/9b00b2fdf23672f8910421653706572201c2ed8b/axevent/send_event/app/send_event.c#L167 Compare that to passing a (not-null) pointer to a null pointer: https://github.com/AxisCommunications/acap-native-sdk-examples/blob/9b00b2fdf23672f8910421653706572201c2ed8b/axevent/send_event/app/send_event.c#L176 |
… the pointer to it is non-NULL. - Added some documentation. - Mimicked the try_from macro from the axevent crate to DRY the code.
- Stubbed out functions for Session (larodConnection) - Start implementing a few functions to list hand handle larodDevices
- Add testing documentation to README.md - Add runner specification to .cargo/config.toml (new) - Set `aarch64-unknown-linux-gnu` to default build target. - Add remote-test.sh script to run tests on remote target.
@apljungquist I just set up the P1468-LE camera and set up a remote testing workflow. I've added some documentation and configuration files in this commit. It might be useful for you in your endeavors. I have confirmed it works for the larod library so far on the P1468-LE running OS 11.11.73. It might be worth considering as a cherry pick, or I could open a separate merge request for it.
running 10 tests test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s |
Neat. I didn’t know about I currently think of tests in two categories:
Were you aware of |
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 like the simplicity of this script but before I make any suggestions about how to upstream it I have a couple of questions to make sure I understand it.
- Change error handling to better check for a non-NULL larodError pointer. - Add some documentation for LarodMap functions. - Set LarodMap functions to public. - Add some additional tests. - Hide on-device tests behind a device-tests feature. - Add a basic example.
Using that may ultimately be a better approach than using the runner as discussed above. |
…sion from which it was acquired.
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.
Happy to see you are still making progress. I dropped a couple of comments on superficial stuff that came to mind while skimming. Do with them as you please.
@@ -589,7 +601,29 @@ impl<'a> Tensor<'a> { | |||
Err(maybe_error.unwrap_or(Error::MissingLarodError)) | |||
} | |||
} | |||
pub fn set_pitches() {} | |||
pub fn set_pitches(&mut self, pitches: &[usize]) -> Result<()> { |
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.
Did you know you can make a function generic over N
?
I haven't looked closely at what we are trying to do here, so I can't say if it would help or be wise, just some basic pattern matching going on in my brain.
https://doc.rust-lang.org/reference/items/generics.html#const-generics
P: AsRef<Path>, | ||
{ | ||
let f = File::open(model_file).map_err(Error::IOError)?; | ||
let Ok(device_name) = CString::new(<(M, H)>::as_str()) 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.
Are you planning to use the result of as_str
for anything other than creating a CString
?
If not you may be interested to know that there are c-string literals these days.
f422122
to
96b2918
Compare
@apljungquist In general this crate is "done" in the sense that the vast majority of the functions and functionality exist. I have also documented most things. I didn't go into as much detail as the official documentation, but included a link to the Axis ACAP page. I guess I'll mark it as ready for review and I can address any shortcomings. There are still a few things I'm not keen on, and a few that I'm not sure how best to implement. ErgonomicsFirst, because the type implementing for buffer in running_stream.iter() {
if let Some(ft) = preprocessor.input_tensors_mut().and_then(|it| it.first_mut()) {
ft.copy_from_slice(frame_slice);
job_request.run()?
}
} I was hopeful for something like this: let preproc_tensor_iter = preprocessor.input_tensors_mut()?.iter_mut()?;
let preproc_input_tensor = preproc_tensor_iter.next()?
for buffer in running_stream.iter() {
...
} But then, since Access MethodsSecond, the larod library has a Nevertheless, if one were to call fn get_model(model_id: u64) -> Box<dyn LarodModel> {
if preprocessor_model {
Box::new(Preprocessor {})
} else {
Box::new(InferenceModel {})
}
} This would be a particularly involved function if we need to map the file descriptors and everything back into an |
That's great news. I'll take a look at it this weekend. It would be nice with an example app that demonstrates that it works and how to use it. |
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 it's cool that we are getting close to having larod bindings and appreciate your effort. The PR is on the big side and it would be nice to split it up somehow to make it easier to reason about and keep moving forward. One thing that seems reasonable to chip off is the sys-crate
, so I've focused on that this morning. I'm thinking that we can discuss it in this PR and once we are happy we isolate it and merge. What do you think?
On the topic of how to review, do you have any requests of suggestions for me?
crates/larod/examples/basic.rs
Outdated
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 don't have a camera at hand, but I guess these examples are meant to be executed using cargo run
and relying on remote-test.sh
for them to execute on the camera. Is that correct?
Would it be possible to package them as example apps?
Do you see any drawback to doing so?
Advantages in my opinion include:
- It is consistent with the rest of the project where most programs that are intended to run on the camera are packaged as applications.
- It communicates that the examples can run as normal applications with all the restrictions that entail, and shows how to set up a manifest that allows it.
@@ -0,0 +1,141 @@ | |||
//! # Simple example |
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.
Nice, detailed instructions. I look forward to trying them when I have a camera at hand.
crates/larod-sys/build.rs
Outdated
.allowlist_recursively(false) | ||
.allowlist_function("^(larod.*)$") | ||
.allowlist_type("^(_?larod.*)$") | ||
.default_enum_style(bindgen::EnumVariation::Rust { |
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 don't think we should use Rust style for enums that are returned from foreign functions because it would be undefined behavior if the foreign function adds a new variant. What do you think?
crates/larod-sys/src/lib.rs
Outdated
#[allow(non_camel_case_types)] | ||
#[repr(u32)] | ||
#[derive(Debug)] | ||
pub enum FDAccessFlag { |
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.
Why are these types in the sys
crate?
The acap-rs workspace doesn't currently provide wrappers for the larod library. When it is ready, I hope this merge request will provide that.
Immediate goals:
Since larod uses peripheral hardware, I plan to build the tests and execute them on an Axis P1468-LE.
Checklist before requesting a review