diff --git a/core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/RequestTimer.kt b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/RequestTimer.kt index 781bc9448..1dde1d6a8 100644 --- a/core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/RequestTimer.kt +++ b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/RequestTimer.kt @@ -42,14 +42,14 @@ internal class RequestTimer { /** * Consumes the start time recorded for [requestId] and appends a `Duration: N s` line to [text], logging completion - * under [label]. Returns [text] unchanged when no start time was recorded for the id. + * under [logLabel]. Returns [text] unchanged when no start time was recorded for the id. */ - fun appendDuration(requestId: Int, text: String, label: String): String { + fun appendDuration(requestId: Int, text: String, logLabel: String): String { val start = startTimes.value[requestId] startTimes.update { it.remove(requestId) } if (start == null) return text val seconds = (nowMillis - start) / MILLIS_PER_SECOND - Logger.i { "$label $requestId complete in $seconds s" } + Logger.i { "$logLabel $requestId complete in $seconds s" } return "$text\n\nDuration: ${NumberFormatter.format(seconds, 1)} s" } diff --git a/core/data/src/commonTest/kotlin/org/meshtastic/core/data/manager/RequestTimerTest.kt b/core/data/src/commonTest/kotlin/org/meshtastic/core/data/manager/RequestTimerTest.kt index a047d401c..245effd15 100644 --- a/core/data/src/commonTest/kotlin/org/meshtastic/core/data/manager/RequestTimerTest.kt +++ b/core/data/src/commonTest/kotlin/org/meshtastic/core/data/manager/RequestTimerTest.kt @@ -26,7 +26,7 @@ class RequestTimerTest { fun appendDuration_withoutStart_returnsTextUnchanged() { val timer = RequestTimer() - assertEquals("base", timer.appendDuration(requestId = 1, text = "base", label = "Test")) + assertEquals("base", timer.appendDuration(requestId = 1, text = "base", logLabel = "Test")) } @Test @@ -34,7 +34,7 @@ class RequestTimerTest { val timer = RequestTimer() timer.start(requestId = 7) - val result = timer.appendDuration(requestId = 7, text = "base", label = "Test") + val result = timer.appendDuration(requestId = 7, text = "base", logLabel = "Test") assertTrue(result.startsWith("base\n\nDuration: "), "expected a duration suffix, got: $result") assertTrue(result.endsWith(" s")) @@ -45,9 +45,9 @@ class RequestTimerTest { val timer = RequestTimer() timer.start(requestId = 7) - timer.appendDuration(requestId = 7, text = "first", label = "Test") + timer.appendDuration(requestId = 7, text = "first", logLabel = "Test") // The start time is single-use; a second response for the same id gets no duration. - assertEquals("second", timer.appendDuration(requestId = 7, text = "second", label = "Test")) + assertEquals("second", timer.appendDuration(requestId = 7, text = "second", logLabel = "Test")) } @Test @@ -57,7 +57,7 @@ class RequestTimerTest { timer.start(requestId = 2) // Consuming one id must not affect the other. - timer.appendDuration(requestId = 1, text = "a", label = "Test") - assertTrue(timer.appendDuration(requestId = 2, text = "b", label = "Test").contains("Duration: ")) + timer.appendDuration(requestId = 1, text = "a", logLabel = "Test") + assertTrue(timer.appendDuration(requestId = 2, text = "b", logLabel = "Test").contains("Duration: ")) } } diff --git a/core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/AdminActionsUseCase.kt b/core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/AdminActionsUseCase.kt index 4c5f38f96..3957a264e 100644 --- a/core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/AdminActionsUseCase.kt +++ b/core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/AdminActionsUseCase.kt @@ -39,7 +39,7 @@ constructor( * @return The packet ID of the request. */ open suspend fun reboot(destNum: Int): Int { - val packetId = radioController.getPacketId() + val packetId = radioController.generatePacketId() radioController.reboot(destNum, packetId) return packetId } @@ -51,7 +51,7 @@ constructor( * @return The packet ID of the request. */ open suspend fun shutdown(destNum: Int): Int { - val packetId = radioController.getPacketId() + val packetId = radioController.generatePacketId() radioController.shutdown(destNum, packetId) return packetId } @@ -64,7 +64,7 @@ constructor( * @return The packet ID of the request. */ open suspend fun factoryReset(destNum: Int, isLocal: Boolean): Int { - val packetId = radioController.getPacketId() + val packetId = radioController.generatePacketId() radioController.factoryReset(destNum, packetId) if (isLocal) { @@ -84,7 +84,7 @@ constructor( * @return The packet ID of the request. */ open suspend fun nodedbReset(destNum: Int, preserveFavorites: Boolean, isLocal: Boolean): Int { - val packetId = radioController.getPacketId() + val packetId = radioController.generatePacketId() radioController.nodedbReset(destNum, packetId, preserveFavorites) if (isLocal) { diff --git a/core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/CleanNodeDatabaseUseCase.kt b/core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/CleanNodeDatabaseUseCase.kt index ab73435e3..bc053a54a 100644 --- a/core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/CleanNodeDatabaseUseCase.kt +++ b/core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/CleanNodeDatabaseUseCase.kt @@ -58,7 +58,7 @@ constructor( nodeRepository.deleteNodes(nodeNums) for (nodeNum in nodeNums) { - val packetId = radioController.getPacketId() + val packetId = radioController.generatePacketId() radioController.removeByNodenum(packetId, nodeNum) } } diff --git a/core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/RadioConfigUseCase.kt b/core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/RadioConfigUseCase.kt index 15698119c..af7fdfa29 100644 --- a/core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/RadioConfigUseCase.kt +++ b/core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/RadioConfigUseCase.kt @@ -35,7 +35,7 @@ open class RadioConfigUseCase constructor(private val radioController: RadioCont * @return The packet ID of the request. */ open suspend fun setOwner(destNum: Int, user: User): Int { - val packetId = radioController.getPacketId() + val packetId = radioController.generatePacketId() radioController.setOwner(destNum, user, packetId) return packetId } @@ -47,7 +47,7 @@ open class RadioConfigUseCase constructor(private val radioController: RadioCont * @return The packet ID of the request. */ open suspend fun getOwner(destNum: Int): Int { - val packetId = radioController.getPacketId() + val packetId = radioController.generatePacketId() radioController.getOwner(destNum, packetId) return packetId } @@ -60,7 +60,7 @@ open class RadioConfigUseCase constructor(private val radioController: RadioCont * @return The packet ID of the request. */ open suspend fun setConfig(destNum: Int, config: Config): Int { - val packetId = radioController.getPacketId() + val packetId = radioController.generatePacketId() radioController.setConfig(destNum, config, packetId) return packetId } @@ -73,7 +73,7 @@ open class RadioConfigUseCase constructor(private val radioController: RadioCont * @return The packet ID of the request. */ open suspend fun getConfig(destNum: Int, configType: Int): Int { - val packetId = radioController.getPacketId() + val packetId = radioController.generatePacketId() radioController.getConfig(destNum, configType, packetId) return packetId } @@ -86,7 +86,7 @@ open class RadioConfigUseCase constructor(private val radioController: RadioCont * @return The packet ID of the request. */ open suspend fun setModuleConfig(destNum: Int, config: ModuleConfig): Int { - val packetId = radioController.getPacketId() + val packetId = radioController.generatePacketId() radioController.setModuleConfig(destNum, config, packetId) return packetId } @@ -99,7 +99,7 @@ open class RadioConfigUseCase constructor(private val radioController: RadioCont * @return The packet ID of the request. */ open suspend fun getModuleConfig(destNum: Int, moduleConfigType: Int): Int { - val packetId = radioController.getPacketId() + val packetId = radioController.generatePacketId() radioController.getModuleConfig(destNum, moduleConfigType, packetId) return packetId } @@ -112,7 +112,7 @@ open class RadioConfigUseCase constructor(private val radioController: RadioCont * @return The packet ID of the request. */ open suspend fun getChannel(destNum: Int, index: Int): Int { - val packetId = radioController.getPacketId() + val packetId = radioController.generatePacketId() radioController.getChannel(destNum, index, packetId) return packetId } @@ -125,7 +125,7 @@ open class RadioConfigUseCase constructor(private val radioController: RadioCont * @return The packet ID of the request. */ open suspend fun setRemoteChannel(destNum: Int, channel: org.meshtastic.proto.Channel): Int { - val packetId = radioController.getPacketId() + val packetId = radioController.generatePacketId() radioController.setRemoteChannel(destNum, channel, packetId) return packetId } @@ -152,7 +152,7 @@ open class RadioConfigUseCase constructor(private val radioController: RadioCont * @return The packet ID of the request. */ open suspend fun getRingtone(destNum: Int): Int { - val packetId = radioController.getPacketId() + val packetId = radioController.generatePacketId() radioController.getRingtone(destNum, packetId) return packetId } @@ -169,7 +169,7 @@ open class RadioConfigUseCase constructor(private val radioController: RadioCont * @return The packet ID of the request. */ open suspend fun getCannedMessages(destNum: Int): Int { - val packetId = radioController.getPacketId() + val packetId = radioController.generatePacketId() radioController.getCannedMessages(destNum, packetId) return packetId } @@ -181,7 +181,7 @@ open class RadioConfigUseCase constructor(private val radioController: RadioCont * @return The packet ID of the request. */ open suspend fun getDeviceConnectionStatus(destNum: Int): Int { - val packetId = radioController.getPacketId() + val packetId = radioController.generatePacketId() radioController.getDeviceConnectionStatus(destNum, packetId) return packetId } diff --git a/core/repository/README.md b/core/repository/README.md index 799d87ac0..8f460b84e 100644 --- a/core/repository/README.md +++ b/core/repository/README.md @@ -36,7 +36,7 @@ src/ │ ├── AdminController.kt ← config, channels, owner, device lifecycle, editSettings │ ├── MessagingController.kt ← send packets, reactions, contacts │ ├── NodeController.kt ← favorite, ignore, mute, remove nodes -│ ├── RequestController.kt ← telemetry, traceroute, position queries +│ ├── QueryController.kt ← telemetry, traceroute, position queries │ ├── CommandSender.kt │ ├── AdminPacketHandler.kt │ ├── FromRadioPacketHandler.kt @@ -130,7 +130,7 @@ connection state). Handlers inject `ServiceStateWriter` for mutations. The full `ServiceRepository` union is still available for backward compatibility. Radio commands are issued through `RadioController` (a composite of `AdminController`, -`MessagingController`, `NodeController`, `RequestController`) rather than an action/intent bus. +`MessagingController`, `NodeController`, `QueryController`) rather than an action/intent bus. ### `NodeRepository` diff --git a/core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/RequestController.kt b/core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/QueryController.kt similarity index 93% rename from core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/RequestController.kt rename to core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/QueryController.kt index 44ac1b6af..39f72bc87 100644 --- a/core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/RequestController.kt +++ b/core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/QueryController.kt @@ -19,14 +19,14 @@ package org.meshtastic.core.repository import org.meshtastic.core.model.Position /** - * Mesh request operations — position, traceroute, telemetry, user info, and metadata queries. + * Mesh query operations — position, traceroute, telemetry, user info, and metadata. * * These are "pull" operations that request data from remote nodes. When the SDK is adopted, implementations delegate to * `RadioClient.telemetry` and `RadioClient.routing` sub-APIs. * * @see RadioController which extends this interface for backward compatibility */ -interface RequestController { +interface QueryController { /** Requests device metadata from a remote node. */ suspend fun refreshMetadata(destNum: Int) diff --git a/core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/RadioController.kt b/core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/RadioController.kt index 529346857..646c3cf13 100644 --- a/core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/RadioController.kt +++ b/core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/RadioController.kt @@ -30,7 +30,7 @@ import org.meshtastic.proto.ClientNotification * - [AdminController] — config, channels, owner, device lifecycle (→ SDK `AdminApi`) * - [MessagingController] — send packets, reactions, contacts (→ SDK `RadioClient.send*`) * - [NodeController] — favorite, ignore, mute, remove nodes (→ SDK `AdminApi` node ops) - * - [RequestController] — telemetry, traceroute, position queries (→ SDK `TelemetryApi` / `RoutingApi`) + * - [QueryController] — telemetry, traceroute, position queries (→ SDK `TelemetryApi` / `RoutingApi`) * * When migrating to the SDK, each sub-interface becomes a thin adapter over the corresponding SDK API. The composite * [RadioController] can then be deprecated and consumers migrated to the narrower interfaces one at a time. @@ -39,7 +39,7 @@ interface RadioController : AdminController, MessagingController, NodeController, - RequestController, + QueryController, ConnectionStateProvider { /** * Flow of notifications from the radio client. @@ -56,7 +56,7 @@ interface RadioController : * * @return A unique 32-bit integer. */ - fun getPacketId(): Int + fun generatePacketId(): Int /** Starts providing the phone's location to the mesh. */ fun startProvideLocation() diff --git a/core/service/README.md b/core/service/README.md index 1f93dbebd..2be3c8394 100644 --- a/core/service/README.md +++ b/core/service/README.md @@ -17,8 +17,8 @@ A high-level repository that wraps the service connection and exposes reactive ` ### 3. `ConnectionState` Represents the current state of the radio connection (`Connected`, `Disconnected`, `DeviceSleep`, etc.). -### 4. `DirectRadioControllerImpl` -The in-process `RadioController` composition root (Desktop, iOS, and single-process Android). It assembles four focused sub-controllers — `AdminControllerImpl`, `MessagingControllerImpl`, `NodeControllerImpl`, `RequestControllerImpl` — via Kotlin interface delegation, and owns the cross-cutting concerns (connection state, packet-id, location, device-address switching). Commands are direct suspend calls to `CommandSender`; admin sends are fire-and-forget (the device is the source of truth). Config writes use the `editSettings { }` transaction. +### 4. `RadioControllerImpl` +The in-process `RadioController` composition root (Desktop, iOS, and single-process Android). It assembles four focused sub-controllers — `AdminControllerImpl`, `MessagingControllerImpl`, `NodeControllerImpl`, `QueryControllerImpl` — via Kotlin interface delegation, and owns the cross-cutting concerns (connection state, packet-id, location, device-address switching). Commands are direct suspend calls to `CommandSender`; admin sends are fire-and-forget (the device is the source of truth). Config writes use the `editSettings { }` transaction. ## Dependency Graph diff --git a/core/service/src/androidMain/kotlin/org/meshtastic/core/service/di/CoreServiceAndroidModule.kt b/core/service/src/androidMain/kotlin/org/meshtastic/core/service/di/CoreServiceAndroidModule.kt index a57cd454d..ff5edf523 100644 --- a/core/service/src/androidMain/kotlin/org/meshtastic/core/service/di/CoreServiceAndroidModule.kt +++ b/core/service/src/androidMain/kotlin/org/meshtastic/core/service/di/CoreServiceAndroidModule.kt @@ -36,14 +36,14 @@ import org.meshtastic.core.repository.NodeRepository import org.meshtastic.core.repository.NotificationManager import org.meshtastic.core.repository.PacketRepository import org.meshtastic.core.repository.PlatformAnalytics +import org.meshtastic.core.repository.QueryController import org.meshtastic.core.repository.RadioConfigRepository import org.meshtastic.core.repository.RadioController import org.meshtastic.core.repository.RadioInterfaceService -import org.meshtastic.core.repository.RequestController import org.meshtastic.core.repository.ServiceRepository import org.meshtastic.core.repository.UiPrefs -import org.meshtastic.core.service.DirectRadioControllerImpl import org.meshtastic.core.service.MeshService +import org.meshtastic.core.service.RadioControllerImpl import org.meshtastic.core.service.startService @Module @@ -57,7 +57,7 @@ class CoreServiceAndroidModule { AdminController::class, MessagingController::class, NodeController::class, - RequestController::class, + QueryController::class, ], ) fun radioController( @@ -78,7 +78,7 @@ class CoreServiceAndroidModule { messageProcessor: Lazy, radioConfigRepository: RadioConfigRepository, @Named("ServiceScope") scope: CoroutineScope, - ): RadioController = DirectRadioControllerImpl( + ): RadioController = RadioControllerImpl( serviceRepository = serviceRepository, nodeRepository = nodeRepository, commandSender = commandSender, diff --git a/core/service/src/commonMain/kotlin/org/meshtastic/core/service/AdminControllerImpl.kt b/core/service/src/commonMain/kotlin/org/meshtastic/core/service/AdminControllerImpl.kt index 2aad63dbe..488c929e0 100644 --- a/core/service/src/commonMain/kotlin/org/meshtastic/core/service/AdminControllerImpl.kt +++ b/core/service/src/commonMain/kotlin/org/meshtastic/core/service/AdminControllerImpl.kt @@ -38,10 +38,10 @@ import org.meshtastic.proto.User * [AdminController] implementation: local/remote configuration, channels, owner, device lifecycle, and the * [editSettings] transaction. * - * Focused collaborator of [DirectRadioControllerImpl]. Builds [AdminMessage] protos directly and delegates to - * [CommandSender] for transport, mirroring the SDK's `AdminApiImpl` pattern. Config/channel writes use fire-and-forget - * optimistic local persistence ([handledLaunch]): the device is the source of truth and re-sends its full config on - * every connection, so persistence is a cache optimization, not a correctness requirement. + * Focused collaborator of [RadioControllerImpl]. Builds [AdminMessage] protos directly and delegates to [CommandSender] + * for transport, mirroring the SDK's `AdminApiImpl` pattern. Config/channel writes use fire-and-forget optimistic local + * persistence ([handledLaunch]): the device is the source of truth and re-sends its full config on every connection, so + * persistence is a cache optimization, not a correctness requirement. */ @Suppress("TooManyFunctions") internal class AdminControllerImpl( @@ -159,7 +159,7 @@ internal class AdminControllerImpl( override suspend fun reboot(destNum: Int, packetId: Int) { Logger.i { "Reboot requested for node $destNum" } - commandSender.sendAdmin(destNum, packetId) { AdminMessage(reboot_seconds = DEFAULT_REBOOT_DELAY) } + commandSender.sendAdmin(destNum, packetId) { AdminMessage(reboot_seconds = DEFAULT_DELAY_SECONDS) } } override suspend fun rebootToDfu(nodeNum: Int) { @@ -174,7 +174,7 @@ internal class AdminControllerImpl( } override suspend fun shutdown(destNum: Int, packetId: Int) { - commandSender.sendAdmin(destNum, packetId) { AdminMessage(shutdown_seconds = DEFAULT_REBOOT_DELAY) } + commandSender.sendAdmin(destNum, packetId) { AdminMessage(shutdown_seconds = DEFAULT_DELAY_SECONDS) } } override suspend fun factoryReset(destNum: Int, packetId: Int) { @@ -211,6 +211,6 @@ internal class AdminControllerImpl( } private companion object { - private const val DEFAULT_REBOOT_DELAY = 5 + private const val DEFAULT_DELAY_SECONDS = 5 } } diff --git a/core/service/src/commonMain/kotlin/org/meshtastic/core/service/MessagingControllerImpl.kt b/core/service/src/commonMain/kotlin/org/meshtastic/core/service/MessagingControllerImpl.kt index dea3077e1..b4573aa28 100644 --- a/core/service/src/commonMain/kotlin/org/meshtastic/core/service/MessagingControllerImpl.kt +++ b/core/service/src/commonMain/kotlin/org/meshtastic/core/service/MessagingControllerImpl.kt @@ -42,7 +42,7 @@ import org.meshtastic.proto.User /** * [MessagingController] implementation: sends data packets, reactions, and shared contacts. * - * Focused collaborator of [DirectRadioControllerImpl]. Mirrors the SDK's `RadioClient.send*` surface — when the SDK is + * Focused collaborator of [RadioControllerImpl]. Mirrors the SDK's `RadioClient.send*` surface — when the SDK is * adopted this becomes a thin adapter over `RadioClient`. */ internal class MessagingControllerImpl( diff --git a/core/service/src/commonMain/kotlin/org/meshtastic/core/service/NodeControllerImpl.kt b/core/service/src/commonMain/kotlin/org/meshtastic/core/service/NodeControllerImpl.kt index 054183cc3..e1418445d 100644 --- a/core/service/src/commonMain/kotlin/org/meshtastic/core/service/NodeControllerImpl.kt +++ b/core/service/src/commonMain/kotlin/org/meshtastic/core/service/NodeControllerImpl.kt @@ -27,8 +27,8 @@ import org.meshtastic.proto.AdminMessage /** * [NodeController] implementation: favorite, ignore, mute, and remove nodes. * - * Focused collaborator of [DirectRadioControllerImpl]. Favorite/ignore are idempotent (no-op when already in the - * requested state), mirroring the SDK's `AdminApi.setFavorite`/`setIgnored`. + * Focused collaborator of [RadioControllerImpl]. Favorite/ignore are idempotent (no-op when already in the requested + * state), mirroring the SDK's `AdminApi.setFavorite`/`setIgnored`. */ internal class NodeControllerImpl( private val commandSender: CommandSender, diff --git a/core/service/src/commonMain/kotlin/org/meshtastic/core/service/RequestControllerImpl.kt b/core/service/src/commonMain/kotlin/org/meshtastic/core/service/QueryControllerImpl.kt similarity index 88% rename from core/service/src/commonMain/kotlin/org/meshtastic/core/service/RequestControllerImpl.kt rename to core/service/src/commonMain/kotlin/org/meshtastic/core/service/QueryControllerImpl.kt index d0d5a74c2..8eb8def18 100644 --- a/core/service/src/commonMain/kotlin/org/meshtastic/core/service/RequestControllerImpl.kt +++ b/core/service/src/commonMain/kotlin/org/meshtastic/core/service/QueryControllerImpl.kt @@ -19,20 +19,20 @@ package org.meshtastic.core.service import org.meshtastic.core.model.Position import org.meshtastic.core.repository.CommandSender import org.meshtastic.core.repository.NodeManager -import org.meshtastic.core.repository.RequestController +import org.meshtastic.core.repository.QueryController import org.meshtastic.core.repository.UiPrefs import org.meshtastic.proto.AdminMessage /** - * [RequestController] implementation: position, traceroute, telemetry, user info, and metadata "pull" queries. + * [QueryController] implementation: position, traceroute, telemetry, user info, and metadata "pull" queries. * - * Focused collaborator of [DirectRadioControllerImpl]. Mirrors the SDK's `TelemetryApi`/`RoutingApi` surface. + * Focused collaborator of [RadioControllerImpl]. Mirrors the SDK's `TelemetryApi`/`RoutingApi` surface. */ -internal class RequestControllerImpl( +internal class QueryControllerImpl( private val commandSender: CommandSender, private val nodeManager: NodeManager, private val uiPrefs: UiPrefs, -) : RequestController { +) : QueryController { private val myNodeNum: Int get() = nodeManager.myNodeNum.value ?: 0 diff --git a/core/service/src/commonMain/kotlin/org/meshtastic/core/service/DirectRadioControllerImpl.kt b/core/service/src/commonMain/kotlin/org/meshtastic/core/service/RadioControllerImpl.kt similarity index 95% rename from core/service/src/commonMain/kotlin/org/meshtastic/core/service/DirectRadioControllerImpl.kt rename to core/service/src/commonMain/kotlin/org/meshtastic/core/service/RadioControllerImpl.kt index 837278c1b..0daded9f3 100644 --- a/core/service/src/commonMain/kotlin/org/meshtastic/core/service/DirectRadioControllerImpl.kt +++ b/core/service/src/commonMain/kotlin/org/meshtastic/core/service/RadioControllerImpl.kt @@ -34,10 +34,10 @@ import org.meshtastic.core.repository.NodeRepository import org.meshtastic.core.repository.NotificationManager import org.meshtastic.core.repository.PacketRepository import org.meshtastic.core.repository.PlatformAnalytics +import org.meshtastic.core.repository.QueryController import org.meshtastic.core.repository.RadioConfigRepository import org.meshtastic.core.repository.RadioController import org.meshtastic.core.repository.RadioInterfaceService -import org.meshtastic.core.repository.RequestController import org.meshtastic.core.repository.ServiceRepository import org.meshtastic.core.repository.UiPrefs import org.meshtastic.proto.ClientNotification @@ -48,7 +48,7 @@ import org.meshtastic.proto.ClientNotification * * Rather than implementing every command itself, this class **assembles** four focused collaborators — one per * sub-interface — and delegates to them via Kotlin interface delegation, mirroring the SDK's layered API design - * ([AdminController] → `AdminApi`, [MessagingController] → `RadioClient.send*`, [NodeController]/[RequestController] → + * ([AdminController] → `AdminApi`, [MessagingController] → `RadioClient.send*`, [NodeController]/[QueryController] → * `AdminApi`/`TelemetryApi`/`RoutingApi`). When the SDK is adopted, each collaborator becomes a thin adapter and this * class is the seam where they are wired together. * @@ -56,7 +56,7 @@ import org.meshtastic.proto.ClientNotification * surfacing, packet-id generation, location provisioning, and device-address switching. */ @Suppress("LongParameterList") -class DirectRadioControllerImpl( +class RadioControllerImpl( private val serviceRepository: ServiceRepository, nodeRepository: NodeRepository, private val commandSender: CommandSender, @@ -85,7 +85,7 @@ class DirectRadioControllerImpl( packetRepository, ), NodeController by NodeControllerImpl(commandSender, nodeManager, packetRepository, scope), - RequestController by RequestControllerImpl(commandSender, nodeManager, uiPrefs) { + QueryController by QueryControllerImpl(commandSender, nodeManager, uiPrefs) { // ── Connection State ──────────────────────────────────────────────────── @@ -101,7 +101,7 @@ class DirectRadioControllerImpl( // ── Packet ID & Location ──────────────────────────────────────────────── - override fun getPacketId(): Int = commandSender.generatePacketId() + override fun generatePacketId(): Int = commandSender.generatePacketId() override fun startProvideLocation() { locationManager.restart() diff --git a/core/service/src/commonTest/kotlin/org/meshtastic/core/service/DirectRadioControllerImplTest.kt b/core/service/src/commonTest/kotlin/org/meshtastic/core/service/RadioControllerImplTest.kt similarity index 99% rename from core/service/src/commonTest/kotlin/org/meshtastic/core/service/DirectRadioControllerImplTest.kt rename to core/service/src/commonTest/kotlin/org/meshtastic/core/service/RadioControllerImplTest.kt index 5b93ed625..ae4045bd7 100644 --- a/core/service/src/commonTest/kotlin/org/meshtastic/core/service/DirectRadioControllerImplTest.kt +++ b/core/service/src/commonTest/kotlin/org/meshtastic/core/service/RadioControllerImplTest.kt @@ -58,7 +58,7 @@ import kotlin.test.assertNull import kotlin.test.assertSame import kotlin.test.assertTrue -class DirectRadioControllerImplTest { +class RadioControllerImplTest { private val nodeRepository: NodeRepository = mock(MockMode.autofill) private val commandSender: CommandSender = mock(MockMode.autofill) @@ -80,10 +80,10 @@ class DirectRadioControllerImplTest { private fun createController( serviceRepository: ServiceRepository = ServiceRepositoryImpl(), myNodeNum: Int? = 1234, - ): DirectRadioControllerImpl { + ): RadioControllerImpl { every { nodeManager.myNodeNum } returns MutableStateFlow(myNodeNum) every { meshPrefs.deviceAddress } returns MutableStateFlow(null) - return DirectRadioControllerImpl( + return RadioControllerImpl( serviceRepository = serviceRepository, nodeRepository = nodeRepository, commandSender = commandSender, diff --git a/core/testing/src/commonMain/kotlin/org/meshtastic/core/testing/FakeRadioController.kt b/core/testing/src/commonMain/kotlin/org/meshtastic/core/testing/FakeRadioController.kt index 2880ed556..5ce42d269 100644 --- a/core/testing/src/commonMain/kotlin/org/meshtastic/core/testing/FakeRadioController.kt +++ b/core/testing/src/commonMain/kotlin/org/meshtastic/core/testing/FakeRadioController.kt @@ -153,14 +153,15 @@ class FakeRadioController : editSettingsCalled = true val scope = object : AdminEditScope { - override suspend fun setOwner(user: User) = setOwner(destNum, user, getPacketId()) + override suspend fun setOwner(user: User) = setOwner(destNum, user, generatePacketId()) - override suspend fun setConfig(config: Config) = setConfig(destNum, config, getPacketId()) + override suspend fun setConfig(config: Config) = setConfig(destNum, config, generatePacketId()) override suspend fun setModuleConfig(config: ModuleConfig) = - setModuleConfig(destNum, config, getPacketId()) + setModuleConfig(destNum, config, generatePacketId()) - override suspend fun setChannel(channel: Channel) = setRemoteChannel(destNum, channel, getPacketId()) + override suspend fun setChannel(channel: Channel) = + setRemoteChannel(destNum, channel, generatePacketId()) override suspend fun setFixedPosition(position: Position) = this@FakeRadioController.setFixedPosition(destNum, position) @@ -168,7 +169,7 @@ class FakeRadioController : scope.block() } - override fun getPacketId(): Int = 1 + override fun generatePacketId(): Int = 1 override fun startProvideLocation() { startProvideLocationCalled = true diff --git a/desktopApp/src/main/kotlin/org/meshtastic/desktop/di/DesktopKoinModule.kt b/desktopApp/src/main/kotlin/org/meshtastic/desktop/di/DesktopKoinModule.kt index bcffdcb1d..c877d43c7 100644 --- a/desktopApp/src/main/kotlin/org/meshtastic/desktop/di/DesktopKoinModule.kt +++ b/desktopApp/src/main/kotlin/org/meshtastic/desktop/di/DesktopKoinModule.kt @@ -58,13 +58,13 @@ import org.meshtastic.core.repository.NeighborInfoResponseProvider import org.meshtastic.core.repository.NodeController import org.meshtastic.core.repository.NotificationManager import org.meshtastic.core.repository.PlatformAnalytics +import org.meshtastic.core.repository.QueryController import org.meshtastic.core.repository.RadioController import org.meshtastic.core.repository.RadioTransportFactory -import org.meshtastic.core.repository.RequestController import org.meshtastic.core.repository.ServiceRepository import org.meshtastic.core.repository.ServiceStateWriter import org.meshtastic.core.repository.TracerouteResponseProvider -import org.meshtastic.core.service.DirectRadioControllerImpl +import org.meshtastic.core.service.RadioControllerImpl import org.meshtastic.core.service.ServiceRepositoryImpl import org.meshtastic.desktop.DesktopBuildConfig import org.meshtastic.desktop.DesktopNotificationManager @@ -177,7 +177,7 @@ private fun desktopPlatformStubsModule() = module { ) } single { - DirectRadioControllerImpl( + RadioControllerImpl( serviceRepository = get(), nodeRepository = get(), commandSender = get(), @@ -199,7 +199,7 @@ private fun desktopPlatformStubsModule() = module { single { get() } single { get() } single { get() } - single { get() } + single { get() } single { when (DesktopOS.current()) { DesktopOS.Linux -> LinuxNotificationSender() diff --git a/docs/en/developer/architecture.md b/docs/en/developer/architecture.md index 6d1f53a49..15ef7a124 100644 --- a/docs/en/developer/architecture.md +++ b/docs/en/developer/architecture.md @@ -130,9 +130,9 @@ focused sub-interfaces so callers can depend on just the slice they need: | `AdminController` | Config, channels, owner, device lifecycle, `editSettings { }` transactions | | `MessagingController` | Send packets, reactions, shared contacts | | `NodeController` | Favorite, ignore, mute, remove nodes | -| `RequestController` | Telemetry, traceroute, position/user-info queries | +| `QueryController` | Telemetry, traceroute, position/user-info queries | -`DirectRadioControllerImpl` (`core:service`) is the in-process composition root for all targets +`RadioControllerImpl` (`core:service`) is the in-process composition root for all targets (Desktop, iOS, single-process Android). It assembles the four sub-controllers via Kotlin interface delegation and adds the cross-cutting concerns (connection state, packet-id, location, device-address switching). Commands are direct suspend calls; admin writes are fire-and-forget diff --git a/feature/firmware/src/commonMain/kotlin/org/meshtastic/feature/firmware/ota/Esp32OtaUpdateHandler.kt b/feature/firmware/src/commonMain/kotlin/org/meshtastic/feature/firmware/ota/Esp32OtaUpdateHandler.kt index 603fae5a3..84e6bb996 100644 --- a/feature/firmware/src/commonMain/kotlin/org/meshtastic/feature/firmware/ota/Esp32OtaUpdateHandler.kt +++ b/feature/firmware/src/commonMain/kotlin/org/meshtastic/feature/firmware/ota/Esp32OtaUpdateHandler.kt @@ -190,7 +190,7 @@ class Esp32OtaUpdateHandler( val myInfo = nodeRepository.myNodeInfo.value ?: return val myNodeNum = myInfo.myNodeNum Logger.i { "ESP32 OTA: Triggering reboot OTA mode $mode with hash" } - radioController.requestRebootOta(radioController.getPacketId(), myNodeNum, mode, hash) + radioController.requestRebootOta(radioController.generatePacketId(), myNodeNum, mode, hash) } /** diff --git a/feature/map/src/commonMain/kotlin/org/meshtastic/feature/map/BaseMapViewModel.kt b/feature/map/src/commonMain/kotlin/org/meshtastic/feature/map/BaseMapViewModel.kt index c07a6339b..3c94aa76b 100644 --- a/feature/map/src/commonMain/kotlin/org/meshtastic/feature/map/BaseMapViewModel.kt +++ b/feature/map/src/commonMain/kotlin/org/meshtastic/feature/map/BaseMapViewModel.kt @@ -162,7 +162,7 @@ open class BaseMapViewModel( safeLaunch(context = ioDispatcher, tag = "sendDataPacket") { radioController.sendMessage(p) } } - fun generatePacketId(): Int = radioController.getPacketId() + fun generatePacketId(): Int = radioController.generatePacketId() data class MapFilterState( val onlyFavorites: Boolean, diff --git a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/detail/CommonNodeRequestActions.kt b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/detail/CommonNodeRequestActions.kt index 917f71ff0..0c1b695d7 100644 --- a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/detail/CommonNodeRequestActions.kt +++ b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/detail/CommonNodeRequestActions.kt @@ -67,7 +67,7 @@ constructor( override suspend fun requestNeighborInfo(destNum: Int, longName: String) { Logger.i { "Requesting NeighborInfo for '$destNum'" } - val packetId = radioController.getPacketId() + val packetId = radioController.generatePacketId() radioController.requestNeighborInfo(packetId, destNum) _lastRequestNeighborTimes.update { it + (destNum to nowMillis) } showFeedback(UiText.Resource(Res.string.requesting_from, Res.string.neighbor_info, longName)) @@ -81,7 +81,7 @@ constructor( override suspend fun requestTelemetry(destNum: Int, longName: String, type: TelemetryType) { Logger.i { "Requesting telemetry for '$destNum'" } - val packetId = radioController.getPacketId() + val packetId = radioController.generatePacketId() radioController.requestTelemetry(packetId, destNum, type.ordinal) val typeRes = @@ -100,7 +100,7 @@ constructor( override suspend fun requestTraceroute(destNum: Int, longName: String) { Logger.i { "Requesting traceroute for '$destNum'" } - val packetId = radioController.getPacketId() + val packetId = radioController.generatePacketId() radioController.requestTraceroute(packetId, destNum) _lastTracerouteTime.value = nowMillis showFeedback(UiText.Resource(Res.string.requesting_from, Res.string.traceroute, longName)) diff --git a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/detail/NodeDetailViewModel.kt b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/detail/NodeDetailViewModel.kt index 2129058f5..14681bd34 100644 --- a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/detail/NodeDetailViewModel.kt +++ b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/detail/NodeDetailViewModel.kt @@ -39,7 +39,7 @@ import org.meshtastic.core.model.NodeAddress import org.meshtastic.core.model.SessionStatus import org.meshtastic.core.navigation.Route import org.meshtastic.core.navigation.SettingsRoute -import org.meshtastic.core.repository.RequestController +import org.meshtastic.core.repository.QueryController import org.meshtastic.core.resources.Res import org.meshtastic.core.resources.UiText import org.meshtastic.core.resources.connect_radio_for_remote_admin @@ -81,7 +81,7 @@ class NodeDetailViewModel( private val savedStateHandle: SavedStateHandle, private val nodeManagementActions: NodeManagementActions, private val nodeRequestActions: NodeRequestActions, - private val requestController: RequestController, + private val queryController: QueryController, private val getNodeDetailsUseCase: GetNodeDetailsUseCase, private val ensureRemoteAdminSession: EnsureRemoteAdminSessionUseCase, private val observeRemoteAdminSessionStatus: ObserveRemoteAdminSessionStatusUseCase, @@ -174,7 +174,7 @@ class NodeDetailViewModel( /** * Re-fetch device metadata (firmware/edition/role) for [destNum]. Refreshes the session passkey as a side effect. */ - fun refreshMetadata(destNum: Int) = viewModelScope.launch { requestController.refreshMetadata(destNum) } + fun refreshMetadata(destNum: Int) = viewModelScope.launch { queryController.refreshMetadata(destNum) } /** * Ensure a remote-admin session passkey is fresh, then request navigation to the remote-admin screen. Surfaces a diff --git a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/detail/NodeManagementActions.kt b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/detail/NodeManagementActions.kt index 73133b49a..dc6e57750 100644 --- a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/detail/NodeManagementActions.kt +++ b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/detail/NodeManagementActions.kt @@ -60,7 +60,7 @@ constructor( open suspend fun removeNode(nodeNum: Int) { Logger.i { "Removing node '$nodeNum'" } - val packetId = radioController.getPacketId() + val packetId = radioController.generatePacketId() radioController.removeByNodenum(packetId, nodeNum) nodeRepository.deleteNode(nodeNum) } diff --git a/feature/node/src/commonTest/kotlin/org/meshtastic/feature/node/detail/HandleNodeActionTest.kt b/feature/node/src/commonTest/kotlin/org/meshtastic/feature/node/detail/HandleNodeActionTest.kt index aaf26a753..276cb4be9 100644 --- a/feature/node/src/commonTest/kotlin/org/meshtastic/feature/node/detail/HandleNodeActionTest.kt +++ b/feature/node/src/commonTest/kotlin/org/meshtastic/feature/node/detail/HandleNodeActionTest.kt @@ -34,7 +34,7 @@ import org.meshtastic.core.domain.usecase.session.EnsureRemoteAdminSessionUseCas import org.meshtastic.core.domain.usecase.session.ObserveRemoteAdminSessionStatusUseCase import org.meshtastic.core.model.Node import org.meshtastic.core.model.SessionStatus -import org.meshtastic.core.repository.RequestController +import org.meshtastic.core.repository.QueryController import org.meshtastic.core.ui.util.SnackbarManager import org.meshtastic.feature.node.component.NodeMenuAction import org.meshtastic.feature.node.domain.usecase.GetNodeDetailsUseCase @@ -51,7 +51,7 @@ class HandleNodeActionTest { private val testDispatcher = UnconfinedTestDispatcher() private val nodeManagementActions: NodeManagementActions = mock() private val nodeRequestActions: NodeRequestActions = mock() - private val requestController: RequestController = mock() + private val queryController: QueryController = mock() private val getNodeDetailsUseCase: GetNodeDetailsUseCase = mock() private val ensureRemoteAdminSession: EnsureRemoteAdminSessionUseCase = mock() private val observeRemoteAdminSessionStatus: ObserveRemoteAdminSessionStatusUseCase = mock() @@ -93,7 +93,7 @@ class HandleNodeActionTest { savedStateHandle = SavedStateHandle(mapOf("destNum" to 1234)), nodeManagementActions = nodeManagementActions, nodeRequestActions = nodeRequestActions, - requestController = requestController, + queryController = queryController, getNodeDetailsUseCase = getNodeDetailsUseCase, ensureRemoteAdminSession = ensureRemoteAdminSession, observeRemoteAdminSessionStatus = observeRemoteAdminSessionStatus, diff --git a/feature/node/src/commonTest/kotlin/org/meshtastic/feature/node/detail/NodeDetailViewModelTest.kt b/feature/node/src/commonTest/kotlin/org/meshtastic/feature/node/detail/NodeDetailViewModelTest.kt index 52af1674c..e1bf663be 100644 --- a/feature/node/src/commonTest/kotlin/org/meshtastic/feature/node/detail/NodeDetailViewModelTest.kt +++ b/feature/node/src/commonTest/kotlin/org/meshtastic/feature/node/detail/NodeDetailViewModelTest.kt @@ -42,7 +42,7 @@ import org.meshtastic.core.domain.usecase.session.ObserveRemoteAdminSessionStatu import org.meshtastic.core.model.Node import org.meshtastic.core.model.SessionStatus import org.meshtastic.core.navigation.SettingsRoute -import org.meshtastic.core.repository.RequestController +import org.meshtastic.core.repository.QueryController import org.meshtastic.core.resources.Res import org.meshtastic.core.resources.UiText import org.meshtastic.core.resources.connect_radio_for_remote_admin @@ -64,7 +64,7 @@ class NodeDetailViewModelTest { private lateinit var viewModel: NodeDetailViewModel private val nodeManagementActions: NodeManagementActions = mock() private val nodeRequestActions: NodeRequestActions = mock() - private val requestController: RequestController = mock() + private val queryController: QueryController = mock() private val getNodeDetailsUseCase: GetNodeDetailsUseCase = mock() private val ensureRemoteAdminSession: EnsureRemoteAdminSessionUseCase = mock() private val observeRemoteAdminSessionStatus: ObserveRemoteAdminSessionStatusUseCase = mock() @@ -97,7 +97,7 @@ class NodeDetailViewModelTest { savedStateHandle = SavedStateHandle(if (nodeId != null) mapOf("destNum" to nodeId) else emptyMap()), nodeManagementActions = nodeManagementActions, nodeRequestActions = nodeRequestActions, - requestController = requestController, + queryController = queryController, getNodeDetailsUseCase = getNodeDetailsUseCase, ensureRemoteAdminSession = ensureRemoteAdminSession, observeRemoteAdminSessionStatus = observeRemoteAdminSessionStatus,