优化保存恢复功能
This commit is contained in:
parent
f7ac311563
commit
08148209e7
@ -17,14 +17,16 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
window.readVcdFile = async () => {
|
window.readVcdFile = async () => {
|
||||||
let inputFile = 'test.vcd';
|
let inputVcdFile = 'test.vcd';
|
||||||
const response = await fetch(inputFile);
|
let inputViewFile = 'test.vcd.view';
|
||||||
|
|
||||||
|
const response = await fetch(inputVcdFile);
|
||||||
const blob = await response.blob();
|
const blob = await response.blob();
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
reader.onload = event => {
|
reader.onload = event => {
|
||||||
const arrayBuffer = event.target.result;
|
const arrayBuffer = event.target.result;
|
||||||
resolve([arrayBuffer, inputFile]);
|
resolve([arrayBuffer, inputVcdFile, inputViewFile]);
|
||||||
};
|
};
|
||||||
reader.readAsArrayBuffer(blob);
|
reader.readAsArrayBuffer(blob);
|
||||||
});
|
});
|
||||||
|
Binary file not shown.
@ -86,10 +86,10 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 初始化载入 vcd 文件
|
// 初始化载入 vcd 文件
|
||||||
const [arrayBuffer, inputFile] = await window.readVcdFile();
|
const [arrayBuffer, inputFile, inputViewFile] = await window.readVcdFile();
|
||||||
|
|
||||||
// 寻找 inputFile 下同名的文件,载入配置中
|
// 寻找 inputFile 下同名的文件,载入配置中
|
||||||
await recoverFromInputFile(inputFile);
|
await recoverFromInputFile(inputFile, inputViewFile);
|
||||||
|
|
||||||
const url = await getCrossOriginWorkerURL(window.workerPath);
|
const url = await getCrossOriginWorkerURL(window.workerPath);
|
||||||
const worker = new Worker(url, {
|
const worker = new Worker(url, {
|
||||||
|
@ -3,9 +3,12 @@ console.log('digital-vcd-viewer mode: ' + mode);
|
|||||||
|
|
||||||
let vscode = window.acquireVsCodeApi === undefined ? undefined : acquireVsCodeApi();
|
let vscode = window.acquireVsCodeApi === undefined ? undefined : acquireVsCodeApi();
|
||||||
|
|
||||||
import { debounceWrapper } from '@/hook/utils';
|
import { globalLookup } from '@/hook/global';
|
||||||
|
import { makeSaveViewPayload } from '@/hook/recover';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
|
export let saveDelay = 1000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {string} file
|
* @param {string} file
|
||||||
@ -27,4 +30,28 @@ export async function saveView(file, payload) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const saveViewApi = debounceWrapper(saveView, 1000);
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {number} delay
|
||||||
|
* @returns {(config: import('@/hook/recover').SavePayloadConfig) => void}
|
||||||
|
*/
|
||||||
|
function debounceSaveView(delay) {
|
||||||
|
let timer;
|
||||||
|
const configPool = {};
|
||||||
|
return function (config) {
|
||||||
|
// 记录所有的 payload tag
|
||||||
|
Object.assign(configPool, config);
|
||||||
|
|
||||||
|
if (timer) {
|
||||||
|
clearTimeout(timer);
|
||||||
|
}
|
||||||
|
timer = setTimeout(() => {
|
||||||
|
const payload = makeSaveViewPayload(configPool);
|
||||||
|
const savePath = globalLookup.originVcdViewFile;
|
||||||
|
saveView(savePath, payload);
|
||||||
|
}, delay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const saveViewApi = debounceSaveView(saveDelay);
|
@ -19,11 +19,10 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { emitter, globalLookup } from '@/hook/global';
|
import { emitter, globalLookup } from '@/hook/global';
|
||||||
import { eventHandler, registerWheelEvent } from '@/hook/wave-view';
|
import { computed, defineComponent, ref } from 'vue';
|
||||||
import { computed, defineComponent, ref, onMounted } from 'vue';
|
|
||||||
import { time2cursorX, MovingPivot, cursorX2time } from './cursor';
|
import { time2cursorX, MovingPivot, cursorX2time } from './cursor';
|
||||||
import formatTime from '@/hook/wave-view/format-time';
|
import formatTime from '@/hook/wave-view/format-time';
|
||||||
import { getNearestUserPivot, UserPivots } from './pivot-view';
|
import { getNearestUserPivot } from './pivot-view';
|
||||||
import { RelativeAxis } from './relative-axis';
|
import { RelativeAxis } from './relative-axis';
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,12 +17,12 @@ import { RelativeAxis } from './relative-axis';
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 该数据结构描述了所有通过 makePivot 创建的 用户信标
|
* @description 该数据结构描述了所有通过 makePivot 创建的 用户信标
|
||||||
* @type {Map<number, UserPivot>} number 为时间
|
* @type {Map<number, UserPivot>} number 为当前信标在数轴中的刻度
|
||||||
*/
|
*/
|
||||||
export const UserPivots = reactive(new Map());
|
export const UserPivots = reactive(new Map());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {Map<number, UserPivot>} number 为 id
|
* @type {Map<number, UserPivot>} number 为 id,id 是创建的 Unix 时间戳
|
||||||
*/
|
*/
|
||||||
export const Id2Pivot = reactive(new Map());
|
export const Id2Pivot = reactive(new Map());
|
||||||
|
|
||||||
|
28
src/components/right-nav.js
Normal file
28
src/components/right-nav.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { saveViewApi } from "@/api";
|
||||||
|
import { globalLookup } from "@/hook/global";
|
||||||
|
import { reactive } from "vue";
|
||||||
|
|
||||||
|
export const controlPanel = reactive({
|
||||||
|
sections: [
|
||||||
|
{
|
||||||
|
iconClass: 'iconfont icon-collections'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
iconClass: 'iconfont icon-setting'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
iconClass: 'iconfont icon-about'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
currentIndex: -1,
|
||||||
|
click(index) {
|
||||||
|
if (this.currentIndex === index) {
|
||||||
|
this.currentIndex = -1;
|
||||||
|
} else {
|
||||||
|
this.currentIndex = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存
|
||||||
|
saveViewApi({ rightNavIndex: true });
|
||||||
|
}
|
||||||
|
});
|
@ -28,65 +28,32 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import { reactive } from 'vue';
|
import { defineComponent, reactive } from 'vue';
|
||||||
|
|
||||||
import TreeView from '@/components/treeview';
|
import TreeView from '@/components/treeview';
|
||||||
import Setting from '@/components/setting';
|
import Setting from '@/components/setting';
|
||||||
import About from '@/components/about';
|
import About from '@/components/about';
|
||||||
import { emitter } from '@/hook/global';
|
import { emitter } from '@/hook/global';
|
||||||
|
import { controlPanel } from './right-nav';
|
||||||
|
|
||||||
|
defineComponent({ name: 'right-nav' });
|
||||||
export default {
|
const props = defineProps({
|
||||||
name: 'right-nav',
|
topModules: {
|
||||||
components: {
|
type: Array,
|
||||||
TreeView,
|
required: true
|
||||||
Setting,
|
|
||||||
About
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
topModules: Array
|
|
||||||
},
|
|
||||||
setup(props) {
|
|
||||||
|
|
||||||
const controlPanel = reactive({
|
|
||||||
sections: [
|
|
||||||
{
|
|
||||||
iconClass: 'iconfont icon-collections'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
iconClass: 'iconfont icon-setting'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
iconClass: 'iconfont icon-about'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
currentIndex: -1,
|
|
||||||
click(index) {
|
|
||||||
if (this.currentIndex === index) {
|
|
||||||
this.currentIndex = -1;
|
|
||||||
} else {
|
|
||||||
this.currentIndex = index;
|
|
||||||
}
|
|
||||||
|
|
||||||
// console.log(this.currentIndex);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
emitter.on('right-nav', index => {
|
emitter.on('right-nav', index => {
|
||||||
if (controlPanel.currentIndex === index) {
|
if (controlPanel.currentIndex === index) {
|
||||||
controlPanel.currentIndex = -1;
|
controlPanel.currentIndex = -1;
|
||||||
} else {
|
} else {
|
||||||
controlPanel.currentIndex = index;
|
controlPanel.currentIndex = index;
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
return {
|
|
||||||
props,
|
|
||||||
controlPanel
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@ -1,14 +1,10 @@
|
|||||||
import { globalLookup } from "@/hook/global";
|
import { globalLookup } from "@/hook/global";
|
||||||
import { updateCurrentGroups } from "./manage-group";
|
import { updateCurrentGroups } from "./manage-group";
|
||||||
import { makeSaveViewPayload } from "@/hook/recover";
|
|
||||||
import { saveViewApi } from "@/api";
|
import { saveViewApi } from "@/api";
|
||||||
|
|
||||||
export function onUpdate() {
|
export function onUpdate() {
|
||||||
globalLookup.render();
|
globalLookup.render();
|
||||||
updateCurrentGroups();
|
updateCurrentGroups();
|
||||||
// 保存视图
|
// 保存视图
|
||||||
// TODO: 优化:仅保存视图
|
saveViewApi({ views: true });
|
||||||
const savePath = globalLookup.originFile + '.view';
|
|
||||||
const payload = makeSaveViewPayload();
|
|
||||||
saveViewApi(savePath, payload);
|
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ import { globalLookup } from "@/hook/global";
|
|||||||
import { colorPicker, contextmenu, groupcontextmenu, handleGroupContextmenuFromSignalItem } from "./handle-contextmenu";
|
import { colorPicker, contextmenu, groupcontextmenu, handleGroupContextmenuFromSignalItem } from "./handle-contextmenu";
|
||||||
import { reactive, ref } from "vue";
|
import { reactive, ref } from "vue";
|
||||||
import { findViewIndexByLink } from "@/hook/wave-container-view";
|
import { findViewIndexByLink } from "@/hook/wave-container-view";
|
||||||
import { makeSaveViewPayload } from "@/hook/recover";
|
|
||||||
import { saveViewApi } from "@/api";
|
import { saveViewApi } from "@/api";
|
||||||
|
|
||||||
export const groupColors = [
|
export const groupColors = [
|
||||||
@ -138,10 +137,7 @@ export function createGroup() {
|
|||||||
updateCurrentGroups();
|
updateCurrentGroups();
|
||||||
|
|
||||||
// 保存视图
|
// 保存视图
|
||||||
// TODO: 优化:仅保存视图
|
saveViewApi({ views: true });
|
||||||
const savePath = globalLookup.originFile + '.view';
|
|
||||||
const payload = makeSaveViewPayload();
|
|
||||||
saveViewApi(savePath, payload);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function cancelGroup() {
|
export function cancelGroup() {
|
||||||
@ -168,10 +164,7 @@ export function cancelGroup() {
|
|||||||
updateCurrentGroups();
|
updateCurrentGroups();
|
||||||
|
|
||||||
// 保存视图
|
// 保存视图
|
||||||
// TODO: 优化:仅保存视图
|
saveViewApi({ views: true });
|
||||||
const savePath = globalLookup.originFile + '.view';
|
|
||||||
const payload = makeSaveViewPayload();
|
|
||||||
saveViewApi(savePath, payload);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -203,7 +196,5 @@ export function deleteGroup() {
|
|||||||
updateCurrentGroups();
|
updateCurrentGroups();
|
||||||
|
|
||||||
// 保存视图
|
// 保存视图
|
||||||
const savePath = globalLookup.originFile + '.view';
|
saveViewApi({ waves: true });
|
||||||
const payload = makeSaveViewPayload();
|
|
||||||
saveViewApi(savePath, payload);
|
|
||||||
}
|
}
|
@ -43,9 +43,8 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { saveViewApi } from '@/api';
|
import { saveViewApi } from '@/api';
|
||||||
import { globalLookup } from '@/hook/global';
|
import { globalLookup } from '@/hook/global';
|
||||||
import { makeSaveViewPayload } from '@/hook/recover';
|
|
||||||
import { sidebarSelectedWires } from '@/hook/sidebar-select-wire';
|
import { sidebarSelectedWires } from '@/hook/sidebar-select-wire';
|
||||||
import { defineComponent, nextTick, ref, watch } from 'vue';
|
import { defineComponent, ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
// 负责展示波形形式的模块,分为数字形式、折线模拟形式、阶梯模拟形式
|
// 负责展示波形形式的模块,分为数字形式、折线模拟形式、阶梯模拟形式
|
||||||
@ -71,10 +70,7 @@ function onSignalModalUpdate() {
|
|||||||
globalLookup.render();
|
globalLookup.render();
|
||||||
|
|
||||||
// 保存
|
// 保存
|
||||||
// TODO: 优化:只保存 waves
|
saveViewApi({ waves: true });
|
||||||
const savePath = globalLookup.originFile + '.view';
|
|
||||||
const payload = makeSaveViewPayload();
|
|
||||||
saveViewApi(savePath, payload);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,9 +26,8 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { saveViewApi } from '@/api';
|
import { saveViewApi } from '@/api';
|
||||||
import { globalLookup } from '@/hook/global';
|
import { globalLookup } from '@/hook/global';
|
||||||
import { makeSaveViewPayload } from '@/hook/recover';
|
|
||||||
import { sidebarSelectedWires } from '@/hook/sidebar-select-wire';
|
import { sidebarSelectedWires } from '@/hook/sidebar-select-wire';
|
||||||
import { defineComponent, onMounted, reactive, ref, watch } from 'vue';
|
import { defineComponent, onMounted, reactive, ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
// 负责展示波形 数值的 类型,分为 二进制、十六进制、八进制、十进制、浮点数(半精度、单精度、双精度)
|
// 负责展示波形 数值的 类型,分为 二进制、十六进制、八进制、十进制、浮点数(半精度、单精度、双精度)
|
||||||
@ -110,10 +109,7 @@ function onChange() {
|
|||||||
globalLookup.render({ type: 'value' });
|
globalLookup.render({ type: 'value' });
|
||||||
|
|
||||||
// 保存
|
// 保存
|
||||||
// TODO: 优化:只保存 waves
|
saveViewApi({ waves: true });
|
||||||
const savePath = globalLookup.originFile + '.view';
|
|
||||||
const payload = makeSaveViewPayload();
|
|
||||||
saveViewApi(savePath, payload);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
5
src/components/treeview/modules.js
Normal file
5
src/components/treeview/modules.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Set<ScopeItem>}
|
||||||
|
*/
|
||||||
|
export const TreeviewExpandSignals = new Set();
|
@ -22,18 +22,22 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
import { reactive, onMounted } from 'vue';
|
import { reactive, defineComponent } from 'vue';
|
||||||
import { emitter, globalLookup } from '@/hook/global';
|
import { emitter, globalLookup } from '@/hook/global';
|
||||||
import { isScope, isVariable, makeIconClass } from '@/hook/utils';
|
import { isScope, isVariable, makeIconClass } from '@/hook/utils';
|
||||||
|
import { TreeviewExpandSignals } from './modules';
|
||||||
|
import { saveViewApi } from '@/api';
|
||||||
|
|
||||||
|
defineComponent({ name: 'modules' });
|
||||||
|
const props = defineProps({
|
||||||
|
module: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "modules",
|
|
||||||
props: {
|
|
||||||
module: Object
|
|
||||||
},
|
|
||||||
setup(props) {
|
|
||||||
const module = props.module;
|
const module = props.module;
|
||||||
globalLookup.initcurrentModule(module);
|
globalLookup.initcurrentModule(module);
|
||||||
|
|
||||||
@ -54,27 +58,32 @@ export default {
|
|||||||
globalLookup.currentModule = module;
|
globalLookup.currentModule = module;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getExpandStatus() {
|
||||||
|
if (mods.length === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TreeviewExpandSignals.has(module);
|
||||||
|
}
|
||||||
|
|
||||||
const expandManage = reactive({
|
const expandManage = reactive({
|
||||||
expanded: false,
|
expanded: getExpandStatus(),
|
||||||
expandTagClass: mods.length === 0 ? '' : 'collapse-tag',
|
expandTagClass: mods.length === 0 ? '' : 'collapse-tag',
|
||||||
click() {
|
click() {
|
||||||
this.expanded = !this.expanded;
|
this.expanded = !this.expanded;
|
||||||
if (this.expandTagClass) {
|
if (this.expandTagClass) {
|
||||||
this.expandTagClass = this.expanded ? 'expand-tag' : 'collapse-tag';
|
this.expandTagClass = this.expanded ? 'expand-tag' : 'collapse-tag';
|
||||||
}
|
}
|
||||||
|
if (this.expanded) {
|
||||||
|
TreeviewExpandSignals.add(module);
|
||||||
|
} else {
|
||||||
|
TreeviewExpandSignals.delete(module);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存
|
||||||
|
saveViewApi({ treeviewExpands: true });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
|
||||||
module,
|
|
||||||
mods,
|
|
||||||
clickItem,
|
|
||||||
expandManage,
|
|
||||||
globalLookup,
|
|
||||||
makeIconClass
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@ -28,7 +28,6 @@ import { makeIconClass } from '@/hook/utils';
|
|||||||
import { WaveContainerView } from '@/hook/wave-container-view';
|
import { WaveContainerView } from '@/hook/wave-container-view';
|
||||||
import { controller } from './signals';
|
import { controller } from './signals';
|
||||||
import { saveViewApi } from '@/api';
|
import { saveViewApi } from '@/api';
|
||||||
import { makeSaveViewPayload } from '@/hook/recover';
|
|
||||||
|
|
||||||
defineComponent({ name: 'signals' });
|
defineComponent({ name: 'signals' });
|
||||||
|
|
||||||
@ -91,9 +90,7 @@ function toggleRender(event, signal) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 保存
|
// 保存
|
||||||
const savePath = globalLookup.originFile + '.view';
|
saveViewApi({ waves: true });
|
||||||
const payload = makeSaveViewPayload();
|
|
||||||
saveViewApi(savePath, payload);
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -13,11 +13,16 @@ export const globalLookup = reactive({
|
|||||||
/**
|
/**
|
||||||
* @description 读取的文件的路径
|
* @description 读取的文件的路径
|
||||||
*/
|
*/
|
||||||
originFile: '',
|
originVcdFile: '',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 读取文件的 view 路径
|
||||||
|
*/
|
||||||
|
originVcdViewFile: '',
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 所有的顶层文件
|
* @description 所有的顶层文件
|
||||||
* @type {TopModWireItem[]}
|
* @type {ScopeItem[]}
|
||||||
*/
|
*/
|
||||||
topModules: [],
|
topModules: [],
|
||||||
|
|
||||||
@ -112,16 +117,23 @@ export const globalLookup = reactive({
|
|||||||
|
|
||||||
xScale: 1,
|
xScale: 1,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 初始化 module,为每一个 module 配上 parent 并给出对应的 nameClass
|
||||||
|
* @param {ScopeItem} module
|
||||||
|
*/
|
||||||
initcurrentModule(module) {
|
initcurrentModule(module) {
|
||||||
if (this.currentModule === undefined && module) {
|
if (this.currentModule === undefined && module) {
|
||||||
this.currentModule = module;
|
this.currentModule = module;
|
||||||
}
|
}
|
||||||
|
if (module.nameClass === undefined) {
|
||||||
|
module.nameClass = module.name;
|
||||||
|
}
|
||||||
// 创造 parent,当然,这一步也可能在 recoverSession 中被实现了
|
// 创造 parent,当然,这一步也可能在 recoverSession 中被实现了
|
||||||
if (module.body && module.body instanceof Array) {
|
if (module.body && module.body instanceof Array) {
|
||||||
for (const childModule of module.body) {
|
for (const childModule of module.body) {
|
||||||
if (childModule.parent === undefined) {
|
// 计算 nameClass
|
||||||
childModule.parent = module;
|
childModule.parent = module;
|
||||||
}
|
childModule.nameClass = module.nameClass + '.' + childModule.name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -3,16 +3,40 @@ import { globalLookup } from "./global";
|
|||||||
import { WaveContainerView } from "./wave-container-view";
|
import { WaveContainerView } from "./wave-container-view";
|
||||||
import { isScope, isVariable } from "./utils";
|
import { isScope, isVariable } from "./utils";
|
||||||
import { BSON } from "bson";
|
import { BSON } from "bson";
|
||||||
|
import { Id2Pivot, orderedTimes, UserPivots } from "@/components/pivot/pivot-view";
|
||||||
|
import { TreeviewExpandSignals } from "@/components/treeview/modules";
|
||||||
|
import { controlPanel } from "@/components/right-nav";
|
||||||
|
|
||||||
export const recoverConfig = {
|
export const recoverConfig = {
|
||||||
/**
|
/**
|
||||||
* @type {Map<string, IRenderOption>} 其中的 string 是形如 `cpu.alu.add1` 这样的字符串
|
* @type {Map<string, IRenderOption>} 其中的 string 是形如 `cpu.alu.add1` 这样的字符串
|
||||||
*/
|
*/
|
||||||
waves: new Map(),
|
waves: new Map(),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {WaveRenderSidebarItem[] | undefined}
|
* @type {WaveRenderSidebarItem[] | undefined}
|
||||||
*/
|
*/
|
||||||
views: undefined
|
views: undefined,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Pstate | undefined}
|
||||||
|
*/
|
||||||
|
state: undefined,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {import("@/components/pivot/pivot-view").UserPivot[] | undefined}
|
||||||
|
*/
|
||||||
|
pivots: undefined,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Set<string> | undefined} nameClass 的集合,代表所有需要打开的 treeview 中的 scope
|
||||||
|
*/
|
||||||
|
treeviewExpands: undefined,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {number} 右侧的面版的打开的编号,-1 代表没有面板被打开
|
||||||
|
*/
|
||||||
|
rightNavIndex: -1
|
||||||
};
|
};
|
||||||
|
|
||||||
async function findRecoverFile(recoverJsonPath) {
|
async function findRecoverFile(recoverJsonPath) {
|
||||||
@ -33,12 +57,30 @@ async function findRecoverFile(recoverJsonPath) {
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {string} inputFile 读取的 vcd 文件的路径
|
* @param {string} inputFile 读取的 vcd 文件的路径
|
||||||
|
* @param {string} inputViewFile 读取的 vcd view 文件的路径,它如果不存在则自动创建
|
||||||
*/
|
*/
|
||||||
export async function recoverFromInputFile(inputFile) {
|
async function attemptRecover(inputFile, inputViewFile) {
|
||||||
globalLookup.originFile = inputFile;
|
let recoverResult = await findRecoverFile(inputViewFile);
|
||||||
const recoverJsonPath = inputFile + '.view';
|
if (recoverResult !== undefined) {
|
||||||
const recoverJson = await findRecoverFile(recoverJsonPath);
|
globalLookup.originVcdViewFile = inputViewFile;
|
||||||
|
return recoverResult;
|
||||||
|
}
|
||||||
|
globalLookup.originVcdViewFile = inputFile + '.view';
|
||||||
|
return await findRecoverFile(globalLookup.originVcdViewFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {string} inputFile 读取的 vcd 文件的路径
|
||||||
|
* @param {string} inputViewFile 读取的 vcd view 文件的路径,它如果不存在则自动创建
|
||||||
|
*/
|
||||||
|
export async function recoverFromInputFile(inputFile, inputViewFile) {
|
||||||
|
globalLookup.originVcdFile = inputFile;
|
||||||
|
// 先尝试从 inputViewFile 中寻找,如果找不到,则从同名的 .view 中寻找
|
||||||
|
const recoverJson = await attemptRecover(inputFile, inputViewFile);
|
||||||
|
|
||||||
if (recoverJson) {
|
if (recoverJson) {
|
||||||
|
// 加载 waves
|
||||||
const waves = recoverJson.waves;
|
const waves = recoverJson.waves;
|
||||||
if (waves instanceof Array && waves.length > 0) {
|
if (waves instanceof Array && waves.length > 0) {
|
||||||
for (const wave of waves) {
|
for (const wave of waves) {
|
||||||
@ -51,10 +93,32 @@ export async function recoverFromInputFile(inputFile) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 加载 views
|
||||||
const views = recoverJson.views;
|
const views = recoverJson.views;
|
||||||
if (views instanceof Array && views.length > 0) {
|
if (views instanceof Array && views.length > 0) {
|
||||||
recoverConfig.views = recoverJson.views;
|
recoverConfig.views = recoverJson.views;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 加载 state
|
||||||
|
recoverConfig.state = recoverJson.state;
|
||||||
|
|
||||||
|
// 加载 pivots
|
||||||
|
const pivots = recoverJson.pivots;
|
||||||
|
if (pivots instanceof Array && pivots.length > 0) {
|
||||||
|
recoverConfig.pivots = pivots;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载 treeviewExpands
|
||||||
|
const treeviewExpands = recoverJson.treeviewExpands;
|
||||||
|
if (treeviewExpands instanceof Array && treeviewExpands.length > 0) {
|
||||||
|
recoverConfig.treeviewExpands = treeviewExpands;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载 rightNavIndex
|
||||||
|
const rightNavIndex = recoverJson.rightNavIndex;
|
||||||
|
if (rightNavIndex !== undefined) {
|
||||||
|
recoverConfig.rightNavIndex = rightNavIndex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,15 +133,12 @@ class PrefixNode {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 恢复现场的函数,主要恢复两个变量 currentWiresRenderView 和 currentSignalRenderOptions
|
* @description 恢复现场的函数,主要恢复两个变量 currentWiresRenderView 和 currentSignalRenderOptions
|
||||||
* @param {TopModWireItem[]} topModules
|
* @param {ScopeItem[]} topModules
|
||||||
*/
|
*/
|
||||||
export function recoverSession(topModules) {
|
export function recoverSession(topModules) {
|
||||||
const waves = recoverConfig.waves;
|
// 恢复 waves
|
||||||
if (waves.size === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 利用前缀树 记忆化搜索
|
// 利用前缀树 记忆化搜索
|
||||||
|
const waves = recoverConfig.waves;
|
||||||
for (const [name, option] of waves.entries()) {
|
for (const [name, option] of waves.entries()) {
|
||||||
const result = names2wire(topModules, name);
|
const result = names2wire(topModules, name);
|
||||||
if (result) {
|
if (result) {
|
||||||
@ -90,6 +151,7 @@ export function recoverSession(topModules) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 恢复 views
|
||||||
// 如果存在 views
|
// 如果存在 views
|
||||||
if (recoverConfig.views !== undefined) {
|
if (recoverConfig.views !== undefined) {
|
||||||
const renderViews = globalLookup.currentWiresRenderView;
|
const renderViews = globalLookup.currentWiresRenderView;
|
||||||
@ -120,12 +182,57 @@ export function recoverSession(topModules) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 恢复 state
|
||||||
|
const pstate = recoverConfig.state;
|
||||||
|
if (pstate !== undefined) {
|
||||||
|
Object.assign(globalLookup.pstate, pstate);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 恢复 pivots
|
||||||
|
const pivots = recoverConfig.pivots;
|
||||||
|
if (pivots !== undefined && pivots instanceof Array) {
|
||||||
|
for (const pivot of pivots) {
|
||||||
|
const time = pivot.time;
|
||||||
|
const id = pivot.id;
|
||||||
|
UserPivots.set(time, pivot);
|
||||||
|
Id2Pivot.set(id, pivot);
|
||||||
|
orderedTimes.push(time);
|
||||||
|
}
|
||||||
|
orderedTimes.sort((a, b) => a < b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 恢复 treeviewExpands
|
||||||
|
const treeviewExpands = recoverConfig.treeviewExpands;
|
||||||
|
if (treeviewExpands !== undefined && treeviewExpands instanceof Array) {
|
||||||
|
for (const nameClass of treeviewExpands) {
|
||||||
|
const scope = names2wire(topModules, nameClass);
|
||||||
|
if (scope !== undefined) {
|
||||||
|
TreeviewExpandSignals.add(scope);
|
||||||
|
} else {
|
||||||
|
console.log('fail to recover [treeviewExpands] item: ' + nameClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 恢复 rightNavIndex
|
||||||
|
const rightNavIndex = recoverConfig.rightNavIndex;
|
||||||
|
if (rightNavIndex !== undefined) {
|
||||||
|
controlPanel.currentIndex = rightNavIndex;
|
||||||
|
}
|
||||||
|
|
||||||
globalLookup.render();
|
globalLookup.render();
|
||||||
|
|
||||||
|
recoverConfig.pivots = null;
|
||||||
|
recoverConfig.state = null;
|
||||||
|
recoverConfig.views = null;
|
||||||
|
recoverConfig.treeviewExpands = null;
|
||||||
|
recoverConfig.waves = null;
|
||||||
|
recoverConfig.rightNavIndex = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {TopModWireItem[]} topModules
|
* @param {ScopeItem[]} topModules
|
||||||
* @param {string} name
|
* @param {string} name
|
||||||
* @returns {WireItem | undefined}
|
* @returns {WireItem | undefined}
|
||||||
*/
|
*/
|
||||||
@ -144,13 +251,17 @@ function names2wire(topModules, name) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isScope(targetNode)) {
|
// 搜索完成直接返回
|
||||||
nodes = targetNode.body;
|
if (layer === deps.length - 1) {
|
||||||
} else if (isVariable(targetNode)) {
|
|
||||||
result = targetNode;
|
result = targetNode;
|
||||||
if (result.parent === undefined) {
|
if (result.parent === undefined) {
|
||||||
result.parent = lastNode;
|
result.parent = lastNode;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isScope(targetNode)) {
|
||||||
|
nodes = targetNode.body;
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -173,6 +284,10 @@ function link2names(link) {
|
|||||||
const names = [];
|
const names = [];
|
||||||
const signal = globalLookup.link2CurrentWires.get(link);
|
const signal = globalLookup.link2CurrentWires.get(link);
|
||||||
if (signal) {
|
if (signal) {
|
||||||
|
if (signal.nameClass !== undefined) {
|
||||||
|
return signal.nameClass;
|
||||||
|
}
|
||||||
|
|
||||||
let node = signal;
|
let node = signal;
|
||||||
while (node !== undefined) {
|
while (node !== undefined) {
|
||||||
names.push(node.name);
|
names.push(node.name);
|
||||||
@ -195,16 +310,64 @@ function stableClone(obj) {
|
|||||||
return JSON.parse(JSON.stringify(obj));
|
return JSON.parse(JSON.stringify(obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function makeSaveViewPayload() {
|
|
||||||
const waves = [];
|
|
||||||
const views = [];
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} SavePayloadConfig
|
||||||
|
* @property {boolean} waves
|
||||||
|
* @property {boolean} views
|
||||||
|
* @property {boolean} state
|
||||||
|
* @property {boolean} pivots
|
||||||
|
* @property {boolean} treeviewExpands
|
||||||
|
* @property {boolean} rightNavIndex
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {SavePayloadConfig} config
|
||||||
|
* @param {keyof SavePayloadConfig} property
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function defaultFalseWrapper(config, property) {
|
||||||
|
if (config === undefined) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config[property] === undefined) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Boolean(config[property]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {SavePayloadConfig | undefined} config
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function makeSaveViewPayload(config) {
|
||||||
|
const payload = {};
|
||||||
|
config = config || {};
|
||||||
|
|
||||||
|
// 波形 profile
|
||||||
|
if (defaultFalseWrapper(config, 'waves')) {
|
||||||
|
// 有 waves 也一定要有 views
|
||||||
|
config.views = true;
|
||||||
|
|
||||||
|
const waves = [];
|
||||||
for (const wire of globalLookup.currentWires) {
|
for (const wire of globalLookup.currentWires) {
|
||||||
const option = globalLookup.currentSignalRenderOptions.get(wire.link) || {};
|
const option = globalLookup.currentSignalRenderOptions.get(wire.link) || {};
|
||||||
const name = link2names(wire.link);
|
const name = link2names(wire.link);
|
||||||
option.highlight = 0;
|
option.highlight = 0;
|
||||||
waves.push({ name, option });
|
waves.push({ name, option });
|
||||||
}
|
}
|
||||||
|
Object.assign(payload, { waves });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 视图列表
|
||||||
|
if (defaultFalseWrapper(config, 'views')) {
|
||||||
|
const views = [];
|
||||||
for (const view of globalLookup.currentWiresRenderView) {
|
for (const view of globalLookup.currentWiresRenderView) {
|
||||||
// link 转换成 names
|
// link 转换成 names
|
||||||
const saveView = stableClone(view);
|
const saveView = stableClone(view);
|
||||||
@ -220,5 +383,42 @@ export function makeSaveViewPayload() {
|
|||||||
}
|
}
|
||||||
views.push(saveView);
|
views.push(saveView);
|
||||||
}
|
}
|
||||||
return { waves, views };
|
Object.assign(payload, { views });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 窗口状态
|
||||||
|
if (defaultFalseWrapper(config, 'state')) {
|
||||||
|
const state = globalLookup.pstate;
|
||||||
|
Object.assign(payload, { state });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 信标相关
|
||||||
|
if (defaultFalseWrapper(config, 'pivots')) {
|
||||||
|
const pivots = [];
|
||||||
|
for (const pivot of UserPivots.values()) {
|
||||||
|
pivots.push(pivot);
|
||||||
|
}
|
||||||
|
Object.assign(payload, { pivots });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 右侧信号展开信息
|
||||||
|
if (defaultFalseWrapper(config, 'treeviewExpands')) {
|
||||||
|
const treeviewExpands = [];
|
||||||
|
for (const signal of TreeviewExpandSignals) {
|
||||||
|
treeviewExpands.push(signal.nameClass);
|
||||||
|
}
|
||||||
|
Object.assign(payload, { treeviewExpands });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 右侧控制面板打开序号
|
||||||
|
if (defaultFalseWrapper(config, 'rightNavIndex')) {
|
||||||
|
const rightNavIndex = controlPanel.currentIndex;
|
||||||
|
Object.assign(payload, { rightNavIndex });
|
||||||
|
}
|
||||||
|
|
||||||
|
return payload;
|
||||||
}
|
}
|
@ -46,7 +46,8 @@
|
|||||||
* @property {string} link
|
* @property {string} link
|
||||||
* @property {string} name
|
* @property {string} name
|
||||||
* @property {number} size
|
* @property {number} size
|
||||||
* @property {WireItem | undefined} parent
|
* @property {string} nameClass
|
||||||
|
* @property {ScopeItem | undefined} parent
|
||||||
* @property {string} type
|
* @property {string} type
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -60,14 +61,16 @@
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description
|
* @description Scope
|
||||||
* @typedef {Object} TopModWireItem
|
* @typedef {Object} ScopeItem
|
||||||
* @property {string} kind
|
* @property {string} kind
|
||||||
* @property {string} link
|
* @property {string} link
|
||||||
* @property {string} name
|
* @property {string} name
|
||||||
* @property {number} size
|
* @property {number} size
|
||||||
* @property {string} type
|
* @property {string} type
|
||||||
* @property {(TopModWireItem | WireItem)[]} body
|
* @property {string} nameClass
|
||||||
|
* @property {ScopeItem | undefined} parent
|
||||||
|
* @property {(ScopeItem | WireItem)[]} body
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
x
Reference in New Issue
Block a user