Skip to content
onem2m-mgr.go 36.5 KiB
Newer Older
Yann Garcia's avatar
Yann Garcia committed
/*
 * 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 sssmgr
Yann Garcia's avatar
Yann Garcia committed

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"reflect"
	"strconv"
	"strings"
	"sync"
	"time"

	log "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-logger"

	uuid "github.com/google/uuid"
)

// Sensors-Sharing Service Manager
type SssMgr struct {
	name                 string
	namespace            string
Yann Garcia's avatar
Yann Garcia committed
	bindingProtocol      string
	host                 string
	port                 int
	mutex                sync.Mutex
	wg                   sync.WaitGroup
	refreshTicker        *time.Ticker
	sss_discovery_notify func()
	sss_status_notify    func()
	sss_data_notify      func()
Yann Garcia's avatar
Yann Garcia committed
}

type IotPlatformInfo struct {
	Address       string
	Port          int
	Name          string
	IotPlatformId string
type Point struct {
	Latitude  float64
	Longitude float64
Yann Garcia's avatar
Yann Garcia committed
}

type SensorCharacteristic struct {
	CharacteristicName          string
	CharacteristicValue         string
	CharacteristicUnitOfMeasure *string
}

type SensorDiscoveryInfo struct {
	SensorIdentifier         string
	SensorType               string
	SensorPropertyList       []string
	SensorCharacteristicList []SensorCharacteristic
	SensorPosition           *Point
	IotPlatformId            string
Yann Garcia's avatar
Yann Garcia committed
}

var registeredIotPlatformsMap = map[string]IotPlatformInfo{} // List of discovered IOT Plateform
var sensorsMap = map[string]SensorDiscoveryInfo{}            // Map sensors by sensorIdentifier
var sensorsPerPlatformMap = map[string][]string{}            // Map dsensorIdentifiers per platform
Yann Garcia's avatar
Yann Garcia committed

// 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 (
	iot_platform_address = "lab-oai.etsi.org"
	iot_platform_port    = 31110
	iot_platform_name    = "laboai-acme-ic-cse"
	iot_platform_id      = "7feaadbb0400"
Yann Garcia's avatar
Yann Garcia committed
var protocol SssMgrBindingProtocol

// NewSssMgr - Creates and initializes a new SSS Traffic Manager
Yann Garcia's avatar
Yann Garcia committed
func NewSssMgr(name string, namespace string, bindingProtocol string, host string, port int, sss_discovery_notify func(), sss_status_notify func(), sss_data_notify func()) (tm *SssMgr, err error) {
Yann Garcia's avatar
Yann Garcia committed
	if name == "" {
		err = errors.New("Missing connector name")
		return nil, err
	}

	// Create new Traffic Manager
	tm = new(SssMgr)
Yann Garcia's avatar
Yann Garcia committed
	tm.name = name
	if namespace != "" {
		tm.namespace = namespace
	} else {
		tm.namespace = "default"
	}

Yann Garcia's avatar
Yann Garcia committed
	tm.bindingProtocol = bindingProtocol
	tm.host = host
	tm.port = port
	if tm.bindingProtocol == "MQTT" {
		if tm.host == "" {
			err := errors.New("Host not set for MQTTP protocol")
			log.Error(err.Error())
			return nil, err
		}
		if tm.port == 0 {
			tm.port = 1883
		}
		protocol = NewSssMgrMqtt()
	} else if tm.bindingProtocol == "HTTP" {
		protocol = NewSssMgrHttp()
	} else {
		err := errors.New("Binding protocol not set")
		log.Error(err.Error())
		return nil, err
	}
	err = protocol.init(tm)
	if err != nil {
		log.Error(err.Error())
		return nil, err
	}

	tm.sss_discovery_notify = sss_discovery_notify
	tm.sss_status_notify = sss_status_notify
	tm.sss_data_notify = sss_data_notify

Yann Garcia's avatar
Yann Garcia committed
	tm.init()

	return tm, nil
}

// Profiling init
func (tm *SssMgr) init() {
Yann Garcia's avatar
Yann Garcia committed
	if profiling {
		profilingTimers = make(map[string]time.Time)
	}

	registeredIotPlatformsMap = make(map[string]IotPlatformInfo, 1)
	registeredIotPlatformsMap[iot_platform_address] = IotPlatformInfo{ // FIXME FSCOM How to register IoT platform to meep-sss (see meep-iot?)
		Address:       iot_platform_address,
		Port:          iot_platform_port,
		Name:          iot_platform_name,
		IotPlatformId: iot_platform_id,
	}
	sensorsMap = make(map[string]SensorDiscoveryInfo, 0)
	sensorsPerPlatformMap = make(map[string][]string, 0)
Yann Garcia's avatar
Yann Garcia committed
	tm.refreshTicker = nil
// DeleteSssMgr -
func (tm *SssMgr) DeleteSssMgr() (err error) {
Yann Garcia's avatar
Yann Garcia committed
	tm.stopRefreshTicker()

Yann Garcia's avatar
Yann Garcia committed
	if protocol != nil {
		protocol.uninit()
		protocol = nil
	}
Yann Garcia's avatar
Yann Garcia committed
	return nil
}

func (tm *SssMgr) startRefreshTicker() {
Yann Garcia's avatar
Yann Garcia committed
	log.Debug("Starting refresh loop")
	tm.refreshTicker = time.NewTicker(refreshTickerExpeary * time.Second)
	go func() {
		if tm.refreshTicker != nil {
			for range tm.refreshTicker.C {
				// Refresh the list of devices
				tm.wg.Add(1)
				err := tm.populateDevicesPerIotPlatforms()
				if err != nil {
					log.Error(err)
				}
				tm.wg.Done()
			}
		}
	}()
}

func (tm *SssMgr) stopRefreshTicker() {
Yann Garcia's avatar
Yann Garcia committed
	if tm.refreshTicker != nil {
		// Refresh the list of devices
		tm.wg.Add(1)
		tm.refreshTicker.Stop()
		tm.refreshTicker = nil
		registeredIotPlatformsMap = nil
		sensorsMap = nil
		sensorsPerPlatformMap = nil
Yann Garcia's avatar
Yann Garcia committed
		tm.wg.Done()
		log.Debug("Refresh loop stopped")
	}
}

func (tm *SssMgr) SensorDiscoveryInfoAll() (sensors []SensorDiscoveryInfo, err error) {
Yann Garcia's avatar
Yann Garcia committed
	if profiling {
		profilingTimers["GetDevices"] = time.Now()
	log.Info(">>> SensorDiscoveryInfoAll")
Yann Garcia's avatar
Yann Garcia committed

	err = tm.populateDevicesPerIotPlatforms() // FIXME FSCOM User timer. See startRefreshTicker
	if err != nil {
		return sensors, err
Yann Garcia's avatar
Yann Garcia committed
	}

	tm.wg.Wait()
	log.Info("SensorDiscoveryInfoAll: After Synchro")
Yann Garcia's avatar
Yann Garcia committed

	sensors = make([]SensorDiscoveryInfo, 0)
Yann Garcia's avatar
Yann Garcia committed
	if len(registeredIotPlatformsMap) == 0 {
		return sensors, nil
	for _, v := range sensorsMap {
		log.Info("SensorDiscoveryInfoAll: adding sensor: ", v)
		sensors = append(sensors, v)
Yann Garcia's avatar
Yann Garcia committed
	} // End of 'for' statement
	log.Info("SensorDiscoveryInfoAll: sensors: ", sensors)
Yann Garcia's avatar
Yann Garcia committed

	if profiling {
		now := time.Now()
		log.Debug("SensorDiscoveryInfoAll: ", now.Sub(profilingTimers["GetDevices"]))
	return sensors, nil
func (tm *SssMgr) GetSensor(sensorIdentifier string) (sensor SensorDiscoveryInfo, err error) {
Yann Garcia's avatar
Yann Garcia committed
	if profiling {
		profilingTimers["GetSensor"] = time.Now()
	log.Info(">>> GetSensor: sensorIdentifier: ", sensorIdentifier)
Yann Garcia's avatar
Yann Garcia committed

	tm.wg.Wait()
	log.Info("GetSensor: After Synchro")
Yann Garcia's avatar
Yann Garcia committed

	if val, ok := sensorsMap[sensorIdentifier]; !ok {
Yann Garcia's avatar
Yann Garcia committed
		err = errors.New("Wrong Device identifier")
		return sensor, err
Yann Garcia's avatar
Yann Garcia committed
	} else {
		sensor = val
Yann Garcia's avatar
Yann Garcia committed
	}

	if profiling {
		now := time.Now()
		log.Debug("GetSensor: ", now.Sub(profilingTimers["GetSensor"]))
Yann Garcia's avatar
Yann Garcia committed
	}
	log.Info("GetSensor: sensor: ", sensor)
Yann Garcia's avatar
Yann Garcia committed

	return sensor, nil
Yann Garcia's avatar
Yann Garcia committed
}

/*
 * func populateDevicesPerIotPlatforms IoT devices for all registered Iot platform
 * @return {struct} nil on success, error otherwise
 */
func (tm *SssMgr) populateDevicesPerIotPlatforms() error {
Yann Garcia's avatar
Yann Garcia committed

	tm.mutex.Lock()
	defer tm.mutex.Unlock()

	if profiling {
		profilingTimers["populateDevicesPerIotPlatforms"] = time.Now()
	}

	if len(registeredIotPlatformsMap) == 0 {
		return nil
	}

	// Refresh the list of devices for all registered Iot platform
	for _, iotPlatform := range registeredIotPlatformsMap {
		log.Debug("populateDevicesPerIotPlatforms: processing: ", iotPlatform.Address)
		err := tm.populateSensors(iotPlatform)
Yann Garcia's avatar
Yann Garcia committed
		if err != nil {
			log.Error("populateDevicesPerIotPlatforms: ", err)
			continue
		}
	} // End of 'for' statement

	if profiling {
		now := time.Now()
		log.Debug("populateDevicesPerIotPlatforms: ", now.Sub(profilingTimers["populateDevicesPerIotPlatforms"]))
	}

	return nil
}

/*
 * func PopulateDevices IoT devices for the specified Iot platform
 * @param {string} iotPlatformId contains the IoT platform identifier
 * @return {struct} nil on success, error otherwise
 */
func (tm *SssMgr) populateSensors(iotPlatformInfo IotPlatformInfo) error {
Yann Garcia's avatar
Yann Garcia committed
	if profiling {
		profilingTimers["populateSensors"] = time.Now()
Yann Garcia's avatar
Yann Garcia committed
	log.Info(">>> populateSensors: iotPlatformInfo=", iotPlatformInfo)
Yann Garcia's avatar
Yann Garcia committed

	// 1. Get the list of the AE
Yann Garcia's avatar
Yann Garcia committed
	// Build the context
	var ctx = SssMgrBindingProtocolContext{
		host: iotPlatformInfo.Address,
		port: iotPlatformInfo.Port,
		name: iotPlatformInfo.Name,
		to:   iotPlatformInfo.Name,
		from: "Admin", // FIXME FSCOM How to get it
		op:   2,       // RETRIEVE
		ty:   -1,
		rqi:  uuid.New().String(),
		rvi:  []string{"4"}, // FIXME FSCOM How to get it
		code: 200,
	}
Yann Garcia's avatar
Yann Garcia committed
	// Build the queries
	queries := map[string]string{}
	queries["fu"] = "1" // Filter usage
Yann Garcia's avatar
Yann Garcia committed
	queries["ty"] = "3" // Filter on oneM2M CIN for sensors
	ctx.queries = queries
	err, resp := protocol.send(ctx)
Yann Garcia's avatar
Yann Garcia committed
	if err != nil {
Yann Garcia's avatar
Yann Garcia committed
		log.Error("oneM2M_create: ", err.Error())
Yann Garcia's avatar
Yann Garcia committed
		return err
	}
Yann Garcia's avatar
Yann Garcia committed
	log.Debug("populateSensors: resp: ", resp)
	log.Debug("populateSensors: TypeOf(resp): ", reflect.TypeOf(resp))
	oneM2M_uril := resp.(map[string]interface{})
	log.Debug("populateSensors: oneM2M_uril: ", oneM2M_uril)
	log.Debug("populateSensors: TypeOf(oneM2M_uril): ", reflect.TypeOf(oneM2M_uril))
	log.Debug("populateSensors: len(oneM2M_uril): ", len(oneM2M_uril))
	// Loop for each CIN and build the sensor list
Yann Garcia's avatar
Yann Garcia committed
	for _, v := range oneM2M_uril["m2m:uril"].([]interface{}) {
		log.Debug("populateSensors: Processing key: v: ", v)
		log.Debug("populateSensors: Processing key: TypeOf(v): ", reflect.TypeOf(v))
		s := v.(string)
		if s == "laboai-cse-in/acpCreateACPs" || s == "laboai-cse-in/CAdmin" {
			// FIXME FSCOM Bug in MQTT DISCOVERY request which does not provide the same response that HTTP DISCOVERY with the same filter criteria
			continue // Discard it
Yann Garcia's avatar
Yann Garcia committed
		}
Yann Garcia's avatar
Yann Garcia committed
		ctx.to = s
		ctx.queries["fu"] = "2"
		err, resp := protocol.send(ctx)
Yann Garcia's avatar
Yann Garcia committed
		if err != nil {
Yann Garcia's avatar
Yann Garcia committed
			log.Error("oneM2M_create: ", err.Error())
			continue
		}
		log.Debug("populateSensors: resp: ", resp)
		log.Debug("populateSensors: type(resp): ", reflect.TypeOf(resp))
		if resp.(map[string]interface{}) == nil || resp.(map[string]interface{})["m2m:cnt"] == nil {
Yann Garcia's avatar
Yann Garcia committed
			continue
		}
Yann Garcia's avatar
Yann Garcia committed
		oneM2M_cin := resp.(map[string]interface{})["m2m:cnt"].(map[string]interface{})
		log.Debug("populateSensors: type(oneM2M_cin): ", reflect.TypeOf(oneM2M_cin))
		log.Debug("populateSensors: len(oneM2M_cin): ", len(oneM2M_cin))
		log.Debug("populateSensors: oneM2M_cin: ", oneM2M_cin)
Yann Garcia's avatar
Yann Garcia committed
		var sensor = SensorDiscoveryInfo{
			IotPlatformId: iotPlatformInfo.IotPlatformId,
		}
		for k, v := range oneM2M_cin {
			log.Debug(k, " value is ", v)
			log.Debug("populateSensors: type(v): ", reflect.TypeOf(v))
Yann Garcia's avatar
Yann Garcia committed

Yann Garcia's avatar
Yann Garcia committed
			if k == "ri" {
				if item, ok := v.(string); ok {
					sensor.SensorIdentifier = item
				} else {
					log.Error("populateSensors: Failed to process ", k)
				}
			} else if k == "ty" {
				if item, ok := v.(float64); ok {
					sensor.SensorType = strconv.FormatFloat(item, 'f', -1, 64)
Yann Garcia's avatar
Yann Garcia committed
				} else {
Yann Garcia's avatar
Yann Garcia committed
					log.Error("populateSensors: Failed to process ", k)
				}
			} else {
				sensor.SensorPropertyList = append(sensor.SensorPropertyList, k)
				if item, ok := v.(string); ok {
					sensor.SensorCharacteristicList = append(
						sensor.SensorCharacteristicList,
						SensorCharacteristic{
							CharacteristicName:  k,
							CharacteristicValue: string(item),
						})
				} else if item, ok := v.(float64); ok {
					sensor.SensorCharacteristicList = append(
						sensor.SensorCharacteristicList,
						SensorCharacteristic{
							CharacteristicName:  k,
							CharacteristicValue: strconv.FormatFloat(item, 'f', -1, 64),
						})
				} else if item, ok := v.(int64); ok {
					sensor.SensorCharacteristicList = append(
						sensor.SensorCharacteristicList,
						SensorCharacteristic{
							CharacteristicName:  k,
							CharacteristicValue: strconv.FormatInt(item, 10),
						})
				} else if item, ok := v.(bool); ok {
					sensor.SensorCharacteristicList = append(
						sensor.SensorCharacteristicList,
						SensorCharacteristic{
							CharacteristicName:  k,
							CharacteristicValue: strconv.FormatBool(item),
						})
				} else if item, ok := v.([]string); ok {
					sensor.SensorCharacteristicList = append(
						sensor.SensorCharacteristicList,
						SensorCharacteristic{
							CharacteristicName:  k,
							CharacteristicValue: strings.Join(item, ","),
						})
				} else if item, ok := v.([]int64); ok {
					log.Error("populateSensors: Failed to convert list of int64 into string: ", item)
				} else if item, ok := v.([]interface{}); ok {
					log.Debug("populateSensors: Got []interface {} for ", k)
					log.Debug("populateSensors: ValueOf ", reflect.ValueOf(item))
					s := SensorCharacteristic{
						CharacteristicName: k,
Yann Garcia's avatar
Yann Garcia committed
					var buf bytes.Buffer
					fmt.Fprintf(&buf, "%T", reflect.ValueOf(item))
					s.CharacteristicValue = buf.String()
					sensor.SensorCharacteristicList = append(sensor.SensorCharacteristicList, s)
				} else {
					log.Error("populateSensors: Failed to process ", k)
Yann Garcia's avatar
Yann Garcia committed
				}
Yann Garcia's avatar
Yann Garcia committed
			}
Yann Garcia's avatar
Yann Garcia committed

Yann Garcia's avatar
Yann Garcia committed
		} // End of 'for' statement
		log.Info("populateSensors: sensor: ", sensor)
		sensorsMap[sensor.SensorIdentifier] = sensor
		sensorsPerPlatformMap[sensor.IotPlatformId] = append(sensorsPerPlatformMap[sensor.IotPlatformId], sensor.SensorIdentifier)
Yann Garcia's avatar
Yann Garcia committed
	} // End of 'for' statement
Yann Garcia's avatar
Yann Garcia committed

	// // 1. Get the list of the AE
	// // Build the URL
	// url := "http://" + iotPlatformInfo.Address + ":" + strconv.Itoa(int(iotPlatformInfo.Port)) + "/" + iotPlatformInfo.Name
	// log.Debug("populateSensors: url=", url)
	// // Build the headers
	// var headers = http.Header{}
	// headers["Accept"] = []string{headerAccept}
	// headers["Content-Type"] = []string{headerContentType}
	// headers["X-M2M-Origin"] = []string{"CAdmin"} // FIXME FSCOM How to get it
	// headers["X-M2M-RI"] = []string{uuid.New().String()}
	// headers["X-M2M-RVI"] = []string{"4"} // FIXME FSCOM How to get it
	// // Build the queries
	// queries := map[string]string{}
	// queries["fu"] = "1" // Filter usage
	// queries["ty"] = "4" // Filter on oneM2M CIN for sensors
	// // Send the request
	// response, err := sendRequest("GET", url, headers, nil, nil, queries, 200)
	// if err != nil {
	// 	log.Error("populateSensors: ", err.Error())
	// 	return err
	// }
	// log.Debug("populateSensors: response: ", string(response))

	// var oneM2M_uril map[string][]string
	// err = json.Unmarshal(response, &oneM2M_uril)
	// if err != nil {
	// 	log.Error("populateSensors: ", err.Error())
	// 	return err
	// }
	// log.Debug("populateSensors: oneM2M_uril: ", len(oneM2M_uril))
	// log.Debug(oneM2M_uril)
	// if _, ok := oneM2M_uril["m2m:uril"]; !ok {
	// 	err := errors.New("populateSensors: CharacteristicName not found: m2m:uril")
	// 	log.Error(err.Error())
	// 	return err
	// }
	// // Loop for each CIN and build the sensor list
	// for _, v := range oneM2M_uril["m2m:uril"] {
	// 	log.Debug("populateSensors: Processing key: ", v)

	// 	url := "http://" + iotPlatformInfo.Address + ":" + strconv.Itoa(int(iotPlatformInfo.Port)) + "/" + v
	// 	log.Debug("populateSensors: url=", url)
	// 	// Build the headers
	// 	var headers = http.Header{}
	// 	headers["Accept"] = []string{headerAccept}
	// 	headers["Content-Type"] = []string{headerContentType}
	// 	headers["X-M2M-Origin"] = []string{"CAdmin"} // FIXME FSCOM How to get it
	// 	headers["X-M2M-RI"] = []string{uuid.New().String()}
	// 	headers["X-M2M-RVI"] = []string{"4"} // FIXME FSCOM How to get it
	// 	// Build the queries
	// 	queries := map[string]string{}
	// 	queries["fu"] = "2" // Filter usage
	// 	// Send the request
	// 	response, err := sendRequest("GET", url, headers, nil, nil, queries, 200)
	// 	if err != nil {
	// 		log.Error("populateSensors: ", err.Error())
	// 		return err
	// 	}
	// 	log.Debug("populateSensors: response: ", string(response))
	// 	var oneM2M_cin map[string]map[string]interface{}
	// 	err = json.Unmarshal(response, &oneM2M_cin)
	// 	if err != nil {
	// 		log.Error("populateSensors: ", err.Error())
	// 		continue
	// 	}
	// 	log.Debug("populateSensors: type(oneM2M_cin): ", reflect.TypeOf(oneM2M_cin))
	// 	log.Debug("populateSensors: len(oneM2M_cin): ", len(oneM2M_cin))
	// 	log.Debug("populateSensors: oneM2M_cin: ", oneM2M_cin)
	// 	for _, m := range oneM2M_cin {
	// 		//log.Debug("==> ", i, " value is ", m)
	// 		var sensor = SensorDiscoveryInfo{
	// 			IotPlatformId: iotPlatformInfo.IotPlatformId,
	// 		}

	// 		// m is a map[string]interface.
	// 		// loop over keys and values in the map.
	// 		for k, v := range m {
	// 			log.Debug(k, " value is ", v)
	// 			log.Debug("populateSensors: type(v): ", reflect.TypeOf(v))

	// 			if k == "ri" {
	// 				if item, ok := v.(string); ok {
	// 					sensor.SensorIdentifier = item
	// 				} else {
	// 					log.Error("populateSensors: Failed to process ", k)
	// 				}
	// 			} else if k == "ty" {
	// 				if item, ok := v.(float64); ok {
	// 					sensor.SensorType = strconv.FormatFloat(item, 'f', -1, 64)
	// 				} else {
	// 					log.Error("populateSensors: Failed to process ", k)
	// 				}
	// 			} else {
	// 				sensor.SensorPropertyList = append(sensor.SensorPropertyList, k)
	// 				if item, ok := v.(string); ok {
	// 					sensor.SensorCharacteristicList = append(
	// 						sensor.SensorCharacteristicList,
	// 						SensorCharacteristic{
	// 							CharacteristicName:  k,
	// 							CharacteristicValue: string(item),
	// 						})
	// 				} else if item, ok := v.(float64); ok {
	// 					sensor.SensorCharacteristicList = append(
	// 						sensor.SensorCharacteristicList,
	// 						SensorCharacteristic{
	// 							CharacteristicName:  k,
	// 							CharacteristicValue: strconv.FormatFloat(item, 'f', -1, 64),
	// 						})
	// 				} else if item, ok := v.(int64); ok {
	// 					sensor.SensorCharacteristicList = append(
	// 						sensor.SensorCharacteristicList,
	// 						SensorCharacteristic{
	// 							CharacteristicName:  k,
	// 							CharacteristicValue: strconv.FormatInt(item, 10),
	// 						})
	// 				} else if item, ok := v.(bool); ok {
	// 					sensor.SensorCharacteristicList = append(
	// 						sensor.SensorCharacteristicList,
	// 						SensorCharacteristic{
	// 							CharacteristicName:  k,
	// 							CharacteristicValue: strconv.FormatBool(item),
	// 						})
	// 				} else if item, ok := v.([]string); ok {
	// 					sensor.SensorCharacteristicList = append(
	// 						sensor.SensorCharacteristicList,
	// 						SensorCharacteristic{
	// 							CharacteristicName:  k,
	// 							CharacteristicValue: strings.Join(item, ","),
	// 						})
	// 				} else if item, ok := v.([]int64); ok {
	// 					log.Error("populateSensors: Failed to convert list of int64 into string: ", item)
	// 				} else if item, ok := v.([]interface{}); ok {
	// 					log.Debug("populateSensors: Got []interface {} for ", k)
	// 					log.Debug("populateSensors: ValueOf ", reflect.ValueOf(item))
	// 					s := SensorCharacteristic{
	// 						CharacteristicName: k,
	// 					}
	// 					var buf bytes.Buffer
	// 					fmt.Fprintf(&buf, "%T", reflect.ValueOf(item))
	// 					s.CharacteristicValue = buf.String()
	// 					sensor.SensorCharacteristicList = append(sensor.SensorCharacteristicList, s)
	// 				} else {
	// 					log.Error("populateSensors: Failed to process ", k)
	// 				}
	// 			}
	// 			// if k == "rn" {
	// 			// 	if item, ok := v.(string); ok {
	// 			// 		sensor.DeviceId = item
	// 			// 	} else {
	// 			// 		log.Error("populateSensors: Failed to process ", k)
	// 			// 	}
	// 			// } else if k == "ri" {
	// 			// 	if item, ok := v.(string); ok {
	// 			// 		sensor.SensorIdentifier = item
	// 			// 	} else {
	// 			// 		log.Error("populateSensors: Failed to process ", k)
	// 			// 	}
	// 			// } else if k == "ty" {
	// 			// 	if item, ok := v.(float64); ok {
	// 			// 		sensor.SensorStatusType = strconv.FormatFloat(item, 'f', -1, 64)
	// 			// 	} else {
	// 			// 		log.Error("populateSensors: Failed to process ", k)
	// 			// 	}
	// 			// } else { // default: if k == "lt" || k == "et" || k == "ct" || k == "st" || k == "pi" || k == "lbl" {
	// 			// 	if item, ok := v.(string); ok {
	// 			// 		sensor.SensorCharacteristicList = append(
	// 			// 			sensor.SensorCharacteristicList,
	// 			// 			SensorCharacteristic{
	// 			// 				CharacteristicName:          k,
	// 			// 				CharacteristicValue:         string(item),
	// 			// 				CharacteristicUnitOfMeasure: nil,
	// 			// 			})
	// 			// 	} else if item, ok := v.(float64); ok {
	// 			// 		sensor.SensorCharacteristicList = append(
	// 			// 			sensor.SensorCharacteristicList,
	// 			// 			SensorCharacteristic{
	// 			// 				CharacteristicName:          k,
	// 			// 				CharacteristicValue:         strconv.FormatFloat(item, 'f', -1, 64),
	// 			// 				CharacteristicUnitOfMeasure: nil,
	// 			// 			})
	// 			// 	} else if item, ok := v.([]string); ok {
	// 			// 		sensor.SensorCharacteristicList = append(
	// 			// 			sensor.SensorCharacteristicList,
	// 			// 			SensorCharacteristic{
	// 			// 				CharacteristicName:          k,
	// 			// 				CharacteristicValue:         strings.Join(item, ","),
	// 			// 				CharacteristicUnitOfMeasure: nil,
	// 			// 			})
	// 			// 	} else {
	// 			// 		log.Error("populateSensors: Failed to process ", k)
	// 			// 	}
	// 			// }
	// 		} // End of 'for' loop
	// 		log.Info("populateSensors: sensor: ", sensor)
	// 		sensorsMap[sensor.SensorIdentifier] = sensor
	// 		sensorsPerPlatformMap[sensor.IotPlatformId] = append(sensorsPerPlatformMap[sensor.IotPlatformId], sensor.SensorIdentifier)
	// 	} // End of 'for' loop

	// } // End of 'for' statement
	log.Info("populateSensors: sensorsMap: ", sensorsMap)
	log.Info("populateSensors: sensorsPerPlatformMap: ", sensorsPerPlatformMap)
Yann Garcia's avatar
Yann Garcia committed

	if profiling {
		now := time.Now()
		log.Debug("populateSensors: ", now.Sub(profilingTimers["populateSensors"]))
func (tm *SssMgr) oneM2M_create(sensor SensorDiscoveryInfo, requestedIotPlatformId string, type_ string) (sensorResp SensorDiscoveryInfo, err error) {
Yann Garcia's avatar
Yann Garcia committed
	// FIXME FSCOM: requestedIotPlatformId should be useless

Yann Garcia's avatar
Yann Garcia committed
	// Create the initial payload dictionary
Yann Garcia's avatar
Yann Garcia committed
	var bodyMap = map[string]map[string]interface{}{}
	// Initialize the entry
	if type_ == "AE" { // FIXME FSCOM Clarify how to map Deviceinfo with oneM2M AE/CNT/fexContainer
		bodyMap["m2m:ae"] = make(map[string]interface{}, 0)
		bodyMap["m2m:ae"]["api"] = "Norg.etsi." + requestedIotPlatformId + "." + sensor.SensorIdentifier
		bodyMap["m2m:ae"]["rn"] = sensor.SensorIdentifier
Yann Garcia's avatar
Yann Garcia committed
		bodyMap["m2m:ae"]["rr"] = false
		bodyMap["m2m:ae"]["srv"] = []string{"4"}
Yann Garcia's avatar
Yann Garcia committed
	} else if type_ == "CNT" {
		bodyMap["m2m:cnt"] = make(map[string]interface{}, 0)
		bodyMap["m2m:cnt"]["mbs"] = 10000
		bodyMap["m2m:cnt"]["mni"] = 10
		bodyMap["m2m:cnt"]["rn"] = sensor.SensorIdentifier
		bodyMap["m2m:cnt"]["srv"] = []string{"4"}
Yann Garcia's avatar
Yann Garcia committed
		// Add metadata
		if len(sensor.SensorCharacteristicList) != 0 {
			for _, val := range sensor.SensorCharacteristicList {
Yann Garcia's avatar
Yann Garcia committed
				log.Debug("oneM2M_create: Adding CNT metadata: ", val)
				// FIXME FSCOM Add metadata
			} // End of 'for' statement
		}
	} else {
		err = errors.New("oneM2M_create: Invalid type")
		log.Error("oneM2M_create: ", err.Error())
		return sensorResp, err
Yann Garcia's avatar
Yann Garcia committed
	}
Yann Garcia's avatar
Yann Garcia committed

	// Send it and get the result
	var ctx = SssMgrBindingProtocolContext{
		host: registeredIotPlatformsMap[requestedIotPlatformId].Address,
		port: registeredIotPlatformsMap[requestedIotPlatformId].Port,
		name: registeredIotPlatformsMap[requestedIotPlatformId].Name,
		to:   registeredIotPlatformsMap[requestedIotPlatformId].Name,
		from: requestedIotPlatformId,
		op:   1, // CREATE
		rqi:  uuid.New().String(),
		rvi:  []string{"4"}, // FIXME FSCOM How to get it
		body: bodyMap,
		code: 201,
Yann Garcia's avatar
Yann Garcia committed
	}
Yann Garcia's avatar
Yann Garcia committed
	if type_ == "AE" {
		ctx.ty = 2
	} else if type_ == "CNT" {
		ctx.ty = 4
	} else {
		err = errors.New("oneM2M_create: Invalid type")
		log.Error("send: ", err.Error())
		return sensorResp, err
Yann Garcia's avatar
Yann Garcia committed
	}
Yann Garcia's avatar
Yann Garcia committed

	//var resp = map[string]map[string]interface{}{}
	err, resp := protocol.send(ctx)
Yann Garcia's avatar
Yann Garcia committed
	if err != nil {
		log.Error("oneM2M_create: ", err.Error())
		return sensorResp, err
Yann Garcia's avatar
Yann Garcia committed
	}
Yann Garcia's avatar
Yann Garcia committed

	// Build the headers
	// var headers = http.Header{}
	// headers["Accept"] = []string{headerAccept}
	// headers["X-M2M-Origin"] = []string{"C" + requestedIotPlatformId} // FIXME FSCOM How to get it
	// headers["X-M2M-RI"] = []string{uuid.New().String()}
	// headers["X-M2M-RVI"] = []string{"4"}
	// var s string
	// if type_ == "AE" {
	// 	s = headerContentType + ";ty=2"
	// } else if type_ == "CNT" {
	// 	s = headerContentType + ";ty=4"
	// }
	// headers["Content-Type"] = []string{s}
	// // Build the url and the body
	// var url string
	// var bodyMap = map[string]map[string]interface{}{}
	// Initialize the entry
	// if type_ == "AE" { // FIXME FSCOM Clarify how to map Deviceinfo with oneM2M AE/CNT/fexContainer
	// bodyMap["m2m:ae"] = make(map[string]interface{}, 0)
	// bodyMap["m2m:ae"]["api"] = "Norg.etsi." + requestedIotPlatformId + "." + sensor.SensorIdentifier
	// bodyMap["m2m:ae"]["rn"] = sensor.SensorIdentifier
	// bodyMap["m2m:ae"]["rr"] = false
	// bodyMap["m2m:ae"]["srv"] = []string{"4"}
	// url = "http://" + registeredIotPlatformsMap[requestedIotPlatformId].Address + ":" + strconv.Itoa(int(registeredIotPlatformsMap[requestedIotPlatformId].Port)) + "/" + registeredIotPlatformsMap[requestedIotPlatformId].Name
	// } else if type_ == "CNT" {
	// bodyMap["m2m:cnt"] = make(map[string]interface{}, 0)
	// bodyMap["m2m:cnt"]["mbs"] = 10000
	// bodyMap["m2m:cnt"]["mni"] = 10
	// bodyMap["m2m:cnt"]["rn"] = sensor.SensorIdentifier
	// bodyMap["m2m:cnt"]["srv"] = []string{"4"}
	// // Add metadata
	// if len(sensor.SensorCharacteristicList) != 0 {
	// 	for _, val := range sensor.SensorCharacteristicList {
	// 		log.Debug("oneM2M_create: Adding CNT metadata: ", val)
	// 		// FIXME FSCOM Add metadata
	// 	} // End of 'for' statement
	// }
	// url = "http://" + registeredIotPlatformsMap[requestedIotPlatformId].Address + ":" + strconv.Itoa(int(registeredIotPlatformsMap[requestedIotPlatformId].Port)) + "/" + registeredIotPlatformsMap[requestedIotPlatformId].Name
	// } else {
	// 	err = errors.New("oneM2M_create: Invalid type")
	// 	log.Error("oneM2M_create: ", err.Error())
	// 	return sensorResp, err
	// }
	// log.Debug("oneM2M_create: url=", url)
	// log.Debug("oneM2M_create: bodyMap=", bodyMap)
	// body, err := json.Marshal(bodyMap)
	// if err != nil {
	// 	log.Error("oneM2M_create: ", err.Error())
	// 	return sensorResp, err
	// }
	// log.Debug("oneM2M_create: Request body: ", string(body))
	// Send the request
	// response, err := sendRequest("POST", url, headers, bytes.NewBuffer(body), nil, nil, 201)
	// if err != nil {
	// 	log.Error("oneM2M_create: ", err.Error())
	// 	return sensorResp, err
	// }
	// log.Debug("oneM2M_create: response: ", string(response))
	// var d map[string]map[string]interface{}
	// err = json.Unmarshal(response, &d)
	// if err != nil {
	// 	log.Error("oneM2M_create: ", err.Error())
	// 	return sensorResp, err
	// }
	log.Debug("oneM2M_create: d: ", resp)
	log.Debug("oneM2M_create: TypeOf(d): ", reflect.TypeOf(resp))
Yann Garcia's avatar
Yann Garcia committed

	// Add additional entries
Yann Garcia's avatar
Yann Garcia committed
	// sensorResp, err = tm.oneM2M_deserialize(sensorResp, resp)
	// if err != nil {
	// 	log.Error("oneM2M_create: ", err.Error())
	// 	return sensorResp, err
	// }
	// log.Debug("oneM2M_create: sensorResp: ", sensorResp)
Yann Garcia's avatar
Yann Garcia committed

	return sensorResp, nil
func (tm *SssMgr) oneM2M_discovery(sensor SensorDiscoveryInfo, requestedIotPlatformId string, type_ string) (sensorResp SensorDiscoveryInfo, err error) {
Yann Garcia's avatar
Yann Garcia committed
	// FIXME FSCOM: requestedIotPlatformId should be useless

	// 1. Get the list of the AE
	// Build the URL
	url := "http://" + registeredIotPlatformsMap[requestedIotPlatformId].Address + ":" + strconv.Itoa(int(registeredIotPlatformsMap[requestedIotPlatformId].Port)) + "/" + registeredIotPlatformsMap[requestedIotPlatformId].Name
Yann Garcia's avatar
Yann Garcia committed
	log.Debug("oneM2M_discovery: url=", url)
	// Build the headers
	var headers = http.Header{}
	headers["Accept"] = []string{headerAccept}
	headers["Content-Type"] = []string{headerContentType}
	headers["X-M2M-Origin"] = []string{"CAdmin"} // FIXME FSCOM How to get it
	headers["X-M2M-RI"] = []string{uuid.New().String()}
	headers["X-M2M-RVI"] = []string{"4"} // FIXME FSCOM How to get it
Yann Garcia's avatar
Yann Garcia committed
	// Build the queries
	queries := map[string]string{}
	if type_ == "CN" {
		queries["fu"] = "1" // Filter usage
		queries["ty"] = "4" // Filter on oneM2M CIN for sensor
Yann Garcia's avatar
Yann Garcia committed
	} else {
		err = errors.New("oneM2M_discovery: Invalid type")
		log.Error("oneM2M_discovery: ", err.Error())
		return sensorResp, err
Yann Garcia's avatar
Yann Garcia committed
	}
	// Send the request
	response, err := sendRequest("GET", url, headers, nil, nil, queries, 200)
	if err != nil {
		log.Error("oneM2M_discovery: ", err.Error())
		return sensorResp, err
Yann Garcia's avatar
Yann Garcia committed
	}
	log.Debug("oneM2M_discovery: response: ", string(response))

	var d map[string]map[string]interface{}
	err = json.Unmarshal(response, &d)
	if err != nil {
		log.Error("oneM2M_discovery: ", err.Error())
		return sensorResp, err
Yann Garcia's avatar
Yann Garcia committed
	}
	log.Debug("oneM2M_discovery: d: ", d)
	// Add additional entries
	sensorResp, err = tm.oneM2M_deserialize(sensor, d)
Yann Garcia's avatar
Yann Garcia committed
	if err != nil {
		log.Error("oneM2M_discovery: ", err.Error())
		return sensorResp, err
Yann Garcia's avatar
Yann Garcia committed
	}
	// log.Debug("oneM2M_discovery: sensorResp: ", sensorResp)
	return sensorResp, nil
func (tm *SssMgr) oneM2M_get(sensor SensorDiscoveryInfo, requestedIotPlatformId string, type_ string) (sensorResp SensorDiscoveryInfo, err error) {
Yann Garcia's avatar
Yann Garcia committed
	// FIXME FSCOM: requestedIotPlatformId should be useless

	if sensor.SensorIdentifier == "" {
Yann Garcia's avatar
Yann Garcia committed
		err = errors.New("oneM2M_get: Cannot find \"ri\" value")
		log.Error("oneM2M_get: ", err.Error())
		return sensorResp, err
Yann Garcia's avatar
Yann Garcia committed
	}

	// 1. Get the list of the AE
	// Build the URL
	url := "http://" + registeredIotPlatformsMap[requestedIotPlatformId].Address + ":" + strconv.Itoa(int(registeredIotPlatformsMap[requestedIotPlatformId].Port)) + "/" + sensor.SensorIdentifier
Yann Garcia's avatar
Yann Garcia committed
	log.Debug("oneM2M_get: url=", url)
	// Build the headers
	var headers = http.Header{}
	headers["Accept"] = []string{headerAccept}
	headers["Content-Type"] = []string{headerContentType}
	headers["X-M2M-Origin"] = []string{"CAdmin"} // FIXME FSCOM How to get it
	headers["X-M2M-RI"] = []string{uuid.New().String()}
	headers["X-M2M-RVI"] = []string{"4"} // FIXME FSCOM How to get it
Yann Garcia's avatar
Yann Garcia committed
	// Build the queries
	queries := map[string]string{}
	if type_ == "AE" {
		queries = nil
	} else if type_ == "CN" {
		queries["fu"] = "1" // Filter usage
		queries["ty"] = "4" // Filter on oneM2M CIN for sensor
Yann Garcia's avatar
Yann Garcia committed
	} else {
		err = errors.New("oneM2M_get: Invalid type")
		log.Error("oneM2M_get: ", err.Error())
		return sensorResp, err
Yann Garcia's avatar
Yann Garcia committed
	}
	// Send the request
	response, err := sendRequest("GET", url, headers, nil, nil, queries, 200)
	if err != nil {
		log.Error("oneM2M_get: ", err.Error())
		return sensorResp, err
Yann Garcia's avatar
Yann Garcia committed
	}
	log.Debug("oneM2M_get: response: ", string(response))

	var d map[string]map[string]interface{}
	err = json.Unmarshal(response, &d)
	if err != nil {
		log.Error("oneM2M_get: ", err.Error())
		return sensorResp, err
Yann Garcia's avatar
Yann Garcia committed
	}
	log.Debug("oneM2M_get: d: ", d)

	// Add additional entries
	sensorResp, err = tm.oneM2M_deserialize(sensor, d)
Yann Garcia's avatar
Yann Garcia committed
	if err != nil {
		log.Error("oneM2M_get: ", err.Error())
		return sensorResp, err
Yann Garcia's avatar
Yann Garcia committed
	}
	log.Debug("oneM2M_get: sensorResp: ", sensorResp)
	return sensorResp, nil
Yann Garcia's avatar
Yann Garcia committed
func (tm *SssMgr) oneM2M_subscribe(sensor SensorDiscoveryInfo, requestedIotPlatformId string, type_ string) (subscription string, err error) {
	// FIXME FSCOM: requestedIotPlatformId should be useless

	// Build the headers
	var headers = http.Header{}
	headers["Accept"] = []string{headerAccept}
	headers["X-M2M-Origin"] = []string{"C" + requestedIotPlatformId} // FIXME FSCOM How to get it
	headers["X-M2M-RI"] = []string{uuid.New().String()}
	headers["X-M2M-RVI"] = []string{"4"}
	s := headerContentType + ";ty=23"
	headers["Content-Type"] = []string{s}
	// Build the url and the body
	var url string
	var bodyMap = map[string]map[string]interface{}{}
	bodyMap["m2m:sub"] = make(map[string]interface{}, 0)
	net := make(map[string][4]int)
	net["net"] = [4]int{1, 2, 3, 4}
	bodyMap["m2m:sub"]["enc"] = net
	bodyMap["m2m:sub"]["nu"] = "" // FIXME The URI of the listener
	bodyMap["m2m:sub"]["rn"] = sensor.SensorIdentifier
	url = "http://" + registeredIotPlatformsMap[requestedIotPlatformId].Address + ":" + strconv.Itoa(int(registeredIotPlatformsMap[requestedIotPlatformId].Port)) + "/" + registeredIotPlatformsMap[requestedIotPlatformId].Name
	log.Debug("oneM2M_subscribe: url=", url)
	log.Debug("oneM2M_subscribe: bodyMap=", bodyMap)
	body, err := json.Marshal(bodyMap)
	if err != nil {
		log.Error("oneM2M_subscribe: ", err.Error())
		return "", err
	}
	log.Debug("oneM2M_subscribe: Request body: ", string(body))
	// Send the request
	response, err := sendRequest("POST", url, headers, bytes.NewBuffer(body), nil, nil, 201)
	if err != nil {
		log.Error("oneM2M_subscribe: ", err.Error())
		return "", err
	}
	log.Debug("oneM2M_subscribe: response: ", string(response))
	var d map[string]map[string]interface{}
	err = json.Unmarshal(response, &d)
	if err != nil {
		log.Error("oneM2M_subscribe: ", err.Error())
		return "", err
	}
	log.Debug("oneM2M_subscribe: d: ", d)

	err = errors.New("oneM2M_subscribe: To be implemented")
	return "", err /*nil*/
}

func (tm *SssMgr) oneM2M_delete(sensor SensorDiscoveryInfo, requestedIotPlatformId string, type_ string) (err error) {
Yann Garcia's avatar
Yann Garcia committed
	// FIXME FSCOM: requestedIotPlatformId should be useless

	if sensor.SensorIdentifier == "" {
Yann Garcia's avatar
Yann Garcia committed
		err = errors.New("oneM2M_delete: Cannot find \"ri\" value")
		log.Error("oneM2M_delete: ", err.Error())
		return err
	}

	// Build the URL
	url := "http://" + registeredIotPlatformsMap[requestedIotPlatformId].Address + ":" + strconv.Itoa(int(registeredIotPlatformsMap[requestedIotPlatformId].Port)) + "/" + sensor.SensorIdentifier
Yann Garcia's avatar
Yann Garcia committed
	log.Debug("oneM2M_delete: url=", url)
	// Build the headers
	var headers = http.Header{}
	headers["Accept"] = []string{headerAccept}
	headers["Content-Type"] = []string{headerContentType}
	headers["X-M2M-Origin"] = []string{"C" + requestedIotPlatformId} // FIXME FSCOM How to get it
	headers["X-M2M-RI"] = []string{uuid.New().String()}
	headers["X-M2M-RI"] = []string{uuid.New().String()}
	headers["X-M2M-RVI"] = []string{"4"}
Yann Garcia's avatar
Yann Garcia committed
	// Send the request
	_, err = sendRequest("DELETE", url, headers, nil, nil, nil, 200)
	if err != nil {
		log.Error("oneM2M_delete: ", err.Error())
		return err
	}

	return nil
}

func (tm *SssMgr) oneM2M_subscribe_discovery_event(requestedIotPlatformId string) (err error) {

	return nil
}

func (tm *SssMgr) oneM2M_deserialize(sensor SensorDiscoveryInfo, response map[string]map[string]interface{}) (sensorResp SensorDiscoveryInfo, err error) {
	sensorResp = sensor // Same data structure
Yann Garcia's avatar
Yann Garcia committed

	log.Debug("oneM2M_deserialize: type(response): ", reflect.TypeOf(response))
	log.Debug("oneM2M_deserialize: len(response): ", len(response))
	log.Debug("oneM2M_deserialize: response: ", response)

	if val, ok := response["m2m:ae"]; ok {
		log.Debug("oneM2M_deserialize: val: ", val)