fix: 尝试去修复后端无法停止扫描数码管的问题
This commit is contained in:
		@@ -8,6 +8,8 @@ using DotNext;
 | 
			
		||||
using Peripherals.SevenDigitalTubesClient;
 | 
			
		||||
using System.Collections.Concurrent;
 | 
			
		||||
 | 
			
		||||
#pragma warning disable 1998
 | 
			
		||||
 | 
			
		||||
namespace server.Hubs;
 | 
			
		||||
 | 
			
		||||
[Hub]
 | 
			
		||||
@@ -16,7 +18,7 @@ public interface IDigitalTubesHub
 | 
			
		||||
    Task<bool> StartScan();
 | 
			
		||||
    Task<bool> StopScan();
 | 
			
		||||
    Task<bool> SetFrequency(int frequency);
 | 
			
		||||
    Task<DigitalTubeTaskStatus> GetStatus();
 | 
			
		||||
    Task<DigitalTubeTaskStatus?> GetStatus();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[Receiver]
 | 
			
		||||
@@ -31,23 +33,27 @@ public class DigitalTubeTaskStatus
 | 
			
		||||
    public int Frequency { get; set; } = 100;
 | 
			
		||||
    public bool IsRunning { get; set; } = false;
 | 
			
		||||
 | 
			
		||||
    public DigitalTubeTaskStatus(DigitalTubeInfo info)
 | 
			
		||||
    public DigitalTubeTaskStatus(ScanTaskInfo info)
 | 
			
		||||
    {
 | 
			
		||||
        Frequency = info.Frequency;
 | 
			
		||||
        IsRunning = info.IsRunning;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public class DigitalTubeInfo
 | 
			
		||||
public class ScanTaskInfo
 | 
			
		||||
{
 | 
			
		||||
    public string BoardID { get; set; }
 | 
			
		||||
    public string ClientID { get; set; }
 | 
			
		||||
    public Task? ScanTask { 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 bool IsRunning { get; set; } = false;
 | 
			
		||||
 | 
			
		||||
    public DigitalTubeInfo(string clientID, SevenDigitalTubesCtrl client)
 | 
			
		||||
    public ScanTaskInfo(
 | 
			
		||||
        string boardID, string clientID, SevenDigitalTubesCtrl client)
 | 
			
		||||
    {
 | 
			
		||||
        BoardID = boardID;
 | 
			
		||||
        ClientID = clientID;
 | 
			
		||||
        TubeClient = client;
 | 
			
		||||
    }
 | 
			
		||||
@@ -58,11 +64,10 @@ public class DigitalTubeInfo
 | 
			
		||||
public class DigitalTubesHub : Hub<IDigitalTubesReceiver>, IDigitalTubesHub
 | 
			
		||||
{
 | 
			
		||||
    private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
 | 
			
		||||
 | 
			
		||||
    private readonly IHubContext<DigitalTubesHub, IDigitalTubesReceiver> _hubContext;
 | 
			
		||||
    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)
 | 
			
		||||
    {
 | 
			
		||||
@@ -95,17 +100,18 @@ public class DigitalTubesHub : Hub<IDigitalTubesReceiver>, IDigitalTubesHub
 | 
			
		||||
        return boardRet.Value.Value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Task ScanAllTubes(DigitalTubeInfo info)
 | 
			
		||||
    private Task ScanAllTubes(ScanTaskInfo scanInfo)
 | 
			
		||||
    {
 | 
			
		||||
        var token = scanInfo.CTS.Token;
 | 
			
		||||
        return Task.Run(async () =>
 | 
			
		||||
        {
 | 
			
		||||
            var cntError = 0;
 | 
			
		||||
            while (!info.CTS.IsCancellationRequested)
 | 
			
		||||
            while (!token.IsCancellationRequested)
 | 
			
		||||
            {
 | 
			
		||||
                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)
 | 
			
		||||
                {
 | 
			
		||||
                    logger.Error($"Failed to scan tubes: {dataRet.Error}");
 | 
			
		||||
@@ -113,126 +119,138 @@ public class DigitalTubesHub : Hub<IDigitalTubesReceiver>, IDigitalTubesHub
 | 
			
		||||
                    if (cntError > 3)
 | 
			
		||||
                    {
 | 
			
		||||
                        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;
 | 
			
		||||
                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
 | 
			
		||||
        {
 | 
			
		||||
            var board = TryGetBoard().OrThrow(() => new Exception("Board not found"));
 | 
			
		||||
            if (_infoDict.GetOrAdd(
 | 
			
		||||
                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);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            var key = (board.ID.ToString(), Context.ConnectionId);
 | 
			
		||||
 | 
			
		||||
            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)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error(ex, "Failed to start scan");
 | 
			
		||||
            return Task.FromResult(false);
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Task<bool> StopScan()
 | 
			
		||||
    public async Task<bool> StopScan()
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            var board = TryGetBoard().OrThrow(() => new Exception("Board not found"));
 | 
			
		||||
            if (_infoDict.GetOrAdd(
 | 
			
		||||
                board.ID.ToString(),
 | 
			
		||||
                (_) => new DigitalTubeInfo(
 | 
			
		||||
                    Context.ConnectionId,
 | 
			
		||||
                    new SevenDigitalTubesCtrl(board.IpAddr, board.Port, 2))
 | 
			
		||||
            ) is DigitalTubeInfo info)
 | 
			
		||||
            {
 | 
			
		||||
                info.IsRunning = false;
 | 
			
		||||
                info.CTS.Cancel();
 | 
			
		||||
            }
 | 
			
		||||
            var key = (board.ID.ToString(), Context.ConnectionId);
 | 
			
		||||
 | 
			
		||||
            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)
 | 
			
		||||
        {
 | 
			
		||||
            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
 | 
			
		||||
        {
 | 
			
		||||
            if (frequency < 1 || frequency > 1000) return Task.FromException<bool>(
 | 
			
		||||
                new ArgumentException("Frequency must be between 1 and 1000"));
 | 
			
		||||
            if (frequency < 1 || frequency > 1000)
 | 
			
		||||
                return false;
 | 
			
		||||
 | 
			
		||||
            var board = TryGetBoard().OrThrow(() => new Exception("Board not found"));
 | 
			
		||||
            if (_infoDict.GetOrAdd(
 | 
			
		||||
                board.ID.ToString(),
 | 
			
		||||
                (_) => new DigitalTubeInfo(
 | 
			
		||||
                    Context.ConnectionId,
 | 
			
		||||
                    new SevenDigitalTubesCtrl(board.IpAddr, board.Port, 2))
 | 
			
		||||
            ) is DigitalTubeInfo info)
 | 
			
		||||
            {
 | 
			
		||||
                info.Frequency = frequency;
 | 
			
		||||
            }
 | 
			
		||||
            var key = (board.ID.ToString(), Context.ConnectionId);
 | 
			
		||||
 | 
			
		||||
            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)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error(ex, "Failed to set frequency");
 | 
			
		||||
            return Task.FromResult(false);
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Task<DigitalTubeTaskStatus> GetStatus()
 | 
			
		||||
    public async Task<DigitalTubeTaskStatus?> GetStatus()
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            var board = TryGetBoard().OrThrow(() => new Exception("Board not found"));
 | 
			
		||||
            if (_infoDict.GetOrAdd(
 | 
			
		||||
                board.ID.ToString(),
 | 
			
		||||
                (_) => new DigitalTubeInfo(
 | 
			
		||||
                    Context.ConnectionId,
 | 
			
		||||
                    new SevenDigitalTubesCtrl(board.IpAddr, board.Port, 2))
 | 
			
		||||
            ) is DigitalTubeInfo info)
 | 
			
		||||
            {
 | 
			
		||||
                return Task.FromResult(new DigitalTubeTaskStatus(info));
 | 
			
		||||
            }
 | 
			
		||||
            var key = (board.ID.ToString(), Context.ConnectionId);
 | 
			
		||||
 | 
			
		||||
            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)
 | 
			
		||||
        {
 | 
			
		||||
            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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user