From 0ee52416780f8789e04db37c47902c975ddba9c9 Mon Sep 17 00:00:00 2001 From: Travis Wyatt Date: Wed, 26 Jul 2023 01:34:47 -0700 Subject: [PATCH 1/3] Provide service, characteristic and/or descriptor UUIDs to data processor --- README.md | 2 +- core/api/core.api | 2 +- core/src/commonMain/kotlin/logs/Hex.kt | 2 +- core/src/commonMain/kotlin/logs/LogMessage.kt | 110 ++++---- core/src/commonMain/kotlin/logs/Logger.kt | 24 +- core/src/commonMain/kotlin/logs/Logging.kt | 9 +- core/src/commonTest/kotlin/LogMessageTests.kt | 236 ++++++++++++++++++ 7 files changed, 323 insertions(+), 62 deletions(-) create mode 100644 core/src/commonTest/kotlin/LogMessageTests.kt diff --git a/README.md b/README.md index d5c9ef39c..d85004b06 100644 --- a/README.md +++ b/README.md @@ -174,7 +174,7 @@ val peripheral = scope.peripheral(advertisement) { // or... - data = DataProcessor { bytes -> + data = DataProcessor { bytes, _, _, _ -> // todo: Convert `bytes` to desired String representation, for example: bytes.joinToString { byte -> byte.toString() } // Show data as integer representation of bytes. } diff --git a/core/api/core.api b/core/api/core.api index 8a7f45239..048233cab 100644 --- a/core/api/core.api +++ b/core/api/core.api @@ -531,7 +531,7 @@ public final class com/juul/kable/logs/Logging { } public abstract interface class com/juul/kable/logs/Logging$DataProcessor { - public abstract fun process ([B)Ljava/lang/String; + public abstract fun process ([BLjava/util/UUID;Ljava/util/UUID;Ljava/util/UUID;)Ljava/lang/String; } public final class com/juul/kable/logs/Logging$Format : java/lang/Enum { diff --git a/core/src/commonMain/kotlin/logs/Hex.kt b/core/src/commonMain/kotlin/logs/Hex.kt index 07b04fc8d..d6df676d3 100644 --- a/core/src/commonMain/kotlin/logs/Hex.kt +++ b/core/src/commonMain/kotlin/logs/Hex.kt @@ -15,7 +15,7 @@ public class HexBuilder internal constructor() { public fun Hex(init: HexBuilder.() -> Unit = {}): Logging.DataProcessor { val config = HexBuilder().apply(init) - return Logging.DataProcessor { data -> + return Logging.DataProcessor { data, _, _, _ -> data.toHexString(separator = config.separator, lowerCase = config.lowerCase) } } diff --git a/core/src/commonMain/kotlin/logs/LogMessage.kt b/core/src/commonMain/kotlin/logs/LogMessage.kt index 1a2f92d46..93e6079cb 100644 --- a/core/src/commonMain/kotlin/logs/LogMessage.kt +++ b/core/src/commonMain/kotlin/logs/LogMessage.kt @@ -5,20 +5,45 @@ import com.juul.kable.Characteristic import com.juul.kable.Descriptor import com.juul.kable.Service import com.juul.kable.WriteType +import com.juul.kable.logs.Logging.Format.Compact +import com.juul.kable.logs.Logging.Format.Multiline internal expect val LOG_INDENT: String? -internal class LogMessage { +internal class LogMessage( + private val logging: Logging, + platformIdentifier: String?, + private val indent: String? = LOG_INDENT, +) { - var message: String = "" + private val prefix = logging.identifier ?: platformIdentifier + var message: String = "" + private var serviceUuid: Uuid? = null + private var characteristicUuid: Uuid? = null + private var descriptorUuid: Uuid? = null private val details = mutableListOf>() + var data: ByteArray? = null - fun detail(key: String, value: String) { - details += key to value + fun detail(service: Service) { + serviceUuid = service.serviceUuid + characteristicUuid = null + descriptorUuid = null + } + + fun detail(characteristic: Characteristic) { + serviceUuid = characteristic.serviceUuid + characteristicUuid = characteristic.characteristicUuid + descriptorUuid = null + } + + fun detail(descriptor: Descriptor) { + serviceUuid = descriptor.serviceUuid + characteristicUuid = descriptor.characteristicUuid + descriptorUuid = descriptor.descriptorUuid } - fun detail(key: String, value: ByteArray) { + fun detail(key: String, value: String) { details += key to value } @@ -30,62 +55,55 @@ internal class LogMessage { detail(key, value.toString()) } - fun build(logging: Logging, platformIdentifier: String?): String = buildString { - val prefix = logging.identifier ?: platformIdentifier + private var isFirst = true + + private fun StringBuilder.append(label: String, value: Any) { + when (logging.format) { + Compact -> if (isFirst) append('(') else append(", ") + Multiline -> { + appendLine() + if (indent != null) append(indent) + } + } + isFirst = false + + append(label) + when (logging.format) { + Compact -> append("=") + Multiline -> append(": ") + } + append(value) + } + + fun build(): String = buildString { if (!prefix.isNullOrEmpty()) { append(prefix) append(' ') } append(message) - when (logging.format) { - Logging.Format.Compact -> if (details.isNotEmpty()) append('(') - Logging.Format.Multiline -> appendLine() + isFirst = true + + serviceUuid?.let { append("service", it) } + characteristicUuid?.let { append("characteristic", it) } + descriptorUuid?.let { append("descriptor", it) } + + details.forEach { (key, value) -> + append(key, value) } - details.forEachIndexed { index, detail -> - val (key, value) = detail - - if (value !is ByteArray || logging.level == Logging.Level.Data) { - if (index > 0) { - when (logging.format) { - Logging.Format.Compact -> append(", ") - Logging.Format.Multiline -> appendLine() - } - } - - if (logging.format == Logging.Format.Multiline && LOG_INDENT != null) append(LOG_INDENT) - - append(key) - when (logging.format) { - Logging.Format.Compact -> append("=") - Logging.Format.Multiline -> append(": ") - } - if (value is ByteArray) append(logging.data.process(value)) else append(value) + if (logging.level == Logging.Level.Data) { + data?.let { + append("data", logging.data.process(it, serviceUuid, characteristicUuid, descriptorUuid)) } } - if (logging.format == Logging.Format.Compact && details.isNotEmpty()) append(')') + if (logging.format == Compact && !isFirst) append(')') } } internal fun LogMessage.detail(data: ByteArray?) { - if (data != null) detail("data", data) -} - -internal fun LogMessage.detail(service: Service) { - detail("service", service.serviceUuid) -} - -internal fun LogMessage.detail(characteristic: Characteristic) { - detail("service", characteristic.serviceUuid) - detail("characteristic", characteristic.characteristicUuid) -} - -internal fun LogMessage.detail(descriptor: Descriptor) { - detail("service", descriptor.serviceUuid) - detail("characteristic", descriptor.characteristicUuid) - detail("descriptor", descriptor.descriptorUuid) + if (data != null) this@detail.data = data } internal fun LogMessage.detail(writeType: WriteType) { diff --git a/core/src/commonMain/kotlin/logs/Logger.kt b/core/src/commonMain/kotlin/logs/Logger.kt index fe89a6512..7741709b0 100644 --- a/core/src/commonMain/kotlin/logs/Logger.kt +++ b/core/src/commonMain/kotlin/logs/Logger.kt @@ -11,43 +11,43 @@ internal class Logger( inline fun verbose(throwable: Throwable? = null, crossinline init: LogMessage.() -> Unit) { if (logging.level == Events || logging.level == Data) { - val message = LogMessage() + val message = LogMessage(logging, identifier) message.init() - logging.engine.verbose(throwable, tag, message.build(logging, identifier)) + logging.engine.verbose(throwable, tag, message.build()) } } inline fun debug(throwable: Throwable? = null, crossinline init: LogMessage.() -> Unit) { if (logging.level == Events || logging.level == Data) { - val message = LogMessage() + val message = LogMessage(logging, identifier) message.init() - logging.engine.debug(throwable, tag, message.build(logging, identifier)) + logging.engine.debug(throwable, tag, message.build()) } } inline fun info(throwable: Throwable? = null, crossinline init: LogMessage.() -> Unit) { if (logging.level == Events || logging.level == Data) { - val message = LogMessage() + val message = LogMessage(logging, identifier) message.init() - logging.engine.info(throwable, tag, message.build(logging, identifier)) + logging.engine.info(throwable, tag, message.build()) } } inline fun warn(throwable: Throwable? = null, crossinline init: LogMessage.() -> Unit) { - val message = LogMessage() + val message = LogMessage(logging, identifier) message.init() - logging.engine.warn(throwable, tag, message.build(logging, identifier)) + logging.engine.warn(throwable, tag, message.build()) } inline fun error(throwable: Throwable? = null, crossinline init: LogMessage.() -> Unit) { - val message = LogMessage() + val message = LogMessage(logging, identifier) message.init() - logging.engine.error(throwable, tag, message.build(logging, identifier)) + logging.engine.error(throwable, tag, message.build()) } inline fun assert(throwable: Throwable? = null, crossinline init: LogMessage.() -> Unit) { - val message = LogMessage() + val message = LogMessage(logging, identifier) message.init() - logging.engine.assert(throwable, tag, message.build(logging, identifier)) + logging.engine.assert(throwable, tag, message.build()) } } diff --git a/core/src/commonMain/kotlin/logs/Logging.kt b/core/src/commonMain/kotlin/logs/Logging.kt index 8462ef59f..19c7d762f 100644 --- a/core/src/commonMain/kotlin/logs/Logging.kt +++ b/core/src/commonMain/kotlin/logs/Logging.kt @@ -1,5 +1,7 @@ package com.juul.kable.logs +import com.benasher44.uuid.Uuid + internal typealias LoggingBuilder = Logging.() -> Unit public class Logging { @@ -41,7 +43,12 @@ public class Logging { } public fun interface DataProcessor { - public fun process(data: ByteArray): String + public fun process( + data: ByteArray, + serviceUuid: Uuid?, + characteristicUuid: Uuid?, + descriptorUuid: Uuid?, + ): String } /** diff --git a/core/src/commonTest/kotlin/LogMessageTests.kt b/core/src/commonTest/kotlin/LogMessageTests.kt new file mode 100644 index 000000000..c0d05a72c --- /dev/null +++ b/core/src/commonTest/kotlin/LogMessageTests.kt @@ -0,0 +1,236 @@ +package com.juul.kable + +import com.benasher44.uuid.uuidFrom +import com.juul.kable.logs.LogMessage +import com.juul.kable.logs.Logging +import com.juul.kable.logs.Logging.Format.Compact +import com.juul.kable.logs.Logging.Format.Multiline +import com.juul.kable.logs.Logging.Level.Data +import com.juul.kable.logs.detail +import kotlin.test.Test +import kotlin.test.assertEquals + +private val TEST_UUID_1 = uuidFrom("ad0be000-0000-4000-a000-000000000000") +private val TEST_UUID_2 = uuidFrom("c0c0a000-0000-4000-a000-000000000000") +private val TEST_UUID_3 = uuidFrom("decade00-0000-4000-a000-000000000000") + +class LogMessageTests { + + @Test + fun loggingIdentifierTakesPrecedenceOverPlatformIdentifier() { + val logging = Logging().apply { + identifier = "logging" + format = Compact + } + val log = LogMessage(logging, platformIdentifier = "platform").apply { + message = "Hello, world!" + } + assertEquals( + expected = "logging Hello, world!", + actual = log.build(), + ) + } + + @Test + fun compactFormat_onlyMessage() { + val logging = Logging().apply { + format = Compact + } + val log = LogMessage(logging, platformIdentifier = "example").apply { + message = "Hello, world!" + } + assertEquals( + expected = "example Hello, world!", + actual = log.build(), + ) + } + + @Test + fun multilineFormat_onlyMessage() { + val logging = Logging().apply { + format = Multiline + } + val log = LogMessage(logging, platformIdentifier = "example").apply { + message = "Hello, world!" + } + assertEquals( + expected = "example Hello, world!", + actual = log.build(), + ) + } + + @Test + fun compactFormat_messageAndService() { + val logging = Logging().apply { + format = Compact + } + val log = LogMessage(logging, platformIdentifier = "example").apply { + message = "Compact" + detail("service", TEST_UUID_1) + } + assertEquals( + expected = "example Compact(service=$TEST_UUID_1)", + actual = log.build(), + ) + } + + @Test + fun multilineFormat_messageAndService() { + val logging = Logging().apply { + format = Multiline + } + val log = LogMessage(logging, platformIdentifier = "example", indent = " ").apply { + message = "test" + detail("service", TEST_UUID_1) + } + assertEquals( + expected = """ + example test + service: $TEST_UUID_1 + """.trimIndent(), + actual = log.build(), + ) + } + + @Test + fun compactFormat_messageAndCharacteristic() { + val logging = Logging().apply { + format = Compact + } + val log = LogMessage(logging, platformIdentifier = "example").apply { + message = "Compact" + detail("service", TEST_UUID_1) + detail("characteristic", TEST_UUID_2) + } + assertEquals( + expected = "example Compact(service=$TEST_UUID_1, characteristic=$TEST_UUID_2)", + actual = log.build(), + ) + } + + @Test + fun multilineFormat_messageAndCharacteristic() { + val logging = Logging().apply { + format = Multiline + } + val log = LogMessage(logging, platformIdentifier = "example", indent = " ").apply { + message = "Hello, world!" + detail("service", TEST_UUID_1) + detail("characteristic", TEST_UUID_2) + } + assertEquals( + expected = """ + example Hello, world! + service: $TEST_UUID_1 + characteristic: $TEST_UUID_2 + """.trimIndent(), + actual = log.build(), + ) + } + + @Test + fun compactFormat_messageAndDescriptor() { + val logging = Logging().apply { + format = Compact + } + val log = LogMessage(logging, platformIdentifier = "example").apply { + message = "Compact" + detail("service", TEST_UUID_1) + detail("characteristic", TEST_UUID_2) + detail("descriptor", TEST_UUID_3) + } + assertEquals( + expected = "example Compact(service=$TEST_UUID_1, characteristic=$TEST_UUID_2, descriptor=$TEST_UUID_3)", + actual = log.build(), + ) + } + + @Test + fun multilineFormat_messageAndDescriptor() { + val logging = Logging().apply { + format = Multiline + } + val log = LogMessage(logging, platformIdentifier = "example", indent = " ").apply { + message = "Hello, world!" + detail("service", TEST_UUID_1) + detail("characteristic", TEST_UUID_2) + detail("descriptor", TEST_UUID_3) + } + assertEquals( + expected = """ + example Hello, world! + service: $TEST_UUID_1 + characteristic: $TEST_UUID_2 + descriptor: $TEST_UUID_3 + """.trimIndent(), + actual = log.build(), + ) + } + + @Test + fun compactFormat_messageServiceAndNumericDetail() { + val logging = Logging().apply { + format = Compact + } + val log = LogMessage(logging, platformIdentifier = "example").apply { + message = "Compact" + detail("service", TEST_UUID_1) + detail("extra", 1) + } + assertEquals( + expected = "example Compact(service=$TEST_UUID_1, extra=1)", + actual = log.build(), + ) + } + + @Test + fun multilineFormat_messageServiceAndNumericDetail() { + val logging = Logging().apply { + format = Multiline + } + val log = LogMessage(logging, platformIdentifier = "example", indent = " ").apply { + message = "Hello, world!" + detail("service", TEST_UUID_1) + detail("extra", 1) + } + assertEquals( + expected = """ + example Hello, world! + service: $TEST_UUID_1 + extra: 1 + """.trimIndent(), + actual = log.build(), + ) + } + + @Test + fun compactFormatAndDefaultLogLevel_doesNotLogData() { + val logging = Logging().apply { + format = Compact + } + val log = LogMessage(logging, platformIdentifier = "example").apply { + message = "Compact" + detail(byteArrayOf(1, 2)) + } + assertEquals( + expected = "example Compact", + actual = log.build(), + ) + } + + @Test + fun compactFormatAndDataLogLevel_logsData() { + val logging = Logging().apply { + format = Compact + level = Data + } + val log = LogMessage(logging, platformIdentifier = "example").apply { + message = "Compact" + detail(byteArrayOf(1, 2)) + } + assertEquals( + expected = "example Compact(data=01 02)", + actual = log.build(), + ) + } +} From 8afa1f5656dfe445a45a9b818a3c7c0156176e95 Mon Sep 17 00:00:00 2001 From: Travis Wyatt Date: Wed, 26 Jul 2023 10:59:45 -0700 Subject: [PATCH 2/3] Provide operation to data processor --- README.md | 2 +- core/api/core.api | 11 +++++- .../BluetoothDeviceAndroidPeripheral.kt | 5 ++- core/src/androidMain/kotlin/gatt/Callback.kt | 8 ++-- .../src/androidMain/kotlin/logs/LogMessage.kt | 18 ++++----- .../CBPeripheralCoreBluetoothPeripheral.kt | 5 ++- .../appleMain/kotlin/PeripheralDelegate.kt | 3 +- core/src/appleMain/kotlin/logs/LogMessage.kt | 19 +++++++--- core/src/commonMain/kotlin/logs/Hex.kt | 2 +- core/src/commonMain/kotlin/logs/LogMessage.kt | 38 ++++++++++--------- core/src/commonMain/kotlin/logs/Logging.kt | 4 ++ core/src/commonTest/kotlin/LogMessageTests.kt | 6 +-- .../BluetoothDeviceWebBluetoothPeripheral.kt | 11 +++--- core/src/jsMain/kotlin/logs/LogMessage.kt | 13 ++----- 14 files changed, 85 insertions(+), 60 deletions(-) diff --git a/README.md b/README.md index d85004b06..8de1876cb 100644 --- a/README.md +++ b/README.md @@ -174,7 +174,7 @@ val peripheral = scope.peripheral(advertisement) { // or... - data = DataProcessor { bytes, _, _, _ -> + data = DataProcessor { bytes, _, _, _, _ -> // todo: Convert `bytes` to desired String representation, for example: bytes.joinToString { byte -> byte.toString() } // Show data as integer representation of bytes. } diff --git a/core/api/core.api b/core/api/core.api index 048233cab..7259845e2 100644 --- a/core/api/core.api +++ b/core/api/core.api @@ -531,7 +531,16 @@ public final class com/juul/kable/logs/Logging { } public abstract interface class com/juul/kable/logs/Logging$DataProcessor { - public abstract fun process ([BLjava/util/UUID;Ljava/util/UUID;Ljava/util/UUID;)Ljava/lang/String; + public abstract fun process ([BLcom/juul/kable/logs/Logging$DataProcessor$Operation;Ljava/util/UUID;Ljava/util/UUID;Ljava/util/UUID;)Ljava/lang/String; +} + +public final class com/juul/kable/logs/Logging$DataProcessor$Operation : java/lang/Enum { + public static final field Change Lcom/juul/kable/logs/Logging$DataProcessor$Operation; + public static final field Read Lcom/juul/kable/logs/Logging$DataProcessor$Operation; + public static final field Write Lcom/juul/kable/logs/Logging$DataProcessor$Operation; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lcom/juul/kable/logs/Logging$DataProcessor$Operation; + public static fun values ()[Lcom/juul/kable/logs/Logging$DataProcessor$Operation; } public final class com/juul/kable/logs/Logging$Format : java/lang/Enum { diff --git a/core/src/androidMain/kotlin/BluetoothDeviceAndroidPeripheral.kt b/core/src/androidMain/kotlin/BluetoothDeviceAndroidPeripheral.kt index 68e4a2592..483fbb557 100644 --- a/core/src/androidMain/kotlin/BluetoothDeviceAndroidPeripheral.kt +++ b/core/src/androidMain/kotlin/BluetoothDeviceAndroidPeripheral.kt @@ -30,6 +30,7 @@ import com.juul.kable.gatt.Response.OnReadRemoteRssi import com.juul.kable.gatt.Response.OnServicesDiscovered import com.juul.kable.logs.Logger import com.juul.kable.logs.Logging +import com.juul.kable.logs.Logging.DataProcessor.Operation import com.juul.kable.logs.detail import kotlinx.atomicfu.atomic import kotlinx.atomicfu.updateAndGet @@ -230,7 +231,7 @@ internal class BluetoothDeviceAndroidPeripheral( message = "write" detail(characteristic) detail(writeType) - detail(data) + detail(data, Operation.Write) } val platformCharacteristic = discoveredServices.obtain(characteristic, writeType.properties) @@ -269,7 +270,7 @@ internal class BluetoothDeviceAndroidPeripheral( logger.debug { message = "write" detail(platformDescriptor) - detail(data) + detail(data, Operation.Write) } connection.execute { diff --git a/core/src/androidMain/kotlin/gatt/Callback.kt b/core/src/androidMain/kotlin/gatt/Callback.kt index a3f55d792..9a7863541 100644 --- a/core/src/androidMain/kotlin/gatt/Callback.kt +++ b/core/src/androidMain/kotlin/gatt/Callback.kt @@ -38,6 +38,8 @@ import com.juul.kable.gatt.Response.OnReadRemoteRssi import com.juul.kable.gatt.Response.OnServicesDiscovered import com.juul.kable.logs.Logger import com.juul.kable.logs.Logging +import com.juul.kable.logs.Logging.DataProcessor.Operation.Change +import com.juul.kable.logs.Logging.DataProcessor.Operation.Read import com.juul.kable.logs.detail import com.juul.kable.toLazyCharacteristic import kotlinx.coroutines.channels.Channel @@ -156,7 +158,7 @@ internal class Callback( message = "onCharacteristicRead" detail(characteristic) detail(event.status) - detail(value) + detail(value, Read) } onResponse.trySendOrLog(event) } @@ -193,7 +195,7 @@ internal class Callback( logger.debug { message = "onCharacteristicChanged" detail(characteristic) - detail(value) + detail(value, Change) } val event = CharacteristicChange(characteristic.toLazyCharacteristic(), value) onCharacteristicChanged.tryEmitOrLog(event) @@ -221,7 +223,7 @@ internal class Callback( message = "onDescriptorRead" detail(descriptor) detail(event.status) - detail(value) + detail(value, Read) } onResponse.trySendOrLog(event) } diff --git a/core/src/androidMain/kotlin/logs/LogMessage.kt b/core/src/androidMain/kotlin/logs/LogMessage.kt index 8a1f9037c..a412244ce 100644 --- a/core/src/androidMain/kotlin/logs/LogMessage.kt +++ b/core/src/androidMain/kotlin/logs/LogMessage.kt @@ -4,7 +4,6 @@ package com.juul.kable.logs import android.bluetooth.BluetoothGattCharacteristic import android.bluetooth.BluetoothGattDescriptor -import android.bluetooth.BluetoothGattService import com.juul.kable.gatt.GattStatus internal actual val LOG_INDENT: String? = null @@ -13,16 +12,17 @@ internal fun LogMessage.detail(status: GattStatus) { detail("status", status.toString()) } -internal fun LogMessage.detail(service: BluetoothGattService) { - detail("service", service.uuid.toString()) -} - internal fun LogMessage.detail(characteristic: BluetoothGattCharacteristic) { - detail(characteristic.service) - detail("characteristic", characteristic.uuid.toString()) + detail( + characteristic.service.uuid, + characteristic.uuid, + ) } internal fun LogMessage.detail(descriptor: BluetoothGattDescriptor) { - detail(descriptor.characteristic) - detail("descriptor", descriptor.uuid.toString()) + detail( + descriptor.characteristic.service.uuid, + descriptor.characteristic.uuid, + descriptor.uuid, + ) } diff --git a/core/src/appleMain/kotlin/CBPeripheralCoreBluetoothPeripheral.kt b/core/src/appleMain/kotlin/CBPeripheralCoreBluetoothPeripheral.kt index 517ce53a9..a6a7e9eec 100644 --- a/core/src/appleMain/kotlin/CBPeripheralCoreBluetoothPeripheral.kt +++ b/core/src/appleMain/kotlin/CBPeripheralCoreBluetoothPeripheral.kt @@ -23,6 +23,7 @@ import com.juul.kable.WriteType.WithResponse import com.juul.kable.WriteType.WithoutResponse import com.juul.kable.logs.Logger import com.juul.kable.logs.Logging +import com.juul.kable.logs.Logging.DataProcessor.Operation.Write import com.juul.kable.logs.detail import kotlinx.atomicfu.atomic import kotlinx.coroutines.CoroutineName @@ -240,7 +241,7 @@ internal class CBPeripheralCoreBluetoothPeripheral( message = "write" detail(characteristic) detail(writeType) - detail(data) + detail(data, Write) } val platformCharacteristic = discoveredServices.obtain(characteristic, writeType.properties) @@ -301,7 +302,7 @@ internal class CBPeripheralCoreBluetoothPeripheral( logger.debug { message = "write" detail(descriptor) - detail(data) + detail(data, Write) } val platformDescriptor = discoveredServices.obtain(descriptor) diff --git a/core/src/appleMain/kotlin/PeripheralDelegate.kt b/core/src/appleMain/kotlin/PeripheralDelegate.kt index a89f26845..ad7827288 100644 --- a/core/src/appleMain/kotlin/PeripheralDelegate.kt +++ b/core/src/appleMain/kotlin/PeripheralDelegate.kt @@ -8,6 +8,7 @@ import com.juul.kable.PeripheralDelegate.Response.DidWriteValueForCharacteristic import com.juul.kable.logs.LogMessage import com.juul.kable.logs.Logger import com.juul.kable.logs.Logging +import com.juul.kable.logs.Logging.DataProcessor.Operation.Change import com.juul.kable.logs.detail import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel.Factory.BUFFERED @@ -155,7 +156,7 @@ internal class PeripheralDelegate( logger.debug(error) { message = "didUpdateValueForCharacteristic" detail(didUpdateValueForCharacteristic) - detail(didUpdateValueForCharacteristic.value) + detail(didUpdateValueForCharacteristic.value, Change) } val characteristic = didUpdateValueForCharacteristic.toLazyCharacteristic() diff --git a/core/src/appleMain/kotlin/logs/LogMessage.kt b/core/src/appleMain/kotlin/logs/LogMessage.kt index 63a8a782c..ade9b7073 100644 --- a/core/src/appleMain/kotlin/logs/LogMessage.kt +++ b/core/src/appleMain/kotlin/logs/LogMessage.kt @@ -1,6 +1,8 @@ package com.juul.kable.logs +import com.juul.kable.logs.Logging.DataProcessor.Operation import com.juul.kable.toByteArray +import com.juul.kable.toUuid import platform.CoreBluetooth.CBCharacteristic import platform.CoreBluetooth.CBDescriptor import platform.CoreBluetooth.CBService @@ -9,8 +11,8 @@ import platform.Foundation.NSError internal actual val LOG_INDENT: String? = " " -internal fun LogMessage.detail(data: NSData?) { - if (data != null) detail(data.toByteArray()) +internal fun LogMessage.detail(data: NSData?, operation: Operation) { + detail(data?.toByteArray(), operation) } internal fun LogMessage.detail(error: NSError?) { @@ -22,11 +24,16 @@ internal fun LogMessage.detail(service: CBService) { } internal fun LogMessage.detail(characteristic: CBCharacteristic) { - detail(characteristic.service!!) - detail("characteristic", characteristic.UUID.UUIDString) + detail( + characteristic.service!!.UUID.toUuid(), + characteristic.UUID.toUuid(), + ) } internal fun LogMessage.detail(descriptor: CBDescriptor) { - detail(descriptor.characteristic!!) - detail("descriptor", descriptor.UUID.UUIDString) + detail( + descriptor.characteristic!!.service!!.UUID.toUuid(), + descriptor.characteristic!!.UUID.toUuid(), + descriptor.UUID.toUuid(), + ) } diff --git a/core/src/commonMain/kotlin/logs/Hex.kt b/core/src/commonMain/kotlin/logs/Hex.kt index d6df676d3..a3f7682e4 100644 --- a/core/src/commonMain/kotlin/logs/Hex.kt +++ b/core/src/commonMain/kotlin/logs/Hex.kt @@ -15,7 +15,7 @@ public class HexBuilder internal constructor() { public fun Hex(init: HexBuilder.() -> Unit = {}): Logging.DataProcessor { val config = HexBuilder().apply(init) - return Logging.DataProcessor { data, _, _, _ -> + return Logging.DataProcessor { data, _, _, _, _ -> data.toHexString(separator = config.separator, lowerCase = config.lowerCase) } } diff --git a/core/src/commonMain/kotlin/logs/LogMessage.kt b/core/src/commonMain/kotlin/logs/LogMessage.kt index 93e6079cb..fbf7888e4 100644 --- a/core/src/commonMain/kotlin/logs/LogMessage.kt +++ b/core/src/commonMain/kotlin/logs/LogMessage.kt @@ -3,8 +3,8 @@ package com.juul.kable.logs import com.benasher44.uuid.Uuid import com.juul.kable.Characteristic import com.juul.kable.Descriptor -import com.juul.kable.Service import com.juul.kable.WriteType +import com.juul.kable.logs.Logging.DataProcessor.Operation import com.juul.kable.logs.Logging.Format.Compact import com.juul.kable.logs.Logging.Format.Multiline @@ -19,28 +19,36 @@ internal class LogMessage( private val prefix = logging.identifier ?: platformIdentifier var message: String = "" + private var operation: Operation? = null private var serviceUuid: Uuid? = null private var characteristicUuid: Uuid? = null private var descriptorUuid: Uuid? = null private val details = mutableListOf>() - var data: ByteArray? = null + private var data: ByteArray? = null - fun detail(service: Service) { - serviceUuid = service.serviceUuid - characteristicUuid = null - descriptorUuid = null + fun detail(serviceUuid: Uuid, characteristicUuid: Uuid) { + this.serviceUuid = serviceUuid + this.characteristicUuid = characteristicUuid + this.descriptorUuid = null + } + + fun detail(serviceUuid: Uuid, characteristicUuid: Uuid, descriptorUuid: Uuid) { + this.serviceUuid = serviceUuid + this.characteristicUuid = characteristicUuid + this.descriptorUuid = descriptorUuid } fun detail(characteristic: Characteristic) { - serviceUuid = characteristic.serviceUuid - characteristicUuid = characteristic.characteristicUuid - descriptorUuid = null + detail(characteristic.serviceUuid, characteristic.characteristicUuid) } fun detail(descriptor: Descriptor) { - serviceUuid = descriptor.serviceUuid - characteristicUuid = descriptor.characteristicUuid - descriptorUuid = descriptor.descriptorUuid + detail(descriptor.serviceUuid, descriptor.characteristicUuid, descriptor.descriptorUuid) + } + + fun detail(data: ByteArray?, operation: Operation) { + this.data = data + this.operation = operation } fun detail(key: String, value: String) { @@ -94,7 +102,7 @@ internal class LogMessage( if (logging.level == Logging.Level.Data) { data?.let { - append("data", logging.data.process(it, serviceUuid, characteristicUuid, descriptorUuid)) + append("data", logging.data.process(it, operation, serviceUuid, characteristicUuid, descriptorUuid)) } } @@ -102,10 +110,6 @@ internal class LogMessage( } } -internal fun LogMessage.detail(data: ByteArray?) { - if (data != null) this@detail.data = data -} - internal fun LogMessage.detail(writeType: WriteType) { detail("writeType", writeType.name) } diff --git a/core/src/commonMain/kotlin/logs/Logging.kt b/core/src/commonMain/kotlin/logs/Logging.kt index 19c7d762f..defa476ed 100644 --- a/core/src/commonMain/kotlin/logs/Logging.kt +++ b/core/src/commonMain/kotlin/logs/Logging.kt @@ -43,8 +43,12 @@ public class Logging { } public fun interface DataProcessor { + + public enum class Operation { Read, Write, Change } + public fun process( data: ByteArray, + operation: Operation?, serviceUuid: Uuid?, characteristicUuid: Uuid?, descriptorUuid: Uuid?, diff --git a/core/src/commonTest/kotlin/LogMessageTests.kt b/core/src/commonTest/kotlin/LogMessageTests.kt index c0d05a72c..fd3633fe7 100644 --- a/core/src/commonTest/kotlin/LogMessageTests.kt +++ b/core/src/commonTest/kotlin/LogMessageTests.kt @@ -3,10 +3,10 @@ package com.juul.kable import com.benasher44.uuid.uuidFrom import com.juul.kable.logs.LogMessage import com.juul.kable.logs.Logging +import com.juul.kable.logs.Logging.DataProcessor.Operation.Read import com.juul.kable.logs.Logging.Format.Compact import com.juul.kable.logs.Logging.Format.Multiline import com.juul.kable.logs.Logging.Level.Data -import com.juul.kable.logs.detail import kotlin.test.Test import kotlin.test.assertEquals @@ -210,7 +210,7 @@ class LogMessageTests { } val log = LogMessage(logging, platformIdentifier = "example").apply { message = "Compact" - detail(byteArrayOf(1, 2)) + detail(byteArrayOf(1, 2), Read) } assertEquals( expected = "example Compact", @@ -226,7 +226,7 @@ class LogMessageTests { } val log = LogMessage(logging, platformIdentifier = "example").apply { message = "Compact" - detail(byteArrayOf(1, 2)) + detail(byteArrayOf(1, 2), Read) } assertEquals( expected = "example Compact(data=01 02)", diff --git a/core/src/jsMain/kotlin/BluetoothDeviceWebBluetoothPeripheral.kt b/core/src/jsMain/kotlin/BluetoothDeviceWebBluetoothPeripheral.kt index 4f4b55fea..068b71f24 100644 --- a/core/src/jsMain/kotlin/BluetoothDeviceWebBluetoothPeripheral.kt +++ b/core/src/jsMain/kotlin/BluetoothDeviceWebBluetoothPeripheral.kt @@ -11,6 +11,7 @@ import com.juul.kable.external.BluetoothRemoteGATTServer import com.juul.kable.external.string import com.juul.kable.logs.Logger import com.juul.kable.logs.Logging +import com.juul.kable.logs.Logging.DataProcessor.Operation import com.juul.kable.logs.detail import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.NonCancellable @@ -208,7 +209,7 @@ internal class BluetoothDeviceWebBluetoothPeripheral( message = "write" detail(characteristic) detail(writeType) - detail(data) + detail(data, Operation.Write) } val platformCharacteristic = discoveredServices.obtain(characteristic, writeType.properties) @@ -230,7 +231,7 @@ internal class BluetoothDeviceWebBluetoothPeripheral( logger.debug { message = "read" detail(characteristic) - detail(value) + detail(value, Operation.Read) } return value } @@ -248,7 +249,7 @@ internal class BluetoothDeviceWebBluetoothPeripheral( logger.debug { message = "write" detail(descriptor) - detail(data) + detail(data, Operation.Write) } val platformDescriptor = discoveredServices.obtain(descriptor) @@ -267,7 +268,7 @@ internal class BluetoothDeviceWebBluetoothPeripheral( logger.debug { message = "read" detail(descriptor) - detail(value) + detail(value, Operation.Read) } return value } @@ -373,7 +374,7 @@ internal class BluetoothDeviceWebBluetoothPeripheral( logger.debug { message = CHARACTERISTIC_VALUE_CHANGED detail(this@createListener) - detail(data) + detail(data, Operation.Change) } val characteristicChange = ObservationEvent.CharacteristicChange(this, data) diff --git a/core/src/jsMain/kotlin/logs/LogMessage.kt b/core/src/jsMain/kotlin/logs/LogMessage.kt index 68a75a58d..784cd7a14 100644 --- a/core/src/jsMain/kotlin/logs/LogMessage.kt +++ b/core/src/jsMain/kotlin/logs/LogMessage.kt @@ -1,21 +1,16 @@ package com.juul.kable.logs import com.juul.kable.external.BluetoothRemoteGATTCharacteristic -import com.juul.kable.external.BluetoothRemoteGATTService +import com.juul.kable.logs.Logging.DataProcessor.Operation import com.juul.kable.toByteArray import org.khronos.webgl.DataView internal actual val LOG_INDENT: String? = " " -internal fun LogMessage.detail(data: DataView?) { - if (data != null) detail(data.buffer.toByteArray()) -} - -internal fun LogMessage.detail(service: BluetoothRemoteGATTService) { - detail("service", service.uuid) +internal fun LogMessage.detail(data: DataView?, operation: Operation) { + detail(data?.buffer?.toByteArray(), operation) } internal fun LogMessage.detail(characteristic: BluetoothRemoteGATTCharacteristic) { - detail(characteristic.service) - detail("characteristic", characteristic.uuid) + detail(characteristic.service.uuid, characteristic.uuid) } From 9d75e0ab09738e4d5ff1c223aa8f214137b1f61f Mon Sep 17 00:00:00 2001 From: Travis Wyatt Date: Wed, 26 Jul 2023 14:00:25 -0700 Subject: [PATCH 3/3] Mark `DataProcessor` as `@ObsoleteKableApi` --- core/src/commonMain/kotlin/logs/Logging.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/src/commonMain/kotlin/logs/Logging.kt b/core/src/commonMain/kotlin/logs/Logging.kt index defa476ed..f940ed4e4 100644 --- a/core/src/commonMain/kotlin/logs/Logging.kt +++ b/core/src/commonMain/kotlin/logs/Logging.kt @@ -1,6 +1,7 @@ package com.juul.kable.logs import com.benasher44.uuid.Uuid +import com.juul.kable.ObsoleteKableApi internal typealias LoggingBuilder = Logging.() -> Unit @@ -42,6 +43,7 @@ public class Logging { Multiline, } + @ObsoleteKableApi // Planned to be replaced w/ I/O interceptors: https://github.com/JuulLabs/kable/issues/539 public fun interface DataProcessor { public enum class Operation { Read, Write, Change } @@ -67,5 +69,7 @@ public class Logging { public var engine: LogEngine = SystemLogEngine public var level: Level = Level.Warnings public var format: Format = Format.Multiline + + @ObsoleteKableApi // Planned to be replaced w/ I/O interceptors: https://github.com/JuulLabs/kable/issues/539 public var data: DataProcessor = Hex }