1# SPDX-FileCopyrightText: 2014-2024 Fredrik Ahlberg, Angus Gratton, 2# Espressif Systems (Shanghai) CO LTD, other contributors as noted. 3# 4# SPDX-License-Identifier: BSD-3-Clause 5import threading 6import time 7import logging 8import socket 9 10from esp_rfc2217_server.esp_port_manager import EspPortManager 11 12 13class Redirector(object): 14 def __init__(self, serial_instance, socket, debug=False, esp32r0delay=False): 15 self.serial = serial_instance 16 self.socket = socket 17 self._write_lock = threading.Lock() 18 self.rfc2217 = EspPortManager( 19 self.serial, 20 self, 21 esp32r0delay, 22 logger=logging.getLogger("rfc2217.server") if debug else None, 23 ) 24 self.log = logging.getLogger("redirector") 25 self.force_exit = False 26 27 def statusline_poller(self): 28 self.log.debug("status line poll thread started") 29 while self.alive: 30 time.sleep(1) 31 self.rfc2217.check_modem_lines() 32 self.log.debug("status line poll thread terminated") 33 34 def shortcircuit(self): 35 """connect the serial port to the TCP port by copying everything 36 from one side to the other""" 37 self.alive = True 38 self.thread_read = threading.Thread(target=self.reader) 39 self.thread_read.daemon = True 40 self.thread_read.name = "serial->socket" 41 self.thread_read.start() 42 self.thread_poll = threading.Thread(target=self.statusline_poller) 43 self.thread_poll.daemon = True 44 self.thread_poll.name = "status line poll" 45 self.thread_poll.start() 46 self.writer() 47 48 def reader(self): 49 """loop forever and copy serial->socket""" 50 self.log.debug("reader thread started") 51 while self.alive: 52 try: 53 data = self.serial.read(self.serial.in_waiting or 1) 54 if data: 55 # escape outgoing data when needed (Telnet IAC (0xff) character) 56 self.write(b"".join(self.rfc2217.escape(data))) 57 except socket.error as msg: 58 self.log.error("{}".format(msg)) 59 # probably got disconnected 60 break 61 self.alive = False 62 self.log.debug("reader thread terminated") 63 64 def write(self, data): 65 """thread safe socket write with no data escaping. used to send telnet stuff""" 66 with self._write_lock: 67 self.socket.sendall(data) 68 69 def writer(self): 70 """loop forever and copy socket->serial""" 71 while self.alive: 72 try: 73 data = self.socket.recv(1024) 74 if not data: 75 break 76 self.serial.write(b"".join(self.rfc2217.filter(data))) 77 except socket.error as msg: 78 self.log.error("{}".format(msg)) 79 # probably got disconnected 80 break 81 self.stop() 82 83 def stop(self): 84 """Stop copying""" 85 self.log.debug("stopping") 86 if self.alive: 87 self.alive = False 88 self.thread_read.join() 89 self.thread_poll.join() 90