feat: 使用SignalR来控制jtag边界扫描
This commit is contained in:
@@ -1,26 +1,25 @@
|
||||
import { ref, computed, reactive } from 'vue'
|
||||
import { defineStore } from 'pinia'
|
||||
import { isBoolean } from 'lodash';
|
||||
import { ref, computed, reactive } from "vue";
|
||||
import { defineStore } from "pinia";
|
||||
import { isBoolean } from "lodash";
|
||||
|
||||
// 约束电平状态类型
|
||||
export type ConstraintLevel = 'high' | 'low' | 'undefined';
|
||||
|
||||
export const useConstraintsStore = defineStore('constraints', () => {
|
||||
export type ConstraintLevel = "high" | "low" | "undefined";
|
||||
|
||||
export const useConstraintsStore = defineStore("constraints", () => {
|
||||
// 约束状态存储
|
||||
const constraintStates = reactive<Record<string, ConstraintLevel>>({});
|
||||
|
||||
// 约束颜色映射
|
||||
const constraintColors = {
|
||||
high: '#ff3333', // 高电平为红色
|
||||
low: '#3333ff', // 低电平为蓝色
|
||||
undefined: '#999999' // 未定义为灰色
|
||||
high: "#ff3333", // 高电平为红色
|
||||
low: "#3333ff", // 低电平为蓝色
|
||||
undefined: "#999999", // 未定义为灰色
|
||||
};
|
||||
|
||||
// 获取约束状态
|
||||
function getConstraintState(constraint: string): ConstraintLevel {
|
||||
if (!constraint) return 'undefined';
|
||||
return constraintStates[constraint] || 'undefined';
|
||||
if (!constraint) return "undefined";
|
||||
return constraintStates[constraint] || "undefined";
|
||||
}
|
||||
|
||||
// 设置约束状态
|
||||
@@ -30,7 +29,9 @@ export const useConstraintsStore = defineStore('constraints', () => {
|
||||
}
|
||||
|
||||
// 批量设置约束状态
|
||||
function batchSetConstraintStates(states: Record<string, ConstraintLevel> | Record<string, boolean>) {
|
||||
function batchSetConstraintStates(
|
||||
states: Record<string, ConstraintLevel> | Partial<Record<string, boolean>>,
|
||||
) {
|
||||
// 收集发生变化的约束
|
||||
const changedConstraints: [string, ConstraintLevel][] = [];
|
||||
|
||||
@@ -38,6 +39,8 @@ export const useConstraintsStore = defineStore('constraints', () => {
|
||||
Object.entries(states).forEach(([constraint, level]) => {
|
||||
if (isBoolean(level)) {
|
||||
level = level ? "high" : "low";
|
||||
} else {
|
||||
level = "low";
|
||||
}
|
||||
|
||||
if (constraintStates[constraint] !== level) {
|
||||
@@ -48,7 +51,7 @@ export const useConstraintsStore = defineStore('constraints', () => {
|
||||
|
||||
// 通知所有变化
|
||||
changedConstraints.forEach(([constraint, level]) => {
|
||||
stateChangeCallbacks.forEach(callback => callback(constraint, level));
|
||||
stateChangeCallbacks.forEach((callback) => callback(constraint, level));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -60,7 +63,7 @@ export const useConstraintsStore = defineStore('constraints', () => {
|
||||
|
||||
// 清除所有约束状态
|
||||
function clearAllConstraintStates() {
|
||||
Object.keys(constraintStates).forEach(key => {
|
||||
Object.keys(constraintStates).forEach((key) => {
|
||||
delete constraintStates[key];
|
||||
});
|
||||
}
|
||||
@@ -71,9 +74,14 @@ export const useConstraintsStore = defineStore('constraints', () => {
|
||||
}
|
||||
|
||||
// 注册约束状态变化回调
|
||||
const stateChangeCallbacks: ((constraint: string, level: ConstraintLevel) => void)[] = [];
|
||||
const stateChangeCallbacks: ((
|
||||
constraint: string,
|
||||
level: ConstraintLevel,
|
||||
) => void)[] = [];
|
||||
|
||||
function onConstraintStateChange(callback: (constraint: string, level: ConstraintLevel) => void) {
|
||||
function onConstraintStateChange(
|
||||
callback: (constraint: string, level: ConstraintLevel) => void,
|
||||
) {
|
||||
stateChangeCallbacks.push(callback);
|
||||
return () => {
|
||||
const index = stateChangeCallbacks.indexOf(callback);
|
||||
@@ -86,7 +94,7 @@ export const useConstraintsStore = defineStore('constraints', () => {
|
||||
// 触发约束变化
|
||||
function notifyConstraintChange(constraint: string, level: ConstraintLevel) {
|
||||
setConstraintState(constraint, level);
|
||||
stateChangeCallbacks.forEach(callback => callback(constraint, level));
|
||||
stateChangeCallbacks.forEach((callback) => callback(constraint, level));
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -98,6 +106,5 @@ export const useConstraintsStore = defineStore('constraints', () => {
|
||||
getAllConstraintStates,
|
||||
onConstraintStateChange,
|
||||
notifyConstraintChange,
|
||||
}
|
||||
})
|
||||
|
||||
};
|
||||
});
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import { ref, reactive, watchPostEffect } from "vue";
|
||||
import { ref, reactive, watchPostEffect, onMounted, onUnmounted } from "vue";
|
||||
import { defineStore } from "pinia";
|
||||
import { useLocalStorage } from "@vueuse/core";
|
||||
import { isString, toNumber } from "lodash";
|
||||
import { isString, toNumber, type Dictionary } from "lodash";
|
||||
import z from "zod";
|
||||
import { isNumber } from "mathjs";
|
||||
import { JtagClient, MatrixKeyClient, PowerClient } from "@/APIClient";
|
||||
import { Mutex, withTimeout } from "async-mutex";
|
||||
import { useConstraintsStore } from "@/stores/constraints";
|
||||
import { useDialogStore } from "./dialog";
|
||||
import { toFileParameterOrUndefined } from "@/utils/Common";
|
||||
import { AuthManager } from "@/utils/AuthManager";
|
||||
import { HubConnectionBuilder } from "@microsoft/signalr";
|
||||
import { getHubProxyFactory, getReceiverRegister } from "@/TypedSignalR.Client";
|
||||
|
||||
export const useEquipments = defineStore("equipments", () => {
|
||||
// Global Stores
|
||||
@@ -22,13 +23,28 @@ export const useEquipments = defineStore("equipments", () => {
|
||||
// Jtag
|
||||
const jtagBitstream = ref<File>();
|
||||
const jtagBoundaryScanFreq = ref(100);
|
||||
const jtagBoundaryScanErrorCount = ref(0); // 边界扫描连续错误计数
|
||||
const maxJtagBoundaryScanErrors = 5; // 最大允许连续错误次数
|
||||
const jtagClientMutex = withTimeout(
|
||||
new Mutex(),
|
||||
1000,
|
||||
new Error("JtagClient Mutex Timeout!"),
|
||||
);
|
||||
const jtagHubConnection = new HubConnectionBuilder()
|
||||
.withUrl("/hubs/JtagHub")
|
||||
.withAutomaticReconnect()
|
||||
.build();
|
||||
const jtagHubProxy =
|
||||
getHubProxyFactory("IJtagHub").createHubProxy(jtagHubConnection);
|
||||
const jtagHubSubscription = getReceiverRegister("IJtagReceiver").register(
|
||||
jtagHubConnection,
|
||||
{
|
||||
onReceiveBoundaryScanData: async (msg) => {
|
||||
constrainsts.batchSetConstraintStates(msg);
|
||||
},
|
||||
},
|
||||
);
|
||||
onMounted(() => {
|
||||
jtagHubConnection.start();
|
||||
});
|
||||
|
||||
// Matrix Key
|
||||
const matrixKeyStates = reactive(new Array<boolean>(16).fill(false));
|
||||
@@ -50,41 +66,6 @@ export const useEquipments = defineStore("equipments", () => {
|
||||
const enableMatrixKey = ref(false);
|
||||
const enablePower = ref(false);
|
||||
|
||||
// Watch
|
||||
watchPostEffect(async () => {
|
||||
if (true === enableJtagBoundaryScan.value) {
|
||||
// 重新启用时重置错误计数器
|
||||
jtagBoundaryScanErrorCount.value = 0;
|
||||
jtagBoundaryScan();
|
||||
}
|
||||
});
|
||||
|
||||
// Parse and Set
|
||||
function setAddr(address: string | undefined): boolean {
|
||||
if (isString(address) && z.string().ip("4").safeParse(address).success) {
|
||||
boardAddr.value = address;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function setPort(port: string | number | undefined): boolean {
|
||||
if (isString(port) && port.length != 0) {
|
||||
const portNumber = toNumber(port);
|
||||
if (z.number().nonnegative().max(65535).safeParse(portNumber).success) {
|
||||
boardPort.value = portNumber;
|
||||
return true;
|
||||
}
|
||||
} else if (isNumber(port)) {
|
||||
if (z.number().nonnegative().max(65535).safeParse(port).success) {
|
||||
boardPort.value = port;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function setMatrixKey(
|
||||
keyNum: number | string | undefined,
|
||||
keyValue: boolean,
|
||||
@@ -105,38 +86,12 @@ export const useEquipments = defineStore("equipments", () => {
|
||||
return false;
|
||||
}
|
||||
|
||||
async function jtagBoundaryScan() {
|
||||
const release = await jtagClientMutex.acquire();
|
||||
try {
|
||||
// 自动开启电源
|
||||
await powerSetOnOff(true);
|
||||
|
||||
const jtagClient = AuthManager.createAuthenticatedJtagClient();
|
||||
const portStates = await jtagClient.boundaryScanLogicalPorts(
|
||||
boardAddr.value,
|
||||
boardPort.value,
|
||||
);
|
||||
|
||||
constrainsts.batchSetConstraintStates(portStates);
|
||||
|
||||
// 扫描成功,重置错误计数器
|
||||
jtagBoundaryScanErrorCount.value = 0;
|
||||
} catch (error) {
|
||||
jtagBoundaryScanErrorCount.value++;
|
||||
|
||||
console.error(`边界扫描错误 (${jtagBoundaryScanErrorCount.value}/${maxJtagBoundaryScanErrors}):`, error);
|
||||
|
||||
// 如果错误次数超过最大允许次数,才停止扫描并显示错误
|
||||
if (jtagBoundaryScanErrorCount.value >= maxJtagBoundaryScanErrors) {
|
||||
dialog.error("边界扫描发生连续错误,已自动停止");
|
||||
enableJtagBoundaryScan.value = false;
|
||||
jtagBoundaryScanErrorCount.value = 0; // 重置错误计数器
|
||||
}
|
||||
} finally {
|
||||
release();
|
||||
|
||||
if (enableJtagBoundaryScan.value)
|
||||
setTimeout(jtagBoundaryScan, 1000 / jtagBoundaryScanFreq.value);
|
||||
async function jtagBoundaryScanSetOnOff(enable: boolean) {
|
||||
enableJtagBoundaryScan.value = enable;
|
||||
if (enable) {
|
||||
jtagHubProxy.startBoundaryScan(jtagBoundaryScanFreq.value);
|
||||
} else {
|
||||
jtagHubProxy.stopBoundaryScan();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,7 +99,7 @@ export const useEquipments = defineStore("equipments", () => {
|
||||
try {
|
||||
// 自动开启电源
|
||||
await powerSetOnOff(true);
|
||||
|
||||
|
||||
const jtagClient = AuthManager.createAuthenticatedJtagClient();
|
||||
const resp = await jtagClient.uploadBitstream(
|
||||
boardAddr.value,
|
||||
@@ -163,7 +118,7 @@ export const useEquipments = defineStore("equipments", () => {
|
||||
try {
|
||||
// 自动开启电源
|
||||
await powerSetOnOff(true);
|
||||
|
||||
|
||||
const jtagClient = AuthManager.createAuthenticatedJtagClient();
|
||||
const resp = await jtagClient.downloadBitstream(
|
||||
boardAddr.value,
|
||||
@@ -184,7 +139,7 @@ export const useEquipments = defineStore("equipments", () => {
|
||||
try {
|
||||
// 自动开启电源
|
||||
await powerSetOnOff(true);
|
||||
|
||||
|
||||
const jtagClient = AuthManager.createAuthenticatedJtagClient();
|
||||
const resp = await jtagClient.getDeviceIDCode(
|
||||
boardAddr.value,
|
||||
@@ -204,7 +159,7 @@ export const useEquipments = defineStore("equipments", () => {
|
||||
try {
|
||||
// 自动开启电源
|
||||
await powerSetOnOff(true);
|
||||
|
||||
|
||||
const jtagClient = AuthManager.createAuthenticatedJtagClient();
|
||||
const resp = await jtagClient.setSpeed(
|
||||
boardAddr.value,
|
||||
@@ -224,7 +179,8 @@ export const useEquipments = defineStore("equipments", () => {
|
||||
const release = await matrixKeypadClientMutex.acquire();
|
||||
console.log("set Key !!!!!!!!!!!!");
|
||||
try {
|
||||
const matrixKeypadClient = AuthManager.createAuthenticatedMatrixKeyClient();
|
||||
const matrixKeypadClient =
|
||||
AuthManager.createAuthenticatedMatrixKeyClient();
|
||||
const resp = await matrixKeypadClient.setMatrixKeyStatus(
|
||||
boardAddr.value,
|
||||
boardPort.value,
|
||||
@@ -243,7 +199,8 @@ export const useEquipments = defineStore("equipments", () => {
|
||||
const release = await matrixKeypadClientMutex.acquire();
|
||||
try {
|
||||
if (enable) {
|
||||
const matrixKeypadClient = AuthManager.createAuthenticatedMatrixKeyClient();
|
||||
const matrixKeypadClient =
|
||||
AuthManager.createAuthenticatedMatrixKeyClient();
|
||||
const resp = await matrixKeypadClient.enabelMatrixKey(
|
||||
boardAddr.value,
|
||||
boardPort.value,
|
||||
@@ -251,7 +208,8 @@ export const useEquipments = defineStore("equipments", () => {
|
||||
enableMatrixKey.value = resp;
|
||||
return resp;
|
||||
} else {
|
||||
const matrixKeypadClient = AuthManager.createAuthenticatedMatrixKeyClient();
|
||||
const matrixKeypadClient =
|
||||
AuthManager.createAuthenticatedMatrixKeyClient();
|
||||
const resp = await matrixKeypadClient.disableMatrixKey(
|
||||
boardAddr.value,
|
||||
boardPort.value,
|
||||
@@ -290,16 +248,13 @@ export const useEquipments = defineStore("equipments", () => {
|
||||
return {
|
||||
boardAddr,
|
||||
boardPort,
|
||||
setAddr,
|
||||
setPort,
|
||||
setMatrixKey,
|
||||
|
||||
// Jtag
|
||||
enableJtagBoundaryScan,
|
||||
jtagBoundaryScanSetOnOff,
|
||||
jtagBitstream,
|
||||
jtagBoundaryScanFreq,
|
||||
jtagBoundaryScanErrorCount,
|
||||
jtagClientMutex,
|
||||
jtagUploadBitstream,
|
||||
jtagDownloadBitstream,
|
||||
jtagGetIDCode,
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
import { ref, computed } from 'vue'
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
export const useSidebarStore = defineStore('sidebar', () => {
|
||||
const isClose = ref(false);
|
||||
|
||||
function closeSidebar() {
|
||||
isClose.value = true;
|
||||
console.info("Close sidebar");
|
||||
}
|
||||
|
||||
function openSidebar() {
|
||||
isClose.value = false;
|
||||
console.info("Open sidebar");
|
||||
}
|
||||
|
||||
function toggleSidebar() {
|
||||
if (isClose.value) {
|
||||
openSidebar();
|
||||
// themeSidebar.value = "card-dash sidebar-base sidebar-open"
|
||||
} else {
|
||||
closeSidebar();
|
||||
// themeSidebar.value = "card-dash sidebar-base sidebar-close"
|
||||
}
|
||||
}
|
||||
|
||||
return { isClose, closeSidebar, openSidebar, toggleSidebar }
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user