# Примеры с SysFS (RK3399) и rppal(RPI)
This commit is contained in:
parent
80b100ad66
commit
71aec0db30
14 changed files with 598 additions and 7 deletions
BIN
.DS_Store
vendored
BIN
.DS_Store
vendored
Binary file not shown.
32
Cargo.lock
generated
32
Cargo.lock
generated
|
|
@ -876,6 +876,29 @@ dependencies = [
|
||||||
"windows-link",
|
"windows-link",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pi_nku_menu"
|
||||||
|
version = "0.1.1"
|
||||||
|
dependencies = [
|
||||||
|
"config_lib",
|
||||||
|
"env_logger",
|
||||||
|
"log",
|
||||||
|
"menu_tui",
|
||||||
|
"rppal",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pi_nku_sync"
|
||||||
|
version = "0.1.1"
|
||||||
|
dependencies = [
|
||||||
|
"clap",
|
||||||
|
"config_lib",
|
||||||
|
"env_logger",
|
||||||
|
"log",
|
||||||
|
"protocol_lib",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project-lite"
|
name = "pin-project-lite"
|
||||||
version = "0.2.17"
|
version = "0.2.17"
|
||||||
|
|
@ -1035,6 +1058,15 @@ dependencies = [
|
||||||
"protocol_lib",
|
"protocol_lib",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rppal"
|
||||||
|
version = "0.22.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c1ce3b019009cff02cb6b0e96e7cc2e5c5b90187dc1a490f8ef1521d0596b026"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.38.44"
|
version = "0.38.44"
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,14 @@ members = [
|
||||||
"menu_tui",
|
"menu_tui",
|
||||||
"protocol_lib",
|
"protocol_lib",
|
||||||
"rk_nku_sync",
|
"rk_nku_sync",
|
||||||
"rk_nku_menu", "logger",
|
"pi_nku_sync",
|
||||||
|
"pi_nku_menu",
|
||||||
|
"rk_nku_menu",
|
||||||
|
"logger",
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
authors = ["MU LLC <info@machunit.com>"]
|
authors = ["Dmitry Akimov <von.akimov@gmail.com>"]
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
inquire = "0.7.5"
|
inquire = "0.7.5"
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,4 @@ default-target = "aarch64-unknown-linux-gnu"
|
||||||
[build.zig]
|
[build.zig]
|
||||||
enable = true # Сборка с участием линковщика Zig
|
enable = true # Сборка с участием линковщика Zig
|
||||||
version = "2.31" # Версия glibc, установленная на RK3399-ROC-PC-PRO
|
version = "2.31" # Версия glibc, установленная на RK3399-ROC-PC-PRO
|
||||||
|
# version = "2.28" # Версия glibc, установленная на RPi Zero W
|
||||||
# [target.aarch64-unknown-linux-gnu]
|
|
||||||
# pre-build = [
|
|
||||||
# "apt-get update && apt-get install --assume-yes libxkbcommon"
|
|
||||||
# ]
|
|
||||||
|
|
|
||||||
25
pi_nku_configs/menu_style.toml
Normal file
25
pi_nku_configs/menu_style.toml
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
# First come some various options
|
||||||
|
shadow = false # Don't draw shadows around stacked views
|
||||||
|
borders = "simple" # Alternatives are "none" and "outset"
|
||||||
|
|
||||||
|
# Here we define the color palette.
|
||||||
|
[colors]
|
||||||
|
background = "#000000"
|
||||||
|
# If the value is an array, the first valid color will be used.
|
||||||
|
# If the terminal doesn't support custom color,
|
||||||
|
# non-base colors will be skipped.
|
||||||
|
shadow = ["#000000", "black"]
|
||||||
|
view = "#d3d7cf"
|
||||||
|
|
||||||
|
# Array and simple values have the same effect.
|
||||||
|
primary = ["#111111"]
|
||||||
|
secondary = "#EEEEEE"
|
||||||
|
tertiary = "#444444"
|
||||||
|
|
||||||
|
# Hex values can use lower or uppercase.
|
||||||
|
title_primary = "#0000FF"
|
||||||
|
title_secondary = "#ffff55"
|
||||||
|
|
||||||
|
# Lower precision values can use only 3 digits.
|
||||||
|
highlight = "#00F"
|
||||||
|
highlight_inactive = "#5555FF"
|
||||||
81
pi_nku_configs/nku_scheme.toml
Normal file
81
pi_nku_configs/nku_scheme.toml
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
# [table name] должно соответствовать названию соответствующего параметра в ПО для интерфейсной платы
|
||||||
|
# name - текстовое название параметра (используется в TUI меню)
|
||||||
|
# possible_values - список возможных значений параметра
|
||||||
|
# default - значение по умолчанию (одно из possible_values)
|
||||||
|
# current - актуальное текущее значение параметра (одно из possible_values). Если не указано или некорректно, то используется значение по умолчанию
|
||||||
|
# should_sync - true, если параметр необходимо синхронизировать (pull & push) с внешним микроконтроллером
|
||||||
|
|
||||||
|
[groupnumber]
|
||||||
|
name = "Номер в группе"
|
||||||
|
possible_values = [
|
||||||
|
"0",
|
||||||
|
"1",
|
||||||
|
"2",
|
||||||
|
"3",
|
||||||
|
"4",
|
||||||
|
"5",
|
||||||
|
"6",
|
||||||
|
"7",
|
||||||
|
"8",
|
||||||
|
"9",
|
||||||
|
"10",
|
||||||
|
"11",
|
||||||
|
"12",
|
||||||
|
"13",
|
||||||
|
"14",
|
||||||
|
"15",
|
||||||
|
]
|
||||||
|
default = "1"
|
||||||
|
current = "1"
|
||||||
|
should_sync = "true"
|
||||||
|
|
||||||
|
[soundvolume]
|
||||||
|
name = "Громкость звука"
|
||||||
|
possible_values = [
|
||||||
|
"0%",
|
||||||
|
"25%",
|
||||||
|
"50%",
|
||||||
|
"75%",
|
||||||
|
"100%"
|
||||||
|
]
|
||||||
|
default = "50%"
|
||||||
|
current = "50%"
|
||||||
|
should_sync = "true"
|
||||||
|
|
||||||
|
[musicvolume]
|
||||||
|
name = "Громкость музыки"
|
||||||
|
possible_values = [
|
||||||
|
"0%",
|
||||||
|
"25%",
|
||||||
|
"50%",
|
||||||
|
"75%",
|
||||||
|
"100%"
|
||||||
|
]
|
||||||
|
default = "0%"
|
||||||
|
current = "25%"
|
||||||
|
should_sync = "true"
|
||||||
|
|
||||||
|
[loadcapacity]
|
||||||
|
name = "Грузоподъемность"
|
||||||
|
possible_values = [
|
||||||
|
"СКРЫТО",
|
||||||
|
"240кг 3чел.",
|
||||||
|
"320кг 4чел.",
|
||||||
|
"400кг 5чел.",
|
||||||
|
"450кг 6чел.",
|
||||||
|
"525кг 7чел.",
|
||||||
|
"630кг 8чел.",
|
||||||
|
"800кг 10чел.",
|
||||||
|
"800кг 11чел.",
|
||||||
|
"1000кг 13чел.",
|
||||||
|
"1150кг 15чел.",
|
||||||
|
"1275кг 16чел.",
|
||||||
|
"1275кг 17чел.",
|
||||||
|
"1425кг 18чел.",
|
||||||
|
"1600кг 20чел.",
|
||||||
|
"1600кг 21чел.",
|
||||||
|
]
|
||||||
|
default = "СКРЫТО"
|
||||||
|
current = "400кг 5чел."
|
||||||
|
should_sync = "true"
|
||||||
|
|
||||||
17
pi_nku_configs/pi_scheme.toml
Normal file
17
pi_nku_configs/pi_scheme.toml
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
# [table name] должно соответствовать названию соответствующего параметра в ПО для интерфейсной платы
|
||||||
|
# name - текстовое название параметра (используется в TUI меню)
|
||||||
|
# possible_values - список возможных значений параметра
|
||||||
|
# default - значение по умолчанию (одно из possible_values)
|
||||||
|
# current - актуальное текущее значение параметра (одно из possible_values). Если не указано или некорректно, то используется значение по умолчанию
|
||||||
|
|
||||||
|
[device]
|
||||||
|
name = "Имя порта"
|
||||||
|
possible_values = ["/dev/serial0"]
|
||||||
|
default = "/dev/serial0"
|
||||||
|
current = "/dev/serial0"
|
||||||
|
|
||||||
|
[baudrate]
|
||||||
|
name = "Скорость передачи данных"
|
||||||
|
possible_values = ["9600", "38400", "115200"]
|
||||||
|
default = "115200"
|
||||||
|
current = "115200"
|
||||||
13
pi_nku_menu/Cargo.toml
Normal file
13
pi_nku_menu/Cargo.toml
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
[package]
|
||||||
|
name = "pi_nku_menu"
|
||||||
|
version = "0.1.1"
|
||||||
|
edition = "2024"
|
||||||
|
authors.workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
thiserror = "2.0"
|
||||||
|
rppal = "0.22.1"
|
||||||
|
log = { workspace = true }
|
||||||
|
env_logger = { workspace = true }
|
||||||
|
config_lib = { path = "../config_lib" }
|
||||||
|
menu_tui = { path = "../menu_tui" }
|
||||||
25
pi_nku_menu/assets/style.toml
Normal file
25
pi_nku_menu/assets/style.toml
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
# First come some various options
|
||||||
|
shadow = false # Don't draw shadows around stacked views
|
||||||
|
borders = "simple" # Alternatives are "none" and "outset"
|
||||||
|
|
||||||
|
# Here we define the color palette.
|
||||||
|
[colors]
|
||||||
|
background = "#000000"
|
||||||
|
# If the value is an array, the first valid color will be used.
|
||||||
|
# If the terminal doesn't support custom color,
|
||||||
|
# non-base colors will be skipped.
|
||||||
|
shadow = ["#000000", "black"]
|
||||||
|
view = "#d3d7cf"
|
||||||
|
|
||||||
|
# Array and simple values have the same effect.
|
||||||
|
primary = ["#111111"]
|
||||||
|
secondary = "#EEEEEE"
|
||||||
|
tertiary = "#444444"
|
||||||
|
|
||||||
|
# Hex values can use lower or uppercase.
|
||||||
|
title_primary = "#0000FF"
|
||||||
|
title_secondary = "#ffff55"
|
||||||
|
|
||||||
|
# Lower precision values can use only 3 digits.
|
||||||
|
highlight = "#00F"
|
||||||
|
highlight_inactive = "#5555FF"
|
||||||
94
pi_nku_menu/src/main.rs
Normal file
94
pi_nku_menu/src/main.rs
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
use log::debug;
|
||||||
|
use menu_tui::menu_navigation::NavigationManager;
|
||||||
|
use menu_tui::menu_process::DeviceMenu;
|
||||||
|
use rppal::gpio::{Gpio, InputPin, Trigger};
|
||||||
|
|
||||||
|
/// ### Путь к файлу-схемы параметров устройства
|
||||||
|
const NKU_DEVICE_CONFIG_PATH: &str = "pi_nku_configs/nku_scheme.toml";
|
||||||
|
const TUI_APP_CONFIG_PATH: &str = "pi_nku_configs/menu_style.toml";
|
||||||
|
|
||||||
|
/// Максимальное время бездействия [c], после достижения которого происходит выход из меню
|
||||||
|
const IDLE_TIMEOUT_SEC: u64 = 30;
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
env_logger::init();
|
||||||
|
|
||||||
|
// Создаём меню
|
||||||
|
let mut menu = DeviceMenu::new(NKU_DEVICE_CONFIG_PATH, TUI_APP_CONFIG_PATH);
|
||||||
|
|
||||||
|
let nav_manager = menu.get_navigation_manager();
|
||||||
|
|
||||||
|
menu.show_main_menu();
|
||||||
|
|
||||||
|
// Навигация по меню с помощью кнопок, подключенных к GPIO
|
||||||
|
let _gpio_buttons = gpio_navigation_handlers(nav_manager)?;
|
||||||
|
|
||||||
|
menu.launch_idling_watchdog(IDLE_TIMEOUT_SEC);
|
||||||
|
|
||||||
|
menu.run();
|
||||||
|
|
||||||
|
menu.quit();
|
||||||
|
|
||||||
|
let device_config = menu.get_schema_config()?;
|
||||||
|
|
||||||
|
debug!("Итоговая конфигурация:");
|
||||||
|
for parameter in device_config.get_parameters_names()? {
|
||||||
|
let value = device_config.get_parameter_value(¶meter).unwrap();
|
||||||
|
debug!("{}: {}", parameter, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
device_config.save_parameters_values()?;
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// BCM номер порта кнопки ввода
|
||||||
|
const IN_BTN: u8 = 7;
|
||||||
|
/// BCM номер порта кнопки выбора
|
||||||
|
const SEL_BTN: u8 = 8;
|
||||||
|
|
||||||
|
fn gpio_navigation_handlers(
|
||||||
|
nav_manager: NavigationManager,
|
||||||
|
) -> Result<(InputPin, InputPin), rppal::gpio::Error> {
|
||||||
|
let gpio = Gpio::new()?;
|
||||||
|
|
||||||
|
let mut button_in = gpio.get(IN_BTN)?.into_input_pullup();
|
||||||
|
let mut button_sel = gpio.get(SEL_BTN)?.into_input_pullup();
|
||||||
|
|
||||||
|
button_in.set_reset_on_drop(false);
|
||||||
|
button_sel.set_reset_on_drop(false);
|
||||||
|
|
||||||
|
let nav_down = nav_manager.clone();
|
||||||
|
let mut last_down = Instant::now() - Duration::from_millis(350);
|
||||||
|
button_in.set_async_interrupt(
|
||||||
|
Trigger::FallingEdge,
|
||||||
|
Some(Duration::from_millis(50)),
|
||||||
|
move |_event| {
|
||||||
|
let now = Instant::now();
|
||||||
|
if now.duration_since(last_down) >= Duration::from_millis(350) {
|
||||||
|
last_down = now;
|
||||||
|
log::debug!("IN_BTN -> navigate_down");
|
||||||
|
nav_down.navigate_down();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let nav_sel = nav_manager.clone();
|
||||||
|
let mut last_sel = Instant::now() - Duration::from_millis(350);
|
||||||
|
button_sel.set_async_interrupt(
|
||||||
|
Trigger::FallingEdge,
|
||||||
|
Some(Duration::from_millis(50)),
|
||||||
|
move |_event| {
|
||||||
|
let now = Instant::now();
|
||||||
|
if now.duration_since(last_sel) >= Duration::from_millis(350) {
|
||||||
|
last_sel = now;
|
||||||
|
log::debug!("SEL_BTN -> select_item");
|
||||||
|
nav_sel.select_item();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok((button_in, button_sel))
|
||||||
|
}
|
||||||
13
pi_nku_sync/Cargo.toml
Normal file
13
pi_nku_sync/Cargo.toml
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
[package]
|
||||||
|
name = "pi_nku_sync"
|
||||||
|
version = "0.1.1"
|
||||||
|
edition = "2024"
|
||||||
|
authors.workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
protocol_lib = { path = "../protocol_lib" }
|
||||||
|
config_lib = { path = "../config_lib" }
|
||||||
|
clap = { workspace = true }
|
||||||
|
log = { workspace = true }
|
||||||
|
env_logger = { workspace = true }
|
||||||
|
|
||||||
130
pi_nku_sync/src/main.rs
Normal file
130
pi_nku_sync/src/main.rs
Normal file
|
|
@ -0,0 +1,130 @@
|
||||||
|
mod nku_client;
|
||||||
|
mod serial_config;
|
||||||
|
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use clap::Parser;
|
||||||
|
use log::{error, info, warn};
|
||||||
|
|
||||||
|
/// Количество попыток выполнить запрос
|
||||||
|
const REQUEST_ATTEMPTS: u8 = 5;
|
||||||
|
|
||||||
|
#[derive(Parser)]
|
||||||
|
#[command(author = "Akimov Dmitry MU LLC", name = "smart_sync", version = "0.1.0", about, long_about = None)]
|
||||||
|
struct Args {
|
||||||
|
/// Тип команды: pull - запрос сохраненных в устройстве настроек, push - отправка новых настроек
|
||||||
|
#[arg(short = 'm', long = "mode")]
|
||||||
|
mode: CommandMode,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<(), String> {
|
||||||
|
let args = Args::parse();
|
||||||
|
|
||||||
|
env_logger::init();
|
||||||
|
info!("rk_nku_sync> command mode: {:?}", args.mode);
|
||||||
|
|
||||||
|
let mut nku_client = nku_client::NkuClient::new()?;
|
||||||
|
|
||||||
|
match args.mode {
|
||||||
|
CommandMode::Pull => pull_parameters(&mut nku_client)?,
|
||||||
|
CommandMode::Push => push_parameters(&mut nku_client)?,
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Отправка настроек на устройство
|
||||||
|
fn push_parameters(client: &mut nku_client::NkuClient) -> Result<(), String> {
|
||||||
|
// Цикл попыток установить соединение
|
||||||
|
let mut attempts: u8 = 1;
|
||||||
|
'push_request_loop: loop {
|
||||||
|
warn!("Push request attempt: {attempts}");
|
||||||
|
if client.push_parameters_to_device().is_ok() {
|
||||||
|
break 'push_request_loop;
|
||||||
|
}
|
||||||
|
let result = client.push_parameters_to_device();
|
||||||
|
|
||||||
|
if result.is_ok() {
|
||||||
|
break 'push_request_loop;
|
||||||
|
}
|
||||||
|
|
||||||
|
error!(
|
||||||
|
"{}",
|
||||||
|
format!("Failed to push parameters: {result:?}").as_str()
|
||||||
|
);
|
||||||
|
|
||||||
|
attempts += 1;
|
||||||
|
|
||||||
|
if attempts > REQUEST_ATTEMPTS {
|
||||||
|
return Err("Push request failed!".to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("Parameters have been pushed to device!");
|
||||||
|
|
||||||
|
// Запуск стриминга данных от станции
|
||||||
|
attempts = 1;
|
||||||
|
'start_streaming_loop: loop {
|
||||||
|
warn!("Start streaming attempt: {attempts}");
|
||||||
|
|
||||||
|
let result = client.start_elevator_data_streaming(nku_client::StreamingMode::OnChangeMode);
|
||||||
|
|
||||||
|
if result.is_ok() {
|
||||||
|
break 'start_streaming_loop;
|
||||||
|
}
|
||||||
|
error!(
|
||||||
|
"{}",
|
||||||
|
format!("Failed to push parameters: {result:?}").as_str()
|
||||||
|
);
|
||||||
|
attempts += 1;
|
||||||
|
|
||||||
|
if attempts > REQUEST_ATTEMPTS {
|
||||||
|
return Err("Start streaming failed!".to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
info!("Data streaming from MCU has been started!");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Получение настроек, сохраненных в устройстве
|
||||||
|
fn pull_parameters(client: &mut nku_client::NkuClient) -> Result<(), String> {
|
||||||
|
let mut attempts: u8 = 1;
|
||||||
|
// Цикл попыток установить соединение
|
||||||
|
'pull_request_loop: loop {
|
||||||
|
warn!("Pull request attempt: {attempts}");
|
||||||
|
|
||||||
|
let result = client.pull_parameters_from_device();
|
||||||
|
if result.is_ok() {
|
||||||
|
break 'pull_request_loop;
|
||||||
|
}
|
||||||
|
error!(
|
||||||
|
"{}",
|
||||||
|
format!("Failed to push parameters: {result:?}").as_str()
|
||||||
|
);
|
||||||
|
|
||||||
|
attempts += 1;
|
||||||
|
|
||||||
|
if attempts > REQUEST_ATTEMPTS {
|
||||||
|
return Err("Pull request failed!".to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
info!("Parameters have been pulled from device!");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
enum CommandMode {
|
||||||
|
Pull,
|
||||||
|
Push,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for CommandMode {
|
||||||
|
type Err = String;
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s {
|
||||||
|
"pull" => Ok(CommandMode::Pull),
|
||||||
|
"push" => Ok(CommandMode::Push),
|
||||||
|
_ => Err(format!("Unknown command mode: {s}")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
124
pi_nku_sync/src/nku_client.rs
Normal file
124
pi_nku_sync/src/nku_client.rs
Normal file
|
|
@ -0,0 +1,124 @@
|
||||||
|
use crate::serial_config::SerialPortConfig;
|
||||||
|
use config_lib::device_config::DeviceConfig;
|
||||||
|
use log::{debug, info};
|
||||||
|
use protocol_lib::client::HostClient;
|
||||||
|
|
||||||
|
const BOARD_RESPONSE_TIMEOUT_MS: std::time::Duration = std::time::Duration::from_millis(5000);
|
||||||
|
const SERIAL_PORT_CONFIG_PATH: &str = "pi_nku_configs/pi_scheme.toml";
|
||||||
|
const NKU_DEVICE_CONFIG_PATH: &str = "pi_nku_configs/nku_scheme.toml";
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
/// Режимы стриминга данных от устройства
|
||||||
|
pub enum StreamingMode {
|
||||||
|
/// Без стриминга
|
||||||
|
__SilentMode = 0,
|
||||||
|
/// Отправка данных, в случае изменения их состояния (и наличия)
|
||||||
|
OnChangeMode = 1,
|
||||||
|
/// Отправка данных периодически, с заданным в настройках (periodicity) периодом
|
||||||
|
__PeriodicMode = 2,
|
||||||
|
/// Отправка данных по требованию
|
||||||
|
__OnDemandMode = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct NkuClient {
|
||||||
|
nku_client: HostClient,
|
||||||
|
nku_config: DeviceConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NkuClient {
|
||||||
|
pub fn new() -> Result<Self, String> {
|
||||||
|
// Чтение параметров последовательного порта
|
||||||
|
let serial_config = SerialPortConfig::new(SERIAL_PORT_CONFIG_PATH)?;
|
||||||
|
|
||||||
|
let nku_config = DeviceConfig::create_parameter_list(NKU_DEVICE_CONFIG_PATH)?;
|
||||||
|
debug!("Parameters list: {:#?}", nku_config.get_parameters_names());
|
||||||
|
|
||||||
|
let port_name = serial_config.get_serial_name();
|
||||||
|
let baudrate = serial_config.get_serial_baudrate();
|
||||||
|
|
||||||
|
debug!("Serial config: {port_name}, {baudrate}");
|
||||||
|
|
||||||
|
let nku_client = HostClient::connect(&port_name, baudrate, BOARD_RESPONSE_TIMEOUT_MS)?;
|
||||||
|
|
||||||
|
info!("Connection with IMv has been established!");
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
nku_client,
|
||||||
|
nku_config,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ### Запрос начала стриминга данных со станции управления
|
||||||
|
pub fn start_elevator_data_streaming(&mut self, mode: StreamingMode) -> Result<String, String> {
|
||||||
|
let response_from_mcu = self
|
||||||
|
.nku_client
|
||||||
|
.send_request(format!("set mode {}", mode.clone() as u8).as_str())?;
|
||||||
|
if !response_from_mcu.contains(format!("mode: {}", mode as u8).as_str()) {
|
||||||
|
return Err("START STREAMING> invalid response".into());
|
||||||
|
}
|
||||||
|
Ok(response_from_mcu)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ### Запрос сохраненных в устройстве настроек
|
||||||
|
pub fn pull_parameters_from_device(&mut self) -> Result<(), String> {
|
||||||
|
let parameters_list = self.nku_config.get_parameters_names()?;
|
||||||
|
|
||||||
|
for parameter in parameters_list {
|
||||||
|
let request_string = format!("get {parameter}");
|
||||||
|
debug!("PULL> sending request: {request_string}");
|
||||||
|
let response_from_mcu = self.nku_client.send_request(&request_string)?;
|
||||||
|
debug!("PULL> response from MCU: {response_from_mcu}");
|
||||||
|
|
||||||
|
let parameter_value =
|
||||||
|
NkuClient::extract_parameter_value(¶meter, response_from_mcu)?;
|
||||||
|
self.nku_config
|
||||||
|
.set_parameter_value_using_index(¶meter, parameter_value)?;
|
||||||
|
}
|
||||||
|
self.nku_config.save_parameters_values()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Извлечение значения параметра из отклика от MCU
|
||||||
|
fn extract_parameter_value(
|
||||||
|
parameter_name: &str,
|
||||||
|
response_from_mcu: String,
|
||||||
|
) -> Result<u8, String> {
|
||||||
|
// Ожидается ответ в формате "parameter_name:value"
|
||||||
|
let tokens = response_from_mcu.split(':').collect::<Vec<&str>>().to_vec();
|
||||||
|
|
||||||
|
if tokens.len() != 2 {
|
||||||
|
return Err(format!(
|
||||||
|
"Failed to parse response from MCU: {response_from_mcu}"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if tokens[0] != parameter_name {
|
||||||
|
return Err(format!(
|
||||||
|
"Failed to parse response from MCU: {response_from_mcu}"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
tokens[1].trim().parse::<u8>().map_err(|e| e.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Отправка новых настроек на устройство для последующего сохранения
|
||||||
|
pub fn push_parameters_to_device(&mut self) -> Result<(), String> {
|
||||||
|
let parameters_list = self.nku_config.get_parameters_names()?;
|
||||||
|
|
||||||
|
for parameter in parameters_list {
|
||||||
|
let parameter_value = self
|
||||||
|
.nku_config
|
||||||
|
.get_parameter_index_using_value(¶meter)?;
|
||||||
|
let request_string = format!("set {parameter} {parameter_value}");
|
||||||
|
debug!("PUSH> sending request: {request_string}");
|
||||||
|
let response_from_mcu = self.nku_client.send_request(&request_string)?;
|
||||||
|
|
||||||
|
debug!("PUSH> response from MCU: {response_from_mcu}");
|
||||||
|
if !response_from_mcu.contains(format!("{parameter}: {parameter_value}").as_str()) {
|
||||||
|
return Err("PUSH> invalid response".into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
38
pi_nku_sync/src/serial_config.rs
Normal file
38
pi_nku_sync/src/serial_config.rs
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
use config_lib::device_config::DeviceConfig;
|
||||||
|
use log::debug;
|
||||||
|
|
||||||
|
pub struct SerialPortConfig {
|
||||||
|
serial_name: String,
|
||||||
|
serial_baudrate: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SerialPortConfig {
|
||||||
|
/// Чтение параметров последовательного порта из файла-схемы TOML
|
||||||
|
pub fn new(path_to_scheme: &str) -> Result<Self, String> {
|
||||||
|
let smart_serial_parameters = DeviceConfig::create_parameter_list(path_to_scheme)?;
|
||||||
|
|
||||||
|
let serial_name = smart_serial_parameters.get_parameter_value("device")?;
|
||||||
|
let serial_baudrate = smart_serial_parameters
|
||||||
|
.get_parameter_value("baudrate")?
|
||||||
|
.parse::<u32>()
|
||||||
|
.map_err(|e: std::num::ParseIntError| e.to_string())?;
|
||||||
|
|
||||||
|
debug!("serial port_name: {}", serial_name);
|
||||||
|
debug!("serial baudrate: {}", serial_baudrate);
|
||||||
|
|
||||||
|
Ok(SerialPortConfig {
|
||||||
|
serial_name,
|
||||||
|
serial_baudrate,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Чтение имени последовательного порта из конфига
|
||||||
|
pub fn get_serial_name(&self) -> String {
|
||||||
|
self.serial_name.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Чтение скорости последовательного порта из конфига
|
||||||
|
pub fn get_serial_baudrate(&self) -> u32 {
|
||||||
|
self.serial_baudrate
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue