Black Lives Matter - Action and Equality. ... Adafruit joins the Stop Hate for Profit campaign.
0

Handling Error of adafruit.io loop in python script
Moderators: adafruit_support_bill, adafruit

Forum rules
If you're posting code, please make sure your code does not include your Adafruit IO Active Key or WiFi network credentials.
Please be positive and constructive with your questions and comments.

Handling Error of adafruit.io loop in python script

by michelblk on Tue Feb 16, 2016 1:59 pm

I was trying to handle errors that may occur because of disconnects or unreachability while an adafruit.io loop (background), but my script still keeps stopping the adafruit.io loop. Even the default disconnected() function doesn't get called and client.is_conntected() is still true.

I tried to catch the error with this code:
Code: Select all | TOGGLE FULL SIZE
try:
    client.loop_background()
except:
    print "error"


This is the error message, when I'm blocking my internet connection:
Code: Select all | TOGGLE FULL SIZE
[Errno 104] Connection reset by peer
Exception in thread Thread-1:
Traceback (most recent call last):
  File "/usr/lib/python2.7/threading.py", line 552, in __bootstrap_inner
    self.run()
  File "/usr/lib/python2.7/threading.py", line 505, in run
    self.__target(*self.__args, **self.__kwargs)
  File "/usr/local/lib/python2.7/dist-packages/paho/mqtt/client.py", line 2287, in _thread_main
    self.loop_forever()
  File "/usr/local/lib/python2.7/dist-packages/paho/mqtt/client.py", line 1261, in loop_forever
    rc = self.loop(timeout, max_packets)
  File "/usr/local/lib/python2.7/dist-packages/paho/mqtt/client.py", line 811, in loop
    rc = self.loop_read(max_packets)
  File "/usr/local/lib/python2.7/dist-packages/paho/mqtt/client.py", line 1075, in loop_read
    return self._loop_rc_handle(rc)
  File "/usr/local/lib/python2.7/dist-packages/paho/mqtt/client.py", line 1382, in _loop_rc_handle
    self.on_disconnect(self, self._userdata, rc)
  File "/usr/local/lib/python2.7/dist-packages/Adafruit_IO/mqtt_client.py", line 81, in _mqtt_disconnect
    raise RuntimeError('Unexpected disconnect with rc: {0}'.format(rc))
RuntimeError: Unexpected disconnect with rc: 1

But how can I get the function "RuntimeError" to try again without changing mqtt_client.py

michelblk
 
Posts: 2
Joined: Thu Feb 11, 2016 12:46 pm

Re: Handling Error of adafruit.io loop in python script

by tdicola on Wed Feb 17, 2016 4:49 pm

Ah that runtime error is telling you the server disconnected your script. This might happen if there's a transient issue with the service, or sometimes even as a part of the mqtt protocol (it has keepalives and timeouts built in so clients sometimes need to take action to keep from being disconnected). I think your best bet is to catch the runtime error and reconnect to the service (by calling connect again), for example make your loop look like:
Code: Select all | TOGGLE FULL SIZE
try:
    client.loop_background()
    # Your main loop goes here.
except RuntimeError:
    client.connect()

In a later version of the library I'll look at changing it to throw an explicit error type, like IODisconnected or something similar, so that way you could catch it instead of just the generic RuntimeError. For now though catching the runtime error should work since that's the only error the library throws. The big thing is to call connect again after catching the error so it will reconnect and get back to your program logic.

tdicola
 
Posts: 1074
Joined: Thu Oct 17, 2013 9:11 pm

Re: Handling Error of adafruit.io loop in python script

by michelblk on Thu Feb 18, 2016 11:55 am

Thanks so much for your respond
I just tried the following script and it still doesn't work. It's still spitting out the error and doesn't reconnect, but my main loop just continues to run.
Code: Select all | TOGGLE FULL SIZE
while True:
    try:
        client.loop_background()
        while True:
                # my main loop
                print time.time()
                time.sleep(1)
    except RuntimeError:
        print '---This is an error---'
        client.connect()

So the script doesn't catch the RuntimeError.
I may be wrong, but I believe the error is only catchable in the library itself, because it doesn't trigger the except pharse.

michelblk
 
Posts: 2
Joined: Thu Feb 11, 2016 12:46 pm

Re: Handling Error of adafruit.io loop in python script

by krambriw on Sat Feb 20, 2016 5:31 am

I have the same problem as you experience. Exactly the same RuntimeError can happen suddenly. I also agree that this has to be catched by the origin module that is raising it

Besides, the connection attempt fails many times and you need to retry again and again (especially yesterday was a mess, it has worked better this night it seems)

Also, I have some other design thoughts:
- after disconnection, what is the recommended action to take? In Python I am currently also deleting the client instance before creating a new one for the next trial
del client


- is it recommended to use delays in between the various commands?

Like
# Create an MQTT client instance.
client = MQTTClient(adafruitUser, adafruitIOkey)
# Setup the callback functions defined above.
client.on_connect = connected
client.on_disconnect = disconnected
client.on_message = message
time.sleep(1.0)

# Connect to the Adafruit IO server and start subscribing.
client.connect()
print self.text.plugin_init
time.sleep(1.0)
client.loop_background()
print self.text.comms_up
self.started = True


krambriw
 
Posts: 22
Joined: Fri Feb 19, 2016 6:07 pm

Re: Handling Error of adafruit.io loop in python script

by mikeadamz on Mon Mar 14, 2016 4:20 pm

Because the RuntimeError is being raised by the background thread in loop_background() this except won't catch it.

Code: Select all | TOGGLE FULL SIZE
Exception in thread Thread-1:
Traceback (most recent call last):
  File "/usr/lib/python2.7/threading.py", line 810, in __bootstrap_inner
    self.run()
  File "/usr/lib/python2.7/threading.py", line 763, in run
    self.__target(*self.__args, **self.__kwargs)
  File "/usr/local/lib/python2.7/dist-packages/paho/mqtt/client.py", line 2287, in _thread_main
    self.loop_forever()
  File "/usr/local/lib/python2.7/dist-packages/paho/mqtt/client.py", line 1261, in loop_forever
    rc = self.loop(timeout, max_packets)
  File "/usr/local/lib/python2.7/dist-packages/paho/mqtt/client.py", line 811, in loop
    rc = self.loop_read(max_packets)
  File "/usr/local/lib/python2.7/dist-packages/paho/mqtt/client.py", line 1075, in loop_read
    return self._loop_rc_handle(rc)
  File "/usr/local/lib/python2.7/dist-packages/paho/mqtt/client.py", line 1382, in _loop_rc_handle
    self.on_disconnect(self, self._userdata, rc)
  File "/usr/local/lib/python2.7/dist-packages/Adafruit_IO/mqtt_client.py", line 80, in _mqtt_disconnect
    raise RuntimeError('Unexpected disconnect with rc: {0}'.format(rc))
RuntimeError: Unexpected disconnect with rc: 1



tdicola wrote:Ah that runtime error is telling you the server disconnected your script. This might happen if there's a transient issue with the service, or sometimes even as a part of the mqtt protocol (it has keepalives and timeouts built in so clients sometimes need to take action to keep from being disconnected). I think your best bet is to catch the runtime error and reconnect to the service (by calling connect again), for example make your loop look like:
Code: Select all | TOGGLE FULL SIZE
try:
    client.loop_background()
    # Your main loop goes here.
except RuntimeError:
    client.connect()

In a later version of the library I'll look at changing it to throw an explicit error type, like IODisconnected or something similar, so that way you could catch it instead of just the generic RuntimeError. For now though catching the runtime error should work since that's the only error the library throws. The big thing is to call connect again after catching the error so it will reconnect and get back to your program logic.

mikeadamz
 
Posts: 17
Joined: Thu Jan 21, 2016 1:20 am

Re: Handling Error of adafruit.io loop in python script

by mikeadamz on Thu Mar 17, 2016 1:51 pm

This is how I worked around this problem, the MQTT Client class has a is_connected method.. I removed the client.connect statement from outside of the loop, and simply added a test expression that connects if client.is_connected is not true:

Code: Select all | TOGGLE FULL SIZE
# Create an MQTT client instance.
client = MQTTClient(ADAFRUIT_IO_USERNAME, ADAFRUIT_IO_KEY)

# Setup the callback functions defined above.
client.on_connect    = connected
client.on_disconnect = disconnected
client.on_message    = message

# Now the program needs to use a client loop function to ensure messages are
# sent and received.  There are a few options for driving the message loop,
# depending on what your program needs to do. 

# The first option is to run a thread in the background so you can continue
# doing things in your program.
client.loop_background()
# Now send new values every 10 seconds.
log.info('Publishing a new message every 60 seconds (press Ctrl-C to quit)...')

while True:
    if client.is_connected():
        value = sensor.get_temperature(W1ThermSensor.DEGREES_F)
        log.info("Publishing value '" + str(value) + "' to " + TEMP_FEED)
        client.publish(TEMP_FEED, value)
        time.sleep(60)
    else:
        log.warning("Client not connected, connecting..")
        client.connect()
        time.sleep(30)

The first time the loop runs, it will connect. If there's a disconnect for whatever reason the next loop iteration will simply reconnect. For some reason client.connect does not happen instantly, so I sleep for 30 seconds before the loop runs again. Before I added the sleep client.is_connected() would evaluate to false for several loop iterations before it actually connected.

mikeadamz
 
Posts: 17
Joined: Thu Jan 21, 2016 1:20 am

Please be positive and constructive with your questions and comments.