ek-ui 0.4.5

ek 生态常驻主界面(egui/eframe):插件即界面——观测页 + 日志页,车头与 UI 同插件
Documentation
//! `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
}

/// 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);
    }
}