basic concepts - modrpc/info GitHub Wiki

Table of Contents

Overview

ModPy is a Python library for programming software systems in distributed and mobile environments, where a ModPy system is loosely defined to be a collection of ModPy nodes. It provides functions which facilitate the development of services of nodes which can be used by remote nodes. Also, it allows to define processes reactive to remote events.

ModPy Nodes

A ModPy system is a collection of interconnected ModPy nodes, where a ModPy node is any device where the ModPy runtime is running. Each ModPy node has a unique name within the system. A ModPy system is a dynamic notion in the sense that as ModPy nodes come and go, the system can dynamically change.

Within the system, nodes can interact with other nodes by using the names of nodes. A node name is a string which makes handling of the ModPy system very easy.

  • A ModPy node is a container of ModPy modules.
  • A ModPy module consists of ModPy resources.
  • A ModPy resource is a function, an event, or a process.
    • A ModPy function is a service which can be accessed by other nodes.
    • A ModPy event is an event which can be observed by other nodes.
    • A ModPy process is a piece of code reactive to ModPy events.

ModPy Nodes

A ModPy node is any device which runs the ModPy runtime. Each ModPy node has a unique name, through which other nodes can designate the node. A node can provide services to other node. Also, a node can contain reactive processes, which reacts to events generated by other nodes. Typically, a set of services and reactive processes are bundled in a single Python module. For example, mods.robot_ctrl package can contain servo module, which in turn contain services such as turn_right and turn_left.

A ModPy node is a container of ModPy modules.

node = modpy.init_node(name="mynode0")      # initialize the node
from mods.robot_ctrl import servo, motor    # import ModPy modules
node.start()                                # start the node

ModPy Resources

A ModPy resource is an artifact which either provides services to other nodes or adds an internal behavior of nodes. There are three types of resources: functions, events, and processes.

ModPy Functions

A ModPy function is a Python function which can be invoked either locally or from other nodes. A ModPy function can be defined by adding the decorator @modpy.func.

@modpy.func
def add(a, b):
  return a + b
Given that a ModPy node "n0" contains this resource, another note n1 can call this function as follows.
result = await modpy.call("n0/add", 10, 20)
"n0/add" is the ModPy URL which designates the "add" resource within the node named "n0". For details on URLs, see the ModRPC protocol.

ModPy Events

A ModPy event is a resource which can be fired by its owner and be observed by its subscribers. To define a new event, use the @modpy.event decorator.

@modpy.event
def data_ready_ev():
  # return payload, if any, which will be shipped along with the event notification
  return data_value; 

Given that a node "n0" provides data_ready_ev event, "n0" can generate its event instance using the ModPy function fire. It will wake up all subscribers, which have been waiting for this event.

 # a function inside node "n0" can fire the event

 @modpy.proc
 def battery_watcher():
   while True:
     if (battery_level < 25):
       # by default, the result of data_ready_ev() will be sent as the payload
       # of the event instance; one can override this by adding the optional 
       # payload argument
       await modpy.fire("data_ready_ev");

Writing a code which waits for the event is simple.

# another note has a process which waits for the event

@modpy.proc
def my_event_subscriber():
  while True:
     event = await modpy.waitfor("n0/data_ready_ev")
     print(event.value)  # event.value contains the data sent

Also, one can wait for multiple events at the same time.

while True:
  event = await modpy.waitfor("n0/data_ready_ev", "n1/data_ready_ev")
  if (event.key == "n0/data_ready_ev"):
    print("Data ready at n0")
  else:
    print("Data ready at n1")

ModPy Processes

A ModPy process is a function with an infinite loop which waits for events and performs actions over the received events. A ModPy process can be started, stopped, resumed, and killed.

A ModPy node can contain multiple processes alive at the same time.

A ModPy process is essentially a generalization of event callbacks, which waits for some event and perform some predefined functions.

Below example shows a process which models a pipeline stage which 1) waits for data to be ready, 2) grabs the data, and 3) notify that data is available.

@modpy.proc
def fifo_stage(leftnode):
  while True:
     event = await modpy.waitfor("%s/data_ready" % leftnode)
     data = ""
     while data != "<EOF>":
       line = await modpy.call("%s/get_line" % leftnode)
     await modpy.fire("data_ready")
As was illustrated in this example, we don't need to hardcode the name of the node. Also, note that the format of name (naming scheme) is not enforced. By default, it uses the naming scheme specified in the ModRPC protocol but user can customize the naming scheme.

Consider a series of nodes: "n0" through "n9". Each node contains a fifo_stage process inside.

for i in reversed(range(9)):
  await modpy.start("n%d:fifo_stage"%(i+1), "n%d"%i)

ModPy Modules

ModPy stands for "Modular RPC for Python". Originally, I started working on a different language (Golang) where there is no "module" construct. However, Python does have "modules" so it could be a bit confusing when we say "ModPy modules'. Just consider them as loosely meaning that it is a collection of ModPy resources -- techinically, it is a Python package which contains ModPy resource definitions.

⚠️ **GitHub.com Fallback** ⚠️