|
1 /* |
|
2 * Copyright 2004-2006 Intel Corporation |
|
3 * |
|
4 * Licensed under the Apache License, Version 2.0 (the "License"); |
|
5 * you may not use this file except in compliance with the License. |
|
6 * You may obtain a copy of the License at |
|
7 * |
|
8 * http://www.apache.org/licenses/LICENSE-2.0 |
|
9 * |
|
10 * Unless required by applicable law or agreed to in writing, software |
|
11 * distributed under the License is distributed on an "AS IS" BASIS, |
|
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
13 * See the License for the specific language governing permissions and |
|
14 * limitations under the License. |
|
15 */ |
|
16 |
|
17 #ifdef HAVE_CONFIG_H |
|
18 # include <dtn-config.h> |
|
19 #endif |
|
20 |
|
21 #include <oasys/tclcmd/TclCommand.h> |
|
22 |
|
23 #include "Simulator.h" |
|
24 #include "Node.h" |
|
25 #include "Topology.h" |
|
26 #include "SimLog.h" |
|
27 #include "bundling/BundleTimestamp.h" |
|
28 |
|
29 using namespace dtn; |
|
30 |
|
31 namespace dtnsim { |
|
32 |
|
33 template<> |
|
34 Simulator* oasys::Singleton<Simulator, false>::instance_ = NULL; |
|
35 |
|
36 //---------------------------------------------------------------------- |
|
37 |
|
38 double Simulator::time_ = 0; |
|
39 bool Simulator::interrupted_ = false; |
|
40 double Simulator::runtill_ = -1; |
|
41 |
|
42 //---------------------------------------------------------------------- |
|
43 Simulator::Simulator() |
|
44 : Logger("Simulator", "/dtnsim"), |
|
45 eventq_(), |
|
46 exit_event_(NULL) |
|
47 { |
|
48 } |
|
49 |
|
50 //---------------------------------------------------------------------- |
|
51 void |
|
52 Simulator::post(SimEvent* e) |
|
53 { |
|
54 instance_->eventq_.push(e); |
|
55 } |
|
56 |
|
57 //---------------------------------------------------------------------- |
|
58 void |
|
59 Simulator::exit() |
|
60 { |
|
61 ::exit(0); |
|
62 } |
|
63 |
|
64 //---------------------------------------------------------------------- |
|
65 int |
|
66 Simulator::run_node_events() |
|
67 { |
|
68 bool done; |
|
69 int next_timer; |
|
70 do { |
|
71 done = true; |
|
72 next_timer = -1; |
|
73 |
|
74 Topology::NodeTable::iterator iter; |
|
75 for (iter = Topology::node_table()->begin(); |
|
76 iter != Topology::node_table()->end(); |
|
77 ++iter) |
|
78 { |
|
79 check_interrupt(); |
|
80 |
|
81 Node* node = iter->second; |
|
82 node->set_active(); |
|
83 |
|
84 int next = oasys::TimerSystem::instance()->run_expired_timers(); |
|
85 if (next != -1) { |
|
86 if (next_timer == -1) { |
|
87 next_timer = next; |
|
88 } else { |
|
89 next_timer = std::min(next_timer, next); |
|
90 } |
|
91 } |
|
92 |
|
93 log_debug("processing all bundle events for node %s", node->name()); |
|
94 if (node->process_one_bundle_event()) { |
|
95 done = false; |
|
96 while (node->process_one_bundle_event()) { |
|
97 check_interrupt(); |
|
98 } |
|
99 } |
|
100 } |
|
101 } while (!done); |
|
102 |
|
103 return next_timer; |
|
104 } |
|
105 |
|
106 //---------------------------------------------------------------------- |
|
107 void |
|
108 Simulator::log_inqueue_stats() |
|
109 { |
|
110 Topology::NodeTable::iterator node_iter; |
|
111 for (node_iter = Topology::node_table()->begin(); |
|
112 node_iter != Topology::node_table()->end(); |
|
113 ++node_iter) |
|
114 { |
|
115 Node* node = node_iter->second; |
|
116 |
|
117 oasys::ScopeLock l(node->pending_bundles()->lock(), "log_inqueue_stats"); |
|
118 BundleList::iterator bundle_iter; |
|
119 for (bundle_iter = node->pending_bundles()->begin(); |
|
120 bundle_iter != node->pending_bundles()->end(); |
|
121 ++bundle_iter) |
|
122 { |
|
123 Bundle* bundle = *bundle_iter; |
|
124 SimLog::instance()->log_inqueue(node, bundle); |
|
125 } |
|
126 } |
|
127 } |
|
128 |
|
129 //---------------------------------------------------------------------- |
|
130 void |
|
131 Simulator::run() |
|
132 { |
|
133 oasys::Log* log = oasys::Log::instance(); |
|
134 log->set_prefix("--"); |
|
135 |
|
136 log_debug("Configuring all nodes"); |
|
137 Topology::NodeTable::iterator iter; |
|
138 for (iter = Topology::node_table()->begin(); |
|
139 iter != Topology::node_table()->end(); |
|
140 ++iter) |
|
141 { |
|
142 iter->second->configure(); |
|
143 } |
|
144 |
|
145 log_debug("Setting up interrupt handler"); |
|
146 signal(SIGINT, handle_interrupt); |
|
147 |
|
148 log_debug("Starting Simulator event loop..."); |
|
149 |
|
150 while (1) { |
|
151 check_interrupt(); |
|
152 |
|
153 int next_timer_ms = run_node_events(); |
|
154 double next_timer = (next_timer_ms == -1) ? INT_MAX : |
|
155 time_ + (((double)next_timer_ms) / 1000); |
|
156 double next_event = INT_MAX; |
|
157 log->set_prefix("--"); |
|
158 |
|
159 SimEvent* e = NULL; |
|
160 if (! eventq_.empty()) { |
|
161 e = eventq_.top(); |
|
162 next_event = e->time(); |
|
163 } |
|
164 |
|
165 if ((next_timer_ms == -1) && (e == NULL)) { |
|
166 break; |
|
167 } |
|
168 else if (next_timer < next_event) { |
|
169 time_ = next_timer; |
|
170 log_debug("advancing time by %u ms to %f for next timer", |
|
171 next_timer_ms, time_); |
|
172 } |
|
173 else { |
|
174 ASSERT(e != NULL); |
|
175 eventq_.pop(); |
|
176 time_ = e->time(); |
|
177 |
|
178 if (e->is_valid()) { |
|
179 ASSERT(e->handler() != NULL); |
|
180 /* Process the event */ |
|
181 log_debug("Event:%p type %s at time %f", |
|
182 e, e->type_str(), time_); |
|
183 e->handler()->process(e); |
|
184 } |
|
185 } |
|
186 |
|
187 if ((Simulator::runtill_ != -1) && |
|
188 (time_ > Simulator::runtill_)) { |
|
189 log_info("Exiting simulation. " |
|
190 "Current time (%f) > Max time (%f)", |
|
191 time_, Simulator::runtill_); |
|
192 goto done; |
|
193 } |
|
194 } |
|
195 |
|
196 log_info("Simulator loop done -- no pending events or timers (time is %f)", |
|
197 time_); |
|
198 |
|
199 if (exit_event_) { |
|
200 run_at_event(exit_event_); |
|
201 } |
|
202 |
|
203 done: |
|
204 log_inqueue_stats(); |
|
205 SimLog::instance()->flush(); |
|
206 } |
|
207 |
|
208 //---------------------------------------------------------------------- |
|
209 void |
|
210 Simulator::pause() |
|
211 { |
|
212 oasys::StaticStringBuffer<128> cmd; |
|
213 cmd.appendf("puts \"Simulator paused at time %f...\"", time_); |
|
214 oasys::TclCommandInterp::instance()->exec_command(cmd.c_str()); |
|
215 |
|
216 run_console(false); |
|
217 } |
|
218 |
|
219 //---------------------------------------------------------------------- |
|
220 void |
|
221 Simulator::run_console(bool complete) |
|
222 { |
|
223 Node* cur_active = Node::active_node(); |
|
224 interrupted_ = true; |
|
225 |
|
226 if (complete) { |
|
227 oasys::TclCommandInterp::instance()->command_loop("dtnsim% "); |
|
228 } else { |
|
229 // we can't re-enter tclreadline more than once so if it's not |
|
230 // complete we need to use the simple command loop |
|
231 oasys::TclCommandInterp::instance()->exec_command( |
|
232 "simple_command_loop \"dtnsim% \""); |
|
233 } |
|
234 |
|
235 interrupted_ = false; |
|
236 cur_active->set_active(); |
|
237 } |
|
238 |
|
239 //---------------------------------------------------------------------- |
|
240 void |
|
241 Simulator::handle_interrupt(int sig) |
|
242 { |
|
243 (void)sig; |
|
244 |
|
245 if (interrupted_) { |
|
246 instance()->exit(); |
|
247 } else { |
|
248 interrupted_ = true; |
|
249 } |
|
250 } |
|
251 |
|
252 //---------------------------------------------------------------------- |
|
253 void |
|
254 Simulator::check_interrupt() |
|
255 { |
|
256 if (interrupted_) { |
|
257 oasys::StaticStringBuffer<128> cmd; |
|
258 cmd.appendf("puts \"Simulator interrupted at time %f...\"", time_); |
|
259 oasys::TclCommandInterp::instance()->exec_command(cmd.c_str()); |
|
260 run_console(false); |
|
261 } |
|
262 } |
|
263 |
|
264 //---------------------------------------------------------------------- |
|
265 /** |
|
266 * Override gettimeofday to return the simulator time. |
|
267 */ |
|
268 extern "C" int |
|
269 gettimeofday(struct timeval *tv, struct timezone *tz) |
|
270 { |
|
271 (void)tz; |
|
272 double now = Simulator::time(); |
|
273 DOUBLE_TO_TIMEVAL(now, *tv); |
|
274 |
|
275 // Sometimes converting a double like 100.2 into an timeval |
|
276 // results in a value like of 100.199999 so we fudge it a bit here |
|
277 // to make the output look prettier |
|
278 if ((tv->tv_usec % 1000) == 999) { |
|
279 tv->tv_usec += 1; |
|
280 } |
|
281 return 0; |
|
282 } |
|
283 |
|
284 //---------------------------------------------------------------------- |
|
285 void |
|
286 Simulator::process(SimEvent *e) |
|
287 { |
|
288 switch (e->type()) { |
|
289 case SIM_AT_EVENT: { |
|
290 run_at_event((SimAtEvent*)e); |
|
291 break; |
|
292 } |
|
293 default: |
|
294 NOTREACHED; |
|
295 } |
|
296 } |
|
297 |
|
298 //---------------------------------------------------------------------- |
|
299 void |
|
300 Simulator::set_exit_event(SimAtEvent* evt) |
|
301 { |
|
302 ASSERTF(exit_event_ == NULL, "cannot set multiple exit events"); |
|
303 exit_event_ = evt; |
|
304 } |
|
305 |
|
306 //---------------------------------------------------------------------- |
|
307 void |
|
308 Simulator::run_at_event(SimAtEvent* evt) |
|
309 { |
|
310 int err = oasys::TclCommandInterp::instance()-> |
|
311 exec_command(evt->objc_, evt->objv_); |
|
312 if (err != 0) { |
|
313 oasys::StringBuffer cmd; |
|
314 cmd.appendf("puts \"ERROR in at command, pausing simulation\""); |
|
315 oasys::TclCommandInterp::instance()->exec_command(cmd.c_str()); |
|
316 pause(); |
|
317 } |
|
318 } |
|
319 |
|
320 } // namespace dtnsim |