From a0c90b1e826a90f6b690feb69b74579a9b15cb7a Mon Sep 17 00:00:00 2001 From: logonkhi Date: Thu, 24 Jul 2025 18:28:09 +0900 Subject: [PATCH] =?UTF-8?q?=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81=20?= =?UTF-8?q?=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .editorconfig | 249 ++++++++++++++++++ .../Prefabs/UI/Buttons/UISearchInput.prefab | 8 +- .../UI/Factory/AlarmIconManager.prefab | 2 +- .../UI/Factory/AlarmSingleIconManager.prefab | 6 +- .../Prefabs/UI/Factory/InfoWindow.prefab | 6 +- .../Prefabs/UI/Loading/UILoading.prefab | 43 ++- .../Resources/Prefabs/UI/Modal/Alert.prefab | 2 +- .../Resources/Prefabs/UI/Modal/Confirm.prefab | 2 +- .../Resources/Prefabs/UI/Modal/Toast.prefab | 2 +- .../Prefabs/UI/Toolbar/Toolbar.prefab | 2 +- .../UI/Toolbar/ToolbarExpandableButton.prefab | 2 +- .../UI/Toolbar/ToolbarRadioButton.prefab | 4 +- .../UI/Toolbar/ToolbarStandardButton.prefab | 2 +- .../UI/Toolbar/ToolbarSubMenuPanel.prefab | 2 +- .../UI/Toolbar/ToolbarToggleButton.prefab | 2 +- .../Prefabs/UI/Toolbox/Toolbox.prefab | 2 +- .../UI/Toolbox/ToolboxRadioButton.prefab | 2 +- .../UI/Toolbox/ToolboxStandardButton.prefab | 2 +- .../UI/Toolbox/ToolboxSubMenuPanel.prefab | 2 +- .../UI/Toolbox/ToolboxToggleButton.prefab | 2 +- .../Prefabs/UI/Tooltip/Tooltip.prefab | 4 +- Assets/Scripts/SampleProject/AppMain.cs | 4 +- .../Scripts/SampleProject/Config/AppConfig.cs | 6 + Assets/Scripts/SampleProject/SceneMain.cs | 36 ++- Assets/Scripts/UVC/Data/DataRepository.cs | 20 +- .../Scripts/UVC/Data/Http/HttpDataFetcher.cs | 24 +- .../UVC/Data/Http/HttpDataProcessor.cs | 79 +++--- .../Scripts/UVC/Data/Mqtt/MqttDataReceiver.cs | 49 ++-- Assets/Scripts/UVC/Data/Mqtt/MqttWorker.cs | 2 +- .../Scripts/UVC/Event/UniTaskEventHandler.cs | 34 +-- .../Scripts/UVC/Extention/RectTransformEx.cs | 90 +++---- .../Scripts/UVC/Factory/Alarm/AlarmManager.cs | 50 +++- .../UVC/Factory/Buttons/UISearchInput.cs | 34 +-- Assets/Scripts/UVC/Factory/Component/AGV.cs | 53 +++- .../UVC/Factory/Component/AGVManager.cs | 52 ++-- .../UVC/Factory/Playback/PlaybackService.cs | 4 +- Assets/Scripts/UVC/Network/HttpRequester.cs | 9 +- .../UVC/Tests/Data/HttpDataFetcherTests.cs | 6 +- Assets/Scripts/UVC/UI/Loading/UILoading.cs | 15 +- .../Scripts/UVC/UI/Menu/TopMenuController.cs | 3 +- Assets/StreamingAssets/AppConfig.json | 1 + 41 files changed, 618 insertions(+), 301 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..6038d5a3 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,249 @@ +# 상위 디렉터리에서 .editorconfig 설정을 상속하려면 아래 행을 제거하세요. +root = true + +# C# 파일 +[*.cs] + +#### 코어 EditorConfig 옵션 #### + +# 들여쓰기 및 간격 +indent_size = 4 +indent_style = space +tab_width = 4 + +# 새 줄 기본 설정 +end_of_line = crlf +insert_final_newline = false + +charset = utf-8 +trim_trailing_whitespace = true + +#### .NET 코드 작업 #### + +# 멤버 입력 +dotnet_hide_advanced_members = false +dotnet_member_insertion_location = with_other_members_of_the_same_kind +dotnet_property_generation_behavior = prefer_throwing_properties + +# 기호 검색 +dotnet_search_reference_assemblies = true + +#### .NET 코딩 규칙 #### + +# Using 구성 +dotnet_separate_import_directive_groups = false +dotnet_sort_system_directives_first = false +file_header_template = unset + +# this. 및 Me. 기본 설정 +dotnet_style_qualification_for_event = false +dotnet_style_qualification_for_field = false +dotnet_style_qualification_for_method = false +dotnet_style_qualification_for_property = false + +# 언어 키워드 및 BCL 형식 기본 설정 +dotnet_style_predefined_type_for_locals_parameters_members = true +dotnet_style_predefined_type_for_member_access = true + +# 괄호 기본 설정 +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_operators = never_if_unnecessary +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity + +# 한정자 기본 설정 +dotnet_style_require_accessibility_modifiers = for_non_interface_members + +# 식 수준 기본 설정 +dotnet_prefer_system_hash_code = true +dotnet_style_coalesce_expression = true +dotnet_style_collection_initializer = true +dotnet_style_explicit_tuple_names = true +dotnet_style_namespace_match_folder = true +dotnet_style_null_propagation = true +dotnet_style_object_initializer = true +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_prefer_auto_properties = true +dotnet_style_prefer_collection_expression = when_types_loosely_match +dotnet_style_prefer_compound_assignment = true +dotnet_style_prefer_conditional_expression_over_assignment = true +dotnet_style_prefer_conditional_expression_over_return = true +dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed +dotnet_style_prefer_inferred_anonymous_type_member_names = true +dotnet_style_prefer_inferred_tuple_names = true +dotnet_style_prefer_is_null_check_over_reference_equality_method = true +dotnet_style_prefer_simplified_boolean_expressions = true +dotnet_style_prefer_simplified_interpolation = true + +# 필드 기본 설정 +dotnet_style_readonly_field = true + +# 매개 변수 기본 설정 +dotnet_code_quality_unused_parameters = all + +# 비표시 오류(Suppression) 기본 설정 +dotnet_remove_unnecessary_suppression_exclusions = none + +# 새 줄 기본 설정 +dotnet_style_allow_multiple_blank_lines_experimental = true +dotnet_style_allow_statement_immediately_after_block_experimental = true + +#### C# 코딩 규칙 #### + +# var 기본 설정 +csharp_style_var_elsewhere = false +csharp_style_var_for_built_in_types = false +csharp_style_var_when_type_is_apparent = false + +# 식 본문 멤버 +csharp_style_expression_bodied_accessors = true +csharp_style_expression_bodied_constructors = false +csharp_style_expression_bodied_indexers = true +csharp_style_expression_bodied_lambdas = true +csharp_style_expression_bodied_local_functions = false +csharp_style_expression_bodied_methods = false +csharp_style_expression_bodied_operators = false +csharp_style_expression_bodied_properties = true + +# 패턴 일치 기본 설정 +csharp_style_pattern_matching_over_as_with_null_check = true +csharp_style_pattern_matching_over_is_with_cast_check = true +csharp_style_prefer_extended_property_pattern = true +csharp_style_prefer_not_pattern = true +csharp_style_prefer_pattern_matching = true +csharp_style_prefer_switch_expression = true + +# Null 검사 기본 설정 +csharp_style_conditional_delegate_call = true + +# 한정자 기본 설정 +csharp_prefer_static_anonymous_function = true +csharp_prefer_static_local_function = true +csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async +csharp_style_prefer_readonly_struct = true +csharp_style_prefer_readonly_struct_member = true + +# 코드 블록 기본 설정 +csharp_prefer_braces = true +csharp_prefer_simple_using_statement = true +csharp_prefer_system_threading_lock = true +csharp_style_namespace_declarations = block_scoped +csharp_style_prefer_method_group_conversion = true +csharp_style_prefer_primary_constructors = true +csharp_style_prefer_top_level_statements = true + +# 식 수준 기본 설정 +csharp_prefer_simple_default_expression = true +csharp_style_deconstructed_variable_declaration = true +csharp_style_implicit_object_creation_when_type_is_apparent = true +csharp_style_inlined_variable_declaration = true +csharp_style_prefer_implicitly_typed_lambda_expression = true +csharp_style_prefer_index_operator = true +csharp_style_prefer_local_over_anonymous_function = true +csharp_style_prefer_null_check_over_type_check = true +csharp_style_prefer_range_operator = true +csharp_style_prefer_tuple_swap = true +csharp_style_prefer_unbound_generic_type_in_nameof = true +csharp_style_prefer_utf8_string_literals = true +csharp_style_throw_expression = true +csharp_style_unused_value_assignment_preference = discard_variable +csharp_style_unused_value_expression_statement_preference = discard_variable + +# 'using' 지시문 기본 설정 +csharp_using_directive_placement = outside_namespace + +# 새 줄 기본 설정 +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true +csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true +csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true +csharp_style_allow_embedded_statements_on_same_line_experimental = true + +#### C# 서식 설정 규칙 #### + +# 새 줄 기본 설정 +csharp_new_line_before_catch = true +csharp_new_line_before_else = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_open_brace = all +csharp_new_line_between_query_expression_clauses = true + +# 들여쓰기 기본 설정 +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = true +csharp_indent_labels = one_less_than_current +csharp_indent_switch_labels = true + +# 공간 기본 설정 +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# 기본 설정 래핑 +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true + +#### 명명 스타일 #### + +# 명명 규칙 + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# 기호 사양 + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# 명명 스타일 + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case diff --git a/Assets/Resources/Prefabs/UI/Buttons/UISearchInput.prefab b/Assets/Resources/Prefabs/UI/Buttons/UISearchInput.prefab index 7f84c5f5..000c6964 100644 --- a/Assets/Resources/Prefabs/UI/Buttons/UISearchInput.prefab +++ b/Assets/Resources/Prefabs/UI/Buttons/UISearchInput.prefab @@ -62,6 +62,8 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 836d6527858b7494297e122eb69601fd, type: 3} m_Name: m_EditorClassIdentifier: + inputField: {fileID: 4095973766915737330} + searchButton: {fileID: 3154601933036176681} --- !u!114 &1278875195337434962 MonoBehaviour: m_ObjectHideFlags: 0 @@ -69,20 +71,20 @@ MonoBehaviour: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1928778550412568362} - m_Enabled: 0 + m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} - m_Color: {r: 1, g: 1, b: 1, a: 1} + m_Color: {r: 1, g: 1, b: 1, a: 0} m_RaycastTarget: 0 m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_Sprite: {fileID: 694507160, guid: fe40b90c67f2f75419562a6fe1003d5b, type: 3} + m_Sprite: {fileID: 0} m_Type: 1 m_PreserveAspect: 0 m_FillCenter: 1 diff --git a/Assets/Resources/Prefabs/UI/Factory/AlarmIconManager.prefab b/Assets/Resources/Prefabs/UI/Factory/AlarmIconManager.prefab index e810e01e..f76dc01b 100644 --- a/Assets/Resources/Prefabs/UI/Factory/AlarmIconManager.prefab +++ b/Assets/Resources/Prefabs/UI/Factory/AlarmIconManager.prefab @@ -390,7 +390,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_Sprite: {fileID: 887145076, guid: 4cf3568ca3f55f64cb11447d139d7a3d, type: 3} + m_Sprite: {fileID: 21300000, guid: 4cf3568ca3f55f64cb11447d139d7a3d, type: 3} m_Type: 1 m_PreserveAspect: 0 m_FillCenter: 1 diff --git a/Assets/Resources/Prefabs/UI/Factory/AlarmSingleIconManager.prefab b/Assets/Resources/Prefabs/UI/Factory/AlarmSingleIconManager.prefab index a04a5e4b..bcdd93a3 100644 --- a/Assets/Resources/Prefabs/UI/Factory/AlarmSingleIconManager.prefab +++ b/Assets/Resources/Prefabs/UI/Factory/AlarmSingleIconManager.prefab @@ -198,7 +198,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_Sprite: {fileID: 887145076, guid: 4cf3568ca3f55f64cb11447d139d7a3d, type: 3} + m_Sprite: {fileID: 21300000, guid: 4cf3568ca3f55f64cb11447d139d7a3d, type: 3} m_Type: 1 m_PreserveAspect: 0 m_FillCenter: 1 @@ -340,7 +340,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_Sprite: {fileID: -27720893, guid: e5829cbc100001646956a9c3ed4e33c5, type: 3} + m_Sprite: {fileID: 21300000, guid: e5829cbc100001646956a9c3ed4e33c5, type: 3} m_Type: 1 m_PreserveAspect: 0 m_FillCenter: 1 @@ -521,7 +521,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_Sprite: {fileID: 887145076, guid: 4cf3568ca3f55f64cb11447d139d7a3d, type: 3} + m_Sprite: {fileID: 21300000, guid: 4cf3568ca3f55f64cb11447d139d7a3d, type: 3} m_Type: 1 m_PreserveAspect: 0 m_FillCenter: 1 diff --git a/Assets/Resources/Prefabs/UI/Factory/InfoWindow.prefab b/Assets/Resources/Prefabs/UI/Factory/InfoWindow.prefab index 645eef9e..c028f51e 100644 --- a/Assets/Resources/Prefabs/UI/Factory/InfoWindow.prefab +++ b/Assets/Resources/Prefabs/UI/Factory/InfoWindow.prefab @@ -198,7 +198,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_Sprite: {fileID: 887145076, guid: 4cf3568ca3f55f64cb11447d139d7a3d, type: 3} + m_Sprite: {fileID: 21300000, guid: 4cf3568ca3f55f64cb11447d139d7a3d, type: 3} m_Type: 1 m_PreserveAspect: 0 m_FillCenter: 1 @@ -275,7 +275,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_Sprite: {fileID: -27720893, guid: e5829cbc100001646956a9c3ed4e33c5, type: 3} + m_Sprite: {fileID: 21300000, guid: e5829cbc100001646956a9c3ed4e33c5, type: 3} m_Type: 1 m_PreserveAspect: 0 m_FillCenter: 1 @@ -394,7 +394,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_Sprite: {fileID: -27720893, guid: e5829cbc100001646956a9c3ed4e33c5, type: 3} + m_Sprite: {fileID: 21300000, guid: e5829cbc100001646956a9c3ed4e33c5, type: 3} m_Type: 1 m_PreserveAspect: 0 m_FillCenter: 1 diff --git a/Assets/Resources/Prefabs/UI/Loading/UILoading.prefab b/Assets/Resources/Prefabs/UI/Loading/UILoading.prefab index 0f3bbb41..9580de51 100644 --- a/Assets/Resources/Prefabs/UI/Loading/UILoading.prefab +++ b/Assets/Resources/Prefabs/UI/Loading/UILoading.prefab @@ -10,9 +10,8 @@ GameObject: m_Component: - component: {fileID: 3316965954832882549} - component: {fileID: 3078805581424071851} - - component: {fileID: 3314964221659757925} - - component: {fileID: 4097232251975178814} - component: {fileID: 5836275117983516284} + - component: {fileID: 4097232251975178814} m_Layer: 5 m_Name: UILoading m_TagString: Untagged @@ -29,7 +28,7 @@ RectTransform: m_GameObject: {fileID: 3247177050376678973} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: - {fileID: 88171281312113102} @@ -53,26 +52,8 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 0575433bbc705184a91373cc1596e713, type: 3} m_Name: m_EditorClassIdentifier: ---- !u!222 &3314964221659757925 -CanvasRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 3247177050376678973} - m_CullTransparentMesh: 0 ---- !u!225 &4097232251975178814 -CanvasGroup: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 3247177050376678973} - m_Enabled: 1 - m_Alpha: 1 - m_Interactable: 1 - m_BlocksRaycasts: 1 - m_IgnoreParentGroups: 0 + canvasGroup: {fileID: 4097232251975178814} + loadinImage: {fileID: 5537735754607583444} --- !u!223 &5836275117983516284 Canvas: m_ObjectHideFlags: 0 @@ -94,8 +75,20 @@ Canvas: m_AdditionalShaderChannelsFlag: 0 m_UpdateRectTransformForStandalone: 0 m_SortingLayerID: 0 - m_SortingOrder: 0 + m_SortingOrder: 100 m_TargetDisplay: 0 +--- !u!225 &4097232251975178814 +CanvasGroup: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3247177050376678973} + m_Enabled: 1 + m_Alpha: 1 + m_Interactable: 1 + m_BlocksRaycasts: 1 + m_IgnoreParentGroups: 0 --- !u!1 &3720191927695001841 GameObject: m_ObjectHideFlags: 0 @@ -131,7 +124,7 @@ RectTransform: m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} m_AnchoredPosition: {x: 0, y: 0} - m_SizeDelta: {x: 128, y: 128} + m_SizeDelta: {x: 32, y: 32} m_Pivot: {x: 0.5, y: 0.5} --- !u!222 &7648764534251572644 CanvasRenderer: diff --git a/Assets/Resources/Prefabs/UI/Modal/Alert.prefab b/Assets/Resources/Prefabs/UI/Modal/Alert.prefab index ae01384b..319d9115 100644 --- a/Assets/Resources/Prefabs/UI/Modal/Alert.prefab +++ b/Assets/Resources/Prefabs/UI/Modal/Alert.prefab @@ -926,7 +926,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_Sprite: {fileID: 887145076, guid: 4cf3568ca3f55f64cb11447d139d7a3d, type: 3} + m_Sprite: {fileID: 21300000, guid: 4cf3568ca3f55f64cb11447d139d7a3d, type: 3} m_Type: 1 m_PreserveAspect: 0 m_FillCenter: 1 diff --git a/Assets/Resources/Prefabs/UI/Modal/Confirm.prefab b/Assets/Resources/Prefabs/UI/Modal/Confirm.prefab index 9856e009..17c4e753 100644 --- a/Assets/Resources/Prefabs/UI/Modal/Confirm.prefab +++ b/Assets/Resources/Prefabs/UI/Modal/Confirm.prefab @@ -926,7 +926,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_Sprite: {fileID: 887145076, guid: 4cf3568ca3f55f64cb11447d139d7a3d, type: 3} + m_Sprite: {fileID: 21300000, guid: 4cf3568ca3f55f64cb11447d139d7a3d, type: 3} m_Type: 1 m_PreserveAspect: 0 m_FillCenter: 1 diff --git a/Assets/Resources/Prefabs/UI/Modal/Toast.prefab b/Assets/Resources/Prefabs/UI/Modal/Toast.prefab index 6561f1fb..40b43d2b 100644 --- a/Assets/Resources/Prefabs/UI/Modal/Toast.prefab +++ b/Assets/Resources/Prefabs/UI/Modal/Toast.prefab @@ -138,7 +138,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_Sprite: {fileID: 887145076, guid: 4cf3568ca3f55f64cb11447d139d7a3d, type: 3} + m_Sprite: {fileID: 21300000, guid: 4cf3568ca3f55f64cb11447d139d7a3d, type: 3} m_Type: 1 m_PreserveAspect: 0 m_FillCenter: 1 diff --git a/Assets/Resources/Prefabs/UI/Toolbar/Toolbar.prefab b/Assets/Resources/Prefabs/UI/Toolbar/Toolbar.prefab index 252bc00a..4d960c67 100644 --- a/Assets/Resources/Prefabs/UI/Toolbar/Toolbar.prefab +++ b/Assets/Resources/Prefabs/UI/Toolbar/Toolbar.prefab @@ -173,7 +173,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_Sprite: {fileID: 887145076, guid: 4cf3568ca3f55f64cb11447d139d7a3d, type: 3} + m_Sprite: {fileID: 21300000, guid: 4cf3568ca3f55f64cb11447d139d7a3d, type: 3} m_Type: 1 m_PreserveAspect: 0 m_FillCenter: 1 diff --git a/Assets/Resources/Prefabs/UI/Toolbar/ToolbarExpandableButton.prefab b/Assets/Resources/Prefabs/UI/Toolbar/ToolbarExpandableButton.prefab index d6b94ac7..fc12167f 100644 --- a/Assets/Resources/Prefabs/UI/Toolbar/ToolbarExpandableButton.prefab +++ b/Assets/Resources/Prefabs/UI/Toolbar/ToolbarExpandableButton.prefab @@ -281,7 +281,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_Sprite: {fileID: -27720893, guid: e5829cbc100001646956a9c3ed4e33c5, type: 3} + m_Sprite: {fileID: 21300000, guid: e5829cbc100001646956a9c3ed4e33c5, type: 3} m_Type: 1 m_PreserveAspect: 0 m_FillCenter: 1 diff --git a/Assets/Resources/Prefabs/UI/Toolbar/ToolbarRadioButton.prefab b/Assets/Resources/Prefabs/UI/Toolbar/ToolbarRadioButton.prefab index ae0d58d1..cbb28497 100644 --- a/Assets/Resources/Prefabs/UI/Toolbar/ToolbarRadioButton.prefab +++ b/Assets/Resources/Prefabs/UI/Toolbar/ToolbarRadioButton.prefab @@ -145,7 +145,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_Sprite: {fileID: -27720893, guid: e5829cbc100001646956a9c3ed4e33c5, type: 3} + m_Sprite: {fileID: 21300000, guid: e5829cbc100001646956a9c3ed4e33c5, type: 3} m_Type: 1 m_PreserveAspect: 0 m_FillCenter: 1 @@ -172,7 +172,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 0 + m_IsActive: 1 --- !u!224 &117691393038772277 RectTransform: m_ObjectHideFlags: 0 diff --git a/Assets/Resources/Prefabs/UI/Toolbar/ToolbarStandardButton.prefab b/Assets/Resources/Prefabs/UI/Toolbar/ToolbarStandardButton.prefab index afe56cbe..32a64a7d 100644 --- a/Assets/Resources/Prefabs/UI/Toolbar/ToolbarStandardButton.prefab +++ b/Assets/Resources/Prefabs/UI/Toolbar/ToolbarStandardButton.prefab @@ -281,7 +281,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_Sprite: {fileID: -27720893, guid: e5829cbc100001646956a9c3ed4e33c5, type: 3} + m_Sprite: {fileID: 21300000, guid: e5829cbc100001646956a9c3ed4e33c5, type: 3} m_Type: 1 m_PreserveAspect: 0 m_FillCenter: 1 diff --git a/Assets/Resources/Prefabs/UI/Toolbar/ToolbarSubMenuPanel.prefab b/Assets/Resources/Prefabs/UI/Toolbar/ToolbarSubMenuPanel.prefab index 55a1eebd..7148cd62 100644 --- a/Assets/Resources/Prefabs/UI/Toolbar/ToolbarSubMenuPanel.prefab +++ b/Assets/Resources/Prefabs/UI/Toolbar/ToolbarSubMenuPanel.prefab @@ -162,7 +162,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_Sprite: {fileID: 887145076, guid: 4cf3568ca3f55f64cb11447d139d7a3d, type: 3} + m_Sprite: {fileID: 21300000, guid: 4cf3568ca3f55f64cb11447d139d7a3d, type: 3} m_Type: 1 m_PreserveAspect: 0 m_FillCenter: 1 diff --git a/Assets/Resources/Prefabs/UI/Toolbar/ToolbarToggleButton.prefab b/Assets/Resources/Prefabs/UI/Toolbar/ToolbarToggleButton.prefab index b1be448b..be1a2a75 100644 --- a/Assets/Resources/Prefabs/UI/Toolbar/ToolbarToggleButton.prefab +++ b/Assets/Resources/Prefabs/UI/Toolbar/ToolbarToggleButton.prefab @@ -145,7 +145,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_Sprite: {fileID: -27720893, guid: e5829cbc100001646956a9c3ed4e33c5, type: 3} + m_Sprite: {fileID: 21300000, guid: e5829cbc100001646956a9c3ed4e33c5, type: 3} m_Type: 1 m_PreserveAspect: 0 m_FillCenter: 1 diff --git a/Assets/Resources/Prefabs/UI/Toolbox/Toolbox.prefab b/Assets/Resources/Prefabs/UI/Toolbox/Toolbox.prefab index 2060ef3a..7293b986 100644 --- a/Assets/Resources/Prefabs/UI/Toolbox/Toolbox.prefab +++ b/Assets/Resources/Prefabs/UI/Toolbox/Toolbox.prefab @@ -173,7 +173,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_Sprite: {fileID: 887145076, guid: 4cf3568ca3f55f64cb11447d139d7a3d, type: 3} + m_Sprite: {fileID: 21300000, guid: 4cf3568ca3f55f64cb11447d139d7a3d, type: 3} m_Type: 1 m_PreserveAspect: 0 m_FillCenter: 1 diff --git a/Assets/Resources/Prefabs/UI/Toolbox/ToolboxRadioButton.prefab b/Assets/Resources/Prefabs/UI/Toolbox/ToolboxRadioButton.prefab index 0af5e2f5..2c520149 100644 --- a/Assets/Resources/Prefabs/UI/Toolbox/ToolboxRadioButton.prefab +++ b/Assets/Resources/Prefabs/UI/Toolbox/ToolboxRadioButton.prefab @@ -145,7 +145,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_Sprite: {fileID: -27720893, guid: e5829cbc100001646956a9c3ed4e33c5, type: 3} + m_Sprite: {fileID: 21300000, guid: e5829cbc100001646956a9c3ed4e33c5, type: 3} m_Type: 1 m_PreserveAspect: 0 m_FillCenter: 1 diff --git a/Assets/Resources/Prefabs/UI/Toolbox/ToolboxStandardButton.prefab b/Assets/Resources/Prefabs/UI/Toolbox/ToolboxStandardButton.prefab index f585f38d..36bd9031 100644 --- a/Assets/Resources/Prefabs/UI/Toolbox/ToolboxStandardButton.prefab +++ b/Assets/Resources/Prefabs/UI/Toolbox/ToolboxStandardButton.prefab @@ -281,7 +281,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_Sprite: {fileID: -27720893, guid: e5829cbc100001646956a9c3ed4e33c5, type: 3} + m_Sprite: {fileID: 21300000, guid: e5829cbc100001646956a9c3ed4e33c5, type: 3} m_Type: 1 m_PreserveAspect: 0 m_FillCenter: 1 diff --git a/Assets/Resources/Prefabs/UI/Toolbox/ToolboxSubMenuPanel.prefab b/Assets/Resources/Prefabs/UI/Toolbox/ToolboxSubMenuPanel.prefab index 3f2bc6c1..32713a6a 100644 --- a/Assets/Resources/Prefabs/UI/Toolbox/ToolboxSubMenuPanel.prefab +++ b/Assets/Resources/Prefabs/UI/Toolbox/ToolboxSubMenuPanel.prefab @@ -162,7 +162,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_Sprite: {fileID: 887145076, guid: 4cf3568ca3f55f64cb11447d139d7a3d, type: 3} + m_Sprite: {fileID: 21300000, guid: 4cf3568ca3f55f64cb11447d139d7a3d, type: 3} m_Type: 1 m_PreserveAspect: 0 m_FillCenter: 1 diff --git a/Assets/Resources/Prefabs/UI/Toolbox/ToolboxToggleButton.prefab b/Assets/Resources/Prefabs/UI/Toolbox/ToolboxToggleButton.prefab index 7f200453..e96be642 100644 --- a/Assets/Resources/Prefabs/UI/Toolbox/ToolboxToggleButton.prefab +++ b/Assets/Resources/Prefabs/UI/Toolbox/ToolboxToggleButton.prefab @@ -145,7 +145,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_Sprite: {fileID: -27720893, guid: e5829cbc100001646956a9c3ed4e33c5, type: 3} + m_Sprite: {fileID: 21300000, guid: e5829cbc100001646956a9c3ed4e33c5, type: 3} m_Type: 1 m_PreserveAspect: 0 m_FillCenter: 1 diff --git a/Assets/Resources/Prefabs/UI/Tooltip/Tooltip.prefab b/Assets/Resources/Prefabs/UI/Tooltip/Tooltip.prefab index 57d4dbfa..3bc9a32b 100644 --- a/Assets/Resources/Prefabs/UI/Tooltip/Tooltip.prefab +++ b/Assets/Resources/Prefabs/UI/Tooltip/Tooltip.prefab @@ -66,7 +66,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_Sprite: {fileID: -27720893, guid: e5829cbc100001646956a9c3ed4e33c5, type: 3} + m_Sprite: {fileID: 21300000, guid: e5829cbc100001646956a9c3ed4e33c5, type: 3} m_Type: 1 m_PreserveAspect: 0 m_FillCenter: 1 @@ -345,7 +345,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_Sprite: {fileID: 887145076, guid: 4cf3568ca3f55f64cb11447d139d7a3d, type: 3} + m_Sprite: {fileID: 21300000, guid: 4cf3568ca3f55f64cb11447d139d7a3d, type: 3} m_Type: 1 m_PreserveAspect: 0 m_FillCenter: 1 diff --git a/Assets/Scripts/SampleProject/AppMain.cs b/Assets/Scripts/SampleProject/AppMain.cs index 1f945857..ac249ffa 100644 --- a/Assets/Scripts/SampleProject/AppMain.cs +++ b/Assets/Scripts/SampleProject/AppMain.cs @@ -39,6 +39,8 @@ namespace SampleProject { if (AppConfig.LoadConfig()) { + Application.targetFrameRate = AppConfig.Config.TargetFrameRate; + //기본 언어 설정 bool success = LocalizationManager.Instance.LoadDefaultLocalizationData(AppConfig.Config.Language); Debug.Log($"LocalizationManager: LoadDefaultLocalizationData success: {success}"); @@ -56,7 +58,7 @@ namespace SampleProject private void SetNetworkConfig() { - URLList.Add("baseinfo", "http://localhost:8888/baseinfo/00:00"); + URLList.Add("baseinfo", "http://localhost:8888/baseinfo"); var agvDataMask = new DataMask(); agvDataMask.ObjectName = "AGV"; // AGV 객체의 이름을 설정합니다. diff --git a/Assets/Scripts/SampleProject/Config/AppConfig.cs b/Assets/Scripts/SampleProject/Config/AppConfig.cs index c438f1b8..fd2b7e31 100644 --- a/Assets/Scripts/SampleProject/Config/AppConfig.cs +++ b/Assets/Scripts/SampleProject/Config/AppConfig.cs @@ -43,6 +43,12 @@ namespace SampleProject.Config [JsonProperty("language")] public string Language { get; set; } + /// + /// 목표 프레임 레이트 설정입니다. + /// + [JsonProperty("targetFrameRate")] + public int TargetFrameRate { get; set; } + /// /// 애플리케이션의 창 관련 설정입니다. /// diff --git a/Assets/Scripts/SampleProject/SceneMain.cs b/Assets/Scripts/SampleProject/SceneMain.cs index dc3df6a2..f25c1d68 100644 --- a/Assets/Scripts/SampleProject/SceneMain.cs +++ b/Assets/Scripts/SampleProject/SceneMain.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading.Tasks; using UnityEngine; using UVC.Core; @@ -6,7 +6,9 @@ using UVC.Data; using UVC.Data.Core; using UVC.Data.Http; using UVC.Factory.Alarm; +using UVC.Factory.Component; using UVC.Factory.Playback; +using UVC.UI.Loading; using UVC.UI.Tooltip; namespace SampleProject @@ -33,13 +35,19 @@ namespace SampleProject /// 이 메서드는 AGV 관리자 생성과 관련된 필요한 초기화 또는 설정 작업을 수행하기 위한 것입니다. /// 내부적으로 호출되며 외부 코드에서 직접 사용하도록 의도된 것이 아닙니다. /// - internal void OnAGVManagerCreated() + internal void OnAGVCreated() { AlarmManager.Instance.Run(); } private async void OnAppInitialized() { + + // AGVManager 생성 시 이벤트 처리 + AGVManager.Instance.OnAGVCreated += OnAGVCreated; + + PlaybackService.Instance.OnStopPlayback += OnStopPlayback; + await requestDataAsync(); if (Initialized != null) @@ -47,21 +55,23 @@ namespace SampleProject Initialized.Invoke(); } - PlaybackService.Instance.OnStopPlayback += OnStopPlayback; + //MqttReceiver 시작 + DataRepository.Instance.MqttReceiver.Start(); } private async Task requestDataAsync() { - var httpFetcher = DataRepository.Instance.HttpFetcher; - var splitRequest = new HttpRequestConfig(URLList.Get("baseinfo")) - .setSplitResponseByKey(true) // 응답을 키별로 분할 - .AddSplitConfig("AGV", DataMapperValidator.Get("AGV")) // "AGV" 키에 대한 매퍼, Validator 설정 - .AddSplitConfig("ALARM", DataMapperValidator.Get("ALARM")); // "ALARM" 키에 대한 매퍼, Validator 설정 - httpFetcher.Add("baseInfo", splitRequest); - await httpFetcher.Excute("baseInfo"); - - //MqttReceiver 시작 - DataRepository.Instance.MqttReceiver.Start(); + UILoading.Show(); + //Debug.Log("Requesting BaseInfo data..."); + //var httpFetcher = DataRepository.Instance.HttpFetcher; + //var splitRequest = new HttpRequestConfig(URLList.Get("baseinfo")) + // .setSplitResponseByKey(true) // 응답을 키별로 분할 + // .AddSplitConfig("AGV", DataMapperValidator.Get("AGV")) // "AGV" 키에 대한 매퍼, Validator 설정 + // .AddSplitConfig("ALARM", DataMapperValidator.Get("ALARM")); // "ALARM" 키에 대한 매퍼, Validator 설정 + //httpFetcher.Add("baseInfo", splitRequest); + //await httpFetcher.Excute("baseInfo"); + //Debug.Log("BaseInfo data request completed."); + UILoading.Hide(); } private async void OnStopPlayback() diff --git a/Assets/Scripts/UVC/Data/DataRepository.cs b/Assets/Scripts/UVC/Data/DataRepository.cs index bc21cbcd..8df37e90 100644 --- a/Assets/Scripts/UVC/Data/DataRepository.cs +++ b/Assets/Scripts/UVC/Data/DataRepository.cs @@ -1,8 +1,11 @@ -using Cysharp.Threading.Tasks; +#nullable enable + +using Cysharp.Threading.Tasks; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; +using UnityEngine; using UVC.Data.Core; using UVC.Data.Http; using UVC.Data.Mqtt; @@ -28,7 +31,7 @@ namespace UVC.Data /// /// 외부에서의 인스턴스 생성을 방지하는 보호된 생성자입니다. /// - protected DataRepository() + protected DataRepository() { // Best MQTT 초기화 작업을 Main 스레드에서 호출 해야 한다. Best.HTTP.Shared.HTTPManager.Setup(); @@ -56,7 +59,7 @@ namespace UVC.Data private Dictionary> dataUpdateHandlers = new Dictionary>(); private HttpDataFetcher httpFetcher = new HttpDataFetcher(); - public HttpDataFetcher HttpFetcher => httpFetcher; + public HttpDataFetcher HttpFetcher => httpFetcher; private MqttDataReceiver mqttReceiver = new MqttDataReceiver(); public MqttDataReceiver MqttReceiver => mqttReceiver; @@ -81,10 +84,9 @@ namespace UVC.Data { if (!dataObjects.ContainsKey(key)) { - var newData = dataObject.Clone(fromPool: false); - - dataObjects.Add(key, newData); dataObject.MarkAllAsUpdated(); + var newData = dataObject.Clone(fromPool: false); + dataObjects.Add(key, newData); NotifyDataUpdate(key, newData); return dataObject; } @@ -101,7 +103,8 @@ namespace UVC.Data { newDataObject = dataObject; } - NotifyDataUpdate(key, obj); + bool shouldInvoke = !updatedDataOnly || newDataObject.UpdatedCount > 0; + if(shouldInvoke) NotifyDataUpdate(key, newDataObject); return newDataObject; } } @@ -130,7 +133,7 @@ namespace UVC.Data /// /// 검색할 데이터 객체의 키 /// 키가 존재하면 해당 데이터 객체, 존재하지 않으면 null - public IDataObject GetData(string key) + public IDataObject? GetData(string key) { if (string.IsNullOrEmpty(key)) throw new ArgumentNullException(nameof(key), "키는 null이거나 빈 문자열일 수 없습니다."); @@ -233,6 +236,7 @@ namespace UVC.Data throw new ArgumentNullException(nameof(key), "키는 null이거나 빈 문자열일 수 없습니다."); if (dataObject == null) throw new ArgumentNullException(nameof(dataObject), "데이터 객체는 null일 수 없습니다."); + //Debug.Log($"NotifyDataUpdate: {key}, {dataObject.GetType().Name}"); lock (syncLock) { if (dataUpdateHandlers.ContainsKey(key)) diff --git a/Assets/Scripts/UVC/Data/Http/HttpDataFetcher.cs b/Assets/Scripts/UVC/Data/Http/HttpDataFetcher.cs index 01d760c9..e9f5ddf8 100644 --- a/Assets/Scripts/UVC/Data/Http/HttpDataFetcher.cs +++ b/Assets/Scripts/UVC/Data/Http/HttpDataFetcher.cs @@ -183,24 +183,24 @@ namespace UVC.Data.Http { if (!infoList.ContainsKey(key)) { - throw new KeyNotFoundException($"No HTTP request found with key '{key}'."); + Debug.LogError($"No HTTP request found with key '{key}'."); + return; } + Debug.Log($"Executing HTTP request for key: {key}"); + HttpRequestConfig info = infoList[key]; // 반복 설정에 관계없이 이전에 실행 중인 반복 작업이 있다면 중지 await StopRepeat(key); - // 스레드풀에서 요청 처리 실행 - await UniTask.SwitchToThreadPool(); - try { if (!info.Repeat) { // 단일 실행 로직 호출 - await ExecuteSingle(key, info); - await UniTask.SwitchToMainThread(); + await UniTask.RunOnThreadPool(() => ExecuteSingle(key, info)); + Debug.Log($"HTTP request '{key}' executed successfully."); } else { @@ -211,8 +211,6 @@ namespace UVC.Data.Http } catch (Exception ex) { - // 예외가 발생한 경우에도 메인 스레드로 복귀 - await UniTask.SwitchToMainThread(); throw; // 예외 재발생 } } @@ -234,7 +232,6 @@ namespace UVC.Data.Http /// HTTP 요청 중 다른 예외가 발생한 경우 private async UniTask ExecuteSingle(string key, HttpRequestConfig info, CancellationToken cancellationToken = default) { - int retryCount = 0; Exception lastException = null; @@ -264,11 +261,10 @@ namespace UVC.Data.Http { result = await HttpRequester.Request(info.Url, info.Method, info.Body, info.Headers); } - + Debug.Log($"HTTP request '{key}' completed"); cancellationToken.ThrowIfCancellationRequested(); - - await HttpDataProcessor.ProcessResponseAsync(key, info, result, cancellationToken); - + HttpDataProcessor.ProcessResponse(key, info, result, cancellationToken); + Debug.Log($"HTTP request '{key}' processed successfully"); return; } catch (OperationCanceledException) @@ -349,7 +345,7 @@ namespace UVC.Data.Http try { // 단일 실행 로직 호출 - await ExecuteSingle(key, info, cts.Token); + await UniTask.RunOnThreadPool(() => ExecuteSingle(key, info, cts.Token)); // 지정된 횟수만큼 반복한 경우 중지 if (info.RepeatCount > 0) diff --git a/Assets/Scripts/UVC/Data/Http/HttpDataProcessor.cs b/Assets/Scripts/UVC/Data/Http/HttpDataProcessor.cs index ff237c6d..fc7f83d2 100644 --- a/Assets/Scripts/UVC/Data/Http/HttpDataProcessor.cs +++ b/Assets/Scripts/UVC/Data/Http/HttpDataProcessor.cs @@ -1,12 +1,14 @@ -#nullable enable +#nullable enable using Cysharp.Threading.Tasks; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.IO; +using System.Linq; using System.Text; using System.Threading; +using UnityEngine; using UVC.Data.Core; using UVC.Log; @@ -39,7 +41,7 @@ namespace UVC.Data.Http /// 5. 설정에 따라 성공(`SuccessHandler`) 또는 실패(`FailHandler`) 핸들러를 메인 스레드에서 호출하여 UI 업데이트 등을 안전하게 처리할 수 있도록 합니다. /// 6. 모든 과정에서 발생하는 예외(JSON 파싱, 작업 취소 등)를 처리하고 로그를 남깁니다. /// - public static async UniTask ProcessResponseAsync(string key, HttpRequestConfig info, string responseContent, CancellationToken cancellationToken) + public static void ProcessResponse(string key, HttpRequestConfig info, string responseContent, CancellationToken cancellationToken) { // 매핑된 최종 데이터 객체를 담을 변수입니다. try-finally 블록에서 자원 해제를 위해 외부에 선언합니다. IDataObject? mappedObject = null; @@ -54,12 +56,10 @@ namespace UVC.Data.Http // 핸들러는 UI와 상호작용할 수 있으므로 메인 스레드에서 호출합니다. if (info.FailHandler != null) { - await UniTask.SwitchToMainThread(); - info.FailHandler.Invoke("Response content is null or empty."); + UniTask.Post(() => info.FailHandler.Invoke("Response content is null or empty.")); } return; } - // ResponseMask를 사용해 응답이 성공적인지 확인하고, 실제 데이터 부분을 추출합니다. // 예를 들어, 응답이 {"status":"OK", "data":{...}} 형태일 때, "status"가 "OK"인지 확인하고 "data" 부분만 가져옵니다. HttpResponseResult responseResult = info.ResponseMask.Apply(responseContent); @@ -68,34 +68,30 @@ namespace UVC.Data.Http if (info.FailHandler != null) { string errorMessage = responseResult.Message!; - await UniTask.SwitchToMainThread(); - info.FailHandler.Invoke(errorMessage); + UniTask.Post(() => info.FailHandler.Invoke(errorMessage)); } return; } - // 마스크를 통해 추출된 실제 데이터 responseContent = responseResult.Data!.Trim(); - // 응답을 최상위 키를 기준으로 분할 처리해야 하는 경우 if (info.SplitResponseByKey && responseContent.StartsWith("{")) { - await ProcessSplitResponse(info, responseContent, cancellationToken); + ProcessSplitResponse(info, responseContent, cancellationToken); return; // 분할 처리가 완료되면 이 메서드의 나머지 로직은 실행하지 않습니다. } // 응답이 JSON 객체 형태인 경우 (e.g., {"id": 1, "name": "item"}) else if (responseContent.StartsWith("{")) { - mappedObject = await ProcessObjectResponse(info, responseContent, cancellationToken); + mappedObject = ProcessObjectResponse(info, responseContent, cancellationToken); if (mappedObject == null) return; } // 응답이 JSON 배열 형태인 경우 (e.g., [{"id": 1}, {"id": 2}]) else if (responseContent.StartsWith("[")) { - mappedObject = await ProcessArrayResponse(info, responseContent, cancellationToken); + mappedObject = ProcessArrayResponse(info, responseContent, cancellationToken); if (mappedObject == null) return; } - // 핸들러 호출 전 작업 취소 요청이 있었는지 다시 확인합니다. cancellationToken.ThrowIfCancellationRequested(); @@ -108,7 +104,6 @@ namespace UVC.Data.Http // 만약 반환된 객체가 원본과 같다면, 핸들러에 전달하기 위해 복제본을 만듭니다. if (repoObject == mappedObject) repoObject = mappedObject.Clone(fromPool: false); } - // 'UpdatedDataOnly' 옵션이 켜져 있고, 실제로 데이터가 업데이트되었을 때만 핸들러를 호출합니다. if (info.UpdatedDataOnly) { @@ -117,21 +112,18 @@ namespace UVC.Data.Http if (info.SuccessHandler != null) { var handlerData = repoObject; - await UniTask.SwitchToMainThread(); - info.SuccessHandler.Invoke(handlerData); + UniTask.Post(() => info.SuccessHandler.Invoke(handlerData)); } return; } } - // 최종적으로 처리된 데이터가 있는 경우 성공 핸들러를 호출합니다. if (repoObject != null) { if (info.SuccessHandler != null) { var handlerData = repoObject; - await UniTask.SwitchToMainThread(); - info.SuccessHandler.Invoke(handlerData); + UniTask.Post(() => info.SuccessHandler.Invoke(handlerData)); } } // 처리된 데이터가 없는 경우 실패 핸들러를 호출합니다. @@ -139,8 +131,7 @@ namespace UVC.Data.Http { if (info.FailHandler != null) { - await UniTask.SwitchToMainThread(); - info.FailHandler.Invoke("Data is Null"); + UniTask.Post(() => info.FailHandler.Invoke("Data is Null")); } } } @@ -150,8 +141,7 @@ namespace UVC.Data.Http ULog.Error($"JSON parsing error for {key}: {ex.Message}\nResponse: {responseContent}", ex); if (info.FailHandler != null) { - await UniTask.SwitchToMainThread(); - info.FailHandler.Invoke($"JSON parsing error: {ex.Message}"); + UniTask.Post(() => info.FailHandler.Invoke($"JSON parsing error: {ex.Message}")); } } catch (OperationCanceledException) @@ -165,8 +155,7 @@ namespace UVC.Data.Http ULog.Error($"Error processing response for {key}: {ex.Message}", ex); if (info.FailHandler != null) { - await UniTask.SwitchToMainThread(); - info.FailHandler.Invoke($"Error processing response: {ex.Message}"); + UniTask.Post(() => info.FailHandler.Invoke($"Error processing response: {ex.Message}")); } } finally @@ -188,7 +177,7 @@ namespace UVC.Data.Http /// "AGV"와 "ALARM"을 각각 별개의 데이터로 간주하고 처리합니다. /// 각 키에 대해 별도의 DataMapper나 Validator를 `HttpRequestConfig`에 설정할 수 있습니다. /// - public static async UniTask ProcessSplitResponse(HttpRequestConfig info, string jsonResponse, CancellationToken? cancellationToken = null) + public static void ProcessSplitResponse(HttpRequestConfig info, string jsonResponse, CancellationToken? cancellationToken = null) { JObject responseObject = JObject.Parse(jsonResponse); // JSON 객체의 모든 프로퍼티(키-값 쌍)를 순회합니다. @@ -197,6 +186,7 @@ namespace UVC.Data.Http cancellationToken?.ThrowIfCancellationRequested(); string subKey = property.Name; // "AGV", "ALARM" 등 + JToken subToken = property.Value; // `[...]` 또는 `{...}` IDataObject? subMappedObject = null; @@ -208,19 +198,24 @@ namespace UVC.Data.Http var subKeyDataMapper = splitConfig?.DataMapper; var subKeyValidator = splitConfig?.Validator; + //매퍼가 null인 경우, 해당 키는 처리하지 않습니다. + if (subKeyDataMapper == null) continue; + try { // 하위 데이터가 배열 또는 객체 형태인지에 따라 적절한 처리 메서드를 호출합니다. - if (subToken is JArray) + if (subToken is JArray && subToken.Count() > 0) { // 분할 처리 중 개별 항목의 실패가 전체 실패로 이어지지 않도록 `invokeFailHandler`를 false로 설정합니다. - subMappedObject = await ProcessArrayResponse(info, subToken.ToString(), cancellationToken, subKeyDataMapper, subKeyValidator, invokeFailHandler: false); + subMappedObject = ProcessArrayResponse(info, subToken.ToString(), cancellationToken, subKeyDataMapper, subKeyValidator, invokeFailHandler: false); } - else if (subToken is JObject) + else if (subToken is JObject && subToken.Count() > 0) { - subMappedObject = await ProcessObjectResponse(info, subToken.ToString(), cancellationToken, subKeyDataMapper, subKeyValidator, invokeFailHandler: false); + subMappedObject = ProcessObjectResponse(info, subToken.ToString(), cancellationToken, subKeyDataMapper, subKeyValidator, invokeFailHandler: false); } + //Debug.Log($"Processing split response for key: {subKey}, {subToken.Count()}. {subToken.GetType().Name} subMappedObject == null:{subMappedObject == null}"); + // 매핑된 결과가 없으면 (예: 유효성 검사 실패) 다음 키로 넘어갑니다. if (subMappedObject == null) { @@ -236,10 +231,10 @@ namespace UVC.Data.Http if (shouldInvokeHandler && info.SuccessHandler != null) { var handlerData = repoObject.Clone(fromPool: false); - await UniTask.SwitchToMainThread(); - info.SuccessHandler.Invoke(handlerData); + //await UniTask.SwitchToMainThread(); + UniTask.Post(() => info.SuccessHandler.Invoke(handlerData)); // 다음 키 처리를 위해 다시 스레드 풀로 전환합니다. - await UniTask.SwitchToThreadPool(); + //await UniTask.SwitchToThreadPool(); } } finally @@ -250,10 +245,10 @@ namespace UVC.Data.Http } } - private static async UniTask ProcessObjectResponse(HttpRequestConfig info, string jsonResponse, CancellationToken? cancellationToken, DataMapper? dataMapper = null, DataValidator? validator = null, bool invokeFailHandler = true) + private static IDataObject? ProcessObjectResponse(HttpRequestConfig info, string jsonResponse, CancellationToken? cancellationToken, DataMapper? dataMapper = null, DataValidator? validator = null, bool invokeFailHandler = true) { Action? failHandler = invokeFailHandler ? info.FailHandler : null; - return await ProcessObjectResponse(jsonResponse, cancellationToken, dataMapper ?? info.DataMapper, validator ?? info.Validator, failHandler); + return ProcessObjectResponse(jsonResponse, cancellationToken, dataMapper ?? info.DataMapper, validator ?? info.Validator, failHandler); } /// @@ -270,7 +265,7 @@ namespace UVC.Data.Http /// 통과하면 `DataMapper`를 사용해 JSON을 `IDataObject`로 변환합니다. /// 대용량 JSON의 경우 메모리 효율을 위해 스트림 기반 파싱을 지원합니다. /// - public static async UniTask ProcessObjectResponse(string jsonResponse, CancellationToken? cancellationToken, DataMapper? dataMapper, DataValidator? validator, Action? failHandler) + public static IDataObject? ProcessObjectResponse(string jsonResponse, CancellationToken? cancellationToken, DataMapper? dataMapper, DataValidator? validator, Action? failHandler) { // 유효성 검사기가 설정된 경우 if (validator != null) @@ -293,8 +288,7 @@ namespace UVC.Data.Http { if (failHandler != null) { - await UniTask.SwitchToMainThread(); - failHandler.Invoke("Data is not Valid"); + UniTask.Post(() => failHandler.Invoke("Data is not Valid")); } return null; } @@ -324,10 +318,10 @@ namespace UVC.Data.Http } } - private static async UniTask ProcessArrayResponse(HttpRequestConfig info, string jsonResponse, CancellationToken? cancellationToken, DataMapper? dataMapper = null, DataValidator? validator = null, bool invokeFailHandler = true) + private static IDataObject? ProcessArrayResponse(HttpRequestConfig info, string jsonResponse, CancellationToken? cancellationToken, DataMapper? dataMapper = null, DataValidator? validator = null, bool invokeFailHandler = true) { Action? failHandler = invokeFailHandler ? info.FailHandler : null; - return await ProcessArrayResponse(jsonResponse, cancellationToken, dataMapper ?? info.DataMapper, validator ?? info.Validator, failHandler); + return ProcessArrayResponse(jsonResponse, cancellationToken, dataMapper ?? info.DataMapper, validator ?? info.Validator, failHandler); } /// @@ -343,7 +337,7 @@ namespace UVC.Data.Http /// `DataValidator`를 통해 배열의 각 항목을 필터링하여 유효한 데이터만 포함하는 새 배열을 만들 수 있습니다. /// 그 후 `DataMapper`를 통해 배열 전체를 `DataArray` 객체로 변환합니다. /// - public static async UniTask ProcessArrayResponse(string jsonResponse, CancellationToken? cancellationToken, DataMapper? dataMapper, DataValidator? validator, Action? failHandler) + public static IDataObject? ProcessArrayResponse(string jsonResponse, CancellationToken? cancellationToken, DataMapper? dataMapper, DataValidator? validator, Action? failHandler) { JArray? sourceArray; @@ -366,8 +360,7 @@ namespace UVC.Data.Http { if (failHandler != null) { - await UniTask.SwitchToMainThread(); - failHandler.Invoke("Data is not Valid or empty after validation"); + UniTask.Post(() => failHandler.Invoke("Data is not Valid or empty after validation")); } return null; } diff --git a/Assets/Scripts/UVC/Data/Mqtt/MqttDataReceiver.cs b/Assets/Scripts/UVC/Data/Mqtt/MqttDataReceiver.cs index 369ef171..7ad9aea1 100644 --- a/Assets/Scripts/UVC/Data/Mqtt/MqttDataReceiver.cs +++ b/Assets/Scripts/UVC/Data/Mqtt/MqttDataReceiver.cs @@ -1,11 +1,13 @@ -#nullable enable +#nullable enable using Cysharp.Threading.Tasks; using Newtonsoft.Json.Linq; using SampleProject.Config; using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Threading; +using UnityEngine; using UVC.Data.Core; using UVC.Data.Http; using UVC.Log; @@ -91,7 +93,7 @@ namespace UVC.Data.Mqtt /// /// 토픽별 파이프라인 정보를 저장하는 딕셔너리 /// - private Dictionary infoList; + private ConcurrentDictionary configList; private MqttWorker mqttWorker; @@ -103,7 +105,7 @@ namespace UVC.Data.Mqtt public MqttDataReceiver() { mqttWorker = new MqttWorker(); - infoList = new Dictionary(); + configList = new ConcurrentDictionary(); } /// @@ -141,14 +143,7 @@ namespace UVC.Data.Mqtt /// public void Add(MqttSubscriptionConfig info) { - if (!infoList.ContainsKey(info.Topic)) - { - infoList.Add(info.Topic, info); - } - else - { - infoList[info.Topic] = info; - } + configList[info.Topic] = info; } /// @@ -157,10 +152,7 @@ namespace UVC.Data.Mqtt /// 제거할 토픽 이름 public void Remove(string topic) { - if (infoList.ContainsKey(topic)) - { - infoList.Remove(topic); - } + configList.TryRemove(topic, out _); } /// @@ -181,7 +173,7 @@ namespace UVC.Data.Mqtt { // Mockup 모드인 경우 MockMQTTService를 사용하여 테스트 환경을 설정합니다. mockupMQTT = new MockMQTTService(); - foreach (var topic in infoList.Keys) + foreach (var topic in configList.Keys) { mockupMQTT.AddTopicHandler(topic, OnTopicMessage); } @@ -231,38 +223,39 @@ namespace UVC.Data.Mqtt /// 등록된 데이터 매퍼를 통해 메시지를 변환한 후, 해당 토픽에 등록된 핸들러에게 전달합니다. /// 'UpdatedDataOnly' 설정에 따라 데이터가 변경된 경우에만 핸들러를 호출할 수도 있습니다. /// - private async void OnTopicMessageLogic(string topic, string message) + private void OnTopicMessageLogic(string topic, string message) { + //Debug.Log($"OnTopicMessageLogic topic: {topic}, configList.ContainsKey(topic): {configList.ContainsKey(topic)}"); // 토픽이 infoList와 readyHandlerList에 존재하고, 준비 상태가 true인 경우에만 처리합니다. - if (infoList.ContainsKey(topic)) + if (configList.TryGetValue(topic, out var config)) { - MqttSubscriptionConfig info = infoList[topic]; IDataObject? mappedObject = null; message = message.Trim(); + if (!string.IsNullOrEmpty(message)) { try { if (message.StartsWith("{")) { - mappedObject = await HttpDataProcessor.ProcessObjectResponse(message, null, info.DataMapper, info.Validator, null); + mappedObject = HttpDataProcessor.ProcessObjectResponse(message, null, config.DataMapper, config.Validator, null); } else if (message.StartsWith("[")) { - mappedObject = await HttpDataProcessor.ProcessArrayResponse(message, null, info.DataMapper, info.Validator, null); + mappedObject = HttpDataProcessor.ProcessArrayResponse(message, null, config.DataMapper, config.Validator, null); } - + //Debug.Log($"OnTopicMessageLogic topic: {topic}, mappedObject == null: {mappedObject == null}, config.DataMapper:{config.DataMapper == null}, config.Validator:{config.Validator == null}"); if (mappedObject == null) return; // DataRepository는 내부적으로 데이터를 복사/업데이트하므로, mappedObject는 여기서 임시 객체가 됩니다. - var repoObject = DataRepository.Instance.AddOrUpdateData(topic, mappedObject, info.UpdatedDataOnly); + var repoObject = DataRepository.Instance.AddOrUpdateData(topic, mappedObject, config.UpdatedDataOnly); if (repoObject == mappedObject) repoObject = mappedObject.Clone(fromPool: false); // 핸들러 호출이 필요한지 확인 - bool shouldInvoke = !info.UpdatedDataOnly || (repoObject != null && repoObject.UpdatedCount > 0); - if (shouldInvoke) + bool shouldInvoke = !config.UpdatedDataOnly || (repoObject != null && repoObject.UpdatedCount > 0); + if (shouldInvoke && config.Handler != null) { var handlerData = repoObject; // 핸들러를 메인 스레드에서 안전하게 호출 - UniTask.Post(() => info.Handler?.Invoke(handlerData)); + UniTask.Post(() => config.Handler?.Invoke(handlerData)); } } catch (Exception ex) @@ -292,7 +285,7 @@ namespace UVC.Data.Mqtt if (!UseMockup) { if (!mqttWorker.IsRunning) return; - foreach (var topic in infoList.Keys) + foreach (var topic in configList.Keys) { mqttWorker.RemoveListener(topic, OnTopicPacketMessage); } @@ -314,7 +307,7 @@ namespace UVC.Data.Mqtt { if (!UseMockup) mqttWorker.Dispose(); else mockupMQTT?.Disconnect(); - infoList.Clear(); + configList.Clear(); } } diff --git a/Assets/Scripts/UVC/Data/Mqtt/MqttWorker.cs b/Assets/Scripts/UVC/Data/Mqtt/MqttWorker.cs index de0cf126..9e6b2d34 100644 --- a/Assets/Scripts/UVC/Data/Mqtt/MqttWorker.cs +++ b/Assets/Scripts/UVC/Data/Mqtt/MqttWorker.cs @@ -1,4 +1,4 @@ -using Cysharp.Threading.Tasks; +using Cysharp.Threading.Tasks; using NUnit.Framework; using System; using System.Collections.Concurrent; diff --git a/Assets/Scripts/UVC/Event/UniTaskEventHandler.cs b/Assets/Scripts/UVC/Event/UniTaskEventHandler.cs index a59f8421..dcf9265f 100644 --- a/Assets/Scripts/UVC/Event/UniTaskEventHandler.cs +++ b/Assets/Scripts/UVC/Event/UniTaskEventHandler.cs @@ -1,29 +1,29 @@ -using Cysharp.Threading.Tasks; +using Cysharp.Threading.Tasks; namespace UVC.Event { /// - /// 񵿱 ̺Ʈ ó ׸ Ʈ - /// UniTask ȯϴ ̺Ʈ ڵ鷯 Ͽ 񵿱 ۾ ̺Ʈ ó ְ + /// 비동기 이벤트 처리를 위한 델리게이트입니다. + /// UniTask를 반환하는 이벤트 핸들러를 정의하여, 비동기 작업을 이벤트 기반으로 처리할 수 있게 합니다. /// - /// ̺Ʈ ͸ ϴ ׸ Ÿ Ű - /// ̺Ʈ ߻Ų ü - /// ̺Ʈ õ - /// 񵿱 ۾ Ÿ UniTask + /// 이벤트 데이터를 담는 제네릭 타입 매개변수 + /// 이벤트를 발생시킨 객체 + /// 이벤트에 포함된 데이터 + /// 비동기 작업을 나타내는 UniTask /// /// - /// // ̺Ʈ + /// // 이벤트 정의 예시 /// public class NetworkManager /// { - /// // UniTaskEventHandler Ÿ ̺Ʈ + /// // UniTaskEventHandler 타입의 이벤트 선언 /// public event UniTaskEventHandler OnDataReceived; /// - /// // ̺Ʈ ߻ ޼ҵ + /// // 이벤트 발생 메서드 /// public async UniTask RaiseDataReceivedEvent(DataReceivedEventArgs args) /// { /// if (OnDataReceived != null) /// { - /// // ϵ ڵ鷯 + /// // 모든 등록된 핸들러를 비동기적으로 실행 /// foreach (var handler in OnDataReceived.GetInvocationList()) /// { /// await ((UniTaskEventHandler)handler).Invoke(this, args); @@ -32,7 +32,7 @@ namespace UVC.Event /// } /// } /// - /// // ̺Ʈ + /// // 이벤트 구독 예시 /// public class DataProcessor /// { /// public void Initialize(NetworkManager networkManager) @@ -42,17 +42,17 @@ namespace UVC.Event /// /// private async UniTask HandleDataReceived(object sender, DataReceivedEventArgs e) /// { - /// // 񵿱 ̺Ʈ ó + /// // 비동기 이벤트 처리 로직 /// await ProcessDataAsync(e.Data); /// } /// - /// private async UniTask ProcessDataAsync(byte[] data) + /// private async UniTask ProcessDataAsync(byte[] buffers) /// { - /// await UniTask.Delay(100); // 񵿱 ۾ - /// // ó + /// await UniTask.Delay(100); // 예시 비동기 작업 + /// // 데이터 처리 로직 /// } /// } /// /// public delegate UniTask UniTaskEventHandler(object sender, TEventArgs e); -} +} \ No newline at end of file diff --git a/Assets/Scripts/UVC/Extention/RectTransformEx.cs b/Assets/Scripts/UVC/Extention/RectTransformEx.cs index b2522c2e..9cb799a0 100644 --- a/Assets/Scripts/UVC/Extention/RectTransformEx.cs +++ b/Assets/Scripts/UVC/Extention/RectTransformEx.cs @@ -4,28 +4,28 @@ using UnityEngine; namespace UVC.Extension { /// - /// RectTransform Ȯ ޼带 ϴ ŬԴϴ. + /// RectTransform에 대한 확장 메서드를 제공하는 클래스입니다. /// public static class RectTransformEx { /// - /// RectTransform մϴ. θ ü ¿ մϴ. - /// Ŀ ڵ 𼭸 ˴ϴ(anchorMin=[0,0], anchorMax=[1,1]). + /// RectTransform의 여백을 설정합니다. 부모를 기준으로 모든 방향의 여백을 설정하여 크기를 조절합니다. + /// 앵커는 부모의 전체를 채우도록 설정됩니다(anchorMin=[0,0], anchorMax=[1,1]). /// - /// RectTransform - /// - /// - /// - /// Ʒ + /// 대상 RectTransform + /// 왼쪽 여백 + /// 오른쪽 여백 + /// 위쪽 여백 + /// 아래쪽 여백 /// /// - /// // Ʈ RectTransform + /// // 패널 RectTransform의 여백 설정 /// RectTransform panelRect = panel.GetComponent(); - /// panelRect.SetRectMargin(10f, 10f, 10f, 10f); // 10ȼ + /// panelRect.SetRectMargin(10f, 10f, 10f, 10f); // 모든 방향에 10의 여백 설정 /// - /// // UI Ҹ θ ̳ʿ ߵ ֱ + /// // 자식 UI 요소의 여백 다르게 설정 /// RectTransform childRect = childObject.GetComponent(); - /// childRect.SetRectMargin(5f, 5f, 20f, 5f); // ܿ ū + /// childRect.SetRectMargin(5f, 5f, 20f, 5f); // 위쪽에 더 많은 여백 설정 /// /// public static void SetRectMargin(this RectTransform trans, float left, float right, float top, float bottom) @@ -37,14 +37,14 @@ namespace UVC.Extension } /// - /// RectTransform ʺ մϴ. + /// RectTransform의 너비를 설정합니다. /// - /// RectTransform - /// ʺ + /// 대상 RectTransform + /// 설정할 너비 /// /// - /// // ư ʺ 200 - /// RectTransform buttonRect = button.GetComponent(); + /// // 버튼의 너비를 200으로 설정 + /// RectTransform buttonRect = searchButton.GetComponent(); /// buttonRect.SetWidth(200f); /// /// @@ -54,13 +54,13 @@ namespace UVC.Extension } /// - /// RectTransform ̸ մϴ. + /// RectTransform의 높이를 설정합니다. /// - /// RectTransform - /// + /// 대상 RectTransform + /// 설정할 높이 /// /// - /// // г ̸ 150 + /// // 패널의 높이를 150으로 설정 /// RectTransform panelRect = panel.GetComponent(); /// panelRect.SetHeight(150f); /// @@ -71,13 +71,13 @@ namespace UVC.Extension } /// - /// RectTransform ũ⸦ մϴ. + /// RectTransform의 크기를 설정합니다. /// - /// RectTransform - /// ũ (ʺ, ) + /// 대상 RectTransform + /// 설정할 크기 (너비, 높이) /// /// - /// // ̹ ũ⸦ 100x100 + /// // 이미지의 크기를 100x100으로 설정 /// RectTransform imageRect = image.GetComponent(); /// imageRect.SetSize(new Vector2(100f, 100f)); /// @@ -89,15 +89,15 @@ namespace UVC.Extension } /// - /// RectTransform Ŀ ߾ӿ ġŰ ǹ ߾ մϴ. + /// RectTransform의 앵커와 피벗을 중앙으로 설정합니다. /// - /// RectTransform + /// 대상 RectTransform /// /// - /// // ư ȭ ߾ӿ ġŰ - /// RectTransform buttonRect = button.GetComponent(); + /// // 버튼의 기준점을 중앙으로 설정 + /// RectTransform buttonRect = searchButton.GetComponent(); /// buttonRect.SetAnchorsToCenter(); - /// buttonRect.anchoredPosition = Vector2.zero; // ߾ ġ ġ + /// buttonRect.anchoredPosition = Vector2.zero; // 중앙에 배치 /// /// public static void SetAnchorsToCenter(this RectTransform trans) @@ -108,15 +108,15 @@ namespace UVC.Extension } /// - /// RectTransform Ŀ ǹ մϴ. + /// RectTransform의 앵커와 피벗을 왼쪽 상단으로 설정합니다. /// - /// RectTransform + /// 대상 RectTransform /// /// - /// // UI Ҹ ܿ ġŰ + /// // UI 요소의 기준점을 왼쪽 상단으로 설정 /// RectTransform elementRect = element.GetComponent(); /// elementRect.SetAnchorsToTopLeft(); - /// elementRect.anchoredPosition = new Vector2(10f, -10f); // ణ ߰ + /// elementRect.anchoredPosition = new Vector2(10f, -10f); // 왼쪽 상단에서 오프셋 설정 /// /// public static void SetAnchorsToTopLeft(this RectTransform trans) @@ -127,12 +127,12 @@ namespace UVC.Extension } /// - /// RectTransform θ մϴ( ). + /// RectTransform을 부모의 크기에 맞게 늘립니다 (모든 여백 0). /// - /// RectTransform + /// 대상 RectTransform /// /// - /// // ̹ г ü ä + /// // 배경 이미지를 부모 패널에 꽉 채우기 /// RectTransform backgroundRect = backgroundImage.GetComponent(); /// backgroundRect.StretchToParentEdges(); /// @@ -143,13 +143,13 @@ namespace UVC.Extension } /// - /// RectTransform ġ θ RectTransform ġ(0~1) մϴ. + /// RectTransform의 위치를 부모 RectTransform 내의 정규화된 좌표(0~1)로 설정합니다. /// - /// RectTransform - /// ȭ ġ (0,0= ϴ, 1,1= ) + /// 대상 RectTransform + /// 정규화된 위치 (0,0=왼쪽 하단, 1,1=오른쪽 상단) /// /// - /// // Ҹ θ ܿ ġ + /// // UI 요소를 부모의 오른쪽 상단 근처에 배치 /// RectTransform elementRect = element.GetComponent(); /// elementRect.SetNormalizedPosition(new Vector2(0.95f, 0.95f)); /// @@ -170,13 +170,13 @@ namespace UVC.Extension } /// - /// RectTransform 簢 ǥ ȯմϴ. + /// RectTransform의 월드 좌표 기준 사각 영역을 가져옵니다. /// - /// RectTransform - /// ǥ 簢 + /// 대상 RectTransform + /// 월드 좌표계에서의 Rect /// /// - /// // UI Ұ Ư ǥ ϴ Ȯ + /// // 마우스가 UI 요소 위에 있는지 확인 /// RectTransform elementRect = element.GetComponent(); /// Rect worldRect = elementRect.GetWorldRect(); /// Vector3 worldPos = Camera.main.ScreenToWorldPoint(Input.mousePosition); diff --git a/Assets/Scripts/UVC/Factory/Alarm/AlarmManager.cs b/Assets/Scripts/UVC/Factory/Alarm/AlarmManager.cs index 5659da1a..54a83659 100644 --- a/Assets/Scripts/UVC/Factory/Alarm/AlarmManager.cs +++ b/Assets/Scripts/UVC/Factory/Alarm/AlarmManager.cs @@ -1,10 +1,11 @@ -#nullable enable +#nullable enable using SampleProject; using System; using System.Collections.Generic; using System.Linq; using UnityEngine; +using UnityEngine.LightTransport; using UVC.Core; using UVC.Data; using UVC.Data.Core; @@ -34,7 +35,7 @@ namespace UVC.Factory.Alarm // 알람 데이터에 포함된 설비 ID를 이용해 실제 3D 객체를 찾기 위해 사용됩니다. private FactoryObjectManager? dataManager; - private bool testMode = true; // 테스트 모드 여부를 나타내는 플래그입니다. + private bool testMode = false; // 테스트 모드 여부를 나타내는 플래그입니다. // 테스트용으로 사용할 AGV 이름 리스트입니다. private List agvNames = new List(); @@ -45,6 +46,10 @@ namespace UVC.Factory.Alarm // 테스트용으로, 새로 발생하는 알람에 AGV ID를 순차적으로 할당하기 위한 인덱스입니다. private int agvIdx = 50; + private List buffers = new List(); + + private bool runned = false; + /// /// AlarmManager의 초기화 메서드입니다. /// Awake 메서드에서 호출되며, MonoBehaviour가 생성될 때 한 번만 실행됩니다. @@ -54,6 +59,9 @@ namespace UVC.Factory.Alarm // SceneMain의 초기화가 완료되었을 때 OnSceneInitialized 메서드를 호출하도록 이벤트에 등록합니다. // 이를 통해 필요한 다른 매니저들이 준비된 후에 로직을 실행할 수 있습니다. SceneMain.Instance.Initialized += OnSceneInitialized; + + //playback에서도 데이터를 업데이트 하기에 DataRepository에 핸들러를 추가합니다. + DataRepository.Instance.AddDataUpdateHandler("ALARM", OnUpdateData); } /// @@ -81,7 +89,8 @@ namespace UVC.Factory.Alarm /// 이 메서드는 외부에서 호출되어야 알람 데이터 수신이 시작됩니다. /// public void Run() - { + { + Debug.Log($"AlarmManager Run. buffers.Count:{buffers.Count}"); // MQTT 파이프라인 정보(MqttSubscriptionConfig) 생성: // - "ALARM" 토픽을 구독합니다. // - 위에서 정의한 dataMask를 사용해 수신된 JSON 데이터를 DataObject로 변환합니다. @@ -96,10 +105,30 @@ namespace UVC.Factory.Alarm // 4. 생성한 파이프라인을 전역 MQTT 파이프라인에 추가하여 데이터 수신을 시작합니다. DataRepository.Instance.MqttReceiver.Add(pipelineInfo); - //playback에서도 데이터를 업데이트 하기에 DataRepository에 핸들러를 추가합니다. - DataRepository.Instance.AddDataUpdateHandler("ALARM", OnUpdateData); + runned = true; + + // 초기 데이터가 있다면 즉시 업데이트를 수행합니다. + if (buffers.Count == 0) { + var buffer = DataRepository.Instance.GetData("ALARM"); + Debug.Log($"AlarmManager Run. buffer == null:{buffer == null}"); + if (buffer != null) + { + OnUpdateData(buffer); + } + } + else + { + // 초기 데이터가 있다면, OnUpdateData를 호출하여 기존 데이터를 처리합니다. + foreach (var buffer in buffers) + { + OnUpdateData(buffer); + } + buffers.Clear(); // 초기 데이터 처리가 끝나면 버퍼를 비웁니다. + } } + + /// /// MQTT 파이프라인으로부터 데이터가 업데이트될 때마다 호출되는 메인 핸들러입니다. /// 수신된 알람 데이터 배열을 분석하여 추가, 수정, 제거된 알람을 각각 처리합니다. @@ -113,12 +142,21 @@ namespace UVC.Factory.Alarm DataArray? arr = data as DataArray; if (arr == null || arr.Count == 0) return; + //Debug.Log($"AlarmManager OnUpdateData: count:{arr.Count}, Added={arr.AddedItems.Count}, Removed={arr.RemovedItems.Count}, Modified={arr.ModifiedList.Count}"); + + //3d 객체가 생성 된 후 호출 되도록 하기 위해 + // Run으로 호출 된 후 호출 되도록 + if (!runned) + { + buffers.Add(data); + return; + } + // 데이터 배열에서 추가, 제거, 수정된 항목 리스트를 가져옵니다. var AddedItems = arr.AddedItems; var RemovedItems = new List(arr.RemovedItems); var ModifiedList = arr.ModifiedList; - //Debug.Log($"AlarmManager OnUpdateData: Added={AddedItems.Count}, Removed={RemovedItems.Count}, Modified={ModifiedList.Count}"); // 'CLEAR_TIME'이 설정된 항목은 '해제된 알람'으로 간주하고, 제거 리스트에 추가합니다. // AddedItems나 ModifiedList에 포함되어 있더라도 CLEAR_TIME이 있으면 즉시 해제 처리하기 위함입니다. diff --git a/Assets/Scripts/UVC/Factory/Buttons/UISearchInput.cs b/Assets/Scripts/UVC/Factory/Buttons/UISearchInput.cs index 14e51776..ce4d3214 100644 --- a/Assets/Scripts/UVC/Factory/Buttons/UISearchInput.cs +++ b/Assets/Scripts/UVC/Factory/Buttons/UISearchInput.cs @@ -7,16 +7,15 @@ using UnityEngine.UI; using UVC.Factory.Component; using UVC.Locale; using UVC.UI.Modal; -using UVC.Util; namespace UVC.Factory.Buttons { public class UISearchInput : MonoBehaviour { + [SerializeField] private TMP_InputField inputField; - - private Button button; - private GameObject textArea; + [SerializeField] + private Button searchButton; private bool initialized = false; @@ -27,31 +26,23 @@ namespace UVC.Factory.Buttons SceneMain.Instance.Initialized += OnSceneInitialized; } - private void OnSceneInitialized() + private void OnSceneInitialized() { - inputField = GetComponent(); - - button = GetComponentInChildren