feat: Add voltage drop calculation and update button in ObjectFlowDesigner

This commit is contained in:
Chop
2025-06-26 23:40:37 +02:00
parent 85746160a1
commit 8feeddc067
2 changed files with 216 additions and 5 deletions

View File

@@ -11,6 +11,9 @@
<header class="header">
<h1>Power System Designer</h1>
<div class="controls">
<button id="calculateBtn" class="btn btn-primary">
Calculate Voltage Drops
</button>
<button id="clearBtn" class="btn btn-danger">Clear All</button>
<button id="exportBtn" class="btn btn-secondary">Export Data</button>
<button id="importBtn" class="btn btn-secondary">Import Data</button>

216
script.js
View File

@@ -71,7 +71,13 @@ class ObjectFlowDesigner {
.getElementById("fileInput")
.addEventListener("change", this.importData.bind(this));
// Window resize
// Calculate button (if it exists)
const calculateBtn = document.getElementById("calculateBtn");
if (calculateBtn) {
calculateBtn.addEventListener("click", () => {
this.updateCalculations();
});
}
window.addEventListener("resize", this.resizeCanvas.bind(this));
}
@@ -824,6 +830,35 @@ class ObjectFlowDesigner {
yOffset += 13;
}
// 5. Voltage information (if calculated)
const hasVoltageData =
obj.data.voltage !== null && obj.data.voltage !== undefined;
if (hasVoltageData) {
// Show voltage level
const voltage = Math.round(obj.data.voltage * 10) / 10; // Round to 1 decimal
this.ctx.fillText(`${voltage}V`, obj.x + obj.width / 2, yOffset);
yOffset += 13;
// Show voltage drop percentage with color coding
const dropPercentage = this.getVoltageDropPercentage(obj);
const dropText = `${Math.round(dropPercentage * 10) / 10}%`;
// Color code based on voltage drop severity
if (dropPercentage > 10) {
this.ctx.fillStyle = "#e53e3e"; // Red for excessive drop (>10%)
} else if (dropPercentage > 7) {
this.ctx.fillStyle = "#fbb040"; // Orange for warning (7-10%)
} else {
this.ctx.fillStyle = "#38a169"; // Green for acceptable (<7%)
}
this.ctx.fillText(`-${dropText}`, obj.x + obj.width / 2, yOffset);
yOffset += 13;
// Reset color for next text
this.ctx.fillStyle = isSelected ? "#fbb040" : "#2d3748";
}
// Type description (moved down to accommodate other info)
if (obj.data.boxPoleType) {
this.ctx.fillText(
@@ -920,6 +955,22 @@ class ObjectFlowDesigner {
textLines.push(`${cableTypeDisplay} ${connection.data.crossSection}mm²`);
textLines.push(`${connection.data.length}m`);
// Add electrical calculations if available
if (
connection.data.sectionCurrent !== null &&
connection.data.voltageDrop !== null
) {
textLines.push(
`${Math.round(connection.data.sectionCurrent * 10) / 10}A/φ`
);
textLines.push(
`ΔU: ${Math.round(connection.data.voltageDrop * 1000) / 1000}V`
);
textLines.push(
`R: ${Math.round(connection.data.sectionResistance * 1000) / 1000}Ω`
);
}
if (textLines.length > 0) {
this.ctx.font = "11px Arial";
this.ctx.textAlign = "center";
@@ -1249,6 +1300,11 @@ class ObjectFlowDesigner {
const isOverheadCable =
conn.data.cableType === "AL" || conn.data.cableType === "AsXSn";
// Calculate electrical properties for display
const hasElectricalData =
conn.data.sectionCurrent !== null &&
conn.data.sectionCurrent !== undefined;
panel.innerHTML = `
<div class="property-group">
<h4>Power Cable Properties</h4>
@@ -1287,19 +1343,38 @@ class ObjectFlowDesigner {
<p><strong>Type:</strong> ${conn.data.cableType} ${
isOverheadCable ? "(Overhead)" : "(Ground)"
}</p>
<p><strong>From:</strong> ${conn.from.data.name} (${
<p><strong>From:</strong> ${conn.from.data.number || conn.from.data.name} (${
conn.from.type === "triangle"
? "Transformer"
: this.getNodeDisplayType(conn.from.data.nodeType)
})</p>
<p><strong>To:</strong> ${conn.to.data.name} (${
<p><strong>To:</strong> ${conn.to.data.number || conn.to.data.name} (${
conn.to.type === "triangle"
? "Transformer"
: this.getNodeDisplayType(conn.to.data.nodeType)
})</p>
<p><strong>Capacity:</strong> ${conn.data.crossSection} mm² × ${
<p><strong>Specifications:</strong> ${conn.data.crossSection}mm² × ${
conn.data.length
}m</p>
${
hasElectricalData
? `
<p><strong>Section Resistance:</strong> ${
Math.round(conn.data.sectionResistance * 1000) / 1000
} Ω</p>
<p><strong>Section Current:</strong> ${
Math.round(conn.data.sectionCurrent * 10) / 10
} A (per phase)</p>
<p><strong>Voltage Drop:</strong> ${
Math.round(conn.data.voltageDrop * 1000) / 1000
} V</p>
<p><strong>Total Consumers:</strong> ${this.getTotalConsumers(conn.to)}</p>
<p><strong>Diversity Factor:</strong> ${(
this.getDiversityFactor(this.getTotalConsumers(conn.to)) * 100
).toFixed(0)}%</p>
`
: ""
}
${
isOverheadCable
? '<p class="warning"><strong>⚠️ Overhead cables can only connect to poles or end connections!</strong></p>'
@@ -1309,6 +1384,7 @@ class ObjectFlowDesigner {
<div class="property-group">
<h4>Actions</h4>
<button class="btn btn-primary" onclick="designer.updateCalculations()">Calculate Voltage Drops</button>
<button class="btn btn-danger" onclick="designer.deleteConnection(${
conn.id
})">Delete Cable</button>
@@ -1684,6 +1760,138 @@ class ObjectFlowDesigner {
return power3Phase + power1Phase + customPower;
}
// Calculate voltage drop for the entire system
calculateVoltageDrops() {
// Reset all voltage calculations
this.objects.forEach((obj) => {
if (obj.data) {
obj.data.voltage = null;
obj.data.voltageDrop = null;
}
});
this.connections.forEach((conn) => {
if (conn.data) {
conn.data.sectionResistance = null;
conn.data.sectionCurrent = null;
conn.data.voltageDrop = null;
}
});
// Find transformer and set its voltage to 230V base
// Note: All calculations use 230V as reference voltage (Polish LV standard)
// regardless of transformer's rated bottom voltage
const transformer = this.objects.find((obj) => obj.type === "triangle");
if (!transformer) return;
transformer.data.voltage = 230; // Start calculations from 230V base
transformer.data.voltageDrop = 0; // No drop at transformer level for 230V base
// Calculate voltage drops working from transformer outward
this.calculateVoltageDropRecursive(transformer);
}
calculateVoltageDropRecursive(node) {
// Process all outgoing connections from this node
for (const connection of node.connections.outputs) {
const toNode = connection.to;
// Calculate current flowing through this section
const sectionCurrent = this.calculateSectionCurrent(toNode);
// Calculate section resistance = length / (crossSection * 35)
const sectionResistance =
connection.data.length / (connection.data.crossSection * 35);
// Calculate voltage drop = resistance * current
const voltageDrop = sectionResistance * sectionCurrent;
// Store calculation results in connection data
connection.data.sectionResistance = sectionResistance;
connection.data.sectionCurrent = sectionCurrent;
connection.data.voltageDrop = voltageDrop;
// Calculate voltage at destination node
const sourceVoltage = node.data.voltage || 230; // Always use 230V as base
toNode.data.voltage = sourceVoltage - voltageDrop;
toNode.data.voltageDrop = 230 - toNode.data.voltage; // Total drop from base 230V
// Continue recursively to downstream nodes
this.calculateVoltageDropRecursive(toNode);
}
}
calculateSectionCurrent(node) {
// Calculate total current flowing to this node and all downstream nodes
let totalPower = this.calculateDownstreamPower(node);
// Apply diversity factor based on total number of consumers
const totalConsumers = this.getTotalConsumers(node);
const diversityFactor = this.getDiversityFactor(totalConsumers);
const adjustedPower = totalPower * diversityFactor;
// Calculate current per phase using single-phase approach: I = P / (3 * 230V)
// This gives current per phase in a 3-phase system
const current = (adjustedPower * 1000) / (3 * 230);
return current;
}
// Get diversity factor based on total number of consumers
getDiversityFactor(totalConsumers) {
if (totalConsumers === 1) return 1.0; // 100%
if (totalConsumers === 2) return 0.59; // 59%
return 0.45; // 45% for 3+
}
// Calculate total number of consumers downstream from a node
getTotalConsumers(node) {
let totalConsumers = 0;
const visited = new Set();
const countRecursive = (currentNode) => {
if (visited.has(currentNode.id)) return 0;
visited.add(currentNode.id);
// Count consumers at this node
let consumers = 0;
if (currentNode.data.consumers3Phase)
consumers += currentNode.data.consumers3Phase;
if (currentNode.data.consumers1Phase)
consumers += Math.ceil(currentNode.data.consumers1Phase / 3); // Convert 1-phase to equivalent 3-phase units
// Count downstream consumers
for (const connection of currentNode.connections.outputs) {
consumers += countRecursive(connection.to);
}
return consumers;
};
totalConsumers = countRecursive(node);
return totalConsumers;
}
// Get voltage drop percentage for a node
getVoltageDropPercentage(node) {
if (!node.data.voltage) return 0;
const baseVoltage = 230; // Base voltage for drop calculations
const actualVoltage = node.data.voltage;
return ((baseVoltage - actualVoltage) / baseVoltage) * 100;
}
// Check if voltage drop exceeds acceptable limits
isVoltageDropExcessive(node) {
const dropPercentage = this.getVoltageDropPercentage(node);
return dropPercentage > 10; // 10% is the limit for 230V (23V)
}
// Update calculations and trigger re-render
updateCalculations() {
this.calculateVoltageDrops();
this.render();
}
}
// Initialize the application