use std::time::Duration;
use commands::Commands;
use device::Status;
use image::ImageError;
use log::{trace, debug, error};
#[cfg(feature = "structopt")]
use structopt::StructOpt;
#[cfg(feature = "strum")]
use strum::VariantNames;
use rusb::{Context, Device, DeviceDescriptor, DeviceHandle, Direction, TransferType, UsbContext};
pub mod device;
use device::*;
pub mod commands;
pub mod bitmap;
pub mod tiff;
pub mod render;
pub struct PTouch {
_device: Device<Context>,
handle: DeviceHandle<Context>,
descriptor: DeviceDescriptor,
timeout: Duration,
cmd_ep: u8,
stat_ep: u8,
}
pub const BROTHER_VID: u16 = 0x04F9;
#[derive(Clone, PartialEq, Debug)]
#[cfg_attr(feature = "structopt", derive(StructOpt))]
pub struct Options {
#[cfg_attr(feature = "structopt", structopt(long, possible_values = &device::PTouchDevice::VARIANTS, default_value = "pt-p710bt"))]
pub device: device::PTouchDevice,
#[cfg_attr(feature = "structopt", structopt(long, default_value = "0"))]
pub index: usize,
#[cfg_attr(feature = "structopt", structopt(long, default_value = "500"))]
pub timeout_milliseconds: u64,
#[cfg_attr(feature = "structopt", structopt(long, hidden = true))]
pub no_reset: bool,
#[cfg_attr(feature = "structopt", structopt(long, hidden = true))]
pub usb_no_claim: bool,
#[cfg_attr(feature = "structopt", structopt(long, hidden = true))]
pub usb_no_detach: bool,
#[cfg_attr(feature = "structopt", structopt(long))]
pub no_status_fetch: bool,
}
lazy_static::lazy_static! {
static ref CONTEXT: Context = {
Context::new().unwrap()
};
}
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("USB error: {:?}", 0)]
Usb(rusb::Error),
#[error("IO error: {:?}", 0)]
Io(std::io::Error),
#[error("Image error: {:?}", 0)]
Image(ImageError),
#[error("Invalid device index")]
InvalidIndex,
#[error("No supported languages")]
NoLanguages,
#[error("Unable to locate expected endpoints")]
InvalidEndpoints,
#[error("Renderer error")]
Render,
#[error("Operation timeout")]
Timeout,
#[error("PTouch Error ({:?} {:?})", 0, 1)]
PTouch(Error1, Error2),
}
impl From<std::io::Error> for Error {
fn from(e: std::io::Error) -> Self {
Error::Io(e)
}
}
impl From<rusb::Error> for Error {
fn from(e: rusb::Error) -> Self {
Error::Usb(e)
}
}
impl From<ImageError> for Error {
fn from(e: ImageError) -> Self {
Error::Image(e)
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Info {
pub manufacturer: String,
pub product: String,
pub serial: String,
}
impl PTouch {
pub fn new(o: &Options) -> Result<Self, Error> {
Self::new_with_context(o, &CONTEXT)
}
pub fn new_with_context(o: &Options, context: &Context) -> Result<Self, Error> {
let devices = context.devices()?;
let mut matches: Vec<_> = devices
.iter()
.filter_map(|d| {
let desc = match d.device_descriptor() {
Ok(d) => d,
Err(e) => {
debug!("Could not fetch descriptor for device {:?}: {:?}", d, e);
return None;
}
};
if desc.vendor_id() == BROTHER_VID && desc.product_id() == o.device as u16 {
Some((d, desc))
} else {
None
}
})
.collect();
if matches.len() < o.index || matches.len() == 0 {
debug!(
"Device index ({}) exceeds number of discovered devices ({})",
o.index,
matches.len()
);
return Err(Error::InvalidIndex);
}
debug!("Found matching devices: {:?}", matches);
let (device, descriptor) = matches.remove(o.index);
let mut handle = match device.open() {
Ok(v) => v,
Err(e) => {
debug!("Error opening device");
return Err(e.into());
}
};
if let Err(e) = handle.reset() {
debug!("Error resetting device handle");
return Err(e.into())
}
let config_desc = match device.config_descriptor(0) {
Ok(v) => v,
Err(e) => {
debug!("Failed to fetch config descriptor");
return Err(e.into());
}
};
let interface = match config_desc.interfaces().next() {
Some(i) => i,
None => {
debug!("No interfaces found");
return Err(Error::InvalidEndpoints);
}
};
let (mut cmd_ep, mut stat_ep) = (None, None);
for interface_desc in interface.descriptors() {
for endpoint_desc in interface_desc.endpoint_descriptors() {
match (endpoint_desc.transfer_type(), endpoint_desc.direction()) {
(TransferType::Bulk, Direction::In) => stat_ep = Some(endpoint_desc.address()),
(TransferType::Bulk, Direction::Out) => cmd_ep = Some(endpoint_desc.address()),
(_, _) => continue,
}
}
}
let (cmd_ep, stat_ep) = match (cmd_ep, stat_ep) {
(Some(cmd), Some(stat)) => (cmd, stat),
_ => {
debug!("Failed to locate command and status endpoints");
return Err(Error::InvalidEndpoints);
}
};
debug!("Checking for active kernel driver");
match handle.kernel_driver_active(interface.number())? {
true => {
if !o.usb_no_detach {
debug!("Detaching kernel driver");
handle.detach_kernel_driver(interface.number())?;
} else {
debug!("Kernel driver detach disabled");
}
},
false => {
debug!("Kernel driver inactive");
},
}
if !o.usb_no_claim {
debug!("Claiming interface");
handle.claim_interface(interface.number())?;
} else {
debug!("Claim interface disabled");
}
let mut s = Self {
_device: device,
handle,
descriptor,
cmd_ep,
stat_ep,
timeout: Duration::from_millis(o.timeout_milliseconds),
};
if !o.no_reset {
s.invalidate()?;
s.init()?;
} else {
debug!("Skipping device reset");
}
Ok(s)
}
pub fn info(&mut self) -> Result<Info, Error> {
let timeout = Duration::from_millis(200);
let languages = self.handle.read_languages(timeout)?;
let active_config = self.handle.active_configuration()?;
trace!("Active configuration: {}", active_config);
trace!("Languages: {:?}", languages);
if languages.len() == 0 {
return Err(Error::NoLanguages);
}
let language = languages[0];
let manufacturer =
self.handle
.read_manufacturer_string(language, &self.descriptor, timeout)?;
let product = self
.handle
.read_product_string(language, &self.descriptor, timeout)?;
let serial = self
.handle
.read_serial_number_string(language, &self.descriptor, timeout)?;
Ok(Info {
manufacturer,
product,
serial,
})
}
pub fn status(&mut self) -> Result<Status, Error> {
self.status_req()?;
let d = self.read(self.timeout)?;
let s = Status::from(d);
debug!("Status: {:02x?}", s);
Ok(s)
}
pub fn print_raw(&mut self, data: Vec<[u8; 16]>, info: &PrintInfo) -> Result<(), Error> {
self.switch_mode(Mode::Raster)?;
self.set_status_notify(true)?;
self.set_print_info(info)?;
self.set_various_mode(VariousMode::AUTO_CUT)?;
self.set_advanced_mode(AdvancedMode::NO_CHAIN)?;
self.set_margin(0)?;
self.set_compression_mode(CompressionMode::None)?;
for line in data {
self.raster_transfer(&line)?;
}
self.print_and_feed()?;
let mut i = 0;
loop {
if let Ok(s) = self.read_status(self.timeout) {
if !s.error1.is_empty() || !s.error2.is_empty() {
debug!("Print error: {:?} {:?}", s.error1, s.error2);
return Err(Error::PTouch(s.error1, s.error2));
}
if s.status_type == DeviceStatus::PhaseChange {
debug!("Started printing");
}
if s.status_type == DeviceStatus::Completed {
debug!("Print completed");
break;
}
}
if i > 10 {
debug!("Print timeout");
return Err(Error::Timeout);
}
i += 1;
std::thread::sleep(Duration::from_secs(1));
}
Ok(())
}
fn read(&mut self, timeout: Duration) -> Result<[u8; 32], Error> {
let mut buff = [0u8; 32];
let n = self.handle.read_bulk(self.stat_ep, &mut buff, timeout)?;
if n != 32 {
return Err(Error::Timeout)
}
Ok(buff)
}
fn write(&mut self, data: &[u8], timeout: Duration) -> Result<(), Error> {
debug!("WRITE: {:02x?}", data);
let n = self.handle.write_bulk(self.cmd_ep, &data, timeout)?;
if n != data.len() {
return Err(Error::Timeout)
}
Ok(())
}
}