Compare commits
2 Commits
7403b5e53f
...
c303359b27
| Author | SHA1 | Date | |
|---|---|---|---|
| c303359b27 | |||
| 45ce330daf |
@@ -4,6 +4,15 @@ target = "xtensa-esp32-espidf"
|
|||||||
[target.xtensa-esp32-espidf]
|
[target.xtensa-esp32-espidf]
|
||||||
linker = "ldproxy"
|
linker = "ldproxy"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
# symbols are nice and they don't increase the size on Flash
|
||||||
|
debug = true
|
||||||
|
opt-level = "z"
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
opt-level = "s"
|
||||||
|
|
||||||
|
|
||||||
[unstable]
|
[unstable]
|
||||||
build-std = ["std", "panic_abort"]
|
build-std = ["std", "panic_abort"]
|
||||||
build-std-features = ["panic_immediate_abort"]
|
build-std-features = ["panic_immediate_abort"]
|
||||||
@@ -13,3 +22,7 @@ extra-link-arg = true # No longer necessary since 1.56, as it was stabilized:
|
|||||||
[env]
|
[env]
|
||||||
ESP_IDF_SYS_GLOB_BASE = { value = ".", relative = true }
|
ESP_IDF_SYS_GLOB_BASE = { value = ".", relative = true }
|
||||||
|
|
||||||
|
ESP_IDF_SYS_GLOB_0 = { value = "/sdkconfig.release" }
|
||||||
|
ESP_IDF_SYS_GLOB_1 = { value = "/sdkconfig.debug" }
|
||||||
|
ESP_IDF_SYS_GLOB_2 = { value = "/sdkconfig.defaults" }
|
||||||
|
|
||||||
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target
|
||||||
1398
Cargo.lock
generated
Normal file
1398
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
20
Cargo.toml
20
Cargo.toml
@@ -1,20 +1,24 @@
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "rusty-espresso"
|
name = "rusty-espresso"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
bind = []
|
bind = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
log = "0.4.14"
|
||||||
|
time = { version = "0.3.5", features = ["formatting"] }
|
||||||
anyhow = {version = "1", features = ["backtrace"]}
|
anyhow = {version = "1", features = ["backtrace"]}
|
||||||
esp-idf-sys = { version = "0.20" }
|
esp-idf-sys = { version = "0.31.5", features = ["binstart"] }
|
||||||
embedded-svc = "0.8.3"
|
embedded-svc = "0.14"
|
||||||
esp-idf-svc = { version = "0.20", features = ["binstart"] }
|
esp-idf-svc = { version = "0.41.2" }
|
||||||
esp-idf-hal = "0.20"
|
esp-idf-hal = "0.37.3"
|
||||||
|
maud = "0.22.3"
|
||||||
|
quick-protobuf = "0.8.0"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
embuild = "0.24"
|
embuild = "0.29.0"
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
|
|
||||||
|
|||||||
3
env.sh
3
env.sh
@@ -1,7 +1,8 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
export PATH=$HOME/xtensa-esp32-elf-clang/bin:$PATH
|
# export PATH=/home/jakob/esp/llvm-project/build/bin/clang:$PATH
|
||||||
. $HOME/esp/esp-idf/export.sh
|
. $HOME/esp/esp-idf/export.sh
|
||||||
|
export PATH=/home/jakob/esp/xtensa-esp32-elf-clang/bin:$PATH
|
||||||
rustup default esp
|
rustup default esp
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
5
nvs.csv
Normal file
5
nvs.csv
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
key,type,encoding,value
|
||||||
|
data,namespace,,data
|
||||||
|
ssid,data,hex2bin,776c616e41
|
||||||
|
password,data,hex2bin,537570657253696c656e6365
|
||||||
|
authmethod,data,hex2bin,57504132506572736f6e616c
|
||||||
|
5
partition.csv
Normal file
5
partition.csv
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# ESP-IDF Partition Table
|
||||||
|
# Name, Type, SubType, Offset, Size, Flags
|
||||||
|
nvs, data, nvs, 0x9000, 0x6000,
|
||||||
|
phy_init, data, phy, 0xf000, 0x1000,
|
||||||
|
factory, app, factory, 0x10000, 1M,
|
||||||
|
4
rerun.sh
Normal file
4
rerun.sh
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
cargo clean --release -p esp-idf-svc
|
||||||
|
cargo build --release --example nvs-example
|
||||||
|
espflash --partition-table partition.csv /dev/ttyUSB0 target/xtensa-esp32-espidf/release/examples/nvs-example
|
||||||
|
parttool.py --partition-table-file partition.csv --port /dev/ttyUSB0 write_partition --partition-name=nvs --input "nvs.bin"
|
||||||
3
run.sh
Normal file
3
run.sh
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
espflash --partition-table partition.csv /dev/ttyUSB0 target/xtensa-esp32-espidf/release/rusty-espresso
|
||||||
|
parttool.py --partition-table-file partition.csv --port /dev/ttyUSB0 write_partition --partition-name=nvs --input "nvs.bin"
|
||||||
|
espmonitor --speed 115200 /dev/ttyUSB0 --bin target/xtensa-esp32-espidf/release/rusty-espresso
|
||||||
@@ -4,3 +4,7 @@ CONFIG_LWIP_IPV4_NAPT=y
|
|||||||
#CONFIG_ESP_SYSTEM_USE_EH_FRAME=y
|
#CONFIG_ESP_SYSTEM_USE_EH_FRAME=y
|
||||||
#CONFIG_COMPILER_CXX_EXCEPTIONS=y
|
#CONFIG_COMPILER_CXX_EXCEPTIONS=y
|
||||||
|
|
||||||
|
# Workaround for https://github.com/espressif/esp-idf/issues/7631
|
||||||
|
CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=n
|
||||||
|
CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL=n
|
||||||
|
CONFIG_HTTPD_MAX_REQ_HDR_LEN=2048
|
||||||
|
|||||||
5
src/api_pid_get.json
Normal file
5
src/api_pid_get.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"target" : 96.2,
|
||||||
|
"current": 92.4,
|
||||||
|
"enabled": false
|
||||||
|
}
|
||||||
5
src/api_pid_post.json
Normal file
5
src/api_pid_post.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"target": 96.2,
|
||||||
|
"enabled": false
|
||||||
|
}
|
||||||
|
|
||||||
73
src/backup_manager.rs
Normal file
73
src/backup_manager.rs
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
use std::cmp;
|
||||||
|
use std::sync::mpsc::{channel, Receiver, Sender};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::thread;
|
||||||
|
use std::vec::Vec;
|
||||||
|
|
||||||
|
use esp_idf_svc::nvs::EspDefaultNvs;
|
||||||
|
use esp_idf_svc::nvs_storage::EspNvsStorage;
|
||||||
|
use embedded_svc::storage::Storage;
|
||||||
|
|
||||||
|
pub type BackupManagerTx = Sender<BackupManagerMessage>;
|
||||||
|
|
||||||
|
pub struct BackupManager {
|
||||||
|
pub tx: BackupManagerTx,
|
||||||
|
join_handle: thread::JoinHandle<()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct BackupManagerInner {
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum BackupManagerMessage {
|
||||||
|
Save(String, Vec<u8>, Sender<bool>),
|
||||||
|
Load(String, Sender<Option<Vec<u8>>>),
|
||||||
|
GetNvs(Sender<Arc<EspDefaultNvs>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BackupManager {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let (tx, rx) = channel();
|
||||||
|
let mut inner = BackupManagerInner {
|
||||||
|
};
|
||||||
|
let jh = thread::spawn(move || inner.serve(rx));
|
||||||
|
|
||||||
|
BackupManager {
|
||||||
|
tx: tx,
|
||||||
|
join_handle: jh,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_sender(&self) -> BackupManagerTx {
|
||||||
|
self.tx.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BackupManagerInner {
|
||||||
|
fn serve(&mut self, rx: Receiver<BackupManagerMessage>) {
|
||||||
|
let mut nvs = Arc::new(EspDefaultNvs::new().unwrap());
|
||||||
|
let mut nvs_store = EspNvsStorage::new_default(Arc::clone(&nvs), "data", true).unwrap();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match rx.recv() {
|
||||||
|
Err(x) => {
|
||||||
|
// some error reading from the channel, bail
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Ok(msg) => match msg {
|
||||||
|
BackupManagerMessage::Save(key, value, resp) => {
|
||||||
|
let result = nvs_store.put_raw(key, value).unwrap();
|
||||||
|
resp.send(result).unwrap_or(());
|
||||||
|
},
|
||||||
|
BackupManagerMessage::Load(key, resp) => {
|
||||||
|
let value = nvs_store.get_raw(key).unwrap();
|
||||||
|
resp.send(value).unwrap_or(());
|
||||||
|
},
|
||||||
|
BackupManagerMessage::GetNvs(resp) => {
|
||||||
|
resp.send(Arc::clone(&nvs)).unwrap_or(())
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} // match rx.recv()
|
||||||
|
} // loop
|
||||||
|
() // exit
|
||||||
|
}
|
||||||
|
}
|
||||||
24
src/index.html
Normal file
24
src/index.html
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: serif;
|
||||||
|
text-align: center;
|
||||||
|
color: #321C0B;
|
||||||
|
background-color: #D5E4F6;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
color: #B15C1B;
|
||||||
|
}
|
||||||
|
h4 {
|
||||||
|
color: #2969B2;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<link rel="icon" type="image/png" href="favicon.png"></link>
|
||||||
|
<title>Rusty Espresso Controller</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Rusty Espresso Controller</h1>
|
||||||
|
<h4>© 2021 Jakob Dalsgaard</h4>
|
||||||
|
<p>Check the official homepage for more information: <a href="https://rusty-espresso.dalsgaard.net/">https://rusty-espresso.dalsgaard.net/</a>.</p>
|
||||||
|
</body>
|
||||||
303
src/main.rs
303
src/main.rs
@@ -1,4 +1,303 @@
|
|||||||
|
use embedded_svc::httpd::registry::*;
|
||||||
|
use embedded_svc::httpd::*;
|
||||||
|
use embedded_svc::ipv4::ClientConfiguration as Ipv4ClientConfiguration;
|
||||||
|
use embedded_svc::ipv4::DHCPClientSettings;
|
||||||
|
use embedded_svc::storage::Storage;
|
||||||
|
use embedded_svc::wifi::{AuthMethod, ClientConfiguration, Configuration, Wifi};
|
||||||
|
use esp_idf_svc::httpd::Server;
|
||||||
|
use esp_idf_svc::netif::EspNetifStack;
|
||||||
|
use esp_idf_svc::nvs::EspDefaultNvs;
|
||||||
|
use esp_idf_svc::nvs_storage::EspNvsStorage;
|
||||||
|
use esp_idf_svc::sntp::*;
|
||||||
|
use log::*;
|
||||||
|
|
||||||
fn main() {
|
use std::env;
|
||||||
println!("Hello, world!");
|
use std::fmt;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
use std::thread;
|
||||||
|
use std::time::SystemTime;
|
||||||
|
use time::{
|
||||||
|
format_description::well_known::Rfc3339,
|
||||||
|
OffsetDateTime };
|
||||||
|
// use embedded_svc::anyerror::*;
|
||||||
|
// use esp_idf_hal::prelude::*;
|
||||||
|
use esp_idf_svc::httpd as idf_httpd;
|
||||||
|
use esp_idf_svc::sysloop::*;
|
||||||
|
use esp_idf_svc::wifi::*;
|
||||||
|
use esp_idf_sys::EspError;
|
||||||
|
use std::sync::mpsc::{channel, Receiver, Sender};
|
||||||
|
use std::time::Duration;
|
||||||
|
use maud::{Markup, html, DOCTYPE};
|
||||||
|
|
||||||
|
mod backup_manager;
|
||||||
|
mod pid_manager;
|
||||||
|
mod s11n;
|
||||||
|
|
||||||
|
use crate::backup_manager::BackupManagerTx;
|
||||||
|
|
||||||
|
// use std::{thread, time::Duration};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct WifiData {
|
||||||
|
ssid: String,
|
||||||
|
password: String,
|
||||||
|
auth: AuthMethod,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for WifiData {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"WifiData: ({}, [password not displayed], {})",
|
||||||
|
self.ssid,
|
||||||
|
WifiData::authmethod_to_str(self.auth)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WifiData {
|
||||||
|
const auth_methods: [(&'static str, AuthMethod); 9] = [
|
||||||
|
("None", AuthMethod::None),
|
||||||
|
("WEP", AuthMethod::WEP),
|
||||||
|
("WPA", AuthMethod::WPA),
|
||||||
|
("WPA2Personal", AuthMethod::WPA2Personal),
|
||||||
|
("WPAWPA2Personal", AuthMethod::WPAWPA2Personal),
|
||||||
|
("WPA2Enterprise", AuthMethod::WPA2Enterprise),
|
||||||
|
("WPA3Personal", AuthMethod::WPA3Personal),
|
||||||
|
("WPA2WPA3Personal", AuthMethod::WPA2WPA3Personal),
|
||||||
|
("WAPIPersonal", AuthMethod::WAPIPersonal),
|
||||||
|
];
|
||||||
|
|
||||||
|
fn str_to_authmethod(s: &str) -> AuthMethod {
|
||||||
|
WifiData::auth_methods
|
||||||
|
.iter()
|
||||||
|
.find(|(n, _)| s.eq(*n))
|
||||||
|
.unwrap_or(&WifiData::auth_methods[0])
|
||||||
|
.1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn authmethod_to_str(a: AuthMethod) -> &'static str {
|
||||||
|
WifiData::auth_methods
|
||||||
|
.iter()
|
||||||
|
.find(|(_, k)| a == *k)
|
||||||
|
.unwrap_or(&WifiData::auth_methods[0])
|
||||||
|
.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_nvs(nvs: BackupManagerTx) -> anyhow::Result<Self> {
|
||||||
|
let ssid_b = get_nvs_key(&nvs, "ssid".to_string()).unwrap_or([].to_vec());
|
||||||
|
let password_b = get_nvs_key(&nvs, "password".to_string()).unwrap_or([].to_vec());
|
||||||
|
let auth_b = get_nvs_key(&nvs, "authmethod".to_string()).unwrap_or([].to_vec());
|
||||||
|
let ssid = String::from_utf8(ssid_b).unwrap();
|
||||||
|
let password = String::from_utf8(password_b).unwrap();
|
||||||
|
let auth = WifiData::str_to_authmethod(std::str::from_utf8(&auth_b).unwrap());
|
||||||
|
|
||||||
|
Ok(WifiData { ssid: ssid, password: password, auth: auth })
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
pub fn from_nvs(storage: &mut EspNvsStorage) -> anyhow::Result<Self> {
|
||||||
|
Ok(WifiData {
|
||||||
|
ssid: String::from_utf8(storage.get_raw("ssid")?.unwrap_or([].to_vec()))?,
|
||||||
|
password: String::from_utf8(storage.get_raw("password")?.unwrap_or([].to_vec()))?,
|
||||||
|
auth: WifiData::str_to_authmethod(
|
||||||
|
std::str::from_utf8(&storage.get_raw("authmethod")?.unwrap_or([].to_vec()))
|
||||||
|
.unwrap_or(""),
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
pub fn to_nvs(self, storage: &mut EspNvsStorage) -> anyhow::Result<()> {
|
||||||
|
storage.put_raw("ssid", self.ssid.as_bytes())?;
|
||||||
|
storage.put_raw("password", self.password.as_bytes())?;
|
||||||
|
storage.put_raw(
|
||||||
|
"authmethod",
|
||||||
|
WifiData::authmethod_to_str(self.auth).as_bytes(),
|
||||||
|
)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_nvs_key (nvs: &BackupManagerTx, key: String) -> Option<Vec<u8>> {
|
||||||
|
let (tx, rx) = channel();
|
||||||
|
nvs.send(backup_manager::BackupManagerMessage::Load(key, tx));
|
||||||
|
rx.recv_timeout(Duration::from_millis(100)).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_nvs (nvs: &BackupManagerTx) -> Arc<EspDefaultNvs> {
|
||||||
|
let (tx, rx) = channel();
|
||||||
|
nvs.send(backup_manager::BackupManagerMessage::GetNvs(tx));
|
||||||
|
rx.recv().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn to_rfc3339<T>(dt: T) -> String where T: Into<OffsetDateTime> {
|
||||||
|
dt.into().format(&Rfc3339).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct HttpdState {
|
||||||
|
pid_data_library: pid_manager::PidManager,
|
||||||
|
nvs_store: backup_manager::BackupManagerTx,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> anyhow::Result<()> {
|
||||||
|
esp_idf_sys::link_patches();
|
||||||
|
|
||||||
|
env::set_var("RUST_BACKTRACE", "1");
|
||||||
|
|
||||||
|
esp_idf_svc::log::EspLogger::initialize_default();
|
||||||
|
|
||||||
|
//let mut nvs = Arc::new(EspDefaultNvs::new()?);
|
||||||
|
//let mut nvs_store = EspNvsStorage::new_default(nvs.clone(), "data", true)?;
|
||||||
|
|
||||||
|
info!("EspNvsStorage done");
|
||||||
|
thread::sleep(Duration::from_secs(10));
|
||||||
|
let mut nvs_backup_manager = backup_manager::BackupManager::new();
|
||||||
|
let wd = WifiData::from_nvs(nvs_backup_manager.get_sender())?;
|
||||||
|
info!("wifidata is: {}", &wd);
|
||||||
|
thread::sleep(Duration::from_secs(1));
|
||||||
|
|
||||||
|
|
||||||
|
let mut w = wifi(wd, get_nvs(&nvs_backup_manager.get_sender()))?;
|
||||||
|
|
||||||
|
info!("We now have wifi -- trying to establish NTP");
|
||||||
|
|
||||||
|
let sntp_conf = SntpConf {
|
||||||
|
servers: [ "0.dk.pool.ntp.org".to_string() ],
|
||||||
|
operating_mode: OperatingMode::Poll,
|
||||||
|
sync_mode: SyncMode::Immediate
|
||||||
|
};
|
||||||
|
let ntp = EspSntp::new(&sntp_conf)?;
|
||||||
|
|
||||||
|
thread::sleep(Duration::from_secs(1));
|
||||||
|
|
||||||
|
info!("Current time is: {:?}", to_rfc3339(SystemTime::now()));
|
||||||
|
|
||||||
|
let mut httpd_state = HttpdState {
|
||||||
|
pid_data_library: pid_manager::PidManager::new(&nvs_backup_manager.get_sender()),
|
||||||
|
nvs_store: nvs_backup_manager.get_sender(),
|
||||||
|
};
|
||||||
|
let mut h = httpd(httpd_state)?;
|
||||||
|
|
||||||
|
thread::sleep(Duration::from_secs(300));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MyServer {
|
||||||
|
state: i32,
|
||||||
|
server: Option<Server>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn httpd(mut state: HttpdState) -> anyhow::Result<idf_httpd::Server> {
|
||||||
|
|
||||||
|
fn header (page_title: &str) -> Markup {
|
||||||
|
html! {
|
||||||
|
head {
|
||||||
|
title { (page_title) }
|
||||||
|
link rel="stylesheet" href="/static/styles.css";
|
||||||
|
link rel="icon" type="image/png" href="/favicon.png";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn web_frontpage_get(req: Request) -> anyhow::Result<Response> {
|
||||||
|
Ok(Response::ok().body(include_str!("index.html").into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
let api_pid_post = move |_| {
|
||||||
|
Ok(Response::ok()
|
||||||
|
.content_type("application/json")
|
||||||
|
.body(include_str!("api_pid_post.json").into()))
|
||||||
|
};
|
||||||
|
|
||||||
|
fn api_pid_get(req: Request) -> anyhow::Result<Response> {
|
||||||
|
Ok(Response::ok()
|
||||||
|
.content_type("appliation/json")
|
||||||
|
.body(include_str!("api_pid_get.json").into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn web_create_pid_form(req: Request) -> anyhow::Result<Response> {
|
||||||
|
let heading = &"Create a PID Data";
|
||||||
|
let markup = html! {
|
||||||
|
(DOCTYPE)
|
||||||
|
html {
|
||||||
|
(header(heading))
|
||||||
|
h1 { (heading) }
|
||||||
|
body {
|
||||||
|
form action="create-pid" method="post" {
|
||||||
|
label for="name" { "name" }
|
||||||
|
input type="text" name="name";
|
||||||
|
input type="submit" value="create";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(Response::ok().content_type("text/html").body(markup.into_string().into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn web_pid_get(req: Request, mut pid_manager_tx: pid_manager::PidManagerTx) -> anyhow::Result<Response> {
|
||||||
|
let (tx, rx) = channel();
|
||||||
|
pid_manager_tx.send(pid_manager::PidManagerMessage::GetLibraryItemsList(tx));
|
||||||
|
let v = rx.recv_timeout(Duration::from_millis(100)).unwrap();
|
||||||
|
|
||||||
|
let heading = &"Test Maud page";
|
||||||
|
|
||||||
|
// try out the maud templating language
|
||||||
|
let markup = html! {
|
||||||
|
html {
|
||||||
|
(header(heading))
|
||||||
|
h1 { (heading) }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(Response::ok().content_type("text/html").body(markup.into_string().into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn static_png(req: Request, b: &[u8]) -> anyhow::Result<Response> {
|
||||||
|
Ok(Response::ok().content_type("image/png").header("Cache-Control", "public, s-maxage=28800, max-age=28800").body(b.to_vec().into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn static_css_gz(req: Request, b: &[u8]) -> anyhow::Result<Response> {
|
||||||
|
Ok(Response::ok().content_type("text/css").header("Cache-Control", "public, s-maxage=28800, max-age=28800").header("Content-Encoding", "gzip").body(b.to_vec().into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
let server = idf_httpd::ServerRegistry::new()
|
||||||
|
.at("/")
|
||||||
|
.get(web_frontpage_get)?
|
||||||
|
.at("/favicon.png")
|
||||||
|
.get(move |r| static_png(r, include_bytes!("static/favicon.png")))?
|
||||||
|
.at("/static/styles.css")
|
||||||
|
.get(move |r| static_css_gz(r, include_bytes!("static/styles.css.gz")))?
|
||||||
|
.at("/create-pid-form")
|
||||||
|
.get(web_create_pid_form)?
|
||||||
|
.at("/pid")
|
||||||
|
.get(move |r| web_pid_get(r, state.pid_data_library.tx.clone()))?
|
||||||
|
.at("/api/v1/pid")
|
||||||
|
.get(api_pid_get)?
|
||||||
|
.at("/api/v1/pid")
|
||||||
|
.post(api_pid_post)?;
|
||||||
|
|
||||||
|
server.start(&idf_httpd::Configuration::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wifi(wifidata: WifiData, nvs: Arc<EspDefaultNvs>) -> anyhow::Result<EspWifi> {
|
||||||
|
let mut wifi = EspWifi::new(
|
||||||
|
Arc::new(EspNetifStack::new()?),
|
||||||
|
Arc::new(EspSysLoopStack::new()?),
|
||||||
|
nvs,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
wifi.set_configuration(&Configuration::Client(ClientConfiguration {
|
||||||
|
ssid: wifidata.ssid.into(),
|
||||||
|
password: wifidata.password.into(),
|
||||||
|
bssid: None,
|
||||||
|
channel: None, // unknown channel, let's see how that works :-)
|
||||||
|
ip_conf: Some(Ipv4ClientConfiguration::DHCP(DHCPClientSettings { hostname: Some(String::from("rusty-espress-XX")) } )),
|
||||||
|
auth_method: wifidata.auth,
|
||||||
|
}))?;
|
||||||
|
|
||||||
|
Ok(wifi)
|
||||||
}
|
}
|
||||||
|
|||||||
1
src/mod.rs
Normal file
1
src/mod.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
pub mod s11n;
|
||||||
209
src/pid_manager.rs
Normal file
209
src/pid_manager.rs
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
use std::cmp;
|
||||||
|
use std::sync::mpsc::{channel, Receiver, Sender};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::thread;
|
||||||
|
use std::vec::Vec;
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use quick_protobuf::{Writer, MessageWrite};
|
||||||
|
use std::borrow::Cow;
|
||||||
|
use std::convert::Into;
|
||||||
|
|
||||||
|
use crate::s11n::converter;
|
||||||
|
use crate::s11n::pb::pid1;
|
||||||
|
use crate::backup_manager::BackupManagerTx;
|
||||||
|
|
||||||
|
|
||||||
|
pub type PidManagerTx = Sender<PidManagerMessage>;
|
||||||
|
|
||||||
|
pub struct PidManager {
|
||||||
|
pub tx: PidManagerTx,
|
||||||
|
join_handle: thread::JoinHandle<()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PidManagerInner {
|
||||||
|
library: PidDataLibrary,
|
||||||
|
current_pid_data: Option<PidData>,
|
||||||
|
nvs_store: BackupManagerTx,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct PidData {
|
||||||
|
p: f64,
|
||||||
|
i: f64,
|
||||||
|
d: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct PidDataLibraryItem {
|
||||||
|
item_name: String,
|
||||||
|
data: PidData,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct PidDataLibrary {
|
||||||
|
items: HashMap<u16, PidDataLibraryItem>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum PidManagerMessage {
|
||||||
|
CreatePidData(String, Sender<Option<u16>>),
|
||||||
|
DeletePidData(u16),
|
||||||
|
UpdatePidDataName(u16, String),
|
||||||
|
UpdatePidData(u16, PidData),
|
||||||
|
SetActivePidData(u16),
|
||||||
|
GetLibraryItemsList(Sender<Vec<(u16, String)>>),
|
||||||
|
GetLibraryItem(u16, Sender<Option<PidDataLibraryItem>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PidManager {
|
||||||
|
pub fn new(nvs: &BackupManagerTx) -> Self {
|
||||||
|
let (tx, rx) = channel();
|
||||||
|
let mut inner = PidManagerInner {
|
||||||
|
library: PidDataLibrary {
|
||||||
|
items: HashMap::new(),
|
||||||
|
},
|
||||||
|
current_pid_data: None,
|
||||||
|
nvs_store: nvs.clone(),
|
||||||
|
};
|
||||||
|
let jh = thread::spawn(move || inner.serve(rx));
|
||||||
|
|
||||||
|
PidManager {
|
||||||
|
tx: tx,
|
||||||
|
join_handle: jh,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_sender(&self) -> PidManagerTx {
|
||||||
|
self.tx.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PidManagerInner {
|
||||||
|
fn serve(&mut self, rx: Receiver<PidManagerMessage>) {
|
||||||
|
loop {
|
||||||
|
match rx.recv() {
|
||||||
|
Err(x) => {
|
||||||
|
// some error reading from the channel, bail
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Ok(msg) => match msg {
|
||||||
|
PidManagerMessage::CreatePidData(name, resp) => {
|
||||||
|
// we only allow 16 sets of piddata
|
||||||
|
if self.library.items.len() >= 16 {
|
||||||
|
resp.send(None).unwrap_or(());
|
||||||
|
} else {
|
||||||
|
// get current max item_id and increment by 1
|
||||||
|
let new_id = self.library.items.keys().max().unwrap_or(&0) + 1;
|
||||||
|
let item = PidDataLibraryItem {
|
||||||
|
item_name: name.clone(),
|
||||||
|
data: PidData {
|
||||||
|
p: 0.0f64,
|
||||||
|
i: 0.0f64,
|
||||||
|
d: 0.0f64,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
self.library.items.insert(new_id.clone(), item);
|
||||||
|
resp.send(Some(new_id.clone())).unwrap_or(());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
PidManagerMessage::DeletePidData(id) => {
|
||||||
|
self.library.items.remove(&id);
|
||||||
|
},
|
||||||
|
PidManagerMessage::UpdatePidDataName(id, name) => {
|
||||||
|
if let Some(item) = self.library.items.get_mut(&id) {
|
||||||
|
item.item_name = name;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
PidManagerMessage::UpdatePidData(id, data) => {
|
||||||
|
if let Some(library_item) = self.library.items.get_mut(&id) {
|
||||||
|
library_item.data = data;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
PidManagerMessage::SetActivePidData(id) => {
|
||||||
|
if let Some(pid_data) = self.library.items.get(&id) {
|
||||||
|
self.current_pid_data = Some(pid_data.data.clone());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
PidManagerMessage::GetLibraryItemsList(sender) => {
|
||||||
|
let res = self
|
||||||
|
.library
|
||||||
|
.items
|
||||||
|
.iter()
|
||||||
|
.map(|(k, v)| (*k, v.item_name.clone()))
|
||||||
|
.collect();
|
||||||
|
sender.send(res).unwrap_or(());
|
||||||
|
},
|
||||||
|
PidManagerMessage::GetLibraryItem(id, sender) => {
|
||||||
|
if let Some(item) = self.library.items.get(&id) {
|
||||||
|
sender.send(Some(item.clone())).unwrap_or(());
|
||||||
|
} else {
|
||||||
|
sender.send(None).unwrap_or(());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}, // end of enum match
|
||||||
|
} // end of Err/Some match
|
||||||
|
} // end of loop
|
||||||
|
() // exit
|
||||||
|
}
|
||||||
|
|
||||||
|
fn backup(&mut self) {
|
||||||
|
let v: pid1::PidDataLibrary = self.library.to_owned().into();
|
||||||
|
let mut vb = Vec::new();
|
||||||
|
vb.extend([0x00, 0x01]);
|
||||||
|
vb.extend(converter::serialise(v));
|
||||||
|
// sender.send(vb);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn restore(&mut self, bytesv: &Vec<u8>) {
|
||||||
|
if let converter::Input::Pid1(pb) = converter::parse(&bytesv) {
|
||||||
|
self.library = PidDataLibrary::from(pb);
|
||||||
|
} else {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<pid1::PidData<'a>> for PidDataLibraryItem {
|
||||||
|
fn from(v: pid1::PidData<'a>) -> Self {
|
||||||
|
PidDataLibraryItem {
|
||||||
|
item_name: v.name.to_string(),
|
||||||
|
data: PidData {
|
||||||
|
p: v.p,
|
||||||
|
i: v.i,
|
||||||
|
d: v.d,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<pid1::PidDataLibrary<'a>> for PidDataLibrary {
|
||||||
|
fn from(v: pid1::PidDataLibrary<'a>) -> Self {
|
||||||
|
PidDataLibrary {
|
||||||
|
items: v.items.iter().fold(HashMap::new(), |mut acc, (k, v)| { acc.insert(*k as u16, PidDataLibraryItem::from(v.to_owned())); acc }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Into<pid1::PidData<'a>> for PidDataLibraryItem {
|
||||||
|
fn into(self) -> pid1::PidData<'a> {
|
||||||
|
pid1::PidData {
|
||||||
|
name: Cow::Owned(self.item_name),
|
||||||
|
p: self.data.p,
|
||||||
|
i: self.data.i,
|
||||||
|
d: self.data.d,
|
||||||
|
created: 0,
|
||||||
|
modified: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Into<pid1::PidDataLibrary<'a>> for PidDataLibrary {
|
||||||
|
fn into(self) -> pid1::PidDataLibrary<'a> {
|
||||||
|
pid1::PidDataLibrary {
|
||||||
|
items: self.items.iter().fold(HashMap::new(), |mut acc, (k, v)| { acc.insert(*k as u32, v.to_owned().into()); acc }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
43
src/s11n/converter.rs
Normal file
43
src/s11n/converter.rs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
use crate::s11n::pb;
|
||||||
|
use quick_protobuf::{deserialize_from_slice, serialize_into_vec, Reader, Writer, MessageWrite};
|
||||||
|
|
||||||
|
pub enum Input<'a> {
|
||||||
|
Wifi1(pb::wifi1::WifiData<'a>),
|
||||||
|
Pid1(pb::pid1::PidDataLibrary<'a>),
|
||||||
|
Empty,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse<'a>(b: &'a Vec<u8>) -> Input<'a> {
|
||||||
|
if b.len() < 2 {
|
||||||
|
return Input::Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
let header: u16 = (b[0] as u16) << 8 | (b[0] as u16);
|
||||||
|
|
||||||
|
match header {
|
||||||
|
1 => { // this is a PidLibrary version 1
|
||||||
|
if let Ok(v) = deserialize_from_slice(&b[2..]) {
|
||||||
|
Input::Pid1(v)
|
||||||
|
} else {
|
||||||
|
Input::Empty
|
||||||
|
}
|
||||||
|
},
|
||||||
|
2 => { // this is a WifiLibrary version 1
|
||||||
|
if let Ok(v) = deserialize_from_slice(&b[2..]) {
|
||||||
|
Input::Wifi1(v)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Input::Empty
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => { // everything else...
|
||||||
|
Input::Empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serialise<T: MessageWrite> (p: T) -> Vec<u8> {
|
||||||
|
serialize_into_vec(&p).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
3
src/s11n/mod.rs
Normal file
3
src/s11n/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
pub mod converter;
|
||||||
|
// Automatically generated mod.rs
|
||||||
|
pub mod pb;
|
||||||
3
src/s11n/pb/mod.rs
Normal file
3
src/s11n/pb/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
// Automatically generated mod.rs
|
||||||
|
pub mod pid1;
|
||||||
|
pub mod wifi1;
|
||||||
103
src/s11n/pb/pid1.rs
Normal file
103
src/s11n/pb/pid1.rs
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
// Automatically generated rust module for 'pid1.proto' file
|
||||||
|
|
||||||
|
#![allow(non_snake_case)]
|
||||||
|
#![allow(non_upper_case_globals)]
|
||||||
|
#![allow(non_camel_case_types)]
|
||||||
|
#![allow(unused_imports)]
|
||||||
|
#![allow(unknown_lints)]
|
||||||
|
#![allow(clippy::all)]
|
||||||
|
#![cfg_attr(rustfmt, rustfmt_skip)]
|
||||||
|
|
||||||
|
|
||||||
|
use std::borrow::Cow;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
type KVMap<K, V> = HashMap<K, V>;
|
||||||
|
use quick_protobuf::{MessageRead, MessageWrite, BytesReader, Writer, WriterBackend, Result};
|
||||||
|
use quick_protobuf::sizeofs::*;
|
||||||
|
use super::super::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Default, PartialEq, Clone)]
|
||||||
|
pub struct PidData<'a> {
|
||||||
|
pub name: Cow<'a, str>,
|
||||||
|
pub created: u64,
|
||||||
|
pub modified: u64,
|
||||||
|
pub p: f64,
|
||||||
|
pub i: f64,
|
||||||
|
pub d: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> MessageRead<'a> for PidData<'a> {
|
||||||
|
fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result<Self> {
|
||||||
|
let mut msg = Self::default();
|
||||||
|
while !r.is_eof() {
|
||||||
|
match r.next_tag(bytes) {
|
||||||
|
Ok(34) => msg.name = r.read_string(bytes).map(Cow::Borrowed)?,
|
||||||
|
Ok(40) => msg.created = r.read_uint64(bytes)?,
|
||||||
|
Ok(48) => msg.modified = r.read_uint64(bytes)?,
|
||||||
|
Ok(81) => msg.p = r.read_double(bytes)?,
|
||||||
|
Ok(89) => msg.i = r.read_double(bytes)?,
|
||||||
|
Ok(97) => msg.d = r.read_double(bytes)?,
|
||||||
|
Ok(t) => { r.read_unknown(bytes, t)?; }
|
||||||
|
Err(e) => return Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> MessageWrite for PidData<'a> {
|
||||||
|
fn get_size(&self) -> usize {
|
||||||
|
0
|
||||||
|
+ if self.name == "" { 0 } else { 1 + sizeof_len((&self.name).len()) }
|
||||||
|
+ if self.created == 0u64 { 0 } else { 1 + sizeof_varint(*(&self.created) as u64) }
|
||||||
|
+ if self.modified == 0u64 { 0 } else { 1 + sizeof_varint(*(&self.modified) as u64) }
|
||||||
|
+ if self.p == 0f64 { 0 } else { 1 + 8 }
|
||||||
|
+ if self.i == 0f64 { 0 } else { 1 + 8 }
|
||||||
|
+ if self.d == 0f64 { 0 } else { 1 + 8 }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_message<W: WriterBackend>(&self, w: &mut Writer<W>) -> Result<()> {
|
||||||
|
if self.name != "" { w.write_with_tag(34, |w| w.write_string(&**&self.name))?; }
|
||||||
|
if self.created != 0u64 { w.write_with_tag(40, |w| w.write_uint64(*&self.created))?; }
|
||||||
|
if self.modified != 0u64 { w.write_with_tag(48, |w| w.write_uint64(*&self.modified))?; }
|
||||||
|
if self.p != 0f64 { w.write_with_tag(81, |w| w.write_double(*&self.p))?; }
|
||||||
|
if self.i != 0f64 { w.write_with_tag(89, |w| w.write_double(*&self.i))?; }
|
||||||
|
if self.d != 0f64 { w.write_with_tag(97, |w| w.write_double(*&self.d))?; }
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, PartialEq, Clone)]
|
||||||
|
pub struct PidDataLibrary<'a> {
|
||||||
|
pub items: KVMap<u32, pb::pid1::PidData<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> MessageRead<'a> for PidDataLibrary<'a> {
|
||||||
|
fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result<Self> {
|
||||||
|
let mut msg = Self::default();
|
||||||
|
while !r.is_eof() {
|
||||||
|
match r.next_tag(bytes) {
|
||||||
|
Ok(34) => {
|
||||||
|
let (key, value) = r.read_map(bytes, |r, bytes| Ok(r.read_uint32(bytes)?), |r, bytes| Ok(r.read_message::<pb::pid1::PidData>(bytes)?))?;
|
||||||
|
msg.items.insert(key, value);
|
||||||
|
}
|
||||||
|
Ok(t) => { r.read_unknown(bytes, t)?; }
|
||||||
|
Err(e) => return Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> MessageWrite for PidDataLibrary<'a> {
|
||||||
|
fn get_size(&self) -> usize {
|
||||||
|
0
|
||||||
|
+ self.items.iter().map(|(k, v)| 1 + sizeof_len(2 + sizeof_varint(*(k) as u64) + sizeof_len((v).get_size()))).sum::<usize>()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_message<W: WriterBackend>(&self, w: &mut Writer<W>) -> Result<()> {
|
||||||
|
for (k, v) in self.items.iter() { w.write_with_tag(34, |w| w.write_map(2 + sizeof_varint(*(k) as u64) + sizeof_len((v).get_size()), 8, |w| w.write_uint32(*k), 18, |w| w.write_message(v)))?; }
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
148
src/s11n/pb/wifi1.rs
Normal file
148
src/s11n/pb/wifi1.rs
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
// Automatically generated rust module for 'wifi1.proto' file
|
||||||
|
|
||||||
|
#![allow(non_snake_case)]
|
||||||
|
#![allow(non_upper_case_globals)]
|
||||||
|
#![allow(non_camel_case_types)]
|
||||||
|
#![allow(unused_imports)]
|
||||||
|
#![allow(unknown_lints)]
|
||||||
|
#![allow(clippy::all)]
|
||||||
|
#![cfg_attr(rustfmt, rustfmt_skip)]
|
||||||
|
|
||||||
|
|
||||||
|
use std::borrow::Cow;
|
||||||
|
use quick_protobuf::{MessageRead, MessageWrite, BytesReader, Writer, WriterBackend, Result};
|
||||||
|
use quick_protobuf::sizeofs::*;
|
||||||
|
use super::super::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Default, PartialEq, Clone)]
|
||||||
|
pub struct WifiData<'a> {
|
||||||
|
pub ssid: Cow<'a, str>,
|
||||||
|
pub password: Cow<'a, str>,
|
||||||
|
pub auth: pb::wifi1::mod_WifiData::AuthMethod,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> MessageRead<'a> for WifiData<'a> {
|
||||||
|
fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result<Self> {
|
||||||
|
let mut msg = Self::default();
|
||||||
|
while !r.is_eof() {
|
||||||
|
match r.next_tag(bytes) {
|
||||||
|
Ok(34) => msg.ssid = r.read_string(bytes).map(Cow::Borrowed)?,
|
||||||
|
Ok(42) => msg.password = r.read_string(bytes).map(Cow::Borrowed)?,
|
||||||
|
Ok(48) => msg.auth = r.read_enum(bytes)?,
|
||||||
|
Ok(t) => { r.read_unknown(bytes, t)?; }
|
||||||
|
Err(e) => return Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> MessageWrite for WifiData<'a> {
|
||||||
|
fn get_size(&self) -> usize {
|
||||||
|
0
|
||||||
|
+ if self.ssid == "" { 0 } else { 1 + sizeof_len((&self.ssid).len()) }
|
||||||
|
+ if self.password == "" { 0 } else { 1 + sizeof_len((&self.password).len()) }
|
||||||
|
+ if self.auth == pb::wifi1::mod_WifiData::AuthMethod::PLAIN { 0 } else { 1 + sizeof_varint(*(&self.auth) as u64) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_message<W: WriterBackend>(&self, w: &mut Writer<W>) -> Result<()> {
|
||||||
|
if self.ssid != "" { w.write_with_tag(34, |w| w.write_string(&**&self.ssid))?; }
|
||||||
|
if self.password != "" { w.write_with_tag(42, |w| w.write_string(&**&self.password))?; }
|
||||||
|
if self.auth != pb::wifi1::mod_WifiData::AuthMethod::PLAIN { w.write_with_tag(48, |w| w.write_enum(*&self.auth as i32))?; }
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod mod_WifiData {
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
|
pub enum AuthMethod {
|
||||||
|
PLAIN = 1,
|
||||||
|
WEP = 2,
|
||||||
|
WPA = 3,
|
||||||
|
WPA2Personal = 4,
|
||||||
|
WPAWPA2Personal = 5,
|
||||||
|
WPA2Enterprise = 6,
|
||||||
|
WPA3Personal = 7,
|
||||||
|
WPA2WPA3Personal = 8,
|
||||||
|
WAPIPersonal = 9,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for AuthMethod {
|
||||||
|
fn default() -> Self {
|
||||||
|
AuthMethod::PLAIN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<i32> for AuthMethod {
|
||||||
|
fn from(i: i32) -> Self {
|
||||||
|
match i {
|
||||||
|
1 => AuthMethod::PLAIN,
|
||||||
|
2 => AuthMethod::WEP,
|
||||||
|
3 => AuthMethod::WPA,
|
||||||
|
4 => AuthMethod::WPA2Personal,
|
||||||
|
5 => AuthMethod::WPAWPA2Personal,
|
||||||
|
6 => AuthMethod::WPA2Enterprise,
|
||||||
|
7 => AuthMethod::WPA3Personal,
|
||||||
|
8 => AuthMethod::WPA2WPA3Personal,
|
||||||
|
9 => AuthMethod::WAPIPersonal,
|
||||||
|
_ => Self::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a str> for AuthMethod {
|
||||||
|
fn from(s: &'a str) -> Self {
|
||||||
|
match s {
|
||||||
|
"PLAIN" => AuthMethod::PLAIN,
|
||||||
|
"WEP" => AuthMethod::WEP,
|
||||||
|
"WPA" => AuthMethod::WPA,
|
||||||
|
"WPA2Personal" => AuthMethod::WPA2Personal,
|
||||||
|
"WPAWPA2Personal" => AuthMethod::WPAWPA2Personal,
|
||||||
|
"WPA2Enterprise" => AuthMethod::WPA2Enterprise,
|
||||||
|
"WPA3Personal" => AuthMethod::WPA3Personal,
|
||||||
|
"WPA2WPA3Personal" => AuthMethod::WPA2WPA3Personal,
|
||||||
|
"WAPIPersonal" => AuthMethod::WAPIPersonal,
|
||||||
|
_ => Self::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, PartialEq, Clone)]
|
||||||
|
pub struct WifiDataLibrary<'a> {
|
||||||
|
pub version: u32,
|
||||||
|
pub data: Vec<pb::wifi1::WifiData<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> MessageRead<'a> for WifiDataLibrary<'a> {
|
||||||
|
fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result<Self> {
|
||||||
|
let mut msg = Self::default();
|
||||||
|
while !r.is_eof() {
|
||||||
|
match r.next_tag(bytes) {
|
||||||
|
Ok(8) => msg.version = r.read_uint32(bytes)?,
|
||||||
|
Ok(34) => msg.data.push(r.read_message::<pb::wifi1::WifiData>(bytes)?),
|
||||||
|
Ok(t) => { r.read_unknown(bytes, t)?; }
|
||||||
|
Err(e) => return Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> MessageWrite for WifiDataLibrary<'a> {
|
||||||
|
fn get_size(&self) -> usize {
|
||||||
|
0
|
||||||
|
+ if self.version == 0u32 { 0 } else { 1 + sizeof_varint(*(&self.version) as u64) }
|
||||||
|
+ self.data.iter().map(|s| 1 + sizeof_len((s).get_size())).sum::<usize>()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_message<W: WriterBackend>(&self, w: &mut Writer<W>) -> Result<()> {
|
||||||
|
if self.version != 0u32 { w.write_with_tag(8, |w| w.write_uint32(*&self.version))?; }
|
||||||
|
for s in &self.data { w.write_with_tag(34, |w| w.write_message(s))?; }
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
17
src/s11n/pid1.proto
Normal file
17
src/s11n/pid1.proto
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package pb.pid1;
|
||||||
|
|
||||||
|
message PidData {
|
||||||
|
string name = 4;
|
||||||
|
uint64 created = 5;
|
||||||
|
uint64 modified = 6;
|
||||||
|
|
||||||
|
double p = 10;
|
||||||
|
double i = 11;
|
||||||
|
double d = 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PidDataLibrary {
|
||||||
|
map<uint32, PidData> items = 4;
|
||||||
|
}
|
||||||
25
src/s11n/wifi1.proto
Normal file
25
src/s11n/wifi1.proto
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package pb.wifi1;
|
||||||
|
|
||||||
|
message WifiData {
|
||||||
|
string ssid = 4;
|
||||||
|
string password = 5;
|
||||||
|
enum AuthMethod {
|
||||||
|
PLAIN = 1;
|
||||||
|
WEP = 2;
|
||||||
|
WPA = 3;
|
||||||
|
WPA2Personal = 4;
|
||||||
|
WPAWPA2Personal = 5;
|
||||||
|
WPA2Enterprise = 6;
|
||||||
|
WPA3Personal = 7;
|
||||||
|
WPA2WPA3Personal = 8;
|
||||||
|
WAPIPersonal = 9;
|
||||||
|
}
|
||||||
|
AuthMethod auth = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
message WifiDataLibrary {
|
||||||
|
uint32 version = 1;
|
||||||
|
repeated WifiData data = 4;
|
||||||
|
}
|
||||||
BIN
src/static/favicon.png
Normal file
BIN
src/static/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
12
src/static/styles.css
Normal file
12
src/static/styles.css
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
body {
|
||||||
|
font-family: serif;
|
||||||
|
text-align: center;
|
||||||
|
color: #321C0B;
|
||||||
|
background-color: #D5E4F6;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
color: #B15C1B;
|
||||||
|
}
|
||||||
|
h4 {
|
||||||
|
color: #2969B2;
|
||||||
|
}
|
||||||
BIN
src/static/styles.css.gz
Normal file
BIN
src/static/styles.css.gz
Normal file
Binary file not shown.
7
src/web_pid_get.html
Normal file
7
src/web_pid_get.html
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<h1>Heureka!</h1>
|
||||||
|
<p>Counter is: {{ counter }}</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
Reference in New Issue
Block a user