Files
swift-estimated-pressures-core/Sources/EstimatedPressureDependency/Key.swift
2024-05-24 16:44:41 -04:00

249 lines
7.6 KiB
Swift

import Dependencies
import DependenciesMacros
import Foundation
@_exported import SharedModels
extension DependencyValues {
public var estimatedPressuresClient: EstimatedPressureDependency {
get { self[EstimatedPressureDependency.self] }
set { self[EstimatedPressureDependency.self] = newValue }
}
}
@DependencyClient
public struct EstimatedPressureDependency {
public var estimatedAirflow: (EstimatedAirflowRequest) async throws -> Positive<Double>
public var estimatedPressure: (EstimatedPressureRequest) async throws -> Positive<Double>
public struct EstimatedPressureRequest: Equatable {
public let existingPressure: Positive<Double>
public let existingAirflow: Positive<Double>
public let targetAirflow: Positive<Double>
public init(
existingPressure: Double,
existingAirflow: Double,
targetAirflow: Double
) {
self.existingPressure = .init(wrappedValue: existingPressure)
self.existingAirflow = .init(wrappedValue: existingAirflow)
self.targetAirflow = .init(wrappedValue: targetAirflow)
}
}
public struct EstimatedAirflowRequest: Equatable {
public let existingAirflow: Positive<Double>
public let existingPressure: Positive<Double>
public let targetPressure: Positive<Double>
public init(
existingAirflow: Double,
existingPressure: Double,
targetPressure: Double
) {
self.existingPressure = .init(wrappedValue: existingPressure)
self.existingAirflow = .init(wrappedValue: existingAirflow)
self.targetPressure = .init(wrappedValue: targetPressure)
}
}
public func estimatedPressure(
existingPressure: Double,
existingAirflow: Double,
targetAirflow: Double
) async throws -> Positive<Double> {
try await self.estimatedPressure(
.init(
existingPressure: existingPressure,
existingAirflow: existingAirflow,
targetAirflow: targetAirflow
)
)
}
}
extension EstimatedPressureDependency {
private func estimatedPressure(
existingPressure: Positive<Double?>,
existingAirflow: Double,
targetAirflow: Double
) async throws -> Positive<Double> {
try await self.estimatedPressure(
.init(
existingPressure: existingPressure.positiveValue ?? 0,
existingAirflow: existingAirflow,
targetAirflow: targetAirflow
)
)
}
public func estimatedPressure(
for equipmentMeasurement: EquipmentMeasurement,
at upgradedAirflow: Double
) async throws -> EquipmentMeasurement {
switch equipmentMeasurement {
case let .airHandler(airHandler):
guard let airflow = airHandler.airflow else {
throw InvalidAirflow()
}
return try await .airHandler(
.init(
airflow: upgradedAirflow,
returnPlenumPressure: self.estimatedPressure(
existingPressure: airHandler.$returnPlenumPressure,
existingAirflow: airflow,
targetAirflow: upgradedAirflow
),
postFilterPressure: self.estimatedPressure(
existingPressure: airHandler.$postFilterPressure,
existingAirflow: airflow,
targetAirflow: upgradedAirflow
),
postCoilPressure: self.estimatedPressure(
existingPressure: airHandler.$postCoilPressure,
existingAirflow: airflow,
targetAirflow: upgradedAirflow
),
supplyPlenumPressure: self.estimatedPressure(
existingPressure: airHandler.$supplyPlenumPressure,
existingAirflow: airflow,
targetAirflow: upgradedAirflow
)
)
)
case let .furnaceAndCoil(furnaceAndCoil):
guard let airflow = furnaceAndCoil.airflow else {
throw InvalidAirflow()
}
return try await .furnaceAndCoil(
.init(
airflow: upgradedAirflow,
returnPlenumPressure: self.estimatedPressure(
existingPressure: furnaceAndCoil.$returnPlenumPressure,
existingAirflow: airflow,
targetAirflow: upgradedAirflow
),
postFilterPressure: self.estimatedPressure(
existingPressure: furnaceAndCoil.$postFilterPressure,
existingAirflow: airflow,
targetAirflow: upgradedAirflow
),
preCoilPressure: self.estimatedPressure(
existingPressure: furnaceAndCoil.$preCoilPressure,
existingAirflow: airflow,
targetAirflow: upgradedAirflow
),
supplyPlenumPressure: self.estimatedPressure(
existingPressure: furnaceAndCoil.$supplyPlenumPressure,
existingAirflow: airflow,
targetAirflow: upgradedAirflow
)
)
)
}
}
public func estimatedPressure(
for equipmentMeasurement: EquipmentMeasurement,
at upgradedAirflow: Double,
with filterPressureDrop: Positive<Double>
) async throws -> EquipmentMeasurement {
let estimate = try await estimatedPressure(
for: equipmentMeasurement,
at: upgradedAirflow
)
switch estimate {
case var .airHandler(airHandler):
airHandler.postFilterPressure = airHandler.returnPlenumPressure + filterPressureDrop.positiveValue
return .airHandler(airHandler)
case var .furnaceAndCoil(furnaceAndCoil):
furnaceAndCoil.postFilterPressure = furnaceAndCoil.returnPlenumPressure + filterPressureDrop.positiveValue
return .furnaceAndCoil(furnaceAndCoil)
}
}
}
extension EstimatedPressureDependency: DependencyKey {
public static var testValue: EstimatedPressureDependency { Self() }
public static var liveValue: EstimatedPressureDependency {
.init(
estimatedAirflow: { request in
guard request.existingPressure.positiveValue > 0 else {
throw LessThanZeroError.existingPressure
}
let value = request.existingAirflow.positiveValue * sqrt(
request.targetPressure.positiveValue / request.existingPressure.positiveValue
)
return .init(wrappedValue: value)
},
estimatedPressure: { request in
guard request.existingAirflow.positiveValue > 0 else {
throw LessThanZeroError.existingAirflow
}
let value = pow(
request.targetAirflow.positiveValue / request.existingAirflow.positiveValue,
2
) * request.existingPressure.positiveValue
return .init(wrappedValue: value)
}
)
}
}
fileprivate extension EquipmentMeasurement.AirHandler {
init(
airflow: Double? = nil,
returnPlenumPressure: Positive<Double>,
postFilterPressure: Positive<Double>,
postCoilPressure: Positive<Double>,
supplyPlenumPressure: Positive<Double>
) {
self.init(
airflow: airflow,
returnPlenumPressure: returnPlenumPressure.positiveValue,
postFilterPressure: postFilterPressure.positiveValue,
postCoilPressure: postCoilPressure.positiveValue,
supplyPlenumPressure: supplyPlenumPressure.positiveValue
)
}
}
fileprivate extension EquipmentMeasurement.FurnaceAndCoil {
init(
airflow: Double? = nil,
returnPlenumPressure: Positive<Double>,
postFilterPressure: Positive<Double>,
preCoilPressure: Positive<Double>,
supplyPlenumPressure: Positive<Double>
) {
self.init(
airflow: airflow,
returnPlenumPressure: returnPlenumPressure.positiveValue,
postFilterPressure: postFilterPressure.positiveValue,
preCoilPressure: preCoilPressure.positiveValue,
supplyPlenumPressure: supplyPlenumPressure.positiveValue
)
}
}
struct InvalidAirflow: Error { }
enum LessThanZeroError: Error {
case existingAirflow
case existingPressure
}