diff --git a/blog/index.html b/blog/index.html index c383680..c68d672 100644 --- a/blog/index.html +++ b/blog/index.html @@ -1,7 +1,7 @@ -Blog | WLDT

Blog

\ No newline at end of file diff --git a/blog/index.xml b/blog/index.xml index 9b8595b..d7e3a0b 100644 --- a/blog/index.xml +++ b/blog/index.xml @@ -1 +1 @@ -Blog on WLDThttps://wldt.github.io/blog/Recent content in Blog on WLDTHugo -- gohugo.ioenCopyright (c) 2023 HyasThu, 07 Sep 2023 16:21:44 +0200ls WLDT Library Version 0.3.0https://wldt.github.io/blog/ls-wldt-library-version-0.3.0/Wed, 13 Mar 2024 16:27:22 +0200https://wldt.github.io/blog/ls-wldt-library-version-0.3.0/📣 We’re thrilled to announce the release of version 0.3.0 of the White Label Digital Twins (WLDT) library! This release brings significant enhancements, improvements, and new features to further empower developers in designing, developing, and deploying Digital Twins within Internet of Things (IoT) ecosystems. \ No newline at end of file +Blog on WLDThttps://wldt.github.io/blog/Recent content in Blog on WLDTHugo -- gohugo.ioenCopyright (c) 2023 HyasThu, 07 Sep 2023 16:21:44 +0200WLDT Library Version 0.4.0https://wldt.github.io/blog/wldt-library-version-0.4.0/Thu, 29 Aug 2024 17:40:54 +0200https://wldt.github.io/blog/wldt-library-version-0.4.0/We’re excited to announce the release of WLDT version 0.4.0! This update brings powerful new features to enhance your Digital Twin (DT) experience, including event observation capabilities, a robust storage layer, and a flexible query system.WLDT Library Version 0.3.0https://wldt.github.io/blog/wldt-library-version-0.3.0/Wed, 13 Mar 2024 16:27:22 +0200https://wldt.github.io/blog/wldt-library-version-0.3.0/📣 We’re thrilled to announce the release of version 0.3.0 of the White Label Digital Twins (WLDT) library! This release brings significant enhancements, improvements, and new features to further empower developers in designing, developing, and deploying Digital Twins within Internet of Things (IoT) ecosystems. \ No newline at end of file diff --git a/blog/sitemap.xml b/blog/sitemap.xml index fc8204e..23dceb9 100644 --- a/blog/sitemap.xml +++ b/blog/sitemap.xml @@ -1 +1 @@ -https://wldt.github.io/blog/ls-wldt-library-version-0.3.0/2024-03-15T10:45:36+01:00monthly0.5 \ No newline at end of file +https://wldt.github.io/blog/wldt-library-version-0.4.0/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/blog/wldt-library-version-0.3.0/2024-09-05T15:02:33+02:00monthly0.5 \ No newline at end of file diff --git a/blog/ls-wldt-library-version-0.3.0/index.html b/blog/wldt-library-version-0.3.0/index.html similarity index 68% rename from blog/ls-wldt-library-version-0.3.0/index.html rename to blog/wldt-library-version-0.3.0/index.html index 125cbca..0f4804e 100644 --- a/blog/ls-wldt-library-version-0.3.0/index.html +++ b/blog/wldt-library-version-0.3.0/index.html @@ -1,9 +1,9 @@ -ls WLDT Library Version 0.3.0 | WLDT

WLDT Library Version 0.3.0

March 13, 2024 ‐ 3 min read

📣 We’re thrilled to announce the release of version 0.3.0 of the White Label Digital Twins (WLDT) library! This release brings significant enhancements, improvements, and new features to further empower developers in designing, developing, and deploying Digital Twins within Internet of Things (IoT) ecosystems.

For detailed information about these changes and their impact, please refer to the information provided:

Let’s dive into the key changes and updates included in this release:

Migration Digital Adapters

In version 0.3.0, we’ve made several enhancements and adjustments to the Digital Adapter class to improve its functionality and usability. Notable changes include:

  • Discontinued Methods: Several methods have been discontinued and removed from the DigitalAdapter class to streamline its interface and improve clarity. Method Signature Changes: The signatures of certain methods have been updated for consistency and clarity, ensuring a more intuitive developer experience.
  • New Methods: We’ve introduced new methods to provide additional functionality and flexibility for handling state updates and event notifications.

Migration Shadowing Function

We’ve made significant improvements to the ShadowingModelFunction, which is now renamed to ShadowingFunction. Additionally, we’ve introduced changes to how the DigitalTwinState is managed within the Shadowing Function, providing developers with more control and flexibility.

Migrating WLDT Engine & DT Creation

In version 0.3.0, the WldtEngine class has been renamed to DigitalTwin, offering improved clarity and consistency. We’ve also made adjustments to the lifecycle management of Digital Twins, streamlining the process and enhancing usability.

Digital Twin & Digital Twin Engine

We’ve introduced enhancements to the Digital Twin and Digital Twin Engine classes, providing developers with improved functionality and ease of use. Notable updates include:

  • Simplified Digital Twin Creation: Creating and managing Digital Twins is now more intuitive and streamlined.
  • Lifecycle Management: We’ve enhanced the lifecycle management of Digital Twins, making it easier to start, stop, and manage multiple instances.

Digital Twin State Manager

The DigitalTwinStateManager class has been improved to provide better support for managing the state of Digital Twins. With features such as transaction support and event notification, developers can more effectively manage changes to Digital Twin states and respond to events.

To learn more about the capabilities of the DigitalTwinStateManager, please refer to the Digital Twin State Manager section.

Digital Adapter

We’ve extended and improved the Digital Adapter base class to provide enhanced support for handling Digital Twin state updates and event notifications. With the introduction of the onStateUpdate and onEventNotificationReceived methods, developers can more effectively respond to changes in Digital Twin states and events.

Get Started with WLDT 0.3.0

To get started with version 0.3.0 of the WLDT library, simply update your dependencies to include the latest release. Detailed documentation and usage examples are available in the project repository, providing comprehensive guidance on leveraging the new features and enhancements.

We’re excited about the improvements and new capabilities introduced in WLDT 0.3.0, and we can’t wait to see how developers utilize them to create innovative IoT solutions powered by Digital Twins. As always, we welcome your feedback and contributions to help us further improve the library and empower the community.

Happy coding with WLDT 0.3.0! 🚀

\ No newline at end of file diff --git a/blog/wldt-library-version-0.4.0/index.html b/blog/wldt-library-version-0.4.0/index.html new file mode 100644 index 0000000..2dac31a --- /dev/null +++ b/blog/wldt-library-version-0.4.0/index.html @@ -0,0 +1,7 @@ + +WLDT Library Version 0.4.0 | WLDT

WLDT Library Version 0.4.0

August 29, 2024 ‐ 1 min read

We’re excited to announce the release of WLDT version 0.4.0! This update brings powerful new features to enhance your Digital Twin (DT) experience, including event observation capabilities, a robust storage layer, and a flexible query system.

For detailed information about these changes and their impact, please refer to the information provided:

Key Highlights

  • WldtEventObserver: A new class has been introduced, simplifying the observation of specific events generated by Digital Twins and their components
  • Storage Layer: The new storage layer allows DTs to store data related to their state, events, actions, and more. It consists of:
    • Storage Manager: Manage and use various storage systems (e.g., in-memory, file-based, DBMS) simultaneously.
    • WldtStorage: An abstract class to implement custom storage systems. The default in-memory storage is available for development and testing.
    • Query System: The query system enables external components like Digital Adapters to retrieve stored data efficiently, supporting both synchronous and asynchronous queries.

WLDT 0.4.0 significantly enhances the flexibility and capabilities of Digital Twins, making it easier to manage and retrieve data, and observe events. We encourage developers to explore these new features and integrate them into their projects.

Stay tuned for more updates!

+
\ No newline at end of file diff --git a/docs/adapters/http-digital-adapter/index.html b/docs/adapters/http-digital-adapter/index.html index 8371e25..ced853c 100644 --- a/docs/adapters/http-digital-adapter/index.html +++ b/docs/adapters/http-digital-adapter/index.html @@ -2,23 +2,23 @@ HTTP Digital Adapter | WLDT

HTTP Digital Adapter

The HttpDigitalAdapter is a powerful component designed to facilitate the integration of Digital Twins into HTTP-based systems. It serves as a bridge between a Digital Twin and HTTP-based applications, allowing developers to easily expose and interact with -Digital Twin data and functionalities over HTTP.

Key Features:

  • HTTP Integration: Seamlessly integrates Digital Twins into HTTP environments, enabling communication with web applications and services.
  • Dynamic Configuration: Offers a flexible configuration mechanism through the HttpDigitalAdapterConfiguration, allowing developers to customize the adapter’s behavior based on specific requirements.
  • State Monitoring: Monitors changes in the Digital Twin state and provides HTTP endpoints to query the state of the Digital Twin (properties, events, actions and relationships).
  • Event Notifications: Allows developers to retrieve event notifications triggered by changes in the Digital Twin state.

HTTP Digital Adapter

A complete example is provided in the test folder with a complete DT Creation in the TestMain class together with a demo DT with and emulated Physical Adapter and the HTTP Digital Adapter.

WLDT-Core Version Compatibility

The correct mapping and compatibility between versions is reported in the following table

http-digital-adapterwldt-core 0.2.1wldt-core 0.3.0
0.1.1

Installation

To use HttpDigitalAdapter in your Java project, you can include it as a dependency using Maven or Gradle.

Maven

<dependency>
+Digital Twin data and functionalities over HTTP.

Key Features:

  • HTTP Integration: Seamlessly integrates Digital Twins into HTTP environments, enabling communication with web applications and services.
  • Dynamic Configuration: Offers a flexible configuration mechanism through the HttpDigitalAdapterConfiguration, allowing developers to customize the adapter’s behavior based on specific requirements.
  • State Monitoring: Monitors changes in the Digital Twin state and provides HTTP endpoints to query the state of the Digital Twin (properties, events, actions and relationships).
  • Event Notifications: Allows developers to retrieve event notifications triggered by changes in the Digital Twin state.
  • Storage & Query: Since version 0.2 the HTTP Digital Adapter is able to retrieve Storage Statistics and execute query on the target DT

HTTP Digital Adapter

A complete example is provided in the test folder with a complete DT Creation in the TestMain class together with a demo DT with and emulated Physical Adapter and the HTTP Digital Adapter.

WLDT-Core Version Compatibility

The correct mapping and compatibility between versions is reported in the following table

http-digital-adapterwldt-core 0.2.1wldt-core 0.3.0wldt-core 0.4.0
0.1.1
0.2

Installation

To use HttpDigitalAdapter in your Java project, you can include it as a dependency using Maven or Gradle.

Maven

<dependency>
     <groupId>io.github.wldt</groupId>
     <artifactId>http-digital-adapter</artifactId>
-    <version>0.1.1</version>
+    <version>0.2</version>
 </dependency>
-

Gradle

implementation 'io.github.wldt:http-digital-adapter:0.1.1'
+

Gradle

implementation 'io.github.wldt:http-digital-adapter:0.2'
 

Class Structure & Functionalities

HttpDigitalAdapterConfiguration

The HttpDigitalAdapterConfiguration is a crucial part of the HttpDigitalAdapter, providing the necessary settings to tailor the adapter’s behavior to meet specific needs.

Represents the configuration for an HTTP Digital Adapter, specifying the host, port, and filters for properties, actions, events, and relationships.

The filters are used to selectively include or exclude specific properties, actions, @@ -66,6 +66,66 @@ digitalTwinEngine.startAll();

HTTP RESTful API

This section of the documentation provides detailed information about the RESTful API exposed by the WLDT - HTTP Digital Adapter. The API allows you to interact with the Digital Twin (DT) instance, retrieve its state, read properties, actions, event and relationships description, -and trigger actions.

Available endpoints with the associated methods are:

  • GET /instance: Retrieves information about the Digital Twin instance.
  • GET /state: Retrieves the current state of the Digital Twin.
  • GET /state/changes: Retrieves the list of state changes in the Digital Twin.
  • GET /state/previous: Retrieves the previous state of the Digital Twin.
  • GET /state/properties: Retrieves the list of properties in the Digital Twin state.
  • GET /properties/{propertyKey}: Retrieves the value of a specific property (e.g., /properties/color) from the Digital Twin state.
  • GET /state/events: Retrieves the list of events in the Digital Twin state.
  • GET /state/actions: Retrieves the list of actions in the Digital Twin state.
  • POST /state/actions/{actionKey}: Triggers the specified action (e.g., /state/actions/switch_on) in the Digital Twin state. The raw body contains the action request payload.
  • GET /state/relationships: Retrieves the list of relationships in the Digital Twin state.
  • GET /state/relationships/{relationshipName}/instances: Retrieves the instances of the specified relationship (e.g., /state/relationships/insideIn/instances) in the Digital Twin state.

Note: Replace {propertyKey}, {actionKey}, and {relationshipName} with the actual values you want to retrieve or trigger. -Make sure to use the appropriate HTTP method (GET, POST) and include any required parameters or payload as described in each endpoint’s description. For more detailed information, refer to the Postman Collection for this API available in the folder api: http_adapter_api_postman.json

+and trigger actions.

Available endpoints with the associated methods are:

Note: Replace {propertyKey}, {actionKey}, and {relationshipName} with the actual values you want to retrieve or trigger. +Make sure to use the appropriate HTTP method (GET, POST) and include any required parameters or payload as described in each endpoint’s description. For more detailed information, refer to the Postman Collection for this API available in the folder api: http_adapter_api_postman.json

Example of Storage Query Requests are the following:

Retrieve the first 4 Digital Twin State Variations

{
+    "resourceType": "DIGITAL_TWIN_STATE",
+    "queryType": "SAMPLE_RANGE",
+    "startIndex": 0,
+    "endIndex": 3
+}
+

Retrieve Digital Twin State Variations in a Time Range

{
+    "resourceType": "DIGITAL_TWIN_STATE",
+    "queryType": "TIME_RANGE",
+    "startIndex": 161989898,
+    "endIndex": 162989898
+}
+

Retrieve the last Digital Twin State

{
+    "resourceType": "DIGITAL_TWIN_STATE",
+    "queryType": "LAST_VALUE"
+}
+

Available keywords for Query Resource Type and Query Type are the following (as explained in the dedicated Query System Page):

- PHYSICAL_ASSET_PROPERTY_VARIATION
+	- TIME_RANGE
+	- SAMPLE_RANGE
+	- COUNT
+- PHYSICAL_ASSET_EVENT_NOTIFICATION
+	- TIME_RANGE
+	- SAMPLE_RANGE
+	- COUNT
+- PHYSICAL_ACTION_REQUEST
+	- TIME_RANGE
+	- SAMPLE_RANGE
+	- COUNT
+- DIGITAL_ACTION_REQUEST
+	- TIME_RANGE
+	- SAMPLE_RANGE
+	- COUNT
+- DIGITAL_TWIN_STATE
+	- TIME_RANGE
+	- SAMPLE_RANGE
+	- COUNT
+	- LAST_VALUE
+- NEW_PAD_NOTIFICATION
+	- TIME_RANGE
+	- SAMPLE_RANGE
+	- COUNT
+- UPDATED_PAD_NOTIFICATION
+	- TIME_RANGE
+	- SAMPLE_RANGE
+	- COUNT
+- PHYSICAL_RELATIONSHIP_INSTANCE_CREATED_NOTIFICATION
+	- TIME_RANGE
+	- SAMPLE_RANGE
+	- COUNT
+- PHYSICAL_RELATIONSHIP_INSTANCE_DELETED_NOTIFICATION
+	- TIME_RANGE
+	- SAMPLE_RANGE
+	- COUNT
+- LIFE_CYCLE_EVENT
+	- TIME_RANGE
+	- SAMPLE_RANGE
+	- COUNT
+	- LAST_VALUE
+- STORAGE_STATS
+	- LAST_VALUE
+
\ No newline at end of file diff --git a/docs/adapters/index.html b/docs/adapters/index.html index ac64c4d..08ac96a 100644 --- a/docs/adapters/index.html +++ b/docs/adapters/index.html @@ -2,10 +2,10 @@ Adapters | WLDT
\ No newline at end of file diff --git a/docs/adapters/mqtt-digital-adapter/index.html b/docs/adapters/mqtt-digital-adapter/index.html index 0d754ff..f77e55f 100644 --- a/docs/adapters/mqtt-digital-adapter/index.html +++ b/docs/adapters/mqtt-digital-adapter/index.html @@ -5,17 +5,17 @@ MqttDigitalAdapterConfiguration, and MqttDigitalAdapterConfigurationBuilder classes and guides you through using these classes to set up an MQTT Digital Adapter within WLDT.">

MQTT Digital Adapter

The MqttDigitalAdapter,
MqttDigitalAdapterConfiguration, and MqttDigitalAdapterConfigurationBuilder classes and guides you through using these classes to set up an MQTT Digital Adapter within WLDT.

Requires an external MQTT broker to send messages.

Main functionalities are:

  • Manages the interaction between the Digital Twin and external systems.
  • Handles state updates, events, and property changes.
  • Dynamic configuration of the MqttDigitalAdapter with broker details, topics, and other settings.
  • Allows customization of data and payload management associated to MQTT topics for properties, events, and actions.

MQTT Digital Adapter

Prerequisites:

  • External MQTT Broker: The MqttDigitalAdapter library requires an external MQTT broker for optimal functionality and communication.
  • Users must have access to a reliable MQTT broker to which the adapter can subscribe and publish.
  • This external broker serves as the central communication hub, facilitating the exchange of messages between the adapter and digital applications

A complete example is provided in the test folder with a complete DT Creation in the TestMain class together with MQTT IoT demo device and -a test MQTT consumer.

WLDT-Core Version Compatibility

The correct mapping and compatibility between versions is reported in the following table

mqtt-digital-adapterwldt-core 0.2.1wldt-core 0.3.0
0.1.0
0.1.1

Installation

To use MqttDigitalAdapter in your Java project, you can include it as a dependency using Maven or Gradle.

Maven

<dependency>
+Change Logs

MQTT Digital Adapter

The MqttDigitalAdapter,
MqttDigitalAdapterConfiguration, and MqttDigitalAdapterConfigurationBuilder classes and guides you through using these classes to set up an MQTT Digital Adapter within WLDT.

Requires an external MQTT broker to send messages.

Main functionalities are:

  • Manages the interaction between the Digital Twin and external systems.
  • Handles state updates, events, and property changes.
  • Dynamic configuration of the MqttDigitalAdapter with broker details, topics, and other settings.
  • Allows customization of data and payload management associated to MQTT topics for properties, events, and actions.

MQTT Digital Adapter

Prerequisites:

  • External MQTT Broker: The MqttDigitalAdapter library requires an external MQTT broker for optimal functionality and communication.
  • Users must have access to a reliable MQTT broker to which the adapter can subscribe and publish.
  • This external broker serves as the central communication hub, facilitating the exchange of messages between the adapter and digital applications

A complete example is provided in the test folder with a complete DT Creation in the TestMain class together with MQTT IoT demo device and +a test MQTT consumer.

WLDT-Core Version Compatibility

The correct mapping and compatibility between versions is reported in the following table

mqtt-digital-adapterwldt-core 0.2.1wldt-core 0.3.0wldt-core 0.4.0
0.1.0
0.1.1

Installation

To use MqttDigitalAdapter in your Java project, you can include it as a dependency using Maven or Gradle.

Maven

<dependency>
     <groupId>io.github.wldt</groupId>
     <artifactId>mqtt-digital-adapter</artifactId>
     <version>0.1.1</version>
@@ -55,5 +55,5 @@
 
 digitalTwinEngine.addDigitalTwin(digitalTwin);
 digitalTwinEngine.startAll();
-
+
\ No newline at end of file diff --git a/docs/adapters/mqtt-physical-adapter/index.html b/docs/adapters/mqtt-physical-adapter/index.html index a17b834..4d59227 100644 --- a/docs/adapters/mqtt-physical-adapter/index.html +++ b/docs/adapters/mqtt-physical-adapter/index.html @@ -2,22 +2,22 @@ MQTT Physical Adapter | WLDT

MQTT Physical Adapter

The MqttPhysicalAdapter library provides a streamlined solution for efficiently managing physical assets through the MQTT protocol. It offers a range of features, including a versatile builder for effortless configuration of MQTT connections, dedicated classes for handling both incoming and outgoing topics, and a specialized adapter designed for seamless integration with diverse physical assets.

Key Features:

  • Builder for MQTT Configuration: The library incorporates a flexible builder pattern, enabling users to effortlessly configure the essential parameters of the MQTT connection. This includes specifying the MQTT broker’s address, port, and other relevant details to establish a reliable and customizable communication link.
  • Incoming and Outgoing Topic Handling: MqttPhysicalAdapter facilitates the handling of incoming and outgoing topics, crucial for communication between the physical assets and the MQTT broker. The library includes dedicated classes for defining and managing topics, allowing users to efficiently subscribe to incoming data and publish outgoing messages.
  • Adapter for Physical Asset Integration: At the core of the library is a robust adapter designed specifically for integrating with various physical assets. This adapter streamlines the process of connecting and interacting with physical devices, ensuring a smooth and standardized approach to managing asset-related data.

In the WLDT library, Physical Adapters has the responsibility to generate and publish the PhysicalAssetDescription (PAD) to describe the capabilities and the characteristics of our object allowing the Shadowing Function to decide how to digitalize its physical counterpart.

In the MqttPhysicalAdapter the generation of the PAD (Physical Asset Description) is automatically and internally executed by the adapter itself accordingly to the adapter configuration in terms of MQTT topics and their mapping with DT’s properties, events and actions.

MQTT Physical Adapter

Prerequisites:

  • External MQTT Broker: The MqttPhysicalAdapter library requires an external MQTT broker for optimal functionality.
  • Users must have access to a reliable MQTT broker to which the adapter can subscribe.
  • This external broker serves as the central communication hub, facilitating the exchange of messages between the adapter and the physical assets.

A complete example is provided in the test folder with a complete DT Creation in the TestMain class together with MQTT IoT demo device and -a test MQTT consumer.

WLDT-Core Version Compatibility

The correct mapping and compatibility between versions is reported in the following table

mqtt-physical-adapterwldt-core 0.2.1wldt-core 0.3.0
0.1.0
0.1.1

Installation

To use MqttPhysicalAdapter in your Java project, you can include it as a dependency using Maven or Gradle.

Maven

<dependency>
+a test MQTT consumer.

WLDT-Core Version Compatibility

The correct mapping and compatibility between versions is reported in the following table

mqtt-physical-adapterwldt-core 0.2.1wldt-core 0.3.0wldt-core 0.4.0
0.1.0
0.1.1

Installation

To use MqttPhysicalAdapter in your Java project, you can include it as a dependency using Maven or Gradle.

Maven

<dependency>
     <groupId>io.github.wldt</groupId>
     <artifactId>mqtt-physical-adapter</artifactId>
     <version>0.1.1</version>
@@ -78,5 +78,5 @@
         return properties;
     }
 

This information are used by the adapter to build the PAD describe the capabilities and the characteristics of our object allowing -the Shadowing Function to decide how to digitalize its physical counterpart.

+the Shadowing Function to decide how to digitalize its physical counterpart.

\ No newline at end of file diff --git a/docs/adapters/sitemap.xml b/docs/adapters/sitemap.xml index 3f568e1..8f03aa0 100644 --- a/docs/adapters/sitemap.xml +++ b/docs/adapters/sitemap.xml @@ -1 +1 @@ -https://wldt.github.io/docs/adapters/mqtt-physical-adapter/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/docs/adapters/mqtt-digital-adapter/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/docs/adapters/http-digital-adapter/2024-03-15T10:45:36+01:00monthly0.5 \ No newline at end of file +https://wldt.github.io/docs/adapters/mqtt-physical-adapter/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/adapters/mqtt-digital-adapter/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/adapters/http-digital-adapter/2024-09-05T15:02:33+02:00monthly0.5 \ No newline at end of file diff --git a/docs/change-logs/change-log-0.3.0/index.html b/docs/change-logs/change-log-0.3.0/index.html index c93cd8b..17811cf 100644 --- a/docs/change-logs/change-log-0.3.0/index.html +++ b/docs/change-logs/change-log-0.3.0/index.html @@ -2,16 +2,16 @@ Change Log 0.3.0 | WLDT

Change Log 0.3.0

Digital Adapters

  • The following methods have been discontinued and removed from the DigitalAdapter class:
    • onStateChangePropertyCreated
    • onStateChangePropertyUpdated
    • onStateChangePropertyDeleted
    • onStatePropertyUpdated
    • onStatePropertyDeleted
    • onStateChangeActionEnabled
    • onStateChangeActionUpdated
    • onStateChangeActionDisabled
    • onStateChangeEventRegistered
    • onStateChangeEventRegistrationUpdated
    • onStateChangeEventUnregistered
    • onStateChangeRelationshipInstanceDeleted
    • onStateChangeRelationshipDeleted
    • onStateChangeRelationshipInstanceCreated
    • onStateChangeRelationshipCreated
    • onDigitalTwinStateEventNotificationReceived
  • The Signature of the following methods have been changed:
    • onDigitalTwinSync(IDigitalTwinState currentDigitalTwinState) -> onDigitalTwinSync(DigitalTwinState currentDigitalTwinState)
    • onDigitalTwinUnSync(IDigitalTwinState currentDigitalTwinState) -> onDigitalTwinUnSync(DigitalTwinState currentDigitalTwinState)
  • New methods that have been added are:
    • onStateUpdate(DigitalTwinState newDigitalTwinState, DigitalTwinState previousDigitalTwinState, ArrayList<DigitalTwinStateChange> digitalTwinStateChangeList)
    • onEventNotificationReceived(DigitalTwinStateEventNotification<?> digitalTwinStateEventNotification)
  • For additional details about Digital Adapters check Sub Section [[Change Log - v.0.3.0#Digital Adapter| Digital Adapters]]

Shadowing Function

  • ShadowingModelFunction is now ShadowingFunction
  • this.digitalTwinState is not directly accessible anymore and it is wrapped through the DigitalTwinStateManager using the variable digitalTwinStateManager (see next descriptions and changes)
  • The method addRelationshipInstance now take only one parameter that is the DigitalTwinStateRelationshipInstance
  • The same change for example should be applied in the point of the code where the Shadowing Function receive a variation from the Physical world through a target adapter and the callback method onPhysicalAssetPropertyVariation(...)
  • When the Shadowing Function has to compute the new DT State it can now work with the following method to handle DT State Transition:
    • this.digitalTwinStateManager.startStateTransaction()
    • DT State variation methods such as:
      • digitalTwinStateManager.createProperty()
      • digitalTwinStateManager.updateProperty()
      • digitalTwinStateManager.updatePropertyValue()
      • digitalTwinStateManager.deleteProperty()
      • digitalTwinStateManager.enableAction()
      • digitalTwinStateManager.updateAction()
      • digitalTwinStateManager.disableAction()
      • digitalTwinStateManager.registerEvent()
      • digitalTwinStateManager.updateRegisteredEvent()
      • digitalTwinStateManager.unRegisterEvent()
      • digitalTwinStateManager.createRelationship()
      • digitalTwinStateManager.addRelationshipInstance()
      • digitalTwinStateManager.deleteRelationship()
      • digitalTwinStateManager.deleteRelationshipInstance()
    • At the end the transaction can be committed using the method: digitalTwinStateManager.commitStateTransaction()
    • The method notifyDigitalTwinStateEvent is now available through the digitalTwinStateManager
  • Additional Details associated to Shadowing Function Migration can be found in the dedicated section [[Change Log - v.0.3.0#Shadowing Function Changes | Shadowing Function Changes]]

WLDT Engine & DT Creation

  • WldtEngine is now DigitalTwin and model and structure a single Digital Twin and takes the following parameters:
    • String digitalTwinId
    • ShadowingFunction shadowingFunction
  • The startLifeCycle has been removed from the DigitalTwin (previously WLDT Engine) and now DigitalTwinEngine should be used to start twins
  • Once a new Digital Twin has been create it has to be added to the DigitalTwinEngine
  • DigitalTwinEngine has dedicated method to start and stop twins such as:
    • startAll()
    • startDigitalTwin(<DIGITAL_TWIN_ID>);
    • stopAll()
    • digitalTwinEngine.stopDigitalTwin(<DIGITAL_TWIN_ID>);

Digital Twin & Digital Twin Engine

With the following code we now create a new Digital Twin Instance

// Create the new Digital Twin with its Shadowing Function  
+Change Logs

Change Log 0.3.0

Digital Adapters

  • The following methods have been discontinued and removed from the DigitalAdapter class:
    • onStateChangePropertyCreated
    • onStateChangePropertyUpdated
    • onStateChangePropertyDeleted
    • onStatePropertyUpdated
    • onStatePropertyDeleted
    • onStateChangeActionEnabled
    • onStateChangeActionUpdated
    • onStateChangeActionDisabled
    • onStateChangeEventRegistered
    • onStateChangeEventRegistrationUpdated
    • onStateChangeEventUnregistered
    • onStateChangeRelationshipInstanceDeleted
    • onStateChangeRelationshipDeleted
    • onStateChangeRelationshipInstanceCreated
    • onStateChangeRelationshipCreated
    • onDigitalTwinStateEventNotificationReceived
  • The Signature of the following methods have been changed:
    • onDigitalTwinSync(IDigitalTwinState currentDigitalTwinState) -> onDigitalTwinSync(DigitalTwinState currentDigitalTwinState)
    • onDigitalTwinUnSync(IDigitalTwinState currentDigitalTwinState) -> onDigitalTwinUnSync(DigitalTwinState currentDigitalTwinState)
  • New methods that have been added are:
    • onStateUpdate(DigitalTwinState newDigitalTwinState, DigitalTwinState previousDigitalTwinState, ArrayList<DigitalTwinStateChange> digitalTwinStateChangeList)
    • onEventNotificationReceived(DigitalTwinStateEventNotification<?> digitalTwinStateEventNotification)
  • For additional details about Digital Adapters check Sub Section [[Change Log - v.0.3.0#Digital Adapter| Digital Adapters]]

Shadowing Function

  • ShadowingModelFunction is now ShadowingFunction
  • this.digitalTwinState is not directly accessible anymore and it is wrapped through the DigitalTwinStateManager using the variable digitalTwinStateManager (see next descriptions and changes)
  • The method addRelationshipInstance now take only one parameter that is the DigitalTwinStateRelationshipInstance
  • The same change for example should be applied in the point of the code where the Shadowing Function receive a variation from the Physical world through a target adapter and the callback method onPhysicalAssetPropertyVariation(...)
  • When the Shadowing Function has to compute the new DT State it can now work with the following method to handle DT State Transition:
    • this.digitalTwinStateManager.startStateTransaction()
    • DT State variation methods such as:
      • digitalTwinStateManager.createProperty()
      • digitalTwinStateManager.updateProperty()
      • digitalTwinStateManager.updatePropertyValue()
      • digitalTwinStateManager.deleteProperty()
      • digitalTwinStateManager.enableAction()
      • digitalTwinStateManager.updateAction()
      • digitalTwinStateManager.disableAction()
      • digitalTwinStateManager.registerEvent()
      • digitalTwinStateManager.updateRegisteredEvent()
      • digitalTwinStateManager.unRegisterEvent()
      • digitalTwinStateManager.createRelationship()
      • digitalTwinStateManager.addRelationshipInstance()
      • digitalTwinStateManager.deleteRelationship()
      • digitalTwinStateManager.deleteRelationshipInstance()
    • At the end the transaction can be committed using the method: digitalTwinStateManager.commitStateTransaction()
    • The method notifyDigitalTwinStateEvent is now available through the digitalTwinStateManager
  • Additional Details associated to Shadowing Function Migration can be found in the dedicated section [[Change Log - v.0.3.0#Shadowing Function Changes | Shadowing Function Changes]]

WLDT Engine & DT Creation

  • WldtEngine is now DigitalTwin and model and structure a single Digital Twin and takes the following parameters:
    • String digitalTwinId
    • ShadowingFunction shadowingFunction
  • The startLifeCycle has been removed from the DigitalTwin (previously WLDT Engine) and now DigitalTwinEngine should be used to start twins
  • Once a new Digital Twin has been create it has to be added to the DigitalTwinEngine
  • DigitalTwinEngine has dedicated method to start and stop twins such as:
    • startAll()
    • startDigitalTwin(<DIGITAL_TWIN_ID>);
    • stopAll()
    • digitalTwinEngine.stopDigitalTwin(<DIGITAL_TWIN_ID>);

Digital Twin & Digital Twin Engine

With the following code we now create a new Digital Twin Instance

// Create the new Digital Twin with its Shadowing Function  
 DigitalTwin digitalTwin = new DigitalTwin(digitalTwinId, new DemoShadowingFunction());  
   
 // Physical Adapter with Configuration  
@@ -235,5 +235,5 @@
         System.out.println("No state changes detected.");
     }
 }
-

In this example, the method iterates over the list of state changes, extracts information about each change, and performs custom actions based on the changes. Developers can adapt this method to suit the specific requirements of their Digital Twin application.

+

In this example, the method iterates over the list of state changes, extracts information about each change, and performs custom actions based on the changes. Developers can adapt this method to suit the specific requirements of their Digital Twin application.

\ No newline at end of file diff --git a/docs/change-logs/change-log-0.4.0/index.html b/docs/change-logs/change-log-0.4.0/index.html new file mode 100644 index 0000000..e3c4231 --- /dev/null +++ b/docs/change-logs/change-log-0.4.0/index.html @@ -0,0 +1,140 @@ + +Change Log 0.4.0 | WLDT

Change Log 0.4.0

New Features

WldtEventObserver

A new class called WldtEventObserver has been introduced to allow a simplified observation of target specific events generated by the Digital Twin and its components such as adapters and the model. Main mapped events and filters are:

  • State Events: State Update and State Event Notifications
  • Physical Asset Events: Physical Property Variation, Physical Event Notification, Physical Relationship Instance Creation and Deletion
  • Physical Asset Action Events: Physical Action Trigger
  • Digital Action Events: Digital Action Event
  • Physical Asset Description Events: Physical Asset Description Available and Updated
  • Life Cycle Events: Digital Twin Life Cycle Events
  • Query Request Events: Storage Query Request Events (See next sections for additional information)

For each event type dedicated observation and un-observation methods (e.g., observePhysicalAssetEvents() and unObservePhysicalAssetEvents()) are available in order to create an instance of the observer and decide which events to receive.

To build a WldtEventObserver a dedicated listener IWldtEventObserverListener should be implemented by the developer to receive the callbacks related to the incoming events. All the events are of the generic type WldtEvent and it is up to the developer the validate and check the received object and if it match with the expected one.

An example of usage for the event observer is the following:

WldtEventObserver eventObserver = new WldtEventObserver(  
+        "DT_TEST_ID_1",  
+        "test-observer",  
+        myObserverListener);  
+  
+// Start all the available observation  
+eventObserver.observePhysicalAssetEvents();  
+eventObserver.observePhysicalAssetActionEvents();  
+eventObserver.observeStateEvents();  
+eventObserver.observeDigitalActionEvents();  
+eventObserver.observePhysicalAssetDescriptionEvents();  
+eventObserver.observeLifeCycleEvents();
+

The WldtEventObserver has been currently used internally within the library to simplify the implementation and usage of the Storage Layer and the associated Storage Query System as described in the dedicated sections.

Storage Layer

A new storage layer has been integrated into the core WLDT library, enabling Digital Twins (DTs) to store data related to the evolution of their state, generated events, and any variations involving properties, events, actions, relationships, and life cycle. The Storage Layer consists of two main components:

  • Storage Manager: This is the central component of the storage system, facilitating the structured and modular storage and retrieval of information. It allows developers to create and utilize various storage systems (e.g., in-memory, file-based, or DBMS) simultaneously. The Storage Layer is accessible in both read and write modes internally by the DT’s Model, and in read-only mode via the Query System by Digital Adapters.
  • Query System: To delegate and encapsulate the responsibility of data storage within the DT’s model, a query system has been integrated. This system enables Digital Adapters to retrieve stored data and expose it according to their specific logic and implementation.

The storage layer is designed for easy extension, allowing developers to create and share new storage layers (e.g., using Redis, MySQL, or MongoDB). The provided in-memory implementation serves only for basic development and testing purposes. Similarly, the Query Manager can be extended and customized by developers to implement additional query management features or to enhance the default functionalities provided by the library.

Storage Manager

The main module of the Storage Layer is the one associated to Storage Capabilities and it is composed by two main classes: StorageManager and WldStorage with the following characteristics and main methods:

  • StorageManager: The StorageManager class is a class that represents the storage manager for a DigitalTwin. It is responsible for managing the storage of the data related to the DigitalTwin. It is an observer of the WldtEventBus, and it is able to save the data in the available storages. The class extends a DigitalTwinWorker, in order to allow the component to work in a structure and integrated way on a different thread that the core of a DT can coordinate starting and stopping it when required. The manager allow the usage of different storage systems at the same time in order to allow the developers to memorize the information accordingly to their need in the right storage system at the same time (e.g., REDIS for quick cached information and MongDB for historical data). Main associated methods are:
    • putStorage(WldtStorage storage): Add a new WldtStorage to the StorageManager
    • getStorageIdList(): Returns the list of id of the WldtStorage in the StorageManager
    • isStorageAvailable(String storageId): Checks if a target Storage Id is available in the Storage Manager
    • getStorage(String storageId): Get the target WldtStorage by id from the Storage Manager
    • removeStorage(String storageId): Remove an existing WldtStorage by id from the StorageManager
  • WldtStorage: Defines an abstract class allowing the Digital Twin developer to implement its internal storage system for the Digital Twin instance.
    • The class defines methods for the management of:
      • Digital Twin State storage and retrieval with the associated change list;
      • Generated State Digital Events;
      • Life Cycle State storage and retrieval;
      • Physical Asset Description storage and retrieval;
      • Physical Asset Property Variation storage and retrieval;
      • Physical Asset Relationship Instance storage and retrieval;
      • Digital Action Request storage and retrieval;
      • Physical Asset Action Request storage and retrieval;
      • Physical Asset Event Notification storage and retrieval;
    • Each WldtStorage instance can be configured (using the right constructor method) to:
      • Observe all Wldt events (stateEvents, physicalAssetEvents, physicalAssetActionEvents, physicalAssetDescriptionEvents, digitalActionEvents, lifeCycleEvents)
      • Filter only for specific class of events
      • Once the WldtStorage has been properly configured to receive target events the StorageManager automatically save information of interest for that specific storage. For example we can have a StorageA (e.g, REDIS) configured to receive all the generated events and a StorageB (e.g., MongoDB) in charge of saving only DT’s state variation over time.
    • The default implementation of the WldtStorage is the class DefaultWldtStorage. This class provides a simple storage solution for digital twin states, digital twin state changes, physical asset events, and digital twin events. The class provides ONLY a memory based approach for storage using ArrayLists and HashMaps and more advanced solution should be implemented for production oriented Digital Twins for examples using external storage and memorization solutions.
    • Methods available and implemented by WldtStorage implementations are the following grouped by categories:
      • Digital Twin State:
        • saveDigitalTwinState(DigitalTwinState digitalTwinState, List<DigitalTwinStateChange> digitalTwinStateChangeList): Save a new computed instance of the DT State in the Storage together with the list of the changes with respect to the previous state
        • getLastDigitalTwinState(): Returns the latest computed Digital Twin State of the target Digital Twin instance
        • getDigitalTwinStateCount(): Returns the number of computed and stored Digital Twin States
        • getDigitalTwinStateInTimeRange(long startTimestampMs, long endTimestampMs): Retrieves a list of DigitalTwinState objects within the specified time range
        • getDigitalTwinStateInRange(int startIndex, int endIndex): Retrieves a list of Digital Twin states within the specified range of indices
      • Digital Twin State Event Notification:
        • saveDigitalTwinStateEventNotification(DigitalTwinStateEventNotification<?> digitalTwinStateEventNotification): Save the Digital Twin State Event Notification
        • getDigitalTwinStateEventNotificationCount(): Get the number of Digital Twin State Event Notification
        • getDigitalTwinStateEventNotificationInTimeRange(long startTimestampMs, long endTimestampMs): Get the Digital Twin State Event Notification in the specified time range
        • getDigitalTwinStateEventNotificationInRange(int startIndex, int endIndex): Get the Digital Twin State Event Notification in the specified range of indices
      • Life Cycle State Variation:
        • saveLifeCycleState(LifeCycleStateVariation lifeCycleStateVariation): Save the LifeCycleState of the Digital Twin
        • getLastLifeCycleState(): Get the last LifeCycleState of the Digital Twin
        • getLifeCycleStateCount(): Get the number of LifeCycleState of the Digital Twin
        • getLifeCycleStateInTimeRange(long startTimestampMs, long endTimestampMs): Get the last LifeCycleState of the Digital Twin
        • getLifeCycleStateInRange(int startIndex, int endIndex): Get the LifeCycleState of the Digital Twin in the specified range of indices
      • Physical Asset Event Notification:
        • savePhysicalAssetEventNotification(PhysicalAssetEventNotification physicalAssetEventNotification): Save the Physical Asset Event Notification
        • getPhysicalAssetEventNotificationCount(): Get the number of Physical Asset Event Notification
        • getPhysicalAssetEventNotificationInTimeRange(long startTimestampMs, long endTimestampMs): Get the Physical Asset Event Notification in the specified time range
        • getPhysicalAssetEventNotificationInRange(int startIndex, int endIndex): Get the Physical Asset Event Notification in the specified range of indices
      • Physical Action Request:
        • savePhysicalAssetActionRequest(PhysicalAssetActionRequest physicalAssetActionRequest): Save Physical Asset Action Request
        • getPhysicalAssetActionRequestCount(): Get the number of Physical Asset Action Request
        • getPhysicalAssetActionRequestInTimeRange(long startTimestampMs, long endTimestampMs): Get the Physical Asset Action Request in the specified time range
        • getPhysicalAssetActionRequestInRange(int startIndex, int endIndex): Get the Physical Asset Action Request in the specified range of indices
      • Digital Action Request:
        • saveDigitalActionRequest(DigitalActionRequest digitalActionRequest): Save a Digital Action Request
        • getDigitalActionRequestCount(): Get the number of Digital Action Request Stored
        • getDigitalActionRequestInTimeRange(long startTimestampMs, long endTimestampMs): Get the Digital Action Request in the specified time range
        • getDigitalActionRequestInRange(int startIndex, int endIndex): Get the Digital Action Request in the specified range of indices
      • Physical Asset Description (PAD) Notification
        • New PAD Notification
          • saveNewPhysicalAssetDescriptionNotification(PhysicalAssetDescriptionNotification physicalAssetDescriptionNotification): Save a new Physical Asset Description Available
          • getNewPhysicalAssetDescriptionNotificationCount(): Get the number of New Physical Asset Description Notifications available
          • getNewPhysicalAssetDescriptionNotificationInTimeRange(long startTimestampMs, long endTimestampMs): Get the New Physical Asset Description Available in the specified time range
          • getNewPhysicalAssetDescriptionNotificationInRange(int startIndex, int endIndex): Get the New Physical Asset Description Available in the specified range of indices
        • Updated PAD Notification
          • saveUpdatedPhysicalAssetDescriptionNotification(PhysicalAssetDescriptionNotification physicalAssetDescriptionNotification): Save the updated Physical Asset Description Notification
          • getUpdatedPhysicalAssetDescriptionNotificationCount(): Get the number of Updated Physical Asset Description
          • getUpdatedPhysicalAssetDescriptionNotificationInTimeRange(long startTimestampMs, long endTimestampMs): Get the Updated Physical Asset Description in the specified time range
          • getUpdatedPhysicalAssetDescriptionNotificationInRange(int startIndex, int endIndex): Get the Updated Physical Asset Description in the specified range of indices
      • Physical Asset Property Variation:
        • savePhysicalAssetPropertyVariation(PhysicalAssetPropertyVariation physicalAssetPropertyVariation): Save the Physical Asset Property Variation
        • getPhysicalAssetPropertyVariationCount(): Get the number of Physical Asset Property Variation
        • getPhysicalAssetPropertyVariationInTimeRange(long startTimestampMs, long endTimestampMs): Get the Physical Asset Property Variation in the specified time range
        • getPhysicalAssetPropertyVariationInRange(int startIndex, int endIndex): Get the Physical Asset Property Variation in the specified range of indices
      • Physical Asset Relationship Instance Notification
        • Created Relationship Instance
          • savePhysicalAssetRelationshipInstanceCreatedNotification(PhysicalRelationshipInstanceVariation physicalRelationshipInstanceVariation): Save the Physical Asset Relationship Instance Created Event
          • getPhysicalAssetRelationshipInstanceCreatedNotificationCount(): Get the number of Physical Asset Relationship Instance Created Event
          • getPhysicalAssetRelationshipInstanceCreatedNotificationInTimeRange(long startTimestampMs, long endTimestampMs): Get the Physical Asset Relationship Instance Created Event in the specified time range
          • getPhysicalAssetRelationshipInstanceCreatedNotificationInRange(int startIndex, int endIndex): Get the Physical Asset Relationship Instance Created Event in the specified range of indices
        • Deleted Relationship Instance
          • savePhysicalAssetRelationshipInstanceDeletedNotification(PhysicalRelationshipInstanceVariation physicalRelationshipInstanceVariation): Save the Physical Asset Relationship Instance Updated Event
          • getPhysicalAssetRelationshipInstanceDeletedNotificationCount(): Get the number of Physical Asset Relationship Instance Updated Event
          • getPhysicalAssetRelationshipInstanceDeletedNotificationInTimeRange(long startTimestampMs, long endTimestampMs): Get the Physical Asset Relationship Instance Updated Event in the specified time range
          • getPhysicalAssetRelationshipInstanceDeletedNotificationInRange(int startIndex, int endIndex): Get the Physical Asset Relationship Instance Updated Event in the specified range of indices

Some examples of usage for the Storage Layer are the following:

Lets’ create a new Digital Twin with a single Storage in charge of automatically observe and store all the event generated and going through the target DT instance

// Create the Digital Twin Engine
+DigitalTwinEngine digitalTwinEngine = new DigitalTwinEngine();  
+
+// Create a new Digital Twin with a Demo Shadowing Function
+DigitalTwin digitalTwin = new DigitalTwin(TEST_DIGITAL_TWIN_ID, new DemoShadowingFunction());
+
+// Physical Adapter Configuration  
+DemoPhysicalAdapter physicalAdapter = new DemoPhysicalAdapter(...);  
+digitalTwin.addPhysicalAdapter(physicalAdapter);
+
+// Digital Adapter Configuration
+digitalAdapter = new DemoDigitalAdapter(...);  
+digitalTwin.addDigitalAdapter(digitalAdapter);
+
+// Create a new WldtStorage instance using the default implementation and observing all the events  
+DefaultWldtStorage myStorage = new DefaultWldtStorage("test_storage", true)
+
+// Add the new Default Storage Instance to the Digital Twin Storage Manager 
+digitalTwin.getStorageManager().putStorage(myStorage);
+
+// Add the Twin to the Engine  
+digitalTwinEngine.addDigitalTwin(digitalTwin);  
+  
+// Start the Digital Twin  
+digitalTwinEngine.startDigitalTwin(TEST_DIGITAL_TWIN_ID);
+

Now let’s suppose to have two additional implementation of the WldtStorage class supporting Redis and MongDB and called RedisWldtStorage and MongoDbWldtStorage. +We would like to use Redis to automatically observe all the events and MongoDb only to store DT’s state and life cycle variations.

[...]
+
+// Create a new RedisWldtStorage instance using the default implementation and observing all the events  
+RedisWldtStorage myRedisStorage = new RedisWldtStorage("redis_storage", true);
+myRedisStorage.setRedisConfiguration(myRedisConfiguration);
+
+// Add the new Redis Storage Instance to the Digital Twin Storage Manager 
+digitalTwin.getStorageManager().putStorage(myRedisStorage);
+
+// Create a new MongoDbWldtStorage instance using the default implementation and observing only State and LifeCycle Events
+MongoDbWldtStorage myMongoDbStorage = new MongoDbWldtStorage("mongo_db_storage", true, false, false, false, false, true);
+myMongoDbStorage.setMongoDbConfiguration(myMongoDbConfiguration);
+
+// Add the new MongoDb Storage Instance to the Digital Twin Storage Manager 
+digitalTwin.getStorageManager().putStorage(myRedisStorage);
+
+[...]
+

Within the ShadowingFunction it is possible to have the reference to the StorageManager in order to access available Storage in both reading and writing mode. +This is an example of how to retrieve an available WldtStorage through its id and the use it to read Properties values in a time range of the last 5 minutes:

String TARGET_STORAGE_ID = "test_storage";  
+  
+if(this.storageManager.isStorageAvailable(TARGET_STORAGE_ID)){  
+  
+    // Access the Storage Manager to store the last value of the property  
+    WldtStorage targetStorage = this.storageManager.getStorage(TARGET_STORAGE_ID);  
+  
+    // Get the current time in milliseconds  
+    long endTime = System.currentTimeMillis();  
+  
+    // Get the Time in the last 5 minutes  
+    long startTime = endTime - (5 * 60 * 1000);  
+  
+    // Get the last Physical Asset Action Request in the last 5 minutes  
+    List<PhysicalAssetPropertyVariationRecord> propertyVariationRecords = targetStorage.getPhysicalAssetPropertyVariationInTimeRange(startTime, endTime);  
+        for(PhysicalAssetPropertyVariationRecord propertyVariationRecord : propertyVariationRecords){  
+        logger.info("Property Variation Record: {}", propertyVariationRecord);  
+        [...]  
+    }  
+}
+

Note: The StorageManager, as previously described, can automatically store DT-related events based on the configuration and setup of each WldtStorage instance added to the manager. However, since the ShadowingFunction has direct access to the StorageManager in both read and write modes, manual handling of data storage is also possible. To achieve this, you can disable automatic storage by setting it to false for specific event types or for all event types. This allows you to manually manage the storage of information within the ShadowingFunction.

Query System

Given the library’s goal of maximizing modularity and decoupling responsibilities among the available components, the Query System has been introduced. This system allows components external to the core responsibilities of the Digital Twin (e.g., Digital Adapters and Augmentation Functions) to retrieve stored data and use or expose it according to their specific logic and implementation. For instance, an HTTP Digital Adapter could expose stored information about a DT’s state variations over time, or a Monitoring Adapter could use available storage instances to retrieve events for a deeper understanding of the target DT instance’s behavior. The query system has been implemented entirely through dedicated events in order to maximize the decoupling of the solution and and supports at the same time both synchronous and asynchronous queries.

The main classes associated to the Query System are the following:

  • QueryManager: This class represents the Query Manager responsible to handle the query request and manage the query execution and has been designed to be extended by the user to implement the desired query management logic (e.g., as with the DefaultQueryManager).
  • QueryRequest: The class contains all the information needed to perform a query on the storage system
  • QueryRequestType: This Enum represents the Query Request Type used to specify the type of query to be performed on the storage system supporting:
    • TIME_RANGE
    • SAMPLE_RANGE
    • LAST_VALUE
    • COUNT
  • QueryResourceType: This Enum represents the Query Resource Type used to specify the type of resource to be queried on the storage system supporting the following resource types mapping those available and managed by the storage manager:
    • PHYSICAL_ASSET_PROPERTY_VARIATION
    • PHYSICAL_ASSET_EVENT_NOTIFICATION
    • PHYSICAL_ACTION_REQUEST
    • DIGITAL_ACTION_REQUEST
    • DIGITAL_TWIN_STATE
    • NEW_PAD_NOTIFICATION
    • UPDATED_PAD_NOTIFICATION
    • PHYSICAL_RELATIONSHIP_INSTANCE_CREATED_NOTIFICATION
    • PHYSICAL_RELATIONSHIP_INSTANCE_DELETED_NOTIFICATION
    • LIFE_CYCLE_EVENT
  • QueryExecutor: This class represents the Query Executor used to execute queries on the storage system supporting both synchronous and asynchronous query execution. Internally is implemented through an event-based mechanism to handle the query request and response
  • QueryResult: This class represents the Query Result returned by the Query Executor containing the query results and the query status (successful or not) and error message (if any) together with also the original request
  • IQueryResultListener: This interface represents the Query Result Listener used to receive the query results

An example of Synchronous query is:

QueryExecutor queryExecutor = new QueryExecutor(TEST_DIGITAL_TWIN_ID, "query-executor");  
+  
+// Create Query Request to the Storage Manager for the Last Digital Twin State  
+QueryRequest queryRequest = new QueryRequest();  
+queryRequest.setResourceType(QueryResourceType.DIGITAL_TWIN_STATE);  
+queryRequest.setRequestType(QueryRequestType.LAST_VALUE);  
+  
+// Send the Query Request to the Storage Manager for the target DT  
+QueryResult<?> queryResult = queryExecutor.syncQueryExecute(queryRequest);
+

Following the same approach an Asynchrounouse query can be executed as follows:

QueryExecutor queryExecutor = new QueryExecutor(TEST_DIGITAL_TWIN_ID, "query-executor");  
+  
+// Create Query Request to the Storage Manager for the Last Digital Twin State  
+QueryRequest queryRequest = new QueryRequest();  
+queryRequest.setResourceType(QueryResourceType.DIGITAL_TWIN_STATE);  
+queryRequest.setRequestType(QueryRequestType.LAST_VALUE);  
+    
+// Send the Query Request to the Storage Manager for the target DT  
+queryExecutor.asyncQueryExecute(queryRequest, new IQueryResultListener() {  
+    @Override  
+    public void onQueryResult(QueryResult<?> queryResult) {  
+        [...]  
+    }  
+});
+

The class DigitalAdapter has been updated adding also an internal reference to a QueryExecutor in order to simplify the interaction with the query system directly from an adapter like in the following example where we use the query Executor of the Digital Adapter invokeAction callback through its internal variable accessible through this.queryExecutor without creating a new executor:

public <T> void invokeAction(String actionKey, T body){  
+    try {  
+          
+        // Create Query Request to the Storage Manager for the Last Digital Twin State  
+        QueryRequest queryRequest = new QueryRequest();  
+        queryRequest.setResourceType(QueryResourceType.DIGITAL_TWIN_STATE);  
+        queryRequest.setRequestType(QueryRequestType.LAST_VALUE);  
+  
+        // Send the Query Request to the Storage Manager for the target DT  
+        QueryResult<?> queryResult = this.queryExecutor.syncQueryExecute(queryRequest);  
+        
+        // Do Something with the Query Result  
+        for(Object result : queryResult.getResults()){  
+	        // Check the type of the Resulting class accordingly to the query
+		    if(result instanceof DigitalTwinState)  
+		        logger.info("LAST DT STATE: {}", result);  
+		    else
+			    logger.error("INVALID RESULT TYPE: {}", result.getClass().getName());  
+		}
+          
+        logger.info("INVOKING ACTION: {} BODY: {}", actionKey, body);  
+        publishDigitalActionWldtEvent(actionKey, body);  
+    } catch (EventBusException e) {  
+        e.printStackTrace();  
+    }  
+}
+

Migration Info: 0.3.0 - 0.4.0

  • Now PhysicalAssetRelationship constructor has also the type in order to match the DigitalTwinStateRelationship and simplify its management
  • The method notifyDigitalTwinStateEvent throws only the Exception WldtDigitalTwinStateEventNotificationException while EventBusException has been removed

Additional Improvements & Fixed Bugs

  • Synchronized the update of the current DT Life Cycle State in order to avoid wrong data
  • The WldtEventBus now supports the use of topics Wildcard (at the moment only multi-level with the character *). For example with this approach is possible to subscribe to all the events associated to property variations (topic: dt.physical.event.property.*). New methods added to WldtEventBus are:
    • matchWildCardType(String eventType, String filterType): Check if the provided event type match the WildCard Type
    • isWildCardType(String filterEventType): Check if the provided event type is a WildCard Type
  • The class WldtEventTypes has been introduced to contain all the event types in the WLDT Framework and support internal message exchange. Includes types for events associated and adopted by: i) Physical Adapters; ii) Model and Shadowing Function; and iii) Digital Adapters.
  • The EventManager class has been added to centralize and simplify the event management in the WLDT Framework providing a set of static methods to publish events associated to a target digital twin and publisher (e.g., the physical adapter of the twin).
  • Now PhysicalAssetRelationship class has also the type in order to match the DigitalTwinStateRelationship and simplify its management
  • The internal class ModelEngine has been renamed into DigitalTwinModel as an initial update for further development of the next version 0.5.0 where the structure of the DT’s Model and the associated classes will be improved
+
\ No newline at end of file diff --git a/docs/change-logs/index.html b/docs/change-logs/index.html index eb712cb..a6aa484 100644 --- a/docs/change-logs/index.html +++ b/docs/change-logs/index.html @@ -2,10 +2,10 @@ Change Logs | WLDT
+
\ No newline at end of file diff --git a/docs/change-logs/index.xml b/docs/change-logs/index.xml index 890d00b..bcecd41 100644 --- a/docs/change-logs/index.xml +++ b/docs/change-logs/index.xml @@ -1 +1 @@ -Change Logs on WLDThttps://wldt.github.io/docs/change-logs/Recent content in Change Logs on WLDTHugo -- gohugo.ioenCopyright (c) 2023 HyasFri, 09 Feb 2024 12:24:39 +0100Change Log 0.3.0https://wldt.github.io/docs/change-logs/change-log-0.3.0/Fri, 09 Feb 2024 12:23:33 +0100https://wldt.github.io/docs/change-logs/change-log-0.3.0/Digital Adapters The following methods have been discontinued and removed from the DigitalAdapter class: onStateChangePropertyCreated onStateChangePropertyUpdated onStateChangePropertyDeleted onStatePropertyUpdated onStatePropertyDeleted onStateChangeActionEnabled onStateChangeActionUpdated onStateChangeActionDisabled onStateChangeEventRegistered onStateChangeEventRegistrationUpdated onStateChangeEventUnregistered onStateChangeRelationshipInstanceDeleted onStateChangeRelationshipDeleted onStateChangeRelationshipInstanceCreated onStateChangeRelationshipCreated onDigitalTwinStateEventNotificationReceived The Signature of the following methods have been changed: onDigitalTwinSync(IDigitalTwinState currentDigitalTwinState) -&gt; onDigitalTwinSync(DigitalTwinState currentDigitalTwinState) onDigitalTwinUnSync(IDigitalTwinState currentDigitalTwinState) -&gt; onDigitalTwinUnSync(DigitalTwinState currentDigitalTwinState) New methods that have been added are: onStateUpdate(DigitalTwinState newDigitalTwinState, DigitalTwinState previousDigitalTwinState, ArrayList&lt;DigitalTwinStateChange&gt; digitalTwinStateChangeList) onEventNotificationReceived(DigitalTwinStateEventNotification&lt;? \ No newline at end of file +Change Logs on WLDThttps://wldt.github.io/docs/change-logs/Recent content in Change Logs on WLDTHugo -- gohugo.ioenCopyright (c) 2023 HyasFri, 09 Feb 2024 12:24:39 +0100Change Log 0.3.0https://wldt.github.io/docs/change-logs/change-log-0.3.0/Fri, 09 Feb 2024 12:23:33 +0100https://wldt.github.io/docs/change-logs/change-log-0.3.0/Digital Adapters The following methods have been discontinued and removed from the DigitalAdapter class: onStateChangePropertyCreated onStateChangePropertyUpdated onStateChangePropertyDeleted onStatePropertyUpdated onStatePropertyDeleted onStateChangeActionEnabled onStateChangeActionUpdated onStateChangeActionDisabled onStateChangeEventRegistered onStateChangeEventRegistrationUpdated onStateChangeEventUnregistered onStateChangeRelationshipInstanceDeleted onStateChangeRelationshipDeleted onStateChangeRelationshipInstanceCreated onStateChangeRelationshipCreated onDigitalTwinStateEventNotificationReceived The Signature of the following methods have been changed: onDigitalTwinSync(IDigitalTwinState currentDigitalTwinState) -&gt; onDigitalTwinSync(DigitalTwinState currentDigitalTwinState) onDigitalTwinUnSync(IDigitalTwinState currentDigitalTwinState) -&gt; onDigitalTwinUnSync(DigitalTwinState currentDigitalTwinState) New methods that have been added are: onStateUpdate(DigitalTwinState newDigitalTwinState, DigitalTwinState previousDigitalTwinState, ArrayList&lt;DigitalTwinStateChange&gt; digitalTwinStateChangeList) onEventNotificationReceived(DigitalTwinStateEventNotification&lt;?Change Log 0.4.0https://wldt.github.io/docs/change-logs/change-log-0.4.0/Thu, 29 Aug 2024 17:24:16 +0200https://wldt.github.io/docs/change-logs/change-log-0.4.0/New Features WldtEventObserver A new class called WldtEventObserver has been introduced to allow a simplified observation of target specific events generated by the Digital Twin and its components such as adapters and the model. \ No newline at end of file diff --git a/docs/change-logs/sitemap.xml b/docs/change-logs/sitemap.xml index 6c255d0..67c037c 100644 --- a/docs/change-logs/sitemap.xml +++ b/docs/change-logs/sitemap.xml @@ -1 +1 @@ -https://wldt.github.io/docs/change-logs/change-log-0.3.0/2024-03-15T10:45:36+01:00monthly0.5 \ No newline at end of file +https://wldt.github.io/docs/change-logs/change-log-0.3.0/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/change-logs/change-log-0.4.0/2024-09-05T15:02:33+02:00monthly0.5 \ No newline at end of file diff --git a/docs/guides/configurable-adapters/index.html b/docs/guides/configurable-adapters/index.html index 80278bb..f88a7f8 100644 --- a/docs/guides/configurable-adapters/index.html +++ b/docs/guides/configurable-adapters/index.html @@ -2,16 +2,16 @@ Configurable Adapters | WLDT

Configurable Adapters

The WLDT library provides a native method to define Configurable Physical ad Digital Adapters specifying a custom configuration class passed as parameter in the constructor.

Starting with the Physical Adapter created in the previous example DemoPhysicalAdapter instead of extending the base class PhysicalAdapter we can extend now ConfigurablePhysicalAdapter<C> where C is the name of the that we would like to use as configuration.

In our example we can create a simple configuration class called DemoPhysicalAdapterConfiguration where we move @@ -233,5 +233,5 @@ } } } -

+
\ No newline at end of file diff --git a/docs/guides/digital-actions/index.html b/docs/guides/digital-actions/index.html index 55c968c..7e5b2a5 100644 --- a/docs/guides/digital-actions/index.html +++ b/docs/guides/digital-actions/index.html @@ -2,16 +2,16 @@ Digital Actions | WLDT

Digital Actions

In this demo implementation, we are going to emulate an incoming Digital Action on the Digital Adapter in order to show how it can be handled by the adapter and properly forwarded to the Shadowing Function for validation and the consequent interaction with the Physical Adapter and then with the physical twin.

In order to add a demo Digital Action trigger on the Digital Adapter we add the following method to the DemoDigitalAdapter class:

private Runnable emulateIncomingDigitalAction(){
     return () -> {
         try {
@@ -71,5 +71,5 @@
         e.printStackTrace();
     }
 }
-
+
\ No newline at end of file diff --git a/docs/guides/digital-adapter/index.html b/docs/guides/digital-adapter/index.html index 98fdd65..c2054e7 100644 --- a/docs/guides/digital-adapter/index.html +++ b/docs/guides/digital-adapter/index.html @@ -2,16 +2,16 @@ Digital Adapter | WLDT

Digital Adapter

The las component that we have to implement to complete our first simple Digital Twin definition through the WLDT library is a Digital Adapter in charge of:

  • Receiving event from the DT’s Core related to the variation of properties, events, available actions and relationships
  • Expose received information to the external world according to its implementation and the supported protocol
  • Handle incoming digital action and forward them to the Core in order to be validated and processed by the Shadowing Function

The basic library class that we are going to extend is called DigitalAdapter and creating a new class named DemoDigitalAdapter. The DigitalTwinAdapter class can take as Generic Type the type of Configuration used to configure its behaviours. In this simplified example we are defining a DigitalAdapter without any Configuration.

A Digital Adapter has direct access to the current DT’s State through callbacks or directly in a synchronous way using the @@ -190,5 +190,5 @@ }

In this example, the method iterates over the list of state changes, extracts information about each change, and performs custom actions based on the changes. Developers can adapt this method to suit the specific requirements of their Digital Twin application.

Both Physical Adapters and Digital Adapters can be defined natively with a custom configuration provided by the developer -as illustrated in the dedicated Section: Configurable Physical & Digital Adapters.

+as illustrated in the dedicated Section: Configurable Physical & Digital Adapters.

\ No newline at end of file diff --git a/docs/guides/dt-engine-dt-instance/index.html b/docs/guides/dt-engine-dt-instance/index.html index 4280321..db9b84e 100644 --- a/docs/guides/dt-engine-dt-instance/index.html +++ b/docs/guides/dt-engine-dt-instance/index.html @@ -2,16 +2,16 @@ DT Engine & DT Instance | WLDT

DT Engine & DT Instance

Now that we have created the main fundamental element of a DT (Physical Adapter, Shadowing Function and Digital Adapter) we can create Class file with a main to create the WLDT Engine with the created components and start the DT.

Create a new Java file called DemoDigitalTwin adding the following code:

With the following code we now create a new Digital Twin Instance

// Create the new Digital Twin with its Shadowing Function  
 DigitalTwin digitalTwin = new DigitalTwin(digitalTwinId, new DemoShadowingFunction());  
   
@@ -87,5 +87,5 @@
         }
     }
 }
-
+
\ No newline at end of file diff --git a/docs/guides/dt-relationships/index.html b/docs/guides/dt-relationships/index.html new file mode 100644 index 0000000..6784296 --- /dev/null +++ b/docs/guides/dt-relationships/index.html @@ -0,0 +1,182 @@ + +DT Relationships | WLDT

DT Relationships

The same management that we have illustrated for Properties, Events and Action can be applied also to Digital Twin Relationships. +Relationships represent the links that exist between the modeled physical assets and other physical entity +of the organizations through links to their corresponding Digital Twins. +Like properties, relationships can be observed, dynamically created, and change over time, +but unlike properties, they are not properly part of the PA’s state but of its operational context +(e.g., a DT of a robot within a production line).

It is necessary to distinguish between two concepts: i) Relationship; and ii) Relationship Instance. +The first one models the relationship from a semantic point of view, +defining its name and target type. +The second one represents an instantiation of the concept in reality. +For example, in the context of a Smart Home, +the Home Digital Twin (DT) will define a Relationship called has_room +which has possible targets represented by DTs that represent different rooms of the house. +The actual link between the Home DT and the Bedroom DT +will be modeled by a specific Relationship Instance of the has_room relationship.

Within the state of the DT, it is necessary to +differentiate between the concept of a relationship and that of an instance of a relationship. +In the first case, we refer to a semantic concept where each relationship, +through its name and the semantic type of its target, +determines the different type of link that the DT can establish. +On the other hand, an instanc of a relationship represents the concrete +link present between the DT that establishes it and the target DT. +For instance, in the case of a Smart Home, +the Bedroom DT may have two relationships in its model: one named is_room_of and another called has_device. +An instance of the first type of relationship could, for example, +have the Home DT as its target, while the has_device relationship could have +multiple instances, one for each device present in the room. +An example of a possible instance is one targeting the Air Conditioner DT.

From an implementation perspective, in the Physical Adapter and in particular where we handle the definition of the PAD we can also +specify the existing relationships. In our case, since the Relationship is useful also to define its future instance we +keep a reference of the relationship as in internal variable called insideInRelationship.

Then we can update the code as follows:

private PhysicalAssetRelationship<String> insideInRelationship = null;
+
+@Override
+public void onIncomingPhysicalAction(PhysicalAssetActionWldtEvent<?> physicalAssetActionWldtEvent) {
+    try{
+        
+        [...]
+        
+        //Create Test Relationship to describe that the Physical Device is inside a building
+        this.insideInRelationship=new PhysicalAssetRelationship<>("insideId");
+        pad.getRelationships().add(insideInRelationship);
+        
+        [...]
+        
+    } catch (Exception e){
+        e.printStackTrace();
+    }
+}
+

Of course always in the Physical Adapter we need to publish an effective instance of the definite Relationship. +To do that, we have defined a dedicated method that we can call inside the adapter to notify the DT’s Core and in +particular the Shadowing Function on the presence of a new Relationship.

The following method can be added for example at the beginning of the Device Emulation:

private void publishPhysicalRelationshipInstance() {
+    try{
+
+        String relationshipTarget = "building-hq";
+
+        Map<String, Object> relationshipMetadata = new HashMap<>();
+        relationshipMetadata.put("floor", "f0");
+        relationshipMetadata.put("room", "r0");
+
+        PhysicalAssetRelationshipInstance<String> relInstance = this.insideInRelationship.createRelationshipInstance(relationshipTarget, relationshipMetadata);
+
+        PhysicalAssetRelationshipInstanceCreatedWldtEvent<String> relInstanceEvent = new PhysicalAssetRelationshipInstanceCreatedWldtEvent<>(relInstance);
+        publishPhysicalAssetRelationshipCreatedWldtEvent(relInstanceEvent);
+
+    }catch (Exception e){
+        e.printStackTrace();
+    }
+}
+

On the other hand, as already done for all the other Properties, Actions and Events we have to handle them on the +Shadowing Function and in particular updating the onDigitalTwinBound(...) method managing Relationship declaration. +Also for the Relationships there is the method denoted as observePhysicalAssetRelationship(relationship) to observe the variation +of the target entity.

@Override
+protected void onDigitalTwinBound(Map<String, PhysicalAssetDescription> adaptersPhysicalAssetDescriptionMap) {
+
+    try{
+
+        //Iterate over all the received PAD from connected Physical Adapters
+        adaptersPhysicalAssetDescriptionMap.values().forEach(pad -> {
+            
+            [...]
+
+            //Iterate over Physical Relationships
+            pad.getRelationships().forEach(relationship -> {
+                try{
+                    if(relationship != null && relationship.getName().equals(GlobalKeywords.INSIDE_IN_RELATIONSHIP)){
+                        DigitalTwinStateRelationship<String> insideInDtStateRelationship = new DigitalTwinStateRelationship<>(relationship.getName(), relationship.getName());
+                        this.digitalTwinState.createRelationship(insideInDtStateRelationship);
+                        observePhysicalAssetRelationship(relationship);
+                    }
+                }catch (Exception e){
+                    e.printStackTrace();
+                }
+            });
+
+        });
+
+        [...]
+
+    }catch (Exception e){
+        e.printStackTrace();
+    }
+}
+

When an Instance for a target observed Relationship has been notified by the Physical Adapter, we will receive a call back on the +Shadowing Function method called: onPhysicalAssetRelationshipEstablished(PhysicalAssetRelationshipInstanceCreatedWldtEvent<?> physicalAssetRelationshipInstanceCreatedWldtEvent). +The object PhysicalAssetRelationshipInstanceCreatedWldtEvent describes the events and contains an object PhysicalAssetRelationshipInstance +with all the information about the new Relationship Instance.

The Shadowing Function analyzes the instance and create the corresponding Digital Relationship instance on the DT’State +through the class DigitalTwinStateRelationshipInstance and the method this.digitalTwinState.addRelationshipInstance(relName, instance);. +The resulting implemented method is the following:

//// Physical Relationships Notification Callbacks ////
+@Override
+protected void onPhysicalAssetRelationshipEstablished(PhysicalAssetRelationshipInstanceCreatedWldtEvent<?> physicalAssetRelationshipInstanceCreatedWldtEvent) {
+    try{
+
+        if(physicalAssetRelationshipInstanceCreatedWldtEvent != null
+        && physicalAssetRelationshipInstanceCreatedWldtEvent.getBody() != null){
+    
+            PhysicalAssetRelationshipInstance<?> paRelInstance = physicalAssetRelationshipInstanceCreatedWldtEvent.getBody();
+        
+            if(paRelInstance.getTargetId() instanceof String){
+        
+                String relName = paRelInstance.getRelationship().getName();
+                String relKey = paRelInstance.getKey();
+                String relTargetId = (String)paRelInstance.getTargetId();
+            
+                DigitalTwinStateRelationshipInstance<String> instance = new DigitalTwinStateRelationshipInstance<String>(relName, relTargetId, relKey);
+
+                //Update Digital Twin State
+                //NEW from 0.3.0 -> Start State Transaction
+                this.digitalTwinStateManager.startStateTransaction();
+  
+                this.digitalTwinStateManager.addRelationshipInstance(instance);
+  
+                //NEW from 0.3.0 -> Commit State Transaction
+                this.digitalTwinStateManager.commitStateTransaction();
+            }
+        }
+    }catch (Exception e){
+        e.printStackTrace();
+    }
+}
+
+@Override
+protected void onPhysicalAssetRelationshipDeleted(PhysicalAssetRelationshipInstanceDeletedWldtEvent<?> physicalAssetRelationshipInstanceDeletedWldtEvent) {
+
+}
+

At the end the new DT’s Relationships and the associated instances can be managed +on a Digital Adapter using the onDigitalTwinSync(IDigitalTwinState currentDigitalTwinState) method and +the following DT state callback method: onStateUpdate().

For example a simple implementation logging on the console can be:

@Override
+protected void onStateUpdate(DigitalTwinState newDigitalTwinState, DigitalTwinState previousDigitalTwinState, ArrayList<DigitalTwinStateChange> digitalTwinStateChangeList) {
+
+  // In newDigitalTwinState we have the new DT State
+  System.out.println("New DT State is: " + newDigitalTwinState);
+
+  // The previous DT State is available through the variable previousDigitalTwinState
+  System.out.println("Previous DT State is: " + previousDigitalTwinState);
+
+  // We can also check each DT's state change potentially differentiating the behaviour for each change
+  if (digitalTwinStateChangeList != null && !digitalTwinStateChangeList.isEmpty()) {
+
+    // Iterate through each state change in the list
+    [...]
+
+      // Specific log example for Relationships Instance Variation
+      if(resourceType.equals(DigitalTwinStateChange.ResourceType.RELATIONSHIP_INSTANCE))
+        System.out.println("New Relationship Instance operation:" + operation + " Resource:" + resource);
+    }
+  } else {
+    // No state changes
+    System.out.println("No state changes detected.");
+  }
+}
+
+
\ No newline at end of file diff --git a/docs/guides/index.html b/docs/guides/index.html index bcaecc9..29152c8 100644 --- a/docs/guides/index.html +++ b/docs/guides/index.html @@ -2,10 +2,10 @@ Getting Started | WLDT
+
\ No newline at end of file diff --git a/docs/guides/index.xml b/docs/guides/index.xml index 42749b0..39e89ed 100644 --- a/docs/guides/index.xml +++ b/docs/guides/index.xml @@ -1 +1 @@ -Getting Started on WLDThttps://wldt.github.io/docs/guides/Recent content in Getting Started on WLDTHugo -- gohugo.ioenCopyright (c) 2023 HyasFri, 09 Feb 2024 12:11:11 +0100Physical Adapterhttps://wldt.github.io/docs/guides/physical-adapter/Fri, 09 Feb 2024 12:09:02 +0100https://wldt.github.io/docs/guides/physical-adapter/The developer can use an existing Physical Adapter or create a new one to handle the communication with a specific physical twin.Shadowing Functionhttps://wldt.github.io/docs/guides/shadowing-function/Fri, 09 Feb 2024 12:15:33 +0100https://wldt.github.io/docs/guides/shadowing-function/After the definition of the Physical Adapter it is time to start implementing the core of our DT through the definition of its shadowing function in charge of:Digital Adapterhttps://wldt.github.io/docs/guides/digital-adapter/Fri, 09 Feb 2024 12:16:06 +0100https://wldt.github.io/docs/guides/digital-adapter/The las component that we have to implement to complete our first simple Digital Twin definition through the WLDT library is a Digital Adapter in charge of:DT Engine & DT Instancehttps://wldt.github.io/docs/guides/dt-engine-dt-instance/Fri, 09 Feb 2024 12:16:36 +0100https://wldt.github.io/docs/guides/dt-engine-dt-instance/Now that we have created the main fundamental element of a DT (Physical Adapter, Shadowing Function and Digital Adapter) we can create Class file with a main to create the WLDT Engine with the created components and start the DT.Digital Actionshttps://wldt.github.io/docs/guides/digital-actions/Fri, 09 Feb 2024 12:18:13 +0100https://wldt.github.io/docs/guides/digital-actions/In this demo implementation, we are going to emulate an incoming Digital Action on the Digital Adapter in order to show how it can be handled by the adapter and properly forwarded to the Shadowing Function for validation and the consequent interaction with the Physical Adapter and then with the physical twin.Configurable Adaptershttps://wldt.github.io/docs/guides/configurable-adapters/Fri, 09 Feb 2024 12:19:37 +0100https://wldt.github.io/docs/guides/configurable-adapters/The WLDT library provides a native method to define Configurable Physical ad Digital Adapters specifying a custom configuration class passed as parameter in the constructor. \ No newline at end of file +Getting Started on WLDThttps://wldt.github.io/docs/guides/Recent content in Getting Started on WLDTHugo -- gohugo.ioenCopyright (c) 2023 HyasFri, 09 Feb 2024 12:11:11 +0100Physical Adapterhttps://wldt.github.io/docs/guides/physical-adapter/Fri, 09 Feb 2024 12:09:02 +0100https://wldt.github.io/docs/guides/physical-adapter/The developer can use an existing Physical Adapter or create a new one to handle the communication with a specific physical twin.Shadowing Functionhttps://wldt.github.io/docs/guides/shadowing-function/Fri, 09 Feb 2024 12:15:33 +0100https://wldt.github.io/docs/guides/shadowing-function/After the definition of the Physical Adapter it is time to start implementing the core of our DT through the definition of its shadowing function in charge of:Digital Adapterhttps://wldt.github.io/docs/guides/digital-adapter/Fri, 09 Feb 2024 12:16:06 +0100https://wldt.github.io/docs/guides/digital-adapter/The las component that we have to implement to complete our first simple Digital Twin definition through the WLDT library is a Digital Adapter in charge of:DT Engine & DT Instancehttps://wldt.github.io/docs/guides/dt-engine-dt-instance/Fri, 09 Feb 2024 12:16:36 +0100https://wldt.github.io/docs/guides/dt-engine-dt-instance/Now that we have created the main fundamental element of a DT (Physical Adapter, Shadowing Function and Digital Adapter) we can create Class file with a main to create the WLDT Engine with the created components and start the DT.Digital Actionshttps://wldt.github.io/docs/guides/digital-actions/Fri, 09 Feb 2024 12:18:13 +0100https://wldt.github.io/docs/guides/digital-actions/In this demo implementation, we are going to emulate an incoming Digital Action on the Digital Adapter in order to show how it can be handled by the adapter and properly forwarded to the Shadowing Function for validation and the consequent interaction with the Physical Adapter and then with the physical twin.DT Relationshipshttps://wldt.github.io/docs/guides/dt-relationships/Fri, 09 Feb 2024 12:19:08 +0100https://wldt.github.io/docs/guides/dt-relationships/The same management that we have illustrated for Properties, Events and Action can be applied also to Digital Twin Relationships. Relationships represent the links that exist between the modeled physical assets and other physical entity of the organizations through links to their corresponding Digital Twins.Configurable Adaptershttps://wldt.github.io/docs/guides/configurable-adapters/Fri, 09 Feb 2024 12:19:37 +0100https://wldt.github.io/docs/guides/configurable-adapters/The WLDT library provides a native method to define Configurable Physical ad Digital Adapters specifying a custom configuration class passed as parameter in the constructor.Storage Layerhttps://wldt.github.io/docs/guides/storage-layer/Thu, 29 Aug 2024 17:46:07 +0200https://wldt.github.io/docs/guides/storage-layer/The storage layer has been integrated into the core WLDT library, enabling Digital Twins to manually and automatically store data related to the evolution of their state, generated events (as illustrated in DT Events Page), and any variations involving properties, events, actions, relationships, and life cycle. \ No newline at end of file diff --git a/docs/guides/physical-adapter/index.html b/docs/guides/physical-adapter/index.html index 4882626..b64f431 100644 --- a/docs/guides/physical-adapter/index.html +++ b/docs/guides/physical-adapter/index.html @@ -2,16 +2,16 @@ Physical Adapter | WLDT

Physical Adapter

The developer can use an existing Physical Adapter or create a new one to handle the communication with a specific physical twin. In this documentation we focus on the creation of a new Physical Adapter in order to explain library core functionalities. However, existing Physical Adapters can be found on the official repository and linked in the core documentation and webpage (WLDT-GitHub).

In general WLDT Physical Adapter extends the class PhysicalAdapter and it is responsible to talk with the physical world and handling the following main tasks:

  • Generate a PAD describing the properties, events, actions and relationships available on the physical twin using the class PhysicalAssetDescription
  • Generate Physical Event using the class PhysicalAssetEventWldtEvent associated to the variation of any aspect of the physical state (properties, events, and relationships)
  • Handle action request coming from the Digital World through the DT Shadowing Function by implementing the method onIncomingPhysicalAction and processing events modeled through the class PhysicalAssetActionWldtEvent

Create a new class called DemoPhysicalAdapter extending the library class PhysicalAdapter and implement the following methods:

  • onAdapterStart: A callback method used to notify when the adapter has been effectively started withing the DT’s life cycle
  • onAdapterStop: A call method invoked when the adapter has been stopped and will be dismissed by the core
  • onIncomingPhysicalAction: The callback method called when a new PhysicalAssetActionWldtEvent is sent by the Shadowing Function upon the receiving of a valid Digital Action through a Digital Adapter

Then you have to create a constructor for your Physical Adapter with a single String parameter representing the id of the adapter. This id will be used internally by the library to handle and coordinate multiple adapters, adapts logs and execute functions upon the arrival of a new event. @@ -257,5 +257,5 @@ } }

Both Physical Adapters and Digital Adapters can be defined natively with a custom configuration provided by the developer -as illustrated in the dedicated Section: Configurable Physical & Digital Adapters.

+as illustrated in the dedicated Section: Configurable Physical & Digital Adapters.

\ No newline at end of file diff --git a/docs/guides/shadowing-function/index.html b/docs/guides/shadowing-function/index.html index 2ed487f..4bb9125 100644 --- a/docs/guides/shadowing-function/index.html +++ b/docs/guides/shadowing-function/index.html @@ -2,16 +2,16 @@ Shadowing Function | WLDT

Shadowing Function

After the definition of the Physical Adapter it is time to start implementing the core of our DT through the definition of its shadowing function in charge of:

  • Handle received PAD from Physical Adapters in order to device which properties, events, relationships or actions available on connected physical twins should be mapped and managed into the DT State
  • Manage incoming notifications/callbacks associated to the variation of physical properties (e.g, temperature variation) or the generation of physical event (e.g., overheating)
  • Process action requests from the digital world that should be validated and forward to the correct Physical Adapter in order to trigger the associated actions on the physical world

The Shadowing Function has the responsibility to build and maintain the updated state of the Digital Twin The internal variable of any WLDT Shadowing Function (available through the base class ShadowingFunction) used to do that is DigitalTwinStateManager accessible through the variable: this.digitalTwinStateManager

When the Shadowing Function has to compute the new DT State it can now work with the following method to handle DT State Transition:

  • Start the DT State Transaction: startStateTransaction()
  • DT State variation methods such as:
    • createProperty()
    • updateProperty()
    • updatePropertyValue()
    • deleteProperty()
    • enableAction()
    • updateAction()
    • disableAction()
    • registerEvent()
    • updateRegisteredEvent()
    • unRegisterEvent()
    • createRelationship()
    • addRelationshipInstance()
    • deleteRelationship()
    • deleteRelationshipInstance()
  • At the end the transaction can be committed using the method: commitStateTransaction()

To access the current DT State the Shadowing Function implementation can use the method this.digitalTwinStateManager.getDigitalTwinState() The information available on the DT State are:

  • properties: List of Properties with their values (if available)
  • actions: List of Actions that can be called on the DT
  • events: List of Events that can be generated by the DT
  • relationships: List of Relationships and their instances (if available)
  • evaluationInstant: The timestamp representing the evaluation instant of the DT state

Available main methods on that class instance are:

  • Properties:
    • getProperty(String propertyKey): Retrieves if present the target DigitalTwinStateProperty by Key
    • containsProperty(String propertyKey): Checks if a target Property Key is already available in the current Digital Twin’s State
    • getPropertyList(): Loads the list of available Properties (described by the class DigitalTwinStateProperty) available on the Digital Twin’s State
    • createProperty(DigitalTwinStateProperty<?> dtStateProperty): Allows the creation of a new Property on the Digital Twin’s State through the class DigitalTwinStateProperty
    • readProperty(String propertyKey): Retrieves if present the target DigitalTwinStateProperty by Key
    • updateProperty(DigitalTwinStateProperty<?> dtStateProperty): Updates the target property using the DigitalTwinStateProperty and the associated Property Key field
    • deleteProperty(String propertyKey): Deletes the target property identified by the specified key
  • Actions:
    • containsAction(String actionKey): Checks if a Digital Twin State Action with the specified key is correctly registered
    • getAction(String actionKey): Loads the target DigitalTwinStateAction by key
    • getActionList(): Gets the list of available Actions registered on the Digital Twin’s State
    • enableAction(DigitalTwinStateAction digitalTwinStateAction): Enables and registers the target Action described through an instance of the DigitalTwinStateAction class
    • updateAction(DigitalTwinStateAction digitalTwinStateAction): Update the already registered target Action described through an instance of the DigitalTwinStateAction class
    • disableAction(String actionKey): Disables and unregisters the target Action described through an instance of the DigitalTwinStateAction class
  • Events:
    • containsEvent(String eventKey): Check if a Digital Twin State Event with the specified key is correctly registered
    • getEvent(String eventKey): Return the description of a registered Digital Twin State Event according to its Key
    • getEventList(): Return the list of existing and registered Digital Twin State Events
    • registerEvent(DigitalTwinStateEvent digitalTwinStateEvent): Register a new Digital Twin State Event
    • updateRegisteredEvent(DigitalTwinStateEvent digitalTwinStateEvent): Update the registration and signature of an existing Digital Twin State Event
    • unRegisterEvent(String eventKey): Un-register a Digital Twin State Event
    • notifyDigitalTwinStateEvent(DigitalTwinStateEventNotification<?> digitalTwinStateEventNotification): Method to notify the occurrence of the target Digital Twin State Event
  • Relationships:
    • containsRelationship(String relationshipName): Checks if a Relationship Name is already available in the current Digital Twin’s State
    • createRelationship(DigitalTwinStateRelationship<?> relationship): Creates a new Relationships (described by the class DigitalTwinStateRelationship) in the Digital Twin’s State
    • addRelationshipInstance(String name, DigitalTwinStateRelationshipInstance<?> instance): Adds a new Relationship instance described through the class DigitalTwinStateRelationshipInstance and identified through its name
    • getRelationshipList(): Loads the list of existing relationships on the Digital Twin’s State through a list of DigitalTwinStateRelationship
    • getRelationship(String name): Gets a target Relationship identified through its name and described through the class DigitalTwinStateRelationship
    • deleteRelationship(String name): Deletes a target Relationship identified through its name
    • deleteRelationshipInstance(String relationshipName, String instanceKey): Deletes the target Relationship Instance using relationship name and instance Key

The basic library class that we are going to extend is called ShadowingFunction and creating a new class named DemoShadowingFunction the resulting @@ -273,5 +273,5 @@ to the Physical Adapter with the method of the Shadowing Function denoted as this.publishPhysicalAssetActionWldtEvent and passing directly the action key and the target Body. No additional processing or validation have been introduced here, but they might be required in advanced scenario in order to properly adapt incoming digital action request to what is effectively expected on the -physical counterpart.

+physical counterpart.

\ No newline at end of file diff --git a/docs/guides/sitemap.xml b/docs/guides/sitemap.xml index 864bad4..61d7373 100644 --- a/docs/guides/sitemap.xml +++ b/docs/guides/sitemap.xml @@ -1 +1 @@ -https://wldt.github.io/docs/guides/physical-adapter/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/docs/guides/shadowing-function/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/docs/guides/digital-adapter/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/docs/guides/dt-engine-dt-instance/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/docs/guides/digital-actions/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/docs/guides/configurable-adapters/2024-03-15T10:45:36+01:00monthly0.5 \ No newline at end of file +https://wldt.github.io/docs/guides/physical-adapter/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/guides/shadowing-function/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/guides/digital-adapter/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/guides/dt-engine-dt-instance/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/guides/digital-actions/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/guides/dt-relationships/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/guides/configurable-adapters/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/guides/storage-layer/2024-09-05T15:02:33+02:00monthly0.5 \ No newline at end of file diff --git a/docs/guides/storage-layer/index.html b/docs/guides/storage-layer/index.html new file mode 100644 index 0000000..d981d87 --- /dev/null +++ b/docs/guides/storage-layer/index.html @@ -0,0 +1,130 @@ + +Storage Layer | WLDT

Storage Layer

The storage layer has been integrated into the core WLDT library, enabling Digital Twins to manually and automatically store data related to the evolution of their state, generated events (as illustrated in DT Events Page), and any variations involving properties, events, actions, relationships, and life cycle.

The WLDT Storage Layer consists of two main components:

  • Storage Manager: This is the central component of the storage system, facilitating the structured and modular storage and retrieval of information. It allows developers to create and utilize various storage systems (e.g., in-memory, file-based, or DBMS) simultaneously. The Storage Layer is accessible in both read and write modes internally by the DT’s Model, and in read-only mode via the Query System by Digital Adapters.
  • Query System: To delegate and encapsulate the responsibility of data storage within the DT’s model, a query system has been integrated. This system enables Digital Adapters to retrieve stored data and expose it according to their specific logic and implementation.

The storage layer is designed for easy extension, allowing developers to create and share new storage layers (e.g., using Redis, MySQL, or MongoDB). The provided in-memory implementation serves only for basic development and testing purposes. Similarly, the Query Manager can be extended and customized by developers to implement additional query management features or to enhance the default functionalities provided by the library.

Storage Manager

The main module of the Storage Layer is the one associated to Storage Capabilities and it is composed by two main classes: StorageManager and WldStorage with the following characteristics and main methods:

  • StorageManager: The StorageManager class is a class that represents the storage manager for a DigitalTwin. It is responsible for managing the storage of the data related to the DigitalTwin. It is an observer of the WldtEventBus, and it is able to save the data in the available storages. The class extends a DigitalTwinWorker, in order to allow the component to work in a structure and integrated way on a different thread that the core of a DT can coordinate starting and stopping it when required. The manager allow the usage of different storage systems at the same time in order to allow the developers to memorize the information accordingly to their need in the right storage system at the same time (e.g., REDIS for quick cached information and MongDB for historical data). Main associated methods are:
    • putStorage(WldtStorage storage): Add a new WldtStorage to the StorageManager
    • getStorageIdList(): Returns the list of id of the WldtStorage in the StorageManager
    • isStorageAvailable(String storageId): Checks if a target Storage Id is available in the Storage Manager
    • getStorage(String storageId): Get the target WldtStorage by id from the Storage Manager
    • removeStorage(String storageId): Remove an existing WldtStorage by id from the StorageManager
  • WldtStorage: Defines an abstract class allowing the Digital Twin developer to implement its internal storage system for the Digital Twin instance.
    • The class defines methods for the management of:
      • Digital Twin State storage and retrieval with the associated change list;
      • Generated State Digital Events;
      • Life Cycle State storage and retrieval;
      • Physical Asset Description storage and retrieval;
      • Physical Asset Property Variation storage and retrieval;
      • Physical Asset Relationship Instance storage and retrieval;
      • Digital Action Request storage and retrieval;
      • Physical Asset Action Request storage and retrieval;
      • Physical Asset Event Notification storage and retrieval;
    • Each WldtStorage instance can be configured (using the right constructor method) to:
      • Observe all Wldt events (stateEvents, physicalAssetEvents, physicalAssetActionEvents, physicalAssetDescriptionEvents, digitalActionEvents, lifeCycleEvents)
      • Filter only for specific class of events
      • Once the WldtStorage has been properly configured to receive target events the StorageManager automatically save information of interest for that specific storage. For example we can have a StorageA (e.g, REDIS) configured to receive all the generated events and a StorageB (e.g., MongoDB) in charge of saving only DT’s state variation over time.
    • The default implementation of the WldtStorage is the class DefaultWldtStorage. This class provides a simple storage solution for digital twin states, digital twin state changes, physical asset events, and digital twin events. The class provides ONLY a memory based approach for storage using ArrayLists and HashMaps and more advanced solution should be implemented for production oriented Digital Twins for examples using external storage and memorization solutions.
    • Each Record written and returned by methods available through the WldtStorage implementations are extension of the StorageRecord used to represents a single record in the storage with a unique id
    • Methods available and implemented by WldtStorage implementations are the following grouped by categories:
      • Digital Twin State:
        • saveDigitalTwinState(DigitalTwinState digitalTwinState, List<DigitalTwinStateChange> digitalTwinStateChangeList): Save a new computed instance of the DT State in the Storage together with the list of the changes with respect to the previous state
        • getLastDigitalTwinState(): Returns the latest computed Digital Twin State of the target Digital Twin instance
        • getDigitalTwinStateCount(): Returns the number of computed and stored Digital Twin States
        • getDigitalTwinStateInTimeRange(long startTimestampMs, long endTimestampMs): Retrieves a list of DigitalTwinState objects within the specified time range
        • getDigitalTwinStateInRange(int startIndex, int endIndex): Retrieves a list of Digital Twin states within the specified range of indices
      • Digital Twin State Event Notification:
        • saveDigitalTwinStateEventNotification(DigitalTwinStateEventNotification<?> digitalTwinStateEventNotification): Save the Digital Twin State Event Notification
        • getDigitalTwinStateEventNotificationCount(): Get the number of Digital Twin State Event Notification
        • getDigitalTwinStateEventNotificationInTimeRange(long startTimestampMs, long endTimestampMs): Get the Digital Twin State Event Notification in the specified time range
        • getDigitalTwinStateEventNotificationInRange(int startIndex, int endIndex): Get the Digital Twin State Event Notification in the specified range of indices
      • Life Cycle State Variation:
        • saveLifeCycleState(LifeCycleStateVariation lifeCycleStateVariation): Save the LifeCycleState of the Digital Twin
        • getLastLifeCycleState(): Get the last LifeCycleState of the Digital Twin
        • getLifeCycleStateCount(): Get the number of LifeCycleState of the Digital Twin
        • getLifeCycleStateInTimeRange(long startTimestampMs, long endTimestampMs): Get the last LifeCycleState of the Digital Twin
        • getLifeCycleStateInRange(int startIndex, int endIndex): Get the LifeCycleState of the Digital Twin in the specified range of indices
      • Physical Asset Event Notification:
        • savePhysicalAssetEventNotification(PhysicalAssetEventNotification physicalAssetEventNotification): Save the Physical Asset Event Notification
        • getPhysicalAssetEventNotificationCount(): Get the number of Physical Asset Event Notification
        • getPhysicalAssetEventNotificationInTimeRange(long startTimestampMs, long endTimestampMs): Get the Physical Asset Event Notification in the specified time range
        • getPhysicalAssetEventNotificationInRange(int startIndex, int endIndex): Get the Physical Asset Event Notification in the specified range of indices
      • Physical Action Request:
        • savePhysicalAssetActionRequest(PhysicalAssetActionRequest physicalAssetActionRequest): Save Physical Asset Action Request
        • getPhysicalAssetActionRequestCount(): Get the number of Physical Asset Action Request
        • getPhysicalAssetActionRequestInTimeRange(long startTimestampMs, long endTimestampMs): Get the Physical Asset Action Request in the specified time range
        • getPhysicalAssetActionRequestInRange(int startIndex, int endIndex): Get the Physical Asset Action Request in the specified range of indices
      • Digital Action Request:
        • saveDigitalActionRequest(DigitalActionRequest digitalActionRequest): Save a Digital Action Request
        • getDigitalActionRequestCount(): Get the number of Digital Action Request Stored
        • getDigitalActionRequestInTimeRange(long startTimestampMs, long endTimestampMs): Get the Digital Action Request in the specified time range
        • getDigitalActionRequestInRange(int startIndex, int endIndex): Get the Digital Action Request in the specified range of indices
      • Physical Asset Description (PAD) Notification
        • New PAD Notification
          • saveNewPhysicalAssetDescriptionNotification(PhysicalAssetDescriptionNotification physicalAssetDescriptionNotification): Save a new Physical Asset Description Available
          • getNewPhysicalAssetDescriptionNotificationCount(): Get the number of New Physical Asset Description Notifications available
          • getNewPhysicalAssetDescriptionNotificationInTimeRange(long startTimestampMs, long endTimestampMs): Get the New Physical Asset Description Available in the specified time range
          • getNewPhysicalAssetDescriptionNotificationInRange(int startIndex, int endIndex): Get the New Physical Asset Description Available in the specified range of indices
        • Updated PAD Notification
          • saveUpdatedPhysicalAssetDescriptionNotification(PhysicalAssetDescriptionNotification physicalAssetDescriptionNotification): Save the updated Physical Asset Description Notification
          • getUpdatedPhysicalAssetDescriptionNotificationCount(): Get the number of Updated Physical Asset Description
          • getUpdatedPhysicalAssetDescriptionNotificationInTimeRange(long startTimestampMs, long endTimestampMs): Get the Updated Physical Asset Description in the specified time range
          • getUpdatedPhysicalAssetDescriptionNotificationInRange(int startIndex, int endIndex): Get the Updated Physical Asset Description in the specified range of indices
      • Physical Asset Property Variation:
        • savePhysicalAssetPropertyVariation(PhysicalAssetPropertyVariation physicalAssetPropertyVariation): Save the Physical Asset Property Variation
        • getPhysicalAssetPropertyVariationCount(): Get the number of Physical Asset Property Variation
        • getPhysicalAssetPropertyVariationInTimeRange(long startTimestampMs, long endTimestampMs): Get the Physical Asset Property Variation in the specified time range
        • getPhysicalAssetPropertyVariationInRange(int startIndex, int endIndex): Get the Physical Asset Property Variation in the specified range of indices
      • Physical Asset Relationship Instance Notification
        • Created Relationship Instance
          • savePhysicalAssetRelationshipInstanceCreatedNotification(PhysicalRelationshipInstanceVariation physicalRelationshipInstanceVariation): Save the Physical Asset Relationship Instance Created Event
          • getPhysicalAssetRelationshipInstanceCreatedNotificationCount(): Get the number of Physical Asset Relationship Instance Created Event
          • getPhysicalAssetRelationshipInstanceCreatedNotificationInTimeRange(long startTimestampMs, long endTimestampMs): Get the Physical Asset Relationship Instance Created Event in the specified time range
          • getPhysicalAssetRelationshipInstanceCreatedNotificationInRange(int startIndex, int endIndex): Get the Physical Asset Relationship Instance Created Event in the specified range of indices
        • Deleted Relationship Instance
          • savePhysicalAssetRelationshipInstanceDeletedNotification(PhysicalRelationshipInstanceVariation physicalRelationshipInstanceVariation): Save the Physical Asset Relationship Instance Updated Event
          • getPhysicalAssetRelationshipInstanceDeletedNotificationCount(): Get the number of Physical Asset Relationship Instance Updated Event
          • getPhysicalAssetRelationshipInstanceDeletedNotificationInTimeRange(long startTimestampMs, long endTimestampMs): Get the Physical Asset Relationship Instance Updated Event in the specified time range
          • getPhysicalAssetRelationshipInstanceDeletedNotificationInRange(int startIndex, int endIndex): Get the Physical Asset Relationship Instance Updated Event in the specified range of indices
      • Storage Statistics:
        • Retrieve and returns storage statistics in terms of the number of stored records for each type and the associated time range of the stored records (start and end timestamp in milliseconds). Storage Statistics are mapped and modeled using the classes StorageStats and StorageStatsRecord.

Storage Manager Code

Some examples of usage for the Storage Layer are the following:

Lets’ create a new Digital Twin with a single Storage in charge of automatically observe and store all the event generated and going through the target DT instance

// Create the Digital Twin Engine
+DigitalTwinEngine digitalTwinEngine = new DigitalTwinEngine();  
+
+// Create a new Digital Twin with a Demo Shadowing Function
+DigitalTwin digitalTwin = new DigitalTwin(TEST_DIGITAL_TWIN_ID, new DemoShadowingFunction());
+
+// Physical Adapter Configuration  
+DemoPhysicalAdapter physicalAdapter = new DemoPhysicalAdapter(...);  
+digitalTwin.addPhysicalAdapter(physicalAdapter);
+
+// Digital Adapter Configuration
+digitalAdapter = new DemoDigitalAdapter(...);  
+digitalTwin.addDigitalAdapter(digitalAdapter);
+
+// Create a new WldtStorage instance using the default implementation and observing all the events  
+DefaultWldtStorage myStorage = new DefaultWldtStorage("test_storage", true);
+
+// Add the new Default Storage Instance to the Digital Twin Storage Manager 
+digitalTwin.getStorageManager().putStorage(myStorage);
+
+// Add the Twin to the Engine  
+digitalTwinEngine.addDigitalTwin(digitalTwin);  
+  
+// Start the Digital Twin  
+digitalTwinEngine.startDigitalTwin(TEST_DIGITAL_TWIN_ID);
+

Now let’s suppose to have two additional implementation of the WldtStorage class supporting Redis and MongDB and called RedisWldtStorage and MongoDbWldtStorage. +We would like to use Redis to automatically observe all the events and MongoDb only to store DT’s state and life cycle variations.

[...]
+
+// Create a new RedisWldtStorage instance using the default implementation and observing all the events  
+RedisWldtStorage myRedisStorage = new RedisWldtStorage("redis_storage", true);
+myRedisStorage.setRedisConfiguration(myRedisConfiguration);
+
+// Add the new Redis Storage Instance to the Digital Twin Storage Manager 
+digitalTwin.getStorageManager().putStorage(myRedisStorage);
+
+// Create a new MongoDbWldtStorage instance using the default implementation and observing only State and LifeCycle Events
+MongoDbWldtStorage myMongoDbStorage = new MongoDbWldtStorage("mongo_db_storage", true, false, false, false, false, true);
+myMongoDbStorage.setMongoDbConfiguration(myMongoDbConfiguration);
+
+// Add the new MongoDb Storage Instance to the Digital Twin Storage Manager 
+digitalTwin.getStorageManager().putStorage(myRedisStorage);
+
+[...]
+

Within the ShadowingFunction it is possible to have the reference to the StorageManager in order to access available Storage in both reading and writing mode. +This is an example of how to retrieve an available WldtStorage through its id and the use it to read Properties values in a time range of the last 5 minutes:

String TARGET_STORAGE_ID = "test_storage";  
+  
+if(this.storageManager.isStorageAvailable(TARGET_STORAGE_ID)){  
+  
+    // Access the Storage Manager to store the last value of the property  
+    WldtStorage targetStorage = this.storageManager.getStorage(TARGET_STORAGE_ID);  
+  
+    // Get the current time in milliseconds  
+    long endTime = System.currentTimeMillis();  
+  
+    // Get the Time in the last 5 minutes  
+    long startTime = endTime - (5 * 60 * 1000);  
+  
+    // Get the last Physical Asset Action Request in the last 5 minutes  
+    List<PhysicalAssetPropertyVariationRecord> propertyVariationRecords = targetStorage.getPhysicalAssetPropertyVariationInTimeRange(startTime, endTime);  
+        for(PhysicalAssetPropertyVariationRecord propertyVariationRecord : propertyVariationRecords){  
+        logger.info("Property Variation Record: {}", propertyVariationRecord);  
+        [...]  
+    }  
+}
+

Note: The StorageManager, as previously described, can automatically store DT-related events based on the configuration and setup of each WldtStorage instance added to the manager. However, since the ShadowingFunction has direct access to the StorageManager in both read and write modes, manual handling of data storage is also possible. To achieve this, you can disable automatic storage by setting it to false for specific event types or for all event types. This allows you to manually manage the storage of information within the ShadowingFunction.

Custom Storage

As previously mentioned, the class WldtStorage is an abstract class allowing the developer to implement its internal storage system for the Digital Twin instance. The class defines a set of abstract methods that should be implemented by the developer to shape the management of reading and writing data from and to the target Storage and associated to the identified variations and changes:

  • Digital Twin State
  • Generated State Digital Events
  • Life Cycle State
  • Physical Asset Description
  • Physical Asset Property Variation
  • Physical Asset Relationship Instance
  • Digital Action Request
  • Physical Asset Action Request
  • Physical Asset Event Notification
  • Storage Statistics

According to the type of the managed resource the type of queries can be characterized in terms of:

  • Time Query: Returns available records in a time range made by a start time in ms and and end time in ms
  • Index Query: Returns available records in a index range made a start and end index
  • Last Value Query: Return the last available value for the target resource
  • Count Query: Returns the number of element of that specific resource

All the stored and retrieve information and record as mapped into dedicated classes available in the package storage.model and associated to the different type of managed resources: digital, lifecycle, physical, state. Each record class extends the base class StorageRecord.

A default Storage module denoted as DefaultWldtStorage is natively available in the library providing a simple in-memory storage solution but a developer can implement its own Storage module (e.g., to enable the support for MongoDb or REDIS).

In order to implement its own storage module the developer should extend the basic abstract class WldtStorage and implement all the supported method to handle data writing and reading. These new classes can extend also the constructor and the required information for example to handle and manager storage configuration (e.g., ip address and port of the storage system or the local folder where tha stored file should be written).

Query System

Given the library’s goal of maximizing modularity and decoupling responsibilities among the available components, the Query System has been introduced. This system allows components external to the core responsibilities of the Digital Twin (e.g., Digital Adapters and Augmentation Functions) to retrieve stored data and use or expose it according to their specific logic and implementation. For instance, an HTTP Digital Adapter could expose stored information about a DT’s state variations over time, or a Monitoring Adapter could use available storage instances to retrieve events for a deeper understanding of the target DT instance’s behavior. The query system has been implemented entirely through dedicated events in order to maximize the decoupling of the solution and and supports at the same time both synchronous and asynchronous queries.

The main classes associated to the Query System are the following:

  • QueryManager: This class represents the Query Manager responsible to handle the query request and manage the query execution and has been designed to be extended by the user to implement the desired query management logic (e.g., as with the DefaultQueryManager).
  • QueryRequest: The class contains all the information needed to perform a query on the storage system
  • QueryRequestType: This Enum represents the Query Request Type used to specify the type of query to be performed on the storage system supporting:
    • TIME_RANGE
    • SAMPLE_RANGE
    • LAST_VALUE
    • COUNT
  • QueryResourceType: This Enum represents the Query Resource Type used to specify the type of resource to be queried on the storage system supporting the following resource types mapping those available and managed by the storage manager (and the supported and associated RequestType):
    • PHYSICAL_ASSET_PROPERTY_VARIATION
      • TIME_RANGE
      • SAMPLE_RANGE
      • COUNT
    • PHYSICAL_ASSET_EVENT_NOTIFICATION
      • TIME_RANGE
      • SAMPLE_RANGE
      • COUNT
    • PHYSICAL_ACTION_REQUEST
      • TIME_RANGE
      • SAMPLE_RANGE
      • COUNT
    • DIGITAL_ACTION_REQUEST
      • TIME_RANGE
      • SAMPLE_RANGE
      • COUNT
    • DIGITAL_TWIN_STATE
      • TIME_RANGE
      • SAMPLE_RANGE
      • COUNT
      • LAST_VALUE
    • NEW_PAD_NOTIFICATION
      • TIME_RANGE
      • SAMPLE_RANGE
      • COUNT
    • UPDATED_PAD_NOTIFICATION
      • TIME_RANGE
      • SAMPLE_RANGE
      • COUNT
    • PHYSICAL_RELATIONSHIP_INSTANCE_CREATED_NOTIFICATION
      • TIME_RANGE
      • SAMPLE_RANGE
      • COUNT
    • PHYSICAL_RELATIONSHIP_INSTANCE_DELETED_NOTIFICATION
      • TIME_RANGE
      • SAMPLE_RANGE
      • COUNT
    • LIFE_CYCLE_EVENT
      • TIME_RANGE
      • SAMPLE_RANGE
      • COUNT
      • LAST_VALUE
    • STORAGE_STATS
      • LAST_VALUE
  • QueryExecutor: This class represents the Query Executor used to execute queries on the storage system supporting both synchronous and asynchronous query execution. Internally is implemented through an event-based mechanism to handle the query request and response
  • QueryResult: This class represents the Query Result returned by the Query Executor containing the query results and the query status (successful or not) and error message (if any) together with also the original request
  • IQueryResultListener: This interface represents the Query Result Listener used to receive the query results

Query System Code

An example of Synchronous query is:

QueryExecutor queryExecutor = new QueryExecutor(TEST_DIGITAL_TWIN_ID, "query-executor");  
+  
+// Create Query Request to the Storage Manager for the Last Digital Twin State  
+QueryRequest queryRequest = new QueryRequest();  
+queryRequest.setResourceType(QueryResourceType.DIGITAL_TWIN_STATE);  
+queryRequest.setRequestType(QueryRequestType.LAST_VALUE);  
+  
+// Send the Query Request to the Storage Manager for the target DT  
+QueryResult<?> queryResult = queryExecutor.syncQueryExecute(queryRequest);
+

Following the same approach an Asynchrounouse query can be executed as follows:

QueryExecutor queryExecutor = new QueryExecutor(TEST_DIGITAL_TWIN_ID, "query-executor");  
+  
+// Create Query Request to the Storage Manager for the Last Digital Twin State  
+QueryRequest queryRequest = new QueryRequest();  
+queryRequest.setResourceType(QueryResourceType.DIGITAL_TWIN_STATE);  
+queryRequest.setRequestType(QueryRequestType.LAST_VALUE);  
+    
+// Send the Query Request to the Storage Manager for the target DT  
+queryExecutor.asyncQueryExecute(queryRequest, new IQueryResultListener() {  
+    @Override  
+    public void onQueryResult(QueryResult<?> queryResult) {  
+        [...]  
+    }  
+});
+

The class DigitalAdapter has been updated adding also an internal reference to a QueryExecutor in order to simplify the interaction with the query system directly from an adapter like in the following example where we use the query Executor of the Digital Adapter invokeAction callback through its internal variable accessible through this.queryExecutor without creating a new executor:

public <T> void invokeAction(String actionKey, T body){  
+    try {  
+          
+        // Create Query Request to the Storage Manager for the Last Digital Twin State  
+        QueryRequest queryRequest = new QueryRequest();  
+        queryRequest.setResourceType(QueryResourceType.DIGITAL_TWIN_STATE);  
+        queryRequest.setRequestType(QueryRequestType.LAST_VALUE);  
+  
+        // Send the Query Request to the Storage Manager for the target DT  
+        QueryResult<?> queryResult = this.queryExecutor.syncQueryExecute(queryRequest);  
+        
+        // Do Something with the Query Result  
+        for(Object result : queryResult.getResults()){  
+	        // Check the type of the Resulting class accordingly to the query
+		    if(result instanceof DigitalTwinState)  
+		        logger.info("LAST DT STATE: {}", result);  
+		    else
+			    logger.error("INVALID RESULT TYPE: {}", result.getClass().getName());  
+		}
+          
+        logger.info("INVOKING ACTION: {} BODY: {}", actionKey, body);  
+        publishDigitalActionWldtEvent(actionKey, body);  
+    } catch (EventBusException e) {  
+        e.printStackTrace();  
+    }  
+}
+

Custom Query Manager

The class in charge of managing an incoming query is called QueryManager and it is characterized by two core methods:

  • handleQuery(QueryRequest queryRequest, Map<String, WldtStorage> storageMap): Handle Query Request allowing its management through the storage map and the associated storage objects. Uses the method getTargetStorage to select the target storage to be used to handle the query.
  • getTargetStorage(QueryRequest queryRequest, Map<String, WldtStorage> storageMap): The method has been designed to return the desired storage object from the storage map to be used for the query management starting from the target QueryRequest and the StorageMap of the DT. In the default implementation, the method returns the first storage object available in the storage map.

The QueryManager class has a list of methods that structure the type of available queries and that return +an instance of QueryResult with an error and a message of “Query not supported by the current implementation !”. The list of these methods is the following:

  • handleLifeCycleEventQuery(...): Handle Life Cycle Event Query Request
  • handlePhysicalRelationshipInstanceDeletedNotificationQuery(...): Handle Physical Relationship Instance Deleted Notification Query Request
  • handlePhysicalRelationshipInstanceCreatedNotificationQuery(...): Handle Physical Relationship Instance Created Notification Query Request
  • handleUpdatedPadNotification(...): Handle Updated Pad Notification Query Request
  • handleNewPadNotification(...): Handle New Pad Notification Query Request
  • handleDigitalActionRequestQuery(...): Handle Digital Action Request Query Request
  • handlePhysicalActionRequestQuery(...): Handle Physical Action Request Query Request
  • handlePhysicalAssetEventNotificationQuery(...): Handle Physical Asset Event Notification Query Request
  • handlePhysicalAssetPropertyVariationQuery(...): Handle Physical Asset Property Variation Query Request
  • handleStateQuery(...): Handle Digital Twin State Query Request
  • handleStorageStatsQuery(...): Handle Storage Stats Query

The library provides a default implementation that is ready to use and automatically activated in every DT instance and called DefaultQueryManager. This class extends the QueryManager class and implements the default behavior for the query management implementing each of the previous listed methods. In the default implementation the method getTargetStorage returns the first storage object available in the storage map. The behavior can be changed by overriding the method in the custom query manager implementation. In the custom implementation, the method can be used to select different storage according to the query.

In order to extend and customize the adopter QueryManager a developer can:

  • Create a new Class extending the QueryManager
  • Implement (if required) a custom getTargetStorage method to return the correct available storage according to the type of request (e.g., the State of the DT are stored on MongoDB while the variation of the Physical Asset on REDIS)
  • If required the developer can implement o change the behavior of the query manager in terms of the query management methods

One a custom QueryManager has been defined it can be set and configured through the following method setQueryManager on the StorageManager of each Digital Twin:

digitalTwin.getStorageManager().setQueryManager(myQueryManager);
+
+
\ No newline at end of file diff --git a/docs/index.html b/docs/index.html index dfdbb78..0346734 100644 --- a/docs/index.html +++ b/docs/index.html @@ -2,10 +2,10 @@ Docs | WLDT
+
\ No newline at end of file diff --git a/docs/introduction/dt-events/index.html b/docs/introduction/dt-events/index.html new file mode 100644 index 0000000..546f710 --- /dev/null +++ b/docs/introduction/dt-events/index.html @@ -0,0 +1,17 @@ + +DT Events | WLDT

DT Events

DT Life Cycle

The entire WLDT Library and the associated DTs modeling has been designed as an event-driven system where each component is (almost) fully decoupled +from the other and focused on a specific responsibilities. The communication among these independent components (e.g., Physical Adapters, Digital Adapters, Model, Storage, Shadowing Function etc ..) +is implemented through an event-driven system and and effective exchange of messages and depicted in the reported Figure.

In order to better understand the type and nature of the available events this section provides additional information about the characteristics and responsibility for each category and are of interest in the Digital Twin.

Physical Asset Events

Those events maps every bidirectional interaction with the Physical Asset (or multiple assets) that the DT is in charge of digitalizing. Involved events are related to:

  • Physical Asset Description: Associated to any variation in the description of the Physical Asset through the structure denoted as PhysicalAssetDescription (PAD)
    • New Physical Asset Description: Maps the availability of a new PAD from a specific target Physical Asset through a Physical Adapter
    • Updated Physical Asset Description: Maps the availability of an updated PAD from a specific target Physical Asset through a Physical Adapter
  • Physical Asset Variation: Associated to any variation of the Physical Asset in terms of Properties, Events and Relationships Instances (previously declared in the PAD)
    • Property Variation: Maps a variation of a Physical Property (e.g., temperature value of 25 Celsius Degrees)
    • Event Notification: Maps an event notification generated by the Physical Asset (e.g., Over-Heating)
    • Relation Instance Variation: Maps a variation of Relationships instance associated to a Relationship type declared in the PAD
  • Physical Asset Action Request: Maps an event coming from the DT’s Core for the Physical Asset (managed by the Physical Adapter) to trigger an action in the physical world (e.g., turn on the switch)

Digital Twin’s Events

Those events are on the other end in charge of mapping event generate by the DT itself during its evolution and across its life-cycle. They can be associated to the following aspects:

  • Life Cycle Variation: Maps a change in the life cycle’s state of the DT for example moving from Bound to Synchronized
  • DT State Variation: Maps a variation in the State of the DT communicating the new State and the list of associated changes
  • DT State Event Notification: Maps an event generated by the DT for example mapping an event notification coming from the Physical Asset (e.g., Over-Heating) or a “new” notification from the DT (e.g., Anomaly-Detected)
  • Digital Action Request: Maps an action request on an available action exposed by the DT and that is requested by and external application. This Action trigger can be internally managed by the DT or can generate then a trigger for a Physical Action as previously described.
+
\ No newline at end of file diff --git a/docs/introduction/dt-life-cycle/index.html b/docs/introduction/dt-life-cycle/index.html index 4729379..2176add 100644 --- a/docs/introduction/dt-life-cycle/index.html +++ b/docs/introduction/dt-life-cycle/index.html @@ -2,16 +2,16 @@ DT Life Cycle | WLDT

DT Life Cycle

DT Life Cycle

The modeling of the concept of DT includes also the definition and characterization of its life cycle. Based on the scientific literature, we model (and then map into the library) a life cycle with 5 states through which the DT goes from when it is executed to when it is stopped. The previous Figure shows a graphical representation of the life cycle with the following steps:

  • Operating & Not Bound: this is the state in which the DT is located following the initialization phase, @@ -42,5 +42,5 @@ expose the DT’s State, its properties and capabilities to the external digital world. At the same time, eDT can be used by Digital Adapters to trigger action on the DT and consequently to propagate (if acceptable and/or needed) the incoming request to the physical assets bound with the target DT. Supported events are illustrated in the following -schema.

    DT Life Cycle

+schema.

DT Life Cycle

\ No newline at end of file diff --git a/docs/introduction/dt-model/index.html b/docs/introduction/dt-model/index.html index 39758a4..29bd1ee 100644 --- a/docs/introduction/dt-model/index.html +++ b/docs/introduction/dt-model/index.html @@ -2,16 +2,16 @@ DT Model | WLDT

DT Model


With respect to the element present in the real world, it is defined as a Physical Asset (PA) with the intention of referring to any entity that has a manifestation or relevance in the physical world and a well-defined lifespan.

Abstract DT Structure

The previous Figure schematically illustrates the main component of an abstract Digital Twin and clarifies its responsibility to be a bridge between the cyber and the physical world. @@ -43,5 +43,5 @@ propagates the request through its physical interface. An important aspect to emphasize is that the request for a digital_action does not directly change the state of the DT since any changes can only occur as a result of the -shadowing function from the PA to the DT, as described earlier.

+shadowing function from the PA to the DT, as described earlier.

\ No newline at end of file diff --git a/docs/introduction/index.html b/docs/introduction/index.html index 0e06a40..8726629 100644 --- a/docs/introduction/index.html +++ b/docs/introduction/index.html @@ -2,10 +2,10 @@ Introduction | WLDT
+
\ No newline at end of file diff --git a/docs/introduction/index.xml b/docs/introduction/index.xml index 854b3f5..0435c68 100644 --- a/docs/introduction/index.xml +++ b/docs/introduction/index.xml @@ -1 +1 @@ -Introduction on WLDThttps://wldt.github.io/docs/introduction/Recent content in Introduction on WLDTHugo -- gohugo.ioenCopyright (c) 2023 HyasThu, 07 Sep 2023 16:06:50 +0200Library Structure & Basic Conceptshttps://wldt.github.io/docs/introduction/library-structure-basic-concepts/Fri, 09 Feb 2024 11:54:42 +0100https://wldt.github.io/docs/introduction/library-structure-basic-concepts/The WLDT framework intends to maximize modularity, re-usability and flexibility in order to effectively mirror physical smart objects in their digital counterparts.DT Modelhttps://wldt.github.io/docs/introduction/dt-model/Fri, 09 Feb 2024 11:44:11 +0100https://wldt.github.io/docs/introduction/dt-model/With respect to the element present in the real world, it is defined as a Physical Asset (PA) with the intention of referring to any entity that has a manifestation or relevance in the physical world and a well-defined lifespan.DT Life Cyclehttps://wldt.github.io/docs/introduction/dt-life-cycle/Fri, 09 Feb 2024 11:51:38 +0100https://wldt.github.io/docs/introduction/dt-life-cycle/The modeling of the concept of DT includes also the definition and characterization of its life cycle. Based on the scientific literature, we model (and then map into the library) a life cycle with 5 states through which the DT goes from when it is executed to when it is stopped. \ No newline at end of file +Introduction on WLDThttps://wldt.github.io/docs/introduction/Recent content in Introduction on WLDTHugo -- gohugo.ioenCopyright (c) 2023 HyasThu, 07 Sep 2023 16:06:50 +0200Library Structure & Basic Conceptshttps://wldt.github.io/docs/introduction/library-structure-basic-concepts/Fri, 09 Feb 2024 11:54:42 +0100https://wldt.github.io/docs/introduction/library-structure-basic-concepts/The WLDT framework intends to maximize modularity, re-usability and flexibility in order to effectively mirror physical smart objects in their digital counterparts.DT Modelhttps://wldt.github.io/docs/introduction/dt-model/Fri, 09 Feb 2024 11:44:11 +0100https://wldt.github.io/docs/introduction/dt-model/With respect to the element present in the real world, it is defined as a Physical Asset (PA) with the intention of referring to any entity that has a manifestation or relevance in the physical world and a well-defined lifespan.DT Life Cyclehttps://wldt.github.io/docs/introduction/dt-life-cycle/Fri, 09 Feb 2024 11:51:38 +0100https://wldt.github.io/docs/introduction/dt-life-cycle/The modeling of the concept of DT includes also the definition and characterization of its life cycle. Based on the scientific literature, we model (and then map into the library) a life cycle with 5 states through which the DT goes from when it is executed to when it is stopped.DT Eventshttps://wldt.github.io/docs/introduction/dt-events/Wed, 04 Sep 2024 16:00:10 +0200https://wldt.github.io/docs/introduction/dt-events/The entire WLDT Library and the associated DTs modeling has been designed as an event-driven system where each component is (almost) fully decoupled from the other and focused on a specific responsibilities. \ No newline at end of file diff --git a/docs/introduction/library-structure-basic-concepts/index.html b/docs/introduction/library-structure-basic-concepts/index.html index d6e4e2c..c3b896a 100644 --- a/docs/introduction/library-structure-basic-concepts/index.html +++ b/docs/introduction/library-structure-basic-concepts/index.html @@ -2,16 +2,16 @@ Library Structure & Basic Concepts | WLDT

Library Structure & Basic Concepts

The WLDT framework intends to maximize modularity, re-usability and flexibility in order to effectively mirror physical smart objects in their digital counterparts. The proposed library focuses on the simplification of twins design and development aiming to provide a set of core features and functionalities for the widespread adoption of Internet of Things DTs applications.

A WLDT instance is a general purpose software entity @@ -73,10 +73,10 @@ selects the components of the various PADs that it is interested in managing.

  • Digital Adapter: It provides the set of callbacks that each specific implementation can use to be notified of changes in the DT state. Symmetrically to what happens with Physical Adapters, a Digital Twin can define -multiple Digital Adapters to expose its state and functionality through different protocols.
  • Therefore, to create a Digital Twin using WLDT, it is necessary to define and instantiate a DT with its Shadowing Function and +multiple Digital Adapters to expose its state and functionality through different protocols.

  • Storage Layer: The storage layer has been integrated into the core library with the aim to enable a manual or automatic storage of data related to the evolution of Digital Twins state, the associated generated and processed events, and any variations involving properties, events, actions, relationships, and life cycle.
  • Therefore, to create a Digital Twin using WLDT, it is necessary to define and instantiate a DT with its Shadowing Function and at least one Physical Adapter and one Digital Adapter, in order to enable connection with the physical entity and allow the DT to be used by external applications. Once the 3 components are defined, it is possible to instantiate the WLDT Engine and, subsequently, start the lifecycle of the DT. In the following sections we will go through the fundamental steps to start working with the library and creating all -the basic modules to design, develop and execute our first Java Digital Twin.

    +the basic modules to design, develop and execute our first Java Digital Twin.

    \ No newline at end of file diff --git a/docs/introduction/sitemap.xml b/docs/introduction/sitemap.xml index c6c3f46..d1bdc7e 100644 --- a/docs/introduction/sitemap.xml +++ b/docs/introduction/sitemap.xml @@ -1 +1 @@ -https://wldt.github.io/docs/introduction/library-structure-basic-concepts/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/docs/introduction/dt-model/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/docs/introduction/dt-life-cycle/2024-03-15T10:45:36+01:00monthly0.5 \ No newline at end of file +https://wldt.github.io/docs/introduction/library-structure-basic-concepts/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/introduction/dt-model/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/introduction/dt-life-cycle/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/introduction/dt-events/2024-09-05T15:02:33+02:00monthly0.5 \ No newline at end of file diff --git a/docs/sitemap.xml b/docs/sitemap.xml index a93d56f..9779651 100644 --- a/docs/sitemap.xml +++ b/docs/sitemap.xml @@ -1 +1 @@ -https://wldt.github.io/docs/introduction/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/docs/guides/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/docs/adapters/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/docs/change-logs/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/docs/introduction/library-structure-basic-concepts/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/docs/introduction/dt-model/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/docs/introduction/dt-life-cycle/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/docs/guides/physical-adapter/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/docs/guides/shadowing-function/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/docs/guides/digital-adapter/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/docs/guides/dt-engine-dt-instance/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/docs/guides/digital-actions/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/docs/guides/configurable-adapters/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/docs/adapters/mqtt-physical-adapter/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/docs/adapters/mqtt-digital-adapter/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/docs/adapters/http-digital-adapter/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/docs/change-logs/change-log-0.3.0/2024-03-15T10:45:36+01:00monthly0.5 \ No newline at end of file +https://wldt.github.io/docs/introduction/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/guides/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/adapters/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/change-logs/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/introduction/library-structure-basic-concepts/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/introduction/dt-model/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/introduction/dt-life-cycle/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/introduction/dt-events/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/guides/physical-adapter/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/guides/shadowing-function/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/guides/digital-adapter/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/guides/dt-engine-dt-instance/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/guides/digital-actions/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/guides/dt-relationships/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/guides/configurable-adapters/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/guides/storage-layer/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/adapters/mqtt-physical-adapter/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/adapters/mqtt-digital-adapter/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/adapters/http-digital-adapter/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/change-logs/change-log-0.3.0/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/change-logs/change-log-0.4.0/2024-09-05T15:02:33+02:00monthly0.5 \ No newline at end of file diff --git a/en/sitemap.xml b/en/sitemap.xml index 54eaf8a..2456f25 100644 --- a/en/sitemap.xml +++ b/en/sitemap.xml @@ -1 +1 @@ -https://wldt.github.io/blog/ls-wldt-library-version-0.3.0/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/blog/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/docs/introduction/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/docs/guides/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/docs/guides/physical-adapter/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/docs/guides/shadowing-function/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/docs/guides/digital-adapter/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/docs/guides/dt-engine-dt-instance/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/docs/guides/digital-actions/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/docs/guides/configurable-adapters/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/docs/adapters/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/docs/adapters/mqtt-physical-adapter/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/docs/change-logs/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/docs/adapters/mqtt-digital-adapter/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/docs/change-logs/change-log-0.3.0/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/docs/adapters/http-digital-adapter/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/docs/introduction/library-structure-basic-concepts/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/docs/introduction/dt-model/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/docs/introduction/dt-life-cycle/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/docs/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/about/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/privacy/2024-03-15T10:45:36+01:00monthly0.5https://wldt.github.io/categories/monthly0.5https://wldt.github.io/contributors/monthly0.5https://wldt.github.io/tags/monthly0.5 \ No newline at end of file +https://wldt.github.io/blog/wldt-library-version-0.4.0/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/blog/wldt-library-version-0.3.0/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/blog/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/introduction/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/guides/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/guides/physical-adapter/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/guides/shadowing-function/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/guides/digital-adapter/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/guides/dt-engine-dt-instance/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/guides/digital-actions/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/guides/dt-relationships/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/guides/configurable-adapters/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/adapters/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/adapters/mqtt-physical-adapter/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/change-logs/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/adapters/mqtt-digital-adapter/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/change-logs/change-log-0.3.0/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/adapters/http-digital-adapter/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/introduction/library-structure-basic-concepts/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/introduction/dt-model/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/introduction/dt-life-cycle/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/introduction/dt-events/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/guides/storage-layer/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/change-logs/change-log-0.4.0/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/docs/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/about/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/privacy/2024-09-05T15:02:33+02:00monthly0.5https://wldt.github.io/categories/monthly0.5https://wldt.github.io/contributors/monthly0.5https://wldt.github.io/tags/monthly0.5 \ No newline at end of file diff --git a/images/wldt_events_hu53c7ad9b57bde89835661b1cad9a910f_2490323_1024x0_resize_q75_box.jpg b/images/wldt_events_hu53c7ad9b57bde89835661b1cad9a910f_2490323_1024x0_resize_q75_box.jpg new file mode 100644 index 0000000..57f08b2 Binary files /dev/null and b/images/wldt_events_hu53c7ad9b57bde89835661b1cad9a910f_2490323_1024x0_resize_q75_box.jpg differ diff --git a/images/wldt_events_hu53c7ad9b57bde89835661b1cad9a910f_2490323_1024x0_resize_q75_h2_box.webp b/images/wldt_events_hu53c7ad9b57bde89835661b1cad9a910f_2490323_1024x0_resize_q75_h2_box.webp new file mode 100644 index 0000000..8c6ed63 Binary files /dev/null and b/images/wldt_events_hu53c7ad9b57bde89835661b1cad9a910f_2490323_1024x0_resize_q75_h2_box.webp differ diff --git a/images/wldt_events_hu53c7ad9b57bde89835661b1cad9a910f_2490323_1366x0_resize_q75_box.jpg b/images/wldt_events_hu53c7ad9b57bde89835661b1cad9a910f_2490323_1366x0_resize_q75_box.jpg new file mode 100644 index 0000000..b2ea09d Binary files /dev/null and b/images/wldt_events_hu53c7ad9b57bde89835661b1cad9a910f_2490323_1366x0_resize_q75_box.jpg differ diff --git a/images/wldt_events_hu53c7ad9b57bde89835661b1cad9a910f_2490323_1366x0_resize_q75_h2_box.webp b/images/wldt_events_hu53c7ad9b57bde89835661b1cad9a910f_2490323_1366x0_resize_q75_h2_box.webp new file mode 100644 index 0000000..aced924 Binary files /dev/null and b/images/wldt_events_hu53c7ad9b57bde89835661b1cad9a910f_2490323_1366x0_resize_q75_h2_box.webp differ diff --git a/images/wldt_events_hu53c7ad9b57bde89835661b1cad9a910f_2490323_480x0_resize_q75_box.jpg b/images/wldt_events_hu53c7ad9b57bde89835661b1cad9a910f_2490323_480x0_resize_q75_box.jpg new file mode 100644 index 0000000..dfb50c5 Binary files /dev/null and b/images/wldt_events_hu53c7ad9b57bde89835661b1cad9a910f_2490323_480x0_resize_q75_box.jpg differ diff --git a/images/wldt_events_hu53c7ad9b57bde89835661b1cad9a910f_2490323_480x0_resize_q75_h2_box.webp b/images/wldt_events_hu53c7ad9b57bde89835661b1cad9a910f_2490323_480x0_resize_q75_h2_box.webp new file mode 100644 index 0000000..7e5b04a Binary files /dev/null and b/images/wldt_events_hu53c7ad9b57bde89835661b1cad9a910f_2490323_480x0_resize_q75_h2_box.webp differ diff --git a/images/wldt_events_hu53c7ad9b57bde89835661b1cad9a910f_2490323_640x0_resize_q75_box.jpg b/images/wldt_events_hu53c7ad9b57bde89835661b1cad9a910f_2490323_640x0_resize_q75_box.jpg new file mode 100644 index 0000000..f67f318 Binary files /dev/null and b/images/wldt_events_hu53c7ad9b57bde89835661b1cad9a910f_2490323_640x0_resize_q75_box.jpg differ diff --git a/images/wldt_events_hu53c7ad9b57bde89835661b1cad9a910f_2490323_640x0_resize_q75_h2_box.webp b/images/wldt_events_hu53c7ad9b57bde89835661b1cad9a910f_2490323_640x0_resize_q75_h2_box.webp new file mode 100644 index 0000000..f0da367 Binary files /dev/null and b/images/wldt_events_hu53c7ad9b57bde89835661b1cad9a910f_2490323_640x0_resize_q75_h2_box.webp differ diff --git a/images/wldt_events_hu53c7ad9b57bde89835661b1cad9a910f_2490323_768x0_resize_q75_box.jpg b/images/wldt_events_hu53c7ad9b57bde89835661b1cad9a910f_2490323_768x0_resize_q75_box.jpg new file mode 100644 index 0000000..1429a10 Binary files /dev/null and b/images/wldt_events_hu53c7ad9b57bde89835661b1cad9a910f_2490323_768x0_resize_q75_box.jpg differ diff --git a/images/wldt_events_hu53c7ad9b57bde89835661b1cad9a910f_2490323_768x0_resize_q75_h2_box.webp b/images/wldt_events_hu53c7ad9b57bde89835661b1cad9a910f_2490323_768x0_resize_q75_h2_box.webp new file mode 100644 index 0000000..85b2add Binary files /dev/null and b/images/wldt_events_hu53c7ad9b57bde89835661b1cad9a910f_2490323_768x0_resize_q75_h2_box.webp differ diff --git a/index.xml b/index.xml index be998b3..cb88eda 100644 --- a/index.xml +++ b/index.xml @@ -1,2 +1 @@ -White Label Digital Twins on WLDThttps://wldt.github.io/Recent content in White Label Digital Twins on WLDTHugo -- gohugo.ioenCopyright (c) 2023 HyasFri, 06 Oct 2023 08:47:36 +0000ls WLDT Library Version 0.3.0https://wldt.github.io/blog/ls-wldt-library-version-0.3.0/Wed, 13 Mar 2024 16:27:22 +0200https://wldt.github.io/blog/ls-wldt-library-version-0.3.0/📣 We&rsquo;re thrilled to announce the release of version 0.3.0 of the White Label Digital Twins (WLDT) library! This release brings significant enhancements, improvements, and new features to further empower developers in designing, developing, and deploying Digital Twins within Internet of Things (IoT) ecosystems.Physical Adapterhttps://wldt.github.io/docs/guides/physical-adapter/Fri, 09 Feb 2024 12:09:02 +0100https://wldt.github.io/docs/guides/physical-adapter/The developer can use an existing Physical Adapter or create a new one to handle the communication with a specific physical twin.Shadowing Functionhttps://wldt.github.io/docs/guides/shadowing-function/Fri, 09 Feb 2024 12:15:33 +0100https://wldt.github.io/docs/guides/shadowing-function/After the definition of the Physical Adapter it is time to start implementing the core of our DT through the definition of its shadowing function in charge of:Digital Adapterhttps://wldt.github.io/docs/guides/digital-adapter/Fri, 09 Feb 2024 12:16:06 +0100https://wldt.github.io/docs/guides/digital-adapter/The las component that we have to implement to complete our first simple Digital Twin definition through the WLDT library is a Digital Adapter in charge of:DT Engine & DT Instancehttps://wldt.github.io/docs/guides/dt-engine-dt-instance/Fri, 09 Feb 2024 12:16:36 +0100https://wldt.github.io/docs/guides/dt-engine-dt-instance/Now that we have created the main fundamental element of a DT (Physical Adapter, Shadowing Function and Digital Adapter) we can create Class file with a main to create the WLDT Engine with the created components and start the DT.Digital Actionshttps://wldt.github.io/docs/guides/digital-actions/Fri, 09 Feb 2024 12:18:13 +0100https://wldt.github.io/docs/guides/digital-actions/In this demo implementation, we are going to emulate an incoming Digital Action on the Digital Adapter in order to show how it can be handled by the adapter and properly forwarded to the Shadowing Function for validation and the consequent interaction with the Physical Adapter and then with the physical twin.Configurable Adaptershttps://wldt.github.io/docs/guides/configurable-adapters/Fri, 09 Feb 2024 12:19:37 +0100https://wldt.github.io/docs/guides/configurable-adapters/The WLDT library provides a native method to define Configurable Physical ad Digital Adapters specifying a custom configuration class passed as parameter in the constructor.MQTT Physical Adapterhttps://wldt.github.io/docs/adapters/mqtt-physical-adapter/Fri, 09 Feb 2024 12:55:52 +0100https://wldt.github.io/docs/adapters/mqtt-physical-adapter/The MqttPhysicalAdapter library provides a streamlined solution for efficiently managing physical assets through the MQTT protocol. It offers a range of features, including a versatile builder for effortless configuration of MQTT connections, dedicated classes for handling both incoming and outgoing topics, and a specialized adapter designed for seamless integration with diverse physical assets.MQTT Digital Adapterhttps://wldt.github.io/docs/adapters/mqtt-digital-adapter/Fri, 09 Feb 2024 12:58:14 +0100https://wldt.github.io/docs/adapters/mqtt-digital-adapter/The MqttDigitalAdapter, -MqttDigitalAdapterConfiguration, and MqttDigitalAdapterConfigurationBuilder classes and guides you through using these classes to set up an MQTT Digital Adapter within WLDT.Change Log 0.3.0https://wldt.github.io/docs/change-logs/change-log-0.3.0/Fri, 09 Feb 2024 12:23:33 +0100https://wldt.github.io/docs/change-logs/change-log-0.3.0/Digital Adapters The following methods have been discontinued and removed from the DigitalAdapter class: onStateChangePropertyCreated onStateChangePropertyUpdated onStateChangePropertyDeleted onStatePropertyUpdated onStatePropertyDeleted onStateChangeActionEnabled onStateChangeActionUpdated onStateChangeActionDisabled onStateChangeEventRegistered onStateChangeEventRegistrationUpdated onStateChangeEventUnregistered onStateChangeRelationshipInstanceDeleted onStateChangeRelationshipDeleted onStateChangeRelationshipInstanceCreated onStateChangeRelationshipCreated onDigitalTwinStateEventNotificationReceived The Signature of the following methods have been changed: onDigitalTwinSync(IDigitalTwinState currentDigitalTwinState) -&gt; onDigitalTwinSync(DigitalTwinState currentDigitalTwinState) onDigitalTwinUnSync(IDigitalTwinState currentDigitalTwinState) -&gt; onDigitalTwinUnSync(DigitalTwinState currentDigitalTwinState) New methods that have been added are: onStateUpdate(DigitalTwinState newDigitalTwinState, DigitalTwinState previousDigitalTwinState, ArrayList&lt;DigitalTwinStateChange&gt; digitalTwinStateChangeList) onEventNotificationReceived(DigitalTwinStateEventNotification&lt;? \ No newline at end of file +White Label Digital Twins on WLDThttps://wldt.github.io/Recent content in White Label Digital Twins on WLDTHugo -- gohugo.ioenCopyright (c) 2023 HyasFri, 06 Oct 2023 08:47:36 +0000WLDT Library Version 0.4.0https://wldt.github.io/blog/wldt-library-version-0.4.0/Thu, 29 Aug 2024 17:40:54 +0200https://wldt.github.io/blog/wldt-library-version-0.4.0/We&rsquo;re excited to announce the release of WLDT version 0.4.0! This update brings powerful new features to enhance your Digital Twin (DT) experience, including event observation capabilities, a robust storage layer, and a flexible query system.WLDT Library Version 0.3.0https://wldt.github.io/blog/wldt-library-version-0.3.0/Wed, 13 Mar 2024 16:27:22 +0200https://wldt.github.io/blog/wldt-library-version-0.3.0/📣 We&rsquo;re thrilled to announce the release of version 0.3.0 of the White Label Digital Twins (WLDT) library! This release brings significant enhancements, improvements, and new features to further empower developers in designing, developing, and deploying Digital Twins within Internet of Things (IoT) ecosystems.Physical Adapterhttps://wldt.github.io/docs/guides/physical-adapter/Fri, 09 Feb 2024 12:09:02 +0100https://wldt.github.io/docs/guides/physical-adapter/The developer can use an existing Physical Adapter or create a new one to handle the communication with a specific physical twin.Shadowing Functionhttps://wldt.github.io/docs/guides/shadowing-function/Fri, 09 Feb 2024 12:15:33 +0100https://wldt.github.io/docs/guides/shadowing-function/After the definition of the Physical Adapter it is time to start implementing the core of our DT through the definition of its shadowing function in charge of:Digital Adapterhttps://wldt.github.io/docs/guides/digital-adapter/Fri, 09 Feb 2024 12:16:06 +0100https://wldt.github.io/docs/guides/digital-adapter/The las component that we have to implement to complete our first simple Digital Twin definition through the WLDT library is a Digital Adapter in charge of:DT Engine & DT Instancehttps://wldt.github.io/docs/guides/dt-engine-dt-instance/Fri, 09 Feb 2024 12:16:36 +0100https://wldt.github.io/docs/guides/dt-engine-dt-instance/Now that we have created the main fundamental element of a DT (Physical Adapter, Shadowing Function and Digital Adapter) we can create Class file with a main to create the WLDT Engine with the created components and start the DT.Digital Actionshttps://wldt.github.io/docs/guides/digital-actions/Fri, 09 Feb 2024 12:18:13 +0100https://wldt.github.io/docs/guides/digital-actions/In this demo implementation, we are going to emulate an incoming Digital Action on the Digital Adapter in order to show how it can be handled by the adapter and properly forwarded to the Shadowing Function for validation and the consequent interaction with the Physical Adapter and then with the physical twin.DT Relationshipshttps://wldt.github.io/docs/guides/dt-relationships/Fri, 09 Feb 2024 12:19:08 +0100https://wldt.github.io/docs/guides/dt-relationships/The same management that we have illustrated for Properties, Events and Action can be applied also to Digital Twin Relationships. Relationships represent the links that exist between the modeled physical assets and other physical entity of the organizations through links to their corresponding Digital Twins.Configurable Adaptershttps://wldt.github.io/docs/guides/configurable-adapters/Fri, 09 Feb 2024 12:19:37 +0100https://wldt.github.io/docs/guides/configurable-adapters/The WLDT library provides a native method to define Configurable Physical ad Digital Adapters specifying a custom configuration class passed as parameter in the constructor.MQTT Physical Adapterhttps://wldt.github.io/docs/adapters/mqtt-physical-adapter/Fri, 09 Feb 2024 12:55:52 +0100https://wldt.github.io/docs/adapters/mqtt-physical-adapter/The MqttPhysicalAdapter library provides a streamlined solution for efficiently managing physical assets through the MQTT protocol. It offers a range of features, including a versatile builder for effortless configuration of MQTT connections, dedicated classes for handling both incoming and outgoing topics, and a specialized adapter designed for seamless integration with diverse physical assets. \ No newline at end of file diff --git a/privacy/index.html b/privacy/index.html index e76f49c..9b9e6ea 100644 --- a/privacy/index.html +++ b/privacy/index.html @@ -3,5 +3,5 @@
    WLDT

    Privacy Policy

    Last updated on March 15, 2024

    +

    Privacy Policy

    Last updated on September 5, 2024

    \ No newline at end of file diff --git a/search-index.json b/search-index.json index 40acbb5..c960b61 100644 --- a/search-index.json +++ b/search-index.json @@ -1 +1 @@ -[{"content":"📣 We\u0026rsquo;re thrilled to announce the release of version 0.3.0 of the White Label Digital Twins (WLDT) library! This release brings significant enhancements, improvements, and new features to further empower developers in designing, developing, and deploying Digital Twins within Internet of Things (IoT) ecosystems.\nFor detailed information about these changes and their impact, please refer to the information provided:\nOfficial Changelog Official Documentation Let\u0026rsquo;s dive into the key changes and updates included in this release:\nMigration Digital Adapters In version 0.3.0, we\u0026rsquo;ve made several enhancements and adjustments to the Digital Adapter class to improve its functionality and usability. Notable changes include:\nDiscontinued Methods: Several methods have been discontinued and removed from the DigitalAdapter class to streamline its interface and improve clarity. Method Signature Changes: The signatures of certain methods have been updated for consistency and clarity, ensuring a more intuitive developer experience. New Methods: We\u0026rsquo;ve introduced new methods to provide additional functionality and flexibility for handling state updates and event notifications. Migration Shadowing Function We\u0026rsquo;ve made significant improvements to the ShadowingModelFunction, which is now renamed to ShadowingFunction. Additionally, we\u0026rsquo;ve introduced changes to how the DigitalTwinState is managed within the Shadowing Function, providing developers with more control and flexibility.\nMigrating WLDT Engine \u0026amp; DT Creation In version 0.3.0, the WldtEngine class has been renamed to DigitalTwin, offering improved clarity and consistency. We\u0026rsquo;ve also made adjustments to the lifecycle management of Digital Twins, streamlining the process and enhancing usability.\nDigital Twin \u0026amp; Digital Twin Engine We\u0026rsquo;ve introduced enhancements to the Digital Twin and Digital Twin Engine classes, providing developers with improved functionality and ease of use. Notable updates include:\nSimplified Digital Twin Creation: Creating and managing Digital Twins is now more intuitive and streamlined. Lifecycle Management: We\u0026rsquo;ve enhanced the lifecycle management of Digital Twins, making it easier to start, stop, and manage multiple instances. Digital Twin State Manager The DigitalTwinStateManager class has been improved to provide better support for managing the state of Digital Twins. With features such as transaction support and event notification, developers can more effectively manage changes to Digital Twin states and respond to events.\nTo learn more about the capabilities of the DigitalTwinStateManager, please refer to the Digital Twin State Manager section.\nDigital Adapter We\u0026rsquo;ve extended and improved the Digital Adapter base class to provide enhanced support for handling Digital Twin state updates and event notifications. With the introduction of the onStateUpdate and onEventNotificationReceived methods, developers can more effectively respond to changes in Digital Twin states and events.\nGet Started with WLDT 0.3.0 To get started with version 0.3.0 of the WLDT library, simply update your dependencies to include the latest release. Detailed documentation and usage examples are available in the project repository, providing comprehensive guidance on leveraging the new features and enhancements.\nWe\u0026rsquo;re excited about the improvements and new capabilities introduced in WLDT 0.3.0, and we can\u0026rsquo;t wait to see how developers utilize them to create innovative IoT solutions powered by Digital Twins. As always, we welcome your feedback and contributions to help us further improve the library and empower the community.\nHappy coding with WLDT 0.3.0! 🚀\n","date":"March 13, 2024","id":0,"permalink":"/blog/ls-wldt-library-version-0.3.0/","summary":"📣 We\u0026rsquo;re thrilled to announce the release of version 0.3.0 of the White Label Digital Twins (WLDT) library! This release brings significant enhancements, improvements, and new features to further empower developers in designing, developing, and deploying Digital Twins within Internet of Things (IoT) ecosystems.","tags":"","title":"ls WLDT Library Version 0.3.0"},{"content":"","date":"September 7, 2023","id":1,"permalink":"/blog/","summary":"","tags":"","title":"Blog"},{"content":"","date":"September 7, 2023","id":2,"permalink":"/docs/introduction/","summary":"","tags":"","title":"Introduction"},{"content":"","date":"February 9, 2024","id":3,"permalink":"/docs/guides/","summary":"","tags":"","title":"Getting Started"},{"content":"The developer can use an existing Physical Adapter or create a new one to handle the communication with a specific physical twin. In this documentation we focus on the creation of a new Physical Adapter in order to explain library core functionalities. However, existing Physical Adapters can be found on the official repository and linked in the core documentation and webpage (WLDT-GitHub).\nIn general WLDT Physical Adapter extends the class PhysicalAdapter and it is responsible to talk with the physical world and handling the following main tasks:\nGenerate a PAD describing the properties, events, actions and relationships available on the physical twin using the class PhysicalAssetDescription Generate Physical Event using the class PhysicalAssetEventWldtEvent associated to the variation of any aspect of the physical state (properties, events, and relationships) Handle action request coming from the Digital World through the DT Shadowing Function by implementing the method onIncomingPhysicalAction and processing events modeled through the class PhysicalAssetActionWldtEvent Create a new class called DemoPhysicalAdapter extending the library class PhysicalAdapter and implement the following methods:\nonAdapterStart: A callback method used to notify when the adapter has been effectively started withing the DT\u0026rsquo;s life cycle onAdapterStop: A call method invoked when the adapter has been stopped and will be dismissed by the core onIncomingPhysicalAction: The callback method called when a new PhysicalAssetActionWldtEvent is sent by the Shadowing Function upon the receiving of a valid Digital Action through a Digital Adapter Then you have to create a constructor for your Physical Adapter with a single String parameter representing the id of the adapter. This id will be used internally by the library to handle and coordinate multiple adapters, adapts logs and execute functions upon the arrival of a new event. The resulting empty class will the following:\npublic class DemoPhysicalAdapter extends PhysicalAdapter { public DemoPhysicalAdapter(String id) { super(id); } @Override public void onIncomingPhysicalAction(PhysicalAssetActionWldtEvent\u0026lt;?\u0026gt; physicalAssetActionWldtEvent) { } @Override public void onAdapterStart() { } @Override public void onAdapterStop() { } } In our test Physical Adapter example we are going to emulate the communication with an Internet of Things device with the following sensing and actuation characteristics:\nA Temperature Sensor generating data about new measurements The possibility to generate OVER-HEATING events An action to set the target desired temperature value The first step will be to generate and publish the PhysicalAssetDescription (PAD) to describe the capabilities and the characteristics of our object allowing the Shadowing Function to decide how to digitalize its physical counterpart. Of course in our case the PAD is generated manually but according to the nature of the connected physical twin it can be automatically generated starting from a discovery or a configuration passed to the adapter.\nThe generation of the PAD for each active Physical Adapter is the fundamental DT process to handle the binding procedure and to allow the Shadowing Function and consequently the core of the twin to be aware of what is available in the physical world and consequently decide what to observe and digitalize.\nIn order to publish the PAD we can update the onAdapterStart method with the following lines of code:\nprivate final static String TEMPERATURE_PROPERTY_KEY = \u0026#34;temperature-property-key\u0026#34;; private final static String OVERHEATING_EVENT_KEY = \u0026#34;overheating-event-key\u0026#34;; private final static String SET_TEMPERATURE_ACTION_KEY = \u0026#34;set-temperatura-action-key\u0026#34;; @Override public void onAdapterStart() { try { //Create an empty PAD PhysicalAssetDescription pad = new PhysicalAssetDescription(); //Add a new Property associated to the target PAD with a key and a default value PhysicalAssetProperty\u0026lt;Double\u0026gt; temperatureProperty = new PhysicalAssetProperty\u0026lt;Double\u0026gt;(TEMPERATURE_PROPERTY_KEY, 0.0); pad.getProperties().add(temperatureProperty); //Add the declaration of a new type of generated event associated to a event key // and the content type of the generated payload PhysicalAssetEvent overheatingEvent = new PhysicalAssetEvent(OVERHEATING_EVENT_KEY, \u0026#34;text/plain\u0026#34;); pad.getEvents().add(overheatingEvent); //Declare the availability of a target action characterized by a Key, an action type // and the expected content type and the request body PhysicalAssetAction setTemperatureAction = new PhysicalAssetAction(SET_TEMPERATURE_ACTION_KEY, \u0026#34;temperature.actuation\u0026#34;, \u0026#34;text/plain\u0026#34;); pad.getActions().add(setTemperatureAction); //Notify the new PAD to the DT\u0026#39;s Shadowing Function this.notifyPhysicalAdapterBound(pad); //TODO add here the Device Emulation method } catch (PhysicalAdapterException | EventBusException e) { e.printStackTrace(); } } Now we need a simple code to emulate the generation of new temperature measurements and over-heating events. In a real Physical Adapter implementation we have to implement the real communication with the physical twin in order to read its state variation over time according to the supported protocols. In our simplified Physical Adapter we can the following function:\nprivate final static int MESSAGE_UPDATE_TIME = 1000; private final static int MESSAGE_UPDATE_NUMBER = 10; private final static double TEMPERATURE_MIN_VALUE = 20; private final static double TEMPERATURE_MAX_VALUE = 30; private Runnable deviceEmulation(){ return () -\u0026gt; { try { //Sleep 5 seconds to emulate device startup Thread.sleep(5000); //Create a new random object to emulate temperature variations Random r = new Random(); //Publish an initial Event for a normal condition publishPhysicalAssetEventWldtEvent(new PhysicalAssetEventWldtEvent\u0026lt;\u0026gt;(OVERHEATING_EVENT_KEY, \u0026#34;normal\u0026#34;)); //Emulate the generation on \u0026#39;n\u0026#39; temperature measurements for(int i = 0; i \u0026lt; MESSAGE_UPDATE_NUMBER; i++){ //Sleep to emulate sensor measurement Thread.sleep(MESSAGE_UPDATE_TIME); //Update the double randomTemperature = TEMPERATURE_MIN_VALUE + (TEMPERATURE_MAX_VALUE - TEMPERATURE_MIN_VALUE) * r.nextDouble(); //Create a new event to notify the variation of a Physical Property PhysicalAssetPropertyWldtEvent\u0026lt;Double\u0026gt; newPhysicalPropertyEvent = new PhysicalAssetPropertyWldtEvent\u0026lt;\u0026gt;(TEMPERATURE_PROPERTY_KEY, randomTemperature); //Publish the WLDTEvent associated to the Physical Property Variation publishPhysicalAssetPropertyWldtEvent(newPhysicalPropertyEvent); } //Publish a demo Physical Event associated to a \u0026#39;critical\u0026#39; overheating condition publishPhysicalAssetEventWldtEvent(new PhysicalAssetEventWldtEvent\u0026lt;\u0026gt;(OVERHEATING_EVENT_KEY, \u0026#34;critical\u0026#34;)); } catch (EventBusException | InterruptedException e) { e.printStackTrace(); } }; } Now we have to call the deviceEmulationFunction() inside the onAdapterStart() triggering its execution and emulating the physical counterpart of our DT. To do that add the following line at the end of the onAdapterStart() method after the this.notifyPhysicalAdapterBound(pad);.\nThe last step will be to handle an incoming action trying to set a new temperature on the device by implementing the method onIncomingPhysicalAction(). This method will receive a PhysicalAssetActionWldtEvent\u0026lt;?\u0026gt; physicalAssetActionWldtEvent associated to the action request generated by the shadowing function. Since a Physical Adapter can handle multiple action we have to check both action-key and body type in order to properly process the action (in our case just logging the request). The new update method will result like this:\n@Override public void onIncomingPhysicalAction(PhysicalAssetActionWldtEvent\u0026lt;?\u0026gt; physicalAssetActionWldtEvent) { try{ if(physicalAssetActionWldtEvent != null \u0026amp;\u0026amp; physicalAssetActionWldtEvent.getActionKey().equals(SET_TEMPERATURE_ACTION_KEY) \u0026amp;\u0026amp; physicalAssetActionWldtEvent.getBody() instanceof String) { System.out.println(\u0026#34;Received Action Request: \u0026#34; + physicalAssetActionWldtEvent.getActionKey() + \u0026#34; with Body: \u0026#34; + physicalAssetActionWldtEvent.getBody()); } else System.err.println(\u0026#34;Wrong Action Received !\u0026#34;); }catch (Exception e){ e.printStackTrace(); } } The overall class will result as following:\nimport it.wldt.adapter.physical.*; import it.wldt.adapter.physical.event.PhysicalAssetActionWldtEvent; import it.wldt.adapter.physical.event.PhysicalAssetEventWldtEvent; import it.wldt.adapter.physical.event.PhysicalAssetPropertyWldtEvent; import it.wldt.exception.EventBusException; import it.wldt.exception.PhysicalAdapterException; import java.util.Random; public class DemoPhysicalAdapter extends PhysicalAdapter { private final static String TEMPERATURE_PROPERTY_KEY = \u0026#34;temperature-property-key\u0026#34;; private final static String OVERHEATING_EVENT_KEY = \u0026#34;overheating-event-key\u0026#34;; private final static String SET_TEMPERATURE_ACTION_KEY = \u0026#34;set-temperature-action-key\u0026#34;; private final static int MESSAGE_UPDATE_TIME = 1000; private final static int MESSAGE_UPDATE_NUMBER = 10; private final static double TEMPERATURE_MIN_VALUE = 20; private final static double TEMPERATURE_MAX_VALUE = 30; public DemoPhysicalAdapter(String id) { super(id); } @Override public void onIncomingPhysicalAction(PhysicalAssetActionWldtEvent\u0026lt;?\u0026gt; physicalAssetActionWldtEvent) { try{ if(physicalAssetActionWldtEvent != null \u0026amp;\u0026amp; physicalAssetActionWldtEvent.getActionKey().equals(SET_TEMPERATURE_ACTION_KEY) \u0026amp;\u0026amp; physicalAssetActionWldtEvent.getBody() instanceof Double) { System.out.println(\u0026#34;Received Action Request: \u0026#34; + physicalAssetActionWldtEvent.getActionKey() + \u0026#34; with Body: \u0026#34; + physicalAssetActionWldtEvent.getBody()); } else System.err.println(\u0026#34;Wrong Action Received !\u0026#34;); }catch (Exception e){ e.printStackTrace(); } } @Override public void onAdapterStart() { try { //Create an empty PAD PhysicalAssetDescription pad = new PhysicalAssetDescription(); //Add a new Property associated to the target PAD with a key and a default value PhysicalAssetProperty\u0026lt;Double\u0026gt; temperatureProperty = new PhysicalAssetProperty\u0026lt;Double\u0026gt;(GlobalKeywords.TEMPERATURE_PROPERTY_KEY, 0.0); pad.getProperties().add(temperatureProperty); //Add the declaration of a new type of generated event associated to a event key // and the content type of the generated payload PhysicalAssetEvent overheatingEvent = new PhysicalAssetEvent(GlobalKeywords.OVERHEATING_EVENT_KEY, \u0026#34;text/plain\u0026#34;); pad.getEvents().add(overheatingEvent); //Declare the availability of a target action characterized by a Key, an action type // and the expected content type and the request body PhysicalAssetAction setTemperatureAction = new PhysicalAssetAction(GlobalKeywords.SET_TEMPERATURE_ACTION_KEY, \u0026#34;temperature.actuation\u0026#34;, \u0026#34;text/plain\u0026#34;); pad.getActions().add(setTemperatureAction); //Notify the new PAD to the DT\u0026#39;s Shadowing Function this.notifyPhysicalAdapterBound(pad); //Start Device Emulation new Thread(deviceEmulation()).start(); } catch (PhysicalAdapterException | EventBusException e) { e.printStackTrace(); } } @Override public void onAdapterStop() { } private Runnable deviceEmulation(){ return () -\u0026gt; { try { //Sleep 5 seconds to emulate device startup Thread.sleep(5000); //Create a new random object to emulate temperature variations Random r = new Random(); //Publish an initial Event for a normal condition publishPhysicalAssetEventWldtEvent(new PhysicalAssetEventWldtEvent\u0026lt;\u0026gt;(GlobalKeywords.OVERHEATING_EVENT_KEY, \u0026#34;normal\u0026#34;)); //Emulate the generation on \u0026#39;n\u0026#39; temperature measurements for(int i = 0; i \u0026lt; GlobalKeywords.MESSAGE_UPDATE_NUMBER; i++){ //Sleep to emulate sensor measurement Thread.sleep(GlobalKeywords.MESSAGE_UPDATE_TIME); //Update the double randomTemperature = GlobalKeywords.TEMPERATURE_MIN_VALUE + (GlobalKeywords.TEMPERATURE_MAX_VALUE - GlobalKeywords.TEMPERATURE_MIN_VALUE) * r.nextDouble(); //Create a new event to notify the variation of a Physical Property PhysicalAssetPropertyWldtEvent\u0026lt;Double\u0026gt; newPhysicalPropertyEvent = new PhysicalAssetPropertyWldtEvent\u0026lt;\u0026gt;(GlobalKeywords.TEMPERATURE_PROPERTY_KEY, randomTemperature); //Publish the WLDTEvent associated to the Physical Property Variation publishPhysicalAssetPropertyWldtEvent(newPhysicalPropertyEvent); } //Publish a demo Physical Event associated to a \u0026#39;critical\u0026#39; overheating condition publishPhysicalAssetEventWldtEvent(new PhysicalAssetEventWldtEvent\u0026lt;\u0026gt;(GlobalKeywords.OVERHEATING_EVENT_KEY, \u0026#34;critical\u0026#34;)); } catch (EventBusException | InterruptedException e) { e.printStackTrace(); } }; } } Both Physical Adapters and Digital Adapters can be defined natively with a custom configuration provided by the developer as illustrated in the dedicated Section: Configurable Physical \u0026amp; Digital Adapters.\n","date":"February 9, 2024","id":4,"permalink":"/docs/guides/physical-adapter/","summary":"The developer can use an existing Physical Adapter or create a new one to handle the communication with a specific physical twin.","tags":"","title":"Physical Adapter"},{"content":"After the definition of the Physical Adapter it is time to start implementing the core of our DT through the definition of its shadowing function in charge of:\nHandle received PAD from Physical Adapters in order to device which properties, events, relationships or actions available on connected physical twins should be mapped and managed into the DT State Manage incoming notifications/callbacks associated to the variation of physical properties (e.g, temperature variation) or the generation of physical event (e.g., overheating) Process action requests from the digital world that should be validated and forward to the correct Physical Adapter in order to trigger the associated actions on the physical world The Shadowing Function has the responsibility to build and maintain the updated state of the Digital Twin The internal variable of any WLDT Shadowing Function (available through the base class ShadowingFunction) used to do that is DigitalTwinStateManager accessible through the variable: this.digitalTwinStateManager\nWhen the Shadowing Function has to compute the new DT State it can now work with the following method to handle DT State Transition:\nStart the DT State Transaction: startStateTransaction() DT State variation methods such as: createProperty() updateProperty() updatePropertyValue() deleteProperty() enableAction() updateAction() disableAction() registerEvent() updateRegisteredEvent() unRegisterEvent() createRelationship() addRelationshipInstance() deleteRelationship() deleteRelationshipInstance() At the end the transaction can be committed using the method: commitStateTransaction() To access the current DT State the Shadowing Function implementation can use the method this.digitalTwinStateManager.getDigitalTwinState() The information available on the DT State are:\nproperties: List of Properties with their values (if available) actions: List of Actions that can be called on the DT events: List of Events that can be generated by the DT relationships: List of Relationships and their instances (if available) evaluationInstant: The timestamp representing the evaluation instant of the DT state Available main methods on that class instance are:\nProperties: getProperty(String propertyKey): Retrieves if present the target DigitalTwinStateProperty by Key containsProperty(String propertyKey): Checks if a target Property Key is already available in the current Digital Twin\u0026rsquo;s State getPropertyList(): Loads the list of available Properties (described by the class DigitalTwinStateProperty) available on the Digital Twin\u0026rsquo;s State createProperty(DigitalTwinStateProperty\u0026lt;?\u0026gt; dtStateProperty): Allows the creation of a new Property on the Digital Twin\u0026rsquo;s State through the class DigitalTwinStateProperty readProperty(String propertyKey): Retrieves if present the target DigitalTwinStateProperty by Key updateProperty(DigitalTwinStateProperty\u0026lt;?\u0026gt; dtStateProperty): Updates the target property using the DigitalTwinStateProperty and the associated Property Key field deleteProperty(String propertyKey): Deletes the target property identified by the specified key Actions: containsAction(String actionKey): Checks if a Digital Twin State Action with the specified key is correctly registered getAction(String actionKey): Loads the target DigitalTwinStateAction by key getActionList(): Gets the list of available Actions registered on the Digital Twin\u0026rsquo;s State enableAction(DigitalTwinStateAction digitalTwinStateAction): Enables and registers the target Action described through an instance of the DigitalTwinStateAction class updateAction(DigitalTwinStateAction digitalTwinStateAction): Update the already registered target Action described through an instance of the DigitalTwinStateAction class disableAction(String actionKey): Disables and unregisters the target Action described through an instance of the DigitalTwinStateAction class Events: containsEvent(String eventKey): Check if a Digital Twin State Event with the specified key is correctly registered getEvent(String eventKey): Return the description of a registered Digital Twin State Event according to its Key getEventList(): Return the list of existing and registered Digital Twin State Events registerEvent(DigitalTwinStateEvent digitalTwinStateEvent): Register a new Digital Twin State Event updateRegisteredEvent(DigitalTwinStateEvent digitalTwinStateEvent): Update the registration and signature of an existing Digital Twin State Event unRegisterEvent(String eventKey): Un-register a Digital Twin State Event notifyDigitalTwinStateEvent(DigitalTwinStateEventNotification\u0026lt;?\u0026gt; digitalTwinStateEventNotification): Method to notify the occurrence of the target Digital Twin State Event Relationships: containsRelationship(String relationshipName): Checks if a Relationship Name is already available in the current Digital Twin\u0026rsquo;s State createRelationship(DigitalTwinStateRelationship\u0026lt;?\u0026gt; relationship): Creates a new Relationships (described by the class DigitalTwinStateRelationship) in the Digital Twin\u0026rsquo;s State addRelationshipInstance(String name, DigitalTwinStateRelationshipInstance\u0026lt;?\u0026gt; instance): Adds a new Relationship instance described through the class DigitalTwinStateRelationshipInstance and identified through its name getRelationshipList(): Loads the list of existing relationships on the Digital Twin\u0026rsquo;s State through a list of DigitalTwinStateRelationship getRelationship(String name): Gets a target Relationship identified through its name and described through the class DigitalTwinStateRelationship deleteRelationship(String name): Deletes a target Relationship identified through its name deleteRelationshipInstance(String relationshipName, String instanceKey): Deletes the target Relationship Instance using relationship name and instance Key The basic library class that we are going to extend is called ShadowingFunction and creating a new class named DemoShadowingFunction the resulting code is the same after implementing required methods the basic constructor with the id String parameter.\nimport it.wldt.adapter.digital.event.DigitalActionWldtEvent; import it.wldt.adapter.physical.PhysicalAssetDescription; import it.wldt.adapter.physical.event.PhysicalAssetEventWldtEvent; import it.wldt.adapter.physical.event.PhysicalAssetPropertyWldtEvent; import it.wldt.adapter.physical.event.PhysicalAssetRelationshipInstanceCreatedWldtEvent; import it.wldt.adapter.physical.event.PhysicalAssetRelationshipInstanceDeletedWldtEvent; import it.wldt.core.model.ShadowingModelFunction; import java.util.Map; public class DemoShadowingFunction extends ShadowingModelFunction { public DemoShadowingFunction(String id) { super(id); } //// Shadowing Function Management Callbacks //// @Override protected void onCreate() { } @Override protected void onStart() { } @Override protected void onStop() { } //// Bound LifeCycle State Management Callbacks //// @Override protected void onDigitalTwinBound(Map\u0026lt;String, PhysicalAssetDescription\u0026gt; adaptersPhysicalAssetDescriptionMap) { } @Override protected void onDigitalTwinUnBound(Map\u0026lt;String, PhysicalAssetDescription\u0026gt; map, String s) { } @Override protected void onPhysicalAdapterBidingUpdate(String s, PhysicalAssetDescription physicalAssetDescription) { } //// Physical Property Variation Callback //// @Override protected void onPhysicalAssetPropertyVariation(PhysicalAssetPropertyWldtEvent\u0026lt;?\u0026gt; physicalAssetPropertyWldtEvent) { } //// Physical Event Notification Callback //// @Override protected void onPhysicalAssetEventNotification(PhysicalAssetEventWldtEvent\u0026lt;?\u0026gt; physicalAssetEventWldtEvent) { } //// Physical Relationships Notification Callbacks //// @Override protected void onPhysicalAssetRelationshipEstablished(PhysicalAssetRelationshipInstanceCreatedWldtEvent\u0026lt;?\u0026gt; physicalAssetRelationshipInstanceCreatedWldtEvent) { } @Override protected void onPhysicalAssetRelationshipDeleted(PhysicalAssetRelationshipInstanceDeletedWldtEvent\u0026lt;?\u0026gt; physicalAssetRelationshipInstanceDeletedWldtEvent) { } //// Digital Action Received Callbacks //// @Override protected void onDigitalActionEvent(DigitalActionWldtEvent\u0026lt;?\u0026gt; digitalActionWldtEvent) { } } The methods onCreate(), onStart() and onStop() are used to receive callbacks from the DT\u0026rsquo;s core when the Shadowing Function has been effectively created within the twin, is started or stopped according to the evolution of its life cycle. In our initial implementation we are not implementing any of them but they can be useful to trigger specific behaviours according to the different phases.\nThe first method that we have to implement in order to analyze received PAD and build the Digital Twin State in terms of properties, events, relationships and available actions is the onDigitalTwinBound(Map\u0026lt;String, PhysicalAssetDescription\u0026gt; map) method. In our initial implementation we just pass through all the received characteristics recevied from each connected Physical Adapter mapping every physical entity into the DT\u0026rsquo;s state without any change or adaptation (Of course complex behaviour can be implemented to customized the digitalization process).\nThrough the following method we implement the following behaviour:\nAnalyze each received PAD from each connected and active Physical Adapter (in our case we will have just 1 Physical Adapter) Iterate over all the received Properties for each PAD and create the same Property on the Digital Twin State Start observing target Physical Properties in order to receive notification callback about physical variation through the method observePhysicalAssetProperty(property); Analyze received PAD\u0026rsquo;s Events declaration and recreates them also on the DT\u0026rsquo;s State Start observing target Physical Event in order to receive notification callback about physical event generation through the method observePhysicalAssetEvent(event); Check available Physical Action and enable them on the DT\u0026rsquo;s State. Enabled Digital Action are automatically observed by the Shadowing Function in order to receive action requests from active Digital Adapters The possibility to manually observe Physical Properties and Event has been introduced to allow the Shadowing Function to decide what to do according to the nature of the property or of the target event. For example in some cases with static properties it will not be necessary to observe any variation, and it will be enough to read the initial value to build the digital replica of that specific property.\nSince the DT State is managed through the DigitalTwinStateManager class all the changes and variation should be applied on the DT ShadowingFunction using the previously presented transaction management and the correct call of methods startStateTransaction() and commitStateTransaction().\n@Override protected void onDigitalTwinBound(Map\u0026lt;String, PhysicalAssetDescription\u0026gt; adaptersPhysicalAssetDescriptionMap) { try{ // NEW from 0.3.0 -\u0026gt; Start DT State Change Transaction this.digitalTwinStateManager.startStateTransaction(); //Iterate over all the received PAD from connected Physical Adapters adaptersPhysicalAssetDescriptionMap.values().forEach(pad -\u0026gt; { //Iterate over all the received PAD from connected Physical Adapters adaptersPhysicalAssetDescriptionMap.values().forEach(pad -\u0026gt; { pad.getProperties().forEach(property -\u0026gt; { try { //Create and write the property on the DT\u0026#39;s State this.digitalTwinState.createProperty(new DigitalTwinStateProperty\u0026lt;\u0026gt;(property.getKey(),(Double) property.getInitialValue())); //Start observing the variation of the physical property in order to receive notifications //Without this call the Shadowing Function will not receive any notifications or callback about //incoming physical property of the target type and with the target key this.observePhysicalAssetProperty(property); } catch (Exception e) { e.printStackTrace(); } }); //Iterate over available declared Physical Events for the target Physical Adapter\u0026#39;s PAD pad.getEvents().forEach(event -\u0026gt; { try { //Instantiate a new DT State Event with the same key and type DigitalTwinStateEvent dtStateEvent = new DigitalTwinStateEvent(event.getKey(), event.getType()); //Create and write the event on the DT\u0026#39;s State this.digitalTwinState.registerEvent(dtStateEvent); //Start observing the variation of the physical event in order to receive notifications //Without this call the Shadowing Function will not receive any notifications or callback about //incoming physical events of the target type and with the target key this.observePhysicalAssetEvent(event); } catch (Exception e) { e.printStackTrace(); } }); //Iterate over available declared Physical Actions for the target Physical Adapter\u0026#39;s PAD pad.getActions().forEach(action -\u0026gt; { try { //Instantiate a new DT State Action with the same key and type DigitalTwinStateAction dtStateAction = new DigitalTwinStateAction(action.getKey(), action.getType(), action.getContentType()); //Enable the action on the DT\u0026#39;s State this.digitalTwinState.enableAction(dtStateAction); } catch (Exception e) { e.printStackTrace(); } }); }); // NEW from 0.3.0 -\u0026gt; Commit DT State Change Transaction to apply the changes on the DT State and notify about the change this.digitalTwinStateManager.commitStateTransaction(); //Start observation to receive all incoming Digital Action through active Digital Adapter //Without this call the Shadowing Function will not receive any notifications or callback about //incoming request to execute an exposed DT\u0026#39;s Action observeDigitalActionEvents(); //Notify the DT Core that the Bounding phase has been correctly completed and the DT has evaluated its //internal status according to what is available and declared through the Physical Adapters notifyShadowingSync(); }catch (Exception e){ e.printStackTrace(); } } In particular the method observeDigitalActionEvents() should be called start the observation of digital actions and to receive all incoming Digital Action through active Digital Adapters. Without this call the Shadowing Function will not receive any notifications or callback about incoming request to execute an exposed DT\u0026rsquo;s Action. Of course, we have to call this method if we are mapping any digital action in our DT.\nAnother fundamental method is notifyShadowingSync() used to notify the DT Core that the Bounding phase has been correctly completed and the DT has evaluated its internal status according to what is available and declared through the Physical Adapters.\nAs mentioned, in the previous example the Shadowing Function does not apply any control or check on the nature of declared physical property. Of course in order to have a more granular control, it will be possible to use property Key or any other field or even the type of the instance through an instanceof check to implement different controls and behaviours.\nA variation (only for the property management code) to the previous method can be the following:\n//Iterate over available declared Physical Property for the target Physical Adapter\u0026#39;s PAD pad.getProperties().forEach(property -\u0026gt; { try { //Check property Key and Instance of to validate that is a Double if(property.getKey().equals(\u0026#34;temperature-property-key\u0026#34;) \u0026amp;\u0026amp; property.getInitialValue() != null \u0026amp;\u0026amp; property.getInitialValue() instanceof Double) { //Instantiate a new DT State Property of the right type, the same key and initial value DigitalTwinStateProperty\u0026lt;Double\u0026gt; dtStateProperty = new DigitalTwinStateProperty\u0026lt;Double\u0026gt;(property.getKey(),(Double) property.getInitialValue()); //Create and write the property on the DT\u0026#39;s State this.digitalTwinState.createProperty(dtStateProperty); //Start observing the variation of the physical property in order to receive notifications //Without this call the Shadowing Function will not receive any notifications or callback about //incoming physical property of the target type and with the target key this.observePhysicalAssetProperty(property); } } catch (Exception e) { e.printStackTrace(); } }); The next method that we have to implement in order to properly define and implement the behaviour of our DT through its ShadowingModelFunction are:\nonPhysicalAssetPropertyVariation: Method called when a new variation for a specific Physical Property has been detected by the associated Physical Adapter. The method receive as parameter a specific WLDT Event called PhysicalAssetPropertyWldtEvent\u0026lt;?\u0026gt; physicalPropertyEventMessage containing all the information generated by the Physical Adapter upon the variation of the monitored physical counterpart. onPhysicalAssetEventNotification: Callback method used to be notified by a PhysicalAdapter about the generation of a Physical Event. As for the previous method, also this function receive a WLDT Event parameter of type onPhysicalAssetEventNotification(PhysicalAssetEventWldtEvent\u0026lt;?\u0026gt; physicalAssetEventWldtEvent)) containing all the field of the generated physical event. onDigitalActionEvent: On the opposite this method is triggered from one of the active Digital Adapter when an Action request has been received on the Digital Interface. The method receive as parameter an instance of the WLDT Event class DigitalActionWldtEvent\u0026lt;?\u0026gt; digitalActionWldtEvent describing the target digital action request and the associated body. For the onPhysicalAssetPropertyVariation a simple implementation in charge ONLY of mapping the new Physical Property value into the corresponding DT\u0026rsquo;State property can be implemented as follows:\nThe DT State transaction management should be applied in the point of the code where the Shadowing Function receive a variation from the Physical world through a target adapter and the callback method onPhysicalAssetPropertyVariation(...)\n@Override protected void onPhysicalAssetPropertyVariation(PhysicalAssetPropertyWldtEvent\u0026lt;?\u0026gt; physicalPropertyEventMessage) { try { //Update Digital Twin State //NEW from 0.3.0 -\u0026gt; Start State Transaction this.digitalTwinStateManager.startStateTransaction(); this.digitalTwinState.updateProperty(new DigitalTwinStateProperty\u0026lt;\u0026gt;(physicalPropertyEventMessage.getPhysicalPropertyId(), physicalPropertyEventMessage.getBody())); //NEW from 0.3.0 -\u0026gt; Commit State Transaction this.digitalTwinStateManager.commitStateTransaction(); } catch (WldtDigitalTwinStatePropertyException | WldtDigitalTwinStatePropertyBadRequestException | WldtDigitalTwinStatePropertyNotFoundException | WldtDigitalTwinStateException e) { e.printStackTrace(); } } In this case as reported in the code, we call the method this.digitalTwinState.updateProperty on the Shadowing Function in order to update an existing DT\u0026rsquo;State property (previously created in the onDigitalTwinBound method). To update the value we directly use the received data on the PhysicalAssetPropertyWldtEvent without any additional check or change that might be instead needed in advanced examples.\nFollowing the same principle, a simplified digital mapping between physical and digital state upon the receving of a physical event variation can be the following:\n@Override protected void onPhysicalAssetEventNotification(PhysicalAssetEventWldtEvent\u0026lt;?\u0026gt; physicalAssetEventWldtEvent) { try { this.digitalTwinStateManager.notifyDigitalTwinStateEvent(new DigitalTwinStateEventNotification\u0026lt;\u0026gt;(physicalAssetEventWldtEvent.getPhysicalEventKey(), physicalAssetEventWldtEvent.getBody(), physicalAssetEventWldtEvent.getCreationTimestamp())); } catch (WldtDigitalTwinStateEventNotificationException | EventBusException e) { e.printStackTrace(); } } With respect to events management, we use the Shadowint Function method this.digitalTwinState.notifyDigitalTwinStateEvent to notify the other DT Components (e.g., Digital Adapters) the incoming Physical Event by creating a new instance of a DigitalTwinStateEventNotification class containing all the information associated to the event. Of course, additional controls and checks can be introduced in this method validating and processing the incoming physical message to define complex behaviours.\nThe last method that we are going to implement is the onDigitalActionEvent one where we have to handle an incoming Digital Action request associated to an Action declared on the DT\u0026rsquo;s State in the onDigitalTwinBound method. In that case the Digital Action should be forwarded to the Physical Interface in order to be sent to the physical counterpart for the effective execution.\n@Override protected void onDigitalActionEvent(DigitalActionWldtEvent\u0026lt;?\u0026gt; digitalActionWldtEvent) { try { this.publishPhysicalAssetActionWldtEvent(digitalActionWldtEvent.getActionKey(), digitalActionWldtEvent.getBody()); } catch (EventBusException e) { e.printStackTrace(); } } Also in that case we are forwarding the incoming Digital Action request described through the class DigitalActionWldtEvent to the Physical Adapter with the method of the Shadowing Function denoted as this.publishPhysicalAssetActionWldtEvent and passing directly the action key and the target Body. No additional processing or validation have been introduced here, but they might be required in advanced scenario in order to properly adapt incoming digital action request to what is effectively expected on the physical counterpart.\n","date":"February 9, 2024","id":5,"permalink":"/docs/guides/shadowing-function/","summary":"After the definition of the Physical Adapter it is time to start implementing the core of our DT through the definition of its shadowing function in charge of:","tags":"","title":"Shadowing Function"},{"content":"The las component that we have to implement to complete our first simple Digital Twin definition through the WLDT library is a Digital Adapter in charge of:\nReceiving event from the DT\u0026rsquo;s Core related to the variation of properties, events, available actions and relationships Expose received information to the external world according to its implementation and the supported protocol Handle incoming digital action and forward them to the Core in order to be validated and processed by the Shadowing Function The basic library class that we are going to extend is called DigitalAdapter and creating a new class named DemoDigitalAdapter. The DigitalTwinAdapter class can take as Generic Type the type of Configuration used to configure its behaviours. In this simplified example we are defining a DigitalAdapter without any Configuration.\nA Digital Adapter has direct access to the current DT\u0026rsquo;s State through callbacks or directly in a synchronous way using the internal variable called: digitalTwinState. Through it is possibile to navigate all the fields currently composing the state of our Digital Twin.\nThe Digital Adapter class has e long list of callback and notification method to allow the adapter to be updated about all the variation and changes on the twin. Available callbacks can be summarized as follows:\nDigital Adapter Start/Stop: onAdapterStart(): Feedback when the Digital Adapter correctly starts onAdapterStop(): Feedback when the Digital Adapter has been stopped Digital Twin Life Cycle Notifications: onDigitalTwinCreate(): The DT has been created onDigitalTwinStart(): The DT started onDigitalTwinSync(IDigitalTwinState digitalTwinState): The DT is Synchronized with its physical counterpart. The current DigitalTwinState is passed as parameter to allow the Digital Adapter to know the current state and consequently implement its behaviour onDigitalTwinUnSync(IDigitalTwinState digitalTwinState): The DT is not synchronized anymore with its physical counterpart. The last current DigitalTwinState is passed as parameter to allow the Digital Adapter to know the last state and consequently implement its behaviour onDigitalTwinStop(): The DT is stopped onDigitalTwinDestroy(): The DT has been destroyed and the application stopped The Digital Adapter DT State variations and DT events are received by the Adapter from the DT core belongs to the following categories:\nDigital Twin State Update through the method onStateUpdate(...) providing information about the new state of the Digital Twin, the previous state, and a list of changes that occurred between these two states. In the previous version each variation of a property, relationships, actions or events were notified. In the new version only a committed DT\u0026rsquo;State variation is notified to listeners. Event Notifications through the method onEventNotificationReceived(...) whenever there is a notification about an event related to the Digital Twin\u0026rsquo;s state coming from the physical world, generated by the twin and processed by the Shadowing Function. For example in the DT State we can have the declaration of the over-heating-alert structured and received in the DT State while the effective occurrence of the event and the associated notification is notified through this dedicated callback The onStateUpdate method is an abstract method that must be implemented by any class extending the DigitalAdapter class. This method is called whenever there is an update to the Digital Twin\u0026rsquo;s state. It provides information about the new state of the Digital Twin, the previous state, and a list of changes that occurred between these two states.\nThe explanation of the parameters is the following:\nnewDigitalTwinState: This parameter represents the updated state of the Digital Twin. It is an instance of the DigitalTwinState class, which encapsulates the current state information. previousDigitalTwinState: This parameter represents the state of the Digital Twin before the update. It is also an instance of the DigitalTwinState class. digitalTwinStateChangeList: This parameter is an ArrayList containing DigitalTwinStateChange objects. Each DigitalTwinStateChange object encapsulates information about a specific change that occurred between the previous and new states. It includes details such as the property or aspect of the state that changed, the previous value, and the new value. Another core method where a Digital Adapter receive the description of the DT\u0026rsquo;State is onDigitalTwinSync(IDigitalTwinState digitalTwinState). The Adapter using the parameter digitalTwinState can analyze available properties, actions, events and relationships and decide how to implement its internal behaviour with the methods presented in ShadowingFunction. The DT State is automatically monitored by each Digital Adapter while for the Events potentially generated by the DT can be observed by each adapter using:\nobserveAllDigitalTwinEventsNotifications: Enable the observation of available Digital Twin State Events Notifications. unObserveAllDigitalTwinEventsNotifications: Cancel the observation of Digital Twin State Events Notifications observeDigitalTwinEventsNotifications: Enable the observation of the notification associated to a specific list of Digital Twin State events. With respect to event a notification contains the new associated value unObserveDigitalTwinEventsNotifications: Cancel the observation of a target list of properties observeDigitalTwinEventNotification: Enable the observation of the notification associated to a single Digital Twin State event. With respect to event a notification contains the new associated value unObserveDigitalTwinEventNotification: Cancel the observation of a single target event The resulting code will be the following after adding the required methods (still empty) and the basic constructor with the id String parameter is the following:\nimport it.wldt.adapter.digital.DigitalAdapter; import it.wldt.core.state.*; public class DemoDigitalAdapter extends DigitalAdapter\u0026lt;Void\u0026gt; { public DemoDigitalAdapter(String id) { super(id); } /** * Callback to notify the adapter on its correct startup */ @Override public void onAdapterStart() {} /** * Callback to notify the adapter that has been stopped */ @Override public void onAdapterStop() {} /** * DT Life Cycle notification that the DT is correctly on Sync * @param digitalTwinState */ @Override public void onDigitalTwinSync(DigitalTwinState digitalTwinState) {} /** * DT Life Cycle notification that the DT is currently Not Sync * @param digitalTwinState */ @Override public void onDigitalTwinUnSync(DigitalTwinState digitalTwinState) {} /** * DT Life Cycle notification that the DT has been created */ @Override public void onDigitalTwinCreate() {} /** * DT Life Cycle Notification that the DT has correctly Started */ @Override public void onDigitalTwinStart() {} /** * DT Life Cycle Notification that the DT has been stopped */ @Override public void onDigitalTwinStop() {} /** * DT Life Cycle Notification that the DT has destroyed */ @Override public void onDigitalTwinDestroy() {} /** * Callback method allowing the Digital Adapter to receive the updated Digital Twin State together with * the previous state and the list of applied changes * * @param newDigitalTwinState The new Digital Twin State computed by the Shadowing Function * @param previousDigitalTwinState The previous Digital Twin State * @param digitalTwinStateChangeList The list of applied changes to compute the new Digital Twin State */ @Override protected void onStateUpdate(DigitalTwinState newDigitalTwinState, DigitalTwinState previousDigitalTwinState, ArrayList\u0026lt;DigitalTwinStateChange\u0026gt; digitalTwinStateChangeList) {} /** * Callback method to receive a new computed Event Notification (associated to event declared in the DT State) * * @param digitalTwinStateEventNotification The generated Notification associated to a DT Event */ @Override protected void onEventNotificationReceived(DigitalTwinStateEventNotification\u0026lt;?\u0026gt; digitalTwinStateEventNotification) {} } By default, a Digital Adapter observes all the variation on the DT\u0026rsquo;s State in terms of Properties, Relationships, Actions and Events. As previously mentioned the observation of DT\u0026rsquo;s State Properties allows to receive also properties variation on the method since a property is natively composed by its description (e.g., type) and its current value. On the opposite the observation on DT\u0026rsquo;s State Action, Relationships and Events allow ONLY to receive callbacks when a new entity is added or an update is occurred without receiving updates on values variation.\nThe only thing that we should add in the onDigitalTwinSync(IDigitalTwinState currentDigitalTwinState) callback is the direct observation for Events. Following this approach we can change our Digital Adapter in the following methods:\nIn onDigitalTwinSync we observe in this first simple implementation only the incoming values for declared Events in the DT\u0026rsquo;State. As previously mentioned the observation of any variation of the State structure together with Properties Values are by default observed by any Digital Adapter. In this method we use the internal variable digitalTwinState to access the DT\u0026rsquo;s state and find available Events declaration that we would like to observe.\npublic void onDigitalTwinSync(IDigitalTwinState currentDigitalTwinState) { try { //Retrieve the list of available events and observe all variations digitalTwinState.getEventList() .map(eventList -\u0026gt; eventList.stream() .map(DigitalTwinStateEvent::getKey) .collect(Collectors.toList())) .ifPresent(eventKeys -\u0026gt; { try { observeDigitalTwinEventsNotifications(eventKeys); } catch (EventBusException e) { e.printStackTrace(); } }); } catch (Exception e) { e.printStackTrace(); } } Developers extending the DigitalAdapter class should implement the onStateUpdate method to define custom logic that needs to be executed whenever the state of the Digital Twin is updated. This could include tasks such as processing state changes, updating internal variables, triggering specific actions, or notifying other components about the state update.\nHere\u0026rsquo;s an example of how the method might be implemented in a concrete subclass of DigitalAdapter:\n@Override protected void onStateUpdate(DigitalTwinState newDigitalTwinState, DigitalTwinState previousDigitalTwinState, ArrayList\u0026lt;DigitalTwinStateChange\u0026gt; digitalTwinStateChangeList) { // In newDigitalTwinState we have the new DT State System.out.println(\u0026#34;New DT State is: \u0026#34; + newDigitalTwinState); // The previous DT State is available through the variable previousDigitalTwinState System.out.println(\u0026#34;Previous DT State is: \u0026#34; + previousDigitalTwinState); // We can also check each DT\u0026#39;s state change potentially differentiating the behaviour for each change if (digitalTwinStateChangeList != null \u0026amp;\u0026amp; !digitalTwinStateChangeList.isEmpty()) { // Iterate through each state change in the list for (DigitalTwinStateChange stateChange : digitalTwinStateChangeList) { // Get information from the state change DigitalTwinStateChange.Operation operation = stateChange.getOperation(); DigitalTwinStateChange.ResourceType resourceType = stateChange.getResourceType(); DigitalTwinStateResource resource = stateChange.getResource(); // Perform different actions based on the type of operation switch (operation) { case OPERATION_UPDATE: // Handle an update operation System.out.println(\u0026#34;Update operation on \u0026#34; + resourceType + \u0026#34;: \u0026#34; + resource); break; case OPERATION_UPDATE_VALUE: // Handle an update value operation System.out.println(\u0026#34;Update value operation on \u0026#34; + resourceType + \u0026#34;: \u0026#34; + resource); break; case OPERATION_ADD: // Handle an add operation System.out.println(\u0026#34;Add operation on \u0026#34; + resourceType + \u0026#34;: \u0026#34; + resource); break; case OPERATION_REMOVE: // Handle a remove operation System.out.println(\u0026#34;Remove operation on \u0026#34; + resourceType + \u0026#34;: \u0026#34; + resource); break; default: // Handle unknown operation (optional) System.out.println(\u0026#34;Unknown operation on \u0026#34; + resourceType + \u0026#34;: \u0026#34; + resource); break; } } } else { // No state changes System.out.println(\u0026#34;No state changes detected.\u0026#34;); } } In this example, the method iterates over the list of state changes, extracts information about each change, and performs custom actions based on the changes. Developers can adapt this method to suit the specific requirements of their Digital Twin application.\nBoth Physical Adapters and Digital Adapters can be defined natively with a custom configuration provided by the developer as illustrated in the dedicated Section: Configurable Physical \u0026amp; Digital Adapters.\n","date":"February 9, 2024","id":6,"permalink":"/docs/guides/digital-adapter/","summary":"The las component that we have to implement to complete our first simple Digital Twin definition through the WLDT library is a Digital Adapter in charge of:","tags":"","title":"Digital Adapter"},{"content":"Now that we have created the main fundamental element of a DT (Physical Adapter, Shadowing Function and Digital Adapter) we can create Class file with a main to create the WLDT Engine with the created components and start the DT.\nCreate a new Java file called DemoDigitalTwin adding the following code:\nWith the following code we now create a new Digital Twin Instance\n// Create the new Digital Twin with its Shadowing Function DigitalTwin digitalTwin = new DigitalTwin(digitalTwinId, new DemoShadowingFunction()); // Physical Adapter with Configuration digitalTwin.addPhysicalAdapter( new DemoPhysicalAdapter( String.format(\u0026#34;%s-%s\u0026#34;, digitalTwinId, \u0026#34;test-physical-adapter\u0026#34;), new DemoPhysicalAdapterConfiguration(), true)); // Digital Adapter with Configuration digitalTwin.addDigitalAdapter( new DemoDigitalAdapter( String.format(\u0026#34;%s-%s\u0026#34;, digitalTwinId, \u0026#34;test-digital-adapter\u0026#34;), new DemoDigitalAdapterConfiguration()) ); DTs cannot be directly run but it should be added to the DigitalTwinEngine in order to be executed through the WLDT Library\n// Create the Digital Twin Engine DigitalTwinEngine digitalTwinEngine = new DigitalTwinEngine(); // Add the Digital Twin to the Engine digitalTwinEngine.addDigitalTwin(digitalTwin); In order to start a DT from the Engine you can:\n// Directly start when you add it passing a second boolean value = true digitalTwinEngine.addDigitalTwin(digitalTwin. true); // Starting the single DT on the engine through its id digitalTwinEngine.startDigitalTwin(DIGITAL_TWIN_ID); // Start all the DTs registered on the engine digitalTwinEngine.startAll(); To stop a single twin or all the twin registered on the engine:\n// Stop a single DT on the engine through its id digitalTwinEngine.stopDigitalTwin(DIGITAL_TWIN_ID); // Stop all the DTs registered on the engine digitalTwinEngine.stopAll(); It is also possible to remove a DT from the Engine with a consequent stop if it is active and the deletion of its reference from the engine:\n// Remove a single DT on the engine through its id digitalTwinEngine.removeDigitalTwin(DIGITAL_TWIN_ID); // Remove all the DTs registered on the engine digitalTwinEngine.removeAll(); The resulting code in our case is:\npublic class DemoDigitalTwin { public static void main(String[] args) { try{ // Create the new Digital Twin DigitalTwin digitalTwin = new DigitalTwin( \u0026#34;test-dt-id\u0026#34;, new DemoShadowingFunction(\u0026#34;test-shadowing-function\u0026#34;) ); //Default Physical and Digital Adapter //digitalTwin.addPhysicalAdapter(new DemoPhysicalAdapter(\u0026#34;test-physical-adapter\u0026#34;)); //digitalTwin.addDigitalAdapter(new DemoDigitalAdapter(\u0026#34;test-digital-adapter\u0026#34;)); //Physical and Digital Adapters with Configuration digitalTwin.addPhysicalAdapter(new DemoConfPhysicalAdapter(\u0026#34;test-physical-adapter\u0026#34;, new DemoPhysicalAdapterConfiguration())); digitalTwin.addDigitalAdapter(new DemoConfDigitalAdapter(\u0026#34;test-digital-adapter\u0026#34;, new DemoDigitalAdapterConfiguration())); // Create the Digital Twin Engine DigitalTwinEngine digitalTwinEngine = new DigitalTwinEngine(); // Add the Digital Twin to the Engine digitalTwinEngine.addDigitalTwin(digitalTwin); // Set a new Event-Logger to a Custom One that we created with the class \u0026#39;DemoEventLogger\u0026#39; WldtEventBus.getInstance().setEventLogger(new DemoEventLogger()); // Start all the DTs registered on the engine digitalTwinEngine.startAll(); }catch (Exception e){ e.printStackTrace(); } } } ","date":"February 9, 2024","id":7,"permalink":"/docs/guides/dt-engine-dt-instance/","summary":"Now that we have created the main fundamental element of a DT (Physical Adapter, Shadowing Function and Digital Adapter) we can create Class file with a main to create the WLDT Engine with the created components and start the DT.","tags":"","title":"DT Engine \u0026 DT Instance"},{"content":"In this demo implementation, we are going to emulate an incoming Digital Action on the Digital Adapter in order to show how it can be handled by the adapter and properly forwarded to the Shadowing Function for validation and the consequent interaction with the Physical Adapter and then with the physical twin.\nIn order to add a demo Digital Action trigger on the Digital Adapter we add the following method to the DemoDigitalAdapter class:\nprivate Runnable emulateIncomingDigitalAction(){ return () -\u0026gt; { try { System.out.println(\u0026#34;Sleeping before Emulating Incoming Digital Action ...\u0026#34;); Thread.sleep(5000); Random random = new Random(); //Emulate the generation on \u0026#39;n\u0026#39; temperature measurements for(int i = 0; i \u0026lt; 10; i++){ //Sleep to emulate sensor measurement Thread.sleep(1000); double randomTemperature = 25.0 + (30.0 - 25.0) * random.nextDouble(); publishDigitalActionWldtEvent(\u0026#34;set-temperature-action-key\u0026#34;, randomTemperature); } } catch (Exception e) { e.printStackTrace(); } }; } This method uses the Digital Adapter internal function denoted as publishDigitalActionWldtEvent(String actionKey, T body) allowing the adapter to send a notification to the DT\u0026rsquo;s Core (and consequently the Shadowing Function) about the arrival of a Digital Action with a specific key and body. In our case the key is set-temperature-action-key as declared in the Physical Adapter and in the PAD and the value is a simple Double with the new temperature value.\nThen we call this method in the following way at the end ot the onDigitalTwinSync(IDigitalTwinState currentDigitalTwinState) method.\n//Start Digital Action Emulation new Thread(emulateIncomingDigitalAction()).start(); Now the Shadowing Function should be updated in order to handle the incoming Action request from the Digital Adapter. In our case the shadowing function does not apply any validation or check and just forward to action to the Physical Adapter in order to be then forwarded to the physical twin. Of course advanced implementation can be introduced for example to validate action, adapt payload and data-formats or to augment functionalities (e.g., trigger multiple physical actions from a single digital request).\nIn our simple demo implementation the updated Shadowing Function method onDigitalActionEvent(DigitalActionWldtEvent\u0026lt;?\u0026gt; digitalActionWldtEvent) results as follows:\n@Override protected void onDigitalActionEvent(DigitalActionWldtEvent\u0026lt;?\u0026gt; digitalActionWldtEvent) { try { this.publishPhysicalAssetActionWldtEvent(digitalActionWldtEvent.getActionKey(), digitalActionWldtEvent.getBody()); } catch (Exception e) { e.printStackTrace(); } } This forwarding of the action triggers the corresponding Physical Adapter method onIncomingPhysicalAction(PhysicalAssetActionWldtEvent\u0026lt;?\u0026gt; physicalAssetActionWldtEvent) that in our case is emulated just with a Log on the console. Also in that case advanced Physical Adapter implementation can be introduced for example to adapt the request from a high-level (and potentially standard) DT action description to the custom requirements of the specific physical twin managed by the adapter.\n@Override public void onIncomingPhysicalAction(PhysicalAssetActionWldtEvent\u0026lt;?\u0026gt; physicalAssetActionWldtEvent) { try{ if(physicalAssetActionWldtEvent != null \u0026amp;\u0026amp; physicalAssetActionWldtEvent.getActionKey().equals(SET_TEMPERATURE_ACTION_KEY) \u0026amp;\u0026amp; physicalAssetActionWldtEvent.getBody() instanceof Double) { System.out.println(\u0026#34;Received Action Request: \u0026#34; + physicalAssetActionWldtEvent.getActionKey() + \u0026#34; with Body: \u0026#34; + physicalAssetActionWldtEvent.getBody()); } else System.err.println(\u0026#34;Wrong Action Received !\u0026#34;); }catch (Exception e){ e.printStackTrace(); } } ","date":"February 9, 2024","id":8,"permalink":"/docs/guides/digital-actions/","summary":"In this demo implementation, we are going to emulate an incoming Digital Action on the Digital Adapter in order to show how it can be handled by the adapter and properly forwarded to the Shadowing Function for validation and the consequent interaction with the Physical Adapter and then with the physical twin.","tags":"","title":"Digital Actions"},{"content":"The WLDT library provides a native method to define Configurable Physical ad Digital Adapters specifying a custom configuration class passed as parameter in the constructor.\nStarting with the Physical Adapter created in the previous example DemoPhysicalAdapter instead of extending the base class PhysicalAdapter we can extend now ConfigurablePhysicalAdapter\u0026lt;C\u0026gt; where C is the name of the that we would like to use as configuration.\nIn our example we can create a simple configuration class called DemoPhysicalAdapterConfiguration where we move the constant variable used to implement the behaviour of our demo physical adapter. The resulting class will be the following:\npublic class DemoPhysicalAdapterConfiguration { private int messageUpdateTime = GlobalKeywords.MESSAGE_UPDATE_TIME; private int messageUpdateNumber = GlobalKeywords.MESSAGE_UPDATE_NUMBER; private double temperatureMinValue = GlobalKeywords.TEMPERATURE_MIN_VALUE; private double temperatureMaxValue = GlobalKeywords.TEMPERATURE_MAX_VALUE; public DemoPhysicalAdapterConfiguration() { } public DemoPhysicalAdapterConfiguration(int messageUpdateTime, int messageUpdateNumber, double temperatureMinValue, double temperatureMaxValue) { this.messageUpdateTime = messageUpdateTime; this.messageUpdateNumber = messageUpdateNumber; this.temperatureMinValue = temperatureMinValue; this.temperatureMaxValue = temperatureMaxValue; } public int getMessageUpdateTime() { return messageUpdateTime; } public void setMessageUpdateTime(int messageUpdateTime) { this.messageUpdateTime = messageUpdateTime; } public int getMessageUpdateNumber() { return messageUpdateNumber; } public void setMessageUpdateNumber(int messageUpdateNumber) { this.messageUpdateNumber = messageUpdateNumber; } public double getTemperatureMinValue() { return temperatureMinValue; } public void setTemperatureMinValue(double temperatureMinValue) { this.temperatureMinValue = temperatureMinValue; } public double getTemperatureMaxValue() { return temperatureMaxValue; } public void setTemperatureMaxValue(double temperatureMaxValue) { this.temperatureMaxValue = temperatureMaxValue; } } Now we can create or update our Physical Adapter extending ConfigurablePhysicalAdapter\u0026lt;DemoPhysicalAdapterConfiguration\u0026gt; as illustrated in the following snippet:\npublic class DemoPhysicalAdapter extends ConfigurablePhysicalAdapter\u0026lt;DemoPhysicalAdapterConfiguration\u0026gt; { [...] } Extending this class also the constructor should be updated getting as a parameter the expected configuration instance. Our constructor will be the following:\npublic DemoConfPhysicalAdapter(String id, DemoPhysicalAdapterConfiguration configuration) { super(id, configuration); } After that change since we removed and moved the used constant values into the new Configuration class we have also to update the deviceEmulation() method having access to the configuration through the method getConfiguration() or this.getConfiguration() directly on the adapter.\nprivate Runnable deviceEmulation(){ return () -\u0026gt; { try { System.out.println(\u0026#34;[DemoPhysicalAdapter] -\u0026gt; Sleeping before Starting Physical Device Emulation ...\u0026#34;); //Sleep 5 seconds to emulate device startup Thread.sleep(10000); System.out.println(\u0026#34;[DemoPhysicalAdapter] -\u0026gt; Starting Physical Device Emulation ...\u0026#34;); //Create a new random object to emulate temperature variations Random r = new Random(); //Publish an initial Event for a normal condition publishPhysicalAssetEventWldtEvent(new PhysicalAssetEventWldtEvent\u0026lt;\u0026gt;(GlobalKeywords.OVERHEATING_EVENT_KEY, \u0026#34;normal\u0026#34;)); //Emulate the generation on \u0026#39;n\u0026#39; temperature measurements for(int i = 0; i \u0026lt; getConfiguration().getMessageUpdateNumber(); i++){ //Sleep to emulate sensor measurement Thread.sleep(getConfiguration().getMessageUpdateTime()); //Update the double randomTemperature = getConfiguration().getTemperatureMinValue() + (getConfiguration().getTemperatureMaxValue() - getConfiguration().getTemperatureMinValue()) * r.nextDouble(); //Create a new event to notify the variation of a Physical Property PhysicalAssetPropertyWldtEvent\u0026lt;Double\u0026gt; newPhysicalPropertyEvent = new PhysicalAssetPropertyWldtEvent\u0026lt;\u0026gt;(GlobalKeywords.TEMPERATURE_PROPERTY_KEY, randomTemperature); //Publish the WLDTEvent associated to the Physical Property Variation publishPhysicalAssetPropertyWldtEvent(newPhysicalPropertyEvent); } //Publish a demo Physical Event associated to a \u0026#39;critical\u0026#39; overheating condition publishPhysicalAssetEventWldtEvent(new PhysicalAssetEventWldtEvent\u0026lt;\u0026gt;(GlobalKeywords.OVERHEATING_EVENT_KEY, \u0026#34;critical\u0026#34;)); } catch (EventBusException | InterruptedException e) { e.printStackTrace(); } }; } A similar approach can be adopted also for the Digital Adapter with the small difference that the base class DigitalAdapter already allow the possibility to specify a configuration. For this reason in the previous example we extended DigitalAdapter\u0026lt;Void\u0026gt; avoiding to specifying a configuration.\nIn this updated version we can create a new DemoDigitalAdapterConfiguration class containing the parameter association to the emulation of the action and then update our adapter to support the new configuration. Our new configuration class will be:\npublic class DemoDigitalAdapterConfiguration { private int sleepTimeMs = GlobalKeywords.ACTION_SLEEP_TIME_MS; private int emulatedActionCount = GlobalKeywords.EMULATED_ACTION_COUNT; private double temperatureMinValue = GlobalKeywords.TEMPERATURE_MIN_VALUE; private double temperatureMaxValue = GlobalKeywords.TEMPERATURE_MAX_VALUE; public DemoDigitalAdapterConfiguration() { } public DemoDigitalAdapterConfiguration(int sleepTimeMs, int emulatedActionCount, double temperatureMinValue, double temperatureMaxValue) { this.sleepTimeMs = sleepTimeMs; this.emulatedActionCount = emulatedActionCount; this.temperatureMinValue = temperatureMinValue; this.temperatureMaxValue = temperatureMaxValue; } public int getSleepTimeMs() { return sleepTimeMs; } public void setSleepTimeMs(int sleepTimeMs) { this.sleepTimeMs = sleepTimeMs; } public int getEmulatedActionCount() { return emulatedActionCount; } public void setEmulatedActionCount(int emulatedActionCount) { this.emulatedActionCount = emulatedActionCount; } public double getTemperatureMinValue() { return temperatureMinValue; } public void setTemperatureMinValue(double temperatureMinValue) { this.temperatureMinValue = temperatureMinValue; } public double getTemperatureMaxValue() { return temperatureMaxValue; } public void setTemperatureMaxValue(double temperatureMaxValue) { this.temperatureMaxValue = temperatureMaxValue; } } After that we can update the declaration of our Digital Adapter and modify its constructor to accept the configuration. The resulting class will be:\npublic class DemoDigitalAdapter extends DigitalAdapter\u0026lt;DemoDigitalAdapterConfiguration\u0026gt; { public DemoDigitalAdapter(String id, DemoDigitalAdapterConfiguration configuration) { super(id, configuration); } [...] } Of course the possibility to have this configuration will allow us to improve the emulateIncomingDigitalAction method in the following way having access to the configuration through the method getConfiguration() or this.getConfiguration() directly on the adapter:\nprivate Runnable emulateIncomingDigitalAction(){ return () -\u0026gt; { try { System.out.println(\u0026#34;[DemoDigitalAdapter] -\u0026gt; Sleeping before Emulating Incoming Digital Action ...\u0026#34;); Thread.sleep(5000); Random random = new Random(); //Emulate the generation on \u0026#39;n\u0026#39; temperature measurements for(int i = 0; i \u0026lt; getConfiguration().getEmulatedActionCount(); i++){ //Sleep to emulate sensor measurement Thread.sleep(getConfiguration().getSleepTimeMs()); double randomTemperature = getConfiguration().getTemperatureMinValue() + (getConfiguration().getTemperatureMaxValue() - getConfiguration().getTemperatureMinValue()) * random.nextDouble(); publishDigitalActionWldtEvent(\u0026#34;set-temperature-action-key\u0026#34;, randomTemperature); } } catch (Exception e) { e.printStackTrace(); } }; } When we have updated both adapters making them configurable we can update our main function in the process that we have previouly device using the updated adapters and passing the configurations:\npublic class DemoDigitalTwin { public static void main(String[] args) { try{ WldtEngine digitalTwinEngine = new WldtEngine(new DemoShadowingFunction(\u0026#34;test-shadowing-function\u0026#34;), \u0026#34;test-digital-twin\u0026#34;); //Default Physical and Digital Adapter //digitalTwinEngine.addPhysicalAdapter(new DemoPhysicalAdapter(\u0026#34;test-physical-adapter\u0026#34;)); //digitalTwinEngine.addDigitalAdapter(new DemoDigitalAdapter(\u0026#34;test-digital-adapter\u0026#34;)); //Physical and Digital Adapters with Configuration digitalTwinEngine.addPhysicalAdapter(new DemoConfPhysicalAdapter(\u0026#34;test-physical-adapter\u0026#34;, new DemoPhysicalAdapterConfiguration())); digitalTwinEngine.addDigitalAdapter(new DemoConfDigitalAdapter(\u0026#34;test-digital-adapter\u0026#34;, new DemoDigitalAdapterConfiguration())); digitalTwinEngine.startLifeCycle(); }catch (Exception e){ e.printStackTrace(); } } } ","date":"February 9, 2024","id":9,"permalink":"/docs/guides/configurable-adapters/","summary":"The WLDT library provides a native method to define Configurable Physical ad Digital Adapters specifying a custom configuration class passed as parameter in the constructor.","tags":"","title":"Configurable Adapters"},{"content":"","date":"February 9, 2024","id":10,"permalink":"/docs/adapters/","summary":"","tags":"","title":"Adapters"},{"content":"The MqttPhysicalAdapter library provides a streamlined solution for efficiently managing physical assets through the MQTT protocol. It offers a range of features, including a versatile builder for effortless configuration of MQTT connections, dedicated classes for handling both incoming and outgoing topics, and a specialized adapter designed for seamless integration with diverse physical assets.\nKey Features:\nBuilder for MQTT Configuration: The library incorporates a flexible builder pattern, enabling users to effortlessly configure the essential parameters of the MQTT connection. This includes specifying the MQTT broker\u0026rsquo;s address, port, and other relevant details to establish a reliable and customizable communication link. Incoming and Outgoing Topic Handling: MqttPhysicalAdapter facilitates the handling of incoming and outgoing topics, crucial for communication between the physical assets and the MQTT broker. The library includes dedicated classes for defining and managing topics, allowing users to efficiently subscribe to incoming data and publish outgoing messages. Adapter for Physical Asset Integration: At the core of the library is a robust adapter designed specifically for integrating with various physical assets. This adapter streamlines the process of connecting and interacting with physical devices, ensuring a smooth and standardized approach to managing asset-related data. In the WLDT library, Physical Adapters has the responsibility to generate and publish the PhysicalAssetDescription (PAD) to describe the capabilities and the characteristics of our object allowing the Shadowing Function to decide how to digitalize its physical counterpart.\nIn the MqttPhysicalAdapter the generation of the PAD (Physical Asset Description) is automatically and internally executed by the adapter itself accordingly to the adapter configuration in terms of MQTT topics and their mapping with DT\u0026rsquo;s properties, events and actions.\nPrerequisites:\nExternal MQTT Broker: The MqttPhysicalAdapter library requires an external MQTT broker for optimal functionality. Users must have access to a reliable MQTT broker to which the adapter can subscribe. This external broker serves as the central communication hub, facilitating the exchange of messages between the adapter and the physical assets. A complete example is provided in the test folder with a complete DT Creation in the TestMain class together with MQTT IoT demo device and a test MQTT consumer.\nWLDT-Core Version Compatibility The correct mapping and compatibility between versions is reported in the following table\nmqtt-physical-adapter wldt-core 0.2.1 wldt-core 0.3.0 0.1.0 ✅ ❌ 0.1.1 ❌ ✅ Installation To use MqttPhysicalAdapter in your Java project, you can include it as a dependency using Maven or Gradle.\nMaven \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;io.github.wldt\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;mqtt-physical-adapter\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;0.1.1\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; Gradle implementation \u0026#39;io.github.wldt:mqtt-physical-adapter:0.1.1\u0026#39; Class Structure \u0026amp; Functionalities MqttPhysicalAdapterConfigurationBuilder \u0026amp; Main Methods The MqttPhysicalAdapterConfigurationBuilder is the class used to build the configuration used by the PhysicalAdater and described and implemented through the class MqttPhysicalAdapterConfiguration.\nIn order to create a configuration builder we can use the static method builder() on the MqttPhysicalAdapterConfiguration class:\nMqttPhysicalAdapterConfiguration.builder(); On the builder the available methods that can be used are:\naddPhysicalAssetPropertyAndTopic public \u0026lt;T\u0026gt; MqttPhysicalAdapterConfigurationBuilder addPhysicalAssetPropertyAndTopic(String propertyKey, T initialValue, String topic, Function\u0026lt;String, T\u0026gt; topicFunction) throws MqttPhysicalAdapterConfigurationException Adds a physical asset property and its corresponding MQTT topic to the configuration.\nType Parameter T: The type of the property. propertyKey: The key of the property. initialValue: The initial value of the property. topic: The MQTT topic associated with the property. topicFunction: A function to parse the MQTT topic payload into the property type. Returns: The updated MqttPhysicalAdapterConfigurationBuilder.\nThrows: MqttPhysicalAdapterConfigurationException - If there is a configuration error.\naddPhysicalAssetActionAndTopic public \u0026lt;T\u0026gt; MqttPhysicalAdapterConfigurationBuilder addPhysicalAssetActionAndTopic(String actionKey, String type, String contentType, String topic, Function\u0026lt;T, String\u0026gt; topicFunction) throws MqttPhysicalAdapterConfigurationException Adds a physical asset action and its corresponding MQTT topic to the configuration.\nType Parameter T: The type of the action payload. actionKey: The key of the action. type: The type of the action. contentType: The content type of the action. topic: The MQTT topic associated with the action. topicFunction: A function to convert the action payload into the MQTT topic payload. Returns: The updated MqttPhysicalAdapterConfigurationBuilder.\nThrows: MqttPhysicalAdapterConfigurationException - If there is a configuration error.\naddPhysicalAssetEventAndTopic public \u0026lt;T\u0026gt; MqttPhysicalAdapterConfigurationBuilder addPhysicalAssetEventAndTopic(String eventKey, String type, String topic, Function\u0026lt;String, T\u0026gt; topicFunction) throws MqttPhysicalAdapterConfigurationException Adds a physical asset event and its corresponding MQTT topic to the configuration.\nType Parameter T: The type of the event payload. eventKey: The key of the event. type: The type of the event. topic: The MQTT topic associated with the event. topicFunction: A function to parse the MQTT topic payload into the event payload type. Returns: The updated MqttPhysicalAdapterConfigurationBuilder.\nThrows: MqttPhysicalAdapterConfigurationException - If there is a configuration error.\naddIncomingTopic public MqttPhysicalAdapterConfigurationBuilder addIncomingTopic(DigitalTwinIncomingTopic topic, List\u0026lt;PhysicalAssetProperty\u0026lt;?\u0026gt;\u0026gt; properties, List\u0026lt;PhysicalAssetEvent\u0026gt; events) throws MqttPhysicalAdapterConfigurationException This method is used when multiple properties or events can be associated to a single MQTT topic on the physical device. It adds a DigitalTwinIncomingTopic describing the topic where the DT will receive the data along with its related lists of properties and events associated to that topic.\ntopic: The DigitalTwinIncomingTopic to be added. properties: The list of related physical asset properties. events: The list of related physical asset events. Returns: The updated MqttPhysicalAdapterConfigurationBuilder.\nThrows: MqttPhysicalAdapterConfigurationException - If there is a configuration error.\naddOutgoingTopic public MqttPhysicalAdapterConfigurationBuilder addOutgoingTopic(String actionKey, String type, String contentType, DigitalTwinOutgoingTopic topic) throws MqttPhysicalAdapterConfigurationException Adds a DigitalTwinOutgoingTopic to the configuration.\nactionKey: The key of the associated action. type: The type of the associated action. contentType: The content type of the associated action. topic: The DigitalTwinOutgoingTopic to be added. Returns: The updated MqttPhysicalAdapterConfigurationBuilder.\nThrows: MqttPhysicalAdapterConfigurationException - If there is a configuration error.\nAdditional Methods setConnectionTimeout(Integer connectionTimeout): Sets the connection timeout for the MQTT client. Returns the builder for method chaining. Throws MqttPhysicalAdapterConfigurationException if the provided timeout is invalid. setCleanSessionFlag(boolean cleanSession): Sets the clean session flag for the MQTT client. Returns the builder for method chaining. setAutomaticReconnectFlag(boolean automaticReconnect): Sets the automatic reconnect flag for the MQTT client. Returns the builder for method chaining. setMqttClientPersistence(MqttClientPersistence persistence): Sets the persistence for the MQTT client. Returns the builder for method chaining. Throws MqttPhysicalAdapterConfigurationException if the provided persistence is null. build(): Builds and returns the finalized MqttPhysicalAdapterConfiguration object. Throws MqttPhysicalAdapterConfigurationException if the configuration is incomplete or invalid. MqttPhysicalAdapter The MqttPhysicalAdapter class is the core component for interacting with physical assets. Instantiate it with a unique ID and the configuration. It extends the default WLDT Library PhysicalAdapter implementing all the functionalities to automatically interact with an MQTT physical device following the specifications and details provided in the MqttPhysicalAdapterConfiguration and built using MqttPhysicalAdapterConfigurationBuilder.\nAn example of its creation is:\nMqttPhysicalAdapter mqttPhysicalAdapter = new MqttPhysicalAdapter(\u0026#34;uniqueId\u0026#34;, configuration); Integrated Example This example demonstrates the integration of a MqttPhysicalAdapter within a Digital Twin setup, where multiple adapters (including a console adapter) are added to a Digital Twin, and the overall system is managed by a DigitalTwinEngine.\n// Create a Digital Twin with a default shadowing function DigitalTwin digitalTwin = new DigitalTwin(\u0026#34;mqtt-digital-twin\u0026#34;, new DefaultShadowingFunction()); // Create an instance of ConsoleDigitalAdapter ConsoleDigitalAdapter consoleDigitalAdapter = new ConsoleDigitalAdapter(); // Create an instance of MqttPhysicalAdapterConfiguration MqttPhysicalAdapterConfiguration config = MqttPhysicalAdapterConfiguration.builder(\u0026#34;127.0.0.1\u0026#34;, 1883) .addPhysicalAssetPropertyAndTopic(\u0026#34;intensity\u0026#34;, 0, \u0026#34;sensor/intensity\u0026#34;, Integer::parseInt) .addIncomingTopic(new DigitalTwinIncomingTopic(\u0026#34;sensor/state\u0026#34;, getSensorStateFunction()), createIncomingTopicRelatedPropertyList(), new ArrayList\u0026lt;\u0026gt;()) .addPhysicalAssetEventAndTopic(\u0026#34;overheating\u0026#34;, \u0026#34;text/plain\u0026#34;, \u0026#34;sensor/overheating\u0026#34;, Function.identity()) .addPhysicalAssetActionAndTopic(\u0026#34;switch-off\u0026#34;, \u0026#34;sensor.actuation\u0026#34;, \u0026#34;text/plain\u0026#34;, \u0026#34;sensor/actions/switch\u0026#34;, actionBody -\u0026gt; \u0026#34;switch\u0026#34; + actionBody) .build(); // Create an instance of the MQTT Physical Adapter using the defined configuration MqttPhysicalAdapter mqttPhysicalAdapter = new MqttPhysicalAdapter(\u0026#34;test-mqtt-pa\u0026#34;, config); // Add both Digital and Physical Adapters to the Digital Twin digitalTwin.addDigitalAdapter(consoleDigitalAdapter); digitalTwin.addPhysicalAdapter(mqttPhysicalAdapter); // Create the Digital Twin Engine DigitalTwinEngine digitalTwinEngine = new DigitalTwinEngine(); // Add the Digital Twin to the Engine digitalTwinEngine.addDigitalTwin(digitalTwin); // Start all the Digital Twins registered on the engine digitalTwinEngine.startAll(); In this example the createIncomingTopicRelatedPropertyList() used to map properties and events associated to a single topic is the following:\nprivate static List\u0026lt;PhysicalAssetProperty\u0026lt;?\u0026gt;\u0026gt; createIncomingTopicRelatedPropertyList(){ List\u0026lt;PhysicalAssetProperty\u0026lt;?\u0026gt;\u0026gt; properties = new ArrayList\u0026lt;\u0026gt;(); properties.add(new PhysicalAssetProperty\u0026lt;\u0026gt;(\u0026#34;temperature\u0026#34;, 0)); properties.add(new PhysicalAssetProperty\u0026lt;\u0026gt;(\u0026#34;humidity\u0026#34;, 0)); return properties; } This information are used by the adapter to build the PAD describe the capabilities and the characteristics of our object allowing the Shadowing Function to decide how to digitalize its physical counterpart.\n","date":"February 9, 2024","id":11,"permalink":"/docs/adapters/mqtt-physical-adapter/","summary":"The MqttPhysicalAdapter library provides a streamlined solution for efficiently managing physical assets through the MQTT protocol. It offers a range of features, including a versatile builder for effortless configuration of MQTT connections, dedicated classes for handling both incoming and outgoing topics, and a specialized adapter designed for seamless integration with diverse physical assets.","tags":"","title":"MQTT Physical Adapter"},{"content":"","date":"February 9, 2024","id":12,"permalink":"/docs/change-logs/","summary":"","tags":"","title":"Change Logs"},{"content":"The MqttDigitalAdapter,\nMqttDigitalAdapterConfiguration, and MqttDigitalAdapterConfigurationBuilder classes and guides you through using these classes to set up an MQTT Digital Adapter within WLDT.\nRequires an external MQTT broker to send messages.\nMain functionalities are:\nManages the interaction between the Digital Twin and external systems. Handles state updates, events, and property changes. Dynamic configuration of the MqttDigitalAdapter with broker details, topics, and other settings. Allows customization of data and payload management associated to MQTT topics for properties, events, and actions. Prerequisites:\nExternal MQTT Broker: The MqttDigitalAdapter library requires an external MQTT broker for optimal functionality and communication. Users must have access to a reliable MQTT broker to which the adapter can subscribe and publish. This external broker serves as the central communication hub, facilitating the exchange of messages between the adapter and digital applications A complete example is provided in the test folder with a complete DT Creation in the TestMain class together with MQTT IoT demo device and a test MQTT consumer.\nWLDT-Core Version Compatibility The correct mapping and compatibility between versions is reported in the following table\nmqtt-digital-adapter wldt-core 0.2.1 wldt-core 0.3.0 0.1.0 ✅ ❌ 0.1.1 ❌ ✅ Installation To use MqttDigitalAdapter in your Java project, you can include it as a dependency using Maven or Gradle.\nMaven \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;io.github.wldt\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;mqtt-digital-adapter\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;0.1.1\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; Gradle implementation \u0026#39;io.github.wldt:mqtt-digital-adapter:0.1.1\u0026#39; Class Structure \u0026amp; Functionalities MqttDigitalAdapterConfiguration MqttDigitalAdapterConfiguration is a crucial class in the Digital Twin library, allowing developers to configure the behavior of the MQTT Digital Adapter. It provides a flexible and customizable way to set up MQTT communication parameters, topics for properties, events, and actions.\nKey functionalities and exposed capabilities:\nBroker Configuration brokerAddress and brokerPort: Set the MQTT broker\u0026rsquo;s address and port. username and password: Set optional credentials for connecting to the broker. Client Configuration clientId: Unique identifier for the MQTT client. cleanSessionFlag: Flag indicating whether the client starts with a clean session. connectionTimeout: Maximum time to wait for the connection to the MQTT broker. MQTT Client Persistence persistence: Configurable persistence for the MQTT client\u0026rsquo;s data. Reconnect Configuration: automaticReconnectFlag: Flag enabling or disabling automatic reconnection. Topic Configuration: propertyUpdateTopics: Map of property update topics. eventNotificationTopics: Map of event notification topics. actionIncomingTopics: Map of incoming action topics. Builder Methods: builder: Static method to start building a new configuration. addPropertyTopic: Add a property topic with specified parameters. addEventNotificationTopic: Add an event notification topic. addActionTopic: Add an action topic. setConnectionTimeout: Set the connection timeout. setCleanSessionFlag: Set the clean session flag. setAutomaticReconnectFlag: Set the automatic reconnect flag. setMqttClientPersistence: Set the MQTT client persistence. build: Finalize the configuration and build the instance. MqttDigitalAdapterConfigurationBuilder The MqttDigitalAdapterConfigurationBuilder is a powerful tool designed to simplify the process of constructing configurations for the MQTT Digital Adapter in the Digital Twin library. It offers a fluent and intuitive interface, allowing developers to define various aspects of MQTT communication seamlessly.\nBuilder Instantiation The builder is instantiated by providing essential parameters like brokerAddress and brokerPort or including an optional clientId\nMqttDigitalAdapterConfigurationBuilder builder = MqttDigitalAdapterConfiguration.builder(\u0026#34;127.0.0.1\u0026#34;, 1883); Adding Property Topics Developers can add property topics with specific configurations, such as the key, topic, QoS level, and a function to convert property values to payload.\nbuilder.addPropertyTopic(\u0026#34;energy\u0026#34;, \u0026#34;dummy/properties/energy\u0026#34;, MqttQosLevel.MQTT_QOS_0, value -\u0026gt; String.valueOf(((Double)value).intValue())); Adding Event Notification Topics Event notification topics are easily added, including event keys, topics, QoS levels, and payload conversion functions.\nbuilder.addEventNotificationTopic(\u0026#34;overheating\u0026#34;, \u0026#34;dummy/events/overheating/notifications\u0026#34;, MqttQosLevel.MQTT_QOS_0, Object::toString); Adding Action Topics Developers can include action topics with key, topic, and a function to convert the payload to the desired action.\nbuilder.addActionTopic(\u0026#34;switch_off\u0026#34;, \u0026#34;app/actions/switch-off\u0026#34;, msg -\u0026gt; \u0026#34;OFF\u0026#34;); Connection Options Developers can set the connection timeout for the MQTT client.\nbuilder.setConnectionTimeout(30); The clean session flag can be configured based on the desired behavior.\nbuilder.setCleanSessionFlag(true); Developers can specify whether the MQTT client should automatically reconnect in case of a connection failure.\nbuilder.setAutomaticReconnectFlag(true); The builder allows setting a custom MQTT client persistence, such as an in-memory persistence or a file-based one.\nbuilder.setMqttClientPersistence(new MemoryPersistence()); Building Configuration The final configuration is built using the build method.\nMqttDigitalAdapterConfiguration configuration = builder.build(); MqttDigitalAdapter MqttDigitalAdapter extends DigitalAdapter and specializes in MQTT communication for Digital Twin instances. It handles the publication of state updates, events, and property changes over MQTT. This class facilitates seamless integration with MQTT-enabled systems.\nIt uses the information defined and provided in the `` to handle the communication both with the DT Core and external application interested to interact with the DT through the MQTT protocol.\nHere\u0026rsquo;s a basic example illustrating how to use MqttDigitalAdapter:\n// Create a Digital Twin instance DigitalTwin digitalTwin = new DigitalTwin(\u0026#34;my-digital-twin\u0026#34;, new DefaultShadowingFunction()); // Add a Physical Adapter to the DT [...] // Build the MQTT Digital Adapter Configuration MqttDigitalAdapterConfiguration configuration = MqttDigitalAdapterConfiguration.builder(\u0026#34;127.0.0.1\u0026#34;, 1883) .addPropertyTopic(\u0026#34;energy\u0026#34;, \u0026#34;dummy/properties/energy\u0026#34;, MqttQosLevel.MQTT_QOS_0, value -\u0026gt; String.valueOf(((Double)value).intValue())) .addActionTopic(\u0026#34;switch_off\u0026#34;, \u0026#34;app/actions/switch-off\u0026#34;, msg -\u0026gt; \u0026#34;OFF\u0026#34;) .build(); // Add the MQTT Digital Adapter to the Digital Twin digitalTwin.addDigitalAdapter(new MqttDigitalAdapter(\u0026#34;mqtt-da\u0026#34;, configuration)); // Create the Digital Twin Engine and start the simulation DigitalTwinEngine digitalTwinEngine = new DigitalTwinEngine(); digitalTwinEngine.addDigitalTwin(digitalTwin); digitalTwinEngine.startAll(); ","date":"February 9, 2024","id":13,"permalink":"/docs/adapters/mqtt-digital-adapter/","summary":"The MqttDigitalAdapter,\nMqttDigitalAdapterConfiguration, and MqttDigitalAdapterConfigurationBuilder classes and guides you through using these classes to set up an MQTT Digital Adapter within WLDT.","tags":"","title":"MQTT Digital Adapter"},{"content":"Digital Adapters The following methods have been discontinued and removed from the DigitalAdapter class: onStateChangePropertyCreated onStateChangePropertyUpdated onStateChangePropertyDeleted onStatePropertyUpdated onStatePropertyDeleted onStateChangeActionEnabled onStateChangeActionUpdated onStateChangeActionDisabled onStateChangeEventRegistered onStateChangeEventRegistrationUpdated onStateChangeEventUnregistered onStateChangeRelationshipInstanceDeleted onStateChangeRelationshipDeleted onStateChangeRelationshipInstanceCreated onStateChangeRelationshipCreated onDigitalTwinStateEventNotificationReceived The Signature of the following methods have been changed: onDigitalTwinSync(IDigitalTwinState currentDigitalTwinState) -\u0026gt; onDigitalTwinSync(DigitalTwinState currentDigitalTwinState) onDigitalTwinUnSync(IDigitalTwinState currentDigitalTwinState) -\u0026gt; onDigitalTwinUnSync(DigitalTwinState currentDigitalTwinState) New methods that have been added are: onStateUpdate(DigitalTwinState newDigitalTwinState, DigitalTwinState previousDigitalTwinState, ArrayList\u0026lt;DigitalTwinStateChange\u0026gt; digitalTwinStateChangeList) onEventNotificationReceived(DigitalTwinStateEventNotification\u0026lt;?\u0026gt; digitalTwinStateEventNotification) For additional details about Digital Adapters check Sub Section [[Change Log - v.0.3.0#Digital Adapter| Digital Adapters]] Shadowing Function ShadowingModelFunction is now ShadowingFunction this.digitalTwinState is not directly accessible anymore and it is wrapped through the DigitalTwinStateManager using the variable digitalTwinStateManager (see next descriptions and changes) The method addRelationshipInstance now take only one parameter that is the DigitalTwinStateRelationshipInstance The same change for example should be applied in the point of the code where the Shadowing Function receive a variation from the Physical world through a target adapter and the callback method onPhysicalAssetPropertyVariation(...) When the Shadowing Function has to compute the new DT State it can now work with the following method to handle DT State Transition: this.digitalTwinStateManager.startStateTransaction() DT State variation methods such as: digitalTwinStateManager.createProperty() digitalTwinStateManager.updateProperty() digitalTwinStateManager.updatePropertyValue() digitalTwinStateManager.deleteProperty() digitalTwinStateManager.enableAction() digitalTwinStateManager.updateAction() digitalTwinStateManager.disableAction() digitalTwinStateManager.registerEvent() digitalTwinStateManager.updateRegisteredEvent() digitalTwinStateManager.unRegisterEvent() digitalTwinStateManager.createRelationship() digitalTwinStateManager.addRelationshipInstance() digitalTwinStateManager.deleteRelationship() digitalTwinStateManager.deleteRelationshipInstance() At the end the transaction can be committed using the method: digitalTwinStateManager.commitStateTransaction() The method notifyDigitalTwinStateEvent is now available through the digitalTwinStateManager Additional Details associated to Shadowing Function Migration can be found in the dedicated section [[Change Log - v.0.3.0#Shadowing Function Changes | Shadowing Function Changes]] WLDT Engine \u0026amp; DT Creation WldtEngine is now DigitalTwin and model and structure a single Digital Twin and takes the following parameters: String digitalTwinId ShadowingFunction shadowingFunction The startLifeCycle has been removed from the DigitalTwin (previously WLDT Engine) and now DigitalTwinEngine should be used to start twins Once a new Digital Twin has been create it has to be added to the DigitalTwinEngine DigitalTwinEngine has dedicated method to start and stop twins such as: startAll() startDigitalTwin(\u0026lt;DIGITAL_TWIN_ID\u0026gt;); stopAll() digitalTwinEngine.stopDigitalTwin(\u0026lt;DIGITAL_TWIN_ID\u0026gt;); Digital Twin \u0026amp; Digital Twin Engine With the following code we now create a new Digital Twin Instance\n// Create the new Digital Twin with its Shadowing Function DigitalTwin digitalTwin = new DigitalTwin(digitalTwinId, new DemoShadowingFunction()); // Physical Adapter with Configuration digitalTwin.addPhysicalAdapter( new DemoPhysicalAdapter( String.format(\u0026#34;%s-%s\u0026#34;, digitalTwinId, \u0026#34;test-physical-adapter\u0026#34;), new DemoPhysicalAdapterConfiguration(), true)); // Digital Adapter with Configuration digitalTwin.addDigitalAdapter( new DemoDigitalAdapter( String.format(\u0026#34;%s-%s\u0026#34;, digitalTwinId, \u0026#34;test-digital-adapter\u0026#34;), new DemoDigitalAdapterConfiguration()) ); In the new version the DT cannot be directly run but it should be added to the DigitalTwinEngine in order to be executed through the WLDT Library\n// Create the Digital Twin Engine DigitalTwinEngine digitalTwinEngine = new DigitalTwinEngine(); // Add the Digital Twin to the Engine digitalTwinEngine.addDigitalTwin(digitalTwin); In order to start a DT from the Engine you can:\n// Directly start when you add it passing a second boolean value = true digitalTwinEngine.addDigitalTwin(digitalTwin. true); // Starting the single DT on the engine through its id digitalTwinEngine.startDigitalTwin(DIGITAL_TWIN_ID); // Start all the DTs registered on the engine digitalTwinEngine.startAll(); To stop a single twin or all the twin registered on the engine:\n// Stop a single DT on the engine through its id digitalTwinEngine.stopDigitalTwin(DIGITAL_TWIN_ID); // Stop all the DTs registered on the engine digitalTwinEngine.stopAll(); It is also possible to remove a DT from the Engine with a consequent stop if it is active and the deletion of its reference from the engine:\n// Remove a single DT on the engine through its id digitalTwinEngine.removeDigitalTwin(DIGITAL_TWIN_ID); // Remove all the DTs registered on the engine digitalTwinEngine.removeAll(); Digital Twin State DT State now has the reference timestamp representing the evaluation instant of the digital twin state, this timestamp is computed through the DigitalTwinStateManager and cannot manually set by the developer The information available on the DT State are: properties: List of Properties with their values (if available) actions: List of Actions that can be called on the DT events: List of Events that can be generated by the DT relationships: List of Relationships and their instances (if available) evaluationInstant: The timestamp representing the evaluation instant of the DT state Available main methods on that class instance are: Properties:\n- getProperty(String propertyKey): Retrieves if present the target DigitalTwinStateProperty by Key\n- containsProperty(String propertyKey): Checks if a target Property Key is already available in the current Digital Twin\u0026rsquo;s State\n- getPropertyList(): Loads the list of available Properties (described by the class DigitalTwinStateProperty) available on the Digital Twin\u0026rsquo;s State\n- createProperty(DigitalTwinStateProperty\u0026lt;?\u0026gt; dtStateProperty): Allows the creation of a new Property on the Digital Twin\u0026rsquo;s State through the class DigitalTwinStateProperty\n- readProperty(String propertyKey): Retrieves if present the target DigitalTwinStateProperty by Key\n- updateProperty(DigitalTwinStateProperty\u0026lt;?\u0026gt; dtStateProperty): Updates the target property using the DigitalTwinStateProperty and the associated Property Key field\n- deleteProperty(String propertyKey): Deletes the target property identified by the specified key Actions:\n- containsAction(String actionKey): Checks if a Digital Twin State Action with the specified key is correctly registered\n- getAction(String actionKey): Loads the target DigitalTwinStateAction by key\n- getActionList(): Gets the list of available Actions registered on the Digital Twin\u0026rsquo;s State\n- enableAction(DigitalTwinStateAction digitalTwinStateAction): Enables and registers the target Action described through an instance of the DigitalTwinStateAction class\n- updateAction(DigitalTwinStateAction digitalTwinStateAction): Update the already registered target Action described through an instance of the DigitalTwinStateAction class\n- disableAction(String actionKey): Disables and unregisters the target Action described through an instance of the DigitalTwinStateAction class Events:\n- containsEvent(String eventKey): Check if a Digital Twin State Event with the specified key is correctly registered\n- getEvent(String eventKey): Return the description of a registered Digital Twin State Event according to its Key\n- getEventList(): Return the list of existing and registered Digital Twin State Events\n- registerEvent(DigitalTwinStateEvent digitalTwinStateEvent): Register a new Digital Twin State Event\n- updateRegisteredEvent(DigitalTwinStateEvent digitalTwinStateEvent): Update the registration and signature of an existing Digital Twin State Event\n- unRegisterEvent(String eventKey): Un-register a Digital Twin State Event\n- notifyDigitalTwinStateEvent(DigitalTwinStateEventNotification\u0026lt;?\u0026gt; digitalTwinStateEventNotification): Method to notify the occurrence of the target Digital Twin State Event Relationships:\n- containsRelationship(String relationshipName): Checks if a Relationship Name is already available in the current Digital Twin\u0026rsquo;s State\n- createRelationship(DigitalTwinStateRelationship\u0026lt;?\u0026gt; relationship): Creates a new Relationships (described by the class DigitalTwinStateRelationship) in the Digital Twin\u0026rsquo;s State\n- addRelationshipInstance(String name, DigitalTwinStateRelationshipInstance\u0026lt;?\u0026gt; instance): Adds a new Relationship instance described through the class DigitalTwinStateRelationshipInstance and identified through its name\n- getRelationshipList(): Loads the list of existing relationships on the Digital Twin\u0026rsquo;s State through a list of DigitalTwinStateRelationship\n- getRelationship(String name): Gets a target Relationship identified through its name and described through the class DigitalTwinStateRelationship\n- deleteRelationship(String name): Deletes a target Relationship identified through its name\n- deleteRelationshipInstance(String relationshipName, String instanceKey): Deletes the target Relationship Instance using relationship name and instance Key Digital Twin State Manager The DigitalTwinStateManager is a Java class that serves as the default implementation of the IDigitalTwinStateManager interface within the White Label Digital Twin Java Framework (whitelabel-digitaltwin). This class allows developers to manage the state of a digital twin, including properties, actions, events, and relationships.\nFeatures State Management: Handles the creation, update, and deletion of properties, actions, events, and relationships associated with the digital twin state. Transaction Support: Allows developers to start, commit, or rollback transactions to manage changes to the digital twin state. Event Notification: Notifies listeners about updates to the digital twin state through the WLDT event bus. When the Shadowing Function has to compute the new DT State it can now work with the following method to handle DT State Transition: - Start the DT State Transaction: startStateTransaction() - DT State variation methods such as: - createProperty() - updateProperty()\n- updatePropertyValue() - deleteProperty() - enableAction() - updateAction() - disableAction() - registerEvent() - updateRegisteredEvent() - unRegisterEvent() - createRelationship() - addRelationshipInstance() - deleteRelationship() - deleteRelationshipInstance()\nAt the end the transaction can be committed using the method: commitStateTransaction()\nUsage To use the DigitalTwinStateManager within your digital twin implementation:\nInitialization: Create an instance of the DigitalTwinStateManager. DigitalTwinStateManager digitalTwinStateManager = new DigitalTwinStateManager(); State Transaction:\nStart a new transaction using startStateTransaction() to manage changes. Make changes to the digital twin state. Commit the transaction using commitStateTransaction() to apply the changes. digitalTwinStateManager.startStateTransaction(); // Make changes to properties, actions, events, or relationships // [...] digitalTwinStateManager.commitStateTransaction(); Event Notification:\nDT State Updates after a commit action are automatically notified to Digital Adapter by the Manager Once an event incoming from the physical or generated by the DT itself is handled by the Shadowing Function, the developer can use notifyDigitalTwinStateUpdate to notify Digital Adapter listening about events variations. // Notify a specific event notification digitalTwinStateManager.notifyDigitalTwinStateEvent(digitalTwinStateEventNotification); Property, Action, Event, Relationship Management:\nCreate, update, or delete properties, actions, events, or relationships as needed. // Begin Digital Twin State Transaction digitalTwinStateManager.startStateTransaction(); // Create a new property digitalTwinStateManager.createProperty(dtStateProperty); // Update an existing property digitalTwinStateManager.updateProperty(dtStateProperty); // Delete a property digitalTwinStateManager.deleteProperty(propertyKey);` // Commit DT State Update to apply all the changes and notify the Digital Adapters and other listeners about the variation digitalTwinStateManager.commitStateTransaction(); Exception Handling The class throws WldtDigitalTwinStateException to indicate errors related to digital twin state management. Proper exception handling is advised to manage potential errors during state transactions. Shadowing Function Changes Now that the DT State is managed through the DigitalTwinStateManager class all the changes and variation should be applied on the DT ShadowingFunction using the previously presented transaction management and the correct call of methods startStateTransaction() and commitStateTransaction().\nHere there is an example of the change with a simple and demo shadowing function on callback onDigitalTwinBound:\n@Override protected void onDigitalTwinBound(Map\u0026lt;String, PhysicalAssetDescription\u0026gt; adaptersPhysicalAssetDescriptionMap) { try{ // NEW -\u0026gt; Start DT State Change Transaction this.digitalTwinStateManager.startStateTransaction(); for(Map.Entry\u0026lt;String, PhysicalAssetDescription\u0026gt; entry : adaptersPhysicalAssetDescriptionMap.entrySet()){ String adapterId = entry.getKey(); PhysicalAssetDescription physicalAssetDescription = entry.getValue(); //In that simple case the Digital Twin shadow all the properties and actions available in the physical asset for(PhysicalAssetProperty\u0026lt;?\u0026gt; p : physicalAssetDescription.getProperties()) this.digitalTwinStateManager.createProperty(new DigitalTwinStateProperty\u0026lt;\u0026gt;(p.getKey(), p.getInitialValue())); for(PhysicalAssetAction a : physicalAssetDescription.getActions()) this.digitalTwinStateManager.enableAction(new DigitalTwinStateAction(a.getKey(), a.getType(), a.getContentType())); for(PhysicalAssetEvent e: physicalAssetDescription.getEvents()) this.digitalTwinStateManager.registerEvent(new DigitalTwinStateEvent(e.getKey(), physicalAssetEvent.getType())); } // NEW -\u0026gt; Commit DT State Change Transaction to apply the changes on the DT State and notify about the change this.digitalTwinStateManager.commitStateTransaction(); //Observer Target Physical Properties for(Map.Entry\u0026lt;String, PhysicalAssetDescription\u0026gt; entry : adaptersPhysicalAssetDescriptionMap.entrySet()){ [...] } //Observe all the target available Physical Asset Events for each Adapter for(Map.Entry\u0026lt;String, PhysicalAssetDescription\u0026gt; entry : adaptersPhysicalAssetDescriptionMap.entrySet()){ [...] } // Observer for Incoming Digital Actions observeDigitalActionEvents(); //Notify Shadowing Completed notifyShadowingSync(); }catch (Exception e){ e.printStackTrace(); } } The same change for example should be applied in the point of the code where the Shadowing Function receive a variation from the Physical world through a target adapter and the callback method onPhysicalAssetPropertyVariation(...)\n@Override protected void onPhysicalAssetPropertyVariation(PhysicalAssetPropertyWldtEvent\u0026lt;?\u0026gt; physicalPropertyEventMessage) { try { if(physicalPropertyEventMessage != null \u0026amp;\u0026amp; getPhysicalEventsFilter().contains(physicalPropertyEventMessage.getType())){ if(physicalPropertyEventMessage.getPhysicalPropertyId().equals(TestPhysicalAdapter.SWITCH_PROPERTY_KEY) \u0026amp;\u0026amp; physicalPropertyEventMessage.getBody() instanceof String){ [...] } else{ //Update Digital Twin State //NEW -\u0026gt; Start State Transaction this.digitalTwinStateManager.startStateTransaction(); // Update State Property Value this.digitalTwinStateManager.updateProperty( new DigitalTwinStateProperty\u0026lt;\u0026gt;( physicalPropertyEventMessage.getPhysicalPropertyId(), physicalPropertyEventMessage.getBody())); //NEW -\u0026gt; Commit State Transaction this.digitalTwinStateManager.commitStateTransaction(); } } else logger.error(\u0026#34;WRONG Physical Event Message Received !\u0026#34;); }catch (Exception e){ e.printStackTrace(); } } Digital Adapter The Digital Adapter base class has been significantly extended and improved with respect to the previous version. In this new Version notifications that are received by the Adapter from the the DT core belongs to the following categories:\nDigital Twin State Update through the method onStateUpdate(...) providing information about the new state of the Digital Twin, the previous state, and a list of changes that occurred between these two states. In the previous version each variation of a property, relationships, actions or events were notified. In the new version only a committed DT\u0026rsquo;State variation is notified to listeners. Event Notifications through the method onEventNotificationReceived(...) whenever there is a notification about an event related to the Digital Twin\u0026rsquo;s state coming from the physical world, generated by the twin and processed by the Shadowing Function. For example in the DT State we can have the declaration of the over-heating-alert structured and received in the DT State while the effective occurrence of the event and the associated notification is notified through this dedicated callback The onStateUpdate method is an abstract method that must be implemented by any class extending the DigitalAdapter class. This method is called whenever there is an update to the Digital Twin\u0026rsquo;s state. It provides information about the new state of the Digital Twin, the previous state, and a list of changes that occurred between these two states.\nHere is an explanation of the parameters:\nnewDigitalTwinState: This parameter represents the updated state of the Digital Twin. It is an instance of the DigitalTwinState class, which encapsulates the current state information.\npreviousDigitalTwinState: This parameter represents the state of the Digital Twin before the update. It is also an instance of the DigitalTwinState class.\ndigitalTwinStateChangeList: This parameter is an ArrayList containing DigitalTwinStateChange objects. Each DigitalTwinStateChange object encapsulates information about a specific change that occurred between the previous and new states. It includes details such as the property or aspect of the state that changed, the previous value, and the new value.\nThe DT State is automatically monitored by each Digital Adapter while for the Events potentially generated by the DT can be observed by each adapter using:\nobserveAllDigitalTwinEventsNotifications: Enable the observation of available Digital Twin State Events Notifications. unObserveAllDigitalTwinEventsNotifications: Cancel the observation of Digital Twin State Events Notifications observeDigitalTwinEventsNotifications: Enable the observation of the notification associated to a specific list of Digital Twin State events. With respect to event a notification contains the new associated value unObserveDigitalTwinEventsNotifications: Cancel the observation of a target list of properties observeDigitalTwinEventNotification: Enable the observation of the notification associated to a single Digital Twin State event. With respect to event a notification contains the new associated value unObserveDigitalTwinEventNotification: Cancel the observation of a single target event DigitalTwinStateChange Class DigitalTwinStateChange Class\nThe DigitalTwinStateChange class is a representation of a change that occurred in the state of a Digital Twin. It encapsulates information about the type of operation, the resource type, and the affected resource within the Digital Twin.\nEnums:\nOperation: Enumerates different types of operations that can be performed on a Digital Twin state. The possible operations are: OPERATION_UPDATE: Represents an update operation on a resource. OPERATION_UPDATE_VALUE: Represents an update operation specifically on the value of a resource. OPERATION_ADD: Represents an addition operation of a new resource. OPERATION_REMOVE: Represents a removal operation of an existing resource. ResourceType: Enumerates different types of resources within a Digital Twin. The possible resource types are: PROPERTY: Represents a property of the Digital Twin. PROPERTY_VALUE: Represents the value of a property within the Digital Twin. EVENT: Represents an event associated with the Digital Twin. ACTION: Represents an action that can be performed on the Digital Twin. RELATIONSHIP: Represents a relationship between different components of the Digital Twin. RELATIONSHIP_INSTANCE: Represents an instance of a relationship. Fields:\noperation: Indicates the type of operation performed on the Digital Twin state (e.g., update, add, remove). resourceType: Represents the type of resource affected by the change (e.g., property, event, relationship). resource: The specific resource that has undergone the change, represented by an instance of the DigitalTwinStateResource class. Available type of DigitalTwinStateResource are:\nDigitalTwinStateProperty\u0026lt;T\u0026gt;: This class define a generic property associated to the Digital Twin State. Each property is associated to a Key and a Value. Furthermore, it can also be associated to a type to identify its nature and data structure. By default, it is associated to the type of the Class (e.g., java.lang.String) but it can be directly changed by the developer to associate it to a specific ontology or data type. DigitalTwinStateEvent: This class define a generic event associated to the Digital Twin State. Events enable a mechanism for asynchronous messages to be sent by the digital twin (e.g., an overheating) . They are different from Properties that can change values according to the type of Digital Twin and may be associated also to telemetry patterns. Each event is associated to a Key and a Type used to identify its nature and data structure. By default, it is associated to the type of the Class (e.g., java.lang.String) but it can be directly changed by the developer to associate it to a specific ontology or data type. DigitalTwinStateAction: This class define a generic action associated to the Digital Twin State. Each action is by a key, an action type and a content type used to identify the expected input required by the action. The type of the can be directly changed by the developer to associate it to a specific ontology or data type. DigitalTwinStateRelationship\u0026lt;T\u0026gt;: Structures and describes a Relationship in the Digital Twins\u0026rsquo;s State. This is just the description of the relationships while the effective values/instances are described through the other class DigitalTwinStateRelationshipInstance DigitalTwinStateRelationshipInstance\u0026lt;T\u0026gt;: Structures and describes a Relationship Instance in the Digital Twins\u0026rsquo;s State. This is effective description of a relationship while its generic declaration is described through the class DigitalTwinStateRelationship. When there is a change in the DT State it is possibile to cast the received resource variation to the correct one. For example in the following code we detect and manage the variation on a Property Value:\n// Get information from the state change DigitalTwinStateChange.Operation operation = stateChange.getOperation(); DigitalTwinStateChange.ResourceType resourceType = stateChange.getResourceType(); DigitalTwinStateResource resource = stateChange.getResource(); // Search for property value variation if(resourceType.equals(DigitalTwinStateChange.ResourceType.PROPERTY_VALUE) \u0026amp;\u0026amp; operation.equals(DigitalTwinStateChange.Operation.OPERATION_UPDATE) \u0026amp;\u0026amp; resource instanceof DigitalTwinStateProperty){ DigitalTwinStateProperty\u0026lt;?\u0026gt; digitalTwinStateProperty = (DigitalTwinStateProperty\u0026lt;?\u0026gt;) resource; if(getConfiguration().getPropertyUpdateTopics().containsKey(digitalTwinStateProperty.getKey())){ //Handle property value variation } } Constructors:\nDigitalTwinStateChange(): An empty constructor that allows creating an instance of the class. DigitalTwinStateChange(Operation operation, ResourceType resourceType, DigitalTwinStateResource resource): Constructs a DigitalTwinStateChange object with specified operation, resource type, and resource. Throws a WldtDigitalTwinStateException if any of the parameters is missing or null. Methods:\nAccessor methods (getOperation(), getResourceType(), getResource()) to retrieve the values of the fields. Mutator methods (setOperation(), setResourceType(), setResource()) to update the values of the fields. Usage Examples Developers extending the DigitalAdapter class should implement the onStateUpdate method to define custom logic that needs to be executed whenever the state of the Digital Twin is updated. This could include tasks such as processing state changes, updating internal variables, triggering specific actions, or notifying other components about the state update.\nHere\u0026rsquo;s an example of how the method might be implemented in a concrete subclass of DigitalAdapter:\n@Override protected void onStateUpdate(DigitalTwinState newDigitalTwinState, DigitalTwinState previousDigitalTwinState, ArrayList\u0026lt;DigitalTwinStateChange\u0026gt; digitalTwinStateChangeList) { // In newDigitalTwinState we have the new DT State System.out.println(\u0026#34;New DT State is: \u0026#34; + newDigitalTwinState); // The previous DT State is available through the variable previousDigitalTwinState System.out.println(\u0026#34;Previous DT State is: \u0026#34; + previousDigitalTwinState); // We can also check each DT\u0026#39;s state change potentially differentiating the behaviour for each change if (digitalTwinStateChangeList != null \u0026amp;\u0026amp; !digitalTwinStateChangeList.isEmpty()) { // Iterate through each state change in the list for (DigitalTwinStateChange stateChange : digitalTwinStateChangeList) { // Get information from the state change DigitalTwinStateChange.Operation operation = stateChange.getOperation(); DigitalTwinStateChange.ResourceType resourceType = stateChange.getResourceType(); DigitalTwinStateResource resource = stateChange.getResource(); // Perform different actions based on the type of operation switch (operation) { case OPERATION_UPDATE: // Handle an update operation System.out.println(\u0026#34;Update operation on \u0026#34; + resourceType + \u0026#34;: \u0026#34; + resource); break; case OPERATION_UPDATE_VALUE: // Handle an update value operation System.out.println(\u0026#34;Update value operation on \u0026#34; + resourceType + \u0026#34;: \u0026#34; + resource); break; case OPERATION_ADD: // Handle an add operation System.out.println(\u0026#34;Add operation on \u0026#34; + resourceType + \u0026#34;: \u0026#34; + resource); break; case OPERATION_REMOVE: // Handle a remove operation System.out.println(\u0026#34;Remove operation on \u0026#34; + resourceType + \u0026#34;: \u0026#34; + resource); break; default: // Handle unknown operation (optional) System.out.println(\u0026#34;Unknown operation on \u0026#34; + resourceType + \u0026#34;: \u0026#34; + resource); break; } } } else { // No state changes System.out.println(\u0026#34;No state changes detected.\u0026#34;); } } In this example, the method iterates over the list of state changes, extracts information about each change, and performs custom actions based on the changes. Developers can adapt this method to suit the specific requirements of their Digital Twin application.\n","date":"February 9, 2024","id":14,"permalink":"/docs/change-logs/change-log-0.3.0/","summary":"Digital Adapters The following methods have been discontinued and removed from the DigitalAdapter class: onStateChangePropertyCreated onStateChangePropertyUpdated onStateChangePropertyDeleted onStatePropertyUpdated onStatePropertyDeleted onStateChangeActionEnabled onStateChangeActionUpdated onStateChangeActionDisabled onStateChangeEventRegistered onStateChangeEventRegistrationUpdated onStateChangeEventUnregistered onStateChangeRelationshipInstanceDeleted onStateChangeRelationshipDeleted onStateChangeRelationshipInstanceCreated onStateChangeRelationshipCreated onDigitalTwinStateEventNotificationReceived The Signature of the following methods have been changed: onDigitalTwinSync(IDigitalTwinState currentDigitalTwinState) -\u0026gt; onDigitalTwinSync(DigitalTwinState currentDigitalTwinState) onDigitalTwinUnSync(IDigitalTwinState currentDigitalTwinState) -\u0026gt; onDigitalTwinUnSync(DigitalTwinState currentDigitalTwinState) New methods that have been added are: onStateUpdate(DigitalTwinState newDigitalTwinState, DigitalTwinState previousDigitalTwinState, ArrayList\u0026lt;DigitalTwinStateChange\u0026gt; digitalTwinStateChangeList) onEventNotificationReceived(DigitalTwinStateEventNotification\u0026lt;?","tags":"","title":"Change Log 0.3.0"},{"content":"The HttpDigitalAdapter is a powerful component designed to facilitate the integration of Digital Twins into HTTP-based systems. It serves as a bridge between a Digital Twin and HTTP-based applications, allowing developers to easily expose and interact with Digital Twin data and functionalities over HTTP.\nKey Features:\nHTTP Integration: Seamlessly integrates Digital Twins into HTTP environments, enabling communication with web applications and services. Dynamic Configuration: Offers a flexible configuration mechanism through the HttpDigitalAdapterConfiguration, allowing developers to customize the adapter\u0026rsquo;s behavior based on specific requirements. State Monitoring: Monitors changes in the Digital Twin state and provides HTTP endpoints to query the state of the Digital Twin (properties, events, actions and relationships). Event Notifications: Allows developers to retrieve event notifications triggered by changes in the Digital Twin state. A complete example is provided in the test folder with a complete DT Creation in the TestMain class together with a demo DT with and emulated Physical Adapter and the HTTP Digital Adapter.\nWLDT-Core Version Compatibility The correct mapping and compatibility between versions is reported in the following table\nhttp-digital-adapter wldt-core 0.2.1 wldt-core 0.3.0 0.1.1 ❌ ✅ Installation To use HttpDigitalAdapter in your Java project, you can include it as a dependency using Maven or Gradle.\nMaven \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;io.github.wldt\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;http-digital-adapter\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;0.1.1\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; Gradle implementation \u0026#39;io.github.wldt:http-digital-adapter:0.1.1\u0026#39; Class Structure \u0026amp; Functionalities HttpDigitalAdapterConfiguration The HttpDigitalAdapterConfiguration is a crucial part of the HttpDigitalAdapter, providing the necessary settings to tailor the adapter\u0026rsquo;s behavior to meet specific needs.\nRepresents the configuration for an HTTP Digital Adapter, specifying the host, port, and filters for properties, actions, events, and relationships.\nThe filters are used to selectively include or exclude specific properties, actions, events, and relationships when interacting with the HTTP Digital Adapter. Filters are meant to be white list filters, if they are empty, it means that ALL fields are considered\nThis class provides methods to add filters for each type and getters to retrieve the configured values.\nKey functionalities and exposed capabilities:\nBasic Configuration Adapter ID: A unique identifier for the HttpDigitalAdapter instance. Host: The hostname or IP address on which the adapter will listen for incoming HTTP requests. Port: The port number on which the adapter will listen for incoming HTTP requests. Filters (Optional) addPropertyFilter(String propertyKey): Adds a single property key to the property filter. addPropertiesFilter(Collection\u0026lt;String\u0026gt; propertiesKey): Adds a collection of property keys to the property filter. addActionFilter(String actionKey): Adds a single action key to the action filter. addActionsFilter(Collection\u0026lt;String\u0026gt; actionsKey): Adds a collection of action keys to the action filter. addEventFilter(String eventKey): Adds a single event key to the event filter. addEventsFilter(Collection\u0026lt;String\u0026gt; eventsKey): Adds a collection of event keys to the event filter. addRelationshipFilter(String relationshipName): Adds a single relationship name to the relationship filter. addRelationshipsFilter(Collection\u0026lt;String\u0026gt; relationshipNames): Adds a collection of relationship names to the relationship filter. Configured Filter can be accessed using: getPropertyFilter() getActionFilter() getEventFilter() getRelationshipFilter() A basic example without any filter that accesses and uses the entire DT State is:\nHttpDigitalAdapterConfiguration config = new HttpDigitalAdapterConfiguration(\u0026#34;my-http-adapter\u0026#34;, \u0026#34;localhost\u0026#34;, 8080); An example of using filter to select specific field of interest can be structured ad follows:\nHttpDigitalAdapterConfiguration config = new HttpDigitalAdapterConfiguration(\u0026#34;my-http-adapter\u0026#34;, \u0026#34;localhost\u0026#34;, 8080); // Add property filter config.addPropertyFilter(\u0026#34;temperature\u0026#34;); config.addPropertiesFilter(Arrays.asList(\u0026#34;humidity\u0026#34;, \u0026#34;pressure\u0026#34;)); // Add action filter config.addActionFilter(\u0026#34;start\u0026#34;); config.addActionsFilter(Arrays.asList(\u0026#34;stop\u0026#34;, \u0026#34;reset\u0026#34;)); // Add event filter config.addEventFilter(\u0026#34;temperatureChange\u0026#34;); config.addEventsFilter(Collections.singletonList(\u0026#34;pressureChange\u0026#34;)); // Add relationship filter config.addRelationshipFilter(\u0026#34;connectedTo\u0026#34;); config.addRelationshipsFilter(Arrays.asList(\u0026#34;parentOf\u0026#34;, \u0026#34;siblingOf\u0026#34;)); // Retrieve and display filters List\u0026lt;String\u0026gt; propertyFilter = config.getPropertyFilter(); List\u0026lt;String\u0026gt; actionFilter = config.getActionFilter(); List\u0026lt;String\u0026gt; eventFilter = config.getEventFilter(); List\u0026lt;String\u0026gt; relationshipFilter = config.getRelationshipFilter(); System.out.println(\u0026#34;Property Filter: \u0026#34; + propertyFilter); System.out.println(\u0026#34;Action Filter: \u0026#34; + actionFilter); System.out.println(\u0026#34;Event Filter: \u0026#34; + eventFilter); System.out.println(\u0026#34;Relationship Filter: \u0026#34; + relationshipFilter); HttpDigitalAdapter The HttpDigitalAdapter itself is the core component responsible for handling HTTP requests and interacting with the underlying Digital Twin. It extends the capabilities of the base DigitalAdapter to specifically cater to HTTP-based scenarios.\nKey Functionalities:\nRESTful Endpoints: Provides RESTful endpoints for reading properties, invoking actions, querying events, and managing relationships. State Updates: Automatically reflects changes in the Digital Twin state to the HTTP endpoints, ensuring real-time information. Event Handling: Listens for Digital Twin events and provides events notifications to HTTP clients. Here\u0026rsquo;s a basic example illustrating how to use MqttDigitalAdapter:\nGetting Started: Create HttpDigitalAdapterConfiguration:\nHttpDigitalAdapterConfiguration config = new HttpDigitalAdapterConfiguration(\u0026#34;my-http-adapter\u0026#34;, \u0026#34;localhost\u0026#34;, 8080); Instantiate HttpDigitalAdapter:\nDigitalTwin digitalTwin = new DigitalTwin(\u0026#34;my-digital-twin\u0026#34;, new DefaultShadowingFunction()); HttpDigitalAdapter httpDigitalAdapter = new HttpDigitalAdapter(config, digitalTwin); // Add a Physical Adapter to the DT [...] Add HttpDigitalAdapter to DigitalTwin:\ndigitalTwin.addDigitalAdapter(httpDigitalAdapter); Start the Digital Twin Engine:\nDigitalTwinEngine digitalTwinEngine = new DigitalTwinEngine(); digitalTwinEngine.addDigitalTwin(digitalTwin); digitalTwinEngine.startAll(); HTTP RESTful API This section of the documentation provides detailed information about the RESTful API exposed by the WLDT - HTTP Digital Adapter. The API allows you to interact with the Digital Twin (DT) instance, retrieve its state, read properties, actions, event and relationships description, and trigger actions.\nAvailable endpoints with the associated methods are:\nGET /instance: Retrieves information about the Digital Twin instance. GET /state: Retrieves the current state of the Digital Twin. GET /state/changes: Retrieves the list of state changes in the Digital Twin. GET /state/previous: Retrieves the previous state of the Digital Twin. GET /state/properties: Retrieves the list of properties in the Digital Twin state. GET /properties/{propertyKey}: Retrieves the value of a specific property (e.g., /properties/color) from the Digital Twin state. GET /state/events: Retrieves the list of events in the Digital Twin state. GET /state/actions: Retrieves the list of actions in the Digital Twin state. POST /state/actions/{actionKey}: Triggers the specified action (e.g., /state/actions/switch_on) in the Digital Twin state. The raw body contains the action request payload. GET /state/relationships: Retrieves the list of relationships in the Digital Twin state. GET /state/relationships/{relationshipName}/instances: Retrieves the instances of the specified relationship (e.g., /state/relationships/insideIn/instances) in the Digital Twin state. Note: Replace {propertyKey}, {actionKey}, and {relationshipName} with the actual values you want to retrieve or trigger. Make sure to use the appropriate HTTP method (GET, POST) and include any required parameters or payload as described in each endpoint\u0026rsquo;s description. For more detailed information, refer to the Postman Collection for this API available in the folder api: http_adapter_api_postman.json\n","date":"February 9, 2024","id":15,"permalink":"/docs/adapters/http-digital-adapter/","summary":"The HttpDigitalAdapter is a powerful component designed to facilitate the integration of Digital Twins into HTTP-based systems. It serves as a bridge between a Digital Twin and HTTP-based applications, allowing developers to easily expose and interact with Digital Twin data and functionalities over HTTP.","tags":"","title":"HTTP Digital Adapter"},{"content":"The WLDT framework intends to maximize modularity, re-usability and flexibility in order to effectively mirror physical smart objects in their digital counterparts. The proposed library focuses on the simplification of twins design and development aiming to provide a set of core features and functionalities for the widespread adoption of Internet of Things DTs applications.\nA WLDT instance is a general purpose software entity implementing all the features and functionalities of a Digital Twin running in cloud or on the edge. It has the peculiar characteristic to be generic and ``attachable\u0026rsquo;\u0026rsquo; to any physical thing in order to impersonate and maintain its digital replica and extend the provided functionalities for example through the support of additional protocols or a specific translation or normalization for data and formats.\nHereafter, the requirements that led the design and development of the WLDT framework are:\ni) Simplicity - with WLDT developers must have the possibility to easily create a new instance by using existing modules or customizing the behavior according the need of their application scenario; ii) Extensibility - while WLDT must be as simple and light as possible, the API should be also easily extendible in order to let programmers to personalize the configuration and/or to add new features loading and executing multiple modules at the same times; iii) Portability \u0026amp; Micorservice Readiness - a digital twin implemented through WLDT must be able to run on any platform without changes and customization. Our goal is to have a simple and light core engine with a strategic set of IoT-oriented features allowing the developer to easily create DT applications modeled as independent software agents and packed as microservices. In the following Figure, the main components that make up the architecture of WLDT are represented, and thus through which the individual Digital Twin is implemented. Specifically, from the image it is possible to identify the three levels on which the architecture is developed: the one related to the core of the library, the one that models the DT, and finally, that of the adapters.\nEach of this core components has the following main characteristics:\nMetrics Manager: Provides the functionalities for managing and tracking various metrics within DT instances combining both internal and custom metrics through a flexible and extensible approach. Logger: Is designed to facilitate efficient and customizable logging within implemented and deployed DTs with configurable log levels and versatile output options. Utils \u0026amp; Commons: Hold a collection of utility classes and common functionalities that can be readily employed across DT implementations ranging from handling common data structures to providing helpful tools for string manipulation. Event Communication Bus: Represents the internal Event Bus, designed to support communication between the different components of the DT\u0026rsquo;s instance. It allows defining customized events to model both physical and digital input and outputs. Each WLDT\u0026rsquo;s component can publish on the shared Event Bus and define an Event Filter to specify which types of events it is interested in managing, associating a specific callback to each one to process the different messages. Digital Twin Engine: Defines the multi-thread engine of the library allowing the execution and monitoring of multiple DTs (and their core components) simultaneously. Therefore, it is also responsible for orchestrating the different internal modules of the architecture while keeping track of each one, and it can be considered the core of the platform itself allowing the execution and control of the deployed DTs. Currently, it supports the execution of twins on the same Java process, however the same engine abstraction might be used to extend the framework to support distributed execution for example through different processes or microservices. Digital Twin: Models a modular DT structure built through the combination of core functionalities together with physical and digital adapter capabilities. This Layer includes the Digital Twin State responsible to structure the state of the DT by defining the list of properties, events, and actions. The different instances included in the lists can correspond directly to elements of the physical asset or can derive from their combination, in any case, it is the Shadowing Function (SF) that defines the mapping, following the model defined by the designer. This component also exposes a set of methods to allow SF manipulation. Every time the Digital Twin State is modified, the latter generates the corresponding DT\u0026rsquo;s event to notify all the components about the variation. Shadowing Function: It is the library component responsible for defining the behavior of the Digital Twin by interacting with the Digital Twin State. Specifically, it implements the shadowing process that allows keeping the DT synchronized with its physical entity. This component is based on a specific implementation of a WLDT Worker called Model Engine, in order to be executed by the WLDT Engine. The Shadowing Model Function is the fundamental component that must be extended by the DT designer to concretize its model. The shadowing function observes the life cycle of the Digital Twin to be notified of the different state changes. For example, it is informed when the DT enters the Bound state, i.e. when its Physical Adapters have completed the binding procedure with the physical asset. This component also allows the designer to define the behavior of the DT in case a property is modified, an event is triggered, or an action is invoked. Physical Adapter: It defines the essential functionalities that the individual extensions, related to specific protocols, must implement. As provided by the DT definition, a DT can be equipped with multiple Physical Adapters in order to manage communication with the corresponding physical entity. Each will produce a Physical Asset Description (PAD), i.e., a description of the properties, events, actions, and relationships that the physical asset exposes through the specific protocol. The DT transitions from the Unbound to the Bound state when all its Physical Adapters have produced their respective PADs. The Shadowing Function, following the DT model, selects the components of the various PADs that it is interested in managing. Digital Adapter: It provides the set of callbacks that each specific implementation can use to be notified of changes in the DT state. Symmetrically to what happens with Physical Adapters, a Digital Twin can define multiple Digital Adapters to expose its state and functionality through different protocols. Therefore, to create a Digital Twin using WLDT, it is necessary to define and instantiate a DT with its Shadowing Function and at least one Physical Adapter and one Digital Adapter, in order to enable connection with the physical entity and allow the DT to be used by external applications. Once the 3 components are defined, it is possible to instantiate the WLDT Engine and, subsequently, start the lifecycle of the DT. In the following sections we will go through the fundamental steps to start working with the library and creating all the basic modules to design, develop and execute our first Java Digital Twin.\n","date":"February 9, 2024","id":16,"permalink":"/docs/introduction/library-structure-basic-concepts/","summary":"The WLDT framework intends to maximize modularity, re-usability and flexibility in order to effectively mirror physical smart objects in their digital counterparts.","tags":"","title":"Library Structure \u0026 Basic Concepts"},{"content":" With respect to the element present in the real world, it is defined as a Physical Asset (PA) with the intention of referring to any entity that has a manifestation or relevance in the physical world and a well-defined lifespan.\nThe previous Figure schematically illustrates the main component of an abstract Digital Twin and clarifies its responsibility to be a bridge between the cyber and the physical world. The blueprint components (then mapped into the WLDT Library) are:\nPhysical Interface The entity in charge of both the initial digitalization o shadowing process and the perpetual responsibility to keep the DT and PA in synch during its life cycle. It can execute multiple Physical Asset Adapters to interact with the PA and detect and digitalize the physical event coming from the physical entity according to its nature and the supported protocols and data formats (e.g., through HTTP and JSON). Digital Interface The component complementary to the Physical Interface and in charge of handling DT\u0026rsquo;s internal variations and events towards external digital entities and consumers. It executes multiple and reusable Digital Adapters in charge of handling digital interactions and events and responsible for making the DT interoperable with external applications. DT\u0026rsquo;s Model The module defining the DT\u0026rsquo;s behaviour and its augmented functionalities. It supports the execution of different configurable and reusable modules and functionalities handling both physical and digial events according to the implemented behaviour. Furthermore, the Model is the component responsible to handle and keep updated the Digital Twin State as described in the following sections. The Digital Twin Model(M) allows capturing and representing the PA at an appropriate level of abstraction, i.e., avoiding irrelevant aspects for its purpose and modeling only domain-level information rather than technological ones. Finally, the link between the physical and digital copy is defined as shadowing. Specifically, the term defines the process that enables continuous and (almost) real-time updating of the internal state of the DT in relation to changes that occur in the PA.\nEach DT is thus equipped with an internal model, which defines how the PA is represented in the digital level. The DT\u0026rsquo;s representation denoted as Digital Twin State supported and defined through M is defined in terms of:\nProperties: represent the observable attributes of the corresponding PA as labeled data whose values can dynamically change over time, in accordance with the evolution of the PA\u0026rsquo;s state. Events: represent the domain-level events that can be observed in the PA. Relationships: represent the links that exist between the modeled PA and other physical assets of the organizations through links to their corresponding Digital Twins. Like properties, relationships can be observed, dynamically created, and change over time, but unlike properties, they are not properly part of the PA\u0026rsquo;s state but of its operational context (e.g., a DT of a robot within a production line). Actions: represent the actions that can be invoked on the PA through interaction with the DT or directly on the DT if they are not directly available on the PA (the DT is augmenting the physical capabilities). Once the model M is defined, the dynamic state of the DT (SDT) can be defined by through the combination of its properties, events, relationships and actions associated to the DT timestamp that represents the current time of synchronization between the physical and digital counterparts.\nThe Shadowing Process The shadowing process (also known as replication of digitalization) allows to keep the Digital Twin State synchronized with that of the corresponding physical resource according to what is defined by the model M. Specifically, each relevant update of the PA state (SPA) is translated into a sequence of 3 main steps:\neach relevant change in physical asset state is modeled by a physical_event (e_pa); the event is propagated to the DT; given the new physical_event, the DT\u0026rsquo;s is updated through the application of a shadowing function, which depends on the model M The shadowing process allows also the DT to reflect and invoke possible actions of the PA. The DT receives an action request (denoted as digital_action) on its digital interface, applies the shadowing function to validate it and then propagates the request through its physical interface. An important aspect to emphasize is that the request for a digital_action does not directly change the state of the DT since any changes can only occur as a result of the shadowing function from the PA to the DT, as described earlier.\n","date":"February 9, 2024","id":17,"permalink":"/docs/introduction/dt-model/","summary":"With respect to the element present in the real world, it is defined as a Physical Asset (PA) with the intention of referring to any entity that has a manifestation or relevance in the physical world and a well-defined lifespan.","tags":"","title":"DT Model"},{"content":"\rThe modeling of the concept of DT includes also the definition and characterization of its life cycle. Based on the scientific literature, we model (and then map into the library) a life cycle with 5 states through which the DT goes from when it is executed to when it is stopped. The previous Figure shows a graphical representation of the life cycle with the following steps:\nOperating \u0026amp; Not Bound: this is the state in which the DT is located following the initialization phase, indicating that all internal modules of the DT are active but there is no association yet with the corresponding PA. Bound: this is the state in which the DT transitions following the correct execution of the binding procedure. The binding procedure allows to connect the two parts and enables bidirectional flow of events. Shadowed: this is the state reached by the DT when the shadowing process begins and its state is correctly synchronized with that of the PA. Out of Sync: this is the state that determines the presence of errors in the shadowing process. When in this state, the DT is not able to handle either state alignment events or those generated by the application layer. Done: this is the state that the DT reaches when the shadowing process is stopped, but the DT continues to be active to handle requests coming from external applications. From Unbound to Bound Taking into account the target reference Life Cycle the first point to address is how we can move from an UnBound state to a Bound condition with respect to the relationship with the Physical Layer.\nThe previous Figure illustrates a simple scenario where a Physical Asset uses two protocols (P1 and P2) to communicate and it is connected to the Digital Twin through a DT\u0026rsquo;s Physical Interface enabled with two dedicated Adapters for protocol P1 and P2. In order to move from the Unbound to Bound state the DT should be aware of the description of the target asset with respect to the two protocols. For example through P1 the asset exposes telemetry data (e.g., light bulb status and energy consumption) while on P2 allows incoming action requests (e.g., turn on/off the light). The Digital Twin can start the shadowing process only when it is bound and has a description of the properties and capabilities of the associated physical counterpart. The schematic procedure is illustrated in the following Figure:\nInvolved steps are:\nThe Adapter P1 communicates with the PA through Protocol 1 and provides a Physical Asset Description from its perspective The Adapter P2 communicates with the PA through Protocol 2 and provides a Physical Asset Description from its perspective Only when all Physical Adapters have been correctly bound (it may require time) to the Physical Asset and the associated Physical Asset Descriptions have been generated, the DT can move from UnBound to Bound Main core aspects associated to the concept of Physical Asset Description (PAD) are the following:\nIt is used to describe the list of properties, actions and relationships of a Physical Asset Each Physical Adapter generates a dedicated PAD associated to its perspective on the Physical Assets and its capabilities to read data and execute actions It is a responsibility of the DT to handle multiple descriptions in order to build the digital replica It will be used by the DT to handle the shadowing process and keep the digital replica synchronized with the physical counterpart From Bound to Shadowed Following the same approach described in the previous step we need to define a procedure to allow the DT to move from a Bound state to a Shadowed condition where the twin identified the interesting capabilities of the Physical Asset that has to be digitalized and according to the received Physical Asset Descriptions start the shadowing procedure to be synchronized with the physical world.\nAs schematically illustrated in the previous Figure, involved steps are:\nThe Model defines which properties should be monitored on the Physical Asset and start observing them through the target adapters Involved Physical Adapters communicate with the Physical Asset, receive data and generate Events (ePA) to notify about physical property changes Received ePA will be used by the Digital Twin Model in order to run the Shadowing function and compute the new DT State The DT can move from the Bound to Shadowed phase until it is able to maintain a proper synchronization with the physical asset over time through its shadowing process and the generation and maintenance of the DT\u0026rsquo;s State The Digital Twin State is structured and characterized by the following elements:\nA list of properties A list of actions A list of relationships Listed elements can be directly associated to the corresponding element of the Physical Asset or generated by DT Model combining multiple physical properties, actions or relationships at the same time. The Digital Twin State can be managed through the Shadowing Function and exposes a set of methods for its manipulated. When there is a change in the DT State an event (eDT) will be generated\nThe manipulation of DT\u0026rsquo;s State generates a set of DT\u0026rsquo;s events (eDT) associated to each specific variation and evolution of the twin during its life cyle. These events are used by the Digital Interface and in particular by its Digital Adapters to expose the DT\u0026rsquo;s State, its properties and capabilities to the external digital world. At the same time, eDT can be used by Digital Adapters to trigger action on the DT and consequently to propagate (if acceptable and/or needed) the incoming request to the physical assets bound with the target DT. Supported events are illustrated in the following schema.\n","date":"February 9, 2024","id":18,"permalink":"/docs/introduction/dt-life-cycle/","summary":"The modeling of the concept of DT includes also the definition and characterization of its life cycle. Based on the scientific literature, we model (and then map into the library) a life cycle with 5 states through which the DT goes from when it is executed to when it is stopped.","tags":"","title":"DT Life Cycle"},{"content":"","date":"September 7, 2023","id":19,"permalink":"/docs/","summary":"","tags":"","title":"Docs"},{"content":"Welcome to White Label Digital Twins (WLDT), an open-source project dedicated to supporting the design, development, and deployment of Digital Twins within the Internet of Things (IoT) ecosystems.\nThe WLDT library has been designed to align with the latest DT definitions from both Industrial and Scientific domains. It identifies DTs as active, flexible, and scalable software components. Our library aims to provide developers with the tools and resources necessary to create robust Digital Twins that effectively simulate and monitor physical assets within IoT environments.\nWhether you\u0026rsquo;re working on IoT, Industrial IoT (IIoT) applications, Smart Cities projects, or any other IoT-related endeavor, the WLDT library offers a versatile solution for implementing Digital Twins that accurately represent real-world objects and support informed decision-making processes.\n💻 Team \u0026amp; Mantainers [Founders \u0026amp; Main Contributors] Marco Picone - University of Modena \u0026amp; Reggio Emilia, Italy - (Link) [Key Contributors] Samuele Burattini - University of Bologna, Italy - (Link) [Additional Contributors] Marta Spadoni - University of Bologna, Italy - Master Thesis 2022 📜 Scientitic Citation \u0026amp; References If you use the WLDT Library in a Scientific Paper please use this reference:\n@article{PICONE2021100661, title = {WLDT: A general purpose library to build IoT digital twins}, journal = {SoftwareX}, volume = {13}, pages = {100661}, year = {2021}, issn = {2352-7110}, doi = {https://doi.org/10.1016/j.softx.2021.100661}, url = {https://www.sciencedirect.com/science/article/pii/S2352711021000066}, author = {Marco Picone and Marco Mamei and Franco Zambonelli}, keywords = {Internet of Things, Digital twin, Library, Software agent} } 📨 Community Join our community and contribute to the advancement of Digital Twin technology with White Label Digital Twins!\nWLDT questions, feedback and discussions are tracked using slack channels in the WLDT Slack Workspace.\nThe workspace is available here: WLDT Slack Workspace\nNew users first need to join the MEC Sandbox slack workspace by creating a new account using the invitation link provided here: Join the WLDT Slack Workspace\n🐛 Reporting Issues WLDT issues should be reported on Slack, where they can be discussed with the core team that maintains the WLDT Library.\n","date":"March 13, 2024","id":20,"permalink":"/about/","summary":"Welcome to White Label Digital Twins (WLDT), an open-source project dedicated to supporting the design, development, and deployment of Digital Twins within the Internet of Things (IoT) ecosystems.","tags":"","title":"About WLDT Library"},{"content":" What is a digital Twin? A Digital Twin (DT) is a comprehensive software representation of any individual Physical Asset (PA) in the real world.\nIt includes the properties, conditions, relationships, and behavior(s) of the real-life object through models and data.\nA Digital Twin is a set of realistic models that can digitalize an object’s behavior in the deployed environment and has the responsibility to represent and reflect its physical counterpart over time maintaining its digital replica across the object’s entire lifecycle.\nWhat can WLDT do for me? The White Label Digital Twin (WLDT) library aims to support the design, development, and deployment of Digital Twins within the Internet of Things (IoT) ecosystems.\nThe library has been designed following the latest DT definitions coming from both Industrial and Scientific domains and identifying DTs as active, flexible and scalable software components.\nScientitic Citation \u0026amp; Reference If you use the WLDT Library in a Scientific Paper refer to the About Page for additional information and scientific references. Thanks :)\n","date":"October 6, 2023","id":21,"permalink":"/","summary":"What is a digital Twin? A Digital Twin (DT) is a comprehensive software representation of any individual Physical Asset (PA) in the real world.","tags":"","title":"White Label Digital Twins"},{"content":"","date":"September 7, 2023","id":22,"permalink":"/privacy/","summary":"","tags":"","title":"Privacy Policy"},{"content":"","date":"January 1, 0001","id":23,"permalink":"/categories/","summary":"","tags":"","title":"Categories"},{"content":"","date":"January 1, 0001","id":24,"permalink":"/contributors/","summary":"","tags":"","title":"Contributors"},{"content":"","date":"January 1, 0001","id":25,"permalink":"/tags/","summary":"","tags":"","title":"Tags"}] \ No newline at end of file +[{"content":"We\u0026rsquo;re excited to announce the release of WLDT version 0.4.0! This update brings powerful new features to enhance your Digital Twin (DT) experience, including event observation capabilities, a robust storage layer, and a flexible query system.\nFor detailed information about these changes and their impact, please refer to the information provided:\nOfficial Changelog Official Documentation Key Highlights WldtEventObserver: A new class has been introduced, simplifying the observation of specific events generated by Digital Twins and their components Storage Layer: The new storage layer allows DTs to store data related to their state, events, actions, and more. It consists of: Storage Manager: Manage and use various storage systems (e.g., in-memory, file-based, DBMS) simultaneously. WldtStorage: An abstract class to implement custom storage systems. The default in-memory storage is available for development and testing. Query System: The query system enables external components like Digital Adapters to retrieve stored data efficiently, supporting both synchronous and asynchronous queries. WLDT 0.4.0 significantly enhances the flexibility and capabilities of Digital Twins, making it easier to manage and retrieve data, and observe events. We encourage developers to explore these new features and integrate them into their projects.\nStay tuned for more updates!\n","date":"August 29, 2024","id":0,"permalink":"/blog/wldt-library-version-0.4.0/","summary":"We\u0026rsquo;re excited to announce the release of WLDT version 0.4.0! This update brings powerful new features to enhance your Digital Twin (DT) experience, including event observation capabilities, a robust storage layer, and a flexible query system.","tags":"","title":"WLDT Library Version 0.4.0"},{"content":"📣 We\u0026rsquo;re thrilled to announce the release of version 0.3.0 of the White Label Digital Twins (WLDT) library! This release brings significant enhancements, improvements, and new features to further empower developers in designing, developing, and deploying Digital Twins within Internet of Things (IoT) ecosystems.\nFor detailed information about these changes and their impact, please refer to the information provided:\nOfficial Changelog Official Documentation Let\u0026rsquo;s dive into the key changes and updates included in this release:\nMigration Digital Adapters In version 0.3.0, we\u0026rsquo;ve made several enhancements and adjustments to the Digital Adapter class to improve its functionality and usability. Notable changes include:\nDiscontinued Methods: Several methods have been discontinued and removed from the DigitalAdapter class to streamline its interface and improve clarity. Method Signature Changes: The signatures of certain methods have been updated for consistency and clarity, ensuring a more intuitive developer experience. New Methods: We\u0026rsquo;ve introduced new methods to provide additional functionality and flexibility for handling state updates and event notifications. Migration Shadowing Function We\u0026rsquo;ve made significant improvements to the ShadowingModelFunction, which is now renamed to ShadowingFunction. Additionally, we\u0026rsquo;ve introduced changes to how the DigitalTwinState is managed within the Shadowing Function, providing developers with more control and flexibility.\nMigrating WLDT Engine \u0026amp; DT Creation In version 0.3.0, the WldtEngine class has been renamed to DigitalTwin, offering improved clarity and consistency. We\u0026rsquo;ve also made adjustments to the lifecycle management of Digital Twins, streamlining the process and enhancing usability.\nDigital Twin \u0026amp; Digital Twin Engine We\u0026rsquo;ve introduced enhancements to the Digital Twin and Digital Twin Engine classes, providing developers with improved functionality and ease of use. Notable updates include:\nSimplified Digital Twin Creation: Creating and managing Digital Twins is now more intuitive and streamlined. Lifecycle Management: We\u0026rsquo;ve enhanced the lifecycle management of Digital Twins, making it easier to start, stop, and manage multiple instances. Digital Twin State Manager The DigitalTwinStateManager class has been improved to provide better support for managing the state of Digital Twins. With features such as transaction support and event notification, developers can more effectively manage changes to Digital Twin states and respond to events.\nTo learn more about the capabilities of the DigitalTwinStateManager, please refer to the Digital Twin State Manager section.\nDigital Adapter We\u0026rsquo;ve extended and improved the Digital Adapter base class to provide enhanced support for handling Digital Twin state updates and event notifications. With the introduction of the onStateUpdate and onEventNotificationReceived methods, developers can more effectively respond to changes in Digital Twin states and events.\nGet Started with WLDT 0.3.0 To get started with version 0.3.0 of the WLDT library, simply update your dependencies to include the latest release. Detailed documentation and usage examples are available in the project repository, providing comprehensive guidance on leveraging the new features and enhancements.\nWe\u0026rsquo;re excited about the improvements and new capabilities introduced in WLDT 0.3.0, and we can\u0026rsquo;t wait to see how developers utilize them to create innovative IoT solutions powered by Digital Twins. As always, we welcome your feedback and contributions to help us further improve the library and empower the community.\nHappy coding with WLDT 0.3.0! 🚀\n","date":"March 13, 2024","id":1,"permalink":"/blog/wldt-library-version-0.3.0/","summary":"📣 We\u0026rsquo;re thrilled to announce the release of version 0.3.0 of the White Label Digital Twins (WLDT) library! This release brings significant enhancements, improvements, and new features to further empower developers in designing, developing, and deploying Digital Twins within Internet of Things (IoT) ecosystems.","tags":"","title":"WLDT Library Version 0.3.0"},{"content":"","date":"September 7, 2023","id":2,"permalink":"/blog/","summary":"","tags":"","title":"Blog"},{"content":"","date":"September 7, 2023","id":3,"permalink":"/docs/introduction/","summary":"","tags":"","title":"Introduction"},{"content":"","date":"February 9, 2024","id":4,"permalink":"/docs/guides/","summary":"","tags":"","title":"Getting Started"},{"content":"The developer can use an existing Physical Adapter or create a new one to handle the communication with a specific physical twin. In this documentation we focus on the creation of a new Physical Adapter in order to explain library core functionalities. However, existing Physical Adapters can be found on the official repository and linked in the core documentation and webpage (WLDT-GitHub).\nIn general WLDT Physical Adapter extends the class PhysicalAdapter and it is responsible to talk with the physical world and handling the following main tasks:\nGenerate a PAD describing the properties, events, actions and relationships available on the physical twin using the class PhysicalAssetDescription Generate Physical Event using the class PhysicalAssetEventWldtEvent associated to the variation of any aspect of the physical state (properties, events, and relationships) Handle action request coming from the Digital World through the DT Shadowing Function by implementing the method onIncomingPhysicalAction and processing events modeled through the class PhysicalAssetActionWldtEvent Create a new class called DemoPhysicalAdapter extending the library class PhysicalAdapter and implement the following methods:\nonAdapterStart: A callback method used to notify when the adapter has been effectively started withing the DT\u0026rsquo;s life cycle onAdapterStop: A call method invoked when the adapter has been stopped and will be dismissed by the core onIncomingPhysicalAction: The callback method called when a new PhysicalAssetActionWldtEvent is sent by the Shadowing Function upon the receiving of a valid Digital Action through a Digital Adapter Then you have to create a constructor for your Physical Adapter with a single String parameter representing the id of the adapter. This id will be used internally by the library to handle and coordinate multiple adapters, adapts logs and execute functions upon the arrival of a new event. The resulting empty class will the following:\npublic class DemoPhysicalAdapter extends PhysicalAdapter { public DemoPhysicalAdapter(String id) { super(id); } @Override public void onIncomingPhysicalAction(PhysicalAssetActionWldtEvent\u0026lt;?\u0026gt; physicalAssetActionWldtEvent) { } @Override public void onAdapterStart() { } @Override public void onAdapterStop() { } } In our test Physical Adapter example we are going to emulate the communication with an Internet of Things device with the following sensing and actuation characteristics:\nA Temperature Sensor generating data about new measurements The possibility to generate OVER-HEATING events An action to set the target desired temperature value The first step will be to generate and publish the PhysicalAssetDescription (PAD) to describe the capabilities and the characteristics of our object allowing the Shadowing Function to decide how to digitalize its physical counterpart. Of course in our case the PAD is generated manually but according to the nature of the connected physical twin it can be automatically generated starting from a discovery or a configuration passed to the adapter.\nThe generation of the PAD for each active Physical Adapter is the fundamental DT process to handle the binding procedure and to allow the Shadowing Function and consequently the core of the twin to be aware of what is available in the physical world and consequently decide what to observe and digitalize.\nIn order to publish the PAD we can update the onAdapterStart method with the following lines of code:\nprivate final static String TEMPERATURE_PROPERTY_KEY = \u0026#34;temperature-property-key\u0026#34;; private final static String OVERHEATING_EVENT_KEY = \u0026#34;overheating-event-key\u0026#34;; private final static String SET_TEMPERATURE_ACTION_KEY = \u0026#34;set-temperatura-action-key\u0026#34;; @Override public void onAdapterStart() { try { //Create an empty PAD PhysicalAssetDescription pad = new PhysicalAssetDescription(); //Add a new Property associated to the target PAD with a key and a default value PhysicalAssetProperty\u0026lt;Double\u0026gt; temperatureProperty = new PhysicalAssetProperty\u0026lt;Double\u0026gt;(TEMPERATURE_PROPERTY_KEY, 0.0); pad.getProperties().add(temperatureProperty); //Add the declaration of a new type of generated event associated to a event key // and the content type of the generated payload PhysicalAssetEvent overheatingEvent = new PhysicalAssetEvent(OVERHEATING_EVENT_KEY, \u0026#34;text/plain\u0026#34;); pad.getEvents().add(overheatingEvent); //Declare the availability of a target action characterized by a Key, an action type // and the expected content type and the request body PhysicalAssetAction setTemperatureAction = new PhysicalAssetAction(SET_TEMPERATURE_ACTION_KEY, \u0026#34;temperature.actuation\u0026#34;, \u0026#34;text/plain\u0026#34;); pad.getActions().add(setTemperatureAction); //Notify the new PAD to the DT\u0026#39;s Shadowing Function this.notifyPhysicalAdapterBound(pad); //TODO add here the Device Emulation method } catch (PhysicalAdapterException | EventBusException e) { e.printStackTrace(); } } Now we need a simple code to emulate the generation of new temperature measurements and over-heating events. In a real Physical Adapter implementation we have to implement the real communication with the physical twin in order to read its state variation over time according to the supported protocols. In our simplified Physical Adapter we can the following function:\nprivate final static int MESSAGE_UPDATE_TIME = 1000; private final static int MESSAGE_UPDATE_NUMBER = 10; private final static double TEMPERATURE_MIN_VALUE = 20; private final static double TEMPERATURE_MAX_VALUE = 30; private Runnable deviceEmulation(){ return () -\u0026gt; { try { //Sleep 5 seconds to emulate device startup Thread.sleep(5000); //Create a new random object to emulate temperature variations Random r = new Random(); //Publish an initial Event for a normal condition publishPhysicalAssetEventWldtEvent(new PhysicalAssetEventWldtEvent\u0026lt;\u0026gt;(OVERHEATING_EVENT_KEY, \u0026#34;normal\u0026#34;)); //Emulate the generation on \u0026#39;n\u0026#39; temperature measurements for(int i = 0; i \u0026lt; MESSAGE_UPDATE_NUMBER; i++){ //Sleep to emulate sensor measurement Thread.sleep(MESSAGE_UPDATE_TIME); //Update the double randomTemperature = TEMPERATURE_MIN_VALUE + (TEMPERATURE_MAX_VALUE - TEMPERATURE_MIN_VALUE) * r.nextDouble(); //Create a new event to notify the variation of a Physical Property PhysicalAssetPropertyWldtEvent\u0026lt;Double\u0026gt; newPhysicalPropertyEvent = new PhysicalAssetPropertyWldtEvent\u0026lt;\u0026gt;(TEMPERATURE_PROPERTY_KEY, randomTemperature); //Publish the WLDTEvent associated to the Physical Property Variation publishPhysicalAssetPropertyWldtEvent(newPhysicalPropertyEvent); } //Publish a demo Physical Event associated to a \u0026#39;critical\u0026#39; overheating condition publishPhysicalAssetEventWldtEvent(new PhysicalAssetEventWldtEvent\u0026lt;\u0026gt;(OVERHEATING_EVENT_KEY, \u0026#34;critical\u0026#34;)); } catch (EventBusException | InterruptedException e) { e.printStackTrace(); } }; } Now we have to call the deviceEmulationFunction() inside the onAdapterStart() triggering its execution and emulating the physical counterpart of our DT. To do that add the following line at the end of the onAdapterStart() method after the this.notifyPhysicalAdapterBound(pad);.\nThe last step will be to handle an incoming action trying to set a new temperature on the device by implementing the method onIncomingPhysicalAction(). This method will receive a PhysicalAssetActionWldtEvent\u0026lt;?\u0026gt; physicalAssetActionWldtEvent associated to the action request generated by the shadowing function. Since a Physical Adapter can handle multiple action we have to check both action-key and body type in order to properly process the action (in our case just logging the request). The new update method will result like this:\n@Override public void onIncomingPhysicalAction(PhysicalAssetActionWldtEvent\u0026lt;?\u0026gt; physicalAssetActionWldtEvent) { try{ if(physicalAssetActionWldtEvent != null \u0026amp;\u0026amp; physicalAssetActionWldtEvent.getActionKey().equals(SET_TEMPERATURE_ACTION_KEY) \u0026amp;\u0026amp; physicalAssetActionWldtEvent.getBody() instanceof String) { System.out.println(\u0026#34;Received Action Request: \u0026#34; + physicalAssetActionWldtEvent.getActionKey() + \u0026#34; with Body: \u0026#34; + physicalAssetActionWldtEvent.getBody()); } else System.err.println(\u0026#34;Wrong Action Received !\u0026#34;); }catch (Exception e){ e.printStackTrace(); } } The overall class will result as following:\nimport it.wldt.adapter.physical.*; import it.wldt.adapter.physical.event.PhysicalAssetActionWldtEvent; import it.wldt.adapter.physical.event.PhysicalAssetEventWldtEvent; import it.wldt.adapter.physical.event.PhysicalAssetPropertyWldtEvent; import it.wldt.exception.EventBusException; import it.wldt.exception.PhysicalAdapterException; import java.util.Random; public class DemoPhysicalAdapter extends PhysicalAdapter { private final static String TEMPERATURE_PROPERTY_KEY = \u0026#34;temperature-property-key\u0026#34;; private final static String OVERHEATING_EVENT_KEY = \u0026#34;overheating-event-key\u0026#34;; private final static String SET_TEMPERATURE_ACTION_KEY = \u0026#34;set-temperature-action-key\u0026#34;; private final static int MESSAGE_UPDATE_TIME = 1000; private final static int MESSAGE_UPDATE_NUMBER = 10; private final static double TEMPERATURE_MIN_VALUE = 20; private final static double TEMPERATURE_MAX_VALUE = 30; public DemoPhysicalAdapter(String id) { super(id); } @Override public void onIncomingPhysicalAction(PhysicalAssetActionWldtEvent\u0026lt;?\u0026gt; physicalAssetActionWldtEvent) { try{ if(physicalAssetActionWldtEvent != null \u0026amp;\u0026amp; physicalAssetActionWldtEvent.getActionKey().equals(SET_TEMPERATURE_ACTION_KEY) \u0026amp;\u0026amp; physicalAssetActionWldtEvent.getBody() instanceof Double) { System.out.println(\u0026#34;Received Action Request: \u0026#34; + physicalAssetActionWldtEvent.getActionKey() + \u0026#34; with Body: \u0026#34; + physicalAssetActionWldtEvent.getBody()); } else System.err.println(\u0026#34;Wrong Action Received !\u0026#34;); }catch (Exception e){ e.printStackTrace(); } } @Override public void onAdapterStart() { try { //Create an empty PAD PhysicalAssetDescription pad = new PhysicalAssetDescription(); //Add a new Property associated to the target PAD with a key and a default value PhysicalAssetProperty\u0026lt;Double\u0026gt; temperatureProperty = new PhysicalAssetProperty\u0026lt;Double\u0026gt;(GlobalKeywords.TEMPERATURE_PROPERTY_KEY, 0.0); pad.getProperties().add(temperatureProperty); //Add the declaration of a new type of generated event associated to a event key // and the content type of the generated payload PhysicalAssetEvent overheatingEvent = new PhysicalAssetEvent(GlobalKeywords.OVERHEATING_EVENT_KEY, \u0026#34;text/plain\u0026#34;); pad.getEvents().add(overheatingEvent); //Declare the availability of a target action characterized by a Key, an action type // and the expected content type and the request body PhysicalAssetAction setTemperatureAction = new PhysicalAssetAction(GlobalKeywords.SET_TEMPERATURE_ACTION_KEY, \u0026#34;temperature.actuation\u0026#34;, \u0026#34;text/plain\u0026#34;); pad.getActions().add(setTemperatureAction); //Notify the new PAD to the DT\u0026#39;s Shadowing Function this.notifyPhysicalAdapterBound(pad); //Start Device Emulation new Thread(deviceEmulation()).start(); } catch (PhysicalAdapterException | EventBusException e) { e.printStackTrace(); } } @Override public void onAdapterStop() { } private Runnable deviceEmulation(){ return () -\u0026gt; { try { //Sleep 5 seconds to emulate device startup Thread.sleep(5000); //Create a new random object to emulate temperature variations Random r = new Random(); //Publish an initial Event for a normal condition publishPhysicalAssetEventWldtEvent(new PhysicalAssetEventWldtEvent\u0026lt;\u0026gt;(GlobalKeywords.OVERHEATING_EVENT_KEY, \u0026#34;normal\u0026#34;)); //Emulate the generation on \u0026#39;n\u0026#39; temperature measurements for(int i = 0; i \u0026lt; GlobalKeywords.MESSAGE_UPDATE_NUMBER; i++){ //Sleep to emulate sensor measurement Thread.sleep(GlobalKeywords.MESSAGE_UPDATE_TIME); //Update the double randomTemperature = GlobalKeywords.TEMPERATURE_MIN_VALUE + (GlobalKeywords.TEMPERATURE_MAX_VALUE - GlobalKeywords.TEMPERATURE_MIN_VALUE) * r.nextDouble(); //Create a new event to notify the variation of a Physical Property PhysicalAssetPropertyWldtEvent\u0026lt;Double\u0026gt; newPhysicalPropertyEvent = new PhysicalAssetPropertyWldtEvent\u0026lt;\u0026gt;(GlobalKeywords.TEMPERATURE_PROPERTY_KEY, randomTemperature); //Publish the WLDTEvent associated to the Physical Property Variation publishPhysicalAssetPropertyWldtEvent(newPhysicalPropertyEvent); } //Publish a demo Physical Event associated to a \u0026#39;critical\u0026#39; overheating condition publishPhysicalAssetEventWldtEvent(new PhysicalAssetEventWldtEvent\u0026lt;\u0026gt;(GlobalKeywords.OVERHEATING_EVENT_KEY, \u0026#34;critical\u0026#34;)); } catch (EventBusException | InterruptedException e) { e.printStackTrace(); } }; } } Both Physical Adapters and Digital Adapters can be defined natively with a custom configuration provided by the developer as illustrated in the dedicated Section: Configurable Physical \u0026amp; Digital Adapters.\n","date":"February 9, 2024","id":5,"permalink":"/docs/guides/physical-adapter/","summary":"The developer can use an existing Physical Adapter or create a new one to handle the communication with a specific physical twin.","tags":"","title":"Physical Adapter"},{"content":"After the definition of the Physical Adapter it is time to start implementing the core of our DT through the definition of its shadowing function in charge of:\nHandle received PAD from Physical Adapters in order to device which properties, events, relationships or actions available on connected physical twins should be mapped and managed into the DT State Manage incoming notifications/callbacks associated to the variation of physical properties (e.g, temperature variation) or the generation of physical event (e.g., overheating) Process action requests from the digital world that should be validated and forward to the correct Physical Adapter in order to trigger the associated actions on the physical world The Shadowing Function has the responsibility to build and maintain the updated state of the Digital Twin The internal variable of any WLDT Shadowing Function (available through the base class ShadowingFunction) used to do that is DigitalTwinStateManager accessible through the variable: this.digitalTwinStateManager\nWhen the Shadowing Function has to compute the new DT State it can now work with the following method to handle DT State Transition:\nStart the DT State Transaction: startStateTransaction() DT State variation methods such as: createProperty() updateProperty() updatePropertyValue() deleteProperty() enableAction() updateAction() disableAction() registerEvent() updateRegisteredEvent() unRegisterEvent() createRelationship() addRelationshipInstance() deleteRelationship() deleteRelationshipInstance() At the end the transaction can be committed using the method: commitStateTransaction() To access the current DT State the Shadowing Function implementation can use the method this.digitalTwinStateManager.getDigitalTwinState() The information available on the DT State are:\nproperties: List of Properties with their values (if available) actions: List of Actions that can be called on the DT events: List of Events that can be generated by the DT relationships: List of Relationships and their instances (if available) evaluationInstant: The timestamp representing the evaluation instant of the DT state Available main methods on that class instance are:\nProperties: getProperty(String propertyKey): Retrieves if present the target DigitalTwinStateProperty by Key containsProperty(String propertyKey): Checks if a target Property Key is already available in the current Digital Twin\u0026rsquo;s State getPropertyList(): Loads the list of available Properties (described by the class DigitalTwinStateProperty) available on the Digital Twin\u0026rsquo;s State createProperty(DigitalTwinStateProperty\u0026lt;?\u0026gt; dtStateProperty): Allows the creation of a new Property on the Digital Twin\u0026rsquo;s State through the class DigitalTwinStateProperty readProperty(String propertyKey): Retrieves if present the target DigitalTwinStateProperty by Key updateProperty(DigitalTwinStateProperty\u0026lt;?\u0026gt; dtStateProperty): Updates the target property using the DigitalTwinStateProperty and the associated Property Key field deleteProperty(String propertyKey): Deletes the target property identified by the specified key Actions: containsAction(String actionKey): Checks if a Digital Twin State Action with the specified key is correctly registered getAction(String actionKey): Loads the target DigitalTwinStateAction by key getActionList(): Gets the list of available Actions registered on the Digital Twin\u0026rsquo;s State enableAction(DigitalTwinStateAction digitalTwinStateAction): Enables and registers the target Action described through an instance of the DigitalTwinStateAction class updateAction(DigitalTwinStateAction digitalTwinStateAction): Update the already registered target Action described through an instance of the DigitalTwinStateAction class disableAction(String actionKey): Disables and unregisters the target Action described through an instance of the DigitalTwinStateAction class Events: containsEvent(String eventKey): Check if a Digital Twin State Event with the specified key is correctly registered getEvent(String eventKey): Return the description of a registered Digital Twin State Event according to its Key getEventList(): Return the list of existing and registered Digital Twin State Events registerEvent(DigitalTwinStateEvent digitalTwinStateEvent): Register a new Digital Twin State Event updateRegisteredEvent(DigitalTwinStateEvent digitalTwinStateEvent): Update the registration and signature of an existing Digital Twin State Event unRegisterEvent(String eventKey): Un-register a Digital Twin State Event notifyDigitalTwinStateEvent(DigitalTwinStateEventNotification\u0026lt;?\u0026gt; digitalTwinStateEventNotification): Method to notify the occurrence of the target Digital Twin State Event Relationships: containsRelationship(String relationshipName): Checks if a Relationship Name is already available in the current Digital Twin\u0026rsquo;s State createRelationship(DigitalTwinStateRelationship\u0026lt;?\u0026gt; relationship): Creates a new Relationships (described by the class DigitalTwinStateRelationship) in the Digital Twin\u0026rsquo;s State addRelationshipInstance(String name, DigitalTwinStateRelationshipInstance\u0026lt;?\u0026gt; instance): Adds a new Relationship instance described through the class DigitalTwinStateRelationshipInstance and identified through its name getRelationshipList(): Loads the list of existing relationships on the Digital Twin\u0026rsquo;s State through a list of DigitalTwinStateRelationship getRelationship(String name): Gets a target Relationship identified through its name and described through the class DigitalTwinStateRelationship deleteRelationship(String name): Deletes a target Relationship identified through its name deleteRelationshipInstance(String relationshipName, String instanceKey): Deletes the target Relationship Instance using relationship name and instance Key The basic library class that we are going to extend is called ShadowingFunction and creating a new class named DemoShadowingFunction the resulting code is the same after implementing required methods the basic constructor with the id String parameter.\nimport it.wldt.adapter.digital.event.DigitalActionWldtEvent; import it.wldt.adapter.physical.PhysicalAssetDescription; import it.wldt.adapter.physical.event.PhysicalAssetEventWldtEvent; import it.wldt.adapter.physical.event.PhysicalAssetPropertyWldtEvent; import it.wldt.adapter.physical.event.PhysicalAssetRelationshipInstanceCreatedWldtEvent; import it.wldt.adapter.physical.event.PhysicalAssetRelationshipInstanceDeletedWldtEvent; import it.wldt.core.model.ShadowingModelFunction; import java.util.Map; public class DemoShadowingFunction extends ShadowingModelFunction { public DemoShadowingFunction(String id) { super(id); } //// Shadowing Function Management Callbacks //// @Override protected void onCreate() { } @Override protected void onStart() { } @Override protected void onStop() { } //// Bound LifeCycle State Management Callbacks //// @Override protected void onDigitalTwinBound(Map\u0026lt;String, PhysicalAssetDescription\u0026gt; adaptersPhysicalAssetDescriptionMap) { } @Override protected void onDigitalTwinUnBound(Map\u0026lt;String, PhysicalAssetDescription\u0026gt; map, String s) { } @Override protected void onPhysicalAdapterBidingUpdate(String s, PhysicalAssetDescription physicalAssetDescription) { } //// Physical Property Variation Callback //// @Override protected void onPhysicalAssetPropertyVariation(PhysicalAssetPropertyWldtEvent\u0026lt;?\u0026gt; physicalAssetPropertyWldtEvent) { } //// Physical Event Notification Callback //// @Override protected void onPhysicalAssetEventNotification(PhysicalAssetEventWldtEvent\u0026lt;?\u0026gt; physicalAssetEventWldtEvent) { } //// Physical Relationships Notification Callbacks //// @Override protected void onPhysicalAssetRelationshipEstablished(PhysicalAssetRelationshipInstanceCreatedWldtEvent\u0026lt;?\u0026gt; physicalAssetRelationshipInstanceCreatedWldtEvent) { } @Override protected void onPhysicalAssetRelationshipDeleted(PhysicalAssetRelationshipInstanceDeletedWldtEvent\u0026lt;?\u0026gt; physicalAssetRelationshipInstanceDeletedWldtEvent) { } //// Digital Action Received Callbacks //// @Override protected void onDigitalActionEvent(DigitalActionWldtEvent\u0026lt;?\u0026gt; digitalActionWldtEvent) { } } The methods onCreate(), onStart() and onStop() are used to receive callbacks from the DT\u0026rsquo;s core when the Shadowing Function has been effectively created within the twin, is started or stopped according to the evolution of its life cycle. In our initial implementation we are not implementing any of them but they can be useful to trigger specific behaviours according to the different phases.\nThe first method that we have to implement in order to analyze received PAD and build the Digital Twin State in terms of properties, events, relationships and available actions is the onDigitalTwinBound(Map\u0026lt;String, PhysicalAssetDescription\u0026gt; map) method. In our initial implementation we just pass through all the received characteristics recevied from each connected Physical Adapter mapping every physical entity into the DT\u0026rsquo;s state without any change or adaptation (Of course complex behaviour can be implemented to customized the digitalization process).\nThrough the following method we implement the following behaviour:\nAnalyze each received PAD from each connected and active Physical Adapter (in our case we will have just 1 Physical Adapter) Iterate over all the received Properties for each PAD and create the same Property on the Digital Twin State Start observing target Physical Properties in order to receive notification callback about physical variation through the method observePhysicalAssetProperty(property); Analyze received PAD\u0026rsquo;s Events declaration and recreates them also on the DT\u0026rsquo;s State Start observing target Physical Event in order to receive notification callback about physical event generation through the method observePhysicalAssetEvent(event); Check available Physical Action and enable them on the DT\u0026rsquo;s State. Enabled Digital Action are automatically observed by the Shadowing Function in order to receive action requests from active Digital Adapters The possibility to manually observe Physical Properties and Event has been introduced to allow the Shadowing Function to decide what to do according to the nature of the property or of the target event. For example in some cases with static properties it will not be necessary to observe any variation, and it will be enough to read the initial value to build the digital replica of that specific property.\nSince the DT State is managed through the DigitalTwinStateManager class all the changes and variation should be applied on the DT ShadowingFunction using the previously presented transaction management and the correct call of methods startStateTransaction() and commitStateTransaction().\n@Override protected void onDigitalTwinBound(Map\u0026lt;String, PhysicalAssetDescription\u0026gt; adaptersPhysicalAssetDescriptionMap) { try{ // NEW from 0.3.0 -\u0026gt; Start DT State Change Transaction this.digitalTwinStateManager.startStateTransaction(); //Iterate over all the received PAD from connected Physical Adapters adaptersPhysicalAssetDescriptionMap.values().forEach(pad -\u0026gt; { //Iterate over all the received PAD from connected Physical Adapters adaptersPhysicalAssetDescriptionMap.values().forEach(pad -\u0026gt; { pad.getProperties().forEach(property -\u0026gt; { try { //Create and write the property on the DT\u0026#39;s State this.digitalTwinState.createProperty(new DigitalTwinStateProperty\u0026lt;\u0026gt;(property.getKey(),(Double) property.getInitialValue())); //Start observing the variation of the physical property in order to receive notifications //Without this call the Shadowing Function will not receive any notifications or callback about //incoming physical property of the target type and with the target key this.observePhysicalAssetProperty(property); } catch (Exception e) { e.printStackTrace(); } }); //Iterate over available declared Physical Events for the target Physical Adapter\u0026#39;s PAD pad.getEvents().forEach(event -\u0026gt; { try { //Instantiate a new DT State Event with the same key and type DigitalTwinStateEvent dtStateEvent = new DigitalTwinStateEvent(event.getKey(), event.getType()); //Create and write the event on the DT\u0026#39;s State this.digitalTwinState.registerEvent(dtStateEvent); //Start observing the variation of the physical event in order to receive notifications //Without this call the Shadowing Function will not receive any notifications or callback about //incoming physical events of the target type and with the target key this.observePhysicalAssetEvent(event); } catch (Exception e) { e.printStackTrace(); } }); //Iterate over available declared Physical Actions for the target Physical Adapter\u0026#39;s PAD pad.getActions().forEach(action -\u0026gt; { try { //Instantiate a new DT State Action with the same key and type DigitalTwinStateAction dtStateAction = new DigitalTwinStateAction(action.getKey(), action.getType(), action.getContentType()); //Enable the action on the DT\u0026#39;s State this.digitalTwinState.enableAction(dtStateAction); } catch (Exception e) { e.printStackTrace(); } }); }); // NEW from 0.3.0 -\u0026gt; Commit DT State Change Transaction to apply the changes on the DT State and notify about the change this.digitalTwinStateManager.commitStateTransaction(); //Start observation to receive all incoming Digital Action through active Digital Adapter //Without this call the Shadowing Function will not receive any notifications or callback about //incoming request to execute an exposed DT\u0026#39;s Action observeDigitalActionEvents(); //Notify the DT Core that the Bounding phase has been correctly completed and the DT has evaluated its //internal status according to what is available and declared through the Physical Adapters notifyShadowingSync(); }catch (Exception e){ e.printStackTrace(); } } In particular the method observeDigitalActionEvents() should be called start the observation of digital actions and to receive all incoming Digital Action through active Digital Adapters. Without this call the Shadowing Function will not receive any notifications or callback about incoming request to execute an exposed DT\u0026rsquo;s Action. Of course, we have to call this method if we are mapping any digital action in our DT.\nAnother fundamental method is notifyShadowingSync() used to notify the DT Core that the Bounding phase has been correctly completed and the DT has evaluated its internal status according to what is available and declared through the Physical Adapters.\nAs mentioned, in the previous example the Shadowing Function does not apply any control or check on the nature of declared physical property. Of course in order to have a more granular control, it will be possible to use property Key or any other field or even the type of the instance through an instanceof check to implement different controls and behaviours.\nA variation (only for the property management code) to the previous method can be the following:\n//Iterate over available declared Physical Property for the target Physical Adapter\u0026#39;s PAD pad.getProperties().forEach(property -\u0026gt; { try { //Check property Key and Instance of to validate that is a Double if(property.getKey().equals(\u0026#34;temperature-property-key\u0026#34;) \u0026amp;\u0026amp; property.getInitialValue() != null \u0026amp;\u0026amp; property.getInitialValue() instanceof Double) { //Instantiate a new DT State Property of the right type, the same key and initial value DigitalTwinStateProperty\u0026lt;Double\u0026gt; dtStateProperty = new DigitalTwinStateProperty\u0026lt;Double\u0026gt;(property.getKey(),(Double) property.getInitialValue()); //Create and write the property on the DT\u0026#39;s State this.digitalTwinState.createProperty(dtStateProperty); //Start observing the variation of the physical property in order to receive notifications //Without this call the Shadowing Function will not receive any notifications or callback about //incoming physical property of the target type and with the target key this.observePhysicalAssetProperty(property); } } catch (Exception e) { e.printStackTrace(); } }); The next method that we have to implement in order to properly define and implement the behaviour of our DT through its ShadowingModelFunction are:\nonPhysicalAssetPropertyVariation: Method called when a new variation for a specific Physical Property has been detected by the associated Physical Adapter. The method receive as parameter a specific WLDT Event called PhysicalAssetPropertyWldtEvent\u0026lt;?\u0026gt; physicalPropertyEventMessage containing all the information generated by the Physical Adapter upon the variation of the monitored physical counterpart. onPhysicalAssetEventNotification: Callback method used to be notified by a PhysicalAdapter about the generation of a Physical Event. As for the previous method, also this function receive a WLDT Event parameter of type onPhysicalAssetEventNotification(PhysicalAssetEventWldtEvent\u0026lt;?\u0026gt; physicalAssetEventWldtEvent)) containing all the field of the generated physical event. onDigitalActionEvent: On the opposite this method is triggered from one of the active Digital Adapter when an Action request has been received on the Digital Interface. The method receive as parameter an instance of the WLDT Event class DigitalActionWldtEvent\u0026lt;?\u0026gt; digitalActionWldtEvent describing the target digital action request and the associated body. For the onPhysicalAssetPropertyVariation a simple implementation in charge ONLY of mapping the new Physical Property value into the corresponding DT\u0026rsquo;State property can be implemented as follows:\nThe DT State transaction management should be applied in the point of the code where the Shadowing Function receive a variation from the Physical world through a target adapter and the callback method onPhysicalAssetPropertyVariation(...)\n@Override protected void onPhysicalAssetPropertyVariation(PhysicalAssetPropertyWldtEvent\u0026lt;?\u0026gt; physicalPropertyEventMessage) { try { //Update Digital Twin State //NEW from 0.3.0 -\u0026gt; Start State Transaction this.digitalTwinStateManager.startStateTransaction(); this.digitalTwinState.updateProperty(new DigitalTwinStateProperty\u0026lt;\u0026gt;(physicalPropertyEventMessage.getPhysicalPropertyId(), physicalPropertyEventMessage.getBody())); //NEW from 0.3.0 -\u0026gt; Commit State Transaction this.digitalTwinStateManager.commitStateTransaction(); } catch (WldtDigitalTwinStatePropertyException | WldtDigitalTwinStatePropertyBadRequestException | WldtDigitalTwinStatePropertyNotFoundException | WldtDigitalTwinStateException e) { e.printStackTrace(); } } In this case as reported in the code, we call the method this.digitalTwinState.updateProperty on the Shadowing Function in order to update an existing DT\u0026rsquo;State property (previously created in the onDigitalTwinBound method). To update the value we directly use the received data on the PhysicalAssetPropertyWldtEvent without any additional check or change that might be instead needed in advanced examples.\nFollowing the same principle, a simplified digital mapping between physical and digital state upon the receving of a physical event variation can be the following:\n@Override protected void onPhysicalAssetEventNotification(PhysicalAssetEventWldtEvent\u0026lt;?\u0026gt; physicalAssetEventWldtEvent) { try { this.digitalTwinStateManager.notifyDigitalTwinStateEvent(new DigitalTwinStateEventNotification\u0026lt;\u0026gt;(physicalAssetEventWldtEvent.getPhysicalEventKey(), physicalAssetEventWldtEvent.getBody(), physicalAssetEventWldtEvent.getCreationTimestamp())); } catch (WldtDigitalTwinStateEventNotificationException | EventBusException e) { e.printStackTrace(); } } With respect to events management, we use the Shadowint Function method this.digitalTwinState.notifyDigitalTwinStateEvent to notify the other DT Components (e.g., Digital Adapters) the incoming Physical Event by creating a new instance of a DigitalTwinStateEventNotification class containing all the information associated to the event. Of course, additional controls and checks can be introduced in this method validating and processing the incoming physical message to define complex behaviours.\nThe last method that we are going to implement is the onDigitalActionEvent one where we have to handle an incoming Digital Action request associated to an Action declared on the DT\u0026rsquo;s State in the onDigitalTwinBound method. In that case the Digital Action should be forwarded to the Physical Interface in order to be sent to the physical counterpart for the effective execution.\n@Override protected void onDigitalActionEvent(DigitalActionWldtEvent\u0026lt;?\u0026gt; digitalActionWldtEvent) { try { this.publishPhysicalAssetActionWldtEvent(digitalActionWldtEvent.getActionKey(), digitalActionWldtEvent.getBody()); } catch (EventBusException e) { e.printStackTrace(); } } Also in that case we are forwarding the incoming Digital Action request described through the class DigitalActionWldtEvent to the Physical Adapter with the method of the Shadowing Function denoted as this.publishPhysicalAssetActionWldtEvent and passing directly the action key and the target Body. No additional processing or validation have been introduced here, but they might be required in advanced scenario in order to properly adapt incoming digital action request to what is effectively expected on the physical counterpart.\n","date":"February 9, 2024","id":6,"permalink":"/docs/guides/shadowing-function/","summary":"After the definition of the Physical Adapter it is time to start implementing the core of our DT through the definition of its shadowing function in charge of:","tags":"","title":"Shadowing Function"},{"content":"The las component that we have to implement to complete our first simple Digital Twin definition through the WLDT library is a Digital Adapter in charge of:\nReceiving event from the DT\u0026rsquo;s Core related to the variation of properties, events, available actions and relationships Expose received information to the external world according to its implementation and the supported protocol Handle incoming digital action and forward them to the Core in order to be validated and processed by the Shadowing Function The basic library class that we are going to extend is called DigitalAdapter and creating a new class named DemoDigitalAdapter. The DigitalTwinAdapter class can take as Generic Type the type of Configuration used to configure its behaviours. In this simplified example we are defining a DigitalAdapter without any Configuration.\nA Digital Adapter has direct access to the current DT\u0026rsquo;s State through callbacks or directly in a synchronous way using the internal variable called: digitalTwinState. Through it is possibile to navigate all the fields currently composing the state of our Digital Twin.\nThe Digital Adapter class has e long list of callback and notification method to allow the adapter to be updated about all the variation and changes on the twin. Available callbacks can be summarized as follows:\nDigital Adapter Start/Stop: onAdapterStart(): Feedback when the Digital Adapter correctly starts onAdapterStop(): Feedback when the Digital Adapter has been stopped Digital Twin Life Cycle Notifications: onDigitalTwinCreate(): The DT has been created onDigitalTwinStart(): The DT started onDigitalTwinSync(IDigitalTwinState digitalTwinState): The DT is Synchronized with its physical counterpart. The current DigitalTwinState is passed as parameter to allow the Digital Adapter to know the current state and consequently implement its behaviour onDigitalTwinUnSync(IDigitalTwinState digitalTwinState): The DT is not synchronized anymore with its physical counterpart. The last current DigitalTwinState is passed as parameter to allow the Digital Adapter to know the last state and consequently implement its behaviour onDigitalTwinStop(): The DT is stopped onDigitalTwinDestroy(): The DT has been destroyed and the application stopped The Digital Adapter DT State variations and DT events are received by the Adapter from the DT core belongs to the following categories:\nDigital Twin State Update through the method onStateUpdate(...) providing information about the new state of the Digital Twin, the previous state, and a list of changes that occurred between these two states. In the previous version each variation of a property, relationships, actions or events were notified. In the new version only a committed DT\u0026rsquo;State variation is notified to listeners. Event Notifications through the method onEventNotificationReceived(...) whenever there is a notification about an event related to the Digital Twin\u0026rsquo;s state coming from the physical world, generated by the twin and processed by the Shadowing Function. For example in the DT State we can have the declaration of the over-heating-alert structured and received in the DT State while the effective occurrence of the event and the associated notification is notified through this dedicated callback The onStateUpdate method is an abstract method that must be implemented by any class extending the DigitalAdapter class. This method is called whenever there is an update to the Digital Twin\u0026rsquo;s state. It provides information about the new state of the Digital Twin, the previous state, and a list of changes that occurred between these two states.\nThe explanation of the parameters is the following:\nnewDigitalTwinState: This parameter represents the updated state of the Digital Twin. It is an instance of the DigitalTwinState class, which encapsulates the current state information. previousDigitalTwinState: This parameter represents the state of the Digital Twin before the update. It is also an instance of the DigitalTwinState class. digitalTwinStateChangeList: This parameter is an ArrayList containing DigitalTwinStateChange objects. Each DigitalTwinStateChange object encapsulates information about a specific change that occurred between the previous and new states. It includes details such as the property or aspect of the state that changed, the previous value, and the new value. Another core method where a Digital Adapter receive the description of the DT\u0026rsquo;State is onDigitalTwinSync(IDigitalTwinState digitalTwinState). The Adapter using the parameter digitalTwinState can analyze available properties, actions, events and relationships and decide how to implement its internal behaviour with the methods presented in ShadowingFunction. The DT State is automatically monitored by each Digital Adapter while for the Events potentially generated by the DT can be observed by each adapter using:\nobserveAllDigitalTwinEventsNotifications: Enable the observation of available Digital Twin State Events Notifications. unObserveAllDigitalTwinEventsNotifications: Cancel the observation of Digital Twin State Events Notifications observeDigitalTwinEventsNotifications: Enable the observation of the notification associated to a specific list of Digital Twin State events. With respect to event a notification contains the new associated value unObserveDigitalTwinEventsNotifications: Cancel the observation of a target list of properties observeDigitalTwinEventNotification: Enable the observation of the notification associated to a single Digital Twin State event. With respect to event a notification contains the new associated value unObserveDigitalTwinEventNotification: Cancel the observation of a single target event The resulting code will be the following after adding the required methods (still empty) and the basic constructor with the id String parameter is the following:\nimport it.wldt.adapter.digital.DigitalAdapter; import it.wldt.core.state.*; public class DemoDigitalAdapter extends DigitalAdapter\u0026lt;Void\u0026gt; { public DemoDigitalAdapter(String id) { super(id); } /** * Callback to notify the adapter on its correct startup */ @Override public void onAdapterStart() {} /** * Callback to notify the adapter that has been stopped */ @Override public void onAdapterStop() {} /** * DT Life Cycle notification that the DT is correctly on Sync * @param digitalTwinState */ @Override public void onDigitalTwinSync(DigitalTwinState digitalTwinState) {} /** * DT Life Cycle notification that the DT is currently Not Sync * @param digitalTwinState */ @Override public void onDigitalTwinUnSync(DigitalTwinState digitalTwinState) {} /** * DT Life Cycle notification that the DT has been created */ @Override public void onDigitalTwinCreate() {} /** * DT Life Cycle Notification that the DT has correctly Started */ @Override public void onDigitalTwinStart() {} /** * DT Life Cycle Notification that the DT has been stopped */ @Override public void onDigitalTwinStop() {} /** * DT Life Cycle Notification that the DT has destroyed */ @Override public void onDigitalTwinDestroy() {} /** * Callback method allowing the Digital Adapter to receive the updated Digital Twin State together with * the previous state and the list of applied changes * * @param newDigitalTwinState The new Digital Twin State computed by the Shadowing Function * @param previousDigitalTwinState The previous Digital Twin State * @param digitalTwinStateChangeList The list of applied changes to compute the new Digital Twin State */ @Override protected void onStateUpdate(DigitalTwinState newDigitalTwinState, DigitalTwinState previousDigitalTwinState, ArrayList\u0026lt;DigitalTwinStateChange\u0026gt; digitalTwinStateChangeList) {} /** * Callback method to receive a new computed Event Notification (associated to event declared in the DT State) * * @param digitalTwinStateEventNotification The generated Notification associated to a DT Event */ @Override protected void onEventNotificationReceived(DigitalTwinStateEventNotification\u0026lt;?\u0026gt; digitalTwinStateEventNotification) {} } By default, a Digital Adapter observes all the variation on the DT\u0026rsquo;s State in terms of Properties, Relationships, Actions and Events. As previously mentioned the observation of DT\u0026rsquo;s State Properties allows to receive also properties variation on the method since a property is natively composed by its description (e.g., type) and its current value. On the opposite the observation on DT\u0026rsquo;s State Action, Relationships and Events allow ONLY to receive callbacks when a new entity is added or an update is occurred without receiving updates on values variation.\nThe only thing that we should add in the onDigitalTwinSync(IDigitalTwinState currentDigitalTwinState) callback is the direct observation for Events. Following this approach we can change our Digital Adapter in the following methods:\nIn onDigitalTwinSync we observe in this first simple implementation only the incoming values for declared Events in the DT\u0026rsquo;State. As previously mentioned the observation of any variation of the State structure together with Properties Values are by default observed by any Digital Adapter. In this method we use the internal variable digitalTwinState to access the DT\u0026rsquo;s state and find available Events declaration that we would like to observe.\npublic void onDigitalTwinSync(IDigitalTwinState currentDigitalTwinState) { try { //Retrieve the list of available events and observe all variations digitalTwinState.getEventList() .map(eventList -\u0026gt; eventList.stream() .map(DigitalTwinStateEvent::getKey) .collect(Collectors.toList())) .ifPresent(eventKeys -\u0026gt; { try { observeDigitalTwinEventsNotifications(eventKeys); } catch (EventBusException e) { e.printStackTrace(); } }); } catch (Exception e) { e.printStackTrace(); } } Developers extending the DigitalAdapter class should implement the onStateUpdate method to define custom logic that needs to be executed whenever the state of the Digital Twin is updated. This could include tasks such as processing state changes, updating internal variables, triggering specific actions, or notifying other components about the state update.\nHere\u0026rsquo;s an example of how the method might be implemented in a concrete subclass of DigitalAdapter:\n@Override protected void onStateUpdate(DigitalTwinState newDigitalTwinState, DigitalTwinState previousDigitalTwinState, ArrayList\u0026lt;DigitalTwinStateChange\u0026gt; digitalTwinStateChangeList) { // In newDigitalTwinState we have the new DT State System.out.println(\u0026#34;New DT State is: \u0026#34; + newDigitalTwinState); // The previous DT State is available through the variable previousDigitalTwinState System.out.println(\u0026#34;Previous DT State is: \u0026#34; + previousDigitalTwinState); // We can also check each DT\u0026#39;s state change potentially differentiating the behaviour for each change if (digitalTwinStateChangeList != null \u0026amp;\u0026amp; !digitalTwinStateChangeList.isEmpty()) { // Iterate through each state change in the list for (DigitalTwinStateChange stateChange : digitalTwinStateChangeList) { // Get information from the state change DigitalTwinStateChange.Operation operation = stateChange.getOperation(); DigitalTwinStateChange.ResourceType resourceType = stateChange.getResourceType(); DigitalTwinStateResource resource = stateChange.getResource(); // Perform different actions based on the type of operation switch (operation) { case OPERATION_UPDATE: // Handle an update operation System.out.println(\u0026#34;Update operation on \u0026#34; + resourceType + \u0026#34;: \u0026#34; + resource); break; case OPERATION_UPDATE_VALUE: // Handle an update value operation System.out.println(\u0026#34;Update value operation on \u0026#34; + resourceType + \u0026#34;: \u0026#34; + resource); break; case OPERATION_ADD: // Handle an add operation System.out.println(\u0026#34;Add operation on \u0026#34; + resourceType + \u0026#34;: \u0026#34; + resource); break; case OPERATION_REMOVE: // Handle a remove operation System.out.println(\u0026#34;Remove operation on \u0026#34; + resourceType + \u0026#34;: \u0026#34; + resource); break; default: // Handle unknown operation (optional) System.out.println(\u0026#34;Unknown operation on \u0026#34; + resourceType + \u0026#34;: \u0026#34; + resource); break; } } } else { // No state changes System.out.println(\u0026#34;No state changes detected.\u0026#34;); } } In this example, the method iterates over the list of state changes, extracts information about each change, and performs custom actions based on the changes. Developers can adapt this method to suit the specific requirements of their Digital Twin application.\nBoth Physical Adapters and Digital Adapters can be defined natively with a custom configuration provided by the developer as illustrated in the dedicated Section: Configurable Physical \u0026amp; Digital Adapters.\n","date":"February 9, 2024","id":7,"permalink":"/docs/guides/digital-adapter/","summary":"The las component that we have to implement to complete our first simple Digital Twin definition through the WLDT library is a Digital Adapter in charge of:","tags":"","title":"Digital Adapter"},{"content":"Now that we have created the main fundamental element of a DT (Physical Adapter, Shadowing Function and Digital Adapter) we can create Class file with a main to create the WLDT Engine with the created components and start the DT.\nCreate a new Java file called DemoDigitalTwin adding the following code:\nWith the following code we now create a new Digital Twin Instance\n// Create the new Digital Twin with its Shadowing Function DigitalTwin digitalTwin = new DigitalTwin(digitalTwinId, new DemoShadowingFunction()); // Physical Adapter with Configuration digitalTwin.addPhysicalAdapter( new DemoPhysicalAdapter( String.format(\u0026#34;%s-%s\u0026#34;, digitalTwinId, \u0026#34;test-physical-adapter\u0026#34;), new DemoPhysicalAdapterConfiguration(), true)); // Digital Adapter with Configuration digitalTwin.addDigitalAdapter( new DemoDigitalAdapter( String.format(\u0026#34;%s-%s\u0026#34;, digitalTwinId, \u0026#34;test-digital-adapter\u0026#34;), new DemoDigitalAdapterConfiguration()) ); DTs cannot be directly run but it should be added to the DigitalTwinEngine in order to be executed through the WLDT Library\n// Create the Digital Twin Engine DigitalTwinEngine digitalTwinEngine = new DigitalTwinEngine(); // Add the Digital Twin to the Engine digitalTwinEngine.addDigitalTwin(digitalTwin); In order to start a DT from the Engine you can:\n// Directly start when you add it passing a second boolean value = true digitalTwinEngine.addDigitalTwin(digitalTwin. true); // Starting the single DT on the engine through its id digitalTwinEngine.startDigitalTwin(DIGITAL_TWIN_ID); // Start all the DTs registered on the engine digitalTwinEngine.startAll(); To stop a single twin or all the twin registered on the engine:\n// Stop a single DT on the engine through its id digitalTwinEngine.stopDigitalTwin(DIGITAL_TWIN_ID); // Stop all the DTs registered on the engine digitalTwinEngine.stopAll(); It is also possible to remove a DT from the Engine with a consequent stop if it is active and the deletion of its reference from the engine:\n// Remove a single DT on the engine through its id digitalTwinEngine.removeDigitalTwin(DIGITAL_TWIN_ID); // Remove all the DTs registered on the engine digitalTwinEngine.removeAll(); The resulting code in our case is:\npublic class DemoDigitalTwin { public static void main(String[] args) { try{ // Create the new Digital Twin DigitalTwin digitalTwin = new DigitalTwin( \u0026#34;test-dt-id\u0026#34;, new DemoShadowingFunction(\u0026#34;test-shadowing-function\u0026#34;) ); //Default Physical and Digital Adapter //digitalTwin.addPhysicalAdapter(new DemoPhysicalAdapter(\u0026#34;test-physical-adapter\u0026#34;)); //digitalTwin.addDigitalAdapter(new DemoDigitalAdapter(\u0026#34;test-digital-adapter\u0026#34;)); //Physical and Digital Adapters with Configuration digitalTwin.addPhysicalAdapter(new DemoConfPhysicalAdapter(\u0026#34;test-physical-adapter\u0026#34;, new DemoPhysicalAdapterConfiguration())); digitalTwin.addDigitalAdapter(new DemoConfDigitalAdapter(\u0026#34;test-digital-adapter\u0026#34;, new DemoDigitalAdapterConfiguration())); // Create the Digital Twin Engine DigitalTwinEngine digitalTwinEngine = new DigitalTwinEngine(); // Add the Digital Twin to the Engine digitalTwinEngine.addDigitalTwin(digitalTwin); // Set a new Event-Logger to a Custom One that we created with the class \u0026#39;DemoEventLogger\u0026#39; WldtEventBus.getInstance().setEventLogger(new DemoEventLogger()); // Start all the DTs registered on the engine digitalTwinEngine.startAll(); }catch (Exception e){ e.printStackTrace(); } } } ","date":"February 9, 2024","id":8,"permalink":"/docs/guides/dt-engine-dt-instance/","summary":"Now that we have created the main fundamental element of a DT (Physical Adapter, Shadowing Function and Digital Adapter) we can create Class file with a main to create the WLDT Engine with the created components and start the DT.","tags":"","title":"DT Engine \u0026 DT Instance"},{"content":"In this demo implementation, we are going to emulate an incoming Digital Action on the Digital Adapter in order to show how it can be handled by the adapter and properly forwarded to the Shadowing Function for validation and the consequent interaction with the Physical Adapter and then with the physical twin.\nIn order to add a demo Digital Action trigger on the Digital Adapter we add the following method to the DemoDigitalAdapter class:\nprivate Runnable emulateIncomingDigitalAction(){ return () -\u0026gt; { try { System.out.println(\u0026#34;Sleeping before Emulating Incoming Digital Action ...\u0026#34;); Thread.sleep(5000); Random random = new Random(); //Emulate the generation on \u0026#39;n\u0026#39; temperature measurements for(int i = 0; i \u0026lt; 10; i++){ //Sleep to emulate sensor measurement Thread.sleep(1000); double randomTemperature = 25.0 + (30.0 - 25.0) * random.nextDouble(); publishDigitalActionWldtEvent(\u0026#34;set-temperature-action-key\u0026#34;, randomTemperature); } } catch (Exception e) { e.printStackTrace(); } }; } This method uses the Digital Adapter internal function denoted as publishDigitalActionWldtEvent(String actionKey, T body) allowing the adapter to send a notification to the DT\u0026rsquo;s Core (and consequently the Shadowing Function) about the arrival of a Digital Action with a specific key and body. In our case the key is set-temperature-action-key as declared in the Physical Adapter and in the PAD and the value is a simple Double with the new temperature value.\nThen we call this method in the following way at the end ot the onDigitalTwinSync(IDigitalTwinState currentDigitalTwinState) method.\n//Start Digital Action Emulation new Thread(emulateIncomingDigitalAction()).start(); Now the Shadowing Function should be updated in order to handle the incoming Action request from the Digital Adapter. In our case the shadowing function does not apply any validation or check and just forward to action to the Physical Adapter in order to be then forwarded to the physical twin. Of course advanced implementation can be introduced for example to validate action, adapt payload and data-formats or to augment functionalities (e.g., trigger multiple physical actions from a single digital request).\nIn our simple demo implementation the updated Shadowing Function method onDigitalActionEvent(DigitalActionWldtEvent\u0026lt;?\u0026gt; digitalActionWldtEvent) results as follows:\n@Override protected void onDigitalActionEvent(DigitalActionWldtEvent\u0026lt;?\u0026gt; digitalActionWldtEvent) { try { this.publishPhysicalAssetActionWldtEvent(digitalActionWldtEvent.getActionKey(), digitalActionWldtEvent.getBody()); } catch (Exception e) { e.printStackTrace(); } } This forwarding of the action triggers the corresponding Physical Adapter method onIncomingPhysicalAction(PhysicalAssetActionWldtEvent\u0026lt;?\u0026gt; physicalAssetActionWldtEvent) that in our case is emulated just with a Log on the console. Also in that case advanced Physical Adapter implementation can be introduced for example to adapt the request from a high-level (and potentially standard) DT action description to the custom requirements of the specific physical twin managed by the adapter.\n@Override public void onIncomingPhysicalAction(PhysicalAssetActionWldtEvent\u0026lt;?\u0026gt; physicalAssetActionWldtEvent) { try{ if(physicalAssetActionWldtEvent != null \u0026amp;\u0026amp; physicalAssetActionWldtEvent.getActionKey().equals(SET_TEMPERATURE_ACTION_KEY) \u0026amp;\u0026amp; physicalAssetActionWldtEvent.getBody() instanceof Double) { System.out.println(\u0026#34;Received Action Request: \u0026#34; + physicalAssetActionWldtEvent.getActionKey() + \u0026#34; with Body: \u0026#34; + physicalAssetActionWldtEvent.getBody()); } else System.err.println(\u0026#34;Wrong Action Received !\u0026#34;); }catch (Exception e){ e.printStackTrace(); } } ","date":"February 9, 2024","id":9,"permalink":"/docs/guides/digital-actions/","summary":"In this demo implementation, we are going to emulate an incoming Digital Action on the Digital Adapter in order to show how it can be handled by the adapter and properly forwarded to the Shadowing Function for validation and the consequent interaction with the Physical Adapter and then with the physical twin.","tags":"","title":"Digital Actions"},{"content":"The same management that we have illustrated for Properties, Events and Action can be applied also to Digital Twin Relationships. Relationships represent the links that exist between the modeled physical assets and other physical entity of the organizations through links to their corresponding Digital Twins. Like properties, relationships can be observed, dynamically created, and change over time, but unlike properties, they are not properly part of the PA\u0026rsquo;s state but of its operational context (e.g., a DT of a robot within a production line).\nIt is necessary to distinguish between two concepts: i) Relationship; and ii) Relationship Instance. The first one models the relationship from a semantic point of view, defining its name and target type. The second one represents an instantiation of the concept in reality. For example, in the context of a Smart Home, the Home Digital Twin (DT) will define a Relationship called has_room which has possible targets represented by DTs that represent different rooms of the house. The actual link between the Home DT and the Bedroom DT will be modeled by a specific Relationship Instance of the has_room relationship.\nWithin the state of the DT, it is necessary to differentiate between the concept of a relationship and that of an instance of a relationship. In the first case, we refer to a semantic concept where each relationship, through its name and the semantic type of its target, determines the different type of link that the DT can establish. On the other hand, an instanc of a relationship represents the concrete link present between the DT that establishes it and the target DT. For instance, in the case of a Smart Home, the Bedroom DT may have two relationships in its model: one named is_room_of and another called has_device. An instance of the first type of relationship could, for example, have the Home DT as its target, while the has_device relationship could have multiple instances, one for each device present in the room. An example of a possible instance is one targeting the Air Conditioner DT.\nFrom an implementation perspective, in the Physical Adapter and in particular where we handle the definition of the PAD we can also specify the existing relationships. In our case, since the Relationship is useful also to define its future instance we keep a reference of the relationship as in internal variable called insideInRelationship.\nThen we can update the code as follows:\nprivate PhysicalAssetRelationship\u0026lt;String\u0026gt; insideInRelationship = null; @Override public void onIncomingPhysicalAction(PhysicalAssetActionWldtEvent\u0026lt;?\u0026gt; physicalAssetActionWldtEvent) { try{ [...] //Create Test Relationship to describe that the Physical Device is inside a building this.insideInRelationship=new PhysicalAssetRelationship\u0026lt;\u0026gt;(\u0026#34;insideId\u0026#34;); pad.getRelationships().add(insideInRelationship); [...] } catch (Exception e){ e.printStackTrace(); } } Of course always in the Physical Adapter we need to publish an effective instance of the definite Relationship. To do that, we have defined a dedicated method that we can call inside the adapter to notify the DT\u0026rsquo;s Core and in particular the Shadowing Function on the presence of a new Relationship.\nThe following method can be added for example at the beginning of the Device Emulation:\nprivate void publishPhysicalRelationshipInstance() { try{ String relationshipTarget = \u0026#34;building-hq\u0026#34;; Map\u0026lt;String, Object\u0026gt; relationshipMetadata = new HashMap\u0026lt;\u0026gt;(); relationshipMetadata.put(\u0026#34;floor\u0026#34;, \u0026#34;f0\u0026#34;); relationshipMetadata.put(\u0026#34;room\u0026#34;, \u0026#34;r0\u0026#34;); PhysicalAssetRelationshipInstance\u0026lt;String\u0026gt; relInstance = this.insideInRelationship.createRelationshipInstance(relationshipTarget, relationshipMetadata); PhysicalAssetRelationshipInstanceCreatedWldtEvent\u0026lt;String\u0026gt; relInstanceEvent = new PhysicalAssetRelationshipInstanceCreatedWldtEvent\u0026lt;\u0026gt;(relInstance); publishPhysicalAssetRelationshipCreatedWldtEvent(relInstanceEvent); }catch (Exception e){ e.printStackTrace(); } } On the other hand, as already done for all the other Properties, Actions and Events we have to handle them on the Shadowing Function and in particular updating the onDigitalTwinBound(...) method managing Relationship declaration. Also for the Relationships there is the method denoted as observePhysicalAssetRelationship(relationship) to observe the variation of the target entity.\n@Override protected void onDigitalTwinBound(Map\u0026lt;String, PhysicalAssetDescription\u0026gt; adaptersPhysicalAssetDescriptionMap) { try{ //Iterate over all the received PAD from connected Physical Adapters adaptersPhysicalAssetDescriptionMap.values().forEach(pad -\u0026gt; { [...] //Iterate over Physical Relationships pad.getRelationships().forEach(relationship -\u0026gt; { try{ if(relationship != null \u0026amp;\u0026amp; relationship.getName().equals(GlobalKeywords.INSIDE_IN_RELATIONSHIP)){ DigitalTwinStateRelationship\u0026lt;String\u0026gt; insideInDtStateRelationship = new DigitalTwinStateRelationship\u0026lt;\u0026gt;(relationship.getName(), relationship.getName()); this.digitalTwinState.createRelationship(insideInDtStateRelationship); observePhysicalAssetRelationship(relationship); } }catch (Exception e){ e.printStackTrace(); } }); }); [...] }catch (Exception e){ e.printStackTrace(); } } When an Instance for a target observed Relationship has been notified by the Physical Adapter, we will receive a call back on the Shadowing Function method called: onPhysicalAssetRelationshipEstablished(PhysicalAssetRelationshipInstanceCreatedWldtEvent\u0026lt;?\u0026gt; physicalAssetRelationshipInstanceCreatedWldtEvent). The object PhysicalAssetRelationshipInstanceCreatedWldtEvent describes the events and contains an object PhysicalAssetRelationshipInstance with all the information about the new Relationship Instance.\nThe Shadowing Function analyzes the instance and create the corresponding Digital Relationship instance on the DT\u0026rsquo;State through the class DigitalTwinStateRelationshipInstance and the method this.digitalTwinState.addRelationshipInstance(relName, instance);. The resulting implemented method is the following:\n//// Physical Relationships Notification Callbacks //// @Override protected void onPhysicalAssetRelationshipEstablished(PhysicalAssetRelationshipInstanceCreatedWldtEvent\u0026lt;?\u0026gt; physicalAssetRelationshipInstanceCreatedWldtEvent) { try{ if(physicalAssetRelationshipInstanceCreatedWldtEvent != null \u0026amp;\u0026amp; physicalAssetRelationshipInstanceCreatedWldtEvent.getBody() != null){ PhysicalAssetRelationshipInstance\u0026lt;?\u0026gt; paRelInstance = physicalAssetRelationshipInstanceCreatedWldtEvent.getBody(); if(paRelInstance.getTargetId() instanceof String){ String relName = paRelInstance.getRelationship().getName(); String relKey = paRelInstance.getKey(); String relTargetId = (String)paRelInstance.getTargetId(); DigitalTwinStateRelationshipInstance\u0026lt;String\u0026gt; instance = new DigitalTwinStateRelationshipInstance\u0026lt;String\u0026gt;(relName, relTargetId, relKey); //Update Digital Twin State //NEW from 0.3.0 -\u0026gt; Start State Transaction this.digitalTwinStateManager.startStateTransaction(); this.digitalTwinStateManager.addRelationshipInstance(instance); //NEW from 0.3.0 -\u0026gt; Commit State Transaction this.digitalTwinStateManager.commitStateTransaction(); } } }catch (Exception e){ e.printStackTrace(); } } @Override protected void onPhysicalAssetRelationshipDeleted(PhysicalAssetRelationshipInstanceDeletedWldtEvent\u0026lt;?\u0026gt; physicalAssetRelationshipInstanceDeletedWldtEvent) { } At the end the new DT\u0026rsquo;s Relationships and the associated instances can be managed on a Digital Adapter using the onDigitalTwinSync(IDigitalTwinState currentDigitalTwinState) method and the following DT state callback method: onStateUpdate().\nFor example a simple implementation logging on the console can be:\n@Override protected void onStateUpdate(DigitalTwinState newDigitalTwinState, DigitalTwinState previousDigitalTwinState, ArrayList\u0026lt;DigitalTwinStateChange\u0026gt; digitalTwinStateChangeList) { // In newDigitalTwinState we have the new DT State System.out.println(\u0026#34;New DT State is: \u0026#34; + newDigitalTwinState); // The previous DT State is available through the variable previousDigitalTwinState System.out.println(\u0026#34;Previous DT State is: \u0026#34; + previousDigitalTwinState); // We can also check each DT\u0026#39;s state change potentially differentiating the behaviour for each change if (digitalTwinStateChangeList != null \u0026amp;\u0026amp; !digitalTwinStateChangeList.isEmpty()) { // Iterate through each state change in the list [...] // Specific log example for Relationships Instance Variation if(resourceType.equals(DigitalTwinStateChange.ResourceType.RELATIONSHIP_INSTANCE)) System.out.println(\u0026#34;New Relationship Instance operation:\u0026#34; + operation + \u0026#34; Resource:\u0026#34; + resource); } } else { // No state changes System.out.println(\u0026#34;No state changes detected.\u0026#34;); } } ","date":"February 9, 2024","id":10,"permalink":"/docs/guides/dt-relationships/","summary":"The same management that we have illustrated for Properties, Events and Action can be applied also to Digital Twin Relationships. Relationships represent the links that exist between the modeled physical assets and other physical entity of the organizations through links to their corresponding Digital Twins.","tags":"","title":"DT Relationships"},{"content":"The WLDT library provides a native method to define Configurable Physical ad Digital Adapters specifying a custom configuration class passed as parameter in the constructor.\nStarting with the Physical Adapter created in the previous example DemoPhysicalAdapter instead of extending the base class PhysicalAdapter we can extend now ConfigurablePhysicalAdapter\u0026lt;C\u0026gt; where C is the name of the that we would like to use as configuration.\nIn our example we can create a simple configuration class called DemoPhysicalAdapterConfiguration where we move the constant variable used to implement the behaviour of our demo physical adapter. The resulting class will be the following:\npublic class DemoPhysicalAdapterConfiguration { private int messageUpdateTime = GlobalKeywords.MESSAGE_UPDATE_TIME; private int messageUpdateNumber = GlobalKeywords.MESSAGE_UPDATE_NUMBER; private double temperatureMinValue = GlobalKeywords.TEMPERATURE_MIN_VALUE; private double temperatureMaxValue = GlobalKeywords.TEMPERATURE_MAX_VALUE; public DemoPhysicalAdapterConfiguration() { } public DemoPhysicalAdapterConfiguration(int messageUpdateTime, int messageUpdateNumber, double temperatureMinValue, double temperatureMaxValue) { this.messageUpdateTime = messageUpdateTime; this.messageUpdateNumber = messageUpdateNumber; this.temperatureMinValue = temperatureMinValue; this.temperatureMaxValue = temperatureMaxValue; } public int getMessageUpdateTime() { return messageUpdateTime; } public void setMessageUpdateTime(int messageUpdateTime) { this.messageUpdateTime = messageUpdateTime; } public int getMessageUpdateNumber() { return messageUpdateNumber; } public void setMessageUpdateNumber(int messageUpdateNumber) { this.messageUpdateNumber = messageUpdateNumber; } public double getTemperatureMinValue() { return temperatureMinValue; } public void setTemperatureMinValue(double temperatureMinValue) { this.temperatureMinValue = temperatureMinValue; } public double getTemperatureMaxValue() { return temperatureMaxValue; } public void setTemperatureMaxValue(double temperatureMaxValue) { this.temperatureMaxValue = temperatureMaxValue; } } Now we can create or update our Physical Adapter extending ConfigurablePhysicalAdapter\u0026lt;DemoPhysicalAdapterConfiguration\u0026gt; as illustrated in the following snippet:\npublic class DemoPhysicalAdapter extends ConfigurablePhysicalAdapter\u0026lt;DemoPhysicalAdapterConfiguration\u0026gt; { [...] } Extending this class also the constructor should be updated getting as a parameter the expected configuration instance. Our constructor will be the following:\npublic DemoConfPhysicalAdapter(String id, DemoPhysicalAdapterConfiguration configuration) { super(id, configuration); } After that change since we removed and moved the used constant values into the new Configuration class we have also to update the deviceEmulation() method having access to the configuration through the method getConfiguration() or this.getConfiguration() directly on the adapter.\nprivate Runnable deviceEmulation(){ return () -\u0026gt; { try { System.out.println(\u0026#34;[DemoPhysicalAdapter] -\u0026gt; Sleeping before Starting Physical Device Emulation ...\u0026#34;); //Sleep 5 seconds to emulate device startup Thread.sleep(10000); System.out.println(\u0026#34;[DemoPhysicalAdapter] -\u0026gt; Starting Physical Device Emulation ...\u0026#34;); //Create a new random object to emulate temperature variations Random r = new Random(); //Publish an initial Event for a normal condition publishPhysicalAssetEventWldtEvent(new PhysicalAssetEventWldtEvent\u0026lt;\u0026gt;(GlobalKeywords.OVERHEATING_EVENT_KEY, \u0026#34;normal\u0026#34;)); //Emulate the generation on \u0026#39;n\u0026#39; temperature measurements for(int i = 0; i \u0026lt; getConfiguration().getMessageUpdateNumber(); i++){ //Sleep to emulate sensor measurement Thread.sleep(getConfiguration().getMessageUpdateTime()); //Update the double randomTemperature = getConfiguration().getTemperatureMinValue() + (getConfiguration().getTemperatureMaxValue() - getConfiguration().getTemperatureMinValue()) * r.nextDouble(); //Create a new event to notify the variation of a Physical Property PhysicalAssetPropertyWldtEvent\u0026lt;Double\u0026gt; newPhysicalPropertyEvent = new PhysicalAssetPropertyWldtEvent\u0026lt;\u0026gt;(GlobalKeywords.TEMPERATURE_PROPERTY_KEY, randomTemperature); //Publish the WLDTEvent associated to the Physical Property Variation publishPhysicalAssetPropertyWldtEvent(newPhysicalPropertyEvent); } //Publish a demo Physical Event associated to a \u0026#39;critical\u0026#39; overheating condition publishPhysicalAssetEventWldtEvent(new PhysicalAssetEventWldtEvent\u0026lt;\u0026gt;(GlobalKeywords.OVERHEATING_EVENT_KEY, \u0026#34;critical\u0026#34;)); } catch (EventBusException | InterruptedException e) { e.printStackTrace(); } }; } A similar approach can be adopted also for the Digital Adapter with the small difference that the base class DigitalAdapter already allow the possibility to specify a configuration. For this reason in the previous example we extended DigitalAdapter\u0026lt;Void\u0026gt; avoiding to specifying a configuration.\nIn this updated version we can create a new DemoDigitalAdapterConfiguration class containing the parameter association to the emulation of the action and then update our adapter to support the new configuration. Our new configuration class will be:\npublic class DemoDigitalAdapterConfiguration { private int sleepTimeMs = GlobalKeywords.ACTION_SLEEP_TIME_MS; private int emulatedActionCount = GlobalKeywords.EMULATED_ACTION_COUNT; private double temperatureMinValue = GlobalKeywords.TEMPERATURE_MIN_VALUE; private double temperatureMaxValue = GlobalKeywords.TEMPERATURE_MAX_VALUE; public DemoDigitalAdapterConfiguration() { } public DemoDigitalAdapterConfiguration(int sleepTimeMs, int emulatedActionCount, double temperatureMinValue, double temperatureMaxValue) { this.sleepTimeMs = sleepTimeMs; this.emulatedActionCount = emulatedActionCount; this.temperatureMinValue = temperatureMinValue; this.temperatureMaxValue = temperatureMaxValue; } public int getSleepTimeMs() { return sleepTimeMs; } public void setSleepTimeMs(int sleepTimeMs) { this.sleepTimeMs = sleepTimeMs; } public int getEmulatedActionCount() { return emulatedActionCount; } public void setEmulatedActionCount(int emulatedActionCount) { this.emulatedActionCount = emulatedActionCount; } public double getTemperatureMinValue() { return temperatureMinValue; } public void setTemperatureMinValue(double temperatureMinValue) { this.temperatureMinValue = temperatureMinValue; } public double getTemperatureMaxValue() { return temperatureMaxValue; } public void setTemperatureMaxValue(double temperatureMaxValue) { this.temperatureMaxValue = temperatureMaxValue; } } After that we can update the declaration of our Digital Adapter and modify its constructor to accept the configuration. The resulting class will be:\npublic class DemoDigitalAdapter extends DigitalAdapter\u0026lt;DemoDigitalAdapterConfiguration\u0026gt; { public DemoDigitalAdapter(String id, DemoDigitalAdapterConfiguration configuration) { super(id, configuration); } [...] } Of course the possibility to have this configuration will allow us to improve the emulateIncomingDigitalAction method in the following way having access to the configuration through the method getConfiguration() or this.getConfiguration() directly on the adapter:\nprivate Runnable emulateIncomingDigitalAction(){ return () -\u0026gt; { try { System.out.println(\u0026#34;[DemoDigitalAdapter] -\u0026gt; Sleeping before Emulating Incoming Digital Action ...\u0026#34;); Thread.sleep(5000); Random random = new Random(); //Emulate the generation on \u0026#39;n\u0026#39; temperature measurements for(int i = 0; i \u0026lt; getConfiguration().getEmulatedActionCount(); i++){ //Sleep to emulate sensor measurement Thread.sleep(getConfiguration().getSleepTimeMs()); double randomTemperature = getConfiguration().getTemperatureMinValue() + (getConfiguration().getTemperatureMaxValue() - getConfiguration().getTemperatureMinValue()) * random.nextDouble(); publishDigitalActionWldtEvent(\u0026#34;set-temperature-action-key\u0026#34;, randomTemperature); } } catch (Exception e) { e.printStackTrace(); } }; } When we have updated both adapters making them configurable we can update our main function in the process that we have previouly device using the updated adapters and passing the configurations:\npublic class DemoDigitalTwin { public static void main(String[] args) { try{ WldtEngine digitalTwinEngine = new WldtEngine(new DemoShadowingFunction(\u0026#34;test-shadowing-function\u0026#34;), \u0026#34;test-digital-twin\u0026#34;); //Default Physical and Digital Adapter //digitalTwinEngine.addPhysicalAdapter(new DemoPhysicalAdapter(\u0026#34;test-physical-adapter\u0026#34;)); //digitalTwinEngine.addDigitalAdapter(new DemoDigitalAdapter(\u0026#34;test-digital-adapter\u0026#34;)); //Physical and Digital Adapters with Configuration digitalTwinEngine.addPhysicalAdapter(new DemoConfPhysicalAdapter(\u0026#34;test-physical-adapter\u0026#34;, new DemoPhysicalAdapterConfiguration())); digitalTwinEngine.addDigitalAdapter(new DemoConfDigitalAdapter(\u0026#34;test-digital-adapter\u0026#34;, new DemoDigitalAdapterConfiguration())); digitalTwinEngine.startLifeCycle(); }catch (Exception e){ e.printStackTrace(); } } } ","date":"February 9, 2024","id":11,"permalink":"/docs/guides/configurable-adapters/","summary":"The WLDT library provides a native method to define Configurable Physical ad Digital Adapters specifying a custom configuration class passed as parameter in the constructor.","tags":"","title":"Configurable Adapters"},{"content":"","date":"February 9, 2024","id":12,"permalink":"/docs/adapters/","summary":"","tags":"","title":"Adapters"},{"content":"The MqttPhysicalAdapter library provides a streamlined solution for efficiently managing physical assets through the MQTT protocol. It offers a range of features, including a versatile builder for effortless configuration of MQTT connections, dedicated classes for handling both incoming and outgoing topics, and a specialized adapter designed for seamless integration with diverse physical assets.\nKey Features:\nBuilder for MQTT Configuration: The library incorporates a flexible builder pattern, enabling users to effortlessly configure the essential parameters of the MQTT connection. This includes specifying the MQTT broker\u0026rsquo;s address, port, and other relevant details to establish a reliable and customizable communication link. Incoming and Outgoing Topic Handling: MqttPhysicalAdapter facilitates the handling of incoming and outgoing topics, crucial for communication between the physical assets and the MQTT broker. The library includes dedicated classes for defining and managing topics, allowing users to efficiently subscribe to incoming data and publish outgoing messages. Adapter for Physical Asset Integration: At the core of the library is a robust adapter designed specifically for integrating with various physical assets. This adapter streamlines the process of connecting and interacting with physical devices, ensuring a smooth and standardized approach to managing asset-related data. In the WLDT library, Physical Adapters has the responsibility to generate and publish the PhysicalAssetDescription (PAD) to describe the capabilities and the characteristics of our object allowing the Shadowing Function to decide how to digitalize its physical counterpart.\nIn the MqttPhysicalAdapter the generation of the PAD (Physical Asset Description) is automatically and internally executed by the adapter itself accordingly to the adapter configuration in terms of MQTT topics and their mapping with DT\u0026rsquo;s properties, events and actions.\nPrerequisites:\nExternal MQTT Broker: The MqttPhysicalAdapter library requires an external MQTT broker for optimal functionality. Users must have access to a reliable MQTT broker to which the adapter can subscribe. This external broker serves as the central communication hub, facilitating the exchange of messages between the adapter and the physical assets. A complete example is provided in the test folder with a complete DT Creation in the TestMain class together with MQTT IoT demo device and a test MQTT consumer.\nWLDT-Core Version Compatibility The correct mapping and compatibility between versions is reported in the following table\nmqtt-physical-adapter wldt-core 0.2.1 wldt-core 0.3.0 wldt-core 0.4.0 0.1.0 ✅ ❌ ❌ 0.1.1 ❌ ✅ ✅ Installation To use MqttPhysicalAdapter in your Java project, you can include it as a dependency using Maven or Gradle.\nMaven \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;io.github.wldt\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;mqtt-physical-adapter\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;0.1.1\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; Gradle implementation \u0026#39;io.github.wldt:mqtt-physical-adapter:0.1.1\u0026#39; Class Structure \u0026amp; Functionalities MqttPhysicalAdapterConfigurationBuilder \u0026amp; Main Methods The MqttPhysicalAdapterConfigurationBuilder is the class used to build the configuration used by the PhysicalAdater and described and implemented through the class MqttPhysicalAdapterConfiguration.\nIn order to create a configuration builder we can use the static method builder() on the MqttPhysicalAdapterConfiguration class:\nMqttPhysicalAdapterConfiguration.builder(); On the builder the available methods that can be used are:\naddPhysicalAssetPropertyAndTopic public \u0026lt;T\u0026gt; MqttPhysicalAdapterConfigurationBuilder addPhysicalAssetPropertyAndTopic(String propertyKey, T initialValue, String topic, Function\u0026lt;String, T\u0026gt; topicFunction) throws MqttPhysicalAdapterConfigurationException Adds a physical asset property and its corresponding MQTT topic to the configuration.\nType Parameter T: The type of the property. propertyKey: The key of the property. initialValue: The initial value of the property. topic: The MQTT topic associated with the property. topicFunction: A function to parse the MQTT topic payload into the property type. Returns: The updated MqttPhysicalAdapterConfigurationBuilder.\nThrows: MqttPhysicalAdapterConfigurationException - If there is a configuration error.\naddPhysicalAssetActionAndTopic public \u0026lt;T\u0026gt; MqttPhysicalAdapterConfigurationBuilder addPhysicalAssetActionAndTopic(String actionKey, String type, String contentType, String topic, Function\u0026lt;T, String\u0026gt; topicFunction) throws MqttPhysicalAdapterConfigurationException Adds a physical asset action and its corresponding MQTT topic to the configuration.\nType Parameter T: The type of the action payload. actionKey: The key of the action. type: The type of the action. contentType: The content type of the action. topic: The MQTT topic associated with the action. topicFunction: A function to convert the action payload into the MQTT topic payload. Returns: The updated MqttPhysicalAdapterConfigurationBuilder.\nThrows: MqttPhysicalAdapterConfigurationException - If there is a configuration error.\naddPhysicalAssetEventAndTopic public \u0026lt;T\u0026gt; MqttPhysicalAdapterConfigurationBuilder addPhysicalAssetEventAndTopic(String eventKey, String type, String topic, Function\u0026lt;String, T\u0026gt; topicFunction) throws MqttPhysicalAdapterConfigurationException Adds a physical asset event and its corresponding MQTT topic to the configuration.\nType Parameter T: The type of the event payload. eventKey: The key of the event. type: The type of the event. topic: The MQTT topic associated with the event. topicFunction: A function to parse the MQTT topic payload into the event payload type. Returns: The updated MqttPhysicalAdapterConfigurationBuilder.\nThrows: MqttPhysicalAdapterConfigurationException - If there is a configuration error.\naddIncomingTopic public MqttPhysicalAdapterConfigurationBuilder addIncomingTopic(DigitalTwinIncomingTopic topic, List\u0026lt;PhysicalAssetProperty\u0026lt;?\u0026gt;\u0026gt; properties, List\u0026lt;PhysicalAssetEvent\u0026gt; events) throws MqttPhysicalAdapterConfigurationException This method is used when multiple properties or events can be associated to a single MQTT topic on the physical device. It adds a DigitalTwinIncomingTopic describing the topic where the DT will receive the data along with its related lists of properties and events associated to that topic.\ntopic: The DigitalTwinIncomingTopic to be added. properties: The list of related physical asset properties. events: The list of related physical asset events. Returns: The updated MqttPhysicalAdapterConfigurationBuilder.\nThrows: MqttPhysicalAdapterConfigurationException - If there is a configuration error.\naddOutgoingTopic public MqttPhysicalAdapterConfigurationBuilder addOutgoingTopic(String actionKey, String type, String contentType, DigitalTwinOutgoingTopic topic) throws MqttPhysicalAdapterConfigurationException Adds a DigitalTwinOutgoingTopic to the configuration.\nactionKey: The key of the associated action. type: The type of the associated action. contentType: The content type of the associated action. topic: The DigitalTwinOutgoingTopic to be added. Returns: The updated MqttPhysicalAdapterConfigurationBuilder.\nThrows: MqttPhysicalAdapterConfigurationException - If there is a configuration error.\nAdditional Methods setConnectionTimeout(Integer connectionTimeout): Sets the connection timeout for the MQTT client. Returns the builder for method chaining. Throws MqttPhysicalAdapterConfigurationException if the provided timeout is invalid. setCleanSessionFlag(boolean cleanSession): Sets the clean session flag for the MQTT client. Returns the builder for method chaining. setAutomaticReconnectFlag(boolean automaticReconnect): Sets the automatic reconnect flag for the MQTT client. Returns the builder for method chaining. setMqttClientPersistence(MqttClientPersistence persistence): Sets the persistence for the MQTT client. Returns the builder for method chaining. Throws MqttPhysicalAdapterConfigurationException if the provided persistence is null. build(): Builds and returns the finalized MqttPhysicalAdapterConfiguration object. Throws MqttPhysicalAdapterConfigurationException if the configuration is incomplete or invalid. MqttPhysicalAdapter The MqttPhysicalAdapter class is the core component for interacting with physical assets. Instantiate it with a unique ID and the configuration. It extends the default WLDT Library PhysicalAdapter implementing all the functionalities to automatically interact with an MQTT physical device following the specifications and details provided in the MqttPhysicalAdapterConfiguration and built using MqttPhysicalAdapterConfigurationBuilder.\nAn example of its creation is:\nMqttPhysicalAdapter mqttPhysicalAdapter = new MqttPhysicalAdapter(\u0026#34;uniqueId\u0026#34;, configuration); Integrated Example This example demonstrates the integration of a MqttPhysicalAdapter within a Digital Twin setup, where multiple adapters (including a console adapter) are added to a Digital Twin, and the overall system is managed by a DigitalTwinEngine.\n// Create a Digital Twin with a default shadowing function DigitalTwin digitalTwin = new DigitalTwin(\u0026#34;mqtt-digital-twin\u0026#34;, new DefaultShadowingFunction()); // Create an instance of ConsoleDigitalAdapter ConsoleDigitalAdapter consoleDigitalAdapter = new ConsoleDigitalAdapter(); // Create an instance of MqttPhysicalAdapterConfiguration MqttPhysicalAdapterConfiguration config = MqttPhysicalAdapterConfiguration.builder(\u0026#34;127.0.0.1\u0026#34;, 1883) .addPhysicalAssetPropertyAndTopic(\u0026#34;intensity\u0026#34;, 0, \u0026#34;sensor/intensity\u0026#34;, Integer::parseInt) .addIncomingTopic(new DigitalTwinIncomingTopic(\u0026#34;sensor/state\u0026#34;, getSensorStateFunction()), createIncomingTopicRelatedPropertyList(), new ArrayList\u0026lt;\u0026gt;()) .addPhysicalAssetEventAndTopic(\u0026#34;overheating\u0026#34;, \u0026#34;text/plain\u0026#34;, \u0026#34;sensor/overheating\u0026#34;, Function.identity()) .addPhysicalAssetActionAndTopic(\u0026#34;switch-off\u0026#34;, \u0026#34;sensor.actuation\u0026#34;, \u0026#34;text/plain\u0026#34;, \u0026#34;sensor/actions/switch\u0026#34;, actionBody -\u0026gt; \u0026#34;switch\u0026#34; + actionBody) .build(); // Create an instance of the MQTT Physical Adapter using the defined configuration MqttPhysicalAdapter mqttPhysicalAdapter = new MqttPhysicalAdapter(\u0026#34;test-mqtt-pa\u0026#34;, config); // Add both Digital and Physical Adapters to the Digital Twin digitalTwin.addDigitalAdapter(consoleDigitalAdapter); digitalTwin.addPhysicalAdapter(mqttPhysicalAdapter); // Create the Digital Twin Engine DigitalTwinEngine digitalTwinEngine = new DigitalTwinEngine(); // Add the Digital Twin to the Engine digitalTwinEngine.addDigitalTwin(digitalTwin); // Start all the Digital Twins registered on the engine digitalTwinEngine.startAll(); In this example the createIncomingTopicRelatedPropertyList() used to map properties and events associated to a single topic is the following:\nprivate static List\u0026lt;PhysicalAssetProperty\u0026lt;?\u0026gt;\u0026gt; createIncomingTopicRelatedPropertyList(){ List\u0026lt;PhysicalAssetProperty\u0026lt;?\u0026gt;\u0026gt; properties = new ArrayList\u0026lt;\u0026gt;(); properties.add(new PhysicalAssetProperty\u0026lt;\u0026gt;(\u0026#34;temperature\u0026#34;, 0)); properties.add(new PhysicalAssetProperty\u0026lt;\u0026gt;(\u0026#34;humidity\u0026#34;, 0)); return properties; } This information are used by the adapter to build the PAD describe the capabilities and the characteristics of our object allowing the Shadowing Function to decide how to digitalize its physical counterpart.\n","date":"February 9, 2024","id":13,"permalink":"/docs/adapters/mqtt-physical-adapter/","summary":"The MqttPhysicalAdapter library provides a streamlined solution for efficiently managing physical assets through the MQTT protocol. It offers a range of features, including a versatile builder for effortless configuration of MQTT connections, dedicated classes for handling both incoming and outgoing topics, and a specialized adapter designed for seamless integration with diverse physical assets.","tags":"","title":"MQTT Physical Adapter"},{"content":"","date":"February 9, 2024","id":14,"permalink":"/docs/change-logs/","summary":"","tags":"","title":"Change Logs"},{"content":"The MqttDigitalAdapter,\nMqttDigitalAdapterConfiguration, and MqttDigitalAdapterConfigurationBuilder classes and guides you through using these classes to set up an MQTT Digital Adapter within WLDT.\nRequires an external MQTT broker to send messages.\nMain functionalities are:\nManages the interaction between the Digital Twin and external systems. Handles state updates, events, and property changes. Dynamic configuration of the MqttDigitalAdapter with broker details, topics, and other settings. Allows customization of data and payload management associated to MQTT topics for properties, events, and actions. Prerequisites:\nExternal MQTT Broker: The MqttDigitalAdapter library requires an external MQTT broker for optimal functionality and communication. Users must have access to a reliable MQTT broker to which the adapter can subscribe and publish. This external broker serves as the central communication hub, facilitating the exchange of messages between the adapter and digital applications A complete example is provided in the test folder with a complete DT Creation in the TestMain class together with MQTT IoT demo device and a test MQTT consumer.\nWLDT-Core Version Compatibility The correct mapping and compatibility between versions is reported in the following table\nmqtt-digital-adapter wldt-core 0.2.1 wldt-core 0.3.0 wldt-core 0.4.0 0.1.0 ✅ ❌ ❌ 0.1.1 ❌ ✅ ✅ Installation To use MqttDigitalAdapter in your Java project, you can include it as a dependency using Maven or Gradle.\nMaven \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;io.github.wldt\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;mqtt-digital-adapter\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;0.1.1\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; Gradle implementation \u0026#39;io.github.wldt:mqtt-digital-adapter:0.1.1\u0026#39; Class Structure \u0026amp; Functionalities MqttDigitalAdapterConfiguration MqttDigitalAdapterConfiguration is a crucial class in the Digital Twin library, allowing developers to configure the behavior of the MQTT Digital Adapter. It provides a flexible and customizable way to set up MQTT communication parameters, topics for properties, events, and actions.\nKey functionalities and exposed capabilities:\nBroker Configuration brokerAddress and brokerPort: Set the MQTT broker\u0026rsquo;s address and port. username and password: Set optional credentials for connecting to the broker. Client Configuration clientId: Unique identifier for the MQTT client. cleanSessionFlag: Flag indicating whether the client starts with a clean session. connectionTimeout: Maximum time to wait for the connection to the MQTT broker. MQTT Client Persistence persistence: Configurable persistence for the MQTT client\u0026rsquo;s data. Reconnect Configuration: automaticReconnectFlag: Flag enabling or disabling automatic reconnection. Topic Configuration: propertyUpdateTopics: Map of property update topics. eventNotificationTopics: Map of event notification topics. actionIncomingTopics: Map of incoming action topics. Builder Methods: builder: Static method to start building a new configuration. addPropertyTopic: Add a property topic with specified parameters. addEventNotificationTopic: Add an event notification topic. addActionTopic: Add an action topic. setConnectionTimeout: Set the connection timeout. setCleanSessionFlag: Set the clean session flag. setAutomaticReconnectFlag: Set the automatic reconnect flag. setMqttClientPersistence: Set the MQTT client persistence. build: Finalize the configuration and build the instance. MqttDigitalAdapterConfigurationBuilder The MqttDigitalAdapterConfigurationBuilder is a powerful tool designed to simplify the process of constructing configurations for the MQTT Digital Adapter in the Digital Twin library. It offers a fluent and intuitive interface, allowing developers to define various aspects of MQTT communication seamlessly.\nBuilder Instantiation The builder is instantiated by providing essential parameters like brokerAddress and brokerPort or including an optional clientId\nMqttDigitalAdapterConfigurationBuilder builder = MqttDigitalAdapterConfiguration.builder(\u0026#34;127.0.0.1\u0026#34;, 1883); Adding Property Topics Developers can add property topics with specific configurations, such as the key, topic, QoS level, and a function to convert property values to payload.\nbuilder.addPropertyTopic(\u0026#34;energy\u0026#34;, \u0026#34;dummy/properties/energy\u0026#34;, MqttQosLevel.MQTT_QOS_0, value -\u0026gt; String.valueOf(((Double)value).intValue())); Adding Event Notification Topics Event notification topics are easily added, including event keys, topics, QoS levels, and payload conversion functions.\nbuilder.addEventNotificationTopic(\u0026#34;overheating\u0026#34;, \u0026#34;dummy/events/overheating/notifications\u0026#34;, MqttQosLevel.MQTT_QOS_0, Object::toString); Adding Action Topics Developers can include action topics with key, topic, and a function to convert the payload to the desired action.\nbuilder.addActionTopic(\u0026#34;switch_off\u0026#34;, \u0026#34;app/actions/switch-off\u0026#34;, msg -\u0026gt; \u0026#34;OFF\u0026#34;); Connection Options Developers can set the connection timeout for the MQTT client.\nbuilder.setConnectionTimeout(30); The clean session flag can be configured based on the desired behavior.\nbuilder.setCleanSessionFlag(true); Developers can specify whether the MQTT client should automatically reconnect in case of a connection failure.\nbuilder.setAutomaticReconnectFlag(true); The builder allows setting a custom MQTT client persistence, such as an in-memory persistence or a file-based one.\nbuilder.setMqttClientPersistence(new MemoryPersistence()); Building Configuration The final configuration is built using the build method.\nMqttDigitalAdapterConfiguration configuration = builder.build(); MqttDigitalAdapter MqttDigitalAdapter extends DigitalAdapter and specializes in MQTT communication for Digital Twin instances. It handles the publication of state updates, events, and property changes over MQTT. This class facilitates seamless integration with MQTT-enabled systems.\nIt uses the information defined and provided in the `` to handle the communication both with the DT Core and external application interested to interact with the DT through the MQTT protocol.\nHere\u0026rsquo;s a basic example illustrating how to use MqttDigitalAdapter:\n// Create a Digital Twin instance DigitalTwin digitalTwin = new DigitalTwin(\u0026#34;my-digital-twin\u0026#34;, new DefaultShadowingFunction()); // Add a Physical Adapter to the DT [...] // Build the MQTT Digital Adapter Configuration MqttDigitalAdapterConfiguration configuration = MqttDigitalAdapterConfiguration.builder(\u0026#34;127.0.0.1\u0026#34;, 1883) .addPropertyTopic(\u0026#34;energy\u0026#34;, \u0026#34;dummy/properties/energy\u0026#34;, MqttQosLevel.MQTT_QOS_0, value -\u0026gt; String.valueOf(((Double)value).intValue())) .addActionTopic(\u0026#34;switch_off\u0026#34;, \u0026#34;app/actions/switch-off\u0026#34;, msg -\u0026gt; \u0026#34;OFF\u0026#34;) .build(); // Add the MQTT Digital Adapter to the Digital Twin digitalTwin.addDigitalAdapter(new MqttDigitalAdapter(\u0026#34;mqtt-da\u0026#34;, configuration)); // Create the Digital Twin Engine and start the simulation DigitalTwinEngine digitalTwinEngine = new DigitalTwinEngine(); digitalTwinEngine.addDigitalTwin(digitalTwin); digitalTwinEngine.startAll(); ","date":"February 9, 2024","id":15,"permalink":"/docs/adapters/mqtt-digital-adapter/","summary":"The MqttDigitalAdapter,\nMqttDigitalAdapterConfiguration, and MqttDigitalAdapterConfigurationBuilder classes and guides you through using these classes to set up an MQTT Digital Adapter within WLDT.","tags":"","title":"MQTT Digital Adapter"},{"content":"Digital Adapters The following methods have been discontinued and removed from the DigitalAdapter class: onStateChangePropertyCreated onStateChangePropertyUpdated onStateChangePropertyDeleted onStatePropertyUpdated onStatePropertyDeleted onStateChangeActionEnabled onStateChangeActionUpdated onStateChangeActionDisabled onStateChangeEventRegistered onStateChangeEventRegistrationUpdated onStateChangeEventUnregistered onStateChangeRelationshipInstanceDeleted onStateChangeRelationshipDeleted onStateChangeRelationshipInstanceCreated onStateChangeRelationshipCreated onDigitalTwinStateEventNotificationReceived The Signature of the following methods have been changed: onDigitalTwinSync(IDigitalTwinState currentDigitalTwinState) -\u0026gt; onDigitalTwinSync(DigitalTwinState currentDigitalTwinState) onDigitalTwinUnSync(IDigitalTwinState currentDigitalTwinState) -\u0026gt; onDigitalTwinUnSync(DigitalTwinState currentDigitalTwinState) New methods that have been added are: onStateUpdate(DigitalTwinState newDigitalTwinState, DigitalTwinState previousDigitalTwinState, ArrayList\u0026lt;DigitalTwinStateChange\u0026gt; digitalTwinStateChangeList) onEventNotificationReceived(DigitalTwinStateEventNotification\u0026lt;?\u0026gt; digitalTwinStateEventNotification) For additional details about Digital Adapters check Sub Section [[Change Log - v.0.3.0#Digital Adapter| Digital Adapters]] Shadowing Function ShadowingModelFunction is now ShadowingFunction this.digitalTwinState is not directly accessible anymore and it is wrapped through the DigitalTwinStateManager using the variable digitalTwinStateManager (see next descriptions and changes) The method addRelationshipInstance now take only one parameter that is the DigitalTwinStateRelationshipInstance The same change for example should be applied in the point of the code where the Shadowing Function receive a variation from the Physical world through a target adapter and the callback method onPhysicalAssetPropertyVariation(...) When the Shadowing Function has to compute the new DT State it can now work with the following method to handle DT State Transition: this.digitalTwinStateManager.startStateTransaction() DT State variation methods such as: digitalTwinStateManager.createProperty() digitalTwinStateManager.updateProperty() digitalTwinStateManager.updatePropertyValue() digitalTwinStateManager.deleteProperty() digitalTwinStateManager.enableAction() digitalTwinStateManager.updateAction() digitalTwinStateManager.disableAction() digitalTwinStateManager.registerEvent() digitalTwinStateManager.updateRegisteredEvent() digitalTwinStateManager.unRegisterEvent() digitalTwinStateManager.createRelationship() digitalTwinStateManager.addRelationshipInstance() digitalTwinStateManager.deleteRelationship() digitalTwinStateManager.deleteRelationshipInstance() At the end the transaction can be committed using the method: digitalTwinStateManager.commitStateTransaction() The method notifyDigitalTwinStateEvent is now available through the digitalTwinStateManager Additional Details associated to Shadowing Function Migration can be found in the dedicated section [[Change Log - v.0.3.0#Shadowing Function Changes | Shadowing Function Changes]] WLDT Engine \u0026amp; DT Creation WldtEngine is now DigitalTwin and model and structure a single Digital Twin and takes the following parameters: String digitalTwinId ShadowingFunction shadowingFunction The startLifeCycle has been removed from the DigitalTwin (previously WLDT Engine) and now DigitalTwinEngine should be used to start twins Once a new Digital Twin has been create it has to be added to the DigitalTwinEngine DigitalTwinEngine has dedicated method to start and stop twins such as: startAll() startDigitalTwin(\u0026lt;DIGITAL_TWIN_ID\u0026gt;); stopAll() digitalTwinEngine.stopDigitalTwin(\u0026lt;DIGITAL_TWIN_ID\u0026gt;); Digital Twin \u0026amp; Digital Twin Engine With the following code we now create a new Digital Twin Instance\n// Create the new Digital Twin with its Shadowing Function DigitalTwin digitalTwin = new DigitalTwin(digitalTwinId, new DemoShadowingFunction()); // Physical Adapter with Configuration digitalTwin.addPhysicalAdapter( new DemoPhysicalAdapter( String.format(\u0026#34;%s-%s\u0026#34;, digitalTwinId, \u0026#34;test-physical-adapter\u0026#34;), new DemoPhysicalAdapterConfiguration(), true)); // Digital Adapter with Configuration digitalTwin.addDigitalAdapter( new DemoDigitalAdapter( String.format(\u0026#34;%s-%s\u0026#34;, digitalTwinId, \u0026#34;test-digital-adapter\u0026#34;), new DemoDigitalAdapterConfiguration()) ); In the new version the DT cannot be directly run but it should be added to the DigitalTwinEngine in order to be executed through the WLDT Library\n// Create the Digital Twin Engine DigitalTwinEngine digitalTwinEngine = new DigitalTwinEngine(); // Add the Digital Twin to the Engine digitalTwinEngine.addDigitalTwin(digitalTwin); In order to start a DT from the Engine you can:\n// Directly start when you add it passing a second boolean value = true digitalTwinEngine.addDigitalTwin(digitalTwin. true); // Starting the single DT on the engine through its id digitalTwinEngine.startDigitalTwin(DIGITAL_TWIN_ID); // Start all the DTs registered on the engine digitalTwinEngine.startAll(); To stop a single twin or all the twin registered on the engine:\n// Stop a single DT on the engine through its id digitalTwinEngine.stopDigitalTwin(DIGITAL_TWIN_ID); // Stop all the DTs registered on the engine digitalTwinEngine.stopAll(); It is also possible to remove a DT from the Engine with a consequent stop if it is active and the deletion of its reference from the engine:\n// Remove a single DT on the engine through its id digitalTwinEngine.removeDigitalTwin(DIGITAL_TWIN_ID); // Remove all the DTs registered on the engine digitalTwinEngine.removeAll(); Digital Twin State DT State now has the reference timestamp representing the evaluation instant of the digital twin state, this timestamp is computed through the DigitalTwinStateManager and cannot manually set by the developer The information available on the DT State are: properties: List of Properties with their values (if available) actions: List of Actions that can be called on the DT events: List of Events that can be generated by the DT relationships: List of Relationships and their instances (if available) evaluationInstant: The timestamp representing the evaluation instant of the DT state Available main methods on that class instance are: Properties:\n- getProperty(String propertyKey): Retrieves if present the target DigitalTwinStateProperty by Key\n- containsProperty(String propertyKey): Checks if a target Property Key is already available in the current Digital Twin\u0026rsquo;s State\n- getPropertyList(): Loads the list of available Properties (described by the class DigitalTwinStateProperty) available on the Digital Twin\u0026rsquo;s State\n- createProperty(DigitalTwinStateProperty\u0026lt;?\u0026gt; dtStateProperty): Allows the creation of a new Property on the Digital Twin\u0026rsquo;s State through the class DigitalTwinStateProperty\n- readProperty(String propertyKey): Retrieves if present the target DigitalTwinStateProperty by Key\n- updateProperty(DigitalTwinStateProperty\u0026lt;?\u0026gt; dtStateProperty): Updates the target property using the DigitalTwinStateProperty and the associated Property Key field\n- deleteProperty(String propertyKey): Deletes the target property identified by the specified key Actions:\n- containsAction(String actionKey): Checks if a Digital Twin State Action with the specified key is correctly registered\n- getAction(String actionKey): Loads the target DigitalTwinStateAction by key\n- getActionList(): Gets the list of available Actions registered on the Digital Twin\u0026rsquo;s State\n- enableAction(DigitalTwinStateAction digitalTwinStateAction): Enables and registers the target Action described through an instance of the DigitalTwinStateAction class\n- updateAction(DigitalTwinStateAction digitalTwinStateAction): Update the already registered target Action described through an instance of the DigitalTwinStateAction class\n- disableAction(String actionKey): Disables and unregisters the target Action described through an instance of the DigitalTwinStateAction class Events:\n- containsEvent(String eventKey): Check if a Digital Twin State Event with the specified key is correctly registered\n- getEvent(String eventKey): Return the description of a registered Digital Twin State Event according to its Key\n- getEventList(): Return the list of existing and registered Digital Twin State Events\n- registerEvent(DigitalTwinStateEvent digitalTwinStateEvent): Register a new Digital Twin State Event\n- updateRegisteredEvent(DigitalTwinStateEvent digitalTwinStateEvent): Update the registration and signature of an existing Digital Twin State Event\n- unRegisterEvent(String eventKey): Un-register a Digital Twin State Event\n- notifyDigitalTwinStateEvent(DigitalTwinStateEventNotification\u0026lt;?\u0026gt; digitalTwinStateEventNotification): Method to notify the occurrence of the target Digital Twin State Event Relationships:\n- containsRelationship(String relationshipName): Checks if a Relationship Name is already available in the current Digital Twin\u0026rsquo;s State\n- createRelationship(DigitalTwinStateRelationship\u0026lt;?\u0026gt; relationship): Creates a new Relationships (described by the class DigitalTwinStateRelationship) in the Digital Twin\u0026rsquo;s State\n- addRelationshipInstance(String name, DigitalTwinStateRelationshipInstance\u0026lt;?\u0026gt; instance): Adds a new Relationship instance described through the class DigitalTwinStateRelationshipInstance and identified through its name\n- getRelationshipList(): Loads the list of existing relationships on the Digital Twin\u0026rsquo;s State through a list of DigitalTwinStateRelationship\n- getRelationship(String name): Gets a target Relationship identified through its name and described through the class DigitalTwinStateRelationship\n- deleteRelationship(String name): Deletes a target Relationship identified through its name\n- deleteRelationshipInstance(String relationshipName, String instanceKey): Deletes the target Relationship Instance using relationship name and instance Key Digital Twin State Manager The DigitalTwinStateManager is a Java class that serves as the default implementation of the IDigitalTwinStateManager interface within the White Label Digital Twin Java Framework (whitelabel-digitaltwin). This class allows developers to manage the state of a digital twin, including properties, actions, events, and relationships.\nFeatures State Management: Handles the creation, update, and deletion of properties, actions, events, and relationships associated with the digital twin state. Transaction Support: Allows developers to start, commit, or rollback transactions to manage changes to the digital twin state. Event Notification: Notifies listeners about updates to the digital twin state through the WLDT event bus. When the Shadowing Function has to compute the new DT State it can now work with the following method to handle DT State Transition: - Start the DT State Transaction: startStateTransaction() - DT State variation methods such as: - createProperty() - updateProperty()\n- updatePropertyValue() - deleteProperty() - enableAction() - updateAction() - disableAction() - registerEvent() - updateRegisteredEvent() - unRegisterEvent() - createRelationship() - addRelationshipInstance() - deleteRelationship() - deleteRelationshipInstance()\nAt the end the transaction can be committed using the method: commitStateTransaction()\nUsage To use the DigitalTwinStateManager within your digital twin implementation:\nInitialization: Create an instance of the DigitalTwinStateManager. DigitalTwinStateManager digitalTwinStateManager = new DigitalTwinStateManager(); State Transaction:\nStart a new transaction using startStateTransaction() to manage changes. Make changes to the digital twin state. Commit the transaction using commitStateTransaction() to apply the changes. digitalTwinStateManager.startStateTransaction(); // Make changes to properties, actions, events, or relationships // [...] digitalTwinStateManager.commitStateTransaction(); Event Notification:\nDT State Updates after a commit action are automatically notified to Digital Adapter by the Manager Once an event incoming from the physical or generated by the DT itself is handled by the Shadowing Function, the developer can use notifyDigitalTwinStateUpdate to notify Digital Adapter listening about events variations. // Notify a specific event notification digitalTwinStateManager.notifyDigitalTwinStateEvent(digitalTwinStateEventNotification); Property, Action, Event, Relationship Management:\nCreate, update, or delete properties, actions, events, or relationships as needed. // Begin Digital Twin State Transaction digitalTwinStateManager.startStateTransaction(); // Create a new property digitalTwinStateManager.createProperty(dtStateProperty); // Update an existing property digitalTwinStateManager.updateProperty(dtStateProperty); // Delete a property digitalTwinStateManager.deleteProperty(propertyKey);` // Commit DT State Update to apply all the changes and notify the Digital Adapters and other listeners about the variation digitalTwinStateManager.commitStateTransaction(); Exception Handling The class throws WldtDigitalTwinStateException to indicate errors related to digital twin state management. Proper exception handling is advised to manage potential errors during state transactions. Shadowing Function Changes Now that the DT State is managed through the DigitalTwinStateManager class all the changes and variation should be applied on the DT ShadowingFunction using the previously presented transaction management and the correct call of methods startStateTransaction() and commitStateTransaction().\nHere there is an example of the change with a simple and demo shadowing function on callback onDigitalTwinBound:\n@Override protected void onDigitalTwinBound(Map\u0026lt;String, PhysicalAssetDescription\u0026gt; adaptersPhysicalAssetDescriptionMap) { try{ // NEW -\u0026gt; Start DT State Change Transaction this.digitalTwinStateManager.startStateTransaction(); for(Map.Entry\u0026lt;String, PhysicalAssetDescription\u0026gt; entry : adaptersPhysicalAssetDescriptionMap.entrySet()){ String adapterId = entry.getKey(); PhysicalAssetDescription physicalAssetDescription = entry.getValue(); //In that simple case the Digital Twin shadow all the properties and actions available in the physical asset for(PhysicalAssetProperty\u0026lt;?\u0026gt; p : physicalAssetDescription.getProperties()) this.digitalTwinStateManager.createProperty(new DigitalTwinStateProperty\u0026lt;\u0026gt;(p.getKey(), p.getInitialValue())); for(PhysicalAssetAction a : physicalAssetDescription.getActions()) this.digitalTwinStateManager.enableAction(new DigitalTwinStateAction(a.getKey(), a.getType(), a.getContentType())); for(PhysicalAssetEvent e: physicalAssetDescription.getEvents()) this.digitalTwinStateManager.registerEvent(new DigitalTwinStateEvent(e.getKey(), physicalAssetEvent.getType())); } // NEW -\u0026gt; Commit DT State Change Transaction to apply the changes on the DT State and notify about the change this.digitalTwinStateManager.commitStateTransaction(); //Observer Target Physical Properties for(Map.Entry\u0026lt;String, PhysicalAssetDescription\u0026gt; entry : adaptersPhysicalAssetDescriptionMap.entrySet()){ [...] } //Observe all the target available Physical Asset Events for each Adapter for(Map.Entry\u0026lt;String, PhysicalAssetDescription\u0026gt; entry : adaptersPhysicalAssetDescriptionMap.entrySet()){ [...] } // Observer for Incoming Digital Actions observeDigitalActionEvents(); //Notify Shadowing Completed notifyShadowingSync(); }catch (Exception e){ e.printStackTrace(); } } The same change for example should be applied in the point of the code where the Shadowing Function receive a variation from the Physical world through a target adapter and the callback method onPhysicalAssetPropertyVariation(...)\n@Override protected void onPhysicalAssetPropertyVariation(PhysicalAssetPropertyWldtEvent\u0026lt;?\u0026gt; physicalPropertyEventMessage) { try { if(physicalPropertyEventMessage != null \u0026amp;\u0026amp; getPhysicalEventsFilter().contains(physicalPropertyEventMessage.getType())){ if(physicalPropertyEventMessage.getPhysicalPropertyId().equals(TestPhysicalAdapter.SWITCH_PROPERTY_KEY) \u0026amp;\u0026amp; physicalPropertyEventMessage.getBody() instanceof String){ [...] } else{ //Update Digital Twin State //NEW -\u0026gt; Start State Transaction this.digitalTwinStateManager.startStateTransaction(); // Update State Property Value this.digitalTwinStateManager.updateProperty( new DigitalTwinStateProperty\u0026lt;\u0026gt;( physicalPropertyEventMessage.getPhysicalPropertyId(), physicalPropertyEventMessage.getBody())); //NEW -\u0026gt; Commit State Transaction this.digitalTwinStateManager.commitStateTransaction(); } } else logger.error(\u0026#34;WRONG Physical Event Message Received !\u0026#34;); }catch (Exception e){ e.printStackTrace(); } } Digital Adapter The Digital Adapter base class has been significantly extended and improved with respect to the previous version. In this new Version notifications that are received by the Adapter from the the DT core belongs to the following categories:\nDigital Twin State Update through the method onStateUpdate(...) providing information about the new state of the Digital Twin, the previous state, and a list of changes that occurred between these two states. In the previous version each variation of a property, relationships, actions or events were notified. In the new version only a committed DT\u0026rsquo;State variation is notified to listeners. Event Notifications through the method onEventNotificationReceived(...) whenever there is a notification about an event related to the Digital Twin\u0026rsquo;s state coming from the physical world, generated by the twin and processed by the Shadowing Function. For example in the DT State we can have the declaration of the over-heating-alert structured and received in the DT State while the effective occurrence of the event and the associated notification is notified through this dedicated callback The onStateUpdate method is an abstract method that must be implemented by any class extending the DigitalAdapter class. This method is called whenever there is an update to the Digital Twin\u0026rsquo;s state. It provides information about the new state of the Digital Twin, the previous state, and a list of changes that occurred between these two states.\nHere is an explanation of the parameters:\nnewDigitalTwinState: This parameter represents the updated state of the Digital Twin. It is an instance of the DigitalTwinState class, which encapsulates the current state information.\npreviousDigitalTwinState: This parameter represents the state of the Digital Twin before the update. It is also an instance of the DigitalTwinState class.\ndigitalTwinStateChangeList: This parameter is an ArrayList containing DigitalTwinStateChange objects. Each DigitalTwinStateChange object encapsulates information about a specific change that occurred between the previous and new states. It includes details such as the property or aspect of the state that changed, the previous value, and the new value.\nThe DT State is automatically monitored by each Digital Adapter while for the Events potentially generated by the DT can be observed by each adapter using:\nobserveAllDigitalTwinEventsNotifications: Enable the observation of available Digital Twin State Events Notifications. unObserveAllDigitalTwinEventsNotifications: Cancel the observation of Digital Twin State Events Notifications observeDigitalTwinEventsNotifications: Enable the observation of the notification associated to a specific list of Digital Twin State events. With respect to event a notification contains the new associated value unObserveDigitalTwinEventsNotifications: Cancel the observation of a target list of properties observeDigitalTwinEventNotification: Enable the observation of the notification associated to a single Digital Twin State event. With respect to event a notification contains the new associated value unObserveDigitalTwinEventNotification: Cancel the observation of a single target event DigitalTwinStateChange Class DigitalTwinStateChange Class\nThe DigitalTwinStateChange class is a representation of a change that occurred in the state of a Digital Twin. It encapsulates information about the type of operation, the resource type, and the affected resource within the Digital Twin.\nEnums:\nOperation: Enumerates different types of operations that can be performed on a Digital Twin state. The possible operations are: OPERATION_UPDATE: Represents an update operation on a resource. OPERATION_UPDATE_VALUE: Represents an update operation specifically on the value of a resource. OPERATION_ADD: Represents an addition operation of a new resource. OPERATION_REMOVE: Represents a removal operation of an existing resource. ResourceType: Enumerates different types of resources within a Digital Twin. The possible resource types are: PROPERTY: Represents a property of the Digital Twin. PROPERTY_VALUE: Represents the value of a property within the Digital Twin. EVENT: Represents an event associated with the Digital Twin. ACTION: Represents an action that can be performed on the Digital Twin. RELATIONSHIP: Represents a relationship between different components of the Digital Twin. RELATIONSHIP_INSTANCE: Represents an instance of a relationship. Fields:\noperation: Indicates the type of operation performed on the Digital Twin state (e.g., update, add, remove). resourceType: Represents the type of resource affected by the change (e.g., property, event, relationship). resource: The specific resource that has undergone the change, represented by an instance of the DigitalTwinStateResource class. Available type of DigitalTwinStateResource are:\nDigitalTwinStateProperty\u0026lt;T\u0026gt;: This class define a generic property associated to the Digital Twin State. Each property is associated to a Key and a Value. Furthermore, it can also be associated to a type to identify its nature and data structure. By default, it is associated to the type of the Class (e.g., java.lang.String) but it can be directly changed by the developer to associate it to a specific ontology or data type. DigitalTwinStateEvent: This class define a generic event associated to the Digital Twin State. Events enable a mechanism for asynchronous messages to be sent by the digital twin (e.g., an overheating) . They are different from Properties that can change values according to the type of Digital Twin and may be associated also to telemetry patterns. Each event is associated to a Key and a Type used to identify its nature and data structure. By default, it is associated to the type of the Class (e.g., java.lang.String) but it can be directly changed by the developer to associate it to a specific ontology or data type. DigitalTwinStateAction: This class define a generic action associated to the Digital Twin State. Each action is by a key, an action type and a content type used to identify the expected input required by the action. The type of the can be directly changed by the developer to associate it to a specific ontology or data type. DigitalTwinStateRelationship\u0026lt;T\u0026gt;: Structures and describes a Relationship in the Digital Twins\u0026rsquo;s State. This is just the description of the relationships while the effective values/instances are described through the other class DigitalTwinStateRelationshipInstance DigitalTwinStateRelationshipInstance\u0026lt;T\u0026gt;: Structures and describes a Relationship Instance in the Digital Twins\u0026rsquo;s State. This is effective description of a relationship while its generic declaration is described through the class DigitalTwinStateRelationship. When there is a change in the DT State it is possibile to cast the received resource variation to the correct one. For example in the following code we detect and manage the variation on a Property Value:\n// Get information from the state change DigitalTwinStateChange.Operation operation = stateChange.getOperation(); DigitalTwinStateChange.ResourceType resourceType = stateChange.getResourceType(); DigitalTwinStateResource resource = stateChange.getResource(); // Search for property value variation if(resourceType.equals(DigitalTwinStateChange.ResourceType.PROPERTY_VALUE) \u0026amp;\u0026amp; operation.equals(DigitalTwinStateChange.Operation.OPERATION_UPDATE) \u0026amp;\u0026amp; resource instanceof DigitalTwinStateProperty){ DigitalTwinStateProperty\u0026lt;?\u0026gt; digitalTwinStateProperty = (DigitalTwinStateProperty\u0026lt;?\u0026gt;) resource; if(getConfiguration().getPropertyUpdateTopics().containsKey(digitalTwinStateProperty.getKey())){ //Handle property value variation } } Constructors:\nDigitalTwinStateChange(): An empty constructor that allows creating an instance of the class. DigitalTwinStateChange(Operation operation, ResourceType resourceType, DigitalTwinStateResource resource): Constructs a DigitalTwinStateChange object with specified operation, resource type, and resource. Throws a WldtDigitalTwinStateException if any of the parameters is missing or null. Methods:\nAccessor methods (getOperation(), getResourceType(), getResource()) to retrieve the values of the fields. Mutator methods (setOperation(), setResourceType(), setResource()) to update the values of the fields. Usage Examples Developers extending the DigitalAdapter class should implement the onStateUpdate method to define custom logic that needs to be executed whenever the state of the Digital Twin is updated. This could include tasks such as processing state changes, updating internal variables, triggering specific actions, or notifying other components about the state update.\nHere\u0026rsquo;s an example of how the method might be implemented in a concrete subclass of DigitalAdapter:\n@Override protected void onStateUpdate(DigitalTwinState newDigitalTwinState, DigitalTwinState previousDigitalTwinState, ArrayList\u0026lt;DigitalTwinStateChange\u0026gt; digitalTwinStateChangeList) { // In newDigitalTwinState we have the new DT State System.out.println(\u0026#34;New DT State is: \u0026#34; + newDigitalTwinState); // The previous DT State is available through the variable previousDigitalTwinState System.out.println(\u0026#34;Previous DT State is: \u0026#34; + previousDigitalTwinState); // We can also check each DT\u0026#39;s state change potentially differentiating the behaviour for each change if (digitalTwinStateChangeList != null \u0026amp;\u0026amp; !digitalTwinStateChangeList.isEmpty()) { // Iterate through each state change in the list for (DigitalTwinStateChange stateChange : digitalTwinStateChangeList) { // Get information from the state change DigitalTwinStateChange.Operation operation = stateChange.getOperation(); DigitalTwinStateChange.ResourceType resourceType = stateChange.getResourceType(); DigitalTwinStateResource resource = stateChange.getResource(); // Perform different actions based on the type of operation switch (operation) { case OPERATION_UPDATE: // Handle an update operation System.out.println(\u0026#34;Update operation on \u0026#34; + resourceType + \u0026#34;: \u0026#34; + resource); break; case OPERATION_UPDATE_VALUE: // Handle an update value operation System.out.println(\u0026#34;Update value operation on \u0026#34; + resourceType + \u0026#34;: \u0026#34; + resource); break; case OPERATION_ADD: // Handle an add operation System.out.println(\u0026#34;Add operation on \u0026#34; + resourceType + \u0026#34;: \u0026#34; + resource); break; case OPERATION_REMOVE: // Handle a remove operation System.out.println(\u0026#34;Remove operation on \u0026#34; + resourceType + \u0026#34;: \u0026#34; + resource); break; default: // Handle unknown operation (optional) System.out.println(\u0026#34;Unknown operation on \u0026#34; + resourceType + \u0026#34;: \u0026#34; + resource); break; } } } else { // No state changes System.out.println(\u0026#34;No state changes detected.\u0026#34;); } } In this example, the method iterates over the list of state changes, extracts information about each change, and performs custom actions based on the changes. Developers can adapt this method to suit the specific requirements of their Digital Twin application.\n","date":"February 9, 2024","id":16,"permalink":"/docs/change-logs/change-log-0.3.0/","summary":"Digital Adapters The following methods have been discontinued and removed from the DigitalAdapter class: onStateChangePropertyCreated onStateChangePropertyUpdated onStateChangePropertyDeleted onStatePropertyUpdated onStatePropertyDeleted onStateChangeActionEnabled onStateChangeActionUpdated onStateChangeActionDisabled onStateChangeEventRegistered onStateChangeEventRegistrationUpdated onStateChangeEventUnregistered onStateChangeRelationshipInstanceDeleted onStateChangeRelationshipDeleted onStateChangeRelationshipInstanceCreated onStateChangeRelationshipCreated onDigitalTwinStateEventNotificationReceived The Signature of the following methods have been changed: onDigitalTwinSync(IDigitalTwinState currentDigitalTwinState) -\u0026gt; onDigitalTwinSync(DigitalTwinState currentDigitalTwinState) onDigitalTwinUnSync(IDigitalTwinState currentDigitalTwinState) -\u0026gt; onDigitalTwinUnSync(DigitalTwinState currentDigitalTwinState) New methods that have been added are: onStateUpdate(DigitalTwinState newDigitalTwinState, DigitalTwinState previousDigitalTwinState, ArrayList\u0026lt;DigitalTwinStateChange\u0026gt; digitalTwinStateChangeList) onEventNotificationReceived(DigitalTwinStateEventNotification\u0026lt;?","tags":"","title":"Change Log 0.3.0"},{"content":"The HttpDigitalAdapter is a powerful component designed to facilitate the integration of Digital Twins into HTTP-based systems. It serves as a bridge between a Digital Twin and HTTP-based applications, allowing developers to easily expose and interact with Digital Twin data and functionalities over HTTP.\nKey Features:\nHTTP Integration: Seamlessly integrates Digital Twins into HTTP environments, enabling communication with web applications and services. Dynamic Configuration: Offers a flexible configuration mechanism through the HttpDigitalAdapterConfiguration, allowing developers to customize the adapter\u0026rsquo;s behavior based on specific requirements. State Monitoring: Monitors changes in the Digital Twin state and provides HTTP endpoints to query the state of the Digital Twin (properties, events, actions and relationships). Event Notifications: Allows developers to retrieve event notifications triggered by changes in the Digital Twin state. Storage \u0026amp; Query: Since version 0.2 the HTTP Digital Adapter is able to retrieve Storage Statistics and execute query on the target DT A complete example is provided in the test folder with a complete DT Creation in the TestMain class together with a demo DT with and emulated Physical Adapter and the HTTP Digital Adapter.\nWLDT-Core Version Compatibility The correct mapping and compatibility between versions is reported in the following table\nhttp-digital-adapter wldt-core 0.2.1 wldt-core 0.3.0 wldt-core 0.4.0 0.1.1 ❌ ✅ ✅ 0.2 ❌ ❌ ✅ Installation To use HttpDigitalAdapter in your Java project, you can include it as a dependency using Maven or Gradle.\nMaven \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;io.github.wldt\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;http-digital-adapter\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;0.2\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; Gradle implementation \u0026#39;io.github.wldt:http-digital-adapter:0.2\u0026#39; Class Structure \u0026amp; Functionalities HttpDigitalAdapterConfiguration The HttpDigitalAdapterConfiguration is a crucial part of the HttpDigitalAdapter, providing the necessary settings to tailor the adapter\u0026rsquo;s behavior to meet specific needs.\nRepresents the configuration for an HTTP Digital Adapter, specifying the host, port, and filters for properties, actions, events, and relationships.\nThe filters are used to selectively include or exclude specific properties, actions, events, and relationships when interacting with the HTTP Digital Adapter. Filters are meant to be white list filters, if they are empty, it means that ALL fields are considered\nThis class provides methods to add filters for each type and getters to retrieve the configured values.\nKey functionalities and exposed capabilities:\nBasic Configuration Adapter ID: A unique identifier for the HttpDigitalAdapter instance. Host: The hostname or IP address on which the adapter will listen for incoming HTTP requests. Port: The port number on which the adapter will listen for incoming HTTP requests. Filters (Optional) addPropertyFilter(String propertyKey): Adds a single property key to the property filter. addPropertiesFilter(Collection\u0026lt;String\u0026gt; propertiesKey): Adds a collection of property keys to the property filter. addActionFilter(String actionKey): Adds a single action key to the action filter. addActionsFilter(Collection\u0026lt;String\u0026gt; actionsKey): Adds a collection of action keys to the action filter. addEventFilter(String eventKey): Adds a single event key to the event filter. addEventsFilter(Collection\u0026lt;String\u0026gt; eventsKey): Adds a collection of event keys to the event filter. addRelationshipFilter(String relationshipName): Adds a single relationship name to the relationship filter. addRelationshipsFilter(Collection\u0026lt;String\u0026gt; relationshipNames): Adds a collection of relationship names to the relationship filter. Configured Filter can be accessed using: getPropertyFilter() getActionFilter() getEventFilter() getRelationshipFilter() A basic example without any filter that accesses and uses the entire DT State is:\nHttpDigitalAdapterConfiguration config = new HttpDigitalAdapterConfiguration(\u0026#34;my-http-adapter\u0026#34;, \u0026#34;localhost\u0026#34;, 8080); An example of using filter to select specific field of interest can be structured ad follows:\nHttpDigitalAdapterConfiguration config = new HttpDigitalAdapterConfiguration(\u0026#34;my-http-adapter\u0026#34;, \u0026#34;localhost\u0026#34;, 8080); // Add property filter config.addPropertyFilter(\u0026#34;temperature\u0026#34;); config.addPropertiesFilter(Arrays.asList(\u0026#34;humidity\u0026#34;, \u0026#34;pressure\u0026#34;)); // Add action filter config.addActionFilter(\u0026#34;start\u0026#34;); config.addActionsFilter(Arrays.asList(\u0026#34;stop\u0026#34;, \u0026#34;reset\u0026#34;)); // Add event filter config.addEventFilter(\u0026#34;temperatureChange\u0026#34;); config.addEventsFilter(Collections.singletonList(\u0026#34;pressureChange\u0026#34;)); // Add relationship filter config.addRelationshipFilter(\u0026#34;connectedTo\u0026#34;); config.addRelationshipsFilter(Arrays.asList(\u0026#34;parentOf\u0026#34;, \u0026#34;siblingOf\u0026#34;)); // Retrieve and display filters List\u0026lt;String\u0026gt; propertyFilter = config.getPropertyFilter(); List\u0026lt;String\u0026gt; actionFilter = config.getActionFilter(); List\u0026lt;String\u0026gt; eventFilter = config.getEventFilter(); List\u0026lt;String\u0026gt; relationshipFilter = config.getRelationshipFilter(); System.out.println(\u0026#34;Property Filter: \u0026#34; + propertyFilter); System.out.println(\u0026#34;Action Filter: \u0026#34; + actionFilter); System.out.println(\u0026#34;Event Filter: \u0026#34; + eventFilter); System.out.println(\u0026#34;Relationship Filter: \u0026#34; + relationshipFilter); HttpDigitalAdapter The HttpDigitalAdapter itself is the core component responsible for handling HTTP requests and interacting with the underlying Digital Twin. It extends the capabilities of the base DigitalAdapter to specifically cater to HTTP-based scenarios.\nKey Functionalities:\nRESTful Endpoints: Provides RESTful endpoints for reading properties, invoking actions, querying events, and managing relationships. State Updates: Automatically reflects changes in the Digital Twin state to the HTTP endpoints, ensuring real-time information. Event Handling: Listens for Digital Twin events and provides events notifications to HTTP clients. Here\u0026rsquo;s a basic example illustrating how to use MqttDigitalAdapter:\nGetting Started: Create HttpDigitalAdapterConfiguration:\nHttpDigitalAdapterConfiguration config = new HttpDigitalAdapterConfiguration(\u0026#34;my-http-adapter\u0026#34;, \u0026#34;localhost\u0026#34;, 8080); Instantiate HttpDigitalAdapter:\nDigitalTwin digitalTwin = new DigitalTwin(\u0026#34;my-digital-twin\u0026#34;, new DefaultShadowingFunction()); HttpDigitalAdapter httpDigitalAdapter = new HttpDigitalAdapter(config, digitalTwin); // Add a Physical Adapter to the DT [...] Add HttpDigitalAdapter to DigitalTwin:\ndigitalTwin.addDigitalAdapter(httpDigitalAdapter); Start the Digital Twin Engine:\nDigitalTwinEngine digitalTwinEngine = new DigitalTwinEngine(); digitalTwinEngine.addDigitalTwin(digitalTwin); digitalTwinEngine.startAll(); HTTP RESTful API This section of the documentation provides detailed information about the RESTful API exposed by the WLDT - HTTP Digital Adapter. The API allows you to interact with the Digital Twin (DT) instance, retrieve its state, read properties, actions, event and relationships description, and trigger actions.\nAvailable endpoints with the associated methods are:\nGET /instance: Retrieves information about the Digital Twin instance. GET /state: Retrieves the current state of the Digital Twin. GET /state/changes: Retrieves the list of state changes in the Digital Twin. GET /state/previous: Retrieves the previous state of the Digital Twin. GET /state/properties: Retrieves the list of properties in the Digital Twin state. GET /properties/{propertyKey}: Retrieves the value of a specific property (e.g., /properties/color) from the Digital Twin state. GET /state/events: Retrieves the list of events in the Digital Twin state. GET /state/actions: Retrieves the list of actions in the Digital Twin state. POST /state/actions/{actionKey}: Triggers the specified action (e.g., /state/actions/switch_on) in the Digital Twin state. The raw body contains the action request payload. GET /state/relationships: Retrieves the list of relationships in the Digital Twin state. GET /state/relationships/{relationshipName}/instances: Retrieves the instances of the specified relationship (e.g., /state/relationships/insideIn/instances) in the Digital Twin state. GET /storage: Retrieves Storage Statistics from the target Digital Twin POST /storage/query: Allows the execution of a query, where the query structure is specified through a JSON Message in the request Body. For additional information about the Query System see Query System Page Note: Replace {propertyKey}, {actionKey}, and {relationshipName} with the actual values you want to retrieve or trigger. Make sure to use the appropriate HTTP method (GET, POST) and include any required parameters or payload as described in each endpoint\u0026rsquo;s description. For more detailed information, refer to the Postman Collection for this API available in the folder api: http_adapter_api_postman.json\nExample of Storage Query Requests are the following:\nRetrieve the first 4 Digital Twin State Variations\n{ \u0026#34;resourceType\u0026#34;: \u0026#34;DIGITAL_TWIN_STATE\u0026#34;, \u0026#34;queryType\u0026#34;: \u0026#34;SAMPLE_RANGE\u0026#34;, \u0026#34;startIndex\u0026#34;: 0, \u0026#34;endIndex\u0026#34;: 3 } Retrieve Digital Twin State Variations in a Time Range\n{ \u0026#34;resourceType\u0026#34;: \u0026#34;DIGITAL_TWIN_STATE\u0026#34;, \u0026#34;queryType\u0026#34;: \u0026#34;TIME_RANGE\u0026#34;, \u0026#34;startIndex\u0026#34;: 161989898, \u0026#34;endIndex\u0026#34;: 162989898 } Retrieve the last Digital Twin State\n{ \u0026#34;resourceType\u0026#34;: \u0026#34;DIGITAL_TWIN_STATE\u0026#34;, \u0026#34;queryType\u0026#34;: \u0026#34;LAST_VALUE\u0026#34; } Available keywords for Query Resource Type and Query Type are the following (as explained in the dedicated Query System Page):\n- PHYSICAL_ASSET_PROPERTY_VARIATION - TIME_RANGE - SAMPLE_RANGE - COUNT - PHYSICAL_ASSET_EVENT_NOTIFICATION - TIME_RANGE - SAMPLE_RANGE - COUNT - PHYSICAL_ACTION_REQUEST - TIME_RANGE - SAMPLE_RANGE - COUNT - DIGITAL_ACTION_REQUEST - TIME_RANGE - SAMPLE_RANGE - COUNT - DIGITAL_TWIN_STATE - TIME_RANGE - SAMPLE_RANGE - COUNT - LAST_VALUE - NEW_PAD_NOTIFICATION - TIME_RANGE - SAMPLE_RANGE - COUNT - UPDATED_PAD_NOTIFICATION - TIME_RANGE - SAMPLE_RANGE - COUNT - PHYSICAL_RELATIONSHIP_INSTANCE_CREATED_NOTIFICATION - TIME_RANGE - SAMPLE_RANGE - COUNT - PHYSICAL_RELATIONSHIP_INSTANCE_DELETED_NOTIFICATION - TIME_RANGE - SAMPLE_RANGE - COUNT - LIFE_CYCLE_EVENT - TIME_RANGE - SAMPLE_RANGE - COUNT - LAST_VALUE - STORAGE_STATS - LAST_VALUE ","date":"February 9, 2024","id":17,"permalink":"/docs/adapters/http-digital-adapter/","summary":"The HttpDigitalAdapter is a powerful component designed to facilitate the integration of Digital Twins into HTTP-based systems. It serves as a bridge between a Digital Twin and HTTP-based applications, allowing developers to easily expose and interact with Digital Twin data and functionalities over HTTP.","tags":"","title":"HTTP Digital Adapter"},{"content":"The WLDT framework intends to maximize modularity, re-usability and flexibility in order to effectively mirror physical smart objects in their digital counterparts. The proposed library focuses on the simplification of twins design and development aiming to provide a set of core features and functionalities for the widespread adoption of Internet of Things DTs applications.\nA WLDT instance is a general purpose software entity implementing all the features and functionalities of a Digital Twin running in cloud or on the edge. It has the peculiar characteristic to be generic and ``attachable\u0026rsquo;\u0026rsquo; to any physical thing in order to impersonate and maintain its digital replica and extend the provided functionalities for example through the support of additional protocols or a specific translation or normalization for data and formats.\nHereafter, the requirements that led the design and development of the WLDT framework are:\ni) Simplicity - with WLDT developers must have the possibility to easily create a new instance by using existing modules or customizing the behavior according the need of their application scenario; ii) Extensibility - while WLDT must be as simple and light as possible, the API should be also easily extendible in order to let programmers to personalize the configuration and/or to add new features loading and executing multiple modules at the same times; iii) Portability \u0026amp; Micorservice Readiness - a digital twin implemented through WLDT must be able to run on any platform without changes and customization. Our goal is to have a simple and light core engine with a strategic set of IoT-oriented features allowing the developer to easily create DT applications modeled as independent software agents and packed as microservices. In the following Figure, the main components that make up the architecture of WLDT are represented, and thus through which the individual Digital Twin is implemented. Specifically, from the image it is possible to identify the three levels on which the architecture is developed: the one related to the core of the library, the one that models the DT, and finally, that of the adapters.\nEach of this core components has the following main characteristics:\nMetrics Manager: Provides the functionalities for managing and tracking various metrics within DT instances combining both internal and custom metrics through a flexible and extensible approach. Logger: Is designed to facilitate efficient and customizable logging within implemented and deployed DTs with configurable log levels and versatile output options. Utils \u0026amp; Commons: Hold a collection of utility classes and common functionalities that can be readily employed across DT implementations ranging from handling common data structures to providing helpful tools for string manipulation. Event Communication Bus: Represents the internal Event Bus, designed to support communication between the different components of the DT\u0026rsquo;s instance. It allows defining customized events to model both physical and digital input and outputs. Each WLDT\u0026rsquo;s component can publish on the shared Event Bus and define an Event Filter to specify which types of events it is interested in managing, associating a specific callback to each one to process the different messages. Digital Twin Engine: Defines the multi-thread engine of the library allowing the execution and monitoring of multiple DTs (and their core components) simultaneously. Therefore, it is also responsible for orchestrating the different internal modules of the architecture while keeping track of each one, and it can be considered the core of the platform itself allowing the execution and control of the deployed DTs. Currently, it supports the execution of twins on the same Java process, however the same engine abstraction might be used to extend the framework to support distributed execution for example through different processes or microservices. Digital Twin: Models a modular DT structure built through the combination of core functionalities together with physical and digital adapter capabilities. This Layer includes the Digital Twin State responsible to structure the state of the DT by defining the list of properties, events, and actions. The different instances included in the lists can correspond directly to elements of the physical asset or can derive from their combination, in any case, it is the Shadowing Function (SF) that defines the mapping, following the model defined by the designer. This component also exposes a set of methods to allow SF manipulation. Every time the Digital Twin State is modified, the latter generates the corresponding DT\u0026rsquo;s event to notify all the components about the variation. Shadowing Function: It is the library component responsible for defining the behavior of the Digital Twin by interacting with the Digital Twin State. Specifically, it implements the shadowing process that allows keeping the DT synchronized with its physical entity. This component is based on a specific implementation of a WLDT Worker called Model Engine, in order to be executed by the WLDT Engine. The Shadowing Model Function is the fundamental component that must be extended by the DT designer to concretize its model. The shadowing function observes the life cycle of the Digital Twin to be notified of the different state changes. For example, it is informed when the DT enters the Bound state, i.e. when its Physical Adapters have completed the binding procedure with the physical asset. This component also allows the designer to define the behavior of the DT in case a property is modified, an event is triggered, or an action is invoked. Physical Adapter: It defines the essential functionalities that the individual extensions, related to specific protocols, must implement. As provided by the DT definition, a DT can be equipped with multiple Physical Adapters in order to manage communication with the corresponding physical entity. Each will produce a Physical Asset Description (PAD), i.e., a description of the properties, events, actions, and relationships that the physical asset exposes through the specific protocol. The DT transitions from the Unbound to the Bound state when all its Physical Adapters have produced their respective PADs. The Shadowing Function, following the DT model, selects the components of the various PADs that it is interested in managing. Digital Adapter: It provides the set of callbacks that each specific implementation can use to be notified of changes in the DT state. Symmetrically to what happens with Physical Adapters, a Digital Twin can define multiple Digital Adapters to expose its state and functionality through different protocols. Storage Layer: The storage layer has been integrated into the core library with the aim to enable a manual or automatic storage of data related to the evolution of Digital Twins state, the associated generated and processed events, and any variations involving properties, events, actions, relationships, and life cycle. Therefore, to create a Digital Twin using WLDT, it is necessary to define and instantiate a DT with its Shadowing Function and at least one Physical Adapter and one Digital Adapter, in order to enable connection with the physical entity and allow the DT to be used by external applications. Once the 3 components are defined, it is possible to instantiate the WLDT Engine and, subsequently, start the lifecycle of the DT. In the following sections we will go through the fundamental steps to start working with the library and creating all the basic modules to design, develop and execute our first Java Digital Twin.\n","date":"February 9, 2024","id":18,"permalink":"/docs/introduction/library-structure-basic-concepts/","summary":"The WLDT framework intends to maximize modularity, re-usability and flexibility in order to effectively mirror physical smart objects in their digital counterparts.","tags":"","title":"Library Structure \u0026 Basic Concepts"},{"content":" With respect to the element present in the real world, it is defined as a Physical Asset (PA) with the intention of referring to any entity that has a manifestation or relevance in the physical world and a well-defined lifespan.\nThe previous Figure schematically illustrates the main component of an abstract Digital Twin and clarifies its responsibility to be a bridge between the cyber and the physical world. The blueprint components (then mapped into the WLDT Library) are:\nPhysical Interface The entity in charge of both the initial digitalization o shadowing process and the perpetual responsibility to keep the DT and PA in synch during its life cycle. It can execute multiple Physical Asset Adapters to interact with the PA and detect and digitalize the physical event coming from the physical entity according to its nature and the supported protocols and data formats (e.g., through HTTP and JSON). Digital Interface The component complementary to the Physical Interface and in charge of handling DT\u0026rsquo;s internal variations and events towards external digital entities and consumers. It executes multiple and reusable Digital Adapters in charge of handling digital interactions and events and responsible for making the DT interoperable with external applications. DT\u0026rsquo;s Model The module defining the DT\u0026rsquo;s behaviour and its augmented functionalities. It supports the execution of different configurable and reusable modules and functionalities handling both physical and digial events according to the implemented behaviour. Furthermore, the Model is the component responsible to handle and keep updated the Digital Twin State as described in the following sections. The Digital Twin Model(M) allows capturing and representing the PA at an appropriate level of abstraction, i.e., avoiding irrelevant aspects for its purpose and modeling only domain-level information rather than technological ones. Finally, the link between the physical and digital copy is defined as shadowing. Specifically, the term defines the process that enables continuous and (almost) real-time updating of the internal state of the DT in relation to changes that occur in the PA.\nEach DT is thus equipped with an internal model, which defines how the PA is represented in the digital level. The DT\u0026rsquo;s representation denoted as Digital Twin State supported and defined through M is defined in terms of:\nProperties: represent the observable attributes of the corresponding PA as labeled data whose values can dynamically change over time, in accordance with the evolution of the PA\u0026rsquo;s state. Events: represent the domain-level events that can be observed in the PA. Relationships: represent the links that exist between the modeled PA and other physical assets of the organizations through links to their corresponding Digital Twins. Like properties, relationships can be observed, dynamically created, and change over time, but unlike properties, they are not properly part of the PA\u0026rsquo;s state but of its operational context (e.g., a DT of a robot within a production line). Actions: represent the actions that can be invoked on the PA through interaction with the DT or directly on the DT if they are not directly available on the PA (the DT is augmenting the physical capabilities). Once the model M is defined, the dynamic state of the DT (SDT) can be defined by through the combination of its properties, events, relationships and actions associated to the DT timestamp that represents the current time of synchronization between the physical and digital counterparts.\nThe Shadowing Process The shadowing process (also known as replication of digitalization) allows to keep the Digital Twin State synchronized with that of the corresponding physical resource according to what is defined by the model M. Specifically, each relevant update of the PA state (SPA) is translated into a sequence of 3 main steps:\neach relevant change in physical asset state is modeled by a physical_event (e_pa); the event is propagated to the DT; given the new physical_event, the DT\u0026rsquo;s is updated through the application of a shadowing function, which depends on the model M The shadowing process allows also the DT to reflect and invoke possible actions of the PA. The DT receives an action request (denoted as digital_action) on its digital interface, applies the shadowing function to validate it and then propagates the request through its physical interface. An important aspect to emphasize is that the request for a digital_action does not directly change the state of the DT since any changes can only occur as a result of the shadowing function from the PA to the DT, as described earlier.\n","date":"February 9, 2024","id":19,"permalink":"/docs/introduction/dt-model/","summary":"With respect to the element present in the real world, it is defined as a Physical Asset (PA) with the intention of referring to any entity that has a manifestation or relevance in the physical world and a well-defined lifespan.","tags":"","title":"DT Model"},{"content":"\rThe modeling of the concept of DT includes also the definition and characterization of its life cycle. Based on the scientific literature, we model (and then map into the library) a life cycle with 5 states through which the DT goes from when it is executed to when it is stopped. The previous Figure shows a graphical representation of the life cycle with the following steps:\nOperating \u0026amp; Not Bound: this is the state in which the DT is located following the initialization phase, indicating that all internal modules of the DT are active but there is no association yet with the corresponding PA. Bound: this is the state in which the DT transitions following the correct execution of the binding procedure. The binding procedure allows to connect the two parts and enables bidirectional flow of events. Shadowed: this is the state reached by the DT when the shadowing process begins and its state is correctly synchronized with that of the PA. Out of Sync: this is the state that determines the presence of errors in the shadowing process. When in this state, the DT is not able to handle either state alignment events or those generated by the application layer. Done: this is the state that the DT reaches when the shadowing process is stopped, but the DT continues to be active to handle requests coming from external applications. From Unbound to Bound Taking into account the target reference Life Cycle the first point to address is how we can move from an UnBound state to a Bound condition with respect to the relationship with the Physical Layer.\nThe previous Figure illustrates a simple scenario where a Physical Asset uses two protocols (P1 and P2) to communicate and it is connected to the Digital Twin through a DT\u0026rsquo;s Physical Interface enabled with two dedicated Adapters for protocol P1 and P2. In order to move from the Unbound to Bound state the DT should be aware of the description of the target asset with respect to the two protocols. For example through P1 the asset exposes telemetry data (e.g., light bulb status and energy consumption) while on P2 allows incoming action requests (e.g., turn on/off the light). The Digital Twin can start the shadowing process only when it is bound and has a description of the properties and capabilities of the associated physical counterpart. The schematic procedure is illustrated in the following Figure:\nInvolved steps are:\nThe Adapter P1 communicates with the PA through Protocol 1 and provides a Physical Asset Description from its perspective The Adapter P2 communicates with the PA through Protocol 2 and provides a Physical Asset Description from its perspective Only when all Physical Adapters have been correctly bound (it may require time) to the Physical Asset and the associated Physical Asset Descriptions have been generated, the DT can move from UnBound to Bound Main core aspects associated to the concept of Physical Asset Description (PAD) are the following:\nIt is used to describe the list of properties, actions and relationships of a Physical Asset Each Physical Adapter generates a dedicated PAD associated to its perspective on the Physical Assets and its capabilities to read data and execute actions It is a responsibility of the DT to handle multiple descriptions in order to build the digital replica It will be used by the DT to handle the shadowing process and keep the digital replica synchronized with the physical counterpart From Bound to Shadowed Following the same approach described in the previous step we need to define a procedure to allow the DT to move from a Bound state to a Shadowed condition where the twin identified the interesting capabilities of the Physical Asset that has to be digitalized and according to the received Physical Asset Descriptions start the shadowing procedure to be synchronized with the physical world.\nAs schematically illustrated in the previous Figure, involved steps are:\nThe Model defines which properties should be monitored on the Physical Asset and start observing them through the target adapters Involved Physical Adapters communicate with the Physical Asset, receive data and generate Events (ePA) to notify about physical property changes Received ePA will be used by the Digital Twin Model in order to run the Shadowing function and compute the new DT State The DT can move from the Bound to Shadowed phase until it is able to maintain a proper synchronization with the physical asset over time through its shadowing process and the generation and maintenance of the DT\u0026rsquo;s State The Digital Twin State is structured and characterized by the following elements:\nA list of properties A list of actions A list of relationships Listed elements can be directly associated to the corresponding element of the Physical Asset or generated by DT Model combining multiple physical properties, actions or relationships at the same time. The Digital Twin State can be managed through the Shadowing Function and exposes a set of methods for its manipulated. When there is a change in the DT State an event (eDT) will be generated\nThe manipulation of DT\u0026rsquo;s State generates a set of DT\u0026rsquo;s events (eDT) associated to each specific variation and evolution of the twin during its life cyle. These events are used by the Digital Interface and in particular by its Digital Adapters to expose the DT\u0026rsquo;s State, its properties and capabilities to the external digital world. At the same time, eDT can be used by Digital Adapters to trigger action on the DT and consequently to propagate (if acceptable and/or needed) the incoming request to the physical assets bound with the target DT. Supported events are illustrated in the following schema.\n","date":"February 9, 2024","id":20,"permalink":"/docs/introduction/dt-life-cycle/","summary":"The modeling of the concept of DT includes also the definition and characterization of its life cycle. Based on the scientific literature, we model (and then map into the library) a life cycle with 5 states through which the DT goes from when it is executed to when it is stopped.","tags":"","title":"DT Life Cycle"},{"content":"\rThe entire WLDT Library and the associated DTs modeling has been designed as an event-driven system where each component is (almost) fully decoupled from the other and focused on a specific responsibilities. The communication among these independent components (e.g., Physical Adapters, Digital Adapters, Model, Storage, Shadowing Function etc ..) is implemented through an event-driven system and and effective exchange of messages and depicted in the reported Figure.\nIn order to better understand the type and nature of the available events this section provides additional information about the characteristics and responsibility for each category and are of interest in the Digital Twin.\nPhysical Asset Events Those events maps every bidirectional interaction with the Physical Asset (or multiple assets) that the DT is in charge of digitalizing. Involved events are related to:\nPhysical Asset Description: Associated to any variation in the description of the Physical Asset through the structure denoted as PhysicalAssetDescription (PAD) New Physical Asset Description: Maps the availability of a new PAD from a specific target Physical Asset through a Physical Adapter Updated Physical Asset Description: Maps the availability of an updated PAD from a specific target Physical Asset through a Physical Adapter Physical Asset Variation: Associated to any variation of the Physical Asset in terms of Properties, Events and Relationships Instances (previously declared in the PAD) Property Variation: Maps a variation of a Physical Property (e.g., temperature value of 25 Celsius Degrees) Event Notification: Maps an event notification generated by the Physical Asset (e.g., Over-Heating) Relation Instance Variation: Maps a variation of Relationships instance associated to a Relationship type declared in the PAD Physical Asset Action Request: Maps an event coming from the DT\u0026rsquo;s Core for the Physical Asset (managed by the Physical Adapter) to trigger an action in the physical world (e.g., turn on the switch) Digital Twin\u0026rsquo;s Events Those events are on the other end in charge of mapping event generate by the DT itself during its evolution and across its life-cycle. They can be associated to the following aspects:\nLife Cycle Variation: Maps a change in the life cycle\u0026rsquo;s state of the DT for example moving from Bound to Synchronized DT State Variation: Maps a variation in the State of the DT communicating the new State and the list of associated changes DT State Event Notification: Maps an event generated by the DT for example mapping an event notification coming from the Physical Asset (e.g., Over-Heating) or a \u0026ldquo;new\u0026rdquo; notification from the DT (e.g., Anomaly-Detected) Digital Action Request: Maps an action request on an available action exposed by the DT and that is requested by and external application. This Action trigger can be internally managed by the DT or can generate then a trigger for a Physical Action as previously described. ","date":"September 4, 2024","id":21,"permalink":"/docs/introduction/dt-events/","summary":"The entire WLDT Library and the associated DTs modeling has been designed as an event-driven system where each component is (almost) fully decoupled from the other and focused on a specific responsibilities.","tags":"","title":"DT Events"},{"content":"The storage layer has been integrated into the core WLDT library, enabling Digital Twins to manually and automatically store data related to the evolution of their state, generated events (as illustrated in DT Events Page), and any variations involving properties, events, actions, relationships, and life cycle.\nThe WLDT Storage Layer consists of two main components:\nStorage Manager: This is the central component of the storage system, facilitating the structured and modular storage and retrieval of information. It allows developers to create and utilize various storage systems (e.g., in-memory, file-based, or DBMS) simultaneously. The Storage Layer is accessible in both read and write modes internally by the DT\u0026rsquo;s Model, and in read-only mode via the Query System by Digital Adapters. Query System: To delegate and encapsulate the responsibility of data storage within the DT\u0026rsquo;s model, a query system has been integrated. This system enables Digital Adapters to retrieve stored data and expose it according to their specific logic and implementation. The storage layer is designed for easy extension, allowing developers to create and share new storage layers (e.g., using Redis, MySQL, or MongoDB). The provided in-memory implementation serves only for basic development and testing purposes. Similarly, the Query Manager can be extended and customized by developers to implement additional query management features or to enhance the default functionalities provided by the library.\nStorage Manager The main module of the Storage Layer is the one associated to Storage Capabilities and it is composed by two main classes: StorageManager and WldStorage with the following characteristics and main methods:\nStorageManager: The StorageManager class is a class that represents the storage manager for a DigitalTwin. It is responsible for managing the storage of the data related to the DigitalTwin. It is an observer of the WldtEventBus, and it is able to save the data in the available storages. The class extends a DigitalTwinWorker, in order to allow the component to work in a structure and integrated way on a different thread that the core of a DT can coordinate starting and stopping it when required. The manager allow the usage of different storage systems at the same time in order to allow the developers to memorize the information accordingly to their need in the right storage system at the same time (e.g., REDIS for quick cached information and MongDB for historical data). Main associated methods are: putStorage(WldtStorage storage): Add a new WldtStorage to the StorageManager getStorageIdList(): Returns the list of id of the WldtStorage in the StorageManager isStorageAvailable(String storageId): Checks if a target Storage Id is available in the Storage Manager getStorage(String storageId): Get the target WldtStorage by id from the Storage Manager removeStorage(String storageId): Remove an existing WldtStorage by id from the StorageManager WldtStorage: Defines an abstract class allowing the Digital Twin developer to implement its internal storage system for the Digital Twin instance. The class defines methods for the management of: Digital Twin State storage and retrieval with the associated change list; Generated State Digital Events; Life Cycle State storage and retrieval; Physical Asset Description storage and retrieval; Physical Asset Property Variation storage and retrieval; Physical Asset Relationship Instance storage and retrieval; Digital Action Request storage and retrieval; Physical Asset Action Request storage and retrieval; Physical Asset Event Notification storage and retrieval; Each WldtStorage instance can be configured (using the right constructor method) to: Observe all Wldt events (stateEvents, physicalAssetEvents, physicalAssetActionEvents, physicalAssetDescriptionEvents, digitalActionEvents, lifeCycleEvents) Filter only for specific class of events Once the WldtStorage has been properly configured to receive target events the StorageManager automatically save information of interest for that specific storage. For example we can have a StorageA (e.g, REDIS) configured to receive all the generated events and a StorageB (e.g., MongoDB) in charge of saving only DT\u0026rsquo;s state variation over time. The default implementation of the WldtStorage is the class DefaultWldtStorage. This class provides a simple storage solution for digital twin states, digital twin state changes, physical asset events, and digital twin events. The class provides ONLY a memory based approach for storage using ArrayLists and HashMaps and more advanced solution should be implemented for production oriented Digital Twins for examples using external storage and memorization solutions. Each Record written and returned by methods available through the WldtStorage implementations are extension of the StorageRecord used to represents a single record in the storage with a unique id Methods available and implemented by WldtStorage implementations are the following grouped by categories: Digital Twin State: saveDigitalTwinState(DigitalTwinState digitalTwinState, List\u0026lt;DigitalTwinStateChange\u0026gt; digitalTwinStateChangeList): Save a new computed instance of the DT State in the Storage together with the list of the changes with respect to the previous state getLastDigitalTwinState(): Returns the latest computed Digital Twin State of the target Digital Twin instance getDigitalTwinStateCount(): Returns the number of computed and stored Digital Twin States getDigitalTwinStateInTimeRange(long startTimestampMs, long endTimestampMs): Retrieves a list of DigitalTwinState objects within the specified time range getDigitalTwinStateInRange(int startIndex, int endIndex): Retrieves a list of Digital Twin states within the specified range of indices Digital Twin State Event Notification: saveDigitalTwinStateEventNotification(DigitalTwinStateEventNotification\u0026lt;?\u0026gt; digitalTwinStateEventNotification): Save the Digital Twin State Event Notification getDigitalTwinStateEventNotificationCount(): Get the number of Digital Twin State Event Notification getDigitalTwinStateEventNotificationInTimeRange(long startTimestampMs, long endTimestampMs): Get the Digital Twin State Event Notification in the specified time range getDigitalTwinStateEventNotificationInRange(int startIndex, int endIndex): Get the Digital Twin State Event Notification in the specified range of indices Life Cycle State Variation: saveLifeCycleState(LifeCycleStateVariation lifeCycleStateVariation): Save the LifeCycleState of the Digital Twin getLastLifeCycleState(): Get the last LifeCycleState of the Digital Twin getLifeCycleStateCount(): Get the number of LifeCycleState of the Digital Twin getLifeCycleStateInTimeRange(long startTimestampMs, long endTimestampMs): Get the last LifeCycleState of the Digital Twin getLifeCycleStateInRange(int startIndex, int endIndex): Get the LifeCycleState of the Digital Twin in the specified range of indices Physical Asset Event Notification: savePhysicalAssetEventNotification(PhysicalAssetEventNotification physicalAssetEventNotification): Save the Physical Asset Event Notification getPhysicalAssetEventNotificationCount(): Get the number of Physical Asset Event Notification getPhysicalAssetEventNotificationInTimeRange(long startTimestampMs, long endTimestampMs): Get the Physical Asset Event Notification in the specified time range getPhysicalAssetEventNotificationInRange(int startIndex, int endIndex): Get the Physical Asset Event Notification in the specified range of indices Physical Action Request: savePhysicalAssetActionRequest(PhysicalAssetActionRequest physicalAssetActionRequest): Save Physical Asset Action Request getPhysicalAssetActionRequestCount(): Get the number of Physical Asset Action Request getPhysicalAssetActionRequestInTimeRange(long startTimestampMs, long endTimestampMs): Get the Physical Asset Action Request in the specified time range getPhysicalAssetActionRequestInRange(int startIndex, int endIndex): Get the Physical Asset Action Request in the specified range of indices Digital Action Request: saveDigitalActionRequest(DigitalActionRequest digitalActionRequest): Save a Digital Action Request getDigitalActionRequestCount(): Get the number of Digital Action Request Stored getDigitalActionRequestInTimeRange(long startTimestampMs, long endTimestampMs): Get the Digital Action Request in the specified time range getDigitalActionRequestInRange(int startIndex, int endIndex): Get the Digital Action Request in the specified range of indices Physical Asset Description (PAD) Notification New PAD Notification saveNewPhysicalAssetDescriptionNotification(PhysicalAssetDescriptionNotification physicalAssetDescriptionNotification): Save a new Physical Asset Description Available getNewPhysicalAssetDescriptionNotificationCount(): Get the number of New Physical Asset Description Notifications available getNewPhysicalAssetDescriptionNotificationInTimeRange(long startTimestampMs, long endTimestampMs): Get the New Physical Asset Description Available in the specified time range getNewPhysicalAssetDescriptionNotificationInRange(int startIndex, int endIndex): Get the New Physical Asset Description Available in the specified range of indices Updated PAD Notification saveUpdatedPhysicalAssetDescriptionNotification(PhysicalAssetDescriptionNotification physicalAssetDescriptionNotification): Save the updated Physical Asset Description Notification getUpdatedPhysicalAssetDescriptionNotificationCount(): Get the number of Updated Physical Asset Description getUpdatedPhysicalAssetDescriptionNotificationInTimeRange(long startTimestampMs, long endTimestampMs): Get the Updated Physical Asset Description in the specified time range getUpdatedPhysicalAssetDescriptionNotificationInRange(int startIndex, int endIndex): Get the Updated Physical Asset Description in the specified range of indices Physical Asset Property Variation: savePhysicalAssetPropertyVariation(PhysicalAssetPropertyVariation physicalAssetPropertyVariation): Save the Physical Asset Property Variation getPhysicalAssetPropertyVariationCount(): Get the number of Physical Asset Property Variation getPhysicalAssetPropertyVariationInTimeRange(long startTimestampMs, long endTimestampMs): Get the Physical Asset Property Variation in the specified time range getPhysicalAssetPropertyVariationInRange(int startIndex, int endIndex): Get the Physical Asset Property Variation in the specified range of indices Physical Asset Relationship Instance Notification Created Relationship Instance savePhysicalAssetRelationshipInstanceCreatedNotification(PhysicalRelationshipInstanceVariation physicalRelationshipInstanceVariation): Save the Physical Asset Relationship Instance Created Event getPhysicalAssetRelationshipInstanceCreatedNotificationCount(): Get the number of Physical Asset Relationship Instance Created Event getPhysicalAssetRelationshipInstanceCreatedNotificationInTimeRange(long startTimestampMs, long endTimestampMs): Get the Physical Asset Relationship Instance Created Event in the specified time range getPhysicalAssetRelationshipInstanceCreatedNotificationInRange(int startIndex, int endIndex): Get the Physical Asset Relationship Instance Created Event in the specified range of indices Deleted Relationship Instance savePhysicalAssetRelationshipInstanceDeletedNotification(PhysicalRelationshipInstanceVariation physicalRelationshipInstanceVariation): Save the Physical Asset Relationship Instance Updated Event getPhysicalAssetRelationshipInstanceDeletedNotificationCount(): Get the number of Physical Asset Relationship Instance Updated Event getPhysicalAssetRelationshipInstanceDeletedNotificationInTimeRange(long startTimestampMs, long endTimestampMs): Get the Physical Asset Relationship Instance Updated Event in the specified time range getPhysicalAssetRelationshipInstanceDeletedNotificationInRange(int startIndex, int endIndex): Get the Physical Asset Relationship Instance Updated Event in the specified range of indices Storage Statistics: Retrieve and returns storage statistics in terms of the number of stored records for each type and the associated time range of the stored records (start and end timestamp in milliseconds). Storage Statistics are mapped and modeled using the classes StorageStats and StorageStatsRecord. Storage Manager Code Some examples of usage for the Storage Layer are the following:\nLets\u0026rsquo; create a new Digital Twin with a single Storage in charge of automatically observe and store all the event generated and going through the target DT instance\n// Create the Digital Twin Engine DigitalTwinEngine digitalTwinEngine = new DigitalTwinEngine(); // Create a new Digital Twin with a Demo Shadowing Function DigitalTwin digitalTwin = new DigitalTwin(TEST_DIGITAL_TWIN_ID, new DemoShadowingFunction()); // Physical Adapter Configuration DemoPhysicalAdapter physicalAdapter = new DemoPhysicalAdapter(...); digitalTwin.addPhysicalAdapter(physicalAdapter); // Digital Adapter Configuration digitalAdapter = new DemoDigitalAdapter(...); digitalTwin.addDigitalAdapter(digitalAdapter); // Create a new WldtStorage instance using the default implementation and observing all the events DefaultWldtStorage myStorage = new DefaultWldtStorage(\u0026#34;test_storage\u0026#34;, true); // Add the new Default Storage Instance to the Digital Twin Storage Manager digitalTwin.getStorageManager().putStorage(myStorage); // Add the Twin to the Engine digitalTwinEngine.addDigitalTwin(digitalTwin); // Start the Digital Twin digitalTwinEngine.startDigitalTwin(TEST_DIGITAL_TWIN_ID); Now let\u0026rsquo;s suppose to have two additional implementation of the WldtStorage class supporting Redis and MongDB and called RedisWldtStorage and MongoDbWldtStorage. We would like to use Redis to automatically observe all the events and MongoDb only to store DT\u0026rsquo;s state and life cycle variations.\n[...] // Create a new RedisWldtStorage instance using the default implementation and observing all the events RedisWldtStorage myRedisStorage = new RedisWldtStorage(\u0026#34;redis_storage\u0026#34;, true); myRedisStorage.setRedisConfiguration(myRedisConfiguration); // Add the new Redis Storage Instance to the Digital Twin Storage Manager digitalTwin.getStorageManager().putStorage(myRedisStorage); // Create a new MongoDbWldtStorage instance using the default implementation and observing only State and LifeCycle Events MongoDbWldtStorage myMongoDbStorage = new MongoDbWldtStorage(\u0026#34;mongo_db_storage\u0026#34;, true, false, false, false, false, true); myMongoDbStorage.setMongoDbConfiguration(myMongoDbConfiguration); // Add the new MongoDb Storage Instance to the Digital Twin Storage Manager digitalTwin.getStorageManager().putStorage(myRedisStorage); [...] Within the ShadowingFunction it is possible to have the reference to the StorageManager in order to access available Storage in both reading and writing mode. This is an example of how to retrieve an available WldtStorage through its id and the use it to read Properties values in a time range of the last 5 minutes:\nString TARGET_STORAGE_ID = \u0026#34;test_storage\u0026#34;; if(this.storageManager.isStorageAvailable(TARGET_STORAGE_ID)){ // Access the Storage Manager to store the last value of the property WldtStorage targetStorage = this.storageManager.getStorage(TARGET_STORAGE_ID); // Get the current time in milliseconds long endTime = System.currentTimeMillis(); // Get the Time in the last 5 minutes long startTime = endTime - (5 * 60 * 1000); // Get the last Physical Asset Action Request in the last 5 minutes List\u0026lt;PhysicalAssetPropertyVariationRecord\u0026gt; propertyVariationRecords = targetStorage.getPhysicalAssetPropertyVariationInTimeRange(startTime, endTime); for(PhysicalAssetPropertyVariationRecord propertyVariationRecord : propertyVariationRecords){ logger.info(\u0026#34;Property Variation Record: {}\u0026#34;, propertyVariationRecord); [...] } } Note: The StorageManager, as previously described, can automatically store DT-related events based on the configuration and setup of each WldtStorage instance added to the manager. However, since the ShadowingFunction has direct access to the StorageManager in both read and write modes, manual handling of data storage is also possible. To achieve this, you can disable automatic storage by setting it to false for specific event types or for all event types. This allows you to manually manage the storage of information within the ShadowingFunction.\nCustom Storage As previously mentioned, the class WldtStorage is an abstract class allowing the developer to implement its internal storage system for the Digital Twin instance. The class defines a set of abstract methods that should be implemented by the developer to shape the management of reading and writing data from and to the target Storage and associated to the identified variations and changes:\nDigital Twin State Generated State Digital Events Life Cycle State Physical Asset Description Physical Asset Property Variation Physical Asset Relationship Instance Digital Action Request Physical Asset Action Request Physical Asset Event Notification Storage Statistics According to the type of the managed resource the type of queries can be characterized in terms of:\nTime Query: Returns available records in a time range made by a start time in ms and and end time in ms Index Query: Returns available records in a index range made a start and end index Last Value Query: Return the last available value for the target resource Count Query: Returns the number of element of that specific resource All the stored and retrieve information and record as mapped into dedicated classes available in the package storage.model and associated to the different type of managed resources: digital, lifecycle, physical, state. Each record class extends the base class StorageRecord.\nA default Storage module denoted as DefaultWldtStorage is natively available in the library providing a simple in-memory storage solution but a developer can implement its own Storage module (e.g., to enable the support for MongoDb or REDIS).\nIn order to implement its own storage module the developer should extend the basic abstract class WldtStorage and implement all the supported method to handle data writing and reading. These new classes can extend also the constructor and the required information for example to handle and manager storage configuration (e.g., ip address and port of the storage system or the local folder where tha stored file should be written).\nQuery System Given the library\u0026rsquo;s goal of maximizing modularity and decoupling responsibilities among the available components, the Query System has been introduced. This system allows components external to the core responsibilities of the Digital Twin (e.g., Digital Adapters and Augmentation Functions) to retrieve stored data and use or expose it according to their specific logic and implementation. For instance, an HTTP Digital Adapter could expose stored information about a DT\u0026rsquo;s state variations over time, or a Monitoring Adapter could use available storage instances to retrieve events for a deeper understanding of the target DT instance\u0026rsquo;s behavior. The query system has been implemented entirely through dedicated events in order to maximize the decoupling of the solution and and supports at the same time both synchronous and asynchronous queries.\nThe main classes associated to the Query System are the following:\nQueryManager: This class represents the Query Manager responsible to handle the query request and manage the query execution and has been designed to be extended by the user to implement the desired query management logic (e.g., as with the DefaultQueryManager). QueryRequest: The class contains all the information needed to perform a query on the storage system QueryRequestType: This Enum represents the Query Request Type used to specify the type of query to be performed on the storage system supporting: TIME_RANGE SAMPLE_RANGE LAST_VALUE COUNT QueryResourceType: This Enum represents the Query Resource Type used to specify the type of resource to be queried on the storage system supporting the following resource types mapping those available and managed by the storage manager (and the supported and associated RequestType): PHYSICAL_ASSET_PROPERTY_VARIATION TIME_RANGE SAMPLE_RANGE COUNT PHYSICAL_ASSET_EVENT_NOTIFICATION TIME_RANGE SAMPLE_RANGE COUNT PHYSICAL_ACTION_REQUEST TIME_RANGE SAMPLE_RANGE COUNT DIGITAL_ACTION_REQUEST TIME_RANGE SAMPLE_RANGE COUNT DIGITAL_TWIN_STATE TIME_RANGE SAMPLE_RANGE COUNT LAST_VALUE NEW_PAD_NOTIFICATION TIME_RANGE SAMPLE_RANGE COUNT UPDATED_PAD_NOTIFICATION TIME_RANGE SAMPLE_RANGE COUNT PHYSICAL_RELATIONSHIP_INSTANCE_CREATED_NOTIFICATION TIME_RANGE SAMPLE_RANGE COUNT PHYSICAL_RELATIONSHIP_INSTANCE_DELETED_NOTIFICATION TIME_RANGE SAMPLE_RANGE COUNT LIFE_CYCLE_EVENT TIME_RANGE SAMPLE_RANGE COUNT LAST_VALUE STORAGE_STATS LAST_VALUE QueryExecutor: This class represents the Query Executor used to execute queries on the storage system supporting both synchronous and asynchronous query execution. Internally is implemented through an event-based mechanism to handle the query request and response QueryResult: This class represents the Query Result returned by the Query Executor containing the query results and the query status (successful or not) and error message (if any) together with also the original request IQueryResultListener: This interface represents the Query Result Listener used to receive the query results Query System Code An example of Synchronous query is:\nQueryExecutor queryExecutor = new QueryExecutor(TEST_DIGITAL_TWIN_ID, \u0026#34;query-executor\u0026#34;); // Create Query Request to the Storage Manager for the Last Digital Twin State QueryRequest queryRequest = new QueryRequest(); queryRequest.setResourceType(QueryResourceType.DIGITAL_TWIN_STATE); queryRequest.setRequestType(QueryRequestType.LAST_VALUE); // Send the Query Request to the Storage Manager for the target DT QueryResult\u0026lt;?\u0026gt; queryResult = queryExecutor.syncQueryExecute(queryRequest); Following the same approach an Asynchrounouse query can be executed as follows:\nQueryExecutor queryExecutor = new QueryExecutor(TEST_DIGITAL_TWIN_ID, \u0026#34;query-executor\u0026#34;); // Create Query Request to the Storage Manager for the Last Digital Twin State QueryRequest queryRequest = new QueryRequest(); queryRequest.setResourceType(QueryResourceType.DIGITAL_TWIN_STATE); queryRequest.setRequestType(QueryRequestType.LAST_VALUE); // Send the Query Request to the Storage Manager for the target DT queryExecutor.asyncQueryExecute(queryRequest, new IQueryResultListener() { @Override public void onQueryResult(QueryResult\u0026lt;?\u0026gt; queryResult) { [...] } }); The class DigitalAdapter has been updated adding also an internal reference to a QueryExecutor in order to simplify the interaction with the query system directly from an adapter like in the following example where we use the query Executor of the Digital Adapter invokeAction callback through its internal variable accessible through this.queryExecutor without creating a new executor:\npublic \u0026lt;T\u0026gt; void invokeAction(String actionKey, T body){ try { // Create Query Request to the Storage Manager for the Last Digital Twin State QueryRequest queryRequest = new QueryRequest(); queryRequest.setResourceType(QueryResourceType.DIGITAL_TWIN_STATE); queryRequest.setRequestType(QueryRequestType.LAST_VALUE); // Send the Query Request to the Storage Manager for the target DT QueryResult\u0026lt;?\u0026gt; queryResult = this.queryExecutor.syncQueryExecute(queryRequest); // Do Something with the Query Result for(Object result : queryResult.getResults()){ // Check the type of the Resulting class accordingly to the query if(result instanceof DigitalTwinState) logger.info(\u0026#34;LAST DT STATE: {}\u0026#34;, result); else logger.error(\u0026#34;INVALID RESULT TYPE: {}\u0026#34;, result.getClass().getName()); } logger.info(\u0026#34;INVOKING ACTION: {} BODY: {}\u0026#34;, actionKey, body); publishDigitalActionWldtEvent(actionKey, body); } catch (EventBusException e) { e.printStackTrace(); } } Custom Query Manager The class in charge of managing an incoming query is called QueryManager and it is characterized by two core methods:\nhandleQuery(QueryRequest queryRequest, Map\u0026lt;String, WldtStorage\u0026gt; storageMap): Handle Query Request allowing its management through the storage map and the associated storage objects. Uses the method getTargetStorage to select the target storage to be used to handle the query. getTargetStorage(QueryRequest queryRequest, Map\u0026lt;String, WldtStorage\u0026gt; storageMap): The method has been designed to return the desired storage object from the storage map to be used for the query management starting from the target QueryRequest and the StorageMap of the DT. In the default implementation, the method returns the first storage object available in the storage map. The QueryManager class has a list of methods that structure the type of available queries and that return an instance of QueryResult with an error and a message of \u0026ldquo;Query not supported by the current implementation !\u0026rdquo;. The list of these methods is the following:\nhandleLifeCycleEventQuery(...): Handle Life Cycle Event Query Request handlePhysicalRelationshipInstanceDeletedNotificationQuery(...): Handle Physical Relationship Instance Deleted Notification Query Request handlePhysicalRelationshipInstanceCreatedNotificationQuery(...): Handle Physical Relationship Instance Created Notification Query Request handleUpdatedPadNotification(...): Handle Updated Pad Notification Query Request handleNewPadNotification(...): Handle New Pad Notification Query Request handleDigitalActionRequestQuery(...): Handle Digital Action Request Query Request handlePhysicalActionRequestQuery(...): Handle Physical Action Request Query Request handlePhysicalAssetEventNotificationQuery(...): Handle Physical Asset Event Notification Query Request handlePhysicalAssetPropertyVariationQuery(...): Handle Physical Asset Property Variation Query Request handleStateQuery(...): Handle Digital Twin State Query Request handleStorageStatsQuery(...): Handle Storage Stats Query The library provides a default implementation that is ready to use and automatically activated in every DT instance and called DefaultQueryManager. This class extends the QueryManager class and implements the default behavior for the query management implementing each of the previous listed methods. In the default implementation the method getTargetStorage returns the first storage object available in the storage map. The behavior can be changed by overriding the method in the custom query manager implementation. In the custom implementation, the method can be used to select different storage according to the query.\nIn order to extend and customize the adopter QueryManager a developer can:\nCreate a new Class extending the QueryManager Implement (if required) a custom getTargetStorage method to return the correct available storage according to the type of request (e.g., the State of the DT are stored on MongoDB while the variation of the Physical Asset on REDIS) If required the developer can implement o change the behavior of the query manager in terms of the query management methods One a custom QueryManager has been defined it can be set and configured through the following method setQueryManager on the StorageManager of each Digital Twin:\ndigitalTwin.getStorageManager().setQueryManager(myQueryManager); ","date":"August 29, 2024","id":22,"permalink":"/docs/guides/storage-layer/","summary":"The storage layer has been integrated into the core WLDT library, enabling Digital Twins to manually and automatically store data related to the evolution of their state, generated events (as illustrated in DT Events Page), and any variations involving properties, events, actions, relationships, and life cycle.","tags":"","title":"Storage Layer"},{"content":"New Features WldtEventObserver A new class called WldtEventObserver has been introduced to allow a simplified observation of target specific events generated by the Digital Twin and its components such as adapters and the model. Main mapped events and filters are:\nState Events: State Update and State Event Notifications Physical Asset Events: Physical Property Variation, Physical Event Notification, Physical Relationship Instance Creation and Deletion Physical Asset Action Events: Physical Action Trigger Digital Action Events: Digital Action Event Physical Asset Description Events: Physical Asset Description Available and Updated Life Cycle Events: Digital Twin Life Cycle Events Query Request Events: Storage Query Request Events (See next sections for additional information) For each event type dedicated observation and un-observation methods (e.g., observePhysicalAssetEvents() and unObservePhysicalAssetEvents()) are available in order to create an instance of the observer and decide which events to receive.\nTo build a WldtEventObserver a dedicated listener IWldtEventObserverListener should be implemented by the developer to receive the callbacks related to the incoming events. All the events are of the generic type WldtEvent and it is up to the developer the validate and check the received object and if it match with the expected one.\nAn example of usage for the event observer is the following:\nWldtEventObserver eventObserver = new WldtEventObserver( \u0026#34;DT_TEST_ID_1\u0026#34;, \u0026#34;test-observer\u0026#34;, myObserverListener); // Start all the available observation eventObserver.observePhysicalAssetEvents(); eventObserver.observePhysicalAssetActionEvents(); eventObserver.observeStateEvents(); eventObserver.observeDigitalActionEvents(); eventObserver.observePhysicalAssetDescriptionEvents(); eventObserver.observeLifeCycleEvents(); The WldtEventObserver has been currently used internally within the library to simplify the implementation and usage of the Storage Layer and the associated Storage Query System as described in the dedicated sections.\nStorage Layer A new storage layer has been integrated into the core WLDT library, enabling Digital Twins (DTs) to store data related to the evolution of their state, generated events, and any variations involving properties, events, actions, relationships, and life cycle. The Storage Layer consists of two main components:\nStorage Manager: This is the central component of the storage system, facilitating the structured and modular storage and retrieval of information. It allows developers to create and utilize various storage systems (e.g., in-memory, file-based, or DBMS) simultaneously. The Storage Layer is accessible in both read and write modes internally by the DT\u0026rsquo;s Model, and in read-only mode via the Query System by Digital Adapters. Query System: To delegate and encapsulate the responsibility of data storage within the DT\u0026rsquo;s model, a query system has been integrated. This system enables Digital Adapters to retrieve stored data and expose it according to their specific logic and implementation. The storage layer is designed for easy extension, allowing developers to create and share new storage layers (e.g., using Redis, MySQL, or MongoDB). The provided in-memory implementation serves only for basic development and testing purposes. Similarly, the Query Manager can be extended and customized by developers to implement additional query management features or to enhance the default functionalities provided by the library.\nStorage Manager The main module of the Storage Layer is the one associated to Storage Capabilities and it is composed by two main classes: StorageManager and WldStorage with the following characteristics and main methods:\nStorageManager: The StorageManager class is a class that represents the storage manager for a DigitalTwin. It is responsible for managing the storage of the data related to the DigitalTwin. It is an observer of the WldtEventBus, and it is able to save the data in the available storages. The class extends a DigitalTwinWorker, in order to allow the component to work in a structure and integrated way on a different thread that the core of a DT can coordinate starting and stopping it when required. The manager allow the usage of different storage systems at the same time in order to allow the developers to memorize the information accordingly to their need in the right storage system at the same time (e.g., REDIS for quick cached information and MongDB for historical data). Main associated methods are: putStorage(WldtStorage storage): Add a new WldtStorage to the StorageManager getStorageIdList(): Returns the list of id of the WldtStorage in the StorageManager isStorageAvailable(String storageId): Checks if a target Storage Id is available in the Storage Manager getStorage(String storageId): Get the target WldtStorage by id from the Storage Manager removeStorage(String storageId): Remove an existing WldtStorage by id from the StorageManager WldtStorage: Defines an abstract class allowing the Digital Twin developer to implement its internal storage system for the Digital Twin instance. The class defines methods for the management of: Digital Twin State storage and retrieval with the associated change list; Generated State Digital Events; Life Cycle State storage and retrieval; Physical Asset Description storage and retrieval; Physical Asset Property Variation storage and retrieval; Physical Asset Relationship Instance storage and retrieval; Digital Action Request storage and retrieval; Physical Asset Action Request storage and retrieval; Physical Asset Event Notification storage and retrieval; Each WldtStorage instance can be configured (using the right constructor method) to: Observe all Wldt events (stateEvents, physicalAssetEvents, physicalAssetActionEvents, physicalAssetDescriptionEvents, digitalActionEvents, lifeCycleEvents) Filter only for specific class of events Once the WldtStorage has been properly configured to receive target events the StorageManager automatically save information of interest for that specific storage. For example we can have a StorageA (e.g, REDIS) configured to receive all the generated events and a StorageB (e.g., MongoDB) in charge of saving only DT\u0026rsquo;s state variation over time. The default implementation of the WldtStorage is the class DefaultWldtStorage. This class provides a simple storage solution for digital twin states, digital twin state changes, physical asset events, and digital twin events. The class provides ONLY a memory based approach for storage using ArrayLists and HashMaps and more advanced solution should be implemented for production oriented Digital Twins for examples using external storage and memorization solutions. Methods available and implemented by WldtStorage implementations are the following grouped by categories: Digital Twin State: saveDigitalTwinState(DigitalTwinState digitalTwinState, List\u0026lt;DigitalTwinStateChange\u0026gt; digitalTwinStateChangeList): Save a new computed instance of the DT State in the Storage together with the list of the changes with respect to the previous state getLastDigitalTwinState(): Returns the latest computed Digital Twin State of the target Digital Twin instance getDigitalTwinStateCount(): Returns the number of computed and stored Digital Twin States getDigitalTwinStateInTimeRange(long startTimestampMs, long endTimestampMs): Retrieves a list of DigitalTwinState objects within the specified time range getDigitalTwinStateInRange(int startIndex, int endIndex): Retrieves a list of Digital Twin states within the specified range of indices Digital Twin State Event Notification: saveDigitalTwinStateEventNotification(DigitalTwinStateEventNotification\u0026lt;?\u0026gt; digitalTwinStateEventNotification): Save the Digital Twin State Event Notification getDigitalTwinStateEventNotificationCount(): Get the number of Digital Twin State Event Notification getDigitalTwinStateEventNotificationInTimeRange(long startTimestampMs, long endTimestampMs): Get the Digital Twin State Event Notification in the specified time range getDigitalTwinStateEventNotificationInRange(int startIndex, int endIndex): Get the Digital Twin State Event Notification in the specified range of indices Life Cycle State Variation: saveLifeCycleState(LifeCycleStateVariation lifeCycleStateVariation): Save the LifeCycleState of the Digital Twin getLastLifeCycleState(): Get the last LifeCycleState of the Digital Twin getLifeCycleStateCount(): Get the number of LifeCycleState of the Digital Twin getLifeCycleStateInTimeRange(long startTimestampMs, long endTimestampMs): Get the last LifeCycleState of the Digital Twin getLifeCycleStateInRange(int startIndex, int endIndex): Get the LifeCycleState of the Digital Twin in the specified range of indices Physical Asset Event Notification: savePhysicalAssetEventNotification(PhysicalAssetEventNotification physicalAssetEventNotification): Save the Physical Asset Event Notification getPhysicalAssetEventNotificationCount(): Get the number of Physical Asset Event Notification getPhysicalAssetEventNotificationInTimeRange(long startTimestampMs, long endTimestampMs): Get the Physical Asset Event Notification in the specified time range getPhysicalAssetEventNotificationInRange(int startIndex, int endIndex): Get the Physical Asset Event Notification in the specified range of indices Physical Action Request: savePhysicalAssetActionRequest(PhysicalAssetActionRequest physicalAssetActionRequest): Save Physical Asset Action Request getPhysicalAssetActionRequestCount(): Get the number of Physical Asset Action Request getPhysicalAssetActionRequestInTimeRange(long startTimestampMs, long endTimestampMs): Get the Physical Asset Action Request in the specified time range getPhysicalAssetActionRequestInRange(int startIndex, int endIndex): Get the Physical Asset Action Request in the specified range of indices Digital Action Request: saveDigitalActionRequest(DigitalActionRequest digitalActionRequest): Save a Digital Action Request getDigitalActionRequestCount(): Get the number of Digital Action Request Stored getDigitalActionRequestInTimeRange(long startTimestampMs, long endTimestampMs): Get the Digital Action Request in the specified time range getDigitalActionRequestInRange(int startIndex, int endIndex): Get the Digital Action Request in the specified range of indices Physical Asset Description (PAD) Notification New PAD Notification saveNewPhysicalAssetDescriptionNotification(PhysicalAssetDescriptionNotification physicalAssetDescriptionNotification): Save a new Physical Asset Description Available getNewPhysicalAssetDescriptionNotificationCount(): Get the number of New Physical Asset Description Notifications available getNewPhysicalAssetDescriptionNotificationInTimeRange(long startTimestampMs, long endTimestampMs): Get the New Physical Asset Description Available in the specified time range getNewPhysicalAssetDescriptionNotificationInRange(int startIndex, int endIndex): Get the New Physical Asset Description Available in the specified range of indices Updated PAD Notification saveUpdatedPhysicalAssetDescriptionNotification(PhysicalAssetDescriptionNotification physicalAssetDescriptionNotification): Save the updated Physical Asset Description Notification getUpdatedPhysicalAssetDescriptionNotificationCount(): Get the number of Updated Physical Asset Description getUpdatedPhysicalAssetDescriptionNotificationInTimeRange(long startTimestampMs, long endTimestampMs): Get the Updated Physical Asset Description in the specified time range getUpdatedPhysicalAssetDescriptionNotificationInRange(int startIndex, int endIndex): Get the Updated Physical Asset Description in the specified range of indices Physical Asset Property Variation: savePhysicalAssetPropertyVariation(PhysicalAssetPropertyVariation physicalAssetPropertyVariation): Save the Physical Asset Property Variation getPhysicalAssetPropertyVariationCount(): Get the number of Physical Asset Property Variation getPhysicalAssetPropertyVariationInTimeRange(long startTimestampMs, long endTimestampMs): Get the Physical Asset Property Variation in the specified time range getPhysicalAssetPropertyVariationInRange(int startIndex, int endIndex): Get the Physical Asset Property Variation in the specified range of indices Physical Asset Relationship Instance Notification Created Relationship Instance savePhysicalAssetRelationshipInstanceCreatedNotification(PhysicalRelationshipInstanceVariation physicalRelationshipInstanceVariation): Save the Physical Asset Relationship Instance Created Event getPhysicalAssetRelationshipInstanceCreatedNotificationCount(): Get the number of Physical Asset Relationship Instance Created Event getPhysicalAssetRelationshipInstanceCreatedNotificationInTimeRange(long startTimestampMs, long endTimestampMs): Get the Physical Asset Relationship Instance Created Event in the specified time range getPhysicalAssetRelationshipInstanceCreatedNotificationInRange(int startIndex, int endIndex): Get the Physical Asset Relationship Instance Created Event in the specified range of indices Deleted Relationship Instance savePhysicalAssetRelationshipInstanceDeletedNotification(PhysicalRelationshipInstanceVariation physicalRelationshipInstanceVariation): Save the Physical Asset Relationship Instance Updated Event getPhysicalAssetRelationshipInstanceDeletedNotificationCount(): Get the number of Physical Asset Relationship Instance Updated Event getPhysicalAssetRelationshipInstanceDeletedNotificationInTimeRange(long startTimestampMs, long endTimestampMs): Get the Physical Asset Relationship Instance Updated Event in the specified time range getPhysicalAssetRelationshipInstanceDeletedNotificationInRange(int startIndex, int endIndex): Get the Physical Asset Relationship Instance Updated Event in the specified range of indices Some examples of usage for the Storage Layer are the following:\nLets\u0026rsquo; create a new Digital Twin with a single Storage in charge of automatically observe and store all the event generated and going through the target DT instance\n// Create the Digital Twin Engine DigitalTwinEngine digitalTwinEngine = new DigitalTwinEngine(); // Create a new Digital Twin with a Demo Shadowing Function DigitalTwin digitalTwin = new DigitalTwin(TEST_DIGITAL_TWIN_ID, new DemoShadowingFunction()); // Physical Adapter Configuration DemoPhysicalAdapter physicalAdapter = new DemoPhysicalAdapter(...); digitalTwin.addPhysicalAdapter(physicalAdapter); // Digital Adapter Configuration digitalAdapter = new DemoDigitalAdapter(...); digitalTwin.addDigitalAdapter(digitalAdapter); // Create a new WldtStorage instance using the default implementation and observing all the events DefaultWldtStorage myStorage = new DefaultWldtStorage(\u0026#34;test_storage\u0026#34;, true) // Add the new Default Storage Instance to the Digital Twin Storage Manager digitalTwin.getStorageManager().putStorage(myStorage); // Add the Twin to the Engine digitalTwinEngine.addDigitalTwin(digitalTwin); // Start the Digital Twin digitalTwinEngine.startDigitalTwin(TEST_DIGITAL_TWIN_ID); Now let\u0026rsquo;s suppose to have two additional implementation of the WldtStorage class supporting Redis and MongDB and called RedisWldtStorage and MongoDbWldtStorage. We would like to use Redis to automatically observe all the events and MongoDb only to store DT\u0026rsquo;s state and life cycle variations.\n[...] // Create a new RedisWldtStorage instance using the default implementation and observing all the events RedisWldtStorage myRedisStorage = new RedisWldtStorage(\u0026#34;redis_storage\u0026#34;, true); myRedisStorage.setRedisConfiguration(myRedisConfiguration); // Add the new Redis Storage Instance to the Digital Twin Storage Manager digitalTwin.getStorageManager().putStorage(myRedisStorage); // Create a new MongoDbWldtStorage instance using the default implementation and observing only State and LifeCycle Events MongoDbWldtStorage myMongoDbStorage = new MongoDbWldtStorage(\u0026#34;mongo_db_storage\u0026#34;, true, false, false, false, false, true); myMongoDbStorage.setMongoDbConfiguration(myMongoDbConfiguration); // Add the new MongoDb Storage Instance to the Digital Twin Storage Manager digitalTwin.getStorageManager().putStorage(myRedisStorage); [...] Within the ShadowingFunction it is possible to have the reference to the StorageManager in order to access available Storage in both reading and writing mode. This is an example of how to retrieve an available WldtStorage through its id and the use it to read Properties values in a time range of the last 5 minutes:\nString TARGET_STORAGE_ID = \u0026#34;test_storage\u0026#34;; if(this.storageManager.isStorageAvailable(TARGET_STORAGE_ID)){ // Access the Storage Manager to store the last value of the property WldtStorage targetStorage = this.storageManager.getStorage(TARGET_STORAGE_ID); // Get the current time in milliseconds long endTime = System.currentTimeMillis(); // Get the Time in the last 5 minutes long startTime = endTime - (5 * 60 * 1000); // Get the last Physical Asset Action Request in the last 5 minutes List\u0026lt;PhysicalAssetPropertyVariationRecord\u0026gt; propertyVariationRecords = targetStorage.getPhysicalAssetPropertyVariationInTimeRange(startTime, endTime); for(PhysicalAssetPropertyVariationRecord propertyVariationRecord : propertyVariationRecords){ logger.info(\u0026#34;Property Variation Record: {}\u0026#34;, propertyVariationRecord); [...] } } Note: The StorageManager, as previously described, can automatically store DT-related events based on the configuration and setup of each WldtStorage instance added to the manager. However, since the ShadowingFunction has direct access to the StorageManager in both read and write modes, manual handling of data storage is also possible. To achieve this, you can disable automatic storage by setting it to false for specific event types or for all event types. This allows you to manually manage the storage of information within the ShadowingFunction.\nQuery System Given the library\u0026rsquo;s goal of maximizing modularity and decoupling responsibilities among the available components, the Query System has been introduced. This system allows components external to the core responsibilities of the Digital Twin (e.g., Digital Adapters and Augmentation Functions) to retrieve stored data and use or expose it according to their specific logic and implementation. For instance, an HTTP Digital Adapter could expose stored information about a DT\u0026rsquo;s state variations over time, or a Monitoring Adapter could use available storage instances to retrieve events for a deeper understanding of the target DT instance\u0026rsquo;s behavior. The query system has been implemented entirely through dedicated events in order to maximize the decoupling of the solution and and supports at the same time both synchronous and asynchronous queries.\nThe main classes associated to the Query System are the following:\nQueryManager: This class represents the Query Manager responsible to handle the query request and manage the query execution and has been designed to be extended by the user to implement the desired query management logic (e.g., as with the DefaultQueryManager). QueryRequest: The class contains all the information needed to perform a query on the storage system QueryRequestType: This Enum represents the Query Request Type used to specify the type of query to be performed on the storage system supporting: TIME_RANGE SAMPLE_RANGE LAST_VALUE COUNT QueryResourceType: This Enum represents the Query Resource Type used to specify the type of resource to be queried on the storage system supporting the following resource types mapping those available and managed by the storage manager: PHYSICAL_ASSET_PROPERTY_VARIATION PHYSICAL_ASSET_EVENT_NOTIFICATION PHYSICAL_ACTION_REQUEST DIGITAL_ACTION_REQUEST DIGITAL_TWIN_STATE NEW_PAD_NOTIFICATION UPDATED_PAD_NOTIFICATION PHYSICAL_RELATIONSHIP_INSTANCE_CREATED_NOTIFICATION PHYSICAL_RELATIONSHIP_INSTANCE_DELETED_NOTIFICATION LIFE_CYCLE_EVENT QueryExecutor: This class represents the Query Executor used to execute queries on the storage system supporting both synchronous and asynchronous query execution. Internally is implemented through an event-based mechanism to handle the query request and response QueryResult: This class represents the Query Result returned by the Query Executor containing the query results and the query status (successful or not) and error message (if any) together with also the original request IQueryResultListener: This interface represents the Query Result Listener used to receive the query results An example of Synchronous query is:\nQueryExecutor queryExecutor = new QueryExecutor(TEST_DIGITAL_TWIN_ID, \u0026#34;query-executor\u0026#34;); // Create Query Request to the Storage Manager for the Last Digital Twin State QueryRequest queryRequest = new QueryRequest(); queryRequest.setResourceType(QueryResourceType.DIGITAL_TWIN_STATE); queryRequest.setRequestType(QueryRequestType.LAST_VALUE); // Send the Query Request to the Storage Manager for the target DT QueryResult\u0026lt;?\u0026gt; queryResult = queryExecutor.syncQueryExecute(queryRequest); Following the same approach an Asynchrounouse query can be executed as follows:\nQueryExecutor queryExecutor = new QueryExecutor(TEST_DIGITAL_TWIN_ID, \u0026#34;query-executor\u0026#34;); // Create Query Request to the Storage Manager for the Last Digital Twin State QueryRequest queryRequest = new QueryRequest(); queryRequest.setResourceType(QueryResourceType.DIGITAL_TWIN_STATE); queryRequest.setRequestType(QueryRequestType.LAST_VALUE); // Send the Query Request to the Storage Manager for the target DT queryExecutor.asyncQueryExecute(queryRequest, new IQueryResultListener() { @Override public void onQueryResult(QueryResult\u0026lt;?\u0026gt; queryResult) { [...] } }); The class DigitalAdapter has been updated adding also an internal reference to a QueryExecutor in order to simplify the interaction with the query system directly from an adapter like in the following example where we use the query Executor of the Digital Adapter invokeAction callback through its internal variable accessible through this.queryExecutor without creating a new executor:\npublic \u0026lt;T\u0026gt; void invokeAction(String actionKey, T body){ try { // Create Query Request to the Storage Manager for the Last Digital Twin State QueryRequest queryRequest = new QueryRequest(); queryRequest.setResourceType(QueryResourceType.DIGITAL_TWIN_STATE); queryRequest.setRequestType(QueryRequestType.LAST_VALUE); // Send the Query Request to the Storage Manager for the target DT QueryResult\u0026lt;?\u0026gt; queryResult = this.queryExecutor.syncQueryExecute(queryRequest); // Do Something with the Query Result for(Object result : queryResult.getResults()){ // Check the type of the Resulting class accordingly to the query if(result instanceof DigitalTwinState) logger.info(\u0026#34;LAST DT STATE: {}\u0026#34;, result); else logger.error(\u0026#34;INVALID RESULT TYPE: {}\u0026#34;, result.getClass().getName()); } logger.info(\u0026#34;INVOKING ACTION: {} BODY: {}\u0026#34;, actionKey, body); publishDigitalActionWldtEvent(actionKey, body); } catch (EventBusException e) { e.printStackTrace(); } } Migration Info: 0.3.0 - 0.4.0 Now PhysicalAssetRelationship constructor has also the type in order to match the DigitalTwinStateRelationship and simplify its management The method notifyDigitalTwinStateEvent throws only the Exception WldtDigitalTwinStateEventNotificationException while EventBusException has been removed Additional Improvements \u0026amp; Fixed Bugs Synchronized the update of the current DT Life Cycle State in order to avoid wrong data The WldtEventBus now supports the use of topics Wildcard (at the moment only multi-level with the character *). For example with this approach is possible to subscribe to all the events associated to property variations (topic: dt.physical.event.property.*). New methods added to WldtEventBus are: matchWildCardType(String eventType, String filterType): Check if the provided event type match the WildCard Type isWildCardType(String filterEventType): Check if the provided event type is a WildCard Type The class WldtEventTypes has been introduced to contain all the event types in the WLDT Framework and support internal message exchange. Includes types for events associated and adopted by: i) Physical Adapters; ii) Model and Shadowing Function; and iii) Digital Adapters. The EventManager class has been added to centralize and simplify the event management in the WLDT Framework providing a set of static methods to publish events associated to a target digital twin and publisher (e.g., the physical adapter of the twin). Now PhysicalAssetRelationship class has also the type in order to match the DigitalTwinStateRelationship and simplify its management The internal class ModelEngine has been renamed into DigitalTwinModel as an initial update for further development of the next version 0.5.0 where the structure of the DT\u0026rsquo;s Model and the associated classes will be improved ","date":"August 29, 2024","id":23,"permalink":"/docs/change-logs/change-log-0.4.0/","summary":"New Features WldtEventObserver A new class called WldtEventObserver has been introduced to allow a simplified observation of target specific events generated by the Digital Twin and its components such as adapters and the model.","tags":"","title":"Change Log 0.4.0"},{"content":"","date":"September 7, 2023","id":24,"permalink":"/docs/","summary":"","tags":"","title":"Docs"},{"content":"Welcome to White Label Digital Twins (WLDT), an open-source project dedicated to supporting the design, development, and deployment of Digital Twins within the Internet of Things (IoT) ecosystems.\nThe WLDT library has been designed to align with the latest DT definitions from both Industrial and Scientific domains. It identifies DTs as active, flexible, and scalable software components. Our library aims to provide developers with the tools and resources necessary to create robust Digital Twins that effectively simulate and monitor physical assets within IoT environments.\nWhether you\u0026rsquo;re working on IoT, Industrial IoT (IIoT) applications, Smart Cities projects, or any other IoT-related endeavor, the WLDT library offers a versatile solution for implementing Digital Twins that accurately represent real-world objects and support informed decision-making processes.\n💻 Team \u0026amp; Mantainers [Founders \u0026amp; Main Contributors] Marco Picone - University of Modena \u0026amp; Reggio Emilia, Italy - (Link) [Key Contributors] Samuele Burattini - University of Bologna, Italy - (Link) [Additional Contributors] Marta Spadoni - University of Bologna, Italy - Master Thesis 2022 📜 Scientitic Citation \u0026amp; References If you use the WLDT Library in a Scientific Paper please use this reference:\n@article{PICONE2021100661, title = {WLDT: A general purpose library to build IoT digital twins}, journal = {SoftwareX}, volume = {13}, pages = {100661}, year = {2021}, issn = {2352-7110}, doi = {https://doi.org/10.1016/j.softx.2021.100661}, url = {https://www.sciencedirect.com/science/article/pii/S2352711021000066}, author = {Marco Picone and Marco Mamei and Franco Zambonelli}, keywords = {Internet of Things, Digital twin, Library, Software agent} } 📨 Community Join our community and contribute to the advancement of Digital Twin technology with White Label Digital Twins!\nWLDT questions, feedback and discussions are tracked using slack channels in the WLDT Slack Workspace.\nThe workspace is available here: WLDT Slack Workspace\nNew users first need to join the MEC Sandbox slack workspace by creating a new account using the invitation link provided here: Join the WLDT Slack Workspace\n🐛 Reporting Issues WLDT issues should be reported on Slack, where they can be discussed with the core team that maintains the WLDT Library.\n","date":"March 13, 2024","id":25,"permalink":"/about/","summary":"Welcome to White Label Digital Twins (WLDT), an open-source project dedicated to supporting the design, development, and deployment of Digital Twins within the Internet of Things (IoT) ecosystems.","tags":"","title":"About WLDT Library"},{"content":" What is a digital Twin? A Digital Twin (DT) is a comprehensive software representation of any individual Physical Asset (PA) in the real world.\nIt includes the properties, conditions, relationships, and behavior(s) of the real-life object through models and data.\nA Digital Twin is a set of realistic models that can digitalize an object’s behavior in the deployed environment and has the responsibility to represent and reflect its physical counterpart over time maintaining its digital replica across the object’s entire lifecycle.\nWhat can WLDT do for me? The White Label Digital Twin (WLDT) library aims to support the design, development, and deployment of Digital Twins within the Internet of Things (IoT) ecosystems.\nThe library has been designed following the latest DT definitions coming from both Industrial and Scientific domains and identifying DTs as active, flexible and scalable software components.\nScientitic Citation \u0026amp; Reference If you use the WLDT Library in a Scientific Paper refer to the About Page for additional information and scientific references. Thanks :)\n","date":"October 6, 2023","id":26,"permalink":"/","summary":"What is a digital Twin? A Digital Twin (DT) is a comprehensive software representation of any individual Physical Asset (PA) in the real world.","tags":"","title":"White Label Digital Twins"},{"content":"","date":"September 7, 2023","id":27,"permalink":"/privacy/","summary":"","tags":"","title":"Privacy Policy"},{"content":"","date":"January 1, 0001","id":28,"permalink":"/categories/","summary":"","tags":"","title":"Categories"},{"content":"","date":"January 1, 0001","id":29,"permalink":"/contributors/","summary":"","tags":"","title":"Contributors"},{"content":"","date":"January 1, 0001","id":30,"permalink":"/tags/","summary":"","tags":"","title":"Tags"}] \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml index b8f2299..d8750d1 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -1 +1 @@ -https://wldt.github.io/en/sitemap.xml2024-03-15T10:45:36+01:00 \ No newline at end of file +https://wldt.github.io/en/sitemap.xml2024-09-05T15:02:33+02:00 \ No newline at end of file