An automated medication dispensing system built in Kotlin with Jetpack Compose, talking to hardware through the NDK.
技術構成
What the system does#
Ward Stock is an automated medication dispensing system for hospital wards. It integrates with RESTful APIs to pull the medication list and post every dispense back to the central system. The app is written in Kotlin with the entire UI in Jetpack Compose, running on purpose-built hardware with the dispensing mechanism inside the unit.
Talking to hardware through the NDK#
- The vendor library that drives the mechanism is C, so it is called through the Android NDK over JNI.
- Every native call sits behind a single Kotlin interface, which keeps hardware details from leaking upward into the UI layer.
- All hardware commands run on an I/O dispatcher — opening a drawer takes seconds, and blocking the UI thread is never acceptable.
kotlin
external fun openDrawer(slot: Int): Int
private val _state = MutableStateFlow<DispenseState>(DispenseState.Idle)
val state: StateFlow<DispenseState> = _state.asStateFlow()
fun dispense(slot: Int) = viewModelScope.launch(Dispatchers.IO) {
_state.value = DispenseState.Running(slot)
val outcome = runCatching { openDrawer(slot) }
_state.value = outcome.fold(
onSuccess = { DispenseState.Done(slot) },
onFailure = { DispenseState.Failed(it.message) },
)
}- Model every machine state, including the ways a dispense can fail
- Build the Compose UI so targets stay easy to hit with gloves on
- Integrate the RESTful APIs for stock lists and dispense records
- Keep working through short network drops on the ward
State and asynchronous work#
A machine that hands medication to a patient has to say clearly what it is doing, and whether it succeeded.
- A stateful architecture keeps the screen reflecting one machine state — never two competing sources of truth.
Coroutinescarry everything that has to wait: API calls and the dispensing mechanism alike.- Every state on screen traces back to a real event, so a single screenshot is usually enough to debug a report from the ward.