Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2017-2024 German Aerospace Center (DLR) and others.
4 : // This program and the accompanying materials are made available under the
5 : // terms of the Eclipse Public License 2.0 which is available at
6 : // https://www.eclipse.org/legal/epl-2.0/
7 : // This Source Code may also be made available under the following Secondary
8 : // Licenses when the conditions for such availability set forth in the Eclipse
9 : // Public License 2.0 are satisfied: GNU General Public License, version 2
10 : // or later which is available at
11 : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12 : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13 : /****************************************************************************/
14 : /// @file GUI.cpp
15 : /// @author Michael Behrisch
16 : /// @date 07.04.2021
17 : ///
18 : // C++ TraCI client API implementation
19 : /****************************************************************************/
20 : #include <config.h>
21 :
22 : #include <utils/common/MsgHandler.h>
23 : #include <utils/common/SystemFrame.h>
24 : #include <utils/options/OptionsCont.h>
25 : #include <utils/options/OptionsIO.h>
26 : #include <utils/foxtools/MsgHandlerSynchronized.h>
27 : #include <utils/gui/div/GUIGlobalSelection.h>
28 : #include <utils/gui/globjects/GUIGlObjectStorage.h>
29 : #include <utils/gui/settings/GUICompleteSchemeStorage.h>
30 : #include <utils/gui/windows/GUIPerspectiveChanger.h>
31 : #include <utils/gui/events/GUIEvent_AddView.h>
32 : #include <utils/gui/events/GUIEvent_CloseView.h>
33 : #include <utils/xml/XMLSubSys.h>
34 : #include <gui/GUIApplicationWindow.h>
35 : #include <gui/GUIRunThread.h>
36 : #include <guisim/GUIVehicle.h>
37 : #include <guisim/GUIPerson.h>
38 : #include <guisim/GUIContainer.h>
39 : #include <microsim/MSFrame.h>
40 : #include <microsim/MSNet.h>
41 : #include <microsim/transportables/MSTransportableControl.h>
42 : #include <microsim/MSVehicleControl.h>
43 : #include <libsumo/TraCIDefs.h>
44 : #include <libsumo/Helper.h>
45 : #include <libsumo/GUI.h>
46 :
47 :
48 : namespace libsumo {
49 : // ===========================================================================
50 : // static member initializations
51 : // ===========================================================================
52 : SubscriptionResults GUI::mySubscriptionResults;
53 : ContextSubscriptionResults GUI::myContextSubscriptionResults;
54 : GUIApplicationWindow* GUI::myWindow = nullptr;
55 : FXApp* GUI::myApp = nullptr;
56 :
57 :
58 : // ===========================================================================
59 : // static member definitions
60 : // ===========================================================================
61 : std::vector<std::string>
62 884 : GUI::getIDList() {
63 : try {
64 884 : return GUIMainWindow::getInstance()->getViewIDs();
65 24 : } catch (const ProcessError&) {
66 48 : throw TraCIException("GUI is not running, command not implemented in command line sumo");
67 24 : }
68 : }
69 :
70 :
71 : int
72 0 : GUI::getIDCount() {
73 : try {
74 0 : return (int)GUIMainWindow::getInstance()->getViewIDs().size();
75 0 : } catch (const ProcessError&) {
76 0 : throw TraCIException("GUI is not running, command not implemented in command line sumo");
77 0 : }
78 : }
79 :
80 :
81 : double
82 4 : GUI::getZoom(const std::string& viewID) {
83 4 : return getView(viewID)->getChanger().getZoom();
84 : }
85 :
86 :
87 : double
88 0 : GUI::getAngle(const std::string& viewID) {
89 0 : return getView(viewID)->getChanger().getRotation();
90 : }
91 :
92 :
93 : libsumo::TraCIPosition
94 4 : GUI::getOffset(const std::string& viewID) {
95 4 : GUISUMOAbstractView* v = getView(viewID);
96 4 : libsumo::TraCIPosition pos;
97 4 : pos.x = v->getChanger().getXPos();
98 4 : pos.y = v->getChanger().getYPos();
99 4 : return pos;
100 : }
101 :
102 :
103 : std::string
104 4 : GUI::getSchema(const std::string& viewID) {
105 4 : return getView(viewID)->getVisualisationSettings().name;
106 : }
107 :
108 :
109 : libsumo::TraCIPositionVector
110 4 : GUI::getBoundary(const std::string& viewID) {
111 4 : const Boundary& b = getView(viewID)->getVisibleBoundary();
112 : TraCIPositionVector tb;
113 4 : TraCIPosition minV;
114 4 : TraCIPosition maxV;
115 4 : minV.x = b.xmin();
116 4 : maxV.x = b.xmax();
117 4 : minV.y = b.ymin();
118 4 : maxV.y = b.ymax();
119 4 : minV.z = b.zmin();
120 4 : maxV.z = b.zmax();
121 4 : tb.value.push_back(minV);
122 4 : tb.value.push_back(maxV);
123 4 : return tb;
124 4 : }
125 :
126 :
127 : void
128 4 : GUI::setZoom(const std::string& viewID, double zoom) {
129 4 : GUISUMOAbstractView* const v = getView(viewID);
130 4 : const Position off(v->getChanger().getXPos(), v->getChanger().getYPos(), v->getChanger().zoom2ZPos(zoom));
131 : const Position p(off.x(), off.y(), 0);
132 4 : v->setViewportFromToRot(off, p, v->getChanger().getRotation());
133 4 : }
134 :
135 :
136 : void
137 0 : GUI::setAngle(const std::string& viewID, double angle) {
138 0 : GUISUMOAbstractView* const v = getView(viewID);
139 0 : const Position off(v->getChanger().getXPos(), v->getChanger().getYPos(), v->getChanger().getZPos());
140 : const Position p(off.x(), off.y(), 0);
141 0 : v->setViewportFromToRot(off, p, angle);
142 0 : }
143 :
144 :
145 : void
146 4 : GUI::setOffset(const std::string& viewID, double x, double y) {
147 4 : GUISUMOAbstractView* const v = getView(viewID);
148 4 : const Position off(x, y, v->getChanger().getZPos());
149 : const Position p(x, y, 0);
150 4 : v->setViewportFromToRot(off, p, v->getChanger().getRotation());
151 4 : }
152 :
153 :
154 : void
155 8 : GUI::setSchema(const std::string& viewID, const std::string& schemeName) {
156 8 : getView(viewID)->setColorScheme(schemeName);
157 8 : }
158 :
159 :
160 : void
161 4 : GUI::addView(const std::string& viewID, const std::string& schemeName, bool in3D) {
162 : try {
163 : // calling openNewView directly doesn't work from the traci/simulation thread
164 8 : GUIMainWindow::getInstance()->sendBlockingEvent(new GUIEvent_AddView(viewID, schemeName, in3D));
165 0 : } catch (const ProcessError&) {
166 0 : throw TraCIException("GUI is not running, command not implemented in command line sumo");
167 0 : }
168 : // sonar thinks here is a memory leak but the GUIApplicationWindow does the clean up
169 4 : } // NOSONAR
170 :
171 :
172 : void
173 4 : GUI::removeView(const std::string& viewID) {
174 : try {
175 : // calling removeViewByID directly doesn't work from the traci/simulation thread
176 8 : GUIMainWindow::getInstance()->sendBlockingEvent(new GUIEvent_CloseView(viewID));
177 0 : } catch (const ProcessError&) {
178 0 : throw TraCIException("GUI is not running, command not implemented in command line sumo");
179 0 : }
180 : // sonar thinks here is a memory leak but the GUIApplicationWindow does the clean up
181 4 : } // NOSONAR
182 :
183 :
184 : void
185 4 : GUI::setBoundary(const std::string& viewID, double xmin, double ymin, double xmax, double ymax) {
186 4 : getView(viewID)->centerTo(Boundary(xmin, ymin, xmax, ymax));
187 4 : }
188 :
189 :
190 : void
191 408 : GUI::screenshot(const std::string& viewID, const std::string& filename, const int width, const int height) {
192 408 : getView(viewID)->addSnapshot(SIMSTEP, filename, width, height);
193 408 : }
194 :
195 :
196 : void
197 4 : GUI::trackVehicle(const std::string& viewID, const std::string& vehID) {
198 4 : GUISUMOAbstractView* const v = getView(viewID);
199 4 : if (vehID == "") {
200 0 : v->stopTrack();
201 : } else {
202 : GUIGlID glID = 0;
203 4 : SUMOVehicle* veh = MSNet::getInstance()->getVehicleControl().getVehicle(vehID);
204 4 : if (veh != nullptr) {
205 : glID = static_cast<GUIVehicle*>(veh)->getGlID();
206 : } else {
207 0 : MSTransportable* person = MSNet::getInstance()->getPersonControl().get(vehID);
208 0 : if (person != nullptr) {
209 : glID = static_cast<GUIPerson*>(person)->getGlID();
210 : } else {
211 0 : MSTransportable* container = MSNet::getInstance()->getContainerControl().get(vehID);
212 0 : if (container != nullptr) {
213 : glID = static_cast<GUIContainer*>(container)->getGlID();
214 : } else {
215 0 : throw TraCIException("Could not find vehicle or person '" + vehID + "'.");
216 : }
217 : }
218 : }
219 4 : if (v->getTrackedID() != glID) {
220 4 : v->startTrack(glID);
221 : }
222 : }
223 4 : }
224 :
225 :
226 : bool
227 8 : GUI::hasView(const std::string& viewID) {
228 : try {
229 8 : return GUIMainWindow::getInstance()->getViewByID(viewID) != nullptr;
230 0 : } catch (const ProcessError&) {
231 0 : throw TraCIException("GUI is not running, command not implemented in command line sumo");
232 0 : }
233 : }
234 :
235 :
236 : std::string
237 4 : GUI::getTrackedVehicle(const std::string& viewID) {
238 4 : GUISUMOAbstractView* const v = getView(viewID);
239 : GUIGlObject* tracked = nullptr;
240 4 : const GUIGlID gid = v->getTrackedID();
241 4 : if (gid != GUIGlObject::INVALID_ID) {
242 4 : tracked = GUIGlObjectStorage::gIDStorage.getObjectBlocking(gid);
243 : }
244 4 : const std::string result = tracked == nullptr ? "" : tracked->getMicrosimID();
245 4 : if (gid != GUIGlObject::INVALID_ID) {
246 4 : GUIGlObjectStorage::gIDStorage.unblockObject(gid);
247 : }
248 4 : return result;
249 : }
250 :
251 :
252 : void
253 0 : GUI::track(const std::string& objID, const std::string& viewID) {
254 0 : trackVehicle(viewID, objID);
255 0 : }
256 :
257 :
258 : bool
259 16 : GUI::isSelected(const std::string& objID, const std::string& objType) {
260 16 : const std::string fullName = objType + ":" + objID;
261 16 : GUIGlObject* obj = GUIGlObjectStorage::gIDStorage.getObjectBlocking(fullName);
262 16 : if (obj == nullptr) {
263 0 : GUIGlObjectStorage::gIDStorage.unblockObject(obj->getGlID());
264 0 : throw TraCIException("The " + objType + " " + objID + " is not known.");
265 : }
266 16 : const bool result = gSelected.isSelected(obj);
267 16 : GUIGlObjectStorage::gIDStorage.unblockObject(obj->getGlID());
268 16 : return result;
269 : }
270 :
271 :
272 : void
273 8 : GUI::toggleSelection(const std::string& objID, const std::string& objType) {
274 8 : const std::string fullName = objType + ":" + objID;
275 8 : GUIGlObject* obj = GUIGlObjectStorage::gIDStorage.getObjectBlocking(fullName);
276 8 : if (obj == nullptr) {
277 0 : GUIGlObjectStorage::gIDStorage.unblockObject(obj->getGlID());
278 0 : throw TraCIException("The " + objType + " " + objID + " is not known.");
279 : }
280 8 : gSelected.toggleSelection(obj->getGlID());
281 8 : GUIGlObjectStorage::gIDStorage.unblockObject(obj->getGlID());
282 8 : }
283 :
284 :
285 : std::string
286 0 : GUI::getParameter(const std::string& /* viewID */, const std::string& /* name */) {
287 0 : return "";
288 : }
289 :
290 :
291 : void
292 0 : GUI::setParameter(const std::string& /* viewID */, const std::string& /* name */, const std::string& /* value */) {
293 0 : }
294 :
295 :
296 0 : LIBSUMO_GET_PARAMETER_WITH_KEY_IMPLEMENTATION(GUI)
297 76 : LIBSUMO_SUBSCRIPTION_IMPLEMENTATION(GUI, GUI)
298 :
299 :
300 : bool
301 1003 : GUI::start(const std::vector<std::string>& cmd) {
302 1003 : if (cmd[0].find("sumo-gui") == std::string::npos && std::getenv("LIBSUMO_GUI") == nullptr) {
303 : return false;
304 : }
305 : #ifdef WIN32
306 : WRITE_WARNING("Libsumo on Windows does not work with GUI, falling back to plain libsumo.");
307 : return false;
308 : #else
309 : try {
310 466 : if (!GUI::close("Libsumo started new instance.")) {
311 : // SystemFrame::close();
312 : }
313 466 : int argc = 1;
314 466 : char array[1][10] = {{0}};
315 : strcpy(array[0], "dummy");
316 : char* argv[1];
317 466 : argv[0] = array[0];
318 : // make the output aware of threading
319 : MsgHandler::setFactory(&MsgHandlerSynchronized::create);
320 466 : gSimulation = true;
321 466 : XMLSubSys::init();
322 466 : MSFrame::fillOptions();
323 466 : std::vector<std::string> args(cmd.begin() + 1, cmd.end());
324 466 : OptionsIO::setArgs(args);
325 466 : OptionsIO::getOptions(true);
326 466 : OptionsCont::getOptions().processMetaOptions(false);
327 : // Open display
328 466 : myApp = new FXApp("SUMO GUI", "sumo-gui");
329 466 : myApp->init(argc, argv);
330 : int minor, major;
331 465 : if (!FXGLVisual::supported(myApp, major, minor)) {
332 0 : throw ProcessError(TL("This system has no OpenGL support. Exiting."));
333 : }
334 :
335 : // build the main window
336 465 : myWindow = new GUIApplicationWindow(myApp, "*.sumo.cfg,*.sumocfg");
337 465 : gSchemeStorage.init(myApp);
338 465 : myWindow->dependentBuild(true);
339 465 : myApp->create();
340 465 : myWindow->getRunner()->enableLibsumo();
341 : // Load configuration given on command line
342 465 : myWindow->loadOnStartup(true);
343 465 : } catch (const ProcessError& e) {
344 0 : throw TraCIException(e.what());
345 0 : }
346 465 : return true;
347 : #endif
348 : }
349 :
350 :
351 : bool
352 553 : GUI::load(const std::vector<std::string>& /* cmd */) {
353 553 : if (myWindow != nullptr) {
354 2 : WRITE_ERROR("libsumo.load is not implemented for the GUI.");
355 2 : return true;
356 : }
357 : return false;
358 : }
359 :
360 :
361 : bool
362 2 : GUI::hasInstance() {
363 2 : return myWindow != nullptr;
364 : }
365 :
366 :
367 : bool
368 156113 : GUI::step(SUMOTime t) {
369 156113 : if (myWindow != nullptr) {
370 76793 : if (t == 0) {
371 76629 : t = SIMSTEP + DELTA_T;
372 : }
373 155607 : while (SIMSTEP < t) {
374 78814 : myWindow->getRunner()->tryStep();
375 : }
376 : return true;
377 : }
378 : return false;
379 : }
380 :
381 :
382 : bool
383 2015 : GUI::close(const std::string& /*reason*/) {
384 2015 : if (myWindow != nullptr) {
385 454 : myApp->stop();
386 454 : delete myWindow;
387 453 : myWindow = nullptr;
388 453 : SystemFrame::close();
389 453 : delete myApp;
390 453 : return true;
391 : }
392 : return false;
393 : }
394 :
395 :
396 : GUISUMOAbstractView*
397 452 : GUI::getView(const std::string& id) {
398 : // we cannot use myWindow here, this is not set for the traci server
399 : try {
400 452 : GUIGlChildWindow* const c = GUIMainWindow::getInstance()->getViewByID(id);
401 452 : if (c == nullptr) {
402 0 : throw TraCIException("View '" + id + "' is not known");
403 : }
404 452 : return c->getView();
405 0 : } catch (const ProcessError&) {
406 0 : throw TraCIException("GUI is not running, command not implemented in command line sumo");
407 0 : }
408 : }
409 :
410 :
411 : std::shared_ptr<VariableWrapper>
412 263 : GUI::makeWrapper() {
413 263 : return std::make_shared<Helper::SubscriptionWrapper>(handleVariable, mySubscriptionResults, myContextSubscriptionResults);
414 : }
415 :
416 :
417 : bool
418 880 : GUI::handleVariable(const std::string& objID, const int variable, VariableWrapper* wrapper, tcpip::Storage* /* paramData */) {
419 880 : switch (variable) {
420 836 : case TRACI_ID_LIST:
421 836 : return wrapper->wrapStringList(objID, variable, getIDList());
422 0 : case ID_COUNT:
423 0 : return wrapper->wrapInt(objID, variable, getIDCount());
424 4 : case VAR_VIEW_ZOOM:
425 4 : return wrapper->wrapDouble(objID, variable, getZoom(objID));
426 4 : case VAR_VIEW_OFFSET:
427 4 : return wrapper->wrapPosition(objID, variable, getOffset(objID));
428 4 : case VAR_VIEW_SCHEMA:
429 8 : return wrapper->wrapString(objID, variable, getSchema(objID));
430 0 : case VAR_ANGLE:
431 0 : return wrapper->wrapDouble(objID, variable, getAngle(objID));
432 4 : case VAR_VIEW_BOUNDARY:
433 8 : return wrapper->wrapPositionVector(objID, variable, getBoundary(objID));
434 8 : case VAR_HAS_VIEW:
435 12 : return wrapper->wrapInt(objID, variable, hasView(objID) ? 1 : 0);
436 4 : case VAR_TRACK_VEHICLE:
437 8 : return wrapper->wrapString(objID, variable, getTrackedVehicle(objID));
438 : default:
439 : return false;
440 : }
441 : }
442 :
443 : }
444 :
445 :
446 : /****************************************************************************/
|