diff --git a/cue-ios/CueIOS/Models/Storage.swift b/cue-ios/CueIOS/Models/Storage.swift index b6e29fc..4b4f04e 100644 --- a/cue-ios/CueIOS/Models/Storage.swift +++ b/cue-ios/CueIOS/Models/Storage.swift @@ -11,6 +11,7 @@ struct Session: Codable, FetchableRecord, MutablePersistableRecord { var startedAt: Date var label: String? var notes: String? + var firmwareSessionId: Int64? mutating func didInsert(_ inserted: InsertionSuccess) { id = inserted.rowID @@ -24,6 +25,7 @@ struct Measurement: Codable, FetchableRecord, MutablePersistableRecord { var startedAt: Date var config: Data? var resultSummary: Data? + var espTimestamp: Int64? mutating func didInsert(_ inserted: InsertionSuccess) { id = inserted.rowID @@ -89,19 +91,34 @@ final class Storage: @unchecked Sendable { } } + migrator.registerMigration("v2") { db in + try db.alter(table: "measurement") { t in + t.add(column: "espTimestamp", .integer) + } + try db.alter(table: "session") { t in + t.add(column: "firmwareSessionId", .integer) + } + } + try migrator.migrate(dbQueue) } // MARK: - Sessions - func createSession(label: String? = nil) throws -> Session { + func createSession(label: String? = nil, firmwareSessionId: Int64? = nil) throws -> Session { try dbQueue.write { db in - var s = Session(startedAt: Date(), label: label) + var s = Session(startedAt: Date(), label: label, firmwareSessionId: firmwareSessionId) try s.insert(db) return s } } + func fetchSession(_ id: Int64) -> Session? { + try? dbQueue.read { db in + try Session.fetchOne(db, key: id) + } + } + func fetchSessions() throws -> [Session] { try dbQueue.read { db in try Session.order(Column("startedAt").desc).fetchAll(db) @@ -123,13 +140,43 @@ final class Storage: @unchecked Sendable { } } + func updateSessionLabel(_ id: Int64, label: String) throws { + try dbQueue.write { db in + try db.execute( + sql: "UPDATE session SET label = ? WHERE id = ?", + arguments: [label, id] + ) + } + } + + func sessionByFirmwareId(_ fwId: Int64) -> Session? { + try? dbQueue.read { db in + try Session + .filter(Column("firmwareSessionId") == fwId) + .fetchOne(db) + } + } + // MARK: - Measurements + func measurementExists(sessionId: Int64, espTimestamp: Int64) -> Bool { + (try? dbQueue.read { db in + try Measurement + .filter(Column("sessionId") == sessionId) + .filter(Column("espTimestamp") == espTimestamp) + .fetchCount(db) > 0 + }) ?? false + } + func addMeasurement( sessionId: Int64, type: MeasurementType, - config: (any Encodable)? = nil + config: (any Encodable)? = nil, + espTimestamp: Int64? = nil ) throws -> Measurement { + if let ts = espTimestamp, measurementExists(sessionId: sessionId, espTimestamp: ts) { + throw StorageError.duplicate + } let configData: Data? = if let config { try JSONEncoder().encode(config) } else { @@ -140,7 +187,8 @@ final class Storage: @unchecked Sendable { sessionId: sessionId, type: type.rawValue, startedAt: Date(), - config: configData + config: configData, + espTimestamp: espTimestamp ) try m.insert(db) return m @@ -460,6 +508,7 @@ final class Storage: @unchecked Sendable { enum StorageError: Error { case notFound + case duplicate case parseError(String) }