![]() |
Mads
Multi-Agent Distributed System
|
#include <agent.hpp>
Public Types | |
| using | loop_fun_t = std::function< std::chrono::nanoseconds()> |
| Enters the main loop of the agent. It also sets a signal handler for SIGNINT, which will set the running flag to false. | |
Public Member Functions | |
| Agent (std::string name, std::string settings_uri) | |
| Constructs an Agent object with the given name and settings path. If the settings path is a URI, the agent will connect to the broker with a REQ/REP socket and request the same settings file loaded by the broker. | |
| void | init (std::string name, std::string settings_uri, bool crypto=false, std::filesystem::path const &key_dir="", bool install_watchdog=true) |
| Initializes the agent. | |
| void | init (bool crypto=false, bool install_watchdog=true) |
| Initializes the agent. | |
| void | fetch_settings (bool crypto=false) |
| Acquires settings (and any broker-served attachment) without binding the agent to a settings section. | |
| virtual | ~Agent () |
| void | install_loop_watchdog (uint8_t max_count=3) |
| Install a watch thread to ensure exit from loops. | |
| virtual void | load_settings () |
| Additional settings to be loaded. Virtual function to be implemented by the derived class. | |
| void | save_settings (const std::string path=SETTINGS_PATH) |
| Save settings read from broker to file. | |
| nlohmann::json | get_settings () |
| Get all settings as JSON. | |
| virtual void | info (std::ostream &out=std::cout) |
| Prints information about the agent. | |
| void | connect (std::chrono::milliseconds delay=std::chrono::milliseconds(250)) |
| Connects the agent to the publish and subscribe endpoints. | |
| bool | wait_for_connection (std::chrono::milliseconds timeout) |
Blocks until the publisher socket's connection is confirmed by a real ZMQ_EVENT_CONNECTED/ZMQ_EVENT_HANDSHAKE_SUCCEEDED event, or timeout elapses (ZMQ_DEVELOPMENT.md §2.1). | |
| Mads::LinkState | link_state () const |
The current state of this agent's link to the broker: up or down, why (last_handshake – e.g. a ZMQ_EVENT_HANDSHAKE_FAILED_AUTH turns a CURVE rejection into a fact instead of a bare receive timeout), since when, and how often it has dropped and recovered. | |
| void | disconnect () |
| Disconnects the agent from the publish and subscribe endpoints. | |
| void | shutdown () |
| Performs a coordinated shutdown of the agent. | |
| void | set_cross (bool cross) |
| Sets the cross flag. | |
| void | enable_remote_control (bool threaded=false) |
| Enables remote control for the agent. | |
| void | enable_threaded_remote_control () |
| void | register_event (const event_type event=event_type::marker, const nlohmann::json &info=nlohmann::json(), const std::string &info_name="info") |
| Registers an event. | |
| void | publish (nlohmann::json payload, std::string topic="") |
| Publishes a message with the given JSON payload. | |
| void | publish (const char *payload, size_t len, nlohmann::json meta=nlohmann::json{{"format", "raw"}}, std::string topic="") |
| Publishes a message with the given binary blob payload. | |
| void | publish (const std::vector< unsigned char > &payload, nlohmann::json meta=nlohmann::json{{"format", "raw"}}, std::string topic="") |
| Publishes a message with the given binary blob payload. | |
| message_type | receive (bool dont_block=false) |
| Receives a message from the subscribe socket. | |
| bool | receive_raw_message (std::string &topic, std::vector< std::string > &parts, bool dont_block=false) |
| Receives a message without any JSON/blob (de)serialization (P3). | |
| void | publish_raw_message (const std::string &topic, const std::vector< std::string > &parts) |
| Publishes a raw multi-part message with no JSON encoding, no automatic field-stamping (agent_id/hostname/timestamp/...), no compression, and no frame header (P3). | |
| void | loop (loop_fun_t const &lambda, std::chrono::nanoseconds duration) |
| void | loop (loop_fun_t const &lambda) |
| Enters the main loop of the agent. It also sets a signal handler for SIGNINT, which will set the running flag to false. The loop duration is set to the time step of the agent, as pased in the ini file with the time_step parameter (or the finer time_step_us). It it is zero or not set, it will run at max speed. | |
| void | enable_high_res_loop (bool on=true, std::chrono::nanoseconds spin_margin=std::chrono::microseconds(200)) |
| Opt into (or out of) high-resolution loop pacing. | |
| bool | high_res_loop () const |
| Returns whether high-resolution loop pacing is enabled. | |
| void | remote_control (std::string payload_str) |
| Handles remote control commands. | |
| void | set_agent_id (std::string id) |
| Set the agent ID field. | |
| std::string | get_agent_id () |
| Get the agent ID. | |
| void | set_sub_endpoint (std::string endpoint) |
| Set the subscribe endpoint URL. | |
| std::string | sub_endpoint () const |
| Get the subscribe endpoint URL. | |
| void | set_pub_endpoint (std::string endpoint) |
| Set the publish endpoint URL. | |
| std::string | pub_endpoint () const |
| Get the publish endpoint URL. | |
| void | set_pub_topic (std::string topic) |
| Sets the publish topic. | |
| std::string | pub_topic () const |
| Gets the publish topic. | |
| void | set_sub_topic (std::vector< std::string > topics) |
| Sets the subscribe topics. | |
| std::vector< std::string > | sub_topic () const |
| Gets the subscribe topics. | |
| std::map< std::string, std::string > | status () |
| Returns the status of the system. | |
| std::string | name () |
| Returns the name of the agent. | |
| std::tuple< std::string, std::string > | last_message () |
| Returns the last received message by the agent. | |
| std::tuple< std::string, nlohmann::json > | last_json () |
| Returns the last received message as a parsed JSON object. | |
| std::string | last_topic () |
| Returns the topic of the last received message by the agent. | |
| std::tuple< std::string, std::string, std::vector< unsigned char > > | last_blob () |
| Returns the last received blob by the agent. | |
| std::tuple< std::string_view, std::string_view, std::span< const unsigned char > > | last_blob_view () const |
| Zero-copy view of the last received blob. | |
| size_t | dropped_messages () const |
| Number of messages dropped because they were malformed or could not be decoded (bad part count, failed decompression, etc.). | |
| bool | settings_are_local () const |
| Detects if settings are local or loaded from URI. | |
| bool | is_connected () |
| Detects if agent is connected. | |
| int | settings_timeout () |
| Returns the value of timeout in loading settings from URI. | |
| void | set_settings_timeout (int to) |
| Sets the value of timeout in loading settings from URI. Set to for no timeout. | |
| void | set_settings_timeout (std::chrono::milliseconds to) |
| Sets the value of timeout in loading settings from URI. Set to for no timeout. | |
| int | receive_timeout () |
| Returns the value of timeout in receiving messages. | |
| void | set_receive_timeout (int to) |
| Sets the value of timeout in receiving messages. Set to 0 for no timeout. | |
| void | set_receive_timeout (std::chrono::milliseconds to) |
| Sets the value of timeout in receiving messages. Set to 0 for no timeout. | |
| bool | restart () |
| Returns wheter a restart has been requested. | |
| std::shared_ptr< Mads::Runtime > | runtime () const |
| The Runtime that owns this agent's run state. | |
| bool | running () const |
| True while this agent's loops should keep going. | |
| void | set_runtime (std::shared_ptr< Mads::Runtime > runtime) |
| Attach the agent to a different Runtime. | |
| std::filesystem::path | attachment_path () |
| Returns the path to the attachment file. | |
| bool | is_crypto () |
| Returns whether CURVE encryption is enabled. | |
| void | setup_crypto (Mads::auth_verbose verbose=auth_verbose::off) |
| Set the use of CURVE encryption and enables authentication. | |
| std::unique_ptr< CurveAuth > * | curve_auth () |
| Returns a pointer to the CurveAuth object. | |
| std::filesystem::path | key_dir () |
| Returns the path to the etc directory. | |
| void | set_key_dir (const std::filesystem::path &path) |
| Sets the path to the etc directory. | |
| std::string | settings_uri () |
| void | set_conflate (bool conflate) |
| bool | conflate () |
| void | set_high_watermark (int i=1000) |
| Set the high watermark (ZMQ receive queue bound). | |
| int | high_watermark () |
| void | set_delivery (Delivery d) |
| Select subscriber delivery semantics. | |
| Delivery | delivery () const |
| Current delivery semantics. | |
| void | set_wire_format (WireFormat fmt) |
| Select the on-the-wire payload encoding used when publishing. | |
| WireFormat | wire_format () const |
| The wire format used for outgoing messages. | |
| void | set_compression (Compression c) |
| Select the payload compression policy for outgoing messages. | |
| Compression | compression () const |
| The compression policy used for outgoing messages. | |
| Mads::ClockOffsetResult | measure_clock_offset (size_t samples=5, int timeout_ms=1000) |
Runs samples four-timestamp exchanges against the broker's settings endpoint and adopts the result as this agent's own clock-offset measurement (clock-offset source A – clock_offset.hpp §"Source A"). Never throws: an unreachable broker, or one too old to know the "clock" command, leaves any previous measurement in place and returns {valid=false}. | |
| std::vector< Mads::ClockPeerObservation > | broadcast_clock_probe (std::chrono::milliseconds window=std::chrono::milliseconds(300)) |
Broadcasts a clock-sync ping on CLOCKSYNC_TOPIC and collects every responder's pong that arrives within window (clock-offset source B – clock_offset.hpp §"Source B"). Used internally when [agents] clock_source is "peer", and by mads top --probe to build its fleet table. | |
| Mads::ClockOffsetResult | clock_offset () const |
| This agent's currently adopted clock offset: the clock domain's consensus winner (Mads::ClockConsensus, possibly another agent's measurement) if one is known and fresh, else this agent's own last measurement, else {valid=false}. Add offset_us to a wall-clock reading taken on this host to obtain broker-host time. | |
| std::string | clock_domain () const |
| This process's clock-domain identity (Mads::detail::clock_domain_id(): boot_id on Linux, hostname elsewhere). Agents that share this value share a clock and are expected to converge on one identical clock_offset(). | |
Static Public Member Functions | |
| static void | install_signal_handlers () |
| Install SIGINT/SIGTERM handlers that request a clean shutdown. | |
Public Attributes | |
| double | timecode_fps = MADS_FPS |
| Mads::auth_verbose | auth_verbose = auth_verbose::off |
| std::string | server_key_name = "broker" |
| std::string | client_key_name = "client" |
| bool | dummy = false |
Protected Member Functions | |
| bool | keep_running () const |
| True while this agent's loops should keep going. | |
| void | connect_pub (std::chrono::milliseconds delay=std::chrono::milliseconds(0)) |
| Connects the agent to the publish endpoint. | |
| void | connect_sub () |
| Connects the agent to the subscribe endpoint and subscribes to the topics. | |
| bool | receive_raw (zmq::multipart_t &message, bool dont_block=false) |
| Internal use wrapping the receive step both for normal operations and for LastKnown Value (LKV) semantic, when high_watermark is 1. | |
| bool | _topic_matches_subscription (const std::string &topic) const |
MQTT-style wildcard filter (P2): true if topic is accepted by at least one entry of _sub_topic – literal entries are matched exactly as the raw ZMQ SUBSCRIBE prefix already does today, wildcard entries (containing '+'/'#') via Mads::topic_match(). Only ever consulted when _wildcard_sub_topic is non-empty; see connect_sub()/receive_raw(). | |
| void | _apply_socket_options () |
| Resolves the plain libzmq transport-tuning knobs (ZMQ_DEVELOPMENT.md §1.4: TCP keepalive, SNDBUF/RCVBUF, ...) from settings and applies them to both sockets. Called once from init(), after settings are parsed and before connect(). A key left unset in both [agents] and the agent's own section makes no setsockopt() call at all, so an unedited mads.ini is unaffected. | |
| void | _start_io_thread () |
| Brings up _io_thread if it is not already running. Called from whichever of connect_pub()/connect_sub() runs first, since a publish-only agent never reaches the latter and a subscribe-only agent never reaches the former. | |
| void | _configure_clock_sync () |
| Parses the [agents]/[<name>] clock_* settings during init() and appends CLOCKSYNC_TOPIC to _sub_topic when this agent needs to participate (mirrors how enable_remote_control() appends "control"). | |
| bool | _clock_wants_sync () const |
| void | _start_clock_thread () |
| Brings up _clock_thread if it is not already running: announces this agent's adopted clock offset on CLOCKSYNC_TOPIC every clock_announce_ms, and – when clock_interval_ms > 0 and this agent is its clock domain's current winner (or no winner is known yet) – re-measures. A "peer"-source agent takes its very first measurement here too, since broadcast_clock_probe() needs a connected socket that does not exist yet during init(). Called from connect(); joined in disconnect()/shutdown() exactly like _io_thread. | |
| void | _run_peer_measurement () |
| void | _handle_clocksync_message (const nlohmann::json &msg) |
| void | _announce_clock_offset (const Mads::ClockOffsetResult &r) |
| Mads::ClockOffsetResult | _stamp_clock_result (Mads::ClockOffsetResult r, Mads::ClockSource source, uint8_t hops) |
| std::string | _clock_agent_identity () const |
Static Protected Member Functions | |
| static std::tuple< std::string, std::string, std::string > | split_URL (const std::string &url) |
Protected Attributes | |
| std::string | _hostname |
| std::string | _name |
| std::string | _settings_uri |
| std::string | _raw_settings |
| toml::table | _config |
| std::string | _pub_endpoint |
| std::string | _sub_endpoint |
| std::string | _pub_topic |
| std::string | _agent_id |
| std::vector< std::string > | _sub_topic |
| std::vector< std::string > | _wildcard_sub_topic |
| zmq::context_t | _context |
| zmq::socket_t | _publisher |
| zmq::socket_t | _subscriber |
| Mads::SocketMonitor | _pub_monitor |
| Mads::SocketMonitor | _sub_monitor |
| std::map< std::string, std::shared_ptr< LazyPayload > > | _status |
| std::tuple< std::string, std::shared_ptr< LazyPayload > > | _last_message |
| std::tuple< std::string, std::string, std::vector< unsigned char > > | _last_blob |
| std::mutex | _message_state_mutex |
| bool | _cross = false |
| bool | _connected = false |
| int | _receive_timeout = DEFAULT_RECEIVE_TIMEOUT_MS |
| int | _settings_timeout = 0 |
| bool | _init_done = false |
| bool | _settings_fetched = false |
| bool | _restart = false |
| std::shared_ptr< Mads::Runtime > | _runtime = std::make_shared<Mads::Runtime>() |
| std::atomic< bool > | _stopping {false} |
| bool | _remote_controlled = false |
| std::chrono::nanoseconds | _time_step = std::chrono::nanoseconds(0) |
| bool | _high_res_loop = false |
| std::chrono::nanoseconds | _spin_margin = std::chrono::microseconds(200) |
| double | _timecode_offset = 0.0 |
| std::filesystem::path | _attachment_path |
| bool | _crypto = false |
| bool | _conflate = false |
| std::unique_ptr< CurveAuth > | _curve_auth = nullptr |
| std::filesystem::path | _key_dir |
| bool | _last_value_only = false |
| bool | _shutdown_done = false |
| SharedLatest< zmq::multipart_t > | _latest_message |
| std::thread | _io_thread |
| std::atomic< bool > | _io_reads_subscriber {false} |
| std::thread | _watchdog_thread |
| std::thread | _startup_event_thread |
| std::mutex | _event_mtx |
| std::condition_variable | _event_cv |
| std::mutex | _publish_mutex |
| std::atomic< bool > | _watchdog_stop {false} |
| bool | _rc_owns_socket = false |
| WireFormat | _wire_format = WireFormat::Json |
| Compression | _compression = Compression::Auto |
| std::atomic< size_t > | _dropped_messages {0} |
| nlohmann::json | _settings_json |
| Mads::ClockSource | _clock_source = Mads::ClockSource::Broker |
| bool | _clock_sync_responder = true |
| int | _clock_interval_ms = 0 |
| int | _clock_announce_ms = 5000 |
| bool | _clock_correction = false |
| Mads::ClockConsensus | _clock_consensus {std::chrono::seconds(30)} |
| std::mutex | _clock_mtx |
| Mads::ClockOffsetResult | _own_clock_measurement |
| uint64_t | _clock_seq = 0 |
| std::thread | _clock_thread |
| std::mutex | _clocksync_mtx |
| std::chrono::steady_clock::time_point | _clocksync_probe_sent_at {} |
| std::vector< Mads::ClockPeerObservation > | _clocksync_pongs |
| uint64_t | _clocksync_probe_seq = 0 |
| std::map< std::string, std::chrono::steady_clock::time_point > | _clocksync_last_reply |
| using Mads::Agent::loop_fun_t = std::function<std::chrono::nanoseconds()> |
Enters the main loop of the agent. It also sets a signal handler for SIGNINT, which will set the running flag to false.
The loop period is expressed in nanoseconds internally, so that any std::chrono::duration (milliseconds, microseconds, ...) can be passed or returned by the lambda: e.g. agent.loop(cb, 500us); opts into microsecond-resolution pacing, while existing millisecond-based code (agent.loop(cb, 100ms);, return 0ms;) keeps compiling and behaving as before (the conversion to nanoseconds is implicit and lossless).
By default, the wait between iterations uses this_thread::sleep_for, whose actual wake-up jitter is limited by the OS scheduler (typically within a fraction of a millisecond). For tighter, microsecond-accurate pacing, opt into enable_high_res_loop(), which busy-spins the tail of each interval instead of sleeping through it, trading CPU time for precision: the fraction of each interval spent spinning (and so the extra CPU load) scales with spin_margin relative to the loop period, so it is most noticeable on short periods and/or a large spin_margin.
| lambda | the function to be executed in the main loop; it can return the requested duration of the next loop: if this is 0, the default loop time is used, otherwise the value returned by the lambda |
| duration | the duration of the loop (default 0, max speed) |
| AgentError | if not initialized |
| Mads::Agent::Agent | ( | std::string | name, |
| std::string | settings_uri | ||
| ) |
Constructs an Agent object with the given name and settings path. If the settings path is a URI, the agent will connect to the broker with a REQ/REP socket and request the same settings file loaded by the broker.
| name | The name of the agent. If it is a path, it take the filename. |
| settings_path | The path or URI to the settings file for the agent. |
|
virtual |
|
protected |
Publishes r as a CLOCKSYNC_TOPIC announcement and records it into this agent's own local consensus view.
|
protected |
Resolves the plain libzmq transport-tuning knobs (ZMQ_DEVELOPMENT.md §1.4: TCP keepalive, SNDBUF/RCVBUF, ...) from settings and applies them to both sockets. Called once from init(), after settings are parsed and before connect(). A key left unset in both [agents] and the agent's own section makes no setsockopt() call at all, so an unedited mads.ini is unaffected.
|
protected |
Stable per-agent identity for clock-sync bookkeeping (ping/pong/ announce "who"), independent of the optional, user-facing –agent-id/_agent_id used for payload stamping: falls back to "<name>@<pid>" so consensus works even when –agent-id was never set.
|
protected |
True when this agent needs to hear/answer CLOCKSYNC_TOPIC (source != none, or the responder is enabled – true by default). Deliberately not reflected in _sub_topic/sub_topic() – see _configure_clock_sync().
|
protected |
Parses the [agents]/[<name>] clock_* settings during init() and appends CLOCKSYNC_TOPIC to _sub_topic when this agent needs to participate (mirrors how enable_remote_control() appends "control").
|
protected |
Handles one decoded CLOCKSYNC_TOPIC message (ping/pong/announce). Called from receive()'s two data-frame branches and from the _io_thread control-analogous branch; never throws, never surfaces the message to the caller.
|
protected |
One clock_source == "peer" measurement attempt: probes, picks an anchor per the chain-safety rules (bounded hops, non-stale, a real source), composes clock_offset(mine) = anchor.offset_us + theta, and records/announces the result. A no-op if no eligible peer responds.
|
protected |
Fills in the Agent-level bookkeeping estimate()/ClockOffsetEstimator do not know about: source, hops, this agent's identity, and the next measurement sequence number.
|
protected |
Brings up _clock_thread if it is not already running: announces this agent's adopted clock offset on CLOCKSYNC_TOPIC every clock_announce_ms, and – when clock_interval_ms > 0 and this agent is its clock domain's current winner (or no winner is known yet) – re-measures. A "peer"-source agent takes its very first measurement here too, since broadcast_clock_probe() needs a connected socket that does not exist yet during init(). Called from connect(); joined in disconnect()/shutdown() exactly like _io_thread.
|
protected |
Brings up _io_thread if it is not already running. Called from whichever of connect_pub()/connect_sub() runs first, since a publish-only agent never reaches the latter and a subscribe-only agent never reaches the former.
|
protected |
MQTT-style wildcard filter (P2): true if topic is accepted by at least one entry of _sub_topic – literal entries are matched exactly as the raw ZMQ SUBSCRIBE prefix already does today, wildcard entries (containing '+'/'#') via Mads::topic_match(). Only ever consulted when _wildcard_sub_topic is non-empty; see connect_sub()/receive_raw().
| topic | The concrete topic of an already-received message. |
| std::filesystem::path Mads::Agent::attachment_path | ( | ) |
Returns the path to the attachment file.
This is the file that was sent by the broker when reading settings from URI. It is used to load additional plugins or resources.
| std::vector< Mads::ClockPeerObservation > Mads::Agent::broadcast_clock_probe | ( | std::chrono::milliseconds | window = std::chrono::milliseconds(300) | ) |
Broadcasts a clock-sync ping on CLOCKSYNC_TOPIC and collects every responder's pong that arrives within window (clock-offset source B – clock_offset.hpp §"Source B"). Used internally when [agents] clock_source is "peer", and by mads top --probe to build its fleet table.
Blocking: drives this agent's own receive() calls for the duration of window, so nothing else on the calling thread progresses meanwhile, and it is not safe to call concurrently with another in-flight broadcast_clock_probe() on the same Agent (single-flight; the background clock thread already respects this by construction). Returns an empty vector immediately if this agent has no publisher connected (no pub_topic) – it cannot ping without one.
| window | how long to wait for responders. |
| std::string Mads::Agent::clock_domain | ( | ) | const |
This process's clock-domain identity (Mads::detail::clock_domain_id(): boot_id on Linux, hostname elsewhere). Agents that share this value share a clock and are expected to converge on one identical clock_offset().
| Mads::ClockOffsetResult Mads::Agent::clock_offset | ( | ) | const |
This agent's currently adopted clock offset: the clock domain's consensus winner (Mads::ClockConsensus, possibly another agent's measurement) if one is known and fresh, else this agent's own last measurement, else {valid=false}. Add offset_us to a wall-clock reading taken on this host to obtain broker-host time.
| Compression Mads::Agent::compression | ( | ) | const |
The compression policy used for outgoing messages.
| bool Mads::Agent::conflate | ( | ) |
| void Mads::Agent::connect | ( | std::chrono::milliseconds | delay = std::chrono::milliseconds(250) | ) |
Connects the agent to the publish and subscribe endpoints.
Optionally, this function accepts a delay parameter, which bounds how long connecting may take before publishing is safe. This is needed to ensure that the agent is connected before sending messages. The default delay is 250 milliseconds.
delay is an upper bound, not a fixed cost: the publisher waits for its ZMTP handshake and then for SUBSCRIPTION_SETTLE_DELAY of slow-joiner grace, and returns as soon as both are done. Pass 0 only when the agent does not publish immediately – a first message sent with no grace at all is dropped by the PUB socket, unsent and unreported.
| type | The type of connection to be established. |
| delay | Upper bound, in milliseconds, on the connect-and-settle wait. |
| AgentError | if not initialized |
Referenced by Mads::Dealer::connect(), and Mads::Worker::connect().
|
protected |
Connects the agent to the publish endpoint.
| delay | The delay in milliseconds after connecting. |
|
protected |
Connects the agent to the subscribe endpoint and subscribes to the topics.
| std::unique_ptr< CurveAuth > * Mads::Agent::curve_auth | ( | ) |
Returns a pointer to the CurveAuth object.
| Delivery Mads::Agent::delivery | ( | ) | const |
Current delivery semantics.
| void Mads::Agent::disconnect | ( | ) |
Disconnects the agent from the publish and subscribe endpoints.
| size_t Mads::Agent::dropped_messages | ( | ) | const |
Number of messages dropped because they were malformed or could not be decoded (bad part count, failed decompression, etc.).
| void Mads::Agent::enable_high_res_loop | ( | bool | on = true, |
| std::chrono::nanoseconds | spin_margin = std::chrono::microseconds(200) |
||
| ) |
Opt into (or out of) high-resolution loop pacing.
When enabled, the wait between loop iterations sleeps for most of the remaining interval and then busy-spins on a steady_clock check for the last spin_margin, trading CPU usage for microsecond-accurate wake-up timing: the extra CPU cost is roughly proportional to spin_margin divided by the loop period (e.g. a 200us margin on a 500us period keeps a core busy on the order of ~20-40% of the time; a 200us margin on a 100ms period costs a negligible fraction). Leave disabled (the default) for millisecond-granular loops, where plain sleep_for is sufficient and cheaper.
| on | whether to enable high-resolution pacing (default true). |
| spin_margin | the portion of each interval to busy-spin instead of sleep (default 200us); only relevant when on is true. |
| void Mads::Agent::enable_remote_control | ( | bool | threaded = false | ) |
Enables remote control for the agent.
This function subscribes the agent to the "control" topic. If the agent is not a subscriber starts a thread to handle remote control commands.
If the agent is also a subscriber, then the remote control is handled in the main loop by calling the remote_control() function.
| threaded | If true, the agent is NOT supposed to receive data (it is a pure sink) so messages are read on a searate thread |
| AgentError | if not initialized |
| AgentError | if already connected |
Referenced by enable_threaded_remote_control().
|
inline |
Definition at line 477 of file agent.hpp.
References enable_remote_control().
| void Mads::Agent::fetch_settings | ( | bool | crypto = false | ) |
Acquires settings (and any broker-served attachment) without binding the agent to a settings section.
This performs the settings acquisition portion of init() only: CURVE setup, resolving the local-file/broker-URI settings source, and (if settings come from a broker) saving any served attachment and renaming it per the section's attachment_ext. It does not validate or bind the _name section, install the loop watchdog, or call load_settings().
Idempotent: a second call (including the one made internally by init(), if not already fetched) is a no-op. Useful for callers that need to inspect settings or a served attachment (e.g. to select a plugin file) before committing to init()'s section binding.
| crypto | Whether to use CURVE encryption (default false). |
| AgentError | if timed out in reading settings from broker. |
| std::string Mads::Agent::get_agent_id | ( | ) |
| nlohmann::json Mads::Agent::get_settings | ( | ) |
Get all settings as JSON.
| bool Mads::Agent::high_res_loop | ( | ) | const |
Returns whether high-resolution loop pacing is enabled.
| int Mads::Agent::high_watermark | ( | ) |
|
virtual |
Prints information about the agent.
This function prints the agent's name, settings file path, publish endpoint, publish topic, subscribe endpoint, and subscribe topics.
| out | The output stream to be used (default is cout). |
| AgentError | if not initialized |
Reimplemented in Mads::Dealer, Mads::Dummy, Mads::Image, Mads::Logger, Mads::Metadata, and Mads::Worker.
Referenced by Mads::Dealer::info(), Mads::Dummy::info(), Mads::Image::info(), Mads::Metadata::info(), and Mads::Worker::info().
| void Mads::Agent::init | ( | bool | crypto = false, |
| bool | install_watchdog = true |
||
| ) |
Initializes the agent.
This function loads the settings file and sets the member variables of the agent.
| crypto | Whether to use CURVE encryption (default false). |
| AgentError | if timed out in reading settings from broker. |
| void Mads::Agent::init | ( | std::string | name, |
| std::string | settings_uri, | ||
| bool | crypto = false, |
||
| std::filesystem::path const & | key_dir = "", |
||
| bool | install_watchdog = true |
||
| ) |
Initializes the agent.
This function loads the settings file and sets the member variables of the agent.
If the settings_uri is "none", the agent is initialized with default settings and does not attempt to load any settings file. This is useful for testing purposes.
| name | The name of the agent (as it is). |
| settings_uri | The path or URI to the settings file for the agent. |
| crypto | Whether to use CURVE encryption (default false). |
| key_dir | The directory where the CURVE keys are stored. |
| AgentError | if timed out in reading settings from broker. |
| void Mads::Agent::install_loop_watchdog | ( | uint8_t | max_count = 3 | ) |
Install a watch thread to ensure exit from loops.
This starts a low-frequency thread that forces an exit when the agent keeps looping for more than 3 seconds after it was asked to stop (its Runtime was stopped or shutdown/disconnect was requested)
|
static |
Install SIGINT/SIGTERM handlers that request a clean shutdown.
Idempotent and process-global: only the first call installs handlers, so calling loop() repeatedly (or running several agents in one process) does not clobber handler state.
| bool Mads::Agent::is_connected | ( | ) |
Detects if agent is connected.
| bool Mads::Agent::is_crypto | ( | ) |
Returns whether CURVE encryption is enabled.
|
inlineprotected |
| std::filesystem::path Mads::Agent::key_dir | ( | ) |
Returns the path to the etc directory.
| std::tuple< std::string, std::string, std::vector< unsigned char > > Mads::Agent::last_blob | ( | ) |
Returns the last received blob by the agent.
| std::tuple< std::string_view, std::string_view, std::span< const unsigned char > > Mads::Agent::last_blob_view | ( | ) | const |
Zero-copy view of the last received blob.
Returns non-owning views (topic, format, bytes) into the agent's internally stored blob, avoiding the full copy performed by last_blob(). The returned views remain valid only until the next call to receive() and must not be used concurrently with a threaded receive/drain thread.
| std::tuple< std::string, nlohmann::json > Mads::Agent::last_json | ( | ) |
Returns the last received message as a parsed JSON object.
Fast path for consumers that want a nlohmann::json rather than its text: for MsgPack frames this returns the already-decoded object (no re-dump and no re-parse); for JSON frames it parses once and caches. Avoids the round-trip that last_message() + json::parse() would otherwise incur.
| std::tuple< std::string, std::string > Mads::Agent::last_message | ( | ) |
Returns the last received message by the agent.
| std::string Mads::Agent::last_topic | ( | ) |
Returns the topic of the last received message by the agent.
| Mads::LinkState Mads::Agent::link_state | ( | ) | const |
The current state of this agent's link to the broker: up or down, why (last_handshake – e.g. a ZMQ_EVENT_HANDSHAKE_FAILED_AUTH turns a CURVE rejection into a fact instead of a bare receive timeout), since when, and how often it has dropped and recovered.
Reported from the subscriber socket, since that is the one over which an agent would otherwise silently stop hearing from a departed broker; publish-only agents (no sub_topic, so no subscriber connection) fall back to the publisher's. Both connect to the same broker, so in practice they rise and fall together.
A set_cross() agent binds rather than connects, and gets one ZMQ_EVENT_DISCONNECTED per departing peer with no matching handshake event on arrival – events that would add up to "down" as soon as any one peer left. There is no broker link to describe in that topology, so this reports Mads::LinkStatus::Unknown throughout.
|
virtual |
Additional settings to be loaded. Virtual function to be implemented by the derived class.
This function is automatically called by init() to load additional settings.
Reimplemented in Mads::Dummy.
| void Mads::Agent::loop | ( | loop_fun_t const & | lambda | ) |
Enters the main loop of the agent. It also sets a signal handler for SIGNINT, which will set the running flag to false. The loop duration is set to the time step of the agent, as pased in the ini file with the time_step parameter (or the finer time_step_us). It it is zero or not set, it will run at max speed.
| lambda | the function to be executed in the main loop; it can return the requested duration of the next loop: if this is 0, the default loop time is used, otherwise the value returned by the lambda |
| AgentError | if not initialized |
| void Mads::Agent::loop | ( | loop_fun_t const & | lambda, |
| std::chrono::nanoseconds | duration | ||
| ) |
| Mads::ClockOffsetResult Mads::Agent::measure_clock_offset | ( | size_t | samples = 5, |
| int | timeout_ms = 1000 |
||
| ) |
Runs samples four-timestamp exchanges against the broker's settings endpoint and adopts the result as this agent's own clock-offset measurement (clock-offset source A – clock_offset.hpp §"Source A"). Never throws: an unreachable broker, or one too old to know the "clock" command, leaves any previous measurement in place and returns {valid=false}.
Called automatically at the end of init() when [agents] clock_source is "broker" (the default) and settings come from a broker; called again periodically by the background clock thread when clock_interval_ms > 0 and this agent is (or would become) its clock domain's adopted source. Safe to call directly at any other time too.
| samples | number of round-trips; the min-delay one is kept. |
| timeout_ms | per-request send/receive timeout. |
| std::string Mads::Agent::name | ( | ) |
Returns the name of the agent.
|
inline |
Get the publish endpoint URL.
Definition at line 734 of file agent.hpp.
References _pub_endpoint.
|
inline |
Gets the publish topic.
Definition at line 748 of file agent.hpp.
References _pub_topic.
| void Mads::Agent::publish | ( | const char * | payload, |
| size_t | len, | ||
| nlohmann::json | meta = nlohmann::json{{"format", "raw"}}, |
||
| std::string | topic = "" |
||
| ) |
Publishes a message with the given binary blob payload.
| payload | The binary blob payload of the message. |
| format | The format of the blob (a string, default "raw"). |
| topic | The topic of the message. |
| AgentError | if not initialized |
| void Mads::Agent::publish | ( | const std::vector< unsigned char > & | payload, |
| nlohmann::json | meta = nlohmann::json{{"format", "raw"}}, |
||
| std::string | topic = "" |
||
| ) |
Publishes a message with the given binary blob payload.
| payload | The binary blob payload of the message. |
| metadata | The metadata for the blob. |
| topic | The topic of the message. |
| AgentError | if not initialized |
| void Mads::Agent::publish | ( | nlohmann::json | payload, |
| std::string | topic = "" |
||
| ) |
Publishes a message with the given JSON payload.
| payload | The JSON payload of the message. |
| AgentError | if not initialized |
Referenced by Mads::Dummy::publish(), Mads::Image::publish_change(), and Mads::Bridge::route().
| void Mads::Agent::publish_raw_message | ( | const std::string & | topic, |
| const std::vector< std::string > & | parts | ||
| ) |
Publishes a raw multi-part message with no JSON encoding, no automatic field-stamping (agent_id/hostname/timestamp/...), no compression, and no frame header (P3).
Sends topic followed by parts, byte for byte, exactly as given. Intended for mads-play, to republish a recorded frame unchanged – pairs with receive_raw_message() and the parts BagReader hands back, so a JSON message or a blob's meta+bytes parts are never re-parsed, re-dumped, or re-copied through the JSON path.
| topic | The topic frame. |
| parts | The frames to send after the topic, in order. |
| AgentError | if not initialized. |
| message_type Mads::Agent::receive | ( | bool | dont_block = false | ) |
Receives a message from the subscribe socket.
This function receives a message from the subscribe socket and updates the agent's status and last received message.
| AgentError | if the received message has only one part or more than two parts. |
| AgentError | if not initialized |
|
protected |
Internal use wrapping the receive step both for normal operations and for LastKnown Value (LKV) semantic, when high_watermark is 1.
| message | |
| dont_block |
| bool Mads::Agent::receive_raw_message | ( | std::string & | topic, |
| std::vector< std::string > & | parts, | ||
| bool | dont_block = false |
||
| ) |
Receives a message without any JSON/blob (de)serialization (P3).
Exposes the wire frame exactly as received: the topic (part 0) and every subsequent part verbatim as bytes (embedded NUL bytes included), with no frame-header parsing, no decompression, and no JSON decode – whatever was published (JSON text, a MsgPack frame, or a blob's meta+bytes parts) comes back unchanged. Intended for mads-record, so a bag file gets exactly what was on the wire without round-tripping through nlohmann::json or re-copying blob bytes.
Goes through the same receive_raw() path as receive() (wildcard sub_topic filtering (P2) and Last-Known-Value delivery both still apply); this is purely a difference in how the received frame is exposed to the caller, not in what gets delivered.
| topic | Out param: the topic frame. |
| parts | Out param: every frame after the topic, in wire order. Cleared and repopulated on a successful receive; left untouched if nothing was received. |
| dont_block | Whether to return immediately if no message is pending. |
| AgentError | if not initialized, or while threaded remote control owns the subscriber socket. |
| int Mads::Agent::receive_timeout | ( | ) |
Returns the value of timeout in receiving messages.
Referenced by Mads::Worker::pull().
| void Mads::Agent::register_event | ( | const event_type | event = event_type::marker, |
| const nlohmann::json & | info = nlohmann::json(), |
||
| const std::string & | info_name = "info" |
||
| ) |
Registers an event.
This function registers an event with the broker. The event is sent after 500 milliseconds.
| event | The event to be registered. |
| AgentError | if not initialized |
| void Mads::Agent::remote_control | ( | std::string | payload_str | ) |
Handles remote control commands.
This function is called by the main loop to handle remote control commands. It checks if the last received message is a control message and acts accordingly.
| bool Mads::Agent::restart | ( | ) |
Returns wheter a restart has been requested.
|
inline |
True while this agent's loops should keep going.
Combines the Runtime state (group and process level) with the per-agent stop request raised by shutdown()/disconnect(). This is the condition loop() checks between iterations, exposed for application main loops.
Definition at line 942 of file agent.hpp.
References keep_running().
|
inline |
The Runtime that owns this agent's run state.
loop(), the delivery drain thread, and the threaded remote control all keep going while runtime()->running() is true and the agent has not been individually stopped (shutdown()/disconnect()). Stopping the Runtime stops every agent that shares it.
Definition at line 931 of file agent.hpp.
References _runtime.
| void Mads::Agent::save_settings | ( | const std::string | path = SETTINGS_PATH | ) |
Save settings read from broker to file.
| path |
| AgentError | if not initialized or settings are local. |
| void Mads::Agent::set_agent_id | ( | std::string | id | ) |
Set the agent ID field.
| id | The agent ID |
| void Mads::Agent::set_compression | ( | Compression | c | ) |
Select the payload compression policy for outgoing messages.
Default is Compression::Auto (compress only frames >= COMPRESSION_AUTO_THRESHOLD bytes). Compression::Snappy reproduces the historical always-compress behaviour; Compression::None disables it.
| c | The compression policy to use. |
| void Mads::Agent::set_conflate | ( | bool | conflate | ) |
| void Mads::Agent::set_cross | ( | bool | cross | ) |
Sets the cross flag.
If the cross flag is set, the agent will bind to the publish endpoint and connect to the subscribe endpoint. This is useful for testing purposes.
| cross | The value of the cross flag. |
| void Mads::Agent::set_delivery | ( | Delivery | d | ) |
Select subscriber delivery semantics.
| d | Delivery::Queued keeps the receive queue; Delivery::LastKnownValue keeps only the latest message, draining the rest on a background thread. |
| AgentError | if already connected. |
| void Mads::Agent::set_high_watermark | ( | int | i = 1000 | ) |
Set the high watermark (ZMQ receive queue bound).
| i | The queue size. A value of 1 selects Last-Known-Value (LKV) delivery for backward compatibility (equivalent to set_delivery(Delivery::LastKnownValue)); prefer set_delivery() for clarity. A value of 0 means "unlimited" (ZMQ semantics) and Queued delivery. |
| void Mads::Agent::set_key_dir | ( | const std::filesystem::path & | path | ) |
Sets the path to the etc directory.
| path |
|
inline |
Set the publish endpoint URL.
| endpoint | in the form tcp://hostname:port |
Definition at line 727 of file agent.hpp.
References _pub_endpoint.
| void Mads::Agent::set_pub_topic | ( | std::string | topic | ) |
Sets the publish topic.
| topic | The topic to be used for publishing messages. |
| void Mads::Agent::set_receive_timeout | ( | int | to | ) |
Sets the value of timeout in receiving messages. Set to 0 for no timeout.
| to | the timeout in ms. |
| AgentError | if already initialized |
| void Mads::Agent::set_receive_timeout | ( | std::chrono::milliseconds | to | ) |
Sets the value of timeout in receiving messages. Set to 0 for no timeout.
| to | the timeout. |
| AgentError | if already initialized |
| void Mads::Agent::set_runtime | ( | std::shared_ptr< Mads::Runtime > | runtime | ) |
Attach the agent to a different Runtime.
Each agent owns its own Runtime by default; attach several agents to a shared Runtime (e.g. another agent's runtime()) to stop them together without affecting the rest of the process.
| runtime | The Runtime to attach to. |
| AgentError | if runtime is null or the agent is connected. |
| void Mads::Agent::set_settings_timeout | ( | int | to | ) |
Sets the value of timeout in loading settings from URI. Set to for no timeout.
| to | the timeout in ms. |
| AgentError | if already initialized |
| void Mads::Agent::set_settings_timeout | ( | std::chrono::milliseconds | to | ) |
Sets the value of timeout in loading settings from URI. Set to for no timeout.
| to | the timeout in milliseconds. |
| AgentError | if already initialized |
|
inline |
Set the subscribe endpoint URL.
| endpoint | in the form tcp://hostname:port |
Definition at line 713 of file agent.hpp.
References _sub_endpoint.
|
inline |
Sets the subscribe topics.
| topics | The topics to be used for subscribing to messages. |
Definition at line 755 of file agent.hpp.
References _sub_topic.
| void Mads::Agent::set_wire_format | ( | WireFormat | fmt | ) |
Select the on-the-wire payload encoding used when publishing.
Default is WireFormat::Json (legacy, header-less, fully backward compatible). WireFormat::MsgPack emits a self-describing frame header and MessagePack-encoded payloads. Receivers accept both transparently.
| fmt | The wire format to use for outgoing messages. |
| bool Mads::Agent::settings_are_local | ( | ) | const |
Detects if settings are local or loaded from URI.
| int Mads::Agent::settings_timeout | ( | ) |
Returns the value of timeout in loading settings from URI.
| std::string Mads::Agent::settings_uri | ( | ) |
| void Mads::Agent::setup_crypto | ( | Mads::auth_verbose | verbose = auth_verbose::off | ) |
Set the use of CURVE encryption and enables authentication.
| verbose |
| void Mads::Agent::shutdown | ( | ) |
Performs a coordinated shutdown of the agent.
This ensures all background threads (drain, remote control) are properly joined before closing sockets and terminating the ZMQ context. Called automatically by the destructor.
|
staticprotected |
| std::map< std::string, std::string > Mads::Agent::status | ( | ) |
Returns the status of the system.
|
inline |
Get the subscribe endpoint URL.
Definition at line 720 of file agent.hpp.
References _sub_endpoint.
|
inline |
Gets the subscribe topics.
Definition at line 762 of file agent.hpp.
References _sub_topic.
| bool Mads::Agent::wait_for_connection | ( | std::chrono::milliseconds | timeout | ) |
Blocks until the publisher socket's connection is confirmed by a real ZMQ_EVENT_CONNECTED/ZMQ_EVENT_HANDSHAKE_SUCCEEDED event, or timeout elapses (ZMQ_DEVELOPMENT.md §2.1).
| timeout | upper bound on how long to wait. |
| WireFormat Mads::Agent::wire_format | ( | ) | const |
The wire format used for outgoing messages.
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
mutableprotected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
mutableprotected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
Definition at line 1289 of file agent.hpp.
Referenced by Mads::Dummy::load_settings().
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
mutableprotected |
|
protected |
Definition at line 1286 of file agent.hpp.
Referenced by Mads::Dummy::load_settings().
|
protected |
|
protected |
Definition at line 1290 of file agent.hpp.
Referenced by pub_endpoint(), and set_pub_endpoint().
|
protected |
|
protected |
Definition at line 1291 of file agent.hpp.
Referenced by pub_topic(), and Mads::Bridge::route().
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
Definition at line 1319 of file agent.hpp.
Referenced by keep_running(), and runtime().
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
Definition at line 1323 of file agent.hpp.
Referenced by keep_running().
|
protected |
Definition at line 1290 of file agent.hpp.
Referenced by set_sub_endpoint(), and sub_endpoint().
|
protected |
|
protected |
Definition at line 1293 of file agent.hpp.
Referenced by set_sub_topic(), and sub_topic().
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
| Mads::auth_verbose Mads::Agent::auth_verbose = auth_verbose::off |
| std::string Mads::Agent::client_key_name = "client" |
| bool Mads::Agent::dummy = false |
| std::string Mads::Agent::server_key_name = "broker" |
| double Mads::Agent::timecode_fps = MADS_FPS |