Components

Digital twin components in AgeDigitalTwins allow you to model complex entities by breaking them down into logical parts. Components provide a way to organize and manage different aspects of a digital twin, each with their own properties and telemetry.

Overview

Components in AgeDigitalTwins follow the DTDL (Digital Twins Definition Language) specification and provide:

  • Modular Design: Break complex twins into manageable components
  • Component-specific Operations: Read, update, and manage individual components
  • Component Telemetry: Publish telemetry data specific to individual components
  • DTDL Validation: Validate component data against DTDL models
  • Azure Digital Twins Compatibility: Uses the same API patterns as Azure Digital Twins

How Components Work

Components are defined in your DTDL models and represent logical parts of a digital twin:

{
  "@id": "dtmi:example:Building;1",
  "@type": "Interface",
  "displayName": "Building",
  "contents": [
    {
      "@type": "Component",
      "name": "hvac",
      "schema": "dtmi:example:HVAC;1"
    },
    {
      "@type": "Component",
      "name": "lighting",
      "schema": "dtmi:example:LightingSystem;1"
    },
    {
      "@type": "Property",
      "name": "buildingName",
      "schema": "string"
    }
  ]
}

Working with Components

Getting Component Data

Retrieve data for a specific component:

// Get HVAC component data from a building twin
var hvacData = await client.GetComponentAsync<HvacComponent>("building-001", "hvac");

Console.WriteLine($"Set Point: {hvacData.SetPoint}°C");
Console.WriteLine($"Current Temp: {hvacData.CurrentTemperature}°C");

Updating Components

Update specific properties within a component:

// Update HVAC component settings
var patch = new JsonPatchDocument();
patch.Replace("/SetPoint", 22.5);
patch.Replace("/Mode", "Auto");

await client.UpdateComponentAsync("building-001", "hvac", patch);

You can also update with a structured object:

// Update with an object
var hvacUpdate = new {
    SetPoint = 23.0,
    Mode = "Heat",
    ScheduleEnabled = true
};

await client.UpdateComponentAsync("building-001", "hvac", hvacUpdate);

Component Validation

Components are automatically validated against their DTDL schema:

try
{
    // This will validate against the HVAC component schema
    await client.UpdateComponentAsync("building-001", "hvac", new {
        SetPoint = "invalid-temperature", // This will fail validation
        Mode = "InvalidMode"              // This will also fail
    });
}
catch (ValidationException ex)
{
    Console.WriteLine($"Validation failed: {ex.Message}");
}

Component Telemetry

Components can publish their own telemetry data independently:

// Publish HVAC component telemetry
await client.PublishComponentTelemetryAsync("building-001", "hvac", new {
    actualTemperature = 22.8,
    energyUsage = 15.5,
    fanSpeed = 75,
    timestamp = DateTime.UtcNow
});

// Publish lighting component telemetry
await client.PublishComponentTelemetryAsync("building-001", "lighting", new {
    brightnessLevel = 80,
    energyUsage = 5.2,
    motionDetected = true,
    timestamp = DateTime.UtcNow
});

Component telemetry events include the component name in the subject:

{
  "specversion": "1.0",
  "type": "Konnektr.DigitalTwins.Component.Telemetry",
  "source": "https://your-adt-instance/",
  "subject": "building-001/components/hvac",
  "data": {
    "actualTemperature": 22.8,
    "energyUsage": 15.5,
    "fanSpeed": 75,
    "timestamp": "2023-01-01T12:00:00Z"
  }
}

Component Events

When you update components, lifecycle events are generated that include component context:

Component Update Event

{
  "specversion": "1.0",
  "type": "Konnektr.DigitalTwins.Twin.Update",
  "source": "https://your-adt-instance/",
  "subject": "building-001",
  "data": {
    "modelId": "dtmi:example:Building;1",
    "patch": [
      {
        "op": "replace",
        "path": "/hvac/SetPoint",
        "value": 23.0
      }
    ]
  }
}

DTDL Component Definitions

Basic Component Schema

{
  "@id": "dtmi:example:HVAC;1",
  "@type": "Interface",
  "displayName": "HVAC System",
  "contents": [
    {
      "@type": "Property",
      "name": "SetPoint",
      "schema": "double",
      "displayName": "Temperature Set Point",
      "description": "Desired temperature in Celsius"
    },
    {
      "@type": "Property",
      "name": "Mode",
      "schema": {
        "@type": "Enum",
        "valueSchema": "string",
        "enumValues": [
          { "name": "Off", "enumValue": "Off" },
          { "name": "Heat", "enumValue": "Heat" },
          { "name": "Cool", "enumValue": "Cool" },
          { "name": "Auto", "enumValue": "Auto" }
        ]
      }
    },
    {
      "@type": "Telemetry",
      "name": "ActualTemperature",
      "schema": "double",
      "displayName": "Actual Temperature"
    }
  ]
}

Complex Component with Nested Properties

{
  "@id": "dtmi:example:LightingSystem;1",
  "@type": "Interface",
  "displayName": "Lighting System",
  "contents": [
    {
      "@type": "Property",
      "name": "Zones",
      "schema": {
        "@type": "Map",
        "mapKey": {
          "name": "ZoneId",
          "schema": "string"
        },
        "mapValue": {
          "name": "ZoneSettings",
          "schema": {
            "@type": "Object",
            "fields": [
              {
                "name": "brightness",
                "schema": "integer"
              },
              {
                "name": "colorTemperature",
                "schema": "integer"
              },
              {
                "name": "enabled",
                "schema": "boolean"
              }
            ]
          }
        }
      }
    }
  ]
}

Working with Nested Component Data

When components contain complex nested structures, you can work with them using dynamic objects or strongly-typed classes:

// Using dynamic object for flexibility
dynamic lighting = await client.GetComponentAsync("building-001", "lighting");
var zone1Brightness = lighting.Zones["zone1"].brightness;

// Using strongly-typed class for validation
public class LightingZoneSettings
{
    public int Brightness { get; set; }
    public int ColorTemperature { get; set; }
    public bool Enabled { get; set; }
}

public class LightingComponent
{
    public Dictionary<string, LightingZoneSettings> Zones { get; set; }
}

var lightingComponent = await client.GetComponentAsync<LightingComponent>("building-001", "lighting");

Component Best Practices

Design Guidelines

  1. Logical Separation: Group related properties and functionality into components
  2. Single Responsibility: Each component should have a clear, single purpose
  3. Naming Consistency: Use consistent naming conventions for component names and properties
  4. Documentation: Include clear descriptions in your DTDL schemas

Performance Considerations

  1. Granular Updates: Update specific components rather than entire twins when possible
  2. Component Telemetry: Use component-specific telemetry for better data organization
  3. Validation: Design component schemas for efficient validation
  4. Indexing: Consider indexing frequently accessed component properties

Error Handling

try
{
    var component = await client.GetComponentAsync<HvacComponent>("building-001", "hvac");
}
catch (DigitalTwinNotFoundException)
{
    // Twin doesn't exist
}
catch (ComponentNotFoundException)
{
    // Component doesn't exist on the twin
}
catch (ValidationException ex)
{
    // Component data doesn't match schema
    Console.WriteLine($"Validation error: {ex.Message}");
}

Integration Patterns

IoT Device Integration

Map physical device capabilities to digital twin components:

// Map sensor data to appropriate components
public async Task UpdateFromIoTDevice(string twinId, IoTDeviceData data)
{
    // Update HVAC component from temperature sensor
    if (data.TemperatureSensor != null)
    {
        await client.UpdateComponentAsync(twinId, "hvac", new {
            ActualTemperature = data.TemperatureSensor.Value,
            LastUpdated = DateTime.UtcNow
        });
    }

    // Update lighting component from occupancy sensor
    if (data.OccupancySensor != null)
    {
        await client.UpdateComponentAsync(twinId, "lighting", new {
            OccupancyDetected = data.OccupancySensor.Occupied,
            LastMotion = data.OccupancySensor.LastMotionTime
        });
    }
}

System Integration

Use components to integrate with external building management systems:

// Sync with external BMS
public async Task SyncWithBuildingManagementSystem(string buildingId)
{
    var bmsData = await _bmsClient.GetBuildingDataAsync(buildingId);

    // Update each system component based on BMS data
    await client.UpdateComponentAsync(buildingId, "hvac", new {
        SetPoint = bmsData.HvacSetPoint,
        Mode = bmsData.HvacMode,
        ScheduleActive = bmsData.HvacScheduleEnabled
    });

    await client.UpdateComponentAsync(buildingId, "security", new {
        AlarmArmed = bmsData.SecurityArmed,
        AccessControlEnabled = bmsData.AccessControlActive
    });
}

See Also

  • DTDL Reference - Learn about Digital Twin Definition Language
  • Validation - Understand how component validation works
  • Telemetry - Learn about component-specific telemetry
  • API Reference - Complete API documentation for component operations
Cookie Notice

We use cookies to enhance your browsing experience.