/* * Copyright (c) 2024 The AdvantEDGE Authors * * 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 iotmgr import ( "errors" "time" log "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-logger" ) // IOT Manager type IotMgr struct { name string namespace string } type IotPlatformInfo struct { IotPlatformId string UserTransportInfo []MbTransportInfo CustomServicesTransportInfo []TransportInfo Enabled bool } type MbTransportInfo struct { Id string Name string Description string Type_ *string Protocol string Version string Endpoint *EndPointInfo Security *SecurityInfo ImplSpecificInfo *ImplSpecificInfo } type TransportInfo struct { Id string Name string Description string Type_ *string Protocol string Version string Endpoint *EndPointInfo Security *SecurityInfo ImplSpecificInfo string } type EndPointInfo struct { Uris []string Fqdn []string Addresses []Addresses Alternative string } type Addresses struct { Host string Port int32 } type SecurityInfo struct { OAuth2Info *OAuth2Info Extensions string } type OAuth2Info struct { GrantTypes []string TokenEndpoint string } type ImplSpecificInfo struct { EventTopics []string UplinkTopics []string DownlinkTopics []string } type TrafficRuleDescriptor struct { TrafficRuleId string FilterType string Priority int32 TrafficFilter []TrafficFilter Action string DstInterface *InterfaceDescriptor } type InterfaceDescriptor struct { InterfaceType string //TunnelInfo *TunnelInfo FSCOM Not supported SrcMACAddress string DstMACAddress string DstIPAddress string } type TrafficFilter struct { SrcAddress []string DstAddress []string SrcPort []string DstPort []string Protocol []string Tag []string Uri []string PacketLabel []string SrcTunnelAddress []string TgtTunnelAddress []string SrcTunnelPort []string DstTunnelPort []string QCI int32 TC int32 } type KeyValuePair struct { Key string Value string } type DeviceInfo struct { DeviceAuthenticationInfo string DeviceMetadata []KeyValuePair Gpsi string Pei string Supi string Msisdn string Imei string Imsi string Iccid string DeviceId string RequestedMecTrafficRule []TrafficRuleDescriptor RequestedIotPlatformId string RequestedUserTransportId string //DeviceSpecificMessageFormats *DeviceSpecificMessageFormats //DownlinkInfo *DownlinkInfo ClientCertificate string Enabled bool } var registeredIotPlatformsMap = map[string]IotPlatformInfo{} // List of discovered IOT Plateform var devicesMap = map[string]DeviceInfo{} // Map device by deviceId var devicesPerPlatformMap = map[string][]string{} // Map deviceIds per platform var platformPerUserTransportIdMap = map[string][]string{} // Map userTransportId per platform // Timer to refresh devices list for all IoT platform const refreshTickerExpeary = 10 // In seconds // Enable profiling const profiling = false var profilingTimers map[string]time.Time const ( headerAccept = "application/json" headerContentType = "application/json" ) // NewIotMgr - Creates and initializes a new IOT Traffic Manager func NewIotMgr(name string, namespace string) (tm *IotMgr, err error) { if name == "" { err = errors.New("Missing connector name") return nil, err } // Create new Traffic Manager tm = new(IotMgr) tm.name = name if namespace != "" { tm.namespace = namespace } else { tm.namespace = "default" } tm.init() return tm, nil } // Profiling init func (tm *IotMgr) init() { if profiling { profilingTimers = make(map[string]time.Time) } registeredIotPlatformsMap = make(map[string]IotPlatformInfo, 0) devicesMap = make(map[string]DeviceInfo, 0) devicesPerPlatformMap = make(map[string][]string, 0) platformPerUserTransportIdMap = make(map[string][]string, 0) } // DeleteIotMgr - func (tm *IotMgr) DeleteIotMgr() (err error) { return nil } func (tm *IotMgr) RegisterIotPlatformInfo(iotPlatformInfo IotPlatformInfo) (err error) { if profiling { profilingTimers["RegisterIotPlatformInfo"] = time.Now() } log.Info(">>> RegisterIotPlatformInfo: iotPlatformId: ", iotPlatformInfo) if iotPlatformInfo.Enabled { registeredIotPlatformsMap[iotPlatformInfo.IotPlatformId] = iotPlatformInfo } // else, Skip disabled platform if profiling { now := time.Now() log.Debug("RegisterIotPlatformInfo: ", now.Sub(profilingTimers["RegisterIotPlatformInfo"])) } return nil } func (tm *IotMgr) DeregisterIotPlatformInfo(iotPlatformId string) (err error) { if profiling { profilingTimers["DeregisterIotPlatformInfo"] = time.Now() } log.Info(">>> DeregisterIotPlatformInfo: iotPlatformId: ", iotPlatformId) // Remove the list of the devices for this IoT platform if val, ok := devicesPerPlatformMap[iotPlatformId]; ok { // Free resources from devicesMap map for _, dev := range val { delete(devicesMap, dev) } // End of 'for' statement delete(devicesPerPlatformMap, iotPlatformId) log.Info("DeregisterIotPlatformInfo: platformPerUserTransportIdMap (before): ", iotPlatformId) for _, rule := range platformPerUserTransportIdMap { for idx, pltf := range rule { if pltf == iotPlatformId { rule = append(rule[:idx], rule[idx+1:]...) } } // End of 'for' statement } // End of 'for' statement log.Info("DeregisterIotPlatformInfo: platformPerUserTransportIdMap (after): ", iotPlatformId) } if _, ok := registeredIotPlatformsMap[iotPlatformId]; ok { delete(registeredIotPlatformsMap, iotPlatformId) } if profiling { now := time.Now() log.Debug("DeregisterIotPlatformInfo: ", now.Sub(profilingTimers["DeregisterIotPlatformInfo"])) } return nil } func (tm *IotMgr) GetDevices() (devices []DeviceInfo, err error) { if profiling { profilingTimers["GetDevices"] = time.Now() } log.Info(">>> GetDevices") devices = make([]DeviceInfo, 0) if len(registeredIotPlatformsMap) == 0 { return devices, nil } for _, v := range devicesMap { log.Info("GetDevices: adding device: ", v) devices = append(devices, v) } // End of 'for' statement log.Info("GetDevices: devices: ", devices) if profiling { now := time.Now() log.Debug("GetDevices: ", now.Sub(profilingTimers["GetDevices"])) } return devices, nil } func (tm *IotMgr) GetDevice(deviceId string) (device DeviceInfo, err error) { if profiling { profilingTimers["GetDevice"] = time.Now() } log.Info(">>> GetDevice: deviceId: ", deviceId) if val, ok := devicesMap[deviceId]; !ok { err = errors.New("Wrong Device identifier") return device, err } else { device = val } if profiling { now := time.Now() log.Debug("GetDevice: ", now.Sub(profilingTimers["GetDevice"])) } log.Info("GetDevice: device: ", device) return device, nil } func (tm *IotMgr) CreateDevice(device DeviceInfo) (deviceResp DeviceInfo, err error) { log.Info(">>> CreateDevice: ", device) // RequestedMecTrafficRule is not supported yet if len(device.RequestedMecTrafficRule) != 0 { err = errors.New("Unsupported traffic rule provided") log.Error(err.Error()) return deviceResp, err } if len(device.RequestedIotPlatformId) != 0 { deviceResp, err = tm.createDeviceWithIotPlatformId(device, device.RequestedIotPlatformId) } else { deviceResp, err = tm.createDeviceWithRequestedUserTransportId(device, device.RequestedUserTransportId) } if err != nil { log.Error(err.Error()) return deviceResp, err } log.Info("CreateDevice: deviceResp: ", deviceResp) return deviceResp, nil } func (tm *IotMgr) DeleteDevice(deviceId string) (err error) { if profiling { profilingTimers["DeleteDevice"] = time.Now() } log.Info(">>> DeleteDevice: device: ", deviceId) if _, ok := devicesMap[deviceId]; !ok { err = errors.New("Invalid device identifier") log.Error(err.Error()) return err } device := devicesMap[deviceId] // Remove the list of the devices for this IoT platform if val, ok := devicesPerPlatformMap[device.RequestedIotPlatformId]; ok { // Free resource from devicesMap map log.Info("DeleteDevice: devicesPerPlatformMap (before): ", devicesPerPlatformMap) for idx, devId := range val { if devId == device.DeviceId { val = append(val[:idx], val[idx+1:]...) break } } // End of 'for' statement } log.Info("DeleteDevice: devicesPerPlatformMap (after): ", devicesPerPlatformMap) // Free resource from devicesMap map log.Info("DeleteDevice: devicesMap (before): ", devicesMap) delete(devicesMap, device.DeviceId) log.Info("DeleteDevice: devicesMap (after): ", devicesMap) if profiling { now := time.Now() log.Debug("DeleteDevice: ", now.Sub(profilingTimers["DeleteDevice"])) } return nil } func (tm *IotMgr) createDeviceWithIotPlatformId(device DeviceInfo, requestedIotPlatformId string) (deviceResp DeviceInfo, err error) { log.Info(">>> createDeviceWithIotPlatformId: ", device) // Sanity checks if _, ok := registeredIotPlatformsMap[requestedIotPlatformId]; !ok { err = errors.New("Invalid IotPlatform identifier") return deviceResp, err } if _, ok := devicesMap[device.DeviceId]; ok { err = errors.New("Device already exist") return deviceResp, err } devicesMap[device.DeviceId] = device devicesPerPlatformMap[device.DeviceId] = append(devicesPerPlatformMap[device.DeviceId], requestedIotPlatformId) platformPerUserTransportIdMap[requestedIotPlatformId] = append(platformPerUserTransportIdMap[requestedIotPlatformId], device.RequestedUserTransportId) deviceResp = device log.Debug("createDeviceWithIotPlatformId: deviceResp: ", deviceResp) return deviceResp, nil } func (tm *IotMgr) createDeviceWithRequestedUserTransportId(device DeviceInfo, requestedUserTransportId string) (deviceResp DeviceInfo, err error) { log.Info(">>> createDeviceWithRequestedUserTransportId: ", device) if val, ok := platformPerUserTransportIdMap[requestedUserTransportId]; ok { deviceResp, err = tm.createDeviceWithIotPlatformId(device, val[0]) } else { err = errors.New("Invalid UserTransportId") } if err != nil { log.Error("createDeviceWithIotPlatformId: ", err.Error()) return deviceResp, err } log.Info("createDeviceWithIotPlatformId: deviceResp: ", deviceResp) return deviceResp, nil }