1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#![cfg_attr(all(windows, not(debug_assertions)), windows_subsystem = "windows")]
//! `ek-ui` 主程序:参数形态接线(界面零配置口径,2026-10-17 用户定盘)。
//!
//! - 无参:demo 窗(dm + oms + ui;网关经运维窗激活);
//! - `--pick`:选窗启动(S1c;选的是 station 工厂表里装什么 —— 结构面);
//! - `--from <清单>`:launcher 协议(BD-UI-14;与 ek-station 同语义);
//! - `--ready-file <标记>`:launcher 就绪协议伴随旗;
//! - 未知参数 usage exit 2。
//!
//! 纪律:ek-ui 不读任何配置文件(零配置口径,见 DESIGN BD-UI-19;需配置的
//! 插件其配置面归插件自身);插件结构只由 `--from` 清单或 demo 缺省决定,
//! ui 插件缺省自动补入。
use std::path::PathBuf;
use ek_station::StationConfig;
use ek_ui::pick::read_pick;
fn usage_exit() -> ! {
eprintln!("usage: ek-ui [--pick] | [--from <list-file>] [--ready-file <marker>]");
std::process::exit(2);
}
/// ui 插件缺一等补(重复即宿主 Duplicate 拒 → boot 硬报错,须先去重)。
fn ensure_ui(mut cfg: StationConfig) -> StationConfig {
if !cfg.plugins.iter().any(|p| p == "ui") {
cfg.plugins.push("ui".to_string());
}
// cfg 收件壳缺一等补(0.6.1 自审补):运维窗三口 ask `cfg.*`;缺了仍能
// 走直库兜底(BD-CFG-10),但目录面会「cfg 缺席」—— 三口的 UX 保正品
// 归宿主装配面。网关的配置面(save 三行序)不依赖它。
if !cfg.plugins.iter().any(|p| p == "cfg") {
cfg.plugins.push("cfg".to_string());
}
cfg
}
/// demo 缺省(0.2.10 起 = dm + oms + ui;网关经运维窗激活,0.1.12 起工厂无 sim)。
fn demo_cfg() -> StationConfig {
let cfg = StationConfig::demo();
ensure_ui(cfg)
}
fn main() {
let argv: Vec<String> = std::env::args().collect();
if argv.len() == 1 {
let opts = ek_ui::runtime::RunOpts::default();
if let Err(e) = ek_ui::run_station_ui(&demo_cfg(), &opts) {
eprintln!("ek-ui: {e}");
std::process::exit(1);
}
return;
}
let mut from: Option<PathBuf> = None;
let mut ready: Option<PathBuf> = None;
let mut pick = false;
let mut i = 1;
while i < argv.len() {
match argv[i].as_str() {
"--pick" => pick = true,
"--from" => match argv.get(i + 1) {
Some(p) => {
from = Some(PathBuf::from(p));
i += 1;
}
None => usage_exit(),
},
"--ready-file" => match argv.get(i + 1) {
Some(p) => {
ready = Some(PathBuf::from(p));
i += 1;
}
None => usage_exit(),
},
other => {
eprintln!("ek-ui: unknown flag {other}");
usage_exit();
}
}
i += 1;
}
if pick {
// 参数冲突不许静默(0.4.3 BD-UI-64,审核 P8):`--pick` 会另起子进程,
// 清单与就绪标记归**子进程**;原先 pick 优先且 `--from` / `--ready-file`
// 被静默忽略,调用方以为传了清单、实际弹的是选窗。
if from.is_some() || ready.is_some() {
eprintln!(
"ek-ui: --pick 不能与 --from / --ready-file 同用(那两条归 --pick 起的子进程)"
);
usage_exit();
}
if let Err(e) = ek_ui::run_pick_launcher() {
eprintln!("ek-ui --pick: {e}");
std::process::exit(2);
}
return;
}
let opts = ek_ui::runtime::RunOpts {
ready_marker: ready,
..Default::default()
};
let cfg = match from {
Some(from_path) => match read_pick(&from_path) {
Err(e) => {
eprintln!("ek-ui --from: {e}");
std::process::exit(1);
}
Ok(names) => ensure_ui(StationConfig { plugins: names }),
},
None => demo_cfg(),
};
if let Err(e) = ek_ui::run_station_ui(&cfg, &opts) {
eprintln!("ek-ui: {e}");
std::process::exit(1);
}
}