Files
uarep-ctl/scripts/rtr.gd
TotMaxim ec21282ba0
Some checks failed
Локальный тест / test (push) Has been cancelled
Обновление индикатора онлайн в контроле
2026-04-05 16:29:57 +03:00

298 lines
13 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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