1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3 // Copyright (C) 2001-2019 German Aerospace Center (DLR) and others.
4 // This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v2.0
6 // which accompanies this distribution, and is available at
7 // http://www.eclipse.org/legal/epl-v20.html
8 // SPDX-License-Identifier: EPL-2.0
9 /****************************************************************************/
10 /// @file GUITLLogicPhasesTrackerWindow.cpp
11 /// @author Daniel Krajzewicz
12 /// @author Jakob Erdmann
13 /// @author Michael Behrisch
14 /// @date Oct/Nov 2003
15 /// @version $Id$
16 ///
17 // A window displaying the phase diagram of a tl-logic
18 /****************************************************************************/
19
20
21 // ===========================================================================
22 // included modules
23 // ===========================================================================
24 #include <config.h>
25
26 #include <vector>
27 #include <iostream>
28 #include <utils/gui/windows/GUIMainWindow.h>
29 #include <utils/gui/div/GLHelper.h>
30 #include "GUITLLogicPhasesTrackerWindow.h"
31 #include <microsim/traffic_lights/MSTrafficLightLogic.h>
32 #include <microsim/MSLink.h>
33 #include <utils/common/ToString.h>
34 #include <utils/common/MsgHandler.h>
35 #include <guisim/GUITrafficLightLogicWrapper.h>
36 #include <utils/gui/windows/GUIAppEnum.h>
37 #include <utils/gui/images/GUIIconSubSys.h>
38 #include <utils/gui/settings/GUIVisualizationSettings.h>
39 #include <utils/gui/div/GUIDesigns.h>
40 #include <foreign/fontstash/fontstash.h>
41 #include <utils/gui/globjects/GLIncludes.h>
42
43
44 // ===========================================================================
45 // member method definitions
46 // ===========================================================================
47 /* -------------------------------------------------------------------------
48 * GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerPanel-callbacks
49 * ----------------------------------------------------------------------- */
50 FXDEFMAP(GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerPanel) GUITLLogicPhasesTrackerPanelMap[] = {
51 FXMAPFUNC(SEL_CONFIGURE, 0, GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerPanel::onConfigure),
52 FXMAPFUNC(SEL_PAINT, 0, GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerPanel::onPaint),
53
54 };
55
56 // Macro for the GLTestApp class hierarchy implementation
FXIMPLEMENT(GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerPanel,FXGLCanvas,GUITLLogicPhasesTrackerPanelMap,ARRAYNUMBER (GUITLLogicPhasesTrackerPanelMap))57 FXIMPLEMENT(GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerPanel, FXGLCanvas, GUITLLogicPhasesTrackerPanelMap, ARRAYNUMBER(GUITLLogicPhasesTrackerPanelMap))
58
59
60
61 /* -------------------------------------------------------------------------
62 * GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerPanel-methods
63 * ----------------------------------------------------------------------- */
64 GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerPanel::GUITLLogicPhasesTrackerPanel(
65 FXComposite* c, GUIMainWindow& app,
66 GUITLLogicPhasesTrackerWindow& parent)
67 : FXGLCanvas(c, app.getGLVisual(), app.getBuildGLCanvas(), (FXObject*) nullptr, (FXSelector) 0, LAYOUT_SIDE_TOP | LAYOUT_FILL_X | LAYOUT_FILL_Y/*, 0, 0, 300, 200*/),
68 myParent(&parent) {}
69
70
~GUITLLogicPhasesTrackerPanel()71 GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerPanel::~GUITLLogicPhasesTrackerPanel() {}
72
73
74 long
onConfigure(FXObject *,FXSelector,void *)75 GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerPanel::onConfigure(
76 FXObject*, FXSelector, void*) {
77 if (makeCurrent()) {
78 int widthInPixels = getWidth();
79 int heightInPixels = getHeight();
80 if (widthInPixels != 0 && heightInPixels != 0) {
81 glViewport(0, 0, widthInPixels - 1, heightInPixels - 1);
82 glClearColor(0, 0, 0, 1);
83 glDisable(GL_DEPTH_TEST);
84 glDisable(GL_LIGHTING);
85 glDisable(GL_LINE_SMOOTH);
86 glEnable(GL_BLEND);
87 glEnable(GL_ALPHA_TEST);
88 glDisable(GL_COLOR_MATERIAL);
89 glLineWidth(1);
90 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
91 }
92 }
93 return 1;
94 }
95
96
97 long
onPaint(FXObject *,FXSelector,void *)98 GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerPanel::onPaint(
99 FXObject*, FXSelector, void*) {
100 if (!isEnabled()) {
101 return 1;
102 }
103 if (makeCurrent()) {
104 int widthInPixels = getWidth();
105 int heightInPixels = getHeight();
106 if (widthInPixels != 0 && heightInPixels != 0) {
107 glViewport(0, 0, widthInPixels - 1, heightInPixels - 1);
108 glClearColor(0, 0, 0, 1);
109 glDisable(GL_DEPTH_TEST);
110 glDisable(GL_LIGHTING);
111 glDisable(GL_LINE_SMOOTH);
112 glEnable(GL_BLEND);
113 glEnable(GL_ALPHA_TEST);
114 glDisable(GL_COLOR_MATERIAL);
115 glLineWidth(1);
116 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
117 // draw
118 glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
119 myParent->drawValues(*this);
120 swapBuffers();
121 }
122 makeNonCurrent();
123 }
124 return 1;
125 }
126
127
128
129 /* -------------------------------------------------------------------------
130 * GUITLLogicPhasesTrackerWindow - FOX callback mapping
131 * ----------------------------------------------------------------------- */
132 FXDEFMAP(GUITLLogicPhasesTrackerWindow) GUITLLogicPhasesTrackerWindowMap[] = {
133 FXMAPFUNC(SEL_CONFIGURE, 0, GUITLLogicPhasesTrackerWindow::onConfigure),
134 FXMAPFUNC(SEL_PAINT, 0, GUITLLogicPhasesTrackerWindow::onPaint),
135 FXMAPFUNC(SEL_COMMAND, MID_SIMSTEP, GUITLLogicPhasesTrackerWindow::onSimStep),
136
137 };
138
FXIMPLEMENT(GUITLLogicPhasesTrackerWindow,FXMainWindow,GUITLLogicPhasesTrackerWindowMap,ARRAYNUMBER (GUITLLogicPhasesTrackerWindowMap))139 FXIMPLEMENT(GUITLLogicPhasesTrackerWindow, FXMainWindow, GUITLLogicPhasesTrackerWindowMap, ARRAYNUMBER(GUITLLogicPhasesTrackerWindowMap))
140
141
142 /* -------------------------------------------------------------------------
143 * GUITLLogicPhasesTrackerWindow-methods
144 * ----------------------------------------------------------------------- */
145 GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerWindow(
146 GUIMainWindow& app,
147 MSTrafficLightLogic& logic, GUITrafficLightLogicWrapper& wrapper,
148 ValueSource<std::pair<SUMOTime, MSPhaseDefinition> >* src)
149 : FXMainWindow(app.getApp(), "TLS-Tracker", nullptr, nullptr, DECOR_ALL,
150 20, 20, 300, 200),
151 myApplication(&app), myTLLogic(&logic), myAmInTrackingMode(true) {
152 // build the toolbar
153 myToolBarDrag = new FXToolBarShell(this, GUIDesignToolBar);
154 myToolBar = new FXToolBar(this, myToolBarDrag, LAYOUT_SIDE_TOP | LAYOUT_FILL_X | FRAME_RAISED);
155 new FXToolBarGrip(myToolBar, myToolBar, FXToolBar::ID_TOOLBARGRIP, GUIDesignToolBarGrip);
156 // interval manipulation
157 myBeginOffset = new FXRealSpinner(myToolBar, 10, this, MID_SIMSTEP, LAYOUT_TOP | FRAME_SUNKEN | FRAME_THICK);
158 //myBeginOffset->setFormatString("%.0f");
159 //myBeginOffset->setIncrements(1, 10, 100);
160 myBeginOffset->setIncrement(10);
161 myBeginOffset->setRange(60, 3600);
162 myBeginOffset->setValue(240);
163 new FXLabel(myToolBar, "(s)", nullptr, LAYOUT_CENTER_Y);
164 //
165 myConnector = new GLObjectValuePassConnector<std::pair<SUMOTime, MSPhaseDefinition> >(wrapper, src, this);
166 FXint height = (FXint)(myTLLogic->getLinks().size() * 20 + 30 + 8 + 30);
167 app.addChild(this);
168 for (int i = 0; i < (int)myTLLogic->getLinks().size(); ++i) {
169 myLinkNames.push_back(toString<int>(i));
170 }
171 FXVerticalFrame* glcanvasFrame =
172 new FXVerticalFrame(this,
173 FRAME_SUNKEN | LAYOUT_SIDE_TOP | LAYOUT_FILL_X | LAYOUT_FILL_Y,
174 0, 0, 0, 0, 0, 0, 0, 0);
175 myPanel = new
176 GUITLLogicPhasesTrackerPanel(glcanvasFrame, *myApplication, *this);
177 setTitle((logic.getID() + " - " + logic.getProgramID() + " - tracker").c_str());
178 setIcon(GUIIconSubSys::getIcon(ICON_APP_TLSTRACKER));
179 setHeight(height);
180 }
181
182
GUITLLogicPhasesTrackerWindow(GUIMainWindow & app,MSTrafficLightLogic & logic,GUITrafficLightLogicWrapper &,const MSSimpleTrafficLightLogic::Phases &)183 GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerWindow(
184 GUIMainWindow& app,
185 MSTrafficLightLogic& logic, GUITrafficLightLogicWrapper& /*wrapper*/,
186 const MSSimpleTrafficLightLogic::Phases& /*phases*/)
187 : FXMainWindow(app.getApp(), "TLS-Tracker", nullptr, nullptr, DECOR_ALL,
188 20, 20, 300, 200),
189 myApplication(&app), myTLLogic(&logic), myAmInTrackingMode(false),
190 myToolBarDrag(nullptr), myBeginOffset(nullptr) {
191 myConnector = nullptr;
192 FXint height = (FXint)(myTLLogic->getLinks().size() * 20 + 30 + 8);
193 setTitle("TLS-Tracker");
194 app.addChild(this);
195 for (int i = 0; i < (int)myTLLogic->getLinks().size(); ++i) {
196 myLinkNames.push_back(toString<int>(i));
197 }
198 FXVerticalFrame* glcanvasFrame =
199 new FXVerticalFrame(this,
200 FRAME_SUNKEN | LAYOUT_SIDE_TOP | LAYOUT_FILL_X | LAYOUT_FILL_Y,
201 0, 0, 0, 0, 0, 0, 0, 0);
202 myPanel = new
203 GUITLLogicPhasesTrackerPanel(glcanvasFrame, *myApplication, *this);
204 setTitle((logic.getID() + " - " + logic.getProgramID() + " - tracker").c_str());
205 setIcon(GUIIconSubSys::getIcon(ICON_APP_TLSTRACKER));
206 setHeight(height);
207 }
208
209
~GUITLLogicPhasesTrackerWindow()210 GUITLLogicPhasesTrackerWindow::~GUITLLogicPhasesTrackerWindow() {
211 myApplication->removeChild(this);
212 delete myConnector;
213 // just to quit cleanly on a failure
214 if (myLock.locked()) {
215 myLock.unlock();
216 }
217 delete myToolBarDrag;
218 }
219
220
221 void
create()222 GUITLLogicPhasesTrackerWindow::create() {
223 FXMainWindow::create();
224 if (myToolBarDrag != nullptr) {
225 myToolBarDrag->create();
226 }
227 }
228
229
230 void
drawValues(GUITLLogicPhasesTrackerPanel & caller)231 GUITLLogicPhasesTrackerWindow::drawValues(GUITLLogicPhasesTrackerPanel& caller) {
232 // compute what shall be shown (what is visible)
233 myFirstPhase2Show = 0;
234 myFirstPhaseOffset = 0;
235 SUMOTime leftOffset = 0;
236 myFirstTime2Show = 0;
237 if (!myAmInTrackingMode) {
238 myPhases.clear();
239 myDurations.clear();
240 // insert phases
241 MSSimpleTrafficLightLogic* simpleTLLogic = dynamic_cast<MSSimpleTrafficLightLogic*>(myTLLogic);
242 if (simpleTLLogic == nullptr) {
243 return;
244 }
245 const MSSimpleTrafficLightLogic::Phases& phases = simpleTLLogic->getPhases();
246 MSSimpleTrafficLightLogic::Phases::const_iterator j;
247 myLastTime = 0;
248 myBeginTime = 0;
249 for (j = phases.begin(); j != phases.end(); ++j) {
250 myPhases.push_back(*(*j));
251 myDurations.push_back((*j)->duration);
252 myLastTime += (*j)->duration;
253 }
254 if (myLastTime <= myBeginTime) {
255 WRITE_ERROR("Overflow in time computation occurred.");
256 return;
257 }
258 } else {
259 SUMOTime beginOffset = TIME2STEPS(myBeginOffset->getValue());
260 myBeginTime = myLastTime - beginOffset;
261 myFirstTime2Show = myBeginTime;
262 // check whether no phases are known at all
263 if (myDurations.size() != 0) {
264 SUMOTime durs = 0;
265 int phaseOffset = (int)myDurations.size() - 1;
266 DurationsVector::reverse_iterator i = myDurations.rbegin();
267 while (i != myDurations.rend()) {
268 if (durs + (*i) > beginOffset) {
269 myFirstPhase2Show = phaseOffset;
270 myFirstPhaseOffset = (durs + (*i)) - beginOffset;
271 break;
272 }
273 durs += (*i);
274 phaseOffset--;
275 ++i;
276 }
277 if (i == myDurations.rend()) {
278 // there are too few information stored;
279 myFirstPhase2Show = 0;
280 myFirstPhaseOffset = 0;
281 leftOffset = beginOffset - durs;
282 }
283 }
284 }
285 // begin drawing
286 glMatrixMode(GL_PROJECTION);
287 glLoadIdentity();
288 glMatrixMode(GL_MODELVIEW);
289 glLoadIdentity();
290 glTranslated(-1, -1, 0);
291 glScaled(2, 2, 1);
292 glDisable(GL_TEXTURE_2D);
293 // draw the horizontal lines dividing the signal groups
294 glColor3d(1, 1, 1);
295 // compute some values needed more than once
296 const double height = (double) caller.getHeight();
297 const double width = (double) caller.getWidth();
298 const double barWidth = MAX2(1.0, width - 31);
299 const double fontHeight = 0.08 * 300. / height;
300 const double fontWidth = 0.08 * 300. / width;
301 const double h9 = ((double) 9 / height);
302 const double h10 = ((double) 10 / height);
303 const double h11 = ((double) 11 / height);
304 const double h16 = ((double) 16 / height);
305 const double h20 = ((double) 20 / height);
306 // draw the link names and the lines dividing them
307 double h = (double)(1.0 - h10);
308 double h2 = 12;
309 for (int i = 0; i < (int)myTLLogic->getLinks().size() + 1; ++i) {
310 // draw the bar
311 glBegin(GL_LINES);
312 glVertex2d(0, h);
313 glVertex2d((double)(30. / width), h);
314 glEnd();
315 // draw the name
316 if (i < (int)myTLLogic->getLinks().size()) {
317 glTranslated(0, h - h20, 0);
318 GLHelper::drawText(myLinkNames[i], Position(0, 0), 1, fontHeight, RGBColor::WHITE, 0, FONS_ALIGN_LEFT | FONS_ALIGN_BOTTOM, fontWidth);
319 glTranslated(0, -h + h20, 0);
320 h2 += 20;
321 }
322 h -= h20;
323 }
324 glBegin(GL_LINES);
325 glVertex2d(0, h + h20);
326 glVertex2d(1.0, h + h20);
327 glEnd();
328
329 // draw the names closure (vertical line)
330 h += (double) 20 / height;
331 glColor3d(1, 1, 1);
332 glBegin(GL_LINES);
333 glVertex2d((double) 30 / width, 1.0);
334 glVertex2d((double) 30 / width, h);
335 glEnd();
336
337
338 // draw the phases
339 // disable value addition while drawing
340 myLock.lock();
341 // determine the initial offset
342 double x = ((double) 31. / width);
343 double ta = (double) leftOffset / width;
344 ta *= (double)((barWidth / ((double)(myLastTime - myBeginTime))));
345 x += ta;
346
347 // and the initial phase information
348 PhasesVector::iterator pi = myPhases.begin() + myFirstPhase2Show;
349
350 SUMOTime fpo = myFirstPhaseOffset;
351
352 // start drawing
353 for (DurationsVector::iterator pd = myDurations.begin() + myFirstPhase2Show; pd != myDurations.end(); ++pd) {
354 SUMOTime i = 30;
355 // the first phase may be drawn incompletely
356 SUMOTime duration = *pd - fpo;
357 // compute the heigh and the width of the phase
358 h = (double)(1.0 - h10);
359 double a = (double) duration / width;
360 a *= (double)((barWidth / ((double)(myLastTime - myBeginTime))));
361 const double x2 = x + a;
362
363 // go through the links
364 for (int j = 0; j < (int) myTLLogic->getLinks().size(); ++j) {
365 // determine the current link's color
366 LinkState state = pi->getSignalState(j);
367 // draw the bar (red is drawn as a line)
368 GLHelper::setColor(GUIVisualizationSettings::getLinkColor(state));
369 switch (state) {
370 case LINKSTATE_TL_RED:
371 case LINKSTATE_TL_REDYELLOW:
372 // draw a thin line
373 glBegin(GL_QUADS);
374 glVertex2d(x, h - h11);
375 glVertex2d(x, h - h9);
376 glVertex2d(x2, h - h9);
377 glVertex2d(x2, h - h11);
378 glEnd();
379 break;
380 default:
381 // draw a thick block
382 glBegin(GL_QUADS);
383 glVertex2d(x, h - h16);
384 glVertex2d(x, h);
385 glVertex2d(x2, h);
386 glVertex2d(x2, h - h16);
387 glEnd();
388 break;
389 }
390 // proceed to next link
391 h -= h20;
392 }
393 // proceed to next phase
394 i += duration;
395 ++pi;
396 x = x2;
397 // all further phases are drawn in full
398 fpo = 0;
399 }
400 // allow value addition
401 myLock.unlock();
402
403 glColor3d(1, 1, 1);
404 if (myPhases.size() != 0) {
405 SUMOTime tickDist = TIME2STEPS(10);
406 // patch distances - hack
407 double t = myBeginOffset != nullptr ? (double) myBeginOffset->getValue() : STEPS2TIME(myLastTime - myBeginTime);
408 while (t > barWidth / 4.) {
409 tickDist += TIME2STEPS(10);
410 t -= (double)(barWidth / 4.);
411 }
412 // draw time information
413 //h = (double)(myTLLogic->getLinks().size() * 20 + 12);
414 double glh = (double)(1.0 - myTLLogic->getLinks().size() * h20 - h10);
415 // current begin time
416 // time ticks
417 SUMOTime currTime = myFirstTime2Show;
418 int pos = 31;// + /*!!!currTime*/ - myFirstTime2Show;
419 double glpos = (double) pos / width;
420 const double ticSize = 4 / height;
421 while (pos < width + 50) {
422 const std::string timeStr = time2string(currTime);
423 const double w = 50 / width;
424 glTranslated(glpos - w / 2., glh - h20, 0);
425 GLHelper::drawText(timeStr, Position(0, 0), 1, fontHeight, RGBColor::WHITE, 0, FONS_ALIGN_LEFT | FONS_ALIGN_MIDDLE, fontWidth);
426 glTranslated(-glpos + w / 2., -glh + h20, 0);
427
428 glBegin(GL_LINES);
429 glVertex2d(glpos, glh);
430 glVertex2d(glpos, glh - ticSize);
431 glEnd();
432
433 const double a = STEPS2TIME(tickDist) * barWidth / STEPS2TIME(myLastTime - myBeginTime);
434 pos += (int) a;
435 glpos += a / width;
436 currTime += tickDist;
437 }
438 }
439 }
440
441
442 void
addValue(std::pair<SUMOTime,MSPhaseDefinition> def)443 GUITLLogicPhasesTrackerWindow::addValue(std::pair<SUMOTime, MSPhaseDefinition> def) {
444 // do not draw while adding
445 myLock.lock();
446 // set the first time if not set before
447 if (myPhases.size() == 0) {
448 myBeginTime = def.first;
449 }
450 // append or set the phase
451 if (myPhases.size() == 0 || *(myPhases.end() - 1) != def.second) {
452 myPhases.push_back(def.second);
453 myDurations.push_back(DELTA_T);
454 } else {
455 *(myDurations.end() - 1) += DELTA_T;
456 }
457 // set the last time a phase was added at
458 myLastTime = def.first;
459 // allow drawing
460 myLock.unlock();
461 }
462
463
464 long
onConfigure(FXObject * sender,FXSelector sel,void * data)465 GUITLLogicPhasesTrackerWindow::onConfigure(FXObject* sender,
466 FXSelector sel, void* data) {
467 myPanel->onConfigure(sender, sel, data);
468 return FXMainWindow::onConfigure(sender, sel, data);
469 }
470
471
472 long
onPaint(FXObject * sender,FXSelector sel,void * data)473 GUITLLogicPhasesTrackerWindow::onPaint(FXObject* sender,
474 FXSelector sel, void* data) {
475 myPanel->onPaint(sender, sel, data);
476 return FXMainWindow::onPaint(sender, sel, data);
477 }
478
479
480 long
onSimStep(FXObject *,FXSelector,void *)481 GUITLLogicPhasesTrackerWindow::onSimStep(FXObject*,
482 FXSelector, void*) {
483 update();
484 return 1;
485 }
486
487
488 void
setBeginTime(SUMOTime time)489 GUITLLogicPhasesTrackerWindow::setBeginTime(SUMOTime time) {
490 myBeginTime = time;
491 }
492
493
494 /****************************************************************************/
495
496