核心变更:
1. GridAxisValue 源精度命名
- 新增 GridAxisValue 类型,携带 f64 数值 + YAML 源书写文本(Deref 透明兼容算术)
- config.rs 绕过 serde_yaml 归一化,逐 token 捕获轴值原文(logg: 5.0 → g5.0)
- runner/executor/scheduler 全链路改用 DB TEXT 列权威 point_name,
修复 REAL 列回读丢精度导致的 model_name 错配
2. 工作流执行可观测台
- 新增 stats/progress/points 三组 API(进度时间序列、经验速率 ETA、
停滞预警、逐点明细分页、收敛性热力图数据)
- 新增 workflow_progress_snapshots 表 + tasks/grid_points 耗时列
- runner 携带 last_iter/worst_depth/n_depths 进 conv.json
- 前端新增 hash 路由、工作流详情页(概览/网格点/收敛分析三 Tab)、YAML 编辑器
3. 节点停用/启用管理
- 新增 disabled 状态 + disable/enable API;停用节点保持心跳但停止分发,
worker 空闲待命而非退出;移除 revoke API,token 失效统一走重发覆盖;
移除 host_name 字段
4. 白名单结果归档
- 新增 result_filter 模块,只归档有语义产物,丢弃 Tlusty 中间单元(~2MB/模型)
- executor 原子写入归档 + 200 点 LRU 上限
5. 历史数据导入
- sync_seeds 重写为 import_results:经 /admin/import_seed 标记 converged +
按新版命名迁移产物树
6. 部署与目录重规划
- data/results→seeds、data/archive→result + migrate_data_dirs.sh
- deploy.sh 增强(SSH 复用、Profile、远程 env);Dockerfile 瘦身
7. 文档同步更新 api/database/architecture/deployment
274 lines
13 KiB
Bash
Executable File
274 lines
13 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# ==============================================================================
|
||
# push_import_results.sh — 推送 import_results 工具到 Windows 计算节点并编译
|
||
# ==============================================================================
|
||
# 把 import_results 工具的源码推送到 deploy.env.d/<profile> 指定的 Windows 节点,
|
||
# 在该节点原生编译(Windows 上 cargo build),并打印确切的运行命令。
|
||
#
|
||
# 设计前提:
|
||
# - 旧版 Python run_grid.py 的结果在 Windows 机的 E:/fmq/grid(工具必须在该机运行才能读到)
|
||
# - 该机已部署 node(Docker Compose),data 目录在 E:/fmq/dcts/data
|
||
# - DCTS 服务端地址来自 profile 的 DCTS_SERVER_URL
|
||
# - Windows 机需已装 Rust 工具链(cargo/rustc);脚本会自检并给出安装提示
|
||
#
|
||
# 用法:
|
||
# ./scripts/push_import_results.sh # 默认用 node-dckj-win-01.env
|
||
# ./scripts/push_import_results.sh -f deploy.env.d/other-node.env # 指定其他 profile
|
||
# ./scripts/push_import_results.sh --no-build # 只推源码不编译(远端已有缓存时加速)
|
||
# ./scripts/push_import_results.sh --archive-dir E:/fmq/dcts/data/result # 覆盖归档目录
|
||
#
|
||
# 运行(推完编译后,在 Windows 机上执行,或本脚本末尾会打印):
|
||
# E:\fmq\dcts-src\target\release\import_results.exe \
|
||
# -d E:/fmq/grid -c E:/fmq/dcts-src/workflows/sdB_cno.yaml \
|
||
# -r E:/fmq/dcts/data/result -s http://100.66.1.2:8090 \
|
||
# -w sdB_cno -t <admin_token>
|
||
# ==============================================================================
|
||
|
||
set -eo pipefail
|
||
|
||
GREEN='\033[0;32m'
|
||
BLUE='\033[0;34m'
|
||
RED='\033[0;31m'
|
||
YELLOW='\033[1;33m'
|
||
CYAN='\033[0;36m'
|
||
NC='\033[0m'
|
||
|
||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
WORK_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||
cd "${WORK_DIR}"
|
||
|
||
# 默认值(可被 profile 覆盖)
|
||
PROFILE_FILE="deploy.env.d/node-dckj-win-01.env"
|
||
REMOTE_USER="fmq"
|
||
REMOTE_IP="192.168.6.102"
|
||
REMOTE_PORT="22"
|
||
REMOTE_SRC_DIR="E:/fmq/dcts-src"
|
||
DCTS_SERVER_URL="http://100.66.1.2:8090"
|
||
RESULT_DIR="" # 空 → 用 profile 推导(默认 E:/fmq/dcts/data/result)
|
||
DO_BUILD=true
|
||
|
||
# SSH ControlMaster 复用目录(与 deploy.sh 一致,便于共享连接)
|
||
SSH_CONTROL_DIR="${SSH_CONTROL_DIR:-$HOME/.ssh/cm}"
|
||
|
||
# =============================================================================
|
||
# 解析命令行参数
|
||
# =============================================================================
|
||
while [[ $# -gt 0 ]]; do
|
||
case "$1" in
|
||
-f|--profile) PROFILE_FILE="$2"; shift 2 ;;
|
||
--no-build) DO_BUILD=false; shift ;;
|
||
--archive-dir) RESULT_DIR="$2"; shift 2 ;;
|
||
-h|--help)
|
||
sed -n '2,30p' "$0"
|
||
exit 0 ;;
|
||
*) echo -e "${RED}未知参数: $1${NC}"; exit 1 ;;
|
||
esac
|
||
done
|
||
|
||
# =============================================================================
|
||
# 加载 profile(SSH 连接信息 + 服务端地址)
|
||
# =============================================================================
|
||
if [ ! -f "$PROFILE_FILE" ]; then
|
||
echo -e "${RED}错误: 找不到 profile '${PROFILE_FILE}'${NC}"
|
||
exit 1
|
||
fi
|
||
echo -e "${CYAN}[i] 加载 profile: ${PROFILE_FILE}${NC}"
|
||
# shellcheck disable=SC1090
|
||
set -a; source "$PROFILE_FILE"; set +a
|
||
|
||
# profile 提供的变量优先(REMOTE_* / DCTS_SERVER_URL)
|
||
[ -n "${REMOTE_USER:-}" ] && REMOTE_USER="${REMOTE_USER}"
|
||
[ -n "${REMOTE_IP:-}" ] && REMOTE_IP="${REMOTE_IP}"
|
||
[ -n "${REMOTE_PORT:-}" ] && REMOTE_PORT="${REMOTE_PORT}"
|
||
# 源码目录默认放到部署目录旁(E:/fmq/dcts-src),与运行中的 node(E:/fmq/dcts)隔离
|
||
[ -n "${REMOTE_DIR:-}" ] && REMOTE_SRC_DIR="${REMOTE_DIR}-src"
|
||
|
||
# 结果目录:命令行 > 参数 > 默认(部署目录下的 data/result,node 的 DCTS_RESULT_DIR 卷映射到此)
|
||
if [ -z "${RESULT_DIR}" ]; then
|
||
RESULT_DIR="${REMOTE_DIR}/data/result"
|
||
fi
|
||
|
||
echo -e "${CYAN}📢 目标节点: ${YELLOW}${REMOTE_USER}@${REMOTE_IP}:${REMOTE_PORT}${NC}"
|
||
echo -e "${CYAN}📢 源码推送: ${YELLOW}${REMOTE_SRC_DIR}${NC}"
|
||
echo -e "${CYAN}📢 结果目录: ${YELLOW}${RESULT_DIR}${NC}"
|
||
echo -e "${CYAN}📢 服务端: ${YELLOW}${DCTS_SERVER_URL}${NC}"
|
||
echo ""
|
||
|
||
# =============================================================================
|
||
# 建立 SSH ControlMaster 连接复用(一次性输密码,后续 scp/ssh 复用)
|
||
# =============================================================================
|
||
mkdir -p "${SSH_CONTROL_DIR}"
|
||
SOCK="${SSH_CONTROL_DIR}/${REMOTE_USER}@${REMOTE_IP}:${REMOTE_PORT}"
|
||
SSH_OPTS="-o ControlMaster=auto -o ControlPath=${SOCK} -o ControlPersist=1800 -o StrictHostKeyChecking=no"
|
||
|
||
if ssh -p "${REMOTE_PORT}" -o ControlPath="${SOCK}" -O check "${REMOTE_USER}@${REMOTE_IP}" >/dev/null 2>&1; then
|
||
echo -e "${GREEN}[√] 复用已有 SSH 连接${NC}"
|
||
else
|
||
echo -e "${CYAN}=== 建立 SSH 连接(请输入 ${REMOTE_USER}@${REMOTE_IP} 的密码)===${NC}"
|
||
if ! ssh -p "${REMOTE_PORT}" ${SSH_OPTS} -fN "${REMOTE_USER}@${REMOTE_IP}"; then
|
||
echo -e "${RED}SSH 连接失败${NC}"
|
||
exit 1
|
||
fi
|
||
echo -e "${GREEN}[√] SSH 连接已建立${NC}"
|
||
fi
|
||
trap 'ssh -p "${REMOTE_PORT}" -o ControlPath="${SOCK}" "${REMOTE_USER}@${REMOTE_IP}" -O exit 2>/dev/null || true' EXIT
|
||
|
||
# =============================================================================
|
||
# 自检远端 Rust 工具链
|
||
# =============================================================================
|
||
echo -e "\n${CYAN}=== 检查远端 Rust 工具链 ===${NC}"
|
||
HAS_CARGO=$(ssh -p "${REMOTE_PORT}" ${SSH_OPTS} "${REMOTE_USER}@${REMOTE_IP}" \
|
||
"powershell -NoProfile -Command \"if (Get-Command cargo -ErrorAction SilentlyContinue) { 'yes' } else { 'no' }\"" 2>/dev/null | tr -d '[:space:]')
|
||
if [ "$HAS_CARGO" != "yes" ]; then
|
||
echo -e "${RED}[✗] 远端 ${REMOTE_IP} 未检测到 cargo/rustc${NC}"
|
||
echo -e "${YELLOW}请先在该 Windows 机上安装 Rust 工具链:${NC}"
|
||
echo -e " 方式1(PowerShell,需联网): winget install Rustlang.Rustup"
|
||
echo -e " 方式2(浏览器): 访问 https://rustup.rs 下载 rustup-init.exe 并运行"
|
||
echo -e " 安装后重开 PowerShell 再跑本脚本。"
|
||
exit 1
|
||
fi
|
||
echo -e "${GREEN}[√] 远端已安装 Rust 工具链${NC}"
|
||
|
||
# =============================================================================
|
||
# 同步源码到远端(仅 git 跟踪文件,排除 target/data/assets 等大目录)
|
||
# =============================================================================
|
||
echo -e "\n${CYAN}=== 同步源码到 ${REMOTE_SRC_DIR} ===${NC}"
|
||
# 远端先建目录
|
||
ssh -p "${REMOTE_PORT}" ${SSH_OPTS} "${REMOTE_USER}@${REMOTE_IP}" \
|
||
"powershell -NoProfile -Command \"New-Item -ItemType Directory -Force -Path '${REMOTE_SRC_DIR}' | Out-Null\""
|
||
|
||
# 单文件 scp 辅助:失败时打印诊断并退出(不静默吞错)。
|
||
# Windows OpenSSH scp 对带正斜杠的远端路径需用 ':' 分隔主机,路径部分正斜杠 OK。
|
||
scp_one() {
|
||
local src="$1" dst="$2"
|
||
if ! scp -P "${REMOTE_PORT}" ${SSH_OPTS} "$src" "${REMOTE_USER}@${REMOTE_IP}:${dst}"; then
|
||
echo -e "${RED}[✗] 传输失败: ${src} -> ${dst}${NC}"
|
||
exit 1
|
||
fi
|
||
}
|
||
# 递归目录 scp:先建/清远端目标目录再传,避免 Windows scp -r 创建子目录失败。
|
||
scp_dir() {
|
||
local sub="$1"
|
||
[ -d "$sub" ] || return 0
|
||
# 远端先删除旧目录并重建空目录(确保 scp -r 能写入)
|
||
ssh -p "${REMOTE_PORT}" ${SSH_OPTS} "${REMOTE_USER}@${REMOTE_IP}" \
|
||
"powershell -NoProfile -Command \"Remove-Item -Recurse -Force '${REMOTE_SRC_DIR}/${sub}' -ErrorAction SilentlyContinue; New-Item -ItemType Directory -Force -Path '${REMOTE_SRC_DIR}/${sub}' | Out-Null\"" || true
|
||
echo -e " -> ${sub}/"
|
||
if ! scp -P "${REMOTE_PORT}" ${SSH_OPTS} -r "$sub" "${REMOTE_USER}@${REMOTE_IP}:${REMOTE_SRC_DIR}/"; then
|
||
echo -e "${RED}[✗] 传输目录失败: ${sub}${NC}"
|
||
echo -e "${YELLOW} 若反复失败,可在 Windows 机上手动 git clone 后仅同步增量,或检查磁盘空间/权限${NC}"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
echo -e "${CYAN} -> 传输顶层文件 + crates/tools/workflows ...${NC}"
|
||
scp_one Cargo.toml "${REMOTE_SRC_DIR}/"
|
||
[ -f Cargo.lock ] && scp_one Cargo.lock "${REMOTE_SRC_DIR}/"
|
||
# import_results 依赖 common crate,二者都要同步;workflows 含配置(决定命名精度)。
|
||
scp_dir crates
|
||
scp_dir tools
|
||
scp_dir workflows
|
||
echo -e "${GREEN}[√] 源码同步完成${NC}"
|
||
|
||
# =============================================================================
|
||
# 远端编译 import_results(release)
|
||
# =============================================================================
|
||
if [ "$DO_BUILD" = true ]; then
|
||
echo -e "\n${CYAN}=== 远端编译 import_results (release) ===${NC}"
|
||
echo -e "${YELLOW} (首次编译会拉 crate 依赖,可能耗时数分钟,请耐心等待...)${NC}"
|
||
ssh -p "${REMOTE_PORT}" ${SSH_OPTS} "${REMOTE_USER}@${REMOTE_IP}" \
|
||
"powershell -NoProfile -Command \"Set-Location '${REMOTE_SRC_DIR}'; cargo build --release -p import_results 2>&1; \$LASTEXITCODE\"" 2>&1 | tee /tmp/import_build.log | tail -20
|
||
BUILD_RC=$(tail -1 /tmp/import_build.log | tr -d '[:space:]')
|
||
rm -f /tmp/import_build.log
|
||
if [ "$BUILD_RC" != "0" ]; then
|
||
echo -e "${RED}[✗] 远端编译失败 (exit=${BUILD_RC})${NC}"
|
||
exit 1
|
||
fi
|
||
echo -e "${GREEN}[√] 编译成功${NC}"
|
||
else
|
||
echo -e "${YELLOW}[i] 跳过编译(--no-build)${NC}"
|
||
fi
|
||
|
||
# =============================================================================
|
||
# 在远端生成 run_import.bat(Windows 批处理),用户双击或在 cmd 里运行即可,无需手敲命令
|
||
# =============================================================================
|
||
EXE_PATH_WIN=$(echo "${REMOTE_SRC_DIR}/target/release/import_results.exe" | sed 's|/|\\|g')
|
||
CONFIG_PATH="${REMOTE_SRC_DIR}/workflows/sdB_cno.yaml"
|
||
WORK_DIR_WIN=$(echo "${REMOTE_SRC_DIR}" | sed 's|/|\\|g')
|
||
|
||
# .bat 内容:用 quoted heredoc 避免反引号/% 被 bash 解释,占位符再替换。
|
||
# .bat 用 set /p 交互读取 admin token(用户无需手动编辑文件),空回车则跳过入库只迁移 result。
|
||
BAT_CONTENT=$(cat <<'BATEOF'
|
||
@echo off
|
||
chcp 65001 >nul
|
||
setlocal
|
||
|
||
cd /d "__WORK_DIR__"
|
||
|
||
echo ============================================================
|
||
echo DCTS 历史计算结果导入工具
|
||
echo ============================================================
|
||
echo 旧数据目录 : E:\fmq\grid
|
||
echo workflow : __CONFIG__
|
||
echo 结果目录 : __RESULT__
|
||
echo 服务端 : __SERVER__
|
||
echo 工作流名 : sdB_cno
|
||
echo ============================================================
|
||
echo.
|
||
|
||
set /p ADMIN_TOKEN="请输入 Admin Token (DCTS_ADMIN_TOKEN,直接回车则只迁移 result 不入库): "
|
||
|
||
if "%ADMIN_TOKEN%"=="" (
|
||
echo [i] 未输入 token,仅迁移产物树到 result (入库会因无鉴权失败但不阻断迁移)
|
||
"__EXE__" -d E:/fmq/grid -c "__CONFIG__" -r "__RESULT__" -s __SERVER__ -w sdB_cno
|
||
) else (
|
||
"__EXE__" -d E:/fmq/grid -c "__CONFIG__" -r "__RESULT__" -s __SERVER__ -w sdB_cno -t "%ADMIN_TOKEN%"
|
||
)
|
||
|
||
echo.
|
||
echo ============================================================
|
||
echo 执行结束。按任意键退出...
|
||
pause >nul
|
||
BATEOF
|
||
)
|
||
BAT_CONTENT="${BAT_CONTENT//__WORK_DIR__/${WORK_DIR_WIN}}"
|
||
BAT_CONTENT="${BAT_CONTENT//__EXE__/${EXE_PATH_WIN}}"
|
||
BAT_CONTENT="${BAT_CONTENT//__CONFIG__/${CONFIG_PATH}}"
|
||
BAT_CONTENT="${BAT_CONTENT//__RESULT__/${RESULT_DIR}}"
|
||
BAT_CONTENT="${BAT_CONTENT//__SERVER__/${DCTS_SERVER_URL}}"
|
||
|
||
# 经 SSH stdin 管道写入远端 run_import.bat(UTF-8 with BOM 由 chcp 65001 处理中文)
|
||
BAT_REMOTE_PATH="${REMOTE_SRC_DIR}/run_import.bat"
|
||
echo -e "\n${CYAN}=== 在远端生成 run_import.bat ===${NC}"
|
||
printf '%s\r\n' "$BAT_CONTENT" | ssh -p "${REMOTE_PORT}" ${SSH_OPTS} "${REMOTE_USER}@${REMOTE_IP}" \
|
||
"powershell -NoProfile -Command \"\$input = [Console]::In.ReadToEnd(); [System.IO.File]::WriteAllText('${BAT_REMOTE_PATH}', \$input, [System.Text.Encoding]::UTF8)\"" \
|
||
&& echo -e "${GREEN}[√] 已生成: ${BAT_REMOTE_PATH}${NC}" \
|
||
|| { echo -e "${RED}[✗] 生成 run_import.bat 失败${NC}"; exit 1; }
|
||
|
||
# =============================================================================
|
||
# 输出最终提示
|
||
# =============================================================================
|
||
echo ""
|
||
echo -e "${BLUE}==============================================================${NC}"
|
||
echo -e "${GREEN}🎉 推送 + 编译完成!${NC}"
|
||
echo -e "${BLUE}==============================================================${NC}"
|
||
echo ""
|
||
echo -e "${CYAN}在 Windows 机 ${REMOTE_IP} 上启动导入(任选其一):${NC}"
|
||
echo ""
|
||
echo -e " 方式1(推荐):双击运行 ${YELLOW}${WORK_DIR_WIN}\\run_import.bat${NC}"
|
||
echo -e " 会交互提示输入 Admin Token(回车则只迁移 result 不入库)"
|
||
echo ""
|
||
echo -e " 方式2(cmd): cd /d ${WORK_DIR_WIN} 然后 run_import.bat"
|
||
echo ""
|
||
echo -e "${YELLOW}参数(已内置在 .bat 里,无需手填):${NC}"
|
||
echo -e " -d E:/fmq/grid 旧版 Python run_grid.py 的 results 根目录"
|
||
echo -e " -c ${CONFIG_PATH}"
|
||
echo -e " -r ${RESULT_DIR} 结果写入目标(node 容器卷映射处)"
|
||
echo -e " -s ${DCTS_SERVER_URL} 服务端地址(来自 profile)"
|
||
echo -e " -w sdB_cno 工作流名(必须等于将来要启动的工作流名)"
|
||
echo ""
|
||
echo -e "${YELLOW}⚠️ 注意:${NC}"
|
||
echo -e " - -w sdB_cno 必须匹配将来在 Dashboard 创建/启动的工作流名,否则 converged 标记被隔离看不到、导致重算"
|
||
echo -e " - 工具可重复运行(DB 幂等 + result 原子覆盖),网络中断后重跑 run_import.bat 即可补齐"
|
||
echo ""
|