First version,
OLT is activated and enabled.
ONU are in discovery state, OMCI state machine is still missing.

Change-Id: I6dddd4fd3460ee73b44226a546318acb2545d64d
diff --git a/internal/bbsim/bbsim.go b/internal/bbsim/bbsim.go
new file mode 100644
index 0000000..d0acc14
--- /dev/null
+++ b/internal/bbsim/bbsim.go
@@ -0,0 +1,56 @@
+package main
+
+import (
+	"flag"
+	"gerrit.opencord.org/bbsim/internal/bbsim/devices"
+	log "github.com/sirupsen/logrus"
+	"sync"
+)
+
+func getOpts() *CliOptions {
+
+	olt_id := flag.Int("olt_id", 0, "Number of OLT devices to be emulated (default is 1)")
+	nni := flag.Int("nni", 1, "Number of NNI ports per OLT device to be emulated (default is 1)")
+	pon := flag.Int("pon", 1, "Number of PON ports per OLT device to be emulated (default is 1)")
+	onu := flag.Int("onu", 1, "Number of ONU devices per PON port to be emulated (default is 1)")
+	flag.Parse()
+
+	o := new(CliOptions)
+
+	o.OltID = int(*olt_id)
+	o.NumNniPerOlt = int(*nni)
+	o.NumPonPerOlt = int(*pon)
+	o.NumOnuPerPon = int(*onu)
+
+	return o
+}
+
+func init() {
+	log.SetLevel(log.DebugLevel)
+	//log.SetReportCaller(true)
+}
+
+func main() {
+
+	options := getOpts()
+
+	log.WithFields(log.Fields{
+		"OltID": options.OltID,
+		"NumNniPerOlt": options.NumNniPerOlt,
+		"NumPonPerOlt": options.NumPonPerOlt,
+		"NumOnuPerPon": options.NumOnuPerPon,
+	}).Info("BroadBand Simulator is on")
+
+	wg := sync.WaitGroup{}
+	wg.Add(1)
+
+
+	go devices.CreateOLT(options.OltID, options.NumNniPerOlt, options.NumPonPerOlt, options.NumOnuPerPon)
+	log.Debugf("Created OLT with id: %d", options.OltID)
+
+	wg.Wait()
+
+	defer func() {
+		log.Info("BroadBand Simulator is off")
+	}()
+}
\ No newline at end of file
diff --git a/internal/bbsim/devices/olt.go b/internal/bbsim/devices/olt.go
new file mode 100644
index 0000000..8435af1
--- /dev/null
+++ b/internal/bbsim/devices/olt.go
@@ -0,0 +1,429 @@
+package devices
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"gerrit.opencord.org/bbsim/api"
+	"github.com/looplab/fsm"
+	log "github.com/sirupsen/logrus"
+	"google.golang.org/grpc"
+	"net"
+	"sync"
+)
+
+func init() {
+	//log.SetReportCaller(true)
+	log.SetLevel(log.DebugLevel)
+}
+
+func CreateOLT(seq int, nni int, pon int, onuPerPon int) OltDevice {
+	log.WithFields(log.Fields{
+		"ID": seq,
+		"NumNni":nni,
+		"NumPon":pon,
+		"NumOnuPerPon":onuPerPon,
+	}).Debug("CreateOLT")
+
+	olt := OltDevice{
+		ID: seq,
+		NumNni:nni,
+		NumPon:pon,
+		NumOnuPerPon:onuPerPon,
+		Pons: []PonPort{},
+		Nnis: []NniPort{},
+		channel: make(chan interface{}, 32),
+	}
+
+	// OLT State machine
+	olt.InternalState = fsm.NewFSM(
+		"created",
+		fsm.Events{
+			{Name: "enable", Src: []string{"created"}, Dst: "enabled"},
+			{Name: "disable", Src: []string{"enabled"}, Dst: "disabled"},
+		},
+		fsm.Callbacks{
+			"enter_state": func(e *fsm.Event) {
+				olt.stateChange(e)
+			},
+		},
+	)
+
+	// create NNI Port
+	nniPort := NniPort{
+		ID: uint32(0),
+		OperState: DOWN,
+		Type: "nni",
+	}
+	olt.Nnis = append(olt.Nnis, nniPort)
+
+	// create PON ports
+	for i := 0; i < pon; i++ {
+		p := PonPort{
+			NumOnu: olt.NumOnuPerPon,
+			ID: uint32(i),
+			OperState: DOWN,
+			Type: "pon",
+		}
+
+		// create ONU devices
+		for j := 0; j < onuPerPon; j++ {
+			o := CreateONU(olt, p, uint32(j + 1))
+			p.Onus = append(p.Onus, o)
+		}
+
+		olt.Pons = append(olt.Pons, p)
+	}
+
+	wg := sync.WaitGroup{}
+
+	wg.Add(1)
+	go newOltServer(olt)
+	wg.Wait()
+	return olt
+}
+
+func newOltServer(o OltDevice) error {
+	// TODO make configurable
+	address :=  "0.0.0.0:50060"
+	log.Debugf("OLT Listening on: %v", address)
+	lis, err := net.Listen("tcp", address)
+	if err != nil {
+		log.Fatalf("failed to listen: %v", err)
+	}
+	grpcServer := grpc.NewServer()
+	openolt.RegisterOpenoltServer(grpcServer, o)
+
+	go grpcServer.Serve(lis)
+
+	return nil
+}
+
+// Device Methods
+
+func (o OltDevice) Enable (stream openolt.Openolt_EnableIndicationServer) error {
+
+	wg := sync.WaitGroup{}
+	wg.Add(1)
+
+	// create a channel for all the OLT events
+	go o.oltChannels(stream)
+
+	// enable the OLT
+	olt_msg := Message{
+		Type: OltIndication,
+		Data: OltIndicationMessage{
+			OperState: UP,
+		},
+	}
+	o.channel <- olt_msg
+
+	// send NNI Port Indications
+	for _, nni := range o.Nnis {
+		msg := Message{
+			Type: NniIndication,
+			Data: NniIndicationMessage{
+				OperState: UP,
+				NniPortID: nni.ID,
+			},
+		}
+		o.channel <- msg
+	}
+
+	// send PON Port indications
+	for _, pon := range o.Pons {
+		msg := Message{
+			Type: PonIndication,
+			Data: PonIndicationMessage{
+				OperState: UP,
+				PonPortID: pon.ID,
+			},
+		}
+		o.channel <- msg
+
+		for _, onu := range pon.Onus {
+			msg := Message{
+				Type:      OnuDiscIndication,
+				Data: OnuDiscIndicationMessage{
+					Onu:     onu,
+					OperState: UP,
+				},
+			}
+			o.channel <- msg
+		}
+	}
+
+	wg.Wait()
+	return nil
+}
+
+// Helpers method
+
+func (o OltDevice) getPonById(id uint32) (*PonPort, error) {
+	for _, pon := range o.Pons {
+		if pon.ID == id {
+			return &pon, nil
+		}
+	}
+	return nil, errors.New(fmt.Sprintf("Cannot find PonPort with id %d in OLT %d", id, o.ID))
+}
+
+func (o OltDevice) getNniById(id uint32) (*NniPort, error) {
+	for _, nni := range o.Nnis {
+		if nni.ID == id {
+			return &nni, nil
+		}
+	}
+	return nil, errors.New(fmt.Sprintf("Cannot find NniPort with id %d in OLT %d", id, o.ID))
+}
+
+func (o OltDevice) stateChange(e *fsm.Event) {
+	log.WithFields(log.Fields{
+		"oltId": o.ID,
+		"dstState": e.Dst,
+		"srcState": e.Src,
+	}).Debugf("OLT state has changed")
+}
+
+func (o OltDevice) sendOltIndication(msg OltIndicationMessage, stream openolt.Openolt_EnableIndicationServer) {
+	data := &openolt.Indication_OltInd{OltInd: &openolt.OltIndication{OperState: msg.OperState.String()}}
+	if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
+		log.Error("Failed to send Indication_OltInd: %v", err)
+	}
+
+	log.WithFields(log.Fields{
+		"OperState": msg.OperState,
+	}).Debug("Sent Indication_OltInd")
+}
+
+func (o OltDevice) sendNniIndication(msg NniIndicationMessage, stream openolt.Openolt_EnableIndicationServer) {
+	nni, _ := o.getNniById(msg.NniPortID)
+	nni.OperState = UP
+	operData := &openolt.Indication_IntfOperInd{IntfOperInd: &openolt.IntfOperIndication{
+		Type: nni.Type,
+		IntfId: nni.ID,
+		OperState: nni.OperState.String(),
+	}}
+
+	if err := stream.Send(&openolt.Indication{Data: operData}); err != nil {
+		log.Error("Failed to send Indication_IntfOperInd for NNI: %v", err)
+	}
+
+	log.WithFields(log.Fields{
+		"Type": nni.Type,
+		"IntfId": nni.ID,
+		"OperState": nni.OperState.String(),
+	}).Debug("Sent Indication_IntfOperInd for NNI")
+}
+
+func (o OltDevice) sendPonIndication(msg PonIndicationMessage, stream openolt.Openolt_EnableIndicationServer) {
+	pon, _ := o.getPonById(msg.PonPortID)
+	pon.OperState = UP
+	discoverData := &openolt.Indication_IntfInd{IntfInd: &openolt.IntfIndication{
+		IntfId: pon.ID,
+		OperState: pon.OperState.String(),
+	}}
+
+	if err := stream.Send(&openolt.Indication{Data: discoverData}); err != nil {
+		log.Error("Failed to send Indication_IntfInd: %v", err)
+	}
+
+	log.WithFields(log.Fields{
+		"IntfId": pon.ID,
+		"OperState": pon.OperState.String(),
+	}).Debug("Sent Indication_IntfInd")
+
+	operData := &openolt.Indication_IntfOperInd{IntfOperInd: &openolt.IntfOperIndication{
+		Type: pon.Type,
+		IntfId: pon.ID,
+		OperState: pon.OperState.String(),
+	}}
+
+	if err := stream.Send(&openolt.Indication{Data: operData}); err != nil {
+		log.Error("Failed to send Indication_IntfOperInd for PON: %v", err)
+	}
+
+	log.WithFields(log.Fields{
+		"Type": pon.Type,
+		"IntfId": pon.ID,
+		"OperState": pon.OperState.String(),
+	}).Debug("Sent Indication_IntfOperInd for PON")
+}
+
+func (o OltDevice) oltChannels(stream openolt.Openolt_EnableIndicationServer) {
+
+	for message := range o.channel {
+
+		_msg, _ok := message.(Message)
+		if _ok {
+			log.WithFields(log.Fields{
+				"oltId": o.ID,
+				"messageType": _msg.Type,
+			}).Debug("Received message")
+
+			switch _msg.Data.(type) {
+			case OltIndicationMessage:
+				msg, _ := _msg.Data.(OltIndicationMessage)
+				o.InternalState.Event("enable")
+				o.sendOltIndication(msg, stream)
+			case NniIndicationMessage:
+				msg, _ := _msg.Data.(NniIndicationMessage)
+				o.sendNniIndication(msg, stream)
+			case PonIndicationMessage:
+				msg, _ := _msg.Data.(PonIndicationMessage)
+				o.sendPonIndication(msg, stream)
+			case OnuDiscIndicationMessage:
+				msg, _ := _msg.Data.(OnuDiscIndicationMessage)
+				msg.Onu.InternalState.Event("discover")
+				msg.Onu.sendOnuDiscIndication(msg, stream)
+			case OnuIndicationMessage:
+				msg, _ := _msg.Data.(OnuIndicationMessage)
+				pon, _ := o.getPonById(msg.PonPortID)
+				onu, _ := pon.getOnuBySn(msg.OnuSN)
+				onu.InternalState.Event("enable")
+				onu.sendOnuIndication(msg, stream)
+			default:
+				log.Warnf("Received unkown message data %v for type %v", _msg.Data, _msg.Type)
+			}
+		} else {
+			log.Warnf("Received unkown message %v", message)
+		}
+
+	}
+}
+
+// GRPC Endpoints
+
+func (o OltDevice) ActivateOnu(context context.Context, onu *openolt.Onu) (*openolt.Empty, error)  {
+	log.WithFields(log.Fields{
+		"onuSerialNumber": onu.SerialNumber,
+	}).Info("Received ActivateOnu call from VOLTHA")
+	msg := Message{
+		Type:      OnuIndication,
+		Data:      OnuIndicationMessage{
+			OnuSN:     onu.SerialNumber,
+			PonPortID: onu.IntfId,
+			OperState: UP,
+		},
+	}
+	o.channel <- msg
+	return new(openolt.Empty) , nil
+}
+
+func (o OltDevice) DeactivateOnu(context.Context, *openolt.Onu) (*openolt.Empty, error)  {
+	log.Error("DeactivateOnu not implemented")
+	return new(openolt.Empty) , nil
+}
+
+func (o OltDevice) DeleteOnu(context.Context, *openolt.Onu) (*openolt.Empty, error)  {
+	log.Error("DeleteOnu not implemented")
+	return new(openolt.Empty) , nil
+}
+
+func (o OltDevice) DisableOlt(context.Context, *openolt.Empty) (*openolt.Empty, error)  {
+	log.Error("DisableOlt not implemented")
+	return new(openolt.Empty) , nil
+}
+
+func (o OltDevice) DisablePonIf(context.Context, *openolt.Interface) (*openolt.Empty, error)  {
+	log.Error("DisablePonIf not implemented")
+	return new(openolt.Empty) , nil
+}
+
+func (o OltDevice) EnableIndication(_ *openolt.Empty, stream openolt.Openolt_EnableIndicationServer) error  {
+	log.WithField("oltId", o.ID).Info("OLT receives EnableIndication call from VOLTHA")
+	o.Enable(stream)
+	return nil
+}
+
+func (o OltDevice) EnablePonIf(context.Context, *openolt.Interface) (*openolt.Empty, error)  {
+	log.Error("EnablePonIf not implemented")
+	return new(openolt.Empty) , nil
+}
+
+func (o OltDevice) FlowAdd(context.Context, *openolt.Flow) (*openolt.Empty, error)  {
+	log.Error("FlowAdd not implemented")
+	return new(openolt.Empty) , nil
+}
+
+func (o OltDevice) FlowRemove(context.Context, *openolt.Flow) (*openolt.Empty, error)  {
+	log.Error("FlowRemove not implemented")
+	return new(openolt.Empty) , nil
+}
+
+func (o OltDevice) HeartbeatCheck(context.Context, *openolt.Empty) (*openolt.Heartbeat, error)  {
+	log.Error("HeartbeatCheck not implemented")
+	return new(openolt.Heartbeat) , nil
+}
+
+func (o OltDevice) GetDeviceInfo(context.Context, *openolt.Empty) (*openolt.DeviceInfo, error)  {
+
+	log.WithField("oltId", o.ID).Info("OLT receives GetDeviceInfo call from VOLTHA")
+	devinfo := new(openolt.DeviceInfo)
+	devinfo.Vendor = "BBSim"
+	devinfo.Model = "asfvolt16"
+	devinfo.HardwareVersion = ""
+	devinfo.FirmwareVersion = ""
+	devinfo.Technology = "xgspon"
+	devinfo.PonPorts = 1
+	devinfo.OnuIdStart = 1
+	devinfo.OnuIdEnd = 255
+	devinfo.AllocIdStart = 1024
+	devinfo.AllocIdEnd = 16383
+	devinfo.GemportIdStart = 1024
+	devinfo.GemportIdEnd = 65535
+	devinfo.FlowIdStart = 1
+	devinfo.FlowIdEnd = 16383
+
+	return devinfo, nil
+}
+
+func (o OltDevice) OmciMsgOut(context.Context, *openolt.OmciMsg) (*openolt.Empty, error)  {
+	log.Error("OmciMsgOut not implemented")
+	return new(openolt.Empty) , nil
+}
+
+func (o OltDevice) OnuPacketOut(context.Context, *openolt.OnuPacket) (*openolt.Empty, error)  {
+	log.Error("OnuPacketOut not implemented")
+	return new(openolt.Empty) , nil
+}
+
+func (o OltDevice) Reboot(context.Context, *openolt.Empty) (*openolt.Empty, error)  {
+	log.Error("Reboot not implemented")
+	return new(openolt.Empty) , nil
+}
+
+func (o OltDevice) ReenableOlt(context.Context, *openolt.Empty) (*openolt.Empty, error) {
+	log.Error("ReenableOlt not implemented")
+	return new(openolt.Empty) , nil
+}
+
+func (o OltDevice) UplinkPacketOut(context context.Context, packet *openolt.UplinkPacket) (*openolt.Empty, error) {
+	log.Error("UplinkPacketOut not implemented")
+	return new(openolt.Empty) , nil
+}
+
+func (o OltDevice) CollectStatistics(context.Context, *openolt.Empty) (*openolt.Empty, error)  {
+	log.Error("CollectStatistics not implemented")
+	return new(openolt.Empty) , nil
+}
+
+func (o OltDevice) CreateTconts(context context.Context, packet *openolt.Tconts) (*openolt.Empty, error) {
+	log.Error("CreateTconts not implemented")
+	return new(openolt.Empty) , nil
+}
+
+func (o OltDevice) RemoveTconts(context context.Context, packet *openolt.Tconts) (*openolt.Empty, error) {
+	log.Error("RemoveTconts not implemented")
+	return new(openolt.Empty) , nil
+}
+
+func (o OltDevice) GetOnuInfo(context context.Context, packet *openolt.Onu) (*openolt.OnuIndication, error) {
+	log.Error("GetOnuInfo not implemented")
+	return new(openolt.OnuIndication) , nil
+}
+
+func (o OltDevice) GetPonIf(context context.Context, packet *openolt.Interface) (*openolt.IntfIndication, error) {
+	log.Error("GetPonIf not implemented")
+	return new(openolt.IntfIndication) , nil
+}
\ No newline at end of file
diff --git a/internal/bbsim/devices/onu.go b/internal/bbsim/devices/onu.go
new file mode 100644
index 0000000..2b7ee07
--- /dev/null
+++ b/internal/bbsim/devices/onu.go
@@ -0,0 +1,91 @@
+package devices
+
+import (
+	"gerrit.opencord.org/bbsim/api"
+	"github.com/looplab/fsm"
+	log "github.com/sirupsen/logrus"
+)
+
+func CreateONU(olt OltDevice, pon PonPort, id uint32) Onu {
+		o := Onu{
+			ID: id,
+			OperState: DOWN,
+			PonPortID: pon.ID,
+			PonPort: pon,
+		}
+		o.SerialNumber = o.NewSN(olt.ID, pon.ID, o.ID)
+
+		o.InternalState = fsm.NewFSM(
+			"created",
+			fsm.Events{
+				{Name: "discover", Src: []string{"created"}, Dst: "discovered"},
+				{Name: "enable", Src: []string{"discovered"}, Dst: "enabled"},
+			},
+			fsm.Callbacks{
+				"enter_state": func(e *fsm.Event) {
+					olt.stateChange(e)
+				},
+			},
+		)
+		return o
+}
+
+func (o Onu) stateChange(e *fsm.Event) {
+	log.WithFields(log.Fields{
+		"onuID": o.ID,
+		"onuSN": o.SerialNumber,
+		"dstState": e.Dst,
+		"srcState": e.Src,
+	}).Debugf("ONU state has changed")
+}
+
+func (o Onu) NewSN(oltid int, intfid uint32, onuid uint32) *openolt.SerialNumber {
+
+	sn := new(openolt.SerialNumber)
+
+	sn = new(openolt.SerialNumber)
+	sn.VendorId = []byte("BBSM")
+	sn.VendorSpecific = []byte{0, byte(oltid % 256), byte(intfid), byte(onuid)}
+
+	return sn
+}
+
+func (o Onu) sendOnuDiscIndication(msg OnuDiscIndicationMessage, stream openolt.Openolt_EnableIndicationServer) {
+	discoverData := &openolt.Indication_OnuDiscInd{OnuDiscInd: &openolt.OnuDiscIndication{
+		IntfId: msg.Onu.PonPortID,
+		SerialNumber: msg.Onu.SerialNumber,
+	}}
+	if err := stream.Send(&openolt.Indication{Data: discoverData}); err != nil {
+		log.Error("Failed to send Indication_OnuDiscInd: %v", err)
+	}
+	log.WithFields(log.Fields{
+		"IntfId": msg.Onu.PonPortID,
+		"SerialNumber": msg.Onu.SerialNumber,
+	}).Debug("Sent Indication_OnuDiscInd")
+}
+
+func (o Onu) sendOnuIndication(msg OnuIndicationMessage, stream openolt.Openolt_EnableIndicationServer) {
+	// NOTE voltha returns an ID, but if we use that ID then it complains:
+	// expected_onu_id: 1, received_onu_id: 1024, event: ONU-id-mismatch, can happen if both voltha and the olt rebooted
+	// so we're using the internal ID that is 1
+	// o.ID = msg.OnuID
+	o.OperState = msg.OperState
+
+	indData := &openolt.Indication_OnuInd{OnuInd: &openolt.OnuIndication{
+		IntfId: o.PonPortID,
+		OnuId: o.ID,
+		OperState: o.OperState.String(),
+		AdminState: o.OperState.String(),
+		SerialNumber: o.SerialNumber,
+	}}
+	if err := stream.Send(&openolt.Indication{Data: indData}); err != nil {
+		log.Error("Failed to send Indication_OnuInd: %v", err)
+	}
+	log.WithFields(log.Fields{
+		"IntfId": o.PonPortID,
+		"OnuId": o.ID,
+		"OperState": msg.OperState.String(),
+		"AdminState": msg.OperState.String(),
+		"SerialNumber": o.SerialNumber,
+	}).Debug("Sent Indication_OnuInd")
+}
\ No newline at end of file
diff --git a/internal/bbsim/devices/types.go b/internal/bbsim/devices/types.go
new file mode 100644
index 0000000..bb1be32
--- /dev/null
+++ b/internal/bbsim/devices/types.go
@@ -0,0 +1,143 @@
+package devices
+
+import (
+	"bytes"
+	"errors"
+	"fmt"
+	"github.com/looplab/fsm"
+	"gerrit.opencord.org/bbsim/api"
+)
+
+// Devices
+type Onu struct {
+	ID uint32
+	PonPortID uint32
+	PonPort PonPort
+	InternalState *fsm.FSM
+
+	OperState OperState
+	SerialNumber *openolt.SerialNumber
+}
+
+
+
+type NniPort struct {
+	// BBSIM Internals
+	ID uint32
+
+	// PON Attributes
+	OperState OperState
+	Type string
+}
+
+type PonPort struct {
+	// BBSIM Internals
+	ID uint32
+	NumOnu int
+	Onus []Onu
+
+	// PON Attributes
+	OperState OperState
+	Type string
+
+	// NOTE do we need a state machine for the PON Ports?
+}
+
+func (p PonPort) getOnuBySn(sn *openolt.SerialNumber) (*Onu, error) {
+	for _, onu := range p.Onus {
+		if bytes.Equal(onu.SerialNumber.VendorSpecific, sn.VendorSpecific) {
+			return &onu, nil
+		}
+	}
+	return nil, errors.New(fmt.Sprintf("Cannot find Onu with serial number %d in PonPort %d", sn, p.ID))
+}
+
+type OltDevice struct {
+	// BBSIM Internals
+	ID int
+	NumNni int
+	NumPon int
+	NumOnuPerPon int
+	InternalState *fsm.FSM
+	channel chan interface{}
+
+	Pons []PonPort
+	Nnis []NniPort
+
+	// OLT Attributes
+	OperState int
+	AdminState int
+}
+
+// BBSim Internals
+type MessageType int
+
+const (
+	OltIndication     MessageType = 0
+	NniIndication     MessageType = 1
+	PonIndication     MessageType = 2
+	OnuDiscIndication MessageType = 3
+	OnuIndication     MessageType = 4
+	OMCI              MessageType = 5
+)
+
+func (m MessageType) String() string {
+	names := [...]string{
+		"OltIndication",
+		"NniIndication",
+		"PonIndication",
+		"OnuDiscIndication",
+		"OnuIndication",
+		"OMCI",
+	}
+	return names[m]
+}
+
+type Message struct {
+	Type      MessageType
+	Data 	  interface{}
+}
+
+type OltIndicationMessage struct {
+	OperState OperState
+}
+
+type NniIndicationMessage struct {
+	OperState OperState
+	NniPortID uint32
+}
+
+type PonIndicationMessage struct {
+	OperState OperState
+	PonPortID uint32
+}
+
+type OnuDiscIndicationMessage struct {
+	OperState OperState
+	Onu       Onu
+}
+
+type OnuIndicationMessage struct {
+	OperState OperState
+	PonPortID uint32
+	OnuID     uint32
+	OnuSN     *openolt.SerialNumber
+}
+
+
+type OperState int
+
+const (
+	UP OperState = 0
+
+	// The device has been discovered, but not yet activated
+	DOWN OperState = 1
+)
+
+func (m OperState) String() string {
+	names := [...]string{
+		"up",
+		"down",
+	}
+	return names[m]
+}
\ No newline at end of file
diff --git a/internal/bbsim/types.go b/internal/bbsim/types.go
new file mode 100644
index 0000000..bdba10e
--- /dev/null
+++ b/internal/bbsim/types.go
@@ -0,0 +1,12 @@
+package main
+
+// General
+
+type CliOptions struct {
+	OltID 	 	 int
+	NumNniPerOlt int
+	NumPonPerOlt int
+	NumOnuPerPon int
+}
+
+