-
Notifications
You must be signed in to change notification settings - Fork 5
/
server.py
101 lines (74 loc) · 2.92 KB
/
server.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import argparse
import asyncio
from configparser import ConfigParser
import functools
import logging
import logging.config
import os
from .messaging import (
parse_inverter_message,
mock_server_response
)
from .persistence import persistence_client
__clock = 0
logger = logging.getLogger("solis_service")
def load_config(config_file=None):
if config_file is None:
config_file_name = "solis-service.conf"
candidates = [os.path.realpath(os.path.join(prefix, config_file_name))
for prefix in [".", "/etc", "/usr/local/etc"]]
for candidate in candidates:
if os.path.exists(candidate):
config_file = candidate
if config_file is None:
raise ValueError("No config file found")
config_file_path = os.path.realpath(config_file)
logging.config.fileConfig(config_file)
config = ConfigParser()
config.read(config_file_path)
return config
def increment_clock():
global __clock
return ++__clock & 255
def _heartbeat_response():
return mock_server_response(increment_clock(), b'\x11')
def _data_response():
return mock_server_response(increment_clock(), b'\x12')
def _is_heartbeat(message):
return len(message) == 99
def _is_data_packet(message):
return len(message) == 246
async def handle_inverter_message(persist, reader, writer):
buffer_size = 512
message = await reader.read(buffer_size)
if _is_heartbeat(message):
logger.debug(f'Received heartbeat message from {writer.transport.get_extra_info("peername")}')
writer.write(_heartbeat_response())
writer.close()
elif _is_data_packet(message):
writer.write(_data_response())
writer.close()
inverter_data = parse_inverter_message(message)
logger.debug(f'Received data message from {writer.transport.get_extra_info("peername")}')
logger.debug(f'data message: {inverter_data}')
logger.debug(f'persisting data with {persist.description}')
result = await persist.write_measurement(inverter_data)
logger.debug(f'persistence result: {result}')
async def main(hostname, port, config):
logger.info(f"Starting server on {hostname}:{port}")
with persistence_client(config) as client:
server = await asyncio.start_server(functools.partial(handle_inverter_message, client), hostname, port)
async with server:
await server.serve_forever()
def run():
parser = argparse.ArgumentParser(
description="Receive messages from a Solis/Ginsong inverter and persist to a database")
parser.add_argument("--config", help="load a config file")
args = parser.parse_args()
config = load_config(args.config)
service_config = config["service"]
hostname = service_config.get("hostname", "localhost")
port = service_config.get("port", "9042")
asyncio.run(main(hostname, int(port), config))
if __name__ == "__main__":
run()