diff --git a/src/simulator/src/data/load.ts b/src/simulator/src/data/load.ts new file mode 100644 index 00000000..0d7a096d --- /dev/null +++ b/src/simulator/src/data/load.ts @@ -0,0 +1,296 @@ +import { resetScopeList, newCircuit, switchCircuit } from '../circuit' +import { setProjectName } from './save' +import { + scheduleUpdate, + update, + updateSimulationSet, + updateCanvasSet, + gridUpdateSet, +} from '../engine' +import { updateRestrictedElementsInScope } from '../restrictedElementDiv' +import { simulationArea } from '../simulationArea' + +import { loadSubCircuit } from '../subcircuit' +import { scheduleBackup } from './backupCircuit' +import { showProperties } from '../ux' +import { constructNodeConnections, loadNode, replace } from '../node' +import { generateId } from '../utils' +import modules from '../modules' +import { oppositeDirection } from '../canvasApi' +import plotArea from '../plotArea' +import { TestbenchData } from '#/simulator/src/testbench' +import { SimulatorStore } from '#/store/SimulatorStore/SimulatorStore' +import { toRefs } from 'vue' +import { moduleList } from '../metadata' +import { Scope, ProjectData, VerilogMetadata, Layout,TestbenchData, NodeData, CircuitElement, ModuleData } from '../types/loadScope'; // Import required interfaces + + +/** + * Backward compatibility - needs to be deprecated + * @param {CircuitElement} obj - the object to be rectified + * @category data + */ +function rectifyObjectType(obj: string): string { + const rectify: Record = { + FlipFlop: 'DflipFlop', + Ram: 'Rom', + }; + return rectify[obj] || obj; +} + +/** + * Function to load CircuitElements + * @param {ModuleData} data - JSOn data + * @param {Scope} scope - circuit in which we want to load modules + * @category data + */ +function loadModule(data: ModuleData, scope: Scope): void { + // Create circuit element + const obj = new modules[rectifyObjectType(data.objectType)]( + data.x, + data.y, + scope, + ...(data.customData.constructorParamaters || []) + ); + // Sets directions + obj.label = data.label; + obj.labelDirection = data.labelDirection || oppositeDirection[fixDirection[obj.direction]]; + + // Sets delay + obj.propagationDelay = data.propagationDelay || obj.propagationDelay; + obj.fixDirection(); + + // Restore other values + if (data.customData.values) { + for (const prop in data.customData.values) { + if (Object.prototype.hasOwnProperty.call(data.customData.values, prop)) { + obj[prop] = data.customData.values[prop]; + } + } + } + + // Replace new nodes with the correct old nodes (with connections) + if (data.customData.nodes) { + for (const node in data.customData.nodes) { + if (Object.prototype.hasOwnProperty.call(data.customData.nodes, node)) { + const n = data.customData.nodes[node]; + if (Array.isArray(n)) { + for (let i = 0; i < n.length; i++) { + obj[node][i] = replace(obj[node][i], n[i].id); // Pass the id of NodeConnection instead of the whole object + } + } else { + obj[node] = replace(obj[node], n.id); // Again, pass the id property of NodeConnection + } + + } + } + } + if (data.subcircuitMetadata) obj.subcircuitMetadata = data.subcircuitMetadata; +} + +/** + * This function shouldn't ideally exist. But temporary fix + * for some issues while loading nodes. + * @category data + */ +function removeBugNodes(scope: Scope = globalScope): void { + let x = scope.allNodes.length; + for (let i = 0; i < x; i++) { + if ( + scope.allNodes[i].type !== 2 && + scope.allNodes[i].parent.objectType === 'CircuitElement' + ) { + scope.allNodes[i].delete?.(); //even if delete doesn't exist, it won't cause any errors + } + if (scope.allNodes.length !== x) { + i = 0; + x = scope.allNodes.length; + } + } +} + +/** + * Function to load a full circuit + * @param {Scope} scope + * @param {ProjectData} data + * @category data + */ +export function loadScope(scope: Scope, data: ProjectData): void { + const ML = moduleList.slice(); // Module List copy + scope.restrictedCircuitElementsUsed = data.restrictedCircuitElementsUsed; + + // Load all nodes + data.allNodes.forEach((x) => loadNode(x, scope)); + + // Make all connections + for (let i = 0; i < data.allNodes.length; i++) { + constructNodeConnections(scope.allNodes[i], data.allNodes[i]); + } + + // Load all modules + for (let i = 0; i < ML.length; i++) { + if (data[ML[i]]) { + if (ML[i] === 'SubCircuit') { + // Load subcircuits differently + for (let j = 0; j < data[ML[i]].length; j++) { + loadSubCircuit(data[ML[i]][j], scope); + } + } else { + // Load everything else similarly + for (let j = 0; j < data[ML[i]].length; j++) { + loadModule(data[ML[i]][j], scope); + } + } + } + } + + // Update wires accordingly + scope.wires.forEach((x) => { + x.updateData(scope); + }); + + removeBugNodes(scope); // To be deprecated + + // If Verilog Circuit Metadata exists, then restore + if (data.verilogMetadata) { + scope.verilogMetadata = data.verilogMetadata; + } + + // If Test exists, then restore + if (data.testbenchData) { + globalScope.testbenchData = new TestbenchData( + data.testbenchData.testData, + data.testbenchData.currentGroup, + data.testbenchData.currentCase + ); + } + + // If layout exists, then restore + if (data.layout) { + scope.layout = data.layout; + } else { + // Else generate new layout according to how it would have been otherwise (backward compatibility) + scope.layout = {}; + scope.layout.width = 100; + scope.layout.height = + Math.max(scope.Input.length, scope.Output.length) * 20 + 20; + scope.layout.title_x = 50; + scope.layout.title_y = 13; + for (let i = 0; i < scope.Input.length; i++) { + scope.Input[i].layoutProperties = { + x: 0, + y: + scope.layout.height / 2 - + scope.Input.length * 10 + + 20 * i + + 10, + id: generateId(), + }; + } + for (let i = 0; i < scope.Output.length; i++) { + scope.Output[i].layoutProperties = { + x: scope.layout.width, + y: + scope.layout.height / 2 - + scope.Output.length * 10 + + 20 * i + + 10, + id: generateId(), + }; + } + } + // Backward compatibility + if (scope.layout.titleEnabled === undefined) { + scope.layout.titleEnabled = true; + } +} + +// Function to load project from data +/** + * loads a saved project + * @param {ProjectData} data - the json data of the + * @category data + * @exports load + */ +export default function load(data: ProjectData | null): void { + // If project is new and no data is there, then just set project name + const simulatorStore = SimulatorStore(); + const { circuit_list } = toRefs(simulatorStore); + + if (!data) { + setProjectName(__projectName); + return; + } + + const { projectId } = data; + setProjectName(data.name); + + globalScope = undefined; + resetScopeList(); // Remove default scope + + // Load all according to the dependency order + for (let i = 0; i < data.scopes.length; i++) { + let isVerilogCircuit = false; + let isMainCircuit = false; + if (data.scopes[i].verilogMetadata) { + isVerilogCircuit = data.scopes[i].verilogMetadata.isVerilogCircuit; + isMainCircuit = data.scopes[i].verilogMetadata.isMainCircuit; + } + + // Create new circuit + const scope = newCircuit( + data.scopes[i].name || 'Untitled', + data.scopes[i].id, + isVerilogCircuit, + isMainCircuit + ); + + // Load circuit data + loadScope(scope, data.scopes[i]); + + // Focus circuit + globalScope = scope; + + // Center circuit + if (embed) { + globalScope.centerFocus(true); + } else { + globalScope.centerFocus(false); + } + + // update and backup circuit once + update(globalScope, true); + + // Updating restricted element list initially on loading + updateRestrictedElementsInScope(); + + scheduleBackup(); + } + + // Restore clock + simulationArea.changeClockTime(data.timePeriod || 500); + simulationArea.clockEnabled = + data.clockEnabled === undefined ? true : data.clockEnabled; + + if (!embed) { + showProperties(simulationArea.lastSelected); + } + + // Reorder tabs according to the saved order + if (data.orderedTabs?.length) { + circuit_list.value.sort((a, b) => { + return data.orderedTabs.indexOf(String(a.id)) - data.orderedTabs.indexOf(String(b.id)); + }); + } + + // Switch to last focussedCircuit + if (data.focussedCircuit) switchCircuit(String(data.focussedCircuit)); + + updateSimulationSet(true); + updateCanvasSet(true); + gridUpdateSet(true); + + // Reset Timing + if (!embed) plotArea.reset(); + scheduleUpdate(1); +} diff --git a/src/simulator/src/types/loadScope.ts b/src/simulator/src/types/loadScope.ts new file mode 100644 index 00000000..45b54c30 --- /dev/null +++ b/src/simulator/src/types/loadScope.ts @@ -0,0 +1,107 @@ +// Define a generic interface for circuit elements +export interface CircuitElement { + x: number; + y: number; + label?: string; + labelDirection?: string; + propagationDelay?: number; + fixDirection: () => void; + subcircuitMetadata?: SubCircuitMetadata; + layoutProperties?: { + x: number; + y: number; + id: string; + }; +} + +// Define an interface for custom data of circuit elements +export interface CustomData { + constructorParamaters?: unknown[]; + values?: Record; + nodes?: Record; +} + +// Define an interface for node connections +export interface NodeConnection { + id: number; + scopeId: string; +} + +// Define an interface for module data +export interface ModuleData { + objectType: string; + x: number; + y: number; + label?: string; + labelDirection?: string; + propagationDelay?: number; + customData: CustomData; + subcircuitMetadata?: SubCircuitMetadata; +} + +// Define an interface for subcircuit metadata +export interface SubCircuitMetadata { + [key: string]: unknown; +} + +// Define an interface for node data +interface NodeData extends Node { + id: string; + type: number; + parent: { objectType: string }; + delete?: () => void; +} + +// Define an interface for scope (circuit) +export interface Scope { + id: string; + name: string; + allNodes: NodeData[]; + wires: { updateData: (scope: Scope) => void }[]; + restrictedCircuitElementsUsed?: string[]; + verilogMetadata?: VerilogMetadata; + layout?: Layout; + Input: CircuitElement[]; + Output: CircuitElement[]; + testbenchData?: TestbenchData; + centerFocus(isCentered: boolean): void; +} + +// Define an interface for Verilog metadata +export interface VerilogMetadata { + isVerilogCircuit: boolean; + isMainCircuit: boolean; +} + + + +// Define an interface for layout data +export interface Layout { + width: number; + height: number; + title_x: number; + title_y: number; + titleEnabled?: boolean; +} + +export interface TestbenchData { + testData: any; // Define more specific type if you know the structure + currentGroup: string; + currentCase: string; +} + +// Define an interface for the entire project data +export interface ProjectData { + name: string; + projectId?: string; + scopes: Scope[]; + orderedTabs?: string[]; + focussedCircuit?: string; + timePeriod?: number; + clockEnabled?: boolean; + allNodes: NodeData[]; + restrictedCircuitElementsUsed?: string[]; + verilogMetadata?: VerilogMetadata; + testbenchData?: TestbenchData; + layout?: Layout; +}