256dpi / arduino-mqtt

MQTT library for Arduino
MIT License
1.02k stars 234 forks source link

Add Topic Router #132

Open burner- opened 5 years ago

burner- commented 5 years ago

Currently there is only onMessage(messageReceived) callback. It would be nice that user can make routed subscribes that way: client.subscribe("/hello", myHelloHandler); and that mqtt client lib will automatically route all /hello messages to that myHelloHandler. To save memory it would be even better if library supports wildcards so user can do: client.subscribe("/hello/%", myHelloHandler); or client.subscribe("/hello/%/byebye", myHelloHandler); and that will cause that it will get routed to myHelloHandler(int handlerid, String message)

Urs-Eppenberger commented 5 years ago

I support this idea. It would make for cleaner, better readably and maintainable code, at least in my case.

I have a code for devices which subscribe to two topics, one for commands and one for measurement values from other sensors. In the callback routine, I currently need to check first the topic and then decide what action to take based on the topic. With the above feature, I can split the code in two callback routines, with clearly dedicated tasks.

I'm not very fond of the wildcard idea, though. The library is used primarily in small devices, where the need for wildcarding is rare. But I can see the one or the other use case for this too.

256dpi commented 5 years ago

Thank you all for your comments. I thought about this feature myself a couple of times. However, I think that it unnecessarily complicates the code with little benefit for most users. All my Arduino+MQTT projects involved less than 15 topics, which was easy to handle with a if {} else if {} else {} statements. The most memory efficient and performant way is to let the user choose the best way to handle the topic checks.

The only thing I see as a possibility would be to implement an additional router class that could be used to handle such cases. To do that properly we first nee to implement a Trie in lwmqtt that is similar to what I implemented in gomqtt: https://godoc.org/github.com/256dpi/gomqtt/topic#Tree. This structure can then be used to implement the router efficiently. But again, what are the real use cases?

abcd-ca commented 5 years ago

@256dpi what is your strategy when trying to parse which topic it is within your if/elseif/else when the topic has a wildcard like foo/+/bar? You can't just use String.equals() because the topic foo/123/bar obviously won't match foo/+/bar. Do you use a regex lib?

256dpi commented 5 years ago

You can use s.startsWith("foo/") to match the beginning, then use s.indexOf("/", 4) to get the offset of the next "/", then get the segment in the middle using s.substring(4, offset) and use the same approach to get the end of the string using s.substring(offset+1).

abcd-ca commented 5 years ago

@256dpi good call, thanks. Just to add to the router idea for anyone picking up this thread, the one onMessage handler can get pretty big – I have been doing the minimum if/elseif/else and then calling other functions in my file to handle each (kind of like a router but more basic) which keeps the code more manageable to read and maintain. Router could be a cool optional plugin-in project to this one for very complex needs.