Files
imitator-case-y5/uf_broadcast.gd
2025-12-07 16:01:46 +03:00

199 lines
6.9 KiB
GDScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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