from io_module import IO_Module
from modbus import Modbus, MBError, NoResponseError
from log_reader import DigitalLogReader
import colorama
from colorama import Fore
from time import sleep
import time
from datetime import datetime, timedelta, timezone
from mb_registers import DIO_REGS
from serial import Serial


class IO_Digital(IO_Module):

    def __init__(self, modbus: Modbus):
        self.modbus = modbus
        super().__init__(self.modbus)
        self.log = DigitalLogReader(self.modbus)
 
    '''Запрос параметров'''
    # 0x0100 - текущее состояние входов
    def get_inputs_bit(self) -> str:
        data = self.modbus.read_holding_registers(DIO_REGS['in_bits'], 1)
        return format(data[0], '08b')

    # 0x0101 - 0x0110 Счетчики импульсов
    def get_inputs_counters(self):
        data = []
        for i in range(DIO_REGS['in_cnt'], DIO_REGS['in_cnt'] + 16, 2):
            data.append(self.modbus.read_uint32_holding(i))
        return data

    # 0x0101 - 0x0110 Счетчики импульсов
    def get_inputs_counters(self):
        data = []
        for i in range(DIO_REGS['in_cnt'], DIO_REGS['in_cnt'] + 16, 2):
            data.append(self.modbus.read_uint32_holding(i))
        return data                              

    # 0x0120 - режим работы входов
    def get_inputs_mode(self):
        data = self.modbus.read_holding_registers(DIO_REGS['in_mode'], 1)
        return format(data[0], '08b')

    # 0x0122 - нормальное состояние входов
    def get_inputs_norm_state(self):
        data = self.modbus.read_holding_registers(DIO_REGS['in_norm'], 1)
        return format(data[0], '08b')

    # 0x0124 - время антидребезга (ms)
    def get_debounce_channel(self, input):
        data = self.modbus.read_holding_registers(DIO_REGS['in_deb_start'] + input - 1, 1)
        return data[0]

    # 0x0130
    def get_loads_bit(self):
        data = self.modbus.read_holding_registers(DIO_REGS['load_bits'], 1)
        return format(data[0], '08b')

    # 0x0131
    def get_cred_bit(self):
        data = self.modbus.read_holding_registers(DIO_REGS['cred_bits'], 1)
        return format(data[0], '08b')

    def get_debounce_channels(self):
        return self.modbus.read_holding_registers(DIO_REGS['in_deb_start'], 8)

    '''Установка параметров'''
    def set_inputs_mode(self, val):
        self.modbus.write_holding_register(DIO_REGS['in_mode'], val)

    #        
    def set_input_mode(self, input, val):
        ret = self.modbus.read_holding_registers(DIO_REGS['in_mode'], 1)
        if val == 1:
            data = ret[0] | (0b1 << (input - 1))
        else:
            data = ret[0] & ~(0b1 << (input - 1))
        self.set_inputs_mode(data)

    # 0x0200 - текущее состояние выходов в обычно режиме
    def set_outputs(self, val):
        self.modbus.write_holding_register(DIO_REGS['out_cur'], val)

    # Установить значение на конкретном выходе [1..8]
    def set_output(self, output, val):
        ret = self.modbus.read_holding_registers(DIO_REGS['out_cur'], 1)
        if val == 1:
            data = ret[0] | (0b1 << (output - 1))
        else:
            data = ret[0] & ~(0b1 << (output - 1))
        self.set_outputs(data)


    def print_inputs(self):
        # Значения входов (битовое поле)
        print('Inputs values [bit field] :', Fore.GREEN + self.get_inputs_bit())

        # Значение счетчиков
        data = self.get_inputs_counters()
        print('Inputs counters           :', Fore.GREEN + ' | '.join(str(el) for el in data))

        # Режим работы входов (битовое поле)
        print('Inputs mode [bit field]   :', Fore.GREEN + self.get_inputs_mode())

        # Нормальное состояние входов (битовое поле)
        print('Inputs norm [bit field]   :', Fore.GREEN + self.get_inputs_norm_state())

         # Период антидребезга (ms)
        print('Debounce input (ms)       :', Fore.GREEN + ' | '.join(str(el) for el in self.get_debounce_channels()))
        

    def print_loads(self):
        # Значения датчиков нагрузки (битовое поле)
        print('Loads values [bit field] :', Fore.GREEN + self.get_loads_bit())
        # Слово достоверности датчиков нагрузки (битовое поле)
        print('Credibility loads [bit field] :', Fore.GREEN + self.get_cred_bit())


class IO_DigitalTester:

    def __init__(self, dev_tester: IO_Digital, dev_dut: IO_Digital):
        self.tester = dev_tester
        self.dut = dev_dut

    '''Управление DUT'''

    # Подать/снять питание на DUT
    def dut_switch(self, state: bool):
        self.tester.set_output(1, state)

    # Установить 1 на нечетных входах DUT
    def dut_set_odd_inputs(self):
        self.tester.set_output(2, False)

    # Установить 1 на четный входах DUT
    def dut_set_even_imputs(self):
        self.tester.set_output(2, True)

    '''Тест входов'''
    def test_inputs(self):
        self.dut_set_even_imputs()
        time.sleep(0.1)
        self.dut.print_inputs()
        time.sleep(1)
        self.dut_set_odd_inputs()
        time.sleep(0.1)
        self.dut.print_inputs()
        time.sleep(1)

    '''Тест выходов и датчика обрыва нагрузки'''
    def test_load(self):
        # Все выходы DUT разомкнуты
        print("Все выходы DUT разомкнуты...")
        self.dut.set_outputs(0)
        self.dut.print_loads()
        time.sleep(0.1)
        # Замкнуть все выходы DUT (лампочка должна гореть)
        print("Все выходы DUT замкнуты...")
        self.dut.set_outputs(0b11111111)
        time.sleep(0.1)
        
        for i in range(1, 9):
            # Разомкнуть i-ый выход DUT
            print(f"Разомкнуть выход {i}")
            self.dut.set_output(i, 0)
            time.sleep(1)
            self.dut.print_loads()
            self.dut.set_output(i, 1)
            time.sleep(1)

    def get_load(self):
        self.dut.set_outputs(0b11110111)
        # self.dut.set_outputs(0)
        time.sleep(1)
        self.dut.print_loads()


def main():
    colorama.init(autoreset=True)
    
    serial_port = Serial('COM56', 115200, timeout=0.05, parity='N', xonxoff=False)
    
    modbus_tester = Modbus(serial_port, 1)
    # dev_tester = IO_Digital(modbus_tester)
    dio = IO_Digital(modbus_tester)


    '''Тесты отдельного модуля DIO'''
    dio.sys.get_system_vars()

    # dio.print_inputs()
    # dio.get_inputs_counters()

    # modbus_dut = Modbus(serial_port, 2)
    # dev_dut = IO_Digital(modbus_dut)

    # tester = IO_DigitalTester(dev_tester, dev_dut)

    '''Включить DUT'''
    # tester.dut_switch(True)

    '''Запросить системные настройки DUT'''
    # dev_dut.sys.get_system_vars()

    '''Тест входов. Переключение значений на входах DUT'''
    # for i in range(10):
    #     tester.test_inputs()

    '''Тестирование выходов и датчиков обрыва нагрузки'''
    # tester.test_load()
    # for i in range(100):
    #     tester.get_load()




    '''Системные переменные и параметры'''
    # dev_tester.sys.get_system_vars()

    ''' Установить текущее время с учетом часового пояса'''
    # dev.sys.set_rtc()     
    

    '''Лог и архив. Настройки лога.'''
    # dev.log.get_log_info()
    # dev.log.get_random_entries()
    # for i in range(8):
    #     dev.log.set_archive_period(5 + i*2, i)
    
    # dev.log.get_log_info()

    # dev.log.get_all_archive()
    # dev.log.get_all_log()
    
    # dev.log.log_clear()
    # dev.log.archive_clear()

    '''Лог'''
    # dev.log.get_all_log()

    '''Архив'''
    # dev.log.get_all_archive()
    # print(dev.log.get_archive_entry(0, 1))

    '''Сохранение настроек'''
    # dev.sys.save_sattings()

    '''Настройки'''

    '''Регистры модуля'''
    # dev.print_inputs()

    '''Настройи модуля'''
    # for i in range(1, 9):
    #     dev.set_input_mode(i, 1)
        # print('Inputs mode [bit field]   :', Fore.GREEN + dev.get_inputs_mode())
    # dev.set_input_mode(4, 1)
    # dev.print_inputs()

    '''Обновление'''
    # dev.updater.update('fw.bin', 'MDIO-88')
    
    
if __name__ == '__main__':
    main()