diff --git a/.doc_gen/metadata/iot_sitewise_metadata.yaml b/.doc_gen/metadata/iot_sitewise_metadata.yaml new file mode 100644 index 00000000000..3aa344b21a1 --- /dev/null +++ b/.doc_gen/metadata/iot_sitewise_metadata.yaml @@ -0,0 +1,204 @@ +# zexi 0.4.0 +iotsitewise_Hello: + title: Hello &ITSWlong; + title_abbrev: Hello &ITSW; + synopsis: get started using &ITSW;. + category: Hello + languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/iotsitewise + sdkguide: + excerpts: + - description: + snippet_tags: + - iotsitewise.hello.main + services: + iotsitewise: {ListVersions} +iotsitewise_DescribeGateway: + languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/iotsitewise + excerpts: + - description: + snippet_tags: + - sitewise.java2.describe.gateway.main + services: + iotsitewise: {DescribeGateway} +iotsitewise_DeleteGateway: + languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/iotsitewise + excerpts: + - description: + snippet_tags: + - sitewise.java2.delete.gateway.main + services: + iotsitewise: {DeleteGateway} +iotsitewise_CreateGateway: + languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/iotsitewise + excerpts: + - description: + snippet_tags: + - sitewise.java2.create.gateway.main + services: + iotsitewise: {CreateGateway} +iotsitewise_DescribePortal: + languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/iotsitewise + excerpts: + - description: + snippet_tags: + - sitewise.java2.describe.portal.main + services: + iotsitewise: {DescribePortal} +iotsitewise_ListAssetModels: + languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/iotsitewise + excerpts: + - description: + snippet_tags: + - sitewise.java2.list.asset.model.main + services: + iotsitewise: {ListAssetModels} +iotsitewise_DeletePortal: + languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/iotsitewise + excerpts: + - description: + snippet_tags: + - sitewise.java2.delete.portal.main + services: + iotsitewise: {DeletePortal} +iotsitewise_CreatePortal: + languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/iotsitewise + excerpts: + - description: + snippet_tags: + - sitewise.java2.create.portal.main + services: + iotsitewise: {CreatePortal} +iotsitewise_DeleteAssetModel: + languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/iotsitewise + excerpts: + - description: + snippet_tags: + - sitewise.java2.delete.asset.model.main + services: + iotsitewise: {DeleteAssetModel} +iotsitewise_DeleteAsset: + languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/iotsitewise + excerpts: + - description: + snippet_tags: + - sitewise.java2.delete.asset.main + services: + iotsitewise: {DeleteAsset} +iotsitewise_DescribeAssetModel: + languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/iotsitewise + excerpts: + - description: + snippet_tags: + - sitewise.java2.describe.asset.model.main + services: + iotsitewise: {DescribeAssetModel} +iotsitewise_GetAssetPropertyValue: + languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/iotsitewise + excerpts: + - description: + snippet_tags: + - sitewise.java2_get_property.main + services: + iotsitewise: {GetAssetPropertyValue} +iotsitewise_BatchPutAssetPropertyValue: + languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/iotsitewise + excerpts: + - description: + snippet_tags: + - sitewise.java2_put_batch_property.main + services: + iotsitewise: {BatchPutAssetPropertyValue} +iotsitewise_CreateAsset: + languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/iotsitewise + excerpts: + - description: + snippet_tags: + - sitewise.java2_create_asset.main + services: + iotsitewise: {CreateAsset} +iotsitewise_CreateAssetModel: + languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/iotsitewise + excerpts: + - description: + snippet_tags: + - sitewise.java2_create_asset_model.main + services: + iotsitewise: {CreateAssetModel} +iotsitewise_Scenario: + synopsis: Learn core operations for &ITSWlong; using an &AWS; SDK. + category: Basics + languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/iotsitewise + sdkguide: + excerpts: + - description: Run an interactive scenario demonstrating &ITSW; features. + snippet_tags: + - iotsitewise.java2.scenario.main + - description: A wrapper class for &ITSW; SDK methods. + snippet_tags: + - iotsitewise.java2.actions.main + services: + iotsitewise: {} diff --git a/basics_scenarios/sitewise_scenario/README.md b/basics_scenarios/sitewise_scenario/README.md new file mode 100644 index 00000000000..9df7e209432 --- /dev/null +++ b/basics_scenarios/sitewise_scenario/README.md @@ -0,0 +1,39 @@ +## Overview +This AWS IoT SiteWise Service basic scenario demonstrates how to interact with the AWS IoT SiteWise service using an AWS SDK. The scenario covers various operations such as creating an Asset Model, creating assets, sending data to assets, and retrieving data. + +## Key Operations + +1. **Create an AWS SiteWise Asset Model**: + - This step creates an AWS SiteWise Asset Model by invoking the `createAssetModel` method. + +2. **Create an AWS IoT SiteWise Asset**: + - This operation creates an AWS SiteWise asset. + +3. **Retrieve the property ID values**: + - To send data to an asset, we need to get the property ID values for the model properties. This scenario uses temperature and humidity properties. + +4. **Send data to an AWS IoT SiteWise Asset**: + - This operation sends data to an IoT SiteWise Asset. + +5. **Retrieve the value of the IoT SiteWise Asset property**: + - This operation gets data from the asset. + +**Note** See the Eng spec for a full listing of operations. + +## Resources + +This Basics scenario requires an IAM role that has permissions to work with the AWS IoT SiteWise service. The scenario creates this resource using a CloudFormation template. + +## Implementations + +This scenario example will be implemented in the following languages: + +- Java +- Python +- JavaScript + +## Additional Reading + +- [AWS IoT SiteWise Documentation](https://docs.aws.amazon.com/iot-sitewise/latest/userguide/what-is-sitewise.html) + +Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 diff --git a/basics_scenarios/sitewise_scenario/SPECIFICATION.md b/basics_scenarios/sitewise_scenario/SPECIFICATION.md new file mode 100644 index 00000000000..71208af7df0 --- /dev/null +++ b/basics_scenarios/sitewise_scenario/SPECIFICATION.md @@ -0,0 +1,334 @@ +# AWS IoT SiteWise Service Scenario Specification + +## Overview +This SDK Basics scenario demonstrates how to interact with AWS IoT SiteWise using an AWS SDK. +It demonstrates various tasks such as creating a SiteWise Asset Model, creating an asset, +sending data to the asset, reading data, and so on. Finally this scenario demonstrates how +to clean up resources. Its purpose is to demonstrate how to get up and running with +AWS IoT SiteWise and an AWS SDK. + +## Resources +This Basics scenario requires an IAM role that has permissions to work with AWS IoT +SiteWise service. The scenario creates this resource using a CloudFormation template. + +## Hello AWS IoT SiteWise +This program is intended for users not familiar with the AWS IoT SiteWise Service to easily get up and running. The program uses a `listAssetModelsPaginator` to demonstrate how you can read through Asset Model information. + +## Basics Scenario Program Flow +The AWS IoT SiteWise Basics scenario executes the following operations. + +1. **Create an AWS SiteWise Asset Model**: + - Description: This step creates an AWS SiteWise Asset Model by invoking the `createAssetModel` method. + - Exception Handling: Check to see if a `ResourceAlreadyExistsException` is thrown. + If it is thrown, display the asset model ID and move on. + +2. **Create an AWS IoT SiteWise Asset**: + - Description: This operation creates an AWS SiteWise asset. + - The method `createAsset` is called to obtain the asset ID. + - Exception Handling: Check to see if a `ResourceNotFoundException` is thrown if the asset model id provided in the request is invalid. If so, + display the message and end the program. + +3. **Retrieve the property ID values**: + - Description: To send data to an asset, we need to get the property ID values for the + the model properties. This scenario uses temperature and humidity properties. + - The method `listAssetModelProperties` is called to retrieve the property ID values. + - Exception Handling: Check to see if an `IoTSiteWiseException` is thrown. There are not + many other useful exceptions for this specific call. If so, display the message and end the program. + +4. **Send data to an AWS IoT SiteWise Asset**: + - Description: This operation sends data to an IoT SiteWise Asset. + - This step uses the method `batchPutAssetPropertyValue`. + - Exception Handling: Check to see if a `ResourceNotFoundException` is thrown. If so, display the message and end the program. + +5. **Retrieve the value of the IoT SiteWise Asset property**: + - Description: This operation gets data from the asset. + - This step uses the method `getAssetPropertyValue`. + - Exception Handling: Check to see if a `ResourceNotFoundException` is thrown. If so, display the message and end the program. + +6. **Create an IoT SiteWise Portal**: + - Description: This operation creates an IoT SiteWise portal. + - The method `createPortal` is called. + - Exception Handling: Check to see if an `IoTSiteWiseException` is thrown. If so, display the message and end the program. + +7. **Describe the Portal**: + - Description: This operation describes the portal and returns a URL for the portal. + - The method `describePortal` is called and returns the URL. + - Exception Handling: Check to see if a `ResourceNotFoundException` is thrown. If so, display the message and end the program. + +8. **Create an IoT SiteWise Gateway**: + - Description: This operation creates an IoT SiteWise Gateway. + - The method `createGateway` is called. + - Exception Handling: Check to see if an `IoTSiteWiseException` is thrown. If so, display the message and end the program. + +9. **Describe the IoT SiteWise Gateway**: + - Description: This operation describes the Gateway. + - The method `describeGateway` is called. + - Exception Handling: Check to see if a `ResourceNotFoundException` is thrown. If so, display the message and end the program. + +10. **Delete the AWS IoT SiteWise Assets**: + - The `delete` methods are called to clean up the resources. + - Exception Handling: Check to see if a `ResourceNotFoundException` is thrown. If so, display the message and end the program." + +### Program execution +The following shows the output of the AWS IoT SiteWise Basics scenario in the console. + +``` +AWS IoT SiteWise is a fully managed industrial software-as-a-service (SaaS) that +makes it easy to collect, store, organize, and monitor data from industrial equipment and processes. +It is designed to help industrial and manufacturing organizations collect data from their equipment and +processes, and use that data to make informed decisions about their operations. + +One of the key features of AWS IoT SiteWise is its ability to connect to a wide range of industrial +equipment and systems, including programmable logic controllers (PLCs), sensors, and other +industrial devices. It can collect data from these devices and organize it into a unified data model, +making it easier to analyze and gain insights from the data. AWS IoT SiteWise also provides tools for +visualizing the data, setting up alarms and alerts, and generating reports. + +Another key feature of AWS IoT SiteWise is its ability to scale to handle large volumes of data. +It can collect and store data from thousands of devices and process millions of data points per second, +making it suitable for large-scale industrial operations. Additionally, AWS IoT SiteWise is designed +to be secure and compliant, with features like role-based access controls, data encryption, +and integration with other AWS services for additional security and compliance features. + +Let's get started... + + +Enter 'c' followed by to continue: +c +Continuing with the program... + +-------------------------------------------------------------------------------- +Use AWS CloudFormation to create an IAM role that are required for this scenario. +Stack creation requested, ARN is arn:aws:cloudformation:us-east-1:814548047983:stack/RoleSitewise/29f480c0-75fd-11ef-a42e-12cd4e534049 +Stack created successfully +-------------------------------------------------------------------------------- +1. Create an AWS SiteWise Asset Model + An AWS IoT SiteWise Asset Model is a way to represent the physical assets, such as equipment, + processes, and systems, that exist in an industrial environment. This model provides a structured and + hierarchical representation of these assets, allowing users to define the relationships and properties + of each asset. + + +Enter 'c' followed by to continue: +c +Continuing with the program... + +The Asset Model MyAssetModel already exists. The id of the existing model is ffbc475b-73ad-4eb6-bf28-8728818fa8ef. Moving on... + +Enter 'c' followed by to continue: +c +Continuing with the program... + +-------------------------------------------------------------------------------- +2. Create an AWS IoT SiteWise Asset + The IoT SiteWise model defines the structure and metadata for your physical assets. Now we + can use the asset model to create the asset. + + + +Enter 'c' followed by to continue: +c +Continuing with the program... + +Asset created with ID: 4d681624-a303-46dd-8830-6189790ae915 + +Enter 'c' followed by to continue: +c +Continuing with the program... + +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +3. Retrieve the property ID values + To send data to an asset, we need to get the property ID values for the + Temperature and Humidity properties. + + +Enter 'c' followed by to continue: +c +Continuing with the program... + +The Humidity property Id is feb4aba6-55f9-4b00-b366-27b9d7e5a747 +The Temperature property Id is 6cb505aa-6bcc-46f4-a12a-7ca5df8eb028 + +Enter 'c' followed by to continue: +c +Continuing with the program... + +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +4. Send data to an AWS IoT SiteWise Asset +By sending data to an IoT SiteWise Asset, you can aggregate data from +multiple sources, normalize the data into a standard format, and store it in a +centralized location. This makes it easier to analyze and gain insights from the data. + +This example demonstrate how to generate sample data and ingest it into the AWS IoT SiteWise asset. + + + +Enter 'c' followed by to continue: +c +Continuing with the program... + +Data sent successfully. + +Enter 'c' followed by to continue: +c +Continuing with the program... + +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +5. Retrieve the value of the IoT SiteWise Asset property +IoT SiteWise is an AWS service that allows you to collect, process, and analyze industrial data +from connected equipment and sensors. One of the key benefits of reading an IoT SiteWise property +is the ability to gain valuable insights from your industrial data. + + + +Enter 'c' followed by to continue: +c +Continuing with the program... + +The property name is: Temperature property +The value of this property is 23.5 + +Enter 'c' followed by to continue: +c +Continuing with the program... + +The property name is: Humidity property +The value of this property is 65.0 + +Enter 'c' followed by to continue: +c +Continuing with the program... + +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +6. Create an IoT SiteWise Portal + An IoT SiteWise Portal allows you to aggregate data from multiple industrial sources, + such as sensors, equipment, and control systems, into a centralized platform. + + +Enter 'c' followed by to continue: +c +Continuing with the program... + +Portal created successfully. Portal ID 63e65729-b7a1-410a-aa36-94145fe92153 +The portal Id is 63e65729-b7a1-410a-aa36-94145fe92153 + +Enter 'c' followed by to continue: +c +Continuing with the program... + +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +7. Describe the Portal + In this step, we will describe the step and provide the portal URL. + + +Enter 'c' followed by to continue: +c +Continuing with the program... + +Portal URL: https://p-fy9qnrqy.app.iotsitewise.aws + +Enter 'c' followed by to continue: +c +Continuing with the program... + +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +8. Create an IoTSitewise Gateway +IoTSitewise Gateway serves as the bridge between industrial equipment, sensors, and the +cloud-based IoTSitewise service. It is responsible for securely collecting, processing, and +transmitting data from various industrial assets to the IoTSitewise platform, +enabling real-time monitoring, analysis, and optimization of industrial operations. + + + +Enter 'c' followed by to continue: +c +Continuing with the program... + +The ARN of the gateway is arn:aws:iotsitewise:us-east-1:814548047983:gateway/50320670-1d88-4a7e-9013-1d7e8a3af832 +Gateway creation completed successfully. id is 50320670-1d88-4a7e-9013-1d7e8a3af832 +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +9. Describe the IoTSitewise Gateway + +Enter 'c' followed by to continue: +c +Continuing with the program... + +Gateway Name: myGateway11 +Gateway ARN: arn:aws:iotsitewise:us-east-1:814548047983:gateway/50320670-1d88-4a7e-9013-1d7e8a3af832 +Gateway Platform: GatewayPlatform(GreengrassV2=GreengrassV2(CoreDeviceThingName=myThing78)) +Gateway Creation Date: 2024-09-18T20:34:13.117Z +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +10. Delete the AWS IoT SiteWise Assets +Before you can delete the Asset Model, you must delete the assets. + + +Would you like to delete the IoT Sitewise Assets? (y/n) +y +You selected to delete the Sitewise assets. + +Enter 'c' followed by to continue: +c +Continuing with the program... + +Portal 63e65729-b7a1-410a-aa36-94145fe92153 was deleted successfully. +An unexpected error occurred: Cannot invoke "java.util.concurrent.CompletableFuture.join()" because "future" is null +Asset deleted successfully. +Lets wait 1 min for the asset to be deleted +01:00The Gateway was deleted successfully +00:00Countdown complete! + +Enter 'c' followed by to continue: +c +Continuing with the program... + +Delete the AWS IoT SiteWise Asset Model +Asset model deleted successfully. + +Enter 'c' followed by to continue: +c +Continuing with the program... + +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +Delete stack requested .... +Stack deleted successfully. +This concludes the AWS SiteWise Scenario +-------------------------------------------------------------------------------- + + +``` + +## SOS Tags + +The following table describes the metadata used in this Basics Scenario. + + +| action | metadata file | metadata key | +|--------------------------------|-----------------------------------|---------------------------------------- | +| `describeGateway` | iot_sitewise_metadata.yaml | iotsitewise_DescribeGateway | +| `deleteGateway ` | iot_sitewise_metadata.yaml | iotsitewise_DeleteGateway | +| `createGateway ` | iot_sitewise_metadata.yaml | iotsitewise_CreateGateway | +| `describePortal` | iot_sitewise_metadata.yaml | iotsitewise_DescribePortal | +| `listAssetModels` | iot_sitewise_metadata.yaml | iotsitewise_ListAssetModels | +| `deletePortal` | iot_sitewise_metadata.yaml | iotsitewise_DeletePortal | +| `createPortal` | iot_sitewise_metadata.yaml | iotsitewise_CreatePortal | +| `deleteAssetModel` | iot_sitewise_metadata.yaml | iotsitewise_DeleteAssetModel | +| `deleteAsset` | iot_sitewise_metadata.yaml | iotsitewise_DeleteAsset | +| `describeAssetModel` | iot_sitewise_metadata.yaml | iotsitewise_DescribeAssetModel | +| `getAssetPropertyValue` | iot_sitewise_metadata.yaml | iotsitewise_GetAssetPropertyValue | +| `batchPutAssetPropertyValue` | iot_sitewise_metadata.yaml | iotsitewise_BatchPutAssetPropertyValue | +| `createAsset` | iot_sitewise_metadata.yaml | iotsitewise_CreateAsset | +| `createAssetModel ` | iot_sitewise_metadata.yaml | iotsitewise_CreateAssetModel | +| `scenario` | iot_sitewise_metadata.yaml | iotsitewise_Scenario | +| `hello` | iot_sitewise_metadata.yaml | iotsitewise_Hello | + + + diff --git a/javav2/example_code/iotsitewise/README.md b/javav2/example_code/iotsitewise/README.md new file mode 100644 index 00000000000..71c24b6f9b5 --- /dev/null +++ b/javav2/example_code/iotsitewise/README.md @@ -0,0 +1,119 @@ +# AWS IoT SiteWise code examples for the SDK for Java 2.x + +## Overview + +Shows how to use the AWS SDK for Java 2.x to work with AWS IoT SiteWise. + + + + +_AWS IoT SiteWise _ + +## ⚠ Important + +* Running this code might result in charges to your AWS account. For more details, see [AWS Pricing](https://aws.amazon.com/pricing/) and [Free Tier](https://aws.amazon.com/free/). +* Running the tests might result in charges to your AWS account. +* We recommend that you grant your code least privilege. At most, grant only the minimum permissions required to perform the task. For more information, see [Grant least privilege](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege). +* This code is not tested in every AWS Region. For more information, see [AWS Regional Services](https://aws.amazon.com/about-aws/global-infrastructure/regional-product-services). + + + + +## Code examples + +### Prerequisites + +For prerequisites, see the [README](../../README.md#Prerequisites) in the `javav2` folder. + + + + + +### Get started + +- [Hello AWS IoT SiteWise](src/main/java/com/example/iotsitewise/HelloSitewise.java#L14) (`ListVersions`) + + +### Basics + +Code examples that show you how to perform the essential operations within a service. + +- [Learn the basics](src/main/java/com/example/iotsitewise/scenario/SitewiseScenario.java) + + +### Single actions + +Code excerpts that show you how to call individual service functions. + +- [BatchPutAssetPropertyValue](src/main/java/com/example/iotsitewise/scenario/SitewiseActions.java#L169) +- [CreateAsset](src/main/java/com/example/iotsitewise/scenario/SitewiseActions.java#L138) +- [CreateAssetModel](src/main/java/com/example/iotsitewise/scenario/SitewiseActions.java#L88) +- [CreateGateway](src/main/java/com/example/iotsitewise/scenario/SitewiseActions.java#L468) +- [CreatePortal](src/main/java/com/example/iotsitewise/scenario/SitewiseActions.java#L347) +- [DeleteAsset](src/main/java/com/example/iotsitewise/scenario/SitewiseActions.java#L293) +- [DeleteAssetModel](src/main/java/com/example/iotsitewise/scenario/SitewiseActions.java#L320) +- [DeleteGateway](src/main/java/com/example/iotsitewise/scenario/SitewiseActions.java#L513) +- [DeletePortal](src/main/java/com/example/iotsitewise/scenario/SitewiseActions.java#L381) +- [DescribeAssetModel](src/main/java/com/example/iotsitewise/scenario/SitewiseActions.java#L264) +- [DescribeGateway](src/main/java/com/example/iotsitewise/scenario/SitewiseActions.java#L540) +- [DescribePortal](src/main/java/com/example/iotsitewise/scenario/SitewiseActions.java#L439) +- [GetAssetPropertyValue](src/main/java/com/example/iotsitewise/scenario/SitewiseActions.java#L233) +- [ListAssetModels](src/main/java/com/example/iotsitewise/scenario/SitewiseActions.java#L408) + + + + + +## Run the examples + +### Instructions + + + + + +#### Hello AWS IoT SiteWise + +This example shows you how to get started using AWS IoT SiteWise. + + +#### Learn the basics + +This example shows you how to Learn core operations for AWS IoT SiteWise using an AWS SDK. + + + + + + + + + + +### Tests + +⚠ Running tests might result in charges to your AWS account. + + +To find instructions for running these tests, see the [README](../../README.md#Tests) +in the `javav2` folder. + + + + + + +## Additional resources + +- [AWS IoT SiteWise Developer Guide](https://docs.aws.amazon.com/iot-sitewise/latest/userguide/what-is-sitewise.html) +- [AWS IoT SiteWise API Reference](https://docs.aws.amazon.com/iot-sitewise/latest/APIReference/Welcome.html) +- [SDK for Java 2.x AWS IoT SiteWise reference](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iotsitewise/package-summary.html) + + + + +--- + +Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 \ No newline at end of file diff --git a/javav2/example_code/iotsitewise/pom.xml b/javav2/example_code/iotsitewise/pom.xml new file mode 100644 index 00000000000..0efee79a8ad --- /dev/null +++ b/javav2/example_code/iotsitewise/pom.xml @@ -0,0 +1,127 @@ + + + 4.0.0 + + org.example + iotsitewise + 1.0-SNAPSHOT + + + 17 + 17 + UTF-8 + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.1 + + IntegrationTest + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + ${java.version} + ${java.version} + + + + + + + + software.amazon.awssdk + bom + 2.27.22 + pom + import + + + org.apache.logging.log4j + log4j-bom + 2.23.1 + pom + import + + + + + + org.junit.jupiter + junit-jupiter-api + 5.9.2 + test + + + org.junit.jupiter + junit-jupiter-engine + 5.9.2 + test + + + software.amazon.awssdk + secretsmanager + + + software.amazon.awssdk + netty-nio-client + + + org.junit.platform + junit-platform-commons + 1.9.2 + + + software.amazon.awssdk + iotsitewise + + + com.google.code.gson + gson + 2.10.1 + + + org.junit.platform + junit-platform-launcher + 1.9.2 + test + + + software.amazon.awssdk + sso + + + software.amazon.awssdk + ssooidc + + + org.apache.logging.log4j + log4j-core + + + org.slf4j + slf4j-api + 2.0.13 + + + software.amazon.awssdk + cloudformation + + + org.apache.logging.log4j + log4j-slf4j2-impl + + + org.apache.logging.log4j + log4j-1.2-api + + + \ No newline at end of file diff --git a/javav2/example_code/iotsitewise/src/main/java/com/example/iotsitewise/HelloSitewise.java b/javav2/example_code/iotsitewise/src/main/java/com/example/iotsitewise/HelloSitewise.java new file mode 100644 index 00000000000..7e6ee563da5 --- /dev/null +++ b/javav2/example_code/iotsitewise/src/main/java/com/example/iotsitewise/HelloSitewise.java @@ -0,0 +1,42 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package com.example.iotsitewise; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.awssdk.services.iotsitewise.IoTSiteWiseAsyncClient; +import software.amazon.awssdk.services.iotsitewise.model.AssetModelType; +import software.amazon.awssdk.services.iotsitewise.model.ListAssetModelsRequest; +import software.amazon.awssdk.services.iotsitewise.paginators.ListAssetModelsPublisher; +import java.util.concurrent.CompletableFuture; + +// snippet-start:[iotsitewise.hello.main] +public class HelloSitewise { + private static final Logger logger = LoggerFactory.getLogger(HelloSitewise.class); + public static void main(String[] args) { + fetchAssetModels(); + } + + /** + * Fetches asset models using the provided {@link IoTSiteWiseAsyncClient}. + */ + public static void fetchAssetModels() { + IoTSiteWiseAsyncClient siteWiseAsyncClient = IoTSiteWiseAsyncClient.create(); + ListAssetModelsRequest assetModelsRequest = ListAssetModelsRequest.builder() + .assetModelTypes(AssetModelType.ASSET_MODEL) + .build(); + + // Asynchronous paginator - process paginated results. + ListAssetModelsPublisher listModelsPaginator = siteWiseAsyncClient.listAssetModelsPaginator(assetModelsRequest); + CompletableFuture future = listModelsPaginator.subscribe(response -> { + response.assetModelSummaries().forEach(assetSummary -> + logger.info("Asset Model Name: {} ", assetSummary.name()) + ); + }); + + // Wait for the asynchronous operation to complete + future.join(); + } +} +// snippet-end:[iotsitewise.hello.main] \ No newline at end of file diff --git a/javav2/example_code/iotsitewise/src/main/java/com/example/iotsitewise/scenario/CloudFormationHelper.java b/javav2/example_code/iotsitewise/src/main/java/com/example/iotsitewise/scenario/CloudFormationHelper.java new file mode 100644 index 00000000000..1d97c226f18 --- /dev/null +++ b/javav2/example_code/iotsitewise/src/main/java/com/example/iotsitewise/scenario/CloudFormationHelper.java @@ -0,0 +1,162 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package com.example.iotsitewise.scenario; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; +import software.amazon.awssdk.core.retry.RetryMode; +import software.amazon.awssdk.http.async.SdkAsyncHttpClient; +import software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClient; +import software.amazon.awssdk.services.cloudformation.CloudFormationAsyncClient; +import software.amazon.awssdk.services.cloudformation.model.Capability; +import software.amazon.awssdk.services.cloudformation.model.CloudFormationException; +import software.amazon.awssdk.services.cloudformation.model.DescribeStacksRequest; +import software.amazon.awssdk.services.cloudformation.model.DescribeStacksResponse; +import software.amazon.awssdk.services.cloudformation.model.Output; +import software.amazon.awssdk.services.cloudformation.model.Stack; +import software.amazon.awssdk.services.cloudformation.waiters.CloudFormationAsyncWaiter; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.Duration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +public class CloudFormationHelper { + private static final String CFN_TEMPLATE = "SitewiseRoles-template.yaml"; + private static final Logger logger = LoggerFactory.getLogger(CloudFormationHelper.class); + + private static CloudFormationAsyncClient cloudFormationClient; + + private static CloudFormationAsyncClient getCloudFormationClient() { + if (cloudFormationClient == null) { + SdkAsyncHttpClient httpClient = NettyNioAsyncHttpClient.builder() + .maxConcurrency(100) + .connectionTimeout(Duration.ofSeconds(60)) + .readTimeout(Duration.ofSeconds(60)) + .writeTimeout(Duration.ofSeconds(60)) + .build(); + + ClientOverrideConfiguration overrideConfig = ClientOverrideConfiguration.builder() + .apiCallTimeout(Duration.ofMinutes(2)) + .apiCallAttemptTimeout(Duration.ofSeconds(90)) + .retryStrategy(RetryMode.STANDARD) + .build(); + + cloudFormationClient = CloudFormationAsyncClient.builder() + .httpClient(httpClient) + .overrideConfiguration(overrideConfig) + .build(); + } + return cloudFormationClient; + } + + public static void deployCloudFormationStack(String stackName) { + String templateBody; + boolean doesExist = describeStack(stackName); + if (!doesExist) { + try { + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + Path filePath = Paths.get(classLoader.getResource(CFN_TEMPLATE).toURI()); + templateBody = Files.readString(filePath); + } catch (IOException | URISyntaxException e) { + throw new RuntimeException(e); + } + + getCloudFormationClient().createStack(b -> b.stackName(stackName) + .templateBody(templateBody) + .capabilities(Capability.CAPABILITY_IAM)) + .whenComplete((csr, t) -> { + if (csr != null) { + System.out.println("Stack creation requested, ARN is " + csr.stackId()); + try (CloudFormationAsyncWaiter waiter = getCloudFormationClient().waiter()) { + waiter.waitUntilStackCreateComplete(request -> request.stackName(stackName)) + .whenComplete((dsr, th) -> { + if (th != null) { + System.out.println("Error waiting for stack creation: " + th.getMessage()); + } else { + dsr.matched().response().orElseThrow(() -> new RuntimeException("Failed to deploy")); + System.out.println("Stack created successfully"); + } + }).join(); + } + } else { + System.out.format("Error creating stack: " + t.getMessage(), t); + throw new RuntimeException(t.getCause().getMessage(), t); + } + }).join(); + } else { + logger.info("{} stack already exists", CFN_TEMPLATE); + } + } + + // Check to see if the Stack exists before deploying it + public static Boolean describeStack(String stackName) { + try { + CompletableFuture future = getCloudFormationClient().describeStacks(); + DescribeStacksResponse stacksResponse = (DescribeStacksResponse) future.join(); + List stacks = stacksResponse.stacks(); + for (Stack myStack : stacks) { + if (myStack.stackName().compareTo(stackName) == 0) { + return true; + } + } + } catch (CloudFormationException e) { + System.err.println(e.getMessage()); + } + return false; + } + + public static void destroyCloudFormationStack(String stackName) { + getCloudFormationClient().deleteStack(b -> b.stackName(stackName)) + .whenComplete((dsr, t) -> { + if (dsr != null) { + System.out.println("Delete stack requested ...."); + try (CloudFormationAsyncWaiter waiter = getCloudFormationClient().waiter()) { + waiter.waitUntilStackDeleteComplete(request -> request.stackName(stackName)) + .whenComplete((waiterResponse, throwable) -> + System.out.println("Stack deleted successfully.")) + .join(); + } + } else { + System.out.format("Error deleting stack: " + t.getMessage(), t); + throw new RuntimeException(t.getCause().getMessage(), t); + } + }).join(); + } + + public static CompletableFuture> getStackOutputsAsync(String stackName) { + CloudFormationAsyncClient cloudFormationAsyncClient = getCloudFormationClient(); + + DescribeStacksRequest describeStacksRequest = DescribeStacksRequest.builder() + .stackName(stackName) + .build(); + + return cloudFormationAsyncClient.describeStacks(describeStacksRequest) + .handle((describeStacksResponse, throwable) -> { + if (throwable != null) { + throw new RuntimeException("Failed to get stack outputs for: " + stackName, throwable); + } + + // Process the result + if (describeStacksResponse.stacks().isEmpty()) { + throw new RuntimeException("Stack not found: " + stackName); + } + + Stack stack = describeStacksResponse.stacks().get(0); + Map outputs = new HashMap<>(); + for (Output output : stack.outputs()) { + outputs.put(output.outputKey(), output.outputValue()); + } + + return outputs; + }); + } +} diff --git a/javav2/example_code/iotsitewise/src/main/java/com/example/iotsitewise/scenario/SitewiseActions.java b/javav2/example_code/iotsitewise/src/main/java/com/example/iotsitewise/scenario/SitewiseActions.java new file mode 100644 index 00000000000..079ca3a48da --- /dev/null +++ b/javav2/example_code/iotsitewise/src/main/java/com/example/iotsitewise/scenario/SitewiseActions.java @@ -0,0 +1,574 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package com.example.iotsitewise.scenario; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.awssdk.core.retry.RetryMode; +import software.amazon.awssdk.services.iotsitewise.model.AssetModelPropertySummary; +import software.amazon.awssdk.services.iotsitewise.model.BatchPutAssetPropertyValueResponse; +import software.amazon.awssdk.services.iotsitewise.model.CreateGatewayRequest; +import software.amazon.awssdk.services.iotsitewise.model.CreateGatewayResponse; +import software.amazon.awssdk.services.iotsitewise.model.DeleteGatewayRequest; +import software.amazon.awssdk.services.iotsitewise.model.DeleteGatewayResponse; +import software.amazon.awssdk.services.iotsitewise.model.DescribeGatewayRequest; +import software.amazon.awssdk.services.iotsitewise.model.DescribeGatewayResponse; +import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; +import software.amazon.awssdk.http.async.SdkAsyncHttpClient; +import software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClient; +import software.amazon.awssdk.services.iotsitewise.IoTSiteWiseAsyncClient; +import software.amazon.awssdk.services.iotsitewise.model.AssetModelPropertyDefinition; +import software.amazon.awssdk.services.iotsitewise.model.AssetModelSummary; +import software.amazon.awssdk.services.iotsitewise.model.AssetPropertyValue; +import software.amazon.awssdk.services.iotsitewise.model.BatchPutAssetPropertyValueRequest; +import software.amazon.awssdk.services.iotsitewise.model.CreateAssetModelRequest; +import software.amazon.awssdk.services.iotsitewise.model.CreateAssetModelResponse; +import software.amazon.awssdk.services.iotsitewise.model.CreateAssetRequest; +import software.amazon.awssdk.services.iotsitewise.model.CreateAssetResponse; +import software.amazon.awssdk.services.iotsitewise.model.CreatePortalRequest; +import software.amazon.awssdk.services.iotsitewise.model.DeleteAssetModelRequest; +import software.amazon.awssdk.services.iotsitewise.model.DeleteAssetModelResponse; +import software.amazon.awssdk.services.iotsitewise.model.DeleteAssetRequest; +import software.amazon.awssdk.services.iotsitewise.model.DeleteAssetResponse; +import software.amazon.awssdk.services.iotsitewise.model.DeletePortalRequest; +import software.amazon.awssdk.services.iotsitewise.model.DeletePortalResponse; +import software.amazon.awssdk.services.iotsitewise.model.DescribePortalRequest; +import software.amazon.awssdk.services.iotsitewise.model.DescribePortalResponse; +import software.amazon.awssdk.services.iotsitewise.model.GatewayPlatform; +import software.amazon.awssdk.services.iotsitewise.model.GetAssetPropertyValueRequest; +import software.amazon.awssdk.services.iotsitewise.model.GreengrassV2; +import software.amazon.awssdk.services.iotsitewise.model.ListAssetModelPropertiesRequest; +import software.amazon.awssdk.services.iotsitewise.model.ListAssetModelsRequest; +import software.amazon.awssdk.services.iotsitewise.model.Measurement; +import software.amazon.awssdk.services.iotsitewise.model.PropertyDataType; +import software.amazon.awssdk.services.iotsitewise.model.PropertyType; +import software.amazon.awssdk.services.iotsitewise.model.PutAssetPropertyValueEntry; +import software.amazon.awssdk.services.iotsitewise.model.TimeInNanos; +import software.amazon.awssdk.services.iotsitewise.model.Variant; +import java.time.Duration; +import java.time.Instant; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.stream.Collectors; + +// snippet-start:[iotsitewise.java2.actions.main] +public class SitewiseActions { + + private static final Logger logger = LoggerFactory.getLogger(SitewiseActions.class); + + private static IoTSiteWiseAsyncClient ioTSiteWiseAsyncClient; + + private static IoTSiteWiseAsyncClient getAsyncClient() { + if (ioTSiteWiseAsyncClient == null) { + SdkAsyncHttpClient httpClient = NettyNioAsyncHttpClient.builder() + .maxConcurrency(100) + .connectionTimeout(Duration.ofSeconds(60)) + .readTimeout(Duration.ofSeconds(60)) + .writeTimeout(Duration.ofSeconds(60)) + .build(); + + ClientOverrideConfiguration overrideConfig = ClientOverrideConfiguration.builder() + .apiCallTimeout(Duration.ofMinutes(2)) + .apiCallAttemptTimeout(Duration.ofSeconds(90)) + .retryStrategy(RetryMode.STANDARD) + .build(); + + ioTSiteWiseAsyncClient = IoTSiteWiseAsyncClient.builder() + .httpClient(httpClient) + .overrideConfiguration(overrideConfig) + .build(); + } + return ioTSiteWiseAsyncClient; + } + + // snippet-start:[sitewise.java2_create_asset_model.main] + + /** + * Creates an asset model. + * + * @param name the name of the asset model to create. + * @return a {@link CompletableFuture} that represents a {@link CreateAssetModelResponse} result. The calling code + * can attach callbacks, then handle the result or exception by calling {@link CompletableFuture#join()} or + * {@link CompletableFuture#get()}. + *

+ * If any completion stage in this method throws an exception, the method logs the exception cause and keeps it + * available to the calling code as a {@link CompletionException}. By calling + * {@link CompletionException#getCause()}, the calling code can access the original exception. + */ + public CompletableFuture createAssetModelAsync(String name) { + PropertyType humidity = PropertyType.builder() + .measurement(Measurement.builder().build()) + .build(); + + PropertyType temperaturePropertyType = PropertyType.builder() + .measurement(Measurement.builder().build()) + .build(); + + AssetModelPropertyDefinition temperatureProperty = AssetModelPropertyDefinition.builder() + .name("Temperature") + .dataType(PropertyDataType.DOUBLE) + .type(temperaturePropertyType) + .build(); + + AssetModelPropertyDefinition humidityProperty = AssetModelPropertyDefinition.builder() + .name("Humidity") + .dataType(PropertyDataType.DOUBLE) + .type(humidity) + .build(); + + CreateAssetModelRequest createAssetModelRequest = CreateAssetModelRequest.builder() + .assetModelName(name) + .assetModelDescription("This is my asset model") + .assetModelProperties(temperatureProperty, humidityProperty) + .build(); + + return getAsyncClient().createAssetModel(createAssetModelRequest) + .whenComplete((response, exception) -> { + if (exception != null) { + logger.error("Failed to create asset model: {} ", exception.getCause().getMessage()); + } + }); + } + // snippet-end:[sitewise.java2_create_asset_model.main] + + // snippet-start:[sitewise.java2_create_asset.main] + + /** + * Creates an asset with the specified name and asset model Id. + * + * @param assetName the name of the asset to create. + * @param assetModelId the Id of the asset model to associate with the asset. + * @return a {@link CompletableFuture} that represents a {@link CreateAssetResponse} result. The calling code can + * attach callbacks, then handle the result or exception by calling {@link CompletableFuture#join()} or + * {@link CompletableFuture#get()}. + *

+ * If any completion stage in this method throws an exception, the method logs the exception cause and keeps it + * available to the calling code as a {@link CompletionException}. By calling + * {@link CompletionException#getCause()}, the calling code can access the original exception. + */ + public CompletableFuture createAssetAsync(String assetName, String assetModelId) { + CreateAssetRequest createAssetRequest = CreateAssetRequest.builder() + .assetModelId(assetModelId) + .assetDescription("Created using the AWS SDK for Java") + .assetName(assetName) + .build(); + + return getAsyncClient().createAsset(createAssetRequest) + .whenComplete((response, exception) -> { + if (exception != null) { + logger.error("Failed to create asset: {}", exception.getCause().getMessage()); + } + }); + } + // snippet-end:[sitewise.java2_create_asset.main] + + // snippet-start:[sitewise.java2_put_batch_property.main] + /** + * Sends data to the SiteWise service. + * + * @param assetId the ID of the asset to which the data will be sent. + * @param tempPropertyId the ID of the temperature property. + * @param humidityPropId the ID of the humidity property. + * @return a {@link CompletableFuture} that represents a {@link BatchPutAssetPropertyValueResponse} result. The + * calling code can attach callbacks, then handle the result or exception by calling + * {@link CompletableFuture#join()} or {@link CompletableFuture#get()}. + *

+ * If any completion stage in this method throws an exception, the method logs the exception cause and keeps it + * available to the calling code as a {@link CompletionException}. By calling + * {@link CompletionException#getCause()}, the calling code can access the original exception. + */ + public CompletableFuture sendDataToSiteWiseAsync(String assetId, String tempPropertyId, String humidityPropId) { + Map sampleData = generateSampleData(); + long timestamp = Instant.now().toEpochMilli(); + + TimeInNanos time = TimeInNanos.builder() + .timeInSeconds(timestamp / 1000) + .offsetInNanos((int) ((timestamp % 1000) * 1000000)) + .build(); + + BatchPutAssetPropertyValueRequest request = BatchPutAssetPropertyValueRequest.builder() + .entries(Arrays.asList( + PutAssetPropertyValueEntry.builder() + .entryId("entry-3") + .assetId(assetId) + .propertyId(tempPropertyId) + .propertyValues(Arrays.asList( + AssetPropertyValue.builder() + .value(Variant.builder() + .doubleValue(sampleData.get("Temperature")) + .build()) + .timestamp(time) + .build() + )) + .build(), + PutAssetPropertyValueEntry.builder() + .entryId("entry-4") + .assetId(assetId) + .propertyId(humidityPropId) + .propertyValues(Arrays.asList( + AssetPropertyValue.builder() + .value(Variant.builder() + .doubleValue(sampleData.get("Humidity")) + .build()) + .timestamp(time) + .build() + )) + .build() + )) + .build(); + + return getAsyncClient().batchPutAssetPropertyValue(request) + .whenComplete((response, exception) -> { + if (exception != null) { + logger.error("An exception occurred: {}", exception.getCause().getMessage()); + } + }); + } + // snippet-end:[sitewise.java2_put_batch_property.main] + + // snippet-start:[sitewise.java2_get_property.main] + /** + * Fetches the value of an asset property. + * + * @param propId the ID of the asset property to fetch. + * @param assetId the ID of the asset to fetch the property value for. + * @return a {@link CompletableFuture} that represents a {@link Double} result. The calling code can attach + * callbacks, then handle the result or exception by calling {@link CompletableFuture#join()} or + * {@link CompletableFuture#get()}. + *

+ * If any completion stage in this method throws an exception, the method logs the exception cause and keeps + * it available to the calling code as a {@link CompletionException}. By calling + * {@link CompletionException#getCause()}, the calling code can access the original exception. + */ + public CompletableFuture getAssetPropValueAsync(String propId, String assetId) { + GetAssetPropertyValueRequest assetPropertyValueRequest = GetAssetPropertyValueRequest.builder() + .propertyId(propId) + .assetId(assetId) + .build(); + + return getAsyncClient().getAssetPropertyValue(assetPropertyValueRequest) + .handle((response, exception) -> { + if (exception != null) { + logger.error("Error occurred while fetching property value: {}.", exception.getCause().getMessage()); + throw (CompletionException) exception; + } + return response.propertyValue().value().doubleValue(); + }); + } + // snippet-end:[sitewise.java2_get_property.main] + + // snippet-start:[sitewise.java2.describe.asset.model.main] + /** + * Retrieves the property IDs associated with a specific asset model. + * + * @param assetModelId the ID of the asset model that defines the properties. + * @return a {@link CompletableFuture} that represents a {@link Map} result that associates the property name to the + * propert ID. The calling code can attach callbacks, then handle the result or exception by calling + * {@link CompletableFuture#join()} or {@link CompletableFuture#get()}. + *

+ * If any completion stage in this method throws an exception, the method logs the exception cause and keeps + * it available to the calling code as a {@link CompletionException}. By calling + * {@link CompletionException#getCause()}, the calling code can access the original exception. + */ + public CompletableFuture> getPropertyIds(String assetModelId) { + ListAssetModelPropertiesRequest modelPropertiesRequest = ListAssetModelPropertiesRequest.builder().assetModelId(assetModelId).build(); + return getAsyncClient().listAssetModelProperties(modelPropertiesRequest) + .handle((response, throwable) -> { + if (response != null) { + return response.assetModelPropertySummaries().stream() + .collect(Collectors + .toMap(AssetModelPropertySummary::name, AssetModelPropertySummary::id)); + } else { + logger.error("Error occurred while fetching property IDs: {}.", throwable.getCause().getMessage()); + throw (CompletionException) throwable; + } + }); + } + // snippet-end:[sitewise.java2.describe.asset.model.main] + + // snippet-start:[sitewise.java2.delete.asset.main] + /** + * Deletes an asset. + * + * @param assetId the ID of the asset to be deleted. + * @return a {@link CompletableFuture} that represents a {@link DeleteAssetResponse} result. The calling code can + * attach callbacks, then handle the result or exception by calling {@link CompletableFuture#join()} or + * {@link CompletableFuture#get()}. + *

+ * If any completion stage in this method throws an exception, the method logs the exception cause and keeps + * it available to the calling code as a {@link CompletionException}. By calling + * {@link CompletionException#getCause()}, the calling code can access the original exception. + */ + public CompletableFuture deleteAssetAsync(String assetId) { + DeleteAssetRequest deleteAssetRequest = DeleteAssetRequest.builder() + .assetId(assetId) + .build(); + + return getAsyncClient().deleteAsset(deleteAssetRequest) + .whenComplete((response, exception) -> { + if (exception != null) { + logger.error("An error occurred deleting asset with id: {}", assetId); + } + }); + } + // snippet-end:[sitewise.java2.delete.asset.main] + + // snippet-start:[sitewise.java2.delete.asset.model.main] + /** + * Deletes an Asset Model with the specified ID. + * + * @param assetModelId the ID of the Asset Model to delete. + * @return a {@link CompletableFuture} that represents a {@link DeleteAssetModelResponse} result. The calling code + * can attach callbacks, then handle the result or exception by calling {@link CompletableFuture#join()} or + * {@link CompletableFuture#get()}. + *

+ * If any completion stage in this method throws an exception, the method logs the exception cause and keeps + * it available to the calling code as a {@link CompletionException}. By calling + * {@link CompletionException#getCause()}, the calling code can access the original exception. + */ + public CompletableFuture deleteAssetModelAsync(String assetModelId) { + DeleteAssetModelRequest deleteAssetModelRequest = DeleteAssetModelRequest.builder() + .assetModelId(assetModelId) + .build(); + + return getAsyncClient().deleteAssetModel(deleteAssetModelRequest) + .whenComplete((response, exception) -> { + if (exception != null) { + logger.error("Failed to delete asset model with ID:{}.", exception.getMessage()); + } + }); + } + // snippet-end:[sitewise.java2.delete.asset.model.main] + + // snippet-start:[sitewise.java2.create.portal.main] + /** + * Creates a new IoT SiteWise portal. + * + * @param portalName the name of the portal to create. + * @param iamRole the IAM role ARN to use for the portal. + * @param contactEmail the email address of the portal contact. + * @return a {@link CompletableFuture} that represents a {@link String} result of the portal ID. The calling code + * can attach callbacks, then handle the result or exception by calling {@link CompletableFuture#join()} or + * {@link CompletableFuture#get()}. + *

+ * If any completion stage in this method throws an exception, the method logs the exception cause and keeps + * it available to the calling code as a {@link CompletionException}. By calling + * {@link CompletionException#getCause()}, the calling code can access the original exception. + */ + public CompletableFuture createPortalAsync(String portalName, String iamRole, String contactEmail) { + CreatePortalRequest createPortalRequest = CreatePortalRequest.builder() + .portalName(portalName) + .portalDescription("This is my custom IoT SiteWise portal.") + .portalContactEmail(contactEmail) + .roleArn(iamRole) + .build(); + + return getAsyncClient().createPortal(createPortalRequest) + .handle((response, exception) -> { + if (exception != null) { + logger.error("Failed to create portal: {} ", exception.getCause().getMessage()); + throw (CompletionException) exception; + } + return response.portalId(); + }); + } + // snippet-end:[sitewise.java2.create.portal.main] + + // snippet-start:[sitewise.java2.delete.portal.main] + /** + * Deletes a portal. + * + * @param portalId the ID of the portal to be deleted. + * @return a {@link CompletableFuture} that represents a {@link DeletePortalResponse}. The calling code can attach + * callbacks, then handle the result or exception by calling {@link CompletableFuture#join()} or + * {@link CompletableFuture#get()}. + *

+ * If any completion stage in this method throws an exception, the method logs the exception cause and keeps + * it available to the calling code as a {@link CompletionException}. By calling + * {@link CompletionException#getCause()}, the calling code can access the original exception. + */ + public CompletableFuture deletePortalAsync(String portalId) { + DeletePortalRequest deletePortalRequest = DeletePortalRequest.builder() + .portalId(portalId) + .build(); + + return getAsyncClient().deletePortal(deletePortalRequest) + .whenComplete((response, exception) -> { + if (exception != null) { + logger.error("Failed to delete portal with ID: {}. Error: {}", portalId, exception.getCause().getMessage()); + } + }); + } + // snippet-end:[sitewise.java2.delete.portal.main] + + // snippet-start:[sitewise.java2.list.asset.model.main] + /** + * Retrieves the asset model ID for the given asset model name. + * + * @param assetModelName the name of the asset model for the ID. + * @return a {@link CompletableFuture} that represents a {@link String} result of the asset model ID or null if the + * asset model cannot be found. The calling code can attach callbacks, then handle the result or exception + * by calling {@link CompletableFuture#join()} or {@link CompletableFuture#get()}. + *

+ * If any completion stage in this method throws an exception, the method logs the exception cause and keeps + * it available to the calling code as a {@link CompletionException}. By calling + * {@link CompletionException#getCause()}, the calling code can access the original exception. + */ + public CompletableFuture getAssetModelIdAsync(String assetModelName) { + ListAssetModelsRequest listAssetModelsRequest = ListAssetModelsRequest.builder().build(); + return getAsyncClient().listAssetModels(listAssetModelsRequest) + .handle((listAssetModelsResponse, exception) -> { + if (exception != null) { + logger.error("Failed to retrieve Asset Model ID: {}", exception.getCause().getMessage()); + throw (CompletionException) exception; + } + for (AssetModelSummary assetModelSummary : listAssetModelsResponse.assetModelSummaries()) { + if (assetModelSummary.name().equals(assetModelName)) { + return assetModelSummary.id(); + } + } + return null; + }); + } + // snippet-end:[sitewise.java2.list.asset.model.main] + + // snippet-start:[sitewise.java2.describe.portal.main] + /** + * Retrieves a portal's description. + * + * @param portalId the ID of the portal to describe. + * @return a {@link CompletableFuture} that represents a {@link String} result of the portal's start URL + * (see: {@link DescribePortalResponse#portalStartUrl()}). The calling code can attach callbacks, then handle the + * result or exception by calling {@link CompletableFuture#join()} or {@link CompletableFuture#get()}. + *

+ * If any completion stage in this method throws an exception, the method logs the exception cause and keeps + * it available to the calling code as a {@link CompletionException}. By calling + * {@link CompletionException#getCause()}, the calling code can access the original exception. + */ + public CompletableFuture describePortalAsync(String portalId) { + DescribePortalRequest request = DescribePortalRequest.builder() + .portalId(portalId) + .build(); + + return getAsyncClient().describePortal(request) + .handle((response, exception) -> { + if (exception != null) { + logger.error("An exception occurred retrieving the portal description: {}", exception.getCause().getMessage()); + throw (CompletionException) exception; + } + return response.portalStartUrl(); + }); + } + // snippet-end:[sitewise.java2.describe.portal.main] + + // snippet-start:[sitewise.java2.create.gateway.main] + + /** + * Creates a new IoT Sitewise gateway. + * + * @param gatewayName The name of the gateway to create. + * @param myThing The name of the core device thing to associate with the gateway. + * @return a {@link CompletableFuture} that represents a {@link String} result of the gateways ID. The calling code + * can attach callbacks, then handle the result or exception by calling {@link CompletableFuture#join()} or + * {@link CompletableFuture#get()}. + *

+ * If any completion stage in this method throws an exception, the method logs the exception cause and keeps + * it available to the calling code as a {@link CompletionException}. By calling + * {@link CompletionException#getCause()}, the calling code can access the original exception. + */ + public CompletableFuture createGatewayAsync(String gatewayName, String myThing) { + GreengrassV2 gg = GreengrassV2.builder() + .coreDeviceThingName(myThing) + .build(); + + GatewayPlatform platform = GatewayPlatform.builder() + .greengrassV2(gg) + .build(); + + Map tag = new HashMap<>(); + tag.put("Environment", "Production"); + + CreateGatewayRequest createGatewayRequest = CreateGatewayRequest.builder() + .gatewayName(gatewayName) + .gatewayPlatform(platform) + .tags(tag) + .build(); + + return getAsyncClient().createGateway(createGatewayRequest) + .handle((response, exception) -> { + if (exception != null) { + logger.error("Error creating the gateway."); + throw (CompletionException) exception; + } + logger.info("The ARN of the gateway is {}" , response.gatewayArn()); + return response.gatewayId(); + }); + } + // snippet-end:[sitewise.java2.create.gateway.main] + + // snippet-start:[sitewise.java2.delete.gateway.main] + /** + * Deletes the specified gateway. + * + * @param gatewayId the ID of the gateway to delete. + * @return a {@link CompletableFuture} that represents a {@link DeleteGatewayResponse} result.. The calling code + * can attach callbacks, then handle the result or exception by calling {@link CompletableFuture#join()} or + * {@link CompletableFuture#get()}. + *

+ * If any completion stage in this method throws an exception, the method logs the exception cause and keeps + * it available to the calling code as a {@link CompletionException}. By calling + * {@link CompletionException#getCause()}, the calling code can access the original exception. + */ + public CompletableFuture deleteGatewayAsync(String gatewayId) { + DeleteGatewayRequest deleteGatewayRequest = DeleteGatewayRequest.builder() + .gatewayId(gatewayId) + .build(); + + return getAsyncClient().deleteGateway(deleteGatewayRequest) + .whenComplete((response, exception) -> { + if (exception != null) { + logger.error("Failed to delete gateway: {}", exception.getCause().getMessage()); + } + }); + } + // snippet-end:[sitewise.java2.delete.gateway.main] + + // snippet-start:[sitewise.java2.describe.gateway.main] + /** + * Describes the specified gateway. + * + * @param gatewayId the ID of the gateway to describe. + * @return a {@link CompletableFuture} that represents a {@link DescribeGatewayResponse} result. The calling code + * can attach callbacks, then handle the result or exception by calling {@link CompletableFuture#join()} or + * {@link CompletableFuture#get()}. + *

+ * If any completion stage in this method throws an exception, the method logs the exception cause and keeps + * it available to the calling code as a {@link CompletionException}. By calling + * {@link CompletionException#getCause()}, the calling code can access the original exception. + */ + public CompletableFuture describeGatewayAsync(String gatewayId) { + DescribeGatewayRequest request = DescribeGatewayRequest.builder() + .gatewayId(gatewayId) + .build(); + + return getAsyncClient().describeGateway(request) + .whenComplete((response, exception) -> { + if (exception != null) { + logger.error("An error occurred during the describeGateway method: {}", exception.getCause().getMessage()); + } + }); + } + // snippet-end:[sitewise.java2.describe.gateway.main] + + private static Map generateSampleData() { + Map data = new HashMap<>(); + data.put("Temperature", 23.5); + data.put("Humidity", 65.0); + return data; + } +} +// snippet-end:[iotsitewise.java2.actions.main] \ No newline at end of file diff --git a/javav2/example_code/iotsitewise/src/main/java/com/example/iotsitewise/scenario/SitewiseScenario.java b/javav2/example_code/iotsitewise/src/main/java/com/example/iotsitewise/scenario/SitewiseScenario.java new file mode 100644 index 00000000000..2f313697f90 --- /dev/null +++ b/javav2/example_code/iotsitewise/src/main/java/com/example/iotsitewise/scenario/SitewiseScenario.java @@ -0,0 +1,424 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package com.example.iotsitewise.scenario; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.awssdk.services.iotsitewise.model.CreateAssetModelResponse; +import software.amazon.awssdk.services.iotsitewise.model.CreateAssetResponse; +import software.amazon.awssdk.services.iotsitewise.model.IoTSiteWiseException; +import software.amazon.awssdk.services.iotsitewise.model.ResourceAlreadyExistsException; +import software.amazon.awssdk.services.iotsitewise.model.ResourceNotFoundException; +import java.util.Map; +import java.util.Scanner; +import java.util.concurrent.CompletionException; + +// snippet-start:[iotsitewise.java2.scenario.main] +public class SitewiseScenario { + + public static final String DASHES = new String(new char[80]).replace("\0", "-"); + + private static final Logger logger = LoggerFactory.getLogger(SitewiseScenario.class); + static Scanner scanner = new Scanner(System.in); + + private static final String ROLES_STACK = "RoleSitewise"; + + static SitewiseActions sitewiseActions = new SitewiseActions(); + + public static void main(String[] args) throws Throwable { + Scanner scanner = new Scanner(System.in); + String contactEmail = "user@mydomain.com"; // Change email address. + String assetModelName = "MyAssetModel1"; + String assetName = "MyAsset1" ; + String portalName = "MyPortal1" ; + String gatewayName = "MyGateway1" ; + String myThing = "MyThing1" ; + + logger.info(""" + AWS IoT SiteWise is a fully managed software-as-a-service (SaaS) that + makes it easy to collect, store, organize, and monitor data from industrial equipment and processes. + It is designed to help industrial and manufacturing organizations collect data from their equipment and + processes, and use that data to make informed decisions about their operations. + + One of the key features of AWS IoT SiteWise is its ability to connect to a wide range of industrial + equipment and systems, including programmable logic controllers (PLCs), sensors, and other + industrial devices. It can collect data from these devices and organize it into a unified data model, + making it easier to analyze and gain insights from the data. AWS IoT SiteWise also provides tools for + visualizing the data, setting up alarms and alerts, and generating reports. + + Another key feature of AWS IoT SiteWise is its ability to scale to handle large volumes of data. + It can collect and store data from thousands of devices and process millions of data points per second, + making it suitable for large-scale industrial operations. Additionally, AWS IoT SiteWise is designed + to be secure and compliant, with features like role-based access controls, data encryption, + and integration with other AWS services for additional security and compliance features. + + Let's get started... + """); + + waitForInputToContinue(scanner); + logger.info(DASHES); + + try { + runScenario(assetModelName, assetName, portalName, contactEmail, gatewayName, myThing); + } catch (RuntimeException e) { + logger.info(e.getMessage()); + } + } + + public static void runScenario(String assetModelName, String assetName, String portalName, String contactEmail, String gatewayName, String myThing) throws Throwable { + logger.info("Use AWS CloudFormation to create an IAM role that is required for this scenario."); + CloudFormationHelper.deployCloudFormationStack(ROLES_STACK); + Map stackOutputs = CloudFormationHelper.getStackOutputsAsync(ROLES_STACK).join(); + String iamRole = stackOutputs.get("SitewiseRoleArn"); + logger.info("The ARN of the IAM role is {}",iamRole); + logger.info(DASHES); + + logger.info(DASHES); + logger.info("1. Create an AWS SiteWise Asset Model"); + logger.info(""" + An AWS IoT SiteWise Asset Model is a way to represent the physical assets, such as equipment, + processes, and systems, that exist in an industrial environment. This model provides a structured and + hierarchical representation of these assets, allowing users to define the relationships and properties + of each asset. + + This scenario creates two asset model properties: temperature and humidity. + """); + waitForInputToContinue(scanner); + String assetModelId = null; + try { + CreateAssetModelResponse response = sitewiseActions.createAssetModelAsync(assetModelName).join(); + assetModelId = response.assetModelId(); + logger.info("Asset Model successfully created. Asset Model ID: {}. ", assetModelId); + } catch (CompletionException ce) { + Throwable cause = ce.getCause(); + if (cause instanceof ResourceAlreadyExistsException) { + try { + assetModelId = sitewiseActions.getAssetModelIdAsync(assetModelName).join(); + logger.info("The Asset Model {} already exists. The id of the existing model is {}. Moving on...", assetModelName, assetModelId); + } catch (CompletionException cex) { + logger.error("Exception thrown acquiring the asset model id: {}", cex.getCause().getCause(), cex); + return; + } + } else { + logger.info("An unexpected error occurred: " + cause.getMessage(), cause); + return; + } + } + waitForInputToContinue(scanner); + + logger.info(DASHES); + logger.info("2. Create an AWS IoT SiteWise Asset"); + logger.info(""" + The IoT SiteWise model that we just created defines the structure and metadata for your physical assets. + Now we create an asset from the asset model. + + """); + logger.info("Let's wait 30 seconds for the asset to be ready."); + countdown(30); + waitForInputToContinue(scanner); + String assetId; + try { + CreateAssetResponse response = sitewiseActions.createAssetAsync(assetName, assetModelId).join(); + assetId = response.assetId(); + logger.info("Asset created with ID: {}", assetId); + } catch (CompletionException ce) { + Throwable cause = ce.getCause(); + if (cause instanceof ResourceNotFoundException) { + logger.info("The asset model id was not found: {}", cause.getMessage(), cause); + } else { + logger.info("An unexpected error occurred: {}", cause.getMessage(), cause); + } + return; + } + waitForInputToContinue(scanner); + logger.info(DASHES); + + logger.info(DASHES); + logger.info("3. Retrieve the property ID values"); + logger.info(""" + To send data to an asset, we need to get the property ID values. In this scenario, we access the + temperature and humidity property ID values. + """); + waitForInputToContinue(scanner); + Map propertyIds = null; + try { + propertyIds = sitewiseActions.getPropertyIds(assetModelId).join(); + } catch (CompletionException ce) { + Throwable cause = ce.getCause(); + if (cause instanceof IoTSiteWiseException) { + logger.error("IoTSiteWiseException occurred: {}", cause.getMessage(), ce); + } else { + logger.error("An unexpected error occurred: {}", cause.getMessage(), ce); + } + return; + } + String humPropId = propertyIds.get("Humidity"); + logger.info("The Humidity property Id is {}", humPropId); + String tempPropId = propertyIds.get("Temperature"); + logger.info("The Temperature property Id is {}", tempPropId); + + waitForInputToContinue(scanner); + logger.info(DASHES); + + logger.info(DASHES); + logger.info("4. Send data to an AWS IoT SiteWise Asset"); + logger.info(""" + By sending data to an IoT SiteWise Asset, you can aggregate data from + multiple sources, normalize the data into a standard format, and store it in a + centralized location. This makes it easier to analyze and gain insights from the data. + + In this example, we generate sample temperature and humidity data and send it to the AWS IoT SiteWise asset. + + """); + waitForInputToContinue(scanner); + try { + sitewiseActions.sendDataToSiteWiseAsync(assetId, tempPropId, humPropId).join(); + logger.info("Data sent successfully."); + } catch (CompletionException ce) { + Throwable cause = ce.getCause(); + if (cause instanceof ResourceNotFoundException) { + logger.error("The AWS resource was not found: {}", cause.getMessage(), cause); + } else { + logger.error("An unexpected error occurred: {}", cause.getMessage(), cause); + } + return; + } + waitForInputToContinue(scanner); + logger.info(DASHES); + + logger.info(DASHES); + logger.info("5. Retrieve the value of the IoT SiteWise Asset property"); + logger.info(""" + IoT SiteWise is an AWS service that allows you to collect, process, and analyze industrial data + from connected equipment and sensors. One of the key benefits of reading an IoT SiteWise property + is the ability to gain valuable insights from your industrial data. + + """); + waitForInputToContinue(scanner); + try { + Double assetVal = sitewiseActions.getAssetPropValueAsync(tempPropId, assetId).join(); + logger.info("The property name is: {}", "Temperature"); + logger.info("The value of this property is: {}", assetVal); + + waitForInputToContinue(scanner); + + assetVal = sitewiseActions.getAssetPropValueAsync(humPropId, assetId).join(); + logger.info("The property name is: {}", "Humidity"); + logger.info("The value of this property is: {}", assetVal); + } catch (CompletionException ce) { + Throwable cause = ce.getCause(); + if (cause instanceof ResourceNotFoundException) { + logger.info("The AWS resource was not found: {}", cause.getMessage(), cause); + } else { + logger.info("An unexpected error occurred: {}", cause.getMessage(), cause); + } + return; + } + waitForInputToContinue(scanner); + logger.info(DASHES); + + logger.info(DASHES); + logger.info("6. Create an IoT SiteWise Portal"); + logger.info(""" + An IoT SiteWise Portal allows you to aggregate data from multiple industrial sources, + such as sensors, equipment, and control systems, into a centralized platform. + """); + waitForInputToContinue(scanner); + String portalId; + try { + portalId = sitewiseActions.createPortalAsync(portalName, iamRole, contactEmail).join(); + logger.info("Portal created successfully. Portal ID {}", portalId); + } catch (CompletionException ce) { + Throwable cause = ce.getCause(); + if (cause instanceof IoTSiteWiseException siteWiseEx) { + logger.error("IoT SiteWise error occurred: Error message: {}, Error code {}", + siteWiseEx.getMessage(), siteWiseEx.awsErrorDetails().errorCode(), siteWiseEx); + } else { + logger.error("An unexpected error occurred: {}", cause.getMessage()); + } + return; + } + waitForInputToContinue(scanner); + logger.info(DASHES); + + logger.info(DASHES); + logger.info("7. Describe the Portal"); + logger.info(""" + In this step, we get a description of the portal and display the portal URL. + """); + waitForInputToContinue(scanner); + try { + String portalUrl = sitewiseActions.describePortalAsync(portalId).join(); + logger.info("Portal URL: {}", portalUrl); + } catch (CompletionException ce) { + Throwable cause = ce.getCause(); + if (cause instanceof ResourceNotFoundException notFoundException) { + logger.error("A ResourceNotFoundException occurred: Error message: {}, Error code {}", + notFoundException.getMessage(), notFoundException.awsErrorDetails().errorCode(), notFoundException); + } else { + logger.error("An unexpected error occurred: {}", cause.getMessage()); + } + return; + } + waitForInputToContinue(scanner); + logger.info(DASHES); + + logger.info(DASHES); + logger.info("8. Create an IoT SiteWise Gateway"); + logger.info( + """ + IoT SiteWise Gateway serves as the bridge between industrial equipment, sensors, and the + cloud-based IoT SiteWise service. It is responsible for securely collecting, processing, and + transmitting data from various industrial assets to the IoT SiteWise platform, + enabling real-time monitoring, analysis, and optimization of industrial operations. + + """); + waitForInputToContinue(scanner); + String gatewayId = ""; + try { + gatewayId = sitewiseActions.createGatewayAsync(gatewayName, myThing).join(); + logger.info("Gateway creation completed successfully. id is {}", gatewayId ); + } catch (CompletionException ce) { + Throwable cause = ce.getCause(); + if (cause instanceof IoTSiteWiseException siteWiseEx) { + logger.error("IoT SiteWise error occurred: Error message: {}, Error code {}", + siteWiseEx.getMessage(), siteWiseEx.awsErrorDetails().errorCode(), siteWiseEx); + } else { + logger.error("An unexpected error occurred: {}", cause.getMessage()); + } + return; + } + logger.info(DASHES); + logger.info(DASHES); + + logger.info("9. Describe the IoT SiteWise Gateway"); + waitForInputToContinue(scanner); + try { + sitewiseActions.describeGatewayAsync(gatewayId) + .thenAccept(response -> { + logger.info("Gateway Name: {}", response.gatewayName()); + logger.info("Gateway ARN: {}", response.gatewayArn()); + logger.info("Gateway Platform: {}", response.gatewayPlatform()); + logger.info("Gateway Creation Date: {}", response.creationDate()); + }).join(); + } catch (CompletionException ce) { + Throwable cause = ce.getCause(); + if (cause instanceof ResourceNotFoundException notFoundException) { + logger.error("A ResourceNotFoundException occurred: Error message: {}, Error code {}", + notFoundException.getMessage(), notFoundException.awsErrorDetails().errorCode(), notFoundException); + } else { + logger.error("An unexpected error occurred: {}", cause.getMessage(), cause); + } + return; + } + logger.info(DASHES); + + logger.info(DASHES); + logger.info("10. Delete the AWS IoT SiteWise Assets"); + logger.info( + """ + Before you can delete the Asset Model, you must delete the assets. + + """); + logger.info("Would you like to delete the IoT SiteWise Assets? (y/n)"); + String delAns = scanner.nextLine().trim(); + if (delAns.equalsIgnoreCase("y")) { + logger.info("You selected to delete the SiteWise assets."); + waitForInputToContinue(scanner); + try { + sitewiseActions.deletePortalAsync(portalId).join(); + logger.info("Portal {} was deleted successfully.", portalId); + + } catch (CompletionException ce) { + Throwable cause = ce.getCause(); + if (cause instanceof ResourceNotFoundException notFoundException) { + logger.error("A ResourceNotFoundException occurred: Error message: {}, Error code {}", + notFoundException.getMessage(), notFoundException.awsErrorDetails().errorCode(), notFoundException); + } else { + logger.error("An unexpected error occurred: {}", cause.getMessage()); + } + } + + try { + sitewiseActions.deleteGatewayAsync(gatewayId).join(); + logger.info("Gateway {} was deleted successfully.", gatewayId); + } catch (CompletionException ce) { + Throwable cause = ce.getCause(); + if (cause instanceof ResourceNotFoundException notFoundException) { + logger.error("A ResourceNotFoundException occurred: Error message: {}, Error code {}", + notFoundException.getMessage(), notFoundException.awsErrorDetails().errorCode(), notFoundException); + } else { + logger.error("An unexpected error occurred: {}", cause.getMessage()); + } + } + + try { + sitewiseActions.deleteAssetAsync(assetId).join(); + logger.info("Request to delete asset {} sent successfully", assetId); + } catch (CompletionException ce) { + Throwable cause = ce.getCause(); + if (cause instanceof ResourceNotFoundException notFoundException) { + logger.error("A ResourceNotFoundException occurred: Error message: {}, Error code {}", + notFoundException.getMessage(), notFoundException.awsErrorDetails().errorCode(), notFoundException); + } else { + logger.error("An unexpected error occurred: {}", cause.getMessage()); + } + } + logger.info("Let's wait 1 minute for the asset to be deleted."); + countdown(60); + waitForInputToContinue(scanner); + logger.info("Delete the AWS IoT SiteWise Asset Model"); + try { + sitewiseActions.deleteAssetModelAsync(assetModelId).join(); + logger.info("Asset model deleted successfully."); + } catch (CompletionException ce) { + Throwable cause = ce.getCause(); + if (cause instanceof ResourceNotFoundException notFoundException) { + logger.error("A ResourceNotFoundException occurred: Error message: {}, Error code {}", + notFoundException.getMessage(), notFoundException.awsErrorDetails().errorCode(), notFoundException); + } else { + logger.error("An unexpected error occurred: {}", cause.getMessage()); + } + } + waitForInputToContinue(scanner); + + } else { + logger.info("The resources will not be deleted."); + } + logger.info(DASHES); + + logger.info(DASHES); + CloudFormationHelper.destroyCloudFormationStack(ROLES_STACK); + logger.info("This concludes the AWS IoT SiteWise Scenario"); + logger.info(DASHES); + } + + private static void waitForInputToContinue(Scanner scanner) { + while (true) { + logger.info(""); + logger.info("Enter 'c' followed by to continue:"); + String input = scanner.nextLine(); + + if (input.trim().equalsIgnoreCase("c")) { + logger.info("Continuing with the program..."); + logger.info(""); + break; + } else { + logger.info("Invalid input. Please try again."); + } + } + } + + public static void countdown(int totalSeconds) throws InterruptedException { + for (int i = totalSeconds; i >= 0; i--) { + int displayMinutes = i / 60; + int displaySeconds = i % 60; + System.out.printf("\r%02d:%02d", displayMinutes, displaySeconds); + Thread.sleep(1000); // Wait for 1 second + } + System.out.println(); // Move to the next line after countdown + logger.info("Countdown complete!"); + } +} +// snippet-end:[iotsitewise.java2.scenario.main] \ No newline at end of file diff --git a/javav2/example_code/iotsitewise/src/main/resources/SitewiseRoles-template.yaml b/javav2/example_code/iotsitewise/src/main/resources/SitewiseRoles-template.yaml new file mode 100644 index 00000000000..3c0568a6fce --- /dev/null +++ b/javav2/example_code/iotsitewise/src/main/resources/SitewiseRoles-template.yaml @@ -0,0 +1,130 @@ +Resources: + RoleSitewiseF9E96FB3: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Statement: + - Action: sts:AssumeRole + Effect: Allow + Principal: + Service: iotsitewise.amazonaws.com + Version: "2012-10-17" + ManagedPolicyArns: + - Fn::Join: + - "" + - - "arn:" + - Ref: AWS::Partition + - :iam::aws:policy/AWSIoTSiteWiseFullAccess + Metadata: + aws:cdk:path: SitewiseStack/RoleSitewise/Resource + CDKMetadata: + Type: AWS::CDK::Metadata + Properties: + Analytics: v2:deflate64:H4sIAAAAAAAA/zPSMzQ21TNQTCwv1k1OydbNyUzSqw4uSUzO1kksL47PTMzVqw7Kz0nVcU7LA9G1OkGpxfmlRclgEf/SkoLSErAckqhzfl5KZklmfl6tjldiWaK+obmegZ6hkWJWcWamblFpXklmbqpeEIQGALeO80GAAAAA + Metadata: + aws:cdk:path: SitewiseStack/CDKMetadata/Default + Condition: CDKMetadataAvailable +Outputs: + SitewiseRoleArn: + Description: The ARN of the SiteWise Role + Value: + Fn::GetAtt: + - RoleSitewiseF9E96FB3 + - Arn +Conditions: + CDKMetadataAvailable: + Fn::Or: + - Fn::Or: + - Fn::Equals: + - Ref: AWS::Region + - af-south-1 + - Fn::Equals: + - Ref: AWS::Region + - ap-east-1 + - Fn::Equals: + - Ref: AWS::Region + - ap-northeast-1 + - Fn::Equals: + - Ref: AWS::Region + - ap-northeast-2 + - Fn::Equals: + - Ref: AWS::Region + - ap-south-1 + - Fn::Equals: + - Ref: AWS::Region + - ap-southeast-1 + - Fn::Equals: + - Ref: AWS::Region + - ap-southeast-2 + - Fn::Equals: + - Ref: AWS::Region + - ca-central-1 + - Fn::Equals: + - Ref: AWS::Region + - cn-north-1 + - Fn::Equals: + - Ref: AWS::Region + - cn-northwest-1 + - Fn::Or: + - Fn::Equals: + - Ref: AWS::Region + - eu-central-1 + - Fn::Equals: + - Ref: AWS::Region + - eu-north-1 + - Fn::Equals: + - Ref: AWS::Region + - eu-south-1 + - Fn::Equals: + - Ref: AWS::Region + - eu-west-1 + - Fn::Equals: + - Ref: AWS::Region + - eu-west-2 + - Fn::Equals: + - Ref: AWS::Region + - eu-west-3 + - Fn::Equals: + - Ref: AWS::Region + - il-central-1 + - Fn::Equals: + - Ref: AWS::Region + - me-central-1 + - Fn::Equals: + - Ref: AWS::Region + - me-south-1 + - Fn::Equals: + - Ref: AWS::Region + - sa-east-1 + - Fn::Or: + - Fn::Equals: + - Ref: AWS::Region + - us-east-1 + - Fn::Equals: + - Ref: AWS::Region + - us-east-2 + - Fn::Equals: + - Ref: AWS::Region + - us-west-1 + - Fn::Equals: + - Ref: AWS::Region + - us-west-2 +Parameters: + BootstrapVersion: + Type: AWS::SSM::Parameter::Value + Default: /cdk-bootstrap/hnb659fds/version + Description: Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip] +Rules: + CheckBootstrapVersion: + Assertions: + - Assert: + Fn::Not: + - Fn::Contains: + - - "1" + - "2" + - "3" + - "4" + - "5" + - Ref: BootstrapVersion + AssertDescription: CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI. + diff --git a/javav2/example_code/iotsitewise/src/main/resources/log4j2.xml b/javav2/example_code/iotsitewise/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..225afe2b3a8 --- /dev/null +++ b/javav2/example_code/iotsitewise/src/main/resources/log4j2.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/javav2/example_code/iotsitewise/src/test/java/SitewiseTests.java b/javav2/example_code/iotsitewise/src/test/java/SitewiseTests.java new file mode 100644 index 00000000000..c59e652473d --- /dev/null +++ b/javav2/example_code/iotsitewise/src/test/java/SitewiseTests.java @@ -0,0 +1,246 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + + +import com.example.iotsitewise.HelloSitewise; +import com.example.iotsitewise.scenario.CloudFormationHelper; +import com.example.iotsitewise.scenario.SitewiseActions; +import com.google.gson.Gson; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestMethodOrder; +import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.iotsitewise.model.CreateAssetModelResponse; +import software.amazon.awssdk.services.iotsitewise.model.CreateAssetResponse; +import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient; +import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueRequest; +import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueResponse; + +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@TestInstance(TestInstance.Lifecycle.PER_METHOD) +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class SitewiseTests { + + private static final String assetModelName = "MyAssetModel" + UUID.randomUUID(); + private static final String assetName = "MyAsset"; + + private static String assetId = ""; + private static final String portalName = "MyPortal"; + private static String contactEmail = ""; + private static final String gatewayName = "myGateway" + UUID.randomUUID(); + private static final String myThing = "myThing" + UUID.randomUUID(); + + private static String assetModelId = ""; + + private static String iamRole = ""; + + private static final SitewiseActions sitewiseActions = new SitewiseActions(); + + private static Map propertyIds; + + private static String humPropId = ""; + + private static String tempPropId = ""; + + private static String portalId = ""; + + private static String gatewayId = ""; + private static final String ROLES_STACK = "RoleSitewise"; + + @BeforeAll + public static void setUp() { + CloudFormationHelper.deployCloudFormationStack(ROLES_STACK); + Map stackOutputs = CloudFormationHelper.getStackOutputsAsync(ROLES_STACK).join(); + iamRole = stackOutputs.get("SitewiseRoleArn"); + + /* + The following values used in these integration tests are retrieved from AWS Secrets Manager. + */ + Gson gson = new Gson(); + String json = getSecretValues(); + SecretValues values = gson.fromJson(json, SecretValues.class); + contactEmail = values.getContactEmail(); + } + + @Test + @Tag("IntegrationTest") + @Order(1) + public void testHelloService() { + assertDoesNotThrow(HelloSitewise::fetchAssetModels); + } + + @Test + @Tag("IntegrationTest") + @Order(2) + public void testCreateAssetModel() { + assertDoesNotThrow(() -> { + CompletableFuture future = sitewiseActions.createAssetModelAsync(assetModelName); + CreateAssetModelResponse response = future.join(); + + if (response == null || response.assetModelId() == null) { + throw new RuntimeException("Simulating failure: response or assetModelId is null"); + } + assetModelId = response.assetModelId(); + assertNotNull(assetModelId); + }); + } + + @Test + @Tag("IntegrationTest") + @Order(3) + public void testCreateAsset() throws InterruptedException { + Thread.sleep(30000); + assertDoesNotThrow(() -> { + CompletableFuture future = sitewiseActions.createAssetAsync(assetName, assetModelId); + CreateAssetResponse response = future.join(); + assetId = response.assetId(); + assertNotNull(assetId); + }); + } + + @Test + @Tag("IntegrationTest") + @Order(4) + public void testGetPropIds() { + assertDoesNotThrow(() -> { + propertyIds = sitewiseActions.getPropertyIds(assetModelId).join(); + humPropId = propertyIds.get("Humidity"); + System.out.println("The Humidity property Id is " + humPropId); + tempPropId = propertyIds.get("Temperature"); + System.out.println("The Temperature property Id is " + tempPropId); + }); + } + + @Test + @Tag("IntegrationTest") + @Order(5) + public void testSendProps() { + assertDoesNotThrow(() -> { + sitewiseActions.sendDataToSiteWiseAsync(assetId, tempPropId, humPropId).join(); + }); + } + + @Test + @Tag("IntegrationTest") + @Order(6) + public void testGETHumValue() { + assertDoesNotThrow(() -> { + sitewiseActions.getAssetPropValueAsync(humPropId, assetId); + }); + } + + @Test + @Tag("IntegrationTest") + @Order(7) + public void testCreatePortal() { + assertDoesNotThrow(() -> { + portalId = sitewiseActions.createPortalAsync(portalName, iamRole, contactEmail).join(); + assertNotNull(portalId); + }); + } + + @Test + @Tag("IntegrationTest") + @Order(8) + public void testDescribePortal() { + assertDoesNotThrow(() -> { + String portalUrl = sitewiseActions.describePortalAsync(portalId).join(); + assertNotNull(portalUrl); + }); + } + + @Test + @Tag("IntegrationTest") + @Order(9) + public void testCreateGateway() { + assertDoesNotThrow(() -> { + gatewayId = sitewiseActions.createGatewayAsync(gatewayName, myThing).join(); + assertNotNull(gatewayId); + }); + } + + @Test + @Tag("IntegrationTest") + @Order(10) + public void testDescribeGateway() { + assertDoesNotThrow(() -> { + sitewiseActions.describeGatewayAsync(gatewayId).join(); + }); + } + + @Test + @Tag("IntegrationTest") + @Order(11) + public void testDeletePortal() throws InterruptedException { + Thread.sleep(30000); + assertDoesNotThrow(() -> { + sitewiseActions.deletePortalAsync(portalId).join(); + }); + } + + @Test + @Tag("IntegrationTest") + @Order(12) + public void testDeleteAsset() throws InterruptedException { + Thread.sleep(30000); + assertDoesNotThrow(() -> { + sitewiseActions.deleteAssetAsync(assetId).join(); + }); + } + @Test + @Tag("IntegrationTest") + @Order(12) + public void testDeleteAssetModel() throws InterruptedException { + Thread.sleep(30000); + assertDoesNotThrow(() -> { + sitewiseActions.deleteAssetModelAsync(assetModelId).join(); + }); + CloudFormationHelper.destroyCloudFormationStack(ROLES_STACK); + } + + private static String getSecretValues() { + SecretsManagerClient secretClient = SecretsManagerClient.builder() + .region(Region.US_EAST_1) + .credentialsProvider(EnvironmentVariableCredentialsProvider.create()) + .build(); + String secretName = "test/sitewise"; + + GetSecretValueRequest valueRequest = GetSecretValueRequest.builder() + .secretId(secretName) + .build(); + + GetSecretValueResponse valueResponse = secretClient.getSecretValue(valueRequest); + return valueResponse.secretString(); + } + + @Nested + @DisplayName("A class used to get test values from test/sitewise (an AWS Secrets Manager secret)") + class SecretValues { + private String contactEmail; + + private String assetModelHello; + + public String getContactEmail() { + return this.contactEmail; + } + + public String getAssetModelHello() { + return this.assetModelHello; + } + } +} +