Skip to content

Monitors and streaming

Daniel Frantík edited this page Oct 27, 2026 · 8 revisions

Monitors and streaming

Commands that do not simply answer once: ping, torch, monitor-traffic, /listen. Pick the shape by how long you want the answer to run.

You want Use Transports
the current values, once a snapshot helper all
the rows a command produces in a bounded time ExecuteListWithDuration / ExecuteListUntilDone Api, ApiSsl; ExecuteListUntilDone on all
rows as they come, until you stop ExecuteWithCallback / LoadWithCallback all
a notification whenever a table changes LoadListenWithCallback all

One reading

The helpers return one snapshot and finish — no cancelling, no threads:

using tik4net.Objects;
using tik4net.Objects.Interface;
using tik4net.Objects.Tool;

InterfaceMonitorTraffic traffic = connection.GetInterfaceMonitorTrafficSnapshot("ether1");
Console.WriteLine($"{traffic.RxBitsPerSecond} / {traffic.TxBitsPerSecond}");

foreach (ToolPing reply in connection.Ping("10.0.0.1", 4))
    Console.WriteLine($"{reply.Host}  {reply.Time}");

More of them — Traceroute, GetEthernetMonitorSnapshot, RomonPing, … — are on Entity helpers.

Bounded reads

Run a command, collect its rows for a while, and get them back as a list. ExecuteListWithDuration is binary API only (Streaming capability); ExecuteListUntilDone runs on every transport.

Method Ends when Use for
ExecuteListWithDuration(duration) the duration is spent, or the command ends first; then sends /cancel commands that never end by themselves (torch)
ExecuteListUntilDone(timeout) the router says !done; the timeout is a safety net commands that end by themselves (ping count=5)
LoadWithDuration<T>(duration) as ExecuteListWithDuration, typed
var torch = connection.CreateCommand("/tool/torch",
    connection.CreateParameter("interface", "ether1"));

var rows = torch.ExecuteListWithDuration(TimeSpan.FromSeconds(10), out bool wasAborted, out string? reason);
if (wasAborted)
    Console.WriteLine("stopped early: " + reason);   // the router's message, or why the connection was lost
  • The overload without out parameters throws TikCommandAbortException instead. Its InnerException is the cause: the router's error as the specific trap type (TikNoSuchItemException, TikNoSuchCommandException, … or TikCommandTrapException), or a TikCommandFatalException for a lost connection.
  • ExecuteListUntilDone throws the router's error directly, as the specific trap type, and a lost connection as TikCommandFatalException; only its timeout is a TikCommandAbortException.
  • Off the binary API ExecuteListUntilDone is the transport's read of the command, and its timeout ends a read still running with TikCommandAbortException. On a CLI connection the cancel follows its CancellationMode: under Cooperative (the default) the router's command is let run out first and the exception comes then, under AbandonAndClose it comes at the deadline and the connection is closed.

On net8.0 the same two reads exist as IAsyncEnumerable — rows arrive as the router sends them. ExecuteListUntilDoneAsync runs on every transport with AsyncCommands: off the binary API it is the awaited list, its rows yielded once the read has ended (not streamed); ExecuteListWithDurationAsync needs Streaming.

using tik4net;   // TikCommandStreamingExtensions

await foreach (var row in torch.ExecuteListWithDurationAsync(TimeSpan.FromSeconds(10), cancellationToken))
    Console.WriteLine(row.GetResponseField("tx"));
  • A router error or a lost connection ends the enumeration with TikCommandAbortException, after the rows already delivered, so a truncated read is never a complete one. Its InnerException is the cause, as above.

Running monitors

Start a command, get each row in a callback, stop it when you are done. The callbacks run on a background thread — marshal to the UI thread yourself.

using tik4net.Objects;
using tik4net.Objects.Tool;

ITikCommand torch = connection.LoadWithCallback<ToolTorch>(
    item  => Console.WriteLine(item),
    error => Console.WriteLine("ERROR: " + error),
    connection.CreateParameter("interface", "ether1"),
    connection.CreateParameter("port", "any"));

Console.ReadLine();
torch.CancelAndJoin();
  • LoadWithCallback<T> gives typed entities; command.ExecuteWithCallback(row => …) gives raw rows.

  • The error callback receives the router's error as the specific trap type (TikNoSuchItemException, TikNoSuchCommandException, … or TikCommandTrapException), as every other read throws it.

  • To be told when a load has ended, pass an onDone callback. It runs once when the load ends without an error: after the last row, or when Cancel() or your own Close() of the connection stopped it. After an error — the router's, or a lost connection — only the error callback runs, so cleanup that must run on every ending goes in both, or after CancelAndJoin(). The same on every transport and on TikFakeConnection:

    connection.LoadWithCallback<ToolTorch>(item => Console.WriteLine(item), error => Console.WriteLine(error),
        () => Console.WriteLine("done"), connection.CreateParameter("interface", "ether1"));
  • Cancel() asks the command to stop; CancelAndJoin() also waits until the reading thread has ended.

  • CancelAndJoin(TimeSpan) bounds the wait: it returns false when the thread did not end in time, and throws TikConnectionReceiveTimeoutException when the router never answered the cancel.

  • Needs the Listen capability — every shipped transport has it.

Change notifications

LoadListenWithCallback<T> reports changes to a table — a new or changed row, and a deleted one by its .id:

using tik4net.Objects;
using tik4net.Objects.Interface;

ITikCommand listen = connection.LoadListenWithCallback<Interface>(
    onChangeCallback:    iface => Console.WriteLine($"{iface.Name} running={iface.Running}"),
    onDeletedCallback:   id    => Console.WriteLine($"{id} deleted"),
    onExceptionCallback: ex    => Console.WriteLine(ex.Message));

Console.ReadLine();
listen.CancelAndJoin();
  • Rows that already exist are not reported — only later changes.
  • Errors, including a failure of the first read, arrive in onExceptionCallback; the call itself does not throw.

Per transport

Transport How rows arrive
Api, ApiSsl pushed by the router as they happen
WinboxNative, WinboxNativeMac monitors through the router's own WinBox windows (typed values); listen by re-reading and comparing
CLI transports a continuous monitor polls a one-shot snapshot; a finite one (ping, traceroute) streams its rows; torch has its own reader. Listen re-reads and compares
Rest, RestSsl listen re-reads and compares. A monitor's rows arrive all at once when the command ends — RouterOS buffers the whole HTTP response, so give it a count or duration
  • Where listen is emulated, the table is read once before LoadListenWithCallback returns, so the call takes as long as one read of the table (a second or more through RoMON or over MAC-Telnet).
  • On a CLI connection a change you make on the same connection can go unreported — make it on a second one; see Threading and concurrency.

See also

Clone this wiki locally