class_name UF_Broadcast const ADDRESS_LIST: Dictionary = { 'УФ': [50052, '10.1.1.52']} const BROADCAST_ADDR: Array = [50000, '10.1.1.255'] const MIN_PACKET_SIZE: int = 7 var _self_name: String var _socket: SocketUDP var _timer: Timer var _cmd_number_count: int = 0 var status: int = 0 var ip: int = 0 func _init(name: String): '''Инициализация UDP broadcast UF клиента''' _self_name = name _initialize_socket() _initialize_timers() if not _bind_to_device_address(name): push_error('Не удалось инициализировать устройство: %s' % name) func _initialize_socket() -> void: '''Инициализация UDP сокета''' _socket = SocketUDP.new() func _initialize_timers() -> void: '''Инициализация таймеров''' # Таймер для широковещательной рассылки (3 раза в секунду) _timer = Timer.new() _timer.wait_time = 1.0 / 5.0 _timer.timeout.connect(_on_broadcast_timeout) 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 уже добавлен в другую сцену') func _bind_to_device_address(device_name: String) -> bool: '''Привязка к адресу устройства по его имени''' if not ADDRESS_LIST.has(device_name): push_error('Неизвестное устройство: %s' % device_name) return false var port: int = ADDRESS_LIST[device_name][0] var address: String = ADDRESS_LIST[device_name][1] return bind_unit_address(port, address) func bind_unit_address(port: int, address: String) -> bool: '''Привязка сокета к указанному порту и адресу''' if port <= 0 or port > 65535: push_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)]) return false print('Успешно привязано к %s:%d' % [address, port]) return true func _on_broadcast_timeout() -> void: '''Обработчик таймера широковещательной рассылки''' send_broadcast() func send_broadcast() -> bool: '''Отправка широковещательного пакета''' _socket.set_broadcast_enabled(true) var data: PackedByteArray = _form_status_device_packet() var send_result = _socket.send_to(BROADCAST_ADDR[1], BROADCAST_ADDR[0], data) if send_result == OK: return true else: push_error('Ошибка отправки широковещательного пакета') return false func _form_status_device_packet() -> PackedByteArray: var uf: Array = [0x00, 0x00, _cmd_number_count, 0x05, 0x00, 0x00, 0x4f, 0x00, status, 0x00, ip, 0x00, 0x00, 0xff, 0xff, 0x00,\ 0x00, 0x00, 0x00, 0xff, 0xff, 0xc0, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff,\ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00,\ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x01, 0x04, 0x24, 0x01, 0x00, 0x00, 0x26, 0x01,\ 0x00, 0x00, 0x28, 0x01, 0x00, 0x00, 0x2a, 0x01, 0xff, 0xff] var buffer = PackedByteArray() for byte in uf: buffer.append(byte) return buffer func start_broadcast() -> void: '''Запуск широковещательной рассылки и опроса сокета''' _timer.start() print('Широковещательная рассылка запущена') func stop_broadcast() -> void: '''Остановка широковещательной рассылки''' _timer.stop() print('Широковещательная рассылка остановлена') func close_udp_unit() -> void: '''Закрытие UDP соединения''' stop_broadcast() _socket.close() print('UDP соединение закрыто') func _on_poll_timeout() -> void: '''Обработчик таймера опроса сокета''' _poll_socket() func _poll_socket() -> void: '''Опрос сокета для получения входящих пакетов''' while _socket.get_available_packet_count() > 0: var packet = _socket.get_packet() _handle_received_data(packet) func _handle_received_data(data: PackedByteArray) -> void: '''Обработка входящих данных''' if not _validate_received_packet(data): return var command_type = _get_command_type(data) _cmd_number_count = data.decode_u16(2) if command_type == -1: # Проверяем на -1 вместо null return func _validate_received_packet(data: PackedByteArray) -> bool: '''Валидация входящего пакета''' if data.size() < MIN_PACKET_SIZE: push_warning('Получен слишком короткий пакет: %d байт' % data.size()) return false return true func _get_command_type(data: PackedByteArray) -> int: '''Получение типа команды из данных пакета''' if data.size() < 6: return -1 # Используем -1 для обозначения ошибки return data.decode_u8(6) func set_status_uf(new_status: int) -> void: if status != new_status: status = new_status print('Установлен статус УФ: %s' % status) func get_status_uf() -> int: return status func set_power_bit_uf(new_status)-> void: if ip != new_status: ip = new_status func get_power_bit_uf() -> int: return ip #region Вложенный класс SocketUDP class SocketUDP extends PacketPeerUDP: '''Расширенный класс UDP сокета с улучшенной обработкой ошибок''' func send_to(address: String, port: int, data: PackedByteArray) -> int: '''Отправка данных на указанный адрес и порт''' set_dest_address(address, port) var result = put_packet(data) if result != OK: push_error('Ошибка отправки: %s - %s:%d' % [error_string(result), address, port]) return result #endregion