diff --git a/HISTORY.md b/HISTORY.md index 2106a1f..020d520 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,12 @@ # Version History +## 1.1.0 / 2023-05-03 + +- Removed Topics and subscriptions from code and documentation +- Parametrize apiversion +- Update documentation links to Aveva domain +- Added build status to readme file + ## 1.0.2 / 2022-08-17 - Automate dependabot approval and automerge diff --git a/README.md b/README.md index e29c4d4..dc395cd 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # OMF to Data Hub Quick Start Guide -**Version:** 1.0.2 +**Version:** 1.1.0 + +[![Build Status](https://dev.azure.com/osieng/engineering/_apis/build/status%2Fproduct-readiness%2FADH%2Fsample-adh-omf_to_adh_quick_start-guide_jupyter-python?repoName=osisoft%2Fsample-adh-omf_to_adh_quick_start-guide_jupyter-python&branchName=main)](https://dev.azure.com/osieng/engineering/_build/latest?definitionId=4706&repoName=osisoft%2Fsample-adh-omf_to_adh_quick_start-guide_jupyter-python&branchName=main) The sample code in this folder demonstrates an example of using OMF to send data into AVEVA Data Hub and the Sequential Data Store (SDS) using Python Jupyter Notebook. In order to run this sample, you need to have [Python](https://www.python.org/downloads/) installed. @@ -16,29 +18,28 @@ The example in this guide will send data for a drone, including its location, ba ```json { "Resource": "PLACEHOLDER_REPLACE_WITH_RESOURCE", - "ClientId": "PLACEHOLDER_REPLACE_WITH_CLIENT_ID", - "ClientSecret": "PLACEHOLDER_REPLACE_WITH_CLIENT_SECRET", + "ApiVersion": "PLACEHOLDER_REPLACE_WITH_API_VERSION", "TenantId": "PLACEHOLDER_REPLACE_WITH_TENANT_ID", "NamespaceId": "PLACEHOLDER_REPLACE_WITH_NAMESPACE_ID", - "TopicName": "PLACEHOLDER_REPLACE_WITH_TOPIC_NAME", - "TopicDescription": "PLACEHOLDER_REPLACE_WITH_TOPIC_DESCRIPTION", - "SubscriptionName": "PLACEHOLDER_REPLACE_WITH_SUBSCRIPTION_NAME", - "SubscriptionDescription": "PLACEHOLDER_REPLACE_WITH_SUBSCRIPTION_DESCRIPTION" + "CommunityId": "PLACEHOLDER_REPLACE_WITH_COMMUNITY_ID", + "ClientId": "PLACEHOLDER_REPLACE_WITH_APPLICATION_IDENTIFIER", + "ClientSecret": "PLACEHOLDER_REPLACE_WITH_APPLICATION_SECRET", + "OMFConnectionName" : "PLACEHOLDER_REPLACE_WITH_OMF_CONNECTION_NAME", + "OMFConnectionDescription" : "PLACEHOLDER_REPLACE_WITH_OMF_CONNECTION_DESCRIPTION" } ``` | Parameters | Required | Type | Description | | --------------- | -------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Resource | required | string | The endpoint for ADH of the namespace. If the namespace is located in NA, it is https://uswe.datahub.connect.aveva.com and if in EMEA, it is https://euno.datahub.connect.aveva.com | -| ClientId | required | string | The id of the client credentials client to use | -| ClientSecret | required | string | The secret of the client credentials client to use | +| ApiVersion | required | string | The version of the API to be used | | TenantId | required | string | The id of the tenant to use | | NamespaceId | required | string | The id of the namespace to use | -| TopicName | required | string | The name of the OMF topic to create -| TopicDescription | optional | string | The description of the OMF topic to create -| SubscriptionName | required | string | The name of the OMF subscription to create -| SubscriptionDescription | optional | string | The description of the OMF subscription to create - +| CommunityID | options | string | The name of the community to use (if any) +| ClientId | required | string | The id of the client credentials client to use | +| ClientSecret | required | string | The secret of the client credentials client to use +| OMFConnectionName | required | string | The name of the OMF connection to create | +| OMFConnectionDescription | optional | string | The description of the OMF connection to create ### Running the Jupyter Notebook @@ -50,7 +51,7 @@ The example in this guide will send data for a drone, including its location, ba ## Documentation -The documentation for the various topics and APIs used here can be found at the [AVEVA Data Hub documentation website](https://docs.osisoft.com/category/adh-get-started) +The documentation for the various topics and APIs used here can be found at the [AVEVA Data Hub documentation website](https://docs.aveva.com/category/adh-get-started) --- diff --git a/appsettings.placeholder.json b/appsettings.placeholder.json index f56edb2..4755456 100644 --- a/appsettings.placeholder.json +++ b/appsettings.placeholder.json @@ -1,11 +1,11 @@ { - "Resource": "PLACEHOLDER_REPLACE_WITH_RESOURCE", - "ClientId": "PLACEHOLDER_REPLACE_WITH_CLIENT_ID", - "ClientSecret": "PLACEHOLDER_REPLACE_WITH_CLIENT_SECRET", - "TenantId": "PLACEHOLDER_REPLACE_WITH_TENANT_ID", - "NamespaceId": "PLACEHOLDER_REPLACE_WITH_NAMESPACE_ID", - "TopicName": "PLACEHOLDER_REPLACE_WITH_TOPIC_NAME", - "TopicDescription": "PLACEHOLDER_REPLACE_WITH_TOPIC_DESCRIPTION", - "SubscriptionName": "PLACEHOLDER_REPLACE_WITH_SUBSCRIPTION_NAME", - "SubscriptionDescription": "PLACEHOLDER_REPLACE_WITH_SUBSCRIPTION_DESCRIPTION" + "Resource": "PLACEHOLDER_REPLACE_WITH_RESOURCE", + "ApiVersion": "v1", + "TenantId": "PLACEHOLDER_REPLACE_WITH_TENANT_ID", + "NamespaceId": "PLACEHOLDER_REPLACE_WITH_NAMESPACE_ID", + "CommunityId": "PLACEHOLDER_REPLACE_WITH_COMMUNITY_ID", + "ClientId": "PLACEHOLDER_REPLACE_WITH_APPLICATION_IDENTIFIER", + "ClientSecret": "PLACEHOLDER_REPLACE_WITH_APPLICATION_SECRET", + "OMFConnectionName" : "PLACEHOLDER_REPLACE_WITH_OMF_CONNECTION_NAME", + "OMFConnectionDescription" : "PLACEHOLDER_REPLACE_WITH_OMF_CONNECTION_DESCRIPTION" } \ No newline at end of file diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 41c0ee7..777ea8a 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -46,15 +46,10 @@ jobs: pool: name: ${{ parameters.pool }} demands: ${{ parameters.containerDemands }} - variables: - - name: ClientId - value: $(OMFQuickStartClientId) - - name: ClientSecret - value: $(OMFQuickStartClientSecret) steps: - template: '/miscellaneous/build_templates/appsettings.yml@templates' parameters: - secrets: 'OMFQuickStartClientId, OMFQuickStartClientSecret, TopicName, SubscriptionName, TenantId, NamespaceId, Resource' + secrets: 'ClientId, ClientSecret, OMFConnectionName, TenantId, NamespaceId, Resource' - script: | echo Install pip diff --git a/images/OMF_FLOW.png b/images/OMF_FLOW.png new file mode 100644 index 0000000..63d99a5 Binary files /dev/null and b/images/OMF_FLOW.png differ diff --git a/images/topicsub.png b/images/topicsub.png deleted file mode 100644 index 510d6a9..0000000 Binary files a/images/topicsub.png and /dev/null differ diff --git a/quickstart.ipynb b/quickstart.ipynb index 2596309..7036f02 100644 --- a/quickstart.ipynb +++ b/quickstart.ipynb @@ -1,6 +1,7 @@ { "cells": [ { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -9,6 +10,7 @@ ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -22,6 +24,7 @@ ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -39,6 +42,7 @@ ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -47,7 +51,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -64,13 +68,13 @@ "clientSecret = appsettings['ClientSecret']\n", "tenantId = appsettings['TenantId']\n", "namespaceId = appsettings['NamespaceId']\n", - "topicName = appsettings['TopicName']\n", - "topicDescription = appsettings.get('TopicDescription', '')\n", - "subscriptionName = appsettings['SubscriptionName']\n", - "subscriptionDescription = appsettings.get('SubscriptionDescription', '')" + "apiversion = appsettings['ApiVersion']\n", + "OMFConnectionName = appsettings['OMFConnectionName']\n", + "OMFConnectionDescription = appsettings['OMFConnectionDescription']" ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -79,27 +83,15 @@ "\n", "An OMF Connection is made up of one or more Client Credential Clients, a Topic, and a Subscription. Data is sent to a Topic via a Client, where the data is buffered and made available for the Subscription. The Subscription relays data from the Topic to the Sequential Data Store in the namespace that the Subscription resides in. \n", "\n", - "The following diagram illustrates the flow of a namespace in SDS with a subscription receiving data from two topics which in turn are configured to different clients in AVEVA Data Hub\n", + "The following diagram illustrates the flow of a namespace in SDS with 2 OMF endpoints receiving data from different clients in AVEVA Data Hub\n", "\n", - "![Connection](images/topicsub.png)\n", + "![Connection](images/OMF_FLOW.png)\n", "\n", - "To understand how OMF connections are configured in AVEVA Data Hub it is good to first understand the basics of how client-credentials clients, topics, and subscriptions work. \n", + "To understand how OMF connections are configured in AVEVA Data Hub it is good to first understand the basics of how client-credentials clients. \n", "\n", "#### Client Credential Client\n", "Client-credentials clients are used for machine-to-machine communication without the presence of a user. These clients are issued an identifier and secret upon creation, which are later used for authentication against AVEVA Data Hub. More than one secret can be created for a client. Because they access resources on AVEVA Data Hub and are not associated to users, these clients can be assigned any of the roles in the tenant. \n", "\n", - "#### Topics\n", - "OMF topics aggregate OMF messages received from one or more clients and make them available for consumption by a subscription.\n", - "\n", - "A topic must contain at least one Client Id. Clients may be added to or removed from an existing topic. A given client may belong to multiple topics in separate namespaces.\n", - "\n", - "When you create a topic, OMF messages sent from the topic's associated clients are routed to a queue where they can be consumed by a subscription. This queue makes OMF messages available to subscriptions for up to seven days. While the OMF messages are in the topic queue, they are not available for retrieval via an API. They must first be consumed by a subscription and forwarded to a data store. The topic queue stores the OMF messages in the region of its namespace.\n", - "\n", - "#### Subscriptions\n", - "A subscription consumes OMF messages from a topic and forwards them to a data store. Multiple subscriptions can retrieve OMF messages from a single topic.\n", - "\n", - "A subscription can consume OMF messages from a topic in a different namespace; however, the topic's namespace must be in the same region as the subscription's namespace. OMF messages that the subscription is processing are temporarily stored in the region of its namespace. An OMF subscription retrieves OMF messages from a topic and writes them directly to a namespace in the Sequential Data Store. \n", - "\n", "## Creating OMF Connections\n", "\n", "OMF Connections only need to be created once for an application or system sending data to AVEVA Data Hub and there are two ways to do so, described below.\n", @@ -114,6 +106,7 @@ ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -124,11 +117,11 @@ "#### getToken\n", "This method shows how we can programmatically retrieve a bearer token used for authenticating against AVEVA Data Hub when making API calls. We first get the token endpoint from the publicly available identity endpoint, and then use that endpoint to make a POST call specifying our client id and secret to get a response including our token. This will by default expire after 3600 seconds and will allow us to make API calls carrying the privileges of the client specified. \n", "\n", - "#### createTopic\n", - "This method shows how to create a topic in AVEVA Data Hub by making a POST call to the /topics endpoint, attaching our bearer token as authentication header.\n", + "#### createOMFConnection\n", + "This method creates a new OMF Connection in Aveva Data Hub mapped to the specified ClientIds by making a POST call to the endpoint. A given ClientId may only be mapped to one OmfConnection per namespace.\n", "\n", - "#### createSubscription:\n", - "Similar to the above createTopic method, this makes a POST call to the /subscriptions endpoint.\n", + "#### checkOMFConnectionState\n", + "This method verifies the current state of the OMF connection in Aveva Data Hub. This method is used to make sure that the creation of the OMF connection is finished before proceeding with the next steps of the code.\n", "\n", "#### createType, createContainer, sendData\n", "These methods send OMF messages to the OMF endpoint of the specified namespace in order to create types, containers, or post data. Note the headers added to the specific methods; these are required when sending OMF messages to indicate how to process the sent message.\n", @@ -139,7 +132,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -170,29 +163,24 @@ " token = token['access_token']\n", " return token\n", "\n", - "def createTopic(topicJson):\n", + "def createOMFConnection(OMFConnectionJson):\n", " token = getToken()\n", " headers = {\"Authorization\": f'Bearer {token}', \"Content-Type\": \"application/json\"}\n", - " response = requests.post(f'{resource}/api/v1/Tenants/{tenantId}/Namespaces/{namespaceId}/Topics', \n", - " data=topicJson, \n", + " response = requests.post(f'{resource}/api/{apiversion}/Tenants/{tenantId}/Namespaces/{namespaceId}/OmfConnections', \n", + " data=OMFConnectionJson, \n", " headers=headers)\n", "\n", - " if response.status_code == 201:\n", + " if response.status_code == 202:\n", " return json.loads(response.text)[\"Id\"]\n", " else:\n", - " raise Exception(f'Failed to create topic with message: {response.text}, status code: {response.status_code}')\n", + " raise Exception(f'Failed to create OMF Connection with message: {response.text}, status code: {response.status_code}')\n", "\n", - "def createSubscription(subscriptionJson):\n", + "def checkOMFConnectionState(OMFConnectionIdRead):\n", " token = getToken()\n", " headers = {\"Authorization\": f'Bearer {token}', \"Content-Type\": \"application/json\"}\n", - " response = requests.post(f'{resource}/api/v1/Tenants/{tenantId}/Namespaces/{namespaceId}/Subscriptions', \n", - " data=subscriptionJson, \n", + " response = requests.get(f'{resource}/api/{apiversion}/Tenants/{tenantId}/Namespaces/{namespaceId}/OmfConnections/{OMFConnectionIdRead}', \n", " headers=headers)\n", - "\n", - " if response.status_code == 201:\n", - " return json.loads(response.text)[\"Id\"]\n", - " else:\n", - " raise Exception(f'Failed to create subscription with message: {response.text}, status code: {response.status_code}')\n", + " return json.loads(response.text)['State']\n", "\n", "def getSdsType(typeId):\n", " token = getToken()\n", @@ -200,7 +188,7 @@ " \"Authorization\": f'Bearer {token}', \n", " }\n", "\n", - " response = requests.get(f'{resource}/api/v1/Tenants/{tenantId}/Namespaces/{namespaceId}/Types/{typeId}', \n", + " response = requests.get(f'{resource}/api/{apiversion}/Tenants/{tenantId}/Namespaces/{namespaceId}/Types/{typeId}', \n", " headers=headers)\n", " print(json.loads(response.text))\n", " print()\n", @@ -211,7 +199,7 @@ " \"Authorization\": f'Bearer {token}', \n", " }\n", "\n", - " response = requests.get(f'{resource}/api/v1/Tenants/{tenantId}/Namespaces/{namespaceId}/Streams/{streamId}', \n", + " response = requests.get(f'{resource}/api/{apiversion}/Tenants/{tenantId}/Namespaces/{namespaceId}/Streams/{streamId}', \n", " headers=headers)\n", " print(json.loads(response.text))\n", " print()\n", @@ -222,7 +210,7 @@ " \"Authorization\": f'Bearer {token}', \n", " }\n", "\n", - " response = requests.get(f'{resource}/api/v1/Tenants/{tenantId}/Namespaces/{namespaceId}/Streams/{streamId}/Data/Last', \n", + " response = requests.get(f'{resource}/api/{apiversion}/Tenants/{tenantId}/Namespaces/{namespaceId}/Streams/{streamId}/Data/Last', \n", " headers=headers)\n", " print(json.loads(response.text))\n", " print()\n", @@ -238,7 +226,7 @@ " \"messagetype\": \"type\"\n", " }\n", "\n", - " response = requests.post(f'{resource}/api/v1/Tenants/{tenantId}/Namespaces/{namespaceId}/omf', \n", + " response = requests.post(f'{resource}/api/{apiversion}/Tenants/{tenantId}/Namespaces/{namespaceId}/omf', \n", " data=typeJson, \n", " headers=headers)\n", "\n", @@ -258,7 +246,7 @@ " \"messagetype\": \"container\"\n", " }\n", "\n", - " response = requests.post(f'{resource}/api/v1/Tenants/{tenantId}/Namespaces/{namespaceId}/omf', \n", + " response = requests.post(f'{resource}/api/{apiversion}/Tenants/{tenantId}/Namespaces/{namespaceId}/omf', \n", " data=containerJson, \n", " headers=headers)\n", "\n", @@ -278,7 +266,7 @@ " \"messagetype\": \"data\"\n", " }\n", "\n", - " response = requests.post(f'{resource}/api/v1/Tenants/{tenantId}/Namespaces/{namespaceId}/omf', \n", + " response = requests.post(f'{resource}/api/{apiversion}/Tenants/{tenantId}/Namespaces/{namespaceId}/omf', \n", " data=dataJson, \n", " headers=headers)\n", "\n", @@ -290,11 +278,8 @@ "def cleanup():\n", " token = getToken()\n", " headers = {\"Authorization\": f'Bearer {token}'}\n", - "\n", - " print(f'Deleting Topic {topicId} and Subscription {subscriptionId}')\n", - " requests.delete(f'{resource}/api/v1/Tenants/{tenantId}/Namespaces/{namespaceId}/Subscriptions/{subscriptionId}', \n", - " headers=headers)\n", - " requests.delete(f'{resource}/api/v1/Tenants/{tenantId}/Namespaces/{namespaceId}/Topics/{topicId}', \n", + " print(f'Deleting OMFConnection {OMFConnectionId}')\n", + " requests.delete(f'{resource}/api/{apiversion}/Tenants/{tenantId}/Namespaces/{namespaceId}/OmfConnections/{OMFConnectionId}', \n", " headers=headers)\n", "\n", "def test_finished():\n", @@ -305,44 +290,51 @@ ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Creating our OMF Connection\n", "\n", - "Using the methods defined in the code block above, we will create a topic and a subscription that will form our OMF connection" + "Using the methods defined in the code block above, we will create an OMF connection" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Created OMF Connection Id: ebfd2a25-2694-49cb-9d33-f896c18f4370\n" + ] + } + ], "source": [ - "# Create the Topic\n", - "topic = {\n", + "# Create the OMF Connection\n", + "OMFConnection = {\n", + " \"Name\": OMFConnectionName,\n", + " \"Description\": OMFConnectionDescription,\n", " \"ClientIds\": [\n", " clientId\n", " ],\n", - " \"Name\": topicName,\n", - " \"Description\": topicDescription\n", - "}\n", - "topicId = createTopic(json.dumps(topic))\n", - "print(f'Created Topic Id: {topicId}')\n", - "\n", - "# Create the Subscription\n", - "subscription = {\n", - " \"Name\": subscriptionName,\n", - " \"Description\": subscriptionDescription,\n", - " \"TopicId\": topicId,\n", - " \"TopicTenantId\": tenantId,\n", - " \"TopicNamespaceId\": namespaceId\n", "}\n", - "subscriptionId = createSubscription(json.dumps(subscription))\n", - "print(f'Created Subscription Id: {subscriptionId}')" + "\n", + "OMFConnectionId = createOMFConnection(json.dumps(OMFConnection))\n", + "\n", + "#query ADH to get the status of the OMF connection creation\n", + "while checkOMFConnectionState(OMFConnectionId) != 'Active':\n", + " print(f'Creating OMF Connection in Aveva Data Hub')\n", + " time.sleep(5)\n", + "\n", + "\n", + "print(f'Created OMF Connection Id: {OMFConnectionId}')\n" ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -351,6 +343,7 @@ ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -358,6 +351,7 @@ ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -365,6 +359,7 @@ ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -378,10 +373,11 @@ "\n", "* All properties within a type should be inter-dependent, where the properties are used together to make sense of the data. For example, we will be creating a type for drone location, where the properties will be Timestamp, Longitude, Latitude, and Altitude. Removing or omitting one of these properties would result in the type being incomplete, and unable to store the drone’s location properly. Also, adding a property to this Type, such as the battery measurement, is not advised, because it is not a dependent property, and we may want to report this value at a different frequency. Missing a property at an event causes a default value to be recorded which will either misrepresent or break interpolation of that property around that event key.\n", "\n", - "For more information on types, see https://docs.osisoft.com/bundle/omf/page/types/type-messages.html" + "For more information on types, see https://docs.aveva.com/bundle/omf/page/types/type-messages.html" ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -423,9 +419,17 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Request sent with status code 202, operation-id = 6c5b9ba3b781934c9822597d14f1cf1e\n" + ] + } + ], "source": [ "droneTypes = [{\n", " \"id\": \"DroneLocationType\",\n", @@ -452,6 +456,7 @@ ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -465,6 +470,7 @@ ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -473,11 +479,12 @@ "\n", "Immediately after a type has been registered using a type message, containers may be created using that type. Each container represents an instance of the OMF type, meaning that for this example, if we had 100 drones we would create one container for each drone's location, where every container would be of type DroneLocationType.\n", "\n", - "For more information on containers, see https://docs.osisoft.com/bundle/omf/page/containers/container-messages.html\n", + "For more information on containers, see https://docs.aveva.com/bundle/omf/page/containers/container-messages.html\n", "\n" ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -512,9 +519,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Request sent with status code 202, operation-id = 81e964db5ae606fcb1bbdc1c698eb487\n", + "Request sent with status code 202, operation-id = bbbc257c645c30df48ab059334f6c0f4\n" + ] + } + ], "source": [ "locationAndBatteryContainer = [{\n", " \"id\": \"DroneLocationContainer\",\n", @@ -536,6 +552,7 @@ ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -548,9 +565,17 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Request sent with status code 202, operation-id = b321904856db99407292875189df430a\n" + ] + } + ], "source": [ "data = [{\n", " \"containerid\": \"DroneTemperatureContainer\",\n", @@ -608,6 +633,7 @@ ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -621,6 +647,7 @@ ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -636,9 +663,40 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "DroneLocationType SdsType = \n", + "{'Id': 'DroneLocationType', 'Name': 'DroneLocationType', 'Description': '', 'SdsTypeCode': 1, 'Properties': [{'Id': 'Timestamp', 'Name': 'Timestamp', 'Description': '', 'IsKey': True, 'SdsType': {'Id': 'DroneLocationType.Timestamp', 'Name': 'DroneLocationType.Timestamp', 'Description': '', 'SdsTypeCode': 16}}, {'Id': 'Long', 'Name': 'Long', 'Description': '', 'SdsType': {'Id': 'DroneLocationType.Long', 'Name': 'DroneLocationType.Long', 'Description': '', 'SdsTypeCode': 13}}, {'Id': 'Lat', 'Name': 'Lat', 'Description': '', 'SdsType': {'Id': 'DroneLocationType.Lat', 'Name': 'DroneLocationType.Lat', 'Description': '', 'SdsTypeCode': 13}}, {'Id': 'Alt', 'Name': 'Alt', 'Description': '', 'SdsType': {'Id': 'DroneLocationType.Alt', 'Name': 'DroneLocationType.Alt', 'Description': '', 'SdsTypeCode': 13}}]}\n", + "\n", + "DroneDataType SdsType = \n", + "{'Id': 'DroneDataType', 'Name': 'DroneDataType', 'Description': '', 'SdsTypeCode': 1, 'Properties': [{'Id': 'Timestamp', 'Name': 'Timestamp', 'Description': '', 'IsKey': True, 'SdsType': {'Id': 'DroneDataType.Timestamp', 'Name': 'DroneDataType.Timestamp', 'Description': '', 'SdsTypeCode': 16}}, {'Id': 'Value', 'Name': 'Value', 'Description': '', 'SdsType': {'Id': 'DroneDataType.Value', 'Name': 'DroneDataType.Value', 'Description': '', 'SdsTypeCode': 13}}]}\n", + "\n", + "DroneLocationContainer SdsStream = \n", + "{'TypeId': 'DroneLocationType', 'Id': 'DroneLocationContainer', 'Name': 'DroneLocationContainer', 'Description': ''}\n", + "\n", + "DroneBatteryContainer SdsStream = \n", + "{'TypeId': 'DroneDataType', 'Id': 'DroneBatteryContainer', 'Name': 'DroneBatteryContainer', 'Description': ''}\n", + "\n", + "DroneTemperatureContainer SdsStream = \n", + "{'TypeId': 'DroneDataType', 'Id': 'DroneTemperatureContainer', 'Name': 'DroneTemperatureContainer', 'Description': ''}\n", + "\n", + "DroneLocationContainer Data = \n", + "{'Timestamp': '2021-12-11T22:25:23.43Z', 'Long': -77.0364, 'Lat': 38.8951, 'Alt': 230}\n", + "\n", + "DroneBatteryContainer Data = \n", + "{'Timestamp': '2021-12-11T22:25:23.43Z', 'Value': 23}\n", + "\n", + "DroneTemperatureContainer Data = \n", + "{'Timestamp': '2021-12-11T22:25:23.43Z', 'Value': 99.3}\n", + "\n" + ] + } + ], "source": [ "print('DroneLocationType SdsType = ')\n", "getSdsType('DroneLocationType')\n", @@ -670,14 +728,23 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Deleting OMFConnection ebfd2a25-2694-49cb-9d33-f896c18f4370\n" + ] + } + ], "source": [ "test_finished()" ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -704,7 +771,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.1" + "version": "3.10.11" } }, "nbformat": 4,