Newer
Older
* Copyright (c) 2019 InterDigital Communications, Inc
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
package server
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
Kevin Di Lallo
committed
"os"
"time"
sbi "github.com/InterDigitalInc/AdvantEDGE/go-apps/meep-loc-serv/sbi"
Kevin Di Lallo
committed
asc "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-app-support-client"
Kevin Di Lallo
committed
dkm "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-data-key-mgr"
gisClient "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-gis-engine-client"
httpLog "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-http-logger"
log "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-logger"
Kevin Di Lallo
committed
met "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-metrics"
redis "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-redis"
Kevin Di Lallo
committed
scc "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-sandbox-ctrl-client"
smc "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-service-mgmt-client"
Simon Pastor
committed
"github.com/gorilla/mux"
)
const LocServBasePath = "location/v2/"
const locServKey = "loc-serv"
Kevin Di Lallo
committed
const serviceName = "Location Service"
const defaultMepName = "global"
const defaultScopeOfLocality = "MEC_SYSTEM"
const defaultConsumedLocalOnly = true
const typeZone = "zone"
const typeAccessPoint = "accessPoint"
const typeUser = "user"
const typeZonalSubscription = "zonalsubs"
const typeUserSubscription = "usersubs"
const typeZoneStatusSubscription = "zonestatus"
const typeDistanceSubscription = "distance"
const typeAreaCircleSubscription = "areacircle"
Kevin Di Lallo
committed
const (
notifZonalPresence = "ZonalPresenceNotification"
notifZoneStatus = "ZoneStatusNotification"
Kevin Di Lallo
committed
)
Kevin Di Lallo
committed
type UeUserData struct {
queryZoneId []string
queryApId []string
queryAddress []string
Kevin Di Lallo
committed
}
type ApUserData struct {
queryInterestRealm string
apList *AccessPointList
}
var nextZonalSubscriptionIdAvailable int
var nextUserSubscriptionIdAvailable int
Simon Pastor
committed
var nextZoneStatusSubscriptionIdAvailable int
var nextDistanceSubscriptionIdAvailable int
var nextAreaCircleSubscriptionIdAvailable int
var zonalSubscriptionEnteringMap = map[int]string{}
var zonalSubscriptionLeavingMap = map[int]string{}
var zonalSubscriptionTransferringMap = map[int]string{}
var zonalSubscriptionMap = map[int]string{}
var userSubscriptionEnteringMap = map[int]string{}
var userSubscriptionLeavingMap = map[int]string{}
var userSubscriptionTransferringMap = map[int]string{}
var userSubscriptionMap = map[int]string{}
Simon Pastor
committed
var zoneStatusSubscriptionMap = map[int]*ZoneStatusCheck{}
var periodicSubscriptionMap = map[int]*PeriodicCheck{}
Simon Pastor
committed
type ZoneStatusCheck struct {
ZoneId string
Serviceable bool
Unserviceable bool
Unknown bool
Simon Pastor
committed
}
NextTts int32 //next time to send, derived from frequency
NbNotificationsSent int32
NotificationCheckReady bool
Subscription *DistanceNotificationSubscription
NextTts int32 //next time to send, derived from frequency
AddrInArea map[string]bool
NbNotificationsSent int32
NotificationCheckReady bool
Subscription *CircleNotificationSubscription
type PeriodicCheck struct {
NextTts int32 //next time to send, derived from frequency
Subscription *PeriodicNotificationSubscription
}
Kevin Di Lallo
committed
var LOC_SERV_DB = 0
var redisAddr string = "meep-redis-master.default.svc.cluster.local:6379"
var influxAddr string = "http://meep-influxdb.default.svc.cluster.local:8086"
Kevin Di Lallo
committed
var sbxCtrlUrl string = "http://meep-sandbox-ctrl"
Kevin Di Lallo
committed
var hostUrl *url.URL
Kevin Di Lallo
committed
var instanceId string
var instanceName string
Kevin Di Lallo
committed
var sandboxName string
var mepName string = defaultMepName
var scopeOfLocality string = defaultScopeOfLocality
var consumedLocalOnly bool = defaultConsumedLocalOnly
Kevin Di Lallo
committed
var basePath string
Kevin Di Lallo
committed
var baseKey string
Simon Pastor
committed
var gisAppClientUrl string = "http://meep-gis-engine"
const serviceAppVersion = "2.1.1"
var serviceAppInstanceId string
var appEnablementUrl string
var appEnablementEnabled bool
Kevin Di Lallo
committed
var appSupportClient *asc.APIClient
var svcMgmtClient *smc.APIClient
var sbxCtrlClient *scc.APIClient
var registrationTicker *time.Ticker
// Init - Location Service initialization
func Init() (err error) {
Kevin Di Lallo
committed
// Retrieve Instance ID from environment variable if present
instanceIdEnv := strings.TrimSpace(os.Getenv("MEEP_INSTANCE_ID"))
if instanceIdEnv != "" {
instanceId = instanceIdEnv
}
log.Info("MEEP_INSTANCE_ID: ", instanceId)
// Retrieve Instance Name from environment variable
Kevin Di Lallo
committed
instanceNameEnv := strings.TrimSpace(os.Getenv("MEEP_POD_NAME"))
if instanceNameEnv != "" {
instanceName = instanceNameEnv
}
log.Info("MEEP_POD_NAME: ", instanceName)
sandboxNameEnv := strings.TrimSpace(os.Getenv("MEEP_SANDBOX_NAME"))
if sandboxNameEnv != "" {
sandboxName = sandboxNameEnv
}
if sandboxName == "" {
err = errors.New("MEEP_SANDBOX_NAME env variable not set")
log.Error(err.Error())
return err
}
log.Info("MEEP_SANDBOX_NAME: ", sandboxName)
Kevin Di Lallo
committed
// hostUrl is the url of the node serving the resourceURL
// Retrieve public url address where service is reachable, if not present, use Host URL environment variable
hostUrl, err = url.Parse(strings.TrimSpace(os.Getenv("MEEP_PUBLIC_URL")))
if err != nil || hostUrl == nil || hostUrl.String() == "" {
hostUrl, err = url.Parse(strings.TrimSpace(os.Getenv("MEEP_HOST_URL")))
if err != nil {
hostUrl = new(url.URL)
}
Kevin Di Lallo
committed
}
log.Info("MEEP_HOST_URL: ", hostUrl)
Kevin Di Lallo
committed
// Get MEP name
mepNameEnv := strings.TrimSpace(os.Getenv("MEEP_MEP_NAME"))
if mepNameEnv != "" {
mepName = mepNameEnv
}
log.Info("MEEP_MEP_NAME: ", mepName)
appEnablementEnv := strings.TrimSpace(os.Getenv("MEEP_APP_ENABLEMENT"))
if appEnablementEnv != "" {
appEnablementUrl = "http://" + appEnablementEnv
appEnablementEnabled = true
}
log.Info("MEEP_APP_ENABLEMENT: ", appEnablementUrl)
// Get scope of locality
scopeOfLocalityEnv := strings.TrimSpace(os.Getenv("MEEP_SCOPE_OF_LOCALITY"))
if scopeOfLocalityEnv != "" {
scopeOfLocality = scopeOfLocalityEnv
}
log.Info("MEEP_SCOPE_OF_LOCALITY: ", scopeOfLocality)
// Get local consumption
consumedLocalOnlyEnv := strings.TrimSpace(os.Getenv("MEEP_CONSUMED_LOCAL_ONLY"))
if consumedLocalOnlyEnv != "" {
value, err := strconv.ParseBool(consumedLocalOnlyEnv)
if err == nil {
consumedLocalOnly = value
}
}
log.Info("MEEP_CONSUMED_LOCAL_ONLY: ", consumedLocalOnly)
Kevin Di Lallo
committed
// Get locality
localityEnv := strings.TrimSpace(os.Getenv("MEEP_LOCALITY"))
if localityEnv != "" {
locality = strings.Split(localityEnv, ":")
}
log.Info("MEEP_LOCALITY: ", locality)
// Set base path
if mepName == defaultMepName {
basePath = "/" + sandboxName + "/" + LocServBasePath
} else {
basePath = "/" + sandboxName + "/" + mepName + "/" + LocServBasePath
Kevin Di Lallo
committed
// Set base storage key
baseKey = dkm.GetKeyRoot(sandboxName) + locServKey + ":mep:" + mepName + ":"
Kevin Di Lallo
committed
// Connect to Redis DB
_ = rc.DBFlush(baseKey)
gisAppClientCfg.BasePath = gisAppClientUrl + "/gis/v1"
gisAppClient = gisClient.NewAPIClient(gisAppClientCfg)
if gisAppClient == nil {
log.Error("Failed to create GIS App REST API client: ", gisAppClientCfg.BasePath)
err := errors.New("Failed to create GIS App REST API client")
return err
}
userTrackingReInit()
zonalTrafficReInit()
Simon Pastor
committed
zoneStatusReInit()
Kevin Di Lallo
committed
// Initialize SBI
sbiCfg := sbi.SbiCfg{
SandboxName: sandboxName,
RedisAddr: redisAddr,
Kevin Di Lallo
committed
UserInfoCb: updateUserInfo,
ZoneInfoCb: updateZoneInfo,
ApInfoCb: updateAccessPointInfo,
ScenarioNameCb: updateStoreName,
CleanUpCb: cleanUp,
}
err = sbi.Init(sbiCfg)
if err != nil {
log.Error("Failed initialize SBI. Error: ", err)
return err
}
log.Info("SBI Initialized")
// Create App Enablement REST clients
if appEnablementEnabled {
// Create App Info client
Kevin Di Lallo
committed
sbxCtrlClientCfg := scc.NewConfiguration()
sbxCtrlClientCfg.BasePath = sbxCtrlUrl + "/sandbox-ctrl/v1"
sbxCtrlClient = scc.NewAPIClient(sbxCtrlClientCfg)
if sbxCtrlClient == nil {
return errors.New("Failed to create App Info REST API client")
}
// Create App Support client
Kevin Di Lallo
committed
appSupportClientCfg := asc.NewConfiguration()
appSupportClientCfg.BasePath = appEnablementUrl + "/mec_app_support/v1"
Kevin Di Lallo
committed
appSupportClient = asc.NewAPIClient(appSupportClientCfg)
if appSupportClient == nil {
return errors.New("Failed to create App Enablement App Support REST API client")
}
// Create Service Management client
Kevin Di Lallo
committed
srvMgmtClientCfg := smc.NewConfiguration()
srvMgmtClientCfg.BasePath = appEnablementUrl + "/mec_service_mgmt/v1"
Kevin Di Lallo
committed
svcMgmtClient = smc.NewAPIClient(srvMgmtClientCfg)
if svcMgmtClient == nil {
return errors.New("Failed to create App Enablement Service Management REST API client")
}
log.Info("Location Service successfully initialized")
Kevin Di Lallo
committed
return nil
Kevin Di Lallo
committed
}
Kevin Di Lallo
committed
func Run() (err error) {
// Start MEC Service registration ticker
if appEnablementEnabled {
startRegistrationTicker()
}
Kevin Di Lallo
committed
return sbi.Run()
// Stop MEC Service registration ticker
if appEnablementEnabled {
stopRegistrationTicker()
err = sendTerminationConfirmation(serviceAppInstanceId)
periodicTicker.Stop()
err = sbi.Stop()
func startRegistrationTicker() {
// Make sure ticker is not running
if registrationTicker != nil {
log.Warn("Registration ticker already running")
return
}
// Wait a few seconds to allow App Enablement Service to start.
// This is done to avoid the default 20 second TCP socket connect timeout
// if the App Enablement Service is not yet running.
log.Info("Waiting for App Enablement Service to start")
time.Sleep(5 * time.Second)
// Start registration ticker
registrationTicker = time.NewTicker(5 * time.Second)
go func() {
mecAppReadySent := false
// registrationSent := false
for range registrationTicker.C {
// Get Application instance ID if not already available
if serviceAppInstanceId == "" {
var err error
Kevin Di Lallo
committed
serviceAppInstanceId, err = getAppInstanceId()
if err != nil || serviceAppInstanceId == "" {
continue
}
}
// Send App Ready message
if !mecAppReadySent {
err := sendReadyConfirmation(serviceAppInstanceId)
if err != nil {
log.Error("Failure when sending the MecAppReady message. Error: ", err)
continue
}
mecAppReadySent = true
}
// Register service instance
// if !registrationSent {
Kevin Di Lallo
committed
err := registerService(serviceAppInstanceId)
if err != nil {
log.Error("Failed to register to appEnablement DB, keep trying. Error: ", err)
continue
}
// registrationSent = true
// }
// // Register for graceful termination
// if !subscriptionSent {
// err = subscribeAppTermination(serviceAppInstanceId)
// if err != nil {
// log.Error("Failed to subscribe to graceful termination. Error: ", err)
// continue
// }
// sendAppTerminationWhenDone = true
// subscriptionSent = true
// }
// Registration complete
log.Info("Successfully registered with App Enablement Service")
stopRegistrationTicker()
return
}
}()
}
func stopRegistrationTicker() {
if registrationTicker != nil {
log.Info("Stopping App Enablement registration ticker")
registrationTicker.Stop()
registrationTicker = nil
Kevin Di Lallo
committed
func getAppInstanceId() (id string, err error) {
var appInfo scc.ApplicationInfo
appInfo.Id = instanceId
appInfo.Name = instanceName
Kevin Di Lallo
committed
appInfo.Version = serviceAppVersion
appType := scc.SYSTEM_ApplicationType
appInfo.Type_ = &appType
state := scc.INITIALIZED_ApplicationState
response, _, err := sbxCtrlClient.ApplicationsApi.ApplicationsPOST(context.TODO(), appInfo)
log.Error("Failed to get App Instance ID with error: ", err)
return "", err
Kevin Di Lallo
committed
return response.Id, nil
Kevin Di Lallo
committed
func registerService(appInstanceId string) error {
var srvInfo smc.ServiceInfoPost
Kevin Di Lallo
committed
srvInfo.SerName = instanceName
Kevin Di Lallo
committed
srvInfo.Version = serviceAppVersion
Kevin Di Lallo
committed
state := smc.ACTIVE_ServiceState
Kevin Di Lallo
committed
serializer := smc.JSON_SerializerType
Kevin Di Lallo
committed
var transportInfo smc.TransportInfo
Kevin Di Lallo
committed
transportType := smc.REST_HTTP_TransportType
transportInfo.Type_ = &transportType
transportInfo.Protocol = "HTTP"
transportInfo.Version = "2.0"
Kevin Di Lallo
committed
var endpoint smc.OneOfTransportInfoEndpoint
endpointPath := hostUrl.String() + basePath
endpoint.Uris = append(endpoint.Uris, endpointPath)
transportInfo.Endpoint = &endpoint
srvInfo.TransportInfo = &transportInfo
//serCategory
Kevin Di Lallo
committed
var category smc.CategoryRef
category.Href = "catalogueHref"
category.Id = "locationId"
category.Name = "Location"
category.Version = "v2"
srvInfo.SerCategory = &category
//scopeOfLocality
Kevin Di Lallo
committed
localityType := smc.LocalityType(scopeOfLocality)
srvInfo.ScopeOfLocality = &localityType
srvInfo.ConsumedLocalOnly = consumedLocalOnly
Kevin Di Lallo
committed
appServicesPostResponse, _, err := svcMgmtClient.AppServicesApi.AppServicesPOST(context.TODO(), srvInfo, appInstanceId)
if err != nil {
log.Error("Failed to register the service to app enablement registry: ", err)
return err
}
log.Info("Application Enablement Service instance Id: ", appServicesPostResponse.SerInstanceId)
return nil
}
func sendReadyConfirmation(appInstanceId string) error {
Kevin Di Lallo
committed
var appReady asc.AppReadyConfirmation
indication := asc.READY_ReadyIndicationType
appReady.Indication = &indication
Kevin Di Lallo
committed
_, err := appSupportClient.AppConfirmReadyApi.ApplicationsConfirmReadyPOST(context.TODO(), appReady, appInstanceId)
if err != nil {
log.Error("Failed to send a ready confirm acknowlegement: ", err)
func sendTerminationConfirmation(appInstanceId string) error {
Kevin Di Lallo
committed
var appTermination asc.AppTerminationConfirmation
operationAction := asc.TERMINATING_OperationActionType
appTermination.OperationAction = &operationAction
Kevin Di Lallo
committed
_, err := appSupportClient.AppConfirmTerminationApi.ApplicationsConfirmTerminationPOST(context.TODO(), appTermination, appInstanceId)
log.Error("Failed to send a confirm termination acknowlegement: ", err)
// func subscribeAppTermination(appInstanceId string) error {
Kevin Di Lallo
committed
// var subscription asc.AppTerminationNotificationSubscription
// subscription.SubscriptionType = "AppTerminationNotificationSubscription"
// subscription.AppInstanceId = appInstanceId
// subscription.CallbackReference = hostUrl.String() + basePath
Kevin Di Lallo
committed
// _, _, err := appSupportClient.AppSubscriptionsApi.ApplicationsSubscriptionsPOST(context.TODO(), subscription, appInstanceId)
// if err != nil {
// log.Error("Failed to register to App Support subscription: ", err)
// return err
// }
// return nil
// }
Simon Pastor
committed
func deregisterZoneStatus(subsIdStr string) {
if err != nil {
log.Error(err)
}
Simon Pastor
committed
}
func registerZoneStatus(zoneId string, nbOfUsersZoneThreshold int32, nbOfUsersAPThreshold int32, opStatus []OperationStatus, subsIdStr string) {
Simon Pastor
committed
if err != nil {
log.Error(err)
}
Simon Pastor
committed
var zoneStatus ZoneStatusCheck
if opStatus != nil {
for i := 0; i < len(opStatus); i++ {
switch opStatus[i] {
case SERVICEABLE:
zoneStatus.Serviceable = true
case UNSERVICEABLE:
zoneStatus.Unserviceable = true
case OPSTATUS_UNKNOWN:
zoneStatus.Unknown = true
default:
}
}
}
zoneStatus.NbUsersInZoneThreshold = nbOfUsersZoneThreshold
zoneStatus.NbUsersInAPThreshold = nbOfUsersAPThreshold
Simon Pastor
committed
zoneStatus.ZoneId = zoneId
Simon Pastor
committed
zoneStatusSubscriptionMap[subsId] = &zoneStatus
}
func deregisterZonal(subsIdStr string) {
subsId, err := strconv.Atoi(subsIdStr)
if err != nil {
log.Error(err)
}
zonalSubscriptionMap[subsId] = ""
zonalSubscriptionEnteringMap[subsId] = ""
zonalSubscriptionLeavingMap[subsId] = ""
zonalSubscriptionTransferringMap[subsId] = ""
func registerZonal(zoneId string, event []UserEventType, subsIdStr string) {
if err != nil {
log.Error(err)
}
if event != nil {
for i := 0; i < len(event); i++ {
switch event[i] {
zonalSubscriptionEnteringMap[subsId] = zoneId
zonalSubscriptionLeavingMap[subsId] = zoneId
zonalSubscriptionTransferringMap[subsId] = zoneId
default:
}
}
} else {
zonalSubscriptionEnteringMap[subsId] = zoneId
zonalSubscriptionLeavingMap[subsId] = zoneId
zonalSubscriptionTransferringMap[subsId] = zoneId
}
zonalSubscriptionMap[subsId] = zoneId
}
func deregisterUser(subsIdStr string) {
if err != nil {
log.Error(err)
}
userSubscriptionMap[subsId] = ""
userSubscriptionEnteringMap[subsId] = ""
userSubscriptionLeavingMap[subsId] = ""
userSubscriptionTransferringMap[subsId] = ""
func registerUser(userAddress string, event []UserEventType, subsIdStr string) {
if err != nil {
log.Error(err)
}
if event != nil {
for i := 0; i < len(event); i++ {
switch event[i] {
userSubscriptionEnteringMap[subsId] = userAddress
userSubscriptionLeavingMap[subsId] = userAddress
userSubscriptionTransferringMap[subsId] = userAddress
default:
}
}
} else {
userSubscriptionEnteringMap[subsId] = userAddress
userSubscriptionLeavingMap[subsId] = userAddress
userSubscriptionTransferringMap[subsId] = userAddress
}
userSubscriptionMap[subsId] = userAddress
}
func updateNotificationAreaCirclePeriodicTrigger() {
//only check if there is at least one subscription
mutex.Lock()
defer mutex.Unlock()
for _, areaCircleCheck := range areaCircleSubscriptionMap {
if areaCircleCheck != nil {
if areaCircleCheck.NextTts != 0 {
areaCircleCheck.NextTts--
}
if areaCircleCheck.NextTts == 0 {
areaCircleCheck.NotificationCheckReady = true
} else {
areaCircleCheck.NotificationCheckReady = false
}
}
}
}
func checkNotificationDistancePeriodicTrigger() {
//only check if there is at least one subscription
mutex.Lock()
defer mutex.Unlock()
//check all that applies
for subsId, distanceCheck := range distanceSubscriptionMap {
if distanceCheck != nil && distanceCheck.Subscription != nil {
if distanceCheck.Subscription.Count == 0 || (distanceCheck.Subscription.Count != 0 && distanceCheck.NbNotificationsSent < distanceCheck.Subscription.Count) {
if distanceCheck.NextTts != 0 {
distanceCheck.NextTts--
}
if distanceCheck.NextTts == 0 {
distanceCheck.NotificationCheckReady = true
} else {
distanceCheck.NotificationCheckReady = false
}
if !distanceCheck.NotificationCheckReady {
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
//loop through every reference address
returnAddr := make(map[string]*gisClient.Distance)
skipThisSubscription := false
//if reference address is specified, reference addresses are checked agains each monitored address
//if reference address is nil, each pair of the monitored address should be checked
//creating address pairs to check
//e.g. refAddr = A, B ; monitoredAddr = C, D, E ; resultingPairs {A,C - A,D - A,E - B,C - B,D - B-E}
//e.g. monitoredAddr = A, B, C ; resultingPairs {A,B - B,A - A,C - C,A - B,C - C,B}
var addressPairs []Pair
if distanceCheck.Subscription.ReferenceAddress != nil {
for _, refAddr := range distanceCheck.Subscription.ReferenceAddress {
//loop through every monitored address
for _, monitoredAddr := range distanceCheck.Subscription.MonitoredAddress {
pair := Pair{addr1: refAddr, addr2: monitoredAddr}
addressPairs = append(addressPairs, pair)
}
}
} else {
nbIndex := len(distanceCheck.Subscription.MonitoredAddress)
for i := 0; i < nbIndex-1; i++ {
for j := i + 1; j < nbIndex; j++ {
pair := Pair{addr1: distanceCheck.Subscription.MonitoredAddress[i], addr2: distanceCheck.Subscription.MonitoredAddress[j]}
addressPairs = append(addressPairs, pair)
//need pair to be symmetrical so that each is used as reference point and monitored address
pair = Pair{addr1: distanceCheck.Subscription.MonitoredAddress[j], addr2: distanceCheck.Subscription.MonitoredAddress[i]}
addressPairs = append(addressPairs, pair)
}
for _, pair := range addressPairs {
refAddr := pair.addr1
monitoredAddr := pair.addr2
//check if one of the address if both addresses are connected, if not, disregard this pair
if !addressConnectedMap[refAddr] || !addressConnectedMap[monitoredAddr] {
//ignore that pair and continue processing
continue
}
var distParam gisClient.TargetPoint
distParam.AssetName = monitoredAddr
distResp, httpResp, err := gisAppClient.GeospatialDataApi.GetDistanceGeoDataByName(context.TODO(), refAddr, distParam)
if err != nil {
//getting distance of an element that is not in the DB (not in scenario, not connected) returns error code 400 (bad parameters) in the API. Using that error code to track that request made it to GIS but no good result, so ignore that address (monitored or ref)
//ignore that pair and continue processing
continue
} else {
log.Error("Failed to communicate with gis engine: ", err)
return
}
}
switch *distanceCheck.Subscription.Criteria {
case ALL_WITHIN_DISTANCE:
if float32(distance) < distanceCheck.Subscription.Distance {
returnAddr[monitoredAddr] = &distResp
} else {
skipThisSubscription = true
}
case ALL_BEYOND_DISTANCE:
if float32(distance) > distanceCheck.Subscription.Distance {
returnAddr[monitoredAddr] = &distResp
} else {
skipThisSubscription = true
}
case ANY_WITHIN_DISTANCE:
if float32(distance) < distanceCheck.Subscription.Distance {
returnAddr[monitoredAddr] = &distResp
}
case ANY_BEYOND_DISTANCE:
if float32(distance) > distanceCheck.Subscription.Distance {
returnAddr[monitoredAddr] = &distResp
}
default:
if len(returnAddr) > 0 {
//update nb of notification sent anch check if valid
subsIdStr := strconv.Itoa(subsId)
var distanceNotif SubscriptionNotification
distanceNotif.DistanceCriteria = distanceCheck.Subscription.Criteria
distanceNotif.IsFinalNotification = false
distanceNotif.Link = distanceCheck.Subscription.Link
var terminalLocationList []TerminalLocation
for terminalAddr, distanceInfo := range returnAddr {
var terminalLocation TerminalLocation
terminalLocation.Address = terminalAddr
var locationInfo LocationInfo
locationInfo.Latitude = nil
locationInfo.Latitude = append(locationInfo.Latitude, distanceInfo.DstLatitude)
locationInfo.Longitude = append(locationInfo.Longitude, distanceInfo.DstLongitude)
locationInfo.Shape = 2
seconds := time.Now().Unix()
var timestamp TimeStamp
timestamp.Seconds = int32(seconds)
locationInfo.Timestamp = ×tamp
terminalLocation.CurrentLocation = &locationInfo
retrievalStatus := RETRIEVED
terminalLocation.LocationRetrievalStatus = &retrievalStatus
terminalLocationList = append(terminalLocationList, terminalLocation)
}
distanceNotif.TerminalLocation = terminalLocationList
distanceNotif.CallbackData = distanceCheck.Subscription.CallbackReference.CallbackData
var inlineDistanceSubscriptionNotification InlineSubscriptionNotification
inlineDistanceSubscriptionNotification.SubscriptionNotification = &distanceNotif
distanceCheck.NbNotificationsSent++
sendSubscriptionNotification(distanceCheck.Subscription.CallbackReference.NotifyURL, inlineDistanceSubscriptionNotification)
log.Info("Distance Notification"+"("+subsIdStr+") For ", returnAddr)
distanceSubscriptionMap[subsId].NextTts = distanceCheck.Subscription.Frequency
distanceSubscriptionMap[subsId].NotificationCheckReady = false
}
}
}
}
}
func checkNotificationAreaCircle(addressToCheck string) {
//only check if there is at least one subscription
mutex.Lock()
defer mutex.Unlock()
//check all that applies
for subsId, areaCircleCheck := range areaCircleSubscriptionMap {
if areaCircleCheck != nil && areaCircleCheck.Subscription != nil {
if areaCircleCheck.Subscription.Count == 0 || (areaCircleCheck.Subscription.Count != 0 && areaCircleCheck.NbNotificationsSent < areaCircleCheck.Subscription.Count) {
//loop through every reference address
for _, addr := range areaCircleCheck.Subscription.Address {
if addr != addressToCheck {
//check if address is already inside the area or not based on the subscription
var withinRangeParam gisClient.TargetRange
withinRangeParam.Latitude = areaCircleCheck.Subscription.Latitude
withinRangeParam.Longitude = areaCircleCheck.Subscription.Longitude
withinRangeParam.Radius = areaCircleCheck.Subscription.Radius
withinRangeResp, httpResp, err := gisAppClient.GeospatialDataApi.GetWithinRangeByName(context.TODO(), addr, withinRangeParam)
if err != nil {
//getting element that is not in the DB (not in scenario, not connected) returns error code 400 (bad parameters) in the API. Using that error code to track that request made it to GIS but no good result, so ignore that address (monitored or ref)
//if the UE was within the zone, continue processing to send a LEAVING notification, otherwise, go to next subscription
if !areaCircleCheck.AddrInArea[addr] {
continue
}
} else {
log.Error("Failed to communicate with gis engine: ", err)
return
}
}
//check if there is a change
var event EnteringLeavingCriteria
if withinRangeResp.Within {
if areaCircleCheck.AddrInArea[addr] {
//no change
continue
} else {
areaCircleCheck.AddrInArea[addr] = true
event = ENTERING_CRITERIA
}
if !areaCircleCheck.AddrInArea[addr] {
//no change
continue
} else {
areaCircleCheck.AddrInArea[addr] = false
event = LEAVING_CRITERIA
}
//no tracking this event, stop looking for this UE
if *areaCircleCheck.Subscription.EnteringLeavingCriteria != event {
subsIdStr := strconv.Itoa(subsId)
var areaCircleNotif SubscriptionNotification
areaCircleNotif.EnteringLeavingCriteria = areaCircleCheck.Subscription.EnteringLeavingCriteria
areaCircleNotif.IsFinalNotification = false
areaCircleNotif.Link = areaCircleCheck.Subscription.Link
var terminalLocationList []TerminalLocation
var terminalLocation TerminalLocation
terminalLocation.Address = addr
var locationInfo LocationInfo
locationInfo.Latitude = nil
locationInfo.Latitude = append(locationInfo.Latitude, withinRangeResp.SrcLatitude)
locationInfo.Longitude = append(locationInfo.Longitude, withinRangeResp.SrcLongitude)
locationInfo.Shape = 2
seconds := time.Now().Unix()
var timestamp TimeStamp
timestamp.Seconds = int32(seconds)
locationInfo.Timestamp = ×tamp
terminalLocation.CurrentLocation = &locationInfo
retrievalStatus := RETRIEVED
terminalLocation.LocationRetrievalStatus = &retrievalStatus
terminalLocationList = append(terminalLocationList, terminalLocation)
areaCircleNotif.TerminalLocation = terminalLocationList
areaCircleNotif.CallbackData = areaCircleCheck.Subscription.CallbackReference.CallbackData
var inlineCircleSubscriptionNotification InlineSubscriptionNotification
inlineCircleSubscriptionNotification.SubscriptionNotification = &areaCircleNotif
areaCircleCheck.NbNotificationsSent++
sendSubscriptionNotification(areaCircleCheck.Subscription.CallbackReference.NotifyURL, inlineCircleSubscriptionNotification)
log.Info("Area Circle Notification" + "(" + subsIdStr + ") For " + addr + " when " + string(*areaCircleCheck.Subscription.EnteringLeavingCriteria) + " area")
areaCircleSubscriptionMap[subsId].NextTts = areaCircleCheck.Subscription.Frequency
areaCircleSubscriptionMap[subsId].NotificationCheckReady = false
func checkNotificationPeriodicTrigger() {
//only check if there is at least one subscription
mutex.Lock()
defer mutex.Unlock()
//check all that applies
for subsId, periodicCheck := range periodicSubscriptionMap {
if periodicCheck != nil && periodicCheck.Subscription != nil {
//decrement the next time to send a message
periodicCheck.NextTts--
if periodicCheck.NextTts > 0 {
continue
} else { //restart the nextTts and continue processing to send notification or not
periodicCheck.NextTts = periodicCheck.Subscription.Frequency
}
//loop through every reference address
var terminalLocationList []TerminalLocation
var periodicNotif SubscriptionNotification
for _, addr := range periodicCheck.Subscription.Address {
geoDataInfo, _, err := gisAppClient.GeospatialDataApi.GetGeoDataByName(context.TODO(), addr, nil)
if err != nil {
log.Error("Failed to communicate with gis engine: ", err)
return
}
var terminalLocation TerminalLocation
terminalLocation.Address = addr
var locationInfo LocationInfo