feat: usb mode, battery percentage
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
This commit is contained in:
@@ -1,3 +1,7 @@
|
||||
use std::{error::Error, fmt};
|
||||
|
||||
use crate::usb;
|
||||
|
||||
use super::common::file::{Input, Output};
|
||||
use clap::Subcommand;
|
||||
|
||||
@@ -17,3 +21,36 @@ pub enum BackupCmd {
|
||||
input_file: Input,
|
||||
},
|
||||
}
|
||||
|
||||
fn handle_export(
|
||||
device_id: Option<&str>,
|
||||
output: Output,
|
||||
) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
let camera = usb::get_camera(device_id)?;
|
||||
|
||||
let mut writer = output.get_writer()?;
|
||||
|
||||
todo!();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_import(
|
||||
device_id: Option<&str>,
|
||||
input: Input,
|
||||
) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
let camera = usb::get_camera(device_id)?;
|
||||
|
||||
let mut reader = input.get_reader()?;
|
||||
|
||||
todo!();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn handle(cmd: BackupCmd, device_id: Option<&str>) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
match cmd {
|
||||
BackupCmd::Export { output_file } => handle_export(device_id, output_file),
|
||||
BackupCmd::Import { input_file } => handle_import(device_id, input_file),
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
use std::{error::Error, path::PathBuf, str::FromStr};
|
||||
use std::{error::Error, fs::File, io, path::PathBuf, str::FromStr};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Input {
|
||||
@@ -17,6 +17,15 @@ impl FromStr for Input {
|
||||
}
|
||||
}
|
||||
|
||||
impl Input {
|
||||
pub fn get_reader(&self) -> Result<Box<dyn io::Read>, Box<dyn Error + Send + Sync>> {
|
||||
match self {
|
||||
Input::Stdin => Ok(Box::new(io::stdin())),
|
||||
Input::Path(path) => Ok(Box::new(File::open(path)?)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Output {
|
||||
Path(PathBuf),
|
||||
@@ -33,3 +42,12 @@ impl FromStr for Output {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Output {
|
||||
pub fn get_writer(&self) -> Result<Box<dyn io::Write>, Box<dyn Error + Send + Sync>> {
|
||||
match self {
|
||||
Output::Stdout => Ok(Box::new(io::stdout())),
|
||||
Output::Path(path) => Ok(Box::new(File::create(path)?)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -3,30 +3,30 @@ use std::{error::Error, fmt};
|
||||
use clap::Subcommand;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::usb;
|
||||
use crate::{hardware::FujiUsbMode, usb};
|
||||
|
||||
#[derive(Subcommand, Debug)]
|
||||
pub enum DeviceCmd {
|
||||
/// List devices
|
||||
/// List cameras
|
||||
#[command(alias = "l")]
|
||||
List,
|
||||
|
||||
/// Dump device info
|
||||
/// Get camera info
|
||||
#[command(alias = "i")]
|
||||
Info,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct DeviceItemRepr {
|
||||
pub struct CameraItemRepr {
|
||||
pub name: String,
|
||||
pub id: String,
|
||||
pub vendor_id: String,
|
||||
pub product_id: String,
|
||||
}
|
||||
|
||||
impl From<&usb::Device> for DeviceItemRepr {
|
||||
fn from(device: &usb::Device) -> Self {
|
||||
DeviceItemRepr {
|
||||
impl From<&usb::Camera> for CameraItemRepr {
|
||||
fn from(device: &usb::Camera) -> Self {
|
||||
CameraItemRepr {
|
||||
id: device.id(),
|
||||
name: device.name(),
|
||||
vendor_id: format!("0x{:04x}", device.vendor_id()),
|
||||
@@ -35,7 +35,7 @@ impl From<&usb::Device> for DeviceItemRepr {
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for DeviceItemRepr {
|
||||
impl fmt::Display for CameraItemRepr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
@@ -45,24 +45,24 @@ impl fmt::Display for DeviceItemRepr {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_list(json: bool) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
let devices: Vec<DeviceItemRepr> = usb::get_connected_devices()?
|
||||
fn handle_list(json: bool) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
let cameras: Vec<CameraItemRepr> = usb::get_connected_camers()?
|
||||
.iter()
|
||||
.map(|d| d.into())
|
||||
.collect();
|
||||
|
||||
if json {
|
||||
println!("{}", serde_json::to_string_pretty(&devices)?);
|
||||
println!("{}", serde_json::to_string_pretty(&cameras)?);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if devices.is_empty() {
|
||||
println!("No supported devices connected.");
|
||||
if cameras.is_empty() {
|
||||
println!("No supported cameras connected.");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
println!("Connected devices:");
|
||||
for d in devices {
|
||||
println!("Connected cameras:");
|
||||
for d in cameras {
|
||||
println!("- {}", d);
|
||||
}
|
||||
|
||||
@@ -70,29 +70,19 @@ pub fn handle_list(json: bool) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct DeviceRepr {
|
||||
pub struct CameraRepr {
|
||||
#[serde(flatten)]
|
||||
pub device: DeviceItemRepr,
|
||||
pub device: CameraItemRepr,
|
||||
|
||||
pub manufacturer: String,
|
||||
pub model: String,
|
||||
pub device_version: String,
|
||||
pub serial_number: String,
|
||||
pub mode: FujiUsbMode,
|
||||
pub battery: u32,
|
||||
}
|
||||
|
||||
impl DeviceRepr {
|
||||
pub fn from_info(device: &usb::Device, info: &libptp::DeviceInfo) -> Self {
|
||||
DeviceRepr {
|
||||
device: device.into(),
|
||||
manufacturer: info.Manufacturer.clone(),
|
||||
model: info.Model.clone(),
|
||||
device_version: info.DeviceVersion.clone(),
|
||||
serial_number: info.SerialNumber.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for DeviceRepr {
|
||||
impl fmt::Display for CameraRepr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
writeln!(f, "Name: {}", self.device.name)?;
|
||||
writeln!(f, "ID: {}", self.device.id)?;
|
||||
@@ -103,20 +93,29 @@ impl fmt::Display for DeviceRepr {
|
||||
)?;
|
||||
writeln!(f, "Manufacturer: {}", self.manufacturer)?;
|
||||
writeln!(f, "Model: {}", self.model)?;
|
||||
writeln!(f, "Device Version: {}", self.device_version)?;
|
||||
write!(f, "Serial Number: {}", self.serial_number)
|
||||
writeln!(f, "Version: {}", self.device_version)?;
|
||||
writeln!(f, "Serial Number: {}", self.serial_number)?;
|
||||
writeln!(f, "Mode: {}", self.mode)?;
|
||||
write!(f, "Battery: {}%", self.battery)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_info(
|
||||
json: bool,
|
||||
device_id: Option<&str>,
|
||||
) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
let device = usb::get_device(device_id)?;
|
||||
fn handle_info(json: bool, device_id: Option<&str>) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
let camera = usb::get_camera(device_id)?;
|
||||
|
||||
let mut camera = device.camera()?;
|
||||
let info = device.model.get_device_info(&mut camera)?;
|
||||
let repr = DeviceRepr::from_info(&device, &info);
|
||||
let info = camera.get_info()?;
|
||||
let mode = camera.get_fuji_usb_mode()?;
|
||||
let battery = camera.get_fuji_battery_info()?;
|
||||
|
||||
let repr = CameraRepr {
|
||||
device: (&camera).into(),
|
||||
manufacturer: info.Manufacturer.clone(),
|
||||
model: info.Model.clone(),
|
||||
device_version: info.DeviceVersion.clone(),
|
||||
serial_number: info.SerialNumber.clone(),
|
||||
mode,
|
||||
battery,
|
||||
};
|
||||
|
||||
if json {
|
||||
println!("{}", serde_json::to_string_pretty(&repr)?);
|
||||
|
@@ -1,3 +0,0 @@
|
||||
use std::time::Duration;
|
||||
|
||||
pub const TIMEOUT: Duration = Duration::from_millis(500);
|
@@ -1,36 +1,194 @@
|
||||
use std::error::Error;
|
||||
use std::{error::Error, fmt, time::Duration};
|
||||
|
||||
use libptp::{DeviceInfo, StandardCommandCode};
|
||||
use log::debug;
|
||||
use rusb::GlobalContext;
|
||||
use rusb::{DeviceDescriptor, GlobalContext};
|
||||
use serde::Serialize;
|
||||
|
||||
mod common;
|
||||
mod xt5;
|
||||
|
||||
pub trait Camera {
|
||||
fn vendor_id(&self) -> u16;
|
||||
fn product_id(&self) -> u16;
|
||||
fn name(&self) -> &'static str;
|
||||
pub const TIMEOUT: Duration = Duration::from_millis(500);
|
||||
|
||||
fn get_device_info(
|
||||
&self,
|
||||
camera: &mut libptp::Camera<GlobalContext>,
|
||||
) -> Result<DeviceInfo, Box<dyn Error + Send + Sync>> {
|
||||
debug!("Using default GetDeviceInfo command for {}", self.name());
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum DevicePropCode {
|
||||
FujiUsbMode = 0xd16e,
|
||||
FujiBatteryInfo1 = 0xD36A,
|
||||
FujiBatteryInfo2 = 0xD36B,
|
||||
}
|
||||
|
||||
let response = camera.command(
|
||||
StandardCommandCode::GetDeviceInfo,
|
||||
&[],
|
||||
None,
|
||||
Some(common::TIMEOUT),
|
||||
)?;
|
||||
#[derive(Debug, Clone, Copy, Serialize)]
|
||||
pub enum FujiUsbMode {
|
||||
RawConversion, // mode == 6
|
||||
Unsupported,
|
||||
}
|
||||
|
||||
debug!("Received response with {} bytes", response.len());
|
||||
|
||||
let device_info = DeviceInfo::decode(&response)?;
|
||||
|
||||
Ok(device_info)
|
||||
impl From<u32> for FujiUsbMode {
|
||||
fn from(val: u32) -> Self {
|
||||
match val {
|
||||
6 => FujiUsbMode::RawConversion,
|
||||
_ => FujiUsbMode::Unsupported,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const SUPPORTED_MODELS: &[&dyn Camera] = &[&xt5::FujifilmXT5];
|
||||
impl fmt::Display for FujiUsbMode {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let s = match self {
|
||||
FujiUsbMode::RawConversion => "USB RAW CONV./BACKUP RESTORE",
|
||||
FujiUsbMode::Unsupported => "Unsupported USB Mode",
|
||||
};
|
||||
write!(f, "{}", s)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CameraImpl {
|
||||
fn name(&self) -> &'static str;
|
||||
|
||||
fn next_session_id(&self) -> u32;
|
||||
|
||||
fn open_session(
|
||||
&self,
|
||||
ptp: &mut libptp::Camera<GlobalContext>,
|
||||
session_id: u32,
|
||||
) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
debug!("Opening new session with id {}", session_id);
|
||||
ptp.command(
|
||||
StandardCommandCode::OpenSession,
|
||||
&[session_id],
|
||||
None,
|
||||
Some(TIMEOUT),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn close_session(
|
||||
&self,
|
||||
ptp: &mut libptp::Camera<GlobalContext>,
|
||||
) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
debug!("Closing session");
|
||||
ptp.command(StandardCommandCode::CloseSession, &[], None, Some(TIMEOUT))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_prop_value_raw(
|
||||
&self,
|
||||
ptp: &mut libptp::Camera<GlobalContext>,
|
||||
prop: DevicePropCode,
|
||||
) -> Result<Vec<u8>, Box<dyn Error + Send + Sync>> {
|
||||
let session_id = self.next_session_id();
|
||||
self.open_session(ptp, session_id)?;
|
||||
|
||||
debug!("Getting property {:?}", prop);
|
||||
|
||||
let response = ptp.command(
|
||||
StandardCommandCode::GetDevicePropValue,
|
||||
&[prop as u32],
|
||||
None,
|
||||
Some(TIMEOUT),
|
||||
);
|
||||
|
||||
self.close_session(ptp)?;
|
||||
|
||||
let response = response?;
|
||||
debug!("Received response with {} bytes", response.len());
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
fn get_prop_value_scalar(
|
||||
&self,
|
||||
ptp: &mut libptp::Camera<GlobalContext>,
|
||||
prop: DevicePropCode,
|
||||
) -> Result<u32, Box<dyn Error + Send + Sync>> {
|
||||
let data = self.get_prop_value_raw(ptp, prop)?;
|
||||
|
||||
match data.len() {
|
||||
1 => Ok(data[0] as u32),
|
||||
2 => Ok(u16::from_le_bytes([data[0], data[1]]) as u32),
|
||||
4 => Ok(u32::from_le_bytes([data[0], data[1], data[2], data[3]])),
|
||||
n => Err(format!("Cannot parse property {:?} as scalar: {} bytes", prop, n).into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_fuji_usb_mode(
|
||||
&self,
|
||||
ptp: &mut libptp::Camera<GlobalContext>,
|
||||
) -> Result<FujiUsbMode, Box<dyn Error + Send + Sync>> {
|
||||
let result = self.get_prop_value_scalar(ptp, DevicePropCode::FujiUsbMode)?;
|
||||
Ok(result.into())
|
||||
}
|
||||
|
||||
fn get_fuji_battery_info(
|
||||
&self,
|
||||
ptp: &mut libptp::Camera<GlobalContext>,
|
||||
) -> Result<u32, Box<dyn Error + Send + Sync>> {
|
||||
let data = self.get_prop_value_raw(ptp, DevicePropCode::FujiBatteryInfo2)?;
|
||||
debug!("Raw battery data: {:?}", data);
|
||||
|
||||
if data.len() < 3 {
|
||||
return Err("Battery info payload too short".into());
|
||||
}
|
||||
|
||||
let utf16: Vec<u16> = data[1..]
|
||||
.chunks(2)
|
||||
.map(|chunk| u16::from_le_bytes([chunk[0], chunk[1]]))
|
||||
.take_while(|&c| c != 0)
|
||||
.collect();
|
||||
|
||||
debug!("Decoded UTF-16 units: {:?}", utf16);
|
||||
|
||||
let utf8_string = String::from_utf16(&utf16)?;
|
||||
debug!("Decoded UTF-16 string: {}", utf8_string);
|
||||
|
||||
let percentage: u32 = utf8_string
|
||||
.split(',')
|
||||
.next()
|
||||
.ok_or("Failed to parse battery percentage")?
|
||||
.parse()?;
|
||||
|
||||
Ok(percentage)
|
||||
}
|
||||
|
||||
fn get_info(
|
||||
&self,
|
||||
ptp: &mut libptp::Camera<GlobalContext>,
|
||||
) -> Result<DeviceInfo, Box<dyn Error + Send + Sync>> {
|
||||
debug!("Sending GetDeviceInfo command");
|
||||
let response = ptp.command(StandardCommandCode::GetDeviceInfo, &[], None, Some(TIMEOUT))?;
|
||||
debug!("Received response with {} bytes", response.len());
|
||||
|
||||
let info = DeviceInfo::decode(&response)?;
|
||||
Ok(info)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct CameraId {
|
||||
pub vendor: u16,
|
||||
pub product: u16,
|
||||
}
|
||||
|
||||
pub struct SupportedCamera {
|
||||
pub id: CameraId,
|
||||
pub factory: fn() -> Box<dyn CameraImpl>,
|
||||
}
|
||||
|
||||
pub const SUPPORTED_CAMERAS: &[SupportedCamera] = &[SupportedCamera {
|
||||
id: xt5::FUJIFILM_XT5,
|
||||
factory: || Box::new(xt5::FujifilmXT5::new()),
|
||||
}];
|
||||
|
||||
impl From<&SupportedCamera> for Box<dyn CameraImpl> {
|
||||
fn from(camera: &SupportedCamera) -> Self {
|
||||
(camera.factory)()
|
||||
}
|
||||
}
|
||||
|
||||
impl SupportedCamera {
|
||||
pub fn matches_descriptor(&self, descriptor: &DeviceDescriptor) -> bool {
|
||||
descriptor.vendor_id() == self.id.vendor && descriptor.product_id() == self.id.product
|
||||
}
|
||||
}
|
||||
|
@@ -1,18 +1,31 @@
|
||||
use super::Camera;
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
|
||||
use super::{CameraId, CameraImpl};
|
||||
|
||||
pub const FUJIFILM_XT5: CameraId = CameraId {
|
||||
vendor: 0x04cb,
|
||||
product: 0x02fc,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FujifilmXT5;
|
||||
pub struct FujifilmXT5 {
|
||||
session_counter: AtomicU32,
|
||||
}
|
||||
|
||||
impl Camera for FujifilmXT5 {
|
||||
fn vendor_id(&self) -> u16 {
|
||||
0x04cb
|
||||
}
|
||||
|
||||
fn product_id(&self) -> u16 {
|
||||
0x02fc
|
||||
impl FujifilmXT5 {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
session_counter: AtomicU32::new(1),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CameraImpl for FujifilmXT5 {
|
||||
fn name(&self) -> &'static str {
|
||||
"FUJIFILM X-T5"
|
||||
}
|
||||
|
||||
fn next_session_id(&self) -> u32 {
|
||||
self.session_counter.fetch_add(1, Ordering::SeqCst)
|
||||
}
|
||||
}
|
||||
|
@@ -1,23 +1,16 @@
|
||||
use std::error::Error;
|
||||
|
||||
use libptp::DeviceInfo;
|
||||
use rusb::GlobalContext;
|
||||
|
||||
use crate::hardware::SUPPORTED_MODELS;
|
||||
use crate::hardware::{FujiUsbMode, SUPPORTED_CAMERAS};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Device {
|
||||
pub model: &'static dyn crate::hardware::Camera,
|
||||
pub rusb_device: rusb::Device<GlobalContext>,
|
||||
pub struct Camera {
|
||||
camera_impl: Box<dyn crate::hardware::CameraImpl>,
|
||||
rusb_device: rusb::Device<GlobalContext>,
|
||||
}
|
||||
|
||||
impl Device {
|
||||
pub fn camera(&self) -> Result<libptp::Camera<GlobalContext>, Box<dyn Error + Send + Sync>> {
|
||||
let handle = self.rusb_device.open()?;
|
||||
let device = handle.device();
|
||||
let camera = libptp::Camera::new(&device)?;
|
||||
Ok(camera)
|
||||
}
|
||||
|
||||
impl Camera {
|
||||
pub fn id(&self) -> String {
|
||||
let bus = self.rusb_device.bus_number();
|
||||
let address = self.rusb_device.address();
|
||||
@@ -25,7 +18,7 @@ impl Device {
|
||||
}
|
||||
|
||||
pub fn name(&self) -> String {
|
||||
self.model.name().to_string()
|
||||
self.camera_impl.name().to_string()
|
||||
}
|
||||
|
||||
pub fn vendor_id(&self) -> u16 {
|
||||
@@ -37,10 +30,32 @@ impl Device {
|
||||
let descriptor = self.rusb_device.device_descriptor().unwrap();
|
||||
descriptor.product_id()
|
||||
}
|
||||
|
||||
pub fn ptp(&self) -> Result<libptp::Camera<GlobalContext>, Box<dyn Error + Send + Sync>> {
|
||||
let handle = self.rusb_device.open()?;
|
||||
let device = handle.device();
|
||||
let ptp = libptp::Camera::new(&device)?;
|
||||
Ok(ptp)
|
||||
}
|
||||
|
||||
pub fn get_info(&self) -> Result<DeviceInfo, Box<dyn Error + Send + Sync>> {
|
||||
let mut ptp = self.ptp()?;
|
||||
self.camera_impl.get_info(&mut ptp)
|
||||
}
|
||||
|
||||
pub fn get_fuji_usb_mode(&self) -> Result<FujiUsbMode, Box<dyn Error + Send + Sync>> {
|
||||
let mut ptp = self.ptp()?;
|
||||
self.camera_impl.get_fuji_usb_mode(&mut ptp)
|
||||
}
|
||||
|
||||
pub fn get_fuji_battery_info(&self) -> Result<u32, Box<dyn Error + Send + Sync>> {
|
||||
let mut ptp = self.ptp()?;
|
||||
self.camera_impl.get_fuji_battery_info(&mut ptp)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_connected_devices() -> Result<Vec<Device>, Box<dyn Error + Send + Sync>> {
|
||||
let mut connected_devices = Vec::new();
|
||||
pub fn get_connected_camers() -> Result<Vec<Camera>, Box<dyn Error + Send + Sync>> {
|
||||
let mut connected_cameras = Vec::new();
|
||||
|
||||
for device in rusb::devices()?.iter() {
|
||||
let descriptor = match device.device_descriptor() {
|
||||
@@ -48,25 +63,23 @@ pub fn get_connected_devices() -> Result<Vec<Device>, Box<dyn Error + Send + Syn
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
for model in SUPPORTED_MODELS.iter() {
|
||||
if descriptor.vendor_id() == model.vendor_id()
|
||||
&& descriptor.product_id() == model.product_id()
|
||||
{
|
||||
let connected_device = Device {
|
||||
model: *model,
|
||||
for camera in SUPPORTED_CAMERAS.iter() {
|
||||
if camera.matches_descriptor(&descriptor) {
|
||||
let camera = Camera {
|
||||
camera_impl: camera.into(),
|
||||
rusb_device: device,
|
||||
};
|
||||
|
||||
connected_devices.push(connected_device);
|
||||
connected_cameras.push(camera);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(connected_devices)
|
||||
Ok(connected_cameras)
|
||||
}
|
||||
|
||||
pub fn get_connected_device_by_id(id: &str) -> Result<Device, Box<dyn Error + Send + Sync>> {
|
||||
pub fn get_connected_camera_by_id(id: &str) -> Result<Camera, Box<dyn Error + Send + Sync>> {
|
||||
let parts: Vec<&str> = id.split('.').collect();
|
||||
if parts.len() != 2 {
|
||||
return Err(format!("Invalid device id format: {}", id).into());
|
||||
@@ -79,12 +92,10 @@ pub fn get_connected_device_by_id(id: &str) -> Result<Device, Box<dyn Error + Se
|
||||
if device.bus_number() == bus && device.address() == address {
|
||||
let descriptor = device.device_descriptor()?;
|
||||
|
||||
for model in SUPPORTED_MODELS.iter() {
|
||||
if descriptor.vendor_id() == model.vendor_id()
|
||||
&& descriptor.product_id() == model.product_id()
|
||||
{
|
||||
return Ok(Device {
|
||||
model: *model,
|
||||
for camera in SUPPORTED_CAMERAS.iter() {
|
||||
if camera.matches_descriptor(&descriptor) {
|
||||
return Ok(Camera {
|
||||
camera_impl: camera.into(),
|
||||
rusb_device: device,
|
||||
});
|
||||
}
|
||||
@@ -97,10 +108,10 @@ pub fn get_connected_device_by_id(id: &str) -> Result<Device, Box<dyn Error + Se
|
||||
Err(format!("No device found with id: {}", id).into())
|
||||
}
|
||||
|
||||
pub fn get_device(device_id: Option<&str>) -> Result<Device, Box<dyn Error + Send + Sync>> {
|
||||
pub fn get_camera(device_id: Option<&str>) -> Result<Camera, Box<dyn Error + Send + Sync>> {
|
||||
match device_id {
|
||||
Some(id) => get_connected_device_by_id(id),
|
||||
None => get_connected_devices()?
|
||||
Some(id) => get_connected_camera_by_id(id),
|
||||
None => get_connected_camers()?
|
||||
.into_iter()
|
||||
.next()
|
||||
.ok_or_else(|| "No supported devices connected.".into()),
|
||||
|
Reference in New Issue
Block a user