rpc --- مكتبة rpc

تتيح لك وحدة rpc على OpenMV Cam ربط جهاز OpenMV Cam الخاص بك بمتحكم دقيق أو حاسوب آخر وتنفيذ استدعاءات بايثون (أو إجرائية) عن بُعد على OpenMV Cam. كما تتيح وحدة rpc العكس أيضًا إذا أردت أن يتمكن OpenMV Cam من تنفيذ استدعاءات إجرائية (أو بايثون) عن بُعد على متحكم دقيق أو حاسوب آخر.

كيفية استخدام المكتبة

جهاز تابع بسيط يكشف دالة رد نداء واحدة عبر UART:

import rpc
import csi

csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.QVGA)

interface = rpc.rpc_uart_slave(baudrate=115200)

def snapshot(_):
    return csi0.snapshot().compress().bytearray()

interface.register_callback(snapshot)
interface.loop()  # Does not return.

جهاز رئيسي مطابق يطلب من التابع إطار JPEG:

import rpc

interface = rpc.rpc_uart_master(baudrate=115200)

result = interface.call("snapshot")
if result is None:
    print("communication failed")
elif len(result) == 0:
    print("remote function not registered on the slave")
else:
    # result is a memoryview of the JPEG bytes returned by the slave.
    print("received", len(result), "bytes")

استبدل rpc_uart_master / rpc_uart_slave بالزوج المطابق can أو i2c أو spi لاستخدام وسيلة نقل مختلفة.

بشكل عام، لكي يستخدم جهاز التحكم مكتبة rpc ستقوم بإنشاء كائن واجهة باستخدام مكتبة rpc. على سبيل المثال:

interface = rpc.rpc_uart_master(baudrate=115200)

ينشئ هذا واجهة UART للتواصل مع تابع rpc.

بمجرد إنشاء الواجهة، كل ما تحتاجه هو تنفيذ:

memory_view_object_result = interface.call("remote_function_or_method_name", bytes_object_argument)

وستحاول مكتبة rpc تنفيذ "remote_function_or_method_name" على التابع. ستتلقى الدالة أو الطريقة البعيدة الوسيط bytes_object_argument الذي يمكن أن يصل حجمه إلى 2^32-1 بايت. بمجرد انتهاء الطريقة البعيدة من التنفيذ، ستعيد memory_view_object_result الذي يمكن أن يصل حجمه أيضًا إلى 2^32-1 بايت. ولأن كلًا من الوسيط والاستجابة عبارة عن حاويات بايت عامة، يمكنك تمرير أي شيء عبر مكتبة rpc وتلقي أي نوع من الاستجابات. إحدى الطرق البسيطة لتمرير الوسائط هي استخدام struct.pack() لإنشاء الوسيط و struct.unpack() لاستقبال الوسيط على الجانب الآخر. أما بالنسبة للاستجابة، فقد يرسل الجانب الآخر كائن سلسلة نصية أو سلسلة json كنتيجة يمكن للجهاز الرئيسي تفسيرها بعد ذلك.

أما بالنسبة للأخطاء، فإذا حاولت تنفيذ اسم دالة أو طريقة غير موجود، فستعيد الطريقة rpc_master.call() كائن bytes() فارغًا. وإذا فشلت مكتبة rpc في التواصل مع التابع، فستعيد مكتبة rpc القيمة None.

لإبقاء الأمور بسيطة، لا تحتفظ مكتبة rpc باتصال بين الجهازين الرئيسي والتابع. تغلّف الطريقة rpc_master.call() محاولة الاتصال بالتابع، وبدء تنفيذ الدالة أو الطريقة البعيدة، والحصول على النتيجة.

الآن، على جانب التابع، يجب عليك إنشاء واجهة rpc للتواصل مع الجهاز الرئيسي. يبدو هذا كالتالي:

interface = rpc.rpc_uart_slave(baudrate=115200)

سينشئ هذا طبقة واجهة UART للتواصل مع جهاز rpc رئيسي.

بمجرد إنشاء واجهة التابع، تحتاج بعد ذلك إلى تسجيل دوال رد النداء التي يمكن للجهاز الرئيسي استدعاؤها باستخدام كائن الواجهة:

def remote_function_or_method_name(memoryview_object_argument):
    <lots of code>
    return bytes_object_result

interface.register_callback(remote_function_or_method_name)

يمكنك تسجيل أي عدد تريده من دوال رد النداء على التابع. أخيرًا، بمجرد الانتهاء من تسجيل دوال رد النداء، كل ما تحتاجه هو تنفيذ:

interface.loop()

على التابع لبدء تشغيل مكتبة rpc والبدء في الاستماع للجهاز الرئيسي. لاحظ أن الطريقة rpc_slave.loop() لا تعود.

class rpc -- الفئة الأساسية rpc

يُعاد تنفيذ الفئة الأساسية rpc بواسطة الفئتين rpc_master و rpc_slave لإنشاء الواجهتين الرئيسية والتابعة. وهي ليست مخصصة للاستخدام المباشر.

class rpc.rpc

ينشئ كائن rpc. ليس مخصصًا للاستخدام المباشر.

get_bytes(buff: bytearray | memoryview, timeout_ms: int) bytes | None

يُعاد تنفيذها بواسطة الفئات الفرعية الخاصة بوسيلة النقل. تملأ buff ببايتات من الواجهة الأساسية خلال timeout_ms مللي ثانية. تعيد None عند انتهاء المهلة.

put_bytes(data: bytes | memoryview, timeout_ms: int) None

يُعاد تنفيذها بواسطة الفئات الفرعية الخاصة بوسيلة النقل. ترسل data عبر الواجهة الأساسية خلال timeout_ms مللي ثانية.

stream_reader(call_back: Callable[[memoryview], None], queue_depth: int = 1, read_timeout_ms: int = 5000) None

يستقبل تدفقًا من الحمولات من كاتب rpc.stream_writer بعيد. ينبغي استدعاؤه من داخل دالة رد نداء rpc_slave (أو مباشرة بعد استدعاء rpc_master.call ناجح) بمجرد تزامن الجانبين.

  • call_back -- كائن قابل للاستدعاء يُستدعى مرة واحدة لكل حمولة مستلمة بالصيغة call_back(data) حيث data هو memoryview. تُتجاهل القيمة المعادة.

  • queue_depth -- عدد الإطارات قيد النقل التي يُسمح للكاتب بإرسالها قبل الانتظار على القارئ. القيم الأعلى تزيد الإنتاجية على حساب الذاكرة.

  • read_timeout_ms -- عدد المللي ثوانٍ للانتظار لكل حمولة.

يعود عند أي خطأ. للإلغاء، أطلق استثناءً داخل call_back؛ وستنتهي مهلة الجانب البعيد.

stream_writer(call_back: Callable[[], bytes | memoryview], write_timeout_ms: int = 5000) None

يرسل تدفقًا من الحمولات إلى قارئ rpc.stream_reader بعيد. ينبغي استدعاؤه من داخل دالة رد نداء rpc_slave (أو مباشرة بعد استدعاء rpc_master.call ناجح) بمجرد تزامن الجانبين.

  • call_back -- كائن قابل للاستدعاء يُستدعى بدون وسائط ويعيد حمولة bytes أو memoryview التالية لإرسالها.

  • write_timeout_ms -- عدد المللي ثوانٍ للانتظار عند إرسال كل حمولة.

يعود عند أي خطأ. للإلغاء، أطلق استثناءً داخل call_back؛ وستنتهي مهلة الجانب البعيد.

class rpc_master -- الفئة الأساسية rpc_master

إن rpc_master فئة أساسية. استخدم إحدى الفئات الفرعية الخاصة بوسيلة النقل (rpc_can_master أو rpc_i2c_master أو rpc_spi_master أو rpc_uart_master).

class rpc.rpc_master

ينشئ كائن rpc_master. ليس مخصصًا للاستخدام المباشر.

call(name: str, data: bytes = bytes(), send_timeout: int = 1000, recv_timeout: int = 1000) memoryview | None

ينفذ استدعاءً بعيدًا على الجهاز التابع.

  • name -- الاسم النصي للدالة أو الطريقة البعيدة المراد تنفيذها.

  • data -- كائن من نوع bytes يُمرَّر كوسيط للدالة البعيدة.

  • send_timeout -- عدد المللي ثوانٍ للانتظار أثناء الاتصال بالتابع وبدء تنفيذ الدالة البعيدة. بمجرد بدء الجهاز الرئيسي في إرسال الوسيط، لا ينطبق هذا بعد الآن؛ إذ تسمح المكتبة بما يصل إلى 5 ثوانٍ لنقل الوسيط.

  • recv_timeout -- عدد المللي ثوانٍ للانتظار حتى يبدأ التابع في إعادة استجابة. بمجرد بدء الجهاز الرئيسي في استقبال الاستجابة، لا ينطبق هذا بعد الآن؛ إذ تسمح المكتبة بما يصل إلى 5 ثوانٍ لنقل الاستجابة.

تعيد memoryview للاستجابة عند النجاح، أو bytes() فارغًا إذا كان الاسم البعيد غير موجود على التابع، أو None عند فشل الاتصال.

class rpc_slave -- الفئة الأساسية rpc_slave

إن rpc_slave فئة أساسية. استخدم إحدى الفئات الفرعية الخاصة بوسيلة النقل (rpc_can_slave أو rpc_i2c_slave أو rpc_spi_slave أو rpc_uart_slave).

class rpc.rpc_slave

ينشئ كائن rpc_slave. ليس مخصصًا للاستخدام المباشر.

register_callback(cb: Callable[[memoryview], bytes | memoryview]) None

يسجل دالة رد نداء يمكن للجهاز الرئيسي استدعاؤها بالاسم. cb كائن قابل للاستدعاء يأخذ وسيط memoryview واحدًا ويعيد كائنًا من نوع bytes. يُستخدم __name__ الخاص بدالة رد النداء كمفتاح للبحث.

schedule_callback(cb: Callable[[], None]) None

يجدول cb (كائن قابل للاستدعاء لا يأخذ وسائط) لتنفيذه مرة واحدة، مباشرة بعد أن تعيد دالة رد نداء rpc قيد التشغيل حاليًا استجابتها بنجاح إلى الجهاز الرئيسي. يجب استدعاؤه من داخل دالة رد نداء rpc. يتيح تشغيل عمل طويل الأمد أو عمليات نقل العبور rpc.get_bytes/rpc.put_bytes بين معاملات rpc. أعد التسجيل عند كل استدعاء إذا كان التنفيذ المتكرر مطلوبًا.

setup_loop_callback(cb: Callable[[], None]) None

يسجل cb (كائن قابل للاستدعاء لا يأخذ وسائط) ليُستدعى في كل تكرار من rpc_slave.loop. على عكس rpc_slave.schedule_callback، تبقى دالة رد النداء هذه مسجلة. يجب أن تكون غير حاجبة؛ إذ يكون معدل الاستدعاء متغيرًا.

loop(recv_timeout: int = 1000, send_timeout: int = 1000) None

يشغّل حلقة توزيع التابع rpc. لا يعود إلا عبر استثناء يُطلق من دالة رد نداء.

  • recv_timeout -- عدد المللي ثوانٍ للانتظار للحصول على أمر من الجهاز الرئيسي قبل إعادة المحاولة.

  • send_timeout -- عدد المللي ثوانٍ للانتظار حتى يؤكد الجهاز الرئيسي استلام الاستجابة قبل العودة للاستقبال.

class rpc_can_master -- واجهة CAN الرئيسية

التحكم في جهاز rpc آخر عبر CAN.

class rpc.rpc_can_master(message_id: int = 0x7FF, bit_rate: int = 250000, sample_point: float = 75, can_bus: int = 2)
  • message_id -- معرّف رسالة CAN بطول 11 بت يُستخدم لنقل البيانات.

  • bit_rate -- معدل بت CAN بالبت في الثانية.

  • sample_point -- نسبة نقطة أخذ العينة Tseg1/Tseg2 المئوية (مثل 50.0 أو 62.5 أو 75 أو 87.5).

  • can_bus -- رقم طرفية CAN.

يجب أن يتطابق message_id و bit_rate لدى الجهاز الرئيسي والتابع. ويجب إنهاء الناقل بمقاومة 120 أوم.

class rpc_can_slave -- واجهة CAN التابعة

التحكم به بواسطة جهاز rpc آخر عبر CAN.

class rpc.rpc_can_slave(message_id: int = 0x7FF, bit_rate: int = 250000, sample_point: float = 75, can_bus: int = 2)

راجع rpc_can_master للحصول على أوصاف الوسائط.

class rpc_i2c_master -- واجهة I2C الرئيسية

التحكم في جهاز rpc آخر عبر I2C.

class rpc.rpc_i2c_master(slave_addr: int = 0x12, rate: int = 100000, i2c_bus: int = 2)
  • slave_addr -- عنوان I2C بطول 7 بت للجهاز التابع.

  • rate -- تردد ساعة ناقل I2C بوحدة Hz.

  • i2c_bus -- رقم طرفية I2C.

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

class rpc_i2c_slave -- واجهة I2C التابعة

التحكم به بواسطة جهاز rpc آخر عبر I2C.

class rpc.rpc_i2c_slave(slave_addr: int = 0x12, i2c_bus: int = 2)
  • slave_addr -- عنوان I2C بطول 7 بت الذي يستجيب له هذا التابع.

  • i2c_bus -- رقم طرفية I2C.

class rpc_spi_master -- واجهة SPI الرئيسية

التحكم في جهاز rpc آخر عبر SPI.

class rpc.rpc_spi_master(cs_pin: str = 'P3', freq: int = 1000000, clk_polarity: int = 1, clk_phase: int = 0, spi_bus: int = 2)
  • cs_pin -- اسم دبوس تحديد الشريحة.

  • freq -- تردد ساعة ناقل SPI بوحدة Hz.

  • clk_polarity -- مستوى الساعة في وضع الخمول (0 أو 1).

  • clk_phase -- أخذ عينة البيانات عند حافة الساعة الأولى (0) أو الثانية (1).

  • spi_bus -- رقم طرفية SPI.

يجب أن تتطابق إعدادات الجهاز الرئيسي والتابع. وصّل CS و SCLK و MOSI و MISO مباشرة. ويجب أن يشترك الجهازان في أرضي مشترك.

class rpc_spi_slave -- واجهة SPI التابعة

التحكم به بواسطة جهاز rpc آخر عبر SPI.

class rpc.rpc_spi_slave(cs_pin: str = 'P3', clk_polarity: int = 1, clk_phase: int = 0, spi_bus: int = 2)
  • cs_pin -- اسم دبوس دخل تحديد الشريحة.

  • clk_polarity -- مستوى الساعة في وضع الخمول (0 أو 1).

  • clk_phase -- أخذ عينة البيانات عند حافة الساعة الأولى (0) أو الثانية (1).

  • spi_bus -- رقم طرفية SPI.

class rpc_uart_master -- واجهة UART الرئيسية

التحكم في جهاز rpc آخر عبر التسلسلي غير المتزامن (UART).

class rpc.rpc_uart_master(baudrate: int = 9600, uart_port: int = 3)
  • baudrate -- معدل الباود التسلسلي.

  • uart_port -- رقم طرفية UART.

يجب أن يتطابق معدلا الباود لدى الجهاز الرئيسي والتابع. وصّل TX الخاص بالجهاز الرئيسي بـ RX الخاص بالتابع و RX الخاص بالجهاز الرئيسي بـ TX الخاص بالتابع. ويجب أن يشترك الجهازان في أرضي مشترك.

class rpc_uart_slave -- واجهة UART التابعة

التحكم به بواسطة جهاز rpc آخر عبر التسلسلي غير المتزامن (UART).

class rpc.rpc_uart_slave(baudrate: int = 9600, uart_port: int = 3)
  • baudrate -- معدل الباود التسلسلي.

  • uart_port -- رقم طرفية UART.