Rename crate to escpos-vfd and add tokio support Introduce a configurable crate-ready API with presets, typed errors, optional Tokio async support, and expanded documentation.
106 lines
4.1 KiB
Rust
106 lines
4.1 KiB
Rust
//! Имитирует компактный дашборд и обновляет только изменяемые поля через `print_at`.
|
||
|
||
mod common;
|
||
|
||
use std::error::Error;
|
||
|
||
use escpos_vfd::VfdWorker;
|
||
|
||
fn pad_left(s: &str, width: usize) -> String {
|
||
// Подгоняем ширину поля, чтобы при уменьшении числа не оставались "хвосты" от
|
||
// предыдущего значения. Например, после 1000 должно появиться " 999", а не "9990".
|
||
if s.chars().count() >= width {
|
||
s.chars().take(width).collect()
|
||
} else {
|
||
format!("{:>width$}", s, width = width)
|
||
}
|
||
}
|
||
|
||
fn main() -> Result<(), Box<dyn Error>> {
|
||
// Аргументы:
|
||
// 1) port - serial-порт дисплея;
|
||
// 2) width - ширина строки;
|
||
// 3) delay_ms - пауза между изменениями данных.
|
||
let mut args = common::ExampleArgs::from_env()?;
|
||
let delay_ms: u64 = args.parse_or(200);
|
||
let columns = args.columns;
|
||
|
||
// Координаты ниже подобраны под 20 символов. Для более узких дисплеев лучше
|
||
// сделать другой layout, поэтому пример явно сообщает об ограничении.
|
||
if columns < 20 {
|
||
return Err("update_at requires a display width of at least 20 columns".into());
|
||
}
|
||
|
||
// Worker сохраняет кэш строк. Благодаря этому `print_at` и `print_line_diff`
|
||
// согласованно обновляют только изменившиеся области.
|
||
let worker = VfdWorker::start(args.config)?;
|
||
let vfd = worker.handle();
|
||
|
||
vfd.clear()?;
|
||
|
||
// Рисуем "шаблон" один раз. Дальше меняем только числовые поля внутри шаблона.
|
||
// 12345678901234567890
|
||
// Temp: __.__C RPM:____
|
||
// Load: ___% Uptime:____
|
||
vfd.print_line_diff(1, "Temp: 00.00C RPM:0000")?;
|
||
vfd.print_line_diff(2, "Load: 000% Up:0000s")?;
|
||
|
||
// Координаты (1-based):
|
||
// "Temp: 00.00C ..." -> числа начинаются с x=7, длина 5 (00.00)
|
||
let temp_x = 7u8;
|
||
let temp_w = 5usize;
|
||
|
||
// "RPM:0000" -> числа начинаются после "RPM:"; в строке выше это x=16, длина 4
|
||
let rpm_x = 16u8;
|
||
let rpm_w = 4usize;
|
||
|
||
// "Load: 000%" -> числа x=7, длина 3
|
||
let load_x = 7u8;
|
||
let load_w = 3usize;
|
||
|
||
// "Up:0000s" -> числа x=12, длина 4
|
||
let up_x = 12u8;
|
||
let up_w = 4usize;
|
||
|
||
let mut t: f32 = 21.50;
|
||
let mut rpm: u32 = 900;
|
||
let mut load: u32 = 10;
|
||
let mut uptime: u32 = 0;
|
||
|
||
let spinner = ["|", "/", "-", "\\"];
|
||
let mut si = 0usize;
|
||
|
||
loop {
|
||
// Чуть "шевелим" данные, чтобы на дисплее были видны частичные обновления.
|
||
t += 0.03;
|
||
if t > 29.99 {
|
||
t = 21.50;
|
||
}
|
||
|
||
rpm = (rpm + 37) % 9999;
|
||
load = (load + 3) % 100;
|
||
uptime = uptime.wrapping_add(1);
|
||
|
||
// Важно: обновляем только куски строки в фиксированных местах. Это меньше
|
||
// нагружает serial-линию и обычно выглядит спокойнее, чем полная перерисовка.
|
||
let temp_s = pad_left(&format!("{:.2}", t), temp_w);
|
||
let rpm_s = pad_left(&format!("{}", rpm), rpm_w);
|
||
let load_s = pad_left(&format!("{}", load), load_w);
|
||
let up_s = pad_left(&format!("{}", uptime % 10000), up_w);
|
||
|
||
vfd.print_at(temp_x, 1, temp_s)?;
|
||
vfd.print_at(rpm_x, 1, rpm_s)?;
|
||
|
||
vfd.print_at(load_x, 2, load_s)?;
|
||
vfd.print_at(up_x, 2, up_s)?;
|
||
|
||
// "Живой" индикатор справа показывает, что цикл продолжает выполняться.
|
||
if columns >= 20 {
|
||
vfd.print_at(20, 2, spinner[si])?;
|
||
si = (si + 1) % spinner.len();
|
||
}
|
||
|
||
common::sleep_ms(delay_ms);
|
||
}
|
||
}
|