Reticulum/RNS/Interfaces/SerialInterface.py

137 lines
3.6 KiB
Python
Raw Permalink Normal View History

2020-04-22 10:07:13 +00:00
from .Interface import Interface
2018-03-19 15:39:08 +00:00
from time import sleep
2016-06-03 17:02:02 +00:00
import sys
import serial
import threading
2018-03-20 15:57:26 +00:00
import time
import RNS
2016-06-03 17:02:02 +00:00
class HDLC():
2020-05-07 12:14:35 +00:00
# The Serial Interface packetizes data using
# simplified HDLC framing, similar to PPP
FLAG = 0x7E
ESC = 0x7D
ESC_MASK = 0x20
@staticmethod
def escape(data):
data = data.replace(bytes([HDLC.ESC]), bytes([HDLC.ESC, HDLC.ESC^HDLC.ESC_MASK]))
data = data.replace(bytes([HDLC.FLAG]), bytes([HDLC.ESC, HDLC.FLAG^HDLC.ESC_MASK]))
return data
2016-06-03 17:02:02 +00:00
class SerialInterface(Interface):
MAX_CHUNK = 32768
owner = None
port = None
speed = None
databits = None
parity = None
stopbits = None
serial = None
2018-04-05 17:12:21 +00:00
def __init__(self, owner, name, port, speed, databits, parity, stopbits):
2018-03-19 15:39:08 +00:00
self.serial = None
2016-06-03 17:02:02 +00:00
self.owner = owner
2018-04-05 17:12:21 +00:00
self.name = name
2016-06-03 17:02:02 +00:00
self.port = port
self.speed = speed
self.databits = databits
self.parity = serial.PARITY_NONE
self.stopbits = stopbits
2018-03-20 15:57:26 +00:00
self.timeout = 100
self.online = False
2016-06-03 17:02:02 +00:00
if parity.lower() == "e" or parity.lower() == "even":
self.parity = serial.PARITY_EVEN
if parity.lower() == "o" or parity.lower() == "odd":
self.parity = serial.PARITY_ODD
try:
RNS.log("Opening serial port "+self.port+"...")
2016-06-03 17:02:02 +00:00
self.serial = serial.Serial(
port = self.port,
baudrate = self.speed,
bytesize = self.databits,
parity = self.parity,
stopbits = self.stopbits,
xonxoff = False,
rtscts = False,
2018-03-20 15:57:26 +00:00
timeout = 0,
inter_byte_timeout = None,
2016-06-03 17:02:02 +00:00
write_timeout = None,
dsrdtr = False,
)
except Exception as e:
2018-04-05 17:12:21 +00:00
RNS.log("Could not open serial port for interface "+str(self), RNS.LOG_ERROR)
2016-06-03 17:02:02 +00:00
raise e
if self.serial.is_open:
2018-03-20 15:57:26 +00:00
sleep(0.5)
2016-06-03 17:02:02 +00:00
thread = threading.Thread(target=self.readLoop)
thread.setDaemon(True)
thread.start()
2018-03-20 15:57:26 +00:00
self.online = True
RNS.log("Serial port "+self.port+" is now open")
2016-06-03 17:02:02 +00:00
else:
raise IOError("Could not open serial port")
def processIncoming(self, data):
self.owner.inbound(data, self)
2016-06-03 17:02:02 +00:00
def processOutgoing(self,data):
2018-03-20 15:57:26 +00:00
if self.online:
data = bytes([HDLC.FLAG])+HDLC.escape(data)+bytes([HDLC.FLAG])
2018-03-20 15:57:26 +00:00
written = self.serial.write(data)
if written != len(data):
raise IOError("Serial interface only wrote "+str(written)+" bytes of "+str(len(data)))
2016-06-03 17:02:02 +00:00
def readLoop(self):
2018-03-20 15:57:26 +00:00
try:
in_frame = False
escape = False
data_buffer = b""
2018-03-20 15:57:26 +00:00
last_read_ms = int(time.time()*1000)
2018-03-20 15:57:26 +00:00
while self.serial.is_open:
if self.serial.in_waiting:
byte = ord(self.serial.read(1))
2018-03-20 15:57:26 +00:00
last_read_ms = int(time.time()*1000)
if (in_frame and byte == HDLC.FLAG):
in_frame = False
self.processIncoming(data_buffer)
elif (byte == HDLC.FLAG):
in_frame = True
data_buffer = b""
elif (in_frame and len(data_buffer) < RNS.Reticulum.MTU):
if (byte == HDLC.ESC):
escape = True
else:
if (escape):
if (byte == HDLC.FLAG ^ HDLC.ESC_MASK):
byte = HDLC.FLAG
if (byte == HDLC.ESC ^ HDLC.ESC_MASK):
byte = HDLC.ESC
escape = False
data_buffer = data_buffer+bytes([byte])
2018-03-20 15:57:26 +00:00
else:
time_since_last = int(time.time()*1000) - last_read_ms
if len(data_buffer) > 0 and time_since_last > self.timeout:
data_buffer = b""
in_frame = False
escape = False
2018-03-20 15:57:26 +00:00
sleep(0.08)
except Exception as e:
self.online = False
RNS.log("A serial port error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log("The interface "+str(self.name)+" is now offline. Restart Reticulum to attempt reconnection.", RNS.LOG_ERROR)
2016-06-03 17:02:02 +00:00
def __str__(self):
return "SerialInterface["+self.name+"]"