199 lines
6.9 KiB
GDScript
199 lines
6.9 KiB
GDScript
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
|