the-anylogic-company / AnyLogic-JSONifier

A simple library for enabling JSON serialization and deserialization for various AnyLogic types
MIT License
0 stars 1 forks source link

Add DTDL to JSONifier #7

Closed GMGassner closed 3 weeks ago

GMGassner commented 2 months ago

Dear Tyler, I am currently using AL in my research and have recently explored the possibility of utilizing Digital Twins Definition Language v3 for the instantiation of some agents in AL 8.9. In addition to my research, I have observed that several major companies, such as Microsoft and Siemens, are increasingly adopting DTDL for digital twins.

Given that DTDL is JSON-LD based, I believe there is potential to advance support in this direction. Perhaps a separate "DTDLifier" could be developed to facilitate this integration.

I am very interested in contributing to this development if you decide to support DTDL. Please let me know if there is any chance to advance support for DTDL in AL 8.9.

Thank you in advance for considering this request. I look forward to your response.

t-wolfeadam commented 2 months ago

Hi! Thanks for the suggestion! From my (admittedly superficial) knowledge of Azure Digital Twins and DTDL, the scope of what you're proposing can vary dramatically depending on the specifics.

From what I understand about ADT, you're describing the entire model through the DTDL (i.e., the environment, rules and relationships, actions, etc). From a high-level perspective, this is similar to what you're doing when defining a model through AL and the "languages" it provides (i.e., flowcharts, statecharts, Java code, etc.).

If your proposal is to have complete support for converting DTDL to an AL model, this is definitely out of the scope of this project. That would require not only parsing the DTDL but figuring out the AL equivalents and how you can 'map' from one to the other. For example, how to link defined Commands to some code in the user's model.

The other route I can think of is just to use DTDL for the structure of how it defines its objects. Essentially you'd use it as a schema specification for agent types you've already defined in AL, with a customized serializer for converting that structure to the agent type defined in the schema. Customized serializers is already something that's in the works, namely for a way to use JSON with a different structure than what JSONifier expects.

Let me know if I missed anything or what your thoughts are!

GMGassner commented 2 months ago

Hi, thank you so much for the quick reply! You are right regarding the varying scope of my post so I want to narrow it down and explain my experience. I was looking into Manufacturing Ontologies, which ties together DTDL, W3C WoT and ISA95, as an ontology for my project.

My approach was to look at the unit level of manufacturing equipment and have a description, which is relatively flexible. Using the newest version (v3) of DTDL enables that through components and modularity. This concept would be to have one DTDL per unit and use the key concepts in DTDL for a comprehensive description.

  1. Interface: An interface defines the structure and capabilities of a digital twin. It specifies the properties, telemetry, commands, and relationships that a digital twin can have.

  2. Property: Properties are attributes or state variables of a digital twin. They can be read-only or writable and have a specific data type, such as string, integer, or boolean.

  3. Telemetry: Telemetry represents data emitted by a digital twin, typically time-series data from sensors or measurements. It can include data points like temperature, pressure, or status updates.

  4. Command: Commands are actions that can be invoked on a digital twin. They allow you to trigger specific behaviors or operations on the digital twin, such as starting a process or updating a setting.

  5. Relationship: Relationships define how digital twins are connected to each other. They represent the associations or dependencies between different digital twins, such as a device being part of a larger system.

  6. Schema: Schemas define the data types used in properties, telemetry, and commands. DTDL supports primitive types (e.g., string, integer) as well as complex types like enums, objects, and arrays.

  7. Component: Components allow you to modularize and reuse common definitions across multiple interfaces. They enable you to create reusable building blocks for your digital twin models.

Here's an example of a DTDL v3 model that represents a manufacturing machine:

{
  "@id": "dtmi:com:example:ManufacturingMachine;3",
  "@type": "Interface",
  "@context": "dtmi:dtdl:context;3",
  "displayName": "Manufacturing Machine",
  "contents": [
    {
      "@type": "Property",
      "name": "machineId",
      "schema": "string",
      "writable": false
    },
    {
      "@type": "Property",
      "name": "machineType",
      "schema": "string",
      "writable": false
    },
    {
      "@type": "Property",
      "name": "status",
      "schema": {
        "@type": "Enum",
        "valueSchema": "string",
        "enumValues": [
          {
            "name": "Idle",
            "enumValue": "idle"
          },
          {
            "name": "Running",
            "enumValue": "running"
          },
          {
            "name": "Maintenance",
            "enumValue": "maintenance"
          }
        ]
      },
      "writable": true
    },
    {
      "@type": "Telemetry",
      "name": "temperature",
      "schema": "double"
    },
    {
      "@type": "Telemetry",
      "name": "vibration",
      "schema": "double"
    },
    {
      "@type": "Property",
      "name": "productionRate",
      "schema": "integer",
      "unit": "units/hour",
      "writable": false
    },
    {
      "@type": "Relationship",
      "name": "hasOutputProducts",
      "target": "dtmi:com:example:Product;1"
    },
    {
      "@type": "Command",
      "name": "startProduction",
      "request": {
        "name": "productionSettings",
        "schema": {
          "@type": "Object",
          "fields": [
            {
              "name": "productType",
              "schema": "string"
            },
            {
              "name": "quantity",
              "schema": "integer"
            }
          ]
        }
      }
    }
  ]
}

In this example: The @id property identifies the model as a manufacturing machine with version 3. The displayNameproperty provides a human-readable name for the machine. The contentsproperty defines the properties, telemetry, relationships, and commands of the machine: Property elements represent the machine's attributes, such as machineId, machineType, status (an enum property), and productionRate. Telemetry elements define the data points emitted by the machine, such as temperature and vibration. The Relationship element hasOutputProductsindicates that the machine produces products, referencing the Product model. The Command element startProductiondefines an action to start the production process, accepting productionSettingsas a request parameter with productTypeand quantity fields.

So in essence use DTDL to instantiate agents like with the JSONifer, just in a richer format that also enables other use cases. A customized serializer for converting that structure to the agent is essentially what it boils down to.

I would love to help and am happy to hear that something is in the works to enable that. How can i help?

t-wolfeadam commented 2 months ago

Thanks for the extremely detailed explanation, including the example that tied all the mentioned concepts together.

So in essence use DTDL to instantiate agents like with the JSONifer

The part I'm missing though is that the DTDL example you provided is in an interface, not an instance. What you're describing seems to be like a conversion from a DTDL model to an AnyLogic agent type definition. In other words, the library would create agent types themselves dynamically (in traditional Java terms, auto-generated classes). To do this currently would require reverse engineering the XML which makes up the source of an AnyLogic model (which has no guarantee to remain consistent) and is way more complex than what the Jsonifier does.

The only equivalent to the Jsonifier would be if you wanted to take a DTDL instance and import it to the sim to be converted to a pre-defined agent type, with parameters that would map to everything that could be provided by the DTDL instance. The property and telemetry would likely map nicely to parameters, though there's no way to make parameters read-only. You can define enums in the AnyLogic model; they'd just need to be predefined and match up with the naming from the DTDL model. Relationships are trickier because the library would need logic to figure out a way to map whatever value is in the relationship fields to what's in the AnyLogic model (again, pre-defined).

I'm curious to know which route you were thinking about going in! Knowing that will definitely help clarify the feasibility of this.

GMGassner commented 2 months ago

Thanks for taking the time and effort to my request! Sry for the delayed response, I could not find time up until now. I completely agree with you,

create agent types themselves dynamically (in traditional Java terms, auto-generated classes)

is way beyond and not part of my request.

My train of thought was rather simple. From what I gathered a DTDL Interface describes a DT with its components. If I have a DTDL file of every unit I want to have a DT for (like a machine in manufacturing) the sum of DTDL files describe my manufacturing process in a way to be able to have digital twins of every unit and of the whole process. If I wanted to have a simulation that is used for manufacturing optimization, everything that is needed for this to work should be already in the DTDL files (parameters, specification, states, etc.). So my idea was, why not use this detailed description of units and processes to instantiate agents in a simulation. So basically use DTDL not only for connecting machines to their DT, but also to instantiate agents in a simulation model that generates data needed to drive the DT. Of course this has limitation and agent types would have to be compatible in their composition to be instantiated with the files.

In a more structured way: DTDL Interface. "Interface describes the contents (Commands, Components, Properties, Relationships, and Telemetries) of any digital twin. Interfaces are reusable and can be reused as the schema for Components in another Interface."

  1. DTDL Interface:

    • Describes the structure and capabilities of a digital twin of a unit
    • Defines properties, telemetry, relationships, and components
    • Acts as a template or schema for instances
  2. AnyLogic Agent:

    • Represents a unit in a simulation model
    • Has properties, behaviors, and can interact with other agents
    • Can be instantiated from a predefined type

The similarity lies in how both DTDL interfaces and AnyLogic agent types serve as templates for creating instances.

  1. Properties: DTDL properties could map to AnyLogic agent parameters or variables.
  2. Telemetry: Could be represented as variables or outputs in agents.
  3. Relationships: Might be translated to the sequence of units and how agents flow from unit to unit. Alternatively connections
  4. Components: Could map to populations of agents
  5. Command: State charts, Events, etc.

An inspiration was this paper on how DTDL and OPC UA could map to each other Proposal of Mapping Digital Twins Definition Language to Open Platform Communications Unified Architecture

Maybe I'm barking up the wrong tree, but I thought I'll share this with you to see whether it has some merit to it.

t-wolfeadam commented 2 months ago

Again, thank you for such a thorough explanation! I'm starting to see where my previous disconnect was, which I'll speak to first.

The Jsonifier implementation takes advantage of the API shared across all agents, notably the parameter functions (getParameter(name), setParameter(name, value), etc.) which are the main source of initialization targets; most other sources are assumed to be assigned as part of the dynamic logic.

What it seems like you're proposing is an extension of this, also initializing variable values, nested agents/populations, other attributes (e.g., position, speed), connections, schedule events, etc. This is feasible through a much more robust specification (the Jsonifier's intention is to be as simple/inutive as possible).

The more complicated aspect is that the way people define these in their model can vary. For example, sometimes a relationship is defined via a 'Link to agents' and other times with typed variables. This could be theoretically resolved through documentation though.

Where I'm still lost though is the practical, actual nuts and bolts of the implementation and workflow.

If I wanted to have a simulation that is used for manufacturing optimization, everything that is needed for this to work should be already in the DTDL files (parameters, specification, states, etc.).

But this is backwards, is it not? The simulation is essentially a replacement for the real-world equivalent + IoT devices.

To clarify with an example, say I'm wanting to monitor/control my house's temperature/its thermostat. My (limited) understanding is that the Azure DT workflow looks like this:

  1. Define my house (e.g., room relationships/references, thermostat-command-schema) as a model in DTDL and upload that to Azure
  2. Install IoT devices to send out temperature and be able to receive commands
  3. Connect my IoT devices to the Azure DT
  4. Now I can monitor the live temperature and change the thermostat (live) from here

In diagram form:

DTDL ----(uploaded to)----v
                         AZURE
Thermostat <--> IOT <--> DIGITAL TWIN <--> Me (remotely)
^________House_________^

With a simulation model, I'm not just defining the house but also recreating the physics of temperature propagation (or recreating the effects of it) and all the dynamics of the real world system (e.g., simulating how I respond and the points in which I override the set temperature). It also serves as where experimentation/data gathering happens via running experiments. In this way, it's like a replacement for the thermostat + IoT + Azure Digital Twin platform. You could actually create a DTDL model from a simulation model; whereas, the the other way around - uploading a DTDL model to a sim - is like trying to pass it to the IoT devices.

The other (maybe obvious) thing to note is that, unlike Azure DT, syncing to the real, live system only happens at once on startup. After this point, it's assumed it has all the rules and relationships of the system baked into it so that it can accurately play out one possible future trajectory of the environment.

I imagine the true intention is to rather take whatever is outputted by the IoT device, specifically in whatever data format the Azure DT expects as input (maybe this is still referred to as being in DTDL? I couldn't find many resources on actual integration with real systems) and have some library that can parse that and apply it to the sim(?)

t-wolfeadam commented 2 months ago

Also, I did read through the paper you sent (I wasn't previously aware of OPC UA, so thank you!).

From what I gather, in terms of their relationship to a simulation model, the real-time data from both OPC UA-enabled devices or digital twins defined using DTDL could be used to feed into the sim as input on its startup; this would be in whatever data format/structure is outputted by each (or by the underlying IoT device, if you wanted to go that route too).

GMGassner commented 1 month ago

Thanks for thoroughly reading my post and your great answer.

But this is backwards, is it not? The simulation is essentially a replacement for the real-world equivalent + IoT devices.

the real-time data from both OPC UA-enabled devices or digital twins defined using DTDL could be used to feed into the sim as input on its startup

You have grasped my theory very well, but I want to discuss the practical nuts and bolts of implementation and workflow. I hope to build on your example and give you a perspective that illustrates my use case since it aims at a different goal than Azure.

I hypothesize that a digital twin defined in DTDL (or similar) & some initial data should also contain everything needed to be useable as an agent in a simulation. There will be practical limits to the implementation in AnyLogic, but I chose to go down this rabbit hole and explore these.

Based on your example: I want to monitor/control my house's temperature/its thermostat and use a simulation to check different configurations of environment, equipment, and usages.

  1. Define my house (e.g., room relationships/references, thermostat-command-schema, radiators, HVAC, heat sources, cooling sources) in DTDL and instantiate Agents representing the units involved.
  2. Get DTDL for multiple HVAC, radiators, etc.
  3. Use real-world data from IoT devices to have an initial condition to test
  4. run the model and see how a change in initial conditions or a unit/agent/DTDL would affect my room
  5. Now I can choose whatever setup is the right one for me

In mermaid diagram form:

graph LR
    subgraph "DTDL Definition"
        A["Define House in DTDL"]
        A --> C(Rooms)
        A --> D(AirCon)
        A --> E(Radiators)
        A --> F(Heat Sources)
        A --> G(Cooling Sources)
    end
    subgraph "Agent Instantiation"
        H["Instantiate Agents from DTDL"] --> I(AirCon Agents)
        H --> J(Radiator Agents)
        H --> K(Heat Source Agents)
        H --> L(Cooling Source Agents)
    end
    subgraph "Real-World Data"
        M["Get Real-World Data (IoT)"] --> N(Initial Room Temperatures)
        M --> O(HVAC Settings)
        M --> B(Thermostat)
    end
    subgraph "AnyLogic Simulation"
        P["Run Simulation"] --> Q(Room Temperature Changes)
        P --> R(Energy Consumption)
    end
    subgraph "Analysis & Decision"
        S["Analyze Results"]
    end
    B --> P
    C --> H
    D --> H
    E --> H
    F --> H
    G --> H
    I  --> P
    J --> P
    K --> P
    L --> P
    N --> P
    O --> P
    Q --> S
    R --> S

I want to be able to use a file that is intended for digital twins to recreate similar digital twins in an AL model to simulate their behavior and track their metrics. How data finds its way into the sim is my next obstacle, but getting agents configured based on DTDL into a sim would be a first step. Thanks for taking the time to understand my question and I hope I was able to convey the rough idea.

t-wolfeadam commented 1 month ago

You absolutely conveyed the rough idea, so thank you for that! The only bit I'm missing in terms of the feasibility of this is the actual nuts and bolts of the implementation.

Inline with the current example, I created this DTDL definition.

Because you said you were not trying to auto-generate the model itself, just instantiate it, I needed to build the AnyLogic model, pretending there was some pre-established rules about how the DTDL concepts map to the AnyLogic model, shown in the screenshot below. For this example, writable properties = variables; non-writable properties = parameters; interace = agent type; array of an interface = agent population.

image

And now comes the point where the AnyLogic library (i.e., JSONifier or a new one) would come into play. Based on my understanding, the spec doc shows all example implementations using normal JSON. Thus, me/the user would provide a JSON file like this one. Is my understanding correct here?

GMGassner commented 1 month ago

Thank you for your immediate reaction and efforts! From what i can see, you model looks similar to what I would have envisioned given the example we used. If you built the model to this extent, a simple JSON is enough to convey the agents properties into the sim. This is kinda similar to:

Property example(analog to the Telemetry example) This is the semantic information about the sensor in DTDL you modeled. A simple Telemetry definition of a temperature measurement, with the data type double.

    {
      "@type": "Property",
      "name": "currentTemperature",
      "schema": "double",
      "displayName": "Current Temperature"
    },

When JSON is used to serialize Property data for e.g. initial conditions.

"currentTemperature": 42.5

Based on my understanding, the spec doc shows all example implementations using normal JSON. Thus, me/the user would provide a JSON file like this one. Is my understanding correct here?

In your "house" model, the semantics of the DTDL have already been built into the model, with the JSON als properties.

In my idea, a simulation user would have to built a model but their semantic connections, states in a state chart, etc. would be brought in by provide a DTDL file, JSON-LD, to the appropriate agents, like your ihvac.json

Additionally, a "normal" JSON (or database connection), like yours, is used to fill in information for setting the properties within the agent. Like your mymodel.json

If I had correctly interpreted "the practical, actual nuts and bolts of the implementation and workflow." I would have also posted a screenshot of my current implementation, but better late than never.

This is my model for monitoring energy consumption, which is in an early stage and needs improvement.

Screenshot 2024-07-24 091451

with this example JSON (with some meaningless double for properties):

{
    "stateConsumptionRates": {
      "OFF": 0.5,
      "IDLE": 5.0, 
      "WARMUP": 50.0, 
      "RUNNING": 80.0, 
      "COOLDOWN": 10.0 
    },
    "stateProductionRates": {
      "OFF": 0.5, 
      "IDLE": 1.0,
      "WARMUP": 0.5, 
      "RUNNING": 3.0, 
      "COOLDOWN": 1.0 
    },
    "stateTimes": {
      "OFF": 0.5, 
      "IDLE": 1.0,
      "WARMUP": 0.5, 
      "RUNNING": 3.0, 
      "COOLDOWN": 1.0 
    },
    "stateInitialProperties": {
      "intialState": 1.0,
      "targetTemperature": 1.0,
      "intialLevel": 1.0
    },
    "unitProperties": {
      "capacity": 1.0,
      "efficiency": 1.0,
      "transferRate": 600.0
    },
    "unitSpecification": {
      "Type": "Conveyor",
      "manufacturer": "Custom", 
      "model": "BeltConveyor"
    }
  }

It currently works ok with my custom JSON (above), like your DTDL: "Basic House HVAC (Implementation). Instantiating agents into a population works on a basic level with:

Equipment equipment = jsonifier.fromAgentJson(jsonFilePath, Equipment.class, targetPopulation);

However, the semantics of these agents to communicate and get processes working to have these agents act as interconnected units, like in manufacturing, is missing. In my understanding, this gap could be filled with DTDL for instantiation.

From what I read, the format JSON-LD is designed around the concept of a "context" to provide additional mappings from JSON to an RDF model. The context links object properties in a JSON document to concepts in an ontology. And this property is being used in DTDL v3 and can bring together semantics and parameters in a nice way.

When first encountering this property, I immediately thought of using this concept for simulation. I wanted to explore the possibility of using JSON-LD to not only "parse" properties but also semantic information like relationships and connections. In my thought process, directly parsing DTDL could open up the possibility of a simpler base model with the necessary semantic information being implementable/instantiable from the "keywords" (lines with a @).

{
  "@id": "dtmi:com:example:ManufacturingMachine;3",
  "@type": "Interface",
  "@context": "dtmi:dtdl:context;3",
  "displayName": "Manufacturing Machine",
  "contents": [
    {
      "@type": "Property",
      "name": "machineId",
      "schema": "string",
      "writable": false
    },
    {
      "@type": "Property",
      "name": "machineType",
      "schema": "string",
      "writable": false
    },
    {
      "@type": "Property",
      "name": "status",
      "schema": {
        "@type": "Enum",
        "valueSchema": "string",
        "enumValues": [
          {
            "name": "Idle",
            "enumValue": "idle"
          },
          {
            "name": "Running",
            "enumValue": "running"
          },
          {
            "name": "Maintenance",
            "enumValue": "maintenance"
          }
        ]
      },
      "writable": true
    },
    {
      "@type": "Telemetry",
      "name": "temperature",
      "schema": "double"
    },
    {
      "@type": "Telemetry",
      "name": "vibration",
      "schema": "double"
    },
    {
      "@type": "Property",
      "name": "productionRate",
      "schema": "integer",
      "unit": "units/hour",
      "writable": false
    },
    {
      "@type": "Relationship",
      "name": "hasOutputProducts",
      "target": "dtmi:com:example:Product;1"
    },
    {
      "@type": "Command",
      "name": "startProduction",
      "request": {
        "name": "productionSettings",
        "schema": {
          "@type": "Object",
          "fields": [
            {
              "name": "productType",
              "schema": "string"
            },
            {
              "name": "quantity",
              "schema": "integer"
            }
          ]
        }
      }
    }
  ]
}

non-writeable "Property" would be parameters writable "Property" would be variables "Property" of type ENUM would be a state chart "Telemetry" would be an output "Relationship" would be a connection to another agent "Command" would be an event or function

So, if my example could work like envisioned, you would model four agent types (Storage, Converter, Consumer, and Recoverer) with all the necessary bells and whistles for any units they are designed to instantiate. DTDL would do the specific setup for each agent.

Relatively "simple" model + DTDL => "digital twins" in model + JSON => initial condition for simulating "digital twins".

Your DTDL v2 files are quite close to what I tried to show with my DTDL v3, the key differences between DTDL v2 and DTDL v3

Fun fact, this also extends to the Web of Things (WoT) Thing Description , which is currently being merged with DTDL v3 to a universal standard for digital twins. A nice comparison between the two can be found here.

Sorry that it took so many interactions to convey this concept and idea, but I hope that I haven't missed something, and I'm curious what your thoughts are.

t-wolfeadam commented 3 weeks ago

This is my model for monitoring energy consumption, which is in an early stage and needs improvement.

As you point out, this part of the concept is there and I've completely understood the significance of what you're doing here.

However, the semantics of these agents to communicate and get processes working to have these agents act as interconnected units, like in manufacturing, is missing. In my understanding, this gap could be filled with DTDL for instantiation.

This is where it becomes ambiguous again. What you're describing here would require users to already have the such logic/framework set up in their model. The DTDL you provide shows the definition, but not an implementation; to be able to pass the definition would require such a library to modify the actual AnyLogic model.

If it's believed I missing something, I'd be open to hearing what you think the actual logic of the library would look like (as in, pseudo code) - because providing that would really sync our understandings. Unfortunately my time at AnyLogic has come to an end. I'll be forking the repo after transferring it if you want to continue there and if I have personal time, I may look into exploring this further :) Otherwise, thanks for the great educational experience!