from tester import *
from modbus import Modbus
from digital_io import IO_Digital
from serial import Serial
import colorama
from colorama import Fore
from mb_registers import *


class DIO_Tester(Tester):
    def __init__(self, dut_debug_port, mb_port, tester_addr=1, dut_address=2):
        super().__init__(dut_debug_port)

        serial_port = Serial(mb_port, 115200, timeout=0.05, parity='N', xonxoff=False)
        self.modbus_tester = Modbus(serial_port, tester_addr)
        self.modbus_dut = Modbus(serial_port, dut_address)
        
        self.tester = IO_Digital(self.modbus_tester)
        self.dut = IO_Digital(self.modbus_dut)


    def startup(self):
        """Предварительная настройка tester, dut"""
        self.tester.set_outputs_state(0b1111_1111)

        # Подача питания на DUT
        self.tester.set_output(1, 0)
        time.sleep(0.3)

        self.tester.set_output(1, 1)
        time.sleep(0.3)

        # Отключаем безопасные режимы
        self.tester.sys.set_save_mode(0)
        # self.dut.sys.set_save_mode(0)        

        


    def test_di(self):
        """Тест цифровых вхдов"""
        print('[Тестер] Установка выхода 2 --> 0')
        self.tester.set_output(2, 0)
        time.sleep(0.5)

        ret = self.dut.get_inputs_bit()
        even_inputs = ret[::2] # четные
        odd_inputs = ret[1::2] # нечетные

        if odd_inputs != '1111':
            print(Fore.RED + f'Нечетные входы не замкнуты [{odd_inputs}]. ERROR.')
        else:
            print(Fore.GREEN + 'Нечетные входы замкнуты. OK.')

        if even_inputs != '0000':
            print(Fore.RED + f'Четные входы не разомкнуты [{even_inputs}]. ERROR.')
        else:
            print(Fore.GREEN + 'Четные входы разомкнуты. OK.')

        print('[Тестер] Установка выхода 2 --> 1')
        self.tester.set_output(2, 1)
        time.sleep(0.5)

        ret = self.dut.get_inputs_bit()
        even_inputs = ret[::2] # четные
        odd_inputs = ret[1::2] # нечетные

        if odd_inputs != '0000':
            print(Fore.RED + f'Нечетные входы не разомкнуты [{odd_inputs}]. ERROR.')
        else:
            print(Fore.GREEN + 'Нечетные входы разомкнуты. OK.')

        if even_inputs != '1111':
            print(Fore.RED + f'Четные входы не замкнуты [{even_inputs}]. ERROR.')
        else:
            print(Fore.GREEN + 'Четные входы замкнуты. OK.')


    def test_do(self, do_number=1):
        """Проверка выхода"""
        self.tester.set_output(4, 1)
        self.dut.set_outputs_state(0b1111_1111)
        time.sleep(0.5)
        self.dut.set_outputs(0b1111_1111)
        time.sleep(0.5)
        # Разомкнуть нужный выход DUT
        self.dut.set_output(do_number, 0)
        time.sleep(1)
        ret = self.dut.get_loads_bit()
        if ret[-do_number] != '0':
            print(Fore.RED + f'Нагрузка к выходу {do_number} не подключена {ret}. ERROR.')
            return

        self.tester.set_output(4, 0)
        time.sleep(0.5)
        ret = self.dut.get_loads_bit()
        if ret[-do_number] == '0':
            print(Fore.RED + f'Датчик обрыва назгрузки выхода {do_number} не работает {ret}. ERROR.')
            return
        
        print(Fore.GREEN + f'Датчик обрыва нагрузки выхода {do_number}. OK.')


    def test_all_do(self):
        for i in range(1, 9):
            self.test_do(i)
            time.sleep(0.3)



    def test_rtc(self):
        """Проверка часов, батарейки и переключателя modbus адреса"""
        print("[Tester] Установка времени на DUT")
        self.dut.sys.set_rtc()
        # Снимаем питание с DUT
        self.tester.set_output(1, 0)
        print(Fore.YELLOW + "Переключите адрес на DUT в положение 'все переключатели вверх'.")
        print(Fore.YELLOW + "Затем нажмите 'Enter'")
        input()
        # Подаем питание на DUT
        self.tester.set_output(1, 1)
        time.sleep(3)

        # Меняем modbus-адрес DUT 
        self.modbus_dut.address = 15
        dut_time = self.dut.sys.get_rtc()
        print("Время DUT", dut_time)

        print(time.time() + self.dut.sys.utc_offset, dut_time/1000.0)
        print(abs(dut_time/1000.0 - (time.time() + self.dut.sys.utc_offset)))

        if abs(dut_time/1000.0 - (time.time() + self.dut.sys.utc_offset)) > 1:
            print(Fore.RED + f'Время не соответствует реальному. ERROR.')
        else:
            print(Fore.GREEN + 'Тест RTC и переключателя адреса. OK.')
            print(Fore.YELLOW + "Установите modbus адрес в значение 2 и нажмите 'Enter'.")
            input()
 

    def test_led(self):
        print("[Тест... Проверка LED")
        # Зеленые LED индикациии входов
        print(Fore.YELLOW + "Посмотрите на зеленые светодиоды индикации входов")
        self.tester.set_output(2, 1)
        time.sleep(1)
        self.tester.set_output(2, 0)
        time.sleep(1)
        self.tester.set_output(2, 1)
        time.sleep(1)
        self.tester.set_output(2, 0)
        print("Нажмите 'Enter'")
        input()

        # Зеленые LED индикации 
        print(Fore.YELLOW + "Посмотрите на зеленые светодиоды индикации выходов")
        time.sleep(2)
        # включаем все выходы
        self.dut.set_outputs_state(0b1111_1111)
        for i in range(1, 9):
            self.dut.set_output(i, 1)
            time.sleep(1)
        for i in range(8, 0, -1):
            self.dut.set_output(i, 0)
            time.sleep(1)
        print("Нажмите 'Enter'")
        input()

        # Красные LED индикации нагрузки/аварии выходов
        print(Fore.YELLOW + "Посмотрите на красные светодиоды индикации аварий")
        time.sleep(2)

        self.dut.set_outputs_state(0b0000_0000)
        time.sleep(2)

        for i in range(1, 9):
            self.dut.set_output_state(i, 1)
            time.sleep(1)
        for i in range(8, 0, -1):
            self.dut.set_output_state(i, 0)
            time.sleep(1)

        print("Нажмите 'Enter'")
        input()


    def test_battery_voltage(self):
        print("[Тест... Проверка схемы измерения напряжения батарейки CR2032]")
        bat_voltage = self.dut.sys.get_bat_votage()/1000.0
        if bat_voltage < 3.1:
            print(Fore.RED + f' Результат: Напряжение батареи слишком низкое {bat_voltage}. ERROR.')
        else:
            print(Fore.GREEN + f' Результат: Напряжение батареи в норме: {bat_voltage}. OK')


    def test_wdt(self):
        print("[Тест... Проверка WDT]")
        old_uptime = self.dut.sys.get_uptime()
        time.sleep(4)
        self.dut.sys.wdt_stop()
        time.sleep(2)
        new_uptime = self.dut.sys.get_uptime()
        if new_uptime > old_uptime:
            print(Fore.RED + ' Результат: Ошибка работы WDT [Error]')
        else:
            print(Fore.GREEN + f' Результат: WDT работает. OK')


    def set_sys_params(self):
        print("Введите серийный номер устройства и нажмите 'Enter'")
        print('SN: ', end='')
        sn = input()

        # Отправка пароля для разблокирования доступа к системным настройкам
        self.dut.modbus.write_holding_register(SysReg.PASSWORD.value, 1234)
        time.sleep(0.1)

        # Актуальная дата производства
        prod_time = int(time.time()) - time.timezone
        self.dut.modbus.write_uint32(SysReg.PRODATE.value, prod_time)

        # Серийный номер
        self.dut.modbus.write_uint32(SysReg.SN.value, int(sn))
    
        # Статус тестирования
        self.dut.modbus.write_holding_register(SysReg.TEST_STATUS.value, 0x0001)

        # Команда сохранения системных настроек
        self.dut.modbus.write_holding_register(SysReg.SAVE_SYS.value, 0x0001)




    def test_all(self):
        pass

    def foo(self):
        print(self.modbus_dut.address)
        print(self.modbus_tester.address)

        self.modbus_dut.address = 15
        print(self.modbus_dut.address)

    def menu(self):

        self.tester.set_output(1, 1)
        self.startup()
                
        questions = [
	    inquirer.List('action',
                message="Тестирование MDIO_88",
                choices=['Прошить IAP и FW', 
                        'Прочитать системные настройки',
                        'Записать системные настройки',
                        'Полный тест',
                        'Тест цифровых входов',
                        'Тест цифровых выходов',
                        'Тест RTC',
                        'Тест LED',
                        'Тест напряжения батареи CR2032',
                        'Тест WDT',
                        'Завершить'],
            ),
	    ]

        while True:
            answers = inquirer.prompt(questions)

            if answers['action'] == 'Прошить IAP и FW':
                self.write_iap_fw('bin_dio/fw.bin', 'bin_iap/iap.bin')
            elif answers['action'] == 'Прочитать системные настройки':
                self.dut.sys.get_system_vars()
            elif answers['action'] == 'Полный тест':
                print('Запуск цикла тестирования...')
                self.test_all()
            elif answers['action'] == 'Записать системные настройки':
                self.set_sys_params()
            elif answers['action'] == 'Тест цифровых входов':
                self.test_di()
            elif answers['action'] == 'Тест цифровых выходов':
                self.test_all_do()
            elif answers['action'] == 'Тест RTC':
                self.test_rtc()
            elif answers['action'] == 'Тест LED':
                self.test_led()
            elif answers['action'] == 'Тест напряжения батареи CR2032':
                self.test_battery_voltage()
            elif answers['action'] == 'Тест WDT':
                self.test_wdt()
            elif answers['action'] == 'Завершить':
                return






def main():
    tester = DIO_Tester('COM3', 'COM11', 1, 2)
    tester.menu()
    # tester.foo()
    # tester.dut_powerup()


if __name__ == '__main__':
    main()