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)