рефакторинг библиотеки.
This commit is contained in:
+12
-28
@@ -1,13 +1,11 @@
|
||||
//! Демонстрирует мигание произвольного текста в выбранной позиции через `print_at`.
|
||||
|
||||
mod common;
|
||||
|
||||
use anyhow::Result;
|
||||
use std::time::Duration;
|
||||
|
||||
use m::vfd::VfdConfig;
|
||||
use m::worker::VfdWorker;
|
||||
|
||||
fn spaces(n: usize) -> String {
|
||||
" ".repeat(n)
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// args:
|
||||
// 1) port (default: /dev/cu.usbmodem101)
|
||||
@@ -16,28 +14,14 @@ fn main() -> Result<()> {
|
||||
// 4) x (default: 1) 1-based
|
||||
// 5) y (default: 1) 1-based line
|
||||
// 6) text (default: "BLINK")
|
||||
let mut args = std::env::args().skip(1);
|
||||
|
||||
let port_name = args.next().unwrap_or("/dev/cu.usbmodem101".into());
|
||||
|
||||
let width: usize = args.next().as_deref().unwrap_or("20").parse().unwrap_or(20);
|
||||
|
||||
let delay_ms: u64 = args
|
||||
.next()
|
||||
.as_deref()
|
||||
.unwrap_or("400")
|
||||
.parse()
|
||||
.unwrap_or(400);
|
||||
|
||||
let x: u8 = args.next().as_deref().unwrap_or("1").parse().unwrap_or(1);
|
||||
|
||||
let y: u8 = args.next().as_deref().unwrap_or("1").parse().unwrap_or(1);
|
||||
|
||||
let text = args.next().unwrap_or("BLINK".into());
|
||||
let mut args = common::ExampleArgs::from_env();
|
||||
let delay_ms: u64 = args.parse_or(400);
|
||||
let x: u8 = args.parse_or(1);
|
||||
let y: u8 = args.parse_or(1);
|
||||
let text = args.string_or("BLINK");
|
||||
let text_len = text.chars().count();
|
||||
|
||||
let cfg = VfdConfig::new(port_name).with_width(width);
|
||||
let worker = VfdWorker::start(cfg)?;
|
||||
let worker = VfdWorker::start(args.config)?;
|
||||
let vfd = worker.handle();
|
||||
|
||||
vfd.clear();
|
||||
@@ -47,7 +31,7 @@ fn main() -> Result<()> {
|
||||
let _ = vfd.print_at(1, y, ""); // просто чтобы “активировать” строку у некоторых дисплеев
|
||||
}
|
||||
|
||||
let blank = spaces(text_len);
|
||||
let blank = " ".repeat(text_len);
|
||||
|
||||
let mut on = false;
|
||||
loop {
|
||||
@@ -57,6 +41,6 @@ fn main() -> Result<()> {
|
||||
let _ = vfd.print_at(x, y, &text);
|
||||
}
|
||||
on = !on;
|
||||
std::thread::sleep(Duration::from_millis(delay_ms));
|
||||
common::sleep_ms(delay_ms);
|
||||
}
|
||||
}
|
||||
|
||||
+8
-23
@@ -1,7 +1,9 @@
|
||||
//! Демонстрирует четыре уровня яркости и обновление строки без полного мерцания.
|
||||
|
||||
mod common;
|
||||
|
||||
use anyhow::Result;
|
||||
use std::time::Duration;
|
||||
|
||||
use m::vfd::VfdConfig;
|
||||
use m::worker::VfdWorker;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
@@ -9,26 +11,9 @@ fn main() -> Result<()> {
|
||||
// 1) port (default: /dev/cu.usbmodem101)
|
||||
// 2) width (default: 20)
|
||||
// 3) delay_ms (default: 800)
|
||||
let port_name = std::env::args()
|
||||
.nth(1)
|
||||
.unwrap_or("/dev/cu.usbmodem101".into());
|
||||
|
||||
let width: usize = std::env::args()
|
||||
.nth(2)
|
||||
.as_deref()
|
||||
.unwrap_or("20")
|
||||
.parse()
|
||||
.unwrap_or(20);
|
||||
|
||||
let delay_ms: u64 = std::env::args()
|
||||
.nth(3)
|
||||
.as_deref()
|
||||
.unwrap_or("800")
|
||||
.parse()
|
||||
.unwrap_or(800);
|
||||
|
||||
let cfg = VfdConfig::new(port_name).with_width(width);
|
||||
let worker = VfdWorker::start(cfg)?;
|
||||
let mut args = common::ExampleArgs::from_env();
|
||||
let delay_ms: u64 = args.parse_or(800);
|
||||
let worker = VfdWorker::start(args.config)?;
|
||||
let vfd = worker.handle();
|
||||
|
||||
vfd.clear();
|
||||
@@ -47,7 +32,7 @@ fn main() -> Result<()> {
|
||||
|
||||
let _ = vfd.print_line_diff(2, line2);
|
||||
|
||||
std::thread::sleep(Duration::from_millis(delay_ms));
|
||||
common::sleep_ms(delay_ms);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+44
-44
@@ -1,8 +1,11 @@
|
||||
// examples/clock_ru.rs
|
||||
//! Показывает локальные дату, время, день недели и часть суток с обновлением раз в секунду.
|
||||
|
||||
mod common;
|
||||
|
||||
use anyhow::Result;
|
||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||
|
||||
use m::vfd::VfdConfig;
|
||||
use m::vfd::fit_to_width;
|
||||
use m::worker::VfdWorker;
|
||||
|
||||
fn run_date(args: &[&str]) -> Option<String> {
|
||||
@@ -16,16 +19,19 @@ fn run_date(args: &[&str]) -> Option<String> {
|
||||
Some(String::from_utf8_lossy(&out.stdout).trim().to_string())
|
||||
}
|
||||
|
||||
fn local_datetime_ddmmyyyy_hhmmss() -> String {
|
||||
// 28.01.2006 12:03:34
|
||||
run_date(&["+%d.%m.%Y %H:%M:%S"]).unwrap_or_else(|| "??.??.???? ??:??:??".to_string())
|
||||
fn parse_datetime_and_weekday(value: &str) -> Option<(String, u8)> {
|
||||
let (datetime, weekday) = value.split_once('|')?;
|
||||
let weekday = weekday.parse::<u8>().ok()?;
|
||||
(1..=7)
|
||||
.contains(&weekday)
|
||||
.then(|| (datetime.to_string(), weekday))
|
||||
}
|
||||
|
||||
fn local_weekday_num_1_7() -> u8 {
|
||||
// 1..7 (Mon..Sun)
|
||||
run_date(&["+%u"])
|
||||
.and_then(|s| s.parse::<u8>().ok())
|
||||
.unwrap_or(1)
|
||||
fn local_datetime_and_weekday() -> (String, u8) {
|
||||
run_date(&["+%d.%m.%Y %H:%M:%S|%u"])
|
||||
.as_deref()
|
||||
.and_then(parse_datetime_and_weekday)
|
||||
.unwrap_or_else(|| ("??.??.???? ??:??:??".to_string(), 1))
|
||||
}
|
||||
|
||||
fn weekday_ru_full(n: u8) -> &'static str {
|
||||
@@ -74,51 +80,22 @@ fn extract_hour(datetime: &str) -> u8 {
|
||||
.unwrap_or(12)
|
||||
}
|
||||
|
||||
fn fit_to_width(s: &str, width: usize) -> String {
|
||||
// простое “обрезать по символам”, чтобы кириллицу не порвать
|
||||
let mut out = String::new();
|
||||
for (i, ch) in s.chars().enumerate() {
|
||||
if i >= width {
|
||||
break;
|
||||
}
|
||||
out.push(ch);
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// args:
|
||||
// 1) port (default: /dev/cu.usbmodem101)
|
||||
// 2) width (default: 20)
|
||||
// 3) brightness 1..4 (default: 2)
|
||||
let port_name = std::env::args()
|
||||
.nth(1)
|
||||
.unwrap_or("/dev/cu.usbmodem101".into());
|
||||
|
||||
let width: usize = std::env::args()
|
||||
.nth(2)
|
||||
.as_deref()
|
||||
.unwrap_or("20")
|
||||
.parse()
|
||||
.unwrap_or(20);
|
||||
|
||||
let brightness: u8 = std::env::args()
|
||||
.nth(3)
|
||||
.as_deref()
|
||||
.unwrap_or("2")
|
||||
.parse()
|
||||
.unwrap_or(2);
|
||||
|
||||
let cfg = VfdConfig::new(port_name).with_width(width);
|
||||
let worker = VfdWorker::start(cfg)?;
|
||||
let mut args = common::ExampleArgs::from_env();
|
||||
let brightness: u8 = args.parse_or(2);
|
||||
let width = args.width;
|
||||
let worker = VfdWorker::start(args.config)?;
|
||||
let vfd = worker.handle();
|
||||
|
||||
vfd.clear();
|
||||
vfd.set_brightness(brightness);
|
||||
|
||||
loop {
|
||||
let dt = local_datetime_ddmmyyyy_hhmmss();
|
||||
let wd = local_weekday_num_1_7();
|
||||
let (dt, wd) = local_datetime_and_weekday();
|
||||
let hour = extract_hour(&dt);
|
||||
let tod = time_of_day_ru(hour);
|
||||
|
||||
@@ -148,3 +125,26 @@ fn main() -> Result<()> {
|
||||
std::thread::sleep(Duration::from_millis(ms_to_next));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn parses_date_and_weekday_from_one_process_result() {
|
||||
assert_eq!(
|
||||
parse_datetime_and_weekday("15.08.2026 23:59:58|6"),
|
||||
Some(("15.08.2026 23:59:58".to_string(), 6))
|
||||
);
|
||||
assert_eq!(parse_datetime_and_weekday("invalid"), None);
|
||||
assert_eq!(parse_datetime_and_weekday("15.08.2026 23:59:58|8"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn classifies_hours_and_extracts_them_safely() {
|
||||
assert_eq!(extract_hour("15.08.2026 05:00:00"), 5);
|
||||
assert_eq!(extract_hour("invalid"), 12);
|
||||
assert_eq!(time_of_day_ru(5), "утро");
|
||||
assert_eq!(time_of_day_ru(23), "ночь");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
//! Общие утилиты командной строки для всех демонстрационных программ.
|
||||
#![allow(
|
||||
dead_code,
|
||||
reason = "модуль компилируется отдельно для каждого примера, поэтому часть API используется в соседних примерах"
|
||||
)]
|
||||
|
||||
use m::vfd::VfdConfig;
|
||||
use std::str::FromStr;
|
||||
use std::vec::IntoIter;
|
||||
|
||||
const DEFAULT_PORT: &str = "/dev/cu.usbmodem101";
|
||||
const DEFAULT_WIDTH: usize = 20;
|
||||
|
||||
/// Общие аргументы примера: serial-порт, ширина дисплея и оставшиеся параметры.
|
||||
pub struct ExampleArgs {
|
||||
pub config: VfdConfig,
|
||||
pub width: usize,
|
||||
remaining: IntoIter<String>,
|
||||
}
|
||||
|
||||
impl ExampleArgs {
|
||||
/// Читает общие аргументы из командной строки и применяет безопасные значения по умолчанию.
|
||||
pub fn from_env() -> Self {
|
||||
let mut values = std::env::args().skip(1);
|
||||
let port_name = values.next().unwrap_or_else(|| DEFAULT_PORT.to_string());
|
||||
let width = values
|
||||
.next()
|
||||
.and_then(|value| value.parse().ok())
|
||||
.unwrap_or(DEFAULT_WIDTH);
|
||||
let config = VfdConfig::new(port_name).with_width(width);
|
||||
|
||||
Self {
|
||||
width: config.width,
|
||||
config,
|
||||
remaining: values.collect::<Vec<_>>().into_iter(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Читает следующий аргумент нужного типа или возвращает переданное значение по умолчанию.
|
||||
pub fn parse_or<T>(&mut self, default: T) -> T
|
||||
where
|
||||
T: FromStr,
|
||||
{
|
||||
self.remaining
|
||||
.next()
|
||||
.and_then(|value| value.parse().ok())
|
||||
.unwrap_or(default)
|
||||
}
|
||||
|
||||
/// Читает следующий строковый аргумент или возвращает строку по умолчанию.
|
||||
pub fn string_or(&mut self, default: &str) -> String {
|
||||
self.remaining.next().unwrap_or_else(|| default.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
/// Приостанавливает пример на указанное число миллисекунд.
|
||||
pub fn sleep_ms(delay_ms: u64) {
|
||||
std::thread::sleep(std::time::Duration::from_millis(delay_ms));
|
||||
}
|
||||
+10
-36
@@ -1,7 +1,10 @@
|
||||
//! Демонстрирует фоновую бегущую строку с регулируемой скоростью и паузой в конце.
|
||||
|
||||
mod common;
|
||||
|
||||
use anyhow::Result;
|
||||
use std::time::Duration;
|
||||
|
||||
use m::vfd::VfdConfig;
|
||||
use m::worker::VfdWorker;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
@@ -11,40 +14,11 @@ fn main() -> Result<()> {
|
||||
// 3) cps (chars per second, default: 8)
|
||||
// 4) end_pause_ms (default: 1500)
|
||||
// 5) brightness 1..4 (default: 2)
|
||||
let port_name = std::env::args()
|
||||
.nth(1)
|
||||
.unwrap_or("/dev/cu.usbmodem101".into());
|
||||
|
||||
let width: usize = std::env::args()
|
||||
.nth(2)
|
||||
.as_deref()
|
||||
.unwrap_or("20")
|
||||
.parse()
|
||||
.unwrap_or(20);
|
||||
|
||||
let cps: u32 = std::env::args()
|
||||
.nth(3)
|
||||
.as_deref()
|
||||
.unwrap_or("8")
|
||||
.parse()
|
||||
.unwrap_or(8);
|
||||
|
||||
let end_pause_ms: u64 = std::env::args()
|
||||
.nth(4)
|
||||
.as_deref()
|
||||
.unwrap_or("1500")
|
||||
.parse()
|
||||
.unwrap_or(1500);
|
||||
|
||||
let brightness: u8 = std::env::args()
|
||||
.nth(5)
|
||||
.as_deref()
|
||||
.unwrap_or("2")
|
||||
.parse()
|
||||
.unwrap_or(2);
|
||||
|
||||
let cfg = VfdConfig::new(port_name).with_width(width);
|
||||
let worker = VfdWorker::start(cfg)?;
|
||||
let mut args = common::ExampleArgs::from_env();
|
||||
let cps: u32 = args.parse_or(8);
|
||||
let end_pause_ms: u64 = args.parse_or(1500);
|
||||
let brightness: u8 = args.parse_or(2);
|
||||
let worker = VfdWorker::start(args.config)?;
|
||||
let vfd = worker.handle();
|
||||
|
||||
vfd.clear();
|
||||
@@ -61,6 +35,6 @@ fn main() -> Result<()> {
|
||||
let _ = vfd.print_line_diff(1, "Бегущая строка");
|
||||
|
||||
loop {
|
||||
std::thread::sleep(Duration::from_secs(3600));
|
||||
common::sleep_ms(3_600_000);
|
||||
}
|
||||
}
|
||||
|
||||
+9
-23
@@ -1,7 +1,9 @@
|
||||
//! Демонстрирует позиционирование символа и его движение по второй строке дисплея.
|
||||
|
||||
mod common;
|
||||
|
||||
use anyhow::Result;
|
||||
use std::time::Duration;
|
||||
|
||||
use m::vfd::VfdConfig;
|
||||
use m::worker::VfdWorker;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
@@ -9,26 +11,10 @@ fn main() -> Result<()> {
|
||||
// 1) port (default: /dev/cu.usbmodem101)
|
||||
// 2) width (default: 20)
|
||||
// 3) delay_ms (default: 80)
|
||||
let port_name = std::env::args()
|
||||
.nth(1)
|
||||
.unwrap_or("/dev/cu.usbmodem101".into());
|
||||
|
||||
let width: usize = std::env::args()
|
||||
.nth(2)
|
||||
.as_deref()
|
||||
.unwrap_or("20")
|
||||
.parse()
|
||||
.unwrap_or(20);
|
||||
|
||||
let delay_ms: u64 = std::env::args()
|
||||
.nth(3)
|
||||
.as_deref()
|
||||
.unwrap_or("80")
|
||||
.parse()
|
||||
.unwrap_or(80);
|
||||
|
||||
let cfg = VfdConfig::new(port_name).with_width(width);
|
||||
let worker = VfdWorker::start(cfg)?;
|
||||
let mut args = common::ExampleArgs::from_env();
|
||||
let delay_ms: u64 = args.parse_or(80);
|
||||
let width = args.width;
|
||||
let worker = VfdWorker::start(args.config)?;
|
||||
let vfd = worker.handle();
|
||||
|
||||
vfd.clear();
|
||||
@@ -69,6 +55,6 @@ fn main() -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
std::thread::sleep(Duration::from_millis(delay_ms));
|
||||
common::sleep_ms(delay_ms);
|
||||
}
|
||||
}
|
||||
|
||||
+12
-23
@@ -1,7 +1,9 @@
|
||||
//! Имитирует компактный дашборд и обновляет только изменяемые поля через `print_at`.
|
||||
|
||||
mod common;
|
||||
|
||||
use anyhow::Result;
|
||||
use std::time::Duration;
|
||||
|
||||
use m::vfd::VfdConfig;
|
||||
use m::worker::VfdWorker;
|
||||
|
||||
fn pad_left(s: &str, width: usize) -> String {
|
||||
@@ -18,26 +20,13 @@ fn main() -> Result<()> {
|
||||
// 1) port (default: /dev/cu.usbmodem101)
|
||||
// 2) width (default: 20)
|
||||
// 3) delay_ms (default: 200)
|
||||
let port_name = std::env::args()
|
||||
.nth(1)
|
||||
.unwrap_or("/dev/cu.usbmodem101".into());
|
||||
|
||||
let width: usize = std::env::args()
|
||||
.nth(2)
|
||||
.as_deref()
|
||||
.unwrap_or("20")
|
||||
.parse()
|
||||
.unwrap_or(20);
|
||||
|
||||
let delay_ms: u64 = std::env::args()
|
||||
.nth(3)
|
||||
.as_deref()
|
||||
.unwrap_or("200")
|
||||
.parse()
|
||||
.unwrap_or(200);
|
||||
|
||||
let cfg = VfdConfig::new(port_name).with_width(width);
|
||||
let worker = VfdWorker::start(cfg)?;
|
||||
let mut args = common::ExampleArgs::from_env();
|
||||
let delay_ms: u64 = args.parse_or(200);
|
||||
let width = args.width;
|
||||
if width < 20 {
|
||||
anyhow::bail!("update_at requires a display width of at least 20 columns");
|
||||
}
|
||||
let worker = VfdWorker::start(args.config)?;
|
||||
let vfd = worker.handle();
|
||||
|
||||
vfd.clear();
|
||||
@@ -103,6 +92,6 @@ fn main() -> Result<()> {
|
||||
si = (si + 1) % spinner.len();
|
||||
}
|
||||
|
||||
std::thread::sleep(Duration::from_millis(delay_ms));
|
||||
common::sleep_ms(delay_ms);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user