github.com/simpleiot/simpleiot@v0.18.3/network/manager.go (about)

     1  package network
     2  
     3  import (
     4  	"errors"
     5  	"log"
     6  	"time"
     7  )
     8  
     9  // State is used to describe the network state
    10  type State int
    11  
    12  // define valid states
    13  const (
    14  	StateNotDetected State = iota
    15  	StateConfigure
    16  	StateConnecting
    17  	StateConnected
    18  	StateError
    19  )
    20  
    21  func (s State) String() string {
    22  	switch s {
    23  	case StateNotDetected:
    24  		return "Not detected"
    25  	case StateConfigure:
    26  		return "Configure"
    27  	case StateConnecting:
    28  		return "Connecting"
    29  	case StateConnected:
    30  		return "Connected"
    31  	case StateError:
    32  		return "Error"
    33  	default:
    34  		return "unknown"
    35  	}
    36  }
    37  
    38  // Manager is used to configure the network and manage the
    39  // lifecycle.
    40  type Manager struct {
    41  	state          State
    42  	stateStart     time.Time
    43  	interfaces     []Interface
    44  	errResetCnt    int
    45  	errCnt         int
    46  	interfaceIndex int
    47  }
    48  
    49  // NewManager constructor
    50  func NewManager(errResetCnt int) *Manager {
    51  	return &Manager{
    52  		stateStart:  time.Now(),
    53  		errResetCnt: errResetCnt,
    54  	}
    55  }
    56  
    57  // AddInterface adds a network interface to the manager. Interfaces added first
    58  // have higher priority
    59  func (m *Manager) AddInterface(iface Interface) {
    60  	m.interfaces = append(m.interfaces, iface)
    61  }
    62  
    63  func (m *Manager) setState(state State) {
    64  	if state != m.state {
    65  		log.Printf("Network state: %v -> %v", m.state, state)
    66  		m.state = state
    67  		m.stateStart = time.Now()
    68  	}
    69  }
    70  
    71  func (m *Manager) getStatus() (InterfaceStatus, error) {
    72  	if len(m.interfaces) <= 0 {
    73  		return InterfaceStatus{}, nil
    74  	}
    75  
    76  	return m.interfaces[m.interfaceIndex].GetStatus()
    77  }
    78  
    79  // Desc returns current interface description
    80  func (m *Manager) Desc() string {
    81  	if len(m.interfaces) <= 0 {
    82  		return "none"
    83  	}
    84  
    85  	return m.interfaces[m.interfaceIndex].Desc()
    86  }
    87  
    88  // nextInterface resets state and checks if there is another interface to try.
    89  // If there are no more interfaces, sets state to error.
    90  func (m *Manager) nextInterface() {
    91  	m.interfaceIndex++
    92  	if m.interfaceIndex >= len(m.interfaces) {
    93  		m.interfaceIndex = 0
    94  		log.Println("Network: no more interfaces, go to error state")
    95  		m.setState(StateError)
    96  		return
    97  	}
    98  
    99  	m.setState(StateNotDetected)
   100  
   101  	log.Println("Network: trying next interface:", m.Desc())
   102  }
   103  
   104  func (m *Manager) connect() error {
   105  	if len(m.interfaces) <= 0 {
   106  		return errors.New("No interfaces to connect to")
   107  	}
   108  
   109  	return m.interfaces[m.interfaceIndex].Connect()
   110  }
   111  
   112  func (m *Manager) configure() (InterfaceConfig, error) {
   113  	if len(m.interfaces) <= 0 {
   114  		return InterfaceConfig{}, errors.New("No interfaces to configure")
   115  	}
   116  
   117  	return m.interfaces[m.interfaceIndex].Configure()
   118  }
   119  
   120  // Reset resets all network interfaces
   121  func (m *Manager) Reset() {
   122  	for _, i := range m.interfaces {
   123  		err := i.Reset()
   124  		if err != nil {
   125  			log.Println("Error resetting interface:", err)
   126  		}
   127  	}
   128  }
   129  
   130  // Run must be called periodically to process the network life cycle
   131  // -- perhaps every 10s
   132  func (m *Manager) Run() (State, InterfaceConfig, InterfaceStatus) {
   133  	count := 0
   134  
   135  	status := InterfaceStatus{}
   136  	config := InterfaceConfig{}
   137  
   138  	// state machine for network manager
   139  	for {
   140  		count++
   141  		if count > 10 {
   142  			log.Println("network state machine ran too many times")
   143  			if time.Since(m.stateStart) > time.Second*15 {
   144  				log.Println("Network: timeout:", m.Desc())
   145  				m.nextInterface()
   146  			}
   147  
   148  			return m.state, config, status
   149  		}
   150  
   151  		if m.state != StateError {
   152  			var err error
   153  			status, err = m.getStatus()
   154  			if err != nil {
   155  				log.Println("Error getting interface status:", err)
   156  				continue
   157  			}
   158  		}
   159  
   160  		switch m.state {
   161  		case StateNotDetected:
   162  			// give ourselves 15 seconds or so in detecting state
   163  			// in case we just reset the devices
   164  			if status.Detected {
   165  				log.Printf("Network: %v detected\n", m.Desc())
   166  				m.setState(StateConfigure)
   167  				continue
   168  			} else if time.Since(m.stateStart) > time.Second*15 {
   169  				log.Println("Network: timeout detecting:", m.Desc())
   170  				m.nextInterface()
   171  				continue
   172  			}
   173  
   174  		case StateConfigure:
   175  			try := 0
   176  			for ; try < 3; try++ {
   177  				if try > 0 {
   178  					log.Println("Trying again ...")
   179  				}
   180  				var err error
   181  				config, err = m.configure()
   182  				if err != nil {
   183  					log.Printf("Error configuring device: %v: %v\n",
   184  						m.Desc(), err)
   185  
   186  					continue
   187  				}
   188  
   189  				break
   190  			}
   191  
   192  			if try < 3 {
   193  				m.setState(StateConnecting)
   194  			} else {
   195  				log.Println("giving up configuring device:", m.Desc())
   196  				m.nextInterface()
   197  			}
   198  
   199  		case StateConnecting:
   200  			if status.Connected {
   201  				log.Printf("Network: %v connected\n", m.Desc())
   202  				m.setState(StateConnected)
   203  			} else {
   204  				if time.Since(m.stateStart) > time.Minute {
   205  					log.Println("Network: timeout connecting:", m.Desc())
   206  					m.nextInterface()
   207  					continue
   208  				}
   209  
   210  				// try again to connect
   211  				err := m.connect()
   212  				if err != nil {
   213  					log.Println("Error connecting:", err)
   214  				}
   215  			}
   216  
   217  		case StateConnected:
   218  			if !status.Connected {
   219  				// try to reconnect
   220  				m.setState(StateConnecting)
   221  			}
   222  
   223  		case StateError:
   224  			if time.Since(m.stateStart) > time.Minute {
   225  				log.Println("Network: reset and try again ...")
   226  				m.Reset()
   227  				m.setState(StateNotDetected)
   228  			}
   229  		}
   230  
   231  		// if we want to re-run state machine, must execute continue above
   232  		break
   233  	}
   234  
   235  	return m.state, config, status
   236  }
   237  
   238  // Error is called any time there is a network error
   239  // after errResetCnt errors are reached, we reset all the interfaces and
   240  // start over
   241  func (m *Manager) Error() {
   242  	m.errCnt++
   243  
   244  	if m.errCnt >= m.errResetCnt {
   245  		m.Reset()
   246  		m.setState(StateNotDetected)
   247  		m.errCnt = 0
   248  	}
   249  }
   250  
   251  // Success is called any time there is a network success
   252  // so that we know to reset the internal error count
   253  func (m *Manager) Success() {
   254  	m.errCnt = 0
   255  }