mod app_core;
mod event_loop;
mod frames;
mod inprocess;
mod load;
use std::net::Ipv4Addr;
use std::sync::OnceLock;
use std::time::{Duration, Instant};
use bacnet_types::enums::Segmentation;
use ratatui::backend::TestBackend;
use ratatui::crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use ratatui::Terminal;
use super::app::picker::Picker;
use super::app::{update, Action, App, AppConfig};
use super::keymap::map_key;
use super::log_layer::LogRing;
use super::message::{AddressStyle, Command, DeviceRow, WorkerEvent};
use crate::core::interfaces::Ipv4Interface;
pub(super) fn t0() -> Instant {
static BASE: OnceLock<Instant> = OnceLock::new();
*BASE.get_or_init(Instant::now)
}
pub(super) fn at(secs: u64) -> Instant {
t0() + Duration::from_secs(secs)
}
pub(super) fn new_app(picker: Option<Picker>) -> App {
App::new(
AppConfig {
transport: "BIP",
style: AddressStyle::Bip,
color: false,
picker,
now: t0(),
},
LogRing::new(100),
)
}
pub(super) fn connected_app() -> App {
let mut app = new_app(None);
worker(
&mut app,
WorkerEvent::Connected {
local: "10.0.0.1:47808".into(),
},
);
app
}
pub(super) fn row(instance: u32, address: &str, vendor_id: u16, seen: u64) -> DeviceRow {
DeviceRow {
instance,
address: address.into(),
network: None,
vendor_id,
max_apdu: 1476,
segmentation: Segmentation::NONE,
last_seen: at(seen),
}
}
pub(super) fn act(app: &mut App, action: Action) -> Vec<Command> {
update(app, action)
}
pub(super) fn worker(app: &mut App, event: WorkerEvent) -> Vec<Command> {
update(app, Action::Worker(event))
}
pub(super) fn press(app: &mut App, code: KeyCode) -> Vec<Command> {
press_with(app, code, KeyModifiers::NONE)
}
pub(super) fn press_with(app: &mut App, code: KeyCode, modifiers: KeyModifiers) -> Vec<Command> {
match map_key(app, KeyEvent::new(code, modifiers)) {
Some(action) => update(app, action),
None => Vec::new(),
}
}
pub(super) fn type_text(app: &mut App, text: &str) {
for ch in text.chars() {
press(app, KeyCode::Char(ch));
}
}
pub(super) fn tick(app: &mut App, now: Instant) {
let dropped = app.dropped;
let log_generation = app.log.generation();
update(
app,
Action::Tick {
now,
dropped,
log_generation,
},
);
}
pub(super) fn advance(app: &mut App, by: Duration) {
let now = app.now + by;
tick(app, now);
}
pub(super) fn visible(app: &mut App) -> Vec<u32> {
app.settle();
app.devices.visible().map(|r| r.instance).collect()
}
pub(super) fn render(app: &mut App, width: u16, height: u16) -> Terminal<TestBackend> {
app.settle();
let mut terminal = Terminal::new(TestBackend::new(width, height)).unwrap();
terminal
.draw(|frame| super::view::draw(frame, app))
.unwrap();
terminal
}
pub(super) fn screen(app: &mut App, width: u16, height: u16) -> String {
render(app, width, height).backend().to_string()
}
pub(super) fn interfaces() -> Vec<Ipv4Interface> {
vec![
Ipv4Interface {
name: "en0".into(),
ip: Ipv4Addr::new(10, 0, 1, 5),
broadcast: Ipv4Addr::new(10, 0, 1, 255),
},
Ipv4Interface {
name: "en7".into(),
ip: Ipv4Addr::new(192, 168, 50, 20),
broadcast: Ipv4Addr::new(192, 168, 50, 255),
},
]
}