Experience with KA (FSM) in a Python web interface

    Introduction

    Recently, I needed to attach a web interface to one program.
    More precisely, there was already no web interface, but it worked in only one direction, it provided the results of the program core. And it was required to introduce interactivity.

    The program in its original form translated the representation of a certain block of the scheme written in XML files into its graphical representation. The core of the program read XML files, displayed the block diagram blocks described in these files into objects, and then from these objects a picture was drawn and displayed through a browser.

    In accordance with the new requirements, it was necessary to add interactive editing. And this means that it was necessary to add links, forms, process errors and issue messages about them, check the data entered into the forms, take into account browser buttons that allow you to move through the history and so on. And most importantly, do not get confused in all this.

    I have a habit for complicated programs to draw flowcharts. For a program operating in a mode where there are expressed states in which the program can be located, events to which it should respond (for example, button presses) and transitions from one state to another, the concept of a finite state machine or spacecraft is ideal, as it will be abroad FSM or Finit State Machine. Once, even under the DOS, I figured out the menus of one sophisticated program only when I drew a diagram of the SC state diagram for it.

    What is a spacecraft (state machine)

    Those who know what it is, can safely skip this section.

    KA, this is such an approach to the description of the operation of automata. It distinguishes between the states depicted on the diagrams in the form of circles with an inscription inside and transitions, which are drawn in the form of arrows connecting the circles-states. Arrows can do the same trick as returning to the same state. Arrows also usually have designations.

    The work of the spacecraft, in fact, consists in transitions from one state to another.

    What can the application of the spacecraft approach give in this case?

    In this case, this is to describe the operation of the web interface

    - You can draw a readable diagram of the state of the interface.
    - You can write code organized by this diagram.
    - It will be easy to move from code to chart and vice versa.
    - Easy to understand / add / remove states and actions.
    - You can relatively easily cover all the transitions of the interface, which makes testing easier.
    - You can easily create a state table, which makes testing easier.

    Wednesday

    In developing this software, the following software was used:
    Language: Python 2.5.4
    Web Server: Cherrypy 3.1.1

    Code Implementation and Operation

    KA code organization

    The fact that cherrypy was used as a web server dictated some requirements for the organization of the code.

    The code was organized like this: The entire web interface is implemented in the HomePage class.
    The nodal point, both for the web interface and for the CA, the index () method of the

    CA is also implemented in the HomePage class. The spacecraft code can be divided into three parts:

    1. The code in the index () method. It receives the 'tostate' parameter from the browser and calls control methods, implements the control loop of the spacecraft.

    2. Spacecraft control methods. fsm_transition (self, to_state), fsm_process_methods (self, fsm_data)

    3. KA data dictionary (self.fsm_trans).

    4. Interface methods of spacecraft. These are methods called upon transitions from one state of the spacecraft to another and performing specific actions necessary during these transitions.

    KA data dictionary

    It is best to start the description of the code operation with the spacecraft data dictionary. There he is:

    self.fsm_trans = {
        # 1
        'init_logo': {'type': 'Show', 'methods': [self.fsm_logo_show]},
        # 2
        'logo_logo': {'type': 'Show', 'methods': [self.fsm_logo_show]},
    .......
        # 6
        'pg1_prj_select_page': {'type': 'Show', 'methods': [self.fsm_project_set_dir,
                                                    self.fsm_diag_files_rm,
                                                    self.fsm_project_page_algor_start,
                                                    self.fsm_set_cur_page_algor_first_page,
                                                    self.fsm_set_cur_block_first_block_of_page,
                                                    self.fsm_create_cur_page_diag_files,
                                                    self.fsm_page_show]},
    .....
        # 29
        'f3_pg_block_edit_check_pg_block_edit': {'type': 'Act', 'methods':
                                                   [self.fsm_pg_block_edit,
                                                     self.fsm_diag_files_rm,
                                                     self.fsm_create_cur_page_diag_files,
                                                     self.fsm_page_cur_save,
                                                     self.fsm_go_to_page]},
    .....
    


    Dictionary Key This is a string made up of the names of two states, the original and the next. Our SC remembers the initial state in the self.fsm_prev_state variable, the next comes from the browser request parameter. For example, for the transition indicated by '# 1', the initial state is 'init', the next is 'logo', the key is obtained as 'init' + '_' + 'logo'

    The dictionary again is located on the key. It has two keys, 'type' and 'methods'.

    The key 'type' indicates the type of the next state. There are two state types, 'Show' and 'Act'. Type 'Show' means that this is the display state of the generated HTML page and is waiting for the next request from the browser. The type 'Act' means that this state does not need to be stopped.

    The key 'methods' contains an array of interface names of the spacecraft methods. This, in fact, is a sequence of useful actions that must be performed during the transition from one state to another. Moreover, for the type 'Show', the last method of the array should return the generated HTML page, and for the type 'Act' - the name of the state to which you want to go.

    CA code in index () method

    1 @ cherrypy.expose
    2 def index (self, block = '', tostate = '', page = '', project = '', ** data):
        .......
    3 try:
    4 # FSM go to next state
    5 fsm_data = self.fsm_transition (self.http_param ['tostate'])
    6 # If 'tostate' parameter is not valid for this state
    7 except KeyError, error_detail:
    8 return self.fsm_error_show ('KeyError:' + str (error_detail))
    9 processing_result = self.fsm_process_methods (fsm_data)
    10 while True:
    11 # If type is 'Show' return html page 
    12 if fsm_data ['type'] == 'Show':
    13 return processing_result
    14 # If type is 'Act', FSM to next state
    15 # the name of the state is in processing_result
    16 if fsm_data ['type'] == 'Act':
    17 fsm_data = self.fsm_transition (processing_result)
    18 processing_result = self.fsm_process_methods (fsm_data)
    


    The method gets the 'tostate' parameter from the request. after that, the self.fsm_transition () method in line 5 returns the dictionary from the data dictionary containing the state type (Show | ACt) and an array of methods for execution.

    In case of a key error, i.e. it is impossible to switch from this state to the desired one; by exception, the KA switches to the 'error' state. A key error can happen, for example, if the browser is returned several steps along the history and clicked on any link. In this case, the
    page displayed by the HTML browser does not correspond to the state of the satellite and the request may require a transition to a state to which there should not be a transition from the current state.

    On line 9, the self.fsm_process_methods (fsm_data) method processes an array of transition methods and returns the value that the last method returned from the method array. In the case of type 'Show', this is the HTML code of the page, the method 'index' returns it, and the server passes it to the browser.
    If the type is' Act ', this is the name of the next state, on line 17 we transfer the spacecraft to the next state, on line 18 we process the transition methods and so on, until we get to the type Show'.

    Spacecraft control methods

    # FSM methods
    def fsm_transition (self, to_state):
        "" "
        Change state of FSM to state
        Provide decision what to do
        Arguments:
        to_state - State to go to
        Return:
        "" "
        # Remember old state
        self.fsm_prev_state = self.fsm_state
        self.fsm_state = to_state
        # Get return value from dictionary
        key = str (self.fsm_prev_state) + '_' + str (self.fsm_state)
        self.fsm_return = self.fsm_trans [key]
        return self.fsm_return
    def fsm_process_methods (self, fsm_data):
        "" "
        Get fsm_data dictionary, process functions and
        return htmal or next state
        Arguments:
        fsm_data - Dictionary, taken from self.fsm_transition () return value
        It is taken from self.fsm_trans dictionary
        Return:
        html text in case 'type': 'Show'
        name of the next state in case 'type': 'Act'
        "" "
        last_func = len (fsm_data ['methods'])
        i = 0
        for method in fsm_data ['methods']:
            i + = 1
            if i == last_func:
                return method () # For last function return its return value
            method ()
    


    Here, I think, everything is trivial.

    About choosing a method for processing interface methods

    There are no special reasons for storing methods in an array. It would be entirely possible for each transition to have its own method in which nested interface methods would be called.

    The above processing method was chosen deliberately in order to have all the calls to the interface functions in one place and at the same nesting level for code readability. In the implemented code, you can simply take the printed spacecraft diagram and use the code of the spacecraft data dictionary to see which chains of actions are performed for transitions from one
    state to another.

    Conclusion

    I believe that application of the approach using spacecraft during the design process allowed writing flexible, readable, transparent and easily supported code.

    Appendix: Example of a spacecraft diagram (fragment of a real diagram)

    image

    Also popular now: