19#if defined _WIN32 && !defined NOMINMAX
24#include <nlohmann/json.hpp>
31#pragma GCC diagnostic push
32#pragma GCC diagnostic ignored "-Wunknown-attributes"
34#include <toml++/toml.hpp>
36#pragma GCC diagnostic pop
45#include <zmq_addon.hpp>
47#include <condition_variable>
59#define HOST_NAME_MAX 255
62#ifndef MADS_AGENT_NO_INFO
71 std::condition_variable
cv;
92 p._text = std::move(
text);
97 p._doc = std::move(
doc);
101 const std::string &
text()
const {
103 _text = _doc ? _doc->dump() : std::string();
107 const nlohmann::json &
doc()
const {
109 if (_text && !_text->empty())
110 _doc = nlohmann::json::parse(*_text);
112 _doc = nlohmann::json();
118 mutable std::optional<std::string> _text;
119 mutable std::optional<nlohmann::json> _doc;
157std::unique_ptr<Agent>
start_agent(std::string name, std::string settings_uri,
158 std::map<std::string, std::string> crypto_settings = {});
210 void setup_curve_on(zmq::socket_t &socket);
230 std::tuple<std::string, std::string, double>
231 query_broker(std::string uri, std::string
name,
232 int timeout = DEFAULT_SETTINGS_TIMEOUT_MS);
271 void init(std::string
name, std::string
settings_uri,
bool crypto =
false, std::filesystem::path
const &
key_dir =
"",
bool install_watchdog =
true);
283 void init(
bool crypto =
false,
bool install_watchdog =
true);
353#ifndef MADS_AGENT_NO_INFO
363 virtual void info(std::ostream &out = std::cout);
394 void connect(std::chrono::milliseconds delay = std::chrono::milliseconds(250));
492 const nlohmann::json &
info = nlohmann::json(),
493 const std::string &info_name =
"info");
502 void publish(nlohmann::json payload, std::string topic =
"");
514 nlohmann::json meta = nlohmann::json{{
"format",
"raw"}},
515 std::string topic =
"");
526 void publish(
const std::vector<unsigned char> &payload,
527 nlohmann::json meta = nlohmann::json{{
"format",
"raw"}},
528 std::string topic =
"");
541 message_type
receive(
bool dont_block =
false);
570 bool dont_block =
false);
588 const std::vector<std::string> &parts);
626 using loop_fun_t = std::function<std::chrono::nanoseconds()>;
628 std::chrono::nanoseconds duration);
664 std::chrono::nanoseconds spin_margin =
665 std::chrono::microseconds(200));
770 std::map<std::string, std::string>
status();
817 std::tuple<std::string, std::string, std::vector<unsigned char>>
last_blob();
830 std::tuple<std::string_view, std::string_view,
831 std::span<const unsigned char>>
1010 [[deprecated(
"conflate is disabled; use set_delivery(Delivery::LastKnownValue)")]]
1012 [[deprecated(
"conflate is disabled; use delivery()")]]
1110 int timeout_ms = 1000);
1130 std::chrono::milliseconds window = std::chrono::milliseconds(300));
1179 void connect_pub(std::chrono::milliseconds delay = std::chrono::milliseconds(0));
1211 static std::tuple<std::string, std::string, std::string>
split_URL(
const std::string &url);
1308 std::map<std::string, std::shared_ptr<LazyPayload>>
_status;
1310 std::tuple<std::string, std::string, std::vector<unsigned char>>
_last_blob;
1319 std::shared_ptr<Mads::Runtime>
_runtime = std::make_shared<Mads::Runtime>();
1325 std::chrono::nanoseconds
_time_step = std::chrono::nanoseconds(0);
1394 std::map<std::string, std::chrono::steady_clock::time_point>
void set_wire_format(WireFormat fmt)
Select the on-the-wire payload encoding used when publishing.
bool restart()
Returns wheter a restart has been requested.
std::mutex _publish_mutex
std::thread _watchdog_thread
std::string name()
Returns the name of the agent.
std::mutex _clocksync_mtx
Mads::LinkState link_state() const
The current state of this agent's link to the broker: up or down, why (last_handshake – e....
std::map< std::string, std::chrono::steady_clock::time_point > _clocksync_last_reply
std::filesystem::path attachment_path()
Returns the path to the attachment file.
Mads::ClockOffsetResult clock_offset() const
This agent's currently adopted clock offset: the clock domain's consensus winner (Mads::ClockConsensu...
void set_compression(Compression c)
Select the payload compression policy for outgoing messages.
std::string _pub_endpoint
void enable_threaded_remote_control()
void shutdown()
Performs a coordinated shutdown of the agent.
void _announce_clock_offset(const Mads::ClockOffsetResult &r)
void set_sub_topic(std::vector< std::string > topics)
Sets the subscribe topics.
std::thread _startup_event_thread
std::string pub_endpoint() const
Get the publish endpoint URL.
std::filesystem::path _attachment_path
std::string clock_domain() const
This process's clock-domain identity (Mads::detail::clock_domain_id(): boot_id on Linux,...
std::vector< std::string > sub_topic() const
Gets the subscribe topics.
std::string get_agent_id()
Get the agent ID.
void init(bool crypto=false, bool install_watchdog=true)
Initializes the agent.
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...
bool is_crypto()
Returns whether CURVE encryption is enabled.
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,...
Mads::SocketMonitor _sub_monitor
std::string _raw_settings
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 runn...
void set_pub_endpoint(std::string endpoint)
Set the publish endpoint URL.
std::string client_key_name
std::string settings_uri()
std::atomic< size_t > _dropped_messages
std::vector< std::string > _sub_topic
bool _clock_sync_responder
void set_runtime(std::shared_ptr< Mads::Runtime > runtime)
Attach the agent to a different Runtime.
void connect(std::chrono::milliseconds delay=std::chrono::milliseconds(250))
Connects the agent to the publish and subscribe endpoints.
Mads::ClockOffsetResult _own_clock_measurement
void set_receive_timeout(std::chrono::milliseconds to)
Sets the value of timeout in receiving messages. Set to 0 for no timeout.
void set_delivery(Delivery d)
Select subscriber delivery semantics.
std::filesystem::path key_dir()
Returns the path to the etc directory.
std::tuple< std::string, nlohmann::json > last_json()
Returns the last received message as a parsed JSON object.
std::condition_variable _event_cv
std::chrono::nanoseconds _spin_margin
void save_settings(const std::string path=SETTINGS_PATH)
Save settings read from broker to file.
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.
std::unique_ptr< CurveAuth > * curve_auth()
Returns a pointer to the CurveAuth object.
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/hostn...
bool _clock_wants_sync() const
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.
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.
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) seman...
SharedLatest< zmq::multipart_t > _latest_message
void set_settings_timeout(std::chrono::milliseconds to)
Sets the value of timeout in loading settings from URI. Set to for no timeout.
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 – lite...
Mads::ClockOffsetResult _stamp_clock_result(Mads::ClockOffsetResult r, Mads::ClockSource source, uint8_t hops)
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 with...
void set_pub_topic(std::string topic)
Sets the publish topic.
void enable_remote_control(bool threaded=false)
Enables remote control for the agent.
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_H...
virtual void info(std::ostream &out=std::cout)
Prints information about the agent.
void set_cross(bool cross)
Sets the cross flag.
size_t dropped_messages() const
Number of messages dropped because they were malformed or could not be decoded (bad part count,...
std::function< std::chrono::nanoseconds()> loop_fun_t
Enters the main loop of the agent. It also sets a signal handler for SIGNINT, which will set the runn...
void remote_control(std::string payload_str)
Handles remote control commands.
int receive_timeout()
Returns the value of timeout in receiving messages.
Mads::SocketMonitor _pub_monitor
std::vector< Mads::ClockPeerObservation > _clocksync_pongs
std::string _settings_uri
std::atomic< bool > _watchdog_stop
std::atomic< bool > _io_reads_subscriber
void set_conflate(bool conflate)
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.
nlohmann::json _settings_json
void _handle_clocksync_message(const nlohmann::json &msg)
static void install_signal_handlers()
Install SIGINT/SIGTERM handlers that request a clean shutdown.
std::map< std::string, std::string > status()
Returns the status of the system.
uint64_t _clocksync_probe_seq
void set_high_watermark(int i=1000)
Set the high watermark (ZMQ receive queue bound).
zmq::socket_t _subscriber
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.
bool is_connected()
Detects if agent is connected.
void loop(loop_fun_t const &lambda, std::chrono::nanoseconds duration)
std::shared_ptr< Mads::Runtime > _runtime
std::tuple< std::string, std::shared_ptr< LazyPayload > > _last_message
void set_agent_id(std::string id)
Set the agent ID field.
std::string sub_endpoint() const
Get the subscribe endpoint URL.
void _configure_clock_sync()
Parses the [agents]/[<name>] clock_* settings during init() and appends CLOCKSYNC_TOPIC to _sub_topic...
message_type receive(bool dont_block=false)
Receives a message from the subscribe socket.
Mads::ClockSource _clock_source
void _run_peer_measurement()
std::mutex _message_state_mutex
std::chrono::nanoseconds _time_step
void _start_clock_thread()
Brings up _clock_thread if it is not already running: announces this agent's adopted clock offset on ...
bool high_res_loop() const
Returns whether high-resolution loop pacing is enabled.
std::filesystem::path _key_dir
nlohmann::json get_settings()
Get all settings as JSON.
bool running() const
True while this agent's loops should keep going.
static std::tuple< std::string, std::string, std::string > split_URL(const std::string &url)
int settings_timeout()
Returns the value of timeout in loading settings from URI.
bool settings_are_local() const
Detects if settings are local or loaded from URI.
void fetch_settings(bool crypto=false)
Acquires settings (and any broker-served attachment) without binding the agent to a settings section.
void _start_io_thread()
Brings up _io_thread if it is not already running. Called from whichever of connect_pub()/connect_sub...
std::string pub_topic() const
Gets the publish topic.
bool keep_running() const
True while this agent's loops should keep going.
void install_loop_watchdog(uint8_t max_count=3)
Install a watch thread to ensure exit from loops.
void set_sub_endpoint(std::string endpoint)
Set the subscribe endpoint URL.
void set_settings_timeout(int to)
Sets the value of timeout in loading settings from URI. Set to for no timeout.
std::tuple< std::string, std::string, std::vector< unsigned char > > last_blob()
Returns the last received blob by the agent.
std::shared_ptr< Mads::Runtime > runtime() const
The Runtime that owns this agent's run state.
Delivery delivery() const
Current delivery semantics.
void _apply_socket_options()
Resolves the plain libzmq transport-tuning knobs (ZMQ_DEVELOPMENT.md §1.4: TCP keepalive,...
std::map< std::string, std::shared_ptr< LazyPayload > > _status
std::atomic< bool > _stopping
void setup_crypto(Mads::auth_verbose verbose=auth_verbose::off)
Set the use of CURVE encryption and enables authentication.
std::string server_key_name
void set_key_dir(const std::filesystem::path &path)
Sets the path to the etc directory.
Compression compression() const
The compression policy used for outgoing messages.
std::tuple< std::string, std::string > last_message()
Returns the last received message by the agent.
void connect_pub(std::chrono::milliseconds delay=std::chrono::milliseconds(0))
Connects the agent to the publish endpoint.
void publish(nlohmann::json payload, std::string topic="")
Publishes a message with the given JSON payload.
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.
WireFormat wire_format() const
The wire format used for outgoing messages.
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).
std::thread _clock_thread
std::vector< std::string > _wildcard_sub_topic
virtual void load_settings()
Additional settings to be loaded. Virtual function to be implemented by the derived class.
void set_receive_timeout(int to)
Sets the value of timeout in receiving messages. Set to 0 for no timeout.
void connect_sub()
Connects the agent to the subscribe endpoint and subscribes to the topics.
Mads::ClockConsensus _clock_consensus
std::string _clock_agent_identity() const
std::unique_ptr< CurveAuth > _curve_auth
std::string _sub_endpoint
std::chrono::steady_clock::time_point _clocksync_probe_sent_at
std::tuple< std::string, std::string, std::vector< unsigned char > > _last_blob
std::string last_topic()
Returns the topic of the last received message by the agent.
void disconnect()
Disconnects the agent from the publish and subscribe endpoints.
Per-clock-domain adoption of the smallest-delay measurement heard from any agent sharing that domain ...
A received message payload that lazily holds either its JSON text or its parsed nlohmann::json form,...
static LazyPayload from_doc(nlohmann::json doc)
const nlohmann::json & doc() const
Parsed object form (parses the cached text once if only text exists).
const std::string & text() const
JSON text form (dumps the cached object once if only the object exists).
static LazyPayload from_text(std::string text)
One monitor per monitored socket. start() must be called before the socket's connect()/bind(): libzmq...
ClockSource
Which mechanism produced a ClockOffsetResult.
std::unique_ptr< Agent > start_agent(std::string name, std::string settings_uri, std::map< std::string, std::string > crypto_settings={})
Quick Agent initialization function.
Result of a clock-offset measurement: offset_us added to a clock in the measured domain yields broker...
One responder's answer to Agent::broadcast_clock_probe()'s ping (clock-offset source B,...
std::string responder_domain
std::string responder_hostname
ClockOffsetResult responder_adopted
std::string responder_name
std::string responder_agent_id
One four-timestamp exchange, all fields microseconds since the Unix epoch: t1/t4 read from the initia...
std::condition_variable cv