diff --git a/server/src/Controllers.cs b/server/src/Controllers.cs index 7132012..89d31aa 100644 --- a/server/src/Controllers.cs +++ b/server/src/Controllers.cs @@ -160,6 +160,7 @@ public class JtagController : ControllerBase /// 设备地址 /// 设备端口 [HttpGet("GetDeviceIDCode")] + [EnableCors("Users")] [ProducesResponseType(typeof(uint), StatusCodes.Status200OK)] [ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)] public async ValueTask GetDeviceIDCode(string address, int port) @@ -365,7 +366,7 @@ public class JtagController : ControllerBase /// [TODO:return] [HttpPost("BoundaryScanLogicalPorts")] [EnableCors("Users")] - [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(Dictionary), StatusCodes.Status200OK)] [ProducesResponseType(typeof(string), StatusCodes.Status400BadRequest)] [ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)] public async ValueTask BoundaryScanLogicalPorts(string address, int port) diff --git a/src/APIClient.ts b/src/APIClient.ts index d62ea10..42c25fd 100644 --- a/src/APIClient.ts +++ b/src/APIClient.ts @@ -659,7 +659,7 @@ export class JtagClient { * @param port (optional) [TODO:parameter] * @return [TODO:return] */ - boundaryScanLogicalPorts(address: string | undefined, port: number | undefined): Promise { + boundaryScanLogicalPorts(address: string | undefined, port: number | undefined): Promise<{ [key: string]: boolean; }> { let url_ = this.baseUrl + "/api/Jtag/BoundaryScanLogicalPorts?"; if (address === null) throw new Error("The parameter 'address' cannot be null."); @@ -683,15 +683,23 @@ export class JtagClient { }); } - protected processBoundaryScanLogicalPorts(response: Response): Promise { + protected processBoundaryScanLogicalPorts(response: Response): Promise<{ [key: string]: boolean; }> { const status = response.status; let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); }; if (status === 200) { return response.text().then((_responseText) => { let result200: any = null; let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver); - result200 = resultData200 !== undefined ? resultData200 : null; - + if (resultData200) { + result200 = {} as any; + for (let key in resultData200) { + if (resultData200.hasOwnProperty(key)) + (result200)![key] = resultData200[key] !== undefined ? resultData200[key] : null; + } + } + else { + result200 = null; + } return result200; }); } else if (status === 400) { @@ -714,7 +722,7 @@ export class JtagClient { return throwException("An unexpected server error occurred.", status, _responseText, _headers); }); } - return Promise.resolve(null as any); + return Promise.resolve<{ [key: string]: boolean; }>(null as any); } } @@ -1818,4 +1826,4 @@ function throwException(message: string, status: number, response: string, heade throw result; else throw new ApiException(message, status, response, headers, null); -} \ No newline at end of file +} diff --git a/src/components/equipments/MechanicalButton.vue b/src/components/equipments/MechanicalButton.vue index 1e315d7..b167845 100644 --- a/src/components/equipments/MechanicalButton.vue +++ b/src/components/equipments/MechanicalButton.vue @@ -1,12 +1,11 @@ - @@ -258,15 +253,15 @@ defineExpose({ export function getDefaultProps() { return { size: 1, - bindKey: '', + bindKey: "", pins: [ { - pinId: 'BTN', - constraint: '', + pinId: "BTN", + constraint: "", x: 80, - y: 140 - } - ] + y: 140, + }, + ], }; } diff --git a/src/components/equipments/MotherBoard.vue b/src/components/equipments/MotherBoard.vue index 6932298..d9f2ca9 100644 --- a/src/components/equipments/MotherBoard.vue +++ b/src/components/equipments/MotherBoard.vue @@ -39,6 +39,7 @@ const selectecComponentID = inject(CanvasCurrentSelectedComponentID, ref(null)); const width = computed(() => 800 * props.size); const height = computed(() => 600 * props.size); +// Global store const eqps = useEquipments(); const bitstreamFile = ref(); diff --git a/src/components/equipments/MotherBoardCaps.vue b/src/components/equipments/MotherBoardCaps.vue index 4e55ecf..084d0b0 100644 --- a/src/components/equipments/MotherBoardCaps.vue +++ b/src/components/equipments/MotherBoardCaps.vue @@ -6,9 +6,9 @@

Jtag Port: {{ props.jtagPort }}

IDCode: 0x{{ jtagIDCode.toString(16).padStart(8, "0") }}

-
@@ -27,6 +32,10 @@ import { JtagClient } from "@/APIClient"; import z from "zod"; import UploadCard from "@/components/UploadCard.vue"; import { useDialogStore } from "@/stores/dialog"; +import { + useConstraintsStore, + type ConstraintLevel, +} from "@/stores/constraints"; import { isUndefined, toNumber } from "lodash"; import { ref, computed, watch } from "vue"; @@ -37,8 +46,12 @@ interface CapsProps { const props = withDefaults(defineProps(), {}); -const jtagController = new JtagClient(); +// Global Stores const dialog = useDialogStore(); +const constrainsts = useConstraintsStore(); + +const jtagController = new JtagClient(); +const isEnableJtagBoundaryScan = ref(false); // 使用传入的属性或默认值 const jtagIDCode = ref(0); @@ -47,6 +60,28 @@ const boardPort = computed(() => isUndefined(props.jtagPort) ? undefined : toNumber(props.jtagPort), ); +async function toggleJtagBoundaryScan() { + isEnableJtagBoundaryScan.value = !isEnableJtagBoundaryScan.value; + if (isEnableJtagBoundaryScan.value) jtagBoundaryScan(); +} + +async function jtagBoundaryScan() { + try { + const portStates = await jtagController.boundaryScanLogicalPorts( + boardAddress.value, + boardPort.value, + ); + + constrainsts.batchSetConstraintStates(portStates); + } catch (error) { + dialog.error("边界扫描发生错误"); + console.error(error); + isEnableJtagBoundaryScan.value = false; + } finally { + if (isEnableJtagBoundaryScan.value) setTimeout(jtagBoundaryScan, 100); + } +} + async function uploadBitstream(event: Event, bitstream: File) { if (!isUndefined(boardAddress.value) || !isUndefined(boardPort.value)) { dialog.error("开发板地址或端口空缺"); diff --git a/src/components/equipments/PG2L100H_FBG676.vue b/src/components/equipments/PG2L100H_FBG676.vue index e7a208f..4dc172b 100644 --- a/src/components/equipments/PG2L100H_FBG676.vue +++ b/src/components/equipments/PG2L100H_FBG676.vue @@ -1,6 +1,10 @@ @@ -134,7 +141,7 @@ defineExpose({ export function getDefaultProps() { return { size: 1, - pins: [] // 默认不提供引脚,由computedPins计算生成 + pins: [], // 默认不提供引脚,由computedPins计算生成 }; } diff --git a/src/components/equipments/Pin.vue b/src/components/equipments/Pin.vue index 63e6478..50f2fb7 100644 --- a/src/components/equipments/Pin.vue +++ b/src/components/equipments/Pin.vue @@ -1,30 +1,14 @@ @@ -167,20 +155,20 @@ defineExpose({ export function getDefaultProps() { return { size: 1, - label: 'PIN', - constraint: '', - direction: 'input', - type: 'digital', - pinId: 'pin-default', - componentId: '', + label: "PIN", + constraint: "", + direction: "input", + type: "digital", + pinId: "pin-default", + componentId: "", pins: [ { - pinId: 'PIN', - constraint: '', + pinId: "PIN", + constraint: "", x: 0, - y: 0 - } - ] + y: 0, + }, + ], }; } @@ -190,18 +178,25 @@ export function getDefaultProps() { display: block; user-select: none; position: relative; - z-index: 5; /* 提高引脚组件的z-index */ - pointer-events: auto; /* 确保可以接收点击事件 */ - overflow: visible; /* 确保可以看到引脚 */ + z-index: 5; + /* 提高引脚组件的z-index */ + pointer-events: auto; + /* 确保可以接收点击事件 */ + overflow: visible; + /* 确保可以看到引脚 */ } + .interactive { cursor: pointer; transition: filter 0.2s; - pointer-events: auto; /* 确保可以接收点击事件 */ + pointer-events: auto; + /* 确保可以接收点击事件 */ } + .interactive:hover { filter: brightness(1.2); - stroke: rgba(255, 255, 255, 0.3); /* 添加边框以便更容易看到点击区域 */ + stroke: rgba(255, 255, 255, 0.3); + /* 添加边框以便更容易看到点击区域 */ stroke-width: 1; } diff --git a/src/components/equipments/SMT_LED.vue b/src/components/equipments/SMT_LED.vue index c5c033a..d55bf8a 100644 --- a/src/components/equipments/SMT_LED.vue +++ b/src/components/equipments/SMT_LED.vue @@ -64,9 +64,11 @@ @@ -224,7 +201,9 @@ defineExpose({ } .segment { - transition: opacity 0.2s, fill 0.2s; + transition: + opacity 0.2s, + fill 0.2s; } /* 数码管发光效果 */ @@ -238,19 +217,19 @@ defineExpose({ export function getDefaultProps() { return { size: 1, - color: 'red', - cathodeType: 'common', + color: "red", + cathodeType: "common", pins: [ - { pinId: 'a', constraint: '', x: 10 , y: 170 }, - { pinId: 'b', constraint: '', x: 25-1 , y: 170 }, - { pinId: 'c', constraint: '', x: 40-2 , y: 170 }, - { pinId: 'd', constraint: '', x: 55-3 , y: 170 }, - { pinId: 'e', constraint: '', x: 70-4 , y: 170 }, - { pinId: 'f', constraint: '', x: 85-5 , y: 170 }, - { pinId: 'g', constraint: '', x: 100-6, y: 170 }, - { pinId: 'dp', constraint: '', x: 115-7, y: 170 }, - { pinId: 'COM', constraint: '', x: 60 , y: 10 } - ] + { pinId: "a", constraint: "", x: 10, y: 170 }, + { pinId: "b", constraint: "", x: 25 - 1, y: 170 }, + { pinId: "c", constraint: "", x: 40 - 2, y: 170 }, + { pinId: "d", constraint: "", x: 55 - 3, y: 170 }, + { pinId: "e", constraint: "", x: 70 - 4, y: 170 }, + { pinId: "f", constraint: "", x: 85 - 5, y: 170 }, + { pinId: "g", constraint: "", x: 100 - 6, y: 170 }, + { pinId: "dp", constraint: "", x: 115 - 7, y: 170 }, + { pinId: "COM", constraint: "", x: 60, y: 10 }, + ], }; } diff --git a/src/components/equipments/Wire.vue b/src/components/equipments/Wire.vue index 3361727..60770c1 100644 --- a/src/components/equipments/Wire.vue +++ b/src/components/equipments/Wire.vue @@ -1,28 +1,18 @@ diff --git a/src/stores/constraints.ts b/src/stores/constraints.ts index f09783b..ddaa4d8 100644 --- a/src/stores/constraints.ts +++ b/src/stores/constraints.ts @@ -1,81 +1,103 @@ -import { ref, reactive } from 'vue'; +import { ref, computed, reactive } from 'vue' +import { defineStore } from 'pinia' +import { isBoolean } from 'lodash'; + // 约束电平状态类型 export type ConstraintLevel = 'high' | 'low' | 'undefined'; -// 约束状态存储 -const constraintStates = reactive>({}); +export const useConstraintsStore = defineStore('constraints', () => { -// 约束颜色映射 -export const constraintColors = { - high: '#ff3333', // 高电平为红色 - low: '#3333ff', // 低电平为蓝色 - undefined: '#999999' // 未定义为灰色 -}; + // 约束状态存储 + const constraintStates = reactive>({}); -// 获取约束状态 -export function getConstraintState(constraint: string): ConstraintLevel { - if (!constraint) return 'undefined'; - return constraintStates[constraint] || 'undefined'; -} - -// 设置约束状态 -export function setConstraintState(constraint: string, level: ConstraintLevel) { - if (!constraint) return; - constraintStates[constraint] = level; -} - -// 批量设置约束状态 -export function batchSetConstraintStates(states: Record) { - // 收集发生变化的约束 - const changedConstraints: [string, ConstraintLevel][] = []; - - // 更新状态并收集变化 - Object.entries(states).forEach(([constraint, level]) => { - if (constraintStates[constraint] !== level) { - constraintStates[constraint] = level; - changedConstraints.push([constraint, level]); - } - }); - - // 通知所有变化 - changedConstraints.forEach(([constraint, level]) => { - stateChangeCallbacks.forEach(callback => callback(constraint, level)); - }); -} - -// 获取约束对应的颜色 -export function getConstraintColor(constraint: string): string { - const state = getConstraintState(constraint); - return constraintColors[state]; -} - -// 清除所有约束状态 -export function clearAllConstraintStates() { - Object.keys(constraintStates).forEach(key => { - delete constraintStates[key]; - }); -} - -// 获取所有约束状态 -export function getAllConstraintStates(): Record { - return { ...constraintStates }; -} - -// 注册约束状态变化回调 -const stateChangeCallbacks: ((constraint: string, level: ConstraintLevel) => void)[] = []; - -export function onConstraintStateChange(callback: (constraint: string, level: ConstraintLevel) => void) { - stateChangeCallbacks.push(callback); - return () => { - const index = stateChangeCallbacks.indexOf(callback); - if (index > -1) { - stateChangeCallbacks.splice(index, 1); - } + // 约束颜色映射 + const constraintColors = { + high: '#ff3333', // 高电平为红色 + low: '#3333ff', // 低电平为蓝色 + undefined: '#999999' // 未定义为灰色 }; -} -// 触发约束变化 -export function notifyConstraintChange(constraint: string, level: ConstraintLevel) { - setConstraintState(constraint, level); - stateChangeCallbacks.forEach(callback => callback(constraint, level)); -} + // 获取约束状态 + function getConstraintState(constraint: string): ConstraintLevel { + if (!constraint) return 'undefined'; + return constraintStates[constraint] || 'undefined'; + } + + // 设置约束状态 + function setConstraintState(constraint: string, level: ConstraintLevel) { + if (!constraint) return; + constraintStates[constraint] = level; + } + + // 批量设置约束状态 + function batchSetConstraintStates(states: Record | Record) { + // 收集发生变化的约束 + const changedConstraints: [string, ConstraintLevel][] = []; + + // 更新状态并收集变化 + Object.entries(states).forEach(([constraint, level]) => { + if (isBoolean(level)) { + level = level ? "high" : "low"; + } + + if (constraintStates[constraint] !== level) { + constraintStates[constraint] = level; + changedConstraints.push([constraint, level]); + } + }); + + // 通知所有变化 + changedConstraints.forEach(([constraint, level]) => { + stateChangeCallbacks.forEach(callback => callback(constraint, level)); + }); + } + + // 获取约束对应的颜色 + function getConstraintColor(constraint: string): string { + const state = getConstraintState(constraint); + return constraintColors[state]; + } + + // 清除所有约束状态 + function clearAllConstraintStates() { + Object.keys(constraintStates).forEach(key => { + delete constraintStates[key]; + }); + } + + // 获取所有约束状态 + function getAllConstraintStates(): Record { + return { ...constraintStates }; + } + + // 注册约束状态变化回调 + const stateChangeCallbacks: ((constraint: string, level: ConstraintLevel) => void)[] = []; + + function onConstraintStateChange(callback: (constraint: string, level: ConstraintLevel) => void) { + stateChangeCallbacks.push(callback); + return () => { + const index = stateChangeCallbacks.indexOf(callback); + if (index > -1) { + stateChangeCallbacks.splice(index, 1); + } + }; + } + + // 触发约束变化 + function notifyConstraintChange(constraint: string, level: ConstraintLevel) { + setConstraintState(constraint, level); + stateChangeCallbacks.forEach(callback => callback(constraint, level)); + } + + return { + getConstraintState, + setConstraintState, + batchSetConstraintStates, + getConstraintColor, + clearAllConstraintStates, + getAllConstraintStates, + onConstraintStateChange, + notifyConstraintChange, + } +}) + diff --git a/src/views/AdminView.vue b/src/views/AdminView.vue index 71d7187..9b3b625 100644 --- a/src/views/AdminView.vue +++ b/src/views/AdminView.vue @@ -113,7 +113,7 @@