simulator/src/testbench/testbenchInput.js
import CircuitElement from '../circuitElement';
import simulationArea from '../simulationArea';
import { correctWidth, lineTo, moveTo, fillText } from '../canvasApi';
import Node, { findNode } from '../node';
import plotArea from '../plotArea';
/**
* TestBench Input has a node for it's clock input.
* this.testData - the data of all test cases.
* Every testbench has a uniq identifier.
* @class
* @extends CircuitElement
* @param {number} x - the x coord of TB
* @param {number} y - the y coord of TB
* @param {Scope=} scope - the circuit on which TB is drawn
* @param {string} dir - direction
* @param {string} identifier - id to identify tests
* @param {JSON=} testData - input, output and number of tests
* @category testbench
*/
export default class TB_Input extends CircuitElement {
constructor(x, y, scope = globalScope, dir = 'RIGHT', identifier, testData) {
super(x, y, scope, dir, 1);
this.objectType = 'TB_Input';
this.scope.TB_Input.push(this);
this.setIdentifier(identifier || 'Test1');
this.testData = testData || { inputs: [], outputs: [], n: 0 };
this.clockInp = new Node(0, 20, 0, this, 1);
this.outputs = [];
this.running = false; // if tests are undergo
this.iteration = 0;
this.setup();
}
/**
* @memberof TB_Input
* Takes iput when double clicked. For help on generation of input refer to TB_Input.helplink
*/
dblclick() {
this.testData = JSON.parse(prompt('Enter TestBench Json'));
this.setup();
}
setDimensions() {
this.leftDimensionX = 0;
this.rightDimensionX = 120;
this.upDimensionY = 0;
this.downDimensionY = 40 + this.testData.inputs.length * 20;
}
/**
* @memberof TB_Input
* setups the Test by parsing through the testbench data.
*/
setup() {
this.iteration = 0;
this.running = false;
this.nodeList.clean(this.clockInp);
this.deleteNodes();
this.nodeList = [];
this.nodeList.push(this.clockInp);
this.testData = this.testData || { inputs: [], outputs: [], n: 0 };
// this.clockInp = new Node(0,20, 0,this,1);
this.setDimensions();
this.prevClockState = 0;
this.outputs = [];
for (var i = 0; i < this.testData.inputs.length; i++) {
this.outputs.push(new Node(this.rightDimensionX, 30 + i * 20, 1, this, this.testData.inputs[i].bitWidth, this.testData.inputs[i].label));
}
for (var i = 0; i < this.scope.TB_Output.length; i++) {
if (this.scope.TB_Output[i].identifier == this.identifier) { this.scope.TB_Output[i].setup(); }
}
}
/**
* @memberof TB_Input
* toggles state by simply negating this.running so that test cases stop
*/
toggleState() {
this.running = !this.running;
this.prevClockState = 0;
}
/**
* @memberof TB_Input
* function to run from test case 0 again
*/
resetIterations() {
this.iteration = 0;
this.prevClockState = 0;
}
/**
* @memberof TB_Input
* function to resolve the testbench input adds
*/
resolve() {
if (this.clockInp.value != this.prevClockState) {
this.prevClockState = this.clockInp.value;
if (this.clockInp.value == 1 && this.running) {
if (this.iteration < this.testData.n) {
this.iteration++;
} else {
this.running = false;
}
}
}
if (this.running && this.iteration) {
for (var i = 0; i < this.testData.inputs.length; i++) {
this.outputs[i].value = parseInt(this.testData.inputs[i].values[this.iteration - 1], 2);
simulationArea.simulationQueue.add(this.outputs[i]);
}
}
}
/**
* @memberof TB_Input
* was a function to plot values incase any flag used as output to this element
*/
setPlotValue() {
return;
var time = plotArea.stopWatch.ElapsedMilliseconds;
if (this.plotValues.length && this.plotValues[this.plotValues.length - 1][0] == time) { this.plotValues.pop(); }
if (this.plotValues.length == 0) {
this.plotValues.push([time, this.inp1.value]);
return;
}
if (this.plotValues[this.plotValues.length - 1][1] == this.inp1.value) { return; }
this.plotValues.push([time, this.inp1.value]);
}
customSave() {
var data = {
constructorParamaters: [this.direction, this.identifier, this.testData],
nodes: {
outputs: this.outputs.map(findNode),
clockInp: findNode(this.clockInp),
},
};
return data;
}
/**
* This function is used to set a uniq identifier to every testbench
* @memberof TB_Input
*/
setIdentifier(id = '') {
if (id.length == 0 || id == this.identifier) return;
for (var i = 0; i < this.scope.TB_Output.length; i++) {
this.scope.TB_Output[i].checkPairing();
}
for (var i = 0; i < this.scope.TB_Output.length; i++) {
if (this.scope.TB_Output[i].identifier == this.identifier) { this.scope.TB_Output[i].identifier = id; }
}
this.identifier = id;
this.checkPaired();
}
/**
* Check if there is a output tester paired with input TB.
* @memberof TB_Input
*/
checkPaired() {
for (var i = 0; i < this.scope.TB_Output.length; i++) {
if (this.scope.TB_Output[i].identifier == this.identifier) { this.scope.TB_Output[i].checkPairing(); }
}
}
delete() {
super.delete();
this.checkPaired();
}
customDraw() {
var ctx = simulationArea.context;
ctx.beginPath();
ctx.strokeStyle = 'grey';
ctx.fillStyle = '#fcfcfc';
ctx.lineWidth = correctWidth(1);
var xx = this.x;
var yy = this.y;
var xRotate = 0;
var yRotate = 0;
if (this.direction == 'LEFT') {
xRotate = 0;
yRotate = 0;
} else if (this.direction == 'RIGHT') {
xRotate = 120 - this.xSize;
yRotate = 0;
} else if (this.direction == 'UP') {
xRotate = 60 - this.xSize / 2;
yRotate = -20;
} else {
xRotate = 60 - this.xSize / 2;
yRotate = 20;
}
ctx.beginPath();
ctx.textAlign = 'center';
ctx.fillStyle = 'black';
fillText(ctx, `${this.identifier} [INPUT]`, xx + this.rightDimensionX / 2, yy + 14, 10);
fillText(ctx, ['Not Running', 'Running'][+this.running], xx + this.rightDimensionX / 2, yy + 14 + 10 + 20 * this.testData.inputs.length, 10);
fillText(ctx, `Case: ${this.iteration}`, xx + this.rightDimensionX / 2, yy + 14 + 20 + 20 * this.testData.inputs.length, 10);
// fillText(ctx, "Case: "+this.iteration, xx , yy + 20+14, 10);
ctx.fill();
ctx.font = '30px Raleway';
ctx.textAlign = 'right';
ctx.fillStyle = 'blue';
ctx.beginPath();
for (var i = 0; i < this.testData.inputs.length; i++) {
// ctx.beginPath();
fillText(ctx, this.testData.inputs[i].label, this.rightDimensionX - 5 + xx, 30 + i * 20 + yy + 4, 10);
}
ctx.fill();
if (this.running && this.iteration) {
ctx.font = '30px Raleway';
ctx.textAlign = 'left';
ctx.fillStyle = 'blue';
ctx.beginPath();
for (var i = 0; i < this.testData.inputs.length; i++) {
fillText(ctx, this.testData.inputs[i].values[this.iteration - 1], 5 + xx, 30 + i * 20 + yy + 4, 10);
}
ctx.fill();
}
ctx.beginPath();
ctx.strokeStyle = ('rgba(0,0,0,1)');
ctx.lineWidth = correctWidth(3);
var xx = this.x;
var yy = this.y;
// rect(ctx, xx - 20, yy - 20, 40, 40);
moveTo(ctx, 0, 15, xx, yy, this.direction);
lineTo(ctx, 5, 20, xx, yy, this.direction);
lineTo(ctx, 0, 25, xx, yy, this.direction);
ctx.stroke();
}
}
TB_Input.prototype.tooltipText = 'Test Bench Input Selected';
/**
* @memberof TB_Input
* different algo for drawing center elements
* @category testbench
*/
TB_Input.prototype.centerElement = true;
TB_Input.prototype.helplink = 'https://docs.circuitverse.org/#/chapter7/3testcircuits';
TB_Input.prototype.mutableProperties = {
identifier: {
name: 'TestBench Name:',
type: 'text',
maxlength: '10',
func: 'setIdentifier',
},
iteration: {
name: 'Reset Iterations',
type: 'button',
func: 'resetIterations',
},
toggleState: {
name: 'Toggle State',
type: 'button',
func: 'toggleState',
},
};
TB_Input.prototype.objectType = 'TB_Input';