504 lines
20 KiB
GDScript
504 lines
20 KiB
GDScript
class_name rtr
|
||
extends Node
|
||
|
||
#region Перечисления
|
||
enum State {
|
||
IDLE,
|
||
REQUESTING_AFSP_LEFT,
|
||
REQUESTING_AFSP_RIGHT,
|
||
REQUESTING_BPO_LEFT,
|
||
REQUESTING_BPO_RIGHT,
|
||
WAITING_TIMEOUT
|
||
}
|
||
#endregion
|
||
|
||
#region Сигналы
|
||
# Сигналы отправки запроса
|
||
signal request_sent_afsp_left(url: String)
|
||
signal request_sent_afsp_right(url: String)
|
||
signal request_sent_bpo_left(url: String)
|
||
signal request_sent_bpo_right(url: String)
|
||
|
||
# Сигналы получения ответа
|
||
signal response_received_afsp_left(response: String, success: bool)
|
||
signal response_received_afsp_right(response: String, success: bool)
|
||
signal response_received_bpo_left(response: String, success: bool)
|
||
signal response_received_bpo_right(response: String, success: bool)
|
||
|
||
# Сигналы исправности (0 - нет связи, 1 - всё пройдено, 2 - есть ошибки)
|
||
signal serviceability_rtr_afsp_left(state: int)
|
||
signal serviceability_rtr_afsp_right(state: int)
|
||
signal serviceability_rtr_bpo(state: int) # Общий для левого и правого БПО
|
||
#endregion
|
||
|
||
#region Константы
|
||
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
|
||
var addresses: Dictionary
|
||
var current_state: State = State.IDLE
|
||
var current_url: String = ''
|
||
var timeout_timer: Timer
|
||
|
||
# Результаты запросов АФСП
|
||
var last_response_afsp_left: String = ''
|
||
var last_response_afsp_right: String = ''
|
||
var last_error_afsp_left: String = ''
|
||
var last_error_afsp_right: String = ''
|
||
|
||
# Результаты запросов БПО
|
||
var last_response_bpo_left: String = ''
|
||
var last_response_bpo_right: String = ''
|
||
var last_error_bpo_left: String = ''
|
||
var last_error_bpo_right: String = ''
|
||
|
||
# Статус online для каждого борта
|
||
var online_afsp_left: bool = false
|
||
var online_afsp_right: bool = false
|
||
var online_bpo_left: bool = false
|
||
var online_bpo_right: bool = false
|
||
|
||
# Состояния исправности для БПО
|
||
var bpo_left_state: int = 0 # 0 - нет связи, 1 - успешно, 2 - ошибка
|
||
var bpo_right_state: int = 0
|
||
|
||
var last_success_time_afsp_left: int = 0
|
||
var last_success_time_afsp_right: int = 0
|
||
var last_success_time_bpo_left: int = 0
|
||
var last_success_time_bpo_right: int = 0
|
||
|
||
var timeout: float
|
||
var current_device_type: String = ''
|
||
#endregion
|
||
|
||
#region Инициализация
|
||
func _ready() -> void:
|
||
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 Публичные методы
|
||
func get_result_string(result: int) -> String:
|
||
return RESULT_STRINGS.get(result, '<неизвестный код результата HTTPRequest - %d>' % result)
|
||
|
||
# Геттеры АФСП
|
||
func get_afsp_left_response() -> String:
|
||
return last_response_afsp_left
|
||
|
||
func get_afsp_right_response() -> String:
|
||
return last_response_afsp_right
|
||
|
||
func get_afsp_left_error() -> String:
|
||
return last_error_afsp_left
|
||
|
||
func get_afsp_right_error() -> String:
|
||
return last_error_afsp_right
|
||
|
||
func has_afsp_left_success() -> bool:
|
||
return not last_response_afsp_left.is_empty()
|
||
|
||
func has_afsp_right_success() -> bool:
|
||
return not last_response_afsp_right.is_empty()
|
||
|
||
func is_afsp_left_online() -> bool:
|
||
return online_afsp_left
|
||
|
||
func is_afsp_right_online() -> bool:
|
||
return online_afsp_right
|
||
|
||
# Геттеры БПО
|
||
func get_bpo_left_response() -> String:
|
||
return last_response_bpo_left
|
||
|
||
func get_bpo_right_response() -> String:
|
||
return last_response_bpo_right
|
||
|
||
func get_bpo_left_error() -> String:
|
||
return last_error_bpo_left
|
||
|
||
func get_bpo_right_error() -> String:
|
||
return last_error_bpo_right
|
||
|
||
func has_bpo_left_success() -> bool:
|
||
return not last_response_bpo_left.is_empty()
|
||
|
||
func has_bpo_right_success() -> bool:
|
||
return not last_response_bpo_right.is_empty()
|
||
|
||
func is_bpo_left_online() -> bool:
|
||
return online_bpo_left
|
||
|
||
func is_bpo_right_online() -> bool:
|
||
return online_bpo_right
|
||
|
||
# Обновление статуса online
|
||
func _update_online_status(device: String, side: String, is_online: bool) -> void:
|
||
var unit_name = ''
|
||
if device == 'afsp':
|
||
unit_name = 'уарэп-афсп-' + ('левый' if side == 'left' else 'правый')
|
||
else:
|
||
unit_name = 'уарэп-бпо-' + ('1' if side == 'left' else '2')
|
||
|
||
var unit_instance = network.get_unit_instance(unit_name)
|
||
if unit_instance:
|
||
unit_instance.rx_tick = Time.get_ticks_msec()
|
||
if unit_instance.online != is_online:
|
||
unit_instance.online = is_online
|
||
unit_instance.emit_signal('line_changed', unit_instance)
|
||
#endregion
|
||
|
||
#region Обработчики сигналов
|
||
func _on_start_contol_device() -> void:
|
||
if current_state != State.IDLE:
|
||
return
|
||
|
||
# Сбрасываем предыдущие результаты
|
||
last_response_afsp_left = ''
|
||
last_response_afsp_right = ''
|
||
last_error_afsp_left = ''
|
||
last_error_afsp_right = ''
|
||
last_response_bpo_left = ''
|
||
last_response_bpo_right = ''
|
||
last_error_bpo_left = ''
|
||
last_error_bpo_right = ''
|
||
|
||
bpo_left_state = 0
|
||
bpo_right_state = 0
|
||
|
||
addresses['Адрес АФСП левого борта'] = ProjectSettings.get_setting('application/config/%s' % 'Адрес АФСП левого борта')
|
||
addresses['Адрес АФСП правого борта'] = ProjectSettings.get_setting('application/config/%s' % 'Адрес АФСП правого борта')
|
||
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_AFSP_LEFT)
|
||
|
||
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 current_time = Time.get_ticks_msec()
|
||
var is_afsp = (current_state == State.REQUESTING_AFSP_LEFT or current_state == State.REQUESTING_AFSP_RIGHT)
|
||
var is_left = (current_state == State.REQUESTING_AFSP_LEFT or current_state == State.REQUESTING_BPO_LEFT)
|
||
var side_str = 'left' if is_left else 'right'
|
||
var device_str = 'afsp' if is_afsp else 'bpo'
|
||
|
||
if response_code == 200:
|
||
var response_text = body.get_string_from_utf8()
|
||
_save_response(response_text)
|
||
|
||
var test_passed_all = _check_all_tests_passed(response_text)
|
||
|
||
_update_success_time(device_str, side_str, current_time)
|
||
_update_online_status(device_str, side_str, true)
|
||
_emit_response_signal(device_str, side_str, response_text, true)
|
||
|
||
var serviceability_state = 1 if test_passed_all else 2
|
||
|
||
if is_afsp:
|
||
_emit_afsp_serviceability_signal(side_str, serviceability_state)
|
||
else:
|
||
_update_bpo_state(side_str, serviceability_state)
|
||
_emit_bpo_serviceability_signal()
|
||
|
||
_on_request_finished()
|
||
else:
|
||
var error_msg = 'HTTP ошибка %d: %s' % [response_code, get_result_string(result)]
|
||
log.message(Logger.ERROR, error_msg)
|
||
_save_error(error_msg)
|
||
|
||
_update_online_status(device_str, side_str, false)
|
||
_emit_response_signal(device_str, side_str, error_msg, false)
|
||
|
||
if is_afsp:
|
||
_emit_afsp_serviceability_signal(side_str, 0)
|
||
else:
|
||
_update_bpo_state(side_str, 0)
|
||
_emit_bpo_serviceability_signal()
|
||
|
||
_on_request_finished()
|
||
|
||
func _update_bpo_state(side: String, state: int) -> void:
|
||
if side == 'left':
|
||
bpo_left_state = state
|
||
else:
|
||
bpo_right_state = state
|
||
|
||
func _calculate_bpo_combined_state() -> int:
|
||
# Если оба не на связи -> 0
|
||
if bpo_left_state == 0 and bpo_right_state == 0:
|
||
return 0
|
||
# Если хоть один с ошибкой -> 2
|
||
if bpo_left_state == 2 or bpo_right_state == 2:
|
||
return 2
|
||
# Если один не на связи (0), а другой успешно (1) -> 1
|
||
if (bpo_left_state == 0 and bpo_right_state == 1) or (bpo_left_state == 1 and bpo_right_state == 0):
|
||
return 1
|
||
# Оба успешно -> 1
|
||
if bpo_left_state == 1 and bpo_right_state == 1:
|
||
return 1
|
||
# По умолчанию
|
||
return 0
|
||
|
||
func _emit_bpo_serviceability_signal() -> void:
|
||
var combined_state = _calculate_bpo_combined_state()
|
||
emit_signal('serviceability_rtr_bpo', combined_state)
|
||
|
||
|
||
func _state_to_string(state: int) -> String:
|
||
match state:
|
||
0: return 'нет связи'
|
||
1: return 'успешно'
|
||
2: return 'ошибка'
|
||
return 'неизвестно'
|
||
|
||
func _emit_afsp_serviceability_signal(side: String, state: int) -> void:
|
||
if side == 'left':
|
||
emit_signal('serviceability_rtr_afsp_left', state)
|
||
else:
|
||
emit_signal('serviceability_rtr_afsp_right', state)
|
||
|
||
func _check_all_tests_passed(response_text: String) -> bool:
|
||
var json = JSON.new()
|
||
if json.parse(response_text) != OK:
|
||
log.message(Logger.ERROR, '[rtr] Ошибка парсинга JSON')
|
||
return false
|
||
|
||
var data = json.data
|
||
var self_tests = data.get('SelfTest', [])
|
||
|
||
var all_passed = true
|
||
|
||
for test in self_tests:
|
||
var system_list = test.get('SystemList', [])
|
||
for system in system_list:
|
||
# Рекурсивно проверяем систему и её подсистемы
|
||
if not _check_system_passed(system):
|
||
all_passed = false
|
||
# Не прерываем цикл, чтобы найти все ошибки
|
||
|
||
return all_passed
|
||
|
||
# Рекурсивная функция для проверки SystemList и вывода ошибок
|
||
func _check_system_passed(system: Dictionary) -> bool:
|
||
var system_passed = true
|
||
|
||
# Проверяем устройства в текущей системе
|
||
var dev_list = system.get('DevList', [])
|
||
for dev in dev_list:
|
||
var test_passed = dev.get('TestPassed', false)
|
||
if not test_passed:
|
||
system_passed = false
|
||
# Формируем читаемое имя устройства для вывода
|
||
var dev_name = dev.get('DevName', 'Неизвестное устройство')
|
||
var dev_id = dev.get('DevId', '?')
|
||
var error_text = dev.get('ErrorText', 'Нет описания ошибки')
|
||
print_debug('[rtr] НАЙДЕНА ОШИБКА! Устройство: %s (ID: %s). TestPassed: false. Причина: %s' % [dev_name, dev_id, error_text])
|
||
|
||
# Рекурсивно проверяем вложенные подсистемы
|
||
var sub_systems = system.get('SystemList', [])
|
||
for sub in sub_systems:
|
||
if not _check_system_passed(sub):
|
||
system_passed = false
|
||
|
||
return system_passed
|
||
|
||
|
||
# Исправленная функция подсчета (использует Dictionary для передачи по ссылке)
|
||
func _count_tests_in_system(system: Dictionary, counters: Dictionary) -> void:
|
||
var dev_list = system.get('DevList', [])
|
||
for dev in dev_list:
|
||
counters['total'] += 1
|
||
if dev.get('TestPassed', false):
|
||
counters['passed'] += 1
|
||
|
||
var sub_systems = system.get('SystemList', [])
|
||
for sub in sub_systems:
|
||
_count_tests_in_system(sub, counters)
|
||
|
||
func _on_timeout() -> void:
|
||
var was_state = current_state
|
||
current_state = State.WAITING_TIMEOUT
|
||
http_request.cancel_request()
|
||
|
||
var is_afsp = (was_state == State.REQUESTING_AFSP_LEFT or was_state == State.REQUESTING_AFSP_RIGHT)
|
||
var is_left = (was_state == State.REQUESTING_AFSP_LEFT or was_state == State.REQUESTING_BPO_LEFT)
|
||
var side_str = 'left' if is_left else 'right'
|
||
var device_str = 'afsp' if is_afsp else 'bpo'
|
||
|
||
var timeout_msg = 'Таймаут %.1f секунд - %s' % [timeout, get_result_string(HTTPRequest.Result.RESULT_TIMEOUT)]
|
||
_save_error(timeout_msg)
|
||
|
||
_update_online_status(device_str, side_str, false)
|
||
_emit_response_signal(device_str, side_str, timeout_msg, false)
|
||
|
||
if is_afsp:
|
||
_emit_afsp_serviceability_signal(side_str, 0)
|
||
else:
|
||
_update_bpo_state(side_str, 0)
|
||
_emit_bpo_serviceability_signal()
|
||
|
||
_on_request_finished()
|
||
#endregion
|
||
|
||
#region Управление запросами
|
||
func _request_next(next_state: State) -> void:
|
||
current_state = next_state
|
||
current_device_type = 'afsp' if (next_state == State.REQUESTING_AFSP_LEFT or next_state == State.REQUESTING_AFSP_RIGHT) else 'bpo'
|
||
|
||
match current_state:
|
||
State.REQUESTING_AFSP_LEFT:
|
||
current_url = addresses.get('Адрес АФСП левого борта', '')
|
||
State.REQUESTING_AFSP_RIGHT:
|
||
current_url = addresses.get('Адрес АФСП правого борта', '')
|
||
State.REQUESTING_BPO_LEFT:
|
||
current_url = addresses.get('Адрес БПО левого борта', '')
|
||
State.REQUESTING_BPO_RIGHT:
|
||
current_url = addresses.get('Адрес БПО правого борта', '')
|
||
State.IDLE:
|
||
return
|
||
|
||
if current_url.is_empty():
|
||
push_error('URL не задан для ', current_state)
|
||
var error_msg = 'URL не задан'
|
||
_save_error(error_msg)
|
||
|
||
var is_afsp = (current_state == State.REQUESTING_AFSP_LEFT or current_state == State.REQUESTING_AFSP_RIGHT)
|
||
var is_left = (current_state == State.REQUESTING_AFSP_LEFT or current_state == State.REQUESTING_BPO_LEFT)
|
||
var side_str = 'left' if is_left else 'right'
|
||
var device_str = 'afsp' if is_afsp else 'bpo'
|
||
|
||
_update_online_status(device_str, side_str, false)
|
||
_emit_response_signal(device_str, side_str, error_msg, false)
|
||
|
||
if is_afsp:
|
||
_emit_afsp_serviceability_signal(side_str, 0)
|
||
else:
|
||
_update_bpo_state(side_str, 0)
|
||
_emit_bpo_serviceability_signal()
|
||
|
||
_on_request_finished()
|
||
return
|
||
|
||
match current_state:
|
||
State.REQUESTING_AFSP_LEFT:
|
||
emit_signal('request_sent_afsp_left', current_url)
|
||
State.REQUESTING_AFSP_RIGHT:
|
||
emit_signal('request_sent_afsp_right', current_url)
|
||
State.REQUESTING_BPO_LEFT:
|
||
emit_signal('request_sent_bpo_left', current_url)
|
||
State.REQUESTING_BPO_RIGHT:
|
||
emit_signal('request_sent_bpo_right', current_url)
|
||
|
||
timeout_timer.start(timeout)
|
||
var error = http_request.request(current_url)
|
||
if error != OK:
|
||
log.message(Logger.ERROR, 'Ошибка запроса: %s' & error)
|
||
var error_msg = 'Ошибка запроса: ' + error_string(error)
|
||
_save_error(error_msg)
|
||
|
||
var is_afsp = (current_state == State.REQUESTING_AFSP_LEFT or current_state == State.REQUESTING_AFSP_RIGHT)
|
||
var is_left = (current_state == State.REQUESTING_AFSP_LEFT or current_state == State.REQUESTING_BPO_LEFT)
|
||
var side_str = 'left' if is_left else 'right'
|
||
var device_str = 'afsp' if is_afsp else 'bpo'
|
||
|
||
_update_online_status(device_str, side_str, false)
|
||
_emit_response_signal(device_str, side_str, error_msg, false)
|
||
|
||
if is_afsp:
|
||
_emit_afsp_serviceability_signal(side_str, 0)
|
||
else:
|
||
_update_bpo_state(side_str, 0)
|
||
_emit_bpo_serviceability_signal()
|
||
|
||
_on_request_finished()
|
||
|
||
func _on_request_finished() -> void:
|
||
match current_state:
|
||
State.REQUESTING_AFSP_LEFT:
|
||
_request_next(State.REQUESTING_AFSP_RIGHT)
|
||
State.REQUESTING_AFSP_RIGHT:
|
||
_request_next(State.REQUESTING_BPO_LEFT)
|
||
State.REQUESTING_BPO_LEFT:
|
||
_request_next(State.REQUESTING_BPO_RIGHT)
|
||
State.REQUESTING_BPO_RIGHT, State.WAITING_TIMEOUT:
|
||
current_state = State.IDLE
|
||
#endregion
|
||
|
||
#region Сохранение результатов
|
||
func _save_response(response: String) -> void:
|
||
match current_state:
|
||
State.REQUESTING_AFSP_LEFT:
|
||
last_response_afsp_left = response
|
||
State.REQUESTING_AFSP_RIGHT:
|
||
last_response_afsp_right = response
|
||
State.REQUESTING_BPO_LEFT:
|
||
last_response_bpo_left = response
|
||
State.REQUESTING_BPO_RIGHT:
|
||
last_response_bpo_right = response
|
||
|
||
func _save_error(error: String) -> void:
|
||
match current_state:
|
||
State.REQUESTING_AFSP_LEFT:
|
||
last_error_afsp_left = error
|
||
State.REQUESTING_AFSP_RIGHT:
|
||
last_error_afsp_right = error
|
||
State.REQUESTING_BPO_LEFT:
|
||
last_error_bpo_left = error
|
||
State.REQUESTING_BPO_RIGHT:
|
||
last_error_bpo_right = error
|
||
|
||
func _update_success_time(device: String, side: String, time: int) -> void:
|
||
if device == 'afsp':
|
||
if side == 'left':
|
||
last_success_time_afsp_left = time
|
||
else:
|
||
last_success_time_afsp_right = time
|
||
else:
|
||
if side == 'left':
|
||
last_success_time_bpo_left = time
|
||
else:
|
||
last_success_time_bpo_right = time
|
||
|
||
func _emit_response_signal(device: String, side: String, response: String, success: bool) -> void:
|
||
if device == 'afsp':
|
||
if side == 'left':
|
||
emit_signal('response_received_afsp_left', response, success)
|
||
else:
|
||
emit_signal('response_received_afsp_right', response, success)
|
||
else:
|
||
if side == 'left':
|
||
emit_signal('response_received_bpo_left', response, success)
|
||
else:
|
||
emit_signal('response_received_bpo_right', response, success)
|
||
#endregion
|