开放互操作
Open & Interoperable 是 EnerOS 的生态理念:基于国际电力行业标准协议,提供多种 API 类型与数据交换格式,确保与现有 SCADA、EMS、DMS、配网自动化系统的无缝互操作。EnerOS 不锁定用户,所有数据可导出为标准格式,所有接口遵循开放规范。
设计动机
电力系统的协议碎片化问题
电力系统历经数十年发展,存在大量异构协议与系统:
| 系统 | 主要协议 | 数据格式 | 通信模式 |
|---|---|---|---|
| 调度自动化(EMS) | IEC 60870-5-104 | 私有 | 主从 |
| 变电站自动化 | IEC 61850 | MMS/GOOSE | 发布订阅 |
| 配电自动化(DMS) | IEC 60870-5-104 / DNP3 | 私有 | 主从 |
| 远动 RTU | IEC 60870-5-101/104 | 私有 | 主从 |
| 智能电表 | DL/T 698.45 / Modbus | 私有 | 主从 |
| 电网模型 | IEC 61970 (CIM) | XML/RDF | 文件 |
| 配网模型 | IEC 61968 (CIM) | XML/JSON | 文件 |
| 风电/光伏 | IEC 61400-25 | MMS | 发布订阅 |
| 物联网设备 | MQTT / CoAP | JSON | 发布订阅 |
传统方案需要为每个协议开发专门适配器,集成成本高、维护困难。
开放互操作的解决思路
EnerOS 内置主流电力协议适配器,统一对外接口:
┌─────────────────────────────────────────────────────────┐
│ 外部系统 │
│ SCADA │ EMS │ DMS │ IoT │ 电表 │ 风电场 │
└────┬───────┬───────┬───────┬───────┬───────┬────────────┘
│ │ │ │ │ │
▼ ▼ ▼ ▼ ▼ ▼
┌─────────────────────────────────────────────────────────┐
│ EnerOS 协议适配层 (Protocol Layer) │
│ IEC104 │ IEC61850 │ DNP3 │ Modbus │ MQTT │ DL/T698 │
└──────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 统一数据模型 (Unified Data Model) │
│ 基于 IEC 61970/61968 CIM 标准化 │
└──────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ EnerOS 电力原生内核 │
└─────────────────────────────────────────────────────────┘
支持的协议
协议总览
| 协议 | 标准编号 | 适用场景 | 通信模式 | Crate |
|---|---|---|---|---|
| IEC 61850 | 变电站自动化 | 变电站设备 | MMS/GOOSE/SV | eneros-protocol-iec61850 |
| IEC 60870-5-104 | 远动协议 | 调度主站-变电站 | 主从 | eneros-protocol-iec104 |
| IEC 60870-5-101 | 远动协议(串口) | 旧式 RTU | 主从 | eneros-protocol-iec101 |
| IEC 61970 (CIM) | 电网模型 | EMS 模型交换 | 文件 | eneros-protocol-cim |
| IEC 61968 (CIM) | 配网模型 | DMS 模型交换 | 文件 | eneros-protocol-cim |
| DNP3 | 配网自动化 | 北美配网 | 主从 | eneros-protocol-dnp3 |
| Modbus | 工业设备 | 通用工业设备 | 主从 | eneros-protocol-modbus |
| IEC 61400-25 | 风电场 | 风机监控 | MMS | eneros-protocol-iec61400 |
| DL/T 698.45 | 智能电表 | 中国电表 | 主从 | eneros-protocol-dlt698 |
| MQTT 5.0 | 物联网 | IoT 设备 | 发布订阅 | eneros-protocol-mqtt |
| OPC UA | 工业统一架构 | 通用工业 | 客户端-服务器 | eneros-protocol-opcua |
IEC 61850
电力变电站自动化国际标准,支持 MMS、GOOSE、SV 三种通信模式:
use eneros_protocol_iec61850::{IedClient, DataObject, Quality};
// 连接 IED(智能电子设备)
let mut client = IedClient::connect("192.168.1.100:102")?
.authenticate("admin", "password")?
.timeout(std::time::Duration::from_secs(5));
// 读取测量值
let voltage = client.read("LD0/LLN0.Vol.sv").await?;
println!("电压: {} kV, 质量: {:?}", voltage.value, voltage.quality);
// 读取双位置信号(断路器状态)
let breaker_pos = client.read("LD0/XCBR1.Pos.stVal").await?;
println!("断路器状态: {:?}", breaker_pos.value);
// 控制断路器
client.control("LD0/XCBR1.Pos.Oper", ControlAction::Close).await?;
// 订阅 GOOSE 事件
client.subscribe_goose("LD0/LLN0.GOCB1", |event| {
println!("GOOSE 事件: {:?}", event);
}).await?;
IEC 60870-5-104
调度控制中心与变电站之间的远动协议:
use eneros_protocol_iec104::{Iec104Master, AsduType, CauseOfTransmission};
// 创建主站
let mut master = Iec104Master::connect("10.0.0.1:2404")?
.common_address_size(2)
.io_address_size(3)
.timeout(t1: 15s, t2: 10s, t3: 20s);
// 总召唤(读取全部数据)
master.interrogation(AsduType::C_IC_NA_1, 1, CauseOfTransmission::Activation).await?;
// 单点遥控
master.send_command(AsduType::C_SC_NA_1, 1, true).await?;
// 双点遥控(合/分)
master.send_command(AsduType::C_DC_NA_1, 1, DoubleCommand::Close).await?;
// 设点命令(设定值)
master.set_point(AsduType::C_SE_NA_1, 1, 80.0).await?;
// 监听遥测变化
master.on_measurement(|asdu| {
println!("类型: {:?}, 地址: {}, 值: {:?}",
asdu.type_id, asdu.ioa, asdu.values);
}).await?;
DNP3
北美配网自动化标准协议:
use eneros_protocol_dnp3::{Dnp3Master, Command, PointType};
let mut master = Dnp3Master::connect_tcp("10.0.0.2:20000")?
.master_address(1)
.timeout(std::time::Duration::from_secs(5));
// 读取模拟量
let analog = master.read_analog_input(1, 10).await?;
let voltage = analog[0].value;
// 读取数字量
let binary = master.read_binary_input(1, 5).await?;
// 控制
master.control(Command::Operate, PointType::BinaryOutput, 1, true).await?;
Modbus
工业设备通用协议:
use eneros_protocol_modbus::{ModbusClient, Register};
// TCP 连接
let mut client = ModbusClient::connect_tcp("192.168.1.50:502")?;
// RTU 串口连接
let mut rtu_client = ModbusClient::connect_rtu("/dev/ttyUSB0", 9600)?;
// 读取保持寄存器
let registers = client.read_holding_registers(0, 10).await?;
let voltage = f32::from_bits(registers[0] as u32 | (registers[1] as u32) << 16);
// 写入多个寄存器
client.write_multiple_registers(10, &[0x1234, 0x5678]).await?;
// 读取线圈状态
let coils = client.read_coils(0, 16).await?;
DL/T 698.45
中国智能电表通信协议:
use eneros_protocol_dlt698::Dlt698Client;
let mut client = Dlt698Client::connect("/dev/ttyUSB0")?
.baud_rate(2400)
.address("00-00-00-00");
// 读取电表读数
let reading = client.read_meter("00-00-00-00").await?;
println!("当前有功电能: {} kWh", reading.active_energy);
println!("当前无功电能: {} kvarh", reading.reactive_energy);
println!("电压: {} V", reading.voltage);
println!("电流: {} A", reading.current);
// 读取历史数据
let history = client.read_history(
"00-00-00-00",
now!() - Duration::days(1),
now!(),
).await?;
IEC 61970 / 61968 (CIM)
电网模型交换标准,支持导入导出 CIM/XML、CIM/JSON:
use eneros_protocol_cim::{CimImporter, CimExporter, CimFormat};
// 导入 CIM 模型
let importer = CimImporter::new()
.format(CimFormat::Xml)
.validate_schema(true);
let network = importer.import_file("data/ieee14_cim.xml")?;
println!("导入母线数: {}", network.bus_count());
println!("导入支路数: {}", network.branch_count());
// 导出为 CIM/JSON
let exporter = CimExporter::new()
.format(CimFormat::Json)
.profile(CimProfile::Equipment);
let json = exporter.export_to_string(&network)?;
std::fs::write("data/network_cim.json", json)?;
MQTT 5.0
物联网设备通信协议,EnerOS 内置 MQTT broker:
# EnerOS 内置 MQTT broker,默认端口 1883
enerosctl mqtt status
enerosctl mqtt subscribe "grid/+/voltage"
enerosctl mqtt publish "grid/bus_1/voltage" "1.024"
use eneros_protocol_mqtt::{MqttClient, QoS};
let mut client = MqttClient::connect("mqtt://localhost:1883")?
.client_id("eneros-agent-1")
.username("agent")
.password("secret");
// 订阅主题
client.subscribe("grid/+/voltage", QoS::AtLeastOnce).await?;
// 发布消息
client.publish("grid/bus_1/voltage", "1.024", QoS::AtLeastOnce).await?;
// 接收消息
client.on_message(|topic, payload| {
println!("主题: {}, 负载: {}", topic, String::from_utf8_lossy(payload));
}).await?;
开放 API
API 类型对比
EnerOS 同时支持四种 API 类型,适用不同场景:
| API 类型 | 协议 | 数据格式 | 通信模式 | 适用场景 | 性能 |
|---|---|---|---|---|---|
| REST | HTTP/1.1, HTTP/2 | JSON | 请求-响应 | 管理操作、查询 | 中 |
| GraphQL | HTTP | JSON | 请求-响应 | 灵活查询 | 中 |
| WebSocket | WS | JSON/二进制 | 双向流 | 实时交互 | 高 |
| SSE | HTTP | 文本流 | 服务器推送 | 事件订阅 | 高 |
| gRPC | HTTP/2 | Protobuf | 双向流 | 高性能 RPC | 极高 |
REST API
REST API 适用于管理操作和简单查询:
# 查询网络列表
curl http://localhost:8080/api/v1/networks
# 查询特定网络
curl http://localhost:8080/api/v1/networks/ieee14
# 创建 Agent
curl -X POST http://localhost:8080/api/v1/agents \
-H "Content-Type: application/json" \
-d '{
"type": "dispatch",
"name": "dispatch-east-1",
"binding": {
"node_id": 1,
"permissions": ["read", "dispatch"]
}
}'
# 查询时序数据
curl "http://localhost:8080/api/v1/timeseries/bus_1_voltage?start=2026-07-05T00:00:00Z&end=2026-07-06T00:00:00Z&aggregation=avg&interval=15m"
# 控制设备
curl -X POST http://localhost:8080/api/v1/devices/breaker_1/control \
-H "Content-Type: application/json" \
-d '{"action": "open"}'
REST 响应结构示例:
{
"code": 0,
"message": "success",
"data": {
"network": {
"id": "ieee14",
"name": "IEEE 14-bus",
"bus_count": 14,
"branch_count": 20,
"buses": [
{"id": 1, "type": "slack", "voltage_pu": 1.06, "angle_deg": 0.0},
{"id": 2, "type": "pv", "voltage_pu": 1.045, "angle_deg": -4.98}
]
}
},
"timestamp": "2026-07-06T10:30:00Z"
}
GraphQL
GraphQL 适用于灵活查询,客户端可按需获取字段:
query {
network(id: "ieee14") {
id
name
buses {
id
type
voltage {
magnitude_pu
angle_deg
}
load {
p_mw
q_mvar
}
}
branches {
id
from
to
power {
p_mw
q_mvar
}
}
}
}
// Rust 客户端调用 GraphQL
use eneros_client::GraphQLClient;
let client = GraphQLClient::new("http://localhost:8080/graphql")?;
let query = r#"
query NetworkState($id: ID!) {
network(id: $id) {
buses { id voltage { magnitude_pu } }
}
}
"#;
let result: NetworkResponse = client.query(query, json!({"id": "ieee14"})).await?;
WebSocket
WebSocket 适用于实时双向通信:
// 浏览器端 JavaScript
const ws = new WebSocket('ws://localhost:8080/ws/events');
ws.onopen = () => {
ws.send(JSON.stringify({
type: 'subscribe',
topics: ['grid.voltage', 'grid.frequency', 'agent.status']
}));
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('事件:', data);
switch (data.type) {
case 'voltage_change':
updateVoltageChart(data.bus_id, data.value);
break;
case 'fault':
showAlert('故障告警: ' + data.message);
break;
}
};
// Rust 客户端
use eneros_client::WebSocketClient;
let mut ws = WebSocketClient::connect("ws://localhost:8080/ws/events").await?;
ws.subscribe(vec!["grid.voltage", "grid.frequency"]).await?;
while let Some(event) = ws.recv().await {
println!("事件: {:?}", event);
}
SSE (Server-Sent Events)
SSE 适用于服务器推送的事件订阅:
const eventSource = new EventSource('http://localhost:8080/api/v1/events');
eventSource.addEventListener('voltage', (e) => {
const data = JSON.parse(e.data);
console.log('电压事件:', data);
});
eventSource.addEventListener('fault', (e) => {
const data = JSON.parse(e.data);
console.log('故障事件:', data);
});
gRPC
gRPC 适用于高性能 RPC 场景:
// proto/eneros.proto
syntax = "proto3";
service PowerFlowService {
rpc Solve(SolveRequest) returns (SolveResponse);
rpc StreamSolve(stream SolveRequest) returns (stream SolveResponse);
}
message SolveRequest {
string network_id = 1;
string method = 2;
uint32 max_iter = 3;
double tolerance = 4;
}
message SolveResponse {
bool converged = 1;
uint32 iterations = 2;
repeated BusResult buses = 3;
}
use eneros_client::grpc::PowerFlowClient;
let mut client = PowerFlowClient::connect("http://localhost:50051").await?;
let response = client.solve(tonic::Request::new(SolveRequest {
network_id: "ieee14".into(),
method: "newton_raphson".into(),
max_iter: 20,
tolerance: 1e-6,
})).await?;
println!("收敛: {}, 迭代次数: {}",
response.get_ref().converged,
response.get_ref().iterations);
数据交换格式
支持的格式
| 格式 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| JSON | API 通用 | 通用性强 | 体积大 |
| XML | CIM 模型 | 标准兼容 | 冗长 |
| Protobuf | gRPC | 紧凑高效 | 需 schema |
| CSV | 数据导出 | 易读易处理 | 无类型 |
| Parquet | 大数据 | 列式压缩 | 二进制 |
| CBOR | 物联网 | 紧凑二进制 | 工具少 |
use eneros_protocol::{Format, Exporter};
// 导出为不同格式
let exporter = Exporter::new(&network);
// JSON
exporter.to_json("data/network.json")?;
// CIM/XML
exporter.to_cim_xml("data/network_cim.xml")?;
// CSV
exporter.to_csv("data/network.csv")?;
// Parquet
exporter.to_parquet("data/network.parquet")?;
// 导入
let network = Importer::from_json("data/network.json")?;
let network = Importer::from_cim_xml("data/network_cim.xml")?;
与 SCADA/EMS/DMS 集成
与 SCADA 集成
use eneros_protocol_iec104::Iec104Server;
// EnerOS 作为 104 服务器,被 SCADA 主站轮询
let server = Iec104Server::bind("0.0.0.0:2404")?;
server.on_interrogation(move |common_addr| {
// 返回当前所有遥测/遥信数据
let measurements = network.get_all_measurements();
measurements
}).await?;
server.on_command(move |asdu| {
// 接收 SCADA 下发的控制命令
let command = translate_asdu_to_command(asdu);
gateway.execute(command).await
}).await?;
server.start().await?;
与 EMS 集成
use eneros_protocol_cim::{CimImporter, CimExporter};
// 从 EMS 导入电网模型(CIM/XML)
let importer = CimImporter::new()
.format(CimFormat::Xml)
.profile(CimProfile::Equipment)
.validate_schema(true);
let network = importer.import_file("ems_export.xml")?;
// 将 EnerOS 状态导出回 EMS
let exporter = CimExporter::new()
.format(CimFormat::Xml)
.profile(CimProfile::StateVariables);
let state_xml = exporter.export_to_string(&network)?;
// 推送回 EMS
ems_client.upload_state(state_xml).await?;
与 DMS 集成
// 与配网自动化系统集成
use eneros_protocol_dnp3::Dnp3Server;
let server = Dnp3Server::bind_tcp("0.0.0.0:20000")?;
server.on_read_analog(move |start, count| {
// DMS 读取模拟量
network.read_analog_range(start, count)
}).await?;
server.on_read_binary(move |start, count| {
// DMS 读取数字量
network.read_binary_range(start, count)
}).await?;
server.on_control(move |command| {
// DMS 下发控制命令
let cmd = translate_dnp3_command(command);
gateway.execute(cmd).await
}).await?;
server.start().await?;
插件框架
EnerOS 支持自定义插件扩展协议适配:
use eneros_plugin::{ProtocolAdapter, PluginContext, PluginResult};
#[eneros_plugin]
pub struct MyProtocolAdapter {
name: String,
}
impl ProtocolAdapter for MyProtocolAdapter {
fn name(&self) -> &str { &self.name }
fn connect(&mut self, endpoint: &str) -> PluginResult<()> {
// 自定义连接逻辑
Ok(())
}
fn read(&mut self, address: &str) -> PluginResult<Vec<u8>> {
// 自定义读取逻辑
Ok(vec![])
}
fn write(&mut self, address: &str, value: &[u8]) -> PluginResult<()> {
// 自定义写入逻辑
Ok(())
}
}
// 插件元数据
eneros_plugin::register_plugin! {
MyProtocolAdapter,
name: "my-protocol",
version: "1.0.0",
author: "EnerOS Team",
description: "自定义协议适配器",
}
插件安全
| 安全机制 | 说明 |
|---|---|
| Ed25519 签名验证 | 插件必须签名,启动时验证 |
| seccomp 沙箱 | 限制系统调用,防止恶意行为 |
| 资源限制 | CPU、内存、IO 限制 |
| 热加载/卸载 | 运行时动态加载,无需重启 |
use eneros_plugin::{PluginManager, PluginConfig, SecurityPolicy};
let manager = PluginManager::new(PluginConfig {
plugin_dir: "plugins/",
security_policy: SecurityPolicy::Strict,
signature_required: true, // 强制签名验证
seccomp_sandbox: true, // seccomp 沙箱
memory_limit_mb: 128, // 内存上限
cpu_limit_percent: 10, // CPU 上限
});
// 加载签名验证的插件
manager.load("my-protocol-1.0.0.plugin").await?;
// 热卸载
manager.unload("my-protocol").await?;
API 性能对比
| API 类型 | 延迟 | 吞吐 | 并发连接 | 适用场景 |
|---|---|---|---|---|
| REST | 5-50ms | 1万 QPS | 1000 | 管理操作 |
| GraphQL | 5-50ms | 5000 QPS | 1000 | 灵活查询 |
| WebSocket | < 1ms | 10万 消息/秒 | 10000 | 实时交互 |
| SSE | < 1ms | 5万 事件/秒 | 5000 | 事件推送 |
| gRPC | 1-5ms | 5万 QPS | 5000 | 高性能 RPC |
完整集成示例
use eneros_os::EnerOS;
use eneros_protocol_iec104::Iec104Server;
use eneros_protocol_iec61850::IedServer;
use eneros_protocol_mqtt::MqttBroker;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// 1. 启动 EnerOS 内核
let eneros = EnerOS::new("config/eneros.toml")?;
eneros.start().await?;
// 2. 启动 IEC 104 服务器(SCADA 集成)
let iec104 = Iec104Server::bind("0.0.0.0:2404")?;
iec104.start_with_eneros(&eneros).await?;
// 3. 启动 IEC 61850 服务器(变电站自动化)
let iec61850 = IedServer::bind("0.0.0.0:102")?;
iec61850.start_with_eneros(&eneros).await?;
// 4. 启动 MQTT broker(IoT 设备接入)
let mqtt = MqttBroker::bind("0.0.0.0:1883")?;
mqtt.start_with_eneros(&eneros).await?;
// 5. 启动 REST API 服务器
eneros.api_server()
.rest("0.0.0.0:8080")
.graphql("0.0.0.0:8080/graphql")
.websocket("0.0.0.0:8080/ws")
.grpc("0.0.0.0:50051")
.start().await?;
// 6. 加载自定义协议插件
eneros.plugin_manager()
.load("plugins/custom-protocol.plugin").await?;
println!("EnerOS 已启动,监听:");
println!(" - REST API: http://localhost:8080");
println!(" - GraphQL: http://localhost:8080/graphql");
println!(" - WebSocket: ws://localhost:8080/ws");
println!(" - gRPC: localhost:50051");
println!(" - IEC 104: localhost:2404");
println!(" - IEC 61850: localhost:102");
println!(" - MQTT: localhost:1883");
Ok(())
}
限制与权衡
| 权衡点 | 说明 | 缓解策略 |
|---|---|---|
| 协议适配开销 | 每个协议需独立适配 | 内置主流协议,插件扩展 |
| 数据模型转换 | 不同协议数据模型差异 | 统一映射到 CIM |
| 性能损耗 | 协议转换增加延迟 | 内核态直接转换 |
| 兼容性测试 | 协议版本众多 | 提供兼容性测试套件 |
| 安全风险 | 开放接口增加攻击面 | mTLS + 签名验证 |
下一步
- Power-Native First - 电力原生优先设计哲学
- 物联网泛在接入 - 协议适配详解
- API 参考 - 开放 API 完整文档
- 安全守卫 - 互操作安全机制