--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sim/Simulator.cc Thu Apr 21 14:57:45 2011 +0100
@@ -0,0 +1,320 @@
+/*
+ * Copyright 2004-2006 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <dtn-config.h>
+#endif
+
+#include <oasys/tclcmd/TclCommand.h>
+
+#include "Simulator.h"
+#include "Node.h"
+#include "Topology.h"
+#include "SimLog.h"
+#include "bundling/BundleTimestamp.h"
+
+using namespace dtn;
+
+namespace dtnsim {
+
+template<>
+Simulator* oasys::Singleton<Simulator, false>::instance_ = NULL;
+
+//----------------------------------------------------------------------
+
+double Simulator::time_ = 0;
+bool Simulator::interrupted_ = false;
+double Simulator::runtill_ = -1;
+
+//----------------------------------------------------------------------
+Simulator::Simulator()
+ : Logger("Simulator", "/dtnsim"),
+ eventq_(),
+ exit_event_(NULL)
+{
+}
+
+//----------------------------------------------------------------------
+void
+Simulator::post(SimEvent* e)
+{
+ instance_->eventq_.push(e);
+}
+
+//----------------------------------------------------------------------
+void
+Simulator::exit()
+{
+ ::exit(0);
+}
+
+//----------------------------------------------------------------------
+int
+Simulator::run_node_events()
+{
+ bool done;
+ int next_timer;
+ do {
+ done = true;
+ next_timer = -1;
+
+ Topology::NodeTable::iterator iter;
+ for (iter = Topology::node_table()->begin();
+ iter != Topology::node_table()->end();
+ ++iter)
+ {
+ check_interrupt();
+
+ Node* node = iter->second;
+ node->set_active();
+
+ int next = oasys::TimerSystem::instance()->run_expired_timers();
+ if (next != -1) {
+ if (next_timer == -1) {
+ next_timer = next;
+ } else {
+ next_timer = std::min(next_timer, next);
+ }
+ }
+
+ log_debug("processing all bundle events for node %s", node->name());
+ if (node->process_one_bundle_event()) {
+ done = false;
+ while (node->process_one_bundle_event()) {
+ check_interrupt();
+ }
+ }
+ }
+ } while (!done);
+
+ return next_timer;
+}
+
+//----------------------------------------------------------------------
+void
+Simulator::log_inqueue_stats()
+{
+ Topology::NodeTable::iterator node_iter;
+ for (node_iter = Topology::node_table()->begin();
+ node_iter != Topology::node_table()->end();
+ ++node_iter)
+ {
+ Node* node = node_iter->second;
+
+ oasys::ScopeLock l(node->pending_bundles()->lock(), "log_inqueue_stats");
+ BundleList::iterator bundle_iter;
+ for (bundle_iter = node->pending_bundles()->begin();
+ bundle_iter != node->pending_bundles()->end();
+ ++bundle_iter)
+ {
+ Bundle* bundle = *bundle_iter;
+ SimLog::instance()->log_inqueue(node, bundle);
+ }
+ }
+}
+
+//----------------------------------------------------------------------
+void
+Simulator::run()
+{
+ oasys::Log* log = oasys::Log::instance();
+ log->set_prefix("--");
+
+ log_debug("Configuring all nodes");
+ Topology::NodeTable::iterator iter;
+ for (iter = Topology::node_table()->begin();
+ iter != Topology::node_table()->end();
+ ++iter)
+ {
+ iter->second->configure();
+ }
+
+ log_debug("Setting up interrupt handler");
+ signal(SIGINT, handle_interrupt);
+
+ log_debug("Starting Simulator event loop...");
+
+ while (1) {
+ check_interrupt();
+
+ int next_timer_ms = run_node_events();
+ double next_timer = (next_timer_ms == -1) ? INT_MAX :
+ time_ + (((double)next_timer_ms) / 1000);
+ double next_event = INT_MAX;
+ log->set_prefix("--");
+
+ SimEvent* e = NULL;
+ if (! eventq_.empty()) {
+ e = eventq_.top();
+ next_event = e->time();
+ }
+
+ if ((next_timer_ms == -1) && (e == NULL)) {
+ break;
+ }
+ else if (next_timer < next_event) {
+ time_ = next_timer;
+ log_debug("advancing time by %u ms to %f for next timer",
+ next_timer_ms, time_);
+ }
+ else {
+ ASSERT(e != NULL);
+ eventq_.pop();
+ time_ = e->time();
+
+ if (e->is_valid()) {
+ ASSERT(e->handler() != NULL);
+ /* Process the event */
+ log_debug("Event:%p type %s at time %f",
+ e, e->type_str(), time_);
+ e->handler()->process(e);
+ }
+ }
+
+ if ((Simulator::runtill_ != -1) &&
+ (time_ > Simulator::runtill_)) {
+ log_info("Exiting simulation. "
+ "Current time (%f) > Max time (%f)",
+ time_, Simulator::runtill_);
+ goto done;
+ }
+ }
+
+ log_info("Simulator loop done -- no pending events or timers (time is %f)",
+ time_);
+
+ if (exit_event_) {
+ run_at_event(exit_event_);
+ }
+
+done:
+ log_inqueue_stats();
+ SimLog::instance()->flush();
+}
+
+//----------------------------------------------------------------------
+void
+Simulator::pause()
+{
+ oasys::StaticStringBuffer<128> cmd;
+ cmd.appendf("puts \"Simulator paused at time %f...\"", time_);
+ oasys::TclCommandInterp::instance()->exec_command(cmd.c_str());
+
+ run_console(false);
+}
+
+//----------------------------------------------------------------------
+void
+Simulator::run_console(bool complete)
+{
+ Node* cur_active = Node::active_node();
+ interrupted_ = true;
+
+ if (complete) {
+ oasys::TclCommandInterp::instance()->command_loop("dtnsim% ");
+ } else {
+ // we can't re-enter tclreadline more than once so if it's not
+ // complete we need to use the simple command loop
+ oasys::TclCommandInterp::instance()->exec_command(
+ "simple_command_loop \"dtnsim% \"");
+ }
+
+ interrupted_ = false;
+ cur_active->set_active();
+}
+
+//----------------------------------------------------------------------
+void
+Simulator::handle_interrupt(int sig)
+{
+ (void)sig;
+
+ if (interrupted_) {
+ instance()->exit();
+ } else {
+ interrupted_ = true;
+ }
+}
+
+//----------------------------------------------------------------------
+void
+Simulator::check_interrupt()
+{
+ if (interrupted_) {
+ oasys::StaticStringBuffer<128> cmd;
+ cmd.appendf("puts \"Simulator interrupted at time %f...\"", time_);
+ oasys::TclCommandInterp::instance()->exec_command(cmd.c_str());
+ run_console(false);
+ }
+}
+
+//----------------------------------------------------------------------
+/**
+ * Override gettimeofday to return the simulator time.
+ */
+extern "C" int
+gettimeofday(struct timeval *tv, struct timezone *tz)
+{
+ (void)tz;
+ double now = Simulator::time();
+ DOUBLE_TO_TIMEVAL(now, *tv);
+
+ // Sometimes converting a double like 100.2 into an timeval
+ // results in a value like of 100.199999 so we fudge it a bit here
+ // to make the output look prettier
+ if ((tv->tv_usec % 1000) == 999) {
+ tv->tv_usec += 1;
+ }
+ return 0;
+}
+
+//----------------------------------------------------------------------
+void
+Simulator::process(SimEvent *e)
+{
+ switch (e->type()) {
+ case SIM_AT_EVENT: {
+ run_at_event((SimAtEvent*)e);
+ break;
+ }
+ default:
+ NOTREACHED;
+ }
+}
+
+//----------------------------------------------------------------------
+void
+Simulator::set_exit_event(SimAtEvent* evt)
+{
+ ASSERTF(exit_event_ == NULL, "cannot set multiple exit events");
+ exit_event_ = evt;
+}
+
+//----------------------------------------------------------------------
+void
+Simulator::run_at_event(SimAtEvent* evt)
+{
+ int err = oasys::TclCommandInterp::instance()->
+ exec_command(evt->objc_, evt->objv_);
+ if (err != 0) {
+ oasys::StringBuffer cmd;
+ cmd.appendf("puts \"ERROR in at command, pausing simulation\"");
+ oasys::TclCommandInterp::instance()->exec_command(cmd.c_str());
+ pause();
+ }
+}
+
+} // namespace dtnsim