跳到主内容

开放互操作

核心概念

开放互操作

Open & Interoperable 是 EnerOS 的生态理念:基于国际电力行业标准协议,提供多种 API 类型与数据交换格式,确保与现有 SCADA、EMS、DMS、配网自动化系统的无缝互操作。EnerOS 不锁定用户,所有数据可导出为标准格式,所有接口遵循开放规范。

设计动机

电力系统的协议碎片化问题

电力系统历经数十年发展,存在大量异构协议与系统:

系统主要协议数据格式通信模式
调度自动化(EMS)IEC 60870-5-104私有主从
变电站自动化IEC 61850MMS/GOOSE发布订阅
配电自动化(DMS)IEC 60870-5-104 / DNP3私有主从
远动 RTUIEC 60870-5-101/104私有主从
智能电表DL/T 698.45 / Modbus私有主从
电网模型IEC 61970 (CIM)XML/RDF文件
配网模型IEC 61968 (CIM)XML/JSON文件
风电/光伏IEC 61400-25MMS发布订阅
物联网设备MQTT / CoAPJSON发布订阅

传统方案需要为每个协议开发专门适配器,集成成本高、维护困难。

开放互操作的解决思路

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/SVeneros-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风电场风机监控MMSeneros-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 类型协议数据格式通信模式适用场景性能
RESTHTTP/1.1, HTTP/2JSON请求-响应管理操作、查询
GraphQLHTTPJSON请求-响应灵活查询
WebSocketWSJSON/二进制双向流实时交互
SSEHTTP文本流服务器推送事件订阅
gRPCHTTP/2Protobuf双向流高性能 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);

数据交换格式

支持的格式

格式适用场景优点缺点
JSONAPI 通用通用性强体积大
XMLCIM 模型标准兼容冗长
ProtobufgRPC紧凑高效需 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 类型延迟吞吐并发连接适用场景
REST5-50ms1万 QPS1000管理操作
GraphQL5-50ms5000 QPS1000灵活查询
WebSocket< 1ms10万 消息/秒10000实时交互
SSE< 1ms5万 事件/秒5000事件推送
gRPC1-5ms5万 QPS5000高性能 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 + 签名验证

下一步