rpc — biblioteca rpc

O módulo rpc na OpenMV Cam permite-lhe ligar a sua OpenMV Cam a outro microcontrolador ou computador e executar chamadas remotas de python (ou de procedimento) na sua OpenMV Cam. O módulo rpc também permite o inverso, caso queira que a sua OpenMV Cam execute chamadas de procedimento remoto (ou python) noutro microcontrolador ou computador.

Como utilizar a biblioteca

Um escravo mínimo que expõe um callback por 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.

O mestre correspondente que pede ao escravo um fotograma 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")

Substitua rpc_uart_master / rpc_uart_slave pelo par correspondente can, i2c ou spi para utilizar um transporte diferente.

Em geral, para o dispositivo controlador utilizar a biblioteca rpc, deve criar um objeto de interface usando a biblioteca rpc. Por exemplo:

interface = rpc.rpc_uart_master(baudrate=115200)

Isto cria uma interface UART para comunicar com um escravo rpc.

Depois de criada a interface, basta fazer:

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

E a biblioteca rpc tentará executar esse "remote_function_or_method_name" no escravo. A função ou método remoto receberá o bytes_object_argument, que pode ter até 2^32-1 bytes de tamanho. Assim que o método remoto terminar a execução, devolverá um memory_view_object_result, que também pode ter até 2^32-1 bytes. Como o argumento e a resposta são ambos contentores de bytes genéricos, pode passar qualquer coisa através da biblioteca rpc e receber qualquer tipo de resposta. Uma forma simples de passar argumentos é usar struct.pack() para criar o argumento e struct.unpack() para o receber do outro lado. Para a resposta, o outro lado pode enviar um objeto string ou uma string json como resultado, que o mestre pode então interpretar.

Quanto a erros, se tentar executar um nome de função ou método inexistente, o método rpc_master.call() devolverá um objeto bytes() vazio. Se a biblioteca rpc não conseguir comunicar com o escravo, a biblioteca rpc devolverá None.

Para simplificar, a biblioteca rpc não mantém uma ligação entre os dispositivos mestre e escravo. O método rpc_master.call() encapsula a tentativa de ligação ao escravo, o início da execução da função ou método remoto e a obtenção do resultado.

Agora, do lado do escravo, tem de criar uma interface rpc para comunicar com o mestre. Ficará assim:

interface = rpc.rpc_uart_slave(baudrate=115200)

Isto criará a camada de interface UART para comunicar com um mestre rpc.

Depois de criar a interface de escravo, precisa de registar os callbacks que o mestre pode chamar através do objeto de interface:

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

interface.register_callback(remote_function_or_method_name)

Pode registar tantos callbacks quantos desejar no escravo. Por fim, depois de terminar o registo de callbacks, basta executar:

interface.loop()

No escravo, para iniciar a biblioteca rpc e começar a escutar o mestre. Note que o método rpc_slave.loop() não retorna.

class rpc – classe base rpc

A classe base rpc é reimplementada pelas classes rpc_master e rpc_slave para criar as interfaces mestre e escravo. Não se destina a ser utilizada diretamente.

class rpc.rpc

Cria um objeto rpc. Não se destina a ser utilizado diretamente.

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

Reimplementado por subclasses específicas de transporte. Preenche buff com bytes da interface subjacente dentro de timeout_ms milissegundos. Devolve None em caso de timeout.

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

Reimplementado por subclasses específicas de transporte. Envia data pela interface subjacente dentro de timeout_ms milissegundos.

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

Recebe uma sequência de payloads de um rpc.stream_writer remoto. Deve ser chamado dentro de um callback rpc_slave (ou diretamente após um rpc_master.call bem-sucedido) depois de ambos os lados terem sincronizado.

  • call_back – callable invocado uma vez por payload recebido como call_back(data) onde data é um memoryview. O valor de retorno é ignorado.

  • queue_depth – número de fotogramas em voo que o escritor pode enviar antes de aguardar pelo leitor. Valores mais altos aumentam o débito à custa de memória.

  • read_timeout_ms – milissegundos a aguardar por cada payload.

Retorna em caso de qualquer erro. Para cancelar, levante uma exceção dentro de call_back; o lado remoto atingirá o timeout.

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

Envia uma sequência de payloads para um rpc.stream_reader remoto. Deve ser chamado dentro de um callback rpc_slave (ou diretamente após um rpc_master.call bem-sucedido) depois de ambos os lados terem sincronizado.

  • call_back – callable invocado sem argumentos que devolve o próximo payload bytes ou memoryview a enviar.

  • write_timeout_ms – milissegundos a aguardar ao enviar cada payload.

Retorna em caso de qualquer erro. Para cancelar, levante uma exceção dentro de call_back; o lado remoto atingirá o timeout.

class rpc_master – classe base rpc_master

O rpc_master é uma classe base. Utilize uma das subclasses específicas de transporte (rpc_can_master, rpc_i2c_master, rpc_spi_master, rpc_uart_master).

class rpc.rpc_master

Cria um objeto rpc_master. Não se destina a ser utilizado diretamente.

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

Executa uma chamada remota no dispositivo escravo.

  • name – nome em string da função ou método remoto a executar.

  • data – objeto do tipo bytes passado como argumento à função remota.

  • send_timeout – milissegundos a aguardar enquanto se liga ao escravo e inicia a execução da função remota. Após o mestre começar a enviar o argumento, este parâmetro deixa de ser aplicado; a biblioteca permite até 5 segundos para a transferência do argumento.

  • recv_timeout – milissegundos a aguardar que o escravo comece a devolver uma resposta. Após o mestre começar a receber a resposta, este parâmetro deixa de ser aplicado; a biblioteca permite até 5 segundos para a transferência da resposta.

Devolve um memoryview da resposta em caso de sucesso, um bytes() vazio se o nome remoto não existir no escravo, ou None em caso de falha de comunicação.

class rpc_slave – classe base rpc_slave

O rpc_slave é uma classe base. Utilize uma das subclasses específicas de transporte (rpc_can_slave, rpc_i2c_slave, rpc_spi_slave, rpc_uart_slave).

class rpc.rpc_slave

Cria um objeto rpc_slave. Não se destina a ser utilizado diretamente.

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

Regista um callback que o mestre pode invocar pelo nome. cb é um callable que recebe um argumento memoryview e devolve um objeto do tipo bytes. O __name__ do callback é usado como chave de pesquisa.

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

Agenda cb (um callable sem argumentos) para ser executado uma vez, imediatamente após o callback rpc atualmente em execução devolver com sucesso a sua resposta ao mestre. Deve ser chamado dentro de um callback rpc. Permite que trabalho de longa duração ou transferências cut-through rpc.get_bytes/rpc.put_bytes sejam executados entre transações rpc. Registe novamente em cada invocação se for necessária execução repetida.

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

Regista cb (um callable sem argumentos) para ser invocado em cada iteração de rpc_slave.loop. Ao contrário de rpc_slave.schedule_callback, este callback permanece registado. Deve ser não-bloqueante; a taxa de chamada é variável.

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

Executa o ciclo de despacho do escravo rpc. Não retorna exceto por exceção levantada dentro de um callback.

  • recv_timeout – milissegundos a aguardar por um comando do mestre antes de tentar novamente.

  • send_timeout – milissegundos a aguardar que o mestre confirme a resposta antes de voltar a receber.

class rpc_can_master – Interface Mestre CAN

Controla outro dispositivo rpc por 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 de mensagem CAN de 11 bits utilizado para transporte de dados.

  • bit_rate – taxa de bits CAN em bits por segundo.

  • sample_point – percentagem do ponto de amostragem Tseg1/Tseg2 (ex.: 50.0, 62.5, 75, 87.5).

  • can_bus – número do periférico CAN.

Os message_id e bit_rate do mestre e do escravo devem coincidir. O barramento deve ser terminado com 120 ohms.

class rpc_can_slave – Interface Escravo CAN

É controlado por outro dispositivo rpc por CAN.

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

Consulte rpc_can_master para a descrição dos argumentos.

class rpc_i2c_master – Interface Mestre I2C

Controla outro dispositivo rpc por I2C.

class rpc.rpc_i2c_master(slave_addr: int = 0x12, rate: int = 100000, i2c_bus: int = 2)
  • slave_addr – endereço I2C de 7 bits do dispositivo escravo.

  • rate – frequência do relógio do barramento I2C em Hz.

  • i2c_bus – número do periférico I2C.

Os endereços do mestre e do escravo devem coincidir. São necessários pull-ups externos em SCL e SDA, e ambos os dispositivos devem partilhar um terra comum.

class rpc_i2c_slave – Interface Escravo I2C

É controlado por outro dispositivo rpc por I2C.

class rpc.rpc_i2c_slave(slave_addr: int = 0x12, i2c_bus: int = 2)
  • slave_addr – endereço I2C de 7 bits ao qual este escravo responde.

  • i2c_bus – número do periférico I2C.

class rpc_spi_master – Interface Mestre SPI

Controla outro dispositivo rpc por 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 – nome do pino de chip-select.

  • freq – frequência do relógio do barramento SPI em Hz.

  • clk_polarity – nível do relógio em repouso (0 ou 1).

  • clk_phase – amostra os dados na primeira (0) ou segunda (1) transição do relógio.

  • spi_bus – número do periférico SPI.

As definições do mestre e do escravo devem coincidir. Ligue CS, SCLK, MOSI e MISO diretamente. Ambos os dispositivos devem partilhar um terra comum.

class rpc_spi_slave – Interface Escravo SPI

É controlado por outro dispositivo rpc por SPI.

class rpc.rpc_spi_slave(cs_pin: str = 'P3', clk_polarity: int = 1, clk_phase: int = 0, spi_bus: int = 2)
  • cs_pin – nome do pino de entrada chip-select.

  • clk_polarity – nível do relógio em repouso (0 ou 1).

  • clk_phase – amostra os dados na primeira (0) ou segunda (1) transição do relógio.

  • spi_bus – número do periférico SPI.

class rpc_uart_master – Interface Mestre UART

Controla outro dispositivo rpc por Serial Assíncrono (UART).

class rpc.rpc_uart_master(baudrate: int = 9600, uart_port: int = 3)
  • baudrate – taxa de baud série.

  • uart_port – número do periférico UART.

As taxas de baud do mestre e do escravo devem coincidir. Ligue o TX do mestre ao RX do escravo e o RX do mestre ao TX do escravo. Ambos os dispositivos devem partilhar um terra comum.

class rpc_uart_slave – Interface Escravo UART

É controlado por outro dispositivo rpc por Serial Assíncrono (UART).

class rpc.rpc_uart_slave(baudrate: int = 9600, uart_port: int = 3)
  • baudrate – taxa de baud série.

  • uart_port – número do periférico UART.