# lorawan_1.py

# Kommentare ergänzt: Bei den print-Anweisungen wurde jeweils eingefügt,
# ob sie von dem newThread oder dem MainThread ausgehen.
# Alles getestet am 20.11.2023

###################### Display: Importe und Instanziierung #################################

from machine import UART, SPI, Pin, RTC
import vga1_16x16 as font1
import st7789

spi = SPI(1, baudrate = 20_000_000, polarity = 1, sck = Pin(18), mosi = Pin(19))
display = st7789.ST7789(spi, 135, 240, reset=Pin(23, Pin.OUT), cs=Pin(5, Pin.OUT), dc=Pin(16, Pin.OUT), backlight=Pin(4, Pin.OUT), rotation=3)
display.init()
display.fill(0) # Display löschen

###################### Importe, Einstellungen und Funktionen für LoRa_to_Flash #################################

from time import sleep     
from machine import UART, SPI, Pin, RTC
import vga1_bold_16x32 as font2
import vga1_8x8 as font1
import st7789
import sys
from lora import send_command, read_response, response_to_list, lora_init, send_message
import _thread

delimiter = '/' # ggf. anderes Zeichen
data_fname = 'lorawan_data.txt'

# UART:
uart1 = UART(1, baudrate = 57600, tx = 12, rx = 13)

# RTC
rtc = RTC()

# Lock für das Sperren eines fremden Schreib/Lese-Zugriffs auf die Datei lorawan_data.txt 
lock = _thread.allocate_lock()


###################################################################
#                 Funktionen für den newThread                       #
###################################################################

def received_to_list(s, rtc): # s ist vom Typ ByteString
    s = str(s, 'UTF-8')
    s = s[5:len(s)]
    print('newThread: s =', s)
    liste = s.split(',',2)
    print('newThread: ', liste)
    Addr = liste[0]
    Payload_length = int(liste[1])
    # print('PL_Länge: ', Payload_length)
    Payload = liste[2][0:Payload_length]
    d_t_tuple=rtc.datetime() # now() git es nicht!
    print('newThread: d_t_tuple = ', d_t_tuple)
    Date_time_stamp = str(d_t_tuple[2])+'.'+str(d_t_tuple[1])+'.'+str(d_t_tuple[0])+' '+str(d_t_tuple[4])+':'+str(d_t_tuple[5])+':'+str(d_t_tuple[6])
    # print(Date_time_stamp)
    # print(Addr, Payload, Date_time_stamp)
    return [Addr, Payload, Date_time_stamp]

def save(fname, liste, delimiter): # auf ESP32 als Zeile (mit \n abgeschlossen) speichern:
    with lock:
        with open(fname, 'a') as f: # automatisches close
            f.write(liste[0] + delimiter)
            f.write(liste[1] + delimiter)
            f.write(liste[2])
            f.write('\n') 
    
def confirm(addr, d_t_stamp, display, uart): # addr und d_t_stamp sind Zeichenketten!
    display.text(font2, 'Bestaetigung:', 5, 45)
    display.text(font2, 'MSG von ' + addr + '     ', 5, 75) # ggf. Teil von vorheriger Adresse überschreiben
    display.text(font1, d_t_stamp+'      ', 5, 115) # ggf. Teil von vorheriger Adresse überschreiben
    msg = 'MSG confirmed'
    send_message(uart, int(addr), msg)

def receive_save_confirm(fname, uart, display, rtc): # empfängt Daten voneinem LoRa-Modul, speichert die Message (s. o.) im Flash ab und bestätigt den Empfang
    while True:
        print('newThread: r-s-c-Schleifen-Start', rtc.datetime())
        s = read_response(uart)
        print(s)
        sleep(0.2) ### ???
        item = received_to_list(s, rtc)
        print('newThread: item =', item)
        save(data_fname, item, delimiter)
        confirm(item[0], item[2], display, uart)  # Adresse u. Datum  
        r = read_response(uart)
        print('newThread: response =', r)


################ Importe und Daten für WLAN ####################################
    
import network
import socket
from time import time

# Wlan-Zugangsdaten; hier Ihre Wlan-Daten eintragen:
ssid = 'SSID' # Hier die eigene SSID eintragen
password = 'PASSWORT' # Hier das zugehörige Passwort eintragen

##############  Funktionen für WLAN ###################################

def getrequest(connection): # Blockweises Empfangen des Requests
  result = ''
  block_size = 64
  while True:
    data = connection.recv(block_size)
    result += str(data, 'UTF-8')        
    if result.find('\r\n\r\n') >= 0: # Ende des Requests gefunden
      break
  return result  

def http_header(): # zu sendende HTTP-Daten
  return '''HTTP/1.1 200 OK\r
Content-Type: text/html\r
Connection: close\r
\r
'''
            
def read_as_list(fname, delimiter): # liest alle Zeilen der Datei fname und gibt sie als Liste zurück
    liste = []
    with lock:
        with open(fname, 'a') as file:
            file = open(fname, 'r')
            for x in file: # zeilenweise auswerten; entspricht readlines()
                x = x[0:len(x)-2] # \n\r abtrennen
                listen_element = x.split(delimiter)        
                liste.append(listen_element)
    return liste

def html(request, data_fname, delimiter): #  zu sendende HTML-Daten (nur BODY)
    getparam = request.split(' ')[1] # Element 1 von ['GET', '/name', ... ]
    lora_addr = getparam[1:] # '/' gehört nicht zum Namen
    if lora_addr != 'favicon.ico': # favicon.ico nicht auswerten!!!
        # HTML-Dokument erstellen und zurückgeben
        lorawan_list = read_as_list(data_fname, delimiter) # zunächst Gateway-Datenliste auslesen
        all_messages = 'Daten von Adresse ' + lora_addr + '<BR><BR>'
        count = 0 # zählt die zur eingegebenen Adresse gehörigen Datensätze
        for i in lorawan_list:
            if i[0] == lora_addr:
                all_messages = all_messages + i[1] + '   [DT: ' + i[2] + ']<BR>' # Botschaft und Zeit-Datums-Stempel
                count += 1
        if count != 0:  # wenn Daten vorhanden...
            return '<html><body><h1> '+ all_messages + '</h1></body></html>'
        else:
            return '<html><body><h1>' + all_messages + 'Keine Daten vorhanden...'+ '</h1></body></html>' # keine Daten vorhanden 
    else:
        return '<html><body><h1>'+ 'favicon' +'</h1></body></html>' # favicon.ico (Icon); manche Browser fordern das Icon "favicon.ico" an. 


######################################################################
#                   Hauptprogramm (Mainthread)                       #
######################################################################

print('Mainthread gestartet')

# LoRa initialisieren...
print('Mainthread: Lora-Modul initialisieren...')
lora_address = lora_init(uart1)
display.text(font2, 'LORA-ADR: ' + lora_address, 5, 10)
if lora_address == '???':
    display.text(font2, 'Resetten!', 5, 80, st7789.RED)
    print('MainThread: Keine Verbindung zum LORA-Modul!')
    print('MainThread: RESET-Knopf am TTGO betätigen!')
    sys.exit() # Programm beenden, wenn keine Antwort vom LoRa-Modul
    
_thread.start_new_thread(receive_save_confirm, (data_fname, uart1, display, rtc))    

# Konfiguration des Wlans:
station = network.WLAN(network.STA_IF)
station.active(True)
station.connect(ssid, password) 
time0 = time()
while (not station.isconnected()) and (time() - time0 < 5):
    pass

if station.isconnected():
    print('Mainthread: Wlan connected')
    print(station.ifconfig()) # zur Information
    s = socket.socket() # Socket des Servers
    s.bind(('', 80))
    s.listen(5) 
    print('MainThread: Server listening')
    print('--------------------------------------')
  
    # Daten holen
    while True:
        connection, addr = s.accept() 
        print('Mainthread: Client connected: ', addr) # bei manchen Browser connected ohne Senden eines HTML-Requests!
        display.fill(0)
        display.text(font1, 'Abfrage von', 5, 20)  # IP-Adresse des Abfragenden auf Display anzeigen   
        display.text(font1, addr[0], 5, 70)
        request = getrequest(connection) 
        connection.send(http_header())
        print('MainThread: Header> ', http_header()) # zur Kontrolle
        connection.send(html(request, data_fname, delimiter))
        print('MainThread: HTML> ', html(request, data_fname, delimiter)) # zur Kontrolle
        connection.close()
        print('--------------------------------------------')
 
station.active(False)
print('MainThread: WLAN deactivated')
print('MainThread: Please reset!')


          