class_name rtr extends Node #region Перечисления enum State { IDLE, REQUESTING_LEFT, REQUESTING_RIGHT, WAITING_TIMEOUT } #endregion #region Сигналы # Сигналы отправки запроса signal request_sent_left(url: String) # Отправлен запрос к левому борту signal request_sent_right(url: String) # Отправлен запрос к правому борту # Сигналы получения ответа signal response_received_left(response: String, success: bool) # Получен ответ от левого борта signal response_received_right(response: String, success: bool) # Получен ответ от правого борта #endregion #region Константы # Словарь с текстовым описанием кодов результатов HTTP запросов const RESULT_STRINGS = { HTTPRequest.Result.RESULT_SUCCESS: 'Запрос успешен', HTTPRequest.Result.RESULT_CHUNKED_BODY_SIZE_MISMATCH: 'Несоответствие размеров частей сообщения', HTTPRequest.Result.RESULT_CANT_CONNECT: 'Запрос не выполнен при подключении', HTTPRequest.Result.RESULT_CANT_RESOLVE: 'Запрос не выполнен при разрешении', HTTPRequest.Result.RESULT_CONNECTION_ERROR: 'Запрос не выполнен из-за ошибки соединения', HTTPRequest.Result.RESULT_TLS_HANDSHAKE_ERROR: 'Запрос не выполнен на TLS рукопожатии', HTTPRequest.Result.RESULT_NO_RESPONSE: 'Запрос не имеет ответа (пока)', HTTPRequest.Result.RESULT_BODY_SIZE_LIMIT_EXCEEDED: 'Запрос превысил максимальный размер', HTTPRequest.Result.RESULT_BODY_DECOMPRESS_FAILED: 'Не удалось распаковать сообщение', HTTPRequest.Result.RESULT_REQUEST_FAILED: 'Запрос не удался', HTTPRequest.Result.RESULT_DOWNLOAD_FILE_CANT_OPEN: 'Невозможно открыть файл загрузки', HTTPRequest.Result.RESULT_DOWNLOAD_FILE_WRITE_ERROR: 'Невозможно записать в файл загрузки', HTTPRequest.Result.RESULT_REDIRECT_LIMIT_REACHED: 'Достигнут лимит перенаправлений', HTTPRequest.Result.RESULT_TIMEOUT: 'Время истекло' } #endregion #region Переменные var http_request: HTTPRequest # HTTP клиент для выполнения запросов var addresses: Dictionary # Словарь с URL адресами устройств var current_state: State = State.IDLE # Текущее состояние машины состояний var current_url: String = '' # Текущий URL для запроса var timeout_timer: Timer # Таймер ожидания ответа (30 секунд) # Результаты запросов var last_response_left: String = '' # Последний успешный ответ левого борта var last_response_right: String = '' # Последний успешный ответ правого борта var last_error_left: String = '' # Последняя ошибка левого борта var last_error_right: String = '' # Последняя ошибка правого борта # Статус online для каждого борта var online_left: bool = false var online_right: bool = false # Время последнего успешного ответа var last_success_time_left: int = 0 var last_success_time_right: int = 0 var timeout: float #endregion #region Инициализация func _ready() -> void: # Создаем и настраиваем HTTP клиент http_request = HTTPRequest.new() add_child(http_request) http_request.request_completed.connect(_on_request_completed) # Создаем и настраиваем таймер для обработки таймаутов timeout_timer = Timer.new() add_child(timeout_timer) timeout_timer.timeout.connect(_on_timeout) timeout_timer.one_shot = true # Подписываемся на сигнал запуска управления устройством signaller.conn(signaller.start_contol_device, Callable(self, '_on_start_contol_device')) #endregion #region Публичные методы # Возвращает текстовое описание кода результата HTTP запроса func get_result_string(result: int) -> String: return RESULT_STRINGS.get(result, '<неизвестный код результата HTTPRequest - %d>' % result) # Геттеры для получения результатов запросов func get_left_response() -> String: return last_response_left func get_right_response() -> String: return last_response_right func get_left_error() -> String: return last_error_left func get_right_error() -> String: return last_error_right # Проверка успешности запросов func has_left_success() -> bool: return not last_response_left.is_empty() func has_right_success() -> bool: return not last_response_right.is_empty() # Геттеры для статуса online func is_left_online() -> bool: return online_left func is_right_online() -> bool: return online_right # Обновление статуса online func _update_online_status(side: String, is_online: bool) -> void: var unit_instance # Выбираем нужный экземпляр в зависимости от борта if side == "left": unit_instance = network.get_unit_instance('уарэп-афсп-левый') else: # side == "right" unit_instance = network.get_unit_instance('уарэп-афсп-правый') # Обновляем статус если изменился if unit_instance and unit_instance.online != is_online: unit_instance.online = is_online unit_instance.emit_signal('line_changed', unit_instance) #endregion #region Обработчики сигналов # Сигнал от signaller - начало управления устройством func _on_start_contol_device() -> void: # Если уже выполняется запрос - игнорируем if current_state != State.IDLE: return # Сбрасываем предыдущие результаты last_response_left = '' last_response_right = '' last_error_left = '' last_error_right = '' # Загружаем URL адреса из ProjectSettings addresses['Адрес АФСП левого борта'] = ProjectSettings.get_setting('application/config/%s' % 'Адрес АФСП левого борта') addresses['Адрес АФСП правого борта'] = ProjectSettings.get_setting('application/config/%s' % 'Адрес АФСП правого борта') timeout = float(ProjectSettings.get_setting('application/config/%s' % 'Период опроса состояния АФСП, секунды')) # Начинаем с запроса к левому борту _request_next(State.REQUESTING_LEFT) # Сигнал от HTTPRequest - получен ответ на запрос func _on_request_completed(result: int, response_code: int, _headers: PackedStringArray, body: PackedByteArray) -> void: # Если таймаут уже сработал - игнорируем ответ if current_state == State.WAITING_TIMEOUT: return # Останавливаем таймер ожидания timeout_timer.stop() var is_left = (current_state == State.REQUESTING_LEFT) var current_time = Time.get_ticks_msec() # Проверяем успешность ответа if response_code == 200: var response_text = body.get_string_from_utf8() _save_response(response_text) # Обновляем статус online if is_left: last_success_time_left = current_time _update_online_status("left", true) emit_signal('response_received_left', response_text, true) else: last_success_time_right = current_time _update_online_status("right", true) emit_signal('response_received_right', response_text, true) _on_request_finished() else: var error_msg = 'HTTP ошибка %d: %s' % [response_code, get_result_string(result)] _save_error(error_msg) # Обновляем статус online (ошибка) if is_left: _update_online_status("left", false) emit_signal('response_received_left', error_msg, false) else: _update_online_status("right", false) emit_signal('response_received_right', error_msg, false) _on_request_finished() # Сигнал от таймера - истекло время ожидания ответа func _on_timeout() -> void: current_state = State.WAITING_TIMEOUT http_request.cancel_request() var timeout_msg = 'Таймаут %.1f секунд - %s' % [timeout, get_result_string(HTTPRequest.Result.RESULT_TIMEOUT)] _save_error(timeout_msg) var is_left = (current_state == State.REQUESTING_LEFT) # Обновляем статус online (таймаут = offline) if is_left: _update_online_status("left", false) emit_signal('response_received_left', timeout_msg, false) else: _update_online_status("right", false) emit_signal('response_received_right', timeout_msg, false) _on_request_finished() #endregion #region Управление запросами # Переход к следующему запросу в очереди func _request_next(next_state: State) -> void: current_state = next_state # Определяем URL для текущего состояния match current_state: State.REQUESTING_LEFT: current_url = addresses.get('Адрес АФСП левого борта', '') State.REQUESTING_RIGHT: current_url = addresses.get('Адрес АФСП правого борта', '') State.IDLE: return # Проверяем, задан ли URL if current_url.is_empty(): print('URL не задан для ', current_state) var error_msg = 'URL не задан' _save_error(error_msg) var is_left = (current_state == State.REQUESTING_LEFT) # Испускаем сигнал с ошибкой if is_left: _update_online_status("left", false) emit_signal('response_received_left', error_msg, false) else: _update_online_status("right", false) emit_signal('response_received_right', error_msg, false) _on_request_finished() return # Испускаем сигнал отправки запроса match current_state: State.REQUESTING_LEFT: emit_signal('request_sent_left', current_url) State.REQUESTING_RIGHT: emit_signal('request_sent_right', current_url) # Запускаем таймер и отправляем запрос timeout_timer.start(timeout) var error = http_request.request(current_url) if error != OK: print('Ошибка запроса: ', error_string(error)) var error_msg = 'Ошибка запроса: ' + error_string(error) _save_error(error_msg) var is_left = (current_state == State.REQUESTING_LEFT) # Испускаем сигнал с ошибкой if is_left: _update_online_status("left", false) emit_signal('response_received_left', error_msg, false) else: _update_online_status("right", false) emit_signal('response_received_right', error_msg, false) _on_request_finished() # Обработка завершения текущего запроса (успех/ошибка/таймаут) func _on_request_finished() -> void: match current_state: State.REQUESTING_LEFT: # После левого борта переходим к правому _request_next(State.REQUESTING_RIGHT) State.REQUESTING_RIGHT, State.WAITING_TIMEOUT: # После правого борта или таймаута возвращаемся в IDLE current_state = State.IDLE #endregion #region Сохранение результатов # Сохраняет успешный ответ от устройства func _save_response(response: String) -> void: match current_state: State.REQUESTING_LEFT: last_response_left = response State.REQUESTING_RIGHT: last_response_right = response # Сохраняет ошибку от устройства func _save_error(error: String) -> void: match current_state: State.REQUESTING_LEFT: last_error_left = error State.REQUESTING_RIGHT: last_error_right = error #endregion