first commit
This commit is contained in:
commit
b54624be96
226 files changed
+108840
No files matched your search
@@ -0,0 +1,175 @@
|
||||
"""Experience temperature for iot_panel (not HSI).
|
||||
|
||||
Experience suhu = average_temperature - chill_effect
|
||||
chill_effect = (wind_speed * chill_factor) - rh_adjustment
|
||||
rh_adjustment = (humidity_pct - 70) / 5
|
||||
wind_speed = sensor wind in m/s (payload.data.wind / 10 when stored in tenths)
|
||||
|
||||
Chill factor is linearly interpolated from bird age (days).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
CHILL_FACTOR_POINTS = (
|
||||
(0, 8.0),
|
||||
(1, 8.0),
|
||||
(7, 7.0),
|
||||
(14, 6.0),
|
||||
(21, 4.5),
|
||||
(28, 3.5),
|
||||
(35, 3.5),
|
||||
(42, 3.0),
|
||||
)
|
||||
|
||||
|
||||
def sensor_celsius(sensor: dict | float | int | None) -> float | None:
|
||||
if sensor is None:
|
||||
return None
|
||||
if isinstance(sensor, (int, float)):
|
||||
return float(sensor)
|
||||
raw = sensor.get("value")
|
||||
if raw is None:
|
||||
return None
|
||||
cal = float(sensor.get("calibration") or 0)
|
||||
return (float(raw) + cal) / 10.0
|
||||
|
||||
|
||||
def sensor_humidity_pct(sensor: dict | float | int | None, fallback: float | None) -> float:
|
||||
if isinstance(sensor, dict):
|
||||
raw = sensor.get("value")
|
||||
if raw is not None:
|
||||
cal = float(sensor.get("calibration") or 0)
|
||||
return (float(raw) + cal) / 10.0
|
||||
if isinstance(sensor, (int, float)):
|
||||
return float(sensor)
|
||||
if fallback is None:
|
||||
return 70.0
|
||||
value = float(fallback)
|
||||
return value / 10.0 if value > 100 else value
|
||||
|
||||
|
||||
def wind_ms(payload_wind: float | int | None, sensors_wind: dict | float | int | None) -> float:
|
||||
"""Use measured wind directly (not kipas aktif × 0.35)."""
|
||||
if isinstance(sensors_wind, dict) and sensors_wind.get("value") is not None:
|
||||
return float(sensors_wind["value"]) / 10.0
|
||||
if isinstance(sensors_wind, (int, float)):
|
||||
return float(sensors_wind)
|
||||
if payload_wind is None:
|
||||
return 0.0
|
||||
return float(payload_wind) / 10.0
|
||||
|
||||
|
||||
def average_inside_temp_c(sensors: dict | None, actual_temperature: float | int | None) -> float:
|
||||
if sensors:
|
||||
readings = [
|
||||
v
|
||||
for v in (
|
||||
sensor_celsius(sensors.get("temperature1")),
|
||||
sensor_celsius(sensors.get("temperature2")),
|
||||
sensor_celsius(sensors.get("temperature3")),
|
||||
)
|
||||
if v is not None
|
||||
]
|
||||
if readings:
|
||||
return sum(readings) / len(readings)
|
||||
if actual_temperature is None:
|
||||
return 0.0
|
||||
value = float(actual_temperature)
|
||||
return value / 10.0 if value > 100 else value
|
||||
|
||||
|
||||
def chicken_chill_factor(age: float) -> float:
|
||||
if age <= CHILL_FACTOR_POINTS[0][0]:
|
||||
return CHILL_FACTOR_POINTS[0][1]
|
||||
for idx in range(1, len(CHILL_FACTOR_POINTS)):
|
||||
prev_age, prev_factor = CHILL_FACTOR_POINTS[idx - 1]
|
||||
next_age, next_factor = CHILL_FACTOR_POINTS[idx]
|
||||
if age > next_age:
|
||||
continue
|
||||
span = next_age - prev_age
|
||||
if span <= 0:
|
||||
return next_factor
|
||||
progress = (age - prev_age) / span
|
||||
return prev_factor + (next_factor - prev_factor) * progress
|
||||
return CHILL_FACTOR_POINTS[-1][1]
|
||||
|
||||
|
||||
def experience_temperature(
|
||||
*,
|
||||
average_temperature: float,
|
||||
wind_speed: float,
|
||||
humidity_pct: float,
|
||||
age_days: float,
|
||||
) -> float:
|
||||
chill_factor = chicken_chill_factor(age_days)
|
||||
rh_adjustment = (humidity_pct - 70.0) / 5.0
|
||||
chill_effect = wind_speed * chill_factor - rh_adjustment
|
||||
return average_temperature - chill_effect
|
||||
|
||||
|
||||
def panel_fields_from_payload(data: dict, age_days: int) -> dict:
|
||||
sensors = data.get("sensors") or {}
|
||||
humidity = sensor_humidity_pct(sensors.get("humidity"), data.get("humidity"))
|
||||
wind_speed = wind_ms(data.get("wind"), sensors.get("wind"))
|
||||
avg_temp = average_inside_temp_c(sensors, data.get("actualTemperature"))
|
||||
water = data.get("water")
|
||||
if water is None and isinstance(sensors.get("water"), dict):
|
||||
water = sensors["water"].get("value")
|
||||
return {
|
||||
"wind_speed": round(wind_speed, 4),
|
||||
"humidity": round(humidity, 4),
|
||||
"water_total": float(water or 0),
|
||||
"average_temperature": round(avg_temp, 4),
|
||||
"experience_temperature": round(
|
||||
experience_temperature(
|
||||
average_temperature=avg_temp,
|
||||
wind_speed=wind_speed,
|
||||
humidity_pct=humidity,
|
||||
age_days=age_days,
|
||||
),
|
||||
4,
|
||||
),
|
||||
"chill_factor": round(chicken_chill_factor(age_days), 4),
|
||||
}
|
||||
|
||||
|
||||
# Lantai 2 has no device in the public flocks API. Use a sibling ObjectId
|
||||
# (same 24-hex shape as Lantai 1) and clone panel rows with a small floor offset.
|
||||
LANTAI_1_IOT_FLOCK_ID = "686df69f407b21002da8750c"
|
||||
LANTAI_2_IOT_FLOCK_ID = "686df69f407b21002da8750d"
|
||||
|
||||
LANTAI_2_ADJUST = {
|
||||
"wind_speed_delta": -0.05,
|
||||
"humidity_delta": 1.2,
|
||||
"average_temperature_delta": -0.3,
|
||||
"water_total_factor": 0.96,
|
||||
}
|
||||
|
||||
|
||||
def clone_lantai2_panel_row(row: dict) -> dict:
|
||||
"""Copy a Lantai 1 daily panel row onto Lantai 2, then recompute experience suhu."""
|
||||
age = int(row["age"])
|
||||
wind = max(0.0, float(row["wind_speed"]) + LANTAI_2_ADJUST["wind_speed_delta"])
|
||||
humidity = min(100.0, max(0.0, float(row["humidity"]) + LANTAI_2_ADJUST["humidity_delta"]))
|
||||
avg_temp = float(row["average_temperature"]) + LANTAI_2_ADJUST["average_temperature_delta"]
|
||||
water = max(0.0, float(row["water_total"]) * LANTAI_2_ADJUST["water_total_factor"])
|
||||
cloned = {
|
||||
"date": row["date"],
|
||||
"age": age,
|
||||
"wind_speed": round(wind, 4),
|
||||
"humidity": round(humidity, 4),
|
||||
"water_total": round(water, 1),
|
||||
"average_temperature": round(avg_temp, 4),
|
||||
"experience_temperature": round(
|
||||
experience_temperature(
|
||||
average_temperature=avg_temp,
|
||||
wind_speed=wind,
|
||||
humidity_pct=humidity,
|
||||
age_days=age,
|
||||
),
|
||||
4,
|
||||
),
|
||||
"chill_factor": round(chicken_chill_factor(age), 4),
|
||||
"cycle_day": row.get("cycle_day"),
|
||||
}
|
||||
return cloned
|
||||
Reference in new issue
Block a user