diff --git a/index.html b/index.html index a2868cc..b5ec0f4 100644 --- a/index.html +++ b/index.html @@ -11,6 +11,9 @@

Power System Designer

+ diff --git a/script.js b/script.js index 0ce24c2..024c65e 100644 --- a/script.js +++ b/script.js @@ -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 = `

Power Cable Properties

@@ -1287,19 +1343,38 @@ class ObjectFlowDesigner {

Type: ${conn.data.cableType} ${ isOverheadCable ? "(Overhead)" : "(Ground)" }

-

From: ${conn.from.data.name} (${ +

From: ${conn.from.data.number || conn.from.data.name} (${ conn.from.type === "triangle" ? "Transformer" : this.getNodeDisplayType(conn.from.data.nodeType) })

-

To: ${conn.to.data.name} (${ +

To: ${conn.to.data.number || conn.to.data.name} (${ conn.to.type === "triangle" ? "Transformer" : this.getNodeDisplayType(conn.to.data.nodeType) })

-

Capacity: ${conn.data.crossSection} mm² × ${ +

Specifications: ${conn.data.crossSection}mm² × ${ conn.data.length - } m

+ }m

+ ${ + hasElectricalData + ? ` +

Section Resistance: ${ + Math.round(conn.data.sectionResistance * 1000) / 1000 + } Ω

+

Section Current: ${ + Math.round(conn.data.sectionCurrent * 10) / 10 + } A (per phase)

+

Voltage Drop: ${ + Math.round(conn.data.voltageDrop * 1000) / 1000 + } V

+

Total Consumers: ${this.getTotalConsumers(conn.to)}

+

Diversity Factor: ${( + this.getDiversityFactor(this.getTotalConsumers(conn.to)) * 100 + ).toFixed(0)}%

+ ` + : "" + } ${ isOverheadCable ? '

⚠️ Overhead cables can only connect to poles or end connections!

' @@ -1309,6 +1384,7 @@ class ObjectFlowDesigner {

Actions

+ @@ -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