Skip to content

Commit

Permalink
bunch of fixes / cleanups for timeline segments / recorder / etc #11
Browse files Browse the repository at this point in the history
  • Loading branch information
sobri909 committed May 6, 2018
1 parent 6815118 commit 4caa753
Show file tree
Hide file tree
Showing 10 changed files with 99 additions and 48 deletions.
2 changes: 1 addition & 1 deletion LocoKit/Base/Timelines/Items/Path.swift
Original file line number Diff line number Diff line change
Expand Up @@ -242,8 +242,8 @@ open class Path: TimelineItem {
}

override open func samplesChanged() {
_distance = nil
super.samplesChanged()
_distance = nil
}
}

Expand Down
24 changes: 20 additions & 4 deletions LocoKit/Base/Timelines/Merge.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@

import os.log

public extension NSNotification.Name {
public static let mergedTimelineItems = Notification.Name("mergedTimelineItems")
}

typealias MergeScore = ConsumptionScore
public typealias MergeResult = (kept: TimelineItem, killed: [TimelineItem])

internal class Merge: CustomStringConvertible {

Expand Down Expand Up @@ -42,14 +47,25 @@ internal class Merge: CustomStringConvertible {
if let betweener = betweener { store.release(betweener) }
}

@discardableResult func doIt() -> (kept: TimelineItem, killed: [TimelineItem]) {
@discardableResult func doIt() -> MergeResult {
let description = String(describing: self)
os_log("Doing:\n%@", type: .debug, description)

merge(deadman, into: keeper)


let results: MergeResult
if let betweener = betweener {
return (kept: keeper, killed: [deadman, betweener])
results = (kept: keeper, killed: [deadman, betweener])
} else {
return (kept: keeper, killed: [deadman])
results = (kept: keeper, killed: [deadman])
}

// notify listeners
let note = Notification(name: .mergedTimelineItems, object: self,
userInfo: ["description": description, "results": results])
NotificationCenter.default.post(note)

return results
}

private func merge(_ deadman: TimelineItem, into keeper: TimelineItem) {
Expand Down
46 changes: 27 additions & 19 deletions LocoKit/Base/Timelines/TimelineProcessor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,10 @@

import os.log

public extension NSNotification.Name {
public static let mergedTimelineItems = Notification.Name("mergedTimelineItems")
}

public class TimelineProcessor {

public static func process(from fromItem: TimelineItem) {
guard let store = fromItem.store else { return }
store.process {
fromItem.store?.process {
var items: [TimelineItem] = [fromItem]

// collect items before fromItem, up to two keepers
Expand All @@ -36,6 +31,21 @@ public class TimelineProcessor {
workingItem = next
}

// recurse until no remaining possible merges
process(items) { results in
if let kept = results?.kept {
process(from: kept)
}
}
}
}

public static func process(_ items: [TimelineItem], completion: ((MergeResult?) -> Void)? = nil) {
guard let store = items.first?.store else { return }
store.process {

/** collate all the potential merges **/

var merges: [Merge] = []
for workingItem in items {
workingItem.sanitiseEdges()
Expand Down Expand Up @@ -88,7 +98,8 @@ public class TimelineProcessor {
}
}

// sort the merges by highest to lowest score
/** sort the merges by highest to lowest score **/

merges = merges.sorted { $0.score.rawValue > $1.score.rawValue }

if !merges.isEmpty {
Expand All @@ -97,21 +108,18 @@ public class TimelineProcessor {
os_log("Considering:\n%@", type: .debug, descriptions)
}

// do the highest scoring valid merge
if let winningMerge = merges.first, winningMerge.score != .impossible {
let description = String(describing: winningMerge)
os_log("Doing:\n%@", type: .debug, description)
/** find the highest scoring valid merge **/

let results = winningMerge.doIt()
guard let winningMerge = merges.first, winningMerge.score != .impossible else {
completion?(nil)
return
}

onMain {
let note = Notification(name: .mergedTimelineItems, object: self, userInfo: ["merge": description])
NotificationCenter.default.post(note)
}
/** do it **/

// recurse until no valid merges left to do
self.process(from: results.kept)
}
let results = winningMerge.doIt()

completion?(results)
}
}

Expand Down
25 changes: 21 additions & 4 deletions LocoKit/Base/Timelines/TimelineRecorder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,9 @@ public class TimelineRecorder {

public init(store: TimelineStore, classifier: MLCompositeClassifier? = nil) {
self.store = store
store.recorder = self
self.classifier = classifier

// bootstrap the current item
self.currentItem = store.mostRecentItem

let notes = NotificationCenter.default
notes.addObserver(forName: .locomotionSampleUpdated, object: nil, queue: nil) { [weak self] _ in
self?.recordSample()
Expand All @@ -51,6 +49,15 @@ public class TimelineRecorder {
notes.addObserver(forName: .recordingStateChanged, object: nil, queue: nil) { [weak self] _ in
self?.updateSleepModeAcceptability()
}

// keep currentItem sane after merges
notes.addObserver(forName: .mergedTimelineItems, object: nil, queue: nil) { [weak self] note in
guard let results = note.userInfo?["results"] as? MergeResult else { return }
guard let current = self?.currentItem else { return }
if results.killed.contains(current) {
self?.currentItem = results.kept
}
}
}

// MARK: - Starting and stopping recording
Expand Down Expand Up @@ -97,7 +104,17 @@ public class TimelineRecorder {

// MARK: - The recording cycle

private(set) public var currentItem: TimelineItem?
private var _currentItem: TimelineItem?
public private(set) var currentItem: TimelineItem? {
get {
if let item = _currentItem { return item }
_currentItem = store.mostRecentItem
return _currentItem
}
set(newValue) {
_currentItem = newValue
}
}

public var currentVisit: Visit? { return currentItem as? Visit }

Expand Down
9 changes: 7 additions & 2 deletions LocoKit/LocalStore/PersistentObject.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ public protocol PersistentObject: TimelineObject, Persistable {
var transactionDate: Date? { get set }
var lastSaved: Date? { get set }
var unsaved: Bool { get }
var hasChanges: Bool { get }
var hasChanges: Bool { get set }
var needsSave: Bool { get }

func save(immediate: Bool)
func save(in db: Database) throws
Expand All @@ -26,8 +27,12 @@ public protocol PersistentObject: TimelineObject, Persistable {

public extension PersistentObject {
public var unsaved: Bool { return lastSaved == nil }
public var needsSave: Bool { return unsaved || hasChanges }
public func save(immediate: Bool = false) { persistentStore.save(self, immediate: immediate) }
public func save(in db: Database) throws { if unsaved { try insert(db) } else if hasChanges { try update(db) } }
public func save(in db: Database) throws {
if unsaved { try insert(db) } else if hasChanges { try update(db) }
hasChanges = false
}
}

public extension PersistentObject where Self: TimelineItem {
Expand Down
4 changes: 2 additions & 2 deletions LocoKit/LocalStore/PersistentPath.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ open class PersistentPath: Path, PersistentObject {
didSet {
if oldValue != deleted {
hasChanges = true
save(immediate: true)
save()
}
}
}
Expand Down Expand Up @@ -114,7 +114,7 @@ open class PersistentPath: Path, PersistentObject {

public var transactionDate: Date?
public var lastSaved: Date?
public private(set) var hasChanges: Bool = false
public var hasChanges: Bool = false

// MARK: Initialisers

Expand Down
2 changes: 1 addition & 1 deletion LocoKit/LocalStore/PersistentSample.swift
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ open class PersistentSample: LocomotionSample, PersistentObject {
public var persistentStore: PersistentTimelineStore { return store as! PersistentTimelineStore }
public var transactionDate: Date?
public var lastSaved: Date?
public private(set) var hasChanges: Bool = false
public var hasChanges: Bool = false

}

4 changes: 2 additions & 2 deletions LocoKit/LocalStore/PersistentTimelineStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -212,10 +212,10 @@ open class PersistentTimelineStore: TimelineStore {
mutex.sync {
guard immediate || (itemsToSave.count + samplesToSave.count >= saveBatchSize) else { return }

savingItems = itemsToSave
savingItems = itemsToSave.filter { ($0 as? PersistentItem)?.needsSave == true }
itemsToSave.removeAll(keepingCapacity: true)

savingSamples = samplesToSave
savingSamples = samplesToSave.filter { $0.needsSave }
samplesToSave.removeAll(keepingCapacity: true)
}

Expand Down
4 changes: 2 additions & 2 deletions LocoKit/LocalStore/PersistentVisit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ open class PersistentVisit: Visit, PersistentObject {
didSet {
if oldValue != deleted {
hasChanges = true
save(immediate: true)
save()
}
}
}
Expand Down Expand Up @@ -114,7 +114,7 @@ open class PersistentVisit: Visit, PersistentObject {

public var transactionDate: Date?
public var lastSaved: Date?
public private(set) var hasChanges: Bool = false
public var hasChanges: Bool = false

// MARK: Initialisers

Expand Down
27 changes: 16 additions & 11 deletions LocoKit/LocalStore/TimelineSegment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,21 @@
// Created by Matt Greenfield on 29/04/18.
//

import os.log
import GRDB

public class TimelineSegment {

public let store: PersistentTimelineStore
public private(set) var timelineItems: [TimelineItem]?
public var onUpdate: (() -> Void)?

private var _timelineItems: [TimelineItem]?
public var timelineItems: [TimelineItem] {
if let existing = _timelineItems { return existing }
_timelineItems = updatedItems
return _timelineItems!
}

private let query: String
private let arguments: StatementArguments?
private let queue = DispatchQueue(label: "TimelineSegment")
Expand All @@ -37,14 +44,16 @@ public class TimelineSegment {
self.observer = try FetchedRecordsController<RowCopy>(store.pool, sql: fullQuery, arguments: arguments,
queue: queue)

observer.trackChanges { [weak self] observer in
self.observer.trackChanges { [weak self] observer in
self?.needsUpdate()
}
self.observer.trackErrors { observer, error in
os_log("FetchedRecordsController error: %@", type: .error, error.localizedDescription)
}

queue.async {
do {
try self.observer.performFetch()
self.update()
} catch {
fatalError("OOPS: \(error)")
}
Expand All @@ -58,21 +67,17 @@ public class TimelineSegment {
// MARK: - Result updating

private func needsUpdate() {
_timelineItems = nil
onMain {
self.updateTimer?.invalidate()
self.updateTimer = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: false) { [weak self] _ in
self?.update()
self?.onUpdate?()
}
}
}

private func update() {
var items: [TimelineItem] = []
for row in observer.fetchedRecords {
items.append(store.item(for: row.row))
}
self.timelineItems = items
onUpdate?()
private var updatedItems: [TimelineItem] {
return observer.fetchedRecords.map { store.item(for: $0.row) }
}

}

0 comments on commit 4caa753

Please sign in to comment.