Good question. You don't (but there's a catch).
Now I'm going technical, so brace yourself.
In order to do what you're asking you need some stuff that is not doable with ns-3 out-of-the-box. But it can be done.
First and foremost you must decide how to communicate with the ns-3 simulation. An ns-3 simulation can be either a program (if made entirely with C++) or a script in Python. The two alternatives are slightly different with what I'll say in the following, with the Python alternative being slightly simpler to implement.
Let's start with the beginning. The classes responsible for scheduling events and advancing time are the child classes of SimulatorImpl (there are many actually). As they are, they do consume events one after the other, without checking anything.
What you need is a new SimulatorImpl class (or a child of an existing one) that can:
- Receive a signal, or something, telling it to advance "until time x is reached".
- Receive a signal, or something, telling it to continue the execution "until time x is reached".
Note: basically the two are the same. But you can also have a variation like "execute exactly one event", and "execute one event and all the events with the same time", to advance the execution by one event and one "time step".
Just as a reference, check the following function:
oid
DefaultSimulatorImpl::Run()
{
NS_LOG_FUNCTION(this);
// Set the current threadId as the main threadId
m_mainThreadId = std::this_thread::get_id();
ProcessEventsWithContext();
m_stop = false;
while (!m_events->IsEmpty() && !m_stop)
{
ProcessOneEvent();
}
// If the simulator stopped naturally by lack of events, make a
// consistency test to check that we didn't lose any events along the way.
NS_ASSERT(!m_events->IsEmpty() || m_unscheduledEvents == 0);
}
Now, what happens if you set m_stop ? (it is set by calling Simulator::Stop()
Well, the simulation stops.
Can you call Simulator::Run() again? Yes, of course. The problem is calling Simulator::Stop() at the right time. Well, easy... call Simulator::Stop(const Time& delay) before the simulation starts.
This is all good if you know ahead when you want to stop the simulation (and then start again, maybe).
If you need to do it *while* the simulation runs, things gets messy, but you can do it by defining a new SimulatorImpl and adding your own methods.
Now, why did I say that Python and C++ are different? So far they aren't - but with C++ you'll need a way to signal the program (interrupts, RPCs, sockets, etc), while with Python you'll have (maybe) an easier time. Don't underestimate how you'll communicate with your script.
Said so, there's no "out of the box" way, but it's something that is definitely now dramatic to do. Just get your favourite IDE and start developing.