EIS-BLE-S3/cue-ios/CueIOS/Views/OrpView.swift

127 lines
4.1 KiB
Swift

import SwiftUI
import Charts
struct OrpView: View {
@Bindable var state: AppState
var body: some View {
VStack(spacing: 0) {
controlsRow
Divider()
VStack(alignment: .leading, spacing: 12) {
headerValues
chart
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
.padding()
}
}
// MARK: - Controls
private var controlsRow: some View {
HStack(spacing: 10) {
LabeledField("Stabilize s", text: $state.orpStabilize, width: 80)
Button("Measure ORP") { state.startOrp() }
.buttonStyle(ActionButtonStyle(color: .green))
Button("Clear") { state.clearOrpHistory() }
.buttonStyle(ActionButtonStyle(color: Color(red: 0.55, green: 0.3, blue: 0.3)))
Text("n=\(state.orpHistory.count)")
.font(.caption)
.foregroundStyle(.secondary)
Spacer()
}
.padding(.horizontal)
.padding(.vertical, 8)
}
// MARK: - Header
@ViewBuilder
private var headerValues: some View {
if let r = state.orpResult {
VStack(alignment: .leading, spacing: 6) {
Text(String(format: "ORP: %.0f mV", r.vOrpMv))
.font(.system(size: 40, weight: .bold, design: .rounded))
.foregroundStyle(.primary)
Text(String(format: "Temp: %.1f\u{00B0}C", r.tempC))
.font(.subheadline)
.foregroundStyle(.secondary)
if let refR = state.orpRef {
let dV = r.vOrpMv - refR.vOrpMv
Text(String(format: "vs Ref: dORP=%+.1f mV (ref=%.0f mV)",
dV, refR.vOrpMv))
.font(.subheadline)
.foregroundStyle(.secondary)
}
}
} else {
VStack(alignment: .leading, spacing: 6) {
Text("No measurement yet")
.font(.title3)
.foregroundStyle(.secondary)
Text("Open-circuit potential vs AgCl reference. Above ~650 mV indicates sanitary water.")
.font(.caption)
.foregroundStyle(Color(white: 0.4))
}
}
}
// MARK: - Chart
@ViewBuilder
private var chart: some View {
if state.orpHistory.isEmpty {
RoundedRectangle(cornerRadius: 8)
.fill(Color.white.opacity(0.03))
.overlay(
Text("No samples yet")
.font(.caption)
.foregroundStyle(.secondary)
)
} else {
Chart(state.orpHistory) { sample in
LineMark(
x: .value("t", Double(sample.tS)),
y: .value("mV", Double(sample.vMv))
)
.foregroundStyle(Color.orange)
.lineStyle(StrokeStyle(lineWidth: 2))
PointMark(
x: .value("t", Double(sample.tS)),
y: .value("mV", Double(sample.vMv))
)
.foregroundStyle(Color.orange)
.symbolSize(30)
}
.chartXAxisLabel("t (s)")
.chartYAxisLabel("ORP (mV)")
.chartXAxis {
AxisMarks { _ in
AxisGridLine(stroke: StrokeStyle(lineWidth: 0.5))
.foregroundStyle(Color.gray.opacity(0.3))
AxisValueLabel()
.font(.caption2)
.foregroundStyle(.secondary)
}
}
.chartYAxis {
AxisMarks(position: .leading) { _ in
AxisGridLine(stroke: StrokeStyle(lineWidth: 0.5))
.foregroundStyle(Color.gray.opacity(0.3))
AxisValueLabel()
.font(.caption2)
.foregroundStyle(.secondary)
}
}
}
}
}