fix: 尝试去修复后端无法停止扫描数码管的问题

This commit is contained in:
SikongJueluo 2025-08-16 14:21:26 +08:00
parent 9bd3fb29e3
commit c974de593a
No known key found for this signature in database
2 changed files with 94 additions and 76 deletions

View File

@ -8,6 +8,8 @@ using DotNext;
using Peripherals.SevenDigitalTubesClient; using Peripherals.SevenDigitalTubesClient;
using System.Collections.Concurrent; using System.Collections.Concurrent;
#pragma warning disable 1998
namespace server.Hubs; namespace server.Hubs;
[Hub] [Hub]
@ -16,7 +18,7 @@ public interface IDigitalTubesHub
Task<bool> StartScan(); Task<bool> StartScan();
Task<bool> StopScan(); Task<bool> StopScan();
Task<bool> SetFrequency(int frequency); Task<bool> SetFrequency(int frequency);
Task<DigitalTubeTaskStatus> GetStatus(); Task<DigitalTubeTaskStatus?> GetStatus();
} }
[Receiver] [Receiver]
@ -31,23 +33,27 @@ public class DigitalTubeTaskStatus
public int Frequency { get; set; } = 100; public int Frequency { get; set; } = 100;
public bool IsRunning { get; set; } = false; public bool IsRunning { get; set; } = false;
public DigitalTubeTaskStatus(DigitalTubeInfo info) public DigitalTubeTaskStatus(ScanTaskInfo info)
{ {
Frequency = info.Frequency; Frequency = info.Frequency;
IsRunning = info.IsRunning; IsRunning = info.IsRunning;
} }
} }
public class DigitalTubeInfo public class ScanTaskInfo
{ {
public string BoardID { get; set; }
public string ClientID { get; set; } public string ClientID { get; set; }
public Task? ScanTask { get; set; }
public SevenDigitalTubesCtrl TubeClient { get; set; } public SevenDigitalTubesCtrl TubeClient { get; set; }
public CancellationTokenSource CTS { get; set; } = new CancellationTokenSource(); public CancellationTokenSource CTS { get; set; } = new();
public int Frequency { get; set; } = 100; public int Frequency { get; set; } = 100;
public bool IsRunning { get; set; } = false; public bool IsRunning { get; set; } = false;
public DigitalTubeInfo(string clientID, SevenDigitalTubesCtrl client) public ScanTaskInfo(
string boardID, string clientID, SevenDigitalTubesCtrl client)
{ {
BoardID = boardID;
ClientID = clientID; ClientID = clientID;
TubeClient = client; TubeClient = client;
} }
@ -58,11 +64,10 @@ public class DigitalTubeInfo
public class DigitalTubesHub : Hub<IDigitalTubesReceiver>, IDigitalTubesHub public class DigitalTubesHub : Hub<IDigitalTubesReceiver>, IDigitalTubesHub
{ {
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
private readonly IHubContext<DigitalTubesHub, IDigitalTubesReceiver> _hubContext; private readonly IHubContext<DigitalTubesHub, IDigitalTubesReceiver> _hubContext;
private readonly Database.UserManager _userManager = new(); private readonly Database.UserManager _userManager = new();
private ConcurrentDictionary<string, DigitalTubeInfo> _infoDict = new(); private ConcurrentDictionary<(string, string), ScanTaskInfo> _scanTasks = new();
public DigitalTubesHub(IHubContext<DigitalTubesHub, IDigitalTubesReceiver> hubContext) public DigitalTubesHub(IHubContext<DigitalTubesHub, IDigitalTubesReceiver> hubContext)
{ {
@ -95,17 +100,18 @@ public class DigitalTubesHub : Hub<IDigitalTubesReceiver>, IDigitalTubesHub
return boardRet.Value.Value; return boardRet.Value.Value;
} }
private Task ScanAllTubes(DigitalTubeInfo info) private Task ScanAllTubes(ScanTaskInfo scanInfo)
{ {
var token = scanInfo.CTS.Token;
return Task.Run(async () => return Task.Run(async () =>
{ {
var cntError = 0; var cntError = 0;
while (!info.CTS.IsCancellationRequested) while (!token.IsCancellationRequested)
{ {
var beginTime = DateTime.Now; var beginTime = DateTime.Now;
var waitTime = TimeSpan.FromMilliseconds(1000 / info.Frequency); var waitTime = TimeSpan.FromMilliseconds(1000 / scanInfo.Frequency);
var dataRet = await info.TubeClient.ScanAllTubes(); var dataRet = await scanInfo.TubeClient.ScanAllTubes();
if (!dataRet.IsSuccessful) if (!dataRet.IsSuccessful)
{ {
logger.Error($"Failed to scan tubes: {dataRet.Error}"); logger.Error($"Failed to scan tubes: {dataRet.Error}");
@ -113,126 +119,138 @@ public class DigitalTubesHub : Hub<IDigitalTubesReceiver>, IDigitalTubesHub
if (cntError > 3) if (cntError > 3)
{ {
logger.Error($"Too many errors, stopping scan"); logger.Error($"Too many errors, stopping scan");
info.IsRunning = false; break;
} }
} }
await _hubContext.Clients.Client(info.ClientID).OnReceive(dataRet.Value); await _hubContext.Clients.Client(scanInfo.ClientID).OnReceive(dataRet.Value);
var processTime = DateTime.Now - beginTime; var processTime = DateTime.Now - beginTime;
if (processTime < waitTime) if (processTime < waitTime)
{ {
await Task.Delay(waitTime - processTime); await Task.Delay(waitTime - processTime, token);
} }
} }
}, info.CTS.Token); scanInfo.IsRunning = false;
}, token)
.ContinueWith((task) =>
{
if (task.IsFaulted)
{
logger.Error(
$"Digital tubes scan operation failesj for board {task.Exception}");
}
else if (task.IsCanceled)
{
logger.Info(
$"Digital tubes scan operation cancelled for board {scanInfo.BoardID}");
}
else
{
logger.Info(
$"Digital tubes scan completed successfully for board {scanInfo.BoardID}");
}
});
} }
public Task<bool> StartScan() public async Task<bool> StartScan()
{ {
try try
{ {
var board = TryGetBoard().OrThrow(() => new Exception("Board not found")); var board = TryGetBoard().OrThrow(() => new Exception("Board not found"));
if (_infoDict.GetOrAdd( var key = (board.ID.ToString(), Context.ConnectionId);
board.ID.ToString(),
(_) => new DigitalTubeInfo(
Context.ConnectionId,
new SevenDigitalTubesCtrl(board.IpAddr, board.Port, 2))
) is DigitalTubeInfo info)
{
if (!info.IsRunning)
{
info.IsRunning = true;
if (info.CTS.IsCancellationRequested)
{
info.CTS.Dispose();
info.CTS = new CancellationTokenSource();
}
_ = ScanAllTubes(info);
}
}
return Task.FromResult(true); if (_scanTasks.TryGetValue(key, out var existing) && existing.IsRunning)
return true;
var cts = new CancellationTokenSource();
var scanTaskInfo = new ScanTaskInfo(
board.ID.ToString(), Context.ConnectionId,
new SevenDigitalTubesCtrl(board.IpAddr, board.Port, 0)
);
scanTaskInfo.ScanTask = ScanAllTubes(scanTaskInfo);
_scanTasks[key] = scanTaskInfo;
return true;
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex, "Failed to start scan"); logger.Error(ex, "Failed to start scan");
return Task.FromResult(false); return false;
} }
} }
public Task<bool> StopScan() public async Task<bool> StopScan()
{ {
try try
{ {
var board = TryGetBoard().OrThrow(() => new Exception("Board not found")); var board = TryGetBoard().OrThrow(() => new Exception("Board not found"));
if (_infoDict.GetOrAdd( var key = (board.ID.ToString(), Context.ConnectionId);
board.ID.ToString(),
(_) => new DigitalTubeInfo(
Context.ConnectionId,
new SevenDigitalTubesCtrl(board.IpAddr, board.Port, 2))
) is DigitalTubeInfo info)
{
info.IsRunning = false;
info.CTS.Cancel();
}
return Task.FromResult(true); if (_scanTasks.TryRemove(key, out var scanInfo))
{
scanInfo.IsRunning = false;
scanInfo.CTS.Cancel();
if (scanInfo.ScanTask != null)
await scanInfo.ScanTask;
scanInfo.CTS.Dispose();
}
return true;
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex, "Failed to stop scan"); logger.Error(ex, "Failed to stop scan");
return Task.FromResult(false); return false;
} }
} }
public Task<bool> SetFrequency(int frequency) public async Task<bool> SetFrequency(int frequency)
{ {
try try
{ {
if (frequency < 1 || frequency > 1000) return Task.FromException<bool>( if (frequency < 1 || frequency > 1000)
new ArgumentException("Frequency must be between 1 and 1000")); return false;
var board = TryGetBoard().OrThrow(() => new Exception("Board not found")); var board = TryGetBoard().OrThrow(() => new Exception("Board not found"));
if (_infoDict.GetOrAdd( var key = (board.ID.ToString(), Context.ConnectionId);
board.ID.ToString(),
(_) => new DigitalTubeInfo(
Context.ConnectionId,
new SevenDigitalTubesCtrl(board.IpAddr, board.Port, 2))
) is DigitalTubeInfo info)
{
info.Frequency = frequency;
}
return Task.FromResult(true); if (_scanTasks.TryGetValue(key, out var scanInfo) && scanInfo.IsRunning)
{
scanInfo.Frequency = frequency;
return true;
}
else
{
logger.Warn($"SetFrequency called but no running scan for board {board.ID} and client {Context.ConnectionId}");
return false;
}
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex, "Failed to set frequency"); logger.Error(ex, "Failed to set frequency");
return Task.FromResult(false); return false;
} }
} }
public Task<DigitalTubeTaskStatus> GetStatus() public async Task<DigitalTubeTaskStatus?> GetStatus()
{ {
try try
{ {
var board = TryGetBoard().OrThrow(() => new Exception("Board not found")); var board = TryGetBoard().OrThrow(() => new Exception("Board not found"));
if (_infoDict.GetOrAdd( var key = (board.ID.ToString(), Context.ConnectionId);
board.ID.ToString(),
(_) => new DigitalTubeInfo(
Context.ConnectionId,
new SevenDigitalTubesCtrl(board.IpAddr, board.Port, 2))
) is DigitalTubeInfo info)
{
return Task.FromResult(new DigitalTubeTaskStatus(info));
}
return Task.FromException<DigitalTubeTaskStatus>(new ArgumentException("Wrong argument")); if (_scanTasks.TryGetValue(key, out var scanInfo))
{
return new DigitalTubeTaskStatus(scanInfo);
}
else
{
return null;
}
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex, "Failed to get status"); logger.Error(ex, "Failed to get status");
return Task.FromException<DigitalTubeTaskStatus>(new Exception("Failed to get status")); throw new Exception("Failed to get status", ex);
} }
} }

View File

@ -20,7 +20,7 @@ export type IDigitalTubesHub = {
*/ */
setFrequency(frequency: number): Promise<boolean>; setFrequency(frequency: number): Promise<boolean>;
/** /**
* @returns Transpiled from System.Threading.Tasks.Task<server.Hubs.DigitalTubeTaskStatus> * @returns Transpiled from System.Threading.Tasks.Task<server.Hubs.DigitalTubeTaskStatus?>
*/ */
getStatus(): Promise<DigitalTubeTaskStatus>; getStatus(): Promise<DigitalTubeTaskStatus>;
} }