|
Motion Master 6.0.0-alpha.86
Next-generation motion control software
|
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< WsCommand > | parseWsCommand (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. | |
| using mm::RingLogSinkMt = typedef RingLogSink<std::mutex> |
Convenience alias: RingLogSink with a std::mutex (multi-threaded).
| 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.
| sinks | What installLogSinks returned. |
| config | The "logging" block of the configuration file. |
| userCacheRoot | Root the default logs directory hangs off. |
| auto_tuning::ProcessOptions mm::buildAutoTuningOptions | ( | const AutoTuningConfig & | config, |
| const std::filesystem::path & | logFile | ||
| ) |
Resolves what to start, and where its output goes.
| config | The "autoTuning" block of the configuration file. |
| logFile | Motion 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. |
| 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.
| deviceManager | Borrowed. Must outlive the bus this source is registered with. |
| interval | How 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. |
|
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.
|
inline |
Default rolling-release URL for the current TLS private key. See defaultCertUrl.
| 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:
local.motion-master.synapticon.com (loopback) and the *.ip.motion-master.synapticon.com wildcard (off-loopback, one cert for both),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.| certPath | Destination path for the certificate (overwritten on success). |
| keyPath | Destination path for the private key (overwritten on success). |
| certUrl | Source URL for the certificate. |
| keyUrl | Source URL for the private key. |
| 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.
| esiPath | Path to the vendor's ESI XML. |
| outDir | Directory to write the headers into; must exist. |
| 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.
| certPath | Path to the served certificate (overwritten on a successful fetch). |
| keyPath | Path to the served private key (overwritten on a successful fetch). |
| autoUpdate | Whether a missing/expired/expiring cert may be fetched. |
| certUrl | Source URL for the certificate fetch. |
| keyUrl | Source URL for the private-key fetch. |
| 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.
applyLoggingConfig and for the GET /api/log route. | 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.
| objectName | The object's name. |
| entryName | This subindex's name. |
| subindex | CoE subindex. |
| composite | Whether the object has subindices beyond 0 (ARRAY or RECORD). |
| 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:
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.)
| body | The parsed request body. |
| 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.
| message | The raw WebSocket text frame. |
nullopt if message is not a valid (un)subscribe request. | 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.
| certPath | Filesystem path to a PEM-encoded certificate (leaf or fullchain). |
CertInfo, or an error string if the file cannot be opened or parsed. | 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:
defaultCertPath / defaultKeyPath next to the binary (a release install),~/.acme.sh/local.motion-master.synapticon.com_ecc/ (a local acme.sh install),defaultCertPath / defaultKeyPath unconditionally — so the result is always populated and the caller's self-heal can fetch into it.| configCertPath | Certificate path from configuration; empty if unset. |
| configKeyPath | Private-key path from configuration; empty if unset. |
| defaultCertPath | Install-dir certificate path (next to the binary). |
| defaultKeyPath | Install-dir private-key path (next to the binary). |
source when not discovered). | 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.
| process | The process to start. Its options name the binary that is looked for. |
| enabled | AutoTuningConfig::enabled. |
| 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.
|
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.
|
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.