وثائق أصول Core

توثيق محرك حالة العالم الحتمي ومراجع API.

وثائق بوابة القرار

خلية الروبوتات: فرز الحزام الناقل بحالة حتمية

يتتبع روبوت واحد العناصر على حزام ناقل متحرك، ويقوم بمصالحة تحديثات المستشعر، وينفذ عملية الالتقاط/الترتيب في صناديق الشبكة مع إعادة تشغيل حتمية.

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

المشكلة: عندما تتعارض أجهزة الاستشعار والمخططون

في روبوتات الإنتاج، يقوم نظام الرؤية لديك بالإبلاغ عن موقع واحد، بينما يقول جهاز التشفير لديك شيئًا آخر، ويفترض المخطط لديك شيئًا ثالثًا. عندما تفشل عملية الالتقاط، لديك ثلاثة سجلات متضاربة ولا مصدر واحد للحقيقة. هل كان ذلك:

  • هل هناك انحراف في المستشعر أثناء حركة الناقل؟
  • هل هناك موقف راكد في مخطط الحركة؟
  • هل هناك حالة سباق بين تحديث الرؤية وأمر الالتقاط؟

بدون حالة حتمية، يصبح تصحيح الأخطاء علم الآثار. مع AssetCore، كل تغيير في الحالة هو التزام مؤرخ وقابل للتدقيق. تصحيحات المستشعرات، تحديثات المخطط، وأوامر التنفيذ جميعها تمر عبر سجل موثوق واحد. عندما يحدث فشل، يمكنك إعادة تشغيل التسلسل الدقيق ورؤية ما حدث - بدون تخمين.

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

لماذا هذا مهم

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

نموذج النظام

  • الناقل: مواضع مستمرة بُعد واحد مع إحداثيات ثابتة.
  • الحاويات: حاوية شبكة تفرض قواعد الإشغال.
  • القابض: حاوية فتحة بحيث تكون الحركات المحددة للفتحات حتمية.
  • وضع الروبوت: حاوية ثنائية الأبعاد مستمرة للموقع والدوران.
  • الفئات والأشكال: مسجلة بحيث يتم تطبيق قواعد التصادم والتوزيع.
  • الوحدات: مم (fixed_point_scale=1000).

نظرة عامة على سير العمل

  1. الإعداد - تسجيل الفئات/الأشكال وإنشاء الحاويات.
  2. البذور - إنشاء وضع الروبوت، العنصر الوارد، والحاوية المحجوزة.
  3. تقدم الناقل - نقل العنصر بناءً على سرعة الناقل وعدد نقرات المشفر.
  4. تسوية الناقل - تطبيق تصحيح المستشعر على موضع العنصر.
  5. تحريك الروبوت - تحديث وضع الروبوت في بعدين مستمرين.
  6. اختيار - نقل العنصر من الناقل إلى القابض.
  7. تم حظر المكان - تعيد الحاوية المحظورة POSITION_OCCUPIED.
  8. شبكة خالية - ابحث عن نقطة الشبكة المتاحة التالية.
  9. المكان - ضع العنصر في ركيزة الشبكة الفارغة.
  10. إعادة وضع - إعادة وضع العنصر لتأكيد عدم التكرار.

تصميم النظام: ما اخترناه ولماذا

هذا السيناريو يتخذ خيارات تصميم محددة تعكس نموذج الحالة الحتمي لـ AssetCore. إليك ما قمنا بتحسينه وما تنازلنا عنه.

الخيار: إحداثيات النقاط الثابتة

الاختيار: جميع المواقع تستخدم أعداد صحيحة بنقطة ثابتة (fixed_point_scale=1000 تعني 1 وحدة = 1 مم).

  • لماذا: العمليات الحسابية ذات النقطة العائمة غير حتمية عبر وحدات المعالجة المركزية - قد يت diverge الإعادة بسبب اختلافات التقريب. تضمن النقطة الثابتة إعادة تشغيل متطابقة بايت.
  • التجارة: يجب عليك اختيار الدقة مسبقًا (1000 = 0.001mm دقة). بالنسبة لمعظم الروبوتات، فإن دقة المليمتر كافية.
  • عندما يكون الأمر مهمًا: يعتمد تصحيح الأخطاء عبر الزمن، واستعادة الكوارث، وتدقيق الامتثال جميعها على إعادة التشغيل الحتمية.

الخيار: تسوية المستشعر كالتزامات منفصلة

الاختيار: الخطوة 3 (تحديث الترميز) والخطوة 4 (تصحيح الرؤية) هما التزامات منفصلة.

  • لماذا: كل تغيير في الحالة هو قرار صريح له بياناته الوصفية الخاصة. إذا كانت ثقة الرؤية منخفضة، يمكنك الاستعلام عن التصحيحات التي تم تطبيقها وإعادة تشغيلها بدونها.
  • التجارة المتبادلة: عمليتان بدلاً من واحدة (زيادة طفيفة في زمن الانتظار، عادةً أقل من 1 مللي ثانية لكل عملية).
  • عندما يكون الأمر مهمًا: تصحيح انحراف المستشعر، التحقق من دقة نظام الرؤية، إثبات الامتثال.

الخيار: اكتشاف النزاع قبل تغيير الحالة

الاختيار: الخطوة 7 تكشف عن POSITION_OCCUPIED في وقت التحقق (L2)، قبل أي تغيير في الحالة.

  • لماذا: لا توجد التزامات جزئية، لا تنظيف، لا “معاملات تعويضية.” فشل التحقق رخيص (لا يوجد إدخال/إخراج)، وتبقى الحالة نظيفة.
  • التجارة المتبادلة: يجب على المخطط الاستعلام عن المواقع الشاغرة (الخطوة 8) بعد حدوث تعارض. سيفشل القفل المتفائل أقل في كثير من الأحيان ولكنه سيتطلب منطق التراجع.
  • عندما يكون الأمر مهمًا: خلايا متعددة الروبوتات مع اختيارات/أماكن متزامنة. يضمن كاتب واحد لكل مساحة اسم في AssetCore اكتشاف النزاعات بشكل حتمي.

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

الاختيار: مفاتيح التكرار الاختياري اختيارية ولكن، عند وجودها، تقوم بإزالة التكرار في المحاولات بناءً على تجزئة معيارية لحزمة الطلب الكاملة.

  • لماذا: نفس المفتاح + نفس الحمولة تعيد الاستجابة المخزنة؛ نفس المفتاح + حمولة مختلفة يتم رفضها. هذا يمنع تكرار التزامات الحركة تحت إعادة المحاولة.
  • التجارة: يجب على الخدمة الموثوقة أن تولد أو تستخرج مفاتيح مستقرة لكل تفاعل ويجب ألا تقوم بتمرير مفاتيح العملاء غير الموثوق بها.
  • عندما يكون الأمر مهمًا: تسليم مرة واحدة على الأقل، إعادة المحاولة عبر الروابط غير المستقرة، عمال الطابور، والتبديل بين المناطق المتعددة.

ما لا نتعامل معه (ولماذا)

  • تخطيط الحركة: يسجل AssetCore تحديثات الوضع، ولكنه لا يحسب المسارات. قم بالتكامل مع مخطط الحركة الخاص بك (MoveIt، OMPL، مخصص) واحتفظ بالأوضاع المخططة.
  • التحكم في الوقت الحقيقي: AssetCore مخصص للسلطة الحكومية، وليس لدورات التحكم بتردد 1kHz. استخدمه لتنسيق الخطط عالية المستوى وتسجيل نتائج التنفيذ.
  • دمج المستشعرات: يقوم AssetCore بتخزين تصحيحات المستشعرات (الخطوة 4)، لكنه لا يحسبها. يوفر نظام الرؤية الخاص بك التصحيح؛ يجعل AssetCore هذا التصحيح موثوقًا وقابلًا لإعادة التشغيل.

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

دلالات التكرار

مفاتيح التكرار اختيارية وتتحكم فقط في إزالة التكرار عند إعادة المحاولة. عندما يكون المفتاح موجودًا، يقوم Asset Core بتجزئة الحمولة الكاملة للطلب ويستخدم (namespace، key، hash) لتحديد:

  • نفس المفتاح + نفس الحمولة: إرجاع الاستجابة المخزنة (بدون إعادة التنفيذ).
  • نفس المفتاح + حمولة مختلفة: الرفض مع 409 تعارض.
  • فشل التحقق لا يحتفظ بالمفتاح؛ إعادة الاستخدام بعد التصحيح صالحة.
  • No key: request executes normally, retries re-run. لا مفتاح: يتم تنفيذ الطلب بشكل طبيعي، وتُعاد المحاولات. في نشرات الروبوتات، تقوم خدمة التنسيق بإنشاء أو اشتقاق المفاتيح ويجب ألا تقوم بإعادة توجيه مفاتيح العملاء غير الموثوق بها.

كيفية قراءة الدليل

  • يتم سرد قراءات/تدفقات التحقق بشكل منفصل بعد الخطوات.
  • تستخدم خطوات العملية الفردية مساعدي الإجراءات من أجل سهولة القراءة.
  • تظل خطوات العمليات المتعددة كاستدعاءات التزام للحفاظ على الذرية.
  • يستخدم Rust HTTP دائمًا نقطة النهاية الخاصة بالالتزام مع عملية واحدة أو أكثر.

أدوات السيناريو

أحدث تحقق

  • معرف التشغيل: 2026-01-19T18-43-00Z-robotics_cell
  • Status: تم النجاح
  • جذر تجزئة النزاهة: 53624642c9af8f6caf0e4138541c948e0f5bd09b95d480e5e9e233d7416c3d32
  • Checks:
    • {‘kind’: ‘structural_invariants’}: تم اجتيازه
    • {‘kind’: ‘world_seq_monotonic’}: تم النجاح
    • {‘kind’: ‘global_seq_monotonic’}: تم النجاح
    • {‘kind’: ‘replay_idempotent’}: تم المرور
    • {'kind': 'commit_log_integrity', 'expected_chain_hash': 'db3c030f7451ac6b5c4abfe5af7f08f3df463f6991cd19adb0beeb643781f455', 'expected_prev_chain_hash': '402bf8dde049d865bc48a2af2d9d5d9ab1e996b6da0dad5bb56118adccd5c1a2', 'expected_start_offset': 1, 'expected_end_offset': 7, 'expected_entry_count': 7, 'expected_last_global_seq': 23}: تم النجاح
    • {‘kind’: ‘runpack_integrity’}: ناجح

تنزيلات Runpack

لقطة سيناريو

{
  "scenario_id": "robotics_cell",
  "version": "0.1.0",
  "namespace_id": 1,
  "containers": {
    "conveyor": "container-31001",
    "grid": "container-31002",
    "gripper": "container-31003",
    "robot_pose": "container-31004"
  },
  "classes": {
    "item": "class-41001",
    "robot_pose": "class-41002"
  },
  "units": {
    "fixed_point_scale": 1000,
    "length_unit": "mm",
    "time_unit": "ms"
  }
}

فرع الفشل

POSITION_OCCUPIED -> الشبكة_فارغة -> إعادة المحاولة بنفس مفتاح التكرار

سجل التدقيق (اختياري)

تلتقط النسخة النصية أزواج الطلبات والاستجابات الدقيقة التي تم إصدارها خلال التشغيل. هنا مقتطف مضغوط يمكنك استخدامه للتحقق من الحتمية وانتقال البيانات الوصفية:

{
  "kind": "commit",
  "name": "conveyor-reconcile",
  "request": {
    "path": "/v1/write/namespaces/1/commit",
    "idempotency_key": "robotics-cell-reconcile",
    "actor_id": "robotics-cell",
    "metadata": {
      "job_id": "job-robotics-001",
      "sensor_ts_ms": 1725000001250,
      "step": "conveyor-reconcile",
      "trace_id": "trace-robotics-001",
      "vision_confidence": 0.78
    },
    "operations": [
      "MoveInstance"
    ]
  },
  "response": {
    "commit_id": "00000000000000000000000000000002",
    "world_seq_start": 15,
    "world_seq_end": 16,
    "event_count": 2
  }
}

ملاحظة ما قبل الرحلة

  • تقوم استدعاءات الإجراء بتقديم الالتزامات بشكل افتراضي.
  • قم بتعيين ActionOptions(preflight=True) أو استدعاء assetcore_commit_preflight للتحقق من الصحة.

إعداد SDK

from assetcore_sdk import AssetCoreClient
from assetcore_sdk.actions import ActionOptions
from assetcore_sdk.operations import (
    AddInstance,
    CreateContainer,
    MoveInstance,
    RegisterClass,
    RegisterClassContinuousShape1d,
    RegisterClassContinuousShape2d,
    RegisterClassShape,
)

# Snippets assume an async context (e.g., inside async def main()).
write_client = AssetCoreClient(
    base_url="http://localhost:8080",
    api_key="WRITE_API_KEY",
)
read_client = AssetCoreClient(
    base_url="http://localhost:8081",
    api_key="READ_API_KEY",
)

دليل المستخدم

خطوة 1: الإعداد

تسجيل الفصول/الأشكال وإنشاء الحاويات.

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

الشروط المسبقة:

  • لا شيء. هذا هو الالتزام الابتدائي.

ما الذي ينشئه هذا الالتزام:

  • ناقل (مستمر 1D)، صناديق (شبكة)، قاطعة (فتحات)، وضع الروبوت (مستمر 2D).
  • فئات العناصر والروبوتات مع أشكال شبكية ومستمرّة.

تغيير الحالة المتوقع:

  • يتم تسجيل الحاويات والأشكال حتى يمكن التحقق من عمليات MoveInstance المستقبلية.

العمليات

  • CreateContainer (x4) - ينشئ حاوية (منطقة ذاكرة منظمة) بالنوع المطلوب.
  • RegisterClass (x2) - يسجل تعريف فئة بحيث يمكن للعمليات المستقبلية الإشارة إليه.
  • RegisterClassShape - يسجل بصمة شكل الشبكة لفئة أو نوع فئة.
  • RegisterClassContinuousShape1d - يسجل مدى مستمر 1D لفئة أو نوع فئة.
  • RegisterClassContinuousShape2d - يسجل مستطيلًا مستمرًا ثنائي الأبعاد لفئة أو نوع فئة.
await write_client.commit_operations(
    [
        CreateContainer(
            kind={
                "bucket_cell_size": 1000,
                "max_x": 39999,
                "min_x": 0,
                "quantization_inv": 1000,
                "type": "continuous_line_1d",
            },
            policies=None,
            external_id="container-31001",
            owner_external_id="owner-77",
        ),
        CreateContainer(
            kind={"capacity": 16, "grid_width": 4, "type": "grid"},
            policies=None,
            external_id="container-31002",
            owner_external_id="owner-77",
        ),
        CreateContainer(
            kind={"count": 2, "type": "slots"},
            policies=None,
            external_id="container-31003",
            owner_external_id="owner-77",
        ),
        CreateContainer(
            kind={
                "bucket_cell_size": 1000,
                "max_x": 9999,
                "max_y": 9999,
                "min_x": 0,
                "min_y": 0,
                "quantization_inv": 1000,
                "type": "continuous_grid_2d",
            },
            policies=None,
            external_id="container-31004",
            owner_external_id="owner-77",
        ),
        RegisterClass(
            request={
                "behavior": {"balance_scale": 1},
                "class_id": "class-41001",
                "flags": 0,
                "name": "robotics-item-41001",
            },
        ),
        RegisterClass(
            request={
                "behavior": {"balance_scale": 1},
                "class_id": "class-41002",
                "flags": 0,
                "name": "robotics-robot-41002",
            },
        ),
        RegisterClassShape(
            request={"class_id": "class-41001", "shape": {"height": 1, "width": 1}},
        ),
        RegisterClassContinuousShape1d(
            request={"class_id": "class-41001", "span": {"length": 2000}},
        ),
        RegisterClassContinuousShape2d(
            request={
                "class_id": "class-41002",
                "rect": {"height": 1000, "width": 1000},
            },
        ),
    ],
    namespace_id=1,
    idempotency_key="robotics-cell-setup",
    actor_id="robotics-cell",
    metadata={
        "job_id": "job-robotics-001",
        "step": "setup",
        "trace_id": "trace-robotics-001",
    },
)

خطوة 2: البذور

إنشاء وضع الروبوت، العنصر الوارد، والحاوية المحجوزة.

لماذا هذه الخطوة مهمة: توفير أمثلة ملموسة للعملية: وضع الروبوت، عنصر وارد، وصندوق محجوز عن عمد.

الشروط المسبقة:

  • تم إنشاء الحاويات وتسجيل الفئات/الأشكال بواسطة عملية الالتزام.

ما يجب ملاحظته في الكود:

  • يتم وضع وضع الروبوت في 2D مستمر.
  • يتم وضع العنصر الوارد على خط النقل.
  • العنصر المحجوز في السلة يشغل خلية الشبكة المستهدفة.

تغيير الحالة المتوقع:

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

العمليات

  • AddInstance (x3) - يصنع مثيلاً جديداً ويضعه في موقع مستهدف.
await write_client.commit_operations(
    [
        AddInstance(
            class_id="class-41002",
            client_tag="robot-arm-1",
            key=None,
            location={
                "container_id": "container-31004",
                "coord": {"x": 2000, "y": 3000},
                "kind": "continuous_2d",
                "rotation_millideg": 0,
            },
        ),
        AddInstance(
            class_id="class-41001",
            client_tag="inbound-item",
            key=None,
            location={
                "container_id": "container-31001",
                "coord": {"x": 4000},
                "kind": "continuous_1d",
            },
        ),
        AddInstance(
            class_id="class-41001",
            client_tag="blocked-bin",
            key=None,
            location={
                "container_id": "container-31002",
                "kind": "grid_cell",
                "position": 6,
                "rotation": None,
            },
        ),
    ],
    namespace_id=1,
    idempotency_key="robotics-cell-seed",
    actor_id="robotics-cell",
    metadata={
        "job_id": "job-robotics-001",
        "sensor_ts_ms": 1725000000000,
        "step": "seed",
        "trace_id": "trace-robotics-001",
        "vision_confidence": 0.82,
    },
)

الخطوة 3: تقدم الناقل

تحريك العنصر بناءً على سرعة الناقل وعدد نبضات المشفر.

لماذا هذه الخطوة مهمة: تتحرك حزام النقل بناءً على عدادات التشفير. في نظام غير حتمي، ستعتمد على عدد نقرات التشفير وتأمل أن يتطابق مع الواقع. يسجل AssetCore الموقع الدقيق كالتزام - مع طابع زمني، وبيانات تعريف التشفير - لذا إذا فشل الاختيار لاحقًا، يمكنك إعادة التشغيل من هذه النقطة ورؤية ما إذا كان موقع التشفير صحيحًا أو إذا حدث انحراف في المستشعر.

الشروط المسبقة:

  • العنصر الوارد موجود على الناقل من الالتزام الأولي.

ما تقدمه AssetCore:

  • تحديثات الموضع هي عمليات التزام ذرية (لا توجد تحركات جزئية)
  • تلتقط البيانات الوصفية تكتلات المشفر وسرعته للتحليل الجنائي
  • مفتاح التكرار يضمن أن إعادة تشغيل هذا الالتزام تنتج نفس الحالة

ما يجب ملاحظته في الكود:

  • encoder_ticks: 1825 و speed_mm_s: 450 في البيانات الوصفية - تم التقاطها للتدقيق
  • إحداثي 1D مستمر x: 10000 (10 مم مع fixed_point_scale=1000)
  • مفتاح الاستقلالية robotics-cell-advance يمنع الحركات المكررة عند إعادة المحاولة

تغيير الحالة المتوقع:

  • العنصر الوارد ينتقل إلى إحداثيات الناقل الجديدة.

العمليات

  • MoveInstance - ينقل مثيلاً موجودًا إلى موقع جديد.
await write_client.actions.move_instance(
    instance="inst-2",
    to={
        "container_id": "container-31001",
        "coord": {"x": 10000},
        "kind": "continuous_1d",
    },
    options=ActionOptions(
        namespace_id=1,
        idempotency_key="robotics-cell-advance",
        actor_id="robotics-cell",
        metadata={
            "encoder_ticks": 1825,
            "job_id": "job-robotics-001",
            "speed_mm_s": 450,
            "step": "conveyor-advance",
            "trace_id": "trace-robotics-001",
        },
    ),
)

الخطوة 4: تسوية الناقل

قم بتطبيق تصحيح المستشعر على موضع العنصر.

لماذا هذه الخطوة مهمة: نظام الرؤية يكتشف أن الموقع الفعلي يختلف عن تقدير المشفر (10000 مم مشفر مقابل 11250 مم رؤية). يقوم AssetCore بتطبيق التصحيح كالتزام منفصل مع بيانات وصفية vision_confidence: 0.78. إذا قمت بإعادة التشغيل من هذه النقطة، سترى التصحيح الدقيق للحساس الذي تم تطبيقه.

الشروط المسبقة:

  • العنصر الوارد يبقى على الناقل بعد خطوة التقدم.

ما تقدمه AssetCore:

  • تصحيحات المستشعرات هي التزامات من الدرجة الأولى مع بيانات وصفية
  • يتم تسجيل التصحيحات ذات الثقة المنخفضة للتحليل لاحقًا
  • يعرض العرض التكراري الفرق الدقيق بين تقديرات المشفر والرؤية

ما يجب ملاحظته في الكود:

  • دلتا 1250 مم (12.5% انحراف عن تقدير المشفر)
  • ثقة الرؤية 0.78 تم التقاطها في البيانات الوصفية
  • يسمح الالتزام المنفصل بالاستعلام “ما هي التصحيحات التي تم تطبيقها”

تغيير الحالة المتوقع:

  • العنصر الوارد ينتقل إلى الإحداثيات المصححة.

العمليات

  • MoveInstance - ينقل مثيلاً موجودًا إلى موقع جديد.
await write_client.actions.move_instance(
    instance="inst-2",
    to={
        "container_id": "container-31001",
        "coord": {"x": 11250},
        "kind": "continuous_1d",
    },
    options=ActionOptions(
        namespace_id=1,
        idempotency_key="robotics-cell-reconcile",
        actor_id="robotics-cell",
        metadata={
            "job_id": "job-robotics-001",
            "sensor_ts_ms": 1725000001250,
            "step": "conveyor-reconcile",
            "trace_id": "trace-robotics-001",
            "vision_confidence": 0.78,
        },
    ),
)

الخطوة 5: حركة الروبوت

قم بتحديث وضع الروبوت في بعدين مستمرين.

لماذا هذه الخطوة مهمة: تحديثات وضع الروبوت هي تغييرات حالة موثوقة، وليست اقتراحات من المخطط. تسجيلها كالتزامات يجعل الحركة جاهزة للتدقيق.

الشروط المسبقة:

  • توجد نسخة الروبوت في حاوية الوضع من الالتزام الأساسي.

ما يجب ملاحظته في الكود:

  • MoveInstance تستهدف حاوية ثنائية الأبعاد مستمرة ذات إحداثيات ثابتة.

تغيير الحالة المتوقع:

  • يتم تحديث وضع الروبوت إلى إحداثيات X/Y الجديدة.

العمليات

  • MoveInstance - ينقل مثيلاً موجودًا إلى موقع جديد.
await write_client.actions.move_instance(
    instance="inst-1",
    to={
        "container_id": "container-31004",
        "coord": {"x": 3500, "y": 4000},
        "kind": "continuous_2d",
        "rotation_millideg": 0,
    },
    options=ActionOptions(
        namespace_id=1,
        idempotency_key="robotics-cell-robot-move",
        actor_id="robotics-cell",
        metadata={
            "job_id": "job-robotics-001",
            "step": "robot-move",
            "trace_id": "trace-robotics-001",
        },
    ),
)

الخطوة 6: الاختيار

نقل العنصر من الناقل إلى القابض.

لماذا هذه الخطوة مهمة: الالتقاط هو حركة حتمية من الناقل إلى القابض. لا توجد حالة مخفية - العنصر يغادر حاوية واحدة ويدخل أخرى.

الشروط المسبقة:

  • العنصر الوارد على الناقل.
  • فتحة القبضة المستهدفة فارغة.

ما يجب ملاحظته في الكود:

  • MoveInstance تستهدف حاوية الفتحة (الممسك).

تغيير الحالة المتوقع:

  • العنصر يغادر حاوية الناقل ويشغل فتحة القابض.

العمليات

  • MoveInstance - ينقل مثيلاً موجودًا إلى موقع جديد.
await write_client.actions.move_instance(
    instance="inst-2",
    to={"container_id": "container-31003", "kind": "slot", "slot_index": 1},
    options=ActionOptions(
        namespace_id=1,
        idempotency_key="robotics-cell-pick",
        actor_id="robotics-cell",
        metadata={
            "job_id": "job-robotics-001",
            "step": "pick",
            "trace_id": "trace-robotics-001",
        },
    ),
)

الخطوة 7: مكان محجوز (خطأ متوقع)

تم حظر إرجاع الحاوية POSITION_OCCUPIED.

لماذا هذه الخطوة مهمة: الموقع 6 مشغول. يقوم AssetCore بإرجاع خطأ POSITION_OCCUPIED قبل أي تغيير في الحالة. لا يوجد التزام جزئي، ولا حاجة لتنظيف. يحدث اكتشاف النزاع في وقت التحقق (L2)، لذا تبقى حالتك نظيفة.

الشروط المسبقة:

  • العنصر في فتحة القبضة.
  • الخلية المستهدفة في الشبكة مشغولة بواسطة الحاجز.

ما تقدمه AssetCore:

  • تم اكتشاف النزاعات قبل تغيير الحالة (تحقق رخيص)
  • تتضمن استجابة الخطأ تفاصيل النزاع الدقيقة (أي موضع، أي عنصر)
  • تم الحفاظ على مفتاح الاستقلالية لإعادة المحاولة مع الموضع المصحح

ما يجب ملاحظته في الكود:

  • الخطأ محدد (ليس حالة سباق)
  • retryable: false تعني أنه يجب على العميل تغيير الطلب (استعلام عن موقع مجاني)
  • يتم إعادة استخدام نفس مفتاح التكرار بعد إعادة التخطيط

تغيير الحالة المتوقع:

  • لم يتم تطبيق أي تغييرات على الحالة؛ هذه هي فشل التحقق الحتمي.

العمليات

  • MoveInstance - ينقل مثيلاً موجودًا إلى موقع جديد.

خطأ متوقع

{
  "code": "POSITION_OCCUPIED",
  "detail": "Position 6 in container 2 is occupied by item at anchor 6",
  "hint": "Select an unoccupied position or move the existing item first.",
  "retryable": false,
  "status": 409,
  "title": "ConflictError",
  "type": "urn:assetcore:error:POSITION_OCCUPIED"
}
await write_client.actions.move_instance(
    instance="inst-2",
    to={
        "container_id": "container-31002",
        "kind": "grid_cell",
        "position": 6,
        "rotation": None,
    },
    options=ActionOptions(
        namespace_id=1,
        idempotency_key="robotics-cell-place",
        actor_id="robotics-cell",
        metadata={
            "job_id": "job-robotics-001",
            "step": "place-attempt",
            "trace_id": "trace-robotics-001",
        },
    ),
)

الخطوة 8: الشبكة الحرة

ابحث عن نقطة ربط الشبكة المتاحة التالية.

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

الشروط المسبقة:

  • فشلت محاولة التعيين بسبب خلية شبكة مشغولة.

ما تقدمه AssetCore:

  • استعلامات القراءة لا تعيق الكتابات (معمارية CQRS)
  • استعلام بدون شبكة يعيد أول موقع متاح بشكل حتمي
  • تأخر الانتعاش محدود وقابل للقياس (انظر بيانات الاستجابة)

ما يجب ملاحظته في الكود:

  • معلمات الاستعلام: height=1, width=1 (بصمة العنصر)
  • تعيد الاستجابة الموضع 1 (أول نقطة ربط مجانية بترتيب الصفوف)
  • لا حاجة لعملية (استعلام للقراءة فقط)

تغيير الحالة المتوقع:

  • لا شيء. هذه استعلام تخطيط للقراءة فقط.
await read_client.get_container_grid_free(
    container_id="container-31002",
    namespace_id=1,
    height=1,
    width=1,
)

الخطوة 9: المكان

ضع العنصر في نقطة ربط الشبكة الحرة.

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

الشروط المسبقة:

  • العنصر يبقى في القابض.
  • العنصر الحر الذي تم إرجاعه بواسطة grid_free لا يزال متاحًا.

ما يجب ملاحظته في الكود:

  • MoveInstance تستهدف خلية الشبكة الحرة التي تم إرجاعها بواسطة خطوة القراءة.

تغيير الحالة المتوقع:

  • العنصر يترك القابض ويشغل خلية الشبكة الحرة.

العمليات

  • MoveInstance - ينقل مثيلاً موجودًا إلى موقع جديد.
await write_client.actions.move_instance(
    instance="inst-2",
    to={
        "container_id": "container-31002",
        "kind": "grid_cell",
        "position": 1,
        "rotation": None,
    },
    options=ActionOptions(
        namespace_id=1,
        idempotency_key="robotics-cell-place",
        actor_id="robotics-cell",
        metadata={
            "job_id": "job-robotics-001",
            "replan_reason": "grid_free",
            "step": "place",
            "trace_id": "trace-robotics-001",
        },
    ),
)

الخطوة 10: وضع إعادة التشغيل

إعادة تشغيل التعيين لتأكيد خاصية عدم التكرار.

لماذا هذه الخطوة مهمة: إعادة تشغيل نفس الالتزام بالضبط (نفس مفتاح التكرار) تعيد النجاح دون آثار مكررة. هذه هي الطريقة التي تضمن بها AssetCore التكرار - إعادة تشغيل السجل دائمًا تنتج نفس الحالة. في نظام موزع، يعني هذا أن إعادة المحاولة عبر الشبكة آمنة.

الشروط المسبقة:

  • تم بالفعل نجاح الالتزام بالتوزيع.

ما تقدمه AssetCore:

  • الاستقلالية على مستوى الالتزام (ليس فقط HTTP)
  • يعيد التشغيل إنتاج نفس commit_id و world_seq كما في الأصل
  • آمن لإعادة محاولة أي عملية دون فساد في الحالة

ما يجب ملاحظته في الكود:

  • الحمولة متطابقة مع التوزيع الناجح (نفس المفتاح، نفس العمليات)
  • تؤكد الاستجابة عدم وجود أحداث جديدة تم إنشاؤها (غير قابلة للتغيير)
  • هذه هي كيفية عمل استعادة الكوارث: إعادة تشغيل السجل بالكامل

تغيير الحالة المتوقع:

  • لا شيء. تعيد عملية التشغيل السريع الالتزام المخزن في الذاكرة.

العمليات

  • MoveInstance - ينقل مثيلاً موجودًا إلى موقع جديد.
await write_client.actions.move_instance(
    instance="inst-2",
    to={
        "container_id": "container-31002",
        "kind": "grid_cell",
        "position": 1,
        "rotation": None,
    },
    options=ActionOptions(
        namespace_id=1,
        idempotency_key="robotics-cell-place",
        actor_id="robotics-cell",
        metadata={
            "job_id": "job-robotics-001",
            "replan_reason": "grid_free",
            "step": "place",
            "trace_id": "trace-robotics-001",
        },
    ),
)

التحقق

تؤكد هذه القراءات والتدفقات الحالة النهائية بعد الخطوات دون تغيير أي شيء.

قراءة 1: قراءة الناقل

تحقق من أن مواضع الناقل فارغة.

await read_client.get_container_continuous_placements_1d(
    container_id="container-31001",
    namespace_id=1,
)

قراءة 2: قراءة الفتحات

تحقق من أن فتحات القبضة فارغة.

await read_client.get_container_slots(
    container_id="container-31003",
    namespace_id=1,
)

قراءة 3: شبكة القراءة

تحقق من أن مواضع الشبكة تحتوي على حواجز وعناصر.

await read_client.get_container_grid(
    container_id="container-31002",
    namespace_id=1,
)

اقرأ 4: اقرأ الروبوت

تحقق من موضع الروبوت.

await read_client.get_container_continuous_placements_2d(
    container_id="container-31004",
    namespace_id=1,
)

Stream 5: التزام البث

اقرأ التزام التوزيع من SSE.

{
  "id": 1,
  "jsonrpc": "2.0",
  "method": "assetcore_read_stream",
  "params": {
    "from_world_seq": 22,
    "limit": 1,
    "namespace_id": 1
  }
}

ما الذي يثبته هذا: الروبوتات ذات المستوى الإنتاجي مع ضمانات حتمية

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

نقطة الإثبات 1: كل تغيير في الحالة قابل للتدقيق

كل عملية التزام تحمل بيانات وصفية: job_id، trace_id، sensor_ts_ms، vision_confidence. عندما تفشل عملية الالتقاط، لا تخمن - استعلم عن سجل الالتزام وانظر:

  • الموقع الدقيق للترميز عند تقدم الناقل
  • تم تطبيق تصحيح الرؤية (دلتا: 1250 مم، الثقة: 0.78)
  • أي موضع في الشبكة تم حظره
  • تم اختيار موضع الشبكة الجديد بعد إعادة التخطيط

نقطة إثبات 2: اكتشاف النزاع بدون قفل

الخطوة 7 تُظهر اكتشاف POSITION_OCCUPIED في وقت التحقق. لا أقفال موزعة، لا عبء تنسيق. تجعل بنية الكتابة الفردية لـ AssetCore النزاعات حتمية: يتم اكتشافها قبل أي تغيير في الحالة، وتخبرك استجابة الخطأ بالضبط بما يجب إصلاحه.

نقطة الإثبات 3: إعادة التشغيل غير القابلة للتغيير لأغراض التصحيح

الخطوة 10 تثبت خاصية عدم التأثير: إعادة تشغيل نفس الالتزام بالضبط (نفس مفتاح عدم التأثير، نفس العمليات) تنجح دون آثار جانبية. هذه هي الطريقة التي تقوم بها بتصحيح أخطاء الإنتاج:

  1. تصدير سجل الالتزام من نافذة الفشل
  2. إعادة التشغيل محليًا لاستنساخ الحالة الدقيقة
  3. فحص الحالة الوسيطة في أي التزام
  4. إصلاح المشكلة والتحقق باستخدام إعادة التشغيل

قيمة الأعمال: من “لقد فشل” إلى “إليك السبب”

تقدم أنظمة الروبوتات التقليدية لك ثلاثة سجلات متضاربة (الرؤية، التخطيط، التنفيذ) وتجبرك على إعادة بناء ما حدث. يوفر لك AssetCore سجلًا موحدًا موثوقًا حيث يتم توقيع كل تغيير في الحالة، ونسبته (actor_id)، ويمكن إعادة تشغيله.

للتكليف: إعادة تشغيل الإنتاج للتحقق من سلوك الخلية

لأغراض تصحيح الأخطاء: السفر عبر الزمن إلى الالتزام الدقيق حيث حدث الفشل

للتوافق: تصدير سجل تدقيق غير قابل للتغيير مع بيانات تعريف المستشعر

لتحسين الأداء: تحليل بيانات التعهد لتحديد نقاط الاختناق

هذا ما تمكّنه الأدوات ذات المستوى العالمي: إدارة حالة حتمية تتيح لك التركيز على منطق مجال الروبوتات بدلاً من محاربة أخطاء التوفيق بين الحالات.

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

  • جربه محليًا: استنساخ المستودع وتشغيل السيناريو لتنفيذ هذا سير العمل
  • قم بتمديده: أضف تنسيق متعدد الروبوتات عن طريق تقسيمه عبر مساحات الأسماء
  • دمجه: راجع مرجع SDK لأنماط دمج الإنتاج
  • اسألنا: احجز جلسة تقنية متعمقة لمناقشة حالة استخدام الروبوتات الخاصة بك