import Foundation final class TemperatureDataTransfer { private let startDateOfCurrentTransfer: Date private let interval: Int private var dataBuffer: Data = Data() private(set) var currentByteIndex = 0 private(set) var size: Int private(set) var blockSize: Int private var numberOfRecordingsInCurrentTransfer = 0 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)) } var unprocessedByteCount: Int { dataBuffer.count } var remainingBytesToTransfer: Int { size - currentByteIndex } init(info: DeviceInfo) { self.interval = info.measurementInterval let recordingTime = info.numberOfStoredMeasurements * info.measurementInterval self.startDateOfCurrentTransfer = info.nextMeasurement.addingTimeInterval(-TimeInterval(recordingTime)) self.size = info.numberOfRecordedBytes self.blockSize = info.transferBlockSize } func update(info: DeviceInfo) { self.size = info.numberOfRecordedBytes self.blockSize = info.transferBlockSize } func nextRequest() -> BluetoothRequest { guard remainingBytesToTransfer > 0 else { return .clearRecordingBuffer(byteCount: size) } let chunkSize = min(remainingBytesToTransfer, blockSize) return .getRecordingData(offset: currentByteIndex, count: chunkSize) } func add(data: Data, offset: Int, count: Int) { guard currentByteIndex == offset else { 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() { while !dataBuffer.isEmpty { let byte = dataBuffer.removeFirst() guard (byte == 0xFF) else { addRelative(byte: byte) continue } guard dataBuffer.count >= 2 else { // Wait for more data return } let temp0 = TemperatureValue(byte: dataBuffer.removeFirst()) let temp1 = TemperatureValue(byte: dataBuffer.removeFirst()) add(sensor0: temp0, sensor1: temp1) } } 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) { add(sensor0: convertTemp(value: byte >> 4, relativeTo: lastRecording.sensor0), sensor1: convertTemp(value: byte & 0x0F, relativeTo: lastRecording.sensor1)) } private func add(sensor0: TemperatureValue, sensor1: TemperatureValue) { let measurement = TemperatureMeasurement( sensor0: sensor0, sensor1: sensor1, date: dateOfNextRecording) numberOfRecordingsInCurrentTransfer += 1 if measurement.sensor0.isValid { lastRecording.sensor0 = measurement.sensor0 } if measurement.sensor1.isValid { lastRecording.sensor1 = measurement.sensor1 } lastRecording.id = measurement.id measurements.append(measurement) } private func convertTemp(value: UInt8, relativeTo previous: TemperatureValue) -> TemperatureValue { if value == 0 { return .notFound } let newValue = previous.relativeValue - (Double(value) - 8) * 0.5 return .value(newValue) } } private extension TemperatureValue { var relativeValue: Double { if case .value(let double) = self { return double } return 0 } }