609 lines
20 KiB
Python
609 lines
20 KiB
Python
# -*- coding: utf-8 -*-
|
||
from __future__ import annotations
|
||
|
||
__title__ = "legacy_syslog"
|
||
__author__ = 'Mehmet Karatay & "Saraswati" (ChatGPT)'
|
||
__purpose__ = "Legacy tarzı syslog çıktısı üreten köprü"
|
||
__version__ = "0.2.1"
|
||
__date__ = "2025-11-22"
|
||
|
||
"""
|
||
ebuild/io/legacy_syslog.py
|
||
|
||
Revision : 2025-11-22
|
||
Authors : Mehmet Karatay & "Saraswati" (ChatGPT)
|
||
|
||
Amaç
|
||
-----
|
||
Eski Rasp2 tabanlı sistemin syslog çıktısını, yeni ebuild mimarisi ile
|
||
uyumlu ve okunaklı şekilde üretir. Çıktı şu ana bloklardan oluşur:
|
||
|
||
1) Üst bilgi:
|
||
- Versiyon ve zaman satırı
|
||
- Güneş bilgisi (sunrise / sunset, sistem On/Off, lisans id)
|
||
- Mevsim bilgisi (season, bahar dönemi bilgisi)
|
||
- Tatil bilgisi (varsa adıyla)
|
||
|
||
2) Bina ısı bilgisi
|
||
- Bina Isı : [ min - avg - max ]
|
||
|
||
3) Hat sensörleri (burner.py içinden doldurulan kısım):
|
||
- Dış Isı 1
|
||
- Çıkış Isı 2
|
||
- Dönüş hatları (isim map'inden)
|
||
|
||
4) Used dış ısı
|
||
5) Brülör / devirdaim / özet satırı
|
||
|
||
Not
|
||
---
|
||
Bu modül sadece formatlama ve çıktı üretiminden sorumludur. Gerçek
|
||
ölçümler ve kontrol kararları üst katmanlardan (HeatEngine, Burner,
|
||
Building, Environment, SeasonController vb.) alınır.
|
||
"""
|
||
# Bu modül gerçekten hangi path'ten import ediliyor, görmek için:
|
||
# ---------------------------------------------------------
|
||
def _safe_import(desc, import_func):
|
||
"""
|
||
desc: ekranda görünecek ad (örn: 'Building', 'legacy_syslog')
|
||
import_func: gerçek import'u yapan lambda
|
||
"""
|
||
try:
|
||
obj = import_func()
|
||
#print(f"legacy_syslog.py [IMPORT OK] {desc} ->", obj)
|
||
return obj
|
||
except Exception as e:
|
||
print(f"legacy_syslog.py [IMPORT FAIL] {desc}: {e}")
|
||
traceback.print_exc()
|
||
return None
|
||
|
||
from datetime import datetime, time
|
||
from typing import Optional
|
||
|
||
import logging
|
||
import logging.handlers
|
||
|
||
try:
|
||
# SeasonController ve konfig
|
||
from ..core.season import SeasonController
|
||
cfg = _safe_import( "config_statics", lambda: __import__("ebuild.config_statics", fromlist=["*"]),)
|
||
cfv = _safe_import( "config_runtime", lambda: __import__("ebuild.config_runtime", fromlist=["*"]),)
|
||
#from .. import config_statics as cfg
|
||
except ImportError: # test / standalone
|
||
SeasonController = None # type: ignore
|
||
cfg = None # type: ignore
|
||
cfv = None
|
||
print("SeasonController, config_statics import ERROR")
|
||
|
||
# ----------------------------------------------------------------------
|
||
# Logger kurulumu (Syslog + stdout)
|
||
# ----------------------------------------------------------------------
|
||
_LOGGER: Optional[logging.Logger] = None
|
||
|
||
|
||
def _get_logger() -> logging.Logger:
|
||
global _LOGGER
|
||
if _LOGGER is not None:
|
||
return _LOGGER
|
||
#print("logger..1:", stream_fmt)
|
||
logger = logging.getLogger("BRULOR")
|
||
logger.setLevel(logging.INFO)
|
||
|
||
# Aynı handler'ları ikinci kez eklemeyelim
|
||
if not logger.handlers:
|
||
# Syslog handler (Linux: /dev/log)
|
||
try:
|
||
syslog_handler = logging.handlers.SysLogHandler(address="/dev/log")
|
||
# Syslog mesaj formatı: "BRULOR: [ 1 ... ]"
|
||
fmt = logging.Formatter("%(name)s: %(message)s")
|
||
syslog_handler.setFormatter(fmt)
|
||
logger.addHandler(syslog_handler)
|
||
except Exception:
|
||
# /dev/log yoksa sessizce geç; sadece stdout'a yazacağız
|
||
pass
|
||
|
||
# Konsol çıktısı (debug için)
|
||
stream_handler = logging.StreamHandler()
|
||
stream_fmt = logging.Formatter("INFO:BRULOR:%(message)s")
|
||
stream_handler.setFormatter(stream_fmt)
|
||
logger.addHandler(stream_handler)
|
||
print("logger..2:", stream_fmt)
|
||
_LOGGER = logger
|
||
return logger
|
||
|
||
|
||
# ----------------------------------------------------------------------
|
||
# Temel çıktı fonksiyonları
|
||
# ----------------------------------------------------------------------
|
||
|
||
def send_legacy_syslog(message: str) -> None:
|
||
"""
|
||
Verilen mesajı legacy syslog formatına uygun şekilde ilgili hedefe gönderir.
|
||
- Syslog (/dev/log) → program adı: BRULOR
|
||
- Aynı zamanda stdout'a da yazar (DEBUG amaçlı)
|
||
"""
|
||
#print("send_legacy_syslog BRULOR:", message)
|
||
try:
|
||
logger = _get_logger()
|
||
logger.info(message)
|
||
except Exception as e:
|
||
# Logger bir sebeple çökerse bile BRULOR satırını kaybetmeyelim
|
||
print("BRULOR:", message, f"(logger error: {e})")
|
||
|
||
|
||
def format_line(line_no: int, body: str) -> str:
|
||
"""
|
||
BRULOR satırını klasik formata göre hazırlar.
|
||
|
||
Örnek:
|
||
line_no = 2, body = "Sunrise:07:39 Sunset:17:29 Sistem: On Lic:10094"
|
||
|
||
"[ 2 Sunrise:07:39 Sunset:17:29 Sistem: On Lic:10094]"
|
||
|
||
Not:
|
||
Burada "BRULOR" yazmıyoruz; syslog program adı zaten BRULOR olacak.
|
||
"""
|
||
return f"[{line_no:3d} {body}]"
|
||
|
||
|
||
def _format_version_3part(ver: str) -> str:
|
||
"""
|
||
__version__ string'ini "00.02.01" formatına çevirir.
|
||
Örnek:
|
||
"0.2.1" → "00.02.01"
|
||
"""
|
||
parts = (ver or "").split(".")
|
||
nums = []
|
||
for p in parts:
|
||
try:
|
||
nums.append(int(p))
|
||
except ValueError:
|
||
nums.append(0)
|
||
while len(nums) < 3:
|
||
nums.append(0)
|
||
return f"{nums[0]:02d}.{nums[1]:02d}.{nums[2]:02d}"
|
||
|
||
|
||
# ----------------------------------------------------------------------
|
||
# Üst blok (header) üreticiler
|
||
# ----------------------------------------------------------------------
|
||
def emit_header_version(line_no: int, now: datetime) -> int:
|
||
"""
|
||
1. satır: versiyon + zaman bilgisi.
|
||
Örnek:
|
||
************** 00.02.01 2025-11-22 18:15:00 *************
|
||
"""
|
||
v_str = _format_version_3part(__version__)
|
||
body = f"************** {v_str} {now.strftime('%Y-%m-%d %H:%M:%S')} *************"
|
||
send_legacy_syslog(format_line(line_no, body))
|
||
return line_no + 1
|
||
|
||
|
||
def emit_header_sun_and_system(
|
||
line_no: int,
|
||
sunrise: Optional[time],
|
||
sunset: Optional[time],
|
||
system_on: bool,
|
||
licence_id: int,
|
||
) -> int:
|
||
"""
|
||
2. satır: Güneş bilgisi + Sistem On/Off + Lisans id.
|
||
|
||
Örnek:
|
||
[ 2 Sunrise:07:39 Sunset:17:29 Sistem: On Lic:10094]
|
||
"""
|
||
sun_str = ""
|
||
if sunrise is not None:
|
||
sun_str += f"Sunrise:{sunrise.strftime('%H:%M')} "
|
||
if sunset is not None:
|
||
sun_str += f"Sunset:{sunset.strftime('%H:%M')} "
|
||
|
||
sys_str = "On" if system_on else "Off"
|
||
body = f"{sun_str}Sistem: {sys_str} Lic:{licence_id}"
|
||
send_legacy_syslog(format_line(line_no, body))
|
||
return line_no + 1
|
||
|
||
|
||
def _only_date(s: str) -> str:
|
||
"""
|
||
ISO tarih-zaman stringinden sadece YYYY-MM-DD kısmını alır.
|
||
Örn: '2025-09-23T16:33:10.687982+03:00' → '2025-09-23'
|
||
"""
|
||
if not s:
|
||
return "--"
|
||
s = s.strip()
|
||
if "T" in s:
|
||
return s.split("T", 1)[0]
|
||
return s
|
||
|
||
def emit_header_season(
|
||
line_no: int,
|
||
season_ctrl: SeasonController,
|
||
) -> int:
|
||
"""
|
||
Sunrise satırının altına mevsim + (varsa) bahar tasarruf dönemi satırını basar.
|
||
|
||
Beklenen format:
|
||
|
||
BRULOR [ 3 season : Sonbahar 2025-09-23 - 2025-12-20 [89 pass:60 kalan:28] ]
|
||
BRULOR [ 4 bahar : 2025-09-23 - 2025-10-13 ]
|
||
|
||
Notlar:
|
||
- Bilgiler SeasonController.info içinden okunur (dict veya obje olabilir).
|
||
- bahar_tasarruf True DEĞİLSE bahar satırı hiç basılmaz.
|
||
"""
|
||
|
||
# SeasonController.info hem dict hem obje olabilir, ikisini de destekle
|
||
info = getattr(season_ctrl, "info", season_ctrl)
|
||
|
||
def _get(field: str, default=None):
|
||
if isinstance(info, dict):
|
||
return info.get(field, default)
|
||
return getattr(info, field, default)
|
||
|
||
# ---- 3. satır: season ----
|
||
season_name = _get("season", "Unknown")
|
||
season_start = _only_date(_get("season_start", ""))
|
||
season_end = _only_date(_get("season_end", ""))
|
||
season_day = _get("season_day", "")
|
||
season_passed = _get("season_passed", "")
|
||
season_remain = _get("season_remaining", "")
|
||
|
||
body = (
|
||
f"season : {season_name} {season_start} - {season_end} "
|
||
f"[{season_day} pass:{season_passed} kalan:{season_remain}]"
|
||
)
|
||
send_legacy_syslog(format_line(line_no, body))
|
||
line_no += 1
|
||
|
||
# ---- 4. satır: bahar dönemi (SADECE aktifse) ----
|
||
bahar_tasarruf = bool(_get("bahar_tasarruf", False))
|
||
if bahar_tasarruf:
|
||
bahar_basx = _only_date(_get("bahar_basx", ""))
|
||
bahar_bitx = _only_date(_get("bahar_bitx", ""))
|
||
body = f"bahar : {bahar_basx} - {bahar_bitx}"
|
||
send_legacy_syslog(format_line(line_no, body))
|
||
line_no += 1
|
||
|
||
return line_no
|
||
|
||
|
||
def emit_header_holiday(
|
||
line_no: int,
|
||
is_holiday: bool,
|
||
holiday_label: str,
|
||
) -> int:
|
||
"""
|
||
Tatil satırı (sunrise + season altına).
|
||
|
||
Kurallar:
|
||
- Tatil yoksa (False) HİÇ satır basma.
|
||
- Tatil varsa:
|
||
[ 5 Tatil: True Adı: Cumhuriyet Bayramı]
|
||
"""
|
||
if not is_holiday:
|
||
return line_no
|
||
|
||
label = holiday_label or ""
|
||
body = f"Tatil: True Adı: {label}"
|
||
send_legacy_syslog(format_line(line_no, body))
|
||
return line_no + 1
|
||
|
||
|
||
# ----------------------------------------------------------------------
|
||
# Dışarıdan çağrılacak üst-blok helper
|
||
# ----------------------------------------------------------------------
|
||
def emit_top_block(
|
||
now: datetime,
|
||
season_ctrl: SeasonController,
|
||
) -> int:
|
||
"""
|
||
F veya B modundan bağımsız olarak, her tick başında üst bilgiyi üretir.
|
||
|
||
Sıra:
|
||
1) Versiyon + zaman
|
||
2) Sunrise / Sunset / Sistem: On/Off / Lic
|
||
3) Mevsim bilgisi (SeasonController.to_syslog_lines() → sadeleştirilmiş)
|
||
4) Tatil bilgisi (sadece tatil varsa)
|
||
5) Bir sonraki satır numarasını döndürür (bina ısı satırları için).
|
||
"""
|
||
line_no = 1
|
||
|
||
# 1) Versiyon
|
||
line_no = emit_header_version(line_no, now)
|
||
|
||
# Konfigten sistem ve lisans bilgileri
|
||
if cfg is not None:
|
||
licence_id = int(getattr(cfg, "BUILDING_LICENCEID", 0))
|
||
system_onoff = int(getattr(cfg, "BUILDING_SYSTEMONOFF", 1))
|
||
else:
|
||
licence_id = 0
|
||
system_onoff = 1
|
||
|
||
system_on = (system_onoff == 1)
|
||
|
||
# 2) Güneş + Sistem / Lisans
|
||
sunrise = season_ctrl.info.sunrise
|
||
sunset = season_ctrl.info.sunset
|
||
|
||
line_no = emit_header_sun_and_system(
|
||
line_no=line_no,
|
||
sunrise=sunrise,
|
||
sunset=sunset,
|
||
system_on=system_on,
|
||
licence_id=licence_id,
|
||
)
|
||
|
||
# 3) Mevsim bilgisi (sunrise ALTINA)
|
||
line_no = emit_header_season(line_no, season_ctrl)
|
||
|
||
# 4) Tatil bilgisi (sadece True ise)
|
||
line_no = emit_header_holiday(
|
||
line_no=line_no,
|
||
is_holiday=season_ctrl.info.is_holiday,
|
||
holiday_label=season_ctrl.info.holiday_label,
|
||
)
|
||
|
||
# Sonraki satır: bina ısı / dış ısı / F-B detayları için kullanılacak
|
||
return line_no
|
||
|
||
def _fmt_temp(val: Optional[float]) -> str:
|
||
return "None" if val is None else f"{val:.2f}"
|
||
PUMP_SHORT_MAP = {
|
||
"circulation_a": "A",
|
||
"circulation_b": "B",
|
||
"circ_1": "A",
|
||
"circ_2": "B",
|
||
}
|
||
|
||
def _short_pump_name(ch: str) -> str:
|
||
if ch in PUMP_SHORT_MAP:
|
||
return PUMP_SHORT_MAP[ch]
|
||
# sonu _a/_b ise yine yakala
|
||
if ch.endswith("_a"):
|
||
return "A"
|
||
if ch.endswith("_b"):
|
||
return "B"
|
||
return ch # tanımıyorsak orijinal ismi yaz
|
||
|
||
def log_burner_header(
|
||
now: datetime,
|
||
mode: str,
|
||
season,
|
||
building_avg: Optional[float],
|
||
outside_c: Optional[float],
|
||
used_out_c: Optional[float],
|
||
fire_sp: float,
|
||
burner_on: bool,
|
||
pumps_on,
|
||
line_temps: Optional[Dict[str, Optional[float]]] = None,
|
||
ign_stats=None,
|
||
circ_stats=None,
|
||
) -> None:
|
||
"""
|
||
BurnerController'dan tek çağrıyla BRULOR bloğunu basar.
|
||
|
||
- Önce üst blok (versiyon + güneş + mevsim + tatil)
|
||
- Sonra bina ısı satırı
|
||
- Dış ısı / used dış ısı
|
||
- Son satırda brülör ve pompaların durumu
|
||
"""
|
||
#print("log_burner_header CALLED", season)
|
||
# 1) Üst header blok
|
||
if season is None:
|
||
# SeasonController yoksa, sadece versiyon ve zaman bas
|
||
line_no = 1
|
||
v_str = _format_version_3part(__version__)
|
||
body = f"************** {v_str} {now.strftime('%Y-%m-%d %H:%M:%S')} *************"
|
||
send_legacy_syslog(format_line(line_no, body))
|
||
line_no += 1
|
||
else:
|
||
line_no = emit_top_block(now, season)
|
||
|
||
# 2) Bina ısı satırı
|
||
if building_avg is None:
|
||
min_s = "None"
|
||
avg_s = "None"
|
||
max_s = "None"
|
||
else:
|
||
# Şimdilik min=avg=max gibi davranalım; ileride gerçek min/max eklenebilir
|
||
min_s = f"{building_min:5.2f}"
|
||
avg_s = f"{building_avg:5.2f}"
|
||
max_s = f"{building_max:5.2f}"
|
||
|
||
# config’teki mod
|
||
cfg_mode = getattr(cfg, "BUILD_BURNER", "?") if cfg is not None else "?"
|
||
body = f"Build [{mode}-{cfg_mode}] Heats[Min:{min_s}°C Avg:{avg_s}°C Max:{max_s}°C]"
|
||
send_legacy_syslog(format_line(line_no, body))
|
||
line_no += 1
|
||
|
||
# line_temps yoksa, burayı pas geç
|
||
if line_temps is not None:
|
||
# CONFIG'TEN ID'LERİ AL
|
||
outside_id = getattr(cfg, "OUTSIDE_SENSOR_ID", None) if cfg is not None else None
|
||
out_id = getattr(cfg, "BURNER_OUT_SENSOR_ID", None) if cfg is not None else None
|
||
ret_ids = getattr(cfg, "RETURN_LINE_SENSOR_IDS", []) if cfg is not None else []
|
||
ret_map = getattr(cfg, "RETURN_LINE_SENSOR_NAME_MAP", {}) if cfg is not None else {}
|
||
line_no = 4 # dış ısı satırı numarası
|
||
|
||
# 4: Dis isi
|
||
if outside_id and outside_id in line_temps:
|
||
t = line_temps.get(outside_id)
|
||
namex = getattr(cfg, "OUTSIDE_SENSOR_NAME", "Dis isi") if cfg is not None else "Dis isi"
|
||
msg = f"{namex:<15.15}: {_fmt_temp(t)}°C - {outside_id} "
|
||
send_legacy_syslog(format_line(line_no, msg))
|
||
line_no += 1
|
||
|
||
# 5: Cikis isi
|
||
if out_id and out_id in line_temps:
|
||
t = line_temps.get(out_id)
|
||
namex = getattr(cfg, "BURNER_OUT_SENSOR_NAME", "Cikis isi") if cfg is not None else "Cıkıs isi"
|
||
msg = f"{namex:<15.15}: {_fmt_temp(t)}°C - {out_id} "
|
||
send_legacy_syslog(format_line(line_no, msg))
|
||
line_no += 1
|
||
|
||
# 6..N: Donus isi X
|
||
namex = getattr(cfg, "RETURN_LINE_SENSOR_NAME_MAP",[])
|
||
for sid in ret_ids:
|
||
if sid not in line_temps:
|
||
continue
|
||
t = line_temps.get(sid)
|
||
try:
|
||
namexx = ret_map.get(sid)
|
||
except:
|
||
namex = '???'
|
||
msg = f"{namexx:<15.15}: {_fmt_temp(t)}°C - {sid} "
|
||
send_legacy_syslog(format_line(line_no, msg))
|
||
line_no += 1
|
||
|
||
# 3) Dış ısı / used dış ısı
|
||
out_str = "--"
|
||
used_str = "--"
|
||
if outside_c is not None:
|
||
out_str = f"{outside_c:5.2f}"
|
||
if used_out_c is not None:
|
||
used_str = f"{used_out_c:5.2f}"
|
||
usedxx = "Sistem Isı"
|
||
|
||
#------------------------------------------------------------------
|
||
# 9: Sistem Isı - Used + [WEEKEND_HEAT_BOOST_C, BURNER_COMFORT_OFFSET_C]
|
||
# ------------------------------------------------------------------
|
||
used_val = used_out_c if used_out_c is not None else None
|
||
used_str = "None" if used_val is None else f"{used_val:.2f}"
|
||
|
||
if cfv is not None:
|
||
w_val = float(getattr(cfv, "WEEKEND_HEAT_BOOST_C", 0.0) or 0.0)
|
||
c_val = float(getattr(cfv, "BURNER_COMFORT_OFFSET_C", 0.0) or 0.0)
|
||
else:
|
||
w_val = 0.0
|
||
c_val = 0.0
|
||
|
||
# Sayıları [2, 1] gibi, gereksiz .0’sız yazalım
|
||
def _fmt_num(x: float) -> str:
|
||
if x == int(x):
|
||
return str(int(x))
|
||
return f"{x:g}"
|
||
|
||
sabitler_str = f"[w:{_fmt_num(w_val)} c:{_fmt_num(c_val)}]"
|
||
|
||
body = f"{usedxx:<15.15}: {used_str}°C {sabitler_str} "
|
||
send_legacy_syslog(format_line(line_no, body))
|
||
line_no += 1
|
||
|
||
# ------------------------------------------------------------------
|
||
# 11: Brülör Motor satırı (MAX_OUTLET_C ile)
|
||
# ------------------------------------------------------------------
|
||
if cfv is not None:
|
||
max_out = float(getattr(cfv, "MAX_OUTLET_C", 45.0) or 45.0)
|
||
else:
|
||
max_out = 45.0
|
||
|
||
if cfv is not None:
|
||
min_ret = float(getattr(cfv, "CIRCULATION_MIN_RETURN_C", 25.0) or 25.0)
|
||
else:
|
||
min_ret = 25.0
|
||
|
||
br_status = "<CALISIYOR>" if burner_on else "<CALISMIYOR>"
|
||
br_flag = 1 if burner_on else 0
|
||
ign_sw = 0
|
||
ign_total = "00:00:00"
|
||
ign_today = "00:00:00"
|
||
if ign_stats:
|
||
ign_sw = ign_stats.get("switch_count", 0)
|
||
ign_total = ign_stats.get("total_on_str", "00:00:00")
|
||
ign_today = ign_stats.get("today_on_str", "00:00:00")
|
||
# Eski stile benzeteceğiz:
|
||
# [ 11 Brulor Motor : <CALISMIYOR> [0] 0 00:00:00 00:00:00 L:45.0 ]
|
||
body11 = (
|
||
f"Brulor Motor : {br_status} "
|
||
f"[{br_flag}] {ign_sw} {ign_total} {ign_today} L:{max_out:.1f}"
|
||
)
|
||
send_legacy_syslog(format_line(line_no, body11))
|
||
line_no += 1
|
||
# ------------------------------------------------------------------
|
||
# 12: Devirdaim Motor satırı (CIRCULATION_MIN_RETURN_C ile)
|
||
# ------------------------------------------------------------------
|
||
ch_to_logical = {}
|
||
pumps_on_list = list(pumps_on) if pumps_on else []
|
||
|
||
# --- circulation mapping: channel -> logical ('circ_1', 'circ_2') ---
|
||
ch_to_logical = {}
|
||
cfg_groups = getattr(cfg, "BURNER_GROUPS", {})
|
||
# ileride çoklu brülör olursa buraya burner_id parametresi de geçirsin istersen
|
||
grp = cfg_groups.get(0, {})
|
||
circ_cfg = grp.get("circulation", {}) or {}
|
||
|
||
for logical_name, info in circ_cfg.items():
|
||
ch = info.get("channel")
|
||
if ch:
|
||
ch_to_logical[ch] = logical_name
|
||
|
||
# Config’te default=1 olan pompaları da topla (cfg_default_pumps)
|
||
cfg_default_pumps = []
|
||
for logical_name, info in circ_cfg.items():
|
||
ch = info.get("channel")
|
||
if ch and info.get("default", 0):
|
||
cfg_default_pumps.append(ch)
|
||
|
||
# Kısa isim A/B istersek:
|
||
def _logical_to_short(name: str) -> str:
|
||
if name == "circ_1":
|
||
return "A"
|
||
if name == "circ_2":
|
||
return "B"
|
||
return name
|
||
|
||
pump_count = len(cfg_default_pumps)
|
||
dev_status = "<CALISIYOR>" if pump_count > 0 else "<CALISMIYOR>"
|
||
|
||
pump_labels = []
|
||
for ch in cfg_default_pumps:
|
||
logical = ch_to_logical.get(ch)
|
||
if logical is not None:
|
||
pump_labels.append(_logical_to_short(logical))
|
||
else:
|
||
pump_labels.append(ch)
|
||
|
||
pumps_str = ",".join(pump_labels) if pump_labels else "-"
|
||
cir_sw = 0
|
||
cir_total = "00:00:00"
|
||
cir_today = "00:00:00"
|
||
if circ_stats:
|
||
cir_sw = circ_stats.get("switch_count", 0)
|
||
cir_total = circ_stats.get("total_on_str", "00:00:00")
|
||
cir_today = circ_stats.get("today_on_str", "00:00:00")
|
||
# [ 12 Devirdaim Mot: <CALISMIYOR> [0] 0 00:00:00 00:00:00 L:25.0]
|
||
body12 = (
|
||
f"Devirdaim Mot: {dev_status} "
|
||
f"[{pump_count}] {br_flag}] {cir_sw} {cir_total} {cir_today} L:{pumps_str} {min_ret:.1f}"
|
||
)
|
||
send_legacy_syslog(format_line(line_no, body12))
|
||
line_no += 1
|
||
|
||
|
||
return line_no
|
||
|
||
|
||
|
||
# ----------------------------------------------------------------------
|
||
# Örnek kullanım (standalone test)
|
||
# ----------------------------------------------------------------------
|
||
if __name__ == "__main__":
|
||
# Bu blok sadece modülü tek başına test etmek için:
|
||
# python3 -m ebuild.io.legacy_syslog
|
||
if SeasonController is None:
|
||
raise SystemExit("SeasonController import edilemedi (test ortamı).")
|
||
|
||
now = datetime.now()
|
||
# SeasonController.from_now() kullanıyorsan:
|
||
try:
|
||
season = SeasonController.from_now()
|
||
except Exception as e:
|
||
raise SystemExit(f"SeasonController.from_now() hata: {e}")
|
||
|
||
next_line = emit_top_block(now, season)
|
||
|
||
# Test için bina ısısını dummy bas:
|
||
body = "Bina Isı : [ 20.10 - 22.30 - 24.50 ]"
|
||
send_legacy_syslog(format_line(next_line, body))
|