Skip to content

Commit

Permalink
serial number in chargelog
Browse files Browse the repository at this point in the history
fix sdm reg

log counter imported in logdata

detect meters with serial number "0"

flake8
  • Loading branch information
LKuemmel committed Mar 27, 2024
1 parent 4e3305f commit f3a2bb7
Show file tree
Hide file tree
Showing 18 changed files with 105 additions and 18 deletions.
3 changes: 3 additions & 0 deletions packages/control/chargelog/chargelog.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,9 @@ def save_data(chargepoint, charging_ev, immediately: bool = True):
{
"id": chargepoint.num,
"name": chargepoint.data.config.name,
"serial_number": chargepoint.data.get.serial_number,
"imported_at_start": log_data.imported_at_mode_switch,
"imported_at_end": chargepoint.data.get.imported,
},
"vehicle":
{
Expand Down
2 changes: 2 additions & 0 deletions packages/control/chargepoint/chargepoint_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ class Log:
ev: int = -1
prio: bool = False
rfid: Optional[str] = None
serial_number: Optional[str] = None


def connected_vehicle_factory() -> ConnectedVehicle:
Expand All @@ -99,6 +100,7 @@ class Get:
power: float = 0
rfid_timestamp: Optional[float] = None
rfid: Optional[int] = None
serial_number: Optional[str] = None
soc: Optional[float] = None
soc_timestamp: Optional[int] = None
state_str: Optional[str] = None
Expand Down
3 changes: 2 additions & 1 deletion packages/helpermodules/setdata.py
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,8 @@ def process_chargepoint_get_topics(self, msg):
"/get/state_str" in msg.topic or
"/get/heartbeat" in msg.topic or
"/get/rfid" in msg.topic or
"/get/vehicle_id" in msg.topic):
"/get/vehicle_id" in msg.topic or
"/get/serial_number"):
self._validate_value(msg, str)
elif "/get/rfid_timestamp" in msg.topic:
self._validate_value(msg, float)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ def get_values(self) -> None:
charge_state=json_rsp["charge_state"],
phases_in_use=json_rsp["phases_in_use"],
vehicle_id=json_rsp["vehicle_id"],
evse_current=json_rsp["offered_current"]
evse_current=json_rsp["offered_current"],
serial_number=json_rsp["serial"]
)

if json_rsp.get("voltages"):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
rfid="001180644",
rfid_timestamp=1700839714,
vehicle_id="98:ED:5C:B4:EE:8D",
evse_current=6
evse_current=6,
serial_number="823950"
)

SAMPLE = {'charge_state': True,
Expand Down Expand Up @@ -56,7 +57,8 @@
phases_in_use=1,
rfid=None,
frequency=50.2,
evse_current=6
evse_current=6,
serial_number="493826"
)

SAMPLE_EXTENDED = {"date": "2023:09:18-15:13:41",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ def get_values(self) -> None:
plug_state=plug_state,
charge_state=charge_state,
phases_in_use=phases_in_use,
serial_number=self._client.meter_client.get_serial_number()
)
self.store.set(chargepoint_state)
except AttributeError:
Expand Down
11 changes: 8 additions & 3 deletions packages/modules/chargepoints/smartwb/chargepoint_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ def __init__(self, config: SmartWB) -> None:
self.__client_error_context = ErrorCounterContext(
"Anhaltender Fehler beim Auslesen des Ladepunkts. Soll-Stromstärke wird zurückgesetzt.")
self.phases_in_use = 1
self.session = req.get_http_session()

def set_current(self, current: float) -> None:
if self.__client_error_context.error_counter_exceeded():
Expand All @@ -31,14 +32,14 @@ def set_current(self, current: float) -> None:
timeout = self.config.configuration.timeout
# Stromvorgabe in Hundertstel Ampere
params = (('current', int(current*100)),)
req.get_http_session().get('http://'+ip_address+'/setCurrent', params=params, timeout=(timeout, None))
self.session.get('http://'+ip_address+'/setCurrent', params=params, timeout=(timeout, None))

def get_values(self) -> None:
with SingleComponentUpdateContext(self.fault_state):
with self.__client_error_context:
ip_address = self.config.configuration.ip_address
timeout = self.config.configuration.timeout
response = req.get_http_session().get('http://'+ip_address+'/getParameters', timeout=timeout)
response = self.session.get('http://'+ip_address+'/getParameters', timeout=timeout)
json_rsp = response.json()["list"][0]

ev_state = json_rsp["vehicleState"]
Expand Down Expand Up @@ -74,6 +75,9 @@ def get_values(self) -> None:
else:
tag = None

resp = self.session.get('http://'+ip_address+'/evseHost', timeout=timeout)
mac = resp.json()["list"][0]["mac"]

chargepoint_state = ChargepointState(
power=json_rsp["actualPower"] * 1000,
currents=currents,
Expand All @@ -82,7 +86,8 @@ def get_values(self) -> None:
charge_state=charge_state,
phases_in_use=self.phases_in_use,
voltages=voltages,
rfid=tag
rfid=tag,
serial_number=mac
)

self.store.set(chargepoint_state)
Expand Down
26 changes: 23 additions & 3 deletions packages/modules/chargepoints/smartwb/smartwb_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ class TestSmartWb:
imported=54350.0,
plug_state=True,
charge_state=False,
phases_in_use=3
phases_in_use=3,
serial_number="94:B9:7E:69:F0:D1"
)
SAMPLE_V1 = {
"type": "parameters",
Expand Down Expand Up @@ -59,7 +60,8 @@ class TestSmartWb:
plug_state=True,
charge_state=True,
rfid="0a1b2c3d",
rfid_timestamp=1652683252
rfid_timestamp=1652683252,
serial_number="94:B9:7E:69:F0:D1"
)
SAMPLE_V2 = {
"type": "parameters",
Expand Down Expand Up @@ -97,7 +99,8 @@ class TestSmartWb:
charge_state=True,
rfid="0a1b2c3d",
rfid_timestamp=1652683252,
phases_in_use=1
phases_in_use=1,
serial_number="94:B9:7E:69:F0:D1"
)
SAMPLE_NOT_CHARGING_V2 = {
"type": "parameters",
Expand Down Expand Up @@ -126,6 +129,22 @@ class TestSmartWb:
"RFIDUID": "0a1b2c3d"
}]
}
SAMPLE_HOST_DATA = {
"type": "evseHost",
"list": [
{
"ssid": "EVSE-WiFi",
"dns": "192.168.4.1",
"mac": "94:B9:7E:69:F0:D1",
"ip": "192.168.4.1",
"netmask": "255.255.255.0",
"gateway": "0.0.0.0",
"uptime": 1960,
"opMode": "normal",
"firmware": "2.2.5"
}
]
}

@pytest.fixture(autouse=True)
def setup(self, monkeypatch):
Expand All @@ -147,6 +166,7 @@ def cp(self) -> chargepoint_module.ChargepointModule:
def test_get_values_v2(self, cp, requests_mock: requests_mock.mock, params: Params):
# setup
requests_mock.get("http://1.1.1.1/getParameters", json=params.sample_data)
requests_mock.get("http://1.1.1.1/evseHost", json=self.SAMPLE_HOST_DATA)

# execution
cp.get_values()
Expand Down
6 changes: 5 additions & 1 deletion packages/modules/common/abstract_counter.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from abc import abstractmethod
from typing import List, Tuple
from typing import List, Optional, Tuple

from modules.common import modbus

Expand Down Expand Up @@ -36,3 +36,7 @@ def get_power_factors(self) -> List[float]:
@abstractmethod
def get_voltages(self) -> List[float]:
return [230]*3

@abstractmethod
def get_serial_number(self) -> Optional[str]:
return None
2 changes: 2 additions & 0 deletions packages/modules/common/component_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ def __init__(self,
imported: float = 0,
exported: float = 0,
power: float = 0,
serial_number: str = "",
powers: Optional[List[Optional[float]]] = None,
voltages: Optional[List[Optional[float]]] = None,
currents: Optional[List[Optional[float]]] = None,
Expand All @@ -166,6 +167,7 @@ def __init__(self,
self.imported = imported
self.exported = exported
self.power = power
self.serial_number = serial_number
self.phases_in_use = phases_in_use
self.charge_state = charge_state
self.plug_state = plug_state
Expand Down
10 changes: 8 additions & 2 deletions packages/modules/common/hardware_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
f"Vermutlich ist der Zähler falsch konfiguriert oder defekt. {OPEN_TICKET}")
METER_BROKEN = ("Die Spannungen des Zählers konnten nicht korrekt ausgelesen werden. "
f"Der Zähler ist defekt. {OPEN_TICKET}")
METER_NO_SERIAL_NUMBER = ("Die Seriennummer des Zählers für das Ladelog kann nicht ausgelesen werden. Wenn Sie die "
"Seriennummer für Abrechnungszwecke benötigen, wenden Sie sich bitte an unseren Support. Die "
"Funktionalität wird dadurch nicht beeinträchtigt!")
EVSE_BROKEN = ("Auslesen der EVSE nicht möglich. "
f"Vermutlich ist die EVSE defekt oder hat eine unbekannte Modbus-ID. {OPEN_TICKET}")

Expand Down Expand Up @@ -77,13 +80,16 @@ def check_hardware(self: ClientHandlerProtocol):
raise Exception(USB_ADAPTER_BROKEN)
if meter_check_passed is False:
raise Exception(meter_error_msg)
elif meter_check_passed and meter_error_msg == METER_BROKEN:
self.fault_state.warning(METER_BROKEN)
elif meter_check_passed and meter_error_msg is not None:
self.fault_state.warning(meter_error_msg)
if evse_check_passed is False:
raise Exception(EVSE_BROKEN)

def check_meter(self: ClientHandlerProtocol) -> Tuple[bool, Optional[str]]:
try:
serial_number = self.meter_client.get_serial_number()
if serial_number == "0" or serial_number is None:
return True, METER_NO_SERIAL_NUMBER
return True, check_meter_values(self.meter_client.get_voltages())
except Exception:
return False, METER_PROBLEM
35 changes: 32 additions & 3 deletions packages/modules/common/hardware_check_test.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from unittest.mock import Mock
from typing import List, Optional, Tuple, Union
from unittest.mock import Mock, patch

import pytest
from modules.common import sdm
from modules.common.evse import Evse
from modules.common.hardware_check import (
EVSE_BROKEN, LAN_ADAPTER_BROKEN, METER_BROKEN, METER_PROBLEM, USB_ADAPTER_BROKEN,
EVSE_BROKEN, LAN_ADAPTER_BROKEN, METER_BROKEN, METER_NO_SERIAL_NUMBER, METER_PROBLEM, USB_ADAPTER_BROKEN,
SeriesHardwareCheckMixin, check_meter_values)
from modules.common.modbus import NO_CONNECTION, ModbusSerialClient_, ModbusTcpClient_
from modules.conftest import SAMPLE_IP, SAMPLE_PORT
Expand Down Expand Up @@ -81,9 +82,37 @@ def test_hardware_check_succeeds(monkeypatch):
pytest.param([230, 0, 230], METER_BROKEN, id="dreiphasig, L2 defekt"),
]
)
def test_check_meter(voltages, expected_msg, monkeypatch):
def test_check_meter_values(voltages, expected_msg, monkeypatch):
# setup & execution
msg = check_meter_values(voltages)

# assert
assert msg == expected_msg


@patch('modules.common.hardware_check.ClientHandlerProtocol')
@pytest.mark.parametrize("serial_number_return, voltages_return, expected",
[("0", [230]*3, (True, METER_NO_SERIAL_NUMBER)),
(12345, [230]*3, (True, None)),
(Exception(), [230]*3, (False, METER_PROBLEM))])
def test_check_meter(
MockClientHandlerProtocol: Mock,
serial_number_return: Union[int, Exception],
voltages_return: List[int],
expected: Tuple[bool, Optional[str]],
):
# Arrange
mock_meter_client = Mock()
if isinstance(serial_number_return, Exception):
mock_meter_client.get_serial_number.side_effect = serial_number_return
else:
mock_meter_client.get_serial_number.return_value = serial_number_return
mock_meter_client.get_voltages.return_value = voltages_return
MockClientHandlerProtocol.meter_client = mock_meter_client
mixin = SeriesHardwareCheckMixin

# Act
result = mixin.check_meter(MockClientHandlerProtocol)

# Assert
assert result == expected
3 changes: 3 additions & 0 deletions packages/modules/common/mpm3pm.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,6 @@ def get_frequency(self) -> float:
def get_currents(self) -> List[float]:
return [val / 100 for val in self.client.read_input_registers(
0x0E, [ModbusDataType.UINT_32]*3, unit=self.id)]

def get_serial_number(self) -> str:
return str(self.client.read_input_registers(0x33, ModbusDataType.UINT_32, unit=self.id))
3 changes: 3 additions & 0 deletions packages/modules/common/sdm.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ def get_frequency(self) -> float:
frequency = frequency / 10
return frequency

def get_serial_number(self) -> str:
return str(self.client.read_holding_registers(0xFC00, ModbusDataType.UINT_32, unit=self.id))


class Sdm630(Sdm):
def __init__(self, modbus_id: int, client: modbus.ModbusTcpClient_) -> None:
Expand Down
1 change: 1 addition & 0 deletions packages/modules/common/store/_chargepoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ def update(self):
pub_to_broker("openWB/set/chargepoint/" + str(self.num) + "/get/plug_state", self.state.plug_state, 2)
pub_to_broker("openWB/set/chargepoint/" + str(self.num) + "/get/rfid", self.state.rfid)
pub_to_broker("openWB/set/chargepoint/" + str(self.num) + "/get/rfid_timestamp", self.state.rfid_timestamp)
pub_to_broker("openWB/set/chargepoint/" + str(self.num) + "/get/serial_number", self.state.serial_number)
pub_to_broker("openWB/set/chargepoint/" + str(self.num) + "/get/soc", self.state.soc)
pub_to_broker("openWB/set/chargepoint/" + str(self.num) + "/get/soc_timestamp", self.state.soc_timestamp)
pub_to_broker("openWB/set/chargepoint/" + str(self.num) + "/get/evse_current", self.state.evse_current)
Expand Down
2 changes: 2 additions & 0 deletions packages/modules/common/store/_chargepoint_internal.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ def update(self):
"/get/charge_state", self.state.charge_state, 2)
pub_to_broker("openWB/set/internal_chargepoint/" + str(self.num) + "/get/plug_state", self.state.plug_state, 2)
pub_to_broker("openWB/set/internal_chargepoint/" + str(self.num) + "/get/rfid", self.state.rfid)
pub_to_broker("openWB/set/internal_chargepoint/" + str(self.num) +
"/get/serial_number", self.state.serial_number)
pub_to_broker("openWB/set/internal_chargepoint/" + str(self.num) +
"/get/evse_current", self.state.evse_current, 2)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ def store_state(chargepoint_state: ChargepointState) -> None:
imported = self._client.meter_client.get_imported()
power_factors = self._client.meter_client.get_power_factors()
frequency = self._client.meter_client.get_frequency()
serial_number = self._client.meter_client.get_serial_number()
phases_in_use = sum(1 for current in currents if current > 3)
if phases_in_use == 0:
phases_in_use = self.old_phases_in_use
Expand Down Expand Up @@ -105,7 +106,8 @@ def store_state(chargepoint_state: ChargepointState) -> None:
phases_in_use=phases_in_use,
power_factors=power_factors,
rfid=last_tag,
evse_current=self.set_current_evse
evse_current=self.set_current_evse,
serial_number=serial_number
)
except Exception as e:
self._client.read_error += 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

@pytest.mark.parametrize(
"old_chargepoint_state, published_topics",
[(None, 34),
[(None, 36),
(OLD_CHARGEPOINT_STATE, 2)]
)
Expand Down

0 comments on commit f3a2bb7

Please sign in to comment.