Q2NS dev
ns-3 module
Loading...
Searching...
No Matches
q2ns-swap-helper.cc
Go to the documentation of this file.
1/*-----------------------------------------------------------------------------
2 * Q2NS - Quantum Network Simulator
3 * Copyright (c) 2026 quantuminternet.it
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 *---------------------------------------------------------------------------*/
9
10#include "ns3/q2ns-swap-helper.h"
11#include "ns3/q2ns-netcontroller.h"
12#include "ns3/q2ns-qnode.h"
13#include "ns3/q2ns-swap-app.h"
14
15#include "ns3/log.h"
16#include "ns3/names.h"
17#include "ns3/random-variable-stream.h"
18
19#include <algorithm>
20#include <sstream>
21
22namespace q2ns {
23
24NS_LOG_COMPONENT_DEFINE("SwapAppHelper");
25
26std::vector<SwapSessionSpec> AllAtOncePolicy::PlanSessions(const SwapTopologySpec& topo) {
27 std::vector<SwapSessionSpec> out;
28 out.reserve(topo.sessions.size() * std::max(1u, topo.rounds.rounds));
29
30 // RNG for jitter (ns-3): interpret topo.rounds.seed as a stream index
31 auto rvJitter = ns3::CreateObject<ns3::UniformRandomVariable>();
32 rvJitter->SetStream(static_cast<int64_t>(topo.rounds.seed));
33
34 if (!topo.rounds.Enabled()) {
35 out = topo.sessions;
36 return out;
37 }
38
39 // Replicate sessions across rounds, offsetting start times
40 for (uint32_t r = 0; r < topo.rounds.rounds; ++r) {
41 for (const auto& s : topo.sessions) {
42 auto copy = s;
43 const double jitter = topo.rounds.jitter_s > 0.0
44 ? rvJitter->GetValue(-topo.rounds.jitter_s, topo.rounds.jitter_s)
45 : 0.0;
46 copy.start_s = s.start_s + r * topo.rounds.period_s + jitter;
47 // Optionally remap session IDs per round to keep unique: sid = base + r
48 // Here we keep the user-provided sessionId; user should ensure uniqueness across rounds.
49 out.emplace_back(std::move(copy));
50 }
51 }
52 return out;
53}
54
60 m_portBase = base;
61 return *this;
62}
63EntanglementSwapHelper& EntanglementSwapHelper::SetPolicy(std::unique_ptr<ISwapPolicy> policy) {
64 m_policy = std::move(policy);
65 return *this;
66}
67
68
71 bool useIpv6, uint32_t ctrlPayloadBytes) {
72 NS_ASSERT_MSG(m_nc != nullptr, "EntanglementSwapHelper requires NetController");
73
75 BuildQuantum(spec);
76
77 std::unique_ptr<ISwapPolicy> localDefault;
78 ISwapPolicy* policy = m_policy.get();
79 if (!policy) {
80 localDefault = std::make_unique<AllAtOncePolicy>();
81 policy = localDefault.get();
82 }
83 auto planned = policy->PlanSessions(spec);
84
85 InstallUnifiedApps(spec, planned, net, useIpv6, ctrlPayloadBytes);
86
87 NS_LOG_INFO("SwapHelper: installation done (" << planned.size() << " sessions).");
88}
89
90
91
92uint16_t EntanglementSwapHelper::AllocatePort(uint64_t sessionId) const {
93 uint16_t p = static_cast<uint16_t>(m_portBase + (sessionId % 10000ull));
94 if (p == 0)
95 p = m_portBase + 1;
96 return p;
97}
98
100EntanglementSwapHelper::SetNodes(const std::map<std::string, ns3::Ptr<q2ns::QNode>>& nodes) {
101 m_name2node.clear();
102 for (const auto& kv : nodes) {
103 m_name2node.emplace(kv.first, kv.second);
104 }
105 return *this;
106}
107
109 NS_ASSERT_MSG(!m_name2node.empty(), "SetNodes(...) must be called before Install().");
110 m_appByNode.clear();
111 for (const auto& name : spec.nodes) {
112 auto it = m_name2node.find(name);
113 NS_ASSERT_MSG(it != m_name2node.end(), "Missing QNode for '" << name << "' in SetNodes.");
114 ns3::Ptr<q2ns::QNode> qn = it->second;
115
116 // Create and attach one SwapApp per node (QNode derives from ns3::Node, so this is fine)
117 ns3::Ptr<SwapApp> app = ns3::CreateObject<SwapApp>();
118 qn->AddApplication(app);
119 app->SetNetController(m_nc);
120 app->SetStartTime(ns3::Seconds(0.0));
121 app->SetStopTime(ns3::Seconds(1e9));
122 m_appByNode.emplace(name, app);
123 }
124}
125
126
127
129 NS_LOG_INFO("Quantum edges: " << spec.quantumEdges.size());
130 for (const auto& e : spec.quantumEdges) {
131 auto a = ns3::DynamicCast<q2ns::QNode>(m_name2node.at(e.u));
132 auto b = ns3::DynamicCast<q2ns::QNode>(m_name2node.at(e.v));
133 const double delayNs = e.delayNsPerKm * e.distanceKm;
134 auto ch = m_nc->InstallQuantumLink(a, b);
135 ch->SetAttribute(
136 "Delay", ns3::TimeValue(ns3::NanoSeconds(static_cast<uint64_t>(std::max(0.0, delayNs)))));
137 }
138}
139
140
142 const SwapTopologySpec& spec, const std::vector<SwapSessionSpec>& planned,
143 const ns3::ClassicalNetworkBuilder::NetworkHandle& net, bool useIpv6,
144 uint32_t ctrlPayloadBytes) {
145 using ns3::Seconds;
146
147 auto getV4 = [&](const std::string& name) -> ns3::Ipv4Address {
148 auto it = net.nodes.find(name);
149 if (it == net.nodes.end() || it->second.v4Addrs.empty())
150 return ns3::Ipv4Address("0.0.0.0");
151 return it->second.v4Addrs.front();
152 };
153 auto getV6 = [&](const std::string& name) -> ns3::Ipv6Address {
154 auto it = net.nodes.find(name);
155 if (it == net.nodes.end() || it->second.v6Addrs.empty())
156 return ns3::Ipv6Address::GetAny();
157 return it->second.v6Addrs.front();
158 };
159
160 for (auto& kv : m_appByNode) {
161 kv.second->SetPayloadBytes(ctrlPayloadBytes);
162 kv.second->SetUseIpv6(useIpv6);
163 }
164
165 for (const auto& sess : planned) {
166 if (sess.path.size() < 2)
167 continue;
168
169 const uint16_t port = sess.ctrlPort ? sess.ctrlPort : AllocatePort(sess.sessionId);
170 const auto& Aname = sess.path.front();
171 const auto& Bname = sess.path.back();
172
173 const auto addrA_v4 = getV4(Aname);
174 const auto addrB_v4 = getV4(Bname);
175 const auto addrA_v6 = getV6(Aname);
176 const auto addrB_v6 = getV6(Bname);
177
178 const uint32_t kRepeaters =
179 sess.path.size() >= 2 ? static_cast<uint32_t>(sess.path.size() - 2) : 0;
180
181 // Endpoints: Source (Prev)
182 {
183 auto appA = m_appByNode.at(Aname);
185 cfg.sid = sess.sessionId;
186 cfg.role = SwapApp::Role::Prev;
187 cfg.applyCorrections = false;
188 cfg.start = Seconds(sess.start_s);
189 cfg.proto = sess.protocol;
190 cfg.ctrlPort = port;
191 cfg.expectedMsgs = kRepeaters;
192 cfg.verifyFidelity = true;
193 cfg.verifyThreshold = 0.99;
194
195 // Orient the chain
196 auto nextNode = ns3::DynamicCast<q2ns::QNode>(m_name2node.at(sess.path[1]));
197 cfg.genNext = true;
198 cfg.nextPeerId = nextNode->GetId();
199
200 appA->AddSession(cfg);
201 }
202
203 // Endpoints: Sink (Next)
204 {
205 auto appB = m_appByNode.at(Bname);
207 cfg.sid = sess.sessionId;
208 cfg.role = SwapApp::Role::Next;
209 cfg.applyCorrections = true;
210 cfg.start = Seconds(sess.start_s);
211 cfg.proto = sess.protocol;
212 cfg.ctrlPort = port;
213 cfg.expectedMsgs = kRepeaters;
214 cfg.verifyFidelity = true;
215 cfg.verifyThreshold = 0.99;
216 appB->AddSession(cfg);
217 }
218
219 // Repeaters along path: give them A/B endpoint addresses (v4 or v6)
220 for (size_t i = 1; i + 1 < sess.path.size(); ++i) {
221 auto Rname = sess.path[i];
222 auto appR = m_appByNode.at(Rname);
223
224 auto prevNode = ns3::DynamicCast<q2ns::QNode>(m_name2node.at(sess.path[i - 1]));
225 auto nextNode = ns3::DynamicCast<q2ns::QNode>(m_name2node.at(sess.path[i + 1]));
226
228 cfg.sid = sess.sessionId;
229 cfg.role = SwapApp::Role::Repeater;
230 cfg.applyCorrections = false;
231 cfg.start = Seconds(sess.start_s);
232 cfg.proto = sess.protocol;
233 cfg.ctrlPort = port;
234 cfg.prevPeerId = prevNode->GetId();
235 cfg.nextPeerId = nextNode->GetId();
236 cfg.genNext = true;
237
238 if (useIpv6) {
239 cfg.prevEndAddr6 = addrA_v6;
240 cfg.nextEndAddr6 = addrB_v6;
241 } else {
242 cfg.prevEndAddr = addrA_v4;
243 cfg.nextEndAddr = addrB_v4;
244 }
245
246 appR->AddSession(cfg);
247 }
248 }
249}
250
251} // namespace q2ns
std::vector< SwapSessionSpec > PlanSessions(const SwapTopologySpec &topo) override
void BuildQuantum(const SwapTopologySpec &spec)
std::unique_ptr< ISwapPolicy > m_policy
void Install(const SwapTopologySpec &spec, const ns3::ClassicalNetworkBuilder::NetworkHandle &net, bool useIpv6, uint32_t ctrlPayloadBytes=16)
void BindAppsOnProvidedNodes(const SwapTopologySpec &spec)
std::unordered_map< std::string, ns3::Ptr< SwapApp > > m_appByNode
std::unordered_map< std::string, ns3::Ptr< q2ns::QNode > > m_name2node
EntanglementSwapHelper & SetPolicy(std::unique_ptr< ISwapPolicy > policy)
uint16_t AllocatePort(uint64_t sessionId) const
void InstallUnifiedApps(const SwapTopologySpec &spec, const std::vector< SwapSessionSpec > &planned, const ns3::ClassicalNetworkBuilder::NetworkHandle &net, bool useIpv6, uint32_t ctrlPayloadBytes)
EntanglementSwapHelper & SetNetController(NetController *nc)
EntanglementSwapHelper & SetPortBase(uint16_t base)
EntanglementSwapHelper & SetNodes(const std::map< std::string, ns3::Ptr< q2ns::QNode > > &nodes)
virtual std::vector< SwapSessionSpec > PlanSessions(const SwapTopologySpec &topo)=0
Main user-facing facade for creating and configuring a quantum network.
ns3::Ptr< QChannel > InstallQuantumLink(ns3::Ptr< QNode > a, ns3::Ptr< QNode > b)
Install a duplex quantum link between two nodes.
std::vector< std::string > nodes
std::vector< QLinkSpec > quantumEdges
std::vector< SwapSessionSpec > sessions