#!/usr/bin/env python
#
# udp_slip.py
#
#    send and receive UDP SLIP packets
#    command line: python udp_slip.py serial_port serial_speed
#
# Neil Gershenfeld 11/22/09
#
# (c) Massachusetts Institute of Technology 2013
# This work may be reproduced, modified, distributed,
# performed, and displayed for any purpose. Copyright is
# retained and must be preserved. The work is provided
# as is; no warranty is provided, and users accept all 
# liability.
#

from Tkinter import *
from string import *
import serial, time, signal

TIMEOUT = 1
END = 192
ESC = 219
ESC_END = 220
ESC_ESC = 221

def handler(signum, frame):
   #
   # timeout handler
   #
   raise IOError, "timeout"

def high(number):
   #
   # return high byte
   #
   return (number >> 8)

def low(number):
   #
   # return low byte
   #
   return (number & 255)

def idle(parent):
   #
   # idle routine
   #
   if (ser.inWaiting() != 0):
      #
      # serial data waiting, read packet
      #
      # set timout alarm
      #
      signal.signal(signal.SIGALRM, handler)
      signal.alarm(TIMEOUT)
      #
      # try to read packet, otherwise set error message
      #
      try:
         get_packet()
      except:
         sindata.set("timeout")
      #
      # turn off alarm
      #
      signal.alarm(0)
   #
   # sleep to reduce OS load
   #
   time.sleep(0.001)
   parent.after_idle(idle,parent)

def output(byte):
   #
   # output a byte
   #
   ser.write(chr(byte))
   time.sleep(.001) # byte spacing (if needed)

def put_packet():
   #
   # send packet
   #
   ip_header_size = 20
   udp_header_size = 8
   outgoing_data_size = 10
   #
   # IP
   #
   packet = []
   packet.append(0x45) # version = 4, header length = 5 32-bit words
   packet.append(0) # type of service
   packet.append(high(ip_header_size + udp_header_size + outgoing_data_size)) # packet length
   packet.append(low(ip_header_size + udp_header_size + outgoing_data_size)) # packet length
   packet.append(0) # identification high byte
   packet.append(0) # identification low byte
   packet.append(0) # flag, fragment offset high byte
   packet.append(0) # flag, fragment offset low byte
   packet.append(255) # time to live
   packet.append(17) # protocol = 17 for UDP
   packet.append(0) # header checksum high byte
   packet.append(0) # header checksum low byte
   packet.append(int(soutsource1.get())) # source address byte 1
   packet.append(int(soutsource2.get())) # source address byte 2
   packet.append(int(soutsource3.get())) # source address byte 3
   packet.append(int(soutsource4.get())) # source address byte 4
   packet.append(int(soutdest1.get())) # destination address byte 1
   packet.append(int(soutdest2.get())) # destination address byte 2
   packet.append(int(soutdest3.get())) # destination address byte 3
   packet.append(int(soutdest4.get())) # destination address byte 4
   #
   # UDP
   #
   packet.append(high(int(soutsourceport.get()))) # source port
   packet.append(low(int(soutsourceport.get()))) # source port
   packet.append(high(int(soutdestport.get()))) # destination port
   packet.append(low(int(soutdestport.get()))) # destination port
   packet.append(high(udp_header_size + outgoing_data_size)) # payload length
   packet.append(low(udp_header_size + outgoing_data_size)) # payload length
   packet.append(0) # payload checksum (not used)
   packet.append(0) # payload checksum (not used)
   #
   # data
   #
   data = soutdata.get()
   for i in range(len(data)):
      packet.append(ord(data[i]))
   #
   # calculate checksum
   #
   """
   todo:
   ldi zh, high(outgoing_ip_start)
   ldi zl, low(outgoing_ip_start)
   ldi count, ip_header_size
   clr check_lo
   clr check_hi
   clr check_carry
   ip_checksum_loop:
      adiw zl, 1
      ld temp, z
      dec count
      sbiw zl, 1
      ld temp1, z
      add check_lo, temp
      adc check_hi, temp1
      adc check_carry, zero
      adiw zl, 2
      dec count
      brne ip_checksum_loop
   add check_lo, check_carry
   adc check_hi, zero
   com check_lo
   com check_hi
   sts outgoing_ip_checksum, check_hi
   sts outgoing_ip_checksum+1, check_lo
   """
   #
   # send the packet with SLIP mapping and framing
   #
   output(END)
   for byte in range(len(packet)):
      if (packet[byte] == END):
         output(ESC)
         output(ESC_END)
      elif (packet[byte] == ESC):
         output(ESC)
         output(ESC_ESC)
      else:
         output(packet[byte])
   output(END)

def get_packet():
   #
   # read and display packet
   #
   sindata.set("reading ...")
   root.update()
   packet_length_offset = 2
   source_address_offset = 12
   destination_address_offset = 16
   source_port_offset = 20
   destination_port_offset = 22
   data_offset = 28
   #
   # read until END
   #
   packet = []
   while 1:
      byte = ord(ser.read())
      #
      # check for end
      #
      if (byte == END):
         #
         # check if packet received
         #
         if (len(packet) > 0):
            #
            # packet received, update GUI
            #
            sinsource1.set(str(packet[source_address_offset+0]))
            sinsource2.set(str(packet[source_address_offset+1]))
            sinsource3.set(str(packet[source_address_offset+2]))
            sinsource4.set(str(packet[source_address_offset+3]))
            sindest1.set(str(packet[destination_address_offset+0]))
            sindest2.set(str(packet[destination_address_offset+1]))
            sindest3.set(str(packet[destination_address_offset+2]))
            sindest4.set(str(packet[destination_address_offset+3]))
            sinsourceport.set(str(256*packet[source_port_offset+0] \
               + packet[source_port_offset+1]))
            sindestport.set(str(256*packet[destination_port_offset+0] \
               + packet[destination_port_offset+1]))
            packet_length = 256*packet[packet_length_offset+0] \
               + packet[packet_length_offset+1]
            data = join(map(chr,packet[(data_offset):]),sep="")
            sindata.set(data)
            return
         else:
            #
            # no packet received, continue
            #
            continue
      #
      # not END, do SLIP mapping and save byte
      #
      if (byte == ESC):
         byte = ord(ser.read())
         if (byte == ESC_END):
            byte = END
         elif (byte == ESC_ESC):
            byte = ESC
         else:
            print "error: unknown ESC"
            break
      packet.append(byte)

def quit():
   #
   # clean up and quit
   #
   sys.exit()

#
# get command line arguments
#
if (len(sys.argv) != 3):
   print "command line: python udp_slip.py serial_port serial_speed"
   sys.exit()
serial_port = sys.argv[1]
serial_speed = int(sys.argv[2])
#
# set up GUI
#
root = Tk()
root.title('udp_slip.py')
#
incomingframe = Frame(root)
Label(incomingframe,text="incoming packet: ").pack(side="left")
incomingframe.pack()
#
insourceframe = Frame(root)
Label(insourceframe,text="source address: ").pack(side="left")
sinsource1 = StringVar()
winsource1 = Entry(insourceframe, width=3, textvariable=sinsource1)
winsource1.pack(side="left")
Label(insourceframe,text=".").pack(side="left")
sinsource2 = StringVar()
winsource2 = Entry(insourceframe, width=3, textvariable=sinsource2)
winsource2.pack(side="left")
Label(insourceframe,text=".").pack(side="left")
sinsource3 = StringVar()
winsource3 = Entry(insourceframe, width=3, textvariable=sinsource3)
winsource3.pack(side="left")
Label(insourceframe,text=".").pack(side="left")
sinsource4 = StringVar()
winsource4 = Entry(insourceframe, width=3, textvariable=sinsource4)
winsource4.pack(side="left")
insourceframe.pack()
#
indestframe = Frame(root)
Label(indestframe,text="destination address: ").pack(side="left")
sindest1 = StringVar()
windest1 = Entry(indestframe, width=3, textvariable=sindest1)
windest1.pack(side="left")
Label(indestframe,text=".").pack(side="left")
sindest2 = StringVar()
windest2 = Entry(indestframe, width=3, textvariable=sindest2)
windest2.pack(side="left")
Label(indestframe,text=".").pack(side="left")
sindest3 = StringVar()
windest3 = Entry(indestframe, width=3, textvariable=sindest3)
windest3.pack(side="left")
Label(indestframe,text=".").pack(side="left")
sindest4 = StringVar()
windest4 = Entry(indestframe, width=3, textvariable=sindest4)
windest4.pack(side="left")
indestframe.pack()
#
inportframe = Frame(root)
Label(inportframe,text="source port: ").pack(side="left")
sinsourceport = StringVar()
winsourceport = Entry(inportframe, width=4, textvariable=sinsourceport)
winsourceport.pack(side="left")
Label(inportframe,text="  destination port: ").pack(side="left")
sindestport = StringVar()
windestport = Entry(inportframe, width=4, textvariable=sindestport)
windestport.pack(side="left")
inportframe.pack()
#
indataframe = Frame(root)
Label(indataframe,text="data: ").pack(side="left")
sindata = StringVar()
windata = Entry(indataframe, width=30, textvariable=sindata)
windata.pack(side="left")
indataframe.pack()
#
outgoingframe = Frame(root)
Label(outgoingframe,text="\noutgoing packet: ").pack(side="left")
outgoingframe.pack()
#
outsourceframe = Frame(root)
Label(outsourceframe,text="source address: ").pack(side="left")
soutsource1 = StringVar()
soutsource1.set("10")
woutsource1 = Entry(outsourceframe, width=3, textvariable=soutsource1)
woutsource1.pack(side="left")
Label(outsourceframe,text=".").pack(side="left")
soutsource2 = StringVar()
soutsource2.set("0")
woutsource2 = Entry(outsourceframe, width=3, textvariable=soutsource2)
woutsource2.pack(side="left")
Label(outsourceframe,text=".").pack(side="left")
soutsource3 = StringVar()
soutsource3.set("0")
woutsource3 = Entry(outsourceframe, width=3, textvariable=soutsource3)
woutsource3.pack(side="left")
Label(outsourceframe,text=".").pack(side="left")
soutsource4 = StringVar()
soutsource4.set("1")
woutsource4 = Entry(outsourceframe, width=3, textvariable=soutsource4)
woutsource4.pack(side="left")
outsourceframe.pack()
#
outdestframe = Frame(root)
Label(outdestframe,text="destination address: ").pack(side="left")
soutdest1 = StringVar()
soutdest1.set("10")
woutdest1 = Entry(outdestframe, width=3, textvariable=soutdest1)
woutdest1.pack(side="left")
Label(outdestframe,text=".").pack(side="left")
soutdest2 = StringVar()
soutdest2.set("0")
woutdest2 = Entry(outdestframe, width=3, textvariable=soutdest2)
woutdest2.pack(side="left")
Label(outdestframe,text=".").pack(side="left")
soutdest3 = StringVar()
soutdest3.set("0")
woutdest3 = Entry(outdestframe, width=3, textvariable=soutdest3)
woutdest3.pack(side="left")
Label(outdestframe,text=".").pack(side="left")
soutdest4 = StringVar()
soutdest4.set("2")
woutdest4 = Entry(outdestframe, width=3, textvariable=soutdest4)
woutdest4.pack(side="left")
outdestframe.pack()
#
outportframe = Frame(root)
Label(outportframe,text="source port: ").pack(side="left")
soutsourceport = StringVar()
soutsourceport.set("0")
woutsourceport = Entry(outportframe, width=4, textvariable=soutsourceport)
woutsourceport.pack(side="left")
Label(outportframe,text="  destination port: ").pack(side="left")
soutdestport = StringVar()
soutdestport.set("1234")
woutdestport = Entry(outportframe, width=4, textvariable=soutdestport)
woutdestport.pack(side="left")
outportframe.pack()
#
outdataframe = Frame(root)
Label(outdataframe,text="data: ").pack(side="left")
soutdata = StringVar()
woutdata = Entry(outdataframe, width=30, textvariable=soutdata)
woutdata.pack(side="left")
outdataframe.pack()
#
ioframe = Frame(root)
wbtn = Button(ioframe, text="send outgoing packet",command=put_packet)
wbtn.pack(side="left")
ioframe.pack()
#
spaceframe = Frame(root)
Label(spaceframe,text=" ").pack(side="left")
spaceframe.pack()
#
quitframe = Frame(root)
wquit = Button(quitframe, text="quit",command=quit)
wquit.pack(side="left")
quitframe.pack()
#
# open serial port
#
ser = serial.Serial(port=serial_port, baudrate=serial_speed)
ser.flushInput()
#
# start mainloop
#
root.after(100,idle,root)
root.mainloop()
