优化保存恢复功能
This commit is contained in:
parent
f7ac311563
commit
08148209e7
@ -17,14 +17,16 @@
|
||||
|
||||
<script>
|
||||
window.readVcdFile = async () => {
|
||||
let inputFile = 'test.vcd';
|
||||
const response = await fetch(inputFile);
|
||||
let inputVcdFile = 'test.vcd';
|
||||
let inputViewFile = 'test.vcd.view';
|
||||
|
||||
const response = await fetch(inputVcdFile);
|
||||
const blob = await response.blob();
|
||||
const reader = new FileReader();
|
||||
return new Promise((resolve, reject) => {
|
||||
reader.onload = event => {
|
||||
const arrayBuffer = event.target.result;
|
||||
resolve([arrayBuffer, inputFile]);
|
||||
resolve([arrayBuffer, inputVcdFile, inputViewFile]);
|
||||
};
|
||||
reader.readAsArrayBuffer(blob);
|
||||
});
|
||||
|
Binary file not shown.
@ -86,10 +86,10 @@ onMounted(async () => {
|
||||
}
|
||||
|
||||
// 初始化载入 vcd 文件
|
||||
const [arrayBuffer, inputFile] = await window.readVcdFile();
|
||||
const [arrayBuffer, inputFile, inputViewFile] = await window.readVcdFile();
|
||||
|
||||
// 寻找 inputFile 下同名的文件,载入配置中
|
||||
await recoverFromInputFile(inputFile);
|
||||
await recoverFromInputFile(inputFile, inputViewFile);
|
||||
|
||||
const url = await getCrossOriginWorkerURL(window.workerPath);
|
||||
const worker = new Worker(url, {
|
||||
|
@ -3,9 +3,12 @@ console.log('digital-vcd-viewer mode: ' + mode);
|
||||
|
||||
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';
|
||||
|
||||
export let saveDelay = 1000;
|
||||
|
||||
/**
|
||||
*
|
||||
* @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>
|
||||
import { emitter, globalLookup } from '@/hook/global';
|
||||
import { eventHandler, registerWheelEvent } from '@/hook/wave-view';
|
||||
import { computed, defineComponent, ref, onMounted } from 'vue';
|
||||
import { computed, defineComponent, ref } from 'vue';
|
||||
import { time2cursorX, MovingPivot, cursorX2time } from './cursor';
|
||||
import formatTime from '@/hook/wave-view/format-time';
|
||||
import { getNearestUserPivot, UserPivots } from './pivot-view';
|
||||
import { getNearestUserPivot } from './pivot-view';
|
||||
import { RelativeAxis } from './relative-axis';
|
||||
|
||||
|
||||
|
@ -17,12 +17,12 @@ import { RelativeAxis } from './relative-axis';
|
||||
|
||||
/**
|
||||
* @description 该数据结构描述了所有通过 makePivot 创建的 用户信标
|
||||
* @type {Map<number, UserPivot>} number 为时间
|
||||
* @type {Map<number, UserPivot>} number 为当前信标在数轴中的刻度
|
||||
*/
|
||||
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());
|
||||
|
||||
|
@ -128,7 +128,7 @@ const cursorStyle = computed(() => ({
|
||||
}));
|
||||
|
||||
|
||||
function onEnter() {
|
||||
function onEnter() {
|
||||
MovingPivot.enterUserPivot = true;
|
||||
}
|
||||
|
||||
|
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>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { reactive } from 'vue';
|
||||
<script setup>
|
||||
import { defineComponent, reactive } from 'vue';
|
||||
|
||||
import TreeView from '@/components/treeview';
|
||||
import Setting from '@/components/setting';
|
||||
import About from '@/components/about';
|
||||
import { emitter } from '@/hook/global';
|
||||
import { controlPanel } from './right-nav';
|
||||
|
||||
|
||||
export default {
|
||||
name: 'right-nav',
|
||||
components: {
|
||||
TreeView,
|
||||
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 => {
|
||||
if (controlPanel.currentIndex === index) {
|
||||
controlPanel.currentIndex = -1;
|
||||
} else {
|
||||
controlPanel.currentIndex = index;
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
props,
|
||||
controlPanel
|
||||
};
|
||||
defineComponent({ name: 'right-nav' });
|
||||
const props = defineProps({
|
||||
topModules: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
emitter.on('right-nav', index => {
|
||||
if (controlPanel.currentIndex === index) {
|
||||
controlPanel.currentIndex = -1;
|
||||
} else {
|
||||
controlPanel.currentIndex = index;
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
@ -1,14 +1,10 @@
|
||||
import { globalLookup } from "@/hook/global";
|
||||
import { updateCurrentGroups } from "./manage-group";
|
||||
import { makeSaveViewPayload } from "@/hook/recover";
|
||||
import { saveViewApi } from "@/api";
|
||||
|
||||
export function onUpdate() {
|
||||
globalLookup.render();
|
||||
updateCurrentGroups();
|
||||
// 保存视图
|
||||
// TODO: 优化:仅保存视图
|
||||
const savePath = globalLookup.originFile + '.view';
|
||||
const payload = makeSaveViewPayload();
|
||||
saveViewApi(savePath, payload);
|
||||
saveViewApi({ views: true });
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ import { globalLookup } from "@/hook/global";
|
||||
import { colorPicker, contextmenu, groupcontextmenu, handleGroupContextmenuFromSignalItem } from "./handle-contextmenu";
|
||||
import { reactive, ref } from "vue";
|
||||
import { findViewIndexByLink } from "@/hook/wave-container-view";
|
||||
import { makeSaveViewPayload } from "@/hook/recover";
|
||||
import { saveViewApi } from "@/api";
|
||||
|
||||
export const groupColors = [
|
||||
@ -138,10 +137,7 @@ export function createGroup() {
|
||||
updateCurrentGroups();
|
||||
|
||||
// 保存视图
|
||||
// TODO: 优化:仅保存视图
|
||||
const savePath = globalLookup.originFile + '.view';
|
||||
const payload = makeSaveViewPayload();
|
||||
saveViewApi(savePath, payload);
|
||||
saveViewApi({ views: true });
|
||||
}
|
||||
|
||||
export function cancelGroup() {
|
||||
@ -168,10 +164,7 @@ export function cancelGroup() {
|
||||
updateCurrentGroups();
|
||||
|
||||
// 保存视图
|
||||
// TODO: 优化:仅保存视图
|
||||
const savePath = globalLookup.originFile + '.view';
|
||||
const payload = makeSaveViewPayload();
|
||||
saveViewApi(savePath, payload);
|
||||
saveViewApi({ views: true });
|
||||
}
|
||||
|
||||
|
||||
@ -203,7 +196,5 @@ export function deleteGroup() {
|
||||
updateCurrentGroups();
|
||||
|
||||
// 保存视图
|
||||
const savePath = globalLookup.originFile + '.view';
|
||||
const payload = makeSaveViewPayload();
|
||||
saveViewApi(savePath, payload);
|
||||
saveViewApi({ waves: true });
|
||||
}
|
@ -43,9 +43,8 @@
|
||||
<script setup>
|
||||
import { saveViewApi } from '@/api';
|
||||
import { globalLookup } from '@/hook/global';
|
||||
import { makeSaveViewPayload } from '@/hook/recover';
|
||||
import { sidebarSelectedWires } from '@/hook/sidebar-select-wire';
|
||||
import { defineComponent, nextTick, ref, watch } from 'vue';
|
||||
import { defineComponent, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
// 负责展示波形形式的模块,分为数字形式、折线模拟形式、阶梯模拟形式
|
||||
@ -71,10 +70,7 @@ function onSignalModalUpdate() {
|
||||
globalLookup.render();
|
||||
|
||||
// 保存
|
||||
// TODO: 优化:只保存 waves
|
||||
const savePath = globalLookup.originFile + '.view';
|
||||
const payload = makeSaveViewPayload();
|
||||
saveViewApi(savePath, payload);
|
||||
saveViewApi({ waves: true });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,9 +26,8 @@
|
||||
<script setup>
|
||||
import { saveViewApi } from '@/api';
|
||||
import { globalLookup } from '@/hook/global';
|
||||
import { makeSaveViewPayload } from '@/hook/recover';
|
||||
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';
|
||||
|
||||
// 负责展示波形 数值的 类型,分为 二进制、十六进制、八进制、十进制、浮点数(半精度、单精度、双精度)
|
||||
@ -110,10 +109,7 @@ function onChange() {
|
||||
globalLookup.render({ type: 'value' });
|
||||
|
||||
// 保存
|
||||
// TODO: 优化:只保存 waves
|
||||
const savePath = globalLookup.originFile + '.view';
|
||||
const payload = makeSaveViewPayload();
|
||||
saveViewApi(savePath, payload);
|
||||
saveViewApi({ waves: true });
|
||||
}
|
||||
}
|
||||
|
||||
|
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,59 +22,68 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
/* eslint-disable */
|
||||
import { reactive, onMounted } from 'vue';
|
||||
import { reactive, defineComponent } from 'vue';
|
||||
import { emitter, globalLookup } from '@/hook/global';
|
||||
import { isScope, isVariable, makeIconClass } from '@/hook/utils';
|
||||
import { TreeviewExpandSignals } from './modules';
|
||||
import { saveViewApi } from '@/api';
|
||||
|
||||
export default {
|
||||
name: "modules",
|
||||
props: {
|
||||
module: Object
|
||||
},
|
||||
setup(props) {
|
||||
const module = props.module;
|
||||
globalLookup.initcurrentModule(module);
|
||||
|
||||
const signals = [];
|
||||
const mods = [];
|
||||
|
||||
for (const wire of module.body) {
|
||||
if (isScope(wire)) {
|
||||
mods.push(wire);
|
||||
} else if (isVariable(wire)) {
|
||||
signals.push(wire);
|
||||
}
|
||||
}
|
||||
|
||||
function clickItem() {
|
||||
emitter.emit('tree-view', signals);
|
||||
// color change into selected
|
||||
globalLookup.currentModule = module;
|
||||
}
|
||||
|
||||
const expandManage = reactive({
|
||||
expanded: false,
|
||||
expandTagClass: mods.length === 0 ? '' : 'collapse-tag',
|
||||
click() {
|
||||
this.expanded = !this.expanded;
|
||||
if (this.expandTagClass) {
|
||||
this.expandTagClass = this.expanded ? 'expand-tag' : 'collapse-tag';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
module,
|
||||
mods,
|
||||
clickItem,
|
||||
expandManage,
|
||||
globalLookup,
|
||||
makeIconClass
|
||||
}
|
||||
defineComponent({ name: 'modules' });
|
||||
const props = defineProps({
|
||||
module: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
const module = props.module;
|
||||
globalLookup.initcurrentModule(module);
|
||||
|
||||
const signals = [];
|
||||
const mods = [];
|
||||
|
||||
for (const wire of module.body) {
|
||||
if (isScope(wire)) {
|
||||
mods.push(wire);
|
||||
} else if (isVariable(wire)) {
|
||||
signals.push(wire);
|
||||
}
|
||||
}
|
||||
|
||||
function clickItem() {
|
||||
emitter.emit('tree-view', signals);
|
||||
// color change into selected
|
||||
globalLookup.currentModule = module;
|
||||
}
|
||||
|
||||
function getExpandStatus() {
|
||||
if (mods.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return TreeviewExpandSignals.has(module);
|
||||
}
|
||||
|
||||
const expandManage = reactive({
|
||||
expanded: getExpandStatus(),
|
||||
expandTagClass: mods.length === 0 ? '' : 'collapse-tag',
|
||||
click() {
|
||||
this.expanded = !this.expanded;
|
||||
if (this.expandTagClass) {
|
||||
this.expandTagClass = this.expanded ? 'expand-tag' : 'collapse-tag';
|
||||
}
|
||||
if (this.expanded) {
|
||||
TreeviewExpandSignals.add(module);
|
||||
} else {
|
||||
TreeviewExpandSignals.delete(module);
|
||||
}
|
||||
|
||||
// 保存
|
||||
saveViewApi({ treeviewExpands: true });
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
@ -28,7 +28,6 @@ import { makeIconClass } from '@/hook/utils';
|
||||
import { WaveContainerView } from '@/hook/wave-container-view';
|
||||
import { controller } from './signals';
|
||||
import { saveViewApi } from '@/api';
|
||||
import { makeSaveViewPayload } from '@/hook/recover';
|
||||
|
||||
defineComponent({ name: 'signals' });
|
||||
|
||||
@ -91,9 +90,7 @@ function toggleRender(event, signal) {
|
||||
}
|
||||
|
||||
// 保存
|
||||
const savePath = globalLookup.originFile + '.view';
|
||||
const payload = makeSaveViewPayload();
|
||||
saveViewApi(savePath, payload);
|
||||
saveViewApi({ waves: true });
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -13,11 +13,16 @@ export const globalLookup = reactive({
|
||||
/**
|
||||
* @description 读取的文件的路径
|
||||
*/
|
||||
originFile: '',
|
||||
originVcdFile: '',
|
||||
|
||||
/**
|
||||
* @description 读取文件的 view 路径
|
||||
*/
|
||||
originVcdViewFile: '',
|
||||
|
||||
/**
|
||||
* @description 所有的顶层文件
|
||||
* @type {TopModWireItem[]}
|
||||
* @type {ScopeItem[]}
|
||||
*/
|
||||
topModules: [],
|
||||
|
||||
@ -112,16 +117,23 @@ export const globalLookup = reactive({
|
||||
|
||||
xScale: 1,
|
||||
|
||||
/**
|
||||
* @description 初始化 module,为每一个 module 配上 parent 并给出对应的 nameClass
|
||||
* @param {ScopeItem} module
|
||||
*/
|
||||
initcurrentModule(module) {
|
||||
if (this.currentModule === undefined && module) {
|
||||
this.currentModule = module;
|
||||
}
|
||||
if (module.nameClass === undefined) {
|
||||
module.nameClass = module.name;
|
||||
}
|
||||
// 创造 parent,当然,这一步也可能在 recoverSession 中被实现了
|
||||
if (module.body && module.body instanceof Array) {
|
||||
for (const childModule of module.body) {
|
||||
if (childModule.parent === undefined) {
|
||||
childModule.parent = module;
|
||||
}
|
||||
// 计算 nameClass
|
||||
childModule.parent = module;
|
||||
childModule.nameClass = module.nameClass + '.' + childModule.name;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -3,16 +3,40 @@ import { globalLookup } from "./global";
|
||||
import { WaveContainerView } from "./wave-container-view";
|
||||
import { isScope, isVariable } from "./utils";
|
||||
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 = {
|
||||
/**
|
||||
* @type {Map<string, IRenderOption>} 其中的 string 是形如 `cpu.alu.add1` 这样的字符串
|
||||
*/
|
||||
waves: new Map(),
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
@ -33,12 +57,30 @@ async function findRecoverFile(recoverJsonPath) {
|
||||
/**
|
||||
*
|
||||
* @param {string} inputFile 读取的 vcd 文件的路径
|
||||
* @param {string} inputViewFile 读取的 vcd view 文件的路径,它如果不存在则自动创建
|
||||
*/
|
||||
export async function recoverFromInputFile(inputFile) {
|
||||
globalLookup.originFile = inputFile;
|
||||
const recoverJsonPath = inputFile + '.view';
|
||||
const recoverJson = await findRecoverFile(recoverJsonPath);
|
||||
async function attemptRecover(inputFile, inputViewFile) {
|
||||
let recoverResult = await findRecoverFile(inputViewFile);
|
||||
if (recoverResult !== undefined) {
|
||||
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) {
|
||||
// 加载 waves
|
||||
const waves = recoverJson.waves;
|
||||
if (waves instanceof Array && waves.length > 0) {
|
||||
for (const wave of waves) {
|
||||
@ -51,10 +93,32 @@ export async function recoverFromInputFile(inputFile) {
|
||||
}
|
||||
}
|
||||
|
||||
// 加载 views
|
||||
const views = recoverJson.views;
|
||||
if (views instanceof Array && views.length > 0) {
|
||||
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
|
||||
* @param {TopModWireItem[]} topModules
|
||||
* @param {ScopeItem[]} topModules
|
||||
*/
|
||||
export function recoverSession(topModules) {
|
||||
const waves = recoverConfig.waves;
|
||||
if (waves.size === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 恢复 waves
|
||||
// 利用前缀树 记忆化搜索
|
||||
const waves = recoverConfig.waves;
|
||||
for (const [name, option] of waves.entries()) {
|
||||
const result = names2wire(topModules, name);
|
||||
if (result) {
|
||||
@ -90,6 +151,7 @@ export function recoverSession(topModules) {
|
||||
}
|
||||
}
|
||||
|
||||
// 恢复 views
|
||||
// 如果存在 views
|
||||
if (recoverConfig.views !== undefined) {
|
||||
const renderViews = globalLookup.currentWiresRenderView;
|
||||
@ -120,16 +182,61 @@ 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();
|
||||
|
||||
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
|
||||
* @returns {WireItem | undefined}
|
||||
*/
|
||||
function names2wire(topModules, name) {
|
||||
function names2wire(topModules, name) {
|
||||
const deps = name.split('.');
|
||||
let layer = 0;
|
||||
let nodes = topModules;
|
||||
@ -144,13 +251,17 @@ function names2wire(topModules, name) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (isScope(targetNode)) {
|
||||
nodes = targetNode.body;
|
||||
} else if (isVariable(targetNode)) {
|
||||
// 搜索完成直接返回
|
||||
if (layer === deps.length - 1) {
|
||||
result = targetNode;
|
||||
if (result.parent === undefined) {
|
||||
result.parent = lastNode;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (isScope(targetNode)) {
|
||||
nodes = targetNode.body;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
@ -173,6 +284,10 @@ function link2names(link) {
|
||||
const names = [];
|
||||
const signal = globalLookup.link2CurrentWires.get(link);
|
||||
if (signal) {
|
||||
if (signal.nameClass !== undefined) {
|
||||
return signal.nameClass;
|
||||
}
|
||||
|
||||
let node = signal;
|
||||
while (node !== undefined) {
|
||||
names.push(node.name);
|
||||
@ -195,30 +310,115 @@ function stableClone(obj) {
|
||||
return JSON.parse(JSON.stringify(obj));
|
||||
}
|
||||
|
||||
export function makeSaveViewPayload() {
|
||||
const waves = [];
|
||||
const views = [];
|
||||
|
||||
for (const wire of globalLookup.currentWires) {
|
||||
const option = globalLookup.currentSignalRenderOptions.get(wire.link) || {};
|
||||
const name = link2names(wire.link);
|
||||
option.highlight = 0;
|
||||
waves.push({ name, option });
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
for (const view of globalLookup.currentWiresRenderView) {
|
||||
// link 转换成 names
|
||||
const saveView = stableClone(view);
|
||||
const viewLink = view.signalInfo.link;
|
||||
saveView.signalInfo.link = link2names(viewLink);
|
||||
const parentLink = saveView.signalInfo.parentLink;
|
||||
if (parentLink !== undefined) {
|
||||
saveView.signalInfo.parentLink = parentLink.endsWith('-group') ? link2names(parentLink.slice(0, -6)) + '-group' : viewLink;
|
||||
|
||||
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) {
|
||||
const option = globalLookup.currentSignalRenderOptions.get(wire.link) || {};
|
||||
const name = link2names(wire.link);
|
||||
option.highlight = 0;
|
||||
waves.push({ name, option });
|
||||
}
|
||||
// children 只会有一层
|
||||
for (const child of saveView.children) {
|
||||
child.signalInfo.link = link2names(child.signalInfo.link);
|
||||
Object.assign(payload, { waves });
|
||||
}
|
||||
|
||||
|
||||
// 视图列表
|
||||
if (defaultFalseWrapper(config, 'views')) {
|
||||
const views = [];
|
||||
for (const view of globalLookup.currentWiresRenderView) {
|
||||
// link 转换成 names
|
||||
const saveView = stableClone(view);
|
||||
const viewLink = view.signalInfo.link;
|
||||
saveView.signalInfo.link = link2names(viewLink);
|
||||
const parentLink = saveView.signalInfo.parentLink;
|
||||
if (parentLink !== undefined) {
|
||||
saveView.signalInfo.parentLink = parentLink.endsWith('-group') ? link2names(parentLink.slice(0, -6)) + '-group' : viewLink;
|
||||
}
|
||||
// children 只会有一层
|
||||
for (const child of saveView.children) {
|
||||
child.signalInfo.link = link2names(child.signalInfo.link);
|
||||
}
|
||||
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} name
|
||||
* @property {number} size
|
||||
* @property {WireItem | undefined} parent
|
||||
* @property {string} nameClass
|
||||
* @property {ScopeItem | undefined} parent
|
||||
* @property {string} type
|
||||
*/
|
||||
|
||||
@ -60,14 +61,16 @@
|
||||
|
||||
|
||||
/**
|
||||
* @description
|
||||
* @typedef {Object} TopModWireItem
|
||||
* @description Scope
|
||||
* @typedef {Object} ScopeItem
|
||||
* @property {string} kind
|
||||
* @property {string} link
|
||||
* @property {string} name
|
||||
* @property {number} size
|
||||
* @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