use crate::cbm::Cbm;
use crate::cbmtype::{CbmDeviceInfo, CbmErrorNumber, CbmErrorNumberOk, CbmStatus};
use crate::channel::CbmChannelManager;
use crate::error::{DeviceError, Error};
use crate::CbmDirListing;
use crate::CbmString;
#[allow(unused_imports)]
use log::{debug, error, info, trace, warn};
use parking_lot::Mutex;
use std::fmt;
use std::sync::Arc;
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct CbmDriveUnit {
pub device_number: u8,
pub device_info: CbmDeviceInfo,
channel_manager: Arc<Mutex<CbmChannelManager>>,
busy: bool,
}
impl fmt::Display for CbmDriveUnit {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Drive {} ({})", self.device_number, self.device_info)
}
}
impl CbmDriveUnit {
pub fn try_from_bus(cbm: &Cbm, device: u8) -> Result<Self, Error> {
if cbm.drive_exists(device)? {
let info = cbm.identify(device)?;
Ok(Self::new(device, info))
} else {
Err(Error::Device {
device,
error: DeviceError::NoDevice,
})
}
}
pub fn new(device_number: u8, device_info: CbmDeviceInfo) -> Self {
Self {
device_number,
device_info,
channel_manager: Arc::new(Mutex::new(CbmChannelManager::new())),
busy: false,
}
}
pub fn get_status(&mut self, cbm: &Cbm) -> Result<CbmStatus, Error> {
self.busy = true;
cbm.get_status(self.device_number)
.inspect(|_| self.busy = false)
.inspect_err(|_| self.busy = false)
}
pub fn send_init(
&mut self,
cbm: &mut Cbm,
ignore_errors: &Vec<CbmErrorNumber>,
) -> Vec<Result<CbmStatus, Error>> {
self.busy = true;
let mut results = Vec::new();
for ii in self.num_disk_drives_iter() {
let cmd = format!("i{}", ii);
let status = match cbm.send_string_command_ascii(self.device_number, &cmd) {
Ok(_) => match cbm.get_status(self.device_number) {
Ok(status) => {
if status.is_ok() != CbmErrorNumberOk::Ok
&& !ignore_errors.contains(&status.error_number)
{
Err(status.into())
} else {
Ok(status)
}
}
Err(e) => Err(e),
},
Err(e) => Err(e),
};
results.push(status);
}
self.busy = false;
results
}
#[allow(dead_code)]
fn reset(&mut self) -> Result<(), Error> {
self.busy = true;
self.channel_manager.lock().reset();
self.busy = true;
Ok(())
}
pub fn num_disk_drives(&self) -> u8 {
self.device_info.device_type.num_disk_drives()
}
pub fn device_info(&self) -> &CbmDeviceInfo {
&self.device_info
}
pub fn device_type_str(&self) -> &str {
self.device_info.device_type.as_str()
}
pub fn description(&self) -> &str {
&self.device_info.description
}
pub fn num_disk_drives_iter(&self) -> impl Iterator<Item = u8> {
0..self.num_disk_drives()
}
pub fn is_responding(&self) -> bool {
true
}
pub fn is_busy(&self) -> bool {
self.busy
}
pub fn dir(&self, cbm: &mut Cbm) -> Result<(Vec<CbmDirListing>, CbmStatus), Error> {
let mut results = Vec::new();
let single_drive_unit = self.num_disk_drives() == 1;
let mut error_status = None;
for ii in self.num_disk_drives_iter() {
debug!("Doing dir of device {} drive {}", self.device_number, ii);
let drive_unit_num = if single_drive_unit { None } else { Some(ii) };
match cbm.dir(self.device_number, drive_unit_num) {
Err(e @ Error::Device { .. }) => {
debug!(
"Got error trying to dir device {} drive {}: {}",
self.device_number, ii, e
);
continue;
}
Err(Error::Status { status, .. }) => {
debug!(
"Got error status trying to dir device {} drive {}: {}",
self.device_number, ii, status
);
error_status = Some(status.clone());
continue;
}
Err(e) => {
info!(
"Hit unrecoverable error trying to dir device {} drive {}: {}",
self.device_number, ii, e
);
return Err(e);
}
Ok(result) => {
results.push(result);
}
}
}
let status = error_status.unwrap_or(cbm.get_status(self.device_number)?);
Ok((results, status))
}
pub fn read_file(
&self,
cbm: &mut Cbm,
filename: &CbmString,
) -> Result<(Vec<u8>, CbmStatus), Error> {
let data = cbm.load_file_petscii(self.device_number, &filename.to_petscii())?;
let status = cbm.get_status(self.device_number)?;
Ok((data, status))
}
}