1use std::{
2 fmt::{self, Debug, Display, Formatter},
3 time::Duration,
4};
5
6use anyhow::{bail, Context, Result};
7
8macro_rules! backends {
9 [$($(# [$($m:tt)*])? $mod:ident :: $name:ident),* $(,)?] => {
10 $(
11 $(# [$($m)*])*
12 mod $mod;
13 $(# [$($m)*])*
14 pub use crate::$mod::$name;
15 )*
16 };
17}
18
19backends![
20 #[cfg(feature = "usb")]
21 usb::UsbBackend,
22 #[cfg(feature = "file")]
23 file::FileBackend,
24];
25
26pub trait Backend {
28 fn send(&mut self, buf: &[u8], timeout: Duration) -> Result<()>;
31
32 fn recv(&mut self, buf: &mut [u8], timeout: Duration) -> Result<usize>;
37}
38
39#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
41pub struct MacAddr(pub [u8; 6]);
42
43pub struct Printer {
45 backend: Box<dyn Backend>,
46}
47
48impl Printer {
49 pub fn new(backend: impl Backend + 'static) -> Self {
51 Self {
52 backend: Box::new(backend),
53 }
54 }
55
56 pub fn find() -> Result<Self> {
58 #[cfg(feature = "usb")]
59 {
60 match crate::usb::UsbBackend::list() {
61 Ok(devs) => {
62 if let Some(dev) = devs.first() {
63 let backend = UsbBackend::open(dev)?;
64 return Ok(Self::new(backend));
65 }
66 }
67 Err(e) => log::error!("cannot get list of usb devices: {e}"),
68 }
69 }
70
71 bail!("no printer found");
72 }
73
74 fn send(&mut self, buf: &[u8], timeout: u64) -> Result<()> {
75 log::trace!("send({}{buf:x?}, {timeout}s);", buf.len());
76 self.backend.send(buf, Duration::from_secs(timeout))
77 }
78 fn recv(&mut self, buf: &mut [u8], timeout: u64) -> Result<usize> {
79 let n = self.backend.recv(buf, Duration::from_secs(timeout))?;
80 log::trace!("recv({}, {timeout}s): {n}{:x?}", buf.len(), &buf[0..n]);
81 Ok(n)
82 }
83 fn query(&mut self, cmd: &[u8]) -> Result<Vec<u8>> {
84 self.send(cmd, 3).context("failed to send request")?;
85 let mut buf = vec![0u8; 1024];
86 let n = self.recv(&mut buf, 3).context("failed receive response")?;
87 buf.truncate(n);
88 Ok(buf)
89 }
90 fn query_string(&mut self, cmd: &[u8]) -> Result<String> {
91 let buf = self.query(cmd)?;
92 let s = String::from_utf8_lossy(&buf);
93 Ok(s.into_owned())
94 }
95
96 pub fn get_ip(&mut self) -> Result<String> {
98 self.query_string(&[0x10, 0xff, 0x20, 0xf0])
99 }
100
101 pub fn get_firmware_ver(&mut self) -> Result<String> {
103 self.query_string(&[0x10, 0xff, 0x20, 0xf1])
104 }
105
106 pub fn get_serial(&mut self) -> Result<String> {
108 self.query_string(&[0x10, 0xff, 0x20, 0xf2])
109 }
110
111 pub fn get_hardware_ver(&mut self) -> Result<String> {
113 self.query_string(&[0x10, 0xff, 0x30, 0x10])
114 }
115
116 pub fn get_name(&mut self) -> Result<String> {
118 self.query_string(&[0x10, 0xff, 0x30, 0x11])
119 }
120
121 pub fn get_mac(&mut self) -> Result<MacAddr> {
124 let buf = self.query(&[0x10, 0xff, 0x30, 0x12])?;
125 if buf.len() < 6 {
127 bail!(
128 "invalid MAC address response, got {} bytes: {:x?}",
129 buf.len(),
130 &buf
131 );
132 }
133 let mut mac = [0u8; 6];
134 mac.copy_from_slice(&buf[0..6]);
135 Ok(MacAddr(mac))
136 }
137
138 pub fn get_battery(&mut self) -> Result<u8> {
140 let buf = self.query(&[0x10, 0xff, 0x50, 0xf1])?;
141 if buf.len() != 2 {
142 bail!("invalid battery response");
143 }
144 Ok(buf[1])
145 }
146
147 pub fn set_concentration(&mut self, c: u8) -> Result<()> {
149 if c > 2 {
150 bail!("invalid concentration: {c}");
151 }
152
153 self.send(&[0x10, 0xff, 0x10, 0x00, c], 1)
154 }
155
156 pub fn reset(&mut self) -> Result<()> {
159 let buf = [
160 0x10, 0xff, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
161 0x00, 0x00,
162 ];
163 self.send(&buf, 3)?;
164 let mut buf = [0u8; 128];
165 let _ = self.backend.recv(&mut buf, Duration::from_secs(1));
166 Ok(())
167 }
168
169 pub fn print_text(&mut self, text: &str) -> Result<()> {
178 let text: Vec<u8> = text
179 .chars()
180 .filter(|ch| matches!(ch, '\n' | '\x20'..='\x7f'))
181 .map(|ch| ch as u8)
182 .collect();
183
184 self.send(&text, 30)?;
185 Ok(())
186 }
187
188 pub fn print_image(&mut self, pixels: &[u8], width: u16) -> Result<()> {
218 if width == 0 || width % 8 != 0 {
219 bail!("width must be non-zero and divisible by 8");
220 }
221
222 let n = pixels.len() * 8;
223 let w = width as usize;
224 let h = n / w;
225
226 if h > 0xff {
227 bail!("document too long");
228 }
229
230 if pixels.len() != (w * h / 8) {
231 bail!("invalid length of pixels: {}", pixels.len());
232 }
233
234 let rs = w / 8;
235
236 let mut packet = vec![
237 0x1d,
238 0x76,
239 0x30,
240 (rs >> 8) as u8,
241 (rs & 0xff) as u8,
242 0x00,
243 h as u8,
244 0x00,
245 ];
246 packet.extend_from_slice(pixels);
247 self.send(&packet, 60)?;
248
249 self.send(&[0x10, 0xff, 0xfe, 0x45], 1)?;
251 Ok(())
252 }
253
254 pub fn print_image_chunked_ext(
257 &mut self,
258 pixels: &[u8],
259 width: u16,
260 chunk_height: u16,
261 delay: Duration,
262 ) -> Result<()> {
263 pixels
264 .chunks(width as usize * chunk_height as usize / 8)
265 .try_for_each(|chunk| {
266 self.print_image(chunk, width)?;
267 std::thread::sleep(delay);
268 Ok(())
269 })
270 }
271
272 pub fn print_image_chunked(&mut self, pixels: &[u8], width: u16) -> Result<()> {
273 self.print_image_chunked_ext(pixels, width, 24, Duration::from_millis(50))
274 }
275
276 pub fn push(&mut self, num: u8) -> Result<()> {
278 self.send(&[0x1b, 0x4a, num], 5)?;
279 Ok(())
280 }
281}
282
283impl Display for MacAddr {
284 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
285 let [x0, x1, x2, x3, x4, x5] = self.0;
286 write!(f, "{x0:02x}:{x1:02x}:{x2:02x}:{x3:02x}:{x4:02x}:{x5:02x}")
287 }
288}
289
290impl Debug for MacAddr {
291 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
292 <Self as Display>::fmt(self, f)
293 }
294}