Feat: Working on pressure estimations feature
This commit is contained in:
@@ -0,0 +1,242 @@
|
||||
import ComposableArchitecture
|
||||
import DependenciesAdditions
|
||||
import EstimatedPressureDependency
|
||||
import SharedModels
|
||||
import Styleguide
|
||||
import SwiftUI
|
||||
import TCAExtras
|
||||
|
||||
@Reducer
|
||||
public struct AirHandlerMeasurementForm {
|
||||
|
||||
@ObservableState
|
||||
public struct State: Equatable {
|
||||
public var calculatedMeasurement: EquipmentMeasurement.AirHandler?
|
||||
public var focusedField: Field?
|
||||
public var measurement: EquipmentMeasurement.AirHandler
|
||||
public var updatedAirflow: Double?
|
||||
public var fanType: FanType
|
||||
public var ratedStaticPressures = RatedStaticPressures()
|
||||
|
||||
public init(
|
||||
calculatedMeasurement: EquipmentMeasurement.AirHandler? = nil,
|
||||
focusedField: Field? = nil,
|
||||
measurement: EquipmentMeasurement.AirHandler = .init(),
|
||||
updatedAirflow: Double? = nil,
|
||||
fanType: FanType = .constantSpeed
|
||||
) {
|
||||
self.calculatedMeasurement = calculatedMeasurement
|
||||
self.focusedField = focusedField
|
||||
self.measurement = measurement
|
||||
self.updatedAirflow = updatedAirflow
|
||||
self.fanType = fanType
|
||||
}
|
||||
|
||||
public var isValid: Bool {
|
||||
return measurement.returnPlenumPressure != nil
|
||||
&& measurement.postFilterPressure != nil
|
||||
&& measurement.postCoilPressure != nil
|
||||
&& measurement.supplyPlenumPressure != nil
|
||||
&& measurement.airflow != nil
|
||||
&& updatedAirflow != nil
|
||||
}
|
||||
|
||||
public enum Field: String, Equatable, CaseIterable, FocusableField {
|
||||
case returnPlenumPressure
|
||||
case postFilterPressure
|
||||
case postCoilPressure
|
||||
case supplyPlenumPressure
|
||||
case airflow
|
||||
case updatedAirflow
|
||||
}
|
||||
}
|
||||
|
||||
public enum Action: BindableAction, ViewAction {
|
||||
case binding(BindingAction<State>)
|
||||
case receive(TaskResult<EquipmentMeasurement.AirHandler?>)
|
||||
case view(View)
|
||||
|
||||
@CasePathable
|
||||
public enum View {
|
||||
case infoButtonTapped
|
||||
case resetButtonTapped
|
||||
case submitField
|
||||
}
|
||||
}
|
||||
|
||||
@Dependency(\.estimatedPressuresClient) var estimatedPressuresClient
|
||||
@Dependency(\.logger["\(Self.self)"]) var logger
|
||||
|
||||
public var body: some Reducer<State, Action> {
|
||||
BindingReducer()
|
||||
Reduce<State, Action> { state, action in
|
||||
switch action {
|
||||
case .binding:
|
||||
return .none
|
||||
|
||||
case let .receive(.success(calculatedMeasurement)):
|
||||
state.calculatedMeasurement = calculatedMeasurement
|
||||
return .none
|
||||
|
||||
case .receive:
|
||||
return .none
|
||||
|
||||
case let .view(action):
|
||||
switch action {
|
||||
|
||||
case .infoButtonTapped:
|
||||
#warning("Fix me.")
|
||||
return .none
|
||||
|
||||
case .resetButtonTapped:
|
||||
state.measurement = .init()
|
||||
state.updatedAirflow = nil
|
||||
return .none
|
||||
|
||||
case .submitField:
|
||||
state.focusedField = state.focusedField?.next
|
||||
guard state.isValid else { return .none }
|
||||
return calculateEstimates(state: state)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
.onFailure(case: \.receive, .log(logger: logger))
|
||||
}
|
||||
|
||||
private func calculateEstimates(state: State) -> Effect<Action> {
|
||||
.receive(action: \.receive) {
|
||||
let result = try await estimatedPressuresClient.estimatedPressure(
|
||||
for: .airHandler(state.measurement),
|
||||
at: state.updatedAirflow ?? 0
|
||||
)
|
||||
|
||||
guard case let .airHandler(airHandler) = result else {
|
||||
return .none
|
||||
}
|
||||
return airHandler
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewAction(for: AirHandlerMeasurementForm.self)
|
||||
public struct AirHandlerMeasurementFormView: View {
|
||||
@FocusState private var focusedField: AirHandlerMeasurementForm.State.Field?
|
||||
@Bindable public var store: StoreOf<AirHandlerMeasurementForm>
|
||||
|
||||
public init(
|
||||
store: StoreOf<AirHandlerMeasurementForm>
|
||||
) {
|
||||
self.store = store
|
||||
self.focusedField = store.focusedField
|
||||
}
|
||||
|
||||
|
||||
public var body: some View {
|
||||
Form {
|
||||
Section {
|
||||
textField(
|
||||
"Return Plenum Pressure",
|
||||
value: $store.measurement.returnPlenumPressure
|
||||
)
|
||||
.focused($focusedField, equals: .returnPlenumPressure)
|
||||
.onSubmit { send(.submitField) }
|
||||
.decimalPad()
|
||||
|
||||
textField(
|
||||
"Post-Filter Pressure",
|
||||
value: $store.measurement.postFilterPressure
|
||||
)
|
||||
.focused($focusedField, equals: .postFilterPressure)
|
||||
.onSubmit { send(.submitField) }
|
||||
.decimalPad()
|
||||
|
||||
textField(
|
||||
"Post-Coil Pressure",
|
||||
value: $store.measurement.postCoilPressure
|
||||
)
|
||||
.focused($focusedField, equals: .postCoilPressure)
|
||||
.onSubmit { send(.submitField) }
|
||||
.decimalPad()
|
||||
|
||||
textField(
|
||||
"Supply Plenum Pressure",
|
||||
value: $store.measurement.supplyPlenumPressure
|
||||
)
|
||||
.focused($focusedField, equals: .supplyPlenumPressure)
|
||||
.onSubmit { send(.submitField) }
|
||||
.decimalPad()
|
||||
|
||||
textField(
|
||||
"Airflow",
|
||||
value: $store.measurement.airflow,
|
||||
fractionLength: 0
|
||||
)
|
||||
.focused($focusedField, equals: .airflow)
|
||||
.onSubmit { send(.submitField) }
|
||||
.numberPad()
|
||||
|
||||
} header: {
|
||||
HStack {
|
||||
Text("System Measurements")
|
||||
Spacer()
|
||||
InfoButton {
|
||||
send(.infoButtonTapped)
|
||||
}
|
||||
.font(.title3)
|
||||
.labelStyle(.iconOnly)
|
||||
.buttonStyle(.plain)
|
||||
.foregroundStyle(Color.accentColor)
|
||||
}
|
||||
}
|
||||
|
||||
Section {
|
||||
FanTypePicker(selection: $store.fanType)
|
||||
.pickerStyle(.segmented)
|
||||
|
||||
textField(
|
||||
"Updated Airflow",
|
||||
value: $store.updatedAirflow,
|
||||
fractionLength: 0
|
||||
)
|
||||
.focused($focusedField, equals: .updatedAirflow)
|
||||
.onSubmit { send(.submitField) }
|
||||
.numberPad()
|
||||
|
||||
} header: {
|
||||
Text("Estimate Settings")
|
||||
} footer: {
|
||||
HStack {
|
||||
Spacer()
|
||||
ResetButton { send(.resetButtonTapped) }
|
||||
Spacer()
|
||||
}
|
||||
.padding(.top)
|
||||
}
|
||||
|
||||
if let calculatedMeasurement = store.calculatedMeasurement {
|
||||
Section {
|
||||
Text("Display calculated measurement here.")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
.bind($focusedField, to: $store.focusedField)
|
||||
}
|
||||
|
||||
private func textField(
|
||||
_ title: LocalizedStringKey,
|
||||
value: Binding<Double?>,
|
||||
fractionLength: Int = 2
|
||||
) -> TextField<Text> {
|
||||
.init(title, value: value, fractionLength: fractionLength, prompt: Text(title))
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
AirHandlerMeasurementFormView(
|
||||
store: Store(initialState: AirHandlerMeasurementForm.State()) {
|
||||
AirHandlerMeasurementForm()
|
||||
}
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user