use super::Session;
use crate::{
error::Error,
protocol::{
caps::{address::Axis, other::HostCooperation},
data::{Op, Operation, Rect},
sense::{Failure, Fault},
},
scan::focus::{Focus, Focused},
};
use std::time::Duration;
use tracing::*;
const FOCUS_TIMEOUT: Duration = Duration::from_secs(60);
const AUTOFOCUS_TIMEOUT: Duration = Duration::from_secs(180);
fn offers(session: &Session, operation: Op) -> Result<(), Error> {
if session.capabilities().features.execute.supports(operation) {
return Ok(());
}
Err(Error::Unsupported {
op: "execute operation",
reason: format!("this unit does not offer {operation:?}"),
})
}
impl Session {
pub fn autofocus(&mut self, x: u32, y: u32, color: Option<u8>) -> Result<(), Error> {
let operation = match color.is_some() {
true => Op::ColorAutoFocus,
false => Op::AutoFocus,
};
offers(self, operation)?;
let coop = self.capabilities().features.cooperation;
if coop.contains(HostCooperation::AUTOFOCUS) {
return Err(Error::Unsupported {
op: "autofocus",
reason: "this unit leaves focusing to the driver".into(),
});
}
let caps = self.capabilities();
for (axis, name, value) in [
(&caps.address.x_axis, 'X', x),
(&caps.address.y_axis, 'Y', y),
] {
if !axis.address_range.contains(&value) {
return Err(Error::Unsupported {
op: "autofocus address",
reason: format!(
"{name} {value} is outside {} to {}",
axis.address_range.start, axis.address_range.last
),
});
}
}
if let Some(frames) = self.frames()
&& !frames.frames.is_empty()
&& frames.at(x, y).is_none()
{
return Err(Error::Unsupported {
op: "autofocus address",
reason: format!("({x}, {y}) is in none of the {:?}", frames.frames),
});
}
self.execute(
operation,
Operation {
color: color.unwrap_or(0),
first: x,
second: y,
},
AUTOFOCUS_TIMEOUT,
)
}
pub fn focus_to(&mut self, position: u16) -> Result<(), Error> {
offers(self, Op::FocusMove)?;
let range = self.capabilities().address.focus_range;
if !range.contains(&position) {
return Err(Error::Unsupported {
op: "focus position",
reason: format!("{position} is outside {} to {}", range.start, range.last),
});
}
self.execute(
Op::FocusMove,
Operation {
first: u32::from(position),
..Operation::default()
},
FOCUS_TIMEOUT,
)
}
pub fn focus_frame(&mut self, frame: Rect, focus: Focus) -> Result<Focused, Error> {
match focus {
Focus::Hold => Ok(Focused::Skipped),
Focus::At(position) => self.focus_to(position).map(|()| Focused::Yes),
Focus::Auto { at, color } => {
let caps = self.capabilities();
let point = |axis: &Axis, origin: u32, size: u32, fraction: f32| {
let offset = (size as f32 * fraction.clamp(0.0, 1.0)) as u32;
origin
.saturating_add(offset)
.clamp(axis.address_range.start, axis.address_range.last)
};
let x = point(
&caps.address.x_axis,
frame.left,
frame.right.saturating_sub(frame.left),
at.0,
);
let y = point(
&caps.address.y_axis,
frame.top,
frame.bottom.saturating_sub(frame.top),
at.1,
);
debug!(x, y, "focusing");
let outcome = match self.autofocus(x, y, color) {
Ok(()) => Ok(Focused::Yes),
Err(Error::Device(fault))
if matches!(*fault, Fault::Reported(Failure::OutOfFocus, _)) =>
{
warn!(x, y, "autofocus did not reach focus");
Ok(Focused::NotReached)
}
Err(e) => Err(e),
};
if let Ok(params) = self.get_parameter(Op::FocusMove) {
info!(position = params.first, "focused at");
}
outcome
}
}
}
}