Skip to main content

csi_webclient/ui/
config.rs

1use crate::state::{
2    AppState, CollectionMode, CsiDeliveryMode, LogMode, OutputMode, PHY_RATES, UserIntent, WiFiMode,
3};
4
5/// Render the configuration view.
6pub fn render(ui: &mut egui::Ui, state: &mut AppState) {
7    ui.heading("Configuration");
8    ui.separator();
9
10    egui::ScrollArea::vertical()
11        .auto_shrink([false, false])
12        .show(ui, |ui| {
13            render_body(ui, state);
14        });
15}
16
17fn render_body(ui: &mut egui::Ui, state: &mut AppState) {
18    ui.collapsing("Wi-Fi", |ui| {
19        ui.horizontal_wrapped(|ui| {
20            ui.label("Mode");
21            wifi_mode_picker(ui, &mut state.persistent.wifi.mode);
22        });
23
24        ui.horizontal_wrapped(|ui| {
25            ui.label("STA SSID");
26            ui.add(
27                egui::TextEdit::singleline(&mut state.persistent.wifi.sta_ssid)
28                    .desired_width(220.0),
29            );
30        });
31
32        ui.horizontal_wrapped(|ui| {
33            ui.label("STA Password");
34            ui.add(
35                egui::TextEdit::singleline(&mut state.persistent.wifi.sta_password)
36                    .password(true)
37                    .desired_width(220.0),
38            );
39        });
40
41        ui.horizontal_wrapped(|ui| {
42            ui.label("Channel");
43            ui.add(
44                egui::TextEdit::singleline(&mut state.persistent.wifi.channel)
45                    .desired_width(60.0),
46            );
47        });
48
49        if ui.button("Apply Wi-Fi Config").clicked() {
50            state.push_intent(UserIntent::SetWifi(state.persistent.wifi.clone()));
51        }
52    });
53
54    ui.separator();
55
56    ui.collapsing("Traffic", |ui| {
57        ui.horizontal_wrapped(|ui| {
58            ui.label("Frequency (Hz)");
59            ui.add(
60                egui::TextEdit::singleline(&mut state.persistent.traffic.frequency_hz)
61                    .desired_width(100.0),
62            );
63            if ui.button("Apply Traffic Config").clicked() {
64                state.push_intent(UserIntent::SetTraffic(state.persistent.traffic.clone()));
65            }
66        });
67    });
68
69    ui.separator();
70
71    ui.collapsing("CSI Flags", |ui| {
72        ui.horizontal_wrapped(|ui| {
73            ui.checkbox(&mut state.persistent.csi.disable_lltf, "disable_lltf");
74            ui.checkbox(&mut state.persistent.csi.disable_htltf, "disable_htltf");
75            ui.checkbox(
76                &mut state.persistent.csi.disable_stbc_htltf,
77                "disable_stbc_htltf",
78            );
79            ui.checkbox(
80                &mut state.persistent.csi.disable_ltf_merge,
81                "disable_ltf_merge",
82            );
83            ui.checkbox(&mut state.persistent.csi.disable_csi, "disable_csi");
84            ui.checkbox(
85                &mut state.persistent.csi.disable_csi_legacy,
86                "disable_csi_legacy",
87            );
88            ui.checkbox(
89                &mut state.persistent.csi.disable_csi_ht20,
90                "disable_csi_ht20",
91            );
92            ui.checkbox(
93                &mut state.persistent.csi.disable_csi_ht40,
94                "disable_csi_ht40",
95            );
96            ui.checkbox(&mut state.persistent.csi.disable_csi_su, "disable_csi_su");
97            ui.checkbox(&mut state.persistent.csi.disable_csi_mu, "disable_csi_mu");
98            ui.checkbox(&mut state.persistent.csi.disable_csi_dcm, "disable_csi_dcm");
99            ui.checkbox(
100                &mut state.persistent.csi.disable_csi_beamformed,
101                "disable_csi_beamformed",
102            );
103        });
104
105        ui.horizontal_wrapped(|ui| {
106            ui.label("csi_he_stbc (u32)");
107            ui.add(
108                egui::TextEdit::singleline(&mut state.persistent.csi.csi_he_stbc)
109                    .desired_width(80.0),
110            );
111            ui.label("val_scale_cfg (u32)");
112            ui.add(
113                egui::TextEdit::singleline(&mut state.persistent.csi.val_scale_cfg)
114                    .desired_width(80.0),
115            );
116        });
117
118        if ui.button("Apply CSI Config").clicked() {
119            state.push_intent(UserIntent::SetCsi(state.persistent.csi.clone()));
120        }
121    });
122
123    ui.separator();
124
125    ui.collapsing("PHY Rate (ESP-NOW only)", |ui| {
126        ui.horizontal_wrapped(|ui| {
127            ui.label("Rate");
128            phy_rate_picker(ui, &mut state.persistent.phy_rate.rate);
129            if ui.button("Apply Rate").clicked() {
130                state.push_intent(UserIntent::SetPhyRate(state.persistent.phy_rate.clone()));
131            }
132        });
133        ui.add(
134            egui::Label::new(
135                "Honored by esp-now-central / esp-now-peripheral; ignored by station/sniffer.",
136            )
137            .wrap(),
138        );
139    });
140
141    ui.separator();
142
143    ui.collapsing("IO Tasks", |ui| {
144        ui.horizontal_wrapped(|ui| {
145            ui.checkbox(&mut state.persistent.io_tasks.tx, "TX (traffic generation)");
146            ui.checkbox(&mut state.persistent.io_tasks.rx, "RX (CSI capture)");
147        });
148        if ui.button("Apply IO Tasks").clicked() {
149            state.push_intent(UserIntent::SetIoTasks(state.persistent.io_tasks.clone()));
150        }
151    });
152
153    ui.separator();
154
155    ui.collapsing("CSI Delivery", |ui| {
156        ui.horizontal_wrapped(|ui| {
157            ui.label("Mode");
158            csi_delivery_picker(ui, &mut state.persistent.csi_delivery.mode);
159            ui.checkbox(
160                &mut state.persistent.csi_delivery.logging,
161                "inline UART logging",
162            );
163        });
164        if ui.button("Apply CSI Delivery").clicked() {
165            state.push_intent(UserIntent::SetCsiDelivery(
166                state.persistent.csi_delivery.clone(),
167            ));
168        }
169    });
170
171    ui.separator();
172
173    ui.horizontal_wrapped(|ui| {
174        ui.label("Collection Mode");
175        collection_mode_picker(ui, &mut state.persistent.collection_mode);
176        if ui.button("Apply").clicked() {
177            state.push_intent(UserIntent::SetCollectionMode(
178                state.persistent.collection_mode,
179            ));
180        }
181    });
182
183    ui.horizontal_wrapped(|ui| {
184        ui.label("Log Mode");
185        log_mode_picker(ui, &mut state.persistent.log_mode);
186        if ui.button("Apply").clicked() {
187            state.push_intent(UserIntent::SetLogMode(state.persistent.log_mode));
188        }
189    });
190
191    ui.horizontal_wrapped(|ui| {
192        ui.label("Output Mode");
193        output_mode_picker(ui, &mut state.persistent.output_mode);
194        if ui.button("Apply").clicked() {
195            state.push_intent(UserIntent::SetOutputMode(state.persistent.output_mode));
196        }
197    });
198
199    ui.horizontal_wrapped(|ui| {
200        if ui.button("Reset Config Defaults").clicked() {
201            state.push_intent(UserIntent::ResetConfig);
202        }
203
204        if ui.button("Refresh Config").clicked() {
205            state.push_intent(UserIntent::FetchConfig);
206        }
207    });
208}
209
210fn wifi_mode_picker(ui: &mut egui::Ui, mode: &mut WiFiMode) {
211    egui::ComboBox::from_id_salt("wifi_mode_combo")
212        .selected_text(mode.as_api_value())
213        .show_ui(ui, |ui| {
214            ui.selectable_value(mode, WiFiMode::Station, "station");
215            ui.selectable_value(mode, WiFiMode::Sniffer, "sniffer");
216            ui.selectable_value(mode, WiFiMode::EspNowCentral, "esp-now-central");
217            ui.selectable_value(mode, WiFiMode::EspNowPeripheral, "esp-now-peripheral");
218        });
219}
220
221fn collection_mode_picker(ui: &mut egui::Ui, mode: &mut CollectionMode) {
222    egui::ComboBox::from_id_salt("collection_mode_combo")
223        .selected_text(mode.as_api_value())
224        .show_ui(ui, |ui| {
225            ui.selectable_value(mode, CollectionMode::Collector, "collector");
226            ui.selectable_value(mode, CollectionMode::Listener, "listener");
227        });
228}
229
230fn log_mode_picker(ui: &mut egui::Ui, mode: &mut LogMode) {
231    egui::ComboBox::from_id_salt("log_mode_combo")
232        .selected_text(mode.as_api_value())
233        .show_ui(ui, |ui| {
234            ui.selectable_value(mode, LogMode::Text, "text");
235            ui.selectable_value(mode, LogMode::ArrayList, "array-list");
236            ui.selectable_value(mode, LogMode::Serialized, "serialized");
237            ui.selectable_value(mode, LogMode::EspCsiTool, "esp-csi-tool");
238        });
239}
240
241fn output_mode_picker(ui: &mut egui::Ui, mode: &mut OutputMode) {
242    egui::ComboBox::from_id_salt("output_mode_combo")
243        .selected_text(mode.as_api_value())
244        .show_ui(ui, |ui| {
245            ui.selectable_value(mode, OutputMode::Stream, "stream");
246            ui.selectable_value(mode, OutputMode::Dump, "dump");
247            ui.selectable_value(mode, OutputMode::Both, "both");
248        });
249}
250
251fn csi_delivery_picker(ui: &mut egui::Ui, mode: &mut CsiDeliveryMode) {
252    egui::ComboBox::from_id_salt("csi_delivery_combo")
253        .selected_text(mode.as_api_value())
254        .show_ui(ui, |ui| {
255            ui.selectable_value(mode, CsiDeliveryMode::Off, "off");
256            ui.selectable_value(mode, CsiDeliveryMode::Callback, "callback");
257            ui.selectable_value(mode, CsiDeliveryMode::Async, "async");
258        });
259}
260
261fn phy_rate_picker(ui: &mut egui::Ui, rate: &mut String) {
262    egui::ComboBox::from_id_salt("phy_rate_combo")
263        .selected_text(rate.as_str())
264        .show_ui(ui, |ui| {
265            for option in PHY_RATES {
266                ui.selectable_value(rate, (*option).to_owned(), *option);
267            }
268        });
269}