From 667f6d997e44a755f31f45ea4a3a5c35743412ec Mon Sep 17 00:00:00 2001 From: TotMaxim Date: Thu, 20 Nov 2025 12:36:08 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82=D0=BE?= =?UTF-8?q?=D1=80=20=D0=B8=20=D0=B4=D0=BE=D1=80=D0=B0=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=D0=BA=D0=B0=20read=5Fisa,=20write=5Fisa?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.gd | 55 ++++- main.tscn | 5 + udp_broadcast.gd | 613 +++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 564 insertions(+), 109 deletions(-) diff --git a/main.gd b/main.gd index a2bc443..7062361 100644 --- a/main.gd +++ b/main.gd @@ -36,46 +36,53 @@ func _exit_tree(): func on_modify_device(toggled: bool, name_device: String) -> void: if toggled: udp_broadcast = UDPBroadcast.new(name_device) - udp_broadcast.add_timer_to_scene(self) + udp_broadcast.add_timers_to_scene(self) # Исправлено имя метода udp_broadcast.start_broadcast() dictionary_yau07[name_device] = udp_broadcast + print("Устройство %s активировано" % name_device) else: - dictionary_yau07[name_device].close_udp_unit() + 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 _on_modify_sockets(toggled: bool, bit: int, device: String)->void: if dictionary_yau07.has(device): var udp_unit = dictionary_yau07[device] - udp_unit.sockets_status_bit = _set_bit(udp_unit.sockets_status_bit, bit, toggled) + var current_status = udp_unit.get_sockets_status() # Используем геттер + var new_status = _set_bit(current_status, bit, toggled) + udp_unit.set_sockets_status(new_status) # Используем сеттер + print("Статус сокетов устройства %s обновлен: 0x%04X" % [device, new_status]) ## Функция для установки статусов ДКМ в ПРД func _on_modify_dkm(toggled: bool, bit: int, device: String)->void: if dictionary_yau07.has(device): var udp_unit = dictionary_yau07[device] - udp_unit.dkm_status_bit = _set_bit(udp_unit.dkm_status_bit, bit, toggled) - - -func _process(_delta: float) -> void: - for device in dictionary_yau07.keys(): - if dictionary_yau07[device]: - dictionary_yau07[device].poll_receive() + var current_status = udp_unit.get_dkm_status() # Используем геттер + var new_status = _set_bit(current_status, bit, toggled) + udp_unit.set_dkm_status(new_status) # Используем сеттер + print("Статус DKM устройства %s обновлен: 0x%04X" % [device, new_status]) ## Функция для установки значений в ячейках УКП func _on_ukp_submitted(_new_text: String, device: String) -> void: + if not dictionary_yau07.has(device): + return + var prep_temperature_ukp1_array: Array = _on_text_temperature_changed(device, 1) var prep_temperature_ukp2_array: Array = _on_text_temperature_changed(device, 2) var prep_power_ukp1_array: Array = _on_text_power_changed(device, 1) - var prep_power_ukp2_array: Array = _on_text_power_changed(device, 2) + var prep_power_ukp2_array: Array = _on_text_power_changed(device, 2) var combine_data_ukp1: Array = _combine_arrays(prep_temperature_ukp1_array, prep_power_ukp1_array) var combine_data_ukp2: Array = _combine_arrays(prep_temperature_ukp2_array, prep_power_ukp2_array) - _form_buffers(device,combine_data_ukp1, 1, dictionary_yau07) - _form_buffers(device,combine_data_ukp2, 2, dictionary_yau07) + _form_buffers(device, combine_data_ukp1, 1, dictionary_yau07) + _form_buffers(device, combine_data_ukp2, 2, dictionary_yau07) func _on_text_temperature_changed(device: String, number_device: int) -> Array: @@ -167,9 +174,31 @@ static func _form_buffers(device: String, ukp_data: Array, number_device: int, d var udp_unit = dic_yau07[device] if number_device == 1: udp_unit.ukp0_data = buffer + print("Данные UKP0 устройства %s обновлены" % device) elif number_device == 2: udp_unit.ukp1_data = buffer + print("Данные UKP1 устройства %s обновлены" % device) static func _set_bit(bits: int, index: int, val: bool) -> int: return bits | (1 << index) if val else bits & ~(1 << index) + + +## Новый метод для работы с ISA портами (если потребуется) +func read_isa_ports(device: String, port_addresses: Array) -> Dictionary: + """Чтение ISA портов указанного устройства""" + if dictionary_yau07.has(device): + return dictionary_yau07[device].read_isa_ports(port_addresses) + else: + push_error("Устройство %s не найдено" % device) + return {} + + +## Новый метод для записи ISA портов (если потребуется) +func write_isa_port(device: String, port_address: int, value: int) -> bool: + """Запись значения в ISA порт указанного устройства""" + if dictionary_yau07.has(device): + return dictionary_yau07[device].set_isa_port(port_address, value) + else: + push_error("Устройство %s не найдено" % device) + return false diff --git a/main.tscn b/main.tscn index 38ea6d6..a80a9f4 100644 --- a/main.tscn +++ b/main.tscn @@ -581,6 +581,7 @@ metadata/place = 13 metadata/device = "ПРД-В1" [node name="dkm" type="GridContainer" parent="TabContainer/ПРД-В1"] +layout_mode = 0 offset_left = 10.0 offset_top = 201.0 offset_right = 50.0 @@ -639,6 +640,7 @@ metadata/bit = 4 metadata/device = "ПРД-К1" [node name="ukp_1" type="GridContainer" parent="TabContainer/ПРД-К1"] +layout_mode = 0 offset_left = 205.0 offset_top = 7.0 offset_right = 939.0 @@ -732,6 +734,7 @@ metadata/place = 5 metadata/device = "ПРД-К1" [node name="ukp_1_lit_1" type="GridContainer" parent="TabContainer/ПРД-К1"] +layout_mode = 0 offset_left = 205.0 offset_top = 195.0 offset_right = 939.0 @@ -765,6 +768,7 @@ metadata/place = 0 metadata/device = "ПРД-К1" [node name="ukp_2" type="GridContainer" parent="TabContainer/ПРД-К1"] +layout_mode = 0 offset_left = 205.0 offset_top = 103.0 offset_right = 939.0 @@ -858,6 +862,7 @@ metadata/place = 13 metadata/device = "ПРД-К1" [node name="dkm" type="GridContainer" parent="TabContainer/ПРД-К1"] +layout_mode = 0 offset_left = 10.0 offset_top = 201.0 offset_right = 50.0 diff --git a/udp_broadcast.gd b/udp_broadcast.gd index fdd958e..44ebb1e 100644 --- a/udp_broadcast.gd +++ b/udp_broadcast.gd @@ -1,6 +1,24 @@ class_name UDPBroadcast -const addres_list: Dictionary = { +# Константы для размеров данных и смещений +const STATUS_PACKET_SIZE: int = 103 +const HEADER_SIZE: int = 8 +const EMS_G_DATA_OFFSET: int = 9 +const EMS_G_DATA_SIZE: int = 26 # 13 * 2 байта +const UG_DATA_OFFSET: int = 35 +const UKP0_DATA_OFFSET: int = 39 +const UKP1_DATA_OFFSET: int = 71 +const UKP_DATA_SIZE: int = 32 + +# Константы команд ISA +const CMD_READ_ISA: int = 0 +const CMD_WRITE_ISA: int = 1 + +# Минимальный размер пакета для валидации +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"], @@ -14,133 +32,536 @@ const addres_list: Dictionary = { 'ПРД-В4': [50042, "10.1.1.42"], 'ПРД-К4': [50043, "10.1.1.43"], } -const broadcast_addr: Array = [50000, "10.1.1.255"] -#const bind_addr: Array = [50_000, "10.1.1.70"] TODO: Порт по умолчанию + +# Широковещательный адрес +const BROADCAST_ADDR: Array = [50000, "10.1.1.255"] + +# Приватные свойства var _socket: SocketUDP -var self_name: String +var _self_name: String var _timer: Timer -var sockets_status_bit: int = 0 -var dkm_status_bit: int = 0 +var _poll_timer: Timer +var _sockets_status_bit: int = 0 +var _dkm_status_bit: int = 0 +var _isa_ports: Dictionary = {} +var _cmd_number_count: int = 0 +var _written_isa_ports: Array = [] +var _last_received_command: int = -1 + +# Публичные свойства с контролем доступа var ukp0_data: PackedByteArray: - set(v): - ukp0_data = v - print('Произошло Обновление данных ukp0_data') + set(value): + if value.size() == UKP_DATA_SIZE: + ukp0_data = value + print('Данные UKP0 обновлены') -var ukp1_data: PackedByteArray: - set(v): - ukp1_data = v - print('Произошло Обновление данных ukp1_data') + +var ukp1_data: PackedByteArray: + set(value): + if value.size() == UKP_DATA_SIZE: + ukp1_data = value + print('Данные UKP1 обновлены') func _init(name: String): - self_name = name + """Инициализация UDP broadcast клиента""" + _self_name = name + _initialize_socket() + _initialize_ukp_data() + _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 / 3.0 - _timer.timeout.connect(_on_timer_timeout) - ukp0_data.resize(32) - ukp0_data.fill(0) - ukp1_data.resize(32) - ukp1_data.fill(0) + _timer.timeout.connect(_on_broadcast_timeout) - if addres_list.has(name): - var port: int = addres_list[name][0] - var addr: String = addres_list[name][1] - bind_unit_addres(port, addr) - else: - push_error("Неизвестное устройство: %s" % name) + # Таймер для частого опроса сокета (100 раз в секунду) + _poll_timer = Timer.new() + _poll_timer.wait_time = 0.01 + _poll_timer.timeout.connect(_on_poll_timeout) -func bind_unit_addres(port_for_bind: int, addr_for_bind: String) -> void: - var bind_result = _socket.bind(port_for_bind, addr_for_bind) +func _initialize_ukp_data() -> void: + """Инициализация данных UKP нулевыми значениями""" + ukp0_data = PackedByteArray() + ukp0_data.resize(UKP_DATA_SIZE) + ukp0_data.fill(0) + + ukp1_data = PackedByteArray() + ukp1_data.resize(UKP_DATA_SIZE) + ukp1_data.fill(0) + + +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: %s" % [addr_for_bind, error_string(bind_result)]) - - -func start_broadcast() -> void: - _timer.start() - - -func stop_broadcast() -> void: - _timer.stop() - - -func send_broadcast() -> bool: - _socket.set_broadcast_enabled(true) - var data: PackedByteArray = _form_status_packet() - # добавление 11 байтов вперед вохоже - _socket.send_to(broadcast_addr[1], broadcast_addr[0], data) + push_error("Не удалось привязаться к %s:%d - %s" % [address, port, error_string(bind_result)]) + return false + + print("Успешно привязано к %s:%d" % [address, port]) return true -func close_udp_unit() -> void: +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 уже добавлен в другую сцену") + + if not _poll_timer.get_parent(): + parent.add_child(_poll_timer) + elif _poll_timer.get_parent() != parent: + push_warning("Таймер poll уже добавлен в другую сцену") + + +func start_broadcast() -> void: + """Запуск широковещательной рассылки и опроса сокета""" + _timer.start() + _poll_timer.start() + print("Широковещательная рассылка запущена") + + +func stop_broadcast() -> void: + """Остановка широковещательной рассылки""" _timer.stop() + _poll_timer.stop() + print("Широковещательная рассылка остановлена") + + +func close_udp_unit() -> void: + """Закрытие UDP соединения""" + stop_broadcast() _socket.close() + print("UDP соединение закрыто") -func add_timer_to_scene(parent: Node) -> void: - parent.add_child(_timer) +#region Публичные методы для управления состоянием + +func set_sockets_status(status: int) -> void: + """Установка статуса сокетов""" + _sockets_status_bit = status -func poll_receive() -> void: - while _socket.get_available_packet_count() > 0: - var broadcast_packet = _socket.get_packet() - var addr_receive = _socket.get_packet_ip() - #var port_receive = _socket.get_packet_port() - #var parsed_data = _parse_packet(broadcast_packet) - _handle_received_data(broadcast_packet, addr_receive) +func get_sockets_status() -> int: + """Получение статуса сокетов""" + return _sockets_status_bit -func _handle_received_data(data: PackedByteArray, addr: String) -> void: - print("данные от %s: %s" % [addr, data]) - # Здесь можно эмитировать сигналы или обрабатывать данные +func set_dkm_status(status: int) -> void: + """Установка статуса DKM""" + _dkm_status_bit = status -func _on_timer_timeout() -> void: +func get_dkm_status() -> int: + """Получение статуса DKM""" + return _dkm_status_bit + + +func set_isa_port(port_addr: int, value: int) -> bool: + """Установка значения ISA порта""" + if port_addr < 0 or port_addr > 0xFFFF: + push_error("Некорректный адрес порта: 0x%04X" % port_addr) + return false + + _isa_ports[port_addr] = value + return true + + +func get_isa_port(port_addr: int) -> int: + """Получение значения ISA порта""" + return _isa_ports.get(port_addr, 0xFFFF) + + +func read_isa_ports(port_addresses: Array) -> Dictionary: + """Чтение значений нескольких 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)) + + return result + +#endregion + +#region Обработчики таймеров + +func _on_broadcast_timeout() -> void: + """Обработчик таймера широковещательной рассылки""" send_broadcast() -func _form_status_packet() -> PackedByteArray: - var buf = PackedByteArray() - buf.resize(126) # Увеличиваем размер до 126 байт +func _on_poll_timeout() -> void: + """Обработчик таймера опроса сокета""" + _poll_socket() + +#endregion + +#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) - # Заголовок (0-5 байты) - buf.encode_u16(0, 0) # 00 00 - buf.encode_u16(2, 0x0cff) # ff 0c - buf.encode_u16(4, 0) # 00 00 - - # Идентификатор пакета - buf[6] = 103 # 67 - - # Статус устройств - buf[7] = 0 - buf[8] = sockets_status_bit - - # Данные EMS-G (байты 9-34) - var ems_g_data = [0x9e00, 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0x0000, - 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0xffff] - for i in range(13): - buf.encode_u16(9 + i * 2, ems_g_data[i]) - - # Данные UG (байты 35-38) - buf.encode_u8(35, 0x00) - buf.encode_u8(36, dkm_status_bit) - buf.encode_u16(37, 0x059e) # 9e 05 - - # Данные UKP0 (байты 39-70) - for i in ukp0_data.size(): - buf[39 + i] = ukp0_data[i] - # Данные UKP1 (байты 71-102) - for i in ukp1_data.size(): - buf[71 + i] = ukp1_data[i] - return buf + if send_result == OK: + return true + else: + 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() + var port = _socket.get_packet_port() + _handle_received_data(packet, address, port) + +#endregion + +#region Формирование пакета статуса устройства + +func _form_status_device_packet() -> PackedByteArray: + var buffer = PackedByteArray() + + # Базовый размер пакета + дополнительные байты для записанных портов + # + 2 байта для полей команды (код + результат) + var header_size = 8 + var ports_count_size = 1 + var cmd_fields_size = 0 + var isa_ports_size = 0 + if _written_isa_ports.size() > 0: + isa_ports_size = _written_isa_ports.size() * 4 + cmd_fields_size = 4 + var total_size = header_size + STATUS_PACKET_SIZE + ports_count_size + cmd_fields_size + isa_ports_size + + buffer.resize(total_size) + buffer.fill(0) + + _encode_packet_header(buffer) + _encode_device_data(buffer) + print('_written_isa_ports.size(): ', _written_isa_ports.size()) + # Добавляем поле команды после статуса + if _written_isa_ports.size() > 0: + var cmd_offset = header_size + STATUS_PACKET_SIZE + if cmd_offset < buffer.size(): + buffer.encode_u16(cmd_offset, _last_received_command) # код команды (0 для статуса) + if (cmd_offset + 1) < buffer.size(): + buffer.encode_u16(cmd_offset+1, 1) # результат выполнения + + var ports_count_offset = cmd_offset + 2 + # Затем ISA порты + buffer.encode_u8(ports_count_offset, _written_isa_ports.size()) + _encode_written_isa_ports(buffer, ports_count_offset+1) + return buffer + + +func _encode_packet_header(buffer: PackedByteArray) -> void: + """Кодирование заголовка пакета""" + # 0-1: Reserved (0x0000) + buffer.encode_u16(0, 0) + # 2-3: Номер команды + buffer.encode_u16(2, _cmd_number_count) + # 4-5: Reserved (0x0000) + buffer.encode_u16(4, 0) + # 6-7: Длина статуса + buffer.encode_u16(6, STATUS_PACKET_SIZE) + + +func _encode_written_isa_ports(buffer: PackedByteArray, offset: int) -> void: + """Добавление данных о записанных ISA портах в конец пакета""" + for i in range(_written_isa_ports.size()): + var port_data = _written_isa_ports[i] + # Добавляем порт и значение (по 2 байта каждый) + buffer.encode_u16(offset, port_data[0]) + buffer.encode_u16(offset + 2, port_data[1]) + offset += 4 + + +func _encode_device_data(buffer: PackedByteArray) -> void: + """Кодирование данных устройств в пакет""" + # 8-9: Статус сокетов + buffer.encode_u16(8, _sockets_status_bit) + + # 9-34: Данные EMS-G + _encode_ems_g_data(buffer) + + # 35-38: Данные UG + _encode_ug_data(buffer) + + # 39-70: Данные UKP0 + # 71-102: Данные UKP1 + _encode_ukp_data(buffer) + + +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 + ] + + for i in range(ems_g_data.size()): + buffer.encode_u16(EMS_G_DATA_OFFSET + i * 2, ems_g_data[i]) + + +func _encode_ug_data(buffer: PackedByteArray) -> void: + """Кодирование данных UG""" + buffer.encode_u16(UG_DATA_OFFSET, _dkm_status_bit) # Статус DKM + buffer.encode_u16(UG_DATA_OFFSET + 2, 0x059e) # DRY CONTACT + + +func _encode_ukp_data(buffer: PackedByteArray) -> void: + """Кодирование данных UKP""" + for i in range(UKP_DATA_SIZE): + if i < ukp0_data.size(): + buffer[UKP0_DATA_OFFSET + i] = ukp0_data[i] + if i < ukp1_data.size(): + buffer[UKP1_DATA_OFFSET + i] = ukp1_data[i] + +#endregion + +#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]) + else: + 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 + + var command_type = _get_command_type(data) + if command_type == -1: # Проверяем на -1 вместо null + return + _cmd_number_count = (_cmd_number_count + 1) & 0xFFFF + _handle_isa_command(command_type, data, address, port) + + +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() < 7: + 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""" + _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) + _: + push_warning("Неизвестная команда ISA: %d от %s:%d" % [command_code, address, port]) + + +func _handle_read_isa_command(data: PackedByteArray, address: String, port: int) -> void: + """Обработка команды чтения ISA портов""" + var ports = _parse_command_ports(data) + if ports.is_empty(): + return + + var port_data = _get_isa_ports_data(ports) + print('Принята команда на чтение ISA портов от %s:%d: %s' % [address, port, port_data]) + + +func _handle_write_isa_command(data: PackedByteArray, address: String, port: int) -> void: + """Обработка команды записи 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]) + return + + var added_ports = 0 + var updated_ports = 0 + + # Обновляем или добавляем записанные порты + for i in range(ports.size()): + var port_addr = ports[i] + var value = data_to_write[i] + set_isa_port(port_addr, value) + + # Ищем порт в массиве, если есть - обновляем, если нет - добавляем + var found = false + for j in range(_written_isa_ports.size()): + 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]) + # Немедленная отправка ответа отправителю + _send_immediate_response(address, port) +#endregion + +#region Парсинг команд ISA + +func _parse_command_ports(data: PackedByteArray) -> Array: + """Парсинг адресов портов из команды""" + if data.size() < 7: + return [] + + var ports_count = data.decode_u8(7) + if data.size() <= (ports_count + 8): + return [] + + var ports = [] + for i in range(ports_count): + var port_address = data.decode_u16(8 + i * 2) + ports.append(port_address) + print("Порт %d: 0x%04X" % [i, port_address]) + + return ports + + +func _parse_command_ports_data(data: PackedByteArray) -> Array: + """Парсинг данных портов из команды записи""" + if data.size() < 8: + return [] + + var ports_count = data.decode_u8(7) + if data.size() <= (ports_count + 10): + return [] + + var ports_data = [] + 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) + ports_data.append(port_data) + print("Данные порта %d: 0x%04X" % [i, port_data]) + + return ports_data + + +func _get_isa_ports_data(ports: Array) -> Array: + """Получение данных указанных ISA портов""" + var result = [] + for port_address in ports: + result.append(get_isa_port(port_address)) + return result + + +func _set_isa_ports_data(ports: Array, data: Array) -> void: + """Установка данных для указанных ISA портов""" + for i in range(ports.size()): + if i < data.size(): + set_isa_port(ports[i], data[i]) + +#endregion + +#region Вложенный класс SocketUDP + class SocketUDP extends PacketPeerUDP: - func send_to(addr: String, port: int, data: PackedByteArray): - set_dest_address(addr, port) - var rc: = put_packet(data) - if rc != OK: - push_error('%s: %s:%s' % [error_string(rc), addr, port]) + """Расширенный класс 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 + +#region Методы управления записанными портами + +func clear_written_ports() -> void: + """Очистка списка записанных портов (ручное управление)""" + _written_isa_ports.clear() + 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) + return true + return false + +#endregion