import tkinter as tk
from tkinter import filedialog
import openpyxl
import pandas as pd
import time
import nidaqmx
import keyboard
import csv
from queue import Queue
import concurrent.futures

root = tk.Tk()
root.withdraw()  # Hide the main window

file_path = filedialog.askopenfilename(title="Select Excel File",
                                       filetypes=[("Excel Files", "*.xlsx *.xlsm *.xlsb")])
df = pd.read_excel(file_path)

length = len(df)

def acquire_lvdt_data_and_control_voltage(input_device, load_channel, disp_channel, output_device,
                                           output_channel_positive, output_channel_negative,
                                           num_samples, sample_rate, max_disp, min_disp,
                                           stop, d_maxmax, d_minmin, max_cycles, d_prev):
    data_buffer = []  # Array to store data during the test
    output_started = False
    output_channel = output_channel_positive
    cycle_count = 1  # Variable to count cycles
    voltage1 = df.iloc[0, 1]
    voltage2 = df.iloc[0, 2]
    previous_displacement = None  # To track rising edge
    reached_min_disp = False
    q_d = Queue(maxsize=num_samples)

    with nidaqmx.Task() as input_task,  nidaqmx.Task() as output_task:

        input_task.ai_channels.add_ai_voltage_chan(f"{input_device}/{load_channel}")
        input_task.ai_channels.add_ai_voltage_chan(f"{input_device}/{disp_channel}")
        input_task.timing.cfg_samp_clk_timing(sample_rate, samps_per_chan=num_samples,
                                              sample_mode=nidaqmx.constants.AcquisitionType.CONTINUOUS)

        output_task.ao_channels.add_ao_voltage_chan(f"{output_device}/{output_channel_positive}")
        output_task.ao_channels.add_ao_voltage_chan(f"{output_device}/{output_channel_negative}")

        try:
            with concurrent.futures.ThreadPoolExecutor() as executor:
                while cycle_count <= max_cycles:
                    # Submit the data acquisition task
                    i=0
                    while i<length:
                        future = executor.submit(input_task.read, number_of_samples_per_channel=num_samples)
                        data = future.result()
                        load = data[0][0]
                        displacement = data[1][0]
                        logic = df.iloc[i,3]
                        c_disp = df.iloc[i,0]
                        n_disp = df.iloc[i+1,0]

                        # Process the previous batch while waiting for the new data
                        
                        if (logic == "<=" and displacement <= c_disp and displacement <= n_disp) or (logic == ">=" and displacement >= c_disp and displacement >= n_disp):
##                            voltage1 = df.iloc[i, 1]
##                            voltage2 = df.iloc[i, 2]
##                            output_task.write([voltage1, voltage2])
                            i += 1
                            if i>= length:
                                break
                        voltage1 = df.iloc[i, 1]
                        voltage2 = df.iloc[i, 2]
                        output_task.write([voltage1, voltage2])
                        print(r_disp, c_disp, voltage1, voltage2, logic)

                        # Wait for the data acquisition task to complete
                        
                        timestamp = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()) + \
                                    f".{int(time.time() * 1000) % 1000:03d}"

                        data_buffer.append([timestamp, c_disp, displacement,  voltage1, voltage2])

                    cycle_count += 1
                    d_prev = displacement

                    # Start the output task if not started
                    if not output_started:
                        output_task.start()
                        output_started = True

                    # Check if the maximum displacement exceeds the stop threshold
                    if displacement >= d_maxmax or displacement <= d_minmin:
                        print(f"Maximum displacement exceeded. Force stopping.")
                        output_task.write([0.0, 0.0])
                        raise StopIteration

        except (StopIteration, KeyboardInterrupt):
            output_task.write([0.0, 0.0])
            print("Test interrupted by user or force stopped.")

        finally:
            if output_started:
                output_task.write([0.0, 0.0])

    save_data_to_csv(data_buffer)


def save_data_to_csv(data):
    file_name = f'lvdt_data_{get_timestamp()}.csv'

    with open(file_name, mode='w', newline='') as csvfile:
        data_writer = csv.writer(csvfile)
        data_writer.writerow(['Time', 'Displacement', 'Load'])
        data_writer.writerows(data)
        print("Data Saved to csv")


def get_timestamp():
    return time.strftime('%Y%m%d_%H%M%S', time.localtime())


def read_zero(input_device, disp_channel, num_samples, sample_rate):
    with nidaqmx.Task() as input_task_2:
        input_task_2.ai_channels.add_ai_voltage_chan(f"{input_device}/{disp_channel}")
        input_task_2.timing.cfg_samp_clk_timing(sample_rate, samps_per_chan=num_samples)

        zero_disp = input_task_2.read(number_of_samples_per_channel=num_samples)[0]
        input_task_2.stop()

        return zero_disp


if __name__ == "__main__":
    input_device_name  = 'Dev1'  # Replace with your input device name
    input_channel_load = 'ai1'  # Replace with your input channel name
    input_channel_disp = 'ai2'  # Replace with your input channel name

    output_device_name = 'Dev1'  # Replace with your output device name
    output_channel_positive = 'ao0'  # Replace with your positive output channel name
    output_channel_negative = 'ao1'  # Replace with your negative output channel name

    num_samples = 100
    sample_rate = 1000.0  # Hz
    print(df['Disp'].max(),df['Disp'].min())

    actual_zero = read_zero(input_device_name, input_channel_disp, num_samples, sample_rate)
    zero = df['Disp'].mean()

    correction = zero - actual_zero
    df['Disp'] = df['Disp'] - correction
    print(zero, actual_zero, df['Disp'].mean())

    max_d = df['Disp'].max()
    d_volt = max_d - actual_zero
    min_d = df['Disp'].min()
    print(max_d, min_d)

    stop_t = d_volt * 0.25  # Set the stop threshold percentage
    dmaxmax = max_d + stop_t
    dminmin = min_d - stop_t
    d_previous = df.iloc[0, 0]
    print(max_d, min_d)

    max_cycles = int(input("Enter num of cycles: "))  # Set the maximum number of cycles

    try:
        acquire_lvdt_data_and_control_voltage(input_device_name, input_channel_load, input_channel_disp,
                                              output_device_name, output_channel_positive, output_channel_negative,
                                              num_samples, sample_rate, max_d, min_d, stop_t, dmaxmax, dminmin,
                                              max_cycles, d_previous)
    except Exception as e:
        print(f"An error occurred: {e}")

