|
5 | 5 |
|
6 | 6 |
|
7 | 7 | import logging |
| 8 | +import math |
8 | 9 | from abc import ABC, abstractmethod |
9 | 10 | from collections.abc import Mapping, Set |
10 | 11 | from datetime import datetime, timezone |
11 | 12 | from typing import Generic, TypeVar |
12 | 13 |
|
13 | 14 | from ... import timeseries |
| 15 | +from ..._internal import _math |
14 | 16 | from ...actor.power_distributing._component_managers._battery_manager import ( |
15 | 17 | _get_battery_inverter_mappings, |
16 | 18 | ) |
@@ -361,9 +363,9 @@ def calculate( |
361 | 363 | Return None if there are no component metrics. |
362 | 364 | """ |
363 | 365 | timestamp = _MIN_TIMESTAMP |
364 | | - usable_capacity_x100: float = 0 |
365 | | - used_capacity_x100: float = 0 |
366 | | - total_capacity_x100: float = 0 |
| 366 | + usable_capacity_x100: float = 0.0 |
| 367 | + used_capacity_x100: float = 0.0 |
| 368 | + total_capacity_x100: float = 0.0 |
367 | 369 |
|
368 | 370 | for battery_id in working_batteries: |
369 | 371 | if battery_id not in metrics_data: |
@@ -395,26 +397,29 @@ def calculate( |
395 | 397 | # Therefore, the variables are named with a `_x100` suffix. |
396 | 398 | usable_capacity_x100 = capacity * (soc_upper_bound - soc_lower_bound) |
397 | 399 | soc_scaled = ( |
398 | | - (soc - soc_lower_bound) / (soc_upper_bound - soc_lower_bound) * 100 |
| 400 | + (soc - soc_lower_bound) / (soc_upper_bound - soc_lower_bound) * 100.0 |
399 | 401 | ) |
400 | 402 | # we are clamping here because the SoC might be out of bounds |
401 | | - soc_scaled = min(max(soc_scaled, 0), 100) |
| 403 | + soc_scaled = min(max(soc_scaled, 0.0), 100.0) |
402 | 404 | timestamp = max(timestamp, metrics.timestamp) |
403 | 405 | used_capacity_x100 += usable_capacity_x100 * soc_scaled |
404 | 406 | total_capacity_x100 += usable_capacity_x100 |
405 | 407 |
|
406 | 408 | if timestamp == _MIN_TIMESTAMP: |
407 | 409 | return Sample(datetime.now(tz=timezone.utc), None) |
408 | 410 |
|
| 411 | + # When the calculated is close to 0.0 or 100.0, they are set to exactly 0.0 or |
| 412 | + # 100.0, to make full/empty checks using the == operator less error prone. |
| 413 | + pct = 0.0 |
409 | 414 | # To avoid zero division error |
410 | | - if total_capacity_x100 == 0: |
411 | | - return Sample( |
412 | | - timestamp=timestamp, |
413 | | - value=Percentage.from_percent(0.0), |
414 | | - ) |
| 415 | + if not _math.is_close_to_zero(total_capacity_x100): |
| 416 | + pct = used_capacity_x100 / total_capacity_x100 |
| 417 | + if math.isclose(pct, 100.0): |
| 418 | + pct = 100.0 |
| 419 | + |
415 | 420 | return Sample( |
416 | 421 | timestamp=timestamp, |
417 | | - value=Percentage.from_percent(used_capacity_x100 / total_capacity_x100), |
| 422 | + value=Percentage.from_percent(pct), |
418 | 423 | ) |
419 | 424 |
|
420 | 425 |
|
|
0 commit comments