rpc — rpc 库

OpenMV Cam上的 rpc 模块允许您将您的OpenMV Cam连接到另一个微控制器或计算机,并在您的OpenMV Cam上执行远程Python(或过程)调用。rpc 模块还允许反向操作,如果您希望您的OpenMV Cam能够在另一个微控制器或计算机上执行远程过程(或Python)调用。

如何使用该库

请查看OpenMV IDE 中的 Remote Control 下的示例脚本。

您需要编辑示例代码以选择要使用的接口,并调整脚本的使用设置。

一般来说,要使用 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() 方法不会返回。此外,为了使从设备更具有抗错误性,您可能希望在 try:except: 中包装 rpc_slave.loop(),以捕获由您的回调方法抛出的任何异常。 rpc 库本身不会生成任何异常。注意:在OpenMV Cam上,传输大型数据结构(如jpeg图像)可能会耗尽堆内存,并生成 MemoryError 异常。

就是这样!rpc 库被设计为简单易用。

class rpc - rpc 虚拟类

rpc基类由 rpc_masterrpc_slave 类重新实现,以创建主和从接口。它是一个纯虚拟类,不应直接使用。

构造函数

class rpc.rpc

创建一个rpc对象。这个构造函数不应该直接使用。

方法

get_bytes(buff, timeout_ms):

这个方法应该由`rpc_master`和`rpc_slave`的特定接口类重新实现。它应该将来自接口的字节填充到 buff 参数中,buff 是一个与 timeout_ms 毫秒长的接口字节对象或内存视图对象相等的 bytearraymemoryview 对象。在超时时,此方法应返回 None。注意,为了主从同步,该方法应尝试始终在至少 timeout_ms 毫秒内完成,并且不要太快,因为 rpc_masterrpc_slave 对象会自动增加 timeout_ms 以进行同步。

put_bytes(data, timeout_ms):

这个方法应该由 rpc_masterrpc_slave 的特定接口类重新实现。它应该在 timeout_ms 毫秒内在接口上发生 data 字节。如果完成速度比超时快,那没问题。不需要返回值。

stream_reader(call_back, queue_depth=1, read_timeout_ms=5000):

这个方法应该直接调用。在主和从设备同步后,可以调用 stream_reader 以尽可能快地从主或从设备接收数据。call_back 将重复调用,其参数是由 stream_writer 发送的 bytes_or_memory_view 参数。call_back 不应返回任何内容。queue_depth 定义了 stream_writer 在减速并等待 stream_writer 之前可能生成多少帧数据。更高的 queue_depth 值会导致更高的性能(在一定程度上),但是需要 stream_reader 能够处理接口层中的未完成数据包。如果使 queue_depth 大于1,则 call_back 应该非常快速且不阻塞。最后,read_timeout_ms 定义了每次调用要等待多少毫秒才能接收到 bytes_or_memory_view 负载。

在任何错误上,stream_reader 将返回。主和从设备可以尝试在之后重新设置流以继续。

如果需要取消 stream_reader ,只需在 call_back 中引发异常并捕捉它。远程端将自动超时。

stream_writer(call_back, write_timeout_ms=5000):

这个方法应该直接调用。在主和从设备同步后,在返回 callback 的回调时,可以调用 stream_writer 以尽可能快地从主或从设备发送数据。call_back 将重复调用,并且应该返回一个由 stream_reader 发送到 bytes_or_memory_view 对象的对象。 call_back 不应该接受任何参数。最后,write_timeout_ms 定义了等待多少毫秒来发送 call_back 返回的 bytes_or_memory_view 对象。

在任何错误上,stream_writer 将返回。主和从设备可以尝试在之后重新设置流以继续。

如果需要取消 stream_writer,只需要在 call_back 中引发异常并捕获它。远程端将自动超时。

class rpc_master - rpc_master 虚拟类

rpc_master是一个纯虚拟类,不应直接使用。特定接口类应该重新实现rpc_master。

构造函数

class rpc.rpc_master

创建一个rpc_master对象。这个构造函数不应该直接使用。

方法

call(name, data=bytes(), send_timeout=1000, recv_timeout=1000):

在从设备上执行远程调用。name 是要执行的远程函数或方法的字符串名称。data 是将作为远程函数或方法的参数发送的 bytes 对象。send_timeout 定义了尝试连接到从设备并让其执行远程函数或方法时要等待多少毫秒。一旦主开始发送参数到从设备,send_timeout 不适用。库将允许参数最多花费5秒钟才能发送。recv_timeout 定义了在从设备开始执行远程方法后等待接收响应的毫秒数。请注意,一旦主设备开始接收响应,recv_timeout 不适用。库将允许响应最多花费5秒钟才能接收到。

请注意,内部将创建包含 data 副本的新数据包,该数据包将在 rpc 库内部创建。如果尝试传递非常大的数据参数,则可能会在 OpenMV Cam 上遇到内存问题。

class rpc_slave - rpc_slave虚拟类

rpc_slave 是一个纯虚拟类,不应直接使用。特定接口类应该重新实现rpc_slave。

构造函数

class rpc.rpc_slave

创建一个rpc_slave对象。这个构造函数不应该直接使用。

方法

register_callback(cb):

注册可以由主设备执行的回调。回调应该接受一个参数,该参数将是一个 memoryview 对象,它应该返回一个 bytes() 类似的对象作为结果。回调应该在可能的情况下在1秒内返回。

schedule_callback(cb):

在执行完 rpc_slave.loop() 后,不可能在 rpc 库外执行长时间运行的操作。 schedule_callback 允许您在完成调用回调后暂时退出 rpc 库。您应该在执行 rpc 回调方法时执行 schedule_callback ,以注册一个新的非rpc回调,该回调将在成功完成您在其中执行 schedule_callback 的回调后立即执行。该函数或方法不应该接受任何参数。在注册的回调返回后,必须在下一个父回调中重新注册。如果父回调发生任何错误,则注册的回调将不会被调用,并且必须再次注册。下面是如何使用它的示例:

def some_function_or_method_that_takes_a_long_time_to_execute():
   <do stuff>

def normal_rpc_call_back(data):
   <process data>
   interface.schedule_callback(some_function_or_method_that_takes_a_long_time_to_execute)
   return bytes(response)

interface.register_callback(normal_rpc_call_back)

interface.loop()

特别是 schedule_callback 允许您使用 get_bytesput_bytes 方法进行直通式数据传输,该方法可在设备之间进行数据传输而不需要包装成数据包,这会限制在 rpc 库内部移动的数据大小而不会耗尽OpenMV Cam的内存。

setup_loop_callback(cb):

循环回调在 rpc_slave.loop() 的每个循环迭代中调用。与 rpc.schedule_callback() 回调不同,一旦注册,此回调将保持注册状态。您可以使用循环回调来闪烁活动LED等。您不应该使用循环回调来执行任何阻塞代码,因为这会妨碍从主服务器轮询通信。此外,循环回调将根据主服务器尝试执行的回调的时间和内容以可变速率调用。鉴于此,循环回调不适用于任何需要以固定频率执行的方法。

在OpenMV Cam上,如果需要以固定频率执行某些操作,应在执行 rpc_slave.loop() 之前设置一个定时器,并使用基于定时器中断的回调以固定频率执行某些函数或方法。有关更多信息,请参阅如何编写中断处理程序。注意:Mutex 库与 rpc 库一起安装在您的OpenMV Cam上。

loop(recv_timeout=1000, send_timeout=1000):

开始在从设备上执行 rpc 库以接收数据。此方法不返回(除非来自回调的异常)。在执行此方法之前,应首先注册所有回调。但是,在正在注册的回调内部执行此方法是可能的。

recv_timeout 定义了在再次尝试从主设备接收命令之前要等待多长时间。 send_timeout 定义了从设备在等待主设备接收回调响应之前要等待多长时间。在再次尝试接收之前,循环回调将被执行。

class rpc_can_master - CAN 主接口

通过CAN控制另一个 rpc 设备。

构造函数

rpc_can_master(message_if=0x7FF, bit_rate=250000, sample_point=75, can_bus=2):

创建一个CAN rpc 主设备。该接口可移动达1 Mb/s。

  • message_id - CAN总线上用于数据传输的CAN消息(11位)。

  • bit_rate - CAN比特率。

  • sample_point - Tseg1/Tseg2 比率。通常为75%。 (50.0、62.5、75、87.5等)

  • can_bus - 要使用的CAN总线号

注意:主和从消息id和can比特率必须匹配。将主can高连接到从can高,将主can低连接到从can低。CAN总线必须使用120欧姆终端电阻终止。

class rpc_can_slave - CAN从接口

通过CAN接口由另一个 rpc 设备控制。

构造函数

rpc_can_slave(message_id=0x7FF, bit_rate=250000, sample_point=75, can_bus=2):

创建一个CAN rpc 从设备。该接口可移动达 1 Mb/s。

  • message_id - CAN总线上用于数据传输的CAN消息(11位)。

  • bit_rate - CAN比特率。

  • sample_point - Tseg1/Tseg2 比率。通常为75%。 (50.0、62.5、75、87.5等)

  • can_bus - 要使用的CAN总线号

注意:主和从消息id和can比特率必须匹配。将主can高连接到从can高,将主can低连接到从can低。CAN总线必须使用120欧姆终端电阻终止。

class rpc_i2c_master - I2C 主接口

通过I2C控制另一个 rpc 设备。

构造函数

class rpc.rpc_i2c_master(slave_addr=0x12, rate=100000, i2c_bus=2)

创建一个 I2C rpc 主设备。该接口可移动达 1 Mb/s。

  • slave_addr - I2C 地址。

  • rate - I2C 总线时钟频率。

  • i2c_bus - 要使用的I2C总线号。

注意:主和从地址必须匹配。将主scl连接到从scl,将主sda连接到从sda。您必须使用外部上拉电阻。最后,两个设备必须共享地线。

class rpc_i2c_slave - I2C 从接口

通过I2C接口由另一个 rpc 设备控制。

构造函数

class rpc.rpc_i2c_slave(slave_addr=0x12, i2c_bus=2)

创建一个I2C rpc 从设备。该接口可移动达1 Mb/s。

  • slave_addr - I2C 地址。

  • i2c_bus - 要使用的I2C总线号。

注意:主和从地址必须匹配。将主scl连接到从scl,将主sda连接到从sda。您必须使用外部上拉电阻。最后,两个设备必须共享地线。

class rpc_spi_master - SPI 主接口

通过SPI控制另一个 rpc 设备。

构造函数

class rpc.rpc_spi_master(cs_pin='P3', freq=10000000, clk_polarity=1, clk_phase=0, spi_bus=2)

创建一个SPI rpc 主设备。该接口可以动达 80 Mb/s。

  • cs_pin - 从设备选择引脚。

  • freq - SPI 总线时钟频率。

  • clk_polarity - 闲置时钟电平(0或1)。

  • clk_phase - 在时钟的第一个(0)或第二个(1)边沿上对数据进行采样。

  • spi_bus - 要使用的SPI总线号。

注意:主和从设备必须匹配。将CS、SCLK、MOSI、MISO连接到CS、SCLK、MOSI、MISO。最后,两个设备必须共享一个公共地线。

class rpc_spi_slave - SPI 从接口

通过SPI由另一个 rpc 设备控制。

class rpc.rpc_spi_slave(cs_pin='P3', clk_polarity=1, clk_phase=0, spi_bus=2)

创建一个 SPI rpc 从设备。该接口可移动达 80 Mb/s。

  • cs_pin - 从设备选择引脚。

  • clk_polarity - 闲置时钟电平(0或1)。

  • clk_phase - 在时钟的第一个(0)或第二个(1)边沿上对数据进行采样。

  • spi_bus - 要使用的SPI总线号。

注意:主和从设备必须匹配。将CS、SCLK、MOSI、MISO连接到CS、SCLK、MOSI、MISO。最后,两个设备必须共享一个公共地线。

class rpc_uart_master - UART 主接口

通过异步串行(UART)控制另一个 rpc 设备。

class rpc.rpc_uart_master(baudrate=115200, uart_port=3)

创建一个UART rpc 主设备。该接口可移动达 7.5 Mb/s。

  • baudrate - 串行波特率。

  • uart_port - 要使用的UART端口。

注意:主和从波特率必须匹配。将主tx连接到从rx,将主rx连接到从tx。最后,两个设备必须共享一个公共地线。

class rpc_uart_slave - UART 从接口

通过异步串行(UART)由另一个 rpc 设备控制。

class rpc.rpc_uart_slave(baudrate=115200, uart_port=3)

创建一个UART rpc 从设备。该接口可移动达7.5 Mb/s.

  • baudrate - 串行波特率。

  • uart_port - 要使用的UART端口。

注意:主和从波特率必须匹配。将主tx连接到从rx,将主rx连接到从tx。最后,两个设备必须共享一个公共地线。

class rpc_usb_vcp_master - USB VCP 主接口

通过USB虚拟COM串口控制另一个 rpc 设备。

class rpc.rpc_usb_vcp_master

创建一个USB VCP rpc 主控。该接口可以以USB速度传输数据。

class rpc_usb_vcp_slave - USB VCP 从机接口

通过USB虚拟COM串口由另一个 rpc 设备控制。

class rpc.rpc_usb_vcp_slave

创建一个USB VCP rpc 从机。该接口可以以USB速度传输数据。

class rpc_network_master - 网络主控接口

通过网络控制另一个 rpc 设备。

class rpc.rpc_network_master(network_if, port=0x1DBA)

创建一个网络 rpc 主控。

  • network_if - 从 network.LAN(), network.WLAN() 等创建的网络接口。

  • port - 路由流量的端口。

class rpc_network_slave - 网络从机接口

由网络中的另一个 rpc 设备控制。

class rpc.rpc_network_slave(network_if, port=0x1DBA)

创建一个网络 rpc 从机。

  • network_if - 从 network.LAN(), network.WLAN() 等创建的网络接口。

  • port - 路由流量的端口。