mirror of
https://github.com/Akkudoktor-EOS/EOS.git
synced 2025-10-30 06:16:21 +00:00
236 lines
7.8 KiB
Python
236 lines
7.8 KiB
Python
|
|
from typing import Optional
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
|
||
|
|
from akkudoktoreos.core.emplan import (
|
||
|
|
BaseInstruction,
|
||
|
|
CommodityQuantity,
|
||
|
|
DDBCInstruction,
|
||
|
|
EnergyManagementPlan,
|
||
|
|
FRBCInstruction,
|
||
|
|
OMBCInstruction,
|
||
|
|
PEBCInstruction,
|
||
|
|
PEBCPowerEnvelope,
|
||
|
|
PEBCPowerEnvelopeElement,
|
||
|
|
PPBCEndInterruptionInstruction,
|
||
|
|
PPBCScheduleInstruction,
|
||
|
|
PPBCStartInterruptionInstruction,
|
||
|
|
)
|
||
|
|
from akkudoktoreos.utils.datetimeutil import Duration, to_datetime, to_duration
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.fixture
|
||
|
|
def fixed_now():
|
||
|
|
return to_datetime("2025-06-01T12:00:00")
|
||
|
|
|
||
|
|
|
||
|
|
class TestEnergyManagementPlan:
|
||
|
|
def test_add_instruction_and_time_range(self, fixed_now):
|
||
|
|
plan = EnergyManagementPlan(
|
||
|
|
id="plan-123",
|
||
|
|
generated_at=fixed_now,
|
||
|
|
instructions=[]
|
||
|
|
)
|
||
|
|
instr1 = OMBCInstruction(
|
||
|
|
resource_id="dev-1",
|
||
|
|
execution_time=fixed_now,
|
||
|
|
operation_mode_id="mymode1",
|
||
|
|
operation_mode_factor=1.0,
|
||
|
|
)
|
||
|
|
instr2 = OMBCInstruction(
|
||
|
|
resource_id="dev-2",
|
||
|
|
execution_time=fixed_now.add(minutes=5),
|
||
|
|
operation_mode_id="mymode1",
|
||
|
|
operation_mode_factor=1.0,
|
||
|
|
)
|
||
|
|
plan.add_instruction(instr1)
|
||
|
|
plan.add_instruction(instr2)
|
||
|
|
|
||
|
|
# Check that valid_from matches the earliest execution_time
|
||
|
|
assert plan.valid_from == fixed_now
|
||
|
|
|
||
|
|
# instr2 has infinite duration so valid_until must be None
|
||
|
|
assert plan.valid_until is None
|
||
|
|
|
||
|
|
assert plan.instructions == [instr1, instr2]
|
||
|
|
|
||
|
|
def test_clear(self, fixed_now):
|
||
|
|
plan = EnergyManagementPlan(
|
||
|
|
id="plan-123",
|
||
|
|
generated_at=fixed_now,
|
||
|
|
instructions=[
|
||
|
|
OMBCInstruction(
|
||
|
|
resource_id="dev-1",
|
||
|
|
execution_time=fixed_now,
|
||
|
|
operation_mode_id="mymode1",
|
||
|
|
operation_mode_factor=1.0,
|
||
|
|
)
|
||
|
|
],
|
||
|
|
)
|
||
|
|
plan.clear()
|
||
|
|
assert plan.instructions == []
|
||
|
|
assert plan.valid_until is None
|
||
|
|
assert plan.valid_from is not None
|
||
|
|
|
||
|
|
def test_get_active_instructions(self, fixed_now):
|
||
|
|
instr1 = OMBCInstruction(
|
||
|
|
resource_id="dev-1",
|
||
|
|
execution_time=fixed_now.subtract(minutes=1),
|
||
|
|
operation_mode_id="mymode1",
|
||
|
|
operation_mode_factor=1.0,
|
||
|
|
)
|
||
|
|
instr2 = OMBCInstruction(
|
||
|
|
resource_id="dev-2",
|
||
|
|
execution_time=fixed_now.add(minutes=1),
|
||
|
|
operation_mode_id="mymode1",
|
||
|
|
operation_mode_factor=1.0,
|
||
|
|
)
|
||
|
|
instr3 = OMBCInstruction(
|
||
|
|
resource_id="dev-3",
|
||
|
|
execution_time=fixed_now.subtract(minutes=10),
|
||
|
|
operation_mode_id="mymode1",
|
||
|
|
operation_mode_factor=1.0,
|
||
|
|
)
|
||
|
|
plan = EnergyManagementPlan(
|
||
|
|
id="plan-123",
|
||
|
|
generated_at=fixed_now,
|
||
|
|
instructions=[instr1, instr2, instr3],
|
||
|
|
)
|
||
|
|
plan._update_time_range()
|
||
|
|
|
||
|
|
active = plan.get_active_instructions(now=fixed_now)
|
||
|
|
ids = {i.resource_id for i in active}
|
||
|
|
assert ids == {"dev-1", "dev-3"}
|
||
|
|
|
||
|
|
def test_get_next_instruction(self, fixed_now):
|
||
|
|
instr1 = OMBCInstruction(
|
||
|
|
resource_id="dev-1",
|
||
|
|
execution_time=fixed_now.subtract(minutes=1),
|
||
|
|
operation_mode_id="mymode1",
|
||
|
|
operation_mode_factor=1.0,
|
||
|
|
)
|
||
|
|
instr2 = OMBCInstruction(
|
||
|
|
resource_id="dev-2",
|
||
|
|
execution_time=fixed_now.add(minutes=10),
|
||
|
|
operation_mode_id="mymode1",
|
||
|
|
operation_mode_factor=1.0,
|
||
|
|
)
|
||
|
|
instr3 = OMBCInstruction(
|
||
|
|
resource_id="dev-3",
|
||
|
|
execution_time=fixed_now.add(minutes=5),
|
||
|
|
operation_mode_id="mymode1",
|
||
|
|
operation_mode_factor=1.0,
|
||
|
|
)
|
||
|
|
plan = EnergyManagementPlan(
|
||
|
|
id="plan-123",
|
||
|
|
generated_at=fixed_now,
|
||
|
|
instructions=[instr1, instr2, instr3],
|
||
|
|
)
|
||
|
|
plan._update_time_range()
|
||
|
|
|
||
|
|
next_instr = plan.get_next_instruction(now=fixed_now)
|
||
|
|
assert next_instr is not None
|
||
|
|
assert next_instr.resource_id == "dev-3"
|
||
|
|
|
||
|
|
def test_get_instructions_for_resource(self, fixed_now):
|
||
|
|
instr1 = OMBCInstruction(
|
||
|
|
resource_id="dev-1",
|
||
|
|
execution_time=fixed_now,
|
||
|
|
operation_mode_id="mymode1",
|
||
|
|
operation_mode_factor=1.0,
|
||
|
|
)
|
||
|
|
instr2 = OMBCInstruction(
|
||
|
|
resource_id="dev-2",
|
||
|
|
execution_time=fixed_now,
|
||
|
|
operation_mode_id="mymode1",
|
||
|
|
operation_mode_factor=1.0,
|
||
|
|
)
|
||
|
|
plan = EnergyManagementPlan(
|
||
|
|
id="plan-123",
|
||
|
|
generated_at=fixed_now,
|
||
|
|
instructions=[instr1, instr2],
|
||
|
|
)
|
||
|
|
dev1_instructions = plan.get_instructions_for_resource("dev-1")
|
||
|
|
assert len(dev1_instructions) == 1
|
||
|
|
assert dev1_instructions[0].resource_id == "dev-1"
|
||
|
|
|
||
|
|
|
||
|
|
def test_add_various_instructions(self, fixed_now):
|
||
|
|
plan = EnergyManagementPlan(
|
||
|
|
id="plan-123",
|
||
|
|
generated_at=fixed_now,
|
||
|
|
instructions=[]
|
||
|
|
)
|
||
|
|
|
||
|
|
instrs = [
|
||
|
|
DDBCInstruction(
|
||
|
|
id="actuatorA@123",
|
||
|
|
execution_time=fixed_now,
|
||
|
|
actuator_id="actuatorA",
|
||
|
|
operation_mode_id="mode123",
|
||
|
|
operation_mode_factor=0.5,
|
||
|
|
),
|
||
|
|
FRBCInstruction(
|
||
|
|
id="actuatorB@456",
|
||
|
|
execution_time=fixed_now.add(minutes=1),
|
||
|
|
actuator_id="actuatorB",
|
||
|
|
operation_mode_id="FRBC_Mode_1",
|
||
|
|
operation_mode_factor=1.0,
|
||
|
|
),
|
||
|
|
OMBCInstruction(
|
||
|
|
id="controller@789",
|
||
|
|
execution_time=fixed_now.add(minutes=2),
|
||
|
|
operation_mode_id="OMBC_Mode_42",
|
||
|
|
operation_mode_factor=0.8,
|
||
|
|
),
|
||
|
|
PPBCEndInterruptionInstruction(
|
||
|
|
id="end_int@001",
|
||
|
|
execution_time=fixed_now.add(minutes=3),
|
||
|
|
power_profile_id="profile-123",
|
||
|
|
sequence_container_id="container-456",
|
||
|
|
power_sequence_id="seq-789",
|
||
|
|
),
|
||
|
|
PPBCStartInterruptionInstruction(
|
||
|
|
id="start_int@002",
|
||
|
|
execution_time=fixed_now.add(minutes=4),
|
||
|
|
power_profile_id="profile-321",
|
||
|
|
sequence_container_id="container-654",
|
||
|
|
power_sequence_id="seq-987",
|
||
|
|
),
|
||
|
|
PPBCScheduleInstruction(
|
||
|
|
id="schedule@003",
|
||
|
|
execution_time=fixed_now.add(minutes=5),
|
||
|
|
power_profile_id="profile-999",
|
||
|
|
sequence_container_id="container-888",
|
||
|
|
power_sequence_id="seq-777",
|
||
|
|
),
|
||
|
|
PEBCInstruction(
|
||
|
|
id="pebc@004",
|
||
|
|
execution_time=fixed_now.add(minutes=6),
|
||
|
|
power_constraints_id="pc-123",
|
||
|
|
power_envelopes=[
|
||
|
|
PEBCPowerEnvelope(
|
||
|
|
id="pebcpe@1234",
|
||
|
|
commodity_quantity=CommodityQuantity.ELECTRIC_POWER_L1,
|
||
|
|
power_envelope_elements = [
|
||
|
|
PEBCPowerEnvelopeElement(
|
||
|
|
duration = to_duration(10),
|
||
|
|
upper_limit = 1010.0,
|
||
|
|
lower_limit = 990.0,
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
]
|
||
|
|
|
||
|
|
for instr in instrs:
|
||
|
|
plan.add_instruction(instr)
|
||
|
|
|
||
|
|
assert len(plan.instructions) == len(instrs)
|
||
|
|
# Check that get_instructions_for_device returns the right instructions
|
||
|
|
assert any(
|
||
|
|
instr for instr in plan.get_instructions_for_resource("actuatorA")
|
||
|
|
if isinstance(instr, DDBCInstruction)
|
||
|
|
)
|