feat: Working on pressure estimations feature, integrating all views and shared settings
This commit is contained in:
@@ -66,15 +66,15 @@ public struct EstimatedPressureDependency {
|
|||||||
|
|
||||||
extension EstimatedPressureDependency {
|
extension EstimatedPressureDependency {
|
||||||
private func estimatedPressure(
|
private func estimatedPressure(
|
||||||
existingPressure: Positive<Double?>,
|
existingPressure: Positive<Double>,
|
||||||
existingAirflow: Double,
|
existingAirflow: Positive<Double>,
|
||||||
targetAirflow: Double
|
targetAirflow: Positive<Double>
|
||||||
) async throws -> Positive<Double> {
|
) async throws -> Positive<Double> {
|
||||||
try await self.estimatedPressure(
|
try await self.estimatedPressure(
|
||||||
.init(
|
.init(
|
||||||
existingPressure: existingPressure.positiveValue ?? 0,
|
existingPressure: existingPressure.positiveValue,
|
||||||
existingAirflow: existingAirflow,
|
existingAirflow: existingAirflow.positiveValue,
|
||||||
targetAirflow: targetAirflow
|
targetAirflow: targetAirflow.positiveValue
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -83,78 +83,75 @@ extension EstimatedPressureDependency {
|
|||||||
equipmentMeasurement: EquipmentMeasurement,
|
equipmentMeasurement: EquipmentMeasurement,
|
||||||
airflow updatedAirflow: Double
|
airflow updatedAirflow: Double
|
||||||
) async throws -> EquipmentMeasurement {
|
) async throws -> EquipmentMeasurement {
|
||||||
|
let updatedAirflow = Positive(updatedAirflow)
|
||||||
|
|
||||||
switch equipmentMeasurement {
|
switch equipmentMeasurement {
|
||||||
case let .airHandler(airHandler):
|
case let .airHandler(airHandler):
|
||||||
guard let existingAirflow = airHandler.airflow else {
|
|
||||||
throw InvalidAirflow()
|
|
||||||
}
|
|
||||||
return try await .airHandler(
|
return try await .airHandler(
|
||||||
.init(
|
.init(
|
||||||
airflow: updatedAirflow,
|
airflow: updatedAirflow,
|
||||||
|
manufacturersIncludedFilterPressureDrop: airHandler.$manufacturersIncludedFilterPressureDrop,
|
||||||
returnPlenumPressure: self.estimatedPressure(
|
returnPlenumPressure: self.estimatedPressure(
|
||||||
existingPressure: airHandler.$returnPlenumPressure,
|
existingPressure: airHandler.$returnPlenumPressure,
|
||||||
existingAirflow: existingAirflow,
|
existingAirflow: airHandler.$airflow,
|
||||||
targetAirflow: updatedAirflow
|
targetAirflow: updatedAirflow
|
||||||
),
|
),
|
||||||
postFilterPressure: self.estimatedPressure(
|
postFilterPressure: self.estimatedPressure(
|
||||||
existingPressure: airHandler.$postFilterPressure,
|
existingPressure: airHandler.$postFilterPressure,
|
||||||
existingAirflow: existingAirflow,
|
existingAirflow: airHandler.$airflow,
|
||||||
targetAirflow: updatedAirflow
|
targetAirflow: updatedAirflow
|
||||||
),
|
),
|
||||||
postCoilPressure: self.estimatedPressure(
|
postCoilPressure: self.estimatedPressure(
|
||||||
existingPressure: airHandler.$postCoilPressure,
|
existingPressure: airHandler.$postCoilPressure,
|
||||||
existingAirflow: existingAirflow,
|
existingAirflow: airHandler.$airflow,
|
||||||
targetAirflow: updatedAirflow
|
targetAirflow: updatedAirflow
|
||||||
),
|
),
|
||||||
supplyPlenumPressure: self.estimatedPressure(
|
supplyPlenumPressure: self.estimatedPressure(
|
||||||
existingPressure: airHandler.$supplyPlenumPressure,
|
existingPressure: airHandler.$supplyPlenumPressure,
|
||||||
existingAirflow: existingAirflow,
|
existingAirflow: airHandler.$airflow,
|
||||||
targetAirflow: updatedAirflow
|
targetAirflow: updatedAirflow
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
case let .furnaceAndCoil(furnaceAndCoil):
|
case let .furnaceAndCoil(furnaceAndCoil):
|
||||||
guard let existingAirflow = furnaceAndCoil.airflow else {
|
|
||||||
throw InvalidAirflow()
|
|
||||||
}
|
|
||||||
return try await .furnaceAndCoil(
|
return try await .furnaceAndCoil(
|
||||||
.init(
|
.init(
|
||||||
airflow: updatedAirflow,
|
airflow: updatedAirflow,
|
||||||
|
manufacturersIncludedFilterPressureDrop: furnaceAndCoil.$manufacturersIncludedFilterPressureDrop,
|
||||||
returnPlenumPressure: self.estimatedPressure(
|
returnPlenumPressure: self.estimatedPressure(
|
||||||
existingPressure: furnaceAndCoil.$returnPlenumPressure,
|
existingPressure: furnaceAndCoil.$returnPlenumPressure,
|
||||||
existingAirflow: existingAirflow,
|
existingAirflow: furnaceAndCoil.$airflow,
|
||||||
targetAirflow: updatedAirflow
|
targetAirflow: updatedAirflow
|
||||||
),
|
),
|
||||||
postFilterPressure: self.estimatedPressure(
|
postFilterPressure: self.estimatedPressure(
|
||||||
existingPressure: furnaceAndCoil.$postFilterPressure,
|
existingPressure: furnaceAndCoil.$postFilterPressure,
|
||||||
existingAirflow: existingAirflow,
|
existingAirflow: furnaceAndCoil.$airflow,
|
||||||
targetAirflow: updatedAirflow
|
targetAirflow: updatedAirflow
|
||||||
),
|
),
|
||||||
preCoilPressure: self.estimatedPressure(
|
preCoilPressure: self.estimatedPressure(
|
||||||
existingPressure: furnaceAndCoil.$preCoilPressure,
|
existingPressure: furnaceAndCoil.$preCoilPressure,
|
||||||
existingAirflow: existingAirflow,
|
existingAirflow: furnaceAndCoil.$airflow,
|
||||||
targetAirflow: updatedAirflow
|
targetAirflow: updatedAirflow
|
||||||
),
|
),
|
||||||
supplyPlenumPressure: self.estimatedPressure(
|
supplyPlenumPressure: self.estimatedPressure(
|
||||||
existingPressure: furnaceAndCoil.$supplyPlenumPressure,
|
existingPressure: furnaceAndCoil.$supplyPlenumPressure,
|
||||||
existingAirflow: existingAirflow,
|
existingAirflow: furnaceAndCoil.$airflow,
|
||||||
targetAirflow: updatedAirflow
|
targetAirflow: updatedAirflow
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func estimatedPressure(
|
public func estimatedPressure(
|
||||||
equipmentMeasurement: EquipmentMeasurement,
|
equipmentMeasurement: EquipmentMeasurement,
|
||||||
airflow updatedAirflow: Double,
|
airflow updatedAirflow: Positive<Double>,
|
||||||
filterPressureDrop: Positive<Double>
|
filterPressureDrop: Positive<Double>
|
||||||
) async throws -> EquipmentMeasurement {
|
) async throws -> EquipmentMeasurement {
|
||||||
let estimate = try await estimatedPressure(
|
let estimate = try await estimatedPressure(
|
||||||
equipmentMeasurement: equipmentMeasurement,
|
equipmentMeasurement: equipmentMeasurement,
|
||||||
airflow: updatedAirflow
|
airflow: updatedAirflow.positiveValue
|
||||||
)
|
)
|
||||||
|
|
||||||
switch estimate {
|
switch estimate {
|
||||||
@@ -183,7 +180,7 @@ extension EstimatedPressureDependency {
|
|||||||
return try await estimatedPressure(
|
return try await estimatedPressure(
|
||||||
equipmentMeasurement: equipmentMeasurement,
|
equipmentMeasurement: equipmentMeasurement,
|
||||||
airflow: updatedAirflow,
|
airflow: updatedAirflow,
|
||||||
filterPressureDrop: filterPressureDrop
|
filterPressureDrop: Positive(filterPressureDrop.positiveValue)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -219,42 +216,6 @@ extension EstimatedPressureDependency: DependencyKey {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 { }
|
struct InvalidAirflow: Error { }
|
||||||
|
|
||||||
enum LessThanZeroError: Error {
|
enum LessThanZeroError: Error {
|
||||||
|
|||||||
@@ -145,6 +145,7 @@ fileprivate extension EquipmentMeasurement.FlaggedMeasurement {
|
|||||||
#if DEBUG
|
#if DEBUG
|
||||||
private let equipmentMeasurement = EquipmentMeasurement.airHandler(.init(
|
private let equipmentMeasurement = EquipmentMeasurement.airHandler(.init(
|
||||||
airflow: 1600,
|
airflow: 1600,
|
||||||
|
manufacturersIncludedFilterPressureDrop: 0.1,
|
||||||
returnPlenumPressure: 0.37,
|
returnPlenumPressure: 0.37,
|
||||||
postFilterPressure: 0.78,
|
postFilterPressure: 0.78,
|
||||||
postCoilPressure: 0.9,
|
postCoilPressure: 0.9,
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import ComposableArchitecture
|
import ComposableArchitecture
|
||||||
|
import DependenciesAdditions
|
||||||
import SharedModels
|
import SharedModels
|
||||||
import Styleguide
|
import Styleguide
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
|
|
||||||
@Reducer
|
@Reducer
|
||||||
public struct EquipmentMeasurementForm {
|
public struct EquipmentMeasurementForm {
|
||||||
|
|
||||||
@@ -11,12 +11,14 @@ public struct EquipmentMeasurementForm {
|
|||||||
|
|
||||||
@Reducer(state: .equatable)
|
@Reducer(state: .equatable)
|
||||||
public enum Destination {
|
public enum Destination {
|
||||||
|
case flaggedMeasurementsList(FlaggedMeasurementsList)
|
||||||
case infoView(InfoViewFeature)
|
case infoView(InfoViewFeature)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ObservableState
|
@ObservableState
|
||||||
public struct State: Equatable {
|
public struct State: Equatable {
|
||||||
@Presents public var destination: Destination.State?
|
@Presents public var destination: Destination.State?
|
||||||
|
@Shared public var sharedSettings: SharedPressureEstimationSettings
|
||||||
public var allowEquipmentTypeSelection: Bool
|
public var allowEquipmentTypeSelection: Bool
|
||||||
public var equipmentType: EquipmentMeasurement.EquipmentType
|
public var equipmentType: EquipmentMeasurement.EquipmentType
|
||||||
public var focusedField: Field?
|
public var focusedField: Field?
|
||||||
@@ -25,12 +27,14 @@ public struct EquipmentMeasurementForm {
|
|||||||
public init(
|
public init(
|
||||||
allowEquipmentTypeSelection: Bool = true,
|
allowEquipmentTypeSelection: Bool = true,
|
||||||
destination: Destination.State? = nil,
|
destination: Destination.State? = nil,
|
||||||
|
sharedSettings: Shared<SharedPressureEstimationSettings>,
|
||||||
equipmentType: EquipmentMeasurement.EquipmentType = .airHandler,
|
equipmentType: EquipmentMeasurement.EquipmentType = .airHandler,
|
||||||
focusedField: Field? = nil,
|
focusedField: Field? = nil,
|
||||||
measurements: Measurements = .init()
|
measurements: Measurements = .init()
|
||||||
) {
|
) {
|
||||||
self.allowEquipmentTypeSelection = allowEquipmentTypeSelection
|
self.allowEquipmentTypeSelection = allowEquipmentTypeSelection
|
||||||
self.destination = destination
|
self.destination = destination
|
||||||
|
self._sharedSettings = sharedSettings
|
||||||
self.equipmentType = equipmentType
|
self.equipmentType = equipmentType
|
||||||
self.focusedField = focusedField
|
self.focusedField = focusedField
|
||||||
self.measurements = measurements
|
self.measurements = measurements
|
||||||
@@ -39,14 +43,27 @@ public struct EquipmentMeasurementForm {
|
|||||||
public var equipmentMeasurement: EquipmentMeasurement {
|
public var equipmentMeasurement: EquipmentMeasurement {
|
||||||
measurements.equipmentMeasurement(type: equipmentType)
|
measurements.equipmentMeasurement(type: equipmentType)
|
||||||
}
|
}
|
||||||
|
|
||||||
public var isValid: Bool {
|
private var baseValidations: Bool {
|
||||||
measurements.airflow != nil
|
return measurements.airflow != nil
|
||||||
&& measurements.returnPlenumPressure != nil
|
&& measurements.returnPlenumPressure != nil
|
||||||
&& measurements.postFilterPressure != nil
|
|
||||||
&& measurements.coilPressure != nil
|
|
||||||
&& measurements.supplyPlenumPressure != nil
|
&& measurements.supplyPlenumPressure != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var furnaceAndCoilValidations: Bool {
|
||||||
|
return measurements.postFilterPressure != nil
|
||||||
|
&& measurements.coilPressure != nil
|
||||||
|
&& baseValidations
|
||||||
|
}
|
||||||
|
|
||||||
|
public var isValid: Bool {
|
||||||
|
switch equipmentType {
|
||||||
|
case .airHandler:
|
||||||
|
return baseValidations
|
||||||
|
case .furnaceAndCoil:
|
||||||
|
return furnaceAndCoilValidations
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public struct Measurements: Equatable {
|
public struct Measurements: Equatable {
|
||||||
public var airflow: Double?
|
public var airflow: Double?
|
||||||
@@ -96,23 +113,24 @@ public struct EquipmentMeasurementForm {
|
|||||||
type: EquipmentMeasurement.EquipmentType
|
type: EquipmentMeasurement.EquipmentType
|
||||||
) -> EquipmentMeasurement {
|
) -> EquipmentMeasurement {
|
||||||
switch type {
|
switch type {
|
||||||
|
|
||||||
case .airHandler:
|
case .airHandler:
|
||||||
return .airHandler(.init(
|
return .airHandler(.init(
|
||||||
airflow: airflow,
|
airflow: airflow ?? 0,
|
||||||
returnPlenumPressure: returnPlenumPressure,
|
manufacturersIncludedFilterPressureDrop: 0.1,
|
||||||
postFilterPressure: postFilterPressure,
|
returnPlenumPressure: returnPlenumPressure ?? 0,
|
||||||
postCoilPressure: coilPressure,
|
postFilterPressure: postFilterPressure ?? 0,
|
||||||
supplyPlenumPressure: supplyPlenumPressure
|
postCoilPressure: coilPressure ?? 0,
|
||||||
|
supplyPlenumPressure: supplyPlenumPressure ?? 0
|
||||||
))
|
))
|
||||||
|
|
||||||
case .furnaceAndCoil:
|
case .furnaceAndCoil:
|
||||||
return .furnaceAndCoil(.init(
|
return .furnaceAndCoil(.init(
|
||||||
airflow: airflow,
|
airflow: airflow ?? 0,
|
||||||
returnPlenumPressure: returnPlenumPressure,
|
manufacturersIncludedFilterPressureDrop: 0,
|
||||||
postFilterPressure: postFilterPressure,
|
returnPlenumPressure: returnPlenumPressure ?? 0,
|
||||||
preCoilPressure: coilPressure,
|
postFilterPressure: postFilterPressure ?? 0,
|
||||||
supplyPlenumPressure: supplyPlenumPressure
|
preCoilPressure: coilPressure ?? 0,
|
||||||
|
supplyPlenumPressure: supplyPlenumPressure ?? 0
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -138,11 +156,15 @@ public struct EquipmentMeasurementForm {
|
|||||||
@CasePathable
|
@CasePathable
|
||||||
public enum View {
|
public enum View {
|
||||||
case infoButtonTapped
|
case infoButtonTapped
|
||||||
|
case nextButtonTapped
|
||||||
|
case onAppear
|
||||||
case resetButtonTapped
|
case resetButtonTapped
|
||||||
case submitField
|
case submitField
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Dependency(\.logger["\(Self.self)"]) var logger
|
||||||
|
|
||||||
public var body: some Reducer<State, Action> {
|
public var body: some Reducer<State, Action> {
|
||||||
BindingReducer()
|
BindingReducer()
|
||||||
Reduce<State, Action> { state, action in
|
Reduce<State, Action> { state, action in
|
||||||
@@ -160,12 +182,37 @@ public struct EquipmentMeasurementForm {
|
|||||||
case .infoButtonTapped:
|
case .infoButtonTapped:
|
||||||
state.destination = .infoView(.init())
|
state.destination = .infoView(.init())
|
||||||
return .none
|
return .none
|
||||||
|
|
||||||
|
case .nextButtonTapped:
|
||||||
|
guard state.isValid else {
|
||||||
|
return .fail(
|
||||||
|
"""
|
||||||
|
Received next button tapped action, but the state is not valid.
|
||||||
|
|
||||||
|
This is considered an application logic error.
|
||||||
|
""",
|
||||||
|
logger: logger
|
||||||
|
)
|
||||||
|
}
|
||||||
|
state.sharedSettings.equipmentMeasurement = state.equipmentMeasurement
|
||||||
|
state.destination = .flaggedMeasurementsList(.init(sharedSettings: state.$sharedSettings))
|
||||||
|
return .none
|
||||||
|
|
||||||
|
case .onAppear:
|
||||||
|
guard let measurement = state.sharedSettings.equipmentMeasurement else {
|
||||||
|
return .none
|
||||||
|
}
|
||||||
|
state.measurements = .init(equipmentMeasurement: measurement)
|
||||||
|
return .none
|
||||||
|
|
||||||
case .resetButtonTapped:
|
case .resetButtonTapped:
|
||||||
state.measurements = .init()
|
state.measurements = .init()
|
||||||
return .none
|
return .none
|
||||||
|
|
||||||
case .submitField:
|
case .submitField:
|
||||||
|
if state.focusedField == .airflow {
|
||||||
|
return .send(.view(.nextButtonTapped))
|
||||||
|
}
|
||||||
state.focusedField = state.focusedField?.next
|
state.focusedField = state.focusedField?.next
|
||||||
return .none
|
return .none
|
||||||
|
|
||||||
@@ -183,6 +230,12 @@ extension Store where State == EquipmentMeasurementForm.State {
|
|||||||
) -> String {
|
) -> String {
|
||||||
let label = label(field: field)
|
let label = label(field: field)
|
||||||
guard field != .airflow else { return label }
|
guard field != .airflow else { return label }
|
||||||
|
if field == .coilPressure && state.equipmentType == .airHandler {
|
||||||
|
return "\(label) (Optional)"
|
||||||
|
}
|
||||||
|
if field == .postFilterPressure && state.equipmentType == .airHandler {
|
||||||
|
return "\(label) (Optional)"
|
||||||
|
}
|
||||||
return "\(label) Pressure"
|
return "\(label) Pressure"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,8 +322,14 @@ public struct EquipmentMeasurementFormView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.onAppear { send(.onAppear) }
|
||||||
.textLabelStyle(.boldSecondary)
|
.textLabelStyle(.boldSecondary)
|
||||||
.textFieldStyle(.roundedBorder)
|
.textFieldStyle(.roundedBorder)
|
||||||
|
.toolbar {
|
||||||
|
NextButton { send(.nextButtonTapped) }
|
||||||
|
.nextButtonStyle(.toolbar)
|
||||||
|
.disabled(!store.isValid)
|
||||||
|
}
|
||||||
.sheet(
|
.sheet(
|
||||||
item: $store.scope(state: \.destination?.infoView, action: \.destination.infoView)
|
item: $store.scope(state: \.destination?.infoView, action: \.destination.infoView)
|
||||||
){ store in
|
){ store in
|
||||||
@@ -278,6 +337,14 @@ public struct EquipmentMeasurementFormView: View {
|
|||||||
InfoView(store: store)
|
InfoView(store: store)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.navigationDestination(
|
||||||
|
item: $store.scope(
|
||||||
|
state: \.destination?.flaggedMeasurementsList,
|
||||||
|
action: \.destination.flaggedMeasurementsList
|
||||||
|
)
|
||||||
|
) { store in
|
||||||
|
FlaggedMeasurementListView(store: store)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func textField(
|
private func textField(
|
||||||
@@ -350,7 +417,9 @@ fileprivate extension InfoViewFeature.State {
|
|||||||
#Preview {
|
#Preview {
|
||||||
NavigationStack {
|
NavigationStack {
|
||||||
EquipmentMeasurementFormView(
|
EquipmentMeasurementFormView(
|
||||||
store: Store(initialState: EquipmentMeasurementForm.State()) {
|
store: Store(initialState: EquipmentMeasurementForm.State(
|
||||||
|
sharedSettings: Shared(SharedPressureEstimationSettings()))
|
||||||
|
) {
|
||||||
EquipmentMeasurementForm()
|
EquipmentMeasurementForm()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ import SharedModels
|
|||||||
import Styleguide
|
import Styleguide
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
|
|
||||||
@Reducer
|
@Reducer
|
||||||
public struct EquipmentSettingsForm {
|
public struct EquipmentSettingsForm {
|
||||||
|
|
||||||
@CasePathable
|
@CasePathable
|
||||||
public enum InfoView {
|
public enum InfoView {
|
||||||
case capacities
|
case capacities
|
||||||
|
case manufacturersIncludedFilterPressureDrop
|
||||||
case ratedStaticPressures
|
case ratedStaticPressures
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,34 +21,27 @@ public struct EquipmentSettingsForm {
|
|||||||
|
|
||||||
@ObservableState
|
@ObservableState
|
||||||
public struct State: Equatable {
|
public struct State: Equatable {
|
||||||
|
|
||||||
@Presents public var destination: Destination.State?
|
@Presents public var destination: Destination.State?
|
||||||
public var coolingCapacity: EquipmentMetadata.CoolingCapacity
|
public var includesFilterDrop: Bool
|
||||||
public var equipmentType: EquipmentMeasurement.EquipmentType
|
public var equipmentType: EquipmentMeasurement.EquipmentType
|
||||||
public var fanType: EquipmentMetadata.FanType
|
|
||||||
public var focusedField: Field? = nil
|
public var focusedField: Field? = nil
|
||||||
public var heatingCapacity: Double?
|
@Shared public var sharedSettings: SharedPressureEstimationSettings
|
||||||
public var ratedStaticPressures: RatedStaticPressures
|
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
coolingCapacity: EquipmentMetadata.CoolingCapacity = .default,
|
|
||||||
destination: Destination.State? = nil,
|
destination: Destination.State? = nil,
|
||||||
|
includesFilterDrop: Bool = false,
|
||||||
equipmentType: EquipmentMeasurement.EquipmentType = .airHandler,
|
equipmentType: EquipmentMeasurement.EquipmentType = .airHandler,
|
||||||
fanType: EquipmentMetadata.FanType = .constantSpeed,
|
sharedSettings: Shared<SharedPressureEstimationSettings>
|
||||||
heatingCapacity: Double? = nil,
|
|
||||||
ratedStaticPressures: RatedStaticPressures = .init()
|
|
||||||
) {
|
) {
|
||||||
self.coolingCapacity = coolingCapacity
|
|
||||||
self.destination = destination
|
self.destination = destination
|
||||||
|
self.includesFilterDrop = includesFilterDrop
|
||||||
self.equipmentType = equipmentType
|
self.equipmentType = equipmentType
|
||||||
self.fanType = fanType
|
self._sharedSettings = sharedSettings
|
||||||
self.heatingCapacity = heatingCapacity
|
|
||||||
self.ratedStaticPressures = ratedStaticPressures
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public var isValid: Bool {
|
public var isValid: Bool {
|
||||||
guard equipmentType == .furnaceAndCoil else { return true }
|
guard equipmentType == .furnaceAndCoil else { return true }
|
||||||
return heatingCapacity != nil
|
return sharedSettings.heatingCapacity != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: These need to be in display order.
|
// Note: These need to be in display order.
|
||||||
@@ -57,6 +50,7 @@ public struct EquipmentSettingsForm {
|
|||||||
case minimumStaticPressure
|
case minimumStaticPressure
|
||||||
case maximumStaticPressure
|
case maximumStaticPressure
|
||||||
case ratedStaticPressure
|
case ratedStaticPressure
|
||||||
|
case manufacturersIncludedFilterPressureDrop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +62,6 @@ public struct EquipmentSettingsForm {
|
|||||||
@CasePathable
|
@CasePathable
|
||||||
public enum View {
|
public enum View {
|
||||||
case infoButtonTapped(InfoView)
|
case infoButtonTapped(InfoView)
|
||||||
case nextButtonTapped
|
|
||||||
case resetButtonTapped
|
case resetButtonTapped
|
||||||
case submitField
|
case submitField
|
||||||
}
|
}
|
||||||
@@ -79,6 +72,17 @@ public struct EquipmentSettingsForm {
|
|||||||
Reduce<State, Action> { state, action in
|
Reduce<State, Action> { state, action in
|
||||||
switch action {
|
switch action {
|
||||||
|
|
||||||
|
case .binding(\.includesFilterDrop):
|
||||||
|
guard state.includesFilterDrop else {
|
||||||
|
state.sharedSettings.manufacturersIncludedFilterPressureDrop = nil
|
||||||
|
return .none
|
||||||
|
}
|
||||||
|
guard state.sharedSettings.manufacturersIncludedFilterPressureDrop != nil else {
|
||||||
|
return .none
|
||||||
|
}
|
||||||
|
state.sharedSettings.manufacturersIncludedFilterPressureDrop = 0.1
|
||||||
|
return .none
|
||||||
|
|
||||||
case .binding:
|
case .binding:
|
||||||
return .none
|
return .none
|
||||||
|
|
||||||
@@ -96,12 +100,8 @@ public struct EquipmentSettingsForm {
|
|||||||
state.destination = .infoView(.init(view: infoView))
|
state.destination = .infoView(.init(view: infoView))
|
||||||
return .none
|
return .none
|
||||||
|
|
||||||
case .nextButtonTapped:
|
|
||||||
#warning("Fix me.")
|
|
||||||
return .none
|
|
||||||
|
|
||||||
case .resetButtonTapped:
|
case .resetButtonTapped:
|
||||||
state.heatingCapacity = nil
|
state.sharedSettings.heatingCapacity = nil
|
||||||
return .none
|
return .none
|
||||||
|
|
||||||
case .submitField:
|
case .submitField:
|
||||||
@@ -138,7 +138,7 @@ public struct EquipmentSettingsFormView: View {
|
|||||||
.listRowBackground(Color.clear)
|
.listRowBackground(Color.clear)
|
||||||
|
|
||||||
Section {
|
Section {
|
||||||
FanTypePicker(selection: $store.fanType)
|
FanTypePicker(selection: $store.sharedSettings.fanType)
|
||||||
.pickerStyle(.segmented)
|
.pickerStyle(.segmented)
|
||||||
} header: {
|
} header: {
|
||||||
Text("Fan Type")
|
Text("Fan Type")
|
||||||
@@ -154,20 +154,16 @@ public struct EquipmentSettingsFormView: View {
|
|||||||
: "Capacity"
|
: "Capacity"
|
||||||
)
|
)
|
||||||
Spacer()
|
Spacer()
|
||||||
CoolingCapacityPicker(selection: $store.coolingCapacity)
|
CoolingCapacityPicker(
|
||||||
// Picker("Cooling Capcity", selection: $store.coolingCapacity) {
|
selection: $store.sharedSettings.coolingCapacity
|
||||||
// ForEach(CoolingCapacity.allCases) {
|
)
|
||||||
// Text($0.description)
|
|
||||||
// .tag($0)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
if store.equipmentType == .furnaceAndCoil {
|
if store.equipmentType == .furnaceAndCoil {
|
||||||
GridRow {
|
GridRow {
|
||||||
TextLabel("Heating")
|
TextLabel("Heating")
|
||||||
textField(
|
textField(
|
||||||
"Heating Capacity",
|
"Heating Capacity",
|
||||||
value: $store.heatingCapacity,
|
value: $store.sharedSettings.heatingCapacity,
|
||||||
fractionLength: 0
|
fractionLength: 0
|
||||||
)
|
)
|
||||||
.focused($focusedField, equals: .heatingCapacity)
|
.focused($focusedField, equals: .heatingCapacity)
|
||||||
@@ -190,7 +186,7 @@ public struct EquipmentSettingsFormView: View {
|
|||||||
TextLabel("Minimum")
|
TextLabel("Minimum")
|
||||||
textField(
|
textField(
|
||||||
"Minimum Pressure",
|
"Minimum Pressure",
|
||||||
value: $store.ratedStaticPressures.minimum,
|
value: $store.sharedSettings.ratedStaticPressures.minimum,
|
||||||
fractionLength: 2
|
fractionLength: 2
|
||||||
)
|
)
|
||||||
.focused($focusedField, equals: .minimumStaticPressure)
|
.focused($focusedField, equals: .minimumStaticPressure)
|
||||||
@@ -200,7 +196,7 @@ public struct EquipmentSettingsFormView: View {
|
|||||||
TextLabel("Maximum")
|
TextLabel("Maximum")
|
||||||
textField(
|
textField(
|
||||||
"Maximum Pressure",
|
"Maximum Pressure",
|
||||||
value: $store.ratedStaticPressures.maximum,
|
value: $store.sharedSettings.ratedStaticPressures.maximum,
|
||||||
fractionLength: 2
|
fractionLength: 2
|
||||||
)
|
)
|
||||||
.focused($focusedField, equals: .maximumStaticPressure)
|
.focused($focusedField, equals: .maximumStaticPressure)
|
||||||
@@ -210,7 +206,7 @@ public struct EquipmentSettingsFormView: View {
|
|||||||
TextLabel("Rated")
|
TextLabel("Rated")
|
||||||
textField(
|
textField(
|
||||||
"Rated Pressure",
|
"Rated Pressure",
|
||||||
value: $store.ratedStaticPressures.rated,
|
value: $store.sharedSettings.ratedStaticPressures.rated,
|
||||||
fractionLength: 2
|
fractionLength: 2
|
||||||
)
|
)
|
||||||
.focused($focusedField, equals: .ratedStaticPressure)
|
.focused($focusedField, equals: .ratedStaticPressure)
|
||||||
@@ -221,74 +217,36 @@ public struct EquipmentSettingsFormView: View {
|
|||||||
header("Rated Static Pressure", infoView: .ratedStaticPressures)
|
header("Rated Static Pressure", infoView: .ratedStaticPressures)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Section {
|
Section {
|
||||||
// Grid(alignment: .leading, horizontalSpacing: 40) {
|
VStack(alignment: .leading) {
|
||||||
// if store.equipmentMeasurement.equipmentType == .furnaceAndCoil {
|
HStack {
|
||||||
// GridRow {
|
TextLabel("Includes Filter Drop")
|
||||||
// TextLabel("Coil")
|
Spacer()
|
||||||
// percentField("Coil Budget", value: $store.budgets.coilBudget.fraction)
|
Toggle("Includes Filter Drop", isOn: $store.includesFilterDrop)
|
||||||
// .focused($focusedField, equals: .coilBudget)
|
}
|
||||||
// .onSubmit { send(.submitField) }
|
if store.includesFilterDrop {
|
||||||
// .numberPad()
|
HStack {
|
||||||
// }
|
TextLabel("Filter Drop")
|
||||||
// }
|
Spacer()
|
||||||
//
|
textField(
|
||||||
// GridRow {
|
"Filter Drop",
|
||||||
// TextLabel("Filter")
|
value: $store.sharedSettings.manufacturersIncludedFilterPressureDrop,
|
||||||
// percentField("Filter Budget", value: $store.budgets.filterBudget.fraction)
|
fractionLength: 2
|
||||||
// .focused($focusedField, equals: .filterBudget)
|
)
|
||||||
// .onSubmit { send(.submitField) }
|
.focused($focusedField, equals: .manufacturersIncludedFilterPressureDrop)
|
||||||
// .numberPad()
|
.decimalPad()
|
||||||
// }
|
.padding(.leading, 40)
|
||||||
//
|
}
|
||||||
// GridRow {
|
}
|
||||||
// TextLabel("Return")
|
}
|
||||||
// percentField("Return Plenum Budget", value: $store.budgets.returnPlenumBudget.fraction)
|
} header: {
|
||||||
// .focused($focusedField, equals: .returnPlenumBudget)
|
header(infoView: .manufacturersIncludedFilterPressureDrop) {
|
||||||
// .onSubmit { send(.submitField) }
|
VStack(alignment: .leading) {
|
||||||
// .numberPad()
|
Text("Manufacturer's")
|
||||||
// }
|
Text("Filter Pressure Drop")
|
||||||
//
|
}
|
||||||
// GridRow {
|
}
|
||||||
// TextLabel("Supply")
|
}
|
||||||
// percentField("Supply Plenum Budget", value: $store.budgets.supplyPlenumBudget.fraction)
|
|
||||||
// .focused($focusedField, equals: .supplyPlenumBudget)
|
|
||||||
// .onSubmit { send(.submitField) }
|
|
||||||
// .numberPad()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// } header: {
|
|
||||||
// VStack {
|
|
||||||
// header("Budgets", infoView: .budgets)
|
|
||||||
// #if os(macOS)
|
|
||||||
// .font(.title2)
|
|
||||||
// .padding(.top, 20)
|
|
||||||
// #endif
|
|
||||||
// FlaggedView(
|
|
||||||
// flagged: Flagged(wrappedValue: store.budgets.total.rawValue, .percent())
|
|
||||||
// )
|
|
||||||
// .flaggedViewStyle(BudgetFlagViewStyle())
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Section {
|
|
||||||
// TextField(
|
|
||||||
// "Airflow",
|
|
||||||
// value: $store.updatedAirflow,
|
|
||||||
// fractionLength: 0
|
|
||||||
// )
|
|
||||||
// } header: {
|
|
||||||
// header("Updated Airflow", infoView: .updatedAirflow)
|
|
||||||
// } footer: {
|
|
||||||
// HStack {
|
|
||||||
// ResetButton { send(.resetButtonTapped) }
|
|
||||||
// Spacer()
|
|
||||||
// NextButton { send(.nextButtonTapped) }
|
|
||||||
// .disabled(!store.isValid)
|
|
||||||
// }
|
|
||||||
// .padding(.top)
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
.labelsHidden()
|
.labelsHidden()
|
||||||
.bind($focusedField, to: $store.focusedField)
|
.bind($focusedField, to: $store.focusedField)
|
||||||
@@ -306,23 +264,23 @@ public struct EquipmentSettingsFormView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func header(
|
private func header<Label: View>(
|
||||||
_ title: String,
|
infoView: EquipmentSettingsForm.InfoView,
|
||||||
infoView: EquipmentSettingsForm.InfoView
|
label: @escaping () -> Label
|
||||||
) -> some View {
|
) -> some View {
|
||||||
HStack {
|
HStack {
|
||||||
Text(title)
|
label()
|
||||||
Spacer()
|
Spacer()
|
||||||
InfoButton { send(.infoButtonTapped(infoView)) }
|
InfoButton { send(.infoButtonTapped(infoView)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// private func percentField(
|
private func header(
|
||||||
// _ title: LocalizedStringKey,
|
_ title: String,
|
||||||
// value: Binding<Double>
|
infoView: EquipmentSettingsForm.InfoView
|
||||||
// ) -> some View {
|
) -> some View {
|
||||||
// TextField(title, value: value, format: .percent, prompt: Text(title))
|
header(infoView: infoView) { Text(title) }
|
||||||
// }
|
}
|
||||||
|
|
||||||
private func textField(
|
private func textField(
|
||||||
_ title: String,
|
_ title: String,
|
||||||
@@ -362,37 +320,29 @@ fileprivate extension InfoViewFeature.State {
|
|||||||
Record the cooling and heating capacities of the system.
|
Record the cooling and heating capacities of the system.
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
// case .budgets:
|
case .manufacturersIncludedFilterPressureDrop:
|
||||||
// self.init(
|
self.init(
|
||||||
// title: "Budgets",
|
title: "Filter Pressure Drop",
|
||||||
// body: """
|
body: """
|
||||||
// Budgeted percentages for static pressure estimations, these generally are set to
|
Record the filter pressure drop that the manufacturer includes in their blower performance table, if applicable.
|
||||||
// reasonable defaults, however you can change them if desired.
|
|
||||||
//
|
Sometimes this information is not listed, therefore it may be reasonable to use a sensible default value of '0.1'.
|
||||||
// Note: These must total up to 100%.
|
|
||||||
// """
|
Note: The value that is set get's deducted from the filter pressure drop when determining the external static pressure of a system.
|
||||||
// )
|
"""
|
||||||
|
)
|
||||||
case .ratedStaticPressures:
|
case .ratedStaticPressures:
|
||||||
self.init(
|
self.init(
|
||||||
title: "Rated Static",
|
title: "Rated Static",
|
||||||
body: """
|
body: """
|
||||||
Record the rated static pressures of the system.
|
Record the rated static pressures of the system.
|
||||||
|
|
||||||
These are generally found on the nameplate of the equipment or in the installation
|
These are generally found on the nameplate of the equipment or in the installation manual.
|
||||||
manual.
|
|
||||||
|
|
||||||
The defaults are generally acceptable for most unitary equipment.
|
The defaults are generally acceptable for most unitary equipment.
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
// case .updatedAirflow:
|
|
||||||
// self.init(
|
|
||||||
// title: "Updated Airflow",
|
|
||||||
// body: """
|
|
||||||
// This is used to generated estimated static pressures at the updated airflow
|
|
||||||
// compared to the existing airflow of the system.
|
|
||||||
// """
|
|
||||||
// )
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -401,9 +351,11 @@ fileprivate extension InfoViewFeature.State {
|
|||||||
NavigationStack {
|
NavigationStack {
|
||||||
EquipmentSettingsFormView(
|
EquipmentSettingsFormView(
|
||||||
store: Store(
|
store: Store(
|
||||||
initialState: EquipmentSettingsForm.State()
|
initialState: EquipmentSettingsForm.State(
|
||||||
|
sharedSettings: Shared(SharedPressureEstimationSettings())
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
EquipmentSettingsForm()
|
EquipmentSettingsForm()._printChanges()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
|
|||||||
@@ -20,12 +20,12 @@ public struct FlaggedMeasurementsList {
|
|||||||
public struct State: Equatable {
|
public struct State: Equatable {
|
||||||
|
|
||||||
@Presents public var destination: Destination.State?
|
@Presents public var destination: Destination.State?
|
||||||
@Shared var sharedSettings: SharedSettings
|
@Shared var sharedSettings: SharedPressureEstimationSettings
|
||||||
public var estimatedMeasurements: IdentifiedArrayOf<FlaggedMeasurementContainer>
|
public var estimatedMeasurements: IdentifiedArrayOf<FlaggedMeasurementContainer>
|
||||||
|
|
||||||
init(
|
init(
|
||||||
destination: Destination.State? = nil,
|
destination: Destination.State? = nil,
|
||||||
sharedSettings: Shared<SharedSettings>,
|
sharedSettings: Shared<SharedPressureEstimationSettings>,
|
||||||
estimatedMeasurements: IdentifiedArrayOf<FlaggedMeasurementContainer> = []
|
estimatedMeasurements: IdentifiedArrayOf<FlaggedMeasurementContainer> = []
|
||||||
) {
|
) {
|
||||||
self.destination = destination
|
self.destination = destination
|
||||||
@@ -33,6 +33,7 @@ public struct FlaggedMeasurementsList {
|
|||||||
self.estimatedMeasurements = estimatedMeasurements
|
self.estimatedMeasurements = estimatedMeasurements
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#warning("Move to shared settings.")
|
||||||
public struct FlaggedMeasurementContainer: Equatable, Identifiable {
|
public struct FlaggedMeasurementContainer: Equatable, Identifiable {
|
||||||
public let id: UUID
|
public let id: UUID
|
||||||
public var flaggedMeasurement: EquipmentMeasurement.FlaggedMeasurement
|
public var flaggedMeasurement: EquipmentMeasurement.FlaggedMeasurement
|
||||||
@@ -115,7 +116,7 @@ public struct FlaggedMeasurementsList {
|
|||||||
|
|
||||||
case .addButtonTapped:
|
case .addButtonTapped:
|
||||||
state.destination = .estimationForm(.init(
|
state.destination = .estimationForm(.init(
|
||||||
coolingCapacity: state.sharedSettings.coolingCapacity
|
coolingCapacity: state.sharedSettings.equipmentMetadata.coolingCapacity
|
||||||
))
|
))
|
||||||
return .none
|
return .none
|
||||||
|
|
||||||
@@ -145,16 +146,20 @@ public struct FlaggedMeasurementsList {
|
|||||||
return .none
|
return .none
|
||||||
|
|
||||||
case .onAppear:
|
case .onAppear:
|
||||||
guard let equipmentMeasurement = state.sharedSettings.equipmentMeasurement,
|
guard let equipmentMeasurement = state.sharedSettings.equipmentMeasurement else {
|
||||||
let budgets = state.sharedSettings.budgets
|
|
||||||
else {
|
|
||||||
return .none
|
return .none
|
||||||
}
|
}
|
||||||
|
if state.sharedSettings.budgets == nil {
|
||||||
|
state.sharedSettings.budgets = .init(
|
||||||
|
equipmentType: state.sharedSettings.equipmentMeasurement!.equipmentType,
|
||||||
|
fanType: state.sharedSettings.equipmentMetadata.fanType
|
||||||
|
)
|
||||||
|
}
|
||||||
state.sharedSettings.flaggedEquipmentMeasurement = .init(
|
state.sharedSettings.flaggedEquipmentMeasurement = .init(
|
||||||
budgets: budgets,
|
budgets: state.sharedSettings.budgets!,
|
||||||
measurement: equipmentMeasurement,
|
measurement: equipmentMeasurement,
|
||||||
ratedPressures: state.sharedSettings.ratedStaticPressures,
|
ratedPressures: state.sharedSettings.equipmentMetadata.ratedStaticPressures,
|
||||||
tons: state.sharedSettings.coolingCapacity
|
tons: state.sharedSettings.equipmentMetadata.coolingCapacity
|
||||||
)
|
)
|
||||||
return .none
|
return .none
|
||||||
}
|
}
|
||||||
@@ -178,8 +183,8 @@ public struct FlaggedMeasurementsList {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return .receive(action: \.receive) { [ratedStaticPressures = state.sharedSettings.ratedStaticPressures] in
|
return .receive(action: \.receive) { [ratedStaticPressures = state.sharedSettings.equipmentMetadata.ratedStaticPressures] in
|
||||||
|
|
||||||
let filterPressureDrop = form.filterPressureDrop != nil
|
let filterPressureDrop = form.filterPressureDrop != nil
|
||||||
? Positive(wrappedValue: form.filterPressureDrop!)
|
? Positive(wrappedValue: form.filterPressureDrop!)
|
||||||
: nil
|
: nil
|
||||||
@@ -221,8 +226,6 @@ public struct FlaggedMeasurementListView: View {
|
|||||||
} header: {
|
} header: {
|
||||||
HStack {
|
HStack {
|
||||||
Text("Existing Measurements")
|
Text("Existing Measurements")
|
||||||
Spacer()
|
|
||||||
// Button("Edit") { }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -278,7 +281,7 @@ public struct FlaggedMeasurementListView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
private let sharedSettings = SharedSettings(
|
private let sharedPressureEstimationSettings = SharedPressureEstimationSettings(
|
||||||
budgets: .init(equipmentType: .airHandler, fanType: .constantSpeed),
|
budgets: .init(equipmentType: .airHandler, fanType: .constantSpeed),
|
||||||
equipmentMeasurement: .mock(type: .airHandler),
|
equipmentMeasurement: .mock(type: .airHandler),
|
||||||
flaggedEquipmentMeasurement: nil
|
flaggedEquipmentMeasurement: nil
|
||||||
@@ -290,10 +293,10 @@ private let flaggedMeasurements = IdentifiedArrayOf<FlaggedMeasurementsList.Stat
|
|||||||
id: UUID(0),
|
id: UUID(0),
|
||||||
name: "Existing",
|
name: "Existing",
|
||||||
flaggedMeasurement: .init(
|
flaggedMeasurement: .init(
|
||||||
budgets: sharedSettings.budgets!,
|
budgets: sharedPressureEstimationSettings.budgets!,
|
||||||
measurement: sharedSettings.equipmentMeasurement!,
|
measurement: sharedPressureEstimationSettings.equipmentMeasurement!,
|
||||||
ratedPressures: sharedSettings.ratedStaticPressures,
|
ratedPressures: sharedPressureEstimationSettings.equipmentMetadata.ratedStaticPressures,
|
||||||
tons: sharedSettings.coolingCapacity
|
tons: sharedPressureEstimationSettings.equipmentMetadata.coolingCapacity
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
@@ -305,7 +308,7 @@ private let flaggedMeasurements = IdentifiedArrayOf<FlaggedMeasurementsList.Stat
|
|||||||
FlaggedMeasurementListView(
|
FlaggedMeasurementListView(
|
||||||
store: Store(
|
store: Store(
|
||||||
initialState: FlaggedMeasurementsList.State(
|
initialState: FlaggedMeasurementsList.State(
|
||||||
sharedSettings: Shared(sharedSettings)
|
sharedSettings: Shared(sharedPressureEstimationSettings)
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
FlaggedMeasurementsList()
|
FlaggedMeasurementsList()
|
||||||
@@ -319,7 +322,7 @@ private let flaggedMeasurements = IdentifiedArrayOf<FlaggedMeasurementsList.Stat
|
|||||||
FlaggedMeasurementListView(
|
FlaggedMeasurementListView(
|
||||||
store: Store(
|
store: Store(
|
||||||
initialState: FlaggedMeasurementsList.State(
|
initialState: FlaggedMeasurementsList.State(
|
||||||
sharedSettings: Shared(sharedSettings)
|
sharedSettings: Shared(sharedPressureEstimationSettings)
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
FlaggedMeasurementsList()
|
FlaggedMeasurementsList()
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import ComposableArchitecture
|
import ComposableArchitecture
|
||||||
|
import DependenciesAdditions
|
||||||
import SharedModels
|
import SharedModels
|
||||||
import Styleguide
|
import Styleguide
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import TCAExtras
|
import TCAExtras
|
||||||
|
|
||||||
|
#warning("Fix shared settings.")
|
||||||
@Reducer
|
@Reducer
|
||||||
public struct PressureEstimationsFeature {
|
public struct PressureEstimationsFeature {
|
||||||
|
|
||||||
@@ -17,17 +19,16 @@ public struct PressureEstimationsFeature {
|
|||||||
@ObservableState
|
@ObservableState
|
||||||
public struct State: Equatable {
|
public struct State: Equatable {
|
||||||
@Presents public var destination: Destination.State?
|
@Presents public var destination: Destination.State?
|
||||||
|
@Shared(.sharedPressureEstimationSettings) var sharedSettings = SharedPressureEstimationSettings()
|
||||||
public var equipmentSettings: EquipmentSettingsForm.State
|
public var equipmentSettings: EquipmentSettingsForm.State
|
||||||
public var equipmentMeasurements: EquipmentMeasurementForm.State?
|
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
destination: Destination.State? = nil,
|
destination: Destination.State? = nil,
|
||||||
equipmentSettings: EquipmentSettingsForm.State = .init(),
|
sharedSettings: SharedPressureEstimationSettings = .init()
|
||||||
equipmentMeasurements: EquipmentMeasurementForm.State? = nil
|
|
||||||
) {
|
) {
|
||||||
self.destination = destination
|
self.destination = destination
|
||||||
self.equipmentSettings = equipmentSettings
|
self._sharedSettings = Shared(sharedSettings)
|
||||||
self.equipmentMeasurements = equipmentMeasurements
|
self._equipmentSettings = .init(sharedSettings: self._sharedSettings)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,7 +42,9 @@ public struct PressureEstimationsFeature {
|
|||||||
case nextButtonTapped
|
case nextButtonTapped
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Dependency(\.logger["\(Self.self)"]) var logger
|
||||||
|
|
||||||
public var body: some Reducer<State, Action> {
|
public var body: some Reducer<State, Action> {
|
||||||
Scope(state: \.equipmentSettings, action: \.equipmentSettings) {
|
Scope(state: \.equipmentSettings, action: \.equipmentSettings) {
|
||||||
EquipmentSettingsForm()
|
EquipmentSettingsForm()
|
||||||
@@ -57,13 +60,42 @@ public struct PressureEstimationsFeature {
|
|||||||
case let .view(action):
|
case let .view(action):
|
||||||
switch action {
|
switch action {
|
||||||
case .nextButtonTapped:
|
case .nextButtonTapped:
|
||||||
return .none
|
return handleNextButtonTapped(&state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.ifLet(\.$destination, action: \.destination)
|
.ifLet(\.$destination, action: \.destination)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func handleNextButtonTapped(_ state: inout State) -> Effect<Action> {
|
||||||
|
guard state.destination == nil else {
|
||||||
|
return .fail(
|
||||||
|
"""
|
||||||
|
Received next button tapped action on equipment settings form, but the destination is not nil.
|
||||||
|
|
||||||
|
This is considered an application logic error.
|
||||||
|
""",
|
||||||
|
logger: logger
|
||||||
|
)
|
||||||
|
}
|
||||||
|
guard state.equipmentSettings.isValid else {
|
||||||
|
return .fail(
|
||||||
|
"""
|
||||||
|
Received next button tapped action on equipment settings form, but the form is invalid.
|
||||||
|
|
||||||
|
This is considered an application logic error.
|
||||||
|
""",
|
||||||
|
logger: logger
|
||||||
|
)
|
||||||
|
}
|
||||||
|
state.destination = .equipmentMeasurements(.init(
|
||||||
|
allowEquipmentTypeSelection: false,
|
||||||
|
sharedSettings: state.$sharedSettings,
|
||||||
|
equipmentType: state.equipmentSettings.equipmentType
|
||||||
|
))
|
||||||
|
return .none
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewAction(for: PressureEstimationsFeature.self)
|
@ViewAction(for: PressureEstimationsFeature.self)
|
||||||
@@ -79,5 +111,30 @@ public struct PressureEstimationsView: View {
|
|||||||
EquipmentSettingsFormView(
|
EquipmentSettingsFormView(
|
||||||
store: store.scope(state: \.equipmentSettings, action: \.equipmentSettings)
|
store: store.scope(state: \.equipmentSettings, action: \.equipmentSettings)
|
||||||
)
|
)
|
||||||
|
.navigationTitle("Equipment Settings")
|
||||||
|
.toolbar {
|
||||||
|
NextButton { send(.nextButtonTapped) }
|
||||||
|
.nextButtonStyle(.toolbar)
|
||||||
|
.disabled(!store.equipmentSettings.isValid)
|
||||||
|
}
|
||||||
|
.navigationDestination(
|
||||||
|
item: $store.scope(
|
||||||
|
state: \.destination?.equipmentMeasurements,
|
||||||
|
action: \.destination.equipmentMeasurements
|
||||||
|
)
|
||||||
|
) { measurementStore in
|
||||||
|
EquipmentMeasurementFormView(store: measurementStore)
|
||||||
|
.navigationTitle("Existing Measurements")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
NavigationStack {
|
||||||
|
PressureEstimationsView(
|
||||||
|
store: Store(initialState: PressureEstimationsFeature.State()) {
|
||||||
|
PressureEstimationsFeature()._printChanges()
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import ComposableArchitecture
|
||||||
|
import SharedModels
|
||||||
|
|
||||||
|
/// Holds onto shared values for several of the views in this feature.
|
||||||
|
@dynamicMemberLookup
|
||||||
|
public struct SharedPressureEstimationSettings: Equatable {
|
||||||
|
public var budgets: BudgetedPercentEnvelope?
|
||||||
|
public var equipmentMeasurement: EquipmentMeasurement?
|
||||||
|
public var equipmentMetadata: EquipmentMetadata
|
||||||
|
public var flaggedEquipmentMeasurement: EquipmentMeasurement.FlaggedMeasurement?
|
||||||
|
public var heatingCapacity: Double?
|
||||||
|
public var manufacturersIncludedFilterPressureDrop: Double?
|
||||||
|
|
||||||
|
public init(
|
||||||
|
budgets: BudgetedPercentEnvelope? = nil,
|
||||||
|
equipmentMeasurement: EquipmentMeasurement? = nil,
|
||||||
|
equipmentMetadata: EquipmentMetadata = .init(),
|
||||||
|
flaggedEquipmentMeasurement: EquipmentMeasurement.FlaggedMeasurement? = nil,
|
||||||
|
heatingCapacity: Double? = nil,
|
||||||
|
manufacturersIncludedFilterPressureDrop: Double? = nil
|
||||||
|
) {
|
||||||
|
self.budgets = budgets
|
||||||
|
self.equipmentMeasurement = equipmentMeasurement
|
||||||
|
self.equipmentMetadata = equipmentMetadata
|
||||||
|
self.flaggedEquipmentMeasurement = flaggedEquipmentMeasurement
|
||||||
|
self.heatingCapacity = heatingCapacity
|
||||||
|
self.manufacturersIncludedFilterPressureDrop = manufacturersIncludedFilterPressureDrop
|
||||||
|
}
|
||||||
|
|
||||||
|
public subscript<T>(dynamicMember keyPath: WritableKeyPath<EquipmentMetadata, T>) -> T {
|
||||||
|
get { equipmentMetadata[keyPath: keyPath] }
|
||||||
|
set { equipmentMetadata[keyPath: keyPath] = newValue }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension PersistenceReaderKey where Self == InMemoryKey<SharedPressureEstimationSettings> {
|
||||||
|
static var sharedPressureEstimationSettings: Self {
|
||||||
|
.inMemory("sharedPressureEstimationSettings")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
import ComposableArchitecture
|
|
||||||
import SharedModels
|
|
||||||
|
|
||||||
|
|
||||||
struct SharedSettings: Equatable {
|
|
||||||
var budgets: BudgetedPercentEnvelope?
|
|
||||||
var coolingCapacity: EquipmentMetadata.CoolingCapacity
|
|
||||||
var equipmentMeasurement: EquipmentMeasurement?
|
|
||||||
var fanType: EquipmentMetadata.FanType
|
|
||||||
var flaggedEquipmentMeasurement: EquipmentMeasurement.FlaggedMeasurement?
|
|
||||||
var heatingCapacity: Double?
|
|
||||||
var ratedStaticPressures: RatedStaticPressures
|
|
||||||
|
|
||||||
init(
|
|
||||||
budgets: BudgetedPercentEnvelope? = nil,
|
|
||||||
coolingCapacity: EquipmentMetadata.CoolingCapacity = .default,
|
|
||||||
equipmentMeasurement: EquipmentMeasurement? = nil,
|
|
||||||
fanType: EquipmentMetadata.FanType = .constantSpeed,
|
|
||||||
flaggedEquipmentMeasurement: EquipmentMeasurement.FlaggedMeasurement? = nil,
|
|
||||||
heatingCapacity: Double? = nil,
|
|
||||||
ratedStaticPressures: RatedStaticPressures = .init()
|
|
||||||
) {
|
|
||||||
self.budgets = budgets
|
|
||||||
self.coolingCapacity = coolingCapacity
|
|
||||||
self.equipmentMeasurement = equipmentMeasurement
|
|
||||||
self.fanType = fanType
|
|
||||||
self.flaggedEquipmentMeasurement = flaggedEquipmentMeasurement
|
|
||||||
self.heatingCapacity = heatingCapacity
|
|
||||||
self.ratedStaticPressures = ratedStaticPressures
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension PersistenceReaderKey where Self == InMemoryKey<SharedSettings> {
|
|
||||||
static var sharedSettings: Self {
|
|
||||||
.inMemory("sharedSettings")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
import Foundation
|
|
||||||
|
|
||||||
@@ -1,8 +1,5 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
#warning("Make values non-optional")
|
|
||||||
#warning("Need to make air handler external static handle large filter pressure drops.")
|
|
||||||
#warning("Add an exterenal static pressure strategy for if the filter is built-in or external")
|
|
||||||
public enum EquipmentMeasurement: Equatable {
|
public enum EquipmentMeasurement: Equatable {
|
||||||
|
|
||||||
case airHandler(AirHandler)
|
case airHandler(AirHandler)
|
||||||
@@ -17,7 +14,7 @@ public enum EquipmentMeasurement: Equatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var externalStaticPressure: Double {
|
public var externalStaticPressure: Positive<Double> {
|
||||||
switch self {
|
switch self {
|
||||||
case let .airHandler(airHandler):
|
case let .airHandler(airHandler):
|
||||||
return airHandler.externalStaticPressure
|
return airHandler.externalStaticPressure
|
||||||
@@ -26,47 +23,75 @@ public enum EquipmentMeasurement: Equatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public var manufacturersIncludedFilterPressureDrop: Positive<Double> {
|
||||||
|
switch self {
|
||||||
|
case let .airHandler(airHandler):
|
||||||
|
return airHandler.$manufacturersIncludedFilterPressureDrop
|
||||||
|
case let .furnaceAndCoil(furnaceAndCoil):
|
||||||
|
return furnaceAndCoil.$manufacturersIncludedFilterPressureDrop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public struct AirHandler: Equatable {
|
public struct AirHandler: Equatable {
|
||||||
|
|
||||||
@Positive
|
@Positive
|
||||||
public var airflow: Double?
|
public var airflow: Double
|
||||||
|
|
||||||
@Positive
|
@Positive
|
||||||
public var returnPlenumPressure: Double?
|
public var manufacturersIncludedFilterPressureDrop: Double
|
||||||
|
|
||||||
|
@Positive
|
||||||
|
public var returnPlenumPressure: Double
|
||||||
|
|
||||||
@Positive
|
@Positive
|
||||||
public var postFilterPressure: Double?
|
public var postFilterPressure: Double
|
||||||
|
|
||||||
@Positive
|
@Positive
|
||||||
public var postCoilPressure: Double?
|
public var postCoilPressure: Double
|
||||||
|
|
||||||
@Positive
|
@Positive
|
||||||
public var supplyPlenumPressure: Double?
|
public var supplyPlenumPressure: Double
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
airflow: Double? = nil,
|
airflow: Double,
|
||||||
returnPlenumPressure: Double? = nil,
|
manufacturersIncludedFilterPressureDrop: Double,
|
||||||
postFilterPressure: Double? = nil,
|
returnPlenumPressure: Double,
|
||||||
postCoilPressure: Double? = nil,
|
postFilterPressure: Double,
|
||||||
supplyPlenumPressure: Double? = nil
|
postCoilPressure: Double,
|
||||||
|
supplyPlenumPressure: Double
|
||||||
) {
|
) {
|
||||||
self.airflow = airflow
|
self.airflow = airflow
|
||||||
|
self.manufacturersIncludedFilterPressureDrop = manufacturersIncludedFilterPressureDrop
|
||||||
self.returnPlenumPressure = returnPlenumPressure
|
self.returnPlenumPressure = returnPlenumPressure
|
||||||
self.postFilterPressure = postFilterPressure
|
self.postFilterPressure = postFilterPressure
|
||||||
self.postCoilPressure = postCoilPressure
|
self.postCoilPressure = postCoilPressure
|
||||||
self.supplyPlenumPressure = supplyPlenumPressure
|
self.supplyPlenumPressure = supplyPlenumPressure
|
||||||
}
|
}
|
||||||
|
|
||||||
public var externalStaticPressure: Double {
|
public init(
|
||||||
var postFilterAdder = 0.0
|
airflow: Positive<Double>,
|
||||||
if let postFilterPressure = $postFilterPressure.positiveValue,
|
manufacturersIncludedFilterPressureDrop: Positive<Double>,
|
||||||
postFilterPressure > 0.1
|
returnPlenumPressure: Positive<Double>,
|
||||||
{
|
postFilterPressure: Positive<Double>,
|
||||||
postFilterAdder = postFilterPressure - 0.1
|
postCoilPressure: Positive<Double>,
|
||||||
|
supplyPlenumPressure: Positive<Double>
|
||||||
|
) {
|
||||||
|
self._airflow = airflow
|
||||||
|
self._manufacturersIncludedFilterPressureDrop = manufacturersIncludedFilterPressureDrop
|
||||||
|
self._returnPlenumPressure = returnPlenumPressure
|
||||||
|
self._postFilterPressure = postFilterPressure
|
||||||
|
self._postCoilPressure = postCoilPressure
|
||||||
|
self._supplyPlenumPressure = supplyPlenumPressure
|
||||||
|
}
|
||||||
|
|
||||||
|
public var externalStaticPressure: Positive<Double> {
|
||||||
|
var postFilterAdder = Positive<Double>.zero
|
||||||
|
if $postFilterPressure > $manufacturersIncludedFilterPressureDrop {
|
||||||
|
postFilterAdder = $postFilterPressure - $manufacturersIncludedFilterPressureDrop
|
||||||
}
|
}
|
||||||
return ($returnPlenumPressure.positiveValue ?? 0)
|
return $returnPlenumPressure
|
||||||
+ postFilterAdder
|
+ postFilterAdder
|
||||||
+ ($supplyPlenumPressure.positiveValue ?? 0)
|
+ $supplyPlenumPressure
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,7 +170,7 @@ public enum EquipmentMeasurement: Equatable {
|
|||||||
.init(
|
.init(
|
||||||
airflow: checkAirflow(value: measurement.airflow, tons: tons),
|
airflow: checkAirflow(value: measurement.airflow, tons: tons),
|
||||||
coilPressureDrop: .init(
|
coilPressureDrop: .init(
|
||||||
wrappedValue: (measurement.$postCoilPressure.positiveValue - measurement.$postFilterPressure.positiveValue) ?? 0,
|
wrappedValue: (measurement.$postCoilPressure.positiveValue - measurement.$postFilterPressure.positiveValue),
|
||||||
.result(.good())
|
.result(.good())
|
||||||
),
|
),
|
||||||
externalStaticPressure: checkExternalStaticPressure(
|
externalStaticPressure: checkExternalStaticPressure(
|
||||||
@@ -155,7 +180,8 @@ public enum EquipmentMeasurement: Equatable {
|
|||||||
filterPressureDrop: .init(
|
filterPressureDrop: .init(
|
||||||
value: measurement.$postFilterPressure.positiveValue - measurement.$returnPlenumPressure.positiveValue,
|
value: measurement.$postFilterPressure.positiveValue - measurement.$returnPlenumPressure.positiveValue,
|
||||||
budget: budgets.filterBudget,
|
budget: budgets.filterBudget,
|
||||||
ratedPressures: ratedPressures
|
ratedPressures: ratedPressures,
|
||||||
|
ignoreMinimum: true
|
||||||
),
|
),
|
||||||
returnPlenumPressure: .init(
|
returnPlenumPressure: .init(
|
||||||
value: measurement.$returnPlenumPressure.positiveValue,
|
value: measurement.$returnPlenumPressure.positiveValue,
|
||||||
@@ -163,7 +189,7 @@ public enum EquipmentMeasurement: Equatable {
|
|||||||
ratedPressures: ratedPressures
|
ratedPressures: ratedPressures
|
||||||
),
|
),
|
||||||
supplyPlenumPressure: .init(
|
supplyPlenumPressure: .init(
|
||||||
value: measurement.$supplyPlenumPressure.positiveValue ?? 0,
|
value: measurement.$supplyPlenumPressure.positiveValue,
|
||||||
budget: budgets.supplyPlenumBudget,
|
budget: budgets.supplyPlenumBudget,
|
||||||
ratedPressures: ratedPressures
|
ratedPressures: ratedPressures
|
||||||
)
|
)
|
||||||
@@ -211,61 +237,61 @@ public enum EquipmentMeasurement: Equatable {
|
|||||||
public struct FurnaceAndCoil: Equatable {
|
public struct FurnaceAndCoil: Equatable {
|
||||||
|
|
||||||
@Positive
|
@Positive
|
||||||
public var airflow: Double?
|
public var airflow: Double
|
||||||
|
|
||||||
@Positive
|
@Positive
|
||||||
public var returnPlenumPressure: Double?
|
public var manufacturersIncludedFilterPressureDrop: Double
|
||||||
|
|
||||||
|
@Positive
|
||||||
|
public var returnPlenumPressure: Double
|
||||||
|
|
||||||
@Positive
|
@Positive
|
||||||
public var postFilterPressure: Double?
|
public var postFilterPressure: Double
|
||||||
|
|
||||||
@Positive
|
@Positive
|
||||||
public var preCoilPressure: Double?
|
public var preCoilPressure: Double
|
||||||
|
|
||||||
@Positive
|
@Positive
|
||||||
public var supplyPlenumPressure: Double?
|
public var supplyPlenumPressure: Double
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
airflow: Double? = nil,
|
airflow: Double,
|
||||||
returnPlenumPressure: Double? = nil,
|
manufacturersIncludedFilterPressureDrop: Double,
|
||||||
postFilterPressure: Double? = nil,
|
returnPlenumPressure: Double,
|
||||||
preCoilPressure: Double? = nil,
|
postFilterPressure: Double,
|
||||||
supplyPlenumPressure: Double? = nil
|
preCoilPressure: Double,
|
||||||
|
supplyPlenumPressure: Double
|
||||||
) {
|
) {
|
||||||
self.airflow = airflow
|
self.airflow = airflow
|
||||||
|
self.manufacturersIncludedFilterPressureDrop = manufacturersIncludedFilterPressureDrop
|
||||||
self.returnPlenumPressure = returnPlenumPressure
|
self.returnPlenumPressure = returnPlenumPressure
|
||||||
self.postFilterPressure = postFilterPressure
|
self.postFilterPressure = postFilterPressure
|
||||||
self.preCoilPressure = preCoilPressure
|
self.preCoilPressure = preCoilPressure
|
||||||
self.supplyPlenumPressure = supplyPlenumPressure
|
self.supplyPlenumPressure = supplyPlenumPressure
|
||||||
}
|
}
|
||||||
|
|
||||||
public var externalStaticPressure: Double {
|
public init(
|
||||||
($postFilterPressure.positiveValue ?? 0) + ($preCoilPressure.positiveValue ?? 0)
|
airflow: Positive<Double>,
|
||||||
|
manufacturersIncludedFilterPressureDrop: Positive<Double>,
|
||||||
|
returnPlenumPressure: Positive<Double>,
|
||||||
|
postFilterPressure: Positive<Double>,
|
||||||
|
preCoilPressure: Positive<Double>,
|
||||||
|
supplyPlenumPressure: Positive<Double>
|
||||||
|
) {
|
||||||
|
self._airflow = airflow
|
||||||
|
self._manufacturersIncludedFilterPressureDrop = manufacturersIncludedFilterPressureDrop
|
||||||
|
self._returnPlenumPressure = returnPlenumPressure
|
||||||
|
self._postFilterPressure = postFilterPressure
|
||||||
|
self._preCoilPressure = preCoilPressure
|
||||||
|
self._supplyPlenumPressure = supplyPlenumPressure
|
||||||
|
}
|
||||||
|
|
||||||
|
public var externalStaticPressure: Positive<Double> {
|
||||||
|
($postFilterPressure - $manufacturersIncludedFilterPressureDrop) + $preCoilPressure
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//extension EquipmentMeasurement.AirHandler {
|
|
||||||
//
|
|
||||||
// public enum Key: String, Equatable, CaseIterable {
|
|
||||||
// case returnPlenumPressure
|
|
||||||
// case postFilterPressure
|
|
||||||
// case postCoilPressure
|
|
||||||
// case supplyPlenumPressure
|
|
||||||
// case airflow
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//extension EquipmentMeasurement.FurnaceAndCoil {
|
|
||||||
//
|
|
||||||
// public enum Key: String, Equatable, CaseIterable {
|
|
||||||
// case returnPlenumPressure
|
|
||||||
// case postFilterPressure
|
|
||||||
// case preCoilPressure
|
|
||||||
// case supplyPlenumPressure
|
|
||||||
// case airflow
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
fileprivate extension Flagged {
|
fileprivate extension Flagged {
|
||||||
|
|
||||||
init(
|
init(
|
||||||
@@ -303,11 +329,11 @@ fileprivate extension Flagged {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func checkExternalStaticPressure(
|
fileprivate func checkExternalStaticPressure(
|
||||||
value: Double,
|
value: Positive<Double>,
|
||||||
ratedPressures: RatedStaticPressures
|
ratedPressures: RatedStaticPressures
|
||||||
) -> Flagged {
|
) -> Flagged {
|
||||||
.init(
|
.init(
|
||||||
wrappedValue: value,
|
wrappedValue: value.positiveValue,
|
||||||
.rated(ratedPressures)
|
.rated(ratedPressures)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -330,6 +356,7 @@ extension EquipmentMeasurement {
|
|||||||
case .airHandler:
|
case .airHandler:
|
||||||
return .airHandler(.init(
|
return .airHandler(.init(
|
||||||
airflow: 1200,
|
airflow: 1200,
|
||||||
|
manufacturersIncludedFilterPressureDrop: 0.1,
|
||||||
returnPlenumPressure: 0.3,
|
returnPlenumPressure: 0.3,
|
||||||
postFilterPressure: 0.6,
|
postFilterPressure: 0.6,
|
||||||
postCoilPressure: 0.9,
|
postCoilPressure: 0.9,
|
||||||
@@ -339,6 +366,7 @@ extension EquipmentMeasurement {
|
|||||||
case .furnaceAndCoil:
|
case .furnaceAndCoil:
|
||||||
return .furnaceAndCoil(.init(
|
return .furnaceAndCoil(.init(
|
||||||
airflow: 1200,
|
airflow: 1200,
|
||||||
|
manufacturersIncludedFilterPressureDrop: 0.0,
|
||||||
returnPlenumPressure: 0.3,
|
returnPlenumPressure: 0.3,
|
||||||
postFilterPressure: 0.6,
|
postFilterPressure: 0.6,
|
||||||
preCoilPressure: 0.4,
|
preCoilPressure: 0.4,
|
||||||
|
|||||||
@@ -3,16 +3,16 @@ import Foundation
|
|||||||
public struct EquipmentMetadata: Equatable {
|
public struct EquipmentMetadata: Equatable {
|
||||||
public var coolingCapacity: CoolingCapacity
|
public var coolingCapacity: CoolingCapacity
|
||||||
public var fanType: FanType
|
public var fanType: FanType
|
||||||
public var ratedStaticPressure: RatedStaticPressures
|
public var ratedStaticPressures: RatedStaticPressures
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
coolingCapacity: CoolingCapacity = .three,
|
coolingCapacity: CoolingCapacity = .three,
|
||||||
fanType: FanType = .constantSpeed,
|
fanType: FanType = .constantSpeed,
|
||||||
ratedStaticPressure: RatedStaticPressures = .init()
|
ratedStaticPressures: RatedStaticPressures = .init()
|
||||||
) {
|
) {
|
||||||
self.coolingCapacity = coolingCapacity
|
self.coolingCapacity = coolingCapacity
|
||||||
self.fanType = fanType
|
self.fanType = fanType
|
||||||
self.ratedStaticPressure = ratedStaticPressure
|
self.ratedStaticPressures = ratedStaticPressures
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum CoolingCapacity: Double, Equatable, CaseIterable, Identifiable, CustomStringConvertible {
|
public enum CoolingCapacity: Double, Equatable, CaseIterable, Identifiable, CustomStringConvertible {
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
/// Represents a number that can be checked if it is within an acceptable range. It can generate errors or warnings depending
|
||||||
|
/// on the current value.
|
||||||
|
///
|
||||||
@dynamicMemberLookup
|
@dynamicMemberLookup
|
||||||
public struct Flagged: Equatable {
|
public struct Flagged: Equatable {
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,10 @@ public struct Positive<Value> where Value: Numeric, Value: Comparable {
|
|||||||
|
|
||||||
public var wrappedValue: Value
|
public var wrappedValue: Value
|
||||||
|
|
||||||
|
public init(_ value: Value) {
|
||||||
|
self.wrappedValue = value
|
||||||
|
}
|
||||||
|
|
||||||
public init(wrappedValue: Value) {
|
public init(wrappedValue: Value) {
|
||||||
self.wrappedValue = wrappedValue
|
self.wrappedValue = wrappedValue
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,9 +10,13 @@ extension View {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#warning("Fix me.")
|
||||||
|
// The decimal pad autocompletes too quickly in the simulator, needs tested on an actual
|
||||||
|
// device.
|
||||||
public func decimalPad() -> some View {
|
public func decimalPad() -> some View {
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
self.keyboardType(.decimalPad)
|
// self.keyboardType(.decimalPad)
|
||||||
|
self.keyboardType(.numberPad)
|
||||||
#else
|
#else
|
||||||
self
|
self
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -88,6 +88,22 @@ extension DefaultNextButtonStyle where ButtonStyle == BorderedProminentButtonSty
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct ToolbarNextButtonStyle: PrimitiveButtonStyle {
|
||||||
|
|
||||||
|
public func makeBody(configuration: Configuration) -> some View {
|
||||||
|
Button(role: configuration.role, action: configuration.trigger) {
|
||||||
|
configuration.label
|
||||||
|
.foregroundStyle(Color.accentColor)
|
||||||
|
}
|
||||||
|
.labelStyle(ReverseLabelStyle())
|
||||||
|
.buttonStyle(.plain)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension AnyPrimitiveButtonStyle<NextButtonType> {
|
||||||
|
public static var toolbar: Self { .init(ToolbarNextButtonStyle()) }
|
||||||
|
}
|
||||||
|
|
||||||
extension AnyButtonStyle where ButtonType == InfoButtonType {
|
extension AnyButtonStyle where ButtonType == InfoButtonType {
|
||||||
public static var `default`: Self {
|
public static var `default`: Self {
|
||||||
.init(DefaultInfoButtonStyle<IconOnlyLabelStyle>(labelStyle: .iconOnly))
|
.init(DefaultInfoButtonStyle<IconOnlyLabelStyle>(labelStyle: .iconOnly))
|
||||||
|
|||||||
Reference in New Issue
Block a user