menubar, tiles, VoltageDrop from old project

This commit is contained in:
Patrick Haßel 2024-10-22 16:38:49 +02:00
parent 5c493be073
commit 58496e74ec
17 changed files with 530 additions and 26 deletions

View File

@ -1 +1,5 @@
<div id="mainMenu">
<div class="mainMenuItem" routerLinkActive="mainMenuItemActive" routerLink="/Solar">Sonnensystem</div>
<div class="mainMenuItem" routerLinkActive="mainMenuItemActive" routerLink="/VoltageDrop">Spannungsabfall</div>
</div>
<router-outlet/> <router-outlet/>

View File

@ -0,0 +1,14 @@
#mainMenu {
border-bottom: 1px solid black;
.mainMenuItem {
float: left;
padding: 0.5em;
border-right: 1px solid black;
}
.mainMenuItemActive {
background-color: lightskyblue;
}
}

View File

@ -1,10 +1,10 @@
import {Component} from '@angular/core'; import {Component} from '@angular/core';
import {RouterOutlet} from '@angular/router'; import {RouterLink, RouterLinkActive, RouterOutlet} from '@angular/router';
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
standalone: true, standalone: true,
imports: [RouterOutlet], imports: [RouterOutlet, RouterLink, RouterLinkActive],
templateUrl: './app.component.html', templateUrl: './app.component.html',
styleUrl: './app.component.less' styleUrl: './app.component.less'
}) })

View File

@ -1,7 +1,9 @@
import {Routes} from '@angular/router'; import {Routes} from '@angular/router';
import {SolarComponent} from './solar/solar.component'; import {SolarComponent} from './solar/solar.component';
import {VoltageDropComponent} from "./voltage-drop/voltage-drop.component";
export const routes: Routes = [ export const routes: Routes = [
{path: 'Solar', component: SolarComponent}, {path: 'Solar', component: SolarComponent},
{path: 'VoltageDrop', component: VoltageDropComponent},
{path: '**', redirectTo: '/Solar'}, {path: '**', redirectTo: '/Solar'},
]; ];

View File

@ -4,12 +4,12 @@ export class Mass {
constructor( constructor(
readonly name: string, readonly name: string,
readonly diameterMeters: number, readonly realMeters: number,
readonly massKg: number, readonly massKg: number,
readonly moons: Mass[], readonly moons: Mass[],
modeMeters?: number, modeMeters?: number,
) { ) {
this.modelMeters = modeMeters || diameterMeters; this.modelMeters = modeMeters || realMeters;
} }
} }

View File

@ -1,11 +1,40 @@
<select [(ngModel)]="pivot"> <div class="tileContainer">
<option *ngFor="let mass of masses" [ngValue]="mass">{{ mass.name }}</option>
</select> <div class="tile">
<input type="number" [(ngModel)]="pivot.modelMeters" (ngModelChange)="ngOnInit()">m
<table> <div class="tileInner">
<tr *ngFor="let mass of masses"> <div class="tileTitle">
<th>{{ mass.name }}</th> Sonnensystem Skalieren
<td>{{ doPrefix(mass.diameterMeters, 'm', 1000, 'k', 0) }}</td> </div>
<td>{{ unit(mass.modelMeters, 'm') }}</td> <div class="tileContent">
<table>
<tr>
<td class="name">Name</td>
<td class="real">Realität</td>
<td class="model" colspan="2">Skaliert</td>
</tr> </tr>
</table> <tr *ngFor="let mass of masses">
<td class="name">{{ mass.name }}</td>
<td class="real">{{ doPrefix(mass.realMeters, 'm', 1000, 'k', 0) }}</td>
<td class="model" colspan="2">{{ unit(mass.modelMeters, 'm') }}</td>
</tr>
<tr>
<th>
<select [(ngModel)]="pivot">
<option *ngFor="let mass of masses" [ngValue]="mass">{{ mass.name }}</option>
</select>
</th>
<td colspan="2">
<input type="number" [(ngModel)]="pivot.modelMeters" (ngModelChange)="ngOnInit()">
</td>
<td class="unit">
&nbsp;m
</td>
</tr>
</table>
</div>
</div>
</div>
</div>

View File

@ -1,19 +1,36 @@
@import "../../tile.less";
table { table {
width: 100%; width: 100%;
} }
th { td {
text-align: left; white-space: nowrap;
border: 0.2em solid white;
} }
td { .name {
text-align: left;
font-weight: bold;
}
.real {
text-align: right; text-align: right;
} }
@media (min-width: 1000px) { .model {
text-align: right;
table { }
width: 400px;
} input {
width: 100%;
text-align: right;
}
select {
width: 100%;
}
.unit {
width: 0;
} }

View File

@ -62,8 +62,8 @@ export class SolarComponent implements OnInit {
protected prefix = ""; protected prefix = "";
ngOnInit() { ngOnInit() {
const scale = this.pivot.modelMeters / this.pivot.diameterMeters; const scale = this.pivot.modelMeters / this.pivot.realMeters;
this.masses.filter(m => m != this.pivot).forEach(m => m.modelMeters = scale * m.diameterMeters); this.masses.filter(m => m != this.pivot).forEach(m => m.modelMeters = scale * m.realMeters);
this.updatePrefix(); this.updatePrefix();
} }

View File

@ -0,0 +1,11 @@
<div class="value">
<div class="label">{{title}}</div>
<div class="input">
<input disabled type="number" [value]="round(value, places)">
<div class="unit">{{unit}}</div>
</div>
<div class="input" *ngIf="percentDivisor !== undefined">
<input *ngIf="value !== undefined" disabled type="number" [class.tooHigh]="percentMax !== undefined && value / percentDivisor > percentMax" [value]="formatPercent()">
<div class="unit">%</div>
</div>
</div>

View File

@ -0,0 +1,44 @@
.value {
clear: both;
.label {
padding: 1vmin 0 0.5vmin 0;
}
.input {
clear: both;
float: left;
}
input[type=number] {
float: left;
text-align: right;
}
input.tooHigh {
background-color: red;
}
.unit {
padding-left: 0.5vmin;
}
.shortcuts {
clear: both;
.shortcut {
float: left;
font-size: 90%;
padding: 0.2vmin 0.7vmin;
margin: 0.5vmin;
border-radius: 0.5vmin;
background-color: lightgray;
}
.shortcutSelected {
background-color: lightgreen;
}
}
}

View File

@ -0,0 +1,65 @@
import {Component, Input, OnInit} from '@angular/core';
import {NgIf} from "@angular/common";
@Component({
selector: 'app-value',
templateUrl: './value.component.html',
standalone: true,
imports: [
NgIf
],
styleUrls: ['./value.component.less']
})
export class ValueComponent implements OnInit {
@Input()
title!: string;
@Input()
unit!: string;
@Input()
value?: number;
@Input()
places?: number;
@Input()
percentDivisor?: number;
@Input()
percentPlaces?: number;
@Input()
percentMax?: number;
constructor() {
}
ngOnInit(): void {
}
round(value: number | undefined, places: number | undefined): number | undefined {
if (value === undefined || places === undefined) {
return value;
}
const divisor = Math.pow(10, places);
return Math.round(value * divisor) / divisor;
}
formatPercent(): number | undefined {
if (this.value === undefined || this.percentDivisor === undefined || this.percentPlaces === undefined) {
return undefined;
}
return this.round(this.value / this.percentDivisor * 100, this.percentPlaces)
}
defined() {
for (let argument of arguments) {
if (argument === undefined || argument === null) {
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,68 @@
<div class="box">
<div class="value">
<div class="label">Spannung</div>
<div class="input"><input type="number" [(ngModel)]="values.voltage" (ngModelChange)="update()"></div>
<div class="unit">V</div>
<div class="shortcuts">
<ng-container *ngFor="let v of voltages">
<div class="shortcut" [class.shortcutSelected]="values.voltage===v" (click)="values.voltage=v; update()">{{v}}</div>
</ng-container>
</div>
</div>
<div class="value">
<div class="label">Leistung</div>
<div class="input"><input type="number" [(ngModel)]="values.power" (ngModelChange)="update()"></div>
<div class="unit">kW</div>
<div class="shortcuts">
<ng-container *ngFor="let c of powers">
<div class="shortcut" [class.shortcutSelected]="values.power===c" (click)="values.power=c; update()">{{c}}</div>
</ng-container>
</div>
</div>
<div class="value">
<div class="label">Querschnitt</div>
<div class="input"><input type="number" [(ngModel)]="values.crossSection" (ngModelChange)="update()"></div>
<div class="unit">mm²</div>
<div class="shortcuts">
<ng-container *ngFor="let c of crossSections">
<div class="shortcut" [class.shortcutSelected]="values.crossSection===c" (click)="values.crossSection=c; update()">{{c}}</div>
</ng-container>
</div>
</div>
<div class="value">
<div class="label">Länge (1-fach)</div>
<div class="input"><input type="number" [(ngModel)]="values.length" (ngModelChange)="update()"></div>
<div class="unit">m</div>
<div class="shortcuts">
<ng-container *ngFor="let l of lengths">
<div class="shortcut" [class.shortcutSelected]="values.length===l" (click)="values.length=l; update()">{{l}}</div>
</ng-container>
</div>
</div>
<div class="value">
<div class="label">Spezifischer Widerstand</div>
<div class="input"><input type="number" [(ngModel)]="values.resistanceSpecific" (ngModelChange)="update()"></div>
<div class="unit">&Omega;*m</div>
<div class="shortcuts">
<ng-container *ngFor="let r of resistances">
<div class="shortcut" [class.shortcutSelected]="values.resistanceSpecific===r.value" (click)="values.resistanceSpecific=r.value; update()">{{r.name}}</div>
</ng-container>
</div>
</div>
</div>
<div class="box">
<app-value title="Spannungsverlust" unit="V" [value]="voltageDrop2Way" [places]="1" [percentDivisor]="values.voltage" [percentPlaces]="2" [percentMax]="0.03"></app-value>
<app-value title="Verlustleistung" unit="W" [value]="powerLoss2Way" [places]="0"></app-value>
<app-value title="Verfügbare Spannung" unit="V" [value]="voltageDropLoad" [places]="1" [percentDivisor]="values.voltage" [percentPlaces]="2"></app-value>
<app-value title="Verfügbare Leistung" unit="W" [value]="powerLoad" [places]="0" [percentDivisor]="values.power * 1000" [percentPlaces]="2"></app-value>
<app-value title="Strom" unit="A" [value]="current" [places]="1"></app-value>
<app-value title="Leitungwiderstand" unit="&Omega;" [value]="resistance2Way" [places]="3"></app-value>
<app-value title="Lastwiderstand" unit="&Omega;" [value]="resistanceLoad" [places]="3"></app-value>
<app-value title="Gesamtwiderstand" unit="&Omega;" [value]="resistanceTotal" [places]="3"></app-value>
</div>

View File

@ -0,0 +1,76 @@
@import "../../tile.less";
table {
width: 100%;
}
th {
text-align: left;
}
td {
text-align: right;
}
.box {
border-top: 1px solid black;
margin-top: 1em;
}
.box:first-child {
border-top-width: 0;
}
.value {
clear: both;
.label {
padding: 1vmin 0 0.5vmin 0;
}
.input {
clear: both;
float: left;
}
input[type=number] {
float: left;
text-align: right;
}
input.tooHigh {
background-color: red;
}
.unit {
padding-left: 0.5vmin;
}
.shortcuts {
clear: both;
.shortcut {
float: left;
font-size: 90%;
padding: 0.2vmin 0.7vmin;
margin: 0.5vmin;
border-radius: 0.5vmin;
background-color: lightgray;
}
.shortcutSelected {
background-color: lightgreen;
}
}
}
@media (min-width: 800px) {
.box {
float: left;
width: 50%;
border-top-width: 0;
margin-top: 0;
}
}

View File

@ -0,0 +1,121 @@
import { Component } from '@angular/core';
import {FormsModule} from "@angular/forms";
import {ValueComponent} from "./value/value.component";
import {NgForOf} from "@angular/common";
class Resistance {
constructor(
readonly name: string,
readonly value: number,
) {
// nothing
}
}
class Values {
constructor(
public length: number,
public voltage: number,
public power: number,
public crossSection: number,
public resistanceSpecific: number,
) {
// nothing
}
}
@Component({
selector: 'app-voltage-drop',
standalone: true,
imports: [
FormsModule,
ValueComponent,
NgForOf
],
templateUrl: './voltage-drop.component.html',
styleUrl: './voltage-drop.component.less'
})
export class VoltageDropComponent {
readonly lengths: number[] = [10, 15, 20, 30, 40, 50, 75, 100];
readonly voltages: number[] = [5, 12, 24, 36, 230, 400];
readonly powers: number[] = [0.5, 1, 2, 3.68, 5, 7.5, 10, 15, 20, 25, 30];
readonly crossSections: number[] = [0.75, 1.5, 2.5, 4, 6, 10, 16, 25, 35, 50];
readonly resistances: Resistance[] = [
{name: 'Silber', value: 0.015},
{name: 'Kupfer', value: 0.017},
{name: 'Aluminium', value: 0.0278},
];
readonly testSet: Values = {
length: this.lengths[4],
voltage: this.voltages[4],
power: this.powers[3],
crossSection: this.crossSections[3],
resistanceSpecific: this.resistances[1].value,
}
readonly testSet2: Values = {
length: 100,
voltage: 230,
power: 2.5,
crossSection: 1.5,
resistanceSpecific: this.resistances[1].value,
}
readonly values: Values = this.testSet;
resistance2Way?: number;
resistanceLoad?: number;
resistanceTotal?: number;
current?: number;
voltageDrop2Way?: number;
powerLoss2Way?: number;
voltageDropLoad?: number;
powerLoad?: number;
constructor() {
}
ngOnInit(): void {
this.update();
}
update() {
if (!this.values || !this.values.resistanceSpecific || !this.values.length || !this.values.crossSection || !this.values.power || !this.values.voltage) {
this.resistance2Way = undefined;
this.resistanceLoad = undefined;
this.resistanceTotal = undefined;
this.current = undefined;
this.voltageDrop2Way = undefined;
this.powerLoss2Way = undefined;
this.voltageDropLoad = undefined;
this.powerLoad = undefined;
return;
}
this.resistance2Way = (this.values.resistanceSpecific * this.values.length / this.values.crossSection) * 2;
this.resistanceLoad = (this.values.voltage * this.values.voltage) / (this.values.power * 1000);
this.resistanceTotal = this.resistance2Way + this.resistanceLoad;
this.current = this.values.voltage / this.resistanceTotal;
this.voltageDrop2Way = this.resistance2Way * this.current;
this.powerLoss2Way = this.voltageDrop2Way * this.current;
this.voltageDropLoad = this.resistanceLoad * this.current;
this.powerLoad = this.voltageDropLoad * this.current;
}
}

View File

@ -0,0 +1 @@
@space: 0.5em;

View File

@ -1,6 +1,7 @@
body { body {
font-family: sans-serif; font-family: sans-serif;
font-size: 4vw; font-size: 4vw;
margin: 0;
} }
div { div {
@ -12,6 +13,16 @@ a {
text-decoration: none; text-decoration: none;
} }
input {
all: unset;
background-color: white;
border: 1px solid lightgray;
}
select {
font-size: inherit;
}
table { table {
border-collapse: collapse; border-collapse: collapse;
} }

View File

@ -0,0 +1,41 @@
@import "config.less";
.tileContainer {
padding: calc(@space / 2);
.tile {
width: 100%;
padding: calc(@space / 2);
.tileInner {
border: 1px solid #ddd;
border-radius: @space;
background-color: #fbfbfb;
.tileTitle {
font-weight: bold;
padding: calc(@space / 2) @space;
background-color: lightskyblue;
}
.tileContent {
padding: calc(@space / 2);
}
}
}
}
@media (min-width: 1000px) {
.tileContainer {
.tile {
float: left;
width: 500px;
}
}
}