add all crates: domain, protocol, application, client, adapters, ESP32 firmware
Server: domain (entities, value objects, ports), protocol (postcard wire types), application (config service, data projection), adapters (config-memory, tcp-server), bootstrap (composition root with fake data). Client: client-domain (layout engine, render tree, HAL ports), client-application (message handling, repaint commands), adapters (tcp-client, display-terminal), client-desktop (end-to-end working). ESP32: client-esp32 firmware with ILI9341 display over SPI, WiFi networking. Display test verified on hardware — landscape orientation, text rendering works. 60 workspace tests, all passing.
This commit is contained in:
12
crates/client-desktop/Cargo.toml
Normal file
12
crates/client-desktop/Cargo.toml
Normal file
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "client-desktop"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
domain.workspace = true
|
||||
protocol.workspace = true
|
||||
client-domain.workspace = true
|
||||
client-application.workspace = true
|
||||
tcp-client.workspace = true
|
||||
display-terminal.workspace = true
|
||||
85
crates/client-desktop/src/main.rs
Normal file
85
crates/client-desktop/src/main.rs
Normal file
@@ -0,0 +1,85 @@
|
||||
use std::thread;
|
||||
use std::sync::mpsc;
|
||||
use std::time::Duration;
|
||||
use client_domain::{BoundingBox, DisplayPort, NetworkPort};
|
||||
use client_application::ClientApp;
|
||||
use tcp_client::StdTcpClient;
|
||||
use display_terminal::TerminalDisplay;
|
||||
use protocol::decode_server_message;
|
||||
|
||||
fn main() {
|
||||
let screen = BoundingBox::screen(240, 320);
|
||||
let mut app = ClientApp::new(screen);
|
||||
let mut display = TerminalDisplay::new();
|
||||
|
||||
println!("=== K-Frame Desktop Client ===");
|
||||
println!("Screen: {}x{}", screen.width, screen.height);
|
||||
|
||||
let (tx, rx) = mpsc::channel();
|
||||
|
||||
thread::spawn(move || {
|
||||
let server_addr = "127.0.0.1:2699";
|
||||
let mut net = StdTcpClient::new();
|
||||
|
||||
loop {
|
||||
if !net.is_connected() {
|
||||
println!("[NET] Connecting to {server_addr}...");
|
||||
match net.connect(server_addr) {
|
||||
Ok(()) => println!("[NET] Connected!"),
|
||||
Err(e) => {
|
||||
println!("[NET] Connection failed: {e}, retrying in 2s...");
|
||||
thread::sleep(Duration::from_secs(2));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match net.receive() {
|
||||
Ok(Some(payload)) => {
|
||||
match decode_server_message(&payload) {
|
||||
Ok(msg) => { let _ = tx.send(msg); }
|
||||
Err(e) => println!("[NET] Decode error: {e}"),
|
||||
}
|
||||
}
|
||||
Ok(None) => {
|
||||
thread::sleep(Duration::from_millis(50));
|
||||
}
|
||||
Err(e) => {
|
||||
println!("[NET] Receive error: {e}, reconnecting...");
|
||||
let _ = net.disconnect();
|
||||
thread::sleep(Duration::from_secs(2));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
println!("[RENDER] Render loop started");
|
||||
|
||||
loop {
|
||||
match rx.recv_timeout(Duration::from_millis(100)) {
|
||||
Ok(msg) => {
|
||||
let repaints = app.handle_message(msg);
|
||||
if !repaints.is_empty() {
|
||||
println!("\n--- Repaint ({} widgets) ---", repaints.len());
|
||||
for cmd in &repaints {
|
||||
display.clear_region(cmd.bounds).unwrap();
|
||||
display.fill_background(cmd.bounds).unwrap();
|
||||
|
||||
for kv in &cmd.state.data {
|
||||
if let protocol::WireValue::String(s) = &kv.value {
|
||||
display.draw_text(
|
||||
&format!("{}: {s}", kv.key),
|
||||
cmd.bounds.x, cmd.bounds.y,
|
||||
cmd.bounds,
|
||||
).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
display.flush().unwrap();
|
||||
}
|
||||
}
|
||||
Err(mpsc::RecvTimeoutError::Timeout) => {}
|
||||
Err(mpsc::RecvTimeoutError::Disconnected) => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user