إدارة رسم بياني للتوائم الرقمية باستخدام العلاقات

قلب Azure Digital Twins هو الرسم البياني المزدوج الذي يمثل بيئتك بأكملها. يتكون الرسم البياني المزدوج من توائم رقمية فردية متصلة عبر العلاقات. تركز هذه المقالة على إدارة العلاقات والرسم البياني ككل؛ للعمل مع التوائم الرقمية الفردية، راجع إدارة التوائم الرقمية.

بمجرد أن يكون لديك مثيل Azure Digital Twins عامل وقمت بإعداد رمز المصادقة في تطبيق العميل الخاص بك، يمكنك إنشاء وتعديل وحذف التوائم الرقمية وعلاقاتها في مثيل Azure Digital Twins.

المتطلبات الأساسية

للعمل مع Azure Digital Twins في هذه المقالة، ستحتاج إلى مثيل Azure Digital Twins والأذونات المطلوبة لاستخدامه. إذا كان لديك بالفعل مثيل Azure Digital Twins تم إعداده، يمكنك استخدام هذا المثيل والتخطي إلى القسم التالي. وإلا، فاتبع الإرشادات الموجودة في إعداد نمذجة ومصادقة. تحتوي الإرشادات على معلومات لمساعدتك في التحقق من إكمال كل خطوة بنجاح.

بعد إعداد المثيل الخاص بك، دون اسم مضيف المثيل. يمكنك العثور على اسم المضيف في بوابة Azure.

واجهات المطور

تسلط هذه المقالة الضوء على كيفية إكمال عمليات الإدارة المختلفة باستخدام .NET (C#) SDK. يمكنك أيضا صياغة استدعاءات الإدارة نفسها هذه باستخدام حزم SDK للغة الأخرى الموضحة في واجهات برمجة تطبيقات Azure Digital Twins وSDKs.

تتضمن واجهات المطور الأخرى التي يمكن استخدامها لإكمال هذه العمليات ما يلي:

الرسوم المرئية

Azure Digital Twins Explorer هو أداة مرئية لاستكشاف البيانات في الرسم البياني Azure Digital Twins. يمكنك استخدام المستكشف لعرض نماذجك وتوائمك وعلاقاتك والاستعلام عنهما وتحريرهما.

للقراءة عن أداة Azure Digital Twins Explorer، راجع Azure Digital Twins Explorer. للحصول على خطوات مفصلة حول كيفية استخدام ميزاته، راجع استخدام Azure Digital Twins Explorer.

إليك الشكل الذي تبدو عليه المرئيات:

Screenshot of Azure Digital Twins Explorer showing sample models and twins.

إنشاء علاقات

تصف العلاقات كيف ترتبط التوائم الرقمية المختلفة ببعضها البعض، والتي تشكل أساس الرسم البياني التوأم.

يتم تعريف أنواع العلاقات التي يمكن إنشاؤها من توأم (مصدر) إلى آخر (هدف) كجزء من نموذج DTDL للتوأم المصدر. يمكنك إنشاء مثيل لعلاقة باستخدام CreateOrReplaceRelationshipAsync() استدعاء SDK مع التوائم وتفاصيل العلاقة التي تتبع تعريف DTDL.

لإنشاء علاقة، عليك تحديد:

  • معرف التوأم المصدر (srcId في نموذج التعليمات البرمجية أدناه): معرف التوأم حيث تنشأ العلاقة.
  • معرف التوأم الهدف (targetId في نموذج التعليمات البرمجية أدناه): معرف التوأم حيث تصل العلاقة.
  • اسم العلاقة (relName في نموذج التعليمات البرمجية أدناه): النوع العام للعلاقة، شيء مثل يحتوي على.
  • معرف العلاقة (relId في نموذج التعليمات البرمجية أدناه): الاسم المحدد لهذه العلاقة، شيء مثل Relationship1.

يجب أن يكون معرف العلاقة فريدًا داخل التوأم المصدر المحدد. لا يلزم أن تكون فريدة من نوعها على مستوى العالم. على سبيل المثال، بالنسبة إلى Foo التوأم، يجب أن يكون كل معرف علاقة محدد فريدا. ومع ذلك، يمكن أن يكون لشريط مزدوج آخر علاقة صادرة تطابق نفس معرف علاقة Foo.

يوضح نموذج التعليمات البرمجية التالي كيفية إنشاء علاقة في مثيل Azure Digital Twins. يستخدم استدعاء SDK (مميز) داخل أسلوب مخصص قد يظهر في سياق برنامج أكبر.

private async static Task CustomMethod_CreateRelationshipAsync(DigitalTwinsClient client, string srcId, string targetId, string relName, IDictionary<string,object> inputProperties)
{
    var relationship = new BasicRelationship
    {
        TargetId = targetId,
        Name = relName,
        Properties = inputProperties
    };

    try
    {
        string relId = $"{srcId}-{relName}->{targetId}";
        await client.CreateOrReplaceRelationshipAsync<BasicRelationship>(srcId, relId, relationship);
        Console.WriteLine($"Created {relName} relationship successfully. Relationship ID is {relId}.");
    }
    catch (RequestFailedException rex)
    {
        Console.WriteLine($"Create relationship error: {rex.Status}:{rex.Message}");
    }

}

يمكن الآن استدعاء هذه الدالة المخصصة لإنشاء علاقة تحتوي على بالطريقة التالية:

await CustomMethod_CreateRelationshipAsync(client, srcId, targetId, "contains", properties);

إذا كنت ترغب في إنشاء علاقات متعددة، يمكنك تكرار الاستدعاءات إلى نفس الأسلوب، وتمرير أنواع علاقات مختلفة إلى الوسيطة.

لمزيد من المعلومات حول فئة BasicRelationshipالمساعد، راجع واجهات برمجة تطبيقات Azure Digital Twins وSDKs.

إنشاء علاقات متعددة بين التوائم

يمكن تصنيف العلاقات على أنها إما:

  • العلاقات المنتهية ولايته: العلاقات التي تنتمي إلى هذا التوأم والتي تشير إلى الخارج لربطها بالتوائم الأخرى. يتم استخدام طريقة GetRelationshipsAsync() للحصول على العلاقات الخارجية لتوأم.
  • العلاقات الواردة: العلاقات التي تنتمي إلى التوائم الأخرى التي تشير إلى هذا التوأم لإنشاء رابط "وارد". يتم استخدام طريقة GetIncomingRelationshipsAsync() للحصول على العلاقات الواردة من التوأم.

لا يوجد أي قيود على عدد العلاقات التي يمكن أن يكون لديك بين توأمين - يمكنك أن يكون لديك العديد من العلاقات بين التوائم كما تريد.

تعني هذه الحقيقة أنه يمكنك التعبير عن عدة أنواع مختلفة من العلاقات بين توأمين في وقت واحد. على سبيل المثال، يمكن أن يكون لدى Twin A علاقة مخزنة وعلاقة مصنعة مع Twin B.

يمكنك حتى إنشاء مثيلات متعددة من نفس نوع العلاقة بين نفس التوأمين، إذا كنت تريد ذلك. في هذا المثال، يمكن أن يكون لدى Twin A علاقتان مخزنتان مختلفتان مع Twin B، طالما أن العلاقات لها معرفات علاقة مختلفة.

إشعار

سمات DTDL للعلاقات minMultiplicity و maxMultiplicity الخاصة بها غير مدعومة حاليا في Azure Digital Twins - حتى إذا تم تعريفها كجزء من نموذج، فلن يتم فرضها بواسطة الخدمة. لمزيد من المعلومات، راجع ملاحظات DTDL الخاصة بالخدمة.

إنشاء علاقات بشكل مجمع باستخدام واجهة برمجة تطبيقات استيراد المهام

يمكنك استخدام واجهة برمجة تطبيقات استيراد المهام لإنشاء العديد من العلاقات في وقت واحد في استدعاء واجهة برمجة تطبيقات واحد. يتطلب هذا الأسلوب استخدام Azure Blob Storage، بالإضافة إلى أذونات الكتابة في مثيل Azure Digital Twins للعلاقات والمهام المجمعة.

تلميح

تسمح واجهة برمجة تطبيقات استيراد المهام أيضا باستيراد النماذج والتوائم في نفس الاستدعاء، لإنشاء جميع أجزاء الرسم البياني في وقت واحد. لمزيد من المعلومات حول هذه العملية، راجع تحميل النماذج والتوائم والعلاقات بشكل مجمع باستخدام واجهة برمجة تطبيقات استيراد المهام.

لاستيراد العلاقات بشكل مجمع، ستحتاج إلى هيكلة علاقاتك (وأي موارد أخرى مضمنة في مهمة الاستيراد المجمع) كملف NDJSON . Relationships يأتي القسم بعد Twins المقطع، مما يجعله آخر قسم بيانات الرسم البياني في الملف. يمكن أن تشير العلاقات المعرفة في الملف إلى التوائم المحددة في هذا الملف أو الموجودة بالفعل في المثيل، ويمكن أن تتضمن اختياريا تهيئة أي خصائص لها العلاقات.

يمكنك عرض مثال لملف استيراد ومشروع عينة لإنشاء هذه الملفات في مقدمة واجهة برمجة تطبيقات استيراد المهام.

بعد ذلك، يجب تحميل الملف إلى كائن ثنائي كبير الحجم للإلحاق في Azure Blob Storage. للحصول على إرشادات حول كيفية إنشاء حاوية تخزين Azure، راجع إنشاء حاوية. ثم قم بتحميل الملف باستخدام أسلوب التحميل المفضل لديك (بعض الخيارات هي الأمر AzCopy أو Azure CLI أو مدخل Microsoft Azure).

بمجرد تحميل ملف NDJSON إلى الحاوية، احصل على عنوان URL الخاص به داخل حاوية الكائن الثنائي كبير الحجم. ستستخدم هذه القيمة لاحقا في نص استدعاء واجهة برمجة التطبيقات للاستيراد المجمع.

فيما يلي لقطة شاشة تعرض قيمة عنوان URL لملف كائن ثنائي كبير الحجم في مدخل Microsoft Azure:

Screenshot of the Azure portal showing the URL of a file in a storage container.

بعد ذلك، يمكن استخدام الملف في استدعاء واجهة برمجة تطبيقات استيراد المهام. ستوفر عنوان URL لتخزين الكائن الثنائي كبير الحجم لملف الإدخال، بالإضافة إلى عنوان URL جديد لتخزين الكائن الثنائي كبير الحجم للإشارة إلى المكان الذي تريد تخزين سجل الإخراج فيه عند إنشائه بواسطة الخدمة.

قائمة العلاقات

سرد خصائص علاقة واحدة

يمكنك دائما إلغاء تسلسل بيانات العلاقة إلى نوع من اختيارك. للوصول الأساسي إلى علاقة، استخدم النوع BasicRelationship. BasicRelationship تمنحك فئة المساعد أيضا حق الوصول إلى الخصائص المحددة على العلاقة، من خلال IDictionary<string, object>. لسرد الخصائص، يمكنك استخدام:

public async Task ListRelationshipProperties(DigitalTwinsClient client, string twinId, string relId, BasicDigitalTwin twin)
{

    var res = await client.GetRelationshipAsync<BasicRelationship>(twinId, relId);
    BasicRelationship rel = res.Value;
    Console.WriteLine($"Relationship Name: {rel.Name}");
    foreach (string prop in rel.Properties.Keys)
    {
        if (twin.Contents.TryGetValue(prop, out object value))
        {
            Console.WriteLine($"Property '{prop}': {value}");
        }
    }
}

سرد العلاقات الصادرة من التوأم الرقمي

للوصول إلى قائمة العلاقات الصادرة لتوأم معين في الرسم البياني، يمكنك استخدام GetRelationships() الأسلوب مثل هذا:

AsyncPageable<BasicRelationship> rels = client.GetRelationshipsAsync<BasicRelationship>(dtId);

تقوم هذه الطريقة بإرجاع Azure.Pageable<T> أو Azure.AsyncPageable<T>، بناءً على ما إذا كنت تستخدم الإصدار المتزامن أو غير المتزامن من المكالمة.

فيما يلي مثال يسترد قائمة العلاقات. يستخدم استدعاء SDK (مميز) داخل أسلوب مخصص قد يظهر في سياق برنامج أكبر.

private static async Task<List<BasicRelationship>> CustomMethod_FindOutgoingRelationshipsAsync(DigitalTwinsClient client, string dtId)
{
    // Find the relationships for the twin
    
    try
    {
        // GetRelationshipsAsync will throw if an error occurs
        AsyncPageable<BasicRelationship> rels = client.GetRelationshipsAsync<BasicRelationship>(dtId);
        var results = new List<BasicRelationship>();
        await foreach (BasicRelationship rel in rels)
        {
            results.Add(rel);
            Console.WriteLine($"Found relationship: {rel.Id}");

            //Print its properties
            Console.WriteLine($"Relationship properties:");
            foreach(KeyValuePair<string, object> property in rel.Properties)
            {
                Console.WriteLine("{0} = {1}", property.Key, property.Value);
            }
        }

        return results;
    }
    catch (RequestFailedException ex)
    {
        Console.WriteLine($"*** Error {ex.Status}/{ex.ErrorCode} retrieving relationships for {dtId} due to {ex.Message}");
        return null;
    }
}

يمكنك الآن استدعاء هذه الطريقة المخصصة لمعرفة العلاقات المنتهية ولايته للتوائم مثل هذا:

await CustomMethod_FindOutgoingRelationshipsAsync(client, twin_Id);

يمكنك استخدام العلاقات المستردة للانتقال إلى توائم أخرى في الرسم البياني الخاص بك عن طريق قراءة target الحقل من العلاقة التي يتم إرجاعها، واستخدامه كمعرف لاستدعاءك التالي إلى GetDigitalTwin().

سرد العلاقات الواردة إلى التوأم الرقمي

يحتوي Azure Digital Twins أيضا على استدعاء SDK للعثور على جميع العلاقات الواردة إلى توأم معين. غالبا ما يكون SDK مفيدا للتنقل العكسي، أو عند حذف توأم.

إشعار

IncomingRelationship لا ترجع المكالمات النص الكامل للعلاقة. لمزيد من المعلومات حول IncomingRelationship الفئة، راجع وثائقها المرجعية.

ركزت عينة التعليمات البرمجية في القسم السابق على العثور على العلاقات الصادرة من التوأم. يتم تنظيم المثال التالي بشكل مماثل، ولكنه يبحث عن العلاقات الواردة إلى التوأم بدلا من ذلك. يستخدم هذا المثال أيضا استدعاء SDK (مميز) داخل أسلوب مخصص قد يظهر في سياق برنامج أكبر.

private static async Task<List<IncomingRelationship>> CustomMethod_FindIncomingRelationshipsAsync(DigitalTwinsClient client, string dtId)
{
    // Find the relationships for the twin
    
    try
    {
        // GetRelationshipsAsync will throw an error if a problem occurs
        AsyncPageable<IncomingRelationship> incomingRels = client.GetIncomingRelationshipsAsync(dtId);

        var results = new List<IncomingRelationship>();
        await foreach (IncomingRelationship incomingRel in incomingRels)
        {
            results.Add(incomingRel);
            Console.WriteLine($"Found incoming relationship: {incomingRel.RelationshipId}");

            //Print its properties
            Response<BasicRelationship> relResponse = await client.GetRelationshipAsync<BasicRelationship>(incomingRel.SourceId, incomingRel.RelationshipId);
            BasicRelationship rel = relResponse.Value;
            Console.WriteLine($"Relationship properties:");
            foreach(KeyValuePair<string, object> property in rel.Properties)
            {
                Console.WriteLine("{0} = {1}", property.Key, property.Value);
            }
        }
        return results;
    }
    catch (RequestFailedException ex)
    {
        Console.WriteLine($"*** Error {ex.Status}/{ex.ErrorCode} retrieving incoming relationships for {dtId} due to {ex.Message}");
        return null;
    }
}

يمكنك الآن استدعاء هذه الطريقة المخصصة لمعرفة العلاقات الواردة للتوائم مثل هذا:

await CustomMethod_FindIncomingRelationshipsAsync(client, twin_Id);

سرد كافة الخصائص والعلاقات المزدوجة

باستخدام الطرق المذكورة أعلاه لإدراج العلاقات الصادرة والواردة إلى التوأم، يمكنك إنشاء أسلوب يطبع معلومات التوأم الكاملة، بما في ذلك خصائص التوأم وكلا نوعي علاقاته. فيما يلي مثال على أسلوب مخصص يوضح كيفية دمج الأساليب المخصصة أعلاه لهذا الغرض.

private static async Task CustomMethod_FetchAndPrintTwinAsync(string twin_Id, DigitalTwinsClient client)
{
    Response<BasicDigitalTwin> res = await client.GetDigitalTwinAsync<BasicDigitalTwin>(twin_Id);
    await CustomMethod_FindOutgoingRelationshipsAsync(client, twin_Id);
    await CustomMethod_FindIncomingRelationshipsAsync(client, twin_Id);

    return;
}

يمكنك الآن استدعاء هذه الدالة المخصصة كما يلي:

await CustomMethod_FetchAndPrintTwinAsync(srcId, client);

تحديث العلاقات

يتم تحديث العلاقات باستخدام UpdateRelationship الأسلوب .

إشعار

هذا الأسلوب لتحديث خصائص العلاقة. إذا كنت بحاجة إلى تغيير التوأم المصدر أو التوأم الهدف للعلاقة، فستحتاج إلى حذف العلاقة وإعادة إنشاء واحدة باستخدام التوائم الجديدة.

المعلمات المطلوبة لاستدعاء العميل هي:

  • معرف التوأم المصدر (التوأم حيث تنشأ العلاقة).
  • معرف العلاقة المراد تحديثها.
  • مستند JSON Patch يحتوي على الخصائص والقيم الجديدة التي تريد تحديثها.

فيما يلي نموذج قصاصة برمجية توضح كيفية استخدام هذا الأسلوب. يستخدم هذا المثال استدعاء SDK (مميز) داخل أسلوب مخصص قد يظهر في سياق برنامج أكبر.

private async static Task CustomMethod_UpdateRelationshipAsync(DigitalTwinsClient client, string srcId, string relId, Azure.JsonPatchDocument updateDocument)
{

    try
    {
        await client.UpdateRelationshipAsync(srcId, relId, updateDocument);
        Console.WriteLine($"Successfully updated {relId}");
    }
    catch (RequestFailedException rex)
    {
        Console.WriteLine($"Update relationship error: {rex.Status}:{rex.Message}");
    }

}

فيما يلي مثال على استدعاء لهذا الأسلوب المخصص، تمرير مستند JSON Patch مع المعلومات لتحديث خاصية.

var updatePropertyPatch = new JsonPatchDocument();
updatePropertyPatch.AppendAdd("/ownershipUser", "ownershipUser NEW value");
await CustomMethod_UpdateRelationshipAsync(client, srcId, $"{srcId}-contains->{targetId}", updatePropertyPatch);

حذف العلاقات

تحدد المعلمة الأولى المصدر التوأم (التوأم حيث تنشأ العلاقة). المعلمة الأخرى هي معرف العلاقة. أنت بحاجة إلى كل من المعرف المزدوج ومعرف العلاقة، لأن معرفات العلاقة فريدة فقط في نطاق التوأم.

فيما يلي نموذج التعليمات البرمجية الذي يوضح كيفية استخدام هذا الأسلوب. يستخدم هذا المثال استدعاء SDK (مميز) داخل أسلوب مخصص قد يظهر في سياق برنامج أكبر.

private static async Task CustomMethod_DeleteRelationshipAsync(DigitalTwinsClient client, string srcId, string relId)
{
    try
    {
        Response response = await client.DeleteRelationshipAsync(srcId, relId);
        await CustomMethod_FetchAndPrintTwinAsync(srcId, client);
        Console.WriteLine("Deleted relationship successfully");
    }
    catch (RequestFailedException e)
    {
        Console.WriteLine($"Error {e.ErrorCode}");
    }
}

يمكنك الآن استدعاء هذا الأسلوب المخصص لحذف علاقة مثل هذه:

await CustomMethod_DeleteRelationshipAsync(client, srcId, $"{srcId}-contains->{targetId}");

إشعار

إذا كنت ترغب في حذف جميع النماذج والتوائم والعلاقات في مثيل في وقت واحد، فاستخدم واجهة برمجة تطبيقات Delete Jobs.

إنشاء عناصر رسم بياني متعددة في وقت واحد

يصف هذا القسم استراتيجيات لإنشاء رسم بياني بعناصر متعددة في نفس الوقت، بدلا من استخدام استدعاءات API الفردية لتحميل النماذج والتوائم والعلاقات لتحميلها واحدا تلو الآخر.

تحميل النماذج والتوائم والعلاقات بشكل مجمع باستخدام واجهة برمجة تطبيقات استيراد المهام

يمكنك استخدام واجهة برمجة تطبيقات استيراد المهام لتحميل نماذج وتوائم وعلاقات متعددة إلى المثيل الخاص بك في استدعاء واجهة برمجة تطبيقات واحد، مما يؤدي إلى إنشاء الرسم البياني بشكل فعال في وقت واحد. يتطلب هذا الأسلوب استخدام Azure Blob Storage، بالإضافة إلى أذونات الكتابة في مثيل Azure Digital Twins لعناصر الرسم البياني (النماذج والتوائم والعلاقات) والمهام المجمعة.

لاستيراد الموارد بشكل مجمع، ابدأ بإنشاء ملف NDJSON يحتوي على تفاصيل مواردك. يبدأ الملف بقسم Header متبوعا بالأقسام الاختيارية Modelsو Twinsو.Relationships ليس عليك تضمين جميع أنواع بيانات الرسم البياني الثلاثة في الملف، ولكن يجب أن تتبع أي مقاطع موجودة هذا الترتيب. يمكن أن تشير التوائم المعرفة في الملف إلى النماذج المحددة في هذا الملف أو الموجودة بالفعل في المثيل، ويمكن أن تتضمن اختياريا تهيئة خصائص التوأم. يمكن أن تشير العلاقات المعرفة في الملف إلى التوائم المحددة في هذا الملف أو الموجودة بالفعل في المثيل، ويمكن أن تتضمن اختياريا تهيئة خصائص العلاقة.

يمكنك عرض مثال لملف استيراد ومشروع عينة لإنشاء هذه الملفات في مقدمة واجهة برمجة تطبيقات استيراد المهام.

بعد ذلك، يجب تحميل الملف إلى كائن ثنائي كبير الحجم للإلحاق في Azure Blob Storage. للحصول على إرشادات حول كيفية إنشاء حاوية تخزين Azure، راجع إنشاء حاوية. ثم قم بتحميل الملف باستخدام أسلوب التحميل المفضل لديك (بعض الخيارات هي الأمر AzCopy أو Azure CLI أو مدخل Microsoft Azure).

بمجرد تحميل ملف NDJSON إلى الحاوية، احصل على عنوان URL الخاص به داخل حاوية الكائن الثنائي كبير الحجم. ستستخدم هذه القيمة لاحقا في نص استدعاء واجهة برمجة التطبيقات للاستيراد المجمع.

فيما يلي لقطة شاشة تعرض قيمة عنوان URL لملف كائن ثنائي كبير الحجم في مدخل Microsoft Azure:

Screenshot of the Azure portal showing the URL of a file in a storage container.

بعد ذلك، يمكن استخدام الملف في استدعاء واجهة برمجة تطبيقات استيراد المهام. ستوفر عنوان URL لتخزين الكائن الثنائي كبير الحجم لملف الإدخال، بالإضافة إلى عنوان URL جديد لتخزين الكائن الثنائي كبير الحجم للإشارة إلى المكان الذي تريد تخزين سجل الإخراج فيه عند إنشائه بواسطة الخدمة.

استيراد الرسم البياني باستخدام Azure Digital Twins Explorer

Azure Digital Twins Explorer هو أداة مرئية لعرض الرسم البياني المزدوج والتفاعل معه. يحتوي على ميزة لاستيراد ملف رسم بياني بتنسيق JSON أو Excel يمكن أن يحتوي على نماذج وتوائم وعلاقات متعددة.

للحصول على معلومات مفصلة حول استخدام هذه الميزة، راجع استيراد الرسم البياني في وثائق Azure Digital Twins Explorer.

إنشاء توائم وعلاقات من ملف CSV

في بعض الأحيان، قد تحتاج إلى إنشاء تسلسلات هرمية مزدوجة من البيانات المخزنة في قاعدة بيانات مختلفة، أو في جدول بيانات أو ملف CSV. يوضح هذا القسم كيفية قراءة البيانات من ملف CSV وإنشاء رسم بياني مزدوج منه.

ضع في اعتبارك جدول البيانات التالي، الذي يصف مجموعة من التوائم الرقمية والعلاقات. يجب أن تكون النماذج المشار إليها في هذا الملف موجودة بالفعل في مثيل Azure Digital Twins.

معرف النموذج معرف التوأم (يجب أن يكون فريدا) اسم العلاقة معرف التوأم الهدف بيانات التأين المزدوج
dtmi:example:Floor; 1 Floor1 تحتوي على Room1
dtmi:example:Floor; 1 Floor0 تحتوي على Room0
dtmi:example:Room; 1 Room1 {"درجة الحرارة": 80}
dtmi:example:Room; 1 Room0 {"درجة الحرارة": 70}

تتمثل إحدى طرق الحصول على هذه البيانات في Azure Digital Twins في تحويل الجدول إلى ملف CSV. بمجرد تحويل الجدول، يمكن كتابة التعليمات البرمجية لتفسير الملف إلى أوامر لإنشاء توائم وعلاقات. يوضح نموذج التعليمات البرمجية التالي قراءة البيانات من ملف CSV وإنشاء رسم بياني مزدوج في Azure Digital Twins.

في التعليمات البرمجية أدناه، يسمى ملف CSV data.csv، وهناك عنصر نائب يمثل اسم المضيف لمثيل Azure Digital Twins. كما تستخدم العينة العديد من الحزم التي يمكنك إضافتها إلى مشروعك للمساعدة في هذه العملية.

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Azure;
using Azure.DigitalTwins.Core;
using Azure.Identity;

namespace creating_twin_graph_from_csv
{
    class Program
    {
        static async Task Main(string[] args)
        {
            var relationshipRecordList = new List<BasicRelationship>();
            var twinList = new List<BasicDigitalTwin>();
            List<List<string>> data = ReadData();
            DigitalTwinsClient client = CreateDtClient();

            // Interpret the CSV file data, by each row
            foreach (List<string> row in data)
            {
                string modelID = row.Count > 0 ? row[0].Trim() : null;
                string srcID = row.Count > 1 ? row[1].Trim() : null;
                string relName = row.Count > 2 ? row[2].Trim() : null;
                string targetID = row.Count > 3 ? row[3].Trim() : null;
                string initProperties = row.Count > 4 ? row[4].Trim() : null;
                Console.WriteLine($"ModelID: {modelID}, TwinID: {srcID}, RelName: {relName}, TargetID: {targetID}, InitData: {initProperties}");
                var props = new Dictionary<string, object>();
                // Parse properties into dictionary (left out for compactness)
                // ...

                // Null check for source and target IDs
                if (!string.IsNullOrWhiteSpace(srcID) && !string.IsNullOrWhiteSpace(targetID) && !string.IsNullOrWhiteSpace(relName))
                {
                    relationshipRecordList.Add(
                        new BasicRelationship
                        {
                            SourceId = srcID,
                            TargetId = targetID,
                            Name = relName,
                        });
                }

                if (!string.IsNullOrWhiteSpace(srcID) && !string.IsNullOrWhiteSpace(modelID))
                twinList.Add(
                    new BasicDigitalTwin
                    {
                        Id = srcID,
                        Metadata = { ModelId = modelID },
                        Contents = props,
                    });
            }

            // Create digital twins
            foreach (BasicDigitalTwin twin in twinList)
            {
                try
                {
                    await client.CreateOrReplaceDigitalTwinAsync<BasicDigitalTwin>(twin.Id, twin);
                    Console.WriteLine("Twin is created");
                }
                catch (RequestFailedException ex)
                {
                    Console.WriteLine($"Error {ex.Status}: {ex.Message}");
                }
            }

            // Create relationships between the twins
            foreach (BasicRelationship rec in relationshipRecordList)
            {
                string relId = $"{rec.SourceId}-{rec.Name}->{rec.TargetId}";
                try
                {
                    await client.CreateOrReplaceRelationshipAsync<BasicRelationship>(rec.SourceId, relId, rec);
                    Console.WriteLine($"Relationship {relId} is created");
                }
                catch (RequestFailedException ex)
                {
                    Console.WriteLine($"Error creating relationship {relId}. {ex.Status}: {ex.Message}");
                }
            }
        }

        // Method to ingest data from the CSV file
        public static List<List<string>> ReadData()
        {
            string path = "<path-to>/data.csv";
            string[] lines = System.IO.File.ReadAllLines(path);
            var data = new List<List<string>>();
            int count = 0;
            foreach (string line in lines)
            {
                if (count++ == 0)
                    continue;
                var cols = new List<string>();
                string[] columns = line.Split(',');
                foreach (string column in columns)
                {
                    cols.Add(column);
                }
                data.Add(cols);
            }
            return data;
        }

        // Method to create the digital twins client
        private static DigitalTwinsClient CreateDtClient()
        {
            string adtInstanceUrl = "https://<your-instance-hostname>";
            var credentials = new DefaultAzureCredential();
            return new DigitalTwinsClient(new Uri(adtInstanceUrl), credentials);
        }
    }
}

نموذج رسم بياني مزدوج قابل للتشغيل

تستخدم القصاصة البرمجية القابلة للتشغيل التالية عمليات العلاقة من هذه المقالة لإنشاء رسم بياني مزدوج من التوائم الرقمية والعلاقات.

إعداد نموذج ملفات المشروع

يستخدم القصاصة البرمجية اثنين من نماذج التعريف، Room.json و Floor.json. لتنزيل ملفات النموذج حتى تتمكن من استخدامها في التعليمات البرمجية الخاصة بك، استخدم هذه الارتباطات للانتقال مباشرة إلى الملفات في GitHub. بعد ذلك، انقر بزر الماوس الأيمن فوق أي مكان على الشاشة، وحدد حفظ باسم في قائمة النقر بزر الماوس الأيمن في المستعرض، واستخدم نافذة حفظ باسم لحفظ الملفات ك Room.json و Floor.json.

بعد ذلك، قم بإنشاء مشروع تطبيق وحدة تحكم جديد في Visual Studio أو المحرر الذي تختاره.

ثم انسخ التعليمات البرمجية التالية للعينة القابلة للتشغيل في مشروعك:

using System;
using System.Threading.Tasks;
using System.IO;
using System.Collections.Generic;
using Azure;
using Azure.DigitalTwins.Core;
using Azure.Identity;

namespace DigitalTwins_Samples
{
    public class GraphOperationsSample
    {
        public static async Task Main(string[] args)
        {
            Console.WriteLine("Hello World!");

            // Create the Azure Digital Twins client for API calls
            DigitalTwinsClient client = createDtClient();
            Console.WriteLine($"Service client created – ready to go");
            Console.WriteLine();

            // Upload models
            Console.WriteLine($"Upload models");
            Console.WriteLine();
            string dtdl = File.ReadAllText("<path-to>/Room.json");
            string dtdl1 = File.ReadAllText("<path-to>/Floor.json");
            var models = new List<string>
            {
                dtdl,
                dtdl1,
            };
            // Upload the models to the service
            await client.CreateModelsAsync(models);

            // Create new (Floor) digital twin
            var floorTwin = new BasicDigitalTwin();
            string srcId = "myFloorID";
            floorTwin.Metadata.ModelId = "dtmi:example:Floor;1";
            // Floor twins have no properties, so nothing to initialize
            // Create the twin
            await client.CreateOrReplaceDigitalTwinAsync<BasicDigitalTwin>(srcId, floorTwin);
            Console.WriteLine("Twin created successfully");

            // Create second (Room) digital twin
            var roomTwin = new BasicDigitalTwin();
            string targetId = "myRoomID";
            roomTwin.Metadata.ModelId = "dtmi:example:Room;1";
            // Initialize properties
            roomTwin.Contents.Add("Temperature", 35.0);
            roomTwin.Contents.Add("Humidity", 55.0);
            // Create the twin
            await client.CreateOrReplaceDigitalTwinAsync<BasicDigitalTwin>(targetId, roomTwin);
            
            // Create relationship between them
            var properties = new Dictionary<string, object>
            {
                { "ownershipUser", "ownershipUser original value" },
            };
            // <UseCreateRelationship>
            await CustomMethod_CreateRelationshipAsync(client, srcId, targetId, "contains", properties);
            // </UseCreateRelationship>
            Console.WriteLine();

            // Update relationship's Name property
            // <UseUpdateRelationship>
            var updatePropertyPatch = new JsonPatchDocument();
            updatePropertyPatch.AppendAdd("/ownershipUser", "ownershipUser NEW value");
            await CustomMethod_UpdateRelationshipAsync(client, srcId, $"{srcId}-contains->{targetId}", updatePropertyPatch);
            // </UseUpdateRelationship>
            Console.WriteLine();

            //Print twins and their relationships
            Console.WriteLine("--- Printing details:");
            Console.WriteLine($"Outgoing relationships from source twin, {srcId}:");
            // <UseFetchAndPrint>
            await CustomMethod_FetchAndPrintTwinAsync(srcId, client);
            // </UseFetchAndPrint>
            Console.WriteLine();
            Console.WriteLine($"Incoming relationships to target twin, {targetId}:");
            await CustomMethod_FetchAndPrintTwinAsync(targetId, client);
            Console.WriteLine("--------");
            Console.WriteLine();

            // Delete the relationship
            // <UseDeleteRelationship>
            await CustomMethod_DeleteRelationshipAsync(client, srcId, $"{srcId}-contains->{targetId}");
            // </UseDeleteRelationship>
            Console.WriteLine();

            // Print twins and their relationships again
            Console.WriteLine("--- Printing details (after relationship deletion):");
            Console.WriteLine("Outgoing relationships from source twin:");
            await CustomMethod_FetchAndPrintTwinAsync(srcId, client);
            Console.WriteLine();
            Console.WriteLine("Incoming relationships to target twin:");
            await CustomMethod_FetchAndPrintTwinAsync(targetId, client);
            Console.WriteLine("--------");
            Console.WriteLine();
        }

        private static DigitalTwinsClient createDtClient()
        {
            string adtInstanceUrl = "https://<your-instance-hostname>";
            var credentials = new DefaultAzureCredential();
            var client = new DigitalTwinsClient(new Uri(adtInstanceUrl), credentials);
            return client;
        }

        // <CreateRelationshipMethod>
        private async static Task CustomMethod_CreateRelationshipAsync(DigitalTwinsClient client, string srcId, string targetId, string relName, IDictionary<string,object> inputProperties)
        {
            var relationship = new BasicRelationship
            {
                TargetId = targetId,
                Name = relName,
                Properties = inputProperties
            };

            try
            {
                string relId = $"{srcId}-{relName}->{targetId}";
                await client.CreateOrReplaceRelationshipAsync<BasicRelationship>(srcId, relId, relationship);
                Console.WriteLine($"Created {relName} relationship successfully. Relationship ID is {relId}.");
            }
            catch (RequestFailedException rex)
            {
                Console.WriteLine($"Create relationship error: {rex.Status}:{rex.Message}");
            }

        }
        // </CreateRelationshipMethod>

        // <UpdateRelationshipMethod>
        private async static Task CustomMethod_UpdateRelationshipAsync(DigitalTwinsClient client, string srcId, string relId, Azure.JsonPatchDocument updateDocument)
        {

            try
            {
                await client.UpdateRelationshipAsync(srcId, relId, updateDocument);
                Console.WriteLine($"Successfully updated {relId}");
            }
            catch (RequestFailedException rex)
            {
                Console.WriteLine($"Update relationship error: {rex.Status}:{rex.Message}");
            }

        }
        // </UpdateRelationshipMethod>

        // <FetchAndPrintMethod>
        private static async Task CustomMethod_FetchAndPrintTwinAsync(string twin_Id, DigitalTwinsClient client)
        {
            Response<BasicDigitalTwin> res = await client.GetDigitalTwinAsync<BasicDigitalTwin>(twin_Id);
            // <UseFindOutgoingRelationships>
            await CustomMethod_FindOutgoingRelationshipsAsync(client, twin_Id);
            // </UseFindOutgoingRelationships>
            // <UseFindIncomingRelationships>
            await CustomMethod_FindIncomingRelationshipsAsync(client, twin_Id);
            // </UseFindIncomingRelationships>

            return;
        }
        // </FetchAndPrintMethod>

        // <FindOutgoingRelationshipsMethod>
        private static async Task<List<BasicRelationship>> CustomMethod_FindOutgoingRelationshipsAsync(DigitalTwinsClient client, string dtId)
        {
            // Find the relationships for the twin
            
            try
            {
                // GetRelationshipsAsync will throw if an error occurs
                // <GetRelationshipsCall>
                AsyncPageable<BasicRelationship> rels = client.GetRelationshipsAsync<BasicRelationship>(dtId);
                // </GetRelationshipsCall>
                var results = new List<BasicRelationship>();
                await foreach (BasicRelationship rel in rels)
                {
                    results.Add(rel);
                    Console.WriteLine($"Found relationship: {rel.Id}");

                    //Print its properties
                    Console.WriteLine($"Relationship properties:");
                    foreach(KeyValuePair<string, object> property in rel.Properties)
                    {
                        Console.WriteLine("{0} = {1}", property.Key, property.Value);
                    }
                }

                return results;
            }
            catch (RequestFailedException ex)
            {
                Console.WriteLine($"*** Error {ex.Status}/{ex.ErrorCode} retrieving relationships for {dtId} due to {ex.Message}");
                return null;
            }
        }
        // </FindOutgoingRelationshipsMethod>

        // <FindIncomingRelationshipsMethod>
        private static async Task<List<IncomingRelationship>> CustomMethod_FindIncomingRelationshipsAsync(DigitalTwinsClient client, string dtId)
        {
            // Find the relationships for the twin
            
            try
            {
                // GetRelationshipsAsync will throw an error if a problem occurs
                AsyncPageable<IncomingRelationship> incomingRels = client.GetIncomingRelationshipsAsync(dtId);

                var results = new List<IncomingRelationship>();
                await foreach (IncomingRelationship incomingRel in incomingRels)
                {
                    results.Add(incomingRel);
                    Console.WriteLine($"Found incoming relationship: {incomingRel.RelationshipId}");

                    //Print its properties
                    Response<BasicRelationship> relResponse = await client.GetRelationshipAsync<BasicRelationship>(incomingRel.SourceId, incomingRel.RelationshipId);
                    BasicRelationship rel = relResponse.Value;
                    Console.WriteLine($"Relationship properties:");
                    foreach(KeyValuePair<string, object> property in rel.Properties)
                    {
                        Console.WriteLine("{0} = {1}", property.Key, property.Value);
                    }
                }
                return results;
            }
            catch (RequestFailedException ex)
            {
                Console.WriteLine($"*** Error {ex.Status}/{ex.ErrorCode} retrieving incoming relationships for {dtId} due to {ex.Message}");
                return null;
            }
        }
        // </FindIncomingRelationshipsMethod>

        // <DeleteRelationshipMethod>
        private static async Task CustomMethod_DeleteRelationshipAsync(DigitalTwinsClient client, string srcId, string relId)
        {
            try
            {
                Response response = await client.DeleteRelationshipAsync(srcId, relId);
                await CustomMethod_FetchAndPrintTwinAsync(srcId, client);
                Console.WriteLine("Deleted relationship successfully");
            }
            catch (RequestFailedException e)
            {
                Console.WriteLine($"Error {e.ErrorCode}");
            }
        }
        // </DeleteRelationshipMethod>
    }
}

إشعار

توجد حاليا مشكلة معروفة تؤثر على DefaultAzureCredential فئة برنامج التضمين قد تؤدي إلى حدوث خطأ أثناء المصادقة. إذا واجهت هذه المشكلة، يمكنك محاولة إنشاء DefaultAzureCredential مثيل باستخدام المعلمة الاختيارية التالية لحلها: new DefaultAzureCredential(new DefaultAzureCredentialOptions { ExcludeSharedTokenCacheCredential = true });

لمزيد من المعلومات حول هذه المشكلة، راجع المشكلات المعروفة في Azure Digital Twins.

تكوين المشروع

بعد ذلك، أكمل الخطوات التالية لتكوين التعليمات البرمجية للمشروع:

  1. أضف ملفات Room.json و Floor.jsonالتي قمت بتنزيلها مسبقا إلى مشروعك، واستبدل <path-to> العناصر النائبة في التعليمات البرمجية لإخبار البرنامج بمكان العثور عليها.

  2. استبدل العنصر النائب <your-instance-hostname> باسم مضيف مثيل Azure Digital Twins.

  3. أضف تبعيتين إلى مشروعك ستكون هناك حاجة إليهما للعمل مع Azure Digital Twins. الأول هو حزمة Azure Digital Twins SDK ل .NET، والثاني يوفر أدوات للمساعدة في المصادقة مقابل Azure.

    dotnet add package Azure.DigitalTwins.Core
    dotnet add package Azure.Identity
    

ستحتاج أيضا إلى إعداد بيانات الاعتماد المحلية إذا كنت تريد تشغيل العينة مباشرة. يستعرض القسم التالي هذه العملية.

إعداد بيانات اعتماد Azure المحلية

تستخدم هذه العينة DefaultAzureCredential (جزء من مكتبة Azure.Identity) لمصادقة المستخدمين مع مثيل Azure Digital Twins عند تشغيله على جهازك المحلي. لمزيد من المعلومات حول الطرق المختلفة التي يمكن لتطبيق العميل مصادقتها باستخدام Azure Digital Twins، راجع كتابة تعليمات برمجية لمصادقة التطبيق.

سيبحث النموذج باستخدام DefaultAzureCredential عن بيانات اعتماد في بيئتك المحلية، مثل تسجيل الدخول إلى Microsoft Azure في Azure CLI محلي أو في Visual Studio أو Visual Studio Code. ولهذا السبب، يجب تسجيل الدخول محلياً إلى Microsoft Azure من خلال أحد هذه الآليات المخصصة لإعداد بيانات الاعتماد للنموذج.

إذا كنت تستخدم Visual Studio أو Visual Studio Code لتشغيل نماذج التعليمات البرمجية، فتأكد من تسجيل الدخول إلى هذا المحرر بنفس بيانات اعتماد Azure التي تريد استخدامها للوصول إلى مثيل Azure Digital Twins. إذا كنت تستخدم نافذة CLI محلية az login ، فقم بتشغيل الأمر لتسجيل الدخول إلى حساب Azure الخاص بك. بعد ذلك، عند تشغيل نموذج التعليمات البرمجية، يجب أن تتم مصادقتك تلقائيا.

تشغيل تطبيق العرض التوضيحي

الآن بعد أن أكملت الإعداد، يمكنك تشغيل نموذج مشروع التعليمات البرمجية.

فيما يلي إخراج وحدة التحكم للبرنامج:

Screenshot of the console output showing the twin details with incoming and outgoing relationships of the twins.

تلميح

الرسم البياني المزدوج هو مفهوم إنشاء علاقات بين التوائم. إذا كنت تريد عرض التمثيل المرئي للرسم البياني المزدوج، فشاهد قسم المرئيات في هذه المقالة.

الخطوات التالية

تعرف على الاستعلام عن الرسم البياني المزدوج Azure Digital Twins: