From c66ae0854d55c9dd8508c5bb336ffdaa40781b48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20Ha=C3=9Fel?= Date: Mon, 3 Feb 2025 13:08:57 +0100 Subject: [PATCH] Relay, WireComponent, recalculate Calculation, Wire disconnectable --- .../angular/src/app/editor/circuit/Circuit.ts | 21 ++- .../src/app/editor/circuit/DEMO_003.ts | 28 +++ .../angular/src/app/editor/circuit/Parts.ts | 5 + .../angular/src/app/editor/circuit/Wires.ts | 3 +- .../app/editor/circuit/circuit.component.less | 56 +++--- .../app/editor/circuit/circuit.component.svg | 22 +-- .../app/editor/circuit/circuit.component.ts | 15 +- .../src/app/editor/editor.component.ts | 4 +- .../src/app/editor/junction/Junction.ts | 30 ++- .../editor/junction/junction.component.svg | 4 +- .../app/editor/menubar/menubar.component.ts | 3 +- .../app/editor/part-wire/wire.component.less | 5 + .../app/editor/part-wire/wire.component.svg | 11 ++ .../app/editor/part-wire/wire.component.ts | 172 ++++++++++++++++++ src/main/angular/src/app/editor/parts/Part.ts | 17 +- .../parts/battery/battery.component.svg | 4 +- .../editor/parts/battery/battery.component.ts | 5 +- .../src/app/editor/parts/part.component.less | 5 +- .../src/app/editor/parts/part.component.svg | 2 + .../src/app/editor/parts/part.component.ts | 13 +- .../src/app/editor/parts/relay/Relay.ts | 91 +++++++++ .../editor/parts/relay/relay.component.less | 21 +++ .../editor/parts/relay/relay.component.svg | 30 +++ .../app/editor/parts/relay/relay.component.ts | 23 +++ src/main/angular/src/app/editor/wire/Wire.ts | 27 ++- 25 files changed, 527 insertions(+), 90 deletions(-) create mode 100644 src/main/angular/src/app/editor/circuit/DEMO_003.ts create mode 100644 src/main/angular/src/app/editor/part-wire/wire.component.less create mode 100644 src/main/angular/src/app/editor/part-wire/wire.component.svg create mode 100644 src/main/angular/src/app/editor/part-wire/wire.component.ts create mode 100644 src/main/angular/src/app/editor/parts/relay/Relay.ts create mode 100644 src/main/angular/src/app/editor/parts/relay/relay.component.less create mode 100644 src/main/angular/src/app/editor/parts/relay/relay.component.svg create mode 100644 src/main/angular/src/app/editor/parts/relay/relay.component.ts diff --git a/src/main/angular/src/app/editor/circuit/Circuit.ts b/src/main/angular/src/app/editor/circuit/Circuit.ts index 17b9d90..089753d 100644 --- a/src/main/angular/src/app/editor/circuit/Circuit.ts +++ b/src/main/angular/src/app/editor/circuit/Circuit.ts @@ -23,7 +23,26 @@ export class Circuit { } calculate() { - Calculation.calculate(this); + for (let i = 0; i < 50; i++) { + Calculation.calculate(this); + const changedParts = []; + for (const part of this.parts) { + if (part.loop()) { + changedParts.push(part); + } + } + if (changedParts.length === 0) { + return; + } + console.log("Recalculating due to parts changed:", changedParts.map(p => p.name).join(", ")); + } + console.error("Too many recalculations!"); + this.resetCalculations(); + } + + private resetCalculations() { + this.parts.forEach(part => part.resetCalculations()); + this.wires.forEach(wire => wire.resetCalculations()); } } diff --git a/src/main/angular/src/app/editor/circuit/DEMO_003.ts b/src/main/angular/src/app/editor/circuit/DEMO_003.ts new file mode 100644 index 0000000..69de832 --- /dev/null +++ b/src/main/angular/src/app/editor/circuit/DEMO_003.ts @@ -0,0 +1,28 @@ +import {Circuit} from "./Circuit"; +import {Battery} from '../parts/battery/Battery'; +import {Light} from '../parts/light/Light'; +import {Wire} from '../wire/Wire'; +import {RESISTANCE_MIN} from './Calculation'; +import {Relay} from '../parts/relay/Relay'; + +const batteryLight = new Battery(1, 1, "Licht Batterie"); + +const light = new Light(2, 1, "Licht"); + +const relay = new Relay(1, 2, "Relais"); + +const batteryRelay = new Battery(1, 3, "Relais Batterie"); + +export const DEMO_003 = new Circuit( + "DEMO_003", + "3. Relais", + [batteryRelay, relay, batteryLight, light], + [ + new Wire(batteryLight.plus, light.a, RESISTANCE_MIN, ""), + new Wire(batteryLight.minus, relay.common, RESISTANCE_MIN, ""), + new Wire(light.b, relay.active, RESISTANCE_MIN, ""), + + new Wire(batteryRelay.minus, relay.coilA, RESISTANCE_MIN, ""), + new Wire(batteryRelay.plus, relay.coilB, RESISTANCE_MIN, ""), + ], +); diff --git a/src/main/angular/src/app/editor/circuit/Parts.ts b/src/main/angular/src/app/editor/circuit/Parts.ts index 2ddf520..8e5ee83 100644 --- a/src/main/angular/src/app/editor/circuit/Parts.ts +++ b/src/main/angular/src/app/editor/circuit/Parts.ts @@ -2,6 +2,7 @@ import {Point} from '../Point'; import {Part, RASTER} from '../parts/Part'; import {Battery} from '../parts/battery/Battery'; import {Light} from '../parts/light/Light'; +import {Relay} from '../parts/relay/Relay'; import {Circuit} from './Circuit'; export class Parts { @@ -49,6 +50,10 @@ export class Parts { return this.add(new Light(rasterX, rasterY, this.generateName("Licht"))); } + newRelay(rasterX: number, rasterY: number): Relay { + return this.add(new Relay(rasterX, rasterY, this.generateName("Relais"))); + } + private generateName(baseName: string) { let counter = 1; let name: string; diff --git a/src/main/angular/src/app/editor/circuit/Wires.ts b/src/main/angular/src/app/editor/circuit/Wires.ts index b24218e..90e92ac 100644 --- a/src/main/angular/src/app/editor/circuit/Wires.ts +++ b/src/main/angular/src/app/editor/circuit/Wires.ts @@ -107,8 +107,7 @@ export class Wires { disconnect(wire: Wire) { this.circuit.wires.splice(this.circuit.wires.indexOf(wire), 1); - wire.start.wires.splice(wire.start.wires.indexOf(wire), 1); - wire.end.wires.splice(wire.end.wires.indexOf(wire), 1); + wire.disconnect(); console.log("Wire disconnected: ", wire); this.circuit.calculate(); } diff --git a/src/main/angular/src/app/editor/circuit/circuit.component.less b/src/main/angular/src/app/editor/circuit/circuit.component.less index 18b0f1c..d9249a2 100644 --- a/src/main/angular/src/app/editor/circuit/circuit.component.less +++ b/src/main/angular/src/app/editor/circuit/circuit.component.less @@ -1,33 +1,31 @@ .circuit { width: 100%; height: 100%; - - -webkit-user-select: none; - -ms-user-select: none; - user-select: none; - - .wire { - stroke-width: 9px; - stroke-linecap: round; - pointer-events: none; - } - - .wireBack { - stroke-width: 11px; - stroke: black; - } - - .wireOpen { - stroke: blue; - stroke-dasharray: 5px 15px; - } - - .wireEnd { - stroke: green; - } - - .wireDuplicate { - stroke: red; - } - +} + +.wireBack { + stroke-linecap: round; + stroke-width: 11px; + stroke: black; + pointer-events: none; +} + +.wire { + stroke-width: 9px; + stroke-dasharray: 0 20; + stroke-linecap: round; + pointer-events: none; +} + +.wireOpen { + stroke: blue; + stroke-dasharray: 5px 15px; +} + +.wireEnd { + stroke: green; +} + +.wireDuplicate { + stroke: red; } diff --git a/src/main/angular/src/app/editor/circuit/circuit.component.svg b/src/main/angular/src/app/editor/circuit/circuit.component.svg index 756c3b5..67926cc 100644 --- a/src/main/angular/src/app/editor/circuit/circuit.component.svg +++ b/src/main/angular/src/app/editor/circuit/circuit.component.svg @@ -2,27 +2,7 @@ - - - - - - - + diff --git a/src/main/angular/src/app/editor/circuit/circuit.component.ts b/src/main/angular/src/app/editor/circuit/circuit.component.ts index 3ca3268..13984fb 100644 --- a/src/main/angular/src/app/editor/circuit/circuit.component.ts +++ b/src/main/angular/src/app/editor/circuit/circuit.component.ts @@ -4,10 +4,9 @@ import {MessageService} from '../message/message.service'; import {Wires} from './Wires'; import {Parts} from './Parts'; import {PartComponent} from '../parts/part.component'; -import {Wire} from '../wire/Wire'; -import {fadeColor} from '../colorHelpers'; import {Circuit} from './Circuit'; +import {WireComponent} from '../part-wire/wire.component'; @Component({ selector: 'app-circuit', @@ -15,6 +14,7 @@ import {Circuit} from './Circuit'; NgForOf, NgIf, PartComponent, + WireComponent, ], templateUrl: './circuit.component.svg', styleUrl: './circuit.component.less' @@ -55,15 +55,4 @@ export class CircuitComponent { this.parts.mouseUp($event); } - voltageColor(wire: Wire) { - if (wire.start.voltage === null || wire.start.minCircuitVoltage === null || wire.start.maxCircuitVoltage === null) { - return 'gray'; - } - const ratio = (wire.start.voltage - wire.start.minCircuitVoltage) / (wire.start.maxCircuitVoltage - wire.start.minCircuitVoltage); - if (ratio < 0.5) { - return fadeColor(ratio * 2, '#008cff', 'magenta'); - } - return fadeColor((ratio - 0.5) * 2, 'magenta', 'red'); - } - } diff --git a/src/main/angular/src/app/editor/editor.component.ts b/src/main/angular/src/app/editor/editor.component.ts index eb55fb8..470a66e 100644 --- a/src/main/angular/src/app/editor/editor.component.ts +++ b/src/main/angular/src/app/editor/editor.component.ts @@ -3,7 +3,7 @@ import {CircuitComponent} from './circuit/circuit.component'; import {MessagesComponent} from './message/messages/messages.component'; import {MenubarComponent} from './menubar/menubar.component'; import {Circuit} from './circuit/Circuit'; -import {DEMO_002} from './circuit/DEMO_002'; +import {DEMO_003} from './circuit/DEMO_003'; @Component({ selector: 'app-editor', @@ -20,7 +20,7 @@ export class EditorComponent implements OnInit { private _circuit: Circuit = Circuit.new(); ngOnInit(): void { - this.circuit = DEMO_002; + this.circuit = DEMO_003; } set circuit(circuit: Circuit) { diff --git a/src/main/angular/src/app/editor/junction/Junction.ts b/src/main/angular/src/app/editor/junction/Junction.ts index 8989bb8..a932ec5 100644 --- a/src/main/angular/src/app/editor/junction/Junction.ts +++ b/src/main/angular/src/app/editor/junction/Junction.ts @@ -2,6 +2,7 @@ import {Wire} from "../wire/Wire"; import {Part} from '../parts/Part'; import {Rect} from '../../Rect'; import {selectTowardsRoot} from '../../selectTowardsRoot'; +import {fadeColor} from '../colorHelpers'; export const JUNCTION_RADIUS_PERCENT = 15; @@ -9,9 +10,9 @@ export class Junction { readonly uuid: string = self.crypto.randomUUID(); - readonly percentX: number; + readonly centerPercentX: number; - readonly percentY: number; + readonly centerPercentY: number; readonly wires: Wire[] = []; @@ -25,11 +26,11 @@ export class Junction { constructor( public readonly part: Part, - percentX: number, - percentY: number, + public readonly percentX: number, + public readonly percentY: number, readonly name: string) { - this.percentX = percentX - JUNCTION_RADIUS_PERCENT; - this.percentY = percentY - JUNCTION_RADIUS_PERCENT; + this.centerPercentX = percentX - JUNCTION_RADIUS_PERCENT; + this.centerPercentY = percentY - JUNCTION_RADIUS_PERCENT; } get fullName(): string { @@ -77,7 +78,7 @@ export class Junction { private findRect(): Rect { const child = document.getElementById(this.uuid); if (!child) { - throw Error(`No HTMLElement found for Junction: uuid=${this.uuid}, name=${this.name}`); + throw Error(`No HTMLElement found for Junction: name=${this.fullName}, uuid=${this.uuid}`); } const parent = selectTowardsRoot(child, "svg.circuit"); @@ -95,4 +96,19 @@ export class Junction { }; } + voltageColor(): string { + if (this.voltage === null || this.minCircuitVoltage === null || this.maxCircuitVoltage === null) { + return 'lightgray'; + } + const ratio = (this.voltage - this.minCircuitVoltage) / (this.maxCircuitVoltage - this.minCircuitVoltage); + if (ratio < 0.5) { + return fadeColor(ratio * 2, '#008cff', 'magenta'); + } + return fadeColor((ratio - 0.5) * 2, 'magenta', 'red'); + } + + resetCalculations() { + this.voltage = null; + } + } diff --git a/src/main/angular/src/app/editor/junction/junction.component.svg b/src/main/angular/src/app/editor/junction/junction.component.svg index 7bbd72c..8ceb3f4 100644 --- a/src/main/angular/src/app/editor/junction/junction.component.svg +++ b/src/main/angular/src/app/editor/junction/junction.component.svg @@ -1,7 +1,7 @@ = new EventEmitter(); - circuits: Circuit[] = [DEMO_001, DEMO_002]; + circuits: Circuit[] = [DEMO_001, DEMO_002, DEMO_003]; } diff --git a/src/main/angular/src/app/editor/part-wire/wire.component.less b/src/main/angular/src/app/editor/part-wire/wire.component.less new file mode 100644 index 0000000..f0949d5 --- /dev/null +++ b/src/main/angular/src/app/editor/part-wire/wire.component.less @@ -0,0 +1,5 @@ +@import "../parts/part.component.less"; + +.partWireBack { + stroke-width: 5px; +} diff --git a/src/main/angular/src/app/editor/part-wire/wire.component.svg b/src/main/angular/src/app/editor/part-wire/wire.component.svg new file mode 100644 index 0000000..93f2b01 --- /dev/null +++ b/src/main/angular/src/app/editor/part-wire/wire.component.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/main/angular/src/app/editor/part-wire/wire.component.ts b/src/main/angular/src/app/editor/part-wire/wire.component.ts new file mode 100644 index 0000000..2d7862e --- /dev/null +++ b/src/main/angular/src/app/editor/part-wire/wire.component.ts @@ -0,0 +1,172 @@ +import {Component, Input} from '@angular/core'; +import {ANIMATION_ELECTRON_STEPS, Wire} from '../wire/Wire'; +import {Junction} from '../junction/Junction'; +import {fadeColor} from '../colorHelpers'; +import {NgIf} from '@angular/common'; + +@Component({ + selector: 'g [inner-wire]', + imports: [ + NgIf + ], + templateUrl: './wire.component.svg', + styleUrl: './wire.component.less' +}) +export class WireComponent { + + protected wire: Wire | null = null; + + protected isWirePart: boolean = false; + + protected fromJ: Junction | null = null; + + protected toJ: Junction | null = null; + + private _manualCurrent_: number = 0; + + @Input() + extraClassesBack: string = ""; + + @Input() + set partWire(wire: Wire) { + this.wire = wire; + this.isWirePart = true; + } + + @Input() + set globalWire(wire: Wire) { + this.wire = wire; + this.isWirePart = false; + } + + @Input() + set fromJunction(junction: Junction) { + this.fromJ = junction; + } + + @Input() + set toJunction(junction: Junction) { + this.toJ = junction; + } + + @Input() + fromX = ""; + + @Input() + fromY = ""; + + @Input() + toX = ""; + + @Input() + toY = ""; + + @Input() + set current(current: number) { + this._manualCurrent_ = current; + } + + get current(): number { + if (this.wire) { + return this.wire.current; + } + return this._manualCurrent_; + } + + protected get voltage(): number | null { + if (this.wire) { + if (this.wire.start.voltage === null || this.wire.end.voltage === null) { + return null; + } + return (this.wire.start.voltage + this.wire.end.voltage) / 2; + } else if (this.fromJ) { + return this.fromJ.voltage; + } else if (this.toJ) { + return this.toJ.voltage; + } + return null; + } + + protected get x1(): string { + if (this.wire) { + if (this.isWirePart) { + return this.wire.start.percentX + '%'; + } else { + return this.wire.start.pixelX + 'px'; + } + } else if (this.fromJ) { + return this.fromJ.percentX + '%'; + } + return this.fromX; + } + + protected get y1(): string { + if (this.wire) { + if (this.isWirePart) { + return this.wire.start.percentY + '%'; + } else { + return this.wire.start.pixelY + 'px'; + } + } else if (this.fromJ) { + return this.fromJ.percentY + '%'; + } + return this.fromY; + } + + protected get x2(): string { + if (this.wire) { + if (this.isWirePart) { + return this.wire.end.percentX + '%'; + } else { + return this.wire.end.pixelX + 'px'; + } + } else if (this.toJ) { + return this.toJ.percentX + '%'; + } + return this.toX; + } + + protected get y2(): string { + if (this.wire) { + if (this.isWirePart) { + return this.wire.end.percentY + '%'; + } else { + return this.wire.end.pixelY + 'px'; + } + } else if (this.toJ) { + return this.toJ.percentY + '%'; + } + return this.toY; + } + + protected get animateFrom(): number { + return this.current < 0 ? 0 : ANIMATION_ELECTRON_STEPS; + } + + protected get animateTo(): number { + return this.current < 0 ? ANIMATION_ELECTRON_STEPS : 0; + } + + protected get animateDuration(): number { + return Math.max(0.05, (1 / Math.abs(this.current)) / 200); + } + + protected get color(): string { + const voltage = this.voltage; + const junction: Junction | null = this.wire ? this.wire.start : this.fromJ ? this.fromJ : this.toJ; + if (!junction) { + throw new Error("Cannot calculate color if no voltage (or junction) given."); + } + const vMin = junction.minCircuitVoltage; + const vMax = junction.maxCircuitVoltage; + if (voltage === null || vMin === null || vMax === null) { + return "lightgray"; + } + const ratio = (voltage - vMin) / (vMax - vMin); + if (ratio < 0.5) { + return fadeColor(ratio * 2, '#008cff', 'magenta'); + } + return fadeColor((ratio - 0.5) * 2, 'magenta', 'red'); + } + +} diff --git a/src/main/angular/src/app/editor/parts/Part.ts b/src/main/angular/src/app/editor/parts/Part.ts index 6e5780f..b13b69f 100644 --- a/src/main/angular/src/app/editor/parts/Part.ts +++ b/src/main/angular/src/app/editor/parts/Part.ts @@ -29,8 +29,6 @@ export abstract class Part { this._h = rasterH * 3 * RASTER; } - abstract get junctions(): Junction[]; - get x(): number { return this._x; } @@ -63,4 +61,19 @@ export abstract class Part { this.junctions.forEach(junction => junction.updatePosition()); } + loop(): boolean { + return false; + }; + + resetCalculations() { + this.junctions.forEach(junction => junction.resetCalculations()); + this.resetCalculations2(); + } + + resetCalculations2(): void { + // + } + + abstract get junctions(): Junction[]; + } diff --git a/src/main/angular/src/app/editor/parts/battery/battery.component.svg b/src/main/angular/src/app/editor/parts/battery/battery.component.svg index 7b091c1..f2310ca 100644 --- a/src/main/angular/src/app/editor/parts/battery/battery.component.svg +++ b/src/main/angular/src/app/editor/parts/battery/battery.component.svg @@ -1,7 +1,7 @@ {{ battery.voltageStr }} - - + + {{ battery.currentStr }} diff --git a/src/main/angular/src/app/editor/parts/battery/battery.component.ts b/src/main/angular/src/app/editor/parts/battery/battery.component.ts index 5ece71b..c241936 100644 --- a/src/main/angular/src/app/editor/parts/battery/battery.component.ts +++ b/src/main/angular/src/app/editor/parts/battery/battery.component.ts @@ -1,9 +1,12 @@ import {Component, Input} from '@angular/core'; import {Battery} from './Battery'; +import {WireComponent} from '../../part-wire/wire.component'; @Component({ selector: 'g[inner-part-battery]', - imports: [], + imports: [ + WireComponent + ], templateUrl: './battery.component.svg', styleUrl: './battery.component.less', }) diff --git a/src/main/angular/src/app/editor/parts/part.component.less b/src/main/angular/src/app/editor/parts/part.component.less index 0dabdcf..e2f1467 100644 --- a/src/main/angular/src/app/editor/parts/part.component.less +++ b/src/main/angular/src/app/editor/parts/part.component.less @@ -1,3 +1,5 @@ +@import "../circuit/circuit.component.less"; + .part { border-radius: 0.51em; border: 1px solid transparent; @@ -18,7 +20,6 @@ opacity: 0.7; } -.partWire { +.partWireBack { stroke-width: 5px; - stroke: black; } diff --git a/src/main/angular/src/app/editor/parts/part.component.svg b/src/main/angular/src/app/editor/parts/part.component.svg index c0ca75e..4701b9b 100644 --- a/src/main/angular/src/app/editor/parts/part.component.svg +++ b/src/main/angular/src/app/editor/parts/part.component.svg @@ -15,6 +15,8 @@ + + diff --git a/src/main/angular/src/app/editor/parts/part.component.ts b/src/main/angular/src/app/editor/parts/part.component.ts index 075395c..68ba67c 100644 --- a/src/main/angular/src/app/editor/parts/part.component.ts +++ b/src/main/angular/src/app/editor/parts/part.component.ts @@ -8,6 +8,8 @@ import {Parts} from '../circuit/Parts'; import {Wires} from '../circuit/Wires'; import {LightComponent} from './light/light.component'; import {JunctionComponent} from '../junction/junction.component'; +import {Relay} from './relay/Relay'; +import {RelayComponent} from './relay/relay.component'; @Component({ selector: 'g[inner-part]', @@ -16,7 +18,8 @@ import {JunctionComponent} from '../junction/junction.component'; NgIf, NgForOf, LightComponent, - JunctionComponent + JunctionComponent, + RelayComponent ], templateUrl: './part.component.svg', styleUrl: './part.component.less', @@ -48,4 +51,12 @@ export class PartComponent { return part instanceof Light; } + asRelay(part: Part): Relay { + return part as Relay; + } + + isRelay(part: Part): boolean { + return part instanceof Relay; + } + } diff --git a/src/main/angular/src/app/editor/parts/relay/Relay.ts b/src/main/angular/src/app/editor/parts/relay/Relay.ts new file mode 100644 index 0000000..d362229 --- /dev/null +++ b/src/main/angular/src/app/editor/parts/relay/Relay.ts @@ -0,0 +1,91 @@ +import {Part} from "../Part"; +import {Junction} from '../../junction/Junction'; +import {PartType} from '../PartType'; +import {siPrefix} from '../../siPrefix'; +import {Wire} from '../../wire/Wire'; +import {RESISTANCE_MIN} from '../../circuit/Calculation'; + +export class Relay extends Part { + + readonly common = new Junction(this, 15, 50, "COM"); + + readonly active = new Junction(this, 85, 50, "NO"); + + readonly inactive = new Junction(this, 85, 15, "NC"); + + readonly coilA = new Junction(this, 15, 85, "A"); + + readonly coilB = new Junction(this, 85, 85, "B"); + + readonly coil: Wire; + + contact: Wire = new Wire(this.common, this.inactive, RESISTANCE_MIN, "Schaltkontakt"); + + constructor( + rasterX: number, + rasterY: number, + name: string, + public voltageMin: number = 1.5, + public voltageMax: number = 3, + public resistance: number = 150, + ) { + super(PartType.Light, name, rasterX, rasterY); + this.coil = new Wire(this.coilA, this.coilB, resistance, "Spule"); + } + + override get junctions(): Junction[] { + return [this.coilA, this.coilB, this.common, this.active, this.inactive]; + } + + get voltage(): number { + if (this.coilA.voltage == null || this.coilB.voltage == null) { + return 0; + } + return this.coilB.voltage - this.coilA.voltage; + } + + get isCoilActive(): boolean { + return this.voltage >= this.voltageMin && !this.defect; + } + + get defect(): boolean { + return this.voltage > this.voltageMax; + } + + get voltageStr(): string { + return siPrefix(this.voltage, 'V', 2); + } + + get current(): number { + if (this.coilA.voltage === null || this.coilB.voltage === null) { + return 0; + } + return (this.coilB.voltage - this.coilA.voltage) / this.resistance; + } + + get currentStr(): string { + const current = Math.abs(this.current) || 0; + return siPrefix(current, 'A', 2); + } + + override loop(): boolean { + const shouldBeActive = this.isCoilActive; + const isActive = this.contact.end === this.active; + if (isActive != shouldBeActive) { + this.contact.disconnect(); + if (shouldBeActive) { + this.contact = new Wire(this.common, this.active, RESISTANCE_MIN, "Schaltkontakt Aktiv"); + } else { + this.contact = new Wire(this.common, this.inactive, RESISTANCE_MIN, "Schaltkontakt Inaktiv"); + } + return true; + } + return false; + } + + override resetCalculations2() { + this.contact.resetCalculations(); + this.coil.resetCalculations(); + } + +} diff --git a/src/main/angular/src/app/editor/parts/relay/relay.component.less b/src/main/angular/src/app/editor/parts/relay/relay.component.less new file mode 100644 index 0000000..3676feb --- /dev/null +++ b/src/main/angular/src/app/editor/parts/relay/relay.component.less @@ -0,0 +1,21 @@ +@import "../part.component.less"; + +.defect { + filter: drop-shadow(0 0 30px black); +} + +.mechanic { + stroke-width: 3px; + stroke: dimgray; + stroke-dasharray: 5 5; +} + +.coil { + stroke: black; + stroke-width: 1px; + fill: darkgray; +} + +.coilActive { + fill: dodgerblue; +} diff --git a/src/main/angular/src/app/editor/parts/relay/relay.component.svg b/src/main/angular/src/app/editor/parts/relay/relay.component.svg new file mode 100644 index 0000000..03bd491 --- /dev/null +++ b/src/main/angular/src/app/editor/parts/relay/relay.component.svg @@ -0,0 +1,30 @@ + + + {{ relay.voltageStr }} + {{ relay.currentStr }} + + + + + + + + + + + + + + + + diff --git a/src/main/angular/src/app/editor/parts/relay/relay.component.ts b/src/main/angular/src/app/editor/parts/relay/relay.component.ts new file mode 100644 index 0000000..5bab283 --- /dev/null +++ b/src/main/angular/src/app/editor/parts/relay/relay.component.ts @@ -0,0 +1,23 @@ +import {Component, Input} from '@angular/core'; +import {Relay} from './Relay'; +import {JUNCTION_RADIUS_PERCENT} from '../../junction/Junction'; +import {NgIf} from '@angular/common'; +import {WireComponent} from '../../part-wire/wire.component'; + +@Component({ + selector: 'g[inner-part-relay]', + imports: [ + NgIf, + WireComponent + ], + templateUrl: './relay.component.svg', + styleUrl: './relay.component.less', +}) +export class RelayComponent { + + @Input() + relay!: Relay; + + protected readonly JUNCTION_RADIUS_PERCENT = JUNCTION_RADIUS_PERCENT; + +} diff --git a/src/main/angular/src/app/editor/wire/Wire.ts b/src/main/angular/src/app/editor/wire/Wire.ts index 47bbd45..c605814 100644 --- a/src/main/angular/src/app/editor/wire/Wire.ts +++ b/src/main/angular/src/app/editor/wire/Wire.ts @@ -1,5 +1,8 @@ import {Junction} from "../junction/Junction"; import {RESISTANCE_MIN} from '../circuit/Calculation'; +import {fadeColor} from '../colorHelpers'; + +export const ANIMATION_ELECTRON_STEPS = 20; export class Wire { @@ -11,15 +14,16 @@ export class Wire { public resistance: number, public name: string | null = null, ) { - this.start.wires.push(this); - this.end.wires.push(this); if (this.resistance === 0) { this.resistance = RESISTANCE_MIN; } + this.start.wires.push(this); + this.end.wires.push(this); } - get absCurrent(): number { - return Math.abs(this.current); + disconnect() { + this.start.wires.splice(this.start.wires.indexOf(this), 1); + this.end.wires.splice(this.end.wires.indexOf(this), 1); } toString() { @@ -39,4 +43,19 @@ export class Wire { throw new Error(`Wire is not connected to given Junction: wire=${this}, junction=${junction}`); } + resetCalculations() { + this.current = 0; + } + + voltageColor(): string { + if (this.start.voltage === null || this.end.voltage === null || this.start.minCircuitVoltage === null || this.start.maxCircuitVoltage === null) { + return 'lightgray'; + } + const ratio = ((this.end.voltage + this.start.voltage) / 2 - this.start.minCircuitVoltage) / (this.start.maxCircuitVoltage - this.start.minCircuitVoltage); + if (ratio < 0.5) { + return fadeColor(ratio * 2, '#008cff', 'magenta'); + } + return fadeColor((ratio - 0.5) * 2, 'magenta', 'red'); + } + }