feat: Begins room pressure calculations
This commit is contained in:
@@ -25,6 +25,8 @@ extension ApiController: DependencyKey {
|
||||
@Dependency(\.psychrometricClient) var psychrometricClient
|
||||
|
||||
return .init(json: { route, logger in
|
||||
logger.debug("API Route: \(route)")
|
||||
|
||||
switch route {
|
||||
case let .calculateDehumidifierSize(request):
|
||||
logger.debug("Calculating dehumidifier size: \(request)")
|
||||
@@ -37,6 +39,11 @@ extension ApiController: DependencyKey {
|
||||
case let .calculateMoldRisk(request):
|
||||
logger.debug("Calculating mold risk: \(request)")
|
||||
return try await psychrometricClient.respond(request, logger)
|
||||
|
||||
case let .calculateRoomPressure(request):
|
||||
logger.debug("Calculating room pressure: \(request)")
|
||||
// FIX:
|
||||
fatalError()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
76
Sources/ApiController/Extensions/RoomPressure.swift
Normal file
76
Sources/ApiController/Extensions/RoomPressure.swift
Normal file
@@ -0,0 +1,76 @@
|
||||
import Foundation
|
||||
import Logging
|
||||
import OrderedCollections
|
||||
import Routes
|
||||
|
||||
public extension RoomPressure.Request {
|
||||
|
||||
static let pascalToIWCMultiplier = 0.004014
|
||||
static let targetVelocity = 400.0
|
||||
static let maxVelocity = 600.0
|
||||
static let minGrilleFreeArea = 0.7
|
||||
static let standardDuctSizes = OrderedSet([4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 18, 20])
|
||||
static let standardGrilleHeights = OrderedSet([4, 6, 8, 10, 12, 14, 20])
|
||||
static let standardGrilleSizes = [
|
||||
4: OrderedSet([8, 10, 12, 14]),
|
||||
6: OrderedSet([8, 10, 12, 14]),
|
||||
8: OrderedSet([10, 12, 14]),
|
||||
10: OrderedSet([10]),
|
||||
12: OrderedSet([12]),
|
||||
14: OrderedSet([14])
|
||||
]
|
||||
|
||||
// OrderedSet([
|
||||
// (width: 8, height: 4),
|
||||
// (width: 10, height: 4),
|
||||
// (width: 12, height: 4),
|
||||
// (width: 14, height: 4),
|
||||
// (width: 8, height: 6),
|
||||
// (width: 10, height: 6),
|
||||
// (width: 12, height: 6),
|
||||
// (width: 14, height: 6),
|
||||
// (width: 10, height: 8),
|
||||
// (width: 12, height: 8),
|
||||
// (width: 14, height: 8),
|
||||
// (width: 10, height: 10),
|
||||
// (width: 12, height: 12),
|
||||
// (width: 14, height: 14)
|
||||
// ])
|
||||
|
||||
func respond(logger: Logger) async throws -> RoomPressure.Response {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
private func getStandardDuctSize(for calculatedSize: Int) -> Int {
|
||||
Self.standardDuctSizes.sorted()
|
||||
.first { $0 >= calculatedSize }
|
||||
?? 20
|
||||
}
|
||||
|
||||
private func getStandardGrilleSize(requiredArea: Double, selectedHeight: Int) throws -> (width: Int, height: Int) {
|
||||
guard let availableSizes = Self.standardGrilleSizes[selectedHeight] else {
|
||||
throw RoomPressureError.invalidPreferredHeight
|
||||
}
|
||||
|
||||
if let width = availableSizes.first(where: {
|
||||
Double($0) * Double(selectedHeight) * Self.minGrilleFreeArea >= (requiredArea / Self.minGrilleFreeArea)
|
||||
}) {
|
||||
return (width, selectedHeight)
|
||||
}
|
||||
|
||||
// If no width matches return largest for the selectedHeight.
|
||||
if let largestSize = availableSizes.last { return (largestSize, selectedHeight) }
|
||||
return (14, selectedHeight)
|
||||
}
|
||||
|
||||
// Calculate door leakage area in sq. ft.
|
||||
private func calculateDoorLeakageArea(doorWidth: Double, doorHeight: Double, doorUndercut: Double) -> Double {
|
||||
let doorLeakageArea = (doorWidth * doorUndercut) / 144.0
|
||||
let doorPerimiterLeakage = ((2 * doorHeight + doorWidth) * 0.125) / 144.0
|
||||
return doorLeakageArea + doorPerimiterLeakage
|
||||
}
|
||||
|
||||
enum RoomPressureError: Error {
|
||||
case invalidPreferredHeight
|
||||
}
|
||||
}
|
||||
@@ -119,7 +119,7 @@ public enum RoomPressure {
|
||||
case ten = 10
|
||||
case twelve = 12
|
||||
case fourteen = 14
|
||||
case twenty = 20
|
||||
// case twenty = 20
|
||||
|
||||
public var label: String { "\(rawValue)\"" }
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ public extension SiteRoute {
|
||||
case calculateDehumidifierSize(DehumidifierSize.Request)
|
||||
case calculateHVACSystemPerformance(HVACSystemPerformance.Request)
|
||||
case calculateMoldRisk(MoldRisk.Request)
|
||||
case calculateRoomPressure(RoomPressure.Request)
|
||||
|
||||
static let rootPath = Path { "api"; "v1" }
|
||||
|
||||
@@ -45,6 +46,16 @@ public extension SiteRoute {
|
||||
Method.post
|
||||
Body(.json(MoldRisk.Request.self))
|
||||
}
|
||||
Route(.case(Self.calculateRoomPressure)) {
|
||||
Path { "api"; "v1"; "calculateRoomPressure" }
|
||||
Method.post
|
||||
OneOf {
|
||||
Body(.json(RoomPressure.Request.KnownAirflow.self))
|
||||
.map(.case(RoomPressure.Request.knownAirflow))
|
||||
Body(.json(RoomPressure.Request.MeasuredPressure.self))
|
||||
.map(.case(RoomPressure.Request.measuredPressure))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -164,6 +175,7 @@ public extension SiteRoute {
|
||||
|
||||
public enum RoomPressure: Equatable, Sendable {
|
||||
case index(mode: Routes.RoomPressure.Mode? = nil)
|
||||
case submit(Routes.RoomPressure.Request)
|
||||
|
||||
public static var index: Self { .index() }
|
||||
|
||||
@@ -177,6 +189,38 @@ public extension SiteRoute {
|
||||
Optionally { Field("form") { Routes.RoomPressure.Mode.parser() } }
|
||||
}
|
||||
}
|
||||
Route(.case(Self.submit)) {
|
||||
Path { rootPath }
|
||||
Method.post
|
||||
Body {
|
||||
OneOf {
|
||||
FormData {
|
||||
Field("targetRoomPressure") { Double.parser() }
|
||||
Field("doorWidth") { Double.parser() }
|
||||
Field("doorHeight") { Double.parser() }
|
||||
Field("doorUndercut") { Double.parser() }
|
||||
Field("supplyAirflow") { Double.parser() }
|
||||
Field("preferredGrilleHeight") {
|
||||
Routes.RoomPressure.CommonReturnGrilleHeight.parser()
|
||||
}
|
||||
}
|
||||
.map(.memberwise(Routes.RoomPressure.Request.KnownAirflow.init))
|
||||
.map(.case(Routes.RoomPressure.Request.knownAirflow))
|
||||
|
||||
FormData {
|
||||
Field("measuredRoomPressure") { Double.parser() }
|
||||
Field("doorWidth") { Double.parser() }
|
||||
Field("doorHeight") { Double.parser() }
|
||||
Field("doorUndercut") { Double.parser() }
|
||||
Field("preferredGrilleHeight") {
|
||||
Routes.RoomPressure.CommonReturnGrilleHeight.parser()
|
||||
}
|
||||
}
|
||||
.map(.memberwise(Routes.RoomPressure.Request.MeasuredPressure.init))
|
||||
.map(.case(Routes.RoomPressure.Request.measuredPressure))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,6 +107,10 @@ extension ViewController: DependencyKey {
|
||||
switch route {
|
||||
case let .index(mode):
|
||||
return request.respond(RoomPressureForm(mode: mode, response: nil))
|
||||
|
||||
// FIX:
|
||||
case .submit:
|
||||
fatalError()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
5
justfile
5
justfile
@@ -1,6 +1,5 @@
|
||||
docker_image := "hvac-toolbox"
|
||||
docker_tag := "latest"
|
||||
do_registery := "registry.digitalocean.com/swift-hvac-toolbox"
|
||||
|
||||
build-docker:
|
||||
@docker build -t {{docker_image}}:{{docker_tag}} .
|
||||
@@ -17,7 +16,3 @@ run-css:
|
||||
|
||||
clean:
|
||||
@rm -rf .build
|
||||
|
||||
push-image tag="prod":
|
||||
@docker tag {{docker_image}}:{{docker_tag}} {{do_registery}}/{{docker_image}}:{{tag}}
|
||||
@docker push {{do_registery}}/{{docker_image}}:{{tag}}
|
||||
|
||||
Reference in New Issue
Block a user