add daemon mode
This commit is contained in:
+60
-1
@@ -1,11 +1,45 @@
|
||||
import csv
|
||||
import json
|
||||
import os
|
||||
import math
|
||||
import struct
|
||||
from typing import Iterable, List
|
||||
from typing import List, Optional
|
||||
|
||||
from .models import PowerMeterReadings
|
||||
|
||||
|
||||
class ReadingsCsvLogger:
|
||||
def __init__(self, file_path: str, fieldnames: List[str]):
|
||||
self._file_path = file_path
|
||||
self._fieldnames = fieldnames
|
||||
self._fp = None
|
||||
self._writer: Optional[csv.DictWriter] = None
|
||||
|
||||
def __enter__(self) -> "ReadingsCsvLogger":
|
||||
is_new_file = (not os.path.exists(self._file_path)) or os.path.getsize(self._file_path) == 0
|
||||
self._fp = open(self._file_path, "a", newline="", encoding="utf-8")
|
||||
self._writer = csv.DictWriter(self._fp, fieldnames=self._fieldnames)
|
||||
if is_new_file:
|
||||
self._writer.writeheader()
|
||||
self._fp.flush()
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc, tb) -> None:
|
||||
if self._fp is not None:
|
||||
try:
|
||||
self._fp.flush()
|
||||
finally:
|
||||
self._fp.close()
|
||||
self._fp = None
|
||||
self._writer = None
|
||||
|
||||
def log(self, row: dict) -> None:
|
||||
if self._writer is None or self._fp is None:
|
||||
raise RuntimeError("CSV logger is not open")
|
||||
self._writer.writerow(row)
|
||||
self._fp.flush()
|
||||
|
||||
|
||||
class PowerMeterDataHandler:
|
||||
"""Parses Modbus RTU responses and computes derived power metrics."""
|
||||
|
||||
@@ -90,6 +124,31 @@ class PowerMeterDataHandler:
|
||||
def print_readings_json(self, readings: PowerMeterReadings) -> None:
|
||||
print(self.readings_to_json(readings, ensure_ascii=False))
|
||||
|
||||
def open_csv_logger(self, file_path: str) -> ReadingsCsvLogger:
|
||||
fieldnames = ["timestamp"] + self._flat_readings_keys()
|
||||
return ReadingsCsvLogger(file_path=file_path, fieldnames=fieldnames)
|
||||
|
||||
@staticmethod
|
||||
def _flat_readings_keys() -> List[str]:
|
||||
# Keep order stable for CSV columns.
|
||||
return [
|
||||
"active_power_w",
|
||||
"rms_current_a",
|
||||
"voltage_v",
|
||||
"frequency_hz",
|
||||
"power_factor",
|
||||
"annual_power_consumption_kwh",
|
||||
"active_consumption_kwh",
|
||||
"reactive_consumption_kwh",
|
||||
"load_time_hours",
|
||||
"work_hours_per_day",
|
||||
"device_address",
|
||||
"apparent_power_vi_va",
|
||||
"apparent_power_pf_va",
|
||||
"reactive_power_var",
|
||||
"apparent_consumption_kvah",
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def _bytes_to_registers_be(data: bytes) -> List[int]:
|
||||
registers: List[int] = []
|
||||
|
||||
Reference in New Issue
Block a user