From d5261f47886b36e2640bd24540aecb1bfe92a649 Mon Sep 17 00:00:00 2001 From: Tot Maxim Date: Thu, 18 Dec 2025 23:38:13 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D1=80=D0=B0=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=D0=BA=D0=B0=20=D0=B6=D1=83=D1=80=D0=BD=D0=B0=D0=BB=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.gd | 7 ++- main.tscn | 37 +++++++---- udp_broadcast.gd | 103 ++++++++++++++++++++++-------- журнал.gd | 161 +++++++++-------------------------------------- 4 files changed, 139 insertions(+), 169 deletions(-) diff --git a/main.gd b/main.gd index 80f1770..4903e6a 100644 --- a/main.gd +++ b/main.gd @@ -170,7 +170,12 @@ func _exit_tree(): func on_modify_device(toggled: bool, name_device: String) -> void: if toggled: - udp_broadcast = UDPBroadcast.new(name_device) + var logger_node: Node + for node in get_tree().get_nodes_in_group('logger'): + if node.get_meta('device') == name_device: + logger_node = node + break + udp_broadcast = UDPBroadcast.new(name_device, logger_node) udp_broadcast.add_timers_to_scene(self) udp_broadcast.start_broadcast() dictionary_yau07[name_device] = udp_broadcast diff --git a/main.tscn b/main.tscn index eef1886..1e52493 100644 --- a/main.tscn +++ b/main.tscn @@ -44,6 +44,8 @@ anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 script = ExtResource("1_ig7tw") +metadata/_edit_vertical_guides_ = [136.0, 440.0] +metadata/_edit_horizontal_guides_ = [48.0, 768.0] [node name="Panel" type="Panel" parent="."] material = SubResource("ShaderMaterial_5vw27") @@ -206,15 +208,20 @@ theme_override_colors/font_color = Color(0, 0, 0, 1) text = "10.1.1.43" [node name="TabContainer" type="TabContainer" parent="."] -layout_mode = 0 -offset_left = 240.0 -offset_top = 17.0 -offset_right = 1536.0 -offset_bottom = 768.0 +layout_mode = 1 +anchors_preset = -1 +anchor_left = 0.16 +anchor_top = 0.02 +anchor_right = 1.024 +anchor_bottom = 0.904 +offset_left = -1.52588e-05 +offset_right = -0.00012207 +offset_bottom = -0.400024 tooltip_text = "Выбор прибора" current_tab = 0 use_hidden_tabs_for_min_size = true tab_focus_mode = 0 +metadata/_edit_use_anchors_ = true [node name="ПРД-Н1" type="Panel" parent="TabContainer" groups=["prd"]] layout_mode = 2 @@ -279,13 +286,18 @@ metadata/bit = 2 metadata/device = "ПРД-Н1" [node name="SocketsBar" type="TabContainer" parent="TabContainer/ПРД-Н1"] -layout_mode = 0 +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 offset_left = 199.0 -offset_right = 1296.0 -offset_bottom = 752.0 -current_tab = 3 +offset_bottom = 32.0 +grow_horizontal = 2 +grow_vertical = 2 +current_tab = 4 tabs_position = 1 tab_focus_mode = 0 +metadata/_edit_lock_ = true [node name="УКП-1" type="GridContainer" parent="TabContainer/ПРД-Н1/SocketsBar"] visible = false @@ -733,14 +745,17 @@ metadata/bit = 7 metadata/device = "ПРД-Н1" [node name="ЕМС-Г" parent="TabContainer/ПРД-Н1/SocketsBar" groups=["ems_g"] instance=ExtResource("2_0xm2m")] +visible = false layout_mode = 2 script = ExtResource("3_1bvp3") metadata/device = "ПРД-Н1" -[node name="Журнал" type="RichTextLabel" parent="TabContainer/ПРД-Н1/SocketsBar"] -visible = false +[node name="Журнал" type="RichTextLabel" parent="TabContainer/ПРД-Н1/SocketsBar" groups=["logger"]] layout_mode = 2 +bbcode_enabled = true +text = "Fish text" script = ExtResource("5_5vw27") +metadata/device = "ПРД-Н1" metadata/_tab_index = 4 [node name="ПРД-В1" type="Panel" parent="TabContainer" groups=["prd"]] diff --git a/udp_broadcast.gd b/udp_broadcast.gd index bb7fc94..d1f26e0 100644 --- a/udp_broadcast.gd +++ b/udp_broadcast.gd @@ -61,22 +61,31 @@ signal update_isa_ports(_isa_ports: Dictionary) signal update_socket_status(_sockets_status_bit: int) -func _init(name: String): +func _init(name: String, logs_node: RichTextLabel): '''Инициализация UDP broadcast клиента''' status_manager = StatusManager.new(self) _self_name = name + initialize_logs_manager(logs_node) _initialize_socket() _initialize_ukp_data() _initialize_timers() - _initialize_isa_ports() + _initialize_isa_ports() if not _bind_to_device_address(name): push_error('Не удалось инициализировать устройство: %s' % name) +func initialize_logs_manager(logs_node: LogsRich) -> void: + '''Инициализация менеджера логов''' + logs_manager = logs_node + logs_manager.info("Логирование инициализировано для %s" % _self_name) + + func _initialize_socket() -> void: '''Инициализация UDP сокета''' _socket = SocketUDP.new() + if logs_manager: + logs_manager.debug("UDP сокет инициализирован") func _initialize_timers() -> void: @@ -85,6 +94,8 @@ func _initialize_timers() -> void: _timer = Timer.new() _timer.wait_time = 1.0 / 5.0 _timer.timeout.connect(_on_broadcast_timeout) + if logs_manager: + logs_manager.debug("Таймеры инициализированы") func _initialize_ukp_data() -> void: @@ -96,6 +107,8 @@ func _initialize_ukp_data() -> void: ukp1_data = PackedByteArray() ukp1_data.resize(UKP_DATA_SIZE) ukp1_data.fill(0) + if logs_manager: + logs_manager.debug("Данные UKP инициализированы") func _initialize_isa_ports() -> void: @@ -104,12 +117,14 @@ func _initialize_isa_ports() -> void: set_isa_port(0x0106, 0) # DOU2 статус set_isa_port(0x0108, 0) # DOU3 статус set_isa_port(0x010A, 0) # ATT + if logs_manager: + logs_manager.debug("ISA порты инициализированы") func _bind_to_device_address(device_name: String) -> bool: '''Привязка к адресу устройства по его имени''' if not ADDRESS_LIST.has(device_name): - push_error('Неизвестное устройство: %s' % device_name) + _log_error('Неизвестное устройство: %s' % device_name) return false var port: int = ADDRESS_LIST[device_name][0] @@ -121,15 +136,15 @@ func _bind_to_device_address(device_name: String) -> bool: func bind_unit_address(port: int, address: String) -> bool: '''Привязка сокета к указанному порту и адресу''' if port <= 0 or port > 65535: - push_error('Некорректный номер порта: %d' % port) + _log_error('Некорректный номер порта: %d' % port) return false var bind_result = _socket.bind(port, address) if bind_result != OK: - push_error('Не удалось привязаться к %s:%d - %s' % [address, port, error_string(bind_result)]) + _log_error('Не удалось привязаться к %s:%d - %s' % [address, port, error_string(bind_result)]) return false - print('Успешно привязано к %s:%d' % [address, port]) + _log_info('Успешно привязано к %s:%d' % [address, port]) return true @@ -139,27 +154,28 @@ func add_timers_to_scene(parent: Node) -> void: if not _timer.get_parent(): parent.add_child(_timer) elif _timer.get_parent() != parent: - push_warning('Таймер broadcast уже добавлен в другую сцену') + _log_warning('Таймер broadcast уже добавлен в другую сцену') + else: + _log_debug("Таймеры добавлены в сцену") func start_broadcast() -> void: '''Запуск широковещательной рассылки и опроса сокета''' _timer.start() - print('Широковещательная рассылка запущена') + _log_info('Широковещательная рассылка запущена') func stop_broadcast() -> void: '''Остановка широковещательной рассылки''' _timer.stop() - #_poll_timer.stop() - print('Широковещательная рассылка остановлена') + _log_info('Широковещательная рассылка остановлена') func close_udp_unit() -> void: '''Закрытие UDP соединения''' stop_broadcast() _socket.close() - print('UDP соединение закрыто') + _log_info('UDP соединение закрыто') #region Публичные методы для управления состоянием @@ -168,7 +184,7 @@ func _handle_set_flag_ems_g(set_flag: bool)->void: var current_status = get_sockets_status() if set_flag: current_status = current_status | (1 << 4) - print('Установлен флаг загрузки EMS_G)') + _log_info('Установлен флаг загрузки EMS-G') set_sockets_status(current_status) @@ -176,6 +192,7 @@ func set_sockets_status(status: int) -> void: '''Установка статуса сокетов''' _sockets_status_bit = status emit_signal('update_socket_status', _sockets_status_bit, _self_name) + _log_debug("Статус сокетов обновлен: 0x%02X" % status) func get_sockets_status() -> int: @@ -186,6 +203,7 @@ func get_sockets_status() -> int: func set_dkm_status(status: int) -> void: '''Установка статуса DKM''' _dkm_status_bit = status + _log_debug("Статус DKM установлен: 0x%02X" % status) func get_dkm_status() -> int: @@ -196,6 +214,7 @@ func get_dkm_status() -> int: func set_ip_status(status: int)->void: '''Установка статуса ИП''' _dry_contact_status_bit = status + _log_debug("Статус ИП установлен: 0x%02X" % status) func get_ip_status()->int: @@ -209,16 +228,18 @@ func set_ems_g_status(in_imp: int, in_const: int, out_imp: int, out_const: int) _input_const = in_const _output_impulse = out_imp _output_const = out_const + _log_debug("Статус EMS-G обновлен: IN_C=0x%04X, IN_I=0x%04X, OUT_C=0x%04X, OUT_I=0x%04X" % [in_const, in_imp, out_const, out_imp]) func set_isa_port(port_addr: int, value: int) -> bool: '''Установка значения ISA порта''' if port_addr < 0 or port_addr > 0xFFFF: - push_error('Некорректный адрес порта: 0x%03X' % port_addr) + _log_error('Некорректный адрес порта: 0x%03X' % port_addr) return false _isa_ports[port_addr] = value emit_signal('update_isa_ports', _isa_ports, _self_name) + _log_debug("Порт ISA 0x%04X установлен: 0x%04X" % [port_addr, value]) return true @@ -235,7 +256,7 @@ func read_isa_ports(port_addresses: Array) -> Dictionary: if port_addr is int and port_addr >= 0 and port_addr <= 0xFFFF: result[port_addr] = get_isa_port(port_addr) else: - push_error('UDPBroadcast: Некорректный адрес порта: ' + str(port_addr)) + _log_error('Некорректный адрес порта: ' + str(port_addr)) return result @@ -265,7 +286,7 @@ func send_broadcast() -> bool: if send_result == OK: return true else: - push_error('Ошибка отправки широковещательного пакета') + _log_error('Ошибка отправки широковещательного пакета') return false @@ -390,9 +411,9 @@ func _send_immediate_response(address: String, port: int) -> void: _socket.set_broadcast_enabled(true) # Включаем обратно для broadcast if send_result == OK: - print('Отправлен немедленный ответ на %s:%d' % [address, port]) + _log_debug('Отправлен немедленный ответ на %s:%d' % [address, port]) else: - push_error('Ошибка отправки ответа на %s:%d' % [address, port]) + _log_error('Ошибка отправки ответа на %s:%d' % [address, port]) #endregion @@ -413,7 +434,7 @@ func _handle_received_data(data: PackedByteArray, address: String, port: int) -> func _validate_received_packet(data: PackedByteArray) -> bool: '''Валидация входящего пакета''' if data.size() < MIN_PACKET_SIZE: - push_warning('Получен слишком короткий пакет: %d байт' % data.size()) + _log_warning('Получен слишком короткий пакет: %d байт' % data.size()) return false return true @@ -436,7 +457,7 @@ func _handle_isa_command(command_code: int, data: PackedByteArray, address: Stri CMD_FLAG_EMS_G: _handle_set_flag_ems_g(true) _: - push_warning('Неизвестная команда ISA: %d от %s:%d' % [command_code, address, port]) + _log_warning('Неизвестная команда ISA: %d от %s:%d' % [command_code, address, port]) func _handle_read_isa_command(data: PackedByteArray, address: String, port: int) -> void: @@ -446,7 +467,7 @@ func _handle_read_isa_command(data: PackedByteArray, address: String, port: int) return var port_data = _get_isa_ports_data(ports) - print('Принята команда на чтение ISA портов от %s:%d: %s' % [address, port, port_data]) + _log_info('Принята команда на чтение ISA портов от %s:%d: %s' % [address, port, port_data]) _written_isa_ports = [] for i in range(ports.size()): _written_isa_ports.append([ports[i], port_data[i]]) @@ -460,7 +481,7 @@ func _handle_write_isa_command(data: PackedByteArray, address: String, port: int var ports = _parse_command_ports(data) var data_to_write = _parse_command_ports_data(data) if ports.size() != data_to_write.size(): - push_warning('Несоответствие размеров портов и данных от %s:%d' % [address, port]) + _log_warning('Несоответствие размеров портов и данных от %s:%d' % [address, port]) return # Обновляем или добавляем записанные порты @@ -480,7 +501,7 @@ func _handle_write_isa_command(data: PackedByteArray, address: String, port: int if not found: _written_isa_ports.append([port_addr, value]) # Добавляем новый порт - print('Принята команда на запись в ISA порты от %s:%d: %s' % [address, port, _isa_ports]) + _log_info('Принята команда на запись в ISA порты от %s:%d: %s' % [address, port, _isa_ports]) # Немедленная отправка ответа отправителю _send_immediate_response(address, port) #endregion @@ -525,7 +546,7 @@ func _parse_command_ports_data(data: PackedByteArray) -> Array: var port = data.decode_u16(8 + i * 4) var port_data = data.decode_u16(data_offset + i * 4) ports_data.append(port_data) - print('Данные порта 0x%02X: 0x%02X' % [port, port_data]) + _log_debug('Данные порта 0x%02X: 0x%02X' % [port, port_data]) return ports_data @@ -545,6 +566,38 @@ func _set_isa_ports_data(ports: Array, data: Array) -> void: #endregion +#region Методы логирования + +func _log_info(message: String) -> void: + '''Логирование информационного сообщения''' + if logs_manager: + logs_manager.info("[%s] %s" % [_self_name, message]) + else: + print("[%s] INFO: %s" % [_self_name, message]) + +func _log_warning(message: String) -> void: + '''Логирование предупреждения''' + if logs_manager: + logs_manager.warning("[%s] %s" % [_self_name, message]) + else: + print("[%s] WARNING: %s" % [_self_name, message]) + +func _log_error(message: String) -> void: + '''Логирование ошибки''' + if logs_manager: + logs_manager.error("[%s] %s" % [_self_name, message]) + else: + print("[%s] ERROR: %s" % [_self_name, message]) + +func _log_debug(message: String) -> void: + '''Логирование отладочной информации''' + if logs_manager: + logs_manager.debug("[%s] %s" % [_self_name, message]) + else: + print("[%s] DEBUG: %s" % [_self_name, message]) + +#endregion + #region Вложенный класс SocketUDP class SocketUDP extends PacketPeerUDP: @@ -567,7 +620,7 @@ class SocketUDP extends PacketPeerUDP: func clear_written_ports() -> void: '''Очистка списка записанных портов (ручное управление)''' _written_isa_ports.clear() - print('Список записанных портов очищен') + _log_info('Список записанных портов очищен') func get_written_ports_count() -> int: '''Получение количества записанных портов''' @@ -578,7 +631,7 @@ func remove_written_port(port_addr: int) -> bool: for i in range(_written_isa_ports.size()): if _written_isa_ports[i][0] == port_addr: _written_isa_ports.remove_at(i) - print('Порт 0x%04X удален из списка записанных' % port_addr) + _log_info('Порт 0x%04X удален из списка записанных' % port_addr) return true return false diff --git a/журнал.gd b/журнал.gd index 0f19e31..4ff5b1d 100644 --- a/журнал.gd +++ b/журнал.gd @@ -1,14 +1,6 @@ class_name LogsRich extends RichTextLabel -var _log_text: String = "": - set(value): - _log_text = value - text = _log_text - scroll_to_line(get_line_count()) - get: - return _log_text - enum LogLevel { INFO, WARNING, @@ -34,36 +26,33 @@ var max_lines: int = 1000 func _ready(): scroll_active = true - scroll_following = true + #scroll_following = true bbcode_enabled = true - - # Добавляем моноширинный шрифт для лучшего отображения логов - var font = get_theme_font("normal_font") - if font: - add_theme_font_override("normal_font", font.duplicate()) - + fit_content = false clear_logs() -func add_log(message: String, level: LogLevel = LogLevel.INFO) -> void: + +func add_log(message: String, level: LogLevel = LogLevel.INFO) -> void: + if not is_inside_tree(): + push_warning("LogsRich не добавлен в дерево сцены!") + return + var timestamp = Time.get_time_string_from_system() var prefix = _level_prefixes.get(level, "[INFO]") var color = _level_colors.get(level, Color.WHITE) var formatted_message = "[%s] %s %s" % [timestamp, prefix, message] + var color_hex = color.to_html(false) - # Используем BBcode для цветного текста - # Важно: to_html() возвращает цвет без #, нужно добавить - var color_hex = color.to_html() + # Просто добавляем текст (RichTextLabel сам обрабатывает BBcode) append_text("[color=#%s]%s[/color]\n" % [color_hex, formatted_message]) - # Обновляем приватную переменную - _log_text = text + # Прокручиваем вниз + if is_inside_tree(): + scroll_to_line(get_line_count()) # Ограничиваем количество строк - limit_line_count() - - # Прокручиваем к последней строке - scroll_to_line(get_line_count()) + limit_line_count_simple() func info(message: String) -> void: add_log(message, LogLevel.INFO) @@ -78,114 +67,22 @@ func debug(message: String) -> void: add_log(message, LogLevel.DEBUG) func clear_logs() -> void: - clear() text = "" - _log_text = "" -func limit_line_count() -> void: - var lines = get_parsed_text().split("\n") - if lines.size() > max_lines: - # Удаляем старые строки - var _new_lines = lines.slice(lines.size() - max_lines) - - # Очищаем и перестраиваем текст с сохранением форматирования - clear() - text = "" - - # Нужно переформатировать все строки снова - # Это сложно сделать правильно без хранения дополнительной информации о цветах - # Поэтому лучше использовать альтернативный подход: - - # Просто обрезаем raw текст - var current_text = get_parsed_text() - var all_lines = current_text.split("\n") - if all_lines.size() > max_lines: - var trimmed_lines = all_lines.slice(all_lines.size() - max_lines) - clear() - text = "\n".join(trimmed_lines) - _log_text = text - -# Альтернативная, более простая реализация limit_line_count func limit_line_count_simple() -> void: - # Получаем сырой текст без BBcode тегов - var raw_text = get_parsed_text() - var lines = raw_text.split("\n") - - if lines.size() > max_lines: - # Удаляем первые строки сверх лимита - clear() - text = "" + var lines = get_line_count() + if lines > max_lines: + # Получаем чистый текст без BBcode + var raw_text = get_parsed_text() + var text_lines = raw_text.split("\n") - # Начинаем с индекса, где превышен лимит - var start_index = lines.size() - max_lines - for i in range(start_index, lines.size()): - # Здесь нужно определить уровень логирования для каждой строки - # Это сложно без хранения дополнительной информации - # Поэтому просто добавляем как INFO - var line = lines[i].strip_edges() - if line.length() > 0: - append_text("[color=#ffffff]%s[/color]\n" % line) - - _log_text = text - -# Лучший подход: хранить логи в массиве и перерисовывать при необходимости -var _log_entries: Array = [] # Каждая запись: {message: String, level: LogLevel, timestamp: String} - -func add_log_advanced(message: String, level: LogLevel = LogLevel.INFO) -> void: - var timestamp = Time.get_time_string_from_system() - var entry = { - "message": message, - "level": level, - "timestamp": timestamp - } - - _log_entries.append(entry) - - # Ограничиваем количество записей - if _log_entries.size() > max_lines: - _log_entries.pop_front() - - # Перерисовываем все логи - redraw_logs() - -func redraw_logs() -> void: - clear() - text = "" - - for entry in _log_entries: - var prefix = _level_prefixes.get(entry.level, "[INFO]") - var color = _level_colors.get(entry.level, Color.WHITE) - var formatted_message = "[%s] %s %s" % [entry.timestamp, prefix, entry.message] - var color_hex = color.to_html() - append_text("[color=#%s]%s[/color]\n" % [color_hex, formatted_message]) - - _log_text = text - scroll_to_line(get_line_count()) - -# Метод для сохранения логов в файл -func save_logs_to_file(path: String = "user://logs.txt") -> void: - var file = FileAccess.open(path, FileAccess.WRITE) - if file: - file.store_string(get_parsed_text()) - file.close() - info("Logs saved to: %s" % path) - else: - error("Failed to save logs to: %s" % path) - -# Метод для фильтрации логов по уровню -func filter_logs_by_level(min_level: LogLevel) -> void: - if _log_entries.is_empty(): - return - - clear() - text = "" - - for entry in _log_entries: - if entry.level >= min_level: - var prefix = _level_prefixes.get(entry.level, "[INFO]") - var color = _level_colors.get(entry.level, Color.WHITE) - var formatted_message = "[%s] %s %s" % [entry.timestamp, prefix, entry.message] - var color_hex = color.to_html() - append_text("[color=#%s]%s[/color]\n" % [color_hex, formatted_message]) - - _log_text = text + if text_lines.size() > max_lines: + # Удаляем первые строки + text_lines = text_lines.slice(text_lines.size() - max_lines) + + # Очищаем и перезаписываем + clear_logs() + for line in text_lines: + if line.strip_edges().length() > 0: + # Добавляем как INFO (теряем цвета, но это проще) + append_text("[color=#ffffff]%s[/color]\n" % line)