feat: Initial duct sizing view and calculations, need to add supply and return trunk sizing.
This commit is contained in:
@@ -22,6 +22,30 @@ extension DatabaseClient.Projects {
|
||||
}
|
||||
}
|
||||
|
||||
extension DatabaseClient {
|
||||
|
||||
func designFrictionRate(
|
||||
projectID: Project.ID
|
||||
) async throws -> (EquipmentInfo, EffectiveLength.MaxContainer, Double)? {
|
||||
guard let equipmentInfo = try await equipment.fetch(projectID) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let equivalentLengths = try await effectiveLength.fetchMax(projectID)
|
||||
guard let tel = equivalentLengths.total else { return nil }
|
||||
|
||||
let componentLosses = try await componentLoss.fetch(projectID)
|
||||
guard componentLosses.count > 0 else { return nil }
|
||||
|
||||
let availableStaticPressure =
|
||||
equipmentInfo.staticPressure - componentLosses.totalComponentPressureLoss
|
||||
|
||||
let designFrictionRate = (availableStaticPressure * 100) / tel
|
||||
|
||||
return (equipmentInfo, equivalentLengths, designFrictionRate)
|
||||
}
|
||||
}
|
||||
|
||||
extension DatabaseClient.ComponentLoss {
|
||||
|
||||
func createDefaults(projectID: Project.ID) async throws {
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import Logging
|
||||
import ManualDClient
|
||||
import ManualDCore
|
||||
|
||||
extension ManualDClient {
|
||||
|
||||
func calculate(
|
||||
rooms: [Room],
|
||||
designFrictionRateResult: (EquipmentInfo, EffectiveLength.MaxContainer, Double)?,
|
||||
projectSHR: Double?,
|
||||
logger: Logger? = nil
|
||||
) async throws -> [DuctSizing.RoomContainer] {
|
||||
guard let designFrictionRateResult else { return [] }
|
||||
let equipmentInfo = designFrictionRateResult.0
|
||||
let effectiveLengths = designFrictionRateResult.1
|
||||
let designFrictionRate = designFrictionRateResult.2
|
||||
|
||||
guard let maxSupply = effectiveLengths.supply else { return [] }
|
||||
guard let maxReturn = effectiveLengths.return else { return [] }
|
||||
|
||||
let ductRooms = try await self.calculateSizes(
|
||||
rooms: rooms,
|
||||
equipmentInfo: equipmentInfo,
|
||||
maxSupplyLength: maxSupply,
|
||||
maxReturnLength: maxReturn,
|
||||
designFrictionRate: designFrictionRate,
|
||||
projectSHR: projectSHR ?? 1.0,
|
||||
logger: logger
|
||||
)
|
||||
|
||||
logger?.debug("Rooms: \(ductRooms)")
|
||||
|
||||
return ductRooms
|
||||
|
||||
}
|
||||
}
|
||||
@@ -117,6 +117,8 @@ extension SiteRoute.View.ProjectRoute {
|
||||
}
|
||||
case .componentLoss(let route):
|
||||
return try await route.renderView(on: request, projectID: projectID)
|
||||
case .ductSizing(let route):
|
||||
return try await route.renderView(on: request, projectID: projectID)
|
||||
case .equipment(let route):
|
||||
return try await route.renderView(on: request, projectID: projectID)
|
||||
case .equivalentLength(let route):
|
||||
@@ -335,6 +337,20 @@ extension SiteRoute.View.ProjectRoute.EquivalentLengthRoute {
|
||||
}
|
||||
}
|
||||
|
||||
extension SiteRoute.View.ProjectRoute.DuctSizingRoute {
|
||||
|
||||
func renderView(on request: ViewController.Request, projectID: Project.ID) async throws
|
||||
-> AnySendableHTML
|
||||
{
|
||||
switch self {
|
||||
case .index:
|
||||
return request.view {
|
||||
ProjectView(projectID: projectID, activeTab: .ductSizing, logger: request.logger)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func _render<C: HTML>(
|
||||
isHtmxRequest: Bool,
|
||||
active activeTab: SiteRoute.View.ProjectRoute.DetailRoute.Tab = .rooms,
|
||||
|
||||
@@ -20,10 +20,6 @@ struct ComponentLossForm: HTML, Sendable {
|
||||
for: .project(.detail(projectID, .componentLoss(.index)))
|
||||
)
|
||||
.appendingPath(componentLoss?.id)
|
||||
// if let componentLoss {
|
||||
// return baseRoute.appending("/\(componentLoss.id)")
|
||||
// }
|
||||
// return baseRoute
|
||||
}
|
||||
|
||||
var body: some HTML {
|
||||
|
||||
81
Sources/ViewController/Views/DuctSizing/DuctSizingView.swift
Normal file
81
Sources/ViewController/Views/DuctSizing/DuctSizingView.swift
Normal file
@@ -0,0 +1,81 @@
|
||||
import Elementary
|
||||
import ElementaryHTMX
|
||||
import ManualDCore
|
||||
import Styleguide
|
||||
|
||||
// TODO: Add error text if prior steps are not completed.
|
||||
|
||||
struct DuctSizingView: HTML, Sendable {
|
||||
|
||||
let rooms: [DuctSizing.RoomContainer]
|
||||
|
||||
var body: some HTML {
|
||||
div {
|
||||
h1(.class("text-2xl py-4")) { "Duct Sizes" }
|
||||
|
||||
div(.class("overflow-x-auto")) {
|
||||
table(.class("table table-zebra")) {
|
||||
thead {
|
||||
tr(.class("text-xl text-gray-400 font-bold")) {
|
||||
th { "ID" }
|
||||
th { "Name" }
|
||||
th { "H-BTU" }
|
||||
th { "C-BTU" }
|
||||
th(.class("hidden 2xl:table-cell")) { "Htg CFM" }
|
||||
th(.class("hidden 2xl:table-cell")) { "Clg CFM" }
|
||||
th { "Dsn CFM" }
|
||||
th(.class("hidden xl:table-cell")) { "Round Size" }
|
||||
th { "Velocity" }
|
||||
th { "Final Size" }
|
||||
th { "Flex Size" }
|
||||
}
|
||||
}
|
||||
tbody {
|
||||
for room in rooms {
|
||||
RoomRow(room: room)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct RoomRow: HTML, Sendable {
|
||||
let room: DuctSizing.RoomContainer
|
||||
|
||||
var body: some HTML<HTMLTag.tr> {
|
||||
tr(.class("text-lg")) {
|
||||
td { room.registerID }
|
||||
td { room.roomName }
|
||||
td { Number(room.heatingLoad, digits: 0) }
|
||||
td { Number(room.coolingLoad, digits: 0) }
|
||||
td(.class("hidden 2xl:table-cell")) { Number(room.heatingCFM, digits: 0) }
|
||||
td(.class("hidden 2xl:table-cell")) { Number(room.coolingCFM, digits: 0) }
|
||||
td {
|
||||
Number(room.designCFM.value, digits: 0)
|
||||
.attributes(
|
||||
.class("badge badge-outline badge-\(room.designCFM.color) text-xl font-bold"))
|
||||
}
|
||||
td(.class("hidden xl:table-cell")) { Number(room.roundSize, digits: 0) }
|
||||
td { Number(room.velocity) }
|
||||
td {
|
||||
Number(room.finalSize)
|
||||
.attributes(.class("badge badge-outline badge-secondary text-xl font-bold"))
|
||||
}
|
||||
td {
|
||||
Number(room.flexSize)
|
||||
.attributes(.class("badge badge-outline badge-primary text-xl font-bold"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension DuctSizing.DesignCFM {
|
||||
var color: String {
|
||||
switch self {
|
||||
case .heating: return "error"
|
||||
case .cooling: return "info"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,21 +2,27 @@ import DatabaseClient
|
||||
import Dependencies
|
||||
import Elementary
|
||||
import ElementaryHTMX
|
||||
import Logging
|
||||
import ManualDClient
|
||||
import ManualDCore
|
||||
import Styleguide
|
||||
|
||||
struct ProjectView: HTML, Sendable {
|
||||
@Dependency(\.database) var database
|
||||
@Dependency(\.manualD) var manualD
|
||||
|
||||
let projectID: Project.ID
|
||||
let activeTab: SiteRoute.View.ProjectRoute.DetailRoute.Tab
|
||||
let logger: Logger?
|
||||
|
||||
init(
|
||||
projectID: Project.ID,
|
||||
activeTab: SiteRoute.View.ProjectRoute.DetailRoute.Tab
|
||||
activeTab: SiteRoute.View.ProjectRoute.DetailRoute.Tab,
|
||||
logger: Logger? = nil
|
||||
) {
|
||||
self.projectID = projectID
|
||||
self.activeTab = activeTab
|
||||
self.logger = logger
|
||||
}
|
||||
|
||||
var body: some HTML {
|
||||
@@ -61,7 +67,15 @@ struct ProjectView: HTML, Sendable {
|
||||
projectID: projectID
|
||||
)
|
||||
case .ductSizing:
|
||||
div { "FIX ME!" }
|
||||
try await DuctSizingView(
|
||||
rooms: manualD.calculate(
|
||||
rooms: database.rooms.fetch(projectID),
|
||||
designFrictionRateResult: database.designFrictionRate(projectID: projectID),
|
||||
projectSHR: database.projects.getSensibleHeatRatio(projectID),
|
||||
logger: logger
|
||||
)
|
||||
)
|
||||
// div { "FIX ME!" }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -178,7 +192,11 @@ extension ProjectView {
|
||||
}
|
||||
li(.class("w-full")) {
|
||||
row(
|
||||
title: "Duct Sizes", icon: .wind, href: "#", isComplete: false, hideIsComplete: true
|
||||
title: "Duct Sizes",
|
||||
icon: .wind,
|
||||
route: .project(.detail(projectID, .ductSizing(.index))),
|
||||
isComplete: false,
|
||||
hideIsComplete: true
|
||||
)
|
||||
.attributes(.class("btn-active"), when: active == .ductSizing)
|
||||
}
|
||||
|
||||
@@ -8,20 +8,21 @@ import Styleguide
|
||||
// TODO: Need to hold the project ID in hidden input field.
|
||||
struct RoomForm: HTML, Sendable {
|
||||
|
||||
static let id = "roomForm"
|
||||
static func id(_ room: Room? = nil) -> String {
|
||||
let baseId = "roomForm"
|
||||
guard let room else { return baseId }
|
||||
return baseId.appending("_\(room.id.idString)")
|
||||
}
|
||||
|
||||
let id: String
|
||||
let dismiss: Bool
|
||||
let projectID: Project.ID
|
||||
let room: Room?
|
||||
|
||||
init(
|
||||
id: String = Self.id,
|
||||
dismiss: Bool,
|
||||
projectID: Project.ID,
|
||||
room: Room? = nil
|
||||
) {
|
||||
self.id = id
|
||||
self.dismiss = dismiss
|
||||
self.projectID = projectID
|
||||
self.room = room
|
||||
@@ -35,7 +36,7 @@ struct RoomForm: HTML, Sendable {
|
||||
}
|
||||
|
||||
var body: some HTML {
|
||||
ModalForm(id: id, dismiss: dismiss) {
|
||||
ModalForm(id: Self.id(room), dismiss: dismiss) {
|
||||
h1(.class("text-3xl font-bold pb-6")) { "Room" }
|
||||
form(
|
||||
.class("modal-backdrop"),
|
||||
|
||||
@@ -22,7 +22,7 @@ struct RoomsView: HTML, Sendable {
|
||||
) {
|
||||
div(.class("flex me-4")) {
|
||||
PlusButton()
|
||||
.attributes(.showModal(id: RoomForm.id))
|
||||
.attributes(.showModal(id: RoomForm.id()))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -134,12 +134,11 @@ struct RoomsView: HTML, Sendable {
|
||||
EditButton()
|
||||
.attributes(
|
||||
.class("join-item"),
|
||||
.showModal(id: "roomForm_\(room.name)")
|
||||
.showModal(id: RoomForm.id(room))
|
||||
)
|
||||
}
|
||||
}
|
||||
RoomForm(
|
||||
id: "roomForm_\(room.name)",
|
||||
dismiss: true,
|
||||
projectID: room.projectID,
|
||||
room: room
|
||||
|
||||
Reference in New Issue
Block a user