Skip to content

Commit 7561707

Browse files
committed
Extract Splitter class, add "ranked" and "mixed" variants
1 parent c06319c commit 7561707

File tree

10 files changed

+479
-44
lines changed

10 files changed

+479
-44
lines changed

lib/loop.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
require 'influx_push'
22
require 'influx_pull'
3-
require 'calculator'
3+
require 'processor'
44

55
class Loop
66
def initialize(config:)
@@ -62,7 +62,7 @@ def process_day(day)
6262
day_records = influx_pull.day_records(day.beginning_of_day)
6363
return if day_records.empty?
6464

65-
splitted_powers = Calculator.new(day_records:, config:).call
65+
splitted_powers = Processor.new(day_records:, config:).call
6666
influx_push.push(splitted_powers)
6767
end
6868

lib/calculator.rb renamed to lib/processor.rb

Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
require 'config'
2+
require 'splitter/mixed'
23

3-
class Calculator
4+
class Processor
45
def initialize(day_records:, config:)
56
@day_records = day_records
67
@config = config
@@ -69,7 +70,7 @@ def power_value(record, sensor_name)
6970
record[config.field(sensor_name)] || 0.0
7071
end
7172

72-
def split_power(record) # rubocop:disable Metrics/AbcSize
73+
def split_power(record)
7374
house_power = house_power(record)
7475
wallbox_power = wallbox_power(record)
7576
heatpump_power = heatpump_power(record)
@@ -82,30 +83,11 @@ def split_power(record) # rubocop:disable Metrics/AbcSize
8283
)
8384
house_power = [house_power, 0].max
8485

85-
total_power = house_power + wallbox_power + heatpump_power
86-
8786
grid_import_power = grid_import_power(record)
8887

89-
house_power_grid = 0
90-
wallbox_power_grid = 0
91-
heatpump_power_grid = 0
92-
93-
unless grid_import_power.zero? || total_power.zero?
94-
house_power_ratio = house_power.fdiv(total_power)
95-
wallbox_power_ratio = wallbox_power.fdiv(total_power)
96-
heatpump_power_ratio = heatpump_power.fdiv(total_power)
97-
98-
house_power_grid = grid_import_power * house_power_ratio
99-
wallbox_power_grid = grid_import_power * wallbox_power_ratio
100-
heatpump_power_grid = grid_import_power * heatpump_power_ratio
101-
end
102-
103-
{
104-
time: record['time'],
105-
106-
house_power_grid:,
107-
wallbox_power_grid:,
108-
heatpump_power_grid:,
109-
}
88+
Splitter::Mixed
89+
.new(grid_import_power:, house_power:, wallbox_power:, heatpump_power:)
90+
.call
91+
.merge(time: record['time'])
11092
end
11193
end

lib/splitter/base.rb

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
module Splitter
2+
class Base
3+
def initialize(
4+
grid_import_power:,
5+
house_power:,
6+
wallbox_power:,
7+
heatpump_power:
8+
)
9+
@grid_import_power = grid_import_power
10+
@house_power = house_power
11+
@wallbox_power = wallbox_power
12+
@heatpump_power = heatpump_power
13+
end
14+
15+
attr_reader :grid_import_power,
16+
:house_power,
17+
:wallbox_power,
18+
:heatpump_power
19+
20+
def call
21+
raise NotImplementedError
22+
end
23+
24+
private
25+
26+
def total
27+
@total ||= house_power + wallbox_power + heatpump_power
28+
end
29+
end
30+
end

lib/splitter/mixed.rb

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
require 'splitter/base'
2+
3+
module Splitter
4+
class Mixed < Base
5+
def call
6+
house_power_grid = 0
7+
wallbox_power_grid = 0
8+
heatpump_power_grid = 0
9+
10+
rest = grid_import_power
11+
12+
# Wallbox power is prioritized over other consumers
13+
if rest.positive? && wallbox_power.positive?
14+
wallbox_power_grid = [wallbox_power, rest].min
15+
rest -= wallbox_power_grid
16+
end
17+
18+
# Now we have to split the remaining power between the house and the heatpump
19+
house_and_heatpump_power = house_power + heatpump_power
20+
if rest.positive? && house_and_heatpump_power.positive?
21+
house_power_ratio = house_power.fdiv(house_and_heatpump_power)
22+
heatpump_power_ratio = heatpump_power.fdiv(house_and_heatpump_power)
23+
24+
house_power_grid = [rest * house_power_ratio, house_power].min
25+
heatpump_power_grid = [rest * heatpump_power_ratio, heatpump_power].min
26+
end
27+
28+
{ house_power_grid:, wallbox_power_grid:, heatpump_power_grid: }
29+
end
30+
end
31+
end

lib/splitter/proportional.rb

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
require 'splitter/base'
2+
3+
module Splitter
4+
class Proportional < Base
5+
def call
6+
house_power_grid = 0
7+
wallbox_power_grid = 0
8+
heatpump_power_grid = 0
9+
10+
if grid_import_power.positive? && total.positive?
11+
house_power_ratio = house_power.fdiv(total)
12+
wallbox_power_ratio = wallbox_power.fdiv(total)
13+
heatpump_power_ratio = heatpump_power.fdiv(total)
14+
15+
house_power_grid = [
16+
grid_import_power * house_power_ratio,
17+
house_power,
18+
].min
19+
20+
wallbox_power_grid = [
21+
grid_import_power * wallbox_power_ratio,
22+
wallbox_power,
23+
].min
24+
25+
heatpump_power_grid = [
26+
grid_import_power * heatpump_power_ratio,
27+
heatpump_power,
28+
].min
29+
end
30+
31+
{ house_power_grid:, wallbox_power_grid:, heatpump_power_grid: }
32+
end
33+
end
34+
end

lib/splitter/ranked.rb

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
require 'splitter/base'
2+
3+
module Splitter
4+
class Ranked < Base
5+
def call
6+
house_power_grid = 0
7+
wallbox_power_grid = 0
8+
heatpump_power_grid = 0
9+
10+
rest = grid_import_power
11+
12+
if rest.positive? && wallbox_power.positive?
13+
wallbox_power_grid = [wallbox_power, grid_import_power].min
14+
rest -= wallbox_power_grid
15+
end
16+
17+
if rest.positive? && heatpump_power.positive?
18+
heatpump_power_grid = [heatpump_power, rest].min
19+
rest -= heatpump_power_grid
20+
end
21+
22+
if rest.positive? && house_power.positive?
23+
house_power_grid = [house_power, rest].min
24+
end
25+
26+
{ house_power_grid:, wallbox_power_grid:, heatpump_power_grid: }
27+
end
28+
end
29+
end

spec/lib/calculator_spec.rb renamed to spec/lib/processor_spec.rb

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
require 'calculator'
1+
require 'processor'
22

3-
describe Calculator do
4-
subject(:calculator) { described_class.new(day_records:, config:) }
3+
describe Processor do
4+
subject(:processor) { described_class.new(day_records:, config:) }
55

66
let(:config) { Config.new(ENV) }
77

@@ -25,7 +25,7 @@
2525
end
2626

2727
describe '#call' do
28-
subject(:call) { calculator.call }
28+
subject(:call) { processor.call }
2929

3030
it 'returns the correct result' do
3131
lines = call.map(&:to_line_protocol)
@@ -37,17 +37,4 @@
3737
)
3838
end
3939
end
40-
41-
describe '#split_power' do
42-
subject(:split_power) { calculator.send(:split_power, day_records.first) }
43-
44-
it 'returns the correct result' do
45-
expect(split_power).to include(
46-
time: a_kind_of(Time),
47-
house_power_grid: 50,
48-
wallbox_power_grid: 30,
49-
heatpump_power_grid: 20,
50-
)
51-
end
52-
end
5340
end

spec/lib/splitter/mixed_spec.rb

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
require 'splitter/mixed'
2+
3+
describe Splitter::Mixed do
4+
subject(:splitter) { described_class.new(**record) }
5+
6+
describe '#call' do
7+
subject(:call) { splitter.call }
8+
9+
context 'when grid is 100%' do
10+
let(:record) do
11+
{
12+
grid_import_power: 100,
13+
house_power: 50,
14+
heatpump_power: 20,
15+
wallbox_power: 30,
16+
}
17+
end
18+
19+
it 'returns full distribution' do
20+
expect(call).to eq(
21+
{
22+
house_power_grid: 50,
23+
heatpump_power_grid: 20,
24+
wallbox_power_grid: 30,
25+
},
26+
)
27+
end
28+
end
29+
30+
context 'when grid is more than 100%' do
31+
let(:record) do
32+
{
33+
grid_import_power: 1000,
34+
house_power: 50,
35+
heatpump_power: 20,
36+
wallbox_power: 30,
37+
}
38+
end
39+
40+
it 'returns full distribution limited to consumption' do
41+
expect(call).to eq(
42+
{
43+
house_power_grid: 50,
44+
heatpump_power_grid: 20,
45+
wallbox_power_grid: 30,
46+
},
47+
)
48+
end
49+
end
50+
51+
context 'when grid is 0%' do
52+
let(:record) do
53+
{
54+
grid_import_power: 0,
55+
house_power: 100,
56+
heatpump_power: 40,
57+
wallbox_power: 60,
58+
}
59+
end
60+
61+
it 'returns 0 distribution' do
62+
expect(call).to eq(
63+
{
64+
house_power_grid: 0,
65+
heatpump_power_grid: 0,
66+
wallbox_power_grid: 0,
67+
},
68+
)
69+
end
70+
end
71+
72+
context 'when grid is 30%' do
73+
let(:record) do
74+
{
75+
grid_import_power: 60,
76+
house_power: 100,
77+
heatpump_power: 40,
78+
wallbox_power: 60,
79+
}
80+
end
81+
82+
it 'returns wallbox-first distribution' do
83+
expect(call).to eq(
84+
{
85+
house_power_grid: 0,
86+
heatpump_power_grid: 0,
87+
wallbox_power_grid: 60,
88+
},
89+
)
90+
end
91+
end
92+
93+
context 'when grid is 60%' do
94+
let(:record) do
95+
{
96+
grid_import_power: 120,
97+
house_power: 100,
98+
heatpump_power: 40,
99+
wallbox_power: 60,
100+
}
101+
end
102+
103+
it 'returns wallbox-first, then others pro-rata' do
104+
expect(call).to eq(
105+
{
106+
house_power_grid: 42.85714285714286,
107+
heatpump_power_grid: 17.142857142857142,
108+
wallbox_power_grid: 60,
109+
},
110+
)
111+
end
112+
end
113+
end
114+
end

0 commit comments

Comments
 (0)