Initial commit

This commit is contained in:
Denis Fedoseev 2023-10-01 22:41:21 +03:00
commit dc4c035a53
11 changed files with 305 additions and 0 deletions

3
.idea/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

31
README.md Normal file
View file

@ -0,0 +1,31 @@
Создаем пару виртуальных портов:
$ socat -d -d pty,rawer,echo=0 pty,rawer,echo=0
2023/10/01 03:18:39 socat[965194] N PTY is /dev/pts/16
2023/10/01 03:18:39 socat[965194] N PTY is /dev/pts/19
Запускаем писателя на другой консоли:
$ ./writer.py
Write: GET_A
Write: GET_B
Write: GET_C
Write: GET_NORESP
Write: INVALID_CMD
...
На следующей консоли за пускаем парсер:
$ ./reader.py
01-10-2023 15:17:07 DEBUG:read: GET_B
01-10-2023 15:17:07 INFO:response_str: B_5V
01-10-2023 15:17:09 DEBUG:read: GET_C
01-10-2023 15:17:09 INFO:response_str: C_15A
01-10-2023 15:17:11 DEBUG:read: GET_NORESP
01-10-2023 15:17:11 WARNING:No value for sensor: NORESP
01-10-2023 15:17:13 DEBUG:read: INVALID_CMD
01-10-2023 15:17:13 WARNING:Command parser error: Unknown command: INVALID
01-10-2023 15:17:15 DEBUG:read: GET_A
...

View file

@ -0,0 +1,13 @@
class CommandParser:
@staticmethod
def parse(command_string):
parsed_command = command_string.split('_')
command = parsed_command[0]
value = parsed_command[1]
match command:
case 'GET':
return {'command': command, 'value': value}
case _:
raise ValueError("Unknown command: "+command)

20
config.yml Normal file
View file

@ -0,0 +1,20 @@
#transport:
# type: serial
# tty_name: "/dev/pts/19"
#
#writer:
# transport:
# type: serial
# tty_name: "/dev/pts/16"
transport:
type: tcp
host: "127.0.0.1"
port: 8889
writer:
transport:
type: tcp
host: "127.0.0.1"
port: 8889

View file

@ -0,0 +1,67 @@
import importlib
import logging
import sys
from command_parser import CommandParser
# В идеале, тут должен быть какой-то класс собирающий данные, но не вижу смысла увеличивать количество кода сейчас
response_data = {
'A': '10V',
'B': '5V',
'C': '15A'
}
response_template = '{sensor}_{value}'
logging.basicConfig(format='%(asctime)s %(levelname)s:%(message)s', datefmt='%d-%m-%Y %H:%M:%S', level=logging.DEBUG)
class DataProcessor:
def __init__(self, config):
self.config = config
try:
package = 'transport.{type}'
full_package_name = package.format(type = self.config["transport"]["type"])
cls = getattr(importlib.import_module(full_package_name), 'Transport')
self.transport = cls(self.config["transport"])
except Exception as err:
print("Can't create transport: ", err)
# raise err
sys.exit(1)
def run(self):
logging.debug("Waiting for commands...")
while True:
command_string = self.transport.readline()
logging.debug('read: %s', command_string)
try:
command = CommandParser.parse(command_string)
except ValueError as err:
logging.warning("Command parser error: %s", err)
self.transport.writeline('Failed to process command!')
else:
parsed_command_name = command.get('command')
response_str = None
match parsed_command_name:
case 'GET':
sensor_name = command.get('value')
response_value = response_data.get(sensor_name)
if bool(response_value):
response_str = response_template.format(sensor=command['value'], value=response_value)
logging.info('response_str: %s', response_str)
else:
response_str = '00'
logging.warning('No value for sensor: %s', sensor_name)
case _:
print('Unknown command: %s', parsed_command_name)
response_str = 'Unknown command'
self.transport.writeline(response_str)

31
reader.py Normal file
View file

@ -0,0 +1,31 @@
import getopt
import sys
import yaml
from data_processor import DataProcessor
config_name = 'config.yml'
options = "hc:"
long_options = ["help", "config="]
argumentList = sys.argv[1:]
arguments, values = getopt.getopt(argumentList, options, long_options)
for currentArgument, currentValue in arguments:
if currentArgument in ("-h", "--help"):
print("reader.py [--config=path/to/config.yml]")
sys.exit(0)
elif currentArgument in ("-c", "--config"):
config_name = currentValue
with open(config_name) as f:
config = yaml.load(f, Loader=yaml.FullLoader)
print("config: ", config)
processor = DataProcessor(config)
processor.run()

View file

@ -0,0 +1,12 @@
class TransportBase:
def __init__(self, config):
self.config = config
def writeline(self, cmd_str):
raise Exception('Implement me!')
def readline(self):
raise Exception('Implement me!')
def close(self):
raise Exception('Implement me!')

View file

@ -0,0 +1,28 @@
import serial
from transport.base import TransportBase
class Transport(TransportBase):
def __init__(self, config):
super().__init__(config)
self.ser = serial.Serial()
self.ser.port = config["tty_name"]
self.ser.open()
self.ser.flushInput()
self.ser.flushOutput()
self.addr = None
def writeline(self, cmd_str):
self.ser.write(str.encode(cmd_str + "\n"))
return 1
def readline(self):
buffer = self.ser.readline()
return buffer.decode('utf-8').rstrip()
def close(self):
self.ser.close()

51
transport/tcp/__init__.py Normal file
View file

@ -0,0 +1,51 @@
import socket
from transport.base import TransportBase
class Transport(TransportBase):
def __init__(self, config):
super().__init__(config)
self.listener = self.__connect(config['host'], config['port'])
self.buffer = bytearray()
def writeline(self, cmd_str):
response_string = cmd_str + "\n"
self.conn.sendall(bytes(response_string, 'utf-8'))
def readline(self):
data = self.conn.recv(16) # команды все короткие, нет смысла много читать из буфера
# print('data: ', data)
if not data:
return ''
line = self.__findLine(data)
return line
def close(self):
self.listener.close()
def __connect(self, host='127.0.0.1', port=12345):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as socket_listener:
socket_listener.bind((host, port))
socket_listener.listen()
conn, addr = socket_listener.accept()
self.conn = conn
return socket_listener
def __findLine(self, data):
data_chunk = bytearray(self.buffer)
data_chunk.extend(data)
# print("line: ", data_chunk)
line_separator_index = data_chunk.find(b"\n")
# print('n idx: ', line_separator_index)
if line_separator_index > 0:
line = data_chunk[0: line_separator_index]
self.buffer = data_chunk[line_separator_index+1:]
return line.decode('utf-8')

24
writer.py Normal file
View file

@ -0,0 +1,24 @@
from time import sleep
import yaml
from transport.serial import Transport
config_name = "config.yml"
with open(config_name) as f:
config = yaml.load(f, Loader=yaml.FullLoader)
connector = Transport(config["writer"]["transport"])
commands_list = ['GET_A', 'GET_B', 'GET_C', 'GET_NORESP', 'INVALID_CMD']
while (True):
for cmd in commands_list:
print('Write: ' + cmd)
connector.writeline(cmd)
sleep(0.5)
print("Response: "+connector.readline())
sleep(1)

25
writer_tcp.py Normal file
View file

@ -0,0 +1,25 @@
import socket
from time import sleep
import yaml
config_name = "config.yml"
with open(config_name) as f:
config = yaml.load(f, Loader=yaml.FullLoader)
commands_list = ['GET_A', 'GET_B', 'GET_C', 'GET_NORESP', 'INVALID_CMD']
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((config["writer"]["transport"]["host"], config["writer"]["transport"]["port"]))
while (True):
for cmd in commands_list:
print('Write: ' + cmd)
s.sendall(bytes(cmd+"\n", 'utf-8'))
# data = s.recv(1024)
sleep(0.5)
# print("Response: ", data.decode('utf-8'))
sleep(1)