Files

228 lines
7.4 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: Sendable {
public var estimatedAirflow: @Sendable (EstimatedAirflowRequest) async throws -> Positive<Double>
public var estimatedPressure: @Sendable (EstimatedPressureRequest) async throws -> Positive<Double>
public struct EstimatedPressureRequest: Equatable, Sendable {
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, Sendable {
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: Positive<Double>,
targetAirflow: Positive<Double>
) async throws -> Positive<Double> {
try await self.estimatedPressure(
.init(
existingPressure: existingPressure.positiveValue,
existingAirflow: existingAirflow.positiveValue,
targetAirflow: targetAirflow.positiveValue
)
)
}
public func estimatedPressure(
equipmentMeasurement: EquipmentMeasurement,
airflow updatedAirflow: Double
) async throws -> EquipmentMeasurement {
let updatedAirflow = Positive(updatedAirflow)
switch equipmentMeasurement {
case let .airHandler(airHandler):
return try await .airHandler(
.init(
airflow: updatedAirflow,
manufacturersIncludedFilterPressureDrop: airHandler.$manufacturersIncludedFilterPressureDrop,
returnPlenumPressure: self.estimatedPressure(
existingPressure: airHandler.$returnPlenumPressure,
existingAirflow: airHandler.$airflow,
targetAirflow: updatedAirflow
),
postFilterPressure: self.estimatedPressure(
existingPressure: airHandler.$postFilterPressure,
existingAirflow: airHandler.$airflow,
targetAirflow: updatedAirflow
),
postCoilPressure: self.estimatedPressure(
existingPressure: airHandler.$postCoilPressure,
existingAirflow: airHandler.$airflow,
targetAirflow: updatedAirflow
),
supplyPlenumPressure: self.estimatedPressure(
existingPressure: airHandler.$supplyPlenumPressure,
existingAirflow: airHandler.$airflow,
targetAirflow: updatedAirflow
)
)
)
case let .furnaceAndCoil(furnaceAndCoil):
return try await .furnaceAndCoil(
.init(
airflow: updatedAirflow,
manufacturersIncludedFilterPressureDrop: furnaceAndCoil.$manufacturersIncludedFilterPressureDrop,
returnPlenumPressure: self.estimatedPressure(
existingPressure: furnaceAndCoil.$returnPlenumPressure,
existingAirflow: furnaceAndCoil.$airflow,
targetAirflow: updatedAirflow
),
postFilterPressure: self.estimatedPressure(
existingPressure: furnaceAndCoil.$postFilterPressure,
existingAirflow: furnaceAndCoil.$airflow,
targetAirflow: updatedAirflow
),
preCoilPressure: self.estimatedPressure(
existingPressure: furnaceAndCoil.$preCoilPressure,
existingAirflow: furnaceAndCoil.$airflow,
targetAirflow: updatedAirflow
),
supplyPlenumPressure: self.estimatedPressure(
existingPressure: furnaceAndCoil.$supplyPlenumPressure,
existingAirflow: furnaceAndCoil.$airflow,
targetAirflow: updatedAirflow
)
)
)
}
}
private func setFilterPressureDrop(
_ filterDrop: Positive<Double>,
measurement: inout EquipmentMeasurement
) {
switch measurement {
case var .airHandler(airHandler):
airHandler.postFilterPressure = airHandler.returnPlenumPressure +
filterDrop.positiveValue
measurement = .airHandler(airHandler)
case var .furnaceAndCoil(furnace):
furnace.postFilterPressure = furnace.returnPlenumPressure + filterDrop.positiveValue
measurement = .furnaceAndCoil(furnace)
}
}
public func estimatedPressure(
equipmentMeasurement: EquipmentMeasurement,
airflow updatedAirflow: Positive<Double>,
filterPressureDrop: Positive<Double>?
) async throws -> EquipmentMeasurement {
var estimate = try await estimatedPressure(
equipmentMeasurement: equipmentMeasurement,
airflow: updatedAirflow.positiveValue
)
guard let filterPressureDrop else { return estimate }
setFilterPressureDrop(filterPressureDrop, measurement: &estimate)
return estimate
}
public func estimatedPressure(
equipmentMeasurement: EquipmentMeasurement,
airflow updatedAirflow: Double,
filterPressureDrop: Double?
) async throws -> EquipmentMeasurement {
try await estimatedPressure(
equipmentMeasurement: equipmentMeasurement,
airflow: Positive(updatedAirflow),
filterPressureDrop: filterPressureDrop != nil ? Positive(filterPressureDrop!) : nil
)
}
}
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)
}
)
}
}
struct InvalidAirflow: Error { }
enum LessThanZeroError: Error {
case existingAirflow
case existingPressure
}