sprint1 refactor
parent
fe4ec551b5
commit
19da29b9b7
6
main.py
6
main.py
|
@ -1,6 +0,0 @@
|
||||||
from src.app import run_test_monitor
|
|
||||||
from src.config import test_config
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
run_test_monitor(config=test_config)
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
import sys
|
||||||
|
from src.app import demo_test
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
if "--demo" in sys.argv:
|
||||||
|
demo_test()
|
||||||
|
|
||||||
|
if "--run-server" in sys.argv:
|
||||||
|
pass
|
|
@ -71,6 +71,20 @@ files = [
|
||||||
{file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
|
{file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nodeenv"
|
||||||
|
version = "1.8.0"
|
||||||
|
description = "Node.js virtual environment builder"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*"
|
||||||
|
files = [
|
||||||
|
{file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"},
|
||||||
|
{file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
setuptools = "*"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "packaging"
|
name = "packaging"
|
||||||
version = "23.2"
|
version = "23.2"
|
||||||
|
@ -97,6 +111,24 @@ files = [
|
||||||
dev = ["pre-commit", "tox"]
|
dev = ["pre-commit", "tox"]
|
||||||
testing = ["pytest", "pytest-benchmark"]
|
testing = ["pytest", "pytest-benchmark"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pyright"
|
||||||
|
version = "1.1.345"
|
||||||
|
description = "Command line wrapper for pyright"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "pyright-1.1.345-py3-none-any.whl", hash = "sha256:00891361baf58698aa660d9374823d65782823ceb4a65515ff5dd159b0d4d2b1"},
|
||||||
|
{file = "pyright-1.1.345.tar.gz", hash = "sha256:bb8c80671cdaeb913142b49642a741959f3fcd728c99814631c2bde3a7864938"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
nodeenv = ">=1.6.0"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
all = ["twine (>=3.4.1)"]
|
||||||
|
dev = ["twine (>=3.4.1)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pytest"
|
name = "pytest"
|
||||||
version = "7.4.4"
|
version = "7.4.4"
|
||||||
|
@ -229,4 +261,4 @@ zstd = ["zstandard (>=0.18.0)"]
|
||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = "^3.10"
|
python-versions = "^3.10"
|
||||||
content-hash = "63e9ec089b621ebbb62f087b0c893cee4608b33e867a94543da384ea6520e3f6"
|
content-hash = "ea7047b815051aa3693aff5c70b1708617c98239d792c2361a90a86548e4184b"
|
||||||
|
|
|
@ -13,6 +13,7 @@ influxdb-client = "^1.39.0"
|
||||||
|
|
||||||
[tool.poetry.group.dev.dependencies]
|
[tool.poetry.group.dev.dependencies]
|
||||||
pytest = "^7.4.4"
|
pytest = "^7.4.4"
|
||||||
|
pyright = "^1.1.345"
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["poetry-core"]
|
requires = ["poetry-core"]
|
||||||
|
|
11
src/app.py
11
src/app.py
|
@ -1,9 +1,10 @@
|
||||||
|
from src.config import test_config
|
||||||
from src.device import FakeInverter
|
from src.device import FakeInverter
|
||||||
from src.monitor import Monitor
|
from src.monitor import Monitor
|
||||||
|
|
||||||
|
|
||||||
def run_test_monitor(config: dict):
|
def demo_test():
|
||||||
monitor = Monitor(session_config=config)
|
monitor = Monitor(session_config=test_config)
|
||||||
device_conf = {
|
device_conf = {
|
||||||
"name": "myinv",
|
"name": "myinv",
|
||||||
"model": "12345",
|
"model": "12345",
|
||||||
|
@ -12,8 +13,4 @@ def run_test_monitor(config: dict):
|
||||||
}
|
}
|
||||||
device = FakeInverter(**device_conf)
|
device = FakeInverter(**device_conf)
|
||||||
monitor.init_device(device)
|
monitor.init_device(device)
|
||||||
monitor.demo_get_device_data_from_file()
|
monitor.mainloop()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
pass
|
|
||||||
|
|
|
@ -2,4 +2,4 @@ from dotenv import dotenv_values
|
||||||
|
|
||||||
|
|
||||||
prod_config = dotenv_values("src/.env_prod")
|
prod_config = dotenv_values("src/.env_prod")
|
||||||
test_config = dotenv_values("nts_ntu/.env_test")
|
test_config = dotenv_values("src/.env_test")
|
||||||
|
|
|
@ -24,13 +24,14 @@ class Device(ABC):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def connect(self):
|
def _connect_and_read(self) -> Any:
|
||||||
"""Реализуется физическое подключение к прибору, для сбора данных"""
|
"""Реализуется физическое подключение к прибору, и считывание данных"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def get_data(self) -> dict:
|
def get_data(self) -> dict:
|
||||||
"""Реализует получение данных от прибора возвращает словарь.
|
"""Реализует получение данных "Монитором" от прибора.
|
||||||
|
Возвращает словарь.
|
||||||
Считанные данные привести к dict описанной ниже структуры:
|
Считанные данные привести к dict описанной ниже структуры:
|
||||||
dict_structure = {
|
dict_structure = {
|
||||||
"measurement": "<name>",
|
"measurement": "<name>",
|
||||||
|
@ -45,25 +46,30 @@ class Device(ABC):
|
||||||
<field_name>: str
|
<field_name>: str
|
||||||
<field_value>: str | int | float | bool
|
<field_value>: str | int | float | bool
|
||||||
<measurement_time>: datetime
|
<measurement_time>: datetime
|
||||||
|
|
||||||
Функция должна вернуть созданный словарь
|
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
class Inverter(Device):
|
class Inverter(Device):
|
||||||
def __init__(self, **kwargs) -> None:
|
def __init__(self, **kwargs) -> None:
|
||||||
self.name: str = kwargs.get("name", "fakeinvertor")
|
self.name: str = "Invertor"
|
||||||
self.model: str = kwargs.get("model", "fakemodel")
|
self.model: str = "model"
|
||||||
self.location: str = kwargs.get("name", "fakelocation")
|
self.location: str = "location"
|
||||||
self._raw_data: str
|
self._raw_data: str
|
||||||
self.port: str = kwargs.get("name", "fakeCOMport")
|
self.port: str = "COM_port"
|
||||||
self.__dict__.update(kwargs)
|
self.__dict__.update(kwargs)
|
||||||
|
|
||||||
def connect(self):
|
def _connect_and_read(self) -> str:
|
||||||
pass
|
"""Подключение к прибору
|
||||||
|
Реализует подключение к прибору и считывает поток данных
|
||||||
|
в виде строки.
|
||||||
|
Считанную строку сохранить в self._raw_data и вернуть
|
||||||
|
"""
|
||||||
|
return self._raw_data
|
||||||
|
|
||||||
def get_data(self, current_time: datetime = datetime.now()) -> dict:
|
def get_data(self, **kwargs) -> dict:
|
||||||
|
""""""
|
||||||
|
current_time = kwargs.get('current_time', datetime.now())
|
||||||
val_template = namedtuple(
|
val_template = namedtuple(
|
||||||
"InvertorData",
|
"InvertorData",
|
||||||
[
|
[
|
||||||
|
@ -71,7 +77,7 @@ class Inverter(Device):
|
||||||
"QQQ", # O/P load percent (Digital) 0, - 0%
|
"QQQ", # O/P load percent (Digital) 0, - 0%
|
||||||
"SS_S", # Battery voltage 12,24,48
|
"SS_S", # Battery voltage 12,24,48
|
||||||
"BBB", # Battery capacity (as O/P load percent)
|
"BBB", # Battery capacity (as O/P load percent)
|
||||||
"TT_T", # Heat Sink Temperature (0-99.9)
|
"TT_T", # Heat Sink Temperature (0-99.9)оо
|
||||||
"MMM", # Utility Power Voltage (0-250VACоооо)
|
"MMM", # Utility Power Voltage (0-250VACоооо)
|
||||||
"RR_R", # Output Power Frequency (40.0-70.0) Hz
|
"RR_R", # Output Power Frequency (40.0-70.0) Hz
|
||||||
"DDD", # DC BUS Voltage (0V)
|
"DDD", # DC BUS Voltage (0V)
|
||||||
|
@ -80,8 +86,8 @@ class Inverter(Device):
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
raw = self._raw_data.strip().lstrip("(").rstrip(")").split()
|
raw = self._raw_data.strip().lstrip("(").rstrip(")").split()
|
||||||
print(raw)
|
if len(raw) != 10:
|
||||||
|
raise TypeError("неверный формат ответа")
|
||||||
values = val_template(*map(float, raw[:-1]), raw[-1])
|
values = val_template(*map(float, raw[:-1]), raw[-1])
|
||||||
answer: dict = {
|
answer: dict = {
|
||||||
"measurement": self.name,
|
"measurement": self.name,
|
||||||
|
@ -89,6 +95,7 @@ class Inverter(Device):
|
||||||
"fields": values._asdict(),
|
"fields": values._asdict(),
|
||||||
"time": current_time,
|
"time": current_time,
|
||||||
}
|
}
|
||||||
|
print(answer)
|
||||||
return answer
|
return answer
|
||||||
|
|
||||||
|
|
||||||
|
@ -97,14 +104,21 @@ class FakeInverter(Inverter):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
self.cur_datetime: datetime = datetime.now() - timedelta(hours=23)
|
self.cur_datetime: datetime = datetime.now() - timedelta(hours=23)
|
||||||
self.delta: timedelta = timedelta(minutes=10)
|
self.delta: timedelta = timedelta(minutes=10)
|
||||||
|
self.line_counter = 0
|
||||||
|
|
||||||
def __getattribute__(self, __name: str) -> Any:
|
def __getattribute__(self, __name: str) -> Any:
|
||||||
if __name == "cur_datetime":
|
if __name == "cur_datetime":
|
||||||
self.__dict__["cur_datetime"] += self.delta
|
self.__dict__["cur_datetime"] += self.delta
|
||||||
|
|
||||||
|
if __name == "_raw_data":
|
||||||
|
with open("telemetry.txt", mode="r") as f:
|
||||||
|
for _ in range(self.line_counter):
|
||||||
|
f.readline()
|
||||||
|
line = f.readline().strip()
|
||||||
|
self.__dict__["_raw_data"] = line
|
||||||
|
self.line_counter += 2
|
||||||
|
|
||||||
return super().__getattribute__(__name)
|
return super().__getattribute__(__name)
|
||||||
|
|
||||||
def set_fake_answer(self, line: str) -> None:
|
def get_data(self, **kwargs) -> dict:
|
||||||
self._raw_data = line
|
return super().get_data(current_time=self.cur_datetime)
|
||||||
|
|
||||||
def get_data(self, current_time: datetime = datetime.now()) -> dict:
|
|
||||||
return super().get_data(self.cur_datetime)
|
|
||||||
|
|
|
@ -11,36 +11,38 @@ class Monitor:
|
||||||
session_config: dict,
|
session_config: dict,
|
||||||
pooling_delay: int | float = 0.1,
|
pooling_delay: int | float = 0.1,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
|
||||||
description
|
|
||||||
"""
|
|
||||||
self.device_list: List[Device] = []
|
self.device_list: List[Device] = []
|
||||||
self.session_config: dict = session_config
|
self.session_config: dict = session_config
|
||||||
self.pooling_delay: int | float = pooling_delay
|
self.pooling_delay: int | float = pooling_delay
|
||||||
|
|
||||||
def init_device(self, device: Device) -> None:
|
def init_device(self, device: Device) -> None:
|
||||||
self.device_list.append(device)
|
"""Инициализация конечного устройства для наблюдения"""
|
||||||
|
if isinstance(device, Device):
|
||||||
|
self.device_list.append(device)
|
||||||
|
else:
|
||||||
|
raise TypeError(
|
||||||
|
'Не могу инициализировать конечное устройство,\n' \
|
||||||
|
'init_device притимает только объекты-наследники класса Device'
|
||||||
|
)
|
||||||
|
|
||||||
def _get_device_data(self, device: Device) -> dict:
|
def __get_device_data(self, device: Device) -> dict:
|
||||||
|
"""Запрос данных от класса устройства
|
||||||
|
Нужно сделать валидацию структуры и параметры данных
|
||||||
|
"""
|
||||||
answer = device.get_data()
|
answer = device.get_data()
|
||||||
return answer
|
return answer
|
||||||
|
|
||||||
def __send_devices_data_to_db(self) -> None:
|
def __send_devices_data_to_db(self) -> None:
|
||||||
|
"""Сбор данных от всех устройств и отправка на сохранение"""
|
||||||
session = InfDBSession(config=self.session_config)
|
session = InfDBSession(config=self.session_config)
|
||||||
for device in self.device_list:
|
for device in self.device_list:
|
||||||
data: dict = self._get_device_data(device)
|
data: dict = self.__get_device_data(device)
|
||||||
session.add(data)
|
session.add(data)
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
def mainloop(self) -> None:
|
def mainloop(self) -> None:
|
||||||
|
"""Главный цикл монитора"""
|
||||||
while True:
|
while True:
|
||||||
self.__send_devices_data_to_db()
|
self.__send_devices_data_to_db()
|
||||||
time.sleep(self.pooling_delay)
|
time.sleep(self.pooling_delay)
|
||||||
|
|
||||||
def demo_get_device_data_from_file(self):
|
|
||||||
with open("telemetry.txt", mode="r") as f:
|
|
||||||
for line in f.readlines():
|
|
||||||
line = line.strip()
|
|
||||||
if line:
|
|
||||||
self.device_list[0].set_fake_answer(line)
|
|
||||||
self.__send_devices_data_to_db()
|
|
||||||
|
|
|
@ -20,9 +20,7 @@ class InfDBSession:
|
||||||
self.__write_api = self.__client.write_api(write_options=SYNCHRONOUS)
|
self.__write_api = self.__client.write_api(write_options=SYNCHRONOUS)
|
||||||
|
|
||||||
def add(self, point: dict) -> None:
|
def add(self, point: dict) -> None:
|
||||||
print(point)
|
|
||||||
self.__points_queue.append(Point.from_dict(point))
|
self.__points_queue.append(Point.from_dict(point))
|
||||||
print(len(self.__points_queue))
|
|
||||||
|
|
||||||
def commit(self) -> None:
|
def commit(self) -> None:
|
||||||
while self.__points_queue:
|
while self.__points_queue:
|
||||||
|
|
Loading…
Reference in New Issue