from ._corridors_mcts import _corridors_mcts
from math import sqrt
import numpy as np
import logging
# converts Trevor's board format to Matt's, putting in a Python dict to be passed to c++
def python_to_c_format(board):
    assert hasattr(board,"N"), "Board must have attribute 'N'"
    assert isinstance(board.N,int), "N must be an integer"
    assert board.N==9, "Board must be 9x9"
    assert hasattr(board,'walls'), "Board must have walls attribute"
    # board has a member 'walls', a 2d numpy array
    assert isinstance(board.walls,np.ndarray), "board.walls must be numpy array"
    assert board.walls.shape==(8,8), "board.walls must be 8x8 grid"

    for w in board.walls.flatten():
        assert w in (0,-1,1), "each wall must be either -1 (vertical), +1 (horizontal), or zero (nonexistant)"

    assert hasattr(board,'turn'), "Board must have a turn attribute"
    assert board.turn in ('red','blue'), "Turn must be one of 'red', 'blue'"
    assert hasattr(board,'wallsPerPiece'), "Board must have a wallsPerPiece attribute"
    assert board.wallsPerPiece==10
    assert hasattr(board,'red'), "board must have a red piece attribute"
    assert hasattr(board,'blue'), "board must have a blue piece attribute"
    assert board.red.color=='red'
    assert board.blue.color=='blue'
    assert isinstance(board.red.walls,int), "Piece must have integer walls attribute"
    assert isinstance(board.blue.walls,int), "Piece must have integer walls attribute"
    assert 0 <=board.red.walls <=10
    assert 0 <=board.blue.walls <=10

    assert isinstance(board.red.location,tuple), "red piece must have a location"
    assert len(board.red.location)==2
    assert isinstance(board.blue.location,tuple), "blue piece must have a location"
    assert len(board.blue.location)==2

    assert 0 <=board.red.location[0] <=8, "red Y coordinate must be between 0 and 8"
    assert 0 <=board.red.location[1] <=8, "red X coordinate must be between 0 and 8"
    assert 0 <=board.blue.location[0] <=8, "blue Y coordinate must be between 0 and 8"
    assert 0 <=board.blue.location[1] <=8 , "blue X coordinate must be between 0 and 8"

    assert board.turn=='blue', "Error: must be blue's turn. Currently assumes AI is only needed to make decisions for blue"
    c_format_board={
        'hero_x':board.red.location[1],
        'hero_y':board.N-1-board.red.location[0],
        'villain_x':board.blue.location[1],
        'villain_y':board.N-1-board.blue.location[0],
        'hero_walls_remaining':board.red.walls,
        'villain_walls_remaining':board.blue.walls,
        'flip': False if board.turn=='red' else True
    }
    BOARD_SIZE = board.N

    wall_middles = [False for _ in range((BOARD_SIZE-1)*(BOARD_SIZE-1))]
    horizontal_walls = [False for _ in range((BOARD_SIZE-1)*BOARD_SIZE)]
    vertical_walls = [False for _ in range((BOARD_SIZE-1)*BOARD_SIZE)]

    # loop over all wall middles and set flags where appropriate
    for x in range(BOARD_SIZE-1): # x is horizontal axis
        for y in range(BOARD_SIZE-1): # y is vertical axis
            curr_wall = board.walls[y,x]
            if curr_wall!=0:
                middle_ind = (BOARD_SIZE-2-y)*(BOARD_SIZE-1) + x
                wall_middles[middle_ind] = True
                if curr_wall==1: # horizontal
                    h_wall_ind = (BOARD_SIZE-2-y)*BOARD_SIZE + x
                    horizontal_walls[h_wall_ind] = True
                    horizontal_walls[h_wall_ind+1] = True
                if curr_wall==-1: # vertical
                    v_wall_ind = x*BOARD_SIZE + (BOARD_SIZE-2-y)
                    vertical_walls[v_wall_ind] = True
                    vertical_walls[v_wall_ind+1] = True

    c_format_board.update({
        'wall_middles':wall_middles,
        'horizontal_walls':horizontal_walls,
        'vertical_walls':vertical_walls
    })
    return c_format_board

def TREVORSc_move_text_to_python(c_move_text,board):

    logging.info(f"c_move_text_to_python({c_move_text})")
    logging.info(f"turn: {board.turn}, {board.red}, blue: {board.blue}")
    assert len(c_move_text)==6
    action = c_move_text[0]
    assert action in '*HV'

    x,y=int(c_move_text[2]),int(c_move_text[4])  # Matt may be doing x,y rather than y,x !!

    i,j=x,y  # WTF?
    logging.info(f"{action} {j} {i}")
    if '*' == action:
        # first let's make sure our coordinates are correct.
        piece = board.red if board.turn=='red' else board.blue
        pj,pi=piece.location
        if board.turn=='blue':
            j=8-j
        diff=abs(pj-j) + abs(pi-i)
        assert diff <=2, f"Bad move for piece {piece}: {j},{i}"

        if diff==1:
            if j==pj:
                if i>pi:
                    command=["move","right"]
                else:
                    command=["move","left"]
            else:
                if j>pj:
                    command=["move","down"]
                else:
                    command=["move","up"]
        else:
            assert 0, "still have to do hop commands"
            command=["hop","?"]  # this is actually difficult

    else:

        if board.turn=='blue':
            j=7-j  # maybe?
        command = ['hwall' if action=='H' else 'vwall', (j,i)]
        # wall

    logging.info(command)
    all_legal_commands=list(board.allLegalCommands())
    if command not in all_legal_commands:
        raise Exception(f"Bad command: {command}")
    return command

def c_move_text_to_python(c_move_text, board):
    assert board.turn=='blue', "Move must be from blue's perspective"
    BOARD_SIZE = board.N
    action_type = c_move_text[0]
    is_positional_move = action_type == '*'
    c_proposed_x, c_proposed_y = eval(c_move_text[1:])

    logging.info(f"red: {board.red}, blue: {board.blue}")
    logging.info(f"c_move_text is {c_move_text}"
    )
    if is_positional_move:
        new_pos_coordinates = (c_proposed_y, BOARD_SIZE-1-c_proposed_x)
        logging.info(f"new_pos_coordinates: {new_pos_coordinates}, curr_pos_coordinates: {board.blue.location}")

        all_legal_positional_moves=[c for c in board.allLegalCommands() if c[0] in ('move','hop')]
        logging.info(f"legal positional moves: {all_legal_positional_moves}")

        # check each legal positional move to find the one which matches new_pos_coordinates
        for m in all_legal_positional_moves:
            # compute resulting coordinates from this legal move beginning from blue's current location
            m_coordinates = list(board.blue.location) # list() ensure's we get a deep copy
            movement_directions = m[1:] #NB: m[1:] only has more than one element when the command in question is a hop
            for d in movement_directions:
                if d=='up': m_coordinates[0]-=1
                if d=='down': m_coordinates[0]+=1
                if d=='left': m_coordinates[1]-=1
                if d=='right': m_coordinates[1]+=1
            # determine if these resulting coordinates match new_coordinates
            if tuple(m_coordinates)==new_pos_coordinates:
                command = m
                return command
        # throw an exception if we couldn't find a match
        raise Exception(f"Couldn't produce a valid python command from '{c_move_text}'")
    else:
        # wall placement
        new_wall_coordinates = (c_proposed_y, BOARD_SIZE-2-c_proposed_x)
        command = ['hwall' if action_type=='H' else 'vwall', new_wall_coordinates]

    return command

    """
    HOP_COMMANDS = [
        ["hop", "up", "up"],
        ["hop", "up", "left"],
        ["hop", "up", "right"],
        ["hop", "down", "down"],
        ["hop", "down", "left"],
        ["hop", "down", "right"],
        ["hop", "left", "up"],
        ["hop", "left", "down"],
        ["hop", "left", "left"],
        ["hop", "right", "up"],
        ["hop", "right", "down"],
        ["hop", "right", "right"],
    ]

    MOVE_COMMANDS = [["move", "up"], ["move", "down"], ["move", "left"], ["move", "right"]]

    [cmd, (j, i)] for cmd in ["hwall", "vwall"] for j in range(M) for i in range(M)

    """

class Corridors_MCTS(_corridors_mcts):

    """
    Class manages state of an MCTS corridors instance.
    The instance runs simulations continuously in a separate
    thread, but is interruptable to take certain actions
    via the API calls below.

    The initializer arguments (with suggesed defaults)
    are the model hyperparemeters:

    c - exploration parameter. Higher values result in wider
        trees, lower values result in deeper trees.

    seed - random seed used for generating rollouts

    min_simulations - game will not provide evaluations
        via get_sorted_actions until at least this many
        simulations have been performed

    max_simulations - simulations will stop once this number
        is reached

    sim_increment - the number of simulations performed on
        each iteration of the event loop. reduce it if the
        API is being too slow.
    """

    def __init__(self, c=sqrt(2), seed=42, min_simulations=10000, max_simulations=100000, sim_increment=250):
        super().__init__(c, seed, min_simulations, max_simulations, sim_increment)

    def __json__(self):
        return {"type": str(type(self)), "name": getattr(self, "name", "unnamed")}

    def __call__(self, board):
        logging.info("Corridors_MCTS.__call__(...)")
        logging.info("\n"+str(board))

        c_format_board = python_to_c_format(board)

        c_move_text = super().set_state_and_make_best_move(c_format_board)

        logging.info(f"c_move_text is {c_move_text}")
        logging.info("\n"+str(self.display(board.turn=='blue')))
        command = c_move_text_to_python(c_move_text, board)
        logging.info(f"command is {command}")
        # ensure it's a legal move
        assert isinstance(command,list), "I represent commands as lists"
        assert command[0] in ("hwall","vwall","move","hop"), "not a legal command"
        all_legal_commands=list(board.allLegalCommands())
        assert command in all_legal_commands
        return command

    def display(self, flip=False):
        """Provides an ASCII representation of the board from
        heros perspective. Flip will provide the same board
        from villain's perspective."""
        return super().display(flip)

    def make_move(self, action_text, flip=False):
        """Makes a move according to the following action text:
        *(X,Y)  - move hero's token to new coordinate (X,Y)
        H(X,Y)  - place a horizontal wall at intersection (X,Y)
        V(X,Y)  - place a vertical wall at intersection (X,Y)"""
        return super().make_move(action_text,flip)

    def get_sorted_actions(self, flip=True):
        """Gets a list of tuples which represent all legal moves.
        List is sorted according to first value. Flip is defaulted
        to true since otherwise we'd be getting action/states from
        villain's perspective.
        Values are:
        0 - visits to state/action pair
        1 - estimated evaluation
        2 - string to describe action (i.e. what you pass to make_move to make that choice)"""
        return super().get_sorted_actions(flip)

    def choose_best_action(self, epsilon=0):
        """ make an epsilon-greedy choice """
        return super().choose_best_action(epsilon)

    def ensure_sims(self, sims):
        """ blocking function that holds until at
        least 'sims' simulations have been performed """
        return super().ensure_sims(sims)

    def get_evaluation(self):
        """ Returns -1, 1, or None, depending on whether
        a non-terminal evaluation is available for this position """
        eval = super().get_evaluation()
        return eval if eval else None

def display_sorted_actions(sorted_actions,list_size=0):
    output = ""
    output += f'Total Visits: {sum(a[0] for a in sorted_actions)}\n'
    for a in sorted_actions[:list_size if list_size else len(sorted_actions)]:
        output += f'Visit count: {a[0]} Equity: {a[1]:.4f} Action: {a[2]}\n'
    output += '\n'
    return output

def computer_self_play(p1, p2=None, stop_on_eval=False):
    """Pass one or two instances of corridors_mcts to perform self-play.
    p1 acts first"""
    if not p2: p2=p1
    p1_is_hero = True

    sorted_actions = p1.get_sorted_actions(True)
    while len(sorted_actions):
        if p1_is_hero:
            print("Hero's turn")
            hero = p1
            villain = p2
        else:
            print("Villain's turn")
            hero = p2
            villain = p1

        # display the current board state and actions
        print(hero.display(not p1_is_hero))
        print(display_sorted_actions(sorted_actions,5))

        # make action selection
        best_action = sorted_actions[0][2]

        hero.make_move(best_action,p1_is_hero)
        if hero is not villain:
            #in the 2-tree case, we have to make the same move in each tree
            villain.make_move(best_action,p1_is_hero)

        # stop early
        if stop_on_eval and hero.get_evaluation():
            print(hero.display(p1_is_hero))
            eval = hero.get_evaluation() * (-1 if p1_is_hero else 1)
            print("Hero wins!" if eval > 0 else "Villain wins!")
            break

        # prepare for next iteration
        sorted_actions = villain.get_sorted_actions(not p1_is_hero)
        hero_sorted_actions = hero.get_sorted_actions(p1_is_hero) # we don't use this, we just call (blocking) to ensure min_simulations is met
        if len(sorted_actions)==0:
            print(hero.display(p1_is_hero))
            print("Hero wins!" if p1_is_hero else "Villain wins!")

        p1_is_hero = not p1_is_hero

def human_computer_play(mcts, human_plays_first=True, hide_humans_moves=True):
    """Pass a single instance of corridors_mcts to perform computer vs human play."""
    humans_turn = human_plays_first
    sorted_actions = mcts.get_sorted_actions(humans_turn)
    while len(sorted_actions):
        print("Your turn" if humans_turn else "Computer's turn")

        # display the current board state and actions
        print(mcts.display(not humans_turn))
        if not hide_humans_moves:
            print(display_sorted_actions(sorted_actions,5))

        # make action selection
        if humans_turn:
            print("Please enter move:")
            action = input()
            while action not in (a[2] for a in sorted_actions):
                print("Illegal move!")
                action = input()
        else:
            action = sorted_actions[0][2]

        mcts.make_move(action,humans_turn)

        # prepare for next iteration
        sorted_actions = mcts.get_sorted_actions(not humans_turn)
        if len(sorted_actions)==0:
            print(hero.display(not humans_turn))
            print("Human wins!" if humans_turn else "Computer wins!")

        humans_turn = not humans_turn

if __name__ == '__main__':
    p1 = Corridors_MCTS(c=sqrt(2),seed=74)
    print("Do you want to play against the computer? No means computer self-play. (y/n)")
    if input()=="y":
        print("Please wait one moment...")
        human_computer_play(p1)
    else:
        p2 = Corridors_MCTS(seed=75)
        p1.ensure_sims(100000)
        p2.ensure_sims(100000)
        computer_self_play(p1,p2,stop_on_eval=True)


# -*- coding: utf-8 -*-
"""
Created on Thu Dec  1 10:27:04 2022

@author: Julien
"""

import tkinter as tk
from tkinter import Tk, LabelFrame, Label, Button, Entry
from tkinter import  IntVar, Frame, Checkbutton
from tkinter import messagebox, StringVar, OptionMenu, filedialog
import time
import pandas as pd
# import matplotlib
# matplotlib.use('TkAgg')
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
import re

import plotly.graph_objs as go
from plotly.offline import plot

# TAG_IHM_001
# Class to setup the main window
class RootGUI():
    def __init__(self, serial, data):
        '''
        Initializing the root GUI and other comps of the program
        '''
        self.root = Tk()
        self.root.resizable(width=False, height=False)
        self.root.title("Serial communication")
        self.root.geometry("360x110")
        self.root.config(bg="white")
        self.serial = serial
        self.data = data

        self.root.protocol("WM_DELETE_WINDOW", self.close_window)

    def close_window(self):
        """
        Method used if clicking on the window's red cross
        """
        print("Closing the window and exit")
        self.root.destroy()
        if hasattr(self.serial, 'ser'):
            if self.serial.ser.is_open:
                # Landing Mode called in case the engine is still functionning (if we click on the red cross before the 'STOP!' button)
                self.serial.SerialIpt(self, self.data.iptSPACE, 30)
                # Closing Serial COM
                self.serial.SerialClose(self)

# TAG_IHM_002
# Class to setup and create the communication manager with MCU
class ComGui():
    def __init__(self, root, serial, data):
        '''
        Initialize the connexion GUI and initialize the main widgets
        '''
        # Initializing the Widgets
        self.root = root
        self.serial = serial
        self.data = data
        self.frame = LabelFrame(root, text="Com Manager",
                                padx=5, pady=5, bg="white")
        self.label_com = Label(
            self.frame, text="Available Port(s): ", bg="white", width=15,
            anchor="w")
        self.label_bd = Label(
            self.frame, text="Baud Rate: ", bg="white", width=15, anchor="w")

        # Setup the Drop option menu
        self.baudOptionMenu()
        self.ComOptionMenu()

        # Add the control buttons for refreshing the COMs & Connect
        self.btn_refresh = Button(self.frame, text="Refresh",
                                  width=10,  command=self.com_refresh)
        self.btn_connect = Button(self.frame, text="Connect",
                                  width=10, state="disabled",
                                  command=self.serial_connect)

        # Optional Graphic parameters
        self.padx = 20
        self.pady = 5

        # Put on the grid all the elements
        self.publish()

    def publish(self):
        '''
         Method to display all the widgets of the main frame
        '''
        self.frame.grid(row=0, column=0, rowspan=3,
                        columnspan=3, padx=5, pady=5)
        self.label_com.grid(column=1, row=2)
        self.label_bd.grid(column=1, row=3)

        self.drop_baud.grid(column=2, row=3, padx=self.padx, pady=self.pady)
        self.drop_com.grid(column=2, row=2, padx=self.padx)

        self.btn_refresh.grid(column=3, row=2)
        self.btn_connect.grid(column=3, row=3)

    def ComOptionMenu(self):
        '''
         Method to get the available COMs connected to the PC
         and list them into the drop menu
        '''
        # Generate the list of available coms

        self.serial.getCOMList()

        self.clicked_com = StringVar()
        self.clicked_com.set(self.serial.com_list[0])
        self.drop_com = OptionMenu(
            self.frame, self.clicked_com, *self.serial.com_list,
            command=self.connect_ctrl)

        self.drop_com.config(width=10)

    def baudOptionMenu(self):
        '''
         Method to list all the baud rates in a drop menu
        '''
        self.clicked_bd = StringVar()
        bds = ["...", "115200"]
        self.clicked_bd .set(bds[0])
        self.drop_baud = OptionMenu(
            self.frame, self.clicked_bd, *bds, command=self.connect_ctrl)
        self.drop_baud.config(width=10)

    def connect_ctrl(self, widget):
        '''
        Method to keep the connect button disabled if all the
        conditions are not cleared
        '''
        # Checking the logic consistency to keep the connection btn
        if "..." in self.clicked_bd.get() or "..." in self.clicked_com.get():
            self.btn_connect["state"] = "disabled"
        else:
            self.btn_connect["state"] = "active"

    def com_refresh(self):
        """
        Method to erase the current selected COM
        """
        print("Refresh")
        # Get the Widget destroyed
        self.drop_com.destroy()

        # Refresh the list of available Coms
        self.ComOptionMenu()

        # Publish the this new droplet
        self.drop_com.grid(column=2, row=2, padx=self.padx)

        # Just in case to secure the connect logic
        logic = []
        self.connect_ctrl(logic)

    def serial_connect(self):
        '''
        Method that updates the GUI during connect / disconnect status
        Manage serials and data flows during connect / disconnect status
        '''
        if self.btn_connect["text"] in "Connect":
            # Start the serial communication
            self.serial.SerialOpen(self)
            # If connection established move on
            if self.serial.ser.status:
                # Update the COM manager
                self.btn_connect["text"] = "Disconnect"
                self.btn_refresh["state"] = "disable"
                self.drop_baud["state"] = "disable"
                self.drop_com["state"] = "disable"
                InfoMsg = f"Successful UART connection using {self.clicked_com.get()}"
                messagebox.showinfo("showinfo", InfoMsg)
                # Display the Motor Ready manager
                self.motor_ready = MotorReadyGUI(self.root, self.serial, self.data)
                # Access to Init UC
                self.serial.SerialIpt(self, self.data.iptENTER, 5)
                # Access to Motor Ready
                self.serial.SerialIpt(self, self.data.iptSPACE, 25)
            else:
                ErrorMsg = f"Failure to estabish UART connection using {self.clicked_com.get()} "
                messagebox.showerror("showerror", ErrorMsg)
        else:

            # Landing mode called in case the motor is still functionning
            self.serial.SerialIpt(self, self.data.iptSPACE, 30)
            # Closing the Serial COM
            self.serial.SerialClose(self)
            # Closing the Motor Ready Manager
            self.motor_ready.MotorReadyClose()

            self.data.ClearData()

            InfoMsg = f"UART connection using {self.clicked_com.get()} is now closed"
            messagebox.showwarning("showinfo", InfoMsg)
            self.btn_connect["text"] = "Connect"
            self.btn_refresh["state"] = "active"
            self.drop_baud["state"] = "active"
            self.drop_com["state"] = "active"

# TAG_IHM_003
class MotorReadyGUI():
    def __init__(self, root, serial, data):
        '''
        Initialize main widgets for Motor Ready GUI
        '''
        self.root = root
        self.serial = serial
        self.data = data

        # Build MotorReadyGUI Static Elements
        self.frame = LabelFrame(root, text="Motor Ready",
                                padx=5, pady=5, bg="white", width=60)
        ## Button to start the Manual Mode Term
        self.btn_mode2 = Button(self.frame, text="Manual Mode Term", width=15,
                                command=self.ModeChoice2)
        ## Button to start the Auto Mode
        self.btn_mode3 = Button(self.frame, text="Auto Mode", width=15,
                                command=self.ModeChoice3)
        ## Button to start the Calibration Mode
        self.btn_mode4 = Button(self.frame, text="Calibration Mode", width=15,
                                command=self.ModeChoice4)
        ## Button to start the Trip Mode
        self.btn_mode5 = Button(self.frame, text="Trip Mode", width=15,
                                command=self.ModeChoice5)

        # Extending the GUI
        self.MotorReadyOpen()

    def MotorReadyOpen(self):
        '''
        Method to display all the widgets
        '''
        self.root.geometry("360x220")
        self.frame.grid(row=4, column=0, rowspan=3, columnspan=5,
                        padx=5, pady=5)
        self.btn_mode2.grid(row=0, column=1,
                                  padx=5, pady=5)
        self.btn_mode3.grid(row=0, column=2,
                                  padx=5, pady=5)
        self.btn_mode4.grid(row=1, column=1,
                                  padx=5, pady=5)
        self.btn_mode5.grid(row=1, column=2,
                                  padx=5, pady=5)

    def MotorReadyClose(self):
        '''
        Method to close the Motor Ready GUI and destroy the widgets
        '''
        # Must destroy all the elements so that they are not kept in memory
        for widget in self.frame.winfo_children():
            widget.destroy()
        self.frame.destroy()
        self.root.geometry("360x110")

    def ModeChoice2(self):
        """
        Method to redirect on manual term mode
        """
        self.manual_term_mode = ModeTermGUI(self.root, self.serial, self.data)
        self.serial.SerialIpt(self, self.data.ipt2, 30)

    def ModeChoice3(self):
        """
        Method to redirect on auto mode
        """
        self.auto_mode = AutoModeGUI(self.root, self.serial, self.data)
        self.serial.SerialIpt(self, self.data.ipt3, 30)

    def ModeChoice4(self):
        """
        Method to redirect on calibration mode
        """
        self.calibration_mode = CalibrationGUI(self.root, self.serial, self.data)
        # It permits to initialize the Gyro
        self.serial.SerialIpt(self, self.data.ipt3, 30)
        self.serial.SerialIpt(self, self.data.iptSPACE, 30)
        # Use the manual term mode (gas values)
        self.serial.SerialIpt(self, self.data.ipt2, 30)

    def ModeChoice5(self):
        """
        Method to redirect on trip mode
        """
        self.trip_mode = TripModeGUI(self.root, self.serial, self.data)
        self.serial.SerialIpt(self, self.data.ipt4, 30)

# TAG_IHM_004
class ModeTermGUI():
    def __init__(self, root, serial, data):
        '''
        Initialize main widgets for Manual Mode Term GUI
        '''
        self.root = root
        self.serial = serial
        self.data = data

        # Build AutoModeGUI Static Elements
        self.frame = LabelFrame(root, text="Manual Mode Term",
                                padx=5, pady=5, bg="white", width=60)
        ## Angle consigne
        self.label_consigne = Label(
            self.frame, text="Gas (‰): ", bg="white", width=15, anchor="w")
        self.label_screen = Label(self.frame, text="0", bg="white", width=5)
        self.consigne_box = Entry(self.frame, width=5)

        ## Button to start the engine
        self.btn_go_consigne = Button(self.frame, text="GO!", width=10,
                                      command=self.start_manual_term_mode)
        ## Button to stop the engine by landing
        self.btn_stop_landing = Button(self.frame, text="STOP!", width=10,
                                       state="disabled",
                                      command=self.stop_manual_term_mode)
        self.btn_plus = Button(self.frame, text="+", width=5,
                               command=self.plus_gas_value)
        self.btn_minus = Button(self.frame, text="-", width=5,
                                state="disabled",
                               command=self.minus_gas_value)
        self.change_mode = Button(self.frame, text="Change Mode", width=15,
                                  command=self.ModeTermClose)

        # Extending the GUI
        self.ModeTermOpen()

    def ModeTermOpen(self):
        '''
        Method to display all the widgets
        '''
        self.root.geometry("960x120")
        self.frame.grid(row=1, column=5, rowspan=3,
                        columnspan=5, padx=5, pady=5)
        self.label_consigne.grid(row=1, column=1)
        self.label_screen.grid(row=1, column=2,
                               padx=5, pady=5)
        self.consigne_box.grid(row=1, column=3,
                               padx=5, pady=5)

        self.btn_go_consigne.grid(row=1, column=5,
                                  padx=5, pady=5)
        self.btn_stop_landing.grid(row=1, column=6,
                                  padx=5, pady=5)
        self.change_mode.grid(row=1, column=7,
                                  padx=5, pady=5)
        self.btn_minus.grid(row=2, column=3,
                                  padx=5, pady=5)
        self.btn_plus.grid(row=2, column=4,
                                  padx=5, pady=5)

    def ModeTermClose(self):
        '''
        Method to close the Mode Term GUI and destroy the widgets
        '''
        self.serial.SerialIpt(self, self.data.iptSPACE, 30)
        # Must destroy all the elements so that they are not kept in memory
        for widget in self.frame.winfo_children():
            widget.destroy()
        self.frame.destroy()

        self.motor_ready = MotorReadyGUI(self.root, self.serial, self.data)

    def start_manual_term_mode(self):
        """
        Method to start the engine after entering the gas value,
        clicking on the button "GO!" and checking if it is a correct value
        """
        # Take the gas value in the "Entry" widget
        gas_value = self.consigne_box.get()
        self.consigne_box.delete(0,"end")

        if gas_value.isdigit():
            gas_value = int(gas_value)
            if (gas_value >= 0) and (gas_value <= 100):
                self.btn_stop_landing["state"] = "active"
                self.serial.ser.write(str(gas_value).encode())
                self.serial.SerialIpt(self, self.data.iptENTER, 30)
                self.label_screen["text"] = gas_value
            else:
                ErrorMsg = "Please enter a number between 0 and 100. If you want a number over 100, please use the '+' symbol."
                messagebox.showerror("showerror", ErrorMsg)
        else:
            ErrorMsg = "Please enter a positive integer."
            messagebox.showerror("showerror", ErrorMsg)

        if self.label_screen["text"] == "0":
            self.btn_minus["state"] = "disabled"
        else:
            self.btn_minus["state"] = "active"

    def minus_gas_value(self):
        """
        Method to remove 1‰ of gas value
        """
        self.consigne_box.delete(0,"end")

        gas_entry = int(self.label_screen["text"])
        gas_entry -=1
        self.label_screen["text"] = str(gas_entry)
        self.serial.SerialIpt(self, self.data.iptMINUS, 30)

        if gas_entry == 0:
            self.btn_stop_landing["state"] = "disabled"

        if self.label_screen["text"] == "0":
            self.btn_minus["state"] = "disabled"
        else:
            self.btn_minus["state"] = "active"

    def plus_gas_value(self):
        """
        Method to add 1‰ of gas value
        """
        self.consigne_box.delete(0,"end")

        gas_entry = int(self.label_screen["text"])
        gas_entry +=1
        self.label_screen["text"] = str(gas_entry)
        self.serial.SerialIpt(self, self.data.iptPLUS, 30)

        if self.label_screen["text"] == "0":
            self.btn_minus["state"] = "disabled"
        else:
            self.btn_minus["state"] = "active"
            self.btn_stop_landing["state"] = "active"

    def stop_manual_term_mode(self):
        """
        Method to stop the engine and landing after clicking on the button "STOP!"
        """
        self.label_screen["text"] = "0"
        # Landing & Return to Motor Ready
        self.serial.SerialIpt(self, self.data.iptSPACE, 30)
        # Go to Manual Term Mode
        self.serial.SerialIpt(self, self.data.ipt2, 30)

        self.btn_stop_landing["state"] = "disabled"
        self.btn_minus["state"] = "disabled"

# TAG_IHM_005
class AutoModeGUI():
    def __init__(self, root, serial, data):
        '''
        Initialize main widgets for Auto Mode GUI
        '''
        self.root = root
        self.serial = serial
        self.data = data

        # Build AutoModeGUI Static Elements
        self.frame = LabelFrame(root, text="Auto Mode",
                                padx=5, pady=5, bg="white", width=60)
        self.label_screen = Label(self.frame, text="0", bg="white", width=5)
        ## Angle consigne
        self.label_consigne = Label(
            self.frame, text="Angle (°): ", bg="white", width=15, anchor="w")
        self.consigne_box = Entry(self.frame, width=5)
        ## Button to start the engine
        self.btn_go_consigne = Button(self.frame, text="GO!", width=10,
                                      command=self.start_auto_mode)
        ## Button to stop the engine by landing
        self.btn_stop_landing = Button(self.frame, text="STOP!", width=10,
                                       state="disabled",
                                      command=self.stop_auto_mode)
        ## kp
        self.label_kp = Label(
            self.frame, text="kp:\t\t0,", bg="white", width=15, anchor="w")
        self.kp_box = Entry(self.frame, width=5)
        ### Default value
        self.kp_box.insert(0, "001")

        ## ki
        self.label_ki = Label(
            self.frame, text="ki:\t\t0,", bg="white", width=15, anchor="w")
        self.ki_box = Entry(self.frame, width=5)
        ### Default value
        self.ki_box.insert(0, "018")

        ## kd
        self.label_kd = Label(
            self.frame, text="kd:\t\t0,", bg="white", width=15, anchor="w")
        self.kd_box = Entry(self.frame, width=5)
        ### Default value
        self.kd_box.insert(0, "1")

        ## Button to come back to default set of kp/ki/kd values
        self.btn_default_set_k = Button(self.frame, text="Default", width=10,
                                      command=self.default_k_values)

        self.change_mode = Button(self.frame, text="Change Mode", width=15,
                                  command=self.AutoModeClose)

        # Extending the GUI
        self.AutoModeOpen()

    def AutoModeOpen(self):
        '''
        Method to display all the widgets
        '''
        self.root.geometry("950x170")
        self.frame.grid(row=1, column=5, rowspan=3,
                        columnspan=5, padx=5, pady=5)
        self.label_screen.grid(row=1, column=2)
        self.label_consigne.grid(row=1, column=1)
        self.consigne_box.grid(row=1, column=3,
                               padx=5, pady=5)
        self.btn_go_consigne.grid(row=1, column=4,
                                  padx=5, pady=5)
        self.btn_stop_landing.grid(row=1, column=5,
                                  padx=5, pady=5)
        self.change_mode.grid(row=1, column=6,
                               padx=5, pady=5)
        self.label_kp.grid(row=2, column=1)
        self.kp_box.grid(row=2, column=2,
                               padx=5, pady=5)
        self.label_ki.grid(row=3, column=1)
        self.ki_box.grid(row=3, column=2,
                               padx=5, pady=5)
        self.label_kd.grid(row=4, column=1)
        self.kd_box.grid(row=4, column=2,
                               padx=5, pady=5)
        self.btn_default_set_k.grid(row=3, column=3,
                               padx=5, pady=5)

    def AutoModeClose(self):
        '''
        Method to close the Auto Mode GUI and destroy the widgets
        '''
        self.serial.ser.write(self.data.ipts.encode())
        time.sleep(.1)
        self.serial.SerialIpt(self, self.data.iptSPACE, 30)
        # Must destroy all the elements so that they are not kept in memory
        for widget in self.frame.winfo_children():
            widget.destroy()
        self.frame.destroy()

        self.motor_ready = MotorReadyGUI(self.root, self.serial, self.data)

    def start_auto_mode(self):
        """
        Method to start the engine after entering the angle value,
        clicking on the button "GO!" and checking if it is a correct value
        """
        # Take the angle value in the "Entry" widget
        angle_value = self.consigne_box.get()

        if angle_value.isdigit():
            angle_value = int(angle_value)
            if (angle_value >= 0) and (angle_value <= 90):
                if self.kp_box["state"] == "disabled":
                    self.consigne_box.delete(0,"end")
                    self.serial.ser.write(self.data.ipts.encode())
                    time.sleep(.1)
                    self.serial.SerialIpt(self, self.data.iptw, 30)
                    self.serial.ser.write(str(angle_value).encode())
                    self.serial.SerialIpt(self, self.data.iptENTER, 30)
                    self.serial.ser.write(self.data.iptr.encode())
                    self.label_screen["text"] = angle_value
                elif self.kp_box["state"] == "normal":
                    # Take the kp value in the "Entry" widget
                    kp_value = self.kp_box.get()
                    # Take the ki value in the "Entry" widget
                    ki_value = self.ki_box.get()
                    # Take the kd value in the "Entry" widget
                    kd_value =self.kd_box.get()
                    # Communicate the values of the Entry widgets

                    if kp_value.isdigit() and ki_value.isdigit() and kd_value.isdigit():
                        self.btn_stop_landing["state"] = "active"
                        self.btn_default_set_k["state"] = "disabled"
                        self.consigne_box.delete(0,"end")
                        ## Angle
                        self.serial.ser.write("0".encode())
                        self.serial.SerialIpt(self, self.data.iptENTER, 30)
                        ## kp
                        self.serial.SerialIpt(self, self.data.iptp, 30)
                        self.serial.ser.write(kp_value.encode())
                        self.serial.SerialIpt(self, self.data.iptENTER, 30)
                        ## ki
                        self.serial.SerialIpt(self, self.data.ipti, 30)
                        self.serial.ser.write(ki_value.encode())
                        self.serial.SerialIpt(self, self.data.iptENTER, 30)
                        ## kd
                        self.serial.SerialIpt(self, self.data.iptd, 30)
                        self.serial.ser.write(kd_value.encode())
                        self.serial.SerialIpt(self, self.data.iptENTER, 30)
                        ## Angle
                        self.serial.SerialIpt(self, self.data.iptw, 30)
                        self.serial.ser.write(str(angle_value).encode())
                        self.serial.SerialIpt(self, self.data.iptENTER, 30)
                        self.serial.ser.write(self.data.iptr.encode())
                        self.label_screen["text"] = angle_value

                        self.kp_box["state"] = "disabled"
                        self.ki_box["state"] = "disabled"
                        self.kd_box["state"] = "disabled"
                    else:
                        ErrorMsg = "Please be sure that the entries for kp, ki and kd values are numbers."
                        messagebox.showerror("showerror", ErrorMsg)
            else:
                ErrorMsg = "Please enter a number between 0 and 90."
                messagebox.showerror("showerror", ErrorMsg)
        else:
            ErrorMsg = "Please enter a positive integer."
            messagebox.showerror("showerror", ErrorMsg)

    def stop_auto_mode(self):
        """
        Method to stop the engine and landing after clicking on the button "STOP!"
        """
        self.label_screen["text"] = "0"
        # Landing & Return to Motor Ready
        self.serial.ser.write(self.data.ipts.encode())
        time.sleep(.1)
        self.serial.SerialIpt(self, self.data.iptSPACE, 30)
        self.serial.SerialIpt(self, self.data.ipt3, 30)

        self.kp_box["state"] = "normal"
        self.ki_box["state"] = "normal"
        self.kd_box["state"] = "normal"

        self.btn_stop_landing["state"] = "disabled"
        self.btn_default_set_k["state"] = "active"

    def default_k_values(self):
        """
        Method to reset the kp/i/d values to default ones
        """
        self.kp_box.delete(0,"end")
        self.kp_box.insert(0, "001")
        self.ki_box.delete(0,"end")
        self.ki_box.insert(0, "018")
        self.kd_box.delete(0,"end")
        self.kd_box.insert(0, "1")

# TAG_IHM_006
class CalibrationGUI():
    def __init__(self, root, serial, data):
        '''
        Initialize main widgets for Calibration GUI
        '''
        self.root = root
        self.serial = serial
        self.data = data

        # Build AutoModeGUI Static Elements
        self.frame = LabelFrame(root, text="Calibration Mode",
                                padx=5, pady=5, bg="white", width=60)

        self.btn_browse_file = Button(self.frame, text="Select File", width=10,
                                      command=self.browseFile)

        self.change_mode = Button(self.frame, text="Change Mode", width=15,
                                  command=self.CalibrationModeClose)

        self.btn_show_graph = Button(self.frame, text="Show Graph", width=10,
                                     state="disabled",
                                      command=self.showgraph)
        self.graph_type = 0
        self.gyro_issue = 0

        # Extending the GUI
        self.CalibrationModeOpen()

    def CalibrationModeOpen(self):
        '''
        Method to display all the widgets
        '''
        self.root.geometry("600x110")
        self.frame.grid(row=1, column=5, rowspan=3,
                        columnspan=5, padx=5, pady=5)
        self.btn_browse_file.grid(row=1, column=1,
                                  padx=5, pady=5)
        self.change_mode.grid(row=1, column=2,
                                  padx=5, pady=5)
        self.btn_show_graph.grid(row=2, column=1,
                                  padx=5, pady=5)

    def CalibrationModeClose(self):
        '''
        Method to close the Calibration Mode GUI and destroy the widgets
        '''
        self.serial.SerialIpt(self, self.data.iptSPACE, 30)
        # Must destroy all the elements so that they are not kept in memory
        for widget in self.frame.winfo_children():
            widget.destroy()
        self.frame.destroy()

        self.motor_ready = MotorReadyGUI(self.root, self.serial, self.data)
        self.data.ClearData()

    def browseFile(self):
        """
        Method to search for txt files and do the calibration protocol
        """
        self.data.ClearData()
        self.graph_type = 0
        self.btn_show_graph["state"] = "disabled"
        self.root.filename = filedialog.askopenfilename(
            initialdir="/",
            title="File Explorer",
            filetypes=(
                ("Text files","*.txt"),
                       ("All files","*.*")
                       )
            )

        myfile = open(self.root.filename, "rt")
        content = myfile.readlines()
        myfile.close()
        new_content=[]

        # We have lines with 2 values, separated by \t
        # 1st one is the gas value
        # 2nd one is the time we want to spend on this value
        # so here is the pattern : 2 integers and \t between them (nothing before, nothing after)
        # other lines will be ignored and considered as errors or comments
        pattern = re.compile("^[0-9]+\t[0-9]+$")
        for i in range(len(content)):
            if pattern.match(content[i]):
                new_content.append(content[i].split("\t"))

        for i in range(len(new_content)):
            new_content[i][1] = int(new_content[i][1])
            self.serial.ser.write(new_content[i][0].encode())
            self.serial.SerialIpt(self, self.data.iptENTER, 30)
            self.serial.ser.write(self.data.iptr.encode())
            timeout = time.time() + new_content[i][1]
            self.serial.SerialOpt(self, timeout)
            if self.data.exceeding_value:
                break
            self.serial.ser.write(self.data.ipts.encode())
            time.sleep(.1)

        self.serial.SerialIpt(self, self.data.iptSPACE, 30)
        if self.data.exceeding_value:
            self.data.exceeding_value = False
            ErrorMsg = "The arm was too close to the maximum value. For safety, landing state has been initiated."
            messagebox.showerror("showerror", ErrorMsg)

        title_file = time.strftime("%Y-%m-%d-%Hh%M-Calibration_mode_Results")

        self.list_modes = ['Consigne Gaz (‰)', 'Position Angulaire X (°)', 'Position Angulaire Y (°)',
                   'Ax Raw (m/s²)', 'Ay Raw (m/s²)', 'Az Raw (m/s²)',
                   'Gx Raw (rad/s)', 'Gy Raw (rad/s)', 'Gz Raw (rad/s)']
        self.df = pd.DataFrame(self.data.record,
                          columns = self.list_modes)

        list_time = []
        for i in range(len(self.df)):
            list_time.append(i*20)
        self.df.insert(1, "Time (ms)", list_time)

        saving_path = filedialog.asksaveasfile(initialfile=title_file,
                                               mode='w',
                                               defaultextension='.csv',
                                               filetypes=(
                                                   ("CSV files","*.csv"),
                                                          ("All files","*.*")
                                                          ))
        self.df.to_csv(saving_path, sep=';', decimal=',', encoding='utf-8', index=False, line_terminator='\n')

        self.btn_show_graph["state"] = "active"
        self.serial.SerialIpt(self, self.data.ipt2, 30)

        if len(new_content) == 1:
            self.graph_type = 1

    def showgraph(self):
        """
        Method to make the graphic (in html with plotly) from the dataframe
        """
        if self.graph_type == 1:
            data_graph = [go.Scatter(x=self.df['Time (ms)'],
                       y=self.df[mode],
                       name=mode,
                       mode='markers',
                       visible="legendonly") for mode in self.list_modes[1:]]

            layout = go.Layout(
                title='Calibration Graphic - Dynamic',
                yaxis=dict(title='Measures'),
                xaxis=dict(title='Time (ms)')
            )

            fig = go.Figure(data=data_graph, layout=layout)
            plot(fig)
        else:
            data_graph = [go.Scatter(x=self.df['Consigne Gaz (‰)'],
                       y=self.df[mode],
                       name=mode,
                       mode='markers',
                       visible="legendonly") for mode in self.list_modes]

            layout = go.Layout(
                title='Calibration Graphic - Static',
                yaxis=dict(title='Measures'),
                xaxis=dict(title='Consigne Gaz (‰)')
            )

            fig = go.Figure(data=data_graph, layout=layout)
            plot(fig)

# TAG_IHM_007
class TripModeGUI():
    def __init__(self, root, serial, data):
        '''
        Initialize main widgets for Trip Mode GUI
        '''
        self.root = root
        self.serial = serial
        self.data = data

        # Build AutoModeGUI Static Elements
        self.frame = LabelFrame(root, text="Trip Mode",
                                padx=5, pady=5, bg="white", width=60)

        self.btn_browse_file = Button(self.frame, text="Select File", width=10,
                                      state="disabled", command=self.browseFile)
        self.btn_show_graph = Button(self.frame, text="Show Graph", width=10,
                                     state="disabled",
                                      command=self.showgraph)
        self.change_mode = Button(self.frame, text="Change Mode", width=15,
                                  command=self.TripModeClose)

        ## kp
        self.label_kp = Label(
            self.frame, text="kp:\t\t0,", bg="white", width=15, anchor="w")
        self.kp_box = Entry(self.frame, width=5)
        ### Default value
        self.kp_box.insert(0, "001")

        ## ki
        self.label_ki = Label(
            self.frame, text="ki:\t\t0,", bg="white", width=15, anchor="w")
        self.ki_box = Entry(self.frame, width=5)
        ### Default value
        self.ki_box.insert(0, "018")

        ## kd
        self.label_kd = Label(
            self.frame, text="kd:\t\t0,", bg="white", width=15, anchor="w")
        self.kd_box = Entry(self.frame, width=5)
        ### Default value
        self.kd_box.insert(0, "1")

        ## Button to come back to default set of kp/ki/kd values
        self.btn_default_set_k = Button(self.frame, text="Default", width=10,
                                      command=self.default_k_values)
        self.btn_check_set_k = Button(self.frame, text="Check", width=10,
                                      command=self.CorrectCoeff)
        self.graph_type = 0

        # Extending the GUI
        self.TripModeOpen()

    def TripModeOpen(self):
        '''
        Method to display all the widgets
        '''
        self.root.geometry("710x180")
        self.frame.grid(row=1, column=5, rowspan=3,
                        columnspan=5, padx=5, pady=5)
        self.btn_browse_file.grid(row=1, column=1,
                                  padx=5, pady=5)
        self.btn_show_graph.grid(row=1, column=2,
                                  padx=5, pady=5)
        self.change_mode.grid(row=1, column=3,
                                  padx=5, pady=5)

        self.label_kp.grid(row=2, column=1)
        self.kp_box.grid(row=2, column=2,
                               padx=5, pady=5)
        self.label_ki.grid(row=3, column=1)
        self.ki_box.grid(row=3, column=2,
                               padx=5, pady=5)
        self.label_kd.grid(row=4, column=1)
        self.kd_box.grid(row=4, column=2,
                               padx=5, pady=5)
        self.btn_default_set_k.grid(row=3, column=3,
                               padx=5, pady=5)
        self.btn_check_set_k.grid(row=4, column=3,
                               padx=5, pady=5)

    def TripModeClose(self):
        '''
        Method to close the Trip Mode GUI and destroy the widgets
        '''
        self.serial.SerialIpt(self, self.data.iptSPACE, 60)
        # Must destroy all the elements so that they are not kept in memory
        for widget in self.frame.winfo_children():
            widget.destroy()
        self.frame.destroy()

        self.motor_ready = MotorReadyGUI(self.root, self.serial, self.data)
        self.data.ClearData()

    def CorrectCoeff(self):
        # Take the kp value in the "Entry" widget
        self.kp_value = self.kp_box.get()
        # Take the ki value in the "Entry" widget
        self.ki_value = self.ki_box.get()
        # Take the kd value in the "Entry" widget
        self.kd_value =self.kd_box.get()
        # Communicate the values of the Entry widgets

        if self.kp_value.isdigit() and self.ki_value.isdigit() and self.kd_value.isdigit():
            self.btn_default_set_k["state"] = "disabled"
            self.serial.ser.write("0".encode())
            self.serial.SerialIpt(self, self.data.iptENTER, 30)
            ## kp
            self.serial.SerialIpt(self, self.data.iptp, 30)
            self.serial.ser.write(self.kp_value.encode())
            self.serial.SerialIpt(self, self.data.iptENTER, 30)
            ## ki
            self.serial.SerialIpt(self, self.data.ipti, 30)
            self.serial.ser.write(self.ki_value.encode())
            self.serial.SerialIpt(self, self.data.iptENTER, 30)
            ## kd
            self.serial.SerialIpt(self, self.data.iptd, 30)
            self.serial.ser.write(self.kd_value.encode())
            self.serial.SerialIpt(self, self.data.iptENTER, 30)

            self.serial.SerialIpt(self, self.data.iptw, 30)

            self.kp_box["state"] = "disabled"
            self.ki_box["state"] = "disabled"
            self.kd_box["state"] = "disabled"
            self.btn_check_set_k["state"] = "disabled"
            self.btn_browse_file["state"] = "active"
        else:
            ErrorMsg = "Please be sure that the entries for kp, ki and kd values are numbers."
            messagebox.showerror("showerror", ErrorMsg)

    def browseFile(self):
        """
        Method to search for txt files and do the "séquence de vol"/trip
        """
        self.data.ClearData()
        self.graph_type = 0
        self.root.filename = filedialog.askopenfilename(
            initialdir="/",
            title="File Explorer",
            filetypes=(
                ("Text files","*.txt"),
                       ("All files","*.*")
                       )
            )

        myfile = open(self.root.filename, "rt")
        content = myfile.readlines()
        myfile.close()
        new_content=[]
        # 1st loop change the raw elements in something we will use in the 2nd loop
        # 'content' holds lists of 2 elements : 1st one is a str which corresponds to the angle value we will assign to Auto Mode
        # 2nd element is an int, the time (in seconds) we want to wait on this position before next position
        pattern = re.compile("^[0-9]+\t[0-9]+$")
        for i in range(len(content)):
            if pattern.match(content[i]):
                new_content.append(content[i].split("\t"))

        for i in range(len(new_content)):
            new_content[i][1] = int(new_content[i][1])
            self.serial.ser.write(new_content[i][0].encode())
            self.serial.SerialIpt(self, self.data.iptENTER, 30)
            self.serial.ser.write(self.data.iptr.encode())
            timeout = time.time() + new_content[i][1]
            self.serial.SerialOpt(self, timeout)
            if self.data.exceeding_value:
                break
            self.serial.ser.write(self.data.ipts.encode())
            time.sleep(.1)
            self.serial.SerialIpt(self, self.data.iptw, 30)

        self.serial.SerialIpt(self, self.data.iptSPACE, 30)
        if self.data.exceeding_value:
            self.data.exceeding_value = False
            ErrorMsg = "The arm was too close to the maximum value. For safety, landing state has been initiated."
            messagebox.showerror("showerror", ErrorMsg)

        title_file = time.strftime("%Y-%m-%d-%Hh%M-Trip_mode_Results")
        self.list_modes = ['Consigne Angle (°)', 'Position Angulaire X (°)', 'Position Angulaire Y (°)',
                   'Ax Raw (m/s²)', 'Ay Raw (m/s²)', 'Az Raw (m/s²)',
                   'Gx Raw (rad/s)', 'Gy Raw (rad/s)', 'Gz Raw (rad/s)',
                   'Erreur P (°)', 'Erreur I (°)', 'Erreur D (°)']
        self.df = pd.DataFrame(self.data.record,
                          columns = self.list_modes)

        list_time = []
        for i in range(len(self.df)):
            list_time.append(i*20)
        self.df.insert(1, 'Time (ms)', list_time)

        saving_path = filedialog.asksaveasfile(initialfile=title_file,
                                               mode='w',
                                               defaultextension='.csv',
                                               filetypes=(
                                                   ("CSV files","*.csv"),
                                                          ("All files","*.*")
                                                          ))
        self.df.to_csv(saving_path, sep=';', decimal=',', encoding='utf-8', index=False, line_terminator='\n')

        self.btn_show_graph["state"] = "active"
        self.serial.SerialIpt(self, self.data.ipt4, 30)

        if len(new_content) == 1:
            self.graph_type = 1

        self.kp_box["state"] = "normal"
        self.ki_box["state"] = "normal"
        self.kd_box["state"] = "normal"
        self.btn_check_set_k["state"] = "active"
        self.btn_browse_file["state"] = "disabled"
        self.btn_default_set_k["state"] = "active"

    def showgraph(self):
        """
        Method to make the graphic (in html with plotly) from the dataframe
        """
        # Main part of the graph
        data_graph = [go.Scatter(x=self.df['Time (ms)'],
                   y=self.df[mode],
                   name=mode,
                   mode='markers',
                   visible="legendonly") for mode in self.list_modes]

        layout = go.Layout(
            title='Trip Mode Graphic : kp = 0,'+self.kp_value+' | ki = 0,'+self.ki_value+' | kd= 0,'+self.kd_value+'*1000',
            yaxis=dict(title='Measures'),
            xaxis=dict(title='Time (ms)')
        )

        fig = go.Figure(data=data_graph, layout=layout)

        # Anayse de performance
        if self.graph_type == 1:
            # Temps du premier dépassement
            # Lines for 1er dépassement (max value)
            line1a = [dict(type="line",
                                xref="x", yref="y",
                                x0=0, y0=self.df['Position Angulaire X (°)'].max(),
                                x1=self.df['Time (ms)'].iloc[self.df['Position Angulaire X (°)'].idxmax()],
                                y1=self.df['Position Angulaire X (°)'].max(),
                                line=dict(color="LightSeaGreen",
                                width=4,
                                dash="dot"))]
            line1b = [dict(type="line",
                                xref="x", yref="y",
                                x0=self.df['Time (ms)'].iloc[self.df['Position Angulaire X (°)'].idxmax()], y0=0,
                                x1=self.df['Time (ms)'].iloc[self.df['Position Angulaire X (°)'].idxmax()],
                                y1=self.df['Position Angulaire X (°)'].max(),
                                line=dict(
                                    color="LightSeaGreen",
                                    width=4,
                                    dash="dot"))]
            depassement_pourcent = (self.df['Position Angulaire X (°)'].max() - self.df['Consigne Angle (°)'].iloc[0])/self.df['Consigne Angle (°)'].iloc[0]*100
            legend1a = [dict(x=self.df['Time (ms)'].iloc[self.df['Position Angulaire X (°)'].idxmax()],
                             y=self.df['Position Angulaire X (°)'].max(),
                             xref="x", yref="y",
                             text="Dépassement :<br> %.2f<br> %.2f %%" % (self.df['Position Angulaire X (°)'].max() , depassement_pourcent) ,
                             ax=0, ay=-40)]
            legend1b = [dict(x=self.df['Time (ms)'].iloc[self.df['Position Angulaire X (°)'].idxmax()],
                             y=0,
                             xref="x", yref="y",
                             text="Tp = %d" % self.df['Time (ms)'].iloc[self.df['Position Angulaire X (°)'].idxmax()],
                             ax=0, ay=40)]
            # Temps de réponse à 5%
            # Lines for +/- 5% of consigne value
            plus5_pourcent = (self.df['Consigne Angle (°)'].iloc[0])*1.05
            line2 = [dict(type="line",
                                xref="x", yref="y",
                                x0=0, y0=plus5_pourcent,
                                x1=self.df['Time (ms)'].max(),
                                y1=plus5_pourcent,
                                line=dict(
                                    color="Yellow",
                                    width=4,
                                    dash="dot"))]
            legend2 = [dict(x=0,
                             y=plus5_pourcent,
                             xref="x", yref="y",
                             text="+5%% : %.2f" % plus5_pourcent,
                             ax=-50, ay=0)]
            moins5_pourcent = (self.df['Consigne Angle (°)'].iloc[0])*0.95
            line3 = [dict(type="line",
                                xref="x", yref="y",
                                x0=0, y0=moins5_pourcent,
                                x1=self.df['Time (ms)'].max(),
                                y1=moins5_pourcent,
                                line=dict(
                                    color="Yellow",
                                    width=4,
                                    dash="dot"))]
            legend3 = [dict(x=0,
                             y=moins5_pourcent,
                             xref="x", yref="y",
                             text="-5%% : %.2f" % moins5_pourcent,
                             ax=-50, ay=0)]
            btwn = ~self.df['Position Angulaire X (°)'].between(moins5_pourcent, plus5_pourcent)
            last_time_5 = self.df[btwn].iloc[-1]['Time (ms)']
            last_time_5_idx = self.df.index[self.df['Time (ms)'] == last_time_5]
            if last_time_5 != self.df['Time (ms)'].iloc[-1]:
                coeff_lineaire_tr =(self.df['Position Angulaire X (°)'].iloc[last_time_5_idx[0] +1] - self.df['Position Angulaire X (°)'].iloc[last_time_5_idx[0]])/(self.df['Time (ms)'].iloc[last_time_5_idx[0] +1] - last_time_5)
                ord_orig_tr = self.df['Position Angulaire X (°)'].iloc[last_time_5_idx[0]] - coeff_lineaire_tr*last_time_5
                if (self.df['Position Angulaire X (°)'].iloc[last_time_5_idx[0]]) > plus5_pourcent:
                    y_tr = plus5_pourcent
                else:
                    y_tr = moins5_pourcent

                tr_5_pourcent = (y_tr - ord_orig_tr)/coeff_lineaire_tr
                line23 = [dict(type="line",
                               xref="x", yref="y",
                               x0=tr_5_pourcent, y0=0,
                               x1=tr_5_pourcent,
                               y1=y_tr,
                               line=dict(
                                   color="Yellow",
                                   width=4,
                                   dash="dot"))]
                legend23 = [dict(x=tr_5_pourcent,
                                 y=0,
                                 xref="x", yref="y",
                                 text="Tr 5%% = %.2f" % tr_5_pourcent,
                                 ax=0, ay=40)]
            else:
                line23 = []
                legend23 = []
            # Temps de montée
            # Lines for 10% of consigne value
            if (self.df['Position Angulaire X (°)'].iloc[0]) >= ((self.df['Consigne Angle (°)'].iloc[0])*0.1):
                tm_10_pourcent = self.df['Time (ms)'].iloc[0]
                line4a = []
                line4b = []
                legend4a = []
                legend4b = []
            else:
                udr = self.df.loc[self.df['Position Angulaire X (°)'] < ((self.df['Consigne Angle (°)'].iloc[0])*0.1)]
                last_time_10 = udr['Time (ms)'].iloc[-1]
                last_time_10_idx = self.df.index[self.df['Time (ms)'] == last_time_10]
                coeff_lineaire_tm10 =(self.df['Position Angulaire X (°)'].iloc[last_time_10_idx[0] +1] - self.df['Position Angulaire X (°)'].iloc[last_time_10_idx[0]])/(self.df['Time (ms)'].iloc[last_time_10_idx[0] +1] - last_time_10)
                ord_orig_tm10 = self.df['Position Angulaire X (°)'].iloc[last_time_10_idx[0]] - coeff_lineaire_tm10*last_time_10
                tm_10_pourcent = (((self.df['Consigne Angle (°)'].iloc[0])*0.1) - ord_orig_tm10)/coeff_lineaire_tm10
                line4a = [dict(type="line",
                                    xref="x", yref="y",
                                    x0=0, y0=(self.df['Consigne Angle (°)'].iloc[0])*0.1,
                                    x1=tm_10_pourcent,
                                    y1=(self.df['Consigne Angle (°)'].iloc[0])*0.1,
                                    line=dict(
                                        color="Purple",
                                        width=4,
                                        dash="dot"))]
                line4b = [dict(type="line",
                                    xref="x", yref="y",
                                    x0=tm_10_pourcent, y0=0,
                                    x1=tm_10_pourcent,
                                    y1=(self.df['Consigne Angle (°)'].iloc[0])*0.1,
                                    line=dict(
                                        color="Purple",
                                        width=4,
                                        dash="dot"))]
                legend4a = [dict(x=0,
                                 y=(self.df['Consigne Angle (°)'].iloc[0])*0.1,
                                 xref="x", yref="y",
                                 text="10%% : %.2f" % ((self.df['Consigne Angle (°)'].iloc[0])*0.1),
                                 ax=-50, ay=0)]
                legend4b = [dict(x=tm_10_pourcent,
                                 y=0,
                                 xref="x", yref="y",
                                 text="%.2f" % tm_10_pourcent,
                                 ax=0, ay=40)]
            # Lines for 90% of consigne value
            ovr = self.df.loc[self.df['Position Angulaire X (°)'] > ((self.df['Consigne Angle (°)'].iloc[0])*0.9)]
            first_time_90 = ovr['Time (ms)'].iloc[0]
            first_time_90_idx = self.df.index[self.df['Time (ms)'] == first_time_90]
            coeff_lineaire_tm90 =(self.df['Position Angulaire X (°)'].iloc[first_time_90_idx[0] -1] - self.df['Position Angulaire X (°)'].iloc[first_time_90_idx[0]])/(self.df['Time (ms)'].iloc[first_time_90_idx[0] -1] - first_time_90)
            ord_orig_tm90 = self.df['Position Angulaire X (°)'].iloc[first_time_90_idx[0]] - coeff_lineaire_tm90*first_time_90
            tm_90_pourcent = (((self.df['Consigne Angle (°)'].iloc[0])*0.9) - ord_orig_tm90)/coeff_lineaire_tm90
            line5a = [dict(type="line",
                                xref="x", yref="y",
                                x0=0, y0=(self.df['Consigne Angle (°)'].iloc[0])*0.9,
                                x1=tm_90_pourcent,
                                y1=(self.df['Consigne Angle (°)'].iloc[0])*0.9,
                                line=dict(
                                    color="Purple",
                                    width=4,
                                    dash="dot"))]
            line5b = [dict(type="line",
                                xref="x", yref="y",
                                x0=tm_90_pourcent, y0=0,
                                x1=tm_90_pourcent,
                                y1=(self.df['Consigne Angle (°)'].iloc[0])*0.9,
                                line=dict(
                                    color="Purple",
                                    width=4,
                                    dash="dot"))]
            legend5a = [dict(x=0,
                             y=(self.df['Consigne Angle (°)'].iloc[0])*0.9,
                             xref="x", yref="y",
                             text="90%% : %.2f" % ((self.df['Consigne Angle (°)'].iloc[0])*0.9),
                             ax=-50, ay=0)]
            legend5b = [dict(x=tm_90_pourcent,
                             y=0,
                             xref="x", yref="y",
                             text="%.2f<br> Tm = %.2f" % (tm_90_pourcent, tm_90_pourcent - tm_10_pourcent),
                             ax=0, ay=40)]
            # Buttons are made here
            fig.update_layout(
                updatemenus=[
                    dict(
                        type="buttons",
                        buttons=[
                            dict(label="Tracés ON",
                                 method="relayout",
                                 args=["shapes", line1a + line1b  + line2 + line3 + line23 + line4a + line4b + line5a + line5b]
                                 ),
                            dict(label="Tracés OFF",
                                 method="relayout",
                                 args=["shapes", []]),
                            dict(label="Paramètres ON",
                                 method="update",
                                 args=[{"visible":[]},{"annotations": legend1a + legend1b + legend2 + legend3 + legend23 + legend4a +legend4b + legend5a +legend5b}]),
                            dict(label="Paramètres OFF",
                                 method="update",
                                 args=[{"visible":[]},{"annotations":[]}])
                        ]
                    )
                ]
            )

        plot(fig)
        # Save the html file with a name
        title_file = time.strftime("%Y-%m-%d-%Hh%M- Trip_mode_Results - kp=0,"+self.kp_value+" & ki=0,"+self.ki_value+" & kd="+self.kd_value+".html")
        fig.write_html(title_file)

    def default_k_values(self):
        """
        Method to reset the kp/i/d values to default ones
        """
        self.kp_box.delete(0,"end")
        self.kp_box.insert(0, "001")
        self.ki_box.delete(0,"end")
        self.ki_box.insert(0, "018")
        self.kd_box.delete(0,"end")
        self.kd_box.insert(0, "1")

if __name__ == "__main__":
    RootGUI()
    ComGui()
    MotorReadyGUI()
    ModeTermGUI()
    AutoModeGUI()
    CalibrationGUI()
    TripModeGUI()


import asyncio
import copy

import torch
import torch.nn as nn
import torch.nn.functional as func
import torch.optim as optim

from images import to_bytes, image_loader

# MSE loss of content (target and content images)
class ContentLoss(nn.Module):
    def __init__(self, target):
        super(ContentLoss, self).__init__()
        self.target = target.detach()
        self.loss = func.mse_loss(self.target, self.target)

    def forward(self, content):
        self.loss = func.mse_loss(content, self.target)
        return content

# Gram matrix for computing style loss
def gram_matrix(image):
    batch_size, h, w, f_map_num = image.size()
    features = image.view(batch_size * h, w * f_map_num)
    gram = torch.mm(features, features.t())
    return gram.div(batch_size * h * w * f_map_num)

# MSE loss of style (target and style images)
class StyleLoss(nn.Module):
    def __init__(self, target_feature):
        super(StyleLoss, self).__init__()
        self.target = gram_matrix(target_feature).detach()
        self.loss = func.mse_loss(self.target, self.target)

    def forward(self, style):
        gram = gram_matrix(style)
        self.loss = func.mse_loss(gram, self.target)
        return style

# Normalization of images (for transforming according to vgg indexes)
class Normalization(nn.Module):
    def __init__(self, mean, std):
        super(Normalization, self).__init__()
        self.mean = mean.clone().detach().view(-1, 1, 1)
        self.std = std.clone().detach().view(-1, 1, 1)

    def forward(self, img):
        return (img - self.mean) / self.std

# Function to compose Generating model (for creating output image)
async def compose_model(content_img, style_img, vgg):
    normalization_mean = torch.tensor([0.485, 0.456, 0.406])
    normalization_std = torch.tensor([0.229, 0.224, 0.225])
    generating_model = nn.Sequential(Normalization(normalization_mean, normalization_std))

    conv_net = copy.deepcopy(vgg)
    layers = ['conv0', 'conv1', 'conv2', 'conv3', 'conv4']
    content_losses, style_losses = [], []
    conv = 0
    for layer in conv_net.children():
        if isinstance(layer, nn.Conv2d):
            conv += 1
            name = 'conv{}'.format(conv)
        elif isinstance(layer, nn.ReLU):
            name = 'relu{}'.format(conv)
            layer = nn.ReLU(inplace=False)
        elif isinstance(layer, nn.MaxPool2d):
            name = 'pool{}'.format(conv)
            layer = nn.AvgPool2d(kernel_size=2, stride=2, padding=0)
        else:
            raise RuntimeError('Unrecognized layer: {}'.format(layer.__class__.__name__))

        generating_model.add_module(name, layer)
        # Adding loss classes after appropriate convolutional layers
        if name == 'conv3':
            target = generating_model(content_img).detach()
            content_loss = ContentLoss(target)
            generating_model.add_module("content_loss{}".format(conv), content_loss)
            content_losses.append(content_loss)
        if name in layers:
            target_feature = generating_model(style_img).detach()
            style_loss = StyleLoss(target_feature)
            generating_model.add_module("style_loss{}".format(conv), style_loss)
            style_losses.append(style_loss)

    return generating_model, style_losses, content_losses

# The main training process:
# - features extraction from provided images by vgg19;
# - generating of output "styled" image by composed model.
async def transferring(content_img, style_img, num_steps=200,
                       style_weight=100000, content_weight=1):
    content_img = await image_loader(content_img)
    style_img = await image_loader(style_img)
    input_img = content_img.clone()

    pretrained_net = torch.load('vgg/vgg19_cutted_5l.pth', map_location='cpu').eval()
    gen_model, style_losses, content_losses = await compose_model(content_img, style_img,
                                                                  pretrained_net)
    optimizer = optim.Adam([input_img.requires_grad_()], lr=0.05)

    # Initializing of vars for saving best output (with minimal loss)
    best_score, best_content_score, best_output_step = 1e3, 1e3, 0
    best_output, redefine = input_img.clone(), False

    print('Computing losses...')
    step = 0
    while step <= num_steps:
        await asyncio.sleep(0)

        input_img.data.clamp_(0, 1)
        optimizer.zero_grad()
        gen_model(input_img)

        style_score, content_score = 0, 0
        for sl in style_losses:
            style_score += sl.loss
        for cl in content_losses:
            content_score += cl.loss

        style_score *= style_weight
        content_score *= content_weight
        total_loss = style_score + content_score
        total_loss.backward()

        step += 1
        if step % 10 == 0:
            print(f"Step {step}: style loss {style_score:.4f}, "
                  f"content loss {content_score:.4f}\n")

        # Check score: if current loss is lower then best, redefine output image
        if (best_score > total_loss) and (best_content_score > content_score):
            best_output = input_img.clone()
            best_score = total_loss
            best_content_score = content_score
            best_output_step = step
            # Redefine only if trained (to avoid redefining at first steps)
            if best_output_step > 50:
                redefine = True

        optimizer.step()

    print("Final modifications of styling...")
    # If score at the last step worse then best, return output image with the best score
    if redefine:
        output = best_output.data.clamp_(0, 1)
        print(f"Image redefined, best score was at step {best_output_step}")
    else:
        output = input_img.data.clamp_(0, 1)
    output_to_bytes = await to_bytes(output)

    del input_img, best_output, output

    return output_to_bytes


from tkinter import *
from tkinter import messagebox
import requests
from PIL import ImageTk, Image
import json
import time

root = Tk()
root.title("RainCloud")
root.iconbitmap("images\Weather.ico")
root.geometry("750x500")
root['background'] = "white"

# title
title= Label(root, text="\nCurrent Weather", font= ("arial", 12, "bold"), fg='white', bg='black', anchor=NW, padx=10, pady=5)
title.place(x=0,y=0, relwidth=1, height=90)

# pannel
location= Entry(root, font=("arial", 13), width=77, fg='black', bg='#f0bf43', bd=0)
location.place(x=10,y=50,height=30)

#function
def popup():
    messagebox.showerror("Not Found", "Invalid City.\n Please enter valid city")

def getweather():
    loca = location.get()
    api = "http://api.openweathermap.org/data/2.5/weather?q="+loca+"&appid=3e0cd0cfdf7135a53bf72064bdad403f"
    api_request = requests.get(api)
    data = json.loads(api_request.content)
    id = data['cod']

    if id == "404":
        popup()

    else:

        condition = data['weather'][0]['main']
        y = data['main']
        temp = int(y['temp']-273.15)
        max_temp = int(y['temp_max']-273.15)
        min_temp = int(y['temp_min']-273.15)
        pressure = y['pressure']
        humidity = y['humidity']
        wind = int(data['wind']['speed'] * 3.6)
        x = data['sys']
        country = x['country']
        timezone = data['timezone']

        sunrise = time.strftime("%I:%M:%S", time.gmtime(x['sunrise'] + timezone))
        sunset = time.strftime("%I:%M:%S", time.gmtime(x['sunset'] + timezone))
        name = data['name']

        # #adding the recieved info to screen
        label_city.configure(text=name + ", " + country, bg="#d0d5db")
        label_temp.configure(text=str(temp)+"°C")
        label_condition.configure(text=condition)
        label_humidity.configure(text="Humidity: "+str(humidity)+" %")
        label_wind.configure(text="Wind: "+str(wind)+" km/h")
        label_pressure.configure(text="Pressure: "+str(pressure)+ "mb")

        label_min_temp.configure(text="Min Temp: "+ str(min_temp)+ "°C")
        label_max_temp.configure(text="Max Temp: "+ str(max_temp)+ "°C")

        label_sunrise.configure(text="Sunrise: "+ str(sunrise) + " AM")
        label_sunset.configure(text="Sunset: "+ str(sunset) + " PM")

        #images
        thermo_label.configure(image=thermo)
        sun_label.configure(image=sun)

# search button
search_icon = Image.open("images\search.png")
search_icon = search_icon.resize((30, 30), Image.ANTIALIAS)
search_icon = ImageTk.PhotoImage(search_icon)

search = Button(root, image=search_icon, bd=0, bg="black", activebackground="#fcba03", command = getweather)
search.place(x=700, y=50,height=30)

f=("arial", 15, "bold")

label_city = Label(root, text="", font=("arial", 25, "bold"), bg="white")
label_city.pack(fill="both")
label_city.place(x=20, y=110)

label_temp = Label(root, font=("arial", 45, "bold"), bg="white")
label_temp.place(x=100, y=180)

label_condition = Label(root, font=("arial", 20, "bold"),fg="#0f0361", bg="white")
label_condition.place(x=70, y=260)

label_humidity = Label(root, font=f,bg="white")
label_humidity.place(x=20, y=340)

label_wind = Label(root, font=f, bg="white")
label_wind.place(x=20, y=370)

label_pressure = Label(root, font=f, bg="white")
label_pressure.place(x=20, y=400)

label_max_temp = Label(root, font=f, bg="white")
label_max_temp.place(x=450, y=230)

label_min_temp = Label(root, font=f, bg="white")
label_min_temp.place(x=450, y=260)

label_sunrise = Label(root, font=f, bg="white")
label_sunrise.place(x=450, y=350)

label_sunset = Label(root, font=f, bg="white")
label_sunset.place(x=450, y=380)

#image
thermo = Image.open("images//thermo.jpg")
thermo = thermo.resize((100, 100), Image.ANTIALIAS)
thermo = ImageTk.PhotoImage(thermo)
thermo_label = Label(root, bd=0, bg="white")
thermo_label.place(x=350, y=200)

sun = Image.open("images\sun.png")
sun = sun.resize((100, 100), Image.ANTIALIAS)
sun = ImageTk.PhotoImage(sun)
sun_label = Label(root, bd=0, bg="white")
sun_label.place(x=350, y=330)

root.mainloop()


import pandas as pd
from tqdm import tqdm

from components.wallet import wallet
from components.algorithms.SmaAndEma import EMAStrategy, SMAStrategy
from components.fetchData import fecthData

def test(df, filename: str):
    dfdata = {'SMA_1': [], 'SMA_2': [], 'Equity_SMA': [], 'Equity_EMA': []}
    dataframe = pd.DataFrame(dfdata)
    columns = list(dataframe)
    data = []

    for sma1 in tqdm(range(1, 51), desc='Progress Bar'):
        for sma2 in range(40, 151):
            walletName = str(sma1)+'_'+str(sma2)
            walletName = wallet(10000)
            walletName.smaValue(sma1, sma2)

            SMA_results = SMAStrategy(walletName, sma1, sma2, df.copy())
            EMA_results = EMAStrategy(walletName, sma1, sma2, df.copy())

            values = [walletName.sma1, walletName.sma2,
                      SMA_results[0], EMA_results[0]]
            zipped = zip(columns, values)
            a_dictionary = dict(zipped)
            data.append(a_dictionary)

            walletName.__del__()

    dataframe = dataframe.append(data, True)

    result = pd.ExcelWriter(f'{filename}.xlsx')
    dataframe.to_excel(result, index=True)
    result.save()

dataframe = fecthData('BTC-USD')
test(dataframe, 'test_result')


from flask import Flask, render_template, redirect, url_for
from flask_bootstrap import Bootstrap
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, SelectField
from wtforms.validators import DataRequired, URL
import csv

app = Flask(__name__)
app.config['SECRET_KEY'] = '8BYkEfBA6O6donzWlSihBXox7C0sKR6b'
Bootstrap(app)

class CafeForm(FlaskForm):
    cafe = StringField('Cafe name', validators=[DataRequired()])
    location_URL = StringField('Cafe Location on Google Maps (URL)', validators=[DataRequired(), URL()])
    opening_time = StringField('Opening Time e.g. 8AM', validators=[DataRequired()])
    closing_time = StringField('Closing Time e.g. 5.30PM', validators=[DataRequired()])
    coffee_rating = SelectField('Coffee Rating', choices=["âœ˜", "â˜•", "â˜•â˜•", "â˜•â˜•â˜•", "â˜•â˜•â˜•â˜•", "â˜•â˜•â˜•â˜•â˜•"], validators=[DataRequired()])
    wifi = SelectField('Wifi Strength Rating', choices=["âœ˜", "ðŸ’ª", "ðŸ’ªðŸ’ª", "ðŸ’ªðŸ’ªðŸ’ª", "ðŸ’ªðŸ’ªðŸ’ªðŸ’ª", "ðŸ’ªðŸ’ªðŸ’ªðŸ’ªðŸ’ª"], validators=[DataRequired()])
    power = SelectField('Power Socket Availability', choices=["âœ˜", "ðŸ”Œ", "ðŸ”ŒðŸ”Œ", "ðŸ”ŒðŸ”ŒðŸ”Œ", "ðŸ”ŒðŸ”ŒðŸ”ŒðŸ”Œ", "ðŸ”ŒðŸ”ŒðŸ”ŒðŸ”ŒðŸ”Œ"], validators=[DataRequired()])
    submit = SubmitField('Submit')

@app.route("/")
def home():
    return render_template("index.html")

@app.route('/add', methods=['GET', 'POST'])
def add_cafe():
    form = CafeForm()
    if form.validate_on_submit():
        with open("cafe-data.csv", mode="a") as csv_file:
            csv_file.write(f"\n{form.cafe.data},"
                           f"{form.location.data},"
                           f"{form.open.data},"
                           f"{form.close.data},"
                           f"{form.coffee_rating.data},"
                           f"{form.wifi_rating.data},"
                           f"{form.power_rating.data}")
        return redirect(url_for('cafes'))
        # cafe = form.cafe.data
        # location_URL = form.location_URL.data
        # opening_time = form.opening_time.data
        # closing_time = form.closing_time.data
        # coffee_rating = form.coffee_rating.data
        # wifi = form.wifi.data
        # power = form.power.data
        # field = [cafe, location_URL, opening_time, closing_time, coffee_rating, wifi, power]
        # with open("cafe-data.csv", "a", encoding="utf8") as new_file:
        #     writer = csv.writer(new_file)
        #     writer.writerow(field)
    return render_template('add.html', form=form)

@app.route('/cafes', methods=['GET', 'POST'])
def cafes():
    with open('cafe-data.csv', newline='', encoding="utf8") as csv_file:
        csv_data = csv.reader(csv_file, delimiter=',')
        list_of_row = []
        for row in csv_data:
            list_of_row.append(row)
    return render_template("cafes.html", cafes=list_of_row)

if __name__ == '__main__':
    app.run(debug=True)


from copy import deepcopy

import numpy as np

from ..operator import type as op
from .svd import tensor_svd
from .type import mps

class canonical_mps(mps):
    """
    MPS representation of quantum pure state

    shape of tensors: [du, dp, dl]
        du: dimension of left leg (1 for left-edge tensor)
        dp: dimension of physical leg (typically 2)
        dl: dimension of right leg (1 for right-edge tensor)

    canonical position: cp in range(n)
        0 <= cp(0) <= cp(1) < n
        tensors [0...cp(0)-1]: left canonical
        tensors [cp(1)+1...n-1]: right canonical
    """

    def __init__(self, tensors, nkeep=None):
        assert isinstance(tensors, list)
        self.tensors = deepcopy(tensors)
        self.nkeep = nkeep
        n = len(self.tensors)
        self.q2t = list(range(n))
        self.t2q = list(range(n))
        self.cp = [0, n - 1]

    def _num_qubits(self):
        return len(self.tensors)

    def _norm(self):
        A = np.identity(1)
        for t in range(self._num_qubits()):
            A = np.einsum("ij,jkl->ikl", A, self._tensor(t))
            A = np.einsum("ijk,ijl->kl", A, self._tensor(t).conj())
        return np.sqrt(np.trace(A))

    def _state_vector(self):
        n = self._num_qubits()
        v = self._tensor(0)
        for t in range(1, n):
            ss0 = list(range(t + 1)) + [t + 3]
            ss1 = [t + 3, t + 1, t + 2]
            v = np.einsum(v, ss0, self._tensor(t), ss1)
        v = v.reshape((2,) * n)
        return np.einsum(v, self.t2q).reshape((2,) * n + (1,))

    def _tensor(self, t):
        return self.tensors[t]

    def _canonicalize(self, p0, p1=None):
        p1 = p0 if p1 is None else p1
        n = len(self.tensors)
        assert 0 <= p0 and p0 <= p1 and p1 < n
        if self.cp[0] < p0:
            for t in range(self.cp[0], p0):
                L, R = tensor_svd(self.tensors[t], [[0, 1], [2]], "left")
                self.tensors[t] = L
                self.tensors[t + 1] = np.einsum(
                    R, [0, 3], self.tensors[t + 1], [3, 1, 2]
                )
        self.cp[0] = p0
        self.cp[1] = max(p0, self.cp[1])
        if self.cp[1] > p1:
            for t in range(self.cp[1], p1, -1):
                L, R = tensor_svd(self.tensors[t], [[0], [1, 2]], "right")
                self.tensors[t - 1] = np.einsum(
                    self.tensors[t - 1], [0, 1, 3], L, [3, 2]
                )
                self.tensors[t] = R
        self.cp[1] = p1

    def _is_canonical(self):
        # tensor shape
        n = len(self.tensors)
        dims = []
        assert self.tensors[0].shape[0] == 1
        dims.append(self.tensors[0].shape[0])
        for t in range(1, n - 1):
            dims.append(self.tensors[t].shape[0])
            assert self.tensors[t].shape[0] == self.tensors[t - 1].shape[2]
            assert self.tensors[t].shape[2] == self.tensors[t + 1].shape[0]
        assert self.tensors[n - 1].shape[2] == 1
        dims.append(self.tensors[n - 1].shape[0])
        dims.append(self.tensors[n - 1].shape[2])

        # qubit <-> tensor mapping
        for q in range(n):
            assert self.t2q[self.q2t[q]] == q
        for t in range(n):
            assert self.q2t[self.t2q[t]] == t

        # canonicality
        assert self.cp[0] in range(n)
        assert self.cp[1] in range(n)
        for t in range(0, self.cp[0]):
            A = np.einsum(self.tensors[t], [2, 3, 1], self.tensors[t].conj(), [2, 3, 0])
            assert np.allclose(A, np.identity(A.shape[0]))
        for t in range(self.cp[1] + 1, n):
            A = np.einsum(self.tensors[t], [1, 3, 2], self.tensors[t].conj(), [0, 3, 2])
            assert np.allclose(A, np.identity(A.shape[0]))
        return True

    def _apply_one(self, p, s):
        """
        apply 1-qubit operator on tensor at s
        """
        assert op.num_qubits(p) == 1
        self.tensors[s] = np.einsum(self.tensors[s], [0, 3, 2], p, [1, 3])
        self.cp[0] = min(self.cp[0], s)
        self.cp[1] = max(self.cp[1], s)

    def _apply_two(self, p, s, reverse=False):
        """
        apply 2-qubit operator on neighboring tensors at s and s+1
        """
        assert op.num_qubits(p) == 2
        self._canonicalize(s, s + 1)
        t0 = self.tensors[s]
        t1 = self.tensors[s + 1]
        t = np.einsum(t0, [0, 1, 4], t1, [4, 2, 3])
        if not reverse:
            t = np.einsum(t, [0, 4, 5, 3], p, [1, 2, 4, 5])
        else:
            t = np.einsum(t, [0, 4, 5, 3], p, [2, 1, 5, 4])
        L, R = tensor_svd(t, [[0, 1], [2, 3]], nkeep=self.nkeep)
        self.tensors[s] = L
        self.tensors[s + 1] = R


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Tue Apr 21 18:08:53 2020

@author: Niels Verleysen

Class that manages the LSTM network for predicting the satellite data.
You first have to build the model by specifying the model architecture.
Then you can train the model on given data and use this model to predict M steps into the future.

S = number of sensors
K = number of previous points visible
M = amount of timesteps predicted into the future
"""

from keras.models import Model
from keras.layers import LSTM, Input, Dense, Conv1D, MaxPooling1D
from keras.callbacks import ModelCheckpoint
from keras import losses
import numpy as np
import csv

class LSTM_network:

    def __init__(self, s, k, m, architecture_LSTM, architecture_CNN = [], architecture_FC = []):
        inputs = Input(shape=(s, k))
        l = inputs

        if len(architecture_CNN) > 0:
            for layer in architecture_CNN:
                l = Conv1D(layer[0], layer[1])(l)
                l = MaxPooling1D()(l)

        for i in range(0, len(architecture_LSTM)-1):
            l = LSTM(int(architecture_LSTM[i]), return_sequences=True)(l)

        l = LSTM(int(architecture_LSTM[-1]), return_sequences=False)(l)

        if len(architecture_FC) > 0:
            for layer in architecture_FC:
                l = Dense(layer)(l)

        outputs = Dense(s*m)(l) # M values for each of S sensors

        self.model = Model(inputs=inputs, outputs=outputs)
        self.model.compile(loss=losses.mean_absolute_error, optimizer='Adam', metrics=['accuracy', 'mae'])

        self.model.summary()

        filepath="./Results/Weights/weights-improvement-{epoch:02d}-{loss:.4f}.hdf5"
        checkpoint = ModelCheckpoint(filepath, monitor='loss', verbose=1, save_best_only=True, mode='min')
        self.callbacks_list = [checkpoint]

    def train(self, X, Y, epochs, batch_size):
        history = self.model.fit(X, Y, epochs=epochs, batch_size=batch_size, callbacks=self.callbacks_list)
        return history

    def load(self, file):
        self.model.load_weights(file)

    def predict(self, previous_vals):
        prediction = self.model.predict(previous_vals, verbose=0)
        return prediction

    """
        Evaluation of the model on given sensor data.
        Expects a set of input-output pairs and returns the average loss and the standard deviation of the loss
        It also outputs a csv file with all predictions, allowing us to make a plot afterwards

        x is a 2D-array with k values for every sensor (used as input)
        y is a 2D-array with m values for every sensor (model will try to predict these)
        sensors is a list of sensor names
    """
    def evaluate_and_write_m(self, x, y, file, s, m, sensors):
        losses = []
        stdvs = []
        with open(file, 'w', newline='') as outfile:
            w = csv.writer(outfile)
            title = []
            for sensor in sensors:
                title.append('Real ' + str(sensor))
                title.append('Predicted ' + str(sensor))
            w.writerow(title)

            # Make single prediction
            yhat = self.model.predict(np.expand_dims(x, axis=0))

            predictions = []

            # Process prediction
            for sensor in range(0, s):
                sensor_losses = []
                sensor_predictions = [[],[]] #[0] is filled with real data, [1] is filled with the predictions
                for value in range(0,m):
                    sensor_losses.append(abs(yhat[0][sensor*m + value] - y[sensor][value]))
                    sensor_predictions[0].append(y[sensor][value])
                    sensor_predictions[1].append(yhat[0][sensor*m + value])
                losses.append(np.mean(np.array(sensor_losses)))
                stdvs.append(np.std(np.array(sensor_losses)))
                predictions.append(sensor_predictions[0])
                predictions.append(sensor_predictions[1])

            predictions = np.transpose(np.array(predictions))
            for row in predictions:
                w.writerow(row)

        return losses, stdvs


import streamlit as st
import streamlit.components as stc

# Utils
import base64
import time
timestr = time.strftime("%Y%m%d-%H%M%S")
import pandas as pd

# Fxn
def text_downloader(raw_text):
	b64 = base64.b64encode(raw_text.encode()).decode()
	new_filename = "new_text_file_{}_.txt".format(timestr)
	st.markdown("#### Download File ###")
	href = f'Click Here!!'
	st.markdown(href,unsafe_allow_html=True)

def csv_downloader(data):
	csvfile = data.to_csv()
	b64 = base64.b64encode(csvfile.encode()).decode()
	new_filename = "new_text_file_{}_.csv".format(timestr)
	st.markdown("#### Download File ###")
	href = f'Click Here!!'
	st.markdown(href,unsafe_allow_html=True)

# Class
class FileDownloader(object):
	"""docstring for FileDownloader
	>>> download = FileDownloader(data,filename,file_ext).download()

	"""
	def __init__(self, data,filename='myfile',file_ext='txt'):
		super(FileDownloader, self).__init__()
		self.data = data
		self.filename = filename
		self.file_ext = file_ext

	def download(self):
		b64 = base64.b64encode(self.data.encode()).decode()
		new_filename = "{}_{}_.{}".format(self.filename,timestr,self.file_ext)
		st.markdown("#### Download File ###")
		href = f'Click Here!!'
		st.markdown(href,unsafe_allow_html=True)

def main():
	menu = ["Home","CSV","About"]

	choice = st.sidebar.selectbox("Menu",menu)

	if choice == "Home":
		st.subheader("Home")
		my_text = st.text_area("Your Message")
		if st.button("Save"):
			st.write(my_text)
			# text_downloader(my_text)
			download = FileDownloader(my_text).download()

	elif choice == "CSV":
		df = pd.read_csv("iris.csv")
		st.dataframe(df)
		# csv_downloader(df)
		download = FileDownloader(df.to_csv(),file_ext='csv').download()

	else:
		st.subheader("About")

if __name__ == '__main__':
	main()


from utils import *
from primitives import *
from collections import namedtuple

class Room:
	"""Room analyzed for right-of-light calculations"""

	def __init__(self, floor, window):
		"""Rectangular floor is passed as (width,length). Window given by two corners
		in 2D and a wall index where:
			0: wall with y = 0
			1: wall with x = width
			2: wall with y = length
			3: wall with x = 0
		"""
		self.scene = None

		width,length = floor[0], floor[1]
		self.floor = build_rectangle(xlim=(0,width), ylim=(0,length))
		self.floor.color = LIGHTGREY

		ll,ur = window.lowleft, window.upright
		fw = window.frame

		self.window = build_rectangle(xlim=(ll[0]+fw,ur[0]-fw), ylim=(ll[1]+fw,ur[1]-fw), zdir='Y')
		self.window.color = LIGHTBLUE[0:3] + [0.15]
		self.window.edgecolor = LIGHTBLUE

		self.sills = []
		silldepth = window.sill

		if silldepth > 1.0E-6:
			self.sills.append(build_rectangle(xlim=(ll[0],ur[0]), ylim=(0,silldepth)))
			self.sills.append(build_rectangle(xlim=(ll[0],ur[0]), ylim=(0,silldepth)))
			ya,yb = -0.5*(ur[1]-ll[1]), 0.5*(ur[1]-ll[1])
			self.sills.append(build_rectangle(xlim=(ya,yb), ylim=(0,silldepth)))
			self.sills.append(build_rectangle(xlim=(ya,yb), ylim=(0,silldepth)))

			self.sills[0].transform(b=ll[1]*EZ)
			self.sills[1].transform(rotation_matrix(EY,np.pi), ur[1]*EZ)
			self.sills[2].transform(rotation_matrix(EY,np.pi/2), 0.5*(ur[1]+ll[1])*EZ + ur[0]*EX)
			self.sills[3].transform(rotation_matrix(EY,-np.pi/2), 0.5*(ur[1]+ll[1])*EZ + ll[0]*EX)

		# Position the window on the correct wall
		A = np.eye(3,3)
		b = (0,0,0)

		if window.wall == 0:
			A = rotation_matrix(EZ, np.pi)
			b = (width,0,0)
		elif window.wall == 1:
			A = rotation_matrix(EZ, 0.5*np.pi)
			b = (width,length,0)
		elif window.wall == 2:
			b = (0,length,0)
		elif window.wall == 3:
			A = rotation_matrix(EZ, -0.5*np.pi)
		else:
			raise RuntimeError("Unknown wall index")

		self.window.transform(A,b,'global')
		for sill in self.sills:
			sill.transform(A,b,'global')

	def grid(size=(8,8), z=850):

		p0 = rect[0]
		dx = (rect[1] - rect[0]) * 1.0/size[0]
		dy = (rect[2] - rect[1]) * 1.0/size[1]

		for i in range(size[0]):
			for j in range(size[1]):
				pij = p0 + i*dx + j*dy
				yield [pij, pij+dx, pij+dx+dy, pij+dy]

	def transform(self, R=None, b=None):
		"""Transform in local coordinate system"""
		if R is None:
			R = np.eye(3,3)
		if b is None:
			b = np.zeros(3)

		self.floor.transform(R,b)
		self.window.transform(R,b)
		for sill in self.sills:
			sill.transform(R,b)

	def plot(self, ax, normals=False):
		self.floor.plot(ax, normals)
		self.window.plot(ax, normals)
		for sill in self.sills:
			sill.plot(ax, normals)

	# Interior walls if rendering in blender or something.
	# opaque off-white patch for the interior wall
	# (bit pointless with matplotlib)
	#fw = self.framewidth
	#maxz = 1.2*self.window.vertices[2][2]
	#p0,p1 = self.plane[0], self.plane[1]

	#wpath = Path( \
		#[(p0[0],0), (p1[0],0), (p1[0],maxz), (p0[0],maxz), (np.nan,np.nan)] \
		 #+ [(v[0],v[2]) for v in self.window.vertices[::-1]] \
		 #+ [(np.nan,np.nan)], \
		#[\
			#Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY, \
			#Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY  \
		#])
	#wpatch = PathPatch(wpath, fc=BEIGE, ec=GREY) #, hatch='/')
	#ax.add_patch(wpatch)
	#art3d.pathpatch_2d_to_3d(wpatch, z=self.plane[2][1], zdir='y')

	#def _sanity_check(self):
		# calculate the solid angle subtended by the window (i) directly
		# and (ii) summing up the solid angles of the individual positions
		#spoints = [p for p in self.ppoints()]
		#pidx = 26

		#for pt in spoints[26:55]:
			#momega = solid_angle(self.window.vertices, pt)
			#somega = 0.0

			#for wp in self.window_partitions:
				#rect = [self.window_points[i] for i in wp]
				#somega = somega + solid_angle(rect, pt)

			#print pidx, momega, r'(%.2f%%)' % (50*momega/np.pi), somega