Skyscraper 2.0
vmconsole.cpp
Go to the documentation of this file.
1/*
2 Skyscraper 2.0 - Virtual Manager Console
3 Copyright (C)2003-2025 Ryan Thoryk
4 https://www.skyscrapersim.net
5 https://sourceforge.net/projects/skyscraper/
6 Contact - ryan@skyscrapersim.net
7
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21*/
22
23#include <thread>
24#include <iostream>
25#include <mutex>
26#include <ctime>
27#include "globals.h"
28#include "sbs.h"
29#include "vm.h"
30#include "hal.h"
31#include "sky.h"
32#include "scriptproc.h"
33#include "enginecontext.h"
34#include "profiler.h"
35#include "gui.h"
36#include "vmconsole.h"
37
38namespace Skyscraper {
39
40//Virtual Manager Console
41
42VMConsoleResult consoleresult; //console input result
43std::mutex mtx_io; //io lock mutex
44std::string prompt; //console prompt
45std::atomic<bool> shutdown; //set to true to shut down input thread
46
48{
49 while (true)
50 {
51 if (shutdown == true)
52 break;
53
54 if (mtx_io.try_lock())
55 {
56 //output console prompt
57 std::string color = GetColors("green");
58 std::string reset = GetColors("reset");
59 std::cout << color << prompt << reset;
60
61 //get keyboard input
62 std::getline(std::cin, consoleresult.textbuffer);
63
64 mtx_io.unlock();
65 }
66 else
67 {
68 //if thread can't get lock, sleep and return
69 std::this_thread::sleep_for(std::chrono::milliseconds(delay));
70 continue;
71 }
72
73 //reset states
75 consoleresult.ready = true;
76 int i = 0;
77
78 //TODO:
79 //fix thread synchronization issues
80
81 //wait for console server to be ready
82 while (consoleresult.server_ready == false)
83 {
84 i++;
85 if (i > 500) //deadlock prevention
86 break;
87 std::this_thread::sleep_for(std::chrono::milliseconds(delay));
88 }
89 i = 0;
90
91 //wait for console to complete
92 while (consoleresult.threadwait == true)
93 {
94 i++;
95 if (i > 500) //deadlock prevention
96 break;
97 std::this_thread::sleep_for(std::chrono::milliseconds(delay));
98 }
99 }
100}
101
103{
104 this->vm = vm;
105
106 //create VM console instance
107#ifdef USING_WX
108 if (vm->GetGUI()->IsConsoleVisible() == false)
109#endif
110 {
111 std::thread coninput(VMConsoleInput(), 1);
112 coninput.detach();
113 }
114
115 Report("\nWelcome to Virtual Manager\n", "cyan");
116}
117
123
124void VMConsole::Process(const std::string &text, bool echo)
125{
126 //process console input
127
128 if (consoleresult.ready == false && text.size() == 0)
129 return;
130
131 if (text.size() > 0 && echo == true)
132 {
133 buffer = text;
134 Report(buffer, "white");
135 }
136 else if (consoleresult.ready == true)
137 {
138 if (mtx_io.try_lock())
139 {
140 //in lock, copy atomic string into new buffer and unlock
142
143 if (vm->GetActiveEngine())
144 {
145 int number = vm->GetActiveEngine()->GetNumber();
146 prompt = "\n" + SBS::ToString(number) + "> ";
147 }
148 else
149 prompt = "\n> ";
150
151 mtx_io.unlock();
152 }
153 else
154 {
155 //if thread can't get lock, sleep and return
156 std::this_thread::sleep_for(std::chrono::milliseconds(1));
157 return;
158 }
159
160 if (buffer == "")
161 {
162 //reset and exit if buffer is empty
163 consoleresult.ready = false;
164 return;
165 }
168 }
169
171
172 std::string commandline = buffer;
173 int pos = commandline.find(" ", 0);
174 std::string command;
175 Ogre::StringVector params;
176 if (pos > 0)
177 {
178 //get command line
179 command = commandline.substr(0, pos);
180 SBS::TrimString(command);
181
182 //get parameters
183 SBS::SplitString(params, commandline.substr(pos), ',');
184 }
185 else
186 command = commandline;
187
188 //shutdown command
189 if (command == "shutdown")
190 {
191 if (params.size() != 1)
192 Report ("Incorrect number of parameters");
193 else
194 {
195 if (params[0] == "all")
196 vm->DeleteEngines();
197 else
198 {
199 EngineContext *engine = vm->GetEngine(SBS::ToInt(params[0]));
200
201 if (engine)
202 engine->Shutdown();
203 }
204 }
205 consoleresult.ready = false;
207 return;
208 }
209
210 //setactive command
211 if (command == "setactive")
212 {
213 if (params.size() != 1)
214 Report("Incorrect number of parameters");
215 else
216 vm->SetActiveEngine(SBS::ToInt(params[0]));
217 consoleresult.ready = false;
219 return;
220 }
221
222 //reload command
223 if (command == "reload")
224 {
225 if (params.size() == 0)
226 {
227 EngineContext *engine = vm->GetActiveEngine();
228
229 if (engine)
230 engine->Reload = true;
231 }
232 else if (params[0] == "all")
233 {
234 for (int i = 0; i < vm->GetEngineCount(); i++)
235 {
236 EngineContext *engine = vm->GetEngine(i);
237
238 if (engine)
239 engine->Reload = true;
240 }
241 }
242 else
243 {
244 EngineContext *engine = vm->GetEngine(SBS::ToInt(params[0]));
245
246 if (engine)
247 engine->Reload = true;
248 }
249 consoleresult.ready = false;
251 return;
252 }
253
254 //vmload command
255 if (command == "vmload")
256 {
257 if (params.size() != 1)
258 Report("Incorrect number of parameters");
259 else
260 vm->Load(false, params[0]);
261 consoleresult.ready = false;
263 return;
264 }
265
266 //switch command
267 if (command == "switch")
268 {
269 if (params.size() != 1)
270 Report("Incorrect number of parameters");
271 else
272 vm->SetActiveEngine(SBS::ToInt(params[0]), true);
273 consoleresult.ready = false;
275 return;
276 }
277
278 //version command
279 if (command == "version")
280 {
281 Report("Skyscraper Frontend version " + vm->version_frontend + " " + vm->version_state);
282 Report("VM version " + vm->version);
283 if (processor)
284 Report("SBS version " + processor->GetEngine()->GetSystem()->version);
285 else
286 Report("No SBS loaded");
287 consoleresult.ready = false;
289 return;
290 }
291
292 //arch command
293 if (command == "arch")
294 {
296 consoleresult.ready = false;
298 return;
299 }
300
301 //platform command
302 if (command == "platform")
303 {
305 consoleresult.ready = false;
307 return;
308 }
309
310 //uname command
311 if (command == "uname")
312 {
313 if (params.size() > 0)
314 Report("VM " + vm->version + " " + vm->version_state + " (" + vm->Architecture + " " + vm->Bits + ")");
315 else
316 Report("VM");
317 consoleresult.ready = false;
319 return;
320 }
321
322 //start command
323 if (command == "start")
324 {
325 vm->Load(false, "Ground.bld");
326 consoleresult.ready = false;
328 return;
329 }
330
331 //ps command
332 if (command == "ps")
333 {
334 int count = vm->GetEngineCount();
335 Report(SBS::ToString(count) + " engines running\n", "cyan");
336 for (int i = 0; i < count; i++)
337 {
338 EngineContext *engine = vm->GetEngine(i);
339 Real elapsed_time = 0;
340 if (vm->GetElapsedTime(i) > 0)
341 elapsed_time = Real(vm->GetElapsedTime(i) / Real(vm->time_stat));
342 unsigned long runtime = engine->GetSystem()->GetRunTime();
343 Report(SBS::ToString(i) + ":\t" + SBS::ToString(elapsed_time * 100) + "\t\t" + SBS::ToString(runtime / 1000) + "\t\t" + engine->GetFilename(), "green");
344 }
345 consoleresult.ready = false;
347 return;
348 }
349
350 //date command
351 if (command == "date")
352 {
353 //print current date and time
354 time_t timestamp = time(0);
355 struct tm datetime = *localtime(&timestamp);
356 Report(asctime(&datetime));
357
359 {
360 consoleresult.ready = false;
362 return;
363 }
364
365 //print simulator time
366 Report("Simulator time: ");
367 int year, month, day, hour, minute, second;
368 SkySystem* sky = vm->GetSkySystem();
369 if (sky->GetCaelumSystem())
370 {
371 sky->GetDate(year, month, day);
372 sky->GetTime(hour, minute, second);
373
374 std::string month_s;
375 switch (month)
376 {
377 case 1:
378 month_s = "January";
379 break;
380 case 2:
381 month_s = "February";
382 break;
383 case 3:
384 month_s = "March";
385 break;
386 case 4:
387 month_s = "April";
388 break;
389 case 5:
390 month_s = "May";
391 break;
392 case 6:
393 month_s = "June";
394 break;
395 case 7:
396 month_s = "July";
397 break;
398 case 8:
399 month_s = "August";
400 break;
401 case 9:
402 month_s = "September";
403 break;
404 case 10:
405 month_s = "October";
406 break;
407 case 11:
408 month_s = "November";
409 break;
410 case 12:
411 month_s = "December";
412 break;
413 }
414
415 std::string pm = "am";
416 int hr = hour;
417 if (hour > 12)
418 {
419 pm = "pm";
420 hr -= 12;
421 }
422 Report(month_s + " " + SBS::ToString(day) + ", " + SBS::ToString(year) + " " + SBS::ToString(hr) + ":" + SBS::ToString(minute) + ":" + SBS::ToString(second) + " " + pm);
423 }
424 else
425 Report("No sky system loaded");
426
427 consoleresult.ready = false;
429 return;
430 }
431
432 //uptime command
433 if (command == "uptime")
434 {
435 EngineContext *engine;
436
437 if (params.size() == 0)
438 engine = vm->GetActiveEngine();
439 else
440 engine = vm->GetEngine(SBS::ToInt(params[0]));
441
442 if (engine)
443 {
444 //get running time of SBS instance
445 Real uptime = engine->GetSystem()->running_time;
446
447 Report(SBS::ToString(uptime) + " seconds");
448 }
449 consoleresult.ready = false;
451 return;
452 }
453
454 //uptime command
455 if (command == "vmuptime")
456 {
457 unsigned long uptime = vm->Uptime() / 1000;
458 Report(SBS::ToString(uptime) + " seconds");
459
460 consoleresult.ready = false;
462 return;
463 }
464
465 //profile command
466 if (command == "profile")
467 {
470 if (params.size() == 1)
471 {
472 if (params[0] == "-a")
474 }
475 std::string output;
477 Report(output, "green");
478 consoleresult.ready = false;
480 return;
481 }
482
483 //vminit command
484 if (command == "vminit")
485 {
486 EngineContext* engine = vm->Initialize(false);
487 engine->LoadDefault();
488 consoleresult.ready = false;
490 return;
491 }
492
493 //boot command
494 if (command == "boot")
495 {
496 if (vm->GetEngineCount() == 0)
497 {
498 ReportError("No engine, run vminit");
499 consoleresult.ready = false;
501 return;
502 }
503
504 if (vm->GetEngineCount() == 1 && params.size() == 0)
505 {
506 vm->GetActiveEngine()->Boot();
507 consoleresult.ready = false;
509 return;
510 }
511
512 if (params.size() == 1)
513 {
514 EngineContext *engine = vm->GetEngine(SBS::ToInt(params[0]));
515 if (engine)
516 engine->Boot();
517 else
518 ReportError("Invalid engine");
519 }
520 consoleresult.ready = false;
522 return;
523 }
524
525 //help command
526 if (command == "help" || command == "?")
527 {
528 if (params.size() == 0)
529 {
530 Report("Commands:");
531 Report("shutdown engine_number|all - shuts down the specified engine");
532 Report("setactive engine_number - makes the specified engine active");
533 Report("reload [all] - reload the current engine or all engines");
534 Report("vmload filename - load building data file");
535 Report("switch engine_number - switch to the specified engine");
536 Report("version - print versions");
537 Report("arch - print machine architecture");
538 Report("platform - print platform information");
539 Report("uname - print VM name and version");
540 Report("start - start simulator with a ground scene");
541 Report("ps - show engine process list");
542 Report("date - show real and simulator date and time");
543 Report("uptime [engine_number] - show the SBS engine uptime in seconds");
544 Report("vmuptime - show uptime of VM in seconds");
545 Report("profile [-a] - shows function-level profiling statistics");
546 Report("vminit - create and initialize a simulator engine");
547 Report("boot [engine_number] - start a simulator engine");
548 Report("help - print this help guide\n");
549 Report("All other commands will be passed to the active simulator engine, if available");
550#ifdef USING_WX
551 if (vm->GetGUI()->IsConsoleVisible() == false)
552#endif
553 Report("\nPress CTRL-c to quit");
554 }
555 consoleresult.ready = false;
557 return;
558 }
559
560 if (processor)
561 {
562 processor->GetEngine()->GetSystem()->DeleteColliders = true;
563
564 //load new commands into script interpreter, and run
565 processor->LoadFromText(buffer);
566 }
567 else
568 {
569 Report("No active engine");
570 }
571
572 consoleresult.ready = false;
574 return;
575}
576
577bool VMConsole::Report(const std::string &text, const std::string &color)
578{
579 //lock mutex, write to console and unlock
580
581#ifdef USING_WX
582 if (vm->GetGUI()->IsConsoleVisible() == false)
583#else
584 if (true)
585#endif
586 {
587 if (mtx_io.try_lock())
588 {
589 vm->GetHAL()->ConsoleOut(text + "\n", color);
590 mtx_io.unlock();
591 }
592 }
593#ifdef USING_WX
594 else
595 vm->GetGUI()->WriteToConsole(text, color);
596#endif
597 return true;
598}
599
600bool VMConsole::ReportError(const std::string &text)
601{
602 Report(text, "red");
603 return false;
604}
605
606std::string GetColors(const std::string &color)
607{
608 //get colors
609 std::string mod;
610 if (color == "blue")
611 mod = "\033[1;34m";
612 else if (color == "green")
613 mod = "\033[1;32m";
614 else if (color == "yellow")
615 mod = "\033[1;33m";
616 else if (color == "red")
617 mod = "\033[1;31m";
618 else if (color == "magenta")
619 mod = "\033[1;35m";
620 else if (color == "cyan")
621 mod = "\033[1;36m";
622 else if (color == "white")
623 mod = "\033[1;37m";
624 else if (color == "black")
625 mod = "\033[1;30m";
626 else if (color == "reset")
627 mod = "\033[0m";
628
629 return mod;
630}
631
632}
static void dumpAll(std::string &output)
Definition profiler.cpp:356
static void CleanupMemory(void)
Definition profiler.h:91
unsigned long GetRunTime()
Definition sbs.cpp:3290
bool DeleteColliders
Definition sbs.h:184
Real running_time
Definition sbs.h:168
std::string version
Definition sbs.h:141
void WriteToConsole(const std::string &message, const std::string &color="white")
Definition gui.cpp:330
bool IsConsoleVisible()
Definition gui.cpp:374
void ConsoleOut(const std::string &message, const std::string &color="white")
Definition hal.cpp:985
bool LoadFromText(const std::string &text)
EngineContext * GetEngine()
void GetTime(int &hour, int &minute, int &second)
Definition sky.cpp:299
Caelum::CaelumSystem * GetCaelumSystem()
Definition sky.h:47
void GetDate(int &year, int &month, int &day)
Definition sky.cpp:314
void operator()(int delay)
Definition vmconsole.cpp:47
std::string buffer
Definition vmconsole.h:49
bool ReportError(const std::string &text)
void Process(const std::string &text="", bool echo=true)
bool Report(const std::string &text, const std::string &color="cyan")
std::string version
Definition vm.h:126
unsigned long Uptime()
Definition vm.cpp:977
GUI * GetGUI()
Definition vm.cpp:142
HAL * GetHAL()
Definition vm.cpp:128
void SetActiveEngine(int number, bool switch_engines=false, bool force=false)
Definition vm.cpp:226
int GetEngineCount(bool loading_only=false)
Definition vm.cpp:442
EngineContext * Initialize(bool clear, EngineContext *parent=0, const Vector3 &position=Vector3::ZERO, Real rotation=0.0, const Vector3 &area_min=Vector3::ZERO, const Vector3 &area_max=Vector3::ZERO)
Definition vm.cpp:727
std::string version_frontend
Definition vm.h:129
unsigned long GetElapsedTime(int instance)
Definition vm.cpp:984
unsigned long time_stat
Definition vm.h:124
bool Load(bool clear, const std::string &filename, EngineContext *parent=0, const Vector3 &position=Vector3::ZERO, Real rotation=0.0, const Vector3 &area_min=Vector3::ZERO, const Vector3 &area_max=Vector3::ZERO)
Definition vm.cpp:696
std::string version_state
Definition vm.h:128
std::string Bits
Definition vm.h:131
std::string Architecture
Definition vm.h:133
std::string Platform
Definition vm.h:132
SkySystem * GetSkySystem()
Definition vm.cpp:135
ScriptProcessor * GetActiveScriptProcessor()
Definition vm.cpp:607
EngineContext * GetActiveEngine()
Definition vm.h:81
EngineContext * GetEngine(int number)
Definition vm.cpp:432
void DeleteEngines()
Definition vm.cpp:196
Ogre::Real Real
Definition globals.h:57
std::vector< String > StringVector
Definition sbs.h:50
bool SBSIMPEXP enable_profiling
Definition profiler.cpp:15
void SplitString(std::vector< std::string > &dest_array, const std::string &original_string, char separator)
Definition globals.cpp:242
bool SBSIMPEXP enable_advanced_profiling
Definition profiler.cpp:16
int ToInt(const std::string &string)
Definition globals.cpp:402
std::string ToString(int number)
Definition globals.cpp:279
void TrimString(std::string &string)
Definition globals.cpp:188
std::mutex mtx_io
Definition vmconsole.cpp:43
std::atomic< bool > shutdown
Definition vmconsole.cpp:45
std::string GetColors(const std::string &color)
std::string prompt
Definition vmconsole.cpp:44
VMConsoleResult consoleresult
Definition vmconsole.cpp:42
std::atomic< bool > server_ready
Definition vmconsole.h:35
std::atomic< bool > ready
Definition vmconsole.h:33
std::atomic< bool > threadwait
Definition vmconsole.h:34