Skip to main content

ua_client/eui/
app.rs

1use tokio::runtime::Runtime;
2use tokio::sync::mpsc;
3
4use crate::engine::{Engine, FilePickTarget, FrontendCtx};
5use crate::messages::UiUpdate;
6use crate::types::AuthMode;
7
8pub struct UaApp {
9    engine: Engine,
10    update_rx: mpsc::UnboundedReceiver<UiUpdate>,
11}
12
13#[derive(Clone)]
14struct EguiCtx(egui::Context);
15
16impl FrontendCtx for EguiCtx {
17    fn request_repaint(&self) {
18        self.0.request_repaint();
19    }
20
21    fn set_clipboard(&self, text: &str) {
22        let s = text.to_owned();
23        self.0.output_mut(|o| o.copied_text = s);
24    }
25
26    fn pick_file(
27        &self,
28        rt: &Runtime,
29        update_tx: &mpsc::UnboundedSender<UiUpdate>,
30        target: FilePickTarget,
31        title: &str,
32        default_dir: &str,
33    ) {
34        let tx = update_tx.clone();
35        let ctx = self.clone();
36        let title = title.to_owned();
37        let default_dir = default_dir.to_owned();
38        rt.spawn_blocking(move || {
39            let mut dlg = rfd::FileDialog::new().set_title(&title);
40            if let Some(parent) = std::path::Path::new(&default_dir).parent()
41                && parent.exists()
42            {
43                dlg = dlg.set_directory(parent);
44            }
45            if let Some(path) = dlg.pick_file() {
46                let s = path.to_string_lossy().into_owned();
47                let update = match target {
48                    FilePickTarget::CertPath => UiUpdate::CertPathPicked(s),
49                    FilePickTarget::KeyPath => UiUpdate::KeyPathPicked(s),
50                };
51                let _ = tx.send(update);
52            }
53            let _ = tx.send(UiUpdate::FilePickerClosed);
54            ctx.request_repaint();
55        });
56    }
57}
58
59const STORAGE_ENDPOINT_URL: &str = "endpoint_url";
60const STORAGE_ENDPOINT_HISTORY: &str = "endpoint_history";
61const STORAGE_AUTH_MODE: &str = "auth_mode";
62const STORAGE_AUTH_USERNAME: &str = "auth_username";
63const STORAGE_AUTH_CERT_PATH: &str = "auth_cert_path";
64const STORAGE_AUTH_KEY_PATH: &str = "auth_key_path";
65const STORAGE_LAST_SELECTIONS: &str = "last_selection_paths";
66
67impl UaApp {
68    pub fn new(
69        rt: Runtime,
70        log_rx: mpsc::UnboundedReceiver<UiUpdate>,
71        storage: Option<&dyn eframe::Storage>,
72    ) -> Self {
73        let (mut engine, update_rx) = Engine::new(rt, log_rx);
74        if let Some(s) = storage {
75            if let Some(url) = eframe::get_value::<String>(s, STORAGE_ENDPOINT_URL) {
76                engine.model.endpoint_url = url;
77            }
78            if let Some(hist) = eframe::get_value::<Vec<String>>(s, STORAGE_ENDPOINT_HISTORY) {
79                engine.model.endpoint_history = hist;
80            }
81            if let Some(m) = eframe::get_value::<String>(s, STORAGE_AUTH_MODE) {
82                engine.model.auth_mode = match m.as_str() {
83                    "UserName" => AuthMode::UserName,
84                    "Certificate" => AuthMode::Certificate,
85                    _ => AuthMode::Anonymous,
86                };
87            }
88            if let Some(s2) = eframe::get_value::<String>(s, STORAGE_AUTH_USERNAME) {
89                engine.model.auth_username = s2;
90            }
91            if let Some(s2) = eframe::get_value::<String>(s, STORAGE_AUTH_CERT_PATH) {
92                engine.model.auth_cert_path = s2;
93            }
94            if let Some(s2) = eframe::get_value::<String>(s, STORAGE_AUTH_KEY_PATH) {
95                engine.model.auth_key_path = s2;
96            }
97            if let Some(stored) = eframe::get_value::<std::collections::HashMap<String, Vec<String>>>(
98                s,
99                STORAGE_LAST_SELECTIONS,
100            ) {
101                use std::str::FromStr;
102                for (url, ids) in stored {
103                    let path: Vec<opcua::types::NodeId> = ids
104                        .iter()
105                        .filter_map(|s| opcua::types::NodeId::from_str(s).ok())
106                        .collect();
107                    if !path.is_empty() {
108                        engine.model.last_selection_paths.insert(url, path);
109                    }
110                }
111            }
112        }
113        Self { engine, update_rx }
114    }
115}
116
117impl eframe::App for UaApp {
118    fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
119        let c = EguiCtx(ctx.clone());
120        while let Ok(update) = self.update_rx.try_recv() {
121            self.engine.apply_update(&c, update);
122        }
123        let mut actions = Vec::new();
124        super::ui::draw(&self.engine.model, ctx, &mut actions);
125        for action in actions {
126            self.engine.dispatch(&c, action);
127        }
128    }
129
130    fn save(&mut self, storage: &mut dyn eframe::Storage) {
131        eframe::set_value(storage, STORAGE_ENDPOINT_URL, &self.engine.model.endpoint_url);
132        eframe::set_value(
133            storage,
134            STORAGE_ENDPOINT_HISTORY,
135            &self.engine.model.endpoint_history,
136        );
137        let auth_mode_str = match self.engine.model.auth_mode {
138            AuthMode::Anonymous => "Anonymous",
139            AuthMode::UserName => "UserName",
140            AuthMode::Certificate => "Certificate",
141        };
142        eframe::set_value(storage, STORAGE_AUTH_MODE, &auth_mode_str.to_string());
143        eframe::set_value(
144            storage,
145            STORAGE_AUTH_USERNAME,
146            &self.engine.model.auth_username,
147        );
148        eframe::set_value(
149            storage,
150            STORAGE_AUTH_CERT_PATH,
151            &self.engine.model.auth_cert_path,
152        );
153        eframe::set_value(
154            storage,
155            STORAGE_AUTH_KEY_PATH,
156            &self.engine.model.auth_key_path,
157        );
158        let paths: std::collections::HashMap<String, Vec<String>> = self
159            .engine
160            .model
161            .last_selection_paths
162            .iter()
163            .map(|(url, path)| (url.clone(), path.iter().map(|n| n.to_string()).collect()))
164            .collect();
165        eframe::set_value(storage, STORAGE_LAST_SELECTIONS, &paths);
166    }
167}