Anders-Holst / xled_plus

Addons to the XLED package, to create nice effects for Twinkly LED lights
MIT License
39 stars 8 forks source link

creating multiple controller objects in sequence create a socket "Address in use error" #28

Open hadesto opened 1 month ago

hadesto commented 1 month ago

Hi!!

thank you so much for putting this out there. This library is pretty awesome.

While playing around with it I noticed that if you create more than one controller at a time for some reason the socket connection is not built separately. It seems like a valid socket connection is given to only one of the controllers, I would have expected each object to be an independent separate socket.

Here's a specific example :


device_ip_list = ["192.32.91.66", "192.32.91.67"]
for controller_ip in device_ip_list:
    ctrl_list.append(HighControlInterface(controller_ip))

The code will run, but the socket builds will fail. Specifically if ctrl_obj is each of the elements in ctrl_list then :

ctrl_obj._udpclient.handle is resulting in OSError: [Errno 48] Address already in use at least for one of the items (i've tried a total of 2 so far).

I also tried using the MultiHighControlInterface class but it seems to be directed at setups joined at the twinkly app level and I'm using each set differently.

Thanks again for this awesome library. Appreciate any help!

hadesto commented 1 month ago

in case it helps :

xled==0.7.0
xled_plus==0.1.35

python 3.10
Anders-Holst commented 1 month ago

Hi,

I remember this issue from a couple of years ago. I see that the latest xled package released (0.7.0) is from 2021, and I think this may have been fixed in 2022. One thing to try is to download the latest xled version from git instead of using "pip install", then you will get the latest version. (Alternatively you can download just the "udpclient.py" file from the xled package and let it replace the old file.) The reason is that the old udpclient.py hogged a receiving port on the local machine, even when, as in this case, no messages are received. Therefore several UDP clients would try to use the same port.

If the above does not help, I may be able to provide a workaround, but try the above first.

Best Regards Anders Holst

hadesto commented 1 month ago

appreciate the speedy response!! thank you so much!!

I went code diving and realized that this is an issue indeed in the original xled package. The library is written such that the source port is forced to be the same as the destination port ( 7777 ). For now I've had to overload the way the udp client handle is created. It is ugly but it works.

Here's what found so far:

for now the way I've gotten away with it is with the following:

for count, (controller_ip, src_port) in enumerate(zip(settings.default.twinkly_controller_ip_list,
                                   settings.default.twinkly_controller_src_ports)):
    ctrl_list.append(HighControlInterface(host=controller_ip))

    ctrl_list[count].udpclient
    if count == 0:
        while ctrl_list[count]._udpclient._handle == None:
            ctrl_list[count].udpclient.handle
            logger.debug("Waiting for default Xled connection to build so we can close it")
        ctrl_list[count]._udpclient._handle.close()
        while ctrl_list[count]._udpclient._handle._closed == False:
            logger.debug("Waiting for default Xled connection to close so we can assign new ones")
    else:
        while ctrl_list[count]._udpclient == None:
            ctrl_list[count].udpclient
            logger.debug("Waiting for default Xled connection to build so we can close it")

    ctrl_list[count]._udpclient._handle = None
    ctrl_list[count]._udpclient._handle = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
    ctrl_list[count]._udpclient._handle.bind(("", src_port))
    # wait this time in seconds before starting to send data
    logger.info(f"Connection configured. Waiting {settings.default.connection_settling_time} seconds for it to settle")
    time.sleep(settings.default.connection_settling_time)

happy to take other suggestions, but it would be great to have this be configurable. On the plus side it would also allow running different instances of the xled-plus derived code to better multiprocess if we can set the port up front.

I'm guessing this is looking more like an xled issue than an xled-plus issue? should I be asking there?

Thanks again for this awesome library and all the work that has gone into it !

hadesto commented 1 month ago

as a side note i did notice that there are changes in the developer branch per your suggestion. will take a look at it too.