fix: 修复jtag边界扫描前后端的bug:无法开始停止,无法通过认证,后端崩溃

This commit is contained in:
SikongJueluo 2025-08-02 13:10:44 +08:00
parent e5f2be616c
commit cb229c2a30
No known key found for this signature in database
5 changed files with 110 additions and 45 deletions

View File

@ -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();

View File

@ -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);

View File

@ -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,8 +9,9 @@ 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 { IJtagHub } from "@/TypedSignalR.Client/server.Hubs.JtagHub";
export const useEquipments = defineStore("equipments", () => { export const useEquipments = defineStore("equipments", () => {
// Global Stores // Global Stores
@ -28,22 +29,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
@ -87,13 +98,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): Promise<boolean> { async function jtagUploadBitstream(bitstream: File): Promise<boolean> {

View File

@ -15,7 +15,10 @@ import {
DebuggerClient, DebuggerClient,
ExamClient, ExamClient,
} 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 =
@ -117,7 +120,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;
@ -183,11 +186,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);
} }
@ -196,6 +199,21 @@ export class AuthManager {
return AuthManager.createAuthenticatedClient(ExamClient); return AuthManager.createAuthenticatedClient(ExamClient);
} }
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(
username: string, username: string,

View File

@ -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,
}, },