# -*- coding: utf8 -*-

import serial
import threading
import random

# Datenmodell

port = 'com1'

class SenderEmpfaenger1(object):
    def __init__(self):
        self.s = serial.Serial(port)
        self.s.setRTS(False)
        self.bitzeit = 1.0
        self.bitpufferEmpfangen = ''
        self.bitpufferGesendet = ''

    def __del__(self):
        self.schnittstelleSchliessen()

    def schnittstelleSchliessen(self):
        self.s.close()

    def setBitzeit(self, t):
        self.bitzeit = t
    
    def getBitpufferEmpfangen(self):
        return self.bitpufferEmpfangen

    def resetBitpufferEmpfangen(self):
        self.bitpufferEmpfangen = ''

    def getBitpufferGesendet(self):
        return self.bitpufferGesendet

    def resetBitpufferGesendet(self):
        self.bitpufferGesendet = ''

    # Sender

    def istAktivSender(self):
        return True

    def bitfolgeSenden(self, bitfolge):
        # Leitung auf 0 setzen fuer die steigende Flanke
        self.s.setRTS(0)
        time.sleep(self.bitzeit)
        # Startbit senden
        self.s.setRTS(1)
        time.sleep(self.bitzeit)
        # Bits senden
        self.bitpufferGesendet = ''
        anzahlBits = len(bitfolge)
        i = 0
        while self.istAktivSender() and i < anzahlBits:
            bit = bitfolge[i]
            # Datenbit senden
            if bit == '1':
                self.s.setRTS(1)
                #print('1')
            else:
                self.s.setRTS(0)
                #print('0')
            self.bitpufferGesendet = self.bitpufferGesendet + bit
            time.sleep(self.bitzeit)
            i = i + 1
        # Stoppbit senden
        self.s.setRTS(0)
        time.sleep(self.bitzeit)
        self.bitpufferGesendet = ''

    # Empfaenger
        
    def istAktivEmpfaenger(self):
        return True

    def bitfolgeEmpfangen(self, anzahlBits):
        self.zustand = 'Startbit abwarten'
        alt = self.s.getCTS()
        neu = self.s.getCTS()
        while self.istAktivEmpfaenger() and self.zustand == 'Startbit abwarten':
            if alt == 0 and neu == 1:
                # Empfang der Daten starten
                self.zustand = 'Byte empfangen'
            else:
                # weiter Abtasten
                alt = neu
                neu = self.s.getCTS()
                time.sleep(0)
        # Bits empfangen
        self.bitpufferEmpfangen = ''
        bitfolge = ''
        # zur Mitte des Datenbits
        time.sleep(self.bitzeit*1.5)
        i = 0
        while i < anzahlBits:
            if self.s.getCTS() == 1:
                #print("1")
                bit = '1'
            else:
                #print("0")
                bit = '0'
            bitfolge = bitfolge + bit
            self.bitpufferEmpfangen = self.bitpufferEmpfangen + bit
            time.sleep(self.bitzeit)
            i = i + 1
        return bitfolge


class SenderEmpfaenger2(SenderEmpfaenger1):
    def __init__(self):
        SenderEmpfaenger1.__init__(self)
        self.adresse = 0
        self.puffer = b''
        
    def setAdresse(self, a):
        self.adresse = a

    def getPuffer(self):
        return self.puffer

    def resetPuffer(self):
        self.puffer = b''

    def byteToBin(self, byte):
        return bin(byte)[2:].zfill(8)

    def rahmenSenden(self, bytefolge, adresseSender, adresseEmpfaenger):
        print('Senden gestartet')
        while self.istAktivSender() and bytefolge != b'':
            try:
                byte = bytefolge[0]
                bitfolge = self.byteToBin(byte)
                halbByteAdresseSender = bin(self.adresse)[2:].zfill(4)
                halbByteAdresseEmpfaenger = bin(adresseEmpfaenger)[2:].zfill(4)
                # Rahmen bilden
                rahmen = halbByteAdresseSender + halbByteAdresseEmpfaenger + bitfolge
                # senden
                print('sende', str(bytes([byte]), 'iso-8859-1'), format(byte,'3d'), rahmen)
                self.bitfolgeSenden(rahmen)
                # nächstes Byte
                bytefolge = bytefolge[1:]
            except:
                pass
        print('Senden gestoppt')

    def binToByte(self, bitfolge):
        return int(bitfolge, 2) 

    def rahmenEmpfangen(self):
        print('Empfang gestartet')
        while self.istAktivEmpfaenger():
            try:
                # empfangen
                bitfolge =  self.bitfolgeEmpfangen(4+4+8)
                halbByteAdresseSender = bitfolge[0:4]
                halbByteAdresseEmpfaenger = bitfolge[4:8]
                halbByteEigeneAdresse = bin(self.adresse)[2:].zfill(4)
                if halbByteAdresseEmpfaenger == halbByteEigeneAdresse or halbByteAdresseEmpfaenger == '1111':
                    bitfolgeDaten = bitfolge[8:16]
                    byte = self.binToByte(bitfolgeDaten)
                    print('empfange', bitfolge, format(byte,'3d'), str(bytes([byte]), 'iso-8859-1'))
                    self.puffer = self.puffer + bytes([byte])                   
            except:
                pass
        print('Empfang gestoppt')

class SenderEmpfaengerThread(SenderEmpfaenger2):
    def __init__(self):
        SenderEmpfaenger2.__init__(self)
        self.stoppFlagEmpfaenger = threading.Event()
        self.stoppFlagSender = threading.Event()
     
    def starteThreadNachrichtSenden(self, bytefolge, adresseEmpfaenger):
        self.stoppFlagSender.set() # zuerst alten Thread sicher stoppen
        try:
            if (self.threadSender != None) and self.threadSender.is_alive():
                self.threadSender.join()
        except:
            pass
        self.stoppFlagSender.clear()
        self.threadSender = threading.Thread(target = self.rahmenSenden, args=(bytefolge, self.adresse, adresseEmpfaenger))
        self.threadSender.start() 

    def starteThreadNachrichtEmpfangen(self):
        self.stoppFlagEmpfaenger.set() # zuerst alten Thread sicher stoppen
        try:
            if (self.threadEmpfaenger != None) and self.threadEmpfaenger.is_alive():
                self.threadEmpfaenger.join()
        except:
            pass
        self.stoppFlagEmpfaenger.clear()
        self.threadEmpfaenger = threading.Thread(target=self.rahmenEmpfangen,args=())
        self.threadEmpfaenger.start()

    def stoppeThreadNachrichtEmpfangen(self):
        self.stoppFlagEmpfaenger.set()
        try:
            th = self.threadEmpfaenger
            if (th != None) and th.is_alive():            # falls Thread noch lebt
                th.join()                                 # auf Ende warten
        except:
            pass

    def stoppeThreadNachrichtSenden(self):
        self.stoppFlagSender.set()
        try:
            th = self.threadSender
            if (th != None) and th.is_alive():            # falls Thread noch lebt
                th.join()                                 # auf Ende warten
        except:
            pass

    def istAktivSender(self):
        return not self.stoppFlagSender.is_set()

    def istAktivEmpfaenger(self):
        return not self.stoppFlagEmpfaenger.is_set()


# GUI

import tkinter

import time

class GUI(tkinter.Tk):

    def __init__(self, model):
        tkinter.Tk.__init__(self)
        # Referenzattribut
        self.model = model
        # Gui schliessen
        self.protocol("WM_DELETE_WINDOW", self.beenden)
        # Fenster
        self.title("Sender/Empfänger")
        self.geometry('400x320+600+200')
        # rahmen
        self.frameEinstellungen = tkinter.Frame(master=self, background="gray")
        self.frameEinstellungen.place(x=5, y=5, width=390, height=60)
        self.frameSender = tkinter.Frame(master=self, background="#D5E88F")
        self.frameSender.place(x=5, y=70, width=390, height=125)
        self.frameMedium = tkinter.Frame(master=self, background="#FBD975")
        self.frameMedium.place(x=5, y=200, width=390, height=50)
        self.frameEmpfaenger = tkinter.Frame(master=self, background="#FFCFC9")
        self.frameEmpfaenger.place(x=5, y=255, width=390, height=60)
        # Einstellungen
        self.lSender = tkinter.Label(master=self.frameEinstellungen, background="gray", anchor=tkinter.E, text='EINSTELLUNGEN')
        self.lSender.place(x=275, y=5, width=100)
        self.lB = tkinter.Label(master=self.frameEinstellungen, background="gray", text='Bitzeit:')
        self.lB.place(x=15, y=5)
        self.eA = tkinter.Entry(master=self.frameEinstellungen)
        self.eA.insert(0, '1.0')
        self.eA.place(x=115, y=5, width=40)
        self.lS = tkinter.Label(master=self.frameEinstellungen, background="gray", text='s')
        self.lS.place(x=165, y=5)
        self.bEinstellungenUebernehmen = tkinter.Button(master=self.frameEinstellungen, text="übernehmen", command=self.bUebernehmenClick)
        self.bEinstellungenUebernehmen.place(x=300,y=30, width=80)        
        self.lAdresseSender = tkinter.Label(master=self.frameEinstellungen, background="gray", text='eigene Adresse:')
        self.lAdresseSender.place(x=15, y=30)
        self.eAdresseSender = tkinter.Entry(master=self.frameEinstellungen)
        self.eAdresseSender.insert(0, str(self.model.adresse))
        self.eAdresseSender.place(x=115, y=30, width=40)
        # Sender
        self.lSender = tkinter.Label(master=self.frameSender, background="#D5E88F", anchor=tkinter.E, text='SENDER')
        self.lSender.place(x=275, y=5, width=100)
        self.lAdresseEmpfaenger = tkinter.Label(master=self.frameSender, background="#D5E88F", text='Zieladresse:')
        self.lAdresseEmpfaenger.place(x=15, y=5)
        self.eAdresseEmpfaenger = tkinter.Entry(master=self.frameSender)
        self.eAdresseEmpfaenger.place(x=115, y=5, width=40)
        self.lZeichen = tkinter.Label(master=self.frameSender, background="#D5E88F", text='Zeichen:')
        self.lZeichen.place(x=15, y=30)
        self.eZeichen = tkinter.Entry(master=self.frameSender)
        self.eZeichen.place(x=115, y=30, width=20)
        self.lBitsGesendet = tkinter.Label(master=self.frameSender, background="#D5E88F", text='Bits/gesendet:')
        self.lBitsGesendet.place(x=15, y=70)
        self.lGesendet = tkinter.Label(master=self.frameSender, background="white", anchor=tkinter.W, text='')
        self.lGesendet.place(x=115, y=70, width=125)
        self.lBitsEmpfangen = tkinter.Label(master=self.frameSender, background="#D5E88F", text='Bits/empfangen:')
        self.lBitsEmpfangen.place(x=15, y=95)
        self.lEmpfangen = tkinter.Label(master=self.frameSender, background="white", anchor=tkinter.W, text='')
        self.lEmpfangen.place(x=115, y=95, width=125)
        self.bStartSender = tkinter.Button(self.frameSender, text="senden", command=self.bSenderStartenClick)
        self.bStartSender.place(x=300,y=30, width=80)
       # Medium
        self.lSender = tkinter.Label(master=self.frameMedium, background="#FBD975", anchor=tkinter.E, text='MEDIUM')
        self.lSender.place(x=275, y=5, width=100)
        self.lAktiv = tkinter.Label(master=self.frameMedium, background="gray")
        self.lAktiv.place(x=15, y=15, width=20, height=20)
        # Empfaenger
        self.lEmpfaenger= tkinter.Label(master=self.frameEmpfaenger, background="#FFCFC9", anchor=tkinter.E, text='EMPFÄNGER')
        self.lEmpfaenger.place(x=275, y=5, width=100)        
        self.lZeichenEmpfangen = tkinter.Label(master=self.frameEmpfaenger, background="#FFCFC9", text='Zeichen/empfangen:')
        self.lZeichenEmpfangen.place(x=15, y=5) 
        self.lTextEmpfaenger= tkinter.Label(master=self.frameEmpfaenger, background="white", anchor=tkinter.W, text='')
        self.lTextEmpfaenger.place(x=15, y=30, width=225)        
        # Polling starten
        self.model.starteThreadNachrichtEmpfangen()
        self.after(0,self.poll)  

    def codieren(self, text):
        return bytes(text, 'iso-8859-1')

    def decodieren(self, bytefolge):
        return str(bytefolge, 'iso-8859-1')

    def poll(self):
        if not self.model.stoppFlagEmpfaenger.is_set():
            bytefolge = self.model.getPuffer()
            aktuellerText = self.decodieren(bytefolge)
            bitfolgeGesendet = self.model.getBitpufferGesendet()
            bitfolgeEmpfangen = self.model.getBitpufferEmpfangen()
            self.lGesendet.config(text=bitfolgeGesendet)
            self.lEmpfangen.config(text=bitfolgeEmpfangen)         
            self.lTextEmpfaenger.config(text=aktuellerText)
            if self.model.s.getCTS():
                self.lAktiv.config(background="red")
            else:
                self.lAktiv.config(background="gray")
            self.after(20,self.poll)  

    def beenden(self):
        self.model.stoppeThreadNachrichtEmpfangen()
        self.model.stoppeThreadNachrichtSenden()
        self.model.s.close()     
        self.quit()              
        self.destroy()       

    def bUebernehmenClick(self):
        # Bitzeit ermitteln
        try:
            bitzeit = float(self.eA.get())
        except:
            self.eA.delete(0,tkinter.END)
            self.eA.insert(0,'0.01')
            bitzeit = 0.05
        self.model.setBitzeit(bitzeit)
        # eigene Adresse ermitteln
        try:
            eigeneAdresse = int(self.eAdresseSender.get())
        except:
            eigeneAdresse = 0
        self.model.setAdresse(eigeneAdresse)
        self.model.stoppeThreadNachrichtEmpfangen()
        self.model.starteThreadNachrichtEmpfangen()
        self.after(0,self.poll)

    def bSenderStartenClick(self):
        # Adresse ermitteln
        try:
            adresseEmpfaenger = int(self.eAdresseEmpfaenger.get())
        except:
            adresseEmpfaenger = 0
        # Bytefolge senden
        text = self.eZeichen.get()
        bytefolge =  self.codieren(text)
        self.lGesendet.config(text='')
        self.lEmpfangen.config(text='')
        self.model.starteThreadNachrichtSenden(bytefolge, adresseEmpfaenger) 


# Hauptprogramm
model = SenderEmpfaengerThread()
gui = GUI(model)
gui.mainloop()
