404 lines
12 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|