blob: 9a11a3b9619f881ca40c1236a04853092d945bf6 [file] [log] [blame]
Takahiro Suzukid7bf8202020-12-17 20:21:59 +09001/*
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 */
16
17//Package core provides the utility for olt devices, flows and statistics
18package core
19
20import (
21 "context"
22 "reflect"
23 "runtime"
24
25 "github.com/opencord/voltha-lib-go/v3/pkg/log"
26)
27
28// DeviceState OLT Device state
29type DeviceState int
30
31const (
32 deviceStateNull DeviceState = iota
33 deviceStateInit
34 deviceStateConnected
35 deviceStateUp
36 deviceStateDown
37)
38
39// Trigger for changing the state
40type Trigger int
41
42const (
43 DeviceInit Trigger = iota
44 GrpcConnected
45 DeviceUpInd
46 DeviceDownInd
47 GrpcDisconnected
48)
49
50// TransitionHandler function type for handling transition
51type TransitionHandler func(ctx context.Context) error
52
53// Transition to store state machine
54type Transition struct {
55 previousState []DeviceState
56 currentState DeviceState
57 before []TransitionHandler
58 after []TransitionHandler
59}
60
61// TransitionMap to store all the states and current device state
62type TransitionMap struct {
63 transitions map[Trigger]Transition
64 currentDeviceState DeviceState
65}
66
67// OpenoltDevice state machine:
68//
69// null ----> init ------> connected -----> up -----> down
70// ^ ^ | ^ | |
71// | | | | | |
72// | +-------------+ +---------+ |
73// | |
74// +-----------------------------------------+
75
76// NewTransitionMap create a new state machine with all the transitions
77func NewTransitionMap(dh *DeviceHandler) *TransitionMap {
78 var transitionMap TransitionMap
79 transitionMap.currentDeviceState = deviceStateNull
80 transitionMap.transitions = make(map[Trigger]Transition)
81 transitionMap.transitions[DeviceInit] =
82 Transition{
83 previousState: []DeviceState{deviceStateNull, deviceStateUp, deviceStateDown},
84 currentState: deviceStateInit,
85 before: []TransitionHandler{dh.doStateInit},
86 after: []TransitionHandler{dh.postInit}}
87 transitionMap.transitions[GrpcDisconnected] =
88 Transition{
89 previousState: []DeviceState{deviceStateConnected, deviceStateDown},
90 currentState: deviceStateInit,
91 before: []TransitionHandler{dh.doStateInit},
92 after: []TransitionHandler{dh.postInit}}
93 transitionMap.transitions[GrpcConnected] =
94 Transition{
95 previousState: []DeviceState{deviceStateInit},
96 currentState: deviceStateConnected,
97 before: []TransitionHandler{dh.doStateConnected}}
98
99 transitionMap.transitions[DeviceUpInd] =
100 Transition{
101 previousState: []DeviceState{deviceStateConnected, deviceStateDown},
102 currentState: deviceStateUp,
103 before: []TransitionHandler{dh.doStateUp}}
104 transitionMap.transitions[DeviceDownInd] =
105 Transition{
106 previousState: []DeviceState{deviceStateUp},
107 currentState: deviceStateDown,
108 before: []TransitionHandler{dh.doStateDown}}
109
110 return &transitionMap
111}
112
113// funcName gets the handler function name
114func funcName(f interface{}) string {
115 p := reflect.ValueOf(f).Pointer()
116 rf := runtime.FuncForPC(p)
117 return rf.Name()
118}
119
120// isValidTransition checks for the new state transition is valid from current state
121func (tMap *TransitionMap) isValidTransition(trigger Trigger) bool {
122 for _, state := range tMap.transitions[trigger].previousState {
123 if tMap.currentDeviceState == state {
124 return true
125 }
126 }
127 return false
128}
129
130// Handle moves the state machine to next state based on the trigger and invokes the before and
131// after handlers if the transition is a valid transition
132func (tMap *TransitionMap) Handle(ctx context.Context, trigger Trigger) {
133
134 if !tMap.isValidTransition(trigger) {
135 logger.Errorw(ctx, "invalid-transition-triggered",
136 log.Fields{
137 "current-state": tMap.currentDeviceState,
138 "trigger": trigger})
139 return
140 }
141
142 beforeHandlers := tMap.transitions[trigger].before
143 if beforeHandlers == nil {
144 logger.Debugw(ctx, "no-handlers-for-before", log.Fields{"trigger": trigger})
145 }
146 for _, handler := range beforeHandlers {
147 logger.Debugw(ctx, "running-before-handler", log.Fields{"handler": funcName(handler)})
148 if err := handler(ctx); err != nil {
149 // TODO handle error
150 logger.Error(ctx, err)
151 return
152 }
153 }
154
155 tMap.currentDeviceState = tMap.transitions[trigger].currentState
156 logger.Debugw(ctx, "updated-device-state ", log.Fields{"current-device-state": tMap.currentDeviceState})
157
158 afterHandlers := tMap.transitions[trigger].after
159 if afterHandlers == nil {
160 logger.Debugw(ctx, "no-handlers-for-after", log.Fields{"trigger": trigger})
161 }
162 for _, handler := range afterHandlers {
163 logger.Debugw(ctx, "running-after-handler", log.Fields{"handler": funcName(handler)})
164 if err := handler(ctx); err != nil {
165 // TODO handle error
166 logger.Error(ctx, err)
167 return
168 }
169 }
170}