1use crate::state::{
2 AppState, CollectionMode, CsiDeliveryMode, LogMode, OutputMode, PHY_RATES, UserIntent, WiFiMode,
3};
4
5pub 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}