feat: Cleaning up shared models.
This commit is contained in:
@@ -5,9 +5,9 @@ public struct FlaggedEquipmentMeasurementView: View {
|
|||||||
|
|
||||||
@Environment(\.flaggedEquipmentMeasurementStyle) private var style
|
@Environment(\.flaggedEquipmentMeasurementStyle) private var style
|
||||||
|
|
||||||
let measurement: FlaggedEquipmentMeasurement
|
let measurement: EquipmentMeasurement.FlaggedMeasurement
|
||||||
|
|
||||||
public init(_ measurement: FlaggedEquipmentMeasurement) {
|
public init(_ measurement: EquipmentMeasurement.FlaggedMeasurement) {
|
||||||
self.measurement = measurement
|
self.measurement = measurement
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ public protocol FlaggedEquipmentMeasurementStyle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public struct FlaggedEquipmentMeasurementStyleConfiguration {
|
public struct FlaggedEquipmentMeasurementStyleConfiguration {
|
||||||
public let measurement: FlaggedEquipmentMeasurement
|
public let measurement: EquipmentMeasurement.FlaggedMeasurement
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct AnyFlaggedEquipmentMeasurementStyle: FlaggedEquipmentMeasurementStyle {
|
public struct AnyFlaggedEquipmentMeasurementStyle: FlaggedEquipmentMeasurementStyle {
|
||||||
@@ -54,7 +54,7 @@ public struct GridFlaggedEquipmentMeasurementStyle: FlaggedEquipmentMeasurementS
|
|||||||
|
|
||||||
public func makeBody(configuration: Configuration) -> some View {
|
public func makeBody(configuration: Configuration) -> some View {
|
||||||
Grid(alignment: .leading, verticalSpacing: 20) {
|
Grid(alignment: .leading, verticalSpacing: 20) {
|
||||||
ForEach(FlaggedEquipmentMeasurement.Key.allCases) { field in
|
ForEach(EquipmentMeasurement.FlaggedMeasurement.Key.allCases) { field in
|
||||||
FlaggedView(
|
FlaggedView(
|
||||||
field.title,
|
field.title,
|
||||||
flagged: configuration.measurement[keyPath: field.flaggedKeyPath]
|
flagged: configuration.measurement[keyPath: field.flaggedKeyPath]
|
||||||
@@ -93,7 +93,7 @@ extension View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Key
|
// MARK: - Key
|
||||||
fileprivate extension FlaggedEquipmentMeasurement {
|
fileprivate extension EquipmentMeasurement.FlaggedMeasurement {
|
||||||
// NOTE: These need to be kept in display order.
|
// NOTE: These need to be kept in display order.
|
||||||
enum Key: Hashable, CaseIterable, Identifiable {
|
enum Key: Hashable, CaseIterable, Identifiable {
|
||||||
case returnPlenum
|
case returnPlenum
|
||||||
@@ -122,7 +122,7 @@ fileprivate extension FlaggedEquipmentMeasurement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var flaggedKeyPath: KeyPath<FlaggedEquipmentMeasurement, Flagged> {
|
var flaggedKeyPath: KeyPath<EquipmentMeasurement.FlaggedMeasurement, Flagged> {
|
||||||
switch self {
|
switch self {
|
||||||
case .returnPlenum:
|
case .returnPlenum:
|
||||||
return \.returnPlenumPressure
|
return \.returnPlenumPressure
|
||||||
|
|||||||
@@ -18,14 +18,14 @@ public struct EquipmentMeasurementForm {
|
|||||||
public struct State: Equatable {
|
public struct State: Equatable {
|
||||||
@Presents public var destination: Destination.State?
|
@Presents public var destination: Destination.State?
|
||||||
public var allowEquipmentTypeSelection: Bool
|
public var allowEquipmentTypeSelection: Bool
|
||||||
public var equipmentType: EquipmentType
|
public var equipmentType: EquipmentMeasurement.EquipmentType
|
||||||
public var focusedField: Field?
|
public var focusedField: Field?
|
||||||
public var measurements: Measurements
|
public var measurements: Measurements
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
allowEquipmentTypeSelection: Bool = true,
|
allowEquipmentTypeSelection: Bool = true,
|
||||||
destination: Destination.State? = nil,
|
destination: Destination.State? = nil,
|
||||||
equipmentType: EquipmentType = .airHandler,
|
equipmentType: EquipmentMeasurement.EquipmentType = .airHandler,
|
||||||
focusedField: Field? = nil,
|
focusedField: Field? = nil,
|
||||||
measurements: Measurements = .init()
|
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 {
|
switch type {
|
||||||
|
|
||||||
case .airHandler:
|
case .airHandler:
|
||||||
@@ -229,14 +231,9 @@ public struct EquipmentMeasurementFormView: View {
|
|||||||
} header: {
|
} header: {
|
||||||
Text("Equipment Type")
|
Text("Equipment Type")
|
||||||
} footer: {
|
} footer: {
|
||||||
Picker("Equipment Type", selection: $store.equipmentType) {
|
EquipmentTypePicker(selection: $store.equipmentType)
|
||||||
ForEach(EquipmentType.allCases) {
|
.pickerStyle(.segmented)
|
||||||
Text($0.description)
|
.labelsHidden()
|
||||||
.tag($0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.pickerStyle(.segmented)
|
|
||||||
.labelsHidden()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Section {
|
Section {
|
||||||
|
|||||||
@@ -23,18 +23,18 @@ public struct EquipmentSettingsForm {
|
|||||||
public struct State: Equatable {
|
public struct State: Equatable {
|
||||||
|
|
||||||
@Presents public var destination: Destination.State?
|
@Presents public var destination: Destination.State?
|
||||||
public var coolingCapacity: CoolingCapacity
|
public var coolingCapacity: EquipmentMetadata.CoolingCapacity
|
||||||
public var equipmentType: EquipmentType
|
public var equipmentType: EquipmentMeasurement.EquipmentType
|
||||||
public var fanType: FanType
|
public var fanType: EquipmentMetadata.FanType
|
||||||
public var focusedField: Field? = nil
|
public var focusedField: Field? = nil
|
||||||
public var heatingCapacity: Double?
|
public var heatingCapacity: Double?
|
||||||
public var ratedStaticPressures: RatedStaticPressures
|
public var ratedStaticPressures: RatedStaticPressures
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
coolingCapacity: CoolingCapacity = .default,
|
coolingCapacity: EquipmentMetadata.CoolingCapacity = .default,
|
||||||
destination: Destination.State? = nil,
|
destination: Destination.State? = nil,
|
||||||
equipmentType: EquipmentType = .airHandler,
|
equipmentType: EquipmentMeasurement.EquipmentType = .airHandler,
|
||||||
fanType: FanType = .constantSpeed,
|
fanType: EquipmentMetadata.FanType = .constantSpeed,
|
||||||
heatingCapacity: Double? = nil,
|
heatingCapacity: Double? = nil,
|
||||||
ratedStaticPressures: RatedStaticPressures = .init()
|
ratedStaticPressures: RatedStaticPressures = .init()
|
||||||
) {
|
) {
|
||||||
@@ -154,12 +154,13 @@ public struct EquipmentSettingsFormView: View {
|
|||||||
: "Capacity"
|
: "Capacity"
|
||||||
)
|
)
|
||||||
Spacer()
|
Spacer()
|
||||||
Picker("Cooling Capcity", selection: $store.coolingCapacity) {
|
CoolingCapacityPicker(selection: $store.coolingCapacity)
|
||||||
ForEach(CoolingCapacity.allCases) {
|
// Picker("Cooling Capcity", selection: $store.coolingCapacity) {
|
||||||
Text($0.description)
|
// ForEach(CoolingCapacity.allCases) {
|
||||||
.tag($0)
|
// Text($0.description)
|
||||||
}
|
// .tag($0)
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
if store.equipmentType == .furnaceAndCoil {
|
if store.equipmentType == .furnaceAndCoil {
|
||||||
GridRow {
|
GridRow {
|
||||||
|
|||||||
@@ -10,13 +10,13 @@ public struct EstimationForm {
|
|||||||
@ObservableState
|
@ObservableState
|
||||||
public struct State: Equatable {
|
public struct State: Equatable {
|
||||||
public var cfmPerTon: Int
|
public var cfmPerTon: Int
|
||||||
public var coolingCapacity: CoolingCapacity
|
public var coolingCapacity: EquipmentMetadata.CoolingCapacity
|
||||||
public var filterPressureDrop: Double?
|
public var filterPressureDrop: Double?
|
||||||
public var name: String
|
public var name: String
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
cfmPerTon: Int = 350,
|
cfmPerTon: Int = 350,
|
||||||
coolingCapacity: CoolingCapacity = .default,
|
coolingCapacity: EquipmentMetadata.CoolingCapacity = .default,
|
||||||
filterPressureDrop: Double? = nil,
|
filterPressureDrop: Double? = nil,
|
||||||
name: String = ""
|
name: String = ""
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -35,13 +35,13 @@ public struct FlaggedMeasurementsList {
|
|||||||
|
|
||||||
public struct FlaggedMeasurementContainer: Equatable, Identifiable {
|
public struct FlaggedMeasurementContainer: Equatable, Identifiable {
|
||||||
public let id: UUID
|
public let id: UUID
|
||||||
public var flaggedMeasurement: FlaggedEquipmentMeasurement
|
public var flaggedMeasurement: EquipmentMeasurement.FlaggedMeasurement
|
||||||
public var name: String
|
public var name: String
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
id: UUID,
|
id: UUID,
|
||||||
name: String,
|
name: String,
|
||||||
flaggedMeasurement: FlaggedEquipmentMeasurement
|
flaggedMeasurement: EquipmentMeasurement.FlaggedMeasurement
|
||||||
) {
|
) {
|
||||||
self.id = id
|
self.id = id
|
||||||
self.name = name
|
self.name = name
|
||||||
@@ -58,8 +58,8 @@ public struct FlaggedMeasurementsList {
|
|||||||
|
|
||||||
@CasePathable
|
@CasePathable
|
||||||
public enum ReceiveAction {
|
public enum ReceiveAction {
|
||||||
case existingFlaggedMeasurement(FlaggedEquipmentMeasurement)
|
case existingFlaggedMeasurement(EquipmentMeasurement.FlaggedMeasurement)
|
||||||
case estimatedFlaggedMeasurement(name: String, measurement: FlaggedEquipmentMeasurement)
|
case estimatedFlaggedMeasurement(name: String, measurement: EquipmentMeasurement.FlaggedMeasurement)
|
||||||
}
|
}
|
||||||
|
|
||||||
@CasePathable
|
@CasePathable
|
||||||
@@ -190,7 +190,7 @@ public struct FlaggedMeasurementsList {
|
|||||||
filterPressureDrop: filterPressureDrop
|
filterPressureDrop: filterPressureDrop
|
||||||
)
|
)
|
||||||
|
|
||||||
let flaggedMeasurement = FlaggedEquipmentMeasurement(
|
let flaggedMeasurement = EquipmentMeasurement.FlaggedMeasurement(
|
||||||
budgets: budgets,
|
budgets: budgets,
|
||||||
measurement: measurement,
|
measurement: measurement,
|
||||||
ratedPressures: ratedStaticPressures,
|
ratedPressures: ratedStaticPressures,
|
||||||
|
|||||||
@@ -4,19 +4,19 @@ import SharedModels
|
|||||||
|
|
||||||
struct SharedSettings: Equatable {
|
struct SharedSettings: Equatable {
|
||||||
var budgets: BudgetedPercentEnvelope?
|
var budgets: BudgetedPercentEnvelope?
|
||||||
var coolingCapacity: CoolingCapacity
|
var coolingCapacity: EquipmentMetadata.CoolingCapacity
|
||||||
var equipmentMeasurement: EquipmentMeasurement?
|
var equipmentMeasurement: EquipmentMeasurement?
|
||||||
var fanType: FanType
|
var fanType: EquipmentMetadata.FanType
|
||||||
var flaggedEquipmentMeasurement: FlaggedEquipmentMeasurement?
|
var flaggedEquipmentMeasurement: EquipmentMeasurement.FlaggedMeasurement?
|
||||||
var heatingCapacity: Double?
|
var heatingCapacity: Double?
|
||||||
var ratedStaticPressures: RatedStaticPressures
|
var ratedStaticPressures: RatedStaticPressures
|
||||||
|
|
||||||
init(
|
init(
|
||||||
budgets: BudgetedPercentEnvelope? = nil,
|
budgets: BudgetedPercentEnvelope? = nil,
|
||||||
coolingCapacity: CoolingCapacity = .default,
|
coolingCapacity: EquipmentMetadata.CoolingCapacity = .default,
|
||||||
equipmentMeasurement: EquipmentMeasurement? = nil,
|
equipmentMeasurement: EquipmentMeasurement? = nil,
|
||||||
fanType: FanType = .constantSpeed,
|
fanType: EquipmentMetadata.FanType = .constantSpeed,
|
||||||
flaggedEquipmentMeasurement: FlaggedEquipmentMeasurement? = nil,
|
flaggedEquipmentMeasurement: EquipmentMeasurement.FlaggedMeasurement? = nil,
|
||||||
heatingCapacity: Double? = nil,
|
heatingCapacity: Double? = nil,
|
||||||
ratedStaticPressures: RatedStaticPressures = .init()
|
ratedStaticPressures: RatedStaticPressures = .init()
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -27,7 +27,10 @@ public struct BudgetedPercentEnvelope: Equatable {
|
|||||||
coilBudget + filterBudget + supplyPlenumBudget + returnPlenumBudget
|
coilBudget + filterBudget + supplyPlenumBudget + returnPlenumBudget
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(equipmentType: EquipmentType, fanType: FanType) {
|
public init(
|
||||||
|
equipmentType: EquipmentMeasurement.EquipmentType,
|
||||||
|
fanType: EquipmentMetadata.FanType
|
||||||
|
) {
|
||||||
switch equipmentType {
|
switch equipmentType {
|
||||||
case .furnaceAndCoil:
|
case .furnaceAndCoil:
|
||||||
switch fanType {
|
switch fanType {
|
||||||
|
|||||||
@@ -1,43 +1,2 @@
|
|||||||
import Foundation
|
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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ public enum EquipmentMeasurement: Equatable {
|
|||||||
|
|
||||||
case airHandler(AirHandler)
|
case airHandler(AirHandler)
|
||||||
case furnaceAndCoil(FurnaceAndCoil)
|
case furnaceAndCoil(FurnaceAndCoil)
|
||||||
|
|
||||||
public var equipmentType: EquipmentType {
|
public var equipmentType: EquipmentType {
|
||||||
switch self {
|
switch self {
|
||||||
case .airHandler:
|
case .airHandler:
|
||||||
@@ -25,7 +25,7 @@ public enum EquipmentMeasurement: Equatable {
|
|||||||
return furnaceAndCoil.externalStaticPressure
|
return furnaceAndCoil.externalStaticPressure
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct AirHandler: Equatable {
|
public struct AirHandler: Equatable {
|
||||||
|
|
||||||
@Positive
|
@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 {
|
public struct FurnaceAndCoil: Equatable {
|
||||||
|
|
||||||
@Positive
|
@Positive
|
||||||
@@ -128,6 +266,62 @@ public enum EquipmentMeasurement: Equatable {
|
|||||||
// case airflow
|
// 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
|
#if DEBUG
|
||||||
extension EquipmentMeasurement {
|
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
|
#endif
|
||||||
|
|||||||
76
Sources/SharedModels/EquipmentMetadata.swift
Normal file
76
Sources/SharedModels/EquipmentMetadata.swift
Normal file
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -151,11 +151,11 @@ extension Flagged.CheckHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static func airflow(
|
public static func airflow(
|
||||||
tons: CoolingCapacity,
|
tons: EquipmentMetadata.CoolingCapacity,
|
||||||
ratings: RatedAirflowPerTon = .init(),
|
ratings: RatedAirflowPerTon = .init(),
|
||||||
goodMessage: Flagged.GoodMessageHandler? = nil
|
goodMessage: Flagged.GoodMessageHandler? = nil
|
||||||
) -> Self {
|
) -> Self {
|
||||||
.rated(RatedAirflowLimits(tons: tons, using: ratings), goodMessage: goodMessage)
|
.rated(RatedAirflowLimits(tons: tons, airflowPerTon: ratings), goodMessage: goodMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func percent(
|
public static func percent(
|
||||||
|
|||||||
@@ -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))
|
|
||||||
}
|
|
||||||
@@ -40,7 +40,10 @@ extension RatedAirflowPerTon {
|
|||||||
// MARK: - Airflow Limits
|
// MARK: - Airflow Limits
|
||||||
public typealias RatedAirflowLimits = RatedEnvelope<AirflowLimits>
|
public typealias RatedAirflowLimits = RatedEnvelope<AirflowLimits>
|
||||||
extension RatedAirflowLimits {
|
extension RatedAirflowLimits {
|
||||||
public init(tons: CoolingCapacity, using airflowPerTon: RatedAirflowPerTon = .init()) {
|
public init(
|
||||||
|
tons: EquipmentMetadata.CoolingCapacity,
|
||||||
|
airflowPerTon: RatedAirflowPerTon = .init()
|
||||||
|
) {
|
||||||
self.init(
|
self.init(
|
||||||
maximum: airflowPerTon.maximum * tons.rawValue,
|
maximum: airflowPerTon.maximum * tons.rawValue,
|
||||||
minimum: airflowPerTon.minimum * tons.rawValue,
|
minimum: airflowPerTon.minimum * tons.rawValue,
|
||||||
|
|||||||
40
Sources/Styleguide/Pickers/CaseIterablePicker.swift
Normal file
40
Sources/Styleguide/Pickers/CaseIterablePicker.swift
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
public struct CaseIterablePicker<Label, Content>: View
|
||||||
|
where Content: CaseIterable,
|
||||||
|
Content: CustomStringConvertible,
|
||||||
|
Content: Hashable,
|
||||||
|
Content: Identifiable,
|
||||||
|
Content.AllCases: RandomAccessCollection,
|
||||||
|
Label: View
|
||||||
|
{
|
||||||
|
|
||||||
|
let label: () -> Label
|
||||||
|
@Binding<Content> var selection: Content
|
||||||
|
|
||||||
|
public init(
|
||||||
|
selection: Binding<Content>,
|
||||||
|
@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<S: StringProtocol>(_ title: S, selection: Binding<Content>) {
|
||||||
|
self.init(selection: selection) {
|
||||||
|
Text(title)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,18 +2,13 @@ import SharedModels
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
public struct CoolingCapacityPicker: View {
|
public struct CoolingCapacityPicker: View {
|
||||||
@Binding var selection: CoolingCapacity
|
@Binding var selection: EquipmentMetadata.CoolingCapacity
|
||||||
|
|
||||||
public init(selection: Binding<CoolingCapacity>) {
|
public init(selection: Binding<EquipmentMetadata.CoolingCapacity>) {
|
||||||
self._selection = selection
|
self._selection = selection
|
||||||
}
|
}
|
||||||
|
|
||||||
public var body: some View {
|
public var body: some View {
|
||||||
Picker("Cooling Capacity", selection: $selection) {
|
CaseIterablePicker("Cooling Capacity", selection: $selection)
|
||||||
ForEach(CoolingCapacity.allCases) {
|
|
||||||
Text($0.description)
|
|
||||||
.tag($0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,18 +2,13 @@ import SharedModels
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
public struct EquipmentTypePicker: View {
|
public struct EquipmentTypePicker: View {
|
||||||
@Binding var selection: EquipmentType
|
@Binding var selection: EquipmentMeasurement.EquipmentType
|
||||||
|
|
||||||
public init(selection: Binding<EquipmentType>) {
|
public init(selection: Binding<EquipmentMeasurement.EquipmentType>) {
|
||||||
self._selection = selection
|
self._selection = selection
|
||||||
}
|
}
|
||||||
|
|
||||||
public var body: some View {
|
public var body: some View {
|
||||||
Picker("Equipment Type", selection: $selection) {
|
CaseIterablePicker("Equipment Type", selection: $selection)
|
||||||
ForEach(EquipmentType.allCases) {
|
|
||||||
Text($0.description)
|
|
||||||
.tag($0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,19 +3,14 @@ import SwiftUI
|
|||||||
|
|
||||||
public struct FanTypePicker: View {
|
public struct FanTypePicker: View {
|
||||||
|
|
||||||
@Binding var selection: FanType
|
@Binding var selection: EquipmentMetadata.FanType
|
||||||
|
|
||||||
public init(selection: Binding<FanType>) {
|
public init(selection: Binding<EquipmentMetadata.FanType>) {
|
||||||
self._selection = selection
|
self._selection = selection
|
||||||
}
|
}
|
||||||
|
|
||||||
public var body: some View {
|
public var body: some View {
|
||||||
Picker("Fan Type", selection: $selection) {
|
CaseIterablePicker("Fan Type", selection: $selection)
|
||||||
ForEach(FanType.allCases) {
|
|
||||||
Text($0.description)
|
|
||||||
.tag($0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user