roboticslab-uc3m / yarp-devices

A place for YARP devices
https://robots.uc3m.es/yarp-devices/
9 stars 7 forks source link

Reroute CAN write calls to controlboard device #209

Closed PeterBowman closed 4 years ago

PeterBowman commented 5 years ago

In the CanBusControlboard+TechnosoftIpos architecture, write operations are performed in a joint-by-joint manner - that is, whenever we want to IPositionControl::positionMove, the control board forwards demanded positions to one IPositionControlRaw::positionMoveRaw call per joint. Each TechnosoftIpos is therefore responsible for constructing and sending its own message(s) through the CAN bus.

Ideally, we would take advantage of TX/RX yarp::dev::ICanBus buffers (see https://github.com/roboticslab-uc3m/yarp-devices/issues/159, proposed implementation plan at https://github.com/roboticslab-uc3m/yarp-devices/issues/208) and send messages in batches. Reception is handled by the read thread in CanBusControlboard. In contrast, transmission is performed in several places, that is, by each TechnosoftIpos device (and others) as explained above. This issue aims to defer the instant in which messages are sent until these are properly formed by each subdevice (TechnosoftIpos). Once collected, let (for instance) CanBusControlboard::positionMove dispatch them in one single call.

Implementation idea:

PeterBowman commented 5 years ago

I'd rather isolate CAN RX/TX so that only the CanBusControlboard can access this device (CanBusHico, CanBusPeak and so on). Therefore, this "master" device would encompass read&write operations as well as routing YARP interfaces (as it does now, but not so effectively as described above).

PeterBowman commented 5 years ago

I'm not sure that TX is attainable in the way I described above, given that certain operations, e.g. mode change, involve a handful of SDO requests that need to be processed sequentially, i.e. sometimes it is required to await a response at step i before proceeding to step i+1. For instance, switching to PT/PVT mode conveys several steps.

PeterBowman commented 5 years ago

RX ready at https://github.com/roboticslab-uc3m/yarp-devices/commit/8130c707030ff2e623aa1136fd7d3201526c500b.

PeterBowman commented 5 years ago

Some thoughts on parallelization:

PeterBowman commented 5 years ago

TX ready at https://github.com/roboticslab-uc3m/yarp-devices/commit/3002147ddf40bb5656e6726d9eb3aaa1495a7aa4. I have refactored read/write threads within CanBusControlboard (for now), also ICanBusSharer has been moved to its own header-only (for now) library, and several dependent apps (programs, examples, tests) have been disabled following https://github.com/roboticslab-uc3m/yarp-devices/issues/227#issuecomment-519710797.

PeterBowman commented 5 years ago

Most importantly, all of this is meaningless unless multi-joint queries can be actually processed in a parallel manner, e.g. via threaded for-loops. Reminds me of GrPPI; perhaps vanilla C++ (11?) renders sufficient for our needs, though.

Split into https://github.com/roboticslab-uc3m/yarp-devices/issues/230.

PeterBowman commented 5 years ago

Small follow-up: TX thread cleaned at https://github.com/roboticslab-uc3m/yarp-devices/commit/20a2390d782706c79031627bdaba7c29b60453c8 and CAN devices forced to non-blocking mode. Also, the period is now configurable.

PS interesting read: https://www.oreilly.com/library/view/linux-device-drivers/0596005903/ch06.html

If a process calls write and there is no space in the buffer, the process must block, and it must be on a different wait queue from the one used for reading. When some data has been written to the hardware device, and space becomes free in the output buffer, the process is awakened and the write call succeeds, although the data may be only partially written if there isn't room in the buffer for the count bytes that were requested.

(...) The input buffer is required to avoid losing data that arrives when nobody is reading. In contrast, data can't be lost on write, because if the system call doesn't accept data bytes, they remain in the user-space buffer. Even so, the output buffer is almost always useful for squeezing more performance out of the hardware.

PS2 in case I would ever want to interrupt a blocked device: https://stackoverflow.com/q/6249577. I see little use of blocking RX operations, whereas TX blocks would render harmful given that canWrite and the registering delegate mechanism are locked by the same mutex. That is, to block writes also means to block the prepareMessage callback, which needs to execute fast.