Skip to content

Commit 805a3cb

Browse files
authored
Merge pull request #1 from shsms/logical_meter
Implement the logical meter
2 parents 0ef8769 + bda3520 commit 805a3cb

25 files changed

Lines changed: 1679 additions & 0 deletions

.github/workflows/ci.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
name: Rust CI
2+
3+
on:
4+
push:
5+
branches: [ v0.x.x ]
6+
pull_request:
7+
merge_group:
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-22.04
12+
13+
steps:
14+
- name: Fetch sources
15+
uses: actions/checkout@v4
16+
with:
17+
submodules: recursive
18+
19+
- name: Run tests
20+
uses: frequenz-floss/gh-action-cargo-test@v1.0.0
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Based on https://github.com/hyperledger/besu/pull/5207/files
2+
name: DCO
3+
on:
4+
merge_group:
5+
6+
jobs:
7+
DCO:
8+
runs-on: ubuntu-latest
9+
if: ${{ github.actor != 'dependabot[bot]' }}
10+
steps:
11+
- run: echo "This DCO job runs on merge_queue event and doesn't check PR contents"
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
name: Release Notes Check
2+
3+
on:
4+
merge_group:
5+
pull_request:
6+
types:
7+
# On by default if you specify no types.
8+
- "opened"
9+
- "reopened"
10+
- "synchronize"
11+
# For `skip-label` only.
12+
- "labeled"
13+
- "unlabeled"
14+
15+
16+
jobs:
17+
check-release-notes:
18+
name: Check release notes are updated
19+
runs-on: ubuntu-latest
20+
permissions:
21+
pull-requests: read
22+
steps:
23+
- name: Check for a release notes update
24+
if: github.event_name == 'pull_request'
25+
uses: brettcannon/check-for-changed-files@871d7b8b5917a4f6f06662e2262e8ffc51dff6d1 # v1.2.1
26+
with:
27+
file-pattern: "RELEASE_NOTES.md"
28+
prereq-pattern: "src/**"
29+
skip-label: "cmd:skip-release-notes"
30+
failure-message: "Missing a release notes update. Please add one or apply the ${skip-label} label to the pull request"

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/target
2+
Cargo.lock

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "submodules/frequenz-api-microgrid"]
2+
path = submodules/frequenz-api-microgrid
3+
url = https://github.com/frequenz-floss/frequenz-api-microgrid

Cargo.toml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
[package]
2+
name = "frequenz-microgrid-rs"
3+
version = "0.1.0"
4+
edition = "2024"
5+
6+
[lib]
7+
name = "frequenz_microgrid"
8+
path = "src/lib.rs"
9+
10+
[dependencies]
11+
chrono = "0.4"
12+
frequenz-microgrid-component-graph = { git = "https://github.com/frequenz-floss/frequenz-microgrid-component-graph-rs.git", rev = "ab3b998" }
13+
frequenz-microgrid-formula-engine = { git = "https://github.com/frequenz-floss/frequenz-microgrid-formula-engine-rs.git", rev = "e0567c0" }
14+
frequenz-resampling = { git = "https://github.com/frequenz-floss/frequenz-resampling-rs.git", rev = "ce84d66" }
15+
prost = "0.13"
16+
prost-types = "0.13"
17+
tokio = { version = "1.45", features = ["rt", "rt-multi-thread"] }
18+
tonic = "0.13"
19+
tracing = { version = "0.1" }
20+
tracing-subscriber = { version = "0.3" }
21+
22+
23+
[build-dependencies]
24+
tonic-build = "0.13"
25+
prost-build = "0.13"

build.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// License: MIT
2+
// Copyright © 2025 Frequenz Energy-as-a-Service GmbH
3+
4+
fn main() -> Result<(), std::io::Error> {
5+
let config = tonic_build::Config::new();
6+
7+
tonic_build::configure()
8+
.compile_protos_with_config(
9+
config,
10+
&["submodules/frequenz-api-microgrid/proto/frequenz/api/microgrid/v1/microgrid.proto"],
11+
&[
12+
"submodules/frequenz-api-microgrid/proto",
13+
"submodules/frequenz-api-microgrid/submodules/frequenz-api-common/proto",
14+
"submodules/frequenz-api-microgrid/submodules/api-common-protos",
15+
],
16+
)
17+
.inspect_err(|e| {
18+
eprintln!("Could not compile protobuf files. Error: {e:?}");
19+
})
20+
}

examples/logical_meter.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// License: MIT
2+
// Copyright © 2025 Frequenz Energy-as-a-Service GmbH
3+
4+
use chrono::TimeDelta;
5+
use frequenz_microgrid::{
6+
Error, LogicalMeterConfig, LogicalMeterHandle, Metric, MicrogridClientHandle,
7+
};
8+
9+
#[tokio::main]
10+
async fn main() -> Result<(), Error> {
11+
tracing_subscriber::fmt::fmt()
12+
.with_file(true)
13+
.with_line_number(true)
14+
.init();
15+
16+
let client = MicrogridClientHandle::new("http://[::1]:8800");
17+
let mut logical_meter = LogicalMeterHandle::try_new(
18+
client,
19+
LogicalMeterConfig {
20+
resampling_interval: TimeDelta::try_seconds(1).unwrap(),
21+
},
22+
)
23+
.await?;
24+
25+
// Create a formula that calculates `grid_power - battery_power`.
26+
let formula_grid = logical_meter.grid(Metric::AcActivePower)?;
27+
let formula_battery = logical_meter.battery(None, Metric::AcActivePower)?;
28+
let formula_consumer = logical_meter.consumer(Metric::AcActivePower)?;
29+
30+
let formula = (logical_meter.grid(Metric::AcActivePower)?
31+
- logical_meter.battery(None, Metric::AcActivePower)?
32+
+ logical_meter.consumer(Metric::AcActivePower)?)?;
33+
34+
let mut rx = formula.subscribe().await?;
35+
let mut grid_rx = formula_grid.subscribe().await?;
36+
let mut battery_rx = formula_battery.subscribe().await?;
37+
let mut consumer_rx = formula_consumer.subscribe().await?;
38+
39+
loop {
40+
let sample = rx.recv().await.unwrap();
41+
let grid_sample = grid_rx.recv().await.unwrap();
42+
let battery_sample = battery_rx.recv().await.unwrap();
43+
let consumer_sample = consumer_rx.recv().await.unwrap();
44+
tracing::info!(
45+
"grid({}) - battery({}) + consumer({}) = {}",
46+
grid_sample.value().unwrap(),
47+
battery_sample.value().unwrap(),
48+
consumer_sample.value().unwrap(),
49+
sample.value().unwrap()
50+
);
51+
}
52+
}

src/client.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// License: MIT
2+
// Copyright © 2025 Frequenz Energy-as-a-Service GmbH
3+
4+
//! A clonable client for the microgrid API.
5+
6+
mod instruction;
7+
mod microgrid_client_actor;
8+
mod retry_tracker;
9+
10+
mod microgrid_client_handle;
11+
pub use microgrid_client_handle::MicrogridClientHandle;

src/client/instruction.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// License: MIT
2+
// Copyright © 2025 Frequenz Energy-as-a-Service GmbH
3+
4+
//! Instructions that can be sent to the client actor from client handles.
5+
6+
use tokio::sync::{broadcast, oneshot};
7+
8+
use crate::{
9+
Error,
10+
proto::common::v1::microgrid::components::{Component, ComponentConnection, ComponentData},
11+
};
12+
13+
/// Instructions that can be sent to the client actor from client handles.
14+
#[derive(Debug)]
15+
pub(super) enum Instruction {
16+
GetComponentDataStream {
17+
component_id: u64,
18+
response_tx: oneshot::Sender<broadcast::Receiver<ComponentData>>,
19+
},
20+
ListComponents {
21+
component_ids: Vec<u64>,
22+
categories: Vec<i32>,
23+
response_tx: oneshot::Sender<Result<Vec<Component>, Error>>,
24+
},
25+
ListConnections {
26+
starts: Vec<u64>,
27+
ends: Vec<u64>,
28+
response_tx: oneshot::Sender<Result<Vec<ComponentConnection>, Error>>,
29+
},
30+
}

0 commit comments

Comments
 (0)