feat: 完成七段数码管后端
This commit is contained in:
parent
0e07a5996a
commit
7bfc362b1f
|
@ -6,7 +6,7 @@ using server.Services;
|
|||
public class ProgressTrackerTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task Test_ProgressReporter_Basic()
|
||||
public void Test_ProgressReporter_Basic()
|
||||
{
|
||||
int reportedValue = -1;
|
||||
var reporter = new ProgressReporter(async v => { reportedValue = v; await Task.CompletedTask; }, 0, 100, 10);
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
<PackageReference Include="NSwag.CodeGeneration.TypeScript" Version="14.4.0" />
|
||||
<PackageReference Include="OpenCvSharp4" Version="4.11.0.20250507" />
|
||||
<PackageReference Include="OpenCvSharp4.runtime.win" Version="4.11.0.20250507" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.10" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.11" />
|
||||
<PackageReference Include="System.Data.SQLite.Core" Version="1.0.119" />
|
||||
<PackageReference Include="Tapper.Analyzer" Version="1.13.1">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
|
|
|
@ -21,8 +21,8 @@ public class NetConfigController : ControllerBase
|
|||
private const int BOARD_PORT = 1234;
|
||||
|
||||
// 本机网络信息
|
||||
private readonly IPAddress _localIP;
|
||||
private readonly byte[] _localMAC;
|
||||
private readonly IPAddress _localIP = IPAddress.Any;
|
||||
private readonly byte[] _localMAC = new byte[6];
|
||||
private readonly string _localIPString;
|
||||
private readonly string _localMACString;
|
||||
private readonly string _localInterface;
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
using Microsoft.AspNetCore.Authorization;
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.AspNetCore.Cors;
|
||||
using TypedSignalR.Client;
|
||||
using Tapper;
|
||||
using server.Services;
|
||||
|
||||
namespace server.Hubs;
|
||||
|
||||
[Hub]
|
||||
public interface IDigitalTubesHub
|
||||
{
|
||||
Task<bool> Join(string taskId);
|
||||
}
|
||||
|
||||
[Receiver]
|
||||
public interface IDigitalTubesReceiver
|
||||
{
|
||||
Task OnReceive();
|
||||
}
|
||||
|
||||
|
||||
[Authorize]
|
||||
[EnableCors("SignalR")]
|
||||
public class DigitalTubesHub : Hub<IDigitalTubesReceiver>, IDigitalTubesHub
|
||||
{
|
||||
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
||||
|
||||
private readonly IHubContext<DigitalTubesHub, IDigitalTubesReceiver> _hubContext;
|
||||
|
||||
public DigitalTubesHub(IHubContext<DigitalTubesHub, IDigitalTubesReceiver> hubContext)
|
||||
{
|
||||
_hubContext = hubContext;
|
||||
}
|
||||
|
||||
public async Task<bool> Join(string taskId)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -69,23 +69,26 @@ public class JtagHub : Hub<IJtagReceiver>, IJtagHub
|
|||
|
||||
public async Task<bool> SetBoundaryScanFreq(int freq)
|
||||
{
|
||||
try
|
||||
return await Task.Run(() =>
|
||||
{
|
||||
var userName = Context.User?.FindFirstValue(ClaimTypes.Name);
|
||||
if (userName is null)
|
||||
try
|
||||
{
|
||||
logger.Error("Can't get user info");
|
||||
var userName = Context.User?.FindFirstValue(ClaimTypes.Name);
|
||||
if (userName is null)
|
||||
{
|
||||
logger.Error("Can't get user info");
|
||||
return false;
|
||||
}
|
||||
|
||||
FreqTable.AddOrUpdate(userName, freq, (key, value) => freq);
|
||||
return true;
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
logger.Error(error);
|
||||
return false;
|
||||
}
|
||||
|
||||
FreqTable.AddOrUpdate(userName, freq, (key, value) => freq);
|
||||
return true;
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
logger.Error(error);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<bool> StartBoundaryScan(int freq = 100)
|
||||
|
@ -99,7 +102,7 @@ public class JtagHub : Hub<IJtagReceiver>, IJtagHub
|
|||
return false;
|
||||
}
|
||||
|
||||
SetBoundaryScanFreq(freq);
|
||||
await SetBoundaryScanFreq(freq);
|
||||
var cts = new CancellationTokenSource();
|
||||
CancellationTokenSourceTable.AddOrUpdate(userName, cts, (key, value) => cts);
|
||||
|
||||
|
@ -145,23 +148,27 @@ public class JtagHub : Hub<IJtagReceiver>, IJtagHub
|
|||
|
||||
public async Task<bool> StopBoundaryScan()
|
||||
{
|
||||
var userName = Context.User?.FindFirstValue(ClaimTypes.Name);
|
||||
if (userName is null)
|
||||
return await Task.Run(() =>
|
||||
{
|
||||
logger.Error("No Such User");
|
||||
return false;
|
||||
}
|
||||
var userName = Context.User?.FindFirstValue(ClaimTypes.Name);
|
||||
if (userName is null)
|
||||
{
|
||||
logger.Error("No Such User");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CancellationTokenSourceTable.TryGetValue(userName, out var cts))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!CancellationTokenSourceTable.TryGetValue(userName, out var cts))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
cts.Cancel();
|
||||
cts.Token.WaitHandle.WaitOne();
|
||||
cts.Cancel();
|
||||
cts.Token.WaitHandle.WaitOne();
|
||||
|
||||
logger.Info($"Boundary scan stopped for user {userName}");
|
||||
return true;
|
||||
});
|
||||
|
||||
logger.Info($"Boundary scan stopped for user {userName}");
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task BoundaryScanLogicPorts(string connectionID, string userName, CancellationToken cancellationToken)
|
||||
|
|
|
@ -33,10 +33,10 @@ public enum ProgressStatus
|
|||
[TranspilationSource]
|
||||
public class ProgressInfo
|
||||
{
|
||||
public string TaskId { get; }
|
||||
public ProgressStatus Status { get; }
|
||||
public int ProgressPercent { get; }
|
||||
public string ErrorMessage { get; }
|
||||
public virtual string TaskId { get; } = string.Empty;
|
||||
public virtual ProgressStatus Status { get; }
|
||||
public virtual int ProgressPercent { get; } = 0;
|
||||
public virtual string ErrorMessage { get; } = string.Empty;
|
||||
};
|
||||
|
||||
[Authorize]
|
||||
|
@ -56,6 +56,6 @@ public class ProgressHub : Hub<IProgressReceiver>, IProgressHub
|
|||
|
||||
public async Task<bool> Join(string taskId)
|
||||
{
|
||||
return _tracker.BindTask(taskId, Context.ConnectionId);
|
||||
return await Task.Run(() => _tracker.BindTask(taskId, Context.ConnectionId));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ public static class MsgBus
|
|||
/// 通信总线初始化
|
||||
/// </summary>
|
||||
/// <returns>无</returns>
|
||||
public async static void Init()
|
||||
public static void Init()
|
||||
{
|
||||
if (!ArpClient.IsAdministrator())
|
||||
{
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
using System.Net;
|
||||
using DotNext;
|
||||
using Peripherals.PowerClient;
|
||||
using WebProtocol;
|
||||
|
||||
namespace Peripherals.HdmiInClient;
|
||||
|
|
|
@ -709,8 +709,10 @@ public class Jtag
|
|||
/// 下载比特流到 JTAG 设备
|
||||
/// </summary>
|
||||
/// <param name="bitstream">比特流数据</param>
|
||||
/// <param name="progress">进度报告器</param>
|
||||
/// <returns>指示下载是否成功的异步结果</returns>
|
||||
public async ValueTask<Result<bool>> DownloadBitstream(byte[] bitstream, ProgressReporter? progress = null)
|
||||
public async ValueTask<Result<bool>> DownloadBitstream(
|
||||
byte[] bitstream, ProgressReporter? progress = null)
|
||||
{
|
||||
// Clear Data
|
||||
MsgBus.UDPServer.ClearUDPData(this.address, 0);
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
using System.Net;
|
||||
using DotNext;
|
||||
using Common;
|
||||
|
||||
namespace Peripherals.SevenDigitalTubesClient;
|
||||
|
||||
static class SevenDigitalTubesAddr
|
||||
{
|
||||
public const UInt32 BASE = 0x0000_0000;
|
||||
}
|
||||
|
||||
public class SevenDigitalTubesCtrl
|
||||
{
|
||||
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
||||
|
||||
readonly int timeout = 500;
|
||||
readonly int taskID;
|
||||
readonly int port;
|
||||
readonly string address;
|
||||
private IPEndPoint ep;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化七段数码管控制器
|
||||
/// </summary>
|
||||
/// <param name="address">七段数码管控制器IP地址</param>
|
||||
/// <param name="port">七段数码管控制器端口</param>
|
||||
/// <param name="taskID">任务ID</param>
|
||||
/// <param name="timeout">超时时间(毫秒)</param>
|
||||
public SevenDigitalTubesCtrl(string address, int port, int taskID, int timeout = 500)
|
||||
{
|
||||
if (timeout < 0)
|
||||
throw new ArgumentException("Timeout couldn't be negative", nameof(timeout));
|
||||
this.address = address;
|
||||
this.port = port;
|
||||
this.ep = new IPEndPoint(IPAddress.Parse(address), port);
|
||||
this.taskID = taskID;
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
public async ValueTask<Result<byte>> ReadTube(int num)
|
||||
{
|
||||
if (num < 0 || num > 31)
|
||||
throw new ArgumentOutOfRangeException(nameof(num), "Tube number must be between 0 and 31");
|
||||
|
||||
var ret = await UDPClientPool.ReadAddr(
|
||||
this.ep, this.taskID, SevenDigitalTubesAddr.BASE + (UInt32)num, this.timeout);
|
||||
if (!ret.IsSuccessful)
|
||||
{
|
||||
logger.Error($"Read tubes failed: {ret.Error}");
|
||||
return new(ret.Error);
|
||||
}
|
||||
if (ret.Value.Options.Data == null || ret.Value.Options.Data.Length < 4)
|
||||
return new(new Exception("Data length is too short"));
|
||||
|
||||
var data = Number.BytesToUInt32(ret.Value.Options.Data, 0, 4, true).Value;
|
||||
|
||||
if ((data >> 8) != num)
|
||||
{
|
||||
logger.Error($"Read wrong tube number: {num} != {data >> 8}");
|
||||
return new(new Exception($"Read wrong tube number: {num} != {data >> 8}"));
|
||||
}
|
||||
|
||||
return (byte)(data & 0xFF);
|
||||
}
|
||||
|
||||
public async ValueTask<Result<byte[]>> ScanTubes()
|
||||
{
|
||||
var tubes = new byte[32];
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
var ret = await ReadTube(i);
|
||||
if (!ret.IsSuccessful)
|
||||
return new(ret.Error);
|
||||
tubes[i] = ret.Value;
|
||||
}
|
||||
return tubes;
|
||||
}
|
||||
|
||||
}
|
|
@ -192,11 +192,6 @@ public class HttpHdmiVideoStreamService : BackgroundService
|
|||
}
|
||||
|
||||
var hdmiInToken = _clientDict[boardId].CTS.Token;
|
||||
if (hdmiInToken == null)
|
||||
{
|
||||
await SendErrorAsync(context.Response, "HDMI input is not available");
|
||||
return;
|
||||
}
|
||||
|
||||
if (path == "/snapshot")
|
||||
{
|
||||
|
|
|
@ -39,12 +39,12 @@ public class ProgressReporter : ProgressInfo, IProgress<int>
|
|||
public ProgressReporter? Child { get; set; }
|
||||
|
||||
private ProgressStatus _status = ProgressStatus.Pending;
|
||||
private string _errorMessage;
|
||||
private string _errorMessage = string.Empty;
|
||||
|
||||
public string TaskId { get; set; } = Guid.NewGuid().ToString();
|
||||
public int ProgressPercent => _progress * 100 / MaxProgress;
|
||||
public ProgressStatus Status => _status;
|
||||
public string ErrorMessage => _errorMessage;
|
||||
public override string TaskId { get; } = Guid.NewGuid().ToString();
|
||||
public override int ProgressPercent => _progress * 100 / MaxProgress;
|
||||
public override ProgressStatus Status => _status;
|
||||
public override string ErrorMessage => _errorMessage;
|
||||
|
||||
public ProgressReporter(Func<int, Task>? reporter = null, int initProgress = 0, int maxProgress = 100, int step = 1)
|
||||
{
|
||||
|
@ -86,7 +86,7 @@ public class ProgressReporter : ProgressInfo, IProgress<int>
|
|||
}
|
||||
}
|
||||
|
||||
public async void Report(int value)
|
||||
public void Report(int value)
|
||||
{
|
||||
if (this._status == ProgressStatus.Pending)
|
||||
this._status = ProgressStatus.InProgress;
|
||||
|
@ -153,7 +153,7 @@ public class ProgressTrackerService : BackgroundService
|
|||
|
||||
private class TaskProgressInfo
|
||||
{
|
||||
public ProgressReporter Reporter { get; set; }
|
||||
public ProgressReporter? Reporter { get; set; }
|
||||
public string? ConnectionId { get; set; }
|
||||
public required CancellationToken CancellationToken { get; set; }
|
||||
public required CancellationTokenSource CancellationTokenSource { get; set; }
|
||||
|
@ -176,10 +176,15 @@ public class ProgressTrackerService : BackgroundService
|
|||
{
|
||||
var info = kvp.Value;
|
||||
// 超过 1 分钟且任务已完成/失败/取消
|
||||
if ((now - info.UpdatedAt).TotalMinutes > 1 &&
|
||||
(info.Reporter.Status == ProgressStatus.Completed ||
|
||||
info.Reporter.Status == ProgressStatus.Failed ||
|
||||
info.Reporter.Status == ProgressStatus.Canceled))
|
||||
if (
|
||||
(now - info.UpdatedAt).TotalMinutes > 1 &&
|
||||
info.Reporter != null &&
|
||||
(
|
||||
info.Reporter.Status == ProgressStatus.Completed ||
|
||||
info.Reporter.Status == ProgressStatus.Failed ||
|
||||
info.Reporter.Status == ProgressStatus.Canceled
|
||||
)
|
||||
)
|
||||
{
|
||||
_taskMap.TryRemove(kvp.Key, out _);
|
||||
logger.Info($"Cleaned up task {kvp.Key}");
|
||||
|
@ -219,7 +224,7 @@ public class ProgressTrackerService : BackgroundService
|
|||
cancellationTokenSource.Token.ThrowIfCancellationRequested();
|
||||
|
||||
// 通过 SignalR 推送进度
|
||||
if (progressInfo.ConnectionId != null)
|
||||
if (progressInfo.ConnectionId != null && progressInfo.Reporter != null)
|
||||
await _hubContext.Clients.Client(progressInfo.ConnectionId).OnReceiveProgress(progressInfo.Reporter);
|
||||
});
|
||||
|
||||
|
@ -243,7 +248,8 @@ public class ProgressTrackerService : BackgroundService
|
|||
{
|
||||
if (_taskMap.TryGetValue(taskId, out var info))
|
||||
{
|
||||
return info.Reporter.Status;
|
||||
if (info.Reporter != null)
|
||||
return info.Reporter.Status;
|
||||
}
|
||||
return Optional<ProgressStatus>.None;
|
||||
}
|
||||
|
@ -265,7 +271,7 @@ public class ProgressTrackerService : BackgroundService
|
|||
{
|
||||
try
|
||||
{
|
||||
if (_taskMap.TryGetValue(taskId, out var info) && info != null)
|
||||
if (_taskMap.TryGetValue(taskId, out var info) && info != null && info.Reporter != null)
|
||||
{
|
||||
lock (info)
|
||||
{
|
||||
|
|
|
@ -607,6 +607,7 @@ public class UDPClientPool
|
|||
/// <param name="devAddr">设备地址</param>
|
||||
/// <param name="data">要写入的32位数据</param>
|
||||
/// <param name="timeout">超时时间(毫秒)</param>
|
||||
/// <param name="progress">进度报告器</param>
|
||||
/// <returns>写入结果,true表示写入成功</returns>
|
||||
public static async ValueTask<Result<bool>> WriteAddr(
|
||||
IPEndPoint endPoint, int taskID, UInt32 devAddr,
|
||||
|
@ -654,6 +655,7 @@ public class UDPClientPool
|
|||
/// <param name="devAddr">设备地址</param>
|
||||
/// <param name="dataArray">要写入的字节数组</param>
|
||||
/// <param name="timeout">超时时间(毫秒)</param>
|
||||
/// <param name="progress">进度报告器</param>
|
||||
/// <returns>写入结果,true表示写入成功</returns>
|
||||
public static async ValueTask<Result<bool>> WriteAddr(
|
||||
IPEndPoint endPoint, int taskID, UInt32 devAddr,
|
||||
|
|
Loading…
Reference in New Issue