Azure Digital Twins 與 Azure 時間序列深入解析 Gen2 之間的模型同步處理

注意

2025 年 3 月之後,時間序列深入解析 (TSI) 服務將不再受到支援。 請考慮盡快將現有 TSI 環境移轉至替代解決方案。 如需淘汰和移轉的詳細資訊,請瀏覽我們的文件

本文說明用來將 Azure Digital Twins (ADT) 中資產模型轉譯為 Azure 時間序列深入解析 (TSI) 中資產模型的最佳做法和工具。 本文是兩部分教學課程系列的第二部分,說明 Azure Digital Twins 與 Azure 時間序列深入解析的整合。 Azure Digital Twins 與時間序列深入解析整合可讓您封存和追蹤 Digital Twins 遙測和計算屬性的歷程記錄。 這系列教學課程旨在讓開發人員致力於整合時間序列深入解析與 Azure Digital Twins。 第 1 部分說明建立資料管線,以將 Azure Digital Twins 的實際時間序列資料帶入時間序列深入解析,而本教學課程系列的第二部分說明 Azure Digital Twins 與時間序列深入解析之間的資產模型同步。 本教學課程說明選擇和建立時間序列識別碼 (TS 識別碼) 命名慣例的最佳做法,以及手動在時間序列模型 (TSM) 中建立階層的最佳做法。

選擇時間序列識別碼

時間序列識別碼是用來識別時間序列深入解析中資產的唯一識別碼。 時間序列資料 (欄位中的遙測資料,這些是時間值組) 會使用 TSID 底下所列的變數來表示。 在 Azure Digital Twins 中,對應項屬性和遙測可用來分別代表對應項所產生的對應項和度量狀態。 自 TSM 的目前設計起,TSID 必須為唯一。 在 Azure Digital Twins 中使用對應項的對應項識別碼,或結合屬性或遙測名稱,一律會在 TSM 中建立唯一的 TS 識別碼。 在一般情況下,<Twin ID> 會是 TSID,而屬性和遙測名稱將會是 TSM 中的變數。 不過,有一些使用案例偏好讓其時間序列深入解析中的資產階層使用複合索引鍵格式壓平合併,例如 <Twin ID>+ <Delimiter of Choice> + <Name of the Property or Telemetry>。 讓我們以範例說明後者的案例。 請考慮建築中模型化為對應項且具有對應項識別碼 Room22 的房間。 其溫度設定屬性會轉譯為 TSID Room22_TempSetting,且溫度測量會轉譯為 TSM 中的 Room22_TempMea

選擇時間序列識別碼

內容化時間序列

時間序列深入解析中資料的內容化 (大部分本質上是空間) 是透過資產階層來達成,而且這同樣用於透過時間序列深入解析總管中的樹狀檢視,以便輕鬆瀏覽資料。 時間序列類型及階層是在時間序列深入解析中使用時間序列模型 (TSM) 來定義。 TSM 中的類型有助於定義變數,而階層層級和執行個體欄位值則用於建構時間序列深入解析總管中的樹狀檢視。 如需 TSM 的詳細資訊,請參閱線上時間序列深入解析文件

在 Azure Digital Twins 中,資產之間的連線會使用對應項關聯性來表示。 對應項關聯性只是已連線資產的圖表。 不過,在時間序列深入解析中,資產之間的關聯性本質上是階層式。 也就是說,資產會共用父子式關聯性,並使用樹狀結構來表示。 若要將關聯性資訊從 Azure Digital Twins 轉譯為時間序列深入解析階層,我們需要從 Azure Digital Twins 選擇相關的階層式關聯性。 Azure Digital Twins 使用開放式標準模型化語言,稱為 Digital Twin Definition Language (DTDL)。 在 DTDL 模型中,會使用稱為 JSON-LD 的 JSON 變體來描述。 如需規格的完整詳細資料,請參閱 DTDL 文件

資產之間的連線

將 Azure Digital Twins 中的圖表表示轉譯為時間序列深入解析中的樹狀結構

本教學課程的下列各節會擷取一些在 Azure Digital Twins 中手動將圖形結構轉譯為時間序列深入解析中樹狀結構的核心案例。

範例系統:本教學課程會使用下列範例來說明以下討論的概念。 這是一個簡單、虛構的建築物管理系統,具有一個樓層和兩個房間。 該系統有三個控溫器,每個房間各有一個,另一個則是樓層的通用控溫器。 此外,該系統也具有水流量計量,可透過房間之間的管道連線測量從 Room21 到 Room22 的水流量。 查看對應項之間的空間關聯性,其具有這兩種類型的關聯性。

  1. 最常見的階層式關聯性。 例如,Building40 -> Floor01 -> FloorTS* -> Temperature

  2. 不是如此常見的迴圈關聯性。 例如,從 Building40 開始,可以透過兩個不同的路徑追蹤 FlowMtr。

    1. Building40 -> Floor01 -> Room21 -> FlowMtr* -> Flow
    2. Building40 -> Floor01 -> Room22 -> FlowMtr* -> Flow
      其中「Flow」是測量 Room21 與 Room22 之間水流量的實際遙測

注意

* FloorTS 代表 FloorThermoStat,而 FlowMtr 代表流量計量,通常作為 TSID 選擇。 溫度和流程是時間序列深入解析中稱為變數的原始遙測。

由於時間序列深入解析中的目前限制,一個資產無法在多個分支中表示,下列各節將說明時間序列深入解析中的階層式和迴圈關聯性的模型化。

案例 1:階層式 (父子式) 關聯性

這是對應項之間最常見的關聯性類型。 下圖說明模型化純父子式關聯性。 執行個體欄位和 TSID 衍生自對應項識別碼,如下所示。 雖然可以使用時間序列深入解析總管手動更新執行個體欄位,但下列名為「使用 API 更新執行個體欄位」的章節說明如何使用 Azure 函式來接聽 Azure Digital Twins 中的模型變更,以及使用 Azure 函式更新時間序列深入解析中的執行個體欄位。

對應對應項識別碼

對應對應項識別碼 2

案例 2:迴圈關聯性

Azure Digital Twins 中的迴圈關聯性與時間序列深入解析中的單一階層關聯性

假設 TSID 必須是唯一的,而且只能在一個階層中表示,此案例代表在對應項 ‘Room21’ 下具有名為 'Flow' 遙測的 ‘FlowMtr’。 未來當時間序列深入解析可以支援 TSM 中時間序列的多重標記法時,遙測 ‘Flow’ 會以 ‘Room 21’‘Room 22’ 表示

下列螢幕擷取畫面顯示將 Azure Digital Twins 中的對應項識別碼手動對應至 TSM 中的執行個體欄位,以及時間序列深入解析中產生的階層。

對應 Azure Digital Twins 中的對應項識別碼

Azure Digital Twins 中的迴圈關聯性與時間序列深入解析中的多個階層,使用重複項目

本教學課程的第 1 部分說明用戶端程式 (Azure 函式) 如何協助將遙測資料從 IoT 中樞或其他事件來源傳輸到 Azure Digital Twins。 此方法建議使用相同的用戶端程式,對父對應項的相關屬性進行更新。 在指定的範例中,從 IoT 中樞讀取 FlowMtr 遙測並更新 FlowMtr 對應項中的屬性「Flow」時,程式也可以更新來源所有可能父系對應項中的對應屬性。 在我們的範例中,會是 Room21 的「outflowmea」 (使用「outflow」關聯性識別) 屬性和 Room22 的「inflowmea」屬性。 以下螢幕擷取畫面顯示時間序列深入解析總管中的最終使用者體驗。 必須注意到,採用此方法會產生重複的資料。

時間序列深入解析總管

下列程式碼片段顯示用戶端應用程式如何使用 Azure Digital Twins API 瀏覽對應項關聯性。

注意

此程式碼片段範例假設讀者熟悉本教學課程的 01 部分,且此程式碼變更是在「ProcessHubToDTEvents」函式內進行。

if (propertyPath.Equals("/Flow"))
{
//Update the flow value property of the flow meter
await AdtUtilities.UpdateTwinProperty(client, twinId, "replace",
propertyPath, "double", propertyValue, log);

//also update the sending end flow
string parentIdOutflow = await AdtUtilities.FindParent(client, twinId,
"outflow", log);
if (parentIdOutflow != null)
await AdtUtilities.UpdateTwinProperty(client, parentIdOutflow, "replace", "outflow", "double", propertyValue, log);
else
log.LogInformation("Unable to find Parent with outflow
relationship for " + twinId );
//and receiving end flow value
string parentIdinflow = await AdtUtilities.FindParent(client, twinId,
"inflow", log);
if (parentIdinflow != null)

await AdtUtilities.UpdateTwinProperty(client, parentIdinflow,
"replace", "inflow", "double", propertyValue, log);
else
log.LogInformation("Unable to find Parent with inflow
relationship for " + twinId);
}

使用 API 更新執行個體欄位

教學課程的本節說明接聽 Azure Digital Twins 中的模型變更,例如建立、刪除對應項,或更新對應項之間的關聯性變更,以及使用時間序列深入解析模型 API 以程式設計方式更新執行個體欄位和階層。 這個更新時間序列深入解析模型的方法通常是透過 Azure 函式來達成。 在 Azure Digital Twins 中,可以將對應項新增或刪除等事件通知路由至下游服務 (例如事件中樞),進而饋送至 Azure 函式。 如需事件路由和篩選的進一步詳細資料,請參閱此處。 本節的其餘部分說明在 Azure 函式中使用時間序列深入解析模型 API 來更新時間序列深入解析模型,以回應 Azure Digital Twins 中的對應項新增 (一種模型變更類型)。

接收和識別對應項新增事件通知

[FunctionName("RouteEventsToTsi")]
public async Task Run([EventGridTrigger]EventGridEvent eventGridEvent)
{
    try
    {
        if (eventGridEvent != null && eventGridEvent.Data != null)
        {
            logger.LogInformation($"EventType: {eventGridEvent.EventType}");
            logger.LogInformation($"EventGridEvent: {JsonConvert.SerializeObject(eventGridEvent)}");

            //Shape event and Send event data to event hub and tsi
            await SendEventToEventHubAsync(eventGridEvent).ConfigureAwait(false);

            //If a new twin was created, update the newly created instance in TSI with info retrieved from ADT
            if (eventGridEvent.EventType == Constants.TwinCreateEventType)
            {
                //retrieve building, floor and room of value twin
                var twinInfo = await RetrieveTwinInfoAsync(eventGridEvent).ConfigureAwait(false);
                //Update Tsi instance with type(sensor type), hierarchy(space hierarchy) and instance fields(twin info retrieved above)
                var instance = await CreateInstanceToSendAsync(twinInfo).ConfigureAwait(false);
                var instanceToUpdate = new List<TimeSeriesInstance>() { instance };
                var response = await tsiClient.TimeSeriesInstances.ExecuteBatchAsync(new InstancesBatchRequest(update: instanceToUpdate)).ConfigureAwait(false);
            }
        }
    }
    catch (Exception ex)
    {
        logger.LogError($"Exception: {ex.Message}");
    }
}

建立時間序列深入解析用戶端並新增執行個體詳細資料

private async Task<TimeSeriesInstance> CreateInstanceToSendAsync(Dictionary<string, string> twinInfo)
{
    try
    {
        tsiClient = await GetTSIClientAsync().ConfigureAwait(false);

        var timeSeriesId = new object[] { twinInfo[Constants.DtId] };
        var instances = await tsiClient.TimeSeriesInstances.ExecuteBatchAsync(
            new InstancesBatchRequest(
                get: new InstancesRequestBatchGetOrDelete(
                    new IList<object>[] { timeSeriesId }))).ConfigureAwait(false);
        var instance = instances.Get.First().Instance;

        if (instance != null)
        {
            instance = await AddHierarchyToInstanceAsync(instance).ConfigureAwait(false);
            instance = await AddTypeToInstanceAsync(instance, twinInfo[Constants.TwinType]).ConfigureAwait(false);

            instance.InstanceFields = new Dictionary<string, object>
                            {
                                { "Building", twinInfo[Constants.BuildingName] },
                                { "Floor", twinInfo[Constants.FloorName] },
                                { "Room", twinInfo[Constants.ParentName] }
                            };

            //If value twin is a sensor value twin, add sensor type instance field to diff from spatial value twin
            if (twinInfo[Constants.ParentType] == Constants.Sensor)
            {
                instance.InstanceFields.Add(Constants.SensorType, twinInfo[Constants.TwinType]);
            }
            return instance;
        }
        else
        {
            logger.LogError($"instance with id {twinInfo[Constants.DtId]} not found");
            return new TimeSeriesInstance();
        }
    }
    catch (Exception e)
    {
        logger.LogError(e.Message);
        throw;
    }
}

將階層資訊套用至執行個體

private async Task<TimeSeriesInstance> AddHierarchyToInstanceAsync(TimeSeriesInstance instance)
{
    if (instance.HierarchyIds == null)
    {
        TimeSeriesHierarchy spacesHierarchy = null;
        try
        {
            var hierarchy = await RunGetHierarchiesAsync(Constants.SpaceHierarchy).ConfigureAwait(false);
            spacesHierarchy = hierarchy.First(h => h.Name.Equals(Constants.SpaceHierarchy));
            instance.HierarchyIds = new List<Guid?>();
            instance.HierarchyIds.Add(spacesHierarchy.Id);
        }
        catch (Exception ex)
        {
            logger.LogWarning($"Hierarchy 'space hierarchy' not found, {ex}");
            throw;
        }
    }
    return instance;
}

下一步

教學課程系列的第三部分示範如何使用時間序列深入解析 API 從 Azure Digital Twins 查詢歷程記錄資料。 這是進行中的工作,區段會在就緒後更新。 同時,鼓勵讀者參閱時間序列深入解析資料查詢 API 文件