mirror of
https://github.com/G2-Games/minidisc-cli.git
synced 2025-04-19 11:42:53 -05:00
Initial code commit
This commit is contained in:
parent
ad3a4330d7
commit
397a886693
6 changed files with 598 additions and 0 deletions
12
Cargo.toml
Normal file
12
Cargo.toml
Normal file
|
@ -0,0 +1,12 @@
|
|||
[package]
|
||||
name = "minidisc-rs"
|
||||
version = "0.0.1"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
nofmt = "1.0.0"
|
||||
once_cell = "1.18.0"
|
||||
rusb = "0.9.3"
|
||||
|
49
src/main.rs
Normal file
49
src/main.rs
Normal file
|
@ -0,0 +1,49 @@
|
|||
mod netmd;
|
||||
|
||||
use crate::netmd::base::NetMD;
|
||||
use crate::netmd::interface::NetMDInterface;
|
||||
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
fn main() {
|
||||
for device in rusb::devices().unwrap().iter() {
|
||||
let device_desc = device.device_descriptor().unwrap();
|
||||
|
||||
let new_device = match device.open() {
|
||||
Ok(device) => device,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
println!("Connected to Bus {:03} Device {:03} VID: {:04x}, PID: {:04x}, {:?}",
|
||||
device.bus_number(),
|
||||
device.address(),
|
||||
device_desc.vendor_id(),
|
||||
device_desc.product_id(),
|
||||
new_device.read_product_string_ascii(&device_desc));
|
||||
|
||||
let player = NetMD::new(new_device, device_desc).unwrap();
|
||||
let player_controller = NetMDInterface::new(player);
|
||||
|
||||
println!(
|
||||
"Player Model: {}",
|
||||
player_controller.net_md_device.device_name().unwrap()
|
||||
);
|
||||
|
||||
player_controller.get_disc_subunit_identifier();
|
||||
|
||||
/*
|
||||
let mut request: [u8; 19] = [0x00, 0x18, 0x06, 0x02, 0x20, 0x18,
|
||||
0x01, 0x00, 0x00, 0x30, 0x00, 0xa,
|
||||
0x00, 0xff, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00];
|
||||
request[4] = 0x75;
|
||||
let test_result = player.send_command(&request);
|
||||
|
||||
match test_result {
|
||||
Ok(_) => println!("Successfully sent command! Response: {:?}", test_result),
|
||||
Err(error) => println!("Command failed! Error: {}", error)
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
228
src/netmd/base.rs
Normal file
228
src/netmd/base.rs
Normal file
|
@ -0,0 +1,228 @@
|
|||
use nofmt;
|
||||
use once_cell::sync::Lazy;
|
||||
use rusb::{DeviceDescriptor, DeviceHandle, Direction, GlobalContext, Recipient, RequestType};
|
||||
use std::error::Error;
|
||||
|
||||
const DEFAULT_TIMEOUT: std::time::Duration = std::time::Duration::new(1, 0);
|
||||
|
||||
const STANDARD_SEND: u8 =
|
||||
rusb::request_type(Direction::Out, RequestType::Vendor, Recipient::Interface);
|
||||
const STANDARD_RECV: u8 =
|
||||
rusb::request_type(Direction::In, RequestType::Vendor, Recipient::Interface);
|
||||
|
||||
// TODO: I think this sucks, figure out a better way
|
||||
pub static DEVICE_IDS: Lazy<Vec<DeviceId>> = Lazy::new(|| nofmt::pls!{
|
||||
Vec::from([
|
||||
DeviceId {vendor_id: 0x04dd, product_id: 0x7202, name: Some(String::from("Sharp IM-MT899H"))},
|
||||
DeviceId {vendor_id: 0x04dd, product_id: 0x9013, name: Some(String::from("Sharp IM-DR400"))},
|
||||
DeviceId {vendor_id: 0x04dd, product_id: 0x9014, name: Some(String::from("Sharp IM-DR80"))},
|
||||
DeviceId {vendor_id: 0x054c, product_id: 0x0034, name: Some(String::from("Sony PCLK-XX"))},
|
||||
DeviceId {vendor_id: 0x054c, product_id: 0x0036, name: Some(String::from("Sony"))},
|
||||
DeviceId {vendor_id: 0x054c, product_id: 0x0075, name: Some(String::from("Sony MZ-N1"))},
|
||||
DeviceId {vendor_id: 0x054c, product_id: 0x007c, name: Some(String::from("Sony"))},
|
||||
DeviceId {vendor_id: 0x054c, product_id: 0x0080, name: Some(String::from("Sony LAM-1"))},
|
||||
DeviceId {vendor_id: 0x054c, product_id: 0x0081, name: Some(String::from("Sony MDS-JB980/MDS-NT1/MDS-JE780"))},
|
||||
DeviceId {vendor_id: 0x054c, product_id: 0x0084, name: Some(String::from("Sony MZ-N505"))},
|
||||
DeviceId {vendor_id: 0x054c, product_id: 0x0085, name: Some(String::from("Sony MZ-S1"))},
|
||||
DeviceId {vendor_id: 0x054c, product_id: 0x0086, name: Some(String::from("Sony MZ-N707"))},
|
||||
DeviceId {vendor_id: 0x054c, product_id: 0x008e, name: Some(String::from("Sony CMT-C7NT"))},
|
||||
DeviceId {vendor_id: 0x054c, product_id: 0x0097, name: Some(String::from("Sony PCGA-MDN1"))},
|
||||
DeviceId {vendor_id: 0x054c, product_id: 0x00ad, name: Some(String::from("Sony CMT-L7HD"))},
|
||||
DeviceId {vendor_id: 0x054c, product_id: 0x00c6, name: Some(String::from("Sony MZ-N10"))},
|
||||
DeviceId {vendor_id: 0x054c, product_id: 0x00c7, name: Some(String::from("Sony MZ-N910"))},
|
||||
DeviceId {vendor_id: 0x054c, product_id: 0x00c8, name: Some(String::from("Sony MZ-N710/NF810"))},
|
||||
DeviceId {vendor_id: 0x054c, product_id: 0x00c9, name: Some(String::from("Sony MZ-N510/N610"))},
|
||||
DeviceId {vendor_id: 0x054c, product_id: 0x00ca, name: Some(String::from("Sony MZ-NE410/NF520D"))},
|
||||
DeviceId {vendor_id: 0x054c, product_id: 0x00e7, name: Some(String::from("Sony CMT-M333NT/M373NT"))},
|
||||
DeviceId {vendor_id: 0x054c, product_id: 0x00eb, name: Some(String::from("Sony MZ-NE810/NE910"))},
|
||||
DeviceId {vendor_id: 0x054c, product_id: 0x0101, name: Some(String::from("Sony LAM"))},
|
||||
DeviceId {vendor_id: 0x054c, product_id: 0x0113, name: Some(String::from("Aiwa AM-NX1"))},
|
||||
DeviceId {vendor_id: 0x054c, product_id: 0x013f, name: Some(String::from("Sony MDS-S500"))},
|
||||
DeviceId {vendor_id: 0x054c, product_id: 0x014c, name: Some(String::from("Aiwa AM-NX9"))},
|
||||
DeviceId {vendor_id: 0x054c, product_id: 0x017e, name: Some(String::from("Sony MZ-NH1"))},
|
||||
DeviceId {vendor_id: 0x054c, product_id: 0x0180, name: Some(String::from("Sony MZ-NH3D"))},
|
||||
DeviceId {vendor_id: 0x054c, product_id: 0x0182, name: Some(String::from("Sony MZ-NH900"))},
|
||||
DeviceId {vendor_id: 0x054c, product_id: 0x0184, name: Some(String::from("Sony MZ-NH700/NH800"))},
|
||||
DeviceId {vendor_id: 0x054c, product_id: 0x0186, name: Some(String::from("Sony MZ-NH600"))},
|
||||
DeviceId {vendor_id: 0x054c, product_id: 0x0187, name: Some(String::from("Sony MZ-NH600D"))},
|
||||
DeviceId {vendor_id: 0x054c, product_id: 0x0188, name: Some(String::from("Sony MZ-N920"))},
|
||||
DeviceId {vendor_id: 0x054c, product_id: 0x018a, name: Some(String::from("Sony LAM-3"))},
|
||||
DeviceId {vendor_id: 0x054c, product_id: 0x01e9, name: Some(String::from("Sony MZ-DH10P"))},
|
||||
DeviceId {vendor_id: 0x054c, product_id: 0x0219, name: Some(String::from("Sony MZ-RH10"))},
|
||||
DeviceId {vendor_id: 0x054c, product_id: 0x021b, name: Some(String::from("Sony MZ-RH710/MZ-RH910"))},
|
||||
DeviceId {vendor_id: 0x054c, product_id: 0x021d, name: Some(String::from("Sony CMT-AH10"))},
|
||||
DeviceId {vendor_id: 0x054c, product_id: 0x022c, name: Some(String::from("Sony CMT-AH10"))},
|
||||
DeviceId {vendor_id: 0x054c, product_id: 0x023c, name: Some(String::from("Sony DS-HMD1"))},
|
||||
DeviceId {vendor_id: 0x054c, product_id: 0x0286, name: Some(String::from("Sony MZ-RH1"))},
|
||||
DeviceId {vendor_id: 0x054c, product_id: 0x011a, name: Some(String::from("Sony CMT-SE7"))},
|
||||
DeviceId {vendor_id: 0x054c, product_id: 0x0148, name: Some(String::from("Sony MDS-A1"))},
|
||||
DeviceId {vendor_id: 0x0b28, product_id: 0x1004, name: Some(String::from("Kenwood MDX-J9"))},
|
||||
DeviceId {vendor_id: 0x04da, product_id: 0x23b3, name: Some(String::from("Panasonic SJ-MR250"))},
|
||||
DeviceId {vendor_id: 0x04da, product_id: 0x23b6, name: Some(String::from("Panasonic SJ-MR270"))},
|
||||
])
|
||||
});
|
||||
|
||||
pub enum Status {
|
||||
Ready,
|
||||
Playing,
|
||||
Paused,
|
||||
FastForward,
|
||||
Rewind,
|
||||
ReadingTOC,
|
||||
NoDisc,
|
||||
DiscBlank,
|
||||
}
|
||||
|
||||
pub struct DeviceId {
|
||||
vendor_id: u16,
|
||||
product_id: u16,
|
||||
name: Option<String>,
|
||||
}
|
||||
|
||||
pub struct NetMD {
|
||||
device_connection: DeviceHandle<GlobalContext>,
|
||||
model: DeviceId,
|
||||
status: Option<Status>,
|
||||
}
|
||||
|
||||
impl NetMD {
|
||||
/// Creates a new `NetMD` struct
|
||||
pub fn new(
|
||||
device: DeviceHandle<GlobalContext>,
|
||||
device_desc: DeviceDescriptor,
|
||||
) -> Result<Self, Box<dyn Error>> {
|
||||
let mut model = DeviceId {
|
||||
vendor_id: device_desc.vendor_id(),
|
||||
product_id: device_desc.product_id(),
|
||||
name: None,
|
||||
};
|
||||
|
||||
for device_type in DEVICE_IDS.iter() {
|
||||
if device_type.vendor_id == model.vendor_id
|
||||
&& device_type.product_id == model.product_id
|
||||
{
|
||||
model.name = device_type.name.clone()
|
||||
}
|
||||
}
|
||||
|
||||
match model.name {
|
||||
None => return Err("Could not find device in list".into()),
|
||||
Some(_) => (),
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
device_connection: device,
|
||||
model,
|
||||
status: None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Gets the device name from the struct
|
||||
pub fn device_name(&self) -> Option<String> {
|
||||
self.model.name.clone()
|
||||
}
|
||||
|
||||
/// Gets the vendor id from the struct
|
||||
pub fn vendor_id(&self) -> u16 {
|
||||
self.model.vendor_id.clone()
|
||||
}
|
||||
|
||||
/// Gets the product id from the struct
|
||||
pub fn product_id(&self) -> u16 {
|
||||
self.model.product_id.clone()
|
||||
}
|
||||
|
||||
/// Poll the device to get either the result
|
||||
/// of the previous command, or the status
|
||||
fn poll(&self, tries: usize) -> Result<(usize, [u8; 4]), Box<dyn Error>> {
|
||||
// Create an array to store the result of the poll
|
||||
let mut poll_result = [0u8; 4];
|
||||
|
||||
// Try until failure or `tries` reached
|
||||
for i in 0..tries {
|
||||
let _status = match self.device_connection.read_control(
|
||||
STANDARD_RECV,
|
||||
0x01,
|
||||
0,
|
||||
0,
|
||||
&mut poll_result,
|
||||
DEFAULT_TIMEOUT,
|
||||
) {
|
||||
Ok(size) => size,
|
||||
Err(error) => return Err(error.into()),
|
||||
};
|
||||
|
||||
if poll_result[0] != 0 {
|
||||
return Ok((poll_result[2] as usize, poll_result));
|
||||
}
|
||||
|
||||
if i > 0 {
|
||||
std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
}
|
||||
}
|
||||
|
||||
Ok((poll_result[2] as usize, poll_result))
|
||||
}
|
||||
|
||||
/// Send a control message to the device
|
||||
pub fn send_command(
|
||||
&self,
|
||||
command: Vec<u8>,
|
||||
use_factory_command: bool,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
//First poll to ensure the device is ready
|
||||
match self.poll(1) {
|
||||
Ok(buffer) => match buffer.1[2] {
|
||||
0 => 0,
|
||||
_ => return Err("Device not ready!".into()),
|
||||
},
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
|
||||
let _ = match use_factory_command {
|
||||
false => 0x80,
|
||||
true => 0xff,
|
||||
};
|
||||
|
||||
match self.device_connection.write_control(
|
||||
STANDARD_SEND,
|
||||
0x80,
|
||||
0,
|
||||
0,
|
||||
&command,
|
||||
DEFAULT_TIMEOUT,
|
||||
) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(error) => Err(error.into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Poll to see if a message is ready,
|
||||
/// and if so, recieve it
|
||||
pub fn read_reply(&self, use_factory_command: bool) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||
let poll_result = match self.poll(30) {
|
||||
Ok(buffer) => buffer,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
|
||||
let request = match use_factory_command {
|
||||
false => 0x81,
|
||||
true => 0xff,
|
||||
};
|
||||
|
||||
// Create a buffer to fill with the result
|
||||
let mut buf: [u8; 255] = [0; 255];
|
||||
|
||||
match self.device_connection.read_control(
|
||||
STANDARD_RECV,
|
||||
request,
|
||||
0,
|
||||
0,
|
||||
&mut buf,
|
||||
DEFAULT_TIMEOUT,
|
||||
) {
|
||||
Ok(_) => Ok(buf[0..poll_result.0].to_vec()),
|
||||
Err(error) => return Err(error.into()),
|
||||
}
|
||||
}
|
||||
}
|
298
src/netmd/interface.rs
Normal file
298
src/netmd/interface.rs
Normal file
|
@ -0,0 +1,298 @@
|
|||
use crate::netmd::utils;
|
||||
use crate::NetMD;
|
||||
use std::error::Error;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum Action {
|
||||
Play = 0x75,
|
||||
Pause = 0x7d,
|
||||
FastForward = 0x39,
|
||||
Rewind = 0x49,
|
||||
}
|
||||
|
||||
enum Track {
|
||||
Previous = 0x0002,
|
||||
Next = 0x8001,
|
||||
Restart = 0x0001,
|
||||
}
|
||||
|
||||
enum DiscFormat {
|
||||
LP4 = 0,
|
||||
LP2 = 2,
|
||||
SPMono = 4,
|
||||
SPStereo = 6,
|
||||
}
|
||||
|
||||
enum WireFormat {
|
||||
PCM = 0x00,
|
||||
L105kbps = 0x90,
|
||||
LP2 = 0x94,
|
||||
LP4 = 0xA8,
|
||||
}
|
||||
|
||||
impl WireFormat {
|
||||
fn frame_size(&self) -> u16 {
|
||||
match self {
|
||||
WireFormat::PCM => 2048,
|
||||
WireFormat::L105kbps => 192,
|
||||
WireFormat::LP2 => 152,
|
||||
WireFormat::LP4 => 96,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum Encoding {
|
||||
SP = 0x90,
|
||||
LP2 = 0x92,
|
||||
LP4 = 0x93,
|
||||
}
|
||||
|
||||
enum Channels {
|
||||
Mono = 0x01,
|
||||
Stereo = 0x00,
|
||||
}
|
||||
|
||||
enum ChannelCount {
|
||||
Mono = 1,
|
||||
Stereo = 2,
|
||||
}
|
||||
|
||||
enum TrackFlag {
|
||||
Protected = 0x03,
|
||||
Unprotected = 0x00,
|
||||
}
|
||||
|
||||
enum DiscFlag {
|
||||
Writable = 0x10,
|
||||
WriteProtected = 0x40,
|
||||
}
|
||||
|
||||
enum NetMDLevel {
|
||||
Level1 = 0x20, // Network MD
|
||||
Level2 = 0x50, // Program play MD
|
||||
Level3 = 0x70, // Editing MD
|
||||
}
|
||||
|
||||
enum Descriptor {
|
||||
DiscTitleTD,
|
||||
AudioUTOC1TD,
|
||||
AudioUTOC4TD,
|
||||
DSITD,
|
||||
AudioContentsTD,
|
||||
RootTD,
|
||||
|
||||
DiscSubunitIdentifier,
|
||||
OperatingStatusBlock,
|
||||
}
|
||||
|
||||
impl Descriptor {
|
||||
fn get_array(&self) -> Vec<u8> {
|
||||
match self {
|
||||
Descriptor::DiscTitleTD => vec![0x10, 0x18, 0x01],
|
||||
Descriptor::AudioUTOC1TD => vec![0x10, 0x18, 0x02],
|
||||
Descriptor::AudioUTOC4TD => vec![0x10, 0x18, 0x03],
|
||||
Descriptor::DSITD => vec![0x10, 0x18, 0x04],
|
||||
Descriptor::AudioContentsTD => vec![0x10, 0x10, 0x01],
|
||||
Descriptor::RootTD => vec![0x10, 0x10, 0x00],
|
||||
Descriptor::DiscSubunitIdentifier => vec![0x00],
|
||||
Descriptor::OperatingStatusBlock => vec![0x80, 0x00],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum DescriptorAction {
|
||||
OpenRead = 1,
|
||||
OpenWrite = 3,
|
||||
Close = 0,
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
enum Status {
|
||||
// NetMD Protocol return status (first byte of request)
|
||||
Control = 0x00,
|
||||
Status = 0x01,
|
||||
SpecificInquiry = 0x02,
|
||||
Notify = 0x03,
|
||||
GeneralInquiry = 0x04,
|
||||
// ... (first byte of response)
|
||||
NotImplemented = 0x08,
|
||||
Accepted = 0x09,
|
||||
Rejected = 0x0a,
|
||||
InTransition = 0x0b,
|
||||
Implemented = 0x0c,
|
||||
Changed = 0x0d,
|
||||
Interim = 0x0f,
|
||||
}
|
||||
|
||||
impl std::convert::TryFrom<u8> for Status {
|
||||
type Error = Box<dyn Error>;
|
||||
|
||||
fn try_from(item: u8) -> Result<Self, Box<dyn Error>> {
|
||||
match item {
|
||||
0x00 => Ok(Status::Control),
|
||||
0x01 => Ok(Status::Status),
|
||||
0x02 => Ok(Status::SpecificInquiry),
|
||||
0x03 => Ok(Status::Notify),
|
||||
0x04 => Ok(Status::GeneralInquiry),
|
||||
0x08 => Ok(Status::NotImplemented),
|
||||
0x09 => Ok(Status::Accepted),
|
||||
0x0a => Ok(Status::Rejected),
|
||||
0x0b => Ok(Status::InTransition),
|
||||
0x0c => Ok(Status::Implemented),
|
||||
0x0d => Ok(Status::Changed),
|
||||
0x0f => Ok(Status::Interim),
|
||||
_ => Err("Not a valid value".into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NetMDInterface {
|
||||
pub net_md_device: NetMD,
|
||||
}
|
||||
|
||||
impl NetMDInterface {
|
||||
const MAX_INTERIM_READ_ATTEMPTS: u8 = 4;
|
||||
const INTERIM_RESPONSE_RETRY_INTERVAL: u32 = 100;
|
||||
|
||||
pub fn new(net_md_device: NetMD) -> Self {
|
||||
NetMDInterface { net_md_device }
|
||||
}
|
||||
|
||||
pub fn get_disc_subunit_identifier(&self) -> Result<(), Box<dyn Error>> {
|
||||
self.change_descriptor_state(
|
||||
Descriptor::DiscSubunitIdentifier,
|
||||
DescriptorAction::OpenRead,
|
||||
);
|
||||
|
||||
let mut query = vec![0x18, 0x09, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00];
|
||||
|
||||
let reply = self.send_query(&mut query, false, false);
|
||||
|
||||
println!("{:X?}", reply);
|
||||
|
||||
/*
|
||||
let descriptor_length = i16::from_le_bytes(reply[0..1].try_into()?);
|
||||
let generation_id = i16::from_le_bytes(reply[0..1].try_into()?);
|
||||
*/
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn playback_control(&self, action: Action) -> Result<(), Box<dyn Error>> {
|
||||
let mut query = vec![0x18, 0xc3, 0xff, 0x00, 0x00, 0x00, 0x00];
|
||||
|
||||
query[3] = action as u8;
|
||||
|
||||
let result = self.send_query(&mut query, false, false)?;
|
||||
|
||||
utils::check_result(result, &[0x18, 0xc5, 0x00, action as u8, 0x00, 0x00, 0x00])?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn play(&self) {
|
||||
let _ = self.playback_control(Action::Play);
|
||||
}
|
||||
|
||||
pub fn fast_forward(&self) {
|
||||
let _ = self.playback_control(Action::FastForward);
|
||||
}
|
||||
|
||||
pub fn rewind(&self) {
|
||||
let _ = self.playback_control(Action::Rewind);
|
||||
}
|
||||
|
||||
pub fn pause(&self) {
|
||||
let _ = self.playback_control(Action::Pause);
|
||||
}
|
||||
|
||||
pub fn stop(&self) -> Result<(), Box<dyn Error>> {
|
||||
let mut query = vec![0x18, 0xc5, 0xff, 0x00, 0x00, 0x00, 0x00];
|
||||
|
||||
let result = self.send_query(&mut query, false, false)?;
|
||||
|
||||
utils::check_result(result, &[0x18, 0xc5, 0x00, 0x00, 0x00, 0x00, 0x00])?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn change_descriptor_state(&self, descriptor: Descriptor, action: DescriptorAction) {
|
||||
let mut query = vec![0x18, 0x08];
|
||||
|
||||
query.append(&mut descriptor.get_array());
|
||||
|
||||
query.push(action as u8);
|
||||
|
||||
query.push(0x00);
|
||||
|
||||
let _ = self.send_query(&mut query, false, false);
|
||||
}
|
||||
|
||||
fn send_query(
|
||||
&self,
|
||||
query: &mut Vec<u8>,
|
||||
test: bool,
|
||||
accept_interim: bool,
|
||||
) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||
self.send_command(query, test)?;
|
||||
|
||||
let result = self.read_reply(accept_interim)?;
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn send_command(&self, query: &mut Vec<u8>, test: bool) -> Result<(), Box<dyn Error>> {
|
||||
let status_byte = match test {
|
||||
true => Status::GeneralInquiry,
|
||||
false => Status::Control,
|
||||
};
|
||||
|
||||
let mut new_query = Vec::new();
|
||||
|
||||
new_query.push(status_byte as u8);
|
||||
new_query.append(query);
|
||||
|
||||
self.net_md_device.send_command(new_query, false)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_reply(&self, accept_interim: bool) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||
let mut current_attempt = 0;
|
||||
let mut data;
|
||||
|
||||
while current_attempt < Self::MAX_INTERIM_READ_ATTEMPTS {
|
||||
data = match self.net_md_device.read_reply(false) {
|
||||
Ok(reply) => reply,
|
||||
Err(error) => return Err(error.into()),
|
||||
};
|
||||
|
||||
let status = match Status::try_from(data[0]) {
|
||||
Ok(status) => status,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
|
||||
match status {
|
||||
Status::NotImplemented => return Err("Not implemented".into()),
|
||||
Status::Rejected => return Err("Rejected".into()),
|
||||
Status::Interim if !accept_interim => {
|
||||
let sleep_time = Self::INTERIM_RESPONSE_RETRY_INTERVAL as u64 * (u64::pow(2, current_attempt as u32) - 1);
|
||||
let sleep_dur = std::time::Duration::from_millis(sleep_time);
|
||||
std::thread::sleep(sleep_dur); // Sleep to wait before retrying
|
||||
current_attempt += 1;
|
||||
continue; // Retry!
|
||||
}
|
||||
Status::Accepted | Status::Implemented | Status::Interim => {
|
||||
if current_attempt >= Self::MAX_INTERIM_READ_ATTEMPTS {
|
||||
return Err("Max interim retry attempts reached".into());
|
||||
}
|
||||
return Ok(data);
|
||||
}
|
||||
_ => return Err("Unknown return status".into()),
|
||||
}
|
||||
}
|
||||
|
||||
// This should NEVER happen unless the code is changed wrongly
|
||||
Err("The max retries is set to 0".into())
|
||||
}
|
||||
}
|
3
src/netmd/mod.rs
Normal file
3
src/netmd/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
pub mod base;
|
||||
pub mod interface;
|
||||
pub mod utils;
|
8
src/netmd/utils.rs
Normal file
8
src/netmd/utils.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
use std::error::Error;
|
||||
|
||||
pub fn check_result(result: Vec<u8>, expected: &[u8]) -> Result<(), Box<dyn Error>> {
|
||||
match result.as_slice().eq(expected) {
|
||||
true => Ok(()),
|
||||
false => Err("Response was not expected!".into()),
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue