Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Discussion options

This is the child machine which handles messages from/to an external bluetooth device, however sending an OutgoingMessage via

machine.processEvent(OutgoingMessage(command))

From a level higher triggers

Recovering from a crash: java.lang.IllegalStateException: Transitioning to state ExpectingResponse(outgoingMessage=OutgoingMessage(data=PingCard(callback=CancellableContinuation(DispatchedContinuation[Dispatchers.IO, Continuation at ...{Active}..., cardType=6)), queue=[], retryCount=0) from another state machine is not possible

I only have one createStateMachine invocation, so I'm not sure what the error is referring to.

state("ble") {
    addInitialState(CommandStates.Waiting) {
        transition<IncomingBleMessage> {
            onTriggered {
                val value = it.event.data
                val parsed = parseSingle(value)
                Timber.tag("StateMachine").w { "Got errant message: ${parsed.commandByte} with data: ${value.dump()}" }
                }
            }
        }
        transitionOn<OutgoingMessage> {
            targetState = {
                CommandStates.ExpectingResponse(event, listOf())
            }
        }
    }

    dataState<CommandStates.ExpectingResponse> {
        val supervisor = SupervisorJob()
        onEntry {
            bleManager.writeToReader(data.outgoingMessage.data)
        }

        val state = this
        transitionConditionally<IncomingBleMessage> {
            type = TransitionType.EXTERNAL
            direction = {
                val value = event.data
                val parsed = parseSingle(value)
                when (val commandByte = (parsed.cmd - 1).toByte()) {
                    state.data.outgoingMessage.data.commandByte -> {
                        if (parsed.isSuccessful()) {
                            state.data.outgoingMessage.data.callback.resume(parsed) {
                                Timber.tag("StateMachine").w { "Resumed too late! $commandByte with data: ${value.dump()}" }
                            }
                            popQueue(state)
                        } else {
                            targetState(CommandStates.ExpectingResponse(state.data.outgoingMessage, state.data.queue, state.data.retryCount + 1))
                        }

                    }

                    else -> {
                        Timber.w { "Got errant message: $commandByte with data: ${value.dump()}" }
                        noTransition()
                    }
                }
            }
        }

        transitionOn<OutgoingMessage> {
            type = TransitionType.EXTERNAL
            targetState = {
                CommandStates.ExpectingResponse(state.data.outgoingMessage, state.data.queue + event)
            }
        }
    }
}


private suspend fun <T : Event> EventAndArgument<T>.popQueue(
    state: DataState<CommandStates.ExpectingResponse>
) = if (state.data.queue.isEmpty()) {
    targetState(CommandStates.Waiting)
} else {
    targetState(
        CommandStates.ExpectingResponse(
            state.data.queue[0],
            state.data.queue.drop(1)
        )
    )
}

data class OutgoingMessage(override val data: Command): DataEvent<Command>
data class Command(val callback: CancellableContinuation<ParsingState.ParsingSuccessful>)

sealed class CommandStates: DefaultState() {
    data object Waiting : CommandStates()
    data class ExpectingResponse(
        val outgoingMessage: OutgoingMessage,
        val queue: List<OutgoingMessage>,
        val retryCount: Int = 0,
    ): CommandStates()
}
You must be logged in to vote

Yes you can pass your data along with Events and store it in States (states are mutable by nature).
I dont think that DataExtractor is suitable for data transformation. DataState+DataEvent api is a implementation of typical data-passing use case. If it is too strict and does not fit your needs, you can use simple States and Events just storing your data in State fields and updating its manually from incoming Events.

Replies: 1 comment · 6 replies

Comment options

Typically this exception is thrown if transition targets state from another different state machine

If you have only one state machine and single invocation of createStateMachine, maybe your machine was recreated because of android configuration change, (effectively this is another StateMachine instance in such case)?

Otherwise this could be false triggering of an error. Minimal sample that reproduces the error could help.

You must be logged in to vote
6 replies
@reactormonk
Comment options

Oh sorry, didn't realize default for intellij is private repo on the "share" button 😅. Fixed.

@nsk90
Comment options

I have run your sample, the problem is that you try to create new states at machine runtime. This is not correct. Final state machines define all the possible states before they run.

One of the problem parts

 targetState = {
      CommandStates.ExpectingResponse(event, listOf())
  }
@reactormonk
Comment options

Understood, so I can only use the data provided by an Event, which then gets passed to the machinery? Or rather, I would use a DataExtractor to modify the data at will before it gets shoved into a new DataState?

@nsk90
Comment options

Yes you can pass your data along with Events and store it in States (states are mutable by nature).
I dont think that DataExtractor is suitable for data transformation. DataState+DataEvent api is a implementation of typical data-passing use case. If it is too strict and does not fit your needs, you can use simple States and Events just storing your data in State fields and updating its manually from incoming Events.

Answer selected by reactormonk
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Category
🙏
Q&A
Labels
None yet
2 participants
Morty Proxy This is a proxified and sanitized view of the page, visit original site.