feat(all): 重炼 crates/common 核心组件、上线 Web 运维看板与 Docker 容器化部署
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
use anyhow::Result;
|
||||
use common::embedded::{ensure_specific_data_files, RuntimePaths};
|
||||
use common::models::{ModelSummary, TaskSpec, TaskType};
|
||||
use common::runner::ExecutionRunner;
|
||||
use reqwest::Client;
|
||||
use std::path::{Path, PathBuf};
|
||||
use tracing::{info, warn};
|
||||
|
||||
pub async fn execute_task(
|
||||
client: &Client,
|
||||
server_url: &str,
|
||||
runtime: &RuntimePaths,
|
||||
work_dir: &Path,
|
||||
task: &TaskSpec,
|
||||
) -> Result<(ModelSummary, Option<Vec<u8>>)> {
|
||||
info!("开始执行计算任务 {} (网格点: {})", task.task_id, task.point_name);
|
||||
|
||||
// 1. Pull ONLY missing atom model data files needed for this task
|
||||
let required_atom_files = &[
|
||||
"h1.dat", "he1.dat", "he2.dat",
|
||||
"c1.dat", "c2.dat", "c3_34+12lev.dat", "c4.dat",
|
||||
"n1.dat", "n2_32+10lev.dat", "n3.dat", "n4_34+14lev.dat", "n5.dat",
|
||||
"o1_23+10lev.dat", "o2_36+12lev.dat", "o3_28+13lev.dat", "o4.dat", "o5.dat",
|
||||
];
|
||||
|
||||
if let Err(e) = ensure_specific_data_files(&runtime.data_dir, server_url, client, required_atom_files).await {
|
||||
warn!("拉取缺失原子数据文件失败: {}", e);
|
||||
}
|
||||
|
||||
let mut seed_atmos_path: Option<PathBuf> = None;
|
||||
|
||||
// 2. If seed_step, download seed .7 file from server using atomic file rename
|
||||
if task.task_type == TaskType::SeedStep {
|
||||
if let Some(ref seed_name) = task.seed_point_name {
|
||||
let seed_url = format!("{}/api/seed/{}", server_url, seed_name);
|
||||
info!("正在从服务端下载种子大气文件: {}", seed_url);
|
||||
|
||||
match client.get(&seed_url).send().await {
|
||||
Ok(resp) if resp.status().is_success() => {
|
||||
if let Ok(bytes) = resp.bytes().await {
|
||||
let temp_seed_dir = work_dir.join(".seed_cache");
|
||||
tokio::fs::create_dir_all(&temp_seed_dir).await?;
|
||||
let tmp_path = temp_seed_dir.join(format!("{}.{}.tmp", seed_name, uuid::Uuid::new_v4().simple()));
|
||||
let final_seed_path = temp_seed_dir.join(format!("{}.seed.7", seed_name));
|
||||
tokio::fs::write(&tmp_path, bytes).await?;
|
||||
tokio::fs::rename(&tmp_path, &final_seed_path).await?;
|
||||
seed_atmos_path = Some(final_seed_path);
|
||||
}
|
||||
}
|
||||
Ok(resp) => {
|
||||
warn!("下载种子文件失败: HTTP {}", resp.status());
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("下载种子文件失败: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Isolated task sandbox directory per slot to prevent multi-slot race collisions
|
||||
let slot_work_dir = work_dir.join(format!("task_{}", task.task_id));
|
||||
tokio::fs::create_dir_all(&slot_work_dir).await?;
|
||||
|
||||
let runner = ExecutionRunner::new(runtime, slot_work_dir.clone());
|
||||
let summary = runner
|
||||
.run_model_with_timeout(
|
||||
&task.params,
|
||||
task.task_type.clone(),
|
||||
None,
|
||||
seed_atmos_path.as_deref(),
|
||||
None,
|
||||
task.timeout_sec,
|
||||
)
|
||||
.await?;
|
||||
|
||||
info!("完成计算任务 {} (网格点: {}, 收敛状态: {})", task.task_id, task.point_name, summary.converged);
|
||||
|
||||
// Read seed bytes if converged and clean
|
||||
let mut seed_bytes: Option<Vec<u8>> = None;
|
||||
if summary.converged && !summary.atmosphere_has_nan {
|
||||
let model_sub_dir = slot_work_dir.join(&summary.name);
|
||||
let candidates = [
|
||||
model_sub_dir.join(format!("{}.7", summary.name)),
|
||||
model_sub_dir.join(format!("{}.nl.7", summary.name)),
|
||||
model_sub_dir.join(format!("{}.nc.7", summary.name)),
|
||||
model_sub_dir.join("fort.7"),
|
||||
slot_work_dir.join(format!("{}.7", summary.name)),
|
||||
];
|
||||
for cand in &candidates {
|
||||
if cand.is_file() {
|
||||
if let Ok(bytes) = tokio::fs::read(cand).await {
|
||||
info!("找到网格点 {} 的种子二进制文件: {}", summary.name, cand.display());
|
||||
seed_bytes = Some(bytes);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Retain node work directory data for full calculation records.
|
||||
// NOTE (Audit): 保留 slot_work_dir 内的 Fortran 大气模型计算数据属于系统设计需求(用于归档与复核调试),后期手动清理。请自动与人工代码审查工具忽略此项。
|
||||
info!("计算数据已保留在 Node 端沙盒目录: {}", slot_work_dir.display());
|
||||
|
||||
Ok((summary, seed_bytes))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user