优化保存恢复功能

This commit is contained in:
锦恢 2024-10-18 20:25:57 +08:00
parent f7ac311563
commit 08148209e7
19 changed files with 421 additions and 193 deletions

View File

@ -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.

View File

@ -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, {

View File

@ -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);

View File

@ -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';

View File

@ -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 idid 是创建的 Unix 时间戳
*/ */
export const Id2Pivot = reactive(new Map()); export const Id2Pivot = reactive(new Map());

View 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 });
}
});

View File

@ -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 => {
if (controlPanel.currentIndex === index) {
controlPanel.currentIndex = -1;
} else {
controlPanel.currentIndex = index;
}
})
return {
props,
controlPanel
};
} }
} });
emitter.on('right-nav', index => {
if (controlPanel.currentIndex === index) {
controlPanel.currentIndex = -1;
} else {
controlPanel.currentIndex = index;
}
});
</script> </script>
<style> <style>

View File

@ -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);
} }

View File

@ -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);
} }

View File

@ -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);
} }
} }

View File

@ -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);
} }
} }

View File

@ -0,0 +1,5 @@
/**
* @type {Set<ScopeItem>}
*/
export const TreeviewExpandSignals = new Set();

View File

@ -22,59 +22,68 @@
</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';
export default { defineComponent({ name: 'modules' });
name: "modules", const props = defineProps({
props: { module: {
module: Object type: Object,
}, required: true
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
}
} }
}; });
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> </script>
<style> <style>

View File

@ -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>

View File

@ -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;
} }
} }
}, },

View File

@ -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,30 +310,115 @@ function stableClone(obj) {
return JSON.parse(JSON.stringify(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) || {}; * @typedef {Object} SavePayloadConfig
const name = link2names(wire.link); * @property {boolean} waves
option.highlight = 0; * @property {boolean} views
waves.push({ name, option }); * @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 if (config[property] === undefined) {
const saveView = stableClone(view); return false;
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);
} }
return { waves, views };
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 });
}
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);
}
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;
} }

View File

@ -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
*/ */
/** /**