diff --git a/Package.swift b/Package.swift index 4a0d97e..1e29c81 100644 --- a/Package.swift +++ b/Package.swift @@ -8,6 +8,7 @@ let package = Package( .executable(name: "App", targets: ["App"]), .library(name: "ApiController", targets: ["ApiController"]), .library(name: "DatabaseClient", targets: ["DatabaseClient"]), + .library(name: "ProjectClient", targets: ["ProjectClient"]), .library(name: "ManualDCore", targets: ["ManualDCore"]), .library(name: "ManualDClient", targets: ["ManualDClient"]), .library(name: "Styleguide", targets: ["Styleguide"]), @@ -63,6 +64,13 @@ let package = Package( .product(name: "Vapor", package: "vapor"), ] ), + .target( + name: "ProjectClient", + dependencies: [ + .target(name: "DatabaseClient"), + .target(name: "ManualDClient"), + ] + ), .target( name: "ManualDCore", dependencies: [ @@ -105,6 +113,7 @@ let package = Package( name: "ViewController", dependencies: [ .target(name: "DatabaseClient"), + .target(name: "ProjectClient"), .target(name: "ManualDClient"), .target(name: "ManualDCore"), .target(name: "Styleguide"), diff --git a/Sources/DatabaseClient/Projects.swift b/Sources/DatabaseClient/Projects.swift index e604a86..0b746f2 100644 --- a/Sources/DatabaseClient/Projects.swift +++ b/Sources/DatabaseClient/Projects.swift @@ -73,10 +73,16 @@ extension DatabaseClient.Projects: TestDependencyKey { ) }, getSensibleHeatRatio: { id in - guard let model = try await ProjectModel.find(id, on: database) else { + guard + let shr = try await ProjectModel.query(on: database) + .field(\.$id) + .field(\.$sensibleHeatRatio) + .filter(\.$id == id) + .first() + else { throw NotFoundError() } - return model.sensibleHeatRatio + return shr.sensibleHeatRatio }, fetch: { userID, request in try await ProjectModel.query(on: database) diff --git a/Sources/ManualDClient/ManualDClient.swift b/Sources/ManualDClient/ManualDClient.swift index 4522aef..79ef91c 100644 --- a/Sources/ManualDClient/ManualDClient.swift +++ b/Sources/ManualDClient/ManualDClient.swift @@ -3,6 +3,13 @@ import DependenciesMacros import Logging import ManualDCore +extension DependencyValues { + public var manualD: ManualDClient { + get { self[ManualDClient.self] } + set { self[ManualDClient.self] = newValue } + } +} + @DependencyClient public struct ManualDClient: Sendable { public var ductSize: @Sendable (DuctSizeRequest) async throws -> DuctSizeResponse @@ -11,144 +18,155 @@ public struct ManualDClient: Sendable { public var equivalentRectangularDuct: @Sendable (EquivalentRectangularDuctRequest) async throws -> EquivalentRectangularDuctResponse - public func calculateSizes( - rooms: [Room], - trunks: [DuctSizing.TrunkSize], - equipmentInfo: EquipmentInfo, - maxSupplyLength: EffectiveLength, - maxReturnLength: EffectiveLength, - designFrictionRate: Double, - projectSHR: Double, - logger: Logger? = nil - ) async throws -> (rooms: [DuctSizing.RoomContainer], trunks: [DuctSizing.TrunkContainer]) { - try await ( - calculateSizes( - rooms: rooms, equipmentInfo: equipmentInfo, - maxSupplyLength: maxSupplyLength, maxReturnLength: maxReturnLength, - designFrictionRate: designFrictionRate, projectSHR: projectSHR - ), - calculateSizes( - rooms: rooms, trunks: trunks, equipmentInfo: equipmentInfo, - maxSupplyLength: maxSupplyLength, maxReturnLength: maxReturnLength, - designFrictionRate: designFrictionRate, projectSHR: projectSHR) - ) - } + // TODO: add (Project.ID) async throws -> ProjectResponse - func calculateSizes( - rooms: [Room], - equipmentInfo: EquipmentInfo, - maxSupplyLength: EffectiveLength, - maxReturnLength: EffectiveLength, - designFrictionRate: Double, - projectSHR: Double, - logger: Logger? = nil - ) async throws -> [DuctSizing.RoomContainer] { - - var retval: [DuctSizing.RoomContainer] = [] - let totalHeatingLoad = rooms.totalHeatingLoad - let totalCoolingSensible = rooms.totalCoolingSensible(shr: projectSHR) - - for room in rooms { - let heatingLoad = room.heatingLoadPerRegister - let coolingLoad = room.coolingSensiblePerRegister(projectSHR: projectSHR) - let heatingPercent = heatingLoad / totalHeatingLoad - let coolingPercent = coolingLoad / totalCoolingSensible - let heatingCFM = heatingPercent * Double(equipmentInfo.heatingCFM) - let coolingCFM = coolingPercent * Double(equipmentInfo.coolingCFM) - let designCFM = DuctSizing.DesignCFM(heating: heatingCFM, cooling: coolingCFM) - let sizes = try await self.ductSize( - .init(designCFM: Int(designCFM.value), frictionRate: designFrictionRate) - ) - - for n in 1...room.registerCount { - - var rectangularWidth: Int? = nil - let rectangularSize = room.rectangularSizes? - .first(where: { $0.register == nil || $0.register == n }) - - if let rectangularSize { - let response = try await self.equivalentRectangularDuct( - .init(round: sizes.finalSize, height: rectangularSize.height) - ) - rectangularWidth = response.width - } - - retval.append( - .init( - roomID: room.id, - roomName: "\(room.name)-\(n)", - roomRegister: n, - heatingLoad: heatingLoad, - coolingLoad: coolingLoad, - heatingCFM: heatingCFM, - coolingCFM: coolingCFM, - designCFM: designCFM, - roundSize: sizes.ductulatorSize, - finalSize: sizes.finalSize, - velocity: sizes.velocity, - flexSize: sizes.flexSize, - rectangularSize: rectangularSize, - rectangularWidth: rectangularWidth - ) - ) - } - } - - return retval - } - - func calculateSizes( - rooms: [Room], - trunks: [DuctSizing.TrunkSize], - equipmentInfo: EquipmentInfo, - maxSupplyLength: EffectiveLength, - maxReturnLength: EffectiveLength, - designFrictionRate: Double, - projectSHR: Double, - logger: Logger? = nil - ) async throws -> [DuctSizing.TrunkContainer] { - - var retval = [DuctSizing.TrunkContainer]() - let totalHeatingLoad = rooms.totalHeatingLoad - let totalCoolingSensible = rooms.totalCoolingSensible(shr: projectSHR) - - for trunk in trunks { - let heatingLoad = trunk.totalHeatingLoad - let coolingLoad = trunk.totalCoolingSensible(projectSHR: projectSHR) - let heatingPercent = heatingLoad / totalHeatingLoad - let coolingPercent = coolingLoad / totalCoolingSensible - let heatingCFM = heatingPercent * Double(equipmentInfo.heatingCFM) - let coolingCFM = coolingPercent * Double(equipmentInfo.coolingCFM) - let designCFM = DuctSizing.DesignCFM(heating: heatingCFM, cooling: coolingCFM) - let sizes = try await self.ductSize( - .init(designCFM: Int(designCFM.value), frictionRate: designFrictionRate) - ) - var width: Int? = nil - if let height = trunk.height { - let rectangularSize = try await self.equivalentRectangularDuct( - .init(round: sizes.finalSize, height: height) - ) - width = rectangularSize.width - } - - retval.append( - .init( - trunk: trunk, - ductSize: .init( - designCFM: designCFM, - roundSize: sizes.ductulatorSize, - finalSize: sizes.finalSize, - velocity: sizes.velocity, - flexSize: sizes.flexSize, - height: trunk.height, - width: width - ) - ) - ) - } - - return retval - } + // TODO: Move to helpers. + // public func calculateSizes( + // rooms: [Room], + // trunks: [DuctSizing.TrunkSize], + // equipmentInfo: EquipmentInfo, + // maxSupplyLength: EffectiveLength, + // maxReturnLength: EffectiveLength, + // designFrictionRate: Double, + // projectSHR: Double, + // logger: Logger? = nil + // ) async throws -> ProjectResponse { + // try await .init( + // rooms: calculateRoomSizes( + // rooms: rooms, + // equipmentInfo: equipmentInfo, + // maxSupplyLength: maxSupplyLength, + // maxReturnLength: maxReturnLength, + // designFrictionRate: designFrictionRate, + // projectSHR: projectSHR + // ), + // trunks: calculateTrunkSizes( + // rooms: rooms, + // trunks: trunks, + // equipmentInfo: equipmentInfo, + // maxSupplyLength: maxSupplyLength, + // maxReturnLength: maxReturnLength, + // designFrictionRate: designFrictionRate, + // projectSHR: projectSHR + // ) + // ) + // } + // + // func calculateRoomSizes( + // rooms: [Room], + // equipmentInfo: EquipmentInfo, + // maxSupplyLength: EffectiveLength, + // maxReturnLength: EffectiveLength, + // designFrictionRate: Double, + // projectSHR: Double, + // logger: Logger? = nil + // ) async throws -> [DuctSizing.RoomContainer] { + // + // var retval: [DuctSizing.RoomContainer] = [] + // let totalHeatingLoad = rooms.totalHeatingLoad + // let totalCoolingSensible = rooms.totalCoolingSensible(shr: projectSHR) + // + // for room in rooms { + // let heatingLoad = room.heatingLoadPerRegister + // let coolingLoad = room.coolingSensiblePerRegister(projectSHR: projectSHR) + // let heatingPercent = heatingLoad / totalHeatingLoad + // let coolingPercent = coolingLoad / totalCoolingSensible + // let heatingCFM = heatingPercent * Double(equipmentInfo.heatingCFM) + // let coolingCFM = coolingPercent * Double(equipmentInfo.coolingCFM) + // let designCFM = DuctSizing.DesignCFM(heating: heatingCFM, cooling: coolingCFM) + // let sizes = try await self.ductSize( + // .init(designCFM: Int(designCFM.value), frictionRate: designFrictionRate) + // ) + // + // for n in 1...room.registerCount { + // + // var rectangularWidth: Int? = nil + // let rectangularSize = room.rectangularSizes? + // .first(where: { $0.register == nil || $0.register == n }) + // + // if let rectangularSize { + // let response = try await self.equivalentRectangularDuct( + // .init(round: sizes.finalSize, height: rectangularSize.height) + // ) + // rectangularWidth = response.width + // } + // + // retval.append( + // .init( + // roomID: room.id, + // roomName: "\(room.name)-\(n)", + // roomRegister: n, + // heatingLoad: heatingLoad, + // coolingLoad: coolingLoad, + // heatingCFM: heatingCFM, + // coolingCFM: coolingCFM, + // designCFM: designCFM, + // roundSize: sizes.ductulatorSize, + // finalSize: sizes.finalSize, + // velocity: sizes.velocity, + // flexSize: sizes.flexSize, + // rectangularSize: rectangularSize, + // rectangularWidth: rectangularWidth + // ) + // ) + // } + // } + // + // return retval + // } + // + // func calculateTrunkSizes( + // rooms: [Room], + // trunks: [DuctSizing.TrunkSize], + // equipmentInfo: EquipmentInfo, + // maxSupplyLength: EffectiveLength, + // maxReturnLength: EffectiveLength, + // designFrictionRate: Double, + // projectSHR: Double, + // logger: Logger? = nil + // ) async throws -> [DuctSizing.TrunkContainer] { + // + // var retval = [DuctSizing.TrunkContainer]() + // let totalHeatingLoad = rooms.totalHeatingLoad + // let totalCoolingSensible = rooms.totalCoolingSensible(shr: projectSHR) + // + // for trunk in trunks { + // let heatingLoad = trunk.totalHeatingLoad + // let coolingLoad = trunk.totalCoolingSensible(projectSHR: projectSHR) + // let heatingPercent = heatingLoad / totalHeatingLoad + // let coolingPercent = coolingLoad / totalCoolingSensible + // let heatingCFM = heatingPercent * Double(equipmentInfo.heatingCFM) + // let coolingCFM = coolingPercent * Double(equipmentInfo.coolingCFM) + // let designCFM = DuctSizing.DesignCFM(heating: heatingCFM, cooling: coolingCFM) + // let sizes = try await self.ductSize( + // .init(designCFM: Int(designCFM.value), frictionRate: designFrictionRate) + // ) + // var width: Int? = nil + // if let height = trunk.height { + // let rectangularSize = try await self.equivalentRectangularDuct( + // .init(round: sizes.finalSize, height: height) + // ) + // width = rectangularSize.width + // } + // + // retval.append( + // .init( + // trunk: trunk, + // ductSize: .init( + // designCFM: designCFM, + // roundSize: sizes.ductulatorSize, + // finalSize: sizes.finalSize, + // velocity: sizes.velocity, + // flexSize: sizes.flexSize, + // height: trunk.height, + // width: width + // ) + // ) + // ) + // } + // + // return retval + // } } @@ -156,15 +174,27 @@ extension ManualDClient: TestDependencyKey { public static let testValue = Self() } -extension DependencyValues { - public var manualD: ManualDClient { - get { self[ManualDClient.self] } - set { self[ManualDClient.self] = newValue } - } -} +// MARK: Project Response +// extension ManualDClient { +// +// public struct ProjectResponse: Codable, Equatable, Sendable { +// public let rooms: [DuctSizing.RoomContainer] +// public let trunks: [DuctSizing.TrunkContainer] +// +// public init( +// rooms: [DuctSizing.RoomContainer], +// trunks: [DuctSizing.TrunkContainer] +// ) { +// self.rooms = rooms +// self.trunks = trunks +// } +// } +// +// } // MARK: Duct Size extension ManualDClient { + public struct DuctSizeRequest: Codable, Equatable, Sendable { public let designCFM: Int public let frictionRate: Double diff --git a/Sources/ProjectClient/DuctCalcClientError.swift b/Sources/ProjectClient/DuctCalcClientError.swift new file mode 100644 index 0000000..9a533b0 --- /dev/null +++ b/Sources/ProjectClient/DuctCalcClientError.swift @@ -0,0 +1,9 @@ +import Foundation + +public struct DuctCalcClientError: Error { + public let reason: String + + public init(_ reason: String) { + self.reason = reason + } +} diff --git a/Sources/ProjectClient/Interface.swift b/Sources/ProjectClient/Interface.swift new file mode 100644 index 0000000..ee173b1 --- /dev/null +++ b/Sources/ProjectClient/Interface.swift @@ -0,0 +1,35 @@ +import Dependencies +import DependenciesMacros +import ManualDCore + +extension DependencyValues { + public var projectClient: ProjectClient { + get { self[ProjectClient.self] } + set { self[ProjectClient.self] = newValue } + } +} + +@DependencyClient +public struct ProjectClient: Sendable { + public var calculateDuctSizes: @Sendable (Project.ID) async throws -> ProjectResponse +} + +extension ProjectClient: TestDependencyKey { + public static let testValue = Self() +} + +extension ProjectClient { + + public struct ProjectResponse: Codable, Equatable, Sendable { + public let rooms: [DuctSizing.RoomContainer] + public let trunks: [DuctSizing.TrunkContainer] + + public init( + rooms: [DuctSizing.RoomContainer], + trunks: [DuctSizing.TrunkContainer] + ) { + self.rooms = rooms + self.trunks = trunks + } + } +} diff --git a/Sources/ProjectClient/Internal/DatabaseClient+calculateDuctSizes.swift b/Sources/ProjectClient/Internal/DatabaseClient+calculateDuctSizes.swift new file mode 100644 index 0000000..984d77c --- /dev/null +++ b/Sources/ProjectClient/Internal/DatabaseClient+calculateDuctSizes.swift @@ -0,0 +1,83 @@ +import DatabaseClient +import Dependencies +import ManualDClient +import ManualDCore + +extension DatabaseClient { + + func calculateDuctSizes( + projectID: Project.ID + ) async throws -> ProjectClient.ProjectResponse { + @Dependency(\.manualD) var manualD + + guard let dfrResponse = try await designFrictionRate(projectID: projectID) else { + throw DuctCalcClientError("Project not complete.") + } + + let ensuredTEL = try dfrResponse.ensureMaxContainer() + + return try await manualD.calculateDuctSizes( + rooms: rooms.fetch(projectID), + trunks: trunkSizes.fetch(projectID), + equipmentInfo: dfrResponse.equipmentInfo, + maxSupplyLength: ensuredTEL.supply, + maxReturnLength: ensuredTEL.return, + designFrictionRate: dfrResponse.designFrictionRate, + projectSHR: ensuredSHR(projectID) + ) + } + + // Fetches the project sensible heat ratio or throws an error if it's nil. + func ensuredSHR(_ projectID: Project.ID) async throws -> Double { + guard let projectSHR = try await projects.getSensibleHeatRatio(projectID) else { + throw DuctCalcClientError("Project sensible heat ratio not set.") + } + return projectSHR + } + + // Internal container. + struct DesignFrictionRateResponse: Equatable, Sendable { + + typealias EnsuredTEL = (supply: EffectiveLength, return: EffectiveLength) + + let designFrictionRate: Double + let equipmentInfo: EquipmentInfo + let telMaxContainer: EffectiveLength.MaxContainer + + func ensureMaxContainer() throws -> EnsuredTEL { + + guard let maxSupplyLength = telMaxContainer.supply else { + throw DuctCalcClientError("Max supply TEL not found") + } + guard let maxReturnLength = telMaxContainer.return else { + throw DuctCalcClientError("Max supply TEL not found") + } + + return (maxSupplyLength, maxReturnLength) + + } + } + + func designFrictionRate( + projectID: Project.ID + ) async throws -> DesignFrictionRateResponse? { + 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.total + + return .init( + designFrictionRate: (availableStaticPressure * 100) / tel, + equipmentInfo: equipmentInfo, + telMaxContainer: equivalentLengths + ) + } +} diff --git a/Sources/ProjectClient/Internal/ManualDClient+calculateDuctSizes.swift b/Sources/ProjectClient/Internal/ManualDClient+calculateDuctSizes.swift new file mode 100644 index 0000000..5c22fa5 --- /dev/null +++ b/Sources/ProjectClient/Internal/ManualDClient+calculateDuctSizes.swift @@ -0,0 +1,201 @@ +import Logging +import ManualDClient +import ManualDCore + +// TODO: Remove Logger and use depedency logger. + +extension ManualDClient { + + func calculateDuctSizes( + rooms: [Room], + trunks: [DuctSizing.TrunkSize], + equipmentInfo: EquipmentInfo, + maxSupplyLength: EffectiveLength, + maxReturnLength: EffectiveLength, + designFrictionRate: Double, + projectSHR: Double, + logger: Logger? = nil + ) async throws -> ProjectClient.ProjectResponse { + try await .init( + rooms: calculateRoomSizes( + rooms: rooms, + equipmentInfo: equipmentInfo, + maxSupplyLength: maxSupplyLength, + maxReturnLength: maxReturnLength, + designFrictionRate: designFrictionRate, + projectSHR: projectSHR + ), + trunks: calculateTrunkSizes( + rooms: rooms, + trunks: trunks, + equipmentInfo: equipmentInfo, + maxSupplyLength: maxSupplyLength, + maxReturnLength: maxReturnLength, + designFrictionRate: designFrictionRate, + projectSHR: projectSHR + ) + ) + } + + func calculateRoomSizes( + rooms: [Room], + equipmentInfo: EquipmentInfo, + maxSupplyLength: EffectiveLength, + maxReturnLength: EffectiveLength, + designFrictionRate: Double, + projectSHR: Double, + logger: Logger? = nil + ) async throws -> [DuctSizing.RoomContainer] { + + var retval: [DuctSizing.RoomContainer] = [] + let totalHeatingLoad = rooms.totalHeatingLoad + let totalCoolingSensible = rooms.totalCoolingSensible(shr: projectSHR) + + for room in rooms { + let heatingLoad = room.heatingLoadPerRegister + let coolingLoad = room.coolingSensiblePerRegister(projectSHR: projectSHR) + let heatingPercent = heatingLoad / totalHeatingLoad + let coolingPercent = coolingLoad / totalCoolingSensible + let heatingCFM = heatingPercent * Double(equipmentInfo.heatingCFM) + let coolingCFM = coolingPercent * Double(equipmentInfo.coolingCFM) + let designCFM = DuctSizing.DesignCFM(heating: heatingCFM, cooling: coolingCFM) + let sizes = try await self.ductSize( + .init(designCFM: Int(designCFM.value), frictionRate: designFrictionRate) + ) + + for n in 1...room.registerCount { + + var rectangularWidth: Int? = nil + let rectangularSize = room.rectangularSizes? + .first(where: { $0.register == nil || $0.register == n }) + + if let rectangularSize { + let response = try await self.equivalentRectangularDuct( + .init(round: sizes.finalSize, height: rectangularSize.height) + ) + rectangularWidth = response.width + } + + retval.append( + .init( + roomID: room.id, + roomName: "\(room.name)-\(n)", + roomRegister: n, + heatingLoad: heatingLoad, + coolingLoad: coolingLoad, + heatingCFM: heatingCFM, + coolingCFM: coolingCFM, + designCFM: designCFM, + roundSize: sizes.ductulatorSize, + finalSize: sizes.finalSize, + velocity: sizes.velocity, + flexSize: sizes.flexSize, + rectangularSize: rectangularSize, + rectangularWidth: rectangularWidth + ) + ) + } + } + + return retval + } + + func calculateTrunkSizes( + rooms: [Room], + trunks: [DuctSizing.TrunkSize], + equipmentInfo: EquipmentInfo, + maxSupplyLength: EffectiveLength, + maxReturnLength: EffectiveLength, + designFrictionRate: Double, + projectSHR: Double, + logger: Logger? = nil + ) async throws -> [DuctSizing.TrunkContainer] { + + var retval = [DuctSizing.TrunkContainer]() + let totalHeatingLoad = rooms.totalHeatingLoad + let totalCoolingSensible = rooms.totalCoolingSensible(shr: projectSHR) + + for trunk in trunks { + let heatingLoad = trunk.totalHeatingLoad + let coolingLoad = trunk.totalCoolingSensible(projectSHR: projectSHR) + let heatingPercent = heatingLoad / totalHeatingLoad + let coolingPercent = coolingLoad / totalCoolingSensible + let heatingCFM = heatingPercent * Double(equipmentInfo.heatingCFM) + let coolingCFM = coolingPercent * Double(equipmentInfo.coolingCFM) + let designCFM = DuctSizing.DesignCFM(heating: heatingCFM, cooling: coolingCFM) + let sizes = try await self.ductSize( + .init(designCFM: Int(designCFM.value), frictionRate: designFrictionRate) + ) + var width: Int? = nil + if let height = trunk.height { + let rectangularSize = try await self.equivalentRectangularDuct( + .init(round: sizes.finalSize, height: height) + ) + width = rectangularSize.width + } + + retval.append( + .init( + trunk: trunk, + ductSize: .init( + designCFM: designCFM, + roundSize: sizes.ductulatorSize, + finalSize: sizes.finalSize, + velocity: sizes.velocity, + flexSize: sizes.flexSize, + height: trunk.height, + width: width + ) + ) + ) + } + + return retval + } + +} + +extension Room { + + var heatingLoadPerRegister: Double { + + heatingLoad / Double(registerCount) + } + + func coolingSensiblePerRegister(projectSHR: Double) -> Double { + let sensible = coolingSensible ?? (coolingTotal * projectSHR) + return sensible / Double(registerCount) + } +} + +extension DuctSizing.TrunkSize.RoomProxy { + + // We need to make sure if registers got removed after a trunk + // was already made / saved that we do not include registers that + // no longer exist. + private var actualRegisterCount: Int { + guard registers.count <= room.registerCount else { + return room.registerCount + } + return registers.count + } + + var totalHeatingLoad: Double { + room.heatingLoadPerRegister * Double(actualRegisterCount) + } + + func totalCoolingSensible(projectSHR: Double) -> Double { + room.coolingSensiblePerRegister(projectSHR: projectSHR) * Double(actualRegisterCount) + } +} + +extension DuctSizing.TrunkSize { + + var totalHeatingLoad: Double { + rooms.reduce(into: 0) { $0 += $1.totalHeatingLoad } + } + + func totalCoolingSensible(projectSHR: Double) -> Double { + rooms.reduce(into: 0) { $0 += $1.totalCoolingSensible(projectSHR: projectSHR) } + } +} diff --git a/Sources/ProjectClient/Live.swift b/Sources/ProjectClient/Live.swift new file mode 100644 index 0000000..28fbd85 --- /dev/null +++ b/Sources/ProjectClient/Live.swift @@ -0,0 +1,19 @@ +import DatabaseClient +import Dependencies +import Logging +import ManualDClient +import ManualDCore + +extension ProjectClient: DependencyKey { + + public static var liveValue: Self { + @Dependency(\.database) var database + + return .init( + calculateDuctSizes: { projectID in + try await database.calculateDuctSizes(projectID: projectID) + } + ) + } + +} diff --git a/Sources/ViewController/Extensions/DatabaseExtensions.swift b/Sources/ViewController/Extensions/DatabaseExtensions.swift index 13888a5..15f09c0 100644 --- a/Sources/ViewController/Extensions/DatabaseExtensions.swift +++ b/Sources/ViewController/Extensions/DatabaseExtensions.swift @@ -24,42 +24,42 @@ extension DatabaseClient.Projects { } } -extension DatabaseClient { - - func calculateDuctSizes( - projectID: Project.ID - ) async throws -> (rooms: [DuctSizing.RoomContainer], trunks: [DuctSizing.TrunkContainer]) { - @Dependency(\.manualD) var manualD - - return try await manualD.calculate( - rooms: rooms.fetch(projectID), - trunks: trunkSizes.fetch(projectID), - designFrictionRateResult: designFrictionRate(projectID: projectID), - projectSHR: projects.getSensibleHeatRatio(projectID) - ) - } - - 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.total - - let designFrictionRate = (availableStaticPressure * 100) / tel - - return (equipmentInfo, equivalentLengths, designFrictionRate) - } -} +// extension DatabaseClient { +// +// func calculateDuctSizes( +// projectID: Project.ID +// ) async throws -> (rooms: [DuctSizing.RoomContainer], trunks: [DuctSizing.TrunkContainer]) { +// @Dependency(\.manualD) var manualD +// +// return try await manualD.calculate( +// rooms: rooms.fetch(projectID), +// trunks: trunkSizes.fetch(projectID), +// designFrictionRateResult: designFrictionRate(projectID: projectID), +// projectSHR: projects.getSensibleHeatRatio(projectID) +// ) +// } +// +// 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.total +// +// let designFrictionRate = (availableStaticPressure * 100) / tel +// +// return (equipmentInfo, equivalentLengths, designFrictionRate) +// } +// } extension DatabaseClient.ComponentLoss { diff --git a/Sources/ViewController/Extensions/ManualDClient+extensions.swift b/Sources/ViewController/Extensions/ManualDClient+extensions.swift index e554b4b..7b56262 100644 --- a/Sources/ViewController/Extensions/ManualDClient+extensions.swift +++ b/Sources/ViewController/Extensions/ManualDClient+extensions.swift @@ -1,38 +1,38 @@ -import Logging -import ManualDClient -import ManualDCore - -extension ManualDClient { - - func calculate( - rooms: [Room], - trunks: [DuctSizing.TrunkSize], - designFrictionRateResult: (EquipmentInfo, EffectiveLength.MaxContainer, Double)?, - projectSHR: Double?, - logger: Logger? = nil - ) async throws -> (rooms: [DuctSizing.RoomContainer], trunks: [DuctSizing.TrunkContainer]) { - 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, - trunks: trunks, - equipmentInfo: equipmentInfo, - maxSupplyLength: maxSupply, - maxReturnLength: maxReturn, - designFrictionRate: designFrictionRate, - projectSHR: projectSHR ?? 1.0, - logger: logger - ) - - // logger?.debug("Rooms: \(ductRooms)") - - return ductRooms - - } -} +// import Logging +// import ManualDClient +// import ManualDCore +// +// extension ManualDClient { +// +// func calculate( +// rooms: [Room], +// trunks: [DuctSizing.TrunkSize], +// designFrictionRateResult: (EquipmentInfo, EffectiveLength.MaxContainer, Double)?, +// projectSHR: Double?, +// logger: Logger? = nil +// ) async throws -> (rooms: [DuctSizing.RoomContainer], trunks: [DuctSizing.TrunkContainer]) { +// 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, +// trunks: trunks, +// equipmentInfo: equipmentInfo, +// maxSupplyLength: maxSupply, +// maxReturnLength: maxReturn, +// designFrictionRate: designFrictionRate, +// projectSHR: projectSHR ?? 1.0, +// logger: logger +// ) +// +// // logger?.debug("Rooms: \(ductRooms)") +// +// return ductRooms +// +// } +// } diff --git a/Sources/ViewController/Live.swift b/Sources/ViewController/Live.swift index ef0d0cc..c5134d0 100644 --- a/Sources/ViewController/Live.swift +++ b/Sources/ViewController/Live.swift @@ -3,6 +3,7 @@ import Dependencies import Elementary import Foundation import ManualDCore +import ProjectClient import Styleguide extension ViewController.Request { @@ -10,6 +11,7 @@ extension ViewController.Request { func render() async -> AnySendableHTML { @Dependency(\.database) var database + @Dependency(\.projectClient) var projectClient switch route { case .test: @@ -18,7 +20,7 @@ extension ViewController.Request { await ResultView { return ( try await database.projects.getCompletedSteps(projectID), - try await database.calculateDuctSizes(projectID: projectID) + try await projectClient.calculateDuctSizes(projectID) ) } onSuccess: { (_, result) in TestPage(trunks: result.trunks, rooms: result.rooms) @@ -555,6 +557,7 @@ extension SiteRoute.View.ProjectRoute.DuctSizingRoute { ) async -> AnySendableHTML { @Dependency(\.database) var database @Dependency(\.manualD) var manualD + @Dependency(\.projectClient) var projectClient switch self { case .index: @@ -563,7 +566,7 @@ extension SiteRoute.View.ProjectRoute.DuctSizingRoute { case .deleteRectangularSize(let roomID, let request): return await ResultView { let room = try await database.rooms.deleteRectangularSize(roomID, request.rectangularSizeID) - return try await database.calculateDuctSizes(projectID: projectID) + return try await projectClient.calculateDuctSizes(projectID) .rooms .filter({ $0.roomID == room.id && $0.roomRegister == request.register }) .first! @@ -577,7 +580,7 @@ extension SiteRoute.View.ProjectRoute.DuctSizingRoute { roomID, .init(id: form.id ?? .init(), register: form.register, height: form.height) ) - return try await database.calculateDuctSizes(projectID: projectID) + return try await projectClient.calculateDuctSizes(projectID) .rooms .filter({ $0.roomID == room.id && $0.roomRegister == form.register }) .first! @@ -612,13 +615,14 @@ extension SiteRoute.View.ProjectRoute.DuctSizingRoute { catching: @escaping @Sendable () async throws -> Void = {} ) async -> AnySendableHTML { @Dependency(\.database) var database + @Dependency(\.projectClient) var project return await request.view { await ResultView { try await catching() return ( try await database.projects.getCompletedSteps(projectID), - try await database.calculateDuctSizes(projectID: projectID) + try await project.calculateDuctSizes(projectID) ) } onSuccess: { (steps, ducts) in ProjectView(projectID: projectID, activeTab: .ductSizing, completedSteps: steps) { diff --git a/justfile b/justfile index 8187525..854158a 100644 --- a/justfile +++ b/justfile @@ -1,6 +1,9 @@ docker_image := "ductcalc" docker_tag := "latest" +clean: + rm -rf .build + install-deps: @curl -sL daisyui.com/fast | bash