Skip to content

Commit

Permalink
Implement characteristic
Browse files Browse the repository at this point in the history
  • Loading branch information
Supereg committed Jun 10, 2024
1 parent 394331f commit 6310d71
Show file tree
Hide file tree
Showing 10 changed files with 393 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public struct RecordAccessOpCode {
/// The operator is ``RecordAccessOperator/null``.
/// The operand should include information similar to ``RecordAccessGeneralResponse`` (specifically containing an error code
/// of ``RecordAccessResponseCode``).
public static let responseCode = RecordAccessOpCode(rawValue: 0x06)
public static let responseCode = RecordAccessOpCode(rawValue: 0x06) // TODO: rename "response"?
/// Request a combined report.
///
/// After record transmission, the control point responds with the ``responseCode`` code.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,47 +11,36 @@ import NIOCore
import SpeziBluetooth


public struct UserControlPoint {
public struct OpCode {
public static let reserved = OpCode(rawValue: 0x00)
public static let registerNewUser = OpCode(rawValue: 0x01)
public static let consent = OpCode(rawValue: 0x02)
public static let deleteUserData = OpCode(rawValue: 0x03)
public static let listAllUsers = OpCode(rawValue: 0x04) // TODO: optional
public static let deleteUser = OpCode(rawValue: 0x05) // TODO: optional
public static let responseCode = OpCode(rawValue: 0x20)

public let rawValue: UInt8

public init(rawValue: UInt8) {
self.rawValue = rawValue
}
public struct UserControlPoint<Parameter: UserControlPointParameter> {
public var opCode: UserControlPointOpCode {
parameter.opCode
}

public let parameter: Parameter

public let opcode: OpCode
public let parameter: Any // TODO: what?
public init(_ parameter: Parameter) {
self.parameter = parameter
}
}


extension UserControlPoint.OpCode: RawRepresentable {}
extension UserControlPoint: Hashable, Sendable {}


extension UserControlPoint.OpCode: Hashable, Sendable {}
extension UserControlPoint: ControlPointCharacteristic {}


extension UserControlPoint.OpCode: ByteCodable {
extension UserControlPoint: ByteCodable {
public init?(from byteBuffer: inout ByteBuffer, preferredEndianness endianness: Endianness) {
guard let rawValue = UInt8(from: &byteBuffer, preferredEndianness: endianness) else {
guard let opCode = UserControlPointOpCode(from: &byteBuffer, preferredEndianness: endianness),
let parameter = Parameter(from: &byteBuffer, preferredEndianness: endianness, opCode: opCode) else {
return nil

Check warning on line 37 in Sources/BluetoothServices/Characteristics/UserData/UserControlPoint.swift

View check run for this annotation

Codecov / codecov/patch

Sources/BluetoothServices/Characteristics/UserData/UserControlPoint.swift#L37

Added line #L37 was not covered by tests
}
self.init(rawValue: rawValue)
}

self.init(parameter)
}
public func encode(to byteBuffer: inout ByteBuffer, preferredEndianness endianness: Endianness) {
rawValue.encode(to: &byteBuffer, preferredEndianness: endianness)
opCode.encode(to: &byteBuffer, preferredEndianness: endianness) // TODO: should we move encoding to Parameter? could avoid custom initializer!
parameter.encode(to: &byteBuffer, preferredEndianness: endianness)
}
}


extension UserControlPoint: ControlPointCharacteristic {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
//
// This source file is part of the Stanford Spezi open-source project
//
// SPDX-FileCopyrightText: 2024 Stanford University and the project authors (see CONTRIBUTORS.md)
//
// SPDX-License-Identifier: MIT
//

import ByteCoding
import NIOCore


// TODO: CharacteristicAccessor extensions?


public enum UserControlPointGenericParameter {
case registerNewUser(consentCode: UInt16)
case consent(userIndex: UInt8, consentCode: UInt16)
case deleteUserData
case listAllUsers
case deleterUser(userIndex: UInt8)
case response(
requestOpCode: UserControlPointOpCode,
response: UserControlPointResponse
)
}


extension UserControlPointGenericParameter: Hashable, Sendable {}


extension UserControlPointGenericParameter: UserControlPointParameter {
public var opCode: UserControlPointOpCode {
switch self {
case .registerNewUser:
return .registerNewUser
case .consent:
return .consent
case .deleteUserData:
return .deleteUserData
case .listAllUsers:
return .listAllUsers
case .deleterUser:
return .deleteUser
case .response:
return .response
}
}

public init?(from byteBuffer: inout ByteBuffer, preferredEndianness endianness: Endianness, opCode: UserControlPointOpCode) {
switch opCode {
case .registerNewUser:
guard let consentCode = UInt16(from: &byteBuffer, preferredEndianness: endianness) else {
return nil

Check warning on line 54 in Sources/BluetoothServices/Characteristics/UserData/UserControlPointGenericParameter.swift

View check run for this annotation

Codecov / codecov/patch

Sources/BluetoothServices/Characteristics/UserData/UserControlPointGenericParameter.swift#L54

Added line #L54 was not covered by tests
}
self = .registerNewUser(consentCode: consentCode)
case .consent:
guard let userIndex = UInt8(from: &byteBuffer, preferredEndianness: endianness),
let consentCode = UInt16(from: &byteBuffer, preferredEndianness: endianness) else {
return nil

Check warning on line 60 in Sources/BluetoothServices/Characteristics/UserData/UserControlPointGenericParameter.swift

View check run for this annotation

Codecov / codecov/patch

Sources/BluetoothServices/Characteristics/UserData/UserControlPointGenericParameter.swift#L60

Added line #L60 was not covered by tests
}
self = .consent(userIndex: userIndex, consentCode: consentCode)
case .deleteUserData:
self = .deleteUserData
case .listAllUsers:
self = .listAllUsers
case .deleteUser:
guard let userIndex = UInt8(from: &byteBuffer, preferredEndianness: endianness) else {
return nil

Check warning on line 69 in Sources/BluetoothServices/Characteristics/UserData/UserControlPointGenericParameter.swift

View check run for this annotation

Codecov / codecov/patch

Sources/BluetoothServices/Characteristics/UserData/UserControlPointGenericParameter.swift#L69

Added line #L69 was not covered by tests
}
self = .deleterUser(userIndex: userIndex)
case .response:
guard let requestOpCode = UserControlPointOpCode(from: &byteBuffer, preferredEndianness: endianness),
let response = UserControlPointResponse(from: &byteBuffer, preferredEndianness: endianness, requestOpCode: requestOpCode) else {
return nil

Check warning on line 75 in Sources/BluetoothServices/Characteristics/UserData/UserControlPointGenericParameter.swift

View check run for this annotation

Codecov / codecov/patch

Sources/BluetoothServices/Characteristics/UserData/UserControlPointGenericParameter.swift#L75

Added line #L75 was not covered by tests
}
self = .response(requestOpCode: requestOpCode, response: response)
default:
return nil

Check warning on line 79 in Sources/BluetoothServices/Characteristics/UserData/UserControlPointGenericParameter.swift

View check run for this annotation

Codecov / codecov/patch

Sources/BluetoothServices/Characteristics/UserData/UserControlPointGenericParameter.swift#L79

Added line #L79 was not covered by tests
}
}

public func encode(to byteBuffer: inout ByteBuffer, preferredEndianness endianness: Endianness) {
switch self {
case let .registerNewUser(consentCode):
consentCode.encode(to: &byteBuffer, preferredEndianness: endianness)
case let .consent(userIndex, consentCode):
userIndex.encode(to: &byteBuffer, preferredEndianness: endianness)
consentCode.encode(to: &byteBuffer, preferredEndianness: endianness)
case .deleteUserData:
break
case .listAllUsers:
break
case let .deleterUser(userIndex):
userIndex.encode(to: &byteBuffer, preferredEndianness: endianness)
case let .response(requestOpCode, response):
requestOpCode.encode(to: &byteBuffer, preferredEndianness: endianness)
response.encode(to: &byteBuffer, preferredEndianness: endianness)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//
// This source file is part of the Stanford Spezi open-source project
//
// SPDX-FileCopyrightText: 2024 Stanford University and the project authors (see CONTRIBUTORS.md)
//
// SPDX-License-Identifier: MIT
//

import ByteCoding
import NIOCore


public struct UserControlPointOpCode {
public static let reserved = UserControlPointOpCode(rawValue: 0x00)
public static let registerNewUser = UserControlPointOpCode(rawValue: 0x01)
public static let consent = UserControlPointOpCode(rawValue: 0x02)
public static let deleteUserData = UserControlPointOpCode(rawValue: 0x03)
public static let listAllUsers = UserControlPointOpCode(rawValue: 0x04) // TODO: optional
public static let deleteUser = UserControlPointOpCode(rawValue: 0x05) // TODO: optional
public static let response = UserControlPointOpCode(rawValue: 0x20)

public let rawValue: UInt8

public init(rawValue: UInt8) {
self.rawValue = rawValue
}
}


extension UserControlPointOpCode: RawRepresentable {}


extension UserControlPointOpCode: Hashable, Sendable {}


extension UserControlPointOpCode: ByteCodable {
public init?(from byteBuffer: inout ByteBuffer, preferredEndianness endianness: Endianness) {
guard let rawValue = UInt8(from: &byteBuffer, preferredEndianness: endianness) else {
return nil

Check warning on line 39 in Sources/BluetoothServices/Characteristics/UserData/UserControlPointOpCode.swift

View check run for this annotation

Codecov / codecov/patch

Sources/BluetoothServices/Characteristics/UserData/UserControlPointOpCode.swift#L39

Added line #L39 was not covered by tests
}
self.init(rawValue: rawValue)
}

public func encode(to byteBuffer: inout ByteBuffer, preferredEndianness endianness: Endianness) {
rawValue.encode(to: &byteBuffer, preferredEndianness: endianness)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// This source file is part of the Stanford Spezi open-source project
//
// SPDX-FileCopyrightText: 2024 Stanford University and the project authors (see CONTRIBUTORS.md)
//
// SPDX-License-Identifier: MIT
//

import ByteCoding
import NIOCore


public protocol UserControlPointParameter: ByteEncodable, Hashable, Sendable {
var opCode: UserControlPointOpCode { get }


init?(from byteBuffer: inout ByteBuffer, preferredEndianness endianness: Endianness, opCode: UserControlPointOpCode)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
//
// This source file is part of the Stanford Spezi open-source project
//
// SPDX-FileCopyrightText: 2024 Stanford University and the project authors (see CONTRIBUTORS.md)
//
// SPDX-License-Identifier: MIT
//

import ByteCoding
import NIOCore


public enum UserControlPointResponse {
case success(UserControlPointResponseParameter? = nil)
case opCodeNotSupported
case invalidParameter
case operationFailed
case userNotAuthorized


public var responseValue: UserControlPointResponseValue {
switch self {
case .success:
return .success
case .opCodeNotSupported:
return .opCodeNotSupported
case .invalidParameter:
return .invalidParameter
case .operationFailed:
return .operationFailed
case .userNotAuthorized:
return .userNotAuthorized
}
}
}


extension UserControlPointResponse: Hashable, Sendable {}


extension UserControlPointResponse: ByteEncodable {
public init?(from byteBuffer: inout ByteBuffer, preferredEndianness endianness: Endianness, requestOpCode: UserControlPointOpCode) {
guard let responseValue = UserControlPointResponseValue(from: &byteBuffer, preferredEndianness: endianness) else {
return nil

Check warning on line 44 in Sources/BluetoothServices/Characteristics/UserData/UserControlPointResponse.swift

View check run for this annotation

Codecov / codecov/patch

Sources/BluetoothServices/Characteristics/UserData/UserControlPointResponse.swift#L44

Added line #L44 was not covered by tests
}

switch responseValue {
case .success:
let parameter: UserControlPointResponseParameter?
switch requestOpCode {
case .registerNewUser, .deleteUser:
guard let userIndex = UInt8(from: &byteBuffer, preferredEndianness: endianness) else {
return nil

Check warning on line 53 in Sources/BluetoothServices/Characteristics/UserData/UserControlPointResponse.swift

View check run for this annotation

Codecov / codecov/patch

Sources/BluetoothServices/Characteristics/UserData/UserControlPointResponse.swift#L53

Added line #L53 was not covered by tests
}
parameter = .userIndex(userIndex)
case .listAllUsers:
guard let numberOfUsers = UInt8(from: &byteBuffer, preferredEndianness: endianness) else {
return nil

Check warning on line 58 in Sources/BluetoothServices/Characteristics/UserData/UserControlPointResponse.swift

View check run for this annotation

Codecov / codecov/patch

Sources/BluetoothServices/Characteristics/UserData/UserControlPointResponse.swift#L58

Added line #L58 was not covered by tests
}
parameter = .numberOfUsers(numberOfUsers)
default:
parameter = nil
}

self = .success(parameter)
case .opCodeNotSupported:
self = .opCodeNotSupported
case .invalidParameter:
self = .invalidParameter
case .operationFailed:
self = .operationFailed
case .userNotAuthorized:
self = .userNotAuthorized
default:
return nil

Check warning on line 75 in Sources/BluetoothServices/Characteristics/UserData/UserControlPointResponse.swift

View check run for this annotation

Codecov / codecov/patch

Sources/BluetoothServices/Characteristics/UserData/UserControlPointResponse.swift#L75

Added line #L75 was not covered by tests
}
}

public func encode(to byteBuffer: inout ByteBuffer, preferredEndianness endianness: Endianness) {
responseValue.encode(to: &byteBuffer, preferredEndianness: endianness)
switch self {
case let .success(parameter):
if let parameter {
parameter.rawValue.encode(to: &byteBuffer, preferredEndianness: endianness)
}
default:
break
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//
// This source file is part of the Stanford Spezi open-source project
//
// SPDX-FileCopyrightText: 2024 Stanford University and the project authors (see CONTRIBUTORS.md)
//
// SPDX-License-Identifier: MIT
//

import ByteCoding
import NIOCore


public enum UserControlPointResponseParameter {
case userIndex(UInt8)
case numberOfUsers(UInt8)


public var rawValue: UInt8 {
switch self {
case let .userIndex(value):
return value
case let .numberOfUsers(value):
return value
}
}
}


extension UserControlPointResponseParameter: Hashable, Sendable {}
Loading

0 comments on commit 6310d71

Please sign in to comment.