开发环境搭建
本页说明如何在本地搭建 EnerOS 开发环境,配置常用 IDE 与调试工具,并遵守项目代码规范与 Git 工作流。EnerOS 是由 56 个 Rust crate 组成的 Cargo workspace,对构建环境有明确要求。
工具链
必备工具
| 工具 | 最低版本 | 推荐版本 | 说明 |
|---|---|---|---|
| Rust | 1.78+ | 1.80+ stable | 通过 rustup 安装 stable |
| cargo | 随 Rust | 随 Rust | 启用 cargo-nextest、cargo-clippy |
| rustfmt | 随 Rust | 随 Rust | 代码格式化,CI 强制校验 |
| clippy | 随 Rust | 随 Rust | 静态检查,零警告容忍 |
| Node.js | 20+ | 22 LTS | 文档站点与前端构建 |
| protoc | 25+ | 27+ | gRPC 接口生成 |
| OpenSSL | 3.0+ | 3.2+ | TLS 依赖 |
| SQLite | 3.40+ | 3.45+ | 内核存储 |
| CMake | 3.20+ | 3.28+ | 部分 C 依赖构建 |
| Git | 2.30+ | 2.45+ | 版本控制 |
安装命令
# 安装 Rust 稳定版工具链
rustup default stable
# 添加必备组件
rustup component add clippy rustfmt rust-src rust-analyzer
# 安装 cargo 子命令
cargo install cargo-nextest
cargo install cargo-deny
cargo install cargo-llvm-cov
cargo install cargo-audit
cargo install cargo-edit # 提供 cargo add / cargo upgrade
cargo install cargo-watch # 文件变化时自动重编译/重测试
cargo install cargo-flamegraph # 性能火焰图(Linux)
# 验证
rustc --version
cargo --version
cargo nextest --version
系统依赖
Ubuntu / Debian
sudo apt update
sudo apt install -y \
build-essential \
pkg-config \
libssl-dev \
libsqlite3-dev \
cmake \
git \
curl \
clang \
libclang-dev \
protobuf-compiler
macOS
xcode-select --install
brew install openssl@3 sqlite pkg-config cmake protobuf
Windows
Windows 推荐使用 MSVC 工具链,安装 Visual Studio Build Tools 后:
choco install openssl pkgconfiglite cmake git protobuf -y
详细系统依赖安装见 环境准备。
克隆与构建
克隆仓库
# Fork 后克隆(推荐)
git clone https://github.com/<your-fork>/EnerOS.git
cd EnerOS
# 配置上游仓库
git remote add upstream https://github.com/Gawg-AI/EnerOS.git
git fetch upstream
首次构建
# Debug 构建(默认)
cargo build
# Release 构建(性能更优,编译更慢)
cargo build --release
# 仅构建特定 crate
cargo build -p eneros-powerflow
# 构建特定二进制
cargo build -p enerosctl
# 构建带特性的 crate
cargo build -p eneros-audit --features worm
cargo build -p eneros-init --features os
构建产物位于 target/debug/ 或 target/release/。完整 Release 构建见 安装与构建。
加速构建
EnerOS workspace 较大,可通过以下方式加速构建:
# ~/.cargo/config.toml
[build]
# 限制并行度避免 OOM(4GB 内存机器建议 2)
jobs = 4
# 启用 mold 链接器(Linux,需先安装 mold)
[target.x86_64-unknown-linux-gnu]
linker = "clang"
rustflags = ["-C", "link-arg=-fuse-ld=mold"]
# 国内镜像源加速依赖下载
[source.crates-io]
replace-with = "ustc"
[source.ustc]
registry = "sparse+https://mirrors.ustc.edu.cn/crates.io-index/"
# 启用 sccache 缓存编译结果(需先 cargo install sccache)
[build]
rustc-wrapper = "sccache"
# 安装 sccache
cargo install sccache
# 设置缓存大小(默认 10GB)
export SCCACHE_CACHE_SIZE="20G"
sccache --start-server
使用 cargo-watch
开发时可用 cargo-watch 自动重编译:
# 监听文件变化并自动运行测试
cargo watch -x "nextest run -p eneros-powerflow"
# 监听变化并自动 clippy
cargo watch -x "clippy --all-targets -- -D warnings"
# 监听变化并运行 + 测试
cargo watch -x run -x "nextest run"
IDE 配置
VS Code
推荐安装以下扩展:
| 扩展 | 用途 | 必需 |
|---|---|---|
| rust-analyzer | Rust LSP 语言服务器 | 是 |
| Even Better TOML | TOML 文件编辑(Cargo.toml、eneros.toml) | 是 |
| CodeLLDB | Rust 调试器 | 是 |
| crates | 依赖版本提示 | 推荐 |
| Better Comments | 注释高亮 | 可选 |
| Markdown All in One | 文档编辑 | 可选 |
推荐配置
在仓库根目录创建 .vscode/settings.json(已提交到仓库):
{
"rust-analyzer.linkedProjects": ["./Cargo.toml"],
"rust-analyzer.checkOnSave": true,
"rust-analyzer.check.command": "clippy",
"rust-analyzer.check.extraArgs": ["--all-targets", "--", "-D", "warnings"],
"rust-analyzer.cargo.features": "all",
"rust-analyzer.inlayHints.parameterHints.enable": true,
"rust-analyzer.inlayHints.typeHints.enable": true,
"rust-analyzer.inlayHints.lifetimeElisionHints.enable": "always",
"rust-analyzer.rustfmt.overrideCommand": ["rustfmt", "--edition", "2021"],
"editor.formatOnSave": true,
"editor.defaultFormatter": "rust-lang.rust-analyzer",
"[rust]": {
"editor.defaultFormatter": "rust-lang.rust-analyzer",
"editor.formatOnSave": true
},
"[toml]": {
"editor.defaultFormatter": "tamasfe.even-better-toml",
"editor.formatOnSave": true
},
"files.exclude": {
"**/target": true,
"**/.cargo-lock": false
},
"search.exclude": {
"**/target": true,
"**/Cargo.lock": true
},
"lldb.executable": "rust-lldb"
}
调试配置
在 .vscode/launch.json 中添加:
{
"version": "0.2.0",
"configurations": [
{
"type": "lldb",
"request": "launch",
"name": "Debug unit tests",
"cargo": {
"args": ["test", "--no-run", "--lib", "--all-features"],
"filter": {
"kind": "lib"
}
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug powerflow tests",
"cargo": {
"args": ["test", "--no-run", "-p", "eneros-powerflow"],
"filter": {
"name": "powerflow",
"kind": "lib"
}
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Run eneros-api",
"cargo": {
"args": ["build", "-p", "eneros-api"]
},
"program": "${workspaceFolder}/target/debug/eneros-api",
"args": ["--config", "eneros.toml"],
"cwd": "${workspaceFolder}"
}
]
}
IntelliJ Rust
IntelliJ IDEA / CLion 用户可使用 Rust 插件:
- 打开
Settings → Plugins → Marketplace,搜索Rust并安装 - 打开
Settings → Languages & Frameworks → Rust:- Toolchain location:
~/.cargo/bin或%USERPROFILE%\.cargo\bin - Explicit path to stdlib:留空(自动检测)
- Use rustfmt:勾选
- Use external linter:选择
Clippy
- Toolchain location:
- 打开
Settings → Languages & Frameworks → Rust → Cargo:- Auto-update project:勾选
- Build on save:勾选
CLion 由于内置 LLDB,可直接在代码行号旁点击设置断点并调试。
Neovim
Neovim 用户推荐使用 nvim-lspconfig 配置 rust-analyzer:
-- init.lua
require'lspconfig'.rust_analyzer.setup{
settings = {
["rust-analyzer"] = {
cargo = { features = "all", allFeatures = true },
checkOnSave = {
command = "clippy",
extraArgs = {"--all-targets", "--", "-D", "warnings"}
},
inlayHints = { parameterHints = { enable = true } }
}
}
}
调试配置
日志输出
EnerOS 使用 tracing 进行结构化日志输出,可通过环境变量控制日志级别:
# 全局日志级别
export RUST_LOG=info
# 按 crate 控制日志级别
export RUST_LOG="eneros_powerflow=debug,eneros_topology=trace,info"
# JSON 格式输出(用于日志采集)
export ENEROS_LOG_FORMAT=json
# 运行时输出
RUST_LOG=debug cargo run -p eneros-api
LLDB / GDB 调试
# 使用 rust-lldb(推荐)
cargo build -p eneros-powerflow
rust-lldb --target ./target/debug/eneros-powerflow-test -- --nocapture
# 常用 LLDB 命令:
# (lldb) b powerflow::newton_raphson::solve # 设置断点
# (lldb) run # 运行
# (lldb) next # 单步跳过
# (lldb) step # 单步进入
# (lldb) print *bus # 打印变量
# (lldb) bt # 打印调用栈
性能分析
# 火焰图(需安装 cargo-flamegraph)
cargo flamegraph --bin eneros-api -- --config eneros.toml
# 性能基准对比
cargo bench -p eneros-powerflow -- --save-baseline my-change
# 修改代码后
cargo bench -p eneros-powerflow -- --baseline my-change
# 内存分配追踪(Linux,需 dhat)
cargo run -p eneros-powerflow --features dhat --release
代码规范
格式化与 Lint
# 格式化(CI 强制校验)
cargo fmt --all
# 检查格式(不修改)
cargo fmt --all -- --check
# Lint(CI 强制校验,零警告容忍)
cargo clippy --all-targets -- -D warnings
# 仅检查特定 crate
cargo clippy -p eneros-powerflow -- -D warnings
# 修复自动可修复的 clippy 警告
cargo clippy --fix --all-targets --allow-dirty --allow-no-vcs
命名规范
| 对象 | 规范 | 示例 |
|---|---|---|
| Crate | eneros-<domain>,kebab-case | eneros-powerflow |
| 模块 | snake_case | mod newton_raphson; |
| 类型 | UpperCamelCase,名词 | PowerflowSolver |
| Trait | UpperCamelCase,名词或形容词 | TopologyEngine、Auditable |
| 函数 | snake_case,动词开头 | find_islands、solve_powerflow |
| 方法 | snake_case,动词开头 | bus.add_load(...) |
| 常量 | SCREAMING_SNAKE_CASE | MAX_HISTORY、DEFAULT_PORT |
| 静态变量 | SCREAMING_SNAKE_CASE | GLOBAL_CONFIG |
| 局部变量 | snake_case | bus_count、total_load |
| 泛型参数 | 单大写字母或 UpperCamelCase | T、AgentKind |
| 生命周期 | 短小写字母 | 'a、'ctx |
错误处理
- 使用
Result<T, EnerOSError>,禁止unwrap进入生产代码(测试除外) - 错误类型实现
thiserror::Error,提供#[error("...")]文本 - 上层错误用
anyhow::Result或自定义Error枚举聚合 ?操作符附加上下文:map_err(|e| EnerOSError::network("connect", e))
// crates/eneros-powerflow/src/error.rs
use thiserror::Error;
#[derive(Debug, Error)]
pub enum PowerflowError {
#[error("潮流计算不收敛,迭代次数 {iterations} 超过上限 {max_iterations}")]
NonConvergence { iterations: usize, max_iterations: usize },
#[error("母线 {bus_id} 电压越限:{voltage} p.u.,允许范围 [{v_min}, {v_max}]")]
VoltageViolation {
bus_id: u64,
voltage: f64,
v_min: f64,
v_max: f64,
},
#[error("拓扑数据无效:{0}")]
InvalidTopology(String),
#[error("线性代数计算失败:{0}")]
Linalg(#[from] eneros_linalg::LinalgError),
}
pub type Result<T> = std::result::Result<T, PowerflowError>;
异步规范
- 统一使用
tokioruntime,避免阻塞调用混入异步上下文 - 长时间 CPU 计算使用
tokio::task::spawn_blocking - 异步函数返回
impl Future<Output = Result<T>>或Pin<Box<dyn Future...>> - 禁止在 async 块中调用
std::sync::Mutex的lock().unwrap()
// 正确:异步 + spawn_blocking 处理 CPU 密集任务
pub async fn solve_powerflow_async(&self) -> Result<PowerflowResult> {
let topology = self.topology.clone();
let result = tokio::task::spawn_blocking(move || {
Self::solve_blocking(&topology)
}).await.map_err(|e| PowerflowError::Linalg(eneros_linalg::LinalgError::Other(e.to_string())))?;
Ok(result)
}
// 错误:阻塞调用混入异步上下文
pub async fn bad_solve(&self) -> Result<PowerflowResult> {
std::thread::sleep(Duration::from_secs(1)); // 禁止!
self.solve_blocking()
}
公共 API 文档
- 公共 API 必须有文档注释
/// - 通过
cargo doc校验,文档示例/// ```rust必须可编译 - 复杂 API 提供
# Examples章节
/// 求解给定拓扑的潮流方程。
///
/// 使用牛顿-拉夫逊法迭代求解,最大迭代次数由 `max_iterations` 控制。
/// 当相邻两次迭代电压差小于 `tolerance` 时认为收敛。
///
/// # 参数
/// - `topology`:电网拓扑,须已完成孤岛检测
/// - `max_iterations`:最大迭代次数,建议 20-50
/// - `tolerance`:收敛容差,默认 1e-8
///
/// # 返回
/// 返回 [`PowerflowResult`],包含每个母线的电压、相角与支路功率。
///
/// # 错误
/// - [`PowerflowError::NonConvergence`]:迭代次数超限仍未收敛
/// - [`PowerflowError::InvalidTopology`]:拓扑数据不完整或存在环
///
/// # Examples
///
/// ```rust
/// use eneros_powerflow::{NewtonRaphsonSolver, PowerflowResult};
/// use eneros_topology::Topology;
///
/// let topo = Topology::from_ieee_14bus();
/// let solver = NewtonRaphsonSolver::new(20, 1e-8);
/// let result: PowerflowResult = solver.solve(&topo).unwrap();
/// assert!(result.converged);
/// ```
pub fn solve(&self, topology: &Topology) -> Result<PowerflowResult> {
// 实现
unimplemented!()
}
提交信息规范
Conventional Commits
EnerOS 采用 Conventional Commits 1.0.0 规范:
<type>(<scope>): <description>
[optional body]
[optional footer(s)]
| 字段 | 说明 |
|---|---|
type | 提交类型,见下表 |
scope | 影响范围,通常为 crate 名(不含 eneros- 前缀),如 powerflow、topology |
description | 简短描述,祈使句,首字母小写,结尾不加句号 |
body | 详细说明,每行 ≤ 72 字符 |
footer | 关联 Issue(Closes #123)、破坏性变更标记(BREAKING CHANGE:) |
提交类型
| 类型 | 用途 | 示例 |
|---|---|---|
feat | 新功能 | feat(powerflow): 支持 PV 母线无功上下限 |
fix | Bug 修复 | fix(topology): 修复孤岛检测漏判 |
docs | 文档变更 | docs(tutorials): 新增潮流计算教程 |
style | 代码风格(不影响逻辑) | style(core): 统一使用 4 空格缩进 |
refactor | 重构(非新功能、非 Bug 修复) | refactor(topology): 抽取图遍历公共方法 |
perf | 性能优化 | perf(timeseries): 优化写入路径减少内存拷贝 |
test | 测试相关 | test(constraint): 补充电压越限边界用例 |
build | 构建系统或依赖 | build(deny): 升级 cargo-deny 至 0.14 |
ci | CI 配置 | ci(workflows): 新增 aarch64 构建矩阵 |
chore | 杂项(不影响源码或测试) | chore: 更新 .gitignore |
revert | 回滚之前的提交 | revert: 回滚 feat(powerflow): PV 无功限制 |
完整示例
feat(powerflow): 支持 PV 母线无功上下限
- 在 PvBus 中新增 q_min/q_max 字段,默认 -inf/+inf 保持向后兼容
- NewtonRaphsonSolver 在无功越限时自动将 PV 节点转为 PQ 节点
- 新增 bus_type_convertor 模块处理节点类型转换逻辑
- 补充 5 个单元测试覆盖越限场景
Closes #123
fix(topology): 修复孤岛检测在多源节点的漏判问题
孤立子图搜索时未考虑多源节点的合并,导致部分工业配网场景下
将同一个电气孤岛误判为两个。
修复方案:在 BFS 遍历前对源节点进行合并去重。
Closes #456
验证提交信息
# 安装 commitlint 工具
cargo install commitlint
# 在 commit-msg 钩子中校验
echo '#!/bin/sh
cargo commitlint --commit-msg-file "$1"' > .git/hooks/commit-msg
chmod +x .git/hooks/commit-msg
分支策略与 Git 工作流
分支模型
EnerOS 采用简化的 Git Flow 模型:
| 分支 | 用途 | 命名 | 生命周期 |
|---|---|---|---|
main | 主干,始终可发布 | main | 永久 |
release/* | 发布分支,仅用于 bug 修复 | release/0.47.0 | 发布后归档 |
feat/* | 功能开发 | feat/powerflow-pv-limits | 合并后删除 |
fix/* | Bug 修复 | fix/123-island-detection | 合并后删除 |
docs/* | 文档 | docs/tutorial-load-flow | 合并后删除 |
hotfix/* | 紧急修复(针对已发布版本) | hotfix/0.47.1-cve-2024-xxxx | 合并后删除 |
工作流
main ─────●───●───●───●───●────────────────●─── (合并 PR)
\ \ /
└─ feat/foo ──●───●─────●─── PR ──/
\ \ \
提交/测试/Review
1. 同步主干
# 始终基于最新 main 切分支
git checkout main
git pull upstream main
git checkout -b feat/my-feature
2. 开发与提交
# 小步提交,每个 commit 独立可编译
git add crates/eneros-powerflow/src/solver.rs
git commit -m "feat(powerflow): 实现 PV 转 PQ 节点逻辑"
git add crates/eneros-powerflow/src/bus_type.rs
git commit -m "feat(powerflow): 抽取节点类型转换器"
git add crates/eneros-powerflow/tests/pv_limits_test.rs
git commit -m "test(powerflow): 补充 PV 无功越限测试"
3. 保持线性历史
# 定期 rebase 至最新 main
git fetch upstream
git rebase upstream/main
# 解决冲突后继续
git add .
git rebase --continue
# 强制推送至个人 fork(使用 --force-with-lease 更安全)
git push origin feat/my-feature --force-with-lease
4. 处理 Review 反馈
# Reviewer 提出修改建议,修改后:
git add .
git commit --fixup <commit-sha> # 创建 fixup 提交
git rebase -i --autosquash upstream/main
# 或直接修改最后一个 commit
git add .
git commit --amend --no-edit
git push origin feat/my-feature --force-with-lease
处理大型 PR
单一 PR 控制在 800 行以内,复杂改动拆分为多个 PR:
# 拆分策略:先合并基础设施 PR,再合并功能 PR
# PR #1: refactor(powerflow): 抽取节点类型转换器
# PR #2: feat(powerflow): 支持 PV 母线无功上下限(依赖 #1)
# PR #3: docs(powerflow): 补充 PV 节点教程(依赖 #2)
在 PR 描述中标注依赖关系:Depends on: #123。
常见问题
链接错误
参考 安装与构建 - 常见构建问题。常见错误:
| 错误信息 | 原因 | 解决方案 |
|---|---|---|
linker 'cc' not found | 缺少 C 编译器 | Ubuntu: sudo apt install build-essential |
failed to run custom build command for 'openssl-sys' | OpenSSL 未找到 | 设置 OPENSSL_DIR 或安装 libssl-dev |
could not find library 'sqlite3' | SQLite 开发库缺失 | Ubuntu: sudo apt install libsqlite3-dev |
linking with 'link.exe' failed (Windows) | MSVC 缺失 | 安装 Visual Studio Build Tools |
protobuf 版本不符
# 检查版本,须 ≥ 25
protoc --version
# libprotoc 27.0
# 升级
sudo apt install -y protobuf-compiler # Ubuntu
brew upgrade protobuf # macOS
choco upgrade protobuf # Windows
大型 PR 拆分
单一 PR 控制在 800 行以内,复杂改动拆为多个:
- 基础设施 PR:抽取公共 trait、类型与工具函数
- 核心实现 PR:实现主要逻辑,依赖基础设施 PR
- 测试 PR:补充完整测试用例
- 文档 PR:更新 API 参考、教程与 CHANGELOG
每个 PR 在描述中标注依赖关系,按顺序合并。
cargo build 时内存不足
# 限制并行度
export CARGO_BUILD_JOBS=2
cargo build
# 或写入配置
# ~/.cargo/config.toml
[build]
jobs = 2
开发检查清单
每次提交 PR 前,请逐项检查:
-
cargo fmt --all -- --check通过 -
cargo clippy --all-targets -- -D warnings通过 -
cargo nextest run全部通过 -
cargo nextest run -p <my-crate>全部通过 - 公共 API 变更已更新
cargo doc - 提交信息符合 Conventional Commits
- 不包含密钥、证书、临时文件等敏感内容
- 不包含
.env、*.pem、*.key等敏感文件 -
cargo deny check通过(无禁用许可或重复依赖) - 已更新
CHANGELOG.md(如涉及行为变更)