1use std::sync::Arc;
13
14use ek_cfg::LoadOutcome;
15use ek_cfg::Store as CfgStore;
16use ek_core::cfg_face::CfgHost;
17use ek_core::main_engine::MainEngine;
18use ek_core::plugin::Plugin;
19use ek_core::section::SectionMap;
20
21use crate::pluginwin::WinState;
22use crate::state::UiShared;
23
24const NS_DM: &str = "dm";
27const NS_OMS: &str = "oms";
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31pub enum FaceKind {
32 Dm,
34 Oms,
36 Tq,
38 Ctp,
40}
41
42pub struct CfgBridge {
44 pub cfg: Option<CfgStore>,
46 pub shared: Arc<UiShared>,
48}
49
50impl CfgBridge {
51 #[must_use]
53 pub fn new(shared: &Arc<UiShared>) -> Self {
54 Self {
55 cfg: CfgStore::eco().ok(),
56 shared: Arc::clone(shared),
57 }
58 }
59}
60
61impl CfgHost for CfgBridge {
62 fn cfg_save(&self, ns: &str, map: &SectionMap) -> Result<(), String> {
63 match &self.cfg {
64 Some(c) => c.store(ns, map).map_err(|e| e.to_string()),
65 None => Err("档案托底根不可用(Store::eco 失败)".to_string()),
66 }
67 }
68
69 fn cfg_load(&self, ns: &str) -> Result<Option<SectionMap>, String> {
70 match &self.cfg {
71 Some(c) => match c.load(ns) {
72 Ok(LoadOutcome::Loaded { map, .. }) => Ok(Some(map)),
73 Ok(LoadOutcome::Fresh) => Ok(None),
74 Err(e) => Err(e.to_string()),
75 },
76 None => Err("档案托底根不可用(Store::eco 失败)".to_string()),
77 }
78 }
79
80 fn log(&self, line: &str) {
81 self.shared.log_line(line);
82 }
83}
84
85#[derive(Default)]
87pub struct FaceStates {
88 pub dm: ek_dm::ui_face::DmUiState,
90 pub oms: ek_oms::ui_face::OmsUiState,
92 pub tq: ek_tq::ui_face::TqUiState,
94 pub wins: std::collections::HashMap<String, WinState>,
96}
97
98impl FaceStates {
99 #[must_use]
101 pub fn new() -> Self {
102 Self::default()
103 }
104}
105
106fn with_plugin<T: Plugin + 'static, R>(
113 engine: &MainEngine,
114 name: &str,
115 f: impl FnOnce(&T) -> R,
116) -> Option<R> {
117 let arc = engine.plugin_arc(name)?;
118 let any = arc.as_any()?;
119 any.downcast_ref::<T>().map(f)
120}
121
122fn face_probe<T: Plugin + 'static>(engine: &MainEngine, name: &str) -> bool {
124 with_plugin(engine, name, |p: &T| p.as_configurable().is_some()).unwrap_or(false)
125}
126
127#[must_use]
129pub fn dm_intervals(engine: &MainEngine) -> Option<Vec<i64>> {
130 with_plugin(engine, ek_ui_ns_dm(), |p: &ek_dm::plugin::DmPlugin| {
131 p.intervals_snapshot()
132 })
133}
134
135fn ek_ui_ns_dm() -> &'static str {
137 NS_DM
138}
139
140#[must_use]
142pub fn face_of(engine: &MainEngine, name: &str) -> Option<FaceKind> {
143 if face_probe::<ek_dm::plugin::DmPlugin>(engine, name) {
144 return Some(FaceKind::Dm);
145 }
146 if face_probe::<ek_oms::plugin::OmsPlugin>(engine, name) {
147 return Some(FaceKind::Oms);
148 }
149 if face_probe::<ek_tq::plugin::TqPlugin>(engine, name) {
150 return Some(FaceKind::Tq);
151 }
152 if face_probe::<ek_ctp::plugin::CtpPlugin>(engine, name) {
153 return Some(FaceKind::Ctp);
154 }
155 None
156}
157
158#[must_use]
160pub fn mounted(engine: &MainEngine) -> Vec<(String, String)> {
161 let mut out: Vec<(String, String)> = Vec::new();
162 for name in engine.plugin_names() {
163 let title = match face_of(engine, &name) {
164 Some(FaceKind::Dm) => "数据管理器 dm",
165 Some(FaceKind::Oms) => "统一受理 oms",
166 Some(FaceKind::Tq) => "天勤桥 tq",
167 Some(FaceKind::Ctp) => "行情网关 ctp",
168 None => continue,
169 };
170 out.push((name, title.to_string()));
171 }
172 out
173}
174
175pub fn apply_section(engine: &MainEngine, ns: &str, section: &SectionMap) -> Option<String> {
178 let rep = match ns {
180 NS_DM => with_plugin(engine, ns, |p: &ek_dm::plugin::DmPlugin| {
181 p.as_configurable()?.apply_config(section).ok()
182 })??,
183 NS_OMS => with_plugin(engine, ns, |p: &ek_oms::plugin::OmsPlugin| {
184 p.as_configurable()?.apply_config(section).ok()
185 })??,
186 _ => {
187 let mut found = None;
189 for name in engine.plugin_names() {
190 if let Some(kind) = face_of(engine, &name) {
191 let hit = match kind {
192 FaceKind::Tq => {
193 with_plugin(engine, &name, |p: &ek_tq::plugin::TqPlugin| {
194 let c = p.as_configurable()?;
195 (c.config_ns() == ns).then(|| c.apply_config(section).ok())
196 })
197 }
198 FaceKind::Ctp => {
199 with_plugin(engine, &name, |p: &ek_ctp::plugin::CtpPlugin| {
200 let c = p.as_configurable()?;
201 (c.config_ns() == ns).then(|| c.apply_config(section).ok())
202 })
203 }
204 _ => None,
205 };
206 if let Some(r) = hit.flatten().flatten() {
208 found = Some(r);
209 break;
210 }
211 }
212 }
213 found?
214 }
215 };
216 Some(format!(
217 "受理 采纳{}/待重启{}",
218 rep.adopted.len(),
219 rep.deferred.len()
220 ))
221}
222
223pub fn render_face(
225 ui: &mut egui::Ui,
226 engine: &MainEngine,
227 name: &str,
228 host: &dyn CfgHost,
229 fs: &mut FaceStates,
230) -> bool {
231 match face_of(engine, name) {
232 Some(FaceKind::Dm) => {
233 let Some(hit) = with_plugin(engine, name, |p: &ek_dm::plugin::DmPlugin| {
234 ek_dm::ui_face::render(ui, p, host, &mut fs.dm)
235 }) else {
236 ui.label("dm 壳取不到(未装配?)");
237 return false;
238 };
239 hit
240 }
241 Some(FaceKind::Oms) => {
242 let Some(hit) = with_plugin(engine, name, |p: &ek_oms::plugin::OmsPlugin| {
243 ek_oms::ui_face::render(ui, p, host, &mut fs.oms)
244 }) else {
245 ui.label("oms 壳取不到(未装配?)");
246 return false;
247 };
248 hit
249 }
250 Some(FaceKind::Tq) => {
251 let Some(hit) = with_plugin(engine, name, |p: &ek_tq::plugin::TqPlugin| {
252 ek_tq::ui_face::render(ui, p, host, &mut fs.tq)
253 }) else {
254 ui.label("tq 壳取不到(未装配?)");
255 return false;
256 };
257 hit
258 }
259 Some(FaceKind::Ctp) => {
260 let Some(arc) = engine.plugin_arc(name) else {
262 ui.label("ctp 壳取不到(未装配?)");
263 return false;
264 };
265 let st = fs.wins.entry(name.to_string()).or_default();
266 crate::pluginwin::render_window(ui, arc.as_ref(), host, st)
267 }
268 None => {
269 ui.label(format!("{name}:无自带面(不在挂载表)"));
270 false
271 }
272 }
273}