rpc — rpc库

rpc 模块允许您将znzpi AI相机连接到另一个微控制器或计算机,并对znzpi AI相机执行远程python(或程序)调用。 如果您希望您的znzpi AI相机能够在另一个微控制器或计算机上执行远程程序(或python)调用,那么``rpc`` 模块也允许反向操作。

如何使用库

请在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" 。 远程函数或方法将接收大小可达 2^32-1 字节的 bytes_object_argument 。 一旦远程方法完成执行,它将返回一个 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)

您可以在slave上注册任意多的回调函数。 最后,一旦你完成了回调,你只需要执行:

interface.loop()

在slave上启动``rpc`` 库并开始监听master。请注意,``rpc_slave.loop()`` 方法不返回。 此外,为了使您的 slave 更健壮地抵抗错误, 您可能需要将``rpc_slave.loop()`` 与 try: 和``except:`` 包装起来,以应对回调方法可能抛出的任何异常。 rpc 库本身不会生成任何异常。 注意:传递大型数据结构(如 jpeg 图像)可能会耗尽znzpi AI相机 上的堆并生成 MemoryError 异常。

就是这样! rpc 库被设计成易于使用。

rpc类 - rpc虚拟类

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

构造函数

class rpc.rpc

创建rpc对象。此构造函数不能直接使用。

方法

get_bytes(buff, timeout_ms):

这个方法需要通过特定的接口类 rpc_masterrpc_slave 来重新实现。 它应该填充 buff 参数,该参数是来自接口的字节的 bytearraymemoryview 对象, 等于 timeout_ms 毫秒内的 buff 对象的长度。超时时,此方法应返回 None 。 请注意,对于 master 和 slave 同步,此方法应尝试始终在至少 timeout_ms 毫秒内完成而不是更快, 因为 rpc_masterrpc_slave 对象将自动增加 timeout_ms 以进行同步。

put_bytes(data, timeout_ms):

这个方法需要通过特定的接口类 rpc_masterrpc_slave 来重新实现。 如果它完成得比超时快,那没问题。预计没有返回值。

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

这个方法是直接调用的。在主设备和从设备同步返回回调后,可能会调用 stream_reader 以尽快从主设备或从设备接收数据。 call_back 将使用 stream_writer 发送的 bytes_or_memory_view argument 重复调用。 call_back 不会返回任何东西。 queue_depth 定义了 stream_writer 在减慢和等待 stream_reader 之前可能生成的数据帧数。 更高的 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 将被重复调用,并且应该返回一个 bytes_or_memory_view 对象,该对象将被发送到 stream_readercall_back 不应接受任何参数。最后,write_timeout_ms 定义了等待发送 call_back 返回的 bytes_or_memory_view 对象的毫秒数。

在任何错误 stream_writer 都会返回。主从设备可以在之后再次尝试设置流以继续。

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

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 定义从服务器开始执行远程方法以接收响应后等待的毫秒数。 请注意,一旦 master 开始接收响应, recv_timeout 将不适用。库将允许接收响应最多需要 5 秒。

注意,一个包含 data 副本的新数据包将在 rpc 库内部创建。 如果试图传递非常大的数据,您可能会在znzpi AI相机上遇到内存问题。

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 库内部移动的数据大小的打包成本,而不会耗尽znzpi AI相机上的内存。

setup_loop_callback(cb):

循环回调在 rpc_slave.loop() 的每次循环迭代中被调用。 与 rpc.schedule_callback() 回调不同,此回调在注册一次后保持注册状态。 您可以使用循环回调来闪烁活动 LED 或类似的东西。 你不应该使用循环回调来执行任何阻塞代码,因为这会妨碍从主节点轮询通信。 此外,循环回调将以可变速率调用,具体取决于主设备尝试执行的回调的时间和内容。 鉴于此,循环回调不适用于任何需要以固定频率执行的方法。

在znzpi AI相机上,如果您需要以固定频率执行某事,您应该在执行 rpc_slave.loop() 之前设置一个计时器, 并使用基于计时器中断的回调以固定频率执行某些功能或方法。请参阅如何编写中断处理程序以获取更多信息。 注意: Mutex 库与 rpc 库一​​起安装在您的 znzpi AI Cam 上。

loop(recv_timeout=1000, send_timeout=1000):

在从设备上开始执行 rpc 库以接收数据。此方法不返回(除非通过回调异常)。 在执行此方法之前,您应该先注册所有回调。但是,可以在之前注册的正在执行的回调中注册新的回调。

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

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总线(11位)上用于数据传输的CAN消息。

  • bit_rate - CAN 比特率.

  • sampling_point - Tseg1/Tseg2 比率. 一般是 75%. (50.0, 62.5, 75, 87.5, 等。)

  • can_bus - 可用CAN 总线数

注意:主从消息 ID 和比特率必须匹配。连接主机高那么从机也高,主机低到那么从机也低。CAN总线必须以 120ohms 终止。 在STM32上使用CAN2。

rpc_can_slave类 - CAN从接口

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

构造函数

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

创建一个CAN rpc slave 。这个接口可以到1 Mb/s。

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

  • bit_rate - CAN 比特率.

  • sampling_point - Tseg1/Tseg2 比率. 一般是 75%. (50.0, 62.5, 75, 87.5, 等。)

  • can_bus - 可用CAN 总线数

注意:主从消息 ID 和比特率必须匹配。连接主机高那么从机也高,主机低到那么从机也低。CAN总线必须以 120ohms 终止。 在STM32上使用CAN2。

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 Bus Clock Frequency.

  • rate - I2C 总线时钟频率。

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

rpc_i2c_slave类 - I2C从接口

通过I2C被另一个``rpc`` 装置控制。

构造函数

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

创建一个I2C rpc slave。这个接口可以到1 Mb/s。

  • slave_addr - I2C 地址

  • rate - I2C 总线时钟频率。

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

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 主服务器。这个接口可以到80mb /s

  • cs_pin - Slave 选择引脚

  • 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 Slave Interface

rpc_spi_slave类 - SPI从接口

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

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

  • cs_pin - Slave 选择引脚

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

  • clk_phase - 在时钟的第一(0)或第二(1)边采样数据。

  • spi_bus - 使用的SPI总线数

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

rpc_uart_master类 - UART主接口

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

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

创建一个UART rpc master。这个接口可以移到7.5 Mb/s。

  • baudrate - 串行波特率.

  • uart_port - 使用的UART端口

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

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。 最后,两个设备必须共地。

rpc_usb_vcp_master类 - USB VCP主接口

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

class rpc.rpc_usb_vcp_master

创建USB VCP rpc 主设备。

rpc_usb_vcp_slave类 - USB VCP从接口

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

class rpc.rpc_usb_vcp_slave

创建USB VCP rpc 从设备。这个接口可以超过12mb /s。

rpc_network_master类 - 网络主控接口

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

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

创建一个网络 rpc 主控

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

  • port - 通讯用的端口

rpc_network_slave类 - 网络从接口

被另外一个 rpc 网络设备控制。

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

创建一个 rpc 网络从设备

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

  • port - 通讯用的端口