diff --git a/project.godot b/project.godot index 797f901a..a72ef926 100644 --- a/project.godot +++ b/project.godot @@ -123,6 +123,7 @@ prd="*res://scripts/prd.gd" navi="*res://scripts/navigation.gd" trassa="*res://scripts/trassa.gd" yemstg="*res://scripts/yemstg.gd" +scouting="*res://scripts/rtr.gd" [debug] diff --git a/scenes/pribor-afsp/pribor-afsp-1.tscn b/scenes/pribor-afsp/pribor-afsp-1.tscn index e99f25c5..f4d16c85 100644 --- a/scenes/pribor-afsp/pribor-afsp-1.tscn +++ b/scenes/pribor-afsp/pribor-afsp-1.tscn @@ -1,7 +1,6 @@ -[gd_scene load_steps=4 format=3 uid="uid://8ll27id5ajq1"] +[gd_scene load_steps=3 format=3 uid="uid://8ll27id5ajq1"] [ext_resource type="Script" uid="uid://d0jwv5opi3mrl" path="res://scenes/pribor-afsp/pribor-afsp.gd" id="1_151rh"] -[ext_resource type="Script" uid="uid://bux47vkw71w1t" path="res://scenes/контроль/request.gd" id="2_4vyhj"] [ext_resource type="PackedScene" uid="uid://dnxq870xf6pkh" path="res://scenes/pribor-afsp/bort-afsp.tscn" id="3_d7iti"] [node name="pribor_afsp" type="Control"] @@ -20,13 +19,6 @@ settings_path_to_url = "Адрес АФСП левого борта" settings_path_to_wait_time = "Период опроса состояния АФСП, секунды" metadata/DeviceName = &"Устройство РР левого борта" -[node name="request" type="HTTPRequest" parent="."] -use_threads = true -script = ExtResource("2_4vyhj") - -[node name="timer" type="Timer" parent="request"] -autostart = true - [node name="bort" parent="." instance=ExtResource("3_d7iti")] layout_mode = 0 offset_left = 24.0 @@ -35,7 +27,4 @@ offset_right = 604.0 offset_bottom = 1216.78 metadata/DeviceName = &"Устройство РР левого борта" -[connection signal="child_entered_tree" from="request" to="request" method="_on_child_entered_tree"] -[connection signal="request_completed" from="request" to="." method="_on_request_completed"] -[connection signal="timeout" from="request/timer" to="request" method="_on_request_timer"] [connection signal="pressed" from="bort" to="." method="_on_item_pressed"] diff --git a/scenes/pribor-afsp/pribor-afsp-2.tscn b/scenes/pribor-afsp/pribor-afsp-2.tscn index 1eae9366..64beffa7 100644 --- a/scenes/pribor-afsp/pribor-afsp-2.tscn +++ b/scenes/pribor-afsp/pribor-afsp-2.tscn @@ -1,7 +1,6 @@ -[gd_scene load_steps=4 format=3 uid="uid://5uv1idl3lqet"] +[gd_scene load_steps=3 format=3 uid="uid://5uv1idl3lqet"] [ext_resource type="Script" uid="uid://d0jwv5opi3mrl" path="res://scenes/pribor-afsp/pribor-afsp.gd" id="1_tfses"] -[ext_resource type="Script" uid="uid://bux47vkw71w1t" path="res://scenes/контроль/request.gd" id="2_miwrd"] [ext_resource type="PackedScene" uid="uid://dnxq870xf6pkh" path="res://scenes/pribor-afsp/bort-afsp.tscn" id="3_xpxb5"] [node name="pribor_afsp" type="Control"] @@ -20,13 +19,6 @@ settings_path_to_url = "Адрес АФСП правого борта" settings_path_to_wait_time = "Период опроса состояния АФСП, секунды" metadata/DeviceName = &"Устройство РР правого борта" -[node name="request" type="HTTPRequest" parent="."] -use_threads = true -script = ExtResource("2_miwrd") - -[node name="timer" type="Timer" parent="request"] -autostart = true - [node name="bort" parent="." instance=ExtResource("3_xpxb5")] layout_mode = 0 offset_left = 12.0 @@ -35,7 +27,4 @@ offset_right = 592.0 offset_bottom = 1130.0 metadata/DeviceName = &"Устройство РР правого борта" -[connection signal="child_entered_tree" from="request" to="request" method="_on_child_entered_tree"] -[connection signal="request_completed" from="request" to="." method="_on_request_completed"] -[connection signal="timeout" from="request/timer" to="request" method="_on_request_timer"] [connection signal="pressed" from="bort" to="." method="_on_item_pressed"] diff --git a/scenes/pribor-afsp/pribor-afsp.gd b/scenes/pribor-afsp/pribor-afsp.gd index 04ef8611..fde8c96b 100644 --- a/scenes/pribor-afsp/pribor-afsp.gd +++ b/scenes/pribor-afsp/pribor-afsp.gd @@ -7,98 +7,164 @@ class_name pribor_afsp extends 'res://scenes/контроль/прибор.gd' @export var settings_path_to_url: String = '' ## Должно быть в ProjectSettings @export var settings_path_to_wait_time: String = '' ## Должно быть в ProjectSettings -signal receive_response_afp_bpo() - static var json_conv: JSON = JSON.new() static var node_maper = load('res://scenes/контроль/node-maper.gd') -static var node_request: Node -static var node_request_timer: Node var ctrl_pos = 0 # SystemName: "gen" DevList - там состояние связи с генератором по кан шине func _ready() -> void: - ProjectSettings.connect('settings_changed', on_settings_chaged) - node_request = $request - node_request_timer = $request/timer - on_settings_chaged() + # Подключаемся к сигналам в зависимости от борта + if this_afsp_name == 'Устройство РР левого борта': + scouting.response_received_left.connect(_on_response_received) + else: + scouting.response_received_right.connect(_on_response_received) + + # При старте отрисовываем последние данные (если есть) + _on_press_device() -func on_settings_chaged(): - node_request.request_url = ProjectSettings.get_setting('application/config/%s' % settings_path_to_url) - node_request_timer.wait_time = float(ProjectSettings.get_setting('application/config/%s' % settings_path_to_wait_time)) - tools.allways(node_request.request_url != null) - - -func _on_request_completed(result, _response_code, headers, body: PackedByteArray): - - var unit_instance: = network.get_unit_instance(unit_name) - - if result != 0: - var rs = node_request.get_result_string(result) - log.error('\"%s\"' % rs) +# Обработка полученного ответа от rtr +func _on_response_received(response: String, success: bool) -> void: + var unit_instance = network.get_unit_instance(unit_name) + + if not success: + log.error('Ошибка запроса: %s' % response) if unit_instance.online: unit_instance.online = false unit_instance.emit_signal('line_changed', unit_instance) + # При ошибке сбрасываем UI в 0 + _reset_ui_to_zero() return - - var json_str: String = body.get_string_from_utf8() - if not json_str: - push_error('не удаётся получить строку utf8 из данных (%s)' % headers) + + # Парсим JSON строку + var parse_result = json_conv.parse(response) + if parse_result != OK: + var err = json_conv.get_error_message() + push_error('разбор json прерван - "%s"' % err) if unit_instance.online: unit_instance.online = false unit_instance.emit_signal('line_changed', unit_instance) + # При ошибке парсинга сбрасываем UI в 0 + _reset_ui_to_zero() return - - var rc: = json_conv.parse(json_str) - if rc != Error.OK: - var err: = json_conv.get_error_message() - push_error('разбор json прерван - \"%s\" (%s)' % [err, headers]) - if unit_instance.online: - unit_instance.online = false - unit_instance.emit_signal('line_changed', unit_instance) - return - + + # Ищем имя прибора в JSON var pp = [] tools.map_solid_to_array(prefix_path, pp) var ret_val = [null, false, 0] var pp_copy = pp.duplicate(true) tools.get_value_by_path(json_conv.data, pp, ret_val) + if not ret_val[1]: push_error('путь %s не найден (%s)' % [pp_copy, ret_val[2]]) + # Если путь не найден, сбрасываем UI в 0 + _reset_ui_to_zero() return - + var received_afsp_name: StringName = ret_val[0] - + + # Проверяем, что ответ от нужного прибора if this_afsp_name != received_afsp_name: - # TODO: Сюда попадаем, когда приходит от другого борта - #push_error('не известное устройство: \"%s\", ожидадось: \"%s\"' % [received_afsp_name, this_afsp_name]) + # Ответ от другого борта - игнорируем return - + + # Обновляем статус online unit_instance.rx_tick = Time.get_ticks_msec() if not unit_instance.online: unit_instance.online = true unit_instance.emit_signal('line_changed', unit_instance) + + # Обновляем UI элементы из JSON + _update_ui(json_conv.data) + +# Обновление интерфейса из JSON данных +func _update_ui(json_data: Dictionary) -> void: + # Конвертер for child in $bort.get_node('konverter/grid').get_children(): - node_maper.map_value_to_item(json_conv.data, child, 'value0') - node_maper.map_value_to_item(json_conv.data, child, 'value1') - node_maper.map_value_to_item(json_conv.data, child, 'value2') - node_maper.map_error_codes_to_item(json_conv.data, child, 'state', 0) + node_maper.map_value_to_item(json_data, child, 'value0') + node_maper.map_value_to_item(json_data, child, 'value1') + node_maper.map_value_to_item(json_data, child, 'value2') + node_maper.map_error_codes_to_item(json_data, child, 'state', 0) + + # Антенна 1 for child in $bort.get_node('antenna1/grid').get_children(): - node_maper.map_value_to_item(json_conv.data, child, 'value0') - node_maper.map_value_to_item(json_conv.data, child, 'value1') - node_maper.map_error_codes_to_item(json_conv.data, child, 'state', 0) + node_maper.map_value_to_item(json_data, child, 'value0') + node_maper.map_value_to_item(json_data, child, 'value1') + node_maper.map_error_codes_to_item(json_data, child, 'state', 0) + + # Антенна 2 for child in $bort.get_node('antenna2/grid').get_children(): - node_maper.map_value_to_item(json_conv.data, child, 'value0') - node_maper.map_value_to_item(json_conv.data, child, 'value1') - node_maper.map_error_codes_to_item(json_conv.data, child, 'state', 0) + node_maper.map_value_to_item(json_data, child, 'value0') + node_maper.map_value_to_item(json_data, child, 'value1') + node_maper.map_error_codes_to_item(json_data, child, 'state', 0) + + # Остальные элементы for child in $bort.get_children(): - node_maper.map_value_to_item(json_conv.data, child, 'value0') - node_maper.map_value_to_item(json_conv.data, child, 'value1') - node_maper.map_error_codes_to_item(json_conv.data, child, 'state', 0) + node_maper.map_value_to_item(json_data, child, 'value0') + node_maper.map_value_to_item(json_data, child, 'value1') + node_maper.map_error_codes_to_item(json_data, child, 'state', 0) - emit_signal('receive_response_afp_bpo') + +# Сброс всех UI элементов в 0 (при отсутствии данных) +func _reset_ui_to_zero() -> void: + # Конвертер + for child in $bort.get_node('konverter/grid').get_children(): + _reset_item_value(child) + + # Антенна 1 + for child in $bort.get_node('antenna1/grid').get_children(): + _reset_item_value(child) + + # Антенна 2 + for child in $bort.get_node('antenna2/grid').get_children(): + _reset_item_value(child) + + # Остальные элементы + for child in $bort.get_children(): + _reset_item_value(child) + + +# Сброс конкретного элемента в 0 +func _reset_item_value(item) -> void: + item.set('value0', 0) + item.set('value1', 0) + item.set('value2', 0) + item.set('state', 0) + + +# Отрисовывание при нажатии на прибор +func _on_press_device() -> void: + # Проверяем, есть ли сохраненные данные в rtr + var has_data = false + var last_response = "" + + if this_afsp_name == 'Устройство РР левого борта': + has_data = scouting.has_left_success() + if has_data: + last_response = scouting.get_left_response() + else: + has_data = scouting.has_right_success() + if has_data: + last_response = scouting.get_right_response() + + # Если есть данные - парсим и отображаем + if has_data and not last_response.is_empty(): + var parse_result = json_conv.parse(last_response) + if parse_result == OK: + # Проверяем, что это данные именно от этого прибора + var pp = [] + tools.map_solid_to_array(prefix_path, pp) + var ret_val = [null, false, 0] + tools.get_value_by_path(json_conv.data, pp, ret_val) + + if ret_val[1] and ret_val[0] == this_afsp_name: + _update_ui(json_conv.data) + return + + # Если данных нет или они не подходят - сбрасываем в 0 + _reset_ui_to_zero() ## Вызывается при клике мыши на элементе. diff --git a/scenes/контроль/request.gd b/scenes/контроль/request.gd index fda7abda..cd860e14 100644 --- a/scenes/контроль/request.gd +++ b/scenes/контроль/request.gd @@ -29,6 +29,7 @@ func get_result_string(result: Result): func _on_request_timer(): + print('_on_request_timer: ', Time.get_ticks_msec()) if HTTPClient.Status.STATUS_DISCONNECTED != get_http_client_status(): return var rc: = request(request_url) diff --git a/scenes/контроль/контроль.tscn b/scenes/контроль/контроль.tscn index e3d87b6b..bb39fe2c 100644 --- a/scenes/контроль/контроль.tscn +++ b/scenes/контроль/контроль.tscn @@ -123,6 +123,8 @@ func _ready(): signaller.conn(signaller.режим_настройки, on_mode_changed) signaller.conn(signaller.режим_эмс, on_mode_changed) signaller.connect('debag_control_pressed', Callable(self, 'on_debag_button_pressed')) + scouting.request_sent_left.connect(_on_request_sent_left.bind($pribor_afsp_1)) + scouting.response_received_left.connect(_on_response_received.bind($pribor_afsp_1)) func online_change_arr(_u, conn_node, pribor_meta): @@ -166,24 +168,17 @@ func on_pribor_press(pribor_path, header_text, pribor_node): pribor.add_to_group('pribor_items') add_child(pribor) update_pribor_items_visibility() - if pribor_node.get_meta('unit_name')[0] == 'уарэп-афсп-левый' or pribor_node.get_meta('unit_name')[0] == 'уарэп-афсп-правый': - pribor.node_request.connect('send_http_request', Callable(self, 'on_send_http_request').bind(pribor_node, pribor.node_request_timer.wait_time)) - pribor.connect('receive_response_afp_bpo', Callable(self, 'on_receive_response_afp_bpo').bind(pribor_node)) - if pribor_node.get_meta('unit_name')[0] == 'уарэп-бпо-1' or pribor_node.get_meta('unit_name')[0] == 'уарэп-бпо-2': - pribor.node_request_1.connect('send_http_request', Callable(self, 'on_send_http_request').bind(pribor_node, pribor.node_request_timer_1.wait_time)) - pribor.connect('receive_response_afp_bpo', Callable(self, 'on_receive_response_afp_bpo').bind(pribor_node)) - #signal receive_response_afp_bpo # Вызов при отправке запроса на селфтест АФСП -func on_send_http_request(node: Node, time_req: float) -> void: +func _on_request_sent_left(_url: String, node: Node) -> void: node.progress_max_value = 100 node.progress_value = 0 node.progress_visible = true - node.timeout_tween = time_req + node.timeout_tween = float(ProjectSettings.get_setting('application/config/%s' % 'Период опроса состояния АФСП, секунды')) -func on_receive_response_afp_bpo(node: Node) -> void: +func _on_response_received(_response: String, _success: bool, node: Node): node.progress_value = 100 node.progress_visible = false diff --git a/scripts/rtr.gd b/scripts/rtr.gd new file mode 100644 index 00000000..a66be793 --- /dev/null +++ b/scripts/rtr.gd @@ -0,0 +1,252 @@ +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 = '' # Последняя ошибка правого борта + +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() +#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() + + # Проверяем успешность ответа + if response_code == 200: + var response_text = body.get_string_from_utf8() + _save_response(response_text) + + # Испускаем сигнал успешного получения ответа + match current_state: + State.REQUESTING_LEFT: + emit_signal('response_received_left', response_text, true) + State.REQUESTING_RIGHT: + 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) + + # Испускаем сигнал с ошибкой + match current_state: + State.REQUESTING_LEFT: + emit_signal('response_received_left', error_msg, false) + State.REQUESTING_RIGHT: + 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) + + # Испускаем сигнал с ошибкой таймаута + match current_state: + State.REQUESTING_LEFT: + emit_signal('response_received_left', timeout_msg, false) + State.REQUESTING_RIGHT: + 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) + + # Испускаем сигнал с ошибкой + match current_state: + State.REQUESTING_LEFT: + emit_signal('response_received_left', error_msg, false) + State.REQUESTING_RIGHT: + 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) + + # Испускаем сигнал с ошибкой + match current_state: + State.REQUESTING_LEFT: + emit_signal('response_received_left', error_msg, false) + State.REQUESTING_RIGHT: + 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 + print('Цикл запросов завершен') +#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 diff --git a/scripts/rtr.gd.uid b/scripts/rtr.gd.uid new file mode 100644 index 00000000..b19ac1eb --- /dev/null +++ b/scripts/rtr.gd.uid @@ -0,0 +1 @@ +uid://ctqcvjqjb1av3