Fix dataSeries using wrong order when reloading charts

Fix showing old data for a moment when reloading the chart
Cosmetic code changes
This commit is contained in:
Antonio 2020-03-13 21:42:57 +01:00
parent 49b2508da4
commit 13a4c4ff3d
5 changed files with 104 additions and 52 deletions

View file

@ -111,10 +111,10 @@ class AdafruitBoard {
}
// Params - Delegates
weak var temperatureDelegate: AdafruitTemperatureDelegate?
weak var lightDelegate: AdafruitLightDelegate?
weak var buttonsDelegate: AdafruitButtonsDelegate?
weak var accelerometerDelegate: AdafruitAccelerometerDelegate?
weak var buttonsDelegate: AdafruitButtonsDelegate?
weak var temperatureDelegate: AdafruitTemperatureDelegate?
weak var humidityDelegate: AdafruitHumidityDelegate?
weak var barometricPressureDelegate: AdafruitBarometricPressureDelegate?
weak var soundDelegate: AdafruitSoundDelegate?
@ -191,7 +191,7 @@ class AdafruitBoard {
}
// Setup services
let selectedServices = services != nil ? services! : BoardService.allCases // If services is nil, select all services
let selectedServices = /*Config.isDebugEnabled ? [.temperature] :*/ (services != nil ? services! : BoardService.allCases) // If services is nil, select all services
self.setupServices(blePeripheral: blePeripheral, services: selectedServices, completion: completion)
}
}
@ -368,7 +368,7 @@ class AdafruitBoard {
case .quaternion: return isQuaternionEnabled
}
}
// MARK: - Read Data
func lightLastValue() -> Float? {
return blePeripheral?.adafruitLightLastValue()

View file

@ -8,7 +8,7 @@
import Foundation
struct SensorDataSeries<T>: Sequence, IteratorProtocol {
class SensorDataSeries<T>: Sequence {
// Config
let kMaxNumItems = 1000
@ -20,39 +20,57 @@ struct SensorDataSeries<T>: Sequence, IteratorProtocol {
private var lastInsertIndex = -1 // Last index where a value was inserted
private var values = [Entry]()
private var valuesLock = NSLock()
private let queue = DispatchQueue(label: "\(Bundle.main.bundleIdentifier!).SensorDataSeries", attributes: .concurrent)
init() {
values.reserveCapacity(kMaxNumItems)
}
// Acccesors
mutating func addValue(_ value: Entry) {
valuesLock.lock(); defer { valuesLock.unlock() }
let insertIndex = (lastInsertIndex + 1) % kMaxNumItems
if insertIndex == values.count { // Array not full. Add value
values.insert(value, at: insertIndex)
func addValue(_ value: Entry) {
queue.async(flags: .barrier) {
let insertIndex = (self.lastInsertIndex + 1) % self.kMaxNumItems
if insertIndex == self.values.count { // Array not full. Add value
self.values.insert(value, at: insertIndex)
}
else { // Array full, replace value
self.values[insertIndex] = value
}
self.lastInsertIndex = insertIndex
}
else { // Array full, replace value
values[insertIndex] = value
}
lastInsertIndex = insertIndex
}
subscript(index: Int) -> Entry {
return values[internalIndex(index)]
subscript(index: Int) -> Entry? {
var result: Entry?
queue.sync {
guard self.values.startIndex..<self.values.endIndex ~= index else { return }
result = values[internalIndex(index)]
}
return result
}
var first: Entry? {
valuesLock.lock(); defer { valuesLock.unlock() }
guard values.count > 0 else { return nil }
return self[0]
var result: Entry?
queue.sync {
let index = internalIndex(0)
result = values.count > index ? values[index] : nil
}
return result
}
var last: Entry? {
var result: Entry?
queue.sync {
let index = internalIndex(values.count-1)
result = values.count > index ? values[index] : nil
}
return result
}
var last: Entry? {
valuesLock.lock(); defer { valuesLock.unlock() }
guard values.count > 0 else { return nil }
return self[values.count-1]
var count: Int {
var result = 0
queue.sync { result = self.values.count }
return result
}
// MARK: - Utils
@ -60,7 +78,7 @@ struct SensorDataSeries<T>: Sequence, IteratorProtocol {
let startIndex = lastInsertIndex - (values.count - 1)
return mod((startIndex + index), kMaxNumItems) // Use mod instead of %, because numerator could be negative
}
private func mod(_ a: Int, _ n: Int) -> Int {
// From: https://stackoverflow.com/questions/41180292/negative-number-modulo-in-swift
precondition(n > 0, "modulus must be positive")
@ -69,15 +87,31 @@ struct SensorDataSeries<T>: Sequence, IteratorProtocol {
}
// MARK: - IteratorProtocol
private var iteratorPosition = 0
mutating func makeIterator() -> SensorDataSeries<T> {
iteratorPosition = 0
return self
func makeIterator() -> Iterator {
return Iterator(self, queue: queue)
}
mutating func next() -> Entry? {
guard iteratorPosition < values.count else { return nil }
defer { iteratorPosition = iteratorPosition + 1 }
return self[internalIndex(iteratorPosition)]
// MARK: -
struct Iterator: IteratorProtocol {
private let dataSeries: SensorDataSeries
private var iteratorPosition = 0
private var queue: DispatchQueue
init(_ dataSeries: SensorDataSeries, queue: DispatchQueue) {
self.dataSeries = dataSeries
self.queue = queue
}
mutating func next() -> Entry? {
var result: Entry?
queue.sync {
guard iteratorPosition < dataSeries.values.count else { return }
defer { iteratorPosition = iteratorPosition + 1 }
result = dataSeries.values[dataSeries.internalIndex(iteratorPosition)]
}
return result
}
}
}

View file

@ -17,8 +17,8 @@ class AccelerometerViewController: ModuleViewController {
@IBOutlet weak var sceneView: SCNView!
// Data
private var acceleration = BlePeripheral.AccelerometerValue(x: 0, y: 0, z: 0)
private var circuitNode: SCNNode?
private var acceleration: BlePeripheral.AccelerometerValue?
private var boardNode: SCNNode?
private var valuesPanelViewController: AccelerometerPanelViewController!
// MARK: - Lifecycle
@ -27,11 +27,10 @@ class AccelerometerViewController: ModuleViewController {
// Add panels
valuesPanelViewController = (addPanelViewController(storyboardIdentifier: AccelerometerPanelViewController.kIdentifier) as! AccelerometerPanelViewController)
// Load scene
if let scene = AdafruitBoardsManager.shared.currentBoard?.assetScene {
circuitNode = scene.rootNode.childNode(withName: "root", recursively: false)!
boardNode = scene.rootNode.childNode(withName: "root", recursively: false)!
// Setup scene
sceneView.scene = scene
@ -44,7 +43,7 @@ class AccelerometerViewController: ModuleViewController {
self.title = localizationManager.localizedString("accelerometer_title")
moduleHelpMessage = localizationManager.localizedString("accelerometer_help")
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
@ -53,6 +52,7 @@ class AccelerometerViewController: ModuleViewController {
if let acceleration = board?.accelerometerLastValue() {
self.acceleration = acceleration
}
SCNTransaction.animationDuration = 0 // The first render should be inmediate and not animated
updateValueUI()
// Set delegate
@ -69,16 +69,19 @@ class AccelerometerViewController: ModuleViewController {
// MARK: - UI
private func updateValueUI() {
guard let acceleration = acceleration else { return }
SCNTransaction.animationDuration = BlePeripheral.kAdafruitSensorDefaultPeriod
// Calculate Euler Angles
let eulerAngles = AccelerometerUtils.accelerationToEuler(acceleration)
//DLog("Euler: pitch: \(eulerAngles.x) yaw: \(eulerAngles.y) roll: \(eulerAngles.z)")
// Update circuit model orientation
SCNTransaction.animationDuration = BlePeripheral.kAdafruitSensorDefaultPeriod
circuitNode?.eulerAngles = eulerAngles
boardNode?.eulerAngles = eulerAngles
// Update panel
valuesPanelViewController.accelerationReceived(acceleration: self.acceleration, eulerAngles: eulerAngles)
valuesPanelViewController.accelerationReceived(acceleration: acceleration, eulerAngles: eulerAngles)
}
}
@ -89,3 +92,5 @@ extension AccelerometerViewController: AdafruitAccelerometerDelegate {
updateValueUI()
}
}

View file

@ -26,10 +26,17 @@ class ChartPanelViewController: ModulePanelViewController {
setupChart()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
notifyDataSetChanged() // Important: reload dataset to avoid showing for a moment a weird chart
}
// MARK: - Data
internal func dataSeriesValueToChartValue(_ value: Float) -> Double {
return Double(value)
}
private var valuesLock = NSLock()
// MARK: - Line Chart
private func setupChart() {
@ -50,11 +57,12 @@ class ChartPanelViewController: ModulePanelViewController {
}
internal func reloadChartEntries(dataSeries: SensorDataSeries<Float>) {
valuesLock.lock(); defer { valuesLock.unlock() } // Don't change the timestamp while addingEntries
let maxTimestamp = dataSeries.max { (a, b) -> Bool in
let minTimestamp = dataSeries.min { (a, b) -> Bool in
return a.timestamp < b.timestamp
}?.timestamp
originTimestamp = maxTimestamp ?? CFAbsoluteTimeGetCurrent()
}?.timestamp
originTimestamp = min(originTimestamp, minTimestamp ?? CFAbsoluteTimeGetCurrent())
let entries = chartEntries(dataSeries: dataSeries)
// Add Dataset
@ -69,11 +77,14 @@ class ChartPanelViewController: ModulePanelViewController {
// Set dataset
chartView.data = LineChartData(dataSet: dataSet)
}
private func notifyDataSetChanged() {
/*
let isViewVisible = self.viewIfLoaded?.window != nil // https://stackoverflow.com/questions/2777438/how-to-tell-if-uiviewcontrollers-view-is-visible
guard isViewVisible else { return }
*/
guard let dataSet = dataSet else { return }
chartView.data?.notifyDataChanged()
@ -85,8 +96,9 @@ class ChartPanelViewController: ModulePanelViewController {
let xOffset = (dataSet.entries.last?.x ?? 0) - (visibleInterval-1)
chartView.moveViewToX(xOffset)
}
}
// MARK:- Utils
private func chartEntries(dataSeries: SensorDataSeries<Float>) -> [ChartDataEntry] {
let chartEntries = dataSeries.map { entry -> ChartDataEntry in
@ -98,6 +110,7 @@ class ChartPanelViewController: ModulePanelViewController {
// MARK: - Actions
func addEntry(_ entry: SensorDataSeries<Float>.Entry) {
valuesLock.lock(); defer { valuesLock.unlock() } // Don't change the timestamp while addingEntries
guard let dataSet = dataSet else { return }
let value = dataSeriesValueToChartValue(entry.value)

View file

@ -17,7 +17,7 @@ class QuaternionViewController: ModuleViewController {
@IBOutlet weak var sceneView: SCNView!
// Data
private var quaternion = BlePeripheral.QuaternionValue(x: 0, y: 0, z: 0, w: 1)
private var quaternion: BlePeripheral.QuaternionValue?
private var boardNode: SCNNode?
private var valuesPanelViewController: QuaternionPanelViewController!
@ -68,12 +68,12 @@ class QuaternionViewController: ModuleViewController {
// MARK: - UI
private func updateValueUI() {
guard let quaternion = quaternion else { return }
let scnQuaternion = simd_quatf(ix: quaternion.x, iy: quaternion.y, iz: quaternion.z, r: quaternion.w)
// Update circuit model orientation
SCNTransaction.animationDuration = BlePeripheral.kAdafruitSensorDefaultPeriod
// let scnQuaternion = SCNQuaternion(quaternion.qx, quaternion.qy, quaternion.qz, quaternion.qw)
let scnQuaternion = simd_quatf(ix: quaternion.x, iy: quaternion.y, iz: quaternion.z, r: quaternion.w)
//boardNode?.orientation = scnQuaternion
boardNode?.simdOrientation = scnQuaternion
// Update panel