Cache TB data: src/data/data_providers.py
Browse files- src/data/data_providers.py +16 -4
src/data/data_providers.py
CHANGED
|
@@ -872,6 +872,8 @@ class EnergyService(BaseService):
|
|
| 872 |
def __init__(self, tb_client: Any = None):
|
| 873 |
self._tb = tb_client
|
| 874 |
self._breaker = CircuitBreaker(threshold=3, cooldown_sec=300)
|
|
|
|
|
|
|
| 875 |
|
| 876 |
def _client(self):
|
| 877 |
if self._tb is None:
|
|
@@ -884,28 +886,36 @@ class EnergyService(BaseService):
|
|
| 884 |
# ------------------------------------------------------------------
|
| 885 |
|
| 886 |
def get_current(self) -> Dict[str, Any]:
|
| 887 |
-
"""Latest power reading from the Plant asset."""
|
|
|
|
|
|
|
|
|
|
| 888 |
if self._breaker.is_open:
|
| 889 |
return {"error": "ThingsBoard circuit breaker open — retrying in 5 min"}
|
| 890 |
try:
|
| 891 |
vals = self._client().get_asset_latest("Plant", ["power", "production"])
|
| 892 |
power_w = vals.get("power")
|
| 893 |
self._breaker.record_success()
|
| 894 |
-
|
| 895 |
"power_kw": round(power_w / 1000, 1) if power_w else None,
|
| 896 |
"source": "ThingsBoard Plant asset",
|
| 897 |
}
|
|
|
|
|
|
|
| 898 |
except Exception as exc:
|
| 899 |
self._breaker.record_failure()
|
| 900 |
return {"error": f"Energy current failed: {exc}"}
|
| 901 |
|
| 902 |
def get_daily_production(self, target_date: Optional[str] = None) -> Dict[str, Any]:
|
| 903 |
-
"""Accumulated energy production for a single day (real TB data).
|
| 904 |
|
| 905 |
Returns dict with daily_kwh, peak_hour, hourly_profile.
|
| 906 |
"""
|
| 907 |
try:
|
| 908 |
target = target_date or str(date.today())
|
|
|
|
|
|
|
|
|
|
| 909 |
day_start = pd.Timestamp(target, tz="UTC")
|
| 910 |
day_end = day_start + pd.Timedelta(days=1)
|
| 911 |
|
|
@@ -943,7 +953,7 @@ class EnergyService(BaseService):
|
|
| 943 |
peak_kwh = kwh
|
| 944 |
peak_hour = h
|
| 945 |
|
| 946 |
-
|
| 947 |
"date": target,
|
| 948 |
"daily_kwh": round(total_kwh, 1),
|
| 949 |
"peak_hour": peak_hour,
|
|
@@ -951,6 +961,8 @@ class EnergyService(BaseService):
|
|
| 951 |
"hourly_profile": hourly_profile,
|
| 952 |
"source": "ThingsBoard Plant asset",
|
| 953 |
}
|
|
|
|
|
|
|
| 954 |
except Exception as exc:
|
| 955 |
return {"date": target_date, "daily_kwh": None, "error": f"Energy fetch failed: {exc}"}
|
| 956 |
|
|
|
|
| 872 |
def __init__(self, tb_client: Any = None):
|
| 873 |
self._tb = tb_client
|
| 874 |
self._breaker = CircuitBreaker(threshold=3, cooldown_sec=300)
|
| 875 |
+
self._current_cache = TTLCache(ttl_seconds=300, redis_prefix="energy:") # 5 min
|
| 876 |
+
self._daily_cache = TTLCache(ttl_seconds=900, redis_prefix="energy_daily:") # 15 min
|
| 877 |
|
| 878 |
def _client(self):
|
| 879 |
if self._tb is None:
|
|
|
|
| 886 |
# ------------------------------------------------------------------
|
| 887 |
|
| 888 |
def get_current(self) -> Dict[str, Any]:
|
| 889 |
+
"""Latest power reading from the Plant asset (5-min TTL cache)."""
|
| 890 |
+
cached = self._current_cache.get("current")
|
| 891 |
+
if cached is not None:
|
| 892 |
+
return cached
|
| 893 |
if self._breaker.is_open:
|
| 894 |
return {"error": "ThingsBoard circuit breaker open — retrying in 5 min"}
|
| 895 |
try:
|
| 896 |
vals = self._client().get_asset_latest("Plant", ["power", "production"])
|
| 897 |
power_w = vals.get("power")
|
| 898 |
self._breaker.record_success()
|
| 899 |
+
result = {
|
| 900 |
"power_kw": round(power_w / 1000, 1) if power_w else None,
|
| 901 |
"source": "ThingsBoard Plant asset",
|
| 902 |
}
|
| 903 |
+
self._current_cache.set("current", result)
|
| 904 |
+
return result
|
| 905 |
except Exception as exc:
|
| 906 |
self._breaker.record_failure()
|
| 907 |
return {"error": f"Energy current failed: {exc}"}
|
| 908 |
|
| 909 |
def get_daily_production(self, target_date: Optional[str] = None) -> Dict[str, Any]:
|
| 910 |
+
"""Accumulated energy production for a single day (real TB data, 15-min TTL cache).
|
| 911 |
|
| 912 |
Returns dict with daily_kwh, peak_hour, hourly_profile.
|
| 913 |
"""
|
| 914 |
try:
|
| 915 |
target = target_date or str(date.today())
|
| 916 |
+
cached = self._daily_cache.get(f"daily:{target}")
|
| 917 |
+
if cached is not None:
|
| 918 |
+
return cached
|
| 919 |
day_start = pd.Timestamp(target, tz="UTC")
|
| 920 |
day_end = day_start + pd.Timedelta(days=1)
|
| 921 |
|
|
|
|
| 953 |
peak_kwh = kwh
|
| 954 |
peak_hour = h
|
| 955 |
|
| 956 |
+
result = {
|
| 957 |
"date": target,
|
| 958 |
"daily_kwh": round(total_kwh, 1),
|
| 959 |
"peak_hour": peak_hour,
|
|
|
|
| 961 |
"hourly_profile": hourly_profile,
|
| 962 |
"source": "ThingsBoard Plant asset",
|
| 963 |
}
|
| 964 |
+
self._daily_cache.set(f"daily:{target}", result)
|
| 965 |
+
return result
|
| 966 |
except Exception as exc:
|
| 967 |
return {"date": target_date, "daily_kwh": None, "error": f"Energy fetch failed: {exc}"}
|
| 968 |
|