VOL-2640 Restructure openolt-adapter repo to best practices

Change-Id: Icead31e8ecb82ec75a22e66361fbf83f80136589
diff --git a/internal/pkg/core/olt_state_transitions.go b/internal/pkg/core/olt_state_transitions.go
new file mode 100644
index 0000000..c10d17d
--- /dev/null
+++ b/internal/pkg/core/olt_state_transitions.go
@@ -0,0 +1,187 @@
+/*
+ * 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 core provides the utility for olt devices, flows and statistics
+package core
+
+import (
+	"context"
+	"reflect"
+	"runtime"
+
+	"github.com/opencord/voltha-lib-go/v3/pkg/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(ctx context.Context) 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, deviceStateDown},
+			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 if the transition is a valid transition
+func (tMap *TransitionMap) Handle(ctx context.Context, 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 {
+		log.Debugw("No handlers for before", log.Fields{"trigger": trigger})
+	}
+	for _, handler := range beforeHandlers {
+		log.Debugw("running-before-handler", log.Fields{"handler": funcName(handler)})
+		if err := handler(ctx); err != nil {
+			// TODO handle error
+			log.Error(err)
+			return
+		}
+	}
+
+	// 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 {
+		log.Debugw("No handlers for after", log.Fields{"trigger": trigger})
+	}
+	for _, handler := range afterHandlers {
+		log.Debugw("running-after-handler", log.Fields{"handler": funcName(handler)})
+		if err := handler(ctx); err != nil {
+			// TODO handle error
+			log.Error(err)
+			return
+		}
+	}
+}