Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 80 additions & 23 deletions MinimedKit/BasalSchedule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public struct BasalScheduleEntry {
public let timeOffset: TimeInterval
public let rate: Double // U/hour

internal init(index: Int, timeOffset: TimeInterval, rate: Double) {
public init(index: Int, timeOffset: TimeInterval, rate: Double) {
self.index = index
self.timeOffset = timeOffset
self.rate = rate
Expand All @@ -23,37 +23,94 @@ public struct BasalScheduleEntry {

public struct BasalSchedule {
public let entries: [BasalScheduleEntry]

public init(data: Data) {
let beginPattern = Data(bytes: [0, 0, 0])
let endPattern = Data(bytes: [0, 0, 0x3F])
var acc = [BasalScheduleEntry]()


public init(entries: [BasalScheduleEntry]) {
self.entries = entries
}
}


extension BasalSchedule {
static let rawValueLength = 192
public typealias RawValue = Data

public init?(rawValue: RawValue) {
var entries = [BasalScheduleEntry]()

for tuple in sequence(first: (index: 0, offset: 0), next: { (index: $0.index + 1, $0.offset + 3) }) {
let beginOfRange = tuple.offset
let endOfRange = beginOfRange + 3
guard endOfRange < data.count else {

guard endOfRange < rawValue.count else {
break
}

let section = data.subdata(in: beginOfRange..<endOfRange)
// sanity check
if (section == beginPattern || section == endPattern) {

if let entry = BasalScheduleEntry(
index: tuple.index,
rawValue: rawValue[beginOfRange..<endOfRange]
) {
if let last = entries.last, last.timeOffset >= entry.timeOffset {
// Stop if the new timeOffset isn't greater than the last one
break
}

entries.append(entry)
} else {
// Stop if we can't decode the entry
break
}
}

let rate = Double(section.subdata(in: 0..<2).to(UInt16.self)) / 40.0
let offsetMinutes = Double(section[2]) * 30

let newBasalScheduleEntry = BasalScheduleEntry(
index: tuple.index,
timeOffset: TimeInterval(minutes: offsetMinutes),
rate: rate
)
acc.append(newBasalScheduleEntry)
guard entries.count > 0 else {
return nil
}
self.entries = acc

self.init(entries: entries)
}

public var rawValue: RawValue {
var buffer = Data(count: BasalSchedule.rawValueLength)
var byteIndex = 0

for rawEntry in entries.map({ $0.rawValue }) {
buffer.replaceSubrange(byteIndex..<(byteIndex + rawEntry.count), with: rawEntry)
byteIndex += rawEntry.count
}

return buffer
}
}


private extension BasalScheduleEntry {
static let rawValueLength = 3
typealias RawValue = Data

init?(index: Int, rawValue: RawValue) {
guard rawValue.count == BasalScheduleEntry.rawValueLength else {
return nil
}

let rawRate = rawValue[rawValue.startIndex..<rawValue.startIndex.advanced(by: 2)]
let rate = Double(rawRate.to(UInt16.self)) / 40.0

let offsetMinutes = Double(rawValue.last!) * 30
let timeOffset = TimeInterval(minutes: offsetMinutes)

guard timeOffset < .hours(24) else {
return nil
}

self.init(index: index, timeOffset: timeOffset, rate: rate)
}

var rawValue: RawValue {
var buffer = Data(count: type(of: self).rawValueLength)

var rate = UInt16(clamping: Int(self.rate * 40))
buffer.replaceSubrange(0..<2, with: UnsafeBufferPointer(start: &rate, count: 1))
buffer[2] = UInt8(clamping: Int(timeOffset.minutes / 30))

return buffer
}
}
37 changes: 37 additions & 0 deletions MinimedKit/MessageType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,48 @@ public enum MessageType: UInt8 {
case writeGlucoseHistoryTimestamp = 0x28
case changeTime = 0x40
case bolus = 0x42

case PumpExperiment_OP67 = 0x43
case PumpExperiment_OP68 = 0x44
case PumpExperiment_OP69 = 0x45

case selectBasalProfile = 0x4a

case changeTempBasal = 0x4c

case PumpExperiment_OP80 = 0x50
case PumpExperiment_OP81 = 0x51
case PumpExperiment_OP82 = 0x52
case PumpExperiment_OP83 = 0x53
case PumpExperiment_OP84 = 0x54
case PumpExperiment_OP85 = 0x55
case PumpExperiment_OP86 = 0x56
case PumpExperiment_OP87 = 0x57
case PumpExperiment_OP88 = 0x58
case PumpExperiment_OP89 = 0x59
case PumpExperiment_OP90 = 0x5a

case buttonPress = 0x5b

case PumpExperiment_OP92 = 0x5c

case powerOn = 0x5d

case PumpExperiment_OP97 = 0x61
case PumpExperiment_OP98 = 0x62
case PumpExperiment_OP99 = 0x63
case PumpExperiment_O100 = 0x64
case PumpExperiment_O101 = 0x65
case PumpExperiment_O103 = 0x67

case readTime = 0x70
case getBattery = 0x72
case readRemainingInsulin = 0x73
case getHistoryPage = 0x80
case getPumpModel = 0x8d
case readProfileSTD512 = 0x92
case readProfileA512 = 0x93
case readProfileB512 = 0x94
case readTempBasal = 0x98
case getGlucosePage = 0x9A
case readCurrentPageNumber = 0x9d
Expand Down Expand Up @@ -61,6 +94,10 @@ public enum MessageType: UInt8 {
return GetPumpModelCarelinkMessageBody.self
case .readProfileSTD512:
return DataFrameMessageBody.self
case .readProfileA512:
return DataFrameMessageBody.self
case .readProfileB512:
return DataFrameMessageBody.self
case .getHistoryPage:
return GetHistoryPageCarelinkMessageBody.self
case .getBattery:
Expand Down
64 changes: 60 additions & 4 deletions MinimedKit/Messages/DataFrameMessageBody.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,71 @@

import Foundation

public class DataFrameMessageBody : CarelinkLongMessageBody {
public let lastFrameFlag: Bool
public class DataFrameMessageBody: CarelinkLongMessageBody {
public let isLastFrame: Bool
public let frameNumber: Int
public let contents: Data

public required init?(rxData: Data) {
self.lastFrameFlag = rxData[0] & 0x80 != 0
self.frameNumber = Int(rxData[0] & 0x7f)
guard rxData.count == type(of: self).length else {
return nil
}

self.isLastFrame = rxData[0] & 0b1000_0000 != 0
self.frameNumber = Int(rxData[0] & 0b0111_1111)
self.contents = rxData.subdata(in: (1..<rxData.count))

super.init(rxData: rxData)
}

init(frameNumber: Int, isLastFrame: Bool, contents: Data) {
self.frameNumber = frameNumber
self.isLastFrame = isLastFrame
self.contents = contents

super.init(rxData: Data())!
}

public override var txData: Data {
var byte0 = UInt8(frameNumber)
if isLastFrame {
byte0 |= 0b1000_0000
}

var data = Data(bytes: [byte0])
data.append(contents)

return data
}
}


extension DataFrameMessageBody {
public static func dataFramesFromContents(_ contents: Data) -> [DataFrameMessageBody] {
var frames = [DataFrameMessageBody]()
let frameContentsSize = DataFrameMessageBody.length - 1

for frameNumber in sequence(first: 0, next: { $0 + 1 }) {
let startIndex = frameNumber * frameContentsSize
var endIndex = startIndex + frameContentsSize
var isLastFrame = false

if endIndex >= contents.count {
isLastFrame = true
endIndex = contents.count
}

frames.append(DataFrameMessageBody(
frameNumber: frameNumber + 1,
isLastFrame: isLastFrame,
contents: contents[startIndex..<endIndex]
))

if isLastFrame {
break
}
}

return frames
}
}
12 changes: 12 additions & 0 deletions MinimedKit/Messages/ReadSettingsCarelinkMessageBody.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import Foundation


public enum BasalProfile {
typealias RawValue = UInt8

case standard
case profileA
Expand All @@ -25,6 +26,17 @@ public enum BasalProfile {
self = .standard
}
}

var rawValue: RawValue {
switch self {
case .standard:
return 0
case .profileA:
return 1
case .profileB:
return 2
}
}
}


Expand Down
14 changes: 14 additions & 0 deletions MinimedKit/Messages/SelectBasalProfileMessageBody.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
// SelectBasalProfileMessageBody.swift
// MinimedKit
//
// Copyright © 2017 Pete Schwamb. All rights reserved.
//

import Foundation

public class SelectBasalProfileMessageBody: CarelinkLongMessageBody {
public convenience init(newProfile: BasalProfile) {
self.init(rxData: Data(bytes: [1, newProfile.rawValue]))!
}
}
32 changes: 28 additions & 4 deletions MinimedKitTests/BasalScheduleTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@ import XCTest

class BasalScheduleTests: XCTestCase {

func testBasicConversion() {
var sampleData: Data {
let sampleDataString = "06000052000178050202000304000402000504000602000704000802000904000a02000b04000c02000d02000e02000f040010020011040012020013040014020015040016020017040018020019000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"

let rxData = Data(hexadecimalString: sampleDataString)!
let profile = BasalSchedule(data: rxData)

return Data(hexadecimalString: sampleDataString)!
}

func testBasicConversion() {
let profile = BasalSchedule(rawValue: sampleData)!

XCTAssertEqual(profile.entries.count, 26)

Expand All @@ -39,5 +42,26 @@ class BasalScheduleTests: XCTestCase {
XCTAssertEqual(basalSchedule[25].index, 25)
XCTAssertEqual(basalSchedule[25].timeOffset, TimeInterval(minutes: 750))
XCTAssertEqual(basalSchedule[25].rate, 0.05, accuracy: .ulpOfOne)

XCTAssertEqual(sampleData.hexadecimalString, profile.rawValue.hexadecimalString)
}

func testTxData() {
let profile = BasalSchedule(entries: [
BasalScheduleEntry(index: 0, timeOffset: .hours(0), rate: 1.0),
BasalScheduleEntry(index: 1, timeOffset: .hours(4), rate: 2.0),
])

XCTAssertEqual("280000500008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", profile.rawValue.hexadecimalString)
}

func testDataFrameParsing() {
let frames = DataFrameMessageBody.dataFramesFromContents(sampleData)

XCTAssertEqual("0106000052000178050202000304000402000504000602000704000802000904000a02000b04000c02000d02000e02000f04001002001104001202001304001402", frames[0].txData.hexadecimalString)
XCTAssertEqual("0200150400160200170400180200190000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", frames[1].txData.hexadecimalString)
XCTAssertEqual("8300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", frames[2].txData.hexadecimalString)

XCTAssertEqual(3, frames.count)
}
}
25 changes: 0 additions & 25 deletions NightscoutUploadKit/NSUserDefaults.swift

This file was deleted.

Loading