AntHocNet 2.6.1
Paper-faithful ant-colony ad hoc routing: the shared core and its adapters
Loading...
Searching...
No Matches
anthocnet-viz-trace.h
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-only
2// Copyright (C) 2026 Daniel Henrique Joppi
3
4/*
5 * --vizTrace (#641): a compact record of one ns-3 run that the learn site
6 * replays with its own renderer. Shared by anthocnet-compare, isl-grid and
7 * leo-walker. One JSON object per line:
8 *
9 * {"v":1,"kind":"header", ...the harness's fields..., "nodes":N,
10 * "step":s, "scale":m} first line
11 * {"t":s, "p":[[x,y,z],...], "tx":n, "rx":n, "ants":n,
12 * "drop":{"route":n,"ttl":n,"queue":n}, "paths":[{"f":[src,dst],"p":[src,...]},...]}
13 * one per step
14 * {"kind":"end","tx":n,"rx":n,"pdr":pct} the run's own PDR books
15 *
16 * p is each node's position in units of `scale` metres, rounded (omitted
17 * when the nodes have no mobility model: the isl-grid torus). tx/rx are
18 * cumulative data packets on the data port, read live from the run's
19 * FlowMonitor -- the same counters the run's PDR is computed from, so the
20 * last step and the end line agree (tools/bench/check-viz-trace.py checks it).
21 * ants counts AntHocNet control transmissions in the step. paths holds, per
22 * data flow seen so far, the strongest route: from the source, the
23 * neighbour with the most regular pheromone toward the destination, hop by
24 * hop (RoutingProtocol::StrongestNextHop, the game's rule).
25 *
26 * Off unless the flag is given; it schedules read-only sampling events and
27 * draws no random numbers, so a run's results are the same with it on.
28 */
29#ifndef ANTHOCNET_VIZ_TRACE_H
30#define ANTHOCNET_VIZ_TRACE_H
31
32#include "ns3/anthocnet-routing-protocol.h"
33#include "ns3/flow-monitor-module.h"
34#include "ns3/ipv4-flow-classifier.h"
35#include "ns3/ipv4.h"
36#include "ns3/mobility-model.h"
37#include "ns3/node-container.h"
38#include "ns3/simulator.h"
39
40#include <cmath>
41#include <cstdint>
42#include <fstream>
43#include <map>
44#include <set>
45#include <string>
46#include <utility>
47#include <vector>
48
49namespace ns3 {
50namespace vizt {
51
52class VizTrace {
53public:
54 bool Active() const { return m_out.is_open(); }
55
56 /// Start recording. `header` is the harness's JSON fields without braces
57 /// (e.g. "\"harness\":\"anthocnet-compare\",\"cell\":\"paper\""). Call
58 /// after the routing protocols and applications are installed and before
59 /// Simulator::Run(); one recorded run per process.
60 void Start(const std::string& path, const std::string& header, const NodeContainer& nodes,
61 Ptr<FlowMonitor> monitor, Ptr<Ipv4FlowClassifier> classifier, uint16_t dataPort,
62 double step, double stop, double scale) {
63 m_out.open(path);
64 if (!m_out) return;
65 m_nodes = nodes;
66 m_monitor = monitor;
67 m_classifier = classifier;
68 m_port = dataPort;
69 m_step = step;
70 m_stop = stop;
71 m_scale = scale;
72 for (uint32_t i = 0; i < nodes.GetN(); ++i) {
73 Ptr<Ipv4> ip = nodes.Get(i)->GetObject<Ipv4>();
74 for (uint32_t k = 0; ip && k < ip->GetNInterfaces(); ++k) {
75 for (uint32_t a = 0; a < ip->GetNAddresses(k); ++a) {
76 const Ipv4Address addr = ip->GetAddress(k, a).GetLocal();
77 if (addr != Ipv4Address::GetLoopback()) m_index[addr.Get()] = i;
78 }
79 }
80 Ptr<anthocnet::RoutingProtocol> rp =
81 ip ? DynamicCast<anthocnet::RoutingProtocol>(ip->GetRoutingProtocol()) : nullptr;
82 m_rp.push_back(rp);
83 if (rp) rp->TraceConnectWithoutContext("Tx", MakeCallback(&VizTrace::AntTx, this));
84 }
85 m_out << "{\"v\":1,\"kind\":\"header\"," << header << ",\"nodes\":" << nodes.GetN()
86 << ",\"step\":" << step << ",\"time\":" << stop << ",\"scale\":" << scale << "}\n";
87 Simulator::Schedule(Seconds(step), &VizTrace::Sample, this);
88 }
89
90 /// The run's own books, after the harness computed its PDR.
91 void Finish(uint64_t tx, uint64_t rx, double pdr) {
92 if (!Active()) return;
93 // The sample due at the stop time never fires (Simulator::Stop runs
94 // first), so the last scheduled step is one interval short and misses
95 // that interval's deliveries. Write the closing step from the final
96 // counters, so the last step and the end books agree.
97 if (m_lastT < m_stop - 1e-9) WriteStep(m_stop);
98 m_out << "{\"kind\":\"end\",\"tx\":" << tx << ",\"rx\":" << rx << ",\"pdr\":" << pdr
99 << "}\n";
100 m_out.close();
101 m_done = true;
102 }
103
104 bool Done() const { return m_done; }
105
106private:
107 void AntTx(uint8_t, uint8_t, bool) { ++m_ants; }
108
109 int IndexOf(Ipv4Address a) const {
110 auto it = m_index.find(a.Get());
111 return it == m_index.end() ? -1 : static_cast<int>(it->second);
112 }
113
114 void Sample() {
115 const double now = Simulator::Now().GetSeconds();
116 WriteStep(now);
117 if (now + m_step <= m_stop + 1e-9)
118 Simulator::Schedule(Seconds(m_step), &VizTrace::Sample, this);
119 }
120
121 void WriteStep(double now) {
122 m_lastT = now;
123 m_out << "{\"t\":" << now;
124 // Positions, if the nodes move at all.
125 bool any = false;
126 for (uint32_t i = 0; i < m_nodes.GetN(); ++i) {
127 if (m_nodes.Get(i)->GetObject<MobilityModel>()) any = true;
128 }
129 if (any) {
130 m_out << ",\"p\":[";
131 for (uint32_t i = 0; i < m_nodes.GetN(); ++i) {
132 Ptr<MobilityModel> m = m_nodes.Get(i)->GetObject<MobilityModel>();
133 const Vector v = m ? m->GetPosition() : Vector();
134 m_out << (i ? "," : "") << '[' << std::lround(v.x / m_scale) << ','
135 << std::lround(v.y / m_scale) << ',' << std::lround(v.z / m_scale) << ']';
136 }
137 m_out << ']';
138 }
139 // Data books and drop causes, from the live FlowMonitor counters.
140 uint64_t tx = 0, rx = 0, dRoute = 0, dTtl = 0, dQueue = 0;
141 std::vector<std::pair<std::pair<int, int>, Ipv4Address>> flows;
142 for (const auto& kv : m_monitor->GetFlowStats()) {
143 const Ipv4FlowClassifier::FiveTuple t = m_classifier->FindFlow(kv.first);
144 if (t.destinationPort != m_port) continue;
145 tx += kv.second.txPackets;
146 rx += kv.second.rxPackets;
147 const std::vector<uint32_t>& pd = kv.second.packetsDropped;
148 auto reason = [&pd](std::size_t i) -> uint64_t { return i < pd.size() ? pd[i] : 0u; };
149 dRoute +=
150 reason(Ipv4FlowProbe::DROP_NO_ROUTE) + reason(Ipv4FlowProbe::DROP_ROUTE_ERROR);
151 dTtl += reason(Ipv4FlowProbe::DROP_TTL_EXPIRE);
152 dQueue += reason(Ipv4FlowProbe::DROP_QUEUE) + reason(Ipv4FlowProbe::DROP_QUEUE_DISC);
153 const int s = IndexOf(t.sourceAddress), d = IndexOf(t.destinationAddress);
154 if (s >= 0 && d >= 0) flows.push_back({{s, d}, t.destinationAddress});
155 }
156 m_out << ",\"tx\":" << tx << ",\"rx\":" << rx << ",\"ants\":" << m_ants
157 << ",\"drop\":{\"route\":" << dRoute << ",\"ttl\":" << dTtl << ",\"queue\":" << dQueue
158 << '}';
159 m_ants = 0;
160 // Strongest routes, one per (source, destination) pair.
161 std::set<std::pair<int, int>> seen;
162 m_out << ",\"paths\":[";
163 bool first = true;
164 for (const auto& fl : flows) {
165 const std::pair<int, int>& f = fl.first;
166 if (!seen.insert(f).second) continue;
167 m_out << (first ? "" : ",") << "{\"f\":[" << f.first << ',' << f.second << "],\"p\":["
168 << f.first;
169 first = false;
170 int cur = f.first;
171 std::set<int> visited{cur};
172 // The address data packets are routed on: the core's destination key.
173 const Ipv4Address dest = fl.second;
174 for (int hop = 0; hop < 64 && cur != f.second; ++hop) {
175 Ptr<anthocnet::RoutingProtocol> rp = m_rp[cur];
176 const int nb = rp ? IndexOf(rp->StrongestNextHop(dest)) : -1;
177 if (nb < 0 || !visited.insert(nb).second) break;
178 m_out << ',' << nb;
179 cur = nb;
180 }
181 m_out << "]}";
182 }
183 m_out << "]}\n";
184 }
185
186 std::ofstream m_out;
187 NodeContainer m_nodes;
188 Ptr<FlowMonitor> m_monitor;
189 Ptr<Ipv4FlowClassifier> m_classifier;
190 uint16_t m_port = 9;
191 double m_step = 1.0, m_stop = 0.0, m_scale = 1.0;
192 std::map<uint32_t, uint32_t> m_index;
193 std::vector<Ptr<anthocnet::RoutingProtocol>> m_rp;
194 uint64_t m_ants = 0;
195 double m_lastT = 0.0;
196 bool m_done = false;
197};
198
199} // namespace vizt
200} // namespace ns3
201
202#endif // ANTHOCNET_VIZ_TRACE_H
void Start(const std::string &path, const std::string &header, const NodeContainer &nodes, Ptr< FlowMonitor > monitor, Ptr< Ipv4FlowClassifier > classifier, uint16_t dataPort, double step, double stop, double scale)
Start recording.
void Finish(uint64_t tx, uint64_t rx, double pdr)
The run's own books, after the harness computed its PDR.