Skip to content

Commit

Permalink
host_wasmtime: support multiple sensor devices per backend
Browse files Browse the repository at this point in the history
  • Loading branch information
yamt committed Dec 26, 2023
1 parent 0883780 commit 1b73a63
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 17 deletions.
13 changes: 13 additions & 0 deletions host_wasmtime/src/dummy_device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use wasi::sensor::sensor::DeviceError;

use crate::traits::BufferPool;
use crate::traits::SensorDevice;
use crate::traits::SensorDeviceGroup;

#[derive(Clone)]
pub struct DummyDeviceConfig {
Expand All @@ -20,6 +21,8 @@ pub struct DummyDeviceConfig {
pub frame_duration: Duration,
}

pub struct DummyDeviceGroup {}

pub struct DummyDevice {
pub pool: Arc<Mutex<Option<Arc<dyn BufferPool + Send + Sync>>>>,
pub config: Arc<Mutex<DummyDeviceConfig>>,
Expand All @@ -38,6 +41,16 @@ impl DummyDevice {
}
}

impl SensorDeviceGroup for DummyDeviceGroup {
fn list_devices(&self) -> Result<Vec<String>, DeviceError> {
Ok(vec!["dummy".to_string()])
}
fn open_device(&self, name: &str) -> Result<Box<dyn SensorDevice + Send + Sync>, DeviceError> {
let dev = DummyDevice::new()?;
Ok(Box::new(dev))
}
}

fn generate_dummy_image(frame_no: u64, xsz: u32, ysz: u32) -> (u32, u32, Vec<u8>) {
let mut img = image::RgbImage::new(xsz, ysz);
let thresh = (xsz as f32 * (frame_no % 100) as f32 / 100 as f32) as u32;
Expand Down
52 changes: 37 additions & 15 deletions host_wasmtime/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@ mod pool;
mod traits;

#[cfg(feature = "dummy")]
use dummy_device::DummyDevice;
use dummy_device::DummyDeviceGroup;

#[cfg(feature = "nokhwa")]
use nokhwa::NokhwaDevice;
use nokhwa::NokhwaDeviceGroup;

use pool::SimplePool;
use traits::SensorDevice;

Expand All @@ -45,6 +47,9 @@ wasmtime::component::bindgen!({
trait WasiSensorView {
fn table(&mut self) -> &mut Table;
fn pools(&mut self) -> &mut HashMap<String, Arc<dyn traits::BufferPool + Send + Sync>>;
fn device_groups(
&mut self,
) -> &HashMap<String, Box<dyn traits::SensorDeviceGroup + Send + Sync>>;
}

pub struct Pool {
Expand Down Expand Up @@ -193,19 +198,16 @@ impl<T: WasiSensorView> wasi::sensor::sensor::HostDevice for T {
) -> Result<Result<Resource<wasi::sensor::sensor::Device>, wasi::sensor::sensor::DeviceError>>
{
trace!("opening a device {}", device_name);
let device_impl: Box<dyn SensorDevice + Send + Sync> = match &*device_name {
#[cfg(feature = "dummy")]
"dummy" => Box::new(match DummyDevice::new() {
Err(e) => return Ok(Err(e)),
Ok(i) => i,
}),
#[cfg(feature = "nokhwa")]
"nokhwa" => Box::new(match NokhwaDevice::new() {
Err(e) => return Ok(Err(e)),
Ok(i) => i,
}),
_ => return Ok(Err(wasi::sensor::sensor::DeviceError::NotFound)),
let v: Vec<&str> = device_name.split(":").collect();
if v.len() != 2 {
return Ok(Err(wasi::sensor::sensor::DeviceError::NotFound));
}
let group_name = v[0];
let name = v[1];
let Some(ref group) = self.device_groups().get(group_name) else {
return Ok(Err(wasi::sensor::sensor::DeviceError::NotFound));
};
let device_impl = group.open_device(name)?;
let device = Device {
device: device_impl,
};
Expand All @@ -214,7 +216,13 @@ impl<T: WasiSensorView> wasi::sensor::sensor::HostDevice for T {
}

fn list_names(&mut self) -> Result<Result<Vec<String>, wasi::sensor::sensor::DeviceError>> {
Ok(Ok(vec!["dummy".to_string(), "nokhwa".to_string()]))
let mut names = Vec::new();
for (k, v) in self.device_groups() {
for n in v.list_devices()? {
names.push(format!("{}:{}", k, n));
}
}
Ok(Ok(names))
}

fn start(
Expand Down Expand Up @@ -269,6 +277,7 @@ struct State {
wasi: WasiCtx,
table: Table,
pools: HashMap<String, Arc<dyn traits::BufferPool + Send + Sync>>,
device_groups: HashMap<String, Box<dyn traits::SensorDeviceGroup + Send + Sync>>,
}

impl WasiView for State {
Expand All @@ -293,6 +302,11 @@ impl WasiSensorView for State {
fn pools(&mut self) -> &mut HashMap<String, Arc<dyn traits::BufferPool + Send + Sync>> {
&mut self.pools
}
fn device_groups(
&mut self,
) -> &HashMap<String, Box<dyn traits::SensorDeviceGroup + Send + Sync>> {
&self.device_groups
}
}

fn main() -> Result<()> {
Expand Down Expand Up @@ -340,13 +354,21 @@ fn main() -> Result<()> {
println!("add wasi");
wasmtime_wasi::preview2::command::sync::add_to_linker(&mut linker)?;

let mut device_groups: HashMap<String, Box<dyn traits::SensorDeviceGroup + Send + Sync>> =
HashMap::new();
#[cfg(feature = "dummy")]
device_groups.insert("dummy".to_string(), Box::new(DummyDeviceGroup {}));
#[cfg(feature = "nokhwa")]
device_groups.insert("nokhwa".to_string(), Box::new(NokhwaDeviceGroup {}));

println!("prepare a store");
let mut store = Store::new(
&engine,
State {
wasi: wasi_ctx,
table: Table::new(),
pools: HashMap::new(),
device_groups: device_groups,
},
);

Expand Down
37 changes: 35 additions & 2 deletions host_wasmtime/src/nokhwa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,51 @@ use nokhwa::CallbackCamera;

use crate::traits::BufferPool;
use crate::traits::SensorDevice;
use crate::traits::SensorDeviceGroup;

pub struct NokhwaDeviceGroup {}

pub struct NokhwaDevice {
camera: CallbackCamera,
}

impl SensorDeviceGroup for NokhwaDeviceGroup {
fn list_devices(&self) -> Result<Vec<String>, DeviceError> {
let mut names = Vec::new();
if let Some(backend) = nokhwa::native_api_backend() {
for device in nokhwa::query(backend)? {
names.push(match device.index() {
CameraIndex::Index(n) => format!("I{}", n),
CameraIndex::String(s) => format!("S{}", s),
})
}
}
Ok(names)
}
fn open_device(&self, name: &str) -> Result<Box<dyn SensorDevice + Send + Sync>, DeviceError> {
let dev = NokhwaDevice::new(name)?;
Ok(Box::new(dev))
}
}

impl NokhwaDevice {
pub fn new() -> Result<Self, DeviceError> {
pub fn new(name: &str) -> Result<Self, DeviceError> {
if !name.is_char_boundary(1) {
return Err(wasi::sensor::sensor::DeviceError::NotFound);
}
let (name_type, name) = name.split_at(1);
let index = match name_type {
"I" => CameraIndex::Index(
name.parse::<u32>()
.or(Err(wasi::sensor::sensor::DeviceError::NotFound))?,
),
"S" => CameraIndex::String(name.to_string()),
_ => return Err(wasi::sensor::sensor::DeviceError::NotFound),
};
nokhwa::nokhwa_initialize(|granted| {
println!("granted: {}", granted);
});
println!("NokhwaDevice granted");
let index = CameraIndex::Index(0); // XXX should not hardcode
let requested =
RequestedFormat::new::<RgbFormat>(RequestedFormatType::AbsoluteHighestFrameRate);
println!("NokhwaDevice creating a threaded camera");
Expand Down
5 changes: 5 additions & 0 deletions host_wasmtime/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ pub trait BufferPool {
fn get_statistics(&self) -> Result<PoolStatistics, Error>;
}

pub trait SensorDeviceGroup {
fn list_devices(&self) -> Result<Vec<String>, DeviceError>;
fn open_device(&self, name: &str) -> Result<Box<dyn SensorDevice + Send + Sync>, DeviceError>;
}

pub trait SensorDevice {
fn start_streaming(
&mut self,
Expand Down

0 comments on commit 1b73a63

Please sign in to comment.