# Serial Backup Server # Copyright © 2015 Jeffrey Armstrong # http://jeff.rainbow-100.com/ # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import time import serial import argparse import os import os.path DEFAULT_PORT = 19200 DEFAULT_DEV = '/dev/ttyUSB0' OK = "OK" RETRY = "RETRY" ABORT = "ABORT" def respond(ser, msg): """Writes a response message to the client""" ser.write(msg) ser.write(";") def fletcher16(data): """Computes the Fletcher 16 checksum of a data stream""" c0 = 0 c1 = 0 for b in data: c0 = (c0 + ord(b)) c1 = (c1 + c0) cb0 = 255 - ((c0+c1) % 255) cb1 = 255 - ((c0 + cb0) % 255) return cb0, cb1 def receive_file(ser, fp): """Receives a file block from the client, determines its validity, and responds accordingly.""" file_contents = b'' length = ord(ser.read()) err = 0 while length > 0: #print(" receiving {0} bytes".format(length)) bytes = b'' for i in range(0, length): bytes = bytes + ser.read() fl1 = ser.read() fl2 = ser.read() v1, v2 = fletcher16(bytes) # print("Fletcher16 - Rainbow({0}, {1}) Python({2}, {3})".format(ord(fl1), ord(fl2), v1, v2)) if (v1 == ord(fl1) and v2 == ord(fl2)): fp.write(bytes) respond(ser, OK) elif err < 10: respond(ser, RETRY) err = err + 1 if err == 1: fp.write(bytes) print(" block retry {0}...".format(err)) else: respond(ser, ABORT) print(" block abort") break length = ord(ser.read()) def readtext(ser, bytes=1): """Reads a string of bytes from the serial port and converts them to a string if necessary (i.e. Python 3)""" b = ser.read(bytes) try: return str(b,'ascii') except TypeError: return b def init(ser): """Waits for an initialization message from the client""" print("Waiting for 'ONLINE'...") while ser.inWaiting() < 6: time.sleep(1) x = readtext(ser, 6) if x == 'ONLINE': print("Received 'ONLINE' message. Awaiting files...") return True return False def readfilename(ser): """Reads the next filename from the client""" fname = b'' b = ser.read() while b != b';': fname = fname + b b = ser.read() try: return str(fname, 'ascii') except TypeError: return fname def prepfile(filename): """Returns an open file for writing based on the reported DOS name""" newfile = filename.replace(':', '') if os.sep != '\\': newfile = newfile.replace('\\', os.sep) dirs = newfile.split(os.sep) fullpath = '' for d in dirs[:-1]: fullpath = os.path.join(fullpath,d) if not os.path.exists(fullpath): os.mkdir(fullpath) return open(newfile, "w") def main_transfer(ser): """Main program loop""" # First wait for the online signal if not init(ser): print("Initialize failed. Quitting.") return while True: fname = readfilename(ser) fp = prepfile(fname) if fp is None: break print("Receiving {0}".format(fname)) respond(ser, OK) receive_file(ser, fp) fp.close() def main(port='/dev/ttyUSB0', speed=19200): """Opens the serial port and starts the transfer""" ser = serial.Serial(port, speed) main_transfer(ser) ser.close() if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('-p', '--port', dest='port', default='/dev/ttyUSB0') parser.add_argument('-s', '--speed', dest='speed', type=int, default=19200) res = parser.parse_args() main(port=res.port, speed=res.speed)