// This source code is part of the near-RT RIC (RAN Intelligent Controller)
// platform project (RICP).
-
package rNibWriter
import (
+ "e2mgr/configuration"
+ "encoding/json"
"fmt"
"gerrit.o-ran-sc.org/r/ric-plt/nodeb-rnib.git/common"
"gerrit.o-ran-sc.org/r/ric-plt/nodeb-rnib.git/entities"
- "gerrit.o-ran-sc.org/r/ric-plt/sdlgo"
"github.com/golang/protobuf/proto"
)
-var writerPool *common.Pool
+const (
+ E2TAddressesKey = "E2TAddresses"
+ RanAddedEvent = "ADDED"
+ RanUpdatedEvent = "UPDATED"
+ RanDeletedEvent = "DELETED"
+)
type rNibWriterInstance struct {
- sdl *common.ISdlInstance
- namespace string
+ sdl common.ISdlInstance
+ rnibWriterConfig configuration.RnibWriterConfig
}
/*
RNibWriter interface allows saving data to the redis DB
*/
type RNibWriter interface {
- SaveNodeb(nbIdentity *entities.NbIdentity, nb *entities.NodebInfo) error
+ SaveNodeb(nodebInfo *entities.NodebInfo) error
UpdateNodebInfo(nodebInfo *entities.NodebInfo) error
+ UpdateNodebInfoAndPublish(nodebInfo *entities.NodebInfo) error
SaveRanLoadInformation(inventoryName string, ranLoadInformation *entities.RanLoadInformation) error
+ SaveE2TInstance(e2tInstance *entities.E2TInstance) error
+ SaveE2TAddresses(addresses []string) error
+ RemoveE2TInstance(e2tAddress string) error
+ UpdateGnbCells(nodebInfo *entities.NodebInfo, servedNrCells []*entities.ServedNRCell) error
+ RemoveServedNrCells(inventoryName string, servedNrCells []*entities.ServedNRCell) error
+ UpdateNodebInfoOnConnectionStatusInversion(nodebInfo *entities.NodebInfo, ent string) error
+ SaveGeneralConfiguration(config *entities.GeneralConfiguration) error
+ RemoveEnb(nodebInfo *entities.NodebInfo) error
+ RemoveServedCells(inventoryName string, servedCells []*entities.ServedCellInfo) error
+ UpdateEnb(nodebInfo *entities.NodebInfo, servedCells []*entities.ServedCellInfo) error
+ AddNbIdentity(nodeType entities.Node_Type, nbIdentity *entities.NbIdentity) error
+ RemoveNbIdentity(nodeType entities.Node_Type, nbIdentity *entities.NbIdentity) error
+ AddEnb(nodebInfo *entities.NodebInfo) error
+ UpdateNbIdentities(nodeType entities.Node_Type, oldNbIdentities []*entities.NbIdentity, newNbIdentities []*entities.NbIdentity) error
}
-
/*
-Init initializes the infrastructure required for the RNibWriter instance
+GetRNibWriter returns reference to RNibWriter
*/
-func Init(namespace string, poolSize int) {
- initPool(poolSize,
- func() interface{} {
- var sdlI common.ISdlInstance = sdlgo.NewSdlInstance(namespace, sdlgo.NewDatabase())
- return &rNibWriterInstance{sdl: &sdlI, namespace: namespace}
- },
- func(obj interface{}) {
- (*obj.(*rNibWriterInstance).sdl).Close()
- })
+
+func GetRNibWriter(sdl common.ISdlInstance, rnibWriterConfig configuration.RnibWriterConfig) RNibWriter {
+ return &rNibWriterInstance{sdl: sdl, rnibWriterConfig: rnibWriterConfig}
}
-/*
-InitPool initializes the writer's instances pool
-*/
-func initPool(poolSize int, newObj func() interface{}, destroyObj func(interface{})) {
- writerPool = common.NewPool(poolSize, newObj, destroyObj)
+func getChannelsAndEventsPair(channel string, ranName string, event string) []string {
+ return []string{channel, fmt.Sprintf("%s_%s", ranName, event)}
}
-/*
-GetRNibWriter returns reference to RNibWriter
-*/
-func GetRNibWriter() RNibWriter {
- return &rNibWriterInstance{}
+
+func (w *rNibWriterInstance) AddNbIdentity(nodeType entities.Node_Type, nbIdentity *entities.NbIdentity) error {
+ nbIdData, err := proto.Marshal(nbIdentity)
+
+ if err != nil {
+ return common.NewInternalError(err)
+ }
+
+ err = w.sdl.AddMember(nodeType.String(), nbIdData)
+
+ if err != nil {
+ return common.NewInternalError(err)
+ }
+ return nil
+}
+
+func (w *rNibWriterInstance) RemoveServedNrCells(inventoryName string, servedNrCells []*entities.ServedNRCell) error {
+ cellKeysToRemove := buildServedNRCellKeysToRemove(inventoryName, servedNrCells)
+
+ err := w.sdl.Remove(cellKeysToRemove)
+
+ if err != nil {
+ return common.NewInternalError(err)
+ }
+
+ return nil
+}
+
+func (w *rNibWriterInstance) RemoveServedCells(inventoryName string, servedCells []*entities.ServedCellInfo) error {
+ cellKeysToRemove := buildServedCellInfoKeysToRemove(inventoryName, servedCells)
+
+ err := w.sdl.Remove(cellKeysToRemove)
+
+ if err != nil {
+ return common.NewInternalError(err)
+ }
+
+ return nil
+}
+
+func (w *rNibWriterInstance) SaveGeneralConfiguration(config *entities.GeneralConfiguration) error {
+
+ err := w.SaveWithKeyAndMarshal(common.BuildGeneralConfigurationKey(), config)
+
+ if err != nil {
+ return common.NewInternalError(err)
+ }
+
+ return nil
}
+
/*
SaveNodeb saves nodeB entity data in the redis DB according to the specified data model
*/
-func (*rNibWriterInstance) SaveNodeb(nbIdentity *entities.NbIdentity, entity *entities.NodebInfo) error {
- w := writerPool.Get().(*rNibWriterInstance)
- isNotEmptyIdentity := isNotEmpty(nbIdentity)
+func (w *rNibWriterInstance) SaveNodeb(nodebInfo *entities.NodebInfo) error {
+
+ data, err := proto.Marshal(nodebInfo)
- if isNotEmptyIdentity && entity.GetNodeType() == entities.Node_UNKNOWN {
- return common.NewValidationError(fmt.Sprintf("#rNibWriter.saveNodeB - Unknown responding node type, entity: %v", entity))
- }
- defer writerPool.Put(w)
- data, err := proto.Marshal(entity)
if err != nil {
return common.NewInternalError(err)
}
+
var pairs []interface{}
- key, rNibErr := common.ValidateAndBuildNodeBNameKey(nbIdentity.InventoryName)
+ key, rNibErr := common.ValidateAndBuildNodeBNameKey(nodebInfo.RanName)
+
if rNibErr != nil {
return rNibErr
}
+
pairs = append(pairs, key, data)
- if isNotEmptyIdentity {
- key, rNibErr = common.ValidateAndBuildNodeBIdKey(entity.GetNodeType().String(), nbIdentity.GlobalNbId.GetPlmnId(), nbIdentity.GlobalNbId.GetNbId())
+ if nodebInfo.GlobalNbId != nil {
+
+ key, rNibErr = common.ValidateAndBuildNodeBIdKey(nodebInfo.GetNodeType().String(), nodebInfo.GlobalNbId.GetPlmnId(), nodebInfo.GlobalNbId.GetNbId())
if rNibErr != nil {
return rNibErr
}
pairs = append(pairs, key, data)
}
- if entity.GetEnb() != nil {
- pairs, rNibErr = appendEnbCells(nbIdentity, entity.GetEnb().GetServedCells(), pairs)
+ if nodebInfo.GetEnb() != nil {
+ pairs, rNibErr = appendEnbCells(nodebInfo.RanName, nodebInfo.GetEnb().GetServedCells(), pairs)
if rNibErr != nil {
return rNibErr
}
}
- if entity.GetGnb() != nil {
- pairs, rNibErr = appendGnbCells(nbIdentity, entity.GetGnb().GetServedNrCells(), pairs)
+
+ if nodebInfo.GetGnb() != nil {
+ pairs, rNibErr = appendGnbCells(nodebInfo.RanName, nodebInfo.GetGnb().GetServedNrCells(), pairs)
if rNibErr != nil {
return rNibErr
}
}
- err = (*w.sdl).Set(pairs)
+
+ err = w.sdl.Set(pairs)
+
if err != nil {
return common.NewInternalError(err)
}
- ranNameIdentity := &entities.NbIdentity{InventoryName: nbIdentity.InventoryName}
+ return nil
+}
- if isNotEmptyIdentity {
- nbIdData, err := proto.Marshal(ranNameIdentity)
- if err != nil {
- return common.NewInternalError(err)
- }
- err = (*w.sdl).RemoveMember(entities.Node_UNKNOWN.String(), nbIdData)
- if err != nil {
- return common.NewInternalError(err)
+func (w *rNibWriterInstance) AddEnb(nodebInfo *entities.NodebInfo) error {
+
+ data, err := proto.Marshal(nodebInfo)
+
+ if err != nil {
+ return common.NewInternalError(err)
+ }
+
+ var pairs []interface{}
+ key, rNibErr := common.ValidateAndBuildNodeBNameKey(nodebInfo.RanName)
+
+ if rNibErr != nil {
+ return rNibErr
+ }
+
+ pairs = append(pairs, key, data)
+
+ if nodebInfo.GlobalNbId != nil {
+
+ key, rNibErr = common.ValidateAndBuildNodeBIdKey(nodebInfo.GetNodeType().String(), nodebInfo.GlobalNbId.GetPlmnId(), nodebInfo.GlobalNbId.GetNbId())
+ if rNibErr != nil {
+ return rNibErr
}
- } else {
- nbIdentity = ranNameIdentity
+ pairs = append(pairs, key, data)
}
- nbIdData, err := proto.Marshal(nbIdentity)
+ pairs, rNibErr = appendEnbCells(nodebInfo.RanName, nodebInfo.GetEnb().GetServedCells(), pairs)
+ if rNibErr != nil {
+ return rNibErr
+ }
+
+ channelsAndEvents := getChannelsAndEventsPair(w.rnibWriterConfig.RanManipulationMessageChannel, nodebInfo.RanName, RanAddedEvent)
+ err = w.sdl.SetAndPublish(channelsAndEvents, pairs)
if err != nil {
return common.NewInternalError(err)
}
- err = (*w.sdl).AddMember(entity.GetNodeType().String(), nbIdData)
+
+ return nil
+}
+
+func (w *rNibWriterInstance) UpdateNbIdentities(nodeType entities.Node_Type, oldNbIdentities []*entities.NbIdentity, newNbIdentities []*entities.NbIdentity) error {
+
+ nbIdIdentitiesToRemove, err := w.buildNbIdentitiesMembers(oldNbIdentities)
+ if err != nil {
+ return err
+ }
+
+ err = w.sdl.RemoveMember(nodeType.String(), nbIdIdentitiesToRemove[:]...)
+ if err != nil {
+ return err
+ }
+
+ nbIdIdentitiesToAdd, err := w.buildNbIdentitiesMembers(newNbIdentities)
+ if err != nil {
+ return err
+ }
+
+ err = w.sdl.AddMember(nodeType.String(), nbIdIdentitiesToAdd[:]...)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (w *rNibWriterInstance) UpdateGnbCells(nodebInfo *entities.NodebInfo, servedNrCells []*entities.ServedNRCell) error {
+
+ pairs, err := buildUpdateNodebInfoPairs(nodebInfo)
+
+ if err != nil {
+ return err
+ }
+
+ pairs, err = appendGnbCells(nodebInfo.RanName, servedNrCells, pairs)
+
+ if err != nil {
+ return err
+ }
+
+ channelsAndEvents := getChannelsAndEventsPair(w.rnibWriterConfig.RanManipulationMessageChannel, nodebInfo.RanName, RanUpdatedEvent)
+ err = w.sdl.SetAndPublish(channelsAndEvents, pairs)
+
if err != nil {
return common.NewInternalError(err)
}
+
return nil
}
-/*
-UpdateNodebInfo...
-*/
-func (*rNibWriterInstance) UpdateNodebInfo(nodebInfo *entities.NodebInfo) error {
- w := writerPool.Get().(*rNibWriterInstance)
- defer writerPool.Put(w)
+func buildServedNRCellKeysToRemove(inventoryName string, servedNrCellsToRemove []*entities.ServedNRCell) []string {
+
+ var cellKeysToRemove []string
+
+ for _, cell := range servedNrCellsToRemove {
+
+ key, _ := common.ValidateAndBuildNrCellIdKey(cell.GetServedNrCellInformation().GetCellId())
+
+ if len(key) != 0 {
+ cellKeysToRemove = append(cellKeysToRemove, key)
+ }
+
+ key, _ = common.ValidateAndBuildCellNamePciKey(inventoryName, cell.GetServedNrCellInformation().GetNrPci())
+
+ if len(key) != 0 {
+ cellKeysToRemove = append(cellKeysToRemove, key)
+ }
+ }
+
+ return cellKeysToRemove
+}
+
+func buildServedCellInfoKeysToRemove(inventoryName string, servedCellsToRemove []*entities.ServedCellInfo) []string {
+
+ var cellKeysToRemove []string
+ for _, cell := range servedCellsToRemove {
+
+ key, _ := common.ValidateAndBuildCellIdKey(cell.GetCellId())
+
+ if len(key) != 0 {
+ cellKeysToRemove = append(cellKeysToRemove, key)
+ }
+
+ key, _ = common.ValidateAndBuildCellNamePciKey(inventoryName, cell.GetPci())
+
+ if len(key) != 0 {
+ cellKeysToRemove = append(cellKeysToRemove, key)
+ }
+ }
+
+ return cellKeysToRemove
+}
+
+func buildUpdateNodebInfoPairs(nodebInfo *entities.NodebInfo) ([]interface{}, error) {
nodebNameKey, rNibErr := common.ValidateAndBuildNodeBNameKey(nodebInfo.GetRanName())
if rNibErr != nil {
- return rNibErr
+ return []interface{}{}, rNibErr
}
nodebIdKey, buildNodebIdKeyError := common.ValidateAndBuildNodeBIdKey(nodebInfo.GetNodeType().String(), nodebInfo.GlobalNbId.GetPlmnId(), nodebInfo.GlobalNbId.GetNbId())
data, err := proto.Marshal(nodebInfo)
if err != nil {
- return common.NewInternalError(err)
+ return []interface{}{}, common.NewInternalError(err)
}
- var pairs []interface{}
- pairs = append(pairs, nodebNameKey, data)
+ pairs := []interface{}{nodebNameKey, data}
if buildNodebIdKeyError == nil {
pairs = append(pairs, nodebIdKey, data)
}
- err = (*w.sdl).Set(pairs)
+ return pairs, nil
+}
+
+func (w *rNibWriterInstance) buildRemoveEnbKeys(nodebInfo *entities.NodebInfo) ([]string, error) {
+ keys := buildServedCellInfoKeysToRemove(nodebInfo.GetRanName(), nodebInfo.GetEnb().GetServedCells())
+
+ nodebNameKey, rNibErr := common.ValidateAndBuildNodeBNameKey(nodebInfo.GetRanName())
+
+ if rNibErr != nil {
+ return []string{}, rNibErr
+ }
+
+ keys = append(keys, nodebNameKey)
+
+ nodebIdKey, buildNodebIdKeyError := common.ValidateAndBuildNodeBIdKey(nodebInfo.GetNodeType().String(), nodebInfo.GlobalNbId.GetPlmnId(), nodebInfo.GlobalNbId.GetNbId())
+
+ if buildNodebIdKeyError == nil {
+ keys = append(keys, nodebIdKey)
+ }
+
+ return keys, nil
+}
+
+func (w *rNibWriterInstance) RemoveNbIdentity(nodeType entities.Node_Type, nbIdentity *entities.NbIdentity) error {
+ nbIdData, err := proto.Marshal(nbIdentity)
+ if err != nil {
+ return common.NewInternalError(err)
+ }
+ err = w.sdl.RemoveMember(nodeType.String(), nbIdData)
+ if err != nil {
+ return common.NewInternalError(err)
+ }
+ return nil
+}
+
+func (w *rNibWriterInstance) updateNodebInfo(nodebInfo *entities.NodebInfo, publish bool) error {
+
+ pairs, err := buildUpdateNodebInfoPairs(nodebInfo)
+
+ if err != nil {
+ return err
+ }
+
+ if publish {
+ channelsAndEvents := getChannelsAndEventsPair(w.rnibWriterConfig.RanManipulationMessageChannel, nodebInfo.RanName, RanUpdatedEvent)
+ err = w.sdl.SetAndPublish(channelsAndEvents, pairs)
+ } else {
+ err = w.sdl.Set(pairs)
+ }
if err != nil {
return common.NewInternalError(err)
return nil
}
+/*
+UpdateNodebInfo...
+*/
+func (w *rNibWriterInstance) UpdateNodebInfo(nodebInfo *entities.NodebInfo) error {
+ return w.updateNodebInfo(nodebInfo, false)
+}
+
+/*
+UpdateNodebInfoAndPublish...
+*/
+func (w *rNibWriterInstance) UpdateNodebInfoAndPublish(nodebInfo *entities.NodebInfo) error {
+ return w.updateNodebInfo(nodebInfo, true)
+}
+
+
/*
SaveRanLoadInformation stores ran load information for the provided ran
*/
-func (*rNibWriterInstance) SaveRanLoadInformation(inventoryName string, ranLoadInformation *entities.RanLoadInformation) error {
- w := writerPool.Get().(*rNibWriterInstance)
- defer writerPool.Put(w)
+func (w *rNibWriterInstance) SaveRanLoadInformation(inventoryName string, ranLoadInformation *entities.RanLoadInformation) error {
key, rnibErr := common.ValidateAndBuildRanLoadInformationKey(inventoryName)
var pairs []interface{}
pairs = append(pairs, key, data)
- err = (*w.sdl).Set(pairs)
+ err = w.sdl.Set(pairs)
+
+ if err != nil {
+ return common.NewInternalError(err)
+ }
+
+ return nil
+}
+
+func (w *rNibWriterInstance) SaveE2TInstance(e2tInstance *entities.E2TInstance) error {
+
+ key, rnibErr := common.ValidateAndBuildE2TInstanceKey(e2tInstance.Address)
+
+ if rnibErr != nil {
+ return rnibErr
+ }
+
+ data, err := json.Marshal(e2tInstance)
+
+ if err != nil {
+ return common.NewInternalError(err)
+ }
+
+ var pairs []interface{}
+ pairs = append(pairs, key, data)
+
+ err = w.sdl.Set(pairs)
+
+ if err != nil {
+ return common.NewInternalError(err)
+ }
+
+ return nil
+}
+
+func (w *rNibWriterInstance) SaveE2TAddresses(addresses []string) error {
+
+ data, err := json.Marshal(addresses)
+
+ if err != nil {
+ return common.NewInternalError(err)
+ }
+
+ var pairs []interface{}
+ pairs = append(pairs, E2TAddressesKey, data)
+
+ err = w.sdl.Set(pairs)
+
+ if err != nil {
+ return common.NewInternalError(err)
+ }
+
+ return nil
+}
+
+func (w *rNibWriterInstance) RemoveE2TInstance(address string) error {
+ key, rNibErr := common.ValidateAndBuildE2TInstanceKey(address)
+ if rNibErr != nil {
+ return rNibErr
+ }
+ err := w.sdl.Remove([]string{key})
+
+ if err != nil {
+ return common.NewInternalError(err)
+ }
+ return nil
+}
+
+func (w *rNibWriterInstance) SaveWithKeyAndMarshal(key string, entity interface{}) error {
+
+ data, err := json.Marshal(entity)
+
+ if err != nil {
+ return common.NewInternalError(err)
+ }
+
+ var pairs []interface{}
+ pairs = append(pairs, key, data)
+
+ err = w.sdl.Set(pairs)
if err != nil {
return common.NewInternalError(err)
}
/*
-Close closes writer's pool
+UpdateNodebInfoOnConnectionStatusInversion...
+*/
+func (w *rNibWriterInstance) UpdateNodebInfoOnConnectionStatusInversion(nodebInfo *entities.NodebInfo, event string) error {
+
+ pairs, err := buildUpdateNodebInfoPairs(nodebInfo)
+
+ if err != nil {
+ return err
+ }
+
+ err = w.sdl.SetAndPublish([]string{w.rnibWriterConfig.StateChangeMessageChannel, event}, pairs)
+
+ if err != nil {
+ return common.NewInternalError(err)
+ }
+
+ return nil
+}
+
+func (w *rNibWriterInstance) RemoveEnb(nodebInfo *entities.NodebInfo) error {
+ keysToRemove, err := w.buildRemoveEnbKeys(nodebInfo)
+ if err != nil {
+ return err
+ }
+
+ channelsAndEvents := getChannelsAndEventsPair(w.rnibWriterConfig.RanManipulationMessageChannel, nodebInfo.RanName, RanDeletedEvent)
+ err = w.sdl.RemoveAndPublish(channelsAndEvents, keysToRemove)
+
+ if err != nil {
+ return common.NewInternalError(err)
+ }
+
+ return nil
+}
+
+func (w *rNibWriterInstance) UpdateEnb(nodebInfo *entities.NodebInfo, servedCells []*entities.ServedCellInfo) error {
+
+ pairs, err := buildUpdateNodebInfoPairs(nodebInfo)
+
+ if err != nil {
+ return err
+ }
+
+ pairs, err = appendEnbCells(nodebInfo.RanName, servedCells, pairs)
+
+ if err != nil {
+ return err
+ }
+
+ channelsAndEvents := getChannelsAndEventsPair(w.rnibWriterConfig.RanManipulationMessageChannel, nodebInfo.RanName, RanUpdatedEvent)
+ err = w.sdl.SetAndPublish(channelsAndEvents, pairs)
+
+ if err != nil {
+ return common.NewInternalError(err)
+ }
+
+ return nil
+}
+
+func (w *rNibWriterInstance) buildNbIdentitiesMembers(nbIdentities []*entities.NbIdentity) ([]interface{}, error) {
+
+ var nbIdIdentitiesMembers []interface{}
+ for _, nbIdentity := range nbIdentities {
+
+ nbIdData, err := proto.Marshal(nbIdentity)
+ if err != nil {
+ return nil, common.NewInternalError(err)
+ }
+ nbIdIdentitiesMembers = append(nbIdIdentitiesMembers, nbIdData)
+ }
+
+ return nbIdIdentitiesMembers, nil
+}
+/*
+Close the writer
*/
func Close() {
- writerPool.Close()
+ //Nothing to do
}
-func appendEnbCells(nbIdentity *entities.NbIdentity, cells []*entities.ServedCellInfo, pairs []interface{}) ([]interface{}, error) {
+func appendEnbCells(inventoryName string, cells []*entities.ServedCellInfo, pairs []interface{}) ([]interface{}, error) {
for _, cell := range cells {
cellEntity := entities.Cell{Type: entities.Cell_LTE_CELL, Cell: &entities.Cell_ServedCellInfo{ServedCellInfo: cell}}
cellData, err := proto.Marshal(&cellEntity)
return pairs, rNibErr
}
pairs = append(pairs, key, cellData)
- key, rNibErr = common.ValidateAndBuildCellNamePciKey(nbIdentity.InventoryName, cell.GetPci())
+ key, rNibErr = common.ValidateAndBuildCellNamePciKey(inventoryName, cell.GetPci())
if rNibErr != nil {
return pairs, rNibErr
}
return pairs, nil
}
-func appendGnbCells(nbIdentity *entities.NbIdentity, cells []*entities.ServedNRCell, pairs []interface{}) ([]interface{}, error) {
+func appendGnbCells(inventoryName string, cells []*entities.ServedNRCell, pairs []interface{}) ([]interface{}, error) {
for _, cell := range cells {
cellEntity := entities.Cell{Type: entities.Cell_NR_CELL, Cell: &entities.Cell_ServedNrCell{ServedNrCell: cell}}
cellData, err := proto.Marshal(&cellEntity)
return pairs, rNibErr
}
pairs = append(pairs, key, cellData)
- key, rNibErr = common.ValidateAndBuildCellNamePciKey(nbIdentity.InventoryName, cell.GetServedNrCellInformation().GetNrPci())
+ key, rNibErr = common.ValidateAndBuildCellNamePciKey(inventoryName, cell.GetServedNrCellInformation().GetNrPci())
if rNibErr != nil {
return pairs, rNibErr
}
}
return pairs, nil
}
-
-func isNotEmpty(nbIdentity *entities.NbIdentity) bool {
- return nbIdentity.GlobalNbId != nil && nbIdentity.GlobalNbId.PlmnId != "" && nbIdentity.GlobalNbId.NbId != ""
-}