Refactor globals
This commit is contained in:
@ -3,15 +3,18 @@ import SwiftUI
|
||||
|
||||
final class BluetoothClient: ObservableObject {
|
||||
|
||||
weak var delegate: TemperatureDataTransferDelegate?
|
||||
|
||||
private let updateInterval = 3.0
|
||||
|
||||
private let minimumOffsetToUpdateDeviceClock = 5.0
|
||||
|
||||
private let connection = DeviceManager()
|
||||
|
||||
private let storage: TemperatureStorage
|
||||
|
||||
init(deviceInfo: DeviceInfo? = nil) {
|
||||
connection.delegate = self
|
||||
init(storage: TemperatureStorage, deviceInfo: DeviceInfo? = nil) {
|
||||
self.storage = storage
|
||||
self.deviceInfo = deviceInfo
|
||||
connection.delegate = self
|
||||
}
|
||||
|
||||
func connect() -> Bool {
|
||||
@ -111,16 +114,17 @@ final class BluetoothClient: ObservableObject {
|
||||
// MARK: Device time
|
||||
|
||||
private func updateDeviceTimeIfNeeded() {
|
||||
guard let info = deviceInfo else {
|
||||
guard let deviceInfo else {
|
||||
return
|
||||
}
|
||||
guard !info.hasDeviceStartTimeSet else {
|
||||
guard !deviceInfo.hasDeviceStartTimeSet || deviceInfo.clockOffset > minimumOffsetToUpdateDeviceClock else {
|
||||
return
|
||||
}
|
||||
|
||||
guard !openRequests.contains(where: { if case .setDeviceStartTime = $0 { return true }; return false }) else {
|
||||
return
|
||||
}
|
||||
let time = info.deviceStartTime.seconds
|
||||
let time = deviceInfo.deviceStartTime.seconds
|
||||
addRequest(.setDeviceStartTime(deviceStartTimeSeconds: time))
|
||||
print("Setting device start time to \(time) s (\(Date().seconds) current)")
|
||||
}
|
||||
@ -144,7 +148,6 @@ final class BluetoothClient: ObservableObject {
|
||||
|
||||
let transfer = TemperatureDataTransfer(info: info)
|
||||
runningTransfer = transfer
|
||||
runningTransfer?.delegate = delegate
|
||||
let next = transfer.nextRequest()
|
||||
addRequest(next)
|
||||
return true
|
||||
@ -171,12 +174,13 @@ final class BluetoothClient: ObservableObject {
|
||||
return
|
||||
}
|
||||
self.deviceInfo = newInfo
|
||||
guard let runningTransfer else {
|
||||
return
|
||||
if let runningTransfer {
|
||||
runningTransfer.update(info: newInfo)
|
||||
let next = runningTransfer.nextRequest()
|
||||
addRequest(next)
|
||||
} else if newInfo.numberOfStoredMeasurements > 0 {
|
||||
collectRecordedData()
|
||||
}
|
||||
runningTransfer.update(info: newInfo)
|
||||
let next = runningTransfer.nextRequest()
|
||||
addRequest(next)
|
||||
}
|
||||
}
|
||||
|
||||
@ -226,13 +230,24 @@ extension BluetoothClient: DeviceManagerDelegate {
|
||||
case .getRecordingData(let offset, let count):
|
||||
didReceive(data: payload, offset: offset, count: count)
|
||||
case .clearRecordingBuffer:
|
||||
runningTransfer?.completeTransfer()
|
||||
runningTransfer = nil
|
||||
didClearDeviceStorage()
|
||||
|
||||
case .setDeviceStartTime:
|
||||
print("Device time set")
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
private func didClearDeviceStorage() {
|
||||
guard let runningTransfer else {
|
||||
return
|
||||
}
|
||||
runningTransfer.completeTransfer()
|
||||
storage.add(runningTransfer.measurements)
|
||||
self.runningTransfer = nil
|
||||
|
||||
updateDeviceTimeIfNeeded()
|
||||
}
|
||||
|
||||
func deviceManager(didChangeState state: DeviceState) {
|
||||
DispatchQueue.main.async {
|
||||
|
@ -1,6 +1,5 @@
|
||||
import SwiftUI
|
||||
import SFSafeSymbols
|
||||
import BottomSheet
|
||||
|
||||
struct ContentView: View {
|
||||
|
||||
@ -144,9 +143,11 @@ struct ContentView: View {
|
||||
|
||||
struct ContentView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
let storage = TemperatureStorage(lastMeasurements: TemperatureMeasurement.mockData)
|
||||
let client = BluetoothClient(storage: storage, deviceInfo: .mock)
|
||||
ContentView()
|
||||
.environmentObject(TemperatureStorage(lastMeasurements: TemperatureMeasurement.mockData))
|
||||
.environmentObject(BluetoothClient(deviceInfo: .mock))
|
||||
.environmentObject(storage)
|
||||
.environmentObject(client)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,10 +11,15 @@ final class TemperatureStorage: ObservableObject {
|
||||
in: .userDomainMask,
|
||||
appropriateFor: nil, create: true)
|
||||
}
|
||||
|
||||
|
||||
@AppStorage("newestDate")
|
||||
private var newestMeasurementTime: Int = 0
|
||||
|
||||
|
||||
/**
|
||||
The date of the latest measurement.
|
||||
|
||||
Incoming data older than this date will be rejected to prevent duplicate measurements
|
||||
*/
|
||||
private var newestMeasurementDate: Date {
|
||||
get {
|
||||
Date(seconds: newestMeasurementTime)
|
||||
@ -30,8 +35,6 @@ final class TemperatureStorage: ObservableObject {
|
||||
@Published
|
||||
var dailyMeasurementCounts: [MeasurementDailyCount] = []
|
||||
|
||||
private var unsavedMeasurements: [TemperatureMeasurement] = []
|
||||
|
||||
private let fileNameFormatter: DateFormatter
|
||||
|
||||
private let storageFolder: URL
|
||||
@ -98,6 +101,12 @@ final class TemperatureStorage: ObservableObject {
|
||||
.filter { $0.date >= startDate }
|
||||
recentMeasurements = yesterdayValues + todayValues
|
||||
}
|
||||
|
||||
private func updateLastMeasurements(_ measurements: [TemperatureMeasurement]) {
|
||||
let startDate = Date().addingTimeInterval(-lastValueInterval).seconds
|
||||
let new = recentMeasurements + measurements
|
||||
recentMeasurements = Array(new.drop { $0.id < startDate })
|
||||
}
|
||||
|
||||
private func loadMeasurements(for date: Date) -> [TemperatureMeasurement] {
|
||||
loadMeasurements(from: fileName(for: date))
|
||||
@ -123,14 +132,20 @@ final class TemperatureStorage: ObservableObject {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
func save() {
|
||||
for (dateIndex, values) in unsavedMeasurements.splitByDate() {
|
||||
|
||||
func add(_ measurements: [TemperatureMeasurement]) {
|
||||
let lastDate = self.newestMeasurementDate.seconds
|
||||
let newValues = measurements
|
||||
.filter { $0.id > lastDate }
|
||||
.splitByDate()
|
||||
|
||||
for (dateIndex, values) in newValues {
|
||||
let count = saveNew(values, for: dateIndex)
|
||||
print("Day \(dateIndex): \(count) of \(values.count) saved")
|
||||
setDailyCount(count, for: dateIndex)
|
||||
print("Day \(dateIndex): \(count) values")
|
||||
}
|
||||
unsavedMeasurements = []
|
||||
saveDailyCounts()
|
||||
updateLastMeasurements(measurements)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -138,21 +153,9 @@ final class TemperatureStorage: ObservableObject {
|
||||
*/
|
||||
private func saveNew(_ measurements: [TemperatureMeasurement], for dateIndex: Int) -> Int {
|
||||
let fileName = fileName(for: dateIndex)
|
||||
var existing = loadMeasurements(from: fileName)
|
||||
guard !existing.isEmpty else {
|
||||
save(measurements, for: fileName)
|
||||
setDailyCount(measurements.count, for: dateIndex)
|
||||
return measurements.count
|
||||
}
|
||||
var inserted = 0
|
||||
for value in measurements {
|
||||
if existing.insert(value) {
|
||||
inserted += 1
|
||||
}
|
||||
}
|
||||
save(existing, for: fileName)
|
||||
setDailyCount(existing.count, for: dateIndex)
|
||||
return inserted
|
||||
let values = loadMeasurements(from: fileName) + measurements
|
||||
save(values, for: fileName)
|
||||
return values.count
|
||||
}
|
||||
|
||||
private func save(_ measurements: [TemperatureMeasurement], for fileName: String) {
|
||||
@ -239,23 +242,6 @@ final class TemperatureStorage: ObservableObject {
|
||||
|
||||
}
|
||||
|
||||
extension TemperatureStorage: TemperatureDataTransferDelegate {
|
||||
|
||||
func didReceiveRecording(_ measurement: TemperatureMeasurement) {
|
||||
// Add to unsaved measurements
|
||||
if unsavedMeasurements.insert(measurement) {
|
||||
incrementCount(for: measurement.date.dateIndex)
|
||||
}
|
||||
|
||||
// Add to last measurements
|
||||
recentMeasurements.insert(measurement)
|
||||
}
|
||||
|
||||
func saveAfterTransfer() {
|
||||
save()
|
||||
}
|
||||
}
|
||||
|
||||
private extension Array where Element == TemperatureMeasurement {
|
||||
|
||||
@discardableResult
|
||||
|
@ -1,7 +1,9 @@
|
||||
import SwiftUI
|
||||
|
||||
let storage = TemperatureStorage()
|
||||
let bluetoothClient = BluetoothClient()
|
||||
private let storage = TemperatureStorage()
|
||||
private let bluetoothClient: BluetoothClient = {
|
||||
.init(storage: storage)
|
||||
}()
|
||||
|
||||
@main
|
||||
struct TempTrackApp: App {
|
||||
@ -11,9 +13,6 @@ struct TempTrackApp: App {
|
||||
ContentView()
|
||||
.environmentObject(storage)
|
||||
.environmentObject(bluetoothClient)
|
||||
.onAppear {
|
||||
bluetoothClient.delegate = storage
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,8 +6,6 @@ final class TemperatureDataTransfer {
|
||||
|
||||
private let interval: Int
|
||||
|
||||
weak var delegate: TemperatureDataTransferDelegate?
|
||||
|
||||
private var dataBuffer: Data = Data()
|
||||
|
||||
private(set) var currentByteIndex = 0
|
||||
@ -17,8 +15,11 @@ final class TemperatureDataTransfer {
|
||||
private(set) var blockSize: Int
|
||||
|
||||
private var numberOfRecordingsInCurrentTransfer = 0
|
||||
|
||||
private(set) var lastRecording: TemperatureMeasurement = .init(sensor0: .notFound, sensor1: .notFound, date: .now)
|
||||
|
||||
var measurements: [TemperatureMeasurement] = []
|
||||
|
||||
/// The last temperatures to calculate relative values
|
||||
private var lastRecording: TemperatureMeasurement = .init(sensor0: .notFound, sensor1: .notFound, date: .now)
|
||||
|
||||
private var dateOfNextRecording: Date {
|
||||
startDateOfCurrentTransfer.addingTimeInterval(TimeInterval(numberOfRecordingsInCurrentTransfer * interval))
|
||||
@ -78,7 +79,6 @@ final class TemperatureDataTransfer {
|
||||
|
||||
func completeTransfer() {
|
||||
processBytes()
|
||||
delegate?.saveAfterTransfer()
|
||||
}
|
||||
|
||||
private func addRelative(byte: UInt8) {
|
||||
@ -100,7 +100,7 @@ final class TemperatureDataTransfer {
|
||||
lastRecording.sensor1 = measurement.sensor1
|
||||
}
|
||||
lastRecording.id = measurement.id
|
||||
delegate?.didReceiveRecording(measurement)
|
||||
measurements.append(measurement)
|
||||
}
|
||||
|
||||
private func convertTemp(value: UInt8, relativeTo previous: TemperatureValue) -> TemperatureValue {
|
||||
|
@ -1,8 +0,0 @@
|
||||
import Foundation
|
||||
|
||||
protocol TemperatureDataTransferDelegate: AnyObject {
|
||||
|
||||
func didReceiveRecording(_ measurement: TemperatureMeasurement)
|
||||
|
||||
func saveAfterTransfer()
|
||||
}
|
@ -5,7 +5,8 @@ struct TemperatureMeasurement: Identifiable {
|
||||
var sensor0: TemperatureValue
|
||||
|
||||
var sensor1: TemperatureValue
|
||||
|
||||
|
||||
/// The seconds since 1970
|
||||
var id: Int
|
||||
|
||||
var date: Date {
|
||||
@ -18,7 +19,7 @@ struct TemperatureMeasurement: Identifiable {
|
||||
}
|
||||
|
||||
var secondsToNow: Int {
|
||||
Date().seconds - id
|
||||
id - Date().seconds
|
||||
}
|
||||
|
||||
var maximumValue: Double? {
|
||||
@ -172,7 +173,7 @@ extension TemperatureMeasurement {
|
||||
TemperatureMeasurement(
|
||||
sensor0: .init(value: $0.element.0),
|
||||
sensor1: .init(value: $0.element.1),
|
||||
id: seconds + $0.offset * 60)
|
||||
id: seconds + ($0.offset - temps.count) * 60)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
Reference in New Issue
Block a user