diff --git a/main.gd b/main.gd index 587f721..eeca9fc 100644 --- a/main.gd +++ b/main.gd @@ -3,6 +3,7 @@ extends Control const CONFIG_PATH = "res://config/default_data.json" const BUFFER_SIZE = 32 var udp_broadcast: UDPBroadcast +var uf_broadcast: UF_Broadcast var default_data: Dictionary = {} var dictionary_yau07: Dictionary var dictionary_sockets_bit: Dictionary ## Байт 8 для отправки о состоянии ячеек @@ -148,6 +149,8 @@ func _ready() -> void: for ems in ems_arr_node: ems.connect('bits_changed', _on_modify_ems_g_status.bind(ems.get_meta('device'))) + $uf_button.connect('toggled', on_modify_uf.bind($uf_button.get_meta('device'))) + _update_tab_container() _load_default_data() _apply_default_data() @@ -191,6 +194,22 @@ func on_modify_device(toggled: bool, name_device: String) -> void: _update_tab_container(toggled, name_device) +func on_modify_uf(toggled: bool, name_device: String) -> void: + if toggled: + uf_broadcast = UF_Broadcast.new(name_device) + uf_broadcast.add_timers_to_scene(self) + uf_broadcast.start_broadcast() + dictionary_yau07[name_device] = uf_broadcast + print("Устройство %s активировано" % name_device) + for bit in get_tree().get_nodes_in_group('uf_status'): + bit.connect('toggled', _on_update_uf_status.bind(bit.get_meta('bit'))) + else: + if dictionary_yau07.has(name_device) and dictionary_yau07[name_device]: + dictionary_yau07[name_device].close_udp_unit() + dictionary_yau07.erase(name_device) + print("Устройство %s деактивировано" % name_device) + + func _update_tab_container(just_activated: bool = false, activated_device: String = ""): var tab_container = $TabContainer var device_nodes = get_tree().get_nodes_in_group('device-addr') @@ -226,6 +245,21 @@ func _on_update_socket_status(status: int, device_name: String)->void: socket.set_pressed_no_signal(is_bit_set == 1) +func _on_update_uf_status(toggled: bool, bit_index: int)->void: + if not dictionary_yau07.has('УФ'): return + var status = dictionary_yau07['УФ'].get_status_uf() + if bit_index == 6: + status = dictionary_yau07['УФ'].get_power_bit_uf() + + if toggled: # Установить бит в 1 + status = status | (1 << bit_index) + else: # Сбросить бит в 0 + status = status & ~(1 << bit_index) + + if bit_index == 6: dictionary_yau07['УФ'].set_power_bit_uf(status) + else: dictionary_yau07['УФ'].set_status_uf(status) + + func _on_update_isa_ports(isa_ports: Dictionary, device_name: String)->void: for port_addr in isa_ports: var value = isa_ports[port_addr] diff --git a/main.tscn b/main.tscn index 6763cdd..1fc8da1 100644 --- a/main.tscn +++ b/main.tscn @@ -2217,3 +2217,62 @@ layout_mode = 2 text = "Загрузка ЭМС" metadata/bit = 4 metadata/device = "ПРД-К4" + +[node name="uf_button" type="CheckButton" parent="."] +layout_mode = 0 +offset_left = 64.0 +offset_top = 480.0 +offset_right = 140.0 +offset_bottom = 511.0 +text = "УФ " +metadata/device = "УФ" + +[node name="addr" type="Label" parent="uf_button"] +layout_mode = 0 +offset_left = 80.0 +offset_top = 3.0 +offset_right = 149.0 +offset_bottom = 26.0 +text = "10.1.1.52" + +[node name="GridContainer" type="GridContainer" parent="."] +layout_mode = 0 +offset_left = 64.0 +offset_top = 520.0 +offset_right = 200.0 +offset_bottom = 761.0 + +[node name="CheckBox" type="CheckBox" parent="GridContainer" groups=["uf_status"]] +layout_mode = 2 +text = "КЭМС-Б1" +metadata/bit = 0 + +[node name="CheckBox2" type="CheckBox" parent="GridContainer" groups=["uf_status"]] +layout_mode = 2 +text = "ЭМС-Б" +metadata/bit = 1 + +[node name="CheckBox3" type="CheckBox" parent="GridContainer" groups=["uf_status"]] +layout_mode = 2 +text = "ЭМС-Б" +metadata/bit = 2 + +[node name="CheckBox4" type="CheckBox" parent="GridContainer" groups=["uf_status"]] +layout_mode = 2 +text = "КЭМС-Б1 load" +metadata/bit = 3 + +[node name="CheckBox5" type="CheckBox" parent="GridContainer" groups=["uf_status"]] +layout_mode = 2 +text = "ЭМС-Б load" +metadata/bit = 4 + +[node name="CheckBox6" type="CheckBox" parent="GridContainer" groups=["uf_status"]] +layout_mode = 2 +text = "ЭМС-Б load" +metadata/bit = 5 + +[node name="CheckBox7" type="CheckBox" parent="GridContainer" groups=["uf_status"]] +layout_mode = 2 +text = "ИП220-27Б" +metadata/bit = 6 diff --git a/udp_broadcast.gd b/udp_broadcast.gd index 1f8ad12..d057f59 100644 --- a/udp_broadcast.gd +++ b/udp_broadcast.gd @@ -355,11 +355,6 @@ func _encode_device_data(buffer: PackedByteArray) -> void: func _encode_ems_g_data(buffer: PackedByteArray) -> void: '''Кодирование данных EMS-G''' - #var ems_g_data = [ - #0x9e00, 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, - #0x0000, 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, - #0xffff - #] buffer.encode_u16(EMS_G_DATA_OFFSET, _input_const) buffer.encode_u16(EMS_G_DATA_OFFSET + 6, _input_impulse) buffer.encode_u16(EMS_G_DATA_OFFSET + 12, _output_const) diff --git a/uf_broadcast.gd b/uf_broadcast.gd new file mode 100644 index 0000000..b08f011 --- /dev/null +++ b/uf_broadcast.gd @@ -0,0 +1,198 @@ +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 diff --git a/uf_broadcast.gd.uid b/uf_broadcast.gd.uid new file mode 100644 index 0000000..6249b85 --- /dev/null +++ b/uf_broadcast.gd.uid @@ -0,0 +1 @@ +uid://lju7b4vxgjpd