Skip to content

Commit 57ee62a

Browse files
authored
Merge pull request #2422 from benderl/json-vehicle
json-vehicle: cache jq.compile
2 parents 268876a + a334169 commit 57ee62a

2 files changed

Lines changed: 67 additions & 42 deletions

File tree

Lines changed: 59 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
from typing import List
21
import logging
32
import jq
43

4+
from typing import List, Optional, Union, Any, Dict
5+
from datetime import datetime
6+
57
from helpermodules.cli import run_using_positional_cli_args
68
from modules.common import req
79
from modules.common import store
@@ -10,14 +12,12 @@
1012
from modules.common.component_state import CarState
1113
from modules.common.configurable_vehicle import ConfigurableVehicle
1214
from modules.vehicles.json.config import JsonSocSetup, JsonSocConfiguration
13-
from typing import Any, Dict
14-
from datetime import datetime
1515

1616

1717
log = logging.getLogger(__name__)
1818

1919

20-
def extract_to_epoch(input_string: str) -> float:
20+
def extract_to_epoch(input_string: Union[str, int, float]) -> float:
2121
# If already an integer, return it
2222
if isinstance(input_string, int) or isinstance(input_string, float):
2323
return int(input_string)
@@ -27,70 +27,89 @@ def extract_to_epoch(input_string: str) -> float:
2727
dt = datetime.fromisoformat(input_string)
2828
return int(dt.timestamp())
2929
except ValueError:
30-
log.exception(f"Kein ISO 8601 formatiertes Datum in '{input_string}' gefunden.")
30+
log.exception(f'Kein ISO 8601 formatiertes Datum in "{input_string}" gefunden.')
3131
return None
3232

3333

34-
def parse_data(data: Dict[str, Any], pattern: str) -> any:
35-
log.debug(f"parse_data: data='{data}' pattern='{pattern}'")
34+
def parse_data(data: Dict[str, Any], compiled_query: Any, pattern: str = None) -> any:
35+
if compiled_query is None:
36+
raise ValueError("Kein Pattern zum extrahieren der Daten definiert. Bitte die Konfiguration aktualisieren!")
3637

37-
if pattern == "":
38-
raise ValueError("Kein Pattern zum extrahieren der Daten definiert. Bitte in der Konfiguration aktualisieren.")
39-
40-
result = jq.compile(pattern).input(data).first()
38+
result = compiled_query.input(data).first()
4139
if result is None:
42-
raise ValueError(f"Pattern {pattern} hat keine Ergebnisse in '{data}' geliefert.")
40+
raise ValueError(f'Pattern "{pattern}" hat keine Ergebnisse in "{data}" geliefert!')
4341

44-
log.debug(f"result='{result}'")
42+
log.debug(f'result="{result}"')
4543
return result
4644

4745

48-
def fetch_soc(config: JsonSocSetup) -> CarState:
46+
def fetch_soc(config: JsonSocSetup, compiled_queries: Dict) -> CarState:
4947
url = config.configuration.url
50-
soc_pattern = config.configuration.soc_pattern
51-
range_pattern = config.configuration.range_pattern
52-
timestamp_pattern = config.configuration.timestamp_pattern
5348
timeout = config.configuration.timeout if isinstance(config.configuration.timeout, int) else None
5449

5550
if url is None or url == "":
5651
raise ValueError("Keine URL zum Abrufen der Daten definiert. Bitte in der Konfiguration aktualisieren.")
52+
if compiled_queries["soc"] is None:
53+
raise ValueError("Kein Pattern zum Extrahieren des SOC definiert. Bitte in der Konfiguration aktualisieren.")
5754

5855
raw_data: Dict[str, Any] = req.get_http_session().get(url, timeout=timeout).json()
5956

60-
soc = float(parse_data(raw_data, soc_pattern))
61-
62-
if range_pattern is None or range_pattern == "":
63-
log.debug("Kein Pattern für Range angegeben, setze Range auf None.")
64-
range = None
65-
else:
66-
range = int(parse_data(raw_data, range_pattern))
57+
soc = float(parse_data(raw_data, compiled_queries["soc"], config.configuration.soc_pattern))
58+
range = (int(parse_data(raw_data, compiled_queries["range"], config.configuration.range_pattern))
59+
if compiled_queries["range"] is not None else None)
60+
timestamp = (extract_to_epoch(parse_data(raw_data,
61+
compiled_queries["timestamp"],
62+
config.configuration.timestamp_pattern))
63+
if compiled_queries["timestamp"] is not None else None)
64+
return CarState(soc=soc, range=range, soc_timestamp=timestamp)
6765

68-
if timestamp_pattern is None or timestamp_pattern == "":
69-
log.debug("Kein Pattern für Timestamp angegeben, setze Timestamp auf None.")
70-
timestamp = None
71-
else:
72-
log.debug(f"timestamp_pattern='{timestamp_pattern}'")
73-
timestamp = parse_data(raw_data, timestamp_pattern)
74-
timestamp = extract_to_epoch(timestamp)
7566

76-
return CarState(soc=soc, range=range, soc_timestamp=timestamp)
67+
def initialize_vehicle(vehicle_config: JsonSocSetup, compiled_queries: Dict) -> None:
68+
config = vehicle_config.configuration
69+
log.debug(f'Initialisiere Fahrzeug mit Konfiguration: {config}')
70+
compiled_queries["soc"] = jq.compile(config.soc_pattern) if config.soc_pattern is not None else None
71+
compiled_queries["range"] = jq.compile(config.range_pattern) if config.range_pattern is not None else None
72+
compiled_queries["timestamp"] = (jq.compile(config.timestamp_pattern)
73+
if config.timestamp_pattern is not None else None)
7774

7875

7976
def create_vehicle(vehicle_config: JsonSocSetup, vehicle: int):
80-
def updater(vehicle_update_data: VehicleUpdateData) -> CarState:
81-
return fetch_soc(vehicle_config)
82-
return ConfigurableVehicle(vehicle_config=vehicle_config, component_updater=updater, vehicle=vehicle,
83-
calc_while_charging=vehicle_config.configuration.calculate_soc)
77+
compiled_queries = {
78+
'soc': None,
79+
'range': None,
80+
'timestamp': None
81+
}
8482

83+
def initializer() -> None:
84+
return initialize_vehicle(vehicle_config, compiled_queries)
8585

86-
def http_update(soc_url: str, range_url: str, charge_point: int):
87-
log.debug("http_soc: soc_url="+soc_url+"range_url="+range_url+"charge_point="+str(charge_point))
86+
def updater(vehicle_update_data: VehicleUpdateData) -> CarState:
87+
return fetch_soc(vehicle_config, compiled_queries)
88+
return ConfigurableVehicle(vehicle_config=vehicle_config,
89+
component_updater=updater,
90+
vehicle=vehicle,
91+
calc_while_charging=vehicle_config.configuration.calculate_soc,
92+
initializer=initializer)
93+
94+
95+
def json_update(charge_point: int,
96+
url: str,
97+
soc_pattern: str,
98+
range_pattern: Optional[str] = None,
99+
timestamp_pattern: Optional[str] = None,
100+
calculate_soc: Optional[bool] = False):
101+
log.debug(f'json-soc: charge_point={charge_point} url="{url}" soc-pattern="{soc_pattern}" '
102+
f'range-pattern="{range_pattern}" timestamp-pattern="{timestamp_pattern}" calculate-soc={calculate_soc}')
88103
store.get_car_value_store(charge_point).store.set(
89-
fetch_soc(JsonSocSetup(configuration=JsonSocConfiguration(soc_url, range_url))))
104+
fetch_soc(JsonSocSetup(configuration=JsonSocConfiguration(url=url,
105+
soc_pattern=soc_pattern,
106+
range_pattern=range_pattern,
107+
timestamp_pattern=timestamp_pattern,
108+
calculate_soc=calculate_soc))))
90109

91110

92111
def main(argv: List[str]):
93-
run_using_positional_cli_args(http_update, argv)
112+
run_using_positional_cli_args(json_update, argv)
94113

95114

96115
device_descriptor = DeviceDescriptor(configuration_factory=JsonSocSetup)

packages/modules/vehicles/json/test_soc.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import unittest
22
from unittest.mock import patch, MagicMock
3-
from soc import fetch_soc, JsonSocSetup, JsonSocConfiguration
3+
from soc import initialize_vehicle, fetch_soc, JsonSocSetup, JsonSocConfiguration
44

55

66
class TestSoc(unittest.TestCase):
@@ -59,13 +59,19 @@ def test_fetch_soc(self, mock_get_http_session):
5959
mock_response.json.return_value = case['sample_data']
6060
mock_get_http_session.return_value.get.return_value = mock_response
6161

62+
compiled_queries = {
63+
'soc': None,
64+
'range': None,
65+
'timestamp': None
66+
}
6267
vehicle_config = JsonSocSetup(configuration=JsonSocConfiguration(
6368
url=case['url'],
6469
soc_pattern=case['soc_pattern'],
6570
range_pattern=case['range_pattern'],
6671
timestamp_pattern=case['timestamp_pattern']
6772
))
68-
car_state = fetch_soc(vehicle_config)
73+
initialize_vehicle(vehicle_config, compiled_queries)
74+
car_state = fetch_soc(vehicle_config, compiled_queries)
6975

7076
self.assertEqual(car_state.soc, case['expected_soc'])
7177
self.assertEqual(car_state.range, case['expected_range'])

0 commit comments

Comments
 (0)