Commit 9ed3252d authored by Yann Garcia's avatar Yann Garcia
Browse files

Add support of V2X message interoperability

parent 5c490713
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -22,6 +22,8 @@ image:
    MEEP_SANDBOX_NAME: {{.SandboxName}}
    MEEP_SVC_PATH: /vis/v2
    MEEP_HOST_URL: {{.HostUrl}}
    MEEP_BROKER: mqtt://test.mosquito.org:1338
    MEEP_POA_LIST: poa-5g1
    {{- if .IsMepService }}
    MEEP_MEP_NAME: {{.MepName}}
    {{- end }}
+1 −1
Original line number Diff line number Diff line
@@ -1618,7 +1618,7 @@ components:
        msgType:
          description: Subscribed V2X message type. Its value is defined by the standardization organization indicated by the attribute stdOrganization. See note 3.
          items:
            type: string
            type: '#/components/schemas/msgType'
          minItems: 0
          type: array
          x-etsi-mec-cardinality: 0..N
+2 −0
Original line number Diff line number Diff line
@@ -5,6 +5,8 @@ echo "MEEP_HOST_URL: ${MEEP_HOST_URL}"
echo "MEEP_SANDBOX_NAME: ${MEEP_SANDBOX_NAME}"
echo "MEEP_MEP_NAME: ${MEEP_MEP_NAME}"
echo "MEEP_CODECOV: ${MEEP_CODECOV}"
echo "MEEP_BROKER: ${MEEP_BROKER}" # E.g. mqtt://test.mosquito.org:1338
echo "MEEP_POA_LIST: ${MEEP_POA_LIST}" # E.g. poa-5g1,poa-5g2

if [[ ! -z "${MEEP_MEP_NAME}" ]]; then
    svcPath="${MEEP_SANDBOX_NAME}/${MEEP_MEP_NAME}"
+4 −0
Original line number Diff line number Diff line
@@ -58,6 +58,7 @@ github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:Htrtb
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/eclipse/paho.mqtt.golang v1.4.2/go.mod h1:JGt0RsEwEX+Xa/agj90YJ9d9DH2b7upDZMK9HRbFvCA=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
@@ -365,6 +366,7 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
@@ -382,6 +384,8 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+234 −22
Original line number Diff line number Diff line
@@ -41,6 +41,8 @@ const postgisPwd = "pwd"
type SbiCfg struct {
	ModuleName     string
	SandboxName    string
	V2xBroker      string
	PoaList        []string
	MepName        string
	RedisAddr      string
	InfluxAddr     string
@@ -48,6 +50,7 @@ type SbiCfg struct {
	PostgisPort    string
	Locality       []string
	ScenarioNameCb func(string)
	V2xNotify      func(v2xMessage []byte, v2xType int32, longitude *float32, latitude *float32)
	CleanUpCb      func()
}

@@ -58,12 +61,15 @@ type VisSbi struct {
	scenarioName             string
	localityEnabled          bool
	locality                 map[string]bool
	v2xBroker                string
	poaList                  []string
	mqLocal                  *mq.MsgQueue
	handlerId                int
	apiMgr                   *sam.SwaggerApiMgr
	activeModel              *mod.Model
	trafficMgr               *tm.TrafficMgr
	updateScenarioNameCB     func(string)
	v2xNotify                func(v2xMessage []byte, v2xType int32, longitude *float32, latitude *float32)
	cleanUpCB                func()
	mutex                    sync.Mutex
	predictionModelSupported bool
@@ -71,6 +77,43 @@ type VisSbi struct {

var sbi *VisSbi

type UuUnicastProvisioningInfoProInfoUuUnicast struct {
	LocationInfo         *LocationInfo
	NeighbourCellInfo    []UuUniNeighbourCellInfo
	V2xApplicationServer *V2xApplicationServer
}
type UuUnicastProvisioningInfoProInfoUuUnicast_list []UuUnicastProvisioningInfoProInfoUuUnicast
type LocationInfo struct {
	Ecgi    *Ecgi
	GeoArea *LocationInfoGeoArea
}
type UuUniNeighbourCellInfo struct {
	Ecgi *Ecgi
	//FddInfo *FddInfo
	Pci  int32
	Plmn *Plmn
	//TddInfo *TddInfo
}
type V2xApplicationServer struct {
	IpAddress string
	UdpPort   string
}
type Ecgi struct {
	CellId *CellId
	Plmn   *Plmn
}
type CellId struct {
	CellId string
}
type Plmn struct {
	Mcc string
	Mnc string
}
type LocationInfoGeoArea struct {
	Latitude  float32
	Longitude float32
}

// Init - V2XI Service SBI initialization
func Init(cfg SbiCfg) (predictionModelSupported bool, err error) {

@@ -83,7 +126,10 @@ func Init(cfg SbiCfg) (predictionModelSupported bool, err error) {
	sbi.sandboxName = cfg.SandboxName
	sbi.mepName = cfg.MepName
	sbi.scenarioName = ""
	sbi.v2xBroker = cfg.V2xBroker
	sbi.poaList = cfg.PoaList
	sbi.updateScenarioNameCB = cfg.ScenarioNameCb
	sbi.v2xNotify = cfg.V2xNotify
	sbi.cleanUpCB = cfg.CleanUpCb
	// redisAddr = cfg.RedisAddr
	// influxAddr = cfg.InfluxAddr
@@ -128,7 +174,6 @@ func Init(cfg SbiCfg) (predictionModelSupported bool, err error) {
		log.Error("Failed to create model: ", err.Error())
		return false, err
	}

	// Get prediction model support
	predictionModelSupportedEnv := strings.TrimSpace(os.Getenv("MEEP_PREDICT_MODEL_SUPPORTED"))
	if predictionModelSupportedEnv != "" {
@@ -139,9 +184,8 @@ func Init(cfg SbiCfg) (predictionModelSupported bool, err error) {
	}
	log.Info("MEEP_PREDICT_MODEL_SUPPORTED: ", sbi.predictionModelSupported)

	if sbi.predictionModelSupported {
	// Connect to VIS Traffic Manager
		sbi.trafficMgr, err = tm.NewTrafficMgr(sbi.moduleName, sbi.sandboxName, postgisUser, postgisPwd, cfg.PostgisHost, cfg.PostgisPort)
	sbi.trafficMgr, err = tm.NewTrafficMgr(sbi.moduleName, sbi.sandboxName, postgisUser, postgisPwd, cfg.PostgisHost, cfg.PostgisPort, cfg.V2xBroker, cfg.PoaList, cfg.V2xNotify)
	if sbi.trafficMgr.GridFileExists {
		if err != nil {
			log.Error("Failed connection to VIS Traffic Manager: ", err)
@@ -158,7 +202,6 @@ func Init(cfg SbiCfg) (predictionModelSupported bool, err error) {
		_ = sbi.trafficMgr.DeleteTrafficMgr()
		sbi.predictionModelSupported = false
	}
	}

	// Initialize service
	processActiveScenarioUpdate()
@@ -271,10 +314,18 @@ func processActiveScenarioUpdate() {
	// Process new scenario
	var scenarioName = sbi.activeModel.GetScenarioName()
	if scenarioName != sbi.scenarioName {
		log.Info("processActiveScenarioUpdate: Entering in then")
		// Update scenario name
		sbi.scenarioName = scenarioName
		sbi.updateScenarioNameCB(sbi.scenarioName)

		err := initializeV2xMessageDistribution()
		if err != nil {
			log.Error("Failed to initialize V2X message distribution: ", err)
			return
		}

		log.Info("processActiveScenarioUpdate: sbi.scenarioName: ", sbi.scenarioName)
		if sbi.predictionModelSupported {
			// Create new tables
			err := sbi.trafficMgr.CreateTables()
@@ -300,21 +351,105 @@ func processActiveScenarioUpdate() {
			}
			log.Info("Populated VIS DB traffic load table")
		}

	}

}

func initializeV2xMessageDistribution() (err error) {
	poaNameList := sbi.activeModel.GetNodeNames(mod.NodeTypePoa4G, mod.NodeTypePoa5G)
	var validPoaNameList []string
	var ecgi_s []string
	for _, poaName := range poaNameList {
		node := sbi.activeModel.GetNode(poaName)
		if node != nil {
			nl := node.(*dataModel.NetworkLocation)
			if nl.GeoData != nil {
				validPoaNameList = append(validPoaNameList, poaName)
				// Generate Ecgi according to ETSI GS MEC 030 Clause 6.5.5 Type: Ecgi
				mnc := "" // TODO Apply numerical conversion directly, -1 if not initialized
				mcc := ""
				cellId := ""
				ecgi := ""
				switch nl.Type_ {
				case mod.NodeTypePoa4G, mod.NodeTypePoa5G:
					poaParent := sbi.activeModel.GetNodeParent(poaName)
					if zone, ok := poaParent.(*dataModel.Zone); ok {
						zoneParent := sbi.activeModel.GetNodeParent(zone.Name)
						if domain, ok := zoneParent.(*dataModel.Domain); ok {
							if domain.CellularDomainConfig != nil {
								mnc = domain.CellularDomainConfig.Mnc
								mcc = domain.CellularDomainConfig.Mcc
								cellId = domain.CellularDomainConfig.DefaultCellId
							}
						}
					}
					if nl.Poa4GConfig != nil {
						cellId = nl.Poa4GConfig.CellId
					} else if nl.Poa5GConfig != nil {
						cellId = nl.Poa5GConfig.CellId
					}
					//log.Info("=================> cellId: ", cellId)
					//log.Info("=================> mnc: ", mnc)
					//log.Info("=================> mcc: ", mcc)
					// Calculate Ecgi
					cellId_num, err := strconv.Atoi(cellId)
					if err != nil {
						log.Error(err.Error())
						return err
					}
					log.Info("initializeV2xMessageDistribution: cellId_num= ", cellId_num)
					TwentyEigthBits := 0xFFFFFFF //  TS 36.413: E-UTRAN Cell Identity (ECI) and E-UTRAN Cell Global Identification (ECGI)
					eci := cellId_num & TwentyEigthBits
					log.Info("initializeV2xMessageDistribution: eci= ", int(eci))
					mcc_num, err := strconv.Atoi(mcc)
					if err != nil {
						log.Error(err.Error())
						return err
					}
					mnc_num, err := strconv.Atoi(mnc)
					if err != nil {
						log.Error(err.Error())
						return err
					}
					log.Info("initializeV2xMessageDistribution: mcc_num= ", int(mcc_num))
					log.Info("initializeV2xMessageDistribution: mnc_num= ", int(mnc_num))
					log.Info("initializeV2xMessageDistribution: plmn= ", int64(mcc_num&0xFFFFFF*1000+mnc_num&0xFFFFFF))
					var ecgi_num int64
					ecgi_num = int64((mcc_num&0xFFFFFF*1000+mnc_num&0xFFFFFF)<<28) | int64(eci)
					log.Info("initializeV2xMessageDistribution: ecgi_num= ", int(ecgi_num))
					ecgi = strconv.FormatInt(int64(ecgi_num), 10)
					log.Info("initializeV2xMessageDistribution: ecgi= ", ecgi)
				} // End of 'switch' statement
				ecgi_s = append(ecgi_s, ecgi)
			}
		}
	} // End of 'for' statement
	log.Info("initializeV2xMessageDistribution: ecgi_s= ", ecgi_s)
	err = sbi.trafficMgr.InitializeV2xMessageDistribution(validPoaNameList, ecgi_s)
	if err != nil {
		log.Error(err.Error())
		return err
	}
	return nil
}

func populatePoaTable() (err error) {
	poaNameList := sbi.activeModel.GetNodeNames(mod.NodeTypePoa4G, mod.NodeTypePoa5G)
	var validPoaNameList []string
	var gpsCoordinates [][]float32
	for _, poaName := range poaNameList {
		node := sbi.activeModel.GetNode(poaName)
		if node != nil {
			nl := node.(*dataModel.NetworkLocation)
			if nl.GeoData != nil {
				location := nl.GeoData.Location.Coordinates
				validPoaNameList = append(validPoaNameList, poaName)
				gpsCoordinates = append(gpsCoordinates, location)
			}
		}
	err = sbi.trafficMgr.PopulatePoaLoad(poaNameList, gpsCoordinates)
	} // End of 'for' statement
	err = sbi.trafficMgr.PopulatePoaLoad(validPoaNameList, gpsCoordinates)
	if err != nil {
		log.Error(err.Error())
		return err
@@ -329,3 +464,80 @@ func GetPredictedPowerValues(hour int32, inRsrp int32, inRsrq int32, poaName str
	}
	return outRsrp, outRsrq, err
}

func GetInfoUuUnicast(params []string, num_item int) (proInfoUuUnicast UuUnicastProvisioningInfoProInfoUuUnicast_list, err error) {
	resp, err := sbi.trafficMgr.GetInfoUuUnicast(params, num_item)
	log.Info("GetInfoUuUnicast: resp= ", resp)
	proInfoUuUnicast = nil
	if err != nil {
		log.Error(err.Error())
	} else {
		proInfoUuUnicast = make([]UuUnicastProvisioningInfoProInfoUuUnicast, len(resp))
		for i := range resp {
			if resp[i].LocationInfo != nil {
				proInfoUuUnicast[i].LocationInfo = new(LocationInfo)
				if resp[i].LocationInfo.Ecgi != nil {
					proInfoUuUnicast[i].LocationInfo.Ecgi = new(Ecgi)
					if resp[i].LocationInfo.Ecgi.CellId != nil {
						proInfoUuUnicast[i].LocationInfo.Ecgi.CellId = new(CellId)
						proInfoUuUnicast[i].LocationInfo.Ecgi.CellId.CellId = resp[i].LocationInfo.Ecgi.CellId.CellId
					}
					if resp[i].LocationInfo.Ecgi.Plmn != nil {
						proInfoUuUnicast[i].LocationInfo.Ecgi.Plmn = new(Plmn)
						proInfoUuUnicast[i].LocationInfo.Ecgi.Plmn.Mcc = resp[i].LocationInfo.Ecgi.Plmn.Mcc
						proInfoUuUnicast[i].LocationInfo.Ecgi.Plmn.Mnc = resp[i].LocationInfo.Ecgi.Plmn.Mnc
					}
				}
				if resp[i].LocationInfo.GeoArea != nil {
					proInfoUuUnicast[i].LocationInfo.GeoArea = new(LocationInfoGeoArea)
					proInfoUuUnicast[i].LocationInfo.GeoArea.Latitude = resp[i].LocationInfo.GeoArea.Latitude
					proInfoUuUnicast[i].LocationInfo.GeoArea.Longitude = resp[i].LocationInfo.GeoArea.Longitude
				}
			}

			if resp[i].NeighbourCellInfo != nil {
				proInfoUuUnicast[i].NeighbourCellInfo = make([]UuUniNeighbourCellInfo, len(resp[i].NeighbourCellInfo))
				for j := range resp[i].NeighbourCellInfo {

					if resp[i].NeighbourCellInfo[j].Ecgi != nil {
						proInfoUuUnicast[i].NeighbourCellInfo[j].Ecgi = new(Ecgi)
						if resp[i].NeighbourCellInfo[j].Ecgi.CellId != nil {
							proInfoUuUnicast[i].NeighbourCellInfo[j].Ecgi.CellId = new(CellId)
							proInfoUuUnicast[i].NeighbourCellInfo[j].Ecgi.CellId.CellId = resp[i].NeighbourCellInfo[j].Ecgi.CellId.CellId
						}
						if resp[i].NeighbourCellInfo[j].Ecgi.Plmn != nil {
							proInfoUuUnicast[i].NeighbourCellInfo[j].Ecgi.Plmn = new(Plmn)
							proInfoUuUnicast[i].NeighbourCellInfo[j].Ecgi.Plmn.Mcc = resp[i].NeighbourCellInfo[j].Ecgi.Plmn.Mcc
							proInfoUuUnicast[i].NeighbourCellInfo[j].Ecgi.Plmn.Mnc = resp[i].NeighbourCellInfo[j].Ecgi.Plmn.Mnc
						}
					}
					proInfoUuUnicast[i].NeighbourCellInfo[j].Pci = resp[i].NeighbourCellInfo[j].Pci
					if resp[i].NeighbourCellInfo[j].Plmn != nil {
						proInfoUuUnicast[i].NeighbourCellInfo[j].Plmn = new(Plmn)
						proInfoUuUnicast[i].NeighbourCellInfo[j].Plmn.Mcc = resp[i].NeighbourCellInfo[j].Plmn.Mcc
						proInfoUuUnicast[i].NeighbourCellInfo[j].Plmn.Mnc = resp[i].NeighbourCellInfo[j].Plmn.Mnc
					}
				} // End of 'for' statement
			}
			if resp[i].V2xApplicationServer != nil {
				proInfoUuUnicast[i].V2xApplicationServer = new(V2xApplicationServer)
				proInfoUuUnicast[i].V2xApplicationServer.IpAddress = resp[i].V2xApplicationServer.IpAddress
				proInfoUuUnicast[i].V2xApplicationServer.UdpPort = resp[i].V2xApplicationServer.UdpPort
			}
		} // End of 'for' statement
	}
	log.Info("GetInfoUuUnicast: proInfoUuUnicast= ", proInfoUuUnicast)
	return proInfoUuUnicast, err
}

func PublishMessageOnMessageBroker(msgContent string, msgEncodeFormat string, stdOrganization string, msgType *int32) (err error) {
	return sbi.trafficMgr.PublishMessageOnMessageBroker(msgContent, msgEncodeFormat, stdOrganization, msgType)
}

func StartV2xMessageBrokerServer() (err error) {
	return sbi.trafficMgr.StartV2xMessageBrokerServer()
}

func StopV2xMessageBrokerServer() {
	sbi.trafficMgr.StopV2xMessageBrokerServer()
}
Loading