FPGA_WebLab/server/src/Hubs/OscilloscopeHub.cs

404 lines
12 KiB
C#

using Microsoft.AspNetCore.Authorization;
using System.Security.Claims;
using Microsoft.AspNetCore.SignalR;
using Microsoft.AspNetCore.Cors;
using TypedSignalR.Client;
using DotNext;
using Tapper;
using System.Collections.Concurrent;
using Peripherals.OscilloscopeClient;
#pragma warning disable 1998
namespace server.Hubs;
[Hub]
public interface IOscilloscopeHub
{
Task<bool> Initialize(OscilloscopeFullConfig config);
Task<bool> StartCapture();
Task<bool> StopCapture();
Task<OscilloscopeDataResponse?> GetData();
Task<bool> SetTrigger(byte level);
Task<bool> SetRisingEdge(bool risingEdge);
Task<bool> SetSampling(ushort decimationRate);
Task<bool> SetFrequency(int frequency);
}
[Receiver]
public interface IOscilloscopeReceiver
{
Task OnDataReceived(OscilloscopeDataResponse data);
}
[TranspilationSource]
public class OscilloscopeDataResponse
{
public uint AdFrequency { get; set; }
public byte AdVpp { get; set; }
public byte AdMax { get; set; }
public byte AdMin { get; set; }
public string WaveformData { get; set; } = "";
}
[TranspilationSource]
public class OscilloscopeFullConfig
{
public bool CaptureEnabled { get; set; }
public byte TriggerLevel { get; set; }
public bool TriggerRisingEdge { get; set; }
public ushort HorizontalShift { get; set; }
public ushort DecimationRate { get; set; }
public int CaptureFrequency { get; set; }
// public bool AutoRefreshRAM { get; set; }
public OscilloscopeConfig ToOscilloscopeConfig()
{
return new OscilloscopeConfig
{
CaptureEnabled = CaptureEnabled,
TriggerLevel = TriggerLevel,
TriggerRisingEdge = TriggerRisingEdge,
HorizontalShift = HorizontalShift,
DecimationRate = DecimationRate,
};
}
}
class OscilloscopeScanTaskInfo
{
public Task? ScanTask { get; set; }
public OscilloscopeCtrl Client { get; set; }
public CancellationTokenSource CTS { get; set; } = new CancellationTokenSource();
public int Frequency { get; set; } = 100;
public OscilloscopeScanTaskInfo(OscilloscopeCtrl client)
{
Client = client;
}
}
[Authorize]
[EnableCors("SignalR")]
public class OscilloscopeHub : Hub<IOscilloscopeReceiver>, IOscilloscopeHub
{
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
private readonly IHubContext<OscilloscopeHub, IOscilloscopeReceiver> _hubContext;
private readonly Database.UserManager _userManager = new();
private static ConcurrentDictionary<string, OscilloscopeScanTaskInfo> _scanTasks = new();
public OscilloscopeHub(IHubContext<OscilloscopeHub, IOscilloscopeReceiver> hubContext)
{
_hubContext = hubContext;
}
private Optional<Database.Board> TryGetBoard()
{
var userName = Context.User?.FindFirstValue(ClaimTypes.Name);
if (string.IsNullOrEmpty(userName))
{
logger.Error("User name is null or empty");
return null;
}
var boardRet = _userManager.GetBoardByUserName(userName);
if (!boardRet.IsSuccessful || !boardRet.Value.HasValue)
{
logger.Error($"Board not found");
return null;
}
return boardRet.Value.Value;
}
private Optional<OscilloscopeCtrl> GetOscilloscope()
{
try
{
var board = TryGetBoard().OrThrow(() => new Exception("Board not found"));
var client = new OscilloscopeCtrl(board.IpAddr, board.Port);
return client;
}
catch (Exception ex)
{
logger.Error(ex, "Failed to get oscilloscope");
return null;
}
}
public async Task<bool> Initialize(OscilloscopeFullConfig config)
{
try
{
var client = GetOscilloscope().OrThrow(() => new Exception("Oscilloscope not found"));
var result = await client.Init(config.ToOscilloscopeConfig());
if (!result.IsSuccessful)
{
logger.Error(result.Error, "Initialize failed");
return false;
}
return result.Value;
}
catch (Exception ex)
{
logger.Error(ex, "Failed to initialize oscilloscope");
return false;
}
}
private Task ScanTask(OscilloscopeScanTaskInfo taskInfo, string clientId)
{
var token = taskInfo.CTS.Token;
return Task.Run(async () =>
{
while (!token.IsCancellationRequested)
{
var data = await GetCaptureData(taskInfo.Client);
if (data == null)
{
logger.Error("GetData failed");
continue;
}
await _hubContext.Clients.Client(clientId).OnDataReceived(data);
await Task.Delay(1000 / taskInfo.Frequency, token);
}
}, token).ContinueWith(t =>
{
if (t.IsFaulted)
logger.Error(t.Exception, "ScanTask failed");
});
}
public async Task<bool> StartCapture()
{
try
{
var board = TryGetBoard().OrThrow(() => new Exception("Board not found"));
var key = board.ID.ToString();
var client = GetOscilloscope().OrThrow(() => new Exception("Oscilloscope not found"));
if (_scanTasks.TryGetValue(key, out var existing))
return true;
var result = await client.SetCaptureEnable(true);
if (!result.IsSuccessful)
{
logger.Error(result.Error, "StartCapture failed");
return false;
}
var scanTaskInfo = new OscilloscopeScanTaskInfo(client);
scanTaskInfo.ScanTask = ScanTask(scanTaskInfo, Context.ConnectionId);
return _scanTasks.TryAdd(key, scanTaskInfo);
}
catch (Exception ex)
{
logger.Error(ex, "Failed to start capture");
return false;
}
}
public async Task<bool> StopCapture()
{
try
{
var board = TryGetBoard().OrThrow(() => new Exception("Board not found"));
var client = GetOscilloscope().OrThrow(() => new Exception("Oscilloscope not found"));
var key = board.ID.ToString();
if (_scanTasks.TryRemove(key, out var taskInfo))
{
taskInfo.CTS.Cancel();
if (taskInfo.ScanTask != null) taskInfo.ScanTask.Wait();
var result = await client.SetCaptureEnable(false);
if (!result.IsSuccessful)
{
logger.Error(result.Error, "StopCapture failed");
return false;
}
return result.Value;
}
throw new Exception("Task not found");
}
catch (Exception ex)
{
logger.Error(ex, "Failed to stop capture");
return false;
}
}
private async Task<OscilloscopeDataResponse?> GetCaptureData(OscilloscopeCtrl oscilloscope)
{
try
{
var freqResult = await oscilloscope.GetADFrequency();
var vppResult = await oscilloscope.GetADVpp();
var maxResult = await oscilloscope.GetADMax();
var minResult = await oscilloscope.GetADMin();
var waveformResult = await oscilloscope.GetWaveformData();
if (!freqResult.IsSuccessful)
{
logger.Error($"获取AD采样频率失败: {freqResult.Error}");
throw new Exception($"获取AD采样频率失败: {freqResult.Error}");
}
if (!vppResult.IsSuccessful)
{
logger.Error($"获取AD采样幅度失败: {vppResult.Error}");
throw new Exception($"获取AD采样幅度失败: {vppResult.Error}");
}
if (!maxResult.IsSuccessful)
{
logger.Error($"获取AD采样最大值失败: {maxResult.Error}");
throw new Exception($"获取AD采样最大值失败: {maxResult.Error}");
}
if (!minResult.IsSuccessful)
{
logger.Error($"获取AD采样最小值失败: {minResult.Error}");
throw new Exception($"获取AD采样最小值失败: {minResult.Error}");
}
if (!waveformResult.IsSuccessful)
{
logger.Error($"获取波形数据失败: {waveformResult.Error}");
throw new Exception($"获取波形数据失败: {waveformResult.Error}");
}
var response = new OscilloscopeDataResponse
{
AdFrequency = freqResult.Value,
AdVpp = vppResult.Value,
AdMax = maxResult.Value,
AdMin = minResult.Value,
WaveformData = Convert.ToBase64String(waveformResult.Value)
};
return new OscilloscopeDataResponse
{
AdFrequency = freqResult.Value,
AdVpp = vppResult.Value,
AdMax = maxResult.Value,
AdMin = minResult.Value,
WaveformData = Convert.ToBase64String(waveformResult.Value)
};
}
catch (Exception ex)
{
logger.Error(ex, "获取示波器数据时发生异常");
return null;
}
}
public async Task<OscilloscopeDataResponse?> GetData()
{
try
{
var oscilloscope = GetOscilloscope().OrThrow(() => new Exception("Oscilloscope not found"));
var response = await GetCaptureData(oscilloscope);
return response;
}
catch (Exception ex)
{
logger.Error(ex, "获取示波器数据时发生异常");
return null;
}
}
public async Task<bool> SetTrigger(byte level)
{
try
{
var client = GetOscilloscope().OrThrow(() => new Exception("Oscilloscope not found"));
var ret = await client.SetTriggerLevel(level);
if (!ret.IsSuccessful)
{
logger.Error(ret.Error, "UpdateTrigger failed");
return false;
}
return ret.Value;
}
catch (Exception ex)
{
logger.Error(ex, "Failed to update trigger");
return false;
}
}
public async Task<bool> SetRisingEdge(bool risingEdge)
{
try
{
var client = GetOscilloscope().OrThrow(() => new Exception("Oscilloscope not found"));
var ret = await client.SetTriggerEdge(risingEdge);
if (!ret.IsSuccessful)
{
logger.Error(ret.Error, "Update Rising Edge failed");
return false;
}
return ret.Value;
}
catch (Exception ex)
{
logger.Error(ex, "SetRisingEdge failed");
return false;
}
}
public async Task<bool> SetSampling(ushort decimationRate)
{
try
{
var client = GetOscilloscope().OrThrow(() => new Exception("Oscilloscope not found"));
var result = await client.SetDecimationRate(decimationRate);
if (!result.IsSuccessful)
{
logger.Error(result.Error, "UpdateSampling failed");
return false;
}
return result.Value;
}
catch (Exception ex)
{
logger.Error(ex, "Failed to update sampling");
return false;
}
}
public async Task<bool> SetFrequency(int frequency)
{
try
{
if (frequency < 1 || frequency > 1000)
return false;
var board = TryGetBoard().OrThrow(() => new Exception("Board not found"));
var key = board.ID.ToString();
if (_scanTasks.TryGetValue(key, out var scanInfo))
{
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 false;
}
}
}