rpc --- rpc 函式庫

OpenMV Cam 上的 rpc 模組可讓你將 OpenMV Cam 連接到另一個微控制器或電腦,並在 OpenMV Cam 上執行遠端 python(或程序)呼叫。rpc 模組也支援反向操作,讓你的 OpenMV Cam 能夠在另一個微控制器或電腦上執行遠端程序(或 python)呼叫。

如何使用本函式庫

透過 UART 公開單一回呼函式的最小化 slave 範例:

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.

向 slave 要求一個 JPEG 影格的對應 master 範例:

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 替換為對應的 cani2cspi 配對,即可使用不同的傳輸方式。

一般而言,控制端裝置要使用 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 函式庫會嘗試在 slave 上執行該 "remote_function_or_method_name"。遠端函式或方法會收到 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() 方法不會回傳。

class rpc -- rpc 基底類別

rpc 基底類別由 rpc_masterrpc_slave 類別重新實作,以建立 master 與 slave 介面。它並非設計成直接使用。

class rpc.rpc

建立一個 rpc 物件。並非設計成直接使用。

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

由各傳輸專屬的子類別重新實作。在 timeout_ms 毫秒內,以底層介面的位元組填入 buff。逾時則回傳 None

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

由各傳輸專屬的子類別重新實作。在 timeout_ms 毫秒內,透過底層介面傳送 data

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),其中 datamemoryview。其回傳值會被忽略。

  • 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 -- 不帶引數呼叫的可呼叫物件,回傳下一個要傳送的 bytesmemoryview 酬載。

  • write_timeout_ms -- 傳送每個酬載時要等待的毫秒數。

發生任何錯誤即回傳。若要取消,請在 call_back 內引發例外;遠端端會逾時。

class rpc_master -- rpc_master 基底類別

rpc_master 是基底類別。請使用其中一個傳輸專屬的子類別(rpc_can_masterrpc_i2c_masterrpc_spi_masterrpc_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;若遠端名稱在 slave 上不存在則回傳空的 bytes();通訊失敗時則回傳 None

class rpc_slave -- rpc_slave 基底類別

rpc_slave 是基底類別。請使用其中一個傳輸專屬的子類別(rpc_can_slaverpc_i2c_slaverpc_spi_slaverpc_uart_slave)。

class rpc.rpc_slave

建立一個 rpc_slave 物件。並非設計成直接使用。

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

註冊一個 master 可依名稱呼叫的回呼函式。cb 是一個可呼叫物件,接受一個 memoryview 引數並回傳類 bytes 物件。回呼函式的 __name__ 會作為查找用的鍵。

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

排程 cb(一個不帶引數的可呼叫物件)執行一次,時機為目前執行中的 rpc 回呼函式成功將其回應回傳給 master 之後。必須在 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 slave 派發迴圈。除非回呼函式引發例外,否則不會回傳。

  • recv_timeout -- 在重試之前等待 master 命令的毫秒數。

  • send_timeout -- 在返回接收狀態之前等待 master 確認回應的毫秒數。

class rpc_can_master -- CAN Master 介面

透過 CAN 控制另一個 rpc 裝置。

class rpc.rpc_can_master(message_id: int = 0x7FF, bit_rate: int = 250000, sample_point: float = 75, can_bus: int = 2)
  • message_id -- 用於資料傳輸的 11 位元 CAN 訊息 id。

  • bit_rate -- 以每秒位元數表示的 CAN 位元速率。

  • sample_point -- Tseg1/Tseg2 取樣點百分比(例如 50.0、62.5、75、87.5)。

  • can_bus -- CAN 周邊裝置編號。

master 與 slave 的 message_idbit_rate 必須相符。匯流排必須以 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 介面

透過 I2C 控制另一個 rpc 裝置。

class rpc.rpc_i2c_master(slave_addr: int = 0x12, rate: int = 100000, i2c_bus: int = 2)
  • slave_addr -- slave 裝置的 7 位元 I2C 位址。

  • rate -- 以 Hz 表示的 I2C 匯流排時脈頻率。

  • i2c_bus -- I2C 周邊裝置編號。

master 與 slave 的位址必須相符。SCL 與 SDA 需要外部上拉電阻,且兩個裝置必須共用同一接地。

class rpc_i2c_slave -- I2C Slave 介面

由另一個 rpc 裝置透過 I2C 控制。

class rpc.rpc_i2c_slave(slave_addr: int = 0x12, i2c_bus: int = 2)
  • slave_addr -- 此 slave 所回應的 7 位元 I2C 位址。

  • i2c_bus -- I2C 周邊裝置編號。

class rpc_spi_master -- SPI Master 介面

透過 SPI 控制另一個 rpc 裝置。

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 -- 以 Hz 表示的 SPI 匯流排時脈頻率。

  • clk_polarity -- 閒置時的時脈準位(0 或 1)。

  • clk_phase -- 在第一個(0)或第二個(1)時脈邊緣取樣資料。

  • spi_bus -- SPI 周邊裝置編號。

master 與 slave 的設定必須相符。請直接連接 CS、SCLK、MOSI、MISO。兩個裝置必須共用同一接地。

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 -- 晶片選擇輸入接腳名稱。

  • clk_polarity -- 閒置時的時脈準位(0 或 1)。

  • clk_phase -- 在第一個(0)或第二個(1)時脈邊緣取樣資料。

  • spi_bus -- SPI 周邊裝置編號。

class rpc_uart_master -- UART Master 介面

透過非同步序列(UART)控制另一個 rpc 裝置。

class rpc.rpc_uart_master(baudrate: int = 9600, uart_port: int = 3)
  • baudrate -- 序列鮑率。

  • uart_port -- UART 周邊裝置編號。

master 與 slave 的鮑率必須相符。請將 master 的 TX 連接到 slave 的 RX,並將 master 的 RX 連接到 slave 的 TX。兩個裝置必須共用同一接地。

class rpc_uart_slave -- UART Slave 介面

由另一個 rpc 裝置透過非同步序列(UART)控制。

class rpc.rpc_uart_slave(baudrate: int = 9600, uart_port: int = 3)
  • baudrate -- 序列鮑率。

  • uart_port -- UART 周邊裝置編號。