From cd8c5e177bb9a2fd8fadc32eecc1c49a6ac1e50f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=ED=98=95=EC=9D=B8?= Date: Wed, 11 Jun 2025 00:36:58 +0900 Subject: [PATCH] =?UTF-8?q?http=20=EC=9D=91=EB=8B=B5=20=EA=B2=80=EC=A6=9D?= =?UTF-8?q?=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/Scripts/UVC/Data/HttpPipeLine.cs | 15 ++++- Assets/Scripts/UVC/Data/HttpPipeLineInfo.cs | 55 +++++++++++++++++++ Assets/Scripts/UVC/Data/MQTTPipeLine.cs | 47 ++++++++++------ Assets/Scripts/UVC/Network/MQTTService.cs | 10 ++-- .../UVC/Tests/Data/HttpPipeLineTests.cs | 18 +++--- .../UVC/Tests/Data/MQTTPipeLineTests.cs | 5 +- Assets/Scripts/UVC/Tests/MockHttpRequester.cs | 2 +- Assets/Scripts/UVC/Tests/Tester.cs | 4 +- 8 files changed, 120 insertions(+), 36 deletions(-) diff --git a/Assets/Scripts/UVC/Data/HttpPipeLine.cs b/Assets/Scripts/UVC/Data/HttpPipeLine.cs index a04a9c08..01ca8362 100644 --- a/Assets/Scripts/UVC/Data/HttpPipeLine.cs +++ b/Assets/Scripts/UVC/Data/HttpPipeLine.cs @@ -227,11 +227,24 @@ namespace UVC.Data } IDataObject? dataObject = null; + result = result.Trim(); + if (!string.IsNullOrEmpty(result)) { - result = result.Trim(); try { + string? responseData = info.ResponseMask.Apply(result); + // responseData가 null인 경우는 올바른 응답이 아니므로 처리하지 않음 + if (responseData == null) + { + throw new Exception($"유효하지 않은 response data. {result}"); + } + else + { + result = responseData.Trim(); + } + + if (result.StartsWith("{")) { if (info.DataMapper != null) diff --git a/Assets/Scripts/UVC/Data/HttpPipeLineInfo.cs b/Assets/Scripts/UVC/Data/HttpPipeLineInfo.cs index 7c7fb204..220fa9d4 100644 --- a/Assets/Scripts/UVC/Data/HttpPipeLineInfo.cs +++ b/Assets/Scripts/UVC/Data/HttpPipeLineInfo.cs @@ -1,5 +1,6 @@ #nullable enable +using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; @@ -34,6 +35,7 @@ namespace UVC.Data private int _repeatCount = 0; // 반복 횟수, 0은 무한 반복 private int _repeatInterval = 1000; // 반복 간격 (ms) private DataMapper? _dataMapper = null; // 데이터 매퍼 + private ResponseMask _responseMask = new ResponseMask(); // response 데이터 성공 여부와 데이터를 구분하는 마스크 private int _maxRetryCount = 3; private int _retryDelay = 1000; // 밀리초 private bool _updatedDataOnly = false; // 업데이트된 데이터만 받을 여부 @@ -83,6 +85,11 @@ namespace UVC.Data /// public DataMapper? DataMapper => _dataMapper; + /// + /// response에 적용되는 데이터 마스크 + /// + public ResponseMask ResponseMask => _responseMask; + /// /// 최대 재시도 횟수 /// @@ -125,6 +132,17 @@ namespace UVC.Data return this; } + /// + /// HTTP 파이프라인에 적용할 response 마스크를 설정하고 업데이트된 파이프라인 구성을 반환합니다. + /// + /// HTTP response에 적용할 입니다. + /// 지정된 response 마스크가 적용된 업데이트된 인스턴스입니다. + public HttpPipeLineInfo setResponseMask(ResponseMask responseMask) + { + _responseMask = responseMask; + return this; + } + /// /// HTTP 요청이 완료된 후 호출될 핸들러를 설정합니다. /// 변경 된 데이터는 IDataObject로 전달됩니다. @@ -186,4 +204,41 @@ namespace UVC.Data } } + + public class ResponseMask + { + private string successKey = "message"; + private string successValue = "success"; + private string dataKey = "data"; + + public string SuccessKey { get; } + public string SuccessValue { get; } + public string DataKey { get; } + + public ResponseMask() { } + + public ResponseMask(string successKey, string successValue, string dataKey) + { + this.successKey = successKey; + this.successValue = successValue; + this.dataKey = dataKey; + } + + + public string? Apply(string response) + { + JObject responseObject = JObject.Parse(response); + if(responseObject.TryGetValue(successKey, out JToken? successToken) && + responseObject.TryGetValue(dataKey, out JToken? dataToken) && + successToken?.ToString().ToLower() == successValue) + { + // 성공적인 응답 처리 로직 + return dataToken?.ToString(); // 실제로는 dataKey에 해당하는 데이터만 반환할 수 있습니다. + } + else + { + return null; // 실패한 응답 처리 로직 + } + } + } } diff --git a/Assets/Scripts/UVC/Data/MQTTPipeLine.cs b/Assets/Scripts/UVC/Data/MQTTPipeLine.cs index 862b3777..6d00136a 100644 --- a/Assets/Scripts/UVC/Data/MQTTPipeLine.cs +++ b/Assets/Scripts/UVC/Data/MQTTPipeLine.cs @@ -2,7 +2,9 @@ using Newtonsoft.Json.Linq; using SampleProject.Config; +using System; using System.Collections.Generic; +using UVC.Log; using UVC.network; using UVC.Tests; @@ -135,30 +137,41 @@ namespace UVC.Data { MQTTPipeLineInfo info = infoList[topic]; IDataObject? dataObject = null; + + message = message.Trim(); if (!string.IsNullOrEmpty(message)) { - message = message.Trim(); - if (message.StartsWith("{")) + try { - JObject source = JObject.Parse(message); - if (info.DataMapper != null) dataObject = info.DataMapper.Map(source); + if (message.StartsWith("{")) + { + JObject source = JObject.Parse(message); + if (info.DataMapper != null) dataObject = info.DataMapper.Map(source); + } + else if (message.StartsWith("[")) + { + JArray source = JArray.Parse(message); + if (info.DataMapper != null) dataObject = info.DataMapper.Map(source); + } + + if (dataObject != null) dataObject = DataRepository.Instance.AddData(topic, dataObject, info.UpdatedDataOnly); + + // 갱신 된 데이터가 있는 경우 핸들러 호출 + if (info.UpdatedDataOnly) + { + if (dataObject != null && dataObject.UpdatedCount > 0) info.Handler?.Invoke(dataObject); + } + else + { + info.Handler?.Invoke(dataObject); + } } - else if (message.StartsWith("[")) + catch (Exception ex) { - JArray source = JArray.Parse(message); - if (info.DataMapper != null) dataObject = info.DataMapper.Map(source); + // 예외 발생 시 로깅 또는 처리 + ULog.Error($"Error processing message for topic '{topic}': {ex.Message}", ex); } } - if (dataObject != null) dataObject = DataRepository.Instance.AddData(topic, dataObject, info.UpdatedDataOnly); - // 갱신 된 데이터가 있는 경우 핸들러 호출 - if (info.UpdatedDataOnly) - { - if (dataObject != null && dataObject.UpdatedCount > 0) info.Handler?.Invoke(dataObject); - } - else - { - info.Handler?.Invoke(dataObject); - } } } diff --git a/Assets/Scripts/UVC/Network/MQTTService.cs b/Assets/Scripts/UVC/Network/MQTTService.cs index f17f4936..d936191d 100644 --- a/Assets/Scripts/UVC/Network/MQTTService.cs +++ b/Assets/Scripts/UVC/Network/MQTTService.cs @@ -323,11 +323,13 @@ namespace UVC.network string payload = Encoding.UTF8.GetString(message.Payload.Data, message.Payload.Offset, message.Payload.Count); ULog.Debug($"MQTT OnTopic {topic.Filter.OriginalFilter} => {payload}"); ServerLog.LogMqtt(MQTTDomain, MQTTPort.ToString(), topic.Filter.OriginalFilter, payload, DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss.fffZ")); - - if(topicHandler.TryGetValue(topic.Filter.OriginalFilter, out var handler)) + if(payload.Trim().Length > 0) { - // 등록된 핸들러가 있으면 호출 - handler.Invoke(topic.Filter.OriginalFilter, payload); + if (topicHandler.TryGetValue(topic.Filter.OriginalFilter, out var handler)) + { + // 등록된 핸들러가 있으면 호출 + handler.Invoke(topic.Filter.OriginalFilter, payload); + } } } diff --git a/Assets/Scripts/UVC/Tests/Data/HttpPipeLineTests.cs b/Assets/Scripts/UVC/Tests/Data/HttpPipeLineTests.cs index 94b0d73d..13a41da0 100644 --- a/Assets/Scripts/UVC/Tests/Data/HttpPipeLineTests.cs +++ b/Assets/Scripts/UVC/Tests/Data/HttpPipeLineTests.cs @@ -222,7 +222,7 @@ namespace UVC.Tests.Data bool handlerCalled = false; DataObject? receivedData = null; - var mockResponse = @"{""name"": ""테스트"", ""value"": 123}"; + var mockResponse = @"{""message"": ""Success"", ""data"": {""name"": ""테스트"", ""value"": 123}}"; // DataMask와 DataMapper 설정 var dataMask = new DataMask(); @@ -277,7 +277,7 @@ namespace UVC.Tests.Data bool handlerCalled = false; IDataObject? receivedData = null; - var mockResponse = @"[{""name"": ""항목1"", ""value"": 10}, {""name"": ""항목2"", ""value"": 20}]"; + var mockResponse = @"{""message"": ""Success"", ""data"": [{""name"": ""항목1"", ""value"": 10}, {""name"": ""항목2"", ""value"": 20}]}"; // 배열용 DataMask 설정 var dataMask = new DataMask @@ -701,9 +701,9 @@ namespace UVC.Tests.Data // 여러 응답을 순차적으로 반환하기 위한 응답 데이터 설정 string[] mockResponses = new string[] { - @"{""id"": 1, ""status"": ""pending"", ""timestamp"": ""2025-06-09T10:00:00Z""}", - @"{""id"": 1, ""status"": ""processing"", ""timestamp"": ""2025-06-09T10:00:10Z""}", - @"{""id"": 1, ""status"": ""completed"", ""timestamp"": ""2025-06-09T10:00:20Z""}" + @"{""message"": ""Success"", ""data"": {""id"": 1, ""status"": ""pending"", ""timestamp"": ""2025-06-09T10:00:00Z""}}", + @"{""message"": ""Success"", ""data"": {""id"": 1, ""status"": ""processing"", ""timestamp"": ""2025-06-09T10:00:10Z""}}", + @"{""message"": ""Success"", ""data"": {""id"": 1, ""status"": ""completed"", ""timestamp"": ""2025-06-09T10:00:20Z""}}" }; // Mock 응답 설정 @@ -780,7 +780,7 @@ namespace UVC.Tests.Data int repeatInterval = 100; // Mock 응답 설정 - string mockResponse = @"{""id"": 2, ""status"": ""running"", ""timestamp"": ""2025-06-09T11:00:00Z""}"; + string mockResponse = @"{""message"": ""Success"", ""data"": {""id"": 2, ""status"": ""running"", ""timestamp"": ""2025-06-09T11:00:00Z""}}"; MockHttpRequester.SetResponse(testUrl, mockResponse); // DataMask와 DataMapper 설정 @@ -846,8 +846,8 @@ namespace UVC.Tests.Data int repeatInterval2 = 150; // Mock 응답 설정 - string mockResponse1 = @"{""id"": 3, ""name"": ""작업1""}"; - string mockResponse2 = @"{""id"": 4, ""name"": ""작업2""}"; + string mockResponse1 = @"{""message"": ""Success"", ""data"": {""id"": 3, ""name"": ""작업1""}}"; + string mockResponse2 = @"{""message"": ""Success"", ""data"": {""id"": 4, ""name"": ""작업2""}}"; MockHttpRequester.SetResponse(testUrl1, mockResponse1); MockHttpRequester.SetResponse(testUrl2, mockResponse2); @@ -931,7 +931,7 @@ namespace UVC.Tests.Data int repeatInterval = 100; // Mock 응답 설정 - string mockResponse = @"{""id"": 5, ""message"": ""자동 중단 테스트""}"; + string mockResponse = @"{""message"": ""Success"", ""data"": {""id"": 5, ""message"": ""자동 중단 테스트""}}"; MockHttpRequester.SetResponse(testUrl, mockResponse); // DataMask 설정 diff --git a/Assets/Scripts/UVC/Tests/Data/MQTTPipeLineTests.cs b/Assets/Scripts/UVC/Tests/Data/MQTTPipeLineTests.cs index cc435688..4d8d3c87 100644 --- a/Assets/Scripts/UVC/Tests/Data/MQTTPipeLineTests.cs +++ b/Assets/Scripts/UVC/Tests/Data/MQTTPipeLineTests.cs @@ -43,14 +43,15 @@ namespace UVC.Tests.Data { Setup(); Debug.Log("===== MQTTPipeLine 테스트 시작 ====="); + // 하나씩 테스트 해야 함 //await RunTestAsync(nameof(ExecutePipeLine_AllTopics_RegistersAndHandlesMessages), ExecutePipeLine_AllTopics_RegistersAndHandlesMessages); //await RunTestAsync(nameof(RemoveTopic_ShouldStopReceivingMessages), RemoveTopic_ShouldStopReceivingMessages); //await RunTestAsync(nameof(UpdatedDataOnly_ShouldOnlyCallHandlerForUpdatedData), UpdatedDataOnly_ShouldOnlyCallHandlerForUpdatedData); - await RunTestAsync(nameof(UpdatedDataOnly_WithMockMQTTService_ShouldOnlyReceiveUpdatedData), UpdatedDataOnly_WithMockMQTTService_ShouldOnlyReceiveUpdatedData); + //await RunTestAsync(nameof(UpdatedDataOnly_WithMockMQTTService_ShouldOnlyReceiveUpdatedData), UpdatedDataOnly_WithMockMQTTService_ShouldOnlyReceiveUpdatedData); //RunTest(nameof(OnTopicMessage_ValidJsonObject_CallsHandler), OnTopicMessage_ValidJsonObject_CallsHandler); //RunTest(nameof(OnTopicMessage_JsonArray_CallsHandler), OnTopicMessage_JsonArray_CallsHandler); //RunTest(nameof(OnTopicMessage_EmptyMessage_DoesNotCallHandler), OnTopicMessage_EmptyMessage_DoesNotCallHandler); - //RunTest(nameof(OnTopicMessage_InvalidJson_DoesNotCallHandler), OnTopicMessage_InvalidJson_DoesNotCallHandler); + RunTest(nameof(OnTopicMessage_InvalidJson_DoesNotCallHandler), OnTopicMessage_InvalidJson_DoesNotCallHandler); Debug.Log("===== MQTTPipeLine 테스트 완료 ====="); } diff --git a/Assets/Scripts/UVC/Tests/MockHttpRequester.cs b/Assets/Scripts/UVC/Tests/MockHttpRequester.cs index 6d779724..76ce1d93 100644 --- a/Assets/Scripts/UVC/Tests/MockHttpRequester.cs +++ b/Assets/Scripts/UVC/Tests/MockHttpRequester.cs @@ -80,7 +80,7 @@ namespace UVC.Tests } else { - response = GetResponse(url); + response = "{\"message\":\"Success\", \"data\":" + GetResponse(url) + "}"; } if (typeof(T) == typeof(string)) diff --git a/Assets/Scripts/UVC/Tests/Tester.cs b/Assets/Scripts/UVC/Tests/Tester.cs index ae8fe5e1..5a6f3fa2 100644 --- a/Assets/Scripts/UVC/Tests/Tester.cs +++ b/Assets/Scripts/UVC/Tests/Tester.cs @@ -7,8 +7,8 @@ namespace UVC.Tests public static void RunAllTests() { //new DataMapperTests().TestAll(); - //new HttpPipeLineTests().TestAll(); - new MQTTPipeLineTests().TestAll(); + new HttpPipeLineTests().TestAll(); + //new MQTTPipeLineTests().TestAll(); } } }