logicsimulator/components/breadboard.ts

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