5 pub use parse_mtx_status
::*;
8 pub use mtx_wrapper
::*;
13 use anyhow
::{bail, Error}
;
15 /// Interface to the media changer device for a single drive
16 pub trait MediaChange
{
18 /// Drive number inside changer
19 fn drive_number(&self) -> u64;
21 /// Drive name (used for debug messages)
22 fn drive_name(&self) -> &str;
24 /// Returns the changer status
25 fn status(&mut self) -> Result
<MtxStatus
, Error
>;
27 /// Transfer media from on slot to another (storage or import export slots)
29 /// Target slot needs to be empty
30 fn transfer_media(&mut self, from
: u64, to
: u64) -> Result
<(), Error
>;
32 /// Load media from storage slot into drive
33 fn load_media_from_slot(&mut self, slot
: u64) -> Result
<(), Error
>;
35 /// Load media by label-text into drive
37 /// This unloads first if the drive is already loaded with another media.
39 /// Note: This refuses to load media inside import/export
40 /// slots. Also, you cannot load cleaning units with this
42 fn load_media(&mut self, label_text
: &str) -> Result
<(), Error
> {
44 if label_text
.starts_with("CLN") {
45 bail
!("unable to load media '{}' (seems top be a a cleaning units)", label_text
);
48 let mut status
= self.status()?
;
50 let mut unload_drive
= false;
53 for (i
, drive_status
) in status
.drives
.iter().enumerate() {
54 if let ElementStatus
::VolumeTag(ref tag
) = drive_status
.status
{
55 if *tag
== label_text
{
56 if i
as u64 != self.drive_number() {
57 bail
!("unable to load media '{}' - media in wrong drive ({} != {})",
58 label_text
, i
, self.drive_number());
60 return Ok(()) // already loaded
63 if i
as u64 == self.drive_number() {
64 match drive_status
.status
{
65 ElementStatus
::Empty
=> { /* OK */ }
,
66 _
=> unload_drive
= true,
72 self.unload_to_free_slot(status
)?
;
73 status
= self.status()?
;
77 for (i
, (import_export
, element_status
)) in status
.slots
.iter().enumerate() {
78 if let ElementStatus
::VolumeTag(tag
) = element_status
{
79 if *tag
== label_text
{
81 bail
!("unable to load media '{}' - inside import/export slot", label_text
);
89 let slot
= match slot
{
90 None
=> bail
!("unable to find media '{}' (offline?)", label_text
),
94 self.load_media_from_slot(slot
as u64)
97 /// Unload media from drive (eject media if necessary)
98 fn unload_media(&mut self, target_slot
: Option
<u64>) -> Result
<(), Error
>;
100 /// List online media labels (label_text/barcodes)
102 /// List acessible (online) label texts. This does not include
103 /// media inside import-export slots or cleaning media.
104 fn online_media_label_texts(&mut self) -> Result
<Vec
<String
>, Error
> {
105 let status
= self.status()?
;
107 let mut list
= Vec
::new();
109 for drive_status
in status
.drives
.iter() {
110 if let ElementStatus
::VolumeTag(ref tag
) = drive_status
.status
{
111 list
.push(tag
.clone());
115 for (import_export
, element_status
) in status
.slots
.iter() {
116 if *import_export { continue; }
117 if let ElementStatus
::VolumeTag(ref tag
) = element_status
{
118 if tag
.starts_with("CLN") { continue; }
119 list
.push(tag
.clone());
126 /// Load/Unload cleaning cartridge
128 /// This fail if there is no cleaning cartridge online. Any media
129 /// inside the drive is automatically unloaded.
130 fn clean_drive(&mut self) -> Result
<(), Error
> {
131 let status
= self.status()?
;
133 let mut cleaning_cartridge_slot
= None
;
135 for (i
, (import_export
, element_status
)) in status
.slots
.iter().enumerate() {
136 if *import_export { continue; }
137 if let ElementStatus
::VolumeTag(ref tag
) = element_status
{
138 if tag
.starts_with("CLN") {
139 cleaning_cartridge_slot
= Some(i
+ 1);
145 let cleaning_cartridge_slot
= match cleaning_cartridge_slot
{
146 None
=> bail
!("clean failed - unable to find cleaning cartridge"),
147 Some(cleaning_cartridge_slot
) => cleaning_cartridge_slot
as u64,
150 if let Some(drive_status
) = status
.drives
.get(self.drive_number() as usize) {
151 match drive_status
.status
{
152 ElementStatus
::Empty
=> { /* OK */ }
,
153 _
=> self.unload_to_free_slot(status
)?
,
157 self.load_media_from_slot(cleaning_cartridge_slot
)?
;
159 self.unload_media(Some(cleaning_cartridge_slot
))?
;
166 /// By moving the media to an empty import-export slot. Returns
167 /// Some(slot) if the media was exported. Returns None if the media is
168 /// not online (already exported).
169 fn export_media(&mut self, label_text
: &str) -> Result
<Option
<u64>, Error
> {
170 let status
= self.status()?
;
172 let mut unload_from_drive
= false;
173 if let Some(drive_status
) = status
.drives
.get(self.drive_number() as usize) {
174 if let ElementStatus
::VolumeTag(ref tag
) = drive_status
.status
{
175 if tag
== label_text
{
176 unload_from_drive
= true;
184 for (i
, (import_export
, element_status
)) in status
.slots
.iter().enumerate() {
186 if to
.is_some() { continue; }
187 if let ElementStatus
::Empty
= element_status
{
188 to
= Some(i
as u64 + 1);
190 } else if let ElementStatus
::VolumeTag(ref tag
) = element_status
{
191 if tag
== label_text
{
192 from
= Some(i
as u64 + 1);
197 if unload_from_drive
{
200 self.unload_media(Some(to
))?
;
203 None
=> bail
!("unable to find free export slot"),
207 (Some(from
), Some(to
)) => {
208 self.transfer_media(from
, to
)?
;
211 (Some(_from
), None
) => bail
!("unable to find free export slot"),
212 (None
, _
) => Ok(None
), // not online
217 /// Unload media to a free storage slot
219 /// If posible to the slot it was previously loaded from.
221 /// Note: This method consumes status - so please read again afterward.
222 fn unload_to_free_slot(&mut self, status
: MtxStatus
) -> Result
<(), Error
> {
224 let drive_status
= &status
.drives
[self.drive_number() as usize];
225 if let Some(slot
) = drive_status
.loaded_slot
{
226 // check if original slot is empty/usable
227 if let Some(info
) = status
.slots
.get(slot
as usize - 1) {
228 if let (_import_export
, ElementStatus
::Empty
) = info
{
229 return self.unload_media(Some(slot
));
234 let mut free_slot
= None
;
235 for i
in 0..status
.slots
.len() {
236 if status
.slots
[i
].0 { continue; }
// skip import/export slots
237 if let ElementStatus
::Empty
= status
.slots
[i
].1 {
238 free_slot
= Some((i
+1) as u64);
242 if let Some(slot
) = free_slot
{
243 self.unload_media(Some(slot
))
245 bail
!("drive '{}' unload failure - no free slot", self.drive_name());