feat: Adds thermal balance point, still need to implement economic balance point.
This commit is contained in:
@@ -112,13 +112,16 @@ extension ViewController: DependencyKey {
|
||||
case let .heatingBalancePoint(route):
|
||||
switch route {
|
||||
case let .index(mode: mode, heatLossMode: heatLossMode):
|
||||
return request.respond(HeatingBalancePointForm(mode: mode, heatLossMode: heatLossMode, response: nil))
|
||||
return request.respond(HeatingBalancePointForm(
|
||||
mode: mode,
|
||||
heatLossMode: heatLossMode,
|
||||
response: nil
|
||||
))
|
||||
case let .heatLossFields(mode: mode):
|
||||
logger.debug("Heat loss mode: \(mode)")
|
||||
return HeatingBalancePointForm.HeatLossFields(mode: mode)
|
||||
case .submit:
|
||||
// FIX:
|
||||
fatalError()
|
||||
case let .submit(request):
|
||||
let response = try await request.respond(logger: logger)
|
||||
return HeatingBalancePointResponse(response: response)
|
||||
}
|
||||
|
||||
case let .hvacSystemPerformance(route):
|
||||
|
||||
@@ -19,8 +19,18 @@ struct HeatingBalancePointForm: HTML, Sendable {
|
||||
}
|
||||
|
||||
var content: some HTML {
|
||||
FormHeader(label: "Balance Point - \(mode.label)", svg: .scale)
|
||||
// TODO: Toggle button
|
||||
div(.class("flex flex-wrap justify-between")) {
|
||||
FormHeader(label: "Balance Point - \(mode.label)", svg: .scale)
|
||||
|
||||
Toggle(
|
||||
isOn: mode == .thermal,
|
||||
onLabel: HeatingBalancePoint.Mode.thermal.label,
|
||||
onAttributes: .hxDefaults(get: .heatingBalancePoint(.index(mode: .thermal, heatLossMode: heatLossMode))),
|
||||
offLabel: HeatingBalancePoint.Mode.economic.label,
|
||||
offAttributes: .hxDefaults(get: .heatingBalancePoint(.index(mode: .economic, heatLossMode: heatLossMode)))
|
||||
)
|
||||
.attributes(.class("mb-6"))
|
||||
}
|
||||
|
||||
form(
|
||||
.hx.post(route: .heatingBalancePoint(.index)),
|
||||
@@ -31,7 +41,11 @@ struct HeatingBalancePointForm: HTML, Sendable {
|
||||
case .thermal:
|
||||
ThermalFields(heatLossMode: heatLossMode)
|
||||
case .economic:
|
||||
div {}
|
||||
div {
|
||||
// FIX:
|
||||
WarningBox("This is still under development and may not be fully functional.")
|
||||
.attributes(.class("mb-6"))
|
||||
}
|
||||
}
|
||||
|
||||
div {
|
||||
@@ -58,7 +72,7 @@ struct HeatingBalancePointForm: HTML, Sendable {
|
||||
div {
|
||||
div(.class("mb-4")) {
|
||||
h4(.class("text-lg font-bold")) { "Capacities" }
|
||||
p(.class("text-sm")) {
|
||||
p(.class("text-xs text-blue-500")) {
|
||||
"Entering known capacities gives better results, otherwise capacities will be estimated."
|
||||
}
|
||||
}
|
||||
@@ -109,16 +123,32 @@ struct HeatingBalancePointForm: HTML, Sendable {
|
||||
}
|
||||
|
||||
private var simplifiedFields: some HTML {
|
||||
LabeledContent(label: "Building Size (ft²)") {
|
||||
Input(id: "simplifiedHeatLoss", placeholder: "Square feet")
|
||||
.attributes(.type(.number), .min("1"), .step("0.5"), .required)
|
||||
div(.class("grid grid-cols-1 lg:grid-cols-3 gap-4")) {
|
||||
LabeledContent(label: "Building Size (ft²)") {
|
||||
Input(id: "simplifiedHeatLoss", placeholder: "Square feet")
|
||||
.attributes(.type(.number), .min("1"), .step("0.5"), .required)
|
||||
}
|
||||
div {
|
||||
InputLabel(for: "climateZone") { "Climate Zone" }
|
||||
Select(for: ClimateZone.self, id: "climateZone") { $0.rawValue }
|
||||
}
|
||||
LabeledContent(label: "Outdoor Design Temperature (°F)") {
|
||||
Input(id: "heatingDesignTemperature", placeholder: "Design temperature (optional)")
|
||||
.attributes(.type(.number), .step("0.5"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var knownFields: some HTML {
|
||||
LabeledContent(label: "Heat Loss (BTU/h)") {
|
||||
Input(id: "knownHeatLoss", placeholder: "Heat loss")
|
||||
.attributes(.type(.number), .min("1"), .step("0.5"), .required)
|
||||
div(.class("grid grid-cols-1 lg:grid-cols-2 gap-4")) {
|
||||
LabeledContent(label: "Heat Loss (BTU/h)") {
|
||||
Input(id: "knownHeatLoss", placeholder: "Heat loss")
|
||||
.attributes(.type(.number), .min("1"), .step("0.5"), .required)
|
||||
}
|
||||
LabeledContent(label: "Outdoor Design Temperature (°F)") {
|
||||
Input(id: "heatingDesignTemperature", placeholder: "Design temperature")
|
||||
.attributes(.type(.number), .step("0.5"), .required)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,14 +160,92 @@ struct HeatingBalancePointResponse: HTML, Sendable {
|
||||
|
||||
let response: HeatingBalancePoint.Response
|
||||
|
||||
var content: some HTML {
|
||||
ResultContainer(reset: .heatingBalancePoint(.index(mode: response.mode, heatLossMode: nil))) {
|
||||
div(
|
||||
.class("""
|
||||
w-full rounded-xl shadow-xl bg-blue-100 text-blue-600 border border-blue-600 py-4
|
||||
""")
|
||||
) {
|
||||
div(.class("flex")) {
|
||||
SVG(.scale, color: "text-blue-600")
|
||||
.attributes(.class("px-4"))
|
||||
p(.class("font-medium")) {
|
||||
"\(response.mode.label) Balance Point"
|
||||
}
|
||||
}
|
||||
switch response {
|
||||
case let .thermal(result):
|
||||
thermalResult(result)
|
||||
}
|
||||
}
|
||||
|
||||
WarningBox(warnings: response.warnings)
|
||||
}
|
||||
}
|
||||
|
||||
func thermalResult(_ result: HeatingBalancePoint.Response.Thermal) -> some HTML {
|
||||
div {
|
||||
VerticalGroup(
|
||||
label: "Balance Point",
|
||||
value: "\(double: result.balancePointTemperature, fractionDigits: 1)",
|
||||
valueLabel: "°F"
|
||||
)
|
||||
.attributes(.class("mb-8"))
|
||||
|
||||
div(.class("grid grid-cols-2 space-y-6")) {
|
||||
div {
|
||||
VerticalGroup(
|
||||
label: "Heat Loss - \(result.heatLossMode.label)",
|
||||
value: "\(double: result.heatLoss, fractionDigits: 0)",
|
||||
valueLabel: "BTU/h"
|
||||
)
|
||||
}
|
||||
div {
|
||||
VerticalGroup(
|
||||
label: "Heating Design Temperature",
|
||||
value: "\(double: result.heatingDesignTemperature, fractionDigits: 0)",
|
||||
valueLabel: "°F"
|
||||
)
|
||||
}
|
||||
div {
|
||||
VerticalGroup(
|
||||
label: "Capacity @ 47°",
|
||||
value: "\(double: result.capacityAt47, fractionDigits: 0)",
|
||||
valueLabel: "BTU/h"
|
||||
)
|
||||
}
|
||||
div {
|
||||
VerticalGroup(
|
||||
label: "Capacity @ 17°",
|
||||
value: "\(double: result.capacityAt17, fractionDigits: 0)",
|
||||
valueLabel: "BTU/h"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private extension HeatingBalancePoint.Mode {
|
||||
|
||||
var label: String { rawValue.capitalized }
|
||||
|
||||
}
|
||||
|
||||
private extension HeatingBalancePoint.HeatLoss.Mode {
|
||||
var label: String { rawValue.capitalized }
|
||||
}
|
||||
|
||||
private extension HeatingBalancePoint.Response {
|
||||
var mode: HeatingBalancePoint.Mode {
|
||||
switch self {
|
||||
case .thermal: return .thermal
|
||||
}
|
||||
}
|
||||
|
||||
var warnings: [String] {
|
||||
switch self {
|
||||
case let .thermal(result): return result.warnings
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user