fix: 尝试去修复后端无法停止扫描数码管的问题
This commit is contained in:
		@@ -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);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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>;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user