diff --git a/main.gd b/main.gd index 18767f5..c9a4811 100644 --- a/main.gd +++ b/main.gd @@ -165,6 +165,7 @@ func on_modify_device(toggled: bool, name_device: String) -> void: dictionary_yau07[name_device] = udp_broadcast print("Устройство %s активировано" % name_device) dictionary_yau07[name_device].connect('update_isa_ports', _on_update_isa_ports) + dictionary_yau07[name_device].connect('update_socket_status', _on_update_socket_status) else: if dictionary_yau07.has(name_device) and dictionary_yau07[name_device]: dictionary_yau07[name_device].close_udp_unit() @@ -202,6 +203,14 @@ func _update_tab_container(just_activated: bool = false, activated_device: Strin tab_container.visible = has_visible_tabs +func _on_update_socket_status(status: int, device_name: String)->void: + var socket_arr = _get_nodes_for_device(device_name, 'socket') + for socket in socket_arr: + var bit_index = socket.get_meta('bit') + var is_bit_set = (status >> bit_index) & 1 + socket.set_pressed_no_signal(is_bit_set == 1) + + 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/udp_broadcast.gd b/udp_broadcast.gd index 4bc9a36..8bea1a2 100644 --- a/udp_broadcast.gd +++ b/udp_broadcast.gd @@ -13,28 +13,29 @@ const UKP_DATA_SIZE: int = 32 # Константы команд ISA const CMD_READ_ISA: int = 0 const CMD_WRITE_ISA: int = 1 +const CMD_FLAG_EMS_G: int = 70 # Минимальный размер пакета для валидации const MIN_PACKET_SIZE: int = 7 # Словарь адресов устройств const ADDRESS_LIST: Dictionary = { - 'ПРД-Н1': [50011, "10.1.1.11"], - 'ПРД-В1': [50012, "10.1.1.12"], - 'ПРД-К1': [50013, "10.1.1.13"], - 'ПРД-Н2': [50021, "10.1.1.21"], - 'ПРД-В2': [50022, "10.1.1.22"], - 'ПРД-К2': [50023, "10.1.1.23"], - 'ПРД-Н3': [50031, "10.1.1.31"], - 'ПРД-В3': [50032, "10.1.1.32"], - 'ПРД-К3': [50033, "10.1.1.33"], - 'ПРД-Н4': [50041, "10.1.1.41"], - 'ПРД-В4': [50042, "10.1.1.42"], - 'ПРД-К4': [50043, "10.1.1.43"], + 'ПРД-Н1': [50011, '10.1.1.11'], + 'ПРД-В1': [50012, '10.1.1.12'], + 'ПРД-К1': [50013, '10.1.1.13'], + 'ПРД-Н2': [50021, '10.1.1.21'], + 'ПРД-В2': [50022, '10.1.1.22'], + 'ПРД-К2': [50023, '10.1.1.23'], + 'ПРД-Н3': [50031, '10.1.1.31'], + 'ПРД-В3': [50032, '10.1.1.32'], + 'ПРД-К3': [50033, '10.1.1.33'], + 'ПРД-Н4': [50041, '10.1.1.41'], + 'ПРД-В4': [50042, '10.1.1.42'], + 'ПРД-К4': [50043, '10.1.1.43'], } # Широковещательный адрес -const BROADCAST_ADDR: Array = [50000, "10.1.1.255"] +const BROADCAST_ADDR: Array = [50000, '10.1.1.255'] # Приватные свойства var status_manager: StatusManager @@ -52,10 +53,11 @@ var ukp0_data: PackedByteArray var ukp1_data: PackedByteArray signal update_isa_ports(_isa_ports: Dictionary) +signal update_socket_status(_sockets_status_bit: int) func _init(name: String): - """Инициализация UDP broadcast клиента""" + '''Инициализация UDP broadcast клиента''' status_manager = StatusManager.new(self) _self_name = name _initialize_socket() @@ -64,16 +66,16 @@ func _init(name: String): _initialize_isa_ports() if not _bind_to_device_address(name): - push_error("Не удалось инициализировать устройство: %s" % name) + push_error('Не удалось инициализировать устройство: %s' % name) func _initialize_socket() -> void: - """Инициализация UDP сокета""" + '''Инициализация UDP сокета''' _socket = SocketUDP.new() func _initialize_timers() -> void: - """Инициализация таймеров""" + '''Инициализация таймеров''' # Таймер для широковещательной рассылки (3 раза в секунду) _timer = Timer.new() _timer.wait_time = 1.0 / 5.0 @@ -81,7 +83,7 @@ func _initialize_timers() -> void: func _initialize_ukp_data() -> void: - """Инициализация данных UKP нулевыми значениями""" + '''Инициализация данных UKP нулевыми значениями''' ukp0_data = PackedByteArray() ukp0_data.resize(UKP_DATA_SIZE) ukp0_data.fill(0) @@ -92,7 +94,7 @@ func _initialize_ukp_data() -> void: func _initialize_isa_ports() -> void: - """Инициализация ISA портов начальными значениями""" + '''Инициализация ISA портов начальными значениями''' # Инициализируем порты, которые могут запрашиваться set_isa_port(0x0106, 0) # DOU2 статус set_isa_port(0x0108, 0) # DOU3 статус @@ -100,9 +102,9 @@ func _initialize_isa_ports() -> void: func _bind_to_device_address(device_name: String) -> bool: - """Привязка к адресу устройства по его имени""" + '''Привязка к адресу устройства по его имени''' if not ADDRESS_LIST.has(device_name): - push_error("Неизвестное устройство: %s" % device_name) + push_error('Неизвестное устройство: %s' % device_name) return false var port: int = ADDRESS_LIST[device_name][0] @@ -112,85 +114,94 @@ 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) + 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)]) + push_error('Не удалось привязаться к %s:%d - %s' % [address, port, error_string(bind_result)]) return false - print("Успешно привязано к %s:%d" % [address, port]) + print('Успешно привязано к %s:%d' % [address, port]) return true 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 уже добавлен в другую сцену") + push_warning('Таймер broadcast уже добавлен в другую сцену') func start_broadcast() -> void: - """Запуск широковещательной рассылки и опроса сокета""" + '''Запуск широковещательной рассылки и опроса сокета''' _timer.start() - print("Широковещательная рассылка запущена") + print('Широковещательная рассылка запущена') func stop_broadcast() -> void: - """Остановка широковещательной рассылки""" + '''Остановка широковещательной рассылки''' _timer.stop() #_poll_timer.stop() - print("Широковещательная рассылка остановлена") + print('Широковещательная рассылка остановлена') func close_udp_unit() -> void: - """Закрытие UDP соединения""" + '''Закрытие UDP соединения''' stop_broadcast() _socket.close() - print("UDP соединение закрыто") + print('UDP соединение закрыто') #region Публичные методы для управления состоянием +func _handle_set_flag_ems_g(set_flag: bool)->void: + '''Установка или сброс 5-го бита (бит 4) в статусе сокетов''' + var current_status = get_sockets_status() + if set_flag: + current_status = current_status | (1 << 4) + print('Установлен флаг загрузки EMS_G)') + set_sockets_status(current_status) + func set_sockets_status(status: int) -> void: - """Установка статуса сокетов""" + '''Установка статуса сокетов''' _sockets_status_bit = status + emit_signal('update_socket_status', _sockets_status_bit, _self_name) func get_sockets_status() -> int: - """Получение статуса сокетов""" + '''Получение статуса сокетов''' return _sockets_status_bit func set_dkm_status(status: int) -> void: - """Установка статуса DKM""" + '''Установка статуса DKM''' _dkm_status_bit = status func get_dkm_status() -> int: - """Получение статуса DKM""" + '''Получение статуса DKM''' return _dkm_status_bit func set_ip_status(status: int)->void: - """Установка статуса ИП""" + '''Установка статуса ИП''' _dry_contact_status_bit = status func get_ip_status()->int: - """Получение статуса ИП""" + '''Получение статуса ИП''' return _dry_contact_status_bit func set_isa_port(port_addr: int, value: int) -> bool: - """Установка значения ISA порта""" + '''Установка значения ISA порта''' if port_addr < 0 or port_addr > 0xFFFF: - push_error("Некорректный адрес порта: 0x%03X" % port_addr) + push_error('Некорректный адрес порта: 0x%03X' % port_addr) return false _isa_ports[port_addr] = value @@ -199,19 +210,19 @@ func set_isa_port(port_addr: int, value: int) -> bool: func get_isa_port(port_addr: int) -> int: - """Получение значения ISA порта""" + '''Получение значения ISA порта''' return _isa_ports.get(port_addr, 0xFFFF) func read_isa_ports(port_addresses: Array) -> Dictionary: - """Чтение значений нескольких ISA портов""" + '''Чтение значений нескольких ISA портов''' var result = {} for port_addr in port_addresses: 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)) + push_error('UDPBroadcast: Некорректный адрес порта: ' + str(port_addr)) return result @@ -220,12 +231,12 @@ func read_isa_ports(port_addresses: Array) -> Dictionary: #region Обработчики таймеров func _on_broadcast_timeout() -> void: - """Обработчик таймера широковещательной рассылки""" + '''Обработчик таймера широковещательной рассылки''' send_broadcast() func _on_poll_timeout() -> void: - """Обработчик таймера опроса сокета""" + '''Обработчик таймера опроса сокета''' _poll_socket() #endregion @@ -233,7 +244,7 @@ func _on_poll_timeout() -> void: #region Работа с сокетом 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) @@ -241,12 +252,12 @@ func send_broadcast() -> bool: if send_result == OK: return true else: - push_error("Ошибка отправки широковещательного пакета") + push_error('Ошибка отправки широковещательного пакета') return false func _poll_socket() -> void: - """Опрос сокета для получения входящих пакетов""" + '''Опрос сокета для получения входящих пакетов''' while _socket.get_available_packet_count() > 0: var packet = _socket.get_packet() var address = _socket.get_packet_ip() @@ -293,7 +304,7 @@ func _form_status_device_packet() -> PackedByteArray: func _encode_packet_header(buffer: PackedByteArray) -> void: - """Кодирование заголовка пакета""" + '''Кодирование заголовка пакета''' # 0-1: Reserved (0x0000) buffer.encode_u16(0, 0) # 2-3: Номер команды @@ -305,7 +316,7 @@ func _encode_packet_header(buffer: PackedByteArray) -> void: func _encode_written_isa_ports(buffer: PackedByteArray, offset: int) -> void: - """Добавление данных о записанных ISA портах в конец пакета""" + '''Добавление данных о записанных ISA портах в конец пакета''' for i in range(_written_isa_ports.size()): var port_data = _written_isa_ports[i] # Добавляем порт и значение (по 2 байта каждый) @@ -315,7 +326,7 @@ func _encode_written_isa_ports(buffer: PackedByteArray, offset: int) -> void: func _encode_device_data(buffer: PackedByteArray) -> void: - """Кодирование данных устройств в пакет""" + '''Кодирование данных устройств в пакет''' # 8-9: Статус сокетов buffer.encode_u16(8, _sockets_status_bit) @@ -331,7 +342,7 @@ func _encode_device_data(buffer: PackedByteArray) -> void: func _encode_ems_g_data(buffer: PackedByteArray) -> void: - """Кодирование данных EMS-G""" + '''Кодирование данных EMS-G''' var ems_g_data = [ 0x9e00, 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0xffff @@ -342,7 +353,7 @@ func _encode_ems_g_data(buffer: PackedByteArray) -> void: func _encode_ug_data(buffer: PackedByteArray) -> void: - """Кодирование данных UG""" + '''Кодирование данных UG''' # D15...D8 = ДКМ (старший байт) buffer.encode_u8(UG_DATA_OFFSET + 1, _dkm_status_bit & 0xFF) # D7...D0 = Сухие контакты (младший байт) @@ -350,7 +361,7 @@ func _encode_ug_data(buffer: PackedByteArray) -> void: func _encode_ukp_data(buffer: PackedByteArray) -> void: - """Кодирование данных UKP""" + '''Кодирование данных UKP''' for i in range(UKP_DATA_SIZE): if i < ukp0_data.size(): buffer[UKP0_DATA_OFFSET + i] = ukp0_data[i] @@ -362,23 +373,23 @@ func _encode_ukp_data(buffer: PackedByteArray) -> void: #region Прямая отправка ответа func _send_immediate_response(address: String, port: int) -> void: - """Немедленная отправка пакета на указанный адрес и порт""" + '''Немедленная отправка пакета на указанный адрес и порт''' var data: PackedByteArray = _form_status_device_packet() _socket.set_broadcast_enabled(false) # Отключаем broadcast для точечной отправки var send_result = _socket.send_to(address, port, data) _socket.set_broadcast_enabled(true) # Включаем обратно для broadcast if send_result == OK: - print("Отправлен немедленный ответ на %s:%d" % [address, port]) + print('Отправлен немедленный ответ на %s:%d' % [address, port]) else: - push_error("Ошибка отправки ответа на %s:%d" % [address, port]) + push_error('Ошибка отправки ответа на %s:%d' % [address, port]) #endregion #region Обработка входящих данных func _handle_received_data(data: PackedByteArray, address: String, port: int) -> void: - """Обработка входящих данных""" + '''Обработка входящих данных''' if not _validate_received_packet(data): return @@ -390,40 +401,36 @@ 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()) + push_warning('Получен слишком короткий пакет: %d байт' % data.size()) return false return true func _get_command_type(data: PackedByteArray) -> int: - """Получение типа команды из данных пакета""" - if data.size() < 7: + '''Получение типа команды из данных пакета''' + if data.size() < 6: return -1 # Используем -1 для обозначения ошибки - - # Определяем длину статуса из заголовка - var status_length = data.decode_u8(7) - if data.size() <= (status_length + 8): - return -1 # Используем -1 для обозначения ошибки - return data.decode_u8(6) func _handle_isa_command(command_code: int, data: PackedByteArray, address: String, port: int) -> void: - """Обработка команд ISA""" + '''Обработка команд ISA''' _last_received_command = command_code # Запоминаем последнюю команду match command_code: CMD_READ_ISA: _handle_read_isa_command(data, address, port) CMD_WRITE_ISA: _handle_write_isa_command(data, address, port) + CMD_FLAG_EMS_G: + _handle_set_flag_ems_g(true) _: - push_warning("Неизвестная команда ISA: %d от %s:%d" % [command_code, address, port]) + push_warning('Неизвестная команда ISA: %d от %s:%d' % [command_code, address, port]) func _handle_read_isa_command(data: PackedByteArray, address: String, port: int) -> void: - """Обработка команды чтения ISA портов""" + '''Обработка команды чтения ISA портов''' var ports = _parse_command_ports(data) if ports.is_empty(): return @@ -439,16 +446,13 @@ func _handle_read_isa_command(data: PackedByteArray, address: String, port: int) func _handle_write_isa_command(data: PackedByteArray, address: String, port: int) -> void: - """Обработка команды записи ISA портов""" + '''Обработка команды записи ISA портов''' 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]) + push_warning('Несоответствие размеров портов и данных от %s:%d' % [address, port]) return - var added_ports = 0 - var updated_ports = 0 - # Обновляем или добавляем записанные порты for i in range(ports.size()): var port_addr = ports[i] @@ -461,15 +465,10 @@ func _handle_write_isa_command(data: PackedByteArray, address: String, port: int if _written_isa_ports[j][0] == port_addr: _written_isa_ports[j][1] = value # Обновляем значение found = true - updated_ports += 1 break if not found: _written_isa_ports.append([port_addr, value]) # Добавляем новый порт - added_ports += 1 - - if added_ports > 0 or updated_ports > 0: - print("Обработана команда записи от %s:%d - добавлено: %d, обновлено: %d портов" % [address, port, added_ports, updated_ports]) print('Принята команда на запись в ISA порты от %s:%d: %s' % [address, port, _isa_ports]) # Немедленная отправка ответа отправителю @@ -479,7 +478,7 @@ func _handle_write_isa_command(data: PackedByteArray, address: String, port: int #region Парсинг команд ISA func _parse_command_ports(data: PackedByteArray) -> Array: - """Парсинг адресов портов из команды""" + '''Парсинг адресов портов из команды''' if data.size() < 7: return [] @@ -488,22 +487,19 @@ func _parse_command_ports(data: PackedByteArray) -> Array: return [] var ports = [] - if data.decode_u8(6) == 0: + if _get_command_type(data) == CMD_READ_ISA: for i in range(ports_count): var port_address = data.decode_u16(8 + i * 2) ports.append(port_address) - print("Порт %d: 0x%03X" % [i, port_address]) - elif data.decode_u8(6) == 1: + elif _get_command_type(data) == CMD_WRITE_ISA: for i in range(ports_count): var port_address = data.decode_u16(8 + i * 4) ports.append(port_address) - print("Порт %d: 0x%03X" % [i, port_address]) - return ports func _parse_command_ports_data(data: PackedByteArray) -> Array: - """Парсинг данных портов из команды записи""" + '''Парсинг данных портов из команды записи''' if data.size() < 8: return [] @@ -515,15 +511,16 @@ func _parse_command_ports_data(data: PackedByteArray) -> Array: var data_offset = 10 # Смещение для данных портов for i in range(ports_count): - if data_offset + i * 2 < data.size(): - var port_data = data.decode_u16(data_offset + i * 2) + if data_offset + i * 4 < data.size(): + var port = data.decode_u16(8 + i * 4) + var port_data = data.decode_u16(data_offset + i * 4) ports_data.append(port_data) - print("Данные порта %d: 0x%02X" % [i, port_data]) + print('Данные порта 0x%02X: 0x%02X' % [port, port_data]) return ports_data func _get_isa_ports_data(ports: Array) -> Array: - """Получение данных указанных ISA портов""" + '''Получение данных указанных ISA портов''' var result = [] for port_address in ports: result.append(get_isa_port(port_address)) @@ -531,7 +528,7 @@ func _get_isa_ports_data(ports: Array) -> Array: func _set_isa_ports_data(ports: Array, data: Array) -> void: - """Установка данных для указанных ISA портов""" + '''Установка данных для указанных ISA портов''' for i in range(ports.size()): if i < data.size(): set_isa_port(ports[i], data[i]) @@ -541,10 +538,10 @@ func _set_isa_ports_data(ports: Array, data: Array) -> void: #region Вложенный класс SocketUDP class SocketUDP extends PacketPeerUDP: - """Расширенный класс UDP сокета с улучшенной обработкой ошибок""" + '''Расширенный класс UDP сокета с улучшенной обработкой ошибок''' func send_to(address: String, port: int, data: PackedByteArray) -> int: - """Отправка данных на указанный адрес и порт""" + '''Отправка данных на указанный адрес и порт''' set_dest_address(address, port) var result = put_packet(data) @@ -558,20 +555,20 @@ class SocketUDP extends PacketPeerUDP: #region Методы управления записанными портами func clear_written_ports() -> void: - """Очистка списка записанных портов (ручное управление)""" + '''Очистка списка записанных портов (ручное управление)''' _written_isa_ports.clear() - print("Список записанных портов очищен") + print('Список записанных портов очищен') func get_written_ports_count() -> int: - """Получение количества записанных портов""" + '''Получение количества записанных портов''' return _written_isa_ports.size() 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) + print('Порт 0x%04X удален из списка записанных' % port_addr) return true return false