blob: 5d53fee5b1fb6fc17ac6b5767b605d898f304526 [file] [log] [blame]
Girish Gowdru0c588b22019-04-23 23:24:56 -04001/*
2 * Copyright 2018-present Open Networking Foundation
3
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7
8 * http://www.apache.org/licenses/LICENSE-2.0
9
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package adaptercore
17
18import (
19 "reflect"
20 "runtime"
21
22 "github.com/opencord/voltha-go/common/log"
23)
24
25// DeviceState OLT Device state
26type DeviceState int
27
28const (
29 // deviceStateNull OLT is not instantiated
30 deviceStateNull DeviceState = iota
31 // deviceStateInit OLT is instantiated
32 deviceStateInit
33 // deviceStateConnected Grpc session established with OLT
34 deviceStateConnected
35 // deviceStateUp Admin state of OLT is UP
36 deviceStateUp
37 // deviceStateDown Admin state of OLT is down
38 deviceStateDown
39)
40
41// Trigger for changing the state
42type Trigger int
43
44const (
45 // DeviceInit Go to Device init state
46 DeviceInit Trigger = iota
47 // GrpcConnected Go to connected state
48 GrpcConnected
49 // DeviceUpInd Go to Device up state
50 DeviceUpInd
51 // DeviceDownInd Go to Device down state
52 DeviceDownInd
53 // GrpcDisconnected Go to Device init state
54 GrpcDisconnected
55)
56
57// TransitionHandler function type for handling transition
58type TransitionHandler func() error
59
60// Transition to store state machine
61type Transition struct {
62 previousState []DeviceState
63 currentState DeviceState
64 before []TransitionHandler
65 after []TransitionHandler
66}
67
68// TransitionMap to store all the states and current device state
69type TransitionMap struct {
70 transitions map[Trigger]Transition
71 currentDeviceState DeviceState
72}
73
74// OpenoltDevice state machine:
75//
76// null ----> init ------> connected -----> up -----> down
77// ^ ^ | ^ | |
78// | | | | | |
79// | +-------------+ +---------+ |
80// | |
81// +-----------------------------------------+
82
83// NewTransitionMap create a new state machine with all the transitions
84func NewTransitionMap(dh *DeviceHandler) *TransitionMap {
85 var transitionMap TransitionMap
86 transitionMap.currentDeviceState = deviceStateNull
87 transitionMap.transitions = make(map[Trigger]Transition)
88 // In doInit establish the grpc session
89 transitionMap.transitions[DeviceInit] =
90 Transition{
91 previousState: []DeviceState{deviceStateNull},
92 currentState: deviceStateInit,
93 before: []TransitionHandler{dh.doStateInit},
94 after: []TransitionHandler{dh.postInit}}
95 // If gRpc session fails, re-establish the grpc session
96 transitionMap.transitions[GrpcDisconnected] =
97 Transition{
98 previousState: []DeviceState{deviceStateConnected, deviceStateDown},
99 currentState: deviceStateInit,
100 before: []TransitionHandler{dh.doStateInit},
101 after: []TransitionHandler{dh.postInit}}
102 // in doConnected, create logical device and read the indications
103 transitionMap.transitions[GrpcConnected] =
104 Transition{
105 previousState: []DeviceState{deviceStateInit},
106 currentState: deviceStateConnected,
107 before: []TransitionHandler{dh.doStateConnected}}
108
109 // Once the olt UP is indication received, then do state up
110 transitionMap.transitions[DeviceUpInd] =
111 Transition{
112 previousState: []DeviceState{deviceStateConnected, deviceStateDown},
113 currentState: deviceStateUp,
114 before: []TransitionHandler{dh.doStateUp}}
115 // If olt DOWN indication comes then do sate down
116 transitionMap.transitions[DeviceDownInd] =
117 Transition{
118 previousState: []DeviceState{deviceStateUp},
119 currentState: deviceStateDown,
120 before: []TransitionHandler{dh.doStateDown}}
121
122 return &transitionMap
123}
124
125// funcName gets the handler function name
126func funcName(f interface{}) string {
127 p := reflect.ValueOf(f).Pointer()
128 rf := runtime.FuncForPC(p)
129 return rf.Name()
130}
131
132// isValidTransition checks for the new state transition is valid from current state
133func (tMap *TransitionMap) isValidTransition(trigger Trigger) bool {
134 // Validate the state transition
135 for _, state := range tMap.transitions[trigger].previousState {
136 if tMap.currentDeviceState == state {
137 return true
138 }
139 }
140 return false
141}
142
143// Handle moves the state machine to next state based on the trigger and invokes the before and
144// after handlers
145func (tMap *TransitionMap) Handle(trigger Trigger) {
146
147 // Check whether the transtion is valid from current state
148 if !tMap.isValidTransition(trigger) {
149 log.Errorw("Invalid transition triggered ", log.Fields{"CurrentState": tMap.currentDeviceState, "Trigger": trigger})
150 return
151 }
152
153 // Invoke the before handlers
154 beforeHandlers := tMap.transitions[trigger].before
155 if beforeHandlers != nil {
156 for _, handler := range beforeHandlers {
157 log.Debugw("running-before-handler", log.Fields{"handler": funcName(handler)})
158 if err := handler(); err != nil {
159 // TODO handle error
160 return
161 }
162 }
163 } else {
164 log.Debugw("No handlers for before", log.Fields{"trigger": trigger})
165 }
166
167 // Update the state
168 tMap.currentDeviceState = tMap.transitions[trigger].currentState
169 log.Debugw("Updated device state ", log.Fields{"CurrentDeviceState": tMap.currentDeviceState})
170
171 // Invoke the after handlers
172 afterHandlers := tMap.transitions[trigger].after
173 if afterHandlers != nil {
174 for _, handler := range afterHandlers {
175 log.Debugw("running-after-handler", log.Fields{"handler": funcName(handler)})
176 if err := handler(); err != nil {
177 // TODO handle error
178 return
179 }
180 }
181 } else {
182 log.Debugw("No handlers for after", log.Fields{"trigger": trigger})
183 }
184}