From 9438ab45321970fd79f6b84292d9405153a57852 Mon Sep 17 00:00:00 2001 From: LKuemmel <76958050+LKuemmel@users.noreply.github.com> Date: Wed, 20 Dec 2023 08:31:03 +0100 Subject: [PATCH] Unix timestamps (#753) * unix timestamps * fix * fix * fix --- packages/conftest.py | 2 +- .../control/algorithm/filter_chargepoints.py | 3 +- .../integration_test/pv_charging_test.py | 12 +-- packages/control/auto_phase_switch_test.py | 28 +++--- packages/control/chargelog.py | 7 +- packages/control/chargepoint/chargepoint.py | 8 +- .../control/chargepoint/control_parameter.py | 6 +- packages/control/counter_test.py | 12 +-- packages/control/ev.py | 2 +- packages/control/ev_test.py | 13 +-- packages/control/general.py | 2 +- .../measurement_logging/write_log.py | 2 +- packages/helpermodules/modbusserver.py | 2 +- packages/helpermodules/setdata.py | 35 ++++--- packages/helpermodules/timecheck.py | 95 +++---------------- packages/helpermodules/timecheck_test.py | 45 --------- packages/helpermodules/update_config.py | 12 ++- packages/main.py | 2 +- .../external_openwb/chargepoint_module.py | 2 +- packages/modules/common/component_state.py | 4 +- .../modules/common/configurable_vehicle.py | 2 +- packages/modules/configuration_test.py | 3 +- .../internal_chargepoint_handler.py | 4 +- packages/test_utils/test_environment.py | 6 ++ 24 files changed, 111 insertions(+), 198 deletions(-) create mode 100644 packages/test_utils/test_environment.py diff --git a/packages/conftest.py b/packages/conftest.py index cf2d3174b5..9c09f72b8a 100644 --- a/packages/conftest.py +++ b/packages/conftest.py @@ -12,7 +12,7 @@ def mock_today(monkeypatch) -> None: datetime_mock.today.return_value = datetime.datetime(2022, 5, 16, 8, 40, 52) monkeypatch.setattr(datetime, "datetime", datetime_mock) mock_today_timestamp = Mock(return_value=1652683252) - monkeypatch.setattr(timecheck, "create_timestamp_unix", mock_today_timestamp) + monkeypatch.setattr(timecheck, "create_timestamp", mock_today_timestamp) @pytest.fixture(autouse=True) diff --git a/packages/control/algorithm/filter_chargepoints.py b/packages/control/algorithm/filter_chargepoints.py index 804e00060e..4f236975dc 100644 --- a/packages/control/algorithm/filter_chargepoints.py +++ b/packages/control/algorithm/filter_chargepoints.py @@ -5,7 +5,6 @@ from control import data from control.algorithm import common from control.chargepoint.chargepoint import Chargepoint -from helpermodules.timecheck import convert_to_unix_timestamp log = logging.getLogger(__name__) @@ -95,7 +94,7 @@ def _get_preferenced_chargepoint(valid_chargepoints: List[Chargepoint]) -> List: chargepoints.update( (cp, cp.data.set.charging_ev_data.data.get.soc) for cp in chargepoints.keys()) elif condition_types[condition] == "plug_in": - chargepoints.update((cp, convert_to_unix_timestamp(cp.data.set.plug_time)) + chargepoints.update((cp, cp.data.set.plug_time) for cp in chargepoints.keys()) elif condition_types[condition] == "imported_since_plugged": chargepoints.update((cp, cp.data.set.log.imported_since_plugged) for cp in chargepoints.keys()) diff --git a/packages/control/algorithm/integration_test/pv_charging_test.py b/packages/control/algorithm/integration_test/pv_charging_test.py index 8fef72a82c..90f34a4932 100644 --- a/packages/control/algorithm/integration_test/pv_charging_test.py +++ b/packages/control/algorithm/integration_test/pv_charging_test.py @@ -127,9 +127,9 @@ def test_start_pv_delay(all_cp_pv_charging_3p, all_cp_not_charging, monkeypatch) for i in range(3, 6): assert data.data.cp_data[f"cp{i}"].data.set.current == 0 assert data.data.cp_data[ - "cp3"].data.control_parameter.timestamp_switch_on_off == "05/16/2022, 08:40:52" + "cp3"].data.control_parameter.timestamp_switch_on_off == 1652683252.0 assert data.data.cp_data[ - "cp4"].data.control_parameter.timestamp_switch_on_off == "05/16/2022, 08:40:52" + "cp4"].data.control_parameter.timestamp_switch_on_off == 1652683252.0 assert data.data.cp_data[ "cp5"].data.control_parameter.timestamp_switch_on_off is None assert data.data.counter_data["counter0"].data.set.raw_power_left == 31775 @@ -145,12 +145,12 @@ def test_pv_delay_expired(all_cp_pv_charging_3p, all_cp_not_charging, monkeypatc data.data.counter_data["counter6"].data.set.raw_currents_left = [16, 12, 14] data.data.counter_data["counter0"].data.set.reserved_surplus = 9000 data.data.cp_data[ - "cp3"].data.control_parameter.timestamp_switch_on_off = "05/16/2022, 08:39:45" + "cp3"].data.control_parameter.timestamp_switch_on_off = 1652683185.0 data.data.cp_data[ "cp3"].data.control_parameter.state = ChargepointState.SWITCH_ON_DELAY # nicht genug Überschuss für beide data.data.cp_data[ - "cp4"].data.control_parameter.timestamp_switch_on_off = "05/16/2022, 08:40:52" + "cp4"].data.control_parameter.timestamp_switch_on_off = 1652683252.0 data.data.cp_data[ "cp4"].data.control_parameter.state = ChargepointState.SWITCH_ON_DELAY data.data.cp_data[ @@ -239,7 +239,7 @@ def test_surplus(params: ParamsSurplus, all_cp_pv_charging_3p, all_cp_charging_3 raw_power_left=32580, raw_currents_left_counter0=[40]*3, raw_currents_left_counter6=[16]*3, - expected_timestamp_auto_phase_switch_cp3="05/16/2022, 08:40:52", + expected_timestamp_auto_phase_switch_cp3=1652683252.0, expected_timestamp_auto_phase_switch_cp4=None, expected_timestamp_auto_phase_switch_cp5=None, expected_current_cp3=10, @@ -253,7 +253,7 @@ def test_surplus(params: ParamsSurplus, all_cp_pv_charging_3p, all_cp_charging_3 raw_power_left=42580, raw_currents_left_counter0=[40]*3, raw_currents_left_counter6=[16]*3, - expected_timestamp_auto_phase_switch_cp3="05/16/2022, 08:40:52", + expected_timestamp_auto_phase_switch_cp3=1652683252.0, expected_timestamp_auto_phase_switch_cp4=None, expected_timestamp_auto_phase_switch_cp5=None, expected_current_cp3=32, diff --git a/packages/control/auto_phase_switch_test.py b/packages/control/auto_phase_switch_test.py index 75dfa0bc05..e832606863 100644 --- a/packages/control/auto_phase_switch_test.py +++ b/packages/control/auto_phase_switch_test.py @@ -31,7 +31,7 @@ class Params: def __init__(self, name: str, max_current_single_phase: int, - timestamp_auto_phase_switch: Optional[str], + timestamp_auto_phase_switch: Optional[float], phases_to_use: int, required_current: float, evu_surplus: int, @@ -43,7 +43,7 @@ def __init__(self, expected_current: float, expected_state: ChargepointState, expected_message: Optional[str] = None, - expected_timestamp_auto_phase_switch: Optional[str] = None) -> None: + expected_timestamp_auto_phase_switch: Optional[float] = None) -> None: self.name = name self.max_current_single_phase = max_current_single_phase self.timestamp_auto_phase_switch = timestamp_auto_phase_switch @@ -66,28 +66,28 @@ def __init__(self, phases_to_use=1, required_current=6, evu_surplus=-800, reserved_evu_overhang=0, get_currents=[15.6, 0, 0], get_power=3450, state=ChargepointState.CHARGING_ALLOWED, expected_phases_to_use=1, expected_current=6, expected_message="Umschaltverzögerung von 1 auf 3 Phasen für 7.0 Min aktiv.", - expected_timestamp_auto_phase_switch="05/16/2022, 08:40:52", + expected_timestamp_auto_phase_switch=1652683252.0, expected_state=ChargepointState.PHASE_SWITCH_DELAY), Params("1to3, not enough power, start timer", max_current_single_phase=16, timestamp_auto_phase_switch=None, phases_to_use=1, required_current=6, evu_surplus=-300, reserved_evu_overhang=0, get_currents=[15.6, 0, 0], get_power=3450, state=ChargepointState.CHARGING_ALLOWED, expected_phases_to_use=1, expected_current=6, expected_state=ChargepointState.CHARGING_ALLOWED), Params("1to3, enough power, timer not expired", max_current_single_phase=16, - timestamp_auto_phase_switch="05/16/2022, 08:35:52", phases_to_use=1, required_current=6, + timestamp_auto_phase_switch=1652682952.0, phases_to_use=1, required_current=6, evu_surplus=-1200, reserved_evu_overhang=460, get_currents=[15.6, 0, 0], get_power=3450, state=ChargepointState.PHASE_SWITCH_DELAY, expected_phases_to_use=1, expected_current=6, expected_message="Umschaltverzögerung von 1 auf 3 Phasen für 7.0 Min aktiv.", - expected_timestamp_auto_phase_switch="05/16/2022, 08:40:52", + expected_timestamp_auto_phase_switch=1652683252.0, expected_state=ChargepointState.PHASE_SWITCH_DELAY), Params("1to3, not enough power, timer not expired", max_current_single_phase=16, - timestamp_auto_phase_switch="05/16/2022, 08:35:52", phases_to_use=1, required_current=6, + timestamp_auto_phase_switch=1652682952.0, phases_to_use=1, required_current=6, evu_surplus=0, reserved_evu_overhang=460, get_currents=[15.6, 0, 0], get_power=3450, state=ChargepointState.PHASE_SWITCH_DELAY, expected_phases_to_use=1, expected_current=6, expected_message=f"Umschaltverzögerung von 1 auf 3 Phasen abgebrochen{Ev.NOT_ENOUGH_POWER}", - expected_timestamp_auto_phase_switch="05/16/2022, 08:40:52", + expected_timestamp_auto_phase_switch=1652683252.0, expected_state=ChargepointState.CHARGING_ALLOWED), Params("1to3, enough power, timer expired", max_current_single_phase=16, - timestamp_auto_phase_switch="05/16/2022, 08:32:52", phases_to_use=1, required_current=6, + timestamp_auto_phase_switch=1652682772.0, phases_to_use=1, required_current=6, evu_surplus=-1200, reserved_evu_overhang=460, get_currents=[15.6, 0, 0], get_power=3450, state=ChargepointState.PHASE_SWITCH_DELAY, expected_phases_to_use=3, expected_current=6, expected_state=ChargepointState.PHASE_SWITCH_DELAY_EXPIRED), @@ -97,25 +97,25 @@ def __init__(self, get_currents=[4.5, 4.4, 5.8], get_power=3381, state=ChargepointState.CHARGING_ALLOWED, expected_phases_to_use=3, expected_current=6, expected_message="Umschaltverzögerung von 3 auf 1 Phasen für 9.0 Min aktiv.", - expected_timestamp_auto_phase_switch="05/16/2022, 08:40:52", + expected_timestamp_auto_phase_switch=1652683252.0, expected_state=ChargepointState.PHASE_SWITCH_DELAY), Params("3to1, not enough power, timer not expired", max_current_single_phase=16, - timestamp_auto_phase_switch="05/16/2022, 08:35:52", + timestamp_auto_phase_switch=1652682952.0, phases_to_use=3, required_current=6, evu_surplus=0, reserved_evu_overhang=-460, get_currents=[4.5, 4.4, 5.8], get_power=3381, state=ChargepointState.PHASE_SWITCH_DELAY, expected_phases_to_use=3, expected_current=6, expected_message="Umschaltverzögerung von 3 auf 1 Phasen für 9.0 Min aktiv.", - expected_timestamp_auto_phase_switch="05/16/2022, 08:40:52", + expected_timestamp_auto_phase_switch=1652683252.0, expected_state=ChargepointState.PHASE_SWITCH_DELAY), Params("3to1, enough power, timer not expired", max_current_single_phase=16, - timestamp_auto_phase_switch="05/16/2022, 08:35:52", phases_to_use=3, required_current=6, + timestamp_auto_phase_switch=1652682952.0, phases_to_use=3, required_current=6, evu_surplus=-860, reserved_evu_overhang=0, get_currents=[4.5, 4.4, 5.8], get_power=3381, state=ChargepointState.PHASE_SWITCH_DELAY, expected_phases_to_use=3, expected_current=6, expected_message=f"Umschaltverzögerung von 3 auf 1 Phasen abgebrochen{Ev.ENOUGH_POWER}", - expected_timestamp_auto_phase_switch="05/16/2022, 08:40:52", + expected_timestamp_auto_phase_switch=1652683252.0, expected_state=ChargepointState.CHARGING_ALLOWED), Params("3to1, not enough power, timer expired", max_current_single_phase=16, - timestamp_auto_phase_switch="05/16/2022, 08:29:52", phases_to_use=3, required_current=6, + timestamp_auto_phase_switch=1652682592.0, phases_to_use=3, required_current=6, evu_surplus=0, reserved_evu_overhang=-460, get_currents=[4.5, 4.4, 5.8], get_power=3381, state=ChargepointState.PHASE_SWITCH_DELAY, expected_phases_to_use=1, expected_current=16, expected_state=ChargepointState.PHASE_SWITCH_DELAY_EXPIRED), diff --git a/packages/control/chargelog.py b/packages/control/chargelog.py index f08c330b83..55f8ecfd52 100644 --- a/packages/control/chargelog.py +++ b/packages/control/chargelog.py @@ -1,3 +1,4 @@ +from datetime import datetime import json import logging import math @@ -53,7 +54,7 @@ def collect_data(chargepoint): f"counter {chargepoint.data.get.imported}") log_data.range_charged = log_data.imported_since_mode_switch / \ charging_ev.ev_template.data.average_consump * 100 - log_data.time_charged, _ = timecheck.get_difference_to_now(log_data.timestamp_start_charging) + log_data.time_charged = timecheck.get_difference_to_now(log_data.timestamp_start_charging)[0] Pub().pub(f"openWB/set/chargepoint/{chargepoint.num}/set/log", asdict(log_data)) except Exception: log.exception("Fehler im Ladelog-Modul") @@ -111,8 +112,8 @@ def save_data(chargepoint, charging_ev, immediately: bool = True, reset: bool = }, "time": { - "begin": log_data.timestamp_start_charging, - "end": timecheck.create_timestamp(), + "begin": datetime.fromtimestamp(log_data.timestamp_start_charging).strftime("%m/%d/%Y, %H:%M:%S"), + "end": datetime.fromtimestamp(timecheck.create_timestamp()).strftime("%m/%d/%Y, %H:%M:%S"), "time_charged": log_data.time_charged }, "data": diff --git a/packages/control/chargepoint/chargepoint.py b/packages/control/chargepoint/chargepoint.py index b54ac6c5b1..b760e28044 100644 --- a/packages/control/chargepoint/chargepoint.py +++ b/packages/control/chargepoint/chargepoint.py @@ -119,8 +119,8 @@ class Log: imported_since_mode_switch: float = 0 imported_since_plugged: float = 0 range_charged: float = 0 - time_charged: str = "00:00" - timestamp_start_charging: Optional[str] = None + time_charged: float = 0 + timestamp_start_charging: Optional[float] = None def connected_vehicle_factory() -> ConnectedVehicle: @@ -142,7 +142,7 @@ class Get: phases_in_use: int = 0 plug_state: bool = False power: float = 0 - rfid_timestamp: Optional[str] = None + rfid_timestamp: Optional[float] = None rfid: Optional[str] = None soc: Optional[float] = None soc_timestamp: Optional[int] = None @@ -170,7 +170,7 @@ class Set: manual_lock: bool = False phases_to_use: int = 0 plug_state_prev: bool = False - plug_time: Optional[str] = None + plug_time: Optional[float] = None required_power: float = 0 rfid: Optional[str] = None target_current: float = 0 # Sollstrom aus fest vorgegebener Stromstärke diff --git a/packages/control/chargepoint/control_parameter.py b/packages/control/chargepoint/control_parameter.py index 1f40c982d1..fc151b9e7b 100644 --- a/packages/control/chargepoint/control_parameter.py +++ b/packages/control/chargepoint/control_parameter.py @@ -44,13 +44,13 @@ class ControlParameter: submode: Chargemode_enum = field( default=Chargemode_enum.STOP, metadata={"topic": "control_parameter/submode", "mutable_by_algorithm": True}) - timestamp_auto_phase_switch: Optional[str] = field( + timestamp_auto_phase_switch: Optional[float] = field( default=None, metadata={"topic": "control_parameter/timestamp_auto_phase_switch", "mutable_by_algorithm": True}) - timestamp_perform_phase_switch: Optional[str] = field( + timestamp_perform_phase_switch: Optional[float] = field( default=None, metadata={"topic": "control_parameter/timestamp_perform_phase_switch", "mutable_by_algorithm": True}) - timestamp_switch_on_off: Optional[str] = field( + timestamp_switch_on_off: Optional[float] = field( default=None, metadata={"topic": "control_parameter/timestamp_switch_on_off", "mutable_by_algorithm": True}) diff --git a/packages/control/counter_test.py b/packages/control/counter_test.py index 0ef2fb9409..e09591894a 100644 --- a/packages/control/counter_test.py +++ b/packages/control/counter_test.py @@ -102,23 +102,23 @@ class Params: cases = [ Params("Einschaltschwelle wurde unterschritten, Timer zurücksetzen", False, 1500, -119, - 1500, '05/16/2022, 08:40:50', ChargepointState.SWITCH_ON_DELAY, + 1500, 1652683250.0, ChargepointState.SWITCH_ON_DELAY, Counter.SWITCH_ON_FALLEN_BELOW.format(1500), None, 0), Params("Timer starten", False, 0, 1501, 1500, None, ChargepointState.NO_CHARGING_ALLOWED, - Counter.SWITCH_ON_WAITING.format(30), '05/16/2022, 08:40:52', 1500), + Counter.SWITCH_ON_WAITING.format(30), 1652683252.0, 1500), Params("Einschaltschwelle nicht erreicht", False, 0, 1499, 1500, None, ChargepointState.NO_CHARGING_ALLOWED, Counter.SWITCH_ON_NOT_EXCEEDED.format(1500), None, 0), Params("Einschaltschwelle läuft", False, 1500, 121, 1500, - '05/16/2022, 08:40:50', ChargepointState.SWITCH_ON_DELAY, None, '05/16/2022, 08:40:50', 1500), + 1652683250.0, ChargepointState.SWITCH_ON_DELAY, None, 1652683250.0, 1500), Params("Feed_in_limit, Einschaltschwelle wurde unterschritten, Timer zurücksetzen", True, 1500, - -681, 15000, '05/16/2022, 08:40:50', ChargepointState.SWITCH_ON_DELAY, + -681, 15000, 1652683250.0, ChargepointState.SWITCH_ON_DELAY, Counter.SWITCH_ON_FALLEN_BELOW.format(1500), None, 0), Params("Feed_in_limit, Timer starten", True, 0, 15001, 15000, None, ChargepointState.NO_CHARGING_ALLOWED, - Counter.SWITCH_ON_WAITING.format(30), '05/16/2022, 08:40:52', 1500), + Counter.SWITCH_ON_WAITING.format(30), 1652683252.0, 1500), Params("Feed_in_limit, Einschaltschwelle nicht erreicht", True, 0, 14999, 15000, None, ChargepointState.NO_CHARGING_ALLOWED, Counter.SWITCH_ON_NOT_EXCEEDED.format(1500), None, 0), Params("Feed_in_limit, Einschaltschwelle läuft", True, 1500, 15001, - 15000, '05/16/2022, 08:40:50', ChargepointState.SWITCH_ON_DELAY, None, '05/16/2022, 08:40:50', 1500), + 15000, 1652683250.0, ChargepointState.SWITCH_ON_DELAY, None, 1652683250.0, 1500), ] diff --git a/packages/control/ev.py b/packages/control/ev.py index dd47350a1b..62dc486964 100644 --- a/packages/control/ev.py +++ b/packages/control/ev.py @@ -157,7 +157,7 @@ def set_factory() -> Set: @dataclass class Get: soc: int = 0 - soc_timestamp: Optional[str] = None + soc_timestamp: float = 0 force_soc_update: bool = False range: float = 0 fault_state: int = 0 diff --git a/packages/control/ev_test.py b/packages/control/ev_test.py index d8e076ee18..1f3078165f 100644 --- a/packages/control/ev_test.py +++ b/packages/control/ev_test.py @@ -1,3 +1,4 @@ +from typing import Optional from unittest.mock import Mock import pytest @@ -11,15 +12,15 @@ @pytest.mark.parametrize( "check_timestamp, charge_state, soc_timestamp, expected_request_soc", - [pytest.param(False, False, "", True, id="no soc_timestamp"), - pytest.param(True, False, "2022/05/16, 8:30:52", False, id="not charging, not expired"), - pytest.param(False, False, "2022/05/15, 20:30:52", True, id="not charging, expired"), - pytest.param(True, True, "2022/05/16, 8:36:52", False, id="charging, not expired"), - pytest.param(False, True, "2022/05/16, 8:35:50", True, id="charging, expired"), + [pytest.param(False, False, None, True, id="no soc_timestamp"), + pytest.param(True, False, 100, False, id="not charging, not expired"), + pytest.param(False, False, 100, True, id="not charging, expired"), + pytest.param(True, True, 100, False, id="charging, not expired"), + pytest.param(False, True, 100, True, id="charging, expired"), ]) def test_soc_interval_expired(check_timestamp: bool, charge_state: bool, - soc_timestamp: str, + soc_timestamp: Optional[float], expected_request_soc: bool, monkeypatch): # setup diff --git a/packages/control/general.py b/packages/control/general.py index eebd93ac33..ec76cfe561 100644 --- a/packages/control/general.py +++ b/packages/control/general.py @@ -103,7 +103,7 @@ class GeneralData: grid_protection_active: bool = False grid_protection_configured: bool = True grid_protection_random_stop: int = 0 - grid_protection_timestamp: Optional[str] = "" + grid_protection_timestamp: Optional[float] = "" mqtt_bridge: bool = False price_kwh: float = 0.3 range_unit: str = "km" diff --git a/packages/helpermodules/measurement_logging/write_log.py b/packages/helpermodules/measurement_logging/write_log.py index 8a1b4259a1..fd76ebe8c2 100644 --- a/packages/helpermodules/measurement_logging/write_log.py +++ b/packages/helpermodules/measurement_logging/write_log.py @@ -95,7 +95,7 @@ def save_log(folder): date = timecheck.create_timestamp_time() else: date = timecheck.create_timestamp_YYYYMMDD() - current_timestamp = timecheck.create_timestamp_unix() + current_timestamp = timecheck.create_timestamp() cp_dict = {} for cp in data.data.cp_data: try: diff --git a/packages/helpermodules/modbusserver.py b/packages/helpermodules/modbusserver.py index 6dcdfd7c29..1b9e03a235 100644 --- a/packages/helpermodules/modbusserver.py +++ b/packages/helpermodules/modbusserver.py @@ -86,7 +86,7 @@ def read_data_store(slave_id, function_code, address): """" Return value of address. """ if address > 10099: Pub().pub("openWB/set/internal_chargepoint/global_data", - {"heartbeat": timecheck.create_timestamp_unix(), "parent_ip": None}) + {"heartbeat": timecheck.create_timestamp(), "parent_ip": None}) chargepoint = SubData.internal_chargepoint_data[f"cp{_get_pos(address, 2)}"] askedvalue = int(str(address)[-2:]) if askedvalue == 00: diff --git a/packages/helpermodules/setdata.py b/packages/helpermodules/setdata.py index 2df989a93b..2cbf6100c3 100644 --- a/packages/helpermodules/setdata.py +++ b/packages/helpermodules/setdata.py @@ -133,6 +133,15 @@ def _validate_value(self, msg: mqtt.MQTTMessage, data_type, ranges=[], collectio valid = True else: log.error(f"Payload ungültig: Topic {msg.topic}, Payload {value} sollte ein String sein.") + elif isinstance(data_type, Tuple): + if int in data_type: + if self._validate_min_max_value(value, msg, int, ranges): + valid = True + if float in data_type: + if self._validate_min_max_value(value, msg, float, ranges): + valid = True + if None in data_type and value is None: + valid = True elif data_type == int or data_type == float: if self._validate_min_max_value(value, msg, data_type, ranges) or isinstance(value, type(None)): valid = True @@ -412,7 +421,7 @@ def process_vehicle_topic(self, msg: mqtt.MQTTMessage): "/ev_template" in msg.topic): self._validate_value(msg, int, [(0, float("inf"))]) elif "/get/soc_timestamp" in msg.topic: - self._validate_value(msg, str) + self._validate_value(msg, float) elif "/get/soc" in msg.topic: self._validate_value(msg, float, [(0, 100)]) elif "/get/range" in msg.topic: @@ -521,7 +530,7 @@ def process_chargepoint_topic(self, msg: mqtt.MQTTMessage): self._validate_value(msg, int, [(0, 4)]) elif ("/set/rfid" in msg.topic or "/set/plug_time" in msg.topic): - self._validate_value(msg, str) + self._validate_value(msg, float) elif "/set/log" in msg.topic: self._validate_value(msg, "json") elif "/set/change_ev_permitted" in msg.topic: @@ -543,13 +552,13 @@ def process_chargepoint_topic(self, msg: mqtt.MQTTMessage): self._validate_value(msg, str) elif "/control_parameter/prio" in msg.topic: self._validate_value(msg, bool) - elif ("/control_parameter/timestamp_switch_on_off" in msg.topic or - "/control_parameter/timestamp_auto_phase_switch" in msg.topic or - "/control_parameter/timestamp_perform_phase_switch" in msg.topic or - "/control_parameter/current_plan" in msg.topic): + elif "/control_parameter/current_plan" in msg.topic: self._validate_value(msg, str) elif ("/control_parameter/imported_instant_charging" in msg.topic or - "/control_parameter/imported_at_plan_start" in msg.topic): + "/control_parameter/imported_at_plan_start" in msg.topic or + "/control_parameter/timestamp_switch_on_off" in msg.topic or + "/control_parameter/timestamp_auto_phase_switch" in msg.topic or + "/control_parameter/timestamp_perform_phase_switch" in msg.topic): self._validate_value(msg, float, [(0, float("inf"))]) elif "/control_parameter/state" in msg.topic: self._validate_value(msg, int, [(0, 7)]) @@ -588,11 +597,11 @@ def process_chargepoint_get_topics(self, msg): self._validate_value(msg, float, [(0, 0), (6, 32), (600, 3200)]) elif ("/get/fault_str" in msg.topic or "/get/state_str" in msg.topic or - "/get/heartbeat" in msg.topic): - self._validate_value(msg, str) - elif ("/get/rfid" in msg.topic or - "/get/rfid_timestamp" in msg.topic): + "/get/heartbeat" in msg.topic or + "/get/rfid" in msg.topic): self._validate_value(msg, str) + elif "/get/rfid_timestamp" in msg.topic: + self._validate_value(msg, float) elif ("/get/soc" in msg.topic): self._validate_value(msg, float, [(0, 100)]) else: @@ -749,7 +758,7 @@ def process_general_topic(self, msg: mqtt.MQTTMessage): "openWB/set/general/mqtt_bridge" in msg.topic): self._validate_value(msg, bool) elif "openWB/set/general/grid_protection_timestamp" in msg.topic: - self._validate_value(msg, str) + self._validate_value(msg, float) elif "openWB/set/general/grid_protection_random_stop" in msg.topic: self._validate_value(msg, int, [(0, 90)]) elif "openWB/set/general/notifications/selected" in msg.topic: @@ -966,7 +975,7 @@ def process_system_topic(self, msg: mqtt.MQTTMessage): elif "openWB/set/system/version" in msg.topic: self._validate_value(msg, str) elif "openWB/set/system/time" in msg.topic: - self._validate_value(msg, int) + self._validate_value(msg, float) elif "openWB/set/system/datastore_version" in msg.topic: self._validate_value(msg, int, [(0, UpdateConfig.DATASTORE_VERSION)]) elif "openWB/set/system/GetRemoteSupport" in msg.topic: diff --git a/packages/helpermodules/timecheck.py b/packages/helpermodules/timecheck.py index 00a5876536..5061106c56 100644 --- a/packages/helpermodules/timecheck.py +++ b/packages/helpermodules/timecheck.py @@ -3,7 +3,6 @@ import copy import logging import datetime -from dateutil.relativedelta import relativedelta from typing import Dict, List, Optional, Tuple, TypeVar, Union from helpermodules.abstract_plans import AutolockPlan, ScheduledChargingPlan, TimeChargingPlan @@ -108,13 +107,6 @@ def is_timeframe_valid(now: datetime.datetime, begin: datetime.datetime, end: da return state -def _calc_begin(end: datetime.datetime, hours: int) -> datetime.datetime: - """ berechnet den Zeitpunkt, der die angegebenen Stunden vor dem Endzeitpunkt liegt. - """ - prev = datetime.timedelta(hours) - return end - prev - - def check_duration(plan: ScheduledChargingPlan, duration: float, buffer: int) -> Tuple[Optional[float], bool]: """ prüft, ob der in angegebene Zeitpunkt abzüglich der Dauer jetzt ist. Um etwas Puffer zu haben, werden bei Überschreiten des Zeitpunkts die nachfolgenden 20 Min auch noch als Ladezeit @@ -188,6 +180,7 @@ def _get_remaining_time(now: datetime.datetime, duration: float, end: datetime.d """ delta = datetime.timedelta(hours=int(duration), minutes=((duration % 1) * 60)) start_time = end-delta + log.debug(f"delta {delta} start_time {start_time} end {end} now {now}") return (start_time-now).total_seconds() @@ -219,93 +212,36 @@ def is_list_valid(hour_list: List[int]) -> bool: return False -def check_timestamp(timestamp: str, duration: int) -> bool: +def check_timestamp(timestamp: int, duration: int) -> bool: """ prüft, ob der Zeitstempel innerhalb der angegebenen Zeit liegt - Parameter - --------- - timestamp: str - Zeitstempel, der geprüft werden soll - duration: - Zeitspanne in s, in der der Zeitstempel gültig ist - Return ------ True: Zeit ist noch nicht abgelaufen False: Zeit ist abgelaufen """ - stamp = datetime.datetime.strptime(timestamp, "%m/%d/%Y, %H:%M:%S") - now = datetime.datetime.today() - delta = datetime.timedelta(seconds=duration) - if (now - delta) > stamp: + if (create_timestamp() - duration) > timestamp: return False else: return True -def create_timestamp() -> str: - try: - stamp = datetime.datetime.today().strftime("%m/%d/%Y, %H:%M:%S") - return stamp - except Exception: - raise - - -def create_timestamp_unix() -> int: - """ Unix Zeitstempel - """ - try: - return int(datetime.datetime.today().timestamp()) - except Exception: - raise +def create_timestamp() -> float: + return datetime.datetime.today().timestamp() def create_timestamp_YYYYMM() -> str: - try: - stamp = datetime.datetime.today().strftime("%Y%m") - return stamp - except Exception: - raise + stamp = datetime.datetime.today().strftime("%Y%m") + return stamp def create_timestamp_YYYYMMDD() -> str: - try: - stamp = datetime.datetime.today().strftime("%Y%m%d") - return stamp - except Exception: - raise - - -def create_timestamp_time() -> str: - try: - stamp = datetime.datetime.today().strftime("%H:%M") - return stamp - except Exception: - raise - - -def convert_YYYYMM_to_unix_timestamp(date: str) -> float: - return datetime.datetime.strptime(date, "%Y%m").timestamp() + stamp = datetime.datetime.today().strftime("%Y%m%d") + return stamp -def convert_to_unix_timestamp(timestamp: str) -> float: - return datetime.datetime.strptime(timestamp, "%m/%d/%Y, %H:%M:%S").timestamp() - - -def get_relative_date_string(date_string: str, day_offset: int = 0, month_offset: int = 0, year_offset: int = 0) -> str: - print_format = "%Y%m%d" if len(date_string) > 6 else "%Y%m" - my_date = datetime.datetime.strptime(date_string, print_format) - return (my_date + relativedelta(years=year_offset, months=month_offset, days=day_offset)).strftime(print_format) - - -def get_difference_to_now(timestamp_begin: str) -> Tuple[str, int]: +def get_difference_to_now(timestamp_begin: float) -> Tuple[str, int]: """ ermittelt den Abstand zwischen zwei Zeitstempeln. - - Parameter - --------- - timestamp_begin: str %m/%d/%Y, %H:%M:%S - Anfangszeitpunkt - Return ------ diff: [str, int] @@ -313,24 +249,21 @@ def get_difference_to_now(timestamp_begin: str) -> Tuple[str, int]: int: Differenz in Sekunden """ try: - diff = datetime.timedelta( - seconds=get_difference(timestamp_begin, datetime.datetime.today().strftime("%m/%d/%Y, %H:%M:%S"))) - return [convert_timedelta_to_time_string(diff), int(diff.total_seconds())] + diff = datetime.timedelta(seconds=create_timestamp()-timestamp_begin) + return (convert_timedelta_to_time_string(diff), int(diff.total_seconds())) except Exception: log.exception("Fehler im System-Modul") - return ["00:00", 0] + return ("00:00", 0) def get_difference(timestamp_begin: str, timestamp_end: str) -> Optional[int]: """ ermittelt den Abstand zwischen zwei Zeitstempeln in absoluten Sekunden. - Parameter --------- timestamp_begin: str %m/%d/%Y, %H:%M:%S Anfangszeitpunkt timestamp_end: str %m/%d/%Y, %H:%M:%S Endzeitpunkt - Return ------ diff: int @@ -348,7 +281,6 @@ def get_difference(timestamp_begin: str, timestamp_end: str) -> Optional[int]: def duration_sum(first: str, second: str) -> str: """ addiert zwei Zeitstrings und gibt das Ergebnis als String zurück. - Parameter --------- first, second: str @@ -368,7 +300,6 @@ def duration_sum(first: str, second: str) -> str: def __get_timedelta_obj(time: str) -> datetime.timedelta: """ erstellt aus einem String ein timedelta-Objekt. - Parameter --------- time: str diff --git a/packages/helpermodules/timecheck_test.py b/packages/helpermodules/timecheck_test.py index c7d2193bb1..adc556e292 100644 --- a/packages/helpermodules/timecheck_test.py +++ b/packages/helpermodules/timecheck_test.py @@ -20,51 +20,6 @@ def __init__(self, name: str, self.second_time = second_time -# cases_get_difference_to_now = [ -# Params("get_difference_to_now_minutes_before", "02/18/2022, 10:42:23", expected_return="87"), -# Params("get_difference_to_now_minutes_after", "02/18/2022, 10:50:45", expected_return="469"), -# Params("get_difference_to_now_hours", "02/18/2022, 10:42:56", expected_return="8651"), -# Params("get_difference_to_now_days", "02/16/2022, 08:18:45", expected_return="164149"), -# ] -cases_get_difference = [ - Params("get_difference_minutes_before", "02/18/2022, 10:40:56", - second_time="02/18/2022, 10:42:23", expected_return=87), - Params("get_difference_minutes_after", "02/18/2022, 10:42:56", - second_time="02/18/2022, 10:50:45", expected_return=469), - Params("get_difference_hours", "02/18/2022, 08:18:45", second_time="02/18/2022, 10:42:56", expected_return=8651), - Params("get_difference_days", "02/16/2022, 10:42:56", second_time="02/18/2022, 08:18:45", expected_return=164149), -] - -cases_duration_sum = [ - Params("duration_sum_minutes", "00:23", second_time="00:56", expected_return="1:19"), - Params("duration_sum_hours", "08:18", second_time="01:56", expected_return="10:14"), - Params("duration_sum_days", "18:8", second_time="10:24", expected_return="28:32"), -] - -# @pytest.fixture(autouse=True) -# def set_up(monkeypatch): -# mock_today = Mock(name="today", return_value="02/18/2022, 10:42") -# monkeypatch.setattr(datetime, "today", mock_today) - - -@pytest.mark.parametrize("params", cases_get_difference, ids=[c.name for c in cases_get_difference]) -def test_get_difference(params: Params): - # execution - diff = timecheck.get_difference(params.first_time, params.second_time) - - # evaluation - assert params.expected_return == diff - - -@pytest.mark.parametrize("params", cases_duration_sum, ids=[c.name for c in cases_duration_sum]) -def test_duration_sum(params: Params): - # execution - diff = timecheck.duration_sum(params.first_time, params.second_time) - - # evaluation - assert params.expected_return == diff - - @pytest.mark.parametrize("begin_hour, begin_min, end_hour, end_min,expected", [pytest.param(0, 0, 5, 5, 9300, id="too early"), pytest.param(8, 18, 10, 35, -780, id="start"), diff --git a/packages/helpermodules/update_config.py b/packages/helpermodules/update_config.py index 2e3d62f340..a73e6fec96 100644 --- a/packages/helpermodules/update_config.py +++ b/packages/helpermodules/update_config.py @@ -32,7 +32,7 @@ class UpdateConfig: - DATASTORE_VERSION = 30 + DATASTORE_VERSION = 31 valid_topic = [ "^openWB/bat/config/configured$", "^openWB/bat/set/charging_power_left$", @@ -1064,3 +1064,13 @@ def upgrade(topic: str, payload) -> None: Pub().pub(topic.replace("openWB/", "openWB/set/"), payload) self._loop_all_received_topics(upgrade) Pub().pub("openWB/system/datastore_version", 30) + + def upgrade_datastore_30(self) -> None: + def upgrade(topic: str, payload) -> None: + if re.search("openWB/vehicle/[0-9]+/get/soc_timestamp", topic) is not None: + payload = decode_payload(payload) + if payload: + updated_payload = datetime.datetime.strptime(payload, "%m/%d/%Y, %H:%M:%S").timestamp() + Pub().pub(topic.replace("openWB/", "openWB/set/"), updated_payload) + self._loop_all_received_topics(upgrade) + Pub().pub("openWB/system/datastore_version", 31) diff --git a/packages/main.py b/packages/main.py index f74d20b86f..50fba50136 100755 --- a/packages/main.py +++ b/packages/main.py @@ -72,7 +72,7 @@ def handler_with_control_interval(): else: self.interval_counter = self.interval_counter + 1 log.info("# ***Start*** ") - Pub().pub("openWB/set/system/time", timecheck.create_timestamp_unix()) + Pub().pub("openWB/set/system/time", timecheck.create_timestamp()) handler_with_control_interval() except KeyboardInterrupt: log.critical("Ausführung durch exit_after gestoppt: "+traceback.format_exc()) diff --git a/packages/modules/chargepoints/external_openwb/chargepoint_module.py b/packages/modules/chargepoints/external_openwb/chargepoint_module.py index 34a0f2cdc9..2c60dd51a4 100644 --- a/packages/modules/chargepoints/external_openwb/chargepoint_module.py +++ b/packages/modules/chargepoints/external_openwb/chargepoint_module.py @@ -45,7 +45,7 @@ def get_values(self) -> None: else: my_ip_address = data.data.system_data["system"].data["ip_address"] pub.pub_single("openWB/set/internal_chargepoint/global_data", - {"heartbeat": timecheck.create_timestamp_unix(), "parent_ip": my_ip_address}, + {"heartbeat": timecheck.create_timestamp(), "parent_ip": my_ip_address}, hostname=ip_address) pub.pub_single("openWB/set/isss/heartbeat", 0, hostname=ip_address) pub.pub_single("openWB/set/isss/parentWB", my_ip_address, diff --git a/packages/modules/common/component_state.py b/packages/modules/common/component_state.py index 44bb180eb8..cd6ddc5e30 100644 --- a/packages/modules/common/component_state.py +++ b/packages/modules/common/component_state.py @@ -106,11 +106,11 @@ def __init__( @auto_str class CarState: - def __init__(self, soc: float, range: Optional[float] = None, soc_timestamp: Optional[str] = None): + def __init__(self, soc: float, range: Optional[float] = None, soc_timestamp: float = 0): """Args: soc: actual state of charge in percent range: actual range in km - soc_timestamp: timestamp of last request in %m/%d/%Y, %H:%M:%S + soc_timestamp: timestamp of last request as unix timestamp """ self.soc = soc self.range = range diff --git a/packages/modules/common/configurable_vehicle.py b/packages/modules/common/configurable_vehicle.py index b54f55f75c..09326a1441 100644 --- a/packages/modules/common/configurable_vehicle.py +++ b/packages/modules/common/configurable_vehicle.py @@ -123,7 +123,7 @@ def _get_carstate_by_source(self, vehicle_update_data, source): def _is_soc_timestamp_valid(self, vehicle_update_data: VehicleUpdateData) -> bool: if vehicle_update_data.timestamp_soc_from_cp: soc_ts = vehicle_update_data.timestamp_soc_from_cp + 60 - now_ts = timecheck.create_timestamp_unix() + now_ts = timecheck.create_timestamp() return soc_ts > now_ts else: return False diff --git a/packages/modules/configuration_test.py b/packages/modules/configuration_test.py index 9de349ddc6..0de0c77819 100644 --- a/packages/modules/configuration_test.py +++ b/packages/modules/configuration_test.py @@ -3,11 +3,12 @@ from modules.configuration import pub_configurable from modules import configuration +from test_utils.test_environment import running_on_github def test_pub_configurable(monkeypatch): # setup - if str(configuration._get_packages_path()) == "/home/runner/work/core/core/packages": + if running_on_github(): # run test on github mock_packages_path = Mock(name="get packages path", return_value=Path("/home/runner/work/core/core/packages")) monkeypatch.setattr(configuration, "_get_packages_path", mock_packages_path) diff --git a/packages/modules/internal_chargepoint_handler/internal_chargepoint_handler.py b/packages/modules/internal_chargepoint_handler/internal_chargepoint_handler.py index bf18f2807d..ffbe071564 100644 --- a/packages/modules/internal_chargepoint_handler/internal_chargepoint_handler.py +++ b/packages/modules/internal_chargepoint_handler/internal_chargepoint_handler.py @@ -247,8 +247,8 @@ def __thread_active(thread: Optional[threading.Thread]) -> bool: self.update_state.update_state(data, heartbeat_expired) def _check_heartbeat_expired(self, heartbeat) -> bool: - if heartbeat+80 < timecheck.create_timestamp_unix(): - log.error(f"Heartbeat Fehler seit {timecheck.create_timestamp_unix()-heartbeat}" + if heartbeat+80 < timecheck.create_timestamp(): + log.error(f"Heartbeat Fehler seit {timecheck.create_timestamp()-heartbeat}" "s keine Verbindung. Stoppe Ladung.") return True else: diff --git a/packages/test_utils/test_environment.py b/packages/test_utils/test_environment.py new file mode 100644 index 0000000000..4d4cda48db --- /dev/null +++ b/packages/test_utils/test_environment.py @@ -0,0 +1,6 @@ + +from pathlib import Path + + +def running_on_github(): + return str(Path(__file__).resolve().parents[2]/"packages") == "/home/runner/work/core/core/packages"