|
Motion Master 6.0.0-alpha.86
Next-generation motion control software
|
Copy-me starter for a Tier 3 extension: your own code inside the real-time loop. More...
#include <example_cyclic_task.h>
Classes | |
| struct | Config |
Everything the task needs to know, so nothing is hard-coded in execute. More... | |
Public Member Functions | |
| ExampleCyclicTask (mm::node::DeviceManager &deviceManager, Config config) | |
| Binds the task to a device manager. Registers nothing and touches no device — a task is constructed before the bus exists and must tolerate that. | |
| void | execute (const mm::core::CycleContext &ctx) noexcept override |
| Runs one cycle. Non-blocking, non-allocating, lock-free. | |
Public Member Functions inherited from mm::core::CyclicTask | |
| virtual | ~CyclicTask ()=default |
| Virtual destructor. | |
Copy-me starter for a Tier 3 extension: your own code inside the real-time loop.
This is the counterpart of example_routes.h, which extends the HTTP API (Tier 2). Where a route plug-in runs on an HTTP thread and may block, a CyclicTask runs on the RT thread, once per cycle, and may not: no locks, no allocation, no bus I/O, and it must return well inside the cycle period (1 ms by default). Everything below honours that, and the rules it follows are the whole point of reading it.
What it does — a very naive thermal interlock. It brings one drive into cyclic synchronous velocity mode, enables it, and runs it at a fixed velocity; if the drive's temperature goes over a limit it issues a quick stop, and it will not enable again until the temperature falls, at which point it climbs back up by itself. Naive is the word: a real interlock has hysteresis, an alarm path, and a considered answer for every sensor failure mode. What is realistic here is the shape — read state, decide, command, once per cycle, with nothing that can block.
main.cc will spin a motor.** It does not wait for anyone to enable the drive; enabling is its job. Check Config against your bus first.The three rules a cyclic task lives by, each visible in execute:
GameLoop enters the cycle before it calls execute, which is what makes the pointer valid for the body — a task takes no guard and checks nothing.Device::value<T>() / setValue<T>().** Both are a hash lookup plus one atomic load or store, and neither can tell whether an object rides the process image or is polled over SDO in the background. Whether the temperature is PDO-mapped is a commissioning decision, and this code does not change if it flips.Driving the CiA402 state machine from here is the right place for it, not a liberty. Mode of operation, controlword and statusword are all in the process image and all exchanged every cycle, so the climb (Switch On Disabled → Ready To Switch On → Switched On → Operation Enabled) is one write per cycle with the wire doing the waiting — where an off-RT caller has to sleep and poll for the same result. What must stay off the RT thread is the EtherCAT AL state (INIT / PRE-OP / SAFE-OP / OP): seconds of mailbox and register traffic, done through the HTTP API. A task runs in OP and never touches it.
One value here is not free. Config's temperature object is SDO-only on most drives, so nothing refills it cyclically — MonitoringManager::keepFresh has to be called once, off the RT thread, before the loop starts. Without it the read never produces a value, the interlock treats that as unsafe, and the drive never spins. main.cc shows the call.
| mm::example::ExampleCyclicTask::ExampleCyclicTask | ( | mm::node::DeviceManager & | deviceManager, |
| Config | config | ||
| ) |
Binds the task to a device manager. Registers nothing and touches no device — a task is constructed before the bus exists and must tolerate that.
|
overridevirtualnoexcept |
Runs one cycle. Non-blocking, non-allocating, lock-free.
Implements mm::core::CyclicTask.