This commit is contained in:
shurongsen 2025-01-02 02:25:42 +08:00
parent 4a8c31ca20
commit f12d886385
10 changed files with 4490 additions and 28 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

11
common/Global.gd Normal file
View File

@ -0,0 +1,11 @@
extends Node
var EgoVehicle3D = null
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
pass

132
common/websocket.gd Normal file
View File

@ -0,0 +1,132 @@
extends Node
class_name WebSocketClient # WebSocket客户端类
# 导出属性
@export var handshake_headers: PackedStringArray # 握手头部信息
@export var supported_protocols: PackedStringArray # 支持的协议列表
var tls_options: TLSOptions = null # TLS选项用于加密通信
# WebSocket连接实例和上一个状态
var socket = WebSocketPeer.new()
var last_state = WebSocketPeer.STATE_CLOSED
# 默认URL
#var default_url = "ws://192.168.4.88:9091"#本地
var default_url = "ws://100.86.230.57:9090"#工作站
#var default_url = "ws://100.118.150.125:9090"#真机
# 信号
signal connected_to_server() # 连接成功信号
signal connection_closed() # 连接关闭信号
signal message_received(message: Variant) # 收到消息信号
signal ret_msg(data)
func _ready():
link()
pass
func link():
# 默认连接到 default_url
socket.connect_to_url(default_url)
# 连接信号到处理函数
socket.connect("message_received", Callable(self, "_on_message_received"))
socket.connect("connected_to_server", Callable(self, "_on_connected_to_server"))
socket.connect("connection_closed", Callable(self, "_on_closed"))
socket.outbound_buffer_size = 655350000
socket.max_queued_packets = 20480000
socket.inbound_buffer_size = 655350000
set_process(true)
# 连接到指定的WebSocket URL
func connect_to_url(url) -> int:
socket.supported_protocols = supported_protocols
socket.handshake_headers = handshake_headers
var err = socket.connect_to_url(url, tls_options)
if err != OK:
return err
return OK
# 发送消息
func send_msg(message) -> int:
if typeof(message) == TYPE_STRING:
#print("Sending text message:", message) # 打印要发送的文本消息
return socket.send_text(message) # 如果消息是字符串,发送文本消息
else:
var message_bytes = var_to_bytes(message) # 将消息转换为字节
#print("Sending binary message:", str(message)) # 打印要发送的二进制消息
return socket.send(message_bytes) # 否则发送二进制消息
# 获取消息并打印到终端
func get_msg() -> Variant:
if socket.get_available_packet_count() < 1:
return null # 如果没有可用消息,返回null
var pkt = socket.get_packet() # 获取消息包
if socket.was_string_packet():
var message = pkt.get_string_from_utf8() # 如果是字符串消息转换为UTF-8字符串
#print("Received message: ", message) # 打印收到的消息到终端
return message
else:
var message = bytes_to_var(pkt) # 转换为变量
#print("Received binary message") # 打印收到的二进制消息到终端
return message
# 关闭连接
func close(code := 1000, reason := "") -> void:
socket.close(code, reason)
last_state = socket.get_ready_state()
# 清除连接
func clear() -> void:
socket = WebSocketPeer.new()
last_state = socket.get_ready_state()
# 获取WebSocket连接实例
func get_socket() -> WebSocketPeer:
return socket
# 轮询连接状态并发送/接收消息
func poll() -> void:
if socket.get_ready_state() != socket.STATE_CLOSED:
socket.poll()
var state = socket.get_ready_state()
if last_state != state:
last_state = state
if state == socket.STATE_OPEN:
print("已连接到主机.")
emit_signal("connected_to_server")
elif state == socket.STATE_CLOSED:
print("已断开与主机的连接.")
emit_signal("connection_closed")
elif state == socket.STATE_CONNECTING:
print("正在连接到主机.")
elif state == socket.STATE_CLOSING:
print("使用套接字流关闭.")
while socket.get_ready_state() == socket.STATE_OPEN and socket.get_available_packet_count() > 0:
var msg_data = get_msg()
#ret_msg.emit(msg_data)
emit_signal("message_received",str_to_var(msg_data))
# 每帧调用poll()函数
func _process(delta):
poll()
# 处理接收到的消息
func _on_message_received(message: Variant):
print("WebSocket message received: %s" % str(message))
# 处理连接成功
func _on_connected_to_server():
print("Connected to WebSocket server")
# 处理连接关闭
func _on_closed():
print("WebSocket connection closed")
set_process(false) # 停止处理

View File

@ -1,8 +1,9 @@
[gd_scene load_steps=23 format=3 uid="uid://dwv63mpx0y857"]
[gd_scene load_steps=27 format=3 uid="uid://dwv63mpx0y857"]
[ext_resource type="Script" path="res://main.gd" id="1_7mntx"]
[ext_resource type="Texture2D" uid="uid://ctqyt5yh6uy78" path="res://res/bg/bg_dropdown_menu_active.png" id="1_rrn5x"]
[ext_resource type="Texture2D" uid="uid://bkau0qkht0c8j" path="res://res/icon/icon_more_option_default.png" id="2_kb1k2"]
[ext_resource type="PackedScene" uid="uid://bo8w4npgct57n" path="res://res3D/ego_vehicle.tscn" id="2_may5t"]
[ext_resource type="Texture2D" uid="uid://bjhn4gko31i06" path="res://res/icon/icon_language_setting_default.png" id="2_nfbe6"]
[ext_resource type="Texture2D" uid="uid://bnl4wc12yukw0" path="res://res/icon/icon_terminal_default.png" id="3_2tet8"]
[ext_resource type="Script" path="res://Modules/ButtonItem.gd" id="3_8cofn"]
@ -22,6 +23,9 @@
[ext_resource type="Texture2D" uid="uid://bkftfoedt3036" path="res://res/icon/icon_safebelt_yes.png" id="14_1xl0y"]
[ext_resource type="PackedScene" uid="uid://dh23at8cqmfe8" path="res://Modules/station_view_new.tscn" id="15_nyjwy"]
[ext_resource type="PackedScene" uid="uid://dwjj0vio2iyaf" path="res://Modules/tunnel_panel.tscn" id="17_6c7jw"]
[ext_resource type="Script" path="res://main_perspective.gd" id="17_cuo7m"]
[ext_resource type="Script" path="res://main_perspectiveinfo.gd" id="17_e06tv"]
[ext_resource type="Environment" uid="uid://c5ksuke04sr07" path="res://res3D/mainEnvironment.tres" id="25_vr8se"]
[node name="Control" type="Control"]
layout_mode = 3
@ -32,6 +36,8 @@ grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_7mntx")
[node name="EgoVehicle" parent="." instance=ExtResource("2_may5t")]
[node name="Background" type="ColorRect" parent="."]
layout_mode = 1
anchors_preset = 15
@ -178,13 +184,22 @@ theme_override_constants/separation = 68
unique_name_in_owner = true
layout_mode = 2
[node name="StatePanel" type="Panel" parent="MarginContainer/VBoxContainer/HBoxContainer2"]
unique_name_in_owner = true
[node name="MainPerspectiveinfo" type="Panel" parent="MarginContainer/VBoxContainer/HBoxContainer2"]
custom_minimum_size = Vector2(1346, 0)
layout_mode = 2
size_flags_horizontal = 3
script = ExtResource("17_e06tv")
[node name="StateView" type="Control" parent="MarginContainer/VBoxContainer/HBoxContainer2/StatePanel"]
[node name="MainPerspective" type="TextureRect" parent="MarginContainer/VBoxContainer/HBoxContainer2/MainPerspectiveinfo"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("17_cuo7m")
[node name="StateView" type="Control" parent="MarginContainer/VBoxContainer/HBoxContainer2/MainPerspectiveinfo"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
@ -192,7 +207,7 @@ anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/VBoxContainer/HBoxContainer2/StatePanel/StateView"]
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/VBoxContainer/HBoxContainer2/MainPerspectiveinfo/StateView"]
layout_mode = 0
offset_left = 50.5
offset_top = 77.5
@ -200,11 +215,11 @@ offset_right = 393.5
offset_bottom = 375.5
theme_override_constants/separation = 20
[node name="Time" type="TextureRect" parent="MarginContainer/VBoxContainer/HBoxContainer2/StatePanel/StateView/VBoxContainer"]
[node name="Time" type="TextureRect" parent="MarginContainer/VBoxContainer/HBoxContainer2/MainPerspectiveinfo/StateView/VBoxContainer"]
layout_mode = 2
texture = ExtResource("12_bp84h")
[node name="MarginContainer" type="MarginContainer" parent="MarginContainer/VBoxContainer/HBoxContainer2/StatePanel/StateView/VBoxContainer/Time"]
[node name="MarginContainer" type="MarginContainer" parent="MarginContainer/VBoxContainer/HBoxContainer2/MainPerspectiveinfo/StateView/VBoxContainer/Time"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
@ -214,17 +229,17 @@ grow_vertical = 2
theme_override_constants/margin_left = 30
theme_override_constants/margin_right = 30
[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer/HBoxContainer2/StatePanel/StateView/VBoxContainer/Time/MarginContainer"]
[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer/HBoxContainer2/MainPerspectiveinfo/StateView/VBoxContainer/Time/MarginContainer"]
layout_mode = 2
[node name="Time" type="Label" parent="MarginContainer/VBoxContainer/HBoxContainer2/StatePanel/StateView/VBoxContainer/Time/MarginContainer/HBoxContainer"]
[node name="Time" type="Label" parent="MarginContainer/VBoxContainer/HBoxContainer2/MainPerspectiveinfo/StateView/VBoxContainer/Time/MarginContainer/HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
theme_override_colors/font_color = Color(0.952941, 0.952941, 0.952941, 1)
theme_override_font_sizes/font_size = 36
text = "16:40"
[node name="Date" type="Label" parent="MarginContainer/VBoxContainer/HBoxContainer2/StatePanel/StateView/VBoxContainer/Time/MarginContainer/HBoxContainer"]
[node name="Date" type="Label" parent="MarginContainer/VBoxContainer/HBoxContainer2/MainPerspectiveinfo/StateView/VBoxContainer/Time/MarginContainer/HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
theme_override_colors/font_color = Color(0.952941, 0.952941, 0.952941, 1)
@ -232,11 +247,11 @@ theme_override_font_sizes/font_size = 36
text = "11月5日"
horizontal_alignment = 2
[node name="Speed" type="TextureRect" parent="MarginContainer/VBoxContainer/HBoxContainer2/StatePanel/StateView/VBoxContainer"]
[node name="Speed" type="TextureRect" parent="MarginContainer/VBoxContainer/HBoxContainer2/MainPerspectiveinfo/StateView/VBoxContainer"]
layout_mode = 2
texture = ExtResource("13_ko5d5")
[node name="MarginContainer" type="MarginContainer" parent="MarginContainer/VBoxContainer/HBoxContainer2/StatePanel/StateView/VBoxContainer/Speed"]
[node name="MarginContainer" type="MarginContainer" parent="MarginContainer/VBoxContainer/HBoxContainer2/MainPerspectiveinfo/StateView/VBoxContainer/Speed"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
@ -246,15 +261,15 @@ grow_vertical = 2
theme_override_constants/margin_top = 30
theme_override_constants/margin_bottom = 30
[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer/HBoxContainer2/StatePanel/StateView/VBoxContainer/Speed/MarginContainer"]
[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer/HBoxContainer2/MainPerspectiveinfo/StateView/VBoxContainer/Speed/MarginContainer"]
layout_mode = 2
theme_override_constants/separation = 0
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/VBoxContainer/HBoxContainer2/StatePanel/StateView/VBoxContainer/Speed/MarginContainer/HBoxContainer"]
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/VBoxContainer/HBoxContainer2/MainPerspectiveinfo/StateView/VBoxContainer/Speed/MarginContainer/HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
[node name="Label" type="Label" parent="MarginContainer/VBoxContainer/HBoxContainer2/StatePanel/StateView/VBoxContainer/Speed/MarginContainer/HBoxContainer/VBoxContainer"]
[node name="Label" type="Label" parent="MarginContainer/VBoxContainer/HBoxContainer2/MainPerspectiveinfo/StateView/VBoxContainer/Speed/MarginContainer/HBoxContainer/VBoxContainer"]
layout_mode = 2
size_flags_vertical = 3
theme_override_colors/font_color = Color(0.584314, 0.584314, 0.584314, 1)
@ -262,7 +277,7 @@ theme_override_font_sizes/font_size = 28
text = "时速"
horizontal_alignment = 1
[node name="Label3" type="Label" parent="MarginContainer/VBoxContainer/HBoxContainer2/StatePanel/StateView/VBoxContainer/Speed/MarginContainer/HBoxContainer/VBoxContainer"]
[node name="Speed" type="Label" parent="MarginContainer/VBoxContainer/HBoxContainer2/MainPerspectiveinfo/StateView/VBoxContainer/Speed/MarginContainer/HBoxContainer/VBoxContainer"]
layout_mode = 2
size_flags_vertical = 3
theme_override_colors/font_color = Color(0.952941, 0.952941, 0.952941, 1)
@ -272,7 +287,7 @@ text = "40
horizontal_alignment = 1
vertical_alignment = 2
[node name="Label2" type="Label" parent="MarginContainer/VBoxContainer/HBoxContainer2/StatePanel/StateView/VBoxContainer/Speed/MarginContainer/HBoxContainer/VBoxContainer"]
[node name="Label2" type="Label" parent="MarginContainer/VBoxContainer/HBoxContainer2/MainPerspectiveinfo/StateView/VBoxContainer/Speed/MarginContainer/HBoxContainer/VBoxContainer"]
layout_mode = 2
size_flags_vertical = 3
theme_override_colors/font_color = Color(0.584314, 0.584314, 0.584314, 1)
@ -281,11 +296,11 @@ text = "km/h"
horizontal_alignment = 1
vertical_alignment = 2
[node name="VBoxContainer2" type="VBoxContainer" parent="MarginContainer/VBoxContainer/HBoxContainer2/StatePanel/StateView/VBoxContainer/Speed/MarginContainer/HBoxContainer"]
[node name="VBoxContainer2" type="VBoxContainer" parent="MarginContainer/VBoxContainer/HBoxContainer2/MainPerspectiveinfo/StateView/VBoxContainer/Speed/MarginContainer/HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
[node name="Label" type="Label" parent="MarginContainer/VBoxContainer/HBoxContainer2/StatePanel/StateView/VBoxContainer/Speed/MarginContainer/HBoxContainer/VBoxContainer2"]
[node name="Label" type="Label" parent="MarginContainer/VBoxContainer/HBoxContainer2/MainPerspectiveinfo/StateView/VBoxContainer/Speed/MarginContainer/HBoxContainer/VBoxContainer2"]
layout_mode = 2
size_flags_vertical = 3
theme_override_colors/font_color = Color(0.584314, 0.584314, 0.584314, 1)
@ -293,14 +308,14 @@ theme_override_font_sizes/font_size = 28
text = "安全带"
horizontal_alignment = 1
[node name="SafetyBeltTexture" type="TextureRect" parent="MarginContainer/VBoxContainer/HBoxContainer2/StatePanel/StateView/VBoxContainer/Speed/MarginContainer/HBoxContainer/VBoxContainer2"]
[node name="SeatBeltTexture" type="TextureRect" parent="MarginContainer/VBoxContainer/HBoxContainer2/MainPerspectiveinfo/StateView/VBoxContainer/Speed/MarginContainer/HBoxContainer/VBoxContainer2"]
layout_mode = 2
size_flags_vertical = 3
texture = ExtResource("14_1xl0y")
expand_mode = 1
stretch_mode = 5
[node name="SafetyBeltText" type="Label" parent="MarginContainer/VBoxContainer/HBoxContainer2/StatePanel/StateView/VBoxContainer/Speed/MarginContainer/HBoxContainer/VBoxContainer2"]
[node name="SeatBeltText" type="Label" parent="MarginContainer/VBoxContainer/HBoxContainer2/MainPerspectiveinfo/StateView/VBoxContainer/Speed/MarginContainer/HBoxContainer/VBoxContainer2"]
layout_mode = 2
size_flags_vertical = 3
theme_override_colors/font_color = Color(0.584314, 0.584314, 0.584314, 1)
@ -309,7 +324,7 @@ text = "已系好"
horizontal_alignment = 1
vertical_alignment = 2
[node name="StateTab" type="TabContainer" parent="MarginContainer/VBoxContainer/HBoxContainer2/StatePanel"]
[node name="StateTab" type="TabContainer" parent="MarginContainer/VBoxContainer/HBoxContainer2/MainPerspectiveinfo"]
unique_name_in_owner = true
visible = false
layout_mode = 1
@ -321,13 +336,13 @@ grow_vertical = 2
current_tab = 1
tabs_visible = false
[node name="LanguagePanelHorizontal" parent="MarginContainer/VBoxContainer/HBoxContainer2/StatePanel/StateTab" instance=ExtResource("12_g3kjd")]
[node name="LanguagePanelHorizontal" parent="MarginContainer/VBoxContainer/HBoxContainer2/MainPerspectiveinfo/StateTab" instance=ExtResource("12_g3kjd")]
unique_name_in_owner = true
visible = false
layout_mode = 2
metadata/_tab_index = 0
[node name="TunnelPanel" parent="MarginContainer/VBoxContainer/HBoxContainer2/StatePanel/StateTab" instance=ExtResource("17_6c7jw")]
[node name="TunnelPanel" parent="MarginContainer/VBoxContainer/HBoxContainer2/MainPerspectiveinfo/StateTab" instance=ExtResource("17_6c7jw")]
layout_mode = 2
metadata/_tab_index = 1
@ -369,14 +384,17 @@ grow_horizontal = 2
grow_vertical = 2
texture = ExtResource("3_kegs1")
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
environment = ExtResource("25_vr8se")
[connection signal="pressed" from="MarginContainer/VBoxContainer/HBoxContainer/MoreOptionPanel/HBoxContainer/Language" to="." method="_on_language_pressed"]
[connection signal="pressed" from="MarginContainer/VBoxContainer/HBoxContainer/MoreOptionPanel/HBoxContainer/Tunnel" to="." method="_on_tunnel_pressed"]
[connection signal="self_click" from="MarginContainer/VBoxContainer/HBoxContainer/MoreOptionPanel/HBoxContainer/ADView" to="." method="_on_ad_view_self_click"]
[connection signal="pressed" from="MarginContainer/VBoxContainer/HBoxContainer/MoreOption" to="." method="_on_more_option_pressed"]
[connection signal="click" from="MarginContainer/VBoxContainer/HBoxContainer2/StatePanel/StateTab/LanguagePanelHorizontal" to="." method="_on_language_panel_horizontal_click"]
[connection signal="close_requeset" from="MarginContainer/VBoxContainer/HBoxContainer2/StatePanel/StateTab/LanguagePanelHorizontal" to="." method="_on_language_close_requeset"]
[connection signal="request_hide" from="MarginContainer/VBoxContainer/HBoxContainer2/StatePanel/StateTab/TunnelPanel" to="." method="_on_tunnel_panel_request_hide"]
[connection signal="search_click" from="MarginContainer/VBoxContainer/HBoxContainer2/StatePanel/StateTab/TunnelPanel" to="." method="_on_tunnel_panel_search_click"]
[connection signal="click" from="MarginContainer/VBoxContainer/HBoxContainer2/MainPerspectiveinfo/StateTab/LanguagePanelHorizontal" to="." method="_on_language_panel_horizontal_click"]
[connection signal="close_requeset" from="MarginContainer/VBoxContainer/HBoxContainer2/MainPerspectiveinfo/StateTab/LanguagePanelHorizontal" to="." method="_on_language_close_requeset"]
[connection signal="request_hide" from="MarginContainer/VBoxContainer/HBoxContainer2/MainPerspectiveinfo/StateTab/TunnelPanel" to="." method="_on_tunnel_panel_request_hide"]
[connection signal="search_click" from="MarginContainer/VBoxContainer/HBoxContainer2/MainPerspectiveinfo/StateTab/TunnelPanel" to="." method="_on_tunnel_panel_search_click"]
[connection signal="click" from="MarginContainer/VBoxContainer/HBoxContainer2/ADPanel/ADTab/LanguageVertical" to="." method="_on_language_vertical_click"]
[connection signal="close_requeset" from="MarginContainer/VBoxContainer/HBoxContainer2/ADPanel/ADTab/LanguageVertical" to="." method="_on_language_close_requeset"]
[connection signal="request_hide" from="MarginContainer/VBoxContainer/HBoxContainer2/ADPanel/ADTab/TunnelPanel" to="." method="_on_tunnel_panel_request_hide"]

13
main_perspective.gd Normal file
View File

@ -0,0 +1,13 @@
extends TextureRect
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
texture = Global.EgoVehicle3D.MainSubGetTexture()
pass # Replace with function body.
func _physics_process(delta: float) -> void:
Global.EgoVehicle3D.main_sub_viewport.size = size
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
pass

18
main_perspectiveinfo.gd Normal file
View File

@ -0,0 +1,18 @@
extends Panel
func _ready() -> void:
Websocket.connected_to_server.connect(connected_to_server)
Websocket.message_received.connect(message_received)
func connected_to_server():
pass
func message_received(data:Dictionary):
if data.is_empty():return
if !data.has("topic"):return
match data.topic:
pass
func _process(delta: float) -> void:
pass

View File

@ -11,9 +11,15 @@ config_version=5
[application]
config/name="Large Screen"
run/main_scene="res://main.tscn"
config/features=PackedStringArray("4.3", "Mobile")
config/icon="res://icon.svg"
[autoload]
Global="*res://common/Global.gd"
Websocket="*res://common/websocket.gd"
[display]
window/size/viewport_width=3840

13
res3D/ego_vehicle.gd Normal file
View File

@ -0,0 +1,13 @@
extends Node3D
@onready var view_camera: Camera3D = %ViewCamera
@onready var side_display_sub_viewport: SubViewport = $SideDisplaySubViewport
@onready var main_sub_viewport: SubViewport = $MainSubViewport
func _init() -> void:
Global.EgoVehicle3D = self
func SideDisplayGetTexture():
return side_display_sub_viewport.get_texture()
func MainSubGetTexture():
return main_sub_viewport.get_texture()

4236
res3D/ego_vehicle.tscn Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,15 @@
[gd_resource type="Environment" load_steps=3 format=3 uid="uid://c5ksuke04sr07"]
[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_rekia"]
sky_top_color = Color(1, 1, 1, 1)
sky_horizon_color = Color(1, 1, 1, 1)
ground_bottom_color = Color(1, 1, 1, 1)
ground_horizon_color = Color(1, 1, 1, 1)
[sub_resource type="Sky" id="Sky_q0c80"]
sky_material = SubResource("ProceduralSkyMaterial_rekia")
[resource]
background_mode = 2
background_energy_multiplier = 0.8
sky = SubResource("Sky_q0c80")