From a85e2828244f9be7d7ffc6fae084db1fd489fa3f Mon Sep 17 00:00:00 2001 From: James Rich Date: Tue, 17 Mar 2026 11:06:40 -0500 Subject: [PATCH] refactor(core:service): Use MeshServiceOrchestrator to abstract service wiring --- .../tracks/extract_services_20260317/plan.md | 8 +-- .../meshtastic/core/service/MeshService.kt | 21 ++------ .../core/service/MeshServiceOrchestrator.kt | 2 + .../service/MeshServiceOrchestratorTest.kt | 52 +++++++++++++++++++ 4 files changed, 61 insertions(+), 22 deletions(-) create mode 100644 core/service/src/commonTest/kotlin/org/meshtastic/core/service/MeshServiceOrchestratorTest.kt diff --git a/conductor/tracks/extract_services_20260317/plan.md b/conductor/tracks/extract_services_20260317/plan.md index 6174b2d63..4a5906c88 100644 --- a/conductor/tracks/extract_services_20260317/plan.md +++ b/conductor/tracks/extract_services_20260317/plan.md @@ -12,10 +12,10 @@ - [x] Task: Move Android `Service` implementations to `core:service/androidMain` [965def0] - [x] Move the files - [x] Update imports and Koin injections -- [ ] Task: Abstract shared service logic into `core:service/commonMain` - - [ ] Write failing tests for abstracted shared logic (TDD Red) - - [ ] Extract interfaces and platform-agnostic logic (TDD Green) - - [ ] Refactor the implementations to use these shared abstractions +- [~] Task: Abstract shared service logic into `core:service/commonMain` + - [x] Write failing tests for abstracted shared logic (TDD Red) + - [x] Extract interfaces and platform-agnostic logic (TDD Green) + - [x] Refactor the implementations to use these shared abstractions - [ ] Task: Conductor - User Manual Verification 'Extraction to core:service' (Protocol in workflow.md) ## Phase 3: Extraction to `core:network` diff --git a/core/service/src/androidMain/kotlin/org/meshtastic/core/service/MeshService.kt b/core/service/src/androidMain/kotlin/org/meshtastic/core/service/MeshService.kt index 6141d9862..675ed0413 100644 --- a/core/service/src/androidMain/kotlin/org/meshtastic/core/service/MeshService.kt +++ b/core/service/src/androidMain/kotlin/org/meshtastic/core/service/MeshService.kt @@ -78,7 +78,7 @@ class MeshService : Service() { private val connectionManager: MeshConnectionManager by inject() - private val serviceNotifications: MeshServiceNotifications by inject() + private val orchestrator: MeshServiceOrchestrator by inject() private val router: MeshRouter by inject() @@ -121,24 +121,8 @@ class MeshService : Service() { throw e } Logger.i { "Creating mesh service" } - serviceNotifications.initChannels() - packetHandler.start(serviceScope) - router.start(serviceScope) - nodeManager.start(serviceScope) - connectionManager.start(serviceScope) - messageProcessor.start(serviceScope) - commandSender.start(serviceScope) - - serviceScope.handledLaunch { radioInterfaceService.connect() } - - radioInterfaceService.receivedData - .onEach { bytes -> messageProcessor.handleFromRadio(bytes, nodeManager.myNodeNum) } - .launchIn(serviceScope) - - serviceRepository.serviceAction.onEach(router.actionHandler::onServiceAction).launchIn(serviceScope) - - nodeManager.loadCachedNodeDB() + orchestrator.start() } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { @@ -207,6 +191,7 @@ class MeshService : Service() { override fun onDestroy() { Logger.i { "Destroying mesh service" } ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE) + orchestrator.stop() serviceJob.cancel() super.onDestroy() } diff --git a/core/service/src/commonMain/kotlin/org/meshtastic/core/service/MeshServiceOrchestrator.kt b/core/service/src/commonMain/kotlin/org/meshtastic/core/service/MeshServiceOrchestrator.kt index 0bcfb62d6..0faf332a8 100644 --- a/core/service/src/commonMain/kotlin/org/meshtastic/core/service/MeshServiceOrchestrator.kt +++ b/core/service/src/commonMain/kotlin/org/meshtastic/core/service/MeshServiceOrchestrator.kt @@ -22,6 +22,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import org.koin.core.annotation.Single import org.meshtastic.core.common.util.handledLaunch import org.meshtastic.core.repository.CommandSender import org.meshtastic.core.repository.MeshConnectionManager @@ -42,6 +43,7 @@ import org.meshtastic.core.repository.ServiceRepository * All injected dependencies are `commonMain` interfaces with real implementations in `core:data`. */ @Suppress("LongParameterList") +@Single class MeshServiceOrchestrator( private val radioInterfaceService: RadioInterfaceService, private val serviceRepository: ServiceRepository, diff --git a/core/service/src/commonTest/kotlin/org/meshtastic/core/service/MeshServiceOrchestratorTest.kt b/core/service/src/commonTest/kotlin/org/meshtastic/core/service/MeshServiceOrchestratorTest.kt new file mode 100644 index 000000000..b8f174271 --- /dev/null +++ b/core/service/src/commonTest/kotlin/org/meshtastic/core/service/MeshServiceOrchestratorTest.kt @@ -0,0 +1,52 @@ +package org.meshtastic.core.service + +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlin.test.Test +import kotlin.test.assertFalse +import kotlin.test.assertTrue +import org.meshtastic.core.repository.* + +class MeshServiceOrchestratorTest { + + @Test + fun testStartWiresComponents() { + val radioInterfaceService = mockk(relaxed = true) + val serviceRepository = mockk(relaxed = true) + val packetHandler = mockk(relaxed = true) + val nodeManager = mockk(relaxed = true) + val messageProcessor = mockk(relaxed = true) + val commandSender = mockk(relaxed = true) + val connectionManager = mockk(relaxed = true) + val router = mockk(relaxed = true) + val serviceNotifications = mockk(relaxed = true) + + every { radioInterfaceService.receivedData } returns MutableSharedFlow() + every { serviceRepository.serviceAction } returns MutableSharedFlow() + + val orchestrator = MeshServiceOrchestrator( + radioInterfaceService, + serviceRepository, + packetHandler, + nodeManager, + messageProcessor, + commandSender, + connectionManager, + router, + serviceNotifications + ) + + assertFalse(orchestrator.isRunning) + orchestrator.start() + assertTrue(orchestrator.isRunning) + + verify { serviceNotifications.initChannels() } + verify { packetHandler.start(any()) } + verify { nodeManager.loadCachedNodeDB() } + + orchestrator.stop() + assertFalse(orchestrator.isRunning) + } +}