init
This commit is contained in:
+144
@@ -0,0 +1,144 @@
|
||||
use anyhow::Result;
|
||||
use encoding_rs::IBM866;
|
||||
use serialport::SerialPort;
|
||||
use std::io::Write;
|
||||
use std::time::Duration;
|
||||
|
||||
const TABLE_CYR: u8 = 6; // PD-2600: your Cyrillic table (ESC t 6)
|
||||
const FIXED_BAUD: u32 = 9600;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct VfdConfig {
|
||||
pub port_name: String,
|
||||
pub width: usize,
|
||||
pub timeout: Duration,
|
||||
}
|
||||
|
||||
impl VfdConfig {
|
||||
pub fn new(port_name: impl Into<String>) -> Self {
|
||||
Self {
|
||||
port_name: port_name.into(),
|
||||
width: 20,
|
||||
timeout: Duration::from_millis(100),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_width(mut self, width: usize) -> Self {
|
||||
self.width = width.max(1);
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn with_timeout(mut self, timeout: Duration) -> Self {
|
||||
self.timeout = timeout;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Vfd {
|
||||
port: Box<dyn SerialPort>,
|
||||
pub width: usize,
|
||||
blank_line: String,
|
||||
}
|
||||
|
||||
impl Vfd {
|
||||
pub fn open(cfg: VfdConfig) -> Result<Self> {
|
||||
let mut port = serialport::new(cfg.port_name, FIXED_BAUD)
|
||||
.timeout(cfg.timeout)
|
||||
.open()?;
|
||||
|
||||
// ESC @
|
||||
port.write_all(&[0x1B, 0x40])?;
|
||||
// ESC t 6
|
||||
port.write_all(&[0x1B, 0x74, TABLE_CYR])?;
|
||||
|
||||
Ok(Self {
|
||||
port,
|
||||
width: cfg.width,
|
||||
blank_line: " ".repeat(cfg.width),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) -> std::io::Result<()> {
|
||||
self.port.write_all(&[0x0C])
|
||||
}
|
||||
|
||||
pub fn print_line(&mut self, line: u8, text: &str) -> std::io::Result<()> {
|
||||
if !(1..=2).contains(&line) {
|
||||
return Ok(());
|
||||
}
|
||||
// overwrite the whole line to avoid leftovers
|
||||
self.goto_xy(1, line)?;
|
||||
|
||||
// FIX: take owned copy so self isn't immutably borrowed during &mut self call
|
||||
let blank = self.blank_line.clone();
|
||||
self.write_cp866(&blank)?;
|
||||
|
||||
self.goto_xy(1, line)?;
|
||||
let fitted = fit_to_width(&sanitize_for_cp866(text), self.width);
|
||||
self.write_cp866(&fitted)
|
||||
}
|
||||
|
||||
/// Print already-prepared fixed-width frame at (1, line) without clearing.
|
||||
/// Use this for marquee to avoid double-writes.
|
||||
pub fn print_frame(&mut self, line: u8, frame: &str) -> std::io::Result<()> {
|
||||
if !(1..=2).contains(&line) {
|
||||
// silently ignore invalid lines (or return an error if you want)
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.goto_xy(1, line)?;
|
||||
|
||||
// frame should already be width-sized; if not, fit it
|
||||
let fitted = fit_to_width(frame, self.width);
|
||||
self.write_cp866(&fitted)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn print_at(&mut self, x: u8, y: u8, text: &str) -> std::io::Result<()> {
|
||||
self.goto_xy(x, y)?;
|
||||
self.write_cp866(&sanitize_for_cp866(text))
|
||||
}
|
||||
|
||||
fn goto_xy(&mut self, x: u8, y: u8) -> std::io::Result<()> {
|
||||
// US $ x y
|
||||
self.port.write_all(&[0x1F, 0x24, x, y])
|
||||
}
|
||||
|
||||
fn write_cp866(&mut self, s: &str) -> std::io::Result<()> {
|
||||
let (bytes, _, _) = IBM866.encode(s);
|
||||
self.port.write_all(&bytes)
|
||||
}
|
||||
/// Epson customer display: US X n (brightness), n=1..4
|
||||
pub fn set_brightness(&mut self, n: u8) -> Result<()> {
|
||||
let n = n.clamp(1, 4);
|
||||
let cmd = [0x1F, 0x58, n]; // US 'X' n
|
||||
self.port.write_all(&cmd)?;
|
||||
self.port.flush()?;
|
||||
std::thread::sleep(std::time::Duration::from_millis(2));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sanitize_for_cp866(s: &str) -> String {
|
||||
s.chars()
|
||||
.map(|c| match c {
|
||||
'…' => '.', //
|
||||
'—' | '–' => '-', //
|
||||
'№' => '#', //
|
||||
'\t' => ' ',
|
||||
'“' | '”' => '"',
|
||||
'‘' | '’' => '\'',
|
||||
_ => c,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn fit_to_width(s: &str, width: usize) -> String {
|
||||
let mut out: String = s.chars().take(width).collect();
|
||||
let len = out.chars().count();
|
||||
if len < width {
|
||||
out.push_str(&" ".repeat(width - len));
|
||||
}
|
||||
out
|
||||
}
|
||||
Reference in New Issue
Block a user