2025-02-10 02:08:03 +00:00
|
|
|
using System.Collections.Concurrent;
|
2024-11-02 07:02:30 +00:00
|
|
|
using System.Diagnostics;
|
2019-10-11 06:15:20 +00:00
|
|
|
using System.Net;
|
|
|
|
using System.Net.Sockets;
|
2024-11-03 08:45:48 +00:00
|
|
|
|
2025-04-02 03:44:23 +00:00
|
|
|
namespace ServiceLib.Services;
|
|
|
|
|
|
|
|
public class SpeedtestService
|
2019-10-11 06:15:20 +00:00
|
|
|
{
|
2025-04-02 03:44:23 +00:00
|
|
|
private static readonly string _tag = "SpeedtestService";
|
|
|
|
private Config? _config;
|
|
|
|
private Action<SpeedTestResult>? _updateFunc;
|
|
|
|
private static readonly ConcurrentBag<string> _lstExitLoop = new();
|
|
|
|
|
|
|
|
public SpeedtestService(Config config, Action<SpeedTestResult> updateFunc)
|
2019-10-11 06:15:20 +00:00
|
|
|
{
|
2025-04-02 03:44:23 +00:00
|
|
|
_config = config;
|
|
|
|
_updateFunc = updateFunc;
|
|
|
|
}
|
2024-12-05 07:21:16 +00:00
|
|
|
|
2025-04-02 03:44:23 +00:00
|
|
|
public void RunLoop(ESpeedActionType actionType, List<ProfileItem> selecteds)
|
|
|
|
{
|
|
|
|
Task.Run(async () =>
|
2019-10-11 06:15:20 +00:00
|
|
|
{
|
2025-04-02 03:44:23 +00:00
|
|
|
await RunAsync(actionType, selecteds);
|
|
|
|
await ProfileExHandler.Instance.SaveTo();
|
|
|
|
UpdateFunc("", ResUI.SpeedtestingCompleted);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public void ExitLoop()
|
|
|
|
{
|
|
|
|
if (_lstExitLoop.Count > 0)
|
|
|
|
{
|
|
|
|
UpdateFunc("", ResUI.SpeedtestingStop);
|
|
|
|
|
|
|
|
_lstExitLoop.Clear();
|
2025-02-10 02:08:03 +00:00
|
|
|
}
|
2025-04-02 03:44:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private async Task RunAsync(ESpeedActionType actionType, List<ProfileItem> selecteds)
|
|
|
|
{
|
|
|
|
var exitLoopKey = Utils.GetGuid(false);
|
|
|
|
_lstExitLoop.Add(exitLoopKey);
|
|
|
|
|
|
|
|
var lstSelected = GetClearItem(actionType, selecteds);
|
2019-10-11 06:15:20 +00:00
|
|
|
|
2025-04-02 03:44:23 +00:00
|
|
|
switch (actionType)
|
2025-02-10 02:08:03 +00:00
|
|
|
{
|
2025-04-02 03:44:23 +00:00
|
|
|
case ESpeedActionType.Tcping:
|
|
|
|
await RunTcpingAsync(lstSelected);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ESpeedActionType.Realping:
|
|
|
|
await RunRealPingBatchAsync(lstSelected, exitLoopKey);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ESpeedActionType.Speedtest:
|
|
|
|
await RunMixedTestAsync(lstSelected, 1, true, exitLoopKey);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ESpeedActionType.Mixedtest:
|
|
|
|
await RunMixedTestAsync(lstSelected, _config.SpeedTestItem.MixedConcurrencyCount, true, exitLoopKey);
|
|
|
|
break;
|
2025-02-08 08:43:40 +00:00
|
|
|
}
|
2025-04-02 03:44:23 +00:00
|
|
|
}
|
2025-02-08 08:43:40 +00:00
|
|
|
|
2025-04-02 03:44:23 +00:00
|
|
|
private List<ServerTestItem> GetClearItem(ESpeedActionType actionType, List<ProfileItem> selecteds)
|
|
|
|
{
|
|
|
|
var lstSelected = new List<ServerTestItem>();
|
|
|
|
foreach (var it in selecteds)
|
2025-02-10 02:08:03 +00:00
|
|
|
{
|
2025-04-02 03:44:23 +00:00
|
|
|
if (it.ConfigType == EConfigType.Custom)
|
2025-02-10 02:08:03 +00:00
|
|
|
{
|
2025-04-02 03:44:23 +00:00
|
|
|
continue;
|
|
|
|
}
|
2025-02-10 02:08:03 +00:00
|
|
|
|
2025-04-02 03:44:23 +00:00
|
|
|
if (it.Port <= 0)
|
|
|
|
{
|
|
|
|
continue;
|
2025-02-10 02:08:03 +00:00
|
|
|
}
|
2025-04-02 03:44:23 +00:00
|
|
|
|
|
|
|
lstSelected.Add(new ServerTestItem()
|
|
|
|
{
|
|
|
|
IndexId = it.IndexId,
|
|
|
|
Address = it.Address,
|
|
|
|
Port = it.Port,
|
|
|
|
ConfigType = it.ConfigType,
|
|
|
|
QueueNum = selecteds.IndexOf(it)
|
|
|
|
});
|
2025-02-10 02:08:03 +00:00
|
|
|
}
|
|
|
|
|
2025-04-02 03:44:23 +00:00
|
|
|
//clear test result
|
|
|
|
foreach (var it in lstSelected)
|
2025-02-08 08:43:40 +00:00
|
|
|
{
|
2025-02-13 11:46:51 +00:00
|
|
|
switch (actionType)
|
2025-02-08 08:43:40 +00:00
|
|
|
{
|
2025-02-13 11:46:51 +00:00
|
|
|
case ESpeedActionType.Tcping:
|
|
|
|
case ESpeedActionType.Realping:
|
2025-04-02 03:44:23 +00:00
|
|
|
UpdateFunc(it.IndexId, ResUI.Speedtesting, "");
|
|
|
|
ProfileExHandler.Instance.SetTestDelay(it.IndexId, 0);
|
2025-02-13 11:46:51 +00:00
|
|
|
break;
|
2025-02-08 08:43:40 +00:00
|
|
|
|
2025-02-13 11:46:51 +00:00
|
|
|
case ESpeedActionType.Speedtest:
|
2025-04-02 03:44:23 +00:00
|
|
|
UpdateFunc(it.IndexId, "", ResUI.SpeedtestingWait);
|
|
|
|
ProfileExHandler.Instance.SetTestSpeed(it.IndexId, 0);
|
2025-02-13 11:46:51 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case ESpeedActionType.Mixedtest:
|
2025-04-02 03:44:23 +00:00
|
|
|
UpdateFunc(it.IndexId, ResUI.Speedtesting, ResUI.SpeedtestingWait);
|
|
|
|
ProfileExHandler.Instance.SetTestDelay(it.IndexId, 0);
|
|
|
|
ProfileExHandler.Instance.SetTestSpeed(it.IndexId, 0);
|
2025-02-13 11:46:51 +00:00
|
|
|
break;
|
2025-02-08 08:43:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-04-02 03:44:23 +00:00
|
|
|
return lstSelected;
|
|
|
|
}
|
|
|
|
|
|
|
|
private async Task RunTcpingAsync(List<ServerTestItem> selecteds)
|
|
|
|
{
|
|
|
|
List<Task> tasks = [];
|
|
|
|
foreach (var it in selecteds)
|
2025-02-08 08:43:40 +00:00
|
|
|
{
|
2025-04-02 03:44:23 +00:00
|
|
|
if (it.ConfigType == EConfigType.Custom)
|
2022-01-28 13:05:41 +00:00
|
|
|
{
|
2025-04-02 03:44:23 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
tasks.Add(Task.Run(async () =>
|
|
|
|
{
|
|
|
|
try
|
2023-01-31 06:05:31 +00:00
|
|
|
{
|
2025-04-02 03:44:23 +00:00
|
|
|
var responseTime = await GetTcpingTime(it.Address, it.Port);
|
2025-02-08 08:43:40 +00:00
|
|
|
|
2025-04-02 03:44:23 +00:00
|
|
|
ProfileExHandler.Instance.SetTestDelay(it.IndexId, responseTime);
|
|
|
|
UpdateFunc(it.IndexId, responseTime.ToString());
|
2023-02-09 07:17:52 +00:00
|
|
|
}
|
2025-04-02 03:44:23 +00:00
|
|
|
catch (Exception ex)
|
2023-01-31 06:05:31 +00:00
|
|
|
{
|
2025-04-02 03:44:23 +00:00
|
|
|
Logging.SaveLog(_tag, ex);
|
2023-01-31 06:05:31 +00:00
|
|
|
}
|
2025-04-02 03:44:23 +00:00
|
|
|
}));
|
|
|
|
}
|
|
|
|
await Task.WhenAll(tasks);
|
|
|
|
}
|
2023-01-31 06:05:31 +00:00
|
|
|
|
2025-04-02 03:44:23 +00:00
|
|
|
private async Task RunRealPingBatchAsync(List<ServerTestItem> lstSelected, string exitLoopKey, int pageSize = 0)
|
|
|
|
{
|
|
|
|
if (pageSize <= 0)
|
|
|
|
{
|
|
|
|
pageSize = lstSelected.Count < Global.SpeedTestPageSize ? lstSelected.Count : Global.SpeedTestPageSize;
|
2024-12-05 07:21:16 +00:00
|
|
|
}
|
2025-04-02 03:44:23 +00:00
|
|
|
var lstTest = GetTestBatchItem(lstSelected, pageSize);
|
2024-12-05 07:21:16 +00:00
|
|
|
|
2025-04-02 03:44:23 +00:00
|
|
|
List<ServerTestItem> lstFailed = new();
|
|
|
|
foreach (var lst in lstTest)
|
2019-10-11 06:15:20 +00:00
|
|
|
{
|
2025-04-02 03:44:23 +00:00
|
|
|
var ret = await RunRealPingAsync(lst, exitLoopKey);
|
|
|
|
if (ret == false)
|
2019-10-11 06:15:20 +00:00
|
|
|
{
|
2025-04-02 03:44:23 +00:00
|
|
|
lstFailed.AddRange(lst);
|
2025-02-13 11:46:51 +00:00
|
|
|
}
|
2025-04-02 03:44:23 +00:00
|
|
|
await Task.Delay(100);
|
2025-02-13 11:46:51 +00:00
|
|
|
}
|
2019-10-11 06:15:20 +00:00
|
|
|
|
2025-04-02 03:44:23 +00:00
|
|
|
//Retest the failed part
|
|
|
|
var pageSizeNext = pageSize / 2;
|
|
|
|
if (lstFailed.Count > 0 && pageSizeNext > 0)
|
2025-02-13 11:46:51 +00:00
|
|
|
{
|
2025-04-02 03:44:23 +00:00
|
|
|
if (_lstExitLoop.Any(p => p == exitLoopKey) == false)
|
2025-02-13 11:46:51 +00:00
|
|
|
{
|
2025-04-02 03:44:23 +00:00
|
|
|
UpdateFunc("", ResUI.SpeedtestingSkip);
|
|
|
|
return;
|
2019-10-11 06:15:20 +00:00
|
|
|
}
|
2025-02-13 11:46:51 +00:00
|
|
|
|
2025-04-02 03:44:23 +00:00
|
|
|
UpdateFunc("", string.Format(ResUI.SpeedtestingTestFailedPart, lstFailed.Count));
|
|
|
|
|
|
|
|
if (pageSizeNext > _config.SpeedTestItem.MixedConcurrencyCount)
|
2019-10-11 06:15:20 +00:00
|
|
|
{
|
2025-04-02 03:44:23 +00:00
|
|
|
await RunRealPingBatchAsync(lstFailed, exitLoopKey, pageSizeNext);
|
2019-10-11 06:15:20 +00:00
|
|
|
}
|
2025-04-02 03:44:23 +00:00
|
|
|
else
|
2019-10-11 06:15:20 +00:00
|
|
|
{
|
2025-04-02 03:44:23 +00:00
|
|
|
await RunMixedTestAsync(lstSelected, _config.SpeedTestItem.MixedConcurrencyCount, false, exitLoopKey);
|
2024-02-02 02:39:07 +00:00
|
|
|
}
|
2019-10-11 06:15:20 +00:00
|
|
|
}
|
2025-04-02 03:44:23 +00:00
|
|
|
}
|
2019-10-11 06:15:20 +00:00
|
|
|
|
2025-04-02 03:44:23 +00:00
|
|
|
private async Task<bool> RunRealPingAsync(List<ServerTestItem> selecteds, string exitLoopKey)
|
|
|
|
{
|
|
|
|
var pid = -1;
|
|
|
|
try
|
2019-10-11 06:15:20 +00:00
|
|
|
{
|
2025-04-02 03:44:23 +00:00
|
|
|
pid = await CoreHandler.Instance.LoadCoreConfigSpeedtest(selecteds);
|
|
|
|
if (pid < 0)
|
2019-10-11 06:15:20 +00:00
|
|
|
{
|
2025-04-02 03:44:23 +00:00
|
|
|
return false;
|
2019-10-11 06:15:20 +00:00
|
|
|
}
|
2023-01-06 11:23:36 +00:00
|
|
|
|
2025-02-13 11:46:51 +00:00
|
|
|
var downloadHandle = new DownloadService();
|
2025-04-02 03:44:23 +00:00
|
|
|
|
2025-02-13 11:46:51 +00:00
|
|
|
List<Task> tasks = new();
|
2024-12-05 07:21:16 +00:00
|
|
|
foreach (var it in selecteds)
|
2019-10-11 06:15:20 +00:00
|
|
|
{
|
2025-04-02 03:44:23 +00:00
|
|
|
if (!it.AllowTest)
|
2024-02-25 10:28:59 +00:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2024-10-08 01:50:03 +00:00
|
|
|
if (it.ConfigType == EConfigType.Custom)
|
2020-04-23 08:20:30 +00:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2025-02-13 11:46:51 +00:00
|
|
|
tasks.Add(Task.Run(async () =>
|
2023-02-08 11:20:44 +00:00
|
|
|
{
|
2025-04-02 03:44:23 +00:00
|
|
|
await DoRealPing(downloadHandle, it);
|
2025-02-13 11:46:51 +00:00
|
|
|
}));
|
2023-02-08 11:20:44 +00:00
|
|
|
}
|
2025-03-06 06:08:29 +00:00
|
|
|
await Task.WhenAll(tasks);
|
2025-02-13 11:46:51 +00:00
|
|
|
}
|
2025-04-02 03:44:23 +00:00
|
|
|
catch (Exception ex)
|
2025-02-13 11:46:51 +00:00
|
|
|
{
|
2025-04-02 03:44:23 +00:00
|
|
|
Logging.SaveLog(_tag, ex);
|
2023-02-08 11:20:44 +00:00
|
|
|
}
|
2025-04-02 03:44:23 +00:00
|
|
|
finally
|
2023-02-08 11:20:44 +00:00
|
|
|
{
|
2025-04-02 03:44:23 +00:00
|
|
|
if (pid > 0)
|
2023-02-08 11:20:44 +00:00
|
|
|
{
|
2025-04-02 03:44:23 +00:00
|
|
|
await ProcUtils.ProcessKill(pid);
|
|
|
|
}
|
2023-01-01 11:42:01 +00:00
|
|
|
}
|
2025-04-02 03:44:23 +00:00
|
|
|
return true;
|
|
|
|
}
|
2020-03-15 10:22:11 +00:00
|
|
|
|
2025-04-02 03:44:23 +00:00
|
|
|
private async Task RunMixedTestAsync(List<ServerTestItem> selecteds, int concurrencyCount, bool blSpeedTest, string exitLoopKey)
|
|
|
|
{
|
|
|
|
using var concurrencySemaphore = new SemaphoreSlim(concurrencyCount);
|
|
|
|
var downloadHandle = new DownloadService();
|
|
|
|
List<Task> tasks = new();
|
|
|
|
foreach (var it in selecteds)
|
2019-10-11 06:15:20 +00:00
|
|
|
{
|
2025-04-02 03:44:23 +00:00
|
|
|
if (_lstExitLoop.Any(p => p == exitLoopKey) == false)
|
|
|
|
{
|
|
|
|
UpdateFunc(it.IndexId, "", ResUI.SpeedtestingSkip);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (it.ConfigType == EConfigType.Custom)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
await concurrencySemaphore.WaitAsync();
|
2019-10-11 06:15:20 +00:00
|
|
|
|
2025-04-02 03:44:23 +00:00
|
|
|
tasks.Add(Task.Run(async () =>
|
2019-10-11 06:15:20 +00:00
|
|
|
{
|
2025-04-02 03:44:23 +00:00
|
|
|
var pid = -1;
|
|
|
|
try
|
2020-02-20 23:03:38 +00:00
|
|
|
{
|
2025-04-02 03:44:23 +00:00
|
|
|
pid = await CoreHandler.Instance.LoadCoreConfigSpeedtest(it);
|
|
|
|
if (pid > 0)
|
|
|
|
{
|
|
|
|
await Task.Delay(500);
|
|
|
|
var delay = await DoRealPing(downloadHandle, it);
|
|
|
|
if (blSpeedTest)
|
|
|
|
{
|
|
|
|
if (delay > 0)
|
|
|
|
{
|
|
|
|
await DoSpeedTest(downloadHandle, it);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
UpdateFunc(it.IndexId, "", ResUI.SpeedtestingSkip);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
UpdateFunc(it.IndexId, "", ResUI.FailedToRunCore);
|
|
|
|
}
|
2020-02-20 23:03:38 +00:00
|
|
|
}
|
2025-04-02 03:44:23 +00:00
|
|
|
catch (Exception ex)
|
|
|
|
{
|
|
|
|
Logging.SaveLog(_tag, ex);
|
|
|
|
}
|
|
|
|
finally
|
2025-02-14 06:55:43 +00:00
|
|
|
{
|
2025-04-02 03:44:23 +00:00
|
|
|
if (pid > 0)
|
|
|
|
{
|
|
|
|
await ProcUtils.ProcessKill(pid);
|
|
|
|
}
|
|
|
|
concurrencySemaphore.Release();
|
2025-02-14 06:55:43 +00:00
|
|
|
}
|
2025-04-02 03:44:23 +00:00
|
|
|
}));
|
|
|
|
}
|
|
|
|
await Task.WhenAll(tasks);
|
|
|
|
}
|
2025-02-23 08:34:40 +00:00
|
|
|
|
2025-04-02 03:44:23 +00:00
|
|
|
private async Task<int> DoRealPing(DownloadService downloadHandle, ServerTestItem it)
|
|
|
|
{
|
|
|
|
var webProxy = new WebProxy($"socks5://{Global.Loopback}:{it.Port}");
|
|
|
|
var responseTime = await downloadHandle.GetRealPingTime(_config.SpeedTestItem.SpeedPingTestUrl, webProxy, 10);
|
|
|
|
|
|
|
|
ProfileExHandler.Instance.SetTestDelay(it.IndexId, responseTime);
|
|
|
|
UpdateFunc(it.IndexId, responseTime.ToString());
|
|
|
|
return responseTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
private async Task DoSpeedTest(DownloadService downloadHandle, ServerTestItem it)
|
|
|
|
{
|
|
|
|
UpdateFunc(it.IndexId, "", ResUI.Speedtesting);
|
|
|
|
|
|
|
|
var webProxy = new WebProxy($"socks5://{Global.Loopback}:{it.Port}");
|
|
|
|
var url = _config.SpeedTestItem.SpeedTestUrl;
|
|
|
|
var timeout = _config.SpeedTestItem.SpeedTestTimeout;
|
|
|
|
await downloadHandle.DownloadDataAsync(url, webProxy, timeout, (success, msg) =>
|
|
|
|
{
|
|
|
|
decimal.TryParse(msg, out var dec);
|
|
|
|
if (dec > 0)
|
2019-10-11 06:15:20 +00:00
|
|
|
{
|
2025-04-02 03:44:23 +00:00
|
|
|
ProfileExHandler.Instance.SetTestSpeed(it.IndexId, dec);
|
2019-10-11 06:15:20 +00:00
|
|
|
}
|
2025-04-02 03:44:23 +00:00
|
|
|
UpdateFunc(it.IndexId, "", msg);
|
|
|
|
});
|
|
|
|
}
|
2019-10-11 06:15:20 +00:00
|
|
|
|
2025-04-02 03:44:23 +00:00
|
|
|
private async Task<int> GetTcpingTime(string url, int port)
|
|
|
|
{
|
|
|
|
var responseTime = -1;
|
2025-02-13 11:46:51 +00:00
|
|
|
|
2025-04-02 03:44:23 +00:00
|
|
|
try
|
|
|
|
{
|
|
|
|
if (!IPAddress.TryParse(url, out var ipAddress))
|
2025-02-13 11:46:51 +00:00
|
|
|
{
|
2025-04-02 03:44:23 +00:00
|
|
|
var ipHostInfo = await Dns.GetHostEntryAsync(url);
|
|
|
|
ipAddress = ipHostInfo.AddressList.First();
|
2025-02-13 11:46:51 +00:00
|
|
|
}
|
2025-04-02 03:44:23 +00:00
|
|
|
|
|
|
|
IPEndPoint endPoint = new(ipAddress, port);
|
|
|
|
using Socket clientSocket = new(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
|
|
|
|
|
|
|
|
var timer = Stopwatch.StartNew();
|
|
|
|
var result = clientSocket.BeginConnect(endPoint, null, null);
|
|
|
|
if (!result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(5)))
|
2025-02-13 11:46:51 +00:00
|
|
|
{
|
2025-04-02 03:44:23 +00:00
|
|
|
throw new TimeoutException("connect timeout (5s): " + url);
|
2025-02-13 11:46:51 +00:00
|
|
|
}
|
2025-04-02 03:44:23 +00:00
|
|
|
timer.Stop();
|
|
|
|
responseTime = (int)timer.Elapsed.TotalMilliseconds;
|
2025-02-13 11:46:51 +00:00
|
|
|
|
2025-04-02 03:44:23 +00:00
|
|
|
clientSocket.EndConnect(result);
|
2025-02-13 11:46:51 +00:00
|
|
|
}
|
2025-04-02 03:44:23 +00:00
|
|
|
catch (Exception ex)
|
|
|
|
{
|
|
|
|
Logging.SaveLog(_tag, ex);
|
|
|
|
}
|
|
|
|
return responseTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
private List<List<ServerTestItem>> GetTestBatchItem(List<ServerTestItem> lstSelected, int pageSize)
|
|
|
|
{
|
|
|
|
List<List<ServerTestItem>> lstTest = new();
|
2025-04-11 01:27:34 +00:00
|
|
|
var lst1 = lstSelected.Where(t => t.ConfigType is not (EConfigType.Hysteria2 or EConfigType.TUIC)).ToList();
|
|
|
|
var lst2 = lstSelected.Where(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC).ToList();
|
2025-02-13 11:46:51 +00:00
|
|
|
|
2025-04-02 03:44:23 +00:00
|
|
|
for (var num = 0; num < (int)Math.Ceiling(lst1.Count * 1.0 / pageSize); num++)
|
2023-01-01 11:42:01 +00:00
|
|
|
{
|
2025-04-02 03:44:23 +00:00
|
|
|
lstTest.Add(lst1.Skip(num * pageSize).Take(pageSize).ToList());
|
|
|
|
}
|
|
|
|
for (var num = 0; num < (int)Math.Ceiling(lst2.Count * 1.0 / pageSize); num++)
|
|
|
|
{
|
|
|
|
lstTest.Add(lst2.Skip(num * pageSize).Take(pageSize).ToList());
|
|
|
|
}
|
|
|
|
|
|
|
|
return lstTest;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void UpdateFunc(string indexId, string delay, string speed = "")
|
|
|
|
{
|
|
|
|
_updateFunc?.Invoke(new() { IndexId = indexId, Delay = delay, Speed = speed });
|
|
|
|
if (indexId.IsNotEmpty() && speed.IsNotEmpty())
|
|
|
|
{
|
|
|
|
ProfileExHandler.Instance.SetTestMessage(indexId, speed);
|
2020-04-16 07:37:17 +00:00
|
|
|
}
|
2019-10-11 06:15:20 +00:00
|
|
|
}
|
2025-01-30 09:10:05 +00:00
|
|
|
}
|