240 lines
7.1 KiB
TypeScript
240 lines
7.1 KiB
TypeScript
// Breadboard component for the LogicSimulator
|
|
// Copyright (C) 2022 Sascha Nitsch
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
interface BreadboardParam extends BaseComponentParam {
|
|
nopower?: boolean;
|
|
onlypower?: boolean;
|
|
}
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
class Breadboard extends BaseComponent {
|
|
private l: string;
|
|
private unitX: number;
|
|
private unitY: number;
|
|
private active: Map<string, TriState>;
|
|
private nopower: boolean;
|
|
private onlypower: boolean;
|
|
private descrlabel: string;
|
|
|
|
constructor(simulator: Simulator, id: string, param: BreadboardParam) {
|
|
simulator.loadCSS('breadboard');
|
|
super(simulator, id, param);
|
|
this.nopower = param.nopower || false;
|
|
this.onlypower = param.onlypower || false;
|
|
this.descrlabel = typeof param.label === 'string' ? param.label : '' || '';
|
|
this.active = new Map<string, TriState>();
|
|
this.l = 'ABCDEFGHIJ';
|
|
let x: number;
|
|
let y: number;
|
|
this.unitX = 2 * 2.54;
|
|
this.unitY = 2 * 2.54;
|
|
if (!param || !param.onlypower) {
|
|
for (x = 0; x < 60; ++x) {
|
|
for (y = 0; y < 10; ++y) {
|
|
this.pins.set(
|
|
this.l[y] + x,
|
|
new TriState(this, (x + 1.5) * this.unitX, this.unitY * (16 - y - (y > 4 ? 2 : 0)), undefined, {
|
|
id: this.l[y] + x,
|
|
})
|
|
);
|
|
}
|
|
}
|
|
}
|
|
y = 12;
|
|
if (!param || !param.nopower) {
|
|
for (x = 0; x < 50; ++x) {
|
|
this.pins.set(
|
|
'pa' + x,
|
|
new TriState(this, this.unitX * (x + 2.5 + Math.floor(x / 5)), 2.5 * this.unitY, undefined, { id: 'pa' + x })
|
|
);
|
|
this.pins.set(
|
|
'ma' + x,
|
|
new TriState(this, this.unitX * (x + 2.5 + Math.floor(x / 5)), 1.5 * this.unitY, undefined, { id: 'ma' + x })
|
|
);
|
|
if (!param || !param.onlypower) {
|
|
this.pins.set(
|
|
'pb' + x,
|
|
new TriState(this, this.unitX * (x + 2.5 + Math.floor(x / 5)), 19.5 * this.unitY, undefined, {
|
|
id: 'pb' + x,
|
|
})
|
|
);
|
|
this.pins.set(
|
|
'mb' + x,
|
|
new TriState(this, this.unitX * (x + 2.5 + Math.floor(x / 5)), 18.5 * this.unitY, undefined, {
|
|
id: 'mb' + x,
|
|
})
|
|
);
|
|
}
|
|
}
|
|
}
|
|
//this.wires = {};
|
|
this.zIndex = 0;
|
|
}
|
|
|
|
setup(parent: SVGElement) {
|
|
super.doSetup('breadboard', parent);
|
|
if (this.element === null) return;
|
|
const g = this.element.childNodes[0];
|
|
const tpl = this.element.querySelectorAll('.pintemplate')[0];
|
|
this.pins.forEach((k) => {
|
|
// const k = this.pins.get(key);
|
|
if (!(k instanceof TriState)) {
|
|
return;
|
|
}
|
|
//console.log(this.state[key].x(),this.state[key].y());
|
|
const node = <HTMLElement>tpl.cloneNode(true);
|
|
node.setAttribute('class', 'pin');
|
|
node.setAttribute('transform', 'translate(' + k.getOffsetX() + ',' + k.getOffsetY() + ')');
|
|
g.appendChild(node);
|
|
});
|
|
if (this.nopower) {
|
|
const power = this.element.querySelectorAll('.power');
|
|
for (let i = 0; i < power.length; ++i) {
|
|
const p = power[i].parentNode;
|
|
if (p !== null) {
|
|
p.removeChild(power[i]);
|
|
}
|
|
}
|
|
}
|
|
if (this.onlypower) {
|
|
let rem = this.element.querySelectorAll('.bottom');
|
|
for (let i = 0; i < rem.length; ++i) {
|
|
const r = rem[i].parentNode;
|
|
if (r !== null) {
|
|
r.removeChild(rem[i]);
|
|
}
|
|
}
|
|
rem = this.element.querySelectorAll('.center');
|
|
for (let i = 0; i < rem.length; ++i) {
|
|
const r = rem[i].parentNode;
|
|
if (r !== null) {
|
|
r.removeChild(rem[i]);
|
|
}
|
|
}
|
|
if (!this.descrlabel) {
|
|
rem = this.element.querySelectorAll('.label');
|
|
for (let i = 0; i < rem.length; ++i) {
|
|
const r = rem[i].parentNode;
|
|
if (r !== null) {
|
|
r.removeChild(rem[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
connected(pin: TriState) {
|
|
this.active.set(pin.getParam().id || '', pin);
|
|
}
|
|
|
|
finalize() {
|
|
let row;
|
|
let pins;
|
|
let col;
|
|
let i;
|
|
const bus = ['pa', 'ma', 'pb', 'mb'];
|
|
for (row = 0; row < 4; ++row) {
|
|
pins = [];
|
|
for (col = 0; col < 50; ++col) {
|
|
const a = this.active.get(bus[row] + col);
|
|
if (a !== undefined) {
|
|
pins.push(a);
|
|
}
|
|
}
|
|
if (pins.length > 1) {
|
|
let sourceId = 0;
|
|
// find names wire
|
|
for (i = 0; i < pins.length; ++i) {
|
|
const pin = pins[i].connectedWire;
|
|
if (pin && pin.autoid === false) {
|
|
sourceId = i;
|
|
break;
|
|
}
|
|
}
|
|
const source = pins[sourceId].connectedWire;
|
|
if (source !== null) {
|
|
for (i = 0; i < pins.length; ++i) {
|
|
if (i === sourceId) continue;
|
|
source.join(pins[i].connectedWire);
|
|
pins[i].connectedWire = null;
|
|
const id = pins[i].getParam().id;
|
|
if (id) {
|
|
this.active.delete(id);
|
|
}
|
|
}
|
|
const id = pins[sourceId].getParam().id;
|
|
if (id) {
|
|
this.active.delete(id);
|
|
}
|
|
// remove ourself
|
|
source.remove(this);
|
|
}
|
|
}
|
|
}
|
|
for (col = 0; col < 60; ++col) {
|
|
for (let start = 0; start < 10; start += 5) {
|
|
pins = [];
|
|
for (row = start; row < start + 5; ++row) {
|
|
const a = this.active.get(this.l[row] + col);
|
|
if (a !== undefined) {
|
|
pins.push(a);
|
|
}
|
|
}
|
|
if (pins.length > 1) {
|
|
for (i = 1; i < pins.length; ++i) {
|
|
if (pins[0].connectedWire) {
|
|
pins[0].connectedWire.join(pins[i].connectedWire);
|
|
pins[i].connectedWire = null;
|
|
const id = pins[i].getParam().id;
|
|
if (id) {
|
|
this.active.delete(id);
|
|
}
|
|
}
|
|
}
|
|
const id = pins[0].getParam().id;
|
|
if (id) {
|
|
this.active.delete(id);
|
|
}
|
|
// remove ourself
|
|
if (pins[0].connectedWire) {
|
|
pins[0].connectedWire.remove(this);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
getPos(column: string): number[] {
|
|
let t;
|
|
if (column[0] >= 'A' && column[0] <= 'J') {
|
|
// exact pin
|
|
t = this.pins.get(column);
|
|
} else {
|
|
t = this.pins.get('E' + column);
|
|
}
|
|
if (t === undefined) return [0, 0];
|
|
const x = this.x + t.getOffsetX() * this.scaleX;
|
|
const y = this.y + t.getOffsetY() * this.scaleY;
|
|
return [x, y];
|
|
}
|
|
|
|
io(): void {
|
|
this.active.forEach((value) => {
|
|
value.getAndReset();
|
|
});
|
|
}
|
|
}
|