VOL-1374: OLT Activation with Edgecore asfvolt16

Change-Id: I61ce4b0a6a3666070d08a162251d42d90817f409
diff --git a/adaptercore/olt_state_transitions.go b/adaptercore/olt_state_transitions.go
new file mode 100644
index 0000000..45e5f5e
--- /dev/null
+++ b/adaptercore/olt_state_transitions.go
@@ -0,0 +1,184 @@
+/*

+ * Copyright 2018-present Open Networking Foundation

+

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+

+ * http://www.apache.org/licenses/LICENSE-2.0

+

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package adaptercore

+

+import (

+    "reflect"

+    "runtime"

+

+    "github.com/opencord/voltha-go/common/log"

+)

+

+// DeviceState OLT Device state

+type DeviceState int

+

+const (

+    // deviceStateNull OLT is not instantiated

+    deviceStateNull DeviceState = iota

+    // deviceStateInit OLT is instantiated

+    deviceStateInit

+    // deviceStateConnected Grpc session established with OLT

+    deviceStateConnected

+    // deviceStateUp Admin state of OLT is UP

+    deviceStateUp

+    // deviceStateDown Admin state of OLT is down

+    deviceStateDown

+)

+

+// Trigger for changing the state

+type Trigger int

+

+const (

+    // DeviceInit Go to Device init state

+    DeviceInit Trigger = iota

+    // GrpcConnected Go to connected state

+    GrpcConnected

+    // DeviceUpInd Go to Device up state

+    DeviceUpInd

+    // DeviceDownInd Go to Device down state

+    DeviceDownInd

+    // GrpcDisconnected Go to Device init state

+    GrpcDisconnected

+)

+

+// TransitionHandler function type for handling transition

+type TransitionHandler func() error

+

+// Transition to store state machine

+type Transition struct {

+    previousState []DeviceState

+    currentState  DeviceState

+    before        []TransitionHandler

+    after         []TransitionHandler

+}

+

+// TransitionMap to store all the states and current device state

+type TransitionMap struct {

+    transitions        map[Trigger]Transition

+    currentDeviceState DeviceState

+}

+

+//    OpenoltDevice state machine:

+//

+//        null ----> init ------> connected -----> up -----> down

+//                   ^ ^             |             ^         | |

+//                   | |             |             |         | |

+//                   | +-------------+             +---------+ |

+//                   |                                         |

+//                   +-----------------------------------------+

+

+// NewTransitionMap create a new state machine with all the transitions

+func NewTransitionMap(dh *DeviceHandler) *TransitionMap {

+    var transitionMap TransitionMap

+    transitionMap.currentDeviceState = deviceStateNull

+    transitionMap.transitions = make(map[Trigger]Transition)

+    // In doInit establish the grpc session

+    transitionMap.transitions[DeviceInit] =

+        Transition{

+            previousState: []DeviceState{deviceStateNull},

+            currentState:  deviceStateInit,

+            before:        []TransitionHandler{dh.doStateInit},

+            after:         []TransitionHandler{dh.postInit}}

+    // If gRpc session fails, re-establish the grpc session

+    transitionMap.transitions[GrpcDisconnected] =

+        Transition{

+            previousState: []DeviceState{deviceStateConnected, deviceStateDown},

+            currentState:  deviceStateInit,

+            before:        []TransitionHandler{dh.doStateInit},

+            after:         []TransitionHandler{dh.postInit}}

+    // in doConnected, create logical device and read the indications

+    transitionMap.transitions[GrpcConnected] =

+        Transition{

+            previousState: []DeviceState{deviceStateInit},

+            currentState:  deviceStateConnected,

+            before:        []TransitionHandler{dh.doStateConnected}}

+

+    // Once the olt UP is indication received, then do state up

+    transitionMap.transitions[DeviceUpInd] =

+        Transition{

+            previousState: []DeviceState{deviceStateConnected, deviceStateDown},

+            currentState:  deviceStateUp,

+            before:        []TransitionHandler{dh.doStateUp}}

+    // If olt DOWN indication comes then do sate down

+    transitionMap.transitions[DeviceDownInd] =

+        Transition{

+            previousState: []DeviceState{deviceStateUp},

+            currentState:  deviceStateDown,

+            before:        []TransitionHandler{dh.doStateDown}}

+

+    return &transitionMap

+}

+

+// funcName gets the handler function name

+func funcName(f interface{}) string {

+    p := reflect.ValueOf(f).Pointer()

+    rf := runtime.FuncForPC(p)

+    return rf.Name()

+}

+

+// isValidTransition checks for the new state transition is valid from current state

+func (tMap *TransitionMap) isValidTransition(trigger Trigger) bool {

+    // Validate the state transition

+    for _, state := range tMap.transitions[trigger].previousState {

+        if tMap.currentDeviceState == state {

+            return true

+        }

+    }

+    return false

+}

+

+// Handle moves the state machine to next state based on the trigger and invokes the before and

+//         after handlers

+func (tMap *TransitionMap) Handle(trigger Trigger) {

+

+    // Check whether the transtion is valid from current state

+    if !tMap.isValidTransition(trigger) {

+        log.Errorw("Invalid transition triggered ", log.Fields{"CurrentState": tMap.currentDeviceState, "Trigger": trigger})

+        return

+    }

+

+    // Invoke the before handlers

+    beforeHandlers := tMap.transitions[trigger].before

+    if beforeHandlers != nil {

+        for _, handler := range beforeHandlers {

+            log.Debugw("running-before-handler", log.Fields{"handler": funcName(handler)})

+            if err := handler(); err != nil {

+                // TODO handle error

+                return

+            }

+        }

+    } else {

+        log.Debugw("No handlers for before", log.Fields{"trigger": trigger})

+    }

+

+    // Update the state

+    tMap.currentDeviceState = tMap.transitions[trigger].currentState

+    log.Debugw("Updated device state ", log.Fields{"CurrentDeviceState": tMap.currentDeviceState})

+

+    // Invoke the after handlers

+    afterHandlers := tMap.transitions[trigger].after

+    if afterHandlers != nil {

+        for _, handler := range afterHandlers {

+            log.Debugw("running-after-handler", log.Fields{"handler": funcName(handler)})

+            if err := handler(); err != nil {

+                // TODO handle error

+                return

+            }

+        }

+    } else {

+        log.Debugw("No handlers for after", log.Fields{"trigger": trigger})

+    }

+}