Imported core, m, ag and serial for refactoring
This commit is contained in:
parent
6f28ac0382
commit
5baa9b75d0
25 changed files with 918 additions and 4 deletions
0
serial/__init__.py
Normal file
0
serial/__init__.py
Normal file
0
serial/build.sh
Executable file
0
serial/build.sh
Executable file
190
serial/com.py
Normal file
190
serial/com.py
Normal file
|
@ -0,0 +1,190 @@
|
|||
#! /bin/env python3
|
||||
# encoding: utf-8
|
||||
|
||||
import time
|
||||
import serial
|
||||
|
||||
from . import et
|
||||
|
||||
prefix = et.prefix
|
||||
|
||||
"""
|
||||
===== OverTalk protocol spec =====
|
||||
==== Transport layer ====
|
||||
|
||||
frame = [ 0x2a | destination (1) | source (1) | payload length (1) | payload (n) | checksum (1) ]
|
||||
|
||||
Any time an 0x2a is encountered the parser should flush its state and begin reading a new frame.
|
||||
Bytes 0x2a, 0x2b, 0x11 and 0x13 are prefixed with 0x2b and inverted (XORed with 0xff) to avoid collisions.
|
||||
|
||||
Frames with destinations unknown to an OverShell are sent to the default interface, or dropped if
|
||||
that's the interface they came from.
|
||||
|
||||
==== Command layer ====
|
||||
Payload consists of one or more commands:
|
||||
|
||||
get_command = [ 0x00 | register (1) | target register (1) ]
|
||||
set_command = [ 0x01 | register (1) | length (1) | value (n) ]
|
||||
|
||||
With a get_command the sender requests the receiver to read its own "register" and issue
|
||||
a set_command that sets the sender's "target register" to that value.
|
||||
|
||||
A set_command does what is says on the box.
|
||||
"""
|
||||
|
||||
class Transport:
|
||||
"""
|
||||
OverTalk Transport layer implementation.
|
||||
|
||||
Reads data from multiple interfaces and either router frames or receives them.
|
||||
A received frame is then made available via a public attribute.
|
||||
|
||||
Interfaces are objects that implement methods read() (returns one byte of data as int), write(buffer, len),
|
||||
and waiting property (contains number of bytes waiting).
|
||||
"""
|
||||
def __init__(self, my_id, interfaces, def_route):
|
||||
"""
|
||||
@param my_id OverTalk address of this Transport instance
|
||||
@param interfaces a dict of interfaces keyed by their IDs
|
||||
@param def_route ID of default interface for sending
|
||||
"""
|
||||
|
||||
assert my_id not in interfaces
|
||||
assert def_route in interfaces
|
||||
|
||||
self._print = et.Output("over.com.Transport", default_suffix="\n", timestamp=True)
|
||||
|
||||
self.my_id = my_id
|
||||
self.def_route = def_route
|
||||
self.interfaces = interfaces
|
||||
|
||||
self.destination_unknown = 0
|
||||
self.incoming = None
|
||||
|
||||
self._print("<Cg>on-line<C/>")
|
||||
|
||||
def update(self):
|
||||
self.incoming = None
|
||||
|
||||
for interface_id, interface in self.interfaces.items():
|
||||
if interface.waiting:
|
||||
interface.last_data_received = time.time()
|
||||
byte = interface.read()
|
||||
|
||||
if byte == 0x2a:
|
||||
interface.rxbuffer = []
|
||||
interface.reading_escape = False
|
||||
interface.reading_frame = True
|
||||
|
||||
if byte == 0x2b:
|
||||
interface.reading_escape = True
|
||||
continue
|
||||
|
||||
if interface.reading_escape:
|
||||
byte ^= 0xff
|
||||
interface.reading_escape = False
|
||||
|
||||
if interface.reading_frame:
|
||||
interface.rxbuffer.append(byte)
|
||||
|
||||
if self.verify_frame(interface):
|
||||
self.process_frame(interface_id, interface.rxbuffer)
|
||||
interface.rxbuffer = []
|
||||
|
||||
def verify_frame(self, interface):
|
||||
buffer_length = len(interface.rxbuffer)
|
||||
|
||||
if buffer_length >= 4:
|
||||
# at this point we can read frame's declared size
|
||||
if buffer_length >= interface.rxbuffer[3] + 5: # header (4) + payload + checksum (1)
|
||||
# a frame has been read, huzzah!
|
||||
interface.reading_frame = False
|
||||
|
||||
# checksum
|
||||
if sum(interface.rxbuffer[:-1]) % 0x100 == interface.rxbuffer[-1]:
|
||||
return True
|
||||
|
||||
else:
|
||||
interface.malformed_frames += 1
|
||||
self._print("broken frame received: <Cr>%s<C/>" %(interface.rxbuffer))
|
||||
interface.rxbuffer = []
|
||||
|
||||
return False
|
||||
|
||||
def escape_frame(self, frame):
|
||||
assert frame[0] == 0x2a
|
||||
|
||||
frame_escaped = [0x2a]
|
||||
|
||||
for byte in frame[1:]:
|
||||
if byte in (0x2a, 0x2b, 0x11, 0x13):
|
||||
frame_escaped.append(0x2b)
|
||||
byte ^= 0xff
|
||||
|
||||
frame_escaped.append(byte)
|
||||
|
||||
return frame_escaped
|
||||
|
||||
def process_frame(self, source_interface_id, frame):
|
||||
payload = frame[4:-1]
|
||||
destination = frame[1]
|
||||
|
||||
if destination == self.my_id:
|
||||
self.incoming = (frame[2], payload)
|
||||
else:
|
||||
if destination in self.interfaces:
|
||||
self._print("routing frame to [%d]" %(destination))
|
||||
self.interfaces[destination].write(self.escape_frame(frame))
|
||||
else:
|
||||
if source_interface_id == self.def_route:
|
||||
self.destination_unknown += 1
|
||||
self._print("unknown destination <Cr>%d<C/> for frame: <Cy>%s<C/>" %(destination,
|
||||
repr(frame)), prefix.fail)
|
||||
else:
|
||||
self._print("routing frame to default route [%d]" %(self.def_route))
|
||||
self.interfaces[self.def_route].write(self.escape_frame(frame))
|
||||
|
||||
def send_data(self, destination, data):
|
||||
frame = [0x2a, destination, self.my_id, len(data)] + list(data)
|
||||
frame.append(sum(frame) % 0x100)
|
||||
frame = self.escape_frame(frame)
|
||||
|
||||
if destination in self.interfaces:
|
||||
s = self.interfaces[destination]
|
||||
else:
|
||||
s = self.interfaces[self.def_route]
|
||||
|
||||
s.write(frame)
|
||||
|
||||
class TTL_Interface:
|
||||
def __init__(self, interface="/dev/ttyUSB0", baudrate=57600):
|
||||
try:
|
||||
self.s = serial.Serial(interface, baudrate, timeout=1)
|
||||
except serial.serialutil.SerialException:
|
||||
self.s = None
|
||||
|
||||
self.rxbuffer = []
|
||||
self.reading_escape = False
|
||||
self.reading_frame = False
|
||||
|
||||
self.malformed_frames = 0
|
||||
self.last_data_received = 0
|
||||
|
||||
@property
|
||||
def waiting(self):
|
||||
if self.s:
|
||||
return self.s.inWaiting()
|
||||
else:
|
||||
return 0
|
||||
|
||||
def read(self):
|
||||
if self.s:
|
||||
return ord(self.s.read())
|
||||
else:
|
||||
return 0
|
||||
|
||||
def write(self, data):
|
||||
print("Sending:", ''.join([hex(x)[2:].zfill(2) for x in data]))
|
||||
|
||||
if self.s:
|
||||
self.s.write(bytes(data))
|
Loading…
Add table
Add a link
Reference in a new issue