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:

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 – 11-битный идентификатор сообщения CAN, используемый для передачи данных.

  • 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 – 7-битный I2C-адрес ведомого устройства.

  • rate – тактовая частота шины I2C в Гц.

  • 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 – 7-битный I2C-адрес, на который отвечает этот ведомый.

  • 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 – имя вывода выбора кристалла (chip-select).

  • freq – тактовая частота шины SPI в Гц.

  • 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 – имя входного вывода выбора кристалла (chip-select).

  • 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.