553 lines
24 KiB
C#
553 lines
24 KiB
C#
#nullable enable
|
|
|
|
using Cysharp.Threading.Tasks;
|
|
using Newtonsoft.Json;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Threading;
|
|
using UnityEngine;
|
|
using UVC.Data.Core;
|
|
using UVC.Log;
|
|
using UVC.Network;
|
|
|
|
namespace UVC.Data.Http
|
|
{
|
|
/// <summary>
|
|
/// HTTP 요청 파이프라인을 관리하는 클래스입니다.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// 이 클래스는 HTTP 요청의 실행 및 반복 요청을 관리합니다.
|
|
/// 등록된 요청(HttpRequestConfig)을 키 값으로 관리하며,
|
|
/// 주기적 데이터 수집을 위한 반복 요청 기능을 제공합니다.
|
|
///
|
|
/// 주요 기능:
|
|
/// - 단일 및 반복 HTTP 요청 관리
|
|
/// - 요청 결과의 JSON 데이터를 IDataObject로 변환
|
|
/// - 안전한 요청 취소 및 자원 정리
|
|
/// - 테스트를 위한 목업 기능 지원
|
|
///
|
|
/// 모든 HTTP 요청은 백그라운드 스레드(스레드풀)에서 처리되어 메인 스레드 차단을 방지합니다.
|
|
/// 요청 결과 처리 시 핸들러(SuccessHandler, FailHandler)는 자동으로 메인 스레드에서 호출됩니다.
|
|
/// 이를 통해 UI 스레드 차단 없이 효율적인 네트워크 작업을 수행하면서도,
|
|
/// UI 업데이트는 안전하게 메인 스레드에서 처리할 수 있습니다.
|
|
///
|
|
/// 모든 반복 실행은 CancellationTokenSource를 통해 취소할 수 있으며,
|
|
/// 취소 후 현재 진행 중인 모든 요청이 안전하게 완료되는 것을 보장합니다.
|
|
/// </remarks>
|
|
/// <example>
|
|
/// <code>
|
|
/// // HttpDataFetcher 인스턴스 생성
|
|
/// var httpFetcher = new HttpDataFetcher();
|
|
///
|
|
/// // 데이터 매퍼 설정 (응답 데이터 변환용)
|
|
/// var dataMask = new DataMask();
|
|
/// dataMask["name"] = "이름";
|
|
/// dataMask["value"] = 0;
|
|
/// var dataMapper = new DataMapper(dataMask);
|
|
///
|
|
/// // 단일 요청 설정 및 등록
|
|
/// var singleRequest = new HttpRequestConfig("https://api.example.com/data")
|
|
/// .SetDataMapper(dataMapper)
|
|
/// .SetSuccessHandler(data => {
|
|
/// // 데이터 처리 로직
|
|
/// ULog.Debug($"데이터 수신: {data?.ToString() ?? "null"}");
|
|
/// });
|
|
/// httpFetcher.AddChild("dataRequest", singleRequest);
|
|
///
|
|
/// // 반복 요청 설정 및 등록
|
|
/// var repeatingRequest = new HttpRequestConfig("https://api.example.com/status")
|
|
/// .SetDataMapper(dataMapper)
|
|
/// .SetSuccessHandler(data => {
|
|
/// // 상태 데이터 처리
|
|
/// ULog.Debug($"상태 업데이트: {data?.ToString() ?? "null"}");
|
|
/// })
|
|
/// .SetRepeat(true, 0, 5000); // 5초마다 무한 반복
|
|
/// httpFetcher.AddChild("statusMonitor", repeatingRequest);
|
|
///
|
|
/// // 응답 분할 요청 설정 (예: 응답이 {"AGV": [...], "ALARM": [...]} 형태)
|
|
/// // 각 키에 맞는 DataMapper와 Validator를 준비합니다.
|
|
/// var agvMask = new DataMask { ["VHL_NAME"] = "차량명" };
|
|
/// var alarmMask = new DataMask { ["MESSAGE"] = "메시지" };
|
|
/// var agvMapper = new DataMapper(agvMask);
|
|
/// var alarmMapper = new DataMapper(alarmMask);
|
|
///
|
|
/// var splitRequest = new HttpRequestConfig("https://api.example.com/baseinfo")
|
|
/// .SetSplitResponseByKey(true) // 응답을 키별로 분할
|
|
/// .AddSplitConfig("AGV", agvMapper) // "AGV" 키에 대한 매퍼 설정
|
|
/// .AddSplitConfig("ALARM", alarmMapper); // "ALARM" 키에 대한 매퍼 설정
|
|
/// httpFetcher.AddChild("baseInfo", splitRequest);
|
|
///
|
|
/// // 요청 실행
|
|
/// await httpFetcher.Excute("dataRequest"); // 단일 실행
|
|
/// await httpFetcher.Excute("statusMonitor"); // 반복 실행 시작
|
|
/// await httpFetcher.Excute("baseInfo"); // 분할 요청 실행
|
|
///
|
|
/// // 분할된 데이터 확인
|
|
/// var agvData = DataRepository.Instance.GetData("AGV");
|
|
/// var alarmData = DataRepository.Instance.GetData("ALARM");
|
|
/// ULog.Debug($"AGV Data from Repository: {agvData}");
|
|
/// ULog.Debug($"Alarm Data from Repository: {alarmData}");
|
|
///
|
|
/// // 나중에 반복 요청 중지
|
|
/// httpFetcher.StopRepeat("statusMonitor");
|
|
///
|
|
/// // 더 이상 필요없는 요청 제거
|
|
/// httpFetcher.RemoveChild("dataRequest");
|
|
/// </code>
|
|
/// </example>
|
|
public class HttpDataFetcher
|
|
{
|
|
/// <summary>
|
|
/// 테스트를 위한 목업 모드 활성화 여부를 설정하거나 가져옵니다.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// true로 설정하면 실제 HTTP 요청 대신 MockHttpRequester를 사용합니다.
|
|
/// 테스트 환경에서 외부 의존성 없이 HTTP 요청을 시뮬레이션할 때 유용합니다.
|
|
/// </remarks>
|
|
public bool UseMockup { get; set; } = false;
|
|
|
|
/// <summary>
|
|
/// 등록된 HTTP 파이프라인 정보를 저장하는 사전
|
|
/// </summary>
|
|
private Dictionary<string, HttpRequestConfig> infoList = new Dictionary<string, HttpRequestConfig>();
|
|
/// <summary>
|
|
/// 실행 중인 반복 작업의 취소 토큰을 관리하는 사전
|
|
/// </summary>
|
|
private Dictionary<string, CancellationTokenSource> repeatTokenSources = new Dictionary<string, CancellationTokenSource>();
|
|
|
|
/// <summary>
|
|
/// 진행 중인 요청의 상태를 추적하는 사전입니다.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// 키는 요청 식별자이고, 값은 현재 요청이 실행 중인지 여부를 나타냅니다.
|
|
/// 이 상태 추적은 StopRepeat 메서드가 요청의 완전한 종료를 보장하기 위해 사용됩니다.
|
|
/// </remarks>
|
|
private Dictionary<string, bool> requestInProgress = new Dictionary<string, bool>();
|
|
|
|
/// <summary>
|
|
/// 새로운 HTTP 요청 정보를 추가하거나 기존 정보를 업데이트합니다.
|
|
/// </summary>
|
|
/// <param name="key">요청을 식별하는 키</param>
|
|
/// <param name="info">HTTP 요청 정보</param>
|
|
/// <remarks>
|
|
/// 동일한 키가 이미 존재하는 경우 새로운 정보로 대체됩니다.
|
|
/// </remarks>
|
|
public void Add(string key, HttpRequestConfig info)
|
|
{
|
|
if (!infoList.ContainsKey(key))
|
|
{
|
|
infoList.Add(key, info);
|
|
}
|
|
else
|
|
{
|
|
infoList[key] = info; // Update existing ItemPrefab
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 지정한 키의 HTTP 요청 정보를 제거합니다.
|
|
/// </summary>
|
|
/// <param name="key">제거할 요청의 키</param>
|
|
/// <remarks>
|
|
/// 실행 중인 반복 작업이 있다면 함께 중지됩니다.
|
|
/// </remarks>
|
|
public async UniTask RemoveAsync(string key)
|
|
{
|
|
if (infoList.ContainsKey(key))
|
|
{
|
|
await StopRepeat(key);
|
|
infoList.Remove(key);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 지정한 키의 HTTP 요청을 실행합니다.
|
|
/// </summary>
|
|
/// <param name="key">실행할 요청의 키</param>
|
|
/// <param name="switchToMainThread">메인 스레드로 전환할지 여부</param>
|
|
/// <returns>비동기 작업</returns>
|
|
/// <remarks>
|
|
/// 요청 정보의 repeat 속성에 따라 단일 실행 또는 반복 실행을 시작합니다.
|
|
/// 이미 실행 중인 반복 작업이 있다면 먼저 중지하고 완료를 대기한 후 새로운 요청을 시작합니다.
|
|
/// 단일 실행의 경우 완료될 때까지 대기하지만, 반복 실행은 백그라운드에서 실행됩니다.
|
|
///
|
|
/// 모든 HTTP 요청 처리는 백그라운드 스레드에서 수행되며, 핸들러만 메인 스레드에서 호출됩니다.
|
|
/// </remarks>
|
|
/// <exception cref="KeyNotFoundException">지정된 키가 등록되어 있지 않은 경우</exception>
|
|
public async UniTask Excute(string key)
|
|
{
|
|
if (!infoList.ContainsKey(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);
|
|
|
|
try
|
|
{
|
|
if (!info.Repeat)
|
|
{
|
|
// WebGL 환경에서는 ThreadPool 사용 불가 -> 메인 스레드에서 비동기 실행
|
|
#if UNITY_WEBGL && !UNITY_EDITOR
|
|
await ExecuteSingle(key, info);
|
|
#else
|
|
// 단일 실행 로직 호출
|
|
await UniTask.RunOnThreadPool(() => ExecuteSingle(key, info));
|
|
#endif
|
|
Debug.Log($"HTTP request '{key}' executed successfully.");
|
|
}
|
|
else
|
|
{
|
|
// 반복 설정이 있는 경우에만 StartRepeat 호출
|
|
// Forget()을 호출하지 않고 StartRepeat가 스레드풀에서 계속 실행되도록 함
|
|
StartRepeat(key).Forget();
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw; // 예외 재발생
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 단일 HTTP 요청을 실행하고 결과를 처리합니다.
|
|
/// </summary>
|
|
/// <param name="key">요청을 식별하는 키</param>
|
|
/// <param name="info">HTTP 요청 정보</param>
|
|
/// <param name="cancellationToken">요청 취소를 위한 취소 토큰</param>
|
|
/// <returns>비동기 작업</returns>
|
|
/// <remarks>
|
|
/// 이 메서드는 백그라운드 스레드에서 HTTP 요청을 보내고, 응답 데이터를 파싱하여 IDataObject로 변환합니다.
|
|
/// JSON 객체 또는 배열 형식의 응답을 처리할 수 있으며, 취소 토큰을 통해 언제든지 작업을 취소할 수 있습니다.
|
|
/// 모든 핸들러 호출은 메인 스레드에서 이루어져 UI 업데이트를 안전하게 수행할 수 있습니다.
|
|
/// </remarks>
|
|
/// <exception cref="OperationCanceledException">작업이 취소된 경우 발생</exception>
|
|
/// <exception cref="JsonException">JSON 응답 파싱 중 오류가 발생한 경우</exception>
|
|
/// <exception cref="Exception">HTTP 요청 중 다른 예외가 발생한 경우</exception>
|
|
private async UniTask ExecuteSingle(string key, HttpRequestConfig info, CancellationToken cancellationToken = default)
|
|
{
|
|
int retryCount = 0;
|
|
Exception lastException = null;
|
|
|
|
while (retryCount <= info.MaxRetryCount)
|
|
{
|
|
// 취소 요청 확인
|
|
if (cancellationToken.IsCancellationRequested)
|
|
{
|
|
throw new OperationCanceledException("Operation cancelled", cancellationToken);
|
|
}
|
|
|
|
lock (requestInProgress)
|
|
{
|
|
requestInProgress[key] = true;
|
|
}
|
|
|
|
IDataObject? mappedObject = null;
|
|
|
|
try
|
|
{
|
|
string result = string.Empty;
|
|
if (UseMockup)
|
|
{
|
|
//result = await MockHttpRequester.Request<string>(info.Url, info.Method, info.Body, info.Headers);
|
|
}
|
|
else
|
|
{
|
|
result = await HttpRequester.Request<string>(info.Url, info.Method, info.Body, info.Headers);
|
|
}
|
|
Debug.Log($"HTTP request '{key}' completed");
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
HttpDataProcessor.ProcessResponse(key, info, result, cancellationToken);
|
|
Debug.Log($"HTTP request '{key}' processed successfully");
|
|
return;
|
|
}
|
|
catch (OperationCanceledException)
|
|
{
|
|
// 취소 예외는 그대로 전파
|
|
throw;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
lastException = ex;
|
|
retryCount++;
|
|
|
|
if (retryCount <= info.MaxRetryCount)
|
|
{
|
|
// 재시도 전에 취소 요청 확인
|
|
if (cancellationToken.IsCancellationRequested)
|
|
{
|
|
throw new OperationCanceledException("Operation cancelled", cancellationToken);
|
|
}
|
|
ULog.Warning($"Request failed for '{key}', retry {retryCount}/{info.MaxRetryCount} after {info.RetryDelay}ms: {ex.Message}", ex);
|
|
await UniTask.Delay(info.RetryDelay);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
//DataMapper가 생성한 임시 객체를 풀에 반환합니다.
|
|
if (mappedObject != null)
|
|
{
|
|
mappedObject.ReturnToPool();
|
|
}
|
|
|
|
lock (requestInProgress)
|
|
{
|
|
requestInProgress[key] = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 모든 재시도 후에도 실패
|
|
ULog.Error($"Request failed for '{key}' after {info.MaxRetryCount} retries: {lastException?.Message}", lastException);
|
|
throw lastException;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 반복 실행을 시작합니다.
|
|
/// </summary>
|
|
/// <param name="key">반복 실행할 요청의 키</param>
|
|
/// <returns>비동기 작업</returns>
|
|
/// <remarks>
|
|
/// 지정된 간격(repeatInterval)으로 HTTP 요청을 백그라운드 스레드에서 반복 실행합니다.
|
|
/// repeatCount가 0인 경우 무한 반복하며, 0보다 큰 경우 지정된 횟수만큼만 실행합니다.
|
|
/// 작업 실행 중 예외가 발생하면 로그를 기록하고 다음 실행을 시도합니다.
|
|
/// 취소 요청이 있거나 최대 실행 횟수에 도달하면 반복이 종료됩니다.
|
|
///
|
|
/// 이 메서드는 백그라운드 스레드에서 실행되며, 모든 핸들러 호출은 메인 스레드에서 이루어집니다.
|
|
/// </remarks>
|
|
/// <exception cref="KeyNotFoundException">지정된 키가 등록되어 있지 않은 경우</exception>
|
|
private async UniTask StartRepeat(string key)
|
|
{
|
|
if (!infoList.ContainsKey(key))
|
|
{
|
|
throw new KeyNotFoundException($"No HTTP request found with key '{key}'.");
|
|
}
|
|
|
|
HttpRequestConfig info = infoList[key];
|
|
if (!info.Repeat) return;
|
|
|
|
// 새 취소 토큰 생성
|
|
CancellationTokenSource cts = new CancellationTokenSource();
|
|
repeatTokenSources[key] = cts;
|
|
|
|
int executionCount = 0;
|
|
|
|
try
|
|
{
|
|
while (!cts.IsCancellationRequested)
|
|
{
|
|
try
|
|
{
|
|
#if UNITY_WEBGL && !UNITY_EDITOR
|
|
await ExecuteSingle(key, info, cts.Token);
|
|
#else
|
|
// 단일 실행 로직 호출
|
|
await UniTask.RunOnThreadPool(() => ExecuteSingle(key, info, cts.Token));
|
|
#endif
|
|
// 지정된 횟수만큼 반복한 경우 중지
|
|
if (info.RepeatCount > 0)
|
|
{
|
|
executionCount++;
|
|
if (executionCount >= info.RepeatCount)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
// 토큰이 취소되지 않은 경우에만 지연
|
|
if (!cts.IsCancellationRequested)
|
|
{
|
|
// 지정된 간격만큼 대기
|
|
await UniTask.Delay(info.RepeatInterval, cancellationToken: cts.Token);
|
|
}
|
|
}
|
|
catch (OperationCanceledException)
|
|
{
|
|
// 취소된 경우 루프 종료
|
|
break;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// 다른 예외 처리
|
|
ULog.Error($"Error in repeat execution for '{key}': {ex.Message}", ex);
|
|
if (cts.IsCancellationRequested)
|
|
{
|
|
break;
|
|
}
|
|
|
|
try
|
|
{
|
|
await UniTask.Delay(info.RepeatInterval, cancellationToken: cts.Token);
|
|
}
|
|
catch (OperationCanceledException)
|
|
{
|
|
// 취소된 경우 무시하고 루프 종료
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
lock (repeatTokenSources) // 스레드 안전성 확보
|
|
{
|
|
if (repeatTokenSources.TryGetValue(key, out var currentCts) && currentCts == cts)
|
|
{
|
|
repeatTokenSources.Remove(key);
|
|
}
|
|
}
|
|
cts.Dispose(); // 여기서 최종적으로 Dispose 호출
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 반복 실행 중인 요청을 중지합니다.
|
|
/// </summary>
|
|
/// <param name="key">중지할 요청의 키</param>
|
|
/// <returns>요청 중지 작업을 나타내는 비동기 작업</returns>
|
|
/// <remarks>
|
|
/// 해당 키로 실행 중인 반복 작업이 없는 경우 아무 작업도 수행하지 않습니다.
|
|
/// 요청이 중지되었더라도 현재 실행 중인 작업이 완전히 종료될 때까지 대기합니다.
|
|
/// 이를 통해 작업 중단 후 자원이 안전하게 정리되는 것을 보장합니다.
|
|
/// </remarks>
|
|
public async UniTask StopRepeat(string key)
|
|
{
|
|
CancellationTokenSource? cts = null;
|
|
lock (repeatTokenSources) // 스레드 안전성 확보
|
|
{
|
|
if (repeatTokenSources.TryGetValue(key, out cts) && !cts.IsCancellationRequested)
|
|
{
|
|
cts.Cancel();
|
|
repeatTokenSources.Remove(key);
|
|
}
|
|
}
|
|
|
|
// 진행 중인 요청이 완료될 때까지 대기
|
|
if (cts != null)
|
|
{
|
|
while (true)
|
|
{
|
|
lock (requestInProgress)
|
|
{
|
|
if (!requestInProgress.ContainsKey(key) || !requestInProgress[key])
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
await UniTask.Delay(10);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 모든 반복 실행 중인 요청을 중지합니다.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// 애플리케이션 종료 시 또는 모든 반복 작업을 일괄 중지해야 할 때 사용합니다.
|
|
/// 이 메서드는 비동기적으로 작동하지만 완료를 대기하지 않습니다.
|
|
/// 모든 작업이 완전히 종료될 때까지 기다려야 하는 경우, 각 키에 대해 개별적으로 StopRepeat를 호출하고 대기해야 합니다.
|
|
/// </remarks>
|
|
public async UniTask StopAllRepeats()
|
|
{
|
|
foreach (var key in new List<string>(repeatTokenSources.Keys))
|
|
{
|
|
await StopRepeat(key);
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// 현재 활성화된 요청 목록과 상태 정보를 반환합니다.
|
|
/// </summary>
|
|
/// <returns>키와 요청 상태 정보를 포함하는 딕셔너리</returns>
|
|
/// <remarks>
|
|
/// 반환되는 딕셔너리는 등록된 모든 HTTP 요청에 대한 상태 정보를 제공합니다.
|
|
/// 각 요청에 대해 활성 상태, 반복 설정, 반복 횟수, 반복 간격을 확인할 수 있습니다.
|
|
/// </remarks>
|
|
/// <example>
|
|
/// <code>
|
|
/// var httpFetcher = new HttpDataFetcher();
|
|
/// // 파이프라인에 요청 추가 후...
|
|
///
|
|
/// // 모든 활성 요청 확인
|
|
/// var activeRequests = httpFetcher.GetActiveRequests();
|
|
/// foreach (var request in activeRequests)
|
|
/// {
|
|
/// ULog.Debug($"요청 키: {request.Key}, 활성 상태: {request.Value.IsActive}, " +
|
|
/// $"반복 중: {request.Value.IsRepeating}, 반복 간격: {request.Value.RepeatInterval}ms");
|
|
/// }
|
|
/// </code>
|
|
/// </example>
|
|
public Dictionary<string, HttpRequestStatus> GetActiveRequests()
|
|
{
|
|
var result = new Dictionary<string, HttpRequestStatus>();
|
|
foreach (var key in infoList.Keys)
|
|
{
|
|
bool isRepeating = repeatTokenSources.ContainsKey(key);
|
|
result[key] = new HttpRequestStatus
|
|
{
|
|
IsActive = isRepeating,
|
|
IsRepeating = isRepeating,
|
|
RepeatCount = isRepeating ? infoList[key].RepeatCount : 0,
|
|
RepeatInterval = isRepeating ? infoList[key].RepeatInterval : 0
|
|
};
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 현재 인스턴스에서 사용되는 모든 리소스를 해제하고 진행 중인 모든 작업을 중지합니다.
|
|
/// </summary>
|
|
/// <remarks>이 메서드는 모든 반복 작업을 중단하고, 내부 상태를 지우고, 관련 리소스를 삭제합니다.
|
|
/// <see cref="Dispose"/>를 호출한 후에는 해당 인스턴스를 더 이상 사용할 수 없습니다.</remarks>
|
|
public void Dispose()
|
|
{
|
|
// 모든 반복 작업 중지
|
|
StopAllRepeats().Forget();
|
|
// 요청 상태 초기화
|
|
requestInProgress.Clear();
|
|
// 등록된 요청 정보 초기화
|
|
infoList.Clear();
|
|
// 취소 토큰 소스 정리
|
|
foreach (var cts in repeatTokenSources.Values)
|
|
{
|
|
cts.Dispose();
|
|
}
|
|
repeatTokenSources.Clear();
|
|
}
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// HTTP 요청의 현재 상태 정보를 나타내는 클래스입니다.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// 이 클래스는 HTTP 파이프라인에 등록된 요청의 활성 상태, 반복 설정,
|
|
/// 반복 횟수, 반복 간격에 관한 정보를 제공합니다.
|
|
/// </remarks>
|
|
public class HttpRequestStatus
|
|
{
|
|
/// <summary>
|
|
/// 요청이 현재 활성 상태인지 여부를 나타냅니다.
|
|
/// </summary>
|
|
public bool IsActive { get; set; }
|
|
/// <summary>
|
|
/// 요청이 반복 실행 중인지 여부를 나타냅니다.
|
|
/// </summary>
|
|
public bool IsRepeating { get; set; }
|
|
/// <summary>
|
|
/// 반복 설정된 횟수를 나타냅니다. 0은 무한 반복을 의미합니다.
|
|
/// </summary>
|
|
public int RepeatCount { get; set; }
|
|
/// <summary>
|
|
/// 반복 요청 간의 간격을 밀리초 단위로 나타냅니다.
|
|
/// </summary>
|
|
public int RepeatInterval { get; set; }
|
|
}
|
|
}
|