class_name sch3_view

extends 'res://scenes/контроль/элемент-я.gd'

## Дополнение №1 к протоколу №120-23900-18
## информационного взаимодействия изделия 5П-28
## с аппаратурой УА-РЭП на кораблях проекта 23900

@export var base_input_address:   = 0     ## Базовый адрес регистров входов
@export var base_holding_address: = 0     ## Базовый адрес регистров хранения
@export var regs_input_count:     = 5     ## Количество регистров входов
@export var regs_holding_count:   = 4     ## Количество регистров хранения
@export var modbus_address:       = 7     ## Адрес устройства на шине Modbus
@export var request_period:       = 200   ## Период в секундах отправки команд на шину Modbus
@export var reconnect_period:     = 3000  ## Период переподключения в секундах

var regs_input:    = {}     ## Регистры входные
var regs_holding:  = {}     ## Регистры хранения
var switches_sync: = false  ## Флаг. Если установлен, то можно переключать
var external_ctl: = false   ## Флаг внешнего управления

func on_error(msg, unit_sch3):
    if unit_sch3.online:
        push_error(msg)


## Вызывается, когда состояния прибора Щ3 "на связи" изменяется[br]
## [param unit_sch3] - Экземпляр представления прибора Щ3
func on_line_changed_sch3(unit_sch3):
    for item in get_tree().get_nodes_in_group('bits_items_good'):
        item.texture = textures[STATE_VAL.NONE]
    if not unit_sch3.online:
        for item in get_tree().get_nodes_in_group('bits_items_switch'):
            item.disabled = true
        regs_holding.clear()
        regs_input.clear()
        switches_sync = false
        $margin/vbox/grid1/lbl_freq.text = '<нет данных>'
        $margin/vbox/grid1/lbl_freq.self_modulate = Color.CADET_BLUE
        $margin/vbox/grid1/state_freq.texture = textures[STATE_VAL.NONE]
        $margin/vbox/grid/state.texture = textures[STATE_VAL.NONE]


func on_settings_changed():
    external_ctl = ProjectSettings.get_setting('application/config/Внешнее управление', false)
    if not external_ctl:
        return
    var items = get_tree().get_nodes_in_group('bits_items_switch')
    for item in items:
        item.disabled = external_ctl
    items = get_tree().get_nodes_in_group('bits_items_good')
    for item in items:
        item.texture = textures[STATE_VAL.NONE]

## Вызывается при изменении состояния выключателя[br]
## [param val] - Состояние выключателя[br]
## [param meta] - Адреса регистров и индексы битов назначенных выключателю[br]
func on_toggled_switch(val: bool, meta: Array, unit_sch3):
    var on_addr  = meta[0]
    var on_bit   = meta[1]
    var off_addr = meta[2]
    var off_bit  = meta[3]
    if regs_holding.has(on_addr) and regs_holding.has(off_addr):
        regs_holding[on_addr]  = tools.set_bit(regs_holding[on_addr],  on_bit,      val)
        regs_holding[off_addr] = tools.set_bit(regs_holding[off_addr], off_bit, not val)
    unit_sch3.push_write_holding(regs_holding)


func _enter_tree() -> void:
    super._enter_tree()
    if Engine.is_editor_hint(): return
    var unit_sch3: = network.get_unit_instance(unit_name)
    unit_sch3.base_holding_address = base_holding_address
    unit_sch3.regs_holding_count = regs_holding_count
    unit_sch3.base_input_address = base_input_address
    unit_sch3.regs_input_count = regs_input_count
    unit_sch3.request_period = request_period
    unit_sch3.reconnect_period = reconnect_period
    unit_sch3.connect('read_input', on_input.bind(unit_sch3))
    unit_sch3.connect('error', on_error.bind(unit_sch3))
    unit_sch3.connect('line_changed', on_line_changed_sch3)


func _ready() -> void:
    var unit_sch3: = network.get_unit_instance(unit_name)
    var items = get_tree().get_nodes_in_group('bits_items_switch')
    for item in items:
        var meta = item.get_meta('bits')
        item.connect('toggled', on_toggled_switch.bind(meta, unit_sch3))
        item.disabled = true


func _exit_tree() -> void:
    if Engine.is_editor_hint(): return
    var unit_sch3 = network.get_unit_instance(unit_name)
    unit_sch3.disconnect('read_input', on_input.bind(unit_sch3))
    external_ctl = ProjectSettings.get_setting('application/config/Внешнее управление', false)
    unit_sch3.disconnect('error', on_error)
    unit_sch3.disconnect('line_changed', on_line_changed_sch3)


## Вызывается по сигналу "приняты данные чтения регистров входов".[br]
## [param base_addr] - Начальный адрес регистров.[br]
## [param data] - Массив значений прочитанных из регистров.[br]
func on_input(base_addr: int, data: Array, unit_sch3):
    if !is_inside_tree():
        return
    if (base_addr != base_input_address) or (data.size() < regs_input_count):
        return
    # "Для контроля состояния управляющего элемента подачи электроэнергии на приборы ..."
    # "Для контроля подачи электроэнергии на приборы ..."
    # Таблицы 3.13 и 3.14. Ecли значения соответствующих битов в этих
    # регистрах равны, то "исправно".
    var items = get_tree().get_nodes_in_group('bits_items_good')
    for i in data.size():
        regs_input[base_addr + i] = data[i]
    # Разрешить работу выключателей только когда прочитано их реальное состояние
    if not switches_sync:
        regs_holding[0] = regs_input[0]
        regs_holding[1] = regs_input[1]
        for i in 16:
            regs_holding[2] = tools.set_bit(regs_holding[0], i, not ((regs_holding[0] >> i) & 1))
            regs_holding[3] = tools.set_bit(regs_holding[1], i, not ((regs_holding[1] >> i) & 1))
        if not external_ctl:
            if regs_holding.has(0) and regs_holding.has(2):
                regs_holding[0] = regs_holding[0] | 1
                regs_holding[2] = regs_holding[2] & 0xffe
        switches_sync = true
        for item in get_tree().get_nodes_in_group('bits_items_switch'):
            var bits = item.get_meta('bits')
            var byte0: int = bits[0]  # Адрес регистра
            var bit0:  int = bits[1]  # Индекс бита
            var byte1: int = bits[2]  # Адрес регистра
            var bit1:  int = bits[3]  # Индекс бита
            var val0: bool = regs_holding[byte0] & (1 << bit0) > 0
            item.set_pressed_no_signal(val0)
            item.disabled = false
            regs_holding[byte1] = tools.set_bit(regs_holding[byte1], bit1, not val0)
        unit_sch3.push_write_holding(regs_holding)
    # Таблица 3.15, Регистр PowerState
    $margin/vbox/grid1/lbl_freq.text = '%d' % (regs_input[4] & 0x1ff)
    $margin/vbox/grid1/lbl_freq.self_modulate = Color.WHITE
    $margin/vbox/grid1/state_freq.texture \
        =    textures[STATE_VAL.GOOD] if regs_input[4] & (1 << 15) \
        else textures[STATE_VAL.ERROR]
    var cnt_good: int = 0
    for item in items:
        var bits: Array = item.get_meta('bits')
        var byte0: int = bits[0]  # Индекс байта в данных "состояния управляющего элемента"
        var bit0:  int = bits[1]  # Индекс бита в данных "состояния управляющего элемента"
        var byte1: int = bits[2]  # Индекс байта в данных "подачи электроэнергии на приборы"
        var bit1:  int = bits[3]  # Индекс бита в данных "подачи электроэнергии на приборы"
        var v = STATE_VAL.GOOD \
            if  (data[byte0] & (1 << bit0)) \
            ==  (data[byte1] & (1 << bit1)) \
            else STATE_VAL.ERROR
        cnt_good += int(v == STATE_VAL.GOOD)
        item.texture = textures[v]
    state = STATE_VAL.GOOD if cnt_good == items.size() else STATE_VAL.ERROR
