Skip to content

kondalrao/fsm_engine

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

18 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Finite State Machine (FSM) Engine

This is an implementation of Finite State Machine in Python. There are many versions of this model but here is another version.

The idea here to view each FSM as an independent container for processing events. These FSM containers can run in different threads/processes/nodes. Since the data and event are together, the event dispatch can send the pair to any FSM container. The trick here is to make sure that the FSM container doesn't maintiain any context of the data. So there is a centralized data/context repository which is used by the FSM containers and the event dispatch would just pick the context-event pair and send it to a free FSM container.

The order of state, event matching is done as following sequence. (state, event) -> (action, next_state) (state, any) -> (action, next_state) (any, event) -> (action, next_state) (any, any) -> (action, next_state)

A lot of work and change of design is needed. Any help/suggestions are very helpful.

TODO:

  • Use other FSM containers other than XML like yaml/json.
  • Enhance the event dispatch mechanism.

###Simply create an fsm in XML.

<?xml version="1.0"?>
<fsm name='calc'>
    <import>simple_calc_actions</import>
    <states prefix="">
        <init>DIGIT1</init>
        <state>DIGIT1</state>
        <state>DIGIT2</state>
    </states>
    <events prefix="">
        <event>DIGIT</event>
        <event>OPERATOR</event>
        <event>WS</event>
        <event>RESULT</event>
    </events>
    <actions prefix="">
        <action>operand1</action>
        <action>operand2</action>
        <action>operator</action>
        <action>result</action>
    </actions>
    <tables>
        <table state='DIGIT1' event='DIGIT' action='operand1' next_state='DIGIT1'/>
        <table state='DIGIT1' event='OPERATOR' action='operator' next_state='DIGIT2'/>
        <table state='DIGIT2' event='DIGIT' action='operand2' next_state='DIGIT2'/>
        <table state='DIGIT2' event='RESULT' action='result' next_state='DIGIT1'/>
        <table state='Any' event='WS' action='null' next_state='Same'/>
        <table state='Any' event='Any' action='log' next_state='Same'/>
    </tables>
</fsm>

###Define actions

num1 = 0
num2 = 0
oper = ''
res = ''

def log(context):
    print ("Error Event Received.")

def null(context):
    pass

def operand1(context):
    global num1
    
    num1 = (num1 * 10) + int(context.data)

def operand2(context):
    global num2

    num2 = (num2 * 10) + int(context.data)

def operator(context):
    global oper

    oper = context.data

def result(context):
    global num1, num2, oper, res

    res = 'NOP'

    if oper == '+':
        res = num1 + num2
    elif oper == '-':
        res = num1 - num2
    elif oper == '*':
        res = num1 * num2
    elif oper == '/':
        if num2 == 0:
            res = 'NOP'
        else:
            res = float(num1) / num2

    print (' ' + str(res))

    # resetting data holders
    num1 = num2 = 0
    oper = ''

###Register and Fire events

import sys
sys.path.append('../..')

from fsm_engine import FSM

fsm_obj = None

def get_datatype(data):
    if data in ['+', '-', '*', '/']:
        return 'OPERATOR'
    elif data.isdigit():
        return 'DIGIT'
    elif data.isspace():
        return 'WS'
    elif data == '=' or data == '\n':
        return 'RESULT'

def initialize():
    global fsm_obj

    fsm_obj = FSM('calc.xml')

def main():
    initialize()

    fd = open('calc.txt')

    for line in fd.readlines():
        print (line.strip('\n'), end=''),

        for ch in line:
            if ch is not '\n':
                ev = get_datatype(ch)
                if ev is not 'WS' or ev is not 'RESULT':
                    fsm_obj.dispatch(event=ev, data=ch)
                else:
                    fsm_obj.dispatch(event=ev)

if __name__ == '__main__':
    main()

######Sample text file used (calc.txt)

1 + 2 =
123 + 321 =
10/2=
10/0=