[VOL-2687] Fixing state transition for ONU Shutdown/PowerOn

Change-Id: I23c38351371bef091919c0beb48132f8d59a08dc
diff --git a/internal/bbsim/api/onus_handler.go b/internal/bbsim/api/onus_handler.go
index 975b426..0f898e8 100644
--- a/internal/bbsim/api/onus_handler.go
+++ b/internal/bbsim/api/onus_handler.go
@@ -19,8 +19,8 @@
 import (
 	"context"
 	"fmt"
-
 	"github.com/opencord/bbsim/api/bbsim"
+	"github.com/opencord/bbsim/internal/bbsim/alarmsim"
 	"github.com/opencord/bbsim/internal/bbsim/devices"
 	log "github.com/sirupsen/logrus"
 	"google.golang.org/grpc/codes"
@@ -97,16 +97,41 @@
 		return res, err
 	}
 
-	dyingGasp := devices.Message{
-		Type: devices.DyingGaspIndication,
-		Data: devices.DyingGaspIndicationMessage{
-			OnuID:     onu.ID,
-			PonPortID: onu.PonPortID,
-			Status:    "on", // TODO do we need a type for Dying Gasp Indication?
-		},
+	dyingGasp := bbsim.ONUAlarmRequest{
+		AlarmType:    "DyingGasp",
+		SerialNumber: onu.Sn(),
+		Status:       "on",
 	}
 
-	onu.Channel <- dyingGasp
+	if err := alarmsim.SimulateOnuAlarm(context.TODO(), &dyingGasp, olt); err != nil {
+		logger.WithFields(log.Fields{
+			"OnuId":  onu.ID,
+			"IntfId": onu.PonPortID,
+			"OnuSn":  onu.Sn(),
+		}).Errorf("Cannot send Dying Gasp: %s", err.Error())
+		res.StatusCode = int32(codes.FailedPrecondition)
+		res.Message = err.Error()
+		return res, err
+	}
+
+	losReq := bbsim.ONUAlarmRequest{
+		AlarmType:    "LossOfSignal",
+		SerialNumber: onu.Sn(),
+		Status:       "on",
+	}
+
+	if err := alarmsim.SimulateOnuAlarm(context.TODO(), &losReq, olt); err != nil {
+		logger.WithFields(log.Fields{
+			"OnuId":  onu.ID,
+			"IntfId": onu.PonPortID,
+			"OnuSn":  onu.Sn(),
+		}).Errorf("Cannot send LOS: %s", err.Error())
+		res.StatusCode = int32(codes.FailedPrecondition)
+		res.Message = err.Error()
+		return res, err
+	}
+
+	// TODO if it's the last ONU on the PON, then send a PON LOS
 
 	if err := onu.InternalState.Event("disable"); err != nil {
 		logger.WithFields(log.Fields{
@@ -155,7 +180,7 @@
 		return res, err
 	}
 
-	if onu.InternalState.Current() == "created" {
+	if onu.InternalState.Current() == "created" || onu.InternalState.Current() == "disabled" {
 		if err := onu.InternalState.Event("initialize"); err != nil {
 			logger.WithFields(log.Fields{
 				"OnuId":  onu.ID,
@@ -168,6 +193,23 @@
 		}
 	}
 
+	losReq := bbsim.ONUAlarmRequest{
+		AlarmType:    "LossOfSignal",
+		SerialNumber: onu.Sn(),
+		Status:       "off",
+	}
+
+	if err := alarmsim.SimulateOnuAlarm(context.TODO(), &losReq, olt); err != nil {
+		logger.WithFields(log.Fields{
+			"OnuId":  onu.ID,
+			"IntfId": onu.PonPortID,
+			"OnuSn":  onu.Sn(),
+		}).Errorf("Cannot send LOS: %s", err.Error())
+		res.StatusCode = int32(codes.FailedPrecondition)
+		res.Message = err.Error()
+		return res, err
+	}
+
 	if err := onu.InternalState.Event("discover"); err != nil {
 		logger.WithFields(log.Fields{
 			"OnuId":  onu.ID,
@@ -179,6 +221,17 @@
 		return res, err
 	}
 
+	if err := onu.InternalState.Event("enable"); err != nil {
+		logger.WithFields(log.Fields{
+			"OnuId":  onu.ID,
+			"IntfId": onu.PonPortID,
+			"OnuSn":  onu.Sn(),
+		}).Errorf("Cannot enable ONU: %s", err.Error())
+		res.StatusCode = int32(codes.FailedPrecondition)
+		res.Message = err.Error()
+		return res, err
+	}
+
 	res.StatusCode = int32(codes.OK)
 	res.Message = fmt.Sprintf("ONU %s successfully powered on.", onu.Sn())
 
diff --git a/internal/bbsim/devices/messageTypes.go b/internal/bbsim/devices/messageTypes.go
index 4872af0..8bb1d25 100644
--- a/internal/bbsim/devices/messageTypes.go
+++ b/internal/bbsim/devices/messageTypes.go
@@ -25,30 +25,29 @@
 type MessageType int
 
 const (
-	OltIndication       MessageType = 0
-	NniIndication       MessageType = 1
-	PonIndication       MessageType = 2
-	OnuDiscIndication   MessageType = 3
-	OnuIndication       MessageType = 4
-	OMCI                MessageType = 5
-	FlowUpdate          MessageType = 6
-	StartEAPOL          MessageType = 7
-	StartDHCP           MessageType = 8
-	OnuPacketOut        MessageType = 9
-	DyingGaspIndication MessageType = 10
+	OltIndication     MessageType = 0
+	NniIndication     MessageType = 1
+	PonIndication     MessageType = 2
+	OnuDiscIndication MessageType = 3
+	OnuIndication     MessageType = 4
+	OMCI              MessageType = 5
+	FlowUpdate        MessageType = 6
+	StartEAPOL        MessageType = 7
+	StartDHCP         MessageType = 8
+	OnuPacketOut      MessageType = 9
 
 	// BBR messages
-	OmciIndication MessageType = 11 // this are OMCI messages going from the OLT to VOLTHA
-	SendEapolFlow  MessageType = 12
-	SendDhcpFlow   MessageType = 13
-	OnuPacketIn    MessageType = 14
+	OmciIndication MessageType = 10 // this are OMCI messages going from the OLT to VOLTHA
+	SendEapolFlow  MessageType = 11
+	SendDhcpFlow   MessageType = 12
+	OnuPacketIn    MessageType = 13
 
 	//IGMP
-	IGMPMembershipReportV2 MessageType = 15 // Version 2 Membership Report (JOIN)
-	IGMPLeaveGroup         MessageType = 16 // Leave Group
+	IGMPMembershipReportV2 MessageType = 14 // Version 2 Membership Report (JOIN)
+	IGMPLeaveGroup         MessageType = 15 // Leave Group
 
-	AlarmIndication        MessageType = 17 // message data is an openolt.AlarmIndication
-	IGMPMembershipReportV3 MessageType = 18 // Version 3 Membership Report
+	AlarmIndication        MessageType = 16 // message data is an openolt.AlarmIndication
+	IGMPMembershipReportV3 MessageType = 17 // Version 3 Membership Report
 )
 
 func (m MessageType) String() string {
@@ -63,7 +62,6 @@
 		"StartEAPOL",
 		"StartDHCP",
 		"OnuPacketOut",
-		"DyingGaspIndication",
 		"OmciIndication",
 		"SendEapolFlow",
 		"SendDhcpFlow",
@@ -136,12 +134,6 @@
 	Type   packetHandlers.PacketType
 }
 
-type DyingGaspIndicationMessage struct {
-	PonPortID uint32
-	OnuID     uint32
-	Status    string
-}
-
 type OperState int
 
 const (
diff --git a/internal/bbsim/devices/olt.go b/internal/bbsim/devices/olt.go
index fa0ba5d..8a0d01e 100644
--- a/internal/bbsim/devices/olt.go
+++ b/internal/bbsim/devices/olt.go
@@ -98,14 +98,14 @@
 		OperState: getOperStateFSM(func(e *fsm.Event) {
 			oltLogger.Debugf("Changing OLT OperState from %s to %s", e.Src, e.Dst)
 		}),
-		NumNni:       nni,
-		NumPon:       pon,
-		NumOnuPerPon: onuPerPon,
-		Pons:         []*PonPort{},
-		Nnis:         []*NniPort{},
-		Delay:        delay,
-		Flows:        make(map[FlowKey]openolt.Flow),
-		enablePerf:   enablePerf,
+		NumNni:        nni,
+		NumPon:        pon,
+		NumOnuPerPon:  onuPerPon,
+		Pons:          []*PonPort{},
+		Nnis:          []*NniPort{},
+		Delay:         delay,
+		Flows:         make(map[FlowKey]openolt.Flow),
+		enablePerf:    enablePerf,
 		PublishEvents: event,
 	}
 
diff --git a/internal/bbsim/devices/onu.go b/internal/bbsim/devices/onu.go
index ee16cb8..43759c9 100644
--- a/internal/bbsim/devices/onu.go
+++ b/internal/bbsim/devices/onu.go
@@ -126,7 +126,7 @@
 			// DEVICE Lifecycle
 			{Name: "initialize", Src: []string{"created", "disabled"}, Dst: "initialized"},
 			{Name: "discover", Src: []string{"initialized", "pon_disabled"}, Dst: "discovered"},
-			{Name: "enable", Src: []string{"discovered", "disabled"}, Dst: "enabled"},
+			{Name: "enable", Src: []string{"discovered"}, Dst: "enabled"},
 			{Name: "receive_eapol_flow", Src: []string{"enabled", "gem_port_added"}, Dst: "eapol_flow_received"},
 			{Name: "add_gem_port", Src: []string{"enabled", "eapol_flow_received"}, Dst: "gem_port_added"},
 			// NOTE should disabled state be different for oper_disabled (emulating an error) and admin_disabled (received a disabled call via VOLTHA)?
@@ -163,6 +163,15 @@
 			"enter_initialized": func(e *fsm.Event) {
 				// create new channel for ProcessOnuMessages Go routine
 				o.Channel = make(chan Message, 2048)
+
+				if err := o.OperState.Event("enable"); err != nil {
+					onuLogger.WithFields(log.Fields{
+						"OnuId":  o.ID,
+						"IntfId": o.PonPortID,
+						"OnuSn":  o.Sn(),
+					}).Errorf("Cannot change ONU OperState to up: %s", err.Error())
+				}
+
 				if !isMock {
 					// start ProcessOnuMessages Go routine
 					go o.ProcessOnuMessages(olt.enableContext, *olt.OpenoltStream, nil)
@@ -190,6 +199,13 @@
 				o.Channel <- msg
 			},
 			"enter_disabled": func(event *fsm.Event) {
+				if err := o.OperState.Event("disable"); err != nil {
+					onuLogger.WithFields(log.Fields{
+						"OnuId":  o.ID,
+						"IntfId": o.PonPortID,
+						"OnuSn":  o.Sn(),
+					}).Errorf("Cannot change ONU OperState to down: %s", err.Error())
+				}
 				msg := Message{
 					Type: OnuIndication,
 					Data: OnuIndicationMessage{
@@ -378,9 +394,6 @@
 				} else if msg.Type == packetHandlers.DHCP {
 					dhcp.HandleNextBbrPacket(o.ID, o.PonPortID, o.Sn(), o.STag, o.HwAddress, o.DoneChannel, msg.Packet, client)
 				}
-			case DyingGaspIndication:
-				msg, _ := message.Data.(DyingGaspIndicationMessage)
-				o.sendDyingGaspInd(msg, stream)
 			case OmciIndication:
 				msg, _ := message.Data.(OmciIndicationMessage)
 				o.handleOmci(msg, client)
@@ -481,31 +494,6 @@
 	return sn
 }
 
-// NOTE handle_/process methods can change the ONU internal state as they are receiving messages
-// send method should not change the ONU state
-
-func (o *Onu) sendDyingGaspInd(msg DyingGaspIndicationMessage, stream openolt.Openolt_EnableIndicationServer) error {
-	alarmData := &openolt.AlarmIndication_DyingGaspInd{
-		DyingGaspInd: &openolt.DyingGaspIndication{
-			IntfId: msg.PonPortID,
-			OnuId:  msg.OnuID,
-			Status: "on",
-		},
-	}
-	data := &openolt.Indication_AlarmInd{AlarmInd: &openolt.AlarmIndication{Data: alarmData}}
-
-	if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
-		onuLogger.Errorf("Failed to send DyingGaspInd : %v", err)
-		return err
-	}
-	onuLogger.WithFields(log.Fields{
-		"IntfId": msg.PonPortID,
-		"OnuSn":  o.Sn(),
-		"OnuId":  msg.OnuID,
-	}).Info("sendDyingGaspInd")
-	return nil
-}
-
 func (o *Onu) sendOnuDiscIndication(msg OnuDiscIndicationMessage, stream openolt.Openolt_EnableIndicationServer) {
 	discoverData := &openolt.Indication_OnuDiscInd{OnuDiscInd: &openolt.OnuDiscIndication{
 		IntfId:       msg.Onu.PonPortID,
@@ -547,7 +535,7 @@
 		SerialNumber: o.SerialNumber,
 	}}
 	if err := stream.Send(&openolt.Indication{Data: indData}); err != nil {
-		// TODO do we need to transition to a broken state?
+		// NOTE do we need to transition to a broken state?
 		log.Errorf("Failed to send Indication_OnuInd: %v", err)
 	}
 	onuLogger.WithFields(log.Fields{
@@ -652,20 +640,20 @@
 
 func (o *Onu) handleFlowUpdate(msg OnuFlowUpdateMessage) {
 	onuLogger.WithFields(log.Fields{
-		"DstPort":   msg.Flow.Classifier.DstPort,
-		"EthType":   fmt.Sprintf("%x", msg.Flow.Classifier.EthType),
-		"FlowId":    msg.Flow.FlowId,
-		"FlowType":  msg.Flow.FlowType,
-		"GemportId": msg.Flow.GemportId,
-		"InnerVlan": msg.Flow.Classifier.IVid,
-		"IntfId":    msg.Flow.AccessIntfId,
-		"IpProto":   msg.Flow.Classifier.IpProto,
-		"OnuId":     msg.Flow.OnuId,
-		"OnuSn":     o.Sn(),
-		"OuterVlan": msg.Flow.Classifier.OVid,
-		"PortNo":    msg.Flow.PortNo,
-		"SrcPort":   msg.Flow.Classifier.SrcPort,
-		"UniID":     msg.Flow.UniId,
+		"DstPort":          msg.Flow.Classifier.DstPort,
+		"EthType":          fmt.Sprintf("%x", msg.Flow.Classifier.EthType),
+		"FlowId":           msg.Flow.FlowId,
+		"FlowType":         msg.Flow.FlowType,
+		"GemportId":        msg.Flow.GemportId,
+		"InnerVlan":        msg.Flow.Classifier.IVid,
+		"IntfId":           msg.Flow.AccessIntfId,
+		"IpProto":          msg.Flow.Classifier.IpProto,
+		"OnuId":            msg.Flow.OnuId,
+		"OnuSn":            o.Sn(),
+		"OuterVlan":        msg.Flow.Classifier.OVid,
+		"PortNo":           msg.Flow.PortNo,
+		"SrcPort":          msg.Flow.Classifier.SrcPort,
+		"UniID":            msg.Flow.UniId,
 		"ClassifierOPbits": msg.Flow.Classifier.OPbits,
 	}).Debug("ONU receives Flow")