From 67426165b71ec1d0f29cfc3e626de30b82039a97 Mon Sep 17 00:00:00 2001 From: jack Date: Wed, 18 Jun 2025 20:15:40 -0500 Subject: [PATCH 1/3] Added dirty flags to Device class --- .../club/clubk/ktag/konfigurator/Device.kt | 62 ++++++++++++++++--- .../clubk/ktag/konfigurator/DeviceState.kt | 8 --- .../clubk/ktag/konfigurator/GameConfig.kt | 2 +- .../clubk/ktag/konfigurator/MainActivity.kt | 13 ++-- 4 files changed, 58 insertions(+), 27 deletions(-) delete mode 100644 app/src/main/java/club/clubk/ktag/konfigurator/DeviceState.kt diff --git a/app/src/main/java/club/clubk/ktag/konfigurator/Device.kt b/app/src/main/java/club/clubk/ktag/konfigurator/Device.kt index 8b607aa..a07886d 100644 --- a/app/src/main/java/club/clubk/ktag/konfigurator/Device.kt +++ b/app/src/main/java/club/clubk/ktag/konfigurator/Device.kt @@ -2,23 +2,65 @@ package club.clubk.ktag.konfigurator import java.util.UUID +sealed class DeviceState { + data object Configurable : DeviceState() + data object Ready : DeviceState() + data object Playing : DeviceState() + data object WrapUp : DeviceState() +} + +sealed class DeviceConfigureState { + data object Discovered: DeviceConfigureState() + data object Configuring: DeviceConfigureState() + data object Success: DeviceConfigureState() + data object Failure: DeviceConfigureState() +} + data class Device(val uuid: UUID = UUID.randomUUID(), var name: String = "Unknown Device", - var address: String = "FF:FF:FF:FF:FF:FF", - var deviceType : Int? = null, - var team : Int? = null, - var playerID : Int? = null, - var deviceState: DeviceState? = null + var address: String = "00:00:00:00:00:00", + var deviceType: UInt? = null, + var deviceState: DeviceState? = null, + var deviceConfigureState: DeviceConfigureState = DeviceConfigureState.Discovered, + // All configurable variables have their own dirty flag + var playerID: UInt? = null, + var dirtyPlayerID: Boolean = false, + var team: UInt? = null, + var dirtyTeam: Boolean = false, + var secondaryColor: UInt? = null, + var dirtySecondaryColor: Boolean = false, + var maxHealth: UInt? = null, + var dirtyMaxHealth: Boolean = false, + var specialWeapons: UInt? = null, + var dirtySpecialWeapons: Boolean = false ) { fun deviceTypeName(): String { return when(deviceType) { - 0 -> "Little Boy BLuE" - 1 -> "2020TPC" - 2 -> "Mobile App" - 3 -> "32ESPecial" + 0u -> "Little Boy BLuE" + 1u -> "2020TPC" + 2u -> "Mobile App" + 3u -> "32ESPecial" else -> "Unknown Device Type" } } -} + fun deviceTypeDrawable() { + return + } + + fun isDirty(): Boolean { + return dirtyPlayerID || dirtyTeam || dirtySecondaryColor + || dirtyMaxHealth || dirtySpecialWeapons + } + + fun isPastEditTime(): Boolean { + return false + } + + fun checkForDirt() { + if (isDirty() && isPastEditTime()) { + return + } + } +} diff --git a/app/src/main/java/club/clubk/ktag/konfigurator/DeviceState.kt b/app/src/main/java/club/clubk/ktag/konfigurator/DeviceState.kt deleted file mode 100644 index 22ea035..0000000 --- a/app/src/main/java/club/clubk/ktag/konfigurator/DeviceState.kt +++ /dev/null @@ -1,8 +0,0 @@ -package club.clubk.ktag.konfigurator - -sealed class DeviceState { - object Configurable : DeviceState() - object Ready : DeviceState() - object Playing : DeviceState() - object WrapUp : DeviceState() -} \ No newline at end of file diff --git a/app/src/main/java/club/clubk/ktag/konfigurator/GameConfig.kt b/app/src/main/java/club/clubk/ktag/konfigurator/GameConfig.kt index ccf6988..47e96a0 100644 --- a/app/src/main/java/club/clubk/ktag/konfigurator/GameConfig.kt +++ b/app/src/main/java/club/clubk/ktag/konfigurator/GameConfig.kt @@ -5,5 +5,5 @@ data class GameConfig(var name: String = "Default", var pregameLength: Int = 60000, var numRounds: Int = 2, var maxHealth: Int = 10, - var numBombs: Int = 1 // Special Weapons Received on Game Reentry + var specialWeapons: Int = 1 // Special Weapons Received on Game Reentry ) diff --git a/app/src/main/java/club/clubk/ktag/konfigurator/MainActivity.kt b/app/src/main/java/club/clubk/ktag/konfigurator/MainActivity.kt index d67932b..b0f0519 100644 --- a/app/src/main/java/club/clubk/ktag/konfigurator/MainActivity.kt +++ b/app/src/main/java/club/clubk/ktag/konfigurator/MainActivity.kt @@ -1,8 +1,6 @@ package club.clubk.ktag.konfigurator import android.Manifest -import android.bluetooth.BluetoothManager -import android.content.Context import android.content.pm.PackageManager import android.os.Build import android.os.Bundle @@ -23,7 +21,6 @@ import androidx.core.content.ContextCompat import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.KeyboardType @@ -253,7 +250,7 @@ fun GameConfigEditor(oldGameConfig: GameConfig, var pregameLength by rememberSaveable(oldGameConfig.pregameLength) { mutableStateOf(oldGameConfig.pregameLength.toString()) } var numRounds by rememberSaveable(oldGameConfig.numRounds) { mutableStateOf(oldGameConfig.numRounds.toString()) } var maxHealth by rememberSaveable(oldGameConfig.maxHealth) { mutableStateOf(oldGameConfig.maxHealth.toString()) } - var numBombs by rememberSaveable(oldGameConfig.numBombs) { mutableStateOf(oldGameConfig.numBombs.toString()) } + var numBombs by rememberSaveable(oldGameConfig.specialWeapons) { mutableStateOf(oldGameConfig.specialWeapons.toString()) } // For tracking validation errors var gameLengthError by rememberSaveable { mutableStateOf(false) } @@ -283,7 +280,7 @@ fun GameConfigEditor(oldGameConfig: GameConfig, pregameLength = pregameLength.toIntOrNull() ?: oldGameConfig.pregameLength, numRounds = numRounds.toIntOrNull() ?: oldGameConfig.numRounds, maxHealth = maxHealth.toIntOrNull() ?: oldGameConfig.maxHealth, - numBombs = numBombs.toIntOrNull() ?: oldGameConfig.numBombs + specialWeapons = numBombs.toIntOrNull() ?: oldGameConfig.specialWeapons ) onSave(newGameConfig) } @@ -479,9 +476,9 @@ fun SideBySideButtons( @Composable fun DeviceCard(stateMachine: StateMachineViewModel, device: Device) { val backgroundColor = when (device.team) { - 0 -> Color.Magenta - 1 -> Color.Red - 2 -> Color.Blue + 0u -> Color.Magenta + 1u -> Color.Red + 2u -> Color.Blue else -> MaterialTheme.colorScheme.surface // Default color } Card(modifier = Modifier From 74fc19c64d5d7bdb71d514f136a84b786fdc2bfa Mon Sep 17 00:00:00 2001 From: jack Date: Wed, 18 Jun 2025 20:52:32 -0500 Subject: [PATCH 2/3] Added setParameter function that handles dirt flags --- .../club/clubk/ktag/konfigurator/Device.kt | 92 ++++++++++++++++--- 1 file changed, 79 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/club/clubk/ktag/konfigurator/Device.kt b/app/src/main/java/club/clubk/ktag/konfigurator/Device.kt index a07886d..e715e5f 100644 --- a/app/src/main/java/club/clubk/ktag/konfigurator/Device.kt +++ b/app/src/main/java/club/clubk/ktag/konfigurator/Device.kt @@ -2,6 +2,8 @@ package club.clubk.ktag.konfigurator import java.util.UUID +const val EDIT_DELAY_TIME: Long = 3L + sealed class DeviceState { data object Configurable : DeviceState() data object Ready : DeviceState() @@ -16,24 +18,44 @@ sealed class DeviceConfigureState { data object Failure: DeviceConfigureState() } +enum class DeviceParameter { + PLAYER_ID, TEAM, SECONDARY_COLOR, MAX_HEALTH, SPECIAL_WEAPONS +} + data class Device(val uuid: UUID = UUID.randomUUID(), var name: String = "Unknown Device", var address: String = "00:00:00:00:00:00", var deviceType: UInt? = null, var deviceState: DeviceState? = null, - var deviceConfigureState: DeviceConfigureState = DeviceConfigureState.Discovered, - // All configurable variables have their own dirty flag - var playerID: UInt? = null, - var dirtyPlayerID: Boolean = false, - var team: UInt? = null, - var dirtyTeam: Boolean = false, - var secondaryColor: UInt? = null, - var dirtySecondaryColor: Boolean = false, - var maxHealth: UInt? = null, - var dirtyMaxHealth: Boolean = false, - var specialWeapons: UInt? = null, - var dirtySpecialWeapons: Boolean = false + // All configurable variables + private var _playerID: UInt? = null, + private var _team: UInt? = null, + private var _secondaryColor: UInt? = null, + private var _maxHealth: UInt? = null, + private var _specialWeapons: UInt? = null ) { + var deviceConfigureState: DeviceConfigureState = DeviceConfigureState.Discovered + private set + + var dirtyPlayerID: Boolean = false + private set + var dirtyTeam: Boolean = false + private set + var dirtySecondaryColor: Boolean = false + private set + var dirtyMaxHealth: Boolean = false + private set + var dirtySpecialWeapons: Boolean = false + private set + + var lastEditTime: Long = 0L + private set + + val playerID: UInt? get() = _playerID + val team: UInt? get() = _team + val secondaryColor: UInt? get() = _secondaryColor + val maxHealth: UInt? get() = _maxHealth + val specialWeapons: UInt? get() = _specialWeapons fun deviceTypeName(): String { return when(deviceType) { @@ -55,7 +77,33 @@ data class Device(val uuid: UUID = UUID.randomUUID(), } fun isPastEditTime(): Boolean { - return false + return System.nanoTime() - lastEditTime!! >= EDIT_DELAY_TIME + } + + fun setParameter(parameter: DeviceParameter, value: Any) { + lastEditTime = System.nanoTime() + when (parameter) { + DeviceParameter.PLAYER_ID -> { + _playerID = value as UInt + dirtyPlayerID = true + } + DeviceParameter.TEAM -> { + _team = value as UInt + dirtyTeam = true + } + DeviceParameter.SECONDARY_COLOR -> { + _secondaryColor = value as UInt + dirtySecondaryColor = true + } + DeviceParameter.MAX_HEALTH -> { + _maxHealth = value as UInt + dirtyMaxHealth = true + } + DeviceParameter.SPECIAL_WEAPONS -> { + _specialWeapons = value as UInt + dirtySpecialWeapons = true + } + } } fun checkForDirt() { @@ -63,4 +111,22 @@ data class Device(val uuid: UUID = UUID.randomUUID(), return } } + + fun getDirtyParameters(): List { + val changed = mutableListOf() + if (dirtyPlayerID) changed.add(DeviceParameter.PLAYER_ID) + if (dirtyTeam) changed.add(DeviceParameter.TEAM) + if (dirtySecondaryColor) changed.add(DeviceParameter.SECONDARY_COLOR) + if (dirtyMaxHealth) changed.add(DeviceParameter.MAX_HEALTH) + if (dirtySpecialWeapons) changed.add(DeviceParameter.SPECIAL_WEAPONS) + return changed + } + + fun clearDirt() { + dirtyPlayerID = false + dirtyTeam = false + dirtySecondaryColor = false + dirtyMaxHealth = false + dirtySpecialWeapons = false + } } From 0827bdc7b4c9cbdc40691ad8e9e24ee3cc5a3388 Mon Sep 17 00:00:00 2001 From: Joe Kearney Date: Sun, 14 Sep 2025 14:41:12 -0500 Subject: [PATCH 3/3] Fixed compile errors. --- .../club/clubk/ktag/konfigurator/Device.kt | 40 +++++++++---------- .../clubk/ktag/konfigurator/MainActivity.kt | 6 +-- .../konfigurator/StateMachineViewModel.kt | 38 ++++++++++++------ 3 files changed, 49 insertions(+), 35 deletions(-) diff --git a/app/src/main/java/club/clubk/ktag/konfigurator/Device.kt b/app/src/main/java/club/clubk/ktag/konfigurator/Device.kt index e715e5f..2b953fe 100644 --- a/app/src/main/java/club/clubk/ktag/konfigurator/Device.kt +++ b/app/src/main/java/club/clubk/ktag/konfigurator/Device.kt @@ -25,14 +25,14 @@ enum class DeviceParameter { data class Device(val uuid: UUID = UUID.randomUUID(), var name: String = "Unknown Device", var address: String = "00:00:00:00:00:00", - var deviceType: UInt? = null, + var deviceType: Int? = null, var deviceState: DeviceState? = null, // All configurable variables - private var _playerID: UInt? = null, - private var _team: UInt? = null, - private var _secondaryColor: UInt? = null, - private var _maxHealth: UInt? = null, - private var _specialWeapons: UInt? = null + private var _playerID: Int? = null, + private var _team: Int? = null, + private var _secondaryColor: Int? = null, + private var _maxHealth: Int? = null, + private var _specialWeapons: Int? = null ) { var deviceConfigureState: DeviceConfigureState = DeviceConfigureState.Discovered private set @@ -51,18 +51,18 @@ data class Device(val uuid: UUID = UUID.randomUUID(), var lastEditTime: Long = 0L private set - val playerID: UInt? get() = _playerID - val team: UInt? get() = _team - val secondaryColor: UInt? get() = _secondaryColor - val maxHealth: UInt? get() = _maxHealth - val specialWeapons: UInt? get() = _specialWeapons + val playerID: Int? get() = _playerID + val team: Int? get() = _team + val secondaryColor: Int? get() = _secondaryColor + val maxHealth: Int? get() = _maxHealth + val specialWeapons: Int? get() = _specialWeapons fun deviceTypeName(): String { return when(deviceType) { - 0u -> "Little Boy BLuE" - 1u -> "2020TPC" - 2u -> "Mobile App" - 3u -> "32ESPecial" + 0 -> "Little Boy BLuE" + 1 -> "2020TPC" + 2 -> "Mobile App" + 3 -> "32ESPecial" else -> "Unknown Device Type" } } @@ -84,23 +84,23 @@ data class Device(val uuid: UUID = UUID.randomUUID(), lastEditTime = System.nanoTime() when (parameter) { DeviceParameter.PLAYER_ID -> { - _playerID = value as UInt + _playerID = value as Int dirtyPlayerID = true } DeviceParameter.TEAM -> { - _team = value as UInt + _team = value as Int dirtyTeam = true } DeviceParameter.SECONDARY_COLOR -> { - _secondaryColor = value as UInt + _secondaryColor = value as Int dirtySecondaryColor = true } DeviceParameter.MAX_HEALTH -> { - _maxHealth = value as UInt + _maxHealth = value as Int dirtyMaxHealth = true } DeviceParameter.SPECIAL_WEAPONS -> { - _specialWeapons = value as UInt + _specialWeapons = value as Int dirtySpecialWeapons = true } } diff --git a/app/src/main/java/club/clubk/ktag/konfigurator/MainActivity.kt b/app/src/main/java/club/clubk/ktag/konfigurator/MainActivity.kt index b0f0519..e714f6a 100644 --- a/app/src/main/java/club/clubk/ktag/konfigurator/MainActivity.kt +++ b/app/src/main/java/club/clubk/ktag/konfigurator/MainActivity.kt @@ -476,9 +476,9 @@ fun SideBySideButtons( @Composable fun DeviceCard(stateMachine: StateMachineViewModel, device: Device) { val backgroundColor = when (device.team) { - 0u -> Color.Magenta - 1u -> Color.Red - 2u -> Color.Blue + 0 -> Color.Magenta + 1 -> Color.Red + 2 -> Color.Blue else -> MaterialTheme.colorScheme.surface // Default color } Card(modifier = Modifier diff --git a/app/src/main/java/club/clubk/ktag/konfigurator/StateMachineViewModel.kt b/app/src/main/java/club/clubk/ktag/konfigurator/StateMachineViewModel.kt index aeedd61..267dbf5 100644 --- a/app/src/main/java/club/clubk/ktag/konfigurator/StateMachineViewModel.kt +++ b/app/src/main/java/club/clubk/ktag/konfigurator/StateMachineViewModel.kt @@ -69,10 +69,12 @@ class StateMachineViewModel(context: Context) : ViewModel() { when (packet) { is HelloPacket -> { // Log.d(TAG_BLE_SCAN, "HelloPacket scanned") - scannedDevice.name = packet.deviceName - scannedDevice.deviceType = packet.deviceType - scannedDevice.team = packet.teamId - scannedDevice.deviceState = DeviceState.Configurable + val scannedDevice = Device( + name = packet.deviceName, + address = result.device.address, + deviceType = packet.deviceType, + deviceState = DeviceState.Configurable) + scannedDevice.setParameter(DeviceParameter.TEAM, packet.teamId) addOrRefreshDevice(scannedDevice) } is ConsolePacket -> { @@ -148,9 +150,13 @@ class StateMachineViewModel(context: Context) : ViewModel() { var oldDevice = currentDevices[index] newDevice.name = oldDevice.name newDevice.deviceType = oldDevice.deviceType ?: newDevice.deviceType - newDevice.team = oldDevice.team ?: newDevice.team - newDevice.playerID = oldDevice.playerID ?: newDevice.playerID newDevice.deviceState = newDevice.deviceState ?: oldDevice.deviceState + oldDevice.team?.let { teamValue -> + newDevice.setParameter(DeviceParameter.TEAM, teamValue) + } + oldDevice.playerID?.let { playerIDValue -> + newDevice.setParameter(DeviceParameter.PLAYER_ID, playerIDValue) + } currentDevices[index] = newDevice } _devices.value = currentDevices @@ -165,18 +171,26 @@ class StateMachineViewModel(context: Context) : ViewModel() { if (index == -1) { return } var oldDevice = currentDevices[index] newDevice.deviceType = newDevice.deviceType ?: oldDevice.deviceType - newDevice.team = newDevice.team ?: oldDevice.team - newDevice.playerID = newDevice.playerID ?: oldDevice.playerID + oldDevice.team?.let { teamValue -> + newDevice.setParameter(DeviceParameter.TEAM, teamValue) + } + oldDevice.playerID?.let { playerIDValue -> + newDevice.setParameter(DeviceParameter.PLAYER_ID, playerIDValue) + } currentDevices[index] = newDevice _devices.value = currentDevices _allDevicesReady.value = allDevicesReady() } - fun updateDeviceTeam(deviceAddress: String, newTeam: Int) { + fun updateDeviceTeam(deviceAddress: String, newTeam: Int?) { _devices.update { currentList -> currentList.map { device -> if (device.address == deviceAddress) { - device.copy(team = newTeam) // Creates a new Device instance + val updatedDevice = device.copy() + if (newTeam != null) { + updatedDevice.setParameter(DeviceParameter.TEAM, newTeam) + } + updatedDevice // Return the modified device } else { device } @@ -187,7 +201,7 @@ class StateMachineViewModel(context: Context) : ViewModel() { fun cycleDeviceTeam(device: Device) { Log.d("STATEMACHINE", "cycling device team") - var newTeam = device.team ?: -1 + var newTeam: Int = (device.team?.toInt() ?: -1) newTeam++ if (newTeam > 2) { newTeam = 0 @@ -244,7 +258,7 @@ class StateMachineViewModel(context: Context) : ViewModel() { parameterPacketGenerator.generatePacket( targetAddress = device.address, subtype = 2, // Request Parameter Change - key1 = 1, value1 = teamId, // Key 1 is Team ID + key1 = 1, value1 = teamId.toInt(), // Key 1 is Team ID key2 = 4, value2 = gameCfg.maxHealth // Key 2 is Max Health ) // If a device for some reason can't be configured (e.g. missing address),