commit dc4c035a536dd44b7c429ffd6430a248926ce864 Author: Denis Fedoseev Date: Sun Oct 1 22:41:21 2023 +0300 Initial commit diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/README.md b/README.md new file mode 100644 index 0000000..23bd2e6 --- /dev/null +++ b/README.md @@ -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 + ... + + diff --git a/command_parser/__init__.py b/command_parser/__init__.py new file mode 100644 index 0000000..e10f3b0 --- /dev/null +++ b/command_parser/__init__.py @@ -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) diff --git a/config.yml b/config.yml new file mode 100644 index 0000000..17a2c64 --- /dev/null +++ b/config.yml @@ -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 \ No newline at end of file diff --git a/data_processor/__init__.py b/data_processor/__init__.py new file mode 100644 index 0000000..35eb334 --- /dev/null +++ b/data_processor/__init__.py @@ -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) diff --git a/reader.py b/reader.py new file mode 100644 index 0000000..6d1c2b5 --- /dev/null +++ b/reader.py @@ -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() diff --git a/transport/base/__init__.py b/transport/base/__init__.py new file mode 100644 index 0000000..230be76 --- /dev/null +++ b/transport/base/__init__.py @@ -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!') diff --git a/transport/serial/__init__.py b/transport/serial/__init__.py new file mode 100644 index 0000000..2ab000f --- /dev/null +++ b/transport/serial/__init__.py @@ -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() diff --git a/transport/tcp/__init__.py b/transport/tcp/__init__.py new file mode 100644 index 0000000..835e178 --- /dev/null +++ b/transport/tcp/__init__.py @@ -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') diff --git a/writer.py b/writer.py new file mode 100644 index 0000000..9b6c486 --- /dev/null +++ b/writer.py @@ -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) diff --git a/writer_tcp.py b/writer_tcp.py new file mode 100644 index 0000000..a73f712 --- /dev/null +++ b/writer_tcp.py @@ -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)