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 { base64ToArrayBuffer, toFileParameterOrUndefined, } from "@/utils/Common"; import { AuthManager } from "@/utils/AuthManager"; import { HubConnection, HubConnectionState } from "@microsoft/signalr"; import { getHubProxyFactory, getReceiverRegister, } from "@/utils/signalR/TypedSignalR.Client"; import { JtagClient, MatrixKeyClient, PowerClient, ResourceClient, ResourcePurpose, type ResourceInfo, } from "@/APIClient"; import type { IDigitalTubesHub, IJtagHub, } from "@/utils/signalR/TypedSignalR.Client/server.Hubs"; 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 enableJtagBoundaryScan = ref(false); 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.createHubConnection("JtagHub"); 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; } }); 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.createClient(ResourceClient); const resp = await resourceClient.addResource( "bitstream", ResourcePurpose.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?: string): Promise { if (bitstreamId === null || bitstreamId === undefined) { dialog.error("请先选择要下载的比特流"); return ""; } const release = await jtagClientMutex.acquire(); try { // 自动开启电源 await powerSetOnOff(true); const jtagClient = AuthManager.createClient(JtagClient); const resp = await jtagClient.downloadBitstream( boardAddr.value, boardPort.value, bitstreamId, ); return resp; } catch (e) { dialog.error("下载错误"); console.error(e); throw e; } finally { release(); } } async function jtagGetIDCode(isQuiet: boolean = false): Promise { const release = await jtagClientMutex.acquire(); try { // 自动开启电源 await powerSetOnOff(true); const jtagClient = AuthManager.createClient(JtagClient); 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.createClient(JtagClient); const resp = await jtagClient.setSpeed( boardAddr.value, boardPort.value, speed, ); return resp; } catch (e) { dialog.error("设置Jtag速度失败"); return false; } finally { release(); } } // Matrix Key const enableMatrixKey = ref(false); const matrixKeyStates = reactive(new Array(16).fill(false)); const matrixKeypadClientMutex = withTimeout( new Mutex(), 1000, new Error("Matrixkeyclient Mutex Timeout!"), ); 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 matrixKeypadSetKeyStates(keyStates: boolean[]) { const release = await matrixKeypadClientMutex.acquire(); try { const matrixKeypadClient = AuthManager.createClient(MatrixKeyClient); 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 { const matrixKeypadClient = AuthManager.createClient(MatrixKeyClient); if (enable) { const resp = await matrixKeypadClient.enabelMatrixKey( boardAddr.value, boardPort.value, ); enableMatrixKey.value = resp; return resp; } else { 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(); } } // Power const powerClientMutex = withTimeout( new Mutex(), 1000, new Error("Matrixkeyclient Mutex Timeout!"), ); const enablePower = ref(false); async function powerSetOnOff(enable: boolean) { const release = await powerClientMutex.acquire(); try { const powerClient = AuthManager.createClient(PowerClient); const resp = await powerClient.setPowerOnOff( boardAddr.value, boardPort.value, enable, ); return resp; } catch (e) { dialog.error("无法开关电源"); console.error(e); return false; } finally { release(); } } // Seven Segment Display const enableSevenSegmentDisplay = ref(false); const sevenSegmentDisplayFrequency = ref(100); const sevenSegmentDisplayData = ref(); const sevenSegmentDisplayHub = ref(); const sevenSegmentDisplayHubProxy = ref(); async function sevenSegmentDisplaySetOnOff(enable: boolean) { if (!sevenSegmentDisplayHub.value || !sevenSegmentDisplayHubProxy.value) return; if (sevenSegmentDisplayHub.value.state === HubConnectionState.Disconnected) await sevenSegmentDisplayHub.value.start(); if (enable) { await sevenSegmentDisplayHubProxy.value.startScan(); } else { await sevenSegmentDisplayHubProxy.value.stopScan(); } } async function sevenSegmentDisplaySetFrequency(frequency: number) { if (!sevenSegmentDisplayHub.value || !sevenSegmentDisplayHubProxy.value) return; if (sevenSegmentDisplayHub.value.state === HubConnectionState.Disconnected) await sevenSegmentDisplayHub.value.start(); await sevenSegmentDisplayHubProxy.value.setFrequency(frequency); } async function sevenSegmentDisplayGetStatus() { if (!sevenSegmentDisplayHub.value || !sevenSegmentDisplayHubProxy.value) return; if (sevenSegmentDisplayHub.value.state === HubConnectionState.Disconnected) await sevenSegmentDisplayHub.value.start(); return await sevenSegmentDisplayHubProxy.value.getStatus(); } async function handleSevenSegmentDisplayOnReceive(msg: string) { const bytes = base64ToArrayBuffer(msg); sevenSegmentDisplayData.value = new Uint8Array(bytes); } onMounted(async () => { // 每次挂载都重新创建连接 sevenSegmentDisplayHub.value = AuthManager.createHubConnection("DigitalTubesHub"); sevenSegmentDisplayHubProxy.value = getHubProxyFactory( "IDigitalTubesHub", ).createHubProxy(sevenSegmentDisplayHub.value); getReceiverRegister("IDigitalTubesReceiver").register( sevenSegmentDisplayHub.value, { onReceive: handleSevenSegmentDisplayOnReceive, }, ); }); onUnmounted(() => { // 断开连接,清理资源 if (sevenSegmentDisplayHub.value) { sevenSegmentDisplayHub.value.stop(); sevenSegmentDisplayHub.value = undefined; sevenSegmentDisplayHubProxy.value = undefined; } }); 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, // Seven Segment Display enableSevenSegmentDisplay, sevenSegmentDisplayData, sevenSegmentDisplayFrequency, sevenSegmentDisplaySetOnOff, sevenSegmentDisplaySetFrequency, sevenSegmentDisplayGetStatus, }; });