import { ref, reactive, watchPostEffect, onMounted, onUnmounted } from "vue"; import { defineStore } from "pinia"; import { useLocalStorage } from "@vueuse/core"; import { isString, toNumber, isUndefined, type Dictionary } from "lodash"; import z from "zod"; import { isNumber } from "mathjs"; 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 { HubConnection, HubConnectionBuilder } from "@microsoft/signalr"; import { getHubProxyFactory, getReceiverRegister } from "@/TypedSignalR.Client"; import type { ResourceInfo } from "@/APIClient"; import type { IJtagHub } from "@/TypedSignalR.Client/server.Hubs.JtagHub"; export const useEquipments = defineStore("equipments", () => { // Global Stores const constrainsts = useConstraintsStore(); const dialog = useDialogStore(); const boardAddr = useLocalStorage("fpga-board-addr", "127.0.0.1"); const boardPort = useLocalStorage("fpga-board-port", 1234); // Jtag const jtagBitstream = ref(); const jtagBoundaryScanFreq = ref(100); const jtagUserBitstreams = ref([]); const jtagClientMutex = withTimeout( new Mutex(), 1000, new Error("JtagClient Mutex Timeout!"), ); const jtagHubConnection = ref(); const jtagHubProxy = ref(); onMounted(async () => { // 每次挂载都重新创建连接 jtagHubConnection.value = AuthManager.createAuthenticatedJtagHubConnection(); jtagHubProxy.value = getHubProxyFactory("IJtagHub").createHubProxy( jtagHubConnection.value, ); getReceiverRegister("IJtagReceiver").register(jtagHubConnection.value, { onReceiveBoundaryScanData: async (msg) => { constrainsts.batchSetConstraintStates(msg); }, }); await jtagHubConnection.value.start(); }); onUnmounted(() => { // 断开连接,清理资源 if (jtagHubConnection.value) { jtagHubConnection.value.stop(); jtagHubConnection.value = undefined; jtagHubProxy.value = undefined; } }); // Matrix Key const matrixKeyStates = reactive(new Array(16).fill(false)); const matrixKeypadClientMutex = withTimeout( new Mutex(), 1000, new Error("Matrixkeyclient Mutex Timeout!"), ); // Power const powerClientMutex = withTimeout( new Mutex(), 1000, new Error("Matrixkeyclient Mutex Timeout!"), ); // Enable Setting const enableJtagBoundaryScan = ref(false); const enableMatrixKey = ref(false); const enablePower = ref(false); function setMatrixKey( keyNum: number | string | undefined, keyValue: boolean, ): boolean { let _keyNum: number; if (isString(keyNum)) { _keyNum = toNumber(keyNum); } else if (isNumber(keyNum)) { _keyNum = keyNum; } else { return false; } if (z.number().nonnegative().max(16).safeParse(_keyNum).success) { matrixKeyStates[_keyNum] = keyValue; return true; } return false; } async function jtagBoundaryScanSetOnOff(enable: boolean) { if (isUndefined(jtagHubProxy.value)) { console.error("JtagHub Not Initialize..."); return; } 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 { try { // 自动开启电源 await powerSetOnOff(true); const resourceClient = AuthManager.createAuthenticatedResourceClient(); const resp = await resourceClient.addResource( 'bitstream', 'user', examId || null, toFileParameterOrUndefined(bitstream), ); // 如果上传成功,设置为当前选中的比特流 if (resp && resp.id !== undefined && resp.id !== null) { return resp.id; } return null; } catch (e) { dialog.error("上传错误"); console.error(e); return null; } } async function jtagDownloadBitstream(bitstreamId?: number): Promise { if (bitstreamId === null || bitstreamId === undefined) { dialog.error("请先选择要下载的比特流"); return false; } const release = await jtagClientMutex.acquire(); try { // 自动开启电源 await powerSetOnOff(true); const jtagClient = AuthManager.createAuthenticatedJtagClient(); const resp = await jtagClient.downloadBitstream( boardAddr.value, boardPort.value, bitstreamId, ); return resp; } catch (e) { dialog.error("下载错误"); console.error(e); return false; } finally { release(); } } async function jtagGetIDCode(isQuiet: boolean = false): Promise { const release = await jtagClientMutex.acquire(); try { // 自动开启电源 await powerSetOnOff(true); const jtagClient = AuthManager.createAuthenticatedJtagClient(); const resp = await jtagClient.getDeviceIDCode( boardAddr.value, boardPort.value, ); return resp; } catch (e) { if (!isQuiet) dialog.error("获取IDCode错误"); return 0xffff_ffff; } finally { release(); } } async function jtagSetSpeed(speed: number): Promise { const release = await jtagClientMutex.acquire(); try { // 自动开启电源 await powerSetOnOff(true); const jtagClient = AuthManager.createAuthenticatedJtagClient(); const resp = await jtagClient.setSpeed( boardAddr.value, boardPort.value, speed, ); return resp; } catch (e) { dialog.error("设置Jtag速度失败"); return false; } finally { release(); } } async function matrixKeypadSetKeyStates(keyStates: boolean[]) { const release = await matrixKeypadClientMutex.acquire(); console.log("set Key !!!!!!!!!!!!"); try { const matrixKeypadClient = AuthManager.createAuthenticatedMatrixKeyClient(); const resp = await matrixKeypadClient.setMatrixKeyStatus( boardAddr.value, boardPort.value, keyStates, ); return resp; } catch (e) { dialog.error("设置矩阵键盘时,服务器发生错误"); return false; } finally { release(); } } async function matrixKeypadEnable(enable: boolean) { const release = await matrixKeypadClientMutex.acquire(); try { if (enable) { const matrixKeypadClient = AuthManager.createAuthenticatedMatrixKeyClient(); const resp = await matrixKeypadClient.enabelMatrixKey( boardAddr.value, boardPort.value, ); enableMatrixKey.value = resp; return resp; } else { const matrixKeypadClient = AuthManager.createAuthenticatedMatrixKeyClient(); const resp = await matrixKeypadClient.disableMatrixKey( boardAddr.value, boardPort.value, ); enableMatrixKey.value = !resp; return resp; } } catch (e) { enableMatrixKey.value = false; dialog.error("设置矩阵键盘是否启用时,服务器发生错误"); return false; } finally { release(); } } async function powerSetOnOff(enable: boolean) { const release = await powerClientMutex.acquire(); try { const powerClient = AuthManager.createAuthenticatedPowerClient(); const resp = await powerClient.setPowerOnOff( boardAddr.value, boardPort.value, enable, ); return resp; } catch (e) { dialog.error("无法开关电源"); console.error(e); return false; } finally { release(); } } return { boardAddr, boardPort, setMatrixKey, // Jtag enableJtagBoundaryScan, jtagBoundaryScanSetOnOff, jtagBitstream, jtagBoundaryScanFreq, jtagUserBitstreams, jtagUploadBitstream, jtagDownloadBitstream, jtagGetIDCode, jtagSetSpeed, // Matrix Key enableMatrixKey, matrixKeyStates, matrixKeypadClientMutex, matrixKeypadEnable, matrixKeypadSetKeyStates, // Power enablePower, powerClientMutex, powerSetOnOff, }; });