From 7bfc362b1f27f9acc4750640c7bd440cbdcc320f Mon Sep 17 00:00:00 2001 From: SikongJueluo Date: Thu, 14 Aug 2025 15:21:15 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90=E4=B8=83=E6=AE=B5?= =?UTF-8?q?=E6=95=B0=E7=A0=81=E7=AE=A1=E5=90=8E=E7=AB=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server.test/ProgressTrackerTest.cs | 2 +- server/server.csproj | 2 +- server/src/Controllers/NetConfigController.cs | 4 +- server/src/Hubs/DigitalTubesHub.cs | 41 ++++++++++ server/src/Hubs/JtagHub.cs | 61 +++++++------- server/src/Hubs/ProgressHub.cs | 10 +-- server/src/MsgBus.cs | 2 +- server/src/Peripherals/HdmiInClient.cs | 1 - server/src/Peripherals/JtagClient.cs | 4 +- .../Peripherals/SevenDigitalTubesClient.cs | 79 +++++++++++++++++++ .../Services/HttpHdmiVideoStreamService.cs | 5 -- server/src/Services/ProgressTrackerService.cs | 34 ++++---- server/src/UdpClientPool.cs | 2 + 13 files changed, 189 insertions(+), 58 deletions(-) create mode 100644 server/src/Hubs/DigitalTubesHub.cs create mode 100644 server/src/Peripherals/SevenDigitalTubesClient.cs diff --git a/server.test/ProgressTrackerTest.cs b/server.test/ProgressTrackerTest.cs index 95b4bed..6ca5a92 100644 --- a/server.test/ProgressTrackerTest.cs +++ b/server.test/ProgressTrackerTest.cs @@ -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); diff --git a/server/server.csproj b/server/server.csproj index b17c9bc..6c501a1 100644 --- a/server/server.csproj +++ b/server/server.csproj @@ -31,7 +31,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/server/src/Controllers/NetConfigController.cs b/server/src/Controllers/NetConfigController.cs index 5f3679f..c7a485d 100644 --- a/server/src/Controllers/NetConfigController.cs +++ b/server/src/Controllers/NetConfigController.cs @@ -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; diff --git a/server/src/Hubs/DigitalTubesHub.cs b/server/src/Hubs/DigitalTubesHub.cs new file mode 100644 index 0000000..da0c961 --- /dev/null +++ b/server/src/Hubs/DigitalTubesHub.cs @@ -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 Join(string taskId); +} + +[Receiver] +public interface IDigitalTubesReceiver +{ + Task OnReceive(); +} + + +[Authorize] +[EnableCors("SignalR")] +public class DigitalTubesHub : Hub, IDigitalTubesHub +{ + private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); + + private readonly IHubContext _hubContext; + + public DigitalTubesHub(IHubContext hubContext) + { + _hubContext = hubContext; + } + + public async Task Join(string taskId) + { + return true; + } +} diff --git a/server/src/Hubs/JtagHub.cs b/server/src/Hubs/JtagHub.cs index 51cf78a..9acd195 100644 --- a/server/src/Hubs/JtagHub.cs +++ b/server/src/Hubs/JtagHub.cs @@ -69,23 +69,26 @@ public class JtagHub : Hub, IJtagHub public async Task 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 StartBoundaryScan(int freq = 100) @@ -99,7 +102,7 @@ public class JtagHub : Hub, 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, IJtagHub public async Task 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) diff --git a/server/src/Hubs/ProgressHub.cs b/server/src/Hubs/ProgressHub.cs index 4bfeaf4..068b919 100644 --- a/server/src/Hubs/ProgressHub.cs +++ b/server/src/Hubs/ProgressHub.cs @@ -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, IProgressHub public async Task Join(string taskId) { - return _tracker.BindTask(taskId, Context.ConnectionId); + return await Task.Run(() => _tracker.BindTask(taskId, Context.ConnectionId)); } } diff --git a/server/src/MsgBus.cs b/server/src/MsgBus.cs index 6b8af28..5a93abc 100644 --- a/server/src/MsgBus.cs +++ b/server/src/MsgBus.cs @@ -21,7 +21,7 @@ public static class MsgBus /// 通信总线初始化 /// /// - public async static void Init() + public static void Init() { if (!ArpClient.IsAdministrator()) { diff --git a/server/src/Peripherals/HdmiInClient.cs b/server/src/Peripherals/HdmiInClient.cs index 493da7e..409a181 100644 --- a/server/src/Peripherals/HdmiInClient.cs +++ b/server/src/Peripherals/HdmiInClient.cs @@ -1,6 +1,5 @@ using System.Net; using DotNext; -using Peripherals.PowerClient; using WebProtocol; namespace Peripherals.HdmiInClient; diff --git a/server/src/Peripherals/JtagClient.cs b/server/src/Peripherals/JtagClient.cs index 04b5dd4..11f43fd 100644 --- a/server/src/Peripherals/JtagClient.cs +++ b/server/src/Peripherals/JtagClient.cs @@ -709,8 +709,10 @@ public class Jtag /// 下载比特流到 JTAG 设备 /// /// 比特流数据 + /// 进度报告器 /// 指示下载是否成功的异步结果 - public async ValueTask> DownloadBitstream(byte[] bitstream, ProgressReporter? progress = null) + public async ValueTask> DownloadBitstream( + byte[] bitstream, ProgressReporter? progress = null) { // Clear Data MsgBus.UDPServer.ClearUDPData(this.address, 0); diff --git a/server/src/Peripherals/SevenDigitalTubesClient.cs b/server/src/Peripherals/SevenDigitalTubesClient.cs new file mode 100644 index 0000000..bc182ea --- /dev/null +++ b/server/src/Peripherals/SevenDigitalTubesClient.cs @@ -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; + + /// + /// 初始化七段数码管控制器 + /// + /// 七段数码管控制器IP地址 + /// 七段数码管控制器端口 + /// 任务ID + /// 超时时间(毫秒) + 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> 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> 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; + } + +} diff --git a/server/src/Services/HttpHdmiVideoStreamService.cs b/server/src/Services/HttpHdmiVideoStreamService.cs index 883137b..e5c3442 100644 --- a/server/src/Services/HttpHdmiVideoStreamService.cs +++ b/server/src/Services/HttpHdmiVideoStreamService.cs @@ -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") { diff --git a/server/src/Services/ProgressTrackerService.cs b/server/src/Services/ProgressTrackerService.cs index 4fecfca..420dfc8 100644 --- a/server/src/Services/ProgressTrackerService.cs +++ b/server/src/Services/ProgressTrackerService.cs @@ -39,12 +39,12 @@ public class ProgressReporter : ProgressInfo, IProgress 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? reporter = null, int initProgress = 0, int maxProgress = 100, int step = 1) { @@ -86,7 +86,7 @@ public class ProgressReporter : ProgressInfo, IProgress } } - 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.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) { diff --git a/server/src/UdpClientPool.cs b/server/src/UdpClientPool.cs index 665109a..16cf988 100644 --- a/server/src/UdpClientPool.cs +++ b/server/src/UdpClientPool.cs @@ -607,6 +607,7 @@ public class UDPClientPool /// 设备地址 /// 要写入的32位数据 /// 超时时间(毫秒) + /// 进度报告器 /// 写入结果,true表示写入成功 public static async ValueTask> WriteAddr( IPEndPoint endPoint, int taskID, UInt32 devAddr, @@ -654,6 +655,7 @@ public class UDPClientPool /// 设备地址 /// 要写入的字节数组 /// 超时时间(毫秒) + /// 进度报告器 /// 写入结果,true表示写入成功 public static async ValueTask> WriteAddr( IPEndPoint endPoint, int taskID, UInt32 devAddr,