rpc --- ไลบรารี rpc

โมดูล rpc บน OpenMV Cam ช่วยให้คุณเชื่อมต่อ OpenMV Cam กับไมโครคอนโทรลเลอร์หรือคอมพิวเตอร์อื่น แล้วเรียกใช้คำสั่ง Python (หรือโพรซีเดอร์) จากระยะไกลบน OpenMV Cam ได้ โมดูล rpc ยังรองรับการทำงานในทิศทางตรงกันข้ามด้วย หากคุณต้องการให้ OpenMV Cam สามารถเรียกใช้โพรซีเดอร์ (หรือ Python) จากระยะไกลบนไมโครคอนโทรลเลอร์หรือคอมพิวเตอร์อื่นได้เช่นกัน

วิธีใช้ไลบรารี

ตัวอย่าง slave ขั้นต่ำที่เปิดเผยคอลแบ็กหนึ่งรายการผ่าน 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.

ตัวอย่าง master ที่สอดคล้องกัน ซึ่งขอเฟรม JPEG จาก slave:

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 ที่เหมาะสมเพื่อใช้การส่งข้อมูลแบบอื่น

โดยทั่วไปสำหรับอุปกรณ์ controller ที่ต้องการใช้ไลบรารี rpc คุณต้องสร้างออบเจกต์อินเทอร์เฟซโดยใช้ไลบรารี rpc ตัวอย่างเช่น:

interface = rpc.rpc_uart_master(baudrate=115200)

คำสั่งนี้จะสร้างอินเทอร์เฟซ UART เพื่อสื่อสารกับ rpc slave

เมื่อสร้างอินเทอร์เฟซแล้ว คุณเพียงแค่ต้องเรียก:

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

แล้วไลบรารี rpc จะพยายามเรียกใช้ "remote_function_or_method_name" นั้นบน slave ฟังก์ชันหรือเมธอดระยะไกลจะได้รับ bytes_object_argument ซึ่งมีขนาดได้สูงสุด 2^32-1 ไบต์ เมื่อเมธอดระยะไกลทำงานเสร็จสิ้นจะส่งคืน memory_view_object_result ซึ่งมีขนาดสูงสุดถึง 2^32-1 ไบต์เช่นกัน เนื่องจากทั้งอาร์กิวเมนต์และการตอบกลับเป็นคอนเทนเนอร์ไบต์ทั่วไป คุณจึงสามารถส่งข้อมูลใดๆ ผ่านไลบรารี rpc และรับการตอบกลับในรูปแบบใดก็ได้ วิธีที่ง่ายในการส่งอาร์กิวเมนต์คือใช้ struct.pack() เพื่อสร้างอาร์กิวเมนต์ และใช้ struct.unpack() เพื่อรับอาร์กิวเมนต์ที่ฝั่งอื่น สำหรับการตอบกลับ ฝั่งอื่นอาจส่งออบเจกต์สตริงหรือสตริง json เป็นผลลัพธ์ ซึ่ง master สามารถนำไปตีความต่อได้

สำหรับข้อผิดพลาด หากคุณพยายามเรียกใช้ชื่อฟังก์ชันหรือเมธอดที่ไม่มีอยู่ เมธอด rpc_master.call() จะคืนค่าออบเจกต์ bytes() ว่าง หากไลบรารี rpc ไม่สามารถสื่อสารกับ slave ได้ ไลบรารี rpc จะคืนค่า None

เพื่อความเรียบง่าย ไลบรารี rpc ไม่รักษาการเชื่อมต่อระหว่างอุปกรณ์ master และ slave เมธอด rpc_master.call() รวมขั้นตอนการพยายามเชื่อมต่อกับ slave การเริ่มต้นเรียกใช้ฟังก์ชันหรือเมธอดระยะไกล และการรับผลลัพธ์ไว้ในขั้นตอนเดียว

ทีนี้ฝั่ง slave คุณต้องสร้างอินเทอร์เฟซ rpc เพื่อสื่อสารกับ master ซึ่งมีลักษณะดังนี้:

interface = rpc.rpc_uart_slave(baudrate=115200)

คำสั่งนี้จะสร้างเลเยอร์อินเทอร์เฟซ UART เพื่อสื่อสารกับ rpc master

เมื่อสร้างอินเทอร์เฟซ slave แล้ว คุณต้องลงทะเบียนคอลแบ็กที่ master สามารถเรียกใช้ผ่านออบเจกต์อินเทอร์เฟซ:

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

interface.register_callback(remote_function_or_method_name)

คุณสามารถลงทะเบียนคอลแบ็กได้จำนวนเท่าใดก็ได้บน slave ท้ายที่สุด เมื่อลงทะเบียนคอลแบ็กครบแล้ว เพียงแค่เรียก:

interface.loop()

บน slave เพื่อเริ่มต้นไลบรารี rpc และเริ่มรอรับคำสั่งจาก master โปรดทราบว่าเมธอด rpc_slave.loop() จะไม่มีการ return

class rpc -- คลาสฐาน rpc

คลาสฐาน rpc ถูก reimplemented โดยคลาส rpc_master และ rpc_slave เพื่อสร้างอินเทอร์เฟซ master และ slave โดยไม่ได้มีไว้ใช้งานโดยตรง

class rpc.rpc

สร้างออบเจกต์ rpc ไม่ได้มีไว้ใช้งานโดยตรง

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

Reimplemented โดย subclass เฉพาะการขนส่ง เติมข้อมูลใน buff ด้วยไบต์จากอินเทอร์เฟซพื้นฐานภายใน timeout_ms มิลลิวินาที คืนค่า None เมื่อหมดเวลา

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

Reimplemented โดย subclass เฉพาะการขนส่ง ส่ง data ผ่านอินเทอร์เฟซพื้นฐานภายใน timeout_ms มิลลิวินาที

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

รับสตรีมของ payload จาก rpc.stream_writer ระยะไกล ควรเรียกจากภายในคอลแบ็ก rpc_slave (หรือหลังจาก rpc_master.call สำเร็จโดยตรง) เมื่อทั้งสองฝั่งซิงโครไนซ์กันแล้ว

  • call_back -- callable ที่ถูกเรียกหนึ่งครั้งต่อ payload ที่ได้รับในรูปแบบ call_back(data) โดยที่ data เป็น memoryview ค่าที่ return จะถูกละเว้น

  • queue_depth -- จำนวนเฟรมที่อยู่ระหว่างดำเนินการที่ writer อนุญาตให้ส่งก่อนรอ reader ค่าที่สูงขึ้นจะเพิ่มปริมาณงานที่ผ่านได้โดยแลกกับหน่วยความจำ

  • read_timeout_ms -- มิลลิวินาทีที่รอต่อ payload

คืนค่าเมื่อเกิดข้อผิดพลาดใดๆ หากต้องการยกเลิก ให้ raise exception ภายใน call_back แล้วฝั่งระยะไกลจะหมดเวลา

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

ส่งสตรีมของ payload ไปยัง rpc.stream_reader ระยะไกล ควรเรียกจากภายในคอลแบ็ก rpc_slave (หรือหลังจาก rpc_master.call สำเร็จโดยตรง) เมื่อทั้งสองฝั่งซิงโครไนซ์กันแล้ว

  • call_back -- callable ที่ถูกเรียกโดยไม่มีอาร์กิวเมนต์ และคืนค่า payload bytes หรือ memoryview ถัดไปที่จะส่ง

  • write_timeout_ms -- มิลลิวินาทีที่รอขณะส่ง payload แต่ละรายการ

คืนค่าเมื่อเกิดข้อผิดพลาดใดๆ หากต้องการยกเลิก ให้ raise exception ภายใน call_back แล้วฝั่งระยะไกลจะหมดเวลา

class rpc_master -- คลาสฐาน rpc_master

rpc_master เป็นคลาสฐาน ให้ใช้ subclass เฉพาะการขนส่ง (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

เรียกใช้คำสั่งระยะไกลบนอุปกรณ์ slave

  • name -- ชื่อสตริงของฟังก์ชันหรือเมธอดระยะไกลที่ต้องการเรียกใช้

  • data -- ออบเจกต์คล้าย bytes ที่ส่งเป็นอาร์กิวเมนต์ให้กับฟังก์ชันระยะไกล

  • send_timeout -- มิลลิวินาทีที่รอขณะเชื่อมต่อกับ slave และเริ่มต้นเรียกใช้ฟังก์ชันระยะไกล เมื่อ master เริ่มส่งอาร์กิวเมนต์แล้ว ค่านี้จะไม่ใช้อีกต่อไป โดยไลบรารีอนุญาตให้ใช้เวลาสูงสุด 5 วินาทีสำหรับการถ่ายโอนอาร์กิวเมนต์

  • recv_timeout -- มิลลิวินาทีที่รอให้ slave เริ่มส่งการตอบกลับ เมื่อ master เริ่มรับการตอบกลับแล้ว ค่านี้จะไม่ใช้อีกต่อไป โดยไลบรารีอนุญาตให้ใช้เวลาสูงสุด 5 วินาทีสำหรับการถ่ายโอนการตอบกลับ

คืนค่า memoryview ของการตอบกลับเมื่อสำเร็จ คืนค่า bytes() ว่างหากชื่อระยะไกลไม่มีอยู่บน slave หรือคืนค่า None เมื่อการสื่อสารล้มเหลว

class rpc_slave -- คลาสฐาน rpc_slave

rpc_slave เป็นคลาสฐาน ให้ใช้ subclass เฉพาะการขนส่ง (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

ลงทะเบียนคอลแบ็กที่ master อาจเรียกใช้ตามชื่อ cb เป็น callable ที่รับอาร์กิวเมนต์ memoryview หนึ่งตัวและคืนค่าออบเจกต์คล้าย bytes โดย __name__ ของคอลแบ็กจะใช้เป็นคีย์ในการค้นหา

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

กำหนดเวลาให้ cb (callable ที่ไม่มีอาร์กิวเมนต์) ทำงานหนึ่งครั้ง ทันทีหลังจากที่คอลแบ็ก rpc ที่กำลังทำงานอยู่ส่งการตอบกลับไปยัง master สำเร็จ ต้องเรียกจากภายในคอลแบ็ก rpc ช่วยให้งานที่ใช้เวลานานหรือการถ่ายโอน rpc.get_bytes/rpc.put_bytes แบบ cut-through สามารถทำงานระหว่างการทำธุรกรรม rpc ได้ หากต้องการเรียกใช้ซ้ำ ให้ลงทะเบียนใหม่ทุกครั้งที่เรียกใช้

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

ลงทะเบียน cb (callable ที่ไม่มีอาร์กิวเมนต์) ให้ถูกเรียกใช้ในทุกรอบของ rpc_slave.loop ต่างจาก rpc_slave.schedule_callback คอลแบ็กนี้จะยังคงลงทะเบียนไว้ ต้องไม่บล็อก และอัตราการเรียกใช้จะแปรผัน

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

รัน loop การส่ง/รับคำสั่งของ rpc slave จะไม่ return ยกเว้นเมื่อมี exception ถูก raise จากคอลแบ็ก

  • recv_timeout -- มิลลิวินาทีที่รอคำสั่งจาก master ก่อนลองใหม่

  • send_timeout -- มิลลิวินาทีที่รอให้ master ยืนยันการรับการตอบกลับก่อนกลับไปรับคำสั่งใหม่

class rpc_can_master -- อินเทอร์เฟซ CAN Master

ควบคุมอุปกรณ์ 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 message id ขนาด 11 บิตที่ใช้สำหรับการส่งข้อมูล

  • bit_rate -- อัตราบิต CAN ในหน่วยบิตต่อวินาที

  • sample_point -- เปอร์เซ็นต์จุดสุ่มตัวอย่าง Tseg1/Tseg2 (เช่น 50.0, 62.5, 75, 87.5)

  • can_bus -- หมายเลขอุปกรณ์ต่อพ่วง CAN

message_id และ bit_rate ของ master และ slave ต้องตรงกัน บัสต้องต่อตัวต้านทานปลายสาย 120 โอห์ม

class rpc_can_slave -- อินเทอร์เฟซ CAN Slave

ถูกควบคุมโดยอุปกรณ์ 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 Master

ควบคุมอุปกรณ์ rpc อื่นผ่าน I2C

class rpc.rpc_i2c_master(slave_addr: int = 0x12, rate: int = 100000, i2c_bus: int = 2)
  • slave_addr -- ที่อยู่ I2C 7 บิตของอุปกรณ์ slave

  • rate -- ความถี่นาฬิกาบัส I2C ในหน่วย Hz

  • i2c_bus -- หมายเลขอุปกรณ์ต่อพ่วง I2C

ที่อยู่ master และ slave ต้องตรงกัน ต้องใช้ pull-up ภายนอกบน SCL และ SDA และอุปกรณ์ทั้งสองต้องมี ground ร่วมกัน

class rpc_i2c_slave -- อินเทอร์เฟซ I2C Slave

ถูกควบคุมโดยอุปกรณ์ rpc อื่นผ่าน I2C

class rpc.rpc_i2c_slave(slave_addr: int = 0x12, i2c_bus: int = 2)
  • slave_addr -- ที่อยู่ I2C 7 บิตที่ slave นี้ตอบรับ

  • i2c_bus -- หมายเลขอุปกรณ์ต่อพ่วง I2C

class rpc_spi_master -- อินเทอร์เฟซ SPI Master

ควบคุมอุปกรณ์ 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 -- ชื่อพินเลือกชิป (chip-select)

  • freq -- ความถี่นาฬิกาบัส SPI ในหน่วย Hz

  • clk_polarity -- ระดับสัญญาณนาฬิกาขณะไม่ทำงาน (0 หรือ 1)

  • clk_phase -- สุ่มตัวอย่างข้อมูลที่ขอบนาฬิกาแรก (0) หรือขอบที่สอง (1)

  • spi_bus -- หมายเลขอุปกรณ์ต่อพ่วง SPI

การตั้งค่า master และ slave ต้องตรงกัน เชื่อมต่อ CS, SCLK, MOSI, MISO โดยตรง อุปกรณ์ทั้งสองต้องมี ground ร่วมกัน

class rpc_spi_slave -- อินเทอร์เฟซ SPI Slave

ถูกควบคุมโดยอุปกรณ์ 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 -- ชื่อพินอินพุตเลือกชิป (chip-select)

  • clk_polarity -- ระดับสัญญาณนาฬิกาขณะไม่ทำงาน (0 หรือ 1)

  • clk_phase -- สุ่มตัวอย่างข้อมูลที่ขอบนาฬิกาแรก (0) หรือขอบที่สอง (1)

  • spi_bus -- หมายเลขอุปกรณ์ต่อพ่วง SPI

class rpc_uart_master -- อินเทอร์เฟซ UART Master

ควบคุมอุปกรณ์ rpc อื่นผ่าน Serial แบบอะซิงโครนัส (UART)

class rpc.rpc_uart_master(baudrate: int = 9600, uart_port: int = 3)
  • baudrate -- อัตราบอดของ serial

  • uart_port -- หมายเลขอุปกรณ์ต่อพ่วง UART

อัตราบอดของ master และ slave ต้องตรงกัน เชื่อมต่อ TX ของ master กับ RX ของ slave และ RX ของ master กับ TX ของ slave อุปกรณ์ทั้งสองต้องมี ground ร่วมกัน

class rpc_uart_slave -- อินเทอร์เฟซ UART Slave

ถูกควบคุมโดยอุปกรณ์ rpc อื่นผ่าน Serial แบบอะซิงโครนัส (UART)

class rpc.rpc_uart_slave(baudrate: int = 9600, uart_port: int = 3)
  • baudrate -- อัตราบอดของ serial

  • uart_port -- หมายเลขอุปกรณ์ต่อพ่วง UART