Motion Master 6.0.0-alpha.86
Next-generation motion control software
Loading...
Searching...
No Matches
mm::node::MonitoringManager Class Reference

Owns the active monitorings and turns each into a lossless stream of recorded rows. More...

#include <monitoring_manager.h>

Public Types

using PublishFn = std::function< void(std::string topic, std::string json)>
 Publishes one batch: json (a {"type":"monitoring","topic",...} envelope) under topic. Wired in the composition root to the WebSocket server's topic publish.
 

Public Member Functions

 MonitoringManager (DeviceManager &deviceManager)
 Constructs the manager over deviceManager. Does not start sampling — call start().
 
 ~MonitoringManager ()
 Stops the owned refresher (and, once added, the sampler thread).
 
 MonitoringManager (const MonitoringManager &)=delete
 
MonitoringManageroperator= (const MonitoringManager &)=delete
 
void setPublish (PublishFn publish)
 Sets the batch publish callback. Call before start().
 
std::expected< Monitoring, std::string > create (Monitoring config)
 Validates config, classifies its parameters, registers SDO ones with the refresher, and registers the monitoring.
 
bool remove (const std::string &topic)
 Removes a monitoring, releasing its SDO parameters from the refresher.
 
std::optional< nlohmann::json > get (const std::string &topic) const
 Returns the monitoring resource as JSON (config + per-parameter source + buffer fill), or nullopt if topic is unknown.
 
nlohmann::json list () const
 Returns all monitoring resources as a JSON array, in topic order.
 
void start ()
 Starts the owned refresher (and, once added, the sampler thread). Idempotent.
 
void stop ()
 Stops the owned refresher (and, once added, the sampler thread). Idempotent.
 
void keepFresh (uint16_t devicePosition, uint16_t index, uint8_t subindex, std::chrono::milliseconds period)
 Keeps an SDO-only object's value fresh in the background, for a cyclic task to read.
 
void stopKeepingFresh (uint16_t devicePosition, uint16_t index, uint8_t subindex)
 Drops a reference taken by keepFresh. Polling stops when the last one is released.
 
void sampleAll ()
 Flushes every monitoring once: delivers each one's recorded cycles since its last flush. The deterministic core the scheduler thread drives; exposed for tests.
 
std::size_t monitoringCount () const
 Number of registered monitorings. For tests / status.
 
std::size_t polledSdoCount () const
 Number of distinct SDO objects currently polled by the refresher. For tests / status.
 

Detailed Description

Owns the active monitorings and turns each into a lossless stream of recorded rows.

A client creates a Monitoring (topic, interval, parameters); the manager validates it, classifies every parameter by how its value is sourced, and on each flush ships every process-data cycle recorded since the last flush. The stream is lossless: each monitoring holds a read cursor into the recorder ring and, each time it is due, decodes and publishes every cycle-row in [cursor, recorderHead()) as one batch, then advances the cursor. interval is the flush cadence, not a sample rate — a longer interval means a bigger batch, never dropped cycles. A cursor that falls more than a whole ring behind (a stalled client) is logged and resynced to the oldest available cycle; the gap is never silent.

Sourcing (decided once at create, against the published process image):

  • PDO — the object is mapped in the live image; its value is decoded for each recorded cycle from that cycle's ring record (no bus access, all values in a row from the same cycle). The decode spec is captured up front and re-captured on a re-map.
  • SDO — the object is not PDO-mapped; it is registered with the owned ParameterRefresher which polls it in the background, and the sampler reads the cached value. See Why a refresher exists at all below.

Monitoring is live-only: a parameter whose owning device is not exchanging (SAFE-OP/OP) samples null. Topics are unique.

Why a refresher exists at all. The sampler must never touch the bus: it serves a lossless per-cycle stream, and an SDO upload is a blocking mailbox round-trip that queues behind the driver's control-plane lock — one per cycle per object is not an option at kHz rates. PDO parameters need no bus access (they decode out of the recorder ring), but an SDO-sourced parameter has no cyclic source at all, so something has to fetch it on a slower cadence, off the flush path. The refresher polls into each device's parameter cache in the background and the flush reads only that cache — which is why every row in one batch shares the same SDO value (slow telemetry, not a per-cycle signal). This manager drives acquire / release rather than merely reading, because the classification is dynamic: a re-map can flip a parameter PDO→SDO or SDO→PDO, and recaptureIfRemapped moves it between the two.

Thread-safe. Owns a private ParameterRefresher because monitoring is its only client today. That is an ownership choice, not a coupling: the refresher depends on nothing but DeviceManager&, its entries are reference-counted per object, and its start / stop are idempotent — so it is usable on its own, and a second client would be served by constructing it in the composition root and injecting it here instead. The App wires only this manager (with a DeviceManager& and a publish callback).

Member Typedef Documentation

◆ PublishFn

using mm::node::MonitoringManager::PublishFn = std::function<void(std::string topic, std::string json)>

Publishes one batch: json (a {"type":"monitoring","topic",...} envelope) under topic. Wired in the composition root to the WebSocket server's topic publish.

Constructor & Destructor Documentation

◆ MonitoringManager() [1/2]

mm::node::MonitoringManager::MonitoringManager ( DeviceManager deviceManager)
explicit

Constructs the manager over deviceManager. Does not start sampling — call start().

◆ ~MonitoringManager()

mm::node::MonitoringManager::~MonitoringManager ( )

Stops the owned refresher (and, once added, the sampler thread).

◆ MonitoringManager() [2/2]

mm::node::MonitoringManager::MonitoringManager ( const MonitoringManager )
delete

Member Function Documentation

◆ create()

std::expected< Monitoring, std::string > mm::node::MonitoringManager::create ( Monitoring  config)

Validates config, classifies its parameters, registers SDO ones with the refresher, and registers the monitoring.

Validation: topic URL-safe; not already registered; interval between 5 ms and 2000 ms (the flush cadence); parameters non-empty; and every parameter is either PDO-mapped or present in its device's object dictionary (otherwise it cannot be sourced).

Returns
The created configuration on success, or an error string describing the first validation failure.

◆ get()

std::optional< nlohmann::json > mm::node::MonitoringManager::get ( const std::string &  topic) const

Returns the monitoring resource as JSON (config + per-parameter source + buffer fill), or nullopt if topic is unknown.

◆ keepFresh()

void mm::node::MonitoringManager::keepFresh ( uint16_t  devicePosition,
uint16_t  index,
uint8_t  subindex,
std::chrono::milliseconds  period 
)

Keeps an SDO-only object's value fresh in the background, for a cyclic task to read.

The Tier-3 door onto the refresher this class owns. A cyclic task reads every value the same way — Device::value<T>() off the parameter's cell — but only PDO-mapped objects are refilled by the RT exchange. An object left out of the process image because it changes slowly (a temperature, a drive's own error register) has no cyclic source, so something has to poll it; that is what the refresher does, and this is how a program asks for it without inventing a monitoring and a WebSocket topic it will never subscribe to.

Reference-counted with stopKeepingFresh and with the monitorings that need the same object: several requesters poll it once, at the shortest period any of them asked for, and it stops being polled when the last one lets go. period is clamped to an internal floor — SDO is slow, and hammering the mailbox would starve the control plane.

Call it off the RT thread, before or during the loop — never from inside a cycle. It takes the refresher's mutex and may allocate. That is the only restriction: registering while the loop runs is fine, since the refresher's thread never touches the RT path.

Parameters
devicePosition1-based bus position.
indexCoE object index.
subindexCoE object subindex.
periodDesired maximum time between polls.

◆ list()

nlohmann::json mm::node::MonitoringManager::list ( ) const

Returns all monitoring resources as a JSON array, in topic order.

◆ monitoringCount()

std::size_t mm::node::MonitoringManager::monitoringCount ( ) const

Number of registered monitorings. For tests / status.

◆ operator=()

MonitoringManager & mm::node::MonitoringManager::operator= ( const MonitoringManager )
delete

◆ polledSdoCount()

std::size_t mm::node::MonitoringManager::polledSdoCount ( ) const

Number of distinct SDO objects currently polled by the refresher. For tests / status.

◆ remove()

bool mm::node::MonitoringManager::remove ( const std::string &  topic)

Removes a monitoring, releasing its SDO parameters from the refresher.

Returns
true if a monitoring with topic existed, false otherwise.

◆ sampleAll()

void mm::node::MonitoringManager::sampleAll ( )

Flushes every monitoring once: delivers each one's recorded cycles since its last flush. The deterministic core the scheduler thread drives; exposed for tests.

◆ setPublish()

void mm::node::MonitoringManager::setPublish ( PublishFn  publish)

Sets the batch publish callback. Call before start().

◆ start()

void mm::node::MonitoringManager::start ( )

Starts the owned refresher (and, once added, the sampler thread). Idempotent.

◆ stop()

void mm::node::MonitoringManager::stop ( )

Stops the owned refresher (and, once added, the sampler thread). Idempotent.

◆ stopKeepingFresh()

void mm::node::MonitoringManager::stopKeepingFresh ( uint16_t  devicePosition,
uint16_t  index,
uint8_t  subindex 
)

Drops a reference taken by keepFresh. Polling stops when the last one is released.


The documentation for this class was generated from the following files: