524 lines
22 KiB
GDScript
524 lines
22 KiB
GDScript
class_name work extends Panel
|
||
|
||
## Основной скрипт для режима "Работа".
|
||
|
||
@export var Bip: PackedScene ## Индикатор излучения цели.
|
||
@export var col_grey: Color ## Второй цвет для фона.
|
||
@export var col_red: Color ## Первый цвет для фона.
|
||
@export var drag_scale_band: float ## Масштаб изменения параметра от движения мыши для строба по радиальному размеру.
|
||
@export var drag_scale_width: float ## Масштаб изменения параметра от движения мыши для строба по угловому размеру.
|
||
@export var freq_high: float ## Верхняя рабочая частота, МГЦ.
|
||
@export var freq_low: float ## Нижняя рабочая частота, МГЦ.
|
||
@export var radius_center: Vector2 ## Координаты центра диаграммы пеленг-частота.
|
||
@export var radius_internal: float ## Внутренний радиус диаграммы пеленг-частота.
|
||
@export var radius_outter: float ## Внешний радиус диаграммы пеленг-частота.
|
||
@export var strob_max_band: float ## Максимальная ширина строба по углу.
|
||
@export var strob_max_width: float ## Максимальная ширина строба по углу.
|
||
@export var strob_min_band: float ## Минимальная ширина строба по частоте.
|
||
@export var strob_min_width: float ## Минимальная ширина строба по углу.
|
||
@export var window_0_id: int ## Идетификатор окна. Присваивается godot от 0 до количества созданных окон.
|
||
|
||
|
||
var drag_button: MouseButton ## Кнопка мыши нажатая при перетаскивании.
|
||
var drag_fsm: DragFSM = DragFSM.IDLE ## Текущее состояние перетаскивания.
|
||
var btn_select: BtnSel = BtnSel.THREAT ## Текущее состояние кнопки выбора цели.
|
||
var drag_pos_begin: Vector2 ## Точка начала перетаскивания.
|
||
var max_zoom: int ## Максимальный уровень детализации.
|
||
var min_zoom: int ## Минимальный уровень детализации.
|
||
var shift_pressed: bool ## Кнопка SHIFT нажата.
|
||
var strob_band_begin: float ## Ширина строба в момент начала перетаскивания.
|
||
var strob_dir_begin: float ## Направление антенны в момент начала перетаскивания.
|
||
var strob_width_begin: float ## Ширина строба по углу в момент начала перетаскивания.
|
||
var view_mode: MapViewMode ## Режим отображения карты
|
||
|
||
|
||
signal drag_begin ## Вызывается в момент начала перетаскивания.
|
||
signal drag_continue ## Вызывается во время продолжения перетаскивания.
|
||
signal drag_end ## Вызывается в момент завершения перетаскивания.
|
||
|
||
|
||
## Размеры карты для разных режимов РТО и РЛС соответственно
|
||
const map_sizes = [
|
||
Rect2i(360, 360, 400, 400),
|
||
Rect2i(82, 82, 955, 955)]
|
||
var fs_band = prd.FS_BAND_RTO ## Текущий режим блоков ФС по частоте
|
||
var fs_selected: Dictionary ## Словарь выбранных секторов
|
||
var fs_active: Dictionary ## Словарь секторов назначенных на подавление
|
||
|
||
## Состояние перетаскивания.
|
||
enum DragFSM {
|
||
DRAG, ## В процессе.
|
||
IDLE, ## Не активно, ожидание.
|
||
OFF ## Выключено.
|
||
}
|
||
|
||
## Режим выбора
|
||
enum BtnSel {
|
||
SECTOR, ## Выбор сектора.
|
||
THREAT, ## Выбор цели.
|
||
FS ## Выбор стробом.
|
||
}
|
||
|
||
|
||
## Режим отображения карты
|
||
enum MapViewMode {
|
||
RTO = 0, ## Режим приоритет РТО (малая карта, широкие секторы)
|
||
RLS = 1 ## Режим приоритет РЛС (большая карта, узкие секторы)
|
||
}
|
||
|
||
|
||
const VAL_TO_EVENT = {
|
||
false: 'manual_control', ## Выбор целей и установка помех вручную.
|
||
true: 'full_auto' } ## Выбор целей и установка помех автоматически.
|
||
|
||
|
||
## Обработчик сигнала изменения списка целей.
|
||
func on_threats_resized(threats: Dictionary): $count_all_pad/count_all.text = '%02d' % threats.size()
|
||
|
||
|
||
## Обработчик выбора цели.
|
||
func on_threat_selected(th, ecm_btns): on_rto_threat_sel(th.id, ecm_btns)
|
||
|
||
|
||
## Встроенный обратный вызов.
|
||
func on_enter_tree() -> void: call_deferred('on_button_strobe_pressed')
|
||
|
||
|
||
func on_btn_activate(val: bool): signaller.emit_signal('emit_changed', val)
|
||
|
||
|
||
## Обработчик нажатия на кнопку вид.
|
||
func on_button_view_toggled(toggled: bool):
|
||
signaller.emit_signal('map_mode_changed', int(toggled))
|
||
fs_band = prd.FS_BAND_RLS if toggled else prd.FS_BAND_RTO
|
||
var s = map_sizes[int(toggled)]
|
||
$tilemap.size = s.size
|
||
$tilemap.position = s.position
|
||
view_mode = MapViewMode.RLS if toggled else MapViewMode.RTO
|
||
|
||
|
||
## Встроенный обратный вызов.
|
||
func _enter_tree() -> void:
|
||
var timer1 = Timer.new()
|
||
timer1.name = 'timer1'
|
||
add_child(timer1)
|
||
timer1.one_shot = true
|
||
timer1.connect('timeout', Callable(self, 'on_enter_tree'))
|
||
timer1.start(1.0)
|
||
|
||
|
||
## Встроенный обратный вызов.
|
||
func _exit_tree() -> void:
|
||
var timer1 = get_node('timer1')
|
||
remove_child(timer1)
|
||
timer1.queue_free()
|
||
|
||
|
||
## Производит инициализацию.
|
||
func _ready():
|
||
min_zoom = ProjectSettings.get_setting('application/config/map_minimum_zoom', 5)
|
||
max_zoom = ProjectSettings.get_setting('application/config/map_maximum_zoom', 19)
|
||
drag_fsm = DragFSM.OFF
|
||
var btns_select: Array = get_tree().get_nodes_in_group('группа-режим-выбора')
|
||
btns_select.any(func(btn): btn.button_connect('pressed', Callable(self, 'on_button_select').bind(btn, btns_select)))
|
||
var nodes: Array = get_tree().get_nodes_in_group('группа-режим-помехи')
|
||
nodes.any(func(btn): btn.button_connect('pressed', Callable(self, 'on_button_pressed').bind(btn, nodes)))
|
||
threats.connect('threat_new', Callable(self, 'on_threat_new'))
|
||
threats.connect('threat_lost', Callable(self, 'on_threat_lost'))
|
||
threats.connect('threats_resized', Callable(self, 'on_threats_resized'))
|
||
threats.connect('threat_update', Callable(self, 'on_threat_update'))
|
||
signaller.connect('rto_threat_sel', Callable(self, 'on_rto_threat_sel').bind(nodes))
|
||
signaller.connect('bip_pressed', Callable(self, 'on_bip_pressed').bind(nodes))
|
||
signaller.connect('rto_threat_unsel', Callable(self, 'on_rto_threat_unsel').bind(nodes))
|
||
signaller.connect('threat_selected', Callable(self, 'on_threat_selected').bind(nodes))
|
||
signaller.connect('interfer_selected', Callable(self, 'on_interfer_selected').bind(nodes))
|
||
signaller.connect('interfer_new', Callable(self, 'on_interfer_new'))
|
||
signaller.connect('interfer_off_all', Callable(self, 'on_interfer_off_all'))
|
||
signaller.connect('interfer_prinuditelno', Callable(self, 'on_interfer_prinuditelno').bind(nodes))
|
||
signaller.connect('fs_selected', Callable(self, 'on_fs_selected'))
|
||
signaller.connect('fs_update_color', Callable(self, 'on_fs_update_color'))
|
||
interfer.connect('interfer_update', Callable(self, 'on_interfer_new'))
|
||
signaller.connect('interfer_prinuditelno', Callable(self, 'on_chk_interfer_prinuditelno'))
|
||
signaller.connect('fs_update_state', Callable(self, 'on_fs_update_state'))
|
||
signaller.connect('map_user_panning', Callable(self, 'on_user_panning'))
|
||
signaller.connect('update_coordinates_map', Callable(self, 'on_update_coordinates_label'))
|
||
signaller.connect('update_scale_map', Callable(self, 'on_update_scale_label'))
|
||
on_btn_center_toggled(true)
|
||
on_btn_activate_toggled(false)
|
||
|
||
|
||
func on_user_panning():
|
||
$btn_center.button_pressed = false
|
||
|
||
|
||
func on_chk_interfer_prinuditelno(_interfer_sectors):
|
||
$chk_auto.set_pressed_no_signal(false)
|
||
on_value_changed(false)
|
||
|
||
|
||
func on_value_changed(val: bool):
|
||
signaller.emit_signal(VAL_TO_EVENT[val])
|
||
|
||
|
||
func get_all_nodes(root):
|
||
var nodes = []
|
||
nodes.append(root)
|
||
for child in root.get_children():
|
||
nodes.append_array(get_all_nodes(child))
|
||
return nodes
|
||
|
||
|
||
## Производит отображение цели на диаграмму.
|
||
func map_threat_to_bip(th: threats.Threat, bip: Control):
|
||
var freq = th.freq
|
||
var sector_key = null
|
||
var sector_width = 50.0
|
||
for key in prd.FS_FREQ:
|
||
var freq_range = prd.FS_FREQ[key]
|
||
var _num_values = (freq_range[1] - freq_range[0]) / sector_width
|
||
if freq_range[0] <= freq and freq <= freq_range[1]:
|
||
sector_key = key
|
||
break
|
||
if sector_key!= null:
|
||
radius_internal = fs_band[sector_key][0]
|
||
radius_outter = fs_band[sector_key][1]
|
||
freq_low = prd.FS_FREQ[sector_key][0]
|
||
freq_high = prd.FS_FREQ[sector_key][1]
|
||
bip.position = tools.pos_calc(
|
||
th.freq,
|
||
th.aoa,
|
||
radius_center,
|
||
radius_internal,
|
||
radius_outter,
|
||
freq_low,
|
||
freq_high) - bip.size / 2
|
||
|
||
|
||
## Обработчик сигнала обновления цели.
|
||
func on_threat_update(th):
|
||
var bip = get_node('%d' % th.id)
|
||
bip.update(th.tick_rx)
|
||
map_threat_to_bip(th, bip)
|
||
|
||
|
||
## Обработчик выбора помехи для цели.
|
||
func on_interfer_new(ecm):
|
||
if interfer.thrs.has(ecm.ispp):
|
||
var th_id = interfer.thrs[ecm.ispp]
|
||
var bip = get_node('%d' % th_id)
|
||
if bip != null:
|
||
if ecm.krp!= interfer.INTERFER_OFF:
|
||
bip.set_track_visible(true)
|
||
if ecm.svk == 1:
|
||
bip.set_ecm_visible(true)
|
||
else: bip.set_ecm_visible(false)
|
||
else:
|
||
bip.set_ecm_visible(false)
|
||
bip.set_track_visible(false)
|
||
else:
|
||
interfer.thrs.erase(ecm.ispp)
|
||
set_fs_color(ecm)
|
||
update_fs_colors()
|
||
set_fs_color(ecm)
|
||
|
||
|
||
## Обработчик выключения всех помех.
|
||
func on_interfer_off_all():
|
||
var bips: Array = get_children().filter(func(item): return item.name.is_valid_int())
|
||
for bip in bips:
|
||
bip.set_ecm_visible(false)
|
||
bip.set_track_visible(false)
|
||
fs_active.clear()
|
||
update_fs_colors()
|
||
|
||
|
||
func on_interfer_off(ecm_id):
|
||
fs_active.erase(ecm_id)
|
||
fs_selected.erase(ecm_id)
|
||
fs_active.clear()
|
||
update_fs_colors()
|
||
|
||
|
||
## Обработчик сигнала обнаружения цели.
|
||
func on_threat_new(th) -> void:
|
||
var bip = Bip.instantiate()
|
||
bip.name = '%d' % th.id
|
||
map_threat_to_bip(th, bip)
|
||
add_child(bip)
|
||
|
||
|
||
## Обработчик сигнала потери цели.
|
||
func on_threat_lost(th) -> void:
|
||
var bip = get_node('%d' % th.id)
|
||
remove_child(bip)
|
||
bip.free()
|
||
|
||
|
||
## обработчик нажатия на цель на экране.
|
||
func on_bip_pressed(th_id, _ecm_btns):
|
||
if btn_select == BtnSel.THREAT:
|
||
signaller.emit_signal('rto_threat_sel', th_id)
|
||
|
||
|
||
## Обработчик выбора РТО цели.
|
||
func on_rto_threat_sel(th_id, ecm_btns):
|
||
var bips: Array = get_children().filter(func(item): return item.name.is_valid_int())
|
||
for item in bips:
|
||
if th_id == item.name.to_int():
|
||
item.set_sel_visible(true)
|
||
unsel_fs_all(false)
|
||
else:
|
||
item.set_sel_visible(false)
|
||
for btn in ecm_btns:
|
||
set_btns_disabled(false, btn)
|
||
|
||
|
||
## Обработчик снятия выбора РТО цели.
|
||
func on_rto_threat_unsel(th_id, _ecm_btns):
|
||
var bips: Array = get_children().filter(func(item): return item.name.is_valid_int())
|
||
for item in bips:
|
||
if th_id == item.name.to_int():
|
||
item.set_sel_visible(false)
|
||
break
|
||
set_ecm_btns_state(true)
|
||
|
||
|
||
## Обработчик нажатия кнопки "Строб". Управляет отображением строба.
|
||
func on_button_strobe_pressed() -> void:
|
||
drag_fsm = DragFSM.IDLE if $btns_select/btn_strob.is_pressed() else DragFSM.OFF
|
||
$canvas.set_strob_visible(drag_fsm == DragFSM.IDLE)
|
||
|
||
|
||
## Поведение радиокнопок в группе. Управляет состоянием кнопок выбора целей.
|
||
func on_button_select(btn_this, btn_others) -> void:
|
||
for btn_other in btn_others: btn_other.set_pressed(btn_this == btn_other)
|
||
btn_select = btn_this.get_meta('state')
|
||
on_button_strobe_pressed()
|
||
signaller.emit_signal('rto_threat_unsel_all')
|
||
unsel_fs_all(true)
|
||
set_ecm_btns_state(true)
|
||
|
||
|
||
## Поведение радиокнопок в группе. Управляет состоянием кнопок в радиогруппе.
|
||
func on_button_pressed(btn_this, btn_others) -> void:
|
||
$chk_auto.set_pressed_no_signal(false)
|
||
on_value_changed(false)
|
||
var fs_arr: Array = []
|
||
for btn_other in btn_others: btn_other.set_pressed(btn_this == btn_other)
|
||
var interfer_name: String = btn_this.get_meta('rfi_name')
|
||
for key in fs_selected.keys():
|
||
fs_arr.append(prd.get_fs_param(key))
|
||
signaller.emit_signal('interfer_init', interfer_name, fs_arr)
|
||
|
||
|
||
## Управляет состоянием кнопок в радиогруппе при выборе помехи.
|
||
func on_interfer_selected(ecm, btns) -> void:
|
||
var ecm_name: String
|
||
if ecm != null:
|
||
ecm_name = ecm.krp
|
||
else:
|
||
ecm_name = interfer.INTERFER_OFF
|
||
for btn in btns:
|
||
set_btns_disabled(false, btn)
|
||
btn.get_meta('rfi_name')
|
||
if ecm_name == btn.get_meta('rfi_name'):
|
||
btn.set_pressed(true)
|
||
else:
|
||
btn.set_pressed(false)
|
||
|
||
|
||
## Возвращает ширину строба по направлению.
|
||
func get_strob_width(pos: Vector2, prev: float) -> float:
|
||
var strob_dir_width: float = prev + (drag_pos_begin.y - pos.y) * drag_scale_width
|
||
strob_dir_width = tools.clip_value(strob_dir_width, strob_min_width, strob_max_width)
|
||
return strob_dir_width
|
||
|
||
|
||
## Возвращает ширину строба по частоте.
|
||
func get_strob_band(pos: Vector2, prev: float) -> float:
|
||
var strob_dir_band: float = prev - (drag_pos_begin.y - pos.y) * drag_scale_band
|
||
strob_dir_band = tools.clip_value(strob_dir_band, strob_min_band, strob_max_band)
|
||
return strob_dir_band
|
||
|
||
|
||
## Устанавливает центр строба по частоте и направлению.
|
||
func set_strob(pos: Vector2) -> void:
|
||
var radp: Vector2 = tools.map_dec_to_rad(radius_center, pos)
|
||
if radp.y < $canvas.get_radius_outter():
|
||
$canvas.set_strob_dir(radp.x)
|
||
$canvas.set_strob_radius(radp.y)
|
||
|
||
|
||
## Обработка движения мыши для управления стробом. Вызывается во время движения мыши.
|
||
func _on_drag_continue(event) -> void:
|
||
if drag_button == MOUSE_BUTTON_RIGHT:
|
||
if shift_pressed:
|
||
var strob_width: float = get_strob_width(event.position, strob_width_begin)
|
||
$canvas.set_strob_width(strob_width)
|
||
else:
|
||
var strob_band: float = get_strob_band(event.position, strob_band_begin)
|
||
$canvas.set_strob_band(strob_band)
|
||
elif drag_button == MOUSE_BUTTON_LEFT:
|
||
set_strob(event.position)
|
||
|
||
|
||
## Обработка движения мыши для управления стробом. Вызывается при нажатии мыши.
|
||
func _on_drag_begin(event) -> void:
|
||
drag_pos_begin = event.position
|
||
if drag_button == MOUSE_BUTTON_LEFT:
|
||
set_strob(event.position)
|
||
elif drag_button == MOUSE_BUTTON_RIGHT:
|
||
strob_width_begin = $canvas.get_strob_width()
|
||
strob_band_begin = $canvas.get_strob_band()
|
||
|
||
|
||
## Обработчик событий ввода. Вызывается godot.
|
||
func _input(event) -> void:
|
||
if not is_visible_in_tree(): return
|
||
if (event is InputEventMouseMotion) and (view_mode == MapViewMode.RTO):
|
||
if drag_fsm == DragFSM.DRAG:
|
||
emit_signal('drag_continue', event)
|
||
elif event is InputEventMouseButton:
|
||
drag_button = event.button_index
|
||
if event.pressed:
|
||
if drag_fsm == DragFSM.IDLE:
|
||
drag_fsm = DragFSM.DRAG
|
||
emit_signal('drag_begin', event)
|
||
else:
|
||
if btn_select == BtnSel.FS:
|
||
sel_fs_sector(event)
|
||
drag_fsm = DragFSM.IDLE
|
||
emit_signal('drag_end', event)
|
||
|
||
elif event is InputEventKey:
|
||
if event.physical_keycode == KEY_SHIFT:
|
||
shift_pressed = event.pressed
|
||
|
||
|
||
## Обработчик нажатия по сектору формирователя сигналов.
|
||
func sel_fs_sector(event):
|
||
if (event.button_index == MOUSE_BUTTON_LEFT) or (event.button_index == MOUSE_BUTTON_RIGHT):
|
||
var center: Vector2 = $tilemap.position
|
||
var center_offset: Vector2 = ProjectSettings.get_setting('application/interfer/center_offset', Vector2i(0, 25))
|
||
var angle: float = event.position.angle_to_point(center + center_offset) * 180/PI - 45.0
|
||
if angle < 0:
|
||
angle += 360
|
||
var distance: float = event.position.distance_to(center + center_offset)
|
||
var sector: int
|
||
var band: int
|
||
var has_band: bool = false
|
||
for sec in prd.FS_SECTORS.values():
|
||
if angle >= sec[0] and angle <= sec[1]:
|
||
sector = prd.FS_SECTORS.find_key(sec)
|
||
for diap in fs_band.values():
|
||
if distance >= diap[0] and distance <= diap[1]:
|
||
band = fs_band.find_key(diap)
|
||
has_band = true
|
||
if not has_band:
|
||
return
|
||
var fs_index = band * 4 + sector
|
||
if event.button_index == MOUSE_BUTTON_LEFT:
|
||
fs_selected[fs_index] = null
|
||
else:
|
||
fs_selected.erase(fs_index)
|
||
update_fs_colors()
|
||
signaller.emit_signal('rto_threat_unsel_all')
|
||
var btn_state = len(fs_selected) == 0
|
||
set_ecm_btns_state(btn_state)
|
||
|
||
|
||
func set_fs_color(ecm):
|
||
var fs_index_arr: Array = []
|
||
prd.get_fs_index(ecm, fs_index_arr)
|
||
if ecm.krp != interfer.INTERFER_OFF:
|
||
for item in fs_index_arr:
|
||
fs_active[item] = null
|
||
else:
|
||
for item in fs_index_arr:
|
||
fs_active.erase(item)
|
||
update_fs_colors()
|
||
|
||
|
||
func update_fs_colors():
|
||
var fs_colors = $canvas.fs_default_colors.duplicate()
|
||
var color_ecm: Vector3 = ProjectSettings.get_setting('application/interfer/interfer_color', Vector3(0.58, 0.47, 0.51))
|
||
var color_select: Vector3 = ProjectSettings.get_setting('application/interfer/select_color', Vector3(1.0, 1.0, 1.0))
|
||
for key in fs_active.keys():
|
||
fs_colors[key] = color_ecm
|
||
for key in fs_selected.keys():
|
||
fs_colors[key] = color_select
|
||
$canvas.set_band_colors(fs_colors)
|
||
|
||
|
||
func unsel_fs_all(need_btns_off: bool):
|
||
fs_selected.clear()
|
||
update_fs_colors()
|
||
if need_btns_off:
|
||
set_ecm_btns_state(true)
|
||
|
||
|
||
func set_ecm_btns_state(value: bool):
|
||
var btns: Array = get_tree().get_nodes_in_group('группа-режим-помехи')
|
||
for btn in btns:
|
||
btn.set_pressed(false)
|
||
set_btns_disabled(value, btn)
|
||
|
||
|
||
func on_fs_selected(ecm):
|
||
var fs_index_arr: Array = []
|
||
prd.get_fs_index(ecm, fs_index_arr)
|
||
fs_selected.clear()
|
||
for item in fs_index_arr:
|
||
fs_selected[item] = null
|
||
update_fs_colors()
|
||
|
||
|
||
func set_btns_disabled(value: bool, btn):
|
||
var external_ctl = ProjectSettings.get_setting('application/config/Внешнее управление', false)
|
||
if external_ctl:
|
||
btn.set_pressed(false)
|
||
btn.set_disabled(true)
|
||
else:
|
||
btn.set_disabled(value)
|
||
|
||
|
||
func on_fs_update_color(ecms):
|
||
fs_active.clear()
|
||
for ecm in ecms.values():
|
||
set_fs_color(ecm)
|
||
update_fs_colors()
|
||
|
||
|
||
func on_interfer_prinuditelno (prenuditelno_sectors, btns):
|
||
for key in prenuditelno_sectors:
|
||
var sectors = []
|
||
for sector in prenuditelno_sectors[key]:
|
||
sectors.append(prd.get_fs_param(sector))
|
||
signaller.emit_signal('interfer_init', key, sectors)
|
||
update_fs_colors()
|
||
for btn in btns:
|
||
btn.set_pressed(false)
|
||
set_btns_disabled(true, btn)
|
||
|
||
|
||
## Вызывается при получении информации о состоянии модулей ФС от КАПС РПБ.
|
||
func on_fs_update_state(_fs_dict: Dictionary):
|
||
pass
|
||
|
||
|
||
func on_btn_center_toggled(toggled_on: bool) -> void:
|
||
$btn_center.tooltip_text = 'Центрирует карту на текущие координаты (%s)' % ('включено' if toggled_on else 'отключено')
|
||
signaller.emit_signal('map_centered', toggled_on)
|
||
|
||
|
||
func on_btn_activate_toggled(toggled_on: bool) -> void:
|
||
$btn_activate.tooltip_text = 'Включает подавление (%s)' % ('включено' if toggled_on else 'отключено')
|
||
|
||
|
||
func on_update_coordinates_label(coordinates_label) -> void:
|
||
$coordinates_map.text = str(coordinates_label)
|
||
|
||
|
||
func on_update_scale_label(scale_label) -> void:
|
||
$scale_map.text = str(scale_label)
|