[Rock-dev] Proposal: NDLCom and Rock
Martin Zenzes
martin.zenzes at dfki.de
Mon Dec 9 12:51:02 CET 2013
On 12/04/2013 08:02 PM, Sylvain Joyeux wrote:
> On 12/04/2013 02:10 PM, Martin Zenzes wrote:
>> - Open more than one "file descriptor" to read and write encoded
>> NDLCom Messages as serial data
>> - `/dev/ttyUSB0` - profane tty
>> - `$IP:port` -- TCP/UDP connections
>> - `/dev/can0` -- No CAN in Sherpa, but others use this already
>> - `/dev/ndlcom0` -- Custom kernel device from !GumBridge FPGA,
>> providing parsed and decoded messages
> Keep it simple, and have the class handle only one single I/O (i.e. be
> one driver). You can always have a wrapper class on top that does the
> I/O-to-deviceId mapping if it is really really needed.
This is what I did for a little proof-of-concept -- subclass
iodrivers_base::Driver as "ndlcom::Driver", implement extractPacket()
there, and subclass this in "ndlcom::Interface" to wrap the
encode/decode steps of NDLCom. There, a "sendPacket()" and
"readPacket()" is provided to the routing layer in
"ndlcom::Communication", which only handles valid, complete
ndlcom::Message objects.
> In the same
> effect, on the oroGen side, there should be IMO only one task per bus
> (i.e. I/O) as well.
What is an I/O (or Bus)? You mean to do the "routing" between of
multiple Busses on the Orogen-level? This would move the routing code
away from the cpp-library and make this whole "ndlcom routing layer"
heavily rock-dependent. Not what everyone is looking for...
But I see one point: putting everything in one big Task would lead to
one process for all the (potentially blocking or slow) read/write
systemcalls... Increasing latency...
Motors are sending/receiving data with ~100Hz
--
Now, some califications on for my initial wording:
>> - Provide received Messages which are directed to the own receiverId
>> as `ndlcom::Message` at the `readMessage()` function to a caller
The "ndlcom::Communication" is supposed to make ndlcom-Messages sent by
an STM32 (for example) to the deviceId of the "ndlcom::Communication"
available in the "readMessage()" or "readPayload()" function, to be
called by the Orogen-level update-hook.
>> - Accept `ndlcom::Message` at the `writeMessage()` function to be sent
>> to the correct hardware interface
Some rock-task (or other calling code, using the cpp-library) calls
"writePayload()" or "writeMessage()" in the Orogen-level and it is
expected that the resulting ndlcom-Message is then sent at the correct
Interface (or Bus) to be received by an STM32
>> - Forward messages not directed at the own deviceId to the correct
>> hardware interface
Some STM32 should also be able to sent a Message to another STM32,
regardless of the structure of the used communication infrastructure.
The "ndlcom::Communication" is able to reach every ndlcom-device, and
should forward messages _not_ directed at the "ndlcom::Communication".
The decision which Interface is used to reach a asked-for device is
based on the origin observed messages in the past. If a device has not
sent messages to "ndlcom::Communication" before, all interfaces have to
be used. The same goes for broadcasts.
---
>> - Handle broadcasts: resend the message on all other hardware interfaces
>> - Mirror all received and transmitted `ndlcom::Message` as encoded raw
>> NDLCom datastream via UDP. This would allow working with an unchanged
>> iStruct Gui. This is transitory, for legacy support of the iStruct
>> stack. Also easy access of all NDLCom-data without additional rock-code
> IMO, you should just make a separate class that "listens" to everything
> that goes through the iodrivers_base::Driver (you can add listeners...)
> and mirror it to UDP instead of adding that as a core feature of the new
> class.
Adding the listeners and forward all ndlcom-raw-data 1:1 to the UDP
mirror would double forwarded Messages: One STM32 sends a message which
is received on /dev/ttyUSB0 and mirrored to the UDP-port. Then the
routing-layer sees that the receiver is on /dev/ttyUSB2, and sends the
packet that way. It is mirrored again... the UDP-Gui sees the message
twice...
Using the iodrivers_base::IOListeners to only mirror messages which
where read at a hardware-interface, and mirroring the messages sent by
the "writeMessage()" or "writePayload()" functions of
"ndlcom::Communications" would do the Job?
>
>> So, after *lotoftext*: This cpp-library is finally to be embedded in a
>> Rock-Task, providing the appropriate Rock-Ports and executing the
>> needed Hooks. Everything else, mapping certain NDLCom-Payload to
>> certain Rock-Types or providing services based on NDLCom Messages, is
>> done by other Rock-Tasks.
> I would definitely not go for mirroring the MARS design. The Mars design
> communicates "behind the scene" between a core component and the
> "device" components, constraining the device components to be in the
> same process than the core, and in general giving headaches.
Yes, I see the "same process" problem. But what do you mean with
"general headaches"? What do you mean with "behind the scene"?
>
> Hopefully for us, there are indeed precedents for this kind of hardware:
> the CAN bus integration as well as the Schilling DTS (the last one is
> not in Rock proper).
But in these, there is only one hardware-Interface (Bus)? NDLCom should
have multiple of them.
>
> The general design there is to split the handling of the protocol between:
> - a bus handling component, which represents a given bus and only does
> the multiplexing/demultiplexing. It would create one port per deviceId
> and only output, on this port, the raw data that is relevant for this
> particular device. If your payload is only raw bytes, you could even use
> iodrivers_base::RawPacket.
Hm Ok. I can see your point, doing it like this. But I would argue that:
- Much overhead for single messages? We are expecting around 2000
Messages per second in both directions, to be handled on a 700MHz ARM
while still doing the motion control and kinematics...
- Also, as said before, this would move all the "routing" into rock...
Hence it will not be easily possible to reuse this ndlcom-routing-code
outside of rock (standalone gui, bare-metal controller).
+ This would give multiple threads for handling the read/write
systemcalls...
> - one component per device, which takes the raw bytes and parse it into
> actual data (and, if needed, is able to properly reply/configure/... the
> device)
One component (this is a rock task?) per device? For 25+ Motors, 10+
Sensorboards? I tought about using one rock-task per data-type, mapping
"base/type/JointState" to "BLDCJointTelemetry" for example, with a fixed
(configured) map from "rock-device-name" to "ndlcomId".
Greetings
--
M.Sc. Martin Zenzes
Space Robotics
Hauptgeschäftsstelle Standort Bremen:
DFKI GmbH
Robotics Innovation Center
Robert-Hooke-Straße 5
28359 Bremen, Germany
Phone: +49 (0) 421 178 45 - 6658
Fax: +49 (0) 421 178 45 - 4150
E-Mail: martin.zenzes at dfki.de
Weitere Informationen: http://www.dfki.de/robotik
-----------------------------------------------------------------------
Deutsches Forschungszentrum fuer Kuenstliche Intelligenz GmbH
Firmensitz: Trippstadter Straße 122, D-67663 Kaiserslautern
Geschaeftsfuehrung: Prof. Dr. Dr. h.c. mult. Wolfgang Wahlster
(Vorsitzender) Dr. Walter Olthoff
Vorsitzender des Aufsichtsrats: Prof. Dr. h.c. Hans A. Aukes
Amtsgericht Kaiserslautern, HRB 2313
Sitz der Gesellschaft: Kaiserslautern (HRB 2313)
USt-Id.Nr.: DE 148646973
Steuernummer: 19/673/0060/3
-----------------------------------------------------------------------
More information about the Rock-dev
mailing list