//! Показывает локальные дату, время, день недели и часть суток с обновлением раз в секунду. mod common; use std::error::Error; use std::time::{Duration, SystemTime, UNIX_EPOCH}; use escpos_vfd::{VfdWorker, fit_to_width}; fn run_date(args: &[&str]) -> Option { // Используем системную `date`, чтобы пример оставался без дополнительных зависимостей. // Если команда недоступна или вернула ошибку, ниже покажем безопасную заглушку. let out = std::process::Command::new("date") .args(args) .output() .ok()?; if !out.status.success() { return None; } Some(String::from_utf8_lossy(&out.stdout).trim().to_string()) } fn parse_datetime_and_weekday(value: &str) -> Option<(String, u8)> { // Формат одной строки: "DD.MM.YYYY HH:MM:SS|N", где N - номер дня недели 1..=7. let (datetime, weekday) = value.split_once('|')?; let weekday = weekday.parse::().ok()?; (1..=7) .contains(&weekday) .then(|| (datetime.to_string(), weekday)) } fn local_datetime_and_weekday() -> (String, u8) { // Один вызов `date` важен: дата, время и день недели приходят из одного snapshot. // Это убирает редкие рассинхронизации на границе полуночи. 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 { match n { 1 => "Понедельник", 2 => "Вторник", 3 => "Среда", 4 => "Четверг", 5 => "Пятница", 6 => "Суббота", 7 => "Воскресенье", _ => "Понедельник", } } fn weekday_ru_short(n: u8) -> &'static str { match n { 1 => "Пн", 2 => "Вт", 3 => "Ср", 4 => "Чт", 5 => "Пт", 6 => "Сб", 7 => "Вс", _ => "Пн", } } fn time_of_day_ru(hour: u8) -> &'static str { match hour { 5..=10 => "утро", 11..=16 => "день", 17..=22 => "вечер", _ => "ночь", } } fn extract_hour(datetime: &str) -> u8 { // Ожидаем "DD.MM.YYYY HH:MM:SS" и берём HH как два символа после пробела. // При неожиданном формате возвращаем 12, чтобы пример продолжал работать. datetime .split_whitespace() .nth(1) .and_then(|t| t.get(0..2)) .and_then(|hh| hh.parse::().ok()) .unwrap_or(12) } fn main() -> Result<(), Box> { // Аргументы: // 1) port - serial-порт дисплея; // 2) width - ширина строки, нужна для подгонки текста; // 3) brightness - яркость из диапазона пресета 1..=4. let mut args = common::ExampleArgs::from_env()?; let brightness: u8 = args.parse_or(4); let columns = args.columns; // Worker остаётся жить до конца процесса. Handle можно клонировать, но сам worker // владеет serial-портом и фоновым потоком записи. let worker = VfdWorker::start(args.config)?; let vfd = worker.handle(); vfd.clear()?; vfd.set_brightness(brightness)?; loop { let (dt, wd) = local_datetime_and_weekday(); let hour = extract_hour(&dt); let tod = time_of_day_ru(hour); // 1 строка: "28.01.2006 12:03:34". `fit_to_width` обрезает по символам и // дополняет пробелами, чтобы старый текст справа не оставался на дисплее. let line1 = fit_to_width(&dt, columns); // 2 строка: сначала пробуем полное название дня недели, если не влазит - // короткое. Это показывает, как приложение может адаптироваться к ширине VFD. let full = format!("{} сейчас {}", weekday_ru_full(wd), tod); let mut line2 = full; if line2.chars().count() > columns { line2 = format!("{} сейчас {}", weekday_ru_short(wd), tod); } line2 = fit_to_width(&line2, columns); vfd.print_line_diff(1, line1)?; vfd.print_line_diff(2, line2)?; // Привязываем паузу к следующей границе секунды. Тогда часы не "уплывают" из-за // времени, потраченного на форматирование и serial-запись. let now = SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap_or_default() .as_millis(); let ms_to_next = 1000 - (now % 1000) as u64; 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), "ночь"); } }