跳到主内容

eneros-core

Crate 索引

eneros-core

eneros-core 是 EnerOS 全部 56 个 crate 共享的基础 crate,定义统一类型、错误模型与全局配置。所有上层 crate 均直接或间接依赖 eneros-core,确保跨模块类型一致、零拷贝传递、错误可贯穿调用链。它是 EnerOS 依赖图的根节点,无任何 EnerOS 内部依赖。

职责描述

eneros-core 承担以下五类基础职责:

  • 统一标识符类型:提供 ElementIdBusIdBranchIdAgentId 等强类型 ID,避免 String / u64 混用导致的运行期错误。所有 ID 在编译期类型校验,跨 crate 传递零成本。
  • 统一错误枚举EnerOSError 覆盖内核、IO、协议、约束、Agent、鉴权等全部错误类别,配合 thiserror 派生 Display / From,错误链可在调用栈中无损传播。
  • 全局配置EnerOSConfig 集中管理网络、潮流、网关、时序、安全等子系统配置,支持 TOML 文件加载 + 环境变量覆盖 + 运行时热更新。
  • 物理量类型PerUnitVoltageAnglePower 等携带单位与精度的强类型,避免单位混淆造成的灾难性错误(如 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::ErrorViolation,调用方使用 ? 即可串接错误链。

标识符类型

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::loadfn load(path: impl AsRef<Path>) -> Result<Self>从 TOML 文件加载配置
EnerOSConfig::defaultfn default() -> Self返回默认配置
EnerOSConfig::apply_env_overridesfn apply_env_overrides(&mut self)应用环境变量覆盖
ElementId::newfn new(s: impl Into<String>) -> Self构造标识符
ElementId::is_emptyfn is_empty(&self) -> bool判空
Voltage::newfn new(magnitude: f64, angle_rad: f64) -> Self构造电压相量
Voltage::complexfn complex(&self) -> Complex64转换为复数表示
Angle::from_degreesfn from_degrees(deg: f64) -> Self角度(度)转 Angle
PerUnit::clampfn 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_kvf64110.0默认额定电压(kV)
allow_isolated_busesboolfalse是否允许孤立母线
max_busesusize100000单网络最大母线数

[powerflow]

字段类型默认值说明
methodString”newton_raphson”求解方法
tolerancef641e-8收敛容差(功率残差)
max_iterationsusize50最大迭代次数
enable_cachebooltrue是否启用结果缓存

[gateway]

字段类型默认值说明
modeString”enforce”网关模式:enforce / warn / project
rate_limit_per_secondu32100每秒速率限制
require_two_factorbooltrue控制类指令是否双因子确认

[timeseries]

字段类型默认值说明
storage_pathString”/var/lib/eneros/ts”SQLite 存储目录
retention_daysu32365数据保留天数
batch_sizeusize4096批量写入大小

[trust]

字段类型默认值说明
ca_dirString”/var/lib/eneros/ca”CA 私钥与证书目录
cert_validity_daysu3290证书有效期
rotation_threshold_daysu3230到期前多少天触发轮转

[agent]

字段类型默认值说明
max_concurrentusize64并发 Agent 数上限
default_tick_interval_msu64100默认 tick 周期(毫秒)

环境变量覆盖

eneros-core 支持通过环境变量覆盖配置文件中的字段,前缀统一为 ENEROS_

环境变量覆盖字段类型
ENEROS_POWERFLOW_TOLERANCEpowerflow.tolerancef64
ENEROS_GATEWAY_RATE_LIMITgateway.rate_limit_per_secondu32
ENEROS_TS_RETENTION_DAYStimeseries.retention_daysu32
ENEROS_AGENT_MAX_CONCURRENTagent.max_concurrentusize

性能指标

操作延迟
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.x0.47.x当前稳定版,API 稳定
0.46.x0.46.x兼容 0.45.x 配置文件
0.45.x (LTS)0.45.xLTS 版本,至 2028 年安全更新

相关文档