rpc --- thư viện rpc

Mô-đun rpc trên OpenMV Cam cho phép bạn kết nối OpenMV Cam với một vi điều khiển hoặc máy tính khác và thực thi các lời gọi python (hoặc thủ tục) từ xa trên OpenMV Cam. Mô-đun rpc cũng hỗ trợ chiều ngược lại, cho phép OpenMV Cam thực thi các lời gọi thủ tục (hoặc python) từ xa trên một vi điều khiển hoặc máy tính khác.

Cách sử dụng thư viện

Một slave tối giản để hiển thị một hàm gọi lại qua 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 tương ứng yêu cầu slave trả về một khung hình 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")

Thay rpc_uart_master / rpc_uart_slave bằng cặp can, i2c hoặc spi tương ứng để sử dụng giao thức truyền thông khác.

Nhìn chung, để thiết bị điều khiển sử dụng thư viện rpc, bạn sẽ tạo một đối tượng giao diện bằng thư viện rpc. Ví dụ:

interface = rpc.rpc_uart_master(baudrate=115200)

Lệnh này tạo một giao diện UART để giao tiếp với một slave rpc.

Sau khi tạo giao diện, bạn chỉ cần thực hiện:

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

Thư viện rpc sẽ cố thực thi "remote_function_or_method_name" đó trên slave. Hàm hoặc phương thức từ xa sẽ nhận bytes_object_argument có kích thước lên tới 2^32-1 byte. Sau khi phương thức từ xa thực thi xong, nó sẽ trả về memory_view_object_result cũng có thể lên tới 2^32-1 byte. Vì đối số và phản hồi đều là các vùng chứa byte chung, bạn có thể truyền bất kỳ dữ liệu nào qua thư viện rpc và nhận bất kỳ kiểu phản hồi nào. Một cách đơn giản để truyền đối số là dùng struct.pack() để tạo đối số và struct.unpack() để nhận đối số ở phía kia. Phía còn lại có thể gửi một đối tượng chuỗi hoặc chuỗi json làm kết quả để master diễn giải.

Về lỗi: nếu bạn cố thực thi một tên hàm hoặc phương thức không tồn tại, phương thức rpc_master.call() sẽ trả về đối tượng bytes() rỗng. Nếu thư viện rpc không thể giao tiếp với slave, thư viện rpc sẽ trả về None.

Để đơn giản hóa, thư viện rpc không duy trì kết nối liên tục giữa thiết bị master và slave. Phương thức rpc_master.call() đóng gói quá trình kết nối đến slave, khởi động thực thi hàm hoặc phương thức từ xa, và lấy kết quả.

Bây giờ, ở phía slave, bạn phải tạo một giao diện rpc để giao tiếp với master. Cách thực hiện như sau:

interface = rpc.rpc_uart_slave(baudrate=115200)

Lệnh này sẽ tạo lớp giao diện UART để giao tiếp với một master rpc.

Sau khi tạo giao diện slave, bạn cần đăng ký các hàm gọi lại mà master có thể gọi thông qua đối tượng giao diện:

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

interface.register_callback(remote_function_or_method_name)

Bạn có thể đăng ký bao nhiêu hàm gọi lại tùy ý trên slave. Cuối cùng, sau khi đăng ký xong tất cả hàm gọi lại, bạn chỉ cần thực thi:

interface.loop()

Trên slave để khởi động thư viện rpc và bắt đầu lắng nghe master. Lưu ý rằng phương thức rpc_slave.loop() không trả về.

class rpc -- lớp cơ sở rpc

Lớp cơ sở rpc được cài đặt lại bởi các lớp rpc_masterrpc_slave để tạo ra các giao diện master và slave. Lớp này không được sử dụng trực tiếp.

class rpc.rpc

Tạo một đối tượng rpc. Không dùng trực tiếp.

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

Được cài đặt lại bởi các lớp con theo giao thức truyền thông. Điền vào buff các byte từ giao diện bên dưới trong vòng timeout_ms mili giây. Trả về None khi hết thời gian chờ.

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

Được cài đặt lại bởi các lớp con theo giao thức truyền thông. Gửi data qua giao diện bên dưới trong vòng timeout_ms mili giây.

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

Nhận một luồng payload từ một rpc.stream_writer từ xa. Nên được gọi từ bên trong một hàm gọi lại của rpc_slave (hoặc ngay sau khi rpc_master.call thành công) sau khi cả hai phía đã đồng bộ.

  • call_back -- hàm gọi lại được gọi mỗi lần nhận một payload dưới dạng call_back(data) trong đó data là một memoryview. Giá trị trả về bị bỏ qua.

  • queue_depth -- số lượng khung hình đang truyền mà writer được phép gửi trước khi chờ reader. Giá trị cao hơn tăng thông lượng nhưng tốn thêm bộ nhớ.

  • read_timeout_ms -- mili giây chờ mỗi payload.

Trả về khi có bất kỳ lỗi nào. Để hủy, hãy ném ngoại lệ bên trong call_back; phía từ xa sẽ hết thời gian chờ.

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

Gửi một luồng payload đến một rpc.stream_reader từ xa. Nên được gọi từ bên trong một hàm gọi lại của rpc_slave (hoặc ngay sau khi rpc_master.call thành công) sau khi cả hai phía đã đồng bộ.

  • call_back -- hàm gọi lại được gọi không có đối số, trả về bytes hoặc payload memoryview tiếp theo để gửi.

  • write_timeout_ms -- mili giây chờ khi gửi mỗi payload.

Trả về khi có bất kỳ lỗi nào. Để hủy, hãy ném ngoại lệ bên trong call_back; phía từ xa sẽ hết thời gian chờ.

class rpc_master -- lớp cơ sở rpc_master

The rpc_master là một lớp cơ sở. Hãy sử dụng một trong các lớp con theo giao thức truyền thông (rpc_can_master, rpc_i2c_master, rpc_spi_master, rpc_uart_master).

class rpc.rpc_master

Tạo một đối tượng rpc_master. Không dùng trực tiếp.

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

Thực thi một lời gọi từ xa trên thiết bị slave.

  • name -- tên chuỗi của hàm hoặc phương thức từ xa cần thực thi.

  • data -- đối tượng kiểu bytes được truyền làm đối số cho hàm từ xa.

  • send_timeout -- mili giây chờ khi kết nối đến slave và bắt đầu thực thi hàm từ xa. Sau khi master bắt đầu gửi đối số, tham số này không còn áp dụng; thư viện cho phép tối đa 5 giây để truyền đối số.

  • recv_timeout -- mili giây chờ slave bắt đầu trả về phản hồi. Sau khi master bắt đầu nhận phản hồi, tham số này không còn áp dụng; thư viện cho phép tối đa 5 giây để truyền phản hồi.

Trả về memoryview của phản hồi khi thành công, bytes() rỗng nếu tên từ xa không tồn tại trên slave, hoặc None khi giao tiếp thất bại.

class rpc_slave -- lớp cơ sở rpc_slave

The rpc_slave là một lớp cơ sở. Hãy sử dụng một trong các lớp con theo giao thức truyền thông (rpc_can_slave, rpc_i2c_slave, rpc_spi_slave, rpc_uart_slave).

class rpc.rpc_slave

Tạo một đối tượng rpc_slave. Không dùng trực tiếp.

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

Đăng ký một hàm gọi lại mà master có thể gọi theo tên. cb là một hàm có thể gọi nhận một đối số memoryview và trả về một đối tượng kiểu bytes. Thuộc tính __name__ của hàm gọi lại được dùng làm khóa tra cứu.

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

Lên lịch cho cb (một hàm không nhận đối số) được thực thi một lần, ngay sau khi hàm gọi lại rpc đang chạy trả thành công phản hồi về master. Phải được gọi từ bên trong một hàm gọi lại rpc. Cho phép các tác vụ chạy lâu hoặc các lần truyền cắt ngang rpc.get_bytes/rpc.put_bytes chạy giữa các giao dịch rpc. Đăng ký lại mỗi lần gọi nếu cần thực thi lặp lại.

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

Đăng ký cb (một hàm không nhận đối số) để được gọi trong mỗi vòng lặp của rpc_slave.loop. Khác với rpc_slave.schedule_callback, hàm gọi lại này vẫn được đăng ký sau khi gọi. Phải không chặn; tần suất gọi có thể thay đổi.

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

Chạy vòng lặp xử lý của rpc slave. Không trả về trừ khi có ngoại lệ ném từ một hàm gọi lại.

  • recv_timeout -- mili giây chờ lệnh từ master trước khi thử lại.

  • send_timeout -- mili giây chờ master xác nhận phản hồi trước khi quay lại nhận.

class rpc_can_master -- Giao diện Master CAN

Điều khiển một thiết bị rpc khác qua CAN.

class rpc.rpc_can_master(message_id: int = 0x7FF, bit_rate: int = 250000, sample_point: float = 75, can_bus: int = 2)
  • message_id -- ID thông điệp CAN 11-bit dùng để truyền dữ liệu.

  • bit_rate -- tốc độ bit CAN tính bằng bit trên giây.

  • sample_point -- phần trăm điểm lấy mẫu Tseg1/Tseg2 (ví dụ: 50.0, 62.5, 75, 87.5).

  • can_bus -- số ngoại vi CAN.

message_idbit_rate của master và slave phải khớp nhau. Bus phải được kết thúc bằng điện trở 120 ohm.

class rpc_can_slave -- Giao diện Slave CAN

Được điều khiển bởi một thiết bị rpc khác qua CAN.

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

Xem rpc_can_master để biết mô tả các đối số.

class rpc_i2c_master -- Giao diện Master I2C

Điều khiển một thiết bị rpc khác qua I2C.

class rpc.rpc_i2c_master(slave_addr: int = 0x12, rate: int = 100000, i2c_bus: int = 2)
  • slave_addr -- địa chỉ I2C 7-bit của thiết bị slave.

  • rate -- tần số xung nhịp bus I2C tính bằng Hz.

  • i2c_bus -- số ngoại vi I2C.

Địa chỉ master và slave phải khớp nhau. Cần có điện trở kéo lên bên ngoài cho SCL và SDA, và cả hai thiết bị phải dùng chung một nền đất.

class rpc_i2c_slave -- Giao diện Slave I2C

Được điều khiển bởi một thiết bị rpc khác qua I2C.

class rpc.rpc_i2c_slave(slave_addr: int = 0x12, i2c_bus: int = 2)
  • slave_addr -- địa chỉ I2C 7-bit mà slave này phản hồi.

  • i2c_bus -- số ngoại vi I2C.

class rpc_spi_master -- Giao diện Master SPI

Điều khiển một thiết bị rpc khác qua 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 -- tên chân (pin) chip-select.

  • freq -- tần số xung nhịp bus SPI tính bằng Hz.

  • clk_polarity -- mức xung nhịp khi không hoạt động (0 hoặc 1).

  • clk_phase -- lấy mẫu dữ liệu tại cạnh xung nhịp thứ nhất (0) hoặc thứ hai (1).

  • spi_bus -- số ngoại vi SPI.

Các thiết lập của master và slave phải khớp nhau. Kết nối trực tiếp CS, SCLK, MOSI, MISO. Cả hai thiết bị phải dùng chung một nền đất.

class rpc_spi_slave -- Giao diện Slave SPI

Được điều khiển bởi một thiết bị rpc khác qua SPI.

class rpc.rpc_spi_slave(cs_pin: str = 'P3', clk_polarity: int = 1, clk_phase: int = 0, spi_bus: int = 2)
  • cs_pin -- tên chân (pin) đầu vào chip-select.

  • clk_polarity -- mức xung nhịp khi không hoạt động (0 hoặc 1).

  • clk_phase -- lấy mẫu dữ liệu tại cạnh xung nhịp thứ nhất (0) hoặc thứ hai (1).

  • spi_bus -- số ngoại vi SPI.

class rpc_uart_master -- Giao diện Master UART

Điều khiển một thiết bị rpc khác qua Async Serial (UART).

class rpc.rpc_uart_master(baudrate: int = 9600, uart_port: int = 3)
  • baudrate -- tốc độ baud nối tiếp.

  • uart_port -- số ngoại vi UART.

Tốc độ baud của master và slave phải khớp nhau. Kết nối TX của master với RX của slave và RX của master với TX của slave. Cả hai thiết bị phải dùng chung một nền đất.

class rpc_uart_slave -- Giao diện Slave UART

Được điều khiển bởi một thiết bị rpc khác qua Async Serial (UART).

class rpc.rpc_uart_slave(baudrate: int = 9600, uart_port: int = 3)
  • baudrate -- tốc độ baud nối tiếp.

  • uart_port -- số ngoại vi UART.