import torch
import pickle
import numpy as np
import torch.nn as nn
import torch.optim as optim
import torch.functional as F
import pytorch_lightning as pl
from torchvision import transforms

from torchvision import models
from torch.utils.data import DataLoader
from src.multi_models.resnet.dataset import FER2013

class EmotionClassifier(pl.LightningModule):
    def __init__(self, model, processed_fer2013, batch_size=128) -> None:
        super(EmotionClassifier, self).__init__()

        self.model = model
        self.criterion = nn.CrossEntropyLoss()
        self.processed_fer2013 = processed_fer2013
        self.train_transform = transforms.Compose([
            transforms.RandomResizedCrop(48, scale=(0.8, 1.2)),
            transforms.RandomApply([transforms.RandomAffine(0, translate=(0.2, 0.2))], p=0.5),
            transforms.RandomHorizontalFlip(),
            transforms.RandomApply([transforms.RandomRotation(10)], p=0.5),
            transforms.Normalize(mean=0, std=255)
        ])
        self.test_transform = transforms.Compose([
            transforms.Normalize(mean=0, std = 255)
        ])
        self.batch_size = batch_size

    def train_dataloader(self):
        train_set = FER2013(self.processed_fer2013['x_train'], self.processed_fer2013['y_train'], transform=self.train_transform)
        train_loader = DataLoader(train_set, batch_size=self.batch_size, shuffle=True, num_workers=16)
        return train_loader

    def val_dataloader(self):
        val_set = FER2013(self.processed_fer2013['x_val'], self.processed_fer2013['y_val'], transform=self.test_transform)
        val_loader = DataLoader(val_set, batch_size=self.batch_size, shuffle=False, num_workers=16)
        return val_loader

    def test_dataloader(self):
        test_set = FER2013(self.processed_fer2013['x_test'], self.processed_fer2013['y_test'], transform=self.test_transform)
        test_loader = DataLoader(test_set, batch_size=self.batch_size, shuffle=False, num_workers=16)
        return test_loader

    def training_step(self, batch, batch_idx):
        images, labels = batch
        output = self.model(images)

        loss = self.criterion(output, labels)
        self.log('train_loss', loss, on_step=True, prog_bar=True)
        return loss

    def validation_step(self, batch, batch_idx):
        images, labels = batch

        output = self.model(images)

        val_loss = self.criterion(output, labels)

        preds = output.argmax(dim=1, keepdim=True)
        corrects = preds.eq(labels.view_as(preds)).sum().item()
        accuracy = corrects / len(batch)

        result = {'val_loss': val_loss, 'corrects': corrects, 'num_labels': labels.shape[0]}
        self.log('val_loss', val_loss)
        return result

    def validation_epoch_end(self, outputs):
        val_losses = [output['val_loss'].item() for output in outputs]
        corrects = [output['corrects'] for output in outputs]
        num_labels = [output['num_labels'] for output in outputs]
        val_acc = np.sum(corrects) / np.sum(num_labels)

        self.log('val_loss_epoch', np.mean(val_losses), on_epoch=True, prog_bar=True)
        self.log('val_accuracy', val_acc, on_epoch=True, prog_bar=True)

    def test_step(self, batch, batch_idx):
        images, labels = batch

        output = self.model(images)

        test_loss = self.criterion(output, labels)

        preds = output.argmax(dim=1, keepdim=True)
        corrects = preds.eq(labels.view_as(preds)).sum().item()

        result = {'test_loss': test_loss, 'corrects': corrects, 'num_labels': preds.shape[0]}
        return result

    def inference(self, input):
        emotions = {
            0:"Angry",
            1:"Disgust",
            2:"Fear",
            3:"Happy",
            4:"Sad",
            5:"Surprize",
            6:"Neutral"
        }
        transform = transforms.Normalize(mean=0, std = 255)
        input = torch.from_numpy(input)[None, None, :, :].float()
        input = transform(input)
        prediction = emotions[self.model(input).argmax(dim=1).item()]
        return prediction

    def test_epoch_end(self, outputs) -> None:
        test_losses = [output['test_loss'].item() for output in outputs]
        corrects = [output['corrects'] for output in outputs]
        num_labels = [output['num_labels'] for output in outputs]
        test_acc = np.sum(corrects) / np.sum(num_labels)

        self.log('test_loss', np.mean(test_losses))
        self.log('test_acc', test_acc)

    def configure_optimizers(self):
        optimizer = optim.SGD(self.parameters(), lr=0.001, momentum=0.9, weight_decay = 0.0001)
        scheduler = optim.lr_scheduler.ReduceLROnPlateau(
            optimizer,
            mode='max',
            factor=0.5,
            patience=5,
            min_lr=0.00001,
            verbose=True
        )
        return {"optimizer": optimizer, "lr_scheduler": scheduler, 'monitor': 'val_loss'}

def resnet(weights_path):
    print("Loading pretrained ResNet.")
    with open(weights_path, 'rb') as f:
        obj = f.read()
        weights = {key: torch.from_numpy(arr) for key, arr in pickle.loads(obj, encoding='latin1').items()}
    resnet50 = models.resnet50()
    resnet50.fc = nn.Linear(in_features=2048, out_features=8631, bias=True)
    resnet50.load_state_dict(weights)

    resnet50.conv1 = nn.Conv2d(1, 64, kernel_size = 3, padding=1, bias=False)
    resnet50.maxpool = nn.Sequential()
    resnet50.fc = nn.Sequential(
        nn.Linear(in_features=2048, out_features=1024, bias=True),
        nn.Linear(in_features=1024, out_features=7, bias=True)
    )
    print("Pretrained ResNet Loaded!")
    return resnet50


from PIL import Image
import os
import glob
import msvcrt

#classes
class image:
    filename = ""
    image = ""
    def __init__(self,filename):
        self.filename = filename
        self.image = Image.open(filename)

#vari
mode = 1
imagelist = []

#functions

#main while
while True:
    if mode == 1:
        print("Please wait...")
        for filename in glob.glob(os.path.abspath('.')+r"\*.*"):
            if filename.endswith("jpg" or "png" or "jpeg"):
                imagelist.append(image(filename))
        print(str(len(imagelist))+" files founded")
        os.system("cls")
        mode = 2

    if mode == 2:
        show = 0
        select = 0
        while True:
            print("Please select image you want to process:")
            i = show
            if len(imagelist) < 5:
                max = len(imagelist)
            else:
                if show + 5 <= len(imagelist):
                    max = show + 5
                else:
                    max = len(imagelist)
                    i = max - 5
            while i < max:
                print(imagelist[i].filename,end="")
                if select == i:
                    print(" <--Selected")
                else:
                    print("\n",end="")
                i += 1
            tinput = msvcrt.getch()
            if tinput == b'H':
                show -= 1
                select -= 1
            if tinput == b'P':
                show += 1
                select += 1
            if tinput == b'\r':
                mode = 3
                break
            if show + 4 > len(imagelist):
                show = len(imagelist) - 4
            if show < 0:
                show = 0
            if select < 0:
                select = 0
            if select >= len(imagelist):
                select = len(imagelist) - 1
            os.system("cls")

    if mode == 3:
        selectmode = 0
        while True:
            os.system("cls")
            print("Please choose the devide mode:")

            print("left to right",end='')

            if selectmode == 0:
                print(" <--Selected")
            else:
                print("\n",end='')

            print("up to down",end='')

            if selectmode == 1:
                print(" <--Selected")
            else:
                print("\n",end='')

            tinput = msvcrt.getch()
            if tinput == b'H':
                selectmode = 0
            if tinput == b'P':
                selectmode = 1
            if tinput == b'\r':
                mode = 4
                break

    if mode == 4:
        print("Please enter the number you want to divide the picture into")
        a = input()
        try:
            a = int(a)
        except ValueError:
            print("NaN,Please retry!")
            continue
        mode = 5

    if mode == 5:
        print("Please enter the beginning number")
        name = input()
        try:
            name = int(name)
        except ValueError:
            print("NaN,Please retry!")
            continue
        mode = 6

    if mode == 6:
        print("Please wait...")
        i = 1
        process = 0
        if selectmode == 0: #left to right
            canvassize = [imagelist[select].image.size[0]/a,imagelist[select].image.size[1]]
        else:
            canvassize = [imagelist[select].image.size[0],imagelist[select].image.size[1]/a]
        if selectmode == 0:
            while i <= a:
                print("width:"+str(process)+","+str(process+canvassize[0]))
                canvas = imagelist[select].image.crop((process,0,process+canvassize[0],canvassize[1]))
                process += canvassize[0]
                canvas.save(str(i+name-1)+".jpg",quality = 100)
                i += 1
        else:
            while i <= a:
                print("height:"+str(process)+","+str(process+canvassize[1]))
                canvas = imagelist[select].image.crop((0,process,canvassize[0],process+canvassize[1]))
                process += canvassize[1]
                canvas.save(str(i+name-1)+".jpg",quality = 100)
                i += 1
        imagelist = []
        mode = 1


import tools.CommandSystem as command_system
from tools.WooCommerceAPI import WC_API
from tools.ProductsParser import ProductsParser
from tools.CommandResult import CommandResult
from gui.BuyGui import BuyGUI

# Получить конкретный продукт
def get_product(id, text):
    wc_data = WC_API()
    label, prod_id, quantity = text.split(":", 3)
    if int(quantity) < 1:
        quantity = "1"
    product = wc_data.get_product_by_id(prod_id.strip())
    product_image = product['images'][0]['src']
    product_data = "" + label + "" + ": " + product['name'] + "\n\nЦена: " + product['price'] + "руб. \n\n" + ProductsParser.remove_html_from_text(product['description']) + ""
    result = CommandResult(text=product_data, image=product_image, menu=BuyGUI.get_buy_menu(prod_id, quantity))
    return result

get_product_command = command_system.Command()

get_product_command.keys = ['Товар', 'товар']
get_product_command.description = 'Вывод товара'
get_product_command.process = get_product


from datetime import datetime
from matplotlib.pyplot import text, title
from wandb import Plotly
from nbformat import write
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
import streamlit as st
from plotly.subplots import make_subplots
from st_aggrid import AgGrid, GridOptionsBuilder
import random
import plot_functions as plt
import pydeck as pdk
import os
showWarningOnDirectExecution = False
hide_menu = """

"""
footer="""

 🔝Back to Top

"""

st.set_page_config( page_title="CVA dashboards", page_icon=":bar_chart", layout="wide")

st.markdown(hide_menu, unsafe_allow_html=True)
# ---- Data Functions ---- #

@st.cache(allow_output_mutation=True)
def get_data_from_csv(file_path="./DATA/dataset1/eco2mix-national-cons-def.csv"):
    df = pd.read_csv(
    file_path,
    sep=";",
    nrows=10000
    )

    # Add 'Year' column to dataframe
    df["Year"] = pd.to_datetime(df["Date"], format="%Y-%m-%d").dt.year
    df["Month"] = pd.to_datetime(df["Date"], format="%Y-%m-%d").dt.month
    df["Day"] = pd.to_datetime(df["Date"], format="%Y-%m-%d").dt.day
    df['date_offset'] = (df["Month"]*100 + df["Day"] - 320)%1300
    df['Season'] = pd.cut(df['date_offset'], [0, 300, 602, 900, 1300],
                        labels=['spring', 'summer', 'autumn', 'winter'])
    df["Season"] = df["Season"].astype('object')

    df.replace('ND', 0,inplace=True)

    df["Ech. comm. Allemagne-Belgique (MW)"] = df["Ech. comm. Allemagne-Belgique (MW)"].astype('float64')
    df["Gaz - Cogénération (MW)"] = df["Gaz - Cogénération (MW)"].astype('float64')

    # df.dropna(inplace=True)

    df['sum_ech'] =  df[['Ech. comm. Angleterre (MW)', 'Ech. comm. Espagne (MW)','Ech. comm. Italie (MW)','Ech. comm. Suisse (MW)','Ech. comm. Allemagne-Belgique (MW)']].sum(axis=1)

    return df.sort_values(by="Date")

# df_x = get_data_from_csv()
# df_x.to_parquet("./DATA/dataset1/df.parquet.gzip",compression='gzip')
df = pd.read_parquet("./DATA/dataset1/df.parquet.gzip")

# df_y = plt.get_data_from_csv("./DATA/dataset2/points-dinjection-de-biomethane-en-france.csv")
# df_y.to_parquet('./DATA/dataset2/df.parquet.gzip',compression='gzip')
df2 = pd.read_parquet('./DATA/dataset2/df.parquet.gzip')
df2.rename(columns={"Annee mise en service":"Year","Type de site":"Type_de_site"}, inplace=True)

def app():
# Load parquet gzip
    ################################## ---- SIDEBAR ---- ##################################
    keys = random.sample(range(1000, 9999), len(df.columns))

    season = st.sidebar.multiselect(
        "Select Season:",
        options=["winter","spring","summer","autumn"],
        default=["winter","spring","summer","autumn"]
    )

    values, values2 = st.slider(
        'Please select the wanted date range ',
        int(df.Year.min()), int(df.Year.max()), (int(df.Year.min()), int(df.Year.max()))
        )

    type_de_site = st.sidebar.multiselect(
        "Type de site:",
        options=df2["Type_de_site"].unique(),
        default=df2["Type_de_site"].unique()
    )

    departement = st.sidebar.multiselect(
        "Déprtements:",
        options=df2["Departement"].unique(),
        default=df2["Departement"].unique()
    )

    # Query
    df_selection = df.query(
        "Season == @season & Year >= @values & Year <= @values2"
    )

    # Query 2
    df_selection2 = df2.query(
        " Year >= @values & Year <= @values2 & Type_de_site == @type_de_site  & Departement==@departement"
    )

    df_selection2.dropna(inplace=True)
    # plt.ag_grid_table(df_selection2,"Date de mise en service")
    ################################## ---- MAINPAGE ---- ##################################
    st.title(":bar_chart: éCO2mix Dashboard")

    #  ---- Metrics ----  #

    ## Consommation (MW)
    total_consommation = int(df_selection["Consommation (MW)"].sum() )

    ## Prévision J-1 (MW)
    j_1_total_consommation = int(df_selection["Prévision J-1 (MW)"].sum() )

    ## Prévision J (MW)
    j_total_consommation = int(df_selection["Prévision J (MW)"].sum() )

    col1, col2, col3 = st.columns(3)

    col1.metric("Total Consommation (MW)",f"{total_consommation:,}")
    col2.metric("Prévision J-1 (MW)",f"{j_1_total_consommation:,}")
    col3.metric("Prévision J (MW)", f"{j_total_consommation:,}")

    st.markdown("""---""")

    #  ---- Call Pyplot functions  ---- #

    fig_consommation_by_year = plt.consommation_bare(df_selection)
    fig_consommation_by_year_line = plt.consommation_Line(df_selection)
    fig_co2_by_year = plt.carbon_bare(df_selection)
    fig_co2_pie_by_year = plt.carbon_pie(df_selection)
    ech_fig = plt.carbon_bar_ech(df_selection)
    fig_ech_by_year = plt.ech_phy_bare(df_selection)
    # line_fig = plt.production_line(df_selection)
    # fig_3d = plt.scatter3d(df_selection)
    multi_line_fig = plt.multiple_lines(df_selection)
    figuree = plt.multiple_line_chart(df_selection)

    ### Display figures as columns
    left_column, right_column  = st.columns(2)
    left_column.plotly_chart(fig_consommation_by_year, use_container_width=False)
    right_column.plotly_chart(fig_consommation_by_year_line, use_container_width=False)

    st.plotly_chart(figuree, use_container_width=True)

    left_column_2, right_column_2  = st.columns(2)
    left_column_2.plotly_chart(fig_co2_by_year, use_container_width=False)
    right_column_2.plotly_chart(fig_co2_pie_by_year, use_container_width=False)

    left_column_3, right_column_3  = st.columns(2)
    left_column_3.plotly_chart(fig_ech_by_year, use_container_width=False)
    right_column_3.plotly_chart(ech_fig, use_container_width=False)

    ### Display figures in a container
    container = st.container()
    with container:
        container.markdown("### **La production selon les différentes filières composant le mix énergétique.**")

        container.plotly_chart(multi_line_fig, use_container_width=True)

        # container.plotly_chart(line_fig, use_container_width=True)

    plt.split_coordinates(df_selection2)
    plt.ag_grid_table(df_selection2,"Date de mise en service")
    # git figures
    fig2 = plt.density_mapbox(df_selection2)
    fig = plt.scatter_mapbox(df_selection2)

    container2 = st.container()
    with container2:

        # container2.plotly_chart(fig_3d, use_container_width=True)
        container2.markdown("### **Points d'injection de Biométhane en France en service**")
        plt.pydeck(df_selection2)
        container2.plotly_chart(fig2, use_container_width=True)
        container2.plotly_chart(fig, use_container_width=True)

    st.markdown(footer,unsafe_allow_html=True)

app()


from spynnaker.pyNN.models.neural_projections.connectors \
    import FixedNumberPreConnector as CommonFixedNumberPreConnector
from pyNN.connectors import FixedNumberPreConnector as \
    PyNNFixedNumberPreConnector

import logging

logger = logging.getLogger(__file__)

class FixedNumberPreConnector(CommonFixedNumberPreConnector,
                              PyNNFixedNumberPreConnector):
    """ Connects a fixed number of pre-synaptic neurons selected at random,\
        to all post-synaptic neurons
    """
    __slots__ = []

    def __init__(
            self, n, allow_self_connections=True, safe=True, verbose=False,
            with_replacement=False, rng=None, callback=None):
        """
        :param n: \
            number of random pre-synaptic neurons connected to output
        :type n: int
        :param allow_self_connections: \
            if the connector is used to connect a Population to itself, this\
            flag determines whether a neuron is allowed to connect to itself,\
            or only to other neurons in the Population.
        :type allow_self_connections: bool
        :param safe: \
            Whether to check that weights and delays have valid values.\
            If False, this check is skipped.
        :type safe: bool
        :param verbose: \
            Whether to output extra information about the connectivity to a\
            CSV file
        :type verbose: bool
        :param with_replacement:\
            if False, once a connection is made, it can't be made again; if\
            True, multiple connections between the same pair of neurons are\
            allowed
        :type with_replacement: bool
        :param rng:
        :param callback:
        """
        # pylint: disable=too-many-arguments
        super(FixedNumberPreConnector, self).__init__(
            n=n, allow_self_connections=allow_self_connections,
            with_replacement=with_replacement, safe=safe, verbose=verbose)

    @property
    def n(self):
        return self._n_pre

    @n.setter
    def n(self, new_value):
        self._n_pre = new_value


""" Code for merging multiple L0 and L2 SVP files into a single CARIS
formatted SVP file.
"""
import click
import csv
import os
import logging
import time
from email import header
from datetime import datetime
from typing import BinaryIO, TextIO, List, Tuple
from pathlib import Path

from mergesvp.lib.errors import SvpMissingDataException
from mergesvp.lib.svpprofile import SvpProfile
from mergesvp.lib.parsers import CarisSvpParser, get_svp_profile_format, get_svp_parser
from mergesvp.lib.svplist import SvpSource, parse_svp_line
from mergesvp.lib.utils import trim_to_longest_dive

logger = logging.getLogger(__name__)

def get_svp_list(input: TextIO) -> List[SvpSource]:
    """ Parses the input csv file into a list of SvpSource objects
    """
    csvreader = csv.reader(input)
    header = next(csvreader) # disregard header line

    svp_list = []
    for row in csvreader:
        src_fn = os.path.basename(input.name)
        svp = parse_svp_line(row, src_fn, csvreader.line_num)
        svp_list.append(svp)

    return svp_list

def find_svp_profile_file(filename: str, base_folder: Path) -> Path:
    """ Attempts to find the SVP profile file in one of the expected
    locations. Raises `SvpMissingDataException` if the SVP profile
    file listed in the SVP source CSV file cannot be found.
    """
    # first check the base folder, then L0, then L2
    if (base_folder / filename).exists():
        return base_folder / filename
    elif (base_folder / 'L0' / filename).exists():
        return base_folder / 'L0' / filename
    elif (base_folder / 'L2' / filename).exists():
        return base_folder / 'L2' / filename
    else:
        raise SvpMissingDataException(
            f'Could not find SVP profile file "{filename}" '
            f'under folder {base_folder}'
        )

def _get_svp(filename: Path, fail_on_error: bool) -> None:
    # find out what format this SVP profile uses
    svp_format = get_svp_profile_format(filename)
    # get a parser to read this file
    svp_parser = get_svp_parser(svp_format)
    svp_parser.fail_on_error = fail_on_error
    # read the svp file into a SVP profile object

    svp = svp_parser.read(filename)

    return svp

def patch_svp(src: SvpSource, svp: SvpProfile) -> None:
    # replace the entire depth vs speed profile with a trimmed version
    # that only includes the deepest dive part
    svp.depth_speed = trim_to_longest_dive(svp.depth_speed)

    # some attributes read from individual SVP files should be replaced
    # by what was included in the source list file (the CSV one)
    svp.latitude = src.latitude
    svp.longitude = src.longitude
    svp.timestamp = src.timestamp

def patch_svp_profiles(svps: List[Tuple[SvpSource, SvpProfile]]) -> None:
    """Performs some additional processing/modifying of the SVP profiles.
    This currently includes filtering out unnecessary parts of the depth vs
    sound profile.
    """
    for (svp_src, svp_profile) in svps:
        patch_svp(svp_src, svp_profile)

def generate_merged_output(
        svp_source_list: List[SvpSource],
        base_folder: Path,
        output: TextIO,
        fail_on_error: bool) -> None:
    """Generates the merged SVP output file"""
    # iterate through each SvpSource object (effectivity each line of
    # the CSV file that gives us a SVP profile filename, date, and
    # location)
    svps = []
    with click.progressbar(svp_source_list, label="Reading SVP files") as svp_sources:
        for svp_source in svp_sources:
            svp_profile_fn = find_svp_profile_file(
                svp_source.filename,
                base_folder
            )
            svp = _get_svp(svp_profile_fn, fail_on_error)
            # append both the source info and the SVP profile data
            # when writing the merged file we use the src data for lat/lng/date
            src_and_svp = (svp_source, svp)
            svps.append(src_and_svp)

    if not fail_on_error:
        # then no exceptions have been thrown, but there could be warning
        # messages so show these to the user.
        for (src, svp) in svps:
            if svp.has_warning():
                logger.warn(f"File {svp.filename} generated the following errors when parsing contents")
                for msg in svp.warnings:
                    logger.warn(f"    {msg}")

    # update details of the SvpProfiles
    patch_svp_profiles(svps)

    # svps was a list of tuples including a SvpSource, and SvpProfile
    # we only need to write the SvpProfile info now that it has been
    # patched
    svps_only = [svp for (_, svp) in svps]

    writer = CarisSvpParser()
    writer.show_progress = True
    output_path = Path(os.path.realpath(output.name))
    writer.write_many(output_path, svps_only)

def merge_raw_svp_process(input: TextIO, output: TextIO, fail_on_error: bool) -> None:
    svps = get_svp_list(input)

    # base folder is what we assume is root of all possible
    # locations for the SVP profile files. SVP profiles must be located
    # under this folder in the same dir, or L0, or L2
    # Assume the base folder is the folder that the
    base_folder = Path(input.name).parent

    generate_merged_output(svps, base_folder, output, fail_on_error)

    header = None


import credentialsDyson as connectionDyson
import credentialsBridge as connectionBridge
import dysonConnect as dyc
import time
import json

# General Data
fetch_data_duration_Min = {
    "REQUEST-CURRENT-FAULTS": 60,
    "REQUEST-PRODUCT-ENVIRONMENT-CURRENT-SENSOR-DATA": 10,
    "REQUEST-CURRENT-STATE": 10
}

# Topic and qos
subscribe_topics = [
    ['/status/current', 0],
    ['/status/software', 0],
    ['/status/connection', 0]
]

# declaring what to do when reciving message

def on_message(client, userdata, msg, action="file"):
    print(msg.topic)
    retJson = json.loads(msg.payload.decode("utf-8"))
    returnMethod = retJson["msg"]
    if (returnMethod == "ENVIRONMENTAL-CURRENT-SENSOR-DATA"):
        deviceData = retJson["data"]
    elif(returnMethod == "CURRENT-STATE" or returnMethod == "STATE-CHANGE"):
        deviceData = retJson["product-state"]

    device = {connectionDyson.host: {"time": "", "data": {}}}
    device[connectionDyson.host]["time"] = dysonServer.get_Time_now()
    for attribute, value in deviceData.items():
        device[connectionDyson.host]["data"].update({attribute: value})

    # Bridging
    bridgedServer = dyc.OhMQTTbridge(connectionBridge)
    bridgedServer.connect()
    bridgedServer.Publish(msg.topic, msg.payload)
    # print(bridgedServer)

    # Save to file
    fileName = connectionDyson.host + '_' + returnMethod + '.json'
    with open(fileName, 'w') as json_file:
        json.dump(device[connectionDyson.host], json_file)

    # Pretty Printing JSON string back
    print(json.dumps(device[connectionDyson.host],
                     indent=4, sort_keys=True))

# Declearing variables
dysonServer = dyc.DysonConnect(
    connectionDyson, subscribeTopics=subscribe_topics)

# the main software
dysonServer.PublishSet(fetch_data_duration_Min.keys())
dysonServer.client.on_message = on_message

# continues fetch data according to 'fetch_data_duration_Min' dict command and timing
i = 0
while True:
    time.sleep(1)
    for command, min in fetch_data_duration_Min.items():
        if (i % (min * 60) == 0):
            dysonServer.Publish(command=command)

    i = i+1


# Backup all in-use volumes in NV region

import boto3

def lambda_handler(event, context):

        # Connect to N. Virginia region
        ec2 = boto3.client('ec2', "us-east-1")

        # Get all in-use volumes in all regions
        result = ec2.describe_volumes( Filters=[{'Name': 'status', 'Values': ['in-use']}])

        for volume in result['Volumes']:
            print ("Backing up %s in %s" % (volume['VolumeId'], volume['AvailabilityZone']))

            # Create snapshot
            result = ec2.create_snapshot(VolumeId=volume['VolumeId'],Description='Created by Lambda backup function ebs-snapshots')

            # Get snapshot resource
            ec2resource = boto3.resource('ec2', "us-east-1")
            snapshot = ec2resource.Snapshot(result['SnapshotId'])

            volumename = 'N/A'

            # Find name tag for volume if it exists
            if 'Tags' in volume:
                for tags in volume['Tags']:
                    if tags["Key"] == 'Name':
                        volumename = tags["Value"]

            # Add volume name to snapshot for easier identification
            snapshot.create_tags(Tags=[{'Key': 'Name','Value': volumename}])


"""This module contains the configuration for the system bundle."""
from typing import Dict

from polywrap_client_config_builder import BundlePackage
from polywrap_core import Uri
from polywrap_fs_plugin import file_system_plugin
from polywrap_http_plugin import http_plugin
from polywrap_uri_resolvers import ExtendableUriResolver

from .embeds import get_embedded_wrap

ipfs_providers = [
    "https://ipfs.wrappers.io",
    "https://ipfs.io",
]

sys_bundle: Dict[str, BundlePackage] = {
    "http": BundlePackage(
        uri=Uri.from_str("plugin/http@1.1.0"),
        package=http_plugin(),
        implements=[
            Uri.from_str("wrapscan.io/polywrap/http@1.0"),
        ],
        redirects_from=[
            Uri.from_str("wrapscan.io/polywrap/http@1.0"),
        ],
    ),
    "http_resolver": BundlePackage(
        uri=Uri.from_str("embed/http-uri-resolver-ext@1.0.1"),
        package=get_embedded_wrap("http-resolver"),
        implements=[
            Uri.from_str("wrapscan.io/polywrap/http-uri-resolver@1.0"),
            *ExtendableUriResolver.DEFAULT_EXT_INTERFACE_URIS,
        ],
        redirects_from=[
            Uri.from_str("wrapscan.io/polywrap/http-uri-resolver@1.0"),
        ],
    ),
    "wrapscan_resolver": BundlePackage(
        uri=Uri("https", "wraps.wrapscan.io/r/polywrap/wrapscan-uri-resolver@1.0"),
        implements=[
            Uri.from_str("wrapscan.io/polywrap/wrapscan-uri-resolver@1.0"),
            *ExtendableUriResolver.DEFAULT_EXT_INTERFACE_URIS,
        ],
        redirects_from=[Uri.from_str("wrapscan.io/polywrap/wrapscan-uri-resolver@1.0")],
    ),
    "ipfs_http_client": BundlePackage(
        uri=Uri.from_str("embed/ipfs-http-client@1.0.0"),
        package=get_embedded_wrap("ipfs-http-client"),
        implements=[Uri.from_str("wrapscan.io/polywrap/ipfs-http-client@1.0")],
        redirects_from=[Uri.from_str("wrapscan.io/polywrap/ipfs-http-client@1.0")],
    ),
    "ipfs_resolver": BundlePackage(
        uri=Uri.from_str("embed/sync-ipfs-uri-resolver-ext@1.0.1"),
        package=get_embedded_wrap("ipfs-sync-resolver"),
        implements=[
            Uri.from_str("wrapscan.io/polywrap/sync-ipfs-uri-resolver@1.0"),
            *ExtendableUriResolver.DEFAULT_EXT_INTERFACE_URIS,
        ],
        redirects_from=[
            Uri.from_str("wrapscan.io/polywrap/sync-ipfs-uri-resolver@1.0"),
        ],
        env={
            "provider": ipfs_providers[0],
            "fallbackProviders": ipfs_providers[1:],
            "retries": {"tryResolveUri": 2, "getFile": 2},
        },
    ),
    "github_resolver": BundlePackage(
        uri=Uri.from_str("wrapscan.io/polywrap/github-uri-resolver@1.0"),
        implements=ExtendableUriResolver.DEFAULT_EXT_INTERFACE_URIS,
    ),
    "file_system": BundlePackage(
        uri=Uri.from_str("plugin/file-system@1.0.0"),
        package=file_system_plugin(),
        implements=[Uri.from_str("wrapscan.io/polywrap/file-system@1.0")],
        redirects_from=[Uri.from_str("wrapscan.io/polywrap/file-system@1.0")],
    ),
    "file_system_resolver": BundlePackage(
        uri=Uri.from_str("embed/file-system-uri-resolver-ext@1.0.1"),
        package=get_embedded_wrap("file-system-resolver"),
        implements=[
            Uri.from_str("wrapscan.io/polywrap/file-system-uri-resolver@1.0"),
            *ExtendableUriResolver.DEFAULT_EXT_INTERFACE_URIS,
        ],
        redirects_from=[
            Uri.from_str("wrapscan.io/polywrap/file-system-uri-resolver@1.0")
        ],
    ),
}

__all__ = ["sys_bundle"]


import functools as ft
import itertools as it
import operator as op
import re

from typing import Dict, IO, List, Tuple

from ortools.linear_solver import pywraplp  # type: ignore

TRecipe = Tuple[List[str], List[str]]

def parse(input_file: IO) -> List[TRecipe]:
    match_expr = re.compile(r"^(.+) \(contains (.+)\)$")

    def parse_line(line: str) -> Tuple[List[str], List[str]]:
        match = match_expr.match(line)
        assert match
        ingredients = match.group(1).split(' ')
        allergens = [x.strip() for x in match.group(2).split(',')]
        return ingredients, allergens

    return [parse_line(x.strip()) for x in input_file]

def find_allergens(recipes: List[TRecipe]) -> Dict[str, str]:
    ingredients = set(x for x in it.chain.from_iterable(x[0] for x in recipes))
    allergens = set(x for x in it.chain.from_iterable(x[1] for x in recipes))

    solver = pywraplp.Solver.CreateSolver('SCIP')
    variables = dict()

    for ingredient, allergen in it.product(ingredients, allergens):
        variables[(ingredient, allergen)] = \
            solver.IntVar(0, 1, f"{ingredient}_{allergen}")

    for recipe_ingredients, recipe_allergens in recipes:
        for recipe_allergen in recipe_allergens:
            # All allergens in a recipe must be in exactly one ingredient in
            # the recipe
            constraint = [variables[(recipe_ingredient, recipe_allergen)] for
                          recipe_ingredient in recipe_ingredients]
            solver.Add(ft.reduce(op.add, constraint) == 1)

    for ingredient in ingredients:
        # Each ingredient contains at most one allergen
        constraint = [variables[(ingredient, allergen)] for
                      allergen in allergens]
        solver.Add(ft.reduce(op.add, constraint) <= 1)

    for allergen in allergens:
        # Each allergen is present in exactly one ingredient
        constraint = [variables[(ingredient, allergen)] for
                      ingredient in ingredients]
        solver.Add(ft.reduce(op.add, constraint) == 1)

    # Arbitrary choice of objective, given that we're only interested in
    # SAT
    solver.Maximize(ft.reduce(op.add, variables.values()))
    status = solver.Solve()

    ingredients_with_allergens = dict()
    if status == pywraplp.Solver.OPTIMAL:
        for ((ingredient, allergen)), outcome_var in variables.items():
            if outcome_var.solution_value() > 0:
                ingredients_with_allergens[ingredient] = allergen
    else:
        raise Exception('no solution found')

    return ingredients_with_allergens

def p_1(input_file: IO,
        debug=False):  # pylint: disable=unused-argument
    recipes = parse(input_file)
    ingredients = set(x for x in it.chain.from_iterable(x[0] for x in recipes))
    allergens = find_allergens(recipes)
    safe_ingredients = {x for x in ingredients if x not in allergens}

    return sum(1 if x in safe_ingredients else 0 for x in
               it.chain.from_iterable((x[0] for x in recipes)))

def p_2(input_file: IO,
        debug=False):  # pylint: disable=unused-argument
    allergens = sorted(find_allergens(parse(input_file)).items(),
                       key=op.itemgetter(1))
    return ','.join(x[0] for x in allergens)


# -*- 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 cv2
import numpy as np
from camera import RealCamera
import time
import matplotlib.pyplot as plt
import divers as div

class FingerTracker(object):
    def __init__(self):
        super(FingerTracker, self).__init__()
        self.cam = RealCamera()
        self.cam.start_pipe()
        self.t0 = time.time()
        self.min_over_time = np.inf
        self.x_tcp, self.y_tcp = None, None

    def max_contour(self, contour_list):
        first_i, second_i = 0, 0
        max_area = 0
        if len(contour_list) != 0:
            for i in range(len(contour_list)):
                cnt = contour_list[i]
                area_cnt = cv2.contourArea(cnt)
                if area_cnt > max_area:
                    max_area = area_cnt
                    first_i = i
            first_max = contour_list[first_i]
            contour_list = np.delete(contour_list, first_i)

            max_area = 0
            for i in range(len(contour_list)):
                cnt = contour_list[i]
                area_cnt = cv2.contourArea(cnt)
                if area_cnt > max_area:
                    max_area = area_cnt
                    second_i = i
            second_max = contour_list[second_i]
            if (len(second_max) > 50):
                return first_max, second_max
            else:
                return None, None
        else:
            return None

    def centroid(self, max_contour):
        moment = cv2.moments(max_contour)
        if moment['m00'] != 0:
            cx = int(moment['m10'] / moment['m00'])
            cy = int(moment['m01'] / moment['m00'])
            return cx, cy
        else:
            return None

    def detect_green(self):
        self.x_tcp, self.y_tcp = 50, 50
        self.t0 = time.time()
        self.min_over_time = np.inf
        print('Pas Normal')
        while (time.time() - self.t0) < 10:

            first_cont, second_cont = None, None

            # Take each frame
            self.cam.get_frame()
            frame = self.cam.color_image
            _, frame = self.cam.get_frame()
            # Convert BGR to HSV
            frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
            hsv = cv2.cvtColor(frame, cv2.COLOR_RGB2HSV)

            # define range for green color in HSV
            lower_green = np.array([60, 40, 40])
            upper_green = np.array([90, 250, 250])

            # Threshold the HSV image to get only green colors
            mask = cv2.inRange(hsv, lower_green, upper_green)
            # Bitwise-AND mask and original image
            res = cv2.bitwise_and(frame, frame, mask=mask)

            gray_mask_image = cv2.cvtColor(res, cv2.COLOR_RGB2GRAY)
            ret, thresh = cv2.threshold(gray_mask_image, 0, 255, 0)
            cont, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

            first_cont, second_cont = self.max_contour(cont)
            if first_cont is not None and second_cont is not None:
                cx1, cy1 = self.centroid(first_cont)
                cv2.circle(frame, (cx1, cy1), 5, [0, 0, 255], -1)

                cx2, cy2 = self.centroid(second_cont)
                cv2.circle(frame, (cx2, cy2), 5, [0, 255, 0], -1)

                print(FT.get_ecartement(cx1, cy1, cx2, cy2), self.min_over_time)

                if FT.get_ecartement(cx1, cy1, cx2, cy2) < self.min_over_time:
                    self.x_tcp, self.y_tcp = (cx1 + cx2) // 2, (cy1 + cy2) // 2
                    self.min_over_time = FT.get_ecartement(cx1, cy1, cx2, cy2)
                    print('Voici mon min : ', self.min_over_time)
                    self.list = [cx1, cy1, cx2, cy2]

                cv2.circle(frame, (self.x_tcp, self.y_tcp), 5, [255, 0, 0], -1)
            else:
                cv2.circle(frame, (self.x_tcp, self.y_tcp), 5, [255, 0, 0], -1)

            cv2.imshow('frame', frame)

            k = cv2.waitKey(5) & 0xFF
            if k == 27:
                break
        cv2.destroyAllWindows()
        return [self.x_tcp, self.y_tcp], self.list

    def get_ecartement(self, fingers):
        # return np.linalg.norm(self.cam.transform_3D(u1, v1)-self.cam.transform_3D(u2, v2))
        u1, v1, u2, v2 = fingers[0], fingers[1], fingers[2], fingers[3]
        ##### Temporaire : renvoie juste la difference en pixel #######"
        return np.linalg.norm(np.array([[u1-u2], [v1-v2]]))

    def get_frame_without_hand(self):
        self.depth_without_hand, self.frame_without_hand = self.cam.get_frame()

    def get_angle(self, fingers):
        u1, v1, u2, v2 = fingers[0], fingers[1], fingers[2], fingers[3]
        angle = 180/np.pi * div.py_ang(np.array([u1-u2, v1-v2]), np.array([1, 0]))
        return(angle - 90)

if __name__=="__main__":
    FT = FingerTracker()

    # input('Type whatever when you are ready to teach me !')

    FT.get_frame_without_hand()

    print('Show me the way !!')

    # tcp, fingers = FT.detect_green()        # In Image Frame
    #
    # np.save('depth_fingertrack.npy', FT.depth_without_hand)
    # np.save('coordfingers.npy', np.array([fingers]))
    # np.save('coordtcp.npy', np.array(tcp))

    tcp = np.load('coordtcp.npy')
    fingers = np.load('coordfingers.npy')[0]
    depth_without_hand = np.load('depth_fingertrack.npy')

    # Get in Camera Frame

    TCP_cam = FT.cam.transform_3D(tcp[0], tcp[1], depth_without_hand)
    P1_cam = FT.cam.transform_3D(fingers[0], fingers[1], depth_without_hand)
    P2_cam = FT.cam.transform_3D(fingers[2], fingers[3], depth_without_hand)

    print('L\'ecartement est {} pixels et l\'angle est {} degrés'.format(FT.get_ecartement(fingers), FT.get_angle(fingers)))

    e, angle = FT.get_ecartement(fingers), FT.get_angle(fingers)

    nuage = FT.cam.transform_image_to_3D(depth_without_hand)

    plt.imshow(nuage[:, :, 2])
    plt.scatter(fingers[0], fingers[1], color='g')
    plt.scatter(fingers[2], fingers[3], color='g')
    plt.scatter(tcp[0], tcp[1], color='r')
    origin = tcp
    vectors = np.array([np.cos(angle), np.sin(angle)])
    plt.quiver(*origin, vectors[0], vectors[1], scale=13)
    plt.show()


from django.urls import path, include, re_path
from . import views, view_staff, view_chief, view_admin
from sci_dashboard_app.dash_apps import simpleexsample
from django.conf.urls import url

urlpatterns = [

    path('admin_home/', view_admin.home, name = 'admin_home'),
    path('staff_home/', view_staff.home, name = 'staff_home'),
    path('chief_home/', view_chief.home, name = 'chief_home'),
    path('dashboard/', views.dashboard, name = 'dashboard'),
    # path('dashboard/(?P<start_date>[0-9][\w-]+)/(?P<end_date>[0-9][\w-]+)/$', views.dashboard, name = 'dashboard'),
    url(r'^dashboard/(?P<start_date>[0-9][\w-]+)/(?P<end_date>[0-9][\w-]+)/$', views.dashboard, name = 'dashboard'),
    path('logout/', views.logoutUser, name = 'logout'),
    path('report/by-status/', views.reportbystatus, name = 'reportbystatus'),
    path('report/by-status/start', views.start, name = 'start'),
    path('report/by-status/success', views.success, name = 'success'),
    path('report/by-status/pending', views.pending, name = 'pending'),
    path('report/by-status/received', views.received, name = 'received'),
    path('report/by-location/', views.reportbylocation, name = 'reportbylocation'),
    path('report/by-location/<int:building_id>/', views.location, name = 'location'),
    path('report/by-location/<int:building_id>/type-<int:type_id>', views.locationtype, name = 'locationtype'),
    path('detailofficer/<int:pk>', views.detailofficer, name = 'detailofficer'),
    path('roomcaretaker/', views.roomcaretaker, name = 'roomcaretaker'),
    path('ITstaff/', views.ITstaff, name = 'ITstaff'),
    path('maintenance/', views.maintenance, name = 'maintenance'),
    path('supervisor/', views.supervisor, name = 'supervisor'),
    path('manager-view/', views.managerview, name = 'manager-view'),
    path('detailofficer/edit-profile/<str:pk>', views.editProfile, name = 'edit-profile'),
    path('board-manager-view/', views.boardmanager, name = 'boardmanager'),

]