State Machine

State Machine Definitions

According to the Wikipedia, a state machine has the following definition:

A finite-state machine (FSM) or finite-state automaton (FSA, plural: automata), finite automaton, or simply a state machine, is a mathematical model of computation. It is an abstract machine that can be in exactly one of a finite number of states at any given time. The FSM can change from one state to another in response to some external inputs; the change from one state to another is called a transition. An FSM is defined by a list of its states, its initial state, and the conditions for each transition.

-- Finite-state machine

State Machines are widely used in the automation field, this is a concept in which you can define a system that is continuously transitioning between states in order to perform some tasks or operations, in a automated or semiautomated way.

With Rackio you can use a declarative approach to define your custom state machines.


Working with State Machines

Let's say you want to code a two steps state machine, you can use Rackio and use the following syntax:

import logging
from rackio import RackioStateMachine, State


class TwoStep(RackioStateMachine):

    # states

    state1  = State('State1', initial=True)
    state2  = State('State2')

    # transitions

    forward = state1.to(state2)
    back = state2.to(state1)

    # parameters

    count = 0

    def on_back(self):

        self.count = 0

    def while_state1(self):

        self.count += 1

        logging.warning("{}: {}".format(self.name, self.count))
        if self.count == 5:
            self.forward()

    def while_state2(self):

        self.count += 1

        logging.warning("{}: {}".format(self.name, self.count))
        if self.count >= 10:
            self.back()

The RackioStateMachine class is the based class for building state machines, you will have to inherit to define your own custom state machines. The State class will define each of the states defined for your custom state machine, the initial argument must be set to true to set the first initial state for each instance of this machine class once rackio is up and running.

The transitions between each defined state must be linked using the to method of each state, and must be assigned to a class attribute. So this state machine class will have the states state1 and state2, and will have the following transitions forward and back.

You can define custom behaviour for your state machine while remaining on a single state, or on a transition (changing from one state to another), for this purpose you must define methods for this class using the while_ and on_ syntax for the method names respectively.

Once you have defined your state machine, you can create instances of this class and attach it to the rackio engine for continous execution.

app = Rackio()

machine1 = TwoStep("machine 1")
machine2 = TwoStep("machine 2")

app.append_machine(machine1, 1)
app.append_machine(machine2, 2)

if __name__ == "__main__":

    app.set_log(file="app.log")
    app.run()

Using the method append_machine you will register a new state machine as plugin to be executed by the rackio engine, the second parameter is the machine execution time in seconds, meaning that this machine will be executed each n seconds as time interval.

In the log file you see information about the execution of the state machines, and you will find also details about any execution error found.

Reserved attributes

In order to use other features of the framework, the RackioStateMachine comes with three class attributes for module interaction. You can make use of them using the following syntax.

class TwoStep(RackioStateMachine):

    def while_some_state(self):

        tag_engine = self.tag_engine
        logger_engine = self.logger_engine
        query_logger = query_logger

The tag_engine is an instance of the tag data engine, you can use it to read or write tags, this way you can perform transitions from another rackio process or even better control the execution of another rackio process.

The logger_engine can be used to log tags values or any other custom structure.

The query_logger can be used for querying already saved trends, signals or waveforms, and even trends history.