gotthardp / python-mercuryapi

Python wrapper for the ThingMagic Mercury API
MIT License
122 stars 63 forks source link

Python freezes when reader unavailable #134

Open jpkelly opened 1 year ago

jpkelly commented 1 year ago

I am running a script that polls the reader.

reader = mercury.Reader("llrp://10.39.54.202")
reader.set_read_plan([antenna], "GEN2", read_power=POWER[antenna - 1])
tag = reader.read(timeout=250)

When the reader becomes unavailable, running the reader.read function freezes the script and only a kill -9 will stop it. I'd like to be able to exit cleanly when the reader becomes unavailable.

Any help appreciated!

jpkelly commented 1 year ago

Here is the full script.

#! /usr/bin/python3

import mercury
import argparse
import time
from pythonosc import dispatcher
from pythonosc import osc_server
from pythonosc import osc_message_builder
from pythonosc import udp_client
# from signal import signal, SIGINT
from sys import exit
import systemd_stopper
import signal

SEND_IP = "x.x.x.x"
SEND_PORT = 7000
OFFSET = 0 # Antenna number offset to get position with multiple readers
TAGVALS = ["0", "0", "0", "0",]
READCB = [0, 0, 0, 0,]
READCBOLD = [0, 0, 0, 0,]
POWER = [850, 850, 850, 850]
ENABLED = [1, 1, 1, 1]
RSSITHRESH = -50

reader = mercury.Reader("llrp://x.x.x.x")
#reader.set_read_plan([1,2,3,4], "GEN2", read_power=1000)
# set_read_powers = reader.set_read_powers([(1, 1100), (2, 1100), (3, 1100), (4, 1100)])

print("Connected Ports: " + str(reader.get_connected_ports()))
print("Supported Regions: " + str(reader.get_supported_regions()))
print("Power Range: " + str(reader.get_power_range()))
print("Antennae: " + str(reader.get_antennas()))
print("Model: " + reader.get_model())
print("Read Powers: " + "1-" + str(POWER[0]) + " 2-" + str(POWER[1]) + " 3-" + str(POWER[2]) + " 4-" + str(POWER[3]))

# OSC CLIENT
parser_client = argparse.ArgumentParser()
parser_client.add_argument("--ip", default=SEND_IP, help="The ip of the OSC server")
parser_client.add_argument("--port", type=int, default=SEND_PORT, help="The port the OSC server is listening on")
args_client = parser_client.parse_args()
client = udp_client.SimpleUDPClient(args_client.ip, args_client.port)

def handler(signal_received, frame):
    print('SIGINT or CTRL-C detected. Exiting gracefully')
    # reader.stop_reading()
    exit(0)

def do_read(antenna):
    signal.alarm(5)
    try:
        reader.set_read_plan([antenna], "GEN2", read_power=POWER[antenna - 1])
        tag = reader.read(timeout=250)
    except Exception as ex:
        print('read error')
    # print(tag, len(tag))
    length = len(tag)

    if length >= 1:
        for i in range(length):
            tagstr = tag[i].epc.decode('ascii')
            # print(i, tagstr, tag[i].antenna, tag[i].rssi)
            print("POS" + str(tag[i].antenna + OFFSET), tagstr, "Read Count: " + str(tag[i].read_count), "Strength: " + str(tag[i].rssi))
            if tag[i].rssi >= RSSITHRESH:
                client.send_message("/healthcare/pos"+str(antenna + OFFSET), tagstr)
    else:
        print("POS" + str(antenna + OFFSET) + " 0")
        client.send_message("/healthcare/pos"+str(antenna + OFFSET), "0")

def send_states():
    for i, val in enumerate(TAGVALS):
        print(i, val)

def read_all_ports():
    for i in range(1, 5):
        if ENABLED[i - 1] == 1:
            do_read(i)
    print()
    for i, val in enumerate(TAGVALS):
        if READCB[i] == 1:
            print(i, val)
            TAGVALS[i] = "0"
        READCB[i] == 0
    return 0

def timeout_handler(num, stack):
    print("Received SIGALRM")
    raise Exception("FUBAR")

def long_function():
    print("LEEEEROYYY JENKINSSSSS!!!")
    time.sleep(60)

signal.signal(signal.SIGALRM, timeout_handler)
# signal.alarm(5)

while __name__ == '__main__':
    stopper = systemd_stopper.install()
    # signal(SIGINT, handler)
    while stopper.run:
        read_all_ports()
    print('Stopped by systemd_stopper. Exiting gracefully')
densjac commented 1 year ago

I think we are experiencing the same situation. At least in 3 of our readers. We are using a custom reader built from a certain provider, maybe they are becoming faulty after a long use and that generate the timeout with the module.

The error is simply a timeout when calling the read function and then everything stops working.

We don't have a solution. What we are doing is to listen the timeout exception and just end the service with an error. The service then restart automatically and reconnect to the reader and we restore the operation. That is just easier than managing the special case and build a workaround. Systemd has the feature to restart in case of error Restart=on-failure.

We are still investigating the issue but everything points to a suddenly timeout in the read function.