class_name yems_boards extends Node

class EmsBoard:
    enum {WAIT, WR_DEV_NUM_W, WRITE, WR_DEV_NUM_R, READ, READ_FLASH, WRITE_FLASH,
    WRITE_FLASH_BEGIN, WRITE_FLASH_END, LOADING}   ## Состояния платы

    enum LoadState {NOT_LOAD, DONE, IN_PROCESS, FAIL, NOT_DEFINED}

    const modulation_dev: Dictionary = {  ## Словарь соответствия диапазона номеру устройства
    0 : 50,
    1 : 51,
    2 : 52,
    3 : 51,
    4 : 52,
    5 : 51,
    6 : 52, }

    var id_board: int                          ## id платы
    var in_devs: Dictionary                    ## Словарь с входными устройствами.
    var out_devs: Dictionary                   ## Словарь с выходными устройствами.
    var base_addr_isa: int                     ## Базовый адрес ячейки isa.
    var ports_to_wr: Array                     ## Порты для записи
    var in_tbl: Array
    var out_tbl: Array
    var online: bool                           ## статус платы
    var ems_load: int = LoadState.NOT_DEFINED  ## состояние загрузки платы
    var state_cmd = WAIT                       ## текущее состояние платы ЭМС
    var state_board = WAIT                     ## текущее состояние платы ЭМС
    var cur_dev                                ## Текущее устройство
    var cur_dev_num: = 0                       ## Номер текущего устройства
    var cur_dev_index: = 0                     ## Индекс текущего устройства в списке
    var dev_num_list: = Array()                ## Список всех устройств
    var need_next_dev: bool = false            ## Нужно ли переходить к следующему устройству
    var unit_ems                               ## Юнит эмс
    var read_done: bool = false                ## Флаг, что данные из ячейки прочитанны
    var need_flash_rd: bool = false            ## Флаг, что надо прочитать флеш
    var need_flash_wr: bool = false            ## Флаг, что надо записать данные в флеш
    var need_write: bool = false               ## Флаг, что надо записать данные в ячейку ЭМС
    var ems_g_fn: String = ''                  ## Имя файла настроек ЭМС-Г
    var cmd_array: Array                       ## Очередь выполнения команд

    signal upd_delay_offset(board, cur_dev)    ## Сигнал о обновлении данных задержек
    signal upd_o_state(board, cur_dev)         ## Сигнал о обновлении данных режима выходов

    func _init(id, base_addr, in_table, out_table, u):
        id_board = id
        in_tbl = in_table
        out_tbl = out_table
        base_addr_isa = base_addr
        unit_ems = u
        for i in range(4):
            ports_to_wr.append(base_addr_isa + 4 + i * 2)  # Заполнение списка портами для записи
        dev_num_list = in_tbl + out_tbl

## Устанавливает состояние входов и выходов платы
    func set_status(status: PackedByteArray):
        var offset = 0
        if id_board < 3:
            offset = yemsconsts.STATUS_OFFSET[id_board]
            var bit_online = 0 + id_board
            var bit_load = 3 + id_board
            online = (status[0] >> bit_online) & 1
            ems_load = LoadState.DONE if ((status[0] >> bit_load) & 1) else LoadState.NOT_LOAD
        else:
            online = status[0] & 1
            var bit_load = 4
            if online:
                if (ems_load != LoadState.IN_PROCESS) and (ems_load != LoadState.FAIL):
                    if (ems_load == LoadState.NOT_DEFINED) or not (((status[0] >> bit_load) & 1)):
                        ems_load = LoadState.NOT_LOAD
            else:
                ems_load = LoadState.NOT_DEFINED
                set_wait()
        var in_blank = int(status[1 + offset] | (status[2 + offset] << 8) | (status[3 + offset] << 16) | \
                (status[4 + offset] << 24) | (status[5 + offset] << 32))
        var in_imp = int(status[7 + offset] | (status[8 + offset] << 8) | (status[9 + offset] << 16) |  \
                (status[10 + offset] << 24) | (status[11 + offset] << 32))
        var out_blank = int(status[13 + offset] | (status[14 + offset] << 8) | (status[15 + offset] << 16) |  \
                (status[16 + offset] << 24) | (status[17 + offset] << 32))
        var out_imp = int(status[19 + offset] | (status[20 + offset] << 8) | (status[21 + offset] << 16)|  \
               (status[22 + offset] << 24) | (status[23 + offset] << 32))
        var out_kz = int(status[25 + offset] | (status[26 + offset] << 8))
        if id_board > 0:
            out_kz = 0

        for in_dev in in_devs.values():
            if (in_blank >> (in_dev.id - 1)) & 1:
                in_dev.state = 1
            elif (in_imp >> (in_dev.id - 1)) & 1:
                in_dev.state = 2
            else:
                in_dev.state = 0

        for out_dev in out_devs.values():
            if (out_kz >> (out_dev.id - yemsconsts.STATUS_OUT_OFFSET)) & 1:
                out_dev.state = 3
            elif (out_blank >> (out_dev.id - yemsconsts.STATUS_OUT_OFFSET)) & 1:
                out_dev.state = 1
            elif (out_imp >> (out_dev.id - yemsconsts.STATUS_OUT_OFFSET)) & 1:
                out_dev.state = 2
            else:
                out_dev.state = 0

## Отправка команды на запись или чтение порта
    func on_wr_device(dev_number):
        if (state_cmd == WR_DEV_NUM_W) or (state_cmd == WR_DEV_NUM_R):
            unit_ems.send_isa(unit_ems.CmdCode.WRITE_ISA, [base_addr_isa + 2, dev_number])
        elif state_cmd == WRITE:
            var ports: Array = []
            if not check_dev_number():
                return
            for port in cur_dev.ports_data:
                if port in ports_to_wr:
                    var data = cur_dev.ports_data[port]
                    ports.append(port)
                    ports.append(data)
            unit_ems.send_isa(unit_ems.CmdCode.WRITE_ISA, ports)
        elif state_cmd == READ:
            var ports: Array = []
            for port in cur_dev.ports_data:
                if port in ports_to_wr:
                    ports.append(port)
            unit_ems.send_isa(unit_ems.CmdCode.READ_ISA, ports)

    ## Проверяет правильность выполнения команды записи номера устройства
    func check_set_dev(cmd_s):
        if unit_ems.isa_ports.has(base_addr_isa + 2):
            cur_dev_num = unit_ems.isa_ports[base_addr_isa + 2]
            state_cmd = cmd_s
        if not check_dev_number():
            return
        on_wr_device(cur_dev_num)

## Обработчик сообщения, о том, что команда выполнена (машина состояний)
    func on_command_done(_yau_07):
        if not check_dev_number():
            return
        if (unit_ems.cmd_code_rx == 1) and state_cmd == WR_DEV_NUM_W:
            check_set_dev(WRITE)
        elif (unit_ems.cmd_code_rx == 1) and state_cmd == WR_DEV_NUM_R:
            check_set_dev(READ)
        elif (state_cmd == READ) or (state_cmd == WRITE) \
            and ((unit_ems.cmd_code_rx == 1) or (unit_ems.cmd_code_rx == 0)):
            for key in unit_ems.isa_ports.keys():
                if cur_dev.ports_data.has(key):
                    cur_dev.ports_data[key] = unit_ems.isa_ports[key]
            cur_dev.set_delay_offset()
            emit_signal('upd_delay_offset', self, cur_dev)
            if cur_dev is yemsdevs.OutDev:  # Если это выход, заполняем комбобоксы и чекбатоны
                cur_dev.set_mode()
                cur_dev.set_btns_state()
                emit_signal('upd_o_state', self, cur_dev)
            if need_next_dev:  # Если нужно вычитать все устройства, выбираем следующее
                cur_dev_index += 1
                if cur_dev_index < len(dev_num_list):
                    cur_dev_num = dev_num_list[cur_dev_index][0]
                    if state_cmd == READ:
                        state_cmd = WR_DEV_NUM_R
                        on_wr_device(cur_dev_num)
                    else:
                        state_cmd = WR_DEV_NUM_W
                        on_wr_device(cur_dev_num)
                else:  # Пробег закончен, выбираем последнее устройство
                    set_wait()
                    read_done = true
                    cur_dev_index -= 1
                    need_next_dev = false
            else:
                if cmd_array.size():
                    execute_cmd()
                else:
                    set_wait()

        elif (state_cmd == READ_FLASH) and (unit_ems.cmd_code_rx == unit_ems.CmdCode.READ_FLASH_MP550):
            on_read_flash()
        elif (state_cmd == WRITE_FLASH) and (unit_ems.cmd_code_rx == unit_ems.CmdCode.WRITE_FLASH_MP550):
            set_wait()
        elif state_board == LOADING:
            var bit_load = 4
            if ((unit_ems.status[0] >> bit_load) & 1):
                ems_load = LoadState.DONE
            set_wait()

## Выбор текущего устройства и проверка правильности его номера
    func check_dev_number():
        cur_dev = get_dev()
        if not cur_dev:
            state_cmd = WAIT
            return false
        return true

## Возвращает устройство по текущему номеру, если он есть в словарях
    func get_dev():
        if in_devs.has(cur_dev_num):
            return in_devs[cur_dev_num]
        elif out_devs.has(cur_dev_num):
            return out_devs[cur_dev_num]
        else:
            return null

## Запуск чтения/записи данных из/в ячейки КЭМС
    func start_rw_kems(read: bool):
        cur_dev_index = 0
        cur_dev_num = dev_num_list[cur_dev_index][0]
        need_next_dev = true
        if read:
            state_cmd = WR_DEV_NUM_R
        else:
            state_cmd = WR_DEV_NUM_W
            need_write = false
        on_wr_device(cur_dev_num)

## Устанавливает связь между входом и выходом
    func set_connection(o_dev, i_dev, val):
        cur_dev_num = o_dev
        if check_dev_number():
            cur_dev.set_dot(i_dev, val)
            state_cmd = WR_DEV_NUM_W
            on_wr_device(cur_dev_num)

## Изменяет режим работы выхода
    func change_mode(o_dev, mode):
        cur_dev_num = o_dev
        if check_dev_number():
            cur_dev.set_sw_mode(mode)
            state_cmd = WR_DEV_NUM_W
            on_wr_device(cur_dev_num)

## Устанавливает задержи устройства
    func set_delay(dev_index, val, is_delay, read):
        cur_dev_num = dev_index
        if check_dev_number():
            if not read:
                if is_delay:
                    cur_dev.set_delay(val)
                else:
                    cur_dev.set_offset(val)
                state_cmd = WR_DEV_NUM_W
            else:
                state_cmd = WR_DEV_NUM_R
            on_wr_device(cur_dev_num)

    ## Отправка команды чтения флеш
    func read_flash():
        unit_ems.send_flash(unit_ems.CmdCode.READ_FLASH_MP550, [], yemsconsts.NUM_BYTE_FROM_FLASH, id_board)
        state_cmd = READ_FLASH

    ## Обработчик выполнения команды чтения флеш, заполняет прочитанными данными устройства
    func on_read_flash():
        need_flash_rd = false
        var amount_dev = unit_ems.flash_len / 10
        for i in amount_dev:
            var cur_i = i * 10
            cur_dev_num = unit_ems.flash_data.decode_u16(cur_i)
            if not check_dev_number(): continue
            cur_dev.ports_data[base_addr_isa + 4] = unit_ems.flash_data.decode_u16(cur_i + 2)
            cur_dev.ports_data[base_addr_isa + 6] = unit_ems.flash_data.decode_u16(cur_i + 4)
            cur_dev.ports_data[base_addr_isa + 8] = unit_ems.flash_data.decode_u16(cur_i + 6)
            cur_dev.ports_data[base_addr_isa + 10] = unit_ems.flash_data.decode_u16(cur_i + 8)
        start_rw_kems(false)

    ## Команда записи в флеш текущих данных из программы
    func write_flash():
        need_flash_wr = false
        var fl_data: Array = []
        var cur_dev_temp = cur_dev_num
        for i in yemsconsts.NUM_INPUT:
            cur_dev_num = i + 1
            append_data_to_flash(fl_data)
        for i in yemsconsts.NUM_OUTPUT:
            cur_dev_num = i + yemsconsts.STATUS_OUT_OFFSET
            append_data_to_flash(fl_data)
        unit_ems.send_flash(unit_ems.CmdCode.WRITE_FLASH_MP550, fl_data, 0, id_board)
        cur_dev_num = cur_dev_temp
        state_cmd = WRITE_FLASH

    ## Заполнение данными с устройств массива для записи флеш
    func append_data_to_flash(fl_data):
        fl_data.append(cur_dev_num)
        if check_dev_number():
            fl_data.append(cur_dev.ports_data[base_addr_isa + 4])
            fl_data.append(cur_dev.ports_data[base_addr_isa + 6])
            fl_data.append(cur_dev.ports_data[base_addr_isa + 8])
            fl_data.append(cur_dev.ports_data[base_addr_isa + 10])
        else:
            for j in range(4):
                fl_data.append(0)

    ## Выстевление в устройства данных прочитанных из файла
    func btn_load(json_data):
        for item in json_data.keys():
            var ports: Dictionary = json_data[item]
            cur_dev_num = int(item)
            if not check_dev_number(): continue
            if ports.has('%d'%(base_addr_isa + 4)):
                cur_dev.ports_data[base_addr_isa + 4] = int(ports['%d'%(base_addr_isa + 4)])
            else: return false
            if ports.has('%d'%(base_addr_isa + 6)):
                cur_dev.ports_data[base_addr_isa + 6] = int(ports['%d'%(base_addr_isa + 6)])
            else: return false
            if ports.has('%d'%(base_addr_isa + 8)):
                cur_dev.ports_data[base_addr_isa + 8] = int(ports['%d'%(base_addr_isa + 8)])
            else: return false
            if ports.has('%d'%(base_addr_isa + 10)):
                cur_dev.ports_data[base_addr_isa + 10] = int(ports['%d'%(base_addr_isa + 10)])
            else: return false
        return true

    func set_load_done():
        if unit_ems.cmd_state != unit_ems.CmdState.DONE: return
        state_board = LOADING
        unit_ems.send_load_ems_done(unit_ems.CmdCode.LOAD_EMS_DONE)

    func execute_cmd():
        if unit_ems.cmd_state != unit_ems.CmdState.DONE: return
        if len(cmd_array):
            state_board = WRITE
            var cmd: Array = cmd_array.pop_front()
            change_mode(cmd[0], cmd[1])

    func add_cmd(band, mode_state):
        var state = 2
        if mode_state:
            state = 3
        cmd_array.append([modulation_dev[band], state])

    func get_dev_mode(fs_index):
        return out_devs[modulation_dev[fs_index/4]].mode

    func set_wait():
        state_cmd = WAIT
        state_board = WAIT
