From 7cd697fb01bb25385a69d398849d6689871155bb Mon Sep 17 00:00:00 2001 From: Christoph Hagen Date: Wed, 14 Jun 2023 17:52:43 +0200 Subject: [PATCH] Show all points for day --- TempTrack.xcodeproj/project.pbxproj | 4 ++ .../UserInterfaceState.xcuserstate | Bin 60563 -> 60670 bytes TempTrack/Bluetooth/BluetoothClient.swift | 6 +-- TempTrack/Bluetooth/DeviceInfo.swift | 7 +++- TempTrack/ContentView.swift | 2 +- TempTrack/Storage/TemperatureStorage.swift | 34 ++++++++-------- .../Temperature/TemperatureDataTransfer.swift | 10 ++++- .../Temperature/TemperatureMeasurement.swift | 21 +++++++++- TempTrack/Views/DayView.swift | 38 ++++++++++++++++++ TempTrack/Views/HistoryList.swift | 7 ++-- TempTrack/Views/LogView.swift | 14 +++---- TempTrack/Views/TemperatureDayOverview.swift | 12 +++--- TempTrack/Views/TemperatureHistoryChart.swift | 2 +- 13 files changed, 116 insertions(+), 41 deletions(-) create mode 100644 TempTrack/Views/DayView.swift diff --git a/TempTrack.xcodeproj/project.pbxproj b/TempTrack.xcodeproj/project.pbxproj index 030a551..28485dc 100644 --- a/TempTrack.xcodeproj/project.pbxproj +++ b/TempTrack.xcodeproj/project.pbxproj @@ -41,6 +41,7 @@ E2A553F92A399F58005204C3 /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2A553F82A399F58005204C3 /* Log.swift */; }; E2A553FB2A39C82D005204C3 /* LogView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2A553FA2A39C82D005204C3 /* LogView.swift */; }; E2A553FD2A39C86B005204C3 /* LogEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2A553FC2A39C86B005204C3 /* LogEntry.swift */; }; + E2A553FF2A3A1024005204C3 /* DayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2A553FE2A3A1024005204C3 /* DayView.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -77,6 +78,7 @@ E2A553F82A399F58005204C3 /* Log.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Log.swift; sourceTree = ""; }; E2A553FA2A39C82D005204C3 /* LogView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogView.swift; sourceTree = ""; }; E2A553FC2A39C86B005204C3 /* LogEntry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogEntry.swift; sourceTree = ""; }; + E2A553FE2A3A1024005204C3 /* DayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DayView.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -178,6 +180,7 @@ 88404DDC2A2F587400D30244 /* HistoryListRow.swift */, 88404DDE2A2F68E100D30244 /* TemperatureDayOverview.swift */, E2A553FA2A39C82D005204C3 /* LogView.swift */, + E2A553FE2A3A1024005204C3 /* DayView.swift */, ); path = Views; sourceTree = ""; @@ -303,6 +306,7 @@ 88404DD82A2F381B00D30244 /* HistoryList.swift in Sources */, 88CDE07E2A28AFF400114294 /* DeviceInfoView.swift in Sources */, 88404DDD2A2F587400D30244 /* HistoryListRow.swift in Sources */, + E2A553FF2A3A1024005204C3 /* DayView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/TempTrack.xcodeproj/project.xcworkspace/xcuserdata/ch.xcuserdatad/UserInterfaceState.xcuserstate b/TempTrack.xcodeproj/project.xcworkspace/xcuserdata/ch.xcuserdatad/UserInterfaceState.xcuserstate index 161a99ee6b33c750075d1242c61abd6f1357ac09..c52ec07517089f0df0e536798d1a58675389c4b6 100644 GIT binary patch delta 28441 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 minimumOffsetToUpdateDeviceClock else { + guard !deviceInfo.hasDeviceStartTimeSet || abs(deviceInfo.clockOffset) > minimumOffsetToUpdateDeviceClock else { return } guard !openRequests.contains(where: { if case .setDeviceStartTime = $0 { return true }; return false }) else { return } - let time = deviceInfo.deviceStartTime.seconds + let time = deviceInfo.calculatedDeviceStartTime.seconds addRequest(.setDeviceStartTime(deviceStartTimeSeconds: time)) - log.info("Setting device start time to \(time) s (\(Date().seconds) current)") + log.info("Setting device start time to \(time) s (correcting offset of \(Int(deviceInfo.clockOffset)) s)") } // MARK: Data transfer diff --git a/TempTrack/Bluetooth/DeviceInfo.swift b/TempTrack/Bluetooth/DeviceInfo.swift index 4676adc..5154233 100644 --- a/TempTrack/Bluetooth/DeviceInfo.swift +++ b/TempTrack/Bluetooth/DeviceInfo.swift @@ -52,9 +52,14 @@ struct DeviceInfo { var clockOffset: TimeInterval { // Measurements are performed on device start (-1) and also count next measurement (+1) - let nextMeasurementTime = deviceStartTime.adding(seconds: (totalNumberOfMeasurements) * measurementInterval) + let nextMeasurementTime = deviceStartTime.adding(seconds: totalNumberOfMeasurements * measurementInterval) return nextMeasurement.timeIntervalSince(nextMeasurementTime) } + + var calculatedDeviceStartTime: Date { + let runtime = totalNumberOfMeasurements * measurementInterval + return nextMeasurement.adding(seconds: -runtime) + } } extension DeviceInfo { diff --git a/TempTrack/ContentView.swift b/TempTrack/ContentView.swift index 2a2a200..96e81de 100644 --- a/TempTrack/ContentView.swift +++ b/TempTrack/ContentView.swift @@ -104,7 +104,7 @@ struct ContentView: View { self.showHistory = true } label: { TemperatureHistoryChart(points: $storage.recentMeasurements) - .frame(height: 150) + .frame(height: 300) .background(Color.white.opacity(0.1)) .cornerRadius(8) } diff --git a/TempTrack/Storage/TemperatureStorage.swift b/TempTrack/Storage/TemperatureStorage.swift index c0bf202..c423070 100644 --- a/TempTrack/Storage/TemperatureStorage.swift +++ b/TempTrack/Storage/TemperatureStorage.swift @@ -63,6 +63,7 @@ final class TemperatureStorage: ObservableObject { } ensureExistenceOfFolder() + recalculateDailyCounts() } private func ensureExistenceOfFolder() { @@ -105,15 +106,15 @@ final class TemperatureStorage: ObservableObject { } let yesterdayValues = loadMeasurements(for: dateIndexOfStart) .filter { $0.date >= startDate } - recentMeasurements = yesterdayValues + todayValues + recentMeasurements = todayValues + yesterdayValues log.info("Loaded \(recentMeasurements.count) recent measurements") } private func updateLastMeasurements(_ measurements: [TemperatureMeasurement]) { let startDate = Date().addingTimeInterval(-lastValueInterval).seconds - let new = recentMeasurements + measurements - recentMeasurements = Array(new.drop { $0.id < startDate }) - log.info("\(recentMeasurements.count) recent measurements (of \(measurements.count) new entries)") + recentMeasurements = (measurements + recentMeasurements) + .filter { $0.id > startDate } + log.info("\(recentMeasurements.count) recent measurements (with \(measurements.count) new entries)") } private func loadMeasurements(for date: Date) -> [TemperatureMeasurement] { @@ -143,17 +144,19 @@ final class TemperatureStorage: ObservableObject { func add(_ measurements: [TemperatureMeasurement]) { let lastDate = self.newestMeasurementDate.seconds - let newerValues = measurements.filter { $0.id > lastDate } + let newerValues: [TemperatureMeasurement] = measurements.filter { $0.id > lastDate }.reversed() let newValues = newerValues.splitByDate() - log.info("Adding \(newValues.count) of \(measurements.count) measurements") + log.info("Adding \(newerValues.count) of \(measurements.count) measurements") for (dateIndex, values) in newValues { let count = saveNew(values, for: dateIndex) setDailyCount(count, for: dateIndex) - //log.info("Day \(dateIndex): \(count) values") } saveDailyCounts() updateLastMeasurements(measurements) + if let newest = newerValues.max()?.id { + newestMeasurementTime = newest + } } func removeMeasurements(for dateIndex: Int) { @@ -176,7 +179,7 @@ final class TemperatureStorage: ObservableObject { */ private func saveNew(_ measurements: [TemperatureMeasurement], for dateIndex: Int) -> Int { let fileName = fileName(for: dateIndex) - let values = loadMeasurements(from: fileName) + measurements + let values = measurements + loadMeasurements(from: fileName) save(values, for: fileName) return values.count } @@ -184,7 +187,7 @@ final class TemperatureStorage: ObservableObject { private func save(_ measurements: [TemperatureMeasurement], for fileName: String) { let fileUrl = fileUrl(for: fileName) do { - let data = try BinaryEncoder.encode(measurements.sorted()) + let data = try BinaryEncoder.encode(measurements.sorted().reversed()) try data.write(to: fileUrl) } catch { log.error("Failed to save \(fileName): \(error)") @@ -203,10 +206,7 @@ final class TemperatureStorage: ObservableObject { private func add(dailyCount count: Int, for dateIndex: Int) { let entry = MeasurementDailyCount(dateIndex: dateIndex, count: count) - guard let index = dailyMeasurementCounts.firstIndex(where: { $0.dateIndex < dateIndex }) else { - dailyMeasurementCounts.append(entry) - return - } + let index = dailyMeasurementCounts.firstIndex(where: { $0.dateIndex > dateIndex }) ?? 0 dailyMeasurementCounts.insert(entry, at: index) } @@ -240,8 +240,10 @@ final class TemperatureStorage: ObservableObject { self.dailyMeasurementCounts = measurements.reduce(into: [Int: Int]()) { counts, value in let index = value.date.dateIndex counts[index] = (counts[index] ?? 0) + 1 - }.map { MeasurementDailyCount(dateIndex: $0.key, count: $0.value) } - .sorted() + } + .map { MeasurementDailyCount(dateIndex: $0.key, count: $0.value) } + .sorted() + .reversed() } func recalculateDailyCounts() { @@ -260,13 +262,13 @@ final class TemperatureStorage: ObservableObject { self.dailyMeasurementCounts = newValues .map { .init(dateIndex: $0.key, count: $0.value) } .sorted() + .reversed() log.info("Daily counts recalculated from \(files.count) files") } } catch { log.error("Failed to load daily counts: \(error)") } } - } private extension Array where Element == TemperatureMeasurement { diff --git a/TempTrack/Temperature/TemperatureDataTransfer.swift b/TempTrack/Temperature/TemperatureDataTransfer.swift index eb3316d..f31d2fd 100644 --- a/TempTrack/Temperature/TemperatureDataTransfer.swift +++ b/TempTrack/Temperature/TemperatureDataTransfer.swift @@ -56,12 +56,16 @@ final class TemperatureDataTransfer { func add(data: Data, offset: Int, count: Int) { guard currentByteIndex == offset else { - log.warning("Discarding \(data.count) bytes at offset \(offset), expected \(currentByteIndex)") + log.warning("Transfer: Discarding \(data.count) bytes at offset \(offset), expected \(currentByteIndex)") return } + if data.count != count { + log.warning("Transfer: Expected \(count) bytes, received only \(data.count)") + } dataBuffer.append(data) currentByteIndex += data.count processBytes() + log.info("Transfer: \(currentByteIndex) bytes (added \(data.count)), \(measurements.count) points") } private func processBytes() { @@ -83,6 +87,10 @@ final class TemperatureDataTransfer { func completeTransfer() { processBytes() + if !dataBuffer.isEmpty { + log.warning("\(dataBuffer.count) bytes remaining in transfer buffer") + } + log.info("Transfer: \(currentByteIndex) bytes, \(measurements.count) points") } private func addRelative(byte: UInt8) { diff --git a/TempTrack/Temperature/TemperatureMeasurement.swift b/TempTrack/Temperature/TemperatureMeasurement.swift index 1bb8f02..80eba0d 100644 --- a/TempTrack/Temperature/TemperatureMeasurement.swift +++ b/TempTrack/Temperature/TemperatureMeasurement.swift @@ -27,7 +27,7 @@ struct TemperatureMeasurement: Identifiable { return sensor1.optionalValue } guard let s1 = sensor1.optionalValue else { - return nil + return s0 } return max(s0, s1) } @@ -37,10 +37,27 @@ struct TemperatureMeasurement: Identifiable { return sensor1.optionalValue } guard let s1 = sensor1.optionalValue else { - return nil + return s0 } return min(s0, s1) } + + var averageValue: Double? { + guard let s0 = sensor0.optionalValue else { + return sensor1.optionalValue + } + guard let s1 = sensor1.optionalValue else { + return s0 + } + return (s0 + s1) / 2 + } + + var displayText: String { + guard let averageValue else { + return "-" + } + return String(format: "%.1f °C", averageValue) + } } extension TemperatureMeasurement { diff --git a/TempTrack/Views/DayView.swift b/TempTrack/Views/DayView.swift new file mode 100644 index 0000000..e55e445 --- /dev/null +++ b/TempTrack/Views/DayView.swift @@ -0,0 +1,38 @@ +import SwiftUI + +private let df: DateFormatter = { + let df = DateFormatter() + df.dateStyle = .short + df.timeStyle = .medium + return df +}() + +struct DayView: View { + + let dateIndex: Int + + @EnvironmentObject + var storage: TemperatureStorage + + var entries: [TemperatureMeasurement] { + storage.loadMeasurements(for: dateIndex) + } + + var body: some View { + TemperatureDayOverview(points: entries) + List(entries) { entry in + HStack { + Text(df.string(from: entry.date)) + Spacer() + Text(entry.displayText) + } + } + } +} + +struct DayView_Previews: PreviewProvider { + static var previews: some View { + DayView(dateIndex: Date.now.dateIndex) + .environmentObject(TemperatureStorage.mock) + } +} diff --git a/TempTrack/Views/HistoryList.swift b/TempTrack/Views/HistoryList.swift index f7352e5..7d96465 100644 --- a/TempTrack/Views/HistoryList.swift +++ b/TempTrack/Views/HistoryList.swift @@ -9,16 +9,17 @@ struct HistoryList: View { NavigationView { List(storage.dailyMeasurementCounts) { day in NavigationLink(destination: { - TemperatureDayOverview(storage: storage, dateIndex: day.dateIndex) + DayView(dateIndex: day.dateIndex) + .environmentObject(storage) }) { HistoryListRow(entry: day) .swipeActions(edge: .trailing, allowsFullSwipe: true) { Button { deleteRow(for: day.dateIndex) } label: { - Label("Delete", systemSymbol: .pencil) + Label("Delete", systemSymbol: .trash) } - .tint(.purple) + .tint(.red) } } } diff --git a/TempTrack/Views/LogView.swift b/TempTrack/Views/LogView.swift index f0f59db..48e0201 100644 --- a/TempTrack/Views/LogView.swift +++ b/TempTrack/Views/LogView.swift @@ -1,17 +1,17 @@ import SwiftUI +private let df: DateFormatter = { + let df = DateFormatter() + df.dateStyle = .short + df.timeStyle = .medium + return df +}() + struct LogView: View { @EnvironmentObject var log: Log - private let df: DateFormatter = { - let df = DateFormatter() - df.dateStyle = .short - df.timeStyle = .medium - return df - }() - var body: some View { List(log.logEntries) { entry in VStack(alignment: .leading) { diff --git a/TempTrack/Views/TemperatureDayOverview.swift b/TempTrack/Views/TemperatureDayOverview.swift index 8b28ceb..1cb2b3b 100644 --- a/TempTrack/Views/TemperatureDayOverview.swift +++ b/TempTrack/Views/TemperatureDayOverview.swift @@ -2,14 +2,14 @@ import SwiftUI import Charts struct TemperatureDayOverview: View { - - let storage: TemperatureStorage - - @State - var points: [TemperatureMeasurement] = [] + + let points: [TemperatureMeasurement] + + init(points: [TemperatureMeasurement]) { + self.points = points + } init(storage: TemperatureStorage, dateIndex: Int) { - self.storage = storage let points = storage.loadMeasurements(for: dateIndex) self.points = points update() diff --git a/TempTrack/Views/TemperatureHistoryChart.swift b/TempTrack/Views/TemperatureHistoryChart.swift index d9e0108..0c28e87 100644 --- a/TempTrack/Views/TemperatureHistoryChart.swift +++ b/TempTrack/Views/TemperatureHistoryChart.swift @@ -34,7 +34,7 @@ struct TemperatureHistoryChart: View { .chartXAxis(.hidden) .chartLegend(.hidden) .chartYAxis { - AxisMarks(position: .trailing, values: .automatic) { value in + AxisMarks(position: .trailing, values: .stride(by: 10)) { value in AxisValueLabel(multiLabelAlignment: .trailing) { if let intValue = value.as(Int.self) { Text("\(intValue)°")