eneros-core
eneros-core 是 EnerOS 全部 56 个 crate 共享的基础 crate,定义统一类型、错误模型与全局配置。所有上层 crate 均直接或间接依赖 eneros-core,确保跨模块类型一致、零拷贝传递、错误可贯穿调用链。它是 EnerOS 依赖图的根节点,无任何 EnerOS 内部依赖。
职责描述
eneros-core 承担以下五类基础职责:
- 统一标识符类型:提供
ElementId、BusId、BranchId、AgentId 等强类型 ID,避免 String / u64 混用导致的运行期错误。所有 ID 在编译期类型校验,跨 crate 传递零成本。
- 统一错误枚举:
EnerOSError 覆盖内核、IO、协议、约束、Agent、鉴权等全部错误类别,配合 thiserror 派生 Display / From,错误链可在调用栈中无损传播。
- 全局配置:
EnerOSConfig 集中管理网络、潮流、网关、时序、安全等子系统配置,支持 TOML 文件加载 + 环境变量覆盖 + 运行时热更新。
- 物理量类型:
PerUnit、Voltage、Angle、Power 等携带单位与精度的强类型,避免单位混淆造成的灾难性错误(如 2003 年美加大停电根因之一)。
- 通用工具:
Duration 扩展、DateTime 别名、Arc 包装的共享句柄等基础设施。
关键类型
错误模型
use thiserror::Error;
#[derive(Debug, Error)]
pub enum EnerOSError {
#[error("invalid network: {0}")]
InvalidNetwork(String),
#[error("power flow divergence after {iterations} iterations, residual={residual:e}")]
PowerFlowDivergence { iterations: usize, residual: f64 },
#[error("constraint violation: {0:?}")]
ConstraintViolation(#[from] Violation),
#[error("protocol error: {0}")]
ProtocolError(String),
#[error("unauthorized: {0}")]
Unauthorized(String),
#[error("not found: {0}")]
NotFound(String),
#[error("io error: {0}")]
Io(#[from] std::io::Error),
#[error("internal: {0}")]
Internal(String),
}
pub type Result<T> = std::result::Result<T, EnerOSError>;
EnerOSError 实现 Send + Sync,可安全跨线程传递;通过 #[from] 自动转换 io::Error 与 Violation,调用方使用 ? 即可串接错误链。
标识符类型
use std::fmt;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct ElementId(pub String);
impl ElementId {
pub fn new(s: impl Into<String>) -> Self {
Self(s.into())
}
pub fn as_str(&self) -> &str {
&self.0
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
}
impl fmt::Display for ElementId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
pub type BusId = ElementId;
pub type BranchId = ElementId;
pub type AgentId = ElementId;
pub type TenantId = ElementId;
使用 pub struct ElementId(String) 而非裸 String,可在函数签名中表达意图,并防止将 BusId 误传给期望 BranchId 的函数。
物理量类型
use std::ops::{Add, Sub, Mul};
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct PerUnit(pub f64);
impl PerUnit {
pub const fn new(v: f64) -> Self {
Self(v)
}
pub fn clamp(&self, min: f64, max: f64) -> Self {
Self(self.0.clamp(min, max))
}
}
#[derive(Debug, Clone, Copy)]
pub struct Voltage {
pub magnitude: PerUnit,
pub angle: Angle,
}
impl Voltage {
pub fn new(magnitude: f64, angle_rad: f64) -> Self {
Self {
magnitude: PerUnit(magnitude),
angle: Angle::from_radians(angle_rad),
}
}
pub fn complex(&self) -> num_complex::Complex64 {
num_complex::Complex64::from_polar(self.magnitude.0, self.angle.as_radians())
}
}
#[derive(Debug, Clone, Copy)]
pub struct Angle(f64);
impl Angle {
pub fn from_radians(rad: f64) -> Self {
Self(rad)
}
pub fn from_degrees(deg: f64) -> Self {
Self(deg.to_radians())
}
pub fn as_radians(&self) -> f64 {
self.0
}
pub fn as_degrees(&self) -> f64 {
self.0.to_degrees()
}
}
#[derive(Debug, Clone, Copy)]
pub struct Power {
pub active_mw: f64,
pub reactive_mvar: f64,
}
母线类型
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum BusType {
PQ,
PV,
Slack,
Isolated,
}
impl BusType {
pub fn is_slack(&self) -> bool {
matches!(self, Self::Slack)
}
pub fn is_pq(&self) -> bool {
matches!(self, Self::PQ)
}
}
全局配置
use serde::{Deserialize, Serialize};
use std::path::Path;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EnerOSConfig {
pub network: NetworkConfig,
pub powerflow: PowerFlowConfig,
pub gateway: GatewayConfig,
pub timeseries: TimeSeriesConfig,
pub trust: TrustConfig,
pub agent: AgentConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NetworkConfig {
pub default_nominal_kv: f64,
pub allow_isolated_buses: bool,
pub max_buses: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PowerFlowConfig {
pub method: String,
pub tolerance: f64,
pub max_iterations: usize,
pub enable_cache: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GatewayConfig {
pub mode: String,
pub rate_limit_per_second: u32,
pub require_two_factor: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TimeSeriesConfig {
pub storage_path: String,
pub retention_days: u32,
pub batch_size: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TrustConfig {
pub ca_dir: String,
pub cert_validity_days: u32,
pub rotation_threshold_days: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentConfig {
pub max_concurrent: usize,
pub default_tick_interval_ms: u64,
}
impl Default for EnerOSConfig {
fn default() -> Self {
Self {
network: NetworkConfig {
default_nominal_kv: 110.0,
allow_isolated_buses: false,
max_buses: 100_000,
},
powerflow: PowerFlowConfig {
method: "newton_raphson".into(),
tolerance: 1e-8,
max_iterations: 50,
enable_cache: true,
},
gateway: GatewayConfig {
mode: "enforce".into(),
rate_limit_per_second: 100,
require_two_factor: true,
},
timeseries: TimeSeriesConfig {
storage_path: "/var/lib/eneros/ts".into(),
retention_days: 365,
batch_size: 4096,
},
trust: TrustConfig {
ca_dir: "/var/lib/eneros/ca".into(),
cert_validity_days: 90,
rotation_threshold_days: 30,
},
agent: AgentConfig {
max_concurrent: 64,
default_tick_interval_ms: 100,
},
}
}
}
impl EnerOSConfig {
pub fn load(path: impl AsRef<Path>) -> Result<Self> {
let text = std::fs::read_to_string(path.as_ref())?;
let mut cfg: Self = toml::from_str(&text)?;
cfg.apply_env_overrides();
Ok(cfg)
}
pub fn apply_env_overrides(&mut self) {
if let Ok(v) = std::env::var("ENEROS_POWERFLOW_TOLERANCE") {
if let Ok(t) = v.parse::<f64>() {
self.powerflow.tolerance = t;
}
}
if let Ok(v) = std::env::var("ENEROS_GATEWAY_RATE_LIMIT") {
if let Ok(r) = v.parse::<u32>() {
self.gateway.rate_limit_per_second = r;
}
}
}
}
核心 API
| 方法 | 签名 | 说明 |
|---|
EnerOSConfig::load | fn load(path: impl AsRef<Path>) -> Result<Self> | 从 TOML 文件加载配置 |
EnerOSConfig::default | fn default() -> Self | 返回默认配置 |
EnerOSConfig::apply_env_overrides | fn apply_env_overrides(&mut self) | 应用环境变量覆盖 |
ElementId::new | fn new(s: impl Into<String>) -> Self | 构造标识符 |
ElementId::is_empty | fn is_empty(&self) -> bool | 判空 |
Voltage::new | fn new(magnitude: f64, angle_rad: f64) -> Self | 构造电压相量 |
Voltage::complex | fn complex(&self) -> Complex64 | 转换为复数表示 |
Angle::from_degrees | fn from_degrees(deg: f64) -> Self | 角度(度)转 Angle |
PerUnit::clamp | fn clamp(&self, min: f64, max: f64) -> Self | 标幺值限幅 |
使用示例
加载配置并校验
use eneros_core::{EnerOSConfig, EnerOSError, ElementId, BusType};
fn main() -> Result<(), EnerOSError> {
let config = EnerOSConfig::load("eneros.toml")?;
println!("潮流方法: {}", config.powerflow.method);
println!("收敛容差: {}", config.powerflow.tolerance);
Ok(())
}
统一错误传播
use eneros_core::{EnerOSError, ElementId};
fn validate(id: &ElementId) -> Result<(), EnerOSError> {
if id.is_empty() {
return Err(EnerOSError::InvalidNetwork("empty id".into()));
}
Ok(())
}
fn find_bus(id: &ElementId) -> Result<BusType, EnerOSError> {
validate(id)?;
Ok(BusType::PQ)
}
物理量运算
use eneros_core::{Voltage, Angle, PerUnit, Power};
let v1 = Voltage::new(1.05, 0.0);
let v2 = Voltage::new(0.98, -0.12);
let diff = v1.magnitude.0 - v2.magnitude.0;
println!("电压差: {:.4} pu", diff);
let angle = Angle::from_degrees(30.0);
println!("角度: {:.4} rad / {:.4} deg", angle.as_radians(), angle.as_degrees());
let load = Power { active_mw: 50.0, reactive_mvar: 12.5 };
println!("负荷: {:.2} MW + {:.2} Mvar", load.active_mw, load.reactive_mvar);
let pu = PerUnit::new(1.08).clamp(0.95, 1.05);
println!("限幅后: {:.4}", pu.0);
配置文件示例(eneros.toml)
[network]
default_nominal_kv = 110.0
allow_isolated_buses = false
max_buses = 100000
[powerflow]
method = "newton_raphson"
tolerance = 1e-8
max_iterations = 50
enable_cache = true
[gateway]
mode = "enforce"
rate_limit_per_second = 100
require_two_factor = true
[timeseries]
storage_path = "/var/lib/eneros/ts"
retention_days = 365
batch_size = 4096
[trust]
ca_dir = "/var/lib/eneros/ca"
cert_validity_days = 90
rotation_threshold_days = 30
[agent]
max_concurrent = 64
default_tick_interval_ms = 100
配置参数表
[network] 段
| 字段 | 类型 | 默认值 | 说明 |
|---|
default_nominal_kv | f64 | 110.0 | 默认额定电压(kV) |
allow_isolated_buses | bool | false | 是否允许孤立母线 |
max_buses | usize | 100000 | 单网络最大母线数 |
[powerflow] 段
| 字段 | 类型 | 默认值 | 说明 |
|---|
method | String | ”newton_raphson” | 求解方法 |
tolerance | f64 | 1e-8 | 收敛容差(功率残差) |
max_iterations | usize | 50 | 最大迭代次数 |
enable_cache | bool | true | 是否启用结果缓存 |
[gateway] 段
| 字段 | 类型 | 默认值 | 说明 |
|---|
mode | String | ”enforce” | 网关模式:enforce / warn / project |
rate_limit_per_second | u32 | 100 | 每秒速率限制 |
require_two_factor | bool | true | 控制类指令是否双因子确认 |
[timeseries] 段
| 字段 | 类型 | 默认值 | 说明 |
|---|
storage_path | String | ”/var/lib/eneros/ts” | SQLite 存储目录 |
retention_days | u32 | 365 | 数据保留天数 |
batch_size | usize | 4096 | 批量写入大小 |
[trust] 段
| 字段 | 类型 | 默认值 | 说明 |
|---|
ca_dir | String | ”/var/lib/eneros/ca” | CA 私钥与证书目录 |
cert_validity_days | u32 | 90 | 证书有效期 |
rotation_threshold_days | u32 | 30 | 到期前多少天触发轮转 |
[agent] 段
| 字段 | 类型 | 默认值 | 说明 |
|---|
max_concurrent | usize | 64 | 并发 Agent 数上限 |
default_tick_interval_ms | u64 | 100 | 默认 tick 周期(毫秒) |
环境变量覆盖
eneros-core 支持通过环境变量覆盖配置文件中的字段,前缀统一为 ENEROS_:
| 环境变量 | 覆盖字段 | 类型 |
|---|
ENEROS_POWERFLOW_TOLERANCE | powerflow.tolerance | f64 |
ENEROS_GATEWAY_RATE_LIMIT | gateway.rate_limit_per_second | u32 |
ENEROS_TS_RETENTION_DAYS | timeseries.retention_days | u32 |
ENEROS_AGENT_MAX_CONCURRENT | agent.max_concurrent | usize |
性能指标
| 操作 | 延迟 |
|---|
EnerOSConfig::load(典型配置文件) | < 1ms |
ElementId 克隆 | < 50ns |
Voltage::complex 转换 | < 5ns |
| 错误构造(含字符串分配) | < 200ns |
依赖关系
自身依赖
[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
thiserror = "1.0"
toml = "0.8"
tracing = "0.1"
num-complex = "0.4"
chrono = { version = "0.4", features = ["serde"] }
uuid = { version = "1", features = ["v4", "serde"] }
被依赖
全部 55 个其他 crate 均直接或间接依赖 eneros-core,是 EnerOS 依赖图的根节点。典型依赖路径:
eneros-core
↓
eneros-linalg → eneros-topology → eneros-powerflow → eneros-analysis
↓ ↓
eneros-timeseries → eneros-twin eneros-constraint → eneros-gateway → eneros-api
无 EnerOS 内部依赖,可独立编译与单元测试。
版本与兼容性
| eneros-core 版本 | EnerOS 版本 | 兼容性说明 |
|---|
| 0.47.x | 0.47.x | 当前稳定版,API 稳定 |
| 0.46.x | 0.46.x | 兼容 0.45.x 配置文件 |
| 0.45.x (LTS) | 0.45.x | LTS 版本,至 2028 年安全更新 |
相关文档