diff --git a/Sources/FlaggedViews/FlaggedEquipmentMeasurementView.swift b/Sources/FlaggedViews/FlaggedEquipmentMeasurementView.swift index fac2683..b8abd9b 100644 --- a/Sources/FlaggedViews/FlaggedEquipmentMeasurementView.swift +++ b/Sources/FlaggedViews/FlaggedEquipmentMeasurementView.swift @@ -5,9 +5,9 @@ public struct FlaggedEquipmentMeasurementView: View { @Environment(\.flaggedEquipmentMeasurementStyle) private var style - let measurement: FlaggedEquipmentMeasurement + let measurement: EquipmentMeasurement.FlaggedMeasurement - public init(_ measurement: FlaggedEquipmentMeasurement) { + public init(_ measurement: EquipmentMeasurement.FlaggedMeasurement) { self.measurement = measurement } @@ -29,7 +29,7 @@ public protocol FlaggedEquipmentMeasurementStyle { } public struct FlaggedEquipmentMeasurementStyleConfiguration { - public let measurement: FlaggedEquipmentMeasurement + public let measurement: EquipmentMeasurement.FlaggedMeasurement } public struct AnyFlaggedEquipmentMeasurementStyle: FlaggedEquipmentMeasurementStyle { @@ -54,7 +54,7 @@ public struct GridFlaggedEquipmentMeasurementStyle: FlaggedEquipmentMeasurementS public func makeBody(configuration: Configuration) -> some View { Grid(alignment: .leading, verticalSpacing: 20) { - ForEach(FlaggedEquipmentMeasurement.Key.allCases) { field in + ForEach(EquipmentMeasurement.FlaggedMeasurement.Key.allCases) { field in FlaggedView( field.title, flagged: configuration.measurement[keyPath: field.flaggedKeyPath] @@ -93,7 +93,7 @@ extension View { } // MARK: - Key -fileprivate extension FlaggedEquipmentMeasurement { +fileprivate extension EquipmentMeasurement.FlaggedMeasurement { // NOTE: These need to be kept in display order. enum Key: Hashable, CaseIterable, Identifiable { case returnPlenum @@ -122,7 +122,7 @@ fileprivate extension FlaggedEquipmentMeasurement { } } - var flaggedKeyPath: KeyPath { + var flaggedKeyPath: KeyPath { switch self { case .returnPlenum: return \.returnPlenumPressure diff --git a/Sources/PressureEstimationsFeature/EquipmentMeasurementForm.swift b/Sources/PressureEstimationsFeature/EquipmentMeasurementForm.swift index a7f4ac5..68ae453 100644 --- a/Sources/PressureEstimationsFeature/EquipmentMeasurementForm.swift +++ b/Sources/PressureEstimationsFeature/EquipmentMeasurementForm.swift @@ -18,14 +18,14 @@ public struct EquipmentMeasurementForm { public struct State: Equatable { @Presents public var destination: Destination.State? public var allowEquipmentTypeSelection: Bool - public var equipmentType: EquipmentType + public var equipmentType: EquipmentMeasurement.EquipmentType public var focusedField: Field? public var measurements: Measurements public init( allowEquipmentTypeSelection: Bool = true, destination: Destination.State? = nil, - equipmentType: EquipmentType = .airHandler, + equipmentType: EquipmentMeasurement.EquipmentType = .airHandler, focusedField: Field? = nil, measurements: Measurements = .init() ) { @@ -92,7 +92,9 @@ public struct EquipmentMeasurementForm { } } - public func equipmentMeasurement(type: EquipmentType) -> EquipmentMeasurement { + public func equipmentMeasurement( + type: EquipmentMeasurement.EquipmentType + ) -> EquipmentMeasurement { switch type { case .airHandler: @@ -229,14 +231,9 @@ public struct EquipmentMeasurementFormView: View { } header: { Text("Equipment Type") } footer: { - Picker("Equipment Type", selection: $store.equipmentType) { - ForEach(EquipmentType.allCases) { - Text($0.description) - .tag($0) - } - } - .pickerStyle(.segmented) - .labelsHidden() + EquipmentTypePicker(selection: $store.equipmentType) + .pickerStyle(.segmented) + .labelsHidden() } } Section { diff --git a/Sources/PressureEstimationsFeature/EquipmentSettingsForm.swift b/Sources/PressureEstimationsFeature/EquipmentSettingsForm.swift index 05e0c10..4f45778 100644 --- a/Sources/PressureEstimationsFeature/EquipmentSettingsForm.swift +++ b/Sources/PressureEstimationsFeature/EquipmentSettingsForm.swift @@ -23,18 +23,18 @@ public struct EquipmentSettingsForm { public struct State: Equatable { @Presents public var destination: Destination.State? - public var coolingCapacity: CoolingCapacity - public var equipmentType: EquipmentType - public var fanType: FanType + public var coolingCapacity: EquipmentMetadata.CoolingCapacity + public var equipmentType: EquipmentMeasurement.EquipmentType + public var fanType: EquipmentMetadata.FanType public var focusedField: Field? = nil public var heatingCapacity: Double? public var ratedStaticPressures: RatedStaticPressures public init( - coolingCapacity: CoolingCapacity = .default, + coolingCapacity: EquipmentMetadata.CoolingCapacity = .default, destination: Destination.State? = nil, - equipmentType: EquipmentType = .airHandler, - fanType: FanType = .constantSpeed, + equipmentType: EquipmentMeasurement.EquipmentType = .airHandler, + fanType: EquipmentMetadata.FanType = .constantSpeed, heatingCapacity: Double? = nil, ratedStaticPressures: RatedStaticPressures = .init() ) { @@ -154,12 +154,13 @@ public struct EquipmentSettingsFormView: View { : "Capacity" ) Spacer() - Picker("Cooling Capcity", selection: $store.coolingCapacity) { - ForEach(CoolingCapacity.allCases) { - Text($0.description) - .tag($0) - } - } + CoolingCapacityPicker(selection: $store.coolingCapacity) +// Picker("Cooling Capcity", selection: $store.coolingCapacity) { +// ForEach(CoolingCapacity.allCases) { +// Text($0.description) +// .tag($0) +// } +// } } if store.equipmentType == .furnaceAndCoil { GridRow { diff --git a/Sources/PressureEstimationsFeature/EstimationsForm.swift b/Sources/PressureEstimationsFeature/EstimationsForm.swift index f563e0f..53441cf 100644 --- a/Sources/PressureEstimationsFeature/EstimationsForm.swift +++ b/Sources/PressureEstimationsFeature/EstimationsForm.swift @@ -10,13 +10,13 @@ public struct EstimationForm { @ObservableState public struct State: Equatable { public var cfmPerTon: Int - public var coolingCapacity: CoolingCapacity + public var coolingCapacity: EquipmentMetadata.CoolingCapacity public var filterPressureDrop: Double? public var name: String public init( cfmPerTon: Int = 350, - coolingCapacity: CoolingCapacity = .default, + coolingCapacity: EquipmentMetadata.CoolingCapacity = .default, filterPressureDrop: Double? = nil, name: String = "" ) { diff --git a/Sources/PressureEstimationsFeature/FlaggedMeasurementsList.swift b/Sources/PressureEstimationsFeature/FlaggedMeasurementsList.swift index 07846cf..4749eb8 100644 --- a/Sources/PressureEstimationsFeature/FlaggedMeasurementsList.swift +++ b/Sources/PressureEstimationsFeature/FlaggedMeasurementsList.swift @@ -35,13 +35,13 @@ public struct FlaggedMeasurementsList { public struct FlaggedMeasurementContainer: Equatable, Identifiable { public let id: UUID - public var flaggedMeasurement: FlaggedEquipmentMeasurement + public var flaggedMeasurement: EquipmentMeasurement.FlaggedMeasurement public var name: String public init( id: UUID, name: String, - flaggedMeasurement: FlaggedEquipmentMeasurement + flaggedMeasurement: EquipmentMeasurement.FlaggedMeasurement ) { self.id = id self.name = name @@ -58,8 +58,8 @@ public struct FlaggedMeasurementsList { @CasePathable public enum ReceiveAction { - case existingFlaggedMeasurement(FlaggedEquipmentMeasurement) - case estimatedFlaggedMeasurement(name: String, measurement: FlaggedEquipmentMeasurement) + case existingFlaggedMeasurement(EquipmentMeasurement.FlaggedMeasurement) + case estimatedFlaggedMeasurement(name: String, measurement: EquipmentMeasurement.FlaggedMeasurement) } @CasePathable @@ -190,7 +190,7 @@ public struct FlaggedMeasurementsList { filterPressureDrop: filterPressureDrop ) - let flaggedMeasurement = FlaggedEquipmentMeasurement( + let flaggedMeasurement = EquipmentMeasurement.FlaggedMeasurement( budgets: budgets, measurement: measurement, ratedPressures: ratedStaticPressures, diff --git a/Sources/PressureEstimationsFeature/SharedSettings.swift b/Sources/PressureEstimationsFeature/SharedSettings.swift index 95ab0ba..118423f 100644 --- a/Sources/PressureEstimationsFeature/SharedSettings.swift +++ b/Sources/PressureEstimationsFeature/SharedSettings.swift @@ -4,19 +4,19 @@ import SharedModels struct SharedSettings: Equatable { var budgets: BudgetedPercentEnvelope? - var coolingCapacity: CoolingCapacity + var coolingCapacity: EquipmentMetadata.CoolingCapacity var equipmentMeasurement: EquipmentMeasurement? - var fanType: FanType - var flaggedEquipmentMeasurement: FlaggedEquipmentMeasurement? + var fanType: EquipmentMetadata.FanType + var flaggedEquipmentMeasurement: EquipmentMeasurement.FlaggedMeasurement? var heatingCapacity: Double? var ratedStaticPressures: RatedStaticPressures init( budgets: BudgetedPercentEnvelope? = nil, - coolingCapacity: CoolingCapacity = .default, + coolingCapacity: EquipmentMetadata.CoolingCapacity = .default, equipmentMeasurement: EquipmentMeasurement? = nil, - fanType: FanType = .constantSpeed, - flaggedEquipmentMeasurement: FlaggedEquipmentMeasurement? = nil, + fanType: EquipmentMetadata.FanType = .constantSpeed, + flaggedEquipmentMeasurement: EquipmentMeasurement.FlaggedMeasurement? = nil, heatingCapacity: Double? = nil, ratedStaticPressures: RatedStaticPressures = .init() ) { diff --git a/Sources/SharedModels/BudgetedPercentEnvelope.swift b/Sources/SharedModels/BudgetedPercentEnvelope.swift index a16e229..2e8512b 100644 --- a/Sources/SharedModels/BudgetedPercentEnvelope.swift +++ b/Sources/SharedModels/BudgetedPercentEnvelope.swift @@ -27,7 +27,10 @@ public struct BudgetedPercentEnvelope: Equatable { coilBudget + filterBudget + supplyPlenumBudget + returnPlenumBudget } - public init(equipmentType: EquipmentType, fanType: FanType) { + public init( + equipmentType: EquipmentMeasurement.EquipmentType, + fanType: EquipmentMetadata.FanType + ) { switch equipmentType { case .furnaceAndCoil: switch fanType { diff --git a/Sources/SharedModels/CoolingCapacity.swift b/Sources/SharedModels/CoolingCapacity.swift index 6339dfb..fbf2875 100644 --- a/Sources/SharedModels/CoolingCapacity.swift +++ b/Sources/SharedModels/CoolingCapacity.swift @@ -1,43 +1,2 @@ import Foundation -public enum CoolingCapacity: Double, Hashable, CaseIterable, Identifiable, CustomStringConvertible { - case half = 0.5 - case threeQuarter = 0.75 - case one = 1 - case oneAndAHalf = 1.5 - case two = 2 - case twoAndAHalf = 2.5 - case three = 3 - case threeAndAHalf = 3.5 - case four = 4 - case five = 5 - - public var id: Self { self } - - public static var `default`: Self { .three } - - public var description: String { - switch self { - case .half: - return "1/2 Ton" - case .threeQuarter: - return "3/4 Ton" - case .one: - return "1 Ton" - case .oneAndAHalf: - return "1.5 Tons" - case .two: - return "2 Tons" - case .twoAndAHalf: - return "2.5 Tons" - case .three: - return "3 Tons" - case .threeAndAHalf: - return "3.5 Tons" - case .four: - return "4 Tons" - case .five: - return "5 Tons" - } - } -} diff --git a/Sources/SharedModels/EquipmentMeasurement.swift b/Sources/SharedModels/EquipmentMeasurement.swift index 6345bfc..401cbb5 100644 --- a/Sources/SharedModels/EquipmentMeasurement.swift +++ b/Sources/SharedModels/EquipmentMeasurement.swift @@ -7,7 +7,7 @@ public enum EquipmentMeasurement: Equatable { case airHandler(AirHandler) case furnaceAndCoil(FurnaceAndCoil) - + public var equipmentType: EquipmentType { switch self { case .airHandler: @@ -25,7 +25,7 @@ public enum EquipmentMeasurement: Equatable { return furnaceAndCoil.externalStaticPressure } } - + public struct AirHandler: Equatable { @Positive @@ -70,6 +70,144 @@ public enum EquipmentMeasurement: Equatable { } } + public enum EquipmentType: Equatable, CaseIterable, CustomStringConvertible, Identifiable { + case airHandler + case furnaceAndCoil + + public var id: Self { self } + + public var description: String { + switch self { + case .airHandler: + return "Air Handler" + + case .furnaceAndCoil: + return "Furnace & Coil" + } + } + } + + // TODO: Needs updated for when forms are using `minmal` values. + public struct FlaggedMeasurement: Equatable { + public var airflow: Flagged + public var coilPressureDrop: Flagged + public var externalStaticPressure: Flagged + public var filterPressureDrop: Flagged + public var returnPlenumPressure: Flagged + public var supplyPlenumPressure: Flagged + + public init( + airflow: Flagged, + coilPressureDrop: Flagged, + externalStaticPressure: Flagged, + filterPressureDrop: Flagged, + returnPlenumPressure: Flagged, + supplyPlenumPressure: Flagged + ) { + self.airflow = airflow + self.coilPressureDrop = coilPressureDrop + self.externalStaticPressure = externalStaticPressure + self.filterPressureDrop = filterPressureDrop + self.returnPlenumPressure = returnPlenumPressure + self.supplyPlenumPressure = supplyPlenumPressure + } + + public init( + budgets: BudgetedPercentEnvelope, + measurement: EquipmentMeasurement, + ratedPressures: RatedStaticPressures, + tons: EquipmentMetadata.CoolingCapacity? + ) { + switch measurement { + case let .airHandler(airHandler): + self = .airHandler( + budgets: budgets, + measurement: airHandler, + ratedPressures: ratedPressures, + tons: tons + ) + case let .furnaceAndCoil(furnaceAndCoil): + self = .furnaceAndCoil( + budgets: budgets, + measurement: furnaceAndCoil, + ratedPressures: ratedPressures, + tons: tons + ) + } + } + + public static func airHandler( + budgets: BudgetedPercentEnvelope, + measurement: EquipmentMeasurement.AirHandler, + ratedPressures: RatedStaticPressures, + tons: EquipmentMetadata.CoolingCapacity? + ) -> Self { + .init( + airflow: checkAirflow(value: measurement.airflow, tons: tons), + coilPressureDrop: .init( + wrappedValue: (measurement.$postCoilPressure.positiveValue - measurement.$postFilterPressure.positiveValue) ?? 0, + .result(.good()) + ), + externalStaticPressure: checkExternalStaticPressure( + value: measurement.externalStaticPressure, + ratedPressures: ratedPressures + ), + filterPressureDrop: .init( + value: measurement.$postFilterPressure.positiveValue - measurement.$returnPlenumPressure.positiveValue, + budget: budgets.filterBudget, + ratedPressures: ratedPressures + ), + returnPlenumPressure: .init( + value: measurement.$returnPlenumPressure.positiveValue, + budget: budgets.returnPlenumBudget, + ratedPressures: ratedPressures + ), + supplyPlenumPressure: .init( + value: measurement.$supplyPlenumPressure.positiveValue ?? 0, + budget: budgets.supplyPlenumBudget, + ratedPressures: ratedPressures + ) + ) + } + + public static func furnaceAndCoil( + budgets: BudgetedPercentEnvelope, + measurement: EquipmentMeasurement.FurnaceAndCoil, + ratedPressures: RatedStaticPressures, + tons: EquipmentMetadata.CoolingCapacity? + ) -> Self { + .init( + airflow: checkAirflow(value: measurement.airflow, tons: tons), + coilPressureDrop: .init( + value: measurement.$preCoilPressure.positiveValue - measurement.$supplyPlenumPressure.positiveValue, + budget: budgets.coilBudget, + ratedPressures: ratedPressures + ), + externalStaticPressure: checkExternalStaticPressure( + value: measurement.externalStaticPressure, + ratedPressures: ratedPressures + ), + filterPressureDrop: .init( + value: measurement.$postFilterPressure.positiveValue - measurement.$returnPlenumPressure.positiveValue, + budget: budgets.filterBudget, + ratedPressures: ratedPressures, + ignoreMinimum: true + ), + returnPlenumPressure: .init( + value: measurement.$returnPlenumPressure.positiveValue, + budget: budgets.returnPlenumBudget, + ratedPressures: ratedPressures + ), + supplyPlenumPressure: .init( + value: measurement.$supplyPlenumPressure.positiveValue, + budget: budgets.supplyPlenumBudget, + ratedPressures: ratedPressures + ) + ) + } + } + + public struct FurnaceAndCoil: Equatable { @Positive @@ -128,6 +266,62 @@ public enum EquipmentMeasurement: Equatable { // case airflow // } //} +fileprivate extension Flagged { + + init( + value: Double, + budget: Percentage, + ratedPressures: RatedStaticPressures, + ignoreMinimum: Bool = false + ) { + let minimum = ignoreMinimum ? 0 : ratedPressures.minimum * budget.fraction + let maximum = ratedPressures.maximum * budget.fraction + let rated = ratedPressures.rated * budget.fraction + self.init( + wrappedValue: value, + .using(maximum: maximum, minimum: minimum, rated: rated) + ) + } + + init( + value: Double?, + budget: Percentage, + ratedPressures: RatedStaticPressures, + ignoreMinimum: Bool = false + ) { + guard let value else { + self = .error(message: "Value is not set.") + return + } + self.init( + value: value, + budget: budget, + ratedPressures: ratedPressures, + ignoreMinimum: ignoreMinimum + ) + } +} + +fileprivate func checkExternalStaticPressure( + value: Double, + ratedPressures: RatedStaticPressures +) -> Flagged { + .init( + wrappedValue: value, + .rated(ratedPressures) + ) +} + +fileprivate func checkAirflow( + value: Double?, + tons: EquipmentMetadata.CoolingCapacity? +) -> Flagged { + guard let value, let tons else { + return .init(wrappedValue: value ?? 0, .result(.good())) + } + return .init(wrappedValue: value, .airflow(tons: tons)) +} + #if DEBUG extension EquipmentMeasurement { @@ -153,4 +347,15 @@ extension EquipmentMeasurement { } } } + +extension EquipmentMeasurement.FlaggedMeasurement { + public static func mock(type equipmentType: EquipmentMeasurement.EquipmentType) -> Self { + .init( + budgets: .init(equipmentType: equipmentType, fanType: .variableSpeed), + measurement: .mock(type: equipmentType), + ratedPressures: .init(), + tons: .default + ) + } +} #endif diff --git a/Sources/SharedModels/EquipmentMetadata.swift b/Sources/SharedModels/EquipmentMetadata.swift new file mode 100644 index 0000000..6507741 --- /dev/null +++ b/Sources/SharedModels/EquipmentMetadata.swift @@ -0,0 +1,76 @@ +import Foundation + +public struct EquipmentMetadata: Equatable { + public var coolingCapacity: CoolingCapacity + public var fanType: FanType + public var ratedStaticPressure: RatedStaticPressures + + public init( + coolingCapacity: CoolingCapacity = .three, + fanType: FanType = .constantSpeed, + ratedStaticPressure: RatedStaticPressures = .init() + ) { + self.coolingCapacity = coolingCapacity + self.fanType = fanType + self.ratedStaticPressure = ratedStaticPressure + } + + public enum CoolingCapacity: Double, Equatable, CaseIterable, Identifiable, CustomStringConvertible { + case half = 0.5 + case threeQuarter = 0.75 + case one = 1 + case oneAndAHalf = 1.5 + case two = 2 + case twoAndAHalf = 2.5 + case three = 3 + case threeAndAHalf = 3.5 + case four = 4 + case five = 5 + + public var id: Self { self } + + public static var `default`: Self { .three } + + public var description: String { + switch self { + case .half: + return "1/2 Ton" + case .threeQuarter: + return "3/4 Ton" + case .one: + return "1 Ton" + case .oneAndAHalf: + return "1.5 Tons" + case .two: + return "2 Tons" + case .twoAndAHalf: + return "2.5 Tons" + case .three: + return "3 Tons" + case .threeAndAHalf: + return "3.5 Tons" + case .four: + return "4 Tons" + case .five: + return "5 Tons" + } + } + } + + public enum FanType: Hashable, Equatable, CaseIterable, CustomStringConvertible, Identifiable { + case constantSpeed + case variableSpeed + + public var id: Self { self } + public static var `default`: Self { .constantSpeed } + + public var description: String { + switch self { + case .constantSpeed: + return "Constant Speed" + case .variableSpeed: + return "Variable Speed" + } + } + } +} diff --git a/Sources/SharedModels/EquipmentType.swift b/Sources/SharedModels/EquipmentType.swift deleted file mode 100644 index cb5e4c6..0000000 --- a/Sources/SharedModels/EquipmentType.swift +++ /dev/null @@ -1,18 +0,0 @@ -import Foundation - -public enum EquipmentType: Equatable, CaseIterable, CustomStringConvertible, Identifiable { - case airHandler - case furnaceAndCoil - - public var id: Self { self } - - public var description: String { - switch self { - case .airHandler: - return "Air Handler" - - case .furnaceAndCoil: - return "Furnace & Coil" - } - } -} diff --git a/Sources/SharedModels/FanType.swift b/Sources/SharedModels/FanType.swift deleted file mode 100644 index d2fd2af..0000000 --- a/Sources/SharedModels/FanType.swift +++ /dev/null @@ -1,17 +0,0 @@ -import Foundation - -public enum FanType: Hashable, Equatable, CaseIterable, CustomStringConvertible, Identifiable { - case constantSpeed - case variableSpeed - - public var id: Self { self } - - public var description: String { - switch self { - case .constantSpeed: - return "Constant Speed" - case .variableSpeed: - return "Variable Speed" - } - } -} diff --git a/Sources/SharedModels/Flagged.swift b/Sources/SharedModels/Flagged.swift index fce4bbc..b16f053 100644 --- a/Sources/SharedModels/Flagged.swift +++ b/Sources/SharedModels/Flagged.swift @@ -151,11 +151,11 @@ extension Flagged.CheckHandler { } public static func airflow( - tons: CoolingCapacity, + tons: EquipmentMetadata.CoolingCapacity, ratings: RatedAirflowPerTon = .init(), goodMessage: Flagged.GoodMessageHandler? = nil ) -> Self { - .rated(RatedAirflowLimits(tons: tons, using: ratings), goodMessage: goodMessage) + .rated(RatedAirflowLimits(tons: tons, airflowPerTon: ratings), goodMessage: goodMessage) } public static func percent( diff --git a/Sources/SharedModels/FlaggedEquipmentMeasurement.swift b/Sources/SharedModels/FlaggedEquipmentMeasurement.swift deleted file mode 100644 index 353fd4e..0000000 --- a/Sources/SharedModels/FlaggedEquipmentMeasurement.swift +++ /dev/null @@ -1,190 +0,0 @@ -import Foundation - -// TODO: Needs updated for when forms are using `minmal` values. -public struct FlaggedEquipmentMeasurement: Equatable { - public var airflow: Flagged - public var coilPressureDrop: Flagged - public var externalStaticPressure: Flagged - public var filterPressureDrop: Flagged - public var returnPlenumPressure: Flagged - public var supplyPlenumPressure: Flagged - - public init( - airflow: Flagged, - coilPressureDrop: Flagged, - externalStaticPressure: Flagged, - filterPressureDrop: Flagged, - returnPlenumPressure: Flagged, - supplyPlenumPressure: Flagged - ) { - self.airflow = airflow - self.coilPressureDrop = coilPressureDrop - self.externalStaticPressure = externalStaticPressure - self.filterPressureDrop = filterPressureDrop - self.returnPlenumPressure = returnPlenumPressure - self.supplyPlenumPressure = supplyPlenumPressure - } - - public init( - budgets: BudgetedPercentEnvelope, - measurement: EquipmentMeasurement, - ratedPressures: RatedStaticPressures, - tons: CoolingCapacity? - ) { - switch measurement { - case let .airHandler(airHandler): - self = .airHandler( - budgets: budgets, - measurement: airHandler, - ratedPressures: ratedPressures, - tons: tons - ) - case let .furnaceAndCoil(furnaceAndCoil): - self = .furnaceAndCoil( - budgets: budgets, - measurement: furnaceAndCoil, - ratedPressures: ratedPressures, - tons: tons - ) - } - } - - public static func airHandler( - budgets: BudgetedPercentEnvelope, - measurement: EquipmentMeasurement.AirHandler, - ratedPressures: RatedStaticPressures, - tons: CoolingCapacity? - ) -> Self { - .init( - airflow: checkAirflow(value: measurement.airflow, tons: tons), - coilPressureDrop: .init( - wrappedValue: (measurement.$postCoilPressure.positiveValue - measurement.$postFilterPressure.positiveValue) ?? 0, - .result(.good()) - ), - externalStaticPressure: checkExternalStaticPressure( - value: measurement.externalStaticPressure, - ratedPressures: ratedPressures - ), - filterPressureDrop: .init( - value: measurement.$postFilterPressure.positiveValue - measurement.$returnPlenumPressure.positiveValue, - budget: budgets.filterBudget, - ratedPressures: ratedPressures - ), - returnPlenumPressure: .init( - value: measurement.$returnPlenumPressure.positiveValue, - budget: budgets.returnPlenumBudget, - ratedPressures: ratedPressures - ), - supplyPlenumPressure: .init( - value: measurement.$supplyPlenumPressure.positiveValue ?? 0, - budget: budgets.supplyPlenumBudget, - ratedPressures: ratedPressures - ) - ) - } - - public static func furnaceAndCoil( - budgets: BudgetedPercentEnvelope, - measurement: EquipmentMeasurement.FurnaceAndCoil, - ratedPressures: RatedStaticPressures, - tons: CoolingCapacity? - ) -> Self { - .init( - airflow: checkAirflow(value: measurement.airflow, tons: tons), - coilPressureDrop: .init( - value: measurement.$preCoilPressure.positiveValue - measurement.$supplyPlenumPressure.positiveValue, - budget: budgets.coilBudget, - ratedPressures: ratedPressures - ), - externalStaticPressure: checkExternalStaticPressure( - value: measurement.externalStaticPressure, - ratedPressures: ratedPressures - ), - filterPressureDrop: .init( - value: measurement.$postFilterPressure.positiveValue - measurement.$returnPlenumPressure.positiveValue, - budget: budgets.filterBudget, - ratedPressures: ratedPressures, - ignoreMinimum: true - ), - returnPlenumPressure: .init( - value: measurement.$returnPlenumPressure.positiveValue, - budget: budgets.returnPlenumBudget, - ratedPressures: ratedPressures - ), - supplyPlenumPressure: .init( - value: measurement.$supplyPlenumPressure.positiveValue, - budget: budgets.supplyPlenumBudget, - ratedPressures: ratedPressures - ) - ) - } -} - -// MARK: - Helpers -#if DEBUG -extension FlaggedEquipmentMeasurement { - public static func mock(type equipmentType: EquipmentType) -> Self { - .init( - budgets: .init(equipmentType: equipmentType, fanType: .variableSpeed), - measurement: .mock(type: equipmentType), - ratedPressures: .init(), - tons: .default - ) - } -} -#endif -fileprivate extension Flagged { - - init( - value: Double, - budget: Percentage, - ratedPressures: RatedStaticPressures, - ignoreMinimum: Bool = false - ) { - let minimum = ignoreMinimum ? 0 : ratedPressures.minimum * budget.fraction - let maximum = ratedPressures.maximum * budget.fraction - let rated = ratedPressures.rated * budget.fraction - self.init( - wrappedValue: value, - .using(maximum: maximum, minimum: minimum, rated: rated) - ) - } - - init( - value: Double?, - budget: Percentage, - ratedPressures: RatedStaticPressures, - ignoreMinimum: Bool = false - ) { - guard let value else { - self = .error(message: "Value is not set.") - return - } - self.init( - value: value, - budget: budget, - ratedPressures: ratedPressures, - ignoreMinimum: ignoreMinimum - ) - } -} - -fileprivate func checkExternalStaticPressure( - value: Double, - ratedPressures: RatedStaticPressures -) -> Flagged { - .init( - wrappedValue: value, - .rated(ratedPressures) - ) -} - -fileprivate func checkAirflow( - value: Double?, - tons: CoolingCapacity? -) -> Flagged { - guard let value, let tons else { - return .init(wrappedValue: value ?? 0, .result(.good())) - } - return .init(wrappedValue: value, .airflow(tons: tons)) -} diff --git a/Sources/SharedModels/RatedEnvelope.swift b/Sources/SharedModels/RatedEnvelope.swift index 3e38dd1..42ba965 100644 --- a/Sources/SharedModels/RatedEnvelope.swift +++ b/Sources/SharedModels/RatedEnvelope.swift @@ -40,7 +40,10 @@ extension RatedAirflowPerTon { // MARK: - Airflow Limits public typealias RatedAirflowLimits = RatedEnvelope extension RatedAirflowLimits { - public init(tons: CoolingCapacity, using airflowPerTon: RatedAirflowPerTon = .init()) { + public init( + tons: EquipmentMetadata.CoolingCapacity, + airflowPerTon: RatedAirflowPerTon = .init() + ) { self.init( maximum: airflowPerTon.maximum * tons.rawValue, minimum: airflowPerTon.minimum * tons.rawValue, diff --git a/Sources/Styleguide/Pickers/CaseIterablePicker.swift b/Sources/Styleguide/Pickers/CaseIterablePicker.swift new file mode 100644 index 0000000..19236ee --- /dev/null +++ b/Sources/Styleguide/Pickers/CaseIterablePicker.swift @@ -0,0 +1,40 @@ +import SwiftUI + +public struct CaseIterablePicker: View +where Content: CaseIterable, + Content: CustomStringConvertible, + Content: Hashable, + Content: Identifiable, + Content.AllCases: RandomAccessCollection, + Label: View +{ + + let label: () -> Label + @Binding var selection: Content + + public init( + selection: Binding, + @ViewBuilder label: @escaping () -> Label + ) { + self.label = label + self._selection = selection + } + + public var body: some View { + Picker(selection: $selection, label: label()) { + ForEach(Content.allCases) { + Text($0.description) + .tag($0) + } + } + } +} + +extension CaseIterablePicker where Label == Text { + + public init(_ title: S, selection: Binding) { + self.init(selection: selection) { + Text(title) + } + } +} diff --git a/Sources/Styleguide/Pickers/CoolingCapacityPicker.swift b/Sources/Styleguide/Pickers/CoolingCapacityPicker.swift index 466e943..7e74949 100644 --- a/Sources/Styleguide/Pickers/CoolingCapacityPicker.swift +++ b/Sources/Styleguide/Pickers/CoolingCapacityPicker.swift @@ -2,18 +2,13 @@ import SharedModels import SwiftUI public struct CoolingCapacityPicker: View { - @Binding var selection: CoolingCapacity - - public init(selection: Binding) { + @Binding var selection: EquipmentMetadata.CoolingCapacity + + public init(selection: Binding) { self._selection = selection } public var body: some View { - Picker("Cooling Capacity", selection: $selection) { - ForEach(CoolingCapacity.allCases) { - Text($0.description) - .tag($0) - } - } + CaseIterablePicker("Cooling Capacity", selection: $selection) } } diff --git a/Sources/Styleguide/Pickers/EquipmentTypePicker.swift b/Sources/Styleguide/Pickers/EquipmentTypePicker.swift index 4d7c059..673361d 100644 --- a/Sources/Styleguide/Pickers/EquipmentTypePicker.swift +++ b/Sources/Styleguide/Pickers/EquipmentTypePicker.swift @@ -2,18 +2,13 @@ import SharedModels import SwiftUI public struct EquipmentTypePicker: View { - @Binding var selection: EquipmentType + @Binding var selection: EquipmentMeasurement.EquipmentType - public init(selection: Binding) { + public init(selection: Binding) { self._selection = selection } public var body: some View { - Picker("Equipment Type", selection: $selection) { - ForEach(EquipmentType.allCases) { - Text($0.description) - .tag($0) - } - } + CaseIterablePicker("Equipment Type", selection: $selection) } } diff --git a/Sources/Styleguide/Pickers/FanTypePicker.swift b/Sources/Styleguide/Pickers/FanTypePicker.swift index b592fab..b18b5ef 100644 --- a/Sources/Styleguide/Pickers/FanTypePicker.swift +++ b/Sources/Styleguide/Pickers/FanTypePicker.swift @@ -3,19 +3,14 @@ import SwiftUI public struct FanTypePicker: View { - @Binding var selection: FanType + @Binding var selection: EquipmentMetadata.FanType - public init(selection: Binding) { + public init(selection: Binding) { self._selection = selection } public var body: some View { - Picker("Fan Type", selection: $selection) { - ForEach(FanType.allCases) { - Text($0.description) - .tag($0) - } - } + CaseIterablePicker("Fan Type", selection: $selection) } }