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 96 : GUI::getIDList() {
63 : try {
64 96 : return GUIMainWindow::getInstance()->getViewIDs();
65 48 : } catch (const ProcessError&) {
66 96 : throw TraCIException("GUI is not running, command not implemented in command line sumo");
67 48 : }
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 0 : GUI::getZoom(const std::string& viewID) {
83 0 : 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 0 : GUI::getOffset(const std::string& viewID) {
95 0 : GUISUMOAbstractView* v = getView(viewID);
96 0 : libsumo::TraCIPosition pos;
97 0 : pos.x = v->getChanger().getXPos();
98 0 : pos.y = v->getChanger().getYPos();
99 0 : return pos;
100 : }
101 :
102 :
103 : std::string
104 0 : GUI::getSchema(const std::string& viewID) {
105 0 : return getView(viewID)->getVisualisationSettings().name;
106 : }
107 :
108 :
109 : libsumo::TraCIPositionVector
110 0 : GUI::getBoundary(const std::string& viewID) {
111 0 : const Boundary& b = getView(viewID)->getVisibleBoundary();
112 : TraCIPositionVector tb;
113 0 : TraCIPosition minV;
114 0 : TraCIPosition maxV;
115 0 : minV.x = b.xmin();
116 0 : maxV.x = b.xmax();
117 0 : minV.y = b.ymin();
118 0 : maxV.y = b.ymax();
119 0 : minV.z = b.zmin();
120 0 : maxV.z = b.zmax();
121 0 : tb.value.push_back(minV);
122 0 : tb.value.push_back(maxV);
123 0 : return tb;
124 0 : }
125 :
126 :
127 : void
128 0 : GUI::setZoom(const std::string& viewID, double zoom) {
129 0 : GUISUMOAbstractView* const v = getView(viewID);
130 0 : const Position off(v->getChanger().getXPos(), v->getChanger().getYPos(), v->getChanger().zoom2ZPos(zoom));
131 : const Position p(off.x(), off.y(), 0);
132 0 : v->setViewportFromToRot(off, p, v->getChanger().getRotation());
133 0 : }
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 0 : GUI::setOffset(const std::string& viewID, double x, double y) {
147 0 : GUISUMOAbstractView* const v = getView(viewID);
148 0 : const Position off(x, y, v->getChanger().getZPos());
149 : const Position p(x, y, 0);
150 0 : v->setViewportFromToRot(off, p, v->getChanger().getRotation());
151 0 : }
152 :
153 :
154 : void
155 0 : GUI::setSchema(const std::string& viewID, const std::string& schemeName) {
156 0 : getView(viewID)->setColorScheme(schemeName);
157 0 : }
158 :
159 :
160 : void
161 0 : 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 0 : 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 0 : } // NOSONAR
170 :
171 :
172 : void
173 0 : GUI::removeView(const std::string& viewID) {
174 : try {
175 : // calling removeViewByID directly doesn't work from the traci/simulation thread
176 0 : 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 0 : } // NOSONAR
182 :
183 :
184 : void
185 0 : GUI::setBoundary(const std::string& viewID, double xmin, double ymin, double xmax, double ymax) {
186 0 : getView(viewID)->centerTo(Boundary(xmin, ymin, xmax, ymax));
187 0 : }
188 :
189 :
190 : void
191 300 : GUI::screenshot(const std::string& viewID, const std::string& filename, const int width, const int height) {
192 300 : getView(viewID)->addSnapshot(SIMSTEP, filename, width, height);
193 300 : }
194 :
195 :
196 : void
197 0 : GUI::trackVehicle(const std::string& viewID, const std::string& vehID) {
198 0 : GUISUMOAbstractView* const v = getView(viewID);
199 0 : if (vehID == "") {
200 0 : v->stopTrack();
201 : } else {
202 : GUIGlID glID = 0;
203 0 : SUMOVehicle* veh = MSNet::getInstance()->getVehicleControl().getVehicle(vehID);
204 0 : 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 0 : if (v->getTrackedID() != glID) {
220 0 : v->startTrack(glID);
221 : }
222 : }
223 0 : }
224 :
225 :
226 : bool
227 0 : GUI::hasView(const std::string& viewID) {
228 : try {
229 0 : 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 0 : GUI::getTrackedVehicle(const std::string& viewID) {
238 0 : GUISUMOAbstractView* const v = getView(viewID);
239 : GUIGlObject* tracked = nullptr;
240 0 : const GUIGlID gid = v->getTrackedID();
241 0 : if (gid != GUIGlObject::INVALID_ID) {
242 0 : tracked = GUIGlObjectStorage::gIDStorage.getObjectBlocking(gid);
243 : }
244 0 : const std::string result = tracked == nullptr ? "" : tracked->getMicrosimID();
245 0 : if (gid != GUIGlObject::INVALID_ID) {
246 0 : GUIGlObjectStorage::gIDStorage.unblockObject(gid);
247 : }
248 0 : 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 0 : GUI::isSelected(const std::string& objID, const std::string& objType) {
260 0 : const std::string fullName = objType + ":" + objID;
261 0 : GUIGlObject* obj = GUIGlObjectStorage::gIDStorage.getObjectBlocking(fullName);
262 0 : if (obj == nullptr) {
263 0 : GUIGlObjectStorage::gIDStorage.unblockObject(obj->getGlID());
264 0 : throw TraCIException("The " + objType + " " + objID + " is not known.");
265 : }
266 0 : const bool result = gSelected.isSelected(obj);
267 0 : GUIGlObjectStorage::gIDStorage.unblockObject(obj->getGlID());
268 0 : return result;
269 : }
270 :
271 :
272 : void
273 0 : GUI::toggleSelection(const std::string& objID, const std::string& objType) {
274 0 : const std::string fullName = objType + ":" + objID;
275 0 : GUIGlObject* obj = GUIGlObjectStorage::gIDStorage.getObjectBlocking(fullName);
276 0 : if (obj == nullptr) {
277 0 : GUIGlObjectStorage::gIDStorage.unblockObject(obj->getGlID());
278 0 : throw TraCIException("The " + objType + " " + objID + " is not known.");
279 : }
280 0 : gSelected.toggleSelection(obj->getGlID());
281 0 : GUIGlObjectStorage::gIDStorage.unblockObject(obj->getGlID());
282 0 : }
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 126 : LIBSUMO_SUBSCRIPTION_IMPLEMENTATION(GUI, GUI)
298 :
299 :
300 : bool
301 1043 : GUI::start(const std::vector<std::string>& cmd) {
302 1043 : 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 482 : if (!GUI::close("Libsumo started new instance.")) {
311 : // SystemFrame::close();
312 : }
313 482 : int argc = 1;
314 482 : char array[1][10] = {{0}};
315 : strcpy(array[0], "dummy");
316 : char* argv[1];
317 482 : argv[0] = array[0];
318 : // make the output aware of threading
319 : MsgHandler::setFactory(&MsgHandlerSynchronized::create);
320 482 : gSimulation = true;
321 482 : XMLSubSys::init();
322 482 : MSFrame::fillOptions();
323 482 : std::vector<std::string> args(cmd.begin() + 1, cmd.end());
324 482 : OptionsIO::setArgs(args);
325 482 : OptionsIO::getOptions(true);
326 482 : OptionsCont::getOptions().processMetaOptions(false);
327 : // Open display
328 482 : myApp = new FXApp("SUMO GUI", "sumo-gui");
329 482 : myApp->init(argc, argv);
330 : int minor, major;
331 482 : 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 482 : myWindow = new GUIApplicationWindow(myApp, "*.sumo.cfg,*.sumocfg");
337 482 : gSchemeStorage.init(myApp);
338 482 : myWindow->dependentBuild(true);
339 482 : myApp->create();
340 482 : myWindow->getRunner()->enableLibsumo();
341 : // Load configuration given on command line
342 482 : myWindow->loadOnStartup(true);
343 482 : } catch (const ProcessError& e) {
344 0 : throw TraCIException(e.what());
345 0 : }
346 482 : return true;
347 : #endif
348 : }
349 :
350 :
351 : bool
352 568 : GUI::load(const std::vector<std::string>& /* cmd */) {
353 568 : 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 158766 : GUI::step(SUMOTime t) {
369 158766 : if (myWindow != nullptr) {
370 77025 : if (t == 0) {
371 76861 : t = SIMSTEP + DELTA_T;
372 : }
373 156315 : while (SIMSTEP < t) {
374 79290 : myWindow->getRunner()->tryStep();
375 : }
376 : return true;
377 : }
378 : return false;
379 : }
380 :
381 :
382 : bool
383 2082 : GUI::close(const std::string& /*reason*/) {
384 2082 : if (myWindow != nullptr) {
385 473 : myApp->stop();
386 473 : delete myWindow;
387 472 : myWindow = nullptr;
388 472 : SystemFrame::close();
389 472 : delete myApp;
390 472 : return true;
391 : }
392 : return false;
393 : }
394 :
395 :
396 : GUISUMOAbstractView*
397 300 : GUI::getView(const std::string& id) {
398 : // we cannot use myWindow here, this is not set for the traci server
399 : try {
400 300 : GUIGlChildWindow* const c = GUIMainWindow::getInstance()->getViewByID(id);
401 300 : if (c == nullptr) {
402 0 : throw TraCIException("View '" + id + "' is not known");
403 : }
404 300 : 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 265 : GUI::makeWrapper() {
413 265 : return std::make_shared<Helper::SubscriptionWrapper>(handleVariable, mySubscriptionResults, myContextSubscriptionResults);
414 : }
415 :
416 :
417 : bool
418 0 : GUI::handleVariable(const std::string& objID, const int variable, VariableWrapper* wrapper, tcpip::Storage* /* paramData */) {
419 0 : switch (variable) {
420 0 : case TRACI_ID_LIST:
421 0 : return wrapper->wrapStringList(objID, variable, getIDList());
422 0 : case ID_COUNT:
423 0 : return wrapper->wrapInt(objID, variable, getIDCount());
424 0 : case VAR_VIEW_ZOOM:
425 0 : return wrapper->wrapDouble(objID, variable, getZoom(objID));
426 0 : case VAR_VIEW_OFFSET:
427 0 : return wrapper->wrapPosition(objID, variable, getOffset(objID));
428 0 : case VAR_VIEW_SCHEMA:
429 0 : return wrapper->wrapString(objID, variable, getSchema(objID));
430 0 : case VAR_ANGLE:
431 0 : return wrapper->wrapDouble(objID, variable, getAngle(objID));
432 0 : case VAR_VIEW_BOUNDARY:
433 0 : return wrapper->wrapPositionVector(objID, variable, getBoundary(objID));
434 0 : case VAR_HAS_VIEW:
435 0 : return wrapper->wrapInt(objID, variable, hasView(objID) ? 1 : 0);
436 0 : case VAR_TRACK_VEHICLE:
437 0 : return wrapper->wrapString(objID, variable, getTrackedVehicle(objID));
438 : default:
439 : return false;
440 : }
441 : }
442 :
443 : }
444 :
445 :
446 : /****************************************************************************/
|