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:
parent
49b2508da4
commit
13a4c4ff3d
5 changed files with 104 additions and 52 deletions
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue