Compare commits
5 Commits
9904fecbee
...
dpp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
51b39cee07 | ||
|
|
0bd1ad8a0e | ||
|
|
f2c7c78b64 | ||
|
|
2f23ffe482 | ||
| cb229c2a30 |
@@ -95,6 +95,12 @@ try
|
|||||||
.AllowAnyMethod()
|
.AllowAnyMethod()
|
||||||
.AllowAnyHeader()
|
.AllowAnyHeader()
|
||||||
);
|
);
|
||||||
|
options.AddPolicy("SignalR", policy => policy
|
||||||
|
.WithOrigins("http://localhost:5173")
|
||||||
|
.AllowAnyHeader()
|
||||||
|
.AllowAnyMethod()
|
||||||
|
.AllowCredentials()
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Use SignalR
|
// Use SignalR
|
||||||
@@ -201,12 +207,15 @@ try
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
app.UseSwaggerUi();
|
app.UseSwaggerUi();
|
||||||
|
|
||||||
|
// SignalR
|
||||||
|
app.UseWebSockets();
|
||||||
app.UseSignalRHubSpecification();
|
app.UseSignalRHubSpecification();
|
||||||
app.UseSignalRHubDevelopmentUI();
|
app.UseSignalRHubDevelopmentUI();
|
||||||
|
|
||||||
// Router
|
// Router
|
||||||
app.MapControllers();
|
app.MapControllers();
|
||||||
app.MapHub<server.Hubs.JtagHub.JtagHub>("hubs/JtagHub").RequireCors("Users");
|
app.MapHub<server.Hubs.JtagHub.JtagHub>("hubs/JtagHub");
|
||||||
|
|
||||||
// Setup Program
|
// Setup Program
|
||||||
MsgBus.Init();
|
MsgBus.Init();
|
||||||
|
|||||||
@@ -25,12 +25,19 @@ public interface IJtagReceiver
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Authorize]
|
[Authorize]
|
||||||
[EnableCors("Users")]
|
[EnableCors("SignalR")]
|
||||||
public class JtagHub : Hub<IJtagReceiver>, IJtagHub
|
public class JtagHub : Hub<IJtagReceiver>, IJtagHub
|
||||||
{
|
{
|
||||||
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
||||||
private ConcurrentDictionary<string, int> FreqTable = new();
|
private static ConcurrentDictionary<string, int> FreqTable = new();
|
||||||
private ConcurrentDictionary<string, CancellationTokenSource> CancellationTokenSourceTable = new();
|
private static ConcurrentDictionary<string, CancellationTokenSource> CancellationTokenSourceTable = new();
|
||||||
|
|
||||||
|
private readonly IHubContext<JtagHub, IJtagReceiver> _hubContext;
|
||||||
|
|
||||||
|
public JtagHub(IHubContext<JtagHub, IJtagReceiver> hubContext)
|
||||||
|
{
|
||||||
|
_hubContext = hubContext;
|
||||||
|
}
|
||||||
|
|
||||||
private Optional<Peripherals.JtagClient.Jtag> GetJtagClient(string userName)
|
private Optional<Peripherals.JtagClient.Jtag> GetJtagClient(string userName)
|
||||||
{
|
{
|
||||||
@@ -92,33 +99,40 @@ public class JtagHub : Hub<IJtagReceiver>, IJtagHub
|
|||||||
}
|
}
|
||||||
|
|
||||||
await SetBoundaryScanFreq(freq);
|
await SetBoundaryScanFreq(freq);
|
||||||
var cts = CancellationTokenSource.CreateLinkedTokenSource();
|
var cts = new CancellationTokenSource();
|
||||||
CancellationTokenSourceTable.AddOrUpdate(userName, cts, (key, value) => cts);
|
CancellationTokenSourceTable.AddOrUpdate(userName, cts, (key, value) => cts);
|
||||||
|
|
||||||
_ = Task
|
_ = Task.Run(
|
||||||
.Run(
|
() => BoundaryScanLogicPorts(Context.ConnectionId, userName, cts.Token),
|
||||||
() => BoundaryScanLogicPorts(
|
|
||||||
Context.ConnectionId,
|
|
||||||
userName,
|
|
||||||
cts.Token),
|
|
||||||
cts.Token)
|
cts.Token)
|
||||||
.ContinueWith((task) =>
|
.ContinueWith((task) =>
|
||||||
{
|
{
|
||||||
if (!task.IsFaulted)
|
if (task.IsFaulted)
|
||||||
{
|
{
|
||||||
return;
|
// 遍历所有异常
|
||||||
|
foreach (var ex in task.Exception.InnerExceptions)
|
||||||
|
{
|
||||||
|
if (ex is OperationCanceledException)
|
||||||
|
{
|
||||||
|
logger.Info($"Boundary scan operation cancelled for user {userName}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.Error($"Boundary scan operation failed for user {userName}: {ex}");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else if (task.IsCanceled)
|
||||||
if (task.Exception.InnerException is OperationCanceledException)
|
|
||||||
{
|
{
|
||||||
logger.Info($"Boundary scan operation cancelled for user {userName}");
|
logger.Info($"Boundary scan operation cancelled for user {userName}");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
logger.Error(task.Exception);
|
logger.Info($"Boundary scan completed successfully for user {userName}");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
logger.Info($"Boundary scan started for user {userName}");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (Exception error)
|
catch (Exception error)
|
||||||
@@ -144,10 +158,12 @@ public class JtagHub : Hub<IJtagReceiver>, IJtagHub
|
|||||||
|
|
||||||
cts.Cancel();
|
cts.Cancel();
|
||||||
cts.Token.WaitHandle.WaitOne();
|
cts.Token.WaitHandle.WaitOne();
|
||||||
|
|
||||||
|
logger.Info($"Boundary scan stopped for user {userName}");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void BoundaryScanLogicPorts(string connectionID, string userName, CancellationToken cancellationToken)
|
private async Task BoundaryScanLogicPorts(string connectionID, string userName, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var jtagCtrl = GetJtagClient(userName).OrThrow(() => new InvalidOperationException("JTAG client not found"));
|
var jtagCtrl = GetJtagClient(userName).OrThrow(() => new InvalidOperationException("JTAG client not found"));
|
||||||
var cntFail = 0;
|
var cntFail = 0;
|
||||||
@@ -161,9 +177,10 @@ public class JtagHub : Hub<IJtagReceiver>, IJtagHub
|
|||||||
{
|
{
|
||||||
logger.Error($"User {userName} boundary scan failed for device {jtagCtrl.address}: {ret.Error}");
|
logger.Error($"User {userName} boundary scan failed for device {jtagCtrl.address}: {ret.Error}");
|
||||||
cntFail++;
|
cntFail++;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.Clients.Client(connectionID).OnReceiveBoundaryScanData(ret.Value);
|
await _hubContext.Clients.Client(connectionID).OnReceiveBoundaryScanData(ret.Value);
|
||||||
// logger.Info($"User {userName} successfully completed boundary scan for device {jtagCtrl.address}");
|
// logger.Info($"User {userName} successfully completed boundary scan for device {jtagCtrl.address}");
|
||||||
|
|
||||||
await Task.Delay(FreqTable.TryGetValue(userName, out var freq) ? 1000 / freq : 1000 / 100, cancellationToken);
|
await Task.Delay(FreqTable.TryGetValue(userName, out var freq) ? 1000 / freq : 1000 / 100, cancellationToken);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using DotNext;
|
using DotNext;
|
||||||
using Peripherals.PowerClient;
|
using Peripherals.PowerClient;
|
||||||
|
using WebProtocol;
|
||||||
|
|
||||||
namespace Peripherals.CameraClient;
|
namespace Peripherals.CameraClient;
|
||||||
|
|
||||||
@@ -19,7 +20,7 @@ class Camera
|
|||||||
{
|
{
|
||||||
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
readonly int timeout = 2000;
|
readonly int timeout = 500;
|
||||||
readonly int taskID;
|
readonly int taskID;
|
||||||
readonly int port;
|
readonly int port;
|
||||||
readonly string address;
|
readonly string address;
|
||||||
@@ -43,7 +44,7 @@ class Camera
|
|||||||
/// <param name="address">摄像头设备IP地址</param>
|
/// <param name="address">摄像头设备IP地址</param>
|
||||||
/// <param name="port">摄像头设备端口</param>
|
/// <param name="port">摄像头设备端口</param>
|
||||||
/// <param name="timeout">超时时间(毫秒)</param>
|
/// <param name="timeout">超时时间(毫秒)</param>
|
||||||
public Camera(string address, int port, int timeout = 2000)
|
public Camera(string address, int port, int timeout = 500)
|
||||||
{
|
{
|
||||||
if (timeout < 0)
|
if (timeout < 0)
|
||||||
throw new ArgumentException("Timeout couldn't be negative", nameof(timeout));
|
throw new ArgumentException("Timeout couldn't be negative", nameof(timeout));
|
||||||
@@ -225,6 +226,7 @@ class Camera
|
|||||||
this.taskID, // taskID
|
this.taskID, // taskID
|
||||||
FrameAddr,
|
FrameAddr,
|
||||||
(int)_currentFrameLength, // 使用当前分辨率的动态大小
|
(int)_currentFrameLength, // 使用当前分辨率的动态大小
|
||||||
|
BurstType.ExtendBurst,
|
||||||
this.timeout);
|
this.timeout);
|
||||||
|
|
||||||
if (!result.IsSuccessful)
|
if (!result.IsSuccessful)
|
||||||
@@ -462,6 +464,20 @@ class Camera
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 配置为960x540分辨率
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>配置结果</returns>
|
||||||
|
public async ValueTask<Result<bool>> ConfigureResolution960x540()
|
||||||
|
{
|
||||||
|
return await ConfigureResolution(
|
||||||
|
hStart: 0, vStart: 0,
|
||||||
|
dvpHo: 960, dvpVo: 540,
|
||||||
|
hts: 1700, vts: 1500,
|
||||||
|
hOffset: 16, vOffset: 4
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 配置为320x240分辨率
|
/// 配置为320x240分辨率
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -543,6 +559,9 @@ class Camera
|
|||||||
case "640x480":
|
case "640x480":
|
||||||
result = await ConfigureResolution640x480();
|
result = await ConfigureResolution640x480();
|
||||||
break;
|
break;
|
||||||
|
case "960x540":
|
||||||
|
result = await ConfigureResolution960x540();
|
||||||
|
break;
|
||||||
case "1280x720":
|
case "1280x720":
|
||||||
result = await ConfigureResolution1280x720();
|
result = await ConfigureResolution1280x720();
|
||||||
break;
|
break;
|
||||||
|
|||||||
118
server/src/Peripherals/HdmiInClient.cs
Normal file
118
server/src/Peripherals/HdmiInClient.cs
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
using System.Net;
|
||||||
|
using DotNext;
|
||||||
|
using Peripherals.PowerClient;
|
||||||
|
using WebProtocol;
|
||||||
|
|
||||||
|
namespace Peripherals.HdmiInClient;
|
||||||
|
|
||||||
|
static class HdmiInAddr
|
||||||
|
{
|
||||||
|
public const UInt32 BASE = 0xA000_0000;
|
||||||
|
public const UInt32 HdmiIn_CTRL = BASE + 0x0; //[0]: rstn, 0 is reset.
|
||||||
|
public const UInt32 HdmiIn_READFIFO = BASE + 0x1;
|
||||||
|
}
|
||||||
|
|
||||||
|
class HdmiIn
|
||||||
|
{
|
||||||
|
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
readonly int timeout = 500;
|
||||||
|
readonly int taskID;
|
||||||
|
readonly int port;
|
||||||
|
readonly string address;
|
||||||
|
private IPEndPoint ep;
|
||||||
|
|
||||||
|
// 动态分辨率参数
|
||||||
|
private UInt16 _currentWidth = 960;
|
||||||
|
private UInt16 _currentHeight = 540;
|
||||||
|
private UInt32 _currentFrameLength = 960 * 540 * 2 / 4; // RGB565格式,2字节/像素,按4字节对齐
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 初始化HDMI输入客户端
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="address">HDMI输入设备IP地址</param>
|
||||||
|
/// <param name="port">HDMI输入设备端口</param>
|
||||||
|
/// <param name="timeout">超时时间(毫秒)</param>
|
||||||
|
public HdmiIn(string address, int port, int timeout = 500)
|
||||||
|
{
|
||||||
|
if (timeout < 0)
|
||||||
|
throw new ArgumentException("Timeout couldn't be negative", nameof(timeout));
|
||||||
|
this.address = address;
|
||||||
|
this.port = port;
|
||||||
|
this.ep = new IPEndPoint(IPAddress.Parse(address), port);
|
||||||
|
this.timeout = timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask<Result<bool>> EnableTrans(bool isEnable)
|
||||||
|
{
|
||||||
|
var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, HdmiInAddr.HdmiIn_CTRL, (isEnable ? 0x00000001u : 0x00000000u));
|
||||||
|
if (!ret.IsSuccessful)
|
||||||
|
{
|
||||||
|
logger.Error($"Failed to write HdmiIn_CTRL to HdmiIn at {this.address}:{this.port}, error: {ret.Error}");
|
||||||
|
return new(ret.Error);
|
||||||
|
}
|
||||||
|
if (!ret.Value)
|
||||||
|
{
|
||||||
|
logger.Error($"HdmiIn_CTRL write returned false for HdmiIn at {this.address}:{this.port}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 读取一帧图像数据
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>包含图像数据的字节数组</returns>
|
||||||
|
public async ValueTask<Result<byte[]>> ReadFrame()
|
||||||
|
{
|
||||||
|
// 只在第一次或出错时清除UDP缓冲区,避免每帧都清除造成延迟
|
||||||
|
// MsgBus.UDPServer.ClearUDPData(this.address, this.taskID);
|
||||||
|
|
||||||
|
logger.Trace($"Reading frame from HdmiIn {this.address}");
|
||||||
|
|
||||||
|
// 使用UDPClientPool读取图像帧数据
|
||||||
|
var result = await UDPClientPool.ReadAddr4BytesAsync(
|
||||||
|
this.ep,
|
||||||
|
this.taskID, // taskID
|
||||||
|
HdmiInAddr.HdmiIn_READFIFO,
|
||||||
|
(int)_currentFrameLength, // 使用当前分辨率的动态大小
|
||||||
|
BurstType.FixedBurst,
|
||||||
|
this.timeout);
|
||||||
|
|
||||||
|
if (!result.IsSuccessful)
|
||||||
|
{
|
||||||
|
logger.Error($"Failed to read frame from HdmiIn {this.address}:{this.port}, error: {result.Error}");
|
||||||
|
// 读取失败时清除缓冲区,为下次读取做准备
|
||||||
|
try
|
||||||
|
{
|
||||||
|
MsgBus.UDPServer.ClearUDPData(this.address, this.taskID);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logger.Warn($"Failed to clear UDP data after read error: {ex.Message}");
|
||||||
|
}
|
||||||
|
return new(result.Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Trace($"Successfully read frame from HdmiIn {this.address}:{this.port}, data length: {result.Value.Length} bytes");
|
||||||
|
return result.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取当前分辨率
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>当前分辨率(宽度, 高度)</returns>
|
||||||
|
public (int Width, int Height) GetCurrentResolution()
|
||||||
|
{
|
||||||
|
return (_currentWidth, _currentHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取当前帧长度
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>当前帧长度</returns>
|
||||||
|
public UInt32 GetCurrentFrameLength()
|
||||||
|
{
|
||||||
|
return _currentFrameLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -612,13 +612,10 @@ public class Jtag
|
|||||||
if (ret.Value)
|
if (ret.Value)
|
||||||
{
|
{
|
||||||
var array = new UInt32[UInt32Num];
|
var array = new UInt32[UInt32Num];
|
||||||
for (int i = 0; i < UInt32Num; i++)
|
var retData = await UDPClientPool.ReadAddr4Bytes(ep, 0, JtagAddr.READ_DATA, (int)UInt32Num);
|
||||||
{
|
if (!retData.IsSuccessful)
|
||||||
var retData = await ReadFIFO(JtagAddr.READ_DATA);
|
return new(new Exception("Read FIFO failed when Load DR"));
|
||||||
if (!retData.IsSuccessful)
|
Buffer.BlockCopy(retData.Value, 0, array, 0, (int)UInt32Num * 4);
|
||||||
return new(new Exception("Read FIFO failed when Load DR"));
|
|
||||||
array[i] = retData.Value;
|
|
||||||
}
|
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -788,7 +785,7 @@ public class Jtag
|
|||||||
{
|
{
|
||||||
var paser = new BsdlParser.Parser();
|
var paser = new BsdlParser.Parser();
|
||||||
var portNum = paser.GetBoundaryRegsNum().Value;
|
var portNum = paser.GetBoundaryRegsNum().Value;
|
||||||
logger.Debug($"Get boundar scan registers number: {portNum}");
|
logger.Debug($"Get boundary scan registers number: {portNum}");
|
||||||
|
|
||||||
// Clear Data
|
// Clear Data
|
||||||
MsgBus.UDPServer.ClearUDPData(this.address, 0);
|
MsgBus.UDPServer.ClearUDPData(this.address, 0);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using System.Collections;
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using Common;
|
using Common;
|
||||||
using DotNext;
|
using DotNext;
|
||||||
|
using WebProtocol;
|
||||||
|
|
||||||
namespace Peripherals.LogicAnalyzerClient;
|
namespace Peripherals.LogicAnalyzerClient;
|
||||||
|
|
||||||
@@ -475,6 +476,7 @@ public class Analyzer
|
|||||||
this.taskID,
|
this.taskID,
|
||||||
AnalyzerAddr.STORE_OFFSET_ADDR,
|
AnalyzerAddr.STORE_OFFSET_ADDR,
|
||||||
capture_length,
|
capture_length,
|
||||||
|
BurstType.ExtendBurst, // 使用扩展突发读取
|
||||||
this.timeout
|
this.timeout
|
||||||
);
|
);
|
||||||
if (!ret.IsSuccessful)
|
if (!ret.IsSuccessful)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using Common;
|
using Common;
|
||||||
using DotNext;
|
using DotNext;
|
||||||
|
using WebProtocol;
|
||||||
|
|
||||||
namespace Peripherals.OscilloscopeClient;
|
namespace Peripherals.OscilloscopeClient;
|
||||||
|
|
||||||
@@ -319,6 +320,7 @@ class Oscilloscope
|
|||||||
this.taskID,
|
this.taskID,
|
||||||
OscilloscopeAddr.RD_DATA_ADDR,
|
OscilloscopeAddr.RD_DATA_ADDR,
|
||||||
(int)OscilloscopeAddr.RD_DATA_LENGTH / 32,
|
(int)OscilloscopeAddr.RD_DATA_LENGTH / 32,
|
||||||
|
BurstType.ExtendBurst, // 使用扩展突发读取
|
||||||
this.timeout
|
this.timeout
|
||||||
);
|
);
|
||||||
if (!ret.IsSuccessful)
|
if (!ret.IsSuccessful)
|
||||||
|
|||||||
@@ -1109,6 +1109,7 @@ public class HttpVideoStreamService : BackgroundService
|
|||||||
return new List<(int, int, string)>
|
return new List<(int, int, string)>
|
||||||
{
|
{
|
||||||
(640, 480, "640x480 (VGA)"),
|
(640, 480, "640x480 (VGA)"),
|
||||||
|
(960, 540, "960x540 (qHD)"),
|
||||||
(1280, 720, "1280x720 (HD)"),
|
(1280, 720, "1280x720 (HD)"),
|
||||||
(1280, 960, "1280x960 (SXGA)"),
|
(1280, 960, "1280x960 (SXGA)"),
|
||||||
(1920, 1080, "1920x1080 (Full HD)")
|
(1920, 1080, "1920x1080 (Full HD)")
|
||||||
|
|||||||
@@ -433,11 +433,12 @@ public class UDPClientPool
|
|||||||
/// <param name="endPoint">IP端点(IP地址与端口)</param>
|
/// <param name="endPoint">IP端点(IP地址与端口)</param>
|
||||||
/// <param name="taskID">任务ID</param>
|
/// <param name="taskID">任务ID</param>
|
||||||
/// <param name="devAddr">设备地址</param>
|
/// <param name="devAddr">设备地址</param>
|
||||||
|
/// <param name="burstType">突发类型</param>
|
||||||
/// <param name="dataLength">要读取的数据长度(4字节)</param>
|
/// <param name="dataLength">要读取的数据长度(4字节)</param>
|
||||||
/// <param name="timeout">超时时间(毫秒)</param>
|
/// <param name="timeout">超时时间(毫秒)</param>
|
||||||
/// <returns>读取结果,包含接收到的字节数组</returns>
|
/// <returns>读取结果,包含接收到的字节数组</returns>
|
||||||
public static async ValueTask<Result<byte[]>> ReadAddr4BytesAsync(
|
public static async ValueTask<Result<byte[]>> ReadAddr4BytesAsync(
|
||||||
IPEndPoint endPoint, int taskID, UInt32 devAddr, int dataLength, int timeout = 1000)
|
IPEndPoint endPoint, int taskID, UInt32 devAddr, int dataLength, BurstType burstType, int timeout = 1000)
|
||||||
{
|
{
|
||||||
var pkgList = new List<SendAddrPackage>();
|
var pkgList = new List<SendAddrPackage>();
|
||||||
var resultData = new List<byte>();
|
var resultData = new List<byte>();
|
||||||
@@ -460,7 +461,7 @@ public class UDPClientPool
|
|||||||
|
|
||||||
var opts = new SendAddrPackOptions
|
var opts = new SendAddrPackOptions
|
||||||
{
|
{
|
||||||
BurstType = BurstType.FixedBurst,
|
BurstType = burstType,
|
||||||
CommandID = Convert.ToByte(taskID),
|
CommandID = Convert.ToByte(taskID),
|
||||||
IsWrite = false,
|
IsWrite = false,
|
||||||
BurstLength = (byte)(currentSegmentSize - 1),
|
BurstLength = (byte)(currentSegmentSize - 1),
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
<fieldset class="fieldset w-full">
|
<fieldset class="fieldset w-full">
|
||||||
<legend class="fieldset-legend text-sm">示例比特流文件</legend>
|
<legend class="fieldset-legend text-sm">示例比特流文件</legend>
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<div v-for="bitstream in availableBitstreams" :key="bitstream.id" class="flex items-center justify-between p-2 bg-base-200 rounded">
|
<div v-for="bitstream in availableBitstreams" :key="bitstream.id" class="flex items-center justify-between p-2 border-2 border-base-300 rounded-lg">
|
||||||
<span class="text-sm">{{ bitstream.name }}</span>
|
<span class="text-sm">{{ bitstream.name }}</span>
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { ref, reactive, watchPostEffect, onMounted, onUnmounted } from "vue";
|
import { ref, reactive, watchPostEffect, onMounted, onUnmounted } from "vue";
|
||||||
import { defineStore } from "pinia";
|
import { defineStore } from "pinia";
|
||||||
import { useLocalStorage } from "@vueuse/core";
|
import { useLocalStorage } from "@vueuse/core";
|
||||||
import { isString, toNumber, type Dictionary } from "lodash";
|
import { isString, toNumber, isUndefined, type Dictionary } from "lodash";
|
||||||
import z from "zod";
|
import z from "zod";
|
||||||
import { isNumber } from "mathjs";
|
import { isNumber } from "mathjs";
|
||||||
import { Mutex, withTimeout } from "async-mutex";
|
import { Mutex, withTimeout } from "async-mutex";
|
||||||
@@ -9,9 +9,10 @@ import { useConstraintsStore } from "@/stores/constraints";
|
|||||||
import { useDialogStore } from "./dialog";
|
import { useDialogStore } from "./dialog";
|
||||||
import { toFileParameterOrUndefined } from "@/utils/Common";
|
import { toFileParameterOrUndefined } from "@/utils/Common";
|
||||||
import { AuthManager } from "@/utils/AuthManager";
|
import { AuthManager } from "@/utils/AuthManager";
|
||||||
import { HubConnectionBuilder } from "@microsoft/signalr";
|
import { HubConnection, HubConnectionBuilder } from "@microsoft/signalr";
|
||||||
import { getHubProxyFactory, getReceiverRegister } from "@/TypedSignalR.Client";
|
import { getHubProxyFactory, getReceiverRegister } from "@/TypedSignalR.Client";
|
||||||
import type { ResourceInfo } from "@/APIClient";
|
import type { ResourceInfo } from "@/APIClient";
|
||||||
|
import type { IJtagHub } from "@/TypedSignalR.Client/server.Hubs.JtagHub";
|
||||||
|
|
||||||
export const useEquipments = defineStore("equipments", () => {
|
export const useEquipments = defineStore("equipments", () => {
|
||||||
// Global Stores
|
// Global Stores
|
||||||
@@ -30,22 +31,32 @@ export const useEquipments = defineStore("equipments", () => {
|
|||||||
1000,
|
1000,
|
||||||
new Error("JtagClient Mutex Timeout!"),
|
new Error("JtagClient Mutex Timeout!"),
|
||||||
);
|
);
|
||||||
const jtagHubConnection = new HubConnectionBuilder()
|
const jtagHubConnection = ref<HubConnection>();
|
||||||
.withUrl("http://localhost:5000/hubs/JtagHub")
|
const jtagHubProxy = ref<IJtagHub>();
|
||||||
.withAutomaticReconnect()
|
|
||||||
.build();
|
onMounted(async () => {
|
||||||
const jtagHubProxy =
|
// 每次挂载都重新创建连接
|
||||||
getHubProxyFactory("IJtagHub").createHubProxy(jtagHubConnection);
|
jtagHubConnection.value =
|
||||||
const jtagHubSubscription = getReceiverRegister("IJtagReceiver").register(
|
AuthManager.createAuthenticatedJtagHubConnection();
|
||||||
jtagHubConnection,
|
jtagHubProxy.value = getHubProxyFactory("IJtagHub").createHubProxy(
|
||||||
{
|
jtagHubConnection.value,
|
||||||
|
);
|
||||||
|
|
||||||
|
getReceiverRegister("IJtagReceiver").register(jtagHubConnection.value, {
|
||||||
onReceiveBoundaryScanData: async (msg) => {
|
onReceiveBoundaryScanData: async (msg) => {
|
||||||
constrainsts.batchSetConstraintStates(msg);
|
constrainsts.batchSetConstraintStates(msg);
|
||||||
},
|
},
|
||||||
},
|
});
|
||||||
);
|
await jtagHubConnection.value.start();
|
||||||
onMounted(() => {
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
// 断开连接,清理资源
|
||||||
|
if (jtagHubConnection.value) {
|
||||||
|
jtagHubConnection.value.stop();
|
||||||
|
jtagHubConnection.value = undefined;
|
||||||
|
jtagHubProxy.value = undefined;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Matrix Key
|
// Matrix Key
|
||||||
@@ -89,13 +100,27 @@ export const useEquipments = defineStore("equipments", () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function jtagBoundaryScanSetOnOff(enable: boolean) {
|
async function jtagBoundaryScanSetOnOff(enable: boolean) {
|
||||||
jtagHubConnection.start();
|
if (isUndefined(jtagHubProxy.value)) {
|
||||||
enableJtagBoundaryScan.value = enable;
|
console.error("JtagHub Not Initialize...");
|
||||||
if (enable) {
|
return;
|
||||||
jtagHubProxy.startBoundaryScan(jtagBoundaryScanFreq.value);
|
|
||||||
} else {
|
|
||||||
jtagHubProxy.stopBoundaryScan();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (enable) {
|
||||||
|
const ret = await jtagHubProxy.value.startBoundaryScan(
|
||||||
|
jtagBoundaryScanFreq.value,
|
||||||
|
);
|
||||||
|
if (!ret) {
|
||||||
|
console.error("Failed to start boundary scan");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const ret = await jtagHubProxy.value.stopBoundaryScan();
|
||||||
|
if (!ret) {
|
||||||
|
console.error("Failed to stop boundary scan");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
enableJtagBoundaryScan.value = enable;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function jtagUploadBitstream(bitstream: File, examId?: string): Promise<number | null> {
|
async function jtagUploadBitstream(bitstream: File, examId?: string): Promise<number | null> {
|
||||||
|
|||||||
@@ -16,7 +16,10 @@ import {
|
|||||||
ExamClient,
|
ExamClient,
|
||||||
ResourceClient,
|
ResourceClient,
|
||||||
} from "@/APIClient";
|
} from "@/APIClient";
|
||||||
|
import router from "@/router";
|
||||||
|
import { HubConnectionBuilder } from "@microsoft/signalr";
|
||||||
import axios, { type AxiosInstance } from "axios";
|
import axios, { type AxiosInstance } from "axios";
|
||||||
|
import { isNull } from "lodash";
|
||||||
|
|
||||||
// 支持的客户端类型联合类型
|
// 支持的客户端类型联合类型
|
||||||
type SupportedClient =
|
type SupportedClient =
|
||||||
@@ -119,7 +122,7 @@ export class AuthManager {
|
|||||||
if (!token) return null;
|
if (!token) return null;
|
||||||
|
|
||||||
const instance = axios.create();
|
const instance = axios.create();
|
||||||
instance.interceptors.request.use(config => {
|
instance.interceptors.request.use((config) => {
|
||||||
config.headers = config.headers || {};
|
config.headers = config.headers || {};
|
||||||
(config.headers as any)["Authorization"] = `Bearer ${token}`;
|
(config.headers as any)["Authorization"] = `Bearer ${token}`;
|
||||||
return config;
|
return config;
|
||||||
@@ -185,11 +188,11 @@ export class AuthManager {
|
|||||||
public static createAuthenticatedNetConfigClient(): NetConfigClient {
|
public static createAuthenticatedNetConfigClient(): NetConfigClient {
|
||||||
return AuthManager.createAuthenticatedClient(NetConfigClient);
|
return AuthManager.createAuthenticatedClient(NetConfigClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static createAuthenticatedOscilloscopeApiClient(): OscilloscopeApiClient {
|
public static createAuthenticatedOscilloscopeApiClient(): OscilloscopeApiClient {
|
||||||
return AuthManager.createAuthenticatedClient(OscilloscopeApiClient);
|
return AuthManager.createAuthenticatedClient(OscilloscopeApiClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static createAuthenticatedDebuggerClient(): DebuggerClient {
|
public static createAuthenticatedDebuggerClient(): DebuggerClient {
|
||||||
return AuthManager.createAuthenticatedClient(DebuggerClient);
|
return AuthManager.createAuthenticatedClient(DebuggerClient);
|
||||||
}
|
}
|
||||||
@@ -201,6 +204,21 @@ export class AuthManager {
|
|||||||
public static createAuthenticatedResourceClient(): ResourceClient {
|
public static createAuthenticatedResourceClient(): ResourceClient {
|
||||||
return AuthManager.createAuthenticatedClient(ResourceClient);
|
return AuthManager.createAuthenticatedClient(ResourceClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static createAuthenticatedJtagHubConnection() {
|
||||||
|
const token = this.getToken();
|
||||||
|
if (isNull(token)) {
|
||||||
|
router.push("/login");
|
||||||
|
throw Error("Token Null!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new HubConnectionBuilder()
|
||||||
|
.withUrl("http://127.0.0.1:5000/hubs/JtagHub", {
|
||||||
|
accessTokenFactory: () => token,
|
||||||
|
})
|
||||||
|
.withAutomaticReconnect()
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
// 登录函数
|
// 登录函数
|
||||||
public static async login(
|
public static async login(
|
||||||
|
|||||||
@@ -53,10 +53,6 @@ export default defineConfig({
|
|||||||
target: "http://localhost:5000",
|
target: "http://localhost:5000",
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
},
|
},
|
||||||
"/hubs": {
|
|
||||||
target: "http://localhost:5000",
|
|
||||||
changeOrigin: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
port: 5173,
|
port: 5173,
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user