diff --git a/scenes/canvas.gd b/scenes/canvas.gd index 6ad2118e..64c7eb7a 100644 --- a/scenes/canvas.gd +++ b/scenes/canvas.gd @@ -9,10 +9,11 @@ func set_strob_radius(val: float): $строб.material.set('shader_parameter/ra func set_strob_center(val: Vector2): $строб.position = val func get_strob_center() -> Vector2: return $строб.position -func get_strob_visible() -> bool: return $строб.visible -func get_strob_width() -> float: return $строб.material.get('shader_parameter/width') -func get_strob_band() -> float: return $строб.material.get('shader_parameter/band') * 125.0 -func get_strob_radius(val: float): return $строб.material.set('shader_parameter/radius', sv(val)) -func get_radius_max(): return $строб.scale.x / 2.0 +func get_strob_visible() -> bool: return $строб.visible +func get_strob_width() -> float: return $строб.material.get('shader_parameter/width') +func get_strob_band() -> float: return $строб.material.get('shader_parameter/band') * 125.0 +func get_strob_radius(val: float): return $строб.material.set('shader_parameter/radius', sv(val)) +func get_radius_max() -> float: return $строб.scale.x / 2.0 +func get_radius_min() -> float: return 200.0 func sv(val: float) -> float: return val / ($строб.scale.x / 2.0) * 0.125 diff --git a/scenes/tab_switch.gd b/scenes/tab_switch.gd index 61594755..c40f5eb3 100644 --- a/scenes/tab_switch.gd +++ b/scenes/tab_switch.gd @@ -12,4 +12,4 @@ func _process(_delta: float) -> void: func _enter_tree() -> void: - REPNetwork.logger_page = $'журнал' + REPNetwork.logger_page = $журнал diff --git a/scenes/журнал.gd b/scenes/журнал.gd index 9aae89ef..7e5141ef 100644 --- a/scenes/журнал.gd +++ b/scenes/журнал.gd @@ -1,9 +1,19 @@ extends Control -func _ready() -> void: Log.info('Модуль журнала работы готов') -func on_line_changed(unit, addr): Log.message(Logger.INFO, 'состоянии связи: %s %s' % [unit, addr]) -func on_command_fail(unit, addr): Log.message(Logger.ERROR, 'команда не выполнена: %s %s' % [unit, addr]) + +func on_line_changed(unit, addr): Log.message(Logger.INFO, 'Состояние связи: %s %s' % [unit, addr]) +func on_command_fail(unit, addr): Log.message(Logger.ERROR, 'Ошибка: команда не выполнена: %s %s' % [unit, addr]) + + +func _ready() -> void: + Log.info('Модуль журнала работы готов') + Threats.connect('threat_new', Callable(self, 'on_threats_changed').bind('Цель обнаружена')) + Threats.connect('threat_lost', Callable(self, 'on_threats_changed').bind('Цель потеряна')) + + +func on_threats_changed(threats: Dictionary, th: Object, msg: String): + Log.message(Logger.INFO, '%s: %s' % [msg, th.to_string()]) func _on_clear_button_down() -> void: diff --git a/scenes/работа.gd b/scenes/работа.gd index 1f784d63..534878f6 100644 --- a/scenes/работа.gd +++ b/scenes/работа.gd @@ -9,6 +9,7 @@ extends Panel @export var drag_scale_width: float @export var drag_scale_band: float @export var window_0_id: int +@export var Bip: PackedScene signal add_imit_threat (signal_name: String) signal del_imit_threat (signal_name: String) @@ -53,15 +54,66 @@ signal full_screen """ Вызывается при смене режима окна полный экран - оконный """ +func _on_full_screen(): toggle_full_screen(window_0_id) + + func _ready(): + $btn_strobe.button_connect('pressed', Callable(self, 'on_button_strobe_pressed')) $btn_off.set_pressed(true) strob_center = $canvas.get_strob_center() on_button_strobe_pressed() drag_fsm = DRAG_FSM.OFF var nodes: = get_tree().get_nodes_in_group('группа-режим-помехи') - for btn in nodes: - btn.button_connect('pressed', Callable(self, 'on_button_pressed').bind(btn, nodes)) + 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_update', Callable(self, 'on_threat_update')) + Threats.connect('threat_lost', Callable(self, 'on_threat_lost')) + Threats.connect('threat_new', Callable(self, 'on_threats_changed')) + Threats.connect('threat_lost', Callable(self, 'on_threats_changed')) + + +func pos_calc(freq, aoa, pos0: Vector2, r0, r1, f0: float, df: float): + var r = ((freq - f0) / df) * r1 + r0 + var aoa_rad = Tools.radians(aoa) - PI / 2.0 + var x = cos(aoa_rad) * r + pos0.x + var y = sin(aoa_rad) * r + pos0.y + return Vector2(x, y) + + +func map_threat_to_bip(th: Threats.Threat, bip: Sprite2D): + th.aoa = float(th.id) * 10.0 + bip.position = pos_calc ( + th.freq, + th.aoa, + $canvas.get_strob_center(), + $canvas.get_radius_min(), + $canvas.get_radius_max(), + REPSettings.ThreatParams.freq_min[0], + REPSettings.ThreatParams.freq_max[0] - REPSettings.ThreatParams.freq_min[0]) + + +func on_threat_update(threats: Dictionary, th: Object): + var bip = get_node('%d' % th.id) + map_threat_to_bip(th, bip) + + +func on_threat_new(threats: Dictionary, th: Object): + var bip = Bip.instantiate() + bip.name = '%d' % th.id + map_threat_to_bip(th, bip) + add_child(bip) + + + +func on_threat_lost(threats: Dictionary, th: Object): + var bip = get_node('%d' % th.id) + remove_child(bip) + bip.free() + + +func on_threats_changed(threats: Dictionary, th: Object): + $count_all_pad/count_all.text = '%02d' % threats.size() func on_button_strobe_pressed(): @@ -153,7 +205,3 @@ func toggle_full_screen(id: int): mode = DisplayServer.WINDOW_MODE_FULLSCREEN DisplayServer.window_set_mode(mode, id) - -func _on_full_screen(): - toggle_full_screen(window_0_id) - diff --git a/scenes/работа.tscn b/scenes/работа.tscn index e9a3cb60..aea64024 100644 --- a/scenes/работа.tscn +++ b/scenes/работа.tscn @@ -1,11 +1,13 @@ -[gd_scene load_steps=8 format=3 uid="uid://b276iygic5itk"] +[gd_scene load_steps=10 format=3 uid="uid://b276iygic5itk"] [ext_resource type="Script" path="res://scenes/работа.gd" id="1_qjoce"] +[ext_resource type="PackedScene" uid="uid://nl1vklubr5kr" path="res://scenes/bip.tscn" id="2_47xio"] [ext_resource type="PackedScene" uid="uid://b5kjdyxuwsot5" path="res://scenes/canvas.tscn" id="2_bl0yt"] [ext_resource type="PackedScene" uid="uid://bq7haw1ypiv48" path="res://scenes/frame.tscn" id="3_l1omj"] [ext_resource type="PackedScene" uid="uid://da7w3vkhadfwe" path="res://scenes/button_flat.tscn" id="4_5es2r"] [ext_resource type="Script" path="res://scenes/frm_threats.gd" id="4_hg23i"] [ext_resource type="Script" path="res://table/table.gd" id="4_mqhcf"] +[ext_resource type="FontFile" uid="uid://befva8r034a4v" path="res://data/DSEG7Classic-Regular.ttf" id="7_22fjb"] [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_5ugj8"] @@ -19,6 +21,7 @@ strob_min_band = 0.1 strob_max_band = 15.538 drag_scale_width = 0.5 drag_scale_band = 0.05 +Bip = ExtResource("2_47xio") metadata/_edit_vertical_guides_ = [] metadata/_edit_lock_ = true @@ -653,6 +656,72 @@ offset_bottom = 1168.0 text = "Автоматически" metadata/rfi_name = "" +[node name="count_all_pad" type="Label" parent="."] +self_modulate = Color(0.403922, 0.403922, 0.403922, 1) +layout_mode = 0 +offset_left = 96.0 +offset_top = 109.0 +offset_right = 164.0 +offset_bottom = 142.0 +theme_override_fonts/font = ExtResource("7_22fjb") +theme_override_font_sizes/font_size = 20 +text = "88" +vertical_alignment = 1 + +[node name="count_all" type="Label" parent="count_all_pad"] +layout_mode = 0 +offset_right = 68.0 +offset_bottom = 33.0 +theme_override_fonts/font = ExtResource("7_22fjb") +theme_override_font_sizes/font_size = 20 +text = "00" +vertical_alignment = 1 + +[node name="count_danger_pad" type="Label" parent="."] +self_modulate = Color(0.403922, 0.403922, 0.403922, 1) +layout_mode = 0 +offset_left = 96.0 +offset_top = 29.0 +offset_right = 188.0 +offset_bottom = 88.0 +theme_override_fonts/font = ExtResource("7_22fjb") +theme_override_font_sizes/font_size = 56 +text = "88" + +[node name="count_danger" type="Label" parent="count_danger_pad"] +layout_mode = 0 +offset_right = 92.0 +offset_bottom = 59.0 +theme_override_fonts/font = ExtResource("7_22fjb") +theme_override_font_sizes/font_size = 56 +text = "00" + +[node name="Label2" type="Label" parent="."] +layout_mode = 0 +offset_left = 18.0 +offset_top = 6.0 +offset_right = 153.0 +offset_bottom = 34.0 +text = "Цели" +vertical_alignment = 1 + +[node name="Label3" type="Label" parent="."] +layout_mode = 0 +offset_left = 18.0 +offset_top = 110.0 +offset_right = 74.0 +offset_bottom = 146.0 +text = "Всего:" +vertical_alignment = 1 + +[node name="Label4" type="Label" parent="."] +layout_mode = 0 +offset_left = 18.0 +offset_top = 50.0 +offset_right = 83.0 +offset_bottom = 72.0 +text = "Опасных:" + [connection signal="drag_begin" from="." to="." method="_on_drag_begin"] [connection signal="drag_continue" from="." to="." method="_on_drag_continue"] [connection signal="full_screen" from="." to="." method="_on_full_screen" flags=3] diff --git a/scripts/capsrpb.gd b/scripts/capsrpb.gd index 64656ab6..b23d3dac 100644 --- a/scripts/capsrpb.gd +++ b/scripts/capsrpb.gd @@ -30,7 +30,7 @@ class CapsRpb: emit_signal('line_changed', self) var utf8_data = data.get_string_from_utf8() if Error.OK == json_conv.parse(utf8_data): - json_dic.merge(json_conv.get_data()) + json_dic.merge(json_conv.get_data(), true) emit_signal('data_received', self) else: var msg = json_conv.get_error_message() diff --git a/scripts/repnetwork.gd b/scripts/repnetwork.gd index 1afa5c74..68a00672 100644 --- a/scripts/repnetwork.gd +++ b/scripts/repnetwork.gd @@ -7,7 +7,7 @@ class Socket extends PacketPeerUDP: self.put_packet(data) -const PROTO_TYPE: = {'yau07tx': yau07.YaU07, 'jsontx': capsrpb.CapsRpb} +const PROTOCOL_TYPES: = {'yau07tx': yau07.YaU07, 'jsontx': capsrpb.CapsRpb} var poll_sockets: Array[Socket] var units: Dictionary var sock_unicast: Socket @@ -15,10 +15,10 @@ var logger_page: Node func create_socket(nm) -> Socket: - var addr = REPSettings.addr[nm][1] - var port = REPSettings.addr[nm][2] - var broad = REPSettings.addr[nm][3] - var bind = REPSettings.addr[nm][4] + var addr = REPSettings.UnitProfiles[nm][1] + var port = REPSettings.UnitProfiles[nm][2] + var broad = REPSettings.UnitProfiles[nm][3] + var bind = REPSettings.UnitProfiles[nm][4] var sock: = Socket.new() sock.set_broadcast_enabled(broad) var rc = Error.OK if not bind else sock.bind(port, addr) @@ -28,32 +28,34 @@ func create_socket(nm) -> Socket: func _ready() -> void: - var addr = REPSettings.addr['уарэп-яу07-частный'][1] - var port = REPSettings.addr['уарэп-яу07-частный'][2] - sock_unicast = Socket.new() - sock_unicast.bind(port, addr) - for key in REPSettings.addr: - var setting = REPSettings.addr[key] + Log.info('Сетевой модуль готов') + poll_sockets.append(create_socket('уарэп-яу07-частный')) + poll_sockets.append(create_socket('уарэп-яу07-общий')) + poll_sockets.append(create_socket('уарэп-капсрпб-пол')) + + sock_unicast = poll_sockets[0] + + for unit_name in REPSettings.UnitProfiles: + var setting = REPSettings.UnitProfiles[unit_name] var proto = setting[0] - if proto in PROTO_TYPE: - var unit = PROTO_TYPE[proto].new(key) - var k = REPSettings.get_unit_key(setting) - units[k] = unit - unit.connect('line_changed', Callable(logger_page, 'on_line_changed').bind(k)) - unit.connect('command_fail', Callable(logger_page, 'on_command_fail').bind(k)) + if proto in PROTOCOL_TYPES: + var unit = PROTOCOL_TYPES[proto].new(unit_name) + var unit_key = REPSettings.get_unit_key(unit_name) + units[unit_key] = unit + unit.connect('line_changed', Callable(logger_page, 'on_line_changed').bind(unit_key)) + unit.connect('command_fail', Callable(logger_page, 'on_command_fail').bind(unit_key)) - for key in units: - var unit = units[key] - Log.info('%s %s:%d' % [unit.name, key[0], key[1]]) + for key in units: Log.info('%s %s:%d' % [units[key].name, key[0], key[1]]) -func poll_receive(sock: Socket): +func poll_receive(sock: Socket) -> bool: while sock.get_available_packet_count() > 0: var data: = sock.get_packet() var addr: = sock.get_packet_ip() var port: = sock.get_packet_port() var addr_port: = [addr, port] if addr_port in units: units[addr_port].parse(data) + return false func _process(delta: float) -> void: @@ -62,4 +64,4 @@ func _process(delta: float) -> void: match unit.process(delta): Error.OK: sock_unicast.send_to(addr, unit.data_tx) Error.FAILED: emit_signal('socket_error', 'ошибка: %s %s:%d' % [unit, addr[0], addr[1]]) - for sock in poll_sockets: poll_receive(sock) + poll_sockets.any(func(sock): poll_receive(sock)) diff --git a/scripts/repsettings.gd b/scripts/repsettings.gd index bd13547e..36cc289a 100644 --- a/scripts/repsettings.gd +++ b/scripts/repsettings.gd @@ -1,7 +1,7 @@ extends Node -@export var addr: = { \ +@export var UnitProfiles: = { \ # Название Протокол Адрес Порт Широков. Привяз. Комментарий 'уарэп-яу07-частный': ['yau07rx', '127.0.0.1', 50079, false, true, 'IP-адрес на котором принимают частные сообщения от ЯУ-07'], 'уарэп-яу07-общий': ['yau07rx', '127.0.0.255', 50000, true, true, 'IP-адрес на котором принимают широковещательные сообщения от ЯУ-07'], @@ -23,5 +23,15 @@ extends Node 'уарэп-щ3': ['uart', '/dev/stty0', 115200, 1, 1, false, 1000, 'Прибор Щ3'] } # скорость, стартовых, стоповых, чётность, таймаут в мкс -func get_unit_key(item: Array) -> Array: return [item[1], item[2]] +@export var ThreatParams = { + 'time_lost': [5000, 'Время после последнего обновления в мс, через которое цель считается потерянной'], + 'freq_min': [200.0, 'Нижняя рабочая частота, МГЦ'], + 'freq_max': [6000.0, 'Нижняя рабочая частота, МГЦ'] } + + func _ready() -> void: Log.info('Модуль настроек готов') + +func get_unit_key(unit_name: String) -> Array: + assert(unit_name in UnitProfiles, 'ошибка: нет такого устройства "%s"' % unit_name) + var item = UnitProfiles[unit_name] + return [item[1], item[2]] diff --git a/scripts/threats.gd b/scripts/threats.gd index cae1699a..e8b97066 100644 --- a/scripts/threats.gd +++ b/scripts/threats.gd @@ -1,75 +1,85 @@ extends Node -enum ThreatSrc { - MP405, - RLKS, - RPB, - SIGMA } - +@onready var timer_check_lost: = Timer.new() class Threat: + var alt: float # Текущая высота, метры. + var aoa: float # Курсовой угол на источник сигнала, градусы. + var atick: float # Тик обнаружения. + var baud: float # Модуляционная скорость. + var fflags: Dictionary # Флаги достоверности полей. + var freq: float # Частота несущей или центральная частота, МГц. + var freqm: String # Режим смены частоты. + var lat: float # Текущие координаты, долгота в градусах. + var lon: float # Текущие координаты, широта в градусах. + var power: float # Уровень принимаемого сигнала, дБм. + var proto: String # Тип протокола источника сигнала. + var rkrp: String # Рекомендуемый режим помехи. + var rparams: String # Рекомендуемые параметры помехи. + var salt: float # Координаты старта, высота от уровня моря, м. + var slat: float # Координаты старта, широта в градусах. + var slon: float # Координаты старта, долгота в градусах. + var tick: int # Время приёма сигнала, мс. + var tmod: String # Тип модуляции. + var utime: Dictionary # Время крайнего обновления. + var width: float # Ширина занимаемого диапазона частот, МГц. + var id: int # Идентификатор цели - enum FFlags { # Допустимые значения для Threat.fflags - ALT = 1 << 1, - AOA = 1 << 2, - ATIME = 1 << 3, - BAUD = 1 << 4, - FFLAGS = 1 << 5, - FREQ = 1 << 6, - FREQM = 1 << 7, - LAT = 1 << 8, - LON = 1 << 9, - POW_RX = 1 << 10, - PROTO = 1 << 11, - RKRP = 1 << 12, - RPARAMS = 1 << 13, - SALT = 1 << 14, - SLAT = 1 << 15, - SLON = 1 << 16, - TICK = 1 << 17, - TMOD = 1 << 18, - UTIME = 1 << 19, - WIDTH = 1 << 20 } + func _init(): Tools.get_members_names(self).any(func(vn): fflags[vn] = false) - var alt: float # Текущая высота, метры. - var aoa: float # Курсовой угол на источник сигнала, градусы. - var atime: Dictionary # Время обнаружения. - var baud: float # Модуляционная скорость. - var fflags: FFlags # Флаги достоверности полей. - var freq: float # Частота несущей или центральная частота, МГц. - var freqm: String # Режим смены частоты. - var lat: float # Текущие координаты, долгота в градусах. - var lon: float # Текущие координаты, широта в градусах. - var pow_rx: float # Уровень принимаемого сигнала, дБм. - var proto: String # Тип протокола источника сигнала. - var rkrp: String # Рекомендуемый режим помехи. - var rparams: String # Рекомендуемые параметры помехи. - var salt: float # Координаты старта, высота от уровня моря, м. - var slat: float # Координаты старта, широта в градусах. - var slon: float # Координаты старта, долгота в градусах. - var tick: int # Время приёма сигнала, мс. - var tmod: String # Тип модуляции. - var utime: Dictionary # Время крайнего обновления. - var width: float # Ширина занимаемого диапазона частот, МГц. + func _to_string() -> String: return 'номер:%d пеленг:%0.1f частота:%0.1f МГц' % [id, aoa, freq] + + func set_from_dic(dic_vals: Dictionary): + var items: = fflags.keys().filter(func(vn): return vn in dic_vals) + items.any(func(vn): set(vn, dic_vals[vn]); fflags[vn] = true; return false) -var _threats = {} +signal threat_new(threats: Dictionary, th: Threat) +signal threat_lost(threats: Dictionary, th: Threat) +signal threat_update(threats: Dictionary, th: Threat) + +var threats = {} """ Список целей. Индекс - <источник цели>:<номер цели> Источник цели может быть: 'RPB', 'SIGMA', 'RLKS', 'MP405' """ -func on_parse_threads(unit: capsrpb.CapsRpb): - print_debug(unit) - func _ready() -> void: Log.info('Модуль обработки целей готов') - var unit = REPSettings.addrs[] + var unit_key = REPSettings.get_unit_key('уарэп-капсрпб-отп') + var unit = REPNetwork.units[unit_key] unit.connect('data_received', Callable(self, 'on_data_capsrpb_received')) + add_child(timer_check_lost) + timer_check_lost.connect('timeout', Callable(self, 'on_timer_check_lost')) + timer_check_lost.start(REPSettings.ThreatParams.time_lost[0] / 2000.0) +func on_timer_check_lost(): + var tick = Time.get_ticks_msec() + for id in threats.keys(): + var th = threats[id] + if (tick - th.atick) >= REPSettings.ThreatParams.time_lost[0]: + threats.erase(th.id) + call_deferred('emit_signal', 'threat_lost', threats, th) + func on_data_capsrpb_received(unit: capsrpb.CapsRpb): - pass - + if 'mis' not in unit.json_dic: + return + var mis = unit.json_dic['mis'] + for dic_th in mis: + if 'id' in dic_th: + var id = int(dic_th['id']) + if id in threats: + var th = threats[id] + th.set_from_dic(dic_th) + th.atick = Time.get_ticks_msec() + call_deferred('emit_signal', 'threat_update', threats, th) + else: + var th = Threat.new() + th.set_from_dic(dic_th) + th.atick = Time.get_ticks_msec() + th.fflags['atick'] = true + threats[id] = th + call_deferred('emit_signal', 'threat_new', threats, th) diff --git a/scripts/tools.gd b/scripts/tools.gd index b5071f9a..495bbf26 100644 --- a/scripts/tools.gd +++ b/scripts/tools.gd @@ -1,6 +1,16 @@ extends Node +func radians(val: float): return (val * PI) / 180.0 + + +func get_members_names(obj: Object) -> Array: + var pl = obj.get_property_list() + var names: = Array() + for item in pl: names.append(item.name) + return names + + func assert_array_ipaddr(val: Array): """ Выкидывает исключение, если массив не содержит ip-адрес """ assert(val.size() == 2)