Files
Cable/Cable/Systems/SystemEditorView.swift
Stefan Lange-Hegermann 38183f5282 Calculator: size cables for ampacity and drop budget
recommendedCrossSection now satisfies two criteria and takes whichever
demands more copper: the voltage-drop budget, and an ampacity at least
as high as the fuse protecting the circuit (ABYC E-11: the OCP rating
must not exceed the conductor's ampacity). Ampacity comes from ABYC
E-11 Table 6A for AWG and ISO 13297 Table A1 for mm², both 105 °C.

Before this, a 40 A load over 0.5 m was sized 1.5 mm² / AWG 16 next to
a 50 A fuse: a cable the fuse can never protect. Long runs stay
voltage-drop driven and unchanged.

The drop budget is now stored per system (3 / 5 / 10 %) and editable in
the system editor, with a global default in Settings that the
standalone calculator uses and new systems inherit.

Cross-sections are derived, never entered by the user, so LoadCableSync
re-derives them when a system opens and when its budget changes.
Library loads are computed instead of being stored as a 1.0 mm²
placeholder. LoadConfigurationStatus therefore reports only what the
user can actually fix: missing length or current.

Also removes the dead cable.pro.* strings and folds three copies of the
mm²-to-AWG conversion into ElectricalCalculations.nearestAWG.
2026-08-18 10:59:16 +02:00

140 lines
5.2 KiB
Swift

//
// SystemEditorView.swift
// Cable
//
// Created by Stefan Lange-Hegermann on 16.09.25.
//
import SwiftUI
struct SystemEditorView: View {
@Binding var systemName: String
@Binding var location: String
@Binding var iconName: String
@Binding var colorName: String
@Binding var maxVoltageDropPercent: Double
@State private var tempLocation: String
private let systemIcons = [
"building.2", "house", "building", "tent", "sailboat",
"airplane", "ferry", "bus", "truck.box",
"server.rack", "externaldrive", "cpu", "gear", "wrench.adjustable", "hammer",
"lightbulb", "bolt", "powerplug", "battery.100","sun.max",
"engine.combustion", "fuelpump", "drop", "flame", "snowflake", "thermometer"
]
/// Targets offered as presets: ABYC E-11 uses 3 % for critical circuits and 10 % for
/// non-critical ones, 5 % is the default middle ground.
private let voltageDropTargets: [Double] = [3, 5, 10]
init(
systemName: Binding<String>,
location: Binding<String>,
iconName: Binding<String>,
colorName: Binding<String>,
maxVoltageDropPercent: Binding<Double>
) {
self._systemName = systemName
self._location = location
self._iconName = iconName
self._colorName = colorName
self._maxVoltageDropPercent = maxVoltageDropPercent
self._tempLocation = State(initialValue: location.wrappedValue)
}
var body: some View {
let editorTitle = String(localized: "editor.system.title", comment: "Title for the system editor")
let namePlaceholder = String(localized: "editor.system.name_field", comment: "Label for the system name text field")
let locationPlaceholder = String(localized: "editor.system.location.optional", comment: "Placeholder text shown when no location is specified")
ItemEditorView(
title: editorTitle,
nameFieldLabel: namePlaceholder,
previewSubtitle: tempLocation.isEmpty ? locationPlaceholder : tempLocation,
icons: systemIcons,
name: $systemName,
iconName: $iconName,
colorName: $colorName,
additionalFields: {
AnyView(
VStack(alignment: .leading, spacing: 12) {
TextField(locationPlaceholder, text: $tempLocation)
.autocapitalization(.words)
.onChange(of: tempLocation) { _, newValue in
location = newValue
}
VStack(alignment: .leading, spacing: 6) {
Text(
String(
localized: "editor.system.voltage_drop.label",
defaultValue: "Voltage drop budget"
)
)
.font(.subheadline.weight(.semibold))
Picker(
String(
localized: "editor.system.voltage_drop.label",
defaultValue: "Voltage drop budget"
),
selection: $maxVoltageDropPercent
) {
ForEach(voltageDropTargets, id: \.self) { target in
Text(String(format: "%.0f %%", target)).tag(target)
}
}
.pickerStyle(.segmented)
.accessibilityIdentifier("system-voltage-drop-picker")
Text(voltageDropHint)
.font(.caption)
.foregroundStyle(.secondary)
}
}
)
}
)
.onAppear {
tempLocation = location
}
}
private var voltageDropHint: String {
switch maxVoltageDropPercent {
case ..<4:
return String(
localized: "editor.system.voltage_drop.hint.critical",
defaultValue: "3 % — for critical circuits like navigation, bilge pumps and electronics."
)
case ..<6:
return String(
localized: "editor.system.voltage_drop.hint.standard",
defaultValue: "5 % — balanced default for mixed installations."
)
default:
return String(
localized: "editor.system.voltage_drop.hint.noncritical",
defaultValue: "10 % — for non-critical loads such as cabin lighting."
)
}
}
}
#Preview {
@Previewable @State var name = "My System"
@Previewable @State var location = "Main Building"
@Previewable @State var icon = "building.2"
@Previewable @State var color = "blue"
@Previewable @State var dropTarget = 5.0
return SystemEditorView(
systemName: $name,
location: $location,
iconName: $icon,
colorName: $color,
maxVoltageDropPercent: $dropTarget
)
}