📜 ⬆️ ⬇️

Experience of using spacecraft (FSM) in the web interface in Python

Introduction

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

The program in its original form translated a block diagram representation recorded in XML files into its graphical representation. The kernel of the program read XML files, displayed blocks of the block diagram described in these files into objects and then from these objects drew a picture and showed it through the browser.

In accordance with the new requirements, it was necessary to add interactive editing. And that means, it was necessary to add links, forms, handle errors and issue messages about them, check the data entered in the forms, take into account browser buttons that allow you to move through the history and so on. And most importantly, in all this is not confused.
')
I have a habit for confusing programs to draw flowcharts. For a program that operates in a mode when there are pronounced states in which a program may be located, events to which it should react (for example button presses) and transitions from one state to another, the concept of a finite state machine or spacecraft is ideal; FSM or Finit State Machine. Once, even under DOS, I figured out the menus of a single sophisticated program only when I drew a state diagram of a spacecraft for it.

What is KA (state machine)

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

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

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

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

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

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

Wednesday

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

Implementation and operation of the code

Organization code spacecraft

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

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

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

1. Code in the index () method. Receives the 'tostate' parameter from the browser and calls the 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. Dictionary of spacecraft data (self.fsm_trans).

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

SC data dictionary

The description of the code is best to start with the vocabulary data dictionary. Here he is:

 self.fsm_trans = {
     # one
     '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 composed of the names of the two states, the source and the next. The initial state is remembered by our KA in the self.fsm_prev_state variable, the following comes from the browser's request parameter. For example, for the transition labeled '# 1', the initial state is 'init', the following is 'logo', the key is obtained as 'init' + '_' + 'logo'

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

The key 'type' indicates the type of the next state. There are two types of state, 'Show' and 'Act'. The 'Show' type indicates that this is the state of the display of the generated HTML page and the waiting for the next request from the browser. The type 'Act' means that you do not need to stop at this state. It is necessary to proceed to the next state.

The 'methods' key is an array of interface method names for the QA. This is, strictly speaking, a sequence of useful actions that must be performed when moving from one state to another. And for the 'Show' type, the last method of the array should return the generated HTML page, and for the 'Act' type, the name of the state to which you want to go.

Spacecraft code in the 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 this, the self.fsm_transition () method in line 5 returns a 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 go from this state to the desired state, by the exception of the KA goes into the 'error' state. A key error can happen, for example, if the browser was returned a few steps through the history and clicked on some link. In this case,
The HTML page displayed by the browser does not correspond to the state of the AC, and the request may require a transition to a state to which there should be no transition from the current state.

In 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 array of methods. In the case of the 'Show' type, this is the HTML page code, the 'index' method returns it, and the server sends it to the browser.
If the type is' Act ', this is the name of the next state, in line 17 we translate the spacecraft to the next state, in line 18 we process transition methods and so on until we reach 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 the choice of the method of processing interface methods

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

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

Conclusion

I believe that the use of a spacecraft in the design approach allowed us to write flexible, readable, transparent and easily supported code.

Appendix: Example of a QA chart (fragment of a real chart)

image

Source: https://habr.com/ru/post/60751/


All Articles