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 public var estimatedPressure: @Sendable (EstimatedPressureRequest) async throws -> Positive public struct EstimatedPressureRequest: Equatable, Sendable { public let existingPressure: Positive public let existingAirflow: Positive public let targetAirflow: Positive 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 public let existingPressure: Positive public let targetPressure: Positive 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 { try await self.estimatedPressure( .init( existingPressure: existingPressure, existingAirflow: existingAirflow, targetAirflow: targetAirflow ) ) } } extension EstimatedPressureDependency { private func estimatedPressure( existingPressure: Positive, existingAirflow: Positive, targetAirflow: Positive ) async throws -> Positive { 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, 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, filterPressureDrop: Positive? ) 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 }