AntHocNet 2.6.1
Paper-faithful ant-colony ad hoc routing: the shared core and its adapters
Loading...
Searching...
No Matches
ant_router_logic.h
Go to the documentation of this file.
1// SPDX-License-Identifier: MIT OR GPL-2.0-only
2// Copyright (C) 2026 Daniel Henrique Joppi
3
4/**
5 * AntRouterLogic: the shared, simulator-agnostic AntHocNet state machine.
6 *
7 * This is the former AntNest + the decision parts of the AntHocNet agent,
8 * with all NS-2 packet/scheduler coupling removed. It owns a node's routing
9 * state (pheromone table, dedup history, sequence counter) and turns received
10 * ants and local data demands into RouteDecisions for the adapter to execute.
11 *
12 * It is deliberately free of I/O: time and randomness come through ports, and
13 * outputs are returned as values. That makes the routing behaviour identical
14 * across NS-2 and NS-3 and testable on its own.
15 */
16#ifndef ANTHOCNET_CORE_ANT_ROUTER_LOGIC_H
17#define ANTHOCNET_CORE_ANT_ROUTER_LOGIC_H
18
19#include <map>
20#include <utility>
21#include <vector>
22
32
33namespace anthocnet {
34namespace core {
35
37public:
38 /// `metric` selects the pheromone formula (item 16); nullptr uses the
39 /// canonical ClassicMetric. `linkState` supplies MAC-layer congestion
40 /// signals for the item-10/A2 metric (nullptr => wall-clock per-hop time).
41 /// Both default to null, so existing adapter call sites are unchanged.
43 const ILinkMetric* metric = nullptr, const ILinkState* linkState = nullptr,
44 const ILinkQuality* linkQuality = nullptr);
45
46 NodeAddress address() const { return address_; }
47 const Config& config() const { return config_; }
48 PheromoneTable& table() { return table_; }
49 const PheromoneTable& table() const { return table_; }
50 PheromoneEngine& engine() { return engine_; }
51
52 // --- observability (item 15) -----------------------------------------
53 /// Attach an optional observer for ant/route events (nullptr to detach).
54 /// Zero-overhead when unset; the observer only reports, never decides.
55 void setObserver(IRouterObserver* observer) { observer_ = observer; }
56 /// Attach the port that says where this node is (ADR-0025; nullptr =
57 /// unknown). Read only when Config::shareKinematics is on.
58 void setKinematics(const IKinematics* kinematics) { kinematics_ = kinematics; }
59 /// #574: the next-hop weight link-lifetime prediction gives `neighbor`
60 /// now: 1 when the gate is off or either side's kinematics are unknown.
61 double linkLifetimeWeight(NodeAddress neighbor) const;
62 /// Attach the residual-energy port (#145; nullptr = unknown). Read only
63 /// by a metric that prices hops by energy.
64 void setEnergy(const IEnergy* energy) { energy_ = energy; }
65 /// What `neighbor`'s last hello said about its position and velocity,
66 /// extrapolated to now (p + v*(now - t_rx)). False when it is unknown: no
67 /// hello from it carried a section, the last one did not, or the
68 /// neighbour was lost since (ADR-0025: absent, not zero).
69 bool neighborKinematics(NodeAddress neighbor, Kinematics& out) const;
70 /// Ants of `type` this node has put on the medium (origination + forward).
71 std::uint64_t antsSent(AntType type) const;
72 /// Non-duplicate ants of `type` this node has received and processed.
73 std::uint64_t antsReceived(AntType type) const;
74 /// Total ant control packets sent across all types (routing overhead).
75 std::uint64_t controlPacketsSent() const;
76 /// LinkFail notes this node re-broadcast on another reporter's behalf
77 /// (issue #20: origins = antsSent(LinkFail) − propagations).
78 std::uint64_t linkfailPropagations() const { return linkfailPropagations_; }
79 /// LinkFail propagations suppressed by an exhausted inherited
80 /// broadcastBudget — how often the depth bound actually bites (issue #20).
81 std::uint64_t linkfailBudgetDrops() const { return linkfailBudgetDrops_; }
82 /// Per-destination advertisements dropped by the origin cooldown
83 /// (config.linkfailNotifyInterval, issue #20).
84 std::uint64_t linkfailOriginsSuppressed() const { return linkfailOriginsSuppressed_; }
85 /// Reactive forward ants steered along the diffusion gradient instead of
86 /// being broadcast (config.enableDirectedReactive). This is the number that
87 /// says whether directed discovery is doing anything at all: zero means the
88 /// virtual table never held a hint where the regular one was empty, so the
89 /// gate is inert rather than harmful.
90 std::uint64_t directedSteers() const { return directedSteers_; }
91 /// Local repairs that expired without a backward ant and therefore released
92 /// the data buffered for their destination — the number of DiscardPending
93 /// decisions this node has emitted ([1] §3.5, D6; drop-cause breakdown,
94 /// issue #215). This is the *event* count: the adapter owns the pending
95 /// queue, so only it knows how many packets each discard released. Counted
96 /// here so the cause stays simulator-agnostic and both adapters get it.
97 std::uint64_t repairDiscards() const { return repairDiscards_; }
98 /// #572: the decay multiplier `dest` got at the last maintenance tick (1
99 /// when the gate is off or the destination is new).
101 auto it = evapMultiplier_.find(dest);
102 return it == evapMultiplier_.end() ? 1.0 : it->second;
103 }
104
105 // --- neighbour learning ----------------------------------------------
106 /// Record that `neighbor` is reachable (link-layer detection / hello),
107 /// seeding an equal-weight regular pheromone entry as the legacy code did.
109 void loseNeighbor(NodeAddress neighbor);
110
111 /// Periodic liveness/maintenance tick (driven by the adapter hello timer):
112 /// expire neighbours not heard from within helloInterval*allowedHelloLoss
113 /// (the portable, NS-3-mandatory detector — ADR-0008) and return any
114 /// link-failure notifications to broadcast. Also expires timed-out local
115 /// repairs ([1] §3.5, D6): a repair with no backward ant within its wait
116 /// window yields a DiscardPending for the destination's buffered packets
117 /// plus a LinkFail notification.
118 std::vector<RouteDecision> onMaintenanceTick();
119
120 /// Remove neighbour `n` and, for every destination whose best path it
121 /// carried, broadcast a LinkFail notification with the new best (0 if the
122 /// route is gone). Used by both the maintenance tick and an adapter's
123 /// MAC transmit-failure hook (ADR-0008 detectors A and D converge here).
124 std::vector<RouteDecision> reportNeighborLoss(NodeAddress n);
125
126 /// Adapter MAC transmit-failure hook (ADR-0008 detector D): a failed unicast
127 /// to `next` means the link is down. Prune the neighbour (emitting any
128 /// LinkFail notifications, exactly as detector A does) and, when the failed
129 /// packet was *data* (`dataDest` set), broadcast a bounded, counted local
130 /// repair ant toward it so the route is rebuilt immediately ([1] §3.5).
131 /// Mirrors the NS-2 `ahn_router` linkFailed path; both adapters call this.
132 std::vector<RouteDecision> reportTxFailure(NodeAddress next,
133 NodeAddress dataDest = kInvalidAddress);
134
135 // --- active sessions (proactive monitoring, item 04) ------------------
136 /// Record that this node just originated data for `dest`, making it an
137 /// active session that proactive ants will monitor (call from the adapter
138 /// data path when the packet is locally originated).
140 /// Destinations with data sent within `config_.sessionTtl` (const query).
141 std::vector<NodeAddress> activeDestinations() const;
142 /// One proactive forward ant per active destination (empty if none or if
143 /// `!config_.enableProactive`), subject to the thesis emission gate
144 /// `shouldSendProactive()`. Also prunes expired sessions.
145 std::vector<AntMessage> createProactiveAnts();
146 /// The thesis's emission gate, re-derived per-link (#180, ADR-0018): true
147 /// when some neighbour's virtual pheromone for `dest` beats the regular
148 /// pheromone ON THE SAME LINK by `config_.proactiveVirtualMargin`, or the
149 /// hint sits on an unsampled link (or the gate does not apply — see the
150 /// definition for the boundary cases). Exposed for tests/observability;
151 /// `createProactiveAnts()` calls it.
153
154 // --- ant construction -------------------------------------------------
156 AntMessage createHelloAnt(); // caps adverts at Config::maxHelloAdverts
157 AntMessage createHelloAnt(std::size_t maxAdverts);
158 /// Build the backward ant for a forward ant that reached this node
159 /// (this == dst). The returned message's `nextHop` is via firstBackHop().
161
162 // --- routing primitives ----------------------------------------------
163 /// Stochastic next hop for `dest` using the ant exponent betaAnts
164 /// (kInvalidAddress if no route). Serves reactive and proactive ants.
165 NodeAddress selectNextHop(NodeAddress dest, bool proactive);
166 /// Stochastic next hop for a *data* packet, using the greedier data
167 /// exponent betaData (kInvalidAddress if no route). `prevHop` is excluded
168 /// unless it is the only option, to suppress data loops (A1).
170 /// Pick a random known destination for a proactive ant (or kInvalidAddress).
172
173 /// Append this node to a forward ant's visited stack, recording this hop's
174 /// cost (congestion-aware MAC estimate when enabled, else wall-clock delta).
175 /// `nextHop` is the interface the ant leaves by, so the congestion signal
176 /// is read for the queue it will actually wait in; kInvalidAddress when
177 /// there is no single one (broadcast, or this node is the destination) —
178 /// the ILinkState then aggregates (#206). Bounded by Config::maxPathLength.
179 void stampForward(AntMessage& ant, NodeAddress nextHop) const;
180
181 /// One hop of path cost from this node to `neighbor`, in the metric's units
182 /// (#181): the SINR link cost (eq. 4.11) when the metric uses link quality,
183 /// else the unloaded reference hop time T_hop as before.
184 double linkCost(NodeAddress neighbor) const;
185
186 /// Advance a backward ant by one hop: move this node from the visited stack
187 /// onto `history` and return the next hop (path management only; the deposit
188 /// state is reconstructed at the receiver from `history`, ADR-0009).
190
191 /// Pheromone this back ant would deposit at the current node, reconstructed
192 /// from its `history` (hops + summed per-hop times) via the link metric.
193 double backAntPheromone(const AntMessage& ant) const;
194
195 /// Update the regular pheromone table from a backward ant that arrived
196 /// here (reinforce the link it came from).
198
199 // --- consolidated receive --------------------------------------------
200 /// Process a received ant arriving from `prevHop`. Performs (src,seq)
201 /// dedup and neighbour learning, then dispatches by type/direction,
202 /// returning the actions the adapter must carry out. May return an empty
203 /// vector (nothing to do) or a single Drop.
204 std::vector<RouteDecision> onReceiveAnt(const AntMessage& ant, NodeAddress prevHop);
205
206 /// Decide what to do with a locally-originated or in-transit *data* packet
207 /// destined for `dest`: route it (Unicast), or (no route) request one and
208 /// Queue it. The adapter emits the returned reactive forward ant, if any.
209 std::vector<RouteDecision> onDataPacket(NodeAddress dest,
210 NodeAddress prevHop = kInvalidAddress);
211
212private:
213 std::uint32_t nextSeq() { return seqNum_++; }
214
215 /// Turn a forward/notification ant into a Broadcast decision, honouring its
216 /// broadcastBudget: an untracked ant (-1) always broadcasts; a budgeted ant
217 /// decrements and broadcasts while >0, and is Dropped once exhausted.
218 RouteDecision broadcastForward(AntMessage& ant);
219 /// Build a Unicast/Broadcast decision carrying `ant`, counting it as sent
220 /// and notifying the observer (the single choke point for ant emission).
221 RouteDecision sendAnt(RouteAction action, NodeAddress nextHop, const AntMessage& ant);
222 /// Apply a received LinkFail notification and, if it costs this node its own
223 /// best path, return a bounded propagated notification.
224 std::vector<RouteDecision> handleLinkFail(const AntMessage& note, NodeAddress reporter);
225
226 /// Fill the transient deposit state (prevHop/hops/pathTime/pheromone) of a
227 /// received backward ant from its `history`, before reinforcement.
228 void computeBackAntState(AntMessage& ant) const;
229
230 /// This node's contribution to a forward ant's path-time estimate: the
231 /// congestion-aware MAC cost (Q_mac+1)*T̂_mac toward `nextHop` when enabled
232 /// and available, else the ant's wall-clock transit delta since the
233 /// previous stamp.
234 double localHopCost(const AntMessage& ant, NodeAddress nextHop) const;
235
236 NodeAddress address_;
237 Config config_;
238 IClock& clock_;
239 IRng& rng_;
240 PheromoneTable table_;
241 PheromoneEngine engine_;
242 ClassicMetric defaultMetric_; ///< used when no metric is injected
243 const ILinkMetric* metric_; ///< pheromone strategy (item 16)
244 const ILinkState* linkState_; ///< MAC congestion signals (item 10/A2), optional
245 const ILinkQuality* linkQuality_; ///< per-neighbour SINR (#181), optional
246 AntHistoryTracker history_;
247 GenerationTracker genQuality_; ///< multipath acceptance filter (#96)
248 std::uint32_t seqNum_ = 0;
249 std::map<NodeAddress, double> activeSessions_; ///< dest -> last data-send time
250 std::map<NodeAddress, double> lastSeen_; ///< neighbor -> last reception time
251 const IKinematics* kinematics_ = nullptr; ///< own position (ADR-0025), optional
252 const IEnergy* energy_ = nullptr; ///< own battery (#145), optional
253 /// ADR-0025: neighbour -> (kinematics its last hello carried, receive
254 /// time). Only filled when Config::shareKinematics is on; erased with the
255 /// neighbour (loseNeighbor) and by a hello that carries no section.
256 std::map<NodeAddress, std::pair<Kinematics, double>> neighborKinematics_;
257 /// #574: per-neighbour next-hop weights, refreshed before each choice
258 /// and handed to the pheromone table (only when enableLinkLifetime).
259 std::map<NodeAddress, double> linkWeights_;
260 /// shareKinematics, or implied by a consumer that needs it (#574).
261 bool sharingKinematics() const { return config_.shareKinematics || config_.enableLinkLifetime; }
262 /// Recompute linkWeights_ for the current neighbours (#574).
263 void refreshLinkWeights();
264 /// #185: smoothed hop count ĥ per (destination, next hop), the thesis's
265 /// eq. 4.2 state. Only populated when Config::hopCountAlpha > 0; entries for
266 /// a neighbour are erased with it (loseNeighbor), so it is bounded by the
267 /// same (dest, neighbour) set as the regular pheromone table.
268 std::map<std::pair<NodeAddress, NodeAddress>, double> hopEstimate_;
269 std::map<NodeAddress, double> lastReactive_; ///< dest -> last reactive-ant time
270 std::map<NodeAddress, double> lastRepair_; ///< dest -> last repair-ant time
271 std::map<NodeAddress, double> repairDeadline_; ///< dest -> repair wait expiry (D6)
272 std::map<NodeAddress, int>
273 txFailures_; ///< next hop -> consecutive MAC tx-failures (detector D debounce)
274 double lastEvaporation_ = 0.0; ///< last evaporateAll time
275
276 IRouterObserver* observer_ = nullptr; ///< optional, item 15
277 std::map<AntType, std::uint64_t> antsSent_; ///< sent counters by type
278 std::map<AntType, std::uint64_t> antsReceived_; ///< received counters by type
279 std::uint64_t linkfailPropagations_ = 0; ///< re-broadcast LinkFails (issue #20)
280 std::uint64_t linkfailBudgetDrops_ = 0; ///< budget-suppressed propagations (issue #20)
281 std::uint64_t linkfailOriginsSuppressed_ =
282 0; ///< cooldown-suppressed advertisements (issue #20)
283 std::uint64_t repairDiscards_ =
284 0; ///< expired local repairs that discarded pending data (#215)
285 std::uint64_t directedSteers_ = 0; ///< reactive ants unicast along the diffusion gradient
286 std::map<NodeAddress, double> lastLinkfailNotify_; ///< dest -> last originated LinkFail time
287
288 /// #571 quiet mode, per active destination: the route last seen and how
289 /// long it has been stable. Only used when Config::enableQuietMode.
290 struct QuietState {
291 NodeAddress best = kInvalidAddress; ///< best regular next hop last round
292 double pheromone = 0.0; ///< its pheromone last round
293 int stable = 0; ///< consecutive stable rounds
294 int skipped = 0; ///< rounds since this dest's last ant
295 };
296 std::map<NodeAddress, QuietState> quiet_;
297 /// True when quiet mode lets `dest`'s proactive ant go this round (and
298 /// advances its state). Always true with the gate off.
299 bool quietAllows(NodeAddress dest);
300 /// #572 adaptive evaporation, per destination: the best regular next hop
301 /// at the last tick and the smoothed rate (1/s) at which it changes.
302 struct ChurnState {
304 double rate = 0.0;
305 };
306 std::map<NodeAddress, ChurnState> churn_;
307 std::map<NodeAddress, double> evapMultiplier_; ///< dest -> decay multiplier
308 /// Update churn_ for a tick `dt` seconds after the last, and refill
309 /// evapMultiplier_ (only when Config::enableAdaptiveEvaporation).
310 void updateChurn(double dt);
311};
312
313} // namespace core
314} // namespace anthocnet
315
316#endif // ANTHOCNET_CORE_ANT_ROUTER_LOGIC_H
AntMessage createHelloAnt(std::size_t maxAdverts)
NodeAddress selectNextHop(NodeAddress dest, bool proactive)
Stochastic next hop for dest using the ant exponent betaAnts (kInvalidAddress if no route).
void stampForward(AntMessage &ant, NodeAddress nextHop) const
Append this node to a forward ant's visited stack, recording this hop's cost (congestion-aware MAC es...
bool neighborKinematics(NodeAddress neighbor, Kinematics &out) const
What neighbor's last hello said about its position and velocity, extrapolated to now (p + v*(now - t_...
void setObserver(IRouterObserver *observer)
Attach an optional observer for ant/route events (nullptr to detach).
AntMessage createBackAnt(const AntMessage &forward)
Build the backward ant for a forward ant that reached this node (this == dst).
std::uint64_t repairDiscards() const
Local repairs that expired without a backward ant and therefore released the data buffered for their ...
void setEnergy(const IEnergy *energy)
Attach the residual-energy port (#145; nullptr = unknown).
void reinforceFromBackAnt(const AntMessage &ant)
Update the regular pheromone table from a backward ant that arrived here (reinforce the link it came ...
std::vector< RouteDecision > reportTxFailure(NodeAddress next, NodeAddress dataDest=kInvalidAddress)
Adapter MAC transmit-failure hook (ADR-0008 detector D): a failed unicast to next means the link is d...
std::uint64_t antsSent(AntType type) const
Ants of type this node has put on the medium (origination + forward).
NodeAddress advanceBackAnt(AntMessage &ant) const
Advance a backward ant by one hop: move this node from the visited stack onto history and return the ...
std::vector< RouteDecision > reportNeighborLoss(NodeAddress n)
Remove neighbour n and, for every destination whose best path it carried, broadcast a LinkFail notifi...
double evaporationMultiplier(NodeAddress dest) const
#572: the decay multiplier dest got at the last maintenance tick (1 when the gate is off or the desti...
const PheromoneTable & table() const
bool shouldSendProactive(NodeAddress dest) const
The thesis's emission gate, re-derived per-link (#180, ADR-0018): true when some neighbour's virtual ...
double linkLifetimeWeight(NodeAddress neighbor) const
#574: the next-hop weight link-lifetime prediction gives neighbor now: 1 when the gate is off or eith...
std::uint64_t controlPacketsSent() const
Total ant control packets sent across all types (routing overhead).
std::vector< NodeAddress > activeDestinations() const
Destinations with data sent within config_.sessionTtl (const query).
AntRouterLogic(NodeAddress address, const Config &config, IClock &clock, IRng &rng, const ILinkMetric *metric=nullptr, const ILinkState *linkState=nullptr, const ILinkQuality *linkQuality=nullptr)
metric selects the pheromone formula (item 16); nullptr uses the canonical ClassicMetric.
double linkCost(NodeAddress neighbor) const
One hop of path cost from this node to neighbor, in the metric's units (#181): the SINR link cost (eq...
void learnNeighbor(NodeAddress neighbor)
Record that neighbor is reachable (link-layer detection / hello), seeding an equal-weight regular phe...
void noteDataSession(NodeAddress dest)
Record that this node just originated data for dest, making it an active session that proactive ants ...
void loseNeighbor(NodeAddress neighbor)
void setKinematics(const IKinematics *kinematics)
Attach the port that says where this node is (ADR-0025; nullptr = unknown).
std::vector< RouteDecision > onReceiveAnt(const AntMessage &ant, NodeAddress prevHop)
Process a received ant arriving from prevHop.
std::uint64_t linkfailPropagations() const
LinkFail notes this node re-broadcast on another reporter's behalf (issue #20: origins = antsSent(Lin...
std::vector< RouteDecision > onDataPacket(NodeAddress dest, NodeAddress prevHop=kInvalidAddress)
Decide what to do with a locally-originated or in-transit data packet destined for dest: route it (Un...
const Config & config() const
double backAntPheromone(const AntMessage &ant) const
Pheromone this back ant would deposit at the current node, reconstructed from its history (hops + sum...
std::vector< AntMessage > createProactiveAnts()
One proactive forward ant per active destination (empty if none or if !config_.enableProactive),...
NodeAddress randomDestination()
Pick a random known destination for a proactive ant (or kInvalidAddress).
std::uint64_t linkfailBudgetDrops() const
LinkFail propagations suppressed by an exhausted inherited broadcastBudget — how often the depth boun...
AntMessage createForwardAnt(AntType type, NodeAddress dest)
std::vector< RouteDecision > onMaintenanceTick()
Periodic liveness/maintenance tick (driven by the adapter hello timer): expire neighbours not heard f...
std::uint64_t antsReceived(AntType type) const
Non-duplicate ants of type this node has received and processed.
std::uint64_t directedSteers() const
Reactive forward ants steered along the diffusion gradient instead of being broadcast (config....
std::uint64_t linkfailOriginsSuppressed() const
Per-destination advertisements dropped by the origin cooldown (config.linkfailNotifyInterval,...
NodeAddress nextHopForData(NodeAddress dest, NodeAddress prevHop=kInvalidAddress)
Stochastic next hop for a data packet, using the greedier data exponent betaData (kInvalidAddress if ...
Multipath acceptance filter for reactive forward ants ([1] §3.1, issue #96).
Definition ant_history.h:63
Source of simulation time (seconds).
Definition ports.h:24
This node's residual energy (#145): the fraction of its initial battery left, 0..1.
Definition ports.h:116
This node's own position and velocity (#573, ADR-0025).
Definition ports.h:105
Per-neighbour link quality (#181, ADR-0024).
Definition ports.h:90
Node-local MAC-layer signals for the congestion-aware per-hop metric (item 10/A2, [1] §3....
Definition ports.h:69
Source of randomness.
Definition ports.h:33
Optional observer the core notifies of routing events (item 15).
Definition ports.h:127
constexpr NodeAddress kInvalidAddress
"No such node / no route" sentinel, mirroring the legacy use of -1.
Definition types.h:27
std::int32_t NodeAddress
Network-layer node address.
Definition types.h:21
AntType
Ant role.
Definition ant_message.h:25
AntHistoryTracker: (src, seqNum) duplicate detection.
Definition ant_history.h:24
Complete, copyable description of an ant packet.
Definition ant_message.h:48
The canonical AntHocNet metric (Eq.2): blend the path-time estimate with the hop-count estimate and i...
Definition link_metric.h:64
bool enableLinkLifetime
#574: discount next hops whose link is predicted to break soon.
Definition config.h:216
bool shareKinematics
ADR-0025 (#573): put this node's position and velocity, read from the IKinematics port,...
Definition config.h:205
Maps an observation to a pheromone (goodness; higher == better).
Definition link_metric.h:45
A node's position (m) and velocity (m/s) in the adapter's Cartesian frame (ADR-0025).
Definition types.h:32