Best Practices¶
When writing your SNAPconnect-based application there are a few recommended practices.
Hooks¶
The first recommended practice is to take advantage of the available hooks for controlling which code is executed when
events occur in SNAPconnect. The available hooks are listed in the snapconnect:index section, but typically the most
important to remember are HOOK_RPC_SENT
and HOOK_SNAPCOM_OPENED
.
Just as HOOK_RPC_SENT
is important in a SNAPpy script, it is important in SNAPconnect applications. This
hook allows your program to know when the system has completed processing a specific outgoing RPC (either unicast,
multicast, or directed multicast), and if it was queued for transmission successfully. Based on this knowledge it might
be appropriate for your application to send another RPC or know that there is room in the queue to send another RPC.
Just as in SNAPpy, the rpc() method (as well as the mcastRpc() and dmcastRpc() methods) returns immediately and
does not mean your RPC has been, or even will be, sent. This hook does not trigger until SNAPconnect is done
processing the specified packet (whether that processing was successful or not).
HOOK_SNAPCOM_OPENED
informs your application that you have successfully connected to a remote SNAPconnect
instance or another SNAPconnect instance has connected to you. This is important to know if your application depends
on having connectivity to another instance. If your application is not connected to another SNAPconnect instance or a
SNAP bridge node, all of your RPCs will fail because there is no one else to talk to.
Asynchronous Programming¶
SNAPconnect is designed to run in a single process within a single thread. As you have seen in our sample
applications, once SNAPconnect and your application are initialized, we call the SNAPconnect
loop()
method. This method is a convenience method that is a “while True” loop that
continually calls the SNAPconnect poll()
method:
def loop(self):
while True:
self.poll()
time.sleep(0.001)
As mentioned in the snapconnect:index section, the poll()
method calls the necessary
components that SNAPconnect uses. Since your program is essentially always running that loop, your application must
be programmed in an asynchronous fashion.
To aid in writing asynchronous programs, access to the asynchronous event scheduler that SNAPconnect uses is available. The event scheduler that SNAPconnect uses is part of a Python package called “apy”.
The apy Package¶
The name “apy” is short for “asynchronous Python” and is an open-source pure Python package that comes bundled with SNAPconnect. It is also available from http://sourceforge.net/projects/apy/.
The apy event scheduler is straightforward to use and allows your program to schedule functions to be called. Access to the event scheduler is available by calling methods on the “scheduler” property of your SNAPconnect instance. To schedule a function to be called later you would call the schedule method. For example, to schedule sending a multi-cast RPC call every one and a half seconds you could enter:
def my_func(self):
self.snap.mcast_rpc(1, 1, "hello")
return True
def start_mcasting(self):
self.snap.scheduler.schedule(1.5, self.my_func)
The signature for the schedule function looks like:
- schedule(self, delay, callable, *args, **kwargs)¶
- Parameters
delay – The delay, in seconds, to use before calling the callable argument. A delay of 0 indicates the callable should be called on the next poll interval
callable – The callable object to schedule
*args – Optional arguments to pass to the callable object
**kwargs – Optional keyword argument to pass to the callable object
The schedule function returns an EventElement object that has a method called “Stop” that enables you to cancel the scheduled event (in other words, cancel having the callable be called).
For example, to schedule a timeout that can be disabled by calling a specific function you could do:
def start_timeout(self):
self.my_timeout = self.snap.scheduler.schedule(10.0, self.on_timeout)
def disable_timeout(self):
self.my_timeout.Stop()
The last thing to know about using the apy event scheduler is how to re-schedule a running function after it runs.
When your function returns after being called by the event scheduler, its return value is checked. If the return
value is True
, then the event is automatically rescheduled using the original delay value. If you would like to
reschedule with a different delay, your function can return a number (integer or float, indicating a delay duration
in seconds) as a different valid delay value. If you don’t want your function to be rescheduled at all, your function
can return None
or False
.
def reschedule_every_second():
print 'Tick'
return 1
comm.scheduler.schedule(0, reschedule_every_second)
def reschedule_for_original_delay():
print 'Tick'
return True #This will be rescheduled every 5 seconds in this example
comm.scheduler.schedule(5, reschedule_for_original_delay)
def do_not_reschedule():
print 'Tick'
return False
comm.scheduler.schedule(5, do_not_reschedule)
Be careful when scheduling SNAPconnect functions that have return values. For example, the command:
comm.scheduler.schedule(0, comm.rpc, addr, "foo") # <-- DON'T DO THIS!
looks like it would schedule a single RPC call. However, the rpc()
function actually
returns an integer for identifying the packet. This integer will cause the rpc()
function
to be continually rescheduled. Instead, you can use a wrapper that will make the call and return None
to avoid rescheduling.
def rpc_with_no_return(*args):
comm.rpc(*args)
return None
comm.scheduler.schedule(0, rpc_with_no_return, addr, "foo")
Multiple Instances¶
When attempting to run multiple instances of SNAPconnect it is important that each instance uses a unique SNAP address. Just as regular SNAP nodes need a unique address to differentiate each other over the air, SNAPconnect instances need unique addresses on the network as well.