mardi 19 mai 2020

Simple State Machine and Transition Table

The goal is to have well defined state transitions, and the ability to provide the next event to execute.

I'd like to know if this is a proper implementation of State Machine, considering how states and transitionTable are defined, and how I handle event as input and output via update.

In many examples I cannot clearly define verbiage for state, so I defined them as verbs (notice ing suffix), as if it represents the ongoing progress of workflow. This may be wrong..

I also defined multiple events that could occur for a single state. For example, GetDeviceStatus and GetIntegrityStatus events both occur in RetrievingStatus state. This is also the case for Downloading state. You can see in the cases, when determining the next event, I need to check what the previous state was first.

If flawed, what are the pitfalls to my design, and how could it be improved? thanks.

enum ExampleState {
    case Initiate
    case Authorizing // getAuthToken
    case RetrievingStatus // getUpdateStatus, getIntegrityCheckStatus
    case Downloading // downloadParam, sendParamToPump->RetrieveStatus
    case Confirming  // confirm
    case End
}

enum ExampleEvent {
    case InitiateSequence
    case GetAuthToken
    case GetDeviceStatus
    case GetIntegrityStatus
    case DownloadFromServer
    case DownloadToDevice
    case Confirm
}

class StateMachine {

var oldState: ExampleState!
var currentState: ExampleState!
var currentEvent: ExampleEvent!

var table: [ExampleState: [ExampleEvent: ExampleState]] = [.Initiate:         [.InitiateSequence:   .Authorizing],
                                                           .Authorizing:      [.GetAuthToken:       .RetrievingStatus],
                                                           .RetrievingStatus: [.GetDeviceStatus:    .Downloading, .GetIntegrityStatus: .Confirming],
                                                           .Downloading:      [.DownloadFromServer: .Downloading, .DownloadToDevice: .RetrievingStatus],
                                                           .Confirming:       [.Confirm:            .End]]

func update(event: ExampleEvent) -> ExampleEventExecutor? {

    let transitionState = table[currentState]![event]!
    let oldState = currentState

    switch (transitionState) {

        case .Initiate:
            currentState = .Initiate
            return InitiateSequence()

        case .Authorizing:
            currentState = .Authorizing
            return GetAuthToken()

        case .RetrievingStatus:
            currentState = .RetrievingStatus
            switch (oldState) {
            case .Authorizing: return GetDeviceStatus()
            case .Downloading: return GetIntegrityStatus()
            default: return nil
            }

        case .Downloading:
            currentState = .Downloading
            switch (oldState) {
            case .RetrievingStatus: return DownloadFromServer()
            case .Downloading: return DownloadToDevice()
            default: return nil
            }

        case .Confirming:
            currentState = .Confirming
            return Confirm()

        case .End:
            currentState = .End
            return nil
    }

}

}

Aucun commentaire:

Enregistrer un commentaire