Commit 203c09a5 authored by Yann Garcia's avatar Yann Garcia
Browse files

Enhance meep-simu5g-mobility-adapter

parent 01bdf3e5
Loading
Loading
Loading
Loading
+60 −53
Changes for go-apps/meep-sandbox-api/server/sandbox_api.go: 60 added lines, 53 removed lines.
Original line number Diff line number Diff line
@@ -1488,23 +1488,64 @@ func deleteUesIfAny(sandbox_name string) {
	}
}

func copyUePhysicalLocation(pl sandboxCtrlClient.PhysicalLocation, userMeta map[string]string) sandboxCtrlClient.PhysicalLocation {
	return sandboxCtrlClient.PhysicalLocation{
		Name:         pl.Name,
		Type_:        pl.Type_,
		GeoData:      pl.GeoData,
		Meta:         pl.Meta,
		UserMeta:     userMeta,
		Connected:    pl.Connected,
		Wireless:     pl.Wireless,
		WirelessType: pl.WirelessType,
		NetChar:      pl.NetChar,
		MacId:        pl.MacId,
	}
}

// ueSpeedClass: hv / lv / zv. SUMO UEs often have velocity 0 (no GIS path);
// classify by name or map icon so monaco-harbor cars/peds are not all stationary.
func ueSpeedClass(pl sandboxCtrlClient.PhysicalLocation, velocityThreshold int) string {
	if pl.GeoData != nil && pl.GeoData.Velocity != 0 {
		if pl.GeoData.Velocity >= float32(velocityThreshold) {
			return "hv"
		}
		return "lv"
	}
	name := strings.ToLower(pl.Name)
	icon := ""
	if pl.Meta != nil {
		icon = pl.Meta["display.map.icon"]
	}
	if strings.HasPrefix(name, "ue-car") || strings.Contains(icon, "car") {
		return "hv"
	}
	if strings.HasPrefix(name, "ue-ped") || strings.Contains(icon, "walk") {
		return "lv"
	}
	return "zv"
}

func parseUes(scenario sandboxCtrlClient.Scenario) (ues map[string][]sandboxCtrlClient.PhysicalLocation, ues_count map[string]int32, err error) {
	log.Debug(">>> parseUes: ", scenario)

	var velocityThreshold = 10 //DEFAULT_VELOCITY_THRESHOLD
	var ok bool

	// Get initial UE counts from scenario meta data
	if scenario.Deployment.UserMeta != nil {
		mecSandboxUserMeta := scenario.Deployment.UserMeta["mec-sandbox"]
		if mecSandboxUserMeta != "" {
			var jsonUeObj map[string]int
			err = json.Unmarshal([]byte(mecSandboxUserMeta), &jsonUeObj)
			if err != nil {
				return nil, nil, err
			// Mixed types (connectivityModel string + count ints); do not Unmarshal into map[string]int.
			var jsonUeObj map[string]interface{}
			if uerr := json.Unmarshal([]byte(mecSandboxUserMeta), &jsonUeObj); uerr != nil {
				log.Error("parseUes: mec-sandbox userMeta: ", uerr.Error())
			} else if raw, ok := jsonUeObj["highVelocitySpeedThreshold"]; ok {
				switch n := raw.(type) {
				case float64:
					velocityThreshold = int(n)
				case int:
					velocityThreshold = n
				}
			if velocityThreshold, ok = jsonUeObj["highVelocitySpeedThreshold"]; !ok {
				velocityThreshold = 0
			}
		}
	}
@@ -1531,48 +1572,14 @@ func parseUes(scenario sandboxCtrlClient.Scenario) (ues map[string][]sandboxCtrl
					pl := nl.PhysicalLocations[l] // Physical Locations

					if pl.Type_ == "UE" && pl.GeoData != nil {
						if pl.GeoData.Velocity == 0 {
							zvList = append(zvList, sandboxCtrlClient.PhysicalLocation{
								Name:         pl.Name,
								Type_:        pl.Type_,
								GeoData:      pl.GeoData,
								Meta:         pl.Meta,
								UserMeta:     userMeta,
								Connected:    pl.Connected,
								Wireless:     pl.Wireless,
								WirelessType: pl.WirelessType,
								NetChar:      pl.NetChar,
								MacId:        pl.MacId,
							})

						} else {
							if float32(pl.GeoData.Velocity) >= float32(velocityThreshold) {
								hvList = append(hvList, sandboxCtrlClient.PhysicalLocation{
									Name:         pl.Name,
									Type_:        pl.Type_,
									GeoData:      pl.GeoData,
									Meta:         pl.Meta,
									UserMeta:     userMeta,
									Connected:    pl.Connected,
									Wireless:     pl.Wireless,
									WirelessType: pl.WirelessType,
									NetChar:      pl.NetChar,
									MacId:        pl.MacId,
								})
							} else {
								lvList = append(lvList, sandboxCtrlClient.PhysicalLocation{
									Name:         pl.Name,
									Type_:        pl.Type_,
									GeoData:      pl.GeoData,
									Meta:         pl.Meta,
									UserMeta:     userMeta,
									Connected:    pl.Connected,
									Wireless:     pl.Wireless,
									WirelessType: pl.WirelessType,
									NetChar:      pl.NetChar,
									MacId:        pl.MacId,
								})
							}
						entry := copyUePhysicalLocation(pl, userMeta)
						switch ueSpeedClass(pl, velocityThreshold) {
						case "hv":
							hvList = append(hvList, entry)
						case "lv":
							lvList = append(lvList, entry)
						default:
							zvList = append(zvList, entry)
						}
					}
				} // End of 'for'statement
@@ -1584,13 +1591,13 @@ func parseUes(scenario sandboxCtrlClient.Scenario) (ues map[string][]sandboxCtrl
	log.Info("parseUes: lvList: ", lvList)
	ues = map[string][]sandboxCtrlClient.PhysicalLocation{
		"defaultStaticUeCount":       zvList,
		"defaultLowVelocityUeCount":  hvList,
		"highVelocitySpeedThreshold": lvList,
		"defaultLowVelocityUeCount":  lvList,
		"highVelocitySpeedThreshold": hvList,
	}
	ues_count = map[string]int32{
		"defaultStaticUeCount":       int32(len(zvList)),
		"defaultLowVelocityUeCount":  int32(len(hvList)),
		"highVelocitySpeedThreshold": int32(len(lvList)),
		"defaultLowVelocityUeCount":  int32(len(lvList)),
		"highVelocitySpeedThreshold": int32(len(hvList)),
	}

	return ues, ues_count, nil
+1 −1
Changes for go-apps/meep-simu5g-mobility-adapter/README.md: 1 added line, 1 removed line.
Original line number Diff line number Diff line
@@ -15,7 +15,7 @@ go build -o bin/meep-simu5g-mobility-adapter ./go-apps/meep-simu5g-mobility-adap
./bin/meep-simu5g-mobility-adapter --config /home/ubuntu-template/etsi-mec-sandbox/tools/simu5g/monaco-harbor-integration-config.json
```

Listens on **UDP 9100**. monaco-harbor INI must send to host B (`172.29.10.53:9100`). After each GIS write the adapter GETs `poaInRange` and logs when the set changes.
Listens on **UDP 9100**. monaco-harbor INI must send to host B (`172.29.10.53:9100`). After each GIS write the adapter GETs `poaInRange` and logs `servingPoa` (wifi > 5g > 4g from monaco names) when the set changes. That is a **logical GIS** attachment, not Simu5G EN-DC / X2 handover (Story 3.4 on host S).

Optional: `--gis-url http://<gis-cluster-ip>/gis/v1` or `--sandbox <ns>` if `gis.sandboxName` in the JSON is wrong.

+39 −3
Changes for go-apps/meep-simu5g-mobility-adapter/publisher/gis.go: 39 added lines, 3 removed lines.
Original line number Diff line number Diff line
@@ -127,6 +127,37 @@ func (p *GISPublisher) PublishUE(ctx context.Context, cfg *bridge.IntegrationCon
	p.logPoaInRange(ctx, asset)
}

// gisLogicalServingPoa approximates GIS selectPoa for monaco names: wifi > 5g > 4g.
// GIS GET GeoDataAsset has poaInRange only (no serving POA field). This is a
// logical attachment, not Simu5G EN-DC / X2 handover (Story 3.4 on host S).
func gisLogicalServingPoa(poas []string) string {
	var wifi, nr, lte string
	for _, p := range poas {
		n := strings.ToLower(p)
		switch {
		case strings.Contains(n, "wifi"):
			wifi = p
		case strings.Contains(n, "gnb") || strings.Contains(n, "5g"):
			nr = p
		case strings.Contains(n, "enb") || strings.Contains(n, "4g"):
			lte = p
		}
	}
	if wifi != "" {
		return wifi
	}
	if nr != "" {
		return nr
	}
	if lte != "" {
		return lte
	}
	if len(poas) > 0 {
		return poas[0]
	}
	return ""
}

func (p *GISPublisher) logPoaInRange(ctx context.Context, asset string) {
	geo, resp, err := p.client.GeospatialDataApi.GetGeoDataByName(ctx, asset, nil)
	if err != nil {
@@ -136,12 +167,17 @@ func (p *GISPublisher) logPoaInRange(ctx context.Context, asset string) {
		return
	}
	joined := strings.Join(geo.PoaInRange, ",")
	serving := gisLogicalServingPoa(geo.PoaInRange)
	key := joined + "|" + serving
	prev, ok := p.lastPoa.Load(asset)
	if ok && prev == joined {
	if ok && prev == key {
		return
	}
	p.lastPoa.Store(asset, joined)
	log.Info("GIS poaInRange asset=" + asset + " poas=[" + joined + "]")
	p.lastPoa.Store(asset, key)
	if serving == "" {
		serving = "none"
	}
	log.Info("GIS poaInRange asset=" + asset + " servingPoa=" + serving + " poas=[" + joined + "] (logical GIS; not Simu5G RAN HO)")
}

func (p *GISPublisher) updateWithRetry(ctx context.Context, assetName string, ue bridge.UELine) error {
+15 −0
Changes for go-apps/meep-simu5g-mobility-adapter/publisher/gis_test.go: 15 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -12,6 +12,21 @@ import (
	bridge "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-simu5g-bridge"
)

func TestGisLogicalServingPoa(t *testing.T) {
	if gisLogicalServingPoa(nil) != "" {
		t.Fatal("empty")
	}
	if g := gisLogicalServingPoa([]string{"poa-enb", "poa-gnb"}); g != "poa-gnb" {
		t.Fatalf("nr prefer %s", g)
	}
	if g := gisLogicalServingPoa([]string{"poa-enb"}); g != "poa-enb" {
		t.Fatalf("lte %s", g)
	}
	if g := gisLogicalServingPoa([]string{"poa-wifi", "poa-gnb", "poa-enb"}); g != "poa-wifi" {
		t.Fatalf("wifi %s", g)
	}
}

func TestGeoDataFromUEPointOrder(t *testing.T) {
	geo := GeoDataFromUE("ue-car-0", bridge.UELine{
		LatDeg:   43.728,
+6 −0
Changes for go-apps/meep-simu5g-rnis-adapter/README.md: 6 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -9,3 +9,9 @@ go run . --config /path/to/simu5g-config.json
```

Uses `rni.pollIntervalMs` and `rni.meAppInst` from the shared integration config.

Lab R3.4 one-shot (S KPIs, no Redis write):

```bash
go run . --config /path/to/simu5g-config.json --once
```
Loading