Files
XRLib/Assets/Scripts/UVC/Data/Mqtt/MqttDataPacket.cs
2025-09-23 20:40:31 +09:00

207 lines
9.0 KiB
C#

#nullable enable
using NUnit.Framework;
using System;
using System.Collections.Generic;
using UVC.Data.Core;
namespace UVC.Data.Mqtt
{
/// <summary>
/// 수신된 단일 MQTT 메시지에 대한 모든 정보를 담는 데이터 컨테이너 클래스입니다.
/// 이 객체는 MqttWorker가 메시지를 수신했을 때 생성되며, MqttDataManager를 통해
/// 최종적으로 데이터 소비자에게 전달됩니다.
/// </summary>
/// <example>
/// <code>
/// // MqttDataPacket 객체 생성 예시
/// var packet = new MqttDataPacket("sensor/temp", "25.5");
///
/// // 데이터 접근 예시
/// Debug.Log($"수신 시간: {packet.Timestamp}");
/// Debug.Log($"토픽: {packet.Topic}");
/// Debug.Log($"내용: {packet.Payload}");
///
/// // MqttDataManager가 이 패킷을 리스너에게 전달한 후,
/// // IsPropagated를 true로 설정하여 중복 전송을 방지합니다.
/// packet.IsPropagated = true;
/// Debug.Log($"처리 완료 여부: {packet.IsPropagated}");
/// </code>
/// </example>
public class MqttDataPacket
{
public bool IsInPool { get; set; } = false;
/// <summary>
/// 데이터가 수신된 시간 (UTC 기준)입니다.
/// 전 세계 어디서든 동일한 시간 기록을 보장하기 위해 협정 세계시(UTC)를 사용합니다.
/// 'init' 키워드는 객체가 처음 생성될 때만 값을 할당할 수 있도록 하여, 데이터의 불변성을 보장합니다.
/// </summary>
public DateTime Timestamp { get; private set; }
/// <summary>
/// 메시지가 발행된 MQTT 토픽입니다. (예: "home/livingroom/light")
/// </summary>
public string Topic { get; private set; }
/// <summary>
/// 메시지의 실제 내용(데이터)입니다. 보통 JSON 형식의 문자열이 담깁니다.
/// </summary>
public string Payload { get; private set; }
/// <summary>
/// 이 데이터가 리스너에게 전파(전달)되었는지 여부를 나타내는 플래그입니다.
/// MqttDataManager가 이 값을 사용하여 동일한 데이터를 중복으로 전달하는 것을 방지합니다.
/// </summary>
public bool IsPropagated { get; set; } = false;
public MqttDataPacket()
{
// 기본 생성자 - 빈 객체 생성 시 사용
this.Timestamp = DateTime.UtcNow;
this.Topic = string.Empty;
this.Payload = string.Empty;
this.IsPropagated = false;
}
/// <summary>
/// DataObject 객체로부터 MqttDataPacket 인스턴스를 생성합니다.
/// TIMESTAMP 필드를 파싱하여 타임스탬프로 사용합니다.
/// </summary>
/// <param name="topic">메시지가 수신된 토픽</param>
/// <param name="dataObject">JSON 데이터를 담고 있는 DataObject</param>
public MqttDataPacket(string topic, DataObject dataObject)
{
this.Topic = topic;
this.Payload = dataObject.ToJson();
this.IsPropagated = false;
// TIMESTAMP 필드에서 시간 값을 파싱합니다. 실패 시 현재 UTC 시간을 사용합니다.
var timestampStr = dataObject.GetString("TIMESTAMP");
if (!string.IsNullOrEmpty(timestampStr) && DateTime.TryParse(timestampStr, null, System.Globalization.DateTimeStyles.AdjustToUniversal, out var parsedTimestamp))
{
this.Timestamp = parsedTimestamp;
}
else
{
this.Timestamp = DateTime.UtcNow;
}
}
/// <summary>
/// 토픽과 JSON 페이로드를 포함하는 MQTT 통신용 데이터 패킷을 나타냅니다.
/// </summary>
/// <remarks> <see cref="MqttDataPacket"/> 클래스는 MQTT 메시지의 토픽과 페이로드를 캡슐화하는 데 사용됩니다.
/// 타임스탬프 및 전파 상태와 같은 메타데이터도 함께 캡슐화합니다. <paramref
/// name="topic"/> 및 <paramref name="json"/> 매개변수는 올바른 사용을 위해 유효한 문자열이어야 합니다.
///</remarks>
/// <param name="topic">MQTT 메시지와 연결된 토픽입니다. null이거나 비어 있을 수 없습니다.</param>
/// <param name="json">MQTT 메시지의 JSON 페이로드입니다. null이거나 비어 있을 수 없습니다.</param>
public MqttDataPacket(string topic, string json)
{
this.Topic = topic;
this.Payload = json;
this.IsPropagated = false;
this.Timestamp = DateTime.UtcNow;
}
/// <summary>
/// DataObject 객체로부터 MqttDataPacket 인스턴스를 설정합니다.
/// TIMESTAMP 필드를 파싱하여 타임스탬프로 사용합니다.
/// </summary>
/// <param name="topic">메시지가 수신된 토픽</param>
/// <param name="dataObject">JSON 데이터를 담고 있는 DataObject</param>
public MqttDataPacket FromDataObject(string topic, DataObject dataObject)
{
this.Topic = topic;
this.Payload = dataObject.ToJson();
this.IsPropagated = false;
// TIMESTAMP 필드에서 시간 값을 파싱합니다. 실패 시 현재 UTC 시간을 사용합니다.
var timestampStr = dataObject.GetString("TIMESTAMP");
if (!string.IsNullOrEmpty(timestampStr) && DateTime.TryParse(timestampStr, null, System.Globalization.DateTimeStyles.AdjustToUniversal, out var parsedTimestamp))
{
this.Timestamp = parsedTimestamp;
}
else
{
this.Timestamp = DateTime.UtcNow;
}
return this;
}
/// <summary>
/// DataObject 객체로부터 MqttDataPacket 인스턴스를 설정합니다.
/// TIMESTAMP 필드를 파싱하여 타임스탬프로 사용합니다.
/// </summary>
/// <param name="topic">메시지가 수신된 토픽</param>
/// <param name="json">MQTT 메시지의 JSON 페이로드입니다. null이거나 비어 있을 수 없습니다.</param>
public MqttDataPacket FromJson(string topic, string json)
{
this.Topic = topic;
this.Payload = json;
this.IsPropagated = false;
this.Timestamp = DateTime.UtcNow;
return this;
}
/// <summary>
/// 객체를 초기 상태로 재설정합니다.
/// </summary>
/// <remarks>이 메서드는 <see cref="Timestamp"/>를 현재 UTC 시간으로 설정하고, <see
/// cref="Topic"/> 및 <see cref="Payload"/> 문자열을 지우고, <see cref="IsPropagated"/>를 <see
/// langword="false"/>로 설정합니다.</remarks>
public void Reset()
{
// 객체를 초기 상태로 재설정합니다.
this.Timestamp = DateTime.UtcNow;
this.Topic = string.Empty;
this.Payload = string.Empty;
this.IsPropagated = false;
}
/// <summary>
/// 재사용을 위해 현재 인스턴스를 풀로 반환합니다.
/// </summary>
/// <remarks>이 메서드는 인스턴스가 더 이상 필요하지 않을 때 호출해야 하며, 이를 통해 인스턴스를 재사용하고
/// 새 인스턴스를 생성하는 오버헤드를 줄일 수 있습니다. 인스턴스를 풀로 반환하기 전에
/// 인스턴스가 유효한 상태인지 확인하십시오.</remarks>
public void ReturnToPool()
{
MqttDataPacketPool.Return(this);
}
/// <summary>
/// <see cref="MqttDataPacket"/> 객체 목록을 JSON 배열 문자열로 변환합니다.
/// </summary>
/// <param name="packets">변환할 <see cref="MqttDataPacket"/> 객체 목록입니다. null일 수 없습니다.</param>
/// <returns>제공된 패킷의 페이로드를 나타내는 JSON 배열 문자열입니다. 목록이 비어 있거나 null이면 "[]"를 반환합니다.
///</returns>
public static string ToJsonFromList(List<MqttDataPacket> packets)
{
if (packets == null || packets.Count == 0) return "[]";
bool isObject = packets.Count == 1 && packets[0].Payload.Trim().StartsWith("{");
if (isObject)
{
return packets[0].Payload;
}
var jsonArray = new System.Text.StringBuilder("[");
for (int i = 0; i < packets.Count; i++)
{
var packet = packets[i];
jsonArray.Append(packet.Payload);
if (i < packets.Count - 1)
{
jsonArray.Append(",");
}
}
jsonArray.Append("]");
return jsonArray.ToString();
}
}
}