diff --git a/.meepctl-repocfg.yaml b/.meepctl-repocfg.yaml index 28399bbb8c79b829b48f2a68b7430278f1943d21..5abbc12b552c6843b7fc2d4099888fa6e2d05e47 100644 --- a/.meepctl-repocfg.yaml +++ b/.meepctl-repocfg.yaml @@ -1,4 +1,4 @@ -# Copyright (c) 2024 The AdvantEDGE Authors +# Copyright (c) 2025 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. @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -version: 1.10.0 +version: 1.11.0 repo: name: etsi-mec-sandbox @@ -424,6 +424,12 @@ repo: meep-virt-chart-templates: charts/meep-virt-chart-templates meep-vis: charts/meep-vis meep-federation: charts/meep-federation + meep-cloud-mosquitto: charts/meep-cloud-mosquitto + meep-mosquitto: charts/meep-mosquitto + meep-acme-mn-cse: charts/meep-acme-mn-cse + meep-acme-in-cse: charts/meep-acme-in-cse + meep-tinyiot-mn-cse: charts/meep-tinyiot-mn-cse + meep-tinyiot-in-cse: charts/meep-tinyiot-in-cse # list of sandbox specific pods sandbox-pods: - meep-gis-engine diff --git a/AUTHORS b/AUTHORS index e8b95b3a62546e21c320078f0a10c46a9617f869..a52e9660f24d25b22930f0f96f991668f8c086fa 100644 --- a/AUTHORS +++ b/AUTHORS @@ -17,3 +17,8 @@ FSCOM InterDigital Communications Inc xFlow Research (Pvt.) Inc. FSCOM + +[v1.11.0-Beta xFlow/FSCOM] +InterDigital Communications Inc +xFlow Research (Pvt.) Inc. +FSCOM diff --git a/README.md b/README.md index 2f18c63af973559faa98387b73c023b557ade723..9bc4fdbcfb531cd8f588c626e1725bd04783f94a 100644 --- a/README.md +++ b/README.md @@ -20,13 +20,19 @@ ------ +**_What's New in v1.11.0-Beta xFlow/FSCOM!_** + +:zap: **New edge native service: [ETSI MEC040 - MEC Federation API](https://interdigitalinc.github.io/AdvantEDGE/docs/overview/edge-services/#mec-federation-service)** + +:zap: ** [ETSI MEC033](https://interdigitalinc.github.io/AdvantEDGE/docs/overview/edge-services/#iot-api) and [ETSI MEC046](https://interdigitalinc.github.io/AdvantEDGE/docs/overview/edge-services/#mec-sensors-sharing) :arrow_up:** + **_What's New in v1.10.0!_** :zap: **New edge native service: [ETSI MEC Profile for CAPIF](https://interdigitalinc.github.io/AdvantEDGE/docs/overview/edge-services/#edge-platform-application-enablement-service)** :zap: **New edge native service: [ETSI MEC040 - MEC Federation API](https://interdigitalinc.github.io/AdvantEDGE/docs/overview/edge-services/#mec-federation-service)** -:zap: **Service API upgrade to MEC Phase 3 for [ETSI MEC011](https://interdigitalinc.github.io/AdvantEDGE/docs/overview/edge-services/#edge-platform-application-enablement-service), [ETSI MEC012](https://interdigitalinc.github.io/AdvantEDGE/docs/overview/edge-services/#radio-network-information-service), [ETSI MEC013](https://interdigitalinc.github.io/AdvantEDGE/docs/overview/edge-services/#location-service), [ETSI MEC021](https://interdigitalinc.github.io/AdvantEDGE/docs/overview/edge-services/#application-mobility-service), [ETSI MEC030](https://interdigitalinc.github.io/AdvantEDGE/docs/overview/edge-services/#v2x-information-service), [ETSI MEC033](https://interdigitalinc.github.io/AdvantEDGE/docs/overview/edge-services/#iot-api), [ETSI MEC040](https://interdigitalinc.github.io/AdvantEDGE/docs/overview/edge-services/#mec-federation-service) and [ETSI MEC046](https://interdigitalinc.github.io/AdvantEDGE/docs/overview/edge-services/#mec-sensors-sharing) :arrow_up:** +:zap: **Service API upgrade to MEC Phase 3 for [ETSI MEC011](https://interdigitalinc.github.io/AdvantEDGE/docs/overview/edge-services/#edge-platform-application-enablement-service), [ETSI MEC012](https://interdigitalinc.github.io/AdvantEDGE/docs/overview/edge-services/#radio-network-information-service), [ETSI MEC013](https://interdigitalinc.github.io/AdvantEDGE/docs/overview/edge-services/#location-service), [ETSI MEC021](https://interdigitalinc.github.io/AdvantEDGE/docs/overview/edge-services/#application-mobility-service), [ETSI MEC030](https://interdigitalinc.github.io/AdvantEDGE/docs/overview/edge-services/#v2x-information-service) and [ETSI MEC040](https://interdigitalinc.github.io/AdvantEDGE/docs/overview/edge-services/#mec-federation-service) :arrow_up:** :zap: **New command line api to develop MEC application/service without GUI** diff --git a/charts/meep-federation/values-template.yaml b/charts/meep-federation/values-template.yaml index f7023deac3d9967894ff2ba20b16f5da5baaeb4b..97b25c805a5765ffe46b81d66673862ef752d341 100644 --- a/charts/meep-federation/values-template.yaml +++ b/charts/meep-federation/values-template.yaml @@ -24,7 +24,7 @@ image: MEEP_SANDBOX_NAME: {{.SandboxName}} MEEP_SVC_PATH: /fed_enablement/v1 MEEP_HOST_URL: {{.HostUrl}} - MEEP_BROKER: mqtt://meep-mosquitto:1883 + MEEP_BROKER: mqtt://meep-cloud-mosquitto:9001 MEEP_TOPIC: ETSI/MEC/Federation {{- if .IsMepService }} MEEP_MEP_NAME: {{.MepName}} diff --git a/charts/meep-vis/values-template.yaml b/charts/meep-vis/values-template.yaml index 69ad6ae3eaf51ff9616198bfc611dcc5270f90c8..6a6eaafaf3882f961a848476b3e1fd43f0adb7e7 100644 --- a/charts/meep-vis/values-template.yaml +++ b/charts/meep-vis/values-template.yaml @@ -22,7 +22,7 @@ image: MEEP_SANDBOX_NAME: {{.SandboxName}} MEEP_SVC_PATH: /vis/v2 MEEP_HOST_URL: {{.HostUrl}} - MEEP_BROKER: mqtt://meep-mosquitto:1883 #mqtt://broker.emqx.io:1883 + MEEP_BROKER: mqtt://meep-mosquitto:9001 MEEP_TOPIC: 3gpp/v2x/obu MEEP_POA_LIST: poa-5g1 {{- if .IsMepService }} diff --git a/docs/api-federation/FedServiceInfoApi.md b/docs/api-federation/FedServiceInfoApi.md index 24a58d01a92ee126ada2d56dee427f14557374ca..e71026d83768bb869c784be91be5356ea8ef0ee0 100644 --- a/docs/api-federation/FedServiceInfoApi.md +++ b/docs/api-federation/FedServiceInfoApi.md @@ -1,6 +1,6 @@ # {{classname}} -All URIs are relative to *https://localhost/sandboxname/sandboxname/sandboxname/sandboxname/fed_enablement/v1* +All URIs are relative to *https://localhost/sandboxname/fed_enablement/v1* Method | HTTP request | Description ------------- | ------------- | ------------- diff --git a/docs/api-federation/SubscriptionApi.md b/docs/api-federation/SubscriptionApi.md index a0ef08c7280ec0314d6855c729b2416c3ff0e032..845d548e1b689502a87f30cefd12d2fd7dbdc4a7 100644 --- a/docs/api-federation/SubscriptionApi.md +++ b/docs/api-federation/SubscriptionApi.md @@ -1,6 +1,6 @@ # {{classname}} -All URIs are relative to *https://localhost/sandboxname/sandboxname/sandboxname/sandboxname/fed_enablement/v1* +All URIs are relative to *https://localhost/sandboxname/fed_enablement/v1* Method | HTTP request | Description ------------- | ------------- | ------------- diff --git a/docs/api-federation/SystemInfoApi.md b/docs/api-federation/SystemInfoApi.md index 64a484c92c872165eb8f03c8a7b85bd9192315ac..cc84e02004c4cb4fc818a22fd073bf4b98134b8b 100644 --- a/docs/api-federation/SystemInfoApi.md +++ b/docs/api-federation/SystemInfoApi.md @@ -1,6 +1,6 @@ # {{classname}} -All URIs are relative to *https://localhost/sandboxname/sandboxname/sandboxname/sandboxname/fed_enablement/v1* +All URIs are relative to *https://localhost/sandboxname/fed_enablement/v1* Method | HTTP request | Description ------------- | ------------- | ------------- diff --git a/docs/api-iot/RegDevApi.md b/docs/api-iot/RegDevApi.md index 6927235585df40767313da72af2385176f36bafb..08da2c2506d5ef6468da9b7c39736303befa4e93 100644 --- a/docs/api-iot/RegDevApi.md +++ b/docs/api-iot/RegDevApi.md @@ -1,6 +1,6 @@ # {{classname}} -All URIs are relative to *https://localhost/sandboxname/sandboxname/amsi/v1* +All URIs are relative to *https://localhost/sandboxname/iots/v1* Method | HTTP request | Description ------------- | ------------- | ------------- diff --git a/docs/api-iot/RegIotPlatApi.md b/docs/api-iot/RegIotPlatApi.md index 3041d85f50b2c75458a2e2b7eb598263178aa413..83062531a92c76668ceae3ae90db693025a4bead 100644 --- a/docs/api-iot/RegIotPlatApi.md +++ b/docs/api-iot/RegIotPlatApi.md @@ -1,6 +1,6 @@ # {{classname}} -All URIs are relative to *https://localhost/sandboxname/sandboxname/amsi/v1* +All URIs are relative to *https://localhost/sandboxname/iots/v1* Method | HTTP request | Description ------------- | ------------- | ------------- diff --git a/docs/api-location/Models/LocationInfo.md b/docs/api-location/Models/LocationInfo.md index fa90c8b2c37f2b0403d75d6afb98b34fa783deab..1b773c1d5f6efcf395ce4621ab3e8e56a1c26e78 100644 --- a/docs/api-location/Models/LocationInfo.md +++ b/docs/api-location/Models/LocationInfo.md @@ -15,7 +15,7 @@ Name | Type | Description | Notes **offsetAngle** | [**Integer**](integer.md) | Present only if \"shape\" equals 6 | [optional] [default to null] **orientationMajorAxis** | [**Integer**](integer.md) | Angle of orientation of the major axis, expressed in the range 0° to 180°, as defined in ETSI TS 123 032 [14]. Present only if \"shape\" equals 4 or 6 | [optional] [default to null] **shape** | [**Integer**](integer.md) | Shape information, as detailed in ETSI TS 123 032 [14], associated with the reported location coordinate: <p>1 = ELLIPSOID_ARC <p>2 = ELLIPSOID_POINT <p>3 = ELLIPSOID_POINT_ALTITUDE <p>4 = ELLIPSOID_POINT_ALTITUDE_UNCERT_ELLIPSOID <p>5 = ELLIPSOID_POINT_UNCERT_CIRCLE <p>6 = ELLIPSOID_POINT_UNCERT_ELLIPSE <p>7 = POLYGON | [default to null] -**timestamp** | [**TimeStamp**](TimeStamp.md) | | [optional] [default to null] +**timeStamp** | [**TimeStamp**](TimeStamp.md) | | [optional] [default to null] **uncertaintyRadius** | [**Integer**](integer.md) | Present only if \"shape\" equals 6 | [optional] [default to null] **velocity** | [**LocationInfo_velocity**](LocationInfo_velocity.md) | | [optional] [default to null] diff --git a/docs/api-location/Models/TerminalDistance.md b/docs/api-location/Models/TerminalDistance.md index a00f9fe3a9966b72ce99f31f02bf8808bf8e50f9..220cfc87c21b55b8ad35f59ed2f14fcb6efdd793 100644 --- a/docs/api-location/Models/TerminalDistance.md +++ b/docs/api-location/Models/TerminalDistance.md @@ -5,7 +5,7 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- **accuracy** | [**Integer**](integer.md) | Accuracy of the provided distance in meters | [optional] [default to null] **distance** | [**Integer**](integer.md) | Distance from terminal to a location or between two terminals specified in meters | [default to null] -**timestamp** | [**TimeStamp**](TimeStamp.md) | | [optional] [default to null] +**timeStamp** | [**TimeStamp**](TimeStamp.md) | | [optional] [default to null] [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/docs/api-location/Models/UserInfo.md b/docs/api-location/Models/UserInfo.md index 0f61f2551653dbf0f4d7e9e36b3acdbc7fd4c6fd..0644a69a43d5b910e8ffce955442f49b7f63fdec 100644 --- a/docs/api-location/Models/UserInfo.md +++ b/docs/api-location/Models/UserInfo.md @@ -9,7 +9,7 @@ Name | Type | Description | Notes **contextLocationInfo** | [**String**](string.md) | Contextual information of a user location (e.g. aisle, floor, room number, etc.). | [optional] [default to null] **locationInfo** | [**LocationInfo**](LocationInfo.md) | | [optional] [default to null] **resourceURL** | [**String**](string.md) | Self-referring URL, see note 1. | [default to null] -**timestamp** | [**TimeStamp**](TimeStamp.md) | | [default to null] +**timeStamp** | [**TimeStamp**](TimeStamp.md) | | [default to null] **zoneId** | [**String**](string.md) | The identity of the zone the user is currently within, see note 1. | [default to null] [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/docs/api-location/Models/ZonalPresenceNotification.md b/docs/api-location/Models/ZonalPresenceNotification.md index 87152288366ec6d5e730d19b760afd520abadd13..2746606a8a6d8838223c4705063b8c088dc87d76 100644 --- a/docs/api-location/Models/ZonalPresenceNotification.md +++ b/docs/api-location/Models/ZonalPresenceNotification.md @@ -9,7 +9,7 @@ Name | Type | Description | Notes **interestRealm** | [**String**](string.md) | Interest realm of access point (e.g. geographical area, a type of industry etc.). | [optional] [default to null] **link** | [**List**](Link.md) | Link to other resources that are in relationship with this notification. The server SHOULD include a link to the related subscription. No other links are required or suggested by this specification | [optional] [default to null] **previousAccessPointId** | [**String**](string.md) | Identifier of access point. | [optional] [default to null] -**timestamp** | [**TimeStamp**](TimeStamp.md) | | [default to null] +**timeStamp** | [**TimeStamp**](TimeStamp.md) | | [default to null] **userEventType** | [**UserEventType**](UserEventType.md) | | [default to null] **zoneId** | [**String**](string.md) | Identifier of zone | [default to null] diff --git a/docs/api-location/Models/ZoneStatusNotification.md b/docs/api-location/Models/ZoneStatusNotification.md index c1cf89e9ab444cec14cb457a0079e623ae5b32c3..23a32413c9fe13ff01dddf22cefa32a74fe3cb8c 100644 --- a/docs/api-location/Models/ZoneStatusNotification.md +++ b/docs/api-location/Models/ZoneStatusNotification.md @@ -9,7 +9,7 @@ Name | Type | Description | Notes **numberOfUsersInAP** | [**Integer**](integer.md) | This element shall be present when ZoneStatusSubscription includes numberOfUsersAPThreshold element and the number of users in an access point exceeds the threshold defined in the subscription. | [optional] [default to null] **numberOfUsersInZone** | [**Integer**](integer.md) | This element shall be present when ZoneStatusSubscription includes numberOfUsersZoneThreshold element and the number of users in a zone exceeds the threshold defined in this subscription. | [optional] [default to null] **operationStatus** | [**OperationStatus**](OperationStatus.md) | | [optional] [default to null] -**timestamp** | [**TimeStamp**](TimeStamp.md) | | [default to null] +**timeStamp** | [**TimeStamp**](TimeStamp.md) | | [default to null] **zoneId** | [**String**](string.md) | Identifier of zone | [default to null] [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/docs/meep-sss/SensorDataSubscriptionApi.md b/docs/meep-sss/SensorDataSubscriptionApi.md index 2f3fca78f1d996be2ce856783eb87ca2227c6f13..ce4d3bc14e56a307959bd9469cfc8e6cd5f7ad74 100644 --- a/docs/meep-sss/SensorDataSubscriptionApi.md +++ b/docs/meep-sss/SensorDataSubscriptionApi.md @@ -133,7 +133,7 @@ This method shall support the URI query parameters, request and response data st Name | Type | Description | Notes ------------- | ------------- | ------------- | ------------- **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. - **body** | [**StatusDataSubscriptionIdBody**](StatusDataSubscriptionIdBody.md)| New SensorDataSubscription is included as entity body of the request | + **body** | [**SensorDataSubscriptionIdBody**](SensorDataSubscriptionIdBody.md)| New SensorDataSubscription is included as entity body of the request | **subscriptionId** | **string**| Unique identifiers of a subscription | ### Return type diff --git a/docs/meep-sss/StatusDataSubscriptionIdBody.md b/docs/meep-sss/StatusDataSubscriptionIdBody.md index 70a2b2e720cbd4db57155dfac153e1731ec911dc..ed1b52db2368fc0315aa62fac5da6d680ebb7556 100644 --- a/docs/meep-sss/StatusDataSubscriptionIdBody.md +++ b/docs/meep-sss/StatusDataSubscriptionIdBody.md @@ -1,4 +1,4 @@ -# StatusDataSubscriptionIdBody +# SensorDataSubscriptionIdBody ## Properties Name | Type | Description | Notes diff --git a/etsi-mec-sandbox.code-workspace b/etsi-mec-sandbox.code-workspace index 75d6051794fc49348259713b0e99321b5afaa131..06ac63daa1c2efbff4512405ab46c33b74ae5644 100644 --- a/etsi-mec-sandbox.code-workspace +++ b/etsi-mec-sandbox.code-workspace @@ -4,13 +4,13 @@ "path": "." }, { - "path": "../etsi-mec-sandbox-frontend" + "path": "../mec-sandbox-scenarios" }, { - "path": "../tmp/tools" + "path": "../etsi-mec-sandbox.TTF_T043_BugFixes" }, { - "path": "../mec-sandbox-scenarios" + "path": "../dev/stf685" } ], "extensions": { diff --git a/examples/demo1/src/demo-client/js/docs/TrackingNotification.md b/examples/demo1/src/demo-client/js/docs/TrackingNotification.md index e38c7036e989b7123e78fbb30b834c384668b4f4..ab2216e120aa1e11e2f769aca56375c98ef7f0ab 100644 --- a/examples/demo1/src/demo-client/js/docs/TrackingNotification.md +++ b/examples/demo1/src/demo-client/js/docs/TrackingNotification.md @@ -10,6 +10,6 @@ Name | Type | Description | Notes **userEventType** | [**UserEventType**](UserEventType.md) | | [optional] **currentAccessPointId** | **String** | Unique identifier of a point of access | [optional] **previousAccessPointId** | **String** | Unique identifier of a point of access | [optional] -**timestamp** | [**TimeStamp**](TimeStamp.md) | | [optional] +**timeStamp** | [**TimeStamp**](TimeStamp.md) | | [optional] diff --git a/examples/demo1/src/demo-client/js/src/ApiClient.js b/examples/demo1/src/demo-client/js/src/ApiClient.js index e0868d0320d2da6fe9505c467ede41ade8d9141a..96432ec2c2379c943ce6c1474927e65a677f7729 100644 --- a/examples/demo1/src/demo-client/js/src/ApiClient.js +++ b/examples/demo1/src/demo-client/js/src/ApiClient.js @@ -85,7 +85,7 @@ this.timeout = 60000; /** - * If set to false an additional timestamp parameter is added to all API GET calls to + * If set to false an additional timeStamp parameter is added to all API GET calls to * prevent browser caching * @type {Boolean} * @default true diff --git a/examples/demo1/src/demo-client/js/src/model/TrackingNotification.js b/examples/demo1/src/demo-client/js/src/model/TrackingNotification.js index ed724a82efa405573900aeb62e3655e64a2a2ab7..389c24e749564d82d889394f2600516302662f7b 100644 --- a/examples/demo1/src/demo-client/js/src/model/TrackingNotification.js +++ b/examples/demo1/src/demo-client/js/src/model/TrackingNotification.js @@ -85,8 +85,8 @@ obj.currentAccessPointId = ApiClient.convertToType(data['currentAccessPointId'], 'String'); if (data.hasOwnProperty('previousAccessPointId')) obj.previousAccessPointId = ApiClient.convertToType(data['previousAccessPointId'], 'String'); - if (data.hasOwnProperty('timestamp')) - obj.timestamp = TimeStamp.constructFromObject(data['timestamp']); + if (data.hasOwnProperty('timeStamp')) + obj.timeStamp = TimeStamp.constructFromObject(data['timeStamp']); } return obj; } @@ -133,9 +133,9 @@ exports.prototype.previousAccessPointId = undefined; /** - * @member {module:model/TimeStamp} timestamp + * @member {module:model/TimeStamp} timeStamp */ - exports.prototype.timestamp = undefined; + exports.prototype.timeStamp = undefined; return exports; diff --git a/examples/demo1/src/demo-client/js/test/api/NotificationsApi.spec.js b/examples/demo1/src/demo-client/js/test/api/NotificationsApi.spec.js index 5e5099457507552143d96f53de8fd9319c7b06d1..7c9456f64ab32955c36262e2a2be393c40869fc1 100644 --- a/examples/demo1/src/demo-client/js/test/api/NotificationsApi.spec.js +++ b/examples/demo1/src/demo-client/js/test/api/NotificationsApi.spec.js @@ -62,9 +62,9 @@ notification.userEventType = new MeepDemoAppApi.UserEventType(); notification.currentAccessPointId = "001010000000000000000000000000001 or poa001"; notification.previousAccessPointId = "001010000000000000000000000000001 or poa001"; - notification.timestamp = new MeepDemoAppApi.TimeStamp(); - notification.timestamp.nanoSeconds = 0; - notification.timestamp.seconds = 0; + notification.timeStamp = new MeepDemoAppApi.TimeStamp(); + notification.timeStamp.nanoSeconds = 0; + notification.timeStamp.seconds = 0; instance.postTrackingNotification(subscriptionId, notification, function(error, data, response) { if (error) { diff --git a/examples/demo1/src/demo-client/js/test/model/TrackingNotification.spec.js b/examples/demo1/src/demo-client/js/test/model/TrackingNotification.spec.js index e60f184e00ba54277ebbdbad992204529ff3b343..5ed6fc87919eb330cbba5c72075666db4bc8f753 100644 --- a/examples/demo1/src/demo-client/js/test/model/TrackingNotification.spec.js +++ b/examples/demo1/src/demo-client/js/test/model/TrackingNotification.spec.js @@ -96,10 +96,10 @@ // expect(instance.previousAccessPointId).to.be(expectedValueLiteral); }); - it('should have the property timestamp (base name: "timestamp")', function() { - // TODO: update the code to test the property timestamp - expect(instance).to.have.property('timestamp'); - // expect(instance.timestamp).to.be(expectedValueLiteral); + it('should have the property timeStamp (base name: "timeStamp")', function() { + // TODO: update the code to test the property timeStamp + expect(instance).to.have.property('timeStamp'); + // expect(instance.timeStamp).to.be(expectedValueLiteral); }); }); diff --git a/examples/demo1/src/demo-server/api/swagger.yaml b/examples/demo1/src/demo-server/api/swagger.yaml index 834bef0367969c9b58399738ccdfe387827f8f07..6a504dc5e890b6818f4592773491e45ca5ead290 100644 --- a/examples/demo1/src/demo-server/api/swagger.yaml +++ b/examples/demo1/src/demo-server/api/swagger.yaml @@ -439,7 +439,7 @@ definitions: type: "string" example: "001010000000000000000000000000001 or poa001" description: "Unique identifier of a point of access" - timestamp: + timeStamp: $ref: "#/definitions/TimeStamp" description: "Zonal or User tracking notification - callback generated toward\ \ an ME app with a zonal or user tracking subscription" diff --git a/examples/demo1/src/demo-server/go/model_tracking_notification.go b/examples/demo1/src/demo-server/go/model_tracking_notification.go index 0e0da3c384fb94a483dd46787c57e6d1e9b1c50e..70e5c272795a23463c2584794b2ceb8df1be4b97 100644 --- a/examples/demo1/src/demo-server/go/model_tracking_notification.go +++ b/examples/demo1/src/demo-server/go/model_tracking_notification.go @@ -46,5 +46,5 @@ type TrackingNotification struct { // Unique identifier of a point of access PreviousAccessPointId string `json:"previousAccessPointId,omitempty"` - Timestamp *TimeStamp `json:"timestamp,omitempty"` + Timestamp *TimeStamp `json:"timeStamp,omitempty"` } diff --git a/examples/demo1/src/demo-server/main.go b/examples/demo1/src/demo-server/main.go index c658dee2ab436b7643765bcad81cb6449b9833b5..60ad27c8bb3551b8ac930dd0715156cbb1255553 100644 --- a/examples/demo1/src/demo-server/main.go +++ b/examples/demo1/src/demo-server/main.go @@ -78,7 +78,7 @@ func main() { func registerLocServ(ue string) { locServCfg := locServClient.NewConfiguration() - locServCfg.BasePath = "http://meep-loc-serv/location/v2" + locServCfg.BasePath = "http://meep-loc-serv/location/v3" locServ := locServClient.NewAPIClient(locServCfg) log.Printf("Created Location Service client before") diff --git a/examples/demo1/src/iperf-proxy-client/js/src/ApiClient.js b/examples/demo1/src/iperf-proxy-client/js/src/ApiClient.js index da9985b71d73300e08693ebc3228685c2553f7bb..1033066c017335a13abec062094490d364d15dda 100644 --- a/examples/demo1/src/iperf-proxy-client/js/src/ApiClient.js +++ b/examples/demo1/src/iperf-proxy-client/js/src/ApiClient.js @@ -85,7 +85,7 @@ this.timeout = 60000; /** - * If set to false an additional timestamp parameter is added to all API GET calls to + * If set to false an additional timeStamp parameter is added to all API GET calls to * prevent browser caching * @type {Boolean} * @default true diff --git a/examples/demo3/src/client/src/ApiClient.js b/examples/demo3/src/client/src/ApiClient.js index 087073b717c1310e1abf86398c4c70653bccd36c..1e2b6e991c0d78a87b9e16f7612d610063b15d41 100644 --- a/examples/demo3/src/client/src/ApiClient.js +++ b/examples/demo3/src/client/src/ApiClient.js @@ -85,7 +85,7 @@ this.timeout = 60000; /** - * If set to false an additional timestamp parameter is added to all API GET calls to + * If set to false an additional timeStamp parameter is added to all API GET calls to * prevent browser caching * @type {Boolean} * @default true diff --git a/examples/demo6/golang/README.md b/examples/demo6/golang/README.md index c9cff48b430522e994931db09239b666d061f570..2f95c4a84d6a3540550a73e4e1616efab1a11057 100644 --- a/examples/demo6/golang/README.md +++ b/examples/demo6/golang/README.md @@ -11,16 +11,16 @@ There is two ways to build demo6 application: ```sh ~$ docker pull golang -~$ cd ~/AdvantEDGE/examples/demo6 -~/AdvantEDGE/examples/demo6$ docker_build.sh +~$ cd ~/etsi-mec-sandbox/examples/demo6 +~/etsi-mec-sandbox/examples/demo6$ docker_build.sh ``` - Manually: ```sh ~$ docker pull golang -~$ cd ~/AdvantEDGE/examples/demo6 -~/AdvantEDGE/examples/demo6$ docker run --rm -it -v$PWD:/opt/local/etsi/demo6 golang +~$ cd ~/etsi-mec-sandbox/examples/demo6 +~/etsi-mec-sandbox/examples/demo6$ docker run --rm -it -v$PWD:/opt/local/etsi/demo6 golang root@56c7b1ce74ca:/go# cd /opt/local/etsi/demo6 root@56c7b1ce74ca:/opt/local/etsi/demo6# go run ./main.go ``` @@ -30,8 +30,8 @@ root@56c7b1ce74ca:/opt/local/etsi/demo6# go run ./main.go The demo6 application can be executed using the script run.sh: ```sh -~$ cd ~/AdvantEDGE/examples/demo6 -~/AdvantEDGE/examples/demo6$ docker_run.sh +~$ cd ~/etsi-mec-sandbox/examples/demo6 +~/etsi-mec-sandbox/examples/demo6$ docker_run.sh ``` # Menu description diff --git a/examples/demo6/golang/app_instance.yaml b/examples/demo6/golang/app_instance.yaml index 79036f8c125c18c0964422ed7efe7fe524989b41..f60957f6a498ce153ca933e8ed1e941ea48dcac0 100644 --- a/examples/demo6/golang/app_instance.yaml +++ b/examples/demo6/golang/app_instance.yaml @@ -3,7 +3,7 @@ # Set where mec application is running either on MEC Sandbox or AdvantEDGE. Expected fields: sandbox | advantedge mode: 'sandbox' # Set MEC plateform address -sandbox: 'try-mec.etsi.org' +sandbox: 'mec-platform.etsi.org' # Set if sandbox url uses https. Expected fields: true | false https: true # Set the mec platform name demo-6 will run on. Example field: mep1 @@ -13,6 +13,6 @@ localurl: 'http://' # Set host port number of demo-6. Example field: '8093' port: '80' # Callback base URL -callbackUrl: 'http://lab-oai.etsi.org' +callbackUrl: 'http://yanngarcia.ddns.net' # Callback port for listener -callbackPort: '80' +callbackPort: '8547' diff --git a/examples/demo6/golang/docker_run.sh b/examples/demo6/golang/docker_run.sh index fd0a91bdf72a5a820f9094028b778034f8fa228a..b8f27ad9ae2c2d4a624855c9f52b098d2a8bab42 100755 --- a/examples/demo6/golang/docker_run.sh +++ b/examples/demo6/golang/docker_run.sh @@ -4,7 +4,8 @@ set -e set +x docker pull golang -docker run --rm --expose 80 --expose 443 --publish 80:80 --publish 443:443 -it -v$PWD:/opt/local/etsi/demo6 -v$HOME/var:/opt/local/etsi/var golang bash -c "cd /opt/local/etsi/demo6/bin && ./demo6 ../app_instance.yaml" +#docker run --rm --expose 80 --expose 443 --publish 80:80 --publish 443:443 -it -v$PWD:/opt/local/etsi/demo6 -v$HOME/var:/opt/local/etsi/var golang bash -c "cd /opt/local/etsi/demo6/bin && ./demo6 ../app_instance.yaml" +docker run --rm -it -v$PWD:/opt/local/etsi/demo6 -v$HOME/var:/opt/local/etsi/var golang bash -c "cd /opt/local/etsi/demo6/bin && ./demo6 ../app_instance.yaml" echo "" echo ">>> Done" diff --git a/examples/demo6/golang/main.go b/examples/demo6/golang/main.go index 5f1af3b19d1a33ed0f25532d63f01f091062a254..6e43abef23d566880eb18186cef022157e75729e 100644 --- a/examples/demo6/golang/main.go +++ b/examples/demo6/golang/main.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 The AdvantEDGE Authors + * Copyright (c) 2024-2025 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. @@ -335,10 +335,26 @@ const ( QUIT = "q" ) +/** + * @brief Clears the terminal screen + * + * This function clears the terminal screen by printing the ANSI escape sequence + * for clearing the screen (\033[2J). + */ func clearScreen() { fmt.Println("\033[2J") } +/** + * @brief Displays the main menu and reads user selection + * + * This function clears the screen and displays a comprehensive menu of available + * commands for the MEC Demo6 application. It includes commands for sandbox management, + * MEC services, UE management, and various MEC API operations. + * + * @param message Optional message to display at the bottom of the menu + * @return []string Array containing the user's command choice and any parameters + */ func menu(message string) []string { clearScreen() fmt.Printf( @@ -378,6 +394,17 @@ func menu(message string) []string { return strings.Split(choice, " ") } +/** + * @brief Performs login to the MEC sandbox system + * + * This function authenticates with the MEC sandbox system using the configured provider. + * It initializes global variables and returns the user code and verification URI + * needed for the authorization process. + * + * @return string User code for authorization + * @return string Verification URI for authorization + * @return error Error if login fails or if already logged in + */ func login() (string, string, error) { fmt.Println(">>> login") @@ -386,7 +413,7 @@ func login() (string, string, error) { return "", "", errors.New("Please, logout first") } - // Initialize g;lobal variables + // Initialize global variables scenarioId = -1 appsInfo.Id = "" terminationSubscriptionID = "" @@ -402,6 +429,15 @@ func login() (string, string, error) { return sandbox.User_code, sandbox.Verification_uri, nil } +/** + * @brief Retrieves the namespace for the current user session + * + * This function calls the MEC sandbox API to get the namespace associated + * with the current user code and stores the sandbox name globally. + * + * @return string The sandbox name/namespace + * @return error Error if the namespace retrieval fails + */ func getNamespace() (string, error) { fmt.Println(">>> Get Namespace") @@ -415,6 +451,20 @@ func getNamespace() (string, error) { return sandboxName, nil } +/** + * @brief Performs logout from the MEC sandbox system + * + * This function performs a complete cleanup of the current session by: + * - Deleting all active subscriptions + * - Deleting termination subscriptions + * - Deleting MEC services + * - Deregistering the application + * - Deleting application instances + * - Terminating active scenarios + * - Logging out from the sandbox + * + * @return error Error if logout fails or if no active session exists + */ func logout() error { fmt.Println(">>> logout: ", sandboxName) @@ -476,6 +526,15 @@ func logout() error { return nil } +/** + * @brief Retrieves the list of available network scenarios + * + * This function calls the MEC sandbox API to get all available network scenarios + * that can be activated in the current sandbox environment. + * + * @return []client.SandboxNetworkScenario Array of available network scenarios + * @return error Error if the scenario list retrieval fails + */ func getListOfScenarios() ([]client.SandboxNetworkScenario, error) { fmt.Println(">>> getListOfScenarios") @@ -489,6 +548,16 @@ func getListOfScenarios() ([]client.SandboxNetworkScenario, error) { return scenarios, nil } +/** + * @brief Retrieves detailed information about a specific network scenario + * + * This function calls the MEC sandbox API to get detailed information about + * a specific network scenario identified by its ID. + * + * @param scenarioId The ID of the scenario to retrieve + * @return []client.Scenario Array containing the scenario details + * @return error Error if the scenario retrieval fails + */ func getScenario(scenarioId string) ([]client.Scenario, error) { fmt.Println(">>> getScenario: ", scenarioId) @@ -503,6 +572,15 @@ func getScenario(scenarioId string) ([]client.Scenario, error) { return scenario, nil } +/** + * @brief Activates a network scenario in the MEC sandbox + * + * This function activates a specific network scenario in the current sandbox + * environment. The scenario must be available in the sandbox. + * + * @param scenarioId The ID of the scenario to activate + * @return error Error if scenario activation fails or if no sandbox is available + */ func activateScenario(scenarioId string) error { fmt.Println(">>> activateScenario: ", scenarioId) @@ -519,6 +597,15 @@ func activateScenario(scenarioId string) error { return nil } +/** + * @brief Terminates an active network scenario in the MEC sandbox + * + * This function terminates a currently active network scenario in the sandbox + * environment. Both sandbox and scenario must be available. + * + * @param scenarioId The ID of the scenario to terminate + * @return error Error if scenario termination fails or if prerequisites are not met + */ func terminateScenario(scenarioId string) error { fmt.Println(">>> terminateScenario: ", scenarioId) @@ -537,6 +624,16 @@ func terminateScenario(scenarioId string) error { return nil } +/** + * @brief Retrieves the list of available MEC services in the sandbox + * + * This function calls the MEC sandbox API to get all available MEC services + * that can be used in the current sandbox environment. Requires an active + * sandbox and network scenario. + * + * @return []client.SandboxMecServices Array of available MEC services + * @return error Error if service list retrieval fails or prerequisites not met + */ func getListOfMECServices() ([]client.SandboxMecServices, error) { fmt.Println(">>> getListOfMECServices") @@ -555,6 +652,16 @@ func getListOfMECServices() ([]client.SandboxMecServices, error) { return services, nil } +/** + * @brief Retrieves the list of MEC application instances in the sandbox + * + * This function calls the MEC sandbox API to get all application instances + * that are currently running in the sandbox environment. Requires an active + * sandbox and network scenario. + * + * @return []client.ApplicationInfo Array of application instance information + * @return error Error if application list retrieval fails or prerequisites not met + */ func getListOfMECAppInstIds() ([]client.ApplicationInfo, error) { fmt.Println(">>> getListOfMECAppInstIds") @@ -573,6 +680,16 @@ func getListOfMECAppInstIds() ([]client.ApplicationInfo, error) { return appsInfos, nil } +/** + * @brief Creates a new MEC application instance in the sandbox + * + * This function creates a new application instance in the MEC sandbox environment + * using the provided application information. Requires an active sandbox and + * network scenario. + * + * @param appInfo The application information for the new instance + * @return error Error if application creation fails or prerequisites not met + */ func createMECAppInstId(appInfo client.ApplicationInfo) error { fmt.Println(">>> createMECAppInstId: ", appInfo) @@ -591,6 +708,15 @@ func createMECAppInstId(appInfo client.ApplicationInfo) error { return nil } +/** + * @brief Deletes the current MEC application instance from the sandbox + * + * This function deletes the currently active application instance from the + * MEC sandbox environment. Requires an active sandbox, network scenario, + * and application instance. + * + * @return error Error if application deletion fails or prerequisites not met + */ func deleteMECAppInstId() error { fmt.Println(">>> deleteMECAppInstId") @@ -612,6 +738,17 @@ func deleteMECAppInstId() error { return nil } +/** + * @brief Validates an index against a maximum length + * + * This function converts a string choice to an integer index and validates + * that it is within the valid range (0 to len-1). + * + * @param choice The string representation of the index + * @param len The maximum length/upper bound for validation + * @return int The validated integer index + * @return error Error if conversion fails or index is out of range + */ func verify_idx_len(choice string, len int) (int, error) { idx, err := strconv.Atoi(choice) if err != nil { @@ -624,6 +761,16 @@ func verify_idx_len(choice string, len int) (int, error) { return idx, nil } +/** + * @brief Retrieves the list of User Equipment (UE) in the sandbox + * + * This function calls the MEC sandbox API to get all User Equipment instances + * that are currently available in the sandbox environment. Requires an active + * sandbox and network scenario. + * + * @return []UeContext Array of UE context information + * @return error Error if UE list retrieval fails or prerequisites not met + */ func getListOfUes() (ues []UeContext, err error) { fmt.Println(">>> getListOfUes") @@ -647,6 +794,16 @@ func getListOfUes() (ues []UeContext, err error) { return ues, nil } +/** + * @brief Increases the count of a specific UE type + * + * This function increases the count of a specific User Equipment type in the + * sandbox environment. The maximum count is limited to 4. Requires an active + * sandbox and network scenario. + * + * @param idx The index of the UE type to increase + * @return error Error if UE increase fails, prerequisites not met, or maximum reached + */ func increaseUE(idx int) error { fmt.Println(">>> increaseUE: idx=", idx) fmt.Println(">>> increaseUE: ues=", ues[idx]) @@ -670,6 +827,16 @@ func increaseUE(idx int) error { return nil } +/** + * @brief Decreases the count of a specific UE type + * + * This function decreases the count of a specific User Equipment type in the + * sandbox environment. The minimum count is 0. Requires an active sandbox + * and network scenario. + * + * @param idx The index of the UE type to decrease + * @return error Error if UE decrease fails, prerequisites not met, or minimum reached + */ func decreaseUE(idx int) error { fmt.Println(">>> decreaseUE: idx=", idx) fmt.Println(">>> decreaseUE: ues=", ues[idx]) @@ -693,6 +860,18 @@ func decreaseUE(idx int) error { return nil } +/** + * @brief Sends MEC 011 Confirm Ready notification to the MEC platform + * + * This function implements the MEC 011 Application Support API Confirm Ready operation. + * It sends a READY indication to the MEC platform and creates a termination subscription. + * + * Reference: ETSI GS MEC 011 V3.2.1 (2024-04) Clause 7.1.2.4 + * + * @return string Subscription ID for termination notifications + * @return *http.Response HTTP response from the MEC platform + * @return error Error if the operation fails or prerequisites are not met + */ func mec011_send_confirm_ready() (subId string, response *http.Response, err error) { fmt.Println(">>> mec011_send_confirm_ready") @@ -721,6 +900,19 @@ func mec011_send_confirm_ready() (subId string, response *http.Response, err err return mec011_send_subscribe_termination() } +/** + * @brief Creates a termination subscription for MEC 011 application lifecycle management + * + * This function implements the MEC 011 Application Support API subscription creation + * for application termination notifications. It creates a subscription to receive + * notifications when the application is about to be terminated. + * + * Reference: ETSI GS MEC 011 V3.2.1 (2024-04) Clause 7.1.3.2 + * + * @return string Subscription ID for the termination subscription + * @return *http.Response HTTP response from the MEC platform + * @return error Error if subscription creation fails or prerequisites are not met + */ func mec011_send_subscribe_termination() (subId string, response *http.Response, err error) { fmt.Println(">>> mec011_send_subscribe_termination") @@ -763,6 +955,16 @@ func mec011_send_subscribe_termination() (subId string, response *http.Response, return subId, response, nil } +/** + * @brief Deletes the termination subscription for MEC 011 application lifecycle management + * + * This function deletes the previously created termination subscription to stop + * receiving application termination notifications from the MEC platform. + * + * Reference: ETSI GS MEC 011 V3.2.1 (2024-04) Clause 7.1.3.2 + * + * @return error Error if subscription deletion fails or prerequisites are not met + */ func delete_termination_subscription() (err error) { fmt.Println(">>> delete_termination_subscription") @@ -792,6 +994,19 @@ func delete_termination_subscription() (err error) { return nil } +/** + * @brief Registers the MEC application with the MEC platform using MEC 011 + * + * This function implements the MEC 011 Application Support API registration operation. + * It registers the application instance with the MEC platform, providing application + * information including name, provider, and instance ID. + * + * Reference: ETSI GS MEC 011 V3.2.1 (2024-04) Clause 7.1.2.1 + * + * @return []byte Response body from the MEC platform + * @return *http.Response HTTP response from the MEC platform + * @return error Error if registration fails or prerequisites are not met + */ func mec011_send_registration() (body []byte, response *http.Response, err error) { fmt.Println(">>> mec011_send_registration: ", appsInfo.Name) fmt.Println(">>> mec011_send_registration: ", appsInfo.Id) @@ -837,6 +1052,17 @@ func mec011_send_registration() (body []byte, response *http.Response, err error return body, response, nil } +/** + * @brief Deregisters the MEC application from the MEC platform using MEC 011 + * + * This function implements the MEC 011 Application Support API deregistration operation. + * It removes the application instance registration from the MEC platform. + * + * Reference: ETSI GS MEC 011 V3.2.1 (2024-04) Clause 7.1.2.2 + * + * @return *http.Response HTTP response from the MEC platform + * @return error Error if deregistration fails or prerequisites are not met + */ func mec011_send_deregistration() (response *http.Response, err error) { fmt.Println(">>> mec011_send_deregistration") @@ -864,6 +1090,19 @@ func mec011_send_deregistration() (response *http.Response, err error) { return response, nil } +/** + * @brief Creates a new MEC service using MEC 011 Service Management API + * + * This function implements the MEC 011 Service Management API service creation operation. + * It creates a new service instance for the registered application, providing service + * information including name, category, transport details, and endpoints. + * + * Reference: ETSI GS MEC 011 V3.2.1 (2024-04) Clause 8.1.2.1 + * + * @return string Resource ID of the created service + * @return *http.Response HTTP response from the MEC platform + * @return error Error if service creation fails or prerequisites are not met + */ func mec011_create_service() (resId string, response *http.Response, err error) { fmt.Println(">>> mec011_create_service") @@ -937,6 +1176,16 @@ func mec011_create_service() (resId string, response *http.Response, err error) return resId, response, nil } +/** + * @brief Deletes a MEC service using MEC 011 Service Management API + * + * This function implements the MEC 011 Service Management API service deletion operation. + * It removes the previously created service instance from the MEC platform. + * + * Reference: ETSI GS MEC 011 V3.2.1 (2024-04) Clause 8.1.2.2 + * + * @return error Error if service deletion fails or prerequisites are not met + */ func mec011_delete_service() (err error) { fmt.Println(">>> mec011_delete_service") @@ -965,6 +1214,18 @@ func mec011_delete_service() (err error) { return nil } +/** + * @brief Retrieves available MEC services using MEC 011 Service Management API + * + * This function implements the MEC 011 Service Management API service discovery operation. + * It retrieves a list of all available MEC services that can be consumed by the application. + * + * Reference: ETSI GS MEC 011 V3.2.1 (2024-04) Clause 8.1.2.3 + * + * @return []byte Response body containing the list of available services + * @return *http.Response HTTP response from the MEC platform + * @return error Error if service discovery fails or prerequisites are not met + */ func mec011_get_mec_services() (body []byte, response *http.Response, err error) { fmt.Println(">>> mec011_get_mec_services") @@ -990,6 +1251,23 @@ func mec011_get_mec_services() (body []byte, response *http.Response, err error) return body, nil, nil } +/** + * @brief Sends HTTP requests to MEC services with proper authentication and headers + * + * This utility function handles HTTP communication with MEC services, including + * request preparation, authentication, and response handling. It supports various + * HTTP methods and query parameters. + * + * @param method HTTP method (GET, POST, DELETE, etc.) + * @param path URL path for the request + * @param body Request body for POST/PUT operations + * @param vars URL variables for path substitution + * @param queryParams Query parameters to append to the URL + * @param location Optional location header for redirects + * @return []byte Response body from the service + * @return *http.Response Complete HTTP response + * @return error Error if the request fails or prerequisites are not met + */ func send_mec_service_request(method string, path string, body io.Reader, vars url.Values, queryParams url.Values, location *string) (resbody []byte, res *http.Response, err error) { fmt.Println(">>> send_mec_service_request: ", appsInfo.Name) @@ -1062,6 +1340,20 @@ func send_mec_service_request(method string, path string, body io.Reader, vars u return resbody, res, err } +/** + * @brief Retrieves UE location information using MEC 013 Location API + * + * This function implements the MEC 013 Location API user location query operation. + * It retrieves the current location information for a specific UE identified by + * its address. + * + * Reference: ETSI GS MEC 013 V3.2.1 (2024-04) Clause 6.1.2.1 + * + * @param ue_address The address of the UE to query + * @return []byte Response body containing UE location information + * @return *http.Response HTTP response from the MEC platform + * @return error Error if location query fails or prerequisites are not met + */ func mec013_get_ue_loc(ue_address string) (body []byte, response *http.Response, err error) { fmt.Println(">>> mec013_get_ue_loc: ", ue_address) @@ -1085,6 +1377,20 @@ func mec013_get_ue_loc(ue_address string) (body []byte, response *http.Response, return body, response, nil } +/** + * @brief Creates a UE location subscription using MEC 013 Location API + * + * This function implements the MEC 013 Location API location event subscription operation. + * It creates a subscription to receive location event notifications for a specific UE, + * including entering and leaving area events. + * + * Reference: ETSI GS MEC 013 V3.2.1 (2024-04) Clause 6.1.3.1 + * + * @param ue_address The address of the UE to monitor + * @return []byte Response body containing subscription information + * @return *http.Response HTTP response from the MEC platform + * @return error Error if subscription creation fails or prerequisites are not met + */ func mec013_subscribe_ue_loc(ue_address string) (body []byte, response *http.Response, err error) { fmt.Println(">>> mec013_subscribe_ue_loc") @@ -1124,7 +1430,7 @@ func mec013_subscribe_ue_loc(ue_address string) (body []byte, response *http.Res } defer response.Body.Close() - fmt.Println("mec013_subscribe_ue_loc: body: " + string(body)) + fmt.Println("mec013_subscribe_ue_loc: body: " + string(body)) var r = map[string]UserLocationEventSubscription{} err = json.Unmarshal([]byte(body), &r) if err != nil { @@ -1141,6 +1447,19 @@ func mec013_subscribe_ue_loc(ue_address string) (body []byte, response *http.Res return body, response, nil } +/** + * @brief Deletes a UE location subscription using MEC 013 Location API + * + * This function implements the MEC 013 Location API subscription deletion operation. + * It removes a previously created location event subscription to stop receiving + * location notifications for a specific UE. + * + * Reference: ETSI GS MEC 013 V3.2.1 (2024-04) Clause 6.1.3.2 + * + * @param choice Subscription ID to delete + * @return *http.Response HTTP response from the MEC platform + * @return error Error if subscription deletion fails or prerequisites are not met + */ func mec013_delete_ue_loc_subscription(choice string) (response *http.Response, err error) { fmt.Println(">>> mec013_delete_ue_loc_subscription: ", choice) @@ -1165,6 +1484,19 @@ func mec013_delete_ue_loc_subscription(choice string) (response *http.Response, return response, nil } +/** + * @brief Retrieves V2X UU unicast provisioning information using MEC 030 V2X API + * + * This function implements the MEC 030 V2X API UU unicast provisioning query operation. + * It retrieves provisioning information for V2X UU unicast communication at a specific + * location identified by ECGI. + * + * Reference: ETSI GS MEC 030 V3.2.1 (2024-04) Clause 6.5.1 + * + * @return []byte Response body containing V2X UU unicast provisioning information + * @return *http.Response HTTP response from the MEC platform + * @return error Error if query fails or prerequisites are not met + */ func mec030_get_v2x_uu_unicast_setting() (body []byte, response *http.Response, err error) { fmt.Println(">>> mec030_get_v2x_uu_unicast_setting") @@ -1186,6 +1518,19 @@ func mec030_get_v2x_uu_unicast_setting() (body []byte, response *http.Response, return body, response, nil } +/** + * @brief Creates a V2X message subscription using MEC 030 V2X API + * + * This function implements the MEC 030 V2X API V2X message subscription operation. + * It creates a subscription to receive V2X messages (CAM, DENM, CPM, VAM) from + * the MEC platform based on specified filter criteria. + * + * Reference: ETSI GS MEC 030 V3.2.1 (2024-04) Clause 6.3.5 + * + * @return []byte Response body containing subscription information + * @return *http.Response HTTP response from the MEC platform + * @return error Error if subscription creation fails or prerequisites are not met + */ func mec030_subscribe_v2x_messages() (body []byte, response *http.Response, err error) { fmt.Println(">>> mec030_subscribe_v2x_messages") @@ -1229,6 +1574,19 @@ func mec030_subscribe_v2x_messages() (body []byte, response *http.Response, err return body, response, nil } +/** + * @brief Deletes a V2X message subscription using MEC 030 V2X API + * + * This function implements the MEC 030 V2X API subscription deletion operation. + * It removes a previously created V2X message subscription to stop receiving + * V2X messages from the MEC platform. + * + * Reference: ETSI GS MEC 030 V3.2.1 (2024-04) Clause 6.3.6 + * + * @param choice Subscription ID to delete + * @return *http.Response HTTP response from the MEC platform + * @return error Error if subscription deletion fails or prerequisites are not met + */ func mec030_delete_v2x_messages_subscription(choice string) (response *http.Response, err error) { fmt.Println(">>> mec030_delete_v2x_messages_subscription: ", choice) @@ -1256,6 +1614,22 @@ func mec030_delete_v2x_messages_subscription(choice string) (response *http.Resp // https://www.calculatorsoup.com/calculators/conversions/convert-decimal-degrees-to-degrees-minutes-seconds.php // https://www.latlong.net/degrees-minutes-seconds-to-decimal-degrees +/** + * @brief Provides predicted QoS information using MEC 030 V2X API + * + * This function implements the MEC 030 V2X API predicted QoS provision operation. + * It provides predicted QoS information for a specific route defined by latitude, + * longitude coordinates and timestamps. + * + * Reference: ETSI GS MEC 030 V3.2.1 (2024-04) Clause 6.5.15 + * + * @param latitudes Comma-separated list of latitude values + * @param longitudes Comma-separated list of longitude values + * @param timestamps Comma-separated list of timestamp values + * @return []byte Response body containing predicted QoS information + * @return *http.Response HTTP response from the MEC platform + * @return error Error if QoS provision fails or prerequisites are not met + */ func mec030_predicted_qos(latitudes string, longitudes string, timestamps string) (body []byte, response *http.Response, err error) { // Sanity checks if sandboxName == "" { @@ -1327,6 +1701,19 @@ func mec030_predicted_qos(latitudes string, longitudes string, timestamps string return body, response, nil } +/** + * @brief Retrieves the list of federation systems using MEC 040 Federation API + * + * This function implements the MEC 040 Federation API system discovery operation. + * It retrieves a list of all available federation systems that can be accessed + * through the MEC platform. + * + * Reference: ETSI GS MEC 040 V3.2.1 (2024-04) Clause 6.1.2.1 + * + * @return []byte Response body containing the list of federation systems + * @return *http.Response HTTP response from the MEC platform + * @return error Error if system discovery fails or prerequisites are not met + */ func mec40_get_systems_list() (body []byte, response *http.Response, err error) { fmt.Println(">>> mec40_get_systems_list") @@ -1350,6 +1737,20 @@ func mec40_get_systems_list() (body []byte, response *http.Response, err error) return body, response, nil } +/** + * @brief Retrieves the list of services for a specific federation system using MEC 040 + * + * This function implements the MEC 040 Federation API service discovery operation. + * It retrieves a list of all available services provided by a specific federation + * system. + * + * Reference: ETSI GS MEC 040 V3.2.1 (2024-04) Clause 6.1.2.2 + * + * @param choice System ID to query for services + * @return []byte Response body containing the list of federation services + * @return *http.Response HTTP response from the MEC platform + * @return error Error if service discovery fails or prerequisites are not met + */ func mec40_get_services_list(choice string) (body []byte, response *http.Response, err error) { fmt.Println(">>> mec40_get_services_list") @@ -1375,6 +1776,21 @@ func mec40_get_services_list(choice string) (body []byte, response *http.Respons return body, response, nil } +/** + * @brief Retrieves detailed information about a specific federation service using MEC 040 + * + * This function implements the MEC 040 Federation API individual service query operation. + * It retrieves detailed information about a specific service provided by a federation + * system. + * + * Reference: ETSI GS MEC 040 V3.2.1 (2024-04) Clause 6.1.2.3 + * + * @param systemId The ID of the federation system + * @param serviceId The ID of the service to query + * @return []byte Response body containing detailed service information + * @return *http.Response HTTP response from the MEC platform + * @return error Error if service query fails or prerequisites are not met + */ func mec40_get_service_list(systemId string, serviceId string) (body []byte, response *http.Response, err error) { fmt.Println(">>> mec40_get_service_list") @@ -1400,6 +1816,19 @@ func mec40_get_service_list(systemId string, serviceId string) (body []byte, res return body, response, nil } +/** + * @brief Creates a system update notification subscription using MEC 040 Federation API + * + * This function implements the MEC 040 Federation API subscription creation operation. + * It creates a subscription to receive notifications about updates to federation + * systems and their services. + * + * Reference: ETSI GS MEC 040 V3.2.1 (2024-04) Clause 6.1.3.1 + * + * @return []byte Response body containing subscription information + * @return *http.Response HTTP response from the MEC platform + * @return error Error if subscription creation fails or prerequisites are not met + */ func mec40_create_subscription() (body []byte, response *http.Response, err error) { fmt.Println(">>> mec40_create_subscription") @@ -1439,6 +1868,20 @@ func mec40_create_subscription() (body []byte, response *http.Response, err erro return body, response, nil } +/** + * @brief Retrieves federation subscription information using MEC 040 Federation API + * + * This function implements the MEC 040 Federation API subscription query operation. + * It retrieves information about federation subscriptions, either all subscriptions + * or a specific subscription by ID. + * + * Reference: ETSI GS MEC 040 V3.2.1 (2024-04) Clause 6.1.3.2 + * + * @param choice Array containing optional subscription ID for individual query + * @return []byte Response body containing subscription information + * @return *http.Response HTTP response from the MEC platform + * @return error Error if subscription query fails or prerequisites are not met + */ func mec40_get_subscriptions(choice []string) (body []byte, response *http.Response, err error) { fmt.Println(">>> mec40_get_subscriptions: ", choice) @@ -1465,6 +1908,19 @@ func mec40_get_subscriptions(choice []string) (body []byte, response *http.Respo return body, response, nil } +/** + * @brief Deletes a federation subscription using MEC 040 Federation API + * + * This function implements the MEC 040 Federation API subscription deletion operation. + * It removes a previously created federation subscription to stop receiving + * system update notifications. + * + * Reference: ETSI GS MEC 040 V3.2.1 (2024-04) Clause 6.1.3.3 + * + * @param choice Subscription ID to delete + * @return *http.Response HTTP response from the MEC platform + * @return error Error if subscription deletion fails or prerequisites are not met + */ func mec40_delete_subscriptions(choice string) (response *http.Response, err error) { fmt.Println(">>> mec40_delete_subscriptions: ", choice) @@ -1490,6 +1946,19 @@ func mec40_delete_subscriptions(choice string) (response *http.Response, err err return response, nil } +/** + * @brief Retrieves all available CAPIF services + * + * This function implements the CAPIF API service discovery operation. + * It retrieves a list of all available service APIs that can be consumed + * through the CAPIF interface. + * + * Reference: ETSI TS 103 384 V3.2.1 (2024-04) Clause 6.1.2.1 + * + * @return []byte Response body containing the list of available CAPIF services + * @return *http.Response HTTP response from the CAPIF platform + * @return error Error if service discovery fails or prerequisites are not met + */ func capif_get_all_svcs() (body []byte, response *http.Response, err error) { fmt.Println(">>> capif_get_all_svcs") @@ -1513,6 +1982,19 @@ func capif_get_all_svcs() (body []byte, response *http.Response, err error) { return body, response, nil } +/** + * @brief Retrieves CAPIF services for the current application instance + * + * This function implements the CAPIF API published service query operation. + * It retrieves a list of service APIs that are published by the current + * application instance. + * + * Reference: ETSI TS 103 384 V3.2.1 (2024-04) Clause 6.1.2.2 + * + * @return []byte Response body containing the list of published services + * @return *http.Response HTTP response from the CAPIF platform + * @return error Error if service query fails or prerequisites are not met + */ func capif_get_svc() (body []byte, response *http.Response, err error) { fmt.Println(">>> capif_get_svc") @@ -1538,6 +2020,18 @@ func capif_get_svc() (body []byte, response *http.Response, err error) { return body, response, nil } +/** + * @brief Creates a new CAPIF service (Not implemented) + * + * This function is a placeholder for the CAPIF API service creation operation. + * Currently not implemented in this demo application. + * + * Reference: ETSI TS 103 384 V3.2.1 (2024-04) Clause 6.1.2.3 + * + * @return []byte Response body (not implemented) + * @return *http.Response HTTP response (not implemented) + * @return error Error indicating not implemented + */ func capif_create_svc() (body []byte, response *http.Response, err error) { fmt.Println(">>> capif_create_svc") @@ -1553,6 +2047,18 @@ func capif_create_svc() (body []byte, response *http.Response, err error) { return nil, nil, errors.New("Not implemented") } +/** + * @brief Deletes a CAPIF service (Not implemented) + * + * This function is a placeholder for the CAPIF API service deletion operation. + * Currently not implemented in this demo application. + * + * Reference: ETSI TS 103 384 V3.2.1 (2024-04) Clause 6.1.2.4 + * + * @param choice Service ID to delete + * @return *http.Response HTTP response (not implemented) + * @return error Error indicating not implemented + */ func capif_delete_svc(choice string) (response *http.Response, err error) { fmt.Println(">>> capif_delete_svc: ", choice) @@ -1571,6 +2077,18 @@ func capif_delete_svc(choice string) (response *http.Response, err error) { return nil, errors.New("Not implemented") } +/** + * @brief Creates a CAPIF subscription (Not implemented) + * + * This function is a placeholder for the CAPIF API subscription creation operation. + * Currently not implemented in this demo application. + * + * Reference: ETSI TS 103 384 V3.2.1 (2024-04) Clause 6.1.3.1 + * + * @return []byte Response body (not implemented) + * @return *http.Response HTTP response (not implemented) + * @return error Error indicating not implemented + */ func capif_create_subscription() (body []byte, response *http.Response, err error) { fmt.Println(">>> capif_create_subscription") @@ -1590,6 +2108,18 @@ func capif_create_subscription() (body []byte, response *http.Response, err erro return nil, nil, errors.New("Not implemented") } +/** + * @brief Retrieves CAPIF subscriptions (Not implemented) + * + * This function is a placeholder for the CAPIF API subscription query operation. + * Currently not implemented in this demo application. + * + * Reference: ETSI TS 103 384 V3.2.1 (2024-04) Clause 6.1.3.2 + * + * @return []byte Response body (not implemented) + * @return *http.Response HTTP response (not implemented) + * @return error Error indicating not implemented + */ func capif_get_subscriptions() (body []byte, response *http.Response, err error) { fmt.Println(">>> capif_get_subscriptions") @@ -1605,6 +2135,18 @@ func capif_get_subscriptions() (body []byte, response *http.Response, err error) return nil, nil, errors.New("Not implemented") } +/** + * @brief Deletes a CAPIF subscription (Not implemented) + * + * This function is a placeholder for the CAPIF API subscription deletion operation. + * Currently not implemented in this demo application. + * + * Reference: ETSI TS 103 384 V3.2.1 (2024-04) Clause 6.1.3.3 + * + * @param choice Subscription ID to delete + * @return *http.Response HTTP response (not implemented) + * @return error Error indicating not implemented + */ func capif_delete_subscriptions(choice string) (response *http.Response, err error) { fmt.Println(">>> capif_delete_subscriptions: ", choice) @@ -1623,6 +2165,15 @@ func capif_delete_subscriptions(choice string) (response *http.Response, err err return nil, errors.New("Not implemented") } +/** + * @brief Returns the current status of the MEC application + * + * This utility function provides a comprehensive status overview of the current + * MEC application state, including sandbox information, application registration, + * service creation, and active subscriptions. + * + * @return string Formatted status string containing current application state + */ func app_status() (resp string) { resp = "" if sandboxName != "" { @@ -1652,11 +2203,25 @@ func app_status() (resp string) { return resp } +/** + * @brief Extracts subscription ID from a subscription URL + * + * This utility function parses a subscription URL to extract the subscription ID + * using regular expressions. It matches the subscription ID pattern from the URL + * and returns the extracted identifier. + * + * @param base_url The base URL for the subscription endpoint + * @param subscription_url The complete subscription URL containing the ID + * @return string The extracted subscription ID + * @return error Error if URL parsing or regex matching fails + */ func extract_subscription_id(base_url string, subscription_url string) (string, error) { fmt.Println(">>> extract_subscription_id: base_url: " + base_url) fmt.Println(">>> extract_subscription_id: subscription_url: " + subscription_url) - re := regexp.MustCompile(base_url + "/(?P.+)") + u, _ := url.Parse(base_url) + fmt.Println("extract_subscription_id: subscription_url: " + u.RequestURI()) + re := regexp.MustCompile(u.RequestURI() + "/(?P.+)") if re == nil { return "", errors.New("Regexp creation failure") } @@ -1668,6 +2233,16 @@ func extract_subscription_id(base_url string, subscription_url string) (string, return matches[re.SubexpIndex("sub_id")], nil } +/** + * @brief Deletes a subscription by URL + * + * This utility function deletes a subscription by sending a DELETE request to + * the specified URL and removes the subscription from the local tracking list. + * + * @param url The URL of the subscription to delete + * @return *http.Response HTTP response from the deletion operation + * @return error Error if deletion fails + */ func delete_subscription(url string) (response *http.Response, err error) { fmt.Println(">>> delete_subscription: ", url) @@ -1682,6 +2257,15 @@ func delete_subscription(url string) (response *http.Response, err error) { return response, nil } +/** + * @brief Removes a subscription from the local tracking list + * + * This utility function removes a subscription from the internal subscriptions + * array based on the provided URL. It searches for the subscription and removes + * it from the list. + * + * @param url The URL of the subscription to remove from the list + */ func delete_subscription_from_list(url string) { fmt.Println(">>> delete_subscription: ", url) for s, l := range subscriptions { @@ -1692,31 +2276,107 @@ func delete_subscription_from_list(url string) { } } +/** + * @brief Handles MEC 013 location notification callbacks + * + * This function serves as the HTTP handler for MEC 013 location event notifications. + * It receives location event notifications from the MEC platform and responds + * with HTTP 200 OK. + * + * Reference: ETSI GS MEC 013 V3.2.1 (2024-04) Clause 6.1.3.1 + * + * @param w HTTP response writer + * @param r HTTP request containing the location notification + */ func mec013_notification(w http.ResponseWriter, r *http.Request) { fmt.Println(">>> mec013_notification: ", r) w.WriteHeader(http.StatusOK) } +/** + * @brief Handles MEC 030 V2X message notification callbacks + * + * This function serves as the HTTP handler for MEC 030 V2X message notifications. + * It receives V2X message notifications from the MEC platform and responds + * with HTTP 200 OK. + * + * Reference: ETSI GS MEC 030 V3.2.1 (2024-04) Clause 6.3.5 + * + * @param w HTTP response writer + * @param r HTTP request containing the V2X message notification + */ func v2x_msg_notification(w http.ResponseWriter, r *http.Request) { fmt.Println(">>> v2x_msg_notification: ", r) w.WriteHeader(http.StatusOK) } +/** + * @brief Handles MEC 040 federation notification callbacks + * + * This function serves as the HTTP handler for MEC 040 federation system update + * notifications. It receives federation notifications from the MEC platform + * and responds with HTTP 200 OK. + * + * Reference: ETSI GS MEC 040 V3.2.1 (2024-04) Clause 6.1.3.1 + * + * @param w HTTP response writer + * @param r HTTP request containing the federation notification + */ func fed_notification(w http.ResponseWriter, r *http.Request) { fmt.Println(">>> fed_notification: ", r) w.WriteHeader(http.StatusOK) } +/** + * @brief Handles CAPIF notification callbacks + * + * This function serves as the HTTP handler for CAPIF service notifications. + * It receives service notifications from the CAPIF platform and responds + * with HTTP 200 OK. + * + * Reference: ETSI TS 103 384 V3.2.1 (2024-04) Clause 6.1.3.1 + * + * @param w HTTP response writer + * @param r HTTP request containing the CAPIF notification + */ func capif_notification(w http.ResponseWriter, r *http.Request) { fmt.Println(">>> capif_notification: ", r) w.WriteHeader(http.StatusOK) } +/** + * @brief Handles MEC 011 service statistic requests + * + * This function serves as the HTTP handler for MEC 011 service statistic requests. + * It provides service statistics information when requested by the MEC platform + * and responds with HTTP 200 OK. + * + * Reference: ETSI GS MEC 011 V3.2.1 (2024-04) Clause 8.1.2.1 + * + * @param w HTTP response writer + * @param r HTTP request for service statistics + */ func mec011_service_statistic_get(w http.ResponseWriter, r *http.Request) { fmt.Println(">>> mec011_service_statistic_get: ", r) w.WriteHeader(http.StatusOK) } +/** + * @brief Main entry point for the MEC Demo6 application + * + * This function initializes the MEC Demo6 application, loads configuration, + * sets up HTTP servers for callbacks, and starts the interactive menu system. + * It handles graceful shutdown and cleanup of resources. + * + * The application demonstrates the usage of various MEC APIs including: + * - MEC 011 Application Support and Service Management + * - MEC 013 Location Services + * - MEC 030 V2X Services + * - MEC 040 Federation Services + * - CAPIF Service APIs + * + * @return void + */ func main() { if len(os.Args) < 2 { // no config argument @@ -1841,6 +2501,18 @@ func main() { } } +/** + * @brief Loads configuration from YAML file using Viper + * + * This function loads application configuration from a YAML file using the + * Viper configuration management library. It supports environment variable + * overrides and automatic configuration file discovery. + * + * @param path Directory path containing the configuration file + * @param name Configuration file name (without extension) + * @return Config Loaded configuration structure + * @return error Error if configuration loading fails + */ func LoadConfig(path string, name string) (config Config, err error) { viper.SetConfigType("yaml") viper.AddConfigPath(path) @@ -1860,6 +2532,16 @@ func LoadConfig(path string, name string) (config Config, err error) { } +/** + * @brief Processes user menu choices and executes corresponding operations + * + * This function handles all user menu selections and executes the appropriate + * operations based on the command choice. It supports operations for sandbox + * management, MEC services, UE management, and various MEC API operations. + * + * @param choice Array containing the user's command choice and parameters + * @return string Status message describing the result of the operation + */ func process_choice(choice []string) string { var message string diff --git a/examples/demo6/python/notebook/MEC application.ipynb b/examples/demo6/python/notebook/MEC application.ipynb index bd90d1335451d71ac4eaea5a52837365ed15ae13..2d83d71b2a920e8c7c7898bc127a864f7da77e7c 100644 --- a/examples/demo6/python/notebook/MEC application.ipynb +++ b/examples/demo6/python/notebook/MEC application.ipynb @@ -72,11 +72,19 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": { "scrolled": true }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/home/jovyan/work/mecapp\n" + ] + } + ], "source": [ "import os\n", "os.chdir(os.path.join(os.getcwd(), '../mecapp'))\n", @@ -92,7 +100,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -132,7 +140,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -158,7 +166,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -198,7 +206,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -249,7 +257,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 34, "metadata": {}, "outputs": [], "source": [ @@ -295,7 +303,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -330,11 +338,20 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 36, "metadata": {}, - "outputs": [], + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "invalid syntax (92138252.py, line 1)", + "output_type": "error", + "traceback": [ + "\u001b[0;36m Cell \u001b[0;32mIn[36], line 1\u001b[0;36m\u001b[0m\n\u001b[0;31m script echo skipping\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" + ] + } + ], "source": [ - "%%script echo skipping\n", + "script echo skipping\n", "# Uncomment the line above to skip execution of this cell\n", "def process_main():\n", " \"\"\"\n", @@ -395,7 +412,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ @@ -437,12 +454,103 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 39, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2025-06-19 09:02:23,282 - __main__ - DEBUG - Starting at 20250619-090223\n", + "2025-06-19 09:02:23,283 - __main__ - DEBUG - \t pwd= /home/jovyan/work/mecapp\n", + "2025-06-19 09:02:23,284 - __main__ - DEBUG - >>> process_login\n", + "2025-06-19 09:02:23,285 DEBUG Starting new HTTPS connection (1): mec-platform2.etsi.org:443\n", + "2025-06-19 09:02:23,443 DEBUG https://mec-platform2.etsi.org:443 \"POST /sandbox-api/v1/login?provider=Jupyter2024 HTTP/11\" 201 0\n", + "2025-06-19 09:02:23,443 DEBUG response body: b'{\"user_code\":\"sbx19g3u4u\",\"verification_uri\":\"\"}'\n", + "2025-06-19 09:02:23,444 - __main__ - DEBUG - process_login (step1): oauth: {'user_code': 'sbx19g3u4u', 'verification_uri': ''}\n", + "2025-06-19 09:02:23,444 - __main__ - DEBUG - =======================> DO AUTHORIZATION WITH CODE : sbx19g3u4u\n", + "2025-06-19 09:02:23,445 - __main__ - DEBUG - =======================> DO AUTHORIZATION HERE : \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "send: b'POST /sandbox-api/v1/login?provider=Jupyter2024 HTTP/1.1\\r\\nHost: mec-platform2.etsi.org\\r\\nAccept-Encoding: identity\\r\\nContent-Length: 2\\r\\nAccept: application/json\\r\\nContent-Type: application/json\\r\\nUser-Agent: Swagger-Codegen/1.0.0/python\\r\\n\\r\\n'\n", + "send: b'{}'\n", + "reply: 'HTTP/1.1 201 Created\\r\\n'\n", + "header: Date: Thu, 19 Jun 2025 09:02:23 GMT\n", + "header: Content-Type: application/json; charset=UTF-8\n", + "header: Content-Length: 48\n", + "header: Connection: keep-alive\n", + "header: Strict-Transport-Security: max-age=15724800; includeSubDomains\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2025-06-19 09:02:26,461 DEBUG https://mec-platform2.etsi.org:443 \"GET /sandbox-api/v1/namespace?user_code=sbx19g3u4u HTTP/11\" 200 0\n", + "2025-06-19 09:02:26,462 DEBUG response body: b'{\"sandbox_name\":\"sbx19g3u4u\"}'\n", + "2025-06-19 09:02:26,462 - __main__ - DEBUG - process_login (step2): result: {'sandbox_name': 'sbx19g3u4u'}\n", + "2025-06-19 09:02:26,463 - __main__ - INFO - Sandbox created: sbx19g3u4u\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "send: b'GET /sandbox-api/v1/namespace?user_code=sbx19g3u4u HTTP/1.1\\r\\nHost: mec-platform2.etsi.org\\r\\nAccept-Encoding: identity\\r\\nAccept: application/json\\r\\nContent-Type: application/json\\r\\nUser-Agent: Swagger-Codegen/1.0.0/python\\r\\n\\r\\n'\n", + "reply: 'HTTP/1.1 200 OK\\r\\n'\n", + "header: Date: Thu, 19 Jun 2025 09:02:26 GMT\n", + "header: Content-Type: application/json; charset=UTF-8\n", + "header: Content-Length: 29\n", + "header: Connection: keep-alive\n", + "header: Strict-Transport-Security: max-age=15724800; includeSubDomains\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2025-06-19 09:02:36,463 - __main__ - DEBUG - >>> get_network_scenarios: sandbox=sbx19g3u4u\n", + "2025-06-19 09:02:36,464 DEBUG Resetting dropped connection: mec-platform2.etsi.org\n", + "2025-06-19 09:02:36,631 DEBUG https://mec-platform2.etsi.org:443 \"GET /sandbox-api/v1/sandboxNetworkScenarios?sandbox_name=sbx19g3u4u HTTP/11\" 200 0\n", + "2025-06-19 09:02:36,632 DEBUG response body: b'[{\"id\":\"4g-5g-macro-v2x\"},{\"id\":\"4g-5g-macro-v2x-fed\"},{\"id\":\"4g-5g-wifi-macro\"},{\"id\":\"4g-macro\"},{\"id\":\"4g-wifi-macro\"},{\"id\":\"dual-mep-4g-5g-wifi-macro\"},{\"id\":\"dual-mep-short-path\"}]'\n", + "2025-06-19 09:02:36,632 - __main__ - DEBUG - get_network_scenarios: result: [{'id': '4g-5g-macro-v2x'}, {'id': '4g-5g-macro-v2x-fed'}, {'id': '4g-5g-wifi-macro'}, {'id': '4g-macro'}, {'id': '4g-wifi-macro'}, {'id': 'dual-mep-4g-5g-wifi-macro'}, {'id': 'dual-mep-short-path'}]\n", + "2025-06-19 09:02:36,633 - __main__ - INFO - nw_scenarios: \n", + "2025-06-19 09:02:36,633 - __main__ - INFO - nw_scenarios: [{'id': '4g-5g-macro-v2x'}, {'id': '4g-5g-macro-v2x-fed'}, {'id': '4g-5g-wifi-macro'}, {'id': '4g-macro'}, {'id': '4g-wifi-macro'}, {'id': 'dual-mep-4g-5g-wifi-macro'}, {'id': 'dual-mep-short-path'}]\n", + "2025-06-19 09:02:36,633 - __main__ - DEBUG - >>> process_logout: sandbox=sbx19g3u4u\n", + "2025-06-19 09:02:36,648 DEBUG https://mec-platform2.etsi.org:443 \"POST /sandbox-api/v1/logout?sandbox_name=sbx19g3u4u HTTP/11\" 204 0\n", + "2025-06-19 09:02:36,649 DEBUG response body: b''\n", + "2025-06-19 09:02:36,650 - __main__ - DEBUG - To check that logout is effective, verify on the MEC Sandbox server that the MEC Sandbox is removed (kubectl get pods -A)\n", + "2025-06-19 09:02:36,651 - __main__ - DEBUG - Stopped at 20250619-090236\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "send: b'GET /sandbox-api/v1/sandboxNetworkScenarios?sandbox_name=sbx19g3u4u HTTP/1.1\\r\\nHost: mec-platform2.etsi.org\\r\\nAccept-Encoding: identity\\r\\nAccept: application/json\\r\\nContent-Type: application/json\\r\\nUser-Agent: Swagger-Codegen/1.0.0/python\\r\\n\\r\\n'\n", + "reply: 'HTTP/1.1 200 OK\\r\\n'\n", + "header: Date: Thu, 19 Jun 2025 09:02:36 GMT\n", + "header: Content-Type: application/json; charset=UTF-8\n", + "header: Content-Length: 186\n", + "header: Connection: keep-alive\n", + "header: Strict-Transport-Security: max-age=15724800; includeSubDomains\n", + "send: b'POST /sandbox-api/v1/logout?sandbox_name=sbx19g3u4u HTTP/1.1\\r\\nHost: mec-platform2.etsi.org\\r\\nAccept-Encoding: identity\\r\\nContent-Length: 2\\r\\nContent-Type: application/json\\r\\nUser-Agent: Swagger-Codegen/1.0.0/python\\r\\n\\r\\n'\n", + "send: b'{}'\n", + "reply: 'HTTP/1.1 204 No Content\\r\\n'\n", + "header: Date: Thu, 19 Jun 2025 09:02:36 GMT\n", + "header: Content-Type: application/json; charset=UTF-8\n", + "header: Connection: keep-alive\n", + "header: Strict-Transport-Security: max-age=15724800; includeSubDomains\n" + ] + } + ], "source": [ - "%%script echo skipping\n", - "# Uncomment the ;line above to skip execution of this cell\n", + "\n", + "# Uncomment the line above to skip execution of this cell\n", "def process_main():\n", " \"\"\"\n", " This is the first sprint of our skeleton of our MEC application:\n", @@ -504,7 +612,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -529,7 +637,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ @@ -570,7 +678,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ @@ -617,9 +725,17 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 15, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "skipping\n" + ] + } + ], "source": [ "%%script echo skipping\n", "# Uncomment the ;line above to skip execution of this cell\n", @@ -713,7 +829,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 16, "metadata": {}, "outputs": [], "source": [ @@ -752,7 +868,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 17, "metadata": {}, "outputs": [], "source": [ @@ -788,7 +904,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 18, "metadata": {}, "outputs": [], "source": [ @@ -837,9 +953,17 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 19, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "skipping\n" + ] + } + ], "source": [ "%%script echo skipping\n", "# Uncomment the ;line above to skip execution of this cell\n", @@ -970,7 +1094,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 20, "metadata": {}, "outputs": [], "source": [ @@ -1035,7 +1159,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 21, "metadata": {}, "outputs": [], "source": [ @@ -1086,7 +1210,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 22, "metadata": {}, "outputs": [], "source": [ @@ -1118,7 +1242,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 23, "metadata": {}, "outputs": [], "source": [ @@ -1166,7 +1290,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 24, "metadata": {}, "outputs": [], "source": [ @@ -1229,9 +1353,17 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 25, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "skipping\n" + ] + } + ], "source": [ "%%script echo skipping\n", "# Uncomment the ;line above to skip execution of this cell\n", @@ -1367,7 +1499,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 26, "metadata": {}, "outputs": [], "source": [ @@ -1445,7 +1577,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 27, "metadata": {}, "outputs": [], "source": [ @@ -1498,9 +1630,17 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 28, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "skipping\n" + ] + } + ], "source": [ "%%script echo skipping\n", "# Uncomment the ;line above to skip execution of this cell\n", @@ -1556,7 +1696,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 29, "metadata": {}, "outputs": [], "source": [ @@ -1606,7 +1746,7 @@ " \"accessPointId\": \"4g-macro-cell-6\",\n", " \"zoneId\": \"zone03\",\n", " \"resourceURL\": \"https://mec-platform.etsi.org/xxx/mep1/location/v3/queries/users?address=10.100.0.1\",\n", - " \"timestamp\": {\n", + " \"timeStamp\": {\n", " \"nanoSeconds\": 0,\n", " \"seconds\": 1729245754\n", " },\n", @@ -1659,9 +1799,17 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 30, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "skipping\n" + ] + } + ], "source": [ "%%script echo skipping\n", "# Uncomment the ;line above to skip execution of this cell\n", @@ -1733,7 +1881,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 31, "metadata": {}, "outputs": [], "source": [ @@ -1861,7 +2009,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 32, "metadata": {}, "outputs": [], "source": [ @@ -1923,11 +2071,19 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 33, "metadata": { "scrolled": true }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "skipping\n" + ] + } + ], "source": [ "%%script echo skipping\n", "# Uncomment the ;line above to skip execution of this cell\n", @@ -2237,7 +2393,7 @@ " def __ne__(self, other):\n", " return not self == other\n", "\n", - "class TimeStamp(object):\n", + "class timeStamp(object):\n", " swagger_types = {'seconds': 'int', 'nano_seconds': 'int'}\n", " attribute_map = {'seconds': 'seconds', 'nano_seconds': 'nanoSeconds'}\n", " def __init__(self, seconds=None, nano_seconds=None): # noqa: E501\n", @@ -2278,7 +2434,7 @@ " ))\n", " else:\n", " result[attr] = value\n", - " if issubclass(TimeStamp, dict):\n", + " if issubclass(timeStamp, dict):\n", " for key, value in self.items():\n", " result[key] = value\n", " return result\n", @@ -2287,7 +2443,7 @@ " def __repr__(self):\n", " return self.to_str()\n", " def __eq__(self, other):\n", - " if not isinstance(other, TimeStamp):\n", + " if not isinstance(other, timeStamp):\n", " return False\n", " return self.__dict__ == other.__dict__\n", " def __ne__(self, other):\n", @@ -2880,7 +3036,7 @@ " return not self == other\n", "\n", "class RouteInfo(object):\n", - " swagger_types = {'_location': 'LocationInfo', '_time_stamp': 'TimeStamp'}\n", + " swagger_types = {'_location': 'LocationInfo', '_time_stamp': 'timeStamp'}\n", " attribute_map = {'_location': 'location', '_time_stamp': 'time'}\n", " def __init__(self, location:LocationInfo, time_stamp=None): # noqa: E501\n", " self._location = None\n", @@ -3317,7 +3473,7 @@ " return not self == other\n", "\n", "class PredictedQos(object):\n", - " swagger_types = {'_location_granularity': 'str', '_notice_period': 'TimeStamp', '_prediction_area': 'PredictionArea', '_prediction_target': 'str', '_qos': 'Qos', '_routes': 'list[Routes]', '_time_granularity': 'TimeStamp'}\n", + " swagger_types = {'_location_granularity': 'str', '_notice_period': 'timeStamp', '_prediction_area': 'PredictionArea', '_prediction_target': 'str', '_qos': 'Qos', '_routes': 'list[Routes]', '_time_granularity': 'timeStamp'}\n", " attribute_map = {'_location_granularity': 'locationGranularity', '_notice_period': 'noticePeriod', '_prediction_area': 'predictionArea', '_prediction_target': 'predictionTarget', '_qos': 'qos', '_routes': 'routes', '_time_granularity': 'timeGranularity'}\n", " def __init__(self, prediction_target:str, location_granularity:str, notice_period=None, time_granularity=None, prediction_area=None, routes=None, qos=None): # noqa: E501\n", " self._prediction_target = None\n", @@ -3453,8 +3609,8 @@ " # Body request\n", " loc1 = LocationInfo(geo_area=LocationInfoGeoArea(latitude=43.729416, longitude=7.414853))\n", " loc2 = LocationInfo(geo_area=LocationInfoGeoArea(latitude=43.732456, longitude=7.418417))\n", - " routeInfo1 = RouteInfo(loc1, TimeStamp(nano_seconds=0, seconds=1653295620))\n", - " routeInfo2 = RouteInfo(loc2, TimeStamp(nano_seconds=0, seconds=1653299220))\n", + " routeInfo1 = RouteInfo(loc1, timeStamp(nano_seconds=0, seconds=1653295620))\n", + " routeInfo2 = RouteInfo(loc2, timeStamp(nano_seconds=0, seconds=1653299220))\n", " routesInfo = [routeInfo1, routeInfo2]\n", " predictedQos = PredictedQos(prediction_target=\"SINGLE_UE_PREDICTION\", location_granularity=\"30\", routes=[Routes(routesInfo)])\n", " (result, status, headers) = service_api.call_api(url, 'POST', header_params=header_params, path_params=path_params, body=predictedQos, async_req=False)\n", @@ -4663,9 +4819,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, - "outputs": [], + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'logger' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[1], line 68\u001b[0m\n\u001b[1;32m 65\u001b[0m \u001b[38;5;66;03m# End of function process_main\u001b[39;00m\n\u001b[1;32m 67\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;18m__name__\u001b[39m \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124m__main__\u001b[39m\u001b[38;5;124m'\u001b[39m:\n\u001b[0;32m---> 68\u001b[0m \u001b[43mprocess_main\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n", + "Cell \u001b[0;32mIn[1], line 17\u001b[0m, in \u001b[0;36mprocess_main\u001b[0;34m()\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 5\u001b[0m \u001b[38;5;124;03mThis code illustrates the usage of MEC-CAPI endpoints:\u001b[39;00m\n\u001b[1;32m 6\u001b[0m \u001b[38;5;124;03m - Login\u001b[39;00m\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 13\u001b[0m \u001b[38;5;124;03m - Logout\u001b[39;00m\n\u001b[1;32m 14\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m \n\u001b[1;32m 15\u001b[0m \u001b[38;5;28;01mglobal\u001b[39;00m LISTENER_IP, LISTENER_PORT, CALLBACK_URI, logger\n\u001b[0;32m---> 17\u001b[0m \u001b[43mlogger\u001b[49m\u001b[38;5;241m.\u001b[39mdebug(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mStarting at \u001b[39m\u001b[38;5;124m'\u001b[39m \u001b[38;5;241m+\u001b[39m time\u001b[38;5;241m.\u001b[39mstrftime(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m%\u001b[39m\u001b[38;5;124mY\u001b[39m\u001b[38;5;124m%\u001b[39m\u001b[38;5;124mm\u001b[39m\u001b[38;5;132;01m%d\u001b[39;00m\u001b[38;5;124m-\u001b[39m\u001b[38;5;124m%\u001b[39m\u001b[38;5;124mH\u001b[39m\u001b[38;5;124m%\u001b[39m\u001b[38;5;124mM\u001b[39m\u001b[38;5;124m%\u001b[39m\u001b[38;5;124mS\u001b[39m\u001b[38;5;124m'\u001b[39m))\n\u001b[1;32m 18\u001b[0m logger\u001b[38;5;241m.\u001b[39mdebug(\u001b[38;5;124m'\u001b[39m\u001b[38;5;130;01m\\t\u001b[39;00m\u001b[38;5;124m pwd= \u001b[39m\u001b[38;5;124m'\u001b[39m \u001b[38;5;241m+\u001b[39m os\u001b[38;5;241m.\u001b[39mgetcwd())\n\u001b[1;32m 20\u001b[0m \u001b[38;5;66;03m# Setup the MEC application\u001b[39;00m\n", + "\u001b[0;31mNameError\u001b[0m: name 'logger' is not defined" + ] + } + ], "source": [ "%%script echo skipping\n", "# Uncomment the ;line above to skip execution of this cell\n", diff --git a/examples/demo7/Dockerfile b/examples/demo7/Dockerfile index a5a306ab06bc7f6eca06cfef48880664a8742b30..3e20537f4ede6a6c94d0d192cc1f13352e319e1f 100644 --- a/examples/demo7/Dockerfile +++ b/examples/demo7/Dockerfile @@ -17,6 +17,8 @@ FROM ubuntu:22.04 COPY ./fsmsggen /fsmsggen COPY ./entrypoint.sh /entrypoint.sh -RUN apt-get update && apt-get install -y curl libjson-c-dev libgps-dev libpcap-dev libssl-dev && chmod +x /entrypoint.sh && chmod +x /fsmsggen +RUN apt-get update && apt-get install -y curl gettext jq libjson-c-dev libgps-dev libpcap-dev libssl-dev mosquitto-clients && chmod +x /entrypoint.sh && chmod +x /fsmsggen + +EXPOSE 1883 ENTRYPOINT ["/entrypoint.sh"] diff --git a/examples/demo7/README.md b/examples/demo7/README.md index 5fdf3ed024568413b5add6c375063abcfa61da27..9183291c090a24f9ff668523a67c79ab4ebef20b 100644 --- a/examples/demo7/README.md +++ b/examples/demo7/README.md @@ -2,3 +2,17 @@ Demo7 showcases the V2X capabilities of the platform. It is a C-V2X OBU simulator taht can be used as MEC application +## How to build demo7 + +```bash +$ cd ./examples/demo7/ +$ ./dockerize.sh +``` + +## How to use it + +### During network scenario deployment + + +### In command line + diff --git a/examples/demo7/entrypoint.sh b/examples/demo7/entrypoint.sh index bb6821d018999548539a7bc67dd2245864faf69d..da149c796afb4457c37cd3a6bc1a6a1a4f84f978 100755 --- a/examples/demo7/entrypoint.sh +++ b/examples/demo7/entrypoint.sh @@ -1,11 +1,11 @@ #!/bin/sh # # Here are some examples of env. variable values: -# COMM: [--iface eth0|--mode 2] +# COMM: --iface eth0 --mode 2 # -# GLOBAL: --verbose --srcaddr 00:01:02:03:04:05 --no-sec true +# GLOBAL: --verbose --srcaddr 00:01:02:03:04:05 --rate 1 --start-vam --brocker-address test.mosquitto.org # -# SEC_MODE: --no-sec true +# SEC_MODE: --no-sec # # BEACON: --no-sec-beacon true # @@ -17,9 +17,34 @@ # E.g. yann@yann-linux:~/frameworks/fsmsggen$ sudo ./build/x86_64-linux-gnu-d/fsmsggen --iface wlo1 --verbose --srcaddr 00:01:02:03:04:05 --no-sec true --cam-station-type passengerCar --cam-station-id 12345 --cam-no-sec --out ./out.pcap - set -e set +vx -echo "Starting '/fsmsggen ${COMM} ${GLOBAL} ${SEC_MODE} ${COMM_MODE} ${BEACON} ${CAM} ${DENM} ${VERBOSE}'" -/fsmsggen ${COMM} ${GLOBAL} ${SEC_MODE} ${BEACON} ${CAM} ${DENM} ${VERBOSE} \ No newline at end of file +echo "MEEP_HOST_URL: ${MEEP_HOST_URL}" +echo "MEEP_SANDBOX_NAME: ${MEEP_SANDBOX_NAME}" +echo "MEEP_MEP_NAME: ${MEEP_MEP_NAME}" + +echo "Waiting for MEC plateform started and stable..." +sleep 10 # Wait for MEC plateform started and stable + +MQTT_ENABLE=${MQTT_ENABLE:-true} +MQTT_HOST=${MOSQUITTO_NODE_IP_ADDRESS:-"meep-mosquitto"} +MQTT_PORT=${MOSQUITTO_NODE_PORT:-443} +UU_IFACE="--brocker-address $MEEP_HOST_URL/$MEEP_SANDBOX_NAME/$MEEP_MEP_NAME/$MQTT_HOST --brocker-port 443 --use-ws --wss-root-ca fullchain.pem --wss-cert chain.pem --wss-key privkey.pem" + +echo "Environment variables set:" +echo "MQTT_ENABLE: ${MQTT_ENABLE}" +echo "MQTT_HOST: ${MQTT_HOST}" +echo "MQTT_PORT: ${MQTT_PORT}" +echo "COMM: ${IFACE}" +echo "MODE: ${MODE}" +echo "UU_IFACE: ${UU_IFACE}" +echo "GLOBAL: ${GLOBAL}" +echo "SEC_MODE: ${SEC_MODE}" +echo "BEACON: ${BEACON}" +echo "CAM: ${CAM}" +echo "DENM: ${DENM}" +echo "VERBOSE: ${VERBOSE}" + +echo "Starting '/fsmsggen ${IFACE} ${MODE} ${UU_IFACE} ${GLOBAL} ${SEC_MODE} ${BEACON} ${CAM} ${DENM} ${VERBOSE}'" +/fsmsggen ${IFACE} ${MODE} ${UU_IFACE} ${GLOBAL} ${SEC_MODE} ${BEACON} ${CAM} ${DENM} ${VERBOSE} \ No newline at end of file diff --git a/examples/demo7/fsmsggen b/examples/demo7/fsmsggen index 526d511e84db749f057649b9cd6242be2c7b7c83..92645860776fe88458d2802eabe49d576a57d7a8 100644 Binary files a/examples/demo7/fsmsggen and b/examples/demo7/fsmsggen differ diff --git a/go-apps/meep-ams/server/ams.go b/go-apps/meep-ams/server/ams.go index 0123bd8b849021fee8b44d0e4623daf7afcb6b74..f977727b7693f4239fff38273df32af2f44db069 100644 --- a/go-apps/meep-ams/server/ams.go +++ b/go-apps/meep-ams/server/ams.go @@ -1221,7 +1221,6 @@ func subscriptionLinkListSubscriptionsGet(w http.ResponseWriter, r *http.Request validQueryParams := []string{"subscriptionType"} err := validateQueryParams(q, validQueryParams) if err != nil { - errHandlerProblemDetails(w, err.Error(), http.StatusBadRequest) return } @@ -1234,14 +1233,7 @@ func subscriptionLinkListSubscriptionsGet(w http.ResponseWriter, r *http.Request } // Create subscription link list - subscriptionLinkList := &SubscriptionLinkList{ - Links: &SubscriptionLinkListLinks{ - Self: &LinkType{ - Href: hostUrl.String() + basePath + "subscriptions", - }, - }, - } - + var subscriptionLinkList SubscriptionLinkList var subscriptionLinkListLinks SubscriptionLinkListLinks // Find subscriptions by type @@ -1281,11 +1273,14 @@ func subscriptionLinkListSubscriptionsGet(w http.ResponseWriter, r *http.Request // Add to link list subscriptionLinkListLinks.Subscription = append(subscriptionLinkListLinks.Subscription, linkListSub) } + subscriptionLinkListLinks.Self = &LinkType{ + Href: hostUrl.String() + basePath + "subscriptions", + } subscriptionLinkList.Links = &subscriptionLinkListLinks // Send response w.WriteHeader(http.StatusOK) - fmt.Fprint(w, convertSubscriptionLinkListToJson(subscriptionLinkList)) + fmt.Fprint(w, convertSubscriptionLinkListToJson(&subscriptionLinkList)) } func appMobilityServicePOST(w http.ResponseWriter, r *http.Request) { diff --git a/go-apps/meep-app-enablement/api/capif-mgmt/swagger.yaml b/go-apps/meep-app-enablement/api/capif-mgmt/swagger.yaml index b2cd8178bfea67c7408e44b3a5a7843c6ca4e128..3caedb9a3dd42fae7d44a27145c50861e8d4600f 100644 --- a/go-apps/meep-app-enablement/api/capif-mgmt/swagger.yaml +++ b/go-apps/meep-app-enablement/api/capif-mgmt/swagger.yaml @@ -1215,20 +1215,21 @@ components: description: "AEF identifier. Shall be set to the value of the 'id' attribute as defined in clause 8.1.2.3." versions: type: array - items: - type: string - description: "API version. This array shall contain a single entry." minItems: 1 maxItems: 1 + items: + type: object + properties: + apiVersion: + type: string + description: "The version of the API as per clause 8.1.2.2" + required: + - apiVersion interfaceDescriptions: description: This type represents information about a transport endpoint - oneOf: - - $ref: '#/components/schemas/EndPointInfo.Uris' - - $ref: '#/components/schemas/EndPointInfo.Fqdn' - - $ref: '#/components/schemas/EndPointInfo.Addresses' - - $ref: '#/components/schemas/EndPointInfo.Alternative' - x-etsi-notes: "NOTE:\tExactly one of \"uris\", \"fqdn\", \"addresses\" or\ - \ \"alternative\" shall be present." + type: array + items: + $ref: '#/components/schemas/InterfaceDescription' vendorSpecific-urn:etsi:mec:capifext:transport-info : $ref: '#/components/schemas/MecTransportInfoCapifExt' description: "Additional attribute of data type MecTransportInfoCapifExt for MEC-specific CAPIF extensions related to alternative transports." @@ -1326,69 +1327,32 @@ components: - RPC - RPC_STREAMING - WEBSOCKET - EndPointInfo.Alternative: - title: EndPointInfo.Alternative - required: - - alternative - type: object - properties: - alternative: - type: object - description: "Entry point information of the service in a format defined\ - \ by an implementation, or in an external specification. See note." - description: This type represents information about a transport endpoint. - EndPointInfo.Address: - title: EndPointInfo.Address - required: - - host - - port + InterfaceDescription: type: object + description: 3GPP-compliant InterfaceDescription (TS 29.222 Table 8.2.4.2.3-1) properties: - host: + ipv4Addr: + type: string + format: ipv4 + description: IPv4 address + ipv6Addr: + type: string + format: ipv6 + description: IPv6 address + fqdn: type: string - description: Host portion of the address - example: "[\"192.0.2.0\"]" + description: Fully Qualified Domain Name port: type: integer - description: Port portion of the address - description: A IP address and port pair - EndPointInfo.Addresses: - title: EndPointInfo.Addresses - required: - - addresses - type: object - properties: - addresses: - type: array - description: Entry point information of the service as one or more pairs - of IP address and port. See note. - items: - $ref: '#/components/schemas/EndPointInfo.Address' - EndPointInfo.Fqdn: - title: EndPointInfo.Fqdn - required: - - fqdn - type: object - properties: - fqdn: - type: array - description: Fully Qualified Domain Name of the service. See note. - items: - type: string - description: 'This type represents information about a transport endpoint. ' - EndPointInfo.Uris: - title: EndPointInfo.Uris - required: - - uris - type: object - properties: - uris: - type: array - description: "Entry point information of the service as string, formatted\ - \ according to URI syntax" - items: - type: string - description: This type represents information about a transport endpoint. + description: TCP port number + apiPrefix: + type: string + description: Optional API prefix path (starts with '/') + oneOf: + - required: [ipv4Addr] + - required: [ipv6Addr] + - required: [fqdn] + additionalProperties: false responses: "400": description: Bad Request. It is used to indicate that incorrect parameters were diff --git a/go-apps/meep-app-enablement/server/capif-mgmt/model_aef_profile.go b/go-apps/meep-app-enablement/server/capif-mgmt/model_aef_profile.go index a75f72a07b85f84274c453ae4bd8356ccbfaeec0..0da750a82c4574c0cb80309414c75b8faf7826af 100644 --- a/go-apps/meep-app-enablement/server/capif-mgmt/model_aef_profile.go +++ b/go-apps/meep-app-enablement/server/capif-mgmt/model_aef_profile.go @@ -13,9 +13,9 @@ type AefProfile struct { // AEF identifier. Shall be set to the value of the 'id' attribute as defined in clause 8.1.2.3. AefId string `json:"aefId"` - Versions []string `json:"versions"` + Versions []Version `json:"versions"` // This type represents information about a transport endpoint - InterfaceDescriptions *OneOfTransportInfoEndpoint `json:"interfaceDescriptions,omitempty"` + InterfaceDescriptions []InterfaceDescription `json:"interfaceDescriptions,omitempty"` VendorSpecificUrnetsimeccapifexttransportInfo *MecTransportInfoCapifExt `json:"vendorSpecific-urn:etsi:mec:capifext:transport-info,omitempty"` } diff --git a/go-apps/meep-app-enablement/server/capif-mgmt/service-mgmt.go b/go-apps/meep-app-enablement/server/capif-mgmt/service-mgmt.go index dbdf4213bf8f7f1826b0409523b37939979d333a..308c8be812695514619fbb0a064ec80b1ea6cbcd 100644 --- a/go-apps/meep-app-enablement/server/capif-mgmt/service-mgmt.go +++ b/go-apps/meep-app-enablement/server/capif-mgmt/service-mgmt.go @@ -20,6 +20,7 @@ import ( "encoding/json" "errors" "fmt" + "net" "net/http" "net/url" "strconv" @@ -44,6 +45,7 @@ const globalMepName = "global" const SER_AVAILABILITY_NOTIF_SUB_TYPE = "SerAvailabilityNotificationSubscription" const SER_AVAILABILITY_NOTIF_TYPE = "SerAvailabilityNotification" const APP_STATE_READY = "READY" +const capifEventsBasePath = "capif-events/v1" // const logModuleAppEnablement = "meep-app-enablement" const serviceName = "App Enablement Service" @@ -281,6 +283,15 @@ func appServicesPOST(w http.ResponseWriter, r *http.Request) { errHandlerProblemDetails(w, errStr, http.StatusBadRequest) return } + // Reject if more than one version is provided in any AEF Profile + for _, aef := range sInfoPost.AefProfiles { + if len(aef.Versions) > 1 { + errStr := fmt.Sprintf("Only one apiVersion is allowed per AEF profile; found %d", len(aef.Versions)) + log.Error(errStr) + errHandlerProblemDetails(w, errStr, http.StatusBadRequest) + return + } + } if sInfoPost.VendorSpecificUrnetsimeccapifextserviceInfo.State == nil { errStr := "Mandatory Service State parameter not present" log.Error(errStr) @@ -314,6 +325,11 @@ func appServicesPOST(w http.ResponseWriter, r *http.Request) { errHandlerProblemDetails(w, errStr, http.StatusBadRequest) return } + if err := validateInterfaceDescriptions(sInfoPost.AefProfiles[0].InterfaceDescriptions); err != nil { + log.Error(err.Error()) + errHandlerProblemDetails(w, err.Error(), http.StatusBadRequest) + return + } if len(sInfoPost.AefProfiles) == 0 || sInfoPost.AefProfiles[0].AefId == "" { errStr := "Mandatory AefProfiles (AefId) not present" log.Error(errStr) @@ -333,9 +349,13 @@ func appServicesPOST(w http.ResponseWriter, r *http.Request) { } // } + if sInfoPost.AefProfiles[0].VendorSpecificUrnetsimeccapifexttransportInfo.Security == nil { + sInfoPost.AefProfiles[0].VendorSpecificUrnetsimeccapifexttransportInfo.Security = &SecurityInfo{} + } + aefProfile := &AefProfile{ AefId: sInfoPost.AefProfiles[0].AefId, - Versions: sInfoPost.AefProfiles[0].Versions, + Versions: []Version{sInfoPost.AefProfiles[0].Versions[0]}, InterfaceDescriptions: sInfoPost.AefProfiles[0].InterfaceDescriptions, VendorSpecificUrnetsimeccapifexttransportInfo: &MecTransportInfoCapifExt{ Name: sInfoPost.AefProfiles[0].VendorSpecificUrnetsimeccapifexttransportInfo.Name, @@ -365,7 +385,7 @@ func appServicesPOST(w http.ResponseWriter, r *http.Request) { Type_: sInfoPost.AefProfiles[0].VendorSpecificUrnetsimeccapifexttransportInfo.Type_, Protocol: sInfoPost.AefProfiles[0].VendorSpecificUrnetsimeccapifexttransportInfo.Protocol, Version: sInfoPost.AefProfiles[0].VendorSpecificUrnetsimeccapifexttransportInfo.Version, - Endpoint: sInfoPost.AefProfiles[0].InterfaceDescriptions, + Endpoint: ConvertInterfaceDescriptionsToEndpoint(sInfoPost.AefProfiles[0].InterfaceDescriptions), Security: sInfoPost.AefProfiles[0].VendorSpecificUrnetsimeccapifexttransportInfo.Security, } @@ -374,7 +394,7 @@ func appServicesPOST(w http.ResponseWriter, r *http.Request) { SerInstanceId: dsInfo.ApiId, SerName: dsInfo.ApiName, SerCategory: dsInfo.VendorSpecificUrnetsimeccapifextserviceInfo.Category, - Version: dsInfo.AefProfiles[0].Versions[0], + Version: dsInfo.AefProfiles[0].Versions[0].APIVersion, State: dsInfo.VendorSpecificUrnetsimeccapifextserviceInfo.State, TransportInfo: &transportInfo_, Serializer: dsInfo.VendorSpecificUrnetsimeccapifextserviceInfo.Serializer, @@ -401,8 +421,11 @@ func appServicesPOST(w http.ResponseWriter, r *http.Request) { return } + // Set Location Header + updatedBasePath := strings.Replace(basePath, "mec_service_mgmt/v1", "published-apis/v1", 1) + // Send response - w.Header().Set("Location", hostUrl.String()+basePath+"applications/"+appId+"/services/"+sInfo.SerInstanceId) + w.Header().Set("Location", hostUrl.String()+updatedBasePath+appId+"/service-apis/"+dsInfo.ApiId) w.WriteHeader(http.StatusCreated) fmt.Fprint(w, convertServiceInfoToJson_1(dsInfo)) } @@ -501,7 +524,7 @@ func appServicesByIdPUT(w http.ResponseWriter, r *http.Request) { Type_: sInfo.AefProfiles[0].VendorSpecificUrnetsimeccapifexttransportInfo.Type_, Protocol: sInfo.AefProfiles[0].VendorSpecificUrnetsimeccapifexttransportInfo.Protocol, Version: sInfo.AefProfiles[0].VendorSpecificUrnetsimeccapifexttransportInfo.Version, - Endpoint: sInfo.AefProfiles[0].InterfaceDescriptions, + Endpoint: ConvertInterfaceDescriptionsToEndpoint(sInfo.AefProfiles[0].InterfaceDescriptions), Security: sInfo.AefProfiles[0].VendorSpecificUrnetsimeccapifexttransportInfo.Security, } @@ -510,7 +533,7 @@ func appServicesByIdPUT(w http.ResponseWriter, r *http.Request) { SerInstanceId: dsInfo.ApiId, SerName: dsInfo.ApiName, SerCategory: dsInfo.VendorSpecificUrnetsimeccapifextserviceInfo.Category, - Version: dsInfo.AefProfiles[0].Versions[0], + Version: dsInfo.AefProfiles[0].Versions[0].APIVersion, State: dsInfo.VendorSpecificUrnetsimeccapifextserviceInfo.State, TransportInfo: &transportInfo_, Serializer: dsInfo.VendorSpecificUrnetsimeccapifextserviceInfo.Serializer, @@ -660,20 +683,21 @@ func appServicesByIdPATCH(w http.ResponseWriter, r *http.Request) { Category: sInfo.VendorSpecificUrnetsimeccapifextserviceInfo.Category, }, } + transportInfo_ := TransportInfo{ Id: sInfo.AefProfiles[0].AefId, Name: sInfo.AefProfiles[0].VendorSpecificUrnetsimeccapifexttransportInfo.Name, Type_: sInfo.AefProfiles[0].VendorSpecificUrnetsimeccapifexttransportInfo.Type_, Protocol: sInfo.AefProfiles[0].VendorSpecificUrnetsimeccapifexttransportInfo.Protocol, Version: sInfo.AefProfiles[0].VendorSpecificUrnetsimeccapifexttransportInfo.Version, - Endpoint: sInfo.AefProfiles[0].InterfaceDescriptions, + Endpoint: ConvertInterfaceDescriptionsToEndpoint(sInfo.AefProfiles[0].InterfaceDescriptions), Security: sInfo.AefProfiles[0].VendorSpecificUrnetsimeccapifexttransportInfo.Security, } // Create Service _sInfo := ServiceInfo{ SerInstanceId: sInfoPrev.SerInstanceId, SerName: sInfoPrev.SerName, - Version: dsInfo.AefProfiles[0].Versions[0], + Version: dsInfo.AefProfiles[0].Versions[0].APIVersion, SerCategory: dsInfo.VendorSpecificUrnetsimeccapifextserviceInfo.Category, State: dsInfo.VendorSpecificUrnetsimeccapifextserviceInfo.State, TransportInfo: &transportInfo_, @@ -1194,7 +1218,11 @@ func applicationsSubscriptionsPOST(w http.ResponseWriter, r *http.Request) { errHandlerProblemDetails(w, "Failed to create subscription", http.StatusInternalServerError) return } - HrefUrl = hostUrl.String() + basePath + "applications/" + appId + "/subscriptions/" + subId + + // Set Location Header + updatedBasePath := strings.Replace(basePath, "mec_service_mgmt/v1", capifEventsBasePath, 1) + HrefUrl := hostUrl.String() + updatedBasePath + appId + "/subscriptions/" + subId + // Send response w.Header().Set("Location", HrefUrl) w.WriteHeader(http.StatusCreated) @@ -1774,8 +1802,8 @@ func getServices(w http.ResponseWriter, r *http.Request, appId string) { for _, service := range sInfoList.Services { aefProfile := AefProfile{ AefId: service.TransportInfo.Id, - Versions: []string{service.Version}, - InterfaceDescriptions: service.TransportInfo.Endpoint, + Versions: []Version{{APIVersion: service.Version}}, + InterfaceDescriptions: ConvertEndpointToInterfaceDescriptions(service.TransportInfo.Endpoint), VendorSpecificUrnetsimeccapifexttransportInfo: &MecTransportInfoCapifExt{ Name: service.TransportInfo.Name, Type_: service.TransportInfo.Type_, @@ -1851,8 +1879,8 @@ func getService(w http.ResponseWriter, r *http.Request, appId string, serviceId for _, service := range sInfoList.Services { aefProfile := AefProfile{ AefId: service.TransportInfo.Id, - Versions: []string{service.Version}, - InterfaceDescriptions: service.TransportInfo.Endpoint, + Versions: []Version{{APIVersion: service.Version}}, + InterfaceDescriptions: ConvertEndpointToInterfaceDescriptions(service.TransportInfo.Endpoint), VendorSpecificUrnetsimeccapifexttransportInfo: &MecTransportInfoCapifExt{ Name: service.TransportInfo.Name, Type_: service.TransportInfo.Type_, @@ -2078,11 +2106,11 @@ func checkSerAvailNotification(sInfo *ServiceInfo, mep string, changeType CapifE } } } - versions := []string{sInfo.Version} + // versions := []string{sInfo.Version} aefProfile := &AefProfile{ AefId: sInfo.TransportInfo.Id, - Versions: versions, - InterfaceDescriptions: sInfo.TransportInfo.Endpoint, + Versions: []Version{{APIVersion: sInfo.Version}}, + InterfaceDescriptions: ConvertEndpointToInterfaceDescriptions(sInfo.TransportInfo.Endpoint), VendorSpecificUrnetsimeccapifexttransportInfo: &MecTransportInfoCapifExt{ Name: sInfo.TransportInfo.Name, Type_: sInfo.TransportInfo.Type_, @@ -2303,3 +2331,192 @@ func errHandlerProblemDetails(w http.ResponseWriter, error string, code int) { w.WriteHeader(code) fmt.Fprint(w, jsonResponse) } + +func validateInterfaceDescriptions(descs []InterfaceDescription) error { + for i, desc := range descs { + hasAddr := desc.Ipv4Addr != nil || desc.Ipv6Addr != nil + hasFqdn := desc.Fqdn != nil + + if (hasAddr && hasFqdn) || (!hasAddr && !hasFqdn) { + return fmt.Errorf("InterfaceDescription[%d]: exactly one of ipv4Addr/ipv6Addr or fqdn must be set", i) + } + } + return nil +} + +func ConvertInterfaceDescriptionsToEndpoint(descs []InterfaceDescription) *OneOfTransportInfoEndpoint { + if len(descs) == 0 { + return nil + } + + // 1. Try to construct URI(s) when all required fields are available + var uris []string + allHaveHost := true + for _, desc := range descs { + var host string + switch { + case desc.Ipv4Addr != nil: + host = *desc.Ipv4Addr + case desc.Ipv6Addr != nil: + host = "[" + *desc.Ipv6Addr + "]" + case desc.Fqdn != nil: + host = *desc.Fqdn + default: + allHaveHost = false + } + + if host == "" { + allHaveHost = false + continue + } + + uri := "http://" + host + if desc.Port != nil { + uri += ":" + fmt.Sprintf("%d", *desc.Port) + } + if desc.ApiPrefix != nil { + uri += *desc.ApiPrefix + } + uris = append(uris, uri) + } + + if allHaveHost && len(uris) > 0 { + return &OneOfTransportInfoEndpoint{ + EndPointInfoUris: EndPointInfoUris{ + Uris: uris, + }, + } + } + + // 2. Try FQDN (without port) + var fqdns []string + for _, desc := range descs { + if desc.Fqdn != nil && desc.Port == nil { + fqdns = append(fqdns, *desc.Fqdn) + } + } + if len(fqdns) > 0 { + return &OneOfTransportInfoEndpoint{ + EndPointInfoFqdn: EndPointInfoFqdn{ + Fqdn: fqdns, + }, + } + } + + // 3. Fallback to addresses (host + port) + var addresses []EndPointInfoAddress + for _, desc := range descs { + if desc.Port == nil { + continue + } + + var host string + switch { + case desc.Ipv4Addr != nil: + host = *desc.Ipv4Addr + case desc.Ipv6Addr != nil: + host = *desc.Ipv6Addr + case desc.Fqdn != nil: + host = *desc.Fqdn + } + + if host != "" { + addresses = append(addresses, EndPointInfoAddress{ + Host: host, + Port: int32(*desc.Port), + }) + } + } + + if len(addresses) > 0 { + return &OneOfTransportInfoEndpoint{ + EndPointInfoAddresses: EndPointInfoAddresses{ + Addresses: addresses, + }, + } + } + + // 4. None matched: treat as bad input + return nil +} + +func ConvertEndpointToInterfaceDescriptions(endpoint *OneOfTransportInfoEndpoint) []InterfaceDescription { + if endpoint == nil { + return nil + } + + var descs []InterfaceDescription + + // 1. uris → try to extract apiPrefix and host/port info + if len(endpoint.Uris) > 0 { + for _, uriStr := range endpoint.Uris { + u, err := url.Parse(uriStr) + if err != nil || u.Host == "" { + continue + } + + host, portStr, _ := net.SplitHostPort(u.Host) + var port *int + if portStr != "" { + p, err := strconv.Atoi(portStr) + if err == nil { + port = &p + } + } else { + host = u.Host + } + + desc := InterfaceDescription{ + ApiPrefix: &u.Path, + Port: port, + } + + if ip := net.ParseIP(host); ip != nil { + if ip.To4() != nil { + desc.Ipv4Addr = &host + } else { + desc.Ipv6Addr = &host + } + } else { + desc.Fqdn = &host + } + + descs = append(descs, desc) + } + return descs + } + + // 2. fqdn (no port) + if len(endpoint.Fqdn) > 0 { + for _, fqdn := range endpoint.Fqdn { + desc := InterfaceDescription{ + Fqdn: &fqdn, + } + descs = append(descs, desc) + } + return descs + } + + // 3. addresses + if len(endpoint.Addresses) > 0 { + for _, addr := range endpoint.Addresses { + port := int(addr.Port) + desc := InterfaceDescription{ + Port: &port, + } + if ip := net.ParseIP(addr.Host); ip != nil { + if ip.To4() != nil { + desc.Ipv4Addr = &addr.Host + } else { + desc.Ipv6Addr = &addr.Host + } + } else { + desc.Fqdn = &addr.Host + } + descs = append(descs, desc) + } + return descs + } + + return nil +} diff --git a/go-apps/meep-app-enablement/server/mae_test.go b/go-apps/meep-app-enablement/server/mae_test.go index 2d4a3cffeecd17bd793417db3e208f4394635d05..148696840ae9dbe0934a07943cb3eb6252c5974d 100644 --- a/go-apps/meep-app-enablement/server/mae_test.go +++ b/go-apps/meep-app-enablement/server/mae_test.go @@ -50,7 +50,7 @@ import ( // const INITIAL = 0 // const UPDATED = 1 -//json format using spacing to facilitate reading +// json format using spacing to facilitate reading const testScenario string = ` { "version": "1.5.3", @@ -7628,10 +7628,11 @@ func TestAppServicesPOST(t *testing.T) { AefProfiles: []capif.AefProfile{ { AefId: "sandboxTransport", - Versions: []string{"3.1.1"}, - InterfaceDescriptions: &capif.OneOfTransportInfoEndpoint{ - EndPointInfoUris: capif.EndPointInfoUris{ - Uris: []string{"http://172.30.225.7/dacdasd/mep1/location/v3/"}, + Versions: []capif.Version{{APIVersion: "3.1.1"}}, + InterfaceDescriptions: []capif.InterfaceDescription{ + { + Ipv4Addr: func(s string) *string { return &s }("172.30.225.7"), + ApiPrefix: func(s string) *string { return &s }("/dacdasd/mep1/location/v3/"), }, }, VendorSpecificUrnetsimeccapifexttransportInfo: &capif.MecTransportInfoCapifExt{ @@ -7781,10 +7782,11 @@ func TestAppServicesPUT(t *testing.T) { AefProfiles: []capif.AefProfile{ { AefId: "sandboxTransport", - Versions: []string{"3.1.1"}, - InterfaceDescriptions: &capif.OneOfTransportInfoEndpoint{ - EndPointInfoUris: capif.EndPointInfoUris{ - Uris: []string{"http://172.30.225.7/dacdasd/mep1/location/v3/"}, + Versions: []capif.Version{{APIVersion: "3.1.1"}}, + InterfaceDescriptions: []capif.InterfaceDescription{ + { + Ipv4Addr: func(s string) *string { return &s }("172.30.225.7"), + ApiPrefix: func(s string) *string { return &s }("/dacdasd/mep1/location/v3/"), }, }, VendorSpecificUrnetsimeccapifexttransportInfo: &capif.MecTransportInfoCapifExt{ @@ -7888,10 +7890,11 @@ func TestAppServicesPATCH(t *testing.T) { AefProfiles: []capif.AefProfile{ { AefId: "sandboxTransport", - Versions: []string{"3.2.1"}, - InterfaceDescriptions: &capif.OneOfTransportInfoEndpoint{ - EndPointInfoUris: capif.EndPointInfoUris{ - Uris: []string{"http://172.30.225.7/dacdasd/mep1/location/v3/"}, + Versions: []capif.Version{{APIVersion: "3.1.1"}}, + InterfaceDescriptions: []capif.InterfaceDescription{ + { + Ipv4Addr: func(s string) *string { return &s }("172.30.225.7"), + ApiPrefix: func(s string) *string { return &s }("/dacdasd/mep1/location/v3/"), }, }, VendorSpecificUrnetsimeccapifexttransportInfo: &capif.MecTransportInfoCapifExt{ @@ -7917,6 +7920,20 @@ func TestAppServicesPATCH(t *testing.T) { }, } + // type ApiResponse struct { + // ApiName string `json:"apiName"` + // ApiId string `json:"apiId"` + // AefProfiles []struct { + // AefId string `json:"aefId"` + // Versions []string `json:"versions"` + // InterfaceDescriptions struct { + // Uris []string `json:"uris"` + // } `json:"interfaceDescriptions"` + // VendorSpecificTransportInfo map[string]interface{} `json:"vendorSpecific-urn:etsi:mec:capifext:transport-info"` + // } `json:"aefProfiles"` + // VendorSpecificServiceInfo map[string]interface{} `json:"vendorSpecific-urn:etsi:mec:capifext:service-info"` + // } + // Marshalling the request body to JSON format body, err := json.Marshal(requestBody) if err != nil { diff --git a/go-apps/meep-app-enablement/server/service-mgmt/service-mgmt.go b/go-apps/meep-app-enablement/server/service-mgmt/service-mgmt.go index 63ea3ab6fd4c476b04dfc3898ac140856476ad29..a3bf270a32dee3c6782964814462c93a3a1a6729 100644 --- a/go-apps/meep-app-enablement/server/service-mgmt/service-mgmt.go +++ b/go-apps/meep-app-enablement/server/service-mgmt/service-mgmt.go @@ -326,6 +326,10 @@ func appServicesPOST(w http.ResponseWriter, r *http.Request) { } } + if sInfoPost.TransportInfo.Security == nil { + sInfoPost.TransportInfo.Security = &SecurityInfo{} + } + // Create Service sInfo := &ServiceInfo{ SerInstanceId: uuid.New().String(), @@ -535,9 +539,24 @@ func appServicesByIdDELETE(w http.ResponseWriter, r *http.Request) { func appServicesGET(w http.ResponseWriter, r *http.Request) { log.Info("appServicesGET") + w.Header().Set("Content-Type", "application/json; charset=UTF-8") + + // Validate query parameters + u, _ := url.Parse(r.URL.String()) + q := u.Query() + validParams := []string{"ser_instance_id"} + err := validateQueryParams(q, validParams) + if err != nil { + errHandlerProblemDetails(w, err.Error(), http.StatusBadRequest) + return + } + serInstanceId := q["ser_instance_id"] + log.Info("appServicesGET: serInstanceId=", serInstanceId) + vars := mux.Vars(r) appId := vars["appInstanceId"] + log.Info("appServicesGET: appInstanceId=", appId) mutex.Lock() defer mutex.Unlock() @@ -567,10 +586,14 @@ func appServicesGET(w http.ResponseWriter, r *http.Request) { func appServicesByIdGET(w http.ResponseWriter, r *http.Request) { log.Info("appServicesByIdGET") + w.Header().Set("Content-Type", "application/json; charset=UTF-8") + vars := mux.Vars(r) svcId := vars["serviceId"] + log.Info("appServicesByIdGET: svcId=", svcId) appId := vars["appInstanceId"] + log.Info("appServicesByIdGET: appId=", appId) mutex.Lock() defer mutex.Unlock() @@ -1624,11 +1647,15 @@ func getAppInfo(appId string) (map[string]string, error) { } func getAppInfoAnyMep(appId string) (map[string]string, error) { + log.Debug(">>> getAppInfoAnyMep: appId=", appId) + var appInfoList []map[string]string // Get app instance from any MEP keyMatchStr := baseKeyAnyMep + "app:" + appId + ":info" err := rc.ForEachEntry(keyMatchStr, populateAppInfo, &appInfoList) + log.Debug("getAppInfoAnyMep: len(appInfoList)=", len(appInfoList)) + log.Debug("getAppInfoAnyMep: appInfoList=", appInfoList) if err != nil || len(appInfoList) != 1 { return nil, errors.New("App Instance not found") } diff --git a/go-apps/meep-dai/server/dai.go b/go-apps/meep-dai/server/dai.go index e1f2dcd571c7898ff9808c95482672612414f137..5840e2a4605ccd25b5b907659be955baf136ad6f 100644 --- a/go-apps/meep-dai/server/dai.go +++ b/go-apps/meep-dai/server/dai.go @@ -608,6 +608,13 @@ func meAppListGET(w http.ResponseWriter, r *http.Request) { vendorId := q["vendorId"] serviceCont := q["serviceCont"] + validParams := []string{"appName", "appProvider", "appSoftVersion", "vendorId", "serviceCont"} + err := validateQueryParams(q, validParams) + if err != nil { + errHandlerProblemDetails(w, err.Error(), http.StatusBadRequest) + return + } + // log.Debug("meAppListGET: appName: ", appName) // log.Debug("meAppListGET: appProvider: ", appProvider) // log.Debug("meAppListGET: appSoftVersion: ", appSoftVersion) @@ -710,6 +717,35 @@ func devAppContextsPOST(w http.ResponseWriter, r *http.Request) { } log.Info("devAppContextsPOST: ", appContext) + // Sanity checks + if appContext.ContextId != "" { // ETSI GS MEC 016 Clause 6.2.3 Type: AppContext. + err = errors.New("ContextId shall not be set") + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + if appContext.AssociateDevAppId == "" { // ETSI GS MEC 016 Clause 6.2.3 Type: AppContext. + err = errors.New("Missing AssociateDevAppId") + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + if len(appContext.AppInfo.UserAppInstanceInfo) == 0 { // ETSI GS MEC 016 Clause 6.2.3 Type: AppContext. + err = errors.New("Missing at least one UserAppInstanceInfo item") + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + for _, item := range appContext.AppInfo.UserAppInstanceInfo { + if item.AppInstanceId != "" { + err = errors.New("UserAppInstanceInfo.AppInstanceId shall not be set") + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + if item.ReferenceURI != "" { + err = errors.New("UserAppInstanceInfo.ReferenceURI shall not be set") + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + } // End of 'for' statement + // Create the AppContext var appContextSbi meepdaimgr.AppContext appContextSbi.AppAutoInstantiation = appContext.AppAutoInstantiation @@ -789,7 +825,7 @@ func devAppContextDELETE(w http.ResponseWriter, r *http.Request) { err := sbi.DeleteAppContext(contextId) if err != nil { log.Error(err.Error()) - errHandlerProblemDetails(w, err.Error(), http.StatusInternalServerError) + errHandlerProblemDetails(w, err.Error(), http.StatusNotFound) return } @@ -817,7 +853,7 @@ func devAppContextPUT(w http.ResponseWriter, r *http.Request) { if appContext.ContextId != contextId { err = errors.New("ContextId mismatch") log.Error(err.Error()) - http.Error(w, err.Error(), http.StatusInternalServerError) + http.Error(w, err.Error(), http.StatusBadRequest) return } @@ -827,7 +863,7 @@ func devAppContextPUT(w http.ResponseWriter, r *http.Request) { if appContext.AppInfo == nil { err = errors.New("AppInfo shall be present") log.Error(err.Error()) - http.Error(w, err.Error(), http.StatusInternalServerError) + http.Error(w, err.Error(), http.StatusBadRequest) return } appContextSbi.AppInfo.AppDId = appContext.AppInfo.AppDId @@ -893,13 +929,13 @@ func appLocationAvailabilityPOST(w http.ResponseWriter, r *http.Request) { if applicationLocationAvailability.AppInfo == nil { err = errors.New("AppInfo mismatch") log.Error(err.Error()) - http.Error(w, err.Error(), http.StatusInternalServerError) + http.Error(w, err.Error(), http.StatusBadRequest) return } if applicationLocationAvailability.AppInfo.AppPackageSource == "" { // Check presence of the filed AppPackageSource in te request err = errors.New("AppPackageSource mismatch") log.Error(err.Error()) - http.Error(w, err.Error(), http.StatusInternalServerError) + http.Error(w, err.Error(), http.StatusBadRequest) return } @@ -917,7 +953,7 @@ func appLocationAvailabilityPOST(w http.ResponseWriter, r *http.Request) { applicationLocationAvailabilitySbi_, err := sbi.PosApplicationLocationAvailability(&applicationLocationAvailabilitySbi) if err != nil { log.Error(err.Error()) - errHandlerProblemDetails(w, err.Error(), http.StatusInternalServerError) + errHandlerProblemDetails(w, err.Error(), http.StatusNotFound) return } @@ -946,7 +982,7 @@ func appLocationAvailabilityPOST(w http.ResponseWriter, r *http.Request) { log.Debug("devAppContextsPOST: *(*item.AppLocation).CountryCode: ", *(*item.AppLocation).CountryCode) applicationLocationAvailability.AppInfo.AvailableLocations[i].AppLocation.CountryCode = *(*item.AppLocation).CountryCode } - log.Debug("devAppContextsPOST: applicationLocationAvailability.AppInfo.AvailableLocations[i].AppLocation: ", applicationLocationAvailability.AppInfo.AvailableLocations[i].AppLocation) + log.Debug("devAppContextsPOST: applicationLocationAvailability.AppInfo.AvailableLocations[", i, "].AppLocation: ", applicationLocationAvailability.AppInfo.AvailableLocations[i].AppLocation) } // End of 'for' statement } log.Debug("devAppContextsPOST: applicationLocationAvailability.AppInfo.AvailableLocations: ", applicationLocationAvailability.AppInfo.AvailableLocations) @@ -956,10 +992,8 @@ func appLocationAvailabilityPOST(w http.ResponseWriter, r *http.Request) { log.Info("json response: ", jsonResponse) w.Header().Set("Content-Type", "application/json; charset=UTF-8") - w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, string(jsonResponse)) - w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, string(jsonResponse)) } func notifyAppContextDeletion(notifyUrl string, contextId string) { @@ -996,3 +1030,21 @@ func errHandlerProblemDetails(w http.ResponseWriter, error string, code int) { w.WriteHeader(code) fmt.Fprint(w, jsonResponse) } + +func validateQueryParams(params url.Values, validParams []string) error { + for param := range params { + found := false + for _, validParam := range validParams { + if param == validParam { + found = true + break + } + } + if !found { + err := errors.New("Invalid query param: " + param) + log.Error(err.Error()) + return err + } + } + return nil +} diff --git a/go-apps/meep-federation/Dockerfile b/go-apps/meep-federation/Dockerfile index 5db45a03e92c5c1b933b1cc434293cce54abcb41..950b2f8ae742ebfd7aed7b280b7333902c77efc7 100644 --- a/go-apps/meep-federation/Dockerfile +++ b/go-apps/meep-federation/Dockerfile @@ -21,7 +21,7 @@ COPY ./data / RUN chmod +x /entrypoint.sh RUN apt-get update \ - && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends ca-certificates + && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends ca-certificates curl jq RUN dpkg --configure -a diff --git a/go-apps/meep-federation/entrypoint.sh b/go-apps/meep-federation/entrypoint.sh index 7856bbaa070a270152b91059b9c9f24eb1b0f840..ea13a6105bf77bdfee9ca3aed9b06f7f7e78ea37 100755 --- a/go-apps/meep-federation/entrypoint.sh +++ b/go-apps/meep-federation/entrypoint.sh @@ -5,6 +5,7 @@ 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 "WEBSOCK_ETPATH: ${WEBSOCK_ETPATH}" if [[ ! -z "${MEEP_MEP_NAME}" ]]; then svcPath="${MEEP_SANDBOX_NAME}/${MEEP_MEP_NAME}" @@ -12,6 +13,9 @@ else svcPath="${MEEP_SANDBOX_NAME}" fi +WEBSOCK_ETPATH="/$MEEP_SANDBOX_NAME"${WEBSOCK_ETPATH:-"/monaco-telecom/meep-cloud-mosquitto"} +MEEP_BROKER="wss://${MEEP_HOST_URL#https://}:443$WEBSOCK_ETPATH" + # Update API yaml basepaths to enable "Try-it-out" feature # OAS2: Set relative path to sandbox name + endpoint path (origin will be derived from browser URL) # OAS3: Set full path to provided Host URL + sandbox name + endpoint path @@ -38,6 +42,14 @@ for file in /user-api/*; do setBasepath "$file" done +echo "Environment variables set:" +echo "MEEP_HOST_URL: ${MEEP_HOST_URL}" +echo "MEEP_SANDBOX_NAME: ${MEEP_SANDBOX_NAME}" +echo "MEEP_MEP_NAME: ${MEEP_MEP_NAME}" +echo "MEEP_INSTANCE_ID: ${MEEP_INSTANCE_ID}" +echo "MEEP_BROKER: ${MEEP_BROKER}" +echo "MEEP_TOPIC: ${MEEP_TOPIC}" + # Start service currenttime=`date "+%Y%m%d-%H%M%S"` filepath="/codecov/codecov-meep-federation-" diff --git a/go-apps/meep-federation/go.mod b/go-apps/meep-federation/go.mod index 5e96e91fd29ad91583de2330ef475cb7343a5db6..92ec1b8047b541d049d435a4806e4bec9211ed9b 100644 --- a/go-apps/meep-federation/go.mod +++ b/go-apps/meep-federation/go.mod @@ -19,7 +19,7 @@ require ( github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-subscriptions v0.0.0 github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-swagger-api-mgr v0.0.0 github.com/antihax/optional v1.0.0 // indirect - github.com/google/uuid v1.2.0 + github.com/google/uuid v1.6.0 github.com/gorilla/handlers v1.5.1 github.com/gorilla/mux v1.8.0 github.com/prometheus/client_golang v1.9.0 diff --git a/go-apps/meep-federation/go.sum b/go-apps/meep-federation/go.sum index 85f587dbfbf507be314535800199a4af25063d62..a509b3bd07b37c60829f42fa76b53fec90af7d5c 100644 --- a/go-apps/meep-federation/go.sum +++ b/go-apps/meep-federation/go.sum @@ -120,6 +120,8 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4 github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= diff --git a/go-apps/meep-federation/sbi/federation-sbi.go b/go-apps/meep-federation/sbi/federation-sbi.go index 73816f1260642dbd44bfdfcc8982fb3573532429..f4289b61201f3f8e34f9efe03107db5c275d78c6 100644 --- a/go-apps/meep-federation/sbi/federation-sbi.go +++ b/go-apps/meep-federation/sbi/federation-sbi.go @@ -20,6 +20,7 @@ import ( "context" "encoding/json" "errors" + "strings" "sync" fm "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-federation-mgr" @@ -322,7 +323,7 @@ func RegisterSystemInfo(requestData SystemInfo) (responseData SystemInfo, err er log.Error(err) return responseData, err } - err = sbi.federationMgr.PublishMessageOnMessageBroker("", requestData.SystemId) // Remove emtry from brocker + err = sbi.federationMgr.PublishMessageOnMessageBroker(s, requestData.SystemId) // Remove emtry from brocker if err != nil { log.Error("Failed to publish message broker server: ", err) return responseData, err @@ -413,7 +414,20 @@ func GetMecServices(host string) (svcs []smc.ServiceInfo, err error) { } log.Info("GetMecServices: svcs: ", svcs) - return svcs, nil + // Extract the services from the specified host + var l []smc.ServiceInfo = []smc.ServiceInfo{} + for _, v := range svcs { + //log.Debug("GetMecServices: processing: ", v) + if v.Links != nil && v.Links.Self != nil { + //log.Debug("GetMecServices: processing: v._links=", *v.Links.Self) + if strings.Contains(v.Links.Self.Href, host) { + l = append(l, v) + } + } + } // End of 'for' statement + //log.Debug("GetMecServices: processing: l=", l) + + return l, nil } func convertSystemInfotoJson(systemInfo *SystemInfo) string { diff --git a/go-apps/meep-federation/server/federation.go b/go-apps/meep-federation/server/federation.go index d445a983a48dda05870df67de5592ed5efc30421..d7a5374f384d0032a065a115a3fd76cceeb80b79 100644 --- a/go-apps/meep-federation/server/federation.go +++ b/go-apps/meep-federation/server/federation.go @@ -174,18 +174,33 @@ func registerService(appInstanceId string) error { return nil } -func getMecServices() ([]smc.ServiceInfo, error) { +func getMecServices(host string) ([]smc.ServiceInfo, error) { + log.Info(">>> getMecServices: ", host) + if svcMgmtClient == nil { err := errors.New("App Enablement Service Management REST API client not set") log.Error(err.Error()) return nil, err } - appServicesResponse, _, err := svcMgmtClient.MecServiceMgmtApi.ServicesGET(context.TODO(), nil) + svcs, _, err := svcMgmtClient.MecServiceMgmtApi.ServicesGET(context.TODO(), nil) if err != nil { log.Error("Failed to retrieve mec services on platform ", err) return nil, err } - return appServicesResponse, nil + // Extract the services from the specified host + var l []smc.ServiceInfo = []smc.ServiceInfo{} + for _, v := range svcs { + //log.Debug("GetMecServices: processing: ", v) + if v.Links != nil && v.Links.Self != nil { + //log.Debug("GetMecServices: processing: v._links=", *v.Links.Self) + if strings.Contains(v.Links.Self.Href, host) { + l = append(l, v) + } + } + } // End of 'for' statement + //log.Debug("GetMecServices: processing: l=", l) + + return l, nil } func sendReadyConfirmation(appInstanceId string) error { @@ -819,11 +834,10 @@ func systeminfoGET(w http.ResponseWriter, r *http.Request) { mutex.Lock() defer mutex.Unlock() - // Parse query parameters + // Validate query parameters u, _ := url.Parse(r.URL.String()) q := u.Query() log.Debug("systeminfoGET: q: ", q) - validParams := []string{"systemId", "systemName", "systemProvider"} // If no query parameters are provided, return all systemInfo if len(q) == 0 { log.Debug("systeminfoGET: No query parameters provided, returning all systemInfo") @@ -853,6 +867,7 @@ func systeminfoGET(w http.ResponseWriter, r *http.Request) { return } // Validate query parameters + validParams := []string{"systemId", "systemName", "systemProvider"} err := validateQueryParams(q, validParams) if err != nil { errHandlerProblemDetails(w, err.Error(), http.StatusBadRequest) @@ -867,84 +882,76 @@ func systeminfoGET(w http.ResponseWriter, r *http.Request) { systemId := q["systemId"] systemName := q["systemName"] systemProvider := q["systemProvider"] - + filter := len(systemId) != 0 || len(systemName) != 0 || len(systemProvider) != 0 log.Debug("systeminfoGET: systemId: ", systemId) log.Debug("systeminfoGET: systemName: ", systemName) log.Debug("systeminfoGET: systemProvider: ", systemProvider) + log.Debug("systeminfoGET: filter: ", filter) + log.Debug("systeminfoGET: systemInfopMap: ", systemInfopMap) - // Filter systemInfo based on query parameters (AND logic) - var filteredSystemInfos []sbi.SystemInfo - for _, val := range systemInfopMap { - log.Debug("systeminfoGET: processing ", val) - shouldInclude := true - // Check systemId filter - must match if provided - if len(systemId) > 0 { - matchFound := false - for _, id := range systemId { - if id == val.SystemId { - log.Debug("systeminfoGET: Apply filter on systemId") - matchFound = true - break + if len(systemInfopMap) == 0 { + err = errors.New("No systemInfo registered") + errHandlerProblemDetails(w, err.Error(), http.StatusNotFound) + return + } else { + l := []sbi.SystemInfo{} + for _, val := range systemInfopMap { + log.Debug("systeminfoGET: processing ", val) + if !filter { + l = append(l, val) + } else { + if len(systemId) != 0 { + for _, v := range systemId { + if v == val.SystemId { + log.Debug("systeminfoGET: Apply filter on systemId") + l = append(l, val) + break + } + } } - } - if !matchFound { - shouldInclude = false - } - } - // Check systemName filter - must match if provided (AND logic) - if shouldInclude && len(systemName) > 0 { - matchFound := false - for _, name := range systemName { - if name == val.SystemName { - log.Debug("systeminfoGET: Apply filter on systemName") - matchFound = true - break + if len(systemName) != 0 { + for _, v := range systemName { + if v == val.SystemName { + log.Debug("systeminfoGET: Apply filter on systemName") + l = append(l, val) + break + } + } } - } - if !matchFound { - shouldInclude = false - } - } - // Check systemProvider filter - must match if provided (AND logic) - if shouldInclude && len(systemProvider) > 0 { - matchFound := false - for _, provider := range systemProvider { - if provider == val.SystemProvider { - log.Debug("systeminfoGET: Apply filter on systemProvider") - matchFound = true - break + if len(systemProvider) != 0 { + for _, v := range systemProvider { + if v == val.SystemProvider { + log.Debug("systeminfoGET: Apply filter on systemProvider") + l = append(l, val) + break + } + } } } - if !matchFound { - shouldInclude = false - } } - if shouldInclude { - filteredSystemInfos = append(filteredSystemInfos, val) + if len(l) == 0 { + err = errors.New("No systemInfo registered with applied filter") + errHandlerProblemDetails(w, err.Error(), http.StatusBadRequest) + return } + // Convert to response format + var systemInfos = []SystemInfo{} + for _, val := range l { + systemInfos = append(systemInfos, SystemInfo{ + SystemId: val.SystemId, + SystemName: val.SystemName, + SystemProvider: val.SystemProvider, + }) + } + // Marshal and send response + jsonResponse, err := json.Marshal(systemInfos) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + w.WriteHeader(http.StatusOK) + fmt.Fprint(w, string(jsonResponse)) } - // Check if any results were found - if len(filteredSystemInfos) == 0 { - errHandlerProblemDetails(w, "No systemInfo found matching the specified criteria", http.StatusNotFound) - return - } - // Convert to response format - var systemInfos = []SystemInfo{} - for _, val := range filteredSystemInfos { - systemInfos = append(systemInfos, SystemInfo{ - SystemId: val.SystemId, - SystemName: val.SystemName, - SystemProvider: val.SystemProvider, - }) - } - // Marshal and send response - jsonResponse, err := json.Marshal(systemInfos) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - w.WriteHeader(http.StatusOK) - fmt.Fprint(w, string(jsonResponse)) } func systeminfoPOST(w http.ResponseWriter, r *http.Request) { @@ -1000,6 +1007,10 @@ func systeminfoPOST(w http.ResponseWriter, r *http.Request) { systemInfopMap[responseData.SystemId] = responseData log.Debug("systeminfoPOST: new systemInfopMap: ", systemInfopMap) + // Populate localSystemInfo + localSystemInfo = responseData + log.Debug("systeminfoPOST: new localSystemInfo: ", localSystemInfo) + // Prepare & send response var l = SystemInfo{ SystemId: responseData.SystemId, @@ -1337,7 +1348,7 @@ func serviceGET(w http.ResponseWriter, r *http.Request) { return } else if localSystemInfo.SystemId == systemIdParamStr { // Local request log.Debug("servicesGET: Local request: ", systemIdParamStr) - services, err = getMecServices() + services, err = getMecServices(localSystemInfo.Host) if err != nil { errHandlerProblemDetails(w, err.Error(), http.StatusInternalServerError) return @@ -1431,6 +1442,9 @@ func servicesGET(w http.ResponseWriter, r *http.Request) { systemIdParamStr := vars["systemId"] log.Debug("servicesGET: systemIdParamStr: ", systemIdParamStr) + log.Debug("servicesGET: systemInfopMap: ", systemInfopMap) + log.Debug("servicesGET: localSystemInfo: ", localSystemInfo) + val, ok := systemInfopMap[systemIdParamStr] var services []smc.ServiceInfo var err error @@ -1441,7 +1455,7 @@ func servicesGET(w http.ResponseWriter, r *http.Request) { return } else if localSystemInfo.SystemId == systemIdParamStr { // Local request log.Debug("servicesGET: Local request: ", systemIdParamStr) - services, err = getMecServices() + services, err = getMecServices(localSystemInfo.Host) if err != nil { errHandlerProblemDetails(w, err.Error(), http.StatusInternalServerError) return diff --git a/go-apps/meep-iot/sbi/iot-sbi.go b/go-apps/meep-iot/sbi/iot-sbi.go index 253f899a014901c9bd1e0491163325cf1731594f..749ba57905abc856388b267464a3731e3cb5447c 100644 --- a/go-apps/meep-iot/sbi/iot-sbi.go +++ b/go-apps/meep-iot/sbi/iot-sbi.go @@ -134,12 +134,19 @@ type TrafficRuleDescriptor struct { type InterfaceDescriptor struct { InterfaceType string - //TunnelInfo *TunnelInfo FSCOM Not supported + TunnelInfo *TunnelInfo SrcMACAddress string DstMACAddress string DstIPAddress string } +type TunnelInfo struct { + TunnelType string + TunnelDstAddress string + TunnelSrcAddress string + TunnelSpecificData string +} + type TrafficFilter struct { SrcAddress []string DstAddress []string @@ -154,6 +161,7 @@ type TrafficFilter struct { SrcTunnelPort []string DstTunnelPort []string QCI int32 + DSCP int32 TC int32 } @@ -162,24 +170,61 @@ type KeyValuePair struct { Value string } +type DownlinkInfo struct { + DownlinkTopic string + DevicePort int32 +} + +type DeviceSpecificMessageFormats struct { + EventMsgFormat *EventMsg + UplinkMsgFormat *UplinkMsg +} + +type EventMsg struct { + EventTopic string + SelectedSerializer *string + IncludeDeviceAddr bool + IncludeDeviceMetadata bool + IncludePei bool + IncludeSupi bool + IncludeImei bool + IncludeImsi bool + IncludeIccid bool + IncludeDeviceId bool +} + +type UplinkMsg struct { + UplinkTopic string + SelectedSerializer *string + IncludeDevicePort bool + IncludeDeviceAddr bool + IncludeDeviceMetadata bool + IncludePei bool + IncludeSupi bool + IncludeImei bool + IncludeImsi bool + IncludeIccid bool + IncludeDeviceId bool +} + 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 + 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 } // Init - IOT Service SBI initialization @@ -545,7 +590,101 @@ func convertDeviceInfoFromIotMgr(dev tm.DeviceInfo) (device DeviceInfo) { device.DeviceMetadata[i] = KeyValuePair{Key: k.Key, Value: k.Value} } // End of 'for' statement } - // FIXME FSCOM Add missing fileds (pointers & arrays) + if len(dev.RequestedMecTrafficRule) != 0 { + device.RequestedMecTrafficRule = make([]TrafficRuleDescriptor, len(dev.RequestedMecTrafficRule)) + for i, v := range dev.RequestedMecTrafficRule { + device.RequestedMecTrafficRule[i] = TrafficRuleDescriptor{ + TrafficRuleId: v.TrafficRuleId, + FilterType: v.FilterType, + Priority: v.Priority, + Action: v.Action, + } + if len(dev.RequestedMecTrafficRule[i].TrafficFilter) != 0 { + device.RequestedMecTrafficRule[i].TrafficFilter = make([]TrafficFilter, len(dev.RequestedMecTrafficRule[i].TrafficFilter)) + for j, u := range dev.RequestedMecTrafficRule[i].TrafficFilter { + device.RequestedMecTrafficRule[i].TrafficFilter[j] = TrafficFilter{ + SrcAddress: u.SrcAddress, + DstAddress: u.DstAddress, + SrcPort: u.SrcPort, + DstPort: u.DstPort, + Protocol: u.Protocol, + Tag: u.Tag, + Uri: u.Uri, + PacketLabel: u.PacketLabel, + SrcTunnelAddress: u.SrcTunnelAddress, + TgtTunnelAddress: u.TgtTunnelAddress, + SrcTunnelPort: u.SrcTunnelPort, + DstTunnelPort: u.DstTunnelPort, + QCI: u.QCI, + DSCP: u.DSCP, + TC: u.TC, + } + } // End of 'for' statement + } + if v.DstInterface != nil { + device.RequestedMecTrafficRule[i].DstInterface = &InterfaceDescriptor{ + InterfaceType: v.DstInterface.InterfaceType, + SrcMACAddress: v.DstInterface.SrcMACAddress, + DstMACAddress: v.DstInterface.DstMACAddress, + DstIPAddress: v.DstInterface.DstIPAddress, + } + if v.DstInterface.TunnelInfo != nil { + device.RequestedMecTrafficRule[i].DstInterface.TunnelInfo = &TunnelInfo{ + TunnelType: v.DstInterface.TunnelInfo.TunnelType, + TunnelDstAddress: v.DstInterface.TunnelInfo.TunnelDstAddress, + TunnelSrcAddress: v.DstInterface.TunnelInfo.TunnelSrcAddress, + TunnelSpecificData: v.DstInterface.TunnelInfo.TunnelSpecificData, + } + } + } + } // End of 'for' statement + } + if dev.DeviceSpecificMessageFormats != nil { + var deviceSpecificMessageFormats = DeviceSpecificMessageFormats{} + if dev.DeviceSpecificMessageFormats.EventMsgFormat != nil { + deviceSpecificMessageFormats.EventMsgFormat = &EventMsg{ + EventTopic: dev.DeviceSpecificMessageFormats.EventMsgFormat.EventTopic, + IncludeDeviceAddr: dev.DeviceSpecificMessageFormats.EventMsgFormat.IncludeDeviceAddr, + IncludeDeviceMetadata: dev.DeviceSpecificMessageFormats.EventMsgFormat.IncludeDeviceMetadata, + IncludePei: dev.DeviceSpecificMessageFormats.EventMsgFormat.IncludePei, + IncludeSupi: dev.DeviceSpecificMessageFormats.EventMsgFormat.IncludeSupi, + IncludeImei: dev.DeviceSpecificMessageFormats.EventMsgFormat.IncludeImei, + IncludeImsi: dev.DeviceSpecificMessageFormats.EventMsgFormat.IncludeImsi, + IncludeIccid: dev.DeviceSpecificMessageFormats.EventMsgFormat.IncludeIccid, + IncludeDeviceId: dev.DeviceSpecificMessageFormats.EventMsgFormat.IncludeDeviceId, + } + if dev.DeviceSpecificMessageFormats.EventMsgFormat.SelectedSerializer != nil { + s := *dev.DeviceSpecificMessageFormats.EventMsgFormat.SelectedSerializer + deviceSpecificMessageFormats.EventMsgFormat.SelectedSerializer = &s + } + } + if dev.DeviceSpecificMessageFormats.UplinkMsgFormat != nil { + deviceSpecificMessageFormats.UplinkMsgFormat = &UplinkMsg{ + UplinkTopic: dev.DeviceSpecificMessageFormats.UplinkMsgFormat.UplinkTopic, + IncludeDevicePort: dev.DeviceSpecificMessageFormats.UplinkMsgFormat.IncludeDevicePort, + IncludeDeviceAddr: dev.DeviceSpecificMessageFormats.UplinkMsgFormat.IncludeDeviceAddr, + IncludeDeviceMetadata: dev.DeviceSpecificMessageFormats.UplinkMsgFormat.IncludeDeviceMetadata, + IncludePei: dev.DeviceSpecificMessageFormats.UplinkMsgFormat.IncludePei, + IncludeSupi: dev.DeviceSpecificMessageFormats.UplinkMsgFormat.IncludeSupi, + IncludeImei: dev.DeviceSpecificMessageFormats.UplinkMsgFormat.IncludeImei, + IncludeImsi: dev.DeviceSpecificMessageFormats.UplinkMsgFormat.IncludeImsi, + IncludeIccid: dev.DeviceSpecificMessageFormats.UplinkMsgFormat.IncludeIccid, + IncludeDeviceId: dev.DeviceSpecificMessageFormats.UplinkMsgFormat.IncludeDeviceId, + } + if dev.DeviceSpecificMessageFormats.UplinkMsgFormat.SelectedSerializer != nil { + s := *dev.DeviceSpecificMessageFormats.UplinkMsgFormat.SelectedSerializer + deviceSpecificMessageFormats.UplinkMsgFormat.SelectedSerializer = &s + } + } + device.DeviceSpecificMessageFormats = &deviceSpecificMessageFormats + } + if dev.DownlinkInfo != nil { + device.DownlinkInfo = &DownlinkInfo{ + DownlinkTopic: dev.DownlinkInfo.DownlinkTopic, + DevicePort: dev.DownlinkInfo.DevicePort, + } + } + //log.Debug("convertDeviceInfoFromIotMgr: device: ", device) return device @@ -587,7 +726,100 @@ func convertDeviceInfoToIotMgr(dev DeviceInfo) (device tm.DeviceInfo) { device.DeviceMetadata[i] = tm.KeyValuePair{Key: k.Key, Value: k.Value} } // End of 'for' statement } - // FIXME FSCOM Add missing fileds (pointers & arrays) + if len(dev.RequestedMecTrafficRule) != 0 { + device.RequestedMecTrafficRule = make([]tm.TrafficRuleDescriptor, len(dev.RequestedMecTrafficRule)) + for i, v := range dev.RequestedMecTrafficRule { + device.RequestedMecTrafficRule[i] = tm.TrafficRuleDescriptor{ + TrafficRuleId: v.TrafficRuleId, + FilterType: v.FilterType, + Priority: v.Priority, + Action: v.Action, + } + if len(dev.RequestedMecTrafficRule[i].TrafficFilter) != 0 { + device.RequestedMecTrafficRule[i].TrafficFilter = make([]tm.TrafficFilter, len(dev.RequestedMecTrafficRule[i].TrafficFilter)) + for j, u := range dev.RequestedMecTrafficRule[i].TrafficFilter { + device.RequestedMecTrafficRule[i].TrafficFilter[j] = tm.TrafficFilter{ + SrcAddress: u.SrcAddress, + DstAddress: u.DstAddress, + SrcPort: u.SrcPort, + DstPort: u.DstPort, + Protocol: u.Protocol, + Tag: u.Tag, + Uri: u.Uri, + PacketLabel: u.PacketLabel, + SrcTunnelAddress: u.SrcTunnelAddress, + TgtTunnelAddress: u.TgtTunnelAddress, + SrcTunnelPort: u.SrcTunnelPort, + DstTunnelPort: u.DstTunnelPort, + QCI: u.QCI, + DSCP: u.DSCP, + TC: u.TC, + } + } // End of 'for' statement + } + if v.DstInterface != nil { + device.RequestedMecTrafficRule[i].DstInterface = &tm.InterfaceDescriptor{ + InterfaceType: v.DstInterface.InterfaceType, + SrcMACAddress: v.DstInterface.SrcMACAddress, + DstMACAddress: v.DstInterface.DstMACAddress, + DstIPAddress: v.DstInterface.DstIPAddress, + } + if v.DstInterface.TunnelInfo != nil { + device.RequestedMecTrafficRule[i].DstInterface.TunnelInfo = &tm.TunnelInfo{ + TunnelType: v.DstInterface.TunnelInfo.TunnelType, + TunnelDstAddress: v.DstInterface.TunnelInfo.TunnelDstAddress, + TunnelSrcAddress: v.DstInterface.TunnelInfo.TunnelSrcAddress, + TunnelSpecificData: v.DstInterface.TunnelInfo.TunnelSpecificData, + } + } + } + } // End of 'for' statement + } + if dev.DeviceSpecificMessageFormats != nil { + var deviceSpecificMessageFormats = tm.DeviceSpecificMessageFormats{} + if dev.DeviceSpecificMessageFormats.EventMsgFormat != nil { + deviceSpecificMessageFormats.EventMsgFormat = &tm.EventMsg{ + EventTopic: dev.DeviceSpecificMessageFormats.EventMsgFormat.EventTopic, + IncludeDeviceAddr: dev.DeviceSpecificMessageFormats.EventMsgFormat.IncludeDeviceAddr, + IncludeDeviceMetadata: dev.DeviceSpecificMessageFormats.EventMsgFormat.IncludeDeviceMetadata, + IncludePei: dev.DeviceSpecificMessageFormats.EventMsgFormat.IncludePei, + IncludeSupi: dev.DeviceSpecificMessageFormats.EventMsgFormat.IncludeSupi, + IncludeImei: dev.DeviceSpecificMessageFormats.EventMsgFormat.IncludeImei, + IncludeImsi: dev.DeviceSpecificMessageFormats.EventMsgFormat.IncludeImsi, + IncludeIccid: dev.DeviceSpecificMessageFormats.EventMsgFormat.IncludeIccid, + IncludeDeviceId: dev.DeviceSpecificMessageFormats.EventMsgFormat.IncludeDeviceId, + } + if dev.DeviceSpecificMessageFormats.EventMsgFormat.SelectedSerializer != nil { + s := *dev.DeviceSpecificMessageFormats.EventMsgFormat.SelectedSerializer + deviceSpecificMessageFormats.EventMsgFormat.SelectedSerializer = &s + } + } + if dev.DeviceSpecificMessageFormats.UplinkMsgFormat != nil { + deviceSpecificMessageFormats.UplinkMsgFormat = &tm.UplinkMsg{ + UplinkTopic: dev.DeviceSpecificMessageFormats.UplinkMsgFormat.UplinkTopic, + IncludeDevicePort: dev.DeviceSpecificMessageFormats.UplinkMsgFormat.IncludeDevicePort, + IncludeDeviceAddr: dev.DeviceSpecificMessageFormats.UplinkMsgFormat.IncludeDeviceAddr, + IncludeDeviceMetadata: dev.DeviceSpecificMessageFormats.UplinkMsgFormat.IncludeDeviceMetadata, + IncludePei: dev.DeviceSpecificMessageFormats.UplinkMsgFormat.IncludePei, + IncludeSupi: dev.DeviceSpecificMessageFormats.UplinkMsgFormat.IncludeSupi, + IncludeImei: dev.DeviceSpecificMessageFormats.UplinkMsgFormat.IncludeImei, + IncludeImsi: dev.DeviceSpecificMessageFormats.UplinkMsgFormat.IncludeImsi, + IncludeIccid: dev.DeviceSpecificMessageFormats.UplinkMsgFormat.IncludeIccid, + IncludeDeviceId: dev.DeviceSpecificMessageFormats.UplinkMsgFormat.IncludeDeviceId, + } + if dev.DeviceSpecificMessageFormats.UplinkMsgFormat.SelectedSerializer != nil { + s := *dev.DeviceSpecificMessageFormats.UplinkMsgFormat.SelectedSerializer + deviceSpecificMessageFormats.UplinkMsgFormat.SelectedSerializer = &s + } + } + device.DeviceSpecificMessageFormats = &deviceSpecificMessageFormats + } + if dev.DownlinkInfo != nil { + device.DownlinkInfo = &tm.DownlinkInfo{ + DownlinkTopic: dev.DownlinkInfo.DownlinkTopic, + DevicePort: dev.DownlinkInfo.DevicePort, + } + } //log.Debug("convertDeviceInfoToIotMgr: device: ", device) return device diff --git a/go-apps/meep-iot/server/meep-iot.go b/go-apps/meep-iot/server/meep-iot.go index aee94cfe55def0901b910fed01ab262d8de7ddac..f6702f86b5d054e2dfa44e5c4ce01f973e6569fd 100644 --- a/go-apps/meep-iot/server/meep-iot.go +++ b/go-apps/meep-iot/server/meep-iot.go @@ -711,6 +711,7 @@ func registerediotplatformsPOST(w http.ResponseWriter, r *http.Request) { log.Debug(">>> registerediotplatformsPOST: ", r) w.Header().Set("Content-Type", "application/json; charset=UTF-8") + var requestData IotPlatformInfo decoder := json.NewDecoder(r.Body) err := decoder.Decode(&requestData) @@ -744,7 +745,12 @@ func registerediotplatformsPOST(w http.ResponseWriter, r *http.Request) { errHandlerProblemDetails(w, "Mandatory attribute Name shall be absent in the request body.", http.StatusBadRequest) return } - if v.Type_ == nil || *v.Type_ != "MB_TOPIC_BASED" { + if v.Type_ == nil { + log.Error("Mandatory Type_ parameter shall be present") + errHandlerProblemDetails(w, "Mandatory attribute Type_ shall be present.", http.StatusBadRequest) + return + } + if *v.Type_ != "MB_TOPIC_BASED" { log.Error("Mandatory Type_ parameter shall be set to MB_TOPIC_BASED") errHandlerProblemDetails(w, "Mandatory attribute Type_ shall be set to MB_TOPIC_BASED in the request body.", http.StatusBadRequest) return @@ -777,14 +783,8 @@ func registerediotplatformsPOST(w http.ResponseWriter, r *http.Request) { } } - if requestData.CustomServicesTransportInfo == nil || len(requestData.CustomServicesTransportInfo) == 0 { - log.Error("No information to register IoT platform") - errHandlerProblemDetails(w, "No information to register IoT platform.", http.StatusInternalServerError) - return - } - - _, ok := registeredIotPlatformsMap[requestData.IotPlatformId] - if ok { + log.Debug("registerediotplatformsPOST: registeredIotPlatformsMap (before): ", registeredIotPlatformsMap) + if _, ok := registeredIotPlatformsMap[requestData.IotPlatformId]; ok { log.Error("IoT platform already created") errHandlerProblemDetails(w, "IoT platform already created.", http.StatusBadRequest) return @@ -799,11 +799,12 @@ func registerediotplatformsPOST(w http.ResponseWriter, r *http.Request) { return } registeredIotPlatformsMap[responseData.IotPlatformId] = responseData - log.Debug("registerediotplatformsPOST: new registeredIotPlatformsMap: ", registeredIotPlatformsMap) + log.Debug("registerediotplatformsPOST: registeredIotPlatformsMap (after): ", registeredIotPlatformsMap) // Prepare & send response c := convertIotPlatformInfoFromSbi(responseData) jsonResponse := convertIoTPlatformInfotoJson(&c) + w.Header().Set("Location", hostUrl.String()+basePath+"registered_iot_platforms/"+responseData.IotPlatformId) w.WriteHeader(http.StatusCreated) fmt.Fprint(w, jsonResponse) } @@ -825,6 +826,8 @@ func registereddevicesByIdDELETE(w http.ResponseWriter, r *http.Request) { return } + // FIXME FSCOM Remove entry into redist + w.WriteHeader(http.StatusNoContent) } @@ -861,6 +864,8 @@ func registereddevicesByIdGET(w http.ResponseWriter, r *http.Request) { func registereddevicesByIdPUT(w http.ResponseWriter, r *http.Request) { log.Debug(">>> registereddevicesByIdPUT: ", r) + w.Header().Set("Content-Type", "application/json; charset=UTF-8") + errHandlerProblemDetails(w, "Not implemented yet", http.StatusBadRequest) } @@ -921,6 +926,7 @@ func registereddevicesGET(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, string(jsonResponse)) w.WriteHeader(http.StatusOK) } + func applyFiltering(devicesList []sbi.DeviceInfo, filter []string) (devices []DeviceInfo, err error) { log.Debug(">>> applyFiltering") devices, err = convertDeviceInfosFromSbi_with_filter(devicesList, filter) @@ -931,6 +937,8 @@ func applyFiltering(devicesList []sbi.DeviceInfo, filter []string) (devices []De func registereddevicesPOST(w http.ResponseWriter, r *http.Request) { log.Debug(">>> registereddevicesPOST: ", r) + w.Header().Set("Content-Type", "application/json; charset=UTF-8") + // Read JSON input stream provided in the Request, and stores it in the bodyBytes as bytes var deviceInfo DeviceInfo bodyBytes, _ := ioutil.ReadAll(r.Body) @@ -966,16 +974,18 @@ func registereddevicesPOST(w http.ResponseWriter, r *http.Request) { errHandlerProblemDetails(w, err.Error(), http.StatusInternalServerError) return } - log.Info("registereddevicesPOST: deviceInfoResp: ", deviceInfoResp) + devInfoResp := convertDeviceInfoFromSbi(deviceInfoResp) + log.Info("registereddevicesPOST: devInfoResp: ", devInfoResp) - jsonResponse, err := json.Marshal(deviceInfoResp) + // Prepare & send the reponse + w.Header().Set("Location", hostUrl.String()+basePath+"registered_devices/"+devInfoResp.DeviceId) + jsonResponse, err := json.Marshal(devInfoResp) if err != nil { log.Error(err.Error()) errHandlerProblemDetails(w, err.Error(), http.StatusInternalServerError) return } log.Info("registereddevicesPOST: jsonResponse: ", string(jsonResponse)) - w.WriteHeader(http.StatusCreated) fmt.Fprint(w, string(jsonResponse)) } @@ -1057,6 +1067,7 @@ func convertIotPlatformInfoFromSbi(val sbi.IotPlatformInfo) (item IotPlatformInf } } e.Extensions = userTransportInfo.Security.Extensions + v.Security = &e } if userTransportInfo.ImplSpecificInfo != nil { v.ImplSpecificInfo = &ImplSpecificInfo{ @@ -1138,6 +1149,7 @@ func convertIotPlatformInfoToSbi(val IotPlatformInfo) (item sbi.IotPlatformInfo) } } e.Extensions = userTransportInfo.Security.Extensions + v.Security = &e } if userTransportInfo.ImplSpecificInfo != nil { v.ImplSpecificInfo = &sbi.ImplSpecificInfo{ @@ -1147,7 +1159,7 @@ func convertIotPlatformInfoToSbi(val IotPlatformInfo) (item sbi.IotPlatformInfo) } } item.UserTransportInfo = append(item.UserTransportInfo, v) - } + } // End of 'for' statement if val.CustomServicesTransportInfo != nil && len(val.CustomServicesTransportInfo) != 0 { item.CustomServicesTransportInfo = make([]sbi.TransportInfo, 0) for _, customServicesTransportInfo := range val.CustomServicesTransportInfo { @@ -1176,7 +1188,7 @@ func convertIotPlatformInfoToSbi(val IotPlatformInfo) (item sbi.IotPlatformInfo) v.Endpoint = &e } item.CustomServicesTransportInfo = append(item.CustomServicesTransportInfo, v) - } + } // End of 'for' statement } return item @@ -1204,9 +1216,116 @@ func convertDeviceInfoFromSbi(dev sbi.DeviceInfo) (device DeviceInfo) { device.DeviceMetadata[i] = KeyValuePair{Key: k.Key, Value: k.Value} } // End of 'for' statement } - // FIXME FSCOM Add missing fileds (pointers & arrays) - //log.Debug("convertDeviceInfosFromSbi: devices: ", devices) + if len(dev.RequestedMecTrafficRule) != 0 { + device.RequestedMecTrafficRule = make([]TrafficRuleDescriptor, len(dev.RequestedMecTrafficRule)) + for i, v := range dev.RequestedMecTrafficRule { + device.RequestedMecTrafficRule[i] = TrafficRuleDescriptor{ + TrafficRuleId: v.TrafficRuleId, + FilterType: v.FilterType, + Priority: v.Priority, + Action: v.Action, + } + if len(dev.RequestedMecTrafficRule[i].TrafficFilter) != 0 { + device.RequestedMecTrafficRule[i].TrafficFilter = make([]TrafficFilter, len(dev.RequestedMecTrafficRule[i].TrafficFilter)) + for j, u := range dev.RequestedMecTrafficRule[i].TrafficFilter { + device.RequestedMecTrafficRule[i].TrafficFilter[j] = TrafficFilter{ + SrcAddress: u.SrcAddress, + DstAddress: u.DstAddress, + SrcPort: u.SrcPort, + DstPort: u.DstPort, + Protocol: u.Protocol, + Tag: u.Tag, + Uri: u.Uri, + PacketLabel: u.PacketLabel, + SrcTunnelAddress: u.SrcTunnelAddress, + TgtTunnelAddress: u.TgtTunnelAddress, + SrcTunnelPort: u.SrcTunnelPort, + DstTunnelPort: u.DstTunnelPort, + QCI: u.QCI, + DSCP: u.DSCP, + TC: u.TC, + } + } // End of 'for' statement + } + if v.DstInterface != nil { + device.RequestedMecTrafficRule[i].DstInterface = &InterfaceDescriptor{ + InterfaceType: v.DstInterface.InterfaceType, + SrcMACAddress: v.DstInterface.SrcMACAddress, + DstMACAddress: v.DstInterface.DstMACAddress, + DstIPAddress: v.DstInterface.DstIPAddress, + } + if v.DstInterface.TunnelInfo != nil { + device.RequestedMecTrafficRule[i].DstInterface.TunnelInfo = &TunnelInfo{ + TunnelType: v.DstInterface.TunnelInfo.TunnelType, + TunnelDstAddress: v.DstInterface.TunnelInfo.TunnelDstAddress, + TunnelSrcAddress: v.DstInterface.TunnelInfo.TunnelSrcAddress, + TunnelSpecificData: v.DstInterface.TunnelInfo.TunnelSpecificData, + } + } + } + } // End of 'for' statement + } + if dev.DeviceSpecificMessageFormats != nil { + var deviceSpecificMessageFormats = DeviceSpecificMessageFormats{} + if dev.DeviceSpecificMessageFormats.EventMsgFormat != nil { + deviceSpecificMessageFormats.EventMsgFormat = &EventMsg{ + EventTopic: dev.DeviceSpecificMessageFormats.EventMsgFormat.EventTopic, + IncludeDeviceAddr: dev.DeviceSpecificMessageFormats.EventMsgFormat.IncludeDeviceAddr, + IncludeDeviceMetadata: dev.DeviceSpecificMessageFormats.EventMsgFormat.IncludeDeviceMetadata, + IncludePei: dev.DeviceSpecificMessageFormats.EventMsgFormat.IncludePei, + IncludeSupi: dev.DeviceSpecificMessageFormats.EventMsgFormat.IncludeSupi, + IncludeImei: dev.DeviceSpecificMessageFormats.EventMsgFormat.IncludeImei, + IncludeImsi: dev.DeviceSpecificMessageFormats.EventMsgFormat.IncludeImsi, + IncludeIccid: dev.DeviceSpecificMessageFormats.EventMsgFormat.IncludeIccid, + IncludeDeviceId: dev.DeviceSpecificMessageFormats.EventMsgFormat.IncludeDeviceId, + } + if dev.DeviceSpecificMessageFormats.EventMsgFormat.SelectedSerializer != nil { + var s SerializerType + if *dev.DeviceSpecificMessageFormats.EventMsgFormat.SelectedSerializer == "JSON" { + s = JSON + } else if *dev.DeviceSpecificMessageFormats.EventMsgFormat.SelectedSerializer == "XML" { + s = XML + } else { + s = PROTOBUF3 + } + deviceSpecificMessageFormats.EventMsgFormat.SelectedSerializer = &s + } + } + if dev.DeviceSpecificMessageFormats.UplinkMsgFormat != nil { + deviceSpecificMessageFormats.UplinkMsgFormat = &UplinkMsg{ + UplinkTopic: dev.DeviceSpecificMessageFormats.UplinkMsgFormat.UplinkTopic, + IncludeDevicePort: dev.DeviceSpecificMessageFormats.UplinkMsgFormat.IncludeDevicePort, + IncludeDeviceAddr: dev.DeviceSpecificMessageFormats.UplinkMsgFormat.IncludeDeviceAddr, + IncludeDeviceMetadata: dev.DeviceSpecificMessageFormats.UplinkMsgFormat.IncludeDeviceMetadata, + IncludePei: dev.DeviceSpecificMessageFormats.UplinkMsgFormat.IncludePei, + IncludeSupi: dev.DeviceSpecificMessageFormats.UplinkMsgFormat.IncludeSupi, + IncludeImei: dev.DeviceSpecificMessageFormats.UplinkMsgFormat.IncludeImei, + IncludeImsi: dev.DeviceSpecificMessageFormats.UplinkMsgFormat.IncludeImsi, + IncludeIccid: dev.DeviceSpecificMessageFormats.UplinkMsgFormat.IncludeIccid, + IncludeDeviceId: dev.DeviceSpecificMessageFormats.UplinkMsgFormat.IncludeDeviceId, + } + if dev.DeviceSpecificMessageFormats.UplinkMsgFormat.SelectedSerializer != nil { + var s SerializerType + if *dev.DeviceSpecificMessageFormats.UplinkMsgFormat.SelectedSerializer == "JSON" { + s = JSON + } else if *dev.DeviceSpecificMessageFormats.UplinkMsgFormat.SelectedSerializer == "XML" { + s = XML + } else { + s = PROTOBUF3 + } + deviceSpecificMessageFormats.UplinkMsgFormat.SelectedSerializer = &s + } + } + device.DeviceSpecificMessageFormats = &deviceSpecificMessageFormats + } + if dev.DownlinkInfo != nil { + device.DownlinkInfo = &DownlinkInfo{ + DownlinkTopic: dev.DownlinkInfo.DownlinkTopic, + DevicePort: dev.DownlinkInfo.DevicePort, + } + } + //log.Debug("convertDeviceInfosFromSbi: devices: ", devices) return device } @@ -1284,8 +1403,101 @@ func convertDeviceInfoToSbi(dev DeviceInfo) (device sbi.DeviceInfo) { device.DeviceMetadata[i] = sbi.KeyValuePair{Key: k.Key, Value: k.Value} } // End of 'for' statement } - // FIXME FSCOM Add missing fileds (pointers & arrays) - //log.Debug("convertDeviceInfoToSbi: devices: ", devices) + if len(dev.RequestedMecTrafficRule) != 0 { + device.RequestedMecTrafficRule = make([]sbi.TrafficRuleDescriptor, len(dev.RequestedMecTrafficRule)) + for i, v := range dev.RequestedMecTrafficRule { + device.RequestedMecTrafficRule[i] = sbi.TrafficRuleDescriptor{ + TrafficRuleId: v.TrafficRuleId, + FilterType: v.FilterType, + Priority: v.Priority, + Action: v.Action, + } + if len(dev.RequestedMecTrafficRule[i].TrafficFilter) != 0 { + device.RequestedMecTrafficRule[i].TrafficFilter = make([]sbi.TrafficFilter, len(dev.RequestedMecTrafficRule[i].TrafficFilter)) + for j, u := range dev.RequestedMecTrafficRule[i].TrafficFilter { + device.RequestedMecTrafficRule[i].TrafficFilter[j] = sbi.TrafficFilter{ + SrcAddress: u.SrcAddress, + DstAddress: u.DstAddress, + SrcPort: u.SrcPort, + DstPort: u.DstPort, + Protocol: u.Protocol, + Tag: u.Tag, + Uri: u.Uri, + PacketLabel: u.PacketLabel, + SrcTunnelAddress: u.SrcTunnelAddress, + TgtTunnelAddress: u.TgtTunnelAddress, + SrcTunnelPort: u.SrcTunnelPort, + DstTunnelPort: u.DstTunnelPort, + QCI: u.QCI, + DSCP: u.DSCP, + TC: u.TC, + } + } // End of 'for' statement + } + if v.DstInterface != nil { + device.RequestedMecTrafficRule[i].DstInterface = &sbi.InterfaceDescriptor{ + InterfaceType: v.DstInterface.InterfaceType, + SrcMACAddress: v.DstInterface.SrcMACAddress, + DstMACAddress: v.DstInterface.DstMACAddress, + DstIPAddress: v.DstInterface.DstIPAddress, + } + if v.DstInterface.TunnelInfo != nil { + device.RequestedMecTrafficRule[i].DstInterface.TunnelInfo = &sbi.TunnelInfo{ + TunnelType: v.DstInterface.TunnelInfo.TunnelType, + TunnelDstAddress: v.DstInterface.TunnelInfo.TunnelDstAddress, + TunnelSrcAddress: v.DstInterface.TunnelInfo.TunnelSrcAddress, + TunnelSpecificData: v.DstInterface.TunnelInfo.TunnelSpecificData, + } + } + } + } // End of 'for' statement + } + if dev.DeviceSpecificMessageFormats != nil { + var deviceSpecificMessageFormats = sbi.DeviceSpecificMessageFormats{} + if dev.DeviceSpecificMessageFormats.EventMsgFormat != nil { + deviceSpecificMessageFormats.EventMsgFormat = &sbi.EventMsg{ + EventTopic: dev.DeviceSpecificMessageFormats.EventMsgFormat.EventTopic, + IncludeDeviceAddr: dev.DeviceSpecificMessageFormats.EventMsgFormat.IncludeDeviceAddr, + IncludeDeviceMetadata: dev.DeviceSpecificMessageFormats.EventMsgFormat.IncludeDeviceMetadata, + IncludePei: dev.DeviceSpecificMessageFormats.EventMsgFormat.IncludePei, + IncludeSupi: dev.DeviceSpecificMessageFormats.EventMsgFormat.IncludeSupi, + IncludeImei: dev.DeviceSpecificMessageFormats.EventMsgFormat.IncludeImei, + IncludeImsi: dev.DeviceSpecificMessageFormats.EventMsgFormat.IncludeImsi, + IncludeIccid: dev.DeviceSpecificMessageFormats.EventMsgFormat.IncludeIccid, + IncludeDeviceId: dev.DeviceSpecificMessageFormats.EventMsgFormat.IncludeDeviceId, + } + if dev.DeviceSpecificMessageFormats.EventMsgFormat.SelectedSerializer != nil { + var s string = string(*dev.DeviceSpecificMessageFormats.EventMsgFormat.SelectedSerializer) + deviceSpecificMessageFormats.EventMsgFormat.SelectedSerializer = &s + } + } + if dev.DeviceSpecificMessageFormats.UplinkMsgFormat != nil { + deviceSpecificMessageFormats.UplinkMsgFormat = &sbi.UplinkMsg{ + UplinkTopic: dev.DeviceSpecificMessageFormats.UplinkMsgFormat.UplinkTopic, + IncludeDevicePort: dev.DeviceSpecificMessageFormats.UplinkMsgFormat.IncludeDevicePort, + IncludeDeviceAddr: dev.DeviceSpecificMessageFormats.UplinkMsgFormat.IncludeDeviceAddr, + IncludeDeviceMetadata: dev.DeviceSpecificMessageFormats.UplinkMsgFormat.IncludeDeviceMetadata, + IncludePei: dev.DeviceSpecificMessageFormats.UplinkMsgFormat.IncludePei, + IncludeSupi: dev.DeviceSpecificMessageFormats.UplinkMsgFormat.IncludeSupi, + IncludeImei: dev.DeviceSpecificMessageFormats.UplinkMsgFormat.IncludeImei, + IncludeImsi: dev.DeviceSpecificMessageFormats.UplinkMsgFormat.IncludeImsi, + IncludeIccid: dev.DeviceSpecificMessageFormats.UplinkMsgFormat.IncludeIccid, + IncludeDeviceId: dev.DeviceSpecificMessageFormats.UplinkMsgFormat.IncludeDeviceId, + } + if dev.DeviceSpecificMessageFormats.UplinkMsgFormat.SelectedSerializer != nil { + var s string = string(*dev.DeviceSpecificMessageFormats.UplinkMsgFormat.SelectedSerializer) + deviceSpecificMessageFormats.UplinkMsgFormat.SelectedSerializer = &s + } + } + device.DeviceSpecificMessageFormats = &deviceSpecificMessageFormats + } + if dev.DownlinkInfo != nil { + device.DownlinkInfo = &sbi.DownlinkInfo{ + DownlinkTopic: dev.DownlinkInfo.DownlinkTopic, + DevicePort: dev.DownlinkInfo.DevicePort, + } + } + //log.Debug("convertDeviceInfoToSbi: devices: ", devices) return device } diff --git a/go-apps/meep-loc-serv/api/swagger.yaml b/go-apps/meep-loc-serv/api/swagger.yaml index 7ff4e72a089724f58368e2b352d7252d4e55d728..6b28a88f504aab78b341889ee92b83c75e0c1dfb 100644 --- a/go-apps/meep-loc-serv/api/swagger.yaml +++ b/go-apps/meep-loc-serv/api/swagger.yaml @@ -350,7 +350,7 @@ paths: example: - userAreaNotification: notificationType: 'UserAreaNotification' - timestamp: + timeStamp: seconds: 1673507343 nanoSeconds": 0 address: 'acr:10.0.0.1' @@ -590,7 +590,7 @@ paths: example: - userDistanceNotification: notificationType: 'UserDistanceNotification' - timestamp: + timeStamp: seconds: 1673507343 nanoSeconds": 0 monitoredUsers: @@ -598,7 +598,7 @@ paths: address: 'acr:10.0.0.1' accessPointId: '001010000000000000000000000000001' zoneId: 'zone01' - timestamp: + timeStamp: seconds: 1673507343 nanoSeconds": 0 resourceURL: 'http://meAppServer.example.com/location/v3/queries/users' @@ -871,7 +871,7 @@ paths: example: - userLocationEventNotification: notificationType: 'UserLocationEventNotification' - timestamp: + timeStamp: seconds: 1673507343 nanoseconds: 0 address: 'acr:10.0.0.1' @@ -1135,7 +1135,7 @@ paths: example: - zoneLocationEventNotification: notificationType: 'ZoneLocationEventNotification' - timestamp: + timeStamp: seconds: 1673507343 nanoseconds: 0 address: 'acr:10.0.0.1' @@ -2450,7 +2450,7 @@ components: - text type: object TerminalDistance: - description: A type containing information about the distance from a terminal to a location or between two terminals, in addition the accuracy and a timestamp of the information are provided. + description: A type containing information about the distance from a terminal to a location or between two terminals, in addition the accuracy and a timeStamp of the information are provided. properties: accuracy: description: Accuracy of the provided distance in meters @@ -2462,7 +2462,7 @@ components: type: integer x-etsi-mec-cardinality: 1 x-etsi-mec-origin-type: integer - timestamp: + timeStamp: $ref: '#/components/schemas/TimeStamp' required: - distance @@ -2696,7 +2696,7 @@ components: - accessPointId - zoneId - resourceURL - - timestamp + - timeStamp type: object x-etsi-notes: "NOTE 1:\tAs specified in [5], clause 5.2.2.7.\nNOTE 2: \tAs specified in [5], clause 5.2.2.5." x-etsi-ref: 6.2.2 @@ -2722,7 +2722,7 @@ components: type: string x-etsi-mec-cardinality: '1' x-etsi-mec-origin-type: AnyURI - timestamp: + timeStamp: $ref: '#/components/schemas/TimeStamp' locationInfo: $ref: '#/components/schemas/LocationInfo' diff --git a/go-apps/meep-loc-serv/server/loc-serv.go b/go-apps/meep-loc-serv/server/loc-serv.go index bfda65dd0591114ae6bbf685b9441c3f28bc1b11..fe37c9ccc7d842e00493f20bd0b93628783a72d7 100644 --- a/go-apps/meep-loc-serv/server/loc-serv.go +++ b/go-apps/meep-loc-serv/server/loc-serv.go @@ -1131,11 +1131,41 @@ func sendDistanceNotification(subsId int, returnAddr map[string]*gisClient.Dista distanceNotif.Links = &SubscriptionLinks{ Subscription: distanceCheck.Subscription.Links.Self, } + + var userData UeUserData + var ul UserList + ul.ResourceURL = hostUrl.String() + basePath + "queries/users" + userData.userList = &ul + keyName := baseKey + typeUser + ":*" + err := rc.ForEachJSONEntry(keyName, populateUserList, &userData) + if err != nil { + log.Warn(err.Error()) + return + } + if userData.userList == nil { + log.Warn("No user found") + return + } + if len(userData.userList.User) == 0 { + log.Warn("No user info available") + return + } var userList UserList var userInfoList []UserInfo for terminalAddr, distanceInfo := range returnAddr { var userInfo UserInfo userInfo.Address = terminalAddr + for _, v := range userData.userList.User { + if v.Address == userInfo.Address { + userInfo.AccessPointId = v.AccessPointId + userInfo.ZoneId = v.ZoneId + break + } + } // End of 'for' statement + if userInfo.AccessPointId == "" || userInfo.ZoneId == "" { + log.Error("User ot found: " + userInfo.Address) + return + } var locationInfo LocationInfo locationInfo.Latitude = nil locationInfo.Latitude = append(locationInfo.Latitude, distanceInfo.DstLatitude) @@ -1143,13 +1173,18 @@ func sendDistanceNotification(subsId int, returnAddr map[string]*gisClient.Dista locationInfo.Longitude = append(locationInfo.Longitude, distanceInfo.DstLongitude) locationInfo.Shape = 2 seconds := time.Now().Unix() - var timestamp TimeStamp - timestamp.Seconds = int32(seconds) + var timeStamp TimeStamp + timeStamp.Seconds = int32(seconds) + userInfo.Timestamp = &timeStamp userInfo.LocationInfo = &locationInfo userInfoList = append(userInfoList, userInfo) } userList.User = userInfoList distanceNotif.MonitoredUsers = &userList + distanceNotif.TimeStamp = &TimeStamp{ + Seconds: int32(time.Now().Unix()), + NanoSeconds: 0, + } distanceNotif.NotificationType = "UserDistanceNotification" var inlineDistanceSubscriptionNotification InlineUserDistanceNotification inlineDistanceSubscriptionNotification.UserDistanceNotification = &distanceNotif @@ -1255,9 +1290,10 @@ func checkNotificationAreaCircle(addressToCheck string) { locationInfo.Longitude = append(locationInfo.Longitude, withinRangeResp.SrcLongitude) locationInfo.Shape = 2 seconds := time.Now().Unix() - var timestamp TimeStamp - timestamp.Seconds = int32(seconds) areaCircleNotif.LocationInfo = &locationInfo + var timeStamp TimeStamp + timeStamp.Seconds = int32(seconds) + areaCircleNotif.TimeStamp = &timeStamp areaCircleNotif.NotificationType = "UserAreaNotification" var inlineCircleSubscriptionNotification InlineUserAreaNotification inlineCircleSubscriptionNotification.UserAreaNotification = &areaCircleNotif @@ -1385,13 +1421,14 @@ func sendNotification(subsId int, periodicCheck *PeriodicCheck1) { locationInfo.Longitude = append(locationInfo.Longitude, geoDataInfo.Location.Coordinates[0]) locationInfo.Shape = 2 seconds := time.Now().Unix() - var timestamp TimeStamp - timestamp.Seconds = int32(seconds) periodicNotif.LocationInfo = &locationInfo periodicNotif.IsFinalNotification = false periodicNotif.Links = &SubscriptionLinks{ Subscription: periodicCheck.Subscription.Links.Self, } + var timeStamp TimeStamp + timeStamp.Seconds = int32(seconds) + periodicNotif.TimeStamp = &timeStamp periodicNotif.NotificationType = "UserLocationPeriodicNotification" result := new(NotificationResult) *result = SUCCESS @@ -1640,9 +1677,9 @@ func checkNotificationRegisteredZoneStatus1(zoneId string, apId string, nbUsersI Href: value_.Subscription.Links.Self.Href, } } - var timestamp TimeStamp - timestamp.Seconds = int32(seconds) - zoneStatusNotif.TimeStamp = ×tamp + var timeStamp TimeStamp + timeStamp.Seconds = int32(seconds) + zoneStatusNotif.TimeStamp = &timeStamp zoneStatusNotif.NotificationType = "ZoneStatusNotification" var inlineZoneStatusNotification InlineZoneStatusNotification inlineZoneStatusNotification.ZoneStatusNotification = &zoneStatusNotif @@ -1701,9 +1738,9 @@ func checkNotificationRegisteredUsers(oldZoneId string, newZoneId string, oldApI } zonal.NotificationType = "UserLocationEventNotification" seconds := time.Now().Unix() - var timestamp TimeStamp - timestamp.Seconds = int32(seconds) - zonal.TimeStamp = ×tamp + var timeStamp TimeStamp + timeStamp.Seconds = int32(seconds) + zonal.TimeStamp = &timeStamp if newZoneId != oldZoneId { //process LEAVING events prior to entering ones if oldZoneId != "" { @@ -2119,9 +2156,9 @@ func sendNotification_1(subsId int, zoneId string, userId string, eventType Loca *event = eventType zonal.UserLocationEvent = event seconds := time.Now().Unix() - var timestamp TimeStamp - timestamp.Seconds = int32(seconds) - zonal.TimeStamp = ×tamp + var timeStamp TimeStamp + timeStamp.Seconds = int32(seconds) + zonal.TimeStamp = &timeStamp var inlineZonal InlineZoneLocationEventNotification inlineZonal.ZoneLocationEventNotification = &zonal sendZonalPresenceNotification_L(subscription.CallbackReference, inlineZonal) @@ -5225,7 +5262,7 @@ func updateStoreName(storeName string) { // 1. Retrieves the current user information from the database based on the provided address. // 2. Updates the user information with the new zone ID, access point ID, and location details. // 3. Determines if the user is connected based on the provided access point ID. -// 4. Updates the user's timestamp to the current time. +// 4. Updates the user's timeStamp to the current time. // 5. Updates the user's relative location information if map ID is provided. // 6. Updates the user's civic address information if country is provided. // 7. Updates the user's location information if longitude and latitude are provided. @@ -5744,7 +5781,7 @@ func userLocationPeriodicReInit() { // // If there's an error during communication with the GIS engine API, it logs the error and returns an appropriate HTTP status code. // -// Finally, it constructs the response InlineTerminalDistance containing the calculated distance and timestamp, +// Finally, it constructs the response InlineTerminalDistance containing the calculated distance and timeStamp, // marshals it into JSON format, sets the HTTP status code to 200 OK, and writes the JSON response to the response writer. // If there's an error during JSON marshaling, it logs the error and returns a 500 Internal Server Error status code. func distanceGet(w http.ResponseWriter, r *http.Request) { @@ -5863,9 +5900,9 @@ func distanceGet(w http.ResponseWriter, r *http.Request) { terminalDistance.Distance = int32(distResp.Distance) seconds := time.Now().Unix() - var timestamp TimeStamp - timestamp.Seconds = int32(seconds) - terminalDistance.Timestamp = ×tamp + var timeStamp TimeStamp + timeStamp.Seconds = int32(seconds) + terminalDistance.Timestamp = &timeStamp response.TerminalDistance = &terminalDistance diff --git a/go-apps/meep-loc-serv/server/loc-serv_test.go b/go-apps/meep-loc-serv/server/loc-serv_test.go index 54f9050ea26320876656d6b2290b3f9eeb1744ca..9bb69ec9b385f0679d1f9959abb59a55648d006a 100644 --- a/go-apps/meep-loc-serv/server/loc-serv_test.go +++ b/go-apps/meep-loc-serv/server/loc-serv_test.go @@ -2595,8 +2595,8 @@ func TestUserInfo(t *testing.T) { expectedResourceURL := expectedListResourceURL + "?address=ue1" //expectedListResourceURL := "" //expectedResourceURL := "" - var timestamp TimeStamp - expectedUserInfo := UserInfo{"ue1", "zone1-poa-cell1", "zone1", expectedResourceURL, ×tamp, nil, nil, "", nil} + var timeStamp TimeStamp + expectedUserInfo := UserInfo{"ue1", "zone1-poa-cell1", "zone1", expectedResourceURL, &timeStamp, nil, nil, "", nil} expectedUserList := UserList{expectedListResourceURL, nil} expectedUserList.User = append(expectedUserList.User, expectedUserInfo) @@ -2655,10 +2655,10 @@ func testUserInfo(t *testing.T, userId string, expectedResponse string) { if err != nil { t.Fatalf("Failed to get expected response") } - //need to remove the timestamp since it was not given in the expected response + //need to remove the timeStamp since it was not given in the expected response if len(respBody.UserList.User) != 0 { - var timestamp TimeStamp - respBody.UserList.User[0].Timestamp = ×tamp + var timeStamp TimeStamp + respBody.UserList.User[0].Timestamp = &timeStamp } receivedResponseStr, err := json.Marshal(respBody.UserList) diff --git a/go-apps/meep-loc-serv/server/model_terminal_distance.go b/go-apps/meep-loc-serv/server/model_terminal_distance.go index bbbc873a892d65794d3f267b138ead3563d692be..2d5eafe028b7976fc1719b6041241ca07d7c43c3 100644 --- a/go-apps/meep-loc-serv/server/model_terminal_distance.go +++ b/go-apps/meep-loc-serv/server/model_terminal_distance.go @@ -9,12 +9,12 @@ */ package server -// A type containing information about the distance from a terminal to a location or between two terminals, in addition the accuracy and a timestamp of the information are provided. +// A type containing information about the distance from a terminal to a location or between two terminals, in addition the accuracy and a timeStamp of the information are provided. type TerminalDistance struct { // Accuracy of the provided distance in meters Accuracy int32 `json:"accuracy,omitempty"` // Distance from terminal to a location or between two terminals specified in meters Distance int32 `json:"distance"` - Timestamp *TimeStamp `json:"timestamp,omitempty"` + Timestamp *TimeStamp `json:"timeStamp,omitempty"` } diff --git a/go-apps/meep-loc-serv/server/model_user_info.go b/go-apps/meep-loc-serv/server/model_user_info.go index 05ef600fbd7d8a89d3c9c598aa88bbbac5922ebb..374bb178b75cdaa19bb7dca05be4f576d37ddab4 100644 --- a/go-apps/meep-loc-serv/server/model_user_info.go +++ b/go-apps/meep-loc-serv/server/model_user_info.go @@ -20,7 +20,7 @@ type UserInfo struct { // Self-referring URL, see note 1. ResourceURL string `json:"resourceURL"` - Timestamp *TimeStamp `json:"timestamp"` + Timestamp *TimeStamp `json:"timeStamp"` LocationInfo *LocationInfo `json:"locationInfo,omitempty"` diff --git a/go-apps/meep-mosquitto/Dockerfile b/go-apps/meep-mosquitto/Dockerfile index 04094594717f6a95e35987d5b213f969b8b680ca..49565dd49dd3537cd9e500659240c9d872995ea8 100644 --- a/go-apps/meep-mosquitto/Dockerfile +++ b/go-apps/meep-mosquitto/Dockerfile @@ -1,5 +1,8 @@ FROM eclipse-mosquitto:latest +# Install some libraries +RUN apk update && apk add curl jq + RUN mkdir -p /run/mosquitto # Create config and log directories RUN mkdir -p /mosquitto/config/conf.d \ @@ -8,7 +11,8 @@ RUN mkdir -p /mosquitto/config/conf.d \ COPY data/mosquitto.conf /mosquitto/config/mosquitto.conf # Copy additional listener config COPY data/listener.conf /mosquitto/config/conf.d/listener.conf -# Expose MQTT default port -EXPOSE 1883 -# Set custom config -CMD ["/usr/sbin/mosquitto", "-c", "/mosquitto/config/mosquitto.conf"] \ No newline at end of file + +WORKDIR /usr/src/app +COPY ./data /usr/src/app +RUN chmod +x /usr/src/app/entrypoint.sh +ENTRYPOINT [ "/usr/src/app/entrypoint.sh" ] diff --git a/go-apps/meep-mosquitto/listener.conf b/go-apps/meep-mosquitto/listener.conf index daa4137828755752c1c298dfd0485923249a1183..40b246c155fb9f9f1ca71afbcca940c8772f5e2a 100644 --- a/go-apps/meep-mosquitto/listener.conf +++ b/go-apps/meep-mosquitto/listener.conf @@ -1,2 +1,9 @@ +# Raw TCP MQTT listener +listener 9001 +protocol mqtt +allow_anonymous true + +# WebSocket listener (plaintext) listener 1883 -allow_anonymous true \ No newline at end of file +protocol websockets +allow_anonymous true diff --git a/go-apps/meep-mosquitto/mosquitto.conf b/go-apps/meep-mosquitto/mosquitto.conf index 2448f4c9d51881d46645479123cbad9917216998..c6fc4ea1069b5b2bfd0f7a5e0b390bec266d04af 100644 --- a/go-apps/meep-mosquitto/mosquitto.conf +++ b/go-apps/meep-mosquitto/mosquitto.conf @@ -5,4 +5,6 @@ persistence true persistence_location /mosquitto/data/ log_dest file /mosquitto/log/mosquitto.log +log_type all + include_dir /mosquitto/config/conf.d diff --git a/go-apps/meep-sandbox-ctrl/server/app-ctrl.go b/go-apps/meep-sandbox-ctrl/server/app-ctrl.go index 8ae028431f4d87785c5b6dba05a03417da0836df..a798cd5133979a01a1885a83206bb5a789ca7a41 100644 --- a/go-apps/meep-sandbox-ctrl/server/app-ctrl.go +++ b/go-apps/meep-sandbox-ctrl/server/app-ctrl.go @@ -18,11 +18,13 @@ package server import ( "context" + "crypto/tls" "encoding/json" "errors" "fmt" "net/http" "net/url" + "os" "strconv" "strings" "time" @@ -60,13 +62,15 @@ const defaultGracePeriod int = 10 var appCtrl *AppCtrl // MEC Fed -const MAX_MEC_FED_DOSCOVERY_ATTEMPT = 5 +const MEC_FED_DISCOVERY_TIMEOUT = 15 +const MAX_MEC_FED_DISCOVERY_ATTEMPT = 30 var ( fed *mfed.APIClient fedResources mfed.SystemInfo fed_timer *time.Ticker fed_timer_count int + fed_list map[string]bool ) // Initialize App Controller @@ -100,6 +104,8 @@ func appCtrlInit(sandboxName string, mqLocal *mq.MsgQueue) error { } log.Info("Connected to Application Store") + fed_list = make(map[string]bool) + return nil } @@ -674,58 +680,106 @@ func appStoreUpdateCb(eventType string, eventData interface{}, userData interfac } func startMECFederationDiscovery(hostUrl string) { - log.Debug(">>> startMECFederationDiscovery") + log.Debug(">>> startMECFederationDiscovery: ", hostUrl) + + // Add MEC platform to the map if it not already present + if _, ok := fed_list[hostUrl]; !ok { + fed_list[hostUrl] = false + } + log.Debug("startMECFederationDiscovery: fed_list=", fed_list) // Make sure ticker is not running if fed_timer != nil { - log.Warn("MEC Federation ticker already running") + log.Debug("startMECFederationDiscovery: MEC Federation ticker already running") return } // Start registration ticker - fed_timer = time.NewTicker(5 * time.Second) + fed_timer = time.NewTicker(MEC_FED_DISCOVERY_TIMEOUT * time.Second) go func() { for range fed_timer.C { - if fed_timer_count == MAX_MEC_FED_DOSCOVERY_ATTEMPT { - log.Info("Stop MEC federation discovery timer") + if fed_timer_count == MAX_MEC_FED_DISCOVERY_ATTEMPT { + log.Info("startMECFederationDiscovery: Stop MEC federation discovery timer") stopMECFederationDiscovery() return } - // Try to connect to MEC federation if any - log.Info("Try to connect to MEC federation if any") - cfg := mfed.NewConfiguration() - cfg.BasePath = "http://mep1-meep-federation/fed_enablement/v1" //hostUrl - fed = mfed.NewAPIClient(cfg) - if fed != nil { - // Send registration - var body = mfed.SystemInfo{ - SystemId: "", // Shall be empty in the request - SystemName: appCtrl.sandboxName, - SystemProvider: "ETSI", + + for k, v := range fed_list { + if !v { // Not register yet + log.Debug("startMECFederationDiscovery: Try to connect to MEC federation " + k) + cfg := mfed.NewConfiguration() + u, err := url.Parse(k) + if err != nil { + log.Error("startMECFederationDiscovery: Failed to parse " + k) + stopMECFederationDiscovery() + return + } + log.Debug("startMECFederationDiscovery: u:", u) + s := strings.Split(u.Host, "-") // Extract MEP identifier + if len(s) == 0 { + log.Error("startMECFederationDiscovery: Failed to extract MEP identifier") + stopMECFederationDiscovery() + return + } + log.Debug("startMECFederationDiscovery: s: ", s) + h := os.Getenv("MEEP_HOST_URL") + if len(h) == 0 { + log.Error("startMECFederationDiscovery: MEEP_HOST_URL env. variable not set") + stopMECFederationDiscovery() + return + } + cfg.BasePath = h + "/" + appCtrl.sandboxName + "/" + s[0] + u.Path + log.Debug("startMECFederationDiscovery: cfg.BasePath: ", cfg.BasePath) + tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}} + cfg.HTTPClient = &http.Client{Transport: tr} + log.Debug("startMECFederationDiscovery: cfg.HTTPClient: ", cfg.HTTPClient) + fed = mfed.NewAPIClient(cfg) + if fed != nil { + // Send registration + var body = mfed.SystemInfo{ + SystemId: "", // Shall be empty in the request + SystemName: appCtrl.sandboxName + "-" + s[0], + SystemProvider: "ETSI", + } + fedResources, response, err := fed.SystemInfoApi.SysteminfoPOST(context.TODO(), body) + log.Debug("startMECFederationDiscovery: response: ", response) + if err != nil { + log.Error(err.Error()) + } else { + log.Debug("startMECFederationDiscovery: ", fedResources) + delete(fed_list, k) + fed_list[k] = true + } + } else { + log.Warn("startMECFederationDiscovery: Failed to connect to MEC federation") + } } - var err error - fedResources, _, err = fed.SystemInfoApi.SysteminfoPOST(context.TODO(), body) - if err != nil { - log.Error(err.Error()) - // Do not raise error, MEC Federation is not mandatory - fed_timer_count += 1 - continue - } else { - log.Info("Successfuly registered to MEC Federation: ", fedResources) - log.Info("Stop MEC federation discovery timer") - stopMECFederationDiscovery() - return + } // End of 'for' statement + fed_timer_count += 1 + var stop bool = true + for _, v := range fed_list { + if !v { + stop = false + break } + } // End of 'for' statement + //log.Debug("startMECFederationDiscovery: Stop MEC federation discovery timer: ", stop) + if stop { + log.Info("startMECFederationDiscovery: Terminate MEC federation discovery timer, all registration done") + stopMECFederationDiscovery() + return } - } + } // End of 'for' statement }() } func stopMECFederationDiscovery() { log.Debug(">>> stopMECFederationDiscovery") if fed_timer != nil { - log.Info("Stopping MEC Federation ticker") + log.Debug("stopMECFederationDiscovery: Stopping MEC Federation ticker") fed_timer.Stop() fed_timer = nil + fed_list = make(map[string]bool) + log.Debug("stopMECFederationDiscovery: len(fed_list)=", len(fed_list)) } } diff --git a/go-apps/meep-sss/go.mod b/go-apps/meep-sss/go.mod index c46ed40ecc81f0c5251b552ca09bac4126b5a3c6..263642e7913089ffeff17196b860133c2c362ae5 100644 --- a/go-apps/meep-sss/go.mod +++ b/go-apps/meep-sss/go.mod @@ -15,7 +15,8 @@ require ( github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-mq v0.0.0 github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-redis v0.0.0 github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-sandbox-ctrl-client v0.0.0 - github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-service-mgmt-client v0.0.0 + github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-service-mgmt-client v0.0.0 + github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-iot-client v0.0.0 github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-subscriptions v0.0.0 // indirect github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-swagger-api-mgr v0.0.0 github.com/gorilla/handlers v1.5.1 @@ -37,6 +38,7 @@ replace ( github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-redis => ../../go-packages/meep-redis github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-sandbox-ctrl-client => ../../go-packages/meep-sandbox-ctrl-client github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-service-mgmt-client => ../../go-packages/meep-service-mgmt-client + github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-iot-client => ../../go-packages/meep-iot-client github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-subscriptions => ../../go-packages/meep-subscriptions github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-swagger-api-mgr => ../../go-packages/meep-swagger-api-mgr github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-websocket => ../../go-packages/meep-websocket diff --git a/go-apps/meep-sss/go.sum b/go-apps/meep-sss/go.sum index d2eacd2e5c2eaef6bcf0775f09b7b004b9fb2d35..7aa8d1dcc421c191638defaaa3316e9e99cbfdde 100644 --- a/go-apps/meep-sss/go.sum +++ b/go-apps/meep-sss/go.sum @@ -1,7 +1,39 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/InterDigitalInc/AdvantEDGE v1.9.2 h1:CAcF+bn5m0Va2mHFL2lE4awU/kjuF6CjC05phiz8vnk= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/RyanCarrier/dijkstra v0.0.0-20190726134004-b51cadb5ae52 h1:trnwuu/Q8T59kgRjXcSDBODnyZP9wes+bnLn0lx4PgM= @@ -41,8 +73,12 @@ github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QH github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -63,7 +99,9 @@ github.com/eclipse/paho.mqtt.golang v1.4.2 h1:66wOzfUHSSI1zamx7jR6yMEI5EuHnT1G6r 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.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= @@ -74,6 +112,9 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= @@ -92,15 +133,27 @@ github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zV github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= @@ -113,14 +166,28 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= @@ -160,6 +227,7 @@ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2p github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d h1:/WZQPMZNsjZ7IlCpsLGdQBINg5bxKQ1K1sh6awxLtkA= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= @@ -170,6 +238,8 @@ github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= @@ -328,12 +398,19 @@ github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijb github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= @@ -346,17 +423,39 @@ golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -370,27 +469,52 @@ golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 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-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/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-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/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-20200520182314-0ba52f642ac2/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-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 h1:OSnWWcOd/CtWQC2cYSBgbTSJv3ciqd8r54ySIW2y3RE= +golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 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-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/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= @@ -404,32 +528,60 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da h1:b3NXsE2LusjYGGjL5bxEVZZORm/YEFFrWFjR8eFrw/c= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -437,44 +589,134 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= @@ -499,7 +741,14 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/go-apps/meep-sss/sbi/sss-sbi.go b/go-apps/meep-sss/sbi/sss-sbi.go index 28fee729720a7e066762cdad2822d9862d854603..becae2e38359f655498c6f4688cf6b996067c7d8 100644 --- a/go-apps/meep-sss/sbi/sss-sbi.go +++ b/go-apps/meep-sss/sbi/sss-sbi.go @@ -17,6 +17,8 @@ package sbi import ( + "encoding/json" + "errors" "sync" log "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-logger" @@ -28,6 +30,14 @@ import ( const moduleName string = "meep-sss-sbi" +type IotPlatformInfo struct { + Host string + Port int + CseName string + Id string + Protocol string +} + type Point struct { Latitude float64 Longitude float64 @@ -53,6 +63,19 @@ type SensorStatusInfo struct { ErrorInformation string } +type SensorData struct { + SensorIdentifier string + Data string + DataFormat string + DataUnitOfMeasure string + DataTimestamp *TimeStamp +} + +type TimeStamp struct { + Seconds int32 + NanoSeconds int32 +} + type SbiCfg struct { ModuleName string SandboxName string @@ -164,12 +187,17 @@ func Init(cfg SbiCfg) (err error) { } // Connect to SSS Manager - sbi.sssMgr, err = tm.NewSssMgr(sbi.moduleName, sbi.sandboxName, sbi.protocol, sbi.host, sbi.port, sbi.hostId, sbi.name, sbi.discoveryNotify, sbi.statusNotify, sbi.dataNotify) - if err != nil { - log.Error("Failed connection to SSS Manager: ", err) - return err + if sbi.port != 0 { + sbi.sssMgr, err = tm.NewSssMgr(sbi.moduleName, sbi.sandboxName, sbi.protocol, sbi.host, sbi.port, sbi.hostId, sbi.name, sbi.discoveryNotify, sbi.statusNotify, sbi.dataNotify) + if err != nil { + log.Error("Failed connection to SSS Manager: ", err) + return err + } + log.Info("Connected to SSS Manager") + } else { + sbi.sssMgr = nil + log.Warn("No IoTPlatform registered") } - log.Info("Connected to SSS Manager") // Initialize service processActiveScenarioUpdate() @@ -237,6 +265,23 @@ func Stop() (err error) { return nil } +func UpdatePlatformList(iotPlatformInfoList []IotPlatformInfo) (err error) { // FIXME FSCOM To be refined to support multiple IoT platform registered + // Connect to SSS Manager + if sbi.sssMgr != nil { + log.Warn("UpdatePlatformList: IoTPlatform already registered") + return nil + } + + sbi.sssMgr, err = tm.NewSssMgr(sbi.moduleName, sbi.sandboxName, iotPlatformInfoList[0].Protocol, iotPlatformInfoList[0].Host, iotPlatformInfoList[0].Port, iotPlatformInfoList[0].Id, iotPlatformInfoList[0].CseName, sbi.discoveryNotify, sbi.statusNotify, sbi.dataNotify) + if err != nil { + log.Error("UpdatePlatformList: Failed connection to SSS Manager: ", err) + return err + } + log.Info("UpdatePlatformList: Connected to SSS Manager") + + return nil +} + // Message Queue handler func msgHandler(msg *mq.Msg, userData interface{}) { switch msg.Message { @@ -292,6 +337,11 @@ func processActiveScenarioUpdate() { func SensorDiscoveryInfoAll() (sensors []SensorDiscoveryInfo, err error) { log.Info("sbi.SensorDiscoveryInfoAll") + if sbi.sssMgr == nil { + err := errors.New("No IoT platform registered") + return nil, err + } + s, err := sbi.sssMgr.SensorDiscoveryInfoAll() if err != nil { return nil, err @@ -307,6 +357,11 @@ func SensorDiscoveryInfoAll() (sensors []SensorDiscoveryInfo, err error) { func GetSensorStatus(sensorIdentifier string) (status SensorStatusInfo, err error) { log.Info("sbi.GetSensorStatus") + if sbi.sssMgr == nil { + err := errors.New("No IoT platform registered") + return status, err + } + s, err := sbi.sssMgr.GetSensor(sensorIdentifier) if err != nil { return status, err @@ -316,7 +371,7 @@ func GetSensorStatus(sensorIdentifier string) (status SensorStatusInfo, err erro sensor := convertSensorDiscoveryInfoFromSssMgr(s) log.Info("sbi.GetSensorStatus: sensors: ", sensor) - // FIXME FSCOM CHeck status of the sensor??? + // FIXME FSCOM Check status of the sensor??? status = SensorStatusInfo{ SensorIdentifier: sensor.SensorIdentifier, SensorStatusType: "ONLINE", @@ -326,6 +381,43 @@ func GetSensorStatus(sensorIdentifier string) (status SensorStatusInfo, err erro return status, nil } +func GetSensorData(sensorIdentifier string) (data SensorData, err error) { + log.Info("sbi.GetSensorData") + + if sbi.sssMgr == nil { + err := errors.New("No IoT platform registered") + return data, err + } + + s, err := sbi.sssMgr.GetSensor(sensorIdentifier) + if err != nil { + return data, err + } + log.Info("sbi.GetSensorData: s: ", s) + log.Info("sbi.GetSensorData: s.SensorPropertyList: ", s.SensorPropertyList) + log.Info("sbi.GetSensorData: s.SensorCharacteristicList: ", s.SensorCharacteristicList) + log.Info("sbi.GetSensorData: s.Flex: ", s.Flex) + + sensor := convertSensorDiscoveryInfoFromSssMgr(s) + log.Info("sbi.GetSensorData: sensors: ", sensor) + + data = SensorData{ // FIXME FSCOM Review content of SensorData. What is the mapping oneM2M? + SensorIdentifier: sensor.SensorIdentifier, + //Data: string(sensor.SensorCharacteristicList), + DataFormat: "", + DataUnitOfMeasure: "", + DataTimestamp: nil, + } + j, err := json.Marshal(sensor.SensorCharacteristicList) + if err != nil { + log.Warn("sbi.GetSensorData: j: ", j) + return data, err + } + data.Data = string(j) + + return data, nil +} + func convertSensorDiscoveryInfoListFromSssMgr(s []tm.SensorDiscoveryInfo) (sensors []SensorDiscoveryInfo) { sensors = make([]SensorDiscoveryInfo, len(s)) for i, val := range s { @@ -336,14 +428,18 @@ func convertSensorDiscoveryInfoListFromSssMgr(s []tm.SensorDiscoveryInfo) (senso } func convertSensorDiscoveryInfoFromSssMgr(s tm.SensorDiscoveryInfo) (sensor SensorDiscoveryInfo) { + log.Info(">>> sbi.convertSensorDiscoveryInfoFromSssMgr: ", s) + sensor = SensorDiscoveryInfo{ SensorIdentifier: s.SensorIdentifier, SensorType: s.SensorType, } + log.Info("sbi.convertSensorDiscoveryInfoFromSssMgr: len(s.SensorPropertyList): ", len(s.SensorPropertyList)) if len(s.SensorPropertyList) != 0 { sensor.SensorPropertyList = make([]string, len(s.SensorPropertyList)) copy(sensor.SensorPropertyList, s.SensorPropertyList) } + log.Info("sbi.convertSensorDiscoveryInfoFromSssMgr: len(s.SensorCharacteristicList): ", len(s.SensorCharacteristicList)) if len(s.SensorCharacteristicList) != 0 { sensor.SensorCharacteristicList = make([]SensorCharacteristic, len(s.SensorCharacteristicList)) for i, val := range s.SensorCharacteristicList { @@ -354,6 +450,19 @@ func convertSensorDiscoveryInfoFromSssMgr(s tm.SensorDiscoveryInfo) (sensor Sens } } // End of 'for' statement } + log.Info("sbi.convertSensorDiscoveryInfoFromSssMgr: len(s.Flex): ", len(s.Flex)) + if len(s.Flex) != 0 { + sensor.SensorCharacteristicList = make([]SensorCharacteristic, len(s.Flex)) + i := 0 + for key, val := range s.Flex { + sensor.SensorCharacteristicList[i] = SensorCharacteristic{ + CharacteristicName: key, + CharacteristicValue: val.(string), + CharacteristicUnitOfMeasure: nil, + } + i += 1 + } // End of 'for' statement + } if s.SensorPosition != nil { sensor.SensorPosition = &Point{ Latitude: s.SensorPosition.Latitude, @@ -361,5 +470,6 @@ func convertSensorDiscoveryInfoFromSssMgr(s tm.SensorDiscoveryInfo) (sensor Sens } } + log.Info("<<< sbi.convertSensorDiscoveryInfoFromSssMgr: ", sensor) return sensor } diff --git a/go-apps/meep-sss/server/api_sensor_data_lookup.go b/go-apps/meep-sss/server/api_sensor_data_lookup.go index 191b7a8a65f79479b8fdc54e489f558711f37d1c..194094776f7dee6bd0dd9948340e687b810f1c46 100644 --- a/go-apps/meep-sss/server/api_sensor_data_lookup.go +++ b/go-apps/meep-sss/server/api_sensor_data_lookup.go @@ -14,6 +14,5 @@ import ( ) func SensorDataLookupGET(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json; charset=UTF-8") - w.WriteHeader(http.StatusOK) + sensorDataLookupGET(w, r) } diff --git a/go-apps/meep-sss/server/convert.go b/go-apps/meep-sss/server/convert.go index 0671a0cc1262fb012aeb122daea7b3b15e838dfb..1dc26516ea1cfbc7eb8bac8db440e08b847c9e28 100644 --- a/go-apps/meep-sss/server/convert.go +++ b/go-apps/meep-sss/server/convert.go @@ -41,6 +41,15 @@ func convertSensorDiscoveryInfoListToJson(sensors []SensorDiscoveryInfo) string return string(jsonInfo) } +func convertSensorDataToJson(data SensorData) string { + jsonInfo, err := json.Marshal(data) + if err != nil { + log.Error(err.Error()) + return "" + } + return string(jsonInfo) +} + func convertSensorStatusToJson(status SensorStatusInfo) string { jsonInfo, err := json.Marshal(status) if err != nil { diff --git a/go-apps/meep-sss/server/meep-sss.go b/go-apps/meep-sss/server/meep-sss.go index d07e22f94a1efdee4ce4562fd1ea6c6d15cb002d..7c0cfb71d5e14281b4abb38461e8261f48183380 100644 --- a/go-apps/meep-sss/server/meep-sss.go +++ b/go-apps/meep-sss/server/meep-sss.go @@ -35,6 +35,7 @@ import ( asc "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-app-support-client" dkm "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-data-key-mgr" httpLog "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-http-logger" + iot "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-iot-client" log "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-logger" met "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-metrics" redis "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-redis" @@ -56,6 +57,8 @@ const defaultScopeOfLocality = "MEC_SYSTEM" const defaultConsumedLocalOnly = true const appTerminationPath = "notifications/mec011/appTermination" +const iotBasePath = "iots/v1" + var redisAddr string = "meep-redis-master.default.svc.cluster.local:6379" var influxAddr string = "http://meep-influxdb.default.svc.cluster.local:8086" var sbxCtrlUrl string = "http://meep-sandbox-ctrl" @@ -88,8 +91,10 @@ var appEnablementServiceId string var appSupportClient *asc.APIClient var svcMgmtClient *smc.APIClient var sbxCtrlClient *scc.APIClient +var iotClient *iot.APIClient -var registrationTicker *time.Ticker +var registrationTicker *time.Ticker = nil +var iotPlatfomrDiscoveryTicker *time.Ticker = nil const SENS_DISCOVERY = "SensorDiscoveryEventSubscription" const SENS_STATUS = "SensorStatusSubscription" @@ -112,12 +117,6 @@ var mutex sync.Mutex var expiryTicker *time.Ticker var nextSubscriptionIdAvailable int -var iot_platform_address string = "lab-oai.etsi.org" -var iot_platform_port int = 31110 -var cse_name string = "laboai-acme-ic-cse" -var iot_platform_id string = "7feaadbb0400" -var iot_platform_protocol string = "REST_HTTP" - func getAppInstanceId() (id string, err error) { var appInfo scc.ApplicationInfo appInfo.Id = instanceId @@ -390,15 +389,16 @@ func Init() (err error) { Locality: locality, ScenarioNameCb: updateStoreName, CleanUpCb: cleanUp, - Protocol: iot_platform_protocol, - Host: iot_platform_address, - Port: iot_platform_port, - Name: cse_name, - HostId: iot_platform_id, + Protocol: "", + Host: "", + Port: 0, + Name: "", + HostId: "", DiscoveryNotify: discoveryNotify, StatusNotify: statusNotify, DataNotify: dataNotify, } + if mepName != defaultMepName { sbiCfg.MepName = mepName } @@ -409,6 +409,14 @@ func Init() (err error) { } log.Info("SBI Initialized") + iotClientCfg := iot.NewConfiguration() + iotClientCfg.BasePath = "http://" + mepName + "-meep-iot/" + iotBasePath + iotClient = iot.NewAPIClient(iotClientCfg) + if iotClient == nil { + return errors.New("Failed to create IoT REST API client") + } + log.Info("Create IoT REST API client") + // Create App Enablement REST clients if appEnablementEnabled { // Create Sandbox Controller client @@ -445,6 +453,9 @@ func Init() (err error) { // Run - Start SSS func Run() (err error) { + // Start Iot platfomr discovery ticker + startIotPlatformDiscoveryTicker() + // Start MEC Service registration ticker if appEnablementEnabled { startRegistrationTicker() @@ -454,6 +465,9 @@ func Run() (err error) { // Stop - Stop SSS func Stop() (err error) { + // Stop Iot platfomr discovery ticker + stopIotPlatfomrDiscoveryTicker() + // Stop MEC Service registration ticker if appEnablementEnabled { stopRegistrationTicker() @@ -546,6 +560,65 @@ func stopRegistrationTicker() { } } +func startIotPlatformDiscoveryTicker() { + // Make sure ticker is not running + if iotPlatfomrDiscoveryTicker != nil { + log.Warn("IoT platform discovery 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 stable state start") + time.Sleep(5 * time.Second) + + // Start registration ticker + iotPlatfomrDiscoveryTicker = time.NewTicker(time.Duration(60) * time.Second) + go func() { + for range iotPlatfomrDiscoveryTicker.C { + log.Info("startIotPlatformDiscoveryTicker: Launch IoT platform discovery") + iotPlatformInfoList, _, err := iotClient.RegIotPlatApi.RegisterediotplatformsGET(context.TODO(), nil) + if err != nil { + log.Error("Failed to initiate the : IoT platform discovery: ", err) + } else { + l := make([]sbi.IotPlatformInfo, 0) + for _, v := range iotPlatformInfoList { + if v.Enabled { + var p = sbi.IotPlatformInfo{ + /* For HTTP */ + Host: v.CustomServicesTransportInfo[0].Endpoint.Addresses[0].Host, + Port: int(v.CustomServicesTransportInfo[0].Endpoint.Addresses[0].Port), + CseName: v.CustomServicesTransportInfo[0].Name, + Id: v.IotPlatformId, + Protocol: v.CustomServicesTransportInfo[0].Protocol, + /* For MQTT, issues :( send req + Host: v.UserTransportInfo[0].Endpoint.Addresses[0].Host, + Port: int(v.UserTransportInfo[0].Endpoint.Addresses[0].Port), + CseName: v.UserTransportInfo[0].Name, + Id: v.IotPlatformId, + Protocol: v.UserTransportInfo[0].Protocol, + */ + } + l = append(l, p) + } // else, skip it + } // End of 'for'statement + if len(l) != 0 { + _ = sbi.UpdatePlatformList(l) + } + } + } // End of 'for' statement + }() +} + +func stopIotPlatfomrDiscoveryTicker() { + if iotPlatfomrDiscoveryTicker != nil { + log.Info("Stopping IoT platform discovery ticker") + iotPlatfomrDiscoveryTicker.Stop() + iotPlatfomrDiscoveryTicker = nil + } +} + func cleanUp() { log.Info("Terminate all") @@ -606,14 +679,31 @@ func sensorDiscoveryLookupGET(w http.ResponseWriter, r *http.Request) { log.Debug("sensorDiscoveryLookupGET: q: ", q) if len(q) == 0 { err := errors.New("Invalid query parameters") + log.Error("sensorDiscoveryLookupGET: ", err.Error()) + errHandlerProblemDetails(w, err.Error(), http.StatusBadRequest) + return + } else if _, ok := q["type"]; !ok { + err := errors.New("Invalid query parameters: 'type' parameter is mandatory") + log.Error("sensorDiscoveryLookupGET: ", err.Error()) + errHandlerProblemDetails(w, err.Error(), http.StatusBadRequest) + return + } else if len(q["type"][0]) == 0 { + err := errors.New("Invalid query parameters: Wrong 'type' parameter value") + log.Error("sensorDiscoveryLookupGET: ", err.Error()) + errHandlerProblemDetails(w, err.Error(), http.StatusBadRequest) + return + } else if _, ok := q["sensorPropertyList"]; !ok { + err := errors.New("Invalid query parameters: 'sensorPropertyList' parameter is mandatory, at leasr one item") + log.Error("sensorDiscoveryLookupGET: ", err.Error()) + errHandlerProblemDetails(w, err.Error(), http.StatusBadRequest) + return + } else if len(q["sensorPropertyList"][0]) == 0 { + err := errors.New("Invalid query parameters: Wrong 'sensorPropertyList' parameter value") + log.Error("sensorDiscoveryLookupGET: ", err.Error()) errHandlerProblemDetails(w, err.Error(), http.StatusBadRequest) return } - log.Debug("sensorDiscoveryLookupGET: q[type][0]: ", q["type"][0]) - //log.Debug("sensorDiscoveryLookupGET: q[sensorCharacteristicList][0]: ", q["sensorCharacteristicList"][0]) - //log.Debug("sensorDiscoveryLookupGET: type(q[sensorCharacteristicList][0]): ", reflect.TypeOf(q["sensorCharacteristicList"][0])) - //q: map[geographicalArea:[[object Object]] sensorCharacteristicList:[[object Object]] sensorPropertyList:[string1,string2] type:[string]]","time":"2025-02-04T08:35:35Z"} - // /sens/v1/queries/sensor_discovery?type=4&sensorPropertyList=%5B%22con%22%5D&sensorCharacteristicList=%5B%7B%22characteristicName%22%3A%22pi%22%2C%22characteristicUnitOfMeasure%22%3A%22ae_parent%22%7D%5D&geographicalArea=%5B%7B%22shape%22%3A0%2C%22radius%22%3A6%2C%22points%22%3A%5B%7B%22latitude%22%3A0.8008281904610115%2C%22longitude%22%3A6.027456183070403%7D%2C%7B%22latitude%22%3A0.8008281904610115%2C%22longitude%22%3A6.027456183070403%7D%5D%7D%5D' \ + s, err := sbi.SensorDiscoveryInfoAll() if err != nil { errHandlerProblemDetails(w, err.Error(), http.StatusInternalServerError) @@ -634,7 +724,7 @@ func sensorDiscoveryLookupGET(w http.ResponseWriter, r *http.Request) { // Prepare response and send jsonResponse := convertSensorDiscoveryInfoListToJson(sensors) if jsonResponse == "" { - log.Error("Marshalling failure") + log.Error("sensorDiscoveryLookupGET: Marshalling failure") errHandlerProblemDetails(w, "Marshalling failure", http.StatusInternalServerError) return } @@ -656,12 +746,20 @@ func sensorStatusLookupGET(w http.ResponseWriter, r *http.Request) { err := errors.New("Invalid query parameters") errHandlerProblemDetails(w, err.Error(), http.StatusBadRequest) return + } else if len(q["sensorIdentifier"][0]) == 0 { + err := errors.New("Invalid query parameters") + errHandlerProblemDetails(w, err.Error(), http.StatusBadRequest) + return } log.Debug("sensorStatusLookupGET: q[sensorIdentifier][0]: ", q["sensorIdentifier"][0]) s, err := sbi.GetSensorStatus(q["sensorIdentifier"][0]) if err != nil { - errHandlerProblemDetails(w, err.Error(), http.StatusInternalServerError) + if err.Error() == "Wrong Device identifier" { + errHandlerProblemDetails(w, err.Error(), http.StatusBadRequest) + } else { + errHandlerProblemDetails(w, err.Error(), http.StatusInternalServerError) + } return } log.Debug("sensorStatusLookupGET: s: ", s) @@ -669,7 +767,7 @@ func sensorStatusLookupGET(w http.ResponseWriter, r *http.Request) { // Prepare response and send jsonResponse := convertSensorStatusToJson(convertSensorStatusInfoFromSbi(s)) if jsonResponse == "" { - log.Error("Marshalling failure") + log.Error("sensorDiscoveryLookupGET: Marshalling failure") errHandlerProblemDetails(w, "Marshalling failure", http.StatusInternalServerError) return } @@ -678,6 +776,49 @@ func sensorStatusLookupGET(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) } +func sensorDataLookupGET(w http.ResponseWriter, r *http.Request) { + log.Debug(">>> sensorDataLookupGET: ", r) + + w.Header().Set("Content-Type", "application/json; charset=UTF-8") + + // Validate query parameters + u, _ := url.Parse(r.URL.String()) + q := u.Query() + log.Debug("sensorDataLookupGET: q: ", q) + if _, ok := q["sensorIdentifier"]; !ok { + err := errors.New("Invalid query parameters") + errHandlerProblemDetails(w, err.Error(), http.StatusBadRequest) + return + } else if len(q["sensorIdentifier"][0]) == 0 { + err := errors.New("Invalid query parameters") + errHandlerProblemDetails(w, err.Error(), http.StatusBadRequest) + return + } + log.Debug("sensorDataLookupGET: q[sensorIdentifier][0]: ", q["sensorIdentifier"][0]) + + s, err := sbi.GetSensorData(q["sensorIdentifier"][0]) + if err != nil { + if err.Error() == "Wrong Device identifier" { + errHandlerProblemDetails(w, err.Error(), http.StatusBadRequest) + } else { + errHandlerProblemDetails(w, err.Error(), http.StatusInternalServerError) + } + return + } + log.Debug("sensorDataLookupGET: s: ", s) + + // Prepare response and send + jsonResponse := convertSensorDataToJson(convertSensorDataFromSbi(s)) + if jsonResponse == "" { + log.Error("sensorDataLookupGET: Marshalling failure") + errHandlerProblemDetails(w, "Marshalling failure", http.StatusInternalServerError) + return + } + log.Info("sensorDataLookupGET: jsonResponse: ", jsonResponse) + fmt.Fprint(w, jsonResponse) + w.WriteHeader(http.StatusOK) +} + func sensorDiscoverySubscriptionGET(w http.ResponseWriter, r *http.Request) { log.Debug(">>> sensorDiscoverySubscriptionGET: ", r) @@ -695,6 +836,7 @@ func sensorDataSubscriptionGET(w http.ResponseWriter, r *http.Request) { subscriptionsGET(SENS_DATA, w, r) } + func sensorDiscoverySubscriptionPOST(w http.ResponseWriter, r *http.Request) { log.Debug(">>> sensorDiscoverySubscriptionPOST: ", r) @@ -785,13 +927,13 @@ func subscriptionsPOST(subType string, w http.ResponseWriter, r *http.Request) { // create a unique link for every subscription and concatenate subscription to it link := new(SubscriptionLinks) self := new(LinkType) - self.Href = hostUrl.String() + basePath + "subscriptions/" + subsIdStr - link.Self = self // switch statement is based on provided subscriptionType in the request body var jsonResponse string switch subscriptionType { case SENS_DISCOVERY: + self.Href = hostUrl.String() + basePath + "subscriptions/sensor_discovery/" + subsIdStr + link.Self = self var sensorDiscoveryEventSubscription SensorDiscoveryEventSubscription jsonResponse, err = processSensorDiscoveryEventSubscription(bodyBytes, link, subsIdStr, &sensorDiscoveryEventSubscription) if err != nil { @@ -802,6 +944,8 @@ func subscriptionsPOST(subType string, w http.ResponseWriter, r *http.Request) { w.Header().Set("Location", sensorDiscoveryEventSubscription.Links.Self.Href) case SENS_STATUS: + self.Href = hostUrl.String() + basePath + "subscriptions/sensor_status/" + subsIdStr + link.Self = self var sensorStatusSubscription SensorStatusSubscription jsonResponse, err = processSensorStatusSubscription(bodyBytes, link, subsIdStr, &sensorStatusSubscription) if err != nil { @@ -812,6 +956,8 @@ func subscriptionsPOST(subType string, w http.ResponseWriter, r *http.Request) { w.Header().Set("Location", sensorStatusSubscription.Links.Self.Href) case SENS_DATA: + self.Href = hostUrl.String() + basePath + "subscriptions/sensor_data/" + subsIdStr + link.Self = self var sensorDataSubscription SensorDataSubscription jsonResponse, err = processSensorDataSubscription(bodyBytes, link, subsIdStr, &sensorDataSubscription) if err != nil { @@ -858,13 +1004,27 @@ func subscriptionsGET(subType string, w http.ResponseWriter, r *http.Request) { u, _ := url.Parse(r.URL.String()) log.Info("url: ", u.RequestURI()) q := u.Query() - sensorIdentifier := q.Get("sensorIdentifier") - log.Debug("subscriptionsGET: sensorIdentifier: ", sensorIdentifier) - sensorIdentifiers := strings.Split(sensorIdentifier, ",") + sensorIdentifiers := []string{} + if len(q) != 0 { + sensorIdentifier := q.Get("sensorIdentifier") + if len(sensorIdentifier) == 0 { + err := errors.New("Wrong query parameters") + log.Error(err.Error()) + errHandlerProblemDetails(w, err.Error(), http.StatusBadRequest) + return + } + log.Debug("subscriptionsGET: sensorIdentifier: ", sensorIdentifier) + sensorIdentifiers = strings.Split(sensorIdentifier, ",") + } log.Debug("subscriptionsGET: sensorIdentifiers: ", sensorIdentifiers) // get the response against particular subscription type - response := createSubscriptionLinkList(subType, sensorIdentifiers) + response, err := createSubscriptionLinkList(subType, sensorIdentifiers) + if err != nil { + log.Error(err.Error()) + errHandlerProblemDetails(w, err.Error(), http.StatusBadRequest) + return + } // prepare & send response jsonResponse, err := json.Marshal(response) @@ -1476,7 +1636,7 @@ func isSubscriptionIdRegisteredSensorData(subsIdStr string) bool { return returnVal } -func createSubscriptionLinkList(subType string, sensorIdentifiers []string) *SubscriptionLinkList { +func createSubscriptionLinkList(subType string, sensorIdentifiers []string) (*SubscriptionLinkList, error) { log.Debug(">>> createSubscriptionLinkList: subType: ", subType) log.Debug(">>> createSubscriptionLinkList: sensorIdentifiers: ", sensorIdentifiers) @@ -1553,7 +1713,7 @@ func createSubscriptionLinkList(subType string, sensorIdentifiers []string) *Sub subscriptionLinkList.Links = links - return subscriptionLinkList + return subscriptionLinkList, nil } /* @@ -1794,49 +1954,87 @@ func dataNotify(map[string]interface{}) { func convertSensorDiscoveryInfoListFromSbi_with_filter(s []sbi.SensorDiscoveryInfo, filter url.Values) (sensors []SensorDiscoveryInfo) { log.Debug(">>> convertSensorDiscoveryInfoListFromSbi_with_filter: s: ", s) log.Debug(">>> convertSensorDiscoveryInfoListFromSbi_with_filter: filter: ", filter) + //log.Debug("sensorDiscoveryLookupGET: q[sensorCharacteristicList][0]: ", q["sensorCharacteristicList"][0]) + //log.Debug("sensorDiscoveryLookupGET: type(q[sensorCharacteristicList][0]): ", reflect.TypeOf(q["sensorCharacteristicList"][0])) + //q: map[geographicalArea:[[object Object]] sensorCharacteristicList:[[object Object]] sensorPropertyList:[string1,string2] type:[string]]","time":"2025-02-04T08:35:35Z"} + // /sens/v1/queries/sensor_discovery?type=4&sensorPropertyList=%5B%22con%22%5D&sensorCharacteristicList=%5B%7B%22characteristicName%22%3A%22pi%22%2C%22characteristicUnitOfMeasure%22%3A%22ae_parent%22%7D%5D&geographicalArea=%5B%7B%22shape%22%3A0%2C%22radius%22%3A6%2C%22points%22%3A%5B%7B%22latitude%22%3A0.8008281904610115%2C%22longitude%22%3A6.027456183070403%7D%2C%7B%22latitude%22%3A0.8008281904610115%2C%22longitude%22%3A6.027456183070403%7D%5D%7D%5D' \ sensors = make([]SensorDiscoveryInfo, 0) for _, val := range s { - log.Debug("convertSensorDiscoveryInfoListFromSbi_with_filter: processing val: ", val.SensorPropertyList) + log.Debug("convertSensorDiscoveryInfoListFromSbi_with_filter: processing val: ", val) if filter["type"][0] != val.SensorType { log.Debug("convertSensorDiscoveryInfoListFromSbi_with_filter: ", filter["type"][0], " not found, discard it") continue // Discard it } else { - if len(val.SensorPropertyList) != 0 { - if f, ok := filter["sensorPropertyList"]; ok { - log.Debug("convertSensorDiscoveryInfoListFromSbi_with_filter: f: ", f) + // Apply filter on sensorPropertyList + if filter["sensorPropertyList"][0] != "saref:any" { // FIXME FSCOM Which values to be used??? + if len(val.SensorPropertyList) == 0 { + log.Debug("convertSensorDiscoveryInfoListFromSbi_with_filter: ", filter["sensorPropertyList"][0], " not found, discard it") + continue // Discard it + } else { found := 0 - for _, fu := range f { - log.Debug("convertSensorDiscoveryInfoListFromSbi_with_filter: processing fu: ", fu) + for _, fu := range filter["sensorPropertyList"] { + log.Debug("convertSensorDiscoveryInfoListFromSbi_with_filter (sensorPropertyList): processing fu: ", fu) l := strings.Split(fu, ",") log.Debug("convertSensorDiscoveryInfoListFromSbi_with_filter: l: ", l) - for _, s := range l { - log.Debug("convertSensorDiscoveryInfoListFromSbi_with_filter: processing s: ", s) - for _, v := range val.SensorPropertyList { - log.Debug("convertSensorDiscoveryInfoListFromSbi_with_filter: processing v: ", v) - if v == s { - found += 1 - log.Debug("convertSensorDiscoveryInfoListFromSbi_with_filter: matching: ", found) - break // Exit loop on sensor SensorPropertyList - } - } // End of 'for' statement - } // End of 'for' statement - log.Debug("convertSensorDiscoveryInfoListFromSbi_with_filter: found: ", found, " compared to ", len(l)) - if found == len(l) { // All filter iems found - sensors = append(sensors, convertSensorDiscoveryInfoFromSbi(val)) - } else { - break // Exit loop on filter list - } + // TODO FSCOM Apply filter on } // End of 'for' statement - // FIXME FSCOM Add filtering - } else { - sensors = append(sensors, convertSensorDiscoveryInfoFromSbi(val)) + if found != len(val.SensorPropertyList) { // Not all filter items found + log.Debug("convertSensorDiscoveryInfoListFromSbi_with_filter: Not all filter items found, discard it") + continue // Discard it + } } - } else { + } // else, continue + + // Apply filter on sensorCharacteristicList + if _, ok := filter["sensorCharacteristicList"]; !ok { // 'sensorCharacteristicList'filter omitted sensors = append(sensors, convertSensorDiscoveryInfoFromSbi(val)) + } else { + found := 0 + for _, fu := range filter["sensorCharacteristicList"] { + log.Debug("convertSensorDiscoveryInfoListFromSbi_with_filter (sensorCharacteristicList): processing fu: ", fu) + l := strings.Split(fu, ",") + log.Debug("convertSensorDiscoveryInfoListFromSbi_with_filter: l: ", l) + // TODO FSCOM Apply filter on + } // End of 'for' statement + if found == len(filter["sensorCharacteristicList"]) { // All filter iems found + sensors = append(sensors, convertSensorDiscoveryInfoFromSbi(val)) + } else { + continue // Skip this item + } + + // if len(val.SensorPropertyList) != 0 { + // if f, ok := filter["sensorPropertyList"]; ok { + // log.Debug("convertSensorDiscoveryInfoListFromSbi_with_filter: f: ", f) + // found := 0 + // for _, fu := range f { + // log.Debug("convertSensorDiscoveryInfoListFromSbi_with_filter: processing fu: ", fu) + // l := strings.Split(fu, ",") + // log.Debug("convertSensorDiscoveryInfoListFromSbi_with_filter: l: ", l) + // for _, s := range l { + // log.Debug("convertSensorDiscoveryInfoListFromSbi_with_filter: processing s: ", s) + // for _, v := range val.SensorPropertyList { + // log.Debug("convertSensorDiscoveryInfoListFromSbi_with_filter: processing v: ", v) + // if v == s { + // found += 1 + // log.Debug("convertSensorDiscoveryInfoListFromSbi_with_filter: matching: ", found) + // break // Exit loop on sensor SensorPropertyList + // } + // } // End of 'for' statement + // } // End of 'for' statement + // log.Debug("convertSensorDiscoveryInfoListFromSbi_with_filter: found: ", found, " compared to ", len(l)) + // if found == len(l) { // All filter iems found + // sensors = append(sensors, convertSensorDiscoveryInfoFromSbi(val)) + // } else { + // break // Exit loop on filter list + // } + // } // End of 'for' statement + // // FIXME FSCOM Add filtering + // } else { + // sensors = append(sensors, convertSensorDiscoveryInfoFromSbi(val)) + // } } } - } // End of 'for' statement log.Debug("convertSensorDiscoveryInfoListFromSbi_with_filter: sensors: ", sensors) @@ -1845,8 +2043,9 @@ func convertSensorDiscoveryInfoListFromSbi_with_filter(s []sbi.SensorDiscoveryIn func convertSensorDiscoveryInfoFromSbi(s sbi.SensorDiscoveryInfo) (sensor SensorDiscoveryInfo) { sensor = SensorDiscoveryInfo{ - SensorIdentifier: s.SensorIdentifier, - SensorType: s.SensorType, + SensorIdentifier: s.SensorIdentifier, + SensorType: s.SensorType, + SensorPropertyList: []string{"SAREF"}, } if len(s.SensorPropertyList) != 0 { copy(sensor.SensorPropertyList, s.SensorPropertyList) @@ -1866,11 +2065,33 @@ func convertSensorDiscoveryInfoFromSbi(s sbi.SensorDiscoveryInfo) (sensor Sensor Latitude: s.SensorPosition.Latitude, Longitude: s.SensorPosition.Longitude, } + } else { // SensorPosition is mandatory + sensor.SensorPosition = &Point{ + Latitude: 0, + Longitude: 0, + } } return sensor } +func convertSensorDataFromSbi(d sbi.SensorData) (data SensorData) { + data = SensorData{ + SensorIdentifier: d.SensorIdentifier, + Data: d.Data, + DataFormat: "", + DataUnitOfMeasure: "", + } + if d.DataTimestamp != nil { + data.DataTimestamp = &TimeStamp{ + Seconds: d.DataTimestamp.Seconds, + NanoSeconds: d.DataTimestamp.NanoSeconds, + } + } + + return data +} + func convertSensorStatusInfoFromSbi(s sbi.SensorStatusInfo) (status SensorStatusInfo) { status = SensorStatusInfo{ SensorIdentifier: s.SensorIdentifier, diff --git a/go-apps/meep-sss/server/model_status_data_subscription_id_body.go b/go-apps/meep-sss/server/model_status_data_subscription_id_body.go index f6b6594b6a0a485869e7d20725293c33ad90228c..c6fa315bc2f698883c2d76919ef01793acf91cc6 100644 --- a/go-apps/meep-sss/server/model_status_data_subscription_id_body.go +++ b/go-apps/meep-sss/server/model_status_data_subscription_id_body.go @@ -9,6 +9,6 @@ */ package server -type StatusDataSubscriptionIdBody struct { +type SensorDataSubscriptionIdBody struct { SensorStatusSubscription *SensorDataSubscription `json:"SensorStatusSubscription,omitempty"` } diff --git a/go-apps/meep-sss/server/routers.go b/go-apps/meep-sss/server/routers.go index 35b06a0dbc8d6ef324419a306f9265eb16458575..a8d164cf5fb93afd80cd01a532b78ebd3b1134f7 100644 --- a/go-apps/meep-sss/server/routers.go +++ b/go-apps/meep-sss/server/routers.go @@ -83,21 +83,21 @@ var routes = Routes{ Route{ "SensorDataLookupGET", strings.ToUpper("Get"), - "/sens/v1/queries/status_data", + "/sens/v1/queries/sensor_data", SensorDataLookupGET, }, Route{ "SensorDataIndividualSubscriptionGET", strings.ToUpper("Get"), - "/sens/v1/queries/status_data/{subscriptionId}", + "/sens/v1/queries/sensor_data/{subscriptionId}", SensorDataIndividualSubscriptionGET, }, Route{ "SensorDataSubscriptionDELETE", strings.ToUpper("Delete"), - "/sens/v1/queries/status_data/{subscriptionId}", + "/sens/v1/queries/sensor_data/{subscriptionId}", SensorDataSubscriptionDELETE, }, @@ -118,7 +118,7 @@ var routes = Routes{ Route{ "SensorDataSubscriptionPUT", strings.ToUpper("Put"), - "/sens/v1/queries/status_data/{subscriptionId}", + "/sens/v1/queries/sensor_data/{subscriptionId}", SensorDataSubscriptionPUT, }, diff --git a/go-apps/meep-tm/server/bwm/bwm.go b/go-apps/meep-tm/server/bwm/bwm.go index b9f62254b4242286fc2b84de5fb640dc54e486bb..b4890ea9200c3e2d82fede6b88618d2180e30c9b 100644 --- a/go-apps/meep-tm/server/bwm/bwm.go +++ b/go-apps/meep-tm/server/bwm/bwm.go @@ -689,6 +689,12 @@ func bandwidthAllocationListGet(w http.ResponseWriter, r *http.Request) { return } + // If filters were provided but no match found → return 404 + if (len(appInstanceId) > 0 || len(appName) > 0 || len(sessionId) > 0) && len(bwInfoList.SessionList) == 0 { + errHandlerProblemDetails(w, "No bandwidth allocations found matching the filter criteria", http.StatusNotFound) + return + } + // Prepare & send response jsonResponse, err := json.Marshal(bwInfoList.SessionList) if err != nil { @@ -853,24 +859,30 @@ func bandwidthAllocationPatch(w http.ResponseWriter, r *http.Request) { Seconds: int32(seconds), } - if (bwInfoDeltaInput.AllocationDirection != "") || (bwInfoDeltaInput.FixedAllocation != "") { - // validate and update the request changes in Allocation direction or Fixed Allocation (bps) - if bwInfoDeltaInput.AllocationDirection != "" { - if (bwInfoDeltaInput.AllocationDirection == "00") || (bwInfoDeltaInput.AllocationDirection == "01") || (bwInfoDeltaInput.AllocationDirection == "10") { - newBwInfo.AllocationDirection = bwInfoDeltaInput.AllocationDirection - } - } else { - newBwInfo.AllocationDirection = bwInfoStored.AllocationDirection - } - if bwInfoDeltaInput.FixedAllocation != "" { - newBwInfo.FixedAllocation = bwInfoDeltaInput.FixedAllocation - } else { - newBwInfo.FixedAllocation = bwInfoStored.FixedAllocation - } - err = UpdateAllocationDirection(&newBwInfo, &bwInfoStored, w) - if err != nil { + // Validate and assign allocationDirection + if bwInfoDeltaInput.AllocationDirection != "" { + switch bwInfoDeltaInput.AllocationDirection { + case "00", "01", "10": + newBwInfo.AllocationDirection = bwInfoDeltaInput.AllocationDirection + default: + errHandlerProblemDetails(w, "Invalid allocationDirection value. Allowed values: 00, 01, 10.", http.StatusBadRequest) return } + } else { + newBwInfo.AllocationDirection = bwInfoStored.AllocationDirection + } + + // Assign FixedAllocation, defaulting to stored value + if bwInfoDeltaInput.FixedAllocation != "" { + newBwInfo.FixedAllocation = bwInfoDeltaInput.FixedAllocation + } else { + newBwInfo.FixedAllocation = bwInfoStored.FixedAllocation + } + + // Call the update function + err = UpdateAllocationDirection(&newBwInfo, &bwInfoStored, w) + if err != nil { + return } // setBwInfo function takes input of new BW allocation information and @@ -913,19 +925,12 @@ func bandwidthAllocationPost(w http.ResponseWriter, r *http.Request) { return } - // verify RequestType attribute is either 1 or 0 + // Check requestType is provided if bwInfo.RequestType != nil { - if (*bwInfo.RequestType == 0) || (*bwInfo.RequestType == 1) { - if (bwInfo.AllocationDirection == "00") || (bwInfo.AllocationDirection == "01") || (bwInfo.AllocationDirection == "10") { - log.Debug("Valid Mandatory attribute allocationDirection") - } else { - log.Error("Valid allocationDirection value should be present") - errHandlerProblemDetails(w, "Valid allocationDirection value should be present in the request body", http.StatusBadRequest) - return - } - } else { - log.Error("Valid requestType value should be present") - errHandlerProblemDetails(w, "Valid requestType value should be present in the request body", http.StatusBadRequest) + // Check requestType value is valid (0 or 1) + if *bwInfo.RequestType != 0 && *bwInfo.RequestType != 1 { + log.Error("Valid requestType value should be 0 or 1") + errHandlerProblemDetails(w, "Valid requestType value should be 0 or 1", http.StatusBadRequest) return } } else { @@ -934,6 +939,27 @@ func bandwidthAllocationPost(w http.ResponseWriter, r *http.Request) { return } + // sessionFilter must not be present when requestType is 0 (application-specific) + if *bwInfo.RequestType == 0 && len(bwInfo.SessionFilter) != 0 { + log.Error("sessionFilter must not be present for application-specific bandwidth allocations.") + errHandlerProblemDetails(w, "sessionFilter must not be present when requestType is 0 (application-specific).", http.StatusBadRequest) + return + } + + // Check allocationDirection and fixedAllocation are provided + if bwInfo.AllocationDirection == "" || bwInfo.FixedAllocation == "" { + log.Error("Mandatory attribute allocationDirection or fixedAllocation is missing") + errHandlerProblemDetails(w, "Mandatory attribute allocationDirection or fixedAllocation is missing", http.StatusBadRequest) + return + } + + // Check allocationDirection value is valid + if bwInfo.AllocationDirection != "00" && bwInfo.AllocationDirection != "01" && bwInfo.AllocationDirection != "10" { + log.Error("Invalid value for allocationDirection") + errHandlerProblemDetails(w, "allocationDirection must be one of: '00', '01', or '10'", http.StatusBadRequest) + return + } + // Validate Mandatory attribute "FixedAllocation" if bwInfo.FixedAllocation != "" { valFixedBuff, err := strconv.ParseUint(bwInfo.FixedAllocation, 10, 64) @@ -982,8 +1008,10 @@ func bandwidthAllocationPost(w http.ResponseWriter, r *http.Request) { return } - if *bwInfo.RequestType == 0 { - bwInfo.SessionFilter = nil + if *bwInfo.RequestType == 0 && len(bwInfo.SessionFilter) != 0 { + log.Error("sessionFilter must not be present for application-specific bandwidth allocations.") + errHandlerProblemDetails(w, "sessionFilter must not be present when requestType is 0 (application-specific).", http.StatusBadRequest) + return } if *bwInfo.RequestType == 1 && bwInfo.SessionFilter != nil { @@ -1108,6 +1136,10 @@ func bandwidthAllocationPost(w http.ResponseWriter, r *http.Request) { errHandlerProblemDetails(w, "Unable to store new Allocation in redis", http.StatusInternalServerError) return } + + // Set Location header using AllocationId + location := fmt.Sprintf("%s%s", hostUrl.String(), basePath) + "bw_allocations/" + bwInfo.AllocationId + w.Header().Set("Location", location) w.WriteHeader(http.StatusCreated) fmt.Fprint(w, string(jsonResponse)) } @@ -1171,6 +1203,13 @@ func bandwidthAllocationPut(w http.ResponseWriter, r *http.Request) { } } + // Reject sessionFilter if requestType is 0 (application-specific) + if *bwInfoInput.RequestType == 0 && len(bwInfoInput.SessionFilter) > 0 { + log.Error("sessionFilter must not be present for application-specific allocations (requestType=0)") + errHandlerProblemDetails(w, "sessionFilter must not be present for application-specific allocations (requestType=0)", http.StatusBadRequest) + return + } + if (bwInfoInput.AllocationDirection == "") || (bwInfoInput.FixedAllocation == "") { log.Error("Mandatory attribute allocationDirection or fixedAllocation is Missing") errHandlerProblemDetails(w, "Mandatory attribute allocationDirection or fixedAllocation is Missing", http.StatusBadRequest) diff --git a/go-apps/meep-tm/server/mts/model_mts_session_info.go b/go-apps/meep-tm/server/mts/model_mts_session_info.go index 4266f391871379fdecec3d74e914fc864b3a94b8..6f219a16cb8e22c70bd799b949c9fbd81c2b06a1 100644 --- a/go-apps/meep-tm/server/mts/model_mts_session_info.go +++ b/go-apps/meep-tm/server/mts/model_mts_session_info.go @@ -31,7 +31,7 @@ type MtsSessionInfo struct { // Name of the application AppName string `json:"appName,omitempty"` // Traffic flow filtering criteria, applicable only if when requestType is set as FLOW_SPECIFIC_MTS_SESSION. Any filtering criteria shall define a single session only. In case multiple sessions match flowFilter the request shall be rejected. If the flowFilter field is included, at least one of its subfields shall be included. Any flowFilter subfield that is not included shall be ignored in traffic flow filtering - FlowFilter []MtsSessionInfoFlowFilter `json:"flowFilter"` + FlowFilter []MtsSessionInfoFlowFilter `json:"flowFilter,omitempty"` // Numeric value (0 to 255) corresponding to a specific MTS mode of the MTS session: 0 = low cost, i.e. using the unmetered access network connection whenever it is available 1 = low latency, i.e. using the access network connection with lower latency 2 = high throughput, i.e. using the access network connection with higher throughput, or multiple access network connection simultaneously 3 = redundancy, i.e. sending duplicated (redundancy) packets over multiple access network connections for high-reliability and low-latency applications 4 = QoS, i.e. performing MTS based on the QoS requirement (qosD) MtsMode *uint32 `json:"mtsMode"` // Indicates the requested ratio between multiple access networks for the network aggregation\\ \\ required for higher throughput. Indicates the requested ratio between multiple access networks for \\ \\ the network aggregation required for higher throughput. This is based on the existing 3GPP ATSSS feature\\ \\ specified in clause 5.32.8 of ETSI TS 123 501 [i.5]. diff --git a/go-apps/meep-tm/server/mts/mts.go b/go-apps/meep-tm/server/mts/mts.go index c090cf39b7b778c04de93832415dfb30ca2da14b..a6371a5501442ffde8acef1aff7f1a42b94e2370 100644 --- a/go-apps/meep-tm/server/mts/mts.go +++ b/go-apps/meep-tm/server/mts/mts.go @@ -796,6 +796,9 @@ func mtsSessionPost(w http.ResponseWriter, r *http.Request) { // Prepare & send response jsonResponse := convertMtsSessionInfoToJson(&requestBody) + // Set Location header using AllocationId + location := fmt.Sprintf("%s%s", hostUrl.String(), basePath) + "mts_sessions/" + sessionIdStr + w.Header().Set("Location", location) w.WriteHeader(http.StatusCreated) fmt.Fprint(w, jsonResponse) } @@ -1262,6 +1265,12 @@ func mtsSessionsListGet(w http.ResponseWriter, r *http.Request) { return } + // If filters were provided but no match found → return 404 + if (len(appInstanceId) > 0 || len(appName) > 0 || len(sessionId) > 0) && len(mtsSessionInfoList.SessionList) == 0 { + errHandlerProblemDetails(w, "No MTS sessions found matching the filter criteria", http.StatusNotFound) + return + } + // Prepare & send response jsonResponse, err := json.Marshal(mtsSessionInfoList.SessionList) if err != nil { diff --git a/go-apps/meep-virt-engine/entrypoint.sh b/go-apps/meep-virt-engine/entrypoint.sh index 0eb84b7a20590a29dbb5ae78d33f3d4cbb634c81..faea0bd6f90d9ddbb20326da34ac6b0cd3450334 100755 --- a/go-apps/meep-virt-engine/entrypoint.sh +++ b/go-apps/meep-virt-engine/entrypoint.sh @@ -22,6 +22,14 @@ mv /meep-tc-engine /templates/sandbox/meep-tc-engine mv /meep-vis /templates/sandbox/meep-vis mv /meep-dai /templates/sandbox/meep-dai mv /meep-tm /templates/sandbox/meep-tm +mv /meep-iot /templates/sandbox/meep-iot +mv /meep-sss /templates/sandbox/meep-sss +mv /meep-mosquitto /templates/sandbox/meep-mosquitto +mv /meep-cloud-mosquitto /templates/sandbox/meep-cloud-mosquitto +mv /meep-acme-in-cse /templates/sandbox/meep-acme-in-cse +mv /meep-acme-mn-cse /templates/sandbox/meep-acme-mn-cse +mv /meep-tinyiot-in-cse /templates/sandbox/meep-tinyiot-in-cse +mv /meep-tinyiot-mn-cse /templates/sandbox/meep-tinyiot-mn-cse mkdir -p /templates/scenario mv /meep-virt-chart-templates /templates/scenario/meep-virt-chart-templates diff --git a/go-apps/meep-vis/entrypoint.sh b/go-apps/meep-vis/entrypoint.sh index 79986b0a1d654c941a676790c4e513df67b6cbd3..b12c3895c1577190aa90491f3b3aedc2c84e24b6 100755 --- a/go-apps/meep-vis/entrypoint.sh +++ b/go-apps/meep-vis/entrypoint.sh @@ -5,10 +5,10 @@ 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://broker.emqx.io:1883 or amqp://ofriqrpk:e_vS3dw1zs2gb8CVlyzGwQZ8gCRoyTt5@lrat-01.rmq2.cloudamqp.com:5672 -echo "MEEP_TOPIC: ${MEEP_TOPIC}" # E.g. 3gpp/v2x/obu or AMQP queue name echo "MEEP_POA_LIST: ${MEEP_POA_LIST}" # E.g. poa-5g1;poa-5g2 +MEEP_BROKER="wss://$MEEP_HOST_URL:1883/$MEEP_SANDBOX_NAME/$MEEP_MEP_NAME/meep-mosquitto" + if [[ ! -z "${MEEP_MEP_NAME}" ]]; then svcPath="${MEEP_SANDBOX_NAME}/${MEEP_MEP_NAME}" else @@ -41,6 +41,15 @@ for file in /user-api/*; do setBasepath "$file" done +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_INSTANCE_ID: ${MEEP_INSTANCE_ID}" +echo "MEEP_BROKER: ${MEEP_BROKER}" # E.g. mqtt://broker.emqx.io:1883 or amqp://ofriqrpk:e_vS3dw1zs2gb8CVlyzGwQZ8gCRoyTt5@lrat-01.rmq2.cloudamqp.com:5672 +echo "MEEP_TOPIC: ${MEEP_TOPIC}" # E.g. 3gpp/v2x/obu or AMQP queue name +echo "MEEP_POA_LIST: ${MEEP_POA_LIST}" # E.g. poa-5g1;poa-5g2 + # Start service currenttime=`date "+%Y%m%d-%H%M%S"` filepath="/codecov/codecov-meep-vis-" diff --git a/go-apps/meep-vis/server/vis_test.go b/go-apps/meep-vis/server/vis_test.go index 0a008d0b5740622756244230eea9f9bdc3a7c107..ede38486f83d0363e3e35d8fc7dea6bf1d58a5ee 100644 --- a/go-apps/meep-vis/server/vis_test.go +++ b/go-apps/meep-vis/server/vis_test.go @@ -2110,7 +2110,7 @@ func TestV2xMsgDistributionServerPost(t *testing.T) { msgProtocol := make([]int32, 1) msgProtocol[0] = 0 infoProtocol := &InfoProtocol{MsgProtocol: msgProtocol, ProtImplementation: ""} - expected_infoConnection := &InfoConnection{IpAddress: "test.mosquito.org", PortNumber: 1338} + expected_infoConnection := &InfoConnection{IpAddress: "test.mosquitto.org", PortNumber: 1338} expected_v2xMsgDistributionServer := make([]V2xMsgDistributionServer, 1) expected_v2xMsgDistributionServer[0] = V2xMsgDistributionServer{InfoConnection: expected_infoConnection, InfoProtocol: infoProtocol} expected_v2xMsgDistributionServerInfo := V2xMsgDistributionServerInfo{ @@ -2190,7 +2190,7 @@ func TestFailV2xMsgDistributionServerPost(t *testing.T) { msgProtocol := make([]int32, 1) msgProtocol[0] = 0 infoProtocol := &InfoProtocol{MsgProtocol: msgProtocol, ProtImplementation: ""} - expected_infoConnection := &InfoConnection{IpAddress: "test.mosquito.org", PortNumber: 1338} + expected_infoConnection := &InfoConnection{IpAddress: "test.mosquitto.org", PortNumber: 1338} expected_v2xMsgDistributionServer := make([]V2xMsgDistributionServer, 1) expected_v2xMsgDistributionServer[0] = V2xMsgDistributionServer{InfoConnection: expected_infoConnection, InfoProtocol: infoProtocol} expected_v2xMsgDistributionServerInfo := V2xMsgDistributionServerInfo{ diff --git a/go-apps/meepctl/cmd/version.go b/go-apps/meepctl/cmd/version.go index ecd5804691394dfc1cb1806e675a80760567ab55..0c5d4355c72bd1704e004e5cf378f418e0b145d8 100644 --- a/go-apps/meepctl/cmd/version.go +++ b/go-apps/meepctl/cmd/version.go @@ -41,7 +41,7 @@ type versionInfo struct { BuildID string `json:"build,omitempty"` } -const meepctlVersion = "1.10.0" +const meepctlVersion = "1.11.0" const na = "NA" const versionDesc = `Display version information diff --git a/go-apps/meepctl/utils/config.go b/go-apps/meepctl/utils/config.go index 1bbe4e5df607975014791288d0f2851a67d7bb62..198b790ebe68375be23228c29f0b3aed9d1714a2 100644 --- a/go-apps/meepctl/utils/config.go +++ b/go-apps/meepctl/utils/config.go @@ -31,7 +31,7 @@ import ( yaml "gopkg.in/yaml.v2" ) -const configVersion = "1.10.0" +const configVersion = "1.11.0" const defaultNotSet = "not set" diff --git a/go-packages/meep-dai-mgr/meep-dai-mgr.go b/go-packages/meep-dai-mgr/meep-dai-mgr.go index ec7156fe27aad9d51e0d710eb3ffb5884e59c6fb..b9360c223da966689d9bdf6accffcab826c1c406 100644 --- a/go-packages/meep-dai-mgr/meep-dai-mgr.go +++ b/go-packages/meep-dai-mgr/meep-dai-mgr.go @@ -1035,23 +1035,6 @@ func (am *DaiMgr) CreateAppContext(appContext *AppContext, remoteUrl string, san return nil, errors.New("CreateAppContext: Invalid input parameters") } app = appContext - if app.ContextId != "" { // ETSI GS MEC 016 Clause 6.2.3 Type: AppContext. - return nil, errors.New("ContextId shall not be set") - } - if app.AssociateDevAppId == "" { // ETSI GS MEC 016 Clause 6.2.3 Type: AppContext. - return nil, errors.New("Missing AssociateDevAppId") - } - if len(app.AppInfo.UserAppInstanceInfo) == 0 { // ETSI GS MEC 016 Clause 6.2.3 Type: AppContext. - return nil, errors.New("Missing at least one UserAppInstanceInfo item") - } - for _, item := range app.AppInfo.UserAppInstanceInfo { - if item.AppInstanceId != "" { - return nil, errors.New("UserAppInstanceInfo.AppInstanceId shall not be set") - } - if item.ReferenceURI != "" { - return nil, errors.New("UserAppInstanceInfo.ReferenceURI shall not be set") - } - } // End of 'for' statement // Whe creating the context, instantiate the application // Retrieve the MEC application description @@ -1151,54 +1134,63 @@ func (am *DaiMgr) CreateAppContext(appContext *AppContext, remoteUrl string, san } func (am *DaiMgr) PutAppContext(appContext AppContext) (err error) { - // if profiling { - // profilingTimers["PutAppContext"] = time.Now() - // } - - // // Sanity checks - // if appContext.ContextId == nil || *appContext.ContextId == "" { // ETSI GS MEC 016 Clause 6.2.3 Type: AppContext. - // return errors.New("ContextId shall be set") - // } - // log.Debug("PutAppContext: appContext: ", appContext) + if profiling { + profilingTimers["PutAppContext"] = time.Now() + } - // // Retrieve the existing AppContext - // curAppContext, err := am.GetAppContextRecord(*appContext.ContextId) - // if err != nil { - // return errors.New("ContextId not found") - // } - // log.Debug("PutAppContext: curAppContext: ", curAppContext) + // Retrieve the existing AppContext + curAppContext, err := am.GetAppContextRecord(appContext.ContextId) + if err != nil { + return errors.New("ContextId not found") + } + log.Debug("PutAppContext: curAppContext: ", curAppContext) - // // Update the curAppContext - // update := false + // Update the curAppContext + update := false // if curAppContext.CallbackReference != appContext.CallbackReference { // curAppContext.CallbackReference = appContext.CallbackReference // update = true // } - // if update { - // query := `UPDATE ` + AppContextTable + ` SET callbackReference = ($1) WHERE contextId = ($2)` - // result, err := am.db.Exec(query, curAppContext.CallbackReference, *curAppContext.ContextId) - // if err != nil { - // log.Error(err.Error()) - // return err - // } - // rowCnt, err := result.RowsAffected() - // if err != nil { - // log.Fatal(err) - // return err - // } - // if rowCnt == 0 { - // return errors.New("Failed to update record") - // } - - // // Notify listener - // am.notifyListener(*curAppContext.ContextId) - // } + if update { + txn := am.db.Txn(false) + raw, err := txn.First("AppContextTable", "ContextId", curAppContext.ContextId) + if err != nil { + txn.Abort() + log.Error(err.Error()) + return err + } + txn.Abort() + if raw == nil { + err = errors.New("Wrong ContextId") + log.Error(err.Error()) + return err + } - // if profiling { - // now := time.Now() - // log.Debug("PutAppContext: ", now.Sub(profilingTimers["PutAppContext"])) - // } + txn = am.db.Txn(true) + record := &AppContextTable{ + ContextId: curAppContext.ContextId, + AssociateDevAppId: curAppContext.AssociateDevAppId, + CallbackReference: NilToEmptyUri(curAppContext.CallbackReference), + AppLocationUpdates: curAppContext.AppLocationUpdates, + AppAutoInstantiation: curAppContext.AppAutoInstantiation, + AppDId: curAppContext.AppInfo.AppDId, + } + err = txn.Insert("AppContextTable", record) + if err != nil { + txn.Abort() + log.Error(err.Error()) + return err + } + + // Notify listener + am.notifyListener(curAppContext.ContextId) + } + + if profiling { + now := time.Now() + log.Debug("PutAppContext: ", now.Sub(profilingTimers["PutAppContext"])) + } return nil } diff --git a/go-packages/meep-federation-mgr/federation-mgr.go b/go-packages/meep-federation-mgr/federation-mgr.go index 8c5e2a525a9cb354559d0c8b8f00c2c34feac078..c1f944300fdf1e0e56ff2c4bea7b65de6200e4c1 100644 --- a/go-packages/meep-federation-mgr/federation-mgr.go +++ b/go-packages/meep-federation-mgr/federation-mgr.go @@ -82,13 +82,10 @@ func (fm *FederationMgr) DeleteFederationMgr(systemId string) (err error) { err := fm.PublishMessageOnMessageBroker("", systemId) // Empty message to remove retain message if err != nil { log.Error(err.Error()) - //return err + return err } } - // Stop MQTT server - //fm.StopFedMessageBrokerServer() - return nil } @@ -98,7 +95,8 @@ func (fm *FederationMgr) DeleteFederationMgr(systemId string) (err error) { * @return {struct} nil on success, error otherwise */ func (fm *FederationMgr) PublishMessageOnMessageBroker(msgContent string, systemId string) (err error) { - log.Info("PublishMessageOnMessageBroker: brokerRunning: ", brokerRunning) + log.Info("PublishMessageOnMessageBroker: brokerRunning: brokerRunning=", brokerRunning) + log.Info("PublishMessageOnMessageBroker: brokerRunning: msgContent=", msgContent) if !brokerRunning { err = errors.New("Message broker mechanism not initialized") @@ -123,7 +121,7 @@ func (fm *FederationMgr) StartFedMessageBrokerServer() (err error) { return err } //log.Info("url:%v - scheme:%v - host:%v - Path:%v - Port:%s", u, u.Scheme, u.Hostname(), u.Path, u.Port()) - if u.Scheme == "mqtt" { + if u.Scheme == "mqtt" || u.Scheme == "ws" || u.Scheme == "wss" { fm.message_broker = &message_broker_mqtt{false, nil, fm.fed_notify} } else { err = errors.New("Invalid url " + fm.broker) diff --git a/go-packages/meep-federation-mgr/go.mod b/go-packages/meep-federation-mgr/go.mod index cc5d6a80d5091887c8d394b918315b13eb2c5a9a..74f7e627a18f12e316d0b82f3a1e01680e151833 100644 --- a/go-packages/meep-federation-mgr/go.mod +++ b/go-packages/meep-federation-mgr/go.mod @@ -6,6 +6,7 @@ require ( github.com/BurntSushi/toml v1.2.0 // indirect github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-logger v0.0.0 github.com/eclipse/paho.mqtt.golang v1.4.2 + github.com/google/uuid v1.6.0 github.com/lib/pq v1.10.9 github.com/roymx/viper v1.3.3-0.20190416163942-b9a223fc58a3 diff --git a/go-packages/meep-federation-mgr/go.sum b/go-packages/meep-federation-mgr/go.sum index dfa12049bd8515ea5a4f820023e07c66abdf4e75..c170690681aeee75416e8764966a473805985031 100644 --- a/go-packages/meep-federation-mgr/go.sum +++ b/go-packages/meep-federation-mgr/go.sum @@ -12,6 +12,8 @@ github.com/eclipse/paho.mqtt.golang v1.4.3 h1:2kwcUGn8seMUfWndX0hGbvH8r7crgcJguQ github.com/eclipse/paho.mqtt.golang v1.4.3/go.mod h1:CSYvoAlsMkhYOXh/oKyxa8EcBci6dVkLCbo5tTC1RIE= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= diff --git a/go-packages/meep-federation-mgr/mqtt.go b/go-packages/meep-federation-mgr/mqtt.go index 6be3641fa5a29b97a3da43c9811b3ff101460174..0e5f2d82a402ee350f03ae1efb885bd09c26b5f7 100644 --- a/go-packages/meep-federation-mgr/mqtt.go +++ b/go-packages/meep-federation-mgr/mqtt.go @@ -17,13 +17,15 @@ package federationmgr import ( + "crypto/tls" "errors" "fmt" - "net/url" "strings" log "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-logger" mqtt "github.com/eclipse/paho.mqtt.golang" + + "github.com/google/uuid" ) const ( @@ -73,24 +75,21 @@ var connectLostHandler mqtt.ConnectionLostHandler = func(client mqtt.Client, err func (broker_mqtt *message_broker_mqtt) Init(tm *FederationMgr) (err error) { log.Debug(">>> message_broker_mqtt: Init") + log.Debug("Add brocker: tm.broker:", tm.broker) broker_mqtt.running = false - u, err := url.ParseRequestURI(tm.broker) - if err != nil { - log.Error(err.Error()) - return err - } - broker_mqtt.opts = mqtt.NewClientOptions() broker_mqtt.opts.SetDefaultPublishHandler(messagePubHandler /*onMessageReceived*/) - //broker_mqtt.opts.SetClientID("AdvantEDGE.meep-federation-mgr") + broker_mqtt.opts.SetClientID("meep-federation-" + uuid.New().String()) broker_mqtt.opts.OnConnect = connectHandler broker_mqtt.opts.OnConnectionLost = connectLostHandler - //broker_mqtt.opts.SetUsername("emqx") - //broker_mqtt.opts.SetPassword("public") - log.Info("Add brocker: ", fmt.Sprintf("tcp://%s:%s", u.Hostname(), u.Port())) - broker_mqtt.opts.AddBroker(fmt.Sprintf("tcp://%s:%s", u.Hostname(), u.Port())) + broker_mqtt.opts.SetUsername("") + broker_mqtt.opts.SetPassword("") + if strings.Index(tm.broker, "mqtts://") == 0 || strings.Index(tm.broker, "wss://") == 0 { + broker_mqtt.opts.SetTLSConfig(&tls.Config{InsecureSkipVerify: true}) + } + broker_mqtt.opts.AddBroker(tm.broker) client = mqtt.NewClient(broker_mqtt.opts) log.Info("Connect to MQTT server...") diff --git a/go-packages/meep-iot-client/README.md b/go-packages/meep-iot-client/README.md index b7c10a4112b6f85fb27010d77121ba44bf74257c..83e66044fc625c6e02564b0576b1401f0a77272f 100644 --- a/go-packages/meep-iot-client/README.md +++ b/go-packages/meep-iot-client/README.md @@ -18,7 +18,7 @@ import "./swagger" ## Documentation for API Endpoints -All URIs are relative to *https://localhost/sandboxname/sandboxname/amsi/v1* +All URIs are relative to *https://localhost/sandboxname/iots/v1* Class | Method | HTTP request | Description ------------ | ------------- | ------------- | ------------- diff --git a/go-packages/meep-iot-client/configuration.go b/go-packages/meep-iot-client/configuration.go index b2852ee53df3856c8370b2bf3494cb7cc6bc49d5..5f393e3c2d3512642f45ac0f5d69f767051d61b1 100644 --- a/go-packages/meep-iot-client/configuration.go +++ b/go-packages/meep-iot-client/configuration.go @@ -60,7 +60,7 @@ type Configuration struct { func NewConfiguration() *Configuration { cfg := &Configuration{ - BasePath: "https://localhost/sandboxname/sandboxname/amsi/v1", + BasePath: "https://localhost/sandboxname/iots/v1", DefaultHeader: make(map[string]string), UserAgent: "Swagger-Codegen/1.0.0/go", } diff --git a/go-packages/meep-iot-client/docs/RegDevApi.md b/go-packages/meep-iot-client/docs/RegDevApi.md index 6927235585df40767313da72af2385176f36bafb..08da2c2506d5ef6468da9b7c39736303befa4e93 100644 --- a/go-packages/meep-iot-client/docs/RegDevApi.md +++ b/go-packages/meep-iot-client/docs/RegDevApi.md @@ -1,6 +1,6 @@ # {{classname}} -All URIs are relative to *https://localhost/sandboxname/sandboxname/amsi/v1* +All URIs are relative to *https://localhost/sandboxname/iots/v1* Method | HTTP request | Description ------------- | ------------- | ------------- diff --git a/go-packages/meep-iot-client/docs/RegIotPlatApi.md b/go-packages/meep-iot-client/docs/RegIotPlatApi.md index 3041d85f50b2c75458a2e2b7eb598263178aa413..83062531a92c76668ceae3ae90db693025a4bead 100644 --- a/go-packages/meep-iot-client/docs/RegIotPlatApi.md +++ b/go-packages/meep-iot-client/docs/RegIotPlatApi.md @@ -1,6 +1,6 @@ # {{classname}} -All URIs are relative to *https://localhost/sandboxname/sandboxname/amsi/v1* +All URIs are relative to *https://localhost/sandboxname/iots/v1* Method | HTTP request | Description ------------- | ------------- | ------------- diff --git a/go-packages/meep-iot-mgr/go.mod b/go-packages/meep-iot-mgr/go.mod index d9b09212a86a776b391f2f2ab8bc2a2bbe6f034f..c082d159a3aaa746ec7de005658eb881edd0cbe0 100644 --- a/go-packages/meep-iot-mgr/go.mod +++ b/go-packages/meep-iot-mgr/go.mod @@ -4,6 +4,7 @@ go 1.16 require ( github.com/BurntSushi/toml v1.2.0 // indirect + github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-sss-mgr v0.0.0 github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-logger v0.0.0 github.com/eclipse/paho.mqtt.golang v1.4.2 github.com/google/uuid v1.6.0 @@ -16,4 +17,5 @@ require ( replace ( github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-logger => ../../go-packages/meep-logger + github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-sss-mgr => ../../go-packages/meep-sss-mgr ) diff --git a/go-packages/meep-iot-mgr/iot-mgr.go b/go-packages/meep-iot-mgr/iot-mgr.go index a517df845209a71ebd8e6be5a984b07d60aa7318..848b63e59d159063edd77a44c0fdcec6a0d67437 100644 --- a/go-packages/meep-iot-mgr/iot-mgr.go +++ b/go-packages/meep-iot-mgr/iot-mgr.go @@ -105,12 +105,19 @@ type TrafficRuleDescriptor struct { type InterfaceDescriptor struct { InterfaceType string - //TunnelInfo *TunnelInfo FSCOM Not supported + TunnelInfo *TunnelInfo SrcMACAddress string DstMACAddress string DstIPAddress string } +type TunnelInfo struct { + TunnelType string + TunnelDstAddress string + TunnelSrcAddress string + TunnelSpecificData string +} + type TrafficFilter struct { SrcAddress []string DstAddress []string @@ -125,6 +132,7 @@ type TrafficFilter struct { SrcTunnelPort []string DstTunnelPort []string QCI int32 + DSCP int32 TC int32 } @@ -133,30 +141,68 @@ type KeyValuePair struct { Value string } +type DownlinkInfo struct { + DownlinkTopic string + DevicePort int32 +} + +type DeviceSpecificMessageFormats struct { + EventMsgFormat *EventMsg + UplinkMsgFormat *UplinkMsg +} + +type EventMsg struct { + EventTopic string + SelectedSerializer *string + IncludeDeviceAddr bool + IncludeDeviceMetadata bool + IncludePei bool + IncludeSupi bool + IncludeImei bool + IncludeImsi bool + IncludeIccid bool + IncludeDeviceId bool +} + +type UplinkMsg struct { + UplinkTopic string + SelectedSerializer *string + IncludeDevicePort bool + IncludeDeviceAddr bool + IncludeDeviceMetadata bool + IncludePei bool + IncludeSupi bool + IncludeImei bool + IncludeImsi bool + IncludeIccid bool + IncludeDeviceId bool +} + 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 + 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 +var registeredIotPlatformsAEMap = map[string]sssmgr.SensorDiscoveryInfo{} // Map AE created per IOT Plateform // Timer to refresh devices list for all IoT platform const refreshTickerExpeary = 30 // In seconds @@ -202,11 +248,18 @@ func (tm *IotMgr) init() { devicesMap = make(map[string]DeviceInfo, 0) devicesPerPlatformMap = make(map[string][]string, 0) platformPerUserTransportIdMap = make(map[string][]string, 0) + registeredIotPlatformsAEMap = make(map[string]sssmgr.SensorDiscoveryInfo, 0) tm.refreshTicker = nil } // DeleteIotMgr - func (tm *IotMgr) DeleteIotMgr() (err error) { + registeredIotPlatformsMap = nil + devicesMap = nil + devicesPerPlatformMap = nil + platformPerUserTransportIdMap = nil + registeredIotPlatformsAEMap = nil + tm.refreshTicker = nil return nil } @@ -219,7 +272,7 @@ func (tm *IotMgr) startRefreshTicker() { for range tm.refreshTicker.C { // Refresh the list of devices tm.wg.Add(1) - log.Debug("startRefreshTicker: registeredIotPlatformsMap: ", registeredIotPlatformsMap) + log.Debug("iotmgr.startRefreshTicker: registeredIotPlatformsMap: ", registeredIotPlatformsMap) for _, v := range registeredIotPlatformsMap { if v.oneM2M != nil { err := tm.populateDevicesPerIotPlatforms(v) @@ -227,14 +280,14 @@ func (tm *IotMgr) startRefreshTicker() { log.Error(err) } } else { - log.Debug("startRefreshTicker: Nothing to do") + log.Debug("iotmgr.startRefreshTicker: Nothing to do") } } // End of 'for' statement - log.Debug("startRefreshTicker: Before Done()") + log.Debug("iotmgr.startRefreshTicker: Before Done()") tm.wg.Done() - log.Debug("startRefreshTicker: After Done()") + log.Debug("iotmgr.startRefreshTicker: After Done()") } // End of 'for' statement - log.Debug("startRefreshTicker: Leaving time loop") + log.Debug("iotmgr.startRefreshTicker: Leaving time loop") } }() } @@ -257,20 +310,33 @@ func (tm *IotMgr) RegisterIotPlatformInfo(iotPlatformInfo IotPlatformInfo) (err log.Info(">>> RegisterIotPlatformInfo: iotPlatformId: ", iotPlatformInfo) if iotPlatformInfo.Enabled { - //{{\"iotPlatformId\": \"1a584db5-6a3e-4f56-b126-29180069ecf1\", \"userTransportInfo\": [{\"id\": \"ca22ca5e-e0ce-4da8-a2ce-2966f4759032\", \"name\": \"MQTT\", \"description\": \"MQTT\", \"type\": \"MB_TOPIC_BASED\", \"protocol\": \"MQTT\", \"version\": \"2\", \"endpoint\": {\"addresses\": [{\"host\": \"172.29.10.56\", \"port\": 1883}]}, \"security\": {}, \"implSpecificInfo\": {}}], \"customServicesTransportInfo\": [{\"id\": \"85fe5e7f-c371-4f71-b7f6-61a1f808fbb3\", \"name\": \"/laboai-acme-ic-cse\", \"description\": \"ACME oneM2M CSE\", \"type\": \"REST_HTTP\", \"protocol\": \"REST_HTTP\", \"version\": \"4\", \"endpoint\": {\"addresses\": [{\"host\": \"172.29.10.20\", \"port\": 31110}]}, \"security\": {}}], \"enabled\": true}} iotPlatformInfo.oneM2M = nil - if len(iotPlatformInfo.CustomServicesTransportInfo) == 0 || iotPlatformInfo.CustomServicesTransportInfo[0].Endpoint == nil || len(iotPlatformInfo.CustomServicesTransportInfo[0].Endpoint.Addresses) == 0 { + if len(iotPlatformInfo.UserTransportInfo) == 0 || iotPlatformInfo.UserTransportInfo[0].Endpoint == nil || len(iotPlatformInfo.UserTransportInfo[0].Endpoint.Addresses) == 0 { log.Warn("RegisterIotPlatformInfo: Cannot use provided CustomServicesTransportInfo") } else { - // FIXME FSCOM How to get the CSE_ID + // FIXME FSCOM How to get the CSE_ID. For the time being, iotPlatformInfo.UserTransportInfo[0].Name is the CSE-resourceID // TODO FSCOM Add notification support? - pltf, err := sssmgr.NewSssMgr(tm.name, tm.namespace, iotPlatformInfo.CustomServicesTransportInfo[0].Protocol /*"MQTT"*/, iotPlatformInfo.CustomServicesTransportInfo[0].Endpoint.Addresses[0].Host /*"172.29.10.56"*/, int(iotPlatformInfo.CustomServicesTransportInfo[0].Endpoint.Addresses[0].Port) /*1883*/, iotPlatformInfo.IotPlatformId /*"7feaadbb0400"*/, iotPlatformInfo.CustomServicesTransportInfo[0].Name /*"laboai-acme-ic-cse"*/, nil, nil, nil) + pltf, err := sssmgr.NewSssMgr(tm.name, tm.namespace, iotPlatformInfo.UserTransportInfo[0].Protocol, iotPlatformInfo.UserTransportInfo[0].Endpoint.Addresses[0].Host, int(iotPlatformInfo.UserTransportInfo[0].Endpoint.Addresses[0].Port), iotPlatformInfo.IotPlatformId, iotPlatformInfo.UserTransportInfo[0].Name, nil, nil, nil) if err != nil { log.Error("RegisterIotPlatformInfo: ", err) iotPlatformInfo.oneM2M = nil } else { log.Info("RegisterIotPlatformInfo: IoT pltf created") iotPlatformInfo.oneM2M = pltf + // Create the an AE for this ETSI MEC platform + var ae = sssmgr.SensorDiscoveryInfo{ + SensorIdentifier: iotPlatformInfo.IotPlatformId, + SensorType: "AE", + SensorPosition: nil, + IotPlatformId: iotPlatformInfo.IotPlatformId, + } + ae, err = pltf.OneM2M_create(ae, "") + if err != nil { + log.Warn("RegisterIotPlatformInfo: Failed to create new AE sensor") + } else { + registeredIotPlatformsAEMap[iotPlatformInfo.IotPlatformId] = ae + } + registeredIotPlatformsAEMap[iotPlatformInfo.IotPlatformId] = ae if tm.refreshTicker == nil { log.Info("RegisterIotPlatformInfo: Start RefreshTicker") tm.startRefreshTicker() @@ -278,6 +344,7 @@ func (tm *IotMgr) RegisterIotPlatformInfo(iotPlatformInfo IotPlatformInfo) (err } } registeredIotPlatformsMap[iotPlatformInfo.IotPlatformId] = iotPlatformInfo + log.Info("RegisterIotPlatformInfo: iotPlatformId: ", registeredIotPlatformsMap[iotPlatformInfo.IotPlatformId]) } // else, Skip disabled platform @@ -314,6 +381,10 @@ func (tm *IotMgr) DeregisterIotPlatformInfo(iotPlatformId string) (err error) { } if pltf, ok := registeredIotPlatformsMap[iotPlatformId]; ok { if pltf.oneM2M != nil { + if val, ok := registeredIotPlatformsAEMap[iotPlatformId]; ok { + _ = pltf.oneM2M.OneM2M_delete(val) + delete(registeredIotPlatformsAEMap, iotPlatformId) + } _ = pltf.oneM2M.DeleteSssMgr() pltf.oneM2M = nil log.Info("RegisterIotPlatformInfo: IoT pltf removed") @@ -395,14 +466,8 @@ func (tm *IotMgr) CreateDevice(device DeviceInfo) (deviceResp DeviceInfo, err er log.Info(">>> CreateDevice: ", device) tm.wg.Wait() - log.Info("GetDevices: After Wait()") + log.Info("CreateDevice: After Wait()") - // 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 { @@ -471,6 +536,10 @@ func (tm *IotMgr) createDeviceWithIotPlatformId(device DeviceInfo, requestedIotP err = errors.New("Device already exist") return deviceResp, err } + if _, ok := registeredIotPlatformsAEMap[requestedIotPlatformId]; !ok { + err = errors.New("Devaice cannot be created witout oneM2M AE parent") + return deviceResp, err + } if registeredIotPlatformsMap[requestedIotPlatformId].oneM2M != nil && device.Enabled == true { log.Info("createDeviceWithIotPlatformId: Create device on IoT platform", device) @@ -500,7 +569,7 @@ func (tm *IotMgr) createDeviceWithIotPlatformId(device DeviceInfo, requestedIotP // //DownlinkInfo *DownlinkInfo // ClientCertificate string // } - sensor, err := registeredIotPlatformsMap[requestedIotPlatformId].oneM2M.OneM2M_create(sensor, "") + sensor, err := registeredIotPlatformsMap[requestedIotPlatformId].oneM2M.OneM2M_create(sensor, registeredIotPlatformsAEMap[requestedIotPlatformId].SensorIdentifier) if err != nil { return deviceResp, err } @@ -522,12 +591,26 @@ func (tm *IotMgr) createDeviceWithRequestedUserTransportId(device DeviceInfo, re if val, ok := platformPerUserTransportIdMap[requestedUserTransportId]; ok { deviceResp, err = tm.createDeviceWithIotPlatformId(device, val[0]) } else { - err = errors.New("Invalid UserTransportId") + err = errors.New("Invalid requestedUserTransportId") } if err != nil { log.Error("createDeviceWithIotPlatformId: ", err.Error()) return deviceResp, err } + // FIXME FSCOM How to manage these fields from DeviceInfo + // DeviceAuthenticationInfo string + // Gpsi string + // Pei string + // Supi string + // Msisdn string + // Imei string + // Imsi string + // Iccid string + // RequestedMecTrafficRule []TrafficRuleDescriptor + // //DeviceSpecificMessageFormats *DeviceSpecificMessageFormats + // //DownlinkInfo *DownlinkInfo + // ClientCertificate string + // } log.Info("createDeviceWithIotPlatformId: deviceResp: ", deviceResp) return deviceResp, nil diff --git a/go-packages/meep-iot-mgr/iot-mgr_test.go b/go-packages/meep-iot-mgr/iot-mgr_test.go index 84ca215aa4d2d3c1ae0bff247d0b87452313fda6..b9be619adec006a2b85763026e61112c49e5b4a0 100644 --- a/go-packages/meep-iot-mgr/iot-mgr_test.go +++ b/go-packages/meep-iot-mgr/iot-mgr_test.go @@ -74,10 +74,10 @@ func TestRegisterIotPlatformInfo(t *testing.T) { var userTransportInfo = []MbTransportInfo{} userTransportInfo = append(userTransportInfo, MbTransportInfo{ Id: "d5673793-c55c-4969-b5bc-2121f84b9f8d", - Name: "MQTT", + Name: "laboai-acme-ic-cse", Description: "MQTT", Protocol: "MQTT", - Version: "2", + Version: "3", Endpoint: &endpoint, }) var adresses_1 = []Addresses{} @@ -91,7 +91,7 @@ func TestRegisterIotPlatformInfo(t *testing.T) { } customServicesTransportInfo = append(customServicesTransportInfo, TransportInfo{ Id: "2ddb713c-2b41-4ded-a7ad-a5a047c5df13", - Name: "/laboai-acme-ic-cse", + Name: "laboai-acme-ic-cse", Description: "ACME oneM2M CSE", Protocol: "REST_HTTP", Version: "4", @@ -430,7 +430,7 @@ func registerIotPltf(tm *IotMgr) (iotPlatformInfo IotPlatformInfo, err error) { var userTransportInfo = []MbTransportInfo{} userTransportInfo = append(userTransportInfo, MbTransportInfo{ Id: "d5673793-c55c-4969-b5bc-2121f84b9f8d", - Name: "MQTT", + Name: "laboai-acme-ic-cse", Description: "MQTT", Protocol: "MQTT", Version: "2", @@ -447,7 +447,7 @@ func registerIotPltf(tm *IotMgr) (iotPlatformInfo IotPlatformInfo, err error) { } customServicesTransportInfo = append(customServicesTransportInfo, TransportInfo{ Id: "2ddb713c-2b41-4ded-a7ad-a5a047c5df13", - Name: "/laboai-acme-ic-cse", + Name: "laboai-acme-ic-cse", Description: "ACME oneM2M CSE", Protocol: "REST_HTTP", Version: "4", @@ -481,7 +481,7 @@ func registerIotPltfAndCreateDevice(tm *IotMgr) (iotPlatformInfo IotPlatformInfo var userTransportInfo = []MbTransportInfo{} userTransportInfo = append(userTransportInfo, MbTransportInfo{ Id: "d5673793-c55c-4969-b5bc-2121f84b9f8d", - Name: "MQTT", + Name: "laboai-acme-ic-cse", Description: "MQTT", Protocol: "MQTT", Version: "2", @@ -498,7 +498,7 @@ func registerIotPltfAndCreateDevice(tm *IotMgr) (iotPlatformInfo IotPlatformInfo } customServicesTransportInfo = append(customServicesTransportInfo, TransportInfo{ Id: "2ddb713c-2b41-4ded-a7ad-a5a047c5df13", - Name: "/laboai-acme-ic-cse", + Name: "laboai-acme-ic-cse", Description: "ACME oneM2M CSE", Protocol: "REST_HTTP", Version: "4", diff --git a/go-packages/meep-loc-serv-client/api/swagger.yaml b/go-packages/meep-loc-serv-client/api/swagger.yaml index b79647645e28881c54a6375745b00f8d476973b8..888bcddb5fb00f6fd3bc147614a68b6bf8872b82 100644 --- a/go-packages/meep-loc-serv-client/api/swagger.yaml +++ b/go-packages/meep-loc-serv-client/api/swagger.yaml @@ -637,7 +637,7 @@ paths: example: - userAreaNotification: notificationType: UserAreaNotification - timestamp: + timeStamp: seconds: 1673507343 nanoSeconds": 0 address: acr:10.0.0.1 @@ -971,7 +971,7 @@ paths: example: - userDistanceNotification: notificationType: UserDistanceNotification - timestamp: + timeStamp: seconds: 1673507343 nanoSeconds": 0 monitoredUsers: @@ -979,7 +979,7 @@ paths: address: acr:10.0.0.1 accessPointId: "001010000000000000000000000000001" zoneId: zone01 - timestamp: + timeStamp: seconds: 1673507343 nanoSeconds": 0 resourceURL: http://meAppServer.example.com/location/v3/queries/users @@ -1388,7 +1388,7 @@ paths: example: - userLocationEventNotification: notificationType: UserLocationEventNotification - timestamp: + timeStamp: seconds: 1673507343 nanoseconds: 0 address: acr:10.0.0.1 @@ -1752,7 +1752,7 @@ paths: example: - zoneLocationEventNotification: notificationType: ZoneLocationEventNotification - timestamp: + timeStamp: seconds: 1673507343 nanoseconds: 0 address: acr:10.0.0.1 @@ -3115,15 +3115,15 @@ components: specified in meters x-etsi-mec-cardinality: 1 x-etsi-mec-origin-type: integer - timestamp: + timeStamp: $ref: '#/components/schemas/TimeStamp' description: "A type containing information about the distance from a terminal\ - \ to a location or between two terminals, in addition the accuracy and a timestamp\ + \ to a location or between two terminals, in addition the accuracy and a timeStamp\ \ of the information are provided." example: distance: 6 accuracy: 0 - timestamp: + timeStamp: seconds: 5 nanoSeconds: 1 TerminalLocation: @@ -3384,7 +3384,7 @@ components: - accessPointId - address - resourceURL - - timestamp + - timeStamp - zoneId type: object properties: @@ -3412,7 +3412,7 @@ components: description: "Self-referring URL, see note 1." x-etsi-mec-cardinality: "1" x-etsi-mec-origin-type: AnyURI - timestamp: + timeStamp: $ref: '#/components/schemas/TimeStamp' locationInfo: $ref: '#/components/schemas/LocationInfo' @@ -3505,7 +3505,7 @@ components: X: 5.025005 "Y": 9.965781 Z: 9.36931 - timestamp: + timeStamp: seconds: 5 nanoSeconds: 1 x-etsi-notes: "NOTE 1:\tAs specified in [5], clause 5.2.2.7.\nNOTE 2: \tAs specified\ @@ -3608,7 +3608,7 @@ components: X: 5.025005 "Y": 9.965781 Z: 9.36931 - timestamp: + timeStamp: seconds: 5 nanoSeconds: 1 - locationInfo: @@ -3687,7 +3687,7 @@ components: X: 5.025005 "Y": 9.965781 Z: 9.36931 - timestamp: + timeStamp: seconds: 5 nanoSeconds: 1 UserLocationEventNotification: @@ -4060,7 +4060,7 @@ components: terminalDistance: distance: 6 accuracy: 0 - timestamp: + timeStamp: seconds: 5 nanoSeconds: 1 InlineUserList: @@ -4148,7 +4148,7 @@ components: X: 5.025005 "Y": 9.965781 Z: 9.36931 - timestamp: + timeStamp: seconds: 5 nanoSeconds: 1 - locationInfo: @@ -4227,7 +4227,7 @@ components: X: 5.025005 "Y": 9.965781 Z: 9.36931 - timestamp: + timeStamp: seconds: 5 nanoSeconds: 1 InlineZoneInfo: diff --git a/go-packages/meep-loc-serv-client/model_terminal_distance.go b/go-packages/meep-loc-serv-client/model_terminal_distance.go index 0401358bfaf8653b8aeea317f4fa3b9ce2073ad8..b508c5b919bb262e3b3fdf0351a2b05f96c9e00d 100644 --- a/go-packages/meep-loc-serv-client/model_terminal_distance.go +++ b/go-packages/meep-loc-serv-client/model_terminal_distance.go @@ -9,11 +9,11 @@ */ package client -// A type containing information about the distance from a terminal to a location or between two terminals, in addition the accuracy and a timestamp of the information are provided. +// A type containing information about the distance from a terminal to a location or between two terminals, in addition the accuracy and a timeStamp of the information are provided. type TerminalDistance struct { // Accuracy of the provided distance in meters Accuracy int32 `json:"accuracy,omitempty"` // Distance from terminal to a location or between two terminals specified in meters Distance int32 `json:"distance"` - Timestamp *TimeStamp `json:"timestamp,omitempty"` + Timestamp *TimeStamp `json:"timeStamp,omitempty"` } diff --git a/go-packages/meep-loc-serv-client/model_user_info.go b/go-packages/meep-loc-serv-client/model_user_info.go index f3ebd4d2a2ddb5ffb462f7c607ae39b32c542c97..8ef97f1de745ff327c45d2abcc1ff04fd88193f7 100644 --- a/go-packages/meep-loc-serv-client/model_user_info.go +++ b/go-packages/meep-loc-serv-client/model_user_info.go @@ -19,7 +19,7 @@ type UserInfo struct { ZoneId string `json:"zoneId"` // Self-referring URL, see note 1. ResourceURL string `json:"resourceURL"` - Timestamp *TimeStamp `json:"timestamp"` + Timestamp *TimeStamp `json:"timeStamp"` LocationInfo *LocationInfo `json:"locationInfo,omitempty"` CivicInfo *CivicAddress `json:"civicInfo,omitempty"` // Reserved for future use. diff --git a/go-packages/meep-sss-client/README.md b/go-packages/meep-sss-client/README.md index 6bfa0a4cc2635c4fd08fda90227b5727b6360157..6fe84ed604d91c194d802e39cf24b3952c23bb10 100644 --- a/go-packages/meep-sss-client/README.md +++ b/go-packages/meep-sss-client/README.md @@ -60,7 +60,7 @@ Class | Method | HTTP request | Description - [SensorStatusSubscription](docs/SensorStatusSubscription.md) - [SensorStatusSubscriptionIdBody](docs/SensorStatusSubscriptionIdBody.md) - [ShapeType](docs/ShapeType.md) - - [StatusDataSubscriptionIdBody](docs/StatusDataSubscriptionIdBody.md) + - [SensorDataSubscriptionIdBody](docs/SensorDataSubscriptionIdBody.md) - [SubscriptionLinkList](docs/SubscriptionLinkList.md) - [SubscriptionLinkListLinks](docs/SubscriptionLinkListLinks.md) - [SubscriptionLinkListSubscription](docs/SubscriptionLinkListSubscription.md) diff --git a/go-packages/meep-sss-client/api_sensor_data_subscription.go b/go-packages/meep-sss-client/api_sensor_data_subscription.go index c541e06a78dbcf889004fb49099286754d8d13c1..f551617a6a93f92c7d1df122116a91ac73b4e7bd 100644 --- a/go-packages/meep-sss-client/api_sensor_data_subscription.go +++ b/go-packages/meep-sss-client/api_sensor_data_subscription.go @@ -498,7 +498,7 @@ This method shall support the URI query parameters, request and response data st * @param subscriptionId Unique identifiers of a subscription @return []SensorDataSubscription */ -func (a *SensorDataSubscriptionApiService) SensorDataSubscriptionPUT(ctx context.Context, body StatusDataSubscriptionIdBody, subscriptionId string) ([]SensorDataSubscription, *http.Response, error) { +func (a *SensorDataSubscriptionApiService) SensorDataSubscriptionPUT(ctx context.Context, body SensorDataSubscriptionIdBody, subscriptionId string) ([]SensorDataSubscription, *http.Response, error) { var ( localVarHttpMethod = strings.ToUpper("Put") localVarPostBody interface{} diff --git a/go-packages/meep-sss-client/model_status_data_subscription_id_body.go b/go-packages/meep-sss-client/model_status_data_subscription_id_body.go index 0b09d725c28b1300586e7ff603315696f0f8cfa3..02e2be891964fcb9a9d3be3d28b0a0ef9bf1b759 100644 --- a/go-packages/meep-sss-client/model_status_data_subscription_id_body.go +++ b/go-packages/meep-sss-client/model_status_data_subscription_id_body.go @@ -9,6 +9,6 @@ */ package client -type StatusDataSubscriptionIdBody struct { +type SensorDataSubscriptionIdBody struct { SensorStatusSubscription *SensorDataSubscription `json:"SensorStatusSubscription,omitempty"` } diff --git a/go-packages/meep-sss-mgr/http.go b/go-packages/meep-sss-mgr/http.go index ce9fcc5f11d5003d99c856b3629a72257b29dd46..e35bcbcc644673b5dfa90f41ed66911e832dbd1a 100644 --- a/go-packages/meep-sss-mgr/http.go +++ b/go-packages/meep-sss-mgr/http.go @@ -71,7 +71,6 @@ func (http_mgr *SssMgrHttp) handleRoot(w http.ResponseWriter, r *http.Request) { return } log.Info("handleRoot: body: ", body) - //map[m2m:sgn:map[nev:map[net:3 rep:map[m2m:cnt:map[cbs:0 cni:0 ct:20250327T111057,824274 et:20300326T111032,133324 lt:20250327T111057,824274 mbs:10000 mni:10 pi:C7feaadbb0400 ri:cnt8465776292050557472 rn:test st:0 ty:3]]] sur:/laboai-acme-ic-cse/sub3916638126520907910]] if _, ok := body["m2m:sgn"]; !ok { err := errors.New("Only m2m:sgn is expected") log.Error(err.Error()) diff --git a/go-packages/meep-sss-mgr/mqtt.go b/go-packages/meep-sss-mgr/mqtt.go index 9eec0dde79d4c506852262f62f4d2adb553f0f3a..e5e1dfb133d57cbbc84df2cb2a5f3a9d7ce55f9f 100644 --- a/go-packages/meep-sss-mgr/mqtt.go +++ b/go-packages/meep-sss-mgr/mqtt.go @@ -6,6 +6,7 @@ import ( "fmt" "reflect" "strconv" + "strings" "sync" log "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-logger" @@ -30,7 +31,7 @@ func onMessageReceived(client mqtt.Client, msg mqtt.Message) { // if _notify != nil { // ... // } else { - // log.Info("onMessageReceived: null pointer for the callbacl") + // log.Info("onMessageReceived: null pointer for the callback") // } }() } @@ -41,7 +42,7 @@ func onMessageReceivedReq(client mqtt.Client, msg mqtt.Message) { // if _notify != nil { // ... // } else { - // log.Info("onMessageReceivedReq: null pointer for the callbacl") + // log.Info("onMessageReceivedReq: null pointer for the callback") // } }() } @@ -54,12 +55,57 @@ func onMessageReceivedResp(client mqtt.Client, msg mqtt.Message) { }() } +func onMessageReceivedNot(client mqtt.Client, msg mqtt.Message) { + go func() { + log.Info("onMessageReceivedNot: Received message: ", string(msg.Payload()), " on topic ", msg.Topic()) + //{\"fr\": \"/laboai-acme-ic-cse\", \"to\": \"mqtt://172.29.10.56:1883\", \"ot\": \"20250327T134413,103223\", \"op\": 5, \"rqi\": \"6400213617020980613\", \"rvi\": \"4\", \"drt\": 1, \"pc\": {\"m2m:sgn\": {\"nev\": {\"net\": 3, \"rep\": {\"m2m:cin\": {\"cnf\": \"text/plain:0\", \"con\": \"toto\", \"rn\": \"1234\", \"ri\": \"cin3503344383965775257\", \"pi\": \"cnt1992620086833081496\", \"ct\": \"20250327T134413,093784\", \"lt\": \"20250327T134413,093784\", \"ty\": 4, \"cs\": 4, \"st\": 2, \"et\": \"20300326T134347,108295\"}}}, \"sur\": \"/laboai-acme-ic-cse/sub3169944238694067800\"}}} on topic /oneM2M/req/laboai-acme-ic-cse/mqtt:::172.29.10.56:1883/json" + // Prepare response + s := strings.Split(msg.Topic(), "/") // /oneM2M/req/laboai-acme-ic-cse/mqtt:::172.29.10.56:1883/json + resp_topic := "/oneM2M/resp/" + s[4] + "/" + s[3] + "/" + s[5] + log.Debug("onMessageReceivedNot: resp_topic: ", resp_topic) + if _notify != nil { + var d map[string]interface{} + err := json.Unmarshal(msg.Payload(), &d) + if err != nil { + log.Warn("onMessageReceivedNot: ", err.Error()) + return + } + if pc, ok := d["pc"]; ok { + log.Debug("onMessageReceivedNot: pc: ", pc) + log.Debug("onMessageReceivedNot: TypeOf(pc): ", reflect.TypeOf(pc)) + if _, ok := d["pc"].(map[string]interface{}); !ok { + log.Warn("onMessageReceivedNot: pc entry has an unexpected type") + return + } + body := d["pc"].(map[string]interface{}) + if _, ok := body["m2m:sgn"]; !ok { + log.Error("Only m2m:sgn is expected") + return + } + if _, ok := body["m2m:sgn"].(map[string]interface{}); !ok { + log.Warn("onMessageReceivedNot: m2m:sgn entry has an unexpected type") + return + } + m := body["m2m:sgn"].(map[string]interface{}) + log.Info("handleRoot: m: ", m) + _notify(m) + } else { + log.Warn("onMessageReceivedNot: pc entry not found") + return + } + } else { + log.Debug("onMessageReceivedNot: null pointer for the callback") + } + // FIXME FSCOM Send the response + }() +} + var connectHandler mqtt.OnConnectHandler = func(client mqtt.Client) { - log.Info("mqtt.OnConnectHandler: Connected") + //log.Info("mqtt.OnConnectHandler: Connected") } var connectLostHandler mqtt.ConnectionLostHandler = func(client mqtt.Client, err error) { - log.Info("Connect lost:", err) + //log.Info("Connect lost:", err) } func NewSssMgrMqtt() (broker_mqtt *SssMgrMqtt) { @@ -76,10 +122,11 @@ func (broker_mqtt *SssMgrMqtt) init(tm *SssMgr, notify func(map[string]interface broker_mqtt.opts = mqtt.NewClientOptions() broker_mqtt.opts.SetDefaultPublishHandler(onMessageReceived) - broker_mqtt.opts.SetClientID("AdvantEDGE.meep-vis-traffic-mgr") + broker_mqtt.opts.SetClientID("AdvantEDGE.meep-sss-mgr") broker_mqtt.opts.OnConnect = connectHandler broker_mqtt.opts.OnConnectionLost = connectLostHandler broker_mqtt.opts.CleanSession = true + //broker_mqtt.opts.ResumeSubs = true broker_mqtt.opts.SetUsername("") broker_mqtt.opts.SetPassword("") log.Info("init: Add brocker: ", fmt.Sprintf("tcp://%s:%d", tm.host, tm.port)) @@ -95,16 +142,28 @@ func (broker_mqtt *SssMgrMqtt) init(tm *SssMgr, notify func(map[string]interface token.Wait() // Subscribe - log.Info("init: Subscribe to: ", "oneM2M/req/+") - token = broker_mqtt.client.Subscribe("oneM2M/req/+", 0, onMessageReceivedReq) // qos:0 + // tp := "oneM2M/req/+/" + tm.cse_name + "/#" + // log.Info("init: Subscribe to: ", tp) + // token = broker_mqtt.client.Subscribe(tp, 0, onMessageReceivedReq) // qos:0 + // if token.Error() != nil { + // log.Error(token.Error()) + // return token.Error() + // } + // token.Wait() + tp := "/oneM2M/resp/+/" + tm.cse_name + "/#" + log.Info("init: Subscribe to: ", tp) + token = broker_mqtt.client.Subscribe(tp, 0, onMessageReceivedResp) // qos:0 if token.Error() != nil { + broker_mqtt.client.Disconnect(250) log.Error(token.Error()) return token.Error() } - // token.Wait() - log.Info("init: Subscribe to: ", "/oneM2M/resp/#") - token = broker_mqtt.client.Subscribe("/oneM2M/resp/#", 0, onMessageReceivedResp) // qos:0 + token.Wait() + tp = "/oneM2M/req/" + tm.cse_name + "/+/#" + log.Info("init: Subscribe to: ", tp) + token = broker_mqtt.client.Subscribe(tp, 1, onMessageReceivedNot) // qos:1 if token.Error() != nil { + broker_mqtt.client.Disconnect(250) log.Error(token.Error()) return token.Error() } @@ -210,6 +269,7 @@ func (broker_mqtt *SssMgrMqtt) send(p_ctx SssMgrBindingProtocolContext) (err err log.Debug("send: Start waiting for the response") _sync_response.Add(1) _sync_response.Wait() + log.Debug("send: Got the response") if val, ok := _responses[token.(*mqtt.PublishToken).MessageID()]; ok { delete(_responses, token.(*mqtt.PublishToken).MessageID()) log.Debug("send: Get the response: ", string(val.Payload())) @@ -222,19 +282,6 @@ func (broker_mqtt *SssMgrMqtt) send(p_ctx SssMgrBindingProtocolContext) (err err if r, ok := d["pc"]; ok { log.Debug("send: r: ", r) log.Debug("send: TypeOf(r): ", reflect.TypeOf(r)) - // var b []byte - // b, err = json.Marshal(r) - // if err != nil { - // log.Error("send: ", err.Error()) - // return err, nil - // } - // log.Info("send: b: ", b) - // log.Info("send: TypeOf(b): ", reflect.TypeOf(b)) - // err = json.Unmarshal(b, &resp) - // if err != nil { - // log.Error("send: ", err.Error()) - // return err, nil - // } return nil, r } return err, nil diff --git a/go-packages/meep-sss-mgr/onem2m-mgr.go b/go-packages/meep-sss-mgr/onem2m-mgr.go index ceebba77f64920186eb82b0434c1b7e6981f24b5..6ccbffe4311fe896883976b537991f748a9ae212 100644 --- a/go-packages/meep-sss-mgr/onem2m-mgr.go +++ b/go-packages/meep-sss-mgr/onem2m-mgr.go @@ -72,6 +72,7 @@ type SensorDiscoveryInfo struct { SensorCharacteristicList []SensorCharacteristic SensorPosition *Point IotPlatformId string + Flex map[string]interface{} } var registeredIotPlatformsMap = map[string]IotPlatformInfo{} // List of discovered IOT Plateform @@ -79,7 +80,7 @@ var sensorsMap = map[string]SensorDiscoveryInfo{} // Map sensors by s var sensorsPerPlatformMap = map[string][]string{} // Map dsensorIdentifiers per platform // Timer to refresh devices list for all IoT platform -const refreshTickerExpeary = 10 // In seconds +const refreshTickerExpeary = 20 // In seconds // Enable profiling const profiling = false @@ -174,6 +175,8 @@ func (tm *SssMgr) init() { sensorsPerPlatformMap = make(map[string][]string, 0) tm.refreshTicker = nil subscriptionListPerSubId = make(map[string]SensorDiscoveryInfo, 0) + + tm.startRefreshTicker() } // DeleteSssMgr - @@ -193,20 +196,23 @@ func (tm *SssMgr) DeleteSssMgr() (err error) { } func (tm *SssMgr) startRefreshTicker() { - log.Debug("Starting refresh loop") + log.Debug(">>> sssmgr.startRefreshTicker") 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() + for range tm.refreshTicker.C { + // Refresh the list of devices + tm.wg.Add(1) + log.Debug("sssmgr.startRefreshTicker: After Add()") + err := tm.populateDevicesPerIotPlatforms() + if err != nil { + log.Error(err) + // continue } - } + log.Debug("sssmgr.startRefreshTicker: Before Done()") + tm.wg.Done() + log.Debug("sssmgr.startRefreshTicker: After Done()") + } // End of 'for' statement + log.Debug("sssmgr.startRefreshTicker: Leaving time loop") }() } @@ -231,11 +237,7 @@ func (tm *SssMgr) SensorDiscoveryInfoAll() (sensors []SensorDiscoveryInfo, err e log.Info(">>> SensorDiscoveryInfoAll") - err = tm.populateDevicesPerIotPlatforms() // FIXME FSCOM User timer. See startRefreshTicker - if err != nil { - return sensors, err - } - + log.Info("SensorDiscoveryInfoAll: Before Synchro") tm.wg.Wait() log.Info("SensorDiscoveryInfoAll: After Synchro") @@ -304,7 +306,12 @@ func (tm *SssMgr) populateDevicesPerIotPlatforms() error { // Refresh the list of devices for all registered Iot platform for _, iotPlatform := range registeredIotPlatformsMap { log.Debug("populateDevicesPerIotPlatforms: processing: ", iotPlatform.Address) - err := tm.populateSensors(iotPlatform) + err := tm.populateSensors(iotPlatform, "3") + if err != nil { + log.Error("populateDevicesPerIotPlatforms: ", err) + continue + } + err = tm.populateSensors(iotPlatform, "28") if err != nil { log.Error("populateDevicesPerIotPlatforms: ", err) continue @@ -324,7 +331,7 @@ func (tm *SssMgr) populateDevicesPerIotPlatforms() error { * @param {string} iotPlatformId contains the IoT platform identifier * @return {struct} nil on success, error otherwise */ -func (tm *SssMgr) populateSensors(iotPlatformInfo IotPlatformInfo) error { +func (tm *SssMgr) populateSensors(iotPlatformInfo IotPlatformInfo, type_ string) error { if profiling { profilingTimers["populateSensors"] = time.Now() } @@ -348,12 +355,12 @@ func (tm *SssMgr) populateSensors(iotPlatformInfo IotPlatformInfo) error { // Build the queries queries := map[string]string{} queries["fu"] = "1" // Filter usage - queries["ty"] = "3" // FIXME FSCOM Filter on oneM2M CNT for sensors or on AE because AE if the platform and CNT is a sensor and CIN the values + queries["ty"] = type_ ctx.queries = queries err, resp := protocol.send(ctx) if err != nil { - log.Error("OneM2M_create: ", err.Error()) + log.Error("populateSensors: ", err.Error()) return err } log.Debug("populateSensors: resp: ", resp) @@ -362,7 +369,7 @@ func (tm *SssMgr) populateSensors(iotPlatformInfo IotPlatformInfo) error { 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 + // Loop for each element and build the sensor list 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)) @@ -375,12 +382,12 @@ func (tm *SssMgr) populateSensors(iotPlatformInfo IotPlatformInfo) error { ctx.queries["fu"] = "2" err, resp := protocol.send(ctx) if err != nil { - log.Error("OneM2M_create: ", err.Error()) + log.Error("populateSensors: ", 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 { + if resp.(map[string]interface{}) == nil { continue } // oneM2M_cin := resp.(map[string]interface{})["m2m:cnt"].(map[string]interface{}) @@ -390,15 +397,41 @@ func (tm *SssMgr) populateSensors(iotPlatformInfo IotPlatformInfo) error { var sensor = SensorDiscoveryInfo{ IotPlatformId: iotPlatformInfo.IotPlatformId, } + if type_ == "28" { + sensor.SensorType = "FLX" + // TODO To be removed sensor.Flex = map[string]interface{}{} // FIXME FSCOM How to create flex container map from list of attributes recieved? Q&D: extract all attributes != attributes[AE/CNT/CIN]??? + // ==> Use cnd which contains the link to download the onthology or the reference to the oneM2M standard + } sensor, err = tm.oneM2M_deserialize(sensor, resp.(map[string]interface{})) if err != nil { log.Warn("populateSensors: ", err.Error()) continue } - log.Info("populateSensors: sensor: ", sensor) + //log.Info("populateSensors: Before sensorsMap: ", sensorsMap) + if _, ok := sensorsMap[sensor.SensorIdentifier]; ok { + delete(sensorsMap, sensor.SensorIdentifier) + } sensorsMap[sensor.SensorIdentifier] = sensor - sensorsPerPlatformMap[sensor.IotPlatformId] = append(sensorsPerPlatformMap[sensor.IotPlatformId], sensor.SensorIdentifier) + //log.Info("populateSensors: After sensorsMap: ", sensorsMap) + //log.Info("populateSensors: Before sensorsPerPlatformMap: ", sensorsPerPlatformMap) + if len(sensorsPerPlatformMap[sensor.IotPlatformId]) == 0 { + sensorsPerPlatformMap[sensor.IotPlatformId] = append(sensorsPerPlatformMap[sensor.IotPlatformId], sensor.SensorIdentifier) + } else { // Replace or add a new element + found := -1 + for i, v := range sensorsPerPlatformMap[sensor.IotPlatformId] { + if v == sensor.SensorIdentifier { + found = i + break + } + } // End of 'for' statement + if found != -1 { // Replace + sensorsPerPlatformMap[sensor.IotPlatformId] = append(sensorsPerPlatformMap[sensor.IotPlatformId][:found], sensorsPerPlatformMap[sensor.IotPlatformId][found+1:]...) + } else { // Add + sensorsPerPlatformMap[sensor.IotPlatformId] = append(sensorsPerPlatformMap[sensor.IotPlatformId], sensor.SensorIdentifier) + } + //log.Info("populateSensors: After sensorsPerPlatformMap: ", sensorsPerPlatformMap) + } } // End of 'for' statement log.Info("populateSensors: sensorsMap: ", sensorsMap) @@ -454,7 +487,11 @@ func (tm *SssMgr) OneM2M_create(sensor SensorDiscoveryInfo, path string) (sensor if len(sensor.SensorCharacteristicList) != 0 { for _, val := range sensor.SensorCharacteristicList { log.Debug("OneM2M_create: Adding CNT metadata: ", val) - bodyMap["m2m:cnt"][val.CharacteristicName] = val.CharacteristicValue + if val.CharacteristicName == "lbl" || val.CharacteristicName == "label" { + bodyMap["m2m:cnt"][val.CharacteristicName] = []string{val.CharacteristicValue} + } else { + bodyMap["m2m:cnt"][val.CharacteristicName] = val.CharacteristicValue + } } // End of 'for' statement } } else if sensor.SensorType == "CIN" { @@ -468,6 +505,38 @@ func (tm *SssMgr) OneM2M_create(sensor SensorDiscoveryInfo, path string) (sensor bodyMap["m2m:cin"][val.CharacteristicName] = val.CharacteristicValue } // End of 'for' statement } + } else if sensor.SensorType == "FLX" { + // Sanity checks + if sensor.Flex == nil { + err = errors.New("flex parameter shall be present") + log.Error("OneM2M_create: ", err.Error()) + return sensorResp, err + } else if _, ok := sensor.Flex["type"]; !ok { + err = errors.New("'type' entry is required") + log.Error("OneM2M_create: ", err.Error()) + return sensorResp, err + } else if _, ok := sensor.Flex["type"].(string); !ok { + err = errors.New("'type' entry is required") + log.Error("OneM2M_create: ", err.Error()) + return sensorResp, err + } else if _, ok := sensor.Flex["cnd"]; !ok { + err = errors.New("'cnd' entry is required") + log.Error("OneM2M_create: ", err.Error()) + return sensorResp, err + } else if _, ok := sensor.Flex["cnd"].(string); !ok { + err = errors.New("'cnd' entry is required") + log.Error("OneM2M_create: ", err.Error()) + return sensorResp, err + } + k := sensor.Flex["type"].(string) + bodyMap[k] = make(map[string]interface{}, 0) + bodyMap[k]["rn"] = sensor.SensorIdentifier + for i, v := range sensor.Flex { + if i == "type" { + continue // skip it + } + bodyMap[k][i] = v + } // End of 'for' statement } else { err = errors.New("OneM2M_create: Invalid type") log.Error("OneM2M_create: ", err.Error()) @@ -496,6 +565,8 @@ func (tm *SssMgr) OneM2M_create(sensor SensorDiscoveryInfo, path string) (sensor ctx.ty = 3 } else if sensor.SensorType == "CIN" { ctx.ty = 4 + } else if sensor.SensorType == "FLX" { + ctx.ty = 28 } else { err = errors.New("OneM2M_create: Invalid type") log.Error("send: ", err.Error()) @@ -510,7 +581,13 @@ func (tm *SssMgr) OneM2M_create(sensor SensorDiscoveryInfo, path string) (sensor log.Debug("OneM2M_create: resp: ", resp) log.Debug("OneM2M_create: TypeOf(resp): ", reflect.TypeOf(resp)) if _, ok := resp.(map[string]interface{}); !ok { - log.Error("OneM2M_create: Interface not available") + err = errors.New("Interface not available") + log.Error("OneM2M_create: ", err.Error()) + return sensorResp, err + } + if val, ok := resp.(map[string]interface{})["m2m:dbg"]; ok { + err = errors.New(val.(string)) + log.Error("OneM2M_create: ", err.Error()) return sensorResp, err } @@ -519,6 +596,7 @@ func (tm *SssMgr) OneM2M_create(sensor SensorDiscoveryInfo, path string) (sensor sensorResp.SensorType = sensor.SensorType sensorResp.IotPlatformId = sensor.IotPlatformId sensorResp.SensorPosition = sensor.SensorPosition + sensorResp.Flex = sensor.Flex sensorResp, err = tm.oneM2M_deserialize(sensorResp, resp.(map[string]interface{})) if err != nil { log.Error("OneM2M_create: ", err.Error()) @@ -587,7 +665,7 @@ func (tm *SssMgr) OneM2M_discovery(type_ string, iotPlatformId string) (sensorRe err, resp := protocol.send(ctx) if err != nil { - log.Error("OneM2M_create: ", err.Error()) + log.Error("OneM2M_discovery: ", err.Error()) return nil, err } log.Debug("OneM2M_discovery: resp: ", resp) @@ -722,9 +800,13 @@ func (tm *SssMgr) OneM2M_subscribe(iotPlatformId string, path string) (subscript var bodyMap = map[string]map[string]interface{}{} bodyMap["m2m:sub"] = make(map[string]interface{}, 0) net := make(map[string][]int) - net["net"] = []int{2, 3, 4} + net["net"] = []int{1, 2, 3, 4} bodyMap["m2m:sub"]["enc"] = net - bodyMap["m2m:sub"]["nu"] = []string{"http://172.29.10.52:31122/"} // FIXME FSCOM The URI of the listener + if tm.bindingProtocol == "MQTT" { + bodyMap["m2m:sub"]["nu"] = []string{"mqtt://172.29.10.56:1883"} // FIXME FSCOM The URI of the listener + } else { + bodyMap["m2m:sub"]["nu"] = []string{"http://172.29.10.52:31122/"} // FIXME FSCOM The URI of the listener + } bodyMap["m2m:sub"]["rn"] = uuid.New().String() ctx.body = bodyMap @@ -736,7 +818,7 @@ func (tm *SssMgr) OneM2M_subscribe(iotPlatformId string, path string) (subscript log.Debug("OneM2M_subscribe: resp: ", resp) log.Debug("OneM2M_subscribe: TypeOf(resp): ", reflect.TypeOf(resp)) if _, ok := resp.(map[string]interface{}); !ok { - log.Error("OneM2M_create: Interface not available") + log.Error("OneM2M_subscribe: Interface not available") return "", err } @@ -759,7 +841,7 @@ func (tm *SssMgr) OneM2M_subscribe(iotPlatformId string, path string) (subscript log.Error("OneM2M_subscribe: ", err.Error()) return "", err } - log.Debug("OneM2M_cOneM2M_subscribereate: sensvorResp: ", v) + log.Debug("OneM2M_subscribe: v: ", v) subscriptionListPerSubId[subId] = v @@ -773,28 +855,28 @@ func (tm *SssMgr) OneM2M_subscribe(iotPlatformId string, path string) (subscript return subId, nil } -func (tm *SssMgr) OneM2M_Delete(sensor SensorDiscoveryInfo) (err error) { +func (tm *SssMgr) OneM2M_delete(sensor SensorDiscoveryInfo) (err error) { if profiling { - profilingTimers["OneM2M_Delete"] = time.Now() + profilingTimers["OneM2M_delete"] = time.Now() } - log.Info(">>> OneM2M_Delete: sensor=", sensor) + log.Info(">>> OneM2M_delete: sensor=", sensor) if sensor.SensorIdentifier == "" { - err = errors.New("OneM2M_Delete: Cannot find \"ri\" value") - log.Error("OneM2M_Delete: ", err.Error()) + err = errors.New("OneM2M_delete: Cannot find \"ri\" value") + log.Error("OneM2M_delete: ", err.Error()) return err } if sensor.IotPlatformId == "" { err = errors.New("IotPlatformId fiels shall be set") - log.Error("OneM2M_Delete: ", err.Error()) + log.Error("OneM2M_delete: ", err.Error()) return err } tm.wg.Wait() - log.Info("OneM2M_Delete: After Synchro") + log.Info("OneM2M_delete: After Synchro") // Send it and get the result var ctx = SssMgrBindingProtocolContext{ @@ -811,40 +893,40 @@ func (tm *SssMgr) OneM2M_Delete(sensor SensorDiscoveryInfo) (err error) { } err, _ = protocol.send(ctx) if err != nil { - log.Error("OneM2M_Delete: ", err.Error()) + log.Error("OneM2M_delete: ", err.Error()) return err } if profiling { now := time.Now() - log.Debug("OneM2M_Delete: ", now.Sub(profilingTimers["OneM2M_Delete"])) + log.Debug("OneM2M_delete: ", now.Sub(profilingTimers["OneM2M_delete"])) } return nil } -func (tm *SssMgr) OneM2M_DeleteSub(subId string) (err error) { +func (tm *SssMgr) OneM2M_delete_subscription(subId string) (err error) { if profiling { - profilingTimers["OneM2M_DeleteSub"] = time.Now() + profilingTimers["OneM2M_delete_subscription"] = time.Now() } - log.Info(">>> OneM2M_DeleteSub: sensor=", subId) + log.Info(">>> OneM2M_delete_subscription: sensor=", subId) if subId == "" { err = errors.New("subId fiels shall be set") - log.Error("OneM2M_DeleteSub: ", err.Error()) + log.Error("OneM2M_delete_subscription: ", err.Error()) return err } if _, ok := subscriptionListPerSubId[subId]; !ok { err = errors.New("Unkmown subscription identifier") - log.Error("OneM2M_DeleteSub: ", err.Error()) + log.Error("OneM2M_delete_subscription: ", err.Error()) return err } tm.wg.Wait() - log.Info("OneM2M_DeleteSub: After Synchro") + log.Info("OneM2M_delete_subscription: After Synchro") // Send it and get the result var ctx = SssMgrBindingProtocolContext{ @@ -861,16 +943,16 @@ func (tm *SssMgr) OneM2M_DeleteSub(subId string) (err error) { } err, _ = protocol.send(ctx) if err != nil { - log.Error("OneM2M_DeleteSub: ", err.Error()) + log.Error("OneM2M_delete_subscription: ", err.Error()) return err } delete(subscriptionListPerSubId, subId) - log.Info("OneM2M_DeleteSub: New subscriptionListPerSubId: ", subscriptionListPerSubId) + log.Info("OneM2M_delete_subscription: New subscriptionListPerSubId: ", subscriptionListPerSubId) if profiling { now := time.Now() - log.Debug("OneM2M_DeleteSub: ", now.Sub(profilingTimers["OneM2M_DeleteSub"])) + log.Debug("OneM2M_delete_subscription: ", now.Sub(profilingTimers["OneM2M_delete_subscription"])) } return nil @@ -889,7 +971,7 @@ func (tm *SssMgr) notify(sub map[string]interface{}) { return } if _, ok := sub["nev"].(map[string]interface{}); !ok { - log.Warn("notify: nev entry has unexpected type") + log.Warn("notify: nev entry has an unexpected type") return } if _, ok := sub["sur"]; !ok { @@ -947,116 +1029,199 @@ func (tm *SssMgr) notify(sub map[string]interface{}) { } func (tm *SssMgr) oneM2M_deserialize(sensor SensorDiscoveryInfo, response map[string]interface{}) (sensorResp SensorDiscoveryInfo, err error) { + log.Debug(">>> oneM2M_deserialize: sensor: ", sensor) log.Debug(">>> oneM2M_deserialize: response: ", response) sensorResp = sensor // Same data structure - for i, m := range response { - log.Debug("==> ", i, " value is ", m) - if _, ok := m.(map[string]interface{}); !ok { - // Skip it - log.Warn("oneM2M_deserialize: m is not map[string]interface{}") - continue + if sensor.SensorType == "FLX" { // Extract flex specific attributes first + // // Sanity checks + if len(response) != 1 { + err = errors.New("Wrong 'flex' parameter value") + log.Error("oneM2M_deserialize: ", err.Error()) + return sensorResp, err } - // m is a map[string]interface. - // loop over keys and values in the map. - for k, v := range m.(map[string]interface{}) { - log.Debug(k, " value is ", v) - log.Debug("oneM2M_deserialize: type(v): ", reflect.TypeOf(v)) - - if k == "ri" { - if item, ok := v.(string); ok { - sensorResp.SensorIdentifier = item - } else { - log.Error("oneM2M_deserialize: Failed to process ", k) - } - } else if k == "ty" { - if item, ok := v.(float64); ok { - switch item { - case 2: - sensorResp.SensorType = "AE" - case 3: - sensorResp.SensorType = "CNT" - case 4: - sensorResp.SensorType = "CIN" - default: - sensorResp.SensorType = strconv.FormatFloat(item, 'f', -1, 64) + keys := make([]string, 0, len(response)) + for k := range response { + keys = append(keys, k) + } + s := keys[0] // Resource type. E.g. cod:color + log.Debug("oneM2M_deserialize: Extract flex specific attributes first") + sensorResp.Flex = make(map[string]interface{}) + log.Debug("oneM2M_deserialize: s=", s) + log.Debug("oneM2M_deserialize: response[s]: ", response[s]) + for k, v := range response[s].(map[string]interface{}) { + log.Debug("oneM2M_deserialize: Processing k= ", k, ", v= ", v) + if e, ok := response[s].(map[string]interface{})[k]; ok { + log.Debug("oneM2M_deserialize: ", k, " is a flex attribute: ", e) + sensorResp.Flex[k] = interface_to_string(e) + } + } // End of 'for' statement + log.Debug("oneM2M_deserialize: After s=", s) + sensorResp.Flex["type"] = s + sensorResp.SensorIdentifier = sensorResp.Flex["ri"].(string) + log.Debug("oneM2M_deserialize: response: ", response) + log.Debug("oneM2M_deserialize: sensorResp.Flex: ", sensorResp.Flex) + } else { + for i, m := range response { + log.Debug("==> ", i, " value is ", m) + if _, ok := m.(map[string]interface{}); !ok { + // Skip it + log.Warn("oneM2M_deserialize: m is not map[string]interface{}") + continue + } + // m is a map[string]interface. + // loop over keys and values in the map. + for k, v := range m.(map[string]interface{}) { + log.Debug(k, " value is ", v) + log.Debug("oneM2M_deserialize: type(v): ", reflect.TypeOf(v)) + + if k == "ri" { + if item, ok := v.(string); ok { + sensorResp.SensorIdentifier = item + } else { + log.Error("oneM2M_deserialize: Failed to process ", k) + } + } else if k == "ty" { + if item, ok := v.(float64); ok { + switch item { + case 2: + sensorResp.SensorType = "AE" + case 3: + sensorResp.SensorType = "CNT" + case 4: + sensorResp.SensorType = "CIN" + case 28: + sensorResp.SensorType = "FLX" + default: + sensorResp.SensorType = strconv.FormatFloat(item, 'f', -1, 64) + } + } else { + log.Error("oneM2M_deserialize: Failed to process ", k) } } else { - log.Error("oneM2M_deserialize: Failed to process ", k) - } - } else { - if item, ok := v.(string); ok { - sensorResp.SensorCharacteristicList = append( - sensorResp.SensorCharacteristicList, - SensorCharacteristic{ - CharacteristicName: k, - CharacteristicValue: string(item), - }) - } else if item, ok := v.(float64); ok { sensorResp.SensorCharacteristicList = append( sensorResp.SensorCharacteristicList, SensorCharacteristic{ CharacteristicName: k, - CharacteristicValue: strconv.FormatFloat(item, 'f', -1, 64), + CharacteristicValue: interface_to_string(v), }) - } else if item, ok := v.(int64); ok { - sensorResp.SensorCharacteristicList = append( - sensorResp.SensorCharacteristicList, - SensorCharacteristic{ - CharacteristicName: k, - CharacteristicValue: strconv.FormatInt(item, 10), - }) - } else if item, ok := v.(bool); ok { - sensorResp.SensorCharacteristicList = append( - sensorResp.SensorCharacteristicList, - SensorCharacteristic{ - CharacteristicName: k, - CharacteristicValue: strconv.FormatBool(item), - }) - } else if item, ok := v.([]string); ok { - sensorResp.SensorCharacteristicList = append( - sensorResp.SensorCharacteristicList, - SensorCharacteristic{ - CharacteristicName: k, - CharacteristicValue: strings.Join(item, ","), - }) - } else if item, ok := v.([]int64); ok { - log.Warn("oneM2M_deserialize: Failed to convert list of int64 into string: ", item) - } else if item, ok := v.(map[string]interface{}); ok { - v, err := json.Marshal(item) - if err == nil { - sensorResp.SensorCharacteristicList = append( - sensorResp.SensorCharacteristicList, - SensorCharacteristic{ - CharacteristicName: k, - CharacteristicValue: string(v), - }) - } else { - log.Warn("oneM2M_deserialize: ", err.Error()) - } - } else if item, ok := v.([]interface{}); ok { - log.Debug("oneM2M_deserialize: Got []interface {} for ", k) - log.Debug("oneM2M_deserialize: ValueOf ", reflect.ValueOf(item)) - v, err := json.Marshal(item) - if err == nil { - sensorResp.SensorCharacteristicList = append( - sensorResp.SensorCharacteristicList, - SensorCharacteristic{ - CharacteristicName: k, - CharacteristicValue: string(v), - }) - } else { - log.Warn("oneM2M_deserialize: ", err.Error()) - } - } else { - log.Warn("oneM2M_deserialize: Failed to process: ", k) + // FIXME FSCOM To be removed + // if item, ok := v.(string); ok { + // sensorResp.SensorCharacteristicList = append( + // sensorResp.SensorCharacteristicList, + // SensorCharacteristic{ + // CharacteristicName: k, + // CharacteristicValue: string(item), + // }) + // } else if item, ok := v.(float64); ok { + // sensorResp.SensorCharacteristicList = append( + // sensorResp.SensorCharacteristicList, + // SensorCharacteristic{ + // CharacteristicName: k, + // CharacteristicValue: strconv.FormatFloat(item, 'f', -1, 64), + // }) + // } else if item, ok := v.(int64); ok { + // sensorResp.SensorCharacteristicList = append( + // sensorResp.SensorCharacteristicList, + // SensorCharacteristic{ + // CharacteristicName: k, + // CharacteristicValue: strconv.FormatInt(item, 10), + // }) + // } else if item, ok := v.(bool); ok { + // sensorResp.SensorCharacteristicList = append( + // sensorResp.SensorCharacteristicList, + // SensorCharacteristic{ + // CharacteristicName: k, + // CharacteristicValue: strconv.FormatBool(item), + // }) + // } else if item, ok := v.([]string); ok { + // sensorResp.SensorCharacteristicList = append( + // sensorResp.SensorCharacteristicList, + // SensorCharacteristic{ + // CharacteristicName: k, + // CharacteristicValue: strings.Join(item, ","), + // }) + // } else if item, ok := v.([]int64); ok { + // log.Warn("oneM2M_deserialize: Failed to convert list of int64 into string: ", item) + // } else if item, ok := v.(map[string]interface{}); ok { + // v, err := json.Marshal(item) + // if err == nil { + // sensorResp.SensorCharacteristicList = append( + // sensorResp.SensorCharacteristicList, + // SensorCharacteristic{ + // CharacteristicName: k, + // CharacteristicValue: string(v), + // }) + // } else { + // log.Warn("oneM2M_deserialize: ", err.Error()) + // } + // } else if item, ok := v.([]interface{}); ok { + // log.Debug("oneM2M_deserialize: Got []interface {} for ", k) + // log.Debug("oneM2M_deserialize: ValueOf ", reflect.ValueOf(item)) + // v, err := json.Marshal(item) + // if err == nil { + // sensorResp.SensorCharacteristicList = append( + // sensorResp.SensorCharacteristicList, + // SensorCharacteristic{ + // CharacteristicName: k, + // CharacteristicValue: string(v), + // }) + // } else { + // log.Warn("oneM2M_deserialize: ", err.Error()) + // } + // } else { + // log.Warn("oneM2M_deserialize: Failed to process: ", k) + // } } - } - } // End of 'for' loop + } // End of 'for' loop - } // End of 'for' loop + } // End of 'for' loop + } log.Debug("oneM2M_deserialize: sensorResp: ", sensorResp) return sensorResp, nil } + +func interface_to_string(v interface{}) (s string) { + if item, ok := v.(string); ok { + return string(item) + } else if item, ok := v.(float64); ok { + return strconv.FormatFloat(item, 'f', -1, 64) + } else if item, ok := v.(int64); ok { + return strconv.FormatInt(item, 10) + } else if item, ok := v.(bool); ok { + return strconv.FormatBool(item) + } else if item, ok := v.([]string); ok { + return strings.Join(item, ",") + } else if item, ok := v.([]int64); ok { + log.Debug("interface_to_string: Got []interface {} for ", v) + log.Debug("interface_to_string: ValueOf ", reflect.ValueOf(item)) + val, err := json.Marshal(item) + if err == nil { + return string(val) + } else { + log.Warn("interface_to_string: ", err.Error()) + } + } else if item, ok := v.(map[string]interface{}); ok { + val, err := json.Marshal(item) + if err == nil { + return string(val) + } else { + log.Warn("interface_to_string: ", err.Error()) + } + } else if item, ok := v.([]interface{}); ok { + log.Debug("interface_to_string: Got []interface {} for ", v) + log.Debug("interface_to_string: ValueOf ", reflect.ValueOf(item)) + val, err := json.Marshal(item) + if err == nil { + return string(val) + } else { + log.Warn("interface_to_string: ", err.Error()) + } + } else { + log.Warn("interface_to_string: Failed to process: ", v) + } + + return "" +} diff --git a/go-packages/meep-sss-mgr/onem2m-mgr_test.go b/go-packages/meep-sss-mgr/onem2m-mgr_test.go index e3b43d099c1ca495f0b170e9c7d95d53541f552e..254dfdcc278990be21060e04263b4b9ebeb2f65e 100644 --- a/go-packages/meep-sss-mgr/onem2m-mgr_test.go +++ b/go-packages/meep-sss-mgr/onem2m-mgr_test.go @@ -20,7 +20,7 @@ import ( "fmt" "reflect" "testing" - "time" + //"time" log "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-logger" ) @@ -34,15 +34,15 @@ const tmNamespace = "sandboxtest" // // Invalid Connector // fmt.Println("Invalid SSS Asset Manager") -// tm, err := NewSssMgr("", tmNamespace, "MQTT", "172.29.10.56", 1883, "7feaadbb0400", "laboai-acme-ic-cse", nil, nil, nil) +// tm, err := NewSssMgr("", tmNamespace, "MQTT", "172.29.10.56", 1883, "0a692154-0f93-11f0-b554-df2f6a756f6a", "laboai-acme-ic-cse", nil, nil, nil) // if err == nil || tm != nil { // t.Fatalf("Service name not set") // } -// tm, err = NewSssMgr(tmName, tmNamespace, "", "172.29.10.56", 1883, "7feaadbb0400", "laboai-acme-ic-cse", nil, nil, nil) +// tm, err = NewSssMgr(tmName, tmNamespace, "", "172.29.10.56", 1883, "0a692154-0f93-11f0-b554-df2f6a756f6a", "laboai-acme-ic-cse", nil, nil, nil) // if err == nil || tm != nil { // t.Fatalf("Binding protocol not set") // } -// tm, err = NewSssMgr(tmName, tmNamespace, "MQTT", "", 1883, "7feaadbb0400", "laboai-acme-ic-cse", nil, nil, nil) +// tm, err = NewSssMgr(tmName, tmNamespace, "MQTT", "", 1883, "0a692154-0f93-11f0-b554-df2f6a756f6a", "laboai-acme-ic-cse", nil, nil, nil) // if err == nil || tm != nil { // t.Fatalf("Host not set") // } @@ -50,14 +50,14 @@ const tmNamespace = "sandboxtest" // if err == nil || tm != nil { // t.Fatalf("Host id not set") // } -// tm, err = NewSssMgr(tmName, tmNamespace, "MQTT", "172.29.10.56", 1883, "7feaadbb0400", "", nil, nil, nil) +// tm, err = NewSssMgr(tmName, tmNamespace, "MQTT", "172.29.10.56", 1883, "0a692154-0f93-11f0-b554-df2f6a756f6a", "", nil, nil, nil) // if err == nil || tm != nil { // t.Fatalf("CSE name not set") // } // // Valid Connector // fmt.Println("Create valid SSS Asset Manager") -// tm, err = NewSssMgr(tmName, tmNamespace, "MQTT", "172.29.10.56", 1883, "7feaadbb0400", "laboai-acme-ic-cse", nil, nil, nil) +// tm, err = NewSssMgr(tmName, tmNamespace, "MQTT", "172.29.10.56", 1883, "0a692154-0f93-11f0-b554-df2f6a756f6a", "laboai-acme-ic-cse", nil, nil, nil) // if err != nil || tm == nil { // t.Fatalf("Failed to create SSS Asset Manager") // } @@ -69,7 +69,7 @@ const tmNamespace = "sandboxtest" // tm = nil // fmt.Println("Create valid SSS Asset Manager") -// tm, err = NewSssMgr(tmName, tmNamespace, "REST_HTTP", "172.29.10.56", 31110, "7feaadbb0400", "laboai-acme-ic-cse", nil, nil, nil) +// tm, err = NewSssMgr(tmName, tmNamespace, "REST_HTTP", "172.29.10.56", 31110, "0a692154-0f93-11f0-b554-df2f6a756f6a", "laboai-acme-ic-cse", nil, nil, nil) // if err != nil || tm == nil { // t.Fatalf("Failed to create SSS Asset Manager") // } @@ -87,7 +87,7 @@ const tmNamespace = "sandboxtest" // log.MeepTextLogInit(t.Name()) // // Valid Connector // fmt.Println("Create valid SSS Asset Manager") -// tm, err := NewSssMgr(tmName, tmNamespace, "REST_HTTP", "lab-oai.etsi.org", 31110, "7feaadbb0400", "laboai-acme-ic-cse", nil, nil, nil) +// tm, err := NewSssMgr(tmName, tmNamespace, "REST_HTTP", "lab-oai.etsi.org", 31110, "0a692154-0f93-11f0-b554-df2f6a756f6a", "laboai-acme-ic-cse", nil, nil, nil) // if err != nil || tm == nil { // t.Fatalf("Failed to create SSS Asset Manager") // } @@ -110,7 +110,7 @@ const tmNamespace = "sandboxtest" // log.MeepTextLogInit(t.Name()) // // Valid Connector // fmt.Println("Create valid SSS Asset Manager") -// tm, err := NewSssMgr(tmName, tmNamespace, "REST_HTTP", "", 0, "7feaadbb0400", "laboai-acme-ic-cse", nil, nil, nil) +// tm, err := NewSssMgr(tmName, tmNamespace, "REST_HTTP", "", 0, "0a692154-0f93-11f0-b554-df2f6a756f6a", "laboai-acme-ic-cse", nil, nil, nil) // if err != nil || tm == nil { // t.Fatalf("Failed to create SSS Asset Manager") // } @@ -136,7 +136,7 @@ const tmNamespace = "sandboxtest" // // Valid Connector // fmt.Println("Create valid SSS Asset Manager") -// tm, err := NewSssMgr(tmName, tmNamespace, "REST_HTTP", "lab-oai.etsi.org", 31110, "7feaadbb0400", "laboai-acme-ic-cse", nil, nil, nil) +// tm, err := NewSssMgr(tmName, tmNamespace, "REST_HTTP", "lab-oai.etsi.org", 31110, "0a692154-0f93-11f0-b554-df2f6a756f6a", "laboai-acme-ic-cse", nil, nil, nil) // if err != nil || tm == nil { // t.Fatalf("Failed to create SSS Asset Manager") // } @@ -170,7 +170,7 @@ const tmNamespace = "sandboxtest" // // Valid Connector // fmt.Println("Create valid SSS Asset Manager") -// tm, err := NewSssMgr(tmName, tmNamespace, "REST_HTTP", "lab-oai.etsi.org", 31110, "7feaadbb0400", "laboai-acme-ic-cse", nil, nil, nil) +// tm, err := NewSssMgr(tmName, tmNamespace, "REST_HTTP", "lab-oai.etsi.org", 31110, "0a692154-0f93-11f0-b554-df2f6a756f6a", "laboai-acme-ic-cse", nil, nil, nil) // if err != nil || tm == nil { // t.Fatalf("Failed to create SSS Asset Manager") // } @@ -179,7 +179,7 @@ const tmNamespace = "sandboxtest" // SensorIdentifier: "12345", // SensorType: "AE", // SensorPosition: nil, -// IotPlatformId: "7feaadbb0400", +// IotPlatformId: "0a692154-0f93-11f0-b554-df2f6a756f6a", // } // new_sensor, err := tm.OneM2M_create(sensor, "") // if err != nil { @@ -191,7 +191,7 @@ const tmNamespace = "sandboxtest" // t.Fatalf("Failed to validate AE content") // } -// _ = tm.OneM2M_Delete(new_sensor) +// _ = tm.OneM2M_delete(new_sensor) // // Cleanup // err = tm.DeleteSssMgr() @@ -207,7 +207,7 @@ const tmNamespace = "sandboxtest" // // Valid Connector // fmt.Println("Create valid SSS Asset Manager") -// tm, err := NewSssMgr(tmName, tmNamespace, "REST_HTTP", "lab-oai.etsi.org", 31110, "7feaadbb0400", "laboai-acme-ic-cse", nil, nil, nil) +// tm, err := NewSssMgr(tmName, tmNamespace, "REST_HTTP", "lab-oai.etsi.org", 31110, "0a692154-0f93-11f0-b554-df2f6a756f6a", "laboai-acme-ic-cse", nil, nil, nil) // if err != nil || tm == nil { // t.Fatalf("Failed to create SSS Asset Manager") // } @@ -216,7 +216,7 @@ const tmNamespace = "sandboxtest" // SensorIdentifier: "CMyAE", // SensorType: "AE", // SensorPosition: nil, -// IotPlatformId: "7feaadbb0400", +// IotPlatformId: "0a692154-0f93-11f0-b554-df2f6a756f6a", // } // new_sensor_ae, err := tm.OneM2M_create(sensor_ae, "") // if err != nil { @@ -232,7 +232,7 @@ const tmNamespace = "sandboxtest" // SensorIdentifier: "CMyCNT", // SensorType: "CNT", // SensorPosition: nil, -// IotPlatformId: "7feaadbb0400", +// IotPlatformId: "0a692154-0f93-11f0-b554-df2f6a756f6a", // } // // sensor_cnt.SensorCharacteristicList = make([]SensorCharacteristic, 1) // // sensor_cnt.SensorCharacteristicList[0] = SensorCharacteristic{CharacteristicName: "con", CharacteristicValue: "OFF"} @@ -244,14 +244,14 @@ const tmNamespace = "sandboxtest" // // Verify content // if !validate_sensor_cnt(sensor_cnt, new_sensor_cnt) { -// t.Fatalf("Failed to validate AE content") +// t.Fatalf("Failed to validate CNT content") // } -// err = tm.OneM2M_Delete(new_sensor_cnt) +// err = tm.OneM2M_delete(new_sensor_cnt) // if err != nil { // t.Fatalf("Failed to create new sensor") // } -// err = tm.OneM2M_Delete(new_sensor_ae) +// err = tm.OneM2M_delete(new_sensor_ae) // if err != nil { // t.Fatalf("Failed to create new sensor") // } @@ -264,13 +264,87 @@ const tmNamespace = "sandboxtest" // tm = nil // } +func TestOneM2M_createAE_FLEXHttp(t *testing.T) { + fmt.Println("--- ", t.Name()) + log.MeepTextLogInit(t.Name()) + + // Valid Connector + fmt.Println("Create valid SSS Asset Manager") + tm, err := NewSssMgr(tmName, tmNamespace, "REST_HTTP", "lab-oai.etsi.org", 31110, "0a692154-0f93-11f0-b554-df2f6a756f6a", "laboai-acme-ic-cse", nil, nil, nil) + if err != nil || tm == nil { + t.Fatalf("Failed to create SSS Asset Manager") + } + + var sensor_ae = SensorDiscoveryInfo{ + SensorIdentifier: "CMyAE", + SensorType: "AE", + SensorPosition: nil, + IotPlatformId: "0a692154-0f93-11f0-b554-df2f6a756f6a", + } + new_sensor_ae, err := tm.OneM2M_create(sensor_ae, "") + if err != nil { + t.Fatalf("Failed to create new AE sensor") + } + + // Verify content + if !validate_sensor_ae(sensor_ae, new_sensor_ae) { + t.Fatalf("Failed to validate AE content") + } + + var sensor_flex = SensorDiscoveryInfo{ + SensorIdentifier: "CMyFLX", + SensorType: "FLX", + SensorPosition: nil, + IotPlatformId: "0a692154-0f93-11f0-b554-df2f6a756f6a", + } + sensor_flex.Flex = make(map[string]interface{}, 0) + sensor_flex.Flex["type"] = "cod:color" + sensor_flex.Flex["cnd"] = "org.onem2m.common.moduleclass.colour" + sensor_flex.Flex["red"] = 20 + sensor_flex.Flex["green"] = 20 + sensor_flex.Flex["blue"] = 20 + sensorPath := new_sensor_ae.SensorIdentifier + new_sensor_flex, err := tm.OneM2M_create(sensor_flex, sensorPath) + if err != nil { + t.Fatalf("Failed to create new CNT sensor") + } + + // Verify content + if !validate_sensor_flex(sensor_flex, new_sensor_flex) { + t.Fatalf("Failed to validate FLEX content") + } + + sensors, err := tm.SensorDiscoveryInfoAll() + if err != nil { + t.Fatalf(err.Error()) + } + fmt.Println("len=", len(sensors)) + fmt.Println("sensors", sensors) + + err = tm.OneM2M_delete(new_sensor_flex) + if err != nil { + t.Fatalf("Failed to create new sensor") + } + err = tm.OneM2M_delete(new_sensor_ae) + if err != nil { + t.Fatalf("Failed to create new sensor") + } + + // Cleanup + err = tm.DeleteSssMgr() + if err != nil { + t.Fatalf("Failed to cleanup SSS Asset Manager") + } + tm = nil +} + // func TestOneM2M_createAE_CNT_CNIHttp(t *testing.T) { // fmt.Println("--- ", t.Name()) // log.MeepTextLogInit(t.Name()) // // Valid Connector // fmt.Println("Create valid SSS Asset Manager") -// tm, err := NewSssMgr(tmName, tmNamespace, "REST_HTTP", "lab-oai.etsi.org", 31110, "7feaadbb0400", "laboai-acme-ic-cse", nil, nil, nil) +// tm, err := NewSssMgr(tmName, tmNamespace, "REST_HTTP", "lab-oai.etsi.org", 31110, "0a692154-0f93-11f0-b554-df2f6a756f6a", "laboai-acme-ic-cse", nil, nil, nil) // if err != nil || tm == nil { // t.Fatalf("Failed to create SSS Asset Manager") // } @@ -279,7 +353,7 @@ const tmNamespace = "sandboxtest" // SensorIdentifier: "CMyAE", // SensorType: "AE", // SensorPosition: nil, -// IotPlatformId: "7feaadbb0400", +// IotPlatformId: "0a692154-0f93-11f0-b554-df2f6a756f6a", // } // new_sensor_ae, err := tm.OneM2M_create(sensor_ae, "") // if err != nil { @@ -295,7 +369,7 @@ const tmNamespace = "sandboxtest" // SensorIdentifier: "CMyCNT", // SensorType: "CNT", // SensorPosition: nil, -// IotPlatformId: "7feaadbb0400", +// IotPlatformId: "0a692154-0f93-11f0-b554-df2f6a756f6a", // } // sensorPath := new_sensor_ae.SensorIdentifier // new_sensor_cnt, err := tm.OneM2M_create(sensor_cnt, sensorPath) @@ -312,7 +386,7 @@ const tmNamespace = "sandboxtest" // SensorIdentifier: "CMyCNI", // SensorType: "CIN", // SensorPosition: nil, -// IotPlatformId: "7feaadbb0400", +// IotPlatformId: "0a692154-0f93-11f0-b554-df2f6a756f6a", // } // sensor_cin.SensorCharacteristicList = make([]SensorCharacteristic, 1) // sensor_cin.SensorCharacteristicList[0] = SensorCharacteristic{CharacteristicName: "con", CharacteristicValue: "OFF"} @@ -327,15 +401,15 @@ const tmNamespace = "sandboxtest" // t.Fatalf("Failed to validate CIN content") // } -// err = tm.OneM2M_Delete(new_sensor_cin) +// err = tm.OneM2M_delete(new_sensor_cin) // if err != nil { // t.Fatalf("Failed to create new sensor") // } -// err = tm.OneM2M_Delete(new_sensor_cnt) +// err = tm.OneM2M_delete(new_sensor_cnt) // if err != nil { // t.Fatalf("Failed to create new sensor") // } -// err = tm.OneM2M_Delete(new_sensor_ae) +// err = tm.OneM2M_delete(new_sensor_ae) // if err != nil { // t.Fatalf("Failed to create new sensor") // } @@ -354,7 +428,7 @@ const tmNamespace = "sandboxtest" // // Valid Connector // fmt.Println("Create valid SSS Asset Manager") -// tm, err := NewSssMgr(tmName, tmNamespace, "REST_HTTP", "lab-oai.etsi.org", 31110, "7feaadbb0400", "laboai-acme-ic-cse", nil, nil, nil) +// tm, err := NewSssMgr(tmName, tmNamespace, "REST_HTTP", "lab-oai.etsi.org", 31110, "0a692154-0f93-11f0-b554-df2f6a756f6a", "laboai-acme-ic-cse", nil, nil, nil) // if err != nil || tm == nil { // t.Fatalf("Failed to create SSS Asset Manager") // } @@ -363,14 +437,14 @@ const tmNamespace = "sandboxtest" // SensorIdentifier: "12345", // SensorType: "AE", // SensorPosition: nil, -// IotPlatformId: "7feaadbb0400", +// IotPlatformId: "0a692154-0f93-11f0-b554-df2f6a756f6a", // } // sensor, err := oneM2M_create(tm, new_sensor, "") // if err != nil { // t.Fatalf("Failed to create new sensor: " + err.Error()) // } -// err = tm.OneM2M_Delete(sensor) +// err = tm.OneM2M_delete(sensor) // if err != nil { // t.Fatalf("Failed to create new sensor: " + err.Error()) // } @@ -389,7 +463,7 @@ const tmNamespace = "sandboxtest" // // Valid Connector // fmt.Println("Create valid SSS Asset Manager") -// tm, err := NewSssMgr(tmName, tmNamespace, "REST_HTTP", "lab-oai.etsi.org", 31110, "7feaadbb0400", "laboai-acme-ic-cse", nil, nil, nil) +// tm, err := NewSssMgr(tmName, tmNamespace, "REST_HTTP", "lab-oai.etsi.org", 31110, "0a692154-0f93-11f0-b554-df2f6a756f6a", "laboai-acme-ic-cse", nil, nil, nil) // if err != nil || tm == nil { // t.Fatalf("Failed to create SSS Asset Manager") // } @@ -423,9 +497,9 @@ const tmNamespace = "sandboxtest" // t.Fatalf("Failed to validate CIN content") // } -// _ = tm.OneM2M_Delete(received_sensors["CIN"]) -// _ = tm.OneM2M_Delete(received_sensors["CNT"]) -// _ = tm.OneM2M_Delete(received_sensors["AE"]) +// _ = tm.OneM2M_delete(received_sensors["CIN"]) +// _ = tm.OneM2M_delete(received_sensors["CNT"]) +// _ = tm.OneM2M_delete(received_sensors["AE"]) // // Cleanup // err = tm.DeleteSssMgr() @@ -441,7 +515,7 @@ const tmNamespace = "sandboxtest" // // Valid Connector // fmt.Println("Create valid SSS Asset Manager") -// tm, err := NewSssMgr(tmName, tmNamespace, "REST_HTTP", "lab-oai.etsi.org", 31110, "7feaadbb0400", "laboai-acme-ic-cse", nil, nil, nil) +// tm, err := NewSssMgr(tmName, tmNamespace, "REST_HTTP", "lab-oai.etsi.org", 31110, "0a692154-0f93-11f0-b554-df2f6a756f6a", "laboai-acme-ic-cse", nil, nil, nil) // if err != nil || tm == nil { // t.Fatalf("Failed to create SSS Asset Manager") // } @@ -482,9 +556,9 @@ const tmNamespace = "sandboxtest" // t.Fatalf("Failed to validate AE content") // } -// _ = tm.OneM2M_Delete(received_sensors["CIN"]) -// _ = tm.OneM2M_Delete(received_sensors["CNT"]) -// _ = tm.OneM2M_Delete(received_sensors["AE"]) +// _ = tm.OneM2M_delete(received_sensors["CIN"]) +// _ = tm.OneM2M_delete(received_sensors["CNT"]) +// _ = tm.OneM2M_delete(received_sensors["AE"]) // // Cleanup // err = tm.DeleteSssMgr() @@ -494,67 +568,67 @@ const tmNamespace = "sandboxtest" // tm = nil // } -func TestOneM2M_subscribeHttp(t *testing.T) { - fmt.Println("--- ", t.Name()) - log.MeepTextLogInit(t.Name()) +// func TestOneM2M_subscribeHttp(t *testing.T) { +// fmt.Println("--- ", t.Name()) +// log.MeepTextLogInit(t.Name()) - // Valid Connector - fmt.Println("Create valid SSS Asset Manager") - tm, err := NewSssMgr(tmName, tmNamespace, "REST_HTTP", "lab-oai.etsi.org", 31110, "7feaadbb0400", "laboai-acme-ic-cse", discoveryNotify, statusNotify, dataNotify) - if err != nil || tm == nil { - t.Fatalf("Failed to create SSS Asset Manager") - } +// // Valid Connector +// fmt.Println("Create valid SSS Asset Manager") +// tm, err := NewSssMgr(tmName, tmNamespace, "REST_HTTP", "lab-oai.etsi.org", 31110, "0a692154-0f93-11f0-b554-df2f6a756f6a", "laboai-acme-ic-cse", discoveryNotify, statusNotify, dataNotify) +// if err != nil || tm == nil { +// t.Fatalf("Failed to create SSS Asset Manager") +// } - _, received_sensors, err := oneM2M_createAE_CNT_CIN(tm) - if err != nil { - t.Fatalf("Failed to create sensors") - } +// _, received_sensors, err := oneM2M_createAE_CNT_CIN(tm) +// if err != nil { +// t.Fatalf("Failed to create sensors") +// } - subscriptionMap := make(map[string]interface{}) - subId, err := tm.OneM2M_subscribe(received_sensors["AE"].IotPlatformId, received_sensors["AE"].SensorIdentifier) - if err != nil { - t.Fatalf("Failed to subscribe") - } - fmt.Println("subId=" + subId) - subscriptionMap[subId] = received_sensors["AE"] +// subscriptionMap := make(map[string]interface{}) +// subId, err := tm.OneM2M_subscribe(received_sensors["AE"].IotPlatformId, received_sensors["AE"].SensorIdentifier) +// if err != nil { +// t.Fatalf("Failed to subscribe") +// } +// fmt.Println("subId=" + subId) +// subscriptionMap[subId] = received_sensors["AE"] - subId, err = tm.OneM2M_subscribe(received_sensors["CNT"].IotPlatformId, received_sensors["CNT"].SensorIdentifier) - if err != nil { - t.Fatalf("Failed to subscribe") - } - fmt.Println("subId=" + subId) - subscriptionMap[subId] = received_sensors["CNT"] +// subId, err = tm.OneM2M_subscribe(received_sensors["CNT"].IotPlatformId, received_sensors["CNT"].SensorIdentifier) +// if err != nil { +// t.Fatalf("Failed to subscribe") +// } +// fmt.Println("subId=" + subId) +// subscriptionMap[subId] = received_sensors["CNT"] - fmt.Println("len(subscriptionMap)=" + fmt.Sprint(len(subscriptionMap))) +// fmt.Println("len(subscriptionMap)=" + fmt.Sprint(len(subscriptionMap))) - fmt.Println("You have 120 seconds to trigger subscriptions") - time.Sleep(time.Duration(120) * time.Second) +// fmt.Println("You have 120 seconds to trigger subscriptions") +// time.Sleep(time.Duration(120) * time.Second) - for k := range subscriptionMap { - err = tm.OneM2M_DeleteSub(k) - if err != nil { - t.Fatalf("Failed to cancel subscription") - } - } // End of 'for' statement +// for k := range subscriptionMap { +// err = tm.OneM2M_delete_subscription(k) +// if err != nil { +// t.Fatalf("Failed to cancel subscription") +// } +// } // End of 'for' statement - _ = tm.OneM2M_Delete(received_sensors["CIN"]) - _ = tm.OneM2M_Delete(received_sensors["CNT"]) - _ = tm.OneM2M_Delete(received_sensors["AE"]) +// _ = tm.OneM2M_delete(received_sensors["CIN"]) +// _ = tm.OneM2M_delete(received_sensors["CNT"]) +// _ = tm.OneM2M_delete(received_sensors["AE"]) - // Cleanup - err = tm.DeleteSssMgr() - if err != nil { - t.Fatalf("Failed to cleanup SSS Asset Manager") - } - tm = nil -} +// // Cleanup +// err = tm.DeleteSssMgr() +// if err != nil { +// t.Fatalf("Failed to cleanup SSS Asset Manager") +// } +// tm = nil +// } // func TestPopulateDevicesPerIotPlatformsMqtt(t *testing.T) { // fmt.Println("--- ", t.Name()) // log.MeepTextLogInit(t.Name()) // // Valid Connector // fmt.Println("Create valid SSS Asset Manager") -// tm, err := NewSssMgr(tmName, tmNamespace, "MQTT", "172.29.10.56", 1883, "7feaadbb0400", "laboai-acme-ic-cse", nil, nil, nil) +// tm, err := NewSssMgr(tmName, tmNamespace, "MQTT", "172.29.10.56", 1883, "0a692154-0f93-11f0-b554-df2f6a756f6a", "laboai-acme-ic-cse", nil, nil, nil) // if err != nil || tm == nil { // t.Fatalf("Failed to create SSS Asset Manager") // } @@ -576,7 +650,7 @@ func TestOneM2M_subscribeHttp(t *testing.T) { // // Valid Connector // fmt.Println("Create valid SSS Asset Manager") -// tm, err := NewSssMgr(tmName, tmNamespace, "MQTT", "172.29.10.56", 1883, "7feaadbb0400", "laboai-acme-ic-cse", nil, nil, nil) +// tm, err := NewSssMgr(tmName, tmNamespace, "MQTT", "172.29.10.56", 1883, "0a692154-0f93-11f0-b554-df2f6a756f6a", "laboai-acme-ic-cse", nil, nil, nil) // if err != nil || tm == nil { // t.Fatalf("Failed to create SSS Asset Manager") // } @@ -605,7 +679,7 @@ func TestOneM2M_subscribeHttp(t *testing.T) { // // Valid Connector // fmt.Println("Create valid SSS Asset Manager") -// tm, err := NewSssMgr(tmName, tmNamespace, "MQTT", "172.29.10.56", 1883, "7feaadbb0400", "laboai-acme-ic-cse", nil, nil, nil) +// tm, err := NewSssMgr(tmName, tmNamespace, "MQTT", "172.29.10.56", 1883, "0a692154-0f93-11f0-b554-df2f6a756f6a", "laboai-acme-ic-cse", nil, nil, nil) // if err != nil || tm == nil { // t.Fatalf("Failed to create SSS Asset Manager") // } @@ -639,7 +713,7 @@ func TestOneM2M_subscribeHttp(t *testing.T) { // // Valid Connector // fmt.Println("Create valid SSS Asset Manager") -// tm, err := NewSssMgr(tmName, tmNamespace, "MQTT", "172.29.10.56", 1883, "7feaadbb0400", "laboai-acme-ic-cse", nil, nil, nil) +// tm, err := NewSssMgr(tmName, tmNamespace, "MQTT", "172.29.10.56", 1883, "0a692154-0f93-11f0-b554-df2f6a756f6a", "laboai-acme-ic-cse", nil, nil, nil) // if err != nil || tm == nil { // t.Fatalf("Failed to create SSS Asset Manager") // } @@ -648,7 +722,7 @@ func TestOneM2M_subscribeHttp(t *testing.T) { // SensorIdentifier: "12345", // SensorType: "AE", // SensorPosition: nil, -// IotPlatformId: "7feaadbb0400", +// IotPlatformId: "0a692154-0f93-11f0-b554-df2f6a756f6a", // } // new_sensor, err := tm.OneM2M_create(sensor, "") // if err != nil { @@ -660,7 +734,7 @@ func TestOneM2M_subscribeHttp(t *testing.T) { // t.Fatalf("Failed to validate AE content") // } -// _ = tm.OneM2M_Delete(new_sensor) +// _ = tm.OneM2M_delete(new_sensor) // // Cleanup // err = tm.DeleteSssMgr() @@ -676,7 +750,7 @@ func TestOneM2M_subscribeHttp(t *testing.T) { // // Valid Connector // fmt.Println("Create valid SSS Asset Manager") -// tm, err := NewSssMgr(tmName, tmNamespace, "MQTT", "172.29.10.56", 1883, "7feaadbb0400", "laboai-acme-ic-cse", nil, nil, nil) +// tm, err := NewSssMgr(tmName, tmNamespace, "MQTT", "172.29.10.56", 1883, "0a692154-0f93-11f0-b554-df2f6a756f6a", "laboai-acme-ic-cse", nil, nil, nil) // if err != nil || tm == nil { // t.Fatalf("Failed to create SSS Asset Manager") // } @@ -685,7 +759,7 @@ func TestOneM2M_subscribeHttp(t *testing.T) { // SensorIdentifier: "CMyAE", // SensorType: "AE", // SensorPosition: nil, -// IotPlatformId: "7feaadbb0400", +// IotPlatformId: "0a692154-0f93-11f0-b554-df2f6a756f6a", // } // new_sensor_ae, err := tm.OneM2M_create(sensor_ae, "") // if err != nil { @@ -701,7 +775,7 @@ func TestOneM2M_subscribeHttp(t *testing.T) { // SensorIdentifier: "CMyCNT", // SensorType: "CNT", // SensorPosition: nil, -// IotPlatformId: "7feaadbb0400", +// IotPlatformId: "0a692154-0f93-11f0-b554-df2f6a756f6a", // } // // sensor_cnt.SensorCharacteristicList = make([]SensorCharacteristic, 1) // // sensor_cnt.SensorCharacteristicList[0] = SensorCharacteristic{CharacteristicName: "con", CharacteristicValue: "OFF"} @@ -716,11 +790,11 @@ func TestOneM2M_subscribeHttp(t *testing.T) { // t.Fatalf("Failed to validate CNT content") // } -// err = tm.OneM2M_Delete(new_sensor_cnt) +// err = tm.OneM2M_delete(new_sensor_cnt) // if err != nil { // t.Fatalf("Failed to create new sensor") // } -// err = tm.OneM2M_Delete(new_sensor_ae) +// err = tm.OneM2M_delete(new_sensor_ae) // if err != nil { // t.Fatalf("Failed to create new sensor") // } @@ -739,7 +813,7 @@ func TestOneM2M_subscribeHttp(t *testing.T) { // // Valid Connector // fmt.Println("Create valid SSS Asset Manager") -// tm, err := NewSssMgr(tmName, tmNamespace, "MQTT", "172.29.10.56", 1883, "7feaadbb0400", "laboai-acme-ic-cse", nil, nil, nil) +// tm, err := NewSssMgr(tmName, tmNamespace, "MQTT", "172.29.10.56", 1883, "0a692154-0f93-11f0-b554-df2f6a756f6a", "laboai-acme-ic-cse", nil, nil, nil) // if err != nil || tm == nil { // t.Fatalf("Failed to create SSS Asset Manager") // } @@ -748,7 +822,7 @@ func TestOneM2M_subscribeHttp(t *testing.T) { // SensorIdentifier: "CMyAE", // SensorType: "AE", // SensorPosition: nil, -// IotPlatformId: "7feaadbb0400", +// IotPlatformId: "0a692154-0f93-11f0-b554-df2f6a756f6a", // } // new_sensor_ae, err := tm.OneM2M_create(sensor_ae, "") // if err != nil { @@ -764,7 +838,7 @@ func TestOneM2M_subscribeHttp(t *testing.T) { // SensorIdentifier: "CMyCNT", // SensorType: "CNT", // SensorPosition: nil, -// IotPlatformId: "7feaadbb0400", +// IotPlatformId: "0a692154-0f93-11f0-b554-df2f6a756f6a", // } // sensorPath := new_sensor_ae.SensorIdentifier // new_sensor_cnt, err := tm.OneM2M_create(sensor_cnt, sensorPath) @@ -781,7 +855,7 @@ func TestOneM2M_subscribeHttp(t *testing.T) { // SensorIdentifier: "CMyCNI", // SensorType: "CIN", // SensorPosition: nil, -// IotPlatformId: "7feaadbb0400", +// IotPlatformId: "0a692154-0f93-11f0-b554-df2f6a756f6a", // } // sensor_cin.SensorCharacteristicList = make([]SensorCharacteristic, 1) // sensor_cin.SensorCharacteristicList[0] = SensorCharacteristic{CharacteristicName: "con", CharacteristicValue: "OFF"} @@ -800,15 +874,15 @@ func TestOneM2M_subscribeHttp(t *testing.T) { // t.Fatalf("received_sensor.SensorCharacteristicList shall not be empty") // } -// err = tm.OneM2M_Delete(new_sensor_cin) +// err = tm.OneM2M_delete(new_sensor_cin) // if err != nil { // t.Fatalf("Failed to create new sensor") // } -// err = tm.OneM2M_Delete(new_sensor_cnt) +// err = tm.OneM2M_delete(new_sensor_cnt) // if err != nil { // t.Fatalf("Failed to create new sensor") // } -// err = tm.OneM2M_Delete(new_sensor_ae) +// err = tm.OneM2M_delete(new_sensor_ae) // if err != nil { // t.Fatalf("Failed to create new sensor") // } @@ -827,7 +901,7 @@ func TestOneM2M_subscribeHttp(t *testing.T) { // // Valid Connector // fmt.Println("Create valid SSS Asset Manager") -// tm, err := NewSssMgr(tmName, tmNamespace, "MQTT", "172.29.10.56", 1883, "7feaadbb0400", "laboai-acme-ic-cse", nil, nil, nil) +// tm, err := NewSssMgr(tmName, tmNamespace, "MQTT", "172.29.10.56", 1883, "0a692154-0f93-11f0-b554-df2f6a756f6a", "laboai-acme-ic-cse", nil, nil, nil) // if err != nil || tm == nil { // t.Fatalf("Failed to create SSS Asset Manager") // } @@ -836,14 +910,14 @@ func TestOneM2M_subscribeHttp(t *testing.T) { // SensorIdentifier: "12345", // SensorType: "AE", // SensorPosition: nil, -// IotPlatformId: "7feaadbb0400", +// IotPlatformId: "0a692154-0f93-11f0-b554-df2f6a756f6a", // } // sensor, err := oneM2M_create(tm, new_sensor, "") // if err != nil { // t.Fatalf("Failed to create new sensor: " + err.Error()) // } -// err = tm.OneM2M_Delete(sensor) +// err = tm.OneM2M_delete(sensor) // if err != nil { // t.Fatalf("Failed to create new sensor: " + err.Error()) // } @@ -862,7 +936,7 @@ func TestOneM2M_subscribeHttp(t *testing.T) { // // Valid Connector // fmt.Println("Create valid SSS Asset Manager") -// tm, err := NewSssMgr(tmName, tmNamespace, "MQTT", "172.29.10.56", 1883, "7feaadbb0400", "laboai-acme-ic-cse", nil, nil, nil) +// tm, err := NewSssMgr(tmName, tmNamespace, "MQTT", "172.29.10.56", 1883, "0a692154-0f93-11f0-b554-df2f6a756f6a", "laboai-acme-ic-cse", nil, nil, nil) // if err != nil || tm == nil { // t.Fatalf("Failed to create SSS Asset Manager") // } @@ -896,9 +970,9 @@ func TestOneM2M_subscribeHttp(t *testing.T) { // t.Fatalf("Failed to validate CIN content") // } -// _ = tm.OneM2M_Delete(received_sensors["CIN"]) -// _ = tm.OneM2M_Delete(received_sensors["CNT"]) -// _ = tm.OneM2M_Delete(received_sensors["AE"]) +// _ = tm.OneM2M_delete(received_sensors["CIN"]) +// _ = tm.OneM2M_delete(received_sensors["CNT"]) +// _ = tm.OneM2M_delete(received_sensors["AE"]) // // Cleanup // err = tm.DeleteSssMgr() @@ -914,7 +988,7 @@ func TestOneM2M_subscribeHttp(t *testing.T) { // // Valid Connector // fmt.Println("Create valid SSS Asset Manager") -// tm, err := NewSssMgr(tmName, tmNamespace, "MQTT", "172.29.10.56", 1883, "7feaadbb0400", "laboai-acme-ic-cse", nil, nil, nil) +// tm, err := NewSssMgr(tmName, tmNamespace, "MQTT", "172.29.10.56", 1883, "0a692154-0f93-11f0-b554-df2f6a756f6a", "laboai-acme-ic-cse", nil, nil, nil) // if err != nil || tm == nil { // t.Fatalf("Failed to create SSS Asset Manager") // } @@ -955,9 +1029,9 @@ func TestOneM2M_subscribeHttp(t *testing.T) { // t.Fatalf("Failed to validate AE content") // } -// _ = tm.OneM2M_Delete(received_sensors["CIN"]) -// _ = tm.OneM2M_Delete(received_sensors["CNT"]) -// _ = tm.OneM2M_Delete(received_sensors["AE"]) +// _ = tm.OneM2M_delete(received_sensors["CIN"]) +// _ = tm.OneM2M_delete(received_sensors["CNT"]) +// _ = tm.OneM2M_delete(received_sensors["AE"]) // // Cleanup // err = tm.DeleteSssMgr() @@ -966,26 +1040,59 @@ func TestOneM2M_subscribeHttp(t *testing.T) { // } // tm = nil // } - -// func TestVaidateOneM2MNotificationServer(t *testing.T) { +// func TestOneM2M_subscribeMQTT(t *testing.T) { // fmt.Println("--- ", t.Name()) // log.MeepTextLogInit(t.Name()) // // Valid Connector // fmt.Println("Create valid SSS Asset Manager") -// tm, err := NewSssMgr(tmName, tmNamespace, "MQTT", "172.29.10.56", 1883, "MQTT", "172.29.10.56", 1883, "7feaadbb0400", "laboai-acme-ic-cse", nil, nil, nil) +// tm, err := NewSssMgr(tmName, tmNamespace, "MQTT", "172.29.10.56", 1883, "0a692154-0f93-11f0-b554-df2f6a756f6a", "laboai-acme-ic-cse", discoveryNotify, statusNotify, dataNotify) // if err != nil || tm == nil { // t.Fatalf("Failed to create SSS Asset Manager") // } -// tm.init() -// fmt.Println("Waiting for 2 minutes to do curl request: curl -v http://mec-platform.etsi.org:31122/sbxykqjr17/mep1/sens/v1 ") +// _, received_sensors, err := oneM2M_createAE_CNT_CIN(tm) +// if err != nil { +// t.Fatalf("Failed to create sensors") +// } + +// subscriptionMap := make(map[string]interface{}) +// subId, err := tm.OneM2M_subscribe(received_sensors["AE"].IotPlatformId, received_sensors["AE"].SensorIdentifier) +// if err != nil { +// t.Fatalf("Failed to subscribe") +// } +// fmt.Println("subId=" + subId) +// subscriptionMap[subId] = received_sensors["AE"] + +// subId, err = tm.OneM2M_subscribe(received_sensors["CNT"].IotPlatformId, received_sensors["CNT"].SensorIdentifier) +// if err != nil { +// t.Fatalf("Failed to subscribe") +// } +// fmt.Println("subId=" + subId) +// subscriptionMap[subId] = received_sensors["CNT"] + +// fmt.Println("len(subscriptionMap)=" + fmt.Sprint(len(subscriptionMap))) + +// fmt.Println("You have 120 seconds to trigger subscriptions") +// time.Sleep(time.Duration(120) * time.Second) + +// for k := range subscriptionMap { +// err = tm.OneM2M_delete_subscription(k) +// if err != nil { +// t.Fatalf("Failed to cancel subscription") +// } +// } // End of 'for' statement + +// _ = tm.OneM2M_delete(received_sensors["CIN"]) +// _ = tm.OneM2M_delete(received_sensors["CNT"]) +// _ = tm.OneM2M_delete(received_sensors["AE"]) // // Cleanup // err = tm.DeleteSssMgr() // if err != nil { // t.Fatalf("Failed to cleanup SSS Asset Manager") // } +// tm = nil // } func oneM2M_create(tm *SssMgr, sensor SensorDiscoveryInfo, path string) (sensorResp SensorDiscoveryInfo, err error) { @@ -1005,7 +1112,7 @@ func oneM2M_createAE_CNT_CIN(tm *SssMgr) (sensors map[string]SensorDiscoveryInfo SensorIdentifier: "CMyAE", SensorType: "AE", SensorPosition: nil, - IotPlatformId: "7feaadbb0400", + IotPlatformId: "0a692154-0f93-11f0-b554-df2f6a756f6a", } sensors[sensor_ae.SensorType] = sensor_ae new_sensor_ae, err := oneM2M_create(tm, sensor_ae, "") @@ -1018,7 +1125,7 @@ func oneM2M_createAE_CNT_CIN(tm *SssMgr) (sensors map[string]SensorDiscoveryInfo SensorIdentifier: "CMyCNT", SensorType: "CNT", SensorPosition: nil, - IotPlatformId: "7feaadbb0400", + IotPlatformId: "0a692154-0f93-11f0-b554-df2f6a756f6a", } sensorPath := new_sensor_ae.SensorIdentifier sensors[sensor_cnt.SensorType] = sensor_cnt @@ -1032,7 +1139,7 @@ func oneM2M_createAE_CNT_CIN(tm *SssMgr) (sensors map[string]SensorDiscoveryInfo SensorIdentifier: "CMyCNI", SensorType: "CIN", SensorPosition: nil, - IotPlatformId: "7feaadbb0400", + IotPlatformId: "0a692154-0f93-11f0-b554-df2f6a756f6a", } sensor_cin.SensorCharacteristicList = make([]SensorCharacteristic, 1) sensor_cin.SensorCharacteristicList[0] = SensorCharacteristic{CharacteristicName: "con", CharacteristicValue: "OFF"} @@ -1122,7 +1229,7 @@ func validate_sensor_cnt(expected_sensor SensorDiscoveryInfo, received_sensor Se fmt.Println(">>> validate_sensor_cnt: received_sensor: ", received_sensor) if received_sensor.SensorIdentifier == "" { - fmt.Println("validate_sensor_ae.SensorIdentifier shall be set") + fmt.Println("validate_sensor_cnt.SensorIdentifier shall be set") return false } if received_sensor.SensorType != received_sensor.SensorType { @@ -1207,6 +1314,39 @@ func validate_sensor_cin(expected_sensor SensorDiscoveryInfo, received_sensor Se return true } +func validate_sensor_flex(expected_sensor SensorDiscoveryInfo, received_sensor SensorDiscoveryInfo) bool { + fmt.Println(">>> validate_sensor_flex: expected_sensor: ", expected_sensor) + fmt.Println(">>> validate_sensor_flex: received_sensor: ", received_sensor) + + if received_sensor.SensorIdentifier == "" { + fmt.Println("validate_sensor_flex.SensorIdentifier shall be set") + return false + } + if received_sensor.SensorType != received_sensor.SensorType { + fmt.Println("validate_sensor_flex.SensorType != SensorType") + return false + } + if received_sensor.IotPlatformId != expected_sensor.IotPlatformId { + fmt.Println("validate_sensor_flex.IotPlatformId != IotPlatformId") + return false + } + if expected_sensor.Flex == nil || received_sensor.Flex == nil { + fmt.Println("validate_sensor_flex.Flex != Flex") + return false + } else if len(expected_sensor.Flex) != len(received_sensor.Flex) { + fmt.Println("len(validate_sensor_flex.Flex) != len(Flex)") + return false + } else if expected_sensor.Flex["type"] != received_sensor.Flex["type"] { + fmt.Println("validate_sensor_flex.Flex[type] != Flex[type]") + return false + } else if expected_sensor.Flex["cnd"] != received_sensor.Flex["cnd"] { + fmt.Println("validate_sensor_flex.Flex[cnd] != Flex[cnd]") + return false + } + + return true +} + func discoveryNotify(map[string]interface{}) { log.Debug(">>> discoveryNotify") diff --git a/go-packages/meep-vis-traffic-mgr/mqtt.go b/go-packages/meep-vis-traffic-mgr/mqtt.go index 43c6832ad960a7e29797b014563533b986c28711..96b52b85cbc1b70908c7fba8ef5b8882bd5fca80 100644 --- a/go-packages/meep-vis-traffic-mgr/mqtt.go +++ b/go-packages/meep-vis-traffic-mgr/mqtt.go @@ -17,10 +17,10 @@ package vistrafficmgr import ( + "crypto/tls" "encoding/hex" "errors" - "fmt" - "net/url" + "strings" log "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-logger" mqtt "github.com/eclipse/paho.mqtt.golang" @@ -59,16 +59,10 @@ var connectLostHandler mqtt.ConnectionLostHandler = func(client mqtt.Client, err } func (broker_mqtt *message_broker_mqtt) Init(tm *TrafficMgr) (err error) { - log.Debug(">>> Init") + log.Debug(">>> Init: ", tm.broker) broker_mqtt.running = false - u, err := url.ParseRequestURI(tm.broker) - if err != nil { - log.Error(err.Error()) - return err - } - broker_mqtt.opts = mqtt.NewClientOptions() broker_mqtt.opts.SetDefaultPublishHandler(onMessageReceived) broker_mqtt.opts.SetClientID("AdvantEDGE.meep-vis-traffic-mgr") @@ -77,8 +71,10 @@ func (broker_mqtt *message_broker_mqtt) Init(tm *TrafficMgr) (err error) { broker_mqtt.opts.CleanSession = true broker_mqtt.opts.SetUsername("") broker_mqtt.opts.SetPassword("") - log.Info("Init: Add brocker: ", fmt.Sprintf("tcp://%s:%s", u.Hostname(), u.Port())) - broker_mqtt.opts.AddBroker(fmt.Sprintf("tcp://%s:%s", u.Hostname(), u.Port())) + if strings.Index(tm.broker, "mqtts://") == 0 || strings.Index(tm.broker, "wss://") == 0 { + broker_mqtt.opts.SetTLSConfig(&tls.Config{InsecureSkipVerify: true}) + } + broker_mqtt.opts.AddBroker(tm.broker) broker_mqtt.client = mqtt.NewClient(broker_mqtt.opts) log.Info("Init: Connect to MQTT server...")