559 lines
27 KiB
GDScript
559 lines
27 KiB
GDScript
@tool
|
||
extends Panel
|
||
|
||
## Выполнение функций информационного ЭМС (ЭМС-ТГ)
|
||
|
||
#region Определяемое при использовании
|
||
## Индекс -> Имя устройства. Нужен для предсказуемого
|
||
## порядка отображения: верх->вниз или лево->право.
|
||
## Имя устройства должно быть взято из settings.gd
|
||
@export var index_to_unit_names: Array[StringName]
|
||
## Минимальная частота, МГЦ
|
||
@export var freq_min: float
|
||
## Максимальная частота, МГЦ
|
||
@export var freq_max: float
|
||
## Время достоверности кластера по умолчанию (если не указано в докладе)
|
||
@export var default_ttl: float
|
||
## Цвет диапазона если включен (разрешён)
|
||
@export_color_no_alpha var color_enabled: Color
|
||
## Цвет диапазона если выключен (запрещён)
|
||
@export_color_no_alpha var color_disabled: Color
|
||
## Прозрачность диапазон на шкале (диапазоны могут накладываться, что бы было видно наложения)
|
||
@export_range(0, 1.0) var color_alpha: float
|
||
## Цвет нечётных строк шкалы
|
||
@export_color_no_alpha var color_odd: Color
|
||
## Цвет чётных строк шкалы
|
||
@export_color_no_alpha var color_even: Color
|
||
## Индекс колонки в таблице для управления работой приёмников
|
||
@export var column_cmd_rx: int
|
||
## Индекс колонки в таблице для управления работой передатчиков
|
||
@export var column_cmd_tx: int
|
||
## Максимальное количество кластеров на одно устройство
|
||
@export var max_clasters_per_unit: int
|
||
## Сообщение пользователю
|
||
@export var msg_select_unit: String
|
||
## Сообщение пользователю
|
||
@export var msg_waiting_data: String
|
||
#endregion
|
||
|
||
#region Загружаемые ресурсы
|
||
@export_file('*.tscn') var table_headers: Array[String]
|
||
@export_file('*.tscn') var table_cells: Array[String]
|
||
@export_file('*.png') var image_file_rx: String
|
||
@export_file('*.png') var image_file_tx: String
|
||
@export var table_rows_hight: int
|
||
@export var table_column_widths: Array[int]
|
||
@export var table_header_widths: Array[int]
|
||
@export var table_header_text: Array[String]
|
||
#endregion
|
||
|
||
#region Заполняется автоматически при запуске
|
||
## Имя устройства -> Индекс устройства
|
||
var name_to_index: Dictionary[StringName, int]
|
||
## Имя устройства -> Шкала
|
||
var name_to_scale: Dictionary[StringName, GridContainer]
|
||
var table_cell_row: Array
|
||
## Образец параметров кластера
|
||
var ref_cls: = {'cname': '<имя>', 'ttl': 1.0, 'daa': 360.0, 'dap': 0.0,
|
||
'cfb': (freq_min + freq_max) / 2.0,
|
||
'wfb': freq_max - freq_min}
|
||
#endregion
|
||
|
||
#region Заполняется во время работы
|
||
var yems_info: Dictionary[StringName, Dictionary] ## Доклады
|
||
var yems_cmd: Dictionary[StringName, Dictionary] ## Команды
|
||
## Порядок следования кластеров в таблице.
|
||
## {имя_устройства: {имя_кластера: индекс_в_таблице}}
|
||
var cnames_table: Dictionary[StringName, Dictionary]
|
||
## Текущее выбранное оператором устройство на шкале
|
||
var selected_unit: Dictionary[StringName, bool]
|
||
## Состояния кластеров в соответствии с докладами и командами
|
||
#endregion
|
||
|
||
|
||
## Состояние кластера
|
||
enum ClasterStatus {
|
||
DELETED, ## Удалён (время ожидания обновления истекло)
|
||
RUNNING ## Работает (информация обновляется)
|
||
}
|
||
|
||
|
||
## Режим управления кластером
|
||
enum ClasterMode {
|
||
MANUAL, ## Ручной, управляется операторром
|
||
AUTO, ## Автоматический, управляется программой
|
||
IGNORE ## Игнорируемый, не управляется
|
||
}
|
||
|
||
|
||
## Состояние кластера. Значения должны соответствовать
|
||
## состояниям [b]six-state-button[/b]
|
||
enum ClasterResult {
|
||
WAITING = 0, ## Ожидание даннных
|
||
DONE = 1, ## Выполнено
|
||
MISSMATCH = 2 ## Несоответствие
|
||
}
|
||
|
||
|
||
func _ready() -> void:
|
||
# Инициализировать переменные
|
||
color_enabled.a = color_alpha
|
||
color_disabled.a = color_alpha
|
||
for i in index_to_unit_names.size():
|
||
var nm: = index_to_unit_names[i]
|
||
name_to_index[nm] = i
|
||
yems_info[nm] = {'mcls': []}
|
||
yems_cmd[nm] = {}
|
||
cnames_table[nm] = {}
|
||
selected_unit[nm] = false
|
||
var btns_scales = get_tree().get_nodes_in_group('кнопки-шкалы')
|
||
for node in btns_scales:
|
||
var nm: = node.get_meta('unit_name') as StringName
|
||
node.connect('toggled', _on_scale_selected.bind(nm))
|
||
var claster_scales = get_tree().get_nodes_in_group('шкалы-кластеров')
|
||
for node in claster_scales:
|
||
var nm: = node.get_meta('unit_name') as StringName
|
||
name_to_scale[nm] = node
|
||
|
||
# Расскрасить чётные-нечётные
|
||
for node in get_tree().get_nodes_in_group('нечётные'):
|
||
node.get_node('panel_rx').back_color = color_odd
|
||
node.get_node('panel_tx').back_color = color_odd
|
||
for node in get_tree().get_nodes_in_group('чётные'):
|
||
node.get_node('panel_rx').back_color = color_even
|
||
node.get_node('panel_tx').back_color = color_even
|
||
|
||
# Подключить обработчик для приёма данных
|
||
for nm in yems_info:
|
||
var yems_unit = network.get_unit_instance(nm)
|
||
yems_unit.connect('data_received', on_data_received.bind(nm))
|
||
|
||
# Настроить таблицу
|
||
var header_row: = []
|
||
for cell_scn in table_headers:
|
||
header_row.append(load(cell_scn))
|
||
for cell_scn in table_cells:
|
||
table_cell_row.append(load(cell_scn))
|
||
var ctable: = $'таблица/сетка'
|
||
ctable.set_header(header_row)
|
||
ctable.set_header_text(table_header_text)
|
||
ctable.set_header_property(column_cmd_rx, 'image_file', image_file_rx)
|
||
ctable.set_header_property(column_cmd_tx, 'image_file', image_file_tx)
|
||
ctable.set_headers_min_size(table_header_widths)
|
||
ctable.set_columns_min_size(table_column_widths)
|
||
ctable.set_rows_min_hight(table_rows_hight)
|
||
ctable.set_header_min_hight(table_rows_hight)
|
||
ctable.pin_header2()
|
||
|
||
# Описатель для отображения импульсного ЭМС:
|
||
# { идентификатор_входа: { 'node_path': путь_к_узлу, 'pname': название_устройства, 'dcls': { имя_кластера: {параметры_кластера}}}}
|
||
# Здесь поля txe и rxe - отвечают за конфигурацию:
|
||
# txe - вход связан с управлением передатчиком
|
||
# rxe - вход связан с управлением приёмника
|
||
##TODO: Должна быть возможность для изменения оператором
|
||
var imp_yems_descr = {
|
||
0: { 'pname': 'эмс-изделие7', # соответствие названия устройства идентификатору входа
|
||
'dcls': {'УИЗП1': {'wfb': 300.0, 'cfb': 4000, 'ttl': -1.0, 'txe': false, 'rxe': false}} },
|
||
1: { 'pname': 'эмс-изделие8', # соответствие названия устройства идентификатору входа
|
||
'dcls': {'УИЗП2': {'wfb': 200.0, 'cfb': 5000, 'ttl': -1.0, 'txe': false, 'rxe': false}} } }
|
||
signaller.connect('yems_input_changed', _on_yems_input_changed.bind(imp_yems_descr))
|
||
for id in imp_yems_descr:
|
||
var descr = imp_yems_descr[id]
|
||
var unit_info = yems_info[descr.pname]
|
||
for cname in descr.dcls:
|
||
var incls = descr.dcls[cname]
|
||
var cls = {'cname': cname, 'status': ClasterStatus.RUNNING}
|
||
cls.merge(incls)
|
||
unit_info.mcls.append(cls)
|
||
call_deferred('_on_defer_ready', imp_yems_descr)
|
||
|
||
|
||
func _on_defer_ready(imp_yems_descr: Dictionary):
|
||
for id in imp_yems_descr:
|
||
var descr = imp_yems_descr[id]
|
||
var unit_info = yems_info[descr.pname]
|
||
on_data_received(unit_info, descr.pname)
|
||
|
||
|
||
## Вызывается при изменении состояния входа ЭМС.[br]
|
||
## [param id] - идентификатор входа ЭМС[br]
|
||
## [param state] - новое состояние входа ЭМС[br]
|
||
## [param imp_yems_sescr] - описатель входов ЭМС[br]
|
||
func _on_yems_input_changed(id: int, state: int, imp_yems_descr):
|
||
# Проверить, есть ли в описателе этот вход
|
||
if not imp_yems_descr.has(id):
|
||
return
|
||
var descr = imp_yems_descr[id]
|
||
# Проверить, есть ли такое устройство
|
||
if not yems_info.has(descr.pname):
|
||
return
|
||
# Получить доклад устройства
|
||
var unit_info = yems_info[descr.pname]
|
||
# Вход один, но кластеров может быть несколько,
|
||
# поэтому нужно пробежать по всем кластерам
|
||
for cls in unit_info.mcls:
|
||
var incls = descr.dcls[cls.cname]
|
||
# Выставить в докладе разрешение работы приёмника
|
||
# или передатчика соответственно состоянию входа
|
||
if incls.rxe:
|
||
cls.rxe = true if state == 1 else false
|
||
if incls.txe:
|
||
cls.txe = true if state == 1 else false
|
||
# Выполнить то же, что и для приёма данных, поэтому:
|
||
on_data_received(unit_info, descr.pname)
|
||
|
||
|
||
## Вызывается при выборе оператором устройства на шкале
|
||
## [param val] - состояние кнопки переключателя устройств
|
||
## [param nm] - имя выбранного устройства
|
||
func _on_scale_selected(val: bool, nm: StringName):
|
||
selected_unit[nm] = val
|
||
if not val:
|
||
$'таблица/сетка'.clear_rows()
|
||
cnames_table[nm].clear()
|
||
$label_msg.text = msg_select_unit
|
||
$label_msg.show()
|
||
return
|
||
var unit_info = yems_info[nm]
|
||
if not unit_info.mcls: # проверить, есть ли данные
|
||
$label_msg.text = msg_waiting_data
|
||
$label_msg.show()
|
||
return # данных ещё нет, ничего не далать
|
||
else:
|
||
$label_msg.hide()
|
||
var ctable = $'таблица/сетка'
|
||
var indexes: = cnames_table[nm]
|
||
update_cvi(nm)
|
||
var new_indexes = update_indexes(unit_info, indexes)
|
||
update_table(ctable, new_indexes)
|
||
map_yems_info_to_table(unit_info, ctable, indexes)
|
||
connect_cmd_handlers(nm, ctable, new_indexes)
|
||
on_data_received(unit_info, nm)
|
||
|
||
|
||
## Обновляет результат выполнения команды оператора
|
||
## [param nm] - имя устройства
|
||
func update_cvi(nm: StringName):
|
||
var unit_info = yems_info[nm]
|
||
var unit_cmd = yems_cmd[nm]
|
||
for cls_info in unit_info.mcls:
|
||
var result_rxe: = ClasterResult.WAITING
|
||
var result_txe: = ClasterResult.WAITING
|
||
if unit_cmd.has(cls_info.cname):
|
||
var cls_cmd = unit_cmd[cls_info.cname]
|
||
if cls_info.status == ClasterStatus.RUNNING:
|
||
if cls_cmd.has('rxe'):
|
||
result_rxe = ClasterResult.DONE \
|
||
if (cls_info.rxe == cls_cmd.rxe) \
|
||
else ClasterResult.MISSMATCH
|
||
else:
|
||
result_rxe = ClasterResult.DONE
|
||
if cls_cmd.has('txe'):
|
||
result_txe = ClasterResult.DONE \
|
||
if (cls_info.txe == cls_cmd.txe) \
|
||
else ClasterResult.MISSMATCH
|
||
else:
|
||
result_txe = ClasterResult.DONE
|
||
else:
|
||
result_rxe = ClasterResult.DONE
|
||
result_txe = ClasterResult.DONE
|
||
cls_info['result_rxe'] = result_rxe
|
||
cls_info['result_txe'] = result_txe
|
||
|
||
|
||
## Подключает обработчики команд к таблице кластеров
|
||
## [param nm] - имя устройства
|
||
## [param new_indexes] - новые индексы рядов таблицы
|
||
## [param cls_table] - таблица для отображения кластеров
|
||
func connect_cmd_handlers(nm: StringName, ctable: GDTable, new_indexes: Array):
|
||
for icls in new_indexes:
|
||
var i_row = icls[0]
|
||
# Имя устройства и имя кластера
|
||
var cls = icls[1]
|
||
# Подключить обработчки разрешения передатчиков
|
||
var node = ctable.get_node2(column_cmd_tx, i_row)
|
||
node.connect_check('toggled', _on_user_cmd.bind(nm, cls.cname, 'txe'))
|
||
# Подключить обработчки разрешения приёмников
|
||
node = ctable.get_node2(column_cmd_rx, i_row)
|
||
node.connect_check('toggled', _on_user_cmd.bind(nm, cls.cname, 'rxe'))
|
||
|
||
|
||
## Вызывается при переключении оператором разрешений для кластера
|
||
func _on_user_cmd(val: bool, nm: StringName, cname: String, param_name: StringName):
|
||
var unit_cmd = yems_cmd[nm]
|
||
if cname in unit_cmd:
|
||
unit_cmd[cname][param_name] = val
|
||
else:
|
||
unit_cmd[cname] = {param_name: val}
|
||
|
||
|
||
## Обрабатывает событие [b]data_received[/b]
|
||
## [param data] - данные принятые от устройства
|
||
## [param nm] - имя устройства
|
||
func on_data_received(data: Dictionary, nm: StringName):
|
||
set_timeouts(nm, data)
|
||
var unit_info: = yems_info[nm]
|
||
unit_info_merge(unit_info, data)
|
||
if not unit_info:
|
||
# для устройства нет данных
|
||
return
|
||
var ctable: GDTable = $'таблица/сетка'
|
||
var cscale: = name_to_scale[nm] as GridContainer # Шкала устройства
|
||
var indexes: = cnames_table[nm] # Индексы строк устройства в таблице
|
||
update_cvi(nm)
|
||
# Если текущее устройство отображается в таблице,
|
||
# обновить таблицу новыми данными
|
||
if selected_unit[nm]:
|
||
var new_indexes = update_indexes(unit_info, indexes)
|
||
update_table(ctable, new_indexes)
|
||
map_yems_info_to_table(unit_info, ctable, indexes)
|
||
connect_cmd_handlers(nm, ctable, new_indexes)
|
||
map_yems_info_to_scale(unit_info, cscale)
|
||
var inters: = find_inters()
|
||
reset_matrix()
|
||
map_inters_to_matrix(inters, $'матрица')
|
||
cmd_to_unit(nm)
|
||
|
||
|
||
## Создаёт новые ряды в таблице
|
||
func update_table(ctable: GDTable, new_indexes: Array):
|
||
# Добавить новые ряды. Два прохода нужно потому,
|
||
# что порядок элементов в словаре не определён.
|
||
for icls in new_indexes:
|
||
ctable.add_row(table_cell_row)
|
||
# Заполнить новые ряды данными
|
||
for icls in new_indexes:
|
||
var i_row = icls[0]
|
||
var cls = icls[1]
|
||
# Установить высоту ряда (можно пропустить, если нужная высота будет выставлена по умолчанию)
|
||
ctable.set_row_min_hight(i_row, table_rows_hight)
|
||
# Установить новый текст
|
||
ctable.set_node_property(column_cmd_rx, i_row, 'pressed', cls.rxe)
|
||
ctable.set_node_property(column_cmd_tx, i_row, 'pressed', cls.txe)
|
||
ctable.set_node_property(column_cmd_rx, i_row, 'state', cls.result_rxe)
|
||
ctable.set_node_property(column_cmd_tx, i_row, 'state', cls.result_txe)
|
||
|
||
|
||
## Обновляет индексы строк в таблице
|
||
func update_indexes(unit_info: Dictionary, indexes: Dictionary) -> Array:
|
||
var new_indexes: = []
|
||
for cls in unit_info.mcls:
|
||
# Проверить, есть ли уже такой индекс
|
||
if indexes.has(cls.cname):
|
||
continue
|
||
# Записать индекс нового ряда (связать с именем кластера)
|
||
var i_row = indexes.size()
|
||
indexes[cls.cname] = i_row
|
||
new_indexes.append([i_row, cls])
|
||
return new_indexes
|
||
|
||
|
||
## Объединяет список кластеров принятых от устройства со списком сохранённым
|
||
## [param unit_info] - доклад ранее полученный
|
||
## [param data_rx] - доклад принятый от устройства
|
||
func unit_info_merge(unit_info: Dictionary, data_rx: Dictionary):
|
||
var drx: = {}
|
||
for cls_rx in data_rx.mcls:
|
||
ref_cls.cname = 'неопр.№%d' % (drx.size() + 1)
|
||
cls_rx.merge(ref_cls, false) # Объеденить без перезаписи
|
||
drx[cls_rx.cname] = cls_rx
|
||
var dinfo: = {}
|
||
for cls in unit_info.mcls:
|
||
dinfo[cls.cname] = cls
|
||
dinfo.merge(drx, true)
|
||
unit_info.merge(data_rx, true)
|
||
unit_info.mcls = dinfo.values()
|
||
|
||
|
||
## Отправляет команду в сетевой стек
|
||
## [param nm] - имя устройства
|
||
func cmd_to_unit(nm: StringName):
|
||
var unit_yemstg: = network.get_unit_instance(nm)
|
||
# Проверить, что сетевой стек отправил предидущую команду
|
||
if unit_yemstg.cmd_state != unit.Unit.CmdState.DONE:
|
||
return
|
||
# Сформировать команду из докладов (данных принятых из сети от устройств)
|
||
var unit_info_copy = yems_info[nm].duplicate(true)
|
||
var unit_cmd = yems_cmd[nm]
|
||
unit_info_copy.mtype = 'cmd'
|
||
# Выбрать только работающие кластеры (время которых ещё не истекло)
|
||
var mcls = unit_info_copy.mcls.filter(func(c): return c.status == ClasterStatus.RUNNING)
|
||
for cls_info in mcls:
|
||
# Если для этого кластера есть команда оператора ...
|
||
if unit_cmd.has(cls_info.cname):
|
||
# ... переписать значениями из команды оператора
|
||
var cmd_cls = unit_cmd[cls_info.cname]
|
||
cls_info.rxe = unit_cmd.get('rxe', cls_info.rxe)
|
||
cls_info.txe = unit_cmd.get('txe', cls_info.txe)
|
||
# Передать команду сетевому стеку
|
||
unit_yemstg.cmd.merge(unit_info_copy, true)
|
||
unit_yemstg.cmd_state = unit.Unit.CmdState.SEND
|
||
|
||
|
||
## Запускает таймер достоверности кластера
|
||
## [param nm] - имя устройства
|
||
## [param unit_info] - доклады устройства
|
||
func set_timeouts(nm: StringName, unit_info: Dictionary):
|
||
for cls in unit_info.mcls:
|
||
var timer_name = NodePath('%s_%s' % [nm, cls.cname])
|
||
var timer = get_node_or_null(timer_name)
|
||
cls['status'] = ClasterStatus.RUNNING
|
||
if cls.ttl < 0.0:
|
||
continue
|
||
if not timer:
|
||
timer = Timer.new()
|
||
timer.name = timer_name
|
||
timer.one_shot = true
|
||
timer.connect('timeout', _on_timeout_ttl.bind(nm, cls.cname))
|
||
add_child(timer)
|
||
timer.start(cls.ttl)
|
||
|
||
|
||
## Обработчик истечения времени достоверности кластера
|
||
## [param nm] - имя устройства
|
||
## [param cname] - имя кластера
|
||
func _on_timeout_ttl(nm: StringName, cname: StringName):
|
||
var timer_name = NodePath('%s_%s' % [nm, cname])
|
||
var timer = get_node_or_null(timer_name)
|
||
if timer:
|
||
remove_child(timer)
|
||
timer.queue_free()
|
||
var unit_info = yems_info[nm]
|
||
for cls in unit_info.mcls:
|
||
if cname == cls.cname:
|
||
cls.status = ClasterStatus.DELETED
|
||
cls.result_rxe = ClasterResult.WAITING
|
||
cls.result_txe = ClasterResult.WAITING
|
||
var ctable: GDTable = $'таблица/сетка'
|
||
var indexes: = cnames_table[nm]
|
||
if indexes:
|
||
map_yems_info_to_table(unit_info, ctable, indexes)
|
||
var claster_scale: = name_to_scale[nm] as GridContainer
|
||
map_yems_info_to_scale(unit_info, claster_scale)
|
||
reset_matrix()
|
||
|
||
|
||
## Сбрасывает элементы матрицы в исходное состояние
|
||
func reset_matrix():
|
||
var nodes = get_tree().get_nodes_in_group('матрица-к')
|
||
for node in nodes:
|
||
node.button_pressed = false
|
||
|
||
|
||
## Отображает пересечения на матрице
|
||
## [param inters] - пересечения
|
||
## [param matrix] - матрица
|
||
func map_inters_to_matrix(inters: Array, matrix: GridContainer) -> void:
|
||
var nodes = matrix.get_children()
|
||
for item in inters:
|
||
var tx_unit_name = item[0]
|
||
var rx_unit_name = item[1]
|
||
var i_col = 1 + name_to_index[tx_unit_name] # (1 +) - одна колонка для значков приёмников
|
||
var i_row = 1 + name_to_index[rx_unit_name] # (1 +) - один ряд для значков передатчиков
|
||
var idx = i_col + i_row * matrix.columns # последовательный индекс в GridContainer
|
||
var node = nodes[idx] # Искомый элемент
|
||
node.button_pressed = true
|
||
|
||
|
||
## Отображает кластеры в таблицу
|
||
## [param unit_info] - доклад от устройства
|
||
## [param ctable] - таблица
|
||
## [param indexes] - индексы в таблице
|
||
func map_yems_info_to_table(unit_info: Dictionary, ctable: GDTable, indexes: Dictionary):
|
||
# Обойти все кластеры устройства
|
||
for cls in unit_info.mcls:
|
||
# Строки для заполнения ряда
|
||
var status_str: = 'Активен' if cls.status == ClasterStatus.RUNNING else 'Удалён'
|
||
var row_text: = [cls.cname, status_str, '%.1f' % cls.ttl, '%.1f' % cls.daa, '%.1f' % cls.dap, '%.1f-%.1f' % [cls.cfb - cls.wfb / 2.0, cls.cfb + cls.wfb / 2.0]]
|
||
# Получить индекс строки кластера в таблице
|
||
var i_row = indexes[cls.cname]
|
||
# Установить новый текст
|
||
ctable.set_row_text(i_row, row_text)
|
||
# Результат выполнения
|
||
ctable.set_node_property(column_cmd_rx, i_row, 'state', cls.result_rxe)
|
||
ctable.set_node_property(column_cmd_tx, i_row, 'state', cls.result_txe)
|
||
|
||
|
||
## Находит пересечения диапазонов всех устройств и кластеров
|
||
func find_inters() -> Array:
|
||
var result = []
|
||
var all_clusters = []
|
||
# Собираем все кластеры из всех устройств в один массив
|
||
for nm in yems_info:
|
||
# nm - имя устройства
|
||
var unit_data = yems_info[nm]
|
||
if not unit_data: continue
|
||
var clasters = unit_data.mcls.filter \
|
||
(func(c): return c.status == ClasterStatus.RUNNING)
|
||
for cls in clasters:
|
||
# cls - один кластер
|
||
var cfb = float(cls.cfb)
|
||
var wfb = float(cls.wfb) / 2.0
|
||
# расчёт границ частотных диапазонов
|
||
var freq_min1 = cfb - wfb
|
||
var freq_max1 = cfb + wfb
|
||
all_clusters.append([nm, cls.cname, freq_min1, freq_max1, cls.txe, cls.rxe])
|
||
|
||
for i in all_clusters.size():
|
||
var cl1 = all_clusters[i]
|
||
var dev1 = cl1[0]
|
||
var name1 = cl1[1]
|
||
var min1 = cl1[2]
|
||
var max1 = cl1[3]
|
||
var txe1 = cl1[4]
|
||
var rxe1 = cl1[5]
|
||
# Сравниваем текущий кластер со всеми последующими,
|
||
# чтобы избежать дублей и самопересечений
|
||
for j in all_clusters.size():
|
||
var cl2 = all_clusters[j]
|
||
var dev2 = cl2[0]
|
||
var name2 = cl2[1]
|
||
var min2 = cl2[2]
|
||
var max2 = cl2[3]
|
||
var txe2 = cl2[4]
|
||
var rxe2 = cl2[5]
|
||
# Если нет пары TR(RT) то пропускаем эту комбинацию
|
||
if not ((txe1 and rxe2) or (rxe1 and txe2)):
|
||
continue
|
||
# Проверка на пересечение диапазонов, пересечение
|
||
# есть если начало одного диапазона меньше конца другого
|
||
if max1 > min2 and max2 > min1:
|
||
#Вычисляем точные границы пересечения
|
||
var inter_start = max(min1, min2)
|
||
var inter_end = min(max1, max2)
|
||
# Формируем результат в требуемом формате
|
||
result.append([dev1, dev2, name1, name2, inter_start, inter_end])
|
||
return result
|
||
|
||
|
||
## Отображает кластеры на шкале
|
||
## [param unit_info] - доклад устройства
|
||
## [param cscale] - шкала кластера
|
||
func map_yems_info_to_scale(unit_info: Dictionary, cscale: GridContainer):
|
||
# Обойти все кластеры устройства
|
||
for cls in unit_info.mcls:
|
||
# Преобразовать диапазоны частот в диапазоны отображения
|
||
var c: = remap(cls.cfb, freq_min, freq_max, 0.0, 1.0)
|
||
var w: = remap(cls.wfb, freq_min * 0.5, freq_max * 0.5, 0.0, 0.5)
|
||
# Чтобы диапазон всегда был виден, даже если он слишком узкий
|
||
w = clampf(w, 0.005, 0.5)
|
||
# Отсечь значения по границам
|
||
var l: = clampf(c - w, 0.0, 1.0)
|
||
var h: = clampf(c + w, 0.0, 1.0)
|
||
# Получить шкалу приёмника
|
||
var scale_rx: = cscale.get_node('panel_rx')
|
||
# Получить шкалу передатчика
|
||
var scale_tx: = cscale.get_node('panel_tx')
|
||
# Диапазон работает (информация обновляется)
|
||
if cls.status == ClasterStatus.RUNNING:
|
||
# Отобразить диапазон приёмника, в соответствии с разрешением
|
||
var color_rxe: = color_enabled if cls.rxe else color_disabled
|
||
scale_rx.set_range(cls.cname, l, h, color_rxe)
|
||
# Отобразить диапазон передатчика, в соответствии с разрешением
|
||
var color_txe: = color_enabled if cls.txe else color_disabled
|
||
scale_tx.set_range(cls.cname, l, h, color_txe)
|
||
# Диапазон удалён (информация не обновляется)
|
||
elif cls.status == ClasterStatus.DELETED:
|
||
scale_rx.del_range(cls.cname)
|
||
scale_tx.del_range(cls.cname)
|