[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