-
Notifications
You must be signed in to change notification settings - Fork 98
Entity value types
The typed structs entity properties use instead of string, and which RouterOS spelling each one
corresponds to. Read this before you compare, parse or round-trip a value: the reason these types
exist is that RouterOS writes the same value differently depending on which transport asked.
⚠️ Alpha — ships inv4.0.0-alpha: tested and functional, but the API may still change before the final 4.0 release. See Connection types & capabilities.
All four are readonly struct, all convert implicitly to and from string, and all are declared
nullable on entities (TikDuration?) because a record carries only the fields the router sent.
Both directions are implicit — string in through Parse, string out through ToString(). What
comes back is not the text you put in:
TikDuration time = "10:00:00"; // the CLI spelling, in
TimeSpan? ts = time.Value; // 10:00:00
string back = time; // "10h" ← the API spelling, outThat is deliberate and it is the point of the type. 10:00:00 and 10h are the same duration written
by two transports, so they parse to one value and compare equal — and ToString() always writes the
compact form, which every transport accepts on write. A value read over the CLI therefore goes back to
the router in the same shape it would have had over the API.
The consequence to know: never compare these as strings, and do not expect a value to round-trip
character-for-character through the type. Compare Value (or the whole struct — == compares the
value, not the text). The same holds for rates: "1M" comes back as "1000000", and "1Gbps" as
"1000000000".
| Type | Used for | The divergence it hides |
|---|---|---|
TikDuration |
time spans and the words used in place of one |
5s (API) vs 00:00:05 (CLI) |
TikDataRate |
rates and sizes in bits per second |
1000000 (API) vs 1M (CLI), plus 1Gbps
|
TikRatePair |
the upload/download pairs on /queue/simple
|
1000000/2000000 vs 1M/2M
|
TikRouterAddress |
where the router is: host, MAC, or both | not a transport divergence — see below |
A length of time, or one of the words RouterOS uses in place of one.
| Value | API / REST / WinBox native | CLI transports |
|---|---|---|
| ten seconds | 10s |
00:00:10 |
| five minutes | 5m |
00:05:00 |
| one day | 1d |
1d00:00:00 |
| 200 ms | 200ms |
00:00:00.200 |
| 21:16:40 | 21h16m40s |
21:16:40 |
| no timeout | none |
none |
Same router, same field, same moment — the spelling depends only on who asked. A string property
hands that straight to you, which is why these are typed.
var lease = connection.LoadSingle<IpDhcpServer>(...);
if (lease.LeaseTime?.Value is TimeSpan ts) // a real duration
Console.WriteLine(ts.TotalSeconds);
else
Console.WriteLine(lease.LeaseTime?.Token); // "none", "auto", "disabled", …
string forTheRouter = lease.LeaseTime; // always the compact formA word is a state, not a failure. lease-time=none, keepalive-timeout=disabled,
enabled=auto — these are real router states, and a type that could not hold them would turn one state
into another rather than into an error. See Words, and the difference from a gap.
ToString() always writes the compact form, which every transport accepts on write. So a value read
over the CLI and written back is not silently reformatted.
A rate or size in bits per second, written four ways for one value.
| Spelling | Where you see it |
|---|---|
1000000 |
the binary API, and what ToString() writes |
1M |
the CLI transports (print as-value) |
1Mbps, 0bps, 1Gbps
|
wherever RouterOS renders a rate for display — print stats over the CLI, /interface/ethernet monitor
|
unlimited, auto
|
a word instead of a number |
The suffixes are decimal, not binary: 500k is 500 000, not 512 000 — measured by writing
limit-at=500k and reading back 500000.
The bps unit is not even consistent within one record: measured on RouterOS 7.24,
/queue/simple print stats over the CLI writes rate=0bps/0bps while the single-valued total-rate
on the same row is a bare 0.
TikDataRate? r = someEntity.MaxLimit;
long? bitsPerSecond = r?.Value; // null when the router sent a word
TikDataRateSpecial? state = r?.Special; // Unlimited / Auto / None — see belowNot every field that looks like a rate is one.
rate-limiton a PPP profile packs six values into one string anddst-limiton a firewall rule packs a count, a burst and a mode — those staystringon purpose.
The upload/download pair RouterOS uses for /queue/simple's max-limit, limit-at, burst-limit
and burst-threshold.
| API | CLI | |
|---|---|---|
max-limit |
1000000/2000000 |
1M/2M |
var q = connection.LoadSingle<QueueSimple>(connection.CreateParameter("name", "customer-1"));
TikDataRate? up = q.MaxLimit?.Upload; // each half is itself a rate,
TikDataRate? down = q.MaxLimit?.Download; // so it can hold a word too
long? bitsPerSecond = q.MaxLimit?.Upload.Value; // ... and .Value is the numberTwo things worth knowing before you write one:
-
Writing one NUMBER means upload, with download zero. Setting
max-limit=1Mreads back as1000000/0— it does not mean "the same on both sides". That assumption silently halves a configuration. A bare word is different:unlimiteddescribes the whole field, so it applies to both halves and writes back asunlimited/unlimited. -
There is no conversion to a single number. A pair holds two, and picking one for you would be a
guess. Read
UploadorDownload.
The single-valued max-limit on /queue/tree reads the same on every transport and stays a plain
long — it is the pairing that changes the spelling, not the magnitude.
TikDuration and TikDataRate hold three different things, and telling them apart matters:
Kind |
What it means | Read it from |
|---|---|---|
Value |
a real duration / rate | Value |
Special |
a word RouterOS uses instead of a value — a real router state | Special |
Unknown |
text tik4net could not read and does not recognise — a gap in the library | Token |
TikDuration lease = entry.LeaseTime ?? default;
switch (lease.Kind)
{
case TikValueKind.Value: // 3d, 00:30:00, …
Console.WriteLine(lease.Value);
break;
case TikValueKind.Special when lease.Special == TikDurationSpecial.None:
Console.WriteLine("no lease time set"); // never a string comparison
break;
case TikValueKind.Unknown:
Console.WriteLine($"tik4net could not read '{lease.Token}'");
break;
}Writing one is named rather than spelled, so a typo is a compile error:
entry.LeaseTime = TikDuration.FromSpecial(TikDurationSpecial.None); // sends "none"
queue.MaxLimit = new TikRatePair(
TikDataRate.FromSpecial(TikDataRateSpecial.Unlimited),
TikDataRate.FromSpecial(TikDataRateSpecial.Unlimited)); // "unlimited/unlimited"The words each type knows: durations take none, disabled, auto, never, immediately and
forever; rates take unlimited, auto and none.
The set of words a field accepts is really a property of that field — lease-time takes none,
ethernet bandwidth takes unlimited. But the property is a shared TikDuration? / TikDataRate?,
so the type can only know which words durations use, not which subset this particular menu takes.
Naming it TikDurationSpecial rather than something per-menu says exactly that much and no more; the
router refuses a word its field does not accept, which is the check that actually decides.
Unknown is the honest fallback: the value survives verbatim, writes back unchanged, and the entity
still loads — one property degrades instead of the whole row failing. But a token that behaves
perfectly is how a missing word stays missing, so parsing one also raises a note on the
value.token trace channel:
value.token -- TikDataRate could not read 'made-up-rate' as a value and does not recognise it as a
RouterOS word — kept verbatim and written back unchanged; if the router really uses it,
it belongs in TikSpecialWords
If you see one against a real router, that is a bug report worth filing — it means tik4net is missing a spelling the router actually uses.
Where the router is. Unlike the three above this is not about a transport divergence — it exists because a router is reached by IP or by MAC, the two are alternatives rather than a pair, and both are strings so an overload cannot tell them apart.
TikRouterAddress.FromHost("192.168.88.1") // IP transports; MAC ones discover via MNDP
TikRouterAddress.FromMac("AA:BB:CC:DD:EE:FF") // MAC transports — no IP anywhere
TikRouterAddress.FromHostAndMac("192.168.88.1", "AA:BB:CC:DD:EE:FF") // MAC transports, skipping the MNDP waitBoth together is a legitimate third case: on a MAC transport the host names the local interface to use and the MAC identifies the router, which saves up to 5 s of MNDP discovery per open.
Parse (and the implicit conversion from string) tells the two apart by shape — six hex pairs
separated by : or - is a MAC and cannot be a host name or an IPv6 address, which needs eight groups
or a ::.
Fields with a fixed set of RouterOS words are mapped to C# enums, each member carrying the router's spelling:
[TikEnum("in-interface")]
InInterface,A value the enum does not know degrades that one property rather than failing the whole entity load — RouterOS adds words between versions, and losing a row because of one unrecognised option would be worse than losing the option.
tik4net.Objects.MacAddress is the MAC in 00:00:00:00:00:00 form, with implicit conversions both
ways, used by helpers such as ExecuteWol. It is a convenience type on the
entity side, not one of the transport-divergence types above.
- Entity reference — which menus are covered
- Entity helpers — the verbs that are not CRUD
- Custom entities — using these types in your own entity
-
Upgrading from 3.x to 4.0 — these types replaced
stringon 28 properties