aseba
aseba copied to clipboard
Advertise targets using Zeroconf
The IANA has assigned a service name for Aseba and Aseba nodes can now advertise tcpin:
targets using Zeroconf with the service type _aseba._tcp
.
Clients could use mDNS-SD to browse for nodes as described in RFC 6763 § 1 ¶ 3. Using mDNS-SD, a wireless Thymio with node id 5036 would be discoverable as Thymio Wireless 5036._aseba._tcp.local.
The SRV record found by the mDNS query indicates the host name and port where the node can be found. For example, the robot above might be found at laptop-01.example.edu
port 49152, assuming the robot was connected by USB to laptop-01
. The TXT record for this host uses the Data Syntax for DNS-SD TXT Records and might specify:
- txtvers = 1
- type = Thymio Wireless
- protovers = 5
- ids = 5036
- pids = 8
indicating a Thymio-II (product id 8 according to productids.h) with node id 5036. A client can receive more that one record for its query and can use the fields in the TXT record to make its choice.
A switch groups together a set of nodes into an Aseba bus and provides a port through which clients can connect to all of them. The switch will advertise itself with a human-readable instance name, and list in its TXT record the ids and pids of the nodes to which it provides access. For example, the TXT record of Classroom 5b Ms\. Coulson._aseba._tcp.local.
might specify:
- txtvers = 1
- type = Aseba Switch
- protovers = 5
- ids = 5036 123 7756 1 3141
- pids = 8 8 8 0 3
indicating three Thymio-IIs, one dummy node and one Marxbot.
If a target is currently being used by an incoming connection, the boolean flag busy
is set in the TXT records. Targets must update it how their status change (incoming connection or connection closed).
Current status
The status of current support is, for infrastructure:
- [x] basic support in Zeroconf
- [x] advertising when available and forgetting when used.
- [ ] advertising when available, forgetting when not running, and
busy
TXT
flag when used.
For program support:
- [x] support in Dummy node
- [x] support in Studio
- [x] support in Playground
- [x] support in Http
- [x] support in old Switch
- [ ] support in new Switch
I agree. Your description raises an interesting question about the protocol version, in the sense that currently the switch does not do any checking of the protocol version, but in your description it advertises protovers, so I guess we should add that? I guess that means that the switch will refuse connecting Dashel targets containing one or more node with wrong version numbers?
The TXT record is a way to filter targets. A target that advertises itself with an outdated protocol version must be ignored by any client unable to use that version. So yes, I think the standard switch should refuse to connect any node with the wrong protocol.
The target chooser in the GUI should also use the TXT record as a filter and should not display any targets with outdated protocol versions.
A client with backwards protocol compatibility may connect if it wishes. For example, a hypothetical switch that could do protocol emulation, would report the protocol version it is willing to offer to client connections, and would use whatever protocols it can handle to talk to its targets. I am not sure that there is a use case for this.
I agree!
I think that I have figured out how Aseba::Zeroconf should interact with the DNS Service. I have combined the scenarios in the attached sequence diagram.
- Two Dashel targets: T1 USB with 1 node, T2 Thymio Wifi Hub with 2 nodes.
- Two client programs, including an Aseba Switch connected to T1.
In the Register scenario, the client learns about Thymio 101 on T1 and registers it with the DNS Service. When the registration is complete, Aseba::Zeroconf
calls the registerComplete
hook, that the client may have overridden in a subclass (or not). An mDNS query finds the registered service. The client then learns the product id of Thymio 101 and updates the mDNS TXT record. There is no hook because an update succeeds or fails immediately
In the meantime, Thymio Wifi Hub 797 starts its own mDNS service. An mDNS query finds both services and the three Aseba nodes they offer.
In the Browse scenario, the client asks Aseba::Zeroconf
for a list of services. This may take some time and should not be blocking. When the DNS service indicates that it has sent all entries, Aseba::Zeroconf
calls the browseComplete
hook, that the client may have overridden in a subclass. A subsequent call from the client retrieves the list of services. In the Resolve scenario, a service chosen by browsing is resolved into a Dashel target with a set of properties. (This is a blocking call to the simplified DNSServiceResolve
function that is appropriate when there is only one SRV and TXT record.)
In the Aseba scenario, the resolved targets are used as usual. Further use of mDNS is not necessary, unless a disconnection occurs.
Thank you for this clarification. In the Browsing scenario, we should integrate the file descriptor of the socket to the zeroconf service either in the Aseba or the Qt loop. As currently I only see the Browsing scenario to be used in QUI application, we can reuse the Qt-zeroconf example at https://doc.qt.io/archives/qq/qq23-bonjour.html that already provides this integration.
I think that we should, as much as possible, avoid creating new threads when not necessary.
The socket file descriptors used by the DNS Service do not transfer data, they are only used to trigger callbacks. Every outstanding request to the service has a unique sdRef, associated with a client-supplied callback and context token. Every sdRef is given a socket file descriptor. The client runs a select
loop on the file descriptors for its outstanding requests. When the service has data for an sdRef, it writes to the file descriptor; the client detects this and asks the service to process the sdRef; the service then calls the callback with the data.
- Option 1 is to ask Dashel to include each of these (ephemeral) file descriptors in its
select
loop. TheincomingData
handler on theHub
would trigger the callback. Unfortunately, onlySocketStream
(tcp:) targets accept an existing socket, but we would wantSocketServerStream
(tcpin:). So a change to Dashel would be necessary. Also, creating an entire Dashel stream just to listen to a socket might be expensive. - Option 2 is just to use Dashel as a round-robin scheduler. Every time through the
run
or (run1s
) loop, if there are outstanding service requests, make a separateselect
call with a short timeout. This will reduce throughput a little, since Dashel cannot concurrently listen for the DNS service and stream data.
In both cases, adding Zeroconf behavior to a client is obtained by subclassing Dashel::Hub
. So Aseba::Zeroconf
shouldn't be a separate object, but a derived class somewhere in the inheritance of the Hub used by a client.
I know the QT code, which is fine as far as it goes, but its only advantage is to leverage QSocketNotifier::Read
and SLOTs. Adding QT as a dependency for Zeroconf seems like overkill to me.
Also, I think that one of the use cases for a switch or a headless wifi hub is to bridge wired and wifi networks, and I think we would want them to be able to discover targets in one or the other networks using Zeroconf.
When you talk about a client, you mean an Aseba application that wants to browse/search services, not advertise them, right? As far as I understood, for advertising, we can block until we can an answer at this should not be too disturbing, right?
For the Qt code, the nice thing is its integration with the Qt main loop. Qt-based Aseba applications such as Studio or VPL do run the Dashel::Hub in a separate thread for blocking select/poll.
I do not suggest to add a Qt dependency for the command-line Aseba zeroconf application, but rather have two versions, and use the Qt one for GUI applications.
So I see three usages:
- Command-line-based or Qt-based zeroconf service advertisement => no need to integrate into Dashel::Hub because we can block when waiting for a request from the zeroconf deamon.
- Command-line based zeroconf service browsing/searching => need to integration into Dashel::Hub or do multi-threading
- Qt-based zeroconf service browsing, we can easily use the Qt code.
If so, only 2 is a problem; the question is, does this use case happens concretely? For now I do not see any, excepted for a Switch which wants to browse zeroconf networks, but I do not see that as an important requirement for our users.
Shall we defined that the entries in the TXT flags are UTF-8 encoded?
Commit a38b483e4a46d38875493ef944025ba6cc90dcc8 adds basic support for Zeroconf to Http, without de-advertising when someone connects to it, as Http does not properly defines the notion of being connected to an IDE. This should be properly defined as part of the new Switch.
I am moving the reminding of this issue to 1.7.