Motion Master 6.0.0-alpha.86
Next-generation motion control software
Loading...
Searching...
No Matches
mm Namespace Reference

Namespaces

namespace  api
 HTTP-transport glue shared by the built-in server and route plug-in libs.
 
namespace  auto_tuning
 
namespace  comm
 
namespace  core
 
namespace  etg
 
namespace  example
 Example application namespace — the starting point for a C++ HTTP-endpoint plug-in.
 
namespace  net
 
namespace  node
 

Classes

struct  AutoTuningStartup
 The startup outcome, as the HTTP layer needs it. More...
 
struct  CertChainLink
 One certificate in the served chain — its subject/issuer common names plus the issuing organization (the friendly CA name, e.g. "Let's Encrypt", which lives in the O field rather than the CN). More...
 
struct  CertInfo
 Validity window and identity of a TLS leaf certificate, plus the full served chain. More...
 
struct  LogSinks
 The two sinks that exist before any configuration is read. More...
 
struct  ObjectAddressGeneratorSummary
 What one run of the object-address generator produced. More...
 
struct  ResolvedCert
 The TLS cert/key paths the server should use, plus where they came from. More...
 
class  RingLogSink
 Thread-safe spdlog sink that retains the most recent log entries in memory. More...
 
struct  WsCommand
 A client request received over the monitoring WebSocket to (un)subscribe to a topic. More...
 

Typedefs

using RingLogSinkMt = RingLogSink< std::mutex >
 Convenience alias: RingLogSink with a std::mutex (multi-threaded).
 

Functions

auto_tuning::ProcessOptions buildAutoTuningOptions (const AutoTuningConfig &config, const std::filesystem::path &logFile)
 Resolves what to start, and where its output goes.
 
AutoTuningStartup startAutoTuning (auto_tuning::Process &process, bool enabled)
 Starts the child unless the configuration or the filesystem says otherwise, and logs it.
 
void stopAutoTuning (auto_tuning::Process &process)
 Stops the child and logs how it ended.
 
node::NotificationBus::Source busHealthSource (node::DeviceManager &deviceManager, std::chrono::milliseconds interval=std::chrono::seconds{1})
 Reports cycles that answered with a short working counter, to the log and to clients.
 
std::expected< CertInfo, std::string > readCertInfo (const std::string &certPath)
 Parses a PEM file and extracts the leaf's validity window and the full certificate chain.
 
std::expected< void, std::string > fetchAndSwapCert (const std::string &certPath, const std::string &keyPath, const std::string &certUrl, const std::string &keyUrl)
 Downloads a fresh certificate and key and atomically installs them.
 
ResolvedCert resolveCertPaths (const std::string &configCertPath, const std::string &configKeyPath, const std::filesystem::path &defaultCertPath, const std::filesystem::path &defaultKeyPath)
 Resolve the TLS cert/key file paths, choosing by discovery when not configured.
 
std::expected< void, std::string > healCertIfNeeded (const std::string &certPath, const std::string &keyPath, bool autoUpdate, const std::string &certUrl, const std::string &keyUrl)
 Assess the served TLS certificate and refresh it if missing, expired, or expiring soon (the startup "cert self-heal").
 
std::string defaultCertUrl ()
 Default rolling-release URL for the current TLS certificate.
 
std::string defaultKeyUrl ()
 Default rolling-release URL for the current TLS private key. See defaultCertUrl.
 
LogSinks installLogSinks ()
 Replaces the default logger with a console sink plus the in-memory ring.
 
std::filesystem::path applyLoggingConfig (const LogSinks &sinks, const LoggingConfig &config, const std::filesystem::path &userCacheRoot)
 Applies the configured levels and adds the rotating file sink.
 
std::optional< WsCommandparseWsCommand (std::string_view message)
 Parses an inbound monitoring-WebSocket text message into a subscribe/unsubscribe command.
 
std::expected< mm::node::Monitoring, std::string > parseMonitoringRequest (const nlohmann::json &body)
 Parses a POST /api/monitorings request body into a Monitoring config.
 
std::string objectIdentifier (std::string_view objectName, std::string_view entryName, uint8_t subindex, bool composite)
 Builds the C++ identifier for one dictionary entry.
 
std::expected< ObjectAddressGeneratorSummary, std::string > generateObjectAddresses (const std::string &esiPath, const std::string &outDir)
 Generates the object-address headers from a vendor's ESI file.
 

Variables

constexpr std::string_view kShortWorkingCounterEvent = "short-working-counter"
 The data.event this source sends.
 
constexpr int kCertExpiringSoonDays = 7
 Number of days before notAfter at which a certificate is considered "expiring soon". A cert inside this window is refreshed proactively: it is one of the conditions that trigger the startup self-heal fetch (healCertIfNeeded), so a cert is renewed before it lapses rather than after. Also logs a startup warning and sets the expiresSoon flag returned by GET /api/cert.
 

Typedef Documentation

◆ RingLogSinkMt

using mm::RingLogSinkMt = typedef RingLogSink<std::mutex>

Convenience alias: RingLogSink with a std::mutex (multi-threaded).

Function Documentation

◆ applyLoggingConfig()

std::filesystem::path mm::applyLoggingConfig ( const LogSinks sinks,
const LoggingConfig config,
const std::filesystem::path &  userCacheRoot 
)

Applies the configured levels and adds the rotating file sink.

Parameters
sinksWhat installLogSinks returned.
configThe "logging" block of the configuration file.
userCacheRootRoot the default logs directory hangs off.
Returns
The active log file, or an empty path when logging to a file is off or the file could not be opened. An unwritable directory is a reason to run without a log file, not a reason not to run, so this never fails.

◆ buildAutoTuningOptions()

auto_tuning::ProcessOptions mm::buildAutoTuningOptions ( const AutoTuningConfig config,
const std::filesystem::path &  logFile 
)

Resolves what to start, and where its output goes.

Parameters
configThe "autoTuning" block of the configuration file.
logFileMotion Master's own log file, or empty when it has none. The child's log is placed beside it, so the child's output is listed and downloadable through /api/user-cache like everything else this process writes. Without a log file of our own there is nowhere better than our streams, which is what an empty path selects.

◆ busHealthSource()

node::NotificationBus::Source mm::busHealthSource ( node::DeviceManager deviceManager,
std::chrono::milliseconds  interval = std::chrono::seconds{1} 
)

Reports cycles that answered with a short working counter, to the log and to clients.

A cycle whose working counter comes back below the expected value is one where some device did not process the frame. The real-time loop is the only thread that sees every cycle, and it cannot log or publish — so it only counts, and this reads that count.

**Why it lives in the app and not in DeviceManager.** Writing a warning to a log is a policy, and DeviceManager is meant to be embeddable without one. It is also the pattern already in place: every background thread in this process belongs to a layer built outward from DeviceManager, which owns none itself.

It both warns and returns a payload. The warning is what reaches a support log from a machine nobody was watching; the payload is what reaches a client that is. Whether a source does one or both is a per-source decision, not a rule.

Silent unless the count has grown since it was last read, so a healthy bus never speaks and one fault is reported once.

Parameters
deviceManagerBorrowed. Must outlive the bus this source is registered with.
intervalHow often the count is read, which is also the most often it can be reported. A second, because someone watching a bus fault should see it while they are still looking at what caused it — and because at 1 kHz a faulting bus bumps the counter a thousand times a second, so reporting at the rate of the fault is useless. Nothing is lost to the gap: the count is cumulative and carries the time of the first and last cycle in it.

◆ defaultCertUrl()

std::string mm::defaultCertUrl ( )
inline

Default rolling-release URL for the current TLS certificate.

Published monthly by .github/workflows/cert-renewal.yml to a fixed-tag release, so this URL always serves the freshest cert regardless of when the application was last released.

◆ defaultKeyUrl()

std::string mm::defaultKeyUrl ( )
inline

Default rolling-release URL for the current TLS private key. See defaultCertUrl.

◆ fetchAndSwapCert()

std::expected< void, std::string > mm::fetchAndSwapCert ( const std::string &  certPath,
const std::string &  keyPath,
const std::string &  certUrl,
const std::string &  keyUrl 
)

Downloads a fresh certificate and key and atomically installs them.

Fetches certUrl and keyUrl over HTTPS (following redirects), then validates the downloaded pair before touching the live files:

  • the certificate parses as PEM and covers both names Motion Master is reached by — local.motion-master.synapticon.com (loopback) and the *.ip.motion-master.synapticon.com wildcard (off-loopback, one cert for both),
  • the certificate is not already expired,
  • the private key parses and matches the certificate. Only if all checks pass are certPath and keyPath atomically replaced (write-to-temp in the same directory, then rename); the key is written with 0600 permissions. On any failure the existing files are left untouched.
Parameters
certPathDestination path for the certificate (overwritten on success).
keyPathDestination path for the private key (overwritten on success).
certUrlSource URL for the certificate.
keyUrlSource URL for the private key.
Returns
Empty on success, or an error string describing the first failure.

◆ generateObjectAddresses()

std::expected< ObjectAddressGeneratorSummary, std::string > mm::generateObjectAddresses ( const std::string &  esiPath,
const std::string &  outDir 
)

Generates the object-address headers from a vendor's ESI file.

Parses esiPath and merges every device's dictionary — the profile dictionary plus each module's, by the ETG.2000 rules mm::etg implements — then writes one header per index range into outDir: profile_device_objects.h (0x1xxx plus the standard MDP objects), cia402_drive_objects.h (0x6xxx), and somanet_drive_objects.h (everything else).

The union of every device in the file is emitted, deliberately. Which module is fitted is unknowable offline, so the honest offering is every object the family could expose; an address that does not apply to the drive in front of you simply fails to resolve at runtime, exactly as a hand-written index would.

One header per index range rather than per device rests on the vendor giving one address one meaning. Merging four devices into a single table keyed by (index,subindex) is sound only if an address names the same quantity, with the same data type and unit, on every one of them. The standards guarantee that for the communication area (0x1xxx) and the CiA 402 profile (0x6xxx); for the manufacturer-specific area (0x2xxx) nothing does, so it is a SOMANET convention — and it holds because the family's devices draw the bulk of their dictionary from one shared ESI module rather than from four descriptions that happen to agree. Type disagreements between devices are reported as warnings and resolved first-wins; one index reused for a different quantity of the same type is what this cannot detect, and would mean a header per device instead of one.

The generator itself knows nothing about types: mm::etg resolves each entry to the C++ type it holds (EsiEntry::valueKind), including the width cross-check that keeps an "ARRAY [0..24] OF BYTE" from being emitted as a uint8_t.

Parameters
esiPathPath to the vendor's ESI XML.
outDirDirectory to write the headers into; must exist.
Returns
A summary, or an error string if the ESI cannot be parsed or a file cannot be written.

◆ healCertIfNeeded()

std::expected< void, std::string > mm::healCertIfNeeded ( const std::string &  certPath,
const std::string &  keyPath,
bool  autoUpdate,
const std::string &  certUrl,
const std::string &  keyUrl 
)

Assess the served TLS certificate and refresh it if missing, expired, or expiring soon (the startup "cert self-heal").

Inspects certPath: a missing cert, an expired one, or one expiring within kCertExpiringSoonDays triggers a fetch from certUrl / keyUrl via fetchAndSwapCert; a cert with ample life left is left alone, so a healthy boot makes no network call. Refreshing on imminent expiry — not just after it lapses — lets an ephemeral container (and the Docker entrypoint's 1-day self-signed fallback, which reads as expiring soon) self-heal to a fresh cert on start. The outcome is logged. When autoUpdate is false no fetch is attempted.

The fatal case is "no certificate that can be served": the cert is missing and either fetching is disabled or the fetch failed. A present cert (expired or expiring) is a degraded success — it still binds TLS, so it is served rather than failing when the fetch cannot run or fails.

Parameters
certPathPath to the served certificate (overwritten on a successful fetch).
keyPathPath to the served private key (overwritten on a successful fetch).
autoUpdateWhether a missing/expired/expiring cert may be fetched.
certUrlSource URL for the certificate fetch.
keyUrlSource URL for the private-key fetch.
Returns
Empty on success (including the present-but-served degraded case), or an error string when TLS cannot be served at all.

◆ installLogSinks()

LogSinks mm::installLogSinks ( )

Replaces the default logger with a console sink plus the in-memory ring.

Call this first, before anything that logs. Levels stay at spdlog's defaults until applyLoggingConfig runs, so the lines emitted while the configuration is being read are kept.

Returns
The two sinks, for applyLoggingConfig and for the GET /api/log route.

◆ objectIdentifier()

std::string mm::objectIdentifier ( std::string_view  objectName,
std::string_view  entryName,
uint8_t  subindex,
bool  composite 
)

Builds the C++ identifier for one dictionary entry.

k + PascalCase(object name), plus PascalCase(entry name) when a subindex names something different from its object. Subindex 0 of a composite is the entry-count field rather than a value, so it becomes k<Object>Count — without that, the safety objects whose subindex 1 repeats the object's own name would collide with their own count field, which is six of the SOMANET dictionary's 826 rows.

Punctuation and spaces are dropped, and a leading digit is prefixed, so the result is always a valid identifier.

Parameters
objectNameThe object's name.
entryNameThis subindex's name.
subindexCoE subindex.
compositeWhether the object has subindices beyond 0 (ARRAY or RECORD).

◆ parseMonitoringRequest()

std::expected< mm::node::Monitoring, std::string > mm::parseMonitoringRequest ( const nlohmann::json &  body)

Parses a POST /api/monitorings request body into a Monitoring config.

Expected shape:

{ "topic": "left-leg", "name": "Left Leg" (optional),
"interval": 1000,
"parameters": [[devicePosition, index, subindex], ...] }
uint8_t subindex
Definition esi_entry.cc:283

interval is the flush cadence in milliseconds. Each parameter is a three-element array of integers. This checks only the request shape (presence, JSON types, ranges) and converts it; semantic validation (URL-safe topic, the reserved notification topic, interval bounds, parameter sourcing) is MonitoringManager::create's job. (A legacy bufferSize key, if present, is ignored.)

Parameters
bodyThe parsed request body.
Returns
The configuration, or an error string naming the first shape problem.

◆ parseWsCommand()

std::optional< WsCommand > mm::parseWsCommand ( std::string_view  message)

Parses an inbound monitoring-WebSocket text message into a subscribe/unsubscribe command.

Accepts exactly {"subscribe":"<topic>"} or {"unsubscribe":"<topic>"} where <topic> is a URL-safe id. Anything else — malformed JSON, a non-object, an unknown key, a non-string or non-URL-safe topic — yields nullopt so the caller can simply ignore it. Pure (no I/O), so it is unit-tested directly; the server's message handler calls it and then ws->subscribe / ws->unsubscribe.

Parameters
messageThe raw WebSocket text frame.
Returns
The parsed command, or nullopt if message is not a valid (un)subscribe request.

◆ readCertInfo()

std::expected< CertInfo, std::string > mm::readCertInfo ( const std::string &  certPath)

Parses a PEM file and extracts the leaf's validity window and the full certificate chain.

The leaf-level subject / issuer / notBefore / notAfter come from the first certificate in the file; chain contains every certificate present, leaf first.

Parameters
certPathFilesystem path to a PEM-encoded certificate (leaf or fullchain).
Returns
The parsed CertInfo, or an error string if the file cannot be opened or parsed.

◆ resolveCertPaths()

ResolvedCert mm::resolveCertPaths ( const std::string &  configCertPath,
const std::string &  configKeyPath,
const std::filesystem::path &  defaultCertPath,
const std::filesystem::path &  defaultKeyPath 
)

Resolve the TLS cert/key file paths, choosing by discovery when not configured.

If configCertPath and configKeyPath are both set (e.g. from the config file) they are used as-is. Otherwise both are chosen from the first available source:

  1. defaultCertPath / defaultKeyPath next to the binary (a release install),
  2. ~/.acme.sh/local.motion-master.synapticon.com_ecc/ (a local acme.sh install),
  3. failing both, defaultCertPath / defaultKeyPath unconditionally — so the result is always populated and the caller's self-heal can fetch into it.
Parameters
configCertPathCertificate path from configuration; empty if unset.
configKeyPathPrivate-key path from configuration; empty if unset.
defaultCertPathInstall-dir certificate path (next to the binary).
defaultKeyPathInstall-dir private-key path (next to the binary).
Returns
The resolved paths and the discovered source (empty source when not discovered).

◆ startAutoTuning()

AutoTuningStartup mm::startAutoTuning ( auto_tuning::Process process,
bool  enabled 
)

Starts the child unless the configuration or the filesystem says otherwise, and logs it.

Call before the calling thread becomes real-time. A child inherits the scheduling policy of the thread that spawned it, and this one runs a spin-waiting numerical worker per core.

Parameters
processThe process to start. Its options name the binary that is looked for.
enabledAutoTuningConfig::enabled.
Returns
The status snapshot, and a client when something is running.

◆ stopAutoTuning()

void mm::stopAutoTuning ( auto_tuning::Process process)

Stops the child and logs how it ended.

Called explicitly rather than left to the destructor, so what happened to it is logged while the log is still being written. A Motion Master that exits leaves nothing of its own running.

Variable Documentation

◆ kCertExpiringSoonDays

constexpr int mm::kCertExpiringSoonDays = 7
inlineconstexpr

Number of days before notAfter at which a certificate is considered "expiring soon". A cert inside this window is refreshed proactively: it is one of the conditions that trigger the startup self-heal fetch (healCertIfNeeded), so a cert is renewed before it lapses rather than after. Also logs a startup warning and sets the expiresSoon flag returned by GET /api/cert.

◆ kShortWorkingCounterEvent

constexpr std::string_view mm::kShortWorkingCounterEvent = "short-working-counter"
inlineconstexpr

The data.event this source sends.

Named here rather than written into the render, because an event name is protocol: a client switches on it, and it must survive a rewrite of the message around it.